From dbda92d16f8655044e082930e4e9d244b87fde77 Mon Sep 17 00:00:00 2001 From: "Bu, Yitian" Date: Mon, 18 Feb 2013 12:53:37 +0000 Subject: printk: Fix rq->lock vs logbuf_lock unlock lock inversion commit 07354eb1a74d1 ("locking printk: Annotate logbuf_lock as raw") reintroduced a lock inversion problem which was fixed in commit 0b5e1c5255 ("printk: Release console_sem after logbuf_lock"). This happened probably when fixing up patch rejects. Restore the ordering and unlock logbuf_lock before releasing console_sem. Signed-off-by: ybu Cc: Peter Zijlstra Cc: stable@vger.kernel.org Link: http://lkml.kernel.org/r/E807E903FE6CBE4D95E420FBFCC273B827413C@nasanexd01h.na.qualcomm.com Signed-off-by: Thomas Gleixner --- kernel/printk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/printk.c b/kernel/printk.c index 267ce780abe8..e698e80d8428 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -1358,9 +1358,9 @@ static int console_trylock_for_printk(unsigned int cpu) } } logbuf_cpu = UINT_MAX; + raw_spin_unlock(&logbuf_lock); if (wake) up(&console_sem); - raw_spin_unlock(&logbuf_lock); return retval; } -- cgit v1.2.3 From 5abf6c643115aa20905e36b649cbe80ad784f332 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 4 Mar 2013 16:25:46 +0000 Subject: ide: tx4938ide: use module_platform_driver_probe() This patch uses module_platform_driver_probe() macro which makes the code smaller and simpler. Signed-off-by: Jingoo Han Signed-off-by: David S. Miller --- drivers/ide/tx4938ide.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/ide/tx4938ide.c b/drivers/ide/tx4938ide.c index 91d49dd957ef..ede8575ac7d5 100644 --- a/drivers/ide/tx4938ide.c +++ b/drivers/ide/tx4938ide.c @@ -203,18 +203,7 @@ static struct platform_driver tx4938ide_driver = { .remove = __exit_p(tx4938ide_remove), }; -static int __init tx4938ide_init(void) -{ - return platform_driver_probe(&tx4938ide_driver, tx4938ide_probe); -} - -static void __exit tx4938ide_exit(void) -{ - platform_driver_unregister(&tx4938ide_driver); -} - -module_init(tx4938ide_init); -module_exit(tx4938ide_exit); +module_platform_driver_probe(tx4938ide_driver, tx4938ide_probe); MODULE_DESCRIPTION("TX4938 internal IDE driver"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 477c17dba125b9b500763f5437fa5051f75b95bf Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 4 Mar 2013 16:26:53 +0000 Subject: ide: tx4939ide: use module_platform_driver_probe() This patch uses module_platform_driver_probe() macro which makes the code smaller and simpler. Signed-off-by: Jingoo Han Signed-off-by: David S. Miller --- drivers/ide/tx4939ide.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/ide/tx4939ide.c b/drivers/ide/tx4939ide.c index c0ab800b7bb3..4ecdee5eca83 100644 --- a/drivers/ide/tx4939ide.c +++ b/drivers/ide/tx4939ide.c @@ -624,18 +624,7 @@ static struct platform_driver tx4939ide_driver = { .resume = tx4939ide_resume, }; -static int __init tx4939ide_init(void) -{ - return platform_driver_probe(&tx4939ide_driver, tx4939ide_probe); -} - -static void __exit tx4939ide_exit(void) -{ - platform_driver_unregister(&tx4939ide_driver); -} - -module_init(tx4939ide_init); -module_exit(tx4939ide_exit); +module_platform_driver_probe(tx4939ide_driver, tx4939ide_probe); MODULE_DESCRIPTION("TX4939 internal IDE driver"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From bf6b4388c61cf88c6faa19cc45390fd36be8fb66 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 4 Mar 2013 16:27:36 +0000 Subject: ide: gayle: use module_platform_driver_probe() This patch uses module_platform_driver_probe() macro which makes the code smaller and simpler. Signed-off-by: Jingoo Han Signed-off-by: David S. Miller --- drivers/ide/gayle.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/drivers/ide/gayle.c b/drivers/ide/gayle.c index 51beb85250d4..0a8440ae0565 100644 --- a/drivers/ide/gayle.c +++ b/drivers/ide/gayle.c @@ -183,20 +183,7 @@ static struct platform_driver amiga_gayle_ide_driver = { }, }; -static int __init amiga_gayle_ide_init(void) -{ - return platform_driver_probe(&amiga_gayle_ide_driver, - amiga_gayle_ide_probe); -} - -module_init(amiga_gayle_ide_init); - -static void __exit amiga_gayle_ide_exit(void) -{ - platform_driver_unregister(&amiga_gayle_ide_driver); -} - -module_exit(amiga_gayle_ide_exit); +module_platform_driver_probe(amiga_gayle_ide_driver, amiga_gayle_ide_probe); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:amiga-gayle-ide"); -- cgit v1.2.3 From d59f9f4d68ca82985a3b4578caa0485a9cb2b71b Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 11 Apr 2013 16:29:07 +0200 Subject: drm/i915: don't enable the plane too early in i9xx_crtc_mode_set This is horrible lore and we should be able to get rid of it now that the lvds/pfit handling code actually does the right thing. Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 6e423e04c35e..8b1cfc92849d 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4733,8 +4733,6 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, i9xx_set_pipeconf(intel_crtc); - intel_enable_pipe(dev_priv, pipe, false); - intel_wait_for_vblank(dev, pipe); I915_WRITE(DSPCNTR(plane), dspcntr); -- cgit v1.2.3 From 4667730163a6d0afb04cefae9805ca1cdad1df46 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 11 Apr 2013 16:29:08 +0200 Subject: drm/i915: drop redundant vblank waits Just blows through 50ms for naught, since the pipe is off. Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 8b1cfc92849d..5a904ddbb0df 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4733,8 +4733,6 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, i9xx_set_pipeconf(intel_crtc); - intel_wait_for_vblank(dev, pipe); - I915_WRITE(DSPCNTR(plane), dspcntr); POSTING_READ(DSPCNTR(plane)); @@ -5704,8 +5702,6 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, ironlake_set_pipeconf(crtc, adjusted_mode, dither); - intel_wait_for_vblank(dev, pipe); - /* Set up the display plane register */ I915_WRITE(DSPCNTR(plane), DISPPLANE_GAMMA_ENABLE); POSTING_READ(DSPCNTR(plane)); -- cgit v1.2.3 From 58c6eaa24dbd8c97ef5f643324f087271aa79d64 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 11 Apr 2013 16:29:09 +0200 Subject: drm/i915: add pipe asserts for the crtc enable sequence The i9xx modeset sequence is currently pretty fishy, so tight it all up with some good assert-sprinkling. We already have good coverage on the disable side, but the enable side is spotty (since until recently it was wrong). Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 5a904ddbb0df..62fd5eae61c8 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1474,6 +1474,8 @@ static void intel_enable_pll(struct drm_i915_private *dev_priv, enum pipe pipe) int reg; u32 val; + assert_pipe_disabled(dev_priv, pipe); + /* No really, not for ILK+ */ BUG_ON(!IS_VALLEYVIEW(dev_priv->dev) && dev_priv->info->gen >= 5); @@ -1835,6 +1837,9 @@ static void intel_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe, int reg; u32 val; + assert_planes_disabled(dev_priv, pipe); + assert_sprites_disabled(dev_priv, pipe); + if (HAS_PCH_LPT(dev_priv->dev)) pch_transcoder = TRANSCODER_A; else -- cgit v1.2.3 From e29a18faaa18470f313b570fdf2d0f75c35e6cb2 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 11 Apr 2013 16:29:10 +0200 Subject: drm/i915: add i9xx pfit pipe asserts We can only enable the pfit if the pipe is disabled. Ensure that this is obeyed with a neat assert. Also check whether the pfit is off before enabling it - if not we've lost track of things somewhere since the pfit is only ever used by the lvds output. v2: Fix spell fail in the commit message pointed out by Ville&Jani. Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_lvds.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index f36f1baabd5a..563f5052fcfc 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -159,6 +159,9 @@ static void intel_pre_enable_lvds(struct intel_encoder *encoder) if (HAS_PCH_SPLIT(dev) || !enc->pfit_control) return; + WARN_ON(I915_READ(PFIT_CONTROL) & PFIT_ENABLE); + assert_pipe_disabled(dev_priv, to_intel_crtc(encoder->base.crtc)->pipe); + /* * Enable automatic panel scaling so that non-native modes * fill the screen. The panel fitter should only be -- cgit v1.2.3 From e3641d3f77bb9aea8442dc25018d651d3a348d76 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 11 Apr 2013 19:49:07 +0200 Subject: drm/i915: move debug output back to the right place When adding the pipe config computation step I've accidentally moved this a bit away. Which momentarily confused me since the pipe config step rejected some modesetting operations I expected and so left me looking in vain for that debug output. v2: Move the debug output into the right function to prevent this from happening again. v3: Make it compile (Ville). Also reorder the patch so that the two bugfixes are first. Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 62fd5eae61c8..ae7c3779baef 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7744,6 +7744,9 @@ intel_modeset_affected_pipes(struct drm_crtc *crtc, unsigned *modeset_pipes, */ *modeset_pipes &= 1 << intel_crtc->pipe; *prepare_pipes &= 1 << intel_crtc->pipe; + + DRM_DEBUG_KMS("set mode pipe masks: modeset: %x, prepare: %x, disable: %x\n", + *modeset_pipes, *prepare_pipes, *disable_pipes); } static bool intel_crtc_in_use(struct drm_crtc *crtc) @@ -7974,9 +7977,6 @@ static int __intel_set_mode(struct drm_crtc *crtc, } } - DRM_DEBUG_KMS("set mode pipe masks: modeset: %x, prepare: %x, disable: %x\n", - modeset_pipes, prepare_pipes, disable_pipes); - for_each_intel_crtc_masked(dev, disable_pipes, intel_crtc) intel_crtc_disable(&intel_crtc->base); -- cgit v1.2.3 From 2582a8504dbdc80b6e16ac8dfd6a7f9d03bece73 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Wed, 17 Apr 2013 17:48:47 +0300 Subject: drm/i915: Use pipe_name() and port_name() where appropriate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Get rid of the few remaining open coded copies of pipe_name() and port_name(). Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index ae7c3779baef..462c81a871ab 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4723,7 +4723,7 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, dspcntr |= DISPPLANE_SEL_PIPE_B; } - DRM_DEBUG_KMS("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B'); + DRM_DEBUG_KMS("Mode for pipe %c:\n", pipe_name(pipe)); drm_mode_debug_printmodeline(mode); intel_set_pipe_timings(intel_crtc, mode, adjusted_mode); @@ -6109,7 +6109,7 @@ static void ironlake_write_eld(struct drm_connector *connector, eldv |= IBX_ELD_VALIDB << 4; eldv |= IBX_ELD_VALIDB << 8; } else { - DRM_DEBUG_DRIVER("ELD on port %c\n", 'A' + i); + DRM_DEBUG_DRIVER("ELD on port %c\n", port_name(i)); eldv = IBX_ELD_VALIDB << ((i - 1) * 4); } -- cgit v1.2.3 From cfc33bf75bfbf8f95bffe040ee1a5ab1cda8c34c Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Wed, 17 Apr 2013 17:48:48 +0300 Subject: drm/i915: Use port_name() in PCH port audio power change message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 0aa2ef0d2ae0..3af983fad67c 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -763,10 +763,12 @@ static void ibx_irq_handler(struct drm_device *dev, u32 pch_iir) ibx_hpd_irq_setup(dev); queue_work(dev_priv->wq, &dev_priv->hotplug_work); } - if (pch_iir & SDE_AUDIO_POWER_MASK) + if (pch_iir & SDE_AUDIO_POWER_MASK) { + int port = ffs((pch_iir & SDE_AUDIO_POWER_MASK) >> + SDE_AUDIO_POWER_SHIFT); DRM_DEBUG_DRIVER("PCH audio power change on port %d\n", - (pch_iir & SDE_AUDIO_POWER_MASK) >> - SDE_AUDIO_POWER_SHIFT); + port_name(port)); + } if (pch_iir & SDE_AUX_MASK) dp_aux_irq_handler(dev); @@ -812,10 +814,12 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir) ibx_hpd_irq_setup(dev); queue_work(dev_priv->wq, &dev_priv->hotplug_work); } - if (pch_iir & SDE_AUDIO_POWER_MASK_CPT) - DRM_DEBUG_DRIVER("PCH audio power change on port %d\n", - (pch_iir & SDE_AUDIO_POWER_MASK_CPT) >> - SDE_AUDIO_POWER_SHIFT_CPT); + if (pch_iir & SDE_AUDIO_POWER_MASK_CPT) { + int port = ffs((pch_iir & SDE_AUDIO_POWER_MASK_CPT) >> + SDE_AUDIO_POWER_SHIFT_CPT); + DRM_DEBUG_DRIVER("PCH audio power change on port %c\n", + port_name(port)); + } if (pch_iir & SDE_AUX_MASK_CPT) dp_aux_irq_handler(dev); -- cgit v1.2.3 From 84f44ce795b3da9a08dc2041ecd60550d34c123e Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Wed, 17 Apr 2013 17:48:49 +0300 Subject: drm/i915: Print plane, pipe, port names as alphabetical insted of decimal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Alway use the alphabetical names in debug/error messages for planes, pipes and ports, instead of using decimal numbers occasionally. Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ddi.c | 10 ++++----- drivers/gpu/drm/i915/intel_display.c | 42 ++++++++++++++++++------------------ drivers/gpu/drm/i915/intel_pm.c | 26 +++++++++++----------- 3 files changed, 39 insertions(+), 39 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 26a0a570f92e..e14fe5f569ee 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -748,8 +748,8 @@ intel_ddi_get_crtc_encoder(struct drm_crtc *crtc) } if (num_encoders != 1) - WARN(1, "%d encoders on crtc for pipe %d\n", num_encoders, - intel_crtc->pipe); + WARN(1, "%d encoders on crtc for pipe %c\n", num_encoders, + pipe_name(intel_crtc->pipe)); BUG_ON(ret == NULL); return ret; @@ -1047,8 +1047,8 @@ void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc) } } else { - WARN(1, "Invalid encoder type %d for pipe %d\n", - intel_encoder->type, pipe); + WARN(1, "Invalid encoder type %d for pipe %c\n", + intel_encoder->type, pipe_name(pipe)); } I915_WRITE(TRANS_DDI_FUNC_CTL(cpu_transcoder), temp); @@ -1148,7 +1148,7 @@ bool intel_ddi_get_hw_state(struct intel_encoder *encoder, } } - DRM_DEBUG_KMS("No pipe for ddi port %i found\n", port); + DRM_DEBUG_KMS("No pipe for ddi port %c found\n", port_name(port)); return false; } diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 462c81a871ab..e472488394ae 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2101,7 +2101,7 @@ static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb, case 1: break; default: - DRM_ERROR("Can't update plane %d in SAREA\n", plane); + DRM_ERROR("Can't update plane %c in SAREA\n", plane_name(plane)); return -EINVAL; } @@ -2198,7 +2198,7 @@ static int ironlake_update_plane(struct drm_crtc *crtc, case 2: break; default: - DRM_ERROR("Can't update plane %d in SAREA\n", plane); + DRM_ERROR("Can't update plane %c in SAREA\n", plane_name(plane)); return -EINVAL; } @@ -2389,9 +2389,9 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, } if (intel_crtc->plane > INTEL_INFO(dev)->num_pipes) { - DRM_ERROR("no plane for crtc: plane %d, num_pipes %d\n", - intel_crtc->plane, - INTEL_INFO(dev)->num_pipes); + DRM_ERROR("no plane for crtc: plane %c, num_pipes %d\n", + plane_name(intel_crtc->plane), + INTEL_INFO(dev)->num_pipes); return -EINVAL; } @@ -3299,7 +3299,7 @@ static struct intel_pch_pll *intel_get_pch_pll(struct intel_crtc *intel_crtc, u3 found: intel_crtc->pch_pll = pll; pll->refcount++; - DRM_DEBUG_DRIVER("using pll %d for pipe %d\n", i, intel_crtc->pipe); + DRM_DEBUG_DRIVER("using pll %d for pipe %c\n", i, pipe_name(intel_crtc->pipe)); prepare: /* separate function? */ DRM_DEBUG_DRIVER("switching PLL %x off\n", pll->pll_reg); @@ -3324,7 +3324,7 @@ void intel_cpt_verify_modeset(struct drm_device *dev, int pipe) udelay(500); if (wait_for(I915_READ(dslreg) != temp, 5)) { if (wait_for(I915_READ(dslreg) != temp, 5)) - DRM_ERROR("mode set failed: pipe %d stuck\n", pipe); + DRM_ERROR("mode set failed: pipe %c stuck\n", pipe_name(pipe)); } } @@ -5344,11 +5344,11 @@ static bool ironlake_check_fdi_lanes(struct intel_crtc *intel_crtc) struct intel_crtc *pipe_B_crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[PIPE_B]); - DRM_DEBUG_KMS("checking fdi config on pipe %i, lanes %i\n", - intel_crtc->pipe, intel_crtc->fdi_lanes); + DRM_DEBUG_KMS("checking fdi config on pipe %c, lanes %i\n", + pipe_name(intel_crtc->pipe), intel_crtc->fdi_lanes); if (intel_crtc->fdi_lanes > 4) { - DRM_DEBUG_KMS("invalid fdi lane config on pipe %i: %i lanes\n", - intel_crtc->pipe, intel_crtc->fdi_lanes); + DRM_DEBUG_KMS("invalid fdi lane config on pipe %c: %i lanes\n", + pipe_name(intel_crtc->pipe), intel_crtc->fdi_lanes); /* Clamp lanes to avoid programming the hw with bogus values. */ intel_crtc->fdi_lanes = 4; @@ -5364,8 +5364,8 @@ static bool ironlake_check_fdi_lanes(struct intel_crtc *intel_crtc) case PIPE_B: if (dev_priv->pipe_to_crtc_mapping[PIPE_C]->enabled && intel_crtc->fdi_lanes > 2) { - DRM_DEBUG_KMS("invalid shared fdi lane config on pipe %i: %i lanes\n", - intel_crtc->pipe, intel_crtc->fdi_lanes); + DRM_DEBUG_KMS("invalid shared fdi lane config on pipe %c: %i lanes\n", + pipe_name(intel_crtc->pipe), intel_crtc->fdi_lanes); /* Clamp lanes to avoid programming the hw with bogus values. */ intel_crtc->fdi_lanes = 2; @@ -5381,8 +5381,8 @@ static bool ironlake_check_fdi_lanes(struct intel_crtc *intel_crtc) case PIPE_C: if (!pipe_B_crtc->base.enabled || pipe_B_crtc->fdi_lanes <= 2) { if (intel_crtc->fdi_lanes > 2) { - DRM_DEBUG_KMS("invalid shared fdi lane config on pipe %i: %i lanes\n", - intel_crtc->pipe, intel_crtc->fdi_lanes); + DRM_DEBUG_KMS("invalid shared fdi lane config on pipe %c: %i lanes\n", + pipe_name(intel_crtc->pipe), intel_crtc->fdi_lanes); /* Clamp lanes to avoid programming the hw with bogus values. */ intel_crtc->fdi_lanes = 2; @@ -5647,7 +5647,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, dpll = ironlake_compute_dpll(intel_crtc, &clock, &fp, &reduced_clock, has_reduced_clock ? &fp2 : NULL); - DRM_DEBUG_KMS("Mode for pipe %d:\n", pipe); + DRM_DEBUG_KMS("Mode for pipe %c:\n", pipe_name(pipe)); drm_mode_debug_printmodeline(mode); /* CPU eDP is the only output that doesn't need a PCH PLL of its own. */ @@ -5656,8 +5656,8 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, pll = intel_get_pch_pll(intel_crtc, dpll, fp); if (pll == NULL) { - DRM_DEBUG_DRIVER("failed to find PLL for pipe %d\n", - pipe); + DRM_DEBUG_DRIVER("failed to find PLL for pipe %c\n", + pipe_name(pipe)); return -EINVAL; } } else @@ -5821,7 +5821,7 @@ static int haswell_crtc_mode_set(struct drm_crtc *crtc, /* determine panel color depth */ dither = intel_crtc->config.dither; - DRM_DEBUG_KMS("Mode for pipe %d:\n", pipe); + DRM_DEBUG_KMS("Mode for pipe %c:\n", pipe_name(pipe)); drm_mode_debug_printmodeline(mode); if (intel_crtc->config.has_dp_encoder) @@ -9051,8 +9051,8 @@ void intel_modeset_init(struct drm_device *dev) for (j = 0; j < dev_priv->num_plane; j++) { ret = intel_plane_init(dev, i, j); if (ret) - DRM_DEBUG_KMS("pipe %d plane %d init failed: %d\n", - i, j, ret); + DRM_DEBUG_KMS("pipe %c plane %d init failed: %d\n", + pipe_name(i), j, ret); } } diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index e34ad9642519..413877d4afea 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -113,8 +113,8 @@ static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval) fbc_ctl |= obj->fence_reg; I915_WRITE(FBC_CONTROL, fbc_ctl); - DRM_DEBUG_KMS("enabled FBC, pitch %d, yoff %d, plane %d, ", - cfb_pitch, crtc->y, intel_crtc->plane); + DRM_DEBUG_KMS("enabled FBC, pitch %d, yoff %d, plane %c, ", + cfb_pitch, crtc->y, plane_name(intel_crtc->plane)); } static bool i8xx_fbc_enabled(struct drm_device *dev) @@ -148,7 +148,7 @@ static void g4x_enable_fbc(struct drm_crtc *crtc, unsigned long interval) /* enable it... */ I915_WRITE(DPFC_CONTROL, I915_READ(DPFC_CONTROL) | DPFC_CTL_EN); - DRM_DEBUG_KMS("enabled fbc on plane %d\n", intel_crtc->plane); + DRM_DEBUG_KMS("enabled fbc on plane %c\n", plane_name(intel_crtc->plane)); } static void g4x_disable_fbc(struct drm_device *dev) @@ -228,7 +228,7 @@ static void ironlake_enable_fbc(struct drm_crtc *crtc, unsigned long interval) sandybridge_blit_fbc_update(dev); } - DRM_DEBUG_KMS("enabled fbc on plane %d\n", intel_crtc->plane); + DRM_DEBUG_KMS("enabled fbc on plane %c\n", plane_name(intel_crtc->plane)); } static void ironlake_disable_fbc(struct drm_device *dev) @@ -2146,15 +2146,15 @@ static void sandybridge_update_sprite_wm(struct drm_device *dev, int pipe, &sandybridge_display_wm_info, latency, &sprite_wm); if (!ret) { - DRM_DEBUG_KMS("failed to compute sprite wm for pipe %d\n", - pipe); + DRM_DEBUG_KMS("failed to compute sprite wm for pipe %c\n", + pipe_name(pipe)); return; } val = I915_READ(reg); val &= ~WM0_PIPE_SPRITE_MASK; I915_WRITE(reg, val | (sprite_wm << WM0_PIPE_SPRITE_SHIFT)); - DRM_DEBUG_KMS("sprite watermarks For pipe %d - %d\n", pipe, sprite_wm); + DRM_DEBUG_KMS("sprite watermarks For pipe %c - %d\n", pipe_name(pipe), sprite_wm); ret = sandybridge_compute_sprite_srwm(dev, pipe, sprite_width, @@ -2163,8 +2163,8 @@ static void sandybridge_update_sprite_wm(struct drm_device *dev, int pipe, SNB_READ_WM1_LATENCY() * 500, &sprite_wm); if (!ret) { - DRM_DEBUG_KMS("failed to compute sprite lp1 wm on pipe %d\n", - pipe); + DRM_DEBUG_KMS("failed to compute sprite lp1 wm on pipe %c\n", + pipe_name(pipe)); return; } I915_WRITE(WM1S_LP_ILK, sprite_wm); @@ -2179,8 +2179,8 @@ static void sandybridge_update_sprite_wm(struct drm_device *dev, int pipe, SNB_READ_WM2_LATENCY() * 500, &sprite_wm); if (!ret) { - DRM_DEBUG_KMS("failed to compute sprite lp2 wm on pipe %d\n", - pipe); + DRM_DEBUG_KMS("failed to compute sprite lp2 wm on pipe %c\n", + pipe_name(pipe)); return; } I915_WRITE(WM2S_LP_IVB, sprite_wm); @@ -2191,8 +2191,8 @@ static void sandybridge_update_sprite_wm(struct drm_device *dev, int pipe, SNB_READ_WM3_LATENCY() * 500, &sprite_wm); if (!ret) { - DRM_DEBUG_KMS("failed to compute sprite lp3 wm on pipe %d\n", - pipe); + DRM_DEBUG_KMS("failed to compute sprite lp3 wm on pipe %c\n", + pipe_name(pipe)); return; } I915_WRITE(WM3S_LP_IVB, sprite_wm); -- cgit v1.2.3 From 4bb6f1f3270923cdcd57293ea736955d5c35db72 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Wed, 17 Apr 2013 17:48:50 +0300 Subject: drm/i915: Use alphabetical names for transcoders too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Print the alphabetical name for transcoders. The code already used the pipe_name() macro for transcoders, so I did the same. But we do have the (unused) transcoder_name() macro which could be used instead. Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index e472488394ae..81ae89b9c794 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1097,14 +1097,14 @@ static void assert_pch_pll(struct drm_i915_private *dev_priv, pch_dpll = I915_READ(PCH_DPLL_SEL); cur_state = pll->pll_reg == _PCH_DPLL_B; if (!WARN(((pch_dpll >> (4 * crtc->pipe)) & 1) != cur_state, - "PLL[%d] not attached to this transcoder %d: %08x\n", - cur_state, crtc->pipe, pch_dpll)) { + "PLL[%d] not attached to this transcoder %c: %08x\n", + cur_state, pipe_name(crtc->pipe), pch_dpll)) { cur_state = !!(val >> (4*crtc->pipe + 3)); WARN(cur_state != state, - "PLL[%d] not %s on this transcoder %d: %08x\n", + "PLL[%d] not %s on this transcoder %c: %08x\n", pll->pll_reg == _PCH_DPLL_B, state_string(state), - crtc->pipe, + pipe_name(crtc->pipe), val); } } @@ -1733,7 +1733,7 @@ static void ironlake_enable_pch_transcoder(struct drm_i915_private *dev_priv, I915_WRITE(reg, val | TRANS_ENABLE); if (wait_for(I915_READ(reg) & TRANS_STATE_ENABLE, 100)) - DRM_ERROR("failed to enable transcoder %d\n", pipe); + DRM_ERROR("failed to enable transcoder %c\n", pipe_name(pipe)); } static void lpt_enable_pch_transcoder(struct drm_i915_private *dev_priv, @@ -1786,7 +1786,7 @@ static void ironlake_disable_pch_transcoder(struct drm_i915_private *dev_priv, I915_WRITE(reg, val); /* wait for PCH transcoder off, transcoder state */ if (wait_for((I915_READ(reg) & TRANS_STATE_ENABLE) == 0, 50)) - DRM_ERROR("failed to disable transcoder %d\n", pipe); + DRM_ERROR("failed to disable transcoder %c\n", pipe_name(pipe)); if (!HAS_PCH_IBX(dev)) { /* Workaround: Clear the timing override chicken bit again. */ -- cgit v1.2.3 From 06da8da2b014d6cc98fa86e72b605e525e6d6884 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Wed, 17 Apr 2013 17:48:51 +0300 Subject: drm/i915: Use alphabetical names for sprites MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add sprite_name() macro which should be used with the kind of sprites that are fixed to pipes (gen4.5+). Also use dev_priv->num_plane to calculate the sprite index insted assuming two sprites per pipe. This should make it print the right name. Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 2 ++ drivers/gpu/drm/i915/intel_display.c | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index d5dcf7fe1ee9..15d0a3a8f319 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -76,6 +76,8 @@ enum plane { }; #define plane_name(p) ((p) + 'A') +#define sprite_name(p, s) ((p) * dev_priv->num_plane + (s) + 'A') + enum port { PORT_A = 0, PORT_B, diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 81ae89b9c794..02faba0263de 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1302,8 +1302,8 @@ static void assert_sprites_disabled(struct drm_i915_private *dev_priv, reg = SPCNTR(pipe, i); val = I915_READ(reg); WARN((val & SP_ENABLE), - "sprite %d assertion failure, should be off on pipe %c but is still active\n", - pipe * 2 + i, pipe_name(pipe)); + "sprite %c assertion failure, should be off on pipe %c but is still active\n", + sprite_name(pipe, i), pipe_name(pipe)); } } @@ -9051,8 +9051,8 @@ void intel_modeset_init(struct drm_device *dev) for (j = 0; j < dev_priv->num_plane; j++) { ret = intel_plane_init(dev, i, j); if (ret) - DRM_DEBUG_KMS("pipe %c plane %d init failed: %d\n", - pipe_name(i), j, ret); + DRM_DEBUG_KMS("pipe %c sprite %c init failed: %d\n", + pipe_name(i), sprite_name(i, j), ret); } } -- cgit v1.2.3 From 855ba3be12badf6228151ca3ccf54632cfdd463d Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Wed, 17 Apr 2013 15:54:57 -0700 Subject: drm/i915: VLV GPU frequency to opcode functions When requesting frequency changes or querying status from the Punit, we need to use an opcode that corresponds to the frequency, taking into account the memory frequency. Signed-off-by: Jesse Barnes Acked-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 2 ++ drivers/gpu/drm/i915/intel_pm.c | 56 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 15d0a3a8f319..abb065502deb 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1887,6 +1887,8 @@ int sandybridge_pcode_read(struct drm_i915_private *dev_priv, u8 mbox, u32 *val) int sandybridge_pcode_write(struct drm_i915_private *dev_priv, u8 mbox, u32 val); int valleyview_punit_read(struct drm_i915_private *dev_priv, u8 addr, u32 *val); int valleyview_punit_write(struct drm_i915_private *dev_priv, u8 addr, u32 val); +int vlv_gpu_freq(int ddr_freq, int val); +int vlv_freq_opcode(int ddr_freq, int val); #define __i915_read(x, y) \ u##x i915_read##x(struct drm_i915_private *dev_priv, u32 reg); diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 413877d4afea..f802368e8e9d 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -4620,3 +4620,59 @@ int valleyview_punit_write(struct drm_i915_private *dev_priv, u8 addr, u32 val) { return vlv_punit_rw(dev_priv, PUNIT_OPCODE_REG_WRITE, addr, &val); } + +int vlv_gpu_freq(int ddr_freq, int val) +{ + int mult, base; + + switch (ddr_freq) { + case 800: + mult = 20; + base = 120; + break; + case 1066: + mult = 22; + base = 133; + break; + case 1333: + mult = 21; + base = 125; + break; + default: + return -1; + } + + return ((val - 0xbd) * mult) + base; +} + +int vlv_freq_opcode(int ddr_freq, int val) +{ + int mult, base; + + switch (ddr_freq) { + case 800: + mult = 20; + base = 120; + break; + case 1066: + mult = 22; + base = 133; + break; + case 1333: + mult = 21; + base = 125; + break; + default: + return -1; + } + + val /= mult; + val -= base / mult; + val += 0xbd; + + if (val > 0xea) + val = 0xea; + + return val; +} + -- cgit v1.2.3 From 0a073b843bcd9a660f76e497182aac97cafddc4c Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Wed, 17 Apr 2013 15:54:58 -0700 Subject: drm/i915: turbo & RC6 support for VLV v7 Uses slightly different interfaces than other platforms. v2: track actual set freq, not requested (Rohit) fix debug prints in init code (Jesse) v3: don't write sleep reg (Jesse) re-add RC6 wake limit write (Ben) fixup thresholds to match other platforms (Ben) clean up mem freq calculation (Ben) clean up debug prints (Ben) v4: move defines from punit patch (Ville) v5: remove writes to nonexistent regs (Jesse) put RP and RC regs together (Jesse) fix RC6 enable (Jesse) v6: use correct fuse reads from NC (Jesse) split out min/max funcs for use in sysfs (Jesse) add debugfs & sysfs freq controls (Jesse) v7: update with Ben's hw_max changes (Jesse) Signed-off-by: Jesse Barnes Reviewed-by: Ben Widawsky (v6) [danvet: Follow checkpatch sugggestion to use min_t to avoid casting fun.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 58 +++++++++-- drivers/gpu/drm/i915/i915_drv.h | 5 + drivers/gpu/drm/i915/i915_irq.c | 5 +- drivers/gpu/drm/i915/i915_reg.h | 21 ++++ drivers/gpu/drm/i915/i915_sysfs.c | 71 +++++++++---- drivers/gpu/drm/i915/intel_pm.c | 199 ++++++++++++++++++++++++++++++++++-- 6 files changed, 320 insertions(+), 39 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index e913d325d5b8..367b534d2260 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -941,7 +941,7 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused) MEMSTAT_VID_SHIFT); seq_printf(m, "Current P-state: %d\n", (rgvstat & MEMSTAT_PSTATE_MASK) >> MEMSTAT_PSTATE_SHIFT); - } else if (IS_GEN6(dev) || IS_GEN7(dev)) { + } else if ((IS_GEN6(dev) || IS_GEN7(dev)) && !IS_VALLEYVIEW(dev)) { u32 gt_perf_status = I915_READ(GEN6_GT_PERF_STATUS); u32 rp_state_limits = I915_READ(GEN6_RP_STATE_LIMITS); u32 rp_state_cap = I915_READ(GEN6_RP_STATE_CAP); @@ -1009,6 +1009,25 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused) seq_printf(m, "Max overclocked frequency: %dMHz\n", dev_priv->rps.hw_max * GT_FREQUENCY_MULTIPLIER); + } else if (IS_VALLEYVIEW(dev)) { + u32 freq_sts, val; + + valleyview_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS, + &freq_sts); + seq_printf(m, "PUNIT_REG_GPU_FREQ_STS: 0x%08x\n", freq_sts); + seq_printf(m, "DDR freq: %d MHz\n", dev_priv->mem_freq); + + valleyview_punit_read(dev_priv, PUNIT_FUSE_BUS1, &val); + seq_printf(m, "max GPU freq: %d MHz\n", + vlv_gpu_freq(dev_priv->mem_freq, val)); + + valleyview_punit_read(dev_priv, PUNIT_REG_GPU_LFM, &val); + seq_printf(m, "min GPU freq: %d MHz\n", + vlv_gpu_freq(dev_priv->mem_freq, val)); + + seq_printf(m, "current GPU freq: %d MHz\n", + vlv_gpu_freq(dev_priv->mem_freq, + (freq_sts >> 8) & 0xff)); } else { seq_printf(m, "no P-state info available\n"); } @@ -1812,7 +1831,11 @@ i915_max_freq_get(void *data, u64 *val) if (ret) return ret; - *val = dev_priv->rps.max_delay * GT_FREQUENCY_MULTIPLIER; + if (IS_VALLEYVIEW(dev)) + *val = vlv_gpu_freq(dev_priv->mem_freq, + dev_priv->rps.max_delay); + else + *val = dev_priv->rps.max_delay * GT_FREQUENCY_MULTIPLIER; mutex_unlock(&dev_priv->rps.hw_lock); return 0; @@ -1837,9 +1860,16 @@ i915_max_freq_set(void *data, u64 val) /* * Turbo will still be enabled, but won't go above the set value. */ - do_div(val, GT_FREQUENCY_MULTIPLIER); - dev_priv->rps.max_delay = val; - gen6_set_rps(dev, val); + if (IS_VALLEYVIEW(dev)) { + val = vlv_freq_opcode(dev_priv->mem_freq, val); + dev_priv->rps.max_delay = val; + gen6_set_rps(dev, val); + } else { + do_div(val, GT_FREQUENCY_MULTIPLIER); + dev_priv->rps.max_delay = val; + gen6_set_rps(dev, val); + } + mutex_unlock(&dev_priv->rps.hw_lock); return 0; @@ -1863,7 +1893,11 @@ i915_min_freq_get(void *data, u64 *val) if (ret) return ret; - *val = dev_priv->rps.min_delay * GT_FREQUENCY_MULTIPLIER; + if (IS_VALLEYVIEW(dev)) + *val = vlv_gpu_freq(dev_priv->mem_freq, + dev_priv->rps.min_delay); + else + *val = dev_priv->rps.min_delay * GT_FREQUENCY_MULTIPLIER; mutex_unlock(&dev_priv->rps.hw_lock); return 0; @@ -1888,9 +1922,15 @@ i915_min_freq_set(void *data, u64 val) /* * Turbo will still be enabled, but won't go below the set value. */ - do_div(val, GT_FREQUENCY_MULTIPLIER); - dev_priv->rps.min_delay = val; - gen6_set_rps(dev, val); + if (IS_VALLEYVIEW(dev)) { + val = vlv_freq_opcode(dev_priv->mem_freq, val); + dev_priv->rps.min_delay = val; + valleyview_set_rps(dev, val); + } else { + do_div(val, GT_FREQUENCY_MULTIPLIER); + dev_priv->rps.min_delay = val; + gen6_set_rps(dev, val); + } mutex_unlock(&dev_priv->rps.hw_lock); return 0; diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index abb065502deb..bd2d7f17393e 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1856,6 +1856,9 @@ extern void intel_disable_fbc(struct drm_device *dev); extern bool ironlake_set_drps(struct drm_device *dev, u8 val); extern void intel_init_pch_refclk(struct drm_device *dev); extern void gen6_set_rps(struct drm_device *dev, u8 val); +extern void valleyview_set_rps(struct drm_device *dev, u8 val); +extern int valleyview_rps_max_freq(struct drm_i915_private *dev_priv); +extern int valleyview_rps_min_freq(struct drm_i915_private *dev_priv); extern void intel_detect_pch(struct drm_device *dev); extern int intel_trans_dp_port_sel(struct drm_crtc *crtc); extern int intel_enable_rc6(const struct drm_device *dev); @@ -1887,6 +1890,8 @@ int sandybridge_pcode_read(struct drm_i915_private *dev_priv, u8 mbox, u32 *val) int sandybridge_pcode_write(struct drm_i915_private *dev_priv, u8 mbox, u32 val); int valleyview_punit_read(struct drm_i915_private *dev_priv, u8 addr, u32 *val); int valleyview_punit_write(struct drm_i915_private *dev_priv, u8 addr, u32 val); +int valleyview_nc_read(struct drm_i915_private *dev_priv, u8 addr, u32 *val); + int vlv_gpu_freq(int ddr_freq, int val); int vlv_freq_opcode(int ddr_freq, int val); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 3af983fad67c..932e7f8b6d5c 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -482,7 +482,10 @@ static void gen6_pm_rps_work(struct work_struct *work) */ if (!(new_delay > dev_priv->rps.max_delay || new_delay < dev_priv->rps.min_delay)) { - gen6_set_rps(dev_priv->dev, new_delay); + if (IS_VALLEYVIEW(dev_priv->dev)) + valleyview_set_rps(dev_priv->dev, new_delay); + else + gen6_set_rps(dev_priv->dev, new_delay); } mutex_unlock(&dev_priv->rps.hw_lock); diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 31de7e4b1f3e..66fb8dd28225 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -4315,6 +4315,7 @@ #define GEN6_RC_CTL_RC6_ENABLE (1<<18) #define GEN6_RC_CTL_RC1e_ENABLE (1<<20) #define GEN6_RC_CTL_RC7_ENABLE (1<<22) +#define GEN7_RC_CTL_TO_MODE (1<<28) #define GEN6_RC_CTL_EI_MODE(x) ((x)<<27) #define GEN6_RC_CTL_HW_ENABLE (1<<31) #define GEN6_RP_DOWN_TIMEOUT 0xA010 @@ -4406,12 +4407,32 @@ #define IOSF_BAR_SHIFT 1 #define IOSF_SB_BUSY (1<<0) #define IOSF_PORT_PUNIT 0x4 +#define IOSF_PORT_NC 0x11 #define VLV_IOSF_DATA 0x182104 #define VLV_IOSF_ADDR 0x182108 #define PUNIT_OPCODE_REG_READ 6 #define PUNIT_OPCODE_REG_WRITE 7 +#define PUNIT_REG_GPU_LFM 0xd3 +#define PUNIT_REG_GPU_FREQ_REQ 0xd4 +#define PUNIT_REG_GPU_FREQ_STS 0xd8 +#define PUNIT_REG_MEDIA_TURBO_FREQ_REQ 0xdc + +#define PUNIT_FUSE_BUS2 0xf6 /* bits 47:40 */ +#define PUNIT_FUSE_BUS1 0xf5 /* bits 55:48 */ + +#define IOSF_NC_FB_GFX_FREQ_FUSE 0x1c +#define FB_GFX_MAX_FREQ_FUSE_SHIFT 3 +#define FB_GFX_MAX_FREQ_FUSE_MASK 0x000007f8 +#define FB_GFX_FGUARANTEED_FREQ_FUSE_SHIFT 11 +#define FB_GFX_FGUARANTEED_FREQ_FUSE_MASK 0x0007f800 +#define IOSF_NC_FB_GFX_FMAX_FUSE_HI 0x34 +#define FB_FMAX_VMIN_FREQ_HI_MASK 0x00000007 +#define IOSF_NC_FB_GFX_FMAX_FUSE_LO 0x30 +#define FB_FMAX_VMIN_FREQ_LO_SHIFT 27 +#define FB_FMAX_VMIN_FREQ_LO_MASK 0xf8000000 + #define GEN6_GT_CORE_STATUS 0x138060 #define GEN6_CORE_CPD_STATE_MASK (7<<4) #define GEN6_RCn_MASK 7 diff --git a/drivers/gpu/drm/i915/i915_sysfs.c b/drivers/gpu/drm/i915/i915_sysfs.c index d5e1890678f9..ca00df2de07b 100644 --- a/drivers/gpu/drm/i915/i915_sysfs.c +++ b/drivers/gpu/drm/i915/i915_sysfs.c @@ -212,7 +212,10 @@ static ssize_t gt_cur_freq_mhz_show(struct device *kdev, int ret; mutex_lock(&dev_priv->rps.hw_lock); - ret = dev_priv->rps.cur_delay * GT_FREQUENCY_MULTIPLIER; + if (IS_VALLEYVIEW(dev_priv->dev)) + ret = vlv_gpu_freq(dev_priv->mem_freq, dev_priv->rps.cur_delay); + else + ret = dev_priv->rps.cur_delay * GT_FREQUENCY_MULTIPLIER; mutex_unlock(&dev_priv->rps.hw_lock); return snprintf(buf, PAGE_SIZE, "%d\n", ret); @@ -226,7 +229,10 @@ static ssize_t gt_max_freq_mhz_show(struct device *kdev, struct device_attribute int ret; mutex_lock(&dev_priv->rps.hw_lock); - ret = dev_priv->rps.max_delay * GT_FREQUENCY_MULTIPLIER; + if (IS_VALLEYVIEW(dev_priv->dev)) + ret = vlv_gpu_freq(dev_priv->mem_freq, dev_priv->rps.max_delay); + else + ret = dev_priv->rps.max_delay * GT_FREQUENCY_MULTIPLIER; mutex_unlock(&dev_priv->rps.hw_lock); return snprintf(buf, PAGE_SIZE, "%d\n", ret); @@ -246,16 +252,25 @@ static ssize_t gt_max_freq_mhz_store(struct device *kdev, if (ret) return ret; - val /= GT_FREQUENCY_MULTIPLIER; - mutex_lock(&dev_priv->rps.hw_lock); - rp_state_cap = I915_READ(GEN6_RP_STATE_CAP); - hw_max = dev_priv->rps.hw_max; - non_oc_max = (rp_state_cap & 0xff); - hw_min = ((rp_state_cap & 0xff0000) >> 16); + if (IS_VALLEYVIEW(dev_priv->dev)) { + val = vlv_freq_opcode(dev_priv->mem_freq, val); + + hw_max = valleyview_rps_max_freq(dev_priv); + hw_min = valleyview_rps_min_freq(dev_priv); + non_oc_max = hw_max; + } else { + val /= GT_FREQUENCY_MULTIPLIER; - if (val < hw_min || val > hw_max || val < dev_priv->rps.min_delay) { + rp_state_cap = I915_READ(GEN6_RP_STATE_CAP); + hw_max = dev_priv->rps.hw_max; + non_oc_max = (rp_state_cap & 0xff); + hw_min = ((rp_state_cap & 0xff0000) >> 16); + } + + if (val < hw_min || val > hw_max || + val < dev_priv->rps.min_delay) { mutex_unlock(&dev_priv->rps.hw_lock); return -EINVAL; } @@ -264,8 +279,12 @@ static ssize_t gt_max_freq_mhz_store(struct device *kdev, DRM_DEBUG("User requested overclocking to %d\n", val * GT_FREQUENCY_MULTIPLIER); - if (dev_priv->rps.cur_delay > val) - gen6_set_rps(dev_priv->dev, val); + if (dev_priv->rps.cur_delay > val) { + if (IS_VALLEYVIEW(dev_priv->dev)) + valleyview_set_rps(dev_priv->dev, val); + else + gen6_set_rps(dev_priv->dev, val); + } dev_priv->rps.max_delay = val; @@ -282,7 +301,10 @@ static ssize_t gt_min_freq_mhz_show(struct device *kdev, struct device_attribute int ret; mutex_lock(&dev_priv->rps.hw_lock); - ret = dev_priv->rps.min_delay * GT_FREQUENCY_MULTIPLIER; + if (IS_VALLEYVIEW(dev_priv->dev)) + ret = vlv_gpu_freq(dev_priv->mem_freq, dev_priv->rps.min_delay); + else + ret = dev_priv->rps.min_delay * GT_FREQUENCY_MULTIPLIER; mutex_unlock(&dev_priv->rps.hw_lock); return snprintf(buf, PAGE_SIZE, "%d\n", ret); @@ -302,21 +324,32 @@ static ssize_t gt_min_freq_mhz_store(struct device *kdev, if (ret) return ret; - val /= GT_FREQUENCY_MULTIPLIER; - mutex_lock(&dev_priv->rps.hw_lock); - rp_state_cap = I915_READ(GEN6_RP_STATE_CAP); - hw_max = dev_priv->rps.hw_max; - hw_min = ((rp_state_cap & 0xff0000) >> 16); + if (IS_VALLEYVIEW(dev)) { + val = vlv_freq_opcode(dev_priv->mem_freq, val); + + hw_max = valleyview_rps_max_freq(dev_priv); + hw_min = valleyview_rps_min_freq(dev_priv); + } else { + val /= GT_FREQUENCY_MULTIPLIER; + + rp_state_cap = I915_READ(GEN6_RP_STATE_CAP); + hw_max = dev_priv->rps.hw_max; + hw_min = ((rp_state_cap & 0xff0000) >> 16); + } if (val < hw_min || val > hw_max || val > dev_priv->rps.max_delay) { mutex_unlock(&dev_priv->rps.hw_lock); return -EINVAL; } - if (dev_priv->rps.cur_delay < val) - gen6_set_rps(dev_priv->dev, val); + if (dev_priv->rps.cur_delay < val) { + if (IS_VALLEYVIEW(dev)) + valleyview_set_rps(dev, val); + else + gen6_set_rps(dev_priv->dev, val); + } dev_priv->rps.min_delay = val; diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index f802368e8e9d..2557926553b3 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -2481,6 +2481,52 @@ void gen6_set_rps(struct drm_device *dev, u8 val) trace_intel_gpu_freq_change(val * 50); } +void valleyview_set_rps(struct drm_device *dev, u8 val) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + unsigned long timeout = jiffies + msecs_to_jiffies(10); + u32 limits = gen6_rps_limits(dev_priv, &val); + u32 pval; + + WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock)); + WARN_ON(val > dev_priv->rps.max_delay); + WARN_ON(val < dev_priv->rps.min_delay); + + DRM_DEBUG_DRIVER("gpu freq request from %d to %d\n", + vlv_gpu_freq(dev_priv->mem_freq, + dev_priv->rps.cur_delay), + vlv_gpu_freq(dev_priv->mem_freq, val)); + + if (val == dev_priv->rps.cur_delay) + return; + + valleyview_punit_write(dev_priv, PUNIT_REG_GPU_FREQ_REQ, val); + + do { + valleyview_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS, &pval); + if (time_after(jiffies, timeout)) { + DRM_DEBUG_DRIVER("timed out waiting for Punit\n"); + break; + } + udelay(10); + } while (pval & 1); + + valleyview_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS, &pval); + if ((pval >> 8) != val) + DRM_DEBUG_DRIVER("punit overrode freq: %d requested, but got %d\n", + val, pval >> 8); + + /* Make sure we continue to get interrupts + * until we hit the minimum or maximum frequencies. + */ + I915_WRITE(GEN6_RP_INTERRUPT_LIMITS, limits); + + dev_priv->rps.cur_delay = pval >> 8; + + trace_intel_gpu_freq_change(vlv_gpu_freq(dev_priv->mem_freq, val)); +} + + static void gen6_disable_rps(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -2742,6 +2788,127 @@ static void gen6_update_ring_freq(struct drm_device *dev) } } +int valleyview_rps_max_freq(struct drm_i915_private *dev_priv) +{ + u32 val, rp0; + + valleyview_nc_read(dev_priv, IOSF_NC_FB_GFX_FREQ_FUSE, &val); + + rp0 = (val & FB_GFX_MAX_FREQ_FUSE_MASK) >> FB_GFX_MAX_FREQ_FUSE_SHIFT; + /* Clamp to max */ + rp0 = min_t(u32, rp0, 0xea); + + return rp0; +} + +static int valleyview_rps_rpe_freq(struct drm_i915_private *dev_priv) +{ + u32 val, rpe; + + valleyview_nc_read(dev_priv, IOSF_NC_FB_GFX_FMAX_FUSE_LO, &val); + rpe = (val & FB_FMAX_VMIN_FREQ_LO_MASK) >> FB_FMAX_VMIN_FREQ_LO_SHIFT; + valleyview_nc_read(dev_priv, IOSF_NC_FB_GFX_FMAX_FUSE_HI, &val); + rpe |= (val & FB_FMAX_VMIN_FREQ_HI_MASK) << 5; + + return rpe; +} + +int valleyview_rps_min_freq(struct drm_i915_private *dev_priv) +{ + u32 val; + + valleyview_punit_read(dev_priv, PUNIT_REG_GPU_LFM, &val); + + return val & 0xff; +} + +static void valleyview_enable_rps(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_ring_buffer *ring; + u32 gtfifodbg, val, rpe; + int i; + + WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock)); + + if ((gtfifodbg = I915_READ(GTFIFODBG))) { + DRM_ERROR("GT fifo had a previous error %x\n", gtfifodbg); + I915_WRITE(GTFIFODBG, gtfifodbg); + } + + gen6_gt_force_wake_get(dev_priv); + + I915_WRITE(GEN6_RP_UP_THRESHOLD, 59400); + I915_WRITE(GEN6_RP_DOWN_THRESHOLD, 245000); + I915_WRITE(GEN6_RP_UP_EI, 66000); + I915_WRITE(GEN6_RP_DOWN_EI, 350000); + + I915_WRITE(GEN6_RP_IDLE_HYSTERSIS, 10); + + I915_WRITE(GEN6_RP_CONTROL, + GEN6_RP_MEDIA_TURBO | + GEN6_RP_MEDIA_HW_NORMAL_MODE | + GEN6_RP_MEDIA_IS_GFX | + GEN6_RP_ENABLE | + GEN6_RP_UP_BUSY_AVG | + GEN6_RP_DOWN_IDLE_CONT); + + I915_WRITE(GEN6_RC6_WAKE_RATE_LIMIT, 0x00280000); + I915_WRITE(GEN6_RC_EVALUATION_INTERVAL, 125000); + I915_WRITE(GEN6_RC_IDLE_HYSTERSIS, 25); + + for_each_ring(ring, dev_priv, i) + I915_WRITE(RING_MAX_IDLE(ring->mmio_base), 10); + + I915_WRITE(GEN6_RC6_THRESHOLD, 0xc350); + + /* allows RC6 residency counter to work */ + I915_WRITE(0x138104, _MASKED_BIT_ENABLE(0x3)); + I915_WRITE(GEN6_RC_CONTROL, + GEN7_RC_CTL_TO_MODE); + + valleyview_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS, &val); + dev_priv->mem_freq = 800 + (266 * (val >> 6) & 3); + DRM_DEBUG_DRIVER("DDR speed: %d MHz", dev_priv->mem_freq); + + DRM_DEBUG_DRIVER("GPLL enabled? %s\n", val & 0x10 ? "yes" : "no"); + DRM_DEBUG_DRIVER("GPU status: 0x%08x\n", val); + + DRM_DEBUG_DRIVER("current GPU freq: %d\n", + vlv_gpu_freq(dev_priv->mem_freq, (val >> 8) & 0xff)); + dev_priv->rps.cur_delay = (val >> 8) & 0xff; + + dev_priv->rps.max_delay = valleyview_rps_max_freq(dev_priv); + dev_priv->rps.hw_max = dev_priv->rps.max_delay; + DRM_DEBUG_DRIVER("max GPU freq: %d\n", vlv_gpu_freq(dev_priv->mem_freq, + dev_priv->rps.max_delay)); + + rpe = valleyview_rps_rpe_freq(dev_priv); + DRM_DEBUG_DRIVER("RPe GPU freq: %d\n", + vlv_gpu_freq(dev_priv->mem_freq, rpe)); + + val = valleyview_rps_min_freq(dev_priv); + DRM_DEBUG_DRIVER("min GPU freq: %d\n", vlv_gpu_freq(dev_priv->mem_freq, + val)); + dev_priv->rps.min_delay = val; + + DRM_DEBUG_DRIVER("setting GPU freq to %d\n", + vlv_gpu_freq(dev_priv->mem_freq, rpe)); + + valleyview_set_rps(dev_priv->dev, rpe); + + /* requires MSI enabled */ + I915_WRITE(GEN6_PMIER, GEN6_PM_DEFERRED_EVENTS); + spin_lock_irq(&dev_priv->rps.lock); + WARN_ON(dev_priv->rps.pm_iir != 0); + I915_WRITE(GEN6_PMIMR, 0); + spin_unlock_irq(&dev_priv->rps.lock); + /* enable all PM interrupts */ + I915_WRITE(GEN6_PMINTRMSK, 0); + + gen6_gt_force_wake_put(dev_priv); +} + void ironlake_teardown_rc6(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -3468,7 +3635,7 @@ void intel_disable_gt_powersave(struct drm_device *dev) if (IS_IRONLAKE_M(dev)) { ironlake_disable_drps(dev); ironlake_disable_rc6(dev); - } else if (INTEL_INFO(dev)->gen >= 6 && !IS_VALLEYVIEW(dev)) { + } else if (INTEL_INFO(dev)->gen >= 6) { cancel_delayed_work_sync(&dev_priv->rps.delayed_resume_work); mutex_lock(&dev_priv->rps.hw_lock); gen6_disable_rps(dev); @@ -3484,8 +3651,13 @@ static void intel_gen6_powersave_work(struct work_struct *work) struct drm_device *dev = dev_priv->dev; mutex_lock(&dev_priv->rps.hw_lock); - gen6_enable_rps(dev); - gen6_update_ring_freq(dev); + + if (IS_VALLEYVIEW(dev)) { + valleyview_enable_rps(dev); + } else { + gen6_enable_rps(dev); + gen6_update_ring_freq(dev); + } mutex_unlock(&dev_priv->rps.hw_lock); } @@ -3497,7 +3669,7 @@ void intel_enable_gt_powersave(struct drm_device *dev) ironlake_enable_drps(dev); ironlake_enable_rc6(dev); intel_init_emon(dev); - } else if ((IS_GEN6(dev) || IS_GEN7(dev)) && !IS_VALLEYVIEW(dev)) { + } else if (IS_GEN6(dev) || IS_GEN7(dev)) { /* * PCU communication is slow and this doesn't need to be * done at any specific time, so do this out of our fast path @@ -4568,14 +4740,13 @@ int sandybridge_pcode_write(struct drm_i915_private *dev_priv, u8 mbox, u32 val) return 0; } -static int vlv_punit_rw(struct drm_i915_private *dev_priv, u8 opcode, +static int vlv_punit_rw(struct drm_i915_private *dev_priv, u32 port, u8 opcode, u8 addr, u32 *val) { - u32 cmd, devfn, port, be, bar; + u32 cmd, devfn, be, bar; bar = 0; be = 0xf; - port = IOSF_PORT_PUNIT; devfn = PCI_DEVFN(2, 0); cmd = (devfn << IOSF_DEVFN_SHIFT) | (opcode << IOSF_OPCODE_SHIFT) | @@ -4597,7 +4768,7 @@ static int vlv_punit_rw(struct drm_i915_private *dev_priv, u8 opcode, I915_WRITE(VLV_IOSF_DOORBELL_REQ, cmd); if (wait_for((I915_READ(VLV_IOSF_DOORBELL_REQ) & IOSF_SB_BUSY) == 0, - 500)) { + 5)) { DRM_ERROR("timeout waiting for pcode %s (%d) to finish\n", opcode == PUNIT_OPCODE_REG_READ ? "read" : "write", addr); @@ -4613,12 +4784,20 @@ static int vlv_punit_rw(struct drm_i915_private *dev_priv, u8 opcode, int valleyview_punit_read(struct drm_i915_private *dev_priv, u8 addr, u32 *val) { - return vlv_punit_rw(dev_priv, PUNIT_OPCODE_REG_READ, addr, val); + return vlv_punit_rw(dev_priv, IOSF_PORT_PUNIT, PUNIT_OPCODE_REG_READ, + addr, val); } int valleyview_punit_write(struct drm_i915_private *dev_priv, u8 addr, u32 val) { - return vlv_punit_rw(dev_priv, PUNIT_OPCODE_REG_WRITE, addr, &val); + return vlv_punit_rw(dev_priv, IOSF_PORT_PUNIT, PUNIT_OPCODE_REG_WRITE, + addr, &val); +} + +int valleyview_nc_read(struct drm_i915_private *dev_priv, u8 addr, u32 *val) +{ + return vlv_punit_rw(dev_priv, IOSF_PORT_NC, PUNIT_OPCODE_REG_READ, + addr, val); } int vlv_gpu_freq(int ddr_freq, int val) -- cgit v1.2.3 From 75e539864a133c4d8d6a4aac9f409e0a23d7bc3f Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 18 Apr 2013 21:10:43 +0200 Subject: drm/i915: fix VLV limits Magic updates. v2: use 64 bit types and math (Ville) v3: Trim out all the m/n/p calculation changes since they are still under discussion. Instead squash in a fixup for hdmi limits which slipped into a different patch. Signed-off-by: Pallavi G Signed-off-by: Vijay Purushothaman Signed-off-by: Yogesh M Signed-off-by: Gajanan Bhat Signed-off-by: Jesse Barnes (v2) Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 02faba0263de..bdbad762134c 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -396,15 +396,15 @@ static const intel_limit_t intel_limits_vlv_dac = { .m1 = { .min = 2, .max = 3 }, .m2 = { .min = 11, .max = 156 }, .p = { .min = 10, .max = 30 }, - .p1 = { .min = 2, .max = 3 }, + .p1 = { .min = 1, .max = 3 }, .p2 = { .dot_limit = 270000, .p2_slow = 2, .p2_fast = 20 }, .find_pll = intel_vlv_find_best_pll, }; static const intel_limit_t intel_limits_vlv_hdmi = { - .dot = { .min = 20000, .max = 165000 }, - .vco = { .min = 4000000, .max = 5994000}, + .dot = { .min = 25000, .max = 270000 }, + .vco = { .min = 4000000, .max = 6000000 }, .n = { .min = 1, .max = 7 }, .m = { .min = 60, .max = 300 }, /* guess */ .m1 = { .min = 2, .max = 3 }, @@ -424,7 +424,7 @@ static const intel_limit_t intel_limits_vlv_dp = { .m1 = { .min = 2, .max = 3 }, .m2 = { .min = 11, .max = 156 }, .p = { .min = 10, .max = 30 }, - .p1 = { .min = 2, .max = 3 }, + .p1 = { .min = 1, .max = 3 }, .p2 = { .dot_limit = 270000, .p2_slow = 2, .p2_fast = 20 }, .find_pll = intel_vlv_find_best_pll, -- cgit v1.2.3 From 598fac6bf8299ed7ae1852426660be3c265047a4 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 18 Apr 2013 22:01:46 +0200 Subject: drm/i915: magic VLV PLL registers in the dpio sideband Stolen from a patch with the below impressive sob-section. Signed-off-by: Pallavi G Signed-off-by: Vijay Purushothaman Signed-off-by: Gajanan Bhat Signed-off-by: Ben Widawsky Signed-off-by: Jesse Barnes [danvet: Drop everything but the header #defines.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 118 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 116 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 66fb8dd28225..fb1a4fa68d55 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -353,6 +353,8 @@ * 0x8100: fast clock controls * * DPIO is VLV only. + * + * Note: digital port B is DDI0, digital pot C is DDI1 */ #define DPIO_PKT (VLV_DISPLAY_BASE + 0x2100) #define DPIO_RID (0<<24) @@ -369,8 +371,20 @@ #define DPIO_SFR_BYPASS (1<<1) #define DPIO_RESET (1<<0) +#define _DPIO_TX3_SWING_CTL4_A 0x690 +#define _DPIO_TX3_SWING_CTL4_B 0x2a90 +#define DPIO_TX3_SWING_CTL4(pipe) _PIPE(pipe, _DPIO_TX_SWING_CTL4_A, \ + _DPIO_TX3_SWING_CTL4_B) + +/* + * Per pipe/PLL DPIO regs + */ #define _DPIO_DIV_A 0x800c #define DPIO_POST_DIV_SHIFT (28) /* 3 bits */ +#define DPIO_POST_DIV_DAC 0 +#define DPIO_POST_DIV_HDMIDP 1 /* DAC 225-400M rate */ +#define DPIO_POST_DIV_LVDS1 2 +#define DPIO_POST_DIV_LVDS2 3 #define DPIO_K_SHIFT (24) /* 4 bits */ #define DPIO_P1_SHIFT (21) /* 3 bits */ #define DPIO_P2_SHIFT (16) /* 5 bits */ @@ -396,14 +410,111 @@ #define _DPIO_CORE_CLK_B 0x803c #define DPIO_CORE_CLK(pipe) _PIPE(pipe, _DPIO_CORE_CLK_A, _DPIO_CORE_CLK_B) +#define _DPIO_IREF_CTL_A 0x8040 +#define _DPIO_IREF_CTL_B 0x8060 +#define DPIO_IREF_CTL(pipe) _PIPE(pipe, _DPIO_IREF_CTL_A, _DPIO_IREF_CTL_B) + +#define DPIO_IREF_BCAST 0xc044 +#define _DPIO_IREF_A 0x8044 +#define _DPIO_IREF_B 0x8064 +#define DPIO_IREF(pipe) _PIPE(pipe, _DPIO_IREF_A, _DPIO_IREF_B) + +#define _DPIO_PLL_CML_A 0x804c +#define _DPIO_PLL_CML_B 0x806c +#define DPIO_PLL_CML(pipe) _PIPE(pipe, _DPIO_PLL_CML_A, _DPIO_PLL_CML_B) + #define _DPIO_LFP_COEFF_A 0x8048 #define _DPIO_LFP_COEFF_B 0x8068 #define DPIO_LFP_COEFF(pipe) _PIPE(pipe, _DPIO_LFP_COEFF_A, _DPIO_LFP_COEFF_B) +#define DPIO_CALIBRATION 0x80ac + #define DPIO_FASTCLK_DISABLE 0x8100 -#define DPIO_DATA_CHANNEL1 0x8220 -#define DPIO_DATA_CHANNEL2 0x8420 +/* + * Per DDI channel DPIO regs + */ + +#define _DPIO_PCS_TX_0 0x8200 +#define _DPIO_PCS_TX_1 0x8400 +#define DPIO_PCS_TX_LANE2_RESET (1<<16) +#define DPIO_PCS_TX_LANE1_RESET (1<<7) +#define DPIO_PCS_TX(port) _PORT(port, _DPIO_PCS_TX_0, _DPIO_PCS_TX_1) + +#define _DPIO_PCS_CLK_0 0x8204 +#define _DPIO_PCS_CLK_1 0x8404 +#define DPIO_PCS_CLK_CRI_RXEB_EIOS_EN (1<<22) +#define DPIO_PCS_CLK_CRI_RXDIGFILTSG_EN (1<<21) +#define DPIO_PCS_CLK_DATAWIDTH_SHIFT (6) +#define DPIO_PCS_CLK_SOFT_RESET (1<<5) +#define DPIO_PCS_CLK(port) _PORT(port, _DPIO_PCS_CLK_0, _DPIO_PCS_CLK_1) + +#define _DPIO_PCS_CTL_OVR1_A 0x8224 +#define _DPIO_PCS_CTL_OVR1_B 0x8424 +#define DPIO_PCS_CTL_OVER1(port) _PORT(port, _DPIO_PCS_CTL_OVR1_A, \ + _DPIO_PCS_CTL_OVR1_B) + +#define _DPIO_PCS_STAGGER0_A 0x822c +#define _DPIO_PCS_STAGGER0_B 0x842c +#define DPIO_PCS_STAGGER0(port) _PORT(port, _DPIO_PCS_STAGGER0_A, \ + _DPIO_PCS_STAGGER0_B) + +#define _DPIO_PCS_STAGGER1_A 0x8230 +#define _DPIO_PCS_STAGGER1_B 0x8430 +#define DPIO_PCS_STAGGER1(port) _PORT(port, _DPIO_PCS_STAGGER1_A, \ + _DPIO_PCS_STAGGER1_B) + +#define _DPIO_PCS_CLOCKBUF0_A 0x8238 +#define _DPIO_PCS_CLOCKBUF0_B 0x8438 +#define DPIO_PCS_CLOCKBUF0(port) _PORT(port, _DPIO_PCS_CLOCKBUF0_A, \ + _DPIO_PCS_CLOCKBUF0_B) + +#define _DPIO_PCS_CLOCKBUF8_A 0x825c +#define _DPIO_PCS_CLOCKBUF8_B 0x845c +#define DPIO_PCS_CLOCKBUF8(port) _PORT(port, _DPIO_PCS_CLOCKBUF8_A, \ + _DPIO_PCS_CLOCKBUF8_B) + +#define _DPIO_TX_SWING_CTL2_A 0x8288 +#define _DPIO_TX_SWING_CTL2_B 0x8488 +#define DPIO_TX_SWING_CTL2(port) _PORT(port, _DPIO_TX_SWING_CTL2_A, \ + _DPIO_TX_SWING_CTL2_B) + +#define _DPIO_TX_SWING_CTL3_A 0x828c +#define _DPIO_TX_SWING_CTL3_B 0x848c +#define DPIO_TX_SWING_CTL3(port) _PORT(port, _DPIO_TX_SWING_CTL3_A, \ + _DPIO_TX_SWING_CTL3_B) + +#define _DPIO_TX_SWING_CTL4_A 0x8290 +#define _DPIO_TX_SWING_CTL4_B 0x8490 +#define DPIO_TX_SWING_CTL4(port) _PORT(port, _DPIO_TX_SWING_CTL4_A, \ + _DPIO_TX_SWING_CTL4_B) + +#define _DPIO_TX_OCALINIT_0 0x8294 +#define _DPIO_TX_OCALINIT_1 0x8494 +#define DPIO_TX_OCALINIT_EN (1<<31) +#define DPIO_TX_OCALINIT(port) _PORT(port, _DPIO_TX_OCALINIT_0, \ + _DPIO_TX_OCALINIT_1) + +#define _DPIO_TX_CTL_0 0x82ac +#define _DPIO_TX_CTL_1 0x84ac +#define DPIO_TX_CTL(port) _PORT(port, _DPIO_TX_CTL_0, _DPIO_TX_CTL_1) + +#define _DPIO_TX_LANE_0 0x82b8 +#define _DPIO_TX_LANE_1 0x84b8 +#define DPIO_TX_LANE(port) _PORT(port, _DPIO_TX_LANE_0, _DPIO_TX_LANE_1) + +#define _DPIO_DATA_CHANNEL1 0x8220 +#define _DPIO_DATA_CHANNEL2 0x8420 +#define DPIO_DATA_CHANNEL(port) _PORT(port, _DPIO_DATA_CHANNEL1, _DPIO_DATA_CHANNEL2) + +#define _DPIO_PORT0_PCS0 0x0220 +#define _DPIO_PORT0_PCS1 0x0420 +#define _DPIO_PORT1_PCS2 0x2620 +#define _DPIO_PORT1_PCS3 0x2820 +#define DPIO_DATA_LANE_A(port) _PORT(port, _DPIO_PORT0_PCS0, _DPIO_PORT1_PCS2) +#define DPIO_DATA_LANE_B(port) _PORT(port, _DPIO_PORT0_PCS1, _DPIO_PORT1_PCS3) +#define DPIO_DATA_CHANNEL1 0x8220 +#define DPIO_DATA_CHANNEL2 0x8420 /* * Fence registers @@ -965,7 +1076,10 @@ #define DPLL_FPA01_P1_POST_DIV_MASK 0x00ff0000 /* i915 */ #define DPLL_FPA01_P1_POST_DIV_MASK_PINEVIEW 0x00ff8000 /* Pineview */ #define DPLL_LOCK_VLV (1<<15) +#define DPLL_INTEGRATED_CRI_CLK_VLV (1<<14) #define DPLL_INTEGRATED_CLOCK_VLV (1<<13) +#define DPLL_PORTC_READY_MASK (0xf << 4) +#define DPLL_PORTB_READY_MASK (0xf) #define DPLL_FPA01_P1_POST_DIV_MASK_I830 0x001f0000 /* -- cgit v1.2.3 From e2fa6fba3d2fa03a5efdedf0b6f045fcc3428a80 Mon Sep 17 00:00:00 2001 From: Pallavi G Date: Thu, 18 Apr 2013 14:44:28 -0700 Subject: drm/i915/dp: program VSwing and Preemphasis control settings on VLV v2 Program few Tx buffer Swing control settings through DPIO. v2: fix up codingstyle (Daniel) call from set_signal_levels (Ville, Daniel) use proper port numbers (Jesse) Signed-off-by: Pallavi G Signed-off-by: Yogesh M Signed-off-by: Gajanan Bhat Signed-off-by: Jesse Barnes (v2 changes) [danvet: Reorder if-ladder to avoid two IS_VLV checks.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 3 +- drivers/gpu/drm/i915/intel_dp.c | 128 ++++++++++++++++++++++++++++++++++- drivers/gpu/drm/i915/intel_drv.h | 2 + 3 files changed, 128 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index bdbad762134c..9ff992aabcd6 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -450,8 +450,7 @@ u32 intel_dpio_read(struct drm_i915_private *dev_priv, int reg) return I915_READ(DPIO_DATA); } -static void intel_dpio_write(struct drm_i915_private *dev_priv, int reg, - u32 val) +void intel_dpio_write(struct drm_i915_private *dev_priv, int reg, u32 val) { WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 89f89b727900..9e16b0ca8dd8 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1461,7 +1461,9 @@ intel_dp_voltage_max(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp_to_dev(intel_dp); - if (IS_GEN7(dev) && is_cpu_edp(intel_dp)) + if (IS_VALLEYVIEW(dev)) + return DP_TRAIN_VOLTAGE_SWING_1200; + else if (IS_GEN7(dev) && is_cpu_edp(intel_dp)) return DP_TRAIN_VOLTAGE_SWING_800; else if (HAS_PCH_CPT(dev) && !is_cpu_edp(intel_dp)) return DP_TRAIN_VOLTAGE_SWING_1200; @@ -1486,7 +1488,19 @@ intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t voltage_swing) default: return DP_TRAIN_PRE_EMPHASIS_0; } - } else if (IS_GEN7(dev) && is_cpu_edp(intel_dp) && !IS_VALLEYVIEW(dev)) { + } else if (IS_VALLEYVIEW(dev)) { + switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) { + case DP_TRAIN_VOLTAGE_SWING_400: + return DP_TRAIN_PRE_EMPHASIS_9_5; + case DP_TRAIN_VOLTAGE_SWING_600: + return DP_TRAIN_PRE_EMPHASIS_6; + case DP_TRAIN_VOLTAGE_SWING_800: + return DP_TRAIN_PRE_EMPHASIS_3_5; + case DP_TRAIN_VOLTAGE_SWING_1200: + default: + return DP_TRAIN_PRE_EMPHASIS_0; + } + } else if (IS_GEN7(dev) && is_cpu_edp(intel_dp)) { switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) { case DP_TRAIN_VOLTAGE_SWING_400: return DP_TRAIN_PRE_EMPHASIS_6; @@ -1511,6 +1525,111 @@ intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t voltage_swing) } } +static uint32_t intel_vlv_signal_levels(struct intel_dp *intel_dp) +{ + struct drm_device *dev = intel_dp_to_dev(intel_dp); + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_digital_port *dport = dp_to_dig_port(intel_dp); + unsigned long demph_reg_value, preemph_reg_value, + uniqtranscale_reg_value; + uint8_t train_set = intel_dp->train_set[0]; + int port; + + if (dport->port == PORT_B) + port = 0; + else if (dport->port == PORT_C) + port = 1; + else + BUG(); + + switch (train_set & DP_TRAIN_PRE_EMPHASIS_MASK) { + case DP_TRAIN_PRE_EMPHASIS_0: + preemph_reg_value = 0x0004000; + switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) { + case DP_TRAIN_VOLTAGE_SWING_400: + demph_reg_value = 0x2B405555; + uniqtranscale_reg_value = 0x552AB83A; + break; + case DP_TRAIN_VOLTAGE_SWING_600: + demph_reg_value = 0x2B404040; + uniqtranscale_reg_value = 0x5548B83A; + break; + case DP_TRAIN_VOLTAGE_SWING_800: + demph_reg_value = 0x2B245555; + uniqtranscale_reg_value = 0x5560B83A; + break; + case DP_TRAIN_VOLTAGE_SWING_1200: + demph_reg_value = 0x2B405555; + uniqtranscale_reg_value = 0x5598DA3A; + break; + default: + return 0; + } + break; + case DP_TRAIN_PRE_EMPHASIS_3_5: + preemph_reg_value = 0x0002000; + switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) { + case DP_TRAIN_VOLTAGE_SWING_400: + demph_reg_value = 0x2B404040; + uniqtranscale_reg_value = 0x5552B83A; + break; + case DP_TRAIN_VOLTAGE_SWING_600: + demph_reg_value = 0x2B404848; + uniqtranscale_reg_value = 0x5580B83A; + break; + case DP_TRAIN_VOLTAGE_SWING_800: + demph_reg_value = 0x2B404040; + uniqtranscale_reg_value = 0x55ADDA3A; + break; + default: + return 0; + } + break; + case DP_TRAIN_PRE_EMPHASIS_6: + preemph_reg_value = 0x0000000; + switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) { + case DP_TRAIN_VOLTAGE_SWING_400: + demph_reg_value = 0x2B305555; + uniqtranscale_reg_value = 0x5570B83A; + break; + case DP_TRAIN_VOLTAGE_SWING_600: + demph_reg_value = 0x2B2B4040; + uniqtranscale_reg_value = 0x55ADDA3A; + break; + default: + return 0; + } + break; + case DP_TRAIN_PRE_EMPHASIS_9_5: + preemph_reg_value = 0x0006000; + switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) { + case DP_TRAIN_VOLTAGE_SWING_400: + demph_reg_value = 0x1B405555; + uniqtranscale_reg_value = 0x55ADDA3A; + break; + default: + return 0; + } + break; + default: + return 0; + } + + /* eDP is only on port C */ + mutex_lock(&dev_priv->dpio_lock); + intel_dpio_write(dev_priv, DPIO_TX_OCALINIT(port), 0x00000000); + intel_dpio_write(dev_priv, DPIO_TX_SWING_CTL4(port), demph_reg_value); + intel_dpio_write(dev_priv, DPIO_TX_SWING_CTL2(port), + uniqtranscale_reg_value); + intel_dpio_write(dev_priv, DPIO_TX_SWING_CTL3(port), 0x0C782040); + intel_dpio_write(dev_priv, DPIO_PCS_STAGGER0(port), 0x00030000); + intel_dpio_write(dev_priv, DPIO_PCS_CTL_OVER1(port), preemph_reg_value); + intel_dpio_write(dev_priv, DPIO_TX_OCALINIT(port), 0x80000000); + mutex_unlock(&dev_priv->dpio_lock); + + return 0; +} + static void intel_get_adjust_train(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_STATUS_SIZE]) { @@ -1685,7 +1804,10 @@ intel_dp_set_signal_levels(struct intel_dp *intel_dp, uint32_t *DP) if (HAS_DDI(dev)) { signal_levels = intel_hsw_signal_levels(train_set); mask = DDI_BUF_EMP_MASK; - } else if (IS_GEN7(dev) && is_cpu_edp(intel_dp) && !IS_VALLEYVIEW(dev)) { + } else if (IS_VALLEYVIEW(dev)) { + signal_levels = intel_vlv_signal_levels(intel_dp); + mask = 0; + } else if (IS_GEN7(dev) && is_cpu_edp(intel_dp)) { signal_levels = intel_gen7_edp_signal_levels(train_set); mask = EDP_LINK_TRAIN_VOL_EMP_MASK_IVB; } else if (IS_GEN6(dev) && is_cpu_edp(intel_dp)) { diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index b5b6d19e6dd3..399b18181b45 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -689,6 +689,8 @@ extern int intel_sprite_get_colorkey(struct drm_device *dev, void *data, struct drm_file *file_priv); extern u32 intel_dpio_read(struct drm_i915_private *dev_priv, int reg); +extern void intel_dpio_write(struct drm_i915_private *dev_priv, int reg, + u32 val); /* Power-related functions, located in intel_pm.c */ extern void intel_init_pm(struct drm_device *dev); -- cgit v1.2.3 From 78c9b7e71d90f32ce19d4a575a1a206cfe7c6a00 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 18 Apr 2013 14:44:29 -0700 Subject: drm/i915: drop init_dpio, shouldn't be needed This is a reset feature we don't actually need. Signed-off-by: Jesse Barnes [danvet: Make it compile.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 9ff992aabcd6..83465622be33 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -467,17 +467,6 @@ void intel_dpio_write(struct drm_i915_private *dev_priv, int reg, u32 val) DRM_ERROR("DPIO write wait timed out\n"); } -static void vlv_init_dpio(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - - /* Reset the DPIO config */ - I915_WRITE(DPIO_CTL, 0); - POSTING_READ(DPIO_CTL); - I915_WRITE(DPIO_CTL, 1); - POSTING_READ(DPIO_CTL); -} - static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc, int refclk) { @@ -9429,9 +9418,6 @@ void intel_modeset_cleanup(struct drm_device *dev) ironlake_teardown_rc6(dev); - if (IS_VALLEYVIEW(dev)) - vlv_init_dpio(dev); - mutex_unlock(&dev->struct_mutex); /* Disable the irq before mode object teardown, for the irq might -- cgit v1.2.3 From 89b667f86a62a99a7b484a7e1b3f8f7a108a7dee Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 18 Apr 2013 14:51:36 -0700 Subject: drm/i915: update VLV PLL and DPIO code v11 In Valleyview voltage swing, pre-emphasis and lane control registers can be programmed only through the h/w side band fabric. Update vlv_update_pll, i9xx_crtc_enable, and intel_enable_pll with the appropriate programming. We need to make sure that the tx lane reset occurs in both the full mode set and DPMS paths, so factor things out to allow that. v2: use different DPIO_DIVISOR values for VGA and DisplayPort v3: Fix update pll logic to use same DPIO_DIVISOR & DPIO_REFSFR values for all display interfaces v4: collapse with various updates v5: squash with crtc enable/pll enable bits v6: split out DP code (jbarnes) put phyready check under IS_VALLEYVIEW (jbarnes) remove unneeded check in 9xx pll div update (Jani) wrap VLV pll update call in IS_VALLEYVIEW (Jani) move port enable back to end of crtc enable (jbarnes) put phyready check under IS_VALLEYVIEW (jbarnes) v7: fix up conflicts against latest drm-intel-next-queued v8: use DPIO reg names, fix pipes (Jani) from mPhy_registers_VLV2_ww20p5 doc v9: update to latest info from driver enabling notes doc driver_vbios_notes_9 v10: fixup a bit of pipe/port confusion to allow eDP and HDMI to work simultaneously (Jesse) v11: use pll/port callbacks for DPIO port activity (Daniel) use separate VLV CRTC enable function (Daniel) move around port ready checks (Jesse) Signed-off-by: Pallavi G Signed-off-by: Vijay Purushothaman Signed-off-by: Gajanan Bhat Signed-off-by: Ben Widawsky Signed-off-by: Jesse Barnes [danvet: Drop pfit changes and add a little comment explaining that vlv has a different enable sequence and so needs it's own crtc_enable callback. Also apply a fixup patch from Wu Fengguang to shut up some compiler warnings.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 237 +++++++++++++++++++++++++++-------- drivers/gpu/drm/i915/intel_dp.c | 69 +++++++++- drivers/gpu/drm/i915/intel_drv.h | 14 +++ drivers/gpu/drm/i915/intel_hdmi.c | 108 ++++++++++++++++ 4 files changed, 370 insertions(+), 58 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 83465622be33..3fadd33c772f 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1576,6 +1576,20 @@ intel_sbi_read(struct drm_i915_private *dev_priv, u16 reg, return I915_READ(SBI_DATA); } +void vlv_wait_port_ready(struct drm_i915_private *dev_priv, int port) +{ + u32 port_mask; + + if (!port) + port_mask = DPLL_PORTB_READY_MASK; + else + port_mask = DPLL_PORTC_READY_MASK; + + if (wait_for((I915_READ(DPLL(0)) & port_mask) == 0, 1000)) + WARN(1, "timed out waiting for port %c ready: 0x%08x\n", + 'B' + port, I915_READ(DPLL(0))); +} + /** * ironlake_enable_pch_pll - enable PCH PLL * @dev_priv: i915 private structure @@ -3678,6 +3692,52 @@ g4x_fixup_plane(struct drm_i915_private *dev_priv, enum pipe pipe) } } +static void valleyview_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); + struct intel_encoder *encoder; + int pipe = intel_crtc->pipe; + int plane = intel_crtc->plane; + + WARN_ON(!crtc->enabled); + + if (intel_crtc->active) + return; + + intel_crtc->active = true; + intel_update_watermarks(dev); + + mutex_lock(&dev_priv->dpio_lock); + + for_each_encoder_on_crtc(dev, crtc, encoder) + if (encoder->pre_pll_enable) + encoder->pre_pll_enable(encoder); + + intel_enable_pll(dev_priv, pipe); + + for_each_encoder_on_crtc(dev, crtc, encoder) + if (encoder->pre_enable) + encoder->pre_enable(encoder); + + /* VLV wants encoder enabling _before_ the pipe is up. */ + for_each_encoder_on_crtc(dev, crtc, encoder) + encoder->enable(encoder); + + intel_enable_pipe(dev_priv, pipe, false); + intel_enable_plane(dev_priv, plane, pipe); + + 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); + + mutex_unlock(&dev_priv->dpio_lock); +} + static void i9xx_crtc_enable(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; @@ -3766,6 +3826,10 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc) i9xx_pfit_disable(intel_crtc); + for_each_encoder_on_crtc(dev, crtc, encoder) + if (encoder->post_disable) + encoder->post_disable(encoder); + intel_disable_pll(dev_priv, pipe); intel_crtc->active = false; @@ -4208,6 +4272,34 @@ static void i9xx_update_pll_dividers(struct intel_crtc *crtc, } } +static void vlv_pllb_recal_opamp(struct drm_i915_private *dev_priv) +{ + u32 reg_val; + + /* + * PLLB opamp always calibrates to max value of 0x3f, force enable it + * and set it to a reasonable value instead. + */ + reg_val = intel_dpio_read(dev_priv, DPIO_IREF(1)); + reg_val &= 0xffffff00; + reg_val |= 0x00000030; + intel_dpio_write(dev_priv, DPIO_IREF(1), reg_val); + + reg_val = intel_dpio_read(dev_priv, DPIO_CALIBRATION); + reg_val &= 0x8cffffff; + reg_val = 0x8c000000; + intel_dpio_write(dev_priv, DPIO_CALIBRATION, reg_val); + + reg_val = intel_dpio_read(dev_priv, DPIO_IREF(1)); + reg_val &= 0xffffff00; + intel_dpio_write(dev_priv, DPIO_IREF(1), reg_val); + + reg_val = intel_dpio_read(dev_priv, DPIO_CALIBRATION); + reg_val &= 0x00ffffff; + reg_val |= 0xb0000000; + intel_dpio_write(dev_priv, DPIO_CALIBRATION, reg_val); +} + static void intel_dp_set_m_n(struct intel_crtc *crtc) { if (crtc->config.has_pch_encoder) @@ -4220,24 +4312,18 @@ static void vlv_update_pll(struct intel_crtc *crtc) { struct drm_device *dev = crtc->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_display_mode *adjusted_mode = + &crtc->config.adjusted_mode; + struct intel_encoder *encoder; int pipe = crtc->pipe; - u32 dpll, mdiv, pdiv; + u32 dpll, mdiv; u32 bestn, bestm1, bestm2, bestp1, bestp2; - bool is_sdvo; - u32 temp; + bool is_hdmi; + u32 coreclk, reg_val, temp; mutex_lock(&dev_priv->dpio_lock); - is_sdvo = intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_SDVO) || - intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_HDMI); - - dpll = DPLL_VGA_MODE_DIS; - dpll |= DPLL_EXT_BUFFER_ENABLE_VLV; - dpll |= DPLL_REFA_CLK_ENABLE_VLV; - dpll |= DPLL_INTEGRATED_CLOCK_VLV; - - I915_WRITE(DPLL(pipe), dpll); - POSTING_READ(DPLL(pipe)); + is_hdmi = intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_HDMI); bestn = crtc->config.dpll.n; bestm1 = crtc->config.dpll.m1; @@ -4245,71 +4331,105 @@ static void vlv_update_pll(struct intel_crtc *crtc) bestp1 = crtc->config.dpll.p1; bestp2 = crtc->config.dpll.p2; - /* - * In Valleyview PLL and program lane counter registers are exposed - * through DPIO interface - */ + /* See eDP HDMI DPIO driver vbios notes doc */ + + /* PLL B needs special handling */ + if (pipe) + vlv_pllb_recal_opamp(dev_priv); + + /* Set up Tx target for periodic Rcomp update */ + intel_dpio_write(dev_priv, DPIO_IREF_BCAST, 0x0100000f); + + /* Disable target IRef on PLL */ + reg_val = intel_dpio_read(dev_priv, DPIO_IREF_CTL(pipe)); + reg_val &= 0x00ffffff; + intel_dpio_write(dev_priv, DPIO_IREF_CTL(pipe), reg_val); + + /* Disable fast lock */ + intel_dpio_write(dev_priv, DPIO_FASTCLK_DISABLE, 0x610); + + /* Set idtafcrecal before PLL is enabled */ mdiv = ((bestm1 << DPIO_M1DIV_SHIFT) | (bestm2 & DPIO_M2DIV_MASK)); mdiv |= ((bestp1 << DPIO_P1_SHIFT) | (bestp2 << DPIO_P2_SHIFT)); mdiv |= ((bestn << DPIO_N_SHIFT)); - mdiv |= (1 << DPIO_POST_DIV_SHIFT); mdiv |= (1 << DPIO_K_SHIFT); - mdiv |= DPIO_ENABLE_CALIBRATION; + if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_HDMI) || + intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_EDP) || + intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_DISPLAYPORT)) + mdiv |= (DPIO_POST_DIV_HDMIDP << DPIO_POST_DIV_SHIFT); intel_dpio_write(dev_priv, DPIO_DIV(pipe), mdiv); - intel_dpio_write(dev_priv, DPIO_CORE_CLK(pipe), 0x01000000); + mdiv |= DPIO_ENABLE_CALIBRATION; + intel_dpio_write(dev_priv, DPIO_DIV(pipe), mdiv); - pdiv = (1 << DPIO_REFSEL_OVERRIDE) | (5 << DPIO_PLL_MODESEL_SHIFT) | - (3 << DPIO_BIAS_CURRENT_CTL_SHIFT) | (1<<20) | - (7 << DPIO_PLL_REFCLK_SEL_SHIFT) | (8 << DPIO_DRIVER_CTL_SHIFT) | - (5 << DPIO_CLK_BIAS_CTL_SHIFT); - intel_dpio_write(dev_priv, DPIO_REFSFR(pipe), pdiv); + /* Set HBR and RBR LPF coefficients */ + if (adjusted_mode->clock == 162000 || + intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_HDMI)) + intel_dpio_write(dev_priv, DPIO_LFP_COEFF(pipe), + 0x005f0021); + else + intel_dpio_write(dev_priv, DPIO_LFP_COEFF(pipe), + 0x00d0000f); + + if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_EDP) || + intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_DISPLAYPORT)) { + /* Use SSC source */ + if (!pipe) + intel_dpio_write(dev_priv, DPIO_REFSFR(pipe), + 0x0df40000); + else + intel_dpio_write(dev_priv, DPIO_REFSFR(pipe), + 0x0df70000); + } else { /* HDMI or VGA */ + /* Use bend source */ + if (!pipe) + intel_dpio_write(dev_priv, DPIO_REFSFR(pipe), + 0x0df70000); + else + intel_dpio_write(dev_priv, DPIO_REFSFR(pipe), + 0x0df40000); + } - intel_dpio_write(dev_priv, DPIO_LFP_COEFF(pipe), 0x005f003b); + coreclk = intel_dpio_read(dev_priv, DPIO_CORE_CLK(pipe)); + coreclk = (coreclk & 0x0000ff00) | 0x01c00000; + if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_DISPLAYPORT) || + intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_EDP)) + coreclk |= 0x01000000; + intel_dpio_write(dev_priv, DPIO_CORE_CLK(pipe), coreclk); - dpll |= DPLL_VCO_ENABLE; - I915_WRITE(DPLL(pipe), dpll); - POSTING_READ(DPLL(pipe)); - if (wait_for(((I915_READ(DPLL(pipe)) & DPLL_LOCK_VLV) == DPLL_LOCK_VLV), 1)) - DRM_ERROR("DPLL %d failed to lock\n", pipe); + intel_dpio_write(dev_priv, DPIO_PLL_CML(pipe), 0x87871000); - intel_dpio_write(dev_priv, DPIO_FASTCLK_DISABLE, 0x620); + for_each_encoder_on_crtc(dev, &crtc->base, encoder) + if (encoder->pre_pll_enable) + encoder->pre_pll_enable(encoder); - if (crtc->config.has_dp_encoder) - intel_dp_set_m_n(crtc); + /* Enable DPIO clock input */ + dpll = DPLL_EXT_BUFFER_ENABLE_VLV | DPLL_REFA_CLK_ENABLE_VLV | + DPLL_VGA_MODE_DIS | DPLL_INTEGRATED_CLOCK_VLV; + if (pipe) + dpll |= DPLL_INTEGRATED_CRI_CLK_VLV; + dpll |= DPLL_VCO_ENABLE; I915_WRITE(DPLL(pipe), dpll); - - /* Wait for the clocks to stabilize. */ POSTING_READ(DPLL(pipe)); udelay(150); - temp = 0; - if (is_sdvo) { + if (wait_for(((I915_READ(DPLL(pipe)) & DPLL_LOCK_VLV) == DPLL_LOCK_VLV), 1)) + DRM_ERROR("DPLL %d failed to lock\n", pipe); + + if (is_hdmi) { temp = 0; if (crtc->config.pixel_multiplier > 1) { temp = (crtc->config.pixel_multiplier - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT; } - } - I915_WRITE(DPLL_MD(pipe), temp); - POSTING_READ(DPLL_MD(pipe)); - /* Now program lane control registers */ - if(intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_DISPLAYPORT) - || intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_HDMI)) { - temp = 0x1000C4; - if(pipe == 1) - temp |= (1 << 21); - intel_dpio_write(dev_priv, DPIO_DATA_CHANNEL1, temp); + I915_WRITE(DPLL_MD(pipe), temp); + POSTING_READ(DPLL_MD(pipe)); } - if(intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_EDP)) { - temp = 0x1000C4; - if(pipe == 1) - temp |= (1 << 21); - intel_dpio_write(dev_priv, DPIO_DATA_CHANNEL2, temp); - } + if (crtc->config.has_dp_encoder) + intel_dp_set_m_n(crtc); mutex_unlock(&dev_priv->dpio_lock); } @@ -4699,7 +4819,7 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, else i9xx_update_pll(intel_crtc, has_reduced_clock ? &reduced_clock : NULL, - num_connectors); + num_connectors); /* Set up the display plane register */ dspcntr = DISPPLANE_GAMMA_ENABLE; @@ -8753,6 +8873,13 @@ static void intel_init_display(struct drm_device *dev) dev_priv->display.crtc_disable = ironlake_crtc_disable; dev_priv->display.off = ironlake_crtc_off; dev_priv->display.update_plane = ironlake_update_plane; + } else if (IS_VALLEYVIEW(dev)) { + dev_priv->display.get_pipe_config = i9xx_get_pipe_config; + dev_priv->display.crtc_mode_set = i9xx_crtc_mode_set; + dev_priv->display.crtc_enable = valleyview_crtc_enable; + dev_priv->display.crtc_disable = i9xx_crtc_disable; + dev_priv->display.off = i9xx_crtc_off; + dev_priv->display.update_plane = i9xx_update_plane; } else { dev_priv->display.get_pipe_config = i9xx_get_pipe_config; dev_priv->display.crtc_mode_set = i9xx_crtc_mode_set; diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 9e16b0ca8dd8..058002659d9b 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1390,15 +1390,77 @@ static void intel_enable_dp(struct intel_encoder *encoder) ironlake_edp_panel_vdd_off(intel_dp, true); intel_dp_complete_link_train(intel_dp); ironlake_edp_backlight_on(intel_dp); + + if (IS_VALLEYVIEW(dev)) { + struct intel_digital_port *dport = + enc_to_dig_port(&encoder->base); + int channel = vlv_dport_to_channel(dport); + + vlv_wait_port_ready(dev_priv, channel); + } } static void intel_pre_enable_dp(struct intel_encoder *encoder) { struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); struct drm_device *dev = encoder->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; if (is_cpu_edp(intel_dp) && !IS_VALLEYVIEW(dev)) ironlake_edp_pll_on(intel_dp); + + if (IS_VALLEYVIEW(dev)) { + struct intel_digital_port *dport = enc_to_dig_port(&encoder->base); + struct intel_crtc *intel_crtc = + to_intel_crtc(encoder->base.crtc); + int port = vlv_dport_to_channel(dport); + int pipe = intel_crtc->pipe; + u32 val; + + WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); + + val = intel_dpio_read(dev_priv, DPIO_DATA_LANE_A(port)); + val = 0; + if (pipe) + val |= (1<<21); + else + val &= ~(1<<21); + val |= 0x001000c4; + intel_dpio_write(dev_priv, DPIO_DATA_CHANNEL(port), val); + + intel_dpio_write(dev_priv, DPIO_PCS_CLOCKBUF0(port), + 0x00760018); + intel_dpio_write(dev_priv, DPIO_PCS_CLOCKBUF8(port), + 0x00400888); + } +} + +static void intel_dp_pre_pll_enable(struct intel_encoder *encoder) +{ + struct intel_digital_port *dport = enc_to_dig_port(&encoder->base); + struct drm_device *dev = encoder->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + int port = vlv_dport_to_channel(dport); + + if (!IS_VALLEYVIEW(dev)) + return; + + WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); + + /* Program Tx lane resets to default */ + intel_dpio_write(dev_priv, DPIO_PCS_TX(port), + DPIO_PCS_TX_LANE2_RESET | + DPIO_PCS_TX_LANE1_RESET); + intel_dpio_write(dev_priv, DPIO_PCS_CLK(port), + DPIO_PCS_CLK_CRI_RXEB_EIOS_EN | + DPIO_PCS_CLK_CRI_RXDIGFILTSG_EN | + (1<dpio_lock)); + switch (train_set & DP_TRAIN_PRE_EMPHASIS_MASK) { case DP_TRAIN_PRE_EMPHASIS_0: preemph_reg_value = 0x0004000; @@ -1615,8 +1679,6 @@ static uint32_t intel_vlv_signal_levels(struct intel_dp *intel_dp) return 0; } - /* eDP is only on port C */ - mutex_lock(&dev_priv->dpio_lock); intel_dpio_write(dev_priv, DPIO_TX_OCALINIT(port), 0x00000000); intel_dpio_write(dev_priv, DPIO_TX_SWING_CTL4(port), demph_reg_value); intel_dpio_write(dev_priv, DPIO_TX_SWING_CTL2(port), @@ -1625,7 +1687,6 @@ static uint32_t intel_vlv_signal_levels(struct intel_dp *intel_dp) intel_dpio_write(dev_priv, DPIO_PCS_STAGGER0(port), 0x00030000); intel_dpio_write(dev_priv, DPIO_PCS_CTL_OVER1(port), preemph_reg_value); intel_dpio_write(dev_priv, DPIO_TX_OCALINIT(port), 0x80000000); - mutex_unlock(&dev_priv->dpio_lock); return 0; } @@ -3078,6 +3139,8 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port) intel_encoder->disable = intel_disable_dp; intel_encoder->post_disable = intel_post_disable_dp; intel_encoder->get_hw_state = intel_dp_get_hw_state; + if (IS_VALLEYVIEW(dev)) + intel_encoder->pre_pll_enable = intel_dp_pre_pll_enable; intel_dig_port->port = port; intel_dig_port->dp.output_reg = output_reg; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 399b18181b45..63264ed0c9a6 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -431,6 +431,19 @@ struct intel_digital_port { struct intel_hdmi hdmi; }; +static inline int +vlv_dport_to_channel(struct intel_digital_port *dport) +{ + switch (dport->port) { + case PORT_B: + return 0; + case PORT_C: + return 1; + default: + BUG(); + } +} + static inline struct drm_crtc * intel_get_crtc_for_pipe(struct drm_device *dev, int pipe) { @@ -606,6 +619,7 @@ intel_pipe_to_cpu_transcoder(struct drm_i915_private *dev_priv, extern void intel_wait_for_vblank(struct drm_device *dev, int pipe); extern void intel_wait_for_pipe_off(struct drm_device *dev, int pipe); extern int ironlake_get_lanes_required(int target_clock, int link_bw, int bpp); +extern void vlv_wait_port_ready(struct drm_i915_private *dev_priv, int port); struct intel_load_detect_pipe { struct drm_framebuffer *release_fb; diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 3e6a3ef10d5c..53ce8a57f589 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -697,6 +697,14 @@ static void intel_enable_hdmi(struct intel_encoder *encoder) I915_WRITE(intel_hdmi->hdmi_reg, temp); POSTING_READ(intel_hdmi->hdmi_reg); } + + if (IS_VALLEYVIEW(dev)) { + struct intel_digital_port *dport = + enc_to_dig_port(&encoder->base); + int channel = vlv_dport_to_channel(dport); + + vlv_wait_port_ready(dev_priv, channel); + } } static void intel_disable_hdmi(struct intel_encoder *encoder) @@ -947,6 +955,101 @@ done: return 0; } +static void intel_hdmi_pre_enable(struct intel_encoder *encoder) +{ + struct intel_digital_port *dport = enc_to_dig_port(&encoder->base); + struct drm_device *dev = encoder->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = + to_intel_crtc(encoder->base.crtc); + int port = vlv_dport_to_channel(dport); + int pipe = intel_crtc->pipe; + u32 val; + + if (!IS_VALLEYVIEW(dev)) + return; + + WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); + + /* Enable clock channels for this port */ + val = intel_dpio_read(dev_priv, DPIO_DATA_LANE_A(port)); + val = 0; + if (pipe) + val |= (1<<21); + else + val &= ~(1<<21); + val |= 0x001000c4; + intel_dpio_write(dev_priv, DPIO_DATA_CHANNEL(port), val); + + /* HDMI 1.0V-2dB */ + intel_dpio_write(dev_priv, DPIO_TX_OCALINIT(port), 0); + intel_dpio_write(dev_priv, DPIO_TX_SWING_CTL4(port), + 0x2b245f5f); + intel_dpio_write(dev_priv, DPIO_TX_SWING_CTL2(port), + 0x5578b83a); + intel_dpio_write(dev_priv, DPIO_TX_SWING_CTL3(port), + 0x0c782040); + intel_dpio_write(dev_priv, DPIO_TX3_SWING_CTL4(port), + 0x2b247878); + intel_dpio_write(dev_priv, DPIO_PCS_STAGGER0(port), 0x00030000); + intel_dpio_write(dev_priv, DPIO_PCS_CTL_OVER1(port), + 0x00002000); + intel_dpio_write(dev_priv, DPIO_TX_OCALINIT(port), + DPIO_TX_OCALINIT_EN); + + /* Program lane clock */ + intel_dpio_write(dev_priv, DPIO_PCS_CLOCKBUF0(port), + 0x00760018); + intel_dpio_write(dev_priv, DPIO_PCS_CLOCKBUF8(port), + 0x00400888); +} + +static void intel_hdmi_pre_pll_enable(struct intel_encoder *encoder) +{ + struct intel_digital_port *dport = enc_to_dig_port(&encoder->base); + struct drm_device *dev = encoder->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + int port = vlv_dport_to_channel(dport); + + if (!IS_VALLEYVIEW(dev)) + return; + + WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); + + /* Program Tx lane resets to default */ + intel_dpio_write(dev_priv, DPIO_PCS_TX(port), + DPIO_PCS_TX_LANE2_RESET | + DPIO_PCS_TX_LANE1_RESET); + intel_dpio_write(dev_priv, DPIO_PCS_CLK(port), + DPIO_PCS_CLK_CRI_RXEB_EIOS_EN | + DPIO_PCS_CLK_CRI_RXDIGFILTSG_EN | + (1<base); + struct drm_i915_private *dev_priv = encoder->base.dev->dev_private; + int port = vlv_dport_to_channel(dport); + + /* Reset lanes to avoid HDMI flicker (VLV w/a) */ + mutex_lock(&dev_priv->dpio_lock); + intel_dpio_write(dev_priv, DPIO_PCS_TX(port), 0x00000000); + intel_dpio_write(dev_priv, DPIO_PCS_CLK(port), 0x00e00060); + mutex_unlock(&dev_priv->dpio_lock); +} + static void intel_hdmi_destroy(struct drm_connector *connector) { drm_sysfs_connector_remove(connector); @@ -1086,6 +1189,11 @@ void intel_hdmi_init(struct drm_device *dev, int hdmi_reg, enum port port) intel_encoder->enable = intel_enable_hdmi; intel_encoder->disable = intel_disable_hdmi; intel_encoder->get_hw_state = intel_hdmi_get_hw_state; + if (IS_VALLEYVIEW(dev)) { + intel_encoder->pre_enable = intel_hdmi_pre_enable; + intel_encoder->pre_pll_enable = intel_hdmi_pre_pll_enable; + intel_encoder->post_disable = intel_hdmi_post_disable; + } intel_encoder->type = INTEL_OUTPUT_HDMI; intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2); -- cgit v1.2.3 From 8664281b64c457705db72fc60143d03827e75ca9 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 12 Apr 2013 17:57:57 -0300 Subject: drm/i915: report Gen5+ CPU and PCH FIFO underruns In this commit we enable both CPU and PCH FIFO underrun reporting and start reporting them. We follow a few rules: - after we receive one of these errors, we mask the interrupt, so we won't get an "interrupt storm" and we also won't flood dmesg; - at each mode set we enable the interrupts again, so we'll see each message at most once per mode set; - in the specific places where we need to ignore the errors, we completely mask the interrupts. The downside of this patch is that since we're completely disabling (masking) the interrupts instead of just not printing error messages, we will mask more than just what we want on IVB/HSW CPU interrupts (due to GEN7_ERR_INT) and on CPT/PPT/LPT PCHs (due to SERR_INT). So when we decide to mask PCH FIFO underruns for pipe A on CPT, we'll also be masking PCH FIFO underruns for pipe B, because both are reported by SERR_INT, which has to be either completely enabled or completely disabled (in othe words, there's no way to disable/enable specific bits of GEN7_ERR_INT and SERR_INT). V2: Rename some functions and variables, downgrade messages to DRM_DEBUG_DRIVER and rebase. Signed-off-by: Paulo Zanoni Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 315 ++++++++++++++++++++++++++++++++++- drivers/gpu/drm/i915/i915_reg.h | 13 +- drivers/gpu/drm/i915/intel_display.c | 14 ++ drivers/gpu/drm/i915/intel_drv.h | 11 ++ 4 files changed, 342 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 932e7f8b6d5c..c79793ee868b 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -112,6 +112,213 @@ ironlake_disable_display_irq(drm_i915_private_t *dev_priv, u32 mask) } } +static bool ivb_can_enable_err_int(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *crtc; + enum pipe pipe; + + for_each_pipe(pipe) { + crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]); + + if (crtc->cpu_fifo_underrun_disabled) + return false; + } + + return true; +} + +static bool cpt_can_enable_serr_int(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + enum pipe pipe; + struct intel_crtc *crtc; + + for_each_pipe(pipe) { + crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]); + + if (crtc->pch_fifo_underrun_disabled) + return false; + } + + return true; +} + +static void ironlake_set_fifo_underrun_reporting(struct drm_device *dev, + enum pipe pipe, bool enable) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + uint32_t bit = (pipe == PIPE_A) ? DE_PIPEA_FIFO_UNDERRUN : + DE_PIPEB_FIFO_UNDERRUN; + + if (enable) + ironlake_enable_display_irq(dev_priv, bit); + else + ironlake_disable_display_irq(dev_priv, bit); +} + +static void ivybridge_set_fifo_underrun_reporting(struct drm_device *dev, + bool enable) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + if (enable) { + if (!ivb_can_enable_err_int(dev)) + return; + + I915_WRITE(GEN7_ERR_INT, ERR_INT_FIFO_UNDERRUN_A | + ERR_INT_FIFO_UNDERRUN_B | + ERR_INT_FIFO_UNDERRUN_C); + + ironlake_enable_display_irq(dev_priv, DE_ERR_INT_IVB); + } else { + ironlake_disable_display_irq(dev_priv, DE_ERR_INT_IVB); + } +} + +static void ibx_set_fifo_underrun_reporting(struct intel_crtc *crtc, + bool enable) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + uint32_t bit = (crtc->pipe == PIPE_A) ? SDE_TRANSA_FIFO_UNDER : + SDE_TRANSB_FIFO_UNDER; + + if (enable) + I915_WRITE(SDEIMR, I915_READ(SDEIMR) & ~bit); + else + I915_WRITE(SDEIMR, I915_READ(SDEIMR) | bit); + + POSTING_READ(SDEIMR); +} + +static void cpt_set_fifo_underrun_reporting(struct drm_device *dev, + enum transcoder pch_transcoder, + bool enable) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + if (enable) { + if (!cpt_can_enable_serr_int(dev)) + return; + + I915_WRITE(SERR_INT, SERR_INT_TRANS_A_FIFO_UNDERRUN | + SERR_INT_TRANS_B_FIFO_UNDERRUN | + SERR_INT_TRANS_C_FIFO_UNDERRUN); + + I915_WRITE(SDEIMR, I915_READ(SDEIMR) & ~SDE_ERROR_CPT); + } else { + I915_WRITE(SDEIMR, I915_READ(SDEIMR) | SDE_ERROR_CPT); + } + + POSTING_READ(SDEIMR); +} + +/** + * intel_set_cpu_fifo_underrun_reporting - enable/disable FIFO underrun messages + * @dev: drm device + * @pipe: pipe + * @enable: true if we want to report FIFO underrun errors, false otherwise + * + * This function makes us disable or enable CPU fifo underruns for a specific + * pipe. Notice that on some Gens (e.g. IVB, HSW), disabling FIFO underrun + * reporting for one pipe may also disable all the other CPU error interruts for + * the other pipes, due to the fact that there's just one interrupt mask/enable + * bit for all the pipes. + * + * Returns the previous state of underrun reporting. + */ +bool intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev, + enum pipe pipe, bool enable) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe]; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + unsigned long flags; + bool ret; + + spin_lock_irqsave(&dev_priv->irq_lock, flags); + + ret = !intel_crtc->cpu_fifo_underrun_disabled; + + if (enable == ret) + goto done; + + intel_crtc->cpu_fifo_underrun_disabled = !enable; + + if (IS_GEN5(dev) || IS_GEN6(dev)) + ironlake_set_fifo_underrun_reporting(dev, pipe, enable); + else if (IS_GEN7(dev)) + ivybridge_set_fifo_underrun_reporting(dev, enable); + +done: + spin_unlock_irqrestore(&dev_priv->irq_lock, flags); + return ret; +} + +/** + * intel_set_pch_fifo_underrun_reporting - enable/disable FIFO underrun messages + * @dev: drm device + * @pch_transcoder: the PCH transcoder (same as pipe on IVB and older) + * @enable: true if we want to report FIFO underrun errors, false otherwise + * + * This function makes us disable or enable PCH fifo underruns for a specific + * PCH transcoder. Notice that on some PCHs (e.g. CPT/PPT), disabling FIFO + * underrun reporting for one transcoder may also disable all the other PCH + * error interruts for the other transcoders, due to the fact that there's just + * one interrupt mask/enable bit for all the transcoders. + * + * Returns the previous state of underrun reporting. + */ +bool intel_set_pch_fifo_underrun_reporting(struct drm_device *dev, + enum transcoder pch_transcoder, + bool enable) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + enum pipe p; + struct drm_crtc *crtc; + struct intel_crtc *intel_crtc; + unsigned long flags; + bool ret; + + if (HAS_PCH_LPT(dev)) { + crtc = NULL; + for_each_pipe(p) { + struct drm_crtc *c = dev_priv->pipe_to_crtc_mapping[p]; + if (intel_pipe_has_type(c, INTEL_OUTPUT_ANALOG)) { + crtc = c; + break; + } + } + if (!crtc) { + DRM_ERROR("PCH FIFO underrun, but no CRTC using the PCH found\n"); + return false; + } + } else { + crtc = dev_priv->pipe_to_crtc_mapping[pch_transcoder]; + } + intel_crtc = to_intel_crtc(crtc); + + spin_lock_irqsave(&dev_priv->irq_lock, flags); + + ret = !intel_crtc->pch_fifo_underrun_disabled; + + if (enable == ret) + goto done; + + intel_crtc->pch_fifo_underrun_disabled = !enable; + + if (HAS_PCH_IBX(dev)) + ibx_set_fifo_underrun_reporting(intel_crtc, enable); + else + cpt_set_fifo_underrun_reporting(dev, pch_transcoder, enable); + +done: + spin_unlock_irqrestore(&dev_priv->irq_lock, flags); + return ret; +} + + void i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask) { @@ -800,10 +1007,58 @@ static void ibx_irq_handler(struct drm_device *dev, u32 pch_iir) if (pch_iir & (SDE_TRANSB_CRC_ERR | SDE_TRANSA_CRC_ERR)) DRM_DEBUG_DRIVER("PCH transcoder CRC error interrupt\n"); - if (pch_iir & SDE_TRANSB_FIFO_UNDER) - DRM_DEBUG_DRIVER("PCH transcoder B underrun interrupt\n"); if (pch_iir & SDE_TRANSA_FIFO_UNDER) - DRM_DEBUG_DRIVER("PCH transcoder A underrun interrupt\n"); + if (intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A, + false)) + DRM_DEBUG_DRIVER("PCH transcoder A FIFO underrun\n"); + + if (pch_iir & SDE_TRANSB_FIFO_UNDER) + if (intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_B, + false)) + DRM_DEBUG_DRIVER("PCH transcoder B FIFO underrun\n"); +} + +static void ivb_err_int_handler(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 err_int = I915_READ(GEN7_ERR_INT); + + if (err_int & ERR_INT_FIFO_UNDERRUN_A) + if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_A, false)) + DRM_DEBUG_DRIVER("Pipe A FIFO underrun\n"); + + if (err_int & ERR_INT_FIFO_UNDERRUN_B) + if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_B, false)) + DRM_DEBUG_DRIVER("Pipe B FIFO underrun\n"); + + if (err_int & ERR_INT_FIFO_UNDERRUN_C) + if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_C, false)) + DRM_DEBUG_DRIVER("Pipe C FIFO underrun\n"); + + I915_WRITE(GEN7_ERR_INT, err_int); +} + +static void cpt_serr_int_handler(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 serr_int = I915_READ(SERR_INT); + + if (serr_int & SERR_INT_TRANS_A_FIFO_UNDERRUN) + if (intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A, + false)) + DRM_DEBUG_DRIVER("PCH transcoder A FIFO underrun\n"); + + if (serr_int & SERR_INT_TRANS_B_FIFO_UNDERRUN) + if (intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_B, + false)) + DRM_DEBUG_DRIVER("PCH transcoder B FIFO underrun\n"); + + if (serr_int & SERR_INT_TRANS_C_FIFO_UNDERRUN) + if (intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_C, + false)) + DRM_DEBUG_DRIVER("PCH transcoder C FIFO underrun\n"); + + I915_WRITE(SERR_INT, serr_int); } static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir) @@ -841,6 +1096,9 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir) DRM_DEBUG_DRIVER(" pipe %c FDI IIR: 0x%08x\n", pipe_name(pipe), I915_READ(FDI_RX_IIR(pipe))); + + if (pch_iir & SDE_ERROR_CPT) + cpt_serr_int_handler(dev); } static irqreturn_t ivybridge_irq_handler(int irq, void *arg) @@ -853,6 +1111,14 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg) atomic_inc(&dev_priv->irq_received); + /* We get interrupts on unclaimed registers, so check for this before we + * do any I915_{READ,WRITE}. */ + if (IS_HASWELL(dev) && + (I915_READ_NOTRACE(FPGA_DBG) & FPGA_DBG_RM_NOCLAIM)) { + DRM_ERROR("Unclaimed register before interrupt\n"); + I915_WRITE_NOTRACE(FPGA_DBG, FPGA_DBG_RM_NOCLAIM); + } + /* disable master interrupt before clearing iir */ de_ier = I915_READ(DEIER); I915_WRITE(DEIER, de_ier & ~DE_MASTER_IRQ_CONTROL); @@ -868,6 +1134,12 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg) POSTING_READ(SDEIER); } + /* On Haswell, also mask ERR_INT because we don't want to risk + * generating "unclaimed register" interrupts from inside the interrupt + * handler. */ + if (IS_HASWELL(dev)) + ironlake_disable_display_irq(dev_priv, DE_ERR_INT_IVB); + gt_iir = I915_READ(GTIIR); if (gt_iir) { snb_gt_irq_handler(dev, dev_priv, gt_iir); @@ -877,6 +1149,9 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg) de_iir = I915_READ(DEIIR); if (de_iir) { + if (de_iir & DE_ERR_INT_IVB) + ivb_err_int_handler(dev); + if (de_iir & DE_AUX_CHANNEL_A_IVB) dp_aux_irq_handler(dev); @@ -914,6 +1189,9 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg) ret = IRQ_HANDLED; } + if (IS_HASWELL(dev) && ivb_can_enable_err_int(dev)) + ironlake_enable_display_irq(dev_priv, DE_ERR_INT_IVB); + I915_WRITE(DEIER, de_ier); POSTING_READ(DEIER); if (!HAS_PCH_NOP(dev)) { @@ -983,6 +1261,14 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) if (de_iir & DE_PIPEB_VBLANK) drm_handle_vblank(dev, 1); + if (de_iir & DE_PIPEA_FIFO_UNDERRUN) + if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_A, false)) + DRM_DEBUG_DRIVER("Pipe A FIFO underrun\n"); + + if (de_iir & DE_PIPEB_FIFO_UNDERRUN) + if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_B, false)) + DRM_DEBUG_DRIVER("Pipe B FIFO underrun\n"); + if (de_iir & DE_PLANEA_FLIP_DONE) { intel_prepare_page_flip(dev, 0); intel_finish_page_flip_plane(dev, 0); @@ -2208,10 +2494,14 @@ static void ibx_irq_postinstall(struct drm_device *dev) drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; u32 mask; - if (HAS_PCH_IBX(dev)) - mask = SDE_GMBUS | SDE_AUX_MASK; - else - mask = SDE_GMBUS_CPT | SDE_AUX_MASK_CPT; + if (HAS_PCH_IBX(dev)) { + mask = SDE_GMBUS | SDE_AUX_MASK | SDE_TRANSB_FIFO_UNDER | + SDE_TRANSA_FIFO_UNDER; + } else { + mask = SDE_GMBUS_CPT | SDE_AUX_MASK_CPT | SDE_ERROR_CPT; + + I915_WRITE(SERR_INT, I915_READ(SERR_INT)); + } if (HAS_PCH_NOP(dev)) return; @@ -2226,7 +2516,8 @@ static int ironlake_irq_postinstall(struct drm_device *dev) /* enable kind of interrupts always enabled */ u32 display_mask = DE_MASTER_IRQ_CONTROL | DE_GSE | DE_PCH_EVENT | DE_PLANEA_FLIP_DONE | DE_PLANEB_FLIP_DONE | - DE_AUX_CHANNEL_A; + DE_AUX_CHANNEL_A | DE_PIPEB_FIFO_UNDERRUN | + DE_PIPEA_FIFO_UNDERRUN; u32 render_irqs; dev_priv->irq_mask = ~display_mask; @@ -2276,12 +2567,14 @@ static int ivybridge_irq_postinstall(struct drm_device *dev) DE_PLANEC_FLIP_DONE_IVB | DE_PLANEB_FLIP_DONE_IVB | DE_PLANEA_FLIP_DONE_IVB | - DE_AUX_CHANNEL_A_IVB; + DE_AUX_CHANNEL_A_IVB | + DE_ERR_INT_IVB; u32 render_irqs; dev_priv->irq_mask = ~display_mask; /* should always can generate irq */ + I915_WRITE(GEN7_ERR_INT, I915_READ(GEN7_ERR_INT)); I915_WRITE(DEIIR, I915_READ(DEIIR)); I915_WRITE(DEIMR, dev_priv->irq_mask); I915_WRITE(DEIER, @@ -2409,6 +2702,8 @@ static void ironlake_irq_uninstall(struct drm_device *dev) I915_WRITE(DEIMR, 0xffffffff); I915_WRITE(DEIER, 0x0); I915_WRITE(DEIIR, I915_READ(DEIIR)); + if (IS_GEN7(dev)) + I915_WRITE(GEN7_ERR_INT, I915_READ(GEN7_ERR_INT)); I915_WRITE(GTIMR, 0xffffffff); I915_WRITE(GTIER, 0x0); @@ -2420,6 +2715,8 @@ static void ironlake_irq_uninstall(struct drm_device *dev) I915_WRITE(SDEIMR, 0xffffffff); I915_WRITE(SDEIER, 0x0); I915_WRITE(SDEIIR, I915_READ(SDEIIR)); + if (HAS_PCH_CPT(dev) || HAS_PCH_LPT(dev)) + I915_WRITE(SERR_INT, I915_READ(SERR_INT)); } static void i8xx_irq_preinstall(struct drm_device * dev) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index fb1a4fa68d55..32f970d2fd51 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -640,7 +640,10 @@ #define ERROR_GEN6 0x040a0 #define GEN7_ERR_INT 0x44040 -#define ERR_INT_MMIO_UNCLAIMED (1<<13) +#define ERR_INT_MMIO_UNCLAIMED (1<<13) +#define ERR_INT_FIFO_UNDERRUN_C (1<<6) +#define ERR_INT_FIFO_UNDERRUN_B (1<<3) +#define ERR_INT_FIFO_UNDERRUN_A (1<<0) #define FPGA_DBG 0x42300 #define FPGA_DBG_RM_NOCLAIM (1<<31) @@ -3622,7 +3625,7 @@ #define DE_PIPEA_FIFO_UNDERRUN (1 << 0) /* More Ivybridge lolz */ -#define DE_ERR_DEBUG_IVB (1<<30) +#define DE_ERR_INT_IVB (1<<30) #define DE_GSE_IVB (1<<29) #define DE_PCH_EVENT_IVB (1<<28) #define DE_DP_A_HOTPLUG_IVB (1<<27) @@ -3781,6 +3784,7 @@ SDE_PORTC_HOTPLUG_CPT | \ SDE_PORTB_HOTPLUG_CPT) #define SDE_GMBUS_CPT (1 << 17) +#define SDE_ERROR_CPT (1 << 16) #define SDE_AUDIO_CP_REQ_C_CPT (1 << 10) #define SDE_AUDIO_CP_CHG_C_CPT (1 << 9) #define SDE_FDI_RXC_CPT (1 << 8) @@ -3805,6 +3809,11 @@ #define SDEIIR 0xc4008 #define SDEIER 0xc400c +#define SERR_INT 0xc4040 +#define SERR_INT_TRANS_C_FIFO_UNDERRUN (1<<6) +#define SERR_INT_TRANS_B_FIFO_UNDERRUN (1<<3) +#define SERR_INT_TRANS_A_FIFO_UNDERRUN (1<<0) + /* digital port hotplug */ #define PCH_PORT_HOTPLUG 0xc4030 /* SHOTPLUG_CTL */ #define PORTD_HOTPLUG_ENABLE (1 << 20) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 3fadd33c772f..8a96c998ee53 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3346,6 +3346,10 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) return; intel_crtc->active = true; + + intel_set_cpu_fifo_underrun_reporting(dev, pipe, true); + intel_set_pch_fifo_underrun_reporting(dev, pipe, true); + intel_update_watermarks(dev); if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { @@ -3437,6 +3441,11 @@ static void haswell_crtc_enable(struct drm_crtc *crtc) return; intel_crtc->active = true; + + intel_set_cpu_fifo_underrun_reporting(dev, pipe, true); + if (intel_crtc->config.has_pch_encoder) + intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A, true); + intel_update_watermarks(dev); if (intel_crtc->config.has_pch_encoder) @@ -3523,6 +3532,7 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) if (dev_priv->cfb_plane == plane) intel_disable_fbc(dev); + intel_set_pch_fifo_underrun_reporting(dev, pipe, false); intel_disable_pipe(dev_priv, pipe); /* Disable PF */ @@ -3536,6 +3546,7 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) ironlake_fdi_disable(crtc); ironlake_disable_pch_transcoder(dev_priv, pipe); + intel_set_pch_fifo_underrun_reporting(dev, pipe, true); if (HAS_PCH_CPT(dev)) { /* disable TRANS_DP_CTL */ @@ -3602,6 +3613,8 @@ static void haswell_crtc_disable(struct drm_crtc *crtc) if (dev_priv->cfb_plane == plane) intel_disable_fbc(dev); + if (intel_crtc->config.has_pch_encoder) + intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A, false); intel_disable_pipe(dev_priv, pipe); intel_ddi_disable_transcoder_func(dev_priv, cpu_transcoder); @@ -3622,6 +3635,7 @@ static void haswell_crtc_disable(struct drm_crtc *crtc) if (intel_crtc->config.has_pch_encoder) { lpt_disable_pch_transcoder(dev_priv); + intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A, true); intel_ddi_fdi_disable(crtc); } diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 63264ed0c9a6..c20201d948eb 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -265,6 +265,10 @@ struct intel_crtc { /* reset counter value when the last flip was submitted */ unsigned int reset_counter; + + /* Access to these should be protected by dev_priv->irq_lock. */ + bool cpu_fifo_underrun_disabled; + bool pch_fifo_underrun_disabled; }; struct intel_plane { @@ -487,6 +491,7 @@ int intel_ddc_get_modes(struct drm_connector *c, struct i2c_adapter *adapter); extern void intel_attach_force_audio_property(struct drm_connector *connector); extern void intel_attach_broadcast_rgb_property(struct drm_connector *connector); +extern bool intel_pipe_has_type(struct drm_crtc *crtc, int type); extern void intel_crt_init(struct drm_device *dev); extern void intel_hdmi_init(struct drm_device *dev, int hdmi_reg, enum port port); @@ -743,5 +748,11 @@ intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector); extern void intel_ddi_fdi_disable(struct drm_crtc *crtc); extern void intel_display_handle_reset(struct drm_device *dev); +extern bool intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev, + enum pipe pipe, + bool enable); +extern bool intel_set_pch_fifo_underrun_reporting(struct drm_device *dev, + enum transcoder pch_transcoder, + bool enable); #endif /* __INTEL_DRV_H__ */ -- cgit v1.2.3 From de032bf40a52dbbada11e071d150d2c062b5527e Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 12 Apr 2013 17:57:58 -0300 Subject: drm/i915: print Gen5+ CPU/PCH poison interrupts This is bad news and shouldn't be happening. V2: Rebase. Signed-off-by: Paulo Zanoni Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 13 +++++++++++-- drivers/gpu/drm/i915/i915_reg.h | 2 ++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index c79793ee868b..dae8953d2912 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1023,6 +1023,9 @@ static void ivb_err_int_handler(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; u32 err_int = I915_READ(GEN7_ERR_INT); + if (err_int & ERR_INT_POISON) + DRM_ERROR("Poison interrupt\n"); + if (err_int & ERR_INT_FIFO_UNDERRUN_A) if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_A, false)) DRM_DEBUG_DRIVER("Pipe A FIFO underrun\n"); @@ -1043,6 +1046,9 @@ static void cpt_serr_int_handler(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; u32 serr_int = I915_READ(SERR_INT); + if (serr_int & SERR_INT_POISON) + DRM_ERROR("PCH poison interrupt\n"); + if (serr_int & SERR_INT_TRANS_A_FIFO_UNDERRUN) if (intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A, false)) @@ -1261,6 +1267,9 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) if (de_iir & DE_PIPEB_VBLANK) drm_handle_vblank(dev, 1); + if (de_iir & DE_POISON) + DRM_ERROR("Poison interrupt\n"); + if (de_iir & DE_PIPEA_FIFO_UNDERRUN) if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_A, false)) DRM_DEBUG_DRIVER("Pipe A FIFO underrun\n"); @@ -2496,7 +2505,7 @@ static void ibx_irq_postinstall(struct drm_device *dev) if (HAS_PCH_IBX(dev)) { mask = SDE_GMBUS | SDE_AUX_MASK | SDE_TRANSB_FIFO_UNDER | - SDE_TRANSA_FIFO_UNDER; + SDE_TRANSA_FIFO_UNDER | SDE_POISON; } else { mask = SDE_GMBUS_CPT | SDE_AUX_MASK_CPT | SDE_ERROR_CPT; @@ -2517,7 +2526,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 | DE_AUX_CHANNEL_A | DE_PIPEB_FIFO_UNDERRUN | - DE_PIPEA_FIFO_UNDERRUN; + DE_PIPEA_FIFO_UNDERRUN | DE_POISON; u32 render_irqs; dev_priv->irq_mask = ~display_mask; diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 32f970d2fd51..9093d6669560 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -640,6 +640,7 @@ #define ERROR_GEN6 0x040a0 #define GEN7_ERR_INT 0x44040 +#define ERR_INT_POISON (1<<31) #define ERR_INT_MMIO_UNCLAIMED (1<<13) #define ERR_INT_FIFO_UNDERRUN_C (1<<6) #define ERR_INT_FIFO_UNDERRUN_B (1<<3) @@ -3810,6 +3811,7 @@ #define SDEIER 0xc400c #define SERR_INT 0xc4040 +#define SERR_INT_POISON (1<<31) #define SERR_INT_TRANS_C_FIFO_UNDERRUN (1<<6) #define SERR_INT_TRANS_B_FIFO_UNDERRUN (1<<3) #define SERR_INT_TRANS_A_FIFO_UNDERRUN (1<<0) -- cgit v1.2.3 From 2bfce95075fa58eaf2ead5b0863c50a3f6098bc2 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Thu, 18 Apr 2013 16:35:40 -0300 Subject: drm/i915: check the power well inside haswell_get_pipe_config This fixes "unclaimed register" messages when booting with eDP only and i915.disable_power_well=1. The error messages were caused by: commit 0e8ffe1bf81b0780cc6229cb38664754dffe8776 Author: Daniel Vetter Date: Thu Mar 28 10:42:00 2013 +0100 drm/i915: add hw state readout/checking for pipe_config Signed-off-by: Paulo Zanoni Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 8a96c998ee53..6f7e4cc35d99 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5978,9 +5978,14 @@ static bool haswell_get_pipe_config(struct intel_crtc *crtc, { struct drm_device *dev = crtc->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; + enum transcoder cpu_transcoder = crtc->config.cpu_transcoder; uint32_t tmp; - tmp = I915_READ(PIPECONF(crtc->config.cpu_transcoder)); + if (!intel_using_power_well(dev_priv->dev) && + cpu_transcoder != TRANSCODER_EDP) + return false; + + tmp = I915_READ(PIPECONF(cpu_transcoder)); if (!(tmp & PIPECONF_ENABLE)) return false; -- cgit v1.2.3 From f196e6bedb1b8a76f8526798e0feeb7a213e7505 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Thu, 18 Apr 2013 16:35:41 -0300 Subject: drm/i915: use cpu_transcoder for TRANS_DDI_FUNC_CTL ... inside haswell_get_pipe_config. Because there's one TRANS_DDI_FUNC_CTL register per CPU transcoder, not per pipe. This solves "unclaimed register" messages when booting with eDP only and using the i915.disable_power_well=1. Also fix a comment and remove an useless empty line. The error messages were caused by: commit 88adfff1ad5019f65b9d0b4e1a4ac900fb065183 Author: Daniel Vetter Date: Thu Mar 28 10:42:01 2013 +0100 drm/i915: hw readout support for ->has_pch_encoders Signed-off-by: Paulo Zanoni Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 6f7e4cc35d99..3c90605ddd45 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5990,16 +5990,15 @@ static bool haswell_get_pipe_config(struct intel_crtc *crtc, return false; /* - * aswell has only FDI/PCH transcoder A. It is which is connected to + * Haswell has only FDI/PCH transcoder A. It is which is connected to * DDI E. So just check whether this pipe is wired to DDI E and whether * the PCH transcoder is on. */ - tmp = I915_READ(TRANS_DDI_FUNC_CTL(crtc->pipe)); + tmp = I915_READ(TRANS_DDI_FUNC_CTL(cpu_transcoder)); if ((tmp & TRANS_DDI_PORT_MASK) == TRANS_DDI_SELECT_PORT(PORT_E) && I915_READ(TRANSCONF(PIPE_A)) & TRANS_ENABLE) pipe_config->has_pch_encoder = true; - return true; } -- cgit v1.2.3 From 29a397ba7a4bded235c79f050753637cad3409ff Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Fri, 19 Apr 2013 12:23:02 +0300 Subject: drm/i915: Move the CSC_MODE bits next to the register MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Shame on me for not putting the bit definitions next to the register definition in the first place. Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 9093d6669560..60e0e19373b1 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -4939,6 +4939,9 @@ #define _PIPE_A_CSC_COEFF_RV_GV 0x49020 #define _PIPE_A_CSC_COEFF_BV 0x49024 #define _PIPE_A_CSC_MODE 0x49028 +#define CSC_BLACK_SCREEN_OFFSET (1 << 2) +#define CSC_POSITION_BEFORE_GAMMA (1 << 1) +#define CSC_MODE_YUV_TO_RGB (1 << 0) #define _PIPE_A_CSC_PREOFF_HI 0x49030 #define _PIPE_A_CSC_PREOFF_ME 0x49034 #define _PIPE_A_CSC_PREOFF_LO 0x49038 @@ -4960,10 +4963,6 @@ #define _PIPE_B_CSC_POSTOFF_ME 0x49144 #define _PIPE_B_CSC_POSTOFF_LO 0x49148 -#define CSC_BLACK_SCREEN_OFFSET (1 << 2) -#define CSC_POSITION_BEFORE_GAMMA (1 << 1) -#define CSC_MODE_YUV_TO_RGB (1 << 0) - #define PIPE_CSC_COEFF_RY_GY(pipe) _PIPE(pipe, _PIPE_A_CSC_COEFF_RY_GY, _PIPE_B_CSC_COEFF_RY_GY) #define PIPE_CSC_COEFF_BY(pipe) _PIPE(pipe, _PIPE_A_CSC_COEFF_BY, _PIPE_B_CSC_COEFF_BY) #define PIPE_CSC_COEFF_RU_GU(pipe) _PIPE(pipe, _PIPE_A_CSC_COEFF_RU_GU, _PIPE_B_CSC_COEFF_RU_GU) -- cgit v1.2.3 From bf98a72650c9272e70e7f0e904f85c025c0bb421 Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Fri, 19 Apr 2013 14:27:31 +0100 Subject: drm/i915: Remove mention of Haswell in DDI code We are trying to have more platform-orthogonal pieces of code. The DDI code shouldn't mention Haswell. v2: Fix the email address Signed-off-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ddi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index e14fe5f569ee..eef450b1af44 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -675,7 +675,7 @@ static void intel_ddi_mode_set(struct drm_encoder *encoder, int pipe = intel_crtc->pipe; int type = intel_encoder->type; - DRM_DEBUG_KMS("Preparing DDI mode for Haswell on port %c, pipe %c\n", + DRM_DEBUG_KMS("Preparing DDI mode on port %c, pipe %c\n", port_name(port), pipe_name(pipe)); intel_crtc->eld_vld = false; -- cgit v1.2.3 From cece5d58d5568e4a7986e43981d21a80ea189a82 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Fri, 19 Apr 2013 08:46:35 -0700 Subject: drm/i915: use vlv_dport_to_channel in vlv_signal_levels Minor cleanup. Would be nice to use an enum for channel in the DPIO macros so we don't mix up pipes and channels, but that's for another patch. Signed-off-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 058002659d9b..1ccf853e2df7 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1595,14 +1595,7 @@ static uint32_t intel_vlv_signal_levels(struct intel_dp *intel_dp) unsigned long demph_reg_value, preemph_reg_value, uniqtranscale_reg_value; uint8_t train_set = intel_dp->train_set[0]; - int port; - - if (dport->port == PORT_B) - port = 0; - else if (dport->port == PORT_C) - port = 1; - else - BUG(); + int port = vlv_dport_to_channel(dport); WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); -- cgit v1.2.3 From 80ad9206c0d863832bc5f6008c4d1868d1df8e70 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Fri, 19 Apr 2013 14:36:51 +0300 Subject: drm/i915: Make struct dpll == intel_clock_t MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows unifying a bunch of the PLL calculations and whatnot. Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 12 ------------ drivers/gpu/drm/i915/intel_drv.h | 18 +++++++++++++----- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 3c90605ddd45..74156e231fd5 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -45,18 +45,6 @@ bool intel_pipe_has_type(struct drm_crtc *crtc, int type); 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 */ - int n; - int m1, m2; - int p1, p2; - /* derived values */ - int dot; - int vco; - int m; - int p; -} intel_clock_t; - typedef struct { int min, max; } intel_range_t; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index c20201d948eb..66922f8f1bc5 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -177,6 +177,18 @@ struct intel_connector { u8 polled; }; +typedef struct dpll { + /* given values */ + int n; + int m1, m2; + int p1, p2; + /* derived values */ + int dot; + int vco; + int m; + int p; +} intel_clock_t; + struct intel_crtc_config { struct drm_display_mode requested_mode; struct drm_display_mode adjusted_mode; @@ -208,11 +220,7 @@ struct intel_crtc_config { /* Settings for the intel dpll used on pretty much everything but * haswell. */ - struct dpll { - unsigned n; - unsigned m1, m2; - unsigned p1, p2; - } dpll; + struct dpll dpll; int pipe_bpp; struct intel_link_m_n dp_m_n; -- cgit v1.2.3 From 2d04befb949744998284d8551ae7cd47059b8a53 Mon Sep 17 00:00:00 2001 From: Kenneth Graunke Date: Mon, 22 Apr 2013 00:53:49 -0700 Subject: drm/i915: Add PTE encoding function to the gtt/ppgtt vtables. Sandybridge/Ivybridge, Bay Trail, and Haswell all have slightly different page table entry formats. Rather than polluting one function with generation checks, simply use a function pointer and set up the correct PTE encoding function at startup. v2: Move the gen6_gtt_pte_t typedef to i915_drv.h so that the function pointers and implementations have identical signatures. Also remove inline keyword on gen6_pte_encode. Both suggested by Jani Nikula. Signed-off-by: Kenneth Graunke Reviewed-by: Jani Nikula Tested-by: Daniel Leung [v1] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 8 ++++++++ drivers/gpu/drm/i915/i915_gem_gtt.c | 30 ++++++++++++++++-------------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index bd2d7f17393e..d80bced96cea 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -395,6 +395,8 @@ enum i915_cache_level { I915_CACHE_LLC_MLC, /* gen6+, in docs at least! */ }; +typedef uint32_t gen6_gtt_pte_t; + /* The Graphics Translation Table is the way in which GEN hardware translates a * Graphics Virtual Address into a Physical Address. In addition to the normal * collateral associated with any va->pa translations GEN hardware also has a @@ -430,6 +432,9 @@ struct i915_gtt { struct sg_table *st, unsigned int pg_start, enum i915_cache_level cache_level); + gen6_gtt_pte_t (*pte_encode)(struct drm_device *dev, + dma_addr_t addr, + enum i915_cache_level level); }; #define gtt_total_entries(gtt) ((gtt).total >> PAGE_SHIFT) @@ -451,6 +456,9 @@ struct i915_hw_ppgtt { struct sg_table *st, unsigned int pg_start, enum i915_cache_level cache_level); + gen6_gtt_pte_t (*pte_encode)(struct drm_device *dev, + dma_addr_t addr, + enum i915_cache_level level); int (*enable)(struct drm_device *dev); void (*cleanup)(struct i915_hw_ppgtt *ppgtt); }; diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 50df194914a6..92e147ff8665 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -28,8 +28,6 @@ #include "i915_trace.h" #include "intel_drv.h" -typedef uint32_t gen6_gtt_pte_t; - /* PPGTT stuff */ #define GEN6_GTT_ADDR_ENCODE(addr) ((addr) | (((addr) >> 28) & 0xff0)) @@ -44,9 +42,9 @@ typedef uint32_t gen6_gtt_pte_t; #define GEN6_PTE_CACHE_LLC_MLC (3 << 1) #define GEN6_PTE_ADDR_ENCODE(addr) GEN6_GTT_ADDR_ENCODE(addr) -static inline gen6_gtt_pte_t gen6_pte_encode(struct drm_device *dev, - dma_addr_t addr, - enum i915_cache_level level) +static gen6_gtt_pte_t gen6_pte_encode(struct drm_device *dev, + dma_addr_t addr, + enum i915_cache_level level) { gen6_gtt_pte_t pte = GEN6_PTE_VALID; pte |= GEN6_PTE_ADDR_ENCODE(addr); @@ -154,9 +152,9 @@ static void gen6_ppgtt_clear_range(struct i915_hw_ppgtt *ppgtt, unsigned first_pte = first_entry % I915_PPGTT_PT_ENTRIES; unsigned last_pte, i; - scratch_pte = gen6_pte_encode(ppgtt->dev, - ppgtt->scratch_page_dma_addr, - I915_CACHE_LLC); + scratch_pte = ppgtt->pte_encode(ppgtt->dev, + ppgtt->scratch_page_dma_addr, + I915_CACHE_LLC); while (num_entries) { last_pte = first_pte + num_entries; @@ -191,8 +189,8 @@ static void gen6_ppgtt_insert_entries(struct i915_hw_ppgtt *ppgtt, dma_addr_t page_addr; page_addr = sg_page_iter_dma_address(&sg_iter); - pt_vaddr[act_pte] = gen6_pte_encode(ppgtt->dev, page_addr, - cache_level); + pt_vaddr[act_pte] = ppgtt->pte_encode(ppgtt->dev, page_addr, + cache_level); if (++act_pte == I915_PPGTT_PT_ENTRIES) { kunmap_atomic(pt_vaddr); act_pt++; @@ -236,6 +234,7 @@ static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt) first_pd_entry_in_global_pt = gtt_total_entries(dev_priv->gtt) - I915_PPGTT_PD_ENTRIES; + ppgtt->pte_encode = gen6_pte_encode; ppgtt->num_pd_entries = I915_PPGTT_PD_ENTRIES; ppgtt->enable = gen6_ppgtt_enable; ppgtt->clear_range = gen6_ppgtt_clear_range; @@ -438,7 +437,8 @@ static void gen6_ggtt_insert_entries(struct drm_device *dev, for_each_sg_page(st->sgl, &sg_iter, st->nents, 0) { addr = sg_page_iter_dma_address(&sg_iter); - iowrite32(gen6_pte_encode(dev, addr, level), >t_entries[i]); + iowrite32(dev_priv->gtt.pte_encode(dev, addr, level), + >t_entries[i]); i++; } @@ -450,7 +450,7 @@ static void gen6_ggtt_insert_entries(struct drm_device *dev, */ if (i != 0) WARN_ON(readl(>t_entries[i-1]) - != gen6_pte_encode(dev, addr, level)); + != dev_priv->gtt.pte_encode(dev, addr, level)); /* This next bit makes the above posting read even more important. We * want to flush the TLBs only after we're certain all the PTE updates @@ -475,8 +475,9 @@ static void gen6_ggtt_clear_range(struct drm_device *dev, first_entry, num_entries, max_entries)) num_entries = max_entries; - scratch_pte = gen6_pte_encode(dev, dev_priv->gtt.scratch_page_dma, - I915_CACHE_LLC); + scratch_pte = dev_priv->gtt.pte_encode(dev, + dev_priv->gtt.scratch_page_dma, + I915_CACHE_LLC); for (i = 0; i < num_entries; i++) iowrite32(scratch_pte, >t_base[i]); readl(gtt_base); @@ -823,6 +824,7 @@ int i915_gem_gtt_init(struct drm_device *dev) } else { dev_priv->gtt.gtt_probe = gen6_gmch_probe; dev_priv->gtt.gtt_remove = gen6_gmch_remove; + dev_priv->gtt.pte_encode = gen6_pte_encode; } ret = dev_priv->gtt.gtt_probe(dev, &dev_priv->gtt.total, -- cgit v1.2.3 From 93c34e70ebf464a9ee142d93b681c5df094ec654 Mon Sep 17 00:00:00 2001 From: Kenneth Graunke Date: Mon, 22 Apr 2013 00:53:50 -0700 Subject: drm/i915: Fix page table entries for Bay Trail. On Bay Trail, bit 1 means "writeable by the GPU." Failing to set that means basically anything using the GPU will cause hangs. v2: Drop accidental inline keyword on byt_pte_encode. Signed-off-by: Kenneth Graunke Reviewed-by: Jani Nikula Tested-by: Daniel Leung [v1] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_gtt.c | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 92e147ff8665..62058dc760ac 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -73,6 +73,27 @@ static gen6_gtt_pte_t gen6_pte_encode(struct drm_device *dev, return pte; } +#define BYT_PTE_WRITEABLE (1 << 1) +#define BYT_PTE_SNOOPED_BY_CPU_CACHES (1 << 2) + +static gen6_gtt_pte_t byt_pte_encode(struct drm_device *dev, + dma_addr_t addr, + enum i915_cache_level level) +{ + gen6_gtt_pte_t pte = GEN6_PTE_VALID; + pte |= GEN6_PTE_ADDR_ENCODE(addr); + + /* Mark the page as writeable. Other platforms don't have a + * setting for read-only/writable, so this matches that behavior. + */ + pte |= BYT_PTE_WRITEABLE; + + if (level != I915_CACHE_NONE) + pte |= BYT_PTE_SNOOPED_BY_CPU_CACHES; + + return pte; +} + static int gen6_ppgtt_enable(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; @@ -234,7 +255,11 @@ static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt) first_pd_entry_in_global_pt = gtt_total_entries(dev_priv->gtt) - I915_PPGTT_PD_ENTRIES; - ppgtt->pte_encode = gen6_pte_encode; + if (IS_VALLEYVIEW(dev)) { + ppgtt->pte_encode = byt_pte_encode; + } else { + ppgtt->pte_encode = gen6_pte_encode; + } ppgtt->num_pd_entries = I915_PPGTT_PD_ENTRIES; ppgtt->enable = gen6_ppgtt_enable; ppgtt->clear_range = gen6_ppgtt_clear_range; @@ -824,7 +849,11 @@ int i915_gem_gtt_init(struct drm_device *dev) } else { dev_priv->gtt.gtt_probe = gen6_gmch_probe; dev_priv->gtt.gtt_remove = gen6_gmch_remove; - dev_priv->gtt.pte_encode = gen6_pte_encode; + if (IS_VALLEYVIEW(dev)) { + dev_priv->gtt.pte_encode = byt_pte_encode; + } else { + dev_priv->gtt.pte_encode = gen6_pte_encode; + } } ret = dev_priv->gtt.gtt_probe(dev, &dev_priv->gtt.total, -- cgit v1.2.3 From 9119708cd484923bbc45fa60740bff58b358b848 Mon Sep 17 00:00:00 2001 From: Kenneth Graunke Date: Mon, 22 Apr 2013 00:53:51 -0700 Subject: drm/i915: Split out Haswell code from gen6_pte_encode. Now that we have function pointers, it's cleaner to just create a new per-platform PTE encoding function. This should be identical in behavior to the previous code. v2: Drop accidental inline keyword on hsw_pte_encode. Signed-off-by: Kenneth Graunke Reviewed-by: Jani Nikula Tested-by: Daniel Leung [v1] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_gtt.c | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 62058dc760ac..ce024bd18eac 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -51,20 +51,13 @@ static gen6_gtt_pte_t gen6_pte_encode(struct drm_device *dev, switch (level) { case I915_CACHE_LLC_MLC: - /* Haswell doesn't set L3 this way */ - if (IS_HASWELL(dev)) - pte |= GEN6_PTE_CACHE_LLC; - else - pte |= GEN6_PTE_CACHE_LLC_MLC; + pte |= GEN6_PTE_CACHE_LLC_MLC; break; case I915_CACHE_LLC: pte |= GEN6_PTE_CACHE_LLC; break; case I915_CACHE_NONE: - if (IS_HASWELL(dev)) - pte |= HSW_PTE_UNCACHED; - else - pte |= GEN6_PTE_UNCACHED; + pte |= GEN6_PTE_UNCACHED; break; default: BUG(); @@ -94,6 +87,19 @@ static gen6_gtt_pte_t byt_pte_encode(struct drm_device *dev, return pte; } +static gen6_gtt_pte_t hsw_pte_encode(struct drm_device *dev, + dma_addr_t addr, + enum i915_cache_level level) +{ + gen6_gtt_pte_t pte = GEN6_PTE_VALID; + pte |= GEN6_PTE_ADDR_ENCODE(addr); + + if (level != I915_CACHE_NONE) + pte |= GEN6_PTE_CACHE_LLC; + + return pte; +} + static int gen6_ppgtt_enable(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; @@ -255,7 +261,9 @@ static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt) first_pd_entry_in_global_pt = gtt_total_entries(dev_priv->gtt) - I915_PPGTT_PD_ENTRIES; - if (IS_VALLEYVIEW(dev)) { + if (IS_HASWELL(dev)) { + ppgtt->pte_encode = hsw_pte_encode; + } else if (IS_VALLEYVIEW(dev)) { ppgtt->pte_encode = byt_pte_encode; } else { ppgtt->pte_encode = gen6_pte_encode; @@ -849,7 +857,9 @@ int i915_gem_gtt_init(struct drm_device *dev) } else { dev_priv->gtt.gtt_probe = gen6_gmch_probe; dev_priv->gtt.gtt_remove = gen6_gmch_remove; - if (IS_VALLEYVIEW(dev)) { + if (IS_HASWELL(dev)) { + dev_priv->gtt.pte_encode = hsw_pte_encode; + } else if (IS_VALLEYVIEW(dev)) { dev_priv->gtt.pte_encode = byt_pte_encode; } else { dev_priv->gtt.pte_encode = gen6_pte_encode; -- cgit v1.2.3 From 259bd5d4e909a6c07e0da8d6f623d2ab89b7a042 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Mon, 22 Apr 2013 15:59:30 -0700 Subject: drm/i915: fix locking around punit access in cur_delayinfo for VLV We need to hold the rps lock around punit access. Reported-by: Kenneth Graunke Signed-off-by: Jesse Barnes Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 367b534d2260..d195d097cbb7 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1012,6 +1012,7 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused) } else if (IS_VALLEYVIEW(dev)) { u32 freq_sts, val; + mutex_lock(&dev_priv->rps.hw_lock); valleyview_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS, &freq_sts); seq_printf(m, "PUNIT_REG_GPU_FREQ_STS: 0x%08x\n", freq_sts); @@ -1028,6 +1029,7 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused) seq_printf(m, "current GPU freq: %d MHz\n", vlv_gpu_freq(dev_priv->mem_freq, (freq_sts >> 8) & 0xff)); + mutex_unlock(&dev_priv->rps.hw_lock); } else { seq_printf(m, "no P-state info available\n"); } -- cgit v1.2.3 From 142e239849c800f9dc23f828762873073f612d3f Mon Sep 17 00:00:00 2001 From: Egbert Eich Date: Thu, 11 Apr 2013 15:57:57 +0200 Subject: drm/i915: Add bit field to record which pins have received HPD events (v3) This way it is possible to limit 're'-detect() of displays to connectors which have received an HPD event. v2: Reordered drm_i915_private: Move hpd_event_bits to hpd state tracking. v3: Fixed merge conflicts with previous patches. Signed-off-by: Egbert Eich Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/i915_irq.c | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index d80bced96cea..9a171dd83dcf 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -951,6 +951,7 @@ typedef struct drm_i915_private { HPD_MARK_DISABLED = 2 } hpd_mark; } hpd_stats[HPD_NUM_PINS]; + u32 hpd_event_bits; struct timer_list hotplug_reenable_timer; int num_pch_pll; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index dae8953d2912..e37820199b26 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -557,6 +557,7 @@ static void i915_hotplug_work_func(struct work_struct *work) struct drm_connector *connector; unsigned long irqflags; bool hpd_disabled = false; + u32 hpd_event_bits; /* HPD irq before everything is fully set up. */ if (!dev_priv->enable_hotplug_processing) @@ -566,6 +567,9 @@ static void i915_hotplug_work_func(struct work_struct *work) DRM_DEBUG_KMS("running encoder hotplug functions\n"); spin_lock_irqsave(&dev_priv->irq_lock, irqflags); + + hpd_event_bits = dev_priv->hpd_event_bits; + dev_priv->hpd_event_bits = 0; list_for_each_entry(connector, &mode_config->connector_list, head) { intel_connector = to_intel_connector(connector); intel_encoder = intel_connector->encoder; @@ -580,6 +584,10 @@ static void i915_hotplug_work_func(struct work_struct *work) | DRM_CONNECTOR_POLL_DISCONNECT; hpd_disabled = true; } + if (hpd_event_bits & (1 << intel_encoder->hpd_pin)) { + DRM_DEBUG_KMS("Connector %s (pin %i) received hotplug event.\n", + drm_get_connector_name(connector), intel_encoder->hpd_pin); + } } /* if there were no outputs to poll, poll was disabled, * therefore make sure it's enabled when disabling HPD on @@ -844,6 +852,7 @@ static inline bool hotplug_irq_storm_detect(struct drm_device *dev, if (!(hpd[i] & hotplug_trigger) || dev_priv->hpd_stats[i].hpd_mark != HPD_ENABLED) + dev_priv->hpd_event_bits |= (1 << i); continue; if (!time_in_range(jiffies, dev_priv->hpd_stats[i].hpd_last_jiffies, @@ -853,6 +862,7 @@ static inline bool hotplug_irq_storm_detect(struct drm_device *dev, dev_priv->hpd_stats[i].hpd_cnt = 0; } else if (dev_priv->hpd_stats[i].hpd_cnt > HPD_STORM_THRESHOLD) { dev_priv->hpd_stats[i].hpd_mark = HPD_MARK_DISABLED; + dev_priv->hpd_event_bits &= ~(1 << i); DRM_DEBUG_KMS("HPD interrupt storm detected on PIN %d\n", i); ret = true; } else { -- cgit v1.2.3 From 321a1b3026ea194dd084cf3bda1e235b2986b0af Mon Sep 17 00:00:00 2001 From: Egbert Eich Date: Thu, 11 Apr 2013 16:00:26 +0200 Subject: drm/i915: Only reprobe display on encoder which has received an HPD event (v2) Instead of calling into the DRM helper layer to poll all connectors for changes in connected displays probe only those connectors which have received a hotplug event. v2: Resolved conflicts with changes in previous commits. Renamed function and and added a WARN_ON() to warn of intel_hpd_irq_event() from being called without mode_config.mutex held - suggested by Jani Nikula. Signed-off-by: Egbert Eich Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index e37820199b26..7c81f0fb721a 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -541,6 +541,21 @@ static int i915_get_vblank_timestamp(struct drm_device *dev, int pipe, crtc); } +static int intel_hpd_irq_event(struct drm_device *dev, struct drm_connector *connector) +{ + enum drm_connector_status old_status; + + WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); + old_status = connector->status; + + connector->status = connector->funcs->detect(connector, false); + DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %d to %d\n", + connector->base.id, + drm_get_connector_name(connector), + old_status, connector->status); + return (old_status != connector->status); +} + /* * Handle hotplug events outside the interrupt handler proper. */ @@ -557,6 +572,7 @@ static void i915_hotplug_work_func(struct work_struct *work) struct drm_connector *connector; unsigned long irqflags; bool hpd_disabled = false; + bool changed = false; u32 hpd_event_bits; /* HPD irq before everything is fully set up. */ @@ -600,14 +616,20 @@ static void i915_hotplug_work_func(struct work_struct *work) spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); - list_for_each_entry(intel_encoder, &mode_config->encoder_list, base.head) - if (intel_encoder->hot_plug) - intel_encoder->hot_plug(intel_encoder); - + list_for_each_entry(connector, &mode_config->connector_list, head) { + intel_connector = to_intel_connector(connector); + intel_encoder = intel_connector->encoder; + if (hpd_event_bits & (1 << intel_encoder->hpd_pin)) { + if (intel_encoder->hot_plug) + intel_encoder->hot_plug(intel_encoder); + if (intel_hpd_irq_event(dev, connector)) + changed = true; + } + } mutex_unlock(&mode_config->mutex); - /* Just fire off a uevent and let userspace tell us what to do */ - drm_helper_hpd_irq_event(dev); + if (changed) + drm_kms_helper_hotplug_event(dev); } static void ironlake_handle_rps_change(struct drm_device *dev) -- cgit v1.2.3 From 79fc46dfd0c7cc68442554a428e03a3b7e0d44aa Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Tue, 23 Apr 2013 16:37:17 +0100 Subject: drm/i915: Turn DEV_INFO_FLAGS into a foreach style macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DEV_INFO_FOR_FLAG() now takes 2 parameters: • A function to apply to the flag • A separator This will allow us to use the macro twice in the DRM_DEBUG_DRIVER() call of i915_dump_device_info(). v2: Fix a typo in the subject (Jani Nikula) v3: Undef the helper macros (Jani Nikula, Daniel vetter) Reviewed-by: Jani Nikula Signed-off-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 10 ++++---- drivers/gpu/drm/i915/i915_dma.c | 10 ++++---- drivers/gpu/drm/i915/i915_drv.h | 50 ++++++++++++++++++------------------- 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index d195d097cbb7..a55630a80f83 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -61,11 +61,11 @@ static int i915_capabilities(struct seq_file *m, void *data) seq_printf(m, "gen: %d\n", info->gen); seq_printf(m, "pch: %d\n", INTEL_PCH_TYPE(dev)); -#define DEV_INFO_FLAG(x) seq_printf(m, #x ": %s\n", yesno(info->x)) -#define DEV_INFO_SEP ; - DEV_INFO_FLAGS; -#undef DEV_INFO_FLAG -#undef DEV_INFO_SEP +#define PRINT_FLAG(x) seq_printf(m, #x ": %s\n", yesno(info->x)) +#define SEP_SEMICOLON ; + DEV_INFO_FOR_EACH_FLAG(PRINT_FLAG, SEP_SEMICOLON); +#undef PRINT_FLAG +#undef SEP_SEMICOLON return 0; } diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 3b315ba85a3e..cc5fd5f75e4c 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1445,15 +1445,15 @@ static void i915_dump_device_info(struct drm_i915_private *dev_priv) { const struct intel_device_info *info = dev_priv->info; -#define DEV_INFO_FLAG(name) info->name ? #name "," : "" -#define DEV_INFO_SEP , +#define PRINT_FLAG(name) info->name ? #name "," : "" +#define SEP_COMMA , DRM_DEBUG_DRIVER("i915 device info: gen=%i, pciid=0x%04x flags=" "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", info->gen, dev_priv->dev->pdev->device, - DEV_INFO_FLAGS); -#undef DEV_INFO_FLAG -#undef DEV_INFO_SEP + DEV_INFO_FOR_EACH_FLAG(PRINT_FLAG, SEP_COMMA)); +#undef PRINT_FLAG +#undef SEP_COMMA } /** diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 9a171dd83dcf..e3df380646e3 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -333,31 +333,31 @@ struct drm_i915_gt_funcs { void (*force_wake_put)(struct drm_i915_private *dev_priv); }; -#define DEV_INFO_FLAGS \ - DEV_INFO_FLAG(is_mobile) DEV_INFO_SEP \ - DEV_INFO_FLAG(is_i85x) DEV_INFO_SEP \ - DEV_INFO_FLAG(is_i915g) DEV_INFO_SEP \ - DEV_INFO_FLAG(is_i945gm) DEV_INFO_SEP \ - DEV_INFO_FLAG(is_g33) DEV_INFO_SEP \ - DEV_INFO_FLAG(need_gfx_hws) DEV_INFO_SEP \ - DEV_INFO_FLAG(is_g4x) DEV_INFO_SEP \ - DEV_INFO_FLAG(is_pineview) DEV_INFO_SEP \ - DEV_INFO_FLAG(is_broadwater) DEV_INFO_SEP \ - DEV_INFO_FLAG(is_crestline) DEV_INFO_SEP \ - DEV_INFO_FLAG(is_ivybridge) DEV_INFO_SEP \ - DEV_INFO_FLAG(is_valleyview) DEV_INFO_SEP \ - DEV_INFO_FLAG(is_haswell) DEV_INFO_SEP \ - DEV_INFO_FLAG(has_force_wake) DEV_INFO_SEP \ - DEV_INFO_FLAG(has_fbc) DEV_INFO_SEP \ - DEV_INFO_FLAG(has_pipe_cxsr) DEV_INFO_SEP \ - DEV_INFO_FLAG(has_hotplug) DEV_INFO_SEP \ - DEV_INFO_FLAG(cursor_needs_physical) DEV_INFO_SEP \ - DEV_INFO_FLAG(has_overlay) DEV_INFO_SEP \ - DEV_INFO_FLAG(overlay_needs_physical) DEV_INFO_SEP \ - DEV_INFO_FLAG(supports_tv) DEV_INFO_SEP \ - DEV_INFO_FLAG(has_bsd_ring) DEV_INFO_SEP \ - DEV_INFO_FLAG(has_blt_ring) DEV_INFO_SEP \ - DEV_INFO_FLAG(has_llc) +#define DEV_INFO_FOR_EACH_FLAG(func, sep) \ + func(is_mobile) sep \ + func(is_i85x) sep \ + func(is_i915g) sep \ + func(is_i945gm) sep \ + func(is_g33) sep \ + func(need_gfx_hws) sep \ + func(is_g4x) sep \ + func(is_pineview) sep \ + func(is_broadwater) sep \ + func(is_crestline) sep \ + func(is_ivybridge) sep \ + func(is_valleyview) sep \ + func(is_haswell) sep \ + func(has_force_wake) sep \ + func(has_fbc) sep \ + func(has_pipe_cxsr) sep \ + func(has_hotplug) sep \ + func(cursor_needs_physical) sep \ + func(has_overlay) sep \ + func(overlay_needs_physical) sep \ + func(supports_tv) sep \ + func(has_bsd_ring) sep \ + func(has_blt_ring) sep \ + func(has_llc) struct intel_device_info { u32 display_mmio_offset; -- cgit v1.2.3 From e2a5800a14abcf861b9d6565de2a22bd6e9ae0ef Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Tue, 23 Apr 2013 16:38:34 +0100 Subject: drm/i915: Replace the line of %s by a DEV_INFO_FOR_EACH_FLAG() invocation This way, when adding a device flag we don't have to manually maintain that list. v2: undefine the helper macros (Jani Nikula, Daniel Vetter) Signed-off-by: Damien Lespiau Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index cc5fd5f75e4c..cfa1298669ca 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1445,13 +1445,17 @@ static void i915_dump_device_info(struct drm_i915_private *dev_priv) { const struct intel_device_info *info = dev_priv->info; +#define PRINT_S(name) "%s" +#define SEP_EMPTY #define PRINT_FLAG(name) info->name ? #name "," : "" #define SEP_COMMA , DRM_DEBUG_DRIVER("i915 device info: gen=%i, pciid=0x%04x flags=" - "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + DEV_INFO_FOR_EACH_FLAG(PRINT_S, SEP_EMPTY), info->gen, dev_priv->dev->pdev->device, DEV_INFO_FOR_EACH_FLAG(PRINT_FLAG, SEP_COMMA)); +#undef PRINT_S +#undef SEP_EMPTY #undef PRINT_FLAG #undef SEP_COMMA } -- cgit v1.2.3 From a587f77987295ddec59afb653cea5335b4d1e191 Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Mon, 22 Apr 2013 18:40:38 +0100 Subject: drm/i915: Use DEV_INFO_FOR_EACH_FLAG() to declare flags as well Signed-off-by: Damien Lespiau Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 31 +++++++------------------------ 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index e3df380646e3..ec8eadd179c4 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -359,36 +359,19 @@ struct drm_i915_gt_funcs { func(has_blt_ring) sep \ func(has_llc) +#define DEFINE_FLAG(name) u8 name:1 +#define SEP_SEMICOLON ; + struct intel_device_info { u32 display_mmio_offset; u8 num_pipes:3; u8 gen; - u8 is_mobile:1; - u8 is_i85x:1; - u8 is_i915g:1; - u8 is_i945gm: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_ivybridge:1; - u8 is_valleyview:1; - u8 has_force_wake:1; - u8 is_haswell:1; - u8 has_fbc: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; - u8 has_llc:1; + DEV_INFO_FOR_EACH_FLAG(DEFINE_FLAG, SEP_SEMICOLON); }; +#undef DEFINE_FLAG +#undef SEP_SEMICOLON + enum i915_cache_level { I915_CACHE_NONE = 0, I915_CACHE_LLC, -- cgit v1.2.3 From dd93be584099b157039c43c7b48eac56223ac94d Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Mon, 22 Apr 2013 18:40:39 +0100 Subject: drm/i915: Turn HAS_DDI() into a device_info flag Signed-off-by: Damien Lespiau Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.c | 2 ++ drivers/gpu/drm/i915/i915_drv.h | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 9ebe895c17d6..564d4c65e25b 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -308,12 +308,14 @@ static const struct intel_device_info intel_valleyview_d_info = { static const struct intel_device_info intel_haswell_d_info = { GEN7_FEATURES, .is_haswell = 1, + .has_ddi = 1, }; static const struct intel_device_info intel_haswell_m_info = { GEN7_FEATURES, .is_haswell = 1, .is_mobile = 1, + .has_ddi = 1, }; static const struct pci_device_id pciidlist[] = { /* aka */ diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index ec8eadd179c4..5407714dee8b 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -357,7 +357,8 @@ struct drm_i915_gt_funcs { func(supports_tv) sep \ func(has_bsd_ring) sep \ func(has_blt_ring) sep \ - func(has_llc) + func(has_llc) sep \ + func(has_ddi) #define DEFINE_FLAG(name) u8 name:1 #define SEP_SEMICOLON ; @@ -1367,7 +1368,7 @@ struct drm_i915_file_private { #define HAS_PIPE_CONTROL(dev) (INTEL_INFO(dev)->gen >= 5) -#define HAS_DDI(dev) (IS_HASWELL(dev)) +#define HAS_DDI(dev) (INTEL_INFO(dev)->has_ddi) #define HAS_POWER_WELL(dev) (IS_HASWELL(dev)) #define INTEL_PCH_DEVICE_ID_MASK 0xff00 -- cgit v1.2.3 From e76ebff887e9cc4a8448a0fc6abbb1925291f38b Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Mon, 22 Apr 2013 18:40:40 +0100 Subject: drm/i915: Introduce HAS_FPGA_DBG_UNCLAIMED() Let's introduce one more of those orthogonal feature macros. This should hopefully make the code more readable and make things easier for new platform enabling. This time, HAS_FPGA_DBG_UNCLAIMED() is true for platforms that have bit 31 of FPGA_DBG able to signal unclaimed writes. Signed-off-by: Damien Lespiau Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 2 +- drivers/gpu/drm/i915/i915_drv.c | 4 ++-- drivers/gpu/drm/i915/i915_drv.h | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index cfa1298669ca..13a72e63b4e9 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1472,7 +1472,7 @@ static void intel_early_sanitize_regs(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - if (IS_HASWELL(dev)) + if (HAS_FPGA_DBG_UNCLAIMED(dev)) I915_WRITE_NOTRACE(FPGA_DBG, FPGA_DBG_RM_NOCLAIM); } diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 564d4c65e25b..896b90430ba9 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -1205,7 +1205,7 @@ ilk_dummy_write(struct drm_i915_private *dev_priv) static void hsw_unclaimed_reg_clear(struct drm_i915_private *dev_priv, u32 reg) { - if (IS_HASWELL(dev_priv->dev) && + if (HAS_FPGA_DBG_UNCLAIMED(dev_priv->dev) && (I915_READ_NOTRACE(FPGA_DBG) & FPGA_DBG_RM_NOCLAIM)) { DRM_ERROR("Unknown unclaimed register before writing to %x\n", reg); @@ -1216,7 +1216,7 @@ hsw_unclaimed_reg_clear(struct drm_i915_private *dev_priv, u32 reg) static void hsw_unclaimed_reg_check(struct drm_i915_private *dev_priv, u32 reg) { - if (IS_HASWELL(dev_priv->dev) && + if (HAS_FPGA_DBG_UNCLAIMED(dev_priv->dev) && (I915_READ_NOTRACE(FPGA_DBG) & FPGA_DBG_RM_NOCLAIM)) { DRM_ERROR("Unclaimed write to %x\n", reg); I915_WRITE_NOTRACE(FPGA_DBG, FPGA_DBG_RM_NOCLAIM); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 5407714dee8b..ffd325c70407 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1370,6 +1370,7 @@ struct drm_i915_file_private { #define HAS_DDI(dev) (INTEL_INFO(dev)->has_ddi) #define HAS_POWER_WELL(dev) (IS_HASWELL(dev)) +#define HAS_FPGA_DBG_UNCLAIMED(dev) (IS_HASWELL(dev)) #define INTEL_PCH_DEVICE_ID_MASK 0xff00 #define INTEL_PCH_IBX_DEVICE_ID_TYPE 0x3b00 -- cgit v1.2.3 From 30568c45d9fc4ee0bb9e1da90e185692fcd67e38 Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Mon, 22 Apr 2013 18:40:41 +0100 Subject: drm/i915: Turn HAS_FPGA_DBG_UNCLAIMED into a device_info flag Signed-off-by: Damien Lespiau Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.c | 2 ++ drivers/gpu/drm/i915/i915_drv.h | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 896b90430ba9..624cdfcc1ba3 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -309,6 +309,7 @@ static const struct intel_device_info intel_haswell_d_info = { GEN7_FEATURES, .is_haswell = 1, .has_ddi = 1, + .has_fpga_dbg = 1, }; static const struct intel_device_info intel_haswell_m_info = { @@ -316,6 +317,7 @@ static const struct intel_device_info intel_haswell_m_info = { .is_haswell = 1, .is_mobile = 1, .has_ddi = 1, + .has_fpga_dbg = 1, }; static const struct pci_device_id pciidlist[] = { /* aka */ diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index ffd325c70407..a4234a837032 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -358,7 +358,8 @@ struct drm_i915_gt_funcs { func(has_bsd_ring) sep \ func(has_blt_ring) sep \ func(has_llc) sep \ - func(has_ddi) + func(has_ddi) sep \ + func(has_fpga_dbg) #define DEFINE_FLAG(name) u8 name:1 #define SEP_SEMICOLON ; @@ -1370,7 +1371,7 @@ struct drm_i915_file_private { #define HAS_DDI(dev) (INTEL_INFO(dev)->has_ddi) #define HAS_POWER_WELL(dev) (IS_HASWELL(dev)) -#define HAS_FPGA_DBG_UNCLAIMED(dev) (IS_HASWELL(dev)) +#define HAS_FPGA_DBG_UNCLAIMED(dev) (INTEL_INFO(dev)->has_fpga_dbg) #define INTEL_PCH_DEVICE_ID_MASK 0xff00 #define INTEL_PCH_IBX_DEVICE_ID_TYPE 0x3b00 -- cgit v1.2.3 From 52ceb908018d3ac3c19cea85d5e407705f0a79c3 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Tue, 23 Apr 2013 10:09:26 -0700 Subject: drm/i915: make sure GPU freq drops to minimum after entering RC6 v4 On VLV, the Punit doesn't automatically drop the GPU to it's minimum voltage level when entering RC6, so we arm a timer to do it for us from the RPS interrupt handler. It'll generally only fire when we go idle (or if for some reason there's a long delay between RPS interrupts), but won't be re-armed again until the next RPS event, so shouldn't affect power consumption after we go idle and it triggers. v2: use delayed work instead of timer + work queue combo (Ville) v3: fix up delayed work cancel (must be outside lock) (Daniel) fix up delayed work handling func for delayed work (Jesse) v4: cancel delayed work before RPS shutdown (Jani) pass delay not absolute time to mod_delayed_work (Jani) Signed-off-by: Jesse Barnes Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 2 ++ drivers/gpu/drm/i915/i915_irq.c | 11 +++++++++++ drivers/gpu/drm/i915/intel_pm.c | 22 ++++++++++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index a4234a837032..95a3cc367d50 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -653,6 +653,7 @@ struct i915_suspend_saved_registers { struct intel_gen6_power_mgmt { struct work_struct work; + struct delayed_work vlv_work; u32 pm_iir; /* lock - irqsave spinlock that protectects the work_struct and * pm_iir. */ @@ -663,6 +664,7 @@ struct intel_gen6_power_mgmt { u8 cur_delay; u8 min_delay; u8 max_delay; + u8 rpe_delay; u8 hw_max; struct delayed_work delayed_resume_work; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 7c81f0fb721a..3cf646cdab1a 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -725,6 +725,17 @@ static void gen6_pm_rps_work(struct work_struct *work) gen6_set_rps(dev_priv->dev, new_delay); } + if (IS_VALLEYVIEW(dev_priv->dev)) { + /* + * On VLV, when we enter RC6 we may not be at the minimum + * voltage level, so arm a timer to check. It should only + * fire when there's activity or once after we've entered + * RC6, and then won't be re-armed until the next RPS interrupt. + */ + mod_delayed_work(dev_priv->wq, &dev_priv->rps.vlv_work, + msecs_to_jiffies(100)); + } + mutex_unlock(&dev_priv->rps.hw_lock); } diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 2557926553b3..93b01e197f42 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -2822,6 +2822,23 @@ int valleyview_rps_min_freq(struct drm_i915_private *dev_priv) return val & 0xff; } +static void vlv_rps_timer_work(struct work_struct *work) +{ + drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t, + rps.vlv_work.work); + + /* + * Timer fired, we must be idle. Drop to min voltage state. + * Note: we use RPe here since it should match the + * Vmin we were shooting for. That should give us better + * perf when we come back out of RC6 than if we used the + * min freq available. + */ + mutex_lock(&dev_priv->rps.hw_lock); + valleyview_set_rps(dev_priv->dev, dev_priv->rps.rpe_delay); + mutex_unlock(&dev_priv->rps.hw_lock); +} + static void valleyview_enable_rps(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -2886,6 +2903,7 @@ static void valleyview_enable_rps(struct drm_device *dev) rpe = valleyview_rps_rpe_freq(dev_priv); DRM_DEBUG_DRIVER("RPe GPU freq: %d\n", vlv_gpu_freq(dev_priv->mem_freq, rpe)); + dev_priv->rps.rpe_delay = rpe; val = valleyview_rps_min_freq(dev_priv); DRM_DEBUG_DRIVER("min GPU freq: %d\n", vlv_gpu_freq(dev_priv->mem_freq, @@ -2895,6 +2913,8 @@ static void valleyview_enable_rps(struct drm_device *dev) DRM_DEBUG_DRIVER("setting GPU freq to %d\n", vlv_gpu_freq(dev_priv->mem_freq, rpe)); + INIT_DELAYED_WORK(&dev_priv->rps.vlv_work, vlv_rps_timer_work); + valleyview_set_rps(dev_priv->dev, rpe); /* requires MSI enabled */ @@ -3637,6 +3657,8 @@ void intel_disable_gt_powersave(struct drm_device *dev) ironlake_disable_rc6(dev); } else if (INTEL_INFO(dev)->gen >= 6) { cancel_delayed_work_sync(&dev_priv->rps.delayed_resume_work); + if (IS_VALLEYVIEW(dev)) + cancel_delayed_work_sync(&dev_priv->rps.vlv_work); mutex_lock(&dev_priv->rps.hw_lock); gen6_disable_rps(dev); mutex_unlock(&dev_priv->rps.hw_lock); -- cgit v1.2.3 From 250848ca04b734c91f491b7c9b6045d2198a208c Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Tue, 23 Apr 2013 10:09:27 -0700 Subject: drm/i915: cancel RPS work before disabling RPS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ville noticed this while doing another review; we may as well cancel this work just to make sure we don't try anything fancy after disabling the RPS interfaces. Reported-by: Ville Syrjälä Signed-off-by: Jesse Barnes Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_pm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 93b01e197f42..72ad817b6bf4 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3657,6 +3657,7 @@ void intel_disable_gt_powersave(struct drm_device *dev) ironlake_disable_rc6(dev); } else if (INTEL_INFO(dev)->gen >= 6) { cancel_delayed_work_sync(&dev_priv->rps.delayed_resume_work); + cancel_work_sync(&dev_priv->rps.work); if (IS_VALLEYVIEW(dev)) cancel_delayed_work_sync(&dev_priv->rps.vlv_work); mutex_lock(&dev_priv->rps.hw_lock); -- cgit v1.2.3 From d20d4f0ca343c0b76567d46fcc343c165e8d7c43 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Tue, 23 Apr 2013 10:09:28 -0700 Subject: drm/i915: create spearate VLV disable_rps function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't want to write reserved regs here, and may want to do other bits in the future, so split it out. Reported-by: Ville Syrjälä Signed-off-by: Jesse Barnes Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_pm.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 72ad817b6bf4..8b7f050decdd 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -2547,6 +2547,25 @@ static void gen6_disable_rps(struct drm_device *dev) I915_WRITE(GEN6_PMIIR, I915_READ(GEN6_PMIIR)); } +static void valleyview_disable_rps(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + I915_WRITE(GEN6_RC_CONTROL, 0); + I915_WRITE(GEN6_PMINTRMSK, 0xffffffff); + I915_WRITE(GEN6_PMIER, 0); + /* Complete PM interrupt masking here doesn't race with the rps work + * item again unmasking PM interrupts because that is using a different + * register (PMIMR) to mask PM interrupts. The only risk is in leaving + * stale bits in PMIIR and PMIMR which gen6_enable_rps will clean up. */ + + spin_lock_irq(&dev_priv->rps.lock); + dev_priv->rps.pm_iir = 0; + spin_unlock_irq(&dev_priv->rps.lock); + + I915_WRITE(GEN6_PMIIR, I915_READ(GEN6_PMIIR)); +} + int intel_enable_rc6(const struct drm_device *dev) { /* Respect the kernel parameter if it is set */ @@ -3661,7 +3680,10 @@ void intel_disable_gt_powersave(struct drm_device *dev) if (IS_VALLEYVIEW(dev)) cancel_delayed_work_sync(&dev_priv->rps.vlv_work); mutex_lock(&dev_priv->rps.hw_lock); - gen6_disable_rps(dev); + if (IS_VALLEYVIEW(dev)) + valleyview_disable_rps(dev); + else + gen6_disable_rps(dev); mutex_unlock(&dev_priv->rps.hw_lock); } } -- cgit v1.2.3 From fd0c06420d39958032655a04cfd194d5a7b38f83 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 24 Apr 2013 11:13:35 +0200 Subject: drm/i915: disable interrupts earlier in the driver unload code Our rps code relies on the interrupts being off to prevent re-arming of the work items at inopportune moments. Also drop the redundant cancel_work for the main rps work, disable_gt_powersave already takes care of that. Finally add a WARN_ON to ensure we obey that piece of ordering constraint. Long term I want to lock down the setup/teardown code in a similar way to how we painstakingly check modeset sequence constraints already. v2: Disable polling after hpd handling is shut down - since Egbert's hpd irq storm handling the hotplug work can re-arm the polling handler. Spotted by Jani Nikula. Cc: Jesse Barnes Cc: Jani Nikula Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 19 ++++++++++++------- drivers/gpu/drm/i915/intel_pm.c | 3 +++ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 74156e231fd5..988e543ec45b 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -9530,12 +9530,23 @@ void intel_modeset_cleanup(struct drm_device *dev) struct drm_crtc *crtc; struct intel_crtc *intel_crtc; + /* + * Interrupts and polling as the first thing to avoid creating havoc. + * Too much stuff here (turning of rps, connectors, ...) would + * experience fancy races otherwise. + */ + drm_irq_uninstall(dev); + cancel_work_sync(&dev_priv->hotplug_work); + /* + * Due to the hpd irq storm handling the hotplug work can re-arm the + * poll handlers. Hence disable polling after hpd handling is shut down. + */ drm_kms_helper_poll_fini(dev); + mutex_lock(&dev->struct_mutex); intel_unregister_dsm_handler(); - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { /* Skip inactive CRTCs */ if (!crtc->fb) @@ -9553,12 +9564,6 @@ 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); - cancel_work_sync(&dev_priv->rps.work); - /* flush any delayed tasks or pending work */ flush_scheduled_work(); diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 8b7f050decdd..bb45122173dd 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3671,6 +3671,9 @@ void intel_disable_gt_powersave(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + /* Interrupts should be disabled already to avoid re-arming. */ + WARN_ON(dev->irq_enabled); + if (IS_IRONLAKE_M(dev)) { ironlake_disable_drps(dev); ironlake_disable_rc6(dev); -- cgit v1.2.3 From 996a2239f93b03c5972923f04b097f65565c5bed Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 19 Apr 2013 11:24:34 +0200 Subject: drm/i915: Disable high-bpc on pre-1.4 EDID screens MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prevents black screens when using 30bpp framebuffers on my HDMI screens here. The DP input on the same screen though reports a 1.4 EDID with the correct 8bpc limit set. v2: Actually check for the right thing! Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 988e543ec45b..15ce99125c64 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7694,6 +7694,13 @@ pipe_config_set_bpp(struct drm_crtc *crtc, bpp, connector->display_info.bpc*3); pipe_config->pipe_bpp = connector->display_info.bpc*3; } + + /* Clamp bpp to 8 on screens without EDID 1.4 */ + if (connector->display_info.bpc == 0 && bpp > 24) { + DRM_DEBUG_KMS("clamping display bpp (was %d) to default limit of 24\n", + bpp); + pipe_config->pipe_bpp = 24; + } } return bpp; -- cgit v1.2.3 From 2a7aceecf15a463ba6bfa83b6579e75bb4703cd9 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 19 Apr 2013 11:24:39 +0200 Subject: drm/i915: Fixup non-24bpp support for VGA screens on Haswell MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The LPT PCH only supports 8bpc, so we need to force the pipe bpp to the right value. Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_crt.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index c063b9f0dd51..991e53047e1d 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -214,6 +214,10 @@ static bool intel_crt_compute_config(struct intel_encoder *encoder, if (HAS_PCH_SPLIT(dev)) pipe_config->has_pch_encoder = true; + /* LPT FDI RX only supports 8bpc. */ + if (HAS_PCH_LPT(dev)) + pipe_config->pipe_bpp = 24; + return true; } -- cgit v1.2.3 From d65406327345e5a5e0f697a3ffe3e53bc9b5d7c6 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Fri, 12 Apr 2013 15:18:36 +0300 Subject: drm/i915: keep max backlight internal to intel_panel.c In preparation of adding locking to backlight, make max backlight value (the modulation frequency the PWM duty cycle value must not exceed) internal to intel_panel.c. Have intel_panel_set_backlight() accept a caller defined range for level, and scale input to max backlight value internally. Clean up intel_panel_get_max_backlight() and usage internally. Signed-off-by: Jani Nikula Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_drv.h | 4 +-- drivers/gpu/drm/i915/intel_opregion.c | 4 +-- drivers/gpu/drm/i915/intel_panel.c | 51 ++++++++++++++++++++--------------- 3 files changed, 32 insertions(+), 27 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 66922f8f1bc5..3595c8204649 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -553,8 +553,8 @@ extern void intel_pch_panel_fitting(struct drm_device *dev, int fitting_mode, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode); -extern u32 intel_panel_get_max_backlight(struct drm_device *dev); -extern void intel_panel_set_backlight(struct drm_device *dev, u32 level); +extern void intel_panel_set_backlight(struct drm_device *dev, + u32 level, u32 max); extern int intel_panel_setup_backlight(struct drm_connector *connector); extern void intel_panel_enable_backlight(struct drm_device *dev, enum pipe pipe); diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c index 4d338740f2cb..42faa5885f5a 100644 --- a/drivers/gpu/drm/i915/intel_opregion.c +++ b/drivers/gpu/drm/i915/intel_opregion.c @@ -152,7 +152,6 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp) { struct drm_i915_private *dev_priv = dev->dev_private; struct opregion_asle __iomem *asle = dev_priv->opregion.asle; - u32 max; DRM_DEBUG_DRIVER("bclp = 0x%08x\n", bclp); @@ -163,8 +162,7 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp) if (bclp > 255) return ASLE_BACKLIGHT_FAILED; - max = intel_panel_get_max_backlight(dev); - intel_panel_set_backlight(dev, bclp * max / 255); + intel_panel_set_backlight(dev, bclp, 255); iowrite32((bclp*0x64)/0xff | ASLE_CBLV_VALID, &asle->cblv); return 0; diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index eb5e6e95f3c7..63a7c36a7603 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -130,6 +130,9 @@ static int is_backlight_combination_mode(struct drm_device *dev) return 0; } +/* XXX: query mode clock or hardware clock and program max PWM appropriately + * when it's 0. + */ static u32 i915_read_blc_pwm_ctl(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -164,7 +167,7 @@ static u32 i915_read_blc_pwm_ctl(struct drm_device *dev) return val; } -static u32 _intel_panel_get_max_backlight(struct drm_device *dev) +static u32 intel_panel_get_max_backlight(struct drm_device *dev) { u32 max; @@ -182,23 +185,8 @@ static u32 _intel_panel_get_max_backlight(struct drm_device *dev) max *= 0xff; } - return max; -} - -u32 intel_panel_get_max_backlight(struct drm_device *dev) -{ - u32 max; - - max = _intel_panel_get_max_backlight(dev); - if (max == 0) { - /* XXX add code here to query mode clock or hardware clock - * and program max PWM appropriately. - */ - pr_warn_once("fixme: max PWM is zero\n"); - return 1; - } - DRM_DEBUG_DRIVER("max backlight PWM = %d\n", max); + return max; } @@ -217,8 +205,11 @@ static u32 intel_panel_compute_brightness(struct drm_device *dev, u32 val) return val; if (i915_panel_invert_brightness > 0 || - dev_priv->quirks & QUIRK_INVERT_BRIGHTNESS) - return intel_panel_get_max_backlight(dev) - val; + dev_priv->quirks & QUIRK_INVERT_BRIGHTNESS) { + u32 max = intel_panel_get_max_backlight(dev); + if (max) + return max - val; + } return val; } @@ -270,6 +261,10 @@ static void intel_panel_actually_set_backlight(struct drm_device *dev, u32 level u32 max = intel_panel_get_max_backlight(dev); u8 lbpc; + /* we're screwed, but keep behaviour backwards compatible */ + if (!max) + max = 1; + lbpc = level * 0xfe / max + 1; level /= lbpc; pci_write_config_byte(dev->pdev, PCI_LBPC, lbpc); @@ -282,9 +277,20 @@ static void intel_panel_actually_set_backlight(struct drm_device *dev, u32 level I915_WRITE(BLC_PWM_CTL, tmp | level); } -void intel_panel_set_backlight(struct drm_device *dev, u32 level) +/* set backlight brightness to level in range [0..max] */ +void intel_panel_set_backlight(struct drm_device *dev, u32 level, u32 max) { struct drm_i915_private *dev_priv = dev->dev_private; + u32 freq; + + freq = intel_panel_get_max_backlight(dev); + if (!freq) { + /* we are screwed, bail out */ + return; + } + + /* scale to hardware */ + level = level * freq / max; dev_priv->backlight.level = level; if (dev_priv->backlight.device) @@ -405,7 +411,8 @@ intel_panel_detect(struct drm_device *dev) static int intel_panel_update_status(struct backlight_device *bd) { struct drm_device *dev = bl_get_data(bd); - intel_panel_set_backlight(dev, bd->props.brightness); + intel_panel_set_backlight(dev, bd->props.brightness, + bd->props.max_brightness); return 0; } @@ -434,7 +441,7 @@ int intel_panel_setup_backlight(struct drm_connector *connector) memset(&props, 0, sizeof(props)); props.type = BACKLIGHT_RAW; props.brightness = dev_priv->backlight.level; - props.max_brightness = _intel_panel_get_max_backlight(dev); + props.max_brightness = intel_panel_get_max_backlight(dev); if (props.max_brightness == 0) { DRM_DEBUG_DRIVER("Failed to get maximum backlight value\n"); return -ENODEV; -- cgit v1.2.3 From 8ba2d18520ce380cf572e9902d9b3b91ece6c2c0 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Fri, 12 Apr 2013 15:18:37 +0300 Subject: drm/i915: protect backlight registers and data with a spinlock Backlight data and registers are fiddled through LVDS/eDP modeset enable/disable hooks, backlight sysfs files, asle interrupts, and register save/restore. Protect the backlight related registers and driver private fields using a spinlock. The locking in register save/restore covers a little more than is strictly necessary, including non-modeset case, for simplicity. v2: Cover register access, save/restore, i915_read_blc_pwm_ctl() and code paths leading there. Signed-off-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 1 + drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/i915_suspend.c | 10 ++++++++++ drivers/gpu/drm/i915/intel_panel.c | 30 +++++++++++++++++++++++++++++- 4 files changed, 41 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 13a72e63b4e9..a1648eb3a8b1 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1633,6 +1633,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) spin_lock_init(&dev_priv->irq_lock); spin_lock_init(&dev_priv->gpu_error.lock); spin_lock_init(&dev_priv->rps.lock); + spin_lock_init(&dev_priv->backlight.lock); mutex_init(&dev_priv->dpio_lock); mutex_init(&dev_priv->rps.hw_lock); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 95a3cc367d50..f6f4839484d1 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -960,6 +960,7 @@ typedef struct drm_i915_private { struct { int level; bool enabled; + spinlock_t lock; /* bl registers and the above bl fields */ struct backlight_device *device; } backlight; diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c index 41f0fdecfbdc..88b9a663944f 100644 --- a/drivers/gpu/drm/i915/i915_suspend.c +++ b/drivers/gpu/drm/i915/i915_suspend.c @@ -192,6 +192,7 @@ static void i915_restore_vga(struct drm_device *dev) static void i915_save_display(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + unsigned long flags; /* Display arbitration control */ if (INTEL_INFO(dev)->gen <= 4) @@ -202,6 +203,8 @@ static void i915_save_display(struct drm_device *dev) if (!drm_core_check_feature(dev, DRIVER_MODESET)) i915_save_display_reg(dev); + spin_lock_irqsave(&dev_priv->backlight.lock, flags); + /* LVDS state */ if (HAS_PCH_SPLIT(dev)) { dev_priv->regfile.savePP_CONTROL = I915_READ(PCH_PP_CONTROL); @@ -222,6 +225,8 @@ static void i915_save_display(struct drm_device *dev) dev_priv->regfile.saveLVDS = I915_READ(LVDS); } + spin_unlock_irqrestore(&dev_priv->backlight.lock, flags); + if (!IS_I830(dev) && !IS_845G(dev) && !HAS_PCH_SPLIT(dev)) dev_priv->regfile.savePFIT_CONTROL = I915_READ(PFIT_CONTROL); @@ -257,6 +262,7 @@ static void i915_restore_display(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; u32 mask = 0xffffffff; + unsigned long flags; /* Display arbitration */ if (INTEL_INFO(dev)->gen <= 4) @@ -265,6 +271,8 @@ static void i915_restore_display(struct drm_device *dev) if (!drm_core_check_feature(dev, DRIVER_MODESET)) i915_restore_display_reg(dev); + spin_lock_irqsave(&dev_priv->backlight.lock, flags); + /* LVDS state */ if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev)) I915_WRITE(BLC_PWM_CTL2, dev_priv->regfile.saveBLC_PWM_CTL2); @@ -304,6 +312,8 @@ static void i915_restore_display(struct drm_device *dev) I915_WRITE(PP_CONTROL, dev_priv->regfile.savePP_CONTROL); } + spin_unlock_irqrestore(&dev_priv->backlight.lock, flags); + /* only restore FBC info on the platform that supports FBC*/ intel_disable_fbc(dev); if (I915_HAS_FBC(dev)) { diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index 63a7c36a7603..5d3e9d7d51a7 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -138,6 +138,8 @@ static u32 i915_read_blc_pwm_ctl(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; u32 val; + WARN_ON(!spin_is_locked(&dev_priv->backlight.lock)); + /* Restore the CTL value if it lost, e.g. GPU reset */ if (HAS_PCH_SPLIT(dev_priv->dev)) { @@ -218,6 +220,9 @@ static u32 intel_panel_get_backlight(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; u32 val; + unsigned long flags; + + spin_lock_irqsave(&dev_priv->backlight.lock, flags); if (HAS_PCH_SPLIT(dev)) { val = I915_READ(BLC_PWM_CPU_CTL) & BACKLIGHT_DUTY_CYCLE_MASK; @@ -235,6 +240,9 @@ static u32 intel_panel_get_backlight(struct drm_device *dev) } val = intel_panel_compute_brightness(dev, val); + + spin_unlock_irqrestore(&dev_priv->backlight.lock, flags); + DRM_DEBUG_DRIVER("get backlight PWM = %d\n", val); return val; } @@ -282,11 +290,14 @@ void intel_panel_set_backlight(struct drm_device *dev, u32 level, u32 max) { struct drm_i915_private *dev_priv = dev->dev_private; u32 freq; + unsigned long flags; + + spin_lock_irqsave(&dev_priv->backlight.lock, flags); freq = intel_panel_get_max_backlight(dev); if (!freq) { /* we are screwed, bail out */ - return; + goto out; } /* scale to hardware */ @@ -298,11 +309,16 @@ void intel_panel_set_backlight(struct drm_device *dev, u32 level, u32 max) if (dev_priv->backlight.enabled) intel_panel_actually_set_backlight(dev, level); +out: + spin_unlock_irqrestore(&dev_priv->backlight.lock, flags); } void intel_panel_disable_backlight(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + unsigned long flags; + + spin_lock_irqsave(&dev_priv->backlight.lock, flags); dev_priv->backlight.enabled = false; intel_panel_actually_set_backlight(dev, 0); @@ -320,12 +336,17 @@ void intel_panel_disable_backlight(struct drm_device *dev) I915_WRITE(BLC_PWM_PCH_CTL1, tmp); } } + + spin_unlock_irqrestore(&dev_priv->backlight.lock, flags); } void intel_panel_enable_backlight(struct drm_device *dev, enum pipe pipe) { struct drm_i915_private *dev_priv = dev->dev_private; + unsigned long flags; + + spin_lock_irqsave(&dev_priv->backlight.lock, flags); if (dev_priv->backlight.level == 0) { dev_priv->backlight.level = intel_panel_get_max_backlight(dev); @@ -375,6 +396,8 @@ set_level: */ dev_priv->backlight.enabled = true; intel_panel_actually_set_backlight(dev, dev_priv->backlight.level); + + spin_unlock_irqrestore(&dev_priv->backlight.lock, flags); } static void intel_panel_init_backlight(struct drm_device *dev) @@ -432,6 +455,7 @@ int intel_panel_setup_backlight(struct drm_connector *connector) struct drm_device *dev = connector->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct backlight_properties props; + unsigned long flags; intel_panel_init_backlight(dev); @@ -441,7 +465,11 @@ int intel_panel_setup_backlight(struct drm_connector *connector) memset(&props, 0, sizeof(props)); props.type = BACKLIGHT_RAW; props.brightness = dev_priv->backlight.level; + + spin_lock_irqsave(&dev_priv->backlight.lock, flags); props.max_brightness = intel_panel_get_max_backlight(dev); + spin_unlock_irqrestore(&dev_priv->backlight.lock, flags); + if (props.max_brightness == 0) { DRM_DEBUG_DRIVER("Failed to get maximum backlight value\n"); return -ENODEV; -- cgit v1.2.3 From 1767afa4d0a6409e24fd9421a7c2df0490a0da4a Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Fri, 12 Apr 2013 15:20:56 +0300 Subject: drm/i915: don't pretend we support ASLE ALS, PFIT, or PFMB In theory, this should prevent the BIOS from requesting them from us, and this should be the right thing. In practice, this is not always the case, and might surprise the BIOS. Signed-off-by: Jani Nikula Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_opregion.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c index 42faa5885f5a..1ed433186762 100644 --- a/drivers/gpu/drm/i915/intel_opregion.c +++ b/drivers/gpu/drm/i915/intel_opregion.c @@ -280,9 +280,7 @@ void intel_opregion_enable_asle(struct drm_device *dev) if (IS_MOBILE(dev)) intel_enable_asle(dev); - iowrite32(ASLE_ALS_EN | ASLE_BLC_EN | ASLE_PFIT_EN | - ASLE_PFMB_EN, - &asle->tche); + iowrite32(ASLE_BLC_EN, &asle->tche); iowrite32(1, &asle->ardy); } } -- cgit v1.2.3 From e93d440b352a1ba9e99cc17f3d46b1841caef3cd Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Fri, 12 Apr 2013 15:20:57 +0300 Subject: drm/i915/opregion: don't pretend we did something when we didn't In theory, the BIOS should not even request these from us now that we aren't claiming we support these, but when it does anyway, don't pretend it succeeded. It should be the right thing to do, but might confuse the BIOS. Signed-off-by: Jani Nikula Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_opregion.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c index 1ed433186762..6f7cdfa935d7 100644 --- a/drivers/gpu/drm/i915/intel_opregion.c +++ b/drivers/gpu/drm/i915/intel_opregion.c @@ -172,29 +172,22 @@ static u32 asle_set_als_illum(struct drm_device *dev, u32 alsi) { /* alsi is the current ALS reading in lux. 0 indicates below sensor range, 0xffff indicates above sensor range. 1-0xfffe are valid */ - return 0; + DRM_DEBUG_DRIVER("Illum is not supported\n"); + return ASLE_ALS_ILLUM_FAILED; } static u32 asle_set_pwm_freq(struct drm_device *dev, u32 pfmb) { - struct drm_i915_private *dev_priv = dev->dev_private; - if (pfmb & ASLE_PFMB_PWM_VALID) { - u32 blc_pwm_ctl = I915_READ(BLC_PWM_CTL); - u32 pwm = pfmb & ASLE_PFMB_PWM_MASK; - blc_pwm_ctl &= BACKLIGHT_DUTY_CYCLE_MASK; - pwm = pwm >> 9; - /* FIXME - what do we do with the PWM? */ - } - return 0; + DRM_DEBUG_DRIVER("PWM freq is not supported\n"); + return ASLE_PWM_FREQ_FAILED; } static u32 asle_set_pfit(struct drm_device *dev, u32 pfit) { /* Panel fitting is currently controlled by the X code, so this is a noop until modesetting support works fully */ - if (!(pfit & ASLE_PFIT_VALID)) - return ASLE_PFIT_FAILED; - return 0; + DRM_DEBUG_DRIVER("Pfit is not supported\n"); + return ASLE_PFIT_FAILED; } void intel_opregion_asle_intr(struct drm_device *dev) -- cgit v1.2.3 From 81a078092ed25b1017d1351c68837b750a09933a Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 24 Apr 2013 22:18:44 +0300 Subject: drm/i915: drop code duplication in favor of asle interrupt handler With the previous work asle and gse interrupt handlers should now be functionally the same. Drop the duplicated code. v2: Drop intel_opregion_gse_intr() also in the !CONFIG_ACPI path. (Damien) Signed-off-by: Jani Nikula Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 2 -- drivers/gpu/drm/i915/i915_irq.c | 4 ++-- drivers/gpu/drm/i915/intel_opregion.c | 37 ----------------------------------- 3 files changed, 2 insertions(+), 41 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index f6f4839484d1..8a257a9114c2 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1821,13 +1821,11 @@ extern int intel_opregion_setup(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 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 diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 3cf646cdab1a..125b45a6780d 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1205,7 +1205,7 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg) dp_aux_irq_handler(dev); if (de_iir & DE_GSE_IVB) - intel_opregion_gse_intr(dev); + intel_opregion_asle_intr(dev); for (i = 0; i < 3; i++) { if (de_iir & (DE_PIPEA_VBLANK_IVB << (5 * i))) @@ -1302,7 +1302,7 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) dp_aux_irq_handler(dev); if (de_iir & DE_GSE) - intel_opregion_gse_intr(dev); + intel_opregion_asle_intr(dev); if (de_iir & DE_PIPEA_VBLANK) drm_handle_vblank(dev, 0); diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c index 6f7cdfa935d7..dda182833209 100644 --- a/drivers/gpu/drm/i915/intel_opregion.c +++ b/drivers/gpu/drm/i915/intel_opregion.c @@ -222,43 +222,6 @@ void intel_opregion_asle_intr(struct drm_device *dev) iowrite32(asle_stat, &asle->aslc); } -void intel_opregion_gse_intr(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - struct opregion_asle __iomem *asle = dev_priv->opregion.asle; - u32 asle_stat = 0; - u32 asle_req; - - if (!asle) - return; - - asle_req = ioread32(&asle->aslc) & ASLE_REQ_MSK; - - if (!asle_req) { - DRM_DEBUG_DRIVER("non asle set request??\n"); - return; - } - - if (asle_req & ASLE_SET_ALS_ILLUM) { - DRM_DEBUG_DRIVER("Illum is not supported\n"); - asle_stat |= ASLE_ALS_ILLUM_FAILED; - } - - if (asle_req & ASLE_SET_BACKLIGHT) - asle_stat |= asle_set_backlight(dev, ioread32(&asle->bclp)); - - if (asle_req & ASLE_SET_PFIT) { - DRM_DEBUG_DRIVER("Pfit is not supported\n"); - asle_stat |= ASLE_PFIT_FAILED; - } - - if (asle_req & ASLE_SET_PWM_FREQ) { - DRM_DEBUG_DRIVER("PWM freq is not supported\n"); - asle_stat |= ASLE_PWM_FREQ_FAILED; - } - - iowrite32(asle_stat, &asle->aslc); -} #define ASLE_ALS_EN (1<<0) #define ASLE_BLC_EN (1<<1) #define ASLE_PFIT_EN (1<<2) -- cgit v1.2.3 From 35ffda4883a8d3f75632d7389dc96a25640033f0 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 25 Apr 2013 16:49:25 +0300 Subject: drm/i915: hsw backlight registers need transcoder instead of pipe v2: Make TRANSCODER_EDP handling more explicit. (Imre) Signed-off-by: Jani Nikula Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 4 ++++ drivers/gpu/drm/i915/intel_panel.c | 7 ++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 60e0e19373b1..e79669fa9fe2 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -2087,6 +2087,10 @@ #define BLM_PIPE_A (0 << 29) #define BLM_PIPE_B (1 << 29) #define BLM_PIPE_C (2 << 29) /* ivb + */ +#define BLM_TRANSCODER_A BLM_PIPE_A /* hsw */ +#define BLM_TRANSCODER_B BLM_PIPE_B +#define BLM_TRANSCODER_C BLM_PIPE_C +#define BLM_TRANSCODER_EDP (3 << 29) #define BLM_PIPE(pipe) ((pipe) << 29) #define BLM_POLARITY_I965 (1 << 28) /* gen4 only */ #define BLM_PHASE_IN_INTERUPT_STATUS (1 << 26) diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index 5d3e9d7d51a7..7f6141d9a06d 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -344,6 +344,8 @@ void intel_panel_enable_backlight(struct drm_device *dev, enum pipe pipe) { struct drm_i915_private *dev_priv = dev->dev_private; + enum transcoder cpu_transcoder = + intel_pipe_to_cpu_transcoder(dev_priv, pipe); unsigned long flags; spin_lock_irqsave(&dev_priv->backlight.lock, flags); @@ -374,7 +376,10 @@ void intel_panel_enable_backlight(struct drm_device *dev, else tmp &= ~BLM_PIPE_SELECT; - tmp |= BLM_PIPE(pipe); + if (cpu_transcoder == TRANSCODER_EDP) + tmp |= BLM_TRANSCODER_EDP; + else + tmp |= BLM_PIPE(cpu_transcoder); tmp &= ~BLM_PWM_ENABLE; I915_WRITE(reg, tmp); -- cgit v1.2.3 From d49f70915c07c1a313e459d23fad61ddbeeb1bae Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Thu, 25 Apr 2013 15:15:00 +0100 Subject: drm/i915: Ivybridge is the odd one when it comes to pipe scalers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Between ivb, hsw and vlv, only Ivybridge has sprites with scaling capabilities. Also make max_downscale coherent with that. v2: Rebase on top of the recent ivb/vlv/hsw sprite scaling fixes. Signed-off-by: Damien Lespiau (v1) Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_sprite.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index c7d25c5dd4e6..18993ad93540 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -918,13 +918,15 @@ intel_plane_init(struct drm_device *dev, enum pipe pipe, int plane) break; case 7: - if (IS_HASWELL(dev) || IS_VALLEYVIEW(dev)) - intel_plane->can_scale = false; - else + if (IS_IVYBRIDGE(dev)) { intel_plane->can_scale = true; + intel_plane->max_downscale = 2; + } else { + intel_plane->can_scale = false; + intel_plane->max_downscale = 1; + } if (IS_VALLEYVIEW(dev)) { - intel_plane->max_downscale = 1; intel_plane->update_plane = vlv_update_plane; intel_plane->disable_plane = vlv_disable_plane; intel_plane->update_colorkey = vlv_update_colorkey; @@ -933,7 +935,6 @@ intel_plane_init(struct drm_device *dev, enum pipe pipe, int plane) plane_formats = vlv_plane_formats; num_plane_formats = ARRAY_SIZE(vlv_plane_formats); } else { - intel_plane->max_downscale = 2; intel_plane->update_plane = ivb_update_plane; intel_plane->disable_plane = ivb_disable_plane; intel_plane->update_colorkey = ivb_update_colorkey; -- cgit v1.2.3 From cbbab5bdea0df4e85ac7fbc0b3e1d7dee71cb708 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 19 Apr 2013 11:14:31 +0200 Subject: drm/i915: consolidate pch pll computations a bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need the dpll/fp/fp2 values only when we need a pch pll. So move them together with the code to acquire such a pll. Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 15ce99125c64..8e8ac366e75e 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5704,7 +5704,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, int plane = intel_crtc->plane; int num_connectors = 0; intel_clock_t clock, reduced_clock; - u32 dpll, fp = 0, fp2 = 0; + u32 dpll = 0, fp = 0, fp2 = 0; bool ok, has_reduced_clock = false; bool is_lvds = false; struct intel_encoder *encoder; @@ -5749,14 +5749,6 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, if (is_lvds && dev_priv->lvds_dither) dither = true; - fp = clock.n << 16 | clock.m1 << 8 | clock.m2; - if (has_reduced_clock) - fp2 = reduced_clock.n << 16 | reduced_clock.m1 << 8 | - reduced_clock.m2; - - dpll = ironlake_compute_dpll(intel_crtc, &clock, &fp, &reduced_clock, - has_reduced_clock ? &fp2 : NULL); - DRM_DEBUG_KMS("Mode for pipe %c:\n", pipe_name(pipe)); drm_mode_debug_printmodeline(mode); @@ -5764,6 +5756,15 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, if (intel_crtc->config.has_pch_encoder) { struct intel_pch_pll *pll; + fp = clock.n << 16 | clock.m1 << 8 | clock.m2; + if (has_reduced_clock) + fp2 = reduced_clock.n << 16 | reduced_clock.m1 << 8 | + reduced_clock.m2; + + dpll = ironlake_compute_dpll(intel_crtc, &clock, + &fp, &reduced_clock, + has_reduced_clock ? &fp2 : NULL); + pll = intel_get_pch_pll(intel_crtc, dpll, fp); if (pll == NULL) { DRM_DEBUG_DRIVER("failed to find PLL for pipe %c\n", -- cgit v1.2.3 From 7429e9d4bfcfd08a00ed7b760386bd81a60e91d6 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 20 Apr 2013 17:19:46 +0200 Subject: drm/i915: shovel compute clock into crtc->config.dpll on ilk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This was somehow lost in the pipe_config->dpll introduction in commit f47709a9502f3715cc488b788ca91cf0c142b1b1 Author: Daniel Vetter Date: Thu Mar 28 10:42:02 2013 +0100 drm/i915: create pipe_config->dpll for clock state While at it, extract a few small helpers for common computations. v2: Use the newly added helpers more thanks to Ville's trick to typedef the legacy intel_clock_t as the new-world struct dpll. Cc: Ville Syrjälä Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 50 ++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 8e8ac366e75e..f079fcd95dc2 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -549,13 +549,18 @@ static void pineview_clock(int refclk, intel_clock_t *clock) clock->dot = clock->vco / clock->p; } +static uint32_t i9xx_dpll_compute_m(struct dpll *dpll) +{ + return 5 * (dpll->m1 + 2) + (dpll->m2 + 2); +} + static void intel_clock(struct drm_device *dev, int refclk, intel_clock_t *clock) { if (IS_PINEVIEW(dev)) { pineview_clock(refclk, clock); return; } - clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2); + clock->m = i9xx_dpll_compute_m(clock); clock->p = clock->p1 * clock->p2; clock->vco = refclk * clock->m / (clock->n + 2); clock->dot = clock->vco / clock->p; @@ -4241,6 +4246,16 @@ static void i9xx_adjust_sdvo_tv_clock(struct intel_crtc *crtc) crtc->config.clock_set = true; } +static uint32_t pnv_dpll_compute_fp(struct dpll *dpll) +{ + return (1 << dpll->n) << 16 | dpll->m1 << 8 | dpll->m2; +} + +static uint32_t i9xx_dpll_compute_fp(struct dpll *dpll) +{ + return dpll->n << 16 | dpll->m1 << 8 | dpll->m2; +} + static void i9xx_update_pll_dividers(struct intel_crtc *crtc, intel_clock_t *reduced_clock) { @@ -4248,18 +4263,15 @@ static void i9xx_update_pll_dividers(struct intel_crtc *crtc, struct drm_i915_private *dev_priv = dev->dev_private; int pipe = crtc->pipe; u32 fp, fp2 = 0; - struct dpll *clock = &crtc->config.dpll; if (IS_PINEVIEW(dev)) { - fp = (1 << clock->n) << 16 | clock->m1 << 8 | clock->m2; + fp = pnv_dpll_compute_fp(&crtc->config.dpll); if (reduced_clock) - fp2 = (1 << reduced_clock->n) << 16 | - reduced_clock->m1 << 8 | reduced_clock->m2; + fp2 = pnv_dpll_compute_fp(reduced_clock); } else { - fp = clock->n << 16 | clock->m1 << 8 | clock->m2; + fp = i9xx_dpll_compute_fp(&crtc->config.dpll); if (reduced_clock) - fp2 = reduced_clock->n << 16 | reduced_clock->m1 << 8 | - reduced_clock->m2; + fp2 = i9xx_dpll_compute_fp(reduced_clock); } I915_WRITE(FP0(pipe), fp); @@ -5592,8 +5604,13 @@ static void ironlake_fdi_set_m_n(struct drm_crtc *crtc) intel_cpu_transcoder_set_m_n(intel_crtc, &m_n); } +static bool ironlake_needs_fb_cb_tune(struct dpll *dpll, int factor) +{ + return i9xx_dpll_compute_m(dpll) < factor * dpll->n; +} + static uint32_t ironlake_compute_dpll(struct intel_crtc *intel_crtc, - intel_clock_t *clock, u32 *fp, + u32 *fp, intel_clock_t *reduced_clock, u32 *fp2) { struct drm_crtc *crtc = &intel_crtc->base; @@ -5633,7 +5650,7 @@ static uint32_t ironlake_compute_dpll(struct intel_crtc *intel_crtc, } else if (is_sdvo && is_tv) factor = 20; - if (clock->m < factor * clock->n) + if (ironlake_needs_fb_cb_tune(&intel_crtc->config.dpll, factor)) *fp |= FP_CB_TUNE; if (fp2 && (reduced_clock->m < factor * reduced_clock->n)) @@ -5657,11 +5674,11 @@ static uint32_t ironlake_compute_dpll(struct intel_crtc *intel_crtc, dpll |= DPLL_DVO_HIGH_SPEED; /* compute bitmask from p1 value */ - dpll |= (1 << (clock->p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT; + dpll |= (1 << (intel_crtc->config.dpll.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT; /* also FPA1 */ - dpll |= (1 << (clock->p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT; + dpll |= (1 << (intel_crtc->config.dpll.p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT; - switch (clock->p2) { + switch (intel_crtc->config.dpll.p2) { case 5: dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_5; break; @@ -5756,12 +5773,11 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, if (intel_crtc->config.has_pch_encoder) { struct intel_pch_pll *pll; - fp = clock.n << 16 | clock.m1 << 8 | clock.m2; + fp = i9xx_dpll_compute_fp(&intel_crtc->config.dpll); if (has_reduced_clock) - fp2 = reduced_clock.n << 16 | reduced_clock.m1 << 8 | - reduced_clock.m2; + fp2 = i9xx_dpll_compute_fp(&reduced_clock); - dpll = ironlake_compute_dpll(intel_crtc, &clock, + dpll = ironlake_compute_dpll(intel_crtc, &fp, &reduced_clock, has_reduced_clock ? &fp2 : NULL); -- cgit v1.2.3 From c6bb353815c30c3f8a33b436314926706f4b6360 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 19 Apr 2013 11:14:33 +0200 Subject: drm/i915: move dp clock computations to encoder->compute_config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the exception of hsw, which has dedicated DP clocks which run at the fixed frequency already, and vlv, which doesn't have optmized pre-defined dp clock parameters (yet). v2: Ville asked me to elaborate a bit more on the longer-term goals wrt dpll settings computation: So ultimately my idea is that in the compute config stage first the crtc code puts the default platform pll limits into the pipe_config. Then encoders can either overwrite that limit structure with their own special stuff (mostly for lvds madness). Or they can pick some or all of the parameters (e.g. just the p2 switchover on hdmi, or all the clock parameters for dp/sdvo tv). Once that's done then the generic crtc code can fill out any missing bits (using the find_best_pll code) and then try to assign which pll to use (if it's a platform with shared plls). In the end the modeset could should simply write the computed stuff into registers and never be able to fail. Of course there's still a lot of data to be moved into pipe_config to make this all happen, hence some of the temporary ugliness. Reviewed-by: Jesse Barnes (v1) Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 97 +----------------------------------- drivers/gpu/drm/i915/intel_dp.c | 45 +++++++++++++++++ 2 files changed, 46 insertions(+), 96 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index f079fcd95dc2..26acc421f0c6 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -101,15 +101,6 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, int target, int refclk, intel_clock_t *match_clock, intel_clock_t *best_clock); -static bool -intel_find_pll_g4x_dp(const intel_limit_t *, struct drm_crtc *crtc, - int target, int refclk, intel_clock_t *match_clock, - intel_clock_t *best_clock); -static bool -intel_find_pll_ironlake_dp(const intel_limit_t *, struct drm_crtc *crtc, - int target, int refclk, intel_clock_t *match_clock, - intel_clock_t *best_clock); - static bool intel_vlv_find_best_pll(const intel_limit_t *limit, struct drm_crtc *crtc, int target, int refclk, intel_clock_t *match_clock, @@ -242,20 +233,6 @@ static const intel_limit_t intel_limits_g4x_dual_channel_lvds = { .find_pll = intel_g4x_find_best_PLL, }; -static const intel_limit_t intel_limits_g4x_display_port = { - .dot = { .min = 161670, .max = 227000 }, - .vco = { .min = 1750000, .max = 3500000}, - .n = { .min = 1, .max = 2 }, - .m = { .min = 97, .max = 108 }, - .m1 = { .min = 0x10, .max = 0x12 }, - .m2 = { .min = 0x05, .max = 0x06 }, - .p = { .min = 10, .max = 20 }, - .p1 = { .min = 1, .max = 2}, - .p2 = { .dot_limit = 0, - .p2_slow = 10, .p2_fast = 10 }, - .find_pll = intel_find_pll_g4x_dp, -}; - static const intel_limit_t intel_limits_pineview_sdvo = { .dot = { .min = 20000, .max = 400000}, .vco = { .min = 1700000, .max = 3500000 }, @@ -362,20 +339,6 @@ static const intel_limit_t intel_limits_ironlake_dual_lvds_100m = { .find_pll = intel_g4x_find_best_PLL, }; -static const intel_limit_t intel_limits_ironlake_display_port = { - .dot = { .min = 25000, .max = 350000 }, - .vco = { .min = 1760000, .max = 3510000}, - .n = { .min = 1, .max = 2 }, - .m = { .min = 81, .max = 90 }, - .m1 = { .min = 12, .max = 22 }, - .m2 = { .min = 5, .max = 9 }, - .p = { .min = 10, .max = 20 }, - .p1 = { .min = 1, .max = 2}, - .p2 = { .dot_limit = 0, - .p2_slow = 10, .p2_fast = 10 }, - .find_pll = intel_find_pll_ironlake_dp, -}; - static const intel_limit_t intel_limits_vlv_dac = { .dot = { .min = 25000, .max = 270000 }, .vco = { .min = 4000000, .max = 6000000 }, @@ -473,10 +436,7 @@ static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc, else limit = &intel_limits_ironlake_single_lvds; } - } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT) || - intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP)) - limit = &intel_limits_ironlake_display_port; - else + } else limit = &intel_limits_ironlake_dac; return limit; @@ -497,8 +457,6 @@ static const intel_limit_t *intel_g4x_limit(struct drm_crtc *crtc) limit = &intel_limits_g4x_hdmi; } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_SDVO)) { limit = &intel_limits_g4x_sdvo; - } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) { - limit = &intel_limits_g4x_display_port; } else /* The option is for other outputs */ limit = &intel_limits_i9xx_sdvo; @@ -745,59 +703,6 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, return found; } -static bool -intel_find_pll_ironlake_dp(const intel_limit_t *limit, struct drm_crtc *crtc, - int target, int refclk, intel_clock_t *match_clock, - intel_clock_t *best_clock) -{ - struct drm_device *dev = crtc->dev; - intel_clock_t clock; - - if (target < 200000) { - clock.n = 1; - clock.p1 = 2; - clock.p2 = 10; - clock.m1 = 12; - clock.m2 = 9; - } else { - clock.n = 2; - clock.p1 = 1; - clock.p2 = 10; - clock.m1 = 14; - clock.m2 = 8; - } - intel_clock(dev, refclk, &clock); - memcpy(best_clock, &clock, sizeof(intel_clock_t)); - return true; -} - -/* DisplayPort has only two frequencies, 162MHz and 270MHz */ -static bool -intel_find_pll_g4x_dp(const intel_limit_t *limit, struct drm_crtc *crtc, - int target, int refclk, intel_clock_t *match_clock, - 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; -} static bool intel_vlv_find_best_pll(const intel_limit_t *limit, struct drm_crtc *crtc, int target, int refclk, intel_clock_t *match_clock, diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 1ccf853e2df7..f63973ad33cb 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -660,6 +660,49 @@ intel_dp_i2c_init(struct intel_dp *intel_dp, return ret; } +static void +intel_dp_set_clock(struct intel_encoder *encoder, + struct intel_crtc_config *pipe_config, int link_bw) +{ + struct drm_device *dev = encoder->base.dev; + + if (IS_G4X(dev)) { + if (link_bw == DP_LINK_BW_1_62) { + pipe_config->dpll.p1 = 2; + pipe_config->dpll.p2 = 10; + pipe_config->dpll.n = 2; + pipe_config->dpll.m1 = 23; + pipe_config->dpll.m2 = 8; + } else { + pipe_config->dpll.p1 = 1; + pipe_config->dpll.p2 = 10; + pipe_config->dpll.n = 1; + pipe_config->dpll.m1 = 14; + pipe_config->dpll.m2 = 2; + } + pipe_config->clock_set = true; + } else if (IS_HASWELL(dev)) { + /* Haswell has special-purpose DP DDI clocks. */ + } else if (HAS_PCH_SPLIT(dev)) { + if (link_bw == DP_LINK_BW_1_62) { + pipe_config->dpll.n = 1; + pipe_config->dpll.p1 = 2; + pipe_config->dpll.p2 = 10; + pipe_config->dpll.m1 = 12; + pipe_config->dpll.m2 = 9; + } else { + pipe_config->dpll.n = 2; + pipe_config->dpll.p1 = 1; + pipe_config->dpll.p2 = 10; + pipe_config->dpll.m1 = 14; + pipe_config->dpll.m2 = 8; + } + pipe_config->clock_set = true; + } else if (IS_VALLEYVIEW(dev)) { + /* FIXME: Need to figure out optimized DP clocks for vlv. */ + } +} + bool intel_dp_compute_config(struct intel_encoder *encoder, struct intel_crtc_config *pipe_config) @@ -765,6 +808,8 @@ found: } pipe_config->pipe_bpp = bpp; + intel_dp_set_clock(encoder, pipe_config, intel_dp->link_bw); + return true; } -- cgit v1.2.3 From d8b322474941fa565ba5c58292ccc54be92cca41 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 25 Apr 2013 17:54:44 +0200 Subject: drm/i915: use pipe_config for lvds dithering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Up to now we've relied on the bios to get this right for us. Let's try out whether our code has improved a bit, since we should dither always when the output bpp doesn't match the plane bpp. - gen5+ should be fine, since we only use the bios hint as an upgrade. - gen4 changes, since here dithering is still controlled in the lvds register. - gen2/3 has implicit dithering depeding upon whether you use 2 or 3 lvds pairs (which makes sense, since it only supports 8bpc pipe outpu configurations). - hsw doesn't support lvds. v2: Remove redudant dither setting. v3: Completly drop reliance on dev_priv->lvds_dither. v4: Enable dithering on gen2/3 only when we have a 18bpp panel, since up-dithering to a 24bpp panel is not supported by the hw. Spotted by Ville. v5: Also only enable lvds port dithering on gen4 for 18bpp modes. In practice this only excludes dithering a 10bpc plane down for a 24bpp lvds panel. Not something we truly care about. Again noticed by Ville. v6: Actually git add. Cc: Ville Syrjälä Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 25 +++++++------------------ drivers/gpu/drm/i915/intel_drv.h | 5 +++++ drivers/gpu/drm/i915/intel_lvds.c | 15 ++++++++++----- 3 files changed, 22 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 26acc421f0c6..38465f0e9710 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5146,8 +5146,7 @@ static int ironlake_get_refclk(struct drm_crtc *crtc) } static void ironlake_set_pipeconf(struct drm_crtc *crtc, - struct drm_display_mode *adjusted_mode, - bool dither) + struct drm_display_mode *adjusted_mode) { struct drm_i915_private *dev_priv = crtc->dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); @@ -5176,7 +5175,7 @@ static void ironlake_set_pipeconf(struct drm_crtc *crtc, } val &= ~(PIPECONF_DITHER_EN | PIPECONF_DITHER_TYPE_MASK); - if (dither) + if (intel_crtc->config.dither) val |= (PIPECONF_DITHER_EN | PIPECONF_DITHER_TYPE_SP); val &= ~PIPECONF_INTERLACE_MASK; @@ -5259,8 +5258,7 @@ static void intel_set_pipe_csc(struct drm_crtc *crtc) } static void haswell_set_pipeconf(struct drm_crtc *crtc, - struct drm_display_mode *adjusted_mode, - bool dither) + struct drm_display_mode *adjusted_mode) { struct drm_i915_private *dev_priv = crtc->dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); @@ -5270,7 +5268,7 @@ static void haswell_set_pipeconf(struct drm_crtc *crtc, val = I915_READ(PIPECONF(cpu_transcoder)); val &= ~(PIPECONF_DITHER_EN | PIPECONF_DITHER_TYPE_MASK); - if (dither) + if (intel_crtc->config.dither) val |= (PIPECONF_DITHER_EN | PIPECONF_DITHER_TYPE_SP); val &= ~PIPECONF_INTERLACE_MASK_HSW; @@ -5631,7 +5629,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, bool is_lvds = false; struct intel_encoder *encoder; int ret; - bool dither, fdi_config_ok; + bool fdi_config_ok; for_each_encoder_on_crtc(dev, crtc, encoder) { switch (encoder->type) { @@ -5666,11 +5664,6 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, /* Ensure that the cursor is valid for the new mode before changing... */ intel_crtc_update_cursor(crtc, true); - /* determine panel color depth */ - dither = intel_crtc->config.dither; - if (is_lvds && dev_priv->lvds_dither) - dither = true; - DRM_DEBUG_KMS("Mode for pipe %c:\n", pipe_name(pipe)); drm_mode_debug_printmodeline(mode); @@ -5737,7 +5730,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, fdi_config_ok = ironlake_check_fdi_lanes(intel_crtc); - ironlake_set_pipeconf(crtc, adjusted_mode, dither); + ironlake_set_pipeconf(crtc, adjusted_mode); /* Set up the display plane register */ I915_WRITE(DSPCNTR(plane), DISPPLANE_GAMMA_ENABLE); @@ -5814,7 +5807,6 @@ static int haswell_crtc_mode_set(struct drm_crtc *crtc, bool is_cpu_edp = false; struct intel_encoder *encoder; int ret; - bool dither; for_each_encoder_on_crtc(dev, crtc, encoder) { switch (encoder->type) { @@ -5850,9 +5842,6 @@ static int haswell_crtc_mode_set(struct drm_crtc *crtc, /* Ensure that the cursor is valid for the new mode before changing... */ intel_crtc_update_cursor(crtc, true); - /* determine panel color depth */ - dither = intel_crtc->config.dither; - DRM_DEBUG_KMS("Mode for pipe %c:\n", pipe_name(pipe)); drm_mode_debug_printmodeline(mode); @@ -5866,7 +5855,7 @@ static int haswell_crtc_mode_set(struct drm_crtc *crtc, if (intel_crtc->config.has_pch_encoder) ironlake_fdi_set_m_n(crtc); - haswell_set_pipeconf(crtc, adjusted_mode, dither); + haswell_set_pipeconf(crtc, adjusted_mode); intel_set_pipe_csc(crtc); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 3595c8204649..a5fe976364e1 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -213,6 +213,11 @@ struct intel_crtc_config { /* DP has a bunch of special case unfortunately, so mark the pipe * accordingly. */ bool has_dp_encoder; + + /* + * Enable dithering, used when the selected pipe bpp doesn't match the + * plane bpp. + */ bool dither; /* Controls for the clock computation, to override various stages. */ diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 563f5052fcfc..84085454b104 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -136,7 +136,10 @@ static void intel_pre_pll_enable_lvds(struct intel_encoder *encoder) * special lvds dither control bit on pch-split platforms, dithering is * only controlled through the PIPECONF reg. */ if (INTEL_INFO(dev)->gen == 4) { - if (dev_priv->lvds_dither) + /* Bspec wording suggests that LVDS port dithering only exists + * for 18bpp panels. */ + if (intel_crtc->config.dither && + intel_crtc->config.pipe_bpp == 18) temp |= LVDS_ENABLE_DITHER; else temp &= ~LVDS_ENABLE_DITHER; @@ -335,7 +338,13 @@ static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder, DRM_DEBUG_KMS("forcing display bpp (was %d) to LVDS (%d)\n", pipe_config->pipe_bpp, lvds_bpp); pipe_config->pipe_bpp = lvds_bpp; + + /* Make sure pre-965 set dither correctly for 18bpp panels. */ + if (INTEL_INFO(dev)->gen < 4 && lvds_bpp == 18) + pfit_control |= PANEL_8TO6_DITHER_ENABLE; + } + /* * We have timings from the BIOS for the panel, put them in * to the adjusted mode. The CRTC will be set up for this mode, @@ -470,10 +479,6 @@ out: pfit_pgm_ratios = 0; } - /* Make sure pre-965 set dither correctly */ - if (INTEL_INFO(dev)->gen < 4 && dev_priv->lvds_dither) - pfit_control |= PANEL_8TO6_DITHER_ENABLE; - if (pfit_control != lvds_encoder->pfit_control || pfit_pgm_ratios != lvds_encoder->pfit_pgm_ratios) { lvds_encoder->pfit_control = pfit_control; -- cgit v1.2.3 From 4f4134ace04fd5b0e8734b65f4046e7aa2e39393 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 19 Apr 2013 11:14:35 +0200 Subject: drm/i915: don't force matching p1 for g4x/ilk+ reduced pll settings g4x dplls and ilk+ pch plls have a separate field for the reduced p1 setting, so this restriction does not apply. Only older platforms have the restriction that the p1 divisors must match. This unnecessary restriction has been introduced in commit cec2f356d59d9e070413e5966a3c5a1af136d948 Author: Sean Paul Date: Tue Jan 10 15:09:36 2012 -0800 drm/i915: Only look for matching clocks for LVDS downcloc Note that with lvds the p2 divisors _always_ match for LVDS, and we don't support auto-downclocking anywhere else. On eDP downclocking works with separate data m/n settings, using the same link clock. Cc: Sean Paul Reviewed-by: Sean Paul Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 38465f0e9710..fa9af52ba5a4 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -685,9 +685,6 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, if (!intel_PLL_is_valid(dev, limit, &clock)) continue; - if (match_clock && - clock.p != match_clock->p) - continue; this_err = abs(clock.dot - target); if (this_err < err_most) { -- cgit v1.2.3 From 9566e9af524856a27f3c6b00821e74ff9ae04732 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 19 Apr 2013 11:14:36 +0200 Subject: drm/i915: remove redundant has_pch_encoder check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If we compute the pch pll state, we _have_ a pch encoder. Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index fa9af52ba5a4..a9013fda1c3a 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5569,8 +5569,7 @@ static uint32_t ironlake_compute_dpll(struct intel_crtc *intel_crtc, } dpll |= DPLL_DVO_HIGH_SPEED; } - if (intel_crtc->config.has_dp_encoder && - intel_crtc->config.has_pch_encoder) + if (intel_crtc->config.has_dp_encoder) dpll |= DPLL_DVO_HIGH_SPEED; /* compute bitmask from p1 value */ -- cgit v1.2.3 From 198a037f02666eeaab5ba07974fa37467b1f6bd8 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 19 Apr 2013 11:14:37 +0200 Subject: drm/i915: simplify config->pixel_multiplier handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We only ever check whether it's strictly bigger than one, so all the is_sdvo/is_hdmi checks are redundant. Flatten the code a bit. Also, s/temp/dpll_md/ Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 58 +++++++++++++++++------------------- 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index a9013fda1c3a..2e83dbe5ecc0 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4235,7 +4235,7 @@ static void vlv_update_pll(struct intel_crtc *crtc) u32 dpll, mdiv; u32 bestn, bestm1, bestm2, bestp1, bestp2; bool is_hdmi; - u32 coreclk, reg_val, temp; + u32 coreclk, reg_val, dpll_md; mutex_lock(&dev_priv->dpio_lock); @@ -4333,16 +4333,13 @@ static void vlv_update_pll(struct intel_crtc *crtc) if (wait_for(((I915_READ(DPLL(pipe)) & DPLL_LOCK_VLV) == DPLL_LOCK_VLV), 1)) DRM_ERROR("DPLL %d failed to lock\n", pipe); - if (is_hdmi) { - temp = 0; - if (crtc->config.pixel_multiplier > 1) { - temp = (crtc->config.pixel_multiplier - 1) - << DPLL_MD_UDI_MULTIPLIER_SHIFT; - } - - I915_WRITE(DPLL_MD(pipe), temp); - POSTING_READ(DPLL_MD(pipe)); + dpll_md = 0; + if (crtc->config.pixel_multiplier > 1) { + dpll_md = (crtc->config.pixel_multiplier - 1) + << DPLL_MD_UDI_MULTIPLIER_SHIFT; } + I915_WRITE(DPLL_MD(pipe), dpll_md); + POSTING_READ(DPLL_MD(pipe)); if (crtc->config.has_dp_encoder) intel_dp_set_m_n(crtc); @@ -4374,14 +4371,15 @@ static void i9xx_update_pll(struct intel_crtc *crtc, else dpll |= DPLLB_MODE_DAC_SERIAL; - if (is_sdvo) { - if ((crtc->config.pixel_multiplier > 1) && - (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev))) { - dpll |= (crtc->config.pixel_multiplier - 1) - << SDVO_MULTIPLIER_SHIFT_HIRES; - } - dpll |= DPLL_DVO_HIGH_SPEED; + if ((crtc->config.pixel_multiplier > 1) && + (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev))) { + dpll |= (crtc->config.pixel_multiplier - 1) + << SDVO_MULTIPLIER_SHIFT_HIRES; } + + if (is_sdvo) + dpll |= DPLL_DVO_HIGH_SPEED; + if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_DISPLAYPORT)) dpll |= DPLL_DVO_HIGH_SPEED; @@ -4441,15 +4439,12 @@ static void i9xx_update_pll(struct intel_crtc *crtc, udelay(150); if (INTEL_INFO(dev)->gen >= 4) { - u32 temp = 0; - if (is_sdvo) { - temp = 0; - if (crtc->config.pixel_multiplier > 1) { - temp = (crtc->config.pixel_multiplier - 1) - << DPLL_MD_UDI_MULTIPLIER_SHIFT; - } + u32 dpll_md = 0; + if (crtc->config.pixel_multiplier > 1) { + dpll_md = (crtc->config.pixel_multiplier - 1) + << DPLL_MD_UDI_MULTIPLIER_SHIFT; } - I915_WRITE(DPLL_MD(pipe), temp); + I915_WRITE(DPLL_MD(pipe), dpll_md); } else { /* The pixel multiplier can only be updated once the * DPLL is enabled and the clocks are stable. @@ -5562,13 +5557,14 @@ static uint32_t ironlake_compute_dpll(struct intel_crtc *intel_crtc, dpll |= DPLLB_MODE_LVDS; else dpll |= DPLLB_MODE_DAC_SERIAL; - if (is_sdvo) { - if (intel_crtc->config.pixel_multiplier > 1) { - dpll |= (intel_crtc->config.pixel_multiplier - 1) - << PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT; - } - dpll |= DPLL_DVO_HIGH_SPEED; + + if (intel_crtc->config.pixel_multiplier > 1) { + dpll |= (intel_crtc->config.pixel_multiplier - 1) + << PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT; } + + if (is_sdvo) + dpll |= DPLL_DVO_HIGH_SPEED; if (intel_crtc->config.has_dp_encoder) dpll |= DPLL_DVO_HIGH_SPEED; -- cgit v1.2.3 From 2dd24552cab40ea829ba3fda890eeafd2c4816d8 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 25 Apr 2013 12:55:01 -0700 Subject: drm/i915: factor out GMCH panel fitting code and use for eDP v3 This gets the panel fitter working on eDP on VLV, and should also apply to eDP panels on G4x chipsets (if we ever detect and mark an all-in-one panel as eDP anyway). A few cleanups are still possible on top of this, for example the LVDS border control could be placed in the LVDS encoder structure and updated based on the result of the panel fitter calculation. Multi-pipe fitting isn't handled correctly either if we ever get a config that wants to try the panel fitter on more than one output at a time. v2: use pipe_config for storing pfit values (Daniel) add i9xx_pfit_enable function for use by 9xx and VLV (Daniel) v3: fixup conflicts and lvds_dither check Reviewed-by: Mika Kuoppala Signed-off-by: Jesse Barnes [danvet: fix up botched conflict resolution from Jesse: - border = LVDS_BORDER_ENABLE was lost for CENTER scaling - comment about gen2/3 panel fitter scaling was lost - dev_priv->lvds_dither reintroduced.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 33 ++++++ drivers/gpu/drm/i915/intel_dp.c | 11 +- drivers/gpu/drm/i915/intel_drv.h | 6 + drivers/gpu/drm/i915/intel_lvds.c | 209 +---------------------------------- drivers/gpu/drm/i915/intel_panel.c | 191 ++++++++++++++++++++++++++++++++ 5 files changed, 241 insertions(+), 209 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 2e83dbe5ecc0..fc6f76837437 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3601,6 +3601,33 @@ g4x_fixup_plane(struct drm_i915_private *dev_priv, enum pipe pipe) } } +static void i9xx_pfit_enable(struct intel_crtc *crtc) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc_config *pipe_config = &crtc->config; + + if (!(intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_EDP) || + intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_LVDS))) + return; + + WARN_ON(I915_READ(PFIT_CONTROL) & PFIT_ENABLE); + assert_pipe_disabled(dev_priv, crtc->pipe); + + /* + * Enable automatic panel scaling so that non-native modes + * fill the screen. The panel fitter should only be + * adjusted whilst the pipe is disabled, according to + * register description and PRM. + */ + DRM_DEBUG_KMS("applying panel-fitter: %x, %x\n", + pipe_config->pfit_control, + pipe_config->pfit_pgm_ratios); + + I915_WRITE(PFIT_PGM_RATIOS, pipe_config->pfit_pgm_ratios); + I915_WRITE(PFIT_CONTROL, pipe_config->pfit_control); +} + static void valleyview_crtc_enable(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; @@ -3634,6 +3661,9 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc) for_each_encoder_on_crtc(dev, crtc, encoder) encoder->enable(encoder); + /* Enable panel fitting for eDP */ + i9xx_pfit_enable(intel_crtc); + intel_enable_pipe(dev_priv, pipe, false); intel_enable_plane(dev_priv, plane, pipe); @@ -3670,6 +3700,9 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc) if (encoder->pre_enable) encoder->pre_enable(encoder); + /* Enable panel fitting for LVDS */ + i9xx_pfit_enable(intel_crtc); + intel_enable_pipe(dev_priv, pipe, false); intel_enable_plane(dev_priv, plane, pipe); if (IS_G4X(dev)) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index f63973ad33cb..9c834bc15ab7 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -712,6 +712,7 @@ intel_dp_compute_config(struct intel_encoder *encoder, struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode; struct drm_display_mode *mode = &pipe_config->requested_mode; struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); + struct intel_crtc *intel_crtc = encoder->new_crtc; struct intel_connector *intel_connector = intel_dp->attached_connector; int lane_count, clock; int max_lane_count = drm_dp_max_lane_count(intel_dp->dpcd); @@ -728,9 +729,13 @@ intel_dp_compute_config(struct intel_encoder *encoder, if (is_edp(intel_dp) && intel_connector->panel.fixed_mode) { intel_fixed_panel_mode(intel_connector->panel.fixed_mode, adjusted_mode); - intel_pch_panel_fitting(dev, - intel_connector->panel.fitting_mode, - mode, adjusted_mode); + if (!HAS_PCH_SPLIT(dev)) + intel_gmch_panel_fitting(intel_crtc, pipe_config, + intel_connector->panel.fitting_mode); + else + intel_pch_panel_fitting(dev, + intel_connector->panel.fitting_mode, + mode, adjusted_mode); } /* We need to take the panel's fixed mode into account. */ target_clock = adjusted_mode->clock; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index a5fe976364e1..9f3f71bc2f22 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -237,6 +237,9 @@ struct intel_crtc_config { int pixel_target_clock; /* Used by SDVO (and if we ever fix it, HDMI). */ unsigned pixel_multiplier; + + /* Panel fitter controls for gen2-gen4 + VLV */ + u32 pfit_control, pfit_pgm_ratios; }; struct intel_crtc { @@ -558,6 +561,9 @@ extern void intel_pch_panel_fitting(struct drm_device *dev, int fitting_mode, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode); +extern void intel_gmch_panel_fitting(struct intel_crtc *crtc, + struct intel_crtc_config *pipe_config, + int fitting_mode); extern void intel_panel_set_backlight(struct drm_device *dev, u32 level, u32 max); extern int intel_panel_setup_backlight(struct drm_connector *connector); diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 84085454b104..7d418818c7a8 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -49,8 +49,6 @@ struct intel_lvds_connector { struct intel_lvds_encoder { struct intel_encoder base; - u32 pfit_control; - u32 pfit_pgm_ratios; bool is_dual_link; u32 reg; @@ -153,32 +151,6 @@ static void intel_pre_pll_enable_lvds(struct intel_encoder *encoder) I915_WRITE(lvds_encoder->reg, temp); } -static void intel_pre_enable_lvds(struct intel_encoder *encoder) -{ - struct drm_device *dev = encoder->base.dev; - struct intel_lvds_encoder *enc = to_lvds_encoder(&encoder->base); - struct drm_i915_private *dev_priv = dev->dev_private; - - if (HAS_PCH_SPLIT(dev) || !enc->pfit_control) - return; - - WARN_ON(I915_READ(PFIT_CONTROL) & PFIT_ENABLE); - assert_pipe_disabled(dev_priv, to_intel_crtc(encoder->base.crtc)->pipe); - - /* - * Enable automatic panel scaling so that non-native modes - * fill the screen. The panel fitter should only be - * adjusted whilst the pipe is disabled, according to - * register description and PRM. - */ - DRM_DEBUG_KMS("applying panel-fitter: %x, %x\n", - enc->pfit_control, - enc->pfit_pgm_ratios); - - I915_WRITE(PFIT_PGM_RATIOS, enc->pfit_pgm_ratios); - I915_WRITE(PFIT_CONTROL, enc->pfit_control); -} - /** * Sets the power state for the panel. */ @@ -247,62 +219,6 @@ static int intel_lvds_mode_valid(struct drm_connector *connector, return MODE_OK; } -static void -centre_horizontally(struct drm_display_mode *mode, - int width) -{ - u32 border, sync_pos, blank_width, sync_width; - - /* keep the hsync and hblank widths constant */ - sync_width = mode->crtc_hsync_end - mode->crtc_hsync_start; - blank_width = mode->crtc_hblank_end - mode->crtc_hblank_start; - sync_pos = (blank_width - sync_width + 1) / 2; - - border = (mode->hdisplay - width + 1) / 2; - border += border & 1; /* make the border even */ - - mode->crtc_hdisplay = width; - mode->crtc_hblank_start = width + border; - mode->crtc_hblank_end = mode->crtc_hblank_start + blank_width; - - mode->crtc_hsync_start = mode->crtc_hblank_start + sync_pos; - mode->crtc_hsync_end = mode->crtc_hsync_start + sync_width; -} - -static void -centre_vertically(struct drm_display_mode *mode, - int height) -{ - u32 border, sync_pos, blank_width, sync_width; - - /* keep the vsync and vblank widths constant */ - sync_width = mode->crtc_vsync_end - mode->crtc_vsync_start; - blank_width = mode->crtc_vblank_end - mode->crtc_vblank_start; - sync_pos = (blank_width - sync_width + 1) / 2; - - border = (mode->vdisplay - height + 1) / 2; - - mode->crtc_vdisplay = height; - mode->crtc_vblank_start = height + border; - mode->crtc_vblank_end = mode->crtc_vblank_start + blank_width; - - mode->crtc_vsync_start = mode->crtc_vblank_start + sync_pos; - mode->crtc_vsync_end = mode->crtc_vsync_start + sync_width; -} - -static inline u32 panel_fitter_scaling(u32 source, u32 target) -{ - /* - * Floating point operation is not supported. So the FACTOR - * is defined, which can avoid the floating point computation - * when calculating the panel ratio. - */ -#define ACCURACY 12 -#define FACTOR (1 << ACCURACY) - u32 ratio = source * FACTOR / target; - return (FACTOR * ratio + FACTOR/2) / FACTOR; -} - static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder, struct intel_crtc_config *pipe_config) { @@ -315,7 +231,6 @@ static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder, struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode; struct drm_display_mode *mode = &pipe_config->requested_mode; struct intel_crtc *intel_crtc = lvds_encoder->base.new_crtc; - u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0; unsigned int lvds_bpp; int pipe; @@ -338,11 +253,6 @@ static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder, DRM_DEBUG_KMS("forcing display bpp (was %d) to LVDS (%d)\n", pipe_config->pipe_bpp, lvds_bpp); pipe_config->pipe_bpp = lvds_bpp; - - /* Make sure pre-965 set dither correctly for 18bpp panels. */ - if (INTEL_INFO(dev)->gen < 4 && lvds_bpp == 18) - pfit_control |= PANEL_8TO6_DITHER_ENABLE; - } /* @@ -361,18 +271,11 @@ static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder, intel_connector->panel.fitting_mode, mode, adjusted_mode); return true; + } else { + intel_gmch_panel_fitting(intel_crtc, pipe_config, + intel_connector->panel.fitting_mode); } - /* Native modes don't need fitting */ - if (adjusted_mode->hdisplay == mode->hdisplay && - adjusted_mode->vdisplay == mode->vdisplay) - goto out; - - /* 965+ wants fuzzy fitting */ - if (INTEL_INFO(dev)->gen >= 4) - pfit_control |= ((intel_crtc->pipe << PFIT_PIPE_SHIFT) | - PFIT_FILTER_FUZZY); - /* * Enable automatic panel scaling for non-native modes so that they fill * the screen. Should be enabled before the pipe is enabled, according @@ -385,107 +288,6 @@ static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder, drm_mode_set_crtcinfo(adjusted_mode, 0); pipe_config->timings_set = true; - switch (intel_connector->panel.fitting_mode) { - case DRM_MODE_SCALE_CENTER: - /* - * For centered modes, we have to calculate border widths & - * heights and modify the values programmed into the CRTC. - */ - centre_horizontally(adjusted_mode, mode->hdisplay); - centre_vertically(adjusted_mode, mode->vdisplay); - border = LVDS_BORDER_ENABLE; - break; - - case DRM_MODE_SCALE_ASPECT: - /* Scale but preserve the aspect ratio */ - if (INTEL_INFO(dev)->gen >= 4) { - u32 scaled_width = adjusted_mode->hdisplay * mode->vdisplay; - u32 scaled_height = mode->hdisplay * adjusted_mode->vdisplay; - - /* 965+ is easy, it does everything in hw */ - if (scaled_width > scaled_height) - pfit_control |= PFIT_ENABLE | PFIT_SCALING_PILLAR; - else if (scaled_width < scaled_height) - pfit_control |= PFIT_ENABLE | PFIT_SCALING_LETTER; - else if (adjusted_mode->hdisplay != mode->hdisplay) - pfit_control |= PFIT_ENABLE | PFIT_SCALING_AUTO; - } else { - u32 scaled_width = adjusted_mode->hdisplay * mode->vdisplay; - u32 scaled_height = mode->hdisplay * adjusted_mode->vdisplay; - /* - * For earlier chips we have to calculate the scaling - * ratio by hand and program it into the - * PFIT_PGM_RATIO register - */ - if (scaled_width > scaled_height) { /* pillar */ - centre_horizontally(adjusted_mode, scaled_height / mode->vdisplay); - - border = LVDS_BORDER_ENABLE; - if (mode->vdisplay != adjusted_mode->vdisplay) { - u32 bits = panel_fitter_scaling(mode->vdisplay, adjusted_mode->vdisplay); - pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT | - bits << PFIT_VERT_SCALE_SHIFT); - pfit_control |= (PFIT_ENABLE | - VERT_INTERP_BILINEAR | - HORIZ_INTERP_BILINEAR); - } - } else if (scaled_width < scaled_height) { /* letter */ - centre_vertically(adjusted_mode, scaled_width / mode->hdisplay); - - border = LVDS_BORDER_ENABLE; - if (mode->hdisplay != adjusted_mode->hdisplay) { - u32 bits = panel_fitter_scaling(mode->hdisplay, adjusted_mode->hdisplay); - pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT | - bits << PFIT_VERT_SCALE_SHIFT); - pfit_control |= (PFIT_ENABLE | - VERT_INTERP_BILINEAR | - HORIZ_INTERP_BILINEAR); - } - } else - /* Aspects match, Let hw scale both directions */ - pfit_control |= (PFIT_ENABLE | - VERT_AUTO_SCALE | HORIZ_AUTO_SCALE | - VERT_INTERP_BILINEAR | - HORIZ_INTERP_BILINEAR); - } - break; - - case DRM_MODE_SCALE_FULLSCREEN: - /* - * Full scaling, even if it changes the aspect ratio. - * Fortunately this is all done for us in hw. - */ - if (mode->vdisplay != adjusted_mode->vdisplay || - mode->hdisplay != adjusted_mode->hdisplay) { - pfit_control |= PFIT_ENABLE; - if (INTEL_INFO(dev)->gen >= 4) - pfit_control |= PFIT_SCALING_AUTO; - else - pfit_control |= (VERT_AUTO_SCALE | - VERT_INTERP_BILINEAR | - HORIZ_AUTO_SCALE | - HORIZ_INTERP_BILINEAR); - } - break; - - default: - break; - } - -out: - /* If not enabling scaling, be consistent and always use 0. */ - if ((pfit_control & PFIT_ENABLE) == 0) { - pfit_control = 0; - pfit_pgm_ratios = 0; - } - - if (pfit_control != lvds_encoder->pfit_control || - pfit_pgm_ratios != lvds_encoder->pfit_pgm_ratios) { - lvds_encoder->pfit_control = pfit_control; - lvds_encoder->pfit_pgm_ratios = pfit_pgm_ratios; - } - dev_priv->lvds_border_bits = border; - /* * XXX: It would be nice to support lower refresh rates on the * panels to reduce power consumption, and perhaps match the @@ -1115,10 +917,6 @@ bool intel_lvds_init(struct drm_device *dev) lvds_encoder->attached_connector = lvds_connector; - if (!HAS_PCH_SPLIT(dev)) { - lvds_encoder->pfit_control = I915_READ(PFIT_CONTROL); - } - intel_encoder = &lvds_encoder->base; encoder = &intel_encoder->base; intel_connector = &lvds_connector->base; @@ -1130,7 +928,6 @@ bool intel_lvds_init(struct drm_device *dev) DRM_MODE_ENCODER_LVDS); intel_encoder->enable = intel_enable_lvds; - intel_encoder->pre_enable = intel_pre_enable_lvds; intel_encoder->pre_pll_enable = intel_pre_pll_enable_lvds; intel_encoder->compute_config = intel_lvds_compute_config; intel_encoder->disable = intel_disable_lvds; diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index 7f6141d9a06d..0f32f6498ad3 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -117,6 +117,197 @@ done: dev_priv->pch_pf_size = (width << 16) | height; } +static void +centre_horizontally(struct drm_display_mode *mode, + int width) +{ + u32 border, sync_pos, blank_width, sync_width; + + /* keep the hsync and hblank widths constant */ + sync_width = mode->crtc_hsync_end - mode->crtc_hsync_start; + blank_width = mode->crtc_hblank_end - mode->crtc_hblank_start; + sync_pos = (blank_width - sync_width + 1) / 2; + + border = (mode->hdisplay - width + 1) / 2; + border += border & 1; /* make the border even */ + + mode->crtc_hdisplay = width; + mode->crtc_hblank_start = width + border; + mode->crtc_hblank_end = mode->crtc_hblank_start + blank_width; + + mode->crtc_hsync_start = mode->crtc_hblank_start + sync_pos; + mode->crtc_hsync_end = mode->crtc_hsync_start + sync_width; +} + +static void +centre_vertically(struct drm_display_mode *mode, + int height) +{ + u32 border, sync_pos, blank_width, sync_width; + + /* keep the vsync and vblank widths constant */ + sync_width = mode->crtc_vsync_end - mode->crtc_vsync_start; + blank_width = mode->crtc_vblank_end - mode->crtc_vblank_start; + sync_pos = (blank_width - sync_width + 1) / 2; + + border = (mode->vdisplay - height + 1) / 2; + + mode->crtc_vdisplay = height; + mode->crtc_vblank_start = height + border; + mode->crtc_vblank_end = mode->crtc_vblank_start + blank_width; + + mode->crtc_vsync_start = mode->crtc_vblank_start + sync_pos; + mode->crtc_vsync_end = mode->crtc_vsync_start + sync_width; +} + +static inline u32 panel_fitter_scaling(u32 source, u32 target) +{ + /* + * Floating point operation is not supported. So the FACTOR + * is defined, which can avoid the floating point computation + * when calculating the panel ratio. + */ +#define ACCURACY 12 +#define FACTOR (1 << ACCURACY) + u32 ratio = source * FACTOR / target; + return (FACTOR * ratio + FACTOR/2) / FACTOR; +} + +void intel_gmch_panel_fitting(struct intel_crtc *intel_crtc, + struct intel_crtc_config *pipe_config, + int fitting_mode) +{ + struct drm_device *dev = intel_crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0; + struct drm_display_mode *mode, *adjusted_mode; + + mode = &pipe_config->requested_mode; + adjusted_mode = &pipe_config->adjusted_mode; + + /* Native modes don't need fitting */ + if (adjusted_mode->hdisplay == mode->hdisplay && + adjusted_mode->vdisplay == mode->vdisplay) + goto out; + + switch (fitting_mode) { + case DRM_MODE_SCALE_CENTER: + /* + * For centered modes, we have to calculate border widths & + * heights and modify the values programmed into the CRTC. + */ + centre_horizontally(adjusted_mode, mode->hdisplay); + centre_vertically(adjusted_mode, mode->vdisplay); + border = LVDS_BORDER_ENABLE; + break; + case DRM_MODE_SCALE_ASPECT: + /* Scale but preserve the aspect ratio */ + if (INTEL_INFO(dev)->gen >= 4) { + u32 scaled_width = adjusted_mode->hdisplay * + mode->vdisplay; + u32 scaled_height = mode->hdisplay * + adjusted_mode->vdisplay; + + /* 965+ is easy, it does everything in hw */ + if (scaled_width > scaled_height) + pfit_control |= PFIT_ENABLE | + PFIT_SCALING_PILLAR; + else if (scaled_width < scaled_height) + pfit_control |= PFIT_ENABLE | + PFIT_SCALING_LETTER; + else if (adjusted_mode->hdisplay != mode->hdisplay) + pfit_control |= PFIT_ENABLE | PFIT_SCALING_AUTO; + } else { + u32 scaled_width = adjusted_mode->hdisplay * + mode->vdisplay; + u32 scaled_height = mode->hdisplay * + adjusted_mode->vdisplay; + /* + * For earlier chips we have to calculate the scaling + * ratio by hand and program it into the + * PFIT_PGM_RATIO register + */ + if (scaled_width > scaled_height) { /* pillar */ + centre_horizontally(adjusted_mode, + scaled_height / + mode->vdisplay); + + border = LVDS_BORDER_ENABLE; + if (mode->vdisplay != adjusted_mode->vdisplay) { + u32 bits = panel_fitter_scaling(mode->vdisplay, adjusted_mode->vdisplay); + pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT | + bits << PFIT_VERT_SCALE_SHIFT); + pfit_control |= (PFIT_ENABLE | + VERT_INTERP_BILINEAR | + HORIZ_INTERP_BILINEAR); + } + } else if (scaled_width < scaled_height) { /* letter */ + centre_vertically(adjusted_mode, + scaled_width / + mode->hdisplay); + + border = LVDS_BORDER_ENABLE; + if (mode->hdisplay != adjusted_mode->hdisplay) { + u32 bits = panel_fitter_scaling(mode->hdisplay, adjusted_mode->hdisplay); + pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT | + bits << PFIT_VERT_SCALE_SHIFT); + pfit_control |= (PFIT_ENABLE | + VERT_INTERP_BILINEAR | + HORIZ_INTERP_BILINEAR); + } + } else { + /* Aspects match, Let hw scale both directions */ + pfit_control |= (PFIT_ENABLE | + VERT_AUTO_SCALE | HORIZ_AUTO_SCALE | + VERT_INTERP_BILINEAR | + HORIZ_INTERP_BILINEAR); + } + } + break; + default: + case DRM_MODE_SCALE_FULLSCREEN: + /* + * Full scaling, even if it changes the aspect ratio. + * Fortunately this is all done for us in hw. + */ + if (mode->vdisplay != adjusted_mode->vdisplay || + mode->hdisplay != adjusted_mode->hdisplay) { + pfit_control |= PFIT_ENABLE; + if (INTEL_INFO(dev)->gen >= 4) + pfit_control |= PFIT_SCALING_AUTO; + else + pfit_control |= (VERT_AUTO_SCALE | + VERT_INTERP_BILINEAR | + HORIZ_AUTO_SCALE | + HORIZ_INTERP_BILINEAR); + } + break; + } + + /* 965+ wants fuzzy fitting */ + /* FIXME: handle multiple panels by failing gracefully */ + if (INTEL_INFO(dev)->gen >= 4) + pfit_control |= ((intel_crtc->pipe << PFIT_PIPE_SHIFT) | + PFIT_FILTER_FUZZY); + +out: + if ((pfit_control & PFIT_ENABLE) == 0) { + pfit_control = 0; + pfit_pgm_ratios = 0; + } + + /* Make sure pre-965 set dither correctly for 18bpp panels. */ + if (INTEL_INFO(dev)->gen < 4 && pipe_config->pipe_bpp == 18) + pfit_control |= PANEL_8TO6_DITHER_ENABLE; + + if (pfit_control != pipe_config->pfit_control || + pfit_pgm_ratios != pipe_config->pfit_pgm_ratios) { + pipe_config->pfit_control = pfit_control; + pipe_config->pfit_pgm_ratios = pfit_pgm_ratios; + } + dev_priv->lvds_border_bits = border; +} + static int is_backlight_combination_mode(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; -- cgit v1.2.3 From b074cec8c652f2d273907a4b35239b4766c894ac Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 25 Apr 2013 12:55:02 -0700 Subject: drm/i915: move PCH pfit controls into pipe_config And put the pfit stuff into substructs while we're at it. Signed-off-by: Jesse Barnes Reviewed-by: Mika Kuoppala Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 2 -- drivers/gpu/drm/i915/intel_ddi.c | 2 +- drivers/gpu/drm/i915/intel_display.c | 65 +++++++++++++++++------------------- drivers/gpu/drm/i915/intel_dp.c | 6 ++-- drivers/gpu/drm/i915/intel_drv.h | 18 +++++++--- drivers/gpu/drm/i915/intel_lvds.c | 6 ++-- drivers/gpu/drm/i915/intel_panel.c | 25 ++++++++------ 7 files changed, 63 insertions(+), 61 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 8a257a9114c2..14156f2b8d76 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1021,8 +1021,6 @@ typedef struct drm_i915_private { struct sdvo_device_mapping sdvo_mappings[2]; /* indicate whether the LVDS_BORDER should be enabled or not */ unsigned int lvds_border_bits; - /* Panel fitter placement and size for Ironlake+ */ - u32 pch_pf_pos, pch_pf_size; struct drm_crtc *plane_to_crtc_mapping[3]; struct drm_crtc *pipe_to_crtc_mapping[3]; diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index eef450b1af44..0d7cf317b73f 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -995,7 +995,7 @@ void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc) /* Can only use the always-on power well for eDP when * not using the panel fitter, and when not using motion * blur mitigation (which we don't support). */ - if (dev_priv->pch_pf_size) + if (intel_crtc->config.pch_pfit.size) temp |= TRANS_DDI_EDP_INPUT_A_ONOFF; else temp |= TRANS_DDI_EDP_INPUT_A_ON; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index fc6f76837437..dacfc6c90550 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3225,6 +3225,28 @@ void intel_cpt_verify_modeset(struct drm_device *dev, int pipe) } } +static void ironlake_pfit_enable(struct intel_crtc *crtc) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + int pipe = crtc->pipe; + + if (crtc->config.pch_pfit.size && + intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_EDP)) { + /* Force use of hard-coded filter coefficients + * as some pre-programmed values are broken, + * e.g. x201. + */ + if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev)) + I915_WRITE(PF_CTL(pipe), PF_ENABLE | PF_FILTER_MED_3x3 | + PF_PIPE_SEL_IVB(pipe)); + else + I915_WRITE(PF_CTL(pipe), PF_ENABLE | PF_FILTER_MED_3x3); + I915_WRITE(PF_WIN_POS(pipe), crtc->config.pch_pfit.pos); + I915_WRITE(PF_WIN_SZ(pipe), crtc->config.pch_pfit.size); + } +} + static void ironlake_crtc_enable(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; @@ -3269,21 +3291,7 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) encoder->pre_enable(encoder); /* Enable panel fitting for LVDS */ - if (dev_priv->pch_pf_size && - (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) || - intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP))) { - /* Force use of hard-coded filter coefficients - * as some pre-programmed values are broken, - * e.g. x201. - */ - if (IS_IVYBRIDGE(dev)) - I915_WRITE(PF_CTL(pipe), PF_ENABLE | PF_FILTER_MED_3x3 | - PF_PIPE_SEL_IVB(pipe)); - else - I915_WRITE(PF_CTL(pipe), PF_ENABLE | PF_FILTER_MED_3x3); - I915_WRITE(PF_WIN_POS(pipe), dev_priv->pch_pf_pos); - I915_WRITE(PF_WIN_SZ(pipe), dev_priv->pch_pf_size); - } + ironlake_pfit_enable(intel_crtc); /* * On ILK+ LUT must be loaded before the pipe is running but with @@ -3353,17 +3361,7 @@ static void haswell_crtc_enable(struct drm_crtc *crtc) intel_ddi_enable_pipe_clock(intel_crtc); /* Enable panel fitting for eDP */ - if (dev_priv->pch_pf_size && - intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP)) { - /* Force use of hard-coded filter coefficients - * as some pre-programmed values are broken, - * e.g. x201. - */ - I915_WRITE(PF_CTL(pipe), PF_ENABLE | PF_FILTER_MED_3x3 | - PF_PIPE_SEL_IVB(pipe)); - I915_WRITE(PF_WIN_POS(pipe), dev_priv->pch_pf_pos); - I915_WRITE(PF_WIN_SZ(pipe), dev_priv->pch_pf_size); - } + ironlake_pfit_enable(intel_crtc); /* * On ILK+ LUT must be loaded before the pipe is running but with @@ -3621,11 +3619,11 @@ static void i9xx_pfit_enable(struct intel_crtc *crtc) * register description and PRM. */ DRM_DEBUG_KMS("applying panel-fitter: %x, %x\n", - pipe_config->pfit_control, - pipe_config->pfit_pgm_ratios); + pipe_config->gmch_pfit.control, + pipe_config->gmch_pfit.pgm_ratios); - I915_WRITE(PFIT_PGM_RATIOS, pipe_config->pfit_pgm_ratios); - I915_WRITE(PFIT_CONTROL, pipe_config->pfit_control); + I915_WRITE(PFIT_PGM_RATIOS, pipe_config->gmch_pfit.pgm_ratios); + I915_WRITE(PFIT_CONTROL, pipe_config->gmch_pfit.control); } static void valleyview_crtc_enable(struct drm_crtc *crtc) @@ -5800,6 +5798,9 @@ static void haswell_modeset_global_resources(struct drm_device *dev) /* XXX: Should check for edp transcoder here, but thanks to init * sequence that's not yet available. Just in case desktop eDP * on PORT D is possible on haswell, too. */ + /* Even the eDP panel fitter is outside the always-on well. */ + if (I915_READ(PF_WIN_SZ(crtc->pipe))) + enable = true; } list_for_each_entry(encoder, &dev->mode_config.encoder_list, @@ -5809,10 +5810,6 @@ static void haswell_modeset_global_resources(struct drm_device *dev) enable = true; } - /* Even the eDP panel fitter is outside the always-on well. */ - if (dev_priv->pch_pf_size) - enable = true; - intel_set_power_well(dev, enable); } diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 9c834bc15ab7..9ac034ee0ddd 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -710,7 +710,6 @@ intel_dp_compute_config(struct intel_encoder *encoder, struct drm_device *dev = encoder->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode; - struct drm_display_mode *mode = &pipe_config->requested_mode; struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); struct intel_crtc *intel_crtc = encoder->new_crtc; struct intel_connector *intel_connector = intel_dp->attached_connector; @@ -733,9 +732,8 @@ intel_dp_compute_config(struct intel_encoder *encoder, intel_gmch_panel_fitting(intel_crtc, pipe_config, intel_connector->panel.fitting_mode); else - intel_pch_panel_fitting(dev, - intel_connector->panel.fitting_mode, - mode, adjusted_mode); + intel_pch_panel_fitting(intel_crtc, pipe_config, + intel_connector->panel.fitting_mode); } /* We need to take the panel's fixed mode into account. */ target_clock = adjusted_mode->clock; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 9f3f71bc2f22..a8c696014967 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -239,7 +239,16 @@ struct intel_crtc_config { unsigned pixel_multiplier; /* Panel fitter controls for gen2-gen4 + VLV */ - u32 pfit_control, pfit_pgm_ratios; + struct { + u32 control; + u32 pgm_ratios; + } gmch_pfit; + + /* Panel fitter placement and size for Ironlake+ */ + struct { + u32 pos; + u32 size; + } pch_pfit; }; struct intel_crtc { @@ -557,10 +566,9 @@ extern void intel_panel_fini(struct intel_panel *panel); 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, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode); +extern void intel_pch_panel_fitting(struct intel_crtc *crtc, + struct intel_crtc_config *pipe_config, + int fitting_mode); extern void intel_gmch_panel_fitting(struct intel_crtc *crtc, struct intel_crtc_config *pipe_config, int fitting_mode); diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 7d418818c7a8..3e29499b2e9a 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -229,7 +229,6 @@ static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder, struct intel_connector *intel_connector = &lvds_encoder->attached_connector->base; struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode; - struct drm_display_mode *mode = &pipe_config->requested_mode; struct intel_crtc *intel_crtc = lvds_encoder->base.new_crtc; unsigned int lvds_bpp; int pipe; @@ -267,9 +266,8 @@ static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder, if (HAS_PCH_SPLIT(dev)) { pipe_config->has_pch_encoder = true; - intel_pch_panel_fitting(dev, - intel_connector->panel.fitting_mode, - mode, adjusted_mode); + intel_pch_panel_fitting(intel_crtc, pipe_config, + intel_connector->panel.fitting_mode); return true; } else { intel_gmch_panel_fitting(intel_crtc, pipe_config, diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index 0f32f6498ad3..00f31f7336f2 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -54,14 +54,17 @@ intel_fixed_panel_mode(struct drm_display_mode *fixed_mode, /* adjusted_mode has been preset to be the panel's fixed mode */ void -intel_pch_panel_fitting(struct drm_device *dev, - int fitting_mode, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) +intel_pch_panel_fitting(struct intel_crtc *intel_crtc, + struct intel_crtc_config *pipe_config, + int fitting_mode) { - struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = intel_crtc->base.dev->dev_private; + struct drm_display_mode *mode, *adjusted_mode; int x, y, width, height; + mode = &pipe_config->requested_mode; + adjusted_mode = &pipe_config->adjusted_mode; + x = y = width = height = 0; /* Native modes don't need fitting */ @@ -113,8 +116,8 @@ intel_pch_panel_fitting(struct drm_device *dev, } done: - dev_priv->pch_pf_pos = (x << 16) | y; - dev_priv->pch_pf_size = (width << 16) | height; + pipe_config->pch_pfit.pos = (x << 16) | y; + pipe_config->pch_pfit.size = (width << 16) | height; } static void @@ -300,10 +303,10 @@ out: if (INTEL_INFO(dev)->gen < 4 && pipe_config->pipe_bpp == 18) pfit_control |= PANEL_8TO6_DITHER_ENABLE; - if (pfit_control != pipe_config->pfit_control || - pfit_pgm_ratios != pipe_config->pfit_pgm_ratios) { - pipe_config->pfit_control = pfit_control; - pipe_config->pfit_pgm_ratios = pfit_pgm_ratios; + if (pfit_control != pipe_config->gmch_pfit.control || + pfit_pgm_ratios != pipe_config->gmch_pfit.pgm_ratios) { + pipe_config->gmch_pfit.control = pfit_control; + pipe_config->gmch_pfit.pgm_ratios = pfit_pgm_ratios; } dev_priv->lvds_border_bits = border; } -- cgit v1.2.3 From ab3e67f43a299b064ccd8cd230d4a006a05c8a4c Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 25 Apr 2013 12:55:03 -0700 Subject: drm/i915: warn about invalid pfit modes We prevent invalid ones from getting here in the first place, but it doesn't hurt to have an extra sanity check. Signed-off-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_panel.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index 00f31f7336f2..2526326efd81 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -58,7 +58,6 @@ intel_pch_panel_fitting(struct intel_crtc *intel_crtc, struct intel_crtc_config *pipe_config, int fitting_mode) { - struct drm_i915_private *dev_priv = intel_crtc->base.dev->dev_private; struct drm_display_mode *mode, *adjusted_mode; int x, y, width, height; @@ -107,12 +106,15 @@ intel_pch_panel_fitting(struct intel_crtc *intel_crtc, } break; - default: case DRM_MODE_SCALE_FULLSCREEN: x = y = 0; width = adjusted_mode->hdisplay; height = adjusted_mode->vdisplay; break; + + default: + WARN(1, "bad panel fit mode: %d\n", fitting_mode); + return; } done: @@ -267,7 +269,6 @@ void intel_gmch_panel_fitting(struct intel_crtc *intel_crtc, } } break; - default: case DRM_MODE_SCALE_FULLSCREEN: /* * Full scaling, even if it changes the aspect ratio. @@ -285,6 +286,9 @@ void intel_gmch_panel_fitting(struct intel_crtc *intel_crtc, HORIZ_INTERP_BILINEAR); } break; + default: + WARN(1, "bad panel fit mode: %d\n", fitting_mode); + return; } /* 965+ wants fuzzy fitting */ -- cgit v1.2.3 From 2ce12e3dffe005f96e3b3ff1f761bbb0bf570fda Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Fri, 1 Mar 2013 14:08:33 -0800 Subject: drm/i915: remove VLV MSI IRQ hack Signed-off-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 125b45a6780d..b0dbf4cd0e07 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -2657,7 +2657,6 @@ static int valleyview_irq_postinstall(struct drm_device *dev) u32 enable_mask; u32 pipestat_enable = PLANE_FLIP_DONE_INT_EN_VLV; u32 render_irqs; - u16 msid; enable_mask = I915_DISPLAY_PORT_INTERRUPT; enable_mask |= I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | @@ -2673,13 +2672,6 @@ static int valleyview_irq_postinstall(struct drm_device *dev) I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT | I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT; - /* Hack for broken MSIs on VLV */ - pci_write_config_dword(dev_priv->dev->pdev, 0x94, 0xfee00000); - pci_read_config_word(dev->pdev, 0x98, &msid); - msid &= 0xff; /* mask out delivery bits */ - msid |= (1<<14); - pci_write_config_word(dev_priv->dev->pdev, 0x98, msid); - I915_WRITE(PORT_HOTPLUG_EN, 0); POSTING_READ(PORT_HOTPLUG_EN); -- cgit v1.2.3 From d8241785c24d4508ecc26fb91b28b39a0dd26070 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sat, 27 Apr 2013 12:44:16 +0100 Subject: drm/i915: Only print the info message about incresing stolen size for FBC once Instead of repeatedly bombarding the user with a request to reboot and increase the stolen size with every fb refresh, just inform them the first time only. v2: Rearrange code so the hint to increase the amount of memory stolen by the BIOS is only emitted if we fail to find sufficient stolen memory for FBC. Signed-off-by: Chris Wilson [danvet: Fixup formatting code mismatch that gcc spotted.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_stolen.c | 1 + drivers/gpu/drm/i915/intel_pm.c | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c index 130d1db27e28..67d3510cafed 100644 --- a/drivers/gpu/drm/i915/i915_gem_stolen.c +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c @@ -136,6 +136,7 @@ static int i915_setup_compression(struct drm_device *dev, int size) err_fb: drm_mm_put_block(compressed_fb); err: + pr_info_once("drm: not enough stolen space for compressed buffer (need %d more bytes), disabling. Hint: you may be able to increase stolen memory size in the BIOS to avoid this.\n", size); return -ENOSPC; } diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index bb45122173dd..0f4b46e94932 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -481,8 +481,6 @@ void intel_update_fbc(struct drm_device *dev) goto out_disable; if (i915_gem_stolen_setup_compression(dev, intel_fb->obj->base.size)) { - DRM_INFO("not enough stolen space for compressed buffer (need %zd bytes), disabling\n", intel_fb->obj->base.size); - DRM_INFO("hint: you may be able to increase stolen memory size in the BIOS to avoid this\n"); DRM_DEBUG_KMS("framebuffer too large, disabling compression\n"); dev_priv->no_fbc_reason = FBC_STOLEN_TOO_SMALL; goto out_disable; -- cgit v1.2.3 From 17322cc3f9ba578f20b5c09fb1630bd234040008 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 18 Feb 2013 15:59:34 -0800 Subject: apparmor: fix auditing of domain transition failures due to incomplete policy When policy specifies a transition to a profile that is not currently loaded, it result in exec being denied. However the failure is not being audited correctly because the audit code is treating this as an allowed permission and thus not reporting it. Signed-off-by: John Johansen Acked-By: Steve Beattie --- security/apparmor/domain.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 859abdaac1ea..7bc85c7f4573 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -443,6 +443,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) } else { error = -ENOENT; info = "profile not found"; + /* remove MAY_EXEC to audit as failure */ + perms.allow &= ~MAY_EXEC; } } } else if (COMPLAIN_MODE(profile)) { -- cgit v1.2.3 From 04266236b1c3030bb7f75472ac85a8b78fcfb284 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 18 Feb 2013 16:00:34 -0800 Subject: apparmor: Remove -W1 warnings Signed-off-by: John Johansen Acked-By: Steve Beattie --- security/apparmor/domain.c | 2 -- security/apparmor/lsm.c | 4 ---- 2 files changed, 6 deletions(-) diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 7bc85c7f4573..7a78e814f0d4 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -752,7 +752,6 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec, bool permtest) { const struct cred *cred; - struct aa_task_cxt *cxt; struct aa_profile *profile, *target = NULL; struct aa_namespace *ns = NULL; struct file_perms perms = {}; @@ -772,7 +771,6 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec, } cred = get_current_cred(); - cxt = cred->security; profile = aa_cred_profile(cred); /* diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index b21830eced41..0f61dadca9e6 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -469,7 +469,6 @@ static int apparmor_file_lock(struct file *file, unsigned int cmd) static int common_mmap(int op, struct file *file, unsigned long prot, unsigned long flags) { - struct dentry *dentry; int mask = 0; if (!file || !file->f_security) @@ -486,7 +485,6 @@ static int common_mmap(int op, struct file *file, unsigned long prot, if (prot & PROT_EXEC) mask |= AA_EXEC_MMAP; - dentry = file->f_path.dentry; return common_file_perm(op, file, mask); } @@ -507,11 +505,9 @@ static int apparmor_getprocattr(struct task_struct *task, char *name, char **value) { int error = -ENOENT; - struct aa_profile *profile; /* released below */ const struct cred *cred = get_task_cred(task); struct aa_task_cxt *cxt = cred->security; - profile = aa_cred_profile(cred); if (strcmp(name, "current") == 0) error = aa_getprocattr(aa_newest_version(cxt->profile), -- cgit v1.2.3 From 50c5ecd5d8ffb0e549676b8fd9781e3b2fd751a0 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 18 Feb 2013 16:01:34 -0800 Subject: apparmor: refactor profile mode macros Signed-off-by: John Johansen Acked-by: Steve Beattie --- security/apparmor/include/policy.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index bda4569fdd83..95979c431e26 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -32,13 +32,13 @@ extern const char *const profile_mode_names[]; #define APPARMOR_NAMES_MAX_INDEX 3 -#define COMPLAIN_MODE(_profile) \ - ((aa_g_profile_mode == APPARMOR_COMPLAIN) || \ - ((_profile)->mode == APPARMOR_COMPLAIN)) +#define PROFILE_MODE(_profile, _mode) \ + ((aa_g_profile_mode == (_mode)) || \ + ((_profile)->mode == (_mode))) -#define KILL_MODE(_profile) \ - ((aa_g_profile_mode == APPARMOR_KILL) || \ - ((_profile)->mode == APPARMOR_KILL)) +#define COMPLAIN_MODE(_profile) PROFILE_MODE((_profile), APPARMOR_COMPLAIN) + +#define KILL_MODE(_profile) PROFILE_MODE((_profile), APPARMOR_KILL) #define PROFILE_IS_HAT(_profile) ((_profile)->flags & PFLAG_HAT) -- cgit v1.2.3 From e573cc30bb36df23fb49a29d96e6c6333d17f59c Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 18 Feb 2013 16:02:34 -0800 Subject: apparmor: fix error code to failure message mapping for name lookup -ESTALE used to be incorrectly used to indicate a disconnected path, when name lookup failed. This was fixed in commit e1b0e444 to correctly return -EACCESS, but the error to failure message mapping was not correctly updated to reflect this change. Signed-off-by: John Johansen Acked-by: Steve Beattie --- security/apparmor/path.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/path.c b/security/apparmor/path.c index e91ffee80162..35b394a75d76 100644 --- a/security/apparmor/path.c +++ b/security/apparmor/path.c @@ -174,7 +174,7 @@ static int get_name_to_buffer(struct path *path, int flags, char *buffer, if (info && error) { if (error == -ENOENT) *info = "Failed name lookup - deleted entry"; - else if (error == -ESTALE) + else if (error == -EACCES) *info = "Failed name lookup - disconnected path"; else if (error == -ENAMETOOLONG) *info = "Failed name lookup - name too long"; -- cgit v1.2.3 From 3cfcc19e0b5390c04cb5bfa4e8fde39395410e61 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 18 Feb 2013 16:03:34 -0800 Subject: apparmor: add utility function to get an arbitrary tasks profile. Signed-off-by: John Johansen Acked-by: Steve Beattie --- security/apparmor/context.c | 17 +++++++++++++++ security/apparmor/domain.c | 10 +++------ security/apparmor/include/context.h | 41 ++++++++++++++++++++++--------------- security/apparmor/ipc.c | 13 ++++-------- 4 files changed, 49 insertions(+), 32 deletions(-) diff --git a/security/apparmor/context.c b/security/apparmor/context.c index 8a9b5027c813..611e6ce70b03 100644 --- a/security/apparmor/context.c +++ b/security/apparmor/context.c @@ -68,6 +68,23 @@ void aa_dup_task_context(struct aa_task_cxt *new, const struct aa_task_cxt *old) aa_get_profile(new->onexec); } +/** + * aa_get_task_profile - Get another task's profile + * @task: task to query (NOT NULL) + * + * Returns: counted reference to @task's profile + */ +struct aa_profile *aa_get_task_profile(struct task_struct *task) +{ + struct aa_profile *p; + + rcu_read_lock(); + p = aa_get_profile(__aa_task_profile(task)); + rcu_read_unlock(); + + return p; +} + /** * aa_replace_current_profile - replace the current tasks profiles * @profile: new profile (NOT NULL) diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 7a78e814f0d4..fb47d5b71ea6 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -62,17 +62,14 @@ static int may_change_ptraced_domain(struct task_struct *task, struct aa_profile *to_profile) { struct task_struct *tracer; - const struct cred *cred = NULL; struct aa_profile *tracerp = NULL; int error = 0; rcu_read_lock(); tracer = ptrace_parent(task); - if (tracer) { + if (tracer) /* released below */ - cred = get_task_cred(tracer); - tracerp = aa_cred_profile(cred); - } + tracerp = aa_get_task_profile(tracer); /* not ptraced */ if (!tracer || unconfined(tracerp)) @@ -82,8 +79,7 @@ static int may_change_ptraced_domain(struct task_struct *task, out: rcu_read_unlock(); - if (cred) - put_cred(cred); + aa_put_profile(tracerp); return error; } diff --git a/security/apparmor/include/context.h b/security/apparmor/include/context.h index a9cbee4d9e48..1e9443a58877 100644 --- a/security/apparmor/include/context.h +++ b/security/apparmor/include/context.h @@ -80,23 +80,8 @@ int aa_replace_current_profile(struct aa_profile *profile); int aa_set_current_onexec(struct aa_profile *profile); int aa_set_current_hat(struct aa_profile *profile, u64 token); int aa_restore_previous_profile(u64 cookie); +struct aa_profile *aa_get_task_profile(struct task_struct *task); -/** - * __aa_task_is_confined - determine if @task has any confinement - * @task: task to check confinement of (NOT NULL) - * - * If @task != current needs to be called in RCU safe critical section - */ -static inline bool __aa_task_is_confined(struct task_struct *task) -{ - struct aa_task_cxt *cxt = __task_cred(task)->security; - - BUG_ON(!cxt || !cxt->profile); - if (unconfined(aa_newest_version(cxt->profile))) - return 0; - - return 1; -} /** * aa_cred_profile - obtain cred's profiles @@ -113,6 +98,30 @@ static inline struct aa_profile *aa_cred_profile(const struct cred *cred) return aa_newest_version(cxt->profile); } +/** + * __aa_task_profile - retrieve another task's profile + * @task: task to query (NOT NULL) + * + * Returns: @task's profile without incrementing its ref count + * + * If @task != current needs to be called in RCU safe critical section + */ +static inline struct aa_profile *__aa_task_profile(struct task_struct *task) +{ + return aa_cred_profile(__task_cred(task)); +} + +/** + * __aa_task_is_confined - determine if @task has any confinement + * @task: task to check confinement of (NOT NULL) + * + * If @task != current needs to be called in RCU safe critical section + */ +static inline bool __aa_task_is_confined(struct task_struct *task) +{ + return !unconfined(__aa_task_profile(task)); +} + /** * __aa_current_profile - find the current tasks confining profile * diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c index cf1071b14232..c51d2266587e 100644 --- a/security/apparmor/ipc.c +++ b/security/apparmor/ipc.c @@ -95,23 +95,18 @@ int aa_ptrace(struct task_struct *tracer, struct task_struct *tracee, * - tracer profile has CAP_SYS_PTRACE */ - struct aa_profile *tracer_p; - /* cred released below */ - const struct cred *cred = get_task_cred(tracer); + struct aa_profile *tracer_p = aa_get_task_profile(tracer); int error = 0; - tracer_p = aa_cred_profile(cred); if (!unconfined(tracer_p)) { - /* lcred released below */ - const struct cred *lcred = get_task_cred(tracee); - struct aa_profile *tracee_p = aa_cred_profile(lcred); + struct aa_profile *tracee_p = aa_get_task_profile(tracee); error = aa_may_ptrace(tracer, tracer_p, tracee_p, mode); error = aa_audit_ptrace(tracer_p, tracee_p, error); - put_cred(lcred); + aa_put_profile(tracee_p); } - put_cred(cred); + aa_put_profile(tracer_p); return error; } -- cgit v1.2.3 From 0ca554b9fca425eb58325a36290deef698cef34b Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 18 Feb 2013 16:04:34 -0800 Subject: apparmor: add kvzalloc to handle zeroing for kvmalloc Signed-off-by: John Johansen Acked-by: Steve Beattie --- security/apparmor/include/apparmor.h | 12 +++++++++++- security/apparmor/lib.c | 14 +++++++++----- security/apparmor/match.c | 4 ++-- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index 40aedd9f73ea..1ba2ca56a6ef 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -15,6 +15,7 @@ #ifndef __APPARMOR_H #define __APPARMOR_H +#include #include #include "match.h" @@ -64,9 +65,18 @@ extern int apparmor_initialized __initdata; /* fn's in lib */ char *aa_split_fqname(char *args, char **ns_name); void aa_info_message(const char *str); -void *kvmalloc(size_t size); +void *__aa_kvmalloc(size_t size, gfp_t flags); void kvfree(void *buffer); +static inline void *kvmalloc(size_t size) +{ + return __aa_kvmalloc(size, 0); +} + +static inline void *kvzalloc(size_t size) +{ + return __aa_kvmalloc(size, __GFP_ZERO); +} /** * aa_strneq - compare null terminated @str to a non null terminated substring diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index 7430298116d6..d6e1f2148398 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -75,15 +75,16 @@ void aa_info_message(const char *str) } /** - * kvmalloc - do allocation preferring kmalloc but falling back to vmalloc - * @size: size of allocation + * __aa_kvmalloc - do allocation preferring kmalloc but falling back to vmalloc + * @size: how many bytes of memory are required + * @flags: the type of memory to allocate (see kmalloc). * * Return: allocated buffer or NULL if failed * * It is possible that policy being loaded from the user is larger than * what can be allocated by kmalloc, in those cases fall back to vmalloc. */ -void *kvmalloc(size_t size) +void *__aa_kvmalloc(size_t size, gfp_t flags) { void *buffer = NULL; @@ -92,14 +93,17 @@ void *kvmalloc(size_t size) /* do not attempt kmalloc if we need more than 16 pages at once */ if (size <= (16*PAGE_SIZE)) - buffer = kmalloc(size, GFP_NOIO | __GFP_NOWARN); + buffer = kmalloc(size, flags | GFP_NOIO | __GFP_NOWARN); if (!buffer) { /* see kvfree for why size must be at least work_struct size * when allocated via vmalloc */ if (size < sizeof(struct work_struct)) size = sizeof(struct work_struct); - buffer = vmalloc(size); + if (flags & __GFP_ZERO) + buffer = vzalloc(size); + else + buffer = vmalloc(size); } return buffer; } diff --git a/security/apparmor/match.c b/security/apparmor/match.c index 90971a8c3789..dfd25a9c9a69 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -30,7 +30,7 @@ * * Returns: pointer to table else NULL on failure * - * NOTE: must be freed by kvfree (not kmalloc) + * NOTE: must be freed by kvfree (not kfree) */ static struct table_header *unpack_table(char *blob, size_t bsize) { @@ -57,7 +57,7 @@ static struct table_header *unpack_table(char *blob, size_t bsize) if (bsize < tsize) goto out; - table = kvmalloc(tsize); + table = kvzalloc(tsize); if (table) { *table = th; if (th.td_flags == YYTD_DATA8) -- cgit v1.2.3 From 7a2871b566f34d980556072943295efd107eb53c Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 18 Feb 2013 16:05:34 -0800 Subject: apparmor: use common fn to clear task_context for domain transitions Signed-off-by: John Johansen Acked-by: Steve Beattie --- security/apparmor/context.c | 17 ++++++----------- security/apparmor/domain.c | 6 +----- security/apparmor/include/context.h | 13 +++++++++++++ 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/security/apparmor/context.c b/security/apparmor/context.c index 611e6ce70b03..3f911afa2bb9 100644 --- a/security/apparmor/context.c +++ b/security/apparmor/context.c @@ -105,16 +105,12 @@ int aa_replace_current_profile(struct aa_profile *profile) return -ENOMEM; cxt = new->security; - if (unconfined(profile) || (cxt->profile->ns != profile->ns)) { + if (unconfined(profile) || (cxt->profile->ns != profile->ns)) /* if switching to unconfined or a different profile namespace * clear out context state */ - aa_put_profile(cxt->previous); - aa_put_profile(cxt->onexec); - cxt->previous = NULL; - cxt->onexec = NULL; - cxt->token = 0; - } + aa_clear_task_cxt_trans(cxt); + /* be careful switching cxt->profile, when racing replacement it * is possible that cxt->profile->replacedby is the reference keeping * @profile valid, so make sure to get its reference before dropping @@ -222,11 +218,10 @@ int aa_restore_previous_profile(u64 token) aa_get_profile(cxt->profile); aa_put_profile(cxt->previous); } - /* clear exec && prev information when restoring to previous context */ + /* ref has been transfered so avoid putting ref in clear_task_cxt */ cxt->previous = NULL; - cxt->token = 0; - aa_put_profile(cxt->onexec); - cxt->onexec = NULL; + /* clear exec && prev information when restoring to previous context */ + aa_clear_task_cxt_trans(cxt); commit_creds(new); return 0; diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index fb47d5b71ea6..07fcb09b990f 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -512,11 +512,7 @@ x_clear: cxt->profile = new_profile; /* clear out all temporary/transitional state from the context */ - aa_put_profile(cxt->previous); - aa_put_profile(cxt->onexec); - cxt->previous = NULL; - cxt->onexec = NULL; - cxt->token = 0; + aa_clear_task_cxt_trans(cxt); audit: error = aa_audit_file(profile, &perms, GFP_KERNEL, OP_EXEC, MAY_EXEC, diff --git a/security/apparmor/include/context.h b/security/apparmor/include/context.h index 1e9443a58877..4cecad313227 100644 --- a/security/apparmor/include/context.h +++ b/security/apparmor/include/context.h @@ -160,4 +160,17 @@ static inline struct aa_profile *aa_current_profile(void) return profile; } +/** + * aa_clear_task_cxt_trans - clear transition tracking info from the cxt + * @cxt: task context to clear (NOT NULL) + */ +static inline void aa_clear_task_cxt_trans(struct aa_task_cxt *cxt) +{ + aa_put_profile(cxt->previous); + aa_put_profile(cxt->onexec); + cxt->previous = NULL; + cxt->onexec = NULL; + cxt->token = 0; +} + #endif /* __AA_CONTEXT_H */ -- cgit v1.2.3 From 4b7c331fc2eceaa4da5ded41c0b2eca3fd924444 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 18 Feb 2013 16:06:34 -0800 Subject: apparmor: remove "permipc" command The "permipc" command is unused and unfinished, remove it. Signed-off-by: John Johansen Acked-by: Kees Cook --- security/apparmor/include/procattr.h | 1 - security/apparmor/lsm.c | 2 -- security/apparmor/procattr.c | 6 ------ 3 files changed, 9 deletions(-) diff --git a/security/apparmor/include/procattr.h b/security/apparmor/include/procattr.h index 544aa6b766a4..6bd5f33d9533 100644 --- a/security/apparmor/include/procattr.h +++ b/security/apparmor/include/procattr.h @@ -21,6 +21,5 @@ int aa_getprocattr(struct aa_profile *profile, char **string); int aa_setprocattr_changehat(char *args, size_t size, int test); int aa_setprocattr_changeprofile(char *fqname, bool onexec, int test); -int aa_setprocattr_permipc(char *fqname); #endif /* __AA_PROCATTR_H */ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 0f61dadca9e6..ed7e3aadba3a 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -572,8 +572,6 @@ static int apparmor_setprocattr(struct task_struct *task, char *name, } else if (strcmp(command, "permprofile") == 0) { error = aa_setprocattr_changeprofile(args, !AA_ONEXEC, AA_DO_TEST); - } else if (strcmp(command, "permipc") == 0) { - error = aa_setprocattr_permipc(args); } else { struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c index 1b41c542d376..6c9390179b89 100644 --- a/security/apparmor/procattr.c +++ b/security/apparmor/procattr.c @@ -163,9 +163,3 @@ int aa_setprocattr_changeprofile(char *fqname, bool onexec, int test) name = aa_split_fqname(fqname, &ns_name); return aa_change_profile(ns_name, name, onexec, test); } - -int aa_setprocattr_permipc(char *fqname) -{ - /* TODO: add ipc permission querying */ - return -ENOTSUPP; -} -- cgit v1.2.3 From cf47aede3b9e197d3b4a028e2157bf7736665ac4 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 18 Feb 2013 16:07:34 -0800 Subject: apparmor: relax the restrictions on setting rlimits Instead of limiting the setting of the processes limits to current, relax this to tasks confined by the same profile, as the apparmor controls for rlimits are at a profile level granularity. Signed-off-by: John Johansen Acked-by: Steve Beattie --- security/apparmor/resource.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/security/apparmor/resource.c b/security/apparmor/resource.c index e1f3d7ef2c54..748bf0ca6c9f 100644 --- a/security/apparmor/resource.c +++ b/security/apparmor/resource.c @@ -15,6 +15,7 @@ #include #include "include/audit.h" +#include "include/context.h" #include "include/resource.h" #include "include/policy.h" @@ -90,17 +91,25 @@ int aa_map_resource(int resource) int aa_task_setrlimit(struct aa_profile *profile, struct task_struct *task, unsigned int resource, struct rlimit *new_rlim) { + struct aa_profile *task_profile; int error = 0; + rcu_read_lock(); + task_profile = aa_get_profile(aa_cred_profile(__task_cred(task))); + rcu_read_unlock(); + /* TODO: extend resource control to handle other (non current) - * processes. AppArmor rules currently have the implicit assumption - * that the task is setting the resource of the current process + * profiles. AppArmor rules currently have the implicit assumption + * that the task is setting the resource of a task confined with + * the same profile. */ - if ((task != current->group_leader) || + if (profile != task_profile || (profile->rlimits.mask & (1 << resource) && new_rlim->rlim_max > profile->rlimits.limits[resource].rlim_max)) error = -EACCES; + aa_put_profile(task_profile); + return audit_resource(profile, resource, new_rlim->rlim_max, error); } -- cgit v1.2.3 From 8e4ff109d0d2194d98e9e16325bb4102f6463b43 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 18 Feb 2013 16:08:34 -0800 Subject: apparmor: misc cleanup of match tidying up comments, includes and defines Signed-off-by: John Johansen Acked-by: Kees Cook --- security/apparmor/include/match.h | 19 +++++++++++++------ security/apparmor/match.c | 3 +-- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h index 775843e7f984..bbbf56f5ba78 100644 --- a/security/apparmor/include/match.h +++ b/security/apparmor/include/match.h @@ -4,7 +4,7 @@ * This file contains AppArmor policy dfa matching engine definitions. * * Copyright (C) 1998-2008 Novell/SUSE - * Copyright 2009-2010 Canonical Ltd. + * Copyright 2009-2012 Canonical Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -16,7 +16,6 @@ #define __AA_MATCH_H #include -#include #define DFA_NOMATCH 0 #define DFA_START 1 @@ -29,12 +28,20 @@ * file format (--tables-file option; see Table File Format in the flex * info pages and the flex sources for documentation). The magic number * used in the header is 0x1B5E783D instead of 0xF13C57B1 though, because - * the YY_ID_CHK (check) and YY_ID_DEF (default) tables are used - * slightly differently (see the apparmor-parser package). + * new tables have been defined and others YY_ID_CHK (check) and YY_ID_DEF + * (default) tables are used slightly differently (see the apparmor-parser + * package). + * + * + * The data in the packed dfa is stored in network byte order, and the tables + * are arranged for flexibility. We convert the table data to host native + * byte order. + * + * The dfa begins with a table set header, and is followed by the actual + * tables. */ #define YYTH_MAGIC 0x1B5E783D -#define YYTH_DEF_RECURSE 0x1 /* DEF Table is recursive */ struct table_set_header { u32 th_magic; /* YYTH_MAGIC */ @@ -63,7 +70,7 @@ struct table_set_header { #define YYTD_DATA32 4 #define YYTD_DATA64 8 -/* Each ACCEPT2 table gets 6 dedicated flags, YYTD_DATAX define the +/* ACCEPT & ACCEPT2 tables gets 6 dedicated flags, YYTD_DATAX define the * first flags */ #define ACCEPT1_FLAGS(X) ((X) & 0x3f) diff --git a/security/apparmor/match.c b/security/apparmor/match.c index dfd25a9c9a69..1ff823031c73 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -4,7 +4,7 @@ * This file contains AppArmor dfa based regular expression matching engine * * Copyright (C) 1998-2008 Novell/SUSE - * Copyright 2009-2010 Canonical Ltd. + * Copyright 2009-2012 Canonical Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -137,7 +137,6 @@ static int verify_dfa(struct aa_dfa *dfa, int flags) for (i = 0; i < state_count; i++) { if (DEFAULT_TABLE(dfa)[i] >= state_count) goto out; - /* TODO: do check that DEF state recursion terminates */ if (BASE_TABLE(dfa)[i] + 255 >= trans_count) { printk(KERN_ERR "AppArmor DFA next/check upper " "bounds error\n"); -- cgit v1.2.3 From 180a6f5965a49535a7704c07691a6d1209904971 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 18 Feb 2013 16:09:34 -0800 Subject: apparmor: move perm defines into policy_unpack Signed-off-by: John Johansen Acked-by: Steve Beattie --- security/apparmor/include/match.h | 2 -- security/apparmor/policy_unpack.c | 3 +++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h index bbbf56f5ba78..001c43aa0406 100644 --- a/security/apparmor/include/match.h +++ b/security/apparmor/include/match.h @@ -20,8 +20,6 @@ #define DFA_NOMATCH 0 #define DFA_START 1 -#define DFA_VALID_PERM_MASK 0xffffffff -#define DFA_VALID_PERM2_MASK 0xffffffff /** * The format used for transition tables is based on the GNU flex table diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 329b1fd30749..ca48a7d8d5b3 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -290,6 +290,9 @@ static int unpack_strdup(struct aa_ext *e, char **string, const char *name) return res; } +#define DFA_VALID_PERM_MASK 0xffffffff +#define DFA_VALID_PERM2_MASK 0xffffffff + /** * verify_accept - verify the accept tables of a dfa * @dfa: dfa to verify accept tables of (NOT NULL) -- cgit v1.2.3 From a4987857d2c958b93b2faafe0811eea1a63ff59a Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 18 Feb 2013 16:10:34 -0800 Subject: apparmor: remove sid from profiles The sid is not going to be a direct property of a profile anymore, instead it will be directly related to the label, and the profile will pickup a label back reference. For null-profiles replace the use of sid with a per namespace unique id. Signed-off-by: John Johansen Acked-by: Kees Cook --- security/apparmor/include/policy.h | 4 ++-- security/apparmor/include/sid.h | 4 +++- security/apparmor/policy.c | 23 ++++++----------------- security/apparmor/policy_unpack.c | 1 - 4 files changed, 11 insertions(+), 21 deletions(-) diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 95979c431e26..b25491a3046a 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -105,6 +105,7 @@ struct aa_ns_acct { * @acct: accounting for the namespace * @unconfined: special unconfined profile for the namespace * @sub_ns: list of namespaces under the current namespace. + * @uniq_null: uniq value used for null learning profiles * * An aa_namespace defines the set profiles that are searched to determine * which profile to attach to a task. Profiles can not be shared between @@ -127,6 +128,7 @@ struct aa_namespace { struct aa_ns_acct acct; struct aa_profile *unconfined; struct list_head sub_ns; + atomic_t uniq_null; }; /* struct aa_policydb - match engine for a policy @@ -148,7 +150,6 @@ struct aa_policydb { * @rename: optional profile name that this profile renamed * @xmatch: optional extended matching for unconfined executables names * @xmatch_len: xmatch prefix len, used to determine xmatch priority - * @sid: the unique security id number of this profile * @audit: the auditing mode of the profile * @mode: the enforcement mode of the profile * @flags: flags controlling profile behavior @@ -184,7 +185,6 @@ struct aa_profile { struct aa_dfa *xmatch; int xmatch_len; - u32 sid; enum audit_mode audit; enum profile_mode mode; u32 flags; diff --git a/security/apparmor/include/sid.h b/security/apparmor/include/sid.h index 020db35c3010..513ca0e48965 100644 --- a/security/apparmor/include/sid.h +++ b/security/apparmor/include/sid.h @@ -16,7 +16,9 @@ #include -struct aa_profile; +/* sid value that will not be allocated */ +#define AA_SID_INVALID 0 +#define AA_SID_ALLOC AA_SID_INVALID u32 aa_alloc_sid(void); void aa_free_sid(u32 sid); diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 813200384d97..13fc9efddd5d 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -87,7 +87,6 @@ #include "include/policy.h" #include "include/policy_unpack.h" #include "include/resource.h" -#include "include/sid.h" /* root profile namespace */ @@ -292,7 +291,6 @@ static struct aa_namespace *alloc_namespace(const char *prefix, if (!ns->unconfined) goto fail_unconfined; - ns->unconfined->sid = aa_alloc_sid(); ns->unconfined->flags = PFLAG_UNCONFINED | PFLAG_IX_ON_NAME_ERROR | PFLAG_IMMUTABLE; @@ -303,6 +301,8 @@ static struct aa_namespace *alloc_namespace(const char *prefix, */ ns->unconfined->ns = aa_get_namespace(ns); + atomic_set(&ns->uniq_null, 0); + return ns; fail_unconfined: @@ -497,7 +497,6 @@ static void __replace_profile(struct aa_profile *old, struct aa_profile *new) /* released when @new is freed */ new->parent = aa_get_profile(old->parent); new->ns = aa_get_namespace(old->ns); - new->sid = old->sid; __list_add_profile(&policy->profiles, new); /* inherit children */ list_for_each_entry_safe(child, tmp, &old->base.profiles, base.list) { @@ -665,7 +664,7 @@ struct aa_profile *aa_alloc_profile(const char *hname) * @hat: true if the null- learning profile is a hat * * Create a null- complain mode profile used in learning mode. The name of - * the profile is unique and follows the format of parent//null-sid. + * the profile is unique and follows the format of parent//null-. * * null profiles are added to the profile list but the list does not * hold a count on them so that they are automatically released when @@ -677,20 +676,19 @@ struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat) { struct aa_profile *profile = NULL; char *name; - u32 sid = aa_alloc_sid(); + int uniq = atomic_inc_return(&parent->ns->uniq_null); /* freed below */ name = kmalloc(strlen(parent->base.hname) + 2 + 7 + 8, GFP_KERNEL); if (!name) goto fail; - sprintf(name, "%s//null-%x", parent->base.hname, sid); + sprintf(name, "%s//null-%x", parent->base.hname, uniq); profile = aa_alloc_profile(name); kfree(name); if (!profile) goto fail; - profile->sid = sid; profile->mode = APPARMOR_COMPLAIN; profile->flags = PFLAG_NULL; if (hat) @@ -708,7 +706,6 @@ struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat) return profile; fail: - aa_free_sid(sid); return NULL; } @@ -749,7 +746,6 @@ static void free_profile(struct aa_profile *profile) aa_free_cap_rules(&profile->caps); aa_free_rlimit_rules(&profile->rlimits); - aa_free_sid(profile->sid); aa_put_dfa(profile->xmatch); aa_put_dfa(profile->policy.dfa); @@ -972,7 +968,6 @@ static void __add_new_profile(struct aa_namespace *ns, struct aa_policy *policy, profile->parent = aa_get_profile((struct aa_profile *) policy); __list_add_profile(&policy->profiles, profile); /* released on free_profile */ - profile->sid = aa_alloc_sid(); profile->ns = aa_get_namespace(ns); } @@ -1110,14 +1105,8 @@ audit: if (!error) { if (rename_profile) __replace_profile(rename_profile, new_profile); - if (old_profile) { - /* when there are both rename and old profiles - * inherit old profiles sid - */ - if (rename_profile) - aa_free_sid(new_profile->sid); + if (old_profile) __replace_profile(old_profile, new_profile); - } if (!(old_profile || rename_profile)) __add_new_profile(ns, policy, new_profile); } diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index ca48a7d8d5b3..6dac7d77cb4d 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -27,7 +27,6 @@ #include "include/match.h" #include "include/policy.h" #include "include/policy_unpack.h" -#include "include/sid.h" /* * The AppArmor interface treats data as a type byte followed by the -- cgit v1.2.3 From 4da05cc08da3f2058cecbe42ed9f4803d669730a Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 18 Feb 2013 16:11:34 -0800 Subject: apparmor: move the free_profile fn ahead of aa_alloc_profile Move the free_profile fn ahead of aa_alloc_profile so it can be used in aa_alloc_profile without a forward declaration. Signed-off-by: John Johansen Acked-by: Kees Cook --- security/apparmor/policy.c | 150 ++++++++++++++++++++++----------------------- 1 file changed, 75 insertions(+), 75 deletions(-) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 13fc9efddd5d..f4ee72b44de4 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -634,81 +634,6 @@ void __init aa_free_root_ns(void) aa_put_namespace(ns); } -/** - * aa_alloc_profile - allocate, initialize and return a new profile - * @hname: name of the profile (NOT NULL) - * - * Returns: refcount profile or NULL on failure - */ -struct aa_profile *aa_alloc_profile(const char *hname) -{ - struct aa_profile *profile; - - /* freed by free_profile - usually through aa_put_profile */ - profile = kzalloc(sizeof(*profile), GFP_KERNEL); - if (!profile) - return NULL; - - if (!policy_init(&profile->base, NULL, hname)) { - kzfree(profile); - return NULL; - } - - /* refcount released by caller */ - return profile; -} - -/** - * aa_new_null_profile - create a new null-X learning profile - * @parent: profile that caused this profile to be created (NOT NULL) - * @hat: true if the null- learning profile is a hat - * - * Create a null- complain mode profile used in learning mode. The name of - * the profile is unique and follows the format of parent//null-. - * - * null profiles are added to the profile list but the list does not - * hold a count on them so that they are automatically released when - * not in use. - * - * Returns: new refcounted profile else NULL on failure - */ -struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat) -{ - struct aa_profile *profile = NULL; - char *name; - int uniq = atomic_inc_return(&parent->ns->uniq_null); - - /* freed below */ - name = kmalloc(strlen(parent->base.hname) + 2 + 7 + 8, GFP_KERNEL); - if (!name) - goto fail; - sprintf(name, "%s//null-%x", parent->base.hname, uniq); - - profile = aa_alloc_profile(name); - kfree(name); - if (!profile) - goto fail; - - profile->mode = APPARMOR_COMPLAIN; - profile->flags = PFLAG_NULL; - if (hat) - profile->flags |= PFLAG_HAT; - - /* released on free_profile */ - profile->parent = aa_get_profile(parent); - profile->ns = aa_get_namespace(parent->ns); - - write_lock(&profile->ns->lock); - __list_add_profile(&parent->base.profiles, profile); - write_unlock(&profile->ns->lock); - - /* refcount released by caller */ - return profile; - -fail: - return NULL; -} - /** * free_profile - free a profile * @profile: the profile to free (MAYBE NULL) @@ -786,6 +711,81 @@ void aa_free_profile_kref(struct kref *kref) free_profile(p); } +/** + * aa_alloc_profile - allocate, initialize and return a new profile + * @hname: name of the profile (NOT NULL) + * + * Returns: refcount profile or NULL on failure + */ +struct aa_profile *aa_alloc_profile(const char *hname) +{ + struct aa_profile *profile; + + /* freed by free_profile - usually through aa_put_profile */ + profile = kzalloc(sizeof(*profile), GFP_KERNEL); + if (!profile) + return NULL; + + if (!policy_init(&profile->base, NULL, hname)) { + kzfree(profile); + return NULL; + } + + /* refcount released by caller */ + return profile; +} + +/** + * aa_new_null_profile - create a new null-X learning profile + * @parent: profile that caused this profile to be created (NOT NULL) + * @hat: true if the null- learning profile is a hat + * + * Create a null- complain mode profile used in learning mode. The name of + * the profile is unique and follows the format of parent//null-. + * + * null profiles are added to the profile list but the list does not + * hold a count on them so that they are automatically released when + * not in use. + * + * Returns: new refcounted profile else NULL on failure + */ +struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat) +{ + struct aa_profile *profile = NULL; + char *name; + int uniq = atomic_inc_return(&parent->ns->uniq_null); + + /* freed below */ + name = kmalloc(strlen(parent->base.hname) + 2 + 7 + 8, GFP_KERNEL); + if (!name) + goto fail; + sprintf(name, "%s//null-%x", parent->base.hname, uniq); + + profile = aa_alloc_profile(name); + kfree(name); + if (!profile) + goto fail; + + profile->mode = APPARMOR_COMPLAIN; + profile->flags = PFLAG_NULL; + if (hat) + profile->flags |= PFLAG_HAT; + + /* released on free_profile */ + profile->parent = aa_get_profile(parent); + profile->ns = aa_get_namespace(parent->ns); + + write_lock(&profile->ns->lock); + __list_add_profile(&parent->base.profiles, profile); + write_unlock(&profile->ns->lock); + + /* refcount released by caller */ + return profile; + +fail: + return NULL; +} + /* TODO: profile accounting - setup in remove */ /** -- cgit v1.2.3 From ed686308c6837ff67f56e4115d0fd6bdc65a4313 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 18 Feb 2013 16:12:34 -0800 Subject: apparmor: reserve and mask off the top 8 bits of the base field The top 8 bits of the base field have never been used, in fact can't be used, by the current 'dfa16' format. However they will be used in the future as flags, so mask them off when using base as an index value. Note: the use of the top 8 bits, without masking is trapped by the verify checks that base entries are within the size bounds. Signed-off-by: John Johansen Acked-by: Kees Cook --- security/apparmor/match.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/security/apparmor/match.c b/security/apparmor/match.c index 1ff823031c73..727eb4200d5c 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -23,6 +23,8 @@ #include "include/apparmor.h" #include "include/match.h" +#define base_idx(X) ((X) & 0xffffff) + /** * unpack_table - unpack a dfa table (one of accept, default, base, next check) * @blob: data to unpack (NOT NULL) @@ -137,7 +139,7 @@ static int verify_dfa(struct aa_dfa *dfa, int flags) for (i = 0; i < state_count; i++) { if (DEFAULT_TABLE(dfa)[i] >= state_count) goto out; - if (BASE_TABLE(dfa)[i] + 255 >= trans_count) { + if (base_idx(BASE_TABLE(dfa)[i]) + 255 >= trans_count) { printk(KERN_ERR "AppArmor DFA next/check upper " "bounds error\n"); goto out; @@ -313,7 +315,7 @@ unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start, u8 *equiv = EQUIV_TABLE(dfa); /* default is direct to next state */ for (; len; len--) { - pos = base[state] + equiv[(u8) *str++]; + pos = base_idx(base[state]) + equiv[(u8) *str++]; if (check[pos] == state) state = next[pos]; else @@ -322,7 +324,7 @@ unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start, } else { /* default is direct to next state */ for (; len; len--) { - pos = base[state] + (u8) *str++; + pos = base_idx(base[state]) + (u8) *str++; if (check[pos] == state) state = next[pos]; else @@ -363,7 +365,7 @@ unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start, u8 *equiv = EQUIV_TABLE(dfa); /* default is direct to next state */ while (*str) { - pos = base[state] + equiv[(u8) *str++]; + pos = base_idx(base[state]) + equiv[(u8) *str++]; if (check[pos] == state) state = next[pos]; else @@ -372,7 +374,7 @@ unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start, } else { /* default is direct to next state */ while (*str) { - pos = base[state] + (u8) *str++; + pos = base_idx(base[state]) + (u8) *str++; if (check[pos] == state) state = next[pos]; else @@ -408,14 +410,14 @@ unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state, u8 *equiv = EQUIV_TABLE(dfa); /* default is direct to next state */ - pos = base[state] + equiv[(u8) c]; + pos = base_idx(base[state]) + equiv[(u8) c]; if (check[pos] == state) state = next[pos]; else state = def[state]; } else { /* default is direct to next state */ - pos = base[state] + (u8) c; + pos = base_idx(base[state]) + (u8) c; if (check[pos] == state) state = next[pos]; else -- cgit v1.2.3 From b492d50bf597b87ab7ea1e738ec837f74b11594e Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 18 Feb 2013 16:13:34 -0800 Subject: apparmor: fix the audit type table The audit type table is missing a comma so that KILLED comes out as KILLEDAUTO. Signed-off-by: John Johansen Acked-by: Steve Beattie --- security/apparmor/audit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index 3ae28db5a64f..031d2d9dd695 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -88,7 +88,7 @@ static const char *const aa_audit_type[] = { "HINT", "STATUS", "ERROR", - "KILLED" + "KILLED", "AUTO" }; -- cgit v1.2.3 From 41d1b3e868c263e8b43dd5903a70633e05ae58a6 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 21 Feb 2013 01:14:17 -0800 Subject: apparmor: Fix smatch warning in aa_remove_profiles smatch reports error: potential NULL dereference 'ns'. this can not actually occur because it relies on aa_split_fqname setting both ns_name and name as null but ns_name will actually always have a value in this case. so remove the unnecessary if (ns_name) conditional that is resulting in the false positive further down. Signed-off-by: John Johansen --- security/apparmor/policy.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index f4ee72b44de4..0f345c4dee5f 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1156,14 +1156,12 @@ ssize_t aa_remove_profiles(char *fqname, size_t size) if (fqname[0] == ':') { char *ns_name; name = aa_split_fqname(fqname, &ns_name); - if (ns_name) { - /* released below */ - ns = aa_find_namespace(root, ns_name); - if (!ns) { - info = "namespace does not exist"; - error = -ENOENT; - goto fail; - } + /* released below */ + ns = aa_find_namespace(root, ns_name); + if (!ns) { + info = "namespace does not exist"; + error = -ENOENT; + goto fail; } } else /* released below */ -- cgit v1.2.3 From 53fe8b9961716033571d9799005bfdbbafa5162c Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 21 Feb 2013 13:25:44 -0800 Subject: apparmor: fix sparse warnings Fix a couple of warning reported by sparse Signed-off-by: John Johansen --- security/apparmor/include/file.h | 14 +++++++------- security/apparmor/lsm.c | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/security/apparmor/include/file.h b/security/apparmor/include/file.h index 967b2deda376..2c922b86bd44 100644 --- a/security/apparmor/include/file.h +++ b/security/apparmor/include/file.h @@ -186,11 +186,6 @@ static inline void aa_free_file_rules(struct aa_file_rules *rules) aa_free_domain_entries(&rules->trans); } -#define ACC_FMODE(x) (("\000\004\002\006"[(x)&O_ACCMODE]) | (((x) << 1) & 0x40)) - -/* from namei.c */ -#define MAP_OPEN_FLAGS(x) ((((x) + 1) & O_ACCMODE) ? (x) + 1 : (x)) - /** * aa_map_file_perms - map file flags to AppArmor permissions * @file: open file to map flags to AppArmor permissions @@ -199,8 +194,13 @@ static inline void aa_free_file_rules(struct aa_file_rules *rules) */ static inline u32 aa_map_file_to_perms(struct file *file) { - int flags = MAP_OPEN_FLAGS(file->f_flags); - u32 perms = ACC_FMODE(file->f_mode); + int flags = file->f_flags; + u32 perms = 0; + + if (file->f_mode & FMODE_WRITE) + perms |= MAY_WRITE; + if (file->f_mode & FMODE_READ) + perms |= MAY_READ; if ((flags & O_APPEND) && (perms & MAY_WRITE)) perms = (perms & ~MAY_WRITE) | MAY_APPEND; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index ed7e3aadba3a..10843aa5a368 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -909,8 +909,11 @@ static int __init apparmor_init(void) error = register_security(&apparmor_ops); if (error) { + struct cred *cred = (struct cred *)current->real_cred; + aa_free_task_context(cred->security); + cred->security = NULL; AA_ERROR("Unable to register AppArmor\n"); - goto set_init_cxt_out; + goto register_security_out; } /* Report that AppArmor successfully initialized */ @@ -924,9 +927,6 @@ static int __init apparmor_init(void) return error; -set_init_cxt_out: - aa_free_task_context(current->real_cred->security); - register_security_out: aa_free_root_ns(); -- cgit v1.2.3 From 214beacaa7b669473bc963af719fa359a8312ea4 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 27 Feb 2013 03:43:40 -0800 Subject: apparmor: localize getting the security context to a few macros Signed-off-by: John Johansen Acked-by: Seth Arnold --- security/apparmor/context.c | 10 +++++----- security/apparmor/domain.c | 6 +++--- security/apparmor/include/context.h | 7 +++++-- security/apparmor/lsm.c | 22 +++++++++++----------- 4 files changed, 24 insertions(+), 21 deletions(-) diff --git a/security/apparmor/context.c b/security/apparmor/context.c index 3f911afa2bb9..d5af1d15f26d 100644 --- a/security/apparmor/context.c +++ b/security/apparmor/context.c @@ -93,7 +93,7 @@ struct aa_profile *aa_get_task_profile(struct task_struct *task) */ int aa_replace_current_profile(struct aa_profile *profile) { - struct aa_task_cxt *cxt = current_cred()->security; + struct aa_task_cxt *cxt = current_cxt(); struct cred *new; BUG_ON(!profile); @@ -104,7 +104,7 @@ int aa_replace_current_profile(struct aa_profile *profile) if (!new) return -ENOMEM; - cxt = new->security; + cxt = cred_cxt(new); if (unconfined(profile) || (cxt->profile->ns != profile->ns)) /* if switching to unconfined or a different profile namespace * clear out context state @@ -136,7 +136,7 @@ int aa_set_current_onexec(struct aa_profile *profile) if (!new) return -ENOMEM; - cxt = new->security; + cxt = cred_cxt(new); aa_get_profile(profile); aa_put_profile(cxt->onexec); cxt->onexec = profile; @@ -163,7 +163,7 @@ int aa_set_current_hat(struct aa_profile *profile, u64 token) return -ENOMEM; BUG_ON(!profile); - cxt = new->security; + cxt = cred_cxt(new); if (!cxt->previous) { /* transfer refcount */ cxt->previous = cxt->profile; @@ -200,7 +200,7 @@ int aa_restore_previous_profile(u64 token) if (!new) return -ENOMEM; - cxt = new->security; + cxt = cred_cxt(new); if (cxt->token != token) { abort_creds(new); return -EACCES; diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 07fcb09b990f..01b7bd669a88 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -356,7 +356,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) if (bprm->cred_prepared) return 0; - cxt = bprm->cred->security; + cxt = cred_cxt(bprm->cred); BUG_ON(!cxt); profile = aa_get_profile(aa_newest_version(cxt->profile)); @@ -551,7 +551,7 @@ int apparmor_bprm_secureexec(struct linux_binprm *bprm) void apparmor_bprm_committing_creds(struct linux_binprm *bprm) { struct aa_profile *profile = __aa_current_profile(); - struct aa_task_cxt *new_cxt = bprm->cred->security; + struct aa_task_cxt *new_cxt = cred_cxt(bprm->cred); /* bail out if unconfined or not changing profile */ if ((new_cxt->profile == profile) || @@ -628,7 +628,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) /* released below */ cred = get_current_cred(); - cxt = cred->security; + cxt = cred_cxt(cred); profile = aa_cred_profile(cred); previous_profile = cxt->previous; diff --git a/security/apparmor/include/context.h b/security/apparmor/include/context.h index 4cecad313227..d44ba5802e3d 100644 --- a/security/apparmor/include/context.h +++ b/security/apparmor/include/context.h @@ -21,6 +21,9 @@ #include "policy.h" +#define cred_cxt(X) (X)->security +#define current_cxt() cred_cxt(current_cred()) + /* struct aa_file_cxt - the AppArmor context the file was opened in * @perms: the permission the file was opened with * @@ -93,7 +96,7 @@ struct aa_profile *aa_get_task_profile(struct task_struct *task); */ static inline struct aa_profile *aa_cred_profile(const struct cred *cred) { - struct aa_task_cxt *cxt = cred->security; + struct aa_task_cxt *cxt = cred_cxt(cred); BUG_ON(!cxt || !cxt->profile); return aa_newest_version(cxt->profile); } @@ -145,7 +148,7 @@ static inline struct aa_profile *__aa_current_profile(void) */ static inline struct aa_profile *aa_current_profile(void) { - const struct aa_task_cxt *cxt = current_cred()->security; + const struct aa_task_cxt *cxt = current_cxt(); struct aa_profile *profile; BUG_ON(!cxt || !cxt->profile); diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 10843aa5a368..2027fdf2060b 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -48,8 +48,8 @@ int apparmor_initialized __initdata; */ static void apparmor_cred_free(struct cred *cred) { - aa_free_task_context(cred->security); - cred->security = NULL; + aa_free_task_context(cred_cxt(cred)); + cred_cxt(cred) = NULL; } /* @@ -62,7 +62,7 @@ static int apparmor_cred_alloc_blank(struct cred *cred, gfp_t gfp) if (!cxt) return -ENOMEM; - cred->security = cxt; + cred_cxt(cred) = cxt; return 0; } @@ -77,8 +77,8 @@ static int apparmor_cred_prepare(struct cred *new, const struct cred *old, if (!cxt) return -ENOMEM; - aa_dup_task_context(cxt, old->security); - new->security = cxt; + aa_dup_task_context(cxt, cred_cxt(old)); + cred_cxt(new) = cxt; return 0; } @@ -87,8 +87,8 @@ static int apparmor_cred_prepare(struct cred *new, const struct cred *old, */ static void apparmor_cred_transfer(struct cred *new, const struct cred *old) { - const struct aa_task_cxt *old_cxt = old->security; - struct aa_task_cxt *new_cxt = new->security; + const struct aa_task_cxt *old_cxt = cred_cxt(old); + struct aa_task_cxt *new_cxt = cred_cxt(new); aa_dup_task_context(new_cxt, old_cxt); } @@ -507,7 +507,7 @@ static int apparmor_getprocattr(struct task_struct *task, char *name, int error = -ENOENT; /* released below */ const struct cred *cred = get_task_cred(task); - struct aa_task_cxt *cxt = cred->security; + struct aa_task_cxt *cxt = cred_cxt(cred); if (strcmp(name, "current") == 0) error = aa_getprocattr(aa_newest_version(cxt->profile), @@ -880,7 +880,7 @@ static int __init set_init_cxt(void) return -ENOMEM; cxt->profile = aa_get_profile(root_ns->unconfined); - cred->security = cxt; + cred_cxt(cred) = cxt; return 0; } @@ -910,8 +910,8 @@ static int __init apparmor_init(void) error = register_security(&apparmor_ops); if (error) { struct cred *cred = (struct cred *)current->real_cred; - aa_free_task_context(cred->security); - cred->security = NULL; + aa_free_task_context(cred_cxt(cred)); + cred_cxt(cred) = NULL; AA_ERROR("Unable to register AppArmor\n"); goto register_security_out; } -- cgit v1.2.3 From 3eea57c26e49a5add4c053a031cc2a1977b7c48e Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 27 Feb 2013 03:44:40 -0800 Subject: apparmor: fix setprocattr arg processing for onexec the exec file isn't processing its command arg. It should only set be responding to a command of exec. Also cleanup setprocattr some more while we are at it. Signed-off-by: John Johansen --- security/apparmor/lsm.c | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 2027fdf2060b..2e2a0dd4a73f 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -529,6 +529,8 @@ static int apparmor_getprocattr(struct task_struct *task, char *name, static int apparmor_setprocattr(struct task_struct *task, char *name, void *value, size_t size) { + struct common_audit_data sa; + struct apparmor_audit_data aad = {0,}; char *command, *args = value; size_t arg_size; int error; @@ -572,28 +574,31 @@ static int apparmor_setprocattr(struct task_struct *task, char *name, } else if (strcmp(command, "permprofile") == 0) { error = aa_setprocattr_changeprofile(args, !AA_ONEXEC, AA_DO_TEST); - } else { - struct common_audit_data sa; - struct apparmor_audit_data aad = {0,}; - sa.type = LSM_AUDIT_DATA_NONE; - sa.aad = &aad; - aad.op = OP_SETPROCATTR; - aad.info = name; - aad.error = -EINVAL; - return aa_audit(AUDIT_APPARMOR_DENIED, - __aa_current_profile(), GFP_KERNEL, - &sa, NULL); - } + } else + goto fail; } else if (strcmp(name, "exec") == 0) { - error = aa_setprocattr_changeprofile(args, AA_ONEXEC, - !AA_DO_TEST); - } else { + if (strcmp(command, "exec") == 0) + error = aa_setprocattr_changeprofile(args, AA_ONEXEC, + !AA_DO_TEST); + else + goto fail; + } else /* only support the "current" and "exec" process attributes */ return -EINVAL; - } + if (!error) error = size; return error; + +fail: + sa.type = LSM_AUDIT_DATA_NONE; + sa.aad = &aad; + aad.profile = aa_current_profile(); + aad.op = OP_SETPROCATTR; + aad.info = name; + aad.error = -EINVAL; + aa_audit_msg(AUDIT_APPARMOR_DENIED, &sa, NULL); + return -EINVAL; } static int apparmor_task_setrlimit(struct task_struct *task, -- cgit v1.2.3 From 2654bfbc2bd0e1e64f0b257c21da23f6cec32c6c Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 27 Feb 2013 03:45:05 -0800 Subject: apparmor: fix fully qualified name parsing currently apparmor name parsing is only correctly handling :: but ::// is also a valid form and what is exported to userspace. Signed-off-by: John Johansen --- security/apparmor/lib.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index d6e1f2148398..d40bc592180d 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -45,8 +45,10 @@ char *aa_split_fqname(char *fqname, char **ns_name) *ns_name = skip_spaces(&name[1]); if (split) { /* overwrite ':' with \0 */ - *split = 0; - name = skip_spaces(split + 1); + *split++ = 0; + if (strncmp(split, "//", 2) == 0) + split += 2; + name = skip_spaces(split); } else /* a ns name without a following profile is allowed */ name = NULL; -- cgit v1.2.3 From 60c4ae101fdebdd1451e93f08161af3d78725cff Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 29 Apr 2013 18:29:19 +0200 Subject: drm/i915: put the right cpu_transcoder into pipe_config for hw state readout This hack is getting a bit messy, but this plugs the leak for now until we have the cpu_transcoder properly pipe_config'ed. Cc: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index dacfc6c90550..90bed0ab0fe2 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7981,6 +7981,7 @@ intel_modeset_check_state(struct drm_device *dev) "(expected %i, found %i)\n", enabled, crtc->base.enabled); memset(&pipe_config, 0, sizeof(pipe_config)); + pipe_config.cpu_transcoder = crtc->config.cpu_transcoder; active = dev_priv->display.get_pipe_config(crtc, &pipe_config); WARN(crtc->active != active, -- cgit v1.2.3 From af13188a1a6623fc8b4b6c42178046fb80f8b1d0 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 19 Feb 2013 17:45:00 +0100 Subject: drm/i915: force bpp for eDP panels We've had our fair share of woes already which showed that we can't rely on the bpc limits in the EDID for eDP panels without risking black screens. So now we limit the depth by what the BIOS recommends in the VBT: commit 2f4f649a69a9eb51f6e98130e19dd90a260a4145 Author: Jani Nikula Date: Mon Nov 12 14:33:44 2012 +0200 drm/i915: do not ignore eDP bpc settings from vbt But that's not enough, since at least the panel on my ASUS Zenbook Prime here is also unhappy if the bpc is too low. Hence just take the firmware value and dither to get what flimsy panels want. Like before we ensure that we don't change the bpp if the firmware doesn't provide a value, see commit 9a30a61f3516871c5c638fd7c025fbaa11ddf7fe Author: Jani Nikula Date: Mon Nov 12 14:33:45 2012 +0200 drm/i915: do not default to 18 bpp for eDP if missing from VBT v2: Apparently there are some horribly broken eDP panels around which only work if the DP link is set up as if we want to driver a 24bpp mode, but still only work if the data is feed at 18bpp. See commit 57c219633275c7e7413f8bc7be250dc092887458 Author: Daniel Vetter Date: Thu Apr 4 17:19:37 2013 +0200 drm/i915: revert eDP bpp clamping code changes for the gory details. Adjust the patch accordingly and update all the relevant comments. v3: Give up on the cargo-culting v2 attempt and just enfore the edp bpp value if it's there. Broken panels be damned! Cc: Jani Nikula Cc: Paulo Zanoni Reviewed-by: Jani Nikula Tested-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 9ac034ee0ddd..82cd9ac16c4a 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -748,6 +748,18 @@ intel_dp_compute_config(struct intel_encoder *encoder, /* Walk through all bpp values. Luckily they're all nicely spaced with 2 * bpc in between. */ bpp = min_t(int, 8*3, pipe_config->pipe_bpp); + + /* + * eDP panels are really fickle, try to enfore the bpp the firmware + * recomments. This means we'll up-dither 16bpp framebuffers on + * high-depth panels. + */ + if (is_edp(intel_dp) && dev_priv->edp.bpp) { + DRM_DEBUG_KMS("forcing bpp for eDP panel to BIOS-provided %i\n", + dev_priv->edp.bpp); + bpp = dev_priv->edp.bpp; + } + for (; bpp >= 6*3; bpp -= 2*3) { mode_rate = intel_dp_link_required(target_clock, bpp); @@ -797,18 +809,6 @@ found: target_clock, adjusted_mode->clock, &pipe_config->dp_m_n); - /* - * XXX: We have a strange regression where using the vbt edp bpp value - * for the link bw computation results in black screens, the panel only - * works when we do the computation at the usual 24bpp (but still - * requires us to use 18bpp). Until that's fully debugged, stay - * bug-for-bug compatible with the old code. - */ - if (is_edp(intel_dp) && dev_priv->edp.bpp) { - DRM_DEBUG_KMS("clamping display bpc (was %d) to eDP (%d)\n", - bpp, dev_priv->edp.bpp); - bpp = min_t(int, bpp, dev_priv->edp.bpp); - } pipe_config->pipe_bpp = bpp; intel_dp_set_clock(encoder, pipe_config, intel_dp->link_bw); -- cgit v1.2.3 From 6ff93609b1cf77121ec61e8fe197d683b1702cd9 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 19 Apr 2013 11:24:36 +0200 Subject: drm/i915: drop adjusted_mode from *_set_pipeconf functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit They can get at the adjusted mode through intel_crtc->config. Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 90bed0ab0fe2..adc56bd4828e 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5168,8 +5168,7 @@ static int ironlake_get_refclk(struct drm_crtc *crtc) return 120000; } -static void ironlake_set_pipeconf(struct drm_crtc *crtc, - struct drm_display_mode *adjusted_mode) +static void ironlake_set_pipeconf(struct drm_crtc *crtc) { struct drm_i915_private *dev_priv = crtc->dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); @@ -5202,7 +5201,7 @@ static void ironlake_set_pipeconf(struct drm_crtc *crtc, val |= (PIPECONF_DITHER_EN | PIPECONF_DITHER_TYPE_SP); val &= ~PIPECONF_INTERLACE_MASK; - if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) + if (intel_crtc->config.adjusted_mode.flags & DRM_MODE_FLAG_INTERLACE) val |= PIPECONF_INTERLACED_ILK; else val |= PIPECONF_PROGRESSIVE; @@ -5280,8 +5279,7 @@ static void intel_set_pipe_csc(struct drm_crtc *crtc) } } -static void haswell_set_pipeconf(struct drm_crtc *crtc, - struct drm_display_mode *adjusted_mode) +static void haswell_set_pipeconf(struct drm_crtc *crtc) { struct drm_i915_private *dev_priv = crtc->dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); @@ -5295,7 +5293,7 @@ static void haswell_set_pipeconf(struct drm_crtc *crtc, val |= (PIPECONF_DITHER_EN | PIPECONF_DITHER_TYPE_SP); val &= ~PIPECONF_INTERLACE_MASK_HSW; - if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) + if (intel_crtc->config.adjusted_mode.flags & DRM_MODE_FLAG_INTERLACE) val |= PIPECONF_INTERLACED_ILK; else val |= PIPECONF_PROGRESSIVE; @@ -5753,7 +5751,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, fdi_config_ok = ironlake_check_fdi_lanes(intel_crtc); - ironlake_set_pipeconf(crtc, adjusted_mode); + ironlake_set_pipeconf(crtc); /* Set up the display plane register */ I915_WRITE(DSPCNTR(plane), DISPPLANE_GAMMA_ENABLE); @@ -5877,7 +5875,7 @@ static int haswell_crtc_mode_set(struct drm_crtc *crtc, if (intel_crtc->config.has_pch_encoder) ironlake_fdi_set_m_n(crtc); - haswell_set_pipeconf(crtc, adjusted_mode); + haswell_set_pipeconf(crtc); intel_set_pipe_csc(crtc); -- cgit v1.2.3 From ff9ce46ed6878d6be08660f7d75897d500a4fe9e Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 24 Apr 2013 14:57:17 +0200 Subject: drm/i915: implement high-bpc + pipeconf-dither support for g4x/vlv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current code is rather ... ugly. The only thing it managed to pull off is getting 6bpc on DP working on g4x. Then someone added another custom hack for 6bpc eDP on vlv. Fix up this entire mess by properly implementing the PIPECONF-based dither/bpc controls on g4x/vlv. Note that compared to pch based platforms g4x/vlv don't support 12bpc modes. g4x is already caught, extend the check for vlv. The other fixup is to restrict the lvds-specific dithering to early gen4 devices - g4x should use the pipeconf dither controls. Note that on gen2/3 the dither control is in the panel fitter even. v2: Don't enable dithering when the pipe is in 10 bpc mode. Quoting from Bspec "PIPEACONF - Pipe A Configuration Register, bit 4": "Programming note: Dithering should only be enabled for 8 bpc or 6 bpc." v3: Actually drop the old ugly dither code. v4: Explain in a short comment why g4x/vlv shouldn't dither for 30 bpp pipes (Jesse). v5: Also clear the dither type correctly as spotted by Ville. v6: As Ville pointed out we need to indeed set the dithering both in the pipeconf register (for DP outputs) and in the LVDS port register (for LVDS ouputs). Otherwise LVDS panel will not get properly dithered. The old patch got away with this since it forgot to clear the LVDS dither bit ... v7: Remove redundant BPC_MASK clearing, spotted by Ville. Cc: Ville Syrjälä Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index adc56bd4828e..983822e10533 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4622,22 +4622,29 @@ static void i9xx_set_pipeconf(struct intel_crtc *intel_crtc) pipeconf &= ~PIPECONF_DOUBLE_WIDE; } - /* default to 8bpc */ - pipeconf &= ~(PIPECONF_BPC_MASK | PIPECONF_DITHER_EN); - if (intel_crtc->config.has_dp_encoder) { - if (intel_crtc->config.dither) { - pipeconf |= PIPECONF_6BPC | - PIPECONF_DITHER_EN | + /* only g4x and later have fancy bpc/dither controls */ + if (IS_G4X(dev) || IS_VALLEYVIEW(dev)) { + pipeconf &= ~(PIPECONF_BPC_MASK | + PIPECONF_DITHER_EN | PIPECONF_DITHER_TYPE_MASK); + + /* Bspec claims that we can't use dithering for 30bpp pipes. */ + if (intel_crtc->config.dither && intel_crtc->config.pipe_bpp != 30) + pipeconf |= PIPECONF_DITHER_EN | PIPECONF_DITHER_TYPE_SP; - } - } - if (IS_VALLEYVIEW(dev) && intel_pipe_has_type(&intel_crtc->base, - INTEL_OUTPUT_EDP)) { - if (intel_crtc->config.dither) { - pipeconf |= PIPECONF_6BPC | - PIPECONF_ENABLE | - I965_PIPECONF_ACTIVE; + switch (intel_crtc->config.pipe_bpp) { + case 18: + pipeconf |= PIPECONF_6BPC; + break; + case 24: + pipeconf |= PIPECONF_8BPC; + break; + case 30: + pipeconf |= PIPECONF_10BPC; + break; + default: + /* Case prevented by intel_choose_pipe_bpp_dither. */ + BUG(); } } -- cgit v1.2.3 From 52541e30339d932382ab9c0c1d1bacc8dacc541e Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 19 Apr 2013 11:24:38 +0200 Subject: drm/i915: allow high-bpc modes on DP Totally untested due to lack of screens supporting more than 8bpc. But now we should have closed all holes in our bpp handling, so this should be safe. The last missing piece was 10bpc support for g4x/vlv, since we directly use the pipe bpp to feed the display link (and anyway, only the cpt has any means to have a pipe bpp != the display link bpp). Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 82cd9ac16c4a..7840b4d22774 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -747,7 +747,7 @@ intel_dp_compute_config(struct intel_encoder *encoder, /* Walk through all bpp values. Luckily they're all nicely spaced with 2 * bpc in between. */ - bpp = min_t(int, 8*3, pipe_config->pipe_bpp); + bpp = pipe_config->pipe_bpp; /* * eDP panels are really fickle, try to enfore the bpp the firmware -- cgit v1.2.3 From 33d29b145305641f2c693b759cac468e0c87ab4d Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 13 Feb 2013 18:04:45 +0100 Subject: drm/i915: move intel_crtc->fdi_lanes to pipe_config We need this for two reasons: - Correct handling of shared fdi lanes on ivb with fastboot. - Handling fdi link bw limits when we only have two fdi lanes by dithering down a bit. Just search&replace in this patch, no functional change at all. Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ddi.c | 7 ++++--- drivers/gpu/drm/i915/intel_display.c | 36 ++++++++++++++++++------------------ drivers/gpu/drm/i915/intel_drv.h | 4 +++- 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 0d7cf317b73f..c9c4741020df 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -181,7 +181,8 @@ void hsw_fdi_link_train(struct drm_crtc *crtc) /* Enable the PCH Receiver FDI PLL */ rx_ctl_val = dev_priv->fdi_rx_config | FDI_RX_ENHANCE_FRAME_ENABLE | - FDI_RX_PLL_ENABLE | ((intel_crtc->fdi_lanes - 1) << 19); + FDI_RX_PLL_ENABLE | + ((intel_crtc->config.fdi_lanes - 1) << 19); I915_WRITE(_FDI_RXA_CTL, rx_ctl_val); POSTING_READ(_FDI_RXA_CTL); udelay(220); @@ -209,7 +210,7 @@ void hsw_fdi_link_train(struct drm_crtc *crtc) * port reversal bit */ I915_WRITE(DDI_BUF_CTL(PORT_E), DDI_BUF_CTL_ENABLE | - ((intel_crtc->fdi_lanes - 1) << 1) | + ((intel_crtc->config.fdi_lanes - 1) << 1) | hsw_ddi_buf_ctl_values[i / 2]); POSTING_READ(DDI_BUF_CTL(PORT_E)); @@ -1022,7 +1023,7 @@ void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc) } else if (type == INTEL_OUTPUT_ANALOG) { temp |= TRANS_DDI_MODE_SELECT_FDI; - temp |= (intel_crtc->fdi_lanes - 1) << 1; + temp |= (intel_crtc->config.fdi_lanes - 1) << 1; } else if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) { diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 983822e10533..a3799779d46f 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2420,7 +2420,7 @@ static void ironlake_fdi_link_train(struct drm_crtc *crtc) reg = FDI_TX_CTL(pipe); temp = I915_READ(reg); temp &= ~(7 << 19); - temp |= (intel_crtc->fdi_lanes - 1) << 19; + temp |= (intel_crtc->config.fdi_lanes - 1) << 19; temp &= ~FDI_LINK_TRAIN_NONE; temp |= FDI_LINK_TRAIN_PATTERN_1; I915_WRITE(reg, temp | FDI_TX_ENABLE); @@ -2518,7 +2518,7 @@ static void gen6_fdi_link_train(struct drm_crtc *crtc) reg = FDI_TX_CTL(pipe); temp = I915_READ(reg); temp &= ~(7 << 19); - temp |= (intel_crtc->fdi_lanes - 1) << 19; + temp |= (intel_crtc->config.fdi_lanes - 1) << 19; temp &= ~FDI_LINK_TRAIN_NONE; temp |= FDI_LINK_TRAIN_PATTERN_1; temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; @@ -2653,7 +2653,7 @@ static void ivb_manual_fdi_link_train(struct drm_crtc *crtc) reg = FDI_TX_CTL(pipe); temp = I915_READ(reg); temp &= ~(7 << 19); - temp |= (intel_crtc->fdi_lanes - 1) << 19; + temp |= (intel_crtc->config.fdi_lanes - 1) << 19; temp &= ~(FDI_LINK_TRAIN_AUTO | FDI_LINK_TRAIN_NONE_IVB); temp |= FDI_LINK_TRAIN_PATTERN_1_IVB; temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; @@ -2755,7 +2755,7 @@ static void ironlake_fdi_pll_enable(struct intel_crtc *intel_crtc) reg = FDI_RX_CTL(pipe); temp = I915_READ(reg); temp &= ~((0x7 << 19) | (0x7 << 16)); - temp |= (intel_crtc->fdi_lanes - 1) << 19; + temp |= (intel_crtc->config.fdi_lanes - 1) << 19; temp |= (I915_READ(PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11; I915_WRITE(reg, temp | FDI_RX_PLL_ENABLE); @@ -5398,12 +5398,12 @@ static bool ironlake_check_fdi_lanes(struct intel_crtc *intel_crtc) to_intel_crtc(dev_priv->pipe_to_crtc_mapping[PIPE_B]); DRM_DEBUG_KMS("checking fdi config on pipe %c, lanes %i\n", - pipe_name(intel_crtc->pipe), intel_crtc->fdi_lanes); - if (intel_crtc->fdi_lanes > 4) { + pipe_name(intel_crtc->pipe), intel_crtc->config.fdi_lanes); + if (intel_crtc->config.fdi_lanes > 4) { DRM_DEBUG_KMS("invalid fdi lane config on pipe %c: %i lanes\n", - pipe_name(intel_crtc->pipe), intel_crtc->fdi_lanes); + pipe_name(intel_crtc->pipe), intel_crtc->config.fdi_lanes); /* Clamp lanes to avoid programming the hw with bogus values. */ - intel_crtc->fdi_lanes = 4; + intel_crtc->config.fdi_lanes = 4; return false; } @@ -5416,28 +5416,28 @@ static bool ironlake_check_fdi_lanes(struct intel_crtc *intel_crtc) return true; case PIPE_B: if (dev_priv->pipe_to_crtc_mapping[PIPE_C]->enabled && - intel_crtc->fdi_lanes > 2) { + intel_crtc->config.fdi_lanes > 2) { DRM_DEBUG_KMS("invalid shared fdi lane config on pipe %c: %i lanes\n", - pipe_name(intel_crtc->pipe), intel_crtc->fdi_lanes); + pipe_name(intel_crtc->pipe), intel_crtc->config.fdi_lanes); /* Clamp lanes to avoid programming the hw with bogus values. */ - intel_crtc->fdi_lanes = 2; + intel_crtc->config.fdi_lanes = 2; return false; } - if (intel_crtc->fdi_lanes > 2) + if (intel_crtc->config.fdi_lanes > 2) WARN_ON(I915_READ(SOUTH_CHICKEN1) & FDI_BC_BIFURCATION_SELECT); else cpt_enable_fdi_bc_bifurcation(dev); return true; case PIPE_C: - if (!pipe_B_crtc->base.enabled || pipe_B_crtc->fdi_lanes <= 2) { - if (intel_crtc->fdi_lanes > 2) { + if (!pipe_B_crtc->base.enabled || pipe_B_crtc->config.fdi_lanes <= 2) { + if (intel_crtc->config.fdi_lanes > 2) { DRM_DEBUG_KMS("invalid shared fdi lane config on pipe %c: %i lanes\n", - pipe_name(intel_crtc->pipe), intel_crtc->fdi_lanes); + pipe_name(intel_crtc->pipe), intel_crtc->config.fdi_lanes); /* Clamp lanes to avoid programming the hw with bogus values. */ - intel_crtc->fdi_lanes = 2; + intel_crtc->config.fdi_lanes = 2; return false; } @@ -5525,7 +5525,7 @@ static void ironlake_fdi_set_m_n(struct drm_crtc *crtc) lane = ironlake_get_lanes_required(target_clock, link_bw, intel_crtc->config.pipe_bpp); - intel_crtc->fdi_lanes = lane; + intel_crtc->config.fdi_lanes = lane; if (intel_crtc->config.pixel_multiplier > 1) link_bw *= intel_crtc->config.pixel_multiplier; @@ -5752,7 +5752,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, /* Note, this also computes intel_crtc->fdi_lanes which is used below in * ironlake_check_fdi_lanes. */ - intel_crtc->fdi_lanes = 0; + intel_crtc->config.fdi_lanes = 0; if (intel_crtc->config.has_pch_encoder) ironlake_fdi_set_m_n(crtc); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index a8c696014967..cc075a312ea3 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -249,6 +249,9 @@ struct intel_crtc_config { u32 pos; u32 size; } pch_pfit; + + /* FDI lanes used, only valid if has_pch_encoder is set. */ + int fdi_lanes; }; struct intel_crtc { @@ -267,7 +270,6 @@ struct intel_crtc { bool lowfreq_avail; struct intel_overlay *overlay; struct intel_unpin_work *unpin_work; - int fdi_lanes; atomic_t unpin_work_count; -- cgit v1.2.3 From 627eb5a318a6caca2145d3c7195b084c59b291d9 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 29 Apr 2013 19:33:42 +0200 Subject: drm/i915: hw state readout support for pipe_config->fdi_lanes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v2: Introduce some nice #defines for the FDI lane width fields and put them to good use. Suggested by Ville. v3: Fixup the mask vs. shift copy&pasta fail Imre Deak spotted, and use the shift #define also in the mask. Cc: Imre Deak Cc: Ville Syrjälä Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 11 +++-------- drivers/gpu/drm/i915/intel_ddi.c | 2 +- drivers/gpu/drm/i915/intel_display.c | 38 ++++++++++++++++++++++++++---------- 3 files changed, 32 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index e79669fa9fe2..2d27960d6dc1 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -4146,10 +4146,9 @@ #define FDI_LINK_TRAIN_600MV_3_5DB_SNB_B (0x39<<22) #define FDI_LINK_TRAIN_800MV_0DB_SNB_B (0x38<<22) #define FDI_LINK_TRAIN_VOL_EMP_MASK (0x3f<<22) -#define FDI_DP_PORT_WIDTH_X1 (0<<19) -#define FDI_DP_PORT_WIDTH_X2 (1<<19) -#define FDI_DP_PORT_WIDTH_X3 (2<<19) -#define FDI_DP_PORT_WIDTH_X4 (3<<19) +#define FDI_DP_PORT_WIDTH_SHIFT 19 +#define FDI_DP_PORT_WIDTH_MASK (7 << FDI_DP_PORT_WIDTH_SHIFT) +#define FDI_DP_PORT_WIDTH(width) (((width) - 1) << FDI_DP_PORT_WIDTH_SHIFT) #define FDI_TX_ENHANCE_FRAME_ENABLE (1<<18) /* Ironlake: hardwired to 1 */ #define FDI_TX_PLL_ENABLE (1<<14) @@ -4174,7 +4173,6 @@ /* train, dp width same as FDI_TX */ #define FDI_FS_ERRC_ENABLE (1<<27) #define FDI_FE_ERRC_ENABLE (1<<26) -#define FDI_DP_PORT_WIDTH_X8 (7<<19) #define FDI_RX_POLARITY_REVERSED_LPT (1<<16) #define FDI_8BPC (0<<16) #define FDI_10BPC (1<<16) @@ -4196,9 +4194,6 @@ #define FDI_LINK_TRAIN_PATTERN_IDLE_CPT (2<<8) #define FDI_LINK_TRAIN_NORMAL_CPT (3<<8) #define FDI_LINK_TRAIN_PATTERN_MASK_CPT (3<<8) -/* LPT */ -#define FDI_PORT_WIDTH_2X_LPT (1<<19) -#define FDI_PORT_WIDTH_1X_LPT (0<<19) #define _FDI_RXA_MISC 0xf0010 #define _FDI_RXB_MISC 0xf1010 diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index c9c4741020df..72941f94c830 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -182,7 +182,7 @@ void hsw_fdi_link_train(struct drm_crtc *crtc) /* Enable the PCH Receiver FDI PLL */ rx_ctl_val = dev_priv->fdi_rx_config | FDI_RX_ENHANCE_FRAME_ENABLE | FDI_RX_PLL_ENABLE | - ((intel_crtc->config.fdi_lanes - 1) << 19); + FDI_DP_PORT_WIDTH(intel_crtc->config.fdi_lanes); I915_WRITE(_FDI_RXA_CTL, rx_ctl_val); POSTING_READ(_FDI_RXA_CTL); udelay(220); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index a3799779d46f..63975c7f9668 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2419,8 +2419,8 @@ static void ironlake_fdi_link_train(struct drm_crtc *crtc) /* enable CPU FDI TX and PCH FDI RX */ reg = FDI_TX_CTL(pipe); temp = I915_READ(reg); - temp &= ~(7 << 19); - temp |= (intel_crtc->config.fdi_lanes - 1) << 19; + temp &= ~FDI_DP_PORT_WIDTH_MASK; + temp |= FDI_DP_PORT_WIDTH(intel_crtc->config.fdi_lanes); temp &= ~FDI_LINK_TRAIN_NONE; temp |= FDI_LINK_TRAIN_PATTERN_1; I915_WRITE(reg, temp | FDI_TX_ENABLE); @@ -2517,8 +2517,8 @@ static void gen6_fdi_link_train(struct drm_crtc *crtc) /* enable CPU FDI TX and PCH FDI RX */ reg = FDI_TX_CTL(pipe); temp = I915_READ(reg); - temp &= ~(7 << 19); - temp |= (intel_crtc->config.fdi_lanes - 1) << 19; + temp &= ~FDI_DP_PORT_WIDTH_MASK; + temp |= FDI_DP_PORT_WIDTH(intel_crtc->config.fdi_lanes); temp &= ~FDI_LINK_TRAIN_NONE; temp |= FDI_LINK_TRAIN_PATTERN_1; temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; @@ -2652,8 +2652,8 @@ static void ivb_manual_fdi_link_train(struct drm_crtc *crtc) /* enable CPU FDI TX and PCH FDI RX */ reg = FDI_TX_CTL(pipe); temp = I915_READ(reg); - temp &= ~(7 << 19); - temp |= (intel_crtc->config.fdi_lanes - 1) << 19; + temp &= ~FDI_DP_PORT_WIDTH_MASK; + temp |= FDI_DP_PORT_WIDTH(intel_crtc->config.fdi_lanes); temp &= ~(FDI_LINK_TRAIN_AUTO | FDI_LINK_TRAIN_NONE_IVB); temp |= FDI_LINK_TRAIN_PATTERN_1_IVB; temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; @@ -2754,8 +2754,8 @@ static void ironlake_fdi_pll_enable(struct intel_crtc *intel_crtc) /* 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->config.fdi_lanes - 1) << 19; + temp &= ~(FDI_DP_PORT_WIDTH_MASK | (0x7 << 16)); + temp |= FDI_DP_PORT_WIDTH(intel_crtc->config.fdi_lanes); temp |= (I915_READ(PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11; I915_WRITE(reg, temp | FDI_RX_PLL_ENABLE); @@ -5784,9 +5784,14 @@ static bool ironlake_get_pipe_config(struct intel_crtc *crtc, if (!(tmp & PIPECONF_ENABLE)) return false; - if (I915_READ(TRANSCONF(crtc->pipe)) & TRANS_ENABLE) + if (I915_READ(TRANSCONF(crtc->pipe)) & TRANS_ENABLE) { pipe_config->has_pch_encoder = true; + tmp = I915_READ(FDI_RX_CTL(crtc->pipe)); + pipe_config->fdi_lanes = ((FDI_DP_PORT_WIDTH_MASK & tmp) >> + FDI_DP_PORT_WIDTH_SHIFT) + 1; + } + return true; } @@ -5922,9 +5927,14 @@ static bool haswell_get_pipe_config(struct intel_crtc *crtc, */ tmp = I915_READ(TRANS_DDI_FUNC_CTL(cpu_transcoder)); if ((tmp & TRANS_DDI_PORT_MASK) == TRANS_DDI_SELECT_PORT(PORT_E) && - I915_READ(TRANSCONF(PIPE_A)) & TRANS_ENABLE) + I915_READ(TRANSCONF(PIPE_A)) & TRANS_ENABLE) { pipe_config->has_pch_encoder = true; + tmp = I915_READ(FDI_RX_CTL(PIPE_A)); + pipe_config->fdi_lanes = ((FDI_DP_PORT_WIDTH_MASK & tmp) >> + FDI_DP_PORT_WIDTH_SHIFT) + 1; + } + return true; } @@ -7886,6 +7896,14 @@ intel_pipe_config_compare(struct intel_crtc_config *current_config, return false; } + if (current_config->fdi_lanes != pipe_config->fdi_lanes) { + DRM_ERROR("mismatch in fdi_lanes " + "(expected %i, found %i)\n", + current_config->fdi_lanes, + pipe_config->fdi_lanes); + return false; + } + return true; } -- cgit v1.2.3 From ca3a0ff80faa7ee043fd615c9d9394757e2acb09 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 14 Feb 2013 16:54:22 +0100 Subject: drm/i915: split up fdi_set_m_n into computation and hw setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit And also move the computed m_n values into the pipe_config. This is a prep step to move the fdi state computation completely into the prepare phase of the modeset sequence. Which will allow us to handle fdi link bw constraints in a better way. Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 26 +++++++++++++++----------- drivers/gpu/drm/i915/intel_drv.h | 3 ++- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 63975c7f9668..0f1e17a7b757 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5499,13 +5499,11 @@ void intel_cpu_transcoder_set_m_n(struct intel_crtc *crtc, } } -static void ironlake_fdi_set_m_n(struct drm_crtc *crtc) +static void ironlake_fdi_compute_config(struct intel_crtc *intel_crtc) { - struct drm_device *dev = crtc->dev; - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct drm_device *dev = intel_crtc->base.dev; struct drm_display_mode *adjusted_mode = &intel_crtc->config.adjusted_mode; - struct intel_link_m_n m_n = {0}; int target_clock, lane, link_bw; /* FDI is a binary signal running at ~2.7GHz, encoding @@ -5530,9 +5528,7 @@ static void ironlake_fdi_set_m_n(struct drm_crtc *crtc) if (intel_crtc->config.pixel_multiplier > 1) link_bw *= intel_crtc->config.pixel_multiplier; intel_link_compute_m_n(intel_crtc->config.pipe_bpp, lane, target_clock, - link_bw, &m_n); - - intel_cpu_transcoder_set_m_n(intel_crtc, &m_n); + link_bw, &intel_crtc->config.fdi_m_n); } static bool ironlake_needs_fb_cb_tune(struct dpll *dpll, int factor) @@ -5753,8 +5749,12 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, /* Note, this also computes intel_crtc->fdi_lanes which is used below in * ironlake_check_fdi_lanes. */ intel_crtc->config.fdi_lanes = 0; - if (intel_crtc->config.has_pch_encoder) - ironlake_fdi_set_m_n(crtc); + if (intel_crtc->config.has_pch_encoder) { + ironlake_fdi_compute_config(intel_crtc); + + intel_cpu_transcoder_set_m_n(intel_crtc, + &intel_crtc->config.fdi_m_n); + } fdi_config_ok = ironlake_check_fdi_lanes(intel_crtc); @@ -5884,8 +5884,12 @@ static int haswell_crtc_mode_set(struct drm_crtc *crtc, intel_set_pipe_timings(intel_crtc, mode, adjusted_mode); - if (intel_crtc->config.has_pch_encoder) - ironlake_fdi_set_m_n(crtc); + if (intel_crtc->config.has_pch_encoder) { + ironlake_fdi_compute_config(intel_crtc); + + intel_cpu_transcoder_set_m_n(intel_crtc, + &intel_crtc->config.fdi_m_n); + } haswell_set_pipeconf(crtc); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index cc075a312ea3..48f309eaf7aa 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -250,8 +250,9 @@ struct intel_crtc_config { u32 size; } pch_pfit; - /* FDI lanes used, only valid if has_pch_encoder is set. */ + /* FDI configuration, only valid if has_pch_encoder is set. */ int fdi_lanes; + struct intel_link_m_n fdi_m_n; }; struct intel_crtc { -- cgit v1.2.3 From 877d48d5f7d4f90d3bd5fb5422e1a88d39e1d3b3 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 19 Apr 2013 11:24:43 +0200 Subject: drm/i915: compute fdi lane config earlier Now that it's split up, we can easily move it around and precompute the fdi lane configuration. Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 71 +++++++++++++++++------------------- 1 file changed, 34 insertions(+), 37 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 0f1e17a7b757..a7674c313e03 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3974,6 +3974,37 @@ bool intel_connector_get_hw_state(struct intel_connector *connector) return encoder->get_hw_state(encoder, &pipe); } +static void ironlake_fdi_compute_config(struct drm_device *dev, + struct intel_crtc_config *pipe_config) +{ + struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode; + int target_clock, lane, link_bw; + + /* 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; + + if (pipe_config->pixel_target_clock) + target_clock = pipe_config->pixel_target_clock; + else + target_clock = adjusted_mode->clock; + + lane = ironlake_get_lanes_required(target_clock, link_bw, + pipe_config->pipe_bpp); + + pipe_config->fdi_lanes = lane; + + if (pipe_config->pixel_multiplier > 1) + link_bw *= pipe_config->pixel_multiplier; + intel_link_compute_m_n(pipe_config->pipe_bpp, lane, target_clock, + link_bw, &pipe_config->fdi_m_n); +} + static bool intel_crtc_compute_config(struct drm_crtc *crtc, struct intel_crtc_config *pipe_config) { @@ -4008,6 +4039,9 @@ static bool intel_crtc_compute_config(struct drm_crtc *crtc, pipe_config->pipe_bpp = 8*3; } + if (pipe_config->has_pch_encoder) + ironlake_fdi_compute_config(dev, pipe_config); + return true; } @@ -5499,38 +5533,6 @@ void intel_cpu_transcoder_set_m_n(struct intel_crtc *crtc, } } -static void ironlake_fdi_compute_config(struct intel_crtc *intel_crtc) -{ - struct drm_device *dev = intel_crtc->base.dev; - struct drm_display_mode *adjusted_mode = - &intel_crtc->config.adjusted_mode; - int target_clock, lane, link_bw; - - /* 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; - - if (intel_crtc->config.pixel_target_clock) - target_clock = intel_crtc->config.pixel_target_clock; - else - target_clock = adjusted_mode->clock; - - lane = ironlake_get_lanes_required(target_clock, link_bw, - intel_crtc->config.pipe_bpp); - - intel_crtc->config.fdi_lanes = lane; - - if (intel_crtc->config.pixel_multiplier > 1) - link_bw *= intel_crtc->config.pixel_multiplier; - intel_link_compute_m_n(intel_crtc->config.pipe_bpp, lane, target_clock, - link_bw, &intel_crtc->config.fdi_m_n); -} - static bool ironlake_needs_fb_cb_tune(struct dpll *dpll, int factor) { return i9xx_dpll_compute_m(dpll) < factor * dpll->n; @@ -5748,10 +5750,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, /* Note, this also computes intel_crtc->fdi_lanes which is used below in * ironlake_check_fdi_lanes. */ - intel_crtc->config.fdi_lanes = 0; if (intel_crtc->config.has_pch_encoder) { - ironlake_fdi_compute_config(intel_crtc); - intel_cpu_transcoder_set_m_n(intel_crtc, &intel_crtc->config.fdi_m_n); } @@ -5885,8 +5884,6 @@ static int haswell_crtc_mode_set(struct drm_crtc *crtc, intel_set_pipe_timings(intel_crtc, mode, adjusted_mode); if (intel_crtc->config.has_pch_encoder) { - ironlake_fdi_compute_config(intel_crtc); - intel_cpu_transcoder_set_m_n(intel_crtc, &intel_crtc->config.fdi_m_n); } -- cgit v1.2.3 From ebfd86fda69bbe4d7b15e0c31e86a3069cf69085 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 19 Apr 2013 11:24:44 +0200 Subject: drm/i915: Split up ironlake_check_fdi_lanes Again in preparation to move the configuration checks into the pipe_config computation stage of the modeset sequence. Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index a7674c313e03..1a3fdba4e74d 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5459,11 +5459,6 @@ static bool ironlake_check_fdi_lanes(struct intel_crtc *intel_crtc) return false; } - if (intel_crtc->config.fdi_lanes > 2) - WARN_ON(I915_READ(SOUTH_CHICKEN1) & FDI_BC_BIFURCATION_SELECT); - else - cpt_enable_fdi_bc_bifurcation(dev); - return true; case PIPE_C: if (!pipe_B_crtc->base.enabled || pipe_B_crtc->config.fdi_lanes <= 2) { @@ -5480,9 +5475,31 @@ static bool ironlake_check_fdi_lanes(struct intel_crtc *intel_crtc) return false; } + return true; + default: + BUG(); + } +} + +static void ivybridge_update_fdi_bc_bifurcation(struct intel_crtc *intel_crtc) +{ + struct drm_device *dev = intel_crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + switch (intel_crtc->pipe) { + case PIPE_A: + break; + case PIPE_B: + if (intel_crtc->config.fdi_lanes > 2) + WARN_ON(I915_READ(SOUTH_CHICKEN1) & FDI_BC_BIFURCATION_SELECT); + else + cpt_enable_fdi_bc_bifurcation(dev); + + break; + case PIPE_C: cpt_enable_fdi_bc_bifurcation(dev); - return true; + break; default: BUG(); } @@ -5756,6 +5773,8 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, } fdi_config_ok = ironlake_check_fdi_lanes(intel_crtc); + if (IS_IVYBRIDGE(dev)) + ivybridge_update_fdi_bc_bifurcation(intel_crtc); ironlake_set_pipeconf(crtc); -- cgit v1.2.3 From 1857e1daa0695d45b2639ac9e3cfcdaede4a7f8a Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 29 Apr 2013 19:34:16 +0200 Subject: drm/i915: move fdi lane configuration checks ahead MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This nicely allows us to drop some hacks which have only been used to work around modeset failures due to lack of fdi lanes. v2: Implement proper checking for Haswell platforms - the fdi link to the LPT PCH has only 2 lanes. Note that we already filter out impossible modes in intel_crt_mode_valid. Unfortunately LPT does not support 6bpc on the fdi rx, so we can't pull clever tricks to squeeze in a few more modes. v2: Rebased on top of Ben Widawsky's num_pipes reorg. v3: Rebase on top of Ville's pipe debug output ocd rampage. v4: Fixup rebase fail spotted by Ville. v5: Fixup rebase fail spotted by Imre Deak. I suck. Cc: Imre Deak Cc: Ville Syrjälä Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 129 ++++++++++++++++++----------------- 1 file changed, 65 insertions(+), 64 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 1a3fdba4e74d..1d157978f10b 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3974,9 +3974,68 @@ bool intel_connector_get_hw_state(struct intel_connector *connector) return encoder->get_hw_state(encoder, &pipe); } -static void ironlake_fdi_compute_config(struct drm_device *dev, +static bool ironlake_check_fdi_lanes(struct drm_device *dev, enum pipe pipe, + struct intel_crtc_config *pipe_config) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *pipe_B_crtc = + to_intel_crtc(dev_priv->pipe_to_crtc_mapping[PIPE_B]); + + DRM_DEBUG_KMS("checking fdi config on pipe %c, lanes %i\n", + pipe_name(pipe), pipe_config->fdi_lanes); + if (pipe_config->fdi_lanes > 4) { + DRM_DEBUG_KMS("invalid fdi lane config on pipe %c: %i lanes\n", + pipe_name(pipe), pipe_config->fdi_lanes); + return false; + } + + if (IS_HASWELL(dev)) { + if (pipe_config->fdi_lanes > 2) { + DRM_DEBUG_KMS("only 2 lanes on haswell, required: %i lanes\n", + pipe_config->fdi_lanes); + return false; + } else { + return true; + } + } + + if (INTEL_INFO(dev)->num_pipes == 2) + return true; + + /* Ivybridge 3 pipe is really complicated */ + switch (pipe) { + case PIPE_A: + return true; + case PIPE_B: + if (dev_priv->pipe_to_crtc_mapping[PIPE_C]->enabled && + pipe_config->fdi_lanes > 2) { + DRM_DEBUG_KMS("invalid shared fdi lane config on pipe %c: %i lanes\n", + pipe_name(pipe), pipe_config->fdi_lanes); + return false; + } + return true; + case PIPE_C: + if (!pipe_B_crtc->base.enabled || + pipe_B_crtc->config.fdi_lanes <= 2) { + if (pipe_config->fdi_lanes > 2) { + DRM_DEBUG_KMS("invalid shared fdi lane config on pipe %c: %i lanes\n", + pipe_name(pipe), pipe_config->fdi_lanes); + return false; + } + } else { + DRM_DEBUG_KMS("fdi link B uses too many lanes to enable link C\n"); + return false; + } + return true; + default: + BUG(); + } +} + +static bool ironlake_fdi_compute_config(struct intel_crtc *intel_crtc, struct intel_crtc_config *pipe_config) { + struct drm_device *dev = intel_crtc->base.dev; struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode; int target_clock, lane, link_bw; @@ -4003,6 +4062,9 @@ static void ironlake_fdi_compute_config(struct drm_device *dev, link_bw *= pipe_config->pixel_multiplier; intel_link_compute_m_n(pipe_config->pipe_bpp, lane, target_clock, link_bw, &pipe_config->fdi_m_n); + + return ironlake_check_fdi_lanes(intel_crtc->base.dev, + intel_crtc->pipe, pipe_config); } static bool intel_crtc_compute_config(struct drm_crtc *crtc, @@ -4040,7 +4102,7 @@ static bool intel_crtc_compute_config(struct drm_crtc *crtc, } if (pipe_config->has_pch_encoder) - ironlake_fdi_compute_config(dev, pipe_config); + return ironlake_fdi_compute_config(to_intel_crtc(crtc), pipe_config); return true; } @@ -5424,63 +5486,6 @@ static void cpt_enable_fdi_bc_bifurcation(struct drm_device *dev) POSTING_READ(SOUTH_CHICKEN1); } -static bool ironlake_check_fdi_lanes(struct intel_crtc *intel_crtc) -{ - struct drm_device *dev = intel_crtc->base.dev; - struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_crtc *pipe_B_crtc = - to_intel_crtc(dev_priv->pipe_to_crtc_mapping[PIPE_B]); - - DRM_DEBUG_KMS("checking fdi config on pipe %c, lanes %i\n", - pipe_name(intel_crtc->pipe), intel_crtc->config.fdi_lanes); - if (intel_crtc->config.fdi_lanes > 4) { - DRM_DEBUG_KMS("invalid fdi lane config on pipe %c: %i lanes\n", - pipe_name(intel_crtc->pipe), intel_crtc->config.fdi_lanes); - /* Clamp lanes to avoid programming the hw with bogus values. */ - intel_crtc->config.fdi_lanes = 4; - - return false; - } - - if (INTEL_INFO(dev)->num_pipes == 2) - return true; - - switch (intel_crtc->pipe) { - case PIPE_A: - return true; - case PIPE_B: - if (dev_priv->pipe_to_crtc_mapping[PIPE_C]->enabled && - intel_crtc->config.fdi_lanes > 2) { - DRM_DEBUG_KMS("invalid shared fdi lane config on pipe %c: %i lanes\n", - pipe_name(intel_crtc->pipe), intel_crtc->config.fdi_lanes); - /* Clamp lanes to avoid programming the hw with bogus values. */ - intel_crtc->config.fdi_lanes = 2; - - return false; - } - - return true; - case PIPE_C: - if (!pipe_B_crtc->base.enabled || pipe_B_crtc->config.fdi_lanes <= 2) { - if (intel_crtc->config.fdi_lanes > 2) { - DRM_DEBUG_KMS("invalid shared fdi lane config on pipe %c: %i lanes\n", - pipe_name(intel_crtc->pipe), intel_crtc->config.fdi_lanes); - /* Clamp lanes to avoid programming the hw with bogus values. */ - intel_crtc->config.fdi_lanes = 2; - - return false; - } - } else { - DRM_DEBUG_KMS("fdi link B uses too many lanes to enable link C\n"); - return false; - } - - return true; - default: - BUG(); - } -} - static void ivybridge_update_fdi_bc_bifurcation(struct intel_crtc *intel_crtc) { struct drm_device *dev = intel_crtc->base.dev; @@ -5672,7 +5677,6 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, bool is_lvds = false; struct intel_encoder *encoder; int ret; - bool fdi_config_ok; for_each_encoder_on_crtc(dev, crtc, encoder) { switch (encoder->type) { @@ -5765,14 +5769,11 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, intel_set_pipe_timings(intel_crtc, mode, adjusted_mode); - /* Note, this also computes intel_crtc->fdi_lanes which is used below in - * ironlake_check_fdi_lanes. */ if (intel_crtc->config.has_pch_encoder) { intel_cpu_transcoder_set_m_n(intel_crtc, &intel_crtc->config.fdi_m_n); } - fdi_config_ok = ironlake_check_fdi_lanes(intel_crtc); if (IS_IVYBRIDGE(dev)) ivybridge_update_fdi_bc_bifurcation(intel_crtc); @@ -5788,7 +5789,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, intel_update_linetime_watermarks(dev, pipe, adjusted_mode); - return fdi_config_ok ? ret : -EINVAL; + return ret; } static bool ironlake_get_pipe_config(struct intel_crtc *crtc, -- cgit v1.2.3 From 1e833f40eb38b033ea185687daa72c073f1acc15 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 19 Feb 2013 22:31:57 +0100 Subject: drm/i915: don't count cpu ports for fdi B/C lane sharing This allows us to use all 4 fdi lanes on fdi B when the cpu eDP is running on pipe C. Yay! v2: Encapsulate test into a little helper function, as suggested by Chris Wilson. Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 1d157978f10b..1ef44b2a6952 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2369,6 +2369,11 @@ static void intel_fdi_normal_train(struct drm_crtc *crtc) FDI_FE_ERRC_ENABLE); } +static bool pipe_has_enabled_pch(struct intel_crtc *intel_crtc) +{ + return intel_crtc->base.enabled && intel_crtc->config.has_pch_encoder; +} + static void ivb_modeset_global_resources(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -2378,10 +2383,13 @@ static void ivb_modeset_global_resources(struct drm_device *dev) to_intel_crtc(dev_priv->pipe_to_crtc_mapping[PIPE_C]); uint32_t temp; - /* When everything is off disable fdi C so that we could enable fdi B - * with all lanes. XXX: This misses the case where a pipe is not using - * any pch resources and so doesn't need any fdi lanes. */ - if (!pipe_B_crtc->base.enabled && !pipe_C_crtc->base.enabled) { + /* + * When everything is off disable fdi C so that we could enable fdi B + * with all lanes. Note that we don't care about enabled pipes without + * an enabled pch encoder. + */ + if (!pipe_has_enabled_pch(pipe_B_crtc) && + !pipe_has_enabled_pch(pipe_C_crtc)) { WARN_ON(I915_READ(FDI_RX_CTL(PIPE_B)) & FDI_RX_ENABLE); WARN_ON(I915_READ(FDI_RX_CTL(PIPE_C)) & FDI_RX_ENABLE); @@ -4015,7 +4023,7 @@ static bool ironlake_check_fdi_lanes(struct drm_device *dev, enum pipe pipe, } return true; case PIPE_C: - if (!pipe_B_crtc->base.enabled || + if (!pipe_has_enabled_pch(pipe_B_crtc) || pipe_B_crtc->config.fdi_lanes <= 2) { if (pipe_config->fdi_lanes > 2) { DRM_DEBUG_KMS("invalid shared fdi lane config on pipe %c: %i lanes\n", -- cgit v1.2.3 From 325b9d048810f7689ec644595061c0b700e64bce Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 19 Apr 2013 11:24:33 +0200 Subject: drm/i915: fixup 12bpc hdmi dotclock handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need to multiply the hdmi port dotclock by 1.5x since it's not really a dotclock, but the 10/8 encoding bitclock divided by 10. Also add correct limit checks for the dotclock and reject modes which don't fit. HDMI 1.4 would allow more, but our hw doesn't support that unfortunately :( Somehow I suspect 12bpc hdmi output never really worked - we really need an i-g-t testcase to check all the different pixel modes and outputs. v2: Fixup the adjusted port clock handling - we need to make sure that the fdi link code still gets the real pixelclock. v3: g4x/vlv don't support 12bpc hdmi output so drop the bogus comment. Acked-by: Ville Syrjälä [danvet: Switch dotclock limit check to <= as suggested by Ville.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_hdmi.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 53ce8a57f589..21302db7f76d 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -783,6 +783,7 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder, struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base); struct drm_device *dev = encoder->base.dev; struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode; + int clock_12bpc = pipe_config->requested_mode.clock * 3 / 2; if (intel_hdmi->color_range_auto) { /* See CEA-861-E - 5.1 Default Encoding Parameters */ @@ -802,16 +803,28 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder, /* * HDMI is either 12 or 8, so if the display lets 10bpc sneak * through, clamp it down. Note that g4x/vlv don't support 12bpc hdmi - * outputs. + * outputs. We also need to check that the higher clock still fits + * within limits. */ - if (pipe_config->pipe_bpp > 8*3 && HAS_PCH_SPLIT(dev)) { + if (pipe_config->pipe_bpp > 8*3 && clock_12bpc <= 225000 + && HAS_PCH_SPLIT(dev)) { DRM_DEBUG_KMS("forcing bpc to 12 for HDMI\n"); pipe_config->pipe_bpp = 12*3; + + /* Need to adjust the port link by 1.5x for 12bpc. */ + adjusted_mode->clock = clock_12bpc; + pipe_config->pixel_target_clock = + pipe_config->requested_mode.clock; } else { DRM_DEBUG_KMS("forcing bpc to 8 for HDMI\n"); pipe_config->pipe_bpp = 8*3; } + if (adjusted_mode->clock > 225000) { + DRM_DEBUG_KMS("too high HDMI clock, rejecting mode\n"); + return false; + } + return true; } -- cgit v1.2.3 From e29c22c0c4fefeb48a0157811930f7e9df0bb3f3 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 21 Feb 2013 00:00:16 +0100 Subject: drm/i915: implement fdi auto-dithering So on a bunch of setups we only have 2 fdi lanes available, e.g. hsw VGA or 3 pipes on ivb. And seemingly a lot of modes don't quite fit into this, among them the default 1080p mode. The solution is to dither down the pipe a bit so that everything fits, which this patch implements. But ports compute their state under the assumption that the bpp they pick will be the one selected, e.g. the display port bw computations won't work otherwise. Now we could adjust our code to again up-dither to the computed DP link parameters, but that's pointless. So instead when the pipe needs to adjust parameters we need to retry the pipe_config computation at the encoder stage. Furthermore we need to inform encoders that they should not increase bandwidth requirements if possible. This is required for the hdmi code, which prefers the pipe to up-dither to either of the two possible hdmi bpc values. LVDS has a similar requirement, although that's probably only theoretical in nature: It's unlikely that we'll ever see an 8bpc high-res lvds panel (which is required to hit the 2 fdi lane limit). eDP is the only thing which could increase the pipe_bpp setting again, even when in the retry-loop. This could hit the WARN. Two reasons for not bothering: - On many eDP panels we'll get a black screen if the bpp settings don't match vbt. So failing the modeset is the right thing to do. But since that also means it's the only way to light up the panel, it should work. So we shouldn't be able to hit this WARN. - There are still opens around the eDP panel handling, and maybe we need additional tricks. Before that happens it's imo no use trying to be too clever. Worst case we just need to kill that WARN or maybe fail the compute config stage if the eDP connector can't get the bpp setting it wants. And since this can only happen with an fdi link in between and so for pch eDP panels it's rather unlikely to blow up, if ever. v2: Rebased on top of a bikeshed from Paulo. v3: Improve commit message around eDP handling with the stuff things with Imre. Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 56 ++++++++++++++++++++++++++++-------- drivers/gpu/drm/i915/intel_drv.h | 7 +++++ drivers/gpu/drm/i915/intel_hdmi.c | 14 ++++++--- drivers/gpu/drm/i915/intel_lvds.c | 2 +- 4 files changed, 62 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 1ef44b2a6952..f40a28589eea 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4040,13 +4040,16 @@ static bool ironlake_check_fdi_lanes(struct drm_device *dev, enum pipe pipe, } } -static bool ironlake_fdi_compute_config(struct intel_crtc *intel_crtc, - struct intel_crtc_config *pipe_config) +#define RETRY 1 +static int ironlake_fdi_compute_config(struct intel_crtc *intel_crtc, + struct intel_crtc_config *pipe_config) { struct drm_device *dev = intel_crtc->base.dev; struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode; int target_clock, lane, link_bw; + bool setup_ok, needs_recompute = false; +retry: /* 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 @@ -4071,12 +4074,26 @@ static bool ironlake_fdi_compute_config(struct intel_crtc *intel_crtc, intel_link_compute_m_n(pipe_config->pipe_bpp, lane, target_clock, link_bw, &pipe_config->fdi_m_n); - return ironlake_check_fdi_lanes(intel_crtc->base.dev, - intel_crtc->pipe, pipe_config); + setup_ok = ironlake_check_fdi_lanes(intel_crtc->base.dev, + intel_crtc->pipe, pipe_config); + if (!setup_ok && pipe_config->pipe_bpp > 6*3) { + pipe_config->pipe_bpp -= 2*3; + DRM_DEBUG_KMS("fdi link bw constraint, reducing pipe bpp to %i\n", + pipe_config->pipe_bpp); + needs_recompute = true; + pipe_config->bw_constrained = true; + + goto retry; + } + + if (needs_recompute) + return RETRY; + + return setup_ok ? 0 : -EINVAL; } -static bool intel_crtc_compute_config(struct drm_crtc *crtc, - struct intel_crtc_config *pipe_config) +static int intel_crtc_compute_config(struct drm_crtc *crtc, + struct intel_crtc_config *pipe_config) { struct drm_device *dev = crtc->dev; struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode; @@ -4085,7 +4102,7 @@ static bool intel_crtc_compute_config(struct drm_crtc *crtc, /* FDI link clock is fixed at 2.7G */ if (pipe_config->requested_mode.clock * 3 > IRONLAKE_FDI_FREQ * 4) - return false; + return -EINVAL; } /* All interlaced capable intel hw wants timings in frames. Note though @@ -4099,7 +4116,7 @@ static bool intel_crtc_compute_config(struct drm_crtc *crtc, */ if ((INTEL_INFO(dev)->gen > 4 || IS_G4X(dev)) && adjusted_mode->hsync_start == adjusted_mode->hdisplay) - return false; + return -EINVAL; if ((IS_G4X(dev) || IS_VALLEYVIEW(dev)) && pipe_config->pipe_bpp > 10*3) { pipe_config->pipe_bpp = 10*3; /* 12bpc is gen5+ */ @@ -4112,7 +4129,7 @@ static bool intel_crtc_compute_config(struct drm_crtc *crtc, if (pipe_config->has_pch_encoder) return ironlake_fdi_compute_config(to_intel_crtc(crtc), pipe_config); - return true; + return 0; } static int valleyview_get_display_clock_speed(struct drm_device *dev) @@ -7692,7 +7709,8 @@ intel_modeset_pipe_config(struct drm_crtc *crtc, struct drm_encoder_helper_funcs *encoder_funcs; struct intel_encoder *encoder; struct intel_crtc_config *pipe_config; - int plane_bpp; + int plane_bpp, ret = -EINVAL; + bool retry = true; pipe_config = kzalloc(sizeof(*pipe_config), GFP_KERNEL); if (!pipe_config) @@ -7705,6 +7723,7 @@ intel_modeset_pipe_config(struct drm_crtc *crtc, if (plane_bpp < 0) goto fail; +encoder_retry: /* Pass our mode to the connectors and the CRTC to give them a chance to * adjust it according to limitations or connector properties, and also * a chance to reject the mode entirely. @@ -7733,10 +7752,23 @@ intel_modeset_pipe_config(struct drm_crtc *crtc, } } - if (!(intel_crtc_compute_config(crtc, pipe_config))) { + ret = intel_crtc_compute_config(crtc, pipe_config); + if (ret < 0) { DRM_DEBUG_KMS("CRTC fixup failed\n"); goto fail; } + + if (ret == RETRY) { + if (WARN(!retry, "loop in pipe configuration computation\n")) { + ret = -EINVAL; + goto fail; + } + + DRM_DEBUG_KMS("CRTC bw constrained, retrying\n"); + retry = false; + goto encoder_retry; + } + DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id); pipe_config->dither = pipe_config->pipe_bpp != plane_bpp; @@ -7746,7 +7778,7 @@ intel_modeset_pipe_config(struct drm_crtc *crtc, return pipe_config; fail: kfree(pipe_config); - return ERR_PTR(-EINVAL); + return ERR_PTR(ret); } /* Computes which crtcs are affected and sets the relevant bits in the mask. For diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 48f309eaf7aa..766afcf39ee9 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -223,6 +223,13 @@ struct intel_crtc_config { /* Controls for the clock computation, to override various stages. */ bool clock_set; + /* + * crtc bandwidth limit, don't increase pipe bpp or clock if not really + * required. This is set in the 2nd loop of calling encoder's + * ->compute_config if the first pick doesn't work out. + */ + bool bw_constrained; + /* Settings for the intel dpll used on pretty much everything but * haswell. */ struct dpll dpll; diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 21302db7f76d..93de5ff77912 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -784,6 +784,7 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder, struct drm_device *dev = encoder->base.dev; struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode; int clock_12bpc = pipe_config->requested_mode.clock * 3 / 2; + int desired_bpp; if (intel_hdmi->color_range_auto) { /* See CEA-861-E - 5.1 Default Encoding Parameters */ @@ -808,16 +809,21 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder, */ if (pipe_config->pipe_bpp > 8*3 && clock_12bpc <= 225000 && HAS_PCH_SPLIT(dev)) { - DRM_DEBUG_KMS("forcing bpc to 12 for HDMI\n"); - pipe_config->pipe_bpp = 12*3; + DRM_DEBUG_KMS("picking bpc to 12 for HDMI output\n"); + desired_bpp = 12*3; /* Need to adjust the port link by 1.5x for 12bpc. */ adjusted_mode->clock = clock_12bpc; pipe_config->pixel_target_clock = pipe_config->requested_mode.clock; } else { - DRM_DEBUG_KMS("forcing bpc to 8 for HDMI\n"); - pipe_config->pipe_bpp = 8*3; + DRM_DEBUG_KMS("picking bpc to 8 for HDMI output\n"); + desired_bpp = 8*3; + } + + if (!pipe_config->bw_constrained) { + DRM_DEBUG_KMS("forcing pipe bpc to %i for HDMI\n", desired_bpp); + pipe_config->pipe_bpp = desired_bpp; } if (adjusted_mode->clock > 225000) { diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 3e29499b2e9a..8d65baf043ea 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -248,7 +248,7 @@ static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder, else lvds_bpp = 6*3; - if (lvds_bpp != pipe_config->pipe_bpp) { + if (lvds_bpp != pipe_config->pipe_bpp && !pipe_config->bw_constrained) { DRM_DEBUG_KMS("forcing display bpp (was %d) to LVDS (%d)\n", pipe_config->pipe_bpp, lvds_bpp); pipe_config->pipe_bpp = lvds_bpp; -- cgit v1.2.3 From 0973f18f8a764d869add12728887c2d1cc281ffb Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 19 Apr 2013 11:25:33 +0200 Subject: drm/i915: stop for_each_intel_crtc_masked macro from leaking Spotted while changing related code. Reviewed-by: Mika Kuoppala Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index f40a28589eea..467c77bc7cb9 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7943,7 +7943,7 @@ intel_modeset_update_state(struct drm_device *dev, unsigned prepare_pipes) list_for_each_entry((intel_crtc), \ &(dev)->mode_config.crtc_list, \ base.head) \ - if (mask & (1 <<(intel_crtc)->pipe)) \ + if (mask & (1 <<(intel_crtc)->pipe)) static bool intel_pipe_config_compare(struct intel_crtc_config *current_config, -- cgit v1.2.3 From 08a24034a84866e3abb7fdb35ed0e479b240c205 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 19 Apr 2013 11:25:34 +0200 Subject: drm/i915: introduce macros to check pipe config properties This code will get _really_ repetive, and we'll end up with tons more of this kind. So extract the common patterns. This should also help when we add a lazy pipe_config compare mode for fastboot. Reviewed-by: Mika Kuoppala Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 467c77bc7cb9..2c3cbec75b4e 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7949,21 +7949,19 @@ static bool intel_pipe_config_compare(struct intel_crtc_config *current_config, struct intel_crtc_config *pipe_config) { - if (current_config->has_pch_encoder != pipe_config->has_pch_encoder) { - DRM_ERROR("mismatch in has_pch_encoder " - "(expected %i, found %i)\n", - current_config->has_pch_encoder, - pipe_config->has_pch_encoder); - return false; +#define PIPE_CONF_CHECK_I(name) \ + if (current_config->name != pipe_config->name) { \ + DRM_ERROR("mismatch in " #name " " \ + "(expected %i, found %i)\n", \ + current_config->name, \ + pipe_config->name); \ + return false; \ } - if (current_config->fdi_lanes != pipe_config->fdi_lanes) { - DRM_ERROR("mismatch in fdi_lanes " - "(expected %i, found %i)\n", - current_config->fdi_lanes, - pipe_config->fdi_lanes); - return false; - } + PIPE_CONF_CHECK_I(has_pch_encoder); + PIPE_CONF_CHECK_I(fdi_lanes); + +#undef PIPE_CONF_CHECK_I return true; } -- cgit v1.2.3 From 72419203cab9acf173956f5564639b0012cd2604 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 4 Apr 2013 13:28:53 +0200 Subject: drm/i915: hw state readout support for fdi m/n We want to use the fdi m/n values to easily compute the adjusted mode dotclock on pch ports. Hence make sure the values stored in the pipe config are always reliable. v2: Fixup FDI TU readout. v3: Rebase on top of moved cpu_transcoder. Reviewed-by: Mika Kuoppala Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 1 + drivers/gpu/drm/i915/intel_display.c | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 2d27960d6dc1..76896baa621c 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -2776,6 +2776,7 @@ /* Transfer unit size for display port - 1, default is 0x3f (for TU size 64) */ #define PIPE_GMCH_DATA_M_TU_SIZE_MASK (0x3f << 25) #define PIPE_GMCH_DATA_M_TU_SIZE_SHIFT 25 +#define TU_SIZE_SHIFT 25 #define PIPE_GMCH_DATA_M_MASK (0xffffff) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 2c3cbec75b4e..f442e0bc8e9f 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5817,6 +5817,22 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, return ret; } +static void ironlake_get_fdi_m_n_config(struct intel_crtc *crtc, + struct intel_crtc_config *pipe_config) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + enum transcoder transcoder = pipe_config->cpu_transcoder; + + pipe_config->fdi_m_n.link_m = I915_READ(PIPE_LINK_M1(transcoder)); + pipe_config->fdi_m_n.link_n = I915_READ(PIPE_LINK_N1(transcoder)); + pipe_config->fdi_m_n.gmch_m = I915_READ(PIPE_DATA_M1(transcoder)) + & ~TU_SIZE_MASK; + pipe_config->fdi_m_n.gmch_n = I915_READ(PIPE_DATA_N1(transcoder)); + pipe_config->fdi_m_n.tu = ((I915_READ(PIPE_DATA_M1(transcoder)) + & TU_SIZE_MASK) >> TU_SIZE_SHIFT) + 1; +} + static bool ironlake_get_pipe_config(struct intel_crtc *crtc, struct intel_crtc_config *pipe_config) { @@ -5834,6 +5850,8 @@ static bool ironlake_get_pipe_config(struct intel_crtc *crtc, tmp = I915_READ(FDI_RX_CTL(crtc->pipe)); pipe_config->fdi_lanes = ((FDI_DP_PORT_WIDTH_MASK & tmp) >> FDI_DP_PORT_WIDTH_SHIFT) + 1; + + ironlake_get_fdi_m_n_config(crtc, pipe_config); } return true; @@ -5979,6 +5997,8 @@ static bool haswell_get_pipe_config(struct intel_crtc *crtc, tmp = I915_READ(FDI_RX_CTL(PIPE_A)); pipe_config->fdi_lanes = ((FDI_DP_PORT_WIDTH_MASK & tmp) >> FDI_DP_PORT_WIDTH_SHIFT) + 1; + + ironlake_get_fdi_m_n_config(crtc, pipe_config); } return true; @@ -7960,6 +7980,11 @@ intel_pipe_config_compare(struct intel_crtc_config *current_config, PIPE_CONF_CHECK_I(has_pch_encoder); PIPE_CONF_CHECK_I(fdi_lanes); + PIPE_CONF_CHECK_I(fdi_m_n.gmch_m); + PIPE_CONF_CHECK_I(fdi_m_n.gmch_n); + PIPE_CONF_CHECK_I(fdi_m_n.link_m); + PIPE_CONF_CHECK_I(fdi_m_n.link_n); + PIPE_CONF_CHECK_I(fdi_m_n.tu); #undef PIPE_CONF_CHECK_I -- cgit v1.2.3 From 1bd1bd806037af04dd1d7bdd39b2b04090c10d2c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 29 Apr 2013 21:56:12 +0200 Subject: drm/i915: hw state readout support for pipe timings This does duplicate the logic in intel_crtc_mode_get a bit, but the issue is that we also should handle interlace modes and other insanity correctly. Hence I've opted for a sligthly more elaborate route where we first read out the crtc timings for the adjusted mode, and then optionally (not sure if we really need it) compute the modeline from that. v2: Also read out the pipe source dimensions into the requested mode. v3: Rebase on top of the moved cpu_transcoder. v4: Simplify CHECK_FLAGS logic as suggested by Chris Wilson. Also properly #undef that macro again. Reviewed-by: Mika Kuoppala (v3) [danvet: Use the existing mask for interlaced bits, spotted by Mika.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 1 + drivers/gpu/drm/i915/intel_display.c | 75 ++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 76896baa621c..b5d87bd457bd 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -2842,6 +2842,7 @@ #define PIPECONF_INTERLACED_ILK (3 << 21) #define PIPECONF_INTERLACED_DBL_ILK (4 << 21) /* ilk/snb only */ #define PIPECONF_PFIT_PF_INTERLACED_DBL_ILK (5 << 21) /* ilk/snb only */ +#define PIPECONF_INTERLACE_MODE_MASK (7 << 21) #define PIPECONF_CXSR_DOWNCLOCK (1<<16) #define PIPECONF_COLOR_RANGE_SELECT (1 << 13) #define PIPECONF_BPC_MASK (0x7 << 5) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index f442e0bc8e9f..9b0d6b03aa1c 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4721,6 +4721,45 @@ static void intel_set_pipe_timings(struct intel_crtc *intel_crtc, ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1)); } +static void intel_get_pipe_timings(struct intel_crtc *crtc, + struct intel_crtc_config *pipe_config) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + enum transcoder cpu_transcoder = pipe_config->cpu_transcoder; + uint32_t tmp; + + tmp = I915_READ(HTOTAL(cpu_transcoder)); + pipe_config->adjusted_mode.crtc_hdisplay = (tmp & 0xffff) + 1; + pipe_config->adjusted_mode.crtc_htotal = ((tmp >> 16) & 0xffff) + 1; + tmp = I915_READ(HBLANK(cpu_transcoder)); + pipe_config->adjusted_mode.crtc_hblank_start = (tmp & 0xffff) + 1; + pipe_config->adjusted_mode.crtc_hblank_end = ((tmp >> 16) & 0xffff) + 1; + tmp = I915_READ(HSYNC(cpu_transcoder)); + pipe_config->adjusted_mode.crtc_hsync_start = (tmp & 0xffff) + 1; + pipe_config->adjusted_mode.crtc_hsync_end = ((tmp >> 16) & 0xffff) + 1; + + tmp = I915_READ(VTOTAL(cpu_transcoder)); + pipe_config->adjusted_mode.crtc_vdisplay = (tmp & 0xffff) + 1; + pipe_config->adjusted_mode.crtc_vtotal = ((tmp >> 16) & 0xffff) + 1; + tmp = I915_READ(VBLANK(cpu_transcoder)); + pipe_config->adjusted_mode.crtc_vblank_start = (tmp & 0xffff) + 1; + pipe_config->adjusted_mode.crtc_vblank_end = ((tmp >> 16) & 0xffff) + 1; + tmp = I915_READ(VSYNC(cpu_transcoder)); + pipe_config->adjusted_mode.crtc_vsync_start = (tmp & 0xffff) + 1; + pipe_config->adjusted_mode.crtc_vsync_end = ((tmp >> 16) & 0xffff) + 1; + + if (I915_READ(PIPECONF(cpu_transcoder)) & PIPECONF_INTERLACE_MASK) { + pipe_config->adjusted_mode.flags |= DRM_MODE_FLAG_INTERLACE; + pipe_config->adjusted_mode.crtc_vtotal += 1; + pipe_config->adjusted_mode.crtc_vblank_end += 1; + } + + tmp = I915_READ(PIPESRC(crtc->pipe)); + pipe_config->requested_mode.vdisplay = (tmp & 0xffff) + 1; + pipe_config->requested_mode.hdisplay = ((tmp >> 16) & 0xffff) + 1; +} + static void i9xx_set_pipeconf(struct intel_crtc *intel_crtc) { struct drm_device *dev = intel_crtc->base.dev; @@ -4937,6 +4976,8 @@ static bool i9xx_get_pipe_config(struct intel_crtc *crtc, if (!(tmp & PIPECONF_ENABLE)) return false; + intel_get_pipe_timings(crtc, pipe_config); + return true; } @@ -5854,6 +5895,8 @@ static bool ironlake_get_pipe_config(struct intel_crtc *crtc, ironlake_get_fdi_m_n_config(crtc, pipe_config); } + intel_get_pipe_timings(crtc, pipe_config); + return true; } @@ -6001,6 +6044,8 @@ static bool haswell_get_pipe_config(struct intel_crtc *crtc, ironlake_get_fdi_m_n_config(crtc, pipe_config); } + intel_get_pipe_timings(crtc, pipe_config); + return true; } @@ -7978,6 +8023,15 @@ intel_pipe_config_compare(struct intel_crtc_config *current_config, return false; \ } +#define PIPE_CONF_CHECK_FLAGS(name, mask) \ + if ((current_config->name ^ pipe_config->name) & (mask)) { \ + DRM_ERROR("mismatch in " #name " " \ + "(expected %i, found %i)\n", \ + current_config->name & (mask), \ + pipe_config->name & (mask)); \ + return false; \ + } + PIPE_CONF_CHECK_I(has_pch_encoder); PIPE_CONF_CHECK_I(fdi_lanes); PIPE_CONF_CHECK_I(fdi_m_n.gmch_m); @@ -7986,7 +8040,28 @@ intel_pipe_config_compare(struct intel_crtc_config *current_config, PIPE_CONF_CHECK_I(fdi_m_n.link_n); PIPE_CONF_CHECK_I(fdi_m_n.tu); + PIPE_CONF_CHECK_I(adjusted_mode.crtc_hdisplay); + PIPE_CONF_CHECK_I(adjusted_mode.crtc_htotal); + PIPE_CONF_CHECK_I(adjusted_mode.crtc_hblank_start); + PIPE_CONF_CHECK_I(adjusted_mode.crtc_hblank_end); + PIPE_CONF_CHECK_I(adjusted_mode.crtc_hsync_start); + PIPE_CONF_CHECK_I(adjusted_mode.crtc_hsync_end); + + PIPE_CONF_CHECK_I(adjusted_mode.crtc_vdisplay); + PIPE_CONF_CHECK_I(adjusted_mode.crtc_vtotal); + PIPE_CONF_CHECK_I(adjusted_mode.crtc_vblank_start); + PIPE_CONF_CHECK_I(adjusted_mode.crtc_vblank_end); + PIPE_CONF_CHECK_I(adjusted_mode.crtc_vsync_start); + PIPE_CONF_CHECK_I(adjusted_mode.crtc_vsync_end); + + PIPE_CONF_CHECK_FLAGS(adjusted_mode.flags, + DRM_MODE_FLAG_INTERLACE); + + PIPE_CONF_CHECK_I(requested_mode.hdisplay); + PIPE_CONF_CHECK_I(requested_mode.vdisplay); + #undef PIPE_CONF_CHECK_I +#undef PIPE_CONF_CHECK_FLAGS return true; } -- cgit v1.2.3 From f599cc2917a1beb1a619d70e3ee7b9bc4dc17bb9 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 29 Apr 2013 13:02:50 +0300 Subject: drm/i915: cleanup opregion technology enabled indicator defines Move near other defines, add TCHE in the name. No functional changes. Signed-off-by: Jani Nikula Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_opregion.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c index dda182833209..75062e0e8e3a 100644 --- a/drivers/gpu/drm/i915/intel_opregion.c +++ b/drivers/gpu/drm/i915/intel_opregion.c @@ -123,6 +123,12 @@ struct opregion_asle { #define ASLE_PFIT_FAILED (1<<14) #define ASLE_PWM_FREQ_FAILED (1<<16) +/* Technology enabled indicator */ +#define ASLE_TCHE_ALS_EN (1 << 0) +#define ASLE_TCHE_BLC_EN (1 << 1) +#define ASLE_TCHE_PFIT_EN (1 << 2) +#define ASLE_TCHE_PFMB_EN (1 << 3) + /* ASLE backlight brightness to set */ #define ASLE_BCLP_VALID (1<<31) #define ASLE_BCLP_MSK (~(1<<31)) @@ -222,11 +228,6 @@ void intel_opregion_asle_intr(struct drm_device *dev) iowrite32(asle_stat, &asle->aslc); } -#define ASLE_ALS_EN (1<<0) -#define ASLE_BLC_EN (1<<1) -#define ASLE_PFIT_EN (1<<2) -#define ASLE_PFMB_EN (1<<3) - void intel_opregion_enable_asle(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -236,7 +237,7 @@ void intel_opregion_enable_asle(struct drm_device *dev) if (IS_MOBILE(dev)) intel_enable_asle(dev); - iowrite32(ASLE_BLC_EN, &asle->tche); + iowrite32(ASLE_TCHE_BLC_EN, &asle->tche); iowrite32(1, &asle->ardy); } } -- cgit v1.2.3 From 68bca4b0c5553c0137d13df37f18a4b059d6e795 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 29 Apr 2013 13:02:51 +0300 Subject: drm/i915: manage opregion asle driver readiness properly Only set ASLE driver readiness (ARDY) and technology enabled indicator (TCHE) once per opregion init. There should be no need to do that at irq postinstall time. Also clear driver readiness at fini. While at it, add defines for driver readiness. Signed-off-by: Jani Nikula Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_opregion.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c index 75062e0e8e3a..5b6d202ec76d 100644 --- a/drivers/gpu/drm/i915/intel_opregion.c +++ b/drivers/gpu/drm/i915/intel_opregion.c @@ -110,6 +110,10 @@ struct opregion_asle { u8 rsvd[102]; } __attribute__((packed)); +/* Driver readiness indicator */ +#define ASLE_ARDY_READY (1 << 0) +#define ASLE_ARDY_NOT_READY (0 << 0) + /* ASLE irq request bits */ #define ASLE_SET_ALS_ILLUM (1 << 0) #define ASLE_SET_BACKLIGHT (1 << 1) @@ -236,9 +240,6 @@ void intel_opregion_enable_asle(struct drm_device *dev) if (asle) { if (IS_MOBILE(dev)) intel_enable_asle(dev); - - iowrite32(ASLE_TCHE_BLC_EN, &asle->tche); - iowrite32(1, &asle->ardy); } } @@ -425,8 +426,12 @@ void intel_opregion_init(struct drm_device *dev) register_acpi_notifier(&intel_opregion_notifier); } - if (opregion->asle) + if (opregion->asle) { intel_opregion_enable_asle(dev); + + iowrite32(ASLE_TCHE_BLC_EN, &opregion->asle->tche); + iowrite32(ASLE_ARDY_READY, &opregion->asle->ardy); + } } void intel_opregion_fini(struct drm_device *dev) @@ -437,6 +442,9 @@ void intel_opregion_fini(struct drm_device *dev) if (!opregion->header) return; + if (opregion->asle) + iowrite32(ASLE_ARDY_NOT_READY, &opregion->asle->ardy); + if (opregion->acpi) { iowrite32(0, &opregion->acpi->drdy); @@ -499,6 +507,8 @@ int intel_opregion_setup(struct drm_device *dev) if (mboxes & MBOX_ASLE) { DRM_DEBUG_DRIVER("ASLE supported\n"); opregion->asle = base + OPREGION_ASLE_OFFSET; + + iowrite32(ASLE_ARDY_NOT_READY, &opregion->asle->ardy); } return 0; -- cgit v1.2.3 From 2cc7aa29143b1276db3e87d2a2d50d3625b77d60 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 29 Apr 2013 13:02:52 +0300 Subject: drm/i915: untie opregion init and asle irq/pipestat enable Stop calling intel_opregion_enable_asle() and consequently intel_enable_asle() on opregion init. It should not be necessary for these reasons: 1) On PCH split platforms, it only enables GSE interrupt, which is enabled in irq postinstall anyway. Moreover, the irq enable uses the wrong bit on IVB+. 2) On gen 2, it would enable a reserved pipestat bit. If there were gen 2 systems with opregion asle support, that is. And the gen 2 irq handler won't handle it anyway. 3) On gen 3-4, the irq postinstall will call intel_opregion_enable_asle() to enable the pipestat. In short, move the asle irq/pipestat enable responsibility to irq postinstall, which already happens to be in place. This should not cause any functional changes, but only do the one line change here for easier bisectability, just in case, and leave all the cleanups this allows to followup patches. Signed-off-by: Jani Nikula Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_opregion.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c index 5b6d202ec76d..4e697993a3f2 100644 --- a/drivers/gpu/drm/i915/intel_opregion.c +++ b/drivers/gpu/drm/i915/intel_opregion.c @@ -427,8 +427,6 @@ void intel_opregion_init(struct drm_device *dev) } if (opregion->asle) { - intel_opregion_enable_asle(dev); - iowrite32(ASLE_TCHE_BLC_EN, &opregion->asle->tche); iowrite32(ASLE_ARDY_READY, &opregion->asle->ardy); } -- cgit v1.2.3 From f898780ba020696e50c04abf2a55790df37ce384 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 29 Apr 2013 13:02:53 +0300 Subject: drm/i915: cleanup redundant checks from intel_enable_asle Realize that intel_enable_asle() is never called on PCH-split platforms or on VLV. Rip out the GSE irq enable for PCH-split platforms, which also happens to be incorrect for IVB+. This should not cause any functional changes. Signed-off-by: Jani Nikula Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index b0dbf4cd0e07..1783ebe4cd99 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -356,21 +356,11 @@ void intel_enable_asle(struct drm_device *dev) drm_i915_private_t *dev_priv = dev->dev_private; unsigned long irqflags; - /* FIXME: opregion/asle for VLV */ - if (IS_VALLEYVIEW(dev)) - return; - spin_lock_irqsave(&dev_priv->irq_lock, irqflags); - if (HAS_PCH_SPLIT(dev)) - ironlake_enable_display_irq(dev_priv, DE_GSE); - else { - i915_enable_pipestat(dev_priv, 1, - PIPE_LEGACY_BLC_EVENT_ENABLE); - if (INTEL_INFO(dev)->gen >= 4) - i915_enable_pipestat(dev_priv, 0, - PIPE_LEGACY_BLC_EVENT_ENABLE); - } + i915_enable_pipestat(dev_priv, 1, PIPE_LEGACY_BLC_EVENT_ENABLE); + if (INTEL_INFO(dev)->gen >= 4) + i915_enable_pipestat(dev_priv, 0, PIPE_LEGACY_BLC_EVENT_ENABLE); spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); } -- cgit v1.2.3 From f49e38dd23d28d4fceea1e84ae444b4c25fc0407 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 29 Apr 2013 13:02:54 +0300 Subject: drm/i915: cleanup opregion asle pipestat enable Both intel_opregion_enable_asle() and intel_enable_asle() have shrunk considerably. Merge them together into a static function in i915_irq.c, and rename to better reflect the purpose and the related platforms. No functional changes. Signed-off-by: Jani Nikula Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 4 ---- drivers/gpu/drm/i915/i915_irq.c | 11 +++++++---- drivers/gpu/drm/i915/intel_opregion.c | 11 ----------- 3 files changed, 7 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 14156f2b8d76..c34103d42c11 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1484,8 +1484,6 @@ i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask); void 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 @@ -1819,12 +1817,10 @@ extern int intel_opregion_setup(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_enable_asle(struct drm_device *dev); #else 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_enable_asle(struct drm_device *dev) { return; } #endif /* intel_acpi.c */ diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 1783ebe4cd99..03a31befa8fd 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -349,13 +349,16 @@ i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask) } /** - * intel_enable_asle - enable ASLE interrupt for OpRegion + * i915_enable_asle_pipestat - enable ASLE pipestat for OpRegion */ -void intel_enable_asle(struct drm_device *dev) +static void i915_enable_asle_pipestat(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; unsigned long irqflags; + if (!dev_priv->opregion.asle || !IS_MOBILE(dev)) + return; + spin_lock_irqsave(&dev_priv->irq_lock, irqflags); i915_enable_pipestat(dev_priv, 1, PIPE_LEGACY_BLC_EVENT_ENABLE); @@ -2964,7 +2967,7 @@ static int i915_irq_postinstall(struct drm_device *dev) I915_WRITE(IER, enable_mask); POSTING_READ(IER); - intel_opregion_enable_asle(dev); + i915_enable_asle_pipestat(dev); return 0; } @@ -3198,7 +3201,7 @@ static int i965_irq_postinstall(struct drm_device *dev) I915_WRITE(PORT_HOTPLUG_EN, 0); POSTING_READ(PORT_HOTPLUG_EN); - intel_opregion_enable_asle(dev); + i915_enable_asle_pipestat(dev); return 0; } diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c index 4e697993a3f2..62b64e4877b8 100644 --- a/drivers/gpu/drm/i915/intel_opregion.c +++ b/drivers/gpu/drm/i915/intel_opregion.c @@ -232,17 +232,6 @@ void intel_opregion_asle_intr(struct drm_device *dev) iowrite32(asle_stat, &asle->aslc); } -void intel_opregion_enable_asle(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - struct opregion_asle __iomem *asle = dev_priv->opregion.asle; - - if (asle) { - if (IS_MOBILE(dev)) - intel_enable_asle(dev); - } -} - #define ACPI_EV_DISPLAY_SWITCH (1<<0) #define ACPI_EV_LID (1<<1) #define ACPI_EV_DOCK (1<<2) -- cgit v1.2.3 From 68fc874289e58e62bd0820db0d52150ce6d9fe03 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 25 Apr 2013 22:52:16 +0200 Subject: drm/i915: move lvds_border_bits to pipe_config pipe_config is the new dev_priv! More seriously, this is actually better since a pipe_config can be thrown away if the modeset compute config stage fails. Whereas any state stored in dev_prive needs to be painstakingly restored, since otherwise a dpms off/on will wreak massive havoc. Yes, that even applies to state only used in ->mode_set callbacks, since we need to call those even for dpms on when the Haswell power well cleared everything out. Reviewed-by: Mika Kuoppala Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 2 -- drivers/gpu/drm/i915/intel_drv.h | 1 + drivers/gpu/drm/i915/intel_lvds.c | 2 +- drivers/gpu/drm/i915/intel_panel.c | 3 +-- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index c34103d42c11..8890b0ba66c3 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1019,8 +1019,6 @@ typedef struct drm_i915_private { /* Kernel Modesetting */ struct sdvo_device_mapping sdvo_mappings[2]; - /* indicate whether the LVDS_BORDER should be enabled or not */ - unsigned int lvds_border_bits; struct drm_crtc *plane_to_crtc_mapping[3]; struct drm_crtc *pipe_to_crtc_mapping[3]; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 766afcf39ee9..dfcf546790bc 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -249,6 +249,7 @@ struct intel_crtc_config { struct { u32 control; u32 pgm_ratios; + u32 lvds_border_bits; } gmch_pfit; /* Panel fitter placement and size for Ironlake+ */ diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 8d65baf043ea..47f47ea64f59 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -116,7 +116,7 @@ static void intel_pre_pll_enable_lvds(struct intel_encoder *encoder) } /* set the corresponsding LVDS_BORDER bit */ - temp |= dev_priv->lvds_border_bits; + temp |= intel_crtc->config.gmch_pfit.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. */ diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index 2526326efd81..4bf1e18f74cc 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -183,7 +183,6 @@ void intel_gmch_panel_fitting(struct intel_crtc *intel_crtc, int fitting_mode) { struct drm_device *dev = intel_crtc->base.dev; - struct drm_i915_private *dev_priv = dev->dev_private; u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0; struct drm_display_mode *mode, *adjusted_mode; @@ -312,7 +311,7 @@ out: pipe_config->gmch_pfit.control = pfit_control; pipe_config->gmch_pfit.pgm_ratios = pfit_pgm_ratios; } - dev_priv->lvds_border_bits = border; + pipe_config->gmch_pfit.lvds_border_bits = border; } static int is_backlight_combination_mode(struct drm_device *dev) -- cgit v1.2.3 From 2deefda541edb0c73e57e988ccaac4cd014da0d3 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 25 Apr 2013 22:52:17 +0200 Subject: drm/i915: rip out indirection for pfit pipe_config assignment This was still required a bit (on the cargo-cult side though) when the state was stored in dev_priv, and when the enable/disable sequence was botched a bit (to avoid too many updates). But with pipeconfig we always get a clean slate, so this is pointless. Rip it out. Reviewed-by: Mika Kuoppala Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_panel.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index 4bf1e18f74cc..56f17b2382fc 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -306,11 +306,8 @@ out: if (INTEL_INFO(dev)->gen < 4 && pipe_config->pipe_bpp == 18) pfit_control |= PANEL_8TO6_DITHER_ENABLE; - if (pfit_control != pipe_config->gmch_pfit.control || - pfit_pgm_ratios != pipe_config->gmch_pfit.pgm_ratios) { - pipe_config->gmch_pfit.control = pfit_control; - pipe_config->gmch_pfit.pgm_ratios = pfit_pgm_ratios; - } + pipe_config->gmch_pfit.control = pfit_control; + pipe_config->gmch_pfit.pgm_ratios = pfit_pgm_ratios; pipe_config->gmch_pfit.lvds_border_bits = border; } -- cgit v1.2.3 From 5a80c45c5297a025c2615624042ea8b6840a5376 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 25 Apr 2013 22:52:18 +0200 Subject: drm/i915: move border color writes to pfit_enable Writing hw registers from compute_config? Just say no! In this case not too horrible since we write a constant 0, and only debugging would put something else in there. But while checking that code I've noticed that this register disappeared on pch platforms, so fix that up, too. And adjust the comment a bit, it's outdated. Reviewed-by: Mika Kuoppala Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 4 ++++ drivers/gpu/drm/i915/intel_lvds.c | 10 ---------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 9b0d6b03aa1c..650433790813 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3632,6 +3632,10 @@ static void i9xx_pfit_enable(struct intel_crtc *crtc) I915_WRITE(PFIT_PGM_RATIOS, pipe_config->gmch_pfit.pgm_ratios); I915_WRITE(PFIT_CONTROL, pipe_config->gmch_pfit.control); + + /* Border color in case we don't scale up to the full screen. Black by + * default, change to something else for debugging. */ + I915_WRITE(BCLRPAT(crtc->pipe), 0); } static void valleyview_crtc_enable(struct drm_crtc *crtc) diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 47f47ea64f59..d256fe454a9d 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -231,7 +231,6 @@ static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder, struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode; struct intel_crtc *intel_crtc = lvds_encoder->base.new_crtc; unsigned int lvds_bpp; - int pipe; /* Should never happen!! */ if (INTEL_INFO(dev)->gen < 4 && intel_crtc->pipe == 0) { @@ -274,15 +273,6 @@ static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder, intel_connector->panel.fitting_mode); } - /* - * Enable automatic panel scaling for non-native modes so that they fill - * the screen. Should be enabled before the pipe is enabled, according - * to register description and PRM. - * Change the value here to see the borders for debugging - */ - for_each_pipe(pipe) - I915_WRITE(BCLRPAT(pipe), 0); - drm_mode_set_crtcinfo(adjusted_mode, 0); pipe_config->timings_set = true; -- cgit v1.2.3 From 3512f976d252bd5d07d04e9e157f0cd210c959a0 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Wed, 24 Apr 2013 18:52:34 +0300 Subject: drm: Add struct drm_rect and assorted utility functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit struct drm_rect represents a simple rectangle. The utility functions are there to help driver writers. v2: Moved the region stuff into its own file, made the smaller funcs static inline, used 64bit maths in the scaled clipping function to avoid overflows (instead it will saturate to INT_MIN or INT_MAX). v3: Renamed drm_region to drm_rect, drm_region_clip to drm_rect_intersect, and drm_region_subsample to drm_rect_downscale. v4: Renamed some function parameters, improve kernel-doc comments a bit, and actually generate documentation for drm_rect.[ch]. v5: s/RETUTRNS/RETURNS/ Reviewed-by: Laurent Pinchart Reviewed-by: Chris Wilson Signed-off-by: Ville Syrjälä Acked-by: Dave Airlie Signed-off-by: Daniel Vetter --- Documentation/DocBook/drm.tmpl | 2 + drivers/gpu/drm/Makefile | 3 +- drivers/gpu/drm/drm_rect.c | 96 ++++++++++++++++++++++++++++++ include/drm/drm_rect.h | 132 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 232 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/drm_rect.c create mode 100644 include/drm/drm_rect.h diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index f9df3b872c16..7c7af25b330c 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -1653,6 +1653,8 @@ void intel_crt_init(struct drm_device *dev) KMS API Functions !Edrivers/gpu/drm/drm_crtc.c +!Edrivers/gpu/drm/drm_rect.c +!Finclude/drm/drm_rect.h diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 0d59b24f8d23..8f94018852a6 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -12,7 +12,8 @@ drm-y := drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \ drm_platform.o drm_sysfs.o drm_hashtab.o drm_mm.o \ drm_crtc.o drm_modes.o drm_edid.o \ drm_info.o drm_debugfs.o drm_encoder_slave.o \ - drm_trace_points.o drm_global.o drm_prime.o + drm_trace_points.o drm_global.o drm_prime.o \ + drm_rect.o drm-$(CONFIG_COMPAT) += drm_ioc32.o drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o diff --git a/drivers/gpu/drm/drm_rect.c b/drivers/gpu/drm/drm_rect.c new file mode 100644 index 000000000000..22091ecdbff4 --- /dev/null +++ b/drivers/gpu/drm/drm_rect.c @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2011-2013 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include + +/** + * drm_rect_intersect - intersect two rectangles + * @r1: first rectangle + * @r2: second rectangle + * + * Calculate the intersection of rectangles @r1 and @r2. + * @r1 will be overwritten with the intersection. + * + * RETURNS: + * %true if rectangle @r1 is still visible after the operation, + * %false otherwise. + */ +bool drm_rect_intersect(struct drm_rect *r1, const struct drm_rect *r2) +{ + r1->x1 = max(r1->x1, r2->x1); + r1->y1 = max(r1->y1, r2->y1); + r1->x2 = min(r1->x2, r2->x2); + r1->y2 = min(r1->y2, r2->y2); + + return drm_rect_visible(r1); +} +EXPORT_SYMBOL(drm_rect_intersect); + +/** + * drm_rect_clip_scaled - perform a scaled clip operation + * @src: source window rectangle + * @dst: destination window rectangle + * @clip: clip rectangle + * @hscale: horizontal scaling factor + * @vscale: vertical scaling factor + * + * Clip rectangle @dst by rectangle @clip. Clip rectangle @src by the + * same amounts multiplied by @hscale and @vscale. + * + * RETURNS: + * %true if rectangle @dst is still visible after being clipped, + * %false otherwise + */ +bool drm_rect_clip_scaled(struct drm_rect *src, struct drm_rect *dst, + const struct drm_rect *clip, + int hscale, int vscale) +{ + int diff; + + diff = clip->x1 - dst->x1; + if (diff > 0) { + int64_t tmp = src->x1 + (int64_t) diff * hscale; + src->x1 = clamp_t(int64_t, tmp, INT_MIN, INT_MAX); + } + diff = clip->y1 - dst->y1; + if (diff > 0) { + int64_t tmp = src->y1 + (int64_t) diff * vscale; + src->y1 = clamp_t(int64_t, tmp, INT_MIN, INT_MAX); + } + diff = dst->x2 - clip->x2; + if (diff > 0) { + int64_t tmp = src->x2 - (int64_t) diff * hscale; + src->x2 = clamp_t(int64_t, tmp, INT_MIN, INT_MAX); + } + diff = dst->y2 - clip->y2; + if (diff > 0) { + int64_t tmp = src->y2 - (int64_t) diff * vscale; + src->y2 = clamp_t(int64_t, tmp, INT_MIN, INT_MAX); + } + + return drm_rect_intersect(dst, clip); +} +EXPORT_SYMBOL(drm_rect_clip_scaled); diff --git a/include/drm/drm_rect.h b/include/drm/drm_rect.h new file mode 100644 index 000000000000..2b7278c1bc42 --- /dev/null +++ b/include/drm/drm_rect.h @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2011-2013 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef DRM_RECT_H +#define DRM_RECT_H + +/** + * drm_rect - two dimensional rectangle + * @x1: horizontal starting coordinate (inclusive) + * @x2: horizontal ending coordinate (exclusive) + * @y1: vertical starting coordinate (inclusive) + * @y2: vertical ending coordinate (exclusive) + */ +struct drm_rect { + int x1, y1, x2, y2; +}; + +/** + * drm_rect_adjust_size - adjust the size of the rectangle + * @r: rectangle to be adjusted + * @dw: horizontal adjustment + * @dh: vertical adjustment + * + * Change the size of rectangle @r by @dw in the horizontal direction, + * and by @dh in the vertical direction, while keeping the center + * of @r stationary. + * + * Positive @dw and @dh increase the size, negative values decrease it. + */ +static inline void drm_rect_adjust_size(struct drm_rect *r, int dw, int dh) +{ + r->x1 -= dw >> 1; + r->y1 -= dh >> 1; + r->x2 += (dw + 1) >> 1; + r->y2 += (dh + 1) >> 1; +} + +/** + * drm_rect_translate - translate the rectangle + * @r: rectangle to be tranlated + * @dx: horizontal translation + * @dy: vertical translation + * + * Move rectangle @r by @dx in the horizontal direction, + * and by @dy in the vertical direction. + */ +static inline void drm_rect_translate(struct drm_rect *r, int dx, int dy) +{ + r->x1 += dx; + r->y1 += dy; + r->x2 += dx; + r->y2 += dy; +} + +/** + * drm_rect_downscale - downscale a rectangle + * @r: rectangle to be downscaled + * @horz: horizontal downscale factor + * @vert: vertical downscale factor + * + * Divide the coordinates of rectangle @r by @horz and @vert. + */ +static inline void drm_rect_downscale(struct drm_rect *r, int horz, int vert) +{ + r->x1 /= horz; + r->y1 /= vert; + r->x2 /= horz; + r->y2 /= vert; +} + +/** + * drm_rect_width - determine the rectangle width + * @r: rectangle whose width is returned + * + * RETURNS: + * The width of the rectangle. + */ +static inline int drm_rect_width(const struct drm_rect *r) +{ + return r->x2 - r->x1; +} + +/** + * drm_rect_height - determine the rectangle height + * @r: rectangle whose height is returned + * + * RETURNS: + * The height of the rectangle. + */ +static inline int drm_rect_height(const struct drm_rect *r) +{ + return r->y2 - r->y1; +} + +/** + * drm_rect_visible - determine if the the rectangle is visible + * @r: rectangle whose visibility is returned + * + * RETURNS: + * %true if the rectangle is visible, %false otherwise. + */ +static inline bool drm_rect_visible(const struct drm_rect *r) +{ + return drm_rect_width(r) > 0 && drm_rect_height(r) > 0; +} + +bool drm_rect_intersect(struct drm_rect *r, const struct drm_rect *clip); +bool drm_rect_clip_scaled(struct drm_rect *src, struct drm_rect *dst, + const struct drm_rect *clip, + int hscale, int vscale); + +#endif -- cgit v1.2.3 From 4954c4282f6b945f1dd5716f92b594a07fa4ffe3 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Wed, 24 Apr 2013 18:52:35 +0300 Subject: drm: Add drm_rect_calc_{hscale, vscale}() utility functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These functions calculate the scaling factor based on the source and destination rectangles. There are two version of the functions, the strict ones that will return an error if the min/max scaling factor is exceeded, and the relaxed versions that will adjust the src/dst rectangles in order to keep the scaling factor withing the limits. v2: Return error instead of adjusting regions, refactor common parts into one function, and split into strict and relaxed versions. v3: Renamed drm_region to drm_rect, add "_rect_" to the function names. v4: Fix "calculcate" typos Reviewed-by: Chris Wilson Signed-off-by: Ville Syrjälä Acked-by: Dave Airlie Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_rect.c | 177 +++++++++++++++++++++++++++++++++++++++++++++ include/drm/drm_rect.h | 12 +++ 2 files changed, 189 insertions(+) diff --git a/drivers/gpu/drm/drm_rect.c b/drivers/gpu/drm/drm_rect.c index 22091ecdbff4..dc11a2867f20 100644 --- a/drivers/gpu/drm/drm_rect.c +++ b/drivers/gpu/drm/drm_rect.c @@ -94,3 +94,180 @@ bool drm_rect_clip_scaled(struct drm_rect *src, struct drm_rect *dst, return drm_rect_intersect(dst, clip); } EXPORT_SYMBOL(drm_rect_clip_scaled); + +static int drm_calc_scale(int src, int dst) +{ + int scale = 0; + + if (src < 0 || dst < 0) + return -EINVAL; + + if (dst == 0) + return 0; + + scale = src / dst; + + return scale; +} + +/** + * drm_rect_calc_hscale - calculate the horizontal scaling factor + * @src: source window rectangle + * @dst: destination window rectangle + * @min_hscale: minimum allowed horizontal scaling factor + * @max_hscale: maximum allowed horizontal scaling factor + * + * Calculate the horizontal scaling factor as + * (@src width) / (@dst width). + * + * RETURNS: + * The horizontal scaling factor, or errno of out of limits. + */ +int drm_rect_calc_hscale(const struct drm_rect *src, + const struct drm_rect *dst, + int min_hscale, int max_hscale) +{ + int src_w = drm_rect_width(src); + int dst_w = drm_rect_width(dst); + int hscale = drm_calc_scale(src_w, dst_w); + + if (hscale < 0 || dst_w == 0) + return hscale; + + if (hscale < min_hscale || hscale > max_hscale) + return -ERANGE; + + return hscale; +} +EXPORT_SYMBOL(drm_rect_calc_hscale); + +/** + * drm_rect_calc_vscale - calculate the vertical scaling factor + * @src: source window rectangle + * @dst: destination window rectangle + * @min_vscale: minimum allowed vertical scaling factor + * @max_vscale: maximum allowed vertical scaling factor + * + * Calculate the vertical scaling factor as + * (@src height) / (@dst height). + * + * RETURNS: + * The vertical scaling factor, or errno of out of limits. + */ +int drm_rect_calc_vscale(const struct drm_rect *src, + const struct drm_rect *dst, + int min_vscale, int max_vscale) +{ + int src_h = drm_rect_height(src); + int dst_h = drm_rect_height(dst); + int vscale = drm_calc_scale(src_h, dst_h); + + if (vscale < 0 || dst_h == 0) + return vscale; + + if (vscale < min_vscale || vscale > max_vscale) + return -ERANGE; + + return vscale; +} +EXPORT_SYMBOL(drm_rect_calc_vscale); + +/** + * drm_calc_hscale_relaxed - calculate the horizontal scaling factor + * @src: source window rectangle + * @dst: destination window rectangle + * @min_hscale: minimum allowed horizontal scaling factor + * @max_hscale: maximum allowed horizontal scaling factor + * + * Calculate the horizontal scaling factor as + * (@src width) / (@dst width). + * + * If the calculated scaling factor is below @min_vscale, + * decrease the height of rectangle @dst to compensate. + * + * If the calculated scaling factor is above @max_vscale, + * decrease the height of rectangle @src to compensate. + * + * RETURNS: + * The horizontal scaling factor. + */ +int drm_rect_calc_hscale_relaxed(struct drm_rect *src, + struct drm_rect *dst, + int min_hscale, int max_hscale) +{ + int src_w = drm_rect_width(src); + int dst_w = drm_rect_width(dst); + int hscale = drm_calc_scale(src_w, dst_w); + + if (hscale < 0 || dst_w == 0) + return hscale; + + if (hscale < min_hscale) { + int max_dst_w = src_w / min_hscale; + + drm_rect_adjust_size(dst, max_dst_w - dst_w, 0); + + return min_hscale; + } + + if (hscale > max_hscale) { + int max_src_w = dst_w * max_hscale; + + drm_rect_adjust_size(src, max_src_w - src_w, 0); + + return max_hscale; + } + + return hscale; +} +EXPORT_SYMBOL(drm_rect_calc_hscale_relaxed); + +/** + * drm_rect_calc_vscale_relaxed - calculate the vertical scaling factor + * @src: source window rectangle + * @dst: destination window rectangle + * @min_vscale: minimum allowed vertical scaling factor + * @max_vscale: maximum allowed vertical scaling factor + * + * Calculate the vertical scaling factor as + * (@src height) / (@dst height). + * + * If the calculated scaling factor is below @min_vscale, + * decrease the height of rectangle @dst to compensate. + * + * If the calculated scaling factor is above @max_vscale, + * decrease the height of rectangle @src to compensate. + * + * RETURNS: + * The vertical scaling factor. + */ +int drm_rect_calc_vscale_relaxed(struct drm_rect *src, + struct drm_rect *dst, + int min_vscale, int max_vscale) +{ + int src_h = drm_rect_height(src); + int dst_h = drm_rect_height(dst); + int vscale = drm_calc_scale(src_h, dst_h); + + if (vscale < 0 || dst_h == 0) + return vscale; + + if (vscale < min_vscale) { + int max_dst_h = src_h / min_vscale; + + drm_rect_adjust_size(dst, 0, max_dst_h - dst_h); + + return min_vscale; + } + + if (vscale > max_vscale) { + int max_src_h = dst_h * max_vscale; + + drm_rect_adjust_size(src, 0, max_src_h - src_h); + + return max_vscale; + } + + return vscale; +} +EXPORT_SYMBOL(drm_rect_calc_vscale_relaxed); diff --git a/include/drm/drm_rect.h b/include/drm/drm_rect.h index 2b7278c1bc42..de24f16a3c4f 100644 --- a/include/drm/drm_rect.h +++ b/include/drm/drm_rect.h @@ -128,5 +128,17 @@ bool drm_rect_intersect(struct drm_rect *r, const struct drm_rect *clip); bool drm_rect_clip_scaled(struct drm_rect *src, struct drm_rect *dst, const struct drm_rect *clip, int hscale, int vscale); +int drm_rect_calc_hscale(const struct drm_rect *src, + const struct drm_rect *dst, + int min_hscale, int max_hscale); +int drm_rect_calc_vscale(const struct drm_rect *src, + const struct drm_rect *dst, + int min_vscale, int max_vscale); +int drm_rect_calc_hscale_relaxed(struct drm_rect *src, + struct drm_rect *dst, + int min_hscale, int max_hscale); +int drm_rect_calc_vscale_relaxed(struct drm_rect *src, + struct drm_rect *dst, + int min_vscale, int max_vscale); #endif -- cgit v1.2.3 From e7272df342ba337e87e210470bb93d97d192f2e0 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Wed, 24 Apr 2013 18:52:36 +0300 Subject: drm: Add drm_rect_debug_print() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a debug function to print the rectangle in a human readable format. v2: Renamed drm_region to drm_rect, the function from drm_region_debug to drm_rect_debug_print(), and use %+d instead of +%d in the format. v3: Use %d format for width/height in the non fixed point case as well Reviewed-by: Chris Wilson Signed-off-by: Ville Syrjälä Acked-by: Dave Airlie Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_rect.c | 22 ++++++++++++++++++++++ include/drm/drm_rect.h | 1 + 2 files changed, 23 insertions(+) diff --git a/drivers/gpu/drm/drm_rect.c b/drivers/gpu/drm/drm_rect.c index dc11a2867f20..7047ca025787 100644 --- a/drivers/gpu/drm/drm_rect.c +++ b/drivers/gpu/drm/drm_rect.c @@ -24,6 +24,7 @@ #include #include #include +#include #include /** @@ -271,3 +272,24 @@ int drm_rect_calc_vscale_relaxed(struct drm_rect *src, return vscale; } EXPORT_SYMBOL(drm_rect_calc_vscale_relaxed); + +/** + * drm_rect_debug_print - print the rectangle information + * @r: rectangle to print + * @fixed_point: rectangle is in 16.16 fixed point format + */ +void drm_rect_debug_print(const struct drm_rect *r, bool fixed_point) +{ + int w = drm_rect_width(r); + int h = drm_rect_height(r); + + if (fixed_point) + DRM_DEBUG_KMS("%d.%06ux%d.%06u%+d.%06u%+d.%06u\n", + w >> 16, ((w & 0xffff) * 15625) >> 10, + h >> 16, ((h & 0xffff) * 15625) >> 10, + r->x1 >> 16, ((r->x1 & 0xffff) * 15625) >> 10, + r->y1 >> 16, ((r->y1 & 0xffff) * 15625) >> 10); + else + DRM_DEBUG_KMS("%dx%d%+d%+d\n", w, h, r->x1, r->y1); +} +EXPORT_SYMBOL(drm_rect_debug_print); diff --git a/include/drm/drm_rect.h b/include/drm/drm_rect.h index de24f16a3c4f..fe767b757c8c 100644 --- a/include/drm/drm_rect.h +++ b/include/drm/drm_rect.h @@ -140,5 +140,6 @@ int drm_rect_calc_hscale_relaxed(struct drm_rect *src, int drm_rect_calc_vscale_relaxed(struct drm_rect *src, struct drm_rect *dst, int min_vscale, int max_vscale); +void drm_rect_debug_print(const struct drm_rect *r, bool fixed_point); #endif -- cgit v1.2.3 From 0894c96bff762d0474a8722bba3d420f643db359 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Wed, 24 Apr 2013 18:52:37 +0300 Subject: drm: Add drm_rect_equals() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drm_rect_equals() tells whether two drm_rects are equal. Reviewed-by: Chris Wilson Signed-off-by: Ville Syrjälä Acked-by: Dave Airlie Signed-off-by: Daniel Vetter --- include/drm/drm_rect.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/include/drm/drm_rect.h b/include/drm/drm_rect.h index fe767b757c8c..64fa265c6ffb 100644 --- a/include/drm/drm_rect.h +++ b/include/drm/drm_rect.h @@ -124,6 +124,21 @@ static inline bool drm_rect_visible(const struct drm_rect *r) return drm_rect_width(r) > 0 && drm_rect_height(r) > 0; } +/** + * drm_rect_equals - determine if two rectangles are equal + * @r1: first rectangle + * @r2: second rectangle + * + * RETURNS: + * %true if the rectangles are equal, %false otherwise. + */ +static inline bool drm_rect_equals(const struct drm_rect *r1, + const struct drm_rect *r2) +{ + return r1->x1 == r2->x1 && r1->x2 == r2->x2 && + r1->y1 == r2->y1 && r1->y2 == r2->y2; +} + bool drm_rect_intersect(struct drm_rect *r, const struct drm_rect *clip); bool drm_rect_clip_scaled(struct drm_rect *src, struct drm_rect *dst, const struct drm_rect *clip, -- cgit v1.2.3 From 1731693a5a372869d017e601a23b1ce2eb3135ed Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Wed, 24 Apr 2013 18:52:38 +0300 Subject: drm/i915: Implement proper clipping for video sprites MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Properly clip the source when the destination gets clipped by the pipe dimensions. Sadly the video sprite hardware is rather limited so it can't do proper sub-pixel postitioning. Resort to truncating the source coordinates to (macro)pixel boundary. The scaling checks are done using the strict drm_region functions. Which means that an error is returned when the min/max scaling ratios are exceeded. Also do some additional checking against various hardware limits. v2: Truncate src coords instead of rounding to avoid increasing src viewport size, and adapt to changes in drm_calc_{h,v}scale(). v3: Adapt to drm_region->drm_rect rename. Fix misaligned crtc_w for packed YUV formats when scaling isn't supported. v4: Use stricter scaling checks, use drm_rect_equals() v5: If sprite is below min size, make it invisible instead returning an error. Use WARN_ON() instead if BUG_ON(), and add one to sanity check the src viewport size. v6: Add comments to remind about src and dst coordinate types Reviewed-by: Chris Wilson Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_sprite.c | 196 +++++++++++++++++++++++++++--------- 1 file changed, 149 insertions(+), 47 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 18993ad93540..87fe3b625c4b 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "intel_drv.h" #include #include "i915_drv.h" @@ -583,6 +584,20 @@ ilk_get_colorkey(struct drm_plane *plane, struct drm_intel_sprite_colorkey *key) key->flags = I915_SET_COLORKEY_NONE; } +static bool +format_is_yuv(uint32_t format) +{ + switch (format) { + case DRM_FORMAT_YUYV: + case DRM_FORMAT_UYVY: + case DRM_FORMAT_VYUY: + case DRM_FORMAT_YVYU: + return true; + default: + return false; + } +} + static int intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, struct drm_framebuffer *fb, int crtc_x, int crtc_y, @@ -600,9 +615,29 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv, pipe); int ret = 0; - int x = src_x >> 16, y = src_y >> 16; - int primary_w = crtc->mode.hdisplay, primary_h = crtc->mode.vdisplay; bool disable_primary = false; + bool visible; + int hscale, vscale; + int max_scale, min_scale; + int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0); + struct drm_rect src = { + /* sample coordinates in 16.16 fixed point */ + .x1 = src_x, + .x2 = src_x + src_w, + .y1 = src_y, + .y2 = src_y + src_h, + }; + struct drm_rect dst = { + /* integer pixels */ + .x1 = crtc_x, + .x2 = crtc_x + crtc_w, + .y1 = crtc_y, + .y2 = crtc_y + crtc_h, + }; + const struct drm_rect clip = { + .x2 = crtc->mode.hdisplay, + .y2 = crtc->mode.vdisplay, + }; intel_fb = to_intel_framebuffer(fb); obj = intel_fb->obj; @@ -618,19 +653,23 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, intel_plane->src_w = src_w; intel_plane->src_h = src_h; - src_w = src_w >> 16; - src_h = src_h >> 16; - /* Pipe must be running... */ - if (!(I915_READ(PIPECONF(cpu_transcoder)) & PIPECONF_ENABLE)) + if (!(I915_READ(PIPECONF(cpu_transcoder)) & PIPECONF_ENABLE)) { + DRM_DEBUG_KMS("Pipe disabled\n"); return -EINVAL; + } - if (crtc_x >= primary_w || crtc_y >= primary_h) + /* Don't modify another pipe's plane */ + if (intel_plane->pipe != intel_crtc->pipe) { + DRM_DEBUG_KMS("Wrong plane <-> crtc mapping\n"); return -EINVAL; + } - /* Don't modify another pipe's plane */ - if (intel_plane->pipe != intel_crtc->pipe) + /* FIXME check all gen limits */ + if (fb->width < 3 || fb->height < 3 || fb->pitches[0] > 16384) { + DRM_DEBUG_KMS("Unsuitable framebuffer for plane\n"); return -EINVAL; + } /* Sprite planes can be linear or x-tiled surfaces */ switch (obj->tiling_mode) { @@ -638,55 +677,115 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, case I915_TILING_X: break; default: + DRM_DEBUG_KMS("Unsupported tiling mode\n"); return -EINVAL; } - /* - * Clamp the width & height into the visible area. Note we don't - * try to scale the source if part of the visible region is offscreen. - * The caller must handle that by adjusting source offset and size. - */ - if ((crtc_x < 0) && ((crtc_x + crtc_w) > 0)) { - crtc_w += crtc_x; - crtc_x = 0; + max_scale = intel_plane->max_downscale << 16; + min_scale = intel_plane->can_scale ? 1 : (1 << 16); + + hscale = drm_rect_calc_hscale(&src, &dst, min_scale, max_scale); + if (hscale < 0) { + DRM_DEBUG_KMS("Horizontal scaling factor out of limits\n"); + drm_rect_debug_print(&src, true); + drm_rect_debug_print(&dst, false); + + return hscale; } - if ((crtc_x + crtc_w) <= 0) /* Nothing to display */ - goto out; - if ((crtc_x + crtc_w) > primary_w) - crtc_w = primary_w - crtc_x; - if ((crtc_y < 0) && ((crtc_y + crtc_h) > 0)) { - crtc_h += crtc_y; - crtc_y = 0; + vscale = drm_rect_calc_vscale(&src, &dst, min_scale, max_scale); + if (vscale < 0) { + DRM_DEBUG_KMS("Vertical scaling factor out of limits\n"); + drm_rect_debug_print(&src, true); + drm_rect_debug_print(&dst, false); + + return vscale; } - if ((crtc_y + crtc_h) <= 0) /* Nothing to display */ - goto out; - if (crtc_y + crtc_h > primary_h) - crtc_h = primary_h - crtc_y; - if (!crtc_w || !crtc_h) /* Again, nothing to display */ - goto out; + visible = drm_rect_clip_scaled(&src, &dst, &clip, hscale, vscale); - /* - * We may not have a scaler, eg. HSW does not have it any more - */ - if (!intel_plane->can_scale && (crtc_w != src_w || crtc_h != src_h)) - return -EINVAL; + crtc_x = dst.x1; + crtc_y = dst.y1; + crtc_w = drm_rect_width(&dst); + crtc_h = drm_rect_height(&dst); - /* - * We can take a larger source and scale it down, but - * only so much... 16x is the max on SNB. - */ - if (((src_w * src_h) / (crtc_w * crtc_h)) > intel_plane->max_downscale) - return -EINVAL; + if (visible) { + /* Make the source viewport size an exact multiple of the scaling factors. */ + drm_rect_adjust_size(&src, + drm_rect_width(&dst) * hscale - drm_rect_width(&src), + drm_rect_height(&dst) * vscale - drm_rect_height(&src)); + + /* sanity check to make sure the src viewport wasn't enlarged */ + WARN_ON(src.x1 < (int) src_x || + src.y1 < (int) src_y || + src.x2 > (int) (src_x + src_w) || + src.y2 > (int) (src_y + src_h)); + + /* + * Hardware doesn't handle subpixel coordinates. + * Adjust to (macro)pixel boundary, but be careful not to + * increase the source viewport size, because that could + * push the downscaling factor out of bounds. + * + * FIXME Should we be really strict and reject the + * config if it results in non (macro)pixel aligned + * coords? + */ + src_x = src.x1 >> 16; + src_w = drm_rect_width(&src) >> 16; + src_y = src.y1 >> 16; + src_h = drm_rect_height(&src) >> 16; + + if (format_is_yuv(fb->pixel_format)) { + src_x &= ~1; + src_w &= ~1; + + /* + * Must keep src and dst the + * same if we can't scale. + */ + if (!intel_plane->can_scale) + crtc_w &= ~1; + + if (crtc_w == 0) + visible = false; + } + } + + /* Check size restrictions when scaling */ + if (visible && (src_w != crtc_w || src_h != crtc_h)) { + unsigned int width_bytes; + + WARN_ON(!intel_plane->can_scale); + + /* FIXME interlacing min height is 6 */ + + if (crtc_w < 3 || crtc_h < 3) + visible = false; + + if (src_w < 3 || src_h < 3) + visible = false; + + width_bytes = ((src_x * pixel_size) & 63) + src_w * pixel_size; + + if (src_w > 2048 || src_h > 2048 || + width_bytes > 4096 || fb->pitches[0] > 4096) { + DRM_DEBUG_KMS("Source dimensions exceed hardware limits\n"); + return -EINVAL; + } + } + + dst.x1 = crtc_x; + dst.x2 = crtc_x + crtc_w; + dst.y1 = crtc_y; + dst.y2 = crtc_y + crtc_h; /* * If the sprite is completely covering the primary plane, * we can disable the primary and save power. */ - if ((crtc_x == 0) && (crtc_y == 0) && - (crtc_w == primary_w) && (crtc_h == primary_h)) - disable_primary = true; + disable_primary = drm_rect_equals(&dst, &clip); + WARN_ON(disable_primary && !visible); mutex_lock(&dev->struct_mutex); @@ -708,8 +807,12 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, if (!disable_primary) intel_enable_primary(crtc); - intel_plane->update_plane(plane, fb, obj, crtc_x, crtc_y, - crtc_w, crtc_h, x, y, src_w, src_h); + if (visible) + intel_plane->update_plane(plane, fb, obj, + crtc_x, crtc_y, crtc_w, crtc_h, + src_x, src_y, src_w, src_h); + else + intel_plane->disable_plane(plane); if (disable_primary) intel_disable_primary(crtc); @@ -732,7 +835,6 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, out_unlock: mutex_unlock(&dev->struct_mutex); -out: return ret; } -- cgit v1.2.3 From 3c3686cd9700efefcfc24ab5910b3e5fffd0b069 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Wed, 24 Apr 2013 18:52:39 +0300 Subject: drm/i915: Relax the sprite scaling limits checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reduce the size of the the src/dst viewport to keep the scalign ratios in check. v2: Below min size sprite handling squashed to previous patch Reviewed-by: Chris Wilson Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_sprite.c | 48 +++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 87fe3b625c4b..19b9cb961b5a 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -681,26 +681,19 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, return -EINVAL; } + /* + * FIXME the following code does a bunch of fuzzy adjustments to the + * coordinates and sizes. We probably need some way to decide whether + * more strict checking should be done instead. + */ max_scale = intel_plane->max_downscale << 16; min_scale = intel_plane->can_scale ? 1 : (1 << 16); - hscale = drm_rect_calc_hscale(&src, &dst, min_scale, max_scale); - if (hscale < 0) { - DRM_DEBUG_KMS("Horizontal scaling factor out of limits\n"); - drm_rect_debug_print(&src, true); - drm_rect_debug_print(&dst, false); - - return hscale; - } - - vscale = drm_rect_calc_vscale(&src, &dst, min_scale, max_scale); - if (vscale < 0) { - DRM_DEBUG_KMS("Vertical scaling factor out of limits\n"); - drm_rect_debug_print(&src, true); - drm_rect_debug_print(&dst, false); + hscale = drm_rect_calc_hscale_relaxed(&src, &dst, min_scale, max_scale); + BUG_ON(hscale < 0); - return vscale; - } + vscale = drm_rect_calc_vscale_relaxed(&src, &dst, min_scale, max_scale); + BUG_ON(vscale < 0); visible = drm_rect_clip_scaled(&src, &dst, &clip, hscale, vscale); @@ -710,6 +703,25 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, crtc_h = drm_rect_height(&dst); if (visible) { + /* check again in case clipping clamped the results */ + hscale = drm_rect_calc_hscale(&src, &dst, min_scale, max_scale); + if (hscale < 0) { + DRM_DEBUG_KMS("Horizontal scaling factor out of limits\n"); + drm_rect_debug_print(&src, true); + drm_rect_debug_print(&dst, false); + + return hscale; + } + + vscale = drm_rect_calc_vscale(&src, &dst, min_scale, max_scale); + if (vscale < 0) { + DRM_DEBUG_KMS("Vertical scaling factor out of limits\n"); + drm_rect_debug_print(&src, true); + drm_rect_debug_print(&dst, false); + + return vscale; + } + /* Make the source viewport size an exact multiple of the scaling factors. */ drm_rect_adjust_size(&src, drm_rect_width(&dst) * hscale - drm_rect_width(&src), @@ -726,10 +738,6 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, * Adjust to (macro)pixel boundary, but be careful not to * increase the source viewport size, because that could * push the downscaling factor out of bounds. - * - * FIXME Should we be really strict and reject the - * config if it results in non (macro)pixel aligned - * coords? */ src_x = src.x1 >> 16; src_w = drm_rect_width(&src) >> 16; -- cgit v1.2.3 From dce3271b1ee05ca01ebdde50d613d7b33ef178a9 Mon Sep 17 00:00:00 2001 From: Mika Kuoppala Date: Tue, 30 Apr 2013 13:30:33 +0300 Subject: drm/i915: reference count for i915_hw_contexts Enabling PPGTT and also the need to track which context was guilty of gpu hang (arb robustness enabling) have put pressure for struct i915_hw_context to be more than just a placeholder for hw context state. In order to track object lifetime properly in a multi peer usage, add reference counting for i915_hw_context. v2: track i915_hw_context pointers instead of using ctx_ids (from Chris Wilson) v3 (Ben): Get rid of do_release() and handle refcounting more compactly. (recommended by Chis) v4: kref_* put inside static inlines (Daniel Vetter) remove code duplication on freeing context (Chris Wilson) v5: idr_remove and ctx->file_priv = NULL in destroy ioctl (Chris) This actually will cause a problem if one destroys a context and later refers to the idea of the context (multiple contexts may have the same id, but only 1 will exist in the idr). v6: Strip out the request related stuff. Reworded commit message. Got rid of do_destroy and introduced i915_gem_context_release_handle, suggested by Chris Wilson. v7: idr_remove can't be called inside idr_for_each (Chris Wilson) Signed-off-by: Ben Widawsky (v5) Signed-off-by: Mika Kuoppala (v7) Reviewed-by: Ben Widawsky Reviewed-by: Chris Wilson [danvet: Squash sob lines, the patch ping-ponged between Ben and Mika a bit ...] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 12 ++++++++++++ drivers/gpu/drm/i915/i915_gem_context.c | 28 ++++++++++++++-------------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 8890b0ba66c3..3ac71db78e9e 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -452,6 +452,7 @@ struct i915_hw_ppgtt { /* This must match up with the value previously used for execbuf2.rsvd1. */ #define DEFAULT_CONTEXT_ID 0 struct i915_hw_context { + struct kref ref; int id; bool is_initialized; struct drm_i915_file_private *file_priv; @@ -1697,6 +1698,17 @@ void i915_gem_context_fini(struct drm_device *dev); void i915_gem_context_close(struct drm_device *dev, struct drm_file *file); int i915_switch_context(struct intel_ring_buffer *ring, struct drm_file *file, int to_id); +void i915_gem_context_free(struct kref *ctx_ref); +static inline void i915_gem_context_reference(struct i915_hw_context *ctx) +{ + kref_get(&ctx->ref); +} + +static inline void i915_gem_context_unreference(struct i915_hw_context *ctx) +{ + kref_put(&ctx->ref, i915_gem_context_free); +} + int i915_gem_context_create_ioctl(struct drm_device *dev, void *data, struct drm_file *file); int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data, diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index a1e8ecb6adf6..9e8c68510e1b 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -124,10 +124,10 @@ static int get_context_size(struct drm_device *dev) return ret; } -static void do_destroy(struct i915_hw_context *ctx) +void i915_gem_context_free(struct kref *ctx_ref) { - if (ctx->file_priv) - idr_remove(&ctx->file_priv->context_idr, ctx->id); + struct i915_hw_context *ctx = container_of(ctx_ref, + typeof(*ctx), ref); drm_gem_object_unreference(&ctx->obj->base); kfree(ctx); @@ -145,6 +145,7 @@ create_hw_context(struct drm_device *dev, if (ctx == NULL) return ERR_PTR(-ENOMEM); + kref_init(&ctx->ref); ctx->obj = i915_gem_alloc_object(dev, dev_priv->hw_context_size); if (ctx->obj == NULL) { kfree(ctx); @@ -169,18 +170,18 @@ create_hw_context(struct drm_device *dev, if (file_priv == NULL) return ctx; - ctx->file_priv = file_priv; - ret = idr_alloc(&file_priv->context_idr, ctx, DEFAULT_CONTEXT_ID + 1, 0, GFP_KERNEL); if (ret < 0) goto err_out; + + ctx->file_priv = file_priv; ctx->id = ret; return ctx; err_out: - do_destroy(ctx); + i915_gem_context_unreference(ctx); return ERR_PTR(ret); } @@ -226,7 +227,7 @@ static int create_default_context(struct drm_i915_private *dev_priv) err_unpin: i915_gem_object_unpin(ctx->obj); err_destroy: - do_destroy(ctx); + i915_gem_context_unreference(ctx); return ret; } @@ -262,6 +263,7 @@ void i915_gem_context_init(struct drm_device *dev) void i915_gem_context_fini(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + struct i915_hw_context *dctx = dev_priv->ring[RCS].default_context; if (dev_priv->hw_contexts_disabled) return; @@ -271,9 +273,8 @@ void i915_gem_context_fini(struct drm_device *dev) * other code, leading to spurious errors. */ intel_gpu_reset(dev); - i915_gem_object_unpin(dev_priv->ring[RCS].default_context->obj); - - do_destroy(dev_priv->ring[RCS].default_context); + i915_gem_object_unpin(dctx->obj); + i915_gem_context_unreference(dctx); } static int context_idr_cleanup(int id, void *p, void *data) @@ -282,8 +283,7 @@ static int context_idr_cleanup(int id, void *p, void *data) BUG_ON(id == DEFAULT_CONTEXT_ID); - do_destroy(ctx); - + i915_gem_context_unreference(ctx); return 0; } @@ -512,8 +512,8 @@ int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data, return -ENOENT; } - do_destroy(ctx); - + idr_remove(&ctx->file_priv->context_idr, ctx->id); + i915_gem_context_unreference(ctx); mutex_unlock(&dev->struct_mutex); DRM_DEBUG_DRIVER("HW context %d destroyed\n", args->ctx_id); -- cgit v1.2.3 From 17aa6be9579eb204b426eeae146a43bf3dd05078 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 30 Apr 2013 14:01:40 +0200 Subject: drm/i915: simplify DP/DDI port width macros If we ever leak a non-DP compliant port width through here, we have a pretty serious issue. So just rip out all these WARNs - if we need them it's probably better to have them at a central place where we compute the dp lane count. Also use the new DDI width macro for FDI mode. Cc: Paulo Zanoni Reviewed-by: Paulo Zanoni [danvet: fixup the embarrassing s/intel_dp->DP/temp/ mistake Paulo spotted.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 11 ++--------- drivers/gpu/drm/i915/intel_ddi.c | 34 ++-------------------------------- drivers/gpu/drm/i915/intel_dp.c | 12 +----------- 3 files changed, 5 insertions(+), 52 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index b5d87bd457bd..aec569f13186 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -2664,9 +2664,7 @@ #define DP_PRE_EMPHASIS_SHIFT 22 /* How many wires to use. I guess 3 was too hard */ -#define DP_PORT_WIDTH_1 (0 << 19) -#define DP_PORT_WIDTH_2 (1 << 19) -#define DP_PORT_WIDTH_4 (3 << 19) +#define DP_PORT_WIDTH(width) (((width) - 1) << 19) #define DP_PORT_WIDTH_MASK (7 << 19) /* Mystic DPCD version 1.1 special mode */ @@ -4755,9 +4753,6 @@ #define TRANS_DDI_EDP_INPUT_B_ONOFF (5<<12) #define TRANS_DDI_EDP_INPUT_C_ONOFF (6<<12) #define TRANS_DDI_BFI_ENABLE (1<<4) -#define TRANS_DDI_PORT_WIDTH_X1 (0<<1) -#define TRANS_DDI_PORT_WIDTH_X2 (1<<1) -#define TRANS_DDI_PORT_WIDTH_X4 (3<<1) /* DisplayPort Transport Control */ #define DP_TP_CTL_A 0x64040 @@ -4801,9 +4796,7 @@ #define DDI_BUF_PORT_REVERSAL (1<<16) #define DDI_BUF_IS_IDLE (1<<7) #define DDI_A_4_LANES (1<<4) -#define DDI_PORT_WIDTH_X1 (0<<1) -#define DDI_PORT_WIDTH_X2 (1<<1) -#define DDI_PORT_WIDTH_X4 (3<<1) +#define DDI_PORT_WIDTH(width) (((width) - 1) << 1) #define DDI_INIT_DISPLAY_DETECTED (1<<0) /* DDI Buffer Translations */ diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 72941f94c830..3ff4de6c6009 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -687,22 +687,7 @@ static void intel_ddi_mode_set(struct drm_encoder *encoder, intel_dp->DP = intel_dig_port->port_reversal | DDI_BUF_CTL_ENABLE | DDI_BUF_EMP_400MV_0DB_HSW; - switch (intel_dp->lane_count) { - case 1: - intel_dp->DP |= DDI_PORT_WIDTH_X1; - break; - case 2: - intel_dp->DP |= DDI_PORT_WIDTH_X2; - break; - case 4: - intel_dp->DP |= DDI_PORT_WIDTH_X4; - break; - default: - intel_dp->DP |= DDI_PORT_WIDTH_X4; - WARN(1, "Unexpected DP lane count %d\n", - intel_dp->lane_count); - break; - } + intel_dp->DP |= DDI_PORT_WIDTH(intel_dp->lane_count); if (intel_dp->has_audio) { DRM_DEBUG_DRIVER("DP audio on pipe %c on DDI\n", @@ -1031,22 +1016,7 @@ void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc) temp |= TRANS_DDI_MODE_SELECT_DP_SST; - switch (intel_dp->lane_count) { - case 1: - temp |= TRANS_DDI_PORT_WIDTH_X1; - break; - case 2: - temp |= TRANS_DDI_PORT_WIDTH_X2; - break; - case 4: - temp |= TRANS_DDI_PORT_WIDTH_X4; - break; - default: - temp |= TRANS_DDI_PORT_WIDTH_X4; - WARN(1, "Unsupported lane count %d\n", - intel_dp->lane_count); - } - + temp |= DDI_PORT_WIDTH(intel_dp->lane_count); } else { WARN(1, "Invalid encoder type %d for pipe %c\n", intel_encoder->type, pipe_name(pipe)); diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 7840b4d22774..a2935234ac03 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -891,18 +891,8 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, /* Handle DP bits in common between all three register formats */ intel_dp->DP |= DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0; + intel_dp->DP |= DP_PORT_WIDTH(intel_dp->lane_count); - switch (intel_dp->lane_count) { - case 1: - intel_dp->DP |= DP_PORT_WIDTH_1; - break; - case 2: - intel_dp->DP |= DP_PORT_WIDTH_2; - break; - case 4: - intel_dp->DP |= DP_PORT_WIDTH_4; - break; - } if (intel_dp->has_audio) { DRM_DEBUG_DRIVER("Enabling DP audio on pipe %c\n", pipe_name(intel_crtc->pipe)); -- cgit v1.2.3 From 168f83660211b9e059e3bc0638daaa01e9ea0b71 Mon Sep 17 00:00:00 2001 From: Mika Kuoppala Date: Fri, 3 May 2013 16:29:08 +0300 Subject: drm/i915: unreference default context on module unload Before module unload is called, gpu_idle() will switch to default context. This will increment ref count of base object as the default context is 'running' on module unload time. Unreference the drm object so that when context is freed, base object is freed as well. v2: added comment to explain the refcounts (Ben Widawsky) Signed-off-by: Mika Kuoppala Reviewed-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_context.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index 9e8c68510e1b..280617ef4996 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -274,6 +274,14 @@ void i915_gem_context_fini(struct drm_device *dev) intel_gpu_reset(dev); i915_gem_object_unpin(dctx->obj); + + /* When default context is created and switched to, base object refcount + * will be 2 (+1 from object creation and +1 from do_switch()). + * i915_gem_context_fini() will be called after gpu_idle() has switched + * to default context. So we need to unreference the base object once + * to offset the do_switch part, so that i915_gem_context_unreference() + * can then free the base object correctly. */ + drm_gem_object_unreference(&dctx->obj->base); i915_gem_context_unreference(dctx); } -- cgit v1.2.3 From 2b87f3b1bab1df814057ff551f4fc49c22a7fde9 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 2 May 2013 15:30:47 -0700 Subject: drm/i915: fix Haswell pfit power well check v2 We can't read the pfit regs if the power well is off, so use the cached value. v2: re-add lost comment (Jesse) make sure the crtc using the fitter is actually enabled (Jesse) Signed-off-by: Jesse Barnes [danvet: Drop now unused dev_priv, as spotted by Mika.] Reviewed-by: Mika Kuoppala Reviewed-by: Paulo Zanoni Tested-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 650433790813..de8be7595a62 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5906,7 +5906,6 @@ static bool ironlake_get_pipe_config(struct intel_crtc *crtc, static void haswell_modeset_global_resources(struct drm_device *dev) { - struct drm_i915_private *dev_priv = dev->dev_private; bool enable = false; struct intel_crtc *crtc; struct intel_encoder *encoder; @@ -5918,7 +5917,7 @@ static void haswell_modeset_global_resources(struct drm_device *dev) * sequence that's not yet available. Just in case desktop eDP * on PORT D is possible on haswell, too. */ /* Even the eDP panel fitter is outside the always-on well. */ - if (I915_READ(PF_WIN_SZ(crtc->pipe))) + if (crtc->config.pch_pfit.size && crtc->base.enabled) enable = true; } -- cgit v1.2.3 From 1ffb064ec4ea1880f618cb4ed900aab3927187d4 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 24 Apr 2013 09:53:25 -0700 Subject: Input: egalax_ts - move to devm_* functions The usage of devm_* functions makes code cleaner and tidier. Signed-off-by: Andy Shevchenko Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/egalax_ts.c | 53 +++++++++-------------------------- 1 file changed, 14 insertions(+), 39 deletions(-) diff --git a/drivers/input/touchscreen/egalax_ts.c b/drivers/input/touchscreen/egalax_ts.c index 17c9097f3b5d..1602c5b6c094 100644 --- a/drivers/input/touchscreen/egalax_ts.c +++ b/drivers/input/touchscreen/egalax_ts.c @@ -166,24 +166,22 @@ static int egalax_firmware_version(struct i2c_client *client) } static int egalax_ts_probe(struct i2c_client *client, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { struct egalax_ts *ts; struct input_dev *input_dev; - int ret; int error; - ts = kzalloc(sizeof(struct egalax_ts), GFP_KERNEL); + ts = devm_kzalloc(&client->dev, sizeof(struct egalax_ts), GFP_KERNEL); if (!ts) { dev_err(&client->dev, "Failed to allocate memory\n"); return -ENOMEM; } - input_dev = input_allocate_device(); + input_dev = devm_input_allocate_device(&client->dev); if (!input_dev) { dev_err(&client->dev, "Failed to allocate memory\n"); - error = -ENOMEM; - goto err_free_ts; + return -ENOMEM; } ts->client = client; @@ -193,19 +191,17 @@ static int egalax_ts_probe(struct i2c_client *client, error = egalax_wake_up_device(client); if (error) { dev_err(&client->dev, "Failed to wake up the controller\n"); - goto err_free_dev; + return error; } - ret = egalax_firmware_version(client); - if (ret < 0) { + error = egalax_firmware_version(client); + if (error < 0) { dev_err(&client->dev, "Failed to read firmware version\n"); - error = -EIO; - goto err_free_dev; + return error; } input_dev->name = "EETI eGalax Touch Screen"; input_dev->id.bustype = BUS_I2C; - input_dev->dev.parent = &client->dev; __set_bit(EV_ABS, input_dev->evbit); __set_bit(EV_KEY, input_dev->evbit); @@ -221,41 +217,21 @@ static int egalax_ts_probe(struct i2c_client *client, input_set_drvdata(input_dev, ts); - error = request_threaded_irq(client->irq, NULL, egalax_ts_interrupt, - IRQF_TRIGGER_LOW | IRQF_ONESHOT, - "egalax_ts", ts); + error = devm_request_threaded_irq(&client->dev, client->irq, NULL, + egalax_ts_interrupt, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "egalax_ts", ts); if (error < 0) { dev_err(&client->dev, "Failed to register interrupt\n"); - goto err_free_dev; + return error; } error = input_register_device(ts->input_dev); if (error) - goto err_free_irq; + return error; i2c_set_clientdata(client, ts); return 0; - -err_free_irq: - free_irq(client->irq, ts); -err_free_dev: - input_free_device(input_dev); -err_free_ts: - kfree(ts); - - return error; -} - -static int egalax_ts_remove(struct i2c_client *client) -{ - struct egalax_ts *ts = i2c_get_clientdata(client); - - free_irq(client->irq, ts); - - input_unregister_device(ts->input_dev); - kfree(ts); - - return 0; } static const struct i2c_device_id egalax_ts_id[] = { @@ -301,7 +277,6 @@ static struct i2c_driver egalax_ts_driver = { }, .id_table = egalax_ts_id, .probe = egalax_ts_probe, - .remove = egalax_ts_remove, }; module_i2c_driver(egalax_ts_driver); -- cgit v1.2.3 From aafc72ee3fa2dc366a2689b63d2b3dbe5302cdc7 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Sun, 5 May 2013 19:34:17 -0700 Subject: Input: ab8500-ponkey - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Dmitry Torokhov --- drivers/input/misc/ab8500-ponkey.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/input/misc/ab8500-ponkey.c b/drivers/input/misc/ab8500-ponkey.c index 2f090b46e716..f2fbdd88ed20 100644 --- a/drivers/input/misc/ab8500-ponkey.c +++ b/drivers/input/misc/ab8500-ponkey.c @@ -127,8 +127,6 @@ static int ab8500_ponkey_remove(struct platform_device *pdev) input_unregister_device(ponkey->idev); kfree(ponkey); - platform_set_drvdata(pdev, NULL); - return 0; } -- cgit v1.2.3 From 45d763f953b5ba90af567153c3e1301d65f510e9 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Sun, 5 May 2013 19:34:45 -0700 Subject: Input: bfin_rotary - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Dmitry Torokhov --- drivers/input/misc/bfin_rotary.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/input/misc/bfin_rotary.c b/drivers/input/misc/bfin_rotary.c index a6666e142a91..cd139cb17e32 100644 --- a/drivers/input/misc/bfin_rotary.c +++ b/drivers/input/misc/bfin_rotary.c @@ -208,7 +208,6 @@ static int bfin_rotary_remove(struct platform_device *pdev) peripheral_free_list(per_cnt); kfree(rotary); - platform_set_drvdata(pdev, NULL); return 0; } -- cgit v1.2.3 From 5ed7ecfc79a7d3f16449e95c7368c396b51e1de9 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Sun, 5 May 2013 19:35:04 -0700 Subject: Input: gpio_tilt_polled - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Acked-by: Heiko Stuebner Signed-off-by: Dmitry Torokhov --- drivers/input/misc/gpio_tilt_polled.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/input/misc/gpio_tilt_polled.c b/drivers/input/misc/gpio_tilt_polled.c index da05cca8b562..714c68369134 100644 --- a/drivers/input/misc/gpio_tilt_polled.c +++ b/drivers/input/misc/gpio_tilt_polled.c @@ -184,8 +184,6 @@ static int gpio_tilt_polled_remove(struct platform_device *pdev) struct gpio_tilt_polled_dev *tdev = platform_get_drvdata(pdev); const struct gpio_tilt_platform_data *pdata = tdev->pdata; - platform_set_drvdata(pdev, NULL); - input_unregister_polled_device(tdev->poll_dev); input_free_polled_device(tdev->poll_dev); -- cgit v1.2.3 From 18fc6f5096d4fed93421bedc2a26350520fbd6ba Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Sun, 5 May 2013 19:35:43 -0700 Subject: Input: max8925_onkey - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Dmitry Torokhov --- drivers/input/misc/max8925_onkey.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/input/misc/max8925_onkey.c b/drivers/input/misc/max8925_onkey.c index f9179b2585a9..eef41cfc054d 100644 --- a/drivers/input/misc/max8925_onkey.c +++ b/drivers/input/misc/max8925_onkey.c @@ -148,8 +148,6 @@ static int max8925_onkey_remove(struct platform_device *pdev) input_unregister_device(info->idev); kfree(info); - platform_set_drvdata(pdev, NULL); - return 0; } -- cgit v1.2.3 From 7a9f4ff23ffc164b542cec07073efbe8f6c2a3f3 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Sun, 5 May 2013 19:35:57 -0700 Subject: Input: mc13783-pwrbutton - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Dmitry Torokhov --- drivers/input/misc/mc13783-pwrbutton.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/input/misc/mc13783-pwrbutton.c b/drivers/input/misc/mc13783-pwrbutton.c index 0906ca593d5f..d0277a7b1579 100644 --- a/drivers/input/misc/mc13783-pwrbutton.c +++ b/drivers/input/misc/mc13783-pwrbutton.c @@ -250,7 +250,6 @@ static int mc13783_pwrbutton_remove(struct platform_device *pdev) input_unregister_device(priv->pwr); kfree(priv); - platform_set_drvdata(pdev, NULL); return 0; } -- cgit v1.2.3 From b189e4d94d747004fc297524377029c9ada72ba2 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Sun, 5 May 2013 19:36:11 -0700 Subject: Input: pm8xxx-vibrator - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Dmitry Torokhov --- drivers/input/misc/pm8xxx-vibrator.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/input/misc/pm8xxx-vibrator.c b/drivers/input/misc/pm8xxx-vibrator.c index a9da65e41c5b..ec086f6f3cc3 100644 --- a/drivers/input/misc/pm8xxx-vibrator.c +++ b/drivers/input/misc/pm8xxx-vibrator.c @@ -249,8 +249,6 @@ static int pm8xxx_vib_remove(struct platform_device *pdev) input_unregister_device(vib->vib_input_dev); kfree(vib); - platform_set_drvdata(pdev, NULL); - return 0; } -- cgit v1.2.3 From 7c0e8123a506928305ff9d650c1ad24866d4d5f6 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Sun, 5 May 2013 19:36:26 -0700 Subject: Input: pmic8xxx-pwrkey - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Cc: Trilok Soni Signed-off-by: Dmitry Torokhov --- drivers/input/misc/pmic8xxx-pwrkey.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/input/misc/pmic8xxx-pwrkey.c b/drivers/input/misc/pmic8xxx-pwrkey.c index 4b811be73974..afd0af9c4850 100644 --- a/drivers/input/misc/pmic8xxx-pwrkey.c +++ b/drivers/input/misc/pmic8xxx-pwrkey.c @@ -177,7 +177,6 @@ static int pmic8xxx_pwrkey_probe(struct platform_device *pdev) free_press_irq: free_irq(key_press_irq, NULL); unreg_input_dev: - platform_set_drvdata(pdev, NULL); input_unregister_device(pwr); pwr = NULL; free_input_dev: @@ -198,7 +197,6 @@ static int pmic8xxx_pwrkey_remove(struct platform_device *pdev) free_irq(key_press_irq, pwrkey); free_irq(key_release_irq, pwrkey); input_unregister_device(pwrkey->pwr); - platform_set_drvdata(pdev, NULL); kfree(pwrkey); return 0; -- cgit v1.2.3 From ace9eac8fb2d97f97a4f891bf760f43915153b9c Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Sun, 5 May 2013 19:36:43 -0700 Subject: Input: pwm-beeper - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Dmitry Torokhov --- drivers/input/misc/pwm-beeper.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c index 0808868461de..a37f0c909aba 100644 --- a/drivers/input/misc/pwm-beeper.c +++ b/drivers/input/misc/pwm-beeper.c @@ -133,7 +133,6 @@ static int pwm_beeper_remove(struct platform_device *pdev) { struct pwm_beeper *beeper = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); input_unregister_device(beeper->input); pwm_disable(beeper->pwm); -- cgit v1.2.3 From ad34b42e542366f7b79d2d8a72fdd6ac3c146513 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Sun, 5 May 2013 19:37:01 -0700 Subject: Input: rotary_encoder - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Dmitry Torokhov --- drivers/input/misc/rotary_encoder.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c index aff47b2c38ff..5b1aff825138 100644 --- a/drivers/input/misc/rotary_encoder.c +++ b/drivers/input/misc/rotary_encoder.c @@ -317,8 +317,6 @@ static int rotary_encoder_remove(struct platform_device *pdev) if (!dev_get_platdata(&pdev->dev)) kfree(pdata); - platform_set_drvdata(pdev, NULL); - return 0; } -- cgit v1.2.3 From 602b6a02cad6a6473668c5a0176a247e5bc6648a Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Sun, 5 May 2013 19:37:43 -0700 Subject: Input: 88pm860x-ts - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/88pm860x-ts.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/input/touchscreen/88pm860x-ts.c b/drivers/input/touchscreen/88pm860x-ts.c index c7068942ebe8..e8d457266b71 100644 --- a/drivers/input/touchscreen/88pm860x-ts.c +++ b/drivers/input/touchscreen/88pm860x-ts.c @@ -299,7 +299,6 @@ static int pm860x_touch_remove(struct platform_device *pdev) input_unregister_device(touch->idev); free_irq(touch->irq, touch); - platform_set_drvdata(pdev, NULL); kfree(touch); return 0; } -- cgit v1.2.3 From 7f1f5d13ef9b9a3af81e2001938bcb377436915d Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Sun, 5 May 2013 19:38:03 -0700 Subject: Input: atmel-wm97xx - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Acked-by: Hans-Christian Egtvedt Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/atmel-wm97xx.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/input/touchscreen/atmel-wm97xx.c b/drivers/input/touchscreen/atmel-wm97xx.c index 2c1e46b7e45b..268a35e55d7f 100644 --- a/drivers/input/touchscreen/atmel-wm97xx.c +++ b/drivers/input/touchscreen/atmel-wm97xx.c @@ -372,7 +372,6 @@ static int __init atmel_wm97xx_probe(struct platform_device *pdev) err_irq: free_irq(atmel_wm97xx->ac97c_irq, atmel_wm97xx); err: - platform_set_drvdata(pdev, NULL); kfree(atmel_wm97xx); return ret; } @@ -386,7 +385,6 @@ static int __exit atmel_wm97xx_remove(struct platform_device *pdev) free_irq(atmel_wm97xx->ac97c_irq, atmel_wm97xx); del_timer_sync(&atmel_wm97xx->pen_timer); wm97xx_unregister_mach_ops(wm); - platform_set_drvdata(pdev, NULL); kfree(atmel_wm97xx); return 0; -- cgit v1.2.3 From fdbe0c5356de959e38ba5bbfa2ff09960a51f86b Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Sun, 5 May 2013 19:38:34 -0700 Subject: Input: da9052_tsi - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/da9052_tsi.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/input/touchscreen/da9052_tsi.c b/drivers/input/touchscreen/da9052_tsi.c index 8f561e22bdd4..ab64d58c3ac0 100644 --- a/drivers/input/touchscreen/da9052_tsi.c +++ b/drivers/input/touchscreen/da9052_tsi.c @@ -329,8 +329,6 @@ static int da9052_ts_remove(struct platform_device *pdev) input_unregister_device(tsi->dev); kfree(tsi); - platform_set_drvdata(pdev, NULL); - return 0; } -- cgit v1.2.3 From fb1b1957ae361ce45042e2e388d9c308bce82099 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Sun, 5 May 2013 19:38:48 -0700 Subject: Input: intel-mid-touch - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/intel-mid-touch.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/input/touchscreen/intel-mid-touch.c b/drivers/input/touchscreen/intel-mid-touch.c index 465db5dba8b4..e30d837dae2f 100644 --- a/drivers/input/touchscreen/intel-mid-touch.c +++ b/drivers/input/touchscreen/intel-mid-touch.c @@ -651,8 +651,6 @@ static int mrstouch_remove(struct platform_device *pdev) input_unregister_device(tsdev->input); kfree(tsdev); - platform_set_drvdata(pdev, NULL); - return 0; } -- cgit v1.2.3 From 34f22fb2ffcf88415d922520849104c867f87c02 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Sun, 5 May 2013 19:39:04 -0700 Subject: Input: jornada720_ts - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/jornada720_ts.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/input/touchscreen/jornada720_ts.c b/drivers/input/touchscreen/jornada720_ts.c index 282d7c7ad2fc..e463a79ffecc 100644 --- a/drivers/input/touchscreen/jornada720_ts.c +++ b/drivers/input/touchscreen/jornada720_ts.c @@ -145,7 +145,6 @@ static int jornada720_ts_probe(struct platform_device *pdev) fail2: free_irq(IRQ_GPIO9, pdev); fail1: - platform_set_drvdata(pdev, NULL); input_free_device(input_dev); kfree(jornada_ts); return error; @@ -156,7 +155,6 @@ static int jornada720_ts_remove(struct platform_device *pdev) struct jornada_ts *jornada_ts = platform_get_drvdata(pdev); free_irq(IRQ_GPIO9, pdev); - platform_set_drvdata(pdev, NULL); input_unregister_device(jornada_ts->dev); kfree(jornada_ts); -- cgit v1.2.3 From 797744421b3f375a5cf3fdb2b97893c67bac5ac8 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Sun, 5 May 2013 19:39:18 -0700 Subject: Input: mc13783_ts - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/mc13783_ts.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/input/touchscreen/mc13783_ts.c b/drivers/input/touchscreen/mc13783_ts.c index 89308fe38752..d6f099c47f84 100644 --- a/drivers/input/touchscreen/mc13783_ts.c +++ b/drivers/input/touchscreen/mc13783_ts.c @@ -233,8 +233,6 @@ static int mc13783_ts_remove(struct platform_device *pdev) { struct mc13783_ts_priv *priv = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); - destroy_workqueue(priv->workq); input_unregister_device(priv->idev); kfree(priv); -- cgit v1.2.3 From 12aa9357b913faf79b0a0df62a4e4200ee638b8c Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Sun, 5 May 2013 19:41:05 -0700 Subject: Input: ti_am335x_tsc - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ti_am335x_tsc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c index 51e7b87827a4..50fb1293874e 100644 --- a/drivers/input/touchscreen/ti_am335x_tsc.c +++ b/drivers/input/touchscreen/ti_am335x_tsc.c @@ -336,7 +336,6 @@ static int titsc_remove(struct platform_device *pdev) input_unregister_device(ts_dev->input); - platform_set_drvdata(pdev, NULL); kfree(ts_dev); return 0; } -- cgit v1.2.3 From e8ecfd62e7297b1f36cb86ac18ca0508f02c65d5 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Sun, 5 May 2013 19:41:20 -0700 Subject: Input: tnetv107x-ts - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/tnetv107x-ts.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/input/touchscreen/tnetv107x-ts.c b/drivers/input/touchscreen/tnetv107x-ts.c index acfb87607b87..c47827a26e3c 100644 --- a/drivers/input/touchscreen/tnetv107x-ts.c +++ b/drivers/input/touchscreen/tnetv107x-ts.c @@ -351,7 +351,6 @@ error_clk: error_map: release_mem_region(ts->res->start, resource_size(ts->res)); error_res: - platform_set_drvdata(pdev, NULL); kfree(ts); return error; @@ -366,7 +365,6 @@ static int tsc_remove(struct platform_device *pdev) 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; -- cgit v1.2.3 From 690351d63589caa7d5b38d6b5e0b71a037ed77c0 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Sun, 5 May 2013 19:41:38 -0700 Subject: Input: altera_ps2 - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Acked-by: Thomas Chou Signed-off-by: Dmitry Torokhov --- drivers/input/serio/altera_ps2.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/input/serio/altera_ps2.c b/drivers/input/serio/altera_ps2.c index 479ce5fe8955..a0a2657e31ff 100644 --- a/drivers/input/serio/altera_ps2.c +++ b/drivers/input/serio/altera_ps2.c @@ -163,7 +163,6 @@ static int altera_ps2_remove(struct platform_device *pdev) { struct ps2if *ps2if = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); serio_unregister_port(ps2if->io); free_irq(ps2if->irq, ps2if); iounmap(ps2if->base); -- cgit v1.2.3 From 8be2d66b48c8831b70556e5291e5ad5a8fa8c5a5 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Sun, 5 May 2013 19:42:04 -0700 Subject: Input: at32psif - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Acked-by: Hans-Christian Egtvedt Signed-off-by: Dmitry Torokhov --- drivers/input/serio/at32psif.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/input/serio/at32psif.c b/drivers/input/serio/at32psif.c index 190ce35af7df..3290b287ac4b 100644 --- a/drivers/input/serio/at32psif.c +++ b/drivers/input/serio/at32psif.c @@ -314,8 +314,6 @@ static int __exit psif_remove(struct platform_device *pdev) clk_put(psif->pclk); kfree(psif); - platform_set_drvdata(pdev, NULL); - return 0; } -- cgit v1.2.3 From 3c02d66b32679cd713b91de6827ee09fe26cf75b Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Sun, 5 May 2013 19:42:33 -0700 Subject: Input: q40kbd - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Dmitry Torokhov --- drivers/input/serio/q40kbd.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/input/serio/q40kbd.c b/drivers/input/serio/q40kbd.c index 436a3433f8e5..7a65a1bc5226 100644 --- a/drivers/input/serio/q40kbd.c +++ b/drivers/input/serio/q40kbd.c @@ -181,7 +181,6 @@ static int q40kbd_remove(struct platform_device *pdev) free_irq(Q40_IRQ_KEYBOARD, q40kbd); kfree(q40kbd); - platform_set_drvdata(pdev, NULL); return 0; } -- cgit v1.2.3 From 66b0f52c1ee8a88c54e150352995d53832e78d43 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Sun, 5 May 2013 20:00:22 -0700 Subject: Input: amimouse - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/amimouse.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/input/mouse/amimouse.c b/drivers/input/mouse/amimouse.c index b55d5af217a7..62ec52b2e347 100644 --- a/drivers/input/mouse/amimouse.c +++ b/drivers/input/mouse/amimouse.c @@ -133,7 +133,6 @@ static int __exit amimouse_remove(struct platform_device *pdev) { struct input_dev *dev = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); input_unregister_device(dev); return 0; } -- cgit v1.2.3 From 3e056a38894481e278b8d504cf7ffc8a64d5e9e0 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Sun, 5 May 2013 20:00:38 -0700 Subject: Input: gpio_mouse - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Acked-by: Hans-Christian Egtvedt Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/gpio_mouse.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/input/mouse/gpio_mouse.c b/drivers/input/mouse/gpio_mouse.c index 532eaca4cc56..6b44413f54e3 100644 --- a/drivers/input/mouse/gpio_mouse.c +++ b/drivers/input/mouse/gpio_mouse.c @@ -138,7 +138,6 @@ static int gpio_mouse_probe(struct platform_device *pdev) out_free_polldev: input_free_polled_device(input_poll); - platform_set_drvdata(pdev, NULL); out_free_gpios: while (--i >= 0) { @@ -165,8 +164,6 @@ static int gpio_mouse_remove(struct platform_device *pdev) gpio_free(pin); } - platform_set_drvdata(pdev, NULL); - return 0; } -- cgit v1.2.3 From 21a8e6a4853b2ed39fa4c5188a710f2cf1b92026 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 10 Apr 2013 23:28:35 +0200 Subject: drm/i915: don't setup hdmi for port D edp in ddi_init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dp_init_connector adjusts the encoder type if it is a eDP panel. Use that to decide whether we should set up a hdmi connector or not. To do so reorder the hdmi connector setup sequence in ddi_init a bit. Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ddi.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 3ff4de6c6009..d7a33856a61a 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -1484,16 +1484,6 @@ void intel_ddi_init(struct drm_device *dev, enum port port) return; } - if (port != PORT_A) { - hdmi_connector = kzalloc(sizeof(struct intel_connector), - GFP_KERNEL); - if (!hdmi_connector) { - kfree(dp_connector); - kfree(intel_dig_port); - return; - } - } - intel_encoder = &intel_dig_port->base; encoder = &intel_encoder->base; @@ -1511,8 +1501,6 @@ void intel_ddi_init(struct drm_device *dev, enum port port) intel_dig_port->port = port; intel_dig_port->port_reversal = I915_READ(DDI_BUF_CTL(port)) & DDI_BUF_PORT_REVERSAL; - if (hdmi_connector) - intel_dig_port->hdmi.hdmi_reg = DDI_BUF_CTL(port); intel_dig_port->dp.output_reg = DDI_BUF_CTL(port); intel_encoder->type = INTEL_OUTPUT_UNKNOWN; @@ -1520,7 +1508,16 @@ void intel_ddi_init(struct drm_device *dev, enum port port) intel_encoder->cloneable = false; intel_encoder->hot_plug = intel_ddi_hot_plug; - if (hdmi_connector) - intel_hdmi_init_connector(intel_dig_port, hdmi_connector); intel_dp_init_connector(intel_dig_port, dp_connector); + + if (intel_encoder->type != INTEL_OUTPUT_EDP) { + hdmi_connector = kzalloc(sizeof(struct intel_connector), + GFP_KERNEL); + if (!hdmi_connector) { + return; + } + + intel_dig_port->hdmi.hdmi_reg = DDI_BUF_CTL(port); + intel_hdmi_init_connector(intel_dig_port, hdmi_connector); + } } -- cgit v1.2.3 From 112522f6789581824903f6f72082b5b841a7f0f9 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 2 May 2013 16:48:07 +0300 Subject: drm/i915: put context upon switching In order to be notified of when the context and all of its associated objects is idle (for if the context maps to a ppgtt) we need a callback from the retire handler. We can arrange this by using the kref_get/put of the context for request tracking and by inserting a request to demarque the switch away from the old context. [Ben: fixed minor error to patch compile, AND s/last_context/from/] Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_context.c | 37 +++++++++++++++++++++------------ drivers/gpu/drm/i915/intel_ringbuffer.h | 2 +- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index 280617ef4996..efa0ede5c679 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -361,13 +361,13 @@ mi_set_context(struct intel_ring_buffer *ring, static int do_switch(struct i915_hw_context *to) { struct intel_ring_buffer *ring = to->ring; - struct drm_i915_gem_object *from_obj = ring->last_context_obj; + struct i915_hw_context *from = ring->last_context; u32 hw_flags = 0; int ret; - BUG_ON(from_obj != NULL && from_obj->pin_count == 0); + BUG_ON(from != NULL && from->obj != NULL && from->obj->pin_count == 0); - if (from_obj == to->obj) + if (from == to) return 0; ret = i915_gem_object_pin(to->obj, CONTEXT_ALIGN, false, false); @@ -390,7 +390,7 @@ static int do_switch(struct i915_hw_context *to) if (!to->is_initialized || is_default_context(to)) hw_flags |= MI_RESTORE_INHIBIT; - else if (WARN_ON_ONCE(from_obj == to->obj)) /* not yet expected */ + else if (WARN_ON_ONCE(from == to)) /* not yet expected */ hw_flags |= MI_FORCE_RESTORE; ret = mi_set_context(ring, to, hw_flags); @@ -405,9 +405,9 @@ static int do_switch(struct i915_hw_context *to) * is a bit suboptimal because the retiring can occur simply after the * MI_SET_CONTEXT instead of when the next seqno has completed. */ - if (from_obj != NULL) { - from_obj->base.read_domains = I915_GEM_DOMAIN_INSTRUCTION; - i915_gem_object_move_to_active(from_obj, ring); + if (from != NULL) { + from->obj->base.read_domains = I915_GEM_DOMAIN_INSTRUCTION; + i915_gem_object_move_to_active(from->obj, ring); /* As long as MI_SET_CONTEXT is serializing, ie. it flushes the * whole damn pipeline, we don't need to explicitly mark the * object dirty. The only exception is that the context must be @@ -415,15 +415,26 @@ static int do_switch(struct i915_hw_context *to) * able to defer doing this until we know the object would be * swapped, but there is no way to do that yet. */ - from_obj->dirty = 1; - BUG_ON(from_obj->ring != ring); - i915_gem_object_unpin(from_obj); + from->obj->dirty = 1; + BUG_ON(from->obj->ring != ring); + + ret = i915_add_request(ring, NULL, NULL); + if (ret) { + /* Too late, we've already scheduled a context switch. + * Try to undo the change so that the hw state is + * consistent with out tracking. In case of emergency, + * scream. + */ + WARN_ON(mi_set_context(ring, from, MI_RESTORE_INHIBIT)); + return ret; + } - drm_gem_object_unreference(&from_obj->base); + i915_gem_object_unpin(from->obj); + i915_gem_context_unreference(from); } - drm_gem_object_reference(&to->obj->base); - ring->last_context_obj = to->obj; + i915_gem_context_reference(to); + ring->last_context = to; to->is_initialized = true; return 0; diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index d66208c2c48b..dac1614a1bca 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -135,7 +135,7 @@ struct intel_ring_buffer { */ bool itlb_before_ctx_switch; struct i915_hw_context *default_context; - struct drm_i915_gem_object *last_context_obj; + struct i915_hw_context *last_context; void *private; }; -- cgit v1.2.3 From 0e50e96bf2d89c3415cb68aead301f485938f1ca Mon Sep 17 00:00:00 2001 From: Mika Kuoppala Date: Thu, 2 May 2013 16:48:08 +0300 Subject: drm/i915: add context into request struct Storing context reference into request struct allows us to inspect context and its associated objects when requests are retired. Both ppgtt and arb robustness work will need this. Signed-off-by: Mika Kuoppala Reviewed-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 3 +++ drivers/gpu/drm/i915/i915_gem.c | 24 ++++++++++++++++++------ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 3ac71db78e9e..ca0b0ce6e276 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1270,6 +1270,9 @@ struct drm_i915_gem_request { /** Postion in the ringbuffer of the end of the request */ u32 tail; + /** Context related to this request */ + struct i915_hw_context *ctx; + /** Time at which this request was emitted, in jiffies. */ unsigned long emitted_jiffies; diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 6be940effefd..8a81d1a21050 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2042,6 +2042,11 @@ i915_add_request(struct intel_ring_buffer *ring, request->seqno = intel_ring_get_seqno(ring); request->ring = ring; request->tail = request_ring_position; + request->ctx = ring->last_context; + + if (request->ctx) + i915_gem_context_reference(request->ctx); + request->emitted_jiffies = jiffies; was_empty = list_empty(&ring->request_list); list_add_tail(&request->list, &ring->request_list); @@ -2094,6 +2099,17 @@ i915_gem_request_remove_from_client(struct drm_i915_gem_request *request) spin_unlock(&file_priv->mm.lock); } +static void i915_gem_free_request(struct drm_i915_gem_request *request) +{ + list_del(&request->list); + i915_gem_request_remove_from_client(request); + + if (request->ctx) + i915_gem_context_unreference(request->ctx); + + kfree(request); +} + static void i915_gem_reset_ring_lists(struct drm_i915_private *dev_priv, struct intel_ring_buffer *ring) { @@ -2104,9 +2120,7 @@ static void i915_gem_reset_ring_lists(struct drm_i915_private *dev_priv, struct drm_i915_gem_request, list); - list_del(&request->list); - i915_gem_request_remove_from_client(request); - kfree(request); + i915_gem_free_request(request); } while (!list_empty(&ring->active_list)) { @@ -2198,9 +2212,7 @@ i915_gem_retire_requests_ring(struct intel_ring_buffer *ring) */ ring->last_retired_head = request->tail; - list_del(&request->list); - i915_gem_request_remove_from_client(request); - kfree(request); + i915_gem_free_request(request); } /* Move any buffers on the active list that are no longer referenced -- cgit v1.2.3 From 4d8a62eac3caad710ef030aab25248d56693a8f1 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 3 May 2013 11:49:51 +0200 Subject: drm/i915: fix up adjusted_mode tracking for interlaced modes With the hw state readout&check code it's important that the values we keep around are the canonical ones. Unfortunately when adding the pipe timings readout support I've missed that the write side adjusts the timings in the pipe config. Fix this up and so prevent the unsightly WARN noise in dmesg. This regression has been introduced in commit 1bd1bd806037af04dd1d7bdd39b2b04090c10d2c Author: Daniel Vetter Date: Mon Apr 29 21:56:12 2013 +0200 drm/i915: hw state readout support for pipe timings Reported-by: Paulo Zanoni Cc: Mika Kuoppala Reviewed-by: Paulo Zanoni Tested-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index de8be7595a62..3a691a13f9e6 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4675,12 +4675,17 @@ static void intel_set_pipe_timings(struct intel_crtc *intel_crtc, struct drm_i915_private *dev_priv = dev->dev_private; enum pipe pipe = intel_crtc->pipe; enum transcoder cpu_transcoder = intel_crtc->config.cpu_transcoder; - uint32_t vsyncshift; + uint32_t vsyncshift, crtc_vtotal, crtc_vblank_end; + + /* We need to be careful not to changed the adjusted mode, for otherwise + * the hw state checker will get angry at the mismatch. */ + crtc_vtotal = adjusted_mode->crtc_vtotal; + crtc_vblank_end = adjusted_mode->crtc_vblank_end; if (!IS_GEN2(dev) && adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) { /* the chip adds 2 halflines automatically */ - adjusted_mode->crtc_vtotal -= 1; - adjusted_mode->crtc_vblank_end -= 1; + crtc_vtotal -= 1; + crtc_vblank_end -= 1; vsyncshift = adjusted_mode->crtc_hsync_start - adjusted_mode->crtc_htotal / 2; } else { @@ -4702,10 +4707,10 @@ static void intel_set_pipe_timings(struct intel_crtc *intel_crtc, I915_WRITE(VTOTAL(cpu_transcoder), (adjusted_mode->crtc_vdisplay - 1) | - ((adjusted_mode->crtc_vtotal - 1) << 16)); + ((crtc_vtotal - 1) << 16)); I915_WRITE(VBLANK(cpu_transcoder), (adjusted_mode->crtc_vblank_start - 1) | - ((adjusted_mode->crtc_vblank_end - 1) << 16)); + ((crtc_vblank_end - 1) << 16)); I915_WRITE(VSYNC(cpu_transcoder), (adjusted_mode->crtc_vsync_start - 1) | ((adjusted_mode->crtc_vsync_end - 1) << 16)); -- cgit v1.2.3 From ab9412ba06484cdfd82bdb748689024efe2221fe Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 3 May 2013 11:49:46 +0200 Subject: drm/i915: s/TRANSCONF/PCH_TRANSCONF/ Every time I read hsw code I get completely confused about this. So call it what it is more explicitly. Also, add an LPT_TRANSCONF for the pch transcoder A and use it in lpt-only code, to really unconfuse me. v2: s/plane/pipe/ in the TRANSCONF #define (Paulo). Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 7 ++++--- drivers/gpu/drm/i915/i915_ums.c | 8 ++++---- drivers/gpu/drm/i915/intel_display.c | 30 +++++++++++++++--------------- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index aec569f13186..fd5601998982 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -4060,9 +4060,10 @@ #define TRANSDPLINK_M2(pipe) _PIPE(pipe, _TRANSA_DP_LINK_M2, _TRANSB_DP_LINK_M2) #define TRANSDPLINK_N2(pipe) _PIPE(pipe, _TRANSA_DP_LINK_N2, _TRANSB_DP_LINK_N2) -#define _TRANSACONF 0xf0008 -#define _TRANSBCONF 0xf1008 -#define TRANSCONF(plane) _PIPE(plane, _TRANSACONF, _TRANSBCONF) +#define _PCH_TRANSACONF 0xf0008 +#define _PCH_TRANSBCONF 0xf1008 +#define PCH_TRANSCONF(pipe) _PIPE(pipe, _PCH_TRANSACONF, _PCH_TRANSBCONF) +#define LPT_TRANSCONF _PCH_TRANSACONF /* lpt has only one transcoder */ #define TRANS_DISABLE (0<<31) #define TRANS_ENABLE (1<<31) #define TRANS_STATE_MASK (1<<30) diff --git a/drivers/gpu/drm/i915/i915_ums.c b/drivers/gpu/drm/i915/i915_ums.c index 985a09716237..75960dd81b5a 100644 --- a/drivers/gpu/drm/i915/i915_ums.c +++ b/drivers/gpu/drm/i915/i915_ums.c @@ -148,7 +148,7 @@ void i915_save_display_reg(struct drm_device *dev) dev_priv->regfile.savePFA_WIN_SZ = I915_READ(_PFA_WIN_SZ); dev_priv->regfile.savePFA_WIN_POS = I915_READ(_PFA_WIN_POS); - dev_priv->regfile.saveTRANSACONF = I915_READ(_TRANSACONF); + dev_priv->regfile.saveTRANSACONF = I915_READ(_PCH_TRANSACONF); dev_priv->regfile.saveTRANS_HTOTAL_A = I915_READ(_TRANS_HTOTAL_A); dev_priv->regfile.saveTRANS_HBLANK_A = I915_READ(_TRANS_HBLANK_A); dev_priv->regfile.saveTRANS_HSYNC_A = I915_READ(_TRANS_HSYNC_A); @@ -205,7 +205,7 @@ void i915_save_display_reg(struct drm_device *dev) dev_priv->regfile.savePFB_WIN_SZ = I915_READ(_PFB_WIN_SZ); dev_priv->regfile.savePFB_WIN_POS = I915_READ(_PFB_WIN_POS); - dev_priv->regfile.saveTRANSBCONF = I915_READ(_TRANSBCONF); + dev_priv->regfile.saveTRANSBCONF = I915_READ(_PCH_TRANSBCONF); dev_priv->regfile.saveTRANS_HTOTAL_B = I915_READ(_TRANS_HTOTAL_B); dev_priv->regfile.saveTRANS_HBLANK_B = I915_READ(_TRANS_HBLANK_B); dev_priv->regfile.saveTRANS_HSYNC_B = I915_READ(_TRANS_HSYNC_B); @@ -379,7 +379,7 @@ void i915_restore_display_reg(struct drm_device *dev) I915_WRITE(_PFA_WIN_SZ, dev_priv->regfile.savePFA_WIN_SZ); I915_WRITE(_PFA_WIN_POS, dev_priv->regfile.savePFA_WIN_POS); - I915_WRITE(_TRANSACONF, dev_priv->regfile.saveTRANSACONF); + I915_WRITE(_PCH_TRANSACONF, dev_priv->regfile.saveTRANSACONF); I915_WRITE(_TRANS_HTOTAL_A, dev_priv->regfile.saveTRANS_HTOTAL_A); I915_WRITE(_TRANS_HBLANK_A, dev_priv->regfile.saveTRANS_HBLANK_A); I915_WRITE(_TRANS_HSYNC_A, dev_priv->regfile.saveTRANS_HSYNC_A); @@ -448,7 +448,7 @@ void i915_restore_display_reg(struct drm_device *dev) I915_WRITE(_PFB_WIN_SZ, dev_priv->regfile.savePFB_WIN_SZ); I915_WRITE(_PFB_WIN_POS, dev_priv->regfile.savePFB_WIN_POS); - I915_WRITE(_TRANSBCONF, dev_priv->regfile.saveTRANSBCONF); + I915_WRITE(_PCH_TRANSBCONF, dev_priv->regfile.saveTRANSBCONF); I915_WRITE(_TRANS_HTOTAL_B, dev_priv->regfile.saveTRANS_HTOTAL_B); I915_WRITE(_TRANS_HBLANK_B, dev_priv->regfile.saveTRANS_HBLANK_B); I915_WRITE(_TRANS_HSYNC_B, dev_priv->regfile.saveTRANS_HSYNC_B); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 3a691a13f9e6..d2dfe90213af 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1206,14 +1206,14 @@ static void assert_pch_refclk_enabled(struct drm_i915_private *dev_priv) WARN(!enabled, "PCH refclk assertion failure, should be active but is disabled\n"); } -static void assert_transcoder_disabled(struct drm_i915_private *dev_priv, - enum pipe pipe) +static void assert_pch_transcoder_disabled(struct drm_i915_private *dev_priv, + enum pipe pipe) { int reg; u32 val; bool enabled; - reg = TRANSCONF(pipe); + reg = PCH_TRANSCONF(pipe); val = I915_READ(reg); enabled = !!(val & TRANS_ENABLE); WARN(enabled, @@ -1565,7 +1565,7 @@ static void intel_disable_pch_pll(struct intel_crtc *intel_crtc) DRM_DEBUG_KMS("disabling PCH PLL %x\n", pll->pll_reg); /* Make sure transcoder isn't still depending on us */ - assert_transcoder_disabled(dev_priv, intel_crtc->pipe); + assert_pch_transcoder_disabled(dev_priv, intel_crtc->pipe); reg = pll->pll_reg; val = I915_READ(reg); @@ -1605,7 +1605,7 @@ static void ironlake_enable_pch_transcoder(struct drm_i915_private *dev_priv, I915_WRITE(reg, val); } - reg = TRANSCONF(pipe); + reg = PCH_TRANSCONF(pipe); val = I915_READ(reg); pipeconf_val = I915_READ(PIPECONF(pipe)); @@ -1659,8 +1659,8 @@ static void lpt_enable_pch_transcoder(struct drm_i915_private *dev_priv, else val |= TRANS_PROGRESSIVE; - I915_WRITE(TRANSCONF(TRANSCODER_A), val); - if (wait_for(I915_READ(_TRANSACONF) & TRANS_STATE_ENABLE, 100)) + I915_WRITE(LPT_TRANSCONF, val); + if (wait_for(I915_READ(LPT_TRANSCONF) & TRANS_STATE_ENABLE, 100)) DRM_ERROR("Failed to enable PCH transcoder\n"); } @@ -1677,7 +1677,7 @@ static void ironlake_disable_pch_transcoder(struct drm_i915_private *dev_priv, /* Ports must be off as well */ assert_pch_ports_disabled(dev_priv, pipe); - reg = TRANSCONF(pipe); + reg = PCH_TRANSCONF(pipe); val = I915_READ(reg); val &= ~TRANS_ENABLE; I915_WRITE(reg, val); @@ -1698,11 +1698,11 @@ static void lpt_disable_pch_transcoder(struct drm_i915_private *dev_priv) { u32 val; - val = I915_READ(_TRANSACONF); + val = I915_READ(LPT_TRANSCONF); val &= ~TRANS_ENABLE; - I915_WRITE(_TRANSACONF, val); + I915_WRITE(LPT_TRANSCONF, val); /* wait for PCH transcoder off, transcoder state */ - if (wait_for((I915_READ(_TRANSACONF) & TRANS_STATE_ENABLE) == 0, 50)) + if (wait_for((I915_READ(LPT_TRANSCONF) & TRANS_STATE_ENABLE) == 0, 50)) DRM_ERROR("Failed to disable PCH transcoder\n"); /* Workaround: clear timing override bit. */ @@ -3011,7 +3011,7 @@ static void ironlake_pch_enable(struct drm_crtc *crtc) int pipe = intel_crtc->pipe; u32 reg, temp; - assert_transcoder_disabled(dev_priv, pipe); + assert_pch_transcoder_disabled(dev_priv, pipe); /* Write the TU size bits before fdi link training, so that error * detection works. */ @@ -3115,7 +3115,7 @@ static void lpt_pch_enable(struct drm_crtc *crtc) struct intel_crtc *intel_crtc = to_intel_crtc(crtc); enum transcoder cpu_transcoder = intel_crtc->config.cpu_transcoder; - assert_transcoder_disabled(dev_priv, TRANSCODER_A); + assert_pch_transcoder_disabled(dev_priv, TRANSCODER_A); lpt_program_iclkip(crtc); @@ -5894,7 +5894,7 @@ static bool ironlake_get_pipe_config(struct intel_crtc *crtc, if (!(tmp & PIPECONF_ENABLE)) return false; - if (I915_READ(TRANSCONF(crtc->pipe)) & TRANS_ENABLE) { + if (I915_READ(PCH_TRANSCONF(crtc->pipe)) & TRANS_ENABLE) { pipe_config->has_pch_encoder = true; tmp = I915_READ(FDI_RX_CTL(crtc->pipe)); @@ -6042,7 +6042,7 @@ static bool haswell_get_pipe_config(struct intel_crtc *crtc, */ tmp = I915_READ(TRANS_DDI_FUNC_CTL(cpu_transcoder)); if ((tmp & TRANS_DDI_PORT_MASK) == TRANS_DDI_SELECT_PORT(PORT_E) && - I915_READ(TRANSCONF(PIPE_A)) & TRANS_ENABLE) { + I915_READ(LPT_TRANSCONF) & TRANS_ENABLE) { pipe_config->has_pch_encoder = true; tmp = I915_READ(FDI_RX_CTL(PIPE_A)); -- cgit v1.2.3 From 275f01b2694a52d13c32358d17d594ec9aba55e3 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 3 May 2013 11:49:47 +0200 Subject: drm/i915: PCH_ prefix for transcoder timings While at it, also extract a common helper to copy the timings from the cpu transcoder to the pch transcoder. That way it's really explicit how the lpt transcoder is hardcoded. v2: - Re-align #defines properly (Paulo). - Use cpu_transcoder when copying pipe timings (Paulo). - s/intel_pch_transcoder_enable/intel_pch_transcoder_set_timings/ since we already have a pch transcoder enable function, and this is clearer, too. - Fixup 80 char line overflow in intel_display.c. I've opted to ignore this in i915_reg.h and i915_ums.c since meh. Cc: Paulo Zanoni Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 70 ++++++++++++++++++------------------ drivers/gpu/drm/i915/i915_ums.c | 48 ++++++++++++------------- drivers/gpu/drm/i915/intel_display.c | 42 +++++++++++++--------- 3 files changed, 85 insertions(+), 75 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index fd5601998982..e888fcca6ee6 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -3929,25 +3929,25 @@ /* transcoder */ -#define _TRANS_HTOTAL_A 0xe0000 -#define TRANS_HTOTAL_SHIFT 16 -#define TRANS_HACTIVE_SHIFT 0 -#define _TRANS_HBLANK_A 0xe0004 -#define TRANS_HBLANK_END_SHIFT 16 -#define TRANS_HBLANK_START_SHIFT 0 -#define _TRANS_HSYNC_A 0xe0008 -#define TRANS_HSYNC_END_SHIFT 16 -#define TRANS_HSYNC_START_SHIFT 0 -#define _TRANS_VTOTAL_A 0xe000c -#define TRANS_VTOTAL_SHIFT 16 -#define TRANS_VACTIVE_SHIFT 0 -#define _TRANS_VBLANK_A 0xe0010 -#define TRANS_VBLANK_END_SHIFT 16 -#define TRANS_VBLANK_START_SHIFT 0 -#define _TRANS_VSYNC_A 0xe0014 -#define TRANS_VSYNC_END_SHIFT 16 -#define TRANS_VSYNC_START_SHIFT 0 -#define _TRANS_VSYNCSHIFT_A 0xe0028 +#define _PCH_TRANS_HTOTAL_A 0xe0000 +#define TRANS_HTOTAL_SHIFT 16 +#define TRANS_HACTIVE_SHIFT 0 +#define _PCH_TRANS_HBLANK_A 0xe0004 +#define TRANS_HBLANK_END_SHIFT 16 +#define TRANS_HBLANK_START_SHIFT 0 +#define _PCH_TRANS_HSYNC_A 0xe0008 +#define TRANS_HSYNC_END_SHIFT 16 +#define TRANS_HSYNC_START_SHIFT 0 +#define _PCH_TRANS_VTOTAL_A 0xe000c +#define TRANS_VTOTAL_SHIFT 16 +#define TRANS_VACTIVE_SHIFT 0 +#define _PCH_TRANS_VBLANK_A 0xe0010 +#define TRANS_VBLANK_END_SHIFT 16 +#define TRANS_VBLANK_START_SHIFT 0 +#define _PCH_TRANS_VSYNC_A 0xe0014 +#define TRANS_VSYNC_END_SHIFT 16 +#define TRANS_VSYNC_START_SHIFT 0 +#define _PCH_TRANS_VSYNCSHIFT_A 0xe0028 #define _TRANSA_DATA_M1 0xe0030 #define _TRANSA_DATA_N1 0xe0034 @@ -4025,22 +4025,22 @@ #define HSW_TVIDEO_DIP_VSC_DATA(trans) \ _TRANSCODER(trans, HSW_VIDEO_DIP_VSC_DATA_A, HSW_VIDEO_DIP_VSC_DATA_B) -#define _TRANS_HTOTAL_B 0xe1000 -#define _TRANS_HBLANK_B 0xe1004 -#define _TRANS_HSYNC_B 0xe1008 -#define _TRANS_VTOTAL_B 0xe100c -#define _TRANS_VBLANK_B 0xe1010 -#define _TRANS_VSYNC_B 0xe1014 -#define _TRANS_VSYNCSHIFT_B 0xe1028 - -#define TRANS_HTOTAL(pipe) _PIPE(pipe, _TRANS_HTOTAL_A, _TRANS_HTOTAL_B) -#define TRANS_HBLANK(pipe) _PIPE(pipe, _TRANS_HBLANK_A, _TRANS_HBLANK_B) -#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 TRANS_VSYNCSHIFT(pipe) _PIPE(pipe, _TRANS_VSYNCSHIFT_A, \ - _TRANS_VSYNCSHIFT_B) +#define _PCH_TRANS_HTOTAL_B 0xe1000 +#define _PCH_TRANS_HBLANK_B 0xe1004 +#define _PCH_TRANS_HSYNC_B 0xe1008 +#define _PCH_TRANS_VTOTAL_B 0xe100c +#define _PCH_TRANS_VBLANK_B 0xe1010 +#define _PCH_TRANS_VSYNC_B 0xe1014 +#define _PCH_TRANS_VSYNCSHIFT_B 0xe1028 + +#define PCH_TRANS_HTOTAL(pipe) _PIPE(pipe, _PCH_TRANS_HTOTAL_A, _PCH_TRANS_HTOTAL_B) +#define PCH_TRANS_HBLANK(pipe) _PIPE(pipe, _PCH_TRANS_HBLANK_A, _PCH_TRANS_HBLANK_B) +#define PCH_TRANS_HSYNC(pipe) _PIPE(pipe, _PCH_TRANS_HSYNC_A, _PCH_TRANS_HSYNC_B) +#define PCH_TRANS_VTOTAL(pipe) _PIPE(pipe, _PCH_TRANS_VTOTAL_A, _PCH_TRANS_VTOTAL_B) +#define PCH_TRANS_VBLANK(pipe) _PIPE(pipe, _PCH_TRANS_VBLANK_A, _PCH_TRANS_VBLANK_B) +#define PCH_TRANS_VSYNC(pipe) _PIPE(pipe, _PCH_TRANS_VSYNC_A, _PCH_TRANS_VSYNC_B) +#define PCH_TRANS_VSYNCSHIFT(pipe) _PIPE(pipe, _PCH_TRANS_VSYNCSHIFT_A, \ + _PCH_TRANS_VSYNCSHIFT_B) #define _TRANSB_DATA_M1 0xe1030 #define _TRANSB_DATA_N1 0xe1034 diff --git a/drivers/gpu/drm/i915/i915_ums.c b/drivers/gpu/drm/i915/i915_ums.c index 75960dd81b5a..4168d2b6b4f1 100644 --- a/drivers/gpu/drm/i915/i915_ums.c +++ b/drivers/gpu/drm/i915/i915_ums.c @@ -149,12 +149,12 @@ void i915_save_display_reg(struct drm_device *dev) dev_priv->regfile.savePFA_WIN_POS = I915_READ(_PFA_WIN_POS); dev_priv->regfile.saveTRANSACONF = I915_READ(_PCH_TRANSACONF); - dev_priv->regfile.saveTRANS_HTOTAL_A = I915_READ(_TRANS_HTOTAL_A); - dev_priv->regfile.saveTRANS_HBLANK_A = I915_READ(_TRANS_HBLANK_A); - dev_priv->regfile.saveTRANS_HSYNC_A = I915_READ(_TRANS_HSYNC_A); - dev_priv->regfile.saveTRANS_VTOTAL_A = I915_READ(_TRANS_VTOTAL_A); - dev_priv->regfile.saveTRANS_VBLANK_A = I915_READ(_TRANS_VBLANK_A); - dev_priv->regfile.saveTRANS_VSYNC_A = I915_READ(_TRANS_VSYNC_A); + dev_priv->regfile.saveTRANS_HTOTAL_A = I915_READ(_PCH_TRANS_HTOTAL_A); + dev_priv->regfile.saveTRANS_HBLANK_A = I915_READ(_PCH_TRANS_HBLANK_A); + dev_priv->regfile.saveTRANS_HSYNC_A = I915_READ(_PCH_TRANS_HSYNC_A); + dev_priv->regfile.saveTRANS_VTOTAL_A = I915_READ(_PCH_TRANS_VTOTAL_A); + dev_priv->regfile.saveTRANS_VBLANK_A = I915_READ(_PCH_TRANS_VBLANK_A); + dev_priv->regfile.saveTRANS_VSYNC_A = I915_READ(_PCH_TRANS_VSYNC_A); } dev_priv->regfile.saveDSPACNTR = I915_READ(_DSPACNTR); @@ -206,12 +206,12 @@ void i915_save_display_reg(struct drm_device *dev) dev_priv->regfile.savePFB_WIN_POS = I915_READ(_PFB_WIN_POS); dev_priv->regfile.saveTRANSBCONF = I915_READ(_PCH_TRANSBCONF); - dev_priv->regfile.saveTRANS_HTOTAL_B = I915_READ(_TRANS_HTOTAL_B); - dev_priv->regfile.saveTRANS_HBLANK_B = I915_READ(_TRANS_HBLANK_B); - dev_priv->regfile.saveTRANS_HSYNC_B = I915_READ(_TRANS_HSYNC_B); - dev_priv->regfile.saveTRANS_VTOTAL_B = I915_READ(_TRANS_VTOTAL_B); - dev_priv->regfile.saveTRANS_VBLANK_B = I915_READ(_TRANS_VBLANK_B); - dev_priv->regfile.saveTRANS_VSYNC_B = I915_READ(_TRANS_VSYNC_B); + dev_priv->regfile.saveTRANS_HTOTAL_B = I915_READ(_PCH_TRANS_HTOTAL_B); + dev_priv->regfile.saveTRANS_HBLANK_B = I915_READ(_PCH_TRANS_HBLANK_B); + dev_priv->regfile.saveTRANS_HSYNC_B = I915_READ(_PCH_TRANS_HSYNC_B); + dev_priv->regfile.saveTRANS_VTOTAL_B = I915_READ(_PCH_TRANS_VTOTAL_B); + dev_priv->regfile.saveTRANS_VBLANK_B = I915_READ(_PCH_TRANS_VBLANK_B); + dev_priv->regfile.saveTRANS_VSYNC_B = I915_READ(_PCH_TRANS_VSYNC_B); } dev_priv->regfile.saveDSPBCNTR = I915_READ(_DSPBCNTR); @@ -380,12 +380,12 @@ void i915_restore_display_reg(struct drm_device *dev) I915_WRITE(_PFA_WIN_POS, dev_priv->regfile.savePFA_WIN_POS); I915_WRITE(_PCH_TRANSACONF, dev_priv->regfile.saveTRANSACONF); - I915_WRITE(_TRANS_HTOTAL_A, dev_priv->regfile.saveTRANS_HTOTAL_A); - I915_WRITE(_TRANS_HBLANK_A, dev_priv->regfile.saveTRANS_HBLANK_A); - I915_WRITE(_TRANS_HSYNC_A, dev_priv->regfile.saveTRANS_HSYNC_A); - I915_WRITE(_TRANS_VTOTAL_A, dev_priv->regfile.saveTRANS_VTOTAL_A); - I915_WRITE(_TRANS_VBLANK_A, dev_priv->regfile.saveTRANS_VBLANK_A); - I915_WRITE(_TRANS_VSYNC_A, dev_priv->regfile.saveTRANS_VSYNC_A); + I915_WRITE(_PCH_TRANS_HTOTAL_A, dev_priv->regfile.saveTRANS_HTOTAL_A); + I915_WRITE(_PCH_TRANS_HBLANK_A, dev_priv->regfile.saveTRANS_HBLANK_A); + I915_WRITE(_PCH_TRANS_HSYNC_A, dev_priv->regfile.saveTRANS_HSYNC_A); + I915_WRITE(_PCH_TRANS_VTOTAL_A, dev_priv->regfile.saveTRANS_VTOTAL_A); + I915_WRITE(_PCH_TRANS_VBLANK_A, dev_priv->regfile.saveTRANS_VBLANK_A); + I915_WRITE(_PCH_TRANS_VSYNC_A, dev_priv->regfile.saveTRANS_VSYNC_A); } /* Restore plane info */ @@ -449,12 +449,12 @@ void i915_restore_display_reg(struct drm_device *dev) I915_WRITE(_PFB_WIN_POS, dev_priv->regfile.savePFB_WIN_POS); I915_WRITE(_PCH_TRANSBCONF, dev_priv->regfile.saveTRANSBCONF); - I915_WRITE(_TRANS_HTOTAL_B, dev_priv->regfile.saveTRANS_HTOTAL_B); - I915_WRITE(_TRANS_HBLANK_B, dev_priv->regfile.saveTRANS_HBLANK_B); - I915_WRITE(_TRANS_HSYNC_B, dev_priv->regfile.saveTRANS_HSYNC_B); - I915_WRITE(_TRANS_VTOTAL_B, dev_priv->regfile.saveTRANS_VTOTAL_B); - I915_WRITE(_TRANS_VBLANK_B, dev_priv->regfile.saveTRANS_VBLANK_B); - I915_WRITE(_TRANS_VSYNC_B, dev_priv->regfile.saveTRANS_VSYNC_B); + I915_WRITE(_PCH_TRANS_HTOTAL_B, dev_priv->regfile.saveTRANS_HTOTAL_B); + I915_WRITE(_PCH_TRANS_HBLANK_B, dev_priv->regfile.saveTRANS_HBLANK_B); + I915_WRITE(_PCH_TRANS_HSYNC_B, dev_priv->regfile.saveTRANS_HSYNC_B); + I915_WRITE(_PCH_TRANS_VTOTAL_B, dev_priv->regfile.saveTRANS_VTOTAL_B); + I915_WRITE(_PCH_TRANS_VBLANK_B, dev_priv->regfile.saveTRANS_VBLANK_B); + I915_WRITE(_PCH_TRANS_VSYNC_B, dev_priv->regfile.saveTRANS_VSYNC_B); } /* Restore plane info */ diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index d2dfe90213af..9f755fd5cd1c 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2995,6 +2995,30 @@ static void lpt_program_iclkip(struct drm_crtc *crtc) mutex_unlock(&dev_priv->dpio_lock); } +static void ironlake_pch_transcoder_set_timings(struct intel_crtc *crtc, + enum pipe pch_transcoder) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + enum transcoder cpu_transcoder = crtc->config.cpu_transcoder; + + I915_WRITE(PCH_TRANS_HTOTAL(pch_transcoder), + I915_READ(HTOTAL(cpu_transcoder))); + I915_WRITE(PCH_TRANS_HBLANK(pch_transcoder), + I915_READ(HBLANK(cpu_transcoder))); + I915_WRITE(PCH_TRANS_HSYNC(pch_transcoder), + I915_READ(HSYNC(cpu_transcoder))); + + I915_WRITE(PCH_TRANS_VTOTAL(pch_transcoder), + I915_READ(VTOTAL(cpu_transcoder))); + I915_WRITE(PCH_TRANS_VBLANK(pch_transcoder), + I915_READ(VBLANK(cpu_transcoder))); + I915_WRITE(PCH_TRANS_VSYNC(pch_transcoder), + I915_READ(VSYNC(cpu_transcoder))); + I915_WRITE(PCH_TRANS_VSYNCSHIFT(pch_transcoder), + I915_READ(VSYNCSHIFT(cpu_transcoder))); +} + /* * Enable PCH resources required for PCH ports: * - PCH PLLs @@ -3058,14 +3082,7 @@ static void ironlake_pch_enable(struct drm_crtc *crtc) /* set transcoder timing, panel must allow it */ assert_panel_unlocked(dev_priv, pipe); - 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))); - I915_WRITE(TRANS_VSYNCSHIFT(pipe), I915_READ(VSYNCSHIFT(pipe))); + ironlake_pch_transcoder_set_timings(intel_crtc, pipe); intel_fdi_normal_train(crtc); @@ -3120,14 +3137,7 @@ static void lpt_pch_enable(struct drm_crtc *crtc) lpt_program_iclkip(crtc); /* Set transcoder timing. */ - I915_WRITE(_TRANS_HTOTAL_A, I915_READ(HTOTAL(cpu_transcoder))); - I915_WRITE(_TRANS_HBLANK_A, I915_READ(HBLANK(cpu_transcoder))); - I915_WRITE(_TRANS_HSYNC_A, I915_READ(HSYNC(cpu_transcoder))); - - I915_WRITE(_TRANS_VTOTAL_A, I915_READ(VTOTAL(cpu_transcoder))); - I915_WRITE(_TRANS_VBLANK_A, I915_READ(VBLANK(cpu_transcoder))); - I915_WRITE(_TRANS_VSYNC_A, I915_READ(VSYNC(cpu_transcoder))); - I915_WRITE(_TRANS_VSYNCSHIFT_A, I915_READ(VSYNCSHIFT(cpu_transcoder))); + ironlake_pch_transcoder_set_timings(intel_crtc, PIPE_A); lpt_enable_pch_transcoder(dev_priv, cpu_transcoder); } -- cgit v1.2.3 From b551842d4d92cd337cc2af27947cc53e09fe34ab Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 3 May 2013 11:49:48 +0200 Subject: drm/i915: make set_m_n functions static This is possible thanks to moving the m/n stuff into pipe_config. Unfortunately we need to move them a bit to avoid forward declarations. Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 68 ++++++++++++++++++------------------ drivers/gpu/drm/i915/intel_drv.h | 4 --- 2 files changed, 34 insertions(+), 38 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 9f755fd5cd1c..4f74a7f55183 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4382,6 +4382,40 @@ static void vlv_pllb_recal_opamp(struct drm_i915_private *dev_priv) intel_dpio_write(dev_priv, DPIO_CALIBRATION, reg_val); } +static void intel_pch_transcoder_set_m_n(struct intel_crtc *crtc, + struct intel_link_m_n *m_n) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + int pipe = crtc->pipe; + + I915_WRITE(TRANSDATA_M1(pipe), TU_SIZE(m_n->tu) | m_n->gmch_m); + I915_WRITE(TRANSDATA_N1(pipe), m_n->gmch_n); + I915_WRITE(TRANSDPLINK_M1(pipe), m_n->link_m); + I915_WRITE(TRANSDPLINK_N1(pipe), m_n->link_n); +} + +static void intel_cpu_transcoder_set_m_n(struct intel_crtc *crtc, + struct intel_link_m_n *m_n) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + int pipe = crtc->pipe; + enum transcoder transcoder = crtc->config.cpu_transcoder; + + if (INTEL_INFO(dev)->gen >= 5) { + I915_WRITE(PIPE_DATA_M1(transcoder), TU_SIZE(m_n->tu) | m_n->gmch_m); + I915_WRITE(PIPE_DATA_N1(transcoder), m_n->gmch_n); + I915_WRITE(PIPE_LINK_M1(transcoder), m_n->link_m); + I915_WRITE(PIPE_LINK_N1(transcoder), m_n->link_n); + } else { + I915_WRITE(PIPE_GMCH_DATA_M(pipe), TU_SIZE(m_n->tu) | m_n->gmch_m); + I915_WRITE(PIPE_GMCH_DATA_N(pipe), m_n->gmch_n); + I915_WRITE(PIPE_DP_LINK_M(pipe), m_n->link_m); + I915_WRITE(PIPE_DP_LINK_N(pipe), m_n->link_n); + } +} + static void intel_dp_set_m_n(struct intel_crtc *crtc) { if (crtc->config.has_pch_encoder) @@ -5606,40 +5640,6 @@ int ironlake_get_lanes_required(int target_clock, int link_bw, int bpp) return bps / (link_bw * 8) + 1; } -void intel_pch_transcoder_set_m_n(struct intel_crtc *crtc, - struct intel_link_m_n *m_n) -{ - struct drm_device *dev = crtc->base.dev; - struct drm_i915_private *dev_priv = dev->dev_private; - int pipe = crtc->pipe; - - I915_WRITE(TRANSDATA_M1(pipe), TU_SIZE(m_n->tu) | m_n->gmch_m); - I915_WRITE(TRANSDATA_N1(pipe), m_n->gmch_n); - I915_WRITE(TRANSDPLINK_M1(pipe), m_n->link_m); - I915_WRITE(TRANSDPLINK_N1(pipe), m_n->link_n); -} - -void intel_cpu_transcoder_set_m_n(struct intel_crtc *crtc, - struct intel_link_m_n *m_n) -{ - struct drm_device *dev = crtc->base.dev; - struct drm_i915_private *dev_priv = dev->dev_private; - int pipe = crtc->pipe; - enum transcoder transcoder = crtc->config.cpu_transcoder; - - if (INTEL_INFO(dev)->gen >= 5) { - I915_WRITE(PIPE_DATA_M1(transcoder), TU_SIZE(m_n->tu) | m_n->gmch_m); - I915_WRITE(PIPE_DATA_N1(transcoder), m_n->gmch_n); - I915_WRITE(PIPE_LINK_M1(transcoder), m_n->link_m); - I915_WRITE(PIPE_LINK_N1(transcoder), m_n->link_n); - } else { - I915_WRITE(PIPE_GMCH_DATA_M(pipe), TU_SIZE(m_n->tu) | m_n->gmch_m); - I915_WRITE(PIPE_GMCH_DATA_N(pipe), m_n->gmch_n); - I915_WRITE(PIPE_DP_LINK_M(pipe), m_n->link_m); - I915_WRITE(PIPE_DP_LINK_N(pipe), m_n->link_n); - } -} - static bool ironlake_needs_fb_cb_tune(struct dpll *dpll, int factor) { return i9xx_dpll_compute_m(dpll) < factor * dpll->n; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index dfcf546790bc..88890a30f356 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -719,10 +719,6 @@ extern void intel_init_clock_gating(struct drm_device *dev); extern void intel_write_eld(struct drm_encoder *encoder, struct drm_display_mode *mode); extern void intel_cpt_verify_modeset(struct drm_device *dev, int pipe); -extern void intel_cpu_transcoder_set_m_n(struct intel_crtc *crtc, - struct intel_link_m_n *m_n); -extern void intel_pch_transcoder_set_m_n(struct intel_crtc *crtc, - struct intel_link_m_n *m_n); extern void intel_prepare_ddi(struct drm_device *dev); extern void hsw_fdi_link_train(struct drm_crtc *crtc); extern void intel_ddi_init(struct drm_device *dev, enum port port); -- cgit v1.2.3 From e3b95f1eb5b9a7ecc7241a27499ae8754b845926 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 3 May 2013 11:49:49 +0200 Subject: drm/i915: Apply OCD to data/link m/n register #defines - PCH_ prefix for pch registers on ibx/cpt/ppt. - Drop the DP_ from the link defines, redundant. - Drop the GMCH from the data defines and instead give the special g4x registers a consistent _G4X postfix. v2: - Realign #defines and use tabs (Paulo). Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 74 ++++++++++++++++++------------------ drivers/gpu/drm/i915/i915_ums.c | 32 ++++++++-------- drivers/gpu/drm/i915/intel_display.c | 16 ++++---- 3 files changed, 61 insertions(+), 61 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index e888fcca6ee6..a47010331e72 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -2768,8 +2768,8 @@ * which is after the LUTs, so we want the bytes for our color format. * For our current usage, this is always 3, one byte for R, G and B. */ -#define _PIPEA_GMCH_DATA_M 0x70050 -#define _PIPEB_GMCH_DATA_M 0x71050 +#define _PIPEA_DATA_M_G4X 0x70050 +#define _PIPEB_DATA_M_G4X 0x71050 /* Transfer unit size for display port - 1, default is 0x3f (for TU size 64) */ #define PIPE_GMCH_DATA_M_TU_SIZE_MASK (0x3f << 25) @@ -2778,8 +2778,8 @@ #define PIPE_GMCH_DATA_M_MASK (0xffffff) -#define _PIPEA_GMCH_DATA_N 0x70054 -#define _PIPEB_GMCH_DATA_N 0x71054 +#define _PIPEA_DATA_N_G4X 0x70054 +#define _PIPEB_DATA_N_G4X 0x71054 #define PIPE_GMCH_DATA_N_MASK (0xffffff) /* @@ -2793,18 +2793,18 @@ * Attributes and VB-ID. */ -#define _PIPEA_DP_LINK_M 0x70060 -#define _PIPEB_DP_LINK_M 0x71060 +#define _PIPEA_LINK_M_G4X 0x70060 +#define _PIPEB_LINK_M_G4X 0x71060 #define PIPEA_DP_LINK_M_MASK (0xffffff) -#define _PIPEA_DP_LINK_N 0x70064 -#define _PIPEB_DP_LINK_N 0x71064 +#define _PIPEA_LINK_N_G4X 0x70064 +#define _PIPEB_LINK_N_G4X 0x71064 #define PIPEA_DP_LINK_N_MASK (0xffffff) -#define PIPE_GMCH_DATA_M(pipe) _PIPE(pipe, _PIPEA_GMCH_DATA_M, _PIPEB_GMCH_DATA_M) -#define PIPE_GMCH_DATA_N(pipe) _PIPE(pipe, _PIPEA_GMCH_DATA_N, _PIPEB_GMCH_DATA_N) -#define PIPE_DP_LINK_M(pipe) _PIPE(pipe, _PIPEA_DP_LINK_M, _PIPEB_DP_LINK_M) -#define PIPE_DP_LINK_N(pipe) _PIPE(pipe, _PIPEA_DP_LINK_N, _PIPEB_DP_LINK_N) +#define PIPE_DATA_M_G4X(pipe) _PIPE(pipe, _PIPEA_DATA_M_G4X, _PIPEB_DATA_M_G4X) +#define PIPE_DATA_N_G4X(pipe) _PIPE(pipe, _PIPEA_DATA_N_G4X, _PIPEB_DATA_N_G4X) +#define PIPE_LINK_M_G4X(pipe) _PIPE(pipe, _PIPEA_LINK_M_G4X, _PIPEB_LINK_M_G4X) +#define PIPE_LINK_N_G4X(pipe) _PIPE(pipe, _PIPEA_LINK_N_G4X, _PIPEB_LINK_N_G4X) /* Display & cursor control */ @@ -3949,14 +3949,14 @@ #define TRANS_VSYNC_START_SHIFT 0 #define _PCH_TRANS_VSYNCSHIFT_A 0xe0028 -#define _TRANSA_DATA_M1 0xe0030 -#define _TRANSA_DATA_N1 0xe0034 -#define _TRANSA_DATA_M2 0xe0038 -#define _TRANSA_DATA_N2 0xe003c -#define _TRANSA_DP_LINK_M1 0xe0040 -#define _TRANSA_DP_LINK_N1 0xe0044 -#define _TRANSA_DP_LINK_M2 0xe0048 -#define _TRANSA_DP_LINK_N2 0xe004c +#define _PCH_TRANSA_DATA_M1 0xe0030 +#define _PCH_TRANSA_DATA_N1 0xe0034 +#define _PCH_TRANSA_DATA_M2 0xe0038 +#define _PCH_TRANSA_DATA_N2 0xe003c +#define _PCH_TRANSA_LINK_M1 0xe0040 +#define _PCH_TRANSA_LINK_N1 0xe0044 +#define _PCH_TRANSA_LINK_M2 0xe0048 +#define _PCH_TRANSA_LINK_N2 0xe004c /* Per-transcoder DIP controls */ @@ -4042,23 +4042,23 @@ #define PCH_TRANS_VSYNCSHIFT(pipe) _PIPE(pipe, _PCH_TRANS_VSYNCSHIFT_A, \ _PCH_TRANS_VSYNCSHIFT_B) -#define _TRANSB_DATA_M1 0xe1030 -#define _TRANSB_DATA_N1 0xe1034 -#define _TRANSB_DATA_M2 0xe1038 -#define _TRANSB_DATA_N2 0xe103c -#define _TRANSB_DP_LINK_M1 0xe1040 -#define _TRANSB_DP_LINK_N1 0xe1044 -#define _TRANSB_DP_LINK_M2 0xe1048 -#define _TRANSB_DP_LINK_N2 0xe104c - -#define TRANSDATA_M1(pipe) _PIPE(pipe, _TRANSA_DATA_M1, _TRANSB_DATA_M1) -#define TRANSDATA_N1(pipe) _PIPE(pipe, _TRANSA_DATA_N1, _TRANSB_DATA_N1) -#define TRANSDATA_M2(pipe) _PIPE(pipe, _TRANSA_DATA_M2, _TRANSB_DATA_M2) -#define TRANSDATA_N2(pipe) _PIPE(pipe, _TRANSA_DATA_N2, _TRANSB_DATA_N2) -#define TRANSDPLINK_M1(pipe) _PIPE(pipe, _TRANSA_DP_LINK_M1, _TRANSB_DP_LINK_M1) -#define TRANSDPLINK_N1(pipe) _PIPE(pipe, _TRANSA_DP_LINK_N1, _TRANSB_DP_LINK_N1) -#define TRANSDPLINK_M2(pipe) _PIPE(pipe, _TRANSA_DP_LINK_M2, _TRANSB_DP_LINK_M2) -#define TRANSDPLINK_N2(pipe) _PIPE(pipe, _TRANSA_DP_LINK_N2, _TRANSB_DP_LINK_N2) +#define _PCH_TRANSB_DATA_M1 0xe1030 +#define _PCH_TRANSB_DATA_N1 0xe1034 +#define _PCH_TRANSB_DATA_M2 0xe1038 +#define _PCH_TRANSB_DATA_N2 0xe103c +#define _PCH_TRANSB_LINK_M1 0xe1040 +#define _PCH_TRANSB_LINK_N1 0xe1044 +#define _PCH_TRANSB_LINK_M2 0xe1048 +#define _PCH_TRANSB_LINK_N2 0xe104c + +#define PCH_TRANS_DATA_M1(pipe) _PIPE(pipe, _PCH_TRANSA_DATA_M1, _PCH_TRANSB_DATA_M1) +#define PCH_TRANS_DATA_N1(pipe) _PIPE(pipe, _PCH_TRANSA_DATA_N1, _PCH_TRANSB_DATA_N1) +#define PCH_TRANS_DATA_M2(pipe) _PIPE(pipe, _PCH_TRANSA_DATA_M2, _PCH_TRANSB_DATA_M2) +#define PCH_TRANS_DATA_N2(pipe) _PIPE(pipe, _PCH_TRANSA_DATA_N2, _PCH_TRANSB_DATA_N2) +#define PCH_TRANS_LINK_M1(pipe) _PIPE(pipe, _PCH_TRANSA_LINK_M1, _PCH_TRANSB_LINK_M1) +#define PCH_TRANS_LINK_N1(pipe) _PIPE(pipe, _PCH_TRANSA_LINK_N1, _PCH_TRANSB_LINK_N1) +#define PCH_TRANS_LINK_M2(pipe) _PIPE(pipe, _PCH_TRANSA_LINK_M2, _PCH_TRANSB_LINK_M2) +#define PCH_TRANS_LINK_N2(pipe) _PIPE(pipe, _PCH_TRANSA_LINK_N2, _PCH_TRANSB_LINK_N2) #define _PCH_TRANSACONF 0xf0008 #define _PCH_TRANSBCONF 0xf1008 diff --git a/drivers/gpu/drm/i915/i915_ums.c b/drivers/gpu/drm/i915/i915_ums.c index 4168d2b6b4f1..5ef30b2e6bc6 100644 --- a/drivers/gpu/drm/i915/i915_ums.c +++ b/drivers/gpu/drm/i915/i915_ums.c @@ -259,14 +259,14 @@ void i915_save_display_reg(struct drm_device *dev) dev_priv->regfile.saveDP_B = I915_READ(DP_B); dev_priv->regfile.saveDP_C = I915_READ(DP_C); dev_priv->regfile.saveDP_D = I915_READ(DP_D); - dev_priv->regfile.savePIPEA_GMCH_DATA_M = I915_READ(_PIPEA_GMCH_DATA_M); - dev_priv->regfile.savePIPEB_GMCH_DATA_M = I915_READ(_PIPEB_GMCH_DATA_M); - dev_priv->regfile.savePIPEA_GMCH_DATA_N = I915_READ(_PIPEA_GMCH_DATA_N); - dev_priv->regfile.savePIPEB_GMCH_DATA_N = I915_READ(_PIPEB_GMCH_DATA_N); - dev_priv->regfile.savePIPEA_DP_LINK_M = I915_READ(_PIPEA_DP_LINK_M); - dev_priv->regfile.savePIPEB_DP_LINK_M = I915_READ(_PIPEB_DP_LINK_M); - dev_priv->regfile.savePIPEA_DP_LINK_N = I915_READ(_PIPEA_DP_LINK_N); - dev_priv->regfile.savePIPEB_DP_LINK_N = I915_READ(_PIPEB_DP_LINK_N); + dev_priv->regfile.savePIPEA_GMCH_DATA_M = I915_READ(_PIPEA_DATA_M_G4X); + dev_priv->regfile.savePIPEB_GMCH_DATA_M = I915_READ(_PIPEB_DATA_M_G4X); + dev_priv->regfile.savePIPEA_GMCH_DATA_N = I915_READ(_PIPEA_DATA_N_G4X); + dev_priv->regfile.savePIPEB_GMCH_DATA_N = I915_READ(_PIPEB_DATA_N_G4X); + dev_priv->regfile.savePIPEA_DP_LINK_M = I915_READ(_PIPEA_LINK_M_G4X); + dev_priv->regfile.savePIPEB_DP_LINK_M = I915_READ(_PIPEB_LINK_M_G4X); + dev_priv->regfile.savePIPEA_DP_LINK_N = I915_READ(_PIPEA_LINK_N_G4X); + dev_priv->regfile.savePIPEB_DP_LINK_N = I915_READ(_PIPEB_LINK_N_G4X); } /* FIXME: regfile.save TV & SDVO state */ @@ -282,14 +282,14 @@ void i915_restore_display_reg(struct drm_device *dev) /* Display port ratios (must be done before clock is set) */ if (SUPPORTS_INTEGRATED_DP(dev)) { - I915_WRITE(_PIPEA_GMCH_DATA_M, dev_priv->regfile.savePIPEA_GMCH_DATA_M); - I915_WRITE(_PIPEB_GMCH_DATA_M, dev_priv->regfile.savePIPEB_GMCH_DATA_M); - I915_WRITE(_PIPEA_GMCH_DATA_N, dev_priv->regfile.savePIPEA_GMCH_DATA_N); - I915_WRITE(_PIPEB_GMCH_DATA_N, dev_priv->regfile.savePIPEB_GMCH_DATA_N); - I915_WRITE(_PIPEA_DP_LINK_M, dev_priv->regfile.savePIPEA_DP_LINK_M); - I915_WRITE(_PIPEB_DP_LINK_M, dev_priv->regfile.savePIPEB_DP_LINK_M); - I915_WRITE(_PIPEA_DP_LINK_N, dev_priv->regfile.savePIPEA_DP_LINK_N); - I915_WRITE(_PIPEB_DP_LINK_N, dev_priv->regfile.savePIPEB_DP_LINK_N); + I915_WRITE(_PIPEA_DATA_M_G4X, dev_priv->regfile.savePIPEA_GMCH_DATA_M); + I915_WRITE(_PIPEB_DATA_M_G4X, dev_priv->regfile.savePIPEB_GMCH_DATA_M); + I915_WRITE(_PIPEA_DATA_N_G4X, dev_priv->regfile.savePIPEA_GMCH_DATA_N); + I915_WRITE(_PIPEB_DATA_N_G4X, dev_priv->regfile.savePIPEB_GMCH_DATA_N); + I915_WRITE(_PIPEA_LINK_M_G4X, dev_priv->regfile.savePIPEA_DP_LINK_M); + I915_WRITE(_PIPEB_LINK_M_G4X, dev_priv->regfile.savePIPEB_DP_LINK_M); + I915_WRITE(_PIPEA_LINK_N_G4X, dev_priv->regfile.savePIPEA_DP_LINK_N); + I915_WRITE(_PIPEB_LINK_N_G4X, dev_priv->regfile.savePIPEB_DP_LINK_N); } /* Fences */ diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 4f74a7f55183..c48bb76c055c 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4389,10 +4389,10 @@ static void intel_pch_transcoder_set_m_n(struct intel_crtc *crtc, struct drm_i915_private *dev_priv = dev->dev_private; int pipe = crtc->pipe; - I915_WRITE(TRANSDATA_M1(pipe), TU_SIZE(m_n->tu) | m_n->gmch_m); - I915_WRITE(TRANSDATA_N1(pipe), m_n->gmch_n); - I915_WRITE(TRANSDPLINK_M1(pipe), m_n->link_m); - I915_WRITE(TRANSDPLINK_N1(pipe), m_n->link_n); + I915_WRITE(PCH_TRANS_DATA_M1(pipe), TU_SIZE(m_n->tu) | m_n->gmch_m); + I915_WRITE(PCH_TRANS_DATA_N1(pipe), m_n->gmch_n); + I915_WRITE(PCH_TRANS_LINK_M1(pipe), m_n->link_m); + I915_WRITE(PCH_TRANS_LINK_N1(pipe), m_n->link_n); } static void intel_cpu_transcoder_set_m_n(struct intel_crtc *crtc, @@ -4409,10 +4409,10 @@ static void intel_cpu_transcoder_set_m_n(struct intel_crtc *crtc, I915_WRITE(PIPE_LINK_M1(transcoder), m_n->link_m); I915_WRITE(PIPE_LINK_N1(transcoder), m_n->link_n); } else { - I915_WRITE(PIPE_GMCH_DATA_M(pipe), TU_SIZE(m_n->tu) | m_n->gmch_m); - I915_WRITE(PIPE_GMCH_DATA_N(pipe), m_n->gmch_n); - I915_WRITE(PIPE_DP_LINK_M(pipe), m_n->link_m); - I915_WRITE(PIPE_DP_LINK_N(pipe), m_n->link_n); + I915_WRITE(PIPE_DATA_M_G4X(pipe), TU_SIZE(m_n->tu) | m_n->gmch_m); + I915_WRITE(PIPE_DATA_N_G4X(pipe), m_n->gmch_n); + I915_WRITE(PIPE_LINK_M_G4X(pipe), m_n->link_m); + I915_WRITE(PIPE_LINK_N_G4X(pipe), m_n->link_n); } } -- cgit v1.2.3 From a1520318a51d6c21b1d9229a9c35b4fcb890b175 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 3 May 2013 11:49:50 +0200 Subject: drm/i915: make intel_cpt_verify_modeset static Only one caller. Also drop the intel_ prefix as is now customary for platform specific and static functions. Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 4 ++-- drivers/gpu/drm/i915/intel_drv.h | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index c48bb76c055c..d50994a22966 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3229,7 +3229,7 @@ prepare: /* separate function? */ return pll; } -void intel_cpt_verify_modeset(struct drm_device *dev, int pipe) +static void cpt_verify_modeset(struct drm_device *dev, int pipe) { struct drm_i915_private *dev_priv = dev->dev_private; int dslreg = PIPEDSL(pipe); @@ -3334,7 +3334,7 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) encoder->enable(encoder); if (HAS_PCH_CPT(dev)) - intel_cpt_verify_modeset(dev, intel_crtc->pipe); + cpt_verify_modeset(dev, intel_crtc->pipe); /* * There seems to be a race in PCH platform hw (at least on some diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 88890a30f356..e3108c0966c3 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -718,7 +718,6 @@ extern void assert_pipe(struct drm_i915_private *dev_priv, enum pipe pipe, extern void intel_init_clock_gating(struct drm_device *dev); extern void intel_write_eld(struct drm_encoder *encoder, struct drm_display_mode *mode); -extern void intel_cpt_verify_modeset(struct drm_device *dev, int pipe); extern void intel_prepare_ddi(struct drm_device *dev); extern void hsw_fdi_link_train(struct drm_crtc *crtc); extern void intel_ddi_init(struct drm_device *dev, enum port port); -- cgit v1.2.3 From 186507e9e8e89d5920305fdffd8cbba6366da795 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Tue, 23 Apr 2013 23:15:29 -0700 Subject: drm/i915: Assert mutex_is_locked on context lookup Because our context refcounting doesn't grab a ref at lookup time, it is unsafe to do so without the lock. NOTE: We don't have an easy way to put the assertion in the lookup function which is where this really belongs. Context switching is good enough because it actually asserts even more correctness by protecting the default_context. Signed-off-by: Ben Widawsky Reviewed-by: Jesse Barnes [danvet: s/BUG/WARN/] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_context.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index efa0ede5c679..c81ae52c8c2e 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -463,6 +463,8 @@ int i915_switch_context(struct intel_ring_buffer *ring, if (dev_priv->hw_contexts_disabled) return 0; + WARN_ON(!mutex_is_locked(&dev_priv->dev->struct_mutex)); + if (ring != &dev_priv->ring[RCS]) return 0; -- cgit v1.2.3 From 0a73287060cdd8fc2b50ecd216c918c2d097de59 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Tue, 23 Apr 2013 23:15:30 -0700 Subject: drm/i915: BUG_ON bad PPGTT offset Because PPGTT PDEs within the GTT are calculated in cachelines (HW guys consistency ftw) we do a divide which will wreak havoc if this is wrong, and I know that from experience). If/when we move to multiple PPGTTs this will have to become a WARN, and return an error. For now however it should always be considered fatal, and only a developer could hit it. Signed-off-by: Ben Widawsky Reviewed-by: Jesse Barnes [danvet: s/BUG/WARN] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_gtt.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index ce024bd18eac..a4f0f9519503 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -110,6 +110,8 @@ static int gen6_ppgtt_enable(struct drm_device *dev) uint32_t pd_entry; int i; + WARN_ON(ppgtt->pd_offset & 0x3f); + pd_addr = (gen6_gtt_pte_t __iomem*)dev_priv->gtt.gsm + ppgtt->pd_offset / sizeof(gen6_gtt_pte_t); for (i = 0; i < ppgtt->num_pd_entries; i++) { -- cgit v1.2.3 From 3e302542055617703b260489ec1ff6fc6f1e68cd Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Tue, 23 Apr 2013 23:15:32 -0700 Subject: drm/i915: Extract PDE writes It also makes some sense IMO to have these two functions separate irrespective of the number of callers. Only the single caller for now, but that will change as we add more PPGTTs. Signed-off-by: Ben Widawsky Reviewed-by: Jesse Barnes [danvet: Resolve conflict.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_gtt.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index a4f0f9519503..85b3d5d4deec 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -100,18 +100,14 @@ static gen6_gtt_pte_t hsw_pte_encode(struct drm_device *dev, return pte; } -static int gen6_ppgtt_enable(struct drm_device *dev) +static void gen6_write_pdes(struct i915_hw_ppgtt *ppgtt) { - drm_i915_private_t *dev_priv = dev->dev_private; - uint32_t pd_offset; - struct intel_ring_buffer *ring; - struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt; + struct drm_i915_private *dev_priv = ppgtt->dev->dev_private; gen6_gtt_pte_t __iomem *pd_addr; uint32_t pd_entry; int i; WARN_ON(ppgtt->pd_offset & 0x3f); - pd_addr = (gen6_gtt_pte_t __iomem*)dev_priv->gtt.gsm + ppgtt->pd_offset / sizeof(gen6_gtt_pte_t); for (i = 0; i < ppgtt->num_pd_entries; i++) { @@ -124,6 +120,19 @@ static int gen6_ppgtt_enable(struct drm_device *dev) writel(pd_entry, pd_addr + i); } readl(pd_addr); +} + +static int gen6_ppgtt_enable(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + uint32_t pd_offset; + struct intel_ring_buffer *ring; + struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt; + int i; + + BUG_ON(ppgtt->pd_offset & 0x3f); + + gen6_write_pdes(ppgtt); pd_offset = ppgtt->pd_offset; pd_offset /= 64; /* in cachelines, */ -- cgit v1.2.3 From 2a456cfbdbcdf8a6a9e041f256f7e859a271a301 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 3 May 2013 09:57:08 +0000 Subject: Thermal: armada_thermal: Remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Cc: Ezequiel Garcia Acked-by: Ezequiel Garcia Acked-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/armada_thermal.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/thermal/armada_thermal.c b/drivers/thermal/armada_thermal.c index 5b4d75fd7b49..0d02d4edeecb 100644 --- a/drivers/thermal/armada_thermal.c +++ b/drivers/thermal/armada_thermal.c @@ -210,7 +210,6 @@ static int armada_thermal_exit(struct platform_device *pdev) platform_get_drvdata(pdev); thermal_zone_device_unregister(armada_thermal); - platform_set_drvdata(pdev, NULL); return 0; } -- cgit v1.2.3 From aa50c4e49c650692559e2da1f5b49a145098ba71 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 3 May 2013 09:57:09 +0000 Subject: Thermal: dove_thermal: Remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Cc: Andrew Lunn Acked-by: Andrew Lunn Acked-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/dove_thermal.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/thermal/dove_thermal.c b/drivers/thermal/dove_thermal.c index 4b15a5f270dc..900479e69ccb 100644 --- a/drivers/thermal/dove_thermal.c +++ b/drivers/thermal/dove_thermal.c @@ -182,7 +182,6 @@ static int dove_thermal_exit(struct platform_device *pdev) platform_get_drvdata(pdev); thermal_zone_device_unregister(dove_thermal); - platform_set_drvdata(pdev, NULL); return 0; } -- cgit v1.2.3 From a2071b0b449e6cb725a15932590ee5753c39d282 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 3 May 2013 09:57:10 +0000 Subject: Thermal: exynos: Remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Acked-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/exynos_thermal.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/thermal/exynos_thermal.c b/drivers/thermal/exynos_thermal.c index d20ce9e61403..992ae6e25112 100644 --- a/drivers/thermal/exynos_thermal.c +++ b/drivers/thermal/exynos_thermal.c @@ -1001,7 +1001,6 @@ static int exynos_tmu_probe(struct platform_device *pdev) return 0; err_clk: - platform_set_drvdata(pdev, NULL); clk_unprepare(data->clk); return ret; } @@ -1016,8 +1015,6 @@ static int exynos_tmu_remove(struct platform_device *pdev) clk_unprepare(data->clk); - platform_set_drvdata(pdev, NULL); - return 0; } -- cgit v1.2.3 From 0f3913bd2dce5a756ca2384c963087713762ab66 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 3 May 2013 09:57:11 +0000 Subject: Thermal: kirkwood: Remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Cc: Nobuhiro Iwamatsu Acked-by: Eduardo Valentin Acked-by: Nobuhiro Iwamatsu Signed-off-by: Zhang Rui --- drivers/thermal/kirkwood_thermal.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/thermal/kirkwood_thermal.c b/drivers/thermal/kirkwood_thermal.c index dfeceaffbc03..b57a45daf0df 100644 --- a/drivers/thermal/kirkwood_thermal.c +++ b/drivers/thermal/kirkwood_thermal.c @@ -108,7 +108,6 @@ static int kirkwood_thermal_exit(struct platform_device *pdev) platform_get_drvdata(pdev); thermal_zone_device_unregister(kirkwood_thermal); - platform_set_drvdata(pdev, NULL); return 0; } -- cgit v1.2.3 From 6135ba36f44069ad789bd9f8d6a75eebc3946eba Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 3 May 2013 09:57:12 +0000 Subject: Thermal: rcar: Remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Cc: Kuninori Morimoto Acked-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/rcar_thermal.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 8d7edd4c8228..63c7c13d79f3 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -487,8 +487,6 @@ static int rcar_thermal_remove(struct platform_device *pdev) rcar_thermal_irq_disable(priv); } - platform_set_drvdata(pdev, NULL); - pm_runtime_put_sync(dev); pm_runtime_disable(dev); -- cgit v1.2.3 From a1cf1150f127f78783f86c695f10337391fc8092 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 3 May 2013 09:57:13 +0000 Subject: Thermal: spear: Remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Cc: Vincenzo Frascino Acked-by: Eduardo Valentin Acked-by: Viresh Kumar Signed-off-by: Zhang Rui --- drivers/thermal/spear_thermal.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/thermal/spear_thermal.c b/drivers/thermal/spear_thermal.c index 3c5ee5607977..1a14eaa1bbd7 100644 --- a/drivers/thermal/spear_thermal.c +++ b/drivers/thermal/spear_thermal.c @@ -174,7 +174,6 @@ static int spear_thermal_exit(struct platform_device *pdev) struct spear_thermal_dev *stdev = spear_thermal->devdata; thermal_zone_device_unregister(spear_thermal); - platform_set_drvdata(pdev, NULL); /* Disable SPEAr Thermal Sensor */ actual_mask = readl_relaxed(stdev->thermal_base); -- cgit v1.2.3 From 7bcbcaa584f9c1723f26413f585a34303e918caa Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 6 May 2013 07:40:39 -0700 Subject: Input: w90p910_ts - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/w90p910_ts.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/input/touchscreen/w90p910_ts.c b/drivers/input/touchscreen/w90p910_ts.c index d2ef8f05c66e..003d0c3b5d08 100644 --- a/drivers/input/touchscreen/w90p910_ts.c +++ b/drivers/input/touchscreen/w90p910_ts.c @@ -318,8 +318,6 @@ static int w90x900ts_remove(struct platform_device *pdev) input_unregister_device(w90p910_ts->input); kfree(w90p910_ts); - platform_set_drvdata(pdev, NULL); - return 0; } -- cgit v1.2.3 From d0c082d1068751e80562d6f894e310d16a985645 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 6 May 2013 07:23:55 -0700 Subject: Input: amikbd - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/amikbd.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/input/keyboard/amikbd.c b/drivers/input/keyboard/amikbd.c index ba0b36f7daea..096d6067ae1f 100644 --- a/drivers/input/keyboard/amikbd.c +++ b/drivers/input/keyboard/amikbd.c @@ -246,7 +246,6 @@ static int __exit amikbd_remove(struct platform_device *pdev) { struct input_dev *dev = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); free_irq(IRQ_AMIGA_CIAA_SP, dev); input_unregister_device(dev); return 0; -- cgit v1.2.3 From 9cc04d7627f9a86c4592885941d48a8586c7913a Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 6 May 2013 07:25:48 -0700 Subject: Input: ep93xx_keypad - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/ep93xx_keypad.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/input/keyboard/ep93xx_keypad.c b/drivers/input/keyboard/ep93xx_keypad.c index 9857e8fd0987..a8d5aacfb98b 100644 --- a/drivers/input/keyboard/ep93xx_keypad.c +++ b/drivers/input/keyboard/ep93xx_keypad.c @@ -330,7 +330,6 @@ static int ep93xx_keypad_probe(struct platform_device *pdev) failed_free_irq: free_irq(keypad->irq, pdev); - platform_set_drvdata(pdev, NULL); failed_free_dev: input_free_device(input_dev); failed_put_clk: @@ -353,8 +352,6 @@ static int ep93xx_keypad_remove(struct platform_device *pdev) free_irq(keypad->irq, pdev); - platform_set_drvdata(pdev, NULL); - if (keypad->enabled) clk_disable(keypad->clk); clk_put(keypad->clk); -- cgit v1.2.3 From da1bfb4b4fcfc4b6ba51d7ba670c5e7fe0cbfce6 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 6 May 2013 07:24:23 -0700 Subject: Input: bf54x-keys - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/bf54x-keys.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/input/keyboard/bf54x-keys.c b/drivers/input/keyboard/bf54x-keys.c index 20b9fa91fb9e..fc88fb48d70d 100644 --- a/drivers/input/keyboard/bf54x-keys.c +++ b/drivers/input/keyboard/bf54x-keys.c @@ -326,7 +326,6 @@ out0: kfree(bf54x_kpad->keycode); out: kfree(bf54x_kpad); - platform_set_drvdata(pdev, NULL); return error; } @@ -346,7 +345,6 @@ static int bfin_kpad_remove(struct platform_device *pdev) kfree(bf54x_kpad->keycode); kfree(bf54x_kpad); - platform_set_drvdata(pdev, NULL); return 0; } -- cgit v1.2.3 From b0ae73b828a76dd759e5232013d7a77cc4b88cac Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 6 May 2013 07:25:07 -0700 Subject: Input: davinci_keyscan - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/davinci_keyscan.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/input/keyboard/davinci_keyscan.c b/drivers/input/keyboard/davinci_keyscan.c index 829753702b62..d15977a8361e 100644 --- a/drivers/input/keyboard/davinci_keyscan.c +++ b/drivers/input/keyboard/davinci_keyscan.c @@ -314,8 +314,6 @@ static int davinci_ks_remove(struct platform_device *pdev) iounmap(davinci_ks->base); release_mem_region(davinci_ks->pbase, davinci_ks->base_size); - platform_set_drvdata(pdev, NULL); - kfree(davinci_ks); return 0; -- cgit v1.2.3 From 9c0219f96433b19c2036ffcbab080b0a2696d2ef Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 6 May 2013 07:31:25 -0700 Subject: Input: gpio_keys - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/gpio_keys.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index b29ca651a395..440ce32462ba 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -767,7 +767,6 @@ static int gpio_keys_probe(struct platform_device *pdev) while (--i >= 0) gpio_remove_key(&ddata->data[i]); - platform_set_drvdata(pdev, NULL); fail1: input_free_device(input); kfree(ddata); -- cgit v1.2.3 From 8b7022b9b049aed217a9cf52dbb5d608529315ee Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 6 May 2013 07:31:56 -0700 Subject: Input: gpio_keys_polled - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/gpio_keys_polled.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/input/keyboard/gpio_keys_polled.c b/drivers/input/keyboard/gpio_keys_polled.c index 21147164874d..cd5ed9e22168 100644 --- a/drivers/input/keyboard/gpio_keys_polled.c +++ b/drivers/input/keyboard/gpio_keys_polled.c @@ -324,7 +324,6 @@ err_free_gpio: err_free_bdev: kfree(bdev); - platform_set_drvdata(pdev, NULL); err_free_pdata: /* If we have no platform_data, we allocated pdata dynamically. */ @@ -355,7 +354,6 @@ static int gpio_keys_polled_remove(struct platform_device *pdev) kfree(pdata); kfree(bdev); - platform_set_drvdata(pdev, NULL); return 0; } -- cgit v1.2.3 From 5d1e9385a130e65c4f1fef9e167ba786ac427381 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 6 May 2013 07:32:17 -0700 Subject: Input: jornada680_kbd - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/jornada680_kbd.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/input/keyboard/jornada680_kbd.c b/drivers/input/keyboard/jornada680_kbd.c index 74e75a6e8deb..a2a034c25f0b 100644 --- a/drivers/input/keyboard/jornada680_kbd.c +++ b/drivers/input/keyboard/jornada680_kbd.c @@ -233,7 +233,6 @@ static int jornada680kbd_probe(struct platform_device *pdev) failed: printk(KERN_ERR "Jornadakbd: failed to register driver, error: %d\n", error); - platform_set_drvdata(pdev, NULL); input_free_polled_device(poll_dev); kfree(jornadakbd); return error; @@ -244,7 +243,6 @@ static int jornada680kbd_remove(struct platform_device *pdev) { struct jornadakbd *jornadakbd = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); input_unregister_polled_device(jornadakbd->poll_dev); input_free_polled_device(jornadakbd->poll_dev); kfree(jornadakbd); -- cgit v1.2.3 From bb41eca764a74032070a5290f2ef8e6b424ad961 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 6 May 2013 07:32:40 -0700 Subject: Input: jornada720_kbd - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/jornada720_kbd.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/input/keyboard/jornada720_kbd.c b/drivers/input/keyboard/jornada720_kbd.c index 5ceef636df2f..b0ad457ca9d8 100644 --- a/drivers/input/keyboard/jornada720_kbd.c +++ b/drivers/input/keyboard/jornada720_kbd.c @@ -146,7 +146,6 @@ static int jornada720_kbd_probe(struct platform_device *pdev) fail2: /* IRQ, DEVICE, MEMORY */ free_irq(IRQ_GPIO0, pdev); fail1: /* DEVICE, MEMORY */ - platform_set_drvdata(pdev, NULL); input_free_device(input_dev); kfree(jornadakbd); return err; @@ -157,7 +156,6 @@ static int jornada720_kbd_remove(struct platform_device *pdev) struct jornadakbd *jornadakbd = platform_get_drvdata(pdev); free_irq(IRQ_GPIO0, pdev); - platform_set_drvdata(pdev, NULL); input_unregister_device(jornadakbd->input); kfree(jornadakbd); -- cgit v1.2.3 From 776f2accca36262f5fec51e0574a875554dd400c Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 6 May 2013 07:32:57 -0700 Subject: Input: matrix_keypad - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Acked-by: Marek Vasut Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/matrix_keypad.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c index 71d77192ac1e..90ff73ace424 100644 --- a/drivers/input/keyboard/matrix_keypad.c +++ b/drivers/input/keyboard/matrix_keypad.c @@ -549,8 +549,6 @@ static int matrix_keypad_remove(struct platform_device *pdev) input_unregister_device(keypad->input_dev); kfree(keypad); - platform_set_drvdata(pdev, NULL); - return 0; } -- cgit v1.2.3 From 19b1d74fee8fd9dffb361e29daf0b0a36d398190 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 6 May 2013 07:34:41 -0700 Subject: Input: omap4-keypad - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/omap4-keypad.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c index e25b022692cd..d715c0d10bf2 100644 --- a/drivers/input/keyboard/omap4-keypad.c +++ b/drivers/input/keyboard/omap4-keypad.c @@ -425,8 +425,6 @@ static int omap4_keypad_remove(struct platform_device *pdev) kfree(keypad_data->keymap); kfree(keypad_data); - platform_set_drvdata(pdev, NULL); - return 0; } -- cgit v1.2.3 From 8b07dd0f8f574f21b6ef3ab138aee8ef28d09b49 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 6 May 2013 07:35:06 -0700 Subject: Input: opencores-kbd - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Cc: Javier Herrero Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/opencores-kbd.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/input/keyboard/opencores-kbd.c b/drivers/input/keyboard/opencores-kbd.c index 7ac5f174c6f7..7b9b44158ad1 100644 --- a/drivers/input/keyboard/opencores-kbd.c +++ b/drivers/input/keyboard/opencores-kbd.c @@ -151,8 +151,6 @@ static int opencores_kbd_remove(struct platform_device *pdev) input_unregister_device(opencores_kbd->input); kfree(opencores_kbd); - platform_set_drvdata(pdev, NULL); - return 0; } -- cgit v1.2.3 From b6a08a4ad8d44be925c5a183130aec348aa1a8b6 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 6 May 2013 07:35:24 -0700 Subject: Input: pmic8xxx-keypad - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/pmic8xxx-keypad.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/input/keyboard/pmic8xxx-keypad.c b/drivers/input/keyboard/pmic8xxx-keypad.c index 74339e139d43..2c9f19ac35ea 100644 --- a/drivers/input/keyboard/pmic8xxx-keypad.c +++ b/drivers/input/keyboard/pmic8xxx-keypad.c @@ -707,7 +707,6 @@ err_gpio_config: err_get_irq: input_free_device(kp->input); err_alloc_device: - platform_set_drvdata(pdev, NULL); kfree(kp); return rc; } @@ -722,7 +721,6 @@ static int pmic8xxx_kp_remove(struct platform_device *pdev) input_unregister_device(kp->input); kfree(kp); - platform_set_drvdata(pdev, NULL); return 0; } -- cgit v1.2.3 From c8712a53671aef12a8b6b3ec4e942ef4f92bc17e Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 6 May 2013 07:35:41 -0700 Subject: Input: pxa27x_keypad - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/pxa27x_keypad.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/input/keyboard/pxa27x_keypad.c b/drivers/input/keyboard/pxa27x_keypad.c index 5330d8fbf6c0..74e30cccbeab 100644 --- a/drivers/input/keyboard/pxa27x_keypad.c +++ b/drivers/input/keyboard/pxa27x_keypad.c @@ -609,7 +609,6 @@ static int pxa27x_keypad_remove(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(res->start, resource_size(res)); - platform_set_drvdata(pdev, NULL); kfree(keypad); return 0; -- cgit v1.2.3 From 84ce3aa1a73d1340584c9bbf371e963a4bcd5aec Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 6 May 2013 07:36:22 -0700 Subject: Input: pxa930_rotary - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/pxa930_rotary.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/input/keyboard/pxa930_rotary.c b/drivers/input/keyboard/pxa930_rotary.c index bcad95be73aa..248cdcf95296 100644 --- a/drivers/input/keyboard/pxa930_rotary.c +++ b/drivers/input/keyboard/pxa930_rotary.c @@ -181,7 +181,6 @@ static int pxa930_rotary_remove(struct platform_device *pdev) free_irq(platform_get_irq(pdev, 0), r); input_unregister_device(r->input_dev); iounmap(r->mmio_base); - platform_set_drvdata(pdev, NULL); kfree(r); return 0; -- cgit v1.2.3 From 413b859e16282c32a3f3edbeeeea9ca440023194 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 6 May 2013 07:37:10 -0700 Subject: Input: samsung-keypad - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Cc: Joonyoung Shim Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/samsung-keypad.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/input/keyboard/samsung-keypad.c b/drivers/input/keyboard/samsung-keypad.c index 22e357b51024..7b938b481bd5 100644 --- a/drivers/input/keyboard/samsung-keypad.c +++ b/drivers/input/keyboard/samsung-keypad.c @@ -487,7 +487,6 @@ static int samsung_keypad_probe(struct platform_device *pdev) err_disable_runtime_pm: pm_runtime_disable(&pdev->dev); device_init_wakeup(&pdev->dev, 0); - platform_set_drvdata(pdev, NULL); err_unprepare_clk: clk_unprepare(keypad->clk); return error; @@ -499,7 +498,6 @@ static int samsung_keypad_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); device_init_wakeup(&pdev->dev, 0); - platform_set_drvdata(pdev, NULL); input_unregister_device(keypad->input_dev); -- cgit v1.2.3 From 86066e6839820b7808b013bac16348b7b1ad8bae Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 6 May 2013 07:38:51 -0700 Subject: Input: sh_keysc - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/sh_keysc.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/input/keyboard/sh_keysc.c b/drivers/input/keyboard/sh_keysc.c index fdb9eb2df380..fe0e498d2479 100644 --- a/drivers/input/keyboard/sh_keysc.c +++ b/drivers/input/keyboard/sh_keysc.c @@ -266,7 +266,6 @@ static int sh_keysc_probe(struct platform_device *pdev) err2: iounmap(priv->iomem_base); err1: - platform_set_drvdata(pdev, NULL); kfree(priv); err0: return error; @@ -285,7 +284,6 @@ static int sh_keysc_remove(struct platform_device *pdev) pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); - platform_set_drvdata(pdev, NULL); kfree(priv); return 0; -- cgit v1.2.3 From e78465ed1f2c0f1012d0cb6a9fb9b9a25e4b8c4d Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 6 May 2013 07:39:01 -0700 Subject: Input: spear-keyboard - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/spear-keyboard.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/input/keyboard/spear-keyboard.c b/drivers/input/keyboard/spear-keyboard.c index cb1e8f614631..7111124b5362 100644 --- a/drivers/input/keyboard/spear-keyboard.c +++ b/drivers/input/keyboard/spear-keyboard.c @@ -290,7 +290,6 @@ static int spear_kbd_remove(struct platform_device *pdev) clk_unprepare(kbd->clk); device_init_wakeup(&pdev->dev, 0); - platform_set_drvdata(pdev, NULL); return 0; } -- cgit v1.2.3 From 94521e5b0624d84e2a8a3cb4bd23d46103a6ec57 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 6 May 2013 07:39:07 -0700 Subject: Input: tnetv107x-keypad - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/tnetv107x-keypad.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/input/keyboard/tnetv107x-keypad.c b/drivers/input/keyboard/tnetv107x-keypad.c index ee1635011292..5f7b427dd7ed 100644 --- a/drivers/input/keyboard/tnetv107x-keypad.c +++ b/drivers/input/keyboard/tnetv107x-keypad.c @@ -296,7 +296,6 @@ error_clk: error_map: release_mem_region(kp->res->start, resource_size(kp->res)); error_res: - platform_set_drvdata(pdev, NULL); kfree(kp); return error; } @@ -311,7 +310,6 @@ static int keypad_remove(struct platform_device *pdev) 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; -- cgit v1.2.3 From 62205e08e6192a1aa67cc647994c69d9cc55e6f6 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 6 May 2013 07:39:14 -0700 Subject: Input: twl4030_keypad - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/twl4030_keypad.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/input/keyboard/twl4030_keypad.c b/drivers/input/keyboard/twl4030_keypad.c index 04f84fd57173..f05ee1db7e64 100644 --- a/drivers/input/keyboard/twl4030_keypad.c +++ b/drivers/input/keyboard/twl4030_keypad.c @@ -438,7 +438,6 @@ static int twl4030_kp_remove(struct platform_device *pdev) free_irq(kp->irq, kp); input_unregister_device(kp->input); - platform_set_drvdata(pdev, NULL); kfree(kp); return 0; -- cgit v1.2.3 From d52014556d8a1f3be57db4f04d6fdce06af87493 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 6 May 2013 07:39:36 -0700 Subject: Input: w90p910_keypad - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Cc: Wan ZongShun Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/w90p910_keypad.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/input/keyboard/w90p910_keypad.c b/drivers/input/keyboard/w90p910_keypad.c index ee163bee8cce..72e6a56a2eba 100644 --- a/drivers/input/keyboard/w90p910_keypad.c +++ b/drivers/input/keyboard/w90p910_keypad.c @@ -249,7 +249,6 @@ static int w90p910_keypad_remove(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(res->start, resource_size(res)); - platform_set_drvdata(pdev, NULL); kfree(keypad); return 0; -- cgit v1.2.3 From beadadfa5467e09e36891f39cae1f5d8d3bbf17e Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Tue, 2 Apr 2013 12:24:37 +0200 Subject: UBIFS: correct mount message When mounting an UBIFS R/W volume, we have the message: UBIFS: mounted UBI device 0, volume 1, name "rootfs"(null) With this patch, we'll have: UBIFS: mounted UBI device 0, volume 1, name "rootfs" Which is, I think, what was intended. Signed-off-by: Richard Genoud Cc: stable@vger.kernel.org [v3.7+] Signed-off-by: Artem Bityutskiy --- fs/ubifs/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index f21acf0ef01f..879b9976c12b 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -1412,7 +1412,7 @@ static int mount_ubifs(struct ubifs_info *c) ubifs_msg("mounted UBI device %d, volume %d, name \"%s\"%s", c->vi.ubi_num, c->vi.vol_id, c->vi.name, - c->ro_mount ? ", R/O mode" : NULL); + c->ro_mount ? ", R/O mode" : ""); x = (long long)c->main_lebs * c->leb_size; y = (long long)c->log_lebs * c->leb_size + c->max_bud_bytes; ubifs_msg("LEB size: %d bytes (%d KiB), min./max. I/O unit sizes: %d bytes/%d bytes", -- cgit v1.2.3 From 8f375e10ee47b9d7b9b3aefcf67854c6e92708be Mon Sep 17 00:00:00 2001 From: Jan-Simon Möller Date: Mon, 6 May 2013 14:52:08 +0200 Subject: drm/i915: Fix declaration of intel_gmbus_{is_forced_bit/is_port_falid} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Description: intel_gmbus_is_forced_bit is no extern as its body is right below. Likewise for intel_gmbus_is_port_valid. This fixes a compilation issue with clang. An initial version of this patch was developed by PaX Team . This is respin of this patch. 20130509: v2: (re-)add inline upon request. Signed-off-by: Jan-Simon Möller CC: pageexec@freemail.hu CC: daniel.vetter@ffwll.ch CC: airlied@linux.ie CC: intel-gfx@lists.freedesktop.org CC: dri-devel@lists.freedesktop.org CC: linux-kernel@vger.kernel.org [danvet: Bikeshed commit message.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index ca0b0ce6e276..729f5b67f53c 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1809,7 +1809,7 @@ void i915_teardown_sysfs(struct drm_device *dev_priv); /* intel_i2c.c */ extern int intel_setup_gmbus(struct drm_device *dev); extern void intel_teardown_gmbus(struct drm_device *dev); -extern inline bool intel_gmbus_is_port_valid(unsigned port) +static inline bool intel_gmbus_is_port_valid(unsigned port) { return (port >= GMBUS_PORT_SSC && port <= GMBUS_PORT_DPD); } @@ -1818,7 +1818,7 @@ extern struct i2c_adapter *intel_gmbus_get_adapter( struct drm_i915_private *dev_priv, unsigned port); 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) +static inline bool intel_gmbus_is_forced_bit(struct i2c_adapter *adapter) { return container_of(adapter, struct intel_gmbus, adapter)->force_bit; } -- cgit v1.2.3 From 177006a10b33c9bd729cd60be0a37b41a23e4df3 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 2 May 2013 10:48:07 -0700 Subject: drm/i915: read current freq from Punit on VLV Instead of returning the cached value, which is just what the kernel requested. Reviewed-by: Kenneth Graunke Signed-off-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_sysfs.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_sysfs.c b/drivers/gpu/drm/i915/i915_sysfs.c index ca00df2de07b..c0d7875b475c 100644 --- a/drivers/gpu/drm/i915/i915_sysfs.c +++ b/drivers/gpu/drm/i915/i915_sysfs.c @@ -212,10 +212,13 @@ static ssize_t gt_cur_freq_mhz_show(struct device *kdev, int ret; mutex_lock(&dev_priv->rps.hw_lock); - if (IS_VALLEYVIEW(dev_priv->dev)) - ret = vlv_gpu_freq(dev_priv->mem_freq, dev_priv->rps.cur_delay); - else + if (IS_VALLEYVIEW(dev_priv->dev)) { + u32 freq; + valleyview_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS, &freq); + ret = vlv_gpu_freq(dev_priv->mem_freq, (freq >> 8) & 0xff); + } else { ret = dev_priv->rps.cur_delay * GT_FREQUENCY_MULTIPLIER; + } mutex_unlock(&dev_priv->rps.hw_lock); return snprintf(buf, PAGE_SIZE, "%d\n", ret); -- cgit v1.2.3 From 2445966ee80837116498bd83084ad6d28272320c Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 2 May 2013 10:48:08 -0700 Subject: drm/i915: go back to switch for VLV mem freq detection v2 Both the docs and the existing code were wrong. So fix both and use a switch statement like we do elsewhere to make things simple & clear. Signed-off-by: Jesse Barnes Reviewed-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_pm.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 0f4b46e94932..556b9892bb92 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -2902,7 +2902,18 @@ static void valleyview_enable_rps(struct drm_device *dev) GEN7_RC_CTL_TO_MODE); valleyview_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS, &val); - dev_priv->mem_freq = 800 + (266 * (val >> 6) & 3); + switch ((val >> 6) & 3) { + case 0: + case 1: + dev_priv->mem_freq = 800; + break; + case 2: + dev_priv->mem_freq = 1066; + break; + case 3: + dev_priv->mem_freq = 1333; + break; + } DRM_DEBUG_DRIVER("DDR speed: %d MHz", dev_priv->mem_freq); DRM_DEBUG_DRIVER("GPLL enabled? %s\n", val & 0x10 ? "yes" : "no"); -- cgit v1.2.3 From 0ef37f3f5e33eae7d6c388a7b374397794beca39 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Fri, 3 May 2013 13:26:37 -0700 Subject: drm/i915: fix panel fitting on LVDS on ILK+ v2 This regression was introduced in: commit b074cec8c652f2d273907a4b35239b4766c894ac Author: Jesse Barnes Date: Thu Apr 25 12:55:02 2013 -0700 drm/i915: move PCH pfit controls into pipe_config In refactoring this, it was only applied to eDP, which is incorrect. In fact, if we ever use the panel fitter to deal with overscan on HDMI, we'll need to extend it again, so just drop the conditional altogether. v2: drop check for eDP since we can use the fitter in any config (Daniel) Signed-off-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index d50994a22966..5491a58e5b37 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3249,8 +3249,7 @@ static void ironlake_pfit_enable(struct intel_crtc *crtc) struct drm_i915_private *dev_priv = dev->dev_private; int pipe = crtc->pipe; - if (crtc->config.pch_pfit.size && - intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_EDP)) { + if (crtc->config.pch_pfit.size) { /* Force use of hard-coded filter coefficients * as some pre-programmed values are broken, * e.g. x201. -- cgit v1.2.3 From 7df5080bc7f3e3fba9cddac71133ed52864c40be Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 2 May 2013 10:48:09 -0700 Subject: drm/i915: set proper DPIO post divider for VGA on VLV v4 Supposedly we should use the DAC divider for <300MHz pixel clocks, but as that doesn't actually work as well as the high freq divider here in practice, just use the high freq divider all the time. v2: remove unconditional write (Jesse) check for pixel rate properly (Jesse) v3: give up, the DAC divider apparently doesn't work, and low res modes work ok (Jesse) remove debug msg (Jesse) Signed-off-by: Jesse Barnes Tested-by: Kenneth Graunke Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 5491a58e5b37..b9d5711c595c 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4468,10 +4468,13 @@ static void vlv_update_pll(struct intel_crtc *crtc) mdiv |= ((bestp1 << DPIO_P1_SHIFT) | (bestp2 << DPIO_P2_SHIFT)); mdiv |= ((bestn << DPIO_N_SHIFT)); mdiv |= (1 << DPIO_K_SHIFT); - if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_HDMI) || - intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_EDP) || - intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_DISPLAYPORT)) - mdiv |= (DPIO_POST_DIV_HDMIDP << DPIO_POST_DIV_SHIFT); + + /* + * Post divider depends on pixel clock rate, DAC vs digital (and LVDS, + * but we don't support that). + * Note: don't use the DAC post divider as it seems unstable. + */ + mdiv |= (DPIO_POST_DIV_HDMIDP << DPIO_POST_DIV_SHIFT); intel_dpio_write(dev_priv, DPIO_DIV(pipe), mdiv); mdiv |= DPIO_ENABLE_CALIBRATION; -- cgit v1.2.3 From b97186f0d94808ce94cd9b77b40e78f2fbd6e6b2 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 3 May 2013 12:15:36 -0300 Subject: drm/i915: add intel_display_power_enabled This should replace intel_using_power_well. The idea is that we're adding the requested power domain as an argument, so this might enable the code to look less platform-specific and also allows us to easily add new domains in case we need. v2: Add more domains to enum intel_display_power_domain v3: Even more domains requested Requested-by: Daniel Vetter Signed-off-by: Paulo Zanoni Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 18 ++++++++++++++++++ drivers/gpu/drm/i915/intel_display.c | 11 ++++++----- drivers/gpu/drm/i915/intel_drv.h | 3 ++- drivers/gpu/drm/i915/intel_pm.c | 24 ++++++++++++++++++++---- 4 files changed, 46 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 729f5b67f53c..c81100c54e24 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -88,6 +88,24 @@ enum port { }; #define port_name(p) ((p) + 'A') +enum intel_display_power_domain { + POWER_DOMAIN_PIPE_A, + POWER_DOMAIN_PIPE_B, + POWER_DOMAIN_PIPE_C, + POWER_DOMAIN_PIPE_A_PANEL_FITTER, + POWER_DOMAIN_PIPE_B_PANEL_FITTER, + POWER_DOMAIN_PIPE_C_PANEL_FITTER, + POWER_DOMAIN_TRANSCODER_A, + POWER_DOMAIN_TRANSCODER_B, + POWER_DOMAIN_TRANSCODER_C, + POWER_DOMAIN_TRANSCODER_EDP = POWER_DOMAIN_TRANSCODER_A + 0xF, +}; + +#define POWER_DOMAIN_PIPE(pipe) ((pipe) + POWER_DOMAIN_PIPE_A) +#define POWER_DOMAIN_PIPE_PANEL_FITTER(pipe) \ + ((pipe) + POWER_DOMAIN_PIPE_A_PANEL_FITTER) +#define POWER_DOMAIN_TRANSCODER(tran) ((tran) + POWER_DOMAIN_TRANSCODER_A) + enum hpd_pin { HPD_NONE = 0, HPD_PORT_A = HPD_NONE, /* PORT_A is internal */ diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index b9d5711c595c..c46f40d88d8e 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1110,8 +1110,8 @@ void assert_pipe(struct drm_i915_private *dev_priv, if (pipe == PIPE_A && dev_priv->quirks & QUIRK_PIPEA_FORCE) state = true; - if (!intel_using_power_well(dev_priv->dev) && - cpu_transcoder != TRANSCODER_EDP) { + if (!intel_display_power_enabled(dev_priv->dev, + POWER_DOMAIN_TRANSCODER(cpu_transcoder))) { cur_state = false; } else { reg = PIPECONF(cpu_transcoder); @@ -3532,7 +3532,8 @@ static void haswell_crtc_disable(struct drm_crtc *crtc) /* XXX: Once we have proper panel fitter state tracking implemented with * hardware state read/check support we should switch to only disable * the panel fitter when we know it's used. */ - if (intel_using_power_well(dev)) { + if (intel_display_power_enabled(dev, + POWER_DOMAIN_PIPE_PANEL_FITTER(pipe))) { I915_WRITE(PF_CTL(pipe), 0); I915_WRITE(PF_WIN_SZ(pipe), 0); } @@ -6039,8 +6040,8 @@ static bool haswell_get_pipe_config(struct intel_crtc *crtc, enum transcoder cpu_transcoder = crtc->config.cpu_transcoder; uint32_t tmp; - if (!intel_using_power_well(dev_priv->dev) && - cpu_transcoder != TRANSCODER_EDP) + if (!intel_display_power_enabled(dev, + POWER_DOMAIN_TRANSCODER(cpu_transcoder))) return false; tmp = I915_READ(PIPECONF(cpu_transcoder)); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index e3108c0966c3..be9ad392b0e8 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -754,7 +754,8 @@ extern void intel_update_fbc(struct drm_device *dev); extern void intel_gpu_ips_init(struct drm_i915_private *dev_priv); extern void intel_gpu_ips_teardown(void); -extern bool intel_using_power_well(struct drm_device *dev); +extern bool intel_display_power_enabled(struct drm_device *dev, + enum intel_display_power_domain domain); extern void intel_init_power_well(struct drm_device *dev); extern void intel_set_power_well(struct drm_device *dev, bool enable); extern void intel_enable_gt_powersave(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 556b9892bb92..9b3e90e10ed7 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -4344,15 +4344,31 @@ void intel_init_clock_gating(struct drm_device *dev) * enable it, so check if it's enabled and also check if we've requested it to * be enabled. */ -bool intel_using_power_well(struct drm_device *dev) +bool intel_display_power_enabled(struct drm_device *dev, + enum intel_display_power_domain domain) { struct drm_i915_private *dev_priv = dev->dev_private; - if (IS_HASWELL(dev)) + if (!HAS_POWER_WELL(dev)) + return true; + + switch (domain) { + case POWER_DOMAIN_PIPE_A: + case POWER_DOMAIN_TRANSCODER_EDP: + return true; + case POWER_DOMAIN_PIPE_B: + case POWER_DOMAIN_PIPE_C: + case POWER_DOMAIN_PIPE_A_PANEL_FITTER: + case POWER_DOMAIN_PIPE_B_PANEL_FITTER: + case POWER_DOMAIN_PIPE_C_PANEL_FITTER: + case POWER_DOMAIN_TRANSCODER_A: + case POWER_DOMAIN_TRANSCODER_B: + case POWER_DOMAIN_TRANSCODER_C: return I915_READ(HSW_PWR_WELL_DRIVER) == (HSW_PWR_WELL_ENABLE | HSW_PWR_WELL_STATE); - else - return true; + default: + BUG(); + } } void intel_set_power_well(struct drm_device *dev, bool enable) -- cgit v1.2.3 From ff57f1b095e7c3ad0720193cd341d8ac1c781156 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 3 May 2013 12:15:37 -0300 Subject: drm/i915: add power well and cpu transcoder info to the error state We need to dump these registers if we want to properly interpret the others. Signed-off-by: Paulo Zanoni Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index c46f40d88d8e..260545cc5049 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -9756,6 +9756,9 @@ int intel_modeset_vga_set_state(struct drm_device *dev, bool state) #include struct intel_display_error_state { + + u32 power_well_driver; + struct intel_cursor_error_state { u32 control; u32 position; @@ -9764,6 +9767,7 @@ struct intel_display_error_state { } cursor[I915_MAX_PIPES]; struct intel_pipe_error_state { + enum transcoder cpu_transcoder; u32 conf; u32 source; @@ -9798,8 +9802,12 @@ intel_display_capture_error_state(struct drm_device *dev) if (error == NULL) return NULL; + if (HAS_POWER_WELL(dev)) + error->power_well_driver = I915_READ(HSW_PWR_WELL_DRIVER); + for_each_pipe(i) { cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv, i); + error->pipe[i].cpu_transcoder = cpu_transcoder; if (INTEL_INFO(dev)->gen <= 6 || IS_VALLEYVIEW(dev)) { error->cursor[i].control = I915_READ(CURCNTR(i)); @@ -9845,8 +9853,13 @@ intel_display_print_error_state(struct seq_file *m, int i; seq_printf(m, "Num Pipes: %d\n", INTEL_INFO(dev)->num_pipes); + if (HAS_POWER_WELL(dev)) + seq_printf(m, "PWR_WELL_CTL2: %08x\n", + error->power_well_driver); for_each_pipe(i) { seq_printf(m, "Pipe [%d]:\n", i); + seq_printf(m, " CPU transcoder: %c\n", + transcoder_name(error->pipe[i].cpu_transcoder)); seq_printf(m, " CONF: %08x\n", error->pipe[i].conf); seq_printf(m, " SRC: %08x\n", error->pipe[i].source); seq_printf(m, " HTOTAL: %08x\n", error->pipe[i].htotal); -- cgit v1.2.3 From 12d217c795071bfee483158e1397c57e8dc3cb76 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 3 May 2013 12:15:38 -0300 Subject: drm/i915: clear FPGA_DBG_RM_NOCLAIM when capturing error state In the error state function we read the registers without checking if the power well is on, so after doing this we have to clear the FPGA_DBG_RM_NOCLAIM bit to prevent the next I915_WRITE from detecting it and printing an error message. The first version of this patch was checking for the power well state and then avoiding reading registers that were off, but the reviewers requested to just read the registers any way and then later clear the FPGA_DBG_RM_NOCLAIM bit. Signed-off-by: Paulo Zanoni Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 260545cc5049..544d766f6ef3 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -9842,6 +9842,13 @@ intel_display_capture_error_state(struct drm_device *dev) error->pipe[i].vsync = I915_READ(VSYNC(cpu_transcoder)); } + /* In the code above we read the registers without checking if the power + * well was on, so here we have to clear the FPGA_DBG_RM_NOCLAIM bit to + * prevent the next I915_WRITE from detecting it and printing an error + * message. */ + if (HAS_POWER_WELL(dev)) + I915_WRITE_NOTRACE(FPGA_DBG, FPGA_DBG_RM_NOCLAIM); + return error; } -- cgit v1.2.3 From 71f8ba6b7e44fc525fb15a60997c0c5064c160e6 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 3 May 2013 12:15:39 -0300 Subject: drm/i915: check the power well on i915_pipe_enabled This fixes "unclaimed register" messages when the power well is disabled and there's a GPU hang. v2: Use the new intel_display_power_enabled(). v3: Use the new domains for intel_display_power_enabled(). Signed-off-by: Paulo Zanoni Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 03a31befa8fd..161101f42717 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -384,6 +384,10 @@ i915_pipe_enabled(struct drm_device *dev, int pipe) enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv, pipe); + if (!intel_display_power_enabled(dev, + POWER_DOMAIN_TRANSCODER(cpu_transcoder))) + return false; + return I915_READ(PIPECONF(cpu_transcoder)) & PIPECONF_ENABLE; } -- cgit v1.2.3 From c77bf5659deb9405ef61080c148e47d2c8ee31e5 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 3 May 2013 12:15:40 -0300 Subject: drm/i915: only disable DDI sound if intel_crtc->eld_vld We already have the same check on intel_enable_ddi. This patch prevents "unclaimed register" messages when the power well is disabled. V2: Reset intel_crtc->eld_vld to false after the mode_set function. V3: Add both "type != INTEL_OUTPUT_EDP" requested. Signed-off-by: Paulo Zanoni Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ddi.c | 11 +++++++---- drivers/gpu/drm/i915/intel_display.c | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index d7a33856a61a..3e946d3cd196 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -1300,7 +1300,7 @@ static void intel_enable_ddi(struct intel_encoder *intel_encoder) ironlake_edp_backlight_on(intel_dp); } - if (intel_crtc->eld_vld) { + if (intel_crtc->eld_vld && type != INTEL_OUTPUT_EDP) { tmp = I915_READ(HSW_AUD_PIN_ELD_CP_VLD); tmp |= ((AUDIO_OUTPUT_ENABLE_A | AUDIO_ELD_VALID_A) << (pipe * 4)); I915_WRITE(HSW_AUD_PIN_ELD_CP_VLD, tmp); @@ -1318,9 +1318,12 @@ static void intel_disable_ddi(struct intel_encoder *intel_encoder) struct drm_i915_private *dev_priv = dev->dev_private; uint32_t tmp; - tmp = I915_READ(HSW_AUD_PIN_ELD_CP_VLD); - tmp &= ~((AUDIO_OUTPUT_ENABLE_A | AUDIO_ELD_VALID_A) << (pipe * 4)); - I915_WRITE(HSW_AUD_PIN_ELD_CP_VLD, tmp); + if (intel_crtc->eld_vld && type != INTEL_OUTPUT_EDP) { + tmp = I915_READ(HSW_AUD_PIN_ELD_CP_VLD); + tmp &= ~((AUDIO_OUTPUT_ENABLE_A | AUDIO_ELD_VALID_A) << + (pipe * 4)); + I915_WRITE(HSW_AUD_PIN_ELD_CP_VLD, tmp); + } if (type == INTEL_OUTPUT_EDP) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 544d766f6ef3..d0c5ecff6948 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3864,8 +3864,8 @@ static void intel_crtc_disable(struct drm_crtc *crtc) /* crtc should still be enabled when we disable it. */ WARN_ON(!crtc->enabled); - intel_crtc->eld_vld = false; dev_priv->display.crtc_disable(crtc); + intel_crtc->eld_vld = false; intel_crtc_update_sarea(crtc, false); dev_priv->display.off(crtc); -- cgit v1.2.3 From ecdb4eb71b8f76db2bf58c86af907e7b8ee056b0 Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Fri, 3 May 2013 18:48:10 +0100 Subject: drm/i915: Add platform information to implemented workarounds Signed-off-by: Damien Lespiau Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.c | 6 ++-- drivers/gpu/drm/i915/intel_pm.c | 77 +++++++++++++++++++++-------------------- 2 files changed, 42 insertions(+), 41 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 624cdfcc1ba3..40b57871a0bf 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -1198,9 +1198,9 @@ MODULE_LICENSE("GPL and additional rights"); static void ilk_dummy_write(struct drm_i915_private *dev_priv) { - /* WaIssueDummyWriteToWakeupFromRC6: Issue a dummy write to wake up the - * chip from rc6 before touching it for real. MI_MODE is masked, hence - * harmless to write 0 into. */ + /* WaIssueDummyWriteToWakeupFromRC6:ilk Issue a dummy write to wake up + * the chip from rc6 before touching it for real. MI_MODE is masked, + * hence harmless to write 0 into. */ I915_WRITE_NOTRACE(MI_MODE, 0); } diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 9b3e90e10ed7..f95b97cf4a0d 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3808,7 +3808,7 @@ static void ironlake_init_clock_gating(struct drm_device *dev) _3D_CHICKEN2_WM_READ_PIPELINED << 16 | _3D_CHICKEN2_WM_READ_PIPELINED); - /* WaDisableRenderCachePipelinedFlush */ + /* WaDisableRenderCachePipelinedFlush:ilk */ I915_WRITE(CACHE_MODE_0, _MASKED_BIT_ENABLE(CM0_PIPELINED_RENDER_FLUSH_DISABLE)); @@ -3875,11 +3875,11 @@ static void gen6_init_clock_gating(struct drm_device *dev) I915_READ(ILK_DISPLAY_CHICKEN2) | ILK_ELPIN_409_SELECT); - /* WaDisableHiZPlanesWhenMSAAEnabled */ + /* WaDisableHiZPlanesWhenMSAAEnabled:snb */ I915_WRITE(_3D_CHICKEN, _MASKED_BIT_ENABLE(_3D_CHICKEN_HIZ_PLANE_DISABLE_MSAA_4X_SNB)); - /* WaSetupGtModeTdRowDispatch */ + /* WaSetupGtModeTdRowDispatch:snb */ if (IS_SNB_GT1(dev)) I915_WRITE(GEN6_GT_MODE, _MASKED_BIT_ENABLE(GEN6_TD_FOUR_ROW_DISPATCH_DISABLE)); @@ -3906,8 +3906,8 @@ static void gen6_init_clock_gating(struct drm_device *dev) * According to the spec, bit 11 (RCCUNIT) must also be set, * but we didn't debug actual testcases to find it out. * - * Also apply WaDisableVDSUnitClockGating and - * WaDisableRCPBUnitClockGating. + * Also apply WaDisableVDSUnitClockGating:snb and + * WaDisableRCPBUnitClockGating:snb. */ I915_WRITE(GEN6_UCGCTL2, GEN7_VDSUNIT_CLOCK_GATE_DISABLE | @@ -3938,7 +3938,7 @@ static void gen6_init_clock_gating(struct drm_device *dev) ILK_DPARBUNIT_CLOCK_GATE_ENABLE | ILK_DPFDUNIT_CLOCK_GATE_ENABLE); - /* WaMbcDriverBootEnable */ + /* WaMbcDriverBootEnable:snb */ I915_WRITE(GEN6_MBCTL, I915_READ(GEN6_MBCTL) | GEN6_MBCTL_ENABLE_BOOT_FETCH); @@ -3968,7 +3968,6 @@ static void gen7_setup_fixed_func_scheduler(struct drm_i915_private *dev_priv) reg |= GEN7_FF_VS_SCHED_HW; reg |= GEN7_FF_DS_SCHED_HW; - /* WaVSRefCountFullforceMissDisable */ if (IS_HASWELL(dev_priv->dev)) reg &= ~GEN7_FF_VS_REF_CNT_FFME; @@ -3999,21 +3998,21 @@ static void haswell_init_clock_gating(struct drm_device *dev) I915_WRITE(WM1_LP_ILK, 0); /* According to the spec, bit 13 (RCZUNIT) must be set on IVB. - * This implements the WaDisableRCZUnitClockGating workaround. + * This implements the WaDisableRCZUnitClockGating:hsw workaround. */ I915_WRITE(GEN6_UCGCTL2, GEN6_RCZUNIT_CLOCK_GATE_DISABLE); - /* Apply the WaDisableRHWOOptimizationForRenderHang workaround. */ + /* Apply the WaDisableRHWOOptimizationForRenderHang:hsw workaround. */ I915_WRITE(GEN7_COMMON_SLICE_CHICKEN1, GEN7_CSC1_RHWO_OPT_DISABLE_IN_RCC); - /* WaApplyL3ControlAndL3ChickenMode requires those two on Ivy Bridge */ + /* WaApplyL3ControlAndL3ChickenMode:hsw */ I915_WRITE(GEN7_L3CNTLREG1, GEN7_WA_FOR_GEN7_L3_CONTROL); I915_WRITE(GEN7_L3_CHICKEN_MODE_REGISTER, GEN7_WA_L3_CHICKEN_MODE); - /* This is required by WaCatErrorRejectionIssue */ + /* This is required by WaCatErrorRejectionIssue:hsw */ I915_WRITE(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG, I915_READ(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG) | GEN7_SQ_CHICKEN_MBCUNIT_SQINTMOB); @@ -4025,17 +4024,18 @@ static void haswell_init_clock_gating(struct drm_device *dev) intel_flush_display_plane(dev_priv, pipe); } + /* WaVSRefCountFullforceMissDisable:hsw */ gen7_setup_fixed_func_scheduler(dev_priv); - /* WaDisable4x2SubspanOptimization */ + /* WaDisable4x2SubspanOptimization:hsw */ I915_WRITE(CACHE_MODE_1, _MASKED_BIT_ENABLE(PIXEL_SUBSPAN_COLLECT_OPT_DISABLE)); - /* WaMbcDriverBootEnable */ + /* WaMbcDriverBootEnable:hsw */ I915_WRITE(GEN6_MBCTL, I915_READ(GEN6_MBCTL) | GEN6_MBCTL_ENABLE_BOOT_FETCH); - /* WaSwitchSolVfFArbitrationPriority */ + /* WaSwitchSolVfFArbitrationPriority:hsw */ I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) | HSW_ECOCHK_ARB_PRIO_SOL); /* XXX: This is a workaround for early silicon revisions and should be @@ -4062,16 +4062,16 @@ static void ivybridge_init_clock_gating(struct drm_device *dev) I915_WRITE(ILK_DSPCLK_GATE_D, ILK_VRHUNIT_CLOCK_GATE_DISABLE); - /* WaDisableEarlyCull */ + /* WaDisableEarlyCull:ivb */ I915_WRITE(_3D_CHICKEN3, _MASKED_BIT_ENABLE(_3D_CHICKEN_SF_DISABLE_OBJEND_CULL)); - /* WaDisableBackToBackFlipFix */ + /* WaDisableBackToBackFlipFix:ivb */ I915_WRITE(IVB_CHICKEN3, CHICKEN3_DGMG_REQ_OUT_FIX_DISABLE | CHICKEN3_DGMG_DONE_FIX_DISABLE); - /* WaDisablePSDDualDispatchEnable */ + /* WaDisablePSDDualDispatchEnable:ivb */ if (IS_IVB_GT1(dev)) I915_WRITE(GEN7_HALF_SLICE_CHICKEN1, _MASKED_BIT_ENABLE(GEN7_PSD_SINGLE_PORT_DISPATCH_ENABLE)); @@ -4079,11 +4079,11 @@ static void ivybridge_init_clock_gating(struct drm_device *dev) I915_WRITE(GEN7_HALF_SLICE_CHICKEN1_GT2, _MASKED_BIT_ENABLE(GEN7_PSD_SINGLE_PORT_DISPATCH_ENABLE)); - /* Apply the WaDisableRHWOOptimizationForRenderHang workaround. */ + /* Apply the WaDisableRHWOOptimizationForRenderHang:ivb workaround. */ I915_WRITE(GEN7_COMMON_SLICE_CHICKEN1, GEN7_CSC1_RHWO_OPT_DISABLE_IN_RCC); - /* WaApplyL3ControlAndL3ChickenMode requires those two on Ivy Bridge */ + /* WaApplyL3ControlAndL3ChickenMode:ivb */ I915_WRITE(GEN7_L3CNTLREG1, GEN7_WA_FOR_GEN7_L3_CONTROL); I915_WRITE(GEN7_L3_CHICKEN_MODE_REGISTER, @@ -4096,7 +4096,7 @@ static void ivybridge_init_clock_gating(struct drm_device *dev) _MASKED_BIT_ENABLE(DOP_CLOCK_GATING_DISABLE)); - /* WaForceL3Serialization */ + /* WaForceL3Serialization:ivb */ I915_WRITE(GEN7_L3SQCREG4, I915_READ(GEN7_L3SQCREG4) & ~L3SQ_URB_READ_CAM_MATCH_DISABLE); @@ -4111,13 +4111,13 @@ static void ivybridge_init_clock_gating(struct drm_device *dev) * but we didn't debug actual testcases to find it out. * * According to the spec, bit 13 (RCZUNIT) must be set on IVB. - * This implements the WaDisableRCZUnitClockGating workaround. + * This implements the WaDisableRCZUnitClockGating:ivb workaround. */ I915_WRITE(GEN6_UCGCTL2, GEN6_RCZUNIT_CLOCK_GATE_DISABLE | GEN6_RCCUNIT_CLOCK_GATE_DISABLE); - /* This is required by WaCatErrorRejectionIssue */ + /* This is required by WaCatErrorRejectionIssue:ivb */ I915_WRITE(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG, I915_READ(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG) | GEN7_SQ_CHICKEN_MBCUNIT_SQINTMOB); @@ -4129,13 +4129,14 @@ static void ivybridge_init_clock_gating(struct drm_device *dev) intel_flush_display_plane(dev_priv, pipe); } - /* WaMbcDriverBootEnable */ + /* WaMbcDriverBootEnable:ivb */ I915_WRITE(GEN6_MBCTL, I915_READ(GEN6_MBCTL) | GEN6_MBCTL_ENABLE_BOOT_FETCH); + /* WaVSRefCountFullforceMissDisable:ivb */ gen7_setup_fixed_func_scheduler(dev_priv); - /* WaDisable4x2SubspanOptimization */ + /* WaDisable4x2SubspanOptimization:ivb */ I915_WRITE(CACHE_MODE_1, _MASKED_BIT_ENABLE(PIXEL_SUBSPAN_COLLECT_OPT_DISABLE)); @@ -4161,46 +4162,46 @@ static void valleyview_init_clock_gating(struct drm_device *dev) I915_WRITE(ILK_DSPCLK_GATE_D, ILK_VRHUNIT_CLOCK_GATE_DISABLE); - /* WaDisableEarlyCull */ + /* WaDisableEarlyCull:vlv */ I915_WRITE(_3D_CHICKEN3, _MASKED_BIT_ENABLE(_3D_CHICKEN_SF_DISABLE_OBJEND_CULL)); - /* WaDisableBackToBackFlipFix */ + /* WaDisableBackToBackFlipFix:vlv */ I915_WRITE(IVB_CHICKEN3, CHICKEN3_DGMG_REQ_OUT_FIX_DISABLE | CHICKEN3_DGMG_DONE_FIX_DISABLE); - /* WaDisablePSDDualDispatchEnable */ + /* WaDisablePSDDualDispatchEnable:vlv */ I915_WRITE(GEN7_HALF_SLICE_CHICKEN1, _MASKED_BIT_ENABLE(GEN7_MAX_PS_THREAD_DEP | GEN7_PSD_SINGLE_PORT_DISPATCH_ENABLE)); - /* Apply the WaDisableRHWOOptimizationForRenderHang workaround. */ + /* Apply the WaDisableRHWOOptimizationForRenderHang:vlv workaround. */ I915_WRITE(GEN7_COMMON_SLICE_CHICKEN1, GEN7_CSC1_RHWO_OPT_DISABLE_IN_RCC); - /* WaApplyL3ControlAndL3ChickenMode requires those two on Ivy Bridge */ + /* WaApplyL3ControlAndL3ChickenMode:vlv */ I915_WRITE(GEN7_L3CNTLREG1, I915_READ(GEN7_L3CNTLREG1) | GEN7_L3AGDIS); I915_WRITE(GEN7_L3_CHICKEN_MODE_REGISTER, GEN7_WA_L3_CHICKEN_MODE); - /* WaForceL3Serialization */ + /* WaForceL3Serialization:vlv */ I915_WRITE(GEN7_L3SQCREG4, I915_READ(GEN7_L3SQCREG4) & ~L3SQ_URB_READ_CAM_MATCH_DISABLE); - /* WaDisableDopClockGating */ + /* WaDisableDopClockGating:vlv */ I915_WRITE(GEN7_ROW_CHICKEN2, _MASKED_BIT_ENABLE(DOP_CLOCK_GATING_DISABLE)); - /* WaForceL3Serialization */ + /* WaForceL3Serialization:vlv */ I915_WRITE(GEN7_L3SQCREG4, I915_READ(GEN7_L3SQCREG4) & ~L3SQ_URB_READ_CAM_MATCH_DISABLE); - /* This is required by WaCatErrorRejectionIssue */ + /* This is required by WaCatErrorRejectionIssue:vlv */ I915_WRITE(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG, I915_READ(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG) | GEN7_SQ_CHICKEN_MBCUNIT_SQINTMOB); - /* WaMbcDriverBootEnable */ + /* WaMbcDriverBootEnable:vlv */ I915_WRITE(GEN6_MBCTL, I915_READ(GEN6_MBCTL) | GEN6_MBCTL_ENABLE_BOOT_FETCH); @@ -4216,10 +4217,10 @@ static void valleyview_init_clock_gating(struct drm_device *dev) * but we didn't debug actual testcases to find it out. * * According to the spec, bit 13 (RCZUNIT) must be set on IVB. - * This implements the WaDisableRCZUnitClockGating workaround. + * This implements the WaDisableRCZUnitClockGating:vlv workaround. * - * Also apply WaDisableVDSUnitClockGating and - * WaDisableRCPBUnitClockGating. + * Also apply WaDisableVDSUnitClockGating:vlv and + * WaDisableRCPBUnitClockGating:vlv. */ I915_WRITE(GEN6_UCGCTL2, GEN7_VDSUNIT_CLOCK_GATE_DISABLE | @@ -4241,7 +4242,7 @@ static void valleyview_init_clock_gating(struct drm_device *dev) _MASKED_BIT_ENABLE(PIXEL_SUBSPAN_COLLECT_OPT_DISABLE)); /* - * WaDisableVLVClockGating_VBIIssue + * WaDisableVLVClockGating_VBIIssue:vlv * Disable clock gating on th GCFG unit to prevent a delay * in the reporting of vblank events. */ -- cgit v1.2.3 From 8693a824873a0a97be77c1d5f1e98777f06f7305 Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Fri, 3 May 2013 18:48:11 +0100 Subject: drm/i915: Add references to some workaround we implement We did not mention the workaround name when implementing those. This should help us track what we already implement. Signed-off-by: Damien Lespiau Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_context.c | 1 + drivers/gpu/drm/i915/intel_ddi.c | 2 ++ drivers/gpu/drm/i915/intel_display.c | 4 ++-- drivers/gpu/drm/i915/intel_pm.c | 3 +++ drivers/gpu/drm/i915/intel_ringbuffer.c | 2 ++ 5 files changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index c81ae52c8c2e..64cb1909a0ce 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -333,6 +333,7 @@ mi_set_context(struct intel_ring_buffer *ring, if (ret) return ret; + /* WaProgramMiArbOnOffAroundMiSetContext:ivb,vlv,hsw */ if (IS_GEN7(ring->dev)) intel_ring_emit(ring, MI_ARB_ON_OFF | MI_ARB_DISABLE); else diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 3e946d3cd196..146893288e53 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -174,6 +174,8 @@ void hsw_fdi_link_train(struct drm_crtc *crtc) * mode set "sequence for CRT port" document: * - TP1 to TP2 time with the default value * - FDI delay to 90h + * + * WaFDIAutoLinkSetTimingOverrride:hsw */ I915_WRITE(_FDI_RXA_MISC, FDI_RX_PWRDN_LANE1_VAL(2) | FDI_RX_PWRDN_LANE0_VAL(2) | diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index d0c5ecff6948..f5523a80efc9 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4125,8 +4125,8 @@ static int intel_crtc_compute_config(struct drm_crtc *crtc, if (!pipe_config->timings_set) drm_mode_set_crtcinfo(adjusted_mode, 0); - /* WaPruneModeWithIncorrectHsyncOffset: Cantiga+ cannot handle modes - * with a hsync front porch of 0. + /* Cantiga+ cannot handle modes with a hsync front porch of 0. + * WaPruneModeWithIncorrectHsyncOffset:ctg,elk,ilk,snb,ivb,vlv,hsw. */ if ((INTEL_INFO(dev)->gen > 4 || IS_G4X(dev)) && adjusted_mode->hsync_start == adjusted_mode->hdisplay) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index f95b97cf4a0d..5093b8612bc4 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -4586,6 +4586,7 @@ static void __gen6_gt_force_wake_get(struct drm_i915_private *dev_priv) FORCEWAKE_ACK_TIMEOUT_MS)) DRM_ERROR("Timed out waiting for forcewake to ack request.\n"); + /* WaRsForcewakeWaitTC0:snb */ __gen6_gt_wait_for_thread_c0(dev_priv); } @@ -4617,6 +4618,7 @@ static void __gen6_gt_force_wake_mt_get(struct drm_i915_private *dev_priv) FORCEWAKE_ACK_TIMEOUT_MS)) DRM_ERROR("Timed out waiting for forcewake to ack request.\n"); + /* WaRsForcewakeWaitTC0:ivb,hsw */ __gen6_gt_wait_for_thread_c0(dev_priv); } @@ -4720,6 +4722,7 @@ static void vlv_force_wake_get(struct drm_i915_private *dev_priv) FORCEWAKE_ACK_TIMEOUT_MS)) DRM_ERROR("Timed out waiting for media to ack forcewake request.\n"); + /* WaRsForcewakeWaitTC0:vlv */ __gen6_gt_wait_for_thread_c0(dev_priv); } diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 1d5d613eb6be..3d2c236e15ab 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -515,6 +515,8 @@ static int init_render_ring(struct intel_ring_buffer *ring) /* We need to disable the AsyncFlip performance optimisations in order * to use MI_WAIT_FOR_EVENT within the CS. It should already be * programmed to '1' on all products. + * + * WaDisableAsyncFlipPerfMode:snb,ivb,hsw,vlv */ if (INTEL_INFO(dev)->gen >= 6) I915_WRITE(MI_MODE, _MASKED_BIT_ENABLE(ASYNC_FLIP_PERF_DISABLE)); -- cgit v1.2.3 From bc5ead8c09b51e85d110132495a9bfa58dc39dab Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 7 May 2013 15:10:29 +0300 Subject: drm/i915: fix hotplug event bit tracking commit 142e239849c800f9dc23f828762873073f612d3f Author: Egbert Eich Date: Thu Apr 11 15:57:57 2013 +0200 drm/i915: Add bit field to record which pins have received HPD events (v3) added a bit field for hotplug event tracking. There ended up being three different v3 of the patch: [1], [2], and [3]. Apparently [1] was the correct one, but some frankenstein combination of the three got committed, which reversed the logic for setting the hotplug bits and misplaced a continue statement, skipping the hotplug irq storm handling altogether. This lead to broken hotplug detection, bisected to commit 321a1b3026ea194dd084cf3bda1e235b2986b0af Author: Egbert Eich Date: Thu Apr 11 16:00:26 2013 +0200 drm/i915: Only reprobe display on encoder which has received an HPD event (v2) which uses the incorrectly set hotplug event bits. Fix the mess. [1] http://mid.gmane.org/1366112220-7638-6-git-send-email-eich@suse.de [2] http://mid.gmane.org/1365688677-13682-1-git-send-email-eich@suse.de [3] http://mid.gmane.org/1365688996-13874-1-git-send-email-eich@suse.de Signed-off-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 161101f42717..879c4ccb00db 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -882,9 +882,9 @@ static inline bool hotplug_irq_storm_detect(struct drm_device *dev, if (!(hpd[i] & hotplug_trigger) || dev_priv->hpd_stats[i].hpd_mark != HPD_ENABLED) - dev_priv->hpd_event_bits |= (1 << i); continue; + dev_priv->hpd_event_bits |= (1 << i); if (!time_in_range(jiffies, dev_priv->hpd_stats[i].hpd_last_jiffies, dev_priv->hpd_stats[i].hpd_last_jiffies + msecs_to_jiffies(HPD_STORM_DETECT_PERIOD))) { -- cgit v1.2.3 From 7d708ee40a6b9ca1112a322e554c887df105b025 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Wed, 17 Apr 2013 14:04:50 +0300 Subject: drm/i915: HSW: allow PCH clock gating for suspend For the device to enter D3 we should enable PCH clock gating. v2: - use HAS_PCH_LPT instead of IS_HASWELL (Ville, Paolo) - rename lpt_allow_clock_gating to lpt_suspend_hw (Paolo) Signed-off-by: Imre Deak Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.c | 2 ++ drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/intel_display.c | 5 +++++ drivers/gpu/drm/i915/intel_drv.h | 1 + drivers/gpu/drm/i915/intel_pm.c | 18 ++++++++++++++++++ 5 files changed, 27 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 40b57871a0bf..707a56ec389d 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -529,6 +529,8 @@ static int i915_drm_freeze(struct drm_device *dev) */ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) dev_priv->display.crtc_disable(crtc); + + intel_modeset_suspend_hw(dev); } i915_save_state(dev); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index c81100c54e24..0ef9b4c6b4e5 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1865,6 +1865,7 @@ static inline void intel_unregister_dsm_handler(void) { return; } /* modesetting */ extern void intel_modeset_init_hw(struct drm_device *dev); +extern void intel_modeset_suspend_hw(struct drm_device *dev); extern void intel_modeset_init(struct drm_device *dev); extern void intel_modeset_gem_init(struct drm_device *dev); extern void intel_modeset_cleanup(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index f5523a80efc9..b89072252b6c 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -9271,6 +9271,11 @@ void intel_modeset_init_hw(struct drm_device *dev) mutex_unlock(&dev->struct_mutex); } +void intel_modeset_suspend_hw(struct drm_device *dev) +{ + intel_suspend_hw(dev); +} + void intel_modeset_init(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index be9ad392b0e8..6096871c4806 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -716,6 +716,7 @@ extern void assert_pipe(struct drm_i915_private *dev_priv, enum pipe pipe, #define assert_pipe_disabled(d, p) assert_pipe(d, p, false) extern void intel_init_clock_gating(struct drm_device *dev); +extern void intel_suspend_hw(struct drm_device *dev); extern void intel_write_eld(struct drm_encoder *encoder, struct drm_display_mode *mode); extern void intel_prepare_ddi(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 5093b8612bc4..22e3af68b136 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3988,6 +3988,18 @@ static void lpt_init_clock_gating(struct drm_device *dev) PCH_LP_PARTITION_LEVEL_DISABLE); } +static void lpt_suspend_hw(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + if (dev_priv->pch_id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE) { + uint32_t val = I915_READ(SOUTH_DSPCLK_GATE_D); + + val &= ~PCH_LP_PARTITION_LEVEL_DISABLE; + I915_WRITE(SOUTH_DSPCLK_GATE_D, val); + } +} + static void haswell_init_clock_gating(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -4340,6 +4352,12 @@ void intel_init_clock_gating(struct drm_device *dev) dev_priv->display.init_clock_gating(dev); } +void intel_suspend_hw(struct drm_device *dev) +{ + if (HAS_PCH_LPT(dev)) + lpt_suspend_hw(dev); +} + /** * We should only use the power well if we explicitly asked the hardware to * enable it, so check if it's enabled and also check if we've requested it to -- cgit v1.2.3 From 615aaa5f96e40971039e01105c09808a3dead7d5 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Wed, 24 Apr 2013 21:09:10 +0300 Subject: drm/i915: Re-enable FBC WM if the watermark is good on gen6+ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the calculated FBC watermark is no good, we simply disable FBC watermarks. But we fail to re-enable them later if the calculated watermark becomes good again. Fix that, but remember to leave FBC watermarks disabled on ILK since that's required by some workarounds. v2: Fix checkpatch complaint Signed-off-by: Ville Syrjälä Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_pm.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 22e3af68b136..7d35f4301155 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -1631,6 +1631,10 @@ static bool ironlake_check_srwm(struct drm_device *dev, int level, I915_WRITE(DISP_ARB_CTL, I915_READ(DISP_ARB_CTL) | DISP_FBC_WM_DIS); return false; + } else if (INTEL_INFO(dev)->gen >= 6) { + /* enable FBC WM (except on ILK, where it must remain off) */ + I915_WRITE(DISP_ARB_CTL, + I915_READ(DISP_ARB_CTL) & ~DISP_FBC_WM_DIS); } if (display_wm > display->max_wm) { -- cgit v1.2.3 From c9cddffc669408a361c62353a36dd40b469dd9c2 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Wed, 8 May 2013 10:45:13 -0700 Subject: drm/i915: BIOS and power context stolen mem handling for VLV v7 But we need to get the right stolen base and make pre-allocated objects for BIOS stuff so we don't clobber it. If the BIOS hasn't allocated a power context, we allocate one here too, from stolen space as required by the docs. v2: fix stolen to phys if ladder (Ben) keep BIOS reserved space out of allocator altogether (Ben) v3: fix mask of stolen base (Ben) v4: clean up preallocated object on unload (Ben) don't zero reg on unload (Jesse) fix mask harder (Jesse) v5: use unref for freeing stolen bits (Chris) move alloc/free to intel_pm.c (Chris) v6: NULL pctx at disable time so error paths work (Ben) v7: use correct PCI device for config read (Jesse) Reviewed-by: Ben Widawsky Signed-off-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 2 ++ drivers/gpu/drm/i915/i915_gem_stolen.c | 12 +++++++-- drivers/gpu/drm/i915/i915_reg.h | 1 + drivers/gpu/drm/i915/intel_pm.c | 49 ++++++++++++++++++++++++++++++++++ 4 files changed, 62 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 0ef9b4c6b4e5..eeec0be671b7 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1073,6 +1073,8 @@ typedef struct drm_i915_private { struct i915_gpu_error gpu_error; + struct drm_i915_gem_object *vlv_pctx; + /* list of fbdev register on this device */ struct intel_fbdev *fbdev; diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c index 67d3510cafed..913994cd0a3a 100644 --- a/drivers/gpu/drm/i915/i915_gem_stolen.c +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c @@ -62,7 +62,10 @@ static unsigned long i915_stolen_to_physical(struct drm_device *dev) * its value of TOLUD. */ base = 0; - if (INTEL_INFO(dev)->gen >= 6) { + if (IS_VALLEYVIEW(dev)) { + pci_read_config_dword(dev->pdev, 0x5c, &base); + base &= ~((1<<20) - 1); + } else if (INTEL_INFO(dev)->gen >= 6) { /* Read Base Data of Stolen Memory Register (BDSM) directly. * Note that there is also a MCHBAR miror at 0x1080c0 or * we could use device 2:0x5c instead. @@ -183,6 +186,7 @@ void i915_gem_cleanup_stolen(struct drm_device *dev) int i915_gem_init_stolen(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + int bios_reserved = 0; dev_priv->mm.stolen_base = i915_stolen_to_physical(dev); if (dev_priv->mm.stolen_base == 0) @@ -191,8 +195,12 @@ int i915_gem_init_stolen(struct drm_device *dev) DRM_DEBUG_KMS("found %zd bytes of stolen memory at %08lx\n", dev_priv->gtt.stolen_size, dev_priv->mm.stolen_base); + if (IS_VALLEYVIEW(dev)) + bios_reserved = 1024*1024; /* top 1M on VLV/BYT */ + /* Basic memrange allocator for stolen space */ - drm_mm_init(&dev_priv->mm.stolen, 0, dev_priv->gtt.stolen_size); + drm_mm_init(&dev_priv->mm.stolen, 0, dev_priv->gtt.stolen_size - + bios_reserved); return 0; } diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index a47010331e72..a809a5633b28 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -700,6 +700,7 @@ #define VLV_IIR (VLV_DISPLAY_BASE + 0x20a4) #define VLV_IMR (VLV_DISPLAY_BASE + 0x20a8) #define VLV_ISR (VLV_DISPLAY_BASE + 0x20ac) +#define VLV_PCBR (VLV_DISPLAY_BASE + 0x2120) #define I915_PIPE_CONTROL_NOTIFY_INTERRUPT (1<<18) #define I915_DISPLAY_PORT_INTERRUPT (1<<17) #define I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT (1<<15) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 7d35f4301155..67294f4cd3a5 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -2566,6 +2566,11 @@ static void valleyview_disable_rps(struct drm_device *dev) spin_unlock_irq(&dev_priv->rps.lock); I915_WRITE(GEN6_PMIIR, I915_READ(GEN6_PMIIR)); + + if (dev_priv->vlv_pctx) { + drm_gem_object_unreference(&dev_priv->vlv_pctx->base); + dev_priv->vlv_pctx = NULL; + } } int intel_enable_rc6(const struct drm_device *dev) @@ -2860,6 +2865,48 @@ static void vlv_rps_timer_work(struct work_struct *work) mutex_unlock(&dev_priv->rps.hw_lock); } +static void valleyview_setup_pctx(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_gem_object *pctx; + unsigned long pctx_paddr; + u32 pcbr; + int pctx_size = 24*1024; + + pcbr = I915_READ(VLV_PCBR); + if (pcbr) { + /* BIOS set it up already, grab the pre-alloc'd space */ + int pcbr_offset; + + pcbr_offset = (pcbr & (~4095)) - dev_priv->mm.stolen_base; + pctx = i915_gem_object_create_stolen_for_preallocated(dev_priv->dev, + pcbr_offset, + pcbr_offset, + pctx_size); + goto out; + } + + /* + * From the Gunit register HAS: + * The Gfx driver is expected to program this register and ensure + * proper allocation within Gfx stolen memory. For example, this + * register should be programmed such than the PCBR range does not + * overlap with other ranges, such as the frame buffer, protected + * memory, or any other relevant ranges. + */ + pctx = i915_gem_object_create_stolen(dev, pctx_size); + if (!pctx) { + DRM_DEBUG("not enough stolen space for PCTX, disabling\n"); + return; + } + + pctx_paddr = dev_priv->mm.stolen_base + pctx->stolen->start; + I915_WRITE(VLV_PCBR, pctx_paddr); + +out: + dev_priv->vlv_pctx = pctx; +} + static void valleyview_enable_rps(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -2874,6 +2921,8 @@ static void valleyview_enable_rps(struct drm_device *dev) I915_WRITE(GTFIFODBG, gtfifodbg); } + valleyview_setup_pctx(dev); + gen6_gt_force_wake_get(dev_priv); I915_WRITE(GEN6_RP_UP_THRESHOLD, 59400); -- cgit v1.2.3 From 3727d55e4d85836aa6cb759a965daaef88074150 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Wed, 8 May 2013 10:45:14 -0700 Subject: drm/i915: allow stolen, pre-allocated objects to avoid GTT allocation v2 In some cases, we may not need GTT address space allocated to a stolen object, so allow passing -1 to the preallocated function to indicate as much. v2: remove BUG_ON(gtt_offset & 4095) now that -1 is allowed (Ville) Reviewed-by: Chris Wilson Signed-off-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_stolen.c | 5 ++++- drivers/gpu/drm/i915/intel_pm.c | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c index 913994cd0a3a..89cbfab9570e 100644 --- a/drivers/gpu/drm/i915/i915_gem_stolen.c +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c @@ -339,7 +339,6 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev, /* KISS and expect everything to be page-aligned */ BUG_ON(stolen_offset & 4095); - BUG_ON(gtt_offset & 4095); BUG_ON(size & 4095); if (WARN_ON(size == 0)) @@ -360,6 +359,10 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev, return NULL; } + /* Some objects just need physical mem from stolen space */ + if (gtt_offset == -1) + return obj; + /* To simplify the initialisation sequence between KMS and GTT, * we allow construction of the stolen object prior to * setting up the GTT space. The actual reservation will occur diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 67294f4cd3a5..6df886e06b64 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -2881,7 +2881,7 @@ static void valleyview_setup_pctx(struct drm_device *dev) pcbr_offset = (pcbr & (~4095)) - dev_priv->mm.stolen_base; pctx = i915_gem_object_create_stolen_for_preallocated(dev_priv->dev, pcbr_offset, - pcbr_offset, + -1, pctx_size); goto out; } -- cgit v1.2.3 From 590e4df8c82e6c2707ae12ba6672ab6fb9cd4b89 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Wed, 8 May 2013 10:45:15 -0700 Subject: drm/i915: VLV support is no longer preliminary Works pretty well actually. Signed-off-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 707a56ec389d..3cb27fa842ee 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -966,12 +966,6 @@ static int i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) struct intel_device_info *intel_info = (struct intel_device_info *) ent->driver_data; - if (intel_info->is_valleyview) - if(!i915_preliminary_hw_support) { - DRM_ERROR("Preliminary hardware support disabled\n"); - return -ENODEV; - } - /* Only bind to function 0 of the device. Early generations * used function 1 as a placeholder for multi-head. This causes * us confusion instead, especially on the systems where both -- cgit v1.2.3 From 9ff8c9bac2d78634f34d9b6e43e04bf316a7f456 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Wed, 8 May 2013 13:14:02 +0300 Subject: drm/i915: use enc_to_intel_dp() instead of opencoding the same Signed-off-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_drv.h | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 6096871c4806..b1f50b15c6f3 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -620,19 +620,17 @@ static inline struct intel_encoder *intel_attached_encoder(struct drm_connector return to_intel_connector(connector)->encoder; } -static inline struct intel_dp *enc_to_intel_dp(struct drm_encoder *encoder) -{ - struct intel_digital_port *intel_dig_port = - container_of(encoder, struct intel_digital_port, base.base); - return &intel_dig_port->dp; -} - static inline struct intel_digital_port * enc_to_dig_port(struct drm_encoder *encoder) { return container_of(encoder, struct intel_digital_port, base.base); } +static inline struct intel_dp *enc_to_intel_dp(struct drm_encoder *encoder) +{ + return &enc_to_dig_port(encoder)->dp; +} + static inline struct intel_digital_port * dp_to_dig_port(struct intel_dp *intel_dp) { -- cgit v1.2.3 From d8e8b582b4e685a0e2a95ca0f4862582d465c649 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Wed, 8 May 2013 13:14:03 +0300 Subject: drm/i915: hsw: replace !is_pch_edp() with port==PORT_A On HSW the CPU side eDP is always on port-A, the PCH side eDP is always on port-D. Signed-off-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index b89072252b6c..157a68f48060 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5969,7 +5969,7 @@ static int haswell_crtc_mode_set(struct drm_crtc *crtc, for_each_encoder_on_crtc(dev, crtc, encoder) { switch (encoder->type) { case INTEL_OUTPUT_EDP: - if (!intel_encoder_is_pch_edp(&encoder->base)) + if (enc_to_dig_port(&encoder->base)->port == PORT_A) is_cpu_edp = true; break; } -- cgit v1.2.3 From 2de6905f0a30c8fbe293e1e3ecdb766bbf5f7760 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Wed, 8 May 2013 13:14:04 +0300 Subject: drm/i915: ilk-ivb: replace !is_pch_edp() with port==PORT_A On ILK-IVB the CPU side eDP is always on port-A. Also reduce somewhat the debug verbosity. v2: - reduce debug verbosity Signed-off-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 157a68f48060..4ca2a3d885a0 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5045,7 +5045,6 @@ static void ironlake_init_pch_refclk(struct drm_device *dev) u32 val, final; bool has_lvds = false; bool has_cpu_edp = false; - bool has_pch_edp = false; bool has_panel = false; bool has_ck505 = false; bool can_ssc = false; @@ -5060,9 +5059,7 @@ static void ironlake_init_pch_refclk(struct drm_device *dev) break; case INTEL_OUTPUT_EDP: has_panel = true; - if (intel_encoder_is_pch_edp(&encoder->base)) - has_pch_edp = true; - else + if (enc_to_dig_port(&encoder->base)->port == PORT_A) has_cpu_edp = true; break; } @@ -5076,9 +5073,8 @@ static void ironlake_init_pch_refclk(struct drm_device *dev) can_ssc = true; } - DRM_DEBUG_KMS("has_panel %d has_lvds %d has_pch_edp %d has_cpu_edp %d has_ck505 %d\n", - has_panel, has_lvds, has_pch_edp, has_cpu_edp, - has_ck505); + DRM_DEBUG_KMS("has_panel %d has_lvds %d has_ck505 %d\n", + has_panel, has_lvds, has_ck505); /* Ironlake: try to setup display ref clock before DPLL * enabling. This is only under driver's control after -- cgit v1.2.3 From f7d24902e197824eee717e4c3f406eb8623e67e0 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Wed, 8 May 2013 13:14:05 +0300 Subject: drm/i915: stop using is_pch_edp() in intel_dp_init_connector() is_pch_edp() will be removed in a follow-up patch, so replace it with a check for the port and VBT info (for port-D eDP). Also make things a bit clearer by using a switch on the ports. v2: - make the comment about not setting the conder type for DP clearer (Ville) Signed-off-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index a2935234ac03..6f3a9e8f7078 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2980,24 +2980,35 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, if (intel_dpd_is_edp(dev)) intel_dp->is_pch_edp = true; + type = DRM_MODE_CONNECTOR_DisplayPort; /* * FIXME : We need to initialize built-in panels before external panels. * For X0, DP_C is fixed as eDP. Revisit this as part of VLV eDP cleanup */ - if (IS_VALLEYVIEW(dev) && port == PORT_C) { - type = DRM_MODE_CONNECTOR_eDP; - intel_encoder->type = INTEL_OUTPUT_EDP; - } else if (port == PORT_A || is_pch_edp(intel_dp)) { + switch (port) { + case PORT_A: type = DRM_MODE_CONNECTOR_eDP; - intel_encoder->type = INTEL_OUTPUT_EDP; - } else { - /* The intel_encoder->type value may be INTEL_OUTPUT_UNKNOWN for - * DDI or INTEL_OUTPUT_DISPLAYPORT for the older gens, so don't - * rewrite it. - */ - type = DRM_MODE_CONNECTOR_DisplayPort; + break; + case PORT_C: + if (IS_VALLEYVIEW(dev)) + type = DRM_MODE_CONNECTOR_eDP; + break; + case PORT_D: + if (HAS_PCH_SPLIT(dev) && intel_dpd_is_edp(dev)) + type = DRM_MODE_CONNECTOR_eDP; + break; + default: /* silence GCC warning */ + break; } + /* + * For eDP we always set the encoder type to INTEL_OUTPUT_EDP, but + * for DP the encoder type can be set by the caller to + * INTEL_OUTPUT_UNKNOWN for DDI, so don't rewrite it. + */ + if (type == DRM_MODE_CONNECTOR_eDP) + intel_encoder->type = INTEL_OUTPUT_EDP; + drm_connector_init(dev, connector, &intel_dp_connector_funcs, type); drm_connector_helper_add(connector, &intel_dp_connector_helper_funcs); -- cgit v1.2.3 From 68b4d8247033b425ae948f02885c2aed5ae823f3 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Wed, 8 May 2013 13:14:06 +0300 Subject: drm/i915: stop using is_pch_edp() in is_cpu_edp() is_pch_edp() will be removed by the next patch, so replace it by a check for the port and device type. Signed-off-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 6f3a9e8f7078..3ab683af5256 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -65,6 +65,13 @@ static bool is_pch_edp(struct intel_dp *intel_dp) return intel_dp->is_pch_edp; } +static struct drm_device *intel_dp_to_dev(struct intel_dp *intel_dp) +{ + struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); + + return intel_dig_port->base.base.dev; +} + /** * is_cpu_edp - is the port on the CPU and attached to an eDP panel? * @intel_dp: DP struct @@ -73,14 +80,12 @@ static bool is_pch_edp(struct intel_dp *intel_dp) */ static bool is_cpu_edp(struct intel_dp *intel_dp) { - return is_edp(intel_dp) && !is_pch_edp(intel_dp); -} - -static struct drm_device *intel_dp_to_dev(struct intel_dp *intel_dp) -{ + struct drm_device *dev = intel_dp_to_dev(intel_dp); struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); + enum port port = intel_dig_port->port; - return intel_dig_port->base.base.dev; + return is_edp(intel_dp) && + (port == PORT_A || (port == PORT_C && IS_VALLEYVIEW(dev))); } static struct intel_dp *intel_attached_dp(struct drm_connector *connector) -- cgit v1.2.3 From 15e6bf74b660c2e7aecc200ac77eb19eb6628240 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Wed, 8 May 2013 13:14:07 +0300 Subject: drm/i915: remove is_pch_edp() helpers and state variable There are no more users for these, so remove them. Signed-off-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 36 ------------------------------------ drivers/gpu/drm/i915/intel_drv.h | 2 -- 2 files changed, 38 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 3ab683af5256..fa7e66b4adc6 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -52,19 +52,6 @@ static bool is_edp(struct intel_dp *intel_dp) return intel_dig_port->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 drm_device *intel_dp_to_dev(struct intel_dp *intel_dp) { struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); @@ -93,25 +80,6 @@ static struct intel_dp *intel_attached_dp(struct drm_connector *connector) return enc_to_intel_dp(&intel_attached_encoder(connector)->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_down(struct intel_dp *intel_dp); static int @@ -2981,10 +2949,6 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, intel_dp->DP = I915_READ(intel_dp->output_reg); intel_dp->attached_connector = intel_connector; - if (HAS_PCH_SPLIT(dev) && port == PORT_D) - if (intel_dpd_is_edp(dev)) - intel_dp->is_pch_edp = true; - type = DRM_MODE_CONNECTOR_DisplayPort; /* * FIXME : We need to initialize built-in panels before external panels. diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index b1f50b15c6f3..118e1bfcbe85 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -451,7 +451,6 @@ struct intel_dp { uint8_t downstream_ports[DP_MAX_DOWNSTREAM_PORTS]; struct i2c_adapter adapter; struct i2c_algo_dp_aux_data algo; - bool is_pch_edp; uint8_t train_set[4]; int panel_power_up_delay; int panel_power_down_delay; @@ -565,7 +564,6 @@ extern void ironlake_edp_panel_on(struct intel_dp *intel_dp); extern void ironlake_edp_panel_off(struct intel_dp *intel_dp); extern void ironlake_edp_panel_vdd_on(struct intel_dp *intel_dp); extern void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync); -extern bool intel_encoder_is_pch_edp(struct drm_encoder *encoder); extern int intel_plane_init(struct drm_device *dev, enum pipe pipe, int plane); extern void intel_flush_display_plane(struct drm_i915_private *dev_priv, enum plane plane); -- cgit v1.2.3 From e7281eab0bb4a5265593866d6f7acea2812fe0ec Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Wed, 8 May 2013 13:14:08 +0300 Subject: drm/i915: print DP init debug messages from a single place Signed-off-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 12 +++--------- drivers/gpu/drm/i915/intel_dp.c | 4 ++++ 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 4ca2a3d885a0..0dab9f64b35d 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8821,10 +8821,8 @@ static void intel_setup_outputs(struct drm_device *dev) intel_hdmi_init(dev, GEN4_HDMIB, PORT_B); } - if (!found && SUPPORTS_INTEGRATED_DP(dev)) { - DRM_DEBUG_KMS("probing DP_B\n"); + if (!found && SUPPORTS_INTEGRATED_DP(dev)) intel_dp_init(dev, DP_B, PORT_B); - } } /* Before G4X SDVOC doesn't have its own detect register */ @@ -8840,17 +8838,13 @@ static void intel_setup_outputs(struct drm_device *dev) DRM_DEBUG_KMS("probing HDMI on SDVOC\n"); intel_hdmi_init(dev, GEN4_HDMIC, PORT_C); } - if (SUPPORTS_INTEGRATED_DP(dev)) { - DRM_DEBUG_KMS("probing DP_C\n"); + if (SUPPORTS_INTEGRATED_DP(dev)) intel_dp_init(dev, DP_C, PORT_C); - } } if (SUPPORTS_INTEGRATED_DP(dev) && - (I915_READ(DP_D) & DP_DETECTED)) { - DRM_DEBUG_KMS("probing DP_D\n"); + (I915_READ(DP_D) & DP_DETECTED)) intel_dp_init(dev, DP_D, PORT_D); - } } else if (IS_GEN2(dev)) intel_dvo_init(dev); diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index fa7e66b4adc6..0291b28ec872 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2978,6 +2978,10 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, if (type == DRM_MODE_CONNECTOR_eDP) intel_encoder->type = INTEL_OUTPUT_EDP; + DRM_DEBUG_KMS("Adding %s connector on port %c\n", + type == DRM_MODE_CONNECTOR_eDP ? "eDP" : "DP", + port_name(port)); + drm_connector_init(dev, connector, &intel_dp_connector_funcs, type); drm_connector_helper_add(connector, &intel_dp_connector_helper_funcs); -- cgit v1.2.3 From 70484559296623d49e559a3a10fa32fd2bc5dcc3 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 30 Apr 2013 14:01:41 +0200 Subject: drm/i915: move sdvo TV clock computation to intel_sdvo.c We have a very nice infrastructure for this now! Note that the multifunction sdvo support is pretty neatly broken: We completely ignore userspace's request for which connector to wire up with the encoder and just use whatever the last detect callback has seen. Not something I'll fix in this patch, but unfortunately something which is also broken in the DDI code ... v2: Don't call sdvo_tv_clock twice. Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 30 ------------------------------ drivers/gpu/drm/i915/intel_sdvo.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 0dab9f64b35d..9365c5aa16bb 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4290,30 +4290,6 @@ static int i9xx_get_refclk(struct drm_crtc *crtc, int num_connectors) return refclk; } -static void i9xx_adjust_sdvo_tv_clock(struct intel_crtc *crtc) -{ - unsigned dotclock = crtc->config.adjusted_mode.clock; - struct dpll *clock = &crtc->config.dpll; - - /* SDVO TV has fixed PLL values depend on its clock range, - this mirrors vbios setting. */ - if (dotclock >= 100000 && dotclock < 140500) { - clock->p1 = 2; - clock->p2 = 10; - clock->n = 3; - clock->m1 = 16; - clock->m2 = 8; - } else if (dotclock >= 140500 && dotclock <= 200000) { - clock->p1 = 1; - clock->p2 = 10; - clock->n = 6; - clock->m1 = 12; - clock->m2 = 8; - } - - crtc->config.clock_set = true; -} - static uint32_t pnv_dpll_compute_fp(struct dpll *dpll) { return (1 << dpll->n) << 16 | dpll->m1 << 8 | dpll->m2; @@ -4972,9 +4948,6 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, intel_crtc->config.dpll.p2 = clock.p2; } - if (is_sdvo && is_tv) - i9xx_adjust_sdvo_tv_clock(intel_crtc); - if (IS_GEN2(dev)) i8xx_update_pll(intel_crtc, adjusted_mode, has_reduced_clock ? &reduced_clock : NULL, @@ -5580,9 +5553,6 @@ static bool ironlake_compute_clocks(struct drm_crtc *crtc, reduced_clock); } - if (is_sdvo && is_tv) - i9xx_adjust_sdvo_tv_clock(to_intel_crtc(crtc)); - return true; } diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index a618a6a45a77..0fc6fc2d6a30 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1041,6 +1041,32 @@ intel_sdvo_get_preferred_input_mode(struct intel_sdvo *intel_sdvo, return true; } +static void i9xx_adjust_sdvo_tv_clock(struct intel_crtc_config *pipe_config) +{ + unsigned dotclock = pipe_config->adjusted_mode.clock; + struct dpll *clock = &pipe_config->dpll; + + /* SDVO TV has fixed PLL values depend on its clock range, + this mirrors vbios setting. */ + if (dotclock >= 100000 && dotclock < 140500) { + clock->p1 = 2; + clock->p2 = 10; + clock->n = 3; + clock->m1 = 16; + clock->m2 = 8; + } else if (dotclock >= 140500 && dotclock <= 200000) { + clock->p1 = 1; + clock->p2 = 10; + clock->n = 6; + clock->m1 = 12; + clock->m2 = 8; + } else { + WARN(1, "SDVO TV clock out of range: %i\n", dotclock); + } + + pipe_config->clock_set = true; +} + static bool intel_sdvo_compute_config(struct intel_encoder *encoder, struct intel_crtc_config *pipe_config) { @@ -1097,6 +1123,10 @@ static bool intel_sdvo_compute_config(struct intel_encoder *encoder, if (intel_sdvo->color_range) pipe_config->limited_color_range = true; + /* Clock computation needs to happen after pixel multiplier. */ + if (intel_sdvo->is_tv) + i9xx_adjust_sdvo_tv_clock(pipe_config); + return true; } -- cgit v1.2.3 From b4c09f3bbda97ec685afd604d8a3a08c72465910 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 30 Apr 2013 14:01:42 +0200 Subject: drm/i915: drop TVclock special casing on ilk+ TV-out uses the same reference clock as everyone else. The only difference seems to be in the slightly different CB tuning limit. Note that PLL_REF_INPUT_TVCLKINBC is a reserved value on ilk+. Also strictly speaking we don't support native TV-out on ilk+, hence all that code is dead. But Bspec still contains some residual mentions of native TV-out on some pch-split platforms, so I've figured it doesn't hurt to keep the code around a bit longer (e.g. in the cb tune function). v2: Improve the commit message as Jani suggested in his review. Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 9365c5aa16bb..5fcd5b09ff56 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5637,9 +5637,6 @@ static uint32_t ironlake_compute_dpll(struct intel_crtc *intel_crtc, if (intel_encoder->needs_tv_clock) is_tv = true; break; - case INTEL_OUTPUT_TVOUT: - is_tv = true; - break; } num_connectors++; @@ -5698,13 +5695,7 @@ static uint32_t ironlake_compute_dpll(struct intel_crtc *intel_crtc, break; } - if (is_sdvo && is_tv) - dpll |= PLL_REF_INPUT_TVCLKINBC; - else if (is_tv) - /* XXX: just matching BIOS for now */ - /* dpll |= PLL_REF_INPUT_TVCLKINBC; */ - dpll |= 3; - else if (is_lvds && intel_panel_use_ssc(dev_priv) && num_connectors < 2) + if (is_lvds && intel_panel_use_ssc(dev_priv) && num_connectors < 2) dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN; else dpll |= PLL_REF_INPUT_DREFCLK; -- cgit v1.2.3 From fec32900cc8f4ec8ad6b3007d2035e598f128f24 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 30 Apr 2013 14:01:43 +0200 Subject: drm/i915: rip out TV-out lore ... This seems to be an impressive piece of copy&pasta lore. I've checked all docs and on most platforms these bits are all MBZ, with the exception of the SDVO pixel multiplier on gen3. On gen4 that moved to a special DPLL_MD registers. No indication whatsoever that we actually need this for native TV-out support. I suspect this started as a hack when we didn't yet have proper pixel multiplier support in place for SDVO TV, but then got stuck in a life of its own. Just rip it out. Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 5fcd5b09ff56..97a29c4c27f7 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4589,10 +4589,6 @@ static void i9xx_update_pll(struct intel_crtc *crtc, if (is_sdvo && intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_TVOUT)) dpll |= PLL_REF_INPUT_TVCLKINBC; - else if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_TVOUT)) - /* XXX: just matching BIOS for now */ - /* dpll |= PLL_REF_INPUT_TVCLKINBC; */ - dpll |= 3; else if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_LVDS) && intel_panel_use_ssc(dev_priv) && num_connectors < 2) dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN; -- cgit v1.2.3 From a16af721e83466b965c7e53d5350e16abf9691e4 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 30 Apr 2013 14:01:44 +0200 Subject: drm/i915: rip out now unused is_foo tracking from crtc code More ugly stuff gone for good! The big special case left now is lvds (which is indeed really special). Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 28 +++------------------------- 1 file changed, 3 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 97a29c4c27f7..df81f1648400 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4879,8 +4879,8 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, int refclk, num_connectors = 0; intel_clock_t clock, reduced_clock; u32 dspcntr; - bool ok, has_reduced_clock = false, is_sdvo = false; - bool is_lvds = false, is_tv = false; + bool ok, has_reduced_clock = false; + bool is_lvds = false; struct intel_encoder *encoder; const intel_limit_t *limit; int ret; @@ -4890,15 +4890,6 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, case INTEL_OUTPUT_LVDS: is_lvds = true; break; - case INTEL_OUTPUT_SDVO: - case INTEL_OUTPUT_HDMI: - is_sdvo = true; - if (encoder->needs_tv_clock) - is_tv = true; - break; - case INTEL_OUTPUT_TVOUT: - is_tv = true; - break; } num_connectors++; @@ -5333,7 +5324,6 @@ static int ironlake_get_refclk(struct drm_crtc *crtc) struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_encoder *encoder; - struct intel_encoder *edp_encoder = NULL; int num_connectors = 0; bool is_lvds = false; @@ -5342,9 +5332,6 @@ static int ironlake_get_refclk(struct drm_crtc *crtc) case INTEL_OUTPUT_LVDS: is_lvds = true; break; - case INTEL_OUTPUT_EDP: - edp_encoder = encoder; - break; } num_connectors++; } @@ -5503,22 +5490,13 @@ static bool ironlake_compute_clocks(struct drm_crtc *crtc, struct intel_encoder *intel_encoder; int refclk; const intel_limit_t *limit; - bool ret, is_sdvo = false, is_tv = false, is_lvds = false; + bool ret, is_lvds = false; for_each_encoder_on_crtc(dev, crtc, intel_encoder) { switch (intel_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) - is_tv = true; - break; - case INTEL_OUTPUT_TVOUT: - is_tv = true; - break; } } -- cgit v1.2.3 From 09ede5414f0215461c933032630bf9c3a61a8ba3 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 30 Apr 2013 14:01:45 +0200 Subject: drm/i915: make SDVO TV-out work for multifunction devices We need to track this correctly. While at it shovel the boolean to track whether the sdvo is in tv mode or not into pipe_config. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=36997 Tested-by: Pierre Assal Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=63609 Tested-by: cancan,feng Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 8 +++----- drivers/gpu/drm/i915/intel_drv.h | 5 ++++- drivers/gpu/drm/i915/intel_sdvo.c | 8 ++------ 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index df81f1648400..e75e54680321 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4587,7 +4587,7 @@ static void i9xx_update_pll(struct intel_crtc *crtc, if (INTEL_INFO(dev)->gen >= 4) dpll |= (6 << PLL_LOAD_PULSE_PHASE_SHIFT); - if (is_sdvo && intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_TVOUT)) + if (crtc->config.sdvo_tv_clock) dpll |= PLL_REF_INPUT_TVCLKINBC; else if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_LVDS) && intel_panel_use_ssc(dev_priv) && num_connectors < 2) @@ -5598,7 +5598,7 @@ static uint32_t ironlake_compute_dpll(struct intel_crtc *intel_crtc, struct intel_encoder *intel_encoder; uint32_t dpll; int factor, num_connectors = 0; - bool is_lvds = false, is_sdvo = false, is_tv = false; + bool is_lvds = false, is_sdvo = false; for_each_encoder_on_crtc(dev, crtc, intel_encoder) { switch (intel_encoder->type) { @@ -5608,8 +5608,6 @@ static uint32_t ironlake_compute_dpll(struct intel_crtc *intel_crtc, case INTEL_OUTPUT_SDVO: case INTEL_OUTPUT_HDMI: is_sdvo = true; - if (intel_encoder->needs_tv_clock) - is_tv = true; break; } @@ -5623,7 +5621,7 @@ static uint32_t ironlake_compute_dpll(struct intel_crtc *intel_crtc, dev_priv->lvds_ssc_freq == 100) || (HAS_PCH_IBX(dev) && intel_is_dual_link_lvds(dev))) factor = 25; - } else if (is_sdvo && is_tv) + } else if (intel_crtc->config.sdvo_tv_clock) factor = 20; if (ironlake_needs_fb_cb_tune(&intel_crtc->config.dpll, factor)) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 118e1bfcbe85..0f3554592719 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -120,7 +120,6 @@ struct intel_encoder { struct intel_crtc *new_crtc; int type; - bool needs_tv_clock; /* * Intel hw has only one MUX where encoders could be clone, hence a * simple flag is enough to compute the possible_clones mask. @@ -223,6 +222,10 @@ struct intel_crtc_config { /* Controls for the clock computation, to override various stages. */ bool clock_set; + /* SDVO TV has a bunch of special case. To make multifunction encoders + * work correctly, we need to track this at runtime.*/ + bool sdvo_tv_clock; + /* * crtc bandwidth limit, don't increase pipe bpp or clock if not really * required. This is set in the 2nd loop of calling encoder's diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 0fc6fc2d6a30..015f9f4263fe 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1092,6 +1092,7 @@ static bool intel_sdvo_compute_config(struct intel_encoder *encoder, (void) intel_sdvo_get_preferred_input_mode(intel_sdvo, mode, adjusted_mode); + pipe_config->sdvo_tv_clock = true; } else if (intel_sdvo->is_lvds) { if (!intel_sdvo_set_output_timings_from_mode(intel_sdvo, intel_sdvo->sdvo_lvds_fixed_mode)) @@ -1655,12 +1656,9 @@ intel_sdvo_detect(struct drm_connector *connector, bool force) if (ret == connector_status_connected) { intel_sdvo->is_tv = false; intel_sdvo->is_lvds = false; - intel_sdvo->base.needs_tv_clock = false; - if (response & SDVO_TV_MASK) { + if (response & SDVO_TV_MASK) intel_sdvo->is_tv = true; - intel_sdvo->base.needs_tv_clock = true; - } if (response & SDVO_LVDS_MASK) intel_sdvo->is_lvds = intel_sdvo->sdvo_lvds_fixed_mode != NULL; } @@ -2349,7 +2347,6 @@ intel_sdvo_tv_init(struct intel_sdvo *intel_sdvo, int type) intel_sdvo_connector->output_flag = type; intel_sdvo->is_tv = true; - intel_sdvo->base.needs_tv_clock = true; intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo); @@ -2437,7 +2434,6 @@ static bool intel_sdvo_output_setup(struct intel_sdvo *intel_sdvo, uint16_t flags) { intel_sdvo->is_tv = false; - intel_sdvo->base.needs_tv_clock = false; intel_sdvo->is_lvds = false; /* SDVO requires XXX1 function may not exist unless it has XXX0 function.*/ -- cgit v1.2.3 From 41aa344866e3ba1d117a798355c35d44d7cc6318 Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Thu, 9 May 2013 20:03:18 -0300 Subject: drm/i915: Organize VBT stuff inside drm_i915_private drm_i915_private is getting bigger and bigger when adding new vbt stuff. So, the better way of getting drm_i915_private organized is to create a special structure for vbt stuff. v2: Basically conflicts fixes Reviewed-by: Jani Nikula Signed-off-by: Rodrigo Vivi Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 8 +-- drivers/gpu/drm/i915/i915_drv.h | 57 +++++++++++--------- drivers/gpu/drm/i915/intel_bios.c | 100 +++++++++++++++++------------------ drivers/gpu/drm/i915/intel_crt.c | 4 +- drivers/gpu/drm/i915/intel_display.c | 12 ++--- drivers/gpu/drm/i915/intel_dp.c | 18 +++---- drivers/gpu/drm/i915/intel_lvds.c | 16 +++--- drivers/gpu/drm/i915/intel_pm.c | 2 +- drivers/gpu/drm/i915/intel_sdvo.c | 6 +-- drivers/gpu/drm/i915/intel_tv.c | 8 +-- 10 files changed, 119 insertions(+), 112 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index a1648eb3a8b1..f5addac7c154 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1742,10 +1742,10 @@ int i915_driver_unload(struct drm_device *dev) * free the memory space allocated for the child device * config parsed from VBT */ - if (dev_priv->child_dev && dev_priv->child_dev_num) { - kfree(dev_priv->child_dev); - dev_priv->child_dev = NULL; - dev_priv->child_dev_num = 0; + if (dev_priv->vbt.child_dev && dev_priv->vbt.child_dev_num) { + kfree(dev_priv->vbt.child_dev); + dev_priv->vbt.child_dev = NULL; + dev_priv->vbt.child_dev_num = 0; } vga_switcheroo_unregister_client(dev->pdev); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index eeec0be671b7..14817de37405 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -891,6 +891,37 @@ enum modeset_restore { MODESET_SUSPENDED, }; +struct intel_vbt_data { + struct drm_display_mode *lfp_lvds_vbt_mode; /* if any */ + struct drm_display_mode *sdvo_lvds_vbt_mode; /* if any */ + + /* Feature bits */ + unsigned int int_tv_support:1; + unsigned int lvds_dither:1; + unsigned int lvds_vbt:1; + unsigned int int_crt_support:1; + unsigned int lvds_use_ssc:1; + unsigned int display_clock_mode:1; + unsigned int fdi_rx_polarity_inverted:1; + int lvds_ssc_freq; + unsigned int bios_lvds_val; /* initial [PCH_]LVDS reg val in VBIOS */ + + /* eDP */ + int edp_rate; + int edp_lanes; + int edp_preemphasis; + int edp_vswing; + bool edp_initialized; + bool edp_support; + int edp_bpp; + struct edp_power_seq edp_pps; + + int crt_ddc_pin; + + int child_dev_num; + struct child_device_config *child_dev; +}; + typedef struct drm_i915_private { struct drm_device *dev; struct kmem_cache *slab; @@ -970,6 +1001,7 @@ typedef struct drm_i915_private { struct intel_fbc_work *fbc_work; struct intel_opregion opregion; + struct intel_vbt_data vbt; /* overlay */ struct intel_overlay *overlay; @@ -986,31 +1018,8 @@ typedef struct drm_i915_private { /* LVDS info */ struct drm_display_mode *lfp_lvds_vbt_mode; /* if any */ struct drm_display_mode *sdvo_lvds_vbt_mode; /* if any */ - - /* Feature bits from the VBIOS */ - unsigned int int_tv_support:1; - unsigned int lvds_dither:1; - unsigned int lvds_vbt:1; - unsigned int int_crt_support:1; - unsigned int lvds_use_ssc:1; - unsigned int display_clock_mode:1; - unsigned int fdi_rx_polarity_inverted:1; - int lvds_ssc_freq; - unsigned int bios_lvds_val; /* initial [PCH_]LVDS reg val in VBIOS */ - 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; - int crt_ddc_pin; struct drm_i915_fence_reg fence_regs[I915_MAX_NUM_FENCES]; /* 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 */ @@ -1052,8 +1061,6 @@ typedef struct drm_i915_private { /* indicates the reduced downclock for LVDS*/ int lvds_downclock; u16 orig_clock; - int child_dev_num; - struct child_device_config *child_dev; bool mchbar_need_disable; diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c index 95070b2124c6..53f2bed8bc5f 100644 --- a/drivers/gpu/drm/i915/intel_bios.c +++ b/drivers/gpu/drm/i915/intel_bios.c @@ -212,7 +212,7 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv, if (!lvds_options) return; - dev_priv->lvds_dither = lvds_options->pixel_dither; + dev_priv->vbt.lvds_dither = lvds_options->pixel_dither; if (lvds_options->panel_type == 0xff) return; @@ -226,7 +226,7 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv, if (!lvds_lfp_data_ptrs) return; - dev_priv->lvds_vbt = 1; + dev_priv->vbt.lvds_vbt = 1; panel_dvo_timing = get_lvds_dvo_timing(lvds_lfp_data, lvds_lfp_data_ptrs, @@ -238,7 +238,7 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv, fill_detail_timing_data(panel_fixed_mode, panel_dvo_timing); - dev_priv->lfp_lvds_vbt_mode = panel_fixed_mode; + dev_priv->vbt.lfp_lvds_vbt_mode = panel_fixed_mode; DRM_DEBUG_KMS("Found panel mode in BIOS VBT tables:\n"); drm_mode_debug_printmodeline(panel_fixed_mode); @@ -274,9 +274,9 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv, /* check the resolution, just to be sure */ if (fp_timing->x_res == panel_fixed_mode->hdisplay && fp_timing->y_res == panel_fixed_mode->vdisplay) { - dev_priv->bios_lvds_val = fp_timing->lvds_reg_val; + dev_priv->vbt.bios_lvds_val = fp_timing->lvds_reg_val; DRM_DEBUG_KMS("VBT initial LVDS value %x\n", - dev_priv->bios_lvds_val); + dev_priv->vbt.bios_lvds_val); } } } @@ -316,7 +316,7 @@ parse_sdvo_panel_data(struct drm_i915_private *dev_priv, fill_detail_timing_data(panel_fixed_mode, dvo_timing + index); - dev_priv->sdvo_lvds_vbt_mode = panel_fixed_mode; + dev_priv->vbt.sdvo_lvds_vbt_mode = panel_fixed_mode; DRM_DEBUG_KMS("Found SDVO panel mode in BIOS VBT tables:\n"); drm_mode_debug_printmodeline(panel_fixed_mode); @@ -345,20 +345,20 @@ parse_general_features(struct drm_i915_private *dev_priv, general = find_section(bdb, BDB_GENERAL_FEATURES); if (general) { - dev_priv->int_tv_support = general->int_tv_support; - dev_priv->int_crt_support = general->int_crt_support; - dev_priv->lvds_use_ssc = general->enable_ssc; - dev_priv->lvds_ssc_freq = + dev_priv->vbt.int_tv_support = general->int_tv_support; + dev_priv->vbt.int_crt_support = general->int_crt_support; + dev_priv->vbt.lvds_use_ssc = general->enable_ssc; + dev_priv->vbt.lvds_ssc_freq = intel_bios_ssc_frequency(dev, general->ssc_freq); - dev_priv->display_clock_mode = general->display_clock_mode; - dev_priv->fdi_rx_polarity_inverted = general->fdi_rx_polarity_inverted; + dev_priv->vbt.display_clock_mode = general->display_clock_mode; + dev_priv->vbt.fdi_rx_polarity_inverted = general->fdi_rx_polarity_inverted; DRM_DEBUG_KMS("BDB_GENERAL_FEATURES int_tv_support %d int_crt_support %d lvds_use_ssc %d lvds_ssc_freq %d display_clock_mode %d fdi_rx_polarity_inverted %d\n", - dev_priv->int_tv_support, - dev_priv->int_crt_support, - dev_priv->lvds_use_ssc, - dev_priv->lvds_ssc_freq, - dev_priv->display_clock_mode, - dev_priv->fdi_rx_polarity_inverted); + dev_priv->vbt.int_tv_support, + dev_priv->vbt.int_crt_support, + dev_priv->vbt.lvds_use_ssc, + dev_priv->vbt.lvds_ssc_freq, + dev_priv->vbt.display_clock_mode, + dev_priv->vbt.fdi_rx_polarity_inverted); } } @@ -375,7 +375,7 @@ parse_general_definitions(struct drm_i915_private *dev_priv, int bus_pin = general->crt_ddc_gmbus_pin; DRM_DEBUG_KMS("crt_ddc_bus_pin: %d\n", bus_pin); if (intel_gmbus_is_port_valid(bus_pin)) - dev_priv->crt_ddc_pin = bus_pin; + dev_priv->vbt.crt_ddc_pin = bus_pin; } else { DRM_DEBUG_KMS("BDB_GD too small (%d). Invalid.\n", block_size); @@ -486,7 +486,7 @@ parse_driver_features(struct drm_i915_private *dev_priv, if (SUPPORTS_EDP(dev) && driver->lvds_config == BDB_DRIVER_FEATURE_EDP) - dev_priv->edp.support = 1; + dev_priv->vbt.edp_support = 1; if (driver->dual_frequency) dev_priv->render_reclock_avail = true; @@ -501,20 +501,20 @@ parse_edp(struct drm_i915_private *dev_priv, struct bdb_header *bdb) 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->vbt.edp_support) DRM_DEBUG_KMS("No eDP BDB found but eDP panel supported.\n"); return; } switch ((edp->color_depth >> (panel_type * 2)) & 3) { case EDP_18BPP: - dev_priv->edp.bpp = 18; + dev_priv->vbt.edp_bpp = 18; break; case EDP_24BPP: - dev_priv->edp.bpp = 24; + dev_priv->vbt.edp_bpp = 24; break; case EDP_30BPP: - dev_priv->edp.bpp = 30; + dev_priv->vbt.edp_bpp = 30; break; } @@ -522,48 +522,48 @@ parse_edp(struct drm_i915_private *dev_priv, struct bdb_header *bdb) edp_pps = &edp->power_seqs[panel_type]; edp_link_params = &edp->link_params[panel_type]; - dev_priv->edp.pps = *edp_pps; + dev_priv->vbt.edp_pps = *edp_pps; - dev_priv->edp.rate = edp_link_params->rate ? DP_LINK_BW_2_7 : + dev_priv->vbt.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; + dev_priv->vbt.edp_lanes = 1; break; case 1: - dev_priv->edp.lanes = 2; + dev_priv->vbt.edp_lanes = 2; break; case 3: default: - dev_priv->edp.lanes = 4; + dev_priv->vbt.edp_lanes = 4; break; } switch (edp_link_params->preemphasis) { case 0: - dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_0; + dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPHASIS_0; break; case 1: - dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_3_5; + dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPHASIS_3_5; break; case 2: - dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_6; + dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPHASIS_6; break; case 3: - dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_9_5; + dev_priv->vbt.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; + dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_400; break; case 1: - dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_600; + dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_600; break; case 2: - dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_800; + dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_800; break; case 3: - dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_1200; + dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_1200; break; } } @@ -611,13 +611,13 @@ parse_device_mapping(struct drm_i915_private *dev_priv, DRM_DEBUG_KMS("no child dev is parsed from VBT\n"); return; } - dev_priv->child_dev = kcalloc(count, sizeof(*p_child), GFP_KERNEL); - if (!dev_priv->child_dev) { + dev_priv->vbt.child_dev = kcalloc(count, sizeof(*p_child), GFP_KERNEL); + if (!dev_priv->vbt.child_dev) { DRM_DEBUG_KMS("No memory space for child device\n"); return; } - dev_priv->child_dev_num = count; + dev_priv->vbt.child_dev_num = count; count = 0; for (i = 0; i < child_device_num; i++) { p_child = &(p_defs->devices[i]); @@ -625,7 +625,7 @@ parse_device_mapping(struct drm_i915_private *dev_priv, /* skip the device block if device type is invalid */ continue; } - child_dev_ptr = dev_priv->child_dev + count; + child_dev_ptr = dev_priv->vbt.child_dev + count; count++; memcpy((void *)child_dev_ptr, (void *)p_child, sizeof(*p_child)); @@ -638,23 +638,23 @@ init_vbt_defaults(struct drm_i915_private *dev_priv) { struct drm_device *dev = dev_priv->dev; - dev_priv->crt_ddc_pin = GMBUS_PORT_VGADDC; + dev_priv->vbt.crt_ddc_pin = GMBUS_PORT_VGADDC; /* LFP panel data */ - dev_priv->lvds_dither = 1; - dev_priv->lvds_vbt = 0; + dev_priv->vbt.lvds_dither = 1; + dev_priv->vbt.lvds_vbt = 0; /* SDVO panel data */ - dev_priv->sdvo_lvds_vbt_mode = NULL; + dev_priv->vbt.sdvo_lvds_vbt_mode = NULL; /* general features */ - dev_priv->int_tv_support = 1; - dev_priv->int_crt_support = 1; + dev_priv->vbt.int_tv_support = 1; + dev_priv->vbt.int_crt_support = 1; /* Default to using SSC */ - dev_priv->lvds_use_ssc = 1; - dev_priv->lvds_ssc_freq = intel_bios_ssc_frequency(dev, 1); - DRM_DEBUG_KMS("Set default to SSC at %dMHz\n", dev_priv->lvds_ssc_freq); + dev_priv->vbt.lvds_use_ssc = 1; + dev_priv->vbt.lvds_ssc_freq = intel_bios_ssc_frequency(dev, 1); + DRM_DEBUG_KMS("Set default to SSC at %dMHz\n", dev_priv->vbt.lvds_ssc_freq); } static int __init intel_no_opregion_vbt_callback(const struct dmi_system_id *id) diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 991e53047e1d..cc414f1a0b92 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -442,7 +442,7 @@ static bool intel_crt_detect_ddc(struct drm_connector *connector) BUG_ON(crt->base.type != INTEL_OUTPUT_ANALOG); - i2c = intel_gmbus_get_adapter(dev_priv, dev_priv->crt_ddc_pin); + i2c = intel_gmbus_get_adapter(dev_priv, dev_priv->vbt.crt_ddc_pin); edid = intel_crt_get_edid(connector, i2c); if (edid) { @@ -648,7 +648,7 @@ static int intel_crt_get_modes(struct drm_connector *connector) int ret; struct i2c_adapter *i2c; - i2c = intel_gmbus_get_adapter(dev_priv, dev_priv->crt_ddc_pin); + i2c = intel_gmbus_get_adapter(dev_priv, dev_priv->vbt.crt_ddc_pin); ret = intel_crt_ddc_get_modes(connector, i2c); if (ret || !IS_G4X(dev)) return ret; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index e75e54680321..a14fec3a6740 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4242,7 +4242,7 @@ static inline bool intel_panel_use_ssc(struct drm_i915_private *dev_priv) { if (i915_panel_use_ssc >= 0) return i915_panel_use_ssc != 0; - return dev_priv->lvds_use_ssc + return dev_priv->vbt.lvds_use_ssc && !(dev_priv->quirks & QUIRK_LVDS_SSC_DISABLE); } @@ -4278,7 +4278,7 @@ static int i9xx_get_refclk(struct drm_crtc *crtc, int num_connectors) refclk = vlv_get_refclk(crtc); } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) && intel_panel_use_ssc(dev_priv) && num_connectors < 2) { - refclk = dev_priv->lvds_ssc_freq * 1000; + refclk = dev_priv->vbt.lvds_ssc_freq * 1000; DRM_DEBUG_KMS("using SSC reference clock of %d MHz\n", refclk / 1000); } else if (!IS_GEN2(dev)) { @@ -5026,7 +5026,7 @@ static void ironlake_init_pch_refclk(struct drm_device *dev) } if (HAS_PCH_IBX(dev)) { - has_ck505 = dev_priv->display_clock_mode; + has_ck505 = dev_priv->vbt.display_clock_mode; can_ssc = has_ck505; } else { has_ck505 = false; @@ -5338,8 +5338,8 @@ static int ironlake_get_refclk(struct drm_crtc *crtc) if (is_lvds && intel_panel_use_ssc(dev_priv) && num_connectors < 2) { DRM_DEBUG_KMS("using SSC reference clock of %d MHz\n", - dev_priv->lvds_ssc_freq); - return dev_priv->lvds_ssc_freq * 1000; + dev_priv->vbt.lvds_ssc_freq); + return dev_priv->vbt.lvds_ssc_freq * 1000; } return 120000; @@ -5618,7 +5618,7 @@ static uint32_t ironlake_compute_dpll(struct intel_crtc *intel_crtc, factor = 21; if (is_lvds) { if ((intel_panel_use_ssc(dev_priv) && - dev_priv->lvds_ssc_freq == 100) || + dev_priv->vbt.lvds_ssc_freq == 100) || (HAS_PCH_IBX(dev) && intel_is_dual_link_lvds(dev))) factor = 25; } else if (intel_crtc->config.sdvo_tv_clock) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 0291b28ec872..2bb4009b7a60 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -727,10 +727,10 @@ intel_dp_compute_config(struct intel_encoder *encoder, * recomments. This means we'll up-dither 16bpp framebuffers on * high-depth panels. */ - if (is_edp(intel_dp) && dev_priv->edp.bpp) { + if (is_edp(intel_dp) && dev_priv->vbt.edp_bpp) { DRM_DEBUG_KMS("forcing bpp for eDP panel to BIOS-provided %i\n", - dev_priv->edp.bpp); - bpp = dev_priv->edp.bpp; + dev_priv->vbt.edp_bpp); + bpp = dev_priv->vbt.edp_bpp; } for (; bpp >= 6*3; bpp -= 2*3) { @@ -2745,11 +2745,11 @@ bool intel_dpd_is_edp(struct drm_device *dev) struct child_device_config *p_child; int i; - if (!dev_priv->child_dev_num) + if (!dev_priv->vbt.child_dev_num) return false; - for (i = 0; i < dev_priv->child_dev_num; i++) { - p_child = dev_priv->child_dev + i; + for (i = 0; i < dev_priv->vbt.child_dev_num; i++) { + p_child = dev_priv->vbt.child_dev + i; if (p_child->dvo_port == PORT_IDPD && p_child->device_type == DEVICE_TYPE_eDP) @@ -2827,7 +2827,7 @@ intel_dp_init_panel_power_sequencer(struct drm_device *dev, DRM_DEBUG_KMS("cur t1_t3 %d t8 %d t9 %d t10 %d t11_t12 %d\n", cur.t1_t3, cur.t8, cur.t9, cur.t10, cur.t11_t12); - vbt = dev_priv->edp.pps; + vbt = dev_priv->vbt.edp_pps; /* Upper limits from eDP 1.3 spec. Note that we use the clunky units of * our hw here, which are all in 100usec. */ @@ -3097,8 +3097,8 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, } /* fallback to VBT if available for eDP */ - if (!fixed_mode && dev_priv->lfp_lvds_vbt_mode) { - fixed_mode = drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode); + if (!fixed_mode && dev_priv->vbt.lfp_lvds_vbt_mode) { + fixed_mode = drm_mode_duplicate(dev, dev_priv->vbt.lfp_lvds_vbt_mode); if (fixed_mode) fixed_mode->type |= DRM_MODE_TYPE_PREFERRED; } diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index d256fe454a9d..e34e290c29eb 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -735,11 +735,11 @@ static bool lvds_is_present_in_vbt(struct drm_device *dev, struct drm_i915_private *dev_priv = dev->dev_private; int i; - if (!dev_priv->child_dev_num) + if (!dev_priv->vbt.child_dev_num) return true; - for (i = 0; i < dev_priv->child_dev_num; i++) { - struct child_device_config *child = dev_priv->child_dev + i; + for (i = 0; i < dev_priv->vbt.child_dev_num; i++) { + struct child_device_config *child = dev_priv->vbt.child_dev + i; /* If the device type is not LFP, continue. * We have to check both the new identifiers as well as the @@ -827,7 +827,7 @@ static bool compute_is_dual_link_lvds(struct intel_lvds_encoder *lvds_encoder) */ val = I915_READ(lvds_encoder->reg); if (!(val & ~(LVDS_PIPE_MASK | LVDS_DETECTED))) - val = dev_priv->bios_lvds_val; + val = dev_priv->vbt.bios_lvds_val; return (val & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP; } @@ -887,7 +887,7 @@ bool intel_lvds_init(struct drm_device *dev) if (HAS_PCH_SPLIT(dev)) { if ((I915_READ(PCH_LVDS) & LVDS_DETECTED) == 0) return false; - if (dev_priv->edp.support) { + if (dev_priv->vbt.edp_support) { DRM_DEBUG_KMS("disable LVDS for eDP support\n"); return false; } @@ -1005,11 +1005,11 @@ bool intel_lvds_init(struct drm_device *dev) } /* Failed to get EDID, what about VBT? */ - if (dev_priv->lfp_lvds_vbt_mode) { + if (dev_priv->vbt.lfp_lvds_vbt_mode) { DRM_DEBUG_KMS("using mode from VBT: "); - drm_mode_debug_printmodeline(dev_priv->lfp_lvds_vbt_mode); + drm_mode_debug_printmodeline(dev_priv->vbt.lfp_lvds_vbt_mode); - fixed_mode = drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode); + fixed_mode = drm_mode_duplicate(dev, dev_priv->vbt.lfp_lvds_vbt_mode); if (fixed_mode) { fixed_mode->type |= DRM_MODE_TYPE_PREFERRED; goto out; diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 6df886e06b64..5a22ce2b8244 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3889,7 +3889,7 @@ static void cpt_init_clock_gating(struct drm_device *dev) val = I915_READ(TRANS_CHICKEN2(pipe)); val |= TRANS_CHICKEN2_TIMING_OVERRIDE; val &= ~TRANS_CHICKEN2_FDI_POLARITY_REVERSED; - if (dev_priv->fdi_rx_polarity_inverted) + if (dev_priv->vbt.fdi_rx_polarity_inverted) val |= TRANS_CHICKEN2_FDI_POLARITY_REVERSED; val &= ~TRANS_CHICKEN2_FRAME_START_DELAY_MASK; val &= ~TRANS_CHICKEN2_DISABLE_DEEP_COLOR_COUNTER; diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 015f9f4263fe..2f54dc3dc5df 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1526,7 +1526,7 @@ intel_sdvo_get_analog_edid(struct drm_connector *connector) return drm_get_edid(connector, intel_gmbus_get_adapter(dev_priv, - dev_priv->crt_ddc_pin)); + dev_priv->vbt.crt_ddc_pin)); } static enum drm_connector_status @@ -1809,9 +1809,9 @@ static void intel_sdvo_get_lvds_modes(struct drm_connector *connector) goto end; /* Fetch modes from VBT */ - if (dev_priv->sdvo_lvds_vbt_mode != NULL) { + if (dev_priv->vbt.sdvo_lvds_vbt_mode != NULL) { newmode = drm_mode_duplicate(connector->dev, - dev_priv->sdvo_lvds_vbt_mode); + dev_priv->vbt.sdvo_lvds_vbt_mode); if (newmode != NULL) { /* Guarantee the mode is preferred */ newmode->type = (DRM_MODE_TYPE_PREFERRED | diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index b945bc54207a..7d11a5adc985 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1521,12 +1521,12 @@ static int tv_is_present_in_vbt(struct drm_device *dev) struct child_device_config *p_child; int i, ret; - if (!dev_priv->child_dev_num) + if (!dev_priv->vbt.child_dev_num) return 1; ret = 0; - for (i = 0; i < dev_priv->child_dev_num; i++) { - p_child = dev_priv->child_dev + i; + for (i = 0; i < dev_priv->vbt.child_dev_num; i++) { + p_child = dev_priv->vbt.child_dev + i; /* * If the device type is not TV, continue. */ @@ -1564,7 +1564,7 @@ intel_tv_init(struct drm_device *dev) return; } /* Even if we have an encoder we may not have a connector */ - if (!dev_priv->int_tv_support) + if (!dev_priv->vbt.int_tv_support) return; /* -- cgit v1.2.3 From abe959c7e06f62f064432a2aa00c199f1f672c81 Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Mon, 6 May 2013 19:37:33 -0300 Subject: drm/i915: Add support for FBC on Ivybridge. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch introduce Frame Buffer Compression (FBC) support for IVB, without enabling it by default. It adds a new function gen7_enable_fbc to avoid getting ironlake_enable_fbc messed with many IS_IVYBRIDGE checks. v2: Fixes from Ville. * Fix Plane. FBC is tied to primary plane A in HSW * Fix DPFC initial write to avoid let trash on the register. v3: Checking for bad plane on intel_update_fbc() as Chris suggested. v4: Ville pointed out that according to BSpec FBC_CTL bits 0:3 must be 0. v5: Up to v4 this work was entirely focused on Haswell. However Ville noticed I could reuse the FBC work done for HSW and get FBC for free at Ivybridge. So it makes more sense enable FBC for IVB first. FBC for HSW comming on next patches. We are just not enabling it by default on IVB. v6: Fix confused commit name (by Matt Turner). v7: Remove gtt_offset shift since it is page aligned byte offset (by Ville). Cc: Matt Turner Cc: Chris Wilson Cc: Ville Syrjälä Reviewed-by: Ville Syrjälä Signed-off-by: Rodrigo Vivi Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.c | 1 + drivers/gpu/drm/i915/i915_reg.h | 6 ++++++ drivers/gpu/drm/i915/intel_pm.c | 33 +++++++++++++++++++++++++++++++-- 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 3cb27fa842ee..cd84f774aace 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -280,6 +280,7 @@ static const struct intel_device_info intel_ivybridge_m_info = { GEN7_FEATURES, .is_ivybridge = 1, .is_mobile = 1, + .has_fbc = 1, }; static const struct intel_device_info intel_ivybridge_q_info = { diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index a809a5633b28..a7fc13d28b5c 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -925,7 +925,9 @@ #define DPFC_CTL_EN (1<<31) #define DPFC_CTL_PLANEA (0<<30) #define DPFC_CTL_PLANEB (1<<30) +#define IVB_DPFC_CTL_PLANE_SHIFT (29) #define DPFC_CTL_FENCE_EN (1<<29) +#define IVB_DPFC_CTL_FENCE_EN (1<<28) #define DPFC_CTL_PERSISTENT_MODE (1<<25) #define DPFC_SR_EN (1<<10) #define DPFC_CTL_LIMIT_1X (0<<6) @@ -958,6 +960,7 @@ #define ILK_DPFC_CHICKEN 0x43224 #define ILK_FBC_RT_BASE 0x2128 #define ILK_FBC_RT_VALID (1<<0) +#define SNB_FBC_FRONT_BUFFER (1<<1) #define ILK_DISPLAY_CHICKEN1 0x42000 #define ILK_FBCQ_DIS (1<<22) @@ -973,6 +976,9 @@ #define SNB_CPU_FENCE_ENABLE (1<<29) #define DPFC_CPU_FENCE_OFFSET 0x100104 +/* Framebuffer compression for Ivybridge */ +#define IVB_FBC_RT_BASE 0x7020 + /* * GPIO regs diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 5a22ce2b8244..e398963797a6 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -253,6 +253,30 @@ static bool ironlake_fbc_enabled(struct drm_device *dev) return I915_READ(ILK_DPFC_CONTROL) & DPFC_CTL_EN; } +static void gen7_enable_fbc(struct drm_crtc *crtc, unsigned long interval) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_framebuffer *fb = crtc->fb; + struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb); + struct drm_i915_gem_object *obj = intel_fb->obj; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + + I915_WRITE(IVB_FBC_RT_BASE, obj->gtt_offset | ILK_FBC_RT_VALID); + + I915_WRITE(ILK_DPFC_CONTROL, DPFC_CTL_EN | DPFC_CTL_LIMIT_1X | + IVB_DPFC_CTL_FENCE_EN | + intel_crtc->plane << IVB_DPFC_CTL_PLANE_SHIFT); + + I915_WRITE(SNB_DPFC_CTL_SA, + SNB_CPU_FENCE_ENABLE | obj->fence_reg); + I915_WRITE(DPFC_CPU_FENCE_OFFSET, crtc->y); + + sandybridge_blit_fbc_update(dev); + + DRM_DEBUG_KMS("enabled fbc on plane %d\n", intel_crtc->plane); +} + bool intel_fbc_enabled(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -439,7 +463,7 @@ void intel_update_fbc(struct drm_device *dev) if (enable_fbc < 0) { DRM_DEBUG_KMS("fbc set to per-chip default\n"); enable_fbc = 1; - if (INTEL_INFO(dev)->gen <= 6) + if (INTEL_INFO(dev)->gen <= 7) enable_fbc = 0; } if (!enable_fbc) { @@ -4507,7 +4531,12 @@ void intel_init_pm(struct drm_device *dev) if (I915_HAS_FBC(dev)) { if (HAS_PCH_SPLIT(dev)) { dev_priv->display.fbc_enabled = ironlake_fbc_enabled; - dev_priv->display.enable_fbc = ironlake_enable_fbc; + if (IS_IVYBRIDGE(dev)) + dev_priv->display.enable_fbc = + gen7_enable_fbc; + else + dev_priv->display.enable_fbc = + ironlake_enable_fbc; dev_priv->display.disable_fbc = ironlake_disable_fbc; } else if (IS_GM45(dev)) { dev_priv->display.fbc_enabled = g4x_fbc_enabled; -- cgit v1.2.3 From 30ca7c6f97e266d122b03261f75f530d5c83608b Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Mon, 6 May 2013 19:37:34 -0300 Subject: drm/i915: IVB FBC WaFbcAsynchFlipDisableFbcQueue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Display register 42000h bit 22 must be set to 1b for the entire time that Frame Buffer Compression is enabled. Reviewed-by: Ville Syrjälä Signed-off-by: Rodrigo Vivi Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_pm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index e398963797a6..48283167c195 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -268,6 +268,8 @@ static void gen7_enable_fbc(struct drm_crtc *crtc, unsigned long interval) IVB_DPFC_CTL_FENCE_EN | intel_crtc->plane << IVB_DPFC_CTL_PLANE_SHIFT); + /* WaFbcAsynchFlipDisableFbcQueue */ + I915_WRITE(ILK_DISPLAY_CHICKEN1, ILK_FBCQ_DIS); I915_WRITE(SNB_DPFC_CTL_SA, SNB_CPU_FENCE_ENABLE | obj->fence_reg); I915_WRITE(DPFC_CPU_FENCE_OFFSET, crtc->y); -- cgit v1.2.3 From b74ea102b746a1e5157d6b0c83f486ad3c6235d1 Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Thu, 9 May 2013 14:08:38 -0300 Subject: drm/i915: IVB FBC WaFbcDisableDpfcClockGating MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Display register 42020h bit 9 must be set to 1b for the entire time that Frame Buffer Compression is enabled. v2: RMW to preserve other bits (by Ville) v3: Fix from Ville: sed &/| at RMW v4: Too far on sed. Cc: Ville Syrjälä Reviewed-by: Ville Syrjälä Signed-off-by: Rodrigo Vivi Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_pm.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 48283167c195..fdc2839448a0 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -242,6 +242,12 @@ static void ironlake_disable_fbc(struct drm_device *dev) dpfc_ctl &= ~DPFC_CTL_EN; I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl); + if (IS_IVYBRIDGE(dev)) + /* WaFbcDisableDpfcClockGating */ + I915_WRITE(ILK_DSPCLK_GATE_D, + I915_READ(ILK_DSPCLK_GATE_D) & + ~ILK_DPFCUNIT_CLOCK_GATE_DISABLE); + DRM_DEBUG_KMS("disabled FBC\n"); } } @@ -270,6 +276,11 @@ static void gen7_enable_fbc(struct drm_crtc *crtc, unsigned long interval) /* WaFbcAsynchFlipDisableFbcQueue */ I915_WRITE(ILK_DISPLAY_CHICKEN1, ILK_FBCQ_DIS); + /* WaFbcDisableDpfcClockGating */ + I915_WRITE(ILK_DSPCLK_GATE_D, + I915_READ(ILK_DSPCLK_GATE_D) | + ILK_DPFCUNIT_CLOCK_GATE_DISABLE); + I915_WRITE(SNB_DPFC_CTL_SA, SNB_CPU_FENCE_ENABLE | obj->fence_reg); I915_WRITE(DPFC_CPU_FENCE_OFFSET, crtc->y); -- cgit v1.2.3 From 891348b2bf08d8946e0621bec49802897b28c1c4 Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Mon, 6 May 2013 19:37:36 -0300 Subject: drm/i915: Enable FBC at Haswell. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch introduce Frame Buffer Compression (FBC) support for HSW. FBC is tied to primary plane A in HSW. v2: Ville pointed out docs say FBC must be disabled before disabling the plane on HSW. v3: Really enabling it by default at HSW. Reviewed-by: Ville Syrjälä Signed-off-by: Rodrigo Vivi Tested-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.c | 1 + drivers/gpu/drm/i915/intel_display.c | 5 +++-- drivers/gpu/drm/i915/intel_pm.c | 21 ++++++++++++--------- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index cd84f774aace..a1a936fd34e0 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -319,6 +319,7 @@ static const struct intel_device_info intel_haswell_m_info = { .is_mobile = 1, .has_ddi = 1, .has_fpga_dbg = 1, + .has_fbc = 1, }; static const struct pci_device_id pciidlist[] = { /* aka */ diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index a14fec3a6740..50814ce84a9f 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3518,11 +3518,12 @@ static void haswell_crtc_disable(struct drm_crtc *crtc) drm_vblank_off(dev, pipe); intel_crtc_update_cursor(crtc, false); - intel_disable_plane(dev_priv, plane, pipe); - + /* FBC must be disabled before disabling the plane on HSW. */ if (dev_priv->cfb_plane == plane) intel_disable_fbc(dev); + intel_disable_plane(dev_priv, plane, pipe); + if (intel_crtc->config.has_pch_encoder) intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A, false); intel_disable_pipe(dev_priv, pipe); diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index fdc2839448a0..10f788b62fa8 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -274,12 +274,14 @@ static void gen7_enable_fbc(struct drm_crtc *crtc, unsigned long interval) IVB_DPFC_CTL_FENCE_EN | intel_crtc->plane << IVB_DPFC_CTL_PLANE_SHIFT); - /* WaFbcAsynchFlipDisableFbcQueue */ - I915_WRITE(ILK_DISPLAY_CHICKEN1, ILK_FBCQ_DIS); - /* WaFbcDisableDpfcClockGating */ - I915_WRITE(ILK_DSPCLK_GATE_D, - I915_READ(ILK_DSPCLK_GATE_D) | - ILK_DPFCUNIT_CLOCK_GATE_DISABLE); + if (IS_IVYBRIDGE(dev)) { + /* WaFbcAsynchFlipDisableFbcQueue */ + I915_WRITE(ILK_DISPLAY_CHICKEN1, ILK_FBCQ_DIS); + /* WaFbcDisableDpfcClockGating */ + I915_WRITE(ILK_DSPCLK_GATE_D, + I915_READ(ILK_DSPCLK_GATE_D) | + ILK_DPFCUNIT_CLOCK_GATE_DISABLE); + } I915_WRITE(SNB_DPFC_CTL_SA, SNB_CPU_FENCE_ENABLE | obj->fence_reg); @@ -476,7 +478,7 @@ void intel_update_fbc(struct drm_device *dev) if (enable_fbc < 0) { DRM_DEBUG_KMS("fbc set to per-chip default\n"); enable_fbc = 1; - if (INTEL_INFO(dev)->gen <= 7) + if (INTEL_INFO(dev)->gen <= 7 && !IS_HASWELL(dev)) enable_fbc = 0; } if (!enable_fbc) { @@ -497,7 +499,8 @@ void intel_update_fbc(struct drm_device *dev) dev_priv->no_fbc_reason = FBC_MODE_TOO_LARGE; goto out_disable; } - if ((IS_I915GM(dev) || IS_I945GM(dev)) && intel_crtc->plane != 0) { + if ((IS_I915GM(dev) || IS_I945GM(dev) || IS_HASWELL(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; @@ -4544,7 +4547,7 @@ void intel_init_pm(struct drm_device *dev) if (I915_HAS_FBC(dev)) { if (HAS_PCH_SPLIT(dev)) { dev_priv->display.fbc_enabled = ironlake_fbc_enabled; - if (IS_IVYBRIDGE(dev)) + if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev)) dev_priv->display.enable_fbc = gen7_enable_fbc; else -- cgit v1.2.3 From 285541647a816e00348916ba7387eeacea30eba9 Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Mon, 6 May 2013 19:37:37 -0300 Subject: drm/i915: HSW FBC WaFbcAsynchFlipDisableFbcQueue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Display register 420B0h bit 22 must be set to 1b for the entire time that Frame Buffer Compression is enabled. Reviewed-by: Ville Syrjälä Signed-off-by: Rodrigo Vivi Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 7 +++++++ drivers/gpu/drm/i915/intel_pm.c | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index a7fc13d28b5c..d48558678e87 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -980,6 +980,13 @@ #define IVB_FBC_RT_BASE 0x7020 +#define _HSW_PIPE_SLICE_CHICKEN_1_A 0x420B0 +#define _HSW_PIPE_SLICE_CHICKEN_1_B 0x420B4 +#define HSW_BYPASS_FBC_QUEUE (1<<22) +#define HSW_PIPE_SLICE_CHICKEN_1(pipe) _PIPE(pipe, + \ + _HSW_PIPE_SLICE_CHICKEN_1_A, + \ + _HSW_PIPE_SLICE_CHICKEN_1_B) + /* * GPIO regs */ diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 10f788b62fa8..4e678bad46d3 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -281,6 +281,10 @@ static void gen7_enable_fbc(struct drm_crtc *crtc, unsigned long interval) I915_WRITE(ILK_DSPCLK_GATE_D, I915_READ(ILK_DSPCLK_GATE_D) | ILK_DPFCUNIT_CLOCK_GATE_DISABLE); + } else { + /* WaFbcAsynchFlipDisableFbcQueue */ + I915_WRITE(HSW_PIPE_SLICE_CHICKEN_1(intel_crtc->pipe), + HSW_BYPASS_FBC_QUEUE); } I915_WRITE(SNB_DPFC_CTL_SA, -- cgit v1.2.3 From d89f2071461d5682b897c73278daaf25fd11aff5 Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Thu, 9 May 2013 14:20:50 -0300 Subject: drm/i915: HSW FBC WaFbcDisableDpfcClockGating MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Display register 46500h bit 23 must be set to 1b for the entire time that Frame Buffer Compression is enabled. v2: Ville suggested to enable it back when disabling fbc to avoid wasting power. v3: RMW to preserve other bits (by Ville) v4: Fix from Ville: sed &/| at RMW v5: Too far on sed. Cc: Ville Syrjälä Reviewed-by: Ville Syrjälä Signed-off-by: Rodrigo Vivi [danvet: Insert missing space that checkpatch spotted.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 3 +++ drivers/gpu/drm/i915/intel_pm.c | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index d48558678e87..7af7ae66b338 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -987,6 +987,9 @@ _HSW_PIPE_SLICE_CHICKEN_1_A, + \ _HSW_PIPE_SLICE_CHICKEN_1_B) +#define HSW_CLKGATE_DISABLE_PART_1 0x46500 +#define HSW_DPFC_GATING_DISABLE (1<<23) + /* * GPIO regs */ diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 4e678bad46d3..d806448f84fa 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -248,6 +248,12 @@ static void ironlake_disable_fbc(struct drm_device *dev) I915_READ(ILK_DSPCLK_GATE_D) & ~ILK_DPFCUNIT_CLOCK_GATE_DISABLE); + if (IS_HASWELL(dev)) + /* WaFbcDisableDpfcClockGating */ + I915_WRITE(HSW_CLKGATE_DISABLE_PART_1, + I915_READ(HSW_CLKGATE_DISABLE_PART_1) & + ~HSW_DPFC_GATING_DISABLE); + DRM_DEBUG_KMS("disabled FBC\n"); } } @@ -285,6 +291,10 @@ static void gen7_enable_fbc(struct drm_crtc *crtc, unsigned long interval) /* WaFbcAsynchFlipDisableFbcQueue */ I915_WRITE(HSW_PIPE_SLICE_CHICKEN_1(intel_crtc->pipe), HSW_BYPASS_FBC_QUEUE); + /* WaFbcDisableDpfcClockGating */ + I915_WRITE(HSW_CLKGATE_DISABLE_PART_1, + I915_READ(HSW_CLKGATE_DISABLE_PART_1) | + HSW_DPFC_GATING_DISABLE); } I915_WRITE(SNB_DPFC_CTL_SA, -- cgit v1.2.3 From 1c0b85c566b7a5a5cdabb767a39b445ce271a4fa Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Fri, 10 May 2013 14:01:51 +0100 Subject: drm/i915: Compute WR PLL dividers dynamically Up to now, we were using a static table to match the clock frequency with a (r2,n2,p) triplet. Despite this table being big, it's by no mean comprehensive and we had to fall back to the closest frequency when the requested TMDS clock wasn't in the table. This patch computes (r2,n2,p) dynamically and get rid of The Big Table. v2: Replace the floating point constant 1e6 by 1000000 Bugzilla: http://bugs.freedesktop.org/show_bug.cgi?id=58497 Signed-off-by: Damien Lespiau Reviewed-by: Paulo Zanoni (v1) Tested-by: Paulo Zanoni (v1) [danvet: s/ /^T/] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ddi.c | 617 ++++++++++++++------------------------- 1 file changed, 214 insertions(+), 403 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 146893288e53..cddcf4af7a5a 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -281,392 +281,6 @@ void hsw_fdi_link_train(struct drm_crtc *crtc) DRM_ERROR("FDI link training failed!\n"); } -/* WRPLL clock dividers */ -struct wrpll_tmds_clock { - u32 clock; - u16 p; /* Post divider */ - u16 n2; /* Feedback divider */ - u16 r2; /* Reference divider */ -}; - -/* Table of matching values for WRPLL clocks programming for each frequency. - * The code assumes this table is sorted. */ -static const struct wrpll_tmds_clock wrpll_tmds_clock_table[] = { - {19750, 38, 25, 18}, - {20000, 48, 32, 18}, - {21000, 36, 21, 15}, - {21912, 42, 29, 17}, - {22000, 36, 22, 15}, - {23000, 36, 23, 15}, - {23500, 40, 40, 23}, - {23750, 26, 16, 14}, - {24000, 36, 24, 15}, - {25000, 36, 25, 15}, - {25175, 26, 40, 33}, - {25200, 30, 21, 15}, - {26000, 36, 26, 15}, - {27000, 30, 21, 14}, - {27027, 18, 100, 111}, - {27500, 30, 29, 19}, - {28000, 34, 30, 17}, - {28320, 26, 30, 22}, - {28322, 32, 42, 25}, - {28750, 24, 23, 18}, - {29000, 30, 29, 18}, - {29750, 32, 30, 17}, - {30000, 30, 25, 15}, - {30750, 30, 41, 24}, - {31000, 30, 31, 18}, - {31500, 30, 28, 16}, - {32000, 30, 32, 18}, - {32500, 28, 32, 19}, - {33000, 24, 22, 15}, - {34000, 28, 30, 17}, - {35000, 26, 32, 19}, - {35500, 24, 30, 19}, - {36000, 26, 26, 15}, - {36750, 26, 46, 26}, - {37000, 24, 23, 14}, - {37762, 22, 40, 26}, - {37800, 20, 21, 15}, - {38000, 24, 27, 16}, - {38250, 24, 34, 20}, - {39000, 24, 26, 15}, - {40000, 24, 32, 18}, - {40500, 20, 21, 14}, - {40541, 22, 147, 89}, - {40750, 18, 19, 14}, - {41000, 16, 17, 14}, - {41500, 22, 44, 26}, - {41540, 22, 44, 26}, - {42000, 18, 21, 15}, - {42500, 22, 45, 26}, - {43000, 20, 43, 27}, - {43163, 20, 24, 15}, - {44000, 18, 22, 15}, - {44900, 20, 108, 65}, - {45000, 20, 25, 15}, - {45250, 20, 52, 31}, - {46000, 18, 23, 15}, - {46750, 20, 45, 26}, - {47000, 20, 40, 23}, - {48000, 18, 24, 15}, - {49000, 18, 49, 30}, - {49500, 16, 22, 15}, - {50000, 18, 25, 15}, - {50500, 18, 32, 19}, - {51000, 18, 34, 20}, - {52000, 18, 26, 15}, - {52406, 14, 34, 25}, - {53000, 16, 22, 14}, - {54000, 16, 24, 15}, - {54054, 16, 173, 108}, - {54500, 14, 24, 17}, - {55000, 12, 22, 18}, - {56000, 14, 45, 31}, - {56250, 16, 25, 15}, - {56750, 14, 25, 17}, - {57000, 16, 27, 16}, - {58000, 16, 43, 25}, - {58250, 16, 38, 22}, - {58750, 16, 40, 23}, - {59000, 14, 26, 17}, - {59341, 14, 40, 26}, - {59400, 16, 44, 25}, - {60000, 16, 32, 18}, - {60500, 12, 39, 29}, - {61000, 14, 49, 31}, - {62000, 14, 37, 23}, - {62250, 14, 42, 26}, - {63000, 12, 21, 15}, - {63500, 14, 28, 17}, - {64000, 12, 27, 19}, - {65000, 14, 32, 19}, - {65250, 12, 29, 20}, - {65500, 12, 32, 22}, - {66000, 12, 22, 15}, - {66667, 14, 38, 22}, - {66750, 10, 21, 17}, - {67000, 14, 33, 19}, - {67750, 14, 58, 33}, - {68000, 14, 30, 17}, - {68179, 14, 46, 26}, - {68250, 14, 46, 26}, - {69000, 12, 23, 15}, - {70000, 12, 28, 18}, - {71000, 12, 30, 19}, - {72000, 12, 24, 15}, - {73000, 10, 23, 17}, - {74000, 12, 23, 14}, - {74176, 8, 100, 91}, - {74250, 10, 22, 16}, - {74481, 12, 43, 26}, - {74500, 10, 29, 21}, - {75000, 12, 25, 15}, - {75250, 10, 39, 28}, - {76000, 12, 27, 16}, - {77000, 12, 53, 31}, - {78000, 12, 26, 15}, - {78750, 12, 28, 16}, - {79000, 10, 38, 26}, - {79500, 10, 28, 19}, - {80000, 12, 32, 18}, - {81000, 10, 21, 14}, - {81081, 6, 100, 111}, - {81624, 8, 29, 24}, - {82000, 8, 17, 14}, - {83000, 10, 40, 26}, - {83950, 10, 28, 18}, - {84000, 10, 28, 18}, - {84750, 6, 16, 17}, - {85000, 6, 17, 18}, - {85250, 10, 30, 19}, - {85750, 10, 27, 17}, - {86000, 10, 43, 27}, - {87000, 10, 29, 18}, - {88000, 10, 44, 27}, - {88500, 10, 41, 25}, - {89000, 10, 28, 17}, - {89012, 6, 90, 91}, - {89100, 10, 33, 20}, - {90000, 10, 25, 15}, - {91000, 10, 32, 19}, - {92000, 10, 46, 27}, - {93000, 10, 31, 18}, - {94000, 10, 40, 23}, - {94500, 10, 28, 16}, - {95000, 10, 44, 25}, - {95654, 10, 39, 22}, - {95750, 10, 39, 22}, - {96000, 10, 32, 18}, - {97000, 8, 23, 16}, - {97750, 8, 42, 29}, - {98000, 8, 45, 31}, - {99000, 8, 22, 15}, - {99750, 8, 34, 23}, - {100000, 6, 20, 18}, - {100500, 6, 19, 17}, - {101000, 6, 37, 33}, - {101250, 8, 21, 14}, - {102000, 6, 17, 15}, - {102250, 6, 25, 22}, - {103000, 8, 29, 19}, - {104000, 8, 37, 24}, - {105000, 8, 28, 18}, - {106000, 8, 22, 14}, - {107000, 8, 46, 29}, - {107214, 8, 27, 17}, - {108000, 8, 24, 15}, - {108108, 8, 173, 108}, - {109000, 6, 23, 19}, - {110000, 6, 22, 18}, - {110013, 6, 22, 18}, - {110250, 8, 49, 30}, - {110500, 8, 36, 22}, - {111000, 8, 23, 14}, - {111264, 8, 150, 91}, - {111375, 8, 33, 20}, - {112000, 8, 63, 38}, - {112500, 8, 25, 15}, - {113100, 8, 57, 34}, - {113309, 8, 42, 25}, - {114000, 8, 27, 16}, - {115000, 6, 23, 18}, - {116000, 8, 43, 25}, - {117000, 8, 26, 15}, - {117500, 8, 40, 23}, - {118000, 6, 38, 29}, - {119000, 8, 30, 17}, - {119500, 8, 46, 26}, - {119651, 8, 39, 22}, - {120000, 8, 32, 18}, - {121000, 6, 39, 29}, - {121250, 6, 31, 23}, - {121750, 6, 23, 17}, - {122000, 6, 42, 31}, - {122614, 6, 30, 22}, - {123000, 6, 41, 30}, - {123379, 6, 37, 27}, - {124000, 6, 51, 37}, - {125000, 6, 25, 18}, - {125250, 4, 13, 14}, - {125750, 4, 27, 29}, - {126000, 6, 21, 15}, - {127000, 6, 24, 17}, - {127250, 6, 41, 29}, - {128000, 6, 27, 19}, - {129000, 6, 43, 30}, - {129859, 4, 25, 26}, - {130000, 6, 26, 18}, - {130250, 6, 42, 29}, - {131000, 6, 32, 22}, - {131500, 6, 38, 26}, - {131850, 6, 41, 28}, - {132000, 6, 22, 15}, - {132750, 6, 28, 19}, - {133000, 6, 34, 23}, - {133330, 6, 37, 25}, - {134000, 6, 61, 41}, - {135000, 6, 21, 14}, - {135250, 6, 167, 111}, - {136000, 6, 62, 41}, - {137000, 6, 35, 23}, - {138000, 6, 23, 15}, - {138500, 6, 40, 26}, - {138750, 6, 37, 24}, - {139000, 6, 34, 22}, - {139050, 6, 34, 22}, - {139054, 6, 34, 22}, - {140000, 6, 28, 18}, - {141000, 6, 36, 23}, - {141500, 6, 22, 14}, - {142000, 6, 30, 19}, - {143000, 6, 27, 17}, - {143472, 4, 17, 16}, - {144000, 6, 24, 15}, - {145000, 6, 29, 18}, - {146000, 6, 47, 29}, - {146250, 6, 26, 16}, - {147000, 6, 49, 30}, - {147891, 6, 23, 14}, - {148000, 6, 23, 14}, - {148250, 6, 28, 17}, - {148352, 4, 100, 91}, - {148500, 6, 33, 20}, - {149000, 6, 48, 29}, - {150000, 6, 25, 15}, - {151000, 4, 19, 17}, - {152000, 6, 27, 16}, - {152280, 6, 44, 26}, - {153000, 6, 34, 20}, - {154000, 6, 53, 31}, - {155000, 6, 31, 18}, - {155250, 6, 50, 29}, - {155750, 6, 45, 26}, - {156000, 6, 26, 15}, - {157000, 6, 61, 35}, - {157500, 6, 28, 16}, - {158000, 6, 65, 37}, - {158250, 6, 44, 25}, - {159000, 6, 53, 30}, - {159500, 6, 39, 22}, - {160000, 6, 32, 18}, - {161000, 4, 31, 26}, - {162000, 4, 18, 15}, - {162162, 4, 131, 109}, - {162500, 4, 53, 44}, - {163000, 4, 29, 24}, - {164000, 4, 17, 14}, - {165000, 4, 22, 18}, - {166000, 4, 32, 26}, - {167000, 4, 26, 21}, - {168000, 4, 46, 37}, - {169000, 4, 104, 83}, - {169128, 4, 64, 51}, - {169500, 4, 39, 31}, - {170000, 4, 34, 27}, - {171000, 4, 19, 15}, - {172000, 4, 51, 40}, - {172750, 4, 32, 25}, - {172800, 4, 32, 25}, - {173000, 4, 41, 32}, - {174000, 4, 49, 38}, - {174787, 4, 22, 17}, - {175000, 4, 35, 27}, - {176000, 4, 30, 23}, - {177000, 4, 38, 29}, - {178000, 4, 29, 22}, - {178500, 4, 37, 28}, - {179000, 4, 53, 40}, - {179500, 4, 73, 55}, - {180000, 4, 20, 15}, - {181000, 4, 55, 41}, - {182000, 4, 31, 23}, - {183000, 4, 42, 31}, - {184000, 4, 30, 22}, - {184750, 4, 26, 19}, - {185000, 4, 37, 27}, - {186000, 4, 51, 37}, - {187000, 4, 36, 26}, - {188000, 4, 32, 23}, - {189000, 4, 21, 15}, - {190000, 4, 38, 27}, - {190960, 4, 41, 29}, - {191000, 4, 41, 29}, - {192000, 4, 27, 19}, - {192250, 4, 37, 26}, - {193000, 4, 20, 14}, - {193250, 4, 53, 37}, - {194000, 4, 23, 16}, - {194208, 4, 23, 16}, - {195000, 4, 26, 18}, - {196000, 4, 45, 31}, - {197000, 4, 35, 24}, - {197750, 4, 41, 28}, - {198000, 4, 22, 15}, - {198500, 4, 25, 17}, - {199000, 4, 28, 19}, - {200000, 4, 37, 25}, - {201000, 4, 61, 41}, - {202000, 4, 112, 75}, - {202500, 4, 21, 14}, - {203000, 4, 146, 97}, - {204000, 4, 62, 41}, - {204750, 4, 44, 29}, - {205000, 4, 38, 25}, - {206000, 4, 29, 19}, - {207000, 4, 23, 15}, - {207500, 4, 40, 26}, - {208000, 4, 37, 24}, - {208900, 4, 48, 31}, - {209000, 4, 48, 31}, - {209250, 4, 31, 20}, - {210000, 4, 28, 18}, - {211000, 4, 25, 16}, - {212000, 4, 22, 14}, - {213000, 4, 30, 19}, - {213750, 4, 38, 24}, - {214000, 4, 46, 29}, - {214750, 4, 35, 22}, - {215000, 4, 43, 27}, - {216000, 4, 24, 15}, - {217000, 4, 37, 23}, - {218000, 4, 42, 26}, - {218250, 4, 42, 26}, - {218750, 4, 34, 21}, - {219000, 4, 47, 29}, - {220000, 4, 44, 27}, - {220640, 4, 49, 30}, - {220750, 4, 36, 22}, - {221000, 4, 36, 22}, - {222000, 4, 23, 14}, - {222525, 4, 28, 17}, - {222750, 4, 33, 20}, - {227000, 4, 37, 22}, - {230250, 4, 29, 17}, - {233500, 4, 38, 22}, - {235000, 4, 40, 23}, - {238000, 4, 30, 17}, - {241500, 2, 17, 19}, - {245250, 2, 20, 22}, - {247750, 2, 22, 24}, - {253250, 2, 15, 16}, - {256250, 2, 18, 19}, - {262500, 2, 31, 32}, - {267250, 2, 66, 67}, - {268500, 2, 94, 95}, - {270000, 2, 14, 14}, - {272500, 2, 77, 76}, - {273750, 2, 57, 56}, - {280750, 2, 24, 23}, - {281250, 2, 23, 22}, - {286000, 2, 17, 16}, - {291750, 2, 26, 24}, - {296703, 2, 56, 51}, - {297000, 2, 22, 20}, - {298000, 2, 21, 19}, -}; - static void intel_ddi_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) @@ -790,27 +404,224 @@ void intel_ddi_put_crtc_pll(struct drm_crtc *crtc) intel_crtc->ddi_pll_sel = PORT_CLK_SEL_NONE; } -static void intel_ddi_calculate_wrpll(int clock, int *p, int *n2, int *r2) +#define LC_FREQ 2700 +#define LC_FREQ_2K (LC_FREQ * 2000) + +#define P_MIN 2 +#define P_MAX 64 +#define P_INC 2 + +/* Constraints for PLL good behavior */ +#define REF_MIN 48 +#define REF_MAX 400 +#define VCO_MIN 2400 +#define VCO_MAX 4800 + +#define ABS_DIFF(a, b) ((a > b) ? (a - b) : (b - a)) + +struct wrpll_rnp { + unsigned p, n2, r2; +}; + +static unsigned wrpll_get_budget_for_freq(int clock) +{ + unsigned budget; + + switch (clock) { + case 25175000: + case 25200000: + case 27000000: + case 27027000: + case 37762500: + case 37800000: + case 40500000: + case 40541000: + case 54000000: + case 54054000: + case 59341000: + case 59400000: + case 72000000: + case 74176000: + case 74250000: + case 81000000: + case 81081000: + case 89012000: + case 89100000: + case 108000000: + case 108108000: + case 111264000: + case 111375000: + case 148352000: + case 148500000: + case 162000000: + case 162162000: + case 222525000: + case 222750000: + case 296703000: + case 297000000: + budget = 0; + break; + case 233500000: + case 245250000: + case 247750000: + case 253250000: + case 298000000: + budget = 1500; + break; + case 169128000: + case 169500000: + case 179500000: + case 202000000: + budget = 2000; + break; + case 256250000: + case 262500000: + case 270000000: + case 272500000: + case 273750000: + case 280750000: + case 281250000: + case 286000000: + case 291750000: + budget = 4000; + break; + case 267250000: + case 268500000: + budget = 5000; + break; + default: + budget = 1000; + break; + } + + return budget; +} + +static void wrpll_update_rnp(uint64_t freq2k, unsigned budget, + unsigned r2, unsigned n2, unsigned p, + struct wrpll_rnp *best) { - u32 i; + uint64_t a, b, c, d, diff, diff_best; - for (i = 0; i < ARRAY_SIZE(wrpll_tmds_clock_table); i++) - if (clock <= wrpll_tmds_clock_table[i].clock) - break; + /* No best (r,n,p) yet */ + if (best->p == 0) { + best->p = p; + best->n2 = n2; + best->r2 = r2; + return; + } + + /* + * Output clock is (LC_FREQ_2K / 2000) * N / (P * R), which compares to + * freq2k. + * + * delta = 1e6 * + * abs(freq2k - (LC_FREQ_2K * n2/(p * r2))) / + * freq2k; + * + * and we would like delta <= budget. + * + * If the discrepancy is above the PPM-based budget, always prefer to + * improve upon the previous solution. However, if you're within the + * budget, try to maximize Ref * VCO, that is N / (P * R^2). + */ + a = freq2k * budget * p * r2; + b = freq2k * budget * best->p * best->r2; + diff = ABS_DIFF((freq2k * p * r2), (LC_FREQ_2K * n2)); + diff_best = ABS_DIFF((freq2k * best->p * best->r2), + (LC_FREQ_2K * best->n2)); + c = 1000000 * diff; + d = 1000000 * diff_best; + + if (a < c && b < d) { + /* If both are above the budget, pick the closer */ + if (best->p * best->r2 * diff < p * r2 * diff_best) { + best->p = p; + best->n2 = n2; + best->r2 = r2; + } + } else if (a >= c && b < d) { + /* If A is below the threshold but B is above it? Update. */ + best->p = p; + best->n2 = n2; + best->r2 = r2; + } else if (a >= c && b >= d) { + /* Both are below the limit, so pick the higher n2/(r2*r2) */ + if (n2 * best->r2 * best->r2 > best->n2 * r2 * r2) { + best->p = p; + best->n2 = n2; + best->r2 = r2; + } + } + /* Otherwise a < c && b >= d, do nothing */ +} + +static void +intel_ddi_calculate_wrpll(int clock /* in Hz */, + unsigned *r2_out, unsigned *n2_out, unsigned *p_out) +{ + uint64_t freq2k; + unsigned p, n2, r2; + struct wrpll_rnp best = { 0, 0, 0 }; + unsigned budget; - if (i == ARRAY_SIZE(wrpll_tmds_clock_table)) - i--; + freq2k = clock / 100; - *p = wrpll_tmds_clock_table[i].p; - *n2 = wrpll_tmds_clock_table[i].n2; - *r2 = wrpll_tmds_clock_table[i].r2; + budget = wrpll_get_budget_for_freq(clock); + + /* Special case handling for 540 pixel clock: bypass WR PLL entirely + * and directly pass the LC PLL to it. */ + if (freq2k == 5400000) { + *n2_out = 2; + *p_out = 1; + *r2_out = 2; + return; + } + + /* + * Ref = LC_FREQ / R, where Ref is the actual reference input seen by + * the WR PLL. + * + * We want R so that REF_MIN <= Ref <= REF_MAX. + * Injecting R2 = 2 * R gives: + * REF_MAX * r2 > LC_FREQ * 2 and + * REF_MIN * r2 < LC_FREQ * 2 + * + * Which means the desired boundaries for r2 are: + * LC_FREQ * 2 / REF_MAX < r2 < LC_FREQ * 2 / REF_MIN + * + */ + for (r2 = LC_FREQ * 2 / REF_MAX + 1; + r2 <= LC_FREQ * 2 / REF_MIN; + r2++) { + + /* + * VCO = N * Ref, that is: VCO = N * LC_FREQ / R + * + * Once again we want VCO_MIN <= VCO <= VCO_MAX. + * Injecting R2 = 2 * R and N2 = 2 * N, we get: + * VCO_MAX * r2 > n2 * LC_FREQ and + * VCO_MIN * r2 < n2 * LC_FREQ) + * + * Which means the desired boundaries for n2 are: + * VCO_MIN * r2 / LC_FREQ < n2 < VCO_MAX * r2 / LC_FREQ + */ + for (n2 = VCO_MIN * r2 / LC_FREQ + 1; + n2 <= VCO_MAX * r2 / LC_FREQ; + n2++) { + + for (p = P_MIN; p <= P_MAX; p += P_INC) + wrpll_update_rnp(freq2k, budget, + r2, n2, p, &best); + } + } - if (wrpll_tmds_clock_table[i].clock != clock) - DRM_INFO("WRPLL: using settings for %dKHz on %dKHz mode\n", - wrpll_tmds_clock_table[i].clock, clock); + *n2_out = best.n2; + *p_out = best.p; + *r2_out = best.r2; - DRM_DEBUG_KMS("WRPLL: %dKHz refresh rate with p=%d, n2=%d r2=%d\n", - clock, *p, *n2, *r2); + DRM_DEBUG_KMS("WRPLL: %dHz refresh rate with p=%d, n2=%d r2=%d\n", + clock, *p_out, *n2_out, *r2_out); } bool intel_ddi_pll_mode_set(struct drm_crtc *crtc, int clock) @@ -851,7 +662,7 @@ bool intel_ddi_pll_mode_set(struct drm_crtc *crtc, int clock) return true; } else if (type == INTEL_OUTPUT_HDMI) { - int p, n2, r2; + unsigned p, n2, r2; if (plls->wrpll1_refcount == 0) { DRM_DEBUG_KMS("Using WRPLL 1 on pipe %c\n", @@ -873,7 +684,7 @@ bool intel_ddi_pll_mode_set(struct drm_crtc *crtc, int clock) WARN(I915_READ(reg) & WRPLL_PLL_ENABLE, "WRPLL already enabled\n"); - intel_ddi_calculate_wrpll(clock, &p, &n2, &r2); + intel_ddi_calculate_wrpll(clock * 1000, &r2, &n2, &p); val = WRPLL_PLL_ENABLE | WRPLL_PLL_SELECT_LCPLL_2700 | WRPLL_DIVIDER_REFERENCE(r2) | WRPLL_DIVIDER_FEEDBACK(n2) | -- cgit v1.2.3 From 7881d4f11c00f506907b1bccb73df81509dc9c15 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 30 Apr 2013 14:01:46 +0200 Subject: drm/i915: rip out an unused lvds_reg variable Somehow this has been forgotten in commit 1974cad0ee4ce84e5cb792e49c4f0d9421e0312c Author: Daniel Vetter Date: Mon Nov 26 17:22:09 2012 +0100 drm/i915: move is_dual_link_lvds to intel_lvds.c Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 50814ce84a9f..e9d01e5d6ccb 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -651,12 +651,6 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, found = false; if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { - int lvds_reg; - - if (HAS_PCH_SPLIT(dev)) - lvds_reg = PCH_LVDS; - else - lvds_reg = LVDS; if (intel_is_dual_link_lvds(dev)) clock.p2 = limit->p2.p2_fast; else -- cgit v1.2.3 From 7dd23ba0899bd1a203cc1d6e7878d7dfc910a511 Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Fri, 10 May 2013 14:33:17 +0100 Subject: drm/i915: Add missing platform tags to FBC workaround comments There was a race between Rodrigo writing those patches and me formalizing the addition of platform tags. This patches fixes it. Signed-off-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_pm.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index d806448f84fa..28cec57e3f8c 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -243,13 +243,13 @@ static void ironlake_disable_fbc(struct drm_device *dev) I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl); if (IS_IVYBRIDGE(dev)) - /* WaFbcDisableDpfcClockGating */ + /* WaFbcDisableDpfcClockGating:ivb */ I915_WRITE(ILK_DSPCLK_GATE_D, I915_READ(ILK_DSPCLK_GATE_D) & ~ILK_DPFCUNIT_CLOCK_GATE_DISABLE); if (IS_HASWELL(dev)) - /* WaFbcDisableDpfcClockGating */ + /* WaFbcDisableDpfcClockGating:hsw */ I915_WRITE(HSW_CLKGATE_DISABLE_PART_1, I915_READ(HSW_CLKGATE_DISABLE_PART_1) & ~HSW_DPFC_GATING_DISABLE); @@ -281,17 +281,17 @@ static void gen7_enable_fbc(struct drm_crtc *crtc, unsigned long interval) intel_crtc->plane << IVB_DPFC_CTL_PLANE_SHIFT); if (IS_IVYBRIDGE(dev)) { - /* WaFbcAsynchFlipDisableFbcQueue */ + /* WaFbcAsynchFlipDisableFbcQueue:ivb */ I915_WRITE(ILK_DISPLAY_CHICKEN1, ILK_FBCQ_DIS); - /* WaFbcDisableDpfcClockGating */ + /* WaFbcDisableDpfcClockGating:ivb */ I915_WRITE(ILK_DSPCLK_GATE_D, I915_READ(ILK_DSPCLK_GATE_D) | ILK_DPFCUNIT_CLOCK_GATE_DISABLE); } else { - /* WaFbcAsynchFlipDisableFbcQueue */ + /* WaFbcAsynchFlipDisableFbcQueue:hsw */ I915_WRITE(HSW_PIPE_SLICE_CHICKEN_1(intel_crtc->pipe), HSW_BYPASS_FBC_QUEUE); - /* WaFbcDisableDpfcClockGating */ + /* WaFbcDisableDpfcClockGating:hsw */ I915_WRITE(HSW_CLKGATE_DISABLE_PART_1, I915_READ(HSW_CLKGATE_DISABLE_PART_1) | HSW_DPFC_GATING_DISABLE); -- cgit v1.2.3 From 0a790cdbfc526890af7dc41515a563f09e427129 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Wed, 17 Apr 2013 18:15:49 -0300 Subject: drm/i915: implement WADPOClockGatingDisable for LPT This should prevent mode set failures on LPT. Signed-off-by: Paulo Zanoni Reviewed-by: Damien Lespiau [danvet: Pimp the w/a tag to fit into Damien's new scheme.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_pm.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 28cec57e3f8c..1a765723d37e 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -4093,6 +4093,11 @@ static void lpt_init_clock_gating(struct drm_device *dev) I915_WRITE(SOUTH_DSPCLK_GATE_D, I915_READ(SOUTH_DSPCLK_GATE_D) | PCH_LP_PARTITION_LEVEL_DISABLE); + + /* WADPOClockGatingDisable:hsw */ + I915_WRITE(_TRANSA_CHICKEN1, + I915_READ(_TRANSA_CHICKEN1) | + TRANS_CHICKEN1_DP0UNIT_GC_DISABLE); } static void lpt_suspend_hw(struct drm_device *dev) -- cgit v1.2.3 From afc2f792cdcb67f4257f0e68d10ee4a7b7eae57a Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sun, 5 May 2013 20:03:40 +0800 Subject: KVM: add missing misc_deregister() on error in kvm_init() Add the missing misc_deregister() before return from kvm_init() in the debugfs init error handling case. Signed-off-by: Wei Yongjun Signed-off-by: Gleb Natapov --- virt/kvm/kvm_main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 302681c4aa44..b547a1ceecbc 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -3181,6 +3181,7 @@ int kvm_init(void *opaque, unsigned vcpu_size, unsigned vcpu_align, out_undebugfs: unregister_syscore_ops(&kvm_syscore_ops); + misc_deregister(&kvm_dev); out_unreg: kvm_async_pf_deinit(); out_free: -- cgit v1.2.3 From b5b3ee6c9cca8b6e1aa8c757e570f08f802c5573 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 6 May 2013 03:10:35 +0100 Subject: apparmor: no need to delay vfree() vfree() can be called from interrupt contexts now Signed-off-by: Al Viro Acked-by: John Johansen Signed-off-by: James Morris --- security/apparmor/lib.c | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index d40bc592180d..fcfe0233574c 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -110,19 +110,6 @@ void *__aa_kvmalloc(size_t size, gfp_t flags) return buffer; } -/** - * do_vfree - workqueue routine for freeing vmalloced memory - * @work: data to be freed - * - * The work_struct is overlaid to the data being freed, as at the point - * the work is scheduled the data is no longer valid, be its freeing - * needs to be delayed until safe. - */ -static void do_vfree(struct work_struct *work) -{ - vfree(work); -} - /** * kvfree - free an allocation do by kvmalloc * @buffer: buffer to free (MAYBE_NULL) @@ -131,13 +118,8 @@ static void do_vfree(struct work_struct *work) */ void kvfree(void *buffer) { - if (is_vmalloc_addr(buffer)) { - /* Data is no longer valid so just use the allocated space - * as the work_struct - */ - struct work_struct *work = (struct work_struct *) buffer; - INIT_WORK(work, do_vfree); - schedule_work(work); - } else + if (is_vmalloc_addr(buffer)) + vfree(buffer); + else kfree(buffer); } -- cgit v1.2.3 From 4726e8fa1dcad533362475ebf91f70d5b6b6292f Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Thu, 9 May 2013 11:41:04 -0400 Subject: security: clarify cap_inode_getsecctx description Make it clear that cap_inode_getsecctx shouldn't return success without filling in the context data. Acked-by: Serge E. Hallyn Signed-off-by: J. Bruce Fields Signed-off-by: James Morris --- include/linux/security.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/linux/security.h b/include/linux/security.h index 4686491852a7..40560f41e3d5 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1392,7 +1392,8 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * @ctxlen contains the length of @ctx. * * @inode_getsecctx: - * Returns a string containing all relevant security context information + * On success, returns 0 and fills out @ctx and @ctxlen with the security + * context for the given @inode. * * @inode we wish to get the security context of. * @ctx is a pointer in which to place the allocated security context. -- cgit v1.2.3 From e2858b4ab1f8d7ce36ae3cd96ba650664af0db5e Mon Sep 17 00:00:00 2001 From: Takuya Yoshikawa Date: Thu, 9 May 2013 15:45:12 +0900 Subject: KVM: MMU: Use kvm_mmu_sync_roots() in kvm_mmu_load() No need to open-code this function. Signed-off-by: Takuya Yoshikawa Reviewed-by: Xiao Guangrong Signed-off-by: Gleb Natapov --- arch/x86/kvm/mmu.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c index 004cc87b781c..40d7b2ddc6c5 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c @@ -3764,9 +3764,7 @@ int kvm_mmu_load(struct kvm_vcpu *vcpu) if (r) goto out; r = mmu_alloc_roots(vcpu); - spin_lock(&vcpu->kvm->mmu_lock); - mmu_sync_roots(vcpu); - spin_unlock(&vcpu->kvm->mmu_lock); + kvm_mmu_sync_roots(vcpu); if (r) goto out; /* set_cr3() should ensure TLB has been flushed */ -- cgit v1.2.3 From 4f3549d72d1b5c90ecc7e673402f38f4486d22c2 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 2 May 2013 22:15:29 +0200 Subject: Driver core: Add offline/online device operations In some cases, graceful hot-removal of devices is not possible, although in principle the devices in question support hotplug. For example, that may happen for the last CPU in the system or for memory modules holding kernel memory. In those cases it is nice to be able to check if the given device can be gracefully hot-removed before triggering a removal procedure that cannot be aborted or reversed. Unfortunately, however, the kernel currently doesn't provide any support for that. To address that deficiency, introduce support for offline and online operations that can be performed on devices, respectively, before a hot-removal and in case when it is necessary (or convenient) to put a device back online after a successful offline (that has not been followed by removal). The idea is that the offline will fail whenever the given device cannot be gracefully removed from the system and it will not be allowed to use the device after a successful offline (until a subsequent online) in analogy with the existing CPU offline/online mechanism. For now, the offline and online operations are introduced at the bus type level, as that should be sufficient for the most urgent use cases (CPUs and memory modules). In the future, however, the approach may be extended to cover some more complicated device offline/online scenarios involving device drivers etc. The lock_device_hotplug() and unlock_device_hotplug() functions are introduced because subsequent patches need to put larger pieces of code under device_hotplug_lock to prevent race conditions between device offline and removal from happening. Signed-off-by: Rafael J. Wysocki Acked-by: Greg Kroah-Hartman Reviewed-by: Toshi Kani --- Documentation/ABI/testing/sysfs-devices-online | 20 ++++ drivers/base/core.c | 130 +++++++++++++++++++++++++ include/linux/device.h | 21 ++++ 3 files changed, 171 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-devices-online diff --git a/Documentation/ABI/testing/sysfs-devices-online b/Documentation/ABI/testing/sysfs-devices-online new file mode 100644 index 000000000000..f990026c0740 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-devices-online @@ -0,0 +1,20 @@ +What: /sys/devices/.../online +Date: April 2013 +Contact: Rafael J. Wysocki +Description: + The /sys/devices/.../online attribute is only present for + devices whose bus types provide .online() and .offline() + callbacks. The number read from it (0 or 1) reflects the value + of the device's 'offline' field. If that number is 1 and '0' + (or 'n', or 'N') is written to this file, the device bus type's + .offline() callback is executed for the device and (if + successful) its 'offline' field is updated accordingly. In + turn, if that number is 0 and '1' (or 'y', or 'Y') is written to + this file, the device bus type's .online() callback is executed + for the device and (if successful) its 'offline' field is + updated as appropriate. + + After a successful execution of the bus type's .offline() + callback the device cannot be used for any purpose until either + it is removed (i.e. device_del() is called for it), or its bus + type's .online() is exeucted successfully. diff --git a/drivers/base/core.c b/drivers/base/core.c index 016312437577..60c975686089 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -403,6 +403,36 @@ static ssize_t store_uevent(struct device *dev, struct device_attribute *attr, static struct device_attribute uevent_attr = __ATTR(uevent, S_IRUGO | S_IWUSR, show_uevent, store_uevent); +static ssize_t show_online(struct device *dev, struct device_attribute *attr, + char *buf) +{ + bool val; + + lock_device_hotplug(); + val = !dev->offline; + unlock_device_hotplug(); + return sprintf(buf, "%u\n", val); +} + +static ssize_t store_online(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + bool val; + int ret; + + ret = strtobool(buf, &val); + if (ret < 0) + return ret; + + lock_device_hotplug(); + ret = val ? device_online(dev) : device_offline(dev); + unlock_device_hotplug(); + return ret < 0 ? ret : count; +} + +static struct device_attribute online_attr = + __ATTR(online, S_IRUGO | S_IWUSR, show_online, store_online); + static int device_add_attributes(struct device *dev, struct device_attribute *attrs) { @@ -516,6 +546,12 @@ static int device_add_attrs(struct device *dev) if (error) goto err_remove_type_groups; + if (device_supports_offline(dev) && !dev->offline_disabled) { + error = device_create_file(dev, &online_attr); + if (error) + goto err_remove_type_groups; + } + return 0; err_remove_type_groups: @@ -536,6 +572,7 @@ static void device_remove_attrs(struct device *dev) struct class *class = dev->class; const struct device_type *type = dev->type; + device_remove_file(dev, &online_attr); device_remove_groups(dev, dev->groups); if (type) @@ -1431,6 +1468,99 @@ EXPORT_SYMBOL_GPL(put_device); EXPORT_SYMBOL_GPL(device_create_file); EXPORT_SYMBOL_GPL(device_remove_file); +static DEFINE_MUTEX(device_hotplug_lock); + +void lock_device_hotplug(void) +{ + mutex_lock(&device_hotplug_lock); +} + +void unlock_device_hotplug(void) +{ + mutex_unlock(&device_hotplug_lock); +} + +static int device_check_offline(struct device *dev, void *not_used) +{ + int ret; + + ret = device_for_each_child(dev, NULL, device_check_offline); + if (ret) + return ret; + + return device_supports_offline(dev) && !dev->offline ? -EBUSY : 0; +} + +/** + * device_offline - Prepare the device for hot-removal. + * @dev: Device to be put offline. + * + * Execute the device bus type's .offline() callback, if present, to prepare + * the device for a subsequent hot-removal. If that succeeds, the device must + * not be used until either it is removed or its bus type's .online() callback + * is executed. + * + * Call under device_hotplug_lock. + */ +int device_offline(struct device *dev) +{ + int ret; + + if (dev->offline_disabled) + return -EPERM; + + ret = device_for_each_child(dev, NULL, device_check_offline); + if (ret) + return ret; + + device_lock(dev); + if (device_supports_offline(dev)) { + if (dev->offline) { + ret = 1; + } else { + ret = dev->bus->offline(dev); + if (!ret) { + kobject_uevent(&dev->kobj, KOBJ_OFFLINE); + dev->offline = true; + } + } + } + device_unlock(dev); + + return ret; +} + +/** + * device_online - Put the device back online after successful device_offline(). + * @dev: Device to be put back online. + * + * If device_offline() has been successfully executed for @dev, but the device + * has not been removed subsequently, execute its bus type's .online() callback + * to indicate that the device can be used again. + * + * Call under device_hotplug_lock. + */ +int device_online(struct device *dev) +{ + int ret = 0; + + device_lock(dev); + if (device_supports_offline(dev)) { + if (dev->offline) { + ret = dev->bus->online(dev); + if (!ret) { + kobject_uevent(&dev->kobj, KOBJ_ONLINE); + dev->offline = false; + } + } else { + ret = 1; + } + } + device_unlock(dev); + + return ret; +} + struct root_device { struct device dev; struct module *owner; diff --git a/include/linux/device.h b/include/linux/device.h index c0a126125325..eeb33315514c 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -71,6 +71,10 @@ extern void bus_remove_file(struct bus_type *, struct bus_attribute *); * the specific driver's probe to initial the matched device. * @remove: Called when a device removed from this bus. * @shutdown: Called at shut-down time to quiesce the device. + * + * @online: Called to put the device back online (after offlining it). + * @offline: Called to put the device offline for hot-removal. May fail. + * * @suspend: Called when a device on this bus wants to go to sleep mode. * @resume: Called to bring a device on this bus out of sleep mode. * @pm: Power management operations of this bus, callback the specific @@ -104,6 +108,9 @@ struct bus_type { int (*remove)(struct device *dev); void (*shutdown)(struct device *dev); + int (*online)(struct device *dev); + int (*offline)(struct device *dev); + int (*suspend)(struct device *dev, pm_message_t state); int (*resume)(struct device *dev); @@ -648,6 +655,8 @@ struct acpi_dev_node { * @release: Callback to free the device after all references have * gone away. This should be set by the allocator of the * device (i.e. the bus driver that discovered the device). + * @offline_disabled: If set, the device is permanently online. + * @offline: Set after successful invocation of bus type's .offline(). * * At the lowest level, every device in a Linux system is represented by an * instance of struct device. The device structure contains the information @@ -720,6 +729,9 @@ struct device { void (*release)(struct device *dev); struct iommu_group *iommu_group; + + bool offline_disabled:1; + bool offline:1; }; static inline struct device *kobj_to_dev(struct kobject *kobj) @@ -856,6 +868,15 @@ extern const char *device_get_devnode(struct device *dev, extern void *dev_get_drvdata(const struct device *dev); extern int dev_set_drvdata(struct device *dev, void *data); +static inline bool device_supports_offline(struct device *dev) +{ + return dev->bus && dev->bus->offline && dev->bus->online; +} + +extern void lock_device_hotplug(void); +extern void unlock_device_hotplug(void); +extern int device_offline(struct device *dev); +extern int device_online(struct device *dev); /* * Root device objects for grouping under /sys/devices */ -- cgit v1.2.3 From 0902a9044fa5b7a0456ea4daacec2c2b3189ba8c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 3 May 2013 00:25:49 +0200 Subject: Driver core: Use generic offline/online for CPU offline/online Rework the CPU hotplug code in drivers/base/cpu.c to use the generic offline/online support introduced previously instead of its own CPU-specific code. For this purpose, modify cpu_subsys to provide offline and online callbacks for CONFIG_HOTPLUG_CPU set and remove the code handling the CPU-specific 'online' sysfs attribute. This modification is not supposed to change the user-observable behavior of the kernel (i.e. the 'online' attribute will be present in exactly the same place in sysfs and should trigger exactly the same actions as before). Signed-off-by: Rafael J. Wysocki Acked-by: Greg Kroah-Hartman Reviewed-by: Toshi Kani --- drivers/base/cpu.c | 89 ++++++++++++++++++++---------------------------------- 1 file changed, 32 insertions(+), 57 deletions(-) diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 3d48fc887ef4..25c8768172e9 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -16,12 +16,6 @@ #include "base.h" -struct bus_type cpu_subsys = { - .name = "cpu", - .dev_name = "cpu", -}; -EXPORT_SYMBOL_GPL(cpu_subsys); - static DEFINE_PER_CPU(struct device *, cpu_sys_devices); #ifdef CONFIG_HOTPLUG_CPU @@ -34,69 +28,45 @@ static void change_cpu_under_node(struct cpu *cpu, cpu->node_id = to_nid; } -static ssize_t show_online(struct device *dev, - struct device_attribute *attr, - char *buf) +static int __ref cpu_subsys_online(struct device *dev) { struct cpu *cpu = container_of(dev, struct cpu, dev); + int cpuid = dev->id; + int from_nid, to_nid; + int ret; + + cpu_hotplug_driver_lock(); + + from_nid = cpu_to_node(cpuid); + ret = cpu_up(cpuid); + /* + * When hot adding memory to memoryless node and enabling a cpu + * on the node, node number of the cpu may internally change. + */ + to_nid = cpu_to_node(cpuid); + if (from_nid != to_nid) + change_cpu_under_node(cpu, from_nid, to_nid); - return sprintf(buf, "%u\n", !!cpu_online(cpu->dev.id)); + cpu_hotplug_driver_unlock(); + return ret; } -static ssize_t __ref store_online(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static int cpu_subsys_offline(struct device *dev) { - struct cpu *cpu = container_of(dev, struct cpu, dev); - int cpuid = cpu->dev.id; - int from_nid, to_nid; - ssize_t ret; + int ret; cpu_hotplug_driver_lock(); - switch (buf[0]) { - case '0': - ret = cpu_down(cpuid); - if (!ret) - kobject_uevent(&dev->kobj, KOBJ_OFFLINE); - break; - case '1': - from_nid = cpu_to_node(cpuid); - ret = cpu_up(cpuid); - - /* - * When hot adding memory to memoryless node and enabling a cpu - * on the node, node number of the cpu may internally change. - */ - to_nid = cpu_to_node(cpuid); - if (from_nid != to_nid) - change_cpu_under_node(cpu, from_nid, to_nid); - - if (!ret) - kobject_uevent(&dev->kobj, KOBJ_ONLINE); - break; - default: - ret = -EINVAL; - } + ret = cpu_down(dev->id); cpu_hotplug_driver_unlock(); - - if (ret >= 0) - ret = count; return ret; } -static DEVICE_ATTR(online, 0644, show_online, store_online); -static void __cpuinit register_cpu_control(struct cpu *cpu) -{ - device_create_file(&cpu->dev, &dev_attr_online); -} void unregister_cpu(struct cpu *cpu) { int logical_cpu = cpu->dev.id; unregister_cpu_under_node(logical_cpu, cpu_to_node(logical_cpu)); - device_remove_file(&cpu->dev, &dev_attr_online); - device_unregister(&cpu->dev); per_cpu(cpu_sys_devices, logical_cpu) = NULL; return; @@ -123,12 +93,18 @@ static DEVICE_ATTR(probe, S_IWUSR, NULL, cpu_probe_store); static DEVICE_ATTR(release, S_IWUSR, NULL, cpu_release_store); #endif /* CONFIG_ARCH_CPU_PROBE_RELEASE */ -#else /* ... !CONFIG_HOTPLUG_CPU */ -static inline void register_cpu_control(struct cpu *cpu) -{ -} #endif /* CONFIG_HOTPLUG_CPU */ +struct bus_type cpu_subsys = { + .name = "cpu", + .dev_name = "cpu", +#ifdef CONFIG_HOTPLUG_CPU + .online = cpu_subsys_online, + .offline = cpu_subsys_offline, +#endif +}; +EXPORT_SYMBOL_GPL(cpu_subsys); + #ifdef CONFIG_KEXEC #include @@ -277,12 +253,11 @@ int __cpuinit register_cpu(struct cpu *cpu, int num) cpu->dev.id = num; cpu->dev.bus = &cpu_subsys; cpu->dev.release = cpu_device_release; + cpu->dev.offline_disabled = !cpu->hotpluggable; #ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE cpu->dev.bus->uevent = arch_cpu_uevent; #endif error = device_register(&cpu->dev); - if (!error && cpu->hotpluggable) - register_cpu_control(cpu); if (!error) per_cpu(cpu_sys_devices, num) = &cpu->dev; if (!error) -- cgit v1.2.3 From 683058e315f00a216fd6c79df4f63bc9945ca434 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 3 May 2013 00:26:16 +0200 Subject: ACPI / hotplug: Use device offline/online for graceful hot-removal Modify the generic ACPI hotplug code to be able to check if devices scheduled for hot-removal may be gracefully removed from the system using the device offline/online mechanism introduced previously. Namely, make acpi_scan_hot_remove() handling device hot-removal call device_offline() for all physical companions of the ACPI device nodes involved in the operation and check the results. If any of the device_offline() calls fails, the function will not progress to the removal phase (which cannot be aborted), unless its (new) force argument is set (in case of a failing offline it will put the devices offlined by it back online). In support of 'forced' device hot-removal, add a new sysfs attribute 'force_remove' that will reside under /sys/firmware/acpi/hotplug/. Signed-off-by: Rafael J. Wysocki Reviewed-by: Toshi Kani --- Documentation/ABI/testing/sysfs-firmware-acpi | 10 ++++ drivers/acpi/internal.h | 2 + drivers/acpi/scan.c | 84 +++++++++++++++++++++++++++ drivers/acpi/sysfs.c | 31 ++++++++++ include/acpi/acpi_bus.h | 1 + 5 files changed, 128 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-firmware-acpi b/Documentation/ABI/testing/sysfs-firmware-acpi index ce9bee98b43b..b4436cca97a8 100644 --- a/Documentation/ABI/testing/sysfs-firmware-acpi +++ b/Documentation/ABI/testing/sysfs-firmware-acpi @@ -44,6 +44,16 @@ Description: or 0 (unset). Attempts to write any other values to it will cause -EINVAL to be returned. +What: /sys/firmware/acpi/hotplug/force_remove +Date: May 2013 +Contact: Rafael J. Wysocki +Description: + The number in this file (0 or 1) determines whether (1) or not + (0) the ACPI subsystem will allow devices to be hot-removed even + if they cannot be put offline gracefully (from the kernel's + viewpoint). That number can be changed by writing a boolean + value to this file. + What: /sys/firmware/acpi/interrupts/ Date: February 2008 Contact: Len Brown diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 6f1afd9118c8..4548f0a114ce 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -47,6 +47,8 @@ void acpi_memory_hotplug_init(void); static inline void acpi_memory_hotplug_init(void) {} #endif +extern bool acpi_force_hot_remove; + void acpi_sysfs_add_hotplug_profile(struct acpi_hotplug_profile *hotplug, const char *name); int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler, diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index fe158fd4f1df..4fd392005ef1 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -27,6 +27,12 @@ extern struct acpi_device *acpi_root; #define ACPI_IS_ROOT_DEVICE(device) (!(device)->parent) +/* + * If set, devices will be hot-removed even if they cannot be put offline + * gracefully (from the kernel's standpoint). + */ +bool acpi_force_hot_remove; + static const char *dummy_hid = "device"; static LIST_HEAD(acpi_device_list); @@ -120,6 +126,59 @@ acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, cha } static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); +static acpi_status acpi_bus_offline_companions(acpi_handle handle, u32 lvl, + void *data, void **ret_p) +{ + struct acpi_device *device = NULL; + struct acpi_device_physical_node *pn; + acpi_status status = AE_OK; + + if (acpi_bus_get_device(handle, &device)) + return AE_OK; + + mutex_lock(&device->physical_node_lock); + + list_for_each_entry(pn, &device->physical_node_list, node) { + int ret; + + ret = device_offline(pn->dev); + if (acpi_force_hot_remove) + continue; + + if (ret < 0) { + status = AE_ERROR; + break; + } + pn->put_online = !ret; + } + + mutex_unlock(&device->physical_node_lock); + + return status; +} + +static acpi_status acpi_bus_online_companions(acpi_handle handle, u32 lvl, + void *data, void **ret_p) +{ + struct acpi_device *device = NULL; + struct acpi_device_physical_node *pn; + + if (acpi_bus_get_device(handle, &device)) + return AE_OK; + + mutex_lock(&device->physical_node_lock); + + list_for_each_entry(pn, &device->physical_node_list, node) + if (pn->put_online) { + device_online(pn->dev); + pn->put_online = false; + } + + mutex_unlock(&device->physical_node_lock); + + return AE_OK; +} + static int acpi_scan_hot_remove(struct acpi_device *device) { acpi_handle handle = device->handle; @@ -136,10 +195,33 @@ static int acpi_scan_hot_remove(struct acpi_device *device) return -EINVAL; } + lock_device_hotplug(); + + status = acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, + NULL, acpi_bus_offline_companions, NULL, + NULL); + if (ACPI_SUCCESS(status) || acpi_force_hot_remove) + status = acpi_bus_offline_companions(handle, 0, NULL, NULL); + + if (ACPI_FAILURE(status) && !acpi_force_hot_remove) { + acpi_bus_online_companions(handle, 0, NULL, NULL); + acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX, + acpi_bus_online_companions, NULL, NULL, + NULL); + + unlock_device_hotplug(); + + put_device(&device->dev); + return -EBUSY; + } + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Hot-removing device %s...\n", dev_name(&device->dev))); acpi_bus_trim(device); + + unlock_device_hotplug(); + /* Device node has been unregistered. */ put_device(&device->dev); device = NULL; @@ -236,6 +318,7 @@ static void acpi_scan_bus_device_check(acpi_handle handle, u32 ost_source) int error; mutex_lock(&acpi_scan_lock); + lock_device_hotplug(); acpi_bus_get_device(handle, &device); if (device) { @@ -259,6 +342,7 @@ static void acpi_scan_bus_device_check(acpi_handle handle, u32 ost_source) kobject_uevent(&device->dev.kobj, KOBJ_ONLINE); out: + unlock_device_hotplug(); acpi_evaluate_hotplug_ost(handle, ost_source, ost_code, NULL); mutex_unlock(&acpi_scan_lock); } diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c index fcae5fa2e1b3..5c5d1624fa2c 100644 --- a/drivers/acpi/sysfs.c +++ b/drivers/acpi/sysfs.c @@ -780,6 +780,33 @@ void acpi_sysfs_add_hotplug_profile(struct acpi_hotplug_profile *hotplug, pr_err(PREFIX "Unable to add hotplug profile '%s'\n", name); } +static ssize_t force_remove_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", !!acpi_force_hot_remove); +} + +static ssize_t force_remove_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t size) +{ + bool val; + int ret; + + ret = strtobool(buf, &val); + if (ret < 0) + return ret; + + lock_device_hotplug(); + acpi_force_hot_remove = val; + unlock_device_hotplug(); + return size; +} + +static const struct kobj_attribute force_remove_attr = + __ATTR(force_remove, S_IRUGO | S_IWUSR, force_remove_show, + force_remove_store); + int __init acpi_sysfs_init(void) { int result; @@ -789,6 +816,10 @@ int __init acpi_sysfs_init(void) return result; hotplug_kobj = kobject_create_and_add("hotplug", acpi_kobj); + result = sysfs_create_file(hotplug_kobj, &force_remove_attr.attr); + if (result) + return result; + result = sysfs_create_file(acpi_kobj, &pm_profile_attr.attr); return result; } diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 98db31d9f9b4..4d5d3e7ba33d 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -286,6 +286,7 @@ struct acpi_device_physical_node { u8 node_id; struct list_head node; struct device *dev; + bool put_online:1; }; /* set maximum of physical nodes to 32 for expansibility */ -- cgit v1.2.3 From ac212b6980d8d5eda705864fc5a8ecddc6d6eacc Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 3 May 2013 00:26:22 +0200 Subject: ACPI / processor: Use common hotplug infrastructure Split the ACPI processor driver into two parts, one that is non-modular, resides in the ACPI core and handles the enumeration and hotplug of processors and one that implements the rest of the existing processor driver functionality. The non-modular part uses an ACPI scan handler object to enumerate processors on the basis of information provided by the ACPI namespace and to hook up with the common ACPI hotplug infrastructure. It also populates the ACPI handle of each processor device having a corresponding object in the ACPI namespace, which allows the driver proper to bind to those devices, and makes the driver bind to them if it is readily available (i.e. loaded) when the scan handler's .attach() routine is running. There are a few reasons to make this change. First, switching the ACPI processor driver to using the common ACPI hotplug infrastructure reduces code duplication and size considerably, even though a new file is created along with a header comment etc. Second, since the common hotplug code attempts to offline devices before starting the (non-reversible) removal procedure, it will abort (and possibly roll back) hot-remove operations involving processors if cpu_down() returns an error code for one of them instead of continuing them blindly (if /sys/firmware/acpi/hotplug/force_remove is unset). That is a more desirable behavior than what the current code does. Finally, the separation of the scan/hotplug part from the driver proper makes it possible to simplify the driver's .remove() routine, because it doesn't need to worry about the possible cleanup related to processor removal any more (the scan/hotplug part is responsible for that now) and can handle device removal and driver removal symmetricaly (i.e. as appropriate). Some user-visible changes in sysfs are made (for example, the 'sysdev' link from the ACPI device node to the processor device's directory is gone and a 'physical_node' link is present instead and a corresponding 'firmware_node' is present in the processor device's directory, the processor driver is now visible under /sys/bus/cpu/drivers/ and bound to the processor device), but that shouldn't affect the functionality that users care about (frequency scaling, C-states and thermal management). Tested on my venerable Toshiba Portege R500. Signed-off-by: Rafael J. Wysocki Acked-by: Greg Kroah-Hartman Reviewed-by: Toshi Kani --- drivers/acpi/Makefile | 1 + drivers/acpi/acpi_processor.c | 484 ++++++++++++++++++++++++ drivers/acpi/glue.c | 6 +- drivers/acpi/internal.h | 3 + drivers/acpi/processor_driver.c | 807 ++++------------------------------------ drivers/acpi/scan.c | 1 + drivers/base/cpu.c | 11 + include/acpi/processor.h | 5 + 8 files changed, 587 insertions(+), 731 deletions(-) create mode 100644 drivers/acpi/acpi_processor.c diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index ecb743bf05a5..93e49bde31ba 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -34,6 +34,7 @@ acpi-$(CONFIG_ACPI_SLEEP) += proc.o acpi-y += bus.o glue.o acpi-y += scan.o acpi-y += resource.o +acpi-y += acpi_processor.o acpi-y += processor_core.o acpi-y += ec.o acpi-$(CONFIG_ACPI_DOCK) += dock.o diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c new file mode 100644 index 000000000000..587d2af4b323 --- /dev/null +++ b/drivers/acpi/acpi_processor.c @@ -0,0 +1,484 @@ +/* + * acpi_processor.c - ACPI processor enumeration support + * + * Copyright (C) 2001, 2002 Andy Grover + * Copyright (C) 2001, 2002 Paul Diefenbaugh + * Copyright (C) 2004 Dominik Brodowski + * Copyright (C) 2004 Anil S Keshavamurthy + * Copyright (C) 2013, Intel Corporation + * Rafael J. Wysocki + * + * 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 +#include +#include +#include +#include + +#include + +#include + +#include "internal.h" + +#define _COMPONENT ACPI_PROCESSOR_COMPONENT + +ACPI_MODULE_NAME("processor"); + +/* -------------------------------------------------------------------------- + Errata Handling + -------------------------------------------------------------------------- */ + +struct acpi_processor_errata errata __read_mostly; +EXPORT_SYMBOL_GPL(errata); + +static int acpi_processor_errata_piix4(struct pci_dev *dev) +{ + u8 value1 = 0; + u8 value2 = 0; + + + if (!dev) + return -EINVAL; + + /* + * Note that 'dev' references the PIIX4 ACPI Controller. + */ + + switch (dev->revision) { + case 0: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found PIIX4 A-step\n")); + break; + case 1: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found PIIX4 B-step\n")); + break; + case 2: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found PIIX4E\n")); + break; + case 3: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found PIIX4M\n")); + break; + default: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found unknown PIIX4\n")); + break; + } + + switch (dev->revision) { + + case 0: /* PIIX4 A-step */ + case 1: /* PIIX4 B-step */ + /* + * See specification changes #13 ("Manual Throttle Duty Cycle") + * and #14 ("Enabling and Disabling Manual Throttle"), plus + * erratum #5 ("STPCLK# Deassertion Time") from the January + * 2002 PIIX4 specification update. Applies to only older + * PIIX4 models. + */ + errata.piix4.throttle = 1; + + case 2: /* PIIX4E */ + case 3: /* PIIX4M */ + /* + * See erratum #18 ("C3 Power State/BMIDE and Type-F DMA + * Livelock") from the January 2002 PIIX4 specification update. + * Applies to all PIIX4 models. + */ + + /* + * BM-IDE + * ------ + * Find the PIIX4 IDE Controller and get the Bus Master IDE + * Status register address. We'll use this later to read + * each IDE controller's DMA status to make sure we catch all + * DMA activity. + */ + dev = pci_get_subsys(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82371AB, + PCI_ANY_ID, PCI_ANY_ID, NULL); + if (dev) { + errata.piix4.bmisx = pci_resource_start(dev, 4); + pci_dev_put(dev); + } + + /* + * Type-F DMA + * ---------- + * Find the PIIX4 ISA Controller and read the Motherboard + * DMA controller's status to see if Type-F (Fast) DMA mode + * is enabled (bit 7) on either channel. Note that we'll + * disable C3 support if this is enabled, as some legacy + * devices won't operate well if fast DMA is disabled. + */ + dev = pci_get_subsys(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82371AB_0, + PCI_ANY_ID, PCI_ANY_ID, NULL); + if (dev) { + pci_read_config_byte(dev, 0x76, &value1); + pci_read_config_byte(dev, 0x77, &value2); + if ((value1 & 0x80) || (value2 & 0x80)) + errata.piix4.fdma = 1; + pci_dev_put(dev); + } + + break; + } + + if (errata.piix4.bmisx) + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Bus master activity detection (BM-IDE) erratum enabled\n")); + if (errata.piix4.fdma) + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Type-F DMA livelock erratum (C3 disabled)\n")); + + return 0; +} + +static int acpi_processor_errata(struct acpi_processor *pr) +{ + int result = 0; + struct pci_dev *dev = NULL; + + + if (!pr) + return -EINVAL; + + /* + * PIIX4 + */ + dev = pci_get_subsys(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82371AB_3, PCI_ANY_ID, + PCI_ANY_ID, NULL); + if (dev) { + result = acpi_processor_errata_piix4(dev); + pci_dev_put(dev); + } + + return result; +} + +/* -------------------------------------------------------------------------- + Initialization + -------------------------------------------------------------------------- */ + +#ifdef CONFIG_ACPI_HOTPLUG_CPU +static int acpi_processor_hotadd_init(struct acpi_processor *pr) +{ + unsigned long long sta; + acpi_status status; + int ret; + + status = acpi_evaluate_integer(pr->handle, "_STA", NULL, &sta); + if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_PRESENT)) + return -ENODEV; + + ret = acpi_map_lsapic(pr->handle, &pr->id); + if (ret) + return ret; + + ret = arch_register_cpu(pr->id); + if (ret) { + acpi_unmap_lsapic(pr->id); + return ret; + } + + /* + * CPU got hot-added, but cpu_data is not initialized yet. Set a flag + * to delay cpu_idle/throttling initialization and do it when the CPU + * gets online for the first time. + */ + pr_info("CPU%d has been hot-added\n", pr->id); + pr->flags.need_hotplug_init = 1; + return 0; +} +#else +static inline int acpi_processor_hotadd_init(struct acpi_processor *pr) +{ + return -ENODEV; +} +#endif /* CONFIG_ACPI_HOTPLUG_CPU */ + +static int acpi_processor_get_info(struct acpi_device *device) +{ + union acpi_object object = { 0 }; + struct acpi_buffer buffer = { sizeof(union acpi_object), &object }; + struct acpi_processor *pr = acpi_driver_data(device); + int cpu_index, device_declaration = 0; + acpi_status status = AE_OK; + static int cpu0_initialized; + + if (num_online_cpus() > 1) + errata.smp = TRUE; + + acpi_processor_errata(pr); + + /* + * Check to see if we have bus mastering arbitration control. This + * is required for proper C3 usage (to maintain cache coherency). + */ + if (acpi_gbl_FADT.pm2_control_block && acpi_gbl_FADT.pm2_control_length) { + pr->flags.bm_control = 1; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Bus mastering arbitration control present\n")); + } else + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "No bus mastering arbitration control\n")); + + if (!strcmp(acpi_device_hid(device), ACPI_PROCESSOR_OBJECT_HID)) { + /* Declared with "Processor" statement; match ProcessorID */ + status = acpi_evaluate_object(pr->handle, NULL, NULL, &buffer); + if (ACPI_FAILURE(status)) { + dev_err(&device->dev, + "Failed to evaluate processor object (0x%x)\n", + status); + return -ENODEV; + } + + /* + * TBD: Synch processor ID (via LAPIC/LSAPIC structures) on SMP. + * >>> 'acpi_get_processor_id(acpi_id, &id)' in + * arch/xxx/acpi.c + */ + pr->acpi_id = object.processor.proc_id; + } else { + /* + * Declared with "Device" statement; match _UID. + * Note that we don't handle string _UIDs yet. + */ + unsigned long long value; + status = acpi_evaluate_integer(pr->handle, METHOD_NAME__UID, + NULL, &value); + if (ACPI_FAILURE(status)) { + dev_err(&device->dev, + "Failed to evaluate processor _UID (0x%x)\n", + status); + return -ENODEV; + } + device_declaration = 1; + pr->acpi_id = value; + } + cpu_index = acpi_get_cpuid(pr->handle, device_declaration, pr->acpi_id); + + /* Handle UP system running SMP kernel, with no LAPIC in MADT */ + if (!cpu0_initialized && (cpu_index == -1) && + (num_online_cpus() == 1)) { + cpu_index = 0; + } + + cpu0_initialized = 1; + + pr->id = cpu_index; + + /* + * Extra Processor objects may be enumerated on MP systems with + * less than the max # of CPUs. They should be ignored _iff + * they are physically not present. + */ + if (pr->id == -1) { + int ret = acpi_processor_hotadd_init(pr); + if (ret) + return ret; + } + /* + * On some boxes several processors use the same processor bus id. + * But they are located in different scope. For example: + * \_SB.SCK0.CPU0 + * \_SB.SCK1.CPU0 + * Rename the processor device bus id. And the new bus id will be + * generated as the following format: + * CPU+CPU ID. + */ + sprintf(acpi_device_bid(device), "CPU%X", pr->id); + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Processor [%d:%d]\n", pr->id, + pr->acpi_id)); + + if (!object.processor.pblk_address) + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No PBLK (NULL address)\n")); + else if (object.processor.pblk_length != 6) + dev_err(&device->dev, "Invalid PBLK length [%d]\n", + object.processor.pblk_length); + else { + pr->throttling.address = object.processor.pblk_address; + pr->throttling.duty_offset = acpi_gbl_FADT.duty_offset; + pr->throttling.duty_width = acpi_gbl_FADT.duty_width; + + pr->pblk = object.processor.pblk_address; + + /* + * We don't care about error returns - we just try to mark + * these reserved so that nobody else is confused into thinking + * that this region might be unused.. + * + * (In particular, allocating the IO range for Cardbus) + */ + request_region(pr->throttling.address, 6, "ACPI CPU throttle"); + } + + /* + * If ACPI describes a slot number for this CPU, we can use it to + * ensure we get the right value in the "physical id" field + * of /proc/cpuinfo + */ + status = acpi_evaluate_object(pr->handle, "_SUN", NULL, &buffer); + if (ACPI_SUCCESS(status)) + arch_fix_phys_package_id(pr->id, object.integer.value); + + return 0; +} + +/* + * Do not put anything in here which needs the core to be online. + * For example MSR access or setting up things which check for cpuinfo_x86 + * (cpu_data(cpu)) values, like CPU feature flags, family, model, etc. + * Such things have to be put in and set up by the processor driver's .probe(). + */ +static DEFINE_PER_CPU(void *, processor_device_array); + +static int __cpuinit acpi_processor_add(struct acpi_device *device, + const struct acpi_device_id *id) +{ + struct acpi_processor *pr; + struct device *dev; + int result = 0; + + pr = kzalloc(sizeof(struct acpi_processor), GFP_KERNEL); + if (!pr) + return -ENOMEM; + + if (!zalloc_cpumask_var(&pr->throttling.shared_cpu_map, GFP_KERNEL)) { + result = -ENOMEM; + goto err_free_pr; + } + + pr->handle = device->handle; + strcpy(acpi_device_name(device), ACPI_PROCESSOR_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_PROCESSOR_CLASS); + device->driver_data = pr; + + result = acpi_processor_get_info(device); + if (result) /* Processor is not physically present or unavailable */ + return 0; + +#ifdef CONFIG_SMP + if (pr->id >= setup_max_cpus && pr->id != 0) + return 0; +#endif + + BUG_ON(pr->id >= nr_cpu_ids); + + /* + * Buggy BIOS check. + * ACPI id of processors can be reported wrongly by the BIOS. + * Don't trust it blindly + */ + if (per_cpu(processor_device_array, pr->id) != NULL && + per_cpu(processor_device_array, pr->id) != device) { + dev_warn(&device->dev, + "BIOS reported wrong ACPI id %d for the processor\n", + pr->id); + /* Give up, but do not abort the namespace scan. */ + goto err; + } + /* + * processor_device_array is not cleared on errors to allow buggy BIOS + * checks. + */ + per_cpu(processor_device_array, pr->id) = device; + + dev = get_cpu_device(pr->id); + ACPI_HANDLE_SET(dev, pr->handle); + result = acpi_bind_one(dev, NULL); + if (result) + goto err; + + pr->dev = dev; + dev->offline = pr->flags.need_hotplug_init; + + /* Trigger the processor driver's .probe() if present. */ + if (device_attach(dev) >= 0) + return 1; + + dev_err(dev, "Processor driver could not be attached\n"); + acpi_unbind_one(dev); + + err: + free_cpumask_var(pr->throttling.shared_cpu_map); + device->driver_data = NULL; + err_free_pr: + kfree(pr); + return result; +} + +#ifdef CONFIG_ACPI_HOTPLUG_CPU +/* -------------------------------------------------------------------------- + Removal + -------------------------------------------------------------------------- */ + +static void acpi_processor_remove(struct acpi_device *device) +{ + struct acpi_processor *pr; + + if (!device || !acpi_driver_data(device)) + return; + + pr = acpi_driver_data(device); + if (pr->id >= nr_cpu_ids) + goto out; + + /* + * The only reason why we ever get here is CPU hot-removal. The CPU is + * already offline and the ACPI device removal locking prevents it from + * being put back online at this point. + * + * Unbind the driver from the processor device and detach it from the + * ACPI companion object. + */ + device_release_driver(pr->dev); + acpi_unbind_one(pr->dev); + + /* Clean up. */ + per_cpu(processor_device_array, pr->id) = NULL; + try_offline_node(cpu_to_node(pr->id)); + + /* Remove the CPU. */ + get_online_cpus(); + arch_unregister_cpu(pr->id); + acpi_unmap_lsapic(pr->id); + put_online_cpus(); + + out: + free_cpumask_var(pr->throttling.shared_cpu_map); + kfree(pr); +} +#endif /* CONFIG_ACPI_HOTPLUG_CPU */ + +/* + * The following ACPI IDs are known to be suitable for representing as + * processor devices. + */ +static const struct acpi_device_id processor_device_ids[] = { + + { ACPI_PROCESSOR_OBJECT_HID, }, + { ACPI_PROCESSOR_DEVICE_HID, }, + + { } +}; + +static struct acpi_scan_handler __refdata processor_handler = { + .ids = processor_device_ids, + .attach = acpi_processor_add, +#ifdef CONFIG_ACPI_HOTPLUG_CPU + .detach = acpi_processor_remove, +#endif + .hotplug = { + .enabled = true, + }, +}; + +void __init acpi_processor_init(void) +{ + acpi_scan_add_handler_with_hotplug(&processor_handler, "processor"); +} diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 40a84cc6740c..9783f400d857 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -105,7 +105,7 @@ acpi_handle acpi_get_child(acpi_handle parent, u64 address) } EXPORT_SYMBOL(acpi_get_child); -static int acpi_bind_one(struct device *dev, acpi_handle handle) +int acpi_bind_one(struct device *dev, acpi_handle handle) { struct acpi_device *acpi_dev; acpi_status status; @@ -188,8 +188,9 @@ static int acpi_bind_one(struct device *dev, acpi_handle handle) kfree(physical_node); goto err; } +EXPORT_SYMBOL_GPL(acpi_bind_one); -static int acpi_unbind_one(struct device *dev) +int acpi_unbind_one(struct device *dev) { struct acpi_device_physical_node *entry; struct acpi_device *acpi_dev; @@ -238,6 +239,7 @@ err: dev_err(dev, "Oops, 'acpi_handle' corrupt\n"); return -EINVAL; } +EXPORT_SYMBOL_GPL(acpi_unbind_one); static int acpi_platform_notify(struct device *dev) { diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 4548f0a114ce..bf792595132c 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -33,6 +33,7 @@ static inline void acpi_pci_slot_init(void) { } void acpi_pci_root_init(void); void acpi_pci_link_init(void); void acpi_pci_root_hp_init(void); +void acpi_processor_init(void); void acpi_platform_init(void); int acpi_sysfs_init(void); void acpi_csrt_init(void); @@ -79,6 +80,8 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, int type, unsigned long long sta); void acpi_device_add_finalize(struct acpi_device *device); void acpi_free_pnp_ids(struct acpi_device_pnp *pnp); +int acpi_bind_one(struct device *dev, acpi_handle handle); +int acpi_unbind_one(struct device *dev); /* -------------------------------------------------------------------------- Power Resource diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index c266cdc11784..ac28f18823b3 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -1,11 +1,13 @@ /* - * acpi_processor.c - ACPI Processor Driver ($Revision: 71 $) + * processor_driver.c - ACPI Processor Driver * * Copyright (C) 2001, 2002 Andy Grover * Copyright (C) 2001, 2002 Paul Diefenbaugh * Copyright (C) 2004 Dominik Brodowski * Copyright (C) 2004 Anil S Keshavamurthy * - Added processor hotplug support + * Copyright (C) 2013, Intel Corporation + * Rafael J. Wysocki * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -24,52 +26,29 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * TBD: - * 1. Make # power states dynamic. - * 2. Support duty_cycle values that span bit 4. - * 3. Optimize by having scheduler determine business instead of - * having us try to calculate it here. - * 4. Need C1 timing -- must modify kernel (IRQ handler) to get this. */ #include #include #include -#include -#include -#include #include #include -#include -#include #include #include #include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include + #include +#include "internal.h" + #define PREFIX "ACPI: " -#define ACPI_PROCESSOR_CLASS "processor" -#define ACPI_PROCESSOR_DEVICE_NAME "Processor" #define ACPI_PROCESSOR_FILE_INFO "info" #define ACPI_PROCESSOR_FILE_THROTTLING "throttling" #define ACPI_PROCESSOR_FILE_LIMIT "limit" #define ACPI_PROCESSOR_NOTIFY_PERFORMANCE 0x80 #define ACPI_PROCESSOR_NOTIFY_POWER 0x81 #define ACPI_PROCESSOR_NOTIFY_THROTTLING 0x82 -#define ACPI_PROCESSOR_DEVICE_HID "ACPI0007" #define ACPI_PROCESSOR_LIMIT_USER 0 #define ACPI_PROCESSOR_LIMIT_THERMAL 1 @@ -81,12 +60,8 @@ MODULE_AUTHOR("Paul Diefenbaugh"); MODULE_DESCRIPTION("ACPI Processor Driver"); MODULE_LICENSE("GPL"); -static int acpi_processor_add(struct acpi_device *device); -static int acpi_processor_remove(struct acpi_device *device); -static void acpi_processor_notify(struct acpi_device *device, u32 event); -static acpi_status acpi_processor_hotadd_init(struct acpi_processor *pr); -static int acpi_processor_handle_eject(struct acpi_processor *pr); -static int acpi_processor_start(struct acpi_processor *pr); +static int acpi_processor_start(struct device *dev); +static int acpi_processor_stop(struct device *dev); static const struct acpi_device_id processor_device_ids[] = { {ACPI_PROCESSOR_OBJECT_HID, 0}, @@ -95,295 +70,27 @@ static const struct acpi_device_id processor_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, processor_device_ids); -static struct acpi_driver acpi_processor_driver = { +static struct device_driver acpi_processor_driver = { .name = "processor", - .class = ACPI_PROCESSOR_CLASS, - .ids = processor_device_ids, - .ops = { - .add = acpi_processor_add, - .remove = acpi_processor_remove, - .notify = acpi_processor_notify, - }, + .bus = &cpu_subsys, + .acpi_match_table = processor_device_ids, + .probe = acpi_processor_start, + .remove = acpi_processor_stop, }; -#define INSTALL_NOTIFY_HANDLER 1 -#define UNINSTALL_NOTIFY_HANDLER 2 - DEFINE_PER_CPU(struct acpi_processor *, processors); EXPORT_PER_CPU_SYMBOL(processors); -struct acpi_processor_errata errata __read_mostly; - -/* -------------------------------------------------------------------------- - Errata Handling - -------------------------------------------------------------------------- */ - -static int acpi_processor_errata_piix4(struct pci_dev *dev) +static void acpi_processor_notify(acpi_handle handle, u32 event, void *data) { - u8 value1 = 0; - u8 value2 = 0; - - - if (!dev) - return -EINVAL; - - /* - * Note that 'dev' references the PIIX4 ACPI Controller. - */ - - switch (dev->revision) { - case 0: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found PIIX4 A-step\n")); - break; - case 1: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found PIIX4 B-step\n")); - break; - case 2: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found PIIX4E\n")); - break; - case 3: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found PIIX4M\n")); - break; - default: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found unknown PIIX4\n")); - break; - } - - switch (dev->revision) { - - case 0: /* PIIX4 A-step */ - case 1: /* PIIX4 B-step */ - /* - * See specification changes #13 ("Manual Throttle Duty Cycle") - * and #14 ("Enabling and Disabling Manual Throttle"), plus - * erratum #5 ("STPCLK# Deassertion Time") from the January - * 2002 PIIX4 specification update. Applies to only older - * PIIX4 models. - */ - errata.piix4.throttle = 1; - - case 2: /* PIIX4E */ - case 3: /* PIIX4M */ - /* - * See erratum #18 ("C3 Power State/BMIDE and Type-F DMA - * Livelock") from the January 2002 PIIX4 specification update. - * Applies to all PIIX4 models. - */ - - /* - * BM-IDE - * ------ - * Find the PIIX4 IDE Controller and get the Bus Master IDE - * Status register address. We'll use this later to read - * each IDE controller's DMA status to make sure we catch all - * DMA activity. - */ - dev = pci_get_subsys(PCI_VENDOR_ID_INTEL, - PCI_DEVICE_ID_INTEL_82371AB, - PCI_ANY_ID, PCI_ANY_ID, NULL); - if (dev) { - errata.piix4.bmisx = pci_resource_start(dev, 4); - pci_dev_put(dev); - } - - /* - * Type-F DMA - * ---------- - * Find the PIIX4 ISA Controller and read the Motherboard - * DMA controller's status to see if Type-F (Fast) DMA mode - * is enabled (bit 7) on either channel. Note that we'll - * disable C3 support if this is enabled, as some legacy - * devices won't operate well if fast DMA is disabled. - */ - dev = pci_get_subsys(PCI_VENDOR_ID_INTEL, - PCI_DEVICE_ID_INTEL_82371AB_0, - PCI_ANY_ID, PCI_ANY_ID, NULL); - if (dev) { - pci_read_config_byte(dev, 0x76, &value1); - pci_read_config_byte(dev, 0x77, &value2); - if ((value1 & 0x80) || (value2 & 0x80)) - errata.piix4.fdma = 1; - pci_dev_put(dev); - } - - break; - } - - if (errata.piix4.bmisx) - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Bus master activity detection (BM-IDE) erratum enabled\n")); - if (errata.piix4.fdma) - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Type-F DMA livelock erratum (C3 disabled)\n")); - - return 0; -} - -static int acpi_processor_errata(struct acpi_processor *pr) -{ - int result = 0; - struct pci_dev *dev = NULL; - - - if (!pr) - return -EINVAL; - - /* - * PIIX4 - */ - dev = pci_get_subsys(PCI_VENDOR_ID_INTEL, - PCI_DEVICE_ID_INTEL_82371AB_3, PCI_ANY_ID, - PCI_ANY_ID, NULL); - if (dev) { - result = acpi_processor_errata_piix4(dev); - pci_dev_put(dev); - } - - return result; -} - -/* -------------------------------------------------------------------------- - Driver Interface - -------------------------------------------------------------------------- */ - -static int acpi_processor_get_info(struct acpi_device *device) -{ - acpi_status status = 0; - union acpi_object object = { 0 }; - struct acpi_buffer buffer = { sizeof(union acpi_object), &object }; + struct acpi_device *device = data; struct acpi_processor *pr; - int cpu_index, device_declaration = 0; - static int cpu0_initialized; - - pr = acpi_driver_data(device); - if (!pr) - return -EINVAL; - - if (num_online_cpus() > 1) - errata.smp = TRUE; - - acpi_processor_errata(pr); - - /* - * Check to see if we have bus mastering arbitration control. This - * is required for proper C3 usage (to maintain cache coherency). - */ - if (acpi_gbl_FADT.pm2_control_block && acpi_gbl_FADT.pm2_control_length) { - pr->flags.bm_control = 1; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Bus mastering arbitration control present\n")); - } else - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "No bus mastering arbitration control\n")); - - if (!strcmp(acpi_device_hid(device), ACPI_PROCESSOR_OBJECT_HID)) { - /* Declared with "Processor" statement; match ProcessorID */ - status = acpi_evaluate_object(pr->handle, NULL, NULL, &buffer); - if (ACPI_FAILURE(status)) { - dev_err(&device->dev, - "Failed to evaluate processor object (0x%x)\n", - status); - return -ENODEV; - } - - /* - * TBD: Synch processor ID (via LAPIC/LSAPIC structures) on SMP. - * >>> 'acpi_get_processor_id(acpi_id, &id)' in - * arch/xxx/acpi.c - */ - pr->acpi_id = object.processor.proc_id; - } else { - /* - * Declared with "Device" statement; match _UID. - * Note that we don't handle string _UIDs yet. - */ - unsigned long long value; - status = acpi_evaluate_integer(pr->handle, METHOD_NAME__UID, - NULL, &value); - if (ACPI_FAILURE(status)) { - dev_err(&device->dev, - "Failed to evaluate processor _UID (0x%x)\n", - status); - return -ENODEV; - } - device_declaration = 1; - pr->acpi_id = value; - } - cpu_index = acpi_get_cpuid(pr->handle, device_declaration, pr->acpi_id); - - /* Handle UP system running SMP kernel, with no LAPIC in MADT */ - if (!cpu0_initialized && (cpu_index == -1) && - (num_online_cpus() == 1)) { - cpu_index = 0; - } - - cpu0_initialized = 1; - - pr->id = cpu_index; - - /* - * Extra Processor objects may be enumerated on MP systems with - * less than the max # of CPUs. They should be ignored _iff - * they are physically not present. - */ - if (pr->id == -1) { - if (ACPI_FAILURE(acpi_processor_hotadd_init(pr))) - return -ENODEV; - } - /* - * On some boxes several processors use the same processor bus id. - * But they are located in different scope. For example: - * \_SB.SCK0.CPU0 - * \_SB.SCK1.CPU0 - * Rename the processor device bus id. And the new bus id will be - * generated as the following format: - * CPU+CPU ID. - */ - sprintf(acpi_device_bid(device), "CPU%X", pr->id); - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Processor [%d:%d]\n", pr->id, - pr->acpi_id)); - - if (!object.processor.pblk_address) - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No PBLK (NULL address)\n")); - else if (object.processor.pblk_length != 6) - dev_err(&device->dev, "Invalid PBLK length [%d]\n", - object.processor.pblk_length); - else { - pr->throttling.address = object.processor.pblk_address; - pr->throttling.duty_offset = acpi_gbl_FADT.duty_offset; - pr->throttling.duty_width = acpi_gbl_FADT.duty_width; - - pr->pblk = object.processor.pblk_address; - - /* - * We don't care about error returns - we just try to mark - * these reserved so that nobody else is confused into thinking - * that this region might be unused.. - * - * (In particular, allocating the IO range for Cardbus) - */ - request_region(pr->throttling.address, 6, "ACPI CPU throttle"); - } - - /* - * If ACPI describes a slot number for this CPU, we can use it - * ensure we get the right value in the "physical id" field - * of /proc/cpuinfo - */ - status = acpi_evaluate_object(pr->handle, "_SUN", NULL, &buffer); - if (ACPI_SUCCESS(status)) - arch_fix_phys_package_id(pr->id, object.integer.value); - - return 0; -} - -static DEFINE_PER_CPU(void *, processor_device_array); - -static void acpi_processor_notify(struct acpi_device *device, u32 event) -{ - struct acpi_processor *pr = acpi_driver_data(device); int saved; + if (device->handle != handle) + return; + + pr = acpi_driver_data(device); if (!pr) return; @@ -420,55 +127,62 @@ static void acpi_processor_notify(struct acpi_device *device, u32 event) return; } -static int acpi_cpu_soft_notify(struct notifier_block *nfb, - unsigned long action, void *hcpu) +static __cpuinit int __acpi_processor_start(struct acpi_device *device); + +static int __cpuinit acpi_cpu_soft_notify(struct notifier_block *nfb, + unsigned long action, void *hcpu) { unsigned int cpu = (unsigned long)hcpu; struct acpi_processor *pr = per_cpu(processors, cpu); + struct acpi_device *device; - if (action == CPU_ONLINE && pr) { - /* CPU got physically hotplugged and onlined the first time: - * Initialize missing things + if (!pr || acpi_bus_get_device(pr->handle, &device)) + return NOTIFY_DONE; + + if (action == CPU_ONLINE) { + /* + * CPU got physically hotplugged and onlined for the first time: + * Initialize missing things. */ if (pr->flags.need_hotplug_init) { + int ret; + pr_info("Will online and init hotplugged CPU: %d\n", pr->id); - WARN(acpi_processor_start(pr), "Failed to start CPU:" - " %d\n", pr->id); pr->flags.need_hotplug_init = 0; - /* Normal CPU soft online event */ + ret = __acpi_processor_start(device); + WARN(ret, "Failed to start CPU: %d\n", pr->id); } else { + /* Normal CPU soft online event. */ acpi_processor_ppc_has_changed(pr, 0); acpi_processor_hotplug(pr); acpi_processor_reevaluate_tstate(pr, action); acpi_processor_tstate_has_changed(pr); } - } - if (action == CPU_DEAD && pr) { - /* invalidate the flag.throttling after one CPU is offline */ + } else if (action == CPU_DEAD) { + /* Invalidate flag.throttling after the CPU is offline. */ acpi_processor_reevaluate_tstate(pr, action); } return NOTIFY_OK; } -static struct notifier_block acpi_cpu_notifier = +static struct notifier_block __refdata acpi_cpu_notifier = { .notifier_call = acpi_cpu_soft_notify, }; -/* - * acpi_processor_start() is called by the cpu_hotplug_notifier func: - * acpi_cpu_soft_notify(). Getting it __cpuinit{data} is difficult, the - * root cause seem to be that acpi_processor_uninstall_hotplug_notify() - * is in the module_exit (__exit) func. Allowing acpi_processor_start() - * to not be in __cpuinit section, but being called from __cpuinit funcs - * via __ref looks like the right thing to do here. - */ -static __ref int acpi_processor_start(struct acpi_processor *pr) +static __cpuinit int __acpi_processor_start(struct acpi_device *device) { - struct acpi_device *device = per_cpu(processor_device_array, pr->id); + struct acpi_processor *pr = acpi_driver_data(device); + acpi_status status; int result = 0; + if (!pr) + return -ENODEV; + + if (pr->flags.need_hotplug_init) + return 0; + #ifdef CONFIG_CPU_FREQ acpi_processor_ppc_has_changed(pr, 0); acpi_processor_load_module(pr); @@ -506,129 +220,48 @@ static __ref int acpi_processor_start(struct acpi_processor *pr) goto err_remove_sysfs_thermal; } - return 0; + status = acpi_install_notify_handler(device->handle, ACPI_DEVICE_NOTIFY, + acpi_processor_notify, device); + if (ACPI_SUCCESS(status)) + return 0; -err_remove_sysfs_thermal: + sysfs_remove_link(&pr->cdev->device.kobj, "device"); + err_remove_sysfs_thermal: sysfs_remove_link(&device->dev.kobj, "thermal_cooling"); -err_thermal_unregister: + err_thermal_unregister: thermal_cooling_device_unregister(pr->cdev); -err_power_exit: + err_power_exit: acpi_processor_power_exit(pr); - return result; } -/* - * Do not put anything in here which needs the core to be online. - * For example MSR access or setting up things which check for cpuinfo_x86 - * (cpu_data(cpu)) values, like CPU feature flags, family, model, etc. - * Such things have to be put in and set up above in acpi_processor_start() - */ -static int __cpuinit acpi_processor_add(struct acpi_device *device) +static int __cpuinit acpi_processor_start(struct device *dev) { - struct acpi_processor *pr = NULL; - int result = 0; - struct device *dev; - - pr = kzalloc(sizeof(struct acpi_processor), GFP_KERNEL); - if (!pr) - return -ENOMEM; + struct acpi_device *device; - if (!zalloc_cpumask_var(&pr->throttling.shared_cpu_map, GFP_KERNEL)) { - result = -ENOMEM; - goto err_free_pr; - } - - pr->handle = device->handle; - strcpy(acpi_device_name(device), ACPI_PROCESSOR_DEVICE_NAME); - strcpy(acpi_device_class(device), ACPI_PROCESSOR_CLASS); - device->driver_data = pr; - - result = acpi_processor_get_info(device); - if (result) { - /* Processor is physically not present */ - return 0; - } - -#ifdef CONFIG_SMP - if (pr->id >= setup_max_cpus && pr->id != 0) - return 0; -#endif - - BUG_ON(pr->id >= nr_cpu_ids); - - /* - * Buggy BIOS check - * ACPI id of processors can be reported wrongly by the BIOS. - * Don't trust it blindly - */ - if (per_cpu(processor_device_array, pr->id) != NULL && - per_cpu(processor_device_array, pr->id) != device) { - dev_warn(&device->dev, - "BIOS reported wrong ACPI id %d for the processor\n", - pr->id); - result = -ENODEV; - goto err_free_cpumask; - } - per_cpu(processor_device_array, pr->id) = device; - - per_cpu(processors, pr->id) = pr; + if (acpi_bus_get_device(ACPI_HANDLE(dev), &device)) + return -ENODEV; - dev = get_cpu_device(pr->id); - if (sysfs_create_link(&device->dev.kobj, &dev->kobj, "sysdev")) { - result = -EFAULT; - goto err_clear_processor; - } - - /* - * Do not start hotplugged CPUs now, but when they - * are onlined the first time - */ - if (pr->flags.need_hotplug_init) - return 0; - - result = acpi_processor_start(pr); - if (result) - goto err_remove_sysfs; - - return 0; - -err_remove_sysfs: - sysfs_remove_link(&device->dev.kobj, "sysdev"); -err_clear_processor: - /* - * processor_device_array is not cleared to allow checks for buggy BIOS - */ - per_cpu(processors, pr->id) = NULL; -err_free_cpumask: - free_cpumask_var(pr->throttling.shared_cpu_map); -err_free_pr: - kfree(pr); - return result; + return __acpi_processor_start(device); } -static int acpi_processor_remove(struct acpi_device *device) +static int acpi_processor_stop(struct device *dev) { - struct acpi_processor *pr = NULL; + struct acpi_device *device; + struct acpi_processor *pr; + if (acpi_bus_get_device(ACPI_HANDLE(dev), &device)) + return 0; - if (!device || !acpi_driver_data(device)) - return -EINVAL; + acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY, + acpi_processor_notify); pr = acpi_driver_data(device); - - if (pr->id >= nr_cpu_ids) - goto free; - - if (device->removal_type == ACPI_BUS_REMOVAL_EJECT) { - if (acpi_processor_handle_eject(pr)) - return -EINVAL; - } + if (!pr) + return 0; acpi_processor_power_exit(pr); - sysfs_remove_link(&device->dev.kobj, "sysdev"); - if (pr->cdev) { sysfs_remove_link(&device->dev.kobj, "thermal_cooling"); sysfs_remove_link(&pr->cdev->device.kobj, "device"); @@ -637,331 +270,47 @@ static int acpi_processor_remove(struct acpi_device *device) } per_cpu(processors, pr->id) = NULL; - per_cpu(processor_device_array, pr->id) = NULL; - try_offline_node(cpu_to_node(pr->id)); - -free: - free_cpumask_var(pr->throttling.shared_cpu_map); - kfree(pr); - return 0; } -#ifdef CONFIG_ACPI_HOTPLUG_CPU -/**************************************************************************** - * Acpi processor hotplug support * - ****************************************************************************/ - -static int is_processor_present(acpi_handle handle) -{ - acpi_status status; - unsigned long long sta = 0; - - - status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); - - if (ACPI_SUCCESS(status) && (sta & ACPI_STA_DEVICE_PRESENT)) - return 1; - - /* - * _STA is mandatory for a processor that supports hot plug - */ - if (status == AE_NOT_FOUND) - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Processor does not support hot plug\n")); - else - ACPI_EXCEPTION((AE_INFO, status, - "Processor Device is not present")); - return 0; -} - -static void acpi_processor_hotplug_notify(acpi_handle handle, - u32 event, void *data) -{ - struct acpi_device *device = NULL; - struct acpi_eject_event *ej_event = NULL; - u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; /* default */ - acpi_status status; - int result; - - acpi_scan_lock_acquire(); - - switch (event) { - case ACPI_NOTIFY_BUS_CHECK: - case ACPI_NOTIFY_DEVICE_CHECK: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Processor driver received %s event\n", - (event == ACPI_NOTIFY_BUS_CHECK) ? - "ACPI_NOTIFY_BUS_CHECK" : "ACPI_NOTIFY_DEVICE_CHECK")); - - if (!is_processor_present(handle)) - break; - - if (!acpi_bus_get_device(handle, &device)) - break; - - result = acpi_bus_scan(handle); - if (result) { - acpi_handle_err(handle, "Unable to add the device\n"); - break; - } - result = acpi_bus_get_device(handle, &device); - if (result) { - acpi_handle_err(handle, "Missing device object\n"); - break; - } - ost_code = ACPI_OST_SC_SUCCESS; - break; - - case ACPI_NOTIFY_EJECT_REQUEST: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "received ACPI_NOTIFY_EJECT_REQUEST\n")); - - if (acpi_bus_get_device(handle, &device)) { - acpi_handle_err(handle, - "Device don't exist, dropping EJECT\n"); - break; - } - if (!acpi_driver_data(device)) { - acpi_handle_err(handle, - "Driver data is NULL, dropping EJECT\n"); - break; - } - - ej_event = kmalloc(sizeof(*ej_event), GFP_KERNEL); - if (!ej_event) { - acpi_handle_err(handle, "No memory, dropping EJECT\n"); - break; - } - - get_device(&device->dev); - ej_event->device = device; - ej_event->event = ACPI_NOTIFY_EJECT_REQUEST; - /* The eject is carried out asynchronously. */ - status = acpi_os_hotplug_execute(acpi_bus_hot_remove_device, - ej_event); - if (ACPI_FAILURE(status)) { - put_device(&device->dev); - kfree(ej_event); - break; - } - goto out; - - default: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Unsupported event [0x%x]\n", event)); - - /* non-hotplug event; possibly handled by other handler */ - goto out; - } - - /* Inform firmware that the hotplug operation has completed */ - (void) acpi_evaluate_hotplug_ost(handle, event, ost_code, NULL); - - out: - acpi_scan_lock_release(); -} - -static acpi_status is_processor_device(acpi_handle handle) -{ - struct acpi_device_info *info; - char *hid; - acpi_status status; - - status = acpi_get_object_info(handle, &info); - if (ACPI_FAILURE(status)) - return status; - - if (info->type == ACPI_TYPE_PROCESSOR) { - kfree(info); - return AE_OK; /* found a processor object */ - } - - if (!(info->valid & ACPI_VALID_HID)) { - kfree(info); - return AE_ERROR; - } - - hid = info->hardware_id.string; - if ((hid == NULL) || strcmp(hid, ACPI_PROCESSOR_DEVICE_HID)) { - kfree(info); - return AE_ERROR; - } - - kfree(info); - return AE_OK; /* found a processor device object */ -} - -static acpi_status -processor_walk_namespace_cb(acpi_handle handle, - u32 lvl, void *context, void **rv) -{ - acpi_status status; - int *action = context; - - status = is_processor_device(handle); - if (ACPI_FAILURE(status)) - return AE_OK; /* not a processor; continue to walk */ - - switch (*action) { - case INSTALL_NOTIFY_HANDLER: - acpi_install_notify_handler(handle, - ACPI_SYSTEM_NOTIFY, - acpi_processor_hotplug_notify, - NULL); - break; - case UNINSTALL_NOTIFY_HANDLER: - acpi_remove_notify_handler(handle, - ACPI_SYSTEM_NOTIFY, - acpi_processor_hotplug_notify); - break; - default: - break; - } - - /* found a processor; skip walking underneath */ - return AE_CTRL_DEPTH; -} - -static acpi_status acpi_processor_hotadd_init(struct acpi_processor *pr) -{ - acpi_handle handle = pr->handle; - - if (!is_processor_present(handle)) { - return AE_ERROR; - } - - if (acpi_map_lsapic(handle, &pr->id)) - return AE_ERROR; - - if (arch_register_cpu(pr->id)) { - acpi_unmap_lsapic(pr->id); - return AE_ERROR; - } - - /* CPU got hot-plugged, but cpu_data is not initialized yet - * Set flag to delay cpu_idle/throttling initialization - * in: - * acpi_processor_add() - * acpi_processor_get_info() - * and do it when the CPU gets online the first time - * TBD: Cleanup above functions and try to do this more elegant. - */ - pr_info("CPU %d got hotplugged\n", pr->id); - pr->flags.need_hotplug_init = 1; - - return AE_OK; -} - -static int acpi_processor_handle_eject(struct acpi_processor *pr) -{ - if (cpu_online(pr->id)) - cpu_down(pr->id); - - get_online_cpus(); - /* - * The cpu might become online again at this point. So we check whether - * the cpu has been onlined or not. If the cpu became online, it means - * that someone wants to use the cpu. So acpi_processor_handle_eject() - * returns -EAGAIN. - */ - if (unlikely(cpu_online(pr->id))) { - put_online_cpus(); - pr_warn("Failed to remove CPU %d, because other task " - "brought the CPU back online\n", pr->id); - return -EAGAIN; - } - arch_unregister_cpu(pr->id); - acpi_unmap_lsapic(pr->id); - put_online_cpus(); - return (0); -} -#else -static acpi_status acpi_processor_hotadd_init(struct acpi_processor *pr) -{ - return AE_ERROR; -} -static int acpi_processor_handle_eject(struct acpi_processor *pr) -{ - return (-EINVAL); -} -#endif - -static -void acpi_processor_install_hotplug_notify(void) -{ -#ifdef CONFIG_ACPI_HOTPLUG_CPU - int action = INSTALL_NOTIFY_HANDLER; - acpi_walk_namespace(ACPI_TYPE_ANY, - ACPI_ROOT_OBJECT, - ACPI_UINT32_MAX, - processor_walk_namespace_cb, NULL, &action, NULL); -#endif - register_hotcpu_notifier(&acpi_cpu_notifier); -} - -static -void acpi_processor_uninstall_hotplug_notify(void) -{ -#ifdef CONFIG_ACPI_HOTPLUG_CPU - int action = UNINSTALL_NOTIFY_HANDLER; - acpi_walk_namespace(ACPI_TYPE_ANY, - ACPI_ROOT_OBJECT, - ACPI_UINT32_MAX, - processor_walk_namespace_cb, NULL, &action, NULL); -#endif - unregister_hotcpu_notifier(&acpi_cpu_notifier); -} - /* * We keep the driver loaded even when ACPI is not running. * This is needed for the powernow-k8 driver, that works even without * ACPI, but needs symbols from this driver */ -static int __init acpi_processor_init(void) +static int __init acpi_processor_driver_init(void) { int result = 0; if (acpi_disabled) return 0; - result = acpi_bus_register_driver(&acpi_processor_driver); + result = driver_register(&acpi_processor_driver); if (result < 0) return result; acpi_processor_syscore_init(); - - acpi_processor_install_hotplug_notify(); - + register_hotcpu_notifier(&acpi_cpu_notifier); acpi_thermal_cpufreq_init(); - acpi_processor_ppc_init(); - acpi_processor_throttling_init(); - return 0; } -static void __exit acpi_processor_exit(void) +static void __exit acpi_processor_driver_exit(void) { if (acpi_disabled) return; acpi_processor_ppc_exit(); - acpi_thermal_cpufreq_exit(); - - acpi_processor_uninstall_hotplug_notify(); - + unregister_hotcpu_notifier(&acpi_cpu_notifier); acpi_processor_syscore_exit(); - - acpi_bus_unregister_driver(&acpi_processor_driver); - - return; + driver_unregister(&acpi_processor_driver); } -module_init(acpi_processor_init); -module_exit(acpi_processor_exit); +module_init(acpi_processor_driver_init); +module_exit(acpi_processor_driver_exit); MODULE_ALIAS("processor"); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 4fd392005ef1..ad82bb2a37e0 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -2124,6 +2124,7 @@ int __init acpi_scan_init(void) acpi_pci_root_init(); acpi_pci_link_init(); + acpi_processor_init(); acpi_platform_init(); acpi_lpss_init(); acpi_csrt_init(); diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 25c8768172e9..7431ba6fc2d4 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -13,11 +13,21 @@ #include #include #include +#include #include "base.h" static DEFINE_PER_CPU(struct device *, cpu_sys_devices); +static int cpu_subsys_match(struct device *dev, struct device_driver *drv) +{ + /* ACPI style match is the only one that may succeed. */ + if (acpi_driver_match_device(dev, drv)) + return 1; + + return 0; +} + #ifdef CONFIG_HOTPLUG_CPU static void change_cpu_under_node(struct cpu *cpu, unsigned int from_nid, unsigned int to_nid) @@ -98,6 +108,7 @@ static DEVICE_ATTR(release, S_IWUSR, NULL, cpu_release_store); struct bus_type cpu_subsys = { .name = "cpu", .dev_name = "cpu", + .match = cpu_subsys_match, #ifdef CONFIG_HOTPLUG_CPU .online = cpu_subsys_online, .offline = cpu_subsys_offline, diff --git a/include/acpi/processor.h b/include/acpi/processor.h index ea69367fdd3b..66096d06925e 100644 --- a/include/acpi/processor.h +++ b/include/acpi/processor.h @@ -6,6 +6,10 @@ #include #include +#define ACPI_PROCESSOR_CLASS "processor" +#define ACPI_PROCESSOR_DEVICE_NAME "Processor" +#define ACPI_PROCESSOR_DEVICE_HID "ACPI0007" + #define ACPI_PROCESSOR_BUSY_METRIC 10 #define ACPI_PROCESSOR_MAX_POWER 8 @@ -207,6 +211,7 @@ struct acpi_processor { struct acpi_processor_throttling throttling; struct acpi_processor_limit limit; struct thermal_cooling_device *cdev; + struct device *dev; /* Processor device. */ }; struct acpi_processor_errata { -- cgit v1.2.3 From e2ff39400d81233374e780b133496a2296643d7d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 8 May 2013 00:29:49 +0200 Subject: ACPI / memhotplug: Bind removable memory blocks to ACPI device nodes During ACPI memory hotplug configuration bind memory blocks residing in modules removable through the standard ACPI mechanism to struct acpi_device objects associated with ACPI namespace objects representing those modules. Accordingly, unbind those memory blocks from the struct acpi_device objects when the memory modules in question are being removed. When "offline" operation for devices representing memory blocks is introduced, this will allow the ACPI core's device hot-remove code to use it to carry out remove_memory() for those memory blocks and check the results of that before it actually removes the modules holding them from the system. Since walk_memory_range() is used for accessing all memory blocks corresponding to a given ACPI namespace object, it is exported from memory_hotplug.c so that the code in acpi_memhotplug.c can use it. Signed-off-by: Rafael J. Wysocki Tested-by: Vasilis Liaskovitis Reviewed-by: Toshi Kani --- drivers/acpi/acpi_memhotplug.c | 53 +++++++++++++++++++++++++++++++++++++++--- include/linux/memory_hotplug.h | 2 ++ mm/memory_hotplug.c | 4 +++- 3 files changed, 55 insertions(+), 4 deletions(-) diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c index 5e6301e94920..5590db12028e 100644 --- a/drivers/acpi/acpi_memhotplug.c +++ b/drivers/acpi/acpi_memhotplug.c @@ -28,6 +28,7 @@ */ #include +#include #include #include "internal.h" @@ -166,13 +167,50 @@ static int acpi_memory_check_device(struct acpi_memory_device *mem_device) return 0; } +static unsigned long acpi_meminfo_start_pfn(struct acpi_memory_info *info) +{ + return PFN_DOWN(info->start_addr); +} + +static unsigned long acpi_meminfo_end_pfn(struct acpi_memory_info *info) +{ + return PFN_UP(info->start_addr + info->length-1); +} + +static int acpi_bind_memblk(struct memory_block *mem, void *arg) +{ + return acpi_bind_one(&mem->dev, (acpi_handle)arg); +} + +static int acpi_bind_memory_blocks(struct acpi_memory_info *info, + acpi_handle handle) +{ + return walk_memory_range(acpi_meminfo_start_pfn(info), + acpi_meminfo_end_pfn(info), (void *)handle, + acpi_bind_memblk); +} + +static int acpi_unbind_memblk(struct memory_block *mem, void *arg) +{ + acpi_unbind_one(&mem->dev); + return 0; +} + +static void acpi_unbind_memory_blocks(struct acpi_memory_info *info, + acpi_handle handle) +{ + walk_memory_range(acpi_meminfo_start_pfn(info), + acpi_meminfo_end_pfn(info), NULL, acpi_unbind_memblk); +} + static int acpi_memory_enable_device(struct acpi_memory_device *mem_device) { + acpi_handle handle = mem_device->device->handle; int result, num_enabled = 0; struct acpi_memory_info *info; int node; - node = acpi_get_node(mem_device->device->handle); + node = acpi_get_node(handle); /* * Tell the VM there is more memory here... * Note: Assume that this function returns zero on success @@ -203,6 +241,12 @@ static int acpi_memory_enable_device(struct acpi_memory_device *mem_device) if (result && result != -EEXIST) continue; + result = acpi_bind_memory_blocks(info, handle); + if (result) { + acpi_unbind_memory_blocks(info, handle); + return -ENODEV; + } + info->enabled = 1; /* @@ -229,10 +273,11 @@ static int acpi_memory_enable_device(struct acpi_memory_device *mem_device) static int acpi_memory_remove_memory(struct acpi_memory_device *mem_device) { + acpi_handle handle = mem_device->device->handle; int result = 0, nid; struct acpi_memory_info *info, *n; - nid = acpi_get_node(mem_device->device->handle); + nid = acpi_get_node(handle); list_for_each_entry_safe(info, n, &mem_device->res_list, list) { if (!info->enabled) @@ -240,6 +285,8 @@ static int acpi_memory_remove_memory(struct acpi_memory_device *mem_device) if (nid < 0) nid = memory_add_physaddr_to_nid(info->start_addr); + + acpi_unbind_memory_blocks(info, handle); result = remove_memory(nid, info->start_addr, info->length); if (result) return result; @@ -300,7 +347,7 @@ static int acpi_memory_device_add(struct acpi_device *device, if (result) { dev_err(&device->dev, "acpi_memory_enable_device() error\n"); acpi_memory_device_free(mem_device); - return -ENODEV; + return result; } dev_dbg(&device->dev, "Memory device configured by ACPI\n"); diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h index 3e622c610925..2975b7b2a9d8 100644 --- a/include/linux/memory_hotplug.h +++ b/include/linux/memory_hotplug.h @@ -245,6 +245,8 @@ static inline int is_mem_section_removable(unsigned long pfn, static inline void try_offline_node(int nid) {} #endif /* CONFIG_MEMORY_HOTREMOVE */ +extern int walk_memory_range(unsigned long start_pfn, unsigned long end_pfn, + void *arg, int (*func)(struct memory_block *, void *)); extern int mem_online_node(int nid); extern int add_memory(int nid, u64 start, u64 size); extern int arch_add_memory(int nid, u64 start, u64 size); diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index a221fac1f47d..5ea1287ee91f 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -1618,6 +1618,7 @@ int offline_pages(unsigned long start_pfn, unsigned long nr_pages) { return __offline_pages(start_pfn, start_pfn + nr_pages, 120 * HZ); } +#endif /* CONFIG_MEMORY_HOTREMOVE */ /** * walk_memory_range - walks through all mem sections in [start_pfn, end_pfn) @@ -1631,7 +1632,7 @@ int offline_pages(unsigned long start_pfn, unsigned long nr_pages) * * Returns the return value of func. */ -static int walk_memory_range(unsigned long start_pfn, unsigned long end_pfn, +int walk_memory_range(unsigned long start_pfn, unsigned long end_pfn, void *arg, int (*func)(struct memory_block *, void *)) { struct memory_block *mem = NULL; @@ -1668,6 +1669,7 @@ static int walk_memory_range(unsigned long start_pfn, unsigned long end_pfn, return 0; } +#ifdef CONFIG_MEMORY_HOTREMOVE /** * offline_memory_block_cb - callback function for offlining memory block * @mem: the memory block to be offlined -- cgit v1.2.3 From 4960e05e22604ee270a023f968e0e4f9bd0c6fef Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 8 May 2013 14:18:37 +0200 Subject: Driver core: Introduce offline/online callbacks for memory blocks Introduce .offline() and .online() callbacks for memory_subsys that will allow the generic device_offline() and device_online() to be used with device objects representing memory blocks. That, in turn, allows the ACPI subsystem to use device_offline() to put removable memory blocks offline, if possible, before removing memory modules holding them. The 'online' sysfs attribute of memory block devices will attempt to put them offline if 0 is written to it and will attempt to apply the previously used online type when onlining them (i.e. when 1 is written to it). Signed-off-by: Rafael J. Wysocki Tested-by: Vasilis Liaskovitis Acked-by: Greg Kroah-Hartman Reviewed-by: Toshi Kani --- drivers/base/memory.c | 112 ++++++++++++++++++++++++++++++++++++++----------- include/linux/memory.h | 1 + 2 files changed, 88 insertions(+), 25 deletions(-) diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 14f8a6954da0..c8f3b63fcacd 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -37,9 +37,14 @@ static inline int base_memory_block_id(int section_nr) return section_nr / sections_per_block; } +static int memory_subsys_online(struct device *dev); +static int memory_subsys_offline(struct device *dev); + static struct bus_type memory_subsys = { .name = MEMORY_CLASS_NAME, .dev_name = MEMORY_CLASS_NAME, + .online = memory_subsys_online, + .offline = memory_subsys_offline, }; static BLOCKING_NOTIFIER_HEAD(memory_chain); @@ -88,6 +93,7 @@ int register_memory(struct memory_block *memory) memory->dev.bus = &memory_subsys; memory->dev.id = memory->start_section_nr / sections_per_block; memory->dev.release = memory_block_release; + memory->dev.offline = memory->state == MEM_OFFLINE; error = device_register(&memory->dev); return error; @@ -278,33 +284,70 @@ static int __memory_block_change_state(struct memory_block *mem, { int ret = 0; - if (mem->state != from_state_req) { - ret = -EINVAL; - goto out; - } + if (mem->state != from_state_req) + return -EINVAL; if (to_state == MEM_OFFLINE) mem->state = MEM_GOING_OFFLINE; ret = memory_block_action(mem->start_section_nr, to_state, online_type); - if (ret) { mem->state = from_state_req; - goto out; + } else { + mem->state = to_state; + if (to_state == MEM_ONLINE) + mem->last_online = online_type; } + return ret; +} - mem->state = to_state; - switch (mem->state) { - case MEM_OFFLINE: - kobject_uevent(&mem->dev.kobj, KOBJ_OFFLINE); - break; - case MEM_ONLINE: - kobject_uevent(&mem->dev.kobj, KOBJ_ONLINE); - break; - default: - break; +static int memory_subsys_online(struct device *dev) +{ + struct memory_block *mem = container_of(dev, struct memory_block, dev); + int ret; + + mutex_lock(&mem->state_mutex); + + ret = mem->state == MEM_ONLINE ? 0 : + __memory_block_change_state(mem, MEM_ONLINE, MEM_OFFLINE, + mem->last_online); + + mutex_unlock(&mem->state_mutex); + return ret; +} + +static int memory_subsys_offline(struct device *dev) +{ + struct memory_block *mem = container_of(dev, struct memory_block, dev); + int ret; + + mutex_lock(&mem->state_mutex); + + ret = mem->state == MEM_OFFLINE ? 0 : + __memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE, -1); + + mutex_unlock(&mem->state_mutex); + return ret; +} + +static int __memory_block_change_state_uevent(struct memory_block *mem, + unsigned long to_state, unsigned long from_state_req, + int online_type) +{ + int ret = __memory_block_change_state(mem, to_state, from_state_req, + online_type); + if (!ret) { + switch (mem->state) { + case MEM_OFFLINE: + kobject_uevent(&mem->dev.kobj, KOBJ_OFFLINE); + break; + case MEM_ONLINE: + kobject_uevent(&mem->dev.kobj, KOBJ_ONLINE); + break; + default: + break; + } } -out: return ret; } @@ -315,8 +358,8 @@ static int memory_block_change_state(struct memory_block *mem, int ret; mutex_lock(&mem->state_mutex); - ret = __memory_block_change_state(mem, to_state, from_state_req, - online_type); + ret = __memory_block_change_state_uevent(mem, to_state, from_state_req, + online_type); mutex_unlock(&mem->state_mutex); return ret; @@ -326,22 +369,34 @@ store_mem_state(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct memory_block *mem; + bool offline; int ret = -EINVAL; mem = container_of(dev, struct memory_block, dev); - if (!strncmp(buf, "online_kernel", min_t(int, count, 13))) + lock_device_hotplug(); + + if (!strncmp(buf, "online_kernel", min_t(int, count, 13))) { + offline = false; ret = memory_block_change_state(mem, MEM_ONLINE, MEM_OFFLINE, ONLINE_KERNEL); - else if (!strncmp(buf, "online_movable", min_t(int, count, 14))) + } else if (!strncmp(buf, "online_movable", min_t(int, count, 14))) { + offline = false; ret = memory_block_change_state(mem, MEM_ONLINE, MEM_OFFLINE, ONLINE_MOVABLE); - else if (!strncmp(buf, "online", min_t(int, count, 6))) + } else if (!strncmp(buf, "online", min_t(int, count, 6))) { + offline = false; ret = memory_block_change_state(mem, MEM_ONLINE, MEM_OFFLINE, ONLINE_KEEP); - else if(!strncmp(buf, "offline", min_t(int, count, 7))) + } else if(!strncmp(buf, "offline", min_t(int, count, 7))) { + offline = true; ret = memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE, -1); + } + if (!ret) + dev->offline = offline; + + unlock_device_hotplug(); if (ret) return ret; @@ -563,6 +618,7 @@ static int init_memory_block(struct memory_block **memory, base_memory_block_id(scn_nr) * sections_per_block; mem->end_section_nr = mem->start_section_nr + sections_per_block - 1; mem->state = state; + mem->last_online = ONLINE_KEEP; mem->section_count++; mutex_init(&mem->state_mutex); start_pfn = section_nr_to_pfn(mem->start_section_nr); @@ -681,14 +737,20 @@ int unregister_memory_section(struct mem_section *section) /* * offline one memory block. If the memory block has been offlined, do nothing. + * + * Call under device_hotplug_lock. */ int offline_memory_block(struct memory_block *mem) { int ret = 0; mutex_lock(&mem->state_mutex); - if (mem->state != MEM_OFFLINE) - ret = __memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE, -1); + if (mem->state != MEM_OFFLINE) { + ret = __memory_block_change_state_uevent(mem, MEM_OFFLINE, + MEM_ONLINE, -1); + if (!ret) + mem->dev.offline = true; + } mutex_unlock(&mem->state_mutex); return ret; diff --git a/include/linux/memory.h b/include/linux/memory.h index 85c31a8e2904..3d5346583022 100644 --- a/include/linux/memory.h +++ b/include/linux/memory.h @@ -26,6 +26,7 @@ struct memory_block { unsigned long start_section_nr; unsigned long end_section_nr; unsigned long state; + int last_online; int section_count; /* -- cgit v1.2.3 From 416ad3c9c0066405b83ec875b75496523549be09 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 6 May 2013 23:50:06 +0000 Subject: freezer: add unsafe versions of freezable helpers for NFS NFS calls the freezable helpers with locks held, which is unsafe and will cause lockdep warnings when 6aa9707 "lockdep: check that no locks held at freeze time" is reapplied (it was reverted in dbf520a). NFS shouldn't be doing this, but it has long-running syscalls that must hold a lock but also shouldn't block suspend. Until NFS freeze handling is rewritten to use a signal to exit out of the critical section, add new *_unsafe versions of the helpers that will not run the lockdep test when 6aa9707 is reapplied, and call them from NFS. In practice the likley result of holding the lock while freezing is that a second task blocked on the lock will never freeze, aborting suspend, but it is possible to manufacture a case using the cgroup freezer, the lock, and the suspend freezer to create a deadlock. Silencing the lockdep warning here will allow problems to be found in other drivers that may have a more serious deadlock risk, and prevent new problems from being added. Signed-off-by: Colin Cross Acked-by: Pavel Machek Acked-by: Tejun Heo Signed-off-by: Rafael J. Wysocki --- fs/nfs/inode.c | 2 +- fs/nfs/nfs3proc.c | 2 +- fs/nfs/nfs4proc.c | 4 ++-- include/linux/freezer.h | 42 +++++++++++++++++++++++++++++++++++++++++- net/sunrpc/sched.c | 2 +- 5 files changed, 46 insertions(+), 6 deletions(-) diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index c1c7a9d78722..ce727047ee87 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -79,7 +79,7 @@ int nfs_wait_bit_killable(void *word) { if (fatal_signal_pending(current)) return -ERESTARTSYS; - freezable_schedule(); + freezable_schedule_unsafe(); return 0; } EXPORT_SYMBOL_GPL(nfs_wait_bit_killable); diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 43ea96ced28c..ce90eb4775c2 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -33,7 +33,7 @@ nfs3_rpc_wrapper(struct rpc_clnt *clnt, struct rpc_message *msg, int flags) res = rpc_call_sync(clnt, msg, flags); if (res != -EJUKEBOX) break; - freezable_schedule_timeout_killable(NFS_JUKEBOX_RETRY_TIME); + freezable_schedule_timeout_killable_unsafe(NFS_JUKEBOX_RETRY_TIME); res = -ERESTARTSYS; } while (!fatal_signal_pending(current)); return res; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 8fbc10054115..9b18af167815 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -268,7 +268,7 @@ static int nfs4_delay(struct rpc_clnt *clnt, long *timeout) *timeout = NFS4_POLL_RETRY_MIN; if (*timeout > NFS4_POLL_RETRY_MAX) *timeout = NFS4_POLL_RETRY_MAX; - freezable_schedule_timeout_killable(*timeout); + freezable_schedule_timeout_killable_unsafe(*timeout); if (fatal_signal_pending(current)) res = -ERESTARTSYS; *timeout <<= 1; @@ -4528,7 +4528,7 @@ int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4 static unsigned long nfs4_set_lock_task_retry(unsigned long timeout) { - freezable_schedule_timeout_killable(timeout); + freezable_schedule_timeout_killable_unsafe(timeout); timeout <<= 1; if (timeout > NFS4_LOCK_MAXTIMEOUT) return NFS4_LOCK_MAXTIMEOUT; diff --git a/include/linux/freezer.h b/include/linux/freezer.h index e70df40d84f6..5b31e21c485f 100644 --- a/include/linux/freezer.h +++ b/include/linux/freezer.h @@ -46,7 +46,11 @@ extern int freeze_kernel_threads(void); extern void thaw_processes(void); extern void thaw_kernel_threads(void); -static inline bool try_to_freeze(void) +/* + * DO NOT ADD ANY NEW CALLERS OF THIS FUNCTION + * If try_to_freeze causes a lockdep warning it means the caller may deadlock + */ +static inline bool try_to_freeze_unsafe(void) { might_sleep(); if (likely(!freezing(current))) @@ -54,6 +58,11 @@ static inline bool try_to_freeze(void) return __refrigerator(false); } +static inline bool try_to_freeze(void) +{ + return try_to_freeze_unsafe(); +} + extern bool freeze_task(struct task_struct *p); extern bool set_freezable(void); @@ -115,6 +124,14 @@ static inline void freezer_count(void) try_to_freeze(); } +/* DO NOT ADD ANY NEW CALLERS OF THIS FUNCTION */ +static inline void freezer_count_unsafe(void) +{ + current->flags &= ~PF_FREEZER_SKIP; + smp_mb(); + try_to_freeze_unsafe(); +} + /** * freezer_should_skip - whether to skip a task when determining frozen * state is reached @@ -152,6 +169,14 @@ static inline bool freezer_should_skip(struct task_struct *p) freezer_count(); \ }) +/* DO NOT ADD ANY NEW CALLERS OF THIS FUNCTION */ +#define freezable_schedule_unsafe() \ +({ \ + freezer_do_not_count(); \ + schedule(); \ + freezer_count_unsafe(); \ +}) + /* Like schedule_timeout_killable(), but should not block the freezer. */ #define freezable_schedule_timeout_killable(timeout) \ ({ \ @@ -162,6 +187,16 @@ static inline bool freezer_should_skip(struct task_struct *p) __retval; \ }) +/* DO NOT ADD ANY NEW CALLERS OF THIS FUNCTION */ +#define freezable_schedule_timeout_killable_unsafe(timeout) \ +({ \ + long __retval; \ + freezer_do_not_count(); \ + __retval = schedule_timeout_killable(timeout); \ + freezer_count_unsafe(); \ + __retval; \ +}) + /* * Freezer-friendly wrappers around wait_event_interruptible(), * wait_event_killable() and wait_event_interruptible_timeout(), originally @@ -225,9 +260,14 @@ static inline void set_freezable(void) {} #define freezable_schedule() schedule() +#define freezable_schedule_unsafe() schedule() + #define freezable_schedule_timeout_killable(timeout) \ schedule_timeout_killable(timeout) +#define freezable_schedule_timeout_killable_unsafe(timeout) \ + schedule_timeout_killable(timeout) + #define wait_event_freezable(wq, condition) \ wait_event_interruptible(wq, condition) diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index f8529fc8e542..8dcfadcef5d3 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -254,7 +254,7 @@ static int rpc_wait_bit_killable(void *word) { if (fatal_signal_pending(current)) return -ERESTARTSYS; - freezable_schedule(); + freezable_schedule_unsafe(); return 0; } -- cgit v1.2.3 From 5853cc2a89f726e21d51ca0fd75757a03126a84b Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Tue, 7 May 2013 17:52:05 +0000 Subject: freezer: add unsafe versions of freezable helpers for CIFS CIFS calls wait_event_freezekillable_unsafe with a VFS lock held, which is unsafe and will cause lockdep warnings when 6aa9707 "lockdep: check that no locks held at freeze time" is reapplied (it was reverted in dbf520a). CIFS shouldn't be doing this, but it has long-running syscalls that must hold a lock but also shouldn't block suspend. Until CIFS freeze handling is rewritten to use a signal to exit out of the critical section, add a new wait_event_freezekillable_unsafe helper that will not run the lockdep test when 6aa9707 is reapplied, and call it from CIFS. In practice the likley result of holding the lock while freezing is that a second task blocked on the lock will never freeze, aborting suspend, but it is possible to manufacture a case using the cgroup freezer, the lock, and the suspend freezer to create a deadlock. Silencing the lockdep warning here will allow problems to be found in other drivers that may have a more serious deadlock risk, and prevent new problems from being added. Acked-by: Pavel Machek Acked-by: Tejun Heo Reviewed-by: Jeff Layton Signed-off-by: Colin Cross Signed-off-by: Rafael J. Wysocki --- fs/cifs/transport.c | 2 +- include/linux/freezer.h | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index bfbf4700d160..b70aa7c91394 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -447,7 +447,7 @@ wait_for_response(struct TCP_Server_Info *server, struct mid_q_entry *midQ) { int error; - error = wait_event_freezekillable(server->response_q, + error = wait_event_freezekillable_unsafe(server->response_q, midQ->mid_state != MID_REQUEST_SUBMITTED); if (error < 0) return -ERESTARTSYS; diff --git a/include/linux/freezer.h b/include/linux/freezer.h index 5b31e21c485f..d3c038ec9a88 100644 --- a/include/linux/freezer.h +++ b/include/linux/freezer.h @@ -212,6 +212,16 @@ static inline bool freezer_should_skip(struct task_struct *p) __retval; \ }) +/* DO NOT ADD ANY NEW CALLERS OF THIS FUNCTION */ +#define wait_event_freezekillable_unsafe(wq, condition) \ +({ \ + int __retval; \ + freezer_do_not_count(); \ + __retval = wait_event_killable(wq, (condition)); \ + freezer_count_unsafe(); \ + __retval; \ +}) + #define wait_event_freezable(wq, condition) \ ({ \ int __retval; \ @@ -277,6 +287,9 @@ static inline void set_freezable(void) {} #define wait_event_freezekillable(wq, condition) \ wait_event_killable(wq, condition) +#define wait_event_freezekillable_unsafe(wq, condition) \ + wait_event_killable(wq, condition) + #endif /* !CONFIG_FREEZER */ #endif /* FREEZER_H_INCLUDED */ -- cgit v1.2.3 From 1b1d2fb4444231f25ddabc598aa2b5a9c0833fba Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 6 May 2013 23:50:08 +0000 Subject: lockdep: remove task argument from debug_check_no_locks_held The only existing caller to debug_check_no_locks_held calls it with 'current' as the task, and the freezer needs to call debug_check_no_locks_held but doesn't already have a current task pointer, so remove the argument. It is already assuming that the current task is relevant by dumping the current stack trace as part of the warning. This was originally part of 6aa9707099c (lockdep: check that no locks held at freeze time) which was reverted in dbf520a9d7d4. Original-author: Mandeep Singh Baines Acked-by: Pavel Machek Acked-by: Tejun Heo Signed-off-by: Colin Cross Signed-off-by: Rafael J. Wysocki --- include/linux/debug_locks.h | 4 ++-- kernel/exit.c | 2 +- kernel/lockdep.c | 17 ++++++++--------- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/include/linux/debug_locks.h b/include/linux/debug_locks.h index 21ca773f77bf..822c1354f3a6 100644 --- a/include/linux/debug_locks.h +++ b/include/linux/debug_locks.h @@ -51,7 +51,7 @@ struct task_struct; extern void debug_show_all_locks(void); extern void debug_show_held_locks(struct task_struct *task); extern void debug_check_no_locks_freed(const void *from, unsigned long len); -extern void debug_check_no_locks_held(struct task_struct *task); +extern void debug_check_no_locks_held(void); #else static inline void debug_show_all_locks(void) { @@ -67,7 +67,7 @@ debug_check_no_locks_freed(const void *from, unsigned long len) } static inline void -debug_check_no_locks_held(struct task_struct *task) +debug_check_no_locks_held(void) { } #endif diff --git a/kernel/exit.c b/kernel/exit.c index af2eb3cbd499..e59756275000 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -835,7 +835,7 @@ void do_exit(long code) /* * Make sure we are holding no locks: */ - debug_check_no_locks_held(tsk); + debug_check_no_locks_held(); /* * We can do this unlocked here. The futex code uses this flag * just to verify whether the pi state cleanup has been done diff --git a/kernel/lockdep.c b/kernel/lockdep.c index 1f3186b37fd5..e16c45b9ee77 100644 --- a/kernel/lockdep.c +++ b/kernel/lockdep.c @@ -4090,7 +4090,7 @@ void debug_check_no_locks_freed(const void *mem_from, unsigned long mem_len) } EXPORT_SYMBOL_GPL(debug_check_no_locks_freed); -static void print_held_locks_bug(struct task_struct *curr) +static void print_held_locks_bug(void) { if (!debug_locks_off()) return; @@ -4099,22 +4099,21 @@ static void print_held_locks_bug(struct task_struct *curr) printk("\n"); printk("=====================================\n"); - printk("[ BUG: lock held at task exit time! ]\n"); + printk("[ BUG: %s/%d still has locks held! ]\n", + current->comm, task_pid_nr(current)); print_kernel_ident(); printk("-------------------------------------\n"); - printk("%s/%d is exiting with locks still held!\n", - curr->comm, task_pid_nr(curr)); - lockdep_print_held_locks(curr); - + lockdep_print_held_locks(current); printk("\nstack backtrace:\n"); dump_stack(); } -void debug_check_no_locks_held(struct task_struct *task) +void debug_check_no_locks_held(void) { - if (unlikely(task->lockdep_depth > 0)) - print_held_locks_bug(task); + if (unlikely(current->lockdep_depth > 0)) + print_held_locks_bug(); } +EXPORT_SYMBOL_GPL(debug_check_no_locks_held); void debug_show_all_locks(void) { -- cgit v1.2.3 From 0f9548ca10916dec166eaf74c816bded7d8e611d Mon Sep 17 00:00:00 2001 From: Mandeep Singh Baines Date: Mon, 6 May 2013 23:50:09 +0000 Subject: lockdep: check that no locks held at freeze time We shouldn't try_to_freeze if locks are held. Holding a lock can cause a deadlock if the lock is later acquired in the suspend or hibernate path (e.g. by dpm). Holding a lock can also cause a deadlock in the case of cgroup_freezer if a lock is held inside a frozen cgroup that is later acquired by a process outside that group. History: This patch was originally applied as 6aa9707099c and reverted in dbf520a9d7d4 because NFS was freezing with locks held. It was deemed better to keep the bad freeze point in NFS to allow laptops to suspend consistently. The previous patch in this series converts NFS to call _unsafe versions of the freezable helpers so that lockdep doesn't complain about them until a more correct fix can be applied. [akpm@linux-foundation.org: export debug_check_no_locks_held] Signed-off-by: Mandeep Singh Baines Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Acked-by: Pavel Machek Acked-by: Tejun Heo Signed-off-by: Colin Cross Signed-off-by: Rafael J. Wysocki --- include/linux/freezer.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/linux/freezer.h b/include/linux/freezer.h index d3c038ec9a88..bcf9e651cc85 100644 --- a/include/linux/freezer.h +++ b/include/linux/freezer.h @@ -3,6 +3,7 @@ #ifndef FREEZER_H_INCLUDED #define FREEZER_H_INCLUDED +#include #include #include #include @@ -60,6 +61,8 @@ static inline bool try_to_freeze_unsafe(void) static inline bool try_to_freeze(void) { + if (!(current->flags & PF_NOFREEZE)) + debug_check_no_locks_held(); return try_to_freeze_unsafe(); } -- cgit v1.2.3 From 18ad0c6297df1d671ecea83b608cd9e432642a05 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 6 May 2013 23:50:10 +0000 Subject: freezer: shorten freezer sleep time using exponential backoff All tasks can easily be frozen in under 10 ms, switch to using an initial 1 ms sleep followed by exponential backoff until 8 ms. Also convert the printed time to ms instead of centiseconds. Acked-by: Pavel Machek Acked-by: Tejun Heo Signed-off-by: Colin Cross Signed-off-by: Rafael J. Wysocki --- kernel/power/process.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/kernel/power/process.c b/kernel/power/process.c index 98088e0e71e8..fc0df8486449 100644 --- a/kernel/power/process.c +++ b/kernel/power/process.c @@ -30,9 +30,10 @@ static int try_to_freeze_tasks(bool user_only) unsigned int todo; bool wq_busy = false; struct timeval start, end; - u64 elapsed_csecs64; - unsigned int elapsed_csecs; + u64 elapsed_msecs64; + unsigned int elapsed_msecs; bool wakeup = false; + int sleep_usecs = USEC_PER_MSEC; do_gettimeofday(&start); @@ -68,22 +69,25 @@ static int try_to_freeze_tasks(bool user_only) /* * We need to retry, but first give the freezing tasks some - * time to enter the refrigerator. + * time to enter the refrigerator. Start with an initial + * 1 ms sleep followed by exponential backoff until 8 ms. */ - msleep(10); + usleep_range(sleep_usecs / 2, sleep_usecs); + if (sleep_usecs < 8 * USEC_PER_MSEC) + sleep_usecs *= 2; } do_gettimeofday(&end); - elapsed_csecs64 = timeval_to_ns(&end) - timeval_to_ns(&start); - do_div(elapsed_csecs64, NSEC_PER_SEC / 100); - elapsed_csecs = elapsed_csecs64; + elapsed_msecs64 = timeval_to_ns(&end) - timeval_to_ns(&start); + do_div(elapsed_msecs64, NSEC_PER_MSEC); + elapsed_msecs = elapsed_msecs64; if (todo) { printk("\n"); - printk(KERN_ERR "Freezing of tasks %s after %d.%02d seconds " + printk(KERN_ERR "Freezing of tasks %s after %d.%03d seconds " "(%d tasks refusing to freeze, wq_busy=%d):\n", wakeup ? "aborted" : "failed", - elapsed_csecs / 100, elapsed_csecs % 100, + elapsed_msecs / 1000, elapsed_msecs % 1000, todo - wq_busy, wq_busy); if (!wakeup) { @@ -96,8 +100,8 @@ static int try_to_freeze_tasks(bool user_only) read_unlock(&tasklist_lock); } } else { - printk("(elapsed %d.%02d seconds) ", elapsed_csecs / 100, - elapsed_csecs % 100); + printk("(elapsed %d.%03d seconds) ", elapsed_msecs / 1000, + elapsed_msecs % 1000); } return todo ? -EBUSY : 0; -- cgit v1.2.3 From 613f5d13b569859171f0896fbc73ee0bfa811fda Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 6 May 2013 23:50:11 +0000 Subject: freezer: skip waking up tasks with PF_FREEZER_SKIP set Android goes through suspend/resume very often (every few seconds when on a busy wifi network with the screen off), and a significant portion of the energy used to go in and out of suspend is spent in the freezer. If a task has called freezer_do_not_count(), don't bother waking it up. If it happens to wake up later it will call freezer_count() and immediately enter the refrigerator. Combined with patches to convert freezable helpers to use freezer_do_not_count() and convert common sites where idle userspace tasks are blocked to use the freezable helpers, this reduces the time and energy required to suspend and resume. Acked-by: Tejun Heo Acked-by: Pavel Machek Signed-off-by: Colin Cross Signed-off-by: Rafael J. Wysocki --- kernel/freezer.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/kernel/freezer.c b/kernel/freezer.c index c38893b0efba..8b2afc1c9df0 100644 --- a/kernel/freezer.c +++ b/kernel/freezer.c @@ -110,6 +110,18 @@ bool freeze_task(struct task_struct *p) { unsigned long flags; + /* + * This check can race with freezer_do_not_count, but worst case that + * will result in an extra wakeup being sent to the task. It does not + * race with freezer_count(), the barriers in freezer_count() and + * freezer_should_skip() ensure that either freezer_count() sees + * freezing == true in try_to_freeze() and freezes, or + * freezer_should_skip() sees !PF_FREEZE_SKIP and freezes the task + * normally. + */ + if (freezer_should_skip(p)) + return false; + spin_lock_irqsave(&freezer_lock, flags); if (!freezing(p) || frozen(p)) { spin_unlock_irqrestore(&freezer_lock, flags); -- cgit v1.2.3 From b01235861b84c0f6107d3f9da189c9898fc3caaf Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 6 May 2013 23:50:12 +0000 Subject: freezer: convert freezable helpers to freezer_do_not_count() Freezing tasks will wake up almost every userspace task from where it is blocking and force it to run until it hits a call to try_to_sleep(), generally on the exit path from the syscall it is blocking in. On resume each task will run again, usually restarting the syscall and running until it hits the same blocking call as it was originally blocked in. Convert the existing wait_event_freezable* wrappers to use freezer_do_not_count(). Combined with a previous patch, these tasks will not run during suspend or resume unless they wake up for another reason, in which case they will run until they hit the try_to_freeze() in freezer_count(), and then continue processing the wakeup after tasks are thawed. This results in a small change in behavior, previously a race between freezing and a normal wakeup would be won by the wakeup, now the task will freeze and then handle the wakeup after thawing. Acked-by: Tejun Heo Signed-off-by: Colin Cross Signed-off-by: Rafael J. Wysocki --- include/linux/freezer.h | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/include/linux/freezer.h b/include/linux/freezer.h index bcf9e651cc85..c71337af9bbd 100644 --- a/include/linux/freezer.h +++ b/include/linux/freezer.h @@ -228,27 +228,19 @@ static inline bool freezer_should_skip(struct task_struct *p) #define wait_event_freezable(wq, condition) \ ({ \ int __retval; \ - for (;;) { \ - __retval = wait_event_interruptible(wq, \ - (condition) || freezing(current)); \ - if (__retval || (condition)) \ - break; \ - try_to_freeze(); \ - } \ + freezer_do_not_count(); \ + __retval = wait_event_interruptible(wq, (condition)); \ + freezer_count(); \ __retval; \ }) #define wait_event_freezable_timeout(wq, condition, timeout) \ ({ \ long __retval = timeout; \ - for (;;) { \ - __retval = wait_event_interruptible_timeout(wq, \ - (condition) || freezing(current), \ - __retval); \ - if (__retval <= 0 || (condition)) \ - break; \ - try_to_freeze(); \ - } \ + freezer_do_not_count(); \ + __retval = wait_event_interruptible_timeout(wq, (condition), \ + __retval); \ + freezer_count(); \ __retval; \ }) -- cgit v1.2.3 From 8ee492d6595573a0d4be168ebda1c7ceb4ec509d Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 6 May 2013 23:50:13 +0000 Subject: freezer: convert freezable helpers to static inline where possible Some of the freezable helpers have to be macros because their condition argument needs to get evaluated every time through the wait loop. Convert the others to static inline to make future changes easier. Acked-by: Tejun Heo Signed-off-by: Colin Cross Signed-off-by: Rafael J. Wysocki --- include/linux/freezer.h | 58 ++++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/include/linux/freezer.h b/include/linux/freezer.h index c71337af9bbd..8430d4c51ece 100644 --- a/include/linux/freezer.h +++ b/include/linux/freezer.h @@ -159,46 +159,46 @@ static inline bool freezer_should_skip(struct task_struct *p) } /* - * These macros are intended to be used whenever you want allow a sleeping + * These functions are intended to be used whenever you want allow a sleeping * task to be frozen. Note that neither return any clear indication of * whether a freeze event happened while in this function. */ /* Like schedule(), but should not block the freezer. */ -#define freezable_schedule() \ -({ \ - freezer_do_not_count(); \ - schedule(); \ - freezer_count(); \ -}) +static inline void freezable_schedule(void) +{ + freezer_do_not_count(); + schedule(); + freezer_count(); +} /* DO NOT ADD ANY NEW CALLERS OF THIS FUNCTION */ -#define freezable_schedule_unsafe() \ -({ \ - freezer_do_not_count(); \ - schedule(); \ - freezer_count_unsafe(); \ -}) +static inline void freezable_schedule_unsafe(void) +{ + freezer_do_not_count(); + schedule(); + freezer_count_unsafe(); +} /* Like schedule_timeout_killable(), but should not block the freezer. */ -#define freezable_schedule_timeout_killable(timeout) \ -({ \ - long __retval; \ - freezer_do_not_count(); \ - __retval = schedule_timeout_killable(timeout); \ - freezer_count(); \ - __retval; \ -}) +static inline long freezable_schedule_timeout_killable(long timeout) +{ + long __retval; + freezer_do_not_count(); + __retval = schedule_timeout_killable(timeout); + freezer_count(); + return __retval; +} /* DO NOT ADD ANY NEW CALLERS OF THIS FUNCTION */ -#define freezable_schedule_timeout_killable_unsafe(timeout) \ -({ \ - long __retval; \ - freezer_do_not_count(); \ - __retval = schedule_timeout_killable(timeout); \ - freezer_count_unsafe(); \ - __retval; \ -}) +static inline long freezable_schedule_timeout_killable_unsafe(long timeout) +{ + long __retval; + freezer_do_not_count(); + __retval = schedule_timeout_killable(timeout); + freezer_count_unsafe(); + return __retval; +} /* * Freezer-friendly wrappers around wait_event_interruptible(), -- cgit v1.2.3 From dd5ec0f4e72bed3d0e589e21fdf46eedafc106b7 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 6 May 2013 23:50:14 +0000 Subject: freezer: add new freezable helpers using freezer_do_not_count() Freezing tasks will wake up almost every userspace task from where it is blocking and force it to run until it hits a call to try_to_sleep(), generally on the exit path from the syscall it is blocking in. On resume each task will run again, usually restarting the syscall and running until it hits the same blocking call as it was originally blocked in. To allow tasks to avoid running on every suspend/resume cycle, this patch adds additional freezable wrappers around blocking calls that call freezer_do_not_count(). Combined with the previous patch, these tasks will not run during suspend or resume unless they wake up for another reason, in which case they will run until they hit the try_to_freeze() in freezer_count(), and then continue processing the wakeup after tasks are thawed. Additional patches will convert the most common locations that userspace blocks in to use freezable helpers. Acked-by: Tejun Heo Signed-off-by: Colin Cross Signed-off-by: Rafael J. Wysocki --- include/linux/freezer.h | 61 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/include/linux/freezer.h b/include/linux/freezer.h index 8430d4c51ece..7fd81b8c4897 100644 --- a/include/linux/freezer.h +++ b/include/linux/freezer.h @@ -180,6 +180,32 @@ static inline void freezable_schedule_unsafe(void) freezer_count_unsafe(); } +/* + * Like freezable_schedule_timeout(), but should not block the freezer. Do not + * call this with locks held. + */ +static inline long freezable_schedule_timeout(long timeout) +{ + long __retval; + freezer_do_not_count(); + __retval = schedule_timeout(timeout); + freezer_count(); + return __retval; +} + +/* + * Like schedule_timeout_interruptible(), but should not block the freezer. Do not + * call this with locks held. + */ +static inline long freezable_schedule_timeout_interruptible(long timeout) +{ + long __retval; + freezer_do_not_count(); + __retval = schedule_timeout_interruptible(timeout); + freezer_count(); + return __retval; +} + /* Like schedule_timeout_killable(), but should not block the freezer. */ static inline long freezable_schedule_timeout_killable(long timeout) { @@ -200,6 +226,20 @@ static inline long freezable_schedule_timeout_killable_unsafe(long timeout) return __retval; } +/* + * Like schedule_hrtimeout_range(), but should not block the freezer. Do not + * call this with locks held. + */ +static inline int freezable_schedule_hrtimeout_range(ktime_t *expires, + unsigned long delta, const enum hrtimer_mode mode) +{ + int __retval; + freezer_do_not_count(); + __retval = schedule_hrtimeout_range(expires, delta, mode); + freezer_count(); + return __retval; +} + /* * Freezer-friendly wrappers around wait_event_interruptible(), * wait_event_killable() and wait_event_interruptible_timeout(), originally @@ -244,6 +284,16 @@ static inline long freezable_schedule_timeout_killable_unsafe(long timeout) __retval; \ }) +#define wait_event_freezable_exclusive(wq, condition) \ +({ \ + int __retval; \ + freezer_do_not_count(); \ + __retval = wait_event_interruptible_exclusive(wq, condition); \ + freezer_count(); \ + __retval; \ +}) + + #else /* !CONFIG_FREEZER */ static inline bool frozen(struct task_struct *p) { return false; } static inline bool freezing(struct task_struct *p) { return false; } @@ -267,18 +317,29 @@ static inline void set_freezable(void) {} #define freezable_schedule_unsafe() schedule() +#define freezable_schedule_timeout(timeout) schedule_timeout(timeout) + +#define freezable_schedule_timeout_interruptible(timeout) \ + schedule_timeout_interruptible(timeout) + #define freezable_schedule_timeout_killable(timeout) \ schedule_timeout_killable(timeout) #define freezable_schedule_timeout_killable_unsafe(timeout) \ schedule_timeout_killable(timeout) +#define freezable_schedule_hrtimeout_range(expires, delta, mode) \ + schedule_hrtimeout_range(expires, delta, mode) + #define wait_event_freezable(wq, condition) \ wait_event_interruptible(wq, condition) #define wait_event_freezable_timeout(wq, condition, timeout) \ wait_event_interruptible_timeout(wq, condition, timeout) +#define wait_event_freezable_exclusive(wq, condition) \ + wait_event_interruptible_exclusive(wq, condition) + #define wait_event_freezekillable(wq, condition) \ wait_event_killable(wq, condition) -- cgit v1.2.3 From e2610b268bb74d24866a9578e78d8c3de90ed596 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 6 May 2013 23:50:15 +0000 Subject: binder: use freezable blocking calls Avoid waking up every thread sleeping in a binder call during suspend and resume by calling a freezable blocking call. Previous patches modified the freezer to avoid sending wakeups to threads that are blocked in freezable blocking calls. This call was selected to be converted to a freezable call because it doesn't hold any locks or release any resources when interrupted that might be needed by another freezing task or a kernel driver during suspend, and is a common site where idle userspace tasks are blocked. Acked-by: Tejun Heo Signed-off-by: Colin Cross Signed-off-by: Rafael J. Wysocki --- drivers/staging/android/binder.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c index 1567ac296b39..1ffc2ebdf612 100644 --- a/drivers/staging/android/binder.c +++ b/drivers/staging/android/binder.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -2140,13 +2141,13 @@ retry: if (!binder_has_proc_work(proc, thread)) ret = -EAGAIN; } else - ret = wait_event_interruptible_exclusive(proc->wait, binder_has_proc_work(proc, thread)); + ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread)); } else { if (non_block) { if (!binder_has_thread_work(thread)) ret = -EAGAIN; } else - ret = wait_event_interruptible(thread->wait, binder_has_thread_work(thread)); + ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread)); } binder_lock(__func__); -- cgit v1.2.3 From 1c441e921201d523b5a6036aea22b0b426bf1af2 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 6 May 2013 23:50:16 +0000 Subject: epoll: use freezable blocking call Avoid waking up every thread sleeping in an epoll_wait call during suspend and resume by calling a freezable blocking call. Previous patches modified the freezer to avoid sending wakeups to threads that are blocked in freezable blocking calls. This call was selected to be converted to a freezable call because it doesn't hold any locks or release any resources when interrupted that might be needed by another freezing task or a kernel driver during suspend, and is a common site where idle userspace tasks are blocked. Acked-by: Tejun Heo Signed-off-by: Colin Cross Signed-off-by: Rafael J. Wysocki --- fs/eventpoll.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/eventpoll.c b/fs/eventpoll.c index deecc7294a67..0cff4434880d 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -1602,7 +1603,8 @@ fetch_events: } spin_unlock_irqrestore(&ep->lock, flags); - if (!schedule_hrtimeout_range(to, slack, HRTIMER_MODE_ABS)) + if (!freezable_schedule_hrtimeout_range(to, slack, + HRTIMER_MODE_ABS)) timed_out = 1; spin_lock_irqsave(&ep->lock, flags); -- cgit v1.2.3 From 9745cdb36da83aeec198650b410ca06304cf7928 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 6 May 2013 23:50:17 +0000 Subject: select: use freezable blocking call Avoid waking up every thread sleeping in a select call during suspend and resume by calling a freezable blocking call. Previous patches modified the freezer to avoid sending wakeups to threads that are blocked in freezable blocking calls. This call was selected to be converted to a freezable call because it doesn't hold any locks or release any resources when interrupted that might be needed by another freezing task or a kernel driver during suspend, and is a common site where idle userspace tasks are blocked. Acked-by: Tejun Heo Signed-off-by: Colin Cross Signed-off-by: Rafael J. Wysocki --- fs/select.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/select.c b/fs/select.c index 8c1c96c27062..6b14dc7df3a4 100644 --- a/fs/select.c +++ b/fs/select.c @@ -27,6 +27,7 @@ #include #include #include +#include #include @@ -236,7 +237,8 @@ int poll_schedule_timeout(struct poll_wqueues *pwq, int state, set_current_state(state); if (!pwq->triggered) - rc = schedule_hrtimeout_range(expires, slack, HRTIMER_MODE_ABS); + rc = freezable_schedule_hrtimeout_range(expires, slack, + HRTIMER_MODE_ABS); __set_current_state(TASK_RUNNING); /* -- cgit v1.2.3 From 56467c7697f5aef6974501fbe2c3e63674583549 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 6 May 2013 23:50:18 +0000 Subject: futex: use freezable blocking call Avoid waking up every thread sleeping in a futex_wait call during suspend and resume by calling a freezable blocking call. Previous patches modified the freezer to avoid sending wakeups to threads that are blocked in freezable blocking calls. This call was selected to be converted to a freezable call because it doesn't hold any locks or release any resources when interrupted that might be needed by another freezing task or a kernel driver during suspend, and is a common site where idle userspace tasks are blocked. Acked-by: Tejun Heo Acked-by: Thomas Gleixner Acked-by: Darren Hart Signed-off-by: Colin Cross Signed-off-by: Rafael J. Wysocki --- kernel/futex.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/futex.c b/kernel/futex.c index b26dcfc02c94..d710fae8abbe 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -61,6 +61,7 @@ #include #include #include +#include #include @@ -1807,7 +1808,7 @@ static void futex_wait_queue_me(struct futex_hash_bucket *hb, struct futex_q *q, * is no timeout, or if it has yet to expire. */ if (!timeout || timeout->task) - schedule(); + freezable_schedule(); } __set_current_state(TASK_RUNNING); } -- cgit v1.2.3 From b0f8c44f30e58c3aaaaaf864d5c3d3cc2e8a4c2d Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 6 May 2013 23:50:19 +0000 Subject: nanosleep: use freezable blocking call Avoid waking up every thread sleeping in a nanosleep call during suspend and resume by calling a freezable blocking call. Previous patches modified the freezer to avoid sending wakeups to threads that are blocked in freezable blocking calls. This call was selected to be converted to a freezable call because it doesn't hold any locks or release any resources when interrupted that might be needed by another freezing task or a kernel driver during suspend, and is a common site where idle userspace tasks are blocked. Acked-by: Tejun Heo Acked-by: Thomas Gleixner Signed-off-by: Colin Cross Signed-off-by: Rafael J. Wysocki --- kernel/hrtimer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index fd4b13b131f8..3ee4d06c6fc2 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -47,6 +47,7 @@ #include #include #include +#include #include @@ -1545,7 +1546,7 @@ static int __sched do_nanosleep(struct hrtimer_sleeper *t, enum hrtimer_mode mod t->task = NULL; if (likely(t->task)) - schedule(); + freezable_schedule(); hrtimer_cancel(&t->timer); mode = HRTIMER_MODE_ABS; -- cgit v1.2.3 From a2d5f1f5d941593e61071dc78e9de228eda5475f Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 6 May 2013 23:50:20 +0000 Subject: sigtimedwait: use freezable blocking call Avoid waking up every thread sleeping in a sigtimedwait call during suspend and resume by calling a freezable blocking call. Previous patches modified the freezer to avoid sending wakeups to threads that are blocked in freezable blocking calls. This call was selected to be converted to a freezable call because it doesn't hold any locks or release any resources when interrupted that might be needed by another freezing task or a kernel driver during suspend, and is a common site where idle userspace tasks are blocked. Acked-by: Tejun Heo Signed-off-by: Colin Cross Signed-off-by: Rafael J. Wysocki --- kernel/signal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/signal.c b/kernel/signal.c index 113411bfe8b1..50e41075ac77 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -2848,7 +2848,7 @@ int do_sigtimedwait(const sigset_t *which, siginfo_t *info, recalc_sigpending(); spin_unlock_irq(&tsk->sighand->siglock); - timeout = schedule_timeout_interruptible(timeout); + timeout = freezable_schedule_timeout_interruptible(timeout); spin_lock_irq(&tsk->sighand->siglock); __set_task_blocked(tsk, &tsk->real_blocked); -- cgit v1.2.3 From 2b15af6f953012aac49984ead3f8ec744d941540 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 6 May 2013 23:50:21 +0000 Subject: af_unix: use freezable blocking calls in read Avoid waking up every thread sleeping in read call on an AF_UNIX socket during suspend and resume by calling a freezable blocking call. Previous patches modified the freezer to avoid sending wakeups to threads that are blocked in freezable blocking calls. This call was selected to be converted to a freezable call because it doesn't hold any locks or release any resources when interrupted that might be needed by another freezing task or a kernel driver during suspend, and is a common site where idle userspace tasks are blocked. Acked-by: Tejun Heo Signed-off-by: Colin Cross Signed-off-by: Rafael J. Wysocki --- net/unix/af_unix.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 826e09938bff..c4ce243824bb 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -114,6 +114,7 @@ #include #include #include +#include struct hlist_head unix_socket_table[2 * UNIX_HASH_SIZE]; EXPORT_SYMBOL_GPL(unix_socket_table); @@ -1879,7 +1880,7 @@ static long unix_stream_data_wait(struct sock *sk, long timeo, set_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags); unix_state_unlock(sk); - timeo = schedule_timeout(timeo); + timeo = freezable_schedule_timeout(timeo); unix_state_lock(sk); clear_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags); } -- cgit v1.2.3 From 40b1936efebdb9c31d9ed6fe59055f71ea366509 Mon Sep 17 00:00:00 2001 From: "Andrii.Tseglytskyi" Date: Thu, 2 May 2013 12:20:10 -0500 Subject: regulator: Introduce TI Adaptive Body Bias(ABB) on-chip LDO driver Adaptive Body Biasing (ABB) modulates transistor bias voltages dynamically in order to optimize switching speed versus leakage. Texas Instruments' SmartReflex 2 technology provides support for this power management technique with Forward Body Biasing (FBB) and Reverse Body Biasing (RBB). These modulate the body voltage of transistor cells or blocks dynamically to gain performance and reduce leakage. TI's SmartReflex white paper[1] has further information for usage in conjunction with other power management techniques. The application of FBB/RBB technique is determined for each unique device in some process nodes, whereas, they are mandated on other process nodes. In a nutshell, ABB technique is implemented on TI SoC as an on-chip LDO which has ABB module controlling the bias voltage. However, the voltage is unique per device. These vary per SoC family and the manner in which these techniques are used may vary depending on the Operating Performance Point (OPP) voltage targeted. For example: OMAP3630/OMAP4430: certain OPPs mandate usage of FBB independent of devices. OMAP4460/OMAP4470: certain OPPs mandate usage of FBB, while others may optionally use FBB or optimization with RBB. OMAP5: ALL OPPs may optionally use ABB, and ABB biasing voltage is influenced by vset fused in s/w and requiring s/w override of default values. Further, two generations of ABB module are used in various TI SoCs. They have remained mostly register field compatible, however the register offset had switched between versions. We introduce ABB LDO support in the form of a regulator which is controlled by voltages denoting the desired Operating Performance Point which is targeted. However, since ABB transition is part of OPP change sequence, the sequencing required to ensure sane operation w.r.t OPP change is left to the controlling driver (example: cpufreq SoC driver) using standard regulator operations. The driver supports all ABB modes and ability to override ABB LDO vset control efuse based ABB mode detection etc. Current implementation is heavily influenced by the original patch series [2][3] from Mike Turquette. However, the current implementation supports only device tree based information. [1] http://www.ti.com/pdfs/wtbu/smartreflex_whitepaper.pdf [2] http://marc.info/?l=linux-omap&m=134931341818379&w=2 [3] http://marc.info/?l=linux-arm-kernel&m=134931402406853&w=2 [nm@ti.com: co-developer] Signed-off-by: Nishanth Menon Signed-off-by: Andrii.Tseglytskyi Signed-off-by: Mark Brown --- .../bindings/regulator/ti-abb-regulator.txt | 128 +++ drivers/regulator/Kconfig | 10 + drivers/regulator/Makefile | 1 + drivers/regulator/ti-abb-regulator.c | 912 +++++++++++++++++++++ 4 files changed, 1051 insertions(+) create mode 100644 Documentation/devicetree/bindings/regulator/ti-abb-regulator.txt create mode 100644 drivers/regulator/ti-abb-regulator.c diff --git a/Documentation/devicetree/bindings/regulator/ti-abb-regulator.txt b/Documentation/devicetree/bindings/regulator/ti-abb-regulator.txt new file mode 100644 index 000000000000..2e57a33e9029 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/ti-abb-regulator.txt @@ -0,0 +1,128 @@ +Adaptive Body Bias(ABB) SoC internal LDO regulator for Texas Instruments SoCs + +Required Properties: +- compatible: Should be one of: + - "ti,abb-v1" for older SoCs like OMAP3 + - "ti,abb-v2" for newer SoCs like OMAP4, OMAP5 +- reg: Address and length of the register set for the device. It contains + the information of registers in the same order as described by reg-names +- reg-names: Should contain the reg names + - "base-address" - contains base address of ABB module + - "int-address" - contains address of interrupt register for ABB module + (also see Optional properties) +- #address-cell: should be 0 +- #size-cell: should be 0 +- clocks: should point to the clock node used by ABB module +- ti,settling-time: Settling time in uSecs from SoC documentation for ABB module + to settle down(target time for SR2_WTCNT_VALUE). +- ti,clock-cycles: SoC specific data about count of system ti,clock-cycles used for + computing settling time from SoC Documentation for ABB module(clock + cycles for SR2_WTCNT_VALUE). +- ti,tranxdone-status-mask: Mask to the int-register to write-to-clear mask + indicating LDO tranxdone (operation complete). +- ti,abb_info: An array of 6-tuples u32 items providing information about ABB + configuration needed per operational voltage of the device. + Each item consists of the following in the same order: + volt: voltage in uV - Only used to index ABB information. + ABB mode: one of the following: + 0-bypass + 1-Forward Body Bias(FBB) + 3-Reverse Body Bias(RBB) + efuse: (see Optional properties) + RBB enable efuse Mask: (See Optional properties) + FBB enable efuse Mask: (See Optional properties) + Vset value efuse Mask: (See Optional properties) + + NOTE: If more than 1 entry is present, then regulator is setup to change + voltage, allowing for various modes to be selected indexed off + the regulator. Further, ABB LDOs are considered always-on by + default. + +Optional Properties: +- reg-names: In addition to the required properties, the following are optional + - "efuse-address" - Contains efuse base address used to pick up ABB info. + - "ldo-address" - Contains address of ABB LDO overide register address. + "efuse-address" is required for this. +- ti,ldovbb-vset-mask - Required if ldo-address is set, mask for LDO override + register to provide override vset value. +- ti,ldovbb-override-mask - Required if ldo-address is set, mask for LDO + override register to enable override vset value. +- ti,abb_opp_sel: Addendum to the description in required properties + efuse: Mandatory if 'efuse-address' register is defined. Provides offset + from efuse-address to pick up ABB characteristics. Set to 0 if + 'efuse-address' is not defined. + RBB enable efuse Mask: Optional if 'efuse-address' register is defined. + 'ABB mode' is force set to RBB mode if value at "efuse-address" + + efuse maps to RBB mask. Set to 0 to ignore this. + FBB enable efuse Mask: Optional if 'efuse-address' register is defined. + 'ABB mode' is force set to FBB mode if value at "efuse-address" + + efuse maps to FBB mask (valid only if RBB mask does not match) + Set to 0 to ignore this. + Vset value efuse Mask: Mandatory if ldo-address is set. Picks up from + efuse the value to set in 'ti,ldovbb-vset-mask' at ldo-address. + +Example #1: Simplest configuration (no efuse data, hard coded ABB table): +abb_x: regulator-abb-x { + compatible = "ti,abb-v1"; + regulator-name = "abb_x"; + #address-cell = <0>; + #size-cells = <0>; + reg = <0x483072f0 0x8>, <0x48306818 0x4>; + reg-names = "base-address", "int-address"; + ti,tranxdone-status-mask = <0x4000000>; + clocks = <&sysclk>; + ti,settling-time = <30>; + ti,clock-cycles = <8>; + ti,abb_info = < + /* uV ABB efuse rbb_m fbb_m vset_m */ + 1012500 0 0 0 0 0 /* Bypass */ + 1200000 3 0 0 0 0 /* RBB mandatory */ + 1320000 1 0 0 0 0 /* FBB mandatory */ + >; +}; + +Example #2: Efuse bits contain ABB mode setting (no LDO override capability) +abb_y: regulator-abb-y { + compatible = "ti,abb-v2"; + regulator-name = "abb_y"; + #address-cell = <0>; + #size-cells = <0>; + reg = <0x4a307bd0 0x8>, <0x4a306014 0x4>, <0x4A002268 0x8>; + reg-names = "base-address", "int-address", "efuse-address"; + ti,tranxdone-status-mask = <0x4000000>; + clocks = <&sysclk>; + ti,settling-time = <50>; + ti,clock-cycles = <16>; + ti,abb_info = < + /* uV ABB efuse rbb_m fbb_m vset_m */ + 975000 0 0 0 0 0 /* Bypass */ + 1012500 0 0 0x40000 0 0 /* RBB optional */ + 1200000 0 0x4 0 0x40000 0 /* FBB optional */ + 1320000 1 0 0 0 0 /* FBB mandatory */ + >; +}; + +Example #3: Efuse bits contain ABB mode setting and LDO override capability +abb_z: regulator-abb-z { + compatible = "ti,abb-v2"; + regulator-name = "abb_z"; + #address-cell = <0>; + #size-cells = <0>; + reg = <0x4ae07ce4 0x8>, <0x4ae06010 0x4>, + <0x4a002194 0x8>, <0x4ae0C314 0x4>; + reg-names = "base-address", "int-address", + "efuse-address", "ldo-address"; + ti,tranxdone-status-mask = <0x8000000>; + /* LDOVBBMM_MUX_CTRL */ + ti,ldovbb-override-mask = <0x400>; + /* LDOVBBMM_VSET_OUT */ + ti,ldovbb-vset-mask = <0x1F>; + clocks = <&sysclk>; + ti,settling-time = <50>; + ti,clock-cycles = <16>; + ti,abb_info = < + /* uV ABB efuse rbb_m fbb_m vset_m */ + 975000 0 0 0 0 0 /* Bypass */ + 1200000 0 0x4 0 0x40000 0x1f00 /* FBB optional, vset */ + >; +}; diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 8bb26446037e..9296425103ef 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -472,6 +472,16 @@ config REGULATOR_TWL4030 This driver supports the voltage regulators provided by this family of companion chips. +config REGULATOR_TI_ABB + bool "TI Adaptive Body Bias on-chip LDO" + depends on ARCH_OMAP + help + Select this option to support Texas Instruments' on-chip Adaptive Body + Bias (ABB) LDO regulators. It is recommended that this option be + enabled on required TI SoC. Certain Operating Performance Points + on TI SoCs may be unstable without enabling this as it provides + device specific optimized bias to allow/optimize functionality. + config REGULATOR_VEXPRESS tristate "Versatile Express regulators" depends on VEXPRESS_CONFIG diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 47a34ff88f98..95350e890973 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -63,6 +63,7 @@ obj-$(CONFIG_REGULATOR_TPS65910) += tps65910-regulator.o obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o obj-$(CONFIG_REGULATOR_TPS80031) += tps80031-regulator.o obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o +obj-$(CONFIG_REGULATOR_TI_ABB) += ti-abb-regulator.o obj-$(CONFIG_REGULATOR_VEXPRESS) += vexpress.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o diff --git a/drivers/regulator/ti-abb-regulator.c b/drivers/regulator/ti-abb-regulator.c new file mode 100644 index 000000000000..c1870ea64939 --- /dev/null +++ b/drivers/regulator/ti-abb-regulator.c @@ -0,0 +1,912 @@ +/* + * Texas Instruments SoC Adaptive Body Bias(ABB) Regulator + * + * Copyright (C) 2011 Texas Instruments, Inc. + * Mike Turquette + * + * Copyright (C) 2012-2013 Texas Instruments, Inc. + * Andrii Tseglytskyi + * Nishanth Menon + * + * 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 "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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * ABB LDO operating states: + * NOMINAL_OPP: bypasses the ABB LDO + * FAST_OPP: sets ABB LDO to Forward Body-Bias + * SLOW_OPP: sets ABB LDO to Reverse Body-Bias + */ +#define TI_ABB_NOMINAL_OPP 0 +#define TI_ABB_FAST_OPP 1 +#define TI_ABB_SLOW_OPP 3 + +/** + * struct ti_abb_info - ABB information per voltage setting + * @opp_sel: one of TI_ABB macro + * @vset: (optional) vset value that LDOVBB needs to be overriden with. + * + * Array of per voltage entries organized in the same order as regulator_desc's + * volt_table list. (selector is used to index from this array) + */ +struct ti_abb_info { + u32 opp_sel; + u32 vset; +}; + +/** + * struct ti_abb_reg - Register description for ABB block + * @setup_reg: setup register offset from base + * @control_reg: control register offset from base + * @sr2_wtcnt_value_mask: setup register- sr2_wtcnt_value mask + * @fbb_sel_mask: setup register- FBB sel mask + * @rbb_sel_mask: setup register- RBB sel mask + * @sr2_en_mask: setup register- enable mask + * @opp_change_mask: control register - mask to trigger LDOVBB change + * @opp_sel_mask: control register - mask for mode to operate + */ +struct ti_abb_reg { + u32 setup_reg; + u32 control_reg; + + /* Setup register fields */ + u32 sr2_wtcnt_value_mask; + u32 fbb_sel_mask; + u32 rbb_sel_mask; + u32 sr2_en_mask; + + /* Control register fields */ + u32 opp_change_mask; + u32 opp_sel_mask; +}; + +/** + * struct ti_abb - ABB instance data + * @rdesc: regulator descriptor + * @clk: clock(usually sysclk) supplying ABB block + * @base: base address of ABB block + * @int_base: interrupt register base address + * @efuse_base: (optional) efuse base address for ABB modes + * @ldo_base: (optional) LDOVBB vset override base address + * @regs: pointer to struct ti_abb_reg for ABB block + * @txdone_mask: mask on int_base for tranxdone interrupt + * @ldovbb_override_mask: mask to ldo_base for overriding default LDO VBB + * vset with value from efuse + * @ldovbb_vset_mask: mask to ldo_base for providing the VSET override + * @info: array to per voltage ABB configuration + * @current_info_idx: current index to info + * @settling_time: SoC specific settling time for LDO VBB + */ +struct ti_abb { + struct regulator_desc rdesc; + struct clk *clk; + void __iomem *base; + void __iomem *int_base; + void __iomem *efuse_base; + void __iomem *ldo_base; + + const struct ti_abb_reg *regs; + u32 txdone_mask; + u32 ldovbb_override_mask; + u32 ldovbb_vset_mask; + + struct ti_abb_info *info; + int current_info_idx; + + u32 settling_time; +}; + +/** + * ti_abb_rmw() - handy wrapper to set specific register bits + * @mask: mask for register field + * @value: value shifted to mask location and written + * @offset: offset of register + * @base: base address + * + * Return: final register value (may be unused) + */ +static inline u32 ti_abb_rmw(u32 mask, u32 value, u32 offset, + void __iomem *base) +{ + u32 val; + + val = readl(base + offset); + val &= ~mask; + val |= (value << __ffs(mask)) & mask; + writel(val, base + offset); + + return val; +} + +/** + * ti_abb_check_txdone() - handy wrapper to check ABB tranxdone status + * @abb: pointer to the abb instance + * + * Return: true or false + */ +static inline bool ti_abb_check_txdone(const struct ti_abb *abb) +{ + return !!(readl(abb->int_base) & abb->txdone_mask); +} + +/** + * ti_abb_clear_txdone() - handy wrapper to clear ABB tranxdone status + * @abb: pointer to the abb instance + */ +static inline void ti_abb_clear_txdone(const struct ti_abb *abb) +{ + writel(abb->txdone_mask, abb->int_base); +}; + +/** + * ti_abb_wait_tranx() - waits for ABB tranxdone event + * @dev: device + * @abb: pointer to the abb instance + * + * Return: 0 on success or -ETIMEDOUT if the event is not cleared on time. + */ +static int ti_abb_wait_txdone(struct device *dev, struct ti_abb *abb) +{ + int timeout = 0; + bool status; + + while (timeout++ <= abb->settling_time) { + status = ti_abb_check_txdone(abb); + if (status) + break; + + udelay(1); + } + + if (timeout > abb->settling_time) { + dev_warn_ratelimited(dev, + "%s:TRANXDONE timeout(%duS) int=0x%08x\n", + __func__, timeout, readl(abb->int_base)); + return -ETIMEDOUT; + } + + return 0; +} + +/** + * ti_abb_clear_all_txdone() - clears ABB tranxdone event + * @dev: device + * @abb: pointer to the abb instance + * + * Return: 0 on success or -ETIMEDOUT if the event is not cleared on time. + */ +static int ti_abb_clear_all_txdone(struct device *dev, const struct ti_abb *abb) +{ + int timeout = 0; + bool status; + + while (timeout++ <= abb->settling_time) { + ti_abb_clear_txdone(abb); + + status = ti_abb_check_txdone(abb); + if (!status) + break; + + udelay(1); + } + + if (timeout > abb->settling_time) { + dev_warn_ratelimited(dev, + "%s:TRANXDONE timeout(%duS) int=0x%08x\n", + __func__, timeout, readl(abb->int_base)); + return -ETIMEDOUT; + } + + return 0; +} + +/** + * ti_abb_program_ldovbb() - program LDOVBB register for override value + * @dev: device + * @abb: pointer to the abb instance + * @info: ABB info to program + */ +static void ti_abb_program_ldovbb(struct device *dev, const struct ti_abb *abb, + struct ti_abb_info *info) +{ + u32 val; + + val = readl(abb->ldo_base); + /* clear up previous values */ + val &= ~(abb->ldovbb_override_mask | abb->ldovbb_vset_mask); + + switch (info->opp_sel) { + case TI_ABB_SLOW_OPP: + case TI_ABB_FAST_OPP: + val |= abb->ldovbb_override_mask; + val |= info->vset << __ffs(abb->ldovbb_vset_mask); + break; + } + + writel(val, abb->ldo_base); +} + +/** + * ti_abb_set_opp() - Setup ABB and LDO VBB for required bias + * @rdev: regulator device + * @abb: pointer to the abb instance + * @info: ABB info to program + * + * Return: 0 on success or appropriate error value when fails + */ +static int ti_abb_set_opp(struct regulator_dev *rdev, struct ti_abb *abb, + struct ti_abb_info *info) +{ + const struct ti_abb_reg *regs = abb->regs; + struct device *dev = &rdev->dev; + int ret; + + ret = ti_abb_clear_all_txdone(dev, abb); + if (ret) + goto out; + + ti_abb_rmw(regs->fbb_sel_mask | regs->rbb_sel_mask, 0, regs->setup_reg, + abb->base); + + switch (info->opp_sel) { + case TI_ABB_SLOW_OPP: + ti_abb_rmw(regs->rbb_sel_mask, 1, regs->setup_reg, abb->base); + break; + case TI_ABB_FAST_OPP: + ti_abb_rmw(regs->fbb_sel_mask, 1, regs->setup_reg, abb->base); + break; + } + + /* program next state of ABB ldo */ + ti_abb_rmw(regs->opp_sel_mask, info->opp_sel, regs->control_reg, + abb->base); + + /* program LDO VBB vset override if needed */ + if (abb->ldo_base) + ti_abb_program_ldovbb(dev, abb, info); + + /* Initiate ABB ldo change */ + ti_abb_rmw(regs->opp_change_mask, 1, regs->control_reg, abb->base); + + /* Wait for ABB LDO to complete transition to new Bias setting */ + ret = ti_abb_wait_txdone(dev, abb); + if (ret) + goto out; + + ret = ti_abb_clear_all_txdone(dev, abb); + if (ret) + goto out; + +out: + return ret; +} + +/** + * ti_abb_set_voltage_sel() - regulator accessor function to set ABB LDO + * @rdev: regulator device + * @sel: selector to index into required ABB LDO settings (maps to + * regulator descriptor's volt_table) + * + * Return: 0 on success or appropriate error value when fails + */ +static int ti_abb_set_voltage_sel(struct regulator_dev *rdev, unsigned sel) +{ + const struct regulator_desc *desc = rdev->desc; + struct ti_abb *abb = rdev_get_drvdata(rdev); + struct device *dev = &rdev->dev; + struct ti_abb_info *info, *oinfo; + int ret = 0; + + if (!abb) { + dev_err_ratelimited(dev, "%s: No regulator drvdata\n", + __func__); + return -ENODEV; + } + + if (!desc->n_voltages || !abb->info) { + dev_err_ratelimited(dev, + "%s: No valid voltage table entries?\n", + __func__); + return -EINVAL; + } + + if (sel >= desc->n_voltages) { + dev_err(dev, "%s: sel idx(%d) >= n_voltages(%d)\n", __func__, + sel, desc->n_voltages); + return -EINVAL; + } + + /* If we are in the same index as we were, nothing to do here! */ + if (sel == abb->current_info_idx) { + dev_dbg(dev, "%s: Already at sel=%d\n", __func__, sel); + return ret; + } + + /* If data is exactly the same, then just update index, no change */ + info = &abb->info[sel]; + oinfo = &abb->info[abb->current_info_idx]; + if (!memcmp(info, oinfo, sizeof(*info))) { + dev_dbg(dev, "%s: Same data new idx=%d, old idx=%d\n", __func__, + sel, abb->current_info_idx); + goto out; + } + + ret = ti_abb_set_opp(rdev, abb, info); + +out: + if (!ret) + abb->current_info_idx = sel; + else + dev_err_ratelimited(dev, + "%s: Volt[%d] idx[%d] mode[%d] Fail(%d)\n", + __func__, desc->volt_table[sel], sel, + info->opp_sel, ret); + return ret; +} + +/** + * ti_abb_get_voltage_sel() - Regulator accessor to get current ABB LDO setting + * @rdev: regulator device + * + * Return: 0 on success or appropriate error value when fails + */ +static int ti_abb_get_voltage_sel(struct regulator_dev *rdev) +{ + const struct regulator_desc *desc = rdev->desc; + struct ti_abb *abb = rdev_get_drvdata(rdev); + struct device *dev = &rdev->dev; + + if (!abb) { + dev_err_ratelimited(dev, "%s: No regulator drvdata\n", + __func__); + return -ENODEV; + } + + if (!desc->n_voltages || !abb->info) { + dev_err_ratelimited(dev, + "%s: No valid voltage table entries?\n", + __func__); + return -EINVAL; + } + + if (abb->current_info_idx > (int)desc->n_voltages) { + dev_err(dev, "%s: Corrupted data? idx(%d) > n_voltages(%d)\n", + __func__, abb->current_info_idx, desc->n_voltages); + return -EINVAL; + } + + return abb->current_info_idx; +} + +/** + * ti_abb_init_timings() - setup ABB clock timing for the current platform + * @dev: device + * @abb: pointer to the abb instance + * + * Return: 0 if timing is updated, else returns error result. + */ +static int ti_abb_init_timings(struct device *dev, struct ti_abb *abb) +{ + u32 clock_cycles; + u32 clk_rate, sr2_wt_cnt_val, cycle_rate; + const struct ti_abb_reg *regs = abb->regs; + int ret; + char *pname = "ti,settling-time"; + + /* read device tree properties */ + ret = of_property_read_u32(dev->of_node, pname, &abb->settling_time); + if (ret) { + dev_err(dev, "Unable to get property '%s'(%d)\n", pname, ret); + return ret; + } + + /* ABB LDO cannot be settle in 0 time */ + if (!abb->settling_time) { + dev_err(dev, "Invalid property:'%s' set as 0!\n", pname); + return -EINVAL; + } + + pname = "ti,clock-cycles"; + ret = of_property_read_u32(dev->of_node, pname, &clock_cycles); + if (ret) { + dev_err(dev, "Unable to get property '%s'(%d)\n", pname, ret); + return ret; + } + /* ABB LDO cannot be settle in 0 clock cycles */ + if (!clock_cycles) { + dev_err(dev, "Invalid property:'%s' set as 0!\n", pname); + return -EINVAL; + } + + abb->clk = devm_clk_get(dev, NULL); + if (IS_ERR(abb->clk)) { + ret = PTR_ERR(abb->clk); + dev_err(dev, "%s: Unable to get clk(%d)\n", __func__, ret); + return ret; + } + + /* + * SR2_WTCNT_VALUE is the settling time for the ABB ldo after a + * transition and must be programmed with the correct time at boot. + * The value programmed into the register is the number of SYS_CLK + * clock cycles that match a given wall time profiled for the ldo. + * This value depends on: + * settling time of ldo in micro-seconds (varies per OMAP family) + * # of clock cycles per SYS_CLK period (varies per OMAP family) + * the SYS_CLK frequency in MHz (varies per board) + * The formula is: + * + * ldo settling time (in micro-seconds) + * SR2_WTCNT_VALUE = ------------------------------------------ + * (# system clock cycles) * (sys_clk period) + * + * Put another way: + * + * SR2_WTCNT_VALUE = settling time / (# SYS_CLK cycles / SYS_CLK rate)) + * + * To avoid dividing by zero multiply both "# clock cycles" and + * "settling time" by 10 such that the final result is the one we want. + */ + + /* Convert SYS_CLK rate to MHz & prevent divide by zero */ + clk_rate = DIV_ROUND_CLOSEST(clk_get_rate(abb->clk), 1000000); + + /* Calculate cycle rate */ + cycle_rate = DIV_ROUND_CLOSEST(clock_cycles * 10, clk_rate); + + /* Calulate SR2_WTCNT_VALUE */ + sr2_wt_cnt_val = DIV_ROUND_CLOSEST(abb->settling_time * 10, cycle_rate); + + dev_dbg(dev, "%s: Clk_rate=%ld, sr2_cnt=0x%08x\n", __func__, + clk_get_rate(abb->clk), sr2_wt_cnt_val); + + ti_abb_rmw(regs->sr2_wtcnt_value_mask, sr2_wt_cnt_val, regs->setup_reg, + abb->base); + + return 0; +} + +/** + * ti_abb_init_table() - Initialize ABB table from device tree + * @dev: device + * @abb: pointer to the abb instance + * @rinit_data: regulator initdata + * + * Return: 0 on success or appropriate error value when fails + */ +static int ti_abb_init_table(struct device *dev, struct ti_abb *abb, + struct regulator_init_data *rinit_data) +{ + struct ti_abb_info *info; + const struct property *prop; + const __be32 *abb_info; + const u32 num_values = 6; + char *pname = "ti,abb_info"; + u32 num_entries, i; + unsigned int *volt_table; + int min_uV = INT_MAX, max_uV = 0; + struct regulation_constraints *c = &rinit_data->constraints; + + prop = of_find_property(dev->of_node, pname, NULL); + if (!prop) { + dev_err(dev, "No '%s' property?\n", pname); + return -ENODEV; + } + + if (!prop->value) { + dev_err(dev, "Empty '%s' property?\n", pname); + return -ENODATA; + } + + /* + * Each abb_info is a set of n-tuple, where n is num_values, consisting + * of voltage and a set of detection logic for ABB information for that + * voltage to apply. + */ + num_entries = prop->length / sizeof(u32); + if (!num_entries || (num_entries % num_values)) { + dev_err(dev, "All '%s' list entries need %d vals\n", pname, + num_values); + return -EINVAL; + } + num_entries /= num_values; + + info = devm_kzalloc(dev, sizeof(*info) * num_entries, GFP_KERNEL); + if (!info) { + dev_err(dev, "Can't allocate info table for '%s' property\n", + pname); + return -ENOMEM; + } + abb->info = info; + + volt_table = devm_kzalloc(dev, sizeof(unsigned int) * num_entries, + GFP_KERNEL); + if (!volt_table) { + dev_err(dev, "Can't allocate voltage table for '%s' property\n", + pname); + return -ENOMEM; + } + + abb->rdesc.n_voltages = num_entries; + abb->rdesc.volt_table = volt_table; + /* We do not know where the OPP voltage is at the moment */ + abb->current_info_idx = -EINVAL; + + abb_info = prop->value; + for (i = 0; i < num_entries; i++, info++, volt_table++) { + u32 efuse_offset, rbb_mask, fbb_mask, vset_mask; + u32 efuse_val; + + /* NOTE: num_values should equal to entries picked up here */ + *volt_table = be32_to_cpup(abb_info++); + info->opp_sel = be32_to_cpup(abb_info++); + efuse_offset = be32_to_cpup(abb_info++); + rbb_mask = be32_to_cpup(abb_info++); + fbb_mask = be32_to_cpup(abb_info++); + vset_mask = be32_to_cpup(abb_info++); + + dev_dbg(dev, + "[%d]v=%d ABB=%d ef=0x%x rbb=0x%x fbb=0x%x vset=0x%x\n", + i, *volt_table, info->opp_sel, efuse_offset, rbb_mask, + fbb_mask, vset_mask); + + /* Find min/max for voltage set */ + if (min_uV > *volt_table) + min_uV = *volt_table; + if (max_uV < *volt_table) + max_uV = *volt_table; + + if (!abb->efuse_base) { + /* Ignore invalid data, but warn to help cleanup */ + if (efuse_offset || rbb_mask || fbb_mask || vset_mask) + dev_err(dev, "prop '%s': v=%d,bad efuse/mask\n", + pname, *volt_table); + goto check_abb; + } + + efuse_val = readl(abb->efuse_base + efuse_offset); + + /* Use ABB recommendation from Efuse */ + if (efuse_val & rbb_mask) + info->opp_sel = TI_ABB_SLOW_OPP; + else if (efuse_val & fbb_mask) + info->opp_sel = TI_ABB_FAST_OPP; + else if (rbb_mask || fbb_mask) + info->opp_sel = TI_ABB_NOMINAL_OPP; + + dev_dbg(dev, + "[%d]v=%d efusev=0x%x final ABB=%d\n", + i, *volt_table, efuse_val, info->opp_sel); + + /* Use recommended Vset bits from Efuse */ + if (!abb->ldo_base) { + if (vset_mask) + dev_err(dev, "prop'%s':v=%d vst=%x LDO base?\n", + pname, *volt_table, vset_mask); + continue; + } + info->vset = efuse_val & vset_mask >> __ffs(vset_mask); + dev_dbg(dev, "[%d]v=%d vset=%x\n", i, *volt_table, info->vset); +check_abb: + switch (info->opp_sel) { + case TI_ABB_NOMINAL_OPP: + case TI_ABB_FAST_OPP: + case TI_ABB_SLOW_OPP: + /* Valid values */ + break; + default: + dev_err(dev, "%s:[%d]v=%d, ABB=%d is invalid! Abort!\n", + __func__, i, *volt_table, info->opp_sel); + return -EINVAL; + } + } + + /* Setup the min/max voltage constraints from the supported list */ + c->min_uV = min_uV; + c->max_uV = max_uV; + + return 0; +} + +static struct regulator_ops ti_abb_reg_ops = { + .list_voltage = regulator_list_voltage_table, + + .set_voltage_sel = ti_abb_set_voltage_sel, + .get_voltage_sel = ti_abb_get_voltage_sel, +}; + +/* Default ABB block offsets, IF this changes in future, create new one */ +static const struct ti_abb_reg abb_regs_v1 = { + /* WARNING: registers are wrongly documented in TRM */ + .setup_reg = 0x04, + .control_reg = 0x00, + + .sr2_wtcnt_value_mask = (0xff << 8), + .fbb_sel_mask = (0x01 << 2), + .rbb_sel_mask = (0x01 << 1), + .sr2_en_mask = (0x01 << 0), + + .opp_change_mask = (0x01 << 2), + .opp_sel_mask = (0x03 << 0), +}; + +static const struct ti_abb_reg abb_regs_v2 = { + .setup_reg = 0x00, + .control_reg = 0x04, + + .sr2_wtcnt_value_mask = (0xff << 8), + .fbb_sel_mask = (0x01 << 2), + .rbb_sel_mask = (0x01 << 1), + .sr2_en_mask = (0x01 << 0), + + .opp_change_mask = (0x01 << 2), + .opp_sel_mask = (0x03 << 0), +}; + +static const struct of_device_id ti_abb_of_match[] = { + {.compatible = "ti,abb-v1", .data = &abb_regs_v1}, + {.compatible = "ti,abb-v2", .data = &abb_regs_v2}, + { }, +}; + +MODULE_DEVICE_TABLE(of, ti_abb_of_match); + +/** + * ti_abb_probe() - Initialize an ABB ldo instance + * @pdev: ABB platform device + * + * Initializes an individual ABB LDO for required Body-Bias. ABB is used to + * addional bias supply to SoC modules for power savings or mandatory stability + * configuration at certain Operating Performance Points(OPPs). + * + * Return: 0 on success or appropriate error value when fails + */ +static int ti_abb_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct of_device_id *match; + struct resource *res; + struct ti_abb *abb; + struct regulator_init_data *initdata = NULL; + struct regulator_dev *rdev = NULL; + struct regulator_desc *desc; + struct regulation_constraints *c; + struct regulator_config config = { }; + char *pname; + int ret = 0; + + match = of_match_device(ti_abb_of_match, dev); + if (!match) { + /* We do not expect this to happen */ + ret = -ENODEV; + dev_err(dev, "%s: Unable to match device\n", __func__); + goto err; + } + if (!match->data) { + ret = -EINVAL; + dev_err(dev, "%s: Bad data in match\n", __func__); + goto err; + } + + abb = devm_kzalloc(dev, sizeof(struct ti_abb), GFP_KERNEL); + if (!abb) { + dev_err(dev, "%s: Unable to allocate ABB struct\n", __func__); + ret = -ENOMEM; + goto err; + } + abb->regs = match->data; + + /* Map ABB resources */ + pname = "base-address"; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); + if (!res) { + dev_err(dev, "Missing '%s' IO resource\n", pname); + ret = -ENODEV; + goto err; + } + abb->base = devm_request_and_ioremap(dev, res); + if (!abb->base) { + dev_err(dev, "Unable to map '%s'\n", pname); + ret = -ENOMEM; + goto err; + } + + pname = "int-address"; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); + if (!res) { + dev_err(dev, "Missing '%s' IO resource\n", pname); + ret = -ENODEV; + goto err; + } + /* + * We may have shared interrupt register offsets which are + * write-1-to-clear between domains ensuring exclusivity. + */ + abb->int_base = devm_ioremap_nocache(dev, res->start, + resource_size(res)); + if (!abb->int_base) { + dev_err(dev, "Unable to map '%s'\n", pname); + ret = -ENOMEM; + goto err; + } + + /* Map Optional resources */ + pname = "efuse-address"; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); + if (!res) { + dev_dbg(dev, "Missing '%s' IO resource\n", pname); + ret = -ENODEV; + goto skip_opt; + } + + /* + * We may have shared efuse register offsets which are read-only + * between domains + */ + abb->efuse_base = devm_ioremap_nocache(dev, res->start, + resource_size(res)); + if (!abb->efuse_base) { + dev_err(dev, "Unable to map '%s'\n", pname); + ret = -ENOMEM; + goto err; + } + + pname = "ldo-address"; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); + if (!res) { + dev_dbg(dev, "Missing '%s' IO resource\n", pname); + ret = -ENODEV; + goto skip_opt; + } + abb->ldo_base = devm_request_and_ioremap(dev, res); + if (!abb->ldo_base) { + dev_err(dev, "Unable to map '%s'\n", pname); + ret = -ENOMEM; + goto err; + } + + /* IF ldo_base is set, the following are mandatory */ + pname = "ti,ldovbb-override-mask"; + ret = + of_property_read_u32(pdev->dev.of_node, pname, + &abb->ldovbb_override_mask); + if (ret) { + dev_err(dev, "Missing '%s' (%d)\n", pname, ret); + goto err; + } + if (!abb->ldovbb_override_mask) { + dev_err(dev, "Invalid property:'%s' set as 0!\n", pname); + ret = -EINVAL; + goto err; + } + + pname = "ti,ldovbb-vset-mask"; + ret = + of_property_read_u32(pdev->dev.of_node, pname, + &abb->ldovbb_vset_mask); + if (ret) { + dev_err(dev, "Missing '%s' (%d)\n", pname, ret); + goto err; + } + if (!abb->ldovbb_vset_mask) { + dev_err(dev, "Invalid property:'%s' set as 0!\n", pname); + ret = -EINVAL; + goto err; + } + +skip_opt: + pname = "ti,tranxdone-status-mask"; + ret = + of_property_read_u32(pdev->dev.of_node, pname, + &abb->txdone_mask); + if (ret) { + dev_err(dev, "Missing '%s' (%d)\n", pname, ret); + goto err; + } + if (!abb->txdone_mask) { + dev_err(dev, "Invalid property:'%s' set as 0!\n", pname); + ret = -EINVAL; + goto err; + } + + initdata = of_get_regulator_init_data(dev, pdev->dev.of_node); + if (!initdata) { + ret = -ENOMEM; + dev_err(dev, "%s: Unable to alloc regulator init data\n", + __func__); + goto err; + } + + /* init ABB opp_sel table */ + ret = ti_abb_init_table(dev, abb, initdata); + if (ret) + goto err; + + /* init ABB timing */ + ret = ti_abb_init_timings(dev, abb); + if (ret) + goto err; + + desc = &abb->rdesc; + desc->name = dev_name(dev); + desc->owner = THIS_MODULE; + desc->type = REGULATOR_VOLTAGE; + desc->ops = &ti_abb_reg_ops; + + c = &initdata->constraints; + if (desc->n_voltages > 1) + c->valid_ops_mask |= REGULATOR_CHANGE_VOLTAGE; + c->always_on = true; + + config.dev = dev; + config.init_data = initdata; + config.driver_data = abb; + config.of_node = pdev->dev.of_node; + + rdev = regulator_register(desc, &config); + if (IS_ERR(rdev)) { + ret = PTR_ERR(rdev); + dev_err(dev, "%s: failed to register regulator(%d)\n", + __func__, ret); + goto err; + } + platform_set_drvdata(pdev, rdev); + + /* Enable the ldo if not already done by bootloader */ + ti_abb_rmw(abb->regs->sr2_en_mask, 1, abb->regs->setup_reg, abb->base); + + return 0; + +err: + dev_err(dev, "%s: Failed to initialize(%d)\n", __func__, ret); + return ret; +} + +/** + * ti_abb_remove() - cleanups + * @pdev: ABB platform device + * + * Return: 0 + */ +static int ti_abb_remove(struct platform_device *pdev) +{ + struct regulator_dev *rdev = platform_get_drvdata(pdev); + + regulator_unregister(rdev); + return 0; +} + +MODULE_ALIAS("platform:ti_abb"); + +static struct platform_driver ti_abb_driver = { + .probe = ti_abb_probe, + .remove = ti_abb_remove, + .driver = { + .name = "ti_abb", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(ti_abb_of_match), + }, +}; +module_platform_driver(ti_abb_driver); + +MODULE_DESCRIPTION("Texas Instruments ABB LDO regulator driver"); +MODULE_AUTHOR("Texas Instruments Inc."); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From f5cd8de2ce32e25e399f1d49d4876bf9450163a2 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sat, 4 May 2013 22:02:27 +0800 Subject: regulator: ti-abb: Fix off-by-one valid range checking for abb->current_info_idx abb->current_info_idx is used as array subscript to access volt_table, thus the valid value range should be 0 ... desc->n_voltages - 1. Signed-off-by: Axel Lin Acked-by: Nishanth Menon Signed-off-by: Mark Brown --- drivers/regulator/ti-abb-regulator.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/ti-abb-regulator.c b/drivers/regulator/ti-abb-regulator.c index c1870ea64939..870d209ec866 100644 --- a/drivers/regulator/ti-abb-regulator.c +++ b/drivers/regulator/ti-abb-regulator.c @@ -387,8 +387,8 @@ static int ti_abb_get_voltage_sel(struct regulator_dev *rdev) return -EINVAL; } - if (abb->current_info_idx > (int)desc->n_voltages) { - dev_err(dev, "%s: Corrupted data? idx(%d) > n_voltages(%d)\n", + if (abb->current_info_idx >= (int)desc->n_voltages) { + dev_err(dev, "%s: Corrupted data? idx(%d) >= n_voltages(%d)\n", __func__, abb->current_info_idx, desc->n_voltages); return -EINVAL; } -- cgit v1.2.3 From 405c54009c85cf03d459f5880744b0d4ebb892e4 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 3 May 2013 20:46:10 +0800 Subject: regulator: Remove all platform_set_drvdata(pdev, NULL) in drivers Since 0998d06310 "device-core: Ensure drvdata = NULL when no driver is bound", this is done by driver core after device_release or on probe failure. Thus we can remove all platform_set_drvdata(pdev, NULL) in drivers. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/88pm8607.c | 1 - drivers/regulator/lp8788-buck.c | 1 - drivers/regulator/lp8788-ldo.c | 2 -- drivers/regulator/max8925-regulator.c | 1 - drivers/regulator/mc13783-regulator.c | 2 -- drivers/regulator/mc13892-regulator.c | 2 -- drivers/regulator/pcap-regulator.c | 1 - drivers/regulator/pcf50633-regulator.c | 1 - drivers/regulator/tps65217-regulator.c | 2 -- drivers/regulator/virtual.c | 2 -- drivers/regulator/wm831x-dcdc.c | 7 ------- drivers/regulator/wm831x-isink.c | 2 -- drivers/regulator/wm831x-ldo.c | 2 -- drivers/regulator/wm8400-regulator.c | 1 - drivers/regulator/wm8994-regulator.c | 2 -- 15 files changed, 29 deletions(-) diff --git a/drivers/regulator/88pm8607.c b/drivers/regulator/88pm8607.c index 493948a38fca..8a7cb1f43046 100644 --- a/drivers/regulator/88pm8607.c +++ b/drivers/regulator/88pm8607.c @@ -406,7 +406,6 @@ static int pm8607_regulator_remove(struct platform_device *pdev) { struct pm8607_regulator_info *info = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); regulator_unregister(info->regulator); return 0; } diff --git a/drivers/regulator/lp8788-buck.c b/drivers/regulator/lp8788-buck.c index eb1e1e88ae51..0b015f2a7fd9 100644 --- a/drivers/regulator/lp8788-buck.c +++ b/drivers/regulator/lp8788-buck.c @@ -533,7 +533,6 @@ static int lp8788_buck_remove(struct platform_device *pdev) { struct lp8788_buck *buck = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); regulator_unregister(buck->regulator); return 0; diff --git a/drivers/regulator/lp8788-ldo.c b/drivers/regulator/lp8788-ldo.c index 0ce2c4c194b3..0527d87c6dd5 100644 --- a/drivers/regulator/lp8788-ldo.c +++ b/drivers/regulator/lp8788-ldo.c @@ -561,7 +561,6 @@ static int lp8788_dldo_remove(struct platform_device *pdev) { struct lp8788_ldo *ldo = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); regulator_unregister(ldo->regulator); return 0; @@ -622,7 +621,6 @@ static int lp8788_aldo_remove(struct platform_device *pdev) { struct lp8788_ldo *ldo = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); regulator_unregister(ldo->regulator); return 0; diff --git a/drivers/regulator/max8925-regulator.c b/drivers/regulator/max8925-regulator.c index 3597da8f0dca..e6d54a546d36 100644 --- a/drivers/regulator/max8925-regulator.c +++ b/drivers/regulator/max8925-regulator.c @@ -327,7 +327,6 @@ static int max8925_regulator_remove(struct platform_device *pdev) { struct regulator_dev *rdev = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); regulator_unregister(rdev); return 0; diff --git a/drivers/regulator/mc13783-regulator.c b/drivers/regulator/mc13783-regulator.c index fdf7f0a09090..5ff99d2703db 100644 --- a/drivers/regulator/mc13783-regulator.c +++ b/drivers/regulator/mc13783-regulator.c @@ -466,8 +466,6 @@ static int mc13783_regulator_remove(struct platform_device *pdev) struct mc13xxx_regulator_priv *priv = platform_get_drvdata(pdev); int i; - platform_set_drvdata(pdev, NULL); - for (i = 0; i < priv->num_regulators; i++) regulator_unregister(priv->regulators[i]); diff --git a/drivers/regulator/mc13892-regulator.c b/drivers/regulator/mc13892-regulator.c index b716283a8760..1037e07937cf 100644 --- a/drivers/regulator/mc13892-regulator.c +++ b/drivers/regulator/mc13892-regulator.c @@ -636,8 +636,6 @@ static int mc13892_regulator_remove(struct platform_device *pdev) struct mc13xxx_regulator_priv *priv = platform_get_drvdata(pdev); int i; - platform_set_drvdata(pdev, NULL); - for (i = 0; i < priv->num_regulators; i++) regulator_unregister(priv->regulators[i]); diff --git a/drivers/regulator/pcap-regulator.c b/drivers/regulator/pcap-regulator.c index 4899342f1fc1..1a73a297fe73 100644 --- a/drivers/regulator/pcap-regulator.c +++ b/drivers/regulator/pcap-regulator.c @@ -260,7 +260,6 @@ static int pcap_regulator_remove(struct platform_device *pdev) struct regulator_dev *rdev = platform_get_drvdata(pdev); regulator_unregister(rdev); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/regulator/pcf50633-regulator.c b/drivers/regulator/pcf50633-regulator.c index 534075e13d6d..54df9f7cb504 100644 --- a/drivers/regulator/pcf50633-regulator.c +++ b/drivers/regulator/pcf50633-regulator.c @@ -106,7 +106,6 @@ static int pcf50633_regulator_remove(struct platform_device *pdev) { struct regulator_dev *rdev = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); regulator_unregister(rdev); return 0; diff --git a/drivers/regulator/tps65217-regulator.c b/drivers/regulator/tps65217-regulator.c index df395187c063..2df4616621f5 100644 --- a/drivers/regulator/tps65217-regulator.c +++ b/drivers/regulator/tps65217-regulator.c @@ -405,8 +405,6 @@ static int tps65217_regulator_remove(struct platform_device *pdev) for (i = 0; i < TPS65217_NUM_REGULATOR; i++) regulator_unregister(tps->rdev[i]); - platform_set_drvdata(pdev, NULL); - return 0; } diff --git a/drivers/regulator/virtual.c b/drivers/regulator/virtual.c index 01c66e9712a4..a9d4284ea007 100644 --- a/drivers/regulator/virtual.c +++ b/drivers/regulator/virtual.c @@ -330,8 +330,6 @@ static int regulator_virtual_remove(struct platform_device *pdev) if (drvdata->enabled) regulator_disable(drvdata->regulator); - platform_set_drvdata(pdev, NULL); - return 0; } diff --git a/drivers/regulator/wm831x-dcdc.c b/drivers/regulator/wm831x-dcdc.c index 0af6898bcd79..46938cf162ad 100644 --- a/drivers/regulator/wm831x-dcdc.c +++ b/drivers/regulator/wm831x-dcdc.c @@ -567,8 +567,6 @@ static int wm831x_buckv_remove(struct platform_device *pdev) struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev); struct wm831x *wm831x = dcdc->wm831x; - platform_set_drvdata(pdev, NULL); - free_irq(wm831x_irq(wm831x, platform_get_irq_byname(pdev, "HC")), dcdc); free_irq(wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV")), @@ -714,8 +712,6 @@ static int wm831x_buckp_remove(struct platform_device *pdev) { struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); - free_irq(wm831x_irq(dcdc->wm831x, platform_get_irq_byname(pdev, "UV")), dcdc); regulator_unregister(dcdc->regulator); @@ -849,8 +845,6 @@ static int wm831x_boostp_remove(struct platform_device *pdev) { struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); - free_irq(wm831x_irq(dcdc->wm831x, platform_get_irq_byname(pdev, "UV")), dcdc); regulator_unregister(dcdc->regulator); @@ -940,7 +934,6 @@ static int wm831x_epe_remove(struct platform_device *pdev) { struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); regulator_unregister(dcdc->regulator); return 0; diff --git a/drivers/regulator/wm831x-isink.c b/drivers/regulator/wm831x-isink.c index 68586ee3e1cb..16ebdf94d0a0 100644 --- a/drivers/regulator/wm831x-isink.c +++ b/drivers/regulator/wm831x-isink.c @@ -225,8 +225,6 @@ static int wm831x_isink_remove(struct platform_device *pdev) { struct wm831x_isink *isink = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); - free_irq(wm831x_irq(isink->wm831x, platform_get_irq(pdev, 0)), isink); regulator_unregister(isink->regulator); diff --git a/drivers/regulator/wm831x-ldo.c b/drivers/regulator/wm831x-ldo.c index 1ec379a9a95c..9ff883f80878 100644 --- a/drivers/regulator/wm831x-ldo.c +++ b/drivers/regulator/wm831x-ldo.c @@ -338,8 +338,6 @@ static int wm831x_gp_ldo_remove(struct platform_device *pdev) { struct wm831x_ldo *ldo = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); - free_irq(wm831x_irq(ldo->wm831x, platform_get_irq_byname(pdev, "UV")), ldo); regulator_unregister(ldo->regulator); diff --git a/drivers/regulator/wm8400-regulator.c b/drivers/regulator/wm8400-regulator.c index c6a32ea80b9d..a09f03ee5506 100644 --- a/drivers/regulator/wm8400-regulator.c +++ b/drivers/regulator/wm8400-regulator.c @@ -250,7 +250,6 @@ static int wm8400_regulator_remove(struct platform_device *pdev) { struct regulator_dev *rdev = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); regulator_unregister(rdev); return 0; diff --git a/drivers/regulator/wm8994-regulator.c b/drivers/regulator/wm8994-regulator.c index a612c356a697..8f2a8a7a3f99 100644 --- a/drivers/regulator/wm8994-regulator.c +++ b/drivers/regulator/wm8994-regulator.c @@ -185,8 +185,6 @@ static int wm8994_ldo_remove(struct platform_device *pdev) { struct wm8994_ldo *ldo = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); - regulator_unregister(ldo->regulator); return 0; -- cgit v1.2.3 From 85bea1bf33f6e66221c5818332382f2ed9f2ad0e Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 8 May 2013 16:09:05 +0530 Subject: regulator: isl6271a: Use NULL instead of 0 init_data is a pointer. Use NULL instead of 0. Silences the following sparse warning: drivers/regulator/isl6271a-regulator.c:133:44: warning: Using plain integer as NULL pointer Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown --- drivers/regulator/isl6271a-regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/isl6271a-regulator.c b/drivers/regulator/isl6271a-regulator.c index d1e5bee2a26b..b99c49b9aff0 100644 --- a/drivers/regulator/isl6271a-regulator.c +++ b/drivers/regulator/isl6271a-regulator.c @@ -130,7 +130,7 @@ static int isl6271a_probe(struct i2c_client *i2c, if (i == 0) config.init_data = init_data; else - config.init_data = 0; + config.init_data = NULL; config.driver_data = pmic; pmic->rdev[i] = regulator_register(&isl_rd[i], &config); -- cgit v1.2.3 From 1586bb4a929688f2c5ba10b62eec6bd61eddaf6e Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 18 Apr 2013 14:52:55 +0800 Subject: regulator: Remove unnecessary include of linux/delay.h from regulator drivers All the drivers that need delay for the regulator voltage output voltage to stabilize after being enabled or after being set to a new value has been converted to implement enable_time and set_voltage_time_sel callbacks. Then regulator core will take care of the necessary delay. This patch removes the unneeded include of linux/delay.h in regulator drivers. Signed-off-by: Axel Lin Cc: Daniel Jeong Cc: Sangbeom Kim Cc: Chiwoong Byun Signed-off-by: Mark Brown --- drivers/regulator/lp8755.c | 1 - drivers/regulator/max77686.c | 1 - drivers/regulator/s2mps11.c | 1 - 3 files changed, 3 deletions(-) diff --git a/drivers/regulator/lp8755.c b/drivers/regulator/lp8755.c index f0f6ea05065b..d9e38b4c2adc 100644 --- a/drivers/regulator/lp8755.c +++ b/drivers/regulator/lp8755.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/regulator/max77686.c b/drivers/regulator/max77686.c index 20935b1a6ed4..f563057e5690 100644 --- a/drivers/regulator/max77686.c +++ b/drivers/regulator/max77686.c @@ -24,7 +24,6 @@ #include #include -#include #include #include #include diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c index cd9ea2ea1826..c9f16e17920f 100644 --- a/drivers/regulator/s2mps11.c +++ b/drivers/regulator/s2mps11.c @@ -12,7 +12,6 @@ */ #include -#include #include #include #include -- cgit v1.2.3 From 697e85bc6a9aa44ecd73392586fe9cfd7e0467ba Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 8 May 2013 13:55:22 +0100 Subject: regmap: Add support for discarding parts of the register cache Allow drivers to discard parts of the register cache, for example if part of the hardware has been reset. Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 1 + drivers/base/regmap/regcache.c | 37 +++++++++++++++++++++++++++++++++++++ include/linux/regmap.h | 9 +++++++++ include/trace/events/regmap.h | 23 +++++++++++++++++++++++ 4 files changed, 70 insertions(+) diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index c130536e0ab0..b33a4ff67adf 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -148,6 +148,7 @@ struct regcache_ops { int (*read)(struct regmap *map, unsigned int reg, unsigned int *value); int (*write)(struct regmap *map, unsigned int reg, unsigned int value); int (*sync)(struct regmap *map, unsigned int min, unsigned int max); + int (*drop)(struct regmap *map, unsigned int min, unsigned int max); }; bool regmap_writeable(struct regmap *map, unsigned int reg); diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 75923f2396bd..8a0ab5fa75f5 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -358,6 +358,43 @@ out: } EXPORT_SYMBOL_GPL(regcache_sync_region); +/** + * regcache_drop_region: Discard part of the register cache + * + * @map: map to operate on + * @min: first register to discard + * @max: last register to discard + * + * Discard part of the register cache. + * + * Return a negative value on failure, 0 on success. + */ +int regcache_drop_region(struct regmap *map, unsigned int min, + unsigned int max) +{ + unsigned int reg; + int ret = 0; + + if (!map->cache_present && !(map->cache_ops && map->cache_ops->drop)) + return -EINVAL; + + map->lock(map); + + trace_regcache_drop_region(map->dev, min, max); + + if (map->cache_present) + for (reg = min; reg < max + 1; reg++) + clear_bit(reg, map->cache_present); + + if (map->cache_ops && map->cache_ops->drop) + ret = map->cache_ops->drop(map, min, max); + + map->unlock(map); + + return ret; +} +EXPORT_SYMBOL_GPL(regcache_drop_region); + /** * regcache_cache_only: Put a register map into cache only mode * diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 02d84e24b7c2..5067ee94eb92 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -394,6 +394,8 @@ bool regmap_can_raw_write(struct regmap *map); int regcache_sync(struct regmap *map); int regcache_sync_region(struct regmap *map, unsigned int min, unsigned int max); +int regcache_drop_region(struct regmap *map, unsigned int min, + unsigned int max); void regcache_cache_only(struct regmap *map, bool enable); void regcache_cache_bypass(struct regmap *map, bool enable); void regcache_mark_dirty(struct regmap *map); @@ -562,6 +564,13 @@ static inline int regcache_sync_region(struct regmap *map, unsigned int min, return -EINVAL; } +static inline int regcache_drop_region(struct regmap *map, unsigned int min, + unsigned int max) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + static inline void regcache_cache_only(struct regmap *map, bool enable) { WARN_ONCE(1, "regmap API is disabled"); diff --git a/include/trace/events/regmap.h b/include/trace/events/regmap.h index a43a2f67bd8e..23d561512f64 100644 --- a/include/trace/events/regmap.h +++ b/include/trace/events/regmap.h @@ -223,6 +223,29 @@ DEFINE_EVENT(regmap_async, regmap_async_complete_done, ); +TRACE_EVENT(regcache_drop_region, + + TP_PROTO(struct device *dev, unsigned int from, + unsigned int to), + + TP_ARGS(dev, from, to), + + TP_STRUCT__entry( + __string( name, dev_name(dev) ) + __field( unsigned int, from ) + __field( unsigned int, to ) + ), + + TP_fast_assign( + __assign_str(name, dev_name(dev)); + __entry->from = from; + __entry->to = to; + ), + + TP_printk("%s %u-%u", __get_str(name), (unsigned int)__entry->from, + (unsigned int)__entry->to) +); + #endif /* _TRACE_REGMAP_H */ /* This part must be outside protection */ -- cgit v1.2.3 From 154881e59b8dbf84121e3e78c4e613e840752aa9 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 8 May 2013 13:55:23 +0100 Subject: regmap: Make regmap_check_range_table() a public API Allow drivers to use an access table as part of their implementation. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 14 +++++++------- include/linux/regmap.h | 3 +++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index a941dcfe7590..307f5a1c1fe8 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -65,9 +65,8 @@ bool regmap_reg_in_ranges(unsigned int reg, } EXPORT_SYMBOL_GPL(regmap_reg_in_ranges); -static bool _regmap_check_range_table(struct regmap *map, - unsigned int reg, - const struct regmap_access_table *table) +bool regmap_check_range_table(struct regmap *map, unsigned int reg, + const struct regmap_access_table *table) { /* Check "no ranges" first */ if (regmap_reg_in_ranges(reg, table->no_ranges, table->n_no_ranges)) @@ -80,6 +79,7 @@ static bool _regmap_check_range_table(struct regmap *map, return regmap_reg_in_ranges(reg, table->yes_ranges, table->n_yes_ranges); } +EXPORT_SYMBOL_GPL(regmap_check_range_table); bool regmap_writeable(struct regmap *map, unsigned int reg) { @@ -90,7 +90,7 @@ bool regmap_writeable(struct regmap *map, unsigned int reg) return map->writeable_reg(map->dev, reg); if (map->wr_table) - return _regmap_check_range_table(map, reg, map->wr_table); + return regmap_check_range_table(map, reg, map->wr_table); return true; } @@ -107,7 +107,7 @@ bool regmap_readable(struct regmap *map, unsigned int reg) return map->readable_reg(map->dev, reg); if (map->rd_table) - return _regmap_check_range_table(map, reg, map->rd_table); + return regmap_check_range_table(map, reg, map->rd_table); return true; } @@ -121,7 +121,7 @@ bool regmap_volatile(struct regmap *map, unsigned int reg) return map->volatile_reg(map->dev, reg); if (map->volatile_table) - return _regmap_check_range_table(map, reg, map->volatile_table); + return regmap_check_range_table(map, reg, map->volatile_table); return true; } @@ -135,7 +135,7 @@ bool regmap_precious(struct regmap *map, unsigned int reg) return map->precious_reg(map->dev, reg); if (map->precious_table) - return _regmap_check_range_table(map, reg, map->precious_table); + return regmap_check_range_table(map, reg, map->precious_table); return false; } diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 5067ee94eb92..d6f3221e29d4 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -400,6 +400,9 @@ void regcache_cache_only(struct regmap *map, bool enable); void regcache_cache_bypass(struct regmap *map, bool enable); void regcache_mark_dirty(struct regmap *map); +bool regmap_check_range_table(struct regmap *map, unsigned int reg, + const struct regmap_access_table *table); + int regmap_register_patch(struct regmap *map, const struct reg_default *regs, int num_regs); -- cgit v1.2.3 From 0186645d2549f94c3a8067c97cad261c678d6718 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 8 May 2013 13:55:24 +0100 Subject: regmap: rbtree: Factor out node allocation In preparation for being slightly smarter about how we allocate memory factor out the node allocation. Signed-off-by: Mark Brown --- drivers/base/regmap/regcache-rbtree.c | 41 +++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index aa0875f6f1b7..3a000145ac6e 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -304,6 +304,27 @@ static int regcache_rbtree_insert_to_block(struct regmap *map, return 0; } +static struct regcache_rbtree_node * +regcache_rbtree_node_alloc(struct regmap *map, unsigned int reg) +{ + struct regcache_rbtree_node *rbnode; + + rbnode = kzalloc(sizeof(*rbnode), GFP_KERNEL); + if (!rbnode) + return NULL; + + rbnode->blklen = sizeof(*rbnode); + rbnode->base_reg = reg; + rbnode->block = kmalloc(rbnode->blklen * map->cache_word_size, + GFP_KERNEL); + if (!rbnode->block) { + kfree(rbnode); + return NULL; + } + + return rbnode; +} + static int regcache_rbtree_write(struct regmap *map, unsigned int reg, unsigned int value) { @@ -354,23 +375,15 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, return 0; } } - /* we did not manage to find a place to insert it in an existing - * block so create a new rbnode with a single register in its block. - * This block will get populated further if any other adjacent - * registers get modified in the future. + + /* We did not manage to find a place to insert it in + * an existing block so create a new rbnode. */ - rbnode = kzalloc(sizeof *rbnode, GFP_KERNEL); + rbnode = regcache_rbtree_node_alloc(map, reg); if (!rbnode) return -ENOMEM; - rbnode->blklen = sizeof(*rbnode); - rbnode->base_reg = reg; - rbnode->block = kmalloc(rbnode->blklen * map->cache_word_size, - GFP_KERNEL); - if (!rbnode->block) { - kfree(rbnode); - return -ENOMEM; - } - regcache_rbtree_set_register(map, rbnode, 0, value); + regcache_rbtree_set_register(map, rbnode, + reg - rbnode->base_reg, value); regcache_rbtree_insert(map, &rbtree_ctx->root, rbnode); rbtree_ctx->cached_rbnode = rbnode; } -- cgit v1.2.3 From 7278af5fb3eb7247449fd4489dacb75b9ba86f73 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 8 May 2013 13:55:25 +0100 Subject: regmap: rbtree: Use range information to allocate nodes If range information has been provided then when we allocate a rbnode within a range allocate the entire range. The goal is to minimise the number of reallocations done when combining or extending blocks. At present only readability and yes_ranges are taken into account, this is expected to cover most cases efficiently. Signed-off-by: Mark Brown --- drivers/base/regmap/regcache-rbtree.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index 3a000145ac6e..4e131c5df844 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -308,13 +308,34 @@ static struct regcache_rbtree_node * regcache_rbtree_node_alloc(struct regmap *map, unsigned int reg) { struct regcache_rbtree_node *rbnode; + const struct regmap_range *range; + int i; rbnode = kzalloc(sizeof(*rbnode), GFP_KERNEL); if (!rbnode) return NULL; - rbnode->blklen = sizeof(*rbnode); - rbnode->base_reg = reg; + /* If there is a read table then use it to guess at an allocation */ + if (map->rd_table) { + for (i = 0; i < map->rd_table->n_yes_ranges; i++) { + if (regmap_reg_in_range(reg, + &map->rd_table->yes_ranges[i])) + break; + } + + if (i != map->rd_table->n_yes_ranges) { + range = &map->rd_table->yes_ranges[i]; + rbnode->blklen = range->range_max - range->range_min + + 1; + rbnode->base_reg = range->range_min; + } + } + + if (!rbnode->blklen) { + rbnode->blklen = sizeof(*rbnode); + rbnode->base_reg = reg; + } + rbnode->block = kmalloc(rbnode->blklen * map->cache_word_size, GFP_KERNEL); if (!rbnode->block) { -- cgit v1.2.3 From f9e464a566a1c3c9b71558e5523288b5432c3e3f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 9 May 2013 14:35:49 +0100 Subject: regmap: debugfs: Don't mark lockdep as broken due to debugfs write A register write to hardware is reasonably unlikely to cause locking dependency issues, the reason we're tainting is that unexpected changes in the hardware configuration may confuse drivers. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 975719bc3450..8772fb77e6ed 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -281,7 +281,7 @@ static ssize_t regmap_map_write_file(struct file *file, return -EINVAL; /* Userspace has been fiddling around behind the kernel's back */ - add_taint(TAINT_USER, LOCKDEP_NOW_UNRELIABLE); + add_taint(TAINT_USER, LOCKDEP_STILL_OK); ret = regmap_write(map, reg, value); if (ret < 0) -- cgit v1.2.3 From b1d9e66c1c8d0e4ad29328c4f37266f292d722d3 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 3 May 2013 14:39:23 +0530 Subject: ASoC: 88pm860x: Remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown --- sound/soc/codecs/88pm860x-codec.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c index 60159c07448d..1382f3f3f4bf 100644 --- a/sound/soc/codecs/88pm860x-codec.c +++ b/sound/soc/codecs/88pm860x-codec.c @@ -1444,7 +1444,7 @@ static int pm860x_codec_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_IRQ, i); if (!res) { dev_err(&pdev->dev, "Failed to get IRQ resources\n"); - goto out; + return -EINVAL; } pm860x->irq[i] = res->start + chip->irq_base; strncpy(pm860x->name[i], res->name, MAX_NAME_LEN); @@ -1454,19 +1454,14 @@ static int pm860x_codec_probe(struct platform_device *pdev) pm860x_dai, ARRAY_SIZE(pm860x_dai)); if (ret) { dev_err(&pdev->dev, "Failed to register codec\n"); - goto out; + return -EINVAL; } return ret; - -out: - platform_set_drvdata(pdev, NULL); - return -EINVAL; } static int pm860x_codec_remove(struct platform_device *pdev) { snd_soc_unregister_codec(&pdev->dev); - platform_set_drvdata(pdev, NULL); return 0; } -- cgit v1.2.3 From 6ab2b7b415441fa46357bef883e1ead086de1387 Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Wed, 8 May 2013 14:15:35 +0100 Subject: ASoC: wm_adsp: Expose coefficient blocks as ALSA binary controls Add initial support for runtime tuning for the ADSP cores. This is achieved by exposing the coefficient configuration blocks as ALSA binary controls. The current code assumes that no controls on the DSP are volatile. Signed-off-by: Dimitris Papastamos Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 458 ++++++++++++++++++++++++++++++++++++++++++++- sound/soc/codecs/wm_adsp.h | 3 + 2 files changed, 454 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 3470b649c0b2..137830611928 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -215,6 +216,36 @@ static struct { [WM_ADSP_FW_RX_ANC] = { .file = "rx-anc" }, }; +struct wm_coeff_ctl_ops { + int (*xget)(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + int (*xput)(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + int (*xinfo)(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo); +}; + +struct wm_coeff { + struct device *dev; + struct list_head ctl_list; + struct regmap *regmap; +}; + +struct wm_coeff_ctl { + const char *name; + struct snd_card *card; + struct wm_adsp_alg_region region; + struct wm_coeff_ctl_ops ops; + struct wm_adsp *adsp; + void *private; + unsigned int enabled:1; + struct list_head list; + void *cache; + size_t len; + unsigned int dirty:1; + struct snd_kcontrol *kcontrol; +}; + static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -334,6 +365,181 @@ static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *region, } } +static int wm_coeff_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = ctl->len; + return 0; +} + +static int wm_coeff_write_control(struct snd_kcontrol *kcontrol, + const void *buf, size_t len) +{ + struct wm_coeff *wm_coeff= snd_kcontrol_chip(kcontrol); + struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; + struct wm_adsp_alg_region *region = &ctl->region; + const struct wm_adsp_region *mem; + struct wm_adsp *adsp = ctl->adsp; + void *scratch; + int ret; + unsigned int reg; + + mem = wm_adsp_find_region(adsp, region->type); + if (!mem) { + adsp_err(adsp, "No base for region %x\n", + region->type); + return -EINVAL; + } + + reg = ctl->region.base; + reg = wm_adsp_region_to_reg(mem, reg); + + scratch = kmemdup(buf, ctl->len, GFP_KERNEL | GFP_DMA); + if (!scratch) + return -ENOMEM; + + ret = regmap_raw_write(wm_coeff->regmap, reg, scratch, + ctl->len); + if (ret) { + adsp_err(adsp, "Failed to write %zu bytes to %x\n", + ctl->len, reg); + kfree(scratch); + return ret; + } + + kfree(scratch); + + return 0; +} + +static int wm_coeff_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; + char *p = ucontrol->value.bytes.data; + + memcpy(ctl->cache, p, ctl->len); + + if (!ctl->enabled) { + ctl->dirty = 1; + return 0; + } + + return wm_coeff_write_control(kcontrol, p, ctl->len); +} + +static int wm_coeff_read_control(struct snd_kcontrol *kcontrol, + void *buf, size_t len) +{ + struct wm_coeff *wm_coeff= snd_kcontrol_chip(kcontrol); + struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; + struct wm_adsp_alg_region *region = &ctl->region; + const struct wm_adsp_region *mem; + struct wm_adsp *adsp = ctl->adsp; + void *scratch; + int ret; + unsigned int reg; + + mem = wm_adsp_find_region(adsp, region->type); + if (!mem) { + adsp_err(adsp, "No base for region %x\n", + region->type); + return -EINVAL; + } + + reg = ctl->region.base; + reg = wm_adsp_region_to_reg(mem, reg); + + scratch = kmalloc(ctl->len, GFP_KERNEL | GFP_DMA); + if (!scratch) + return -ENOMEM; + + ret = regmap_raw_read(wm_coeff->regmap, reg, scratch, ctl->len); + if (ret) { + adsp_err(adsp, "Failed to read %zu bytes from %x\n", + ctl->len, reg); + kfree(scratch); + return ret; + } + + memcpy(buf, scratch, ctl->len); + kfree(scratch); + + return 0; +} + +static int wm_coeff_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; + char *p = ucontrol->value.bytes.data; + + memcpy(p, ctl->cache, ctl->len); + return 0; +} + +static int wm_coeff_add_kcontrol(struct wm_coeff *wm_coeff, + struct wm_coeff_ctl *ctl, + const struct snd_kcontrol_new *kctl) +{ + int ret; + struct snd_kcontrol *kcontrol; + + kcontrol = snd_ctl_new1(kctl, wm_coeff); + ret = snd_ctl_add(ctl->card, kcontrol); + if (ret < 0) { + dev_err(wm_coeff->dev, "Failed to add %s: %d\n", + kctl->name, ret); + return ret; + } + ctl->kcontrol = kcontrol; + return 0; +} + +struct wmfw_ctl_work { + struct wm_coeff *wm_coeff; + struct wm_coeff_ctl *ctl; + struct work_struct work; +}; + +static int wmfw_add_ctl(struct wm_coeff *wm_coeff, + struct wm_coeff_ctl *ctl) +{ + struct snd_kcontrol_new *kcontrol; + int ret; + + if (!wm_coeff || !ctl || !ctl->name || !ctl->card) + return -EINVAL; + + kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL); + if (!kcontrol) + return -ENOMEM; + kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER; + + kcontrol->name = ctl->name; + kcontrol->info = wm_coeff_info; + kcontrol->get = wm_coeff_get; + kcontrol->put = wm_coeff_put; + kcontrol->private_value = (unsigned long)ctl; + + ret = wm_coeff_add_kcontrol(wm_coeff, + ctl, kcontrol); + if (ret < 0) + goto err_kcontrol; + + kfree(kcontrol); + + list_add(&ctl->list, &wm_coeff->ctl_list); + return 0; + +err_kcontrol: + kfree(kcontrol); + return ret; +} + static int wm_adsp_load(struct wm_adsp *dsp) { LIST_HEAD(buf_list); @@ -547,7 +753,156 @@ out: return ret; } -static int wm_adsp_setup_algs(struct wm_adsp *dsp) +static int wm_coeff_init_control_caches(struct wm_coeff *wm_coeff) +{ + struct wm_coeff_ctl *ctl; + int ret; + + list_for_each_entry(ctl, &wm_coeff->ctl_list, + list) { + if (!ctl->enabled || ctl->dirty) + continue; + ret = wm_coeff_read_control(ctl->kcontrol, + ctl->cache, + ctl->len); + if (ret < 0) + return ret; + } + + return 0; +} + +static int wm_coeff_sync_controls(struct wm_coeff *wm_coeff) +{ + struct wm_coeff_ctl *ctl; + int ret; + + list_for_each_entry(ctl, &wm_coeff->ctl_list, + list) { + if (!ctl->enabled) + continue; + if (ctl->dirty) { + ret = wm_coeff_write_control(ctl->kcontrol, + ctl->cache, + ctl->len); + if (ret < 0) + return ret; + ctl->dirty = 0; + } + } + + return 0; +} + +static void wm_adsp_ctl_work(struct work_struct *work) +{ + struct wmfw_ctl_work *ctl_work = container_of(work, + struct wmfw_ctl_work, + work); + + wmfw_add_ctl(ctl_work->wm_coeff, ctl_work->ctl); + kfree(ctl_work); +} + +static int wm_adsp_create_control(struct snd_soc_codec *codec, + const struct wm_adsp_alg_region *region) + +{ + struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec); + struct wm_coeff_ctl *ctl; + struct wmfw_ctl_work *ctl_work; + char *name; + char *region_name; + int ret; + + name = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!name) + return -ENOMEM; + + switch (region->type) { + case WMFW_ADSP1_PM: + region_name = "PM"; + break; + case WMFW_ADSP1_DM: + region_name = "DM"; + break; + case WMFW_ADSP2_XM: + region_name = "XM"; + break; + case WMFW_ADSP2_YM: + region_name = "YM"; + break; + case WMFW_ADSP1_ZM: + region_name = "ZM"; + break; + default: + return -EINVAL; + } + + snprintf(name, PAGE_SIZE, "DSP%d %s %x", + dsp->num, region_name, region->alg); + + list_for_each_entry(ctl, &dsp->wm_coeff->ctl_list, + list) { + if (!strcmp(ctl->name, name)) { + if (!ctl->enabled) + ctl->enabled = 1; + return 0; + } + } + + ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); + if (!ctl) { + ret = -ENOMEM; + goto err_name; + } + ctl->region = *region; + ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL); + if (!ctl->name) { + ret = -ENOMEM; + goto err_ctl; + } + ctl->enabled = 1; + ctl->dirty = 0; + ctl->ops.xget = wm_coeff_get; + ctl->ops.xput = wm_coeff_put; + ctl->card = codec->card->snd_card; + ctl->adsp = dsp; + + ctl->len = region->len; + ctl->cache = kzalloc(ctl->len, GFP_KERNEL); + if (!ctl->cache) { + ret = -ENOMEM; + goto err_ctl_name; + } + + ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL); + if (!ctl_work) { + ret = -ENOMEM; + goto err_ctl_cache; + } + + ctl_work->wm_coeff = dsp->wm_coeff; + ctl_work->ctl = ctl; + INIT_WORK(&ctl_work->work, wm_adsp_ctl_work); + schedule_work(&ctl_work->work); + + kfree(name); + + return 0; + +err_ctl_cache: + kfree(ctl->cache); +err_ctl_name: + kfree(ctl->name); +err_ctl: + kfree(ctl); +err_name: + kfree(name); + return ret; +} + +static int wm_adsp_setup_algs(struct wm_adsp *dsp, struct snd_soc_codec *codec) { struct regmap *regmap = dsp->regmap; struct wmfw_adsp1_id_hdr adsp1_id; @@ -730,7 +1085,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) region->type = WMFW_ADSP1_DM; region->alg = be32_to_cpu(adsp1_alg[i].alg.id); region->base = be32_to_cpu(adsp1_alg[i].dm); + region->len = 0; list_add_tail(®ion->list, &dsp->alg_regions); + if (i + 1 < algs) { + region->len = be32_to_cpu(adsp1_alg[i + 1].dm); + region->len -= be32_to_cpu(adsp1_alg[i].dm); + wm_adsp_create_control(codec, region); + } else { + adsp_warn(dsp, "Missing length info for region DM with ID %x\n", + be32_to_cpu(adsp1_alg[i].alg.id)); + } region = kzalloc(sizeof(*region), GFP_KERNEL); if (!region) @@ -738,7 +1102,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) region->type = WMFW_ADSP1_ZM; region->alg = be32_to_cpu(adsp1_alg[i].alg.id); region->base = be32_to_cpu(adsp1_alg[i].zm); + region->len = 0; list_add_tail(®ion->list, &dsp->alg_regions); + if (i + 1 < algs) { + region->len = be32_to_cpu(adsp1_alg[i + 1].zm); + region->len -= be32_to_cpu(adsp1_alg[i].zm); + wm_adsp_create_control(codec, region); + } else { + adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", + be32_to_cpu(adsp1_alg[i].alg.id)); + } break; case WMFW_ADSP2: @@ -758,7 +1131,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) region->type = WMFW_ADSP2_XM; region->alg = be32_to_cpu(adsp2_alg[i].alg.id); region->base = be32_to_cpu(adsp2_alg[i].xm); + region->len = 0; list_add_tail(®ion->list, &dsp->alg_regions); + if (i + 1 < algs) { + region->len = be32_to_cpu(adsp2_alg[i + 1].xm); + region->len -= be32_to_cpu(adsp2_alg[i].xm); + wm_adsp_create_control(codec, region); + } else { + adsp_warn(dsp, "Missing length info for region XM with ID %x\n", + be32_to_cpu(adsp2_alg[i].alg.id)); + } region = kzalloc(sizeof(*region), GFP_KERNEL); if (!region) @@ -766,7 +1148,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) region->type = WMFW_ADSP2_YM; region->alg = be32_to_cpu(adsp2_alg[i].alg.id); region->base = be32_to_cpu(adsp2_alg[i].ym); + region->len = 0; list_add_tail(®ion->list, &dsp->alg_regions); + if (i + 1 < algs) { + region->len = be32_to_cpu(adsp2_alg[i + 1].ym); + region->len -= be32_to_cpu(adsp2_alg[i].ym); + wm_adsp_create_control(codec, region); + } else { + adsp_warn(dsp, "Missing length info for region YM with ID %x\n", + be32_to_cpu(adsp2_alg[i].alg.id)); + } region = kzalloc(sizeof(*region), GFP_KERNEL); if (!region) @@ -774,7 +1165,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp) region->type = WMFW_ADSP2_ZM; region->alg = be32_to_cpu(adsp2_alg[i].alg.id); region->base = be32_to_cpu(adsp2_alg[i].zm); + region->len = 0; list_add_tail(®ion->list, &dsp->alg_regions); + if (i + 1 < algs) { + region->len = be32_to_cpu(adsp2_alg[i + 1].zm); + region->len -= be32_to_cpu(adsp2_alg[i].zm); + wm_adsp_create_control(codec, region); + } else { + adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", + be32_to_cpu(adsp2_alg[i].alg.id)); + } break; } } @@ -986,6 +1386,7 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, struct snd_soc_codec *codec = w->codec; struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); struct wm_adsp *dsp = &dsps[w->shift]; + struct wm_coeff_ctl *ctl; int ret; int val; @@ -1023,7 +1424,7 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, if (ret != 0) goto err; - ret = wm_adsp_setup_algs(dsp); + ret = wm_adsp_setup_algs(dsp, codec); if (ret != 0) goto err; @@ -1031,6 +1432,16 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, if (ret != 0) goto err; + /* Initialize caches for enabled and non-dirty controls */ + ret = wm_coeff_init_control_caches(dsp->wm_coeff); + if (ret != 0) + goto err; + + /* Sync dirty controls */ + ret = wm_coeff_sync_controls(dsp->wm_coeff); + if (ret != 0) + goto err; + /* Start the core running */ regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, ADSP1_CORE_ENA | ADSP1_START, @@ -1047,6 +1458,11 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, ADSP1_SYS_ENA, 0); + + list_for_each_entry(ctl, &dsp->wm_coeff->ctl_list, + list) { + ctl->enabled = 0; + } break; default: @@ -1099,6 +1515,7 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); struct wm_adsp *dsp = &dsps[w->shift]; struct wm_adsp_alg_region *alg_region; + struct wm_coeff_ctl *ctl; unsigned int val; int ret; @@ -1164,7 +1581,7 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, if (ret != 0) goto err; - ret = wm_adsp_setup_algs(dsp); + ret = wm_adsp_setup_algs(dsp, codec); if (ret != 0) goto err; @@ -1172,6 +1589,16 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, if (ret != 0) goto err; + /* Initialize caches for enabled and non-dirty controls */ + ret = wm_coeff_init_control_caches(dsp->wm_coeff); + if (ret != 0) + goto err; + + /* Sync dirty controls */ + ret = wm_coeff_sync_controls(dsp->wm_coeff); + if (ret != 0) + goto err; + ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, ADSP2_CORE_ENA | ADSP2_START, @@ -1209,6 +1636,11 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, ret); } + list_for_each_entry(ctl, &dsp->wm_coeff->ctl_list, + list) { + ctl->enabled = 0; + } + while (!list_empty(&dsp->alg_regions)) { alg_region = list_first_entry(&dsp->alg_regions, struct wm_adsp_alg_region, @@ -1247,36 +1679,48 @@ int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs) INIT_LIST_HEAD(&adsp->alg_regions); + adsp->wm_coeff = kzalloc(sizeof(*adsp->wm_coeff), + GFP_KERNEL); + if (!adsp->wm_coeff) + return -ENOMEM; + adsp->wm_coeff->regmap = adsp->regmap; + adsp->wm_coeff->dev = adsp->dev; + INIT_LIST_HEAD(&adsp->wm_coeff->ctl_list); + if (dvfs) { adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD"); if (IS_ERR(adsp->dvfs)) { ret = PTR_ERR(adsp->dvfs); dev_err(adsp->dev, "Failed to get DCVDD: %d\n", ret); - return ret; + goto out_coeff; } ret = regulator_enable(adsp->dvfs); if (ret != 0) { dev_err(adsp->dev, "Failed to enable DCVDD: %d\n", ret); - return ret; + goto out_coeff; } ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000); if (ret != 0) { dev_err(adsp->dev, "Failed to initialise DVFS: %d\n", ret); - return ret; + goto out_coeff; } ret = regulator_disable(adsp->dvfs); if (ret != 0) { dev_err(adsp->dev, "Failed to disable DCVDD: %d\n", ret); - return ret; + goto out_coeff; } } return 0; + +out_coeff: + kfree(adsp->wm_coeff); + return ret; } EXPORT_SYMBOL_GPL(wm_adsp2_init); diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index fea514627526..6e890b916592 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -30,6 +30,7 @@ struct wm_adsp_alg_region { unsigned int alg; int type; unsigned int base; + size_t len; }; struct wm_adsp { @@ -55,6 +56,8 @@ struct wm_adsp { bool running; struct regulator *dvfs; + + struct wm_coeff *wm_coeff; }; #define WM_ADSP1(wname, num) \ -- cgit v1.2.3 From 9b74fad508049471a8c783b9960b7834aba76293 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 6 May 2013 20:26:03 +0100 Subject: ASoC: sam9g20ek: Let device core handle pinctrl Since commit ab78029 (drivers/pinctrl: grab default handles from device core) we can rely on device core for handling pinctrl so remove devm_pinctrl_get_select_default() from the driver. Signed-off-by: Mark Brown Tested-by: Bo Shen Acked-by: Bo Shen --- sound/soc/atmel/sam9g20_wm8731.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c index 2d6fbd0125b9..802717eccbd0 100644 --- a/sound/soc/atmel/sam9g20_wm8731.c +++ b/sound/soc/atmel/sam9g20_wm8731.c @@ -38,8 +38,6 @@ #include #include -#include - #include #include @@ -203,15 +201,8 @@ static int at91sam9g20ek_audio_probe(struct platform_device *pdev) struct device_node *codec_np, *cpu_np; struct clk *pllb; struct snd_soc_card *card = &snd_soc_at91sam9g20ek; - struct pinctrl *pinctrl; int ret; - pinctrl = devm_pinctrl_get_select_default(&pdev->dev); - if (IS_ERR(pinctrl)) { - dev_err(&pdev->dev, "Failed to request pinctrl for mck\n"); - return PTR_ERR(pinctrl); - } - if (!np) { if (!(machine_is_at91sam9g20ek() || machine_is_at91sam9g20ek_2mmc())) -- cgit v1.2.3 From 10e8aa9af170886266618370e2ef330e0b2055bb Mon Sep 17 00:00:00 2001 From: Michał Mirosław Date: Sat, 4 May 2013 22:21:38 +0200 Subject: ASoC: fix kernel message grepability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Mirosław Signed-off-by: Mark Brown --- sound/soc/soc-core.c | 77 +++++++++++++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 34 deletions(-) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index d56bbea6e75e..308895a438d6 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -272,8 +272,8 @@ static void soc_init_codec_debugfs(struct snd_soc_codec *codec) codec->debugfs_codec_root = debugfs_create_dir(codec->name, debugfs_card_root); if (!codec->debugfs_codec_root) { - dev_warn(codec->dev, "ASoC: Failed to create codec debugfs" - " directory\n"); + dev_warn(codec->dev, + "ASoC: Failed to create codec debugfs directory\n"); return; } @@ -286,8 +286,8 @@ static void soc_init_codec_debugfs(struct snd_soc_codec *codec) codec->debugfs_codec_root, codec, &codec_reg_fops); if (!codec->debugfs_reg) - dev_warn(codec->dev, "ASoC: Failed to create codec register" - " debugfs file\n"); + dev_warn(codec->dev, + "ASoC: Failed to create codec register debugfs file\n"); snd_soc_dapm_debugfs_init(&codec->dapm, codec->debugfs_codec_root); } @@ -631,8 +631,7 @@ int snd_soc_suspend(struct device *dev) */ if (codec->dapm.idle_bias_off) { dev_dbg(codec->dev, - "ASoC: idle_bias_off CODEC on" - " over suspend\n"); + "ASoC: idle_bias_off CODEC on over suspend\n"); break; } case SND_SOC_BIAS_OFF: @@ -643,8 +642,8 @@ int snd_soc_suspend(struct device *dev) regcache_mark_dirty(codec->control_data); break; default: - dev_dbg(codec->dev, "ASoC: CODEC is on" - " over suspend\n"); + dev_dbg(codec->dev, + "ASoC: CODEC is on over suspend\n"); break; } } @@ -713,8 +712,8 @@ static void soc_resume_deferred(struct work_struct *work) codec->suspended = 0; break; default: - dev_dbg(codec->dev, "ASoC: CODEC was on over" - " suspend\n"); + dev_dbg(codec->dev, + "ASoC: CODEC was on over suspend\n"); break; } } @@ -1110,8 +1109,8 @@ static int soc_probe_codec(struct snd_soc_card *card, } WARN(codec->dapm.idle_bias_off && codec->dapm.bias_level != SND_SOC_BIAS_OFF, - "codec %s can not start from non-off bias" - " with idle_bias_off==1\n", codec->name); + "codec %s can not start from non-off bias with idle_bias_off==1\n", + codec->name); } /* If the driver didn't set I/O up try regmap */ @@ -1582,8 +1581,9 @@ static int snd_soc_init_codec_cache(struct snd_soc_codec *codec, codec->compress_type = compress_type; ret = snd_soc_cache_init(codec); if (ret < 0) { - dev_err(codec->dev, "ASoC: Failed to set cache compression" - " type: %d\n", ret); + dev_err(codec->dev, + "ASoC: Failed to set cache compression type: %d\n", + ret); return ret; } codec->cache_init = 1; @@ -1639,8 +1639,9 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, card->owner, 0, &card->snd_card); if (ret < 0) { - dev_err(card->dev, "ASoC: can't create sound card for" - " card %s: %d\n", card->name, ret); + dev_err(card->dev, + "ASoC: can't create sound card for card %s: %d\n", + card->name, ret); goto base_error; } card->snd_card->dev = card->dev; @@ -1815,8 +1816,8 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) for (i = 0; i < card->num_rtd; i++) { ret = soc_register_ac97_dai_link(&card->rtd[i]); if (ret < 0) { - dev_err(card->dev, "ASoC: failed to register AC97:" - " %d\n", ret); + dev_err(card->dev, + "ASoC: failed to register AC97: %d\n", ret); while (--i >= 0) soc_unregister_ac97_dai_link(card->rtd[i].codec); goto probe_aux_dev_err; @@ -3586,14 +3587,16 @@ int snd_soc_register_card(struct snd_soc_card *card) * not both or neither. */ if (!!link->codec_name == !!link->codec_of_node) { - dev_err(card->dev, "ASoC: Neither/both codec" - " name/of_node are set for %s\n", link->name); + dev_err(card->dev, + "ASoC: Neither/both codec name/of_node are set for %s\n", + link->name); return -EINVAL; } /* Codec DAI name must be specified */ if (!link->codec_dai_name) { - dev_err(card->dev, "ASoC: codec_dai_name not" - " set for %s\n", link->name); + dev_err(card->dev, + "ASoC: codec_dai_name not set for %s\n", + link->name); return -EINVAL; } @@ -3602,8 +3605,9 @@ int snd_soc_register_card(struct snd_soc_card *card) * can be left unspecified, and a dummy platform will be used. */ if (link->platform_name && link->platform_of_node) { - dev_err(card->dev, "ASoC: Both platform name/of_node" - " are set for %s\n", link->name); + dev_err(card->dev, + "ASoC: Both platform name/of_node are set for %s\n", + link->name); return -EINVAL; } @@ -3613,8 +3617,9 @@ int snd_soc_register_card(struct snd_soc_card *card) * name alone.. */ if (link->cpu_name && link->cpu_of_node) { - dev_err(card->dev, "ASoC: Neither/both " - "cpu name/of_node are set for %s\n",link->name); + dev_err(card->dev, + "ASoC: Neither/both cpu name/of_node are set for %s\n", + link->name); return -EINVAL; } /* @@ -3623,8 +3628,9 @@ int snd_soc_register_card(struct snd_soc_card *card) */ if (!link->cpu_dai_name && !(link->cpu_name || link->cpu_of_node)) { - dev_err(card->dev, "ASoC: Neither cpu_dai_name nor " - "cpu_name/of_node are set for %s\n", link->name); + dev_err(card->dev, + "ASoC: Neither cpu_dai_name nor cpu_name/of_node are set for %s\n", + link->name); return -EINVAL; } } @@ -3728,8 +3734,9 @@ static inline char *fmt_multiple_name(struct device *dev, struct snd_soc_dai_driver *dai_drv) { if (dai_drv->name == NULL) { - dev_err(dev, "ASoC: error - multiple DAI %s registered with" - " no name\n", dev_name(dev)); + dev_err(dev, + "ASoC: error - multiple DAI %s registered with no name\n", + dev_name(dev)); return NULL; } @@ -3859,8 +3866,9 @@ static int snd_soc_register_dais(struct device *dev, list_for_each_entry(codec, &codec_list, list) { if (codec->dev == dev) { - dev_dbg(dev, "ASoC: Mapped DAI %s to " - "CODEC %s\n", dai->name, codec->name); + dev_dbg(dev, + "ASoC: Mapped DAI %s to CODEC %s\n", + dai->name, codec->name); dai->codec = codec; break; } @@ -4296,8 +4304,9 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, num_routes = of_property_count_strings(np, propname); if (num_routes < 0 || num_routes & 1) { - dev_err(card->dev, "ASoC: Property '%s' does not exist or its" - " length is not even\n", propname); + dev_err(card->dev, + "ASoC: Property '%s' does not exist or its length is not even\n", + propname); return -EINVAL; } num_routes /= 2; -- cgit v1.2.3 From 8011412999484a82a23dc3c9a5c9d5a1677ca05d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 25 Feb 2013 15:14:19 +0000 Subject: ASoC: dapm: Provide early event callbacks for power up and down Some devices may benefit from being able to start some parts of the widget power up/down sequence earlier on in the sequence than the point at which the final power state is committed. Support these by providing events which are called before any power state changes are done. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- include/sound/soc-dapm.h | 2 ++ sound/soc/soc-dapm.c | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index d4609029f014..f4f85dd1cd97 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -311,6 +311,8 @@ struct device; #define SND_SOC_DAPM_POST_PMD 0x8 /* after widget power down */ #define SND_SOC_DAPM_PRE_REG 0x10 /* before audio path setup */ #define SND_SOC_DAPM_POST_REG 0x20 /* after audio path setup */ +#define SND_SOC_DAPM_WILL_PMU 0x40 /* called at start of sequence */ +#define SND_SOC_DAPM_WILL_PMD 0x80 /* called at start of sequence */ #define SND_SOC_DAPM_PRE_POST_PMD \ (SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD) diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index a80c883bb8be..e4e5420de725 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1277,6 +1277,14 @@ static void dapm_seq_check_event(struct snd_soc_dapm_context *dapm, ev_name = "POST_PMD"; power = 0; break; + case SND_SOC_DAPM_WILL_PMU: + ev_name = "WILL_PMU"; + power = 1; + break; + case SND_SOC_DAPM_WILL_PMD: + ev_name = "WILL_PMD"; + power = 0; + break; default: BUG(); return; @@ -1737,6 +1745,14 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) &async_domain); async_synchronize_full_domain(&async_domain); + list_for_each_entry(w, &down_list, list) { + dapm_seq_check_event(dapm, w, SND_SOC_DAPM_WILL_PMD); + } + + list_for_each_entry(w, &up_list, list) { + dapm_seq_check_event(dapm, w, SND_SOC_DAPM_WILL_PMU); + } + /* Power down widgets first; try to avoid amplifying pops. */ dapm_seq_run(dapm, &down_list, event, false); -- cgit v1.2.3 From 701c73aa89032f2551cdc73725cb881c0886d86f Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 3 May 2013 14:39:22 +0530 Subject: ASoC: ep93xx: Remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown --- sound/soc/cirrus/ep93xx-ac97.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/cirrus/ep93xx-ac97.c b/sound/soc/cirrus/ep93xx-ac97.c index 7798fbd5e81d..840c9b18201e 100644 --- a/sound/soc/cirrus/ep93xx-ac97.c +++ b/sound/soc/cirrus/ep93xx-ac97.c @@ -403,7 +403,6 @@ static int ep93xx_ac97_probe(struct platform_device *pdev) return 0; fail: - platform_set_drvdata(pdev, NULL); ep93xx_ac97_info = NULL; dev_set_drvdata(&pdev->dev, NULL); return ret; @@ -418,7 +417,6 @@ static int ep93xx_ac97_remove(struct platform_device *pdev) /* disable the AC97 controller */ ep93xx_ac97_write_reg(info, AC97GCR, 0); - platform_set_drvdata(pdev, NULL); ep93xx_ac97_info = NULL; dev_set_drvdata(&pdev->dev, NULL); -- cgit v1.2.3 From 2fc059f2cc875a6d7372057093cd78cc9284b555 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 24 Apr 2013 11:54:43 -0300 Subject: ASoC: imx-sgtl5000: Do not enter the error path on success Return on success instead of entering the error path. Signed-off-by: Fabio Estevam Acked-by: Shawn Guo Signed-off-by: Mark Brown --- sound/soc/fsl/imx-sgtl5000.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sound/soc/fsl/imx-sgtl5000.c b/sound/soc/fsl/imx-sgtl5000.c index 9584e78858df..5a6aaa3b947a 100644 --- a/sound/soc/fsl/imx-sgtl5000.c +++ b/sound/soc/fsl/imx-sgtl5000.c @@ -174,6 +174,11 @@ static int imx_sgtl5000_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, data); + of_node_put(ssi_np); + of_node_put(codec_np); + + return 0; + clk_fail: clk_put(data->codec_clk); fail: -- cgit v1.2.3 From f8b24fcbd05bf7a9112812bf6ec60679ae928801 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 24 Apr 2013 13:23:14 -0300 Subject: ASoC: mxs-sgtl5000: Remove unneeded 'ret' variable Variable 'ret' is not needed here, so just remove it. Signed-off-by: Fabio Estevam Acked-by: Shawn Guo Signed-off-by: Mark Brown --- sound/soc/mxs/mxs-sgtl5000.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c index b1d9b5ebeeeb..4f74b051f73a 100644 --- a/sound/soc/mxs/mxs-sgtl5000.c +++ b/sound/soc/mxs/mxs-sgtl5000.c @@ -116,7 +116,7 @@ static int mxs_sgtl5000_probe_dt(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct device_node *saif_np[2], *codec_np; - int i, ret = 0; + int i; if (!np) return 1; /* no device tree */ @@ -142,7 +142,7 @@ static int mxs_sgtl5000_probe_dt(struct platform_device *pdev) of_node_put(saif_np[0]); of_node_put(saif_np[1]); - return ret; + return 0; } static int mxs_sgtl5000_probe(struct platform_device *pdev) -- cgit v1.2.3 From 666c25e3d759dfd33c6df4cd3a26f1bfed65215e Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 24 Apr 2013 13:23:15 -0300 Subject: ASoC: mxs-sgtl5000: Remove unneeded fields from snd_soc_dai_link It makes no sense to hardcode the I2C bus into the codec_name field. cpu_dai_name and platform_name are also overwritten later in mxs_sgtl5000_probe_dt(). So remove the three fields, as mxs platform is dt-only platform. Signed-off-by: Fabio Estevam Acked-by: Shawn Guo Signed-off-by: Mark Brown --- sound/soc/mxs/mxs-sgtl5000.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c index 4f74b051f73a..1b134d72f120 100644 --- a/sound/soc/mxs/mxs-sgtl5000.c +++ b/sound/soc/mxs/mxs-sgtl5000.c @@ -90,17 +90,11 @@ static struct snd_soc_dai_link mxs_sgtl5000_dai[] = { .name = "HiFi Tx", .stream_name = "HiFi Playback", .codec_dai_name = "sgtl5000", - .codec_name = "sgtl5000.0-000a", - .cpu_dai_name = "mxs-saif.0", - .platform_name = "mxs-saif.0", .ops = &mxs_sgtl5000_hifi_ops, }, { .name = "HiFi Rx", .stream_name = "HiFi Capture", .codec_dai_name = "sgtl5000", - .codec_name = "sgtl5000.0-000a", - .cpu_dai_name = "mxs-saif.1", - .platform_name = "mxs-saif.1", .ops = &mxs_sgtl5000_hifi_ops, }, }; -- cgit v1.2.3 From 436947fc82237e2cd78b3b2c11633aaa6ef07641 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 6 May 2013 17:39:08 -0300 Subject: ASoC: fsl: imx-audmux: Let device core handle pinctrl Since commit ab78029 (drivers/pinctrl: grab default handles from device core), we can rely on device core for handling pinctrl. So remove devm_pinctrl_get_select_default() from the driver. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown --- sound/soc/fsl/imx-audmux.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/sound/soc/fsl/imx-audmux.c b/sound/soc/fsl/imx-audmux.c index 47f046a8fdab..e260f1f899db 100644 --- a/sound/soc/fsl/imx-audmux.c +++ b/sound/soc/fsl/imx-audmux.c @@ -26,7 +26,6 @@ #include #include #include -#include #include "imx-audmux.h" @@ -247,7 +246,6 @@ EXPORT_SYMBOL_GPL(imx_audmux_v2_configure_port); static int imx_audmux_probe(struct platform_device *pdev) { struct resource *res; - struct pinctrl *pinctrl; const struct of_device_id *of_id = of_match_device(imx_audmux_dt_ids, &pdev->dev); @@ -256,12 +254,6 @@ static int imx_audmux_probe(struct platform_device *pdev) if (IS_ERR(audmux_base)) return PTR_ERR(audmux_base); - pinctrl = devm_pinctrl_get_select_default(&pdev->dev); - if (IS_ERR(pinctrl)) { - dev_err(&pdev->dev, "setup pinctrl failed!"); - return PTR_ERR(pinctrl); - } - audmux_clk = devm_clk_get(&pdev->dev, "audmux"); if (IS_ERR(audmux_clk)) { dev_dbg(&pdev->dev, "cannot get clock: %ld\n", -- cgit v1.2.3 From bc2716fb5a3bb05abc81c5c4c367cca99854fb9b Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 3 May 2013 14:39:24 +0530 Subject: ASoC: jz4740: Remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Acked-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/codecs/jz4740.c | 2 -- sound/soc/jz4740/jz4740-i2s.c | 1 - 2 files changed, 3 deletions(-) diff --git a/sound/soc/codecs/jz4740.c b/sound/soc/codecs/jz4740.c index 5f607b35b68b..bcebd1a9ce31 100644 --- a/sound/soc/codecs/jz4740.c +++ b/sound/soc/codecs/jz4740.c @@ -384,8 +384,6 @@ static int jz4740_codec_remove(struct platform_device *pdev) { snd_soc_unregister_codec(&pdev->dev); - platform_set_drvdata(pdev, NULL); - return 0; } diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c index 9a126441c5f3..cafc6eda0ac5 100644 --- a/sound/soc/jz4740/jz4740-i2s.c +++ b/sound/soc/jz4740/jz4740-i2s.c @@ -509,7 +509,6 @@ static int jz4740_i2s_dev_remove(struct platform_device *pdev) iounmap(i2s->base); release_mem_region(i2s->mem->start, resource_size(i2s->mem)); - platform_set_drvdata(pdev, NULL); kfree(i2s); return 0; -- cgit v1.2.3 From e76af6d189075a0ca59bc654157e80da53559fb0 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 6 May 2013 15:06:01 -0300 Subject: ASoC: mxs: mxs-saif: Let device core handle pinctrl Since commit ab78029 (drivers/pinctrl: grab default handles from device core), we can rely on device core for handling pinctrl. So remove devm_pinctrl_get_select_default() from the driver. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown --- sound/soc/mxs/mxs-saif.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c index d31dc52fa862..71a972f5af97 100644 --- a/sound/soc/mxs/mxs-saif.c +++ b/sound/soc/mxs/mxs-saif.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -667,7 +666,6 @@ static int mxs_saif_probe(struct platform_device *pdev) struct device_node *np = pdev->dev.of_node; struct resource *iores, *dmares; struct mxs_saif *saif; - struct pinctrl *pinctrl; int ret = 0; struct device_node *master; @@ -707,12 +705,6 @@ static int mxs_saif_probe(struct platform_device *pdev) mxs_saif[saif->id] = saif; - pinctrl = devm_pinctrl_get_select_default(&pdev->dev); - if (IS_ERR(pinctrl)) { - ret = PTR_ERR(pinctrl); - return ret; - } - saif->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(saif->clk)) { ret = PTR_ERR(saif->clk); -- cgit v1.2.3 From 3d77876b8fd43ba845f3503369590a9bcc1f1e0c Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 3 May 2013 14:39:20 +0530 Subject: ASoC: omap-mcbsp: Remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown --- sound/soc/omap/omap-mcbsp.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index eadbfb6b5000..7483efb6dc67 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c @@ -814,8 +814,6 @@ static int asoc_mcbsp_remove(struct platform_device *pdev) clk_put(mcbsp->fclk); - platform_set_drvdata(pdev, NULL); - return 0; } -- cgit v1.2.3 From e75fa9b1f9d430edeb848db329f924996bc964d6 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 3 May 2013 14:39:19 +0530 Subject: ASoC: Samsung: Remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown --- sound/soc/samsung/smdk_wm8580pcm.c | 1 - sound/soc/samsung/smdk_wm8994pcm.c | 1 - 2 files changed, 2 deletions(-) diff --git a/sound/soc/samsung/smdk_wm8580pcm.c b/sound/soc/samsung/smdk_wm8580pcm.c index e43bd4294f99..23a9204b106d 100644 --- a/sound/soc/samsung/smdk_wm8580pcm.c +++ b/sound/soc/samsung/smdk_wm8580pcm.c @@ -176,7 +176,6 @@ static int snd_smdk_probe(struct platform_device *pdev) static int snd_smdk_remove(struct platform_device *pdev) { snd_soc_unregister_card(&smdk_pcm); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/sound/soc/samsung/smdk_wm8994pcm.c b/sound/soc/samsung/smdk_wm8994pcm.c index 3688a32000a2..0c84ca099612 100644 --- a/sound/soc/samsung/smdk_wm8994pcm.c +++ b/sound/soc/samsung/smdk_wm8994pcm.c @@ -146,7 +146,6 @@ static int snd_smdk_probe(struct platform_device *pdev) static int snd_smdk_remove(struct platform_device *pdev) { snd_soc_unregister_card(&smdk_pcm); - platform_set_drvdata(pdev, NULL); return 0; } -- cgit v1.2.3 From 271f193d739f894a7d1229c2cbb72d2f8b847544 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 3 May 2013 18:04:24 -0300 Subject: ASoC: sgtl5000: Fix comment about register addresses The list below the comment relates to sgtl5000 registers addresses, so change the comment to improve the description. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown --- sound/soc/codecs/sgtl5000.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/sgtl5000.h b/sound/soc/codecs/sgtl5000.h index 8a9f43534b79..4b69229a9818 100644 --- a/sound/soc/codecs/sgtl5000.h +++ b/sound/soc/codecs/sgtl5000.h @@ -12,7 +12,7 @@ #define _SGTL5000_H /* - * Register values. + * Registers addresses */ #define SGTL5000_CHIP_ID 0x0000 #define SGTL5000_CHIP_DIG_POWER 0x0002 -- cgit v1.2.3 From e5d80e82e32e4cb66d67a56352e5a594e2a35cd0 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sat, 4 May 2013 15:39:34 -0300 Subject: ASoC: sgtl5000: Convert to use regmap directly Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown --- sound/soc/codecs/sgtl5000.c | 145 +++++++++++++++++++++++++++++++++----------- 1 file changed, 111 insertions(+), 34 deletions(-) diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index 92bbfec9b107..327b4434b4ce 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -34,30 +35,30 @@ #define SGTL5000_MAX_REG_OFFSET 0x013A /* default value of sgtl5000 registers */ -static const u16 sgtl5000_regs[SGTL5000_MAX_REG_OFFSET] = { - [SGTL5000_CHIP_CLK_CTRL] = 0x0008, - [SGTL5000_CHIP_I2S_CTRL] = 0x0010, - [SGTL5000_CHIP_SSS_CTRL] = 0x0008, - [SGTL5000_CHIP_DAC_VOL] = 0x3c3c, - [SGTL5000_CHIP_PAD_STRENGTH] = 0x015f, - [SGTL5000_CHIP_ANA_HP_CTRL] = 0x1818, - [SGTL5000_CHIP_ANA_CTRL] = 0x0111, - [SGTL5000_CHIP_LINE_OUT_VOL] = 0x0404, - [SGTL5000_CHIP_ANA_POWER] = 0x7060, - [SGTL5000_CHIP_PLL_CTRL] = 0x5000, - [SGTL5000_DAP_BASS_ENHANCE] = 0x0040, - [SGTL5000_DAP_BASS_ENHANCE_CTRL] = 0x051f, - [SGTL5000_DAP_SURROUND] = 0x0040, - [SGTL5000_DAP_EQ_BASS_BAND0] = 0x002f, - [SGTL5000_DAP_EQ_BASS_BAND1] = 0x002f, - [SGTL5000_DAP_EQ_BASS_BAND2] = 0x002f, - [SGTL5000_DAP_EQ_BASS_BAND3] = 0x002f, - [SGTL5000_DAP_EQ_BASS_BAND4] = 0x002f, - [SGTL5000_DAP_MAIN_CHAN] = 0x8000, - [SGTL5000_DAP_AVC_CTRL] = 0x0510, - [SGTL5000_DAP_AVC_THRESHOLD] = 0x1473, - [SGTL5000_DAP_AVC_ATTACK] = 0x0028, - [SGTL5000_DAP_AVC_DECAY] = 0x0050, +static const struct reg_default sgtl5000_reg_defaults[] = { + { SGTL5000_CHIP_CLK_CTRL, 0x0008 }, + { SGTL5000_CHIP_I2S_CTRL, 0x0010 }, + { SGTL5000_CHIP_SSS_CTRL, 0x0008 }, + { SGTL5000_CHIP_DAC_VOL, 0x3c3c }, + { SGTL5000_CHIP_PAD_STRENGTH, 0x015f }, + { SGTL5000_CHIP_ANA_HP_CTRL, 0x1818 }, + { SGTL5000_CHIP_ANA_CTRL, 0x0111 }, + { SGTL5000_CHIP_LINE_OUT_VOL, 0x0404 }, + { SGTL5000_CHIP_ANA_POWER, 0x7060 }, + { SGTL5000_CHIP_PLL_CTRL, 0x5000 }, + { SGTL5000_DAP_BASS_ENHANCE, 0x0040 }, + { SGTL5000_DAP_BASS_ENHANCE_CTRL, 0x051f }, + { SGTL5000_DAP_SURROUND, 0x0040 }, + { SGTL5000_DAP_EQ_BASS_BAND0, 0x002f }, + { SGTL5000_DAP_EQ_BASS_BAND1, 0x002f }, + { SGTL5000_DAP_EQ_BASS_BAND2, 0x002f }, + { SGTL5000_DAP_EQ_BASS_BAND3, 0x002f }, + { SGTL5000_DAP_EQ_BASS_BAND4, 0x002f }, + { SGTL5000_DAP_MAIN_CHAN, 0x8000 }, + { SGTL5000_DAP_AVC_CTRL, 0x0510 }, + { SGTL5000_DAP_AVC_THRESHOLD, 0x1473 }, + { SGTL5000_DAP_AVC_ATTACK, 0x0028 }, + { SGTL5000_DAP_AVC_DECAY, 0x0050 }, }; /* regulator supplies for sgtl5000, VDDD is an optional external supply */ @@ -112,6 +113,7 @@ struct sgtl5000_priv { int fmt; /* i2s data format */ struct regulator_bulk_data supplies[SGTL5000_SUPPLY_NUM]; struct ldo_regulator *ldo; + struct regmap *regmap; }; /* @@ -958,17 +960,76 @@ static struct snd_soc_dai_driver sgtl5000_dai = { .symmetric_rates = 1, }; -static int sgtl5000_volatile_register(struct snd_soc_codec *codec, - unsigned int reg) +static bool sgtl5000_volatile(struct device *dev, unsigned int reg) { switch (reg) { case SGTL5000_CHIP_ID: case SGTL5000_CHIP_ADCDAC_CTRL: case SGTL5000_CHIP_ANA_STATUS: - return 1; + return true; } - return 0; + return false; +} + +static bool sgtl5000_readable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SGTL5000_CHIP_ID: + case SGTL5000_CHIP_DIG_POWER: + case SGTL5000_CHIP_CLK_CTRL: + case SGTL5000_CHIP_I2S_CTRL: + case SGTL5000_CHIP_SSS_CTRL: + case SGTL5000_CHIP_ADCDAC_CTRL: + case SGTL5000_CHIP_DAC_VOL: + case SGTL5000_CHIP_PAD_STRENGTH: + case SGTL5000_CHIP_ANA_ADC_CTRL: + case SGTL5000_CHIP_ANA_HP_CTRL: + case SGTL5000_CHIP_ANA_CTRL: + case SGTL5000_CHIP_LINREG_CTRL: + case SGTL5000_CHIP_REF_CTRL: + case SGTL5000_CHIP_MIC_CTRL: + case SGTL5000_CHIP_LINE_OUT_CTRL: + case SGTL5000_CHIP_LINE_OUT_VOL: + case SGTL5000_CHIP_ANA_POWER: + case SGTL5000_CHIP_PLL_CTRL: + case SGTL5000_CHIP_CLK_TOP_CTRL: + case SGTL5000_CHIP_ANA_STATUS: + case SGTL5000_CHIP_SHORT_CTRL: + case SGTL5000_CHIP_ANA_TEST2: + case SGTL5000_DAP_CTRL: + case SGTL5000_DAP_PEQ: + case SGTL5000_DAP_BASS_ENHANCE: + case SGTL5000_DAP_BASS_ENHANCE_CTRL: + case SGTL5000_DAP_AUDIO_EQ: + case SGTL5000_DAP_SURROUND: + case SGTL5000_DAP_FLT_COEF_ACCESS: + case SGTL5000_DAP_COEF_WR_B0_MSB: + case SGTL5000_DAP_COEF_WR_B0_LSB: + case SGTL5000_DAP_EQ_BASS_BAND0: + case SGTL5000_DAP_EQ_BASS_BAND1: + case SGTL5000_DAP_EQ_BASS_BAND2: + case SGTL5000_DAP_EQ_BASS_BAND3: + case SGTL5000_DAP_EQ_BASS_BAND4: + case SGTL5000_DAP_MAIN_CHAN: + case SGTL5000_DAP_MIX_CHAN: + case SGTL5000_DAP_AVC_CTRL: + case SGTL5000_DAP_AVC_THRESHOLD: + case SGTL5000_DAP_AVC_ATTACK: + case SGTL5000_DAP_AVC_DECAY: + case SGTL5000_DAP_COEF_WR_B1_MSB: + case SGTL5000_DAP_COEF_WR_B1_LSB: + case SGTL5000_DAP_COEF_WR_B2_MSB: + case SGTL5000_DAP_COEF_WR_B2_LSB: + case SGTL5000_DAP_COEF_WR_A1_MSB: + case SGTL5000_DAP_COEF_WR_A1_LSB: + case SGTL5000_DAP_COEF_WR_A2_MSB: + case SGTL5000_DAP_COEF_WR_A2_LSB: + return true; + + default: + return false; + } } #ifdef CONFIG_SUSPEND @@ -1300,7 +1361,8 @@ static int sgtl5000_probe(struct snd_soc_codec *codec) struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); /* setup i2c data ops */ - ret = snd_soc_codec_set_cache_io(codec, 16, 16, SND_SOC_I2C); + codec->control_data = sgtl5000->regmap; + ret = snd_soc_codec_set_cache_io(codec, 16, 16, SND_SOC_REGMAP); if (ret < 0) { dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); return ret; @@ -1391,11 +1453,6 @@ static struct snd_soc_codec_driver sgtl5000_driver = { .suspend = sgtl5000_suspend, .resume = sgtl5000_resume, .set_bias_level = sgtl5000_set_bias_level, - .reg_cache_size = ARRAY_SIZE(sgtl5000_regs), - .reg_word_size = sizeof(u16), - .reg_cache_step = 2, - .reg_cache_default = sgtl5000_regs, - .volatile_register = sgtl5000_volatile_register, .controls = sgtl5000_snd_controls, .num_controls = ARRAY_SIZE(sgtl5000_snd_controls), .dapm_widgets = sgtl5000_dapm_widgets, @@ -1404,6 +1461,19 @@ static struct snd_soc_codec_driver sgtl5000_driver = { .num_dapm_routes = ARRAY_SIZE(sgtl5000_dapm_routes), }; +static const struct regmap_config sgtl5000_regmap = { + .reg_bits = 16, + .val_bits = 16, + + .max_register = SGTL5000_MAX_REG_OFFSET, + .volatile_reg = sgtl5000_volatile, + .readable_reg = sgtl5000_readable, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = sgtl5000_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(sgtl5000_reg_defaults), +}; + static int sgtl5000_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1415,6 +1485,13 @@ static int sgtl5000_i2c_probe(struct i2c_client *client, if (!sgtl5000) return -ENOMEM; + sgtl5000->regmap = devm_regmap_init_i2c(client, &sgtl5000_regmap); + if (IS_ERR(sgtl5000->regmap)) { + ret = PTR_ERR(sgtl5000->regmap); + dev_err(&client->dev, "Failed to allocate regmap: %d\n", ret); + return ret; + } + i2c_set_clientdata(client, sgtl5000); ret = snd_soc_register_codec(&client->dev, -- cgit v1.2.3 From 29f421c2107f28976dacc9f1fca4bbfebee5b10f Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 24 Apr 2013 13:23:14 -0300 Subject: ASoC: mxs-sgtl5000: Remove unneeded 'ret' variable Variable 'ret' is not needed here, so just remove it. Signed-off-by: Fabio Estevam Acked-by: Shawn Guo Signed-off-by: Mark Brown --- sound/soc/mxs/mxs-sgtl5000.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c index b1d9b5ebeeeb..4f74b051f73a 100644 --- a/sound/soc/mxs/mxs-sgtl5000.c +++ b/sound/soc/mxs/mxs-sgtl5000.c @@ -116,7 +116,7 @@ static int mxs_sgtl5000_probe_dt(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct device_node *saif_np[2], *codec_np; - int i, ret = 0; + int i; if (!np) return 1; /* no device tree */ @@ -142,7 +142,7 @@ static int mxs_sgtl5000_probe_dt(struct platform_device *pdev) of_node_put(saif_np[0]); of_node_put(saif_np[1]); - return ret; + return 0; } static int mxs_sgtl5000_probe(struct platform_device *pdev) -- cgit v1.2.3 From 24279dcee5456bb141a1ca007a3f0abe02bf91d0 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 24 Apr 2013 13:23:15 -0300 Subject: ASoC: mxs-sgtl5000: Remove unneeded fields from snd_soc_dai_link It makes no sense to hardcode the I2C bus into the codec_name field. cpu_dai_name and platform_name are also overwritten later in mxs_sgtl5000_probe_dt(). So remove the three fields, as mxs platform is dt-only platform. Signed-off-by: Fabio Estevam Acked-by: Shawn Guo Signed-off-by: Mark Brown --- sound/soc/mxs/mxs-sgtl5000.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c index 4f74b051f73a..1b134d72f120 100644 --- a/sound/soc/mxs/mxs-sgtl5000.c +++ b/sound/soc/mxs/mxs-sgtl5000.c @@ -90,17 +90,11 @@ static struct snd_soc_dai_link mxs_sgtl5000_dai[] = { .name = "HiFi Tx", .stream_name = "HiFi Playback", .codec_dai_name = "sgtl5000", - .codec_name = "sgtl5000.0-000a", - .cpu_dai_name = "mxs-saif.0", - .platform_name = "mxs-saif.0", .ops = &mxs_sgtl5000_hifi_ops, }, { .name = "HiFi Rx", .stream_name = "HiFi Capture", .codec_dai_name = "sgtl5000", - .codec_name = "sgtl5000.0-000a", - .cpu_dai_name = "mxs-saif.1", - .platform_name = "mxs-saif.1", .ops = &mxs_sgtl5000_hifi_ops, }, }; -- cgit v1.2.3 From b871f1ad3c8a1ac2fb862f9261f14a67dc2c7b7d Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 9 May 2013 21:15:46 -0300 Subject: ASoC: sgtl5000: Read SGTL5000_CHIP_ID in i2c_probe() The usual place for reading chip ID is inside i2c_probe, so move it there and also convert it to regmap. sgtl5000_enable_regulators() needs to read the chip revision, so keep the revision check there. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown --- sound/soc/codecs/sgtl5000.c | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index 327b4434b4ce..1ab356ab0f31 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -1275,7 +1275,7 @@ static int sgtl5000_replace_vddd_with_ldo(struct snd_soc_codec *codec) static int sgtl5000_enable_regulators(struct snd_soc_codec *codec) { - u16 reg; + int reg; int ret; int rev; int i; @@ -1303,23 +1303,17 @@ static int sgtl5000_enable_regulators(struct snd_soc_codec *codec) /* wait for all power rails bring up */ udelay(10); - /* read chip information */ - reg = snd_soc_read(codec, SGTL5000_CHIP_ID); - if (((reg & SGTL5000_PARTID_MASK) >> SGTL5000_PARTID_SHIFT) != - SGTL5000_PARTID_PART_ID) { - dev_err(codec->dev, - "Device with ID register %x is not a sgtl5000\n", reg); - ret = -ENODEV; - goto err_regulator_disable; - } - - rev = (reg & SGTL5000_REVID_MASK) >> SGTL5000_REVID_SHIFT; - dev_info(codec->dev, "sgtl5000 revision 0x%x\n", rev); - /* * workaround for revision 0x11 and later, * roll back to use internal LDO */ + + ret = regmap_read(sgtl5000->regmap, SGTL5000_CHIP_ID, ®); + if (ret) + goto err_regulator_disable; + + rev = (reg & SGTL5000_REVID_MASK) >> SGTL5000_REVID_SHIFT; + if (external_vddd && rev >= 0x11) { /* disable all regulator first */ regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies), @@ -1478,7 +1472,7 @@ static int sgtl5000_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct sgtl5000_priv *sgtl5000; - int ret; + int ret, reg, rev; sgtl5000 = devm_kzalloc(&client->dev, sizeof(struct sgtl5000_priv), GFP_KERNEL); @@ -1492,6 +1486,21 @@ static int sgtl5000_i2c_probe(struct i2c_client *client, return ret; } + /* read chip information */ + ret = regmap_read(sgtl5000->regmap, SGTL5000_CHIP_ID, ®); + if (ret) + return ret; + + if (((reg & SGTL5000_PARTID_MASK) >> SGTL5000_PARTID_SHIFT) != + SGTL5000_PARTID_PART_ID) { + dev_err(&client->dev, + "Device with ID register %x is not a sgtl5000\n", reg); + return -ENODEV; + } + + rev = (reg & SGTL5000_REVID_MASK) >> SGTL5000_REVID_SHIFT; + dev_info(&client->dev, "sgtl5000 revision 0x%x\n", rev); + i2c_set_clientdata(client, sgtl5000); ret = snd_soc_register_codec(&client->dev, -- cgit v1.2.3 From af8ee11209e749c75eabf32b2a4ca631f396acf8 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 9 May 2013 21:15:47 -0300 Subject: ASoC: sgtl5000: Fix driver probe after reset After a 'reboot' command in Linux or after pressing the system's reset button the sgtl5000 driver fails to probe: sgtl5000 0-000a: Device with ID register ffff is not a sgtl5000 sgtl5000 0-000a: ASoC: failed to probe CODEC -19 imx-sgtl5000 sound.12: ASoC: failed to instantiate card -19 imx-sgtl5000 sound.12: snd_soc_register_card failed (-19) sgtl5000 codec does not have a reset line, nor a reset command in software, so after a system reset the codec does not contain the default register values from sgtl5000_reg_defaults[] anymore, as these are only valid after a power-on-reset cycle. Fix this issue by explicitly reading all the reset register values from sgtl5000_reg_defaults[] and writing them back into sgtl5000 to ensure a sane state. Signed-off-by: Fabio Estevam Tested-by: Eric Nelson Signed-off-by: Mark Brown --- sound/soc/codecs/sgtl5000.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index 1ab356ab0f31..1c3b20fc7ec3 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -1468,6 +1468,31 @@ static const struct regmap_config sgtl5000_regmap = { .num_reg_defaults = ARRAY_SIZE(sgtl5000_reg_defaults), }; +/* + * Write all the default values from sgtl5000_reg_defaults[] array into the + * sgtl5000 registers, to make sure we always start with the sane registers + * values as stated in the datasheet. + * + * Since sgtl5000 does not have a reset line, nor a reset command in software, + * we follow this approach to guarantee we always start from the default values + * and avoid problems like, not being able to probe after an audio playback + * followed by a system reset or a 'reboot' command in Linux + */ +static int sgtl5000_fill_defaults(struct sgtl5000_priv *sgtl5000) +{ + int i, ret, val, index; + + for (i = 0; i < ARRAY_SIZE(sgtl5000_reg_defaults); i++) { + val = sgtl5000_reg_defaults[i].def; + index = sgtl5000_reg_defaults[i].reg; + ret = regmap_write(sgtl5000->regmap, index, val); + if (ret) + return ret; + } + + return 0; +} + static int sgtl5000_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1503,6 +1528,11 @@ static int sgtl5000_i2c_probe(struct i2c_client *client, i2c_set_clientdata(client, sgtl5000); + /* Ensure sgtl5000 will start with sane register values */ + ret = sgtl5000_fill_defaults(sgtl5000); + if (ret) + return ret; + ret = snd_soc_register_codec(&client->dev, &sgtl5000_driver, &sgtl5000_dai, 1); return ret; -- cgit v1.2.3 From ee492cfcb17631e4345fa97f205ca9617fffaebc Mon Sep 17 00:00:00 2001 From: Marek Belisko Date: Thu, 25 Apr 2013 15:13:12 +0200 Subject: ASoC: spdif_transceiver: Change driver filename to spdif_transmitter.c. Transceiver usually means receiver + transmitter. This codec can do only transmit. Update driver accordingly. Signed-off-by: Marek Belisko Signed-off-by: Mark Brown --- sound/soc/codecs/Makefile | 2 +- sound/soc/codecs/spdif_transciever.c | 69 ------------------------------------ sound/soc/codecs/spdif_transmitter.c | 69 ++++++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 70 deletions(-) delete mode 100644 sound/soc/codecs/spdif_transciever.c create mode 100644 sound/soc/codecs/spdif_transmitter.c diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index b9e41c9a1f4c..dae0aa60dac0 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -50,7 +50,7 @@ snd-soc-alc5632-objs := alc5632.o snd-soc-sigmadsp-objs := sigmadsp.o snd-soc-si476x-objs := si476x.o snd-soc-sn95031-objs := sn95031.o -snd-soc-spdif-tx-objs := spdif_transciever.o +snd-soc-spdif-tx-objs := spdif_transmitter.o snd-soc-spdif-rx-objs := spdif_receiver.o snd-soc-ssm2602-objs := ssm2602.o snd-soc-sta32x-objs := sta32x.o diff --git a/sound/soc/codecs/spdif_transciever.c b/sound/soc/codecs/spdif_transciever.c deleted file mode 100644 index 112a49d66e39..000000000000 --- a/sound/soc/codecs/spdif_transciever.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - * ALSA SoC SPDIF DIT driver - * - * This driver is used by controllers which can operate in DIT (SPDI/F) where - * no codec is needed. This file provides stub codec that can be used - * in these configurations. TI DaVinci Audio controller uses this driver. - * - * Author: Steve Chen, - * Copyright: (C) 2009 MontaVista Software, Inc., - * Copyright: (C) 2009 Texas Instruments, India - * - * 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 -#include -#include -#include -#include -#include - -#define DRV_NAME "spdif-dit" - -#define STUB_RATES SNDRV_PCM_RATE_8000_96000 -#define STUB_FORMATS SNDRV_PCM_FMTBIT_S16_LE - - -static struct snd_soc_codec_driver soc_codec_spdif_dit; - -static struct snd_soc_dai_driver dit_stub_dai = { - .name = "dit-hifi", - .playback = { - .stream_name = "Playback", - .channels_min = 1, - .channels_max = 384, - .rates = STUB_RATES, - .formats = STUB_FORMATS, - }, -}; - -static int spdif_dit_probe(struct platform_device *pdev) -{ - return snd_soc_register_codec(&pdev->dev, &soc_codec_spdif_dit, - &dit_stub_dai, 1); -} - -static int spdif_dit_remove(struct platform_device *pdev) -{ - snd_soc_unregister_codec(&pdev->dev); - return 0; -} - -static struct platform_driver spdif_dit_driver = { - .probe = spdif_dit_probe, - .remove = spdif_dit_remove, - .driver = { - .name = DRV_NAME, - .owner = THIS_MODULE, - }, -}; - -module_platform_driver(spdif_dit_driver); - -MODULE_AUTHOR("Steve Chen "); -MODULE_DESCRIPTION("SPDIF dummy codec driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/codecs/spdif_transmitter.c b/sound/soc/codecs/spdif_transmitter.c new file mode 100644 index 000000000000..112a49d66e39 --- /dev/null +++ b/sound/soc/codecs/spdif_transmitter.c @@ -0,0 +1,69 @@ +/* + * ALSA SoC SPDIF DIT driver + * + * This driver is used by controllers which can operate in DIT (SPDI/F) where + * no codec is needed. This file provides stub codec that can be used + * in these configurations. TI DaVinci Audio controller uses this driver. + * + * Author: Steve Chen, + * Copyright: (C) 2009 MontaVista Software, Inc., + * Copyright: (C) 2009 Texas Instruments, India + * + * 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 +#include +#include +#include +#include +#include + +#define DRV_NAME "spdif-dit" + +#define STUB_RATES SNDRV_PCM_RATE_8000_96000 +#define STUB_FORMATS SNDRV_PCM_FMTBIT_S16_LE + + +static struct snd_soc_codec_driver soc_codec_spdif_dit; + +static struct snd_soc_dai_driver dit_stub_dai = { + .name = "dit-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 384, + .rates = STUB_RATES, + .formats = STUB_FORMATS, + }, +}; + +static int spdif_dit_probe(struct platform_device *pdev) +{ + return snd_soc_register_codec(&pdev->dev, &soc_codec_spdif_dit, + &dit_stub_dai, 1); +} + +static int spdif_dit_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + return 0; +} + +static struct platform_driver spdif_dit_driver = { + .probe = spdif_dit_probe, + .remove = spdif_dit_remove, + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(spdif_dit_driver); + +MODULE_AUTHOR("Steve Chen "); +MODULE_DESCRIPTION("SPDIF dummy codec driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); -- cgit v1.2.3 From 1b7c8b350fd4751051f0abba040a29b72f829665 Mon Sep 17 00:00:00 2001 From: Marek Belisko Date: Thu, 25 Apr 2013 15:13:13 +0200 Subject: ASoC: spdif_transmitter: Add DT support. Add devicetree support for this dummy audio soc driver. Signed-off-by: Michal Bachraty Signed-off-by: Marek Belisko Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/spdif-transmitter.txt | 10 ++++++++++ sound/soc/codecs/spdif_transmitter.c | 10 ++++++++++ 2 files changed, 20 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/spdif-transmitter.txt diff --git a/Documentation/devicetree/bindings/sound/spdif-transmitter.txt b/Documentation/devicetree/bindings/sound/spdif-transmitter.txt new file mode 100644 index 000000000000..55a85841dd85 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/spdif-transmitter.txt @@ -0,0 +1,10 @@ +Device-Tree bindings for dummy spdif transmitter + +Required properties: + - compatible: should be "linux,spdif-dit". + +Example node: + + codec: spdif-transmitter { + compatible = "linux,spdif-dit"; + }; diff --git a/sound/soc/codecs/spdif_transmitter.c b/sound/soc/codecs/spdif_transmitter.c index 112a49d66e39..18280499fd55 100644 --- a/sound/soc/codecs/spdif_transmitter.c +++ b/sound/soc/codecs/spdif_transmitter.c @@ -20,6 +20,7 @@ #include #include #include +#include #define DRV_NAME "spdif-dit" @@ -52,12 +53,21 @@ static int spdif_dit_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id spdif_dit_dt_ids[] = { + { .compatible = "linux,spdif-dit", }, + { } +}; +MODULE_DEVICE_TABLE(of, spdif_dit_dt_ids); +#endif + static struct platform_driver spdif_dit_driver = { .probe = spdif_dit_probe, .remove = spdif_dit_remove, .driver = { .name = DRV_NAME, .owner = THIS_MODULE, + .of_match_table = of_match_ptr(spdif_dit_dt_ids), }, }; -- cgit v1.2.3 From f9c8ba8965597bb45b95014338d59ade15d53e93 Mon Sep 17 00:00:00 2001 From: Marek Belisko Date: Thu, 25 Apr 2013 15:13:14 +0200 Subject: ASoC: spdif_receiver: Add DT support. Add devicetree support for this dummy audio soc driver. Signed-off-by: Michal Bachraty Signed-off-by: Marek Belisko Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/spdif-receiver.txt | 10 ++++++++++ sound/soc/codecs/spdif_receiver.c | 10 ++++++++++ 2 files changed, 20 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/spdif-receiver.txt diff --git a/Documentation/devicetree/bindings/sound/spdif-receiver.txt b/Documentation/devicetree/bindings/sound/spdif-receiver.txt new file mode 100644 index 000000000000..80f807bf8a1d --- /dev/null +++ b/Documentation/devicetree/bindings/sound/spdif-receiver.txt @@ -0,0 +1,10 @@ +Device-Tree bindings for dummy spdif receiver + +Required properties: + - compatible: should be "linux,spdif-dir". + +Example node: + + codec: spdif-receiver { + compatible = "linux,spdif-dir"; + }; diff --git a/sound/soc/codecs/spdif_receiver.c b/sound/soc/codecs/spdif_receiver.c index dd8d856053fc..e9d7881ed2c8 100644 --- a/sound/soc/codecs/spdif_receiver.c +++ b/sound/soc/codecs/spdif_receiver.c @@ -21,6 +21,7 @@ #include #include #include +#include #define STUB_RATES SNDRV_PCM_RATE_8000_192000 #define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ @@ -51,12 +52,21 @@ static int spdif_dir_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id spdif_dir_dt_ids[] = { + { .compatible = "linux,spdif-dir", }, + { } +}; +MODULE_DEVICE_TABLE(of, spdif_dir_dt_ids); +#endif + static struct platform_driver spdif_dir_driver = { .probe = spdif_dir_probe, .remove = spdif_dir_remove, .driver = { .name = "spdif-dir", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(spdif_dir_dt_ids), }, }; -- cgit v1.2.3 From 46fdd8b11d4ab58af126344dcbc0bd565174db16 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 20 Apr 2013 19:29:06 +0200 Subject: ASoC: spear: Setup dma data in DAI probe This allows us to access the DAI DMA data when we create the PCM. We'll use this when converting spear to generic DMA engine PCM driver. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/spear/spdif_in.c | 12 ++++-------- sound/soc/spear/spdif_out.c | 7 ++++--- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/sound/soc/spear/spdif_in.c b/sound/soc/spear/spdif_in.c index 14d57e89bcba..82c838753c06 100644 --- a/sound/soc/spear/spdif_in.c +++ b/sound/soc/spear/spdif_in.c @@ -49,15 +49,12 @@ static void spdif_in_configure(struct spdif_in_dev *host) writel(0xF, host->io_base + SPDIF_IN_IRQ_MASK); } -static int spdif_in_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *cpu_dai) +static int spdif_in_dai_probe(struct snd_soc_dai *dai) { - struct spdif_in_dev *host = snd_soc_dai_get_drvdata(cpu_dai); + struct spdif_in_dev *host = snd_soc_dai_get_drvdata(dai); - if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) - return -EINVAL; + dai->capture_dma_data = &host->dma_params; - snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)&host->dma_params); return 0; } @@ -70,7 +67,6 @@ static void spdif_in_shutdown(struct snd_pcm_substream *substream, return; writel(0x0, host->io_base + SPDIF_IN_IRQ_MASK); - snd_soc_dai_set_dma_data(dai, substream, NULL); } static void spdif_in_format(struct spdif_in_dev *host, u32 format) @@ -151,13 +147,13 @@ static int spdif_in_trigger(struct snd_pcm_substream *substream, int cmd, } static struct snd_soc_dai_ops spdif_in_dai_ops = { - .startup = spdif_in_startup, .shutdown = spdif_in_shutdown, .trigger = spdif_in_trigger, .hw_params = spdif_in_hw_params, }; struct snd_soc_dai_driver spdif_in_dai = { + .probe = spdif_in_dai_probe, .capture = { .channels_min = 2, .channels_max = 2, diff --git a/sound/soc/spear/spdif_out.c b/sound/soc/spear/spdif_out.c index 1e3c3dda3598..12b4f2fcb9af 100644 --- a/sound/soc/spear/spdif_out.c +++ b/sound/soc/spear/spdif_out.c @@ -62,8 +62,6 @@ static int spdif_out_startup(struct snd_pcm_substream *substream, if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) return -EINVAL; - snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)&host->dma_params); - ret = clk_enable(host->clk); if (ret) return ret; @@ -84,7 +82,6 @@ static void spdif_out_shutdown(struct snd_pcm_substream *substream, clk_disable(host->clk); host->running = false; - snd_soc_dai_set_dma_data(dai, substream, NULL); } static void spdif_out_clock(struct spdif_out_dev *host, u32 core_freq, @@ -245,6 +242,10 @@ static const struct snd_kcontrol_new spdif_out_controls[] = { int spdif_soc_dai_probe(struct snd_soc_dai *dai) { + struct spdif_out_dev *host = snd_soc_dai_get_drvdata(dai); + + dai->playback_dma_data = &host->dma_params; + return snd_soc_add_dai_controls(dai, spdif_out_controls, ARRAY_SIZE(spdif_out_controls)); } -- cgit v1.2.3 From 52c102e534fd46a25aacab37bbaaa593929a2ca1 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 20 Apr 2013 19:29:07 +0200 Subject: ASoC: spear: Use generic dmaengine PCM Use the generic dmaengine PCM driver instead of a custom implementation. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/spear/spear_pcm.c | 152 +++++--------------------------------------- 1 file changed, 15 insertions(+), 137 deletions(-) diff --git a/sound/soc/spear/spear_pcm.c b/sound/soc/spear/spear_pcm.c index 2fbd4899d8ef..4707f2b862c3 100644 --- a/sound/soc/spear/spear_pcm.c +++ b/sound/soc/spear/spear_pcm.c @@ -13,19 +13,13 @@ #include #include -#include -#include #include -#include -#include -#include #include #include -#include #include #include -static struct snd_pcm_hardware spear_pcm_hardware = { +static const struct snd_pcm_hardware spear_pcm_hardware = { .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), @@ -37,149 +31,33 @@ static struct snd_pcm_hardware spear_pcm_hardware = { .fifo_size = 0, /* fifo size in bytes */ }; -static int spear_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) +static struct dma_chan *spear_pcm_request_chan(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_substream *substream) { - snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + struct spear_dma_data *dma_data; - return 0; -} - -static int spear_pcm_hw_free(struct snd_pcm_substream *substream) -{ - snd_pcm_set_runtime_buffer(substream, NULL); - - return 0; -} - -static int spear_pcm_open(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - - struct spear_dma_data *dma_data = (struct spear_dma_data *) - snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); - int ret; - - ret = snd_soc_set_runtime_hwparams(substream, &spear_pcm_hardware); - if (ret) - return ret; + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); - return snd_dmaengine_pcm_open_request_chan(substream, dma_data->filter, - dma_data); + return snd_dmaengine_pcm_request_channel(dma_data->filter, dma_data); } -static int spear_pcm_mmap(struct snd_pcm_substream *substream, - struct vm_area_struct *vma) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - - return dma_mmap_writecombine(substream->pcm->card->dev, vma, - runtime->dma_area, runtime->dma_addr, - runtime->dma_bytes); -} - -static struct snd_pcm_ops spear_pcm_ops = { - .open = spear_pcm_open, - .close = snd_dmaengine_pcm_close_release_chan, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = spear_pcm_hw_params, - .hw_free = spear_pcm_hw_free, - .trigger = snd_dmaengine_pcm_trigger, - .pointer = snd_dmaengine_pcm_pointer, - .mmap = spear_pcm_mmap, -}; - -static int -spear_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream, - size_t size) -{ - struct snd_pcm_substream *substream = pcm->streams[stream].substream; - struct snd_dma_buffer *buf = &substream->dma_buffer; - - buf->dev.type = SNDRV_DMA_TYPE_DEV; - buf->dev.dev = pcm->card->dev; - buf->private_data = NULL; - - buf->area = dma_alloc_writecombine(pcm->card->dev, size, - &buf->addr, GFP_KERNEL); - if (!buf->area) - return -ENOMEM; - - dev_info(buf->dev.dev, - " preallocate_dma_buffer: area=%p, addr=%p, size=%d\n", - (void *)buf->area, (void *)buf->addr, size); - - buf->bytes = size; - return 0; -} - -static void spear_pcm_free(struct snd_pcm *pcm) -{ - struct snd_pcm_substream *substream; - struct snd_dma_buffer *buf; - int stream; - - for (stream = 0; stream < 2; stream++) { - substream = pcm->streams[stream].substream; - if (!substream) - continue; - - buf = &substream->dma_buffer; - if (!buf || !buf->area) - continue; - - dma_free_writecombine(pcm->card->dev, buf->bytes, - buf->area, buf->addr); - buf->area = NULL; - } -} - -static u64 spear_pcm_dmamask = DMA_BIT_MASK(32); - -static int spear_pcm_new(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_card *card = rtd->card->snd_card; - int ret; - - if (!card->dev->dma_mask) - card->dev->dma_mask = &spear_pcm_dmamask; - if (!card->dev->coherent_dma_mask) - card->dev->coherent_dma_mask = DMA_BIT_MASK(32); - - if (rtd->cpu_dai->driver->playback.channels_min) { - ret = spear_pcm_preallocate_dma_buffer(rtd->pcm, - SNDRV_PCM_STREAM_PLAYBACK, - spear_pcm_hardware.buffer_bytes_max); - if (ret) - return ret; - } - - if (rtd->cpu_dai->driver->capture.channels_min) { - ret = spear_pcm_preallocate_dma_buffer(rtd->pcm, - SNDRV_PCM_STREAM_CAPTURE, - spear_pcm_hardware.buffer_bytes_max); - if (ret) - return ret; - } - - return 0; -} - -static struct snd_soc_platform_driver spear_soc_platform = { - .ops = &spear_pcm_ops, - .pcm_new = spear_pcm_new, - .pcm_free = spear_pcm_free, +static const struct snd_dmaengine_pcm_config spear_dmaengine_pcm_config = { + .pcm_hardware = &spear_pcm_hardware, + .compat_request_channel = spear_pcm_request_chan, + .prealloc_buffer_size = 16 * 1024, }; static int spear_soc_platform_probe(struct platform_device *pdev) { - return snd_soc_register_platform(&pdev->dev, &spear_soc_platform); + return snd_dmaengine_pcm_register(&pdev->dev, + &spear_dmaengine_pcm_config, + SND_DMAENGINE_PCM_FLAG_NO_DT | + SND_DMAENGINE_PCM_FLAG_COMPAT); } static int spear_soc_platform_remove(struct platform_device *pdev) { - snd_soc_unregister_platform(&pdev->dev); - + snd_dmaengine_pcm_unregister(&pdev->dev); return 0; } -- cgit v1.2.3 From bfcc74e6101a978b3987e767815595e5a9fec2ca Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 8 May 2013 11:47:38 +0200 Subject: ASoC: SPEAr spdif_{in,out}: use devm for clk and a few more cleanups MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop dev_set_drvdata as this is handled in the core and use devm_request_and_ioremap instead of devm_ioremap to properly register the address range used Signed-off-by: Uwe Kleine-König Signed-off-by: Mark Brown --- sound/soc/spear/spdif_in.c | 7 +------ sound/soc/spear/spdif_out.c | 7 ++----- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/sound/soc/spear/spdif_in.c b/sound/soc/spear/spdif_in.c index 82c838753c06..643ada6a1fa5 100644 --- a/sound/soc/spear/spdif_in.c +++ b/sound/soc/spear/spdif_in.c @@ -231,7 +231,7 @@ static int spdif_in_probe(struct platform_device *pdev) if (host->irq < 0) return -EINVAL; - host->clk = clk_get(&pdev->dev, NULL); + host->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(host->clk)) return PTR_ERR(host->clk); @@ -253,7 +253,6 @@ static int spdif_in_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, host->irq, spdif_in_irq, 0, "spdif-in", host); if (ret) { - clk_put(host->clk); dev_warn(&pdev->dev, "request_irq failed\n"); return ret; } @@ -273,14 +272,10 @@ static int spdif_in_remove(struct platform_device *pdev) struct spdif_in_dev *host = dev_get_drvdata(&pdev->dev); snd_soc_unregister_component(&pdev->dev); - dev_set_drvdata(&pdev->dev, NULL); - - clk_put(host->clk); return 0; } - static struct platform_driver spdif_in_driver = { .probe = spdif_in_probe, .remove = spdif_in_remove, diff --git a/sound/soc/spear/spdif_out.c b/sound/soc/spear/spdif_out.c index 12b4f2fcb9af..fd25cc5efe27 100644 --- a/sound/soc/spear/spdif_out.c +++ b/sound/soc/spear/spdif_out.c @@ -298,14 +298,14 @@ static int spdif_out_probe(struct platform_device *pdev) return -ENOMEM; } - host->io_base = devm_ioremap(&pdev->dev, res->start, + host->io_base = devm_request_and_ioremap(&pdev->dev, res->start, resource_size(res)); if (!host->io_base) { dev_warn(&pdev->dev, "ioremap failed\n"); return -ENOMEM; } - host->clk = clk_get(&pdev->dev, NULL); + host->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(host->clk)) return PTR_ERR(host->clk); @@ -334,9 +334,6 @@ static int spdif_out_remove(struct platform_device *pdev) struct spdif_out_dev *host = dev_get_drvdata(&pdev->dev); snd_soc_unregister_component(&pdev->dev); - dev_set_drvdata(&pdev->dev, NULL); - - clk_put(host->clk); return 0; } -- cgit v1.2.3 From f656df65743451d77e30e44e014b301721dff7cf Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Tue, 30 Apr 2013 16:09:54 +0200 Subject: ASoC: ux500: register controls to card instead of codec Update mop500_ab8500_machine_init to register mop500_ab8500_ctrls as card control instead of codec control, as it only contains SOC_DAPM_PIN_SWITCH definitions. Signed-off-by: Fabio Baltieri Signed-off-by: Mark Brown --- sound/soc/ux500/mop500_ab8500.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sound/soc/ux500/mop500_ab8500.c b/sound/soc/ux500/mop500_ab8500.c index 892ad9a05c9f..6a33788055f3 100644 --- a/sound/soc/ux500/mop500_ab8500.c +++ b/sound/soc/ux500/mop500_ab8500.c @@ -125,9 +125,9 @@ static int mop500_ab8500_set_mclk(struct device *dev, static int mclk_input_control_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); struct mop500_ab8500_drvdata *drvdata = - snd_soc_card_get_drvdata(codec->card); + snd_soc_card_get_drvdata(card); ucontrol->value.enumerated.item[0] = drvdata->mclk_sel; @@ -137,9 +137,9 @@ static int mclk_input_control_get(struct snd_kcontrol *kcontrol, static int mclk_input_control_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); struct mop500_ab8500_drvdata *drvdata = - snd_soc_card_get_drvdata(codec->card); + snd_soc_card_get_drvdata(card); unsigned int val = ucontrol->value.enumerated.item[0]; if (val > (unsigned int)MCLK_ULPCLK) @@ -385,7 +385,7 @@ int mop500_ab8500_machine_init(struct snd_soc_pcm_runtime *rtd) drvdata->mclk_sel = MCLK_ULPCLK; /* Add controls */ - ret = snd_soc_add_codec_controls(codec, mop500_ab8500_ctrls, + ret = snd_soc_add_card_controls(codec->card, mop500_ab8500_ctrls, ARRAY_SIZE(mop500_ab8500_ctrls)); if (ret < 0) { pr_err("%s: Failed to add machine-controls (%d)!\n", -- cgit v1.2.3 From 2e8e3880a15efacd21d68f77546ccd09f5e99521 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Thu, 2 May 2013 11:52:51 +0200 Subject: ASoC: ux500: drop clock gating widgets from machine driver Drop ab8500 clock gating widgets from mop500_ab8500_ctrls, as these bits are already controlled by ab8500 codec driver and should not be exposed to the user. Signed-off-by: Fabio Baltieri Signed-off-by: Mark Brown --- sound/soc/ux500/mop500_ab8500.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/sound/soc/ux500/mop500_ab8500.c b/sound/soc/ux500/mop500_ab8500.c index 6a33788055f3..44e25a291044 100644 --- a/sound/soc/ux500/mop500_ab8500.c +++ b/sound/soc/ux500/mop500_ab8500.c @@ -160,16 +160,6 @@ static struct snd_kcontrol_new mop500_ab8500_ctrls[] = { SOC_ENUM_EXT("Master Clock Select", soc_enum_mclk, mclk_input_control_get, mclk_input_control_put), - /* Digital interface - Clocks */ - SOC_SINGLE("Digital Interface Master Generator Switch", - AB8500_DIGIFCONF1, AB8500_DIGIFCONF1_ENMASTGEN, - 1, 0), - SOC_SINGLE("Digital Interface 0 Bit-clock Switch", - AB8500_DIGIFCONF1, AB8500_DIGIFCONF1_ENFSBITCLK0, - 1, 0), - SOC_SINGLE("Digital Interface 1 Bit-clock Switch", - AB8500_DIGIFCONF1, AB8500_DIGIFCONF1_ENFSBITCLK1, - 1, 0), SOC_DAPM_PIN_SWITCH("Headset Left"), SOC_DAPM_PIN_SWITCH("Headset Right"), SOC_DAPM_PIN_SWITCH("Earpiece"), -- cgit v1.2.3 From b9600b4b1cf8b8f06b6a5d025eff160f41950485 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Wed, 8 May 2013 09:14:16 +0200 Subject: ASoC: ab8500-codec: Add missing ad_to_slot definitions According to the AB8500 user manual AD to Slot register multiplexer accept values from 0 to 15 where: 0 to 7 corresponds to AD_OUTx slots 8 to 11 corresponds to zero output 12 to 15 sets the output in tristate mode Update enum_ad_to_slot_map array to reflect this definition. This also allows alsamixer to properly display the default configuration, as all controls are set to tristate (=12) at reset. Signed-off-by: Fabio Baltieri Acked-by: Lee Jones Signed-off-by: Mark Brown --- sound/soc/codecs/ab8500-codec.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c index a153b168129b..3126cac7b7c8 100644 --- a/sound/soc/codecs/ab8500-codec.c +++ b/sound/soc/codecs/ab8500-codec.c @@ -1496,6 +1496,12 @@ static const char * const enum_ad_to_slot_map[] = {"AD_OUT1", "AD_OUT7", "AD_OUT8", "zeroes", + "zeroes", + "zeroes", + "zeroes", + "tristate", + "tristate", + "tristate", "tristate"}; static SOC_ENUM_SINGLE_DECL(soc_enum_adslot0map, AB8500_ADSLOTSEL1, AB8500_ADSLOTSELX_EVEN_SHIFT, -- cgit v1.2.3 From 48dcf1d82fd8158ac8a9b843abb4965c69a19781 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 3 May 2013 14:39:21 +0530 Subject: ASoC: mid-x86: Remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown --- sound/soc/mid-x86/mfld_machine.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/mid-x86/mfld_machine.c b/sound/soc/mid-x86/mfld_machine.c index 4139116c33b5..78d582519891 100644 --- a/sound/soc/mid-x86/mfld_machine.c +++ b/sound/soc/mid-x86/mfld_machine.c @@ -425,7 +425,6 @@ static int snd_mfld_mc_remove(struct platform_device *pdev) free_irq(platform_get_irq(pdev, 0), mc_drv_ctx); snd_soc_unregister_card(&snd_soc_card_mfld); kfree(mc_drv_ctx); - platform_set_drvdata(pdev, NULL); return 0; } -- cgit v1.2.3 From 010187fb45368470177b4d42916856f22a08a824 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 12 May 2013 20:07:39 +0200 Subject: ASoC: jz4740-i2s: Use clk_prepare_enable/clk_disable_unprepare In preparation to switching the jz4740 clk driver to the common clk framework update the clk enable/disable calls to clk_prepare_enable/clk_disable_unprepare. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/jz4740/jz4740-i2s.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c index cafc6eda0ac5..4c849a49c72a 100644 --- a/sound/soc/jz4740/jz4740-i2s.c +++ b/sound/soc/jz4740/jz4740-i2s.c @@ -118,7 +118,7 @@ static int jz4740_i2s_startup(struct snd_pcm_substream *substream, ctrl |= JZ_AIC_CTRL_FLUSH; jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl); - clk_enable(i2s->clk_i2s); + clk_prepare_enable(i2s->clk_i2s); conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF); conf |= JZ_AIC_CONF_ENABLE; @@ -140,7 +140,7 @@ static void jz4740_i2s_shutdown(struct snd_pcm_substream *substream, conf &= ~JZ_AIC_CONF_ENABLE; jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf); - clk_disable(i2s->clk_i2s); + clk_disable_unprepare(i2s->clk_i2s); } static int jz4740_i2s_trigger(struct snd_pcm_substream *substream, int cmd, @@ -314,10 +314,10 @@ static int jz4740_i2s_suspend(struct snd_soc_dai *dai) conf &= ~JZ_AIC_CONF_ENABLE; jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf); - clk_disable(i2s->clk_i2s); + clk_disable_unprepare(i2s->clk_i2s); } - clk_disable(i2s->clk_aic); + clk_disable_unprepare(i2s->clk_aic); return 0; } @@ -327,10 +327,10 @@ static int jz4740_i2s_resume(struct snd_soc_dai *dai) struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); uint32_t conf; - clk_enable(i2s->clk_aic); + clk_prepare_enable(i2s->clk_aic); if (dai->active) { - clk_enable(i2s->clk_i2s); + clk_prepare_enable(i2s->clk_i2s); conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF); conf |= JZ_AIC_CONF_ENABLE; @@ -368,7 +368,7 @@ static int jz4740_i2s_dai_probe(struct snd_soc_dai *dai) struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); uint32_t conf; - clk_enable(i2s->clk_aic); + clk_prepare_enable(i2s->clk_aic); jz4740_i2c_init_pcm_config(i2s); @@ -388,7 +388,7 @@ static int jz4740_i2s_dai_remove(struct snd_soc_dai *dai) { struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); - clk_disable(i2s->clk_aic); + clk_disable_unprepare(i2s->clk_aic); return 0; } -- cgit v1.2.3 From d1a0a2995855e8d583c5cf97dbf0f6b376668c45 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 10 May 2013 21:40:10 +0100 Subject: ASoC: wm8994: Support EFS mode for FLL Later WM8994 devices support an enhanced accuracy FLL divisor mode called EFS which allows more precise selection of fractional source to output ratios. Support this on relevant devices. Signed-off-by: Mark Brown --- sound/soc/codecs/wm8994.c | 49 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 1eb152cb1097..9f32dd8660d5 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -2005,15 +2006,16 @@ struct fll_div { u16 outdiv; u16 n; u16 k; + u16 lambda; u16 clk_ref_div; u16 fll_fratio; }; -static int wm8994_get_fll_config(struct fll_div *fll, +static int wm8994_get_fll_config(struct wm8994 *control, struct fll_div *fll, int freq_in, int freq_out) { u64 Kpart; - unsigned int K, Ndiv, Nmod; + unsigned int K, Ndiv, Nmod, gcd_fll; pr_debug("FLL input=%dHz, output=%dHz\n", freq_in, freq_out); @@ -2062,20 +2064,30 @@ static int wm8994_get_fll_config(struct fll_div *fll, Nmod = freq_out % freq_in; pr_debug("Nmod=%d\n", Nmod); - /* Calculate fractional part - scale up so we can round. */ - Kpart = FIXED_FLL_SIZE * (long long)Nmod; + switch (control->type) { + case WM8994: + /* Calculate fractional part - scale up so we can round. */ + Kpart = FIXED_FLL_SIZE * (long long)Nmod; - do_div(Kpart, freq_in); + do_div(Kpart, freq_in); - K = Kpart & 0xFFFFFFFF; + K = Kpart & 0xFFFFFFFF; - if ((K % 10) >= 5) - K += 5; + if ((K % 10) >= 5) + K += 5; - /* Move down to proper range now rounding is done */ - fll->k = K / 10; + /* Move down to proper range now rounding is done */ + fll->k = K / 10; - pr_debug("N=%x K=%x\n", fll->n, fll->k); + pr_debug("N=%x K=%x\n", fll->n, fll->k); + + default: + gcd_fll = gcd(freq_out, freq_in); + + fll->k = (freq_out - (freq_in * fll->n)) / gcd_fll; + fll->lambda = freq_in / gcd_fll; + + } return 0; } @@ -2139,9 +2151,9 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src, * analysis bugs spewing warnings. */ if (freq_out) - ret = wm8994_get_fll_config(&fll, freq_in, freq_out); + ret = wm8994_get_fll_config(control, &fll, freq_in, freq_out); else - ret = wm8994_get_fll_config(&fll, wm8994->fll[id].in, + ret = wm8994_get_fll_config(control, &fll, wm8994->fll[id].in, wm8994->fll[id].out); if (ret < 0) return ret; @@ -2186,6 +2198,17 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src, WM8994_FLL1_N_MASK, fll.n << WM8994_FLL1_N_SHIFT); + if (fll.lambda) { + snd_soc_update_bits(codec, WM8958_FLL1_EFS_1 + reg_offset, + WM8958_FLL1_LAMBDA_MASK, + fll.lambda); + snd_soc_update_bits(codec, WM8958_FLL1_EFS_2 + reg_offset, + WM8958_FLL1_EFS_ENA, WM8958_FLL1_EFS_ENA); + } else { + snd_soc_update_bits(codec, WM8958_FLL1_EFS_2 + reg_offset, + WM8958_FLL1_EFS_ENA, 0); + } + snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_5 + reg_offset, WM8994_FLL1_FRC_NCO | WM8958_FLL1_BYP | WM8994_FLL1_REFCLK_DIV_MASK | -- cgit v1.2.3 From 62477adf5f4ede918a97e648a5173b00bbbb17cc Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Mon, 13 May 2013 13:30:56 +0800 Subject: ASoC: mxs: move to use generic DMA helper With mxs-dma converted to generic DMA bindings, let's move mxs-pcm to use it by removing flages SND_DMAENGINE_PCM_FLAG_NO_DT and SND_DMAENGINE_PCM_FLAG_COMPAT. As the result, those mxs custom dma params code can be removed now. Signed-off-by: Shawn Guo Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/mxs-saif.txt | 17 ++++++++----- sound/soc/mxs/mxs-pcm.c | 18 -------------- sound/soc/mxs/mxs-pcm.h | 7 ------ sound/soc/mxs/mxs-saif.c | 29 +--------------------- sound/soc/mxs/mxs-saif.h | 1 - 5 files changed, 12 insertions(+), 60 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/mxs-saif.txt b/Documentation/devicetree/bindings/sound/mxs-saif.txt index c37ba6143d9b..7ba07a118e37 100644 --- a/Documentation/devicetree/bindings/sound/mxs-saif.txt +++ b/Documentation/devicetree/bindings/sound/mxs-saif.txt @@ -3,8 +3,11 @@ Required properties: - compatible: Should be "fsl,-saif" - reg: Should contain registers location and length -- interrupts: Should contain ERROR and DMA interrupts -- fsl,saif-dma-channel: APBX DMA channel for the SAIF +- interrupts: Should contain ERROR interrupt number +- dmas: DMA specifier, consisting of a phandle to DMA controller node + and SAIF DMA channel ID. + Refer to dma.txt and fsl-mxs-dma.txt for details. +- dma-names: Must be "rx-tx". Optional properties: - fsl,saif-master: phandle to the master SAIF. It's only required for @@ -23,14 +26,16 @@ aliases { saif0: saif@80042000 { compatible = "fsl,imx28-saif"; reg = <0x80042000 2000>; - interrupts = <59 80>; - fsl,saif-dma-channel = <4>; + interrupts = <59>; + dmas = <&dma_apbx 4>; + dma-names = "rx-tx"; }; saif1: saif@80046000 { compatible = "fsl,imx28-saif"; reg = <0x80046000 2000>; - interrupts = <58 81>; - fsl,saif-dma-channel = <5>; + interrupts = <58>; + dmas = <&dma_apbx 5>; + dma-names = "rx-tx"; fsl,saif-master = <&saif0>; }; diff --git a/sound/soc/mxs/mxs-pcm.c b/sound/soc/mxs/mxs-pcm.c index b41fffc056fb..b16abbbf7764 100644 --- a/sound/soc/mxs/mxs-pcm.c +++ b/sound/soc/mxs/mxs-pcm.c @@ -49,24 +49,8 @@ static const struct snd_pcm_hardware snd_mxs_hardware = { .fifo_size = 32, }; -static bool filter(struct dma_chan *chan, void *param) -{ - struct mxs_pcm_dma_params *dma_params = param; - - if (!mxs_dma_is_apbx(chan)) - return false; - - if (chan->chan_id != dma_params->chan_num) - return false; - - chan->private = &dma_params->dma_data; - - return true; -} - static const struct snd_dmaengine_pcm_config mxs_dmaengine_pcm_config = { .pcm_hardware = &snd_mxs_hardware, - .compat_filter_fn = filter, .prealloc_buffer_size = 64 * 1024, }; @@ -74,8 +58,6 @@ int mxs_pcm_platform_register(struct device *dev) { return snd_dmaengine_pcm_register(dev, &mxs_dmaengine_pcm_config, SND_DMAENGINE_PCM_FLAG_NO_RESIDUE | - SND_DMAENGINE_PCM_FLAG_NO_DT | - SND_DMAENGINE_PCM_FLAG_COMPAT | SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX); } EXPORT_SYMBOL_GPL(mxs_pcm_platform_register); diff --git a/sound/soc/mxs/mxs-pcm.h b/sound/soc/mxs/mxs-pcm.h index 3aa918f9ed3e..bc685b67cac7 100644 --- a/sound/soc/mxs/mxs-pcm.h +++ b/sound/soc/mxs/mxs-pcm.h @@ -19,13 +19,6 @@ #ifndef _MXS_PCM_H #define _MXS_PCM_H -#include - -struct mxs_pcm_dma_params { - struct mxs_dma_data dma_data; - int chan_num; -}; - int mxs_pcm_platform_register(struct device *dev); void mxs_pcm_platform_unregister(struct device *dev); diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c index 71a972f5af97..49d870034bc3 100644 --- a/sound/soc/mxs/mxs-saif.c +++ b/sound/soc/mxs/mxs-saif.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -604,8 +603,6 @@ static int mxs_saif_dai_probe(struct snd_soc_dai *dai) struct mxs_saif *saif = dev_get_drvdata(dai->dev); snd_soc_dai_set_drvdata(dai, saif); - dai->playback_dma_data = &saif->dma_param; - dai->capture_dma_data = &saif->dma_param; return 0; } @@ -664,7 +661,7 @@ static irqreturn_t mxs_saif_irq(int irq, void *dev_id) static int mxs_saif_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; - struct resource *iores, *dmares; + struct resource *iores; struct mxs_saif *saif; int ret = 0; struct device_node *master; @@ -719,22 +716,6 @@ static int mxs_saif_probe(struct platform_device *pdev) if (IS_ERR(saif->base)) return PTR_ERR(saif->base); - dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (!dmares) { - /* - * TODO: This is a temporary solution and should be changed - * to use generic DMA binding later when the helplers get in. - */ - ret = of_property_read_u32(np, "fsl,saif-dma-channel", - &saif->dma_param.chan_num); - if (ret) { - dev_err(&pdev->dev, "failed to get dma channel\n"); - return ret; - } - } else { - saif->dma_param.chan_num = dmares->start; - } - saif->irq = platform_get_irq(pdev, 0); if (saif->irq < 0) { ret = saif->irq; @@ -751,14 +732,6 @@ static int mxs_saif_probe(struct platform_device *pdev) return ret; } - saif->dma_param.dma_data.chan_irq = platform_get_irq(pdev, 1); - if (saif->dma_param.dma_data.chan_irq < 0) { - ret = saif->dma_param.dma_data.chan_irq; - dev_err(&pdev->dev, "failed to get dma irq resource: %d\n", - ret); - return ret; - } - platform_set_drvdata(pdev, saif); ret = snd_soc_register_component(&pdev->dev, &mxs_saif_component, diff --git a/sound/soc/mxs/mxs-saif.h b/sound/soc/mxs/mxs-saif.h index 3cb342e5bc90..53eaa4bf0e27 100644 --- a/sound/soc/mxs/mxs-saif.h +++ b/sound/soc/mxs/mxs-saif.h @@ -117,7 +117,6 @@ struct mxs_saif { unsigned int mclk_in_use; void __iomem *base; int irq; - struct mxs_pcm_dma_params dma_param; unsigned int id; unsigned int master_id; unsigned int cur_rate; -- cgit v1.2.3 From 6dc81f6fc0eaf0714bc6e959f8769705f41fd708 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 13 May 2013 13:45:09 +0300 Subject: spi/pxa2xx: fix compile warning in pxa2xx_spi_acpi_get_pdata() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit cbfd6a21b6f (spi/pxa2xx: Convert to devm_ioremap_resource()) converted the driver to use devm_ioremap_resource(). However it causes following warning to be emitted: drivers/spi/spi-pxa2xx.c: In function ‘pxa2xx_spi_acpi_get_pdata’: drivers/spi/spi-pxa2xx.c:1094:3: warning: return makes pointer from integer without a cast [enabled by default] Fix this by returning NULL as it was done previously (error printing is already done by devm_ioremap_resource()). Signed-off-by: Mika Westerberg Cc: Sachin Kamat Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index f5d84d6f8222..e5d782332ca5 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1091,7 +1091,7 @@ pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev) ssp->phys_base = res->start; ssp->mmio_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(ssp->mmio_base)) - return PTR_ERR(ssp->mmio_base); + return NULL; ssp->clk = devm_clk_get(&pdev->dev, NULL); ssp->irq = platform_get_irq(pdev, 0); -- cgit v1.2.3 From cddb339badb03dd96a5272195eec17e7899df154 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 13 May 2013 13:45:10 +0300 Subject: spi/pxa2xx: convert to dma_request_slave_channel_compat() Now that we have these nice DMA API helper functions we can take advantage of those instead of open-coding the channel/request line extraction from ACPI. Use the _compat version which still allows passing the channel/request line from platform data. Signed-off-by: Mika Westerberg Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx-dma.c | 11 ++++++----- drivers/spi/spi-pxa2xx.c | 34 ++-------------------------------- 2 files changed, 8 insertions(+), 37 deletions(-) diff --git a/drivers/spi/spi-pxa2xx-dma.c b/drivers/spi/spi-pxa2xx-dma.c index c735c5a008a2..f4cb744bf23f 100644 --- a/drivers/spi/spi-pxa2xx-dma.c +++ b/drivers/spi/spi-pxa2xx-dma.c @@ -327,22 +327,23 @@ void pxa2xx_spi_dma_start(struct driver_data *drv_data) int pxa2xx_spi_dma_setup(struct driver_data *drv_data) { struct pxa2xx_spi_master *pdata = drv_data->master_info; + struct device *dev = &drv_data->pdev->dev; dma_cap_mask_t mask; dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); - drv_data->dummy = devm_kzalloc(&drv_data->pdev->dev, SZ_2K, GFP_KERNEL); + drv_data->dummy = devm_kzalloc(dev, SZ_2K, GFP_KERNEL); if (!drv_data->dummy) return -ENOMEM; - drv_data->tx_chan = dma_request_channel(mask, pxa2xx_spi_dma_filter, - pdata); + drv_data->tx_chan = dma_request_slave_channel_compat(mask, + pxa2xx_spi_dma_filter, pdata, dev, "tx"); if (!drv_data->tx_chan) return -ENODEV; - drv_data->rx_chan = dma_request_channel(mask, pxa2xx_spi_dma_filter, - pdata); + drv_data->rx_chan = dma_request_slave_channel_compat(mask, + pxa2xx_spi_dma_filter, pdata, dev, "rx"); if (!drv_data->rx_chan) { dma_release_channel(drv_data->tx_chan); drv_data->tx_chan = NULL; diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index e5d782332ca5..d20b31ae96d3 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1040,32 +1040,10 @@ static void cleanup(struct spi_device *spi) } #ifdef CONFIG_ACPI -static int pxa2xx_spi_acpi_add_dma(struct acpi_resource *res, void *data) -{ - struct pxa2xx_spi_master *pdata = data; - - if (res->type == ACPI_RESOURCE_TYPE_FIXED_DMA) { - const struct acpi_resource_fixed_dma *dma; - - dma = &res->data.fixed_dma; - if (pdata->tx_slave_id < 0) { - pdata->tx_slave_id = dma->request_lines; - pdata->tx_chan_id = dma->channels; - } else if (pdata->rx_slave_id < 0) { - pdata->rx_slave_id = dma->request_lines; - pdata->rx_chan_id = dma->channels; - } - } - - /* Tell the ACPI core to skip this resource */ - return 1; -} - static struct pxa2xx_spi_master * pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev) { struct pxa2xx_spi_master *pdata; - struct list_head resource_list; struct acpi_device *adev; struct ssp_device *ssp; struct resource *res; @@ -1103,15 +1081,7 @@ pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev) ssp->port_id = devid; pdata->num_chipselect = 1; - pdata->rx_slave_id = -1; - pdata->tx_slave_id = -1; - - INIT_LIST_HEAD(&resource_list); - acpi_dev_get_resources(adev, &resource_list, pxa2xx_spi_acpi_add_dma, - pdata); - acpi_dev_free_resource_list(&resource_list); - - pdata->enable_dma = pdata->rx_slave_id >= 0 && pdata->tx_slave_id >= 0; + pdata->enable_dma = true; return pdata; } @@ -1214,7 +1184,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) if (platform_info->enable_dma) { status = pxa2xx_spi_dma_setup(drv_data); if (status) { - dev_warn(dev, "failed to setup DMA, using PIO\n"); + dev_dbg(dev, "no DMA channels available, using PIO\n"); platform_info->enable_dma = false; } } -- cgit v1.2.3 From 4b30f2a1218220c295b01af6f219ab0477064a74 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 13 May 2013 13:45:11 +0300 Subject: spi/pxa2xx: add Intel BayTrail ACPI ID Intel BayTrail has one general purpose SPI controller that is compatible with Intel Low Power Subsystem SPI. The controller is enumerated from ACPI namespace with ACPI ID 80860F0E. Signed-off-by: Mika Westerberg Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index d20b31ae96d3..f66995353e70 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1089,6 +1089,7 @@ pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev) static struct acpi_device_id pxa2xx_spi_acpi_match[] = { { "INT33C0", 0 }, { "INT33C1", 0 }, + { "80860F0E", 0 }, { }, }; MODULE_DEVICE_TABLE(acpi, pxa2xx_spi_acpi_match); -- cgit v1.2.3 From 89e8773075bae055090db518bf2085c0d40ca9d5 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Fri, 3 May 2013 16:27:12 +0900 Subject: spi: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Acked-by: Grant Likely Signed-off-by: Mark Brown --- drivers/spi/spi-altera.c | 2 -- drivers/spi/spi-ath79.c | 2 -- drivers/spi/spi-au1550.c | 2 -- drivers/spi/spi-bcm63xx.c | 3 --- drivers/spi/spi-bfin-sport.c | 3 --- drivers/spi/spi-bfin5xx.c | 3 --- drivers/spi/spi-clps711x.c | 2 -- drivers/spi/spi-coldfire-qspi.c | 1 - drivers/spi/spi-dw-mmio.c | 2 -- drivers/spi/spi-ep93xx.c | 2 -- drivers/spi/spi-gpio.c | 2 -- drivers/spi/spi-imx.c | 3 --- drivers/spi/spi-oc-tiny.c | 2 -- drivers/spi/spi-pxa2xx.c | 3 --- drivers/spi/spi-s3c64xx.c | 2 -- 15 files changed, 34 deletions(-) diff --git a/drivers/spi/spi-altera.c b/drivers/spi/spi-altera.c index a537f8dffc09..81b9adb6e766 100644 --- a/drivers/spi/spi-altera.c +++ b/drivers/spi/spi-altera.c @@ -285,7 +285,6 @@ static int altera_spi_probe(struct platform_device *pdev) exit_busy: err = -EBUSY; exit: - platform_set_drvdata(pdev, NULL); spi_master_put(master); return err; } @@ -296,7 +295,6 @@ static int altera_spi_remove(struct platform_device *dev) struct spi_master *master = hw->bitbang.master; spi_bitbang_stop(&hw->bitbang); - platform_set_drvdata(dev, NULL); spi_master_put(master); return 0; } diff --git a/drivers/spi/spi-ath79.c b/drivers/spi/spi-ath79.c index e504b7636058..6f6d45548050 100644 --- a/drivers/spi/spi-ath79.c +++ b/drivers/spi/spi-ath79.c @@ -287,7 +287,6 @@ err_clk_put: err_unmap: iounmap(sp->base); err_put_master: - platform_set_drvdata(pdev, NULL); spi_master_put(sp->bitbang.master); return ret; @@ -302,7 +301,6 @@ static int ath79_spi_remove(struct platform_device *pdev) clk_disable(sp->clk); clk_put(sp->clk); iounmap(sp->base); - platform_set_drvdata(pdev, NULL); spi_master_put(sp->bitbang.master); return 0; diff --git a/drivers/spi/spi-au1550.c b/drivers/spi/spi-au1550.c index 44dd34b6ad09..39560f45ba1d 100644 --- a/drivers/spi/spi-au1550.c +++ b/drivers/spi/spi-au1550.c @@ -987,8 +987,6 @@ static int au1550_spi_remove(struct platform_device *pdev) au1xxx_dbdma_chan_free(hw->dma_tx_ch); } - platform_set_drvdata(pdev, NULL); - spi_master_put(hw->master); return 0; } diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index a4ec5f4ec817..099d0839bbd4 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c @@ -469,7 +469,6 @@ static int bcm63xx_spi_probe(struct platform_device *pdev) out_clk_disable: clk_disable_unprepare(clk); out_err: - platform_set_drvdata(pdev, NULL); spi_master_put(master); out_clk: clk_put(clk); @@ -491,8 +490,6 @@ static int bcm63xx_spi_remove(struct platform_device *pdev) clk_disable_unprepare(bs->clk); clk_put(bs->clk); - platform_set_drvdata(pdev, 0); - spi_master_put(master); return 0; diff --git a/drivers/spi/spi-bfin-sport.c b/drivers/spi/spi-bfin-sport.c index 39b0d1711b4e..6d04c80559bb 100644 --- a/drivers/spi/spi-bfin-sport.c +++ b/drivers/spi/spi-bfin-sport.c @@ -882,9 +882,6 @@ static int bfin_sport_spi_remove(struct platform_device *pdev) peripheral_free_list(drv_data->pin_req); - /* Prevent double remove */ - platform_set_drvdata(pdev, NULL); - return 0; } diff --git a/drivers/spi/spi-bfin5xx.c b/drivers/spi/spi-bfin5xx.c index 317f564c899c..68ca4449e36f 100644 --- a/drivers/spi/spi-bfin5xx.c +++ b/drivers/spi/spi-bfin5xx.c @@ -1418,9 +1418,6 @@ static int bfin_spi_remove(struct platform_device *pdev) peripheral_free_list(drv_data->pin_req); - /* Prevent double remove */ - platform_set_drvdata(pdev, NULL); - return 0; } diff --git a/drivers/spi/spi-clps711x.c b/drivers/spi/spi-clps711x.c index a11cbf02691a..6859a0294c51 100644 --- a/drivers/spi/spi-clps711x.c +++ b/drivers/spi/spi-clps711x.c @@ -254,7 +254,6 @@ err_out: if (gpio_is_valid(hw->chipselect[i])) gpio_free(hw->chipselect[i]); - platform_set_drvdata(pdev, NULL); spi_master_put(master); kfree(master); @@ -274,7 +273,6 @@ static int spi_clps711x_remove(struct platform_device *pdev) gpio_free(hw->chipselect[i]); devm_clk_put(&pdev->dev, hw->spi_clk); - platform_set_drvdata(pdev, NULL); spi_unregister_master(master); kfree(master); diff --git a/drivers/spi/spi-coldfire-qspi.c b/drivers/spi/spi-coldfire-qspi.c index 7b5cc9e4e94d..3f1766003e68 100644 --- a/drivers/spi/spi-coldfire-qspi.c +++ b/drivers/spi/spi-coldfire-qspi.c @@ -524,7 +524,6 @@ static int mcfqspi_remove(struct platform_device *pdev) /* disable the hardware (set the baud rate to 0) */ mcfqspi_wr_qmr(mcfqspi, MCFQSPI_QMR_MSTR); - platform_set_drvdata(pdev, NULL); mcfqspi_cs_teardown(mcfqspi); clk_disable(mcfqspi->clk); clk_put(mcfqspi->clk); diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c index 4a6d5c9057a4..4aa8be865cc0 100644 --- a/drivers/spi/spi-dw-mmio.c +++ b/drivers/spi/spi-dw-mmio.c @@ -111,8 +111,6 @@ static int dw_spi_mmio_remove(struct platform_device *pdev) struct dw_spi_mmio *dwsmmio = platform_get_drvdata(pdev); struct resource *mem; - platform_set_drvdata(pdev, NULL); - clk_disable(dwsmmio->clk); clk_put(dwsmmio->clk); dwsmmio->clk = NULL; diff --git a/drivers/spi/spi-ep93xx.c b/drivers/spi/spi-ep93xx.c index d7bac60253c9..8d4f2a6aab90 100644 --- a/drivers/spi/spi-ep93xx.c +++ b/drivers/spi/spi-ep93xx.c @@ -1132,7 +1132,6 @@ fail_put_clock: clk_put(espi->clk); fail_release_master: spi_master_put(master); - platform_set_drvdata(pdev, NULL); return error; } @@ -1167,7 +1166,6 @@ static int ep93xx_spi_remove(struct platform_device *pdev) ep93xx_spi_release_dma(espi); clk_put(espi->clk); - platform_set_drvdata(pdev, NULL); spi_unregister_master(master); return 0; diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index 0021fc4c45bc..9672c7b87889 100644 --- a/drivers/spi/spi-gpio.c +++ b/drivers/spi/spi-gpio.c @@ -514,8 +514,6 @@ static int spi_gpio_remove(struct platform_device *pdev) status = spi_bitbang_stop(&spi_gpio->bitbang); spi_master_put(spi_gpio->bitbang.master); - platform_set_drvdata(pdev, NULL); - if (SPI_MISO_GPIO != SPI_GPIO_NO_MISO) gpio_free(SPI_MISO_GPIO); if (SPI_MOSI_GPIO != SPI_GPIO_NO_MOSI) diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 0befeeb522f4..f4d7cacbab3b 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -902,7 +902,6 @@ out_gpio_free: } spi_master_put(master); kfree(master); - platform_set_drvdata(pdev, NULL); return ret; } @@ -929,8 +928,6 @@ static int spi_imx_remove(struct platform_device *pdev) release_mem_region(res->start, resource_size(res)); - platform_set_drvdata(pdev, NULL); - return 0; } diff --git a/drivers/spi/spi-oc-tiny.c b/drivers/spi/spi-oc-tiny.c index e60a776ed2d4..58deb79d046b 100644 --- a/drivers/spi/spi-oc-tiny.c +++ b/drivers/spi/spi-oc-tiny.c @@ -368,7 +368,6 @@ exit_gpio: exit_busy: err = -EBUSY; exit: - platform_set_drvdata(pdev, NULL); spi_master_put(master); return err; } @@ -382,7 +381,6 @@ static int tiny_spi_remove(struct platform_device *pdev) spi_bitbang_stop(&hw->bitbang); for (i = 0; i < hw->gpio_cs_count; i++) gpio_free(hw->gpio_cs[i]); - platform_set_drvdata(pdev, NULL); spi_master_put(master); return 0; } diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index f5d84d6f8222..5a7fa2cd1efe 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1299,9 +1299,6 @@ static int pxa2xx_spi_remove(struct platform_device *pdev) /* Disconnect from the SPI framework */ spi_unregister_master(drv_data->master); - /* Prevent double remove */ - platform_set_drvdata(pdev, NULL); - return 0; } diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 5000586cb98d..5f3759d9b8f9 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -1399,7 +1399,6 @@ err3: err2: clk_disable_unprepare(sdd->clk); err0: - platform_set_drvdata(pdev, NULL); spi_master_put(master); return ret; @@ -1420,7 +1419,6 @@ static int s3c64xx_spi_remove(struct platform_device *pdev) clk_disable_unprepare(sdd->clk); - platform_set_drvdata(pdev, NULL); spi_master_put(master); return 0; -- cgit v1.2.3 From 4488cc96c581f130f3e86283d514123dce0dd46b Mon Sep 17 00:00:00 2001 From: Steve Dickson Date: Thu, 2 May 2013 13:18:55 -0400 Subject: NFS: Add NFSv4.2 protocol constants Signed-off-by: Matthew N. Dodd Signed-off-by: Miguel Rodel Felipe Signed-off-by: Phua Eu Gene Signed-off-by: Khin Mi Mi Aung Signed-off-by: Steve Dickson Signed-off-by: J. Bruce Fields --- fs/nfsd/nfsd.h | 6 ++++++ include/linux/nfs4.h | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h index 07a473fd49bc..c0d93170585d 100644 --- a/fs/nfsd/nfsd.h +++ b/fs/nfsd/nfsd.h @@ -243,6 +243,12 @@ void nfsd_lockd_shutdown(void); #define nfserr_reject_deleg cpu_to_be32(NFS4ERR_REJECT_DELEG) #define nfserr_returnconflict cpu_to_be32(NFS4ERR_RETURNCONFLICT) #define nfserr_deleg_revoked cpu_to_be32(NFS4ERR_DELEG_REVOKED) +#define nfserr_partner_notsupp cpu_to_be32(NFS4ERR_PARTNER_NOTSUPP) +#define nfserr_partner_no_auth cpu_to_be32(NFS4ERR_PARTNER_NO_AUTH) +#define nfserr_metadata_notsupp cpu_to_be32(NFS4ERR_METADATA_NOTSUPP) +#define nfserr_offload_denied cpu_to_be32(NFS4ERR_OFFLOAD_DENIED) +#define nfserr_wrong_lfs cpu_to_be32(NFS4ERR_WRONG_LFS) +#define nfserr_badlabel cpu_to_be32(NFS4ERR_BADLABEL) /* error codes for internal use */ /* if a request fails due to kmalloc failure, it gets dropped. diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h index 7b8fc73810ad..7764aca1c6b7 100644 --- a/include/linux/nfs4.h +++ b/include/linux/nfs4.h @@ -219,6 +219,14 @@ enum nfsstat4 { NFS4ERR_REJECT_DELEG = 10085, /* on callback */ NFS4ERR_RETURNCONFLICT = 10086, /* outstanding layoutreturn */ NFS4ERR_DELEG_REVOKED = 10087, /* deleg./layout revoked */ + + /* nfs42 */ + NFS4ERR_PARTNER_NOTSUPP = 10088, + NFS4ERR_PARTNER_NO_AUTH = 10089, + NFS4ERR_METADATA_NOTSUPP = 10090, + NFS4ERR_OFFLOAD_DENIED = 10091, + NFS4ERR_WRONG_LFS = 10092, + NFS4ERR_BADLABEL = 10093, }; static inline bool seqid_mutating_err(u32 err) @@ -378,6 +386,7 @@ enum lock_type4 { #define FATTR4_WORD1_FS_LAYOUT_TYPES (1UL << 30) #define FATTR4_WORD2_LAYOUT_BLKSIZE (1UL << 1) #define FATTR4_WORD2_MDSTHRESHOLD (1UL << 4) +#define FATTR4_WORD2_SECURITY_LABEL (1UL << 17) /* MDS threshold bitmap bits */ #define THRESHOLD_RD (1UL << 0) -- cgit v1.2.3 From 2d6e75e894f355109d1f256cfdcfca9aafa90b9a Mon Sep 17 00:00:00 2001 From: Laurent Navet Date: Thu, 2 May 2013 14:13:30 +0200 Subject: spi: bmc2835: use devm_ioremap_resource() Replace a call to deprecated devm_request_and_ioremap by devm_ioremap_resource. Found with coccicheck and this semantic patch: scripts/coccinelle/api/devm_request_and_ioremap.cocci. Signed-off-by: Laurent Navet Signed-off-by: Mark Brown --- drivers/spi/spi-bcm2835.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index 89c0b5033114..a4185e492321 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -331,10 +331,9 @@ static int bcm2835_spi_probe(struct platform_device *pdev) goto out_master_put; } - bs->regs = devm_request_and_ioremap(&pdev->dev, res); - if (!bs->regs) { - dev_err(&pdev->dev, "could not request/map memory region\n"); - err = -ENODEV; + bs->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(bs->regs)) { + err = PTR_ERR(bs->regs); goto out_master_put; } -- cgit v1.2.3 From eb48241bb4484e56171fcb5beb39f530a2cd484e Mon Sep 17 00:00:00 2001 From: Zhao Hongjiang Date: Fri, 3 May 2013 18:08:24 +0800 Subject: nfsd: get rid of the unused functions in vfs The fh_lock_parent(), nfsd_truncate(), nfsd_notify_change() and nfsd_sync_dir() fuctions are neither implemented nor used, just remove them. Signed-off-by: Zhao Hongjiang Signed-off-by: J. Bruce Fields --- fs/nfsd/vfs.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/fs/nfsd/vfs.h b/fs/nfsd/vfs.h index 5b5894159f22..8d2b40d71669 100644 --- a/fs/nfsd/vfs.h +++ b/fs/nfsd/vfs.h @@ -39,7 +39,6 @@ typedef int (*nfsd_dirop_t)(struct inode *, struct dentry *, int, int); /* nfsd/vfs.c */ -int fh_lock_parent(struct svc_fh *, struct dentry *); int nfsd_racache_init(int); void nfsd_racache_shutdown(void); int nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp, @@ -92,17 +91,13 @@ __be32 nfsd_remove(struct svc_rqst *, struct svc_fh *, char *, int); __be32 nfsd_unlink(struct svc_rqst *, struct svc_fh *, int type, char *name, int len); -int nfsd_truncate(struct svc_rqst *, struct svc_fh *, - unsigned long size); __be32 nfsd_readdir(struct svc_rqst *, struct svc_fh *, loff_t *, struct readdir_cd *, filldir_t); __be32 nfsd_statfs(struct svc_rqst *, struct svc_fh *, struct kstatfs *, int access); -int nfsd_notify_change(struct inode *, struct iattr *); __be32 nfsd_permission(struct svc_rqst *, struct svc_export *, struct dentry *, int); -int nfsd_sync_dir(struct dentry *dp); #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) struct posix_acl *nfsd_get_posix_acl(struct svc_fh *, int); -- cgit v1.2.3 From 4f540e29dc20b87d460559bce184d2238237f48b Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 7 May 2013 11:57:52 -0400 Subject: nfsd4: store correct client minorversion for >=4.2 This code assumes that any client using exchange_id is using NFSv4.1, but with the introduction of 4.2 that will no longer true. This main effect of this is that client callbacks will use the same minorversion as that used on the exchange_id. Note that clients are forbidden from mixing 4.1 and 4.2 compounds. (See rfc 5661, section 2.7, #13: "A client MUST NOT attempt to use a stateid, filehandle, or similar returned object from the COMPOUND procedure with minor version X for another COMPOUND procedure with minor version Y, where X != Y.") However, we do not currently attempt to enforce this except in the case of mixing zero minor version with non-zero minor versions. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 316ec843dec2..91ead0ed9f11 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1709,7 +1709,7 @@ out_new: status = nfserr_jukebox; goto out; } - new->cl_minorversion = 1; + new->cl_minorversion = cstate->minorversion; gen_clid(new, nn); add_to_unconfirmed(new); -- cgit v1.2.3 From 0d422afb892e3f993cf934b76a2c2ef839c446e0 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 8 May 2013 18:05:41 -0400 Subject: security: cap_inode_getsecctx returning garbage We shouldn't be returning success from this function without also filling in the return values ctx and ctxlen. Note currently this doesn't appear to cause bugs since the only inode_getsecctx caller I can find is fs/sysfs/inode.c, which only calls this if security_inode_setsecurity succeeds. Assuming security_inode_setsecurity is set to cap_inode_setsecurity whenever inode_getsecctx is set to cap_inode_getsecctx, this function can never actually called. So I noticed this only because the server labeled NFS patches add a real caller. Acked-by: Serge E. Hallyn Signed-off-by: J. Bruce Fields --- security/capability.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/capability.c b/security/capability.c index 1728d4e375db..83efc90b344d 100644 --- a/security/capability.c +++ b/security/capability.c @@ -843,7 +843,7 @@ static int cap_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen) static int cap_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen) { - return 0; + return -EOPNOTSUPP; } #ifdef CONFIG_KEYS static int cap_key_alloc(struct key *key, const struct cred *cred, -- cgit v1.2.3 From 4bdc33ed5bd9fbaa243bda6fdccb22674aed6305 Mon Sep 17 00:00:00 2001 From: Steve Dickson Date: Thu, 2 May 2013 13:18:57 -0400 Subject: NFSDv4.2: Add NFS v4.2 support to the NFS server This enables NFSv4.2 support for the server. To enable this code do the following: echo "+4.2" >/proc/fs/nfsd/versions after the nfsd kernel module is loaded. On its own this does nothing except allow the server to respond to compounds with minorversion set to 2. All the new NFSv4.2 features are optional, so this is perfectly legal. Signed-off-by: Steve Dickson Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4xdr.c | 1 + fs/nfsd/nfsd.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 6cd86e0fe450..9aeacddafa3f 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -1567,6 +1567,7 @@ struct nfsd4_minorversion_ops { static struct nfsd4_minorversion_ops nfsd4_minorversion[] = { [0] = { nfsd4_dec_ops, ARRAY_SIZE(nfsd4_dec_ops) }, [1] = { nfsd41_dec_ops, ARRAY_SIZE(nfsd41_dec_ops) }, + [2] = { nfsd41_dec_ops, ARRAY_SIZE(nfsd41_dec_ops) }, }; static __be32 diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h index c0d93170585d..15e7e1d531f0 100644 --- a/fs/nfsd/nfsd.h +++ b/fs/nfsd/nfsd.h @@ -24,7 +24,7 @@ /* * nfsd version */ -#define NFSD_SUPPORTED_MINOR_VERSION 1 +#define NFSD_SUPPORTED_MINOR_VERSION 2 /* * Maximum blocksizes supported by daemon under various circumstances. */ -- cgit v1.2.3 From e9b3db6608a159f18bc4bf9657e4aeba24354ea1 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 6 May 2013 15:05:55 -0300 Subject: spi: spi-imx: Let device core handle pinctrl Since commit ab78029 (drivers/pinctrl: grab default handles from device core), we can rely on device core for handling pinctrl. So remove devm_pinctrl_get_select_default() from the driver. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown --- drivers/spi/spi-imx.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 0befeeb522f4..9821ef1deb6f 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -37,7 +37,6 @@ #include #include #include -#include #include @@ -760,7 +759,6 @@ static int spi_imx_probe(struct platform_device *pdev) struct spi_master *master; struct spi_imx_data *spi_imx; struct resource *res; - struct pinctrl *pinctrl; int i, ret, num_cs; if (!np && !mxc_platform_info) { @@ -848,12 +846,6 @@ static int spi_imx_probe(struct platform_device *pdev) goto out_iounmap; } - pinctrl = devm_pinctrl_get_select_default(&pdev->dev); - if (IS_ERR(pinctrl)) { - ret = PTR_ERR(pinctrl); - goto out_free_irq; - } - spi_imx->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); if (IS_ERR(spi_imx->clk_ipg)) { ret = PTR_ERR(spi_imx->clk_ipg); -- cgit v1.2.3 From 0514dd76ea556859d398aaff725817e1c97579ea Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 6 May 2013 20:32:09 +0100 Subject: spi/omap2: Let device core handle pinctrl Since commit ab78029 (drivers/pinctrl: grab default handles from device core) we can rely on device core for handling pinctrl so remove devm_pinctrl_get_select_default() from the driver. Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 86d2158946bb..000922abcb87 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -38,7 +38,6 @@ #include #include #include -#include #include @@ -1186,7 +1185,6 @@ static int omap2_mcspi_probe(struct platform_device *pdev) static int bus_num = 1; struct device_node *node = pdev->dev.of_node; const struct of_device_id *match; - struct pinctrl *pinctrl; master = spi_alloc_master(&pdev->dev, sizeof *mcspi); if (master == NULL) { @@ -1284,11 +1282,6 @@ static int omap2_mcspi_probe(struct platform_device *pdev) if (status < 0) goto dma_chnl_free; - pinctrl = devm_pinctrl_get_select_default(&pdev->dev); - if (IS_ERR(pinctrl)) - dev_warn(&pdev->dev, - "pins are not configured from the driver\n"); - pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT); pm_runtime_enable(&pdev->dev); -- cgit v1.2.3 From de1f9f270ea7fb7af37e1b62580e27b15273d63d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 6 May 2013 20:29:58 +0100 Subject: spi/sirf: Let device core handle pinctrl Since commit ab78029 (drivers/pinctrl: grab default handles from device core) we can rely on device core for handling pinctrl so remove devm_pinctrl_get_select_default() from the driver. Signed-off-by: Mark Brown Acked-by: Barry Song --- drivers/spi/spi-sirf.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/spi/spi-sirf.c b/drivers/spi/spi-sirf.c index 0808cd56bf8d..ac2ea8a1bd65 100644 --- a/drivers/spi/spi-sirf.c +++ b/drivers/spi/spi-sirf.c @@ -19,7 +19,6 @@ #include #include #include -#include #define DRIVER_NAME "sirfsoc_spi" @@ -127,7 +126,6 @@ struct sirfsoc_spi { void __iomem *base; u32 ctrl_freq; /* SPI controller clock speed */ struct clk *clk; - struct pinctrl *p; /* rx & tx bufs from the spi_transfer */ const void *tx; @@ -558,15 +556,10 @@ static int spi_sirfsoc_probe(struct platform_device *pdev) master->bus_num = pdev->id; sspi->bitbang.master->dev.of_node = pdev->dev.of_node; - sspi->p = pinctrl_get_select_default(&pdev->dev); - ret = IS_ERR(sspi->p); - if (ret) - goto free_master; - sspi->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(sspi->clk)) { ret = -EINVAL; - goto free_pin; + goto free_master; } clk_prepare_enable(sspi->clk); sspi->ctrl_freq = clk_get_rate(sspi->clk); @@ -594,8 +587,6 @@ static int spi_sirfsoc_probe(struct platform_device *pdev) free_clk: clk_disable_unprepare(sspi->clk); clk_put(sspi->clk); -free_pin: - pinctrl_put(sspi->p); free_master: spi_master_put(master); err_cs: @@ -618,7 +609,6 @@ static int spi_sirfsoc_remove(struct platform_device *pdev) } clk_disable_unprepare(sspi->clk); clk_put(sspi->clk); - pinctrl_put(sspi->p); spi_master_put(master); return 0; } -- cgit v1.2.3 From 86db3b04fe386caa98bb4be00408f082b60a74b0 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 6 May 2013 15:05:56 -0300 Subject: spi: spi-mxs: Let device core handle pinctrl Since commit ab78029 (drivers/pinctrl: grab default handles from device core), we can rely on device core for handling pinctrl. So remove devm_pinctrl_get_select_default() from the driver. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown --- drivers/spi/spi-mxs.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index 84982768cd10..88fe1cfcbc65 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -46,7 +46,6 @@ #include #include #include -#include #include #include #include @@ -506,7 +505,6 @@ static int mxs_spi_probe(struct platform_device *pdev) struct mxs_spi *spi; struct mxs_ssp *ssp; struct resource *iores; - struct pinctrl *pinctrl; struct clk *clk; void __iomem *base; int devid, clk_freq; @@ -528,10 +526,6 @@ static int mxs_spi_probe(struct platform_device *pdev) if (IS_ERR(base)) return PTR_ERR(base); - pinctrl = devm_pinctrl_get_select_default(&pdev->dev); - if (IS_ERR(pinctrl)) - return PTR_ERR(pinctrl); - clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(clk)) return PTR_ERR(clk); -- cgit v1.2.3 From 785d81e29bd237b4e76ca27c3ebcc3281e663596 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 20 Apr 2013 19:29:04 +0200 Subject: ASoC: ep93xx: Setup dma data in DAI probe This allows us to access the DAI DMA data when we create the PCM. We'll use this when converting ep39xx to generic DMA engine PCM driver. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/cirrus/ep93xx-ac97.c | 14 ++++---------- sound/soc/cirrus/ep93xx-i2s.c | 14 ++++---------- 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/sound/soc/cirrus/ep93xx-ac97.c b/sound/soc/cirrus/ep93xx-ac97.c index 840c9b18201e..3f4f88877c84 100644 --- a/sound/soc/cirrus/ep93xx-ac97.c +++ b/sound/soc/cirrus/ep93xx-ac97.c @@ -314,22 +314,15 @@ static int ep93xx_ac97_trigger(struct snd_pcm_substream *substream, return 0; } -static int ep93xx_ac97_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) +static int ep93xx_ac97_dai_probe(struct snd_soc_dai *dai) { - struct ep93xx_dma_data *dma_data; + dai->playback_dma_data = &ep93xx_ac97_pcm_out; + dai->capture_dma_data = &ep93xx_ac97_pcm_in; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - dma_data = &ep93xx_ac97_pcm_out; - else - dma_data = &ep93xx_ac97_pcm_in; - - snd_soc_dai_set_dma_data(dai, substream, dma_data); return 0; } static const struct snd_soc_dai_ops ep93xx_ac97_dai_ops = { - .startup = ep93xx_ac97_startup, .trigger = ep93xx_ac97_trigger, }; @@ -337,6 +330,7 @@ static struct snd_soc_dai_driver ep93xx_ac97_dai = { .name = "ep93xx-ac97", .id = 0, .ac97_control = 1, + .probe = ep93xx_ac97_dai_probe, .playback = { .stream_name = "AC97 Playback", .channels_min = 2, diff --git a/sound/soc/cirrus/ep93xx-i2s.c b/sound/soc/cirrus/ep93xx-i2s.c index 5c1102e9e159..ef731e687ebb 100644 --- a/sound/soc/cirrus/ep93xx-i2s.c +++ b/sound/soc/cirrus/ep93xx-i2s.c @@ -60,7 +60,6 @@ struct ep93xx_i2s_info { struct clk *mclk; struct clk *sclk; struct clk *lrclk; - struct ep93xx_dma_data *dma_data; void __iomem *regs; }; @@ -139,15 +138,11 @@ static void ep93xx_i2s_disable(struct ep93xx_i2s_info *info, int stream) } } -static int ep93xx_i2s_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) +static int ep93xx_i2s_dai_probe(struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai); - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + dai->playback_dma_data = &ep93xx_i2s_dma_data[SNDRV_PCM_STREAM_PLAYBACK]; + dai->capture_dma_data = &ep93xx_i2s_dma_data[SNDRV_PCM_STREAM_CAPTURE]; - snd_soc_dai_set_dma_data(cpu_dai, substream, - &info->dma_data[substream->stream]); return 0; } @@ -338,7 +333,6 @@ static int ep93xx_i2s_resume(struct snd_soc_dai *dai) #endif static const struct snd_soc_dai_ops ep93xx_i2s_dai_ops = { - .startup = ep93xx_i2s_startup, .shutdown = ep93xx_i2s_shutdown, .hw_params = ep93xx_i2s_hw_params, .set_sysclk = ep93xx_i2s_set_sysclk, @@ -349,6 +343,7 @@ static const struct snd_soc_dai_ops ep93xx_i2s_dai_ops = { static struct snd_soc_dai_driver ep93xx_i2s_dai = { .symmetric_rates= 1, + .probe = ep93xx_i2s_dai_probe, .suspend = ep93xx_i2s_suspend, .resume = ep93xx_i2s_resume, .playback = { @@ -407,7 +402,6 @@ static int ep93xx_i2s_probe(struct platform_device *pdev) } dev_set_drvdata(&pdev->dev, info); - info->dma_data = ep93xx_i2s_dma_data; err = snd_soc_register_component(&pdev->dev, &ep93xx_i2s_component, &ep93xx_i2s_dai, 1); -- cgit v1.2.3 From e27e8a60cb4ca8e3b047c5d6ee9afff9c4c5b61a Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 20 Apr 2013 19:29:05 +0200 Subject: ASoC: ep93xx: Use generic dmaengine PCM Use the generic dmaengine PCM driver instead of a custom implementation. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/cirrus/Kconfig | 2 +- sound/soc/cirrus/ep93xx-pcm.c | 138 ++++-------------------------------------- 2 files changed, 12 insertions(+), 128 deletions(-) diff --git a/sound/soc/cirrus/Kconfig b/sound/soc/cirrus/Kconfig index 88143db7e753..2c20f01e1f7e 100644 --- a/sound/soc/cirrus/Kconfig +++ b/sound/soc/cirrus/Kconfig @@ -1,7 +1,7 @@ config SND_EP93XX_SOC tristate "SoC Audio support for the Cirrus Logic EP93xx series" depends on ARCH_EP93XX && SND_SOC - select SND_SOC_DMAENGINE_PCM + select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for codecs attached to the EP93xx I2S or AC97 interfaces. diff --git a/sound/soc/cirrus/ep93xx-pcm.c b/sound/soc/cirrus/ep93xx-pcm.c index 488032690378..0e9f56e0d4b2 100644 --- a/sound/soc/cirrus/ep93xx-pcm.c +++ b/sound/soc/cirrus/ep93xx-pcm.c @@ -14,20 +14,14 @@ #include #include -#include -#include +#include #include -#include -#include #include -#include #include #include #include -#include -#include static const struct snd_pcm_hardware ep93xx_pcm_hardware = { .info = (SNDRV_PCM_INFO_MMAP | @@ -63,134 +57,24 @@ static bool ep93xx_pcm_dma_filter(struct dma_chan *chan, void *filter_param) return false; } -static int ep93xx_pcm_open(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - - snd_soc_set_runtime_hwparams(substream, &ep93xx_pcm_hardware); - - return snd_dmaengine_pcm_open_request_chan(substream, - ep93xx_pcm_dma_filter, - snd_soc_dai_get_dma_data(rtd->cpu_dai, substream)); -} - -static int ep93xx_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); - - return 0; -} - -static int ep93xx_pcm_hw_free(struct snd_pcm_substream *substream) -{ - snd_pcm_set_runtime_buffer(substream, NULL); - return 0; -} - -static int ep93xx_pcm_mmap(struct snd_pcm_substream *substream, - struct vm_area_struct *vma) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - - return dma_mmap_writecombine(substream->pcm->card->dev, vma, - runtime->dma_area, - runtime->dma_addr, - runtime->dma_bytes); -} - -static struct snd_pcm_ops ep93xx_pcm_ops = { - .open = ep93xx_pcm_open, - .close = snd_dmaengine_pcm_close_release_chan, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = ep93xx_pcm_hw_params, - .hw_free = ep93xx_pcm_hw_free, - .trigger = snd_dmaengine_pcm_trigger, - .pointer = snd_dmaengine_pcm_pointer_no_residue, - .mmap = ep93xx_pcm_mmap, -}; - -static int ep93xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) -{ - struct snd_pcm_substream *substream = pcm->streams[stream].substream; - struct snd_dma_buffer *buf = &substream->dma_buffer; - size_t size = ep93xx_pcm_hardware.buffer_bytes_max; - - buf->dev.type = SNDRV_DMA_TYPE_DEV; - buf->dev.dev = pcm->card->dev; - buf->private_data = NULL; - buf->area = dma_alloc_writecombine(pcm->card->dev, size, - &buf->addr, GFP_KERNEL); - buf->bytes = size; - - return (buf->area == NULL) ? -ENOMEM : 0; -} - -static void ep93xx_pcm_free_dma_buffers(struct snd_pcm *pcm) -{ - struct snd_pcm_substream *substream; - struct snd_dma_buffer *buf; - int stream; - - for (stream = 0; stream < 2; stream++) { - substream = pcm->streams[stream].substream; - if (!substream) - continue; - - buf = &substream->dma_buffer; - if (!buf->area) - continue; - - dma_free_writecombine(pcm->card->dev, buf->bytes, buf->area, - buf->addr); - buf->area = NULL; - } -} - -static u64 ep93xx_pcm_dmamask = DMA_BIT_MASK(32); - -static int ep93xx_pcm_new(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_card *card = rtd->card->snd_card; - struct snd_pcm *pcm = rtd->pcm; - int ret = 0; - - if (!card->dev->dma_mask) - card->dev->dma_mask = &ep93xx_pcm_dmamask; - if (!card->dev->coherent_dma_mask) - card->dev->coherent_dma_mask = DMA_BIT_MASK(32); - - if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { - ret = ep93xx_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_PLAYBACK); - if (ret) - return ret; - } - - if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { - ret = ep93xx_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_CAPTURE); - if (ret) - return ret; - } - - return 0; -} - -static struct snd_soc_platform_driver ep93xx_soc_platform = { - .ops = &ep93xx_pcm_ops, - .pcm_new = &ep93xx_pcm_new, - .pcm_free = &ep93xx_pcm_free_dma_buffers, +static const struct snd_dmaengine_pcm_config ep93xx_dmaengine_pcm_config = { + .pcm_hardware = &ep93xx_pcm_hardware, + .compat_filter_fn = ep93xx_pcm_dma_filter, + .prealloc_buffer_size = 131072, }; static int ep93xx_soc_platform_probe(struct platform_device *pdev) { - return snd_soc_register_platform(&pdev->dev, &ep93xx_soc_platform); + return snd_dmaengine_pcm_register(&pdev->dev, + &ep93xx_dmaengine_pcm_config, + SND_DMAENGINE_PCM_FLAG_NO_RESIDUE | + SND_DMAENGINE_PCM_FLAG_NO_DT | + SND_DMAENGINE_PCM_FLAG_COMPAT); } static int ep93xx_soc_platform_remove(struct platform_device *pdev) { - snd_soc_unregister_platform(&pdev->dev); + snd_dmaengine_pcm_unregister(&pdev->dev); return 0; } -- cgit v1.2.3 From bd5f6a344daff10b58dfefad628e4b203edeb831 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 28 Apr 2013 14:05:22 +0300 Subject: iwlwifi: move BUILD_RAxTID to transport It has nothing to do in FW API. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/commands.h | 4 ---- drivers/net/wireless/iwlwifi/pcie/tx.c | 4 ++++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/dvm/commands.h b/drivers/net/wireless/iwlwifi/dvm/commands.h index 95ca026ecc9d..19f3ce7646ab 100644 --- a/drivers/net/wireless/iwlwifi/dvm/commands.h +++ b/drivers/net/wireless/iwlwifi/dvm/commands.h @@ -838,10 +838,6 @@ struct iwl_qosparam_cmd { #define STA_MODIFY_DELBA_TID_MSK 0x10 #define STA_MODIFY_SLEEP_TX_COUNT_MSK 0x20 -/* Receiver address (actually, Rx station's index into station table), - * combined with Traffic ID (QOS priority), in format used by Tx Scheduler */ -#define BUILD_RAxTID(sta_id, tid) (((sta_id) << 4) + (tid)) - /* agn */ struct iwl_keyinfo { __le16 key_flags; diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index c5e30294c5ac..03ad2f417298 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -1045,6 +1045,10 @@ static inline void iwl_pcie_txq_set_inactive(struct iwl_trans *trans, (1 << SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN)); } +/* Receiver address (actually, Rx station's index into station table), + * combined with Traffic ID (QOS priority), in format used by Tx Scheduler */ +#define BUILD_RAxTID(sta_id, tid) (((sta_id) << 4) + (tid)) + void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo, int sta_id, int tid, int frame_limit, u16 ssn) { -- cgit v1.2.3 From 7df15b1e6f5994115bee369a527b50ec3521a39b Mon Sep 17 00:00:00 2001 From: Hila Gonen Date: Wed, 12 Dec 2012 11:16:19 +0200 Subject: iwlwifi: mvm: Add beacon filtering support Add iwl_beacon_filter_cmd struct, disable and enable beacon filtering as needed. Signed-off-by: Hila Gonen Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/mvm/fw-api-power.h | 76 +++++++++++++++++++++++++ drivers/net/wireless/iwlwifi/mvm/fw-api.h | 2 + drivers/net/wireless/iwlwifi/mvm/mac80211.c | 20 +++++++ drivers/net/wireless/iwlwifi/mvm/mvm.h | 12 ++++ drivers/net/wireless/iwlwifi/mvm/ops.c | 1 + drivers/net/wireless/iwlwifi/mvm/power.c | 67 ++++++++++++++++++++++ 6 files changed, 178 insertions(+) diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h index 81fe45f46be7..90fdfcda15b6 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h @@ -117,4 +117,80 @@ struct iwl_powertable_cmd { __le32 lprx_rssi_threshold; } __packed; +/** + * struct iwl_beacon_filter_cmd + * REPLY_BEACON_FILTERING_CMD = 0xd2 (command) + * @id_and_color: MAC contex identifier + * @bf_energy_delta: Used for RSSI filtering, if in 'normal' state. Send beacon + * to driver if delta in Energy values calculated for this and last + * passed beacon is greater than this threshold. Zero value means that + * the Energy change is ignored for beacon filtering, and beacon will + * not be forced to be sent to driver regardless of this delta. Typical + * energy delta 5dB. + * @bf_roaming_energy_delta: Used for RSSI filtering, if in 'roaming' state. + * Send beacon to driver if delta in Energy values calculated for this + * and last passed beacon is greater than this threshold. Zero value + * means that the Energy change is ignored for beacon filtering while in + * Roaming state, typical energy delta 1dB. + * @bf_roaming_state: Used for RSSI filtering. If absolute Energy values + * calculated for current beacon is less than the threshold, use + * Roaming Energy Delta Threshold, otherwise use normal Energy Delta + * Threshold. Typical energy threshold is -72dBm. + * @bf_temperature_delta: Send Beacon to driver if delta in temperature values + * calculated for this and the last passed beacon is greater than this + * threshold. Zero value means that the temperature changeis ignored for + * beacon filtering; beacons will not be forced to be sent to driver + * regardless of whether its temerature has been changed. + * @bf_enable_beacon_filter: 1, beacon filtering is enabled; 0, disabled. + * @bf_filter_escape_timer: Send beacons to to driver if no beacons were passed + * for a specific period of time. Units: Beacons. + * @ba_escape_timer: Fully receive and parse beacon if no beacons were passed + * for a longer period of time then this escape-timeout. Units: Beacons. + * @ba_enable_beacon_abort: 1, beacon abort is enabled; 0, disabled. + */ +struct iwl_beacon_filter_cmd { + u8 bf_energy_delta; + u8 bf_roaming_energy_delta; + u8 bf_roaming_state; + u8 bf_temperature_delta; + u8 bf_enable_beacon_filter; + u8 bf_debug_flag; + __le16 reserved1; + __le32 bf_escape_timer; + __le32 ba_escape_timer; + u8 ba_enable_beacon_abort; + u8 reserved2[3]; +} __packed; + +/* Beacon filtering and beacon abort */ +#define IWL_BF_ENERGY_DELTA_DEFAULT 5 +#define IWL_BF_ENERGY_DELTA_MAX 255 +#define IWL_BF_ENERGY_DELTA_MIN 0 + +#define IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT 1 +#define IWL_BF_ROAMING_ENERGY_DELTA_MAX 255 +#define IWL_BF_ROAMING_ENERGY_DELTA_MIN 0 + +#define IWL_BF_ROAMING_STATE_DEFAULT 72 +#define IWL_BF_ROAMING_STATE_MAX 255 +#define IWL_BF_ROAMING_STATE_MIN 0 + +#define IWL_BF_TEMPERATURE_DELTA_DEFAULT 5 +#define IWL_BF_TEMPERATURE_DELTA_MAX 255 +#define IWL_BF_TEMPERATURE_DELTA_MIN 0 + +#define IWL_BF_ENABLE_BEACON_FILTER_DEFAULT 1 + +#define IWL_BF_DEBUG_FLAG_DEFAULT 0 + +#define IWL_BF_ESCAPE_TIMER_DEFAULT 50 +#define IWL_BF_ESCAPE_TIMER_MAX 1024 +#define IWL_BF_ESCAPE_TIMER_MIN 0 + +#define IWL_BA_ESCAPE_TIMER_DEFAULT 3 +#define IWL_BA_ESCAPE_TIMER_MAX 1024 +#define IWL_BA_ESCAPE_TIMER_MIN 0 + +#define IWL_BA_ENABLE_BEACON_ABORT_DEFAULT 1 + #endif diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h index 191dcae8ba47..6031dbf83a96 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h @@ -170,6 +170,8 @@ enum { BT_COEX_PROT_ENV = 0xcd, BT_PROFILE_NOTIFICATION = 0xce, + REPLY_BEACON_FILTERING_CMD = 0xd2, + REPLY_DEBUG_CMD = 0xf0, DEBUG_LOG_MSG = 0xf7, diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index dd158ec571fb..e2cf6d92217b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -530,6 +530,17 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, */ iwl_mvm_power_update_mode(mvm, vif); + /* beacon filtering */ + if (!mvm->bf_allowed_vif && + vif->type == NL80211_IFTYPE_STATION && !vif->p2p){ + mvm->bf_allowed_vif = mvmvif; + vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER; + } + + ret = iwl_mvm_disable_beacon_filter(mvm, vif); + if (ret) + goto out_release; + /* * P2P_DEVICE interface does not have a channel context assigned to it, * so a dedicated PHY context is allocated to it and the corresponding @@ -646,6 +657,11 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw, mutex_lock(&mvm->mutex); + if (mvm->bf_allowed_vif == mvmvif) { + mvm->bf_allowed_vif = NULL; + vif->driver_flags &= ~IEEE80211_VIF_BEACON_FILTER; + } + iwl_mvm_vif_dbgfs_clean(mvm, vif); /* @@ -984,9 +1000,13 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, mvmvif->phy_ctxt->channel->band); } else if (old_state == IEEE80211_STA_ASSOC && new_state == IEEE80211_STA_AUTHORIZED) { + /* enable beacon filtering */ + WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif)); ret = 0; } else if (old_state == IEEE80211_STA_AUTHORIZED && new_state == IEEE80211_STA_ASSOC) { + /* disable beacon filtering */ + WARN_ON(iwl_mvm_disable_beacon_filter(mvm, vif)); ret = 0; } else if (old_state == IEEE80211_STA_ASSOC && new_state == IEEE80211_STA_AUTH) { diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index 8269bc562951..4fc64d50dc50 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -266,6 +266,12 @@ struct iwl_mvm { unsigned long status; + /* + * for beacon filtering - + * currently only one interface can be supported + */ + struct iwl_mvm_vif *bf_allowed_vif; + enum iwl_ucode_type cur_ucode; bool ucode_loaded; bool init_ucode_run; @@ -533,4 +539,10 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, enum ieee80211_rssi_event rssi_event); void iwl_mvm_bt_coex_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +/* beacon filtering */ +int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, + struct ieee80211_vif *vif); +int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm, + struct ieee80211_vif *vif); + #endif /* __IWL_MVM_H__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index fe031d304d1e..5bf1eaf9046a 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -292,6 +292,7 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = { CMD(BT_COEX_PROT_ENV), CMD(BT_PROFILE_NOTIFICATION), CMD(BT_CONFIG), + CMD(REPLY_BEACON_FILTERING_CMD), }; #undef CMD diff --git a/drivers/net/wireless/iwlwifi/mvm/power.c b/drivers/net/wireless/iwlwifi/mvm/power.c index ed77e437aac4..30a5c27bd623 100644 --- a/drivers/net/wireless/iwlwifi/mvm/power.c +++ b/drivers/net/wireless/iwlwifi/mvm/power.c @@ -178,3 +178,70 @@ int iwl_mvm_power_disable(struct iwl_mvm *mvm, struct ieee80211_vif *vif) return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_ASYNC, sizeof(cmd), &cmd); } + +static int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm, + struct iwl_beacon_filter_cmd *cmd) +{ + int ret; + + ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_BEACON_FILTERING_CMD, CMD_SYNC, + sizeof(struct iwl_beacon_filter_cmd), cmd); + + if (!ret) { + IWL_DEBUG_POWER(mvm, "ba_enable_beacon_abort is: %d\n", + cmd->ba_enable_beacon_abort); + IWL_DEBUG_POWER(mvm, "ba_escape_timer is: %d\n", + cmd->ba_escape_timer); + IWL_DEBUG_POWER(mvm, "bf_debug_flag is: %d\n", + cmd->bf_debug_flag); + IWL_DEBUG_POWER(mvm, "bf_enable_beacon_filter is: %d\n", + cmd->bf_enable_beacon_filter); + IWL_DEBUG_POWER(mvm, "bf_energy_delta is: %d\n", + cmd->bf_energy_delta); + IWL_DEBUG_POWER(mvm, "bf_escape_timer is: %d\n", + cmd->bf_escape_timer); + IWL_DEBUG_POWER(mvm, "bf_roaming_energy_delta is: %d\n", + cmd->bf_roaming_energy_delta); + IWL_DEBUG_POWER(mvm, "bf_roaming_state is: %d\n", + cmd->bf_roaming_state); + IWL_DEBUG_POWER(mvm, "bf_temperature_delta is: %d\n", + cmd->bf_temperature_delta); + } + return ret; +} + +int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_beacon_filter_cmd cmd = { + .bf_enable_beacon_filter = 1, + .bf_energy_delta = IWL_BF_ENERGY_DELTA_DEFAULT, + .bf_roaming_energy_delta = IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT, + .bf_roaming_state = IWL_BF_ROAMING_STATE_DEFAULT, + .bf_temperature_delta = IWL_BF_TEMPERATURE_DELTA_DEFAULT, + .bf_debug_flag = IWL_BF_DEBUG_FLAG_DEFAULT, + .bf_escape_timer = cpu_to_le32(IWL_BF_ESCAPE_TIMER_DEFAULT), + .ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_DEFAULT), + .ba_enable_beacon_abort = IWL_BA_ENABLE_BEACON_ABORT_DEFAULT, + }; + + if (mvmvif != mvm->bf_allowed_vif || + vif->type != NL80211_IFTYPE_STATION || vif->p2p) + return 0; + + return iwl_mvm_beacon_filter_send_cmd(mvm, &cmd); +} + +int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + struct iwl_beacon_filter_cmd cmd = { + .bf_enable_beacon_filter = 0, + }; + + if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) + return 0; + + return iwl_mvm_beacon_filter_send_cmd(mvm, &cmd); +} -- cgit v1.2.3 From c571573a027bb65ac415141f919df3c0fa0fedb4 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 30 Apr 2013 14:33:04 +0300 Subject: iwlwifi: pcie: prefer to load the firmware in one shot Users complained about allocation failures, so we loaded the firmware in small chunks (PAGE_SIZE). This makes the firmware restart considerably slower. So, always prefer to load it in one shot allocating a big chunk of coherent, and use smaller chunks as a fallback solution. On my laptop, this reduces the fw loading time from 120ms to 20ms. Signed-off-by: Emmanuel Grumbach Reviewed-by: Moshe Island Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/pcie/trans.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 50ba0a468f94..e5365196e5fe 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -405,20 +405,27 @@ static int iwl_pcie_load_section(struct iwl_trans *trans, u8 section_num, { u8 *v_addr; dma_addr_t p_addr; - u32 offset; + u32 offset, chunk_sz = section->len; int ret = 0; IWL_DEBUG_FW(trans, "[%d] uCode section being loaded...\n", section_num); - v_addr = dma_alloc_coherent(trans->dev, PAGE_SIZE, &p_addr, GFP_KERNEL); - if (!v_addr) - return -ENOMEM; + v_addr = dma_alloc_coherent(trans->dev, chunk_sz, &p_addr, + GFP_KERNEL | __GFP_NOWARN); + if (!v_addr) { + IWL_DEBUG_INFO(trans, "Falling back to small chunks of DMA\n"); + chunk_sz = PAGE_SIZE; + v_addr = dma_alloc_coherent(trans->dev, chunk_sz, + &p_addr, GFP_KERNEL); + if (!v_addr) + return -ENOMEM; + } - for (offset = 0; offset < section->len; offset += PAGE_SIZE) { + for (offset = 0; offset < section->len; offset += chunk_sz) { u32 copy_size; - copy_size = min_t(u32, PAGE_SIZE, section->len - offset); + copy_size = min_t(u32, chunk_sz, section->len - offset); memcpy(v_addr, (u8 *)section->data + offset, copy_size); ret = iwl_pcie_load_firmware_chunk(trans, @@ -432,7 +439,7 @@ static int iwl_pcie_load_section(struct iwl_trans *trans, u8 section_num, } } - dma_free_coherent(trans->dev, PAGE_SIZE, v_addr, p_addr); + dma_free_coherent(trans->dev, chunk_sz, v_addr, p_addr); return ret; } -- cgit v1.2.3 From fe0f2de30cc3417794517afe45f57e851b3188ae Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Thu, 21 Mar 2013 10:23:52 +0200 Subject: iwlwifi: mvm: Loosen the channel context/phy context coupling In current implementation, the phy context is tightly coupled with the channel context. This complicates the possibility of using the same phy context for both netdev interfaces and the P2P Device interface. To loosen this coupling: 1. Manage all the phy contexts in the mvm op mode, and only save the phy context id in the chanctx memory. 2. Reference count the phy contexts and free them only when they are not longer used (both by mac80211 and P2P Device). Signed-off-by: Ilan Peer Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 79 ++++++++++++++++++++++++----- drivers/net/wireless/iwlwifi/mvm/mvm.h | 9 ++-- drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c | 73 +++++++++++++------------- 3 files changed, 105 insertions(+), 56 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index e2cf6d92217b..c1ed5c36bf3a 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -127,6 +127,17 @@ static const struct wiphy_wowlan_tcp_support iwl_mvm_wowlan_tcp_support = { }; #endif +static void iwl_mvm_reset_phy_ctxts(struct iwl_mvm *mvm) +{ + int i; + + memset(mvm->phy_ctxts, 0, sizeof(mvm->phy_ctxts)); + for (i = 0; i < NUM_PHY_CTX; i++) { + mvm->phy_ctxts[i].id = i; + mvm->phy_ctxts[i].ref = 0; + } +} + int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) { struct ieee80211_hw *hw = mvm->hw; @@ -158,7 +169,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) hw->sta_data_size = sizeof(struct iwl_mvm_sta); hw->vif_data_size = sizeof(struct iwl_mvm_vif); - hw->chanctx_data_size = sizeof(struct iwl_mvm_phy_ctxt); + hw->chanctx_data_size = sizeof(u16); hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_P2P_CLIENT) | @@ -193,6 +204,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) hw->wiphy->n_addresses++; } + iwl_mvm_reset_phy_ctxts(mvm); + /* we create the 802.11 header and a max-length SSID element */ hw->wiphy->max_scan_ie_len = mvm->fw->ucode_capa.max_probe_length - 24 - 34; @@ -345,8 +358,7 @@ static void iwl_mvm_cleanup_iterator(void *data, u8 *mac, iwl_mvm_te_clear_data(mvm, &mvmvif->time_event_data); spin_unlock_bh(&mvm->time_event_lock); - if (vif->type != NL80211_IFTYPE_P2P_DEVICE) - mvmvif->phy_ctxt = NULL; + mvmvif->phy_ctxt = NULL; } static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) @@ -363,6 +375,9 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL, iwl_mvm_cleanup_iterator, mvm); + mvm->p2p_device_vif = NULL; + + iwl_mvm_reset_phy_ctxts(mvm); memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table)); memset(mvm->sta_drained, 0, sizeof(mvm->sta_drained)); @@ -456,6 +471,20 @@ static void iwl_mvm_power_update_iterator(void *data, u8 *mac, iwl_mvm_power_update_mode(mvm, vif); } +static struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm) +{ + u16 i; + + lockdep_assert_held(&mvm->mutex); + + for (i = 0; i < NUM_PHY_CTX; i++) + if (!mvm->phy_ctxts[i].ref) + return &mvm->phy_ctxts[i]; + + IWL_ERR(mvm, "No available PHY context\n"); + return NULL; +} + static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { @@ -550,7 +579,11 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_channel *chan; struct cfg80211_chan_def chandef; - mvmvif->phy_ctxt = &mvm->phy_ctxt_roc; + mvmvif->phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm); + if (!mvmvif->phy_ctxt) { + ret = -ENOSPC; + goto out_remove_mac; + } /* * The channel used here isn't relevant as it's @@ -583,7 +616,7 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, out_unbind: iwl_mvm_binding_remove_vif(mvm, vif); out_remove_phy: - iwl_mvm_phy_ctxt_remove(mvm, mvmvif->phy_ctxt); + iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt); out_remove_mac: mvmvif->phy_ctxt = NULL; iwl_mvm_mac_ctxt_remove(mvm, vif); @@ -677,7 +710,7 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw, mvm->p2p_device_vif = NULL; iwl_mvm_rm_bcast_sta(mvm, &mvmvif->bcast_sta); iwl_mvm_binding_remove_vif(mvm, vif); - iwl_mvm_phy_ctxt_remove(mvm, mvmvif->phy_ctxt); + iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt); mvmvif->phy_ctxt = NULL; } @@ -1172,6 +1205,7 @@ static int iwl_mvm_roc(struct ieee80211_hw *hw, enum ieee80211_roc_type type) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct cfg80211_chan_def chandef; int ret; @@ -1186,7 +1220,7 @@ static int iwl_mvm_roc(struct ieee80211_hw *hw, mutex_lock(&mvm->mutex); cfg80211_chandef_create(&chandef, channel, NL80211_CHAN_NO_HT); - ret = iwl_mvm_phy_ctxt_changed(mvm, &mvm->phy_ctxt_roc, + ret = iwl_mvm_phy_ctxt_changed(mvm, mvmvif->phy_ctxt, &chandef, 1, 1); /* Schedule the time events */ @@ -1216,15 +1250,29 @@ static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_phy_ctxt *phy_ctxt = (void *)ctx->drv_priv; + u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; + struct iwl_mvm_phy_ctxt *phy_ctxt; int ret; + IWL_DEBUG_MAC80211(mvm, "Add PHY context\n"); + mutex_lock(&mvm->mutex); + phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm); + if (!phy_ctxt) { + ret = -ENOSPC; + goto out; + } - IWL_DEBUG_MAC80211(mvm, "Add PHY context\n"); ret = iwl_mvm_phy_ctxt_add(mvm, phy_ctxt, &ctx->def, ctx->rx_chains_static, ctx->rx_chains_dynamic); + if (ret) { + IWL_ERR(mvm, "Failed to add PHY context\n"); + goto out; + } + + *phy_ctxt_id = phy_ctxt->id; +out: mutex_unlock(&mvm->mutex); return ret; } @@ -1233,10 +1281,11 @@ static void iwl_mvm_remove_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_phy_ctxt *phy_ctxt = (void *)ctx->drv_priv; + u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; + struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id]; mutex_lock(&mvm->mutex); - iwl_mvm_phy_ctxt_remove(mvm, phy_ctxt); + iwl_mvm_phy_ctxt_unref(mvm, phy_ctxt); mutex_unlock(&mvm->mutex); } @@ -1245,7 +1294,8 @@ static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw, u32 changed) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_phy_ctxt *phy_ctxt = (void *)ctx->drv_priv; + u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; + struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id]; mutex_lock(&mvm->mutex); iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->def, @@ -1259,13 +1309,14 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_phy_ctxt *phyctx = (void *)ctx->drv_priv; + u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; + struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id]; struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); int ret; mutex_lock(&mvm->mutex); - mvmvif->phy_ctxt = phyctx; + mvmvif->phy_ctxt = phy_ctxt; switch (vif->type) { case NL80211_IFTYPE_AP: diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index 4fc64d50dc50..3505fcafde66 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -109,6 +109,7 @@ extern struct iwl_mvm_mod_params iwlmvm_mod_params; struct iwl_mvm_phy_ctxt { u16 id; u16 color; + u32 ref; /* * TODO: This should probably be removed. Currently here only for rate @@ -318,7 +319,7 @@ struct iwl_mvm { bool prevent_power_down_d3; #endif - struct iwl_mvm_phy_ctxt phy_ctxt_roc; + struct iwl_mvm_phy_ctxt phy_ctxts[NUM_PHY_CTX]; struct list_head time_event_list; spinlock_t time_event_lock; @@ -448,8 +449,10 @@ int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, struct cfg80211_chan_def *chandef, u8 chains_static, u8 chains_dynamic); -void iwl_mvm_phy_ctxt_remove(struct iwl_mvm *mvm, - struct iwl_mvm_phy_ctxt *ctxt); +void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm, + struct iwl_mvm_phy_ctxt *ctxt); +void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, + struct iwl_mvm_phy_ctxt *ctxt); /* MAC (virtual interface) programming */ int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif); diff --git a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c index a28a1d1f23eb..f80e721eb5d0 100644 --- a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c @@ -195,21 +195,6 @@ static int iwl_mvm_phy_ctxt_apply(struct iwl_mvm *mvm, return ret; } - -struct phy_ctx_used_data { - unsigned long used[BITS_TO_LONGS(NUM_PHY_CTX)]; -}; - -static void iwl_mvm_phy_ctx_used_iter(struct ieee80211_hw *hw, - struct ieee80211_chanctx_conf *ctx, - void *_data) -{ - struct phy_ctx_used_data *data = _data; - struct iwl_mvm_phy_ctxt *phy_ctxt = (void *)ctx->drv_priv; - - __set_bit(phy_ctxt->id, data->used); -} - /* * Send a command to add a PHY context based on the current HW configuration. */ @@ -217,34 +202,31 @@ int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, struct cfg80211_chan_def *chandef, u8 chains_static, u8 chains_dynamic) { - struct phy_ctx_used_data data = { - .used = { }, - }; - - /* - * If this is a regular PHY context (not the ROC one) - * skip the ROC PHY context's ID. - */ - if (ctxt != &mvm->phy_ctxt_roc) - __set_bit(mvm->phy_ctxt_roc.id, data.used); + int ret; + WARN_ON(ctxt->ref); lockdep_assert_held(&mvm->mutex); - ctxt->color++; - if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { - ieee80211_iter_chan_contexts_atomic( - mvm->hw, iwl_mvm_phy_ctx_used_iter, &data); + ctxt->channel = chandef->chan; + ret = iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef, + chains_static, chains_dynamic, + FW_CTXT_ACTION_ADD, 0); - ctxt->id = find_first_zero_bit(data.used, NUM_PHY_CTX); - if (WARN_ONCE(ctxt->id == NUM_PHY_CTX, - "Failed to init PHY context - no free ID!\n")) - return -EIO; - } + if (!ret) + ctxt->ref = 1; + return ret; +} - ctxt->channel = chandef->chan; - return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef, - chains_static, chains_dynamic, - FW_CTXT_ACTION_ADD, 0); +/* + * Update the number of references to the given PHY context. This is valid only + * in case the PHY context was already created, i.e., its reference count > 0. + */ +void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt) +{ + WARN_ON(!ctxt->ref); + lockdep_assert_held(&mvm->mutex); + + ctxt->ref++; } /* @@ -269,7 +251,8 @@ int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, * Once the command is sent, regardless of success or failure, the context is * marked as invalid */ -void iwl_mvm_phy_ctxt_remove(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt) +static void iwl_mvm_phy_ctxt_remove(struct iwl_mvm *mvm, + struct iwl_mvm_phy_ctxt *ctxt) { struct iwl_phy_context_cmd cmd; int ret; @@ -280,7 +263,19 @@ void iwl_mvm_phy_ctxt_remove(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt) ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD, CMD_SYNC, sizeof(struct iwl_phy_context_cmd), &cmd); + ctxt->channel = NULL; if (ret) IWL_ERR(mvm, "Failed to send PHY remove: ctxt id=%d\n", ctxt->id); } + +void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt) +{ + lockdep_assert_held(&mvm->mutex); + + ctxt->ref--; + if (ctxt->ref != 0) + return; + + return iwl_mvm_phy_ctxt_remove(mvm, ctxt); +} -- cgit v1.2.3 From 53a9d61eb74e574aec2db86f63d9809b6116d164 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Sun, 28 Apr 2013 11:55:08 +0300 Subject: iwlwifi: mvm: Change PHY context handling 1. All the phy contexts are added immediately after the firmware is loaded and up. 2. Whenever a PHY context needs to be used, its reference counter is incremented and the PHY context is being configured to the appropriate configuration. 3. When a PHY context is no longer needed, its reference count is decremented. 4. PHY contexts are never removed. Signed-off-by: Ilan Peer Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/mvm/fw.c | 18 +++++++++++++++- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 28 ++++++++----------------- drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c | 32 ----------------------------- 3 files changed, 25 insertions(+), 53 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c index e18c92dd60ec..a4071cf09a24 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/iwlwifi/mvm/fw.c @@ -388,6 +388,8 @@ out: int iwl_mvm_up(struct iwl_mvm *mvm) { int ret, i; + struct ieee80211_channel *chan; + struct cfg80211_chan_def chandef; lockdep_assert_held(&mvm->mutex); @@ -443,8 +445,22 @@ int iwl_mvm_up(struct iwl_mvm *mvm) if (ret) goto error; - IWL_DEBUG_INFO(mvm, "RT uCode started.\n"); + /* Add all the PHY contexts */ + chan = &mvm->hw->wiphy->bands[IEEE80211_BAND_2GHZ]->channels[0]; + cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT); + for (i = 0; i < NUM_PHY_CTX; i++) { + /* + * The channel used here isn't relevant as it's + * going to be overwritten in the other flows. + * For now use the first channel we have. + */ + ret = iwl_mvm_phy_ctxt_add(mvm, &mvm->phy_ctxts[i], + &chandef, 1, 1); + if (ret) + goto error; + } + IWL_DEBUG_INFO(mvm, "RT uCode started.\n"); return 0; error: iwl_trans_stop_device(mvm->trans); diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index c1ed5c36bf3a..d9eabdc6e493 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -576,8 +576,6 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, * MAC context is bound to it at this stage. */ if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { - struct ieee80211_channel *chan; - struct cfg80211_chan_def chandef; mvmvif->phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm); if (!mvmvif->phy_ctxt) { @@ -585,21 +583,10 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, goto out_remove_mac; } - /* - * The channel used here isn't relevant as it's - * going to be overwritten as part of the ROC flow. - * For now use the first channel we have. - */ - chan = &mvm->hw->wiphy->bands[IEEE80211_BAND_2GHZ]->channels[0]; - cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT); - ret = iwl_mvm_phy_ctxt_add(mvm, mvmvif->phy_ctxt, - &chandef, 1, 1); - if (ret) - goto out_remove_mac; - + iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt); ret = iwl_mvm_binding_add_vif(mvm, vif); if (ret) - goto out_remove_phy; + goto out_unref_phy; ret = iwl_mvm_add_bcast_sta(mvm, vif, &mvmvif->bcast_sta); if (ret) @@ -615,7 +602,7 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, out_unbind: iwl_mvm_binding_remove_vif(mvm, vif); - out_remove_phy: + out_unref_phy: iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt); out_remove_mac: mvmvif->phy_ctxt = NULL; @@ -1254,7 +1241,7 @@ static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw, struct iwl_mvm_phy_ctxt *phy_ctxt; int ret; - IWL_DEBUG_MAC80211(mvm, "Add PHY context\n"); + IWL_DEBUG_MAC80211(mvm, "Add channel context\n"); mutex_lock(&mvm->mutex); phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm); @@ -1263,14 +1250,15 @@ static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw, goto out; } - ret = iwl_mvm_phy_ctxt_add(mvm, phy_ctxt, &ctx->def, - ctx->rx_chains_static, - ctx->rx_chains_dynamic); + ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->def, + ctx->rx_chains_static, + ctx->rx_chains_dynamic); if (ret) { IWL_ERR(mvm, "Failed to add PHY context\n"); goto out; } + iwl_mvm_phy_ctxt_ref(mvm, phy_ctxt); *phy_ctxt_id = phy_ctxt->id; out: mutex_unlock(&mvm->mutex); diff --git a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c index f80e721eb5d0..0ea61aaf7754 100644 --- a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c @@ -212,8 +212,6 @@ int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, chains_static, chains_dynamic, FW_CTXT_ACTION_ADD, 0); - if (!ret) - ctxt->ref = 1; return ret; } @@ -223,9 +221,7 @@ int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, */ void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt) { - WARN_ON(!ctxt->ref); lockdep_assert_held(&mvm->mutex); - ctxt->ref++; } @@ -246,36 +242,8 @@ int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, FW_CTXT_ACTION_MODIFY, 0); } -/* - * Send a command to the FW to remove the given phy context. - * Once the command is sent, regardless of success or failure, the context is - * marked as invalid - */ -static void iwl_mvm_phy_ctxt_remove(struct iwl_mvm *mvm, - struct iwl_mvm_phy_ctxt *ctxt) -{ - struct iwl_phy_context_cmd cmd; - int ret; - - lockdep_assert_held(&mvm->mutex); - - iwl_mvm_phy_ctxt_cmd_hdr(ctxt, &cmd, FW_CTXT_ACTION_REMOVE, 0); - ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD, CMD_SYNC, - sizeof(struct iwl_phy_context_cmd), - &cmd); - ctxt->channel = NULL; - if (ret) - IWL_ERR(mvm, "Failed to send PHY remove: ctxt id=%d\n", - ctxt->id); -} - void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt) { lockdep_assert_held(&mvm->mutex); - ctxt->ref--; - if (ctxt->ref != 0) - return; - - return iwl_mvm_phy_ctxt_remove(mvm, ctxt); } -- cgit v1.2.3 From b415210b3a36c414dec646104adf84d3b78d9f99 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 1 May 2013 09:42:06 +0300 Subject: iwlwifi: enable shadow registers for 7000 This will reduce CPU utilization. Instead of waking up the NIC for each Tx manually, the CPU can write to a register that will wake up the NIC automatically. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/iwl-7000.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-7000.c b/drivers/net/wireless/iwlwifi/iwl-7000.c index 50263e87fe15..26969f5f504a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-7000.c +++ b/drivers/net/wireless/iwlwifi/iwl-7000.c @@ -102,7 +102,7 @@ static const struct iwl_base_params iwl7000_base_params = { .chain_noise_scale = 1000, .wd_timeout = IWL_LONG_WD_TIMEOUT, .max_event_log_size = 512, - .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ + .shadow_reg_enable = true, }; static const struct iwl_ht_params iwl7000_ht_params = { -- cgit v1.2.3 From ce7f9ab381ea1cc4514f462d18a15785de050323 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 3 May 2013 11:30:23 +0200 Subject: iwlwifi: mvm: don't assume data section is at 0x800000 In theory, the firmware format allows changing the data section offset. This may not be used today, but there's no reason for the driver to assume the data section is always at 0x800000 as it can know better. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/mvm/debugfs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c index 2053dccefcd6..a12c98be8616 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c @@ -147,10 +147,11 @@ static ssize_t iwl_dbgfs_sram_read(struct file *file, char __user *user_buf, /* default is to dump the entire data segment */ if (!mvm->dbgfs_sram_offset && !mvm->dbgfs_sram_len) { - mvm->dbgfs_sram_offset = 0x800000; if (!mvm->ucode_loaded) return -EINVAL; img = &mvm->fw->img[mvm->cur_ucode]; + mvm->dbgfs_sram_offset = + img->sec[IWL_UCODE_SECTION_DATA].offset; mvm->dbgfs_sram_len = img->sec[IWL_UCODE_SECTION_DATA].len; } len = mvm->dbgfs_sram_len; -- cgit v1.2.3 From b656fa337141136317589cff9f25832a00bc651c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 3 May 2013 11:56:17 +0200 Subject: iwlwifi: pcie: dump stack on NIC error in sync commands Many times, a NIC error is the result of a bad command sent to the device. If the command was sent synchronously, then we'll currently print a message when the command is aborted containing the command. It can be very useful to also see the stack dump though, so also print that. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/pcie/tx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index 03ad2f417298..595df1756b84 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -1522,6 +1522,7 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans, if (test_bit(STATUS_FW_ERROR, &trans_pcie->status)) { IWL_ERR(trans, "FW error in SYNC CMD %s\n", get_cmd_string(trans_pcie, cmd->id)); + dump_stack(); ret = -EIO; goto cancel; } -- cgit v1.2.3 From bfc824b05bdc4ea0b7702263f91dc3fed7b630eb Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 May 2013 16:06:51 +0200 Subject: iwlwifi: nvm: honour VHT enable flag Some devices don't support VHT (802.11ac) and this is encoded in the data stored in the NVM. Read the flag and use it to set up the VHT capabilities accordingly. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/iwl-nvm-parse.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c index 6199a0a597a6..8f2a4e922346 100644 --- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c @@ -89,6 +89,7 @@ enum nvm_sku_bits { NVM_SKU_CAP_BAND_24GHZ = BIT(0), NVM_SKU_CAP_BAND_52GHZ = BIT(1), NVM_SKU_CAP_11N_ENABLE = BIT(2), + NVM_SKU_CAP_11AC_ENABLE = BIT(3), }; /* radio config bits (actual values from NVM definition) */ @@ -258,8 +259,6 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg, struct iwl_nvm_data *data, struct ieee80211_sta_vht_cap *vht_cap) { - /* For now, assume new devices with NVM are VHT capable */ - vht_cap->vht_supported = true; vht_cap->cap = IEEE80211_VHT_CAP_SHORT_GI_80 | @@ -292,7 +291,8 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg, } static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, - struct iwl_nvm_data *data, const __le16 *nvm_sw) + struct iwl_nvm_data *data, const __le16 *nvm_sw, + bool enable_vht) { int n_channels = iwl_init_channel_map(dev, cfg, data, &nvm_sw[NVM_CHANNELS]); @@ -314,7 +314,8 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, n_used += iwl_init_sband_channels(data, sband, n_channels, IEEE80211_BAND_5GHZ); iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ); - iwl_init_vht_hw_capab(cfg, data, &sband->vht_cap); + if (enable_vht) + iwl_init_vht_hw_capab(cfg, data, &sband->vht_cap); if (n_channels != n_used) IWL_ERR_DEV(dev, "NVM: used only %d of %d channels\n", @@ -380,7 +381,8 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, data->hw_addr[4] = hw_addr[5]; data->hw_addr[5] = hw_addr[4]; - iwl_init_sbands(dev, cfg, data, nvm_sw); + iwl_init_sbands(dev, cfg, data, nvm_sw, + sku & NVM_SKU_CAP_11AC_ENABLE); data->calib_version = 255; /* TODO: this value will prevent some checks from -- cgit v1.2.3 From 31d385aeea7e26e70409cd6f126c516047bc9f96 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Tue, 2 Apr 2013 10:25:46 +0300 Subject: iwlwifi: mvm: Allow P2P Device to use an existing PHY context The patch is an optimization, that eliminates unnecessary binding context switching and allows the P2P Device MAC to use the same channel as an existing netdev interface. For each ROC call, check if there is already a channel/phy context that can be used for the P2P Device. If such channel is found, unbind the P2P Device from its current phy context, and bind it to the already used channel/phy context. In case that the phy context is shared, and there is a need to change it, create a new phy context, unbind and create a new binding. Signed-off-by: Ilan Peer Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 99 +++++++++++++++++++++++++++-- 1 file changed, 92 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index d9eabdc6e493..fe22772f3421 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -1194,28 +1194,105 @@ static int iwl_mvm_roc(struct ieee80211_hw *hw, struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct cfg80211_chan_def chandef; - int ret; + struct iwl_mvm_phy_ctxt *phy_ctxt; + int ret, i; + + IWL_DEBUG_MAC80211(mvm, "enter (%d, %d, %d)\n", channel->hw_value, + duration, type); if (vif->type != NL80211_IFTYPE_P2P_DEVICE) { IWL_ERR(mvm, "vif isn't a P2P_DEVICE: %d\n", vif->type); return -EINVAL; } - IWL_DEBUG_MAC80211(mvm, "enter (%d, %d, %d)\n", channel->hw_value, - duration, type); - mutex_lock(&mvm->mutex); + for (i = 0; i < NUM_PHY_CTX; i++) { + phy_ctxt = &mvm->phy_ctxts[i]; + if (phy_ctxt->ref == 0 || mvmvif->phy_ctxt == phy_ctxt) + continue; + + if (phy_ctxt->ref && channel == phy_ctxt->channel) { + /* + * Unbind the P2P_DEVICE from the current PHY context, + * and if the PHY context is not used remove it. + */ + ret = iwl_mvm_binding_remove_vif(mvm, vif); + if (WARN(ret, "Failed unbinding P2P_DEVICE\n")) + goto out_unlock; + + iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt); + + /* Bind the P2P_DEVICE to the current PHY Context */ + mvmvif->phy_ctxt = phy_ctxt; + + ret = iwl_mvm_binding_add_vif(mvm, vif); + if (WARN(ret, "Failed binding P2P_DEVICE\n")) + goto out_unlock; + + iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt); + goto schedule_time_event; + } + } + + /* Need to update the PHY context only if the ROC channel changed */ + if (channel == mvmvif->phy_ctxt->channel) + goto schedule_time_event; + cfg80211_chandef_create(&chandef, channel, NL80211_CHAN_NO_HT); - ret = iwl_mvm_phy_ctxt_changed(mvm, mvmvif->phy_ctxt, - &chandef, 1, 1); + /* + * Change the PHY context configuration as it is currently referenced + * only by the P2P Device MAC + */ + if (mvmvif->phy_ctxt->ref == 1) { + ret = iwl_mvm_phy_ctxt_changed(mvm, mvmvif->phy_ctxt, + &chandef, 1, 1); + if (ret) + goto out_unlock; + } else { + /* + * The PHY context is shared with other MACs. Need to remove the + * P2P Device from the binding, allocate an new PHY context and + * create a new binding + */ + phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm); + if (!phy_ctxt) { + ret = -ENOSPC; + goto out_unlock; + } + + ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &chandef, + 1, 1); + if (ret) { + IWL_ERR(mvm, "Failed to change PHY context\n"); + goto out_unlock; + } + + /* Unbind the P2P_DEVICE from the current PHY context */ + ret = iwl_mvm_binding_remove_vif(mvm, vif); + if (WARN(ret, "Failed unbinding P2P_DEVICE\n")) + goto out_unlock; + + iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt); + + /* Bind the P2P_DEVICE to the new allocated PHY context */ + mvmvif->phy_ctxt = phy_ctxt; + + ret = iwl_mvm_binding_add_vif(mvm, vif); + if (WARN(ret, "Failed binding P2P_DEVICE\n")) + goto out_unlock; + + iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt); + } + +schedule_time_event: /* Schedule the time events */ ret = iwl_mvm_start_p2p_roc(mvm, vif, duration, type); +out_unlock: mutex_unlock(&mvm->mutex); IWL_DEBUG_MAC80211(mvm, "leave\n"); - return ret; } @@ -1285,6 +1362,14 @@ static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw, u16 *phy_ctxt_id = (u16 *)ctx->drv_priv; struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id]; + if (WARN_ONCE((phy_ctxt->ref > 1) && + (changed & ~(IEEE80211_CHANCTX_CHANGE_WIDTH | + IEEE80211_CHANCTX_CHANGE_RX_CHAINS | + IEEE80211_CHANCTX_CHANGE_RADAR)), + "Cannot change PHY. Ref=%d, changed=0x%X\n", + phy_ctxt->ref, changed)) + return; + mutex_lock(&mvm->mutex); iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->def, ctx->rx_chains_static, -- cgit v1.2.3 From 93fc64114b994f9ef6901697f9b0de00762680e9 Mon Sep 17 00:00:00 2001 From: Oren Givon Date: Tue, 23 Apr 2013 18:19:11 +0300 Subject: iwlwifi: add new 7260 and 3160 series device IDs Add new device IDs and configurations to support all the devices. Signed-off-by: Oren Givon Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/iwl-7000.c | 42 ++++++++++++++++++++++++-- drivers/net/wireless/iwlwifi/iwl-config.h | 6 +++- drivers/net/wireless/iwlwifi/pcie/drv.c | 50 +++++++++++++++++++++++++++++-- 3 files changed, 91 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-7000.c b/drivers/net/wireless/iwlwifi/iwl-7000.c index 26969f5f504a..c9aae2a2bbbb 100644 --- a/drivers/net/wireless/iwlwifi/iwl-7000.c +++ b/drivers/net/wireless/iwlwifi/iwl-7000.c @@ -125,7 +125,7 @@ static const struct iwl_ht_params iwl7000_ht_params = { const struct iwl_cfg iwl7260_2ac_cfg = { - .name = "Intel(R) Dual Band Wireless AC7260", + .name = "Intel(R) Dual Band Wireless AC 7260", .fw_name_pre = IWL7260_FW_PRE, IWL_DEVICE_7000, .ht_params = &iwl7000_ht_params, @@ -133,8 +133,44 @@ const struct iwl_cfg iwl7260_2ac_cfg = { .nvm_calib_ver = IWL7260_TX_POWER_VERSION, }; -const struct iwl_cfg iwl3160_ac_cfg = { - .name = "Intel(R) Dual Band Wireless AC3160", +const struct iwl_cfg iwl7260_2n_cfg = { + .name = "Intel(R) Dual Band Wireless N 7260", + .fw_name_pre = IWL7260_FW_PRE, + IWL_DEVICE_7000, + .ht_params = &iwl7000_ht_params, + .nvm_ver = IWL7260_NVM_VERSION, + .nvm_calib_ver = IWL7260_TX_POWER_VERSION, +}; + +const struct iwl_cfg iwl7260_n_cfg = { + .name = "Intel(R) Wireless N 7260", + .fw_name_pre = IWL7260_FW_PRE, + IWL_DEVICE_7000, + .ht_params = &iwl7000_ht_params, + .nvm_ver = IWL7260_NVM_VERSION, + .nvm_calib_ver = IWL7260_TX_POWER_VERSION, +}; + +const struct iwl_cfg iwl3160_2ac_cfg = { + .name = "Intel(R) Dual Band Wireless AC 3160", + .fw_name_pre = IWL3160_FW_PRE, + IWL_DEVICE_7000, + .ht_params = &iwl7000_ht_params, + .nvm_ver = IWL3160_NVM_VERSION, + .nvm_calib_ver = IWL3160_TX_POWER_VERSION, +}; + +const struct iwl_cfg iwl3160_2n_cfg = { + .name = "Intel(R) Dual Band Wireless N 3160", + .fw_name_pre = IWL3160_FW_PRE, + IWL_DEVICE_7000, + .ht_params = &iwl7000_ht_params, + .nvm_ver = IWL3160_NVM_VERSION, + .nvm_calib_ver = IWL3160_TX_POWER_VERSION, +}; + +const struct iwl_cfg iwl3160_n_cfg = { + .name = "Intel(R) Wireless N 3160", .fw_name_pre = IWL3160_FW_PRE, IWL_DEVICE_7000, .ht_params = &iwl7000_ht_params, diff --git a/drivers/net/wireless/iwlwifi/iwl-config.h b/drivers/net/wireless/iwlwifi/iwl-config.h index c38aa8f77554..c3c9268affab 100644 --- a/drivers/net/wireless/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/iwlwifi/iwl-config.h @@ -320,6 +320,10 @@ extern const struct iwl_cfg iwl105_bgn_cfg; extern const struct iwl_cfg iwl105_bgn_d_cfg; extern const struct iwl_cfg iwl135_bgn_cfg; extern const struct iwl_cfg iwl7260_2ac_cfg; -extern const struct iwl_cfg iwl3160_ac_cfg; +extern const struct iwl_cfg iwl7260_2n_cfg; +extern const struct iwl_cfg iwl7260_n_cfg; +extern const struct iwl_cfg iwl3160_2ac_cfg; +extern const struct iwl_cfg iwl3160_2n_cfg; +extern const struct iwl_cfg iwl3160_n_cfg; #endif /* __IWL_CONFIG_H__ */ diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwlwifi/pcie/drv.c index 8cb53ec2b77b..db7bdd35a9c5 100644 --- a/drivers/net/wireless/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/iwlwifi/pcie/drv.c @@ -256,10 +256,54 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = { /* 7000 Series */ {IWL_PCI_DEVICE(0x08B1, 0x4070, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4062, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4170, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4060, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4160, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4062, iwl7260_n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4162, iwl7260_n_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0x4270, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0x4260, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0x4262, iwl7260_n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4470, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4460, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4462, iwl7260_n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4870, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x486E, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4A70, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4A6E, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4A6C, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4020, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0x4220, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4420, iwl7260_2n_cfg)}, {IWL_PCI_DEVICE(0x08B1, 0xC070, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x0070, iwl3160_ac_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x8070, iwl3160_ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC170, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC060, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC160, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC062, iwl7260_n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC162, iwl7260_n_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0xC270, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0xC260, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0xC262, iwl7260_n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC470, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC460, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC462, iwl7260_n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC020, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0xC220, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC420, iwl7260_2n_cfg)}, + +/* 3160 Series */ + {IWL_PCI_DEVICE(0x08B3, 0x0070, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x0170, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x0060, iwl3160_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x0062, iwl3160_n_cfg)}, + {IWL_PCI_DEVICE(0x08B4, 0x0270, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x0470, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x8070, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x8170, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x8060, iwl3160_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x8062, iwl3160_n_cfg)}, + {IWL_PCI_DEVICE(0x08B4, 0x8270, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x8470, iwl3160_2ac_cfg)}, {0} }; -- cgit v1.2.3 From 986dfbcf8b493928188f0e634068993bf2067ad7 Mon Sep 17 00:00:00 2001 From: Ruchika Gupta Date: Fri, 26 Apr 2013 15:44:54 +0530 Subject: crypto: caam - FIX RNG init for RNG greater than equal to 4 For SEC including a RNG block version >= 4, special initialization must occur before any descriptor that uses RNG block can be submitted. This initialization is required not only for SEC with version greater than 5.0, but for SEC with RNG version >=4. There may be a case where RNG has already been instantiated by u-boot or boot ROM code.In such SoCs, if RNG is initialized again SEC would returns "Instantiation error". Hence, the initialization status of RNG4 should be also checked before doing RNG init. Signed-off-by: Ruchika Gupta Signed-off-by: Alex Porosanu Signed-off-by: Andy Fleming Reviewed-by: Vakul Garg Signed-off-by: Herbert Xu --- drivers/crypto/caam/ctrl.c | 10 +++++++--- drivers/crypto/caam/regs.h | 42 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/drivers/crypto/caam/ctrl.c b/drivers/crypto/caam/ctrl.c index 6e94bcd94678..f5d6deced1cb 100644 --- a/drivers/crypto/caam/ctrl.c +++ b/drivers/crypto/caam/ctrl.c @@ -202,6 +202,7 @@ static int caam_probe(struct platform_device *pdev) #ifdef CONFIG_DEBUG_FS struct caam_perfmon *perfmon; #endif + u64 cha_vid; ctrlpriv = kzalloc(sizeof(struct caam_drv_private), GFP_KERNEL); if (!ctrlpriv) @@ -293,11 +294,14 @@ static int caam_probe(struct platform_device *pdev) return -ENOMEM; } + cha_vid = rd_reg64(&topregs->ctrl.perfmon.cha_id); + /* - * RNG4 based SECs (v5+) need special initialization prior - * to executing any descriptors + * If SEC has RNG version >= 4 and RNG state handle has not been + * already instantiated ,do RNG instantiation */ - if (of_device_is_compatible(nprop, "fsl,sec-v5.0")) { + if ((cha_vid & CHA_ID_RNG_MASK) >> CHA_ID_RNG_SHIFT >= 4 && + !(rd_reg32(&topregs->ctrl.r4tst[0].rdsta) & RDSTA_IF0)) { kick_trng(pdev); ret = instantiate_rng(ctrlpriv->jrdev[0]); if (ret) { diff --git a/drivers/crypto/caam/regs.h b/drivers/crypto/caam/regs.h index cd6fedad9935..c09142fc13e3 100644 --- a/drivers/crypto/caam/regs.h +++ b/drivers/crypto/caam/regs.h @@ -117,6 +117,43 @@ struct jr_outentry { #define CHA_NUM_DECONUM_SHIFT 56 #define CHA_NUM_DECONUM_MASK (0xfull << CHA_NUM_DECONUM_SHIFT) +/* CHA Version IDs */ +#define CHA_ID_AES_SHIFT 0 +#define CHA_ID_AES_MASK (0xfull << CHA_ID_AES_SHIFT) + +#define CHA_ID_DES_SHIFT 4 +#define CHA_ID_DES_MASK (0xfull << CHA_ID_DES_SHIFT) + +#define CHA_ID_ARC4_SHIFT 8 +#define CHA_ID_ARC4_MASK (0xfull << CHA_ID_ARC4_SHIFT) + +#define CHA_ID_MD_SHIFT 12 +#define CHA_ID_MD_MASK (0xfull << CHA_ID_MD_SHIFT) + +#define CHA_ID_RNG_SHIFT 16 +#define CHA_ID_RNG_MASK (0xfull << CHA_ID_RNG_SHIFT) + +#define CHA_ID_SNW8_SHIFT 20 +#define CHA_ID_SNW8_MASK (0xfull << CHA_ID_SNW8_SHIFT) + +#define CHA_ID_KAS_SHIFT 24 +#define CHA_ID_KAS_MASK (0xfull << CHA_ID_KAS_SHIFT) + +#define CHA_ID_PK_SHIFT 28 +#define CHA_ID_PK_MASK (0xfull << CHA_ID_PK_SHIFT) + +#define CHA_ID_CRC_SHIFT 32 +#define CHA_ID_CRC_MASK (0xfull << CHA_ID_CRC_SHIFT) + +#define CHA_ID_SNW9_SHIFT 36 +#define CHA_ID_SNW9_MASK (0xfull << CHA_ID_SNW9_SHIFT) + +#define CHA_ID_DECO_SHIFT 56 +#define CHA_ID_DECO_MASK (0xfull << CHA_ID_DECO_SHIFT) + +#define CHA_ID_JR_SHIFT 60 +#define CHA_ID_JR_MASK (0xfull << CHA_ID_JR_SHIFT) + struct sec_vid { u16 ip_id; u8 maj_rev; @@ -228,7 +265,10 @@ struct rng4tst { u32 rtfrqmax; /* PRGM=1: freq. count max. limit register */ u32 rtfrqcnt; /* PRGM=0: freq. count register */ }; - u32 rsvd1[56]; + u32 rsvd1[40]; +#define RDSTA_IF0 0x00000001 + u32 rdsta; + u32 rsvd2[15]; }; /* -- cgit v1.2.3 From 110147c8c5136e1768a382da8896cf7f8b518982 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 13 May 2013 13:26:12 -0600 Subject: ASoC: tegra: always use clk_get() in utility code Now that all of the Tegra device trees have been updated to represent the required audio clocks, remove the compatibility code from the Tegra ASoC utility code, and always use clk_get() rather than clk_get_sys(). Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- sound/soc/tegra/tegra_asoc_utils.c | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/sound/soc/tegra/tegra_asoc_utils.c b/sound/soc/tegra/tegra_asoc_utils.c index 24fb001be7f4..d173880f290d 100644 --- a/sound/soc/tegra/tegra_asoc_utils.c +++ b/sound/soc/tegra/tegra_asoc_utils.c @@ -173,7 +173,6 @@ int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data, struct device *dev) { int ret; - bool new_clocks = false; data->dev = dev; @@ -181,40 +180,28 @@ int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data, data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA20; else if (of_machine_is_compatible("nvidia,tegra30")) data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA30; - else if (of_machine_is_compatible("nvidia,tegra114")) { + else if (of_machine_is_compatible("nvidia,tegra114")) data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA114; - new_clocks = true; - } else { + else { dev_err(data->dev, "SoC unknown to Tegra ASoC utils\n"); return -EINVAL; } - if (new_clocks) - data->clk_pll_a = clk_get(dev, "pll_a"); - else - data->clk_pll_a = clk_get_sys(NULL, "pll_a"); + data->clk_pll_a = clk_get(dev, "pll_a"); if (IS_ERR(data->clk_pll_a)) { dev_err(data->dev, "Can't retrieve clk pll_a\n"); ret = PTR_ERR(data->clk_pll_a); goto err; } - if (new_clocks) - data->clk_pll_a_out0 = clk_get(dev, "pll_a_out0"); - else - data->clk_pll_a_out0 = clk_get_sys(NULL, "pll_a_out0"); + data->clk_pll_a_out0 = clk_get(dev, "pll_a_out0"); if (IS_ERR(data->clk_pll_a_out0)) { dev_err(data->dev, "Can't retrieve clk pll_a_out0\n"); ret = PTR_ERR(data->clk_pll_a_out0); goto err_put_pll_a; } - if (new_clocks) - data->clk_cdev1 = clk_get(dev, "mclk"); - else if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA20) - data->clk_cdev1 = clk_get_sys(NULL, "cdev1"); - else - data->clk_cdev1 = clk_get_sys("extern1", NULL); + data->clk_cdev1 = clk_get(dev, "mclk"); if (IS_ERR(data->clk_cdev1)) { dev_err(data->dev, "Can't retrieve clk cdev1\n"); ret = PTR_ERR(data->clk_cdev1); -- cgit v1.2.3 From afc66bb734d44ea64cc78ae8d8034886e8cfb2c7 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 3 May 2013 11:44:16 +0200 Subject: iwlwifi: mvm: optionally store D3 SRAM after resume The D3 image SRAM is overwritten by the runtime image, so it can't be accessed after resume. However, it can be very useful to look at it to know what happened during D3, so add the ability to store the image and make it available in debugfs. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/mvm/d3.c | 23 ++++++++++ drivers/net/wireless/iwlwifi/mvm/debugfs.c | 70 ++++++++++++++++++++++++++++++ drivers/net/wireless/iwlwifi/mvm/mvm.h | 4 ++ drivers/net/wireless/iwlwifi/mvm/ops.c | 4 ++ 4 files changed, 101 insertions(+) diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c index 16bbdcc8627a..63d788aef3f4 100644 --- a/drivers/net/wireless/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/iwlwifi/mvm/d3.c @@ -1214,6 +1214,26 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, iwl_free_resp(&cmd); } +static void iwl_mvm_read_d3_sram(struct iwl_mvm *mvm) +{ +#ifdef CONFIG_IWLWIFI_DEBUGFS + const struct fw_img *img = &mvm->fw->img[IWL_UCODE_WOWLAN]; + u32 len = img->sec[IWL_UCODE_SECTION_DATA].len; + u32 offs = img->sec[IWL_UCODE_SECTION_DATA].offset; + + if (!mvm->store_d3_resume_sram) + return; + + if (!mvm->d3_resume_sram) { + mvm->d3_resume_sram = kzalloc(len, GFP_KERNEL); + if (!mvm->d3_resume_sram) + return; + } + + iwl_trans_read_mem_bytes(mvm->trans, offs, mvm->d3_resume_sram, len); +#endif +} + int iwl_mvm_resume(struct ieee80211_hw *hw) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); @@ -1245,6 +1265,9 @@ int iwl_mvm_resume(struct ieee80211_hw *hw) goto out_unlock; } + /* query SRAM first in case we want event logging */ + iwl_mvm_read_d3_sram(mvm); + iwl_mvm_query_wakeup_reasons(mvm, vif); out_unlock: diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c index a12c98be8616..0765827d87a6 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c @@ -482,6 +482,70 @@ static ssize_t iwl_dbgfs_fw_restart_write(struct file *file, return count; } +#ifdef CONFIG_PM_SLEEP +static ssize_t iwl_dbgfs_d3_sram_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + char buf[8] = {}; + int store; + + if (copy_from_user(buf, user_buf, sizeof(buf))) + return -EFAULT; + + if (sscanf(buf, "%d", &store) != 1) + return -EINVAL; + + mvm->store_d3_resume_sram = store; + + return count; +} + +static ssize_t iwl_dbgfs_d3_sram_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + const struct fw_img *img; + int ofs, len, pos = 0; + size_t bufsz, ret; + char *buf; + u8 *ptr = mvm->d3_resume_sram; + + img = &mvm->fw->img[IWL_UCODE_WOWLAN]; + len = img->sec[IWL_UCODE_SECTION_DATA].len; + + bufsz = len * 4 + 256; + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + pos += scnprintf(buf, bufsz, "D3 SRAM capture: %sabled\n", + mvm->store_d3_resume_sram ? "en" : "dis"); + + if (ptr) { + for (ofs = 0; ofs < len; ofs += 16) { + pos += scnprintf(buf + pos, bufsz - pos, + "0x%.4x ", ofs); + hex_dump_to_buffer(ptr + ofs, 16, 16, 1, buf + pos, + bufsz - pos, false); + pos += strlen(buf + pos); + if (bufsz - pos > 0) + buf[pos++] = '\n'; + } + } else { + pos += scnprintf(buf + pos, bufsz - pos, + "(no data captured)\n"); + } + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + + kfree(buf); + + return ret; +} +#endif + #define MVM_DEBUGFS_READ_FILE_OPS(name) \ static const struct file_operations iwl_dbgfs_##name##_ops = { \ .read = iwl_dbgfs_##name##_read, \ @@ -525,6 +589,9 @@ MVM_DEBUGFS_READ_FILE_OPS(bt_notif); MVM_DEBUGFS_WRITE_FILE_OPS(power_down_allow); MVM_DEBUGFS_WRITE_FILE_OPS(power_down_d3_allow); MVM_DEBUGFS_WRITE_FILE_OPS(fw_restart); +#ifdef CONFIG_PM_SLEEP +MVM_DEBUGFS_READ_WRITE_FILE_OPS(d3_sram); +#endif /* Interface specific debugfs entries */ MVM_DEBUGFS_READ_FILE_OPS(mac_params); @@ -543,6 +610,9 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) MVM_DEBUGFS_ADD_FILE(power_down_allow, mvm->debugfs_dir, S_IWUSR); MVM_DEBUGFS_ADD_FILE(power_down_d3_allow, mvm->debugfs_dir, S_IWUSR); MVM_DEBUGFS_ADD_FILE(fw_restart, mvm->debugfs_dir, S_IWUSR); +#ifdef CONFIG_PM_SLEEP + MVM_DEBUGFS_ADD_FILE(d3_sram, mvm->debugfs_dir, S_IRUSR | S_IWUSR); +#endif /* * Create a symlink with mac80211. It will be removed when mac80211 diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index 3505fcafde66..712c39ae748f 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -344,6 +344,10 @@ struct iwl_mvm { #ifdef CONFIG_PM_SLEEP int gtk_ivlen, gtk_icvlen, ptk_ivlen, ptk_icvlen; +#ifdef CONFIG_IWLWIFI_DEBUGFS + bool store_d3_resume_sram; + void *d3_resume_sram; +#endif #endif /* BT-Coex */ diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 5bf1eaf9046a..bb79a8dcf6bb 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -443,6 +443,10 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode) kfree(mvm->scan_cmd); +#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_IWLWIFI_DEBUGFS) + kfree(mvm->d3_resume_sram); +#endif + iwl_trans_stop_hw(mvm->trans, true); iwl_phy_db_free(mvm->phy_db); -- cgit v1.2.3 From e28210115b0ff3544b39ee67d8e48dc74b25b6b8 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 8 May 2013 15:55:31 +0200 Subject: iwlwifi: mvm: sanity check context in iwl_mvm_phy_ctxt_unref() In some botched (!) restart scenarios we seem to get here with a NULL PHY context, so warn and exit instead of crashing. Reported-by: Omer Kremer Reviewed-by: Ilan Peer Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c index 0ea61aaf7754..1b4db25d53fb 100644 --- a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c @@ -245,5 +245,9 @@ int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt) { lockdep_assert_held(&mvm->mutex); + + if (WARN_ON_ONCE(!ctxt)) + return; + ctxt->ref--; } -- cgit v1.2.3 From f1ed0450a5fac7067590317cbf027f566b6ccbca Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Sun, 28 Apr 2013 14:00:41 +0200 Subject: KVM: x86: Remove support for reporting coalesced APIC IRQs Since the arrival of posted interrupt support we can no longer guarantee that coalesced IRQs are always reported to the IRQ source. Moreover, accumulated APIC timer events could cause a busy loop when a VCPU should rather be halted. The consensus is to remove coalesced tracking from the LAPIC. Signed-off-by: Jan Kiszka Acked-by: Marcelo Tosatti Signed-off-by: Gleb Natapov --- arch/x86/kvm/lapic.c | 57 +++++++++++++++++++++------------------------------- arch/x86/kvm/lapic.h | 6 +++--- virt/kvm/irq_comm.c | 9 ++++++--- 3 files changed, 32 insertions(+), 40 deletions(-) diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index e1adbb4aca75..9d751931cf84 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -405,17 +405,17 @@ int kvm_lapic_find_highest_irr(struct kvm_vcpu *vcpu) return highest_irr; } -static int __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode, - int vector, int level, int trig_mode, - unsigned long *dest_map); +static void __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode, + int vector, int level, int trig_mode, + unsigned long *dest_map); -int kvm_apic_set_irq(struct kvm_vcpu *vcpu, struct kvm_lapic_irq *irq, - unsigned long *dest_map) +void kvm_apic_set_irq(struct kvm_vcpu *vcpu, struct kvm_lapic_irq *irq, + unsigned long *dest_map) { struct kvm_lapic *apic = vcpu->arch.apic; - return __apic_accept_irq(apic, irq->delivery_mode, irq->vector, - irq->level, irq->trig_mode, dest_map); + __apic_accept_irq(apic, irq->delivery_mode, irq->vector, + irq->level, irq->trig_mode, dest_map); } static int pv_eoi_put_user(struct kvm_vcpu *vcpu, u8 val) @@ -608,7 +608,8 @@ bool kvm_irq_delivery_to_apic_fast(struct kvm *kvm, struct kvm_lapic *src, *r = -1; if (irq->shorthand == APIC_DEST_SELF) { - *r = kvm_apic_set_irq(src->vcpu, irq, dest_map); + kvm_apic_set_irq(src->vcpu, irq, dest_map); + *r = 1; return true; } @@ -653,7 +654,8 @@ bool kvm_irq_delivery_to_apic_fast(struct kvm *kvm, struct kvm_lapic *src, continue; if (*r < 0) *r = 0; - *r += kvm_apic_set_irq(dst[i]->vcpu, irq, dest_map); + kvm_apic_set_irq(dst[i]->vcpu, irq, dest_map); + *r += 1; } ret = true; @@ -662,15 +664,11 @@ out: return ret; } -/* - * Add a pending IRQ into lapic. - * Return 1 if successfully added and 0 if discarded. - */ -static int __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode, - int vector, int level, int trig_mode, - unsigned long *dest_map) +/* Set an IRQ pending in the lapic. */ +static void __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode, + int vector, int level, int trig_mode, + unsigned long *dest_map) { - int result = 0; struct kvm_vcpu *vcpu = apic->vcpu; switch (delivery_mode) { @@ -684,13 +682,10 @@ static int __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode, if (dest_map) __set_bit(vcpu->vcpu_id, dest_map); - if (kvm_x86_ops->deliver_posted_interrupt) { - result = 1; + if (kvm_x86_ops->deliver_posted_interrupt) kvm_x86_ops->deliver_posted_interrupt(vcpu, vector); - } else { - result = !apic_test_and_set_irr(vector, apic); - - if (!result) { + else { + if (apic_test_and_set_irr(vector, apic)) { if (trig_mode) apic_debug("level trig mode repeatedly " "for vector %d", vector); @@ -702,7 +697,7 @@ static int __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode, } out: trace_kvm_apic_accept_irq(vcpu->vcpu_id, delivery_mode, - trig_mode, vector, !result); + trig_mode, vector, false); break; case APIC_DM_REMRD: @@ -714,14 +709,12 @@ out: break; case APIC_DM_NMI: - result = 1; kvm_inject_nmi(vcpu); kvm_vcpu_kick(vcpu); break; case APIC_DM_INIT: if (!trig_mode || level) { - result = 1; /* assumes that there are only KVM_APIC_INIT/SIPI */ apic->pending_events = (1UL << KVM_APIC_INIT); /* make sure pending_events is visible before sending @@ -738,7 +731,6 @@ out: case APIC_DM_STARTUP: apic_debug("SIPI to vcpu %d vector 0x%02x\n", vcpu->vcpu_id, vector); - result = 1; apic->sipi_vector = vector; /* make sure sipi_vector is visible for the receiver */ smp_wmb(); @@ -760,7 +752,6 @@ out: delivery_mode); break; } - return result; } int kvm_apic_compare_prio(struct kvm_vcpu *vcpu1, struct kvm_vcpu *vcpu2) @@ -1470,7 +1461,7 @@ int apic_has_pending_timer(struct kvm_vcpu *vcpu) return 0; } -int kvm_apic_local_deliver(struct kvm_lapic *apic, int lvt_type) +void kvm_apic_local_deliver(struct kvm_lapic *apic, int lvt_type) { u32 reg = kvm_apic_get_reg(apic, lvt_type); int vector, mode, trig_mode; @@ -1479,10 +1470,8 @@ int kvm_apic_local_deliver(struct kvm_lapic *apic, int lvt_type) vector = reg & APIC_VECTOR_MASK; mode = reg & APIC_MODE_MASK; trig_mode = reg & APIC_LVT_LEVEL_TRIGGER; - return __apic_accept_irq(apic, mode, vector, 1, trig_mode, - NULL); + __apic_accept_irq(apic, mode, vector, 1, trig_mode, NULL); } - return 0; } void kvm_apic_nmi_wd_deliver(struct kvm_vcpu *vcpu) @@ -1608,8 +1597,8 @@ void kvm_inject_apic_timer_irqs(struct kvm_vcpu *vcpu) return; if (atomic_read(&apic->lapic_timer.pending) > 0) { - if (kvm_apic_local_deliver(apic, APIC_LVTT)) - atomic_dec(&apic->lapic_timer.pending); + kvm_apic_local_deliver(apic, APIC_LVTT); + atomic_set(&apic->lapic_timer.pending, 0); } } diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h index c730ac9fe801..61a73a01ab0b 100644 --- a/arch/x86/kvm/lapic.h +++ b/arch/x86/kvm/lapic.h @@ -57,9 +57,9 @@ void kvm_apic_update_tmr(struct kvm_vcpu *vcpu, u32 *tmr); void kvm_apic_update_irr(struct kvm_vcpu *vcpu, u32 *pir); int kvm_apic_match_physical_addr(struct kvm_lapic *apic, u16 dest); int kvm_apic_match_logical_addr(struct kvm_lapic *apic, u8 mda); -int kvm_apic_set_irq(struct kvm_vcpu *vcpu, struct kvm_lapic_irq *irq, - unsigned long *dest_map); -int kvm_apic_local_deliver(struct kvm_lapic *apic, int lvt_type); +void kvm_apic_set_irq(struct kvm_vcpu *vcpu, struct kvm_lapic_irq *irq, + unsigned long *dest_map); +void kvm_apic_local_deliver(struct kvm_lapic *apic, int lvt_type); bool kvm_irq_delivery_to_apic_fast(struct kvm *kvm, struct kvm_lapic *src, struct kvm_lapic_irq *irq, int *r, unsigned long *dest_map); diff --git a/virt/kvm/irq_comm.c b/virt/kvm/irq_comm.c index e2e6b4473a96..ef1817b61cf4 100644 --- a/virt/kvm/irq_comm.c +++ b/virt/kvm/irq_comm.c @@ -91,7 +91,8 @@ int kvm_irq_delivery_to_apic(struct kvm *kvm, struct kvm_lapic *src, if (!kvm_is_dm_lowest_prio(irq)) { if (r < 0) r = 0; - r += kvm_apic_set_irq(vcpu, irq, dest_map); + kvm_apic_set_irq(vcpu, irq, dest_map); + r++; } else if (kvm_lapic_enabled(vcpu)) { if (!lowest) lowest = vcpu; @@ -100,8 +101,10 @@ int kvm_irq_delivery_to_apic(struct kvm *kvm, struct kvm_lapic *src, } } - if (lowest) - r = kvm_apic_set_irq(lowest, irq, dest_map); + if (lowest) { + kvm_apic_set_irq(lowest, irq, dest_map); + r = 1; + } return r; } -- cgit v1.2.3 From 9dbce04402e33e362e3e946c437bc70b8102a95d Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 14 May 2013 15:02:44 +0300 Subject: ASoC: wm_adsp: memory leak in wm_adsp_create_control() There are two return paths which don't kfree(name). Signed-off-by: Dan Carpenter Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 137830611928..d715c8ede772 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -836,7 +836,8 @@ static int wm_adsp_create_control(struct snd_soc_codec *codec, region_name = "ZM"; break; default: - return -EINVAL; + ret = -EINVAL; + goto err_name; } snprintf(name, PAGE_SIZE, "DSP%d %s %x", @@ -847,7 +848,7 @@ static int wm_adsp_create_control(struct snd_soc_codec *codec, if (!strcmp(ctl->name, name)) { if (!ctl->enabled) ctl->enabled = 1; - return 0; + goto found; } } @@ -887,6 +888,7 @@ static int wm_adsp_create_control(struct snd_soc_codec *codec, INIT_WORK(&ctl_work->work, wm_adsp_ctl_work); schedule_work(&ctl_work->work); +found: kfree(name); return 0; -- cgit v1.2.3 From 90996f43b3fa36075e501a52a3e2286896d74e79 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 14 May 2013 11:05:30 +0200 Subject: ASoC: core: Move snd_soc_set_runtime_hwparams() to soc-pcm.c snd_soc_set_runtime_hwparams() is the only PCM related function that lives in soc-core.c. All other PCM related functions live in soc-pcm.c, so move snd_soc_set_runtime_hwparams() over as well for a bit more consistency. Signed-off-by: Lars-Peter Clausen Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- sound/soc/soc-core.c | 23 ----------------------- sound/soc/soc-pcm.c | 23 +++++++++++++++++++++++ 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 308895a438d6..9d95ef531308 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2219,29 +2219,6 @@ int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg, } EXPORT_SYMBOL_GPL(snd_soc_test_bits); -/** - * snd_soc_set_runtime_hwparams - set the runtime hardware parameters - * @substream: the pcm substream - * @hw: the hardware parameters - * - * Sets the substream runtime hardware parameters. - */ -int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream, - const struct snd_pcm_hardware *hw) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - runtime->hw.info = hw->info; - runtime->hw.formats = hw->formats; - runtime->hw.period_bytes_min = hw->period_bytes_min; - runtime->hw.period_bytes_max = hw->period_bytes_max; - runtime->hw.periods_min = hw->periods_min; - runtime->hw.periods_max = hw->periods_max; - runtime->hw.buffer_bytes_max = hw->buffer_bytes_max; - runtime->hw.fifo_size = hw->fifo_size; - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_set_runtime_hwparams); - /** * snd_soc_cnew - create new control * @_template: control template diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 73bb8eefa491..058b40378451 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -33,6 +33,29 @@ #define DPCM_MAX_BE_USERS 8 +/** + * snd_soc_set_runtime_hwparams - set the runtime hardware parameters + * @substream: the pcm substream + * @hw: the hardware parameters + * + * Sets the substream runtime hardware parameters. + */ +int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream, + const struct snd_pcm_hardware *hw) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + runtime->hw.info = hw->info; + runtime->hw.formats = hw->formats; + runtime->hw.period_bytes_min = hw->period_bytes_min; + runtime->hw.period_bytes_max = hw->period_bytes_max; + runtime->hw.periods_min = hw->periods_min; + runtime->hw.periods_max = hw->periods_max; + runtime->hw.buffer_bytes_max = hw->buffer_bytes_max; + runtime->hw.fifo_size = hw->fifo_size; + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_set_runtime_hwparams); + /* DPCM stream event, send event to FE and all active BEs. */ static int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir, int event) -- cgit v1.2.3 From bd477c31ca3ae85645fb2852bfa3954a623f9237 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 14 May 2013 11:05:31 +0200 Subject: ASoC: core: Add helper function to initialize the runtime pcm We use the same code to initialize the runtime pcm based on the snd_soc_pcm_stream struct on both the playback and capture path. Factor this code into a helper function to make things a bit more tidy. Signed-off-by: Lars-Peter Clausen Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- sound/soc/soc-pcm.c | 68 +++++++++++++++++++---------------------------------- 1 file changed, 24 insertions(+), 44 deletions(-) diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 058b40378451..2f6f545f4ee8 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -147,6 +147,26 @@ static void soc_pcm_apply_msb(struct snd_pcm_substream *substream, } } +static void soc_pcm_init_runtime_hw(struct snd_pcm_hardware *hw, + struct snd_soc_pcm_stream *codec_stream, + struct snd_soc_pcm_stream *cpu_stream) +{ + hw->rate_min = max(codec_stream->rate_min, cpu_stream->rate_min); + hw->rate_max = max(codec_stream->rate_max, cpu_stream->rate_max); + hw->channels_min = max(codec_stream->channels_min, + cpu_stream->channels_min); + hw->channels_max = min(codec_stream->channels_max, + cpu_stream->channels_max); + hw->formats = codec_stream->formats & cpu_stream->formats; + hw->rates = codec_stream->rates & cpu_stream->rates; + if (codec_stream->rates + & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) + hw->rates |= cpu_stream->rates; + if (cpu_stream->rates + & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) + hw->rates |= codec_stream->rates; +} + /* * Called by ALSA when a PCM substream is opened, the runtime->hw record is * then initialized and any private data can be allocated. This also calls @@ -212,51 +232,11 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) /* Check that the codec and cpu DAIs are compatible */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - runtime->hw.rate_min = - max(codec_dai_drv->playback.rate_min, - cpu_dai_drv->playback.rate_min); - runtime->hw.rate_max = - min(codec_dai_drv->playback.rate_max, - cpu_dai_drv->playback.rate_max); - runtime->hw.channels_min = - max(codec_dai_drv->playback.channels_min, - cpu_dai_drv->playback.channels_min); - runtime->hw.channels_max = - min(codec_dai_drv->playback.channels_max, - cpu_dai_drv->playback.channels_max); - runtime->hw.formats = - codec_dai_drv->playback.formats & cpu_dai_drv->playback.formats; - runtime->hw.rates = - codec_dai_drv->playback.rates & cpu_dai_drv->playback.rates; - if (codec_dai_drv->playback.rates - & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) - runtime->hw.rates |= cpu_dai_drv->playback.rates; - if (cpu_dai_drv->playback.rates - & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) - runtime->hw.rates |= codec_dai_drv->playback.rates; + soc_pcm_init_runtime_hw(&runtime->hw, &codec_dai_drv->playback, + &cpu_dai_drv->playback); } else { - runtime->hw.rate_min = - max(codec_dai_drv->capture.rate_min, - cpu_dai_drv->capture.rate_min); - runtime->hw.rate_max = - min(codec_dai_drv->capture.rate_max, - cpu_dai_drv->capture.rate_max); - runtime->hw.channels_min = - max(codec_dai_drv->capture.channels_min, - cpu_dai_drv->capture.channels_min); - runtime->hw.channels_max = - min(codec_dai_drv->capture.channels_max, - cpu_dai_drv->capture.channels_max); - runtime->hw.formats = - codec_dai_drv->capture.formats & cpu_dai_drv->capture.formats; - runtime->hw.rates = - codec_dai_drv->capture.rates & cpu_dai_drv->capture.rates; - if (codec_dai_drv->capture.rates - & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) - runtime->hw.rates |= cpu_dai_drv->capture.rates; - if (cpu_dai_drv->capture.rates - & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS)) - runtime->hw.rates |= codec_dai_drv->capture.rates; + soc_pcm_init_runtime_hw(&runtime->hw, &codec_dai_drv->capture, + &cpu_dai_drv->capture); } ret = -EINVAL; -- cgit v1.2.3 From 2b581074357c42f63ae827ee28c9f244b91a38ac Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 14 May 2013 11:05:32 +0200 Subject: ASoC: core: Use kasprintf instead of opencoding it kasprintf calculates the size of the result string, allocates a buffer large enough to hold the string and then performs the format string operation. There are a couple of places in ASoC where these three steps are done by hand and where kasprintf can be used instead. Signed-off-by: Lars-Peter Clausen Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- sound/soc/soc-core.c | 6 +----- sound/soc/soc-dapm.c | 31 ++++++++++--------------------- 2 files changed, 11 insertions(+), 26 deletions(-) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 9d95ef531308..4489c5b7b53a 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2237,7 +2237,6 @@ struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template, struct snd_kcontrol_new template; struct snd_kcontrol *kcontrol; char *name = NULL; - int name_len; memcpy(&template, _template, sizeof(template)); template.index = 0; @@ -2246,13 +2245,10 @@ struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template, long_name = template.name; if (prefix) { - name_len = strlen(long_name) + strlen(prefix) + 2; - name = kmalloc(name_len, GFP_KERNEL); + name = kasprintf(GFP_KERNEL, "%s %s", prefix, long_name); if (!name) return NULL; - snprintf(name, name_len, "%s %s", prefix, long_name); - template.name = name; } else { template.name = long_name; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index e4e5420de725..071579be7cb9 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -521,7 +521,6 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w, int wlistentries; size_t wlistsize; bool wname_in_long_name, kcname_in_long_name; - size_t name_len; char *long_name; const char *name; int ret; @@ -586,25 +585,19 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w, } if (wname_in_long_name && kcname_in_long_name) { - name_len = strlen(w->name) - prefix_len + 1 + - strlen(w->kcontrol_news[kci].name) + 1; - - long_name = kmalloc(name_len, GFP_KERNEL); - if (long_name == NULL) { - kfree(wlist); - return -ENOMEM; - } - /* * The control will get a prefix from the control * creation process but we're also using the same * prefix for widgets so cut the prefix off the * front of the widget name. */ - snprintf(long_name, name_len, "%s %s", + long_name = kasprintf(GFP_KERNEL, "%s %s", w->name + prefix_len, w->kcontrol_news[kci].name); - long_name[name_len - 1] = '\0'; + if (long_name == NULL) { + kfree(wlist); + return -ENOMEM; + } name = long_name; } else if (wname_in_long_name) { @@ -3077,7 +3070,6 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_widget *widget) { struct snd_soc_dapm_widget *w; - size_t name_len; int ret; if ((w = dapm_cnew_widget(widget)) == NULL) @@ -3118,19 +3110,16 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, break; } - name_len = strlen(widget->name) + 1; if (dapm->codec && dapm->codec->name_prefix) - name_len += 1 + strlen(dapm->codec->name_prefix); - w->name = kmalloc(name_len, GFP_KERNEL); + w->name = kasprintf(GFP_KERNEL, "%s %s", + dapm->codec->name_prefix, widget->name); + else + w->name = kasprintf(GFP_KERNEL, "%s", widget->name); + if (w->name == NULL) { kfree(w); return NULL; } - if (dapm->codec && dapm->codec->name_prefix) - snprintf((char *)w->name, name_len, "%s %s", - dapm->codec->name_prefix, widget->name); - else - snprintf((char *)w->name, name_len, "%s", widget->name); switch (w->id) { case snd_soc_dapm_switch: -- cgit v1.2.3 From 213fa5d9685b985e0c61a8db1883a3abf94b18d7 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Tue, 14 May 2013 07:54:23 +0100 Subject: regmap: debugfs: Fix return from regmap_debugfs_get_dump_start regmap_debugfs_get_dump_start should return the offset of the register it should start reading from, However in the current code at one point the code does not return correct register offset. With this patch all the returns from this function takes reg_stride in to consideration to return correct offset. Signed-off-by: Srinivas Kandagatla Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 8772fb77e6ed..98cb94e58320 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -145,7 +145,7 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, reg_offset = fpos_offset / map->debugfs_tot_len; *pos = c->min + (reg_offset * map->debugfs_tot_len); mutex_unlock(&map->cache_lock); - return c->base_reg + reg_offset; + return c->base_reg + (reg_offset * map->reg_stride); } *pos = c->max; -- cgit v1.2.3 From cee22a15052faa817e3ec8985a28154d3fabc7aa Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 8 Apr 2013 16:45:40 +0530 Subject: workqueues: Introduce new flag WQ_POWER_EFFICIENT for power oriented workqueues Workqueues can be performance or power-oriented. Currently, most workqueues are bound to the CPU they were created on. This gives good performance (due to cache effects) at the cost of potentially waking up otherwise idle cores (Idle from scheduler's perspective. Which may or may not be physically idle) just to process some work. To save power, we can allow the work to be rescheduled on a core that is already awake. Workqueues created with the WQ_UNBOUND flag will allow some power savings. However, we don't change the default behaviour of the system. To enable power-saving behaviour, a new config option CONFIG_WQ_POWER_EFFICIENT needs to be turned on. This option can also be overridden by the workqueue.power_efficient boot parameter. tj: Updated config description and comments. Renamed CONFIG_WQ_POWER_EFFICIENT to CONFIG_WQ_POWER_EFFICIENT_DEFAULT. Signed-off-by: Viresh Kumar Reviewed-by: Amit Kucheria Signed-off-by: Tejun Heo --- Documentation/kernel-parameters.txt | 15 +++++++++++++++ include/linux/workqueue.h | 27 +++++++++++++++++++++++++++ kernel/power/Kconfig | 20 ++++++++++++++++++++ kernel/workqueue.c | 13 +++++++++++++ 4 files changed, 75 insertions(+) diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index c3bfacb92910..37dfd720de79 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -3320,6 +3320,21 @@ bytes respectively. Such letter suffixes can also be entirely omitted. that this also can be controlled per-workqueue for workqueues visible under /sys/bus/workqueue/. + workqueue.power_efficient + Per-cpu workqueues are generally preferred because + they show better performance thanks to cache + locality; unfortunately, per-cpu workqueues tend to + be more power hungry than unbound workqueues. + + Enabling this makes the per-cpu workqueues which + were observed to contribute significantly to power + consumption unbound, leading to measurably lower + power usage at the cost of small performance + overhead. + + The default value of this parameter is determined by + the config option CONFIG_WQ_POWER_EFFICIENT_DEFAULT. + x2apic_phys [X86-64,APIC] Use x2apic physical mode instead of default x2apic cluster mode on platforms supporting x2apic. diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 623488fdc1f5..fc0136b604f2 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -303,6 +303,33 @@ enum { WQ_CPU_INTENSIVE = 1 << 5, /* cpu instensive workqueue */ WQ_SYSFS = 1 << 6, /* visible in sysfs, see wq_sysfs_register() */ + /* + * Per-cpu workqueues are generally preferred because they tend to + * show better performance thanks to cache locality. Per-cpu + * workqueues exclude the scheduler from choosing the CPU to + * execute the worker threads, which has an unfortunate side effect + * of increasing power consumption. + * + * The scheduler considers a CPU idle if it doesn't have any task + * to execute and tries to keep idle cores idle to conserve power; + * however, for example, a per-cpu work item scheduled from an + * interrupt handler on an idle CPU will force the scheduler to + * excute the work item on that CPU breaking the idleness, which in + * turn may lead to more scheduling choices which are sub-optimal + * in terms of power consumption. + * + * Workqueues marked with WQ_POWER_EFFICIENT are per-cpu by default + * but become unbound if workqueue.power_efficient kernel param is + * specified. Per-cpu workqueues which are identified to + * contribute significantly to power-consumption are identified and + * marked with this flag and enabling the power_efficient mode + * leads to noticeable power saving at the cost of small + * performance disadvantage. + * + * http://thread.gmane.org/gmane.linux.kernel/1480396 + */ + WQ_POWER_EFFICIENT = 1 << 7, + __WQ_DRAINING = 1 << 16, /* internal: workqueue is draining */ __WQ_ORDERED = 1 << 17, /* internal: workqueue is ordered */ diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index 5dfdc9ea180b..46455961a88f 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -263,6 +263,26 @@ config PM_GENERIC_DOMAINS bool depends on PM +config WQ_POWER_EFFICIENT_DEFAULT + bool "Enable workqueue power-efficient mode by default" + depends on PM + default n + help + Per-cpu workqueues are generally preferred because they show + better performance thanks to cache locality; unfortunately, + per-cpu workqueues tend to be more power hungry than unbound + workqueues. + + Enabling workqueue.power_efficient kernel parameter makes the + per-cpu workqueues which were observed to contribute + significantly to power consumption unbound, leading to measurably + lower power usage at the cost of small performance overhead. + + This config option determines whether workqueue.power_efficient + is enabled by default. + + If in doubt, say N. + config PM_GENERIC_DOMAINS_SLEEP def_bool y depends on PM_SLEEP && PM_GENERIC_DOMAINS diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 4aa9f5bc6b2d..8068d97ce141 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -272,6 +272,15 @@ static cpumask_var_t *wq_numa_possible_cpumask; static bool wq_disable_numa; module_param_named(disable_numa, wq_disable_numa, bool, 0444); +/* see the comment above the definition of WQ_POWER_EFFICIENT */ +#ifdef CONFIG_WQ_POWER_EFFICIENT_DEFAULT +static bool wq_power_efficient = true; +#else +static bool wq_power_efficient; +#endif + +module_param_named(power_efficient, wq_power_efficient, bool, 0444); + static bool wq_numa_enabled; /* unbound NUMA affinity enabled */ /* buf for wq_update_unbound_numa_attrs(), protected by CPU hotplug exclusion */ @@ -4085,6 +4094,10 @@ struct workqueue_struct *__alloc_workqueue_key(const char *fmt, struct workqueue_struct *wq; struct pool_workqueue *pwq; + /* see the comment above the definition of WQ_POWER_EFFICIENT */ + if ((flags & WQ_POWER_EFFICIENT) && wq_power_efficient) + flags |= WQ_UNBOUND; + /* allocate wq and format name */ if (flags & WQ_UNBOUND) tbl_size = wq_numa_tbl_len * sizeof(wq->numa_pwq_tbl[0]); -- cgit v1.2.3 From 0668106ca3865ba945e155097fb042bf66d364d3 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 24 Apr 2013 17:12:54 +0530 Subject: workqueue: Add system wide power_efficient workqueues This patch adds system wide workqueues aligned towards power saving. This is done by allocating them with WQ_UNBOUND flag if 'wq_power_efficient' is set to 'true'. tj: updated comments a bit. Signed-off-by: Viresh Kumar Signed-off-by: Tejun Heo --- include/linux/workqueue.h | 8 ++++++++ kernel/workqueue.c | 13 ++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index fc0136b604f2..a9f4119c7e2e 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -360,11 +360,19 @@ enum { * * system_freezable_wq is equivalent to system_wq except that it's * freezable. + * + * *_power_efficient_wq are inclined towards saving power and converted + * into WQ_UNBOUND variants if 'wq_power_efficient' is enabled; otherwise, + * they are same as their non-power-efficient counterparts - e.g. + * system_power_efficient_wq is identical to system_wq if + * 'wq_power_efficient' is disabled. See WQ_POWER_EFFICIENT for more info. */ extern struct workqueue_struct *system_wq; extern struct workqueue_struct *system_long_wq; extern struct workqueue_struct *system_unbound_wq; extern struct workqueue_struct *system_freezable_wq; +extern struct workqueue_struct *system_power_efficient_wq; +extern struct workqueue_struct *system_freezable_power_efficient_wq; static inline struct workqueue_struct * __deprecated __system_nrt_wq(void) { diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 8068d97ce141..16ca2d3dd29f 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -314,6 +314,10 @@ struct workqueue_struct *system_unbound_wq __read_mostly; EXPORT_SYMBOL_GPL(system_unbound_wq); struct workqueue_struct *system_freezable_wq __read_mostly; EXPORT_SYMBOL_GPL(system_freezable_wq); +struct workqueue_struct *system_power_efficient_wq __read_mostly; +EXPORT_SYMBOL_GPL(system_power_efficient_wq); +struct workqueue_struct *system_freezable_power_efficient_wq __read_mostly; +EXPORT_SYMBOL_GPL(system_freezable_power_efficient_wq); static int worker_thread(void *__worker); static void copy_workqueue_attrs(struct workqueue_attrs *to, @@ -4987,8 +4991,15 @@ static int __init init_workqueues(void) WQ_UNBOUND_MAX_ACTIVE); system_freezable_wq = alloc_workqueue("events_freezable", WQ_FREEZABLE, 0); + system_power_efficient_wq = alloc_workqueue("events_power_efficient", + WQ_POWER_EFFICIENT, 0); + system_freezable_power_efficient_wq = alloc_workqueue("events_freezable_power_efficient", + WQ_FREEZABLE | WQ_POWER_EFFICIENT, + 0); BUG_ON(!system_wq || !system_highpri_wq || !system_long_wq || - !system_unbound_wq || !system_freezable_wq); + !system_unbound_wq || !system_freezable_wq || + !system_power_efficient_wq || + !system_freezable_power_efficient_wq); return 0; } early_initcall(init_workqueues); -- cgit v1.2.3 From bbb47bdeae756f04b896b55b51f230f3eb21f207 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 24 Apr 2013 17:12:55 +0530 Subject: PHYLIB: queue work on system_power_efficient_wq Phylib uses workqueues for multiple purposes. There is no real dependency of scheduling these on the cpu which scheduled them. On a idle system, it is observed that and idle cpu wakes up many times just to service this work. It would be better if we can schedule it on a cpu which the scheduler believes to be the most appropriate one. This patch replaces system_wq with system_power_efficient_wq for PHYLIB. Cc: David S. Miller Cc: netdev@vger.kernel.org Signed-off-by: Viresh Kumar Acked-by: David S. Miller Signed-off-by: Tejun Heo --- drivers/net/phy/phy.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index c14f14741b3f..984c0b5ba174 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -439,7 +439,7 @@ void phy_start_machine(struct phy_device *phydev, { phydev->adjust_state = handler; - schedule_delayed_work(&phydev->state_queue, HZ); + queue_delayed_work(system_power_efficient_wq, &phydev->state_queue, HZ); } /** @@ -500,7 +500,7 @@ static irqreturn_t phy_interrupt(int irq, void *phy_dat) disable_irq_nosync(irq); atomic_inc(&phydev->irq_disable); - schedule_work(&phydev->phy_queue); + queue_work(system_power_efficient_wq, &phydev->phy_queue); return IRQ_HANDLED; } @@ -655,7 +655,7 @@ static void phy_change(struct work_struct *work) /* reschedule state queue work to run as soon as possible */ cancel_delayed_work_sync(&phydev->state_queue); - schedule_delayed_work(&phydev->state_queue, 0); + queue_delayed_work(system_power_efficient_wq, &phydev->state_queue, 0); return; @@ -918,7 +918,8 @@ void phy_state_machine(struct work_struct *work) if (err < 0) phy_error(phydev); - schedule_delayed_work(&phydev->state_queue, PHY_STATE_TIME * HZ); + queue_delayed_work(system_power_efficient_wq, &phydev->state_queue, + PHY_STATE_TIME * HZ); } static inline void mmd_phy_indirect(struct mii_bus *bus, int prtad, int devad, -- cgit v1.2.3 From 695588f9454bdbc7c1a2fbb8a6bfdcfba6183348 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 24 Apr 2013 17:12:56 +0530 Subject: block: queue work on power efficient wq Block layer uses workqueues for multiple purposes. There is no real dependency of scheduling these on the cpu which scheduled them. On a idle system, it is observed that and idle cpu wakes up many times just to service this work. It would be better if we can schedule it on a cpu which the scheduler believes to be the most appropriate one. This patch replaces normal workqueues with power efficient versions. Cc: Jens Axboe Signed-off-by: Viresh Kumar Signed-off-by: Tejun Heo --- block/blk-core.c | 3 ++- block/blk-ioc.c | 3 ++- block/genhd.c | 12 ++++++++---- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/block/blk-core.c b/block/blk-core.c index 33c33bc99ddd..f0deb8bf89e1 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -3180,7 +3180,8 @@ int __init blk_dev_init(void) /* used for unplugging and affects IO latency/throughput - HIGHPRI */ kblockd_workqueue = alloc_workqueue("kblockd", - WQ_MEM_RECLAIM | WQ_HIGHPRI, 0); + WQ_MEM_RECLAIM | WQ_HIGHPRI | + WQ_POWER_EFFICIENT, 0); if (!kblockd_workqueue) panic("Failed to create kblockd\n"); diff --git a/block/blk-ioc.c b/block/blk-ioc.c index 9c4bb8266bc8..4464c823cff2 100644 --- a/block/blk-ioc.c +++ b/block/blk-ioc.c @@ -144,7 +144,8 @@ void put_io_context(struct io_context *ioc) if (atomic_long_dec_and_test(&ioc->refcount)) { spin_lock_irqsave(&ioc->lock, flags); if (!hlist_empty(&ioc->icq_list)) - schedule_work(&ioc->release_work); + queue_work(system_power_efficient_wq, + &ioc->release_work); else free_ioc = true; spin_unlock_irqrestore(&ioc->lock, flags); diff --git a/block/genhd.c b/block/genhd.c index 20625eed5511..e9094b375c05 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -1489,9 +1489,11 @@ static void __disk_unblock_events(struct gendisk *disk, bool check_now) intv = disk_events_poll_jiffies(disk); set_timer_slack(&ev->dwork.timer, intv / 4); if (check_now) - queue_delayed_work(system_freezable_wq, &ev->dwork, 0); + queue_delayed_work(system_freezable_power_efficient_wq, + &ev->dwork, 0); else if (intv) - queue_delayed_work(system_freezable_wq, &ev->dwork, intv); + queue_delayed_work(system_freezable_power_efficient_wq, + &ev->dwork, intv); out_unlock: spin_unlock_irqrestore(&ev->lock, flags); } @@ -1534,7 +1536,8 @@ void disk_flush_events(struct gendisk *disk, unsigned int mask) spin_lock_irq(&ev->lock); ev->clearing |= mask; if (!ev->block) - mod_delayed_work(system_freezable_wq, &ev->dwork, 0); + mod_delayed_work(system_freezable_power_efficient_wq, + &ev->dwork, 0); spin_unlock_irq(&ev->lock); } @@ -1627,7 +1630,8 @@ static void disk_check_events(struct disk_events *ev, intv = disk_events_poll_jiffies(disk); if (!ev->block && intv) - queue_delayed_work(system_freezable_wq, &ev->dwork, intv); + queue_delayed_work(system_freezable_power_efficient_wq, + &ev->dwork, intv); spin_unlock_irq(&ev->lock); -- cgit v1.2.3 From a85f1a41f020bc2c97611060bcfae6f48a1db28d Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 24 Apr 2013 17:12:57 +0530 Subject: fbcon: queue work on power efficient wq fbcon uses workqueues and it has no real dependency of scheduling these on the cpu which scheduled them. On a idle system, it is observed that and idle cpu wakes up many times just to service this work. It would be better if we can schedule it on a cpu which the scheduler believes to be the most appropriate one. This patch replaces system_wq with system_power_efficient_wq. Cc: Dave Airlie Cc: linux-fbdev@vger.kernel.org Signed-off-by: Viresh Kumar Signed-off-by: Tejun Heo --- drivers/video/console/fbcon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index a92783e480e6..0d8f98c79a6c 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -404,7 +404,7 @@ static void cursor_timer_handler(unsigned long dev_addr) struct fb_info *info = (struct fb_info *) dev_addr; struct fbcon_ops *ops = info->fbcon_par; - schedule_work(&info->queue); + queue_work(system_power_efficient_wq, &info->queue); mod_timer(&ops->cursor_timer, jiffies + HZ/5); } -- cgit v1.2.3 From fa3ca07e96185aa1496b405472399a2a2a336a17 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sun, 14 Apr 2013 11:36:56 -0700 Subject: cgroup: refactor hierarchy_id handling We're planning to converting hierarchy_ida to an idr and use it to look up hierarchy from its id. As we want the mapping to happen atomically with cgroupfs_root registration, this patch refactors hierarchy_id init / exit so that ida operations happen inside cgroup_[root_]mutex. * s/init_root_id()/cgroup_init_root_id()/ and make it return 0 or -errno like a normal function. * Move hierarchy_id initialization from cgroup_root_from_opts() into cgroup_mount() block where the root is confirmed to be used and being registered while holding both mutexes. * Split cgroup_drop_id() into cgroup_exit_root_id() and cgroup_free_root(), so that ID release can happen before dropping the mutexes in cgroup_kill_sb(). The latter expects hierarchy_id to be exited before being invoked. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- kernel/cgroup.c | 56 +++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 21 deletions(-) diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 2a9926275f80..dbc84f7d23b8 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -1426,13 +1426,13 @@ static void init_cgroup_root(struct cgroupfs_root *root) list_add_tail(&cgrp->allcg_node, &root->allcg_list); } -static bool init_root_id(struct cgroupfs_root *root) +static int cgroup_init_root_id(struct cgroupfs_root *root) { - int ret = 0; + int ret; do { if (!ida_pre_get(&hierarchy_ida, GFP_KERNEL)) - return false; + return -ENOMEM; spin_lock(&hierarchy_id_lock); /* Try to allocate the next unused ID */ ret = ida_get_new_above(&hierarchy_ida, next_hierarchy_id, @@ -1448,7 +1448,18 @@ static bool init_root_id(struct cgroupfs_root *root) } spin_unlock(&hierarchy_id_lock); } while (ret); - return true; + return 0; +} + +static void cgroup_exit_root_id(struct cgroupfs_root *root) +{ + if (root->hierarchy_id) { + spin_lock(&hierarchy_id_lock); + ida_remove(&hierarchy_ida, root->hierarchy_id); + spin_unlock(&hierarchy_id_lock); + + root->hierarchy_id = 0; + } } static int cgroup_test_super(struct super_block *sb, void *data) @@ -1482,10 +1493,6 @@ static struct cgroupfs_root *cgroup_root_from_opts(struct cgroup_sb_opts *opts) if (!root) return ERR_PTR(-ENOMEM); - if (!init_root_id(root)) { - kfree(root); - return ERR_PTR(-ENOMEM); - } init_cgroup_root(root); root->subsys_mask = opts->subsys_mask; @@ -1500,17 +1507,15 @@ static struct cgroupfs_root *cgroup_root_from_opts(struct cgroup_sb_opts *opts) return root; } -static void cgroup_drop_root(struct cgroupfs_root *root) +static void cgroup_free_root(struct cgroupfs_root *root) { - if (!root) - return; + if (root) { + /* hierarhcy ID shoulid already have been released */ + WARN_ON_ONCE(root->hierarchy_id); - BUG_ON(!root->hierarchy_id); - spin_lock(&hierarchy_id_lock); - ida_remove(&hierarchy_ida, root->hierarchy_id); - spin_unlock(&hierarchy_id_lock); - ida_destroy(&root->cgroup_ida); - kfree(root); + ida_destroy(&root->cgroup_ida); + kfree(root); + } } static int cgroup_set_super(struct super_block *sb, void *data) @@ -1597,7 +1602,7 @@ static struct dentry *cgroup_mount(struct file_system_type *fs_type, sb = sget(fs_type, cgroup_test_super, cgroup_set_super, 0, &opts); if (IS_ERR(sb)) { ret = PTR_ERR(sb); - cgroup_drop_root(opts.new_root); + cgroup_free_root(opts.new_root); goto drop_modules; } @@ -1641,6 +1646,10 @@ static struct dentry *cgroup_mount(struct file_system_type *fs_type, if (ret) goto unlock_drop; + ret = cgroup_init_root_id(root); + if (ret) + goto unlock_drop; + ret = rebind_subsystems(root, root->subsys_mask); if (ret == -EBUSY) { free_cg_links(&tmp_cg_links); @@ -1684,7 +1693,7 @@ static struct dentry *cgroup_mount(struct file_system_type *fs_type, * We re-used an existing hierarchy - the new root (if * any) is not needed */ - cgroup_drop_root(opts.new_root); + cgroup_free_root(opts.new_root); if (((root->flags | opts.flags) & CGRP_ROOT_SANE_BEHAVIOR) && root->flags != opts.flags) { @@ -1702,6 +1711,7 @@ static struct dentry *cgroup_mount(struct file_system_type *fs_type, return dget(sb->s_root); unlock_drop: + cgroup_exit_root_id(root); mutex_unlock(&cgroup_root_mutex); mutex_unlock(&cgroup_mutex); mutex_unlock(&inode->i_mutex); @@ -1754,13 +1764,15 @@ static void cgroup_kill_sb(struct super_block *sb) { root_count--; } + cgroup_exit_root_id(root); + mutex_unlock(&cgroup_root_mutex); mutex_unlock(&cgroup_mutex); simple_xattrs_free(&cgrp->xattrs); kill_litter_super(sb); - cgroup_drop_root(root); + cgroup_free_root(root); } static struct file_system_type cgroup_fs_type = { @@ -4642,7 +4654,9 @@ int __init cgroup_init(void) /* Add init_css_set to the hash table */ key = css_set_hash(init_css_set.subsys); hash_add(css_set_table, &init_css_set.hlist, key); - BUG_ON(!init_root_id(&rootnode)); + + /* allocate id for the dummy hierarchy */ + BUG_ON(cgroup_init_root_id(&rootnode)); cgroup_kobj = kobject_create_and_add("cgroup", fs_kobj); if (!cgroup_kobj) { -- cgit v1.2.3 From 54e7b4eb15fc4354d5ada5469e3db4a220ddb3ed Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sun, 14 Apr 2013 11:36:57 -0700 Subject: cgroup: drop hierarchy_id_lock Now that hierarchy_id alloc / free are protected by the cgroup mutexes, there's no need for this separate lock. Drop it. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- kernel/cgroup.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/kernel/cgroup.c b/kernel/cgroup.c index dbc84f7d23b8..3ef677d314bc 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -189,9 +189,13 @@ struct cgroup_event { static LIST_HEAD(roots); static int root_count; +/* + * Hierarchy ID allocation and mapping. It follows the same exclusion + * rules as other root ops - both cgroup_mutex and cgroup_root_mutex for + * writes, either for reads. + */ static DEFINE_IDA(hierarchy_ida); static int next_hierarchy_id; -static DEFINE_SPINLOCK(hierarchy_id_lock); /* dummytop is a shorthand for the dummy hierarchy's top cgroup */ #define dummytop (&rootnode.top_cgroup) @@ -1430,10 +1434,12 @@ static int cgroup_init_root_id(struct cgroupfs_root *root) { int ret; + lockdep_assert_held(&cgroup_mutex); + lockdep_assert_held(&cgroup_root_mutex); + do { if (!ida_pre_get(&hierarchy_ida, GFP_KERNEL)) return -ENOMEM; - spin_lock(&hierarchy_id_lock); /* Try to allocate the next unused ID */ ret = ida_get_new_above(&hierarchy_ida, next_hierarchy_id, &root->hierarchy_id); @@ -1446,18 +1452,17 @@ static int cgroup_init_root_id(struct cgroupfs_root *root) /* Can only get here if the 31-bit IDR is full ... */ BUG_ON(ret); } - spin_unlock(&hierarchy_id_lock); } while (ret); return 0; } static void cgroup_exit_root_id(struct cgroupfs_root *root) { + lockdep_assert_held(&cgroup_mutex); + lockdep_assert_held(&cgroup_root_mutex); + if (root->hierarchy_id) { - spin_lock(&hierarchy_id_lock); ida_remove(&hierarchy_ida, root->hierarchy_id); - spin_unlock(&hierarchy_id_lock); - root->hierarchy_id = 0; } } @@ -4656,8 +4661,14 @@ int __init cgroup_init(void) hash_add(css_set_table, &init_css_set.hlist, key); /* allocate id for the dummy hierarchy */ + mutex_lock(&cgroup_mutex); + mutex_lock(&cgroup_root_mutex); + BUG_ON(cgroup_init_root_id(&rootnode)); + mutex_unlock(&cgroup_root_mutex); + mutex_unlock(&cgroup_mutex); + cgroup_kobj = kobject_create_and_add("cgroup", fs_kobj); if (!cgroup_kobj) { err = -ENOMEM; -- cgit v1.2.3 From 1a574231669f8c3065c83974e9557fcbbd94b8a6 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sun, 14 Apr 2013 11:36:58 -0700 Subject: cgroup: make hierarchy_id use cyclic idr We want to be able to lookup a hierarchy from its id and cyclic allocation is a whole lot simpler with idr. Convert to idr and use idr_alloc_cyclc(). Signed-off-by: Tejun Heo Acked-by: Li Zefan --- kernel/cgroup.c | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 3ef677d314bc..dcb417c6c242 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -194,8 +194,7 @@ static int root_count; * rules as other root ops - both cgroup_mutex and cgroup_root_mutex for * writes, either for reads. */ -static DEFINE_IDA(hierarchy_ida); -static int next_hierarchy_id; +static DEFINE_IDR(cgroup_hierarchy_idr); /* dummytop is a shorthand for the dummy hierarchy's top cgroup */ #define dummytop (&rootnode.top_cgroup) @@ -1432,27 +1431,16 @@ static void init_cgroup_root(struct cgroupfs_root *root) static int cgroup_init_root_id(struct cgroupfs_root *root) { - int ret; + int id; lockdep_assert_held(&cgroup_mutex); lockdep_assert_held(&cgroup_root_mutex); - do { - if (!ida_pre_get(&hierarchy_ida, GFP_KERNEL)) - return -ENOMEM; - /* Try to allocate the next unused ID */ - ret = ida_get_new_above(&hierarchy_ida, next_hierarchy_id, - &root->hierarchy_id); - if (ret == -ENOSPC) - /* Try again starting from 0 */ - ret = ida_get_new(&hierarchy_ida, &root->hierarchy_id); - if (!ret) { - next_hierarchy_id = root->hierarchy_id + 1; - } else if (ret != -EAGAIN) { - /* Can only get here if the 31-bit IDR is full ... */ - BUG_ON(ret); - } - } while (ret); + id = idr_alloc_cyclic(&cgroup_hierarchy_idr, root, 2, 0, GFP_KERNEL); + if (id < 0) + return id; + + root->hierarchy_id = id; return 0; } @@ -1462,7 +1450,7 @@ static void cgroup_exit_root_id(struct cgroupfs_root *root) lockdep_assert_held(&cgroup_root_mutex); if (root->hierarchy_id) { - ida_remove(&hierarchy_ida, root->hierarchy_id); + idr_remove(&cgroup_hierarchy_idr, root->hierarchy_id); root->hierarchy_id = 0; } } -- cgit v1.2.3 From 857a2beb09ab83e9a8185821ae16db7dfbe8b837 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sun, 14 Apr 2013 20:50:08 -0700 Subject: cgroup: implement task_cgroup_path_from_hierarchy() kdbus folks want a sane way to determine the cgroup path that a given task belongs to on a given hierarchy, which is a reasonble thing to expect from cgroup core. Implement task_cgroup_path_from_hierarchy(). v2: Dropped unnecessary NULL check on the return value of task_cgroup_from_root() as suggested by Li Zefan. Signed-off-by: Tejun Heo Acked-by: Greg Kroah-Hartman Acked-by: Li Zefan Cc: Kay Sievers Cc: Lennart Poettering Cc: Daniel Mack --- include/linux/cgroup.h | 2 ++ kernel/cgroup.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 5047355b9a0f..383c630f36f9 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -542,6 +542,8 @@ int cgroup_is_removed(const struct cgroup *cgrp); bool cgroup_is_descendant(struct cgroup *cgrp, struct cgroup *ancestor); int cgroup_path(const struct cgroup *cgrp, char *buf, int buflen); +int task_cgroup_path_from_hierarchy(struct task_struct *task, int hierarchy_id, + char *buf, size_t buflen); int cgroup_task_count(const struct cgroup *cgrp); diff --git a/kernel/cgroup.c b/kernel/cgroup.c index dcb417c6c242..6b2b1d945df2 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -1827,6 +1827,38 @@ out: } EXPORT_SYMBOL_GPL(cgroup_path); +/** + * task_cgroup_path_from_hierarchy - cgroup path of a task on a hierarchy + * @task: target task + * @hierarchy_id: the hierarchy to look up @task's cgroup from + * @buf: the buffer to write the path into + * @buflen: the length of the buffer + * + * Determine @task's cgroup on the hierarchy specified by @hierarchy_id and + * copy its path into @buf. This function grabs cgroup_mutex and shouldn't + * be used inside locks used by cgroup controller callbacks. + */ +int task_cgroup_path_from_hierarchy(struct task_struct *task, int hierarchy_id, + char *buf, size_t buflen) +{ + struct cgroupfs_root *root; + struct cgroup *cgrp = NULL; + int ret = -ENOENT; + + mutex_lock(&cgroup_mutex); + + root = idr_find(&cgroup_hierarchy_idr, hierarchy_id); + if (root) { + cgrp = task_cgroup_from_root(task, root); + ret = cgroup_path(cgrp, buf, buflen); + } + + mutex_unlock(&cgroup_mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(task_cgroup_path_from_hierarchy); + /* * Control Group taskset */ -- cgit v1.2.3 From e628dc999e43a9dd51fb6bd810772c277f934484 Mon Sep 17 00:00:00 2001 From: David Milburn Date: Tue, 14 May 2013 13:48:40 -0500 Subject: libata: export ata_port port_no attribute via /sys While registering host controller track port number based upon number of ports available on the controller, export port_no attribute through /sys. This patch is needed by udev for composing persistent links in /dev/disk/by-path. /sys/devices/pci0000:00/0000:00:1f.2/ata8/ata_port/ata8 total 0 lrwxrwxrwx. 1 root root 0 Mar 6 12:43 device -> ../../../ata8 -r--r--r--. 1 root root 4096 Mar 6 12:43 idle_irq -r--r--r--. 1 root root 4096 Mar 6 12:43 nr_pmp_links -r--r--r--. 1 root root 4096 Mar 6 12:43 port_no drwxr-xr-x. 2 root root 0 Mar 6 12:42 power lrwxrwxrwx. 1 root root 0 Mar 6 12:41 subsystem -> ../../../../../../class/ata_port -rw-r--r--. 1 root root 4096 Mar 6 12:40 uevent 1 Signed-off-by: David Milburn Signed-off-by: Tejun Heo --- drivers/ata/libata-core.c | 6 ++++-- drivers/ata/libata-transport.c | 4 +++- include/linux/libata.h | 1 + 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 63c743baf920..5f7d5f9ee820 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -5636,6 +5636,7 @@ struct ata_port *ata_port_alloc(struct ata_host *host) ap->pflags |= ATA_PFLAG_INITIALIZING | ATA_PFLAG_FROZEN; ap->lock = &host->lock; ap->print_id = -1; + ap->local_port_no = -1; ap->host = host; ap->dev = host->dev; @@ -6126,9 +6127,10 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht) kfree(host->ports[i]); /* give ports names and add SCSI hosts */ - for (i = 0; i < host->n_ports; i++) + for (i = 0; i < host->n_ports; i++) { host->ports[i]->print_id = atomic_inc_return(&ata_print_id); - + host->ports[i]->local_port_no = i + 1; + } /* Create associated sysfs transport objects */ for (i = 0; i < host->n_ports; i++) { diff --git a/drivers/ata/libata-transport.c b/drivers/ata/libata-transport.c index c04d393d20c1..077a856f5fd0 100644 --- a/drivers/ata/libata-transport.c +++ b/drivers/ata/libata-transport.c @@ -37,7 +37,7 @@ #include "libata.h" #include "libata-transport.h" -#define ATA_PORT_ATTRS 2 +#define ATA_PORT_ATTRS 3 #define ATA_LINK_ATTRS 3 #define ATA_DEV_ATTRS 9 @@ -216,6 +216,7 @@ static DEVICE_ATTR(name, S_IRUGO, show_ata_port_##name, NULL) ata_port_simple_attr(nr_pmp_links, nr_pmp_links, "%d\n", int); ata_port_simple_attr(stats.idle_irq, idle_irq, "%ld\n", unsigned long); +ata_port_simple_attr(local_port_no, port_no, "%u\n", unsigned int); static DECLARE_TRANSPORT_CLASS(ata_port_class, "ata_port", NULL, NULL, NULL); @@ -709,6 +710,7 @@ struct scsi_transport_template *ata_attach_transport(void) count = 0; SETUP_PORT_ATTRIBUTE(nr_pmp_links); SETUP_PORT_ATTRIBUTE(idle_irq); + SETUP_PORT_ATTRIBUTE(port_no); BUG_ON(count > ATA_PORT_ATTRS); i->port_attrs[count] = NULL; diff --git a/include/linux/libata.h b/include/linux/libata.h index eae7a053dc51..47e029236f6e 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -746,6 +746,7 @@ struct ata_port { /* Flags that change dynamically, protected by ap->lock */ unsigned int pflags; /* ATA_PFLAG_xxx */ unsigned int print_id; /* user visible unique port ID */ + unsigned int local_port_no; /* host local port num */ unsigned int port_no; /* 0 based port no. inside the host */ #ifdef CONFIG_ATA_SFF -- cgit v1.2.3 From 23958e729e7029678e746bf8f4094c8863a79c3d Mon Sep 17 00:00:00 2001 From: Greg KH Date: Fri, 3 May 2013 16:26:59 -0700 Subject: cgroup.h: remove some functions that are now gone cgroup_lock() and cgroup_unlock() are now no longer exported, so fix cgroup.h to not declare them if CONFIG_CGROUPS is not enabled. Signed-off-by: Greg Kroah-Hartman Acked-by: Li Zefan Signed-off-by: Tejun Heo --- include/linux/cgroup.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 383c630f36f9..4f6f5138c340 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -840,8 +840,6 @@ static inline void cgroup_fork(struct task_struct *p) {} static inline void cgroup_post_fork(struct task_struct *p) {} static inline void cgroup_exit(struct task_struct *p, int callbacks) {} -static inline void cgroup_lock(void) {} -static inline void cgroup_unlock(void) {} static inline int cgroupstats_build(struct cgroupstats *stats, struct dentry *dentry) { -- cgit v1.2.3 From 2423c9c3f0ffffa8f87cbdafe9781273c5d1b6a2 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 14 May 2013 13:52:30 -0700 Subject: blkcg: fix error return path in blkg_create() In blkg_create(), after lookup of parent fails, the control jumps to error path with the error code encoded into @blkg. The error path doesn't use @blkg for the return value. It returns ERR_PTR(ret). Make lookup fail path set @ret instead of @blkg. Note that the parent lookup is guaranteed to succeed at that point and the condition check is purely for sanity and triggers WARN when fails. As such, I don't think it's necessary to mark it for -stable. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-cgroup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index e8918ffaf96d..7fc35f683c4e 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -238,7 +238,7 @@ static struct blkcg_gq *blkg_create(struct blkcg *blkcg, if (blkcg_parent(blkcg)) { blkg->parent = __blkg_lookup(blkcg_parent(blkcg), q, false); if (WARN_ON_ONCE(!blkg->parent)) { - blkg = ERR_PTR(-EINVAL); + ret = -EINVAL; goto err_put_css; } blkg_get(blkg->parent); -- cgit v1.2.3 From dd4a4ffc0a4723cd0bef065af549803d256e1f7e Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 14 May 2013 13:52:30 -0700 Subject: blkcg: move blkg_for_each_descendant_pre() to block/blk-cgroup.h blk-throttle hierarchy support will make use of it. Move blkg_for_each_descendant_pre() from block/blk-cgroup.c to block/blk-cgroup.h. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-cgroup.c | 24 ++---------------------- block/blk-cgroup.h | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index 7fc35f683c4e..b950306838ad 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -32,26 +32,6 @@ EXPORT_SYMBOL_GPL(blkcg_root); static struct blkcg_policy *blkcg_policy[BLKCG_MAX_POLS]; -static struct blkcg_gq *__blkg_lookup(struct blkcg *blkcg, - struct request_queue *q, bool update_hint); - -/** - * blkg_for_each_descendant_pre - pre-order walk of a blkg's descendants - * @d_blkg: loop cursor pointing to the current descendant - * @pos_cgrp: used for iteration - * @p_blkg: target blkg to walk descendants of - * - * Walk @c_blkg through the descendants of @p_blkg. Must be used with RCU - * read locked. If called under either blkcg or queue lock, the iteration - * is guaranteed to include all and only online blkgs. The caller may - * update @pos_cgrp by calling cgroup_rightmost_descendant() to skip - * subtree. - */ -#define blkg_for_each_descendant_pre(d_blkg, pos_cgrp, p_blkg) \ - cgroup_for_each_descendant_pre((pos_cgrp), (p_blkg)->blkcg->css.cgroup) \ - if (((d_blkg) = __blkg_lookup(cgroup_to_blkcg(pos_cgrp), \ - (p_blkg)->q, false))) - static bool blkcg_policy_enabled(struct request_queue *q, const struct blkcg_policy *pol) { @@ -158,8 +138,8 @@ err_free: * @q's bypass state. If @update_hint is %true, the caller should be * holding @q->queue_lock and lookup hint is updated on success. */ -static struct blkcg_gq *__blkg_lookup(struct blkcg *blkcg, - struct request_queue *q, bool update_hint) +struct blkcg_gq *__blkg_lookup(struct blkcg *blkcg, struct request_queue *q, + bool update_hint) { struct blkcg_gq *blkg; diff --git a/block/blk-cgroup.h b/block/blk-cgroup.h index 4e595ee8c915..11f5b92d9a47 100644 --- a/block/blk-cgroup.h +++ b/block/blk-cgroup.h @@ -282,6 +282,26 @@ static inline void blkg_put(struct blkcg_gq *blkg) __blkg_release(blkg); } +struct blkcg_gq *__blkg_lookup(struct blkcg *blkcg, struct request_queue *q, + bool update_hint); + +/** + * blkg_for_each_descendant_pre - pre-order walk of a blkg's descendants + * @d_blkg: loop cursor pointing to the current descendant + * @pos_cgrp: used for iteration + * @p_blkg: target blkg to walk descendants of + * + * Walk @c_blkg through the descendants of @p_blkg. Must be used with RCU + * read locked. If called under either blkcg or queue lock, the iteration + * is guaranteed to include all and only online blkgs. The caller may + * update @pos_cgrp by calling cgroup_rightmost_descendant() to skip + * subtree. + */ +#define blkg_for_each_descendant_pre(d_blkg, pos_cgrp, p_blkg) \ + cgroup_for_each_descendant_pre((pos_cgrp), (p_blkg)->blkcg->css.cgroup) \ + if (((d_blkg) = __blkg_lookup(cgroup_to_blkcg(pos_cgrp), \ + (p_blkg)->q, false))) + /** * blk_get_rl - get request_list to use * @q: request_queue of interest -- cgit v1.2.3 From aa539cb38f4f1365ad52fed6a857b25f8b4dc06c Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 14 May 2013 13:52:31 -0700 Subject: blkcg: implement blkg_for_each_descendant_post() This will be used by blk-throttle hierarchy support. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-cgroup.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/block/blk-cgroup.h b/block/blk-cgroup.h index 11f5b92d9a47..e15f731d2cdd 100644 --- a/block/blk-cgroup.h +++ b/block/blk-cgroup.h @@ -302,6 +302,20 @@ struct blkcg_gq *__blkg_lookup(struct blkcg *blkcg, struct request_queue *q, if (((d_blkg) = __blkg_lookup(cgroup_to_blkcg(pos_cgrp), \ (p_blkg)->q, false))) +/** + * blkg_for_each_descendant_post - post-order walk of a blkg's descendants + * @d_blkg: loop cursor pointing to the current descendant + * @pos_cgrp: used for iteration + * @p_blkg: target blkg to walk descendants of + * + * Similar to blkg_for_each_descendant_pre() but performs post-order + * traversal instead. Synchronization rules are the same. + */ +#define blkg_for_each_descendant_post(d_blkg, pos_cgrp, p_blkg) \ + cgroup_for_each_descendant_post((pos_cgrp), (p_blkg)->blkcg->css.cgroup) \ + if (((d_blkg) = __blkg_lookup(cgroup_to_blkcg(pos_cgrp), \ + (p_blkg)->q, false))) + /** * blk_get_rl - get request_list to use * @q: request_queue of interest -- cgit v1.2.3 From db61367038dcd222476881cb09fd54661b3cd508 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 14 May 2013 13:52:31 -0700 Subject: blkcg: invoke blkcg_policy->pd_init() after parent is linked Currently, when creating a new blkcg_gq, each policy's pd_init_fn() is invoked in blkg_alloc() before the parent is linked. This makes it difficult for policies to perform initializations which are dependent on the parent. This patch moves pd_init_fn() invocations to blkg_create() after the parent blkg is linked where the new blkg is fully initialized. As this means that blkg_free() can't assume that pd's are initialized, pd_exit_fn() invocations are moved to __blkg_release(). This guarantees that pd_exit_fn() is also invoked with fully initialized blkgs with valid parent pointers. This will help implementing hierarchy support in blk-throttle. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-cgroup.c | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index b950306838ad..6bbe0a939596 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -51,18 +51,8 @@ static void blkg_free(struct blkcg_gq *blkg) if (!blkg) return; - for (i = 0; i < BLKCG_MAX_POLS; i++) { - struct blkcg_policy *pol = blkcg_policy[i]; - struct blkg_policy_data *pd = blkg->pd[i]; - - if (!pd) - continue; - - if (pol && pol->pd_exit_fn) - pol->pd_exit_fn(blkg); - - kfree(pd); - } + for (i = 0; i < BLKCG_MAX_POLS; i++) + kfree(blkg->pd[i]); blk_exit_rl(&blkg->rl); kfree(blkg); @@ -114,10 +104,6 @@ static struct blkcg_gq *blkg_alloc(struct blkcg *blkcg, struct request_queue *q, blkg->pd[i] = pd; pd->blkg = blkg; pd->plid = i; - - /* invoke per-policy init */ - if (pol->pd_init_fn) - pol->pd_init_fn(blkg); } return blkg; @@ -214,7 +200,7 @@ static struct blkcg_gq *blkg_create(struct blkcg *blkcg, } blkg = new_blkg; - /* link parent and insert */ + /* link parent */ if (blkcg_parent(blkcg)) { blkg->parent = __blkg_lookup(blkcg_parent(blkcg), q, false); if (WARN_ON_ONCE(!blkg->parent)) { @@ -224,6 +210,15 @@ static struct blkcg_gq *blkg_create(struct blkcg *blkcg, blkg_get(blkg->parent); } + /* invoke per-policy init */ + for (i = 0; i < BLKCG_MAX_POLS; i++) { + struct blkcg_policy *pol = blkcg_policy[i]; + + if (blkg->pd[i] && pol->pd_init_fn) + pol->pd_init_fn(blkg); + } + + /* insert */ spin_lock(&blkcg->lock); ret = radix_tree_insert(&blkcg->blkg_tree, q->id, blkg); if (likely(!ret)) { @@ -381,6 +376,16 @@ static void blkg_rcu_free(struct rcu_head *rcu_head) void __blkg_release(struct blkcg_gq *blkg) { + int i; + + /* tell policies that this one is being freed */ + for (i = 0; i < BLKCG_MAX_POLS; i++) { + struct blkcg_policy *pol = blkcg_policy[i]; + + if (blkg->pd[i] && pol->pd_exit_fn) + pol->pd_exit_fn(blkg); + } + /* release the blkcg and parent blkg refs this blkg has been holding */ css_put(&blkg->blkcg->css); if (blkg->parent) -- cgit v1.2.3 From 2a4fd070ee8561d918a3776388331bb7e92ea59e Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 14 May 2013 13:52:31 -0700 Subject: blkcg: move bulk of blkcg_gq release operations to the RCU callback Currently, when the last reference of a blkcg_gq is put, all then release operations sans the actual freeing happen directly in blkg_put(). As blkg_put() may be called under queue_lock, all pd_exit_fn()s may be too. This makes it impossible for pd_exit_fn()s to use del_timer_sync() on timers which grab the queue_lock which is an irq-safe lock due to the deadlock possibility described in the comment on top of del_timer_sync(). This can be easily avoided by perfoming the release operations in the RCU callback instead of directly from blkg_put(). This patch moves the blkcg_gq release operations to the RCU callback. As this leaves __blkg_release() with only call_rcu() invocation, blkg_rcu_free() is renamed to __blkg_release_rcu(), exported and call_rcu() invocation is now done directly from blkg_put() instead of going through __blkg_release() which is removed. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-cgroup.c | 34 ++++++++++++++++------------------ block/blk-cgroup.h | 4 ++-- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index 6bbe0a939596..d0747605f56c 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -369,13 +369,17 @@ static void blkg_destroy_all(struct request_queue *q) q->root_rl.blkg = NULL; } -static void blkg_rcu_free(struct rcu_head *rcu_head) -{ - blkg_free(container_of(rcu_head, struct blkcg_gq, rcu_head)); -} - -void __blkg_release(struct blkcg_gq *blkg) +/* + * A group is RCU protected, but having an rcu lock does not mean that one + * can access all the fields of blkg and assume these are valid. For + * example, don't try to follow throtl_data and request queue links. + * + * Having a reference to blkg under an rcu allows accesses to only values + * local to groups like group stats and group rate limits. + */ +void __blkg_release_rcu(struct rcu_head *rcu_head) { + struct blkcg_gq *blkg = container_of(rcu_head, struct blkcg_gq, rcu_head); int i; /* tell policies that this one is being freed */ @@ -388,21 +392,15 @@ void __blkg_release(struct blkcg_gq *blkg) /* release the blkcg and parent blkg refs this blkg has been holding */ css_put(&blkg->blkcg->css); - if (blkg->parent) + if (blkg->parent) { + spin_lock_irq(blkg->q->queue_lock); blkg_put(blkg->parent); + spin_unlock_irq(blkg->q->queue_lock); + } - /* - * A group is freed in rcu manner. But having an rcu lock does not - * mean that one can access all the fields of blkg and assume these - * are valid. For example, don't try to follow throtl_data and - * request queue links. - * - * Having a reference to blkg under an rcu allows acess to only - * values local to groups like group stats and group rate limits - */ - call_rcu(&blkg->rcu_head, blkg_rcu_free); + blkg_free(blkg); } -EXPORT_SYMBOL_GPL(__blkg_release); +EXPORT_SYMBOL_GPL(__blkg_release_rcu); /* * The next function used by blk_queue_for_each_rl(). It's a bit tricky diff --git a/block/blk-cgroup.h b/block/blk-cgroup.h index e15f731d2cdd..8056c03a3382 100644 --- a/block/blk-cgroup.h +++ b/block/blk-cgroup.h @@ -266,7 +266,7 @@ static inline void blkg_get(struct blkcg_gq *blkg) blkg->refcnt++; } -void __blkg_release(struct blkcg_gq *blkg); +void __blkg_release_rcu(struct rcu_head *rcu); /** * blkg_put - put a blkg reference @@ -279,7 +279,7 @@ static inline void blkg_put(struct blkcg_gq *blkg) lockdep_assert_held(blkg->q->queue_lock); WARN_ON_ONCE(blkg->refcnt <= 0); if (!--blkg->refcnt) - __blkg_release(blkg); + call_rcu(&blkg->rcu_head, __blkg_release_rcu); } struct blkcg_gq *__blkg_lookup(struct blkcg *blkcg, struct request_queue *q, -- cgit v1.2.3 From 2db6314c213bb21102dd1dad06cfda6a8682d624 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 14 May 2013 13:52:31 -0700 Subject: blk-throttle: remove spurious throtl_enqueue_tg() call from throtl_select_dispatch() throtl_select_dispatch() calls throtl_enqueue_tg() right after tg_update_disptime(), which always calls the function anyway. The call is, while harmless, unnecessary. Remove it. This patch doesn't introduce any behavior difference. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-throttle.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/block/blk-throttle.c b/block/blk-throttle.c index 31146225f3d0..3960787358b6 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -816,10 +816,8 @@ static int throtl_select_dispatch(struct throtl_data *td, struct bio_list *bl) nr_disp += throtl_dispatch_tg(td, tg, bl); - if (tg->nr_queued[0] || tg->nr_queued[1]) { + if (tg->nr_queued[0] || tg->nr_queued[1]) tg_update_disptime(td, tg); - throtl_enqueue_tg(td, tg); - } if (nr_disp >= throtl_quantum) break; -- cgit v1.2.3 From 632b44935f4c99a61c56f8a6f805a1080ab5a432 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 14 May 2013 13:52:31 -0700 Subject: blk-throttle: remove deferred config application mechanism When bps or iops configuration changes, blk-throttle records the new configuration and sets a flag indicating that the config has changed. The flag is checked in the bio dispatch path and applied. This deferred config application was necessary due to limitations in blkcg framework, which haven't existed for quite a while now. This patch removes the deferred config application mechanism and applies new configurations directly from tg_set_conf(), which is simpler. v2: Dropped unnecessary throtl_schedule_delayed_work() call from tg_set_conf() as suggested by Vivek Goyal. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-throttle.c | 74 ++++++++++++++-------------------------------------- 1 file changed, 20 insertions(+), 54 deletions(-) diff --git a/block/blk-throttle.c b/block/blk-throttle.c index 3960787358b6..7dbd0e695df0 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -85,9 +85,6 @@ struct throtl_grp { unsigned long slice_start[2]; unsigned long slice_end[2]; - /* Some throttle limits got updated for the group */ - int limits_changed; - /* Per cpu stats pointer */ struct tg_stats_cpu __percpu *stats_cpu; @@ -112,8 +109,6 @@ struct throtl_data /* Work for dispatching throttled bios */ struct delayed_work throtl_work; - - int limits_changed; }; /* list and work item to allocate percpu group stats */ @@ -223,7 +218,6 @@ static void throtl_pd_init(struct blkcg_gq *blkg) RB_CLEAR_NODE(&tg->rb_node); bio_list_init(&tg->bio_lists[0]); bio_list_init(&tg->bio_lists[1]); - tg->limits_changed = false; tg->bps[READ] = -1; tg->bps[WRITE] = -1; @@ -826,45 +820,6 @@ static int throtl_select_dispatch(struct throtl_data *td, struct bio_list *bl) return nr_disp; } -static void throtl_process_limit_change(struct throtl_data *td) -{ - struct request_queue *q = td->queue; - struct blkcg_gq *blkg, *n; - - if (!td->limits_changed) - return; - - xchg(&td->limits_changed, false); - - throtl_log(td, "limits changed"); - - list_for_each_entry_safe(blkg, n, &q->blkg_list, q_node) { - struct throtl_grp *tg = blkg_to_tg(blkg); - - if (!tg->limits_changed) - continue; - - if (!xchg(&tg->limits_changed, false)) - continue; - - throtl_log_tg(td, tg, "limit change rbps=%llu wbps=%llu" - " riops=%u wiops=%u", tg->bps[READ], tg->bps[WRITE], - tg->iops[READ], tg->iops[WRITE]); - - /* - * Restart the slices for both READ and WRITES. It - * might happen that a group's limit are dropped - * suddenly and we don't want to account recently - * dispatched IO with new low rate - */ - throtl_start_new_slice(td, tg, 0); - throtl_start_new_slice(td, tg, 1); - - if (throtl_tg_on_rr(tg)) - tg_update_disptime(td, tg); - } -} - /* Dispatch throttled bios. Should be called without queue lock held. */ static int throtl_dispatch(struct request_queue *q) { @@ -876,8 +831,6 @@ static int throtl_dispatch(struct request_queue *q) spin_lock_irq(q->queue_lock); - throtl_process_limit_change(td); - if (!total_nr_queued(td)) goto out; @@ -925,8 +878,7 @@ throtl_schedule_delayed_work(struct throtl_data *td, unsigned long delay) struct delayed_work *dwork = &td->throtl_work; - /* schedule work if limits changed even if no bio is queued */ - if (total_nr_queued(td) || td->limits_changed) { + if (total_nr_queued(td)) { mod_delayed_work(kthrotld_workqueue, dwork, delay); throtl_log(td, "schedule work. delay=%lu jiffies=%lu", delay, jiffies); @@ -1023,10 +975,25 @@ static int tg_set_conf(struct cgroup *cgrp, struct cftype *cft, const char *buf, else *(unsigned int *)((void *)tg + cft->private) = ctx.v; - /* XXX: we don't need the following deferred processing */ - xchg(&tg->limits_changed, true); - xchg(&td->limits_changed, true); - throtl_schedule_delayed_work(td, 0); + throtl_log_tg(td, tg, "limit change rbps=%llu wbps=%llu riops=%u wiops=%u", + tg->bps[READ], tg->bps[WRITE], + tg->iops[READ], tg->iops[WRITE]); + + /* + * We're already holding queue_lock and know @tg is valid. Let's + * apply the new config directly. + * + * Restart the slices for both READ and WRITES. It might happen + * that a group's limit are dropped suddenly and we don't want to + * account recently dispatched IO with new low rate. + */ + throtl_start_new_slice(td, tg, 0); + throtl_start_new_slice(td, tg, 1); + + if (throtl_tg_on_rr(tg)) { + tg_update_disptime(td, tg); + throtl_schedule_next_dispatch(td); + } blkg_conf_finish(&ctx); return 0; @@ -1239,7 +1206,6 @@ int blk_throtl_init(struct request_queue *q) return -ENOMEM; td->tg_service_tree = THROTL_RB_ROOT; - td->limits_changed = false; INIT_DELAYED_WORK(&td->throtl_work, blk_throtl_work); q->td = td; -- cgit v1.2.3 From cb76199c36a7ccf0947ef4875b32e0940f50d1a8 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 14 May 2013 13:52:31 -0700 Subject: blk-throttle: collapse throtl_dispatch() into the work function blk-throttle is about to go through major restructuring to support hierarchy. Do cosmetic updates in preparation. * s/throtl_data->throtl_work/throtl_data->dispatch_work/ * s/blk_throtl_work()/blk_throtl_dispatch_work_fn()/ * Collapse throtl_dispatch() into blk_throtl_dispatch_work_fn() This patch is purely cosmetic. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-throttle.c | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/block/blk-throttle.c b/block/blk-throttle.c index 7dbd0e695df0..0a0bc00059df 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -108,7 +108,7 @@ struct throtl_data unsigned int nr_undestroyed_grps; /* Work for dispatching throttled bios */ - struct delayed_work throtl_work; + struct delayed_work dispatch_work; }; /* list and work item to allocate percpu group stats */ @@ -820,10 +820,12 @@ static int throtl_select_dispatch(struct throtl_data *td, struct bio_list *bl) return nr_disp; } -/* Dispatch throttled bios. Should be called without queue lock held. */ -static int throtl_dispatch(struct request_queue *q) +/* work function to dispatch throttled bios */ +void blk_throtl_dispatch_work_fn(struct work_struct *work) { - struct throtl_data *td = q->td; + struct throtl_data *td = container_of(to_delayed_work(work), + struct throtl_data, dispatch_work); + struct request_queue *q = td->queue; unsigned int nr_disp = 0; struct bio_list bio_list_on_stack; struct bio *bio; @@ -859,16 +861,6 @@ out: generic_make_request(bio); blk_finish_plug(&plug); } - return nr_disp; -} - -void blk_throtl_work(struct work_struct *work) -{ - struct throtl_data *td = container_of(work, struct throtl_data, - throtl_work.work); - struct request_queue *q = td->queue; - - throtl_dispatch(q); } /* Call with queue lock held */ @@ -876,7 +868,7 @@ static void throtl_schedule_delayed_work(struct throtl_data *td, unsigned long delay) { - struct delayed_work *dwork = &td->throtl_work; + struct delayed_work *dwork = &td->dispatch_work; if (total_nr_queued(td)) { mod_delayed_work(kthrotld_workqueue, dwork, delay); @@ -1057,7 +1049,7 @@ static void throtl_shutdown_wq(struct request_queue *q) { struct throtl_data *td = q->td; - cancel_delayed_work_sync(&td->throtl_work); + cancel_delayed_work_sync(&td->dispatch_work); } static struct blkcg_policy blkcg_policy_throtl = { @@ -1206,7 +1198,7 @@ int blk_throtl_init(struct request_queue *q) return -ENOMEM; td->tg_service_tree = THROTL_RB_ROOT; - INIT_DELAYED_WORK(&td->throtl_work, blk_throtl_work); + INIT_DELAYED_WORK(&td->dispatch_work, blk_throtl_dispatch_work_fn); q->td = td; td->queue = q; -- cgit v1.2.3 From a9131a27e2a3272df2207277a2be90377ce75fc6 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 14 May 2013 13:52:31 -0700 Subject: blk-throttle: relocate throtl_schedule_delayed_work() Move throtl_schedule_delayed_work() above its first user so that the forward declaration can be removed. This patch is pure relocaiton. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-throttle.c | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/block/blk-throttle.c b/block/blk-throttle.c index 0a0bc00059df..507b1c608941 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -25,8 +25,6 @@ static struct blkcg_policy blkcg_policy_throtl; /* A workqueue to queue throttle related work */ static struct workqueue_struct *kthrotld_workqueue; -static void throtl_schedule_delayed_work(struct throtl_data *td, - unsigned long delay); struct throtl_rb_root { struct rb_root rb; @@ -398,6 +396,19 @@ static void throtl_dequeue_tg(struct throtl_data *td, struct throtl_grp *tg) __throtl_dequeue_tg(td, tg); } +/* Call with queue lock held */ +static void throtl_schedule_delayed_work(struct throtl_data *td, + unsigned long delay) +{ + struct delayed_work *dwork = &td->dispatch_work; + + if (total_nr_queued(td)) { + mod_delayed_work(kthrotld_workqueue, dwork, delay); + throtl_log(td, "schedule work. delay=%lu jiffies=%lu", + delay, jiffies); + } +} + static void throtl_schedule_next_dispatch(struct throtl_data *td) { struct throtl_rb_root *st = &td->tg_service_tree; @@ -863,20 +874,6 @@ out: } } -/* Call with queue lock held */ -static void -throtl_schedule_delayed_work(struct throtl_data *td, unsigned long delay) -{ - - struct delayed_work *dwork = &td->dispatch_work; - - if (total_nr_queued(td)) { - mod_delayed_work(kthrotld_workqueue, dwork, delay); - throtl_log(td, "schedule work. delay=%lu jiffies=%lu", - delay, jiffies); - } -} - static u64 tg_prfill_cpu_rwstat(struct seq_file *sf, struct blkg_policy_data *pd, int off) { -- cgit v1.2.3 From 6a525600ffeb9e0d6cbbebda49eb89d6d3408c2b Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 14 May 2013 13:52:32 -0700 Subject: blk-throttle: remove pointless throtl_nr_queued() optimizations throtl_nr_queued() is used in several places to avoid performing certain operations when the throtl_data is empty. This usually is useless as those paths usually aren't traveled if there's no bio queued. * throtl_schedule_delayed_work() skips scheduling dispatch work item if @td doesn't have any bios queued; however, the only case it can be called when @td is empty is from tg_set_conf() which isn't something we should be optimizing for. * throtl_schedule_next_dispatch() takes a quick exit if @td is empty; however, right after that it triggers BUG if the service tree is empty. The two conditions are equivalent and it can just test @st->count for the quick exit. * blk_throtl_dispatch_work_fn() skips dispatch if @td is empty. This work function isn't usually invoked when @td is empty. The only possibility is from tg_set_conf() and when it happens the normal dispatching path can handle empty @td fine. No need to add special skip path. This patch removes the above three unnecessary optimizations, which leave throtl_log() call in blk_throtl_dispatch_work_fn() the only user of throtl_nr_queued(). Remove throtl_nr_queued() and open code it in throtl_log(). I don't think we need td->nr_queued[] at all. Maybe we can remove it later. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-throttle.c | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/block/blk-throttle.c b/block/blk-throttle.c index 507b1c608941..dbeef303f27b 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -166,11 +166,6 @@ THROTL_TG_FNS(on_rr); #define throtl_log(td, fmt, args...) \ blk_add_trace_msg((td)->queue, "throtl " fmt, ##args) -static inline unsigned int total_nr_queued(struct throtl_data *td) -{ - return td->nr_queued[0] + td->nr_queued[1]; -} - /* * Worker for allocating per cpu stat for tgs. This is scheduled on the * system_wq once there are some groups on the alloc_list waiting for @@ -402,25 +397,18 @@ static void throtl_schedule_delayed_work(struct throtl_data *td, { struct delayed_work *dwork = &td->dispatch_work; - if (total_nr_queued(td)) { - mod_delayed_work(kthrotld_workqueue, dwork, delay); - throtl_log(td, "schedule work. delay=%lu jiffies=%lu", - delay, jiffies); - } + mod_delayed_work(kthrotld_workqueue, dwork, delay); + throtl_log(td, "schedule work. delay=%lu jiffies=%lu", delay, jiffies); } static void throtl_schedule_next_dispatch(struct throtl_data *td) { struct throtl_rb_root *st = &td->tg_service_tree; - /* - * If there are more bios pending, schedule more work. - */ - if (!total_nr_queued(td)) + /* any pending children left? */ + if (!st->count) return; - BUG_ON(!st->count); - update_min_dispatch_time(st); if (time_before_eq(st->min_disptime, jiffies)) @@ -844,14 +832,11 @@ void blk_throtl_dispatch_work_fn(struct work_struct *work) spin_lock_irq(q->queue_lock); - if (!total_nr_queued(td)) - goto out; - bio_list_init(&bio_list_on_stack); throtl_log(td, "dispatch nr_queued=%u read=%u write=%u", - total_nr_queued(td), td->nr_queued[READ], - td->nr_queued[WRITE]); + td->nr_queued[READ] + td->nr_queued[WRITE], + td->nr_queued[READ], td->nr_queued[WRITE]); nr_disp = throtl_select_dispatch(td, &bio_list_on_stack); @@ -859,7 +844,7 @@ void blk_throtl_dispatch_work_fn(struct work_struct *work) throtl_log(td, "bios disp=%u", nr_disp); throtl_schedule_next_dispatch(td); -out: + spin_unlock_irq(q->queue_lock); /* -- cgit v1.2.3 From c9e0332e877c1a1ccfe4ba315a437c7a8cf6e575 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 14 May 2013 13:52:32 -0700 Subject: blk-throttle: rename throtl_rb_root to throtl_service_queue throtl_rb_root will be expanded to cover more roles for hierarchy support. Rename it to throtl_service_queue and make its fields more descriptive. * rb -> pending_tree * left -> first_pending * count -> nr_pending * min_disptime -> first_pending_disptime This patch is purely cosmetic. Signed-off-by: Tejun Heo --- block/blk-throttle.c | 84 ++++++++++++++++++++++++++-------------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/block/blk-throttle.c b/block/blk-throttle.c index dbeef303f27b..b279110ba287 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -26,15 +26,15 @@ static struct blkcg_policy blkcg_policy_throtl; /* A workqueue to queue throttle related work */ static struct workqueue_struct *kthrotld_workqueue; -struct throtl_rb_root { - struct rb_root rb; - struct rb_node *left; - unsigned int count; - unsigned long min_disptime; +struct throtl_service_queue { + struct rb_root pending_tree; /* RB tree of active tgs */ + struct rb_node *first_pending; /* first node in the tree */ + unsigned int nr_pending; /* # queued in the tree */ + unsigned long first_pending_disptime; /* disptime of the first tg */ }; -#define THROTL_RB_ROOT (struct throtl_rb_root) { .rb = RB_ROOT, .left = NULL, \ - .count = 0, .min_disptime = 0} +#define THROTL_SERVICE_QUEUE_INITIALIZER \ + (struct throtl_service_queue){ .pending_tree = RB_ROOT } #define rb_entry_tg(node) rb_entry((node), struct throtl_grp, rb_node) @@ -50,7 +50,7 @@ struct throtl_grp { /* must be the first member */ struct blkg_policy_data pd; - /* active throtl group service_tree member */ + /* active throtl group service_queue member */ struct rb_node rb_node; /* @@ -93,7 +93,7 @@ struct throtl_grp { struct throtl_data { /* service tree for active throtl groups */ - struct throtl_rb_root tg_service_tree; + struct throtl_service_queue service_queue; struct request_queue *queue; @@ -296,17 +296,17 @@ static struct throtl_grp *throtl_lookup_create_tg(struct throtl_data *td, return tg; } -static struct throtl_grp *throtl_rb_first(struct throtl_rb_root *root) +static struct throtl_grp *throtl_rb_first(struct throtl_service_queue *sq) { /* Service tree is empty */ - if (!root->count) + if (!sq->nr_pending) return NULL; - if (!root->left) - root->left = rb_first(&root->rb); + if (!sq->first_pending) + sq->first_pending = rb_first(&sq->pending_tree); - if (root->left) - return rb_entry_tg(root->left); + if (sq->first_pending) + return rb_entry_tg(sq->first_pending); return NULL; } @@ -317,29 +317,29 @@ static void rb_erase_init(struct rb_node *n, struct rb_root *root) RB_CLEAR_NODE(n); } -static void throtl_rb_erase(struct rb_node *n, struct throtl_rb_root *root) +static void throtl_rb_erase(struct rb_node *n, struct throtl_service_queue *sq) { - if (root->left == n) - root->left = NULL; - rb_erase_init(n, &root->rb); - --root->count; + if (sq->first_pending == n) + sq->first_pending = NULL; + rb_erase_init(n, &sq->pending_tree); + --sq->nr_pending; } -static void update_min_dispatch_time(struct throtl_rb_root *st) +static void update_min_dispatch_time(struct throtl_service_queue *sq) { struct throtl_grp *tg; - tg = throtl_rb_first(st); + tg = throtl_rb_first(sq); if (!tg) return; - st->min_disptime = tg->disptime; + sq->first_pending_disptime = tg->disptime; } -static void -tg_service_tree_add(struct throtl_rb_root *st, struct throtl_grp *tg) +static void tg_service_queue_add(struct throtl_service_queue *sq, + struct throtl_grp *tg) { - struct rb_node **node = &st->rb.rb_node; + struct rb_node **node = &sq->pending_tree.rb_node; struct rb_node *parent = NULL; struct throtl_grp *__tg; unsigned long key = tg->disptime; @@ -358,19 +358,19 @@ tg_service_tree_add(struct throtl_rb_root *st, struct throtl_grp *tg) } if (left) - st->left = &tg->rb_node; + sq->first_pending = &tg->rb_node; rb_link_node(&tg->rb_node, parent, node); - rb_insert_color(&tg->rb_node, &st->rb); + rb_insert_color(&tg->rb_node, &sq->pending_tree); } static void __throtl_enqueue_tg(struct throtl_data *td, struct throtl_grp *tg) { - struct throtl_rb_root *st = &td->tg_service_tree; + struct throtl_service_queue *sq = &td->service_queue; - tg_service_tree_add(st, tg); + tg_service_queue_add(sq, tg); throtl_mark_tg_on_rr(tg); - st->count++; + sq->nr_pending++; } static void throtl_enqueue_tg(struct throtl_data *td, struct throtl_grp *tg) @@ -381,7 +381,7 @@ static void throtl_enqueue_tg(struct throtl_data *td, struct throtl_grp *tg) static void __throtl_dequeue_tg(struct throtl_data *td, struct throtl_grp *tg) { - throtl_rb_erase(&tg->rb_node, &td->tg_service_tree); + throtl_rb_erase(&tg->rb_node, &td->service_queue); throtl_clear_tg_on_rr(tg); } @@ -403,18 +403,18 @@ static void throtl_schedule_delayed_work(struct throtl_data *td, static void throtl_schedule_next_dispatch(struct throtl_data *td) { - struct throtl_rb_root *st = &td->tg_service_tree; + struct throtl_service_queue *sq = &td->service_queue; /* any pending children left? */ - if (!st->count) + if (!sq->nr_pending) return; - update_min_dispatch_time(st); + update_min_dispatch_time(sq); - if (time_before_eq(st->min_disptime, jiffies)) + if (time_before_eq(sq->first_pending_disptime, jiffies)) throtl_schedule_delayed_work(td, 0); else - throtl_schedule_delayed_work(td, (st->min_disptime - jiffies)); + throtl_schedule_delayed_work(td, sq->first_pending_disptime - jiffies); } static inline void @@ -794,10 +794,10 @@ static int throtl_select_dispatch(struct throtl_data *td, struct bio_list *bl) { unsigned int nr_disp = 0; struct throtl_grp *tg; - struct throtl_rb_root *st = &td->tg_service_tree; + struct throtl_service_queue *sq = &td->service_queue; while (1) { - tg = throtl_rb_first(st); + tg = throtl_rb_first(sq); if (!tg) break; @@ -1145,7 +1145,7 @@ void blk_throtl_drain(struct request_queue *q) __releases(q->queue_lock) __acquires(q->queue_lock) { struct throtl_data *td = q->td; - struct throtl_rb_root *st = &td->tg_service_tree; + struct throtl_service_queue *sq = &td->service_queue; struct throtl_grp *tg; struct bio_list bl; struct bio *bio; @@ -1154,7 +1154,7 @@ void blk_throtl_drain(struct request_queue *q) bio_list_init(&bl); - while ((tg = throtl_rb_first(st))) { + while ((tg = throtl_rb_first(sq))) { throtl_dequeue_tg(td, tg); while ((bio = bio_list_peek(&tg->bio_lists[READ]))) @@ -1179,7 +1179,7 @@ int blk_throtl_init(struct request_queue *q) if (!td) return -ENOMEM; - td->tg_service_tree = THROTL_RB_ROOT; + td->service_queue = THROTL_SERVICE_QUEUE_INITIALIZER; INIT_DELAYED_WORK(&td->dispatch_work, blk_throtl_dispatch_work_fn); q->td = td; -- cgit v1.2.3 From 5b2c16aae0c074c3bb546c4c066ca7064684553c Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 14 May 2013 13:52:32 -0700 Subject: blk-throttle: simplify throtl_grp flag handling blk-throttle is still using function-defining macros to define flag handling functions, which went out style at least a decade ago. Just define the flag as bitmask and use direct bit operations. This patch doesn't make any functional changes. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-throttle.c | 34 +++++++++------------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/block/blk-throttle.c b/block/blk-throttle.c index b279110ba287..e8ef43d3fab3 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -36,6 +36,10 @@ struct throtl_service_queue { #define THROTL_SERVICE_QUEUE_INITIALIZER \ (struct throtl_service_queue){ .pending_tree = RB_ROOT } +enum tg_state_flags { + THROTL_TG_PENDING = 1 << 0, /* on parent's pending tree */ +}; + #define rb_entry_tg(node) rb_entry((node), struct throtl_grp, rb_node) /* Per-cpu group stats */ @@ -136,26 +140,6 @@ static inline struct throtl_grp *td_root_tg(struct throtl_data *td) return blkg_to_tg(td->queue->root_blkg); } -enum tg_state_flags { - THROTL_TG_FLAG_on_rr = 0, /* on round-robin busy list */ -}; - -#define THROTL_TG_FNS(name) \ -static inline void throtl_mark_tg_##name(struct throtl_grp *tg) \ -{ \ - (tg)->flags |= (1 << THROTL_TG_FLAG_##name); \ -} \ -static inline void throtl_clear_tg_##name(struct throtl_grp *tg) \ -{ \ - (tg)->flags &= ~(1 << THROTL_TG_FLAG_##name); \ -} \ -static inline int throtl_tg_##name(const struct throtl_grp *tg) \ -{ \ - return ((tg)->flags & (1 << THROTL_TG_FLAG_##name)) != 0; \ -} - -THROTL_TG_FNS(on_rr); - #define throtl_log_tg(td, tg, fmt, args...) do { \ char __pbuf[128]; \ \ @@ -369,25 +353,25 @@ static void __throtl_enqueue_tg(struct throtl_data *td, struct throtl_grp *tg) struct throtl_service_queue *sq = &td->service_queue; tg_service_queue_add(sq, tg); - throtl_mark_tg_on_rr(tg); + tg->flags |= THROTL_TG_PENDING; sq->nr_pending++; } static void throtl_enqueue_tg(struct throtl_data *td, struct throtl_grp *tg) { - if (!throtl_tg_on_rr(tg)) + if (!(tg->flags & THROTL_TG_PENDING)) __throtl_enqueue_tg(td, tg); } static void __throtl_dequeue_tg(struct throtl_data *td, struct throtl_grp *tg) { throtl_rb_erase(&tg->rb_node, &td->service_queue); - throtl_clear_tg_on_rr(tg); + tg->flags &= ~THROTL_TG_PENDING; } static void throtl_dequeue_tg(struct throtl_data *td, struct throtl_grp *tg) { - if (throtl_tg_on_rr(tg)) + if (tg->flags & THROTL_TG_PENDING) __throtl_dequeue_tg(td, tg); } @@ -964,7 +948,7 @@ static int tg_set_conf(struct cgroup *cgrp, struct cftype *cft, const char *buf, throtl_start_new_slice(td, tg, 0); throtl_start_new_slice(td, tg, 1); - if (throtl_tg_on_rr(tg)) { + if (tg->flags & THROTL_TG_PENDING) { tg_update_disptime(td, tg); throtl_schedule_next_dispatch(td); } -- cgit v1.2.3 From 0f3457f60edc57332bf6564fa00d561a4372dcb9 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 14 May 2013 13:52:32 -0700 Subject: blk-throttle: add backlink pointer from throtl_grp to throtl_data Add throtl_grp->td so that the td (throtl_data) a given tg (throtl_grp) belongs to can be determined, and remove @td argument from functions which take both @td and @tg as the former now can be determined from the latter. This generally simplifies the code and removes a number of cases where @td is passed as an argument without being actually used. This will also help hierarchy support implementation. While at it, in multi-line conditions, move the logical operators leading broken lines to the end of the previous line. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-throttle.c | 106 +++++++++++++++++++++++++-------------------------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/block/blk-throttle.c b/block/blk-throttle.c index e8ef43d3fab3..a489391f9153 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -57,6 +57,9 @@ struct throtl_grp { /* active throtl group service_queue member */ struct rb_node rb_node; + /* throtl_data this group belongs to */ + struct throtl_data *td; + /* * Dispatch time in jiffies. This is the estimated time when group * will unthrottle and is ready to dispatch more bio. It is used as @@ -140,11 +143,11 @@ static inline struct throtl_grp *td_root_tg(struct throtl_data *td) return blkg_to_tg(td->queue->root_blkg); } -#define throtl_log_tg(td, tg, fmt, args...) do { \ +#define throtl_log_tg(tg, fmt, args...) do { \ char __pbuf[128]; \ \ blkg_path(tg_to_blkg(tg), __pbuf, sizeof(__pbuf)); \ - blk_add_trace_msg((td)->queue, "throtl %s " fmt, __pbuf, ##args); \ + blk_add_trace_msg((tg)->td->queue, "throtl %s " fmt, __pbuf, ##args); \ } while (0) #define throtl_log(td, fmt, args...) \ @@ -193,6 +196,7 @@ static void throtl_pd_init(struct blkcg_gq *blkg) unsigned long flags; RB_CLEAR_NODE(&tg->rb_node); + tg->td = blkg->q->td; bio_list_init(&tg->bio_lists[0]); bio_list_init(&tg->bio_lists[1]); @@ -401,36 +405,34 @@ static void throtl_schedule_next_dispatch(struct throtl_data *td) throtl_schedule_delayed_work(td, sq->first_pending_disptime - jiffies); } -static inline void -throtl_start_new_slice(struct throtl_data *td, struct throtl_grp *tg, bool rw) +static inline void throtl_start_new_slice(struct throtl_grp *tg, bool rw) { tg->bytes_disp[rw] = 0; tg->io_disp[rw] = 0; tg->slice_start[rw] = jiffies; tg->slice_end[rw] = jiffies + throtl_slice; - throtl_log_tg(td, tg, "[%c] new slice start=%lu end=%lu jiffies=%lu", + throtl_log_tg(tg, "[%c] new slice start=%lu end=%lu jiffies=%lu", rw == READ ? 'R' : 'W', tg->slice_start[rw], tg->slice_end[rw], jiffies); } -static inline void throtl_set_slice_end(struct throtl_data *td, - struct throtl_grp *tg, bool rw, unsigned long jiffy_end) +static inline void throtl_set_slice_end(struct throtl_grp *tg, bool rw, + unsigned long jiffy_end) { tg->slice_end[rw] = roundup(jiffy_end, throtl_slice); } -static inline void throtl_extend_slice(struct throtl_data *td, - struct throtl_grp *tg, bool rw, unsigned long jiffy_end) +static inline void throtl_extend_slice(struct throtl_grp *tg, bool rw, + unsigned long jiffy_end) { tg->slice_end[rw] = roundup(jiffy_end, throtl_slice); - throtl_log_tg(td, tg, "[%c] extend slice start=%lu end=%lu jiffies=%lu", + throtl_log_tg(tg, "[%c] extend slice start=%lu end=%lu jiffies=%lu", rw == READ ? 'R' : 'W', tg->slice_start[rw], tg->slice_end[rw], jiffies); } /* Determine if previously allocated or extended slice is complete or not */ -static bool -throtl_slice_used(struct throtl_data *td, struct throtl_grp *tg, bool rw) +static bool throtl_slice_used(struct throtl_grp *tg, bool rw) { if (time_in_range(jiffies, tg->slice_start[rw], tg->slice_end[rw])) return 0; @@ -439,8 +441,7 @@ throtl_slice_used(struct throtl_data *td, struct throtl_grp *tg, bool rw) } /* Trim the used slices and adjust slice start accordingly */ -static inline void -throtl_trim_slice(struct throtl_data *td, struct throtl_grp *tg, bool rw) +static inline void throtl_trim_slice(struct throtl_grp *tg, bool rw) { unsigned long nr_slices, time_elapsed, io_trim; u64 bytes_trim, tmp; @@ -452,7 +453,7 @@ throtl_trim_slice(struct throtl_data *td, struct throtl_grp *tg, bool rw) * renewed. Don't try to trim the slice if slice is used. A new * slice will start when appropriate. */ - if (throtl_slice_used(td, tg, rw)) + if (throtl_slice_used(tg, rw)) return; /* @@ -463,7 +464,7 @@ throtl_trim_slice(struct throtl_data *td, struct throtl_grp *tg, bool rw) * is bad because it does not allow new slice to start. */ - throtl_set_slice_end(td, tg, rw, jiffies + throtl_slice); + throtl_set_slice_end(tg, rw, jiffies + throtl_slice); time_elapsed = jiffies - tg->slice_start[rw]; @@ -492,14 +493,14 @@ throtl_trim_slice(struct throtl_data *td, struct throtl_grp *tg, bool rw) tg->slice_start[rw] += nr_slices * throtl_slice; - throtl_log_tg(td, tg, "[%c] trim slice nr=%lu bytes=%llu io=%lu" + throtl_log_tg(tg, "[%c] trim slice nr=%lu bytes=%llu io=%lu" " start=%lu end=%lu jiffies=%lu", rw == READ ? 'R' : 'W', nr_slices, bytes_trim, io_trim, tg->slice_start[rw], tg->slice_end[rw], jiffies); } -static bool tg_with_in_iops_limit(struct throtl_data *td, struct throtl_grp *tg, - struct bio *bio, unsigned long *wait) +static bool tg_with_in_iops_limit(struct throtl_grp *tg, struct bio *bio, + unsigned long *wait) { bool rw = bio_data_dir(bio); unsigned int io_allowed; @@ -548,8 +549,8 @@ static bool tg_with_in_iops_limit(struct throtl_data *td, struct throtl_grp *tg, return 0; } -static bool tg_with_in_bps_limit(struct throtl_data *td, struct throtl_grp *tg, - struct bio *bio, unsigned long *wait) +static bool tg_with_in_bps_limit(struct throtl_grp *tg, struct bio *bio, + unsigned long *wait) { bool rw = bio_data_dir(bio); u64 bytes_allowed, extra_bytes, tmp; @@ -600,8 +601,8 @@ static bool tg_no_rule_group(struct throtl_grp *tg, bool rw) { * Returns whether one can dispatch a bio or not. Also returns approx number * of jiffies to wait before this bio is with-in IO rate and can be dispatched */ -static bool tg_may_dispatch(struct throtl_data *td, struct throtl_grp *tg, - struct bio *bio, unsigned long *wait) +static bool tg_may_dispatch(struct throtl_grp *tg, struct bio *bio, + unsigned long *wait) { bool rw = bio_data_dir(bio); unsigned long bps_wait = 0, iops_wait = 0, max_wait = 0; @@ -626,15 +627,15 @@ static bool tg_may_dispatch(struct throtl_data *td, struct throtl_grp *tg, * existing slice to make sure it is at least throtl_slice interval * long since now. */ - if (throtl_slice_used(td, tg, rw)) - throtl_start_new_slice(td, tg, rw); + if (throtl_slice_used(tg, rw)) + throtl_start_new_slice(tg, rw); else { if (time_before(tg->slice_end[rw], jiffies + throtl_slice)) - throtl_extend_slice(td, tg, rw, jiffies + throtl_slice); + throtl_extend_slice(tg, rw, jiffies + throtl_slice); } - if (tg_with_in_bps_limit(td, tg, bio, &bps_wait) - && tg_with_in_iops_limit(td, tg, bio, &iops_wait)) { + if (tg_with_in_bps_limit(tg, bio, &bps_wait) && + tg_with_in_iops_limit(tg, bio, &iops_wait)) { if (wait) *wait = 0; return 1; @@ -646,7 +647,7 @@ static bool tg_may_dispatch(struct throtl_data *td, struct throtl_grp *tg, *wait = max_wait; if (time_before(tg->slice_end[rw], jiffies + max_wait)) - throtl_extend_slice(td, tg, rw, jiffies + max_wait); + throtl_extend_slice(tg, rw, jiffies + max_wait); return 0; } @@ -707,10 +708,10 @@ static void tg_update_disptime(struct throtl_data *td, struct throtl_grp *tg) struct bio *bio; if ((bio = bio_list_peek(&tg->bio_lists[READ]))) - tg_may_dispatch(td, tg, bio, &read_wait); + tg_may_dispatch(tg, bio, &read_wait); if ((bio = bio_list_peek(&tg->bio_lists[WRITE]))) - tg_may_dispatch(td, tg, bio, &write_wait); + tg_may_dispatch(tg, bio, &write_wait); min_wait = min(read_wait, write_wait); disptime = jiffies + min_wait; @@ -721,8 +722,8 @@ static void tg_update_disptime(struct throtl_data *td, struct throtl_grp *tg) throtl_enqueue_tg(td, tg); } -static void tg_dispatch_one_bio(struct throtl_data *td, struct throtl_grp *tg, - bool rw, struct bio_list *bl) +static void tg_dispatch_one_bio(struct throtl_grp *tg, bool rw, + struct bio_list *bl) { struct bio *bio; @@ -731,18 +732,17 @@ static void tg_dispatch_one_bio(struct throtl_data *td, struct throtl_grp *tg, /* Drop bio reference on blkg */ blkg_put(tg_to_blkg(tg)); - BUG_ON(td->nr_queued[rw] <= 0); - td->nr_queued[rw]--; + BUG_ON(tg->td->nr_queued[rw] <= 0); + tg->td->nr_queued[rw]--; throtl_charge_bio(tg, bio); bio_list_add(bl, bio); bio->bi_rw |= REQ_THROTTLED; - throtl_trim_slice(td, tg, rw); + throtl_trim_slice(tg, rw); } -static int throtl_dispatch_tg(struct throtl_data *td, struct throtl_grp *tg, - struct bio_list *bl) +static int throtl_dispatch_tg(struct throtl_grp *tg, struct bio_list *bl) { unsigned int nr_reads = 0, nr_writes = 0; unsigned int max_nr_reads = throtl_grp_quantum*3/4; @@ -751,20 +751,20 @@ static int throtl_dispatch_tg(struct throtl_data *td, struct throtl_grp *tg, /* Try to dispatch 75% READS and 25% WRITES */ - while ((bio = bio_list_peek(&tg->bio_lists[READ])) - && tg_may_dispatch(td, tg, bio, NULL)) { + while ((bio = bio_list_peek(&tg->bio_lists[READ])) && + tg_may_dispatch(tg, bio, NULL)) { - tg_dispatch_one_bio(td, tg, bio_data_dir(bio), bl); + tg_dispatch_one_bio(tg, bio_data_dir(bio), bl); nr_reads++; if (nr_reads >= max_nr_reads) break; } - while ((bio = bio_list_peek(&tg->bio_lists[WRITE])) - && tg_may_dispatch(td, tg, bio, NULL)) { + while ((bio = bio_list_peek(&tg->bio_lists[WRITE])) && + tg_may_dispatch(tg, bio, NULL)) { - tg_dispatch_one_bio(td, tg, bio_data_dir(bio), bl); + tg_dispatch_one_bio(tg, bio_data_dir(bio), bl); nr_writes++; if (nr_writes >= max_nr_writes) @@ -791,7 +791,7 @@ static int throtl_select_dispatch(struct throtl_data *td, struct bio_list *bl) throtl_dequeue_tg(td, tg); - nr_disp += throtl_dispatch_tg(td, tg, bl); + nr_disp += throtl_dispatch_tg(tg, bl); if (tg->nr_queued[0] || tg->nr_queued[1]) tg_update_disptime(td, tg); @@ -933,7 +933,7 @@ static int tg_set_conf(struct cgroup *cgrp, struct cftype *cft, const char *buf, else *(unsigned int *)((void *)tg + cft->private) = ctx.v; - throtl_log_tg(td, tg, "limit change rbps=%llu wbps=%llu riops=%u wiops=%u", + throtl_log_tg(tg, "limit change rbps=%llu wbps=%llu riops=%u wiops=%u", tg->bps[READ], tg->bps[WRITE], tg->iops[READ], tg->iops[WRITE]); @@ -945,8 +945,8 @@ static int tg_set_conf(struct cgroup *cgrp, struct cftype *cft, const char *buf, * that a group's limit are dropped suddenly and we don't want to * account recently dispatched IO with new low rate. */ - throtl_start_new_slice(td, tg, 0); - throtl_start_new_slice(td, tg, 1); + throtl_start_new_slice(tg, 0); + throtl_start_new_slice(tg, 1); if (tg->flags & THROTL_TG_PENDING) { tg_update_disptime(td, tg); @@ -1076,7 +1076,7 @@ bool blk_throtl_bio(struct request_queue *q, struct bio *bio) } /* Bio is with-in rate limit of group */ - if (tg_may_dispatch(td, tg, bio, NULL)) { + if (tg_may_dispatch(tg, bio, NULL)) { throtl_charge_bio(tg, bio); /* @@ -1090,12 +1090,12 @@ bool blk_throtl_bio(struct request_queue *q, struct bio *bio) * * So keep on trimming slice even if bio is not queued. */ - throtl_trim_slice(td, tg, rw); + throtl_trim_slice(tg, rw); goto out_unlock; } queue_bio: - throtl_log_tg(td, tg, "[%c] bio. bdisp=%llu sz=%u bps=%llu" + throtl_log_tg(tg, "[%c] bio. bdisp=%llu sz=%u bps=%llu" " iodisp=%u iops=%u queued=%d/%d", rw == READ ? 'R' : 'W', tg->bytes_disp[rw], bio->bi_size, tg->bps[rw], @@ -1142,9 +1142,9 @@ void blk_throtl_drain(struct request_queue *q) throtl_dequeue_tg(td, tg); while ((bio = bio_list_peek(&tg->bio_lists[READ]))) - tg_dispatch_one_bio(td, tg, bio_data_dir(bio), &bl); + tg_dispatch_one_bio(tg, bio_data_dir(bio), &bl); while ((bio = bio_list_peek(&tg->bio_lists[WRITE]))) - tg_dispatch_one_bio(td, tg, bio_data_dir(bio), &bl); + tg_dispatch_one_bio(tg, bio_data_dir(bio), &bl); } spin_unlock_irq(q->queue_lock); -- cgit v1.2.3 From e2d57e60195a65e2f161ac1229ec9c91935e0240 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 14 May 2013 13:52:33 -0700 Subject: blk-throttle: pass around throtl_service_queue instead of throtl_data throtl_service_queue will be used as the basic block to implement hierarchy support. Pass around throtl_service_queue *sq instead of throtl_data *td in the following functions which will be used across multiple levels of hierarchy. * [__]throtl_enqueue/dequeue_tg() * throtl_add_bio_tg() * tg_update_disptime() * throtl_select_dispatch() Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-throttle.c | 53 +++++++++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/block/blk-throttle.c b/block/blk-throttle.c index a489391f9153..9660ec8d0375 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -352,31 +352,33 @@ static void tg_service_queue_add(struct throtl_service_queue *sq, rb_insert_color(&tg->rb_node, &sq->pending_tree); } -static void __throtl_enqueue_tg(struct throtl_data *td, struct throtl_grp *tg) +static void __throtl_enqueue_tg(struct throtl_service_queue *sq, + struct throtl_grp *tg) { - struct throtl_service_queue *sq = &td->service_queue; - tg_service_queue_add(sq, tg); tg->flags |= THROTL_TG_PENDING; sq->nr_pending++; } -static void throtl_enqueue_tg(struct throtl_data *td, struct throtl_grp *tg) +static void throtl_enqueue_tg(struct throtl_service_queue *sq, + struct throtl_grp *tg) { if (!(tg->flags & THROTL_TG_PENDING)) - __throtl_enqueue_tg(td, tg); + __throtl_enqueue_tg(sq, tg); } -static void __throtl_dequeue_tg(struct throtl_data *td, struct throtl_grp *tg) +static void __throtl_dequeue_tg(struct throtl_service_queue *sq, + struct throtl_grp *tg) { - throtl_rb_erase(&tg->rb_node, &td->service_queue); + throtl_rb_erase(&tg->rb_node, sq); tg->flags &= ~THROTL_TG_PENDING; } -static void throtl_dequeue_tg(struct throtl_data *td, struct throtl_grp *tg) +static void throtl_dequeue_tg(struct throtl_service_queue *sq, + struct throtl_grp *tg) { if (tg->flags & THROTL_TG_PENDING) - __throtl_dequeue_tg(td, tg); + __throtl_dequeue_tg(sq, tg); } /* Call with queue lock held */ @@ -689,8 +691,8 @@ static void throtl_charge_bio(struct throtl_grp *tg, struct bio *bio) throtl_update_dispatch_stats(tg_to_blkg(tg), bio->bi_size, bio->bi_rw); } -static void throtl_add_bio_tg(struct throtl_data *td, struct throtl_grp *tg, - struct bio *bio) +static void throtl_add_bio_tg(struct throtl_service_queue *sq, + struct throtl_grp *tg, struct bio *bio) { bool rw = bio_data_dir(bio); @@ -698,11 +700,12 @@ static void throtl_add_bio_tg(struct throtl_data *td, struct throtl_grp *tg, /* Take a bio reference on tg */ blkg_get(tg_to_blkg(tg)); tg->nr_queued[rw]++; - td->nr_queued[rw]++; - throtl_enqueue_tg(td, tg); + tg->td->nr_queued[rw]++; + throtl_enqueue_tg(sq, tg); } -static void tg_update_disptime(struct throtl_data *td, struct throtl_grp *tg) +static void tg_update_disptime(struct throtl_service_queue *sq, + struct throtl_grp *tg) { unsigned long read_wait = -1, write_wait = -1, min_wait = -1, disptime; struct bio *bio; @@ -717,9 +720,9 @@ static void tg_update_disptime(struct throtl_data *td, struct throtl_grp *tg) disptime = jiffies + min_wait; /* Update dispatch time */ - throtl_dequeue_tg(td, tg); + throtl_dequeue_tg(sq, tg); tg->disptime = disptime; - throtl_enqueue_tg(td, tg); + throtl_enqueue_tg(sq, tg); } static void tg_dispatch_one_bio(struct throtl_grp *tg, bool rw, @@ -774,11 +777,11 @@ static int throtl_dispatch_tg(struct throtl_grp *tg, struct bio_list *bl) return nr_reads + nr_writes; } -static int throtl_select_dispatch(struct throtl_data *td, struct bio_list *bl) +static int throtl_select_dispatch(struct throtl_service_queue *sq, + struct bio_list *bl) { unsigned int nr_disp = 0; struct throtl_grp *tg; - struct throtl_service_queue *sq = &td->service_queue; while (1) { tg = throtl_rb_first(sq); @@ -789,12 +792,12 @@ static int throtl_select_dispatch(struct throtl_data *td, struct bio_list *bl) if (time_before(jiffies, tg->disptime)) break; - throtl_dequeue_tg(td, tg); + throtl_dequeue_tg(sq, tg); nr_disp += throtl_dispatch_tg(tg, bl); if (tg->nr_queued[0] || tg->nr_queued[1]) - tg_update_disptime(td, tg); + tg_update_disptime(sq, tg); if (nr_disp >= throtl_quantum) break; @@ -822,7 +825,7 @@ void blk_throtl_dispatch_work_fn(struct work_struct *work) td->nr_queued[READ] + td->nr_queued[WRITE], td->nr_queued[READ], td->nr_queued[WRITE]); - nr_disp = throtl_select_dispatch(td, &bio_list_on_stack); + nr_disp = throtl_select_dispatch(&td->service_queue, &bio_list_on_stack); if (nr_disp) throtl_log(td, "bios disp=%u", nr_disp); @@ -949,7 +952,7 @@ static int tg_set_conf(struct cgroup *cgrp, struct cftype *cft, const char *buf, throtl_start_new_slice(tg, 1); if (tg->flags & THROTL_TG_PENDING) { - tg_update_disptime(td, tg); + tg_update_disptime(&td->service_queue, tg); throtl_schedule_next_dispatch(td); } @@ -1103,11 +1106,11 @@ queue_bio: tg->nr_queued[READ], tg->nr_queued[WRITE]); bio_associate_current(bio); - throtl_add_bio_tg(q->td, tg, bio); + throtl_add_bio_tg(&q->td->service_queue, tg, bio); throttled = true; if (update_disptime) { - tg_update_disptime(td, tg); + tg_update_disptime(&td->service_queue, tg); throtl_schedule_next_dispatch(td); } @@ -1139,7 +1142,7 @@ void blk_throtl_drain(struct request_queue *q) bio_list_init(&bl); while ((tg = throtl_rb_first(sq))) { - throtl_dequeue_tg(td, tg); + throtl_dequeue_tg(sq, tg); while ((bio = bio_list_peek(&tg->bio_lists[READ]))) tg_dispatch_one_bio(tg, bio_data_dir(bio), &bl); -- cgit v1.2.3 From 0049af73bb4b74d1407db59caefc5fe057ee434a Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 14 May 2013 13:52:33 -0700 Subject: blk-throttle: reorganize throtl_service_queue passed around as argument throtl_service_queue will be the building block of hierarchy support and will form a tree. This patch updates its usages as arguments to reduce confusion. * When a service queue is used as the parent role - the host of the rbtree - use @parent_sq instead of @sq. * For functions taking both @tg and @parent_sq, reorder them so that the order is (@tg, @parent_sq) not the other way around. This makes the code follow the usual convention of specifying the primary target of the operation as the first argument. This patch doesn't make any functional differences. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-throttle.c | 100 ++++++++++++++++++++++++++------------------------- 1 file changed, 51 insertions(+), 49 deletions(-) diff --git a/block/blk-throttle.c b/block/blk-throttle.c index 9660ec8d0375..ebaaaa9f57d6 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -284,17 +284,18 @@ static struct throtl_grp *throtl_lookup_create_tg(struct throtl_data *td, return tg; } -static struct throtl_grp *throtl_rb_first(struct throtl_service_queue *sq) +static struct throtl_grp * +throtl_rb_first(struct throtl_service_queue *parent_sq) { /* Service tree is empty */ - if (!sq->nr_pending) + if (!parent_sq->nr_pending) return NULL; - if (!sq->first_pending) - sq->first_pending = rb_first(&sq->pending_tree); + if (!parent_sq->first_pending) + parent_sq->first_pending = rb_first(&parent_sq->pending_tree); - if (sq->first_pending) - return rb_entry_tg(sq->first_pending); + if (parent_sq->first_pending) + return rb_entry_tg(parent_sq->first_pending); return NULL; } @@ -305,29 +306,30 @@ static void rb_erase_init(struct rb_node *n, struct rb_root *root) RB_CLEAR_NODE(n); } -static void throtl_rb_erase(struct rb_node *n, struct throtl_service_queue *sq) +static void throtl_rb_erase(struct rb_node *n, + struct throtl_service_queue *parent_sq) { - if (sq->first_pending == n) - sq->first_pending = NULL; - rb_erase_init(n, &sq->pending_tree); - --sq->nr_pending; + if (parent_sq->first_pending == n) + parent_sq->first_pending = NULL; + rb_erase_init(n, &parent_sq->pending_tree); + --parent_sq->nr_pending; } -static void update_min_dispatch_time(struct throtl_service_queue *sq) +static void update_min_dispatch_time(struct throtl_service_queue *parent_sq) { struct throtl_grp *tg; - tg = throtl_rb_first(sq); + tg = throtl_rb_first(parent_sq); if (!tg) return; - sq->first_pending_disptime = tg->disptime; + parent_sq->first_pending_disptime = tg->disptime; } -static void tg_service_queue_add(struct throtl_service_queue *sq, - struct throtl_grp *tg) +static void tg_service_queue_add(struct throtl_grp *tg, + struct throtl_service_queue *parent_sq) { - struct rb_node **node = &sq->pending_tree.rb_node; + struct rb_node **node = &parent_sq->pending_tree.rb_node; struct rb_node *parent = NULL; struct throtl_grp *__tg; unsigned long key = tg->disptime; @@ -346,39 +348,39 @@ static void tg_service_queue_add(struct throtl_service_queue *sq, } if (left) - sq->first_pending = &tg->rb_node; + parent_sq->first_pending = &tg->rb_node; rb_link_node(&tg->rb_node, parent, node); - rb_insert_color(&tg->rb_node, &sq->pending_tree); + rb_insert_color(&tg->rb_node, &parent_sq->pending_tree); } -static void __throtl_enqueue_tg(struct throtl_service_queue *sq, - struct throtl_grp *tg) +static void __throtl_enqueue_tg(struct throtl_grp *tg, + struct throtl_service_queue *parent_sq) { - tg_service_queue_add(sq, tg); + tg_service_queue_add(tg, parent_sq); tg->flags |= THROTL_TG_PENDING; - sq->nr_pending++; + parent_sq->nr_pending++; } -static void throtl_enqueue_tg(struct throtl_service_queue *sq, - struct throtl_grp *tg) +static void throtl_enqueue_tg(struct throtl_grp *tg, + struct throtl_service_queue *parent_sq) { if (!(tg->flags & THROTL_TG_PENDING)) - __throtl_enqueue_tg(sq, tg); + __throtl_enqueue_tg(tg, parent_sq); } -static void __throtl_dequeue_tg(struct throtl_service_queue *sq, - struct throtl_grp *tg) +static void __throtl_dequeue_tg(struct throtl_grp *tg, + struct throtl_service_queue *parent_sq) { - throtl_rb_erase(&tg->rb_node, sq); + throtl_rb_erase(&tg->rb_node, parent_sq); tg->flags &= ~THROTL_TG_PENDING; } -static void throtl_dequeue_tg(struct throtl_service_queue *sq, - struct throtl_grp *tg) +static void throtl_dequeue_tg(struct throtl_grp *tg, + struct throtl_service_queue *parent_sq) { if (tg->flags & THROTL_TG_PENDING) - __throtl_dequeue_tg(sq, tg); + __throtl_dequeue_tg(tg, parent_sq); } /* Call with queue lock held */ @@ -691,8 +693,8 @@ static void throtl_charge_bio(struct throtl_grp *tg, struct bio *bio) throtl_update_dispatch_stats(tg_to_blkg(tg), bio->bi_size, bio->bi_rw); } -static void throtl_add_bio_tg(struct throtl_service_queue *sq, - struct throtl_grp *tg, struct bio *bio) +static void throtl_add_bio_tg(struct bio *bio, struct throtl_grp *tg, + struct throtl_service_queue *parent_sq) { bool rw = bio_data_dir(bio); @@ -701,11 +703,11 @@ static void throtl_add_bio_tg(struct throtl_service_queue *sq, blkg_get(tg_to_blkg(tg)); tg->nr_queued[rw]++; tg->td->nr_queued[rw]++; - throtl_enqueue_tg(sq, tg); + throtl_enqueue_tg(tg, parent_sq); } -static void tg_update_disptime(struct throtl_service_queue *sq, - struct throtl_grp *tg) +static void tg_update_disptime(struct throtl_grp *tg, + struct throtl_service_queue *parent_sq) { unsigned long read_wait = -1, write_wait = -1, min_wait = -1, disptime; struct bio *bio; @@ -720,9 +722,9 @@ static void tg_update_disptime(struct throtl_service_queue *sq, disptime = jiffies + min_wait; /* Update dispatch time */ - throtl_dequeue_tg(sq, tg); + throtl_dequeue_tg(tg, parent_sq); tg->disptime = disptime; - throtl_enqueue_tg(sq, tg); + throtl_enqueue_tg(tg, parent_sq); } static void tg_dispatch_one_bio(struct throtl_grp *tg, bool rw, @@ -777,14 +779,14 @@ static int throtl_dispatch_tg(struct throtl_grp *tg, struct bio_list *bl) return nr_reads + nr_writes; } -static int throtl_select_dispatch(struct throtl_service_queue *sq, +static int throtl_select_dispatch(struct throtl_service_queue *parent_sq, struct bio_list *bl) { unsigned int nr_disp = 0; struct throtl_grp *tg; while (1) { - tg = throtl_rb_first(sq); + tg = throtl_rb_first(parent_sq); if (!tg) break; @@ -792,12 +794,12 @@ static int throtl_select_dispatch(struct throtl_service_queue *sq, if (time_before(jiffies, tg->disptime)) break; - throtl_dequeue_tg(sq, tg); + throtl_dequeue_tg(tg, parent_sq); nr_disp += throtl_dispatch_tg(tg, bl); if (tg->nr_queued[0] || tg->nr_queued[1]) - tg_update_disptime(sq, tg); + tg_update_disptime(tg, parent_sq); if (nr_disp >= throtl_quantum) break; @@ -952,7 +954,7 @@ static int tg_set_conf(struct cgroup *cgrp, struct cftype *cft, const char *buf, throtl_start_new_slice(tg, 1); if (tg->flags & THROTL_TG_PENDING) { - tg_update_disptime(&td->service_queue, tg); + tg_update_disptime(tg, &td->service_queue); throtl_schedule_next_dispatch(td); } @@ -1106,11 +1108,11 @@ queue_bio: tg->nr_queued[READ], tg->nr_queued[WRITE]); bio_associate_current(bio); - throtl_add_bio_tg(&q->td->service_queue, tg, bio); + throtl_add_bio_tg(bio, tg, &q->td->service_queue); throttled = true; if (update_disptime) { - tg_update_disptime(&td->service_queue, tg); + tg_update_disptime(tg, &td->service_queue); throtl_schedule_next_dispatch(td); } @@ -1132,7 +1134,7 @@ void blk_throtl_drain(struct request_queue *q) __releases(q->queue_lock) __acquires(q->queue_lock) { struct throtl_data *td = q->td; - struct throtl_service_queue *sq = &td->service_queue; + struct throtl_service_queue *parent_sq = &td->service_queue; struct throtl_grp *tg; struct bio_list bl; struct bio *bio; @@ -1141,8 +1143,8 @@ void blk_throtl_drain(struct request_queue *q) bio_list_init(&bl); - while ((tg = throtl_rb_first(sq))) { - throtl_dequeue_tg(sq, tg); + while ((tg = throtl_rb_first(parent_sq))) { + throtl_dequeue_tg(tg, parent_sq); while ((bio = bio_list_peek(&tg->bio_lists[READ]))) tg_dispatch_one_bio(tg, bio_data_dir(bio), &bl); -- cgit v1.2.3 From 49a2f1e3f231f6b2ccfc8192f4c395de7fa910a1 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 14 May 2013 13:52:34 -0700 Subject: blk-throttle: add throtl_grp->service_queue Currently, there's single service_queue per queue - throtl_data->service_queue. All active throtl_grp's are queued on the queue and dispatched according to their limits. To support hierarchy, this will be expanded such that active throtl_grp's form a tree anchored at throtl_data->service_queue and chained through each intermediate throtl_grp's service_queue. This patch adds throtl_grp->service_queue to prepare for hierarchy support. The initialization function - throtl_service_queue_init() - is added and replaces the macro initializer. The newly added tg->service_queue isn't used yet. Following patches will do. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-throttle.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/block/blk-throttle.c b/block/blk-throttle.c index ebaaaa9f57d6..7340440ccfb5 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -33,9 +33,6 @@ struct throtl_service_queue { unsigned long first_pending_disptime; /* disptime of the first tg */ }; -#define THROTL_SERVICE_QUEUE_INITIALIZER \ - (struct throtl_service_queue){ .pending_tree = RB_ROOT } - enum tg_state_flags { THROTL_TG_PENDING = 1 << 0, /* on parent's pending tree */ }; @@ -60,6 +57,9 @@ struct throtl_grp { /* throtl_data this group belongs to */ struct throtl_data *td; + /* this group's service queue */ + struct throtl_service_queue service_queue; + /* * Dispatch time in jiffies. This is the estimated time when group * will unthrottle and is ready to dispatch more bio. It is used as @@ -190,11 +190,18 @@ alloc_stats: goto alloc_stats; } +/* init a service_queue, assumes the caller zeroed it */ +static void throtl_service_queue_init(struct throtl_service_queue *sq) +{ + sq->pending_tree = RB_ROOT; +} + static void throtl_pd_init(struct blkcg_gq *blkg) { struct throtl_grp *tg = blkg_to_tg(blkg); unsigned long flags; + throtl_service_queue_init(&tg->service_queue); RB_CLEAR_NODE(&tg->rb_node); tg->td = blkg->q->td; bio_list_init(&tg->bio_lists[0]); @@ -1168,8 +1175,8 @@ int blk_throtl_init(struct request_queue *q) if (!td) return -ENOMEM; - td->service_queue = THROTL_SERVICE_QUEUE_INITIALIZER; INIT_DELAYED_WORK(&td->dispatch_work, blk_throtl_dispatch_work_fn); + throtl_service_queue_init(&td->service_queue); q->td = td; td->queue = q; -- cgit v1.2.3 From 73f0d49a9637a7ec3448a62a0042e35b14ba18a3 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 14 May 2013 13:52:35 -0700 Subject: blk-throttle: move bio_lists[] and friends to throtl_service_queue throtl_service_queues will eventually form a tree which is anchored at throtl_data->service_queue and queue bios will climb the tree to the top service_queue to be executed. This patch moves bio_lists[] and nr_queued[] from throtl_grp to its service_queue to prepare for that. As currently only the throtl_data->service_queue is in use, this patch just ends up moving throtl_grp->bio_lists[] and ->nr_queued[] to throtl_grp->service_queue.bio_lists[] and ->nr_queued[] without making any functional differences. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-throttle.c | 63 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 24 deletions(-) diff --git a/block/blk-throttle.c b/block/blk-throttle.c index 7340440ccfb5..6f57f94c3c57 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -27,6 +27,17 @@ static struct blkcg_policy blkcg_policy_throtl; static struct workqueue_struct *kthrotld_workqueue; struct throtl_service_queue { + /* + * Bios queued directly to this service_queue or dispatched from + * children throtl_grp's. + */ + struct bio_list bio_lists[2]; /* queued bios [READ/WRITE] */ + unsigned int nr_queued[2]; /* number of queued bios */ + + /* + * RB tree of active children throtl_grp's, which are sorted by + * their ->disptime. + */ struct rb_root pending_tree; /* RB tree of active tgs */ struct rb_node *first_pending; /* first node in the tree */ unsigned int nr_pending; /* # queued in the tree */ @@ -69,12 +80,6 @@ struct throtl_grp { unsigned int flags; - /* Two lists for READ and WRITE */ - struct bio_list bio_lists[2]; - - /* Number of queued bios on READ and WRITE lists */ - unsigned int nr_queued[2]; - /* bytes per second rate limits */ uint64_t bps[2]; @@ -193,6 +198,8 @@ alloc_stats: /* init a service_queue, assumes the caller zeroed it */ static void throtl_service_queue_init(struct throtl_service_queue *sq) { + bio_list_init(&sq->bio_lists[0]); + bio_list_init(&sq->bio_lists[1]); sq->pending_tree = RB_ROOT; } @@ -204,8 +211,6 @@ static void throtl_pd_init(struct blkcg_gq *blkg) throtl_service_queue_init(&tg->service_queue); RB_CLEAR_NODE(&tg->rb_node); tg->td = blkg->q->td; - bio_list_init(&tg->bio_lists[0]); - bio_list_init(&tg->bio_lists[1]); tg->bps[READ] = -1; tg->bps[WRITE] = -1; @@ -624,7 +629,8 @@ static bool tg_may_dispatch(struct throtl_grp *tg, struct bio *bio, * this function with a different bio if there are other bios * queued. */ - BUG_ON(tg->nr_queued[rw] && bio != bio_list_peek(&tg->bio_lists[rw])); + BUG_ON(tg->service_queue.nr_queued[rw] && + bio != bio_list_peek(&tg->service_queue.bio_lists[rw])); /* If tg->bps = -1, then BW is unlimited */ if (tg->bps[rw] == -1 && tg->iops[rw] == -1) { @@ -703,12 +709,13 @@ static void throtl_charge_bio(struct throtl_grp *tg, struct bio *bio) static void throtl_add_bio_tg(struct bio *bio, struct throtl_grp *tg, struct throtl_service_queue *parent_sq) { + struct throtl_service_queue *sq = &tg->service_queue; bool rw = bio_data_dir(bio); - bio_list_add(&tg->bio_lists[rw], bio); + bio_list_add(&sq->bio_lists[rw], bio); /* Take a bio reference on tg */ blkg_get(tg_to_blkg(tg)); - tg->nr_queued[rw]++; + sq->nr_queued[rw]++; tg->td->nr_queued[rw]++; throtl_enqueue_tg(tg, parent_sq); } @@ -716,13 +723,14 @@ static void throtl_add_bio_tg(struct bio *bio, struct throtl_grp *tg, static void tg_update_disptime(struct throtl_grp *tg, struct throtl_service_queue *parent_sq) { + struct throtl_service_queue *sq = &tg->service_queue; unsigned long read_wait = -1, write_wait = -1, min_wait = -1, disptime; struct bio *bio; - if ((bio = bio_list_peek(&tg->bio_lists[READ]))) + if ((bio = bio_list_peek(&sq->bio_lists[READ]))) tg_may_dispatch(tg, bio, &read_wait); - if ((bio = bio_list_peek(&tg->bio_lists[WRITE]))) + if ((bio = bio_list_peek(&sq->bio_lists[WRITE]))) tg_may_dispatch(tg, bio, &write_wait); min_wait = min(read_wait, write_wait); @@ -737,10 +745,11 @@ static void tg_update_disptime(struct throtl_grp *tg, static void tg_dispatch_one_bio(struct throtl_grp *tg, bool rw, struct bio_list *bl) { + struct throtl_service_queue *sq = &tg->service_queue; struct bio *bio; - bio = bio_list_pop(&tg->bio_lists[rw]); - tg->nr_queued[rw]--; + bio = bio_list_pop(&sq->bio_lists[rw]); + sq->nr_queued[rw]--; /* Drop bio reference on blkg */ blkg_put(tg_to_blkg(tg)); @@ -756,6 +765,7 @@ static void tg_dispatch_one_bio(struct throtl_grp *tg, bool rw, static int throtl_dispatch_tg(struct throtl_grp *tg, struct bio_list *bl) { + struct throtl_service_queue *sq = &tg->service_queue; unsigned int nr_reads = 0, nr_writes = 0; unsigned int max_nr_reads = throtl_grp_quantum*3/4; unsigned int max_nr_writes = throtl_grp_quantum - max_nr_reads; @@ -763,7 +773,7 @@ static int throtl_dispatch_tg(struct throtl_grp *tg, struct bio_list *bl) /* Try to dispatch 75% READS and 25% WRITES */ - while ((bio = bio_list_peek(&tg->bio_lists[READ])) && + while ((bio = bio_list_peek(&sq->bio_lists[READ])) && tg_may_dispatch(tg, bio, NULL)) { tg_dispatch_one_bio(tg, bio_data_dir(bio), bl); @@ -773,7 +783,7 @@ static int throtl_dispatch_tg(struct throtl_grp *tg, struct bio_list *bl) break; } - while ((bio = bio_list_peek(&tg->bio_lists[WRITE])) && + while ((bio = bio_list_peek(&sq->bio_lists[WRITE])) && tg_may_dispatch(tg, bio, NULL)) { tg_dispatch_one_bio(tg, bio_data_dir(bio), bl); @@ -790,10 +800,10 @@ static int throtl_select_dispatch(struct throtl_service_queue *parent_sq, struct bio_list *bl) { unsigned int nr_disp = 0; - struct throtl_grp *tg; while (1) { - tg = throtl_rb_first(parent_sq); + struct throtl_grp *tg = throtl_rb_first(parent_sq); + struct throtl_service_queue *sq = &tg->service_queue; if (!tg) break; @@ -805,7 +815,7 @@ static int throtl_select_dispatch(struct throtl_service_queue *parent_sq, nr_disp += throtl_dispatch_tg(tg, bl); - if (tg->nr_queued[0] || tg->nr_queued[1]) + if (sq->nr_queued[0] || sq->nr_queued[1]) tg_update_disptime(tg, parent_sq); if (nr_disp >= throtl_quantum) @@ -1043,6 +1053,7 @@ bool blk_throtl_bio(struct request_queue *q, struct bio *bio) { struct throtl_data *td = q->td; struct throtl_grp *tg; + struct throtl_service_queue *sq; bool rw = bio_data_dir(bio), update_disptime = true; struct blkcg *blkcg; bool throttled = false; @@ -1077,7 +1088,9 @@ bool blk_throtl_bio(struct request_queue *q, struct bio *bio) if (unlikely(!tg)) goto out_unlock; - if (tg->nr_queued[rw]) { + sq = &tg->service_queue; + + if (sq->nr_queued[rw]) { /* * There is already another bio queued in same dir. No * need to update dispatch time. @@ -1112,7 +1125,7 @@ queue_bio: rw == READ ? 'R' : 'W', tg->bytes_disp[rw], bio->bi_size, tg->bps[rw], tg->io_disp[rw], tg->iops[rw], - tg->nr_queued[READ], tg->nr_queued[WRITE]); + sq->nr_queued[READ], sq->nr_queued[WRITE]); bio_associate_current(bio); throtl_add_bio_tg(bio, tg, &q->td->service_queue); @@ -1151,11 +1164,13 @@ void blk_throtl_drain(struct request_queue *q) bio_list_init(&bl); while ((tg = throtl_rb_first(parent_sq))) { + struct throtl_service_queue *sq = &tg->service_queue; + throtl_dequeue_tg(tg, parent_sq); - while ((bio = bio_list_peek(&tg->bio_lists[READ]))) + while ((bio = bio_list_peek(&sq->bio_lists[READ]))) tg_dispatch_one_bio(tg, bio_data_dir(bio), &bl); - while ((bio = bio_list_peek(&tg->bio_lists[WRITE]))) + while ((bio = bio_list_peek(&sq->bio_lists[WRITE]))) tg_dispatch_one_bio(tg, bio_data_dir(bio), &bl); } spin_unlock_irq(q->queue_lock); -- cgit v1.2.3 From 651930bc1c2a2550fde93a8cfa1a201c363a0ca1 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 14 May 2013 13:52:35 -0700 Subject: blk-throttle: dispatch to throtl_data->service_queue.bio_lists[] throtl_service_queues will eventually form a tree which is anchored at throtl_data->service_queue and queue bios will climb the tree to the top service_queue to be executed. This patch makes the dispatch paths in blk_throtl_dispatch_work_fn() and blk_throtl_drain() to dispatch bios to throtl_data->service_queue.bio_lists[] instead of the on-stack bio_lists. This will keep the final dispatch to the top level service_queue share the same mechanism as dispatches through the rest of the hierarchy. As bio's should be issued in a sleepable context, blk_throtl_dispatch_work_fn() transfers all dispatched bio's from the service_queue bio_lists[] into an onstack one before dropping queue_lock and issuing the bio's. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-throttle.c | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/block/blk-throttle.c b/block/blk-throttle.c index 6f57f94c3c57..154bd63719c5 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -743,7 +743,7 @@ static void tg_update_disptime(struct throtl_grp *tg, } static void tg_dispatch_one_bio(struct throtl_grp *tg, bool rw, - struct bio_list *bl) + struct throtl_service_queue *parent_sq) { struct throtl_service_queue *sq = &tg->service_queue; struct bio *bio; @@ -757,13 +757,14 @@ static void tg_dispatch_one_bio(struct throtl_grp *tg, bool rw, tg->td->nr_queued[rw]--; throtl_charge_bio(tg, bio); - bio_list_add(bl, bio); + bio_list_add(&parent_sq->bio_lists[rw], bio); bio->bi_rw |= REQ_THROTTLED; throtl_trim_slice(tg, rw); } -static int throtl_dispatch_tg(struct throtl_grp *tg, struct bio_list *bl) +static int throtl_dispatch_tg(struct throtl_grp *tg, + struct throtl_service_queue *parent_sq) { struct throtl_service_queue *sq = &tg->service_queue; unsigned int nr_reads = 0, nr_writes = 0; @@ -776,7 +777,7 @@ static int throtl_dispatch_tg(struct throtl_grp *tg, struct bio_list *bl) while ((bio = bio_list_peek(&sq->bio_lists[READ])) && tg_may_dispatch(tg, bio, NULL)) { - tg_dispatch_one_bio(tg, bio_data_dir(bio), bl); + tg_dispatch_one_bio(tg, bio_data_dir(bio), parent_sq); nr_reads++; if (nr_reads >= max_nr_reads) @@ -786,7 +787,7 @@ static int throtl_dispatch_tg(struct throtl_grp *tg, struct bio_list *bl) while ((bio = bio_list_peek(&sq->bio_lists[WRITE])) && tg_may_dispatch(tg, bio, NULL)) { - tg_dispatch_one_bio(tg, bio_data_dir(bio), bl); + tg_dispatch_one_bio(tg, bio_data_dir(bio), parent_sq); nr_writes++; if (nr_writes >= max_nr_writes) @@ -796,8 +797,7 @@ static int throtl_dispatch_tg(struct throtl_grp *tg, struct bio_list *bl) return nr_reads + nr_writes; } -static int throtl_select_dispatch(struct throtl_service_queue *parent_sq, - struct bio_list *bl) +static int throtl_select_dispatch(struct throtl_service_queue *parent_sq) { unsigned int nr_disp = 0; @@ -813,7 +813,7 @@ static int throtl_select_dispatch(struct throtl_service_queue *parent_sq, throtl_dequeue_tg(tg, parent_sq); - nr_disp += throtl_dispatch_tg(tg, bl); + nr_disp += throtl_dispatch_tg(tg, parent_sq); if (sq->nr_queued[0] || sq->nr_queued[1]) tg_update_disptime(tg, parent_sq); @@ -830,11 +830,13 @@ void blk_throtl_dispatch_work_fn(struct work_struct *work) { struct throtl_data *td = container_of(to_delayed_work(work), struct throtl_data, dispatch_work); + struct throtl_service_queue *sq = &td->service_queue; struct request_queue *q = td->queue; unsigned int nr_disp = 0; struct bio_list bio_list_on_stack; struct bio *bio; struct blk_plug plug; + int rw; spin_lock_irq(q->queue_lock); @@ -844,10 +846,15 @@ void blk_throtl_dispatch_work_fn(struct work_struct *work) td->nr_queued[READ] + td->nr_queued[WRITE], td->nr_queued[READ], td->nr_queued[WRITE]); - nr_disp = throtl_select_dispatch(&td->service_queue, &bio_list_on_stack); + nr_disp = throtl_select_dispatch(sq); - if (nr_disp) + if (nr_disp) { + for (rw = READ; rw <= WRITE; rw++) { + bio_list_merge(&bio_list_on_stack, &sq->bio_lists[rw]); + bio_list_init(&sq->bio_lists[rw]); + } throtl_log(td, "bios disp=%u", nr_disp); + } throtl_schedule_next_dispatch(td); @@ -1156,27 +1163,26 @@ void blk_throtl_drain(struct request_queue *q) struct throtl_data *td = q->td; struct throtl_service_queue *parent_sq = &td->service_queue; struct throtl_grp *tg; - struct bio_list bl; struct bio *bio; + int rw; queue_lockdep_assert_held(q); - bio_list_init(&bl); - while ((tg = throtl_rb_first(parent_sq))) { struct throtl_service_queue *sq = &tg->service_queue; throtl_dequeue_tg(tg, parent_sq); while ((bio = bio_list_peek(&sq->bio_lists[READ]))) - tg_dispatch_one_bio(tg, bio_data_dir(bio), &bl); + tg_dispatch_one_bio(tg, bio_data_dir(bio), parent_sq); while ((bio = bio_list_peek(&sq->bio_lists[WRITE]))) - tg_dispatch_one_bio(tg, bio_data_dir(bio), &bl); + tg_dispatch_one_bio(tg, bio_data_dir(bio), parent_sq); } spin_unlock_irq(q->queue_lock); - while ((bio = bio_list_pop(&bl))) - generic_make_request(bio); + for (rw = READ; rw <= WRITE; rw++) + while ((bio = bio_list_pop(&parent_sq->bio_lists[rw]))) + generic_make_request(bio); spin_lock_irq(q->queue_lock); } -- cgit v1.2.3 From 0e9f4164ba915052918a77ecb2a59822dbfd661c Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 14 May 2013 13:52:35 -0700 Subject: blk-throttle: generalize update_disptime optimization in blk_throtl_bio() When blk_throtl_bio() wants to queue a bio to a tg (throtl_grp), it avoids invoking tg_update_disptime() and throtl_schedule_next_dispatch() if the tg already has bios queued in that direction. As a new bio is appeneded after the existing ones, it can't change the tg's next dispatch time or the parent's dispatch schedule. This optimization is currently open coded in blk_throtl_bio(). Whether the target biolist was occupied was recorded in a local variable and later used to skip disptime update. This patch moves generalizes it so that throtl_add_bio_tg() sets a new flag THROTL_TG_WAS_EMPTY if the biolist was empty before the new bio was added. tg_update_disptime() clears the flag automatically. blk_throtl_bio() is updated to simply test the flag before updating disptime. This patch doesn't make any functional differences now but will enable using the same optimization for recursive dispatch. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-throttle.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/block/blk-throttle.c b/block/blk-throttle.c index 154bd63719c5..ec9397f3eb0a 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -46,6 +46,7 @@ struct throtl_service_queue { enum tg_state_flags { THROTL_TG_PENDING = 1 << 0, /* on parent's pending tree */ + THROTL_TG_WAS_EMPTY = 1 << 1, /* bio_lists[] became non-empty */ }; #define rb_entry_tg(node) rb_entry((node), struct throtl_grp, rb_node) @@ -712,6 +713,15 @@ static void throtl_add_bio_tg(struct bio *bio, struct throtl_grp *tg, struct throtl_service_queue *sq = &tg->service_queue; bool rw = bio_data_dir(bio); + /* + * If @tg doesn't currently have any bios queued in the same + * direction, queueing @bio can change when @tg should be + * dispatched. Mark that @tg was empty. This is automatically + * cleaered on the next tg_update_disptime(). + */ + if (!sq->nr_queued[rw]) + tg->flags |= THROTL_TG_WAS_EMPTY; + bio_list_add(&sq->bio_lists[rw], bio); /* Take a bio reference on tg */ blkg_get(tg_to_blkg(tg)); @@ -740,6 +750,9 @@ static void tg_update_disptime(struct throtl_grp *tg, throtl_dequeue_tg(tg, parent_sq); tg->disptime = disptime; throtl_enqueue_tg(tg, parent_sq); + + /* see throtl_add_bio_tg() */ + tg->flags &= ~THROTL_TG_WAS_EMPTY; } static void tg_dispatch_one_bio(struct throtl_grp *tg, bool rw, @@ -1061,7 +1074,7 @@ bool blk_throtl_bio(struct request_queue *q, struct bio *bio) struct throtl_data *td = q->td; struct throtl_grp *tg; struct throtl_service_queue *sq; - bool rw = bio_data_dir(bio), update_disptime = true; + bool rw = bio_data_dir(bio); struct blkcg *blkcg; bool throttled = false; @@ -1097,16 +1110,10 @@ bool blk_throtl_bio(struct request_queue *q, struct bio *bio) sq = &tg->service_queue; - if (sq->nr_queued[rw]) { - /* - * There is already another bio queued in same dir. No - * need to update dispatch time. - */ - update_disptime = false; + /* throtl is FIFO - if other bios are already queued, should queue */ + if (sq->nr_queued[rw]) goto queue_bio; - } - /* Bio is with-in rate limit of group */ if (tg_may_dispatch(tg, bio, NULL)) { throtl_charge_bio(tg, bio); @@ -1138,7 +1145,8 @@ queue_bio: throtl_add_bio_tg(bio, tg, &q->td->service_queue); throttled = true; - if (update_disptime) { + /* update @tg's dispatch time if @tg was empty before @bio */ + if (tg->flags & THROTL_TG_WAS_EMPTY) { tg_update_disptime(tg, &td->service_queue); throtl_schedule_next_dispatch(td); } -- cgit v1.2.3 From 77216b0484817817a18aaa6b089dffe49070f7c1 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 14 May 2013 13:52:36 -0700 Subject: blk-throttle: add throtl_service_queue->parent_sq To prepare for hierarchy support, this patch adds throtl_service_queue->service_sq which points to the arent service_queue. Currently, for all service_queues embedded in throtl_grps, it points to throtl_data->service_queue. As throtl_data->service_queue doesn't have a parent its parent_sq is set to NULL. There are a number of functions which take both throtl_grp *tg and throtl_service_queue *parent_sq. With this patch, the parent service_queue can be determined from @tg and the @parent_sq arguments are removed. This patch doesn't make any behavior differences. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-throttle.c | 81 +++++++++++++++++++++++++--------------------------- 1 file changed, 39 insertions(+), 42 deletions(-) diff --git a/block/blk-throttle.c b/block/blk-throttle.c index ec9397f3eb0a..00cfdd05f98f 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -27,6 +27,8 @@ static struct blkcg_policy blkcg_policy_throtl; static struct workqueue_struct *kthrotld_workqueue; struct throtl_service_queue { + struct throtl_service_queue *parent_sq; /* the parent service_queue */ + /* * Bios queued directly to this service_queue or dispatched from * children throtl_grp's. @@ -197,21 +199,24 @@ alloc_stats: } /* init a service_queue, assumes the caller zeroed it */ -static void throtl_service_queue_init(struct throtl_service_queue *sq) +static void throtl_service_queue_init(struct throtl_service_queue *sq, + struct throtl_service_queue *parent_sq) { bio_list_init(&sq->bio_lists[0]); bio_list_init(&sq->bio_lists[1]); sq->pending_tree = RB_ROOT; + sq->parent_sq = parent_sq; } static void throtl_pd_init(struct blkcg_gq *blkg) { struct throtl_grp *tg = blkg_to_tg(blkg); + struct throtl_data *td = blkg->q->td; unsigned long flags; - throtl_service_queue_init(&tg->service_queue); + throtl_service_queue_init(&tg->service_queue, &td->service_queue); RB_CLEAR_NODE(&tg->rb_node); - tg->td = blkg->q->td; + tg->td = td; tg->bps[READ] = -1; tg->bps[WRITE] = -1; @@ -339,9 +344,9 @@ static void update_min_dispatch_time(struct throtl_service_queue *parent_sq) parent_sq->first_pending_disptime = tg->disptime; } -static void tg_service_queue_add(struct throtl_grp *tg, - struct throtl_service_queue *parent_sq) +static void tg_service_queue_add(struct throtl_grp *tg) { + struct throtl_service_queue *parent_sq = tg->service_queue.parent_sq; struct rb_node **node = &parent_sq->pending_tree.rb_node; struct rb_node *parent = NULL; struct throtl_grp *__tg; @@ -367,33 +372,29 @@ static void tg_service_queue_add(struct throtl_grp *tg, rb_insert_color(&tg->rb_node, &parent_sq->pending_tree); } -static void __throtl_enqueue_tg(struct throtl_grp *tg, - struct throtl_service_queue *parent_sq) +static void __throtl_enqueue_tg(struct throtl_grp *tg) { - tg_service_queue_add(tg, parent_sq); + tg_service_queue_add(tg); tg->flags |= THROTL_TG_PENDING; - parent_sq->nr_pending++; + tg->service_queue.parent_sq->nr_pending++; } -static void throtl_enqueue_tg(struct throtl_grp *tg, - struct throtl_service_queue *parent_sq) +static void throtl_enqueue_tg(struct throtl_grp *tg) { if (!(tg->flags & THROTL_TG_PENDING)) - __throtl_enqueue_tg(tg, parent_sq); + __throtl_enqueue_tg(tg); } -static void __throtl_dequeue_tg(struct throtl_grp *tg, - struct throtl_service_queue *parent_sq) +static void __throtl_dequeue_tg(struct throtl_grp *tg) { - throtl_rb_erase(&tg->rb_node, parent_sq); + throtl_rb_erase(&tg->rb_node, tg->service_queue.parent_sq); tg->flags &= ~THROTL_TG_PENDING; } -static void throtl_dequeue_tg(struct throtl_grp *tg, - struct throtl_service_queue *parent_sq) +static void throtl_dequeue_tg(struct throtl_grp *tg) { if (tg->flags & THROTL_TG_PENDING) - __throtl_dequeue_tg(tg, parent_sq); + __throtl_dequeue_tg(tg); } /* Call with queue lock held */ @@ -707,8 +708,7 @@ static void throtl_charge_bio(struct throtl_grp *tg, struct bio *bio) throtl_update_dispatch_stats(tg_to_blkg(tg), bio->bi_size, bio->bi_rw); } -static void throtl_add_bio_tg(struct bio *bio, struct throtl_grp *tg, - struct throtl_service_queue *parent_sq) +static void throtl_add_bio_tg(struct bio *bio, struct throtl_grp *tg) { struct throtl_service_queue *sq = &tg->service_queue; bool rw = bio_data_dir(bio); @@ -727,11 +727,10 @@ static void throtl_add_bio_tg(struct bio *bio, struct throtl_grp *tg, blkg_get(tg_to_blkg(tg)); sq->nr_queued[rw]++; tg->td->nr_queued[rw]++; - throtl_enqueue_tg(tg, parent_sq); + throtl_enqueue_tg(tg); } -static void tg_update_disptime(struct throtl_grp *tg, - struct throtl_service_queue *parent_sq) +static void tg_update_disptime(struct throtl_grp *tg) { struct throtl_service_queue *sq = &tg->service_queue; unsigned long read_wait = -1, write_wait = -1, min_wait = -1, disptime; @@ -747,16 +746,15 @@ static void tg_update_disptime(struct throtl_grp *tg, disptime = jiffies + min_wait; /* Update dispatch time */ - throtl_dequeue_tg(tg, parent_sq); + throtl_dequeue_tg(tg); tg->disptime = disptime; - throtl_enqueue_tg(tg, parent_sq); + throtl_enqueue_tg(tg); /* see throtl_add_bio_tg() */ tg->flags &= ~THROTL_TG_WAS_EMPTY; } -static void tg_dispatch_one_bio(struct throtl_grp *tg, bool rw, - struct throtl_service_queue *parent_sq) +static void tg_dispatch_one_bio(struct throtl_grp *tg, bool rw) { struct throtl_service_queue *sq = &tg->service_queue; struct bio *bio; @@ -770,14 +768,13 @@ static void tg_dispatch_one_bio(struct throtl_grp *tg, bool rw, tg->td->nr_queued[rw]--; throtl_charge_bio(tg, bio); - bio_list_add(&parent_sq->bio_lists[rw], bio); + bio_list_add(&sq->parent_sq->bio_lists[rw], bio); bio->bi_rw |= REQ_THROTTLED; throtl_trim_slice(tg, rw); } -static int throtl_dispatch_tg(struct throtl_grp *tg, - struct throtl_service_queue *parent_sq) +static int throtl_dispatch_tg(struct throtl_grp *tg) { struct throtl_service_queue *sq = &tg->service_queue; unsigned int nr_reads = 0, nr_writes = 0; @@ -790,7 +787,7 @@ static int throtl_dispatch_tg(struct throtl_grp *tg, while ((bio = bio_list_peek(&sq->bio_lists[READ])) && tg_may_dispatch(tg, bio, NULL)) { - tg_dispatch_one_bio(tg, bio_data_dir(bio), parent_sq); + tg_dispatch_one_bio(tg, bio_data_dir(bio)); nr_reads++; if (nr_reads >= max_nr_reads) @@ -800,7 +797,7 @@ static int throtl_dispatch_tg(struct throtl_grp *tg, while ((bio = bio_list_peek(&sq->bio_lists[WRITE])) && tg_may_dispatch(tg, bio, NULL)) { - tg_dispatch_one_bio(tg, bio_data_dir(bio), parent_sq); + tg_dispatch_one_bio(tg, bio_data_dir(bio)); nr_writes++; if (nr_writes >= max_nr_writes) @@ -824,12 +821,12 @@ static int throtl_select_dispatch(struct throtl_service_queue *parent_sq) if (time_before(jiffies, tg->disptime)) break; - throtl_dequeue_tg(tg, parent_sq); + throtl_dequeue_tg(tg); - nr_disp += throtl_dispatch_tg(tg, parent_sq); + nr_disp += throtl_dispatch_tg(tg); if (sq->nr_queued[0] || sq->nr_queued[1]) - tg_update_disptime(tg, parent_sq); + tg_update_disptime(tg); if (nr_disp >= throtl_quantum) break; @@ -991,7 +988,7 @@ static int tg_set_conf(struct cgroup *cgrp, struct cftype *cft, const char *buf, throtl_start_new_slice(tg, 1); if (tg->flags & THROTL_TG_PENDING) { - tg_update_disptime(tg, &td->service_queue); + tg_update_disptime(tg); throtl_schedule_next_dispatch(td); } @@ -1142,12 +1139,12 @@ queue_bio: sq->nr_queued[READ], sq->nr_queued[WRITE]); bio_associate_current(bio); - throtl_add_bio_tg(bio, tg, &q->td->service_queue); + throtl_add_bio_tg(bio, tg); throttled = true; /* update @tg's dispatch time if @tg was empty before @bio */ if (tg->flags & THROTL_TG_WAS_EMPTY) { - tg_update_disptime(tg, &td->service_queue); + tg_update_disptime(tg); throtl_schedule_next_dispatch(td); } @@ -1179,12 +1176,12 @@ void blk_throtl_drain(struct request_queue *q) while ((tg = throtl_rb_first(parent_sq))) { struct throtl_service_queue *sq = &tg->service_queue; - throtl_dequeue_tg(tg, parent_sq); + throtl_dequeue_tg(tg); while ((bio = bio_list_peek(&sq->bio_lists[READ]))) - tg_dispatch_one_bio(tg, bio_data_dir(bio), parent_sq); + tg_dispatch_one_bio(tg, bio_data_dir(bio)); while ((bio = bio_list_peek(&sq->bio_lists[WRITE]))) - tg_dispatch_one_bio(tg, bio_data_dir(bio), parent_sq); + tg_dispatch_one_bio(tg, bio_data_dir(bio)); } spin_unlock_irq(q->queue_lock); @@ -1205,7 +1202,7 @@ int blk_throtl_init(struct request_queue *q) return -ENOMEM; INIT_DELAYED_WORK(&td->dispatch_work, blk_throtl_dispatch_work_fn); - throtl_service_queue_init(&td->service_queue); + throtl_service_queue_init(&td->service_queue, NULL); q->td = td; td->queue = q; -- cgit v1.2.3 From fda6f272c77a7acd798bb247fadc4791574e698b Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 14 May 2013 13:52:36 -0700 Subject: blk-throttle: implement sq_to_tg(), sq_to_td() and throtl_log() Now that both throtl_data and throtl_grp embed throtl_service_queue, we can unify throtl_log() and throtl_log_tg(). * sq_to_tg() is added. This returns the throtl_grp a service_queue is embedded in. If the service_queue is the top-level one embedded in throtl_data, NULL is returned. * sq_to_td() is added. A service_queue is always associated with a throtl_data. This function finds the associated td and returns it. * throtl_log() is updated to take throtl_service_queue instead of throtl_data. If the service_queue is one embedded in throtl_grp, it prints the same header as throtl_log_tg() did. If it's one embedded in throtl_data, it behaves the same as before. This renders throtl_log_tg() unnecessary. Removed. This change is necessary for hierarchy support as we're gonna be using the same code paths to dispatch bios to intermediate service_queues embedded in throtl_grps and the top-level service_queue embedded in throtl_data. This patch doesn't make any behavior changes. v2: throtl_log() didn't print a space after blkg path. Updated so that it prints a space after throtl_grp path. Spotted by Vivek. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-throttle.c | 110 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 81 insertions(+), 29 deletions(-) diff --git a/block/blk-throttle.c b/block/blk-throttle.c index 00cfdd05f98f..2875ff66e1b9 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -151,16 +151,65 @@ static inline struct throtl_grp *td_root_tg(struct throtl_data *td) return blkg_to_tg(td->queue->root_blkg); } -#define throtl_log_tg(tg, fmt, args...) do { \ - char __pbuf[128]; \ +/** + * sq_to_tg - return the throl_grp the specified service queue belongs to + * @sq: the throtl_service_queue of interest + * + * Return the throtl_grp @sq belongs to. If @sq is the top-level one + * embedded in throtl_data, %NULL is returned. + */ +static struct throtl_grp *sq_to_tg(struct throtl_service_queue *sq) +{ + if (sq && sq->parent_sq) + return container_of(sq, struct throtl_grp, service_queue); + else + return NULL; +} + +/** + * sq_to_td - return throtl_data the specified service queue belongs to + * @sq: the throtl_service_queue of interest + * + * A service_queue can be embeded in either a throtl_grp or throtl_data. + * Determine the associated throtl_data accordingly and return it. + */ +static struct throtl_data *sq_to_td(struct throtl_service_queue *sq) +{ + struct throtl_grp *tg = sq_to_tg(sq); + + if (tg) + return tg->td; + else + return container_of(sq, struct throtl_data, service_queue); +} + +/** + * throtl_log - log debug message via blktrace + * @sq: the service_queue being reported + * @fmt: printf format string + * @args: printf args + * + * The messages are prefixed with "throtl BLKG_NAME" if @sq belongs to a + * throtl_grp; otherwise, just "throtl". + * + * TODO: this should be made a function and name formatting should happen + * after testing whether blktrace is enabled. + */ +#define throtl_log(sq, fmt, args...) do { \ + struct throtl_grp *__tg = sq_to_tg((sq)); \ + struct throtl_data *__td = sq_to_td((sq)); \ + \ + (void)__td; \ + if ((__tg)) { \ + char __pbuf[128]; \ \ - blkg_path(tg_to_blkg(tg), __pbuf, sizeof(__pbuf)); \ - blk_add_trace_msg((tg)->td->queue, "throtl %s " fmt, __pbuf, ##args); \ + blkg_path(tg_to_blkg(__tg), __pbuf, sizeof(__pbuf)); \ + blk_add_trace_msg(__td->queue, "throtl %s " fmt, __pbuf, ##args); \ + } else { \ + blk_add_trace_msg(__td->queue, "throtl " fmt, ##args); \ + } \ } while (0) -#define throtl_log(td, fmt, args...) \ - blk_add_trace_msg((td)->queue, "throtl " fmt, ##args) - /* * Worker for allocating per cpu stat for tgs. This is scheduled on the * system_wq once there are some groups on the alloc_list waiting for @@ -402,9 +451,10 @@ static void throtl_schedule_delayed_work(struct throtl_data *td, unsigned long delay) { struct delayed_work *dwork = &td->dispatch_work; + struct throtl_service_queue *sq = &td->service_queue; mod_delayed_work(kthrotld_workqueue, dwork, delay); - throtl_log(td, "schedule work. delay=%lu jiffies=%lu", delay, jiffies); + throtl_log(sq, "schedule work. delay=%lu jiffies=%lu", delay, jiffies); } static void throtl_schedule_next_dispatch(struct throtl_data *td) @@ -429,9 +479,10 @@ static inline void throtl_start_new_slice(struct throtl_grp *tg, bool rw) tg->io_disp[rw] = 0; tg->slice_start[rw] = jiffies; tg->slice_end[rw] = jiffies + throtl_slice; - throtl_log_tg(tg, "[%c] new slice start=%lu end=%lu jiffies=%lu", - rw == READ ? 'R' : 'W', tg->slice_start[rw], - tg->slice_end[rw], jiffies); + throtl_log(&tg->service_queue, + "[%c] new slice start=%lu end=%lu jiffies=%lu", + rw == READ ? 'R' : 'W', tg->slice_start[rw], + tg->slice_end[rw], jiffies); } static inline void throtl_set_slice_end(struct throtl_grp *tg, bool rw, @@ -444,9 +495,10 @@ static inline void throtl_extend_slice(struct throtl_grp *tg, bool rw, unsigned long jiffy_end) { tg->slice_end[rw] = roundup(jiffy_end, throtl_slice); - throtl_log_tg(tg, "[%c] extend slice start=%lu end=%lu jiffies=%lu", - rw == READ ? 'R' : 'W', tg->slice_start[rw], - tg->slice_end[rw], jiffies); + throtl_log(&tg->service_queue, + "[%c] extend slice start=%lu end=%lu jiffies=%lu", + rw == READ ? 'R' : 'W', tg->slice_start[rw], + tg->slice_end[rw], jiffies); } /* Determine if previously allocated or extended slice is complete or not */ @@ -511,10 +563,10 @@ static inline void throtl_trim_slice(struct throtl_grp *tg, bool rw) tg->slice_start[rw] += nr_slices * throtl_slice; - throtl_log_tg(tg, "[%c] trim slice nr=%lu bytes=%llu io=%lu" - " start=%lu end=%lu jiffies=%lu", - rw == READ ? 'R' : 'W', nr_slices, bytes_trim, io_trim, - tg->slice_start[rw], tg->slice_end[rw], jiffies); + throtl_log(&tg->service_queue, + "[%c] trim slice nr=%lu bytes=%llu io=%lu start=%lu end=%lu jiffies=%lu", + rw == READ ? 'R' : 'W', nr_slices, bytes_trim, io_trim, + tg->slice_start[rw], tg->slice_end[rw], jiffies); } static bool tg_with_in_iops_limit(struct throtl_grp *tg, struct bio *bio, @@ -852,7 +904,7 @@ void blk_throtl_dispatch_work_fn(struct work_struct *work) bio_list_init(&bio_list_on_stack); - throtl_log(td, "dispatch nr_queued=%u read=%u write=%u", + throtl_log(sq, "dispatch nr_queued=%u read=%u write=%u", td->nr_queued[READ] + td->nr_queued[WRITE], td->nr_queued[READ], td->nr_queued[WRITE]); @@ -863,7 +915,7 @@ void blk_throtl_dispatch_work_fn(struct work_struct *work) bio_list_merge(&bio_list_on_stack, &sq->bio_lists[rw]); bio_list_init(&sq->bio_lists[rw]); } - throtl_log(td, "bios disp=%u", nr_disp); + throtl_log(sq, "bios disp=%u", nr_disp); } throtl_schedule_next_dispatch(td); @@ -972,9 +1024,10 @@ static int tg_set_conf(struct cgroup *cgrp, struct cftype *cft, const char *buf, else *(unsigned int *)((void *)tg + cft->private) = ctx.v; - throtl_log_tg(tg, "limit change rbps=%llu wbps=%llu riops=%u wiops=%u", - tg->bps[READ], tg->bps[WRITE], - tg->iops[READ], tg->iops[WRITE]); + throtl_log(&tg->service_queue, + "limit change rbps=%llu wbps=%llu riops=%u wiops=%u", + tg->bps[READ], tg->bps[WRITE], + tg->iops[READ], tg->iops[WRITE]); /* * We're already holding queue_lock and know @tg is valid. Let's @@ -1131,12 +1184,11 @@ bool blk_throtl_bio(struct request_queue *q, struct bio *bio) } queue_bio: - throtl_log_tg(tg, "[%c] bio. bdisp=%llu sz=%u bps=%llu" - " iodisp=%u iops=%u queued=%d/%d", - rw == READ ? 'R' : 'W', - tg->bytes_disp[rw], bio->bi_size, tg->bps[rw], - tg->io_disp[rw], tg->iops[rw], - sq->nr_queued[READ], sq->nr_queued[WRITE]); + throtl_log(sq, "[%c] bio. bdisp=%llu sz=%u bps=%llu iodisp=%u iops=%u queued=%d/%d", + rw == READ ? 'R' : 'W', + tg->bytes_disp[rw], bio->bi_size, tg->bps[rw], + tg->io_disp[rw], tg->iops[rw], + sq->nr_queued[READ], sq->nr_queued[WRITE]); bio_associate_current(bio); throtl_add_bio_tg(bio, tg); -- cgit v1.2.3 From 2a0f61e6ecd08d260054bde4b096ff207ce5350f Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 14 May 2013 13:52:36 -0700 Subject: blk-throttle: set REQ_THROTTLED from throtl_charge_bio() and gate stats update with it With proper hierarchy support, a bio can be dispatched multiple times until it reaches the top-level service_queue and we don't want to update dispatch stats at each step. They are local stats and will be kept local. If recursive stats are necessary, they should be implemented separately and definitely not by updating counters recursively on each dispatch. This patch moves REQ_THROTTLED setting to throtl_charge_bio() and gate stats update with it so that dispatch stats are updated only on the first time the bio is charged to a throtl_grp, which will always be the throtl_grp the bio was originally queued to. This means that REQ_THROTTLED would be set even for bios which don't get throttled. As we don't want bios to leave blk-throtl with the flag set, move REQ_THROTLLED clearing to the end of blk_throtl_bio() and clear if the bio is being issued directly. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-throttle.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/block/blk-throttle.c b/block/blk-throttle.c index 2875ff66e1b9..420eaa150d11 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -757,7 +757,22 @@ static void throtl_charge_bio(struct throtl_grp *tg, struct bio *bio) tg->bytes_disp[rw] += bio->bi_size; tg->io_disp[rw]++; - throtl_update_dispatch_stats(tg_to_blkg(tg), bio->bi_size, bio->bi_rw); + /* + * REQ_THROTTLED is used to prevent the same bio to be throttled + * more than once as a throttled bio will go through blk-throtl the + * second time when it eventually gets issued. Set it when a bio + * is being charged to a tg. + * + * Dispatch stats aren't recursive and each @bio should only be + * accounted by the @tg it was originally associated with. Let's + * update the stats when setting REQ_THROTTLED for the first time + * which is guaranteed to be for the @bio's original tg. + */ + if (!(bio->bi_rw & REQ_THROTTLED)) { + bio->bi_rw |= REQ_THROTTLED; + throtl_update_dispatch_stats(tg_to_blkg(tg), bio->bi_size, + bio->bi_rw); + } } static void throtl_add_bio_tg(struct bio *bio, struct throtl_grp *tg) @@ -821,7 +836,6 @@ static void tg_dispatch_one_bio(struct throtl_grp *tg, bool rw) throtl_charge_bio(tg, bio); bio_list_add(&sq->parent_sq->bio_lists[rw], bio); - bio->bi_rw |= REQ_THROTTLED; throtl_trim_slice(tg, rw); } @@ -1128,10 +1142,9 @@ bool blk_throtl_bio(struct request_queue *q, struct bio *bio) struct blkcg *blkcg; bool throttled = false; - if (bio->bi_rw & REQ_THROTTLED) { - bio->bi_rw &= ~REQ_THROTTLED; + /* see throtl_charge_bio() */ + if (bio->bi_rw & REQ_THROTTLED) goto out; - } /* * A throtl_grp pointer retrieved under rcu can be used to access @@ -1205,6 +1218,13 @@ out_unlock: out_unlock_rcu: rcu_read_unlock(); out: + /* + * As multiple blk-throtls may stack in the same issue path, we + * don't want bios to leave with the flag set. Clear the flag if + * being issued. + */ + if (!throttled) + bio->bi_rw &= ~REQ_THROTTLED; return throttled; } -- cgit v1.2.3 From 69df0ab030c94e851b77991c2f5e00bcf5294edc Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 14 May 2013 13:52:36 -0700 Subject: blk-throttle: separate out throtl_service_queue->pending_timer from throtl_data->dispatch_work Currently, throtl_data->dispatch_work is a delayed_work item which handles both delayed dispatch and issuing bios. The two tasks will be separated to support proper hierarchy. To prepare for that, this patch separates out the timer into throtl_service_queue->pending_timer from throtl_data->dispatch_work and make the latter a work_struct. * As the timer is now per-service_queue, it's initialized and del_sync'd as its corresponding service_queue is created and destroyed. The timer, when triggered, simply schedules throtl_data->dispathc_work for execution. * throtl_schedule_delayed_work() is renamed to throtl_schedule_pending_timer() and takes @sq and @expires now. * Simiarly, throtl_schedule_next_dispatch() now takes @sq, which should be the parent_sq of the service_queue which just got a new bio or updated. As the parent_sq is always the top-level service_queue now, this doesn't change anything at this point. This patch doesn't introduce any behavior differences. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-throttle.c | 68 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 45 insertions(+), 23 deletions(-) diff --git a/block/blk-throttle.c b/block/blk-throttle.c index 420eaa150d11..a8d23f0cf357 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -44,6 +44,7 @@ struct throtl_service_queue { struct rb_node *first_pending; /* first node in the tree */ unsigned int nr_pending; /* # queued in the tree */ unsigned long first_pending_disptime; /* disptime of the first tg */ + struct timer_list pending_timer; /* fires on first_pending_disptime */ }; enum tg_state_flags { @@ -121,7 +122,7 @@ struct throtl_data unsigned int nr_undestroyed_grps; /* Work for dispatching throttled bios */ - struct delayed_work dispatch_work; + struct work_struct dispatch_work; }; /* list and work item to allocate percpu group stats */ @@ -131,6 +132,8 @@ static LIST_HEAD(tg_stats_alloc_list); static void tg_stats_alloc_fn(struct work_struct *); static DECLARE_DELAYED_WORK(tg_stats_alloc_work, tg_stats_alloc_fn); +static void throtl_pending_timer_fn(unsigned long arg); + static inline struct throtl_grp *pd_to_tg(struct blkg_policy_data *pd) { return pd ? container_of(pd, struct throtl_grp, pd) : NULL; @@ -255,6 +258,13 @@ static void throtl_service_queue_init(struct throtl_service_queue *sq, bio_list_init(&sq->bio_lists[1]); sq->pending_tree = RB_ROOT; sq->parent_sq = parent_sq; + setup_timer(&sq->pending_timer, throtl_pending_timer_fn, + (unsigned long)sq); +} + +static void throtl_service_queue_exit(struct throtl_service_queue *sq) +{ + del_timer_sync(&sq->pending_timer); } static void throtl_pd_init(struct blkcg_gq *blkg) @@ -293,6 +303,8 @@ static void throtl_pd_exit(struct blkcg_gq *blkg) spin_unlock_irqrestore(&tg_stats_alloc_lock, flags); free_percpu(tg->stats_cpu); + + throtl_service_queue_exit(&tg->service_queue); } static void throtl_pd_reset_stats(struct blkcg_gq *blkg) @@ -447,19 +459,17 @@ static void throtl_dequeue_tg(struct throtl_grp *tg) } /* Call with queue lock held */ -static void throtl_schedule_delayed_work(struct throtl_data *td, - unsigned long delay) +static void throtl_schedule_pending_timer(struct throtl_service_queue *sq, + unsigned long expires) { - struct delayed_work *dwork = &td->dispatch_work; - struct throtl_service_queue *sq = &td->service_queue; - - mod_delayed_work(kthrotld_workqueue, dwork, delay); - throtl_log(sq, "schedule work. delay=%lu jiffies=%lu", delay, jiffies); + mod_timer(&sq->pending_timer, expires); + throtl_log(sq, "schedule timer. delay=%lu jiffies=%lu", + expires - jiffies, jiffies); } -static void throtl_schedule_next_dispatch(struct throtl_data *td) +static void throtl_schedule_next_dispatch(struct throtl_service_queue *sq) { - struct throtl_service_queue *sq = &td->service_queue; + struct throtl_data *td = sq_to_td(sq); /* any pending children left? */ if (!sq->nr_pending) @@ -467,10 +477,14 @@ static void throtl_schedule_next_dispatch(struct throtl_data *td) update_min_dispatch_time(sq); - if (time_before_eq(sq->first_pending_disptime, jiffies)) - throtl_schedule_delayed_work(td, 0); - else - throtl_schedule_delayed_work(td, sq->first_pending_disptime - jiffies); + /* is the next dispatch time in the future? */ + if (time_after(sq->first_pending_disptime, jiffies)) { + throtl_schedule_pending_timer(sq, sq->first_pending_disptime); + return; + } + + /* kick immediate execution */ + queue_work(kthrotld_workqueue, &td->dispatch_work); } static inline void throtl_start_new_slice(struct throtl_grp *tg, bool rw) @@ -901,11 +915,19 @@ static int throtl_select_dispatch(struct throtl_service_queue *parent_sq) return nr_disp; } +static void throtl_pending_timer_fn(unsigned long arg) +{ + struct throtl_service_queue *sq = (void *)arg; + struct throtl_data *td = sq_to_td(sq); + + queue_work(kthrotld_workqueue, &td->dispatch_work); +} + /* work function to dispatch throttled bios */ void blk_throtl_dispatch_work_fn(struct work_struct *work) { - struct throtl_data *td = container_of(to_delayed_work(work), - struct throtl_data, dispatch_work); + struct throtl_data *td = container_of(work, struct throtl_data, + dispatch_work); struct throtl_service_queue *sq = &td->service_queue; struct request_queue *q = td->queue; unsigned int nr_disp = 0; @@ -932,7 +954,7 @@ void blk_throtl_dispatch_work_fn(struct work_struct *work) throtl_log(sq, "bios disp=%u", nr_disp); } - throtl_schedule_next_dispatch(td); + throtl_schedule_next_dispatch(sq); spin_unlock_irq(q->queue_lock); @@ -1020,7 +1042,7 @@ static int tg_set_conf(struct cgroup *cgrp, struct cftype *cft, const char *buf, struct blkcg *blkcg = cgroup_to_blkcg(cgrp); struct blkg_conf_ctx ctx; struct throtl_grp *tg; - struct throtl_data *td; + struct throtl_service_queue *sq; int ret; ret = blkg_conf_prep(blkcg, &blkcg_policy_throtl, buf, &ctx); @@ -1028,7 +1050,7 @@ static int tg_set_conf(struct cgroup *cgrp, struct cftype *cft, const char *buf, return ret; tg = blkg_to_tg(ctx.blkg); - td = ctx.blkg->q->td; + sq = &tg->service_queue; if (!ctx.v) ctx.v = -1; @@ -1056,7 +1078,7 @@ static int tg_set_conf(struct cgroup *cgrp, struct cftype *cft, const char *buf, if (tg->flags & THROTL_TG_PENDING) { tg_update_disptime(tg); - throtl_schedule_next_dispatch(td); + throtl_schedule_next_dispatch(sq->parent_sq); } blkg_conf_finish(&ctx); @@ -1121,7 +1143,7 @@ static void throtl_shutdown_wq(struct request_queue *q) { struct throtl_data *td = q->td; - cancel_delayed_work_sync(&td->dispatch_work); + cancel_work_sync(&td->dispatch_work); } static struct blkcg_policy blkcg_policy_throtl = { @@ -1210,7 +1232,7 @@ queue_bio: /* update @tg's dispatch time if @tg was empty before @bio */ if (tg->flags & THROTL_TG_WAS_EMPTY) { tg_update_disptime(tg); - throtl_schedule_next_dispatch(td); + throtl_schedule_next_dispatch(tg->service_queue.parent_sq); } out_unlock: @@ -1273,7 +1295,7 @@ int blk_throtl_init(struct request_queue *q) if (!td) return -ENOMEM; - INIT_DELAYED_WORK(&td->dispatch_work, blk_throtl_dispatch_work_fn); + INIT_WORK(&td->dispatch_work, blk_throtl_dispatch_work_fn); throtl_service_queue_init(&td->service_queue, NULL); q->td = td; -- cgit v1.2.3 From 7f52f98c2a83339b89a27d01296354e5dbb90ad0 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 14 May 2013 13:52:37 -0700 Subject: blk-throttle: implement dispatch looping throtl_select_dispatch() only dispatches throtl_quantum bios on each invocation. blk_throtl_dispatch_work_fn() in turn depends on throtl_schedule_next_dispatch() scheduling the next dispatch window immediately so that undue delays aren't incurred. This effectively chains multiple dispatch work item executions back-to-back when there are more than throtl_quantum bios to dispatch on a given tick. There is no reason to finish the current work item just to repeat it immediately. This patch makes throtl_schedule_next_dispatch() return %false without doing anything if the current dispatch window is still open and updates blk_throtl_dispatch_work_fn() repeat dispatching after cpu_relax() on %false return. This change will help implementing hierarchy support as dispatching will be done from pending_timer and immediate reschedule of timer function isn't supported and doesn't make much sense. While this patch changes how dispatch behaves when there are more than throtl_quantum bios to dispatch on a single tick, the behavior change is immaterial. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-throttle.c | 82 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 56 insertions(+), 26 deletions(-) diff --git a/block/blk-throttle.c b/block/blk-throttle.c index a8d23f0cf357..8ee8e4e0a2ba 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -467,24 +467,41 @@ static void throtl_schedule_pending_timer(struct throtl_service_queue *sq, expires - jiffies, jiffies); } -static void throtl_schedule_next_dispatch(struct throtl_service_queue *sq) +/** + * throtl_schedule_next_dispatch - schedule the next dispatch cycle + * @sq: the service_queue to schedule dispatch for + * @force: force scheduling + * + * Arm @sq->pending_timer so that the next dispatch cycle starts on the + * dispatch time of the first pending child. Returns %true if either timer + * is armed or there's no pending child left. %false if the current + * dispatch window is still open and the caller should continue + * dispatching. + * + * If @force is %true, the dispatch timer is always scheduled and this + * function is guaranteed to return %true. This is to be used when the + * caller can't dispatch itself and needs to invoke pending_timer + * unconditionally. Note that forced scheduling is likely to induce short + * delay before dispatch starts even if @sq->first_pending_disptime is not + * in the future and thus shouldn't be used in hot paths. + */ +static bool throtl_schedule_next_dispatch(struct throtl_service_queue *sq, + bool force) { - struct throtl_data *td = sq_to_td(sq); - /* any pending children left? */ if (!sq->nr_pending) - return; + return true; update_min_dispatch_time(sq); /* is the next dispatch time in the future? */ - if (time_after(sq->first_pending_disptime, jiffies)) { + if (force || time_after(sq->first_pending_disptime, jiffies)) { throtl_schedule_pending_timer(sq, sq->first_pending_disptime); - return; + return true; } - /* kick immediate execution */ - queue_work(kthrotld_workqueue, &td->dispatch_work); + /* tell the caller to continue dispatching */ + return false; } static inline void throtl_start_new_slice(struct throtl_grp *tg, bool rw) @@ -930,39 +947,47 @@ void blk_throtl_dispatch_work_fn(struct work_struct *work) dispatch_work); struct throtl_service_queue *sq = &td->service_queue; struct request_queue *q = td->queue; - unsigned int nr_disp = 0; struct bio_list bio_list_on_stack; struct bio *bio; struct blk_plug plug; - int rw; + bool dispatched = false; + int rw, ret; spin_lock_irq(q->queue_lock); bio_list_init(&bio_list_on_stack); - throtl_log(sq, "dispatch nr_queued=%u read=%u write=%u", - td->nr_queued[READ] + td->nr_queued[WRITE], - td->nr_queued[READ], td->nr_queued[WRITE]); + while (true) { + throtl_log(sq, "dispatch nr_queued=%u read=%u write=%u", + td->nr_queued[READ] + td->nr_queued[WRITE], + td->nr_queued[READ], td->nr_queued[WRITE]); + + ret = throtl_select_dispatch(sq); + if (ret) { + for (rw = READ; rw <= WRITE; rw++) { + bio_list_merge(&bio_list_on_stack, &sq->bio_lists[rw]); + bio_list_init(&sq->bio_lists[rw]); + } + throtl_log(sq, "bios disp=%u", ret); + dispatched = true; + } - nr_disp = throtl_select_dispatch(sq); + if (throtl_schedule_next_dispatch(sq, false)) + break; - if (nr_disp) { - for (rw = READ; rw <= WRITE; rw++) { - bio_list_merge(&bio_list_on_stack, &sq->bio_lists[rw]); - bio_list_init(&sq->bio_lists[rw]); - } - throtl_log(sq, "bios disp=%u", nr_disp); + /* this dispatch windows is still open, relax and repeat */ + spin_unlock_irq(q->queue_lock); + cpu_relax(); + spin_lock_irq(q->queue_lock); } - throtl_schedule_next_dispatch(sq); - spin_unlock_irq(q->queue_lock); /* * If we dispatched some requests, unplug the queue to make sure * immediate dispatch */ - if (nr_disp) { + if (dispatched) { blk_start_plug(&plug); while((bio = bio_list_pop(&bio_list_on_stack))) generic_make_request(bio); @@ -1078,7 +1103,7 @@ static int tg_set_conf(struct cgroup *cgrp, struct cftype *cft, const char *buf, if (tg->flags & THROTL_TG_PENDING) { tg_update_disptime(tg); - throtl_schedule_next_dispatch(sq->parent_sq); + throtl_schedule_next_dispatch(sq->parent_sq, true); } blkg_conf_finish(&ctx); @@ -1229,10 +1254,15 @@ queue_bio: throtl_add_bio_tg(bio, tg); throttled = true; - /* update @tg's dispatch time if @tg was empty before @bio */ + /* + * Update @tg's dispatch time and force schedule dispatch if @tg + * was empty before @bio. The forced scheduling isn't likely to + * cause undue delay as @bio is likely to be dispatched directly if + * its @tg's disptime is not in the future. + */ if (tg->flags & THROTL_TG_WAS_EMPTY) { tg_update_disptime(tg); - throtl_schedule_next_dispatch(tg->service_queue.parent_sq); + throtl_schedule_next_dispatch(tg->service_queue.parent_sq, true); } out_unlock: -- cgit v1.2.3 From 6e1a5704cbbd244a8db2d7d59215cf9a4c9a0d31 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 14 May 2013 13:52:37 -0700 Subject: blk-throttle: dispatch from throtl_pending_timer_fn() Currently, blk_throtl_dispatch_work_fn() is responsible for both dispatching bio's from throtl_grp's according to their limits and then issuing the dispatched bios. This patch moves the dispatch part to throtl_pending_timer_fn() so that the work item is kicked iff there are bio's to issue. This is to avoid work item execution at each step when hierarchy support is enabled. bio's will be dispatched towards the top-level service_queue from the timers at each layer and the work item will only be used to issue the bio's which reached the top-level service_queue. While fetching bio's to issue from bio_lists[], blk_throtl_dispatch_work_fn() fetches all READs before WRITEs. While the original code also dispatched READs first, if multiple throtl_grps are dispatched on the same run, WRITEs from throtl_grp which is dispatched first would precede READs from throtl_grps which are dispatched later. While this is a behavior change, given that the previous code already prioritized READs and block layer generally prioritizes and segregates READs from WRITEs, this isn't likely to make any noticeable differences. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-throttle.c | 69 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 25 deletions(-) diff --git a/block/blk-throttle.c b/block/blk-throttle.c index 8ee8e4e0a2ba..918d22240856 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -932,31 +932,26 @@ static int throtl_select_dispatch(struct throtl_service_queue *parent_sq) return nr_disp; } +/** + * throtl_pending_timer_fn - timer function for service_queue->pending_timer + * @arg: the throtl_service_queue being serviced + * + * This timer is armed when a child throtl_grp with active bio's become + * pending and queued on the service_queue's pending_tree and expires when + * the first child throtl_grp should be dispatched. This function + * dispatches bio's from the children throtl_grps and kicks + * throtl_data->dispatch_work if there are bio's ready to be issued. + */ static void throtl_pending_timer_fn(unsigned long arg) { struct throtl_service_queue *sq = (void *)arg; struct throtl_data *td = sq_to_td(sq); - - queue_work(kthrotld_workqueue, &td->dispatch_work); -} - -/* work function to dispatch throttled bios */ -void blk_throtl_dispatch_work_fn(struct work_struct *work) -{ - struct throtl_data *td = container_of(work, struct throtl_data, - dispatch_work); - struct throtl_service_queue *sq = &td->service_queue; struct request_queue *q = td->queue; - struct bio_list bio_list_on_stack; - struct bio *bio; - struct blk_plug plug; bool dispatched = false; - int rw, ret; + int ret; spin_lock_irq(q->queue_lock); - bio_list_init(&bio_list_on_stack); - while (true) { throtl_log(sq, "dispatch nr_queued=%u read=%u write=%u", td->nr_queued[READ] + td->nr_queued[WRITE], @@ -964,10 +959,6 @@ void blk_throtl_dispatch_work_fn(struct work_struct *work) ret = throtl_select_dispatch(sq); if (ret) { - for (rw = READ; rw <= WRITE; rw++) { - bio_list_merge(&bio_list_on_stack, &sq->bio_lists[rw]); - bio_list_init(&sq->bio_lists[rw]); - } throtl_log(sq, "bios disp=%u", ret); dispatched = true; } @@ -981,13 +972,41 @@ void blk_throtl_dispatch_work_fn(struct work_struct *work) spin_lock_irq(q->queue_lock); } + if (dispatched) + queue_work(kthrotld_workqueue, &td->dispatch_work); + spin_unlock_irq(q->queue_lock); +} - /* - * If we dispatched some requests, unplug the queue to make sure - * immediate dispatch - */ - if (dispatched) { +/** + * blk_throtl_dispatch_work_fn - work function for throtl_data->dispatch_work + * @work: work item being executed + * + * This function is queued for execution when bio's reach the bio_lists[] + * of throtl_data->service_queue. Those bio's are ready and issued by this + * function. + */ +void blk_throtl_dispatch_work_fn(struct work_struct *work) +{ + struct throtl_data *td = container_of(work, struct throtl_data, + dispatch_work); + struct throtl_service_queue *td_sq = &td->service_queue; + struct request_queue *q = td->queue; + struct bio_list bio_list_on_stack; + struct bio *bio; + struct blk_plug plug; + int rw; + + bio_list_init(&bio_list_on_stack); + + spin_lock_irq(q->queue_lock); + for (rw = READ; rw <= WRITE; rw++) { + bio_list_merge(&bio_list_on_stack, &td_sq->bio_lists[rw]); + bio_list_init(&td_sq->bio_lists[rw]); + } + spin_unlock_irq(q->queue_lock); + + if (!bio_list_empty(&bio_list_on_stack)) { blk_start_plug(&plug); while((bio = bio_list_pop(&bio_list_on_stack))) generic_make_request(bio); -- cgit v1.2.3 From 2a12f0dcdad1ba7c0e53bbff8e5f6d0ee7a29882 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 14 May 2013 13:52:37 -0700 Subject: blk-throttle: make blk_throtl_drain() ready for hierarchy The current blk_throtl_drain() assumes that all active throtl_grps are queued on throtl_data->service_queue, which won't be true once hierarchy support is implemented. This patch makes blk_throtl_drain() perform post-order walk of the blkg hierarchy draining each associated throtl_grp, which guarantees that all bios will eventually be pushed to the top-level service_queue in throtl_data. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-throttle.c | 51 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/block/blk-throttle.c b/block/blk-throttle.c index 918d22240856..8c6e13359781 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -1299,6 +1299,28 @@ out: return throttled; } +/* + * Dispatch all bios from all children tg's queued on @parent_sq. On + * return, @parent_sq is guaranteed to not have any active children tg's + * and all bios from previously active tg's are on @parent_sq->bio_lists[]. + */ +static void tg_drain_bios(struct throtl_service_queue *parent_sq) +{ + struct throtl_grp *tg; + + while ((tg = throtl_rb_first(parent_sq))) { + struct throtl_service_queue *sq = &tg->service_queue; + struct bio *bio; + + throtl_dequeue_tg(tg); + + while ((bio = bio_list_peek(&sq->bio_lists[READ]))) + tg_dispatch_one_bio(tg, bio_data_dir(bio)); + while ((bio = bio_list_peek(&sq->bio_lists[WRITE]))) + tg_dispatch_one_bio(tg, bio_data_dir(bio)); + } +} + /** * blk_throtl_drain - drain throttled bios * @q: request_queue to drain throttled bios for @@ -1309,27 +1331,34 @@ void blk_throtl_drain(struct request_queue *q) __releases(q->queue_lock) __acquires(q->queue_lock) { struct throtl_data *td = q->td; - struct throtl_service_queue *parent_sq = &td->service_queue; - struct throtl_grp *tg; + struct blkcg_gq *blkg; + struct cgroup *pos_cgrp; struct bio *bio; int rw; queue_lockdep_assert_held(q); + rcu_read_lock(); - while ((tg = throtl_rb_first(parent_sq))) { - struct throtl_service_queue *sq = &tg->service_queue; + /* + * Drain each tg while doing post-order walk on the blkg tree, so + * that all bios are propagated to td->service_queue. It'd be + * better to walk service_queue tree directly but blkg walk is + * easier. + */ + blkg_for_each_descendant_post(blkg, pos_cgrp, td->queue->root_blkg) + tg_drain_bios(&blkg_to_tg(blkg)->service_queue); - throtl_dequeue_tg(tg); + tg_drain_bios(&td_root_tg(td)->service_queue); - while ((bio = bio_list_peek(&sq->bio_lists[READ]))) - tg_dispatch_one_bio(tg, bio_data_dir(bio)); - while ((bio = bio_list_peek(&sq->bio_lists[WRITE]))) - tg_dispatch_one_bio(tg, bio_data_dir(bio)); - } + /* finally, transfer bios from top-level tg's into the td */ + tg_drain_bios(&td->service_queue); + + rcu_read_unlock(); spin_unlock_irq(q->queue_lock); + /* all bios now should be in td->service_queue, issue them */ for (rw = READ; rw <= WRITE; rw++) - while ((bio = bio_list_pop(&parent_sq->bio_lists[rw]))) + while ((bio = bio_list_pop(&td->service_queue.bio_lists[rw]))) generic_make_request(bio); spin_lock_irq(q->queue_lock); -- cgit v1.2.3 From 9e660acffcd1b5adc4ec1ffba0cbb584f86b8907 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 14 May 2013 13:52:38 -0700 Subject: blk-throttle: make blk_throtl_bio() ready for hierarchy Currently, blk_throtl_bio() issues the passed in bio directly if it's within limits of its associated tg (throtl_grp). This behavior becomes incorrect with hierarchy support as the bio should be accounted to and throttled by the ancestor throtl_grps too. This patch makes the direct issue path of blk_throtl_bio() to loop until it reaches the top-level service_queue or gets throttled. If the former, the bio can be issued directly; otherwise, it gets queued at the first layer it was above limits. As tg->parent_sq is always the top-level service queue currently, this patch in itself doesn't make any behavior differences. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-throttle.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/block/blk-throttle.c b/block/blk-throttle.c index 8c6e13359781..52321a42cd78 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -1239,12 +1239,16 @@ bool blk_throtl_bio(struct request_queue *q, struct bio *bio) sq = &tg->service_queue; - /* throtl is FIFO - if other bios are already queued, should queue */ - if (sq->nr_queued[rw]) - goto queue_bio; + while (true) { + /* throtl is FIFO - if bios are already queued, should queue */ + if (sq->nr_queued[rw]) + break; - /* Bio is with-in rate limit of group */ - if (tg_may_dispatch(tg, bio, NULL)) { + /* if above limits, break to queue */ + if (!tg_may_dispatch(tg, bio, NULL)) + break; + + /* within limits, let's charge and dispatch directly */ throtl_charge_bio(tg, bio); /* @@ -1259,10 +1263,19 @@ bool blk_throtl_bio(struct request_queue *q, struct bio *bio) * So keep on trimming slice even if bio is not queued. */ throtl_trim_slice(tg, rw); - goto out_unlock; + + /* + * @bio passed through this layer without being throttled. + * Climb up the ladder. If we''re already at the top, it + * can be executed directly. + */ + sq = sq->parent_sq; + tg = sq_to_tg(sq); + if (!tg) + goto out_unlock; } -queue_bio: + /* out-of-limit, queue to @tg */ throtl_log(sq, "[%c] bio. bdisp=%llu sz=%u bps=%llu iodisp=%u iops=%u queued=%d/%d", rw == READ ? 'R' : 'W', tg->bytes_disp[rw], bio->bi_size, tg->bps[rw], -- cgit v1.2.3 From 6bc9c2b464fb89eab705da87aa4284171d942369 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 14 May 2013 13:52:38 -0700 Subject: blk-throttle: make tg_dispatch_one_bio() ready for hierarchy tg_dispatch_one_bio() currently assumes that the parent_sq is the top level one and the bio being dispatched is ready to be issued; however, this assumption will be wrong with proper hierarchy support. This patch makes the following changes to make tg_dispatch_on_bio() ready for hiearchy. * throtl_data->nr_queued[] is incremented in blk_throtl_bio() instead of throtl_add_bio_tg() so that throtl_add_bio_tg() can be used to transfer a bio from a child tg to its parent. * tg_dispatch_one_bio() is updated to distinguish whether its parent is another throtl_grp or the throtl_data. If former, the bio is transferred to the parent throtl_grp using throtl_add_bio_tg(). If latter, the bio is ready to be issued and put on the top-level service_queue's bio_lists[] and throtl_data->nr_queued is decremented. As all throtl_grps currently have the top level service_queue as their ->parent_sq, this patch in itself doesn't make any behavior difference. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-throttle.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/block/blk-throttle.c b/block/blk-throttle.c index 52321a42cd78..04202617fda5 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -824,7 +824,6 @@ static void throtl_add_bio_tg(struct bio *bio, struct throtl_grp *tg) /* Take a bio reference on tg */ blkg_get(tg_to_blkg(tg)); sq->nr_queued[rw]++; - tg->td->nr_queued[rw]++; throtl_enqueue_tg(tg); } @@ -855,20 +854,34 @@ static void tg_update_disptime(struct throtl_grp *tg) static void tg_dispatch_one_bio(struct throtl_grp *tg, bool rw) { struct throtl_service_queue *sq = &tg->service_queue; + struct throtl_service_queue *parent_sq = sq->parent_sq; + struct throtl_grp *parent_tg = sq_to_tg(parent_sq); struct bio *bio; bio = bio_list_pop(&sq->bio_lists[rw]); sq->nr_queued[rw]--; - /* Drop bio reference on blkg */ - blkg_put(tg_to_blkg(tg)); - - BUG_ON(tg->td->nr_queued[rw] <= 0); - tg->td->nr_queued[rw]--; throtl_charge_bio(tg, bio); - bio_list_add(&sq->parent_sq->bio_lists[rw], bio); + + /* + * If our parent is another tg, we just need to transfer @bio to + * the parent using throtl_add_bio_tg(). If our parent is + * @td->service_queue, @bio is ready to be issued. Put it on its + * bio_lists[] and decrease total number queued. The caller is + * responsible for issuing these bios. + */ + if (parent_tg) { + throtl_add_bio_tg(bio, parent_tg); + } else { + bio_list_add(&parent_sq->bio_lists[rw], bio); + BUG_ON(tg->td->nr_queued[rw] <= 0); + tg->td->nr_queued[rw]--; + } throtl_trim_slice(tg, rw); + + /* @bio is transferred to parent, drop its blkg reference */ + blkg_put(tg_to_blkg(tg)); } static int throtl_dispatch_tg(struct throtl_grp *tg) @@ -1283,6 +1296,7 @@ bool blk_throtl_bio(struct request_queue *q, struct bio *bio) sq->nr_queued[READ], sq->nr_queued[WRITE]); bio_associate_current(bio); + tg->td->nr_queued[rw]++; throtl_add_bio_tg(bio, tg); throttled = true; -- cgit v1.2.3 From 2e48a530a3a7daebd0cc17866304a36d39b611de Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 14 May 2013 13:52:38 -0700 Subject: blk-throttle: make throtl_pending_timer_fn() ready for hierarchy throtl_pending_timer_fn() currently assumes that the parent_sq is the top level one and the bio's dispatched are ready to be issued; however, this assumption will be wrong with proper hierarchy support. This patch makes the following changes to make throtl_pending_timer_fn() ready for hiearchy. * If the parent_sq isn't the top-level one, update the parent throtl_grp's dispatch time and schedule the next dispatch as necessary. If the parent's dispatch time is now, repeat the function for the parent throtl_grp. * If the parent_sq is the top-level one, kick issue work_item as before. * The debug message printed by throtl_log() now prints out the service_queue's nr_queued[] instead of the total nr_queued as the latter becomes uninteresting and misleading with hierarchical dispatch. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-throttle.c | 40 +++++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/block/blk-throttle.c b/block/blk-throttle.c index 04202617fda5..bc65077f6e43 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -952,23 +952,33 @@ static int throtl_select_dispatch(struct throtl_service_queue *parent_sq) * This timer is armed when a child throtl_grp with active bio's become * pending and queued on the service_queue's pending_tree and expires when * the first child throtl_grp should be dispatched. This function - * dispatches bio's from the children throtl_grps and kicks - * throtl_data->dispatch_work if there are bio's ready to be issued. + * dispatches bio's from the children throtl_grps to the parent + * service_queue. + * + * If the parent's parent is another throtl_grp, dispatching is propagated + * by either arming its pending_timer or repeating dispatch directly. If + * the top-level service_tree is reached, throtl_data->dispatch_work is + * kicked so that the ready bio's are issued. */ static void throtl_pending_timer_fn(unsigned long arg) { struct throtl_service_queue *sq = (void *)arg; + struct throtl_grp *tg = sq_to_tg(sq); struct throtl_data *td = sq_to_td(sq); struct request_queue *q = td->queue; - bool dispatched = false; + struct throtl_service_queue *parent_sq; + bool dispatched; int ret; spin_lock_irq(q->queue_lock); +again: + parent_sq = sq->parent_sq; + dispatched = false; while (true) { throtl_log(sq, "dispatch nr_queued=%u read=%u write=%u", - td->nr_queued[READ] + td->nr_queued[WRITE], - td->nr_queued[READ], td->nr_queued[WRITE]); + sq->nr_queued[READ] + sq->nr_queued[WRITE], + sq->nr_queued[READ], sq->nr_queued[WRITE]); ret = throtl_select_dispatch(sq); if (ret) { @@ -985,9 +995,25 @@ static void throtl_pending_timer_fn(unsigned long arg) spin_lock_irq(q->queue_lock); } - if (dispatched) - queue_work(kthrotld_workqueue, &td->dispatch_work); + if (!dispatched) + goto out_unlock; + if (parent_sq) { + /* @parent_sq is another throl_grp, propagate dispatch */ + if (tg->flags & THROTL_TG_WAS_EMPTY) { + tg_update_disptime(tg); + if (!throtl_schedule_next_dispatch(parent_sq, false)) { + /* window is already open, repeat dispatching */ + sq = parent_sq; + tg = sq_to_tg(sq); + goto again; + } + } + } else { + /* reached the top-level, queue issueing */ + queue_work(kthrotld_workqueue, &td->dispatch_work); + } +out_unlock: spin_unlock_irq(q->queue_lock); } -- cgit v1.2.3 From c5cc2070b45333f40a3f99319b83c8caeb62ec05 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 14 May 2013 13:52:38 -0700 Subject: blk-throttle: add throtl_qnode for dispatch fairness With flat hierarchy, there's only single level of dispatching happening and fairness beyond that point is the responsibility of the rest of the block layer and driver, which usually works out okay; however, with the planned hierarchy support, service_queue->bio_lists[] can be filled up by bios from a single source. While the limits would still be honored, it'd be very easy to starve IOs from siblings or children. To avoid such starvation, this patch implements throtl_qnode and converts service_queue->bio_lists[] to lists of per-source qnodes which in turn contains the bio's. For example, when a bio is dispatched from a child group, the bio doesn't get queued on ->bio_lists[] directly but it first gets queued on the group's qnode which in turn gets queued on service_queue->queued[]. When dispatching for the upper level, the ->queued[] list is consumed in round-robing order so that the dispatch windows is consumed fairly by all IO sources. There are two ways a bio can come to a throtl_grp - directly queued to the group or dispatched from a child. For the former throtl_grp->qnode_on_self[rw] is used. For the latter, the child's ->qnode_on_parent[rw]. Note that this means that the child which is contributing a bio to its parent should stay pinned until all its bios are dispatched to its grand-parent. This patch moves blkg refcnting from bio add/remove spots to qnode activation/deactivation so that the blkg containing an active qnode is always pinned. As child pins the parent, this is sufficient for keeping the relevant sub-tree pinned while bios are in flight. The starvation issue was spotted by Vivek Goyal. v2: The original patch used the same throtl_grp->qnode_on_self/parent for reads and writes causing RWs to be queued incorrectly if there already are outstanding IOs in the other direction. They should be throtl_grp->qnode_on_self/parent[2] so that READs and WRITEs can use different qnodes. Spotted by Vivek Goyal. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-throttle.c | 201 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 176 insertions(+), 25 deletions(-) diff --git a/block/blk-throttle.c b/block/blk-throttle.c index bc65077f6e43..541bd0dabb9a 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -26,6 +26,35 @@ static struct blkcg_policy blkcg_policy_throtl; /* A workqueue to queue throttle related work */ static struct workqueue_struct *kthrotld_workqueue; +/* + * To implement hierarchical throttling, throtl_grps form a tree and bios + * are dispatched upwards level by level until they reach the top and get + * issued. When dispatching bios from the children and local group at each + * level, if the bios are dispatched into a single bio_list, there's a risk + * of a local or child group which can queue many bios at once filling up + * the list starving others. + * + * To avoid such starvation, dispatched bios are queued separately + * according to where they came from. When they are again dispatched to + * the parent, they're popped in round-robin order so that no single source + * hogs the dispatch window. + * + * throtl_qnode is used to keep the queued bios separated by their sources. + * Bios are queued to throtl_qnode which in turn is queued to + * throtl_service_queue and then dispatched in round-robin order. + * + * It's also used to track the reference counts on blkg's. A qnode always + * belongs to a throtl_grp and gets queued on itself or the parent, so + * incrementing the reference of the associated throtl_grp when a qnode is + * queued and decrementing when dequeued is enough to keep the whole blkg + * tree pinned while bios are in flight. + */ +struct throtl_qnode { + struct list_head node; /* service_queue->queued[] */ + struct bio_list bios; /* queued bios */ + struct throtl_grp *tg; /* tg this qnode belongs to */ +}; + struct throtl_service_queue { struct throtl_service_queue *parent_sq; /* the parent service_queue */ @@ -33,7 +62,7 @@ struct throtl_service_queue { * Bios queued directly to this service_queue or dispatched from * children throtl_grp's. */ - struct bio_list bio_lists[2]; /* queued bios [READ/WRITE] */ + struct list_head queued[2]; /* throtl_qnode [READ/WRITE] */ unsigned int nr_queued[2]; /* number of queued bios */ /* @@ -75,6 +104,17 @@ struct throtl_grp { /* this group's service queue */ struct throtl_service_queue service_queue; + /* + * qnode_on_self is used when bios are directly queued to this + * throtl_grp so that local bios compete fairly with bios + * dispatched from children. qnode_on_parent is used when bios are + * dispatched from this throtl_grp into its parent and will compete + * with the sibling qnode_on_parents and the parent's + * qnode_on_self. + */ + struct throtl_qnode qnode_on_self[2]; + struct throtl_qnode qnode_on_parent[2]; + /* * Dispatch time in jiffies. This is the estimated time when group * will unthrottle and is ready to dispatch more bio. It is used as @@ -250,12 +290,95 @@ alloc_stats: goto alloc_stats; } +static void throtl_qnode_init(struct throtl_qnode *qn, struct throtl_grp *tg) +{ + INIT_LIST_HEAD(&qn->node); + bio_list_init(&qn->bios); + qn->tg = tg; +} + +/** + * throtl_qnode_add_bio - add a bio to a throtl_qnode and activate it + * @bio: bio being added + * @qn: qnode to add bio to + * @queued: the service_queue->queued[] list @qn belongs to + * + * Add @bio to @qn and put @qn on @queued if it's not already on. + * @qn->tg's reference count is bumped when @qn is activated. See the + * comment on top of throtl_qnode definition for details. + */ +static void throtl_qnode_add_bio(struct bio *bio, struct throtl_qnode *qn, + struct list_head *queued) +{ + bio_list_add(&qn->bios, bio); + if (list_empty(&qn->node)) { + list_add_tail(&qn->node, queued); + blkg_get(tg_to_blkg(qn->tg)); + } +} + +/** + * throtl_peek_queued - peek the first bio on a qnode list + * @queued: the qnode list to peek + */ +static struct bio *throtl_peek_queued(struct list_head *queued) +{ + struct throtl_qnode *qn = list_first_entry(queued, struct throtl_qnode, node); + struct bio *bio; + + if (list_empty(queued)) + return NULL; + + bio = bio_list_peek(&qn->bios); + WARN_ON_ONCE(!bio); + return bio; +} + +/** + * throtl_pop_queued - pop the first bio form a qnode list + * @queued: the qnode list to pop a bio from + * @tg_to_put: optional out argument for throtl_grp to put + * + * Pop the first bio from the qnode list @queued. After popping, the first + * qnode is removed from @queued if empty or moved to the end of @queued so + * that the popping order is round-robin. + * + * When the first qnode is removed, its associated throtl_grp should be put + * too. If @tg_to_put is NULL, this function automatically puts it; + * otherwise, *@tg_to_put is set to the throtl_grp to put and the caller is + * responsible for putting it. + */ +static struct bio *throtl_pop_queued(struct list_head *queued, + struct throtl_grp **tg_to_put) +{ + struct throtl_qnode *qn = list_first_entry(queued, struct throtl_qnode, node); + struct bio *bio; + + if (list_empty(queued)) + return NULL; + + bio = bio_list_pop(&qn->bios); + WARN_ON_ONCE(!bio); + + if (bio_list_empty(&qn->bios)) { + list_del_init(&qn->node); + if (tg_to_put) + *tg_to_put = qn->tg; + else + blkg_put(tg_to_blkg(qn->tg)); + } else { + list_move_tail(&qn->node, queued); + } + + return bio; +} + /* init a service_queue, assumes the caller zeroed it */ static void throtl_service_queue_init(struct throtl_service_queue *sq, struct throtl_service_queue *parent_sq) { - bio_list_init(&sq->bio_lists[0]); - bio_list_init(&sq->bio_lists[1]); + INIT_LIST_HEAD(&sq->queued[0]); + INIT_LIST_HEAD(&sq->queued[1]); sq->pending_tree = RB_ROOT; sq->parent_sq = parent_sq; setup_timer(&sq->pending_timer, throtl_pending_timer_fn, @@ -272,8 +395,14 @@ static void throtl_pd_init(struct blkcg_gq *blkg) struct throtl_grp *tg = blkg_to_tg(blkg); struct throtl_data *td = blkg->q->td; unsigned long flags; + int rw; throtl_service_queue_init(&tg->service_queue, &td->service_queue); + for (rw = READ; rw <= WRITE; rw++) { + throtl_qnode_init(&tg->qnode_on_self[rw], tg); + throtl_qnode_init(&tg->qnode_on_parent[rw], tg); + } + RB_CLEAR_NODE(&tg->rb_node); tg->td = td; @@ -715,7 +844,7 @@ static bool tg_may_dispatch(struct throtl_grp *tg, struct bio *bio, * queued. */ BUG_ON(tg->service_queue.nr_queued[rw] && - bio != bio_list_peek(&tg->service_queue.bio_lists[rw])); + bio != throtl_peek_queued(&tg->service_queue.queued[rw])); /* If tg->bps = -1, then BW is unlimited */ if (tg->bps[rw] == -1 && tg->iops[rw] == -1) { @@ -806,11 +935,24 @@ static void throtl_charge_bio(struct throtl_grp *tg, struct bio *bio) } } -static void throtl_add_bio_tg(struct bio *bio, struct throtl_grp *tg) +/** + * throtl_add_bio_tg - add a bio to the specified throtl_grp + * @bio: bio to add + * @qn: qnode to use + * @tg: the target throtl_grp + * + * Add @bio to @tg's service_queue using @qn. If @qn is not specified, + * tg->qnode_on_self[] is used. + */ +static void throtl_add_bio_tg(struct bio *bio, struct throtl_qnode *qn, + struct throtl_grp *tg) { struct throtl_service_queue *sq = &tg->service_queue; bool rw = bio_data_dir(bio); + if (!qn) + qn = &tg->qnode_on_self[rw]; + /* * If @tg doesn't currently have any bios queued in the same * direction, queueing @bio can change when @tg should be @@ -820,9 +962,8 @@ static void throtl_add_bio_tg(struct bio *bio, struct throtl_grp *tg) if (!sq->nr_queued[rw]) tg->flags |= THROTL_TG_WAS_EMPTY; - bio_list_add(&sq->bio_lists[rw], bio); - /* Take a bio reference on tg */ - blkg_get(tg_to_blkg(tg)); + throtl_qnode_add_bio(bio, qn, &sq->queued[rw]); + sq->nr_queued[rw]++; throtl_enqueue_tg(tg); } @@ -833,10 +974,10 @@ static void tg_update_disptime(struct throtl_grp *tg) unsigned long read_wait = -1, write_wait = -1, min_wait = -1, disptime; struct bio *bio; - if ((bio = bio_list_peek(&sq->bio_lists[READ]))) + if ((bio = throtl_peek_queued(&sq->queued[READ]))) tg_may_dispatch(tg, bio, &read_wait); - if ((bio = bio_list_peek(&sq->bio_lists[WRITE]))) + if ((bio = throtl_peek_queued(&sq->queued[WRITE]))) tg_may_dispatch(tg, bio, &write_wait); min_wait = min(read_wait, write_wait); @@ -856,9 +997,16 @@ static void tg_dispatch_one_bio(struct throtl_grp *tg, bool rw) struct throtl_service_queue *sq = &tg->service_queue; struct throtl_service_queue *parent_sq = sq->parent_sq; struct throtl_grp *parent_tg = sq_to_tg(parent_sq); + struct throtl_grp *tg_to_put = NULL; struct bio *bio; - bio = bio_list_pop(&sq->bio_lists[rw]); + /* + * @bio is being transferred from @tg to @parent_sq. Popping a bio + * from @tg may put its reference and @parent_sq might end up + * getting released prematurely. Remember the tg to put and put it + * after @bio is transferred to @parent_sq. + */ + bio = throtl_pop_queued(&sq->queued[rw], &tg_to_put); sq->nr_queued[rw]--; throtl_charge_bio(tg, bio); @@ -871,17 +1019,18 @@ static void tg_dispatch_one_bio(struct throtl_grp *tg, bool rw) * responsible for issuing these bios. */ if (parent_tg) { - throtl_add_bio_tg(bio, parent_tg); + throtl_add_bio_tg(bio, &tg->qnode_on_parent[rw], parent_tg); } else { - bio_list_add(&parent_sq->bio_lists[rw], bio); + throtl_qnode_add_bio(bio, &tg->qnode_on_parent[rw], + &parent_sq->queued[rw]); BUG_ON(tg->td->nr_queued[rw] <= 0); tg->td->nr_queued[rw]--; } throtl_trim_slice(tg, rw); - /* @bio is transferred to parent, drop its blkg reference */ - blkg_put(tg_to_blkg(tg)); + if (tg_to_put) + blkg_put(tg_to_blkg(tg_to_put)); } static int throtl_dispatch_tg(struct throtl_grp *tg) @@ -894,7 +1043,7 @@ static int throtl_dispatch_tg(struct throtl_grp *tg) /* Try to dispatch 75% READS and 25% WRITES */ - while ((bio = bio_list_peek(&sq->bio_lists[READ])) && + while ((bio = throtl_peek_queued(&sq->queued[READ])) && tg_may_dispatch(tg, bio, NULL)) { tg_dispatch_one_bio(tg, bio_data_dir(bio)); @@ -904,7 +1053,7 @@ static int throtl_dispatch_tg(struct throtl_grp *tg) break; } - while ((bio = bio_list_peek(&sq->bio_lists[WRITE])) && + while ((bio = throtl_peek_queued(&sq->queued[WRITE])) && tg_may_dispatch(tg, bio, NULL)) { tg_dispatch_one_bio(tg, bio_data_dir(bio)); @@ -1039,10 +1188,9 @@ void blk_throtl_dispatch_work_fn(struct work_struct *work) bio_list_init(&bio_list_on_stack); spin_lock_irq(q->queue_lock); - for (rw = READ; rw <= WRITE; rw++) { - bio_list_merge(&bio_list_on_stack, &td_sq->bio_lists[rw]); - bio_list_init(&td_sq->bio_lists[rw]); - } + for (rw = READ; rw <= WRITE; rw++) + while ((bio = throtl_pop_queued(&td_sq->queued[rw], NULL))) + bio_list_add(&bio_list_on_stack, bio); spin_unlock_irq(q->queue_lock); if (!bio_list_empty(&bio_list_on_stack)) { @@ -1241,6 +1389,7 @@ static struct blkcg_policy blkcg_policy_throtl = { bool blk_throtl_bio(struct request_queue *q, struct bio *bio) { struct throtl_data *td = q->td; + struct throtl_qnode *qn = NULL; struct throtl_grp *tg; struct throtl_service_queue *sq; bool rw = bio_data_dir(bio); @@ -1308,6 +1457,7 @@ bool blk_throtl_bio(struct request_queue *q, struct bio *bio) * Climb up the ladder. If we''re already at the top, it * can be executed directly. */ + qn = &tg->qnode_on_parent[rw]; sq = sq->parent_sq; tg = sq_to_tg(sq); if (!tg) @@ -1323,7 +1473,7 @@ bool blk_throtl_bio(struct request_queue *q, struct bio *bio) bio_associate_current(bio); tg->td->nr_queued[rw]++; - throtl_add_bio_tg(bio, tg); + throtl_add_bio_tg(bio, qn, tg); throttled = true; /* @@ -1367,9 +1517,9 @@ static void tg_drain_bios(struct throtl_service_queue *parent_sq) throtl_dequeue_tg(tg); - while ((bio = bio_list_peek(&sq->bio_lists[READ]))) + while ((bio = throtl_peek_queued(&sq->queued[READ]))) tg_dispatch_one_bio(tg, bio_data_dir(bio)); - while ((bio = bio_list_peek(&sq->bio_lists[WRITE]))) + while ((bio = throtl_peek_queued(&sq->queued[WRITE]))) tg_dispatch_one_bio(tg, bio_data_dir(bio)); } } @@ -1411,7 +1561,8 @@ void blk_throtl_drain(struct request_queue *q) /* all bios now should be in td->service_queue, issue them */ for (rw = READ; rw <= WRITE; rw++) - while ((bio = bio_list_pop(&td->service_queue.bio_lists[rw]))) + while ((bio = throtl_pop_queued(&td->service_queue.queued[rw], + NULL))) generic_make_request(bio); spin_lock_irq(q->queue_lock); -- cgit v1.2.3 From 32ee5bc4787dfbdb280b4d81a338dcdd55918c1e Mon Sep 17 00:00:00 2001 From: Vivek Goyal Date: Tue, 14 May 2013 13:52:38 -0700 Subject: blk-throttle: Account for child group's start time in parent while bio climbs up With the planned proper hierarchy support, a bio will climb up the tree before actually being dispatched. This makes sure bio is also subjected to parent's throttling limits, if any. It might happen that parent is idle and when bio is transferred to parent, a new slice starts fresh. But that is incorrect as parents wait time should have started when bio was queued in child group and causes IOs to be throttled more than configured as they climb the hierarchy. Given the fact that we have not written hierarchical algorithm in a way where child's and parents time slices are synchronized, we transfer the child's start time to parent if parent was idling. If parent was busy doing dispatch of other bios all this while, this is not an issue. Child's slice start time is passed to parent. Parent looks at its last expired slice start time. If child's start time is after parents old start time, that means parent had been idle and after parent went idle, child had an IO queued. So use child's start time as parent start time. If parent's start time is after child's start time, that means, when IO got queued in child group, parent was not idle. But later it dispatched some IO, its slice got trimmed and then it went idle. After a while child's request got shifted in parent group. In this case use parent's old start time as new start time as that's the duration of slice we did not use. This logic is far from perfect as if there are multiple childs then first child transferring the bio decides the start time while a bio might have queued up even earlier in other child, which is yet to be transferred up to parent. In that case we will lose time and bandwidth in parent. This patch is just an approximation to make situation somewhat better. Signed-off-by: Vivek Goyal Signed-off-by: Tejun Heo --- block/blk-throttle.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/block/blk-throttle.c b/block/blk-throttle.c index 541bd0dabb9a..7477f332c8dc 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -633,6 +633,28 @@ static bool throtl_schedule_next_dispatch(struct throtl_service_queue *sq, return false; } +static inline void throtl_start_new_slice_with_credit(struct throtl_grp *tg, + bool rw, unsigned long start) +{ + tg->bytes_disp[rw] = 0; + tg->io_disp[rw] = 0; + + /* + * Previous slice has expired. We must have trimmed it after last + * bio dispatch. That means since start of last slice, we never used + * that bandwidth. Do try to make use of that bandwidth while giving + * credit. + */ + if (time_after_eq(start, tg->slice_start[rw])) + tg->slice_start[rw] = start; + + tg->slice_end[rw] = jiffies + throtl_slice; + throtl_log(&tg->service_queue, + "[%c] new slice with credit start=%lu end=%lu jiffies=%lu", + rw == READ ? 'R' : 'W', tg->slice_start[rw], + tg->slice_end[rw], jiffies); +} + static inline void throtl_start_new_slice(struct throtl_grp *tg, bool rw) { tg->bytes_disp[rw] = 0; @@ -992,6 +1014,16 @@ static void tg_update_disptime(struct throtl_grp *tg) tg->flags &= ~THROTL_TG_WAS_EMPTY; } +static void start_parent_slice_with_credit(struct throtl_grp *child_tg, + struct throtl_grp *parent_tg, bool rw) +{ + if (throtl_slice_used(parent_tg, rw)) { + throtl_start_new_slice_with_credit(parent_tg, rw, + child_tg->slice_start[rw]); + } + +} + static void tg_dispatch_one_bio(struct throtl_grp *tg, bool rw) { struct throtl_service_queue *sq = &tg->service_queue; @@ -1020,6 +1052,7 @@ static void tg_dispatch_one_bio(struct throtl_grp *tg, bool rw) */ if (parent_tg) { throtl_add_bio_tg(bio, &tg->qnode_on_parent[rw], parent_tg); + start_parent_slice_with_credit(tg, parent_tg, rw); } else { throtl_qnode_add_bio(bio, &tg->qnode_on_parent[rw], &parent_sq->queued[rw]); -- cgit v1.2.3 From 693e751e70843c29884cde326016e746fa16073a Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 14 May 2013 13:52:38 -0700 Subject: blk-throttle: implement throtl_grp->has_rules[] blk_throtl_bio() has a quick exit path for throtl_grps without limits configured. It looks at the bps and iops limits and if both are not configured, the bio is issued immediately. While this is correct in the current flat hierarchy as each throtl_grp behaves completely independently, it would become wrong in proper hierarchy mode. A group without any limits could still be limited by one of its ancestors and bio's queued for such group should not bypass blk-throtl. As having a quick bypass mechanism is beneficial, this patch reimplements the mechanism such that it's correct even with proper hierarchy. throtl_grp->has_rules[] is added. These booleans are updated for the whole subtree whenever a config is updated so that has_rules[] of the whole subtree stays synchronized. They're also updated when a new throtl_grp comes online so that it can't escape the limits of its ancestors. As no throtl_grp has another throtl_grp as parent now, this patch doesn't yet make any behavior differences. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-throttle.c | 49 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/block/blk-throttle.c b/block/blk-throttle.c index 7477f332c8dc..27f006bb363b 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -124,6 +124,9 @@ struct throtl_grp { unsigned int flags; + /* are there any throtl rules between this group and td? */ + bool has_rules[2]; + /* bytes per second rate limits */ uint64_t bps[2]; @@ -422,6 +425,30 @@ static void throtl_pd_init(struct blkcg_gq *blkg) spin_unlock_irqrestore(&tg_stats_alloc_lock, flags); } +/* + * Set has_rules[] if @tg or any of its parents have limits configured. + * This doesn't require walking up to the top of the hierarchy as the + * parent's has_rules[] is guaranteed to be correct. + */ +static void tg_update_has_rules(struct throtl_grp *tg) +{ + struct throtl_grp *parent_tg = sq_to_tg(tg->service_queue.parent_sq); + int rw; + + for (rw = READ; rw <= WRITE; rw++) + tg->has_rules[rw] = (parent_tg && parent_tg->has_rules[rw]) || + (tg->bps[rw] != -1 || tg->iops[rw] != -1); +} + +static void throtl_pd_online(struct blkcg_gq *blkg) +{ + /* + * We don't want new groups to escape the limits of its ancestors. + * Update has_rules[] after a new group is brought online. + */ + tg_update_has_rules(blkg_to_tg(blkg)); +} + static void throtl_pd_exit(struct blkcg_gq *blkg) { struct throtl_grp *tg = blkg_to_tg(blkg); @@ -843,12 +870,6 @@ static bool tg_with_in_bps_limit(struct throtl_grp *tg, struct bio *bio, return 0; } -static bool tg_no_rule_group(struct throtl_grp *tg, bool rw) { - if (tg->bps[rw] == -1 && tg->iops[rw] == -1) - return 1; - return 0; -} - /* * Returns whether one can dispatch a bio or not. Also returns approx number * of jiffies to wait before this bio is with-in IO rate and can be dispatched @@ -1307,6 +1328,8 @@ static int tg_set_conf(struct cgroup *cgrp, struct cftype *cft, const char *buf, struct blkg_conf_ctx ctx; struct throtl_grp *tg; struct throtl_service_queue *sq; + struct blkcg_gq *blkg; + struct cgroup *pos_cgrp; int ret; ret = blkg_conf_prep(blkcg, &blkcg_policy_throtl, buf, &ctx); @@ -1329,6 +1352,17 @@ static int tg_set_conf(struct cgroup *cgrp, struct cftype *cft, const char *buf, tg->bps[READ], tg->bps[WRITE], tg->iops[READ], tg->iops[WRITE]); + /* + * Update has_rules[] flags for the updated tg's subtree. A tg is + * considered to have rules if either the tg itself or any of its + * ancestors has rules. This identifies groups without any + * restrictions in the whole hierarchy and allows them to bypass + * blk-throttle. + */ + tg_update_has_rules(tg); + blkg_for_each_descendant_pre(blkg, pos_cgrp, ctx.blkg) + tg_update_has_rules(blkg_to_tg(blkg)); + /* * We're already holding queue_lock and know @tg is valid. Let's * apply the new config directly. @@ -1415,6 +1449,7 @@ static struct blkcg_policy blkcg_policy_throtl = { .cftypes = throtl_files, .pd_init_fn = throtl_pd_init, + .pd_online_fn = throtl_pd_online, .pd_exit_fn = throtl_pd_exit, .pd_reset_stats_fn = throtl_pd_reset_stats, }; @@ -1442,7 +1477,7 @@ bool blk_throtl_bio(struct request_queue *q, struct bio *bio) blkcg = bio_blkcg(bio); tg = throtl_lookup_tg(td, blkcg); if (tg) { - if (tg_no_rule_group(tg, rw)) { + if (!tg->has_rules[rw]) { throtl_update_dispatch_stats(tg_to_blkg(tg), bio->bi_size, bio->bi_rw); goto out_unlock_rcu; -- cgit v1.2.3 From 9138125beabbb76b4a373d4a619870f6f5d86fc5 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 14 May 2013 13:52:38 -0700 Subject: blk-throttle: implement proper hierarchy support With the recent updates, blk-throttle is finally ready for proper hierarchy support. Dispatching now honors service_queue->parent_sq and propagates correctly. The only thing missing is setting ->parent_sq correctly so that throtl_grp hierarchy matches the cgroup hierarchy. This patch updates throtl_pd_init() such that service_queues form the same hierarchy as the cgroup hierarchy if sane_behavior is enabled. As this concludes proper hierarchy support for blkcg, the shameful .broken_hierarchy tag is removed from blkio_subsys. v2: Updated blkio-controller.txt as suggested by Vivek. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal Cc: Li Zefan --- Documentation/cgroups/blkio-controller.txt | 29 +++++++++++++++-------------- block/blk-cgroup.c | 8 -------- block/blk-throttle.c | 22 +++++++++++++++++++++- include/linux/cgroup.h | 2 ++ 4 files changed, 38 insertions(+), 23 deletions(-) diff --git a/Documentation/cgroups/blkio-controller.txt b/Documentation/cgroups/blkio-controller.txt index da272c8f44e7..cd556b914786 100644 --- a/Documentation/cgroups/blkio-controller.txt +++ b/Documentation/cgroups/blkio-controller.txt @@ -94,11 +94,13 @@ Throttling/Upper Limit policy Hierarchical Cgroups ==================== -- Currently only CFQ supports hierarchical groups. For throttling, - cgroup interface does allow creation of hierarchical cgroups and - internally it treats them as flat hierarchy. - If somebody created a hierarchy like as follows. +Both CFQ and throttling implement hierarchy support; however, +throttling's hierarchy support is enabled iff "sane_behavior" is +enabled from cgroup side, which currently is a development option and +not publicly available. + +If somebody created a hierarchy like as follows. root / \ @@ -106,21 +108,20 @@ Hierarchical Cgroups | test3 - CFQ will handle the hierarchy correctly but and throttling will - practically treat all groups at same level. For details on CFQ - hierarchy support, refer to Documentation/block/cfq-iosched.txt. - Throttling will treat the hierarchy as if it looks like the - following. +CFQ by default and throttling with "sane_behavior" will handle the +hierarchy correctly. For details on CFQ hierarchy support, refer to +Documentation/block/cfq-iosched.txt. For throttling, all limits apply +to the whole subtree while all statistics are local to the IOs +directly generated by tasks in that cgroup. + +Throttling without "sane_behavior" enabled from cgroup side will +practically treat all groups at same level as if it looks like the +following. pivot / / \ \ root test1 test2 test3 - Nesting cgroups, while allowed, isn't officially supported and blkio - genereates warning when cgroups nest. Once throttling implements - hierarchy support, hierarchy will be supported and the warning will - be removed. - Various user visible config options =================================== CONFIG_BLK_CGROUP diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index d0747605f56c..290792a13e3c 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -911,14 +911,6 @@ struct cgroup_subsys blkio_subsys = { .subsys_id = blkio_subsys_id, .base_cftypes = blkcg_files, .module = THIS_MODULE, - - /* - * blkio subsystem is utterly broken in terms of hierarchy support. - * It treats all cgroups equally regardless of where they're - * located in the hierarchy - all cgroups are treated as if they're - * right below the root. Fix it and remove the following. - */ - .broken_hierarchy = true, }; EXPORT_SYMBOL_GPL(blkio_subsys); diff --git a/block/blk-throttle.c b/block/blk-throttle.c index 27f006bb363b..08a32dfd3844 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -397,10 +397,30 @@ static void throtl_pd_init(struct blkcg_gq *blkg) { struct throtl_grp *tg = blkg_to_tg(blkg); struct throtl_data *td = blkg->q->td; + struct throtl_service_queue *parent_sq; unsigned long flags; int rw; - throtl_service_queue_init(&tg->service_queue, &td->service_queue); + /* + * If sane_hierarchy is enabled, we switch to properly hierarchical + * behavior where limits on a given throtl_grp are applied to the + * whole subtree rather than just the group itself. e.g. If 16M + * read_bps limit is set on the root group, the whole system can't + * exceed 16M for the device. + * + * If sane_hierarchy is not enabled, the broken flat hierarchy + * behavior is retained where all throtl_grps are treated as if + * they're all separate root groups right below throtl_data. + * Limits of a group don't interact with limits of other groups + * regardless of the position of the group in the hierarchy. + */ + parent_sq = &td->service_queue; + + if (cgroup_sane_behavior(blkg->blkcg->css.cgroup) && blkg->parent) + parent_sq = &blkg_to_tg(blkg->parent)->service_queue; + + throtl_service_queue_init(&tg->service_queue, parent_sq); + for (rw = READ; rw <= WRITE; rw++) { throtl_qnode_init(&tg->qnode_on_self[rw], tg); throtl_qnode_init(&tg->qnode_on_parent[rw], tg); diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 5047355b9a0f..09f1a1408ae0 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -272,6 +272,8 @@ enum { * - memcg: use_hierarchy is on by default and the cgroup file for * the flag is not created. * + * - blkcg: blk-throttle becomes properly hierarchical. + * * The followings are planned changes. * * - release_agent will be disallowed once replacement notification -- cgit v1.2.3 From 2fa2fe9a142cf8c8a30916ccf7ca6a27019fd6d2 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 7 May 2013 23:34:16 +0200 Subject: drm/i915: panel fitter hw state readout&check support Pfit state readout is a bit ugly on gen2/3 due to the intermingling with the lvds state, but alas. Also note that since state is always cleared to zero we can unconditonally compare all the state and completely neglect the actual platform we're running on. v2: Properly check for the pfit power domain on haswell. v3: Don't check pgm_ratios on gen4+, they're auto-computed by the hw. v4: Properly clear the lvds border bits, upset the state checker a bit. v5: Unconditionally read out panel dither settings on gen2/3. Reviewed-by: Mika Kuoppala Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 67 ++++++++++++++++++++++++++++++++++-- drivers/gpu/drm/i915/intel_lvds.c | 1 + 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index e9d01e5d6ccb..12a92edbdd1a 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4976,6 +4976,36 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, return ret; } +static void i9xx_get_pfit_config(struct intel_crtc *crtc, + struct intel_crtc_config *pipe_config) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + uint32_t tmp; + + tmp = I915_READ(PFIT_CONTROL); + + if (INTEL_INFO(dev)->gen < 4) { + if (crtc->pipe != PIPE_B) + return; + + /* gen2/3 store dither state in pfit control, needs to match */ + pipe_config->gmch_pfit.control = tmp & PANEL_8TO6_DITHER_ENABLE; + } else { + if ((tmp & PFIT_PIPE_MASK) != (crtc->pipe << PFIT_PIPE_SHIFT)) + return; + } + + if (!(tmp & PFIT_ENABLE)) + return; + + pipe_config->gmch_pfit.control = I915_READ(PFIT_CONTROL); + pipe_config->gmch_pfit.pgm_ratios = I915_READ(PFIT_PGM_RATIOS); + if (INTEL_INFO(dev)->gen < 5) + pipe_config->gmch_pfit.lvds_border_bits = + I915_READ(LVDS) & LVDS_BORDER_ENABLE; +} + static bool i9xx_get_pipe_config(struct intel_crtc *crtc, struct intel_crtc_config *pipe_config) { @@ -4989,6 +5019,8 @@ static bool i9xx_get_pipe_config(struct intel_crtc *crtc, intel_get_pipe_timings(crtc, pipe_config); + i9xx_get_pfit_config(crtc, pipe_config); + return true; } @@ -5820,6 +5852,21 @@ static void ironlake_get_fdi_m_n_config(struct intel_crtc *crtc, & TU_SIZE_MASK) >> TU_SIZE_SHIFT) + 1; } +static void ironlake_get_pfit_config(struct intel_crtc *crtc, + struct intel_crtc_config *pipe_config) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + uint32_t tmp; + + tmp = I915_READ(PF_CTL(crtc->pipe)); + + if (tmp & PF_ENABLE) { + pipe_config->pch_pfit.pos = I915_READ(PF_WIN_POS(crtc->pipe)); + pipe_config->pch_pfit.size = I915_READ(PF_WIN_SZ(crtc->pipe)); + } +} + static bool ironlake_get_pipe_config(struct intel_crtc *crtc, struct intel_crtc_config *pipe_config) { @@ -5843,6 +5890,8 @@ static bool ironlake_get_pipe_config(struct intel_crtc *crtc, intel_get_pipe_timings(crtc, pipe_config); + ironlake_get_pfit_config(crtc, pipe_config); + return true; } @@ -5962,6 +6011,7 @@ static bool haswell_get_pipe_config(struct intel_crtc *crtc, struct drm_device *dev = crtc->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; enum transcoder cpu_transcoder = crtc->config.cpu_transcoder; + enum intel_display_power_domain pfit_domain; uint32_t tmp; if (!intel_display_power_enabled(dev, @@ -5991,6 +6041,10 @@ static bool haswell_get_pipe_config(struct intel_crtc *crtc, intel_get_pipe_timings(crtc, pipe_config); + pfit_domain = POWER_DOMAIN_PIPE_PANEL_FITTER(crtc->pipe); + if (intel_display_power_enabled(dev, pfit_domain)) + ironlake_get_pfit_config(crtc, pipe_config); + return true; } @@ -7956,7 +8010,8 @@ intel_modeset_update_state(struct drm_device *dev, unsigned prepare_pipes) if (mask & (1 <<(intel_crtc)->pipe)) static bool -intel_pipe_config_compare(struct intel_crtc_config *current_config, +intel_pipe_config_compare(struct drm_device *dev, + struct intel_crtc_config *current_config, struct intel_crtc_config *pipe_config) { #define PIPE_CONF_CHECK_I(name) \ @@ -8005,6 +8060,14 @@ intel_pipe_config_compare(struct intel_crtc_config *current_config, PIPE_CONF_CHECK_I(requested_mode.hdisplay); PIPE_CONF_CHECK_I(requested_mode.vdisplay); + PIPE_CONF_CHECK_I(gmch_pfit.control); + /* pfit ratios are autocomputed by the hw on gen4+ */ + if (INTEL_INFO(dev)->gen < 4) + PIPE_CONF_CHECK_I(gmch_pfit.pgm_ratios); + PIPE_CONF_CHECK_I(gmch_pfit.lvds_border_bits); + PIPE_CONF_CHECK_I(pch_pfit.pos); + PIPE_CONF_CHECK_I(pch_pfit.size); + #undef PIPE_CONF_CHECK_I #undef PIPE_CONF_CHECK_FLAGS @@ -8116,7 +8179,7 @@ intel_modeset_check_state(struct drm_device *dev) "(expected %i, found %i)\n", crtc->active, active); WARN(active && - !intel_pipe_config_compare(&crtc->config, &pipe_config), + !intel_pipe_config_compare(dev, &crtc->config, &pipe_config), "pipe state doesn't match!\n"); } } diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index e34e290c29eb..36fe291172ee 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -116,6 +116,7 @@ static void intel_pre_pll_enable_lvds(struct intel_encoder *encoder) } /* set the corresponsding LVDS_BORDER bit */ + temp &= ~LVDS_BORDER_ENABLE; temp |= intel_crtc->config.gmch_pfit.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. -- cgit v1.2.3 From 3f8dce3ade6ae025a8a77e7b74ec58a5c26b17e2 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 8 May 2013 10:36:30 +0200 Subject: drm/i915: Use pipe_config state to disable ilk+ pfit No more need to guard the write with a power well check on Haswell now that we have proper pfit state readout: We can simply only clear the pfit if it's actually on. This removes some duplication of knowledge between the haswell pfit disable and pfit state readout code about. While at it extract a little helper for this. Reviewed-by: Mika Kuoppala Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 12a92edbdd1a..33544a256f15 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3410,6 +3410,21 @@ static void haswell_crtc_enable(struct drm_crtc *crtc) intel_wait_for_vblank(dev, intel_crtc->pipe); } +static void ironlake_pfit_disable(struct intel_crtc *crtc) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + int pipe = crtc->pipe; + + /* To avoid upsetting the power well on haswell only disable the pfit if + * it's in use. The hw state code will make sure we get this right. */ + if (crtc->config.pch_pfit.size) { + I915_WRITE(PF_CTL(pipe), 0); + I915_WRITE(PF_WIN_POS(pipe), 0); + I915_WRITE(PF_WIN_SZ(pipe), 0); + } +} + static void ironlake_crtc_disable(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; @@ -3439,9 +3454,7 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) intel_set_pch_fifo_underrun_reporting(dev, pipe, false); intel_disable_pipe(dev_priv, pipe); - /* Disable PF */ - I915_WRITE(PF_CTL(pipe), 0); - I915_WRITE(PF_WIN_SZ(pipe), 0); + ironlake_pfit_disable(intel_crtc); for_each_encoder_on_crtc(dev, crtc, encoder) if (encoder->post_disable) @@ -3524,14 +3537,7 @@ static void haswell_crtc_disable(struct drm_crtc *crtc) intel_ddi_disable_transcoder_func(dev_priv, cpu_transcoder); - /* XXX: Once we have proper panel fitter state tracking implemented with - * hardware state read/check support we should switch to only disable - * the panel fitter when we know it's used. */ - if (intel_display_power_enabled(dev, - POWER_DOMAIN_PIPE_PANEL_FITTER(pipe))) { - I915_WRITE(PF_CTL(pipe), 0); - I915_WRITE(PF_WIN_SZ(pipe), 0); - } + ironlake_pfit_disable(intel_crtc); intel_ddi_disable_pipe_clock(intel_crtc); -- cgit v1.2.3 From 328d8e829b9a05814af4722c1091d62c5533c4f8 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 8 May 2013 10:36:31 +0200 Subject: drm/i915: Use pipe config state to control gmch pfit enable/disable Allows us to rip out a few fragile checks (which are duplicated in the hw state readout now, too). Also prepares us a bit for more than one panel/pfit. Reviewed-by: Mika Kuoppala Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 33544a256f15..7358e4e9761e 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3624,8 +3624,7 @@ static void i9xx_pfit_enable(struct intel_crtc *crtc) struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc_config *pipe_config = &crtc->config; - if (!(intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_EDP) || - intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_LVDS))) + if (!crtc->config.gmch_pfit.control) return; WARN_ON(I915_READ(PFIT_CONTROL) & PFIT_ENABLE); @@ -3744,20 +3743,15 @@ static void i9xx_pfit_disable(struct intel_crtc *crtc) { struct drm_device *dev = crtc->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - enum pipe pipe; - uint32_t pctl = I915_READ(PFIT_CONTROL); - assert_pipe_disabled(dev_priv, crtc->pipe); + if (!crtc->config.gmch_pfit.control) + return; - if (INTEL_INFO(dev)->gen >= 4) - pipe = (pctl & PFIT_PIPE_MASK) >> PFIT_PIPE_SHIFT; - else - pipe = PIPE_B; + assert_pipe_disabled(dev_priv, crtc->pipe); - if (pipe == crtc->pipe) { - DRM_DEBUG_DRIVER("disabling pfit, current: 0x%08x\n", pctl); - I915_WRITE(PFIT_CONTROL, 0); - } + DRM_DEBUG_DRIVER("disabling pfit, current: 0x%08x\n", + I915_READ(PFIT_CONTROL)); + I915_WRITE(PFIT_CONTROL, 0); } static void i9xx_crtc_disable(struct drm_crtc *crtc) -- cgit v1.2.3 From be87f75efed8248d74c8ec56e997de989ecc963e Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 14 May 2013 22:19:47 +0200 Subject: ASoC: ep93xx-i2s: Staticize non exported struct The ep93xx_i2s_dma_data struct is not used outside of ep93xx-i2s.c, so make it static. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/cirrus/ep93xx-i2s.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/cirrus/ep93xx-i2s.c b/sound/soc/cirrus/ep93xx-i2s.c index ef731e687ebb..17ad70bca9fe 100644 --- a/sound/soc/cirrus/ep93xx-i2s.c +++ b/sound/soc/cirrus/ep93xx-i2s.c @@ -63,7 +63,7 @@ struct ep93xx_i2s_info { void __iomem *regs; }; -struct ep93xx_dma_data ep93xx_i2s_dma_data[] = { +static struct ep93xx_dma_data ep93xx_i2s_dma_data[] = { [SNDRV_PCM_STREAM_PLAYBACK] = { .name = "i2s-pcm-out", .port = EP93XX_DMA_I2S1, -- cgit v1.2.3 From 5d0c8a58747c84efe93fb632ae25eb377aea1fa0 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 14 May 2013 22:19:48 +0200 Subject: ASoC: kirkwood-dma: Staticize non exported struct The kirkwood_dma_ops struct is not used outside of kirkwood-dma.c, so make it static. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/kirkwood/kirkwood-dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/kirkwood/kirkwood-dma.c b/sound/soc/kirkwood/kirkwood-dma.c index d3d4bdca1cc6..a9f14530c3db 100644 --- a/sound/soc/kirkwood/kirkwood-dma.c +++ b/sound/soc/kirkwood/kirkwood-dma.c @@ -289,7 +289,7 @@ static snd_pcm_uframes_t kirkwood_dma_pointer(struct snd_pcm_substream return count; } -struct snd_pcm_ops kirkwood_dma_ops = { +static struct snd_pcm_ops kirkwood_dma_ops = { .open = kirkwood_dma_open, .close = kirkwood_dma_close, .ioctl = snd_pcm_lib_ioctl, -- cgit v1.2.3 From 169cc48982f2583de1fea89e7becb1304730a34e Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 14 May 2013 22:19:49 +0200 Subject: ASoC: spear: spdif_in: Staticize non exported struct The spdif_in_dai struct is not used outside of spdif_in.c, so make it static. Signed-off-by: Lars-Peter Clausen Acked-by: Rajeev Kumar Signed-off-by: Mark Brown --- sound/soc/spear/spdif_in.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/spear/spdif_in.c b/sound/soc/spear/spdif_in.c index 643ada6a1fa5..f0071ddbfa7d 100644 --- a/sound/soc/spear/spdif_in.c +++ b/sound/soc/spear/spdif_in.c @@ -152,7 +152,7 @@ static struct snd_soc_dai_ops spdif_in_dai_ops = { .hw_params = spdif_in_hw_params, }; -struct snd_soc_dai_driver spdif_in_dai = { +static struct snd_soc_dai_driver spdif_in_dai = { .probe = spdif_in_dai_probe, .capture = { .channels_min = 2, -- cgit v1.2.3 From 19c7efcd24a71d0f707cecd3c0cee5f4a1289b1e Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 14 May 2013 22:19:50 +0200 Subject: ASoC: spear: spdif_out: Staticize unexported function The spdif_soc_dai_probe function is only used in spdif_out.c, so make it static. Signed-off-by: Lars-Peter Clausen Acked-by: Rajeev Kumar Signed-off-by: Mark Brown --- sound/soc/spear/spdif_out.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/spear/spdif_out.c b/sound/soc/spear/spdif_out.c index fd25cc5efe27..4bde5123cea6 100644 --- a/sound/soc/spear/spdif_out.c +++ b/sound/soc/spear/spdif_out.c @@ -240,7 +240,7 @@ static const struct snd_kcontrol_new spdif_out_controls[] = { spdif_mute_get, spdif_mute_put), }; -int spdif_soc_dai_probe(struct snd_soc_dai *dai) +static int spdif_soc_dai_probe(struct snd_soc_dai *dai) { struct spdif_out_dev *host = snd_soc_dai_get_drvdata(dai); -- cgit v1.2.3 From 60e10d2fb0f0739a5862311258e10520accc9259 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 14 May 2013 22:19:51 +0200 Subject: ASoC: mmp-pcm: Staticize non exported structs and functions The mmp_pcm_ops and mmp_soc_platform struct as well as the mmp_pcm_new() function are only used in mmp-pcm.c, so make them static. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/pxa/mmp-pcm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/pxa/mmp-pcm.c b/sound/soc/pxa/mmp-pcm.c index 349930015264..5d57e071cdf5 100644 --- a/sound/soc/pxa/mmp-pcm.c +++ b/sound/soc/pxa/mmp-pcm.c @@ -147,7 +147,7 @@ static int mmp_pcm_mmap(struct snd_pcm_substream *substream, vma->vm_end - vma->vm_start, vma->vm_page_prot); } -struct snd_pcm_ops mmp_pcm_ops = { +static struct snd_pcm_ops mmp_pcm_ops = { .open = mmp_pcm_open, .close = snd_dmaengine_pcm_close_release_chan, .ioctl = snd_pcm_lib_ioctl, @@ -208,7 +208,7 @@ static int mmp_pcm_preallocate_dma_buffer(struct snd_pcm_substream *substream, return 0; } -int mmp_pcm_new(struct snd_soc_pcm_runtime *rtd) +static int mmp_pcm_new(struct snd_soc_pcm_runtime *rtd) { struct snd_pcm_substream *substream; struct snd_pcm *pcm = rtd->pcm; @@ -229,7 +229,7 @@ err: return ret; } -struct snd_soc_platform_driver mmp_soc_platform = { +static struct snd_soc_platform_driver mmp_soc_platform = { .ops = &mmp_pcm_ops, .pcm_new = mmp_pcm_new, .pcm_free = mmp_pcm_free_dma_buffers, -- cgit v1.2.3 From 5d9ff402152fe9421c5ed86b4f651a8c62de7c7a Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 14 May 2013 22:19:52 +0200 Subject: ASoC: mmp-sspa: Staticize non exported struct The mmp_sspa_dai struct is only used in mmp-sspa.c, so make it static. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/pxa/mmp-sspa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/pxa/mmp-sspa.c b/sound/soc/pxa/mmp-sspa.c index a64779980177..62142ce367c7 100644 --- a/sound/soc/pxa/mmp-sspa.c +++ b/sound/soc/pxa/mmp-sspa.c @@ -388,7 +388,7 @@ static struct snd_soc_dai_ops mmp_sspa_dai_ops = { .set_fmt = mmp_sspa_set_dai_fmt, }; -struct snd_soc_dai_driver mmp_sspa_dai = { +static struct snd_soc_dai_driver mmp_sspa_dai = { .probe = mmp_sspa_probe, .playback = { .channels_min = 1, -- cgit v1.2.3 From 19b6317b8c78a51c41b3d7abf14fdbb0df8f41b7 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 14 May 2013 22:19:53 +0200 Subject: ASoC: bf5xx-tdm-pcm: Staticize non exported struct The bf5xx_tdm_pcm_ops struct is only used in bf5xx-tdm-pcm.c, so make it static. Cc: Scott Jiang Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/blackfin/bf5xx-tdm-pcm.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sound/soc/blackfin/bf5xx-tdm-pcm.c b/sound/soc/blackfin/bf5xx-tdm-pcm.c index 0e6b888bb4cc..19e855d5677e 100644 --- a/sound/soc/blackfin/bf5xx-tdm-pcm.c +++ b/sound/soc/blackfin/bf5xx-tdm-pcm.c @@ -229,8 +229,7 @@ static int bf5xx_pcm_silence(struct snd_pcm_substream *substream, return 0; } - -struct snd_pcm_ops bf5xx_pcm_tdm_ops = { +static struct snd_pcm_ops bf5xx_pcm_tdm_ops = { .open = bf5xx_pcm_open, .ioctl = snd_pcm_lib_ioctl, .hw_params = bf5xx_pcm_hw_params, -- cgit v1.2.3 From cdeecac4e610ce1094497740ef84aa837e2e874f Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 14 May 2013 22:19:54 +0200 Subject: ASoC: mop500_ab8500: Staticize non exported functions The mop500_ab8500_startup(), the mop500_ab8500_shutdown() and the mop500_ab8500_hw_params() function are not used outside of mop500_ab8500, so make them static. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/ux500/mop500_ab8500.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/ux500/mop500_ab8500.c b/sound/soc/ux500/mop500_ab8500.c index 44e25a291044..884a36224fb1 100644 --- a/sound/soc/ux500/mop500_ab8500.c +++ b/sound/soc/ux500/mop500_ab8500.c @@ -183,7 +183,7 @@ static struct snd_kcontrol_new mop500_ab8500_ctrls[] = { /* ASoC */ -int mop500_ab8500_startup(struct snd_pcm_substream *substream) +static int mop500_ab8500_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -192,7 +192,7 @@ int mop500_ab8500_startup(struct snd_pcm_substream *substream) snd_soc_card_get_drvdata(rtd->card)); } -void mop500_ab8500_shutdown(struct snd_pcm_substream *substream) +static void mop500_ab8500_shutdown(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct device *dev = rtd->card->dev; @@ -206,7 +206,7 @@ void mop500_ab8500_shutdown(struct snd_pcm_substream *substream) rx_slots = DEF_RX_SLOTS; } -int mop500_ab8500_hw_params(struct snd_pcm_substream *substream, +static int mop500_ab8500_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; -- cgit v1.2.3 From aba1e2be4dcfda7069cc0b82c73b89707595a454 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 14 May 2013 22:19:55 +0200 Subject: ASoC: mop500: Staticize non exported struct The mop500_dai_links struct is not used outside of mop500.c, so make it static. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/ux500/mop500.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/ux500/mop500.c b/sound/soc/ux500/mop500.c index 204b899c2311..8f5cd00a6e46 100644 --- a/sound/soc/ux500/mop500.c +++ b/sound/soc/ux500/mop500.c @@ -27,7 +27,7 @@ #include "mop500_ab8500.h" /* Define the whole MOP500 soundcard, linking platform to the codec-drivers */ -struct snd_soc_dai_link mop500_dai_links[] = { +static struct snd_soc_dai_link mop500_dai_links[] = { { .name = "ab8500_0", .stream_name = "ab8500_0", -- cgit v1.2.3 From 3eb28d3ca8f0c94ae40f57fbd53ef3805c8fdd2d Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 14 May 2013 22:19:56 +0200 Subject: ASoC: sn95031: Staticize non exported struct The sn95031_codec struct is not used outside of sn95031.c, so make it static. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/codecs/sn95031.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/sn95031.c b/sound/soc/codecs/sn95031.c index d1ae869d3181..dba26e63844e 100644 --- a/sound/soc/codecs/sn95031.c +++ b/sound/soc/codecs/sn95031.c @@ -883,7 +883,7 @@ static int sn95031_codec_remove(struct snd_soc_codec *codec) return 0; } -struct snd_soc_codec_driver sn95031_codec = { +static struct snd_soc_codec_driver sn95031_codec = { .probe = sn95031_codec_probe, .remove = sn95031_codec_remove, .read = sn95031_read, -- cgit v1.2.3 From cde11aedea7325d9e48c9f6f7ac468287d457e79 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 14 May 2013 22:19:57 +0200 Subject: ASoC: davinci-sffsdr: Staticize non exported struct The pcm3008_codec struct is not used outside of davinci-sffsdr.c, so make it static. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/davinci/davinci-sffsdr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/davinci/davinci-sffsdr.c b/sound/soc/davinci/davinci-sffsdr.c index 5be65aae7e0e..24df5bcf3bbc 100644 --- a/sound/soc/davinci/davinci-sffsdr.c +++ b/sound/soc/davinci/davinci-sffsdr.c @@ -106,7 +106,7 @@ static struct pcm3008_setup_data sffsdr_pcm3008_setup = { .pdda_pin = GPIO(38), }; -struct platform_device pcm3008_codec = { +static struct platform_device pcm3008_codec = { .name = "pcm3008-codec", .id = 0, .dev = { -- cgit v1.2.3 From 7ae6871fe51e337caa88025ac2dc0c586c4d4a09 Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Tue, 14 May 2013 17:07:21 +0200 Subject: ASoC: remove saarb and tavorevb3 machine drivers Support for PXA95x was removed in v3.8. This means that the Kconfig symbols MACH_SAARB and MACH_TAVOREVB3 are no longer available. This leaves the SoC Audio support for Marvell Saarb and Marvell Tavor EVB3 unbuildable. Remove these drivers too. Signed-off-by: Paul Bolle Acked-by: Haojian Zhuang Signed-off-by: Mark Brown --- sound/soc/pxa/Kconfig | 20 ----- sound/soc/pxa/Makefile | 4 - sound/soc/pxa/saarb.c | 190 ---------------------------------------------- sound/soc/pxa/tavorevb3.c | 189 --------------------------------------------- 4 files changed, 403 deletions(-) delete mode 100644 sound/soc/pxa/saarb.c delete mode 100644 sound/soc/pxa/tavorevb3.c diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index 4d2e46fae77c..b35809467547 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -130,26 +130,6 @@ config SND_PXA2XX_SOC_PALM27X Say Y if you want to add support for SoC audio on Palm T|X, T5, E2 or LifeDrive handheld computer. -config SND_SOC_SAARB - tristate "SoC Audio support for Marvell Saarb" - depends on SND_PXA2XX_SOC && MACH_SAARB - select MFD_88PM860X - select SND_PXA_SOC_SSP - select SND_SOC_88PM860X - help - Say Y if you want to add support for SoC audio on the - Marvell Saarb reference platform. - -config SND_SOC_TAVOREVB3 - tristate "SoC Audio support for Marvell Tavor EVB3" - depends on SND_PXA2XX_SOC && MACH_TAVOREVB3 - select MFD_88PM860X - select SND_PXA_SOC_SSP - select SND_SOC_88PM860X - help - Say Y if you want to add support for SoC audio on the - Marvell Saarb reference platform. - config SND_PXA910_SOC tristate "SoC Audio for Marvell PXA910 chip" depends on ARCH_MMP && SND diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile index d8a265d2d5d7..2cff67b61dc3 100644 --- a/sound/soc/pxa/Makefile +++ b/sound/soc/pxa/Makefile @@ -23,8 +23,6 @@ snd-soc-e800-objs := e800_wm9712.o snd-soc-spitz-objs := spitz.o snd-soc-em-x270-objs := em-x270.o snd-soc-palm27x-objs := palm27x.o -snd-soc-saarb-objs := saarb.o -snd-soc-tavorevb3-objs := tavorevb3.o snd-soc-zylonite-objs := zylonite.o snd-soc-hx4700-objs := hx4700.o snd-soc-magician-objs := magician.o @@ -48,8 +46,6 @@ obj-$(CONFIG_SND_PXA2XX_SOC_HX4700) += snd-soc-hx4700.o obj-$(CONFIG_SND_PXA2XX_SOC_MAGICIAN) += snd-soc-magician.o obj-$(CONFIG_SND_PXA2XX_SOC_MIOA701) += snd-soc-mioa701.o obj-$(CONFIG_SND_PXA2XX_SOC_Z2) += snd-soc-z2.o -obj-$(CONFIG_SND_SOC_SAARB) += snd-soc-saarb.o -obj-$(CONFIG_SND_SOC_TAVOREVB3) += snd-soc-tavorevb3.o obj-$(CONFIG_SND_SOC_ZYLONITE) += snd-soc-zylonite.o obj-$(CONFIG_SND_PXA2XX_SOC_IMOTE2) += snd-soc-imote2.o obj-$(CONFIG_SND_SOC_RAUMFELD) += snd-soc-raumfeld.o diff --git a/sound/soc/pxa/saarb.c b/sound/soc/pxa/saarb.c deleted file mode 100644 index c34146b776b4..000000000000 --- a/sound/soc/pxa/saarb.c +++ /dev/null @@ -1,190 +0,0 @@ -/* - * saarb.c -- SoC audio for saarb - * - * Copyright (C) 2010 Marvell International Ltd. - * Haojian Zhuang - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "../codecs/88pm860x-codec.h" -#include "pxa-ssp.h" - -static int saarb_pm860x_init(struct snd_soc_pcm_runtime *rtd); - -static struct platform_device *saarb_snd_device; - -static struct snd_soc_jack hs_jack, mic_jack; - -static struct snd_soc_jack_pin hs_jack_pins[] = { - { .pin = "Headset Stereophone", .mask = SND_JACK_HEADPHONE, }, -}; - -static struct snd_soc_jack_pin mic_jack_pins[] = { - { .pin = "Headset Mic 2", .mask = SND_JACK_MICROPHONE, }, -}; - -/* saarb machine dapm widgets */ -static const struct snd_soc_dapm_widget saarb_dapm_widgets[] = { - SND_SOC_DAPM_HP("Headphone Stereophone", NULL), - SND_SOC_DAPM_LINE("Lineout Out 1", NULL), - SND_SOC_DAPM_LINE("Lineout Out 2", NULL), - SND_SOC_DAPM_SPK("Ext Speaker", NULL), - SND_SOC_DAPM_MIC("Ext Mic 1", NULL), - SND_SOC_DAPM_MIC("Headset Mic", NULL), - SND_SOC_DAPM_MIC("Ext Mic 3", NULL), -}; - -/* saarb machine audio map */ -static const struct snd_soc_dapm_route saarb_audio_map[] = { - {"Headset Stereophone", NULL, "HS1"}, - {"Headset Stereophone", NULL, "HS2"}, - - {"Ext Speaker", NULL, "LSP"}, - {"Ext Speaker", NULL, "LSN"}, - - {"Lineout Out 1", NULL, "LINEOUT1"}, - {"Lineout Out 2", NULL, "LINEOUT2"}, - - {"MIC1P", NULL, "Mic1 Bias"}, - {"MIC1N", NULL, "Mic1 Bias"}, - {"Mic1 Bias", NULL, "Ext Mic 1"}, - - {"MIC2P", NULL, "Mic1 Bias"}, - {"MIC2N", NULL, "Mic1 Bias"}, - {"Mic1 Bias", NULL, "Headset Mic 2"}, - - {"MIC3P", NULL, "Mic3 Bias"}, - {"MIC3N", NULL, "Mic3 Bias"}, - {"Mic3 Bias", NULL, "Ext Mic 3"}, -}; - -static int saarb_i2s_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int width = snd_pcm_format_physical_width(params_format(params)); - int ret; - - ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_NET_PLL, 0, - PM860X_CLK_DIR_OUT); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_sysclk(codec_dai, 0, 0, PM860X_CLK_DIR_OUT); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_tdm_slot(cpu_dai, 3, 3, 2, width); - - return ret; -} - -static struct snd_soc_ops saarb_i2s_ops = { - .hw_params = saarb_i2s_hw_params, -}; - -static struct snd_soc_dai_link saarb_dai[] = { - { - .name = "88PM860x I2S", - .stream_name = "I2S Audio", - .cpu_dai_name = "pxa-ssp-dai.1", - .codec_dai_name = "88pm860x-i2s", - .platform_name = "pxa-pcm-audio", - .codec_name = "88pm860x-codec", - .init = saarb_pm860x_init, - .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM, - .ops = &saarb_i2s_ops, - }, -}; - -static struct snd_soc_card snd_soc_card_saarb = { - .name = "Saarb", - .owner = THIS_MODULE, - .dai_link = saarb_dai, - .num_links = ARRAY_SIZE(saarb_dai), - - .dapm_widgets = saarb_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(saarb_dapm_widgets), - .dapm_routes = saarb_audio_map, - .num_dapm_routes = ARRAY_SIZE(saarb_audio_map), -}; - -static int saarb_pm860x_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_codec *codec = rtd->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; - - /* connected pins */ - snd_soc_dapm_enable_pin(dapm, "Ext Speaker"); - snd_soc_dapm_enable_pin(dapm, "Ext Mic 1"); - snd_soc_dapm_enable_pin(dapm, "Ext Mic 3"); - snd_soc_dapm_disable_pin(dapm, "Headset Mic 2"); - snd_soc_dapm_disable_pin(dapm, "Headset Stereophone"); - - /* Headset jack detection */ - snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE - | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2, - &hs_jack); - snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins), - hs_jack_pins); - snd_soc_jack_new(codec, "Microphone Jack", SND_JACK_MICROPHONE, - &mic_jack); - snd_soc_jack_add_pins(&mic_jack, ARRAY_SIZE(mic_jack_pins), - mic_jack_pins); - - /* headphone, microphone detection & headset short detection */ - pm860x_hs_jack_detect(codec, &hs_jack, SND_JACK_HEADPHONE, - SND_JACK_BTN_0, SND_JACK_BTN_1, SND_JACK_BTN_2); - pm860x_mic_jack_detect(codec, &hs_jack, SND_JACK_MICROPHONE); - return 0; -} - -static int __init saarb_init(void) -{ - int ret; - - if (!machine_is_saarb()) - return -ENODEV; - saarb_snd_device = platform_device_alloc("soc-audio", -1); - if (!saarb_snd_device) - return -ENOMEM; - - platform_set_drvdata(saarb_snd_device, &snd_soc_card_saarb); - - ret = platform_device_add(saarb_snd_device); - if (ret) - platform_device_put(saarb_snd_device); - - return ret; -} - -static void __exit saarb_exit(void) -{ - platform_device_unregister(saarb_snd_device); -} - -module_init(saarb_init); -module_exit(saarb_exit); - -MODULE_AUTHOR("Haojian Zhuang "); -MODULE_DESCRIPTION("ALSA SoC 88PM860x Saarb"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/pxa/tavorevb3.c b/sound/soc/pxa/tavorevb3.c deleted file mode 100644 index 8b5ab8f72726..000000000000 --- a/sound/soc/pxa/tavorevb3.c +++ /dev/null @@ -1,189 +0,0 @@ -/* - * tavorevb3.c -- SoC audio for Tavor EVB3 - * - * Copyright (C) 2010 Marvell International Ltd. - * Haojian Zhuang - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "../codecs/88pm860x-codec.h" -#include "pxa-ssp.h" - -static int evb3_pm860x_init(struct snd_soc_pcm_runtime *rtd); - -static struct platform_device *evb3_snd_device; - -static struct snd_soc_jack hs_jack, mic_jack; - -static struct snd_soc_jack_pin hs_jack_pins[] = { - { .pin = "Headset Stereophone", .mask = SND_JACK_HEADPHONE, }, -}; - -static struct snd_soc_jack_pin mic_jack_pins[] = { - { .pin = "Headset Mic 2", .mask = SND_JACK_MICROPHONE, }, -}; - -/* tavorevb3 machine dapm widgets */ -static const struct snd_soc_dapm_widget evb3_dapm_widgets[] = { - SND_SOC_DAPM_HP("Headset Stereophone", NULL), - SND_SOC_DAPM_LINE("Lineout Out 1", NULL), - SND_SOC_DAPM_LINE("Lineout Out 2", NULL), - SND_SOC_DAPM_SPK("Ext Speaker", NULL), - SND_SOC_DAPM_MIC("Ext Mic 1", NULL), - SND_SOC_DAPM_MIC("Headset Mic 2", NULL), - SND_SOC_DAPM_MIC("Ext Mic 3", NULL), -}; - -/* tavorevb3 machine audio map */ -static const struct snd_soc_dapm_route evb3_audio_map[] = { - {"Headset Stereophone", NULL, "HS1"}, - {"Headset Stereophone", NULL, "HS2"}, - - {"Ext Speaker", NULL, "LSP"}, - {"Ext Speaker", NULL, "LSN"}, - - {"Lineout Out 1", NULL, "LINEOUT1"}, - {"Lineout Out 2", NULL, "LINEOUT2"}, - - {"MIC1P", NULL, "Mic1 Bias"}, - {"MIC1N", NULL, "Mic1 Bias"}, - {"Mic1 Bias", NULL, "Ext Mic 1"}, - - {"MIC2P", NULL, "Mic1 Bias"}, - {"MIC2N", NULL, "Mic1 Bias"}, - {"Mic1 Bias", NULL, "Headset Mic 2"}, - - {"MIC3P", NULL, "Mic3 Bias"}, - {"MIC3N", NULL, "Mic3 Bias"}, - {"Mic3 Bias", NULL, "Ext Mic 3"}, -}; - -static int evb3_i2s_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int width = snd_pcm_format_physical_width(params_format(params)); - int ret; - - ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_NET_PLL, 0, - PM860X_CLK_DIR_OUT); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_sysclk(codec_dai, 0, 0, PM860X_CLK_DIR_OUT); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_tdm_slot(cpu_dai, 3, 3, 2, width); - return ret; -} - -static struct snd_soc_ops evb3_i2s_ops = { - .hw_params = evb3_i2s_hw_params, -}; - -static struct snd_soc_dai_link evb3_dai[] = { - { - .name = "88PM860x I2S", - .stream_name = "I2S Audio", - .cpu_dai_name = "pxa-ssp-dai.1", - .codec_dai_name = "88pm860x-i2s", - .platform_name = "pxa-pcm-audio", - .codec_name = "88pm860x-codec", - .init = evb3_pm860x_init, - .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM, - .ops = &evb3_i2s_ops, - }, -}; - -static struct snd_soc_card snd_soc_card_evb3 = { - .name = "Tavor EVB3", - .owner = THIS_MODULE, - .dai_link = evb3_dai, - .num_links = ARRAY_SIZE(evb3_dai), - - .dapm_widgets = evb3_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(evb3_dapm_widgets), - .dapm_routes = evb3_audio_map, - .num_dapm_routes = ARRAY_SIZE(evb3_audio_map), -}; - -static int evb3_pm860x_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_codec *codec = rtd->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; - - /* connected pins */ - snd_soc_dapm_enable_pin(dapm, "Ext Speaker"); - snd_soc_dapm_enable_pin(dapm, "Ext Mic 1"); - snd_soc_dapm_enable_pin(dapm, "Ext Mic 3"); - snd_soc_dapm_disable_pin(dapm, "Headset Mic 2"); - snd_soc_dapm_disable_pin(dapm, "Headset Stereophone"); - - /* Headset jack detection */ - snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE - | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2, - &hs_jack); - snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins), - hs_jack_pins); - snd_soc_jack_new(codec, "Microphone Jack", SND_JACK_MICROPHONE, - &mic_jack); - snd_soc_jack_add_pins(&mic_jack, ARRAY_SIZE(mic_jack_pins), - mic_jack_pins); - - /* headphone, microphone detection & headset short detection */ - pm860x_hs_jack_detect(codec, &hs_jack, SND_JACK_HEADPHONE, - SND_JACK_BTN_0, SND_JACK_BTN_1, SND_JACK_BTN_2); - pm860x_mic_jack_detect(codec, &hs_jack, SND_JACK_MICROPHONE); - return 0; -} - -static int __init tavorevb3_init(void) -{ - int ret; - - if (!machine_is_tavorevb3()) - return -ENODEV; - evb3_snd_device = platform_device_alloc("soc-audio", -1); - if (!evb3_snd_device) - return -ENOMEM; - - platform_set_drvdata(evb3_snd_device, &snd_soc_card_evb3); - - ret = platform_device_add(evb3_snd_device); - if (ret) - platform_device_put(evb3_snd_device); - - return ret; -} - -static void __exit tavorevb3_exit(void) -{ - platform_device_unregister(evb3_snd_device); -} - -module_init(tavorevb3_init); -module_exit(tavorevb3_exit); - -MODULE_AUTHOR("Haojian Zhuang "); -MODULE_DESCRIPTION("ALSA SoC 88PM860x Tavor EVB3"); -MODULE_LICENSE("GPL"); -- cgit v1.2.3 From bd41bc9696b5631b2c2fe26f40c8cdd99b3aeb3e Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Thu, 25 Apr 2013 11:18:46 +0800 Subject: ASoC: fsl: remove use of imx-pcm-audio from fsl_ssi Rather than instantiating imx-pcm-audio to call imx_pcm_dma_init(), fsl_ssi can just directly call it to save the use of imx-pcm-audio. With this change, fsl_ssi becomes not only a cpu DAI but also a platform device, so updates platform device setup in imx-sgtl5000 accordingly. Signed-off-by: Shawn Guo Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_ssi.c | 13 ++++--------- sound/soc/fsl/imx-sgtl5000.c | 2 +- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 0f0bed6def9e..2f2d837df07f 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -122,7 +122,6 @@ struct fsl_ssi_private { bool new_binding; bool ssi_on_imx; struct clk *clk; - struct platform_device *imx_pcm_pdev; struct snd_dmaengine_dai_dma_data dma_params_tx; struct snd_dmaengine_dai_dma_data dma_params_rx; struct imx_dma_data filter_data_tx; @@ -809,13 +808,9 @@ static int fsl_ssi_probe(struct platform_device *pdev) } if (ssi_private->ssi_on_imx) { - ssi_private->imx_pcm_pdev = - platform_device_register_simple("imx-pcm-audio", - -1, NULL, 0); - if (IS_ERR(ssi_private->imx_pcm_pdev)) { - ret = PTR_ERR(ssi_private->imx_pcm_pdev); + ret = imx_pcm_dma_init(pdev); + if (ret) goto error_dev; - } } /* @@ -854,7 +849,7 @@ done: error_dai: if (ssi_private->ssi_on_imx) - platform_device_unregister(ssi_private->imx_pcm_pdev); + imx_pcm_dma_exit(pdev); snd_soc_unregister_component(&pdev->dev); error_dev: @@ -889,7 +884,7 @@ static int fsl_ssi_remove(struct platform_device *pdev) if (!ssi_private->new_binding) platform_device_unregister(ssi_private->pdev); if (ssi_private->ssi_on_imx) { - platform_device_unregister(ssi_private->imx_pcm_pdev); + imx_pcm_dma_exit(pdev); clk_disable_unprepare(ssi_private->clk); clk_put(ssi_private->clk); } diff --git a/sound/soc/fsl/imx-sgtl5000.c b/sound/soc/fsl/imx-sgtl5000.c index 5a6aaa3b947a..a60aaa053d28 100644 --- a/sound/soc/fsl/imx-sgtl5000.c +++ b/sound/soc/fsl/imx-sgtl5000.c @@ -149,7 +149,7 @@ static int imx_sgtl5000_probe(struct platform_device *pdev) data->dai.codec_dai_name = "sgtl5000"; data->dai.codec_of_node = codec_np; data->dai.cpu_of_node = ssi_np; - data->dai.platform_name = "imx-pcm-audio"; + data->dai.platform_of_node = ssi_np; data->dai.init = &imx_sgtl5000_dai_init; data->dai.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM; -- cgit v1.2.3 From 3b7d46380beae3de4a0f03ba4dcbd509c97ab503 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Thu, 25 Apr 2013 11:18:47 +0800 Subject: ASoC: fsl: remove use of imx-pcm-audio from imx-ssi Rather than instantiating imx-pcm-audio to call imx_pcm_dma_init(), imx-ssi can just directly call it to save the use of imx-pcm-audio. With this change, imx-ssi becomes not only a cpu DAI but also a platform device, so updates platform device setup in imx-mc13783 and mx27vis-aic32x4 accordingly. Signed-off-by: Shawn Guo Signed-off-by: Mark Brown --- sound/soc/fsl/imx-mc13783.c | 2 +- sound/soc/fsl/imx-ssi.c | 21 +++++---------------- sound/soc/fsl/imx-ssi.h | 1 - sound/soc/fsl/mx27vis-aic32x4.c | 2 +- 4 files changed, 7 insertions(+), 19 deletions(-) diff --git a/sound/soc/fsl/imx-mc13783.c b/sound/soc/fsl/imx-mc13783.c index 4ae30f21fdb5..9df173c091a6 100644 --- a/sound/soc/fsl/imx-mc13783.c +++ b/sound/soc/fsl/imx-mc13783.c @@ -64,7 +64,7 @@ static struct snd_soc_dai_link imx_mc13783_dai_mc13783[] = { .codec_dai_name = "mc13783-hifi", .codec_name = "mc13783-codec", .cpu_dai_name = "imx-ssi.0", - .platform_name = "imx-pcm-audio.0", + .platform_name = "imx-ssi.0", .ops = &imx_mc13783_hifi_ops, .symmetric_rates = 1, .dai_fmt = FMT_SSI, diff --git a/sound/soc/fsl/imx-ssi.c b/sound/soc/fsl/imx-ssi.c index 902fab02b851..b5a2b040816c 100644 --- a/sound/soc/fsl/imx-ssi.c +++ b/sound/soc/fsl/imx-ssi.c @@ -608,24 +608,13 @@ static int imx_ssi_probe(struct platform_device *pdev) goto failed_pdev_fiq_add; } - ssi->soc_platform_pdev = platform_device_alloc("imx-pcm-audio", pdev->id); - if (!ssi->soc_platform_pdev) { - ret = -ENOMEM; - goto failed_pdev_alloc; - } - - platform_set_drvdata(ssi->soc_platform_pdev, ssi); - ret = platform_device_add(ssi->soc_platform_pdev); - if (ret) { - dev_err(&pdev->dev, "failed to add platform device\n"); - goto failed_pdev_add; - } + ret = imx_pcm_dma_init(pdev); + if (ret) + goto failed_pcm_dma; return 0; -failed_pdev_add: - platform_device_put(ssi->soc_platform_pdev); -failed_pdev_alloc: +failed_pcm_dma: platform_device_del(ssi->soc_platform_pdev_fiq); failed_pdev_fiq_add: platform_device_put(ssi->soc_platform_pdev_fiq); @@ -645,7 +634,7 @@ static int imx_ssi_remove(struct platform_device *pdev) struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); struct imx_ssi *ssi = platform_get_drvdata(pdev); - platform_device_unregister(ssi->soc_platform_pdev); + imx_pcm_dma_exit(pdev); platform_device_unregister(ssi->soc_platform_pdev_fiq); snd_soc_unregister_component(&pdev->dev); diff --git a/sound/soc/fsl/imx-ssi.h b/sound/soc/fsl/imx-ssi.h index bb6b3dbb13fd..b052fad8f6c7 100644 --- a/sound/soc/fsl/imx-ssi.h +++ b/sound/soc/fsl/imx-ssi.h @@ -212,7 +212,6 @@ struct imx_ssi { int enabled; - struct platform_device *soc_platform_pdev; struct platform_device *soc_platform_pdev_fiq; }; diff --git a/sound/soc/fsl/mx27vis-aic32x4.c b/sound/soc/fsl/mx27vis-aic32x4.c index 3d1074179057..f4c3bda5e69e 100644 --- a/sound/soc/fsl/mx27vis-aic32x4.c +++ b/sound/soc/fsl/mx27vis-aic32x4.c @@ -161,7 +161,7 @@ static struct snd_soc_dai_link mx27vis_aic32x4_dai = { .name = "tlv320aic32x4", .stream_name = "TLV320AIC32X4", .codec_dai_name = "tlv320aic32x4-hifi", - .platform_name = "imx-pcm-audio.0", + .platform_name = "imx-ssi.0", .codec_name = "tlv320aic32x4.0-0018", .cpu_dai_name = "imx-ssi.0", .ops = &mx27vis_aic32x4_snd_ops, -- cgit v1.2.3 From 88e89f5548a6e19bf837633f622764f2d1531748 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Thu, 25 Apr 2013 11:18:48 +0800 Subject: ASoC: fsl: create function imx_pcm_fiq_exit() Create function imx_pcm_fiq_exit() to be paired with imx_pcm_fiq_init() just like the pair of imx_pcm_dma_init() and imx_pcm_dma_exit(). Signed-off-by: Shawn Guo Signed-off-by: Mark Brown --- sound/soc/fsl/imx-pcm-fiq.c | 5 +++++ sound/soc/fsl/imx-pcm.c | 2 +- sound/soc/fsl/imx-pcm.h | 5 +++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/sound/soc/fsl/imx-pcm-fiq.c b/sound/soc/fsl/imx-pcm-fiq.c index 670b96b0ce2f..710c06990450 100644 --- a/sound/soc/fsl/imx-pcm-fiq.c +++ b/sound/soc/fsl/imx-pcm-fiq.c @@ -314,3 +314,8 @@ failed_register: return ret; } + +void imx_pcm_fiq_exit(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); +} diff --git a/sound/soc/fsl/imx-pcm.c b/sound/soc/fsl/imx-pcm.c index c49896442d8e..16a956bcc52b 100644 --- a/sound/soc/fsl/imx-pcm.c +++ b/sound/soc/fsl/imx-pcm.c @@ -115,7 +115,7 @@ static int imx_pcm_probe(struct platform_device *pdev) static int imx_pcm_remove(struct platform_device *pdev) { if (strcmp(pdev->id_entry->name, "imx-fiq-pcm-audio") == 0) - snd_soc_unregister_platform(&pdev->dev); + imx_pcm_fiq_exit(pdev); else imx_pcm_dma_exit(pdev); diff --git a/sound/soc/fsl/imx-pcm.h b/sound/soc/fsl/imx-pcm.h index b7fa0d75c687..073bf389c02e 100644 --- a/sound/soc/fsl/imx-pcm.h +++ b/sound/soc/fsl/imx-pcm.h @@ -53,11 +53,16 @@ static inline void imx_pcm_dma_exit(struct platform_device *pdev) #ifdef CONFIG_SND_SOC_IMX_PCM_FIQ int imx_pcm_fiq_init(struct platform_device *pdev); +void imx_pcm_fiq_exit(struct platform_device *pdev); #else static inline int imx_pcm_fiq_init(struct platform_device *pdev) { return -ENODEV; } + +static inline void imx_pcm_fiq_exit(struct platform_device *pdev) +{ +} #endif #endif /* _IMX_PCM_H */ -- cgit v1.2.3 From 2bf9d4bbd0fa97ff6f214484f62fc8aca64d1d00 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Thu, 25 Apr 2013 11:18:49 +0800 Subject: ASoC: fsl: remove use of imx-fiq-pcm-audio from imx-ssi Rather than instantiating imx-fiq-pcm-audio to call imx_pcm_fiq_init(), imx-ssi can just directly call it to save the use of imx-fiq-pcm-audio. With this change, imx-ssi becomes not only a cpu DAI but also a platform device, so updates platform device setup in eukrea-tlv320, phycore-ac97 and wm1133-ev1 accordingly. Signed-off-by: Shawn Guo Signed-off-by: Mark Brown --- sound/soc/fsl/eukrea-tlv320.c | 2 +- sound/soc/fsl/imx-ssi.c | 23 ++++++----------------- sound/soc/fsl/imx-ssi.h | 2 -- sound/soc/fsl/phycore-ac97.c | 2 +- sound/soc/fsl/wm1133-ev1.c | 2 +- 5 files changed, 9 insertions(+), 22 deletions(-) diff --git a/sound/soc/fsl/eukrea-tlv320.c b/sound/soc/fsl/eukrea-tlv320.c index 75ffdf0e2aad..9a4a0ca2c1de 100644 --- a/sound/soc/fsl/eukrea-tlv320.c +++ b/sound/soc/fsl/eukrea-tlv320.c @@ -80,7 +80,7 @@ static struct snd_soc_dai_link eukrea_tlv320_dai = { .name = "tlv320aic23", .stream_name = "TLV320AIC23", .codec_dai_name = "tlv320aic23-hifi", - .platform_name = "imx-fiq-pcm-audio.0", + .platform_name = "imx-ssi.0", .codec_name = "tlv320aic23-codec.0-001a", .cpu_dai_name = "imx-ssi.0", .ops = &eukrea_tlv320_snd_ops, diff --git a/sound/soc/fsl/imx-ssi.c b/sound/soc/fsl/imx-ssi.c index b5a2b040816c..1b2e750151ae 100644 --- a/sound/soc/fsl/imx-ssi.c +++ b/sound/soc/fsl/imx-ssi.c @@ -595,18 +595,9 @@ static int imx_ssi_probe(struct platform_device *pdev) goto failed_register; } - ssi->soc_platform_pdev_fiq = platform_device_alloc("imx-fiq-pcm-audio", pdev->id); - if (!ssi->soc_platform_pdev_fiq) { - ret = -ENOMEM; - goto failed_pdev_fiq_alloc; - } - - platform_set_drvdata(ssi->soc_platform_pdev_fiq, ssi); - ret = platform_device_add(ssi->soc_platform_pdev_fiq); - if (ret) { - dev_err(&pdev->dev, "failed to add platform device\n"); - goto failed_pdev_fiq_add; - } + ret = imx_pcm_fiq_init(pdev); + if (ret) + goto failed_pcm_fiq; ret = imx_pcm_dma_init(pdev); if (ret) @@ -615,10 +606,8 @@ static int imx_ssi_probe(struct platform_device *pdev) return 0; failed_pcm_dma: - platform_device_del(ssi->soc_platform_pdev_fiq); -failed_pdev_fiq_add: - platform_device_put(ssi->soc_platform_pdev_fiq); -failed_pdev_fiq_alloc: + imx_pcm_fiq_exit(pdev); +failed_pcm_fiq: snd_soc_unregister_component(&pdev->dev); failed_register: release_mem_region(res->start, resource_size(res)); @@ -635,7 +624,7 @@ static int imx_ssi_remove(struct platform_device *pdev) struct imx_ssi *ssi = platform_get_drvdata(pdev); imx_pcm_dma_exit(pdev); - platform_device_unregister(ssi->soc_platform_pdev_fiq); + imx_pcm_fiq_exit(pdev); snd_soc_unregister_component(&pdev->dev); diff --git a/sound/soc/fsl/imx-ssi.h b/sound/soc/fsl/imx-ssi.h index b052fad8f6c7..d5003cefca8d 100644 --- a/sound/soc/fsl/imx-ssi.h +++ b/sound/soc/fsl/imx-ssi.h @@ -211,8 +211,6 @@ struct imx_ssi { struct imx_dma_data filter_data_rx; int enabled; - - struct platform_device *soc_platform_pdev_fiq; }; #endif /* _IMX_SSI_H */ diff --git a/sound/soc/fsl/phycore-ac97.c b/sound/soc/fsl/phycore-ac97.c index f8da6dd115ed..ae403c29688f 100644 --- a/sound/soc/fsl/phycore-ac97.c +++ b/sound/soc/fsl/phycore-ac97.c @@ -33,7 +33,7 @@ static struct snd_soc_dai_link imx_phycore_dai_ac97[] = { .codec_dai_name = "wm9712-hifi", .codec_name = "wm9712-codec", .cpu_dai_name = "imx-ssi.0", - .platform_name = "imx-fiq-pcm-audio.0", + .platform_name = "imx-ssi.0", .ops = &imx_phycore_hifi_ops, }, }; diff --git a/sound/soc/fsl/wm1133-ev1.c b/sound/soc/fsl/wm1133-ev1.c index fe54a69073e5..fce63252bdbb 100644 --- a/sound/soc/fsl/wm1133-ev1.c +++ b/sound/soc/fsl/wm1133-ev1.c @@ -245,7 +245,7 @@ static struct snd_soc_dai_link wm1133_ev1_dai = { .stream_name = "Audio", .cpu_dai_name = "imx-ssi.0", .codec_dai_name = "wm8350-hifi", - .platform_name = "imx-fiq-pcm-audio.0", + .platform_name = "imx-ssi.0", .codec_name = "wm8350-codec.0-0x1a", .init = wm1133_ev1_init, .ops = &wm1133_ev1_ops, -- cgit v1.2.3 From dbdf6b54340e1671439a4a5efbd15b7a0b14eacb Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Thu, 25 Apr 2013 11:18:50 +0800 Subject: ASoC: fsl: remove imx-pcm driver With imx-pcm-dma moving to generic dmaengine pcm driver and the removal of imx-pcm-audio/imx-fiq-pcm-audio platform device use, now imx-pcm driver contains a few functions that are only used by imx-pcm-fiq.c. Move these functions into imx-pcm-fiq.c and remove imx-pcm.c completely. Signed-off-by: Shawn Guo Signed-off-by: Mark Brown --- sound/soc/fsl/Kconfig | 5 -- sound/soc/fsl/Makefile | 11 +--- sound/soc/fsl/imx-pcm-dma.c | 2 + sound/soc/fsl/imx-pcm-fiq.c | 87 ++++++++++++++++++++++++++ sound/soc/fsl/imx-pcm.c | 145 -------------------------------------------- sound/soc/fsl/imx-pcm.h | 5 -- 6 files changed, 91 insertions(+), 164 deletions(-) delete mode 100644 sound/soc/fsl/imx-pcm.c diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 3843a18d4e56..7860cc27e5b2 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -108,18 +108,13 @@ if SND_IMX_SOC config SND_SOC_IMX_SSI tristate -config SND_SOC_IMX_PCM - tristate - config SND_SOC_IMX_PCM_FIQ bool select FIQ - select SND_SOC_IMX_PCM config SND_SOC_IMX_PCM_DMA bool select SND_SOC_GENERIC_DMAENGINE_PCM - select SND_SOC_IMX_PCM config SND_SOC_IMX_AUDMUX tristate diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index afd34794db53..91883f8a2321 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -30,18 +30,11 @@ obj-$(CONFIG_SND_MPC52xx_SOC_EFIKA) += efika-audio-fabric.o # i.MX Platform Support snd-soc-imx-ssi-objs := imx-ssi.o snd-soc-imx-audmux-objs := imx-audmux.o -snd-soc-imx-pcm-objs := imx-pcm.o -ifneq ($(CONFIG_SND_SOC_IMX_PCM_FIQ),) - snd-soc-imx-pcm-objs += imx-pcm-fiq.o -endif -ifneq ($(CONFIG_SND_SOC_IMX_PCM_DMA),) - snd-soc-imx-pcm-objs += imx-pcm-dma.o -endif - obj-$(CONFIG_SND_SOC_IMX_SSI) += snd-soc-imx-ssi.o obj-$(CONFIG_SND_SOC_IMX_AUDMUX) += snd-soc-imx-audmux.o -obj-$(CONFIG_SND_SOC_IMX_PCM) += snd-soc-imx-pcm.o +obj-$(CONFIG_SND_SOC_IMX_PCM_FIQ) += imx-pcm-fiq.o +obj-$(CONFIG_SND_SOC_IMX_PCM_DMA) += imx-pcm-dma.o # i.MX Machine Support snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o diff --git a/sound/soc/fsl/imx-pcm-dma.c b/sound/soc/fsl/imx-pcm-dma.c index c246fb514930..fde4d2ea68c8 100644 --- a/sound/soc/fsl/imx-pcm-dma.c +++ b/sound/soc/fsl/imx-pcm-dma.c @@ -67,8 +67,10 @@ int imx_pcm_dma_init(struct platform_device *pdev) SND_DMAENGINE_PCM_FLAG_NO_DT | SND_DMAENGINE_PCM_FLAG_COMPAT); } +EXPORT_SYMBOL_GPL(imx_pcm_dma_init); void imx_pcm_dma_exit(struct platform_device *pdev) { snd_dmaengine_pcm_unregister(&pdev->dev); } +EXPORT_SYMBOL_GPL(imx_pcm_dma_exit); diff --git a/sound/soc/fsl/imx-pcm-fiq.c b/sound/soc/fsl/imx-pcm-fiq.c index 710c06990450..310d90290320 100644 --- a/sound/soc/fsl/imx-pcm-fiq.c +++ b/sound/soc/fsl/imx-pcm-fiq.c @@ -225,6 +225,22 @@ static int snd_imx_close(struct snd_pcm_substream *substream) return 0; } +static int snd_imx_pcm_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + int ret; + + ret = dma_mmap_writecombine(substream->pcm->card->dev, vma, + runtime->dma_area, runtime->dma_addr, runtime->dma_bytes); + + pr_debug("%s: ret: %d %p 0x%08x 0x%08x\n", __func__, ret, + runtime->dma_area, + runtime->dma_addr, + runtime->dma_bytes); + return ret; +} + static struct snd_pcm_ops imx_pcm_ops = { .open = snd_imx_open, .close = snd_imx_close, @@ -236,6 +252,54 @@ static struct snd_pcm_ops imx_pcm_ops = { .mmap = snd_imx_pcm_mmap, }; +static int imx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) +{ + struct snd_pcm_substream *substream = pcm->streams[stream].substream; + struct snd_dma_buffer *buf = &substream->dma_buffer; + size_t size = IMX_SSI_DMABUF_SIZE; + + buf->dev.type = SNDRV_DMA_TYPE_DEV; + buf->dev.dev = pcm->card->dev; + buf->private_data = NULL; + buf->area = dma_alloc_writecombine(pcm->card->dev, size, + &buf->addr, GFP_KERNEL); + if (!buf->area) + return -ENOMEM; + buf->bytes = size; + + return 0; +} + +static u64 imx_pcm_dmamask = DMA_BIT_MASK(32); + +static int imx_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_card *card = rtd->card->snd_card; + struct snd_pcm *pcm = rtd->pcm; + int ret = 0; + + if (!card->dev->dma_mask) + card->dev->dma_mask = &imx_pcm_dmamask; + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = DMA_BIT_MASK(32); + if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { + ret = imx_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_PLAYBACK); + if (ret) + goto out; + } + + if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { + ret = imx_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_CAPTURE); + if (ret) + goto out; + } + +out: + return ret; +} + static int ssi_irq = 0; static int imx_pcm_fiq_new(struct snd_soc_pcm_runtime *rtd) @@ -268,6 +332,27 @@ static int imx_pcm_fiq_new(struct snd_soc_pcm_runtime *rtd) return 0; } +static void imx_pcm_free(struct snd_pcm *pcm) +{ + struct snd_pcm_substream *substream; + struct snd_dma_buffer *buf; + int stream; + + for (stream = 0; stream < 2; stream++) { + substream = pcm->streams[stream].substream; + if (!substream) + continue; + + buf = &substream->dma_buffer; + if (!buf->area) + continue; + + dma_free_writecombine(pcm->card->dev, buf->bytes, + buf->area, buf->addr); + buf->area = NULL; + } +} + static void imx_pcm_fiq_free(struct snd_pcm *pcm) { mxc_set_irq_fiq(ssi_irq, 0); @@ -314,8 +399,10 @@ failed_register: return ret; } +EXPORT_SYMBOL_GPL(imx_pcm_fiq_init); void imx_pcm_fiq_exit(struct platform_device *pdev) { snd_soc_unregister_platform(&pdev->dev); } +EXPORT_SYMBOL_GPL(imx_pcm_fiq_exit); diff --git a/sound/soc/fsl/imx-pcm.c b/sound/soc/fsl/imx-pcm.c deleted file mode 100644 index 16a956bcc52b..000000000000 --- a/sound/soc/fsl/imx-pcm.c +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright 2009 Sascha Hauer - * - * This code is based on code copyrighted by Freescale, - * Liam Girdwood, Javier Martin and probably others. - * - * 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 -#include -#include -#include -#include "imx-pcm.h" - -int snd_imx_pcm_mmap(struct snd_pcm_substream *substream, - struct vm_area_struct *vma) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - int ret; - - ret = dma_mmap_writecombine(substream->pcm->card->dev, vma, - runtime->dma_area, runtime->dma_addr, runtime->dma_bytes); - - pr_debug("%s: ret: %d %p 0x%08x 0x%08x\n", __func__, ret, - runtime->dma_area, - runtime->dma_addr, - runtime->dma_bytes); - return ret; -} -EXPORT_SYMBOL_GPL(snd_imx_pcm_mmap); - -static int imx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) -{ - struct snd_pcm_substream *substream = pcm->streams[stream].substream; - struct snd_dma_buffer *buf = &substream->dma_buffer; - size_t size = IMX_SSI_DMABUF_SIZE; - - buf->dev.type = SNDRV_DMA_TYPE_DEV; - buf->dev.dev = pcm->card->dev; - buf->private_data = NULL; - buf->area = dma_alloc_writecombine(pcm->card->dev, size, - &buf->addr, GFP_KERNEL); - if (!buf->area) - return -ENOMEM; - buf->bytes = size; - - return 0; -} - -static u64 imx_pcm_dmamask = DMA_BIT_MASK(32); - -int imx_pcm_new(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_card *card = rtd->card->snd_card; - struct snd_pcm *pcm = rtd->pcm; - int ret = 0; - - if (!card->dev->dma_mask) - card->dev->dma_mask = &imx_pcm_dmamask; - if (!card->dev->coherent_dma_mask) - card->dev->coherent_dma_mask = DMA_BIT_MASK(32); - if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { - ret = imx_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_PLAYBACK); - if (ret) - goto out; - } - - if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { - ret = imx_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_CAPTURE); - if (ret) - goto out; - } - -out: - return ret; -} -EXPORT_SYMBOL_GPL(imx_pcm_new); - -void imx_pcm_free(struct snd_pcm *pcm) -{ - struct snd_pcm_substream *substream; - struct snd_dma_buffer *buf; - int stream; - - for (stream = 0; stream < 2; stream++) { - substream = pcm->streams[stream].substream; - if (!substream) - continue; - - buf = &substream->dma_buffer; - if (!buf->area) - continue; - - dma_free_writecombine(pcm->card->dev, buf->bytes, - buf->area, buf->addr); - buf->area = NULL; - } -} -EXPORT_SYMBOL_GPL(imx_pcm_free); - -static int imx_pcm_probe(struct platform_device *pdev) -{ - if (strcmp(pdev->id_entry->name, "imx-fiq-pcm-audio") == 0) - return imx_pcm_fiq_init(pdev); - - return imx_pcm_dma_init(pdev); -} - -static int imx_pcm_remove(struct platform_device *pdev) -{ - if (strcmp(pdev->id_entry->name, "imx-fiq-pcm-audio") == 0) - imx_pcm_fiq_exit(pdev); - else - imx_pcm_dma_exit(pdev); - - return 0; -} - -static struct platform_device_id imx_pcm_devtype[] = { - { .name = "imx-pcm-audio", }, - { .name = "imx-fiq-pcm-audio", }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(platform, imx_pcm_devtype); - -static struct platform_driver imx_pcm_driver = { - .driver = { - .name = "imx-pcm", - .owner = THIS_MODULE, - }, - .id_table = imx_pcm_devtype, - .probe = imx_pcm_probe, - .remove = imx_pcm_remove, -}; -module_platform_driver(imx_pcm_driver); - -MODULE_DESCRIPTION("Freescale i.MX PCM driver"); -MODULE_AUTHOR("Sascha Hauer "); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/fsl/imx-pcm.h b/sound/soc/fsl/imx-pcm.h index 073bf389c02e..67f656c7c320 100644 --- a/sound/soc/fsl/imx-pcm.h +++ b/sound/soc/fsl/imx-pcm.h @@ -32,11 +32,6 @@ imx_pcm_dma_params_init_data(struct imx_dma_data *dma_data, dma_data->peripheral_type = IMX_DMATYPE_SSI; } -int snd_imx_pcm_mmap(struct snd_pcm_substream *substream, - struct vm_area_struct *vma); -int imx_pcm_new(struct snd_soc_pcm_runtime *rtd); -void imx_pcm_free(struct snd_pcm *pcm); - #ifdef CONFIG_SND_SOC_IMX_PCM_DMA int imx_pcm_dma_init(struct platform_device *pdev); void imx_pcm_dma_exit(struct platform_device *pdev); -- cgit v1.2.3 From 18032ca062e621e15683cb61c066ef3dc5414a7b Mon Sep 17 00:00:00 2001 From: David Quigley Date: Thu, 2 May 2013 13:19:10 -0400 Subject: NFSD: Server implementation of MAC Labeling Implement labeled NFS on the server: encoding and decoding, and writing and reading, of file labels. Enabled with CONFIG_NFSD_V4_SECURITY_LABEL. Signed-off-by: Matthew N. Dodd Signed-off-by: Miguel Rodel Felipe Signed-off-by: Phua Eu Gene Signed-off-by: Khin Mi Mi Aung Signed-off-by: J. Bruce Fields --- fs/nfsd/Kconfig | 16 ++++++++ fs/nfsd/nfs4proc.c | 41 ++++++++++++++++++++ fs/nfsd/nfs4xdr.c | 108 +++++++++++++++++++++++++++++++++++++++++++++++++---- fs/nfsd/nfsd.h | 18 ++++++++- fs/nfsd/vfs.c | 28 ++++++++++++++ fs/nfsd/vfs.h | 2 + fs/nfsd/xdr4.h | 4 ++ 7 files changed, 207 insertions(+), 10 deletions(-) diff --git a/fs/nfsd/Kconfig b/fs/nfsd/Kconfig index 430b6872806f..dc8f1ef665ce 100644 --- a/fs/nfsd/Kconfig +++ b/fs/nfsd/Kconfig @@ -81,6 +81,22 @@ config NFSD_V4 If unsure, say N. +config NFSD_V4_SECURITY_LABEL + bool "Provide Security Label support for NFSv4 server" + depends on NFSD_V4 && SECURITY + help + + Say Y here if you want enable fine-grained security label attribute + support for NFS version 4. Security labels allow security modules like + SELinux and Smack to label files to facilitate enforcement of their policies. + Without this an NFSv4 mount will have the same label on each file. + + If you do not wish to enable fine-grained security labels SELinux or + Smack policies on NFSv4 files, say N. + + WARNING: there is still a chance of backwards-incompatible protocol changes. + For now we recommend "Y" only for developers and testers." + config NFSD_FAULT_INJECTION bool "NFS server manual fault injection" depends on NFSD_V4 && DEBUG_KERNEL diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 27d74a294515..1a1ff247bc59 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -42,6 +42,36 @@ #include "current_stateid.h" #include "netns.h" +#ifdef CONFIG_NFSD_V4_SECURITY_LABEL +#include + +static inline void +nfsd4_security_inode_setsecctx(struct svc_fh *resfh, struct xdr_netobj *label, u32 *bmval) +{ + struct inode *inode = resfh->fh_dentry->d_inode; + int status; + + mutex_lock(&inode->i_mutex); + status = security_inode_setsecctx(resfh->fh_dentry, + label->data, label->len); + mutex_unlock(&inode->i_mutex); + + if (status) + /* + * XXX: We should really fail the whole open, but we may + * already have created a new file, so it may be too + * late. For now this seems the least of evils: + */ + bmval[2] &= ~FATTR4_WORD2_SECURITY_LABEL; + + return; +} +#else +static inline void +nfsd4_security_inode_setsecctx(struct svc_fh *resfh, struct xdr_netobj *label, u32 *bmval) +{ } +#endif + #define NFSDDBG_FACILITY NFSDDBG_PROC static u32 nfsd_attrmask[] = { @@ -239,6 +269,9 @@ do_open_lookup(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, stru (u32 *)open->op_verf.data, &open->op_truncate, &open->op_created); + if (!status && open->op_label.len) + nfsd4_security_inode_setsecctx(resfh, &open->op_label, open->op_bmval); + /* * Following rfc 3530 14.2.16, use the returned bitmask * to indicate which attributes we used to store the @@ -637,6 +670,9 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if (status) goto out; + if (create->cr_label.len) + nfsd4_security_inode_setsecctx(&resfh, &create->cr_label, create->cr_bmval); + if (create->cr_acl != NULL) do_set_nfs4_acl(rqstp, &resfh, create->cr_acl, create->cr_bmval); @@ -916,6 +952,11 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, setattr->sa_acl); if (status) goto out; + if (setattr->sa_label.len) + status = nfsd4_set_nfs4_label(rqstp, &cstate->current_fh, + &setattr->sa_label); + if (status) + goto out; status = nfsd_setattr(rqstp, &cstate->current_fh, &setattr->sa_iattr, 0, (time_t)0); out: diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 9aeacddafa3f..dfca5121de53 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -55,6 +55,11 @@ #include "cache.h" #include "netns.h" +#ifdef CONFIG_NFSD_V4_SECURITY_LABEL +#include +#endif + + #define NFSDDBG_FACILITY NFSDDBG_XDR /* @@ -242,7 +247,8 @@ nfsd4_decode_bitmap(struct nfsd4_compoundargs *argp, u32 *bmval) static __be32 nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, - struct iattr *iattr, struct nfs4_acl **acl) + struct iattr *iattr, struct nfs4_acl **acl, + struct xdr_netobj *label) { int expected_len, len = 0; u32 dummy32; @@ -380,6 +386,32 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, goto xdr_error; } } + + label->len = 0; +#ifdef CONFIG_NFSD_V4_SECURITY_LABEL + if (bmval[2] & FATTR4_WORD2_SECURITY_LABEL) { + READ_BUF(4); + len += 4; + READ32(dummy32); /* lfs: we don't use it */ + READ_BUF(4); + len += 4; + READ32(dummy32); /* pi: we don't use it either */ + READ_BUF(4); + len += 4; + READ32(dummy32); + READ_BUF(dummy32); + if (dummy32 > NFSD4_MAX_SEC_LABEL_LEN) + return nfserr_badlabel; + len += (XDR_QUADLEN(dummy32) << 2); + READMEM(buf, dummy32); + label->data = kzalloc(dummy32 + 1, GFP_KERNEL); + if (!label->data) + return nfserr_jukebox; + defer_free(argp, kfree, label->data); + memcpy(label->data, buf, dummy32); + } +#endif + if (bmval[0] & ~NFSD_WRITEABLE_ATTRS_WORD0 || bmval[1] & ~NFSD_WRITEABLE_ATTRS_WORD1 || bmval[2] & ~NFSD_WRITEABLE_ATTRS_WORD2) @@ -576,7 +608,7 @@ nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create return status; status = nfsd4_decode_fattr(argp, create->cr_bmval, &create->cr_iattr, - &create->cr_acl); + &create->cr_acl, &create->cr_label); if (status) goto out; @@ -827,7 +859,7 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open) case NFS4_CREATE_UNCHECKED: case NFS4_CREATE_GUARDED: status = nfsd4_decode_fattr(argp, open->op_bmval, - &open->op_iattr, &open->op_acl); + &open->op_iattr, &open->op_acl, &open->op_label); if (status) goto out; break; @@ -841,7 +873,7 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open) READ_BUF(NFS4_VERIFIER_SIZE); COPYMEM(open->op_verf.data, NFS4_VERIFIER_SIZE); status = nfsd4_decode_fattr(argp, open->op_bmval, - &open->op_iattr, &open->op_acl); + &open->op_iattr, &open->op_acl, &open->op_label); if (status) goto out; break; @@ -1063,7 +1095,7 @@ nfsd4_decode_setattr(struct nfsd4_compoundargs *argp, struct nfsd4_setattr *seta if (status) return status; return nfsd4_decode_fattr(argp, setattr->sa_bmval, &setattr->sa_iattr, - &setattr->sa_acl); + &setattr->sa_acl, &setattr->sa_label); } static __be32 @@ -1954,6 +1986,36 @@ nfsd4_encode_aclname(struct svc_rqst *rqstp, struct nfs4_ace *ace, FATTR4_WORD0_RDATTR_ERROR) #define WORD1_ABSENT_FS_ATTRS FATTR4_WORD1_MOUNTED_ON_FILEID +#ifdef CONFIG_NFSD_V4_SECURITY_LABEL +static inline __be32 +nfsd4_encode_security_label(struct svc_rqst *rqstp, void *context, int len, __be32 **pp, int *buflen) +{ + __be32 *p = *pp; + + if (*buflen < ((XDR_QUADLEN(len) << 2) + 4 + 4 + 4)) + return nfserr_resource; + + /* + * For now we use a 0 here to indicate the null translation; in + * the future we may place a call to translation code here. + */ + if ((*buflen -= 8) < 0) + return nfserr_resource; + + WRITE32(0); /* lfs */ + WRITE32(0); /* pi */ + p = xdr_encode_opaque(p, context, len); + *buflen -= (XDR_QUADLEN(len) << 2) + 4; + + *pp = p; + return 0; +} +#else +static inline __be32 +nfsd4_encode_security_label(struct svc_rqst *rqstp, struct dentry *dentry, __be32 **pp, int *buflen) +{ return 0; } +#endif + static __be32 fattr_handle_absent_fs(u32 *bmval0, u32 *bmval1, u32 *rdattr_err) { /* As per referral draft: */ @@ -2013,6 +2075,9 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, int err; int aclsupport = 0; struct nfs4_acl *acl = NULL; + void *context = NULL; + int contextlen; + bool contextsupport = false; struct nfsd4_compoundres *resp = rqstp->rq_resp; u32 minorversion = resp->cstate.minorversion; struct path path = { @@ -2066,6 +2131,21 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, } } +#ifdef CONFIG_NFSD_V4_SECURITY_LABEL + if ((bmval[2] & FATTR4_WORD2_SECURITY_LABEL) || + bmval[0] & FATTR4_WORD0_SUPPORTED_ATTRS) { + err = security_inode_getsecctx(dentry->d_inode, + &context, &contextlen); + contextsupport = (err == 0); + if (bmval2 & FATTR4_WORD2_SECURITY_LABEL) { + if (err == -EOPNOTSUPP) + bmval2 &= ~FATTR4_WORD2_SECURITY_LABEL; + else if (err) + goto out_nfserr; + } + } +#endif /* CONFIG_NFSD_V4_SECURITY_LABEL */ + if (bmval2) { if ((buflen -= 16) < 0) goto out_resource; @@ -2094,6 +2174,8 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, if (!aclsupport) word0 &= ~FATTR4_WORD0_ACL; + if (!contextsupport) + word2 &= ~FATTR4_WORD2_SECURITY_LABEL; if (!word2) { if ((buflen -= 12) < 0) goto out_resource; @@ -2401,6 +2483,12 @@ out_acl: get_parent_attributes(exp, &stat); WRITE64(stat.ino); } + if (bmval2 & FATTR4_WORD2_SECURITY_LABEL) { + status = nfsd4_encode_security_label(rqstp, context, + contextlen, &p, &buflen); + if (status) + goto out; + } if (bmval2 & FATTR4_WORD2_SUPPATTR_EXCLCREAT) { WRITE32(3); WRITE32(NFSD_SUPPATTR_EXCLCREAT_WORD0); @@ -2413,6 +2501,8 @@ out_acl: status = nfs_ok; out: + if (context) + security_release_secctx(context, contextlen); kfree(acl); if (fhp == &tempfh) fh_put(&tempfh); @@ -3177,16 +3267,18 @@ nfsd4_encode_setattr(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4 { __be32 *p; - RESERVE_SPACE(12); + RESERVE_SPACE(16); if (nfserr) { - WRITE32(2); + WRITE32(3); + WRITE32(0); WRITE32(0); WRITE32(0); } else { - WRITE32(2); + WRITE32(3); WRITE32(setattr->sa_bmval[0]); WRITE32(setattr->sa_bmval[1]); + WRITE32(setattr->sa_bmval[2]); } ADJUST_ARGS(); return nfserr; diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h index 15e7e1d531f0..2bbd94e51efc 100644 --- a/fs/nfsd/nfsd.h +++ b/fs/nfsd/nfsd.h @@ -328,6 +328,13 @@ void nfsd_lockd_shutdown(void); #define NFSD4_1_SUPPORTED_ATTRS_WORD2 \ (NFSD4_SUPPORTED_ATTRS_WORD2 | FATTR4_WORD2_SUPPATTR_EXCLCREAT) +#ifdef CONFIG_NFSD_V4_SECURITY_LABEL +#define NFSD4_2_SUPPORTED_ATTRS_WORD2 \ + (NFSD4_1_SUPPORTED_ATTRS_WORD2 | FATTR4_WORD2_SECURITY_LABEL) +#else +#define NFSD4_2_SUPPORTED_ATTRS_WORD2 0 +#endif + static inline u32 nfsd_suppattrs0(u32 minorversion) { return minorversion ? NFSD4_1_SUPPORTED_ATTRS_WORD0 @@ -342,8 +349,11 @@ static inline u32 nfsd_suppattrs1(u32 minorversion) static inline u32 nfsd_suppattrs2(u32 minorversion) { - return minorversion ? NFSD4_1_SUPPORTED_ATTRS_WORD2 - : NFSD4_SUPPORTED_ATTRS_WORD2; + switch (minorversion) { + default: return NFSD4_2_SUPPORTED_ATTRS_WORD2; + case 1: return NFSD4_1_SUPPORTED_ATTRS_WORD2; + case 0: return NFSD4_SUPPORTED_ATTRS_WORD2; + } } /* These will return ERR_INVAL if specified in GETATTR or READDIR. */ @@ -356,7 +366,11 @@ static inline u32 nfsd_suppattrs2(u32 minorversion) #define NFSD_WRITEABLE_ATTRS_WORD1 \ (FATTR4_WORD1_MODE | FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP \ | FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_MODIFY_SET) +#ifdef CONFIG_NFSD_V4_SECURITY_LABEL +#define NFSD_WRITEABLE_ATTRS_WORD2 FATTR4_WORD2_SECURITY_LABEL +#else #define NFSD_WRITEABLE_ATTRS_WORD2 0 +#endif #define NFSD_SUPPATTR_EXCLCREAT_WORD0 \ NFSD_WRITEABLE_ATTRS_WORD0 diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 84ce601d8063..1e757fa45c40 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -28,6 +28,7 @@ #include #include #include +#include #ifdef CONFIG_NFSD_V3 #include "xdr3.h" @@ -621,6 +622,33 @@ int nfsd4_is_junction(struct dentry *dentry) return 0; return 1; } +#ifdef CONFIG_NFSD_V4_SECURITY_LABEL +__be32 nfsd4_set_nfs4_label(struct svc_rqst *rqstp, struct svc_fh *fhp, + struct xdr_netobj *label) +{ + __be32 error; + int host_error; + struct dentry *dentry; + + error = fh_verify(rqstp, fhp, 0 /* S_IFREG */, NFSD_MAY_SATTR); + if (error) + return error; + + dentry = fhp->fh_dentry; + + mutex_lock(&dentry->d_inode->i_mutex); + host_error = security_inode_setsecctx(dentry, label->data, label->len); + mutex_unlock(&dentry->d_inode->i_mutex); + return nfserrno(host_error); +} +#else +__be32 nfsd4_set_nfs4_label(struct svc_rqst *rqstp, struct svc_fh *fhp, + struct xdr_netobj *label) +{ + return nfserr_notsupp; +} +#endif + #endif /* defined(CONFIG_NFSD_V4) */ #ifdef CONFIG_NFSD_V3 diff --git a/fs/nfsd/vfs.h b/fs/nfsd/vfs.h index 8d2b40d71669..a4be2e389670 100644 --- a/fs/nfsd/vfs.h +++ b/fs/nfsd/vfs.h @@ -55,6 +55,8 @@ int nfsd_mountpoint(struct dentry *, struct svc_export *); __be32 nfsd4_set_nfs4_acl(struct svc_rqst *, struct svc_fh *, struct nfs4_acl *); int nfsd4_get_nfs4_acl(struct svc_rqst *, struct dentry *, struct nfs4_acl **); +__be32 nfsd4_set_nfs4_label(struct svc_rqst *, struct svc_fh *, + struct xdr_netobj *); #endif /* CONFIG_NFSD_V4 */ __be32 nfsd_create(struct svc_rqst *, struct svc_fh *, char *name, int len, struct iattr *attrs, diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h index 3b271d2092b6..b3ed6446ed8e 100644 --- a/fs/nfsd/xdr4.h +++ b/fs/nfsd/xdr4.h @@ -40,6 +40,7 @@ #include "state.h" #include "nfsd.h" +#define NFSD4_MAX_SEC_LABEL_LEN 2048 #define NFSD4_MAX_TAGLEN 128 #define XDR_LEN(n) (((n) + 3) & ~3) @@ -118,6 +119,7 @@ struct nfsd4_create { struct iattr cr_iattr; /* request */ struct nfsd4_change_info cr_cinfo; /* response */ struct nfs4_acl *cr_acl; + struct xdr_netobj cr_label; }; #define cr_linklen u.link.namelen #define cr_linkname u.link.name @@ -246,6 +248,7 @@ struct nfsd4_open { struct nfs4_file *op_file; /* used during processing */ struct nfs4_ol_stateid *op_stp; /* used during processing */ struct nfs4_acl *op_acl; + struct xdr_netobj op_label; }; #define op_iattr iattr @@ -330,6 +333,7 @@ struct nfsd4_setattr { u32 sa_bmval[3]; /* request */ struct iattr sa_iattr; /* request */ struct nfs4_acl *sa_acl; + struct xdr_netobj sa_label; }; struct nfsd4_setclientid { -- cgit v1.2.3 From 2fccbd9cc0fdca649b01f1e2d96e5ef85256341a Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 24 Sep 2012 15:53:29 -0400 Subject: sunrpc: server back channel needs no rpcbind method XPRT_BOUND is set on server backchannel xprts by xs_setup_bc_tcp() (using xprt_set_bound()), and is never cleared, so ->rpcbind() will never need to be called. Reported-by: "Myklebust, Trond" Signed-off-by: J. Bruce Fields --- net/sunrpc/xprtsock.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index ffd50348a509..5d6b0daf743d 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -2534,7 +2534,6 @@ static struct rpc_xprt_ops bc_tcp_ops = { .reserve_xprt = xprt_reserve_xprt, .release_xprt = xprt_release_xprt, .alloc_slot = xprt_alloc_slot, - .rpcbind = xs_local_rpcbind, .buf_alloc = bc_malloc, .buf_free = bc_free, .send_request = bc_send_request, -- cgit v1.2.3 From ba4e55bb67894136489b27372166416cd70b0756 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 15 May 2013 10:27:52 -0400 Subject: nfsd4: fix compile in !CONFIG_NFSD_V4_SECURITY_LABEL case Reported-by: kbuild test robot Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4xdr.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index dfca5121de53..170ea7e1ae25 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -2012,7 +2012,7 @@ nfsd4_encode_security_label(struct svc_rqst *rqstp, void *context, int len, __be } #else static inline __be32 -nfsd4_encode_security_label(struct svc_rqst *rqstp, struct dentry *dentry, __be32 **pp, int *buflen) +nfsd4_encode_security_label(struct svc_rqst *rqstp, void *context, int len, __be32 **pp, int *buflen) { return 0; } #endif @@ -2501,8 +2501,10 @@ out_acl: status = nfs_ok; out: +#ifdef CONFIG_NFSD_V4_SECURITY_LABEL if (context) security_release_secctx(context, contextlen); +#endif /* CONFIG_NFSD_V4_SECURITY_LABEL */ kfree(acl); if (fhp == &tempfh) fh_put(&tempfh); -- cgit v1.2.3 From 12b03188ab2afed784e416b4fb1366b4a6915ac0 Mon Sep 17 00:00:00 2001 From: Jon Mason Date: Mon, 6 May 2013 08:03:33 +0000 Subject: PCI: Work around Ivytown NTB BAR size issue Certain NTB devices have a hardware erratum where, regardless of pre-configured value, reading the BAR size returns 4096. To work around this issue, add a PCI quirk to read the appropriate values from an alternative register in PCI config space and move the resource endpoints to the appropriate location. Signed-off-by: Jon Mason Signed-off-by: Bjorn Helgaas --- drivers/pci/quirks.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 7d68aeebf56b..7f492574dcf4 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2865,6 +2865,31 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65f9, quirk_intel_mc_errata); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65fa, quirk_intel_mc_errata); +/* + * Ivytown NTB BAR sizes are misreported by the hardware due to an erratum. To + * work around this, query the size it should be configured to by the device and + * modify the resource end to correspond to this new size. + */ +static void quirk_intel_ntb(struct pci_dev *dev) +{ + int rc; + u8 val; + + rc = pci_read_config_byte(dev, 0x00D0, &val); + if (rc) + return; + + dev->resource[2].end = dev->resource[2].start + ((u64) 1 << val) - 1; + + rc = pci_read_config_byte(dev, 0x00D1, &val); + if (rc) + return; + + dev->resource[4].end = dev->resource[4].start + ((u64) 1 << val) - 1; +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0e08, quirk_intel_ntb); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0e0d, quirk_intel_ntb); + static ktime_t fixup_debug_start(struct pci_dev *dev, void (*fn)(struct pci_dev *dev)) { -- cgit v1.2.3 From f6c1c8ff439ccadc333b3920c7073e0792bcb9af Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 21 Jun 2012 23:48:50 -0600 Subject: PCI/ACPI: Check acpi_resource_to_address64() return value We should check the acpi_resource_to_address64() return value, which also removes the need to validate the resource type beforehand. No functional change. Found by Coverity (CID 113815). Signed-off-by: Bjorn Helgaas Acked-by: Rafael J. Wysocki --- drivers/acpi/pci_root.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 1dd6f6c85874..f6c0998f248a 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -100,13 +100,12 @@ get_root_bridge_busnr_callback(struct acpi_resource *resource, void *data) { struct resource *res = data; struct acpi_resource_address64 address; + acpi_status status; - if (resource->type != ACPI_RESOURCE_TYPE_ADDRESS16 && - resource->type != ACPI_RESOURCE_TYPE_ADDRESS32 && - resource->type != ACPI_RESOURCE_TYPE_ADDRESS64) + status = acpi_resource_to_address64(resource, &address); + if (ACPI_FAILURE(status)) return AE_OK; - acpi_resource_to_address64(resource, &address); if ((address.address_length > 0) && (address.resource_type == ACPI_BUS_NUMBER_RANGE)) { res->start = address.minimum; -- cgit v1.2.3 From 0061d53daf26ff713ab43ab84ae5c44b5edbefa9 Mon Sep 17 00:00:00 2001 From: Marcelo Tosatti Date: Thu, 9 May 2013 20:21:41 -0300 Subject: KVM: x86: limit difference between kvmclock updates kvmclock updates which are isolated to a given vcpu, such as vcpu->cpu migration, should not allow system_timestamp from the rest of the vcpus to remain static. Otherwise ntp frequency correction applies to one vcpu's system_timestamp but not the others. So in those cases, request a kvmclock update for all vcpus. The worst case for a remote vcpu to update its kvmclock is then bounded by maximum nohz sleep latency. Signed-off-by: Marcelo Tosatti Signed-off-by: Gleb Natapov --- arch/x86/kvm/x86.c | 30 ++++++++++++++++++++++++++++-- include/linux/kvm_host.h | 1 + 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 094b5d96ab14..8d28810a5f88 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -1588,6 +1588,30 @@ static int kvm_guest_time_update(struct kvm_vcpu *v) return 0; } +/* + * kvmclock updates which are isolated to a given vcpu, such as + * vcpu->cpu migration, should not allow system_timestamp from + * the rest of the vcpus to remain static. Otherwise ntp frequency + * correction applies to one vcpu's system_timestamp but not + * the others. + * + * So in those cases, request a kvmclock update for all vcpus. + * The worst case for a remote vcpu to update its kvmclock + * is then bounded by maximum nohz sleep latency. + */ + +static void kvm_gen_kvmclock_update(struct kvm_vcpu *v) +{ + int i; + struct kvm *kvm = v->kvm; + struct kvm_vcpu *vcpu; + + kvm_for_each_vcpu(i, vcpu, kvm) { + set_bit(KVM_REQ_CLOCK_UPDATE, &vcpu->requests); + kvm_vcpu_kick(vcpu); + } +} + static bool msr_mtrr_valid(unsigned msr) { switch (msr) { @@ -1985,7 +2009,7 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) kvmclock_reset(vcpu); vcpu->arch.time = data; - kvm_make_request(KVM_REQ_CLOCK_UPDATE, vcpu); + kvm_make_request(KVM_REQ_GLOBAL_CLOCK_UPDATE, vcpu); /* we verify if the enable bit is set... */ if (!(data & 1)) @@ -2702,7 +2726,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) * kvmclock on vcpu->cpu migration */ if (!vcpu->kvm->arch.use_master_clock || vcpu->cpu == -1) - kvm_make_request(KVM_REQ_CLOCK_UPDATE, vcpu); + kvm_make_request(KVM_REQ_GLOBAL_CLOCK_UPDATE, vcpu); if (vcpu->cpu != cpu) kvm_migrate_timers(vcpu); vcpu->cpu = cpu; @@ -5703,6 +5727,8 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu) __kvm_migrate_timers(vcpu); if (kvm_check_request(KVM_REQ_MASTERCLOCK_UPDATE, vcpu)) kvm_gen_update_masterclock(vcpu->kvm); + if (kvm_check_request(KVM_REQ_GLOBAL_CLOCK_UPDATE, vcpu)) + kvm_gen_kvmclock_update(vcpu); if (kvm_check_request(KVM_REQ_CLOCK_UPDATE, vcpu)) { r = kvm_guest_time_update(vcpu); if (unlikely(r)) diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index f0eea07d2c2b..d9a3c30eab2e 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -124,6 +124,7 @@ static inline bool is_error_page(struct page *page) #define KVM_REQ_MCLOCK_INPROGRESS 19 #define KVM_REQ_EPR_EXIT 20 #define KVM_REQ_SCAN_IOAPIC 21 +#define KVM_REQ_GLOBAL_CLOCK_UPDATE 22 #define KVM_USERSPACE_IRQ_SOURCE_ID 0 #define KVM_IRQFD_RESAMPLE_IRQ_SOURCE_ID 1 -- cgit v1.2.3 From 9394c1c65e61eb6f4c1c99f342b49e451ec337b6 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Mon, 11 Mar 2013 13:52:12 +0100 Subject: ARM: 7669/1: keep __my_cpu_offset consistent with generic one Commit 14318efb(ARM: 7587/1: implement optimized percpu variable access) introduces arm's __my_cpu_offset to optimize percpu vaiable access, which really works well on hackbench, but will cause __my_cpu_offset to return garbage value before it is initialized in cpu_init() called by setup_arch, so accessing percpu variable before setup_arch may cause kernel hang. But generic __my_cpu_offset always returns zero before percpu area is brought up, and won't hang kernel. So the patch tries to clear __my_cpu_offset on boot CPU early to avoid boot hang. At least now percpu variable is accessed by lockdep before setup_arch(), and enabling CONFIG_LOCK_STAT or CONFIG_DEBUG_LOCKDEP can trigger kernel hang. Signed-off-by: Ming Lei Signed-off-by: Russell King --- arch/arm/kernel/setup.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index 1522c7ae31b0..dd1c6aacbaf9 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -456,6 +456,13 @@ void __init smp_setup_processor_id(void) for (i = 1; i < nr_cpu_ids; ++i) cpu_logical_map(i) = i == cpu ? 0 : i; + /* + * clear __my_cpu_offset on boot CPU to avoid hang caused by + * using percpu variable early, for example, lockdep will + * access percpu variable inside lock_release + */ + set_my_cpu_offset(0); + printk(KERN_INFO "Booting Linux on physical CPU 0x%x\n", mpidr); } -- cgit v1.2.3 From 049f3e84d34bfc37f25c91715f2e24a90fb8665e Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 29 Apr 2013 16:06:10 +0100 Subject: ARM: 7705/1: use optimized do_div only for EABI In OABI configurations, some uses of the do_div function cause gcc to run out of registers. To work around that, we can force the use of the out-of-line version for configurations that build a OABI kernel. Without this patch, building netx_defconfig results in: net/core/pktgen.c: In function 'pktgen_if_show': net/core/pktgen.c:682:2775: error: can't find a register in class 'GENERAL_REGS' while reloading 'asm' net/core/pktgen.c:682:3153: error: can't find a register in class 'GENERAL_REGS' while reloading 'asm' net/core/pktgen.c:682:2775: error: 'asm' operand has impossible constraints net/core/pktgen.c:682:3153: error: 'asm' operand has impossible constraints Signed-off-by: Arnd Bergmann Signed-off-by: Russell King --- arch/arm/include/asm/div64.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/include/asm/div64.h b/arch/arm/include/asm/div64.h index fe92ccf1d0b0..191ada6e4d2d 100644 --- a/arch/arm/include/asm/div64.h +++ b/arch/arm/include/asm/div64.h @@ -46,7 +46,7 @@ __rem; \ }) -#if __GNUC__ < 4 +#if __GNUC__ < 4 || !defined(CONFIG_AEABI) /* * gcc versions earlier than 4.0 are simply too problematic for the -- cgit v1.2.3 From faefd550c45d8d314e8f260f21565320355c947f Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Wed, 15 May 2013 09:39:17 +0100 Subject: ARM: 7722/1: zImage: Convert 32bits memory size and address from ATAG to 64bits DTB When CONFIG_ARM_APPENDED_DTB is selected, if the bootloader provides an ATAG_MEM it replaces the memory size and the memory address in the memory node of the device tree. In the case of a system which can handle more than 4GB, the memory node cell size is 4: each data (memory size and memory address) are 64 bits and then use 2 cells. The current code in atags_to_fdt.c made the assumption of a cell size of 2 (one cell for the memory size and one cell for the memory address), this leads to an improper write of the data and ends with a boot hang. This patch writes the memory size and the memory address on the memory node in the device tree depending of the size of the memory node (32 bits or 64 bits). It has been tested in the 2 cases: - with a dtb using skeleton.dtsi - and with a dtb using skeleton64.dtsi Signed-off-by: Gregory CLEMENT Acked-by: Nicolas Pitre Signed-off-by: Russell King --- arch/arm/boot/compressed/atags_to_fdt.c | 44 ++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/arch/arm/boot/compressed/atags_to_fdt.c b/arch/arm/boot/compressed/atags_to_fdt.c index aabc02a68482..d1153c8a765a 100644 --- a/arch/arm/boot/compressed/atags_to_fdt.c +++ b/arch/arm/boot/compressed/atags_to_fdt.c @@ -53,6 +53,17 @@ static const void *getprop(const void *fdt, const char *node_path, return fdt_getprop(fdt, offset, property, len); } +static uint32_t get_cell_size(const void *fdt) +{ + int len; + uint32_t cell_size = 1; + const uint32_t *size_len = getprop(fdt, "/", "#size-cells", &len); + + if (size_len) + cell_size = fdt32_to_cpu(*size_len); + return cell_size; +} + static void merge_fdt_bootargs(void *fdt, const char *fdt_cmdline) { char cmdline[COMMAND_LINE_SIZE]; @@ -95,9 +106,11 @@ static void merge_fdt_bootargs(void *fdt, const char *fdt_cmdline) int atags_to_fdt(void *atag_list, void *fdt, int total_space) { struct tag *atag = atag_list; - uint32_t mem_reg_property[2 * NR_BANKS]; + /* In the case of 64 bits memory size, need to reserve 2 cells for + * address and size for each bank */ + uint32_t mem_reg_property[2 * 2 * NR_BANKS]; int memcount = 0; - int ret; + int ret, memsize; /* make sure we've got an aligned pointer */ if ((u32)atag_list & 0x3) @@ -137,8 +150,25 @@ int atags_to_fdt(void *atag_list, void *fdt, int total_space) continue; if (!atag->u.mem.size) continue; - mem_reg_property[memcount++] = cpu_to_fdt32(atag->u.mem.start); - mem_reg_property[memcount++] = cpu_to_fdt32(atag->u.mem.size); + memsize = get_cell_size(fdt); + + if (memsize == 2) { + /* if memsize is 2, that means that + * each data needs 2 cells of 32 bits, + * so the data are 64 bits */ + uint64_t *mem_reg_prop64 = + (uint64_t *)mem_reg_property; + mem_reg_prop64[memcount++] = + cpu_to_fdt64(atag->u.mem.start); + mem_reg_prop64[memcount++] = + cpu_to_fdt64(atag->u.mem.size); + } else { + mem_reg_property[memcount++] = + cpu_to_fdt32(atag->u.mem.start); + mem_reg_property[memcount++] = + cpu_to_fdt32(atag->u.mem.size); + } + } else if (atag->hdr.tag == ATAG_INITRD2) { uint32_t initrd_start, initrd_size; initrd_start = atag->u.initrd.start; @@ -150,8 +180,10 @@ int atags_to_fdt(void *atag_list, void *fdt, int total_space) } } - if (memcount) - setprop(fdt, "/memory", "reg", mem_reg_property, 4*memcount); + if (memcount) { + setprop(fdt, "/memory", "reg", mem_reg_property, + 4 * memcount * memsize); + } return fdt_pack(fdt); } -- cgit v1.2.3 From 3b656fed6ff65d6d268da9ed0760c2a58d125771 Mon Sep 17 00:00:00 2001 From: Christian Daudt Date: Thu, 9 May 2013 22:21:01 +0100 Subject: ARM: 7716/1: bcm281xx: Add L2 support for Rev A2 chips Rev A2 SoCs have an unorthodox memory re-mapping and this needs to be reflected in the cache operations. This patch adds new outer cache functions for the l2x0 driver to support this SoC revision. It also adds a new compatible value for the cache to enable this functionality. Updates from V1: - remove section 1 altogether and note that in comments - simplify section selection caused by section 1 removal - BUG_ON just in case section 1 shows up Signed-off-by: Christian Daudt Reviewed-by: Will Deacon Acked-by: Olof Johansson Signed-off-by: Russell King --- Documentation/devicetree/bindings/arm/l2cc.txt | 3 + arch/arm/boot/dts/bcm11351.dtsi | 8 +- arch/arm/mm/cache-l2x0.c | 158 +++++++++++++++++++++++++ 3 files changed, 165 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/arm/l2cc.txt b/Documentation/devicetree/bindings/arm/l2cc.txt index cbef09b5c8a7..69ddf9fad2dc 100644 --- a/Documentation/devicetree/bindings/arm/l2cc.txt +++ b/Documentation/devicetree/bindings/arm/l2cc.txt @@ -16,6 +16,9 @@ Required properties: performs the same operation). "marvell,"aurora-outer-cache: Marvell Controller designed to be compatible with the ARM one with outer cache mode. + "bcm,bcm11351-a2-pl310-cache": For Broadcom bcm11351 chipset where an + offset needs to be added to the address before passing down to the L2 + cache controller - cache-unified : Specifies the cache is a unified cache. - cache-level : Should be set to 2 for a level 2 cache. - reg : Physical base address and size of cache controller's memory mapped diff --git a/arch/arm/boot/dts/bcm11351.dtsi b/arch/arm/boot/dts/bcm11351.dtsi index 41b2c6c33f09..5e48c85abc2f 100644 --- a/arch/arm/boot/dts/bcm11351.dtsi +++ b/arch/arm/boot/dts/bcm11351.dtsi @@ -47,10 +47,10 @@ }; L2: l2-cache { - compatible = "arm,pl310-cache"; - reg = <0x3ff20000 0x1000>; - cache-unified; - cache-level = <2>; + compatible = "bcm,bcm11351-a2-pl310-cache"; + reg = <0x3ff20000 0x1000>; + cache-unified; + cache-level = <2>; }; timer@35006000 { diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index c465faca51b0..d70e0aba0c9d 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -523,6 +523,147 @@ static void aurora_flush_range(unsigned long start, unsigned long end) } } +/* + * For certain Broadcom SoCs, depending on the address range, different offsets + * need to be added to the address before passing it to L2 for + * invalidation/clean/flush + * + * Section Address Range Offset EMI + * 1 0x00000000 - 0x3FFFFFFF 0x80000000 VC + * 2 0x40000000 - 0xBFFFFFFF 0x40000000 SYS + * 3 0xC0000000 - 0xFFFFFFFF 0x80000000 VC + * + * When the start and end addresses have crossed two different sections, we + * need to break the L2 operation into two, each within its own section. + * For example, if we need to invalidate addresses starts at 0xBFFF0000 and + * ends at 0xC0001000, we need do invalidate 1) 0xBFFF0000 - 0xBFFFFFFF and 2) + * 0xC0000000 - 0xC0001000 + * + * Note 1: + * By breaking a single L2 operation into two, we may potentially suffer some + * performance hit, but keep in mind the cross section case is very rare + * + * Note 2: + * We do not need to handle the case when the start address is in + * Section 1 and the end address is in Section 3, since it is not a valid use + * case + * + * Note 3: + * Section 1 in practical terms can no longer be used on rev A2. Because of + * that the code does not need to handle section 1 at all. + * + */ +#define BCM_SYS_EMI_START_ADDR 0x40000000UL +#define BCM_VC_EMI_SEC3_START_ADDR 0xC0000000UL + +#define BCM_SYS_EMI_OFFSET 0x40000000UL +#define BCM_VC_EMI_OFFSET 0x80000000UL + +static inline int bcm_addr_is_sys_emi(unsigned long addr) +{ + return (addr >= BCM_SYS_EMI_START_ADDR) && + (addr < BCM_VC_EMI_SEC3_START_ADDR); +} + +static inline unsigned long bcm_l2_phys_addr(unsigned long addr) +{ + if (bcm_addr_is_sys_emi(addr)) + return addr + BCM_SYS_EMI_OFFSET; + else + return addr + BCM_VC_EMI_OFFSET; +} + +static void bcm_inv_range(unsigned long start, unsigned long end) +{ + unsigned long new_start, new_end; + + BUG_ON(start < BCM_SYS_EMI_START_ADDR); + + if (unlikely(end <= start)) + return; + + new_start = bcm_l2_phys_addr(start); + new_end = bcm_l2_phys_addr(end); + + /* normal case, no cross section between start and end */ + if (likely(bcm_addr_is_sys_emi(end) || !bcm_addr_is_sys_emi(start))) { + l2x0_inv_range(new_start, new_end); + return; + } + + /* They cross sections, so it can only be a cross from section + * 2 to section 3 + */ + l2x0_inv_range(new_start, + bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR-1)); + l2x0_inv_range(bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR), + new_end); +} + +static void bcm_clean_range(unsigned long start, unsigned long end) +{ + unsigned long new_start, new_end; + + BUG_ON(start < BCM_SYS_EMI_START_ADDR); + + if (unlikely(end <= start)) + return; + + if ((end - start) >= l2x0_size) { + l2x0_clean_all(); + return; + } + + new_start = bcm_l2_phys_addr(start); + new_end = bcm_l2_phys_addr(end); + + /* normal case, no cross section between start and end */ + if (likely(bcm_addr_is_sys_emi(end) || !bcm_addr_is_sys_emi(start))) { + l2x0_clean_range(new_start, new_end); + return; + } + + /* They cross sections, so it can only be a cross from section + * 2 to section 3 + */ + l2x0_clean_range(new_start, + bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR-1)); + l2x0_clean_range(bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR), + new_end); +} + +static void bcm_flush_range(unsigned long start, unsigned long end) +{ + unsigned long new_start, new_end; + + BUG_ON(start < BCM_SYS_EMI_START_ADDR); + + if (unlikely(end <= start)) + return; + + if ((end - start) >= l2x0_size) { + l2x0_flush_all(); + return; + } + + new_start = bcm_l2_phys_addr(start); + new_end = bcm_l2_phys_addr(end); + + /* normal case, no cross section between start and end */ + if (likely(bcm_addr_is_sys_emi(end) || !bcm_addr_is_sys_emi(start))) { + l2x0_flush_range(new_start, new_end); + return; + } + + /* They cross sections, so it can only be a cross from section + * 2 to section 3 + */ + l2x0_flush_range(new_start, + bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR-1)); + l2x0_flush_range(bcm_l2_phys_addr(BCM_VC_EMI_SEC3_START_ADDR), + new_end); +} + static void __init l2x0_of_setup(const struct device_node *np, u32 *aux_val, u32 *aux_mask) { @@ -765,6 +906,21 @@ static const struct l2x0_of_data aurora_no_outer_data = { }, }; +static const struct l2x0_of_data bcm_l2x0_data = { + .setup = pl310_of_setup, + .save = pl310_save, + .outer_cache = { + .resume = pl310_resume, + .inv_range = bcm_inv_range, + .clean_range = bcm_clean_range, + .flush_range = bcm_flush_range, + .sync = l2x0_cache_sync, + .flush_all = l2x0_flush_all, + .inv_all = l2x0_inv_all, + .disable = l2x0_disable, + }, +}; + static const struct of_device_id l2x0_ids[] __initconst = { { .compatible = "arm,pl310-cache", .data = (void *)&pl310_data }, { .compatible = "arm,l220-cache", .data = (void *)&l2x0_data }, @@ -773,6 +929,8 @@ static const struct of_device_id l2x0_ids[] __initconst = { .data = (void *)&aurora_no_outer_data}, { .compatible = "marvell,aurora-outer-cache", .data = (void *)&aurora_with_outer_data}, + { .compatible = "bcm,bcm11351-a2-pl310-cache", + .data = (void *)&bcm_l2x0_data}, {} }; -- cgit v1.2.3 From 35af577aacb2fd2a6b407953044aea1cf0546fad Mon Sep 17 00:00:00 2001 From: Gleb Natapov Date: Thu, 16 May 2013 11:55:51 +0300 Subject: KVM: MMU: clenaup locking in mmu_free_roots() Do locking around each case separately instead of having one lock and two unlocks. Move root_hpa assignment out of the lock. Signed-off-by: Gleb Natapov --- arch/x86/kvm/mmu.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c index 40d7b2ddc6c5..f8ca2f351395 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c @@ -2869,22 +2869,25 @@ static void mmu_free_roots(struct kvm_vcpu *vcpu) if (!VALID_PAGE(vcpu->arch.mmu.root_hpa)) return; - spin_lock(&vcpu->kvm->mmu_lock); + if (vcpu->arch.mmu.shadow_root_level == PT64_ROOT_LEVEL && (vcpu->arch.mmu.root_level == PT64_ROOT_LEVEL || vcpu->arch.mmu.direct_map)) { hpa_t root = vcpu->arch.mmu.root_hpa; + spin_lock(&vcpu->kvm->mmu_lock); sp = page_header(root); --sp->root_count; if (!sp->root_count && sp->role.invalid) { kvm_mmu_prepare_zap_page(vcpu->kvm, sp, &invalid_list); kvm_mmu_commit_zap_page(vcpu->kvm, &invalid_list); } - vcpu->arch.mmu.root_hpa = INVALID_PAGE; spin_unlock(&vcpu->kvm->mmu_lock); + vcpu->arch.mmu.root_hpa = INVALID_PAGE; return; } + + spin_lock(&vcpu->kvm->mmu_lock); for (i = 0; i < 4; ++i) { hpa_t root = vcpu->arch.mmu.pae_root[i]; -- cgit v1.2.3 From fc1f7d5606487ae28d6c84e95401952927d7379e Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 25 Apr 2013 20:31:43 +0000 Subject: clocksource: apb_timer: Remove unsused function Signed-off-by: Thomas Gleixner Acked-by: John Stultz Cc: Magnus Damm Acked-by: Jamie Iles Link: http://lkml.kernel.org/r/20130425143435.558006195@linutronix.de Signed-off-by: Thomas Gleixner --- drivers/clocksource/dw_apb_timer.c | 12 ------------ include/linux/dw_apb_timer.h | 1 - 2 files changed, 13 deletions(-) diff --git a/drivers/clocksource/dw_apb_timer.c b/drivers/clocksource/dw_apb_timer.c index 8c2a35f26d9b..e54ca1062d8e 100644 --- a/drivers/clocksource/dw_apb_timer.c +++ b/drivers/clocksource/dw_apb_timer.c @@ -387,15 +387,3 @@ cycle_t dw_apb_clocksource_read(struct dw_apb_clocksource *dw_cs) { return (cycle_t)~apbt_readl(&dw_cs->timer, APBTMR_N_CURRENT_VALUE); } - -/** - * dw_apb_clocksource_unregister() - unregister and free a clocksource. - * - * @dw_cs: The clocksource to unregister/free. - */ -void dw_apb_clocksource_unregister(struct dw_apb_clocksource *dw_cs) -{ - clocksource_unregister(&dw_cs->cs); - - kfree(dw_cs); -} diff --git a/include/linux/dw_apb_timer.h b/include/linux/dw_apb_timer.h index dd755ce2a5eb..b1cd9597c241 100644 --- a/include/linux/dw_apb_timer.h +++ b/include/linux/dw_apb_timer.h @@ -51,7 +51,6 @@ dw_apb_clocksource_init(unsigned rating, const char *name, void __iomem *base, void dw_apb_clocksource_register(struct dw_apb_clocksource *dw_cs); void dw_apb_clocksource_start(struct dw_apb_clocksource *dw_cs); cycle_t dw_apb_clocksource_read(struct dw_apb_clocksource *dw_cs); -void dw_apb_clocksource_unregister(struct dw_apb_clocksource *dw_cs); extern void dw_apb_timer_init(void); #endif /* __DW_APB_TIMER_H__ */ -- cgit v1.2.3 From 5d33b883aed81c6fbcd09c6f7c3619eee850a7e2 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 25 Apr 2013 20:31:43 +0000 Subject: clocksource: Always verify highres capability If a clocksource has a (wrong) high rating, but can't be used as a timebase for oneshot tick mode, it is unconditionally selected even when the system is already in oneshot tick mode. This causes full system failure. Verify the clocksource selection against the oneshot mode. Signed-off-by: Thomas Gleixner Acked-by: John Stultz Cc: Magnus Damm Link: http://lkml.kernel.org/r/20130425143435.635040849@linutronix.de Signed-off-by: Thomas Gleixner --- kernel/time/clocksource.c | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index c9583382141a..dda5c7130d93 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c @@ -553,6 +553,26 @@ static u64 clocksource_max_deferment(struct clocksource *cs) #ifndef CONFIG_ARCH_USES_GETTIMEOFFSET +static struct clocksource *clocksource_find_best(bool oneshot) +{ + struct clocksource *cs; + + if (!finished_booting || list_empty(&clocksource_list)) + return NULL; + + /* + * We pick the clocksource with the highest rating. If oneshot + * mode is active, we pick the highres valid clocksource with + * the best rating. + */ + list_for_each_entry(cs, &clocksource_list, list) { + if (oneshot && !(cs->flags & CLOCK_SOURCE_VALID_FOR_HRES)) + continue; + return cs; + } + return NULL; +} + /** * clocksource_select - Select the best clocksource available * @@ -563,12 +583,14 @@ static u64 clocksource_max_deferment(struct clocksource *cs) */ static void clocksource_select(void) { + bool oneshot = tick_oneshot_mode_active(); struct clocksource *best, *cs; - if (!finished_booting || list_empty(&clocksource_list)) + /* Find the best suitable clocksource */ + best = clocksource_find_best(oneshot); + if (!best) return; - /* First clocksource on the list has the best rating. */ - best = list_first_entry(&clocksource_list, struct clocksource, list); + /* Check for the override clocksource. */ list_for_each_entry(cs, &clocksource_list, list) { if (strcmp(cs->name, override_name) != 0) @@ -578,8 +600,7 @@ static void clocksource_select(void) * capable clocksource if the tick code is in oneshot * mode (highres or nohz) */ - if (!(cs->flags & CLOCK_SOURCE_VALID_FOR_HRES) && - tick_oneshot_mode_active()) { + if (!(cs->flags & CLOCK_SOURCE_VALID_FOR_HRES) && oneshot) { /* Override clocksource cannot be used. */ printk(KERN_WARNING "Override clocksource %s is not " "HRT compatible. Cannot switch while in " -- cgit v1.2.3 From ba919d1caa2e624eb8c6cae1f2ce0a253e697d45 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 25 Apr 2013 20:31:44 +0000 Subject: clocksource: Let timekeeping_notify return success/error timekeeping_notify() can fail due cs->enable() failure. Though the caller does not notice and happily keeps the wrong clocksource as the current one. Let the caller know about failure, so the current clocksource will be shown correctly in sysfs. Signed-off-by: Thomas Gleixner Acked-by: John Stultz Cc: Magnus Damm Link: http://lkml.kernel.org/r/20130425143435.696321912@linutronix.de Signed-off-by: Thomas Gleixner --- include/linux/clocksource.h | 2 +- kernel/time/clocksource.c | 6 +++--- kernel/time/timekeeping.c | 5 +++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index 7279b94c01da..aa6ba44e75d5 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h @@ -321,7 +321,7 @@ static inline void __clocksource_updatefreq_khz(struct clocksource *cs, u32 khz) } -extern void timekeeping_notify(struct clocksource *clock); +extern int timekeeping_notify(struct clocksource *clock); extern cycle_t clocksource_mmio_readl_up(struct clocksource *); extern cycle_t clocksource_mmio_readl_down(struct clocksource *); diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index dda5c7130d93..1923a340bd91 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c @@ -611,10 +611,10 @@ static void clocksource_select(void) best = cs; break; } - if (curr_clocksource != best) { - printk(KERN_INFO "Switching to clocksource %s\n", best->name); + + if (curr_clocksource != best && !timekeeping_notify(best)) { + pr_info("Switched to clocksource %s\n", best->name); curr_clocksource = best; - timekeeping_notify(curr_clocksource); } } diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 98cd470bbe49..da6e10c7a378 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -648,14 +648,15 @@ static int change_clocksource(void *data) * This function is called from clocksource.c after a new, better clock * source has been registered. The caller holds the clocksource_mutex. */ -void timekeeping_notify(struct clocksource *clock) +int timekeeping_notify(struct clocksource *clock) { struct timekeeper *tk = &timekeeper; if (tk->clock == clock) - return; + return 0; stop_machine(change_clocksource, clock, NULL); tick_clock_notify(); + return tk->clock == clock ? 0 : -1; } /** -- cgit v1.2.3 From 09ac369c825d9d593404306d59062d854b321e9b Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 25 Apr 2013 20:31:44 +0000 Subject: clocksource: Add module refcount Add a module refcount, so the current clocksource cannot be removed unconditionally. Signed-off-by: Thomas Gleixner Cc: John Stultz Cc: Magnus Damm Link: http://lkml.kernel.org/r/20130425143435.762417789@linutronix.de Signed-off-by: Thomas Gleixner --- include/linux/clocksource.h | 3 +++ kernel/time/timekeeping.c | 19 ++++++++++++++----- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index aa6ba44e75d5..32a895b114d8 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h @@ -21,6 +21,7 @@ /* clocksource cycle base type */ typedef u64 cycle_t; struct clocksource; +struct module; #ifdef CONFIG_ARCH_CLOCKSOURCE_DATA #include @@ -162,6 +163,7 @@ extern u64 timecounter_cyc2time(struct timecounter *tc, * @suspend: suspend function for the clocksource, if necessary * @resume: resume function for the clocksource, if necessary * @cycle_last: most recent cycle counter value seen by ::read() + * @owner: module reference, must be set by clocksource in modules */ struct clocksource { /* @@ -195,6 +197,7 @@ struct clocksource { cycle_t cs_last; cycle_t wd_last; #endif + struct module *owner; } ____cacheline_aligned; /* diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index da6e10c7a378..933efa4071c3 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -627,11 +627,20 @@ static int change_clocksource(void *data) write_seqcount_begin(&timekeeper_seq); timekeeping_forward_now(tk); - if (!new->enable || new->enable(new) == 0) { - old = tk->clock; - tk_setup_internals(tk, new); - if (old->disable) - old->disable(old); + /* + * If the cs is in module, get a module reference. Succeeds + * for built-in code (owner == NULL) as well. + */ + if (try_module_get(new->owner)) { + if (!new->enable || new->enable(new) == 0) { + old = tk->clock; + tk_setup_internals(tk, new); + if (old->disable) + old->disable(old); + module_put(old->owner); + } else { + module_put(new->owner); + } } timekeeping_update(tk, true, true); -- cgit v1.2.3 From f5a2e34375a5e2b711aea488ac3ae50eeba6d57c Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 25 Apr 2013 20:31:45 +0000 Subject: clocksource: Allow clocksource select to skip current clocksource Preparatory patch for clocksource unbind support. Split out code from clocksource_select and modify it, so it skips the current clocksource on request and tries to find a fallback clocksource. Convert all existing users. No functional change. Signed-off-by: Thomas Gleixner Cc: John Stultz Cc: Magnus Damm Link: http://lkml.kernel.org/r/20130425143435.834965397@linutronix.de Signed-off-by: Thomas Gleixner --- kernel/time/clocksource.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index 1923a340bd91..9782997cb6cf 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c @@ -553,7 +553,7 @@ static u64 clocksource_max_deferment(struct clocksource *cs) #ifndef CONFIG_ARCH_USES_GETTIMEOFFSET -static struct clocksource *clocksource_find_best(bool oneshot) +static struct clocksource *clocksource_find_best(bool oneshot, bool skipcur) { struct clocksource *cs; @@ -566,6 +566,8 @@ static struct clocksource *clocksource_find_best(bool oneshot) * the best rating. */ list_for_each_entry(cs, &clocksource_list, list) { + if (skipcur && cs == curr_clocksource) + continue; if (oneshot && !(cs->flags & CLOCK_SOURCE_VALID_FOR_HRES)) continue; return cs; @@ -573,26 +575,20 @@ static struct clocksource *clocksource_find_best(bool oneshot) return NULL; } -/** - * clocksource_select - Select the best clocksource available - * - * Private function. Must hold clocksource_mutex when called. - * - * Select the clocksource with the best rating, or the clocksource, - * which is selected by userspace override. - */ -static void clocksource_select(void) +static void __clocksource_select(bool skipcur) { bool oneshot = tick_oneshot_mode_active(); struct clocksource *best, *cs; /* Find the best suitable clocksource */ - best = clocksource_find_best(oneshot); + best = clocksource_find_best(oneshot, skipcur); if (!best) return; /* Check for the override clocksource. */ list_for_each_entry(cs, &clocksource_list, list) { + if (skipcur && cs == curr_clocksource) + continue; if (strcmp(cs->name, override_name) != 0) continue; /* @@ -618,6 +614,19 @@ static void clocksource_select(void) } } +/** + * clocksource_select - Select the best clocksource available + * + * Private function. Must hold clocksource_mutex when called. + * + * Select the clocksource with the best rating, or the clocksource, + * which is selected by userspace override. + */ +static void clocksource_select(void) +{ + return __clocksource_select(false); +} + #else /* !CONFIG_ARCH_USES_GETTIMEOFFSET */ static inline void clocksource_select(void) { } -- cgit v1.2.3 From 29b5407819f59731c9423238fae03b756822708c Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 25 Apr 2013 20:31:45 +0000 Subject: clocksource: Split out user string input Split out the user string input for clocksource override. Preparatory patch for unbind. [ jstultz: Fix an off by one error ] Signed-off-by: Thomas Gleixner Cc: John Stultz Cc: Magnus Damm Link: http://lkml.kernel.org/r/20130425143435.895851338@linutronix.de Signed-off-by: Thomas Gleixner --- kernel/time/clocksource.c | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index 9782997cb6cf..d7f1a45c2fa5 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c @@ -174,7 +174,8 @@ clocks_calc_mult_shift(u32 *mult, u32 *shift, u32 from, u32 to, u32 maxsec) static struct clocksource *curr_clocksource; static LIST_HEAD(clocksource_list); static DEFINE_MUTEX(clocksource_mutex); -static char override_name[32]; +#define CS_NAME_LEN 32 +static char override_name[CS_NAME_LEN]; static int finished_booting; #ifdef CONFIG_CLOCKSOURCE_WATCHDOG @@ -838,6 +839,23 @@ sysfs_show_current_clocksources(struct device *dev, return count; } +static size_t clocksource_get_uname(const char *buf, char *dst, size_t cnt) +{ + size_t ret = cnt; + + /* strings from sysfs write are not 0 terminated! */ + if (!cnt || cnt >= CS_NAME_LEN) + return -EINVAL; + + /* strip of \n: */ + if (buf[cnt-1] == '\n') + cnt--; + if (cnt > 0) + memcpy(dst, buf, cnt); + dst[cnt] = 0; + return ret; +} + /** * sysfs_override_clocksource - interface for manually overriding clocksource * @dev: unused @@ -852,22 +870,13 @@ static ssize_t sysfs_override_clocksource(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - size_t ret = count; - - /* strings from sysfs write are not 0 terminated! */ - if (count >= sizeof(override_name)) - return -EINVAL; - - /* strip of \n: */ - if (buf[count-1] == '\n') - count--; + size_t ret; mutex_lock(&clocksource_mutex); - if (count > 0) - memcpy(override_name, buf, count); - override_name[count] = 0; - clocksource_select(); + ret = clocksource_get_uname(buf, override_name, count); + if (ret >= 0) + clocksource_select(); mutex_unlock(&clocksource_mutex); -- cgit v1.2.3 From 7eaeb34305dee26634f7c98ae62646da5cebe91d Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 25 Apr 2013 20:31:46 +0000 Subject: clocksource: Provide unbind interface in sysfs With the module refcount held for the current clocksource there is no way to unload the module. Provide a sysfs interface which allows to unbind the clocksource. One could argue that the clocksource override could be (ab)used to do so, but the clocksource override cannot be used from the kernel itself, while an unbind function can be used to programmatically check whether a clocksource can be shutdown or not. The unbind functionality uses the new skip current feature of clocksource_select and verifies that a fallback clocksource has been installed. If the clocksource which should be unbound is the current clocksource and no fallback can be found, unbind returns -EBUSY. This does not support the unbinding of a clocksource which is used as the watchdog clocksource. No point in fostering crappy hardware. Signed-off-by: Thomas Gleixner Cc: John Stultz Cc: Magnus Damm Link: http://lkml.kernel.org/r/20130425143435.964218245@linutronix.de Signed-off-by: Thomas Gleixner --- kernel/time/clocksource.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index d7f1a45c2fa5..791d1aeb17ac 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c @@ -440,6 +440,11 @@ static int clocksource_watchdog_kthread(void *data) return 0; } +static bool clocksource_is_watchdog(struct clocksource *cs) +{ + return cs == watchdog; +} + #else /* CONFIG_CLOCKSOURCE_WATCHDOG */ static void clocksource_enqueue_watchdog(struct clocksource *cs) @@ -451,6 +456,7 @@ static void clocksource_enqueue_watchdog(struct clocksource *cs) static inline void clocksource_dequeue_watchdog(struct clocksource *cs) { } static inline void clocksource_resume_watchdog(void) { } static inline int clocksource_watchdog_kthread(void *data) { return 0; } +static bool clocksource_is_watchdog(struct clocksource *cs) { return false; } #endif /* CONFIG_CLOCKSOURCE_WATCHDOG */ @@ -628,6 +634,11 @@ static void clocksource_select(void) return __clocksource_select(false); } +static void clocksource_select_fallback(void) +{ + return __clocksource_select(true); +} + #else /* !CONFIG_ARCH_USES_GETTIMEOFFSET */ static inline void clocksource_select(void) { } @@ -803,6 +814,29 @@ void clocksource_change_rating(struct clocksource *cs, int rating) } EXPORT_SYMBOL(clocksource_change_rating); +/* + * Unbind clocksource @cs. Called with clocksource_mutex held + */ +static int clocksource_unbind(struct clocksource *cs) +{ + /* + * I really can't convince myself to support this on hardware + * designed by lobotomized monkeys. + */ + if (clocksource_is_watchdog(cs)) + return -EBUSY; + + if (cs == curr_clocksource) { + /* Select and try to install a replacement clock source */ + clocksource_select_fallback(); + if (curr_clocksource == cs) + return -EBUSY; + } + clocksource_dequeue_watchdog(cs); + list_del_init(&cs->list); + return 0; +} + /** * clocksource_unregister - remove a registered clocksource * @cs: clocksource to be unregistered @@ -883,6 +917,40 @@ static ssize_t sysfs_override_clocksource(struct device *dev, return ret; } +/** + * sysfs_unbind_current_clocksource - interface for manually unbinding clocksource + * @dev: unused + * @attr: unused + * @buf: unused + * @count: length of buffer + * + * Takes input from sysfs interface for manually unbinding a clocksource. + */ +static ssize_t sysfs_unbind_clocksource(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct clocksource *cs; + char name[CS_NAME_LEN]; + size_t ret; + + ret = clocksource_get_uname(buf, name, count); + if (ret < 0) + return ret; + + ret = -ENODEV; + mutex_lock(&clocksource_mutex); + list_for_each_entry(cs, &clocksource_list, list) { + if (strcmp(cs->name, name)) + continue; + ret = clocksource_unbind(cs); + break; + } + mutex_unlock(&clocksource_mutex); + + return ret ? ret : count; +} + /** * sysfs_show_available_clocksources - sysfs interface for listing clocksource * @dev: unused @@ -925,6 +993,8 @@ sysfs_show_available_clocksources(struct device *dev, static DEVICE_ATTR(current_clocksource, 0644, sysfs_show_current_clocksources, sysfs_override_clocksource); +static DEVICE_ATTR(unbind_clocksource, 0200, NULL, sysfs_unbind_clocksource); + static DEVICE_ATTR(available_clocksource, 0444, sysfs_show_available_clocksources, NULL); @@ -948,6 +1018,9 @@ static int __init init_clocksource_sysfs(void) error = device_create_file( &device_clocksource, &dev_attr_current_clocksource); + if (!error) + error = device_create_file(&device_clocksource, + &dev_attr_unbind_clocksource); if (!error) error = device_create_file( &device_clocksource, -- cgit v1.2.3 From a89c7edbe7d7aa80f507915f3dd801211b116b79 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 25 Apr 2013 20:31:46 +0000 Subject: clocksource: Let clocksource_unregister() return success/error The unregister call can fail, if the clocksource is the current one and there is no replacement clocksource available. It can also fail, if the clocksource is the watchdog clocksource and I'm not going to provide support for this. Signed-off-by: Thomas Gleixner Cc: John Stultz Cc: Magnus Damm Link: http://lkml.kernel.org/r/20130425143436.029915527@linutronix.de Signed-off-by: Thomas Gleixner --- include/linux/clocksource.h | 2 +- kernel/time/clocksource.c | 33 ++++++++++++--------------------- 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index 32a895b114d8..2f39a4911668 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h @@ -282,7 +282,7 @@ static inline s64 clocksource_cyc2ns(cycle_t cycles, u32 mult, u32 shift) extern int clocksource_register(struct clocksource*); -extern void clocksource_unregister(struct clocksource*); +extern int clocksource_unregister(struct clocksource*); extern void clocksource_touch_watchdog(void); extern struct clocksource* clocksource_get_next(void); extern void clocksource_change_rating(struct clocksource *cs, int rating); diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index 791d1aeb17ac..31b90332f47b 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c @@ -389,28 +389,17 @@ static void clocksource_enqueue_watchdog(struct clocksource *cs) static void clocksource_dequeue_watchdog(struct clocksource *cs) { - struct clocksource *tmp; unsigned long flags; spin_lock_irqsave(&watchdog_lock, flags); - if (cs->flags & CLOCK_SOURCE_MUST_VERIFY) { - /* cs is a watched clocksource. */ - list_del_init(&cs->wd_list); - } else if (cs == watchdog) { - /* Reset watchdog cycles */ - clocksource_reset_watchdog(); - /* Current watchdog is removed. Find an alternative. */ - watchdog = NULL; - list_for_each_entry(tmp, &clocksource_list, list) { - if (tmp == cs || tmp->flags & CLOCK_SOURCE_MUST_VERIFY) - continue; - if (!watchdog || tmp->rating > watchdog->rating) - watchdog = tmp; + if (cs != watchdog) { + if (cs->flags & CLOCK_SOURCE_MUST_VERIFY) { + /* cs is a watched clocksource. */ + list_del_init(&cs->wd_list); + /* Check if the watchdog timer needs to be stopped. */ + clocksource_stop_watchdog(); } } - cs->flags &= ~CLOCK_SOURCE_WATCHDOG; - /* Check if the watchdog timer needs to be stopped. */ - clocksource_stop_watchdog(); spin_unlock_irqrestore(&watchdog_lock, flags); } @@ -841,13 +830,15 @@ static int clocksource_unbind(struct clocksource *cs) * clocksource_unregister - remove a registered clocksource * @cs: clocksource to be unregistered */ -void clocksource_unregister(struct clocksource *cs) +int clocksource_unregister(struct clocksource *cs) { + int ret = 0; + mutex_lock(&clocksource_mutex); - clocksource_dequeue_watchdog(cs); - list_del(&cs->list); - clocksource_select(); + if (!list_empty(&cs->list)) + ret = clocksource_unbind(cs); mutex_unlock(&clocksource_mutex); + return ret; } EXPORT_SYMBOL(clocksource_unregister); -- cgit v1.2.3 From 7172a286ced0c1f4f239a0fa09db54ed37d3ead2 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 25 Apr 2013 20:31:47 +0000 Subject: clockevents: Get rid of the notifier chain 7+ years and still a single user. Kill it. Signed-off-by: Thomas Gleixner Cc: John Stultz Cc: Magnus Damm Link: http://lkml.kernel.org/r/20130425143436.098520211@linutronix.de Signed-off-by: Thomas Gleixner --- include/linux/clockchips.h | 1 - kernel/time/clockevents.c | 35 +++-------------------------------- kernel/time/tick-broadcast.c | 5 ++--- kernel/time/tick-common.c | 30 +++++------------------------- kernel/time/tick-internal.h | 7 ++++--- 5 files changed, 14 insertions(+), 64 deletions(-) diff --git a/include/linux/clockchips.h b/include/linux/clockchips.h index 963d71431388..2f498f66c1bb 100644 --- a/include/linux/clockchips.h +++ b/include/linux/clockchips.h @@ -150,7 +150,6 @@ extern void clockevents_exchange_device(struct clock_event_device *old, struct clock_event_device *new); extern void clockevents_set_mode(struct clock_event_device *dev, enum clock_event_mode mode); -extern int clockevents_register_notifier(struct notifier_block *nb); extern int clockevents_program_event(struct clock_event_device *dev, ktime_t expires, bool force); diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c index c6d6400ee137..dd70b4842c62 100644 --- a/kernel/time/clockevents.c +++ b/kernel/time/clockevents.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include "tick-internal.h" @@ -23,10 +22,6 @@ /* The registered clock event devices */ static LIST_HEAD(clockevent_devices); static LIST_HEAD(clockevents_released); - -/* Notification for clock events */ -static RAW_NOTIFIER_HEAD(clockevents_chain); - /* Protection for the above */ static DEFINE_RAW_SPINLOCK(clockevents_lock); @@ -232,30 +227,6 @@ int clockevents_program_event(struct clock_event_device *dev, ktime_t expires, return (rc && force) ? clockevents_program_min_delta(dev) : rc; } -/** - * clockevents_register_notifier - register a clock events change listener - */ -int clockevents_register_notifier(struct notifier_block *nb) -{ - unsigned long flags; - int ret; - - raw_spin_lock_irqsave(&clockevents_lock, flags); - ret = raw_notifier_chain_register(&clockevents_chain, nb); - raw_spin_unlock_irqrestore(&clockevents_lock, flags); - - return ret; -} - -/* - * Notify about a clock event change. Called with clockevents_lock - * held. - */ -static void clockevents_do_notify(unsigned long reason, void *dev) -{ - raw_notifier_call_chain(&clockevents_chain, reason, dev); -} - /* * Called after a notify add to make devices available which were * released from the notifier call. @@ -269,7 +240,7 @@ static void clockevents_notify_released(void) struct clock_event_device, list); list_del(&dev->list); list_add(&dev->list, &clockevent_devices); - clockevents_do_notify(CLOCK_EVT_NOTIFY_ADD, dev); + tick_check_new_device(dev); } } @@ -290,7 +261,7 @@ void clockevents_register_device(struct clock_event_device *dev) raw_spin_lock_irqsave(&clockevents_lock, flags); list_add(&dev->list, &clockevent_devices); - clockevents_do_notify(CLOCK_EVT_NOTIFY_ADD, dev); + tick_check_new_device(dev); clockevents_notify_released(); raw_spin_unlock_irqrestore(&clockevents_lock, flags); @@ -433,7 +404,7 @@ void clockevents_notify(unsigned long reason, void *arg) int cpu; raw_spin_lock_irqsave(&clockevents_lock, flags); - clockevents_do_notify(reason, arg); + tick_notify(reason, arg); switch (reason) { case CLOCK_EVT_NOTIFY_CPU_DEAD: diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c index 24938d577669..3500caaa0bfd 100644 --- a/kernel/time/tick-broadcast.c +++ b/kernel/time/tick-broadcast.c @@ -64,7 +64,7 @@ static void tick_broadcast_start_periodic(struct clock_event_device *bc) /* * Check, if the device can be utilized as broadcast device: */ -int tick_check_broadcast_device(struct clock_event_device *dev) +void tick_install_broadcast_device(struct clock_event_device *dev) { struct clock_event_device *cur = tick_broadcast_device.evtdev; @@ -72,7 +72,7 @@ int tick_check_broadcast_device(struct clock_event_device *dev) (tick_broadcast_device.evtdev && tick_broadcast_device.evtdev->rating >= dev->rating) || (dev->features & CLOCK_EVT_FEAT_C3STOP)) - return 0; + return; clockevents_exchange_device(tick_broadcast_device.evtdev, dev); if (cur) @@ -90,7 +90,6 @@ int tick_check_broadcast_device(struct clock_event_device *dev) */ if (dev->features & CLOCK_EVT_FEAT_ONESHOT) tick_clock_notify(); - return 1; } /* diff --git a/kernel/time/tick-common.c b/kernel/time/tick-common.c index 5d3fb100bc06..dbf4e18d5101 100644 --- a/kernel/time/tick-common.c +++ b/kernel/time/tick-common.c @@ -208,11 +208,11 @@ static void tick_setup_device(struct tick_device *td, /* * Check, if the new registered device should be used. */ -static int tick_check_new_device(struct clock_event_device *newdev) +void tick_check_new_device(struct clock_event_device *newdev) { struct clock_event_device *curdev; struct tick_device *td; - int cpu, ret = NOTIFY_OK; + int cpu; unsigned long flags; raw_spin_lock_irqsave(&tick_device_lock, flags); @@ -275,18 +275,14 @@ static int tick_check_new_device(struct clock_event_device *newdev) tick_oneshot_notify(); raw_spin_unlock_irqrestore(&tick_device_lock, flags); - return NOTIFY_STOP; + return; out_bc: /* * Can the new device be used as a broadcast device ? */ - if (tick_check_broadcast_device(newdev)) - ret = NOTIFY_STOP; - + tick_install_broadcast_device(newdev); raw_spin_unlock_irqrestore(&tick_device_lock, flags); - - return ret; } /* @@ -360,17 +356,10 @@ static void tick_resume(void) raw_spin_unlock_irqrestore(&tick_device_lock, flags); } -/* - * Notification about clock event devices - */ -static int tick_notify(struct notifier_block *nb, unsigned long reason, - void *dev) +void tick_notify(unsigned long reason, void *dev) { switch (reason) { - case CLOCK_EVT_NOTIFY_ADD: - return tick_check_new_device(dev); - case CLOCK_EVT_NOTIFY_BROADCAST_ON: case CLOCK_EVT_NOTIFY_BROADCAST_OFF: case CLOCK_EVT_NOTIFY_BROADCAST_FORCE: @@ -404,21 +393,12 @@ static int tick_notify(struct notifier_block *nb, unsigned long reason, default: break; } - - return NOTIFY_OK; } -static struct notifier_block tick_notifier = { - .notifier_call = tick_notify, -}; - /** * tick_init - initialize the tick control - * - * Register the notifier with the clockevents framework */ void __init tick_init(void) { - clockevents_register_notifier(&tick_notifier); tick_broadcast_init(); } diff --git a/kernel/time/tick-internal.h b/kernel/time/tick-internal.h index f0299eae4602..60742fe6f63d 100644 --- a/kernel/time/tick-internal.h +++ b/kernel/time/tick-internal.h @@ -18,6 +18,8 @@ extern int tick_do_timer_cpu __read_mostly; extern void tick_setup_periodic(struct clock_event_device *dev, int broadcast); extern void tick_handle_periodic(struct clock_event_device *dev); +extern void tick_notify(unsigned long reason, void *dev); +extern void tick_check_new_device(struct clock_event_device *dev); extern void clockevents_shutdown(struct clock_event_device *dev); @@ -90,7 +92,7 @@ static inline bool tick_broadcast_oneshot_available(void) { return false; } */ #ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST extern int tick_device_uses_broadcast(struct clock_event_device *dev, int cpu); -extern int tick_check_broadcast_device(struct clock_event_device *dev); +extern void tick_install_broadcast_device(struct clock_event_device *dev); extern int tick_is_broadcast_device(struct clock_event_device *dev); extern void tick_broadcast_on_off(unsigned long reason, int *oncpu); extern void tick_shutdown_broadcast(unsigned int *cpup); @@ -102,9 +104,8 @@ tick_set_periodic_handler(struct clock_event_device *dev, int broadcast); #else /* !BROADCAST */ -static inline int tick_check_broadcast_device(struct clock_event_device *dev) +static inline void tick_install_broadcast_device(struct clock_event_device *dev) { - return 0; } static inline int tick_is_broadcast_device(struct clock_event_device *dev) -- cgit v1.2.3 From 7126cac426137633e470167524e7bcb590fd49b3 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 25 Apr 2013 20:31:48 +0000 Subject: clockevents: Simplify locking Now that the notifier chain is gone there are no other users and it's pointless to nest tick_device_lock inside of clockevents_lock because there is no other use case. Signed-off-by: Thomas Gleixner Cc: John Stultz Cc: Magnus Damm Link: http://lkml.kernel.org/r/20130425143436.162888472@linutronix.de Signed-off-by: Thomas Gleixner --- kernel/time/tick-common.c | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/kernel/time/tick-common.c b/kernel/time/tick-common.c index dbf4e18d5101..170a4bdfa99e 100644 --- a/kernel/time/tick-common.c +++ b/kernel/time/tick-common.c @@ -33,7 +33,6 @@ DEFINE_PER_CPU(struct tick_device, tick_cpu_device); ktime_t tick_next_period; ktime_t tick_period; int tick_do_timer_cpu __read_mostly = TICK_DO_TIMER_BOOT; -static DEFINE_RAW_SPINLOCK(tick_device_lock); /* * Debugging: see timer_list.c @@ -206,16 +205,14 @@ static void tick_setup_device(struct tick_device *td, } /* - * Check, if the new registered device should be used. + * Check, if the new registered device should be used. Called with + * clockevents_lock held and interrupts disabled. */ void tick_check_new_device(struct clock_event_device *newdev) { struct clock_event_device *curdev; struct tick_device *td; int cpu; - unsigned long flags; - - raw_spin_lock_irqsave(&tick_device_lock, flags); cpu = smp_processor_id(); if (!cpumask_test_cpu(cpu, newdev->cpumask)) @@ -273,8 +270,6 @@ void tick_check_new_device(struct clock_event_device *newdev) tick_setup_device(td, newdev, cpu, cpumask_of(cpu)); if (newdev->features & CLOCK_EVT_FEAT_ONESHOT) tick_oneshot_notify(); - - raw_spin_unlock_irqrestore(&tick_device_lock, flags); return; out_bc: @@ -282,7 +277,6 @@ out_bc: * Can the new device be used as a broadcast device ? */ tick_install_broadcast_device(newdev); - raw_spin_unlock_irqrestore(&tick_device_lock, flags); } /* @@ -311,9 +305,7 @@ static void tick_shutdown(unsigned int *cpup) { struct tick_device *td = &per_cpu(tick_cpu_device, *cpup); struct clock_event_device *dev = td->evtdev; - unsigned long flags; - raw_spin_lock_irqsave(&tick_device_lock, flags); td->mode = TICKDEV_MODE_PERIODIC; if (dev) { /* @@ -325,26 +317,20 @@ static void tick_shutdown(unsigned int *cpup) dev->event_handler = clockevents_handle_noop; td->evtdev = NULL; } - raw_spin_unlock_irqrestore(&tick_device_lock, flags); } static void tick_suspend(void) { struct tick_device *td = &__get_cpu_var(tick_cpu_device); - unsigned long flags; - raw_spin_lock_irqsave(&tick_device_lock, flags); clockevents_shutdown(td->evtdev); - raw_spin_unlock_irqrestore(&tick_device_lock, flags); } static void tick_resume(void) { struct tick_device *td = &__get_cpu_var(tick_cpu_device); - unsigned long flags; int broadcast = tick_resume_broadcast(); - raw_spin_lock_irqsave(&tick_device_lock, flags); clockevents_set_mode(td->evtdev, CLOCK_EVT_MODE_RESUME); if (!broadcast) { @@ -353,9 +339,11 @@ static void tick_resume(void) else tick_resume_oneshot(); } - raw_spin_unlock_irqrestore(&tick_device_lock, flags); } +/* + * Called with clockevents_lock held and interrupts disabled + */ void tick_notify(unsigned long reason, void *dev) { switch (reason) { -- cgit v1.2.3 From 8c53daf63f56791ed47fc585206ef3049489612f Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 25 Apr 2013 20:31:48 +0000 Subject: clockevents: Move the tick_notify() switch case to clockevents_notify() No need to call another function and have duplicated cases. Signed-off-by: Thomas Gleixner Cc: John Stultz Cc: Magnus Damm Link: http://lkml.kernel.org/r/20130425143436.235746557@linutronix.de Signed-off-by: Thomas Gleixner --- kernel/time/clockevents.c | 28 ++++++++++++++++++++++++- kernel/time/tick-common.c | 50 ++++----------------------------------------- kernel/time/tick-internal.h | 5 ++++- 3 files changed, 35 insertions(+), 48 deletions(-) diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c index dd70b4842c62..0e3a8448e115 100644 --- a/kernel/time/clockevents.c +++ b/kernel/time/clockevents.c @@ -404,10 +404,36 @@ void clockevents_notify(unsigned long reason, void *arg) int cpu; raw_spin_lock_irqsave(&clockevents_lock, flags); - tick_notify(reason, arg); switch (reason) { + case CLOCK_EVT_NOTIFY_BROADCAST_ON: + case CLOCK_EVT_NOTIFY_BROADCAST_OFF: + case CLOCK_EVT_NOTIFY_BROADCAST_FORCE: + tick_broadcast_on_off(reason, arg); + break; + + case CLOCK_EVT_NOTIFY_BROADCAST_ENTER: + case CLOCK_EVT_NOTIFY_BROADCAST_EXIT: + tick_broadcast_oneshot_control(reason); + break; + + case CLOCK_EVT_NOTIFY_CPU_DYING: + tick_handover_do_timer(arg); + break; + + case CLOCK_EVT_NOTIFY_SUSPEND: + tick_suspend(); + tick_suspend_broadcast(); + break; + + case CLOCK_EVT_NOTIFY_RESUME: + tick_resume(); + break; + case CLOCK_EVT_NOTIFY_CPU_DEAD: + tick_shutdown_broadcast_oneshot(arg); + tick_shutdown_broadcast(arg); + tick_shutdown(arg); /* * Unregister the clock event devices which were * released from the users in the notify chain. diff --git a/kernel/time/tick-common.c b/kernel/time/tick-common.c index 170a4bdfa99e..84c7cfca4d7d 100644 --- a/kernel/time/tick-common.c +++ b/kernel/time/tick-common.c @@ -284,7 +284,7 @@ out_bc: * * Called with interrupts disabled. */ -static void tick_handover_do_timer(int *cpup) +void tick_handover_do_timer(int *cpup) { if (*cpup == tick_do_timer_cpu) { int cpu = cpumask_first(cpu_online_mask); @@ -301,7 +301,7 @@ static void tick_handover_do_timer(int *cpup) * access the hardware device itself. * We just set the mode and remove it from the lists. */ -static void tick_shutdown(unsigned int *cpup) +void tick_shutdown(unsigned int *cpup) { struct tick_device *td = &per_cpu(tick_cpu_device, *cpup); struct clock_event_device *dev = td->evtdev; @@ -319,14 +319,14 @@ static void tick_shutdown(unsigned int *cpup) } } -static void tick_suspend(void) +void tick_suspend(void) { struct tick_device *td = &__get_cpu_var(tick_cpu_device); clockevents_shutdown(td->evtdev); } -static void tick_resume(void) +void tick_resume(void) { struct tick_device *td = &__get_cpu_var(tick_cpu_device); int broadcast = tick_resume_broadcast(); @@ -341,48 +341,6 @@ static void tick_resume(void) } } -/* - * Called with clockevents_lock held and interrupts disabled - */ -void tick_notify(unsigned long reason, void *dev) -{ - switch (reason) { - - case CLOCK_EVT_NOTIFY_BROADCAST_ON: - case CLOCK_EVT_NOTIFY_BROADCAST_OFF: - case CLOCK_EVT_NOTIFY_BROADCAST_FORCE: - tick_broadcast_on_off(reason, dev); - break; - - case CLOCK_EVT_NOTIFY_BROADCAST_ENTER: - case CLOCK_EVT_NOTIFY_BROADCAST_EXIT: - tick_broadcast_oneshot_control(reason); - break; - - case CLOCK_EVT_NOTIFY_CPU_DYING: - tick_handover_do_timer(dev); - break; - - case CLOCK_EVT_NOTIFY_CPU_DEAD: - tick_shutdown_broadcast_oneshot(dev); - tick_shutdown_broadcast(dev); - tick_shutdown(dev); - break; - - case CLOCK_EVT_NOTIFY_SUSPEND: - tick_suspend(); - tick_suspend_broadcast(); - break; - - case CLOCK_EVT_NOTIFY_RESUME: - tick_resume(); - break; - - default: - break; - } -} - /** * tick_init - initialize the tick control */ diff --git a/kernel/time/tick-internal.h b/kernel/time/tick-internal.h index 60742fe6f63d..06bfc8802dfb 100644 --- a/kernel/time/tick-internal.h +++ b/kernel/time/tick-internal.h @@ -18,8 +18,11 @@ extern int tick_do_timer_cpu __read_mostly; extern void tick_setup_periodic(struct clock_event_device *dev, int broadcast); extern void tick_handle_periodic(struct clock_event_device *dev); -extern void tick_notify(unsigned long reason, void *dev); extern void tick_check_new_device(struct clock_event_device *dev); +extern void tick_handover_do_timer(int *cpup); +extern void tick_shutdown(unsigned int *cpup); +extern void tick_suspend(void); +extern void tick_resume(void); extern void clockevents_shutdown(struct clock_event_device *dev); -- cgit v1.2.3 From ccf33d6880f39a35158fff66db13000ae4943fac Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 25 Apr 2013 20:31:49 +0000 Subject: clockevents: Add module refcount We want to be able to remove clockevent modules as well. Add a refcount so we don't remove a module with an active clock event device. Signed-off-by: Thomas Gleixner Cc: John Stultz Cc: Magnus Damm Link: http://lkml.kernel.org/r/20130425143436.307435149@linutronix.de Signed-off-by: Thomas Gleixner --- include/linux/clockchips.h | 3 +++ kernel/time/clockevents.c | 1 + kernel/time/tick-broadcast.c | 3 +++ kernel/time/tick-common.c | 4 ++++ 4 files changed, 11 insertions(+) diff --git a/include/linux/clockchips.h b/include/linux/clockchips.h index 2f498f66c1bb..ae1193bcf074 100644 --- a/include/linux/clockchips.h +++ b/include/linux/clockchips.h @@ -30,6 +30,7 @@ enum clock_event_nofitiers { #include struct clock_event_device; +struct module; /* Clock event mode commands */ enum clock_event_mode { @@ -83,6 +84,7 @@ enum clock_event_mode { * @irq: IRQ number (only for non CPU local devices) * @cpumask: cpumask to indicate for which CPUs this device works * @list: list head for the management code + * @owner: module reference */ struct clock_event_device { void (*event_handler)(struct clock_event_device *); @@ -112,6 +114,7 @@ struct clock_event_device { int irq; const struct cpumask *cpumask; struct list_head list; + struct module *owner; } ____cacheline_aligned; /* diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c index 0e3a8448e115..89e394caa769 100644 --- a/kernel/time/clockevents.c +++ b/kernel/time/clockevents.c @@ -357,6 +357,7 @@ void clockevents_exchange_device(struct clock_event_device *old, * released list and do a notify add later. */ if (old) { + module_put(old->owner); clockevents_set_mode(old, CLOCK_EVT_MODE_UNUSED); list_del(&old->list); list_add(&old->list, &clockevents_released); diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c index 3500caaa0bfd..0e374cd2e0ef 100644 --- a/kernel/time/tick-broadcast.c +++ b/kernel/time/tick-broadcast.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "tick-internal.h" @@ -73,6 +74,8 @@ void tick_install_broadcast_device(struct clock_event_device *dev) tick_broadcast_device.evtdev->rating >= dev->rating) || (dev->features & CLOCK_EVT_FEAT_C3STOP)) return; + if (!try_module_get(dev->owner)) + return; clockevents_exchange_device(tick_broadcast_device.evtdev, dev); if (cur) diff --git a/kernel/time/tick-common.c b/kernel/time/tick-common.c index 84c7cfca4d7d..433a1e11d13b 100644 --- a/kernel/time/tick-common.c +++ b/kernel/time/tick-common.c @@ -18,6 +18,7 @@ #include #include #include +#include #include @@ -257,6 +258,9 @@ void tick_check_new_device(struct clock_event_device *newdev) goto out_bc; } + if (!try_module_get(newdev->owner)) + return; + /* * Replace the eventually existing device by the new * device. If the current device is the broadcast device, do -- cgit v1.2.3 From 501f867064e95f9a6f540e60705be0937280e7ec Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 25 Apr 2013 20:31:49 +0000 Subject: clockevents: Provide sysfs interface Provide a simple sysfs interface for the clockevent devices. Show the current active clockevent device. Signed-off-by: Thomas Gleixner Cc: John Stultz Cc: Magnus Damm Link: http://lkml.kernel.org/r/20130425143436.371634778@linutronix.de Signed-off-by: Thomas Gleixner --- kernel/time/clockevents.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c index 89e394caa769..0a23f4f29934 100644 --- a/kernel/time/clockevents.c +++ b/kernel/time/clockevents.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "tick-internal.h" @@ -460,4 +461,89 @@ void clockevents_notify(unsigned long reason, void *arg) raw_spin_unlock_irqrestore(&clockevents_lock, flags); } EXPORT_SYMBOL_GPL(clockevents_notify); + +#ifdef CONFIG_SYSFS +struct bus_type clockevents_subsys = { + .name = "clockevents", + .dev_name = "clockevent", +}; + +static DEFINE_PER_CPU(struct device, tick_percpu_dev); +static struct tick_device *tick_get_tick_dev(struct device *dev); + +static ssize_t sysfs_show_current_tick_dev(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct tick_device *td; + ssize_t count = 0; + + raw_spin_lock_irq(&clockevents_lock); + td = tick_get_tick_dev(dev); + if (td && td->evtdev) + count = snprintf(buf, PAGE_SIZE, "%s\n", td->evtdev->name); + raw_spin_unlock_irq(&clockevents_lock); + return count; +} +static DEVICE_ATTR(current_device, 0444, sysfs_show_current_tick_dev, NULL); + +#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST +static struct device tick_bc_dev = { + .init_name = "broadcast", + .id = 0, + .bus = &clockevents_subsys, +}; + +static struct tick_device *tick_get_tick_dev(struct device *dev) +{ + return dev == &tick_bc_dev ? tick_get_broadcast_device() : + &per_cpu(tick_cpu_device, dev->id); +} + +static __init int tick_broadcast_init_sysfs(void) +{ + int err = device_register(&tick_bc_dev); + + if (!err) + err = device_create_file(&tick_bc_dev, &dev_attr_current_device); + return err; +} +#else +static struct tick_device *tick_get_tick_dev(struct device *dev) +{ + return &per_cpu(tick_cpu_device, dev->id); +} +static inline int tick_broadcast_init_sysfs(void) { return 0; } #endif + +static int __init tick_init_sysfs(void) +{ + int cpu; + + for_each_possible_cpu(cpu) { + struct device *dev = &per_cpu(tick_percpu_dev, cpu); + int err; + + dev->id = cpu; + dev->bus = &clockevents_subsys; + err = device_register(dev); + if (!err) + err = device_create_file(dev, &dev_attr_current_device); + if (err) + return err; + } + return tick_broadcast_init_sysfs(); +} + +static int __init clockevents_init_sysfs(void) +{ + int err = subsys_system_register(&clockevents_subsys, NULL); + + if (!err) + err = tick_init_sysfs(); + return err; +} +device_initcall(clockevents_init_sysfs); +#endif /* SYSFS */ + +#endif /* GENERIC_CLOCK_EVENTS */ -- cgit v1.2.3 From 45cb8e01b2ecef1c2afb18333e95793fa1a90281 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 25 Apr 2013 20:31:50 +0000 Subject: clockevents: Split out selection logic Split out the clockevent device selection logic. Preparatory patch to allow unbinding active clockevent devices. Signed-off-by: Thomas Gleixner Cc: John Stultz Cc: Magnus Damm Link: http://lkml.kernel.org/r/20130425143436.431796247@linutronix.de Signed-off-by: Thomas Gleixner --- kernel/time/tick-broadcast.c | 25 ++++++++++++---- kernel/time/tick-common.c | 69 +++++++++++++++++++++++--------------------- 2 files changed, 56 insertions(+), 38 deletions(-) diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c index 0e374cd2e0ef..d067c01586f5 100644 --- a/kernel/time/tick-broadcast.c +++ b/kernel/time/tick-broadcast.c @@ -65,19 +65,34 @@ static void tick_broadcast_start_periodic(struct clock_event_device *bc) /* * Check, if the device can be utilized as broadcast device: */ +static bool tick_check_broadcast_device(struct clock_event_device *curdev, + struct clock_event_device *newdev) +{ + if ((newdev->features & CLOCK_EVT_FEAT_DUMMY) || + (newdev->features & CLOCK_EVT_FEAT_C3STOP)) + return false; + + if (tick_broadcast_device.mode == TICKDEV_MODE_ONESHOT && + !(newdev->features & CLOCK_EVT_FEAT_ONESHOT)) + return false; + + return !curdev || newdev->rating > curdev->rating; +} + +/* + * Conditionally install/replace broadcast device + */ void tick_install_broadcast_device(struct clock_event_device *dev) { struct clock_event_device *cur = tick_broadcast_device.evtdev; - if ((dev->features & CLOCK_EVT_FEAT_DUMMY) || - (tick_broadcast_device.evtdev && - tick_broadcast_device.evtdev->rating >= dev->rating) || - (dev->features & CLOCK_EVT_FEAT_C3STOP)) + if (!tick_check_broadcast_device(cur, dev)) return; + if (!try_module_get(dev->owner)) return; - clockevents_exchange_device(tick_broadcast_device.evtdev, dev); + clockevents_exchange_device(cur, dev); if (cur) cur->event_handler = clockevents_handle_noop; tick_broadcast_device.evtdev = dev; diff --git a/kernel/time/tick-common.c b/kernel/time/tick-common.c index 433a1e11d13b..c34021650348 100644 --- a/kernel/time/tick-common.c +++ b/kernel/time/tick-common.c @@ -205,6 +205,37 @@ static void tick_setup_device(struct tick_device *td, tick_setup_oneshot(newdev, handler, next_event); } +static bool tick_check_percpu(struct clock_event_device *curdev, + struct clock_event_device *newdev, int cpu) +{ + if (!cpumask_test_cpu(cpu, newdev->cpumask)) + return false; + if (cpumask_equal(newdev->cpumask, cpumask_of(cpu))) + return true; + /* Check if irq affinity can be set */ + if (newdev->irq >= 0 && !irq_can_set_affinity(newdev->irq)) + return false; + /* Prefer an existing cpu local device */ + if (curdev && cpumask_equal(curdev->cpumask, cpumask_of(cpu))) + return false; + return true; +} + +static bool tick_check_preferred(struct clock_event_device *curdev, + struct clock_event_device *newdev) +{ + /* Prefer oneshot capable device */ + if (!(newdev->features & CLOCK_EVT_FEAT_ONESHOT)) { + if (curdev && (curdev->features & CLOCK_EVT_FEAT_ONESHOT)) + return false; + if (tick_oneshot_mode_active()) + return false; + } + + /* Use the higher rated one */ + return !curdev || newdev->rating > curdev->rating; +} + /* * Check, if the new registered device should be used. Called with * clockevents_lock held and interrupts disabled. @@ -223,40 +254,12 @@ void tick_check_new_device(struct clock_event_device *newdev) curdev = td->evtdev; /* cpu local device ? */ - if (!cpumask_equal(newdev->cpumask, cpumask_of(cpu))) { - - /* - * If the cpu affinity of the device interrupt can not - * be set, ignore it. - */ - if (!irq_can_set_affinity(newdev->irq)) - goto out_bc; - - /* - * If we have a cpu local device already, do not replace it - * by a non cpu local device - */ - if (curdev && cpumask_equal(curdev->cpumask, cpumask_of(cpu))) - goto out_bc; - } + if (!tick_check_percpu(curdev, newdev, cpu)) + goto out_bc; - /* - * If we have an active device, then check the rating and the oneshot - * feature. - */ - if (curdev) { - /* - * Prefer one shot capable devices ! - */ - if ((curdev->features & CLOCK_EVT_FEAT_ONESHOT) && - !(newdev->features & CLOCK_EVT_FEAT_ONESHOT)) - goto out_bc; - /* - * Check the rating - */ - if (curdev->rating >= newdev->rating) - goto out_bc; - } + /* Preference decision */ + if (!tick_check_preferred(curdev, newdev)) + goto out_bc; if (!try_module_get(newdev->owner)) return; -- cgit v1.2.3 From 03e13cf5ee60584fe0c831682c67212effb7fca4 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 25 Apr 2013 20:31:50 +0000 Subject: clockevents: Implement unbind functionality Provide a sysfs interface to allow unbinding of clockevent devices. The device is unbound if it is unused or if there is a replacement device available. Unbinding of broadcast devices is not supported as we don't want to foster that nonsense. If no replacement device is available the unbind returns -EBUSY. Unbind is available from the kernel and through sysfs, which is necessary to drop the module refcount. Signed-off-by: Thomas Gleixner Cc: John Stultz Cc: Magnus Damm Link: http://lkml.kernel.org/r/20130425143436.499216659@linutronix.de Signed-off-by: Thomas Gleixner --- include/linux/clockchips.h | 1 + kernel/time/clockevents.c | 125 ++++++++++++++++++++++++++++++++++++++++++++ kernel/time/clocksource.c | 9 ++-- kernel/time/tick-common.c | 24 +++++++++ kernel/time/tick-internal.h | 7 +++ 5 files changed, 162 insertions(+), 4 deletions(-) diff --git a/include/linux/clockchips.h b/include/linux/clockchips.h index ae1193bcf074..0857922e8ad0 100644 --- a/include/linux/clockchips.h +++ b/include/linux/clockchips.h @@ -141,6 +141,7 @@ static inline unsigned long div_sc(unsigned long ticks, unsigned long nsec, extern u64 clockevent_delta2ns(unsigned long latch, struct clock_event_device *evt); extern void clockevents_register_device(struct clock_event_device *dev); +extern int clockevents_unbind_device(struct clock_event_device *ced, int cpu); extern void clockevents_config(struct clock_event_device *dev, u32 freq); extern void clockevents_config_and_register(struct clock_event_device *dev, diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c index 0a23f4f29934..38959c866789 100644 --- a/kernel/time/clockevents.c +++ b/kernel/time/clockevents.c @@ -25,6 +25,13 @@ static LIST_HEAD(clockevent_devices); static LIST_HEAD(clockevents_released); /* Protection for the above */ static DEFINE_RAW_SPINLOCK(clockevents_lock); +/* Protection for unbind operations */ +static DEFINE_MUTEX(clockevents_mutex); + +struct ce_unbind { + struct clock_event_device *ce; + int res; +}; /** * clockevents_delta2ns - Convert a latch value (device ticks) to nanoseconds @@ -245,6 +252,90 @@ static void clockevents_notify_released(void) } } +/* + * Try to install a replacement clock event device + */ +static int clockevents_replace(struct clock_event_device *ced) +{ + struct clock_event_device *dev, *newdev = NULL; + + list_for_each_entry(dev, &clockevent_devices, list) { + if (dev == ced || dev->mode != CLOCK_EVT_MODE_UNUSED) + continue; + + if (!tick_check_replacement(newdev, dev)) + continue; + + if (!try_module_get(dev->owner)) + continue; + + if (newdev) + module_put(newdev->owner); + newdev = dev; + } + if (newdev) { + tick_install_replacement(newdev); + list_del_init(&ced->list); + } + return newdev ? 0 : -EBUSY; +} + +/* + * Called with clockevents_mutex and clockevents_lock held + */ +static int __clockevents_try_unbind(struct clock_event_device *ced, int cpu) +{ + /* Fast track. Device is unused */ + if (ced->mode == CLOCK_EVT_MODE_UNUSED) { + list_del_init(&ced->list); + return 0; + } + + return ced == per_cpu(tick_cpu_device, cpu).evtdev ? -EAGAIN : -EBUSY; +} + +/* + * SMP function call to unbind a device + */ +static void __clockevents_unbind(void *arg) +{ + struct ce_unbind *cu = arg; + int res; + + raw_spin_lock(&clockevents_lock); + res = __clockevents_try_unbind(cu->ce, smp_processor_id()); + if (res == -EAGAIN) + res = clockevents_replace(cu->ce); + cu->res = res; + raw_spin_unlock(&clockevents_lock); +} + +/* + * Issues smp function call to unbind a per cpu device. Called with + * clockevents_mutex held. + */ +static int clockevents_unbind(struct clock_event_device *ced, int cpu) +{ + struct ce_unbind cu = { .ce = ced, .res = -ENODEV }; + + smp_call_function_single(cpu, __clockevents_unbind, &cu, 1); + return cu.res; +} + +/* + * Unbind a clockevents device. + */ +int clockevents_unbind_device(struct clock_event_device *ced, int cpu) +{ + int ret; + + mutex_lock(&clockevents_mutex); + ret = clockevents_unbind(ced, cpu); + mutex_unlock(&clockevents_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(clockevents_unbind); + /** * clockevents_register_device - register a clock event device * @dev: device to register @@ -487,6 +578,38 @@ static ssize_t sysfs_show_current_tick_dev(struct device *dev, } static DEVICE_ATTR(current_device, 0444, sysfs_show_current_tick_dev, NULL); +/* We don't support the abomination of removable broadcast devices */ +static ssize_t sysfs_unbind_tick_dev(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + char name[CS_NAME_LEN]; + size_t ret = sysfs_get_uname(buf, name, count); + struct clock_event_device *ce; + + if (ret < 0) + return ret; + + ret = -ENODEV; + mutex_lock(&clockevents_mutex); + raw_spin_lock_irq(&clockevents_lock); + list_for_each_entry(ce, &clockevent_devices, list) { + if (!strcmp(ce->name, name)) { + ret = __clockevents_try_unbind(ce, dev->id); + break; + } + } + raw_spin_unlock_irq(&clockevents_lock); + /* + * We hold clockevents_mutex, so ce can't go away + */ + if (ret == -EAGAIN) + ret = clockevents_unbind(ce, dev->id); + mutex_unlock(&clockevents_mutex); + return ret ? ret : count; +} +static DEVICE_ATTR(unbind_device, 0200, NULL, sysfs_unbind_tick_dev); + #ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST static struct device tick_bc_dev = { .init_name = "broadcast", @@ -529,6 +652,8 @@ static int __init tick_init_sysfs(void) err = device_register(dev); if (!err) err = device_create_file(dev, &dev_attr_current_device); + if (!err) + err = device_create_file(dev, &dev_attr_unbind_device); if (err) return err; } diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index 31b90332f47b..6d05b00410cc 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c @@ -31,6 +31,8 @@ #include #include +#include "tick-internal.h" + void timecounter_init(struct timecounter *tc, const struct cyclecounter *cc, u64 start_tstamp) @@ -174,7 +176,6 @@ clocks_calc_mult_shift(u32 *mult, u32 *shift, u32 from, u32 to, u32 maxsec) static struct clocksource *curr_clocksource; static LIST_HEAD(clocksource_list); static DEFINE_MUTEX(clocksource_mutex); -#define CS_NAME_LEN 32 static char override_name[CS_NAME_LEN]; static int finished_booting; @@ -864,7 +865,7 @@ sysfs_show_current_clocksources(struct device *dev, return count; } -static size_t clocksource_get_uname(const char *buf, char *dst, size_t cnt) +size_t sysfs_get_uname(const char *buf, char *dst, size_t cnt) { size_t ret = cnt; @@ -899,7 +900,7 @@ static ssize_t sysfs_override_clocksource(struct device *dev, mutex_lock(&clocksource_mutex); - ret = clocksource_get_uname(buf, override_name, count); + ret = sysfs_get_uname(buf, override_name, count); if (ret >= 0) clocksource_select(); @@ -925,7 +926,7 @@ static ssize_t sysfs_unbind_clocksource(struct device *dev, char name[CS_NAME_LEN]; size_t ret; - ret = clocksource_get_uname(buf, name, count); + ret = sysfs_get_uname(buf, name, count); if (ret < 0) return ret; diff --git a/kernel/time/tick-common.c b/kernel/time/tick-common.c index c34021650348..5edfb4806032 100644 --- a/kernel/time/tick-common.c +++ b/kernel/time/tick-common.c @@ -205,6 +205,17 @@ static void tick_setup_device(struct tick_device *td, tick_setup_oneshot(newdev, handler, next_event); } +void tick_install_replacement(struct clock_event_device *newdev) +{ + struct tick_device *td = &__get_cpu_var(tick_cpu_device); + int cpu = smp_processor_id(); + + clockevents_exchange_device(td->evtdev, newdev); + tick_setup_device(td, newdev, cpu, cpumask_of(cpu)); + if (newdev->features & CLOCK_EVT_FEAT_ONESHOT) + tick_oneshot_notify(); +} + static bool tick_check_percpu(struct clock_event_device *curdev, struct clock_event_device *newdev, int cpu) { @@ -236,6 +247,19 @@ static bool tick_check_preferred(struct clock_event_device *curdev, return !curdev || newdev->rating > curdev->rating; } +/* + * Check whether the new device is a better fit than curdev. curdev + * can be NULL ! + */ +bool tick_check_replacement(struct clock_event_device *curdev, + struct clock_event_device *newdev) +{ + if (tick_check_percpu(curdev, newdev, smp_processor_id())) + return false; + + return tick_check_preferred(curdev, newdev); +} + /* * Check, if the new registered device should be used. Called with * clockevents_lock held and interrupts disabled. diff --git a/kernel/time/tick-internal.h b/kernel/time/tick-internal.h index 06bfc8802dfb..be1690eaecff 100644 --- a/kernel/time/tick-internal.h +++ b/kernel/time/tick-internal.h @@ -11,6 +11,8 @@ extern seqlock_t jiffies_lock; #define TICK_DO_TIMER_NONE -1 #define TICK_DO_TIMER_BOOT -2 +#define CS_NAME_LEN 32 + DECLARE_PER_CPU(struct tick_device, tick_cpu_device); extern ktime_t tick_next_period; extern ktime_t tick_period; @@ -23,9 +25,14 @@ extern void tick_handover_do_timer(int *cpup); extern void tick_shutdown(unsigned int *cpup); extern void tick_suspend(void); extern void tick_resume(void); +extern bool tick_check_replacement(struct clock_event_device *curdev, + struct clock_event_device *newdev); +extern void tick_install_replacement(struct clock_event_device *dev); extern void clockevents_shutdown(struct clock_event_device *dev); +extern size_t sysfs_get_uname(const char *buf, char *dst, size_t cnt); + /* * NO_HZ / high resolution timer shared code */ -- cgit v1.2.3 From 6fde0f307cdc3cdf7a11a13c5335e11627f9ef24 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Fri, 3 May 2013 14:54:34 -0400 Subject: UBI: drop redundant "UBI error" string The ubi_err() macro automatically prefixes "UBI error" before the message. By also using it here, we get a log like so: UBI error: ubi_init: UBI error: cannot initialize UBI, error -19 Signed-off-by: Mike Frysinger Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/build.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index a56133585e92..745fbc574141 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -1309,7 +1309,7 @@ out_version: out_class: class_destroy(ubi_class); out: - ubi_err("UBI error: cannot initialize UBI, error %d", err); + ubi_err("cannot initialize UBI, error %d", err); return err; } late_initcall(ubi_init); @@ -1346,7 +1346,7 @@ static int __init bytes_str_to_int(const char *str) result = simple_strtoul(str, &endp, 0); if (str == endp || result >= INT_MAX) { - ubi_err("UBI error: incorrect bytes count: \"%s\"\n", str); + ubi_err("incorrect bytes count: \"%s\"\n", str); return -EINVAL; } @@ -1362,7 +1362,7 @@ static int __init bytes_str_to_int(const char *str) case '\0': break; default: - ubi_err("UBI error: incorrect bytes count: \"%s\"\n", str); + ubi_err("incorrect bytes count: \"%s\"\n", str); return -EINVAL; } @@ -1389,14 +1389,14 @@ static int __init ubi_mtd_param_parse(const char *val, struct kernel_param *kp) return -EINVAL; if (mtd_devs == UBI_MAX_DEVICES) { - ubi_err("UBI error: too many parameters, max. is %d\n", + ubi_err("too many parameters, max. is %d\n", UBI_MAX_DEVICES); return -EINVAL; } len = strnlen(val, MTD_PARAM_LEN_MAX); if (len == MTD_PARAM_LEN_MAX) { - ubi_err("UBI error: parameter \"%s\" is too long, max. is %d\n", + ubi_err("parameter \"%s\" is too long, max. is %d\n", val, MTD_PARAM_LEN_MAX); return -EINVAL; } @@ -1416,7 +1416,7 @@ static int __init ubi_mtd_param_parse(const char *val, struct kernel_param *kp) tokens[i] = strsep(&pbuf, ","); if (pbuf) { - ubi_err("UBI error: too many arguments at \"%s\"\n", val); + ubi_err("too many arguments at \"%s\"\n", val); return -EINVAL; } @@ -1433,7 +1433,7 @@ static int __init ubi_mtd_param_parse(const char *val, struct kernel_param *kp) int err = kstrtoint(tokens[2], 10, &p->max_beb_per1024); if (err) { - ubi_err("UBI error: bad value for max_beb_per1024 parameter: %s", + ubi_err("bad value for max_beb_per1024 parameter: %s", tokens[2]); return -EINVAL; } -- cgit v1.2.3 From 1557b9e1cb669f90696c863fbf525a1033022c10 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Mon, 22 Apr 2013 21:40:16 -0400 Subject: UBI: do not abort init when ubi.mtd devices cannot be found The current ubi.mtd parsing logic will warn & continue on when attaching the specified mtd device fails (for any reason). It doesn't however skip things when the specified mtd device can't be opened. This scenario can be hit in a couple of different ways such as: - build NAND controller driver as a module - build UBI into the kernel - include ubi.mtd on the kernel command line - boot the system - MTD devices don't exist, so UBI init fails This is problematic because failing init means the entire UBI layer is unavailable until you reboot and modify the kernel command line. If we just warn and continue on, /dev/ubi_ctrl is available for userland to add UBI volumes on the fly once it loads the NAND driver. Signed-off-by: Mike Frysinger Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/build.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 745fbc574141..8ff08ecb9383 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -1261,7 +1261,11 @@ static int __init ubi_init(void) mtd = open_mtd_device(p->name); if (IS_ERR(mtd)) { err = PTR_ERR(mtd); - goto out_detach; + ubi_err("cannot open mtd %s, error %d", p->name, err); + /* See comment below re-ubi_is_module(). */ + if (ubi_is_module()) + goto out_detach; + continue; } mutex_lock(&ubi_devices_mutex); -- cgit v1.2.3 From bc441bc8e36e0bcdbf21639fb78e94ffa19ea226 Mon Sep 17 00:00:00 2001 From: Laurent Navet Date: Mon, 13 May 2013 17:27:51 +0200 Subject: drivers: net: can: grcan: use devm_ioremap_resource() Replace a call to deprecated devm_request_and_ioremap by devm_ioremap_resource. dev_err() message is no more needed since it's already displayed in devm_ioremap_resource(). Signed-off-by: Laurent Navet Acked-by: Andreas Larsson Signed-off-by: Marc Kleine-Budde --- drivers/net/can/grcan.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/net/can/grcan.c b/drivers/net/can/grcan.c index 17fbc7a09224..9e9d0d628e7b 100644 --- a/drivers/net/can/grcan.c +++ b/drivers/net/can/grcan.c @@ -1683,10 +1683,9 @@ static int grcan_probe(struct platform_device *ofdev) } res = platform_get_resource(ofdev, IORESOURCE_MEM, 0); - base = devm_request_and_ioremap(&ofdev->dev, res); - if (!base) { - dev_err(&ofdev->dev, "couldn't map IO resource\n"); - err = -EADDRNOTAVAIL; + base = devm_ioremap_resource(&ofdev->dev, res); + if (IS_ERR(base)) { + err = PTR_ERR(base); goto exit_error; } -- cgit v1.2.3 From 3e1dd6da1a7af98a795fafa9437679767c605a7b Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 7 May 2013 13:11:29 +0900 Subject: net: can: at91_can: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Marc Kleine-Budde --- drivers/net/can/at91_can.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/can/at91_can.c b/drivers/net/can/at91_can.c index db52f4414def..556656661d6b 100644 --- a/drivers/net/can/at91_can.c +++ b/drivers/net/can/at91_can.c @@ -1393,8 +1393,6 @@ static int at91_can_remove(struct platform_device *pdev) unregister_netdev(dev); - platform_set_drvdata(pdev, NULL); - iounmap(priv->reg_base); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -- cgit v1.2.3 From 5e946e56231b5c765598cd3103faf3c7a0121812 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 7 May 2013 13:14:13 +0900 Subject: net: can: c_can: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Marc Kleine-Budde --- drivers/net/can/c_can/c_can_platform.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/can/c_can/c_can_platform.c b/drivers/net/can/c_can/c_can_platform.c index d63b91904f82..6b6130b8bdc4 100644 --- a/drivers/net/can/c_can/c_can_platform.c +++ b/drivers/net/can/c_can/c_can_platform.c @@ -234,7 +234,6 @@ static int c_can_plat_probe(struct platform_device *pdev) return 0; exit_free_device: - platform_set_drvdata(pdev, NULL); free_c_can_dev(dev); exit_iounmap: iounmap(addr); @@ -255,7 +254,6 @@ static int c_can_plat_remove(struct platform_device *pdev) struct resource *mem; unregister_c_can_dev(dev); - platform_set_drvdata(pdev, NULL); free_c_can_dev(dev); iounmap(priv->base); -- cgit v1.2.3 From 688a2e74210cb42eccf59b8cff1433d53ea0b800 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 7 May 2013 13:15:49 +0900 Subject: net: can: flexcan: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Marc Kleine-Budde --- drivers/net/can/flexcan.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c index 769d29ed106d..4a40a186c8c8 100644 --- a/drivers/net/can/flexcan.c +++ b/drivers/net/can/flexcan.c @@ -1127,7 +1127,6 @@ static int flexcan_remove(struct platform_device *pdev) struct resource *mem; unregister_flexcandev(dev); - platform_set_drvdata(pdev, NULL); iounmap(priv->base); mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); -- cgit v1.2.3 From 5727dc6bf5b70dd278e58e26137e2df729b9c107 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 7 May 2013 13:17:38 +0900 Subject: net: can: ti_hecc: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Marc Kleine-Budde --- drivers/net/can/ti_hecc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c index f21fc37ec578..3a349a22d5bc 100644 --- a/drivers/net/can/ti_hecc.c +++ b/drivers/net/can/ti_hecc.c @@ -1001,7 +1001,6 @@ static int ti_hecc_remove(struct platform_device *pdev) iounmap(priv->base); release_mem_region(res->start, resource_size(res)); free_candev(ndev); - platform_set_drvdata(pdev, NULL); return 0; } -- cgit v1.2.3 From 0b7ddda69263c305375ab88d4272c4a548bf66c0 Mon Sep 17 00:00:00 2001 From: Steffen Trumtrar Date: Thu, 16 May 2013 11:49:41 +0200 Subject: HID: multitouch: add support for Data Modul easyMaxTouch Add vendor ID for Data Modul and their easyMaxTouch device. The device has to be configured to multitouch mode prior to using this driver. Signed-off-by: Steffen Trumtrar Signed-off-by: Jiri Kosina --- drivers/hid/hid-ids.h | 3 +++ drivers/hid/hid-multitouch.c | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 38535c9243d5..0b4598ac43d1 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -248,6 +248,9 @@ #define USB_DEVICE_ID_CYPRESS_BARCODE_4 0xed81 #define USB_DEVICE_ID_CYPRESS_TRUETOUCH 0xc001 +#define USB_VENDOR_ID_DATA_MODUL 0x7374 +#define USB_VENDOR_ID_DATA_MODUL_EASYMAXTOUCH 0x1201 + #define USB_VENDOR_ID_DEALEXTREAME 0x10c5 #define USB_DEVICE_ID_DEALEXTREAME_RADIO_SI4701 0x819a diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index dc3ae5c56f56..1bea65791e45 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -1108,6 +1108,11 @@ static const struct hid_device_id mt_devices[] = { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_TRUETOUCH) }, + /* Data Modul easyMaxTouch */ + { .driver_data = MT_CLS_DEFAULT, + MT_USB_DEVICE(USB_VENDOR_ID_DATA_MODUL, + USB_VENDOR_ID_DATA_MODUL_EASYMAXTOUCH) }, + /* eGalax devices (resistive) */ { .driver_data = MT_CLS_EGALAX, MT_USB_DEVICE(USB_VENDOR_ID_DWAV, -- cgit v1.2.3 From a64cbb949a18a8eefc40881e6e68734ca7275d36 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 16 May 2013 14:30:28 +0100 Subject: ASoC: wm5102: Correct OSR control name for EPOUT Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm5102.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index e895d3939eef..6e1ee130705a 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -814,7 +814,7 @@ SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_5L, SOC_VALUE_ENUM("HPOUT1 OSR", wm5102_hpout_osr[0]), SOC_VALUE_ENUM("HPOUT2 OSR", wm5102_hpout_osr[1]), -SOC_VALUE_ENUM("HPOUT3 OSR", wm5102_hpout_osr[2]), +SOC_VALUE_ENUM("EPOUT OSR", wm5102_hpout_osr[2]), SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp), SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp), -- cgit v1.2.3 From 27474d268c20a0e62f71065a043c08f5adb2419d Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 16 May 2013 12:08:56 +0800 Subject: spi: ep93xx: fix error return code in ep93xx_spi_probe() Fix to return -ENOMEM in the workqueue create error handling case instead of 0, as done elsewhere in this function. Signed-off-by: Wei Yongjun Signed-off-by: Mark Brown --- drivers/spi/spi-ep93xx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-ep93xx.c b/drivers/spi/spi-ep93xx.c index d7bac60253c9..5c63e1323160 100644 --- a/drivers/spi/spi-ep93xx.c +++ b/drivers/spi/spi-ep93xx.c @@ -1104,6 +1104,7 @@ static int ep93xx_spi_probe(struct platform_device *pdev) espi->wq = create_singlethread_workqueue("ep93xx_spid"); if (!espi->wq) { dev_err(&pdev->dev, "unable to create workqueue\n"); + error = -ENOMEM; goto fail_free_dma; } INIT_WORK(&espi->msg_work, ep93xx_spi_work); -- cgit v1.2.3 From 4a577f5275e948f96ea1a9bbce5d994ec6db618b Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 16 May 2013 13:11:32 +0800 Subject: spi: coldfire-qspi: fix error return code in mcfqspi_probe() If pdev->dev.platform_data is not set, mcfqspi_probe() will return 0 and release all the resources, in this case, we should return a error code instead of 0. This patch fix to return -ENOENT in this case and move the check for pdev->dev.platform_data to the begin of this function. Signed-off-by: Wei Yongjun Signed-off-by: Mark Brown --- drivers/spi/spi-coldfire-qspi.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/spi/spi-coldfire-qspi.c b/drivers/spi/spi-coldfire-qspi.c index 7b5cc9e4e94d..f99943bf3519 100644 --- a/drivers/spi/spi-coldfire-qspi.c +++ b/drivers/spi/spi-coldfire-qspi.c @@ -408,6 +408,12 @@ static int mcfqspi_probe(struct platform_device *pdev) struct mcfqspi_platform_data *pdata; int status; + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_dbg(&pdev->dev, "platform data is missing\n"); + return -ENOENT; + } + master = spi_alloc_master(&pdev->dev, sizeof(*mcfqspi)); if (master == NULL) { dev_dbg(&pdev->dev, "spi_alloc_master failed\n"); @@ -458,11 +464,6 @@ static int mcfqspi_probe(struct platform_device *pdev) } clk_enable(mcfqspi->clk); - pdata = pdev->dev.platform_data; - if (!pdata) { - dev_dbg(&pdev->dev, "platform data is missing\n"); - goto fail4; - } master->bus_num = pdata->bus_num; master->num_chipselect = pdata->num_chipselect; -- cgit v1.2.3 From 119363c7dc2bcc0c33c255a7b4979c8c0fdc1896 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 22 Apr 2013 16:29:30 +0200 Subject: cfg80211: add support for per-chain signal strength reporting Signed-off-by: Felix Fietkau Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 14 ++++++++++++++ include/uapi/linux/nl80211.h | 6 ++++++ net/wireless/nl80211.c | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 26b5b692c22b..87f7e1d060ab 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -753,6 +753,8 @@ int cfg80211_check_station_change(struct wiphy *wiphy, * @STATION_INFO_LOCAL_PM: @local_pm filled * @STATION_INFO_PEER_PM: @peer_pm filled * @STATION_INFO_NONPEER_PM: @nonpeer_pm filled + * @STATION_INFO_CHAIN_SIGNAL: @chain_signal filled + * @STATION_INFO_CHAIN_SIGNAL_AVG: @chain_signal_avg filled */ enum station_info_flags { STATION_INFO_INACTIVE_TIME = 1<<0, @@ -781,6 +783,8 @@ enum station_info_flags { STATION_INFO_NONPEER_PM = 1<<23, STATION_INFO_RX_BYTES64 = 1<<24, STATION_INFO_TX_BYTES64 = 1<<25, + STATION_INFO_CHAIN_SIGNAL = 1<<26, + STATION_INFO_CHAIN_SIGNAL_AVG = 1<<27, }; /** @@ -857,6 +861,8 @@ struct sta_bss_parameters { u16 beacon_interval; }; +#define IEEE80211_MAX_CHAINS 4 + /** * struct station_info - station information * @@ -874,6 +880,9 @@ struct sta_bss_parameters { * For CFG80211_SIGNAL_TYPE_MBM, value is expressed in _dBm_. * @signal_avg: Average signal strength, type depends on the wiphy's signal_type. * For CFG80211_SIGNAL_TYPE_MBM, value is expressed in _dBm_. + * @chains: bitmask for filled values in @chain_signal, @chain_signal_avg + * @chain_signal: per-chain signal strength of last received packet in dBm + * @chain_signal_avg: per-chain signal strength average in dBm * @txrate: current unicast bitrate from this station * @rxrate: current unicast bitrate to this station * @rx_packets: packets received from this station @@ -909,6 +918,11 @@ struct station_info { u8 plink_state; s8 signal; s8 signal_avg; + + u8 chains; + s8 chain_signal[IEEE80211_MAX_CHAINS]; + s8 chain_signal_avg[IEEE80211_MAX_CHAINS]; + struct rate_info txrate; struct rate_info rxrate; u32 rx_packets; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index d1e48b5e348f..cbf1e228650f 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1991,6 +1991,10 @@ enum nl80211_sta_bss_param { * @NL80211_STA_INFO_PEER_PM: peer mesh STA link-specific power mode * @NL80211_STA_INFO_NONPEER_PM: neighbor mesh STA power save mode towards * non-peer STA + * @NL80211_STA_INFO_CHAIN_SIGNAL: per-chain signal strength of last PPDU + * Contains a nested array of signal strength attributes (u8, dBm) + * @NL80211_STA_INFO_CHAIN_SIGNAL_AVG: per-chain signal strength average + * Same format as NL80211_STA_INFO_CHAIN_SIGNAL. * @__NL80211_STA_INFO_AFTER_LAST: internal * @NL80211_STA_INFO_MAX: highest possible station info attribute */ @@ -2020,6 +2024,8 @@ enum nl80211_sta_info { NL80211_STA_INFO_NONPEER_PM, NL80211_STA_INFO_RX_BYTES64, NL80211_STA_INFO_TX_BYTES64, + NL80211_STA_INFO_CHAIN_SIGNAL, + NL80211_STA_INFO_CHAIN_SIGNAL_AVG, /* keep last */ __NL80211_STA_INFO_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index afa283841e8c..f687a8d0d026 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3376,6 +3376,32 @@ static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info, return true; } +static bool nl80211_put_signal(struct sk_buff *msg, u8 mask, s8 *signal, + int id) +{ + void *attr; + int i = 0; + + if (!mask) + return true; + + attr = nla_nest_start(msg, id); + if (!attr) + return false; + + for (i = 0; i < IEEE80211_MAX_CHAINS; i++) { + if (!(mask & BIT(i))) + continue; + + if (nla_put_u8(msg, i, signal[i])) + return false; + } + + nla_nest_end(msg, attr); + + return true; +} + static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq, int flags, struct cfg80211_registered_device *rdev, @@ -3447,6 +3473,18 @@ static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq, default: break; } + if (sinfo->filled & STATION_INFO_CHAIN_SIGNAL) { + if (!nl80211_put_signal(msg, sinfo->chains, + sinfo->chain_signal, + NL80211_STA_INFO_CHAIN_SIGNAL)) + goto nla_put_failure; + } + if (sinfo->filled & STATION_INFO_CHAIN_SIGNAL_AVG) { + if (!nl80211_put_signal(msg, sinfo->chains, + sinfo->chain_signal_avg, + NL80211_STA_INFO_CHAIN_SIGNAL_AVG)) + goto nla_put_failure; + } if (sinfo->filled & STATION_INFO_TX_BITRATE) { if (!nl80211_put_sta_rate(msg, &sinfo->txrate, NL80211_STA_INFO_TX_BITRATE)) -- cgit v1.2.3 From ef0621e805f9ef76eaf31ce6205028fe467e9ca9 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 22 Apr 2013 16:29:31 +0200 Subject: mac80211: add support for per-chain signal strength reporting Signed-off-by: Felix Fietkau [fix unit documentation] Signed-off-by: Johannes Berg --- include/net/mac80211.h | 6 ++++++ net/mac80211/cfg.c | 13 ++++++++++++- net/mac80211/rx.c | 14 ++++++++++++++ net/mac80211/sta_info.c | 2 ++ net/mac80211/sta_info.h | 5 +++++ 5 files changed, 39 insertions(+), 1 deletion(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 04c2d4670dc6..5953f25e63f5 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -850,6 +850,10 @@ enum mac80211_rx_flags { * @signal: signal strength when receiving this frame, either in dBm, in dB or * unspecified depending on the hardware capabilities flags * @IEEE80211_HW_SIGNAL_* + * @chains: bitmask of receive chains for which separate signal strength + * values were filled. + * @chain_signal: per-chain signal strength, in dBm (unlike @signal, doesn't + * support dB or unspecified units) * @antenna: antenna used * @rate_idx: index of data rate into band's supported rates or MCS index if * HT or VHT is used (%RX_FLAG_HT/%RX_FLAG_VHT) @@ -881,6 +885,8 @@ struct ieee80211_rx_status { u8 band; u8 antenna; s8 signal; + u8 chains; + s8 chain_signal[IEEE80211_MAX_CHAINS]; u8 ampdu_delimiter_crc; u8 vendor_radiotap_align; u8 vendor_radiotap_oui[3]; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 1a89c80e6407..1f51bdfe574a 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -444,7 +444,7 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) struct ieee80211_local *local = sdata->local; struct timespec uptime; u64 packets = 0; - int ac; + int i, ac; sinfo->generation = sdata->local->sta_generation; @@ -488,6 +488,17 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) sinfo->signal = (s8)sta->last_signal; sinfo->signal_avg = (s8) -ewma_read(&sta->avg_signal); } + if (sta->chains) { + sinfo->filled |= STATION_INFO_CHAIN_SIGNAL | + STATION_INFO_CHAIN_SIGNAL_AVG; + + sinfo->chains = sta->chains; + for (i = 0; i < ARRAY_SIZE(sinfo->chain_signal); i++) { + sinfo->chain_signal[i] = sta->chain_signal_last[i]; + sinfo->chain_signal_avg[i] = + (s8) -ewma_read(&sta->chain_signal_avg[i]); + } + } sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate); sta_set_rate_info_rx(sta, &sinfo->rxrate); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index c8447af76ead..22e412b0767f 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1372,6 +1372,7 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx) struct sk_buff *skb = rx->skb; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + int i; if (!sta) return RX_CONTINUE; @@ -1422,6 +1423,19 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx) ewma_add(&sta->avg_signal, -status->signal); } + if (status->chains) { + sta->chains = status->chains; + for (i = 0; i < ARRAY_SIZE(status->chain_signal); i++) { + int signal = status->chain_signal[i]; + + if (!(status->chains & BIT(i))) + continue; + + sta->chain_signal_last[i] = signal; + ewma_add(&sta->chain_signal_avg[i], -signal); + } + } + /* * Change STA power saving mode only at the end of a frame * exchange sequence. diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 11216bc13b27..a04c5671d7fd 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -358,6 +358,8 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, do_posix_clock_monotonic_gettime(&uptime); sta->last_connected = uptime.tv_sec; ewma_init(&sta->avg_signal, 1024, 8); + for (i = 0; i < ARRAY_SIZE(sta->chain_signal_avg); i++) + ewma_init(&sta->chain_signal_avg[i], 1024, 8); if (sta_prepare_rate_control(local, sta, gfp)) { kfree(sta); diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index adc30045f99e..41c28b977f7c 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -344,6 +344,11 @@ struct sta_info { int last_signal; struct ewma avg_signal; int last_ack_signal; + + u8 chains; + s8 chain_signal_last[IEEE80211_MAX_CHAINS]; + struct ewma chain_signal_avg[IEEE80211_MAX_CHAINS]; + /* Plus 1 for non-QoS frames */ __le16 last_seq_ctrl[IEEE80211_NUM_TIDS + 1]; -- cgit v1.2.3 From 55300a13d2ca1d59f659cf00b9d8dc93ea225882 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Tue, 23 Apr 2013 09:54:21 +0300 Subject: cfg80211: add 60GHz regulatory class Add regulatory class for 60GHz band, according to the last specification. Signed-off-by: Vladimir Kondratiev Signed-off-by: Johannes Berg --- net/wireless/util.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/wireless/util.c b/net/wireless/util.c index f5ad4d94ba88..b11052be09be 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -1169,6 +1169,9 @@ bool ieee80211_operating_class_to_band(u8 operating_class, case 84: *band = IEEE80211_BAND_2GHZ; return true; + case 180: + *band = IEEE80211_BAND_60GHZ; + return true; } return false; -- cgit v1.2.3 From fb4e156886ce6e8309e912d8b370d192330d19d3 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 28 Apr 2013 16:22:06 -0700 Subject: nl80211: Add generic netlink module alias for cfg80211/nl80211 To support auto-loading of wireless modules from netlink users, add module alias for nl80211 family. This also adds NL80211_GENL_NAME constant to define the "nl80211" netlink family name as part of uapi. Signed-off-by: Marcel Holtmann Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 2 ++ net/wireless/core.c | 1 + net/wireless/nl80211.c | 8 ++++---- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index cbf1e228650f..b48430769eda 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -27,6 +27,8 @@ #include +#define NL80211_GENL_NAME "nl80211" + /** * DOC: Station handling * diff --git a/net/wireless/core.c b/net/wireless/core.c index 84c9ad7e1dca..68f0c96c0565 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -34,6 +34,7 @@ MODULE_AUTHOR("Johannes Berg"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("wireless configuration support"); +MODULE_ALIAS_GENL_FAMILY(NL80211_GENL_NAME); /* RCU-protected (and cfg80211_mutex for writers) */ LIST_HEAD(cfg80211_rdev_list); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index f687a8d0d026..9cdcd9ec3317 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -37,10 +37,10 @@ static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb, /* the netlink family */ static struct genl_family nl80211_fam = { - .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */ - .name = "nl80211", /* have users key off the name instead */ - .hdrsize = 0, /* no private header */ - .version = 1, /* no particular meaning now */ + .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */ + .name = NL80211_GENL_NAME, /* have users key off the name instead */ + .hdrsize = 0, /* no private header */ + .version = 1, /* no particular meaning now */ .maxattr = NL80211_ATTR_MAX, .netnsok = true, .pre_doit = nl80211_pre_doit, -- cgit v1.2.3 From 03f831a6f7dc02303df20718a4e96821e43b3d0c Mon Sep 17 00:00:00 2001 From: "Robert P. J. Day" Date: Thu, 2 May 2013 07:15:09 -0400 Subject: wireless: fix kerneldoc content in *80211.h files. Make kerneldoc content match header file content, no functional change. Signed-off-by: Robert P. J. Day Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 1 + include/net/mac80211.h | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 87f7e1d060ab..26e91138a2c6 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4167,6 +4167,7 @@ void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev, * cfg80211_crit_proto_stopped() - indicate critical protocol stopped by driver. * * @wdev: the wireless device for which critical protocol is stopped. + * @gfp: allocation flags * * This function can be called by the driver to indicate it has reverted * operation back to normal. One reason could be that the duration given diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 5953f25e63f5..c36535e7b291 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1241,7 +1241,7 @@ enum ieee80211_sta_rx_bandwidth { * struct ieee80211_sta_rates - station rate selection table * * @rcu_head: RCU head used for freeing the table on update - * @rates: transmit rates/flags to be used by default. + * @rate: transmit rates/flags to be used by default. * Overriding entries per-packet is possible by using cb tx control. */ struct ieee80211_sta_rates { @@ -1282,7 +1282,7 @@ struct ieee80211_sta_rates { * notifications and capabilities. The value is only valid after * the station moves to associated state. * @smps_mode: current SMPS mode (off, static or dynamic) - * @tx_rates: rate control selection table + * @rates: rate control selection table */ struct ieee80211_sta { u32 supp_rates[IEEE80211_NUM_BANDS]; -- cgit v1.2.3 From 04a161f4609dfa387313456fa7ea469fff12cc0d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 3 May 2013 09:35:35 +0200 Subject: mac80211: fix HT beacon-based channel switch handling When an HT AP is advertising channel switch in a beacon, it doesn't (and shouldn't, according to 802.11-2012 Table 8-20) include a secondary channel offset element. The only possible interpretation is that the previous secondary channel offset remains valid, so use that when switching channel based only on beacon information. VHT requires the Wide Bandwidth Channel Switch subelement to be present in the Channel Switch Wrapper element, so the code for that is probably ok (see 802.11ac Draft 4, 8.4.2.165.) Reported-by: Sujith Manoharan Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 29620bfc7a69..a8016c02a757 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1015,7 +1015,8 @@ static void ieee80211_chswitch_timer(unsigned long data) static void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, - u64 timestamp, struct ieee802_11_elems *elems) + u64 timestamp, struct ieee802_11_elems *elems, + bool beacon) { struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; @@ -1032,6 +1033,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, struct cfg80211_chan_def new_vht_chandef = {}; const struct ieee80211_sec_chan_offs_ie *sec_chan_offs; const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie; + const struct ieee80211_ht_operation *ht_oper; int secondary_channel_offset = -1; ASSERT_MGD_MTX(ifmgd); @@ -1048,11 +1050,14 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, sec_chan_offs = elems->sec_chan_offs; wide_bw_chansw_ie = elems->wide_bw_chansw_ie; + ht_oper = elems->ht_operation; if (ifmgd->flags & (IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_40MHZ)) { sec_chan_offs = NULL; wide_bw_chansw_ie = NULL; + /* only used for bandwidth here */ + ht_oper = NULL; } if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT) @@ -1094,10 +1099,20 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, return; } - if (sec_chan_offs) { + if (!beacon && sec_chan_offs) { secondary_channel_offset = sec_chan_offs->sec_chan_offs; + } else if (beacon && ht_oper) { + secondary_channel_offset = + ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET; } else if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) { - /* if HT is enabled and the IE not present, it's still HT */ + /* + * If it's not a beacon, HT is enabled and the IE not present, + * it's 20 MHz, 802.11-2012 8.5.2.6: + * This element [the Secondary Channel Offset Element] is + * present when switching to a 40 MHz channel. It may be + * present when switching to a 20 MHz channel (in which + * case the secondary channel offset is set to SCN). + */ secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; } @@ -2796,7 +2811,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, mutex_unlock(&local->iflist_mtx); } - ieee80211_sta_process_chanswitch(sdata, rx_status->mactime, elems); + ieee80211_sta_process_chanswitch(sdata, rx_status->mactime, + elems, true); } @@ -3210,7 +3226,7 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, ieee80211_sta_process_chanswitch(sdata, rx_status->mactime, - &elems); + &elems, false); } else if (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) { ies_len = skb->len - offsetof(struct ieee80211_mgmt, @@ -3232,7 +3248,7 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, ieee80211_sta_process_chanswitch(sdata, rx_status->mactime, - &elems); + &elems, false); } break; } -- cgit v1.2.3 From 4325f6caad98c075b39f0eaaac6693a0dd43f646 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 8 May 2013 13:09:08 +0200 Subject: wireless: move crypto constants to ieee80211.h mac80211 and the Intel drivers all define crypto constants, move them to ieee80211.h instead. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlegacy/commands.h | 8 ---- drivers/net/wireless/iwlwifi/dvm/commands.h | 8 ---- drivers/net/wireless/iwlwifi/pcie/tx.c | 6 +-- include/linux/ieee80211.h | 9 ++++ net/mac80211/aes_ccm.c | 6 +-- net/mac80211/key.c | 24 +++++----- net/mac80211/key.h | 15 +------ net/mac80211/rx.c | 12 ++--- net/mac80211/wep.c | 48 ++++++++++---------- net/mac80211/wpa.c | 68 +++++++++++++++-------------- 10 files changed, 96 insertions(+), 108 deletions(-) diff --git a/drivers/net/wireless/iwlegacy/commands.h b/drivers/net/wireless/iwlegacy/commands.h index 3b6c99400892..048421511988 100644 --- a/drivers/net/wireless/iwlegacy/commands.h +++ b/drivers/net/wireless/iwlegacy/commands.h @@ -1347,14 +1347,6 @@ struct il_rx_mpdu_res_start { #define TX_CMD_SEC_SHIFT 6 #define TX_CMD_SEC_KEY128 0x08 -/* - * security overhead sizes - */ -#define WEP_IV_LEN 4 -#define WEP_ICV_LEN 4 -#define CCMP_MIC_LEN 8 -#define TKIP_ICV_LEN 4 - /* * C_TX = 0x1c (command) */ diff --git a/drivers/net/wireless/iwlwifi/dvm/commands.h b/drivers/net/wireless/iwlwifi/dvm/commands.h index 95ca026ecc9d..174b0f169497 100644 --- a/drivers/net/wireless/iwlwifi/dvm/commands.h +++ b/drivers/net/wireless/iwlwifi/dvm/commands.h @@ -1224,14 +1224,6 @@ struct iwl_rx_mpdu_res_start { #define TX_CMD_SEC_SHIFT 6 #define TX_CMD_SEC_KEY128 0x08 -/* - * security overhead sizes - */ -#define WEP_IV_LEN 4 -#define WEP_ICV_LEN 4 -#define CCMP_MIC_LEN 8 -#define TKIP_ICV_LEN 4 - /* * REPLY_TX = 0x1c (command) */ diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index c5e30294c5ac..2878ee99a668 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -224,13 +224,13 @@ static void iwl_pcie_txq_update_byte_cnt_tbl(struct iwl_trans *trans, switch (sec_ctl & TX_CMD_SEC_MSK) { case TX_CMD_SEC_CCM: - len += CCMP_MIC_LEN; + len += IEEE80211_CCMP_MIC_LEN; break; case TX_CMD_SEC_TKIP: - len += TKIP_ICV_LEN; + len += IEEE80211_TKIP_ICV_LEN; break; case TX_CMD_SEC_WEP: - len += WEP_IV_LEN + WEP_ICV_LEN; + len += IEEE80211_WEP_IV_LEN + IEEE80211_WEP_ICV_LEN; break; } diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 06b0ed0154a4..d826e5a84af0 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1829,6 +1829,15 @@ enum ieee80211_key_len { WLAN_KEY_LEN_AES_CMAC = 16, }; +#define IEEE80211_WEP_IV_LEN 4 +#define IEEE80211_WEP_ICV_LEN 4 +#define IEEE80211_CCMP_HDR_LEN 8 +#define IEEE80211_CCMP_MIC_LEN 8 +#define IEEE80211_CCMP_PN_LEN 6 +#define IEEE80211_TKIP_IV_LEN 8 +#define IEEE80211_TKIP_ICV_LEN 4 +#define IEEE80211_CMAC_PN_LEN 6 + /* Public action codes */ enum ieee80211_pub_actioncode { WLAN_PUB_ACTION_EXT_CHANSW_ANN = 4, diff --git a/net/mac80211/aes_ccm.c b/net/mac80211/aes_ccm.c index 0785e95c9924..be7614b9ed27 100644 --- a/net/mac80211/aes_ccm.c +++ b/net/mac80211/aes_ccm.c @@ -85,7 +85,7 @@ void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch, *cpos++ = *pos++ ^ e[i]; } - for (i = 0; i < CCMP_MIC_LEN; i++) + for (i = 0; i < IEEE80211_CCMP_MIC_LEN; i++) mic[i] = b[i] ^ s_0[i]; } @@ -123,7 +123,7 @@ int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch, crypto_cipher_encrypt_one(tfm, a, a); } - for (i = 0; i < CCMP_MIC_LEN; i++) { + for (i = 0; i < IEEE80211_CCMP_MIC_LEN; i++) { if ((mic[i] ^ s_0[i]) != a[i]) return -1; } @@ -138,7 +138,7 @@ struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[]) tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC); if (!IS_ERR(tfm)) - crypto_cipher_setkey(tfm, key, ALG_CCMP_KEY_LEN); + crypto_cipher_setkey(tfm, key, WLAN_KEY_LEN_CCMP); return tfm; } diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 67059b88fea5..e39cc91d0cf1 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -335,12 +335,12 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len, switch (cipher) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: - key->conf.iv_len = WEP_IV_LEN; - key->conf.icv_len = WEP_ICV_LEN; + key->conf.iv_len = IEEE80211_WEP_IV_LEN; + key->conf.icv_len = IEEE80211_WEP_ICV_LEN; break; case WLAN_CIPHER_SUITE_TKIP: - key->conf.iv_len = TKIP_IV_LEN; - key->conf.icv_len = TKIP_ICV_LEN; + key->conf.iv_len = IEEE80211_TKIP_IV_LEN; + key->conf.icv_len = IEEE80211_TKIP_ICV_LEN; if (seq) { for (i = 0; i < IEEE80211_NUM_TIDS; i++) { key->u.tkip.rx[i].iv32 = @@ -352,13 +352,13 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len, spin_lock_init(&key->u.tkip.txlock); break; case WLAN_CIPHER_SUITE_CCMP: - key->conf.iv_len = CCMP_HDR_LEN; - key->conf.icv_len = CCMP_MIC_LEN; + key->conf.iv_len = IEEE80211_CCMP_HDR_LEN; + key->conf.icv_len = IEEE80211_CCMP_MIC_LEN; if (seq) { for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++) - for (j = 0; j < CCMP_PN_LEN; j++) + for (j = 0; j < IEEE80211_CCMP_PN_LEN; j++) key->u.ccmp.rx_pn[i][j] = - seq[CCMP_PN_LEN - j - 1]; + seq[IEEE80211_CCMP_PN_LEN - j - 1]; } /* * Initialize AES key state here as an optimization so that @@ -375,9 +375,9 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len, key->conf.iv_len = 0; key->conf.icv_len = sizeof(struct ieee80211_mmie); if (seq) - for (j = 0; j < CMAC_PN_LEN; j++) + for (j = 0; j < IEEE80211_CMAC_PN_LEN; j++) key->u.aes_cmac.rx_pn[j] = - seq[CMAC_PN_LEN - j - 1]; + seq[IEEE80211_CMAC_PN_LEN - j - 1]; /* * Initialize AES key state here as an optimization so that * it does not need to be initialized for every packet. @@ -740,13 +740,13 @@ void ieee80211_get_key_rx_seq(struct ieee80211_key_conf *keyconf, pn = key->u.ccmp.rx_pn[IEEE80211_NUM_TIDS]; else pn = key->u.ccmp.rx_pn[tid]; - memcpy(seq->ccmp.pn, pn, CCMP_PN_LEN); + memcpy(seq->ccmp.pn, pn, IEEE80211_CCMP_PN_LEN); break; case WLAN_CIPHER_SUITE_AES_CMAC: if (WARN_ON(tid != 0)) return; pn = key->u.aes_cmac.rx_pn; - memcpy(seq->aes_cmac.pn, pn, CMAC_PN_LEN); + memcpy(seq->aes_cmac.pn, pn, IEEE80211_CMAC_PN_LEN); break; } } diff --git a/net/mac80211/key.h b/net/mac80211/key.h index e8de3e6d7804..036d57e76a5e 100644 --- a/net/mac80211/key.h +++ b/net/mac80211/key.h @@ -19,17 +19,6 @@ #define NUM_DEFAULT_KEYS 4 #define NUM_DEFAULT_MGMT_KEYS 2 -#define WEP_IV_LEN 4 -#define WEP_ICV_LEN 4 -#define ALG_CCMP_KEY_LEN 16 -#define CCMP_HDR_LEN 8 -#define CCMP_MIC_LEN 8 -#define CCMP_TK_LEN 16 -#define CCMP_PN_LEN 6 -#define TKIP_IV_LEN 8 -#define TKIP_ICV_LEN 4 -#define CMAC_PN_LEN 6 - struct ieee80211_local; struct ieee80211_sub_if_data; struct sta_info; @@ -93,13 +82,13 @@ struct ieee80211_key { * frames and the last counter is used with Robust * Management frames. */ - u8 rx_pn[IEEE80211_NUM_TIDS + 1][CCMP_PN_LEN]; + u8 rx_pn[IEEE80211_NUM_TIDS + 1][IEEE80211_CCMP_PN_LEN]; struct crypto_cipher *tfm; u32 replays; /* dot11RSNAStatsCCMPReplays */ } ccmp; struct { atomic64_t tx_pn; - u8 rx_pn[CMAC_PN_LEN]; + u8 rx_pn[IEEE80211_CMAC_PN_LEN]; struct crypto_cipher *tfm; u32 replays; /* dot11RSNAStatsCMACReplays */ u32 icverrors; /* dot11RSNAStatsCMACICVErrors */ diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 22e412b0767f..6e2c8c5236c4 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1622,7 +1622,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) entry->ccmp = 1; memcpy(entry->last_pn, rx->key->u.ccmp.rx_pn[queue], - CCMP_PN_LEN); + IEEE80211_CCMP_PN_LEN); } return RX_QUEUED; } @@ -1641,21 +1641,21 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) * (IEEE 802.11i, 8.3.3.4.5) */ if (entry->ccmp) { int i; - u8 pn[CCMP_PN_LEN], *rpn; + u8 pn[IEEE80211_CCMP_PN_LEN], *rpn; int queue; if (!rx->key || rx->key->conf.cipher != WLAN_CIPHER_SUITE_CCMP) return RX_DROP_UNUSABLE; - memcpy(pn, entry->last_pn, CCMP_PN_LEN); - for (i = CCMP_PN_LEN - 1; i >= 0; i--) { + memcpy(pn, entry->last_pn, IEEE80211_CCMP_PN_LEN); + for (i = IEEE80211_CCMP_PN_LEN - 1; i >= 0; i--) { pn[i]++; if (pn[i]) break; } queue = rx->security_idx; rpn = rx->key->u.ccmp.rx_pn[queue]; - if (memcmp(pn, rpn, CCMP_PN_LEN)) + if (memcmp(pn, rpn, IEEE80211_CCMP_PN_LEN)) return RX_DROP_UNUSABLE; - memcpy(entry->last_pn, pn, CCMP_PN_LEN); + memcpy(entry->last_pn, pn, IEEE80211_CCMP_PN_LEN); } skb_pull(rx->skb, ieee80211_hdrlen(fc)); diff --git a/net/mac80211/wep.c b/net/mac80211/wep.c index c04d401dae92..6ee2b5863572 100644 --- a/net/mac80211/wep.c +++ b/net/mac80211/wep.c @@ -28,7 +28,7 @@ int ieee80211_wep_init(struct ieee80211_local *local) { /* start WEP IV from a random value */ - get_random_bytes(&local->wep_iv, WEP_IV_LEN); + get_random_bytes(&local->wep_iv, IEEE80211_WEP_IV_LEN); local->wep_tx_tfm = crypto_alloc_cipher("arc4", 0, CRYPTO_ALG_ASYNC); if (IS_ERR(local->wep_tx_tfm)) { @@ -98,20 +98,21 @@ static u8 *ieee80211_wep_add_iv(struct ieee80211_local *local, hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); - if (WARN_ON(skb_tailroom(skb) < WEP_ICV_LEN || - skb_headroom(skb) < WEP_IV_LEN)) + if (WARN_ON(skb_tailroom(skb) < IEEE80211_WEP_ICV_LEN || + skb_headroom(skb) < IEEE80211_WEP_IV_LEN)) return NULL; hdrlen = ieee80211_hdrlen(hdr->frame_control); - newhdr = skb_push(skb, WEP_IV_LEN); - memmove(newhdr, newhdr + WEP_IV_LEN, hdrlen); + newhdr = skb_push(skb, IEEE80211_WEP_IV_LEN); + memmove(newhdr, newhdr + IEEE80211_WEP_IV_LEN, hdrlen); /* the HW only needs room for the IV, but not the actual IV */ if (info->control.hw_key && (info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) return newhdr + hdrlen; - skb_set_network_header(skb, skb_network_offset(skb) + WEP_IV_LEN); + skb_set_network_header(skb, skb_network_offset(skb) + + IEEE80211_WEP_IV_LEN); ieee80211_wep_get_iv(local, keylen, keyidx, newhdr + hdrlen); return newhdr + hdrlen; } @@ -125,8 +126,8 @@ static void ieee80211_wep_remove_iv(struct ieee80211_local *local, unsigned int hdrlen; hdrlen = ieee80211_hdrlen(hdr->frame_control); - memmove(skb->data + WEP_IV_LEN, skb->data, hdrlen); - skb_pull(skb, WEP_IV_LEN); + memmove(skb->data + IEEE80211_WEP_IV_LEN, skb->data, hdrlen); + skb_pull(skb, IEEE80211_WEP_IV_LEN); } @@ -146,7 +147,7 @@ int ieee80211_wep_encrypt_data(struct crypto_cipher *tfm, u8 *rc4key, put_unaligned(icv, (__le32 *)(data + data_len)); crypto_cipher_setkey(tfm, rc4key, klen); - for (i = 0; i < data_len + WEP_ICV_LEN; i++) + for (i = 0; i < data_len + IEEE80211_WEP_ICV_LEN; i++) crypto_cipher_encrypt_one(tfm, data + i, data + i); return 0; @@ -172,7 +173,7 @@ int ieee80211_wep_encrypt(struct ieee80211_local *local, if (!iv) return -1; - len = skb->len - (iv + WEP_IV_LEN - skb->data); + len = skb->len - (iv + IEEE80211_WEP_IV_LEN - skb->data); /* Prepend 24-bit IV to RC4 key */ memcpy(rc4key, iv, 3); @@ -181,10 +182,10 @@ int ieee80211_wep_encrypt(struct ieee80211_local *local, memcpy(rc4key + 3, key, keylen); /* Add room for ICV */ - skb_put(skb, WEP_ICV_LEN); + skb_put(skb, IEEE80211_WEP_ICV_LEN); return ieee80211_wep_encrypt_data(local->wep_tx_tfm, rc4key, keylen + 3, - iv + WEP_IV_LEN, len); + iv + IEEE80211_WEP_IV_LEN, len); } @@ -201,11 +202,11 @@ int ieee80211_wep_decrypt_data(struct crypto_cipher *tfm, u8 *rc4key, return -1; crypto_cipher_setkey(tfm, rc4key, klen); - for (i = 0; i < data_len + WEP_ICV_LEN; i++) + for (i = 0; i < data_len + IEEE80211_WEP_ICV_LEN; i++) crypto_cipher_decrypt_one(tfm, data + i, data + i); crc = cpu_to_le32(~crc32_le(~0, data, data_len)); - if (memcmp(&crc, data + data_len, WEP_ICV_LEN) != 0) + if (memcmp(&crc, data + data_len, IEEE80211_WEP_ICV_LEN) != 0) /* ICV mismatch */ return -1; @@ -237,10 +238,10 @@ static int ieee80211_wep_decrypt(struct ieee80211_local *local, return -1; hdrlen = ieee80211_hdrlen(hdr->frame_control); - if (skb->len < hdrlen + WEP_IV_LEN + WEP_ICV_LEN) + if (skb->len < hdrlen + IEEE80211_WEP_IV_LEN + IEEE80211_WEP_ICV_LEN) return -1; - len = skb->len - hdrlen - WEP_IV_LEN - WEP_ICV_LEN; + len = skb->len - hdrlen - IEEE80211_WEP_IV_LEN - IEEE80211_WEP_ICV_LEN; keyidx = skb->data[hdrlen + 3] >> 6; @@ -256,16 +257,16 @@ static int ieee80211_wep_decrypt(struct ieee80211_local *local, memcpy(rc4key + 3, key->conf.key, key->conf.keylen); if (ieee80211_wep_decrypt_data(local->wep_rx_tfm, rc4key, klen, - skb->data + hdrlen + WEP_IV_LEN, - len)) + skb->data + hdrlen + + IEEE80211_WEP_IV_LEN, len)) ret = -1; /* Trim ICV */ - skb_trim(skb, skb->len - WEP_ICV_LEN); + skb_trim(skb, skb->len - IEEE80211_WEP_ICV_LEN); /* Remove IV */ - memmove(skb->data + WEP_IV_LEN, skb->data, hdrlen); - skb_pull(skb, WEP_IV_LEN); + memmove(skb->data + IEEE80211_WEP_IV_LEN, skb->data, hdrlen); + skb_pull(skb, IEEE80211_WEP_IV_LEN); return ret; } @@ -305,13 +306,14 @@ ieee80211_crypto_wep_decrypt(struct ieee80211_rx_data *rx) if (ieee80211_wep_decrypt(rx->local, rx->skb, rx->key)) return RX_DROP_UNUSABLE; } else if (!(status->flag & RX_FLAG_IV_STRIPPED)) { - if (!pskb_may_pull(rx->skb, ieee80211_hdrlen(fc) + WEP_IV_LEN)) + if (!pskb_may_pull(rx->skb, ieee80211_hdrlen(fc) + + IEEE80211_WEP_IV_LEN)) return RX_DROP_UNUSABLE; if (rx->sta && ieee80211_wep_is_weak_iv(rx->skb, rx->key)) rx->sta->wep_weak_iv_count++; ieee80211_wep_remove_iv(rx->local, rx->skb, rx->key); /* remove ICV */ - if (pskb_trim(rx->skb, rx->skb->len - WEP_ICV_LEN)) + if (pskb_trim(rx->skb, rx->skb->len - IEEE80211_WEP_ICV_LEN)) return RX_DROP_UNUSABLE; } diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index c7c6d644486f..c9edfcb7a13b 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -62,10 +62,10 @@ ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx) tail = MICHAEL_MIC_LEN; if (!info->control.hw_key) - tail += TKIP_ICV_LEN; + tail += IEEE80211_TKIP_ICV_LEN; if (WARN_ON(skb_tailroom(skb) < tail || - skb_headroom(skb) < TKIP_IV_LEN)) + skb_headroom(skb) < IEEE80211_TKIP_IV_LEN)) return TX_DROP; key = &tx->key->conf.key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY]; @@ -198,15 +198,16 @@ static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) if (info->control.hw_key) tail = 0; else - tail = TKIP_ICV_LEN; + tail = IEEE80211_TKIP_ICV_LEN; if (WARN_ON(skb_tailroom(skb) < tail || - skb_headroom(skb) < TKIP_IV_LEN)) + skb_headroom(skb) < IEEE80211_TKIP_IV_LEN)) return -1; - pos = skb_push(skb, TKIP_IV_LEN); - memmove(pos, pos + TKIP_IV_LEN, hdrlen); - skb_set_network_header(skb, skb_network_offset(skb) + TKIP_IV_LEN); + pos = skb_push(skb, IEEE80211_TKIP_IV_LEN); + memmove(pos, pos + IEEE80211_TKIP_IV_LEN, hdrlen); + skb_set_network_header(skb, skb_network_offset(skb) + + IEEE80211_TKIP_IV_LEN); pos += hdrlen; /* the HW only needs room for the IV, but not the actual IV */ @@ -227,7 +228,7 @@ static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) return 0; /* Add room for ICV */ - skb_put(skb, TKIP_ICV_LEN); + skb_put(skb, IEEE80211_TKIP_ICV_LEN); return ieee80211_tkip_encrypt_data(tx->local->wep_tx_tfm, key, skb, pos, len); @@ -290,11 +291,11 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx) return RX_DROP_UNUSABLE; /* Trim ICV */ - skb_trim(skb, skb->len - TKIP_ICV_LEN); + skb_trim(skb, skb->len - IEEE80211_TKIP_ICV_LEN); /* Remove IV */ - memmove(skb->data + TKIP_IV_LEN, skb->data, hdrlen); - skb_pull(skb, TKIP_IV_LEN); + memmove(skb->data + IEEE80211_TKIP_IV_LEN, skb->data, hdrlen); + skb_pull(skb, IEEE80211_TKIP_IV_LEN); return RX_CONTINUE; } @@ -337,9 +338,9 @@ static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch, else qos_tid = 0; - data_len = skb->len - hdrlen - CCMP_HDR_LEN; + data_len = skb->len - hdrlen - IEEE80211_CCMP_HDR_LEN; if (encrypted) - data_len -= CCMP_MIC_LEN; + data_len -= IEEE80211_CCMP_MIC_LEN; /* First block, b_0 */ b_0[0] = 0x59; /* flags: Adata: 1, M: 011, L: 001 */ @@ -348,7 +349,7 @@ static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch, */ b_0[1] = qos_tid | (mgmt << 4); memcpy(&b_0[2], hdr->addr2, ETH_ALEN); - memcpy(&b_0[8], pn, CCMP_PN_LEN); + memcpy(&b_0[8], pn, IEEE80211_CCMP_PN_LEN); /* l(m) */ put_unaligned_be16(data_len, &b_0[14]); @@ -424,15 +425,16 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) if (info->control.hw_key) tail = 0; else - tail = CCMP_MIC_LEN; + tail = IEEE80211_CCMP_MIC_LEN; if (WARN_ON(skb_tailroom(skb) < tail || - skb_headroom(skb) < CCMP_HDR_LEN)) + skb_headroom(skb) < IEEE80211_CCMP_HDR_LEN)) return -1; - pos = skb_push(skb, CCMP_HDR_LEN); - memmove(pos, pos + CCMP_HDR_LEN, hdrlen); - skb_set_network_header(skb, skb_network_offset(skb) + CCMP_HDR_LEN); + pos = skb_push(skb, IEEE80211_CCMP_HDR_LEN); + memmove(pos, pos + IEEE80211_CCMP_HDR_LEN, hdrlen); + skb_set_network_header(skb, skb_network_offset(skb) + + IEEE80211_CCMP_HDR_LEN); /* the HW only needs room for the IV, but not the actual IV */ if (info->control.hw_key && @@ -457,10 +459,10 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) if (info->control.hw_key) return 0; - pos += CCMP_HDR_LEN; + pos += IEEE80211_CCMP_HDR_LEN; ccmp_special_blocks(skb, pn, scratch, 0); ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, scratch, pos, len, - pos, skb_put(skb, CCMP_MIC_LEN)); + pos, skb_put(skb, IEEE80211_CCMP_MIC_LEN)); return 0; } @@ -490,7 +492,7 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx) struct ieee80211_key *key = rx->key; struct sk_buff *skb = rx->skb; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); - u8 pn[CCMP_PN_LEN]; + u8 pn[IEEE80211_CCMP_PN_LEN]; int data_len; int queue; @@ -500,12 +502,13 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx) !ieee80211_is_robust_mgmt_frame(hdr)) return RX_CONTINUE; - data_len = skb->len - hdrlen - CCMP_HDR_LEN - CCMP_MIC_LEN; + data_len = skb->len - hdrlen - IEEE80211_CCMP_HDR_LEN - + IEEE80211_CCMP_MIC_LEN; if (!rx->sta || data_len < 0) return RX_DROP_UNUSABLE; if (status->flag & RX_FLAG_DECRYPTED) { - if (!pskb_may_pull(rx->skb, hdrlen + CCMP_HDR_LEN)) + if (!pskb_may_pull(rx->skb, hdrlen + IEEE80211_CCMP_HDR_LEN)) return RX_DROP_UNUSABLE; } else { if (skb_linearize(rx->skb)) @@ -516,7 +519,7 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx) queue = rx->security_idx; - if (memcmp(pn, key->u.ccmp.rx_pn[queue], CCMP_PN_LEN) <= 0) { + if (memcmp(pn, key->u.ccmp.rx_pn[queue], IEEE80211_CCMP_PN_LEN) <= 0) { key->u.ccmp.replays++; return RX_DROP_UNUSABLE; } @@ -528,19 +531,20 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx) if (ieee80211_aes_ccm_decrypt( key->u.ccmp.tfm, scratch, - skb->data + hdrlen + CCMP_HDR_LEN, data_len, - skb->data + skb->len - CCMP_MIC_LEN, - skb->data + hdrlen + CCMP_HDR_LEN)) + skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN, + data_len, + skb->data + skb->len - IEEE80211_CCMP_MIC_LEN, + skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN)) return RX_DROP_UNUSABLE; } - memcpy(key->u.ccmp.rx_pn[queue], pn, CCMP_PN_LEN); + memcpy(key->u.ccmp.rx_pn[queue], pn, IEEE80211_CCMP_PN_LEN); /* Remove CCMP header and MIC */ - if (pskb_trim(skb, skb->len - CCMP_MIC_LEN)) + if (pskb_trim(skb, skb->len - IEEE80211_CCMP_MIC_LEN)) return RX_DROP_UNUSABLE; - memmove(skb->data + CCMP_HDR_LEN, skb->data, hdrlen); - skb_pull(skb, CCMP_HDR_LEN); + memmove(skb->data + IEEE80211_CCMP_HDR_LEN, skb->data, hdrlen); + skb_pull(skb, IEEE80211_CCMP_HDR_LEN); return RX_CONTINUE; } -- cgit v1.2.3 From bd500af223c9aed7083730b7044d53162065e418 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 6 May 2013 21:09:46 +0200 Subject: mac80211: write memcpy differently for smatch There's no real difference between *array and array, but the former confuses smatch so write it differently. The generated code is exactly the same. Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 1f51bdfe574a..66989458f5ff 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -739,7 +739,7 @@ static void ieee80211_get_et_strings(struct wiphy *wiphy, if (sset == ETH_SS_STATS) { sz_sta_stats = sizeof(ieee80211_gstrings_sta_stats); - memcpy(data, *ieee80211_gstrings_sta_stats, sz_sta_stats); + memcpy(data, ieee80211_gstrings_sta_stats, sz_sta_stats); } drv_get_et_strings(sdata, sset, &(data[sz_sta_stats])); } -- cgit v1.2.3 From 7ade7036043e2e8e2831ae189ce5c248386062f1 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 13 May 2013 11:37:30 +0200 Subject: cfg80211: use C99 initialisers to simplify code a bit Use C99 initialisers for the auth, deauth and disassoc requests to simplify the code. Signed-off-by: Johannes Berg --- net/wireless/mlme.c | 44 ++++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 0c7b7dd855f6..c21e32f9549c 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -264,7 +264,16 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, const u8 *sae_data, int sae_data_len) { struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_auth_request req; + struct cfg80211_auth_request req = { + .ie = ie, + .ie_len = ie_len, + .sae_data = sae_data, + .sae_data_len = sae_data_len, + .auth_type = auth_type, + .key = key, + .key_len = key_len, + .key_idx = key_idx, + }; int err; ASSERT_WDEV_LOCK(wdev); @@ -277,18 +286,8 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, ether_addr_equal(bssid, wdev->current_bss->pub.bssid)) return -EALREADY; - memset(&req, 0, sizeof(req)); - - req.ie = ie; - req.ie_len = ie_len; - req.sae_data = sae_data; - req.sae_data_len = sae_data_len; - req.auth_type = auth_type; req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len, WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); - req.key = key; - req.key_len = key_len; - req.key_idx = key_idx; if (!req.bss) return -ENOENT; @@ -480,7 +479,12 @@ static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, bool local_state_change) { struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_disassoc_request req; + struct cfg80211_disassoc_request req = { + .reason_code = reason, + .local_state_change = local_state_change, + .ie = ie, + .ie_len = ie_len, + }; ASSERT_WDEV_LOCK(wdev); @@ -490,11 +494,6 @@ static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, if (WARN(!wdev->current_bss, "sme_state=%d\n", wdev->sme_state)) return -ENOTCONN; - memset(&req, 0, sizeof(req)); - req.reason_code = reason; - req.local_state_change = local_state_change; - req.ie = ie; - req.ie_len = ie_len; if (ether_addr_equal(wdev->current_bss->pub.bssid, bssid)) req.bss = &wdev->current_bss->pub; else @@ -523,24 +522,21 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, struct net_device *dev) { struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_deauth_request req; u8 bssid[ETH_ALEN]; + struct cfg80211_deauth_request req = { + .reason_code = WLAN_REASON_DEAUTH_LEAVING, + .bssid = bssid, + }; ASSERT_WDEV_LOCK(wdev); if (!rdev->ops->deauth) return; - memset(&req, 0, sizeof(req)); - req.reason_code = WLAN_REASON_DEAUTH_LEAVING; - req.ie = NULL; - req.ie_len = 0; - if (!wdev->current_bss) return; memcpy(bssid, wdev->current_bss->pub.bssid, ETH_ALEN); - req.bssid = bssid; rdev_deauth(rdev, dev, &req); if (wdev->current_bss) { -- cgit v1.2.3 From 6e16d90b5218307db805e6b3e0b06d3946eb8c4c Mon Sep 17 00:00:00 2001 From: Colleen Twitty Date: Wed, 8 May 2013 11:45:59 -0700 Subject: cfg80211: Userspace may inform kernel of mesh auth method. Authentication takes place in userspace, but the beacon is generated in the kernel. Allow userspace to inform the kernel of the authentication method so the appropriate mesh config IE can be set prior to beacon generation when joining the MBSS. Signed-off-by: Colleen Twitty Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 ++ include/uapi/linux/nl80211.h | 5 +++++ net/wireless/mesh.c | 1 + net/wireless/nl80211.c | 8 ++++++++ 4 files changed, 16 insertions(+) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 26e91138a2c6..32a2f1b20861 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1161,6 +1161,7 @@ struct mesh_config { * @sync_method: which synchronization method to use * @path_sel_proto: which path selection protocol to use * @path_metric: which metric to use + * @auth_id: which authentication method this mesh is using * @ie: vendor information elements (optional) * @ie_len: length of vendor information elements * @is_authenticated: this mesh requires authentication @@ -1179,6 +1180,7 @@ struct mesh_setup { u8 sync_method; u8 path_sel_proto; u8 path_metric; + u8 auth_id; const u8 *ie; u8 ie_len; bool is_authenticated; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index b48430769eda..06320713e9c9 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2645,6 +2645,10 @@ enum nl80211_meshconf_params { * @NL80211_MESH_SETUP_USERSPACE_MPM: Enable this option if userspace will * implement an MPM which handles peer allocation and state. * + * @NL80211_MESH_SETUP_AUTH_PROTOCOL: Inform the kernel of the authentication + * method (u8, as defined in IEEE 8.4.2.100.6, e.g. 0x1 for SAE). + * Default is no authentication method required. + * * @NL80211_MESH_SETUP_ATTR_MAX: highest possible mesh setup attribute number * * @__NL80211_MESH_SETUP_ATTR_AFTER_LAST: Internal use @@ -2658,6 +2662,7 @@ enum nl80211_mesh_setup_params { NL80211_MESH_SETUP_USERSPACE_AMPE, NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC, NL80211_MESH_SETUP_USERSPACE_MPM, + NL80211_MESH_SETUP_AUTH_PROTOCOL, /* keep last */ __NL80211_MESH_SETUP_ATTR_AFTER_LAST, diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 0bb93f3061a4..9546ad210550 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -82,6 +82,7 @@ const struct mesh_setup default_mesh_setup = { .sync_method = IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET, .path_sel_proto = IEEE80211_PATH_PROTOCOL_HWMP, .path_metric = IEEE80211_PATH_METRIC_AIRTIME, + .auth_id = 0, /* open */ .ie = NULL, .ie_len = 0, .is_secure = false, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 9cdcd9ec3317..5f10f7acfa06 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4672,6 +4672,7 @@ static const struct nla_policy [NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL] = { .type = NLA_U8 }, [NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC] = { .type = NLA_U8 }, [NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG }, + [NL80211_MESH_SETUP_AUTH_PROTOCOL] = { .type = NLA_U8 }, [NL80211_MESH_SETUP_USERSPACE_MPM] = { .type = NLA_FLAG }, [NL80211_MESH_SETUP_IE] = { .type = NLA_BINARY, .len = IEEE80211_MAX_DATA_LEN }, @@ -4857,6 +4858,13 @@ static int nl80211_parse_mesh_setup(struct genl_info *info, if (setup->is_secure) setup->user_mpm = true; + if (tb[NL80211_MESH_SETUP_AUTH_PROTOCOL]) { + if (!setup->user_mpm) + return -EINVAL; + setup->auth_id = + nla_get_u8(tb[NL80211_MESH_SETUP_AUTH_PROTOCOL]); + } + return 0; } -- cgit v1.2.3 From 0d4261ad5d0028b26cd88e645b4507eed8aab3f7 Mon Sep 17 00:00:00 2001 From: Colleen Twitty Date: Wed, 8 May 2013 11:46:00 -0700 Subject: mac80211: enable Auth Protocol Identifier on mesh config. Previously the mesh_auth_id was disabled. Instead set the correct mesh authentication bit based on the mesh setup. Signed-off-by: Colleen Twitty Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 1 + net/mac80211/mesh.c | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 66989458f5ff..eb4219051043 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1746,6 +1746,7 @@ static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh, ifmsh->mesh_pp_id = setup->path_sel_proto; ifmsh->mesh_pm_id = setup->path_metric; ifmsh->user_mpm = setup->user_mpm; + ifmsh->mesh_auth_id = setup->auth_id; ifmsh->security = IEEE80211_MESH_SEC_NONE; if (setup->is_authenticated) ifmsh->security |= IEEE80211_MESH_SEC_AUTHED; diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 6952760881c8..c13db9ad394b 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -748,7 +748,6 @@ int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) ieee80211_configure_filter(local); ifmsh->mesh_cc_id = 0; /* Disabled */ - ifmsh->mesh_auth_id = 0; /* Disabled */ /* register sync ops from extensible synchronization framework */ ifmsh->sync_ops = ieee80211_mesh_sync_ops_get(ifmsh->mesh_sp_id); ifmsh->adjusting_tbtt = false; -- cgit v1.2.3 From ce85788846ec19dcb7bef0dcbcf83fb64630f426 Mon Sep 17 00:00:00 2001 From: Alexander Bondar Date: Mon, 6 May 2013 17:17:04 +0300 Subject: mac80211: enable power save only if DTIM period is available Generally, the DTIM period is available after a beacon has been received, and if no beacon has been received enabling powersave is problematic anyway for synchronisation. Since some drivers may require the DTIM period for powersave, don't enable powersave until it becomes available in case the scan/association managed to not receive a beacon. Signed-off-by: Alexander Bondar Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index a8016c02a757..ef378b9a32ec 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1390,6 +1390,9 @@ static bool ieee80211_powersave_allowed(struct ieee80211_sub_if_data *sdata) IEEE80211_STA_CONNECTION_POLL)) return false; + if (!sdata->vif.bss_conf.dtim_period) + return false; + rcu_read_lock(); sta = sta_info_get(sdata, mgd->bssid); if (sta) @@ -3126,6 +3129,7 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, } changed |= BSS_CHANGED_DTIM_PERIOD; + ieee80211_recalc_ps_vif(sdata); } if (elems.erp_info) { -- cgit v1.2.3 From 4270b871a266f615440ef716345c9f559f81eb30 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 6 May 2013 10:41:03 +0300 Subject: iwlwifi: mvm: add a missing define in firmware API TX_CMD_SEC_MSK was missing. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h index 007a93b25bd7..6994232f5726 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h @@ -134,6 +134,7 @@ enum iwl_tx_flags { #define TX_CMD_SEC_WEP 0x01 #define TX_CMD_SEC_CCM 0x02 #define TX_CMD_SEC_TKIP 0x03 +#define TX_CMD_SEC_MSK 0x07 #define TX_CMD_SEC_WEP_KEY_IDX_POS 6 #define TX_CMD_SEC_WEP_KEY_IDX_MSK 0xc0 #define TX_CMD_SEC_KEY128 0x08 -- cgit v1.2.3 From 5b1dbfc3aa26327dede4cf7a88d7c1e71728e024 Mon Sep 17 00:00:00 2001 From: Alexander Bondar Date: Tue, 23 Apr 2013 13:32:35 +0300 Subject: iwlwifi: mvm: Add number of DTIMs to skip New host-device API provides the ability to set the number of DTIMs to skip. Add this parameter to the command and set it (to a sane default value.) Signed-off-by: Alexander Bondar Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/mvm/fw-api-power.h | 6 +++--- drivers/net/wireless/iwlwifi/mvm/power.c | 7 ++++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h index 90fdfcda15b6..05e51927b848 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h @@ -101,19 +101,19 @@ enum iwl_power_flags { * @tx_data_timeout: Minimum time (usec) from last Tx packet for AM to * PSM transition - legacy PM * @sleep_interval: not in use - * @keep_alive_beacons: not in use + * @num_skip_dtim: Number of DTIMs to skip if Skip over DTIM flag is set * @lprx_rssi_threshold: Signal strength up to which LP RX can be enabled. * Default: 80dbm */ struct iwl_powertable_cmd { - /* PM_POWER_TABLE_CMD_API_S_VER_5 */ + /* PM_POWER_TABLE_CMD_API_S_VER_6 */ __le16 flags; u8 keep_alive_seconds; u8 debug_flags; __le32 rx_data_timeout; __le32 tx_data_timeout; __le32 sleep_interval[IWL_POWER_VEC_SIZE]; - __le32 keep_alive_beacons; + __le32 num_skip_dtim; __le32 lprx_rssi_threshold; } __packed; diff --git a/drivers/net/wireless/iwlwifi/mvm/power.c b/drivers/net/wireless/iwlwifi/mvm/power.c index 30a5c27bd623..6c5dfc9a8d43 100644 --- a/drivers/net/wireless/iwlwifi/mvm/power.c +++ b/drivers/net/wireless/iwlwifi/mvm/power.c @@ -91,6 +91,9 @@ static void iwl_mvm_power_log(struct iwl_mvm *mvm, le32_to_cpu(cmd->tx_data_timeout)); IWL_DEBUG_POWER(mvm, "LP RX RSSI threshold = %u\n", cmd->lprx_rssi_threshold); + if (cmd->flags & cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) + IWL_DEBUG_POWER(mvm, "DTIMs to skip = %u\n", + le32_to_cpu(cmd->num_skip_dtim)); } } @@ -135,8 +138,10 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, /* Check skip over DTIM conditions */ if (!radar_detect && (dtimper <= 10) && - (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP)) + (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP)) { cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK); + cmd->num_skip_dtim = cpu_to_le32(2); + } /* Check that keep alive period is at least 3 * DTIM */ dtimper_msec = dtimper * vif->bss_conf.beacon_int; -- cgit v1.2.3 From ee1e84225f779386858702872fb299457e7941f9 Mon Sep 17 00:00:00 2001 From: Alexander Bondar Date: Tue, 23 Apr 2013 13:52:10 +0300 Subject: iwlwifi: mvm: configure power management in D3 Configure power management in the D3 firmware by sending the power table command to it when suspending; this uses some values that are more suitable to a low power state. Signed-off-by: Alexander Bondar Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/mvm/d3.c | 4 ++++ drivers/net/wireless/iwlwifi/mvm/power.c | 12 +++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c index 63d788aef3f4..4d3c978b5c76 100644 --- a/drivers/net/wireless/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/iwlwifi/mvm/d3.c @@ -1007,6 +1007,10 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) if (ret) goto out; + ret = iwl_mvm_power_update_mode(mvm, vif); + if (ret) + goto out; + /* must be last -- this switches firmware state */ ret = iwl_mvm_send_cmd_pdu(mvm, D3_CONFIG_CMD, CMD_SYNC, sizeof(d3_cfg_cmd), &d3_cfg_cmd); diff --git a/drivers/net/wireless/iwlwifi/mvm/power.c b/drivers/net/wireless/iwlwifi/mvm/power.c index 6c5dfc9a8d43..f5bdfb71c790 100644 --- a/drivers/net/wireless/iwlwifi/mvm/power.c +++ b/drivers/net/wireless/iwlwifi/mvm/power.c @@ -138,7 +138,8 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, /* Check skip over DTIM conditions */ if (!radar_detect && (dtimper <= 10) && - (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP)) { + (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP || + mvm->cur_ucode == IWL_UCODE_WOWLAN)) { cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK); cmd->num_skip_dtim = cpu_to_le32(2); } @@ -150,8 +151,13 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, keep_alive = DIV_ROUND_UP(keep_alive, MSEC_PER_SEC); cmd->keep_alive_seconds = keep_alive; - cmd->rx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC); - cmd->tx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC); + if (mvm->cur_ucode != IWL_UCODE_WOWLAN) { + cmd->rx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC); + cmd->tx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC); + } else { + cmd->rx_data_timeout = cpu_to_le32(10 * USEC_PER_MSEC); + cmd->tx_data_timeout = cpu_to_le32(10 * USEC_PER_MSEC); + } } int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -- cgit v1.2.3 From bf0fd5da8afdea8e1b1a2b42b8e9db068fbac96b Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 13 May 2013 17:05:27 +0300 Subject: iwlwifi: constify the source buffer of iwl_trans_write_mem Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/iwl-trans.h | 4 ++-- drivers/net/wireless/iwlwifi/pcie/trans.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 7a13790b5bfe..0470334d6a81 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -455,7 +455,7 @@ struct iwl_trans_ops { int (*read_mem)(struct iwl_trans *trans, u32 addr, void *buf, int dwords); int (*write_mem)(struct iwl_trans *trans, u32 addr, - void *buf, int dwords); + const void *buf, int dwords); void (*configure)(struct iwl_trans *trans, const struct iwl_trans_config *trans_cfg); void (*set_pmi)(struct iwl_trans *trans, bool state); @@ -761,7 +761,7 @@ static inline u32 iwl_trans_read_mem32(struct iwl_trans *trans, u32 addr) } static inline int iwl_trans_write_mem(struct iwl_trans *trans, u32 addr, - void *buf, int dwords) + const void *buf, int dwords) { return trans->ops->write_mem(trans, addr, buf, dwords); } diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index e5365196e5fe..a785f49df9b1 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -924,11 +924,11 @@ static int iwl_trans_pcie_read_mem(struct iwl_trans *trans, u32 addr, } static int iwl_trans_pcie_write_mem(struct iwl_trans *trans, u32 addr, - void *buf, int dwords) + const void *buf, int dwords) { unsigned long flags; int offs, ret = 0; - u32 *vals = buf; + const u32 *vals = buf; if (iwl_trans_grab_nic_access(trans, false, &flags)) { iwl_write32(trans, HBUS_TARG_MEM_WADDR, addr); -- cgit v1.2.3 From 82598b4f43312c01b976e02add9a5205e371f858 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 13 May 2013 21:44:42 +0200 Subject: iwlwifi: mvm: fix NVM parsing error path If NVM parsing fails and returns NULL, we continue in the code flow and eventually crash accessing the NULL pointer. Return an error from iwl_nvm_init() if the parsing failed. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/mvm/nvm.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c index b8ec02f89acc..c6c15f21065f 100644 --- a/drivers/net/wireless/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/iwlwifi/mvm/nvm.c @@ -231,8 +231,9 @@ int iwl_nvm_init(struct iwl_mvm *mvm) if (ret < 0) return ret; - ret = 0; mvm->nvm_data = iwl_parse_nvm_sections(mvm); + if (!mvm->nvm_data) + return -ENODATA; - return ret; + return 0; } -- cgit v1.2.3 From 0db53d005d004336be10807d07fc2cb271a53810 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 15 May 2013 13:05:25 +0200 Subject: iwlwifi: reverse DATA/INST section order As the new MVM firmware files package the DATA section first, and debugfs files want to access the DATA and not INST section, reverse the order here. This is only relevant for debugfs code that accesses the SRAM DATA section and uses this constant to access the section. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/iwl-fw.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h index c4c446d41eb0..f844d5c748c0 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw.h @@ -106,11 +106,14 @@ enum iwl_ucode_type { /* * enumeration of ucode section. - * This enumeration is used for legacy tlv style (before 16.0 uCode). + * This enumeration is used directly for older firmware (before 16.0). + * For new firmware, there can be up to 4 sections (see below) but the + * first one packaged into the firmware file is the DATA section and + * some debugging code accesses that. */ enum iwl_ucode_sec { - IWL_UCODE_SECTION_INST, IWL_UCODE_SECTION_DATA, + IWL_UCODE_SECTION_INST, }; /* * For 16.0 uCode and above, there is no differentiation between sections, -- cgit v1.2.3 From 07fd7d284dd01b46dea1986cb3bff20dfffe09bd Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 15 May 2013 14:38:25 +0200 Subject: iwlwifi: nvm: don't print NVM section reads by default These messages aren't really useful, suppress them unless EEPROM debugging is turned on. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/mvm/nvm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c index c6c15f21065f..ce464a5cca29 100644 --- a/drivers/net/wireless/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/iwlwifi/mvm/nvm.c @@ -177,7 +177,8 @@ static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section, offset += ret; } - IWL_INFO(mvm, "NVM section %d read completed\n", section); + IWL_DEBUG_EEPROM(mvm->trans->dev, + "NVM section %d read completed\n", section); return offset; } -- cgit v1.2.3 From 1214755c2bfc74b56093ccae797cb295e89d3400 Mon Sep 17 00:00:00 2001 From: Eran Harary Date: Thu, 9 May 2013 08:07:59 +0300 Subject: iwlwifi: support loading NVM data from file Some newer devices will be integrated into the platform more deeply and will not have embedded NVM (EEPROM/OTP). To support such devices the NVM data must be provided by the platform, allow loading the data via request_firmware() and then send it to the device as needed. Signed-off-by: Eran Harary Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/iwl-drv.c | 3 + drivers/net/wireless/iwlwifi/iwl-modparams.h | 1 + drivers/net/wireless/iwlwifi/mvm/nvm.c | 198 +++++++++++++++++++++++++-- 3 files changed, 194 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c index 39aad9893e0b..4f886133639a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/iwlwifi/iwl-drv.c @@ -1234,6 +1234,9 @@ MODULE_PARM_DESC(wd_disable, "Disable stuck queue watchdog timer 0=system default, " "1=disable, 2=enable (default: 0)"); +module_param_named(nvm_file, iwlwifi_mod_params.nvm_file, charp, S_IRUGO); +MODULE_PARM_DESC(nvm_file, "NVM file name"); + /* * set bt_coex_active to true, uCode will do kill/defer * every time the priority line is asserted (BT is sending signals on the diff --git a/drivers/net/wireless/iwlwifi/iwl-modparams.h b/drivers/net/wireless/iwlwifi/iwl-modparams.h index d6f6c37c09fd..36dfe0919f6b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-modparams.h +++ b/drivers/net/wireless/iwlwifi/iwl-modparams.h @@ -119,6 +119,7 @@ struct iwl_mod_params { int ant_coupling; bool bt_ch_announce; bool auto_agg; + char *nvm_file; }; #endif /* #__iwl_modparams_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c index ce464a5cca29..3f05c6b23874 100644 --- a/drivers/net/wireless/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/iwlwifi/mvm/nvm.c @@ -60,6 +60,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *****************************************************************************/ +#include #include "iwl-trans.h" #include "mvm.h" #include "iwl-eeprom-parse.h" @@ -75,20 +76,46 @@ static const int nvm_to_read[] = { }; /* Default NVM size to read */ -#define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024); +#define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024) +#define IWL_MAX_NVM_SECTION_SIZE 6000 -static inline void iwl_nvm_fill_read(struct iwl_nvm_access_cmd *cmd, - u16 offset, u16 length, u16 section) +#define NVM_WRITE_OPCODE 1 +#define NVM_READ_OPCODE 0 + +/* + * prepare the NVM host command w/ the pointers to the nvm buffer + * and send it to fw + */ +static int iwl_nvm_write_chunk(struct iwl_mvm *mvm, u16 section, + u16 offset, u16 length, const u8 *data) { - cmd->offset = cpu_to_le16(offset); - cmd->length = cpu_to_le16(length); - cmd->type = cpu_to_le16(section); + struct iwl_nvm_access_cmd nvm_access_cmd = { + .offset = cpu_to_le16(offset), + .length = cpu_to_le16(length), + .type = cpu_to_le16(section), + .op_code = NVM_WRITE_OPCODE, + }; + struct iwl_host_cmd cmd = { + .id = NVM_ACCESS_CMD, + .len = { sizeof(struct iwl_nvm_access_cmd), length }, + .flags = CMD_SYNC, + .data = { &nvm_access_cmd, data }, + /* data may come from vmalloc, so use _DUP */ + .dataflags = { 0, IWL_HCMD_DFL_DUP }, + }; + + return iwl_mvm_send_cmd(mvm, &cmd); } static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section, u16 offset, u16 length, u8 *data) { - struct iwl_nvm_access_cmd nvm_access_cmd = {}; + struct iwl_nvm_access_cmd nvm_access_cmd = { + .offset = cpu_to_le16(offset), + .length = cpu_to_le16(length), + .type = cpu_to_le16(section), + .op_code = NVM_READ_OPCODE, + }; struct iwl_nvm_access_resp *nvm_resp; struct iwl_rx_packet *pkt; struct iwl_host_cmd cmd = { @@ -99,7 +126,6 @@ static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section, int ret, bytes_read, offset_read; u8 *resp_data; - iwl_nvm_fill_read(&nvm_access_cmd, offset, length, section); cmd.len[0] = sizeof(struct iwl_nvm_access_cmd); ret = iwl_mvm_send_cmd(mvm, &cmd); @@ -144,6 +170,30 @@ exit: return ret; } +static int iwl_nvm_write_section(struct iwl_mvm *mvm, u16 section, + const u8 *data, u16 length) +{ + int offset = 0; + + /* copy data in chunks of 2k (and remainder if any) */ + + while (offset < length) { + int chunk_size, ret; + + chunk_size = min(IWL_NVM_DEFAULT_CHUNK_SIZE, + length - offset); + + ret = iwl_nvm_write_chunk(mvm, section, offset, + chunk_size, data + offset); + if (ret < 0) + return ret; + + offset += chunk_size; + } + + return 0; +} + /* * Reads an NVM section completely. * NICs prior to 7000 family doesn't have a real NVM, but just read @@ -204,11 +254,143 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm) return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib); } +#define MAX_NVM_FILE_LEN 16384 + +/* + * HOW TO CREATE THE NVM FILE FORMAT: + * ------------------------------ + * 1. create hex file, format: + * 3800 -> header + * 0000 -> header + * 5a40 -> data + * + * rev - 6 bit (word1) + * len - 10 bit (word1) + * id - 4 bit (word2) + * rsv - 12 bit (word2) + * + * 2. flip 8bits with 8 bits per line to get the right NVM file format + * + * 3. create binary file from the hex file + * + * 4. save as "iNVM_xxx.bin" under /lib/firmware + */ +static int iwl_mvm_load_external_nvm(struct iwl_mvm *mvm) +{ + int ret, section_id, section_size; + const struct firmware *fw_entry; + const struct { + __le16 word1; + __le16 word2; + u8 data[]; + } *file_sec; + const u8 *eof; + +#define NVM_WORD1_LEN(x) (8 * (x & 0x03FF)) +#define NVM_WORD2_ID(x) (x >> 12) + + /* + * Obtain NVM image via request_firmware. Since we already used + * request_firmware_nowait() for the firmware binary load and only + * get here after that we assume the NVM request can be satisfied + * synchronously. + */ + ret = request_firmware(&fw_entry, iwlwifi_mod_params.nvm_file, + mvm->trans->dev); + if (ret) { + IWL_ERR(mvm, "ERROR: %s isn't available %d\n", + iwlwifi_mod_params.nvm_file, ret); + return ret; + } + + IWL_INFO(mvm, "Loaded NVM file %s (%zu bytes)\n", + iwlwifi_mod_params.nvm_file, fw_entry->size); + + if (fw_entry->size < sizeof(*file_sec)) { + IWL_ERR(mvm, "NVM file too small\n"); + ret = -EINVAL; + goto out; + } + + if (fw_entry->size > MAX_NVM_FILE_LEN) { + IWL_ERR(mvm, "NVM file too large\n"); + ret = -EINVAL; + goto out; + } + + eof = fw_entry->data + fw_entry->size; + + file_sec = (void *)fw_entry->data; + + while (true) { + if (file_sec->data > eof) { + IWL_ERR(mvm, + "ERROR - NVM file too short for section header\n"); + ret = -EINVAL; + break; + } + + /* check for EOF marker */ + if (!file_sec->word1 && !file_sec->word2) { + ret = 0; + break; + } + + section_size = 2 * NVM_WORD1_LEN(le16_to_cpu(file_sec->word1)); + section_id = NVM_WORD2_ID(le16_to_cpu(file_sec->word2)); + + if (section_size > IWL_MAX_NVM_SECTION_SIZE) { + IWL_ERR(mvm, "ERROR - section too large (%d)\n", + section_size); + ret = -EINVAL; + break; + } + + if (!section_size) { + IWL_ERR(mvm, "ERROR - section empty\n"); + ret = -EINVAL; + break; + } + + if (file_sec->data + section_size > eof) { + IWL_ERR(mvm, + "ERROR - NVM file too short for section (%d bytes)\n", + section_size); + ret = -EINVAL; + break; + } + + ret = iwl_nvm_write_section(mvm, section_id, file_sec->data, + section_size); + if (ret < 0) { + IWL_ERR(mvm, "iwl_mvm_send_cmd failed: %d\n", ret); + break; + } + + /* advance to the next section */ + file_sec = (void *)(file_sec->data + section_size); + } +out: + release_firmware(fw_entry); + return ret; +} + int iwl_nvm_init(struct iwl_mvm *mvm) { int ret, i, section; u8 *nvm_buffer, *temp; + /* load external NVM if configured */ + if (iwlwifi_mod_params.nvm_file) { + /* move to External NVM flow */ + ret = iwl_mvm_load_external_nvm(mvm); + if (ret) + return ret; + } + + /* Read From FW NVM */ + IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from NVM\n"); + /* TODO: find correct NVM max size for a section */ nvm_buffer = kmalloc(mvm->cfg->base_params->eeprom_size, GFP_KERNEL); -- cgit v1.2.3 From 4f59334bb4a626b91ea31f2bb3515e6a586ea1ae Mon Sep 17 00:00:00 2001 From: Eran Harary Date: Mon, 13 May 2013 07:53:26 +0300 Subject: iwlwifi: fix initialisation while RF-kill is asserted If RF-kill is asserted while a device is initialized, the firmware INIT image can now be run to retrieve the NVM data and register to mac80211 properly. Previously, the initialisation would fail in this scenario and the driver wouldn't register with mac80211 at all, making the device unusable. Signed-off-by: Eran Harary Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/iwl-trans.h | 3 ++- drivers/net/wireless/iwlwifi/mvm/fw.c | 19 +++++++++++++++++++ drivers/net/wireless/iwlwifi/mvm/nvm.c | 4 ++-- drivers/net/wireless/iwlwifi/mvm/ops.c | 3 ++- drivers/net/wireless/iwlwifi/pcie/tx.c | 3 ++- 5 files changed, 27 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 0470334d6a81..84f1c8dc9741 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -189,7 +189,8 @@ enum CMD_MODE { CMD_SYNC = 0, CMD_ASYNC = BIT(0), CMD_WANT_SKB = BIT(1), - CMD_ON_DEMAND = BIT(2), + CMD_SEND_IN_RFKILL = BIT(2), + CMD_ON_DEMAND = BIT(3), }; #define DEF_CMD_PAYLOAD_SIZE 320 diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c index a4071cf09a24..20ee2812cc9b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/iwlwifi/mvm/fw.c @@ -326,6 +326,17 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) ret = iwl_nvm_check_version(mvm->nvm_data, mvm->trans); WARN_ON(ret); + /* + * abort after reading the nvm in case RF Kill is on, we will complete + * the init seq later when RF kill will switch to off + */ + if (test_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status)) { + IWL_DEBUG_RF_KILL(mvm, + "jump over all phy activities due to RF kill\n"); + iwl_remove_notification(&mvm->notif_wait, &calib_wait); + return 1; + } + /* Send TX valid antennas before triggering calibrations */ ret = iwl_send_tx_ant_cfg(mvm, iwl_fw_valid_tx_ant(mvm->fw)); if (ret) @@ -402,8 +413,16 @@ int iwl_mvm_up(struct iwl_mvm *mvm) ret = iwl_run_init_mvm_ucode(mvm, false); if (ret && !iwlmvm_mod_params.init_dbg) { IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", ret); + /* this can't happen */ + if (WARN_ON(ret > 0)) + ret = -ERFKILL; goto error; } + /* should stop & start HW since that INIT image just loaded */ + iwl_trans_stop_hw(mvm->trans, false); + ret = iwl_trans_start_hw(mvm->trans); + if (ret) + return ret; } if (iwlmvm_mod_params.init_dbg) diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c index 3f05c6b23874..2cd669c3c47a 100644 --- a/drivers/net/wireless/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/iwlwifi/mvm/nvm.c @@ -98,7 +98,7 @@ static int iwl_nvm_write_chunk(struct iwl_mvm *mvm, u16 section, struct iwl_host_cmd cmd = { .id = NVM_ACCESS_CMD, .len = { sizeof(struct iwl_nvm_access_cmd), length }, - .flags = CMD_SYNC, + .flags = CMD_SYNC | CMD_SEND_IN_RFKILL, .data = { &nvm_access_cmd, data }, /* data may come from vmalloc, so use _DUP */ .dataflags = { 0, IWL_HCMD_DFL_DUP }, @@ -120,7 +120,7 @@ static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section, struct iwl_rx_packet *pkt; struct iwl_host_cmd cmd = { .id = NVM_ACCESS_CMD, - .flags = CMD_SYNC | CMD_WANT_SKB, + .flags = CMD_SYNC | CMD_WANT_SKB | CMD_SEND_IN_RFKILL, .data = { &nvm_access_cmd, }, }; int ret, bytes_read, offset_read; diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index bb79a8dcf6bb..e3f69a08251d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -396,7 +396,8 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, mutex_lock(&mvm->mutex); err = iwl_run_init_mvm_ucode(mvm, true); mutex_unlock(&mvm->mutex); - if (err && !iwlmvm_mod_params.init_dbg) { + /* returns 0 if successful, 1 if success but in rfkill */ + if (err < 0 && !iwlmvm_mod_params.init_dbg) { IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", err); goto out_free; } diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index 595df1756b84..bf5f8246385e 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -1569,7 +1569,8 @@ int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) if (test_bit(STATUS_FW_ERROR, &trans_pcie->status)) return -EIO; - if (test_bit(STATUS_RFKILL, &trans_pcie->status)) { + if (!(cmd->flags & CMD_SEND_IN_RFKILL) && + test_bit(STATUS_RFKILL, &trans_pcie->status)) { IWL_DEBUG_RF_KILL(trans, "Dropping CMD 0x%x: RF KILL\n", cmd->id); return -ERFKILL; -- cgit v1.2.3 From 60191d99a9a472aef07d9e023f47a2503ae51f00 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 15 May 2013 13:08:51 +0200 Subject: iwlwifi: mvm: don't store section offset/length in debugfs When different images can (soon) be accessed through this file, storing the section offset/length on first access to the file breaks (or needs manual reset). Avoid this by not storing the offset/length values but using them locally in the function only. That way, the correct values are always used. While at it, correct the check that firmware is loaded. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/mvm/debugfs.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c index 0765827d87a6..95871b2d63d1 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c @@ -145,16 +145,18 @@ static ssize_t iwl_dbgfs_sram_read(struct file *file, char __user *user_buf, char *buf; u8 *ptr; + if (!mvm->ucode_loaded) + return -EINVAL; + /* default is to dump the entire data segment */ if (!mvm->dbgfs_sram_offset && !mvm->dbgfs_sram_len) { - if (!mvm->ucode_loaded) - return -EINVAL; img = &mvm->fw->img[mvm->cur_ucode]; - mvm->dbgfs_sram_offset = - img->sec[IWL_UCODE_SECTION_DATA].offset; - mvm->dbgfs_sram_len = img->sec[IWL_UCODE_SECTION_DATA].len; + ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; + len = img->sec[IWL_UCODE_SECTION_DATA].len; + } else { + ofs = mvm->dbgfs_sram_offset; + len = mvm->dbgfs_sram_len; } - len = mvm->dbgfs_sram_len; bufsz = len * 4 + 256; buf = kzalloc(bufsz, GFP_KERNEL); @@ -168,12 +170,9 @@ static ssize_t iwl_dbgfs_sram_read(struct file *file, char __user *user_buf, } pos += scnprintf(buf + pos, bufsz - pos, "sram_len: 0x%x\n", len); - pos += scnprintf(buf + pos, bufsz - pos, "sram_offset: 0x%x\n", - mvm->dbgfs_sram_offset); + pos += scnprintf(buf + pos, bufsz - pos, "sram_offset: 0x%x\n", ofs); - iwl_trans_read_mem_bytes(mvm->trans, - mvm->dbgfs_sram_offset, - ptr, len); + iwl_trans_read_mem_bytes(mvm->trans, ofs, ptr, len); for (ofs = 0; ofs < len; ofs += 16) { pos += scnprintf(buf + pos, bufsz - pos, "0x%.4x ", ofs); hex_dump_to_buffer(ptr + ofs, 16, 16, 1, buf + pos, -- cgit v1.2.3 From dfcb4c3aacedee6838e436fb575b31e138505203 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 15 May 2013 11:44:49 +0200 Subject: iwlwifi: mvm: adjust firmware D3 configuration API The D3 firmware API changed to include a new field, adjust the driver to it to avoid getting an NMI when configuring. Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h index 51e015d1dfb2..6f8b2c16ae17 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h @@ -75,13 +75,15 @@ enum iwl_d3_wakeup_flags { * struct iwl_d3_manager_config - D3 manager configuration command * @min_sleep_time: minimum sleep time (in usec) * @wakeup_flags: wakeup flags, see &enum iwl_d3_wakeup_flags + * @wakeup_host_timer: force wakeup after this many seconds * * The structure is used for the D3_CONFIG_CMD command. */ struct iwl_d3_manager_config { __le32 min_sleep_time; __le32 wakeup_flags; -} __packed; /* D3_MANAGER_CONFIG_CMD_S_VER_3 */ + __le32 wakeup_host_timer; +} __packed; /* D3_MANAGER_CONFIG_CMD_S_VER_4 */ /* TODO: OFFLOADS_QUERY_API_S_VER_1 */ -- cgit v1.2.3 From d2cf43674e17ca1c16c68d46d987d2f17bf7c371 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 15 May 2013 19:25:55 +0000 Subject: tcp: speedup tcp_fixup_rcvbuf() tcp_fixup_rcvbuf() contains a loop to estimate initial socket rcv space needed for a given mss. With large MTU (like 64K on lo), we can loop ~500 times and consume a lot of cpu cycles. perf top of 200 concurrent netperf -t TCP_CRR 5.62% netperf [kernel.kallsyms] [k] tcp_init_buffer_space 1.71% netperf [kernel.kallsyms] [k] _raw_spin_lock 1.55% netperf [kernel.kallsyms] [k] kmem_cache_free 1.51% netperf [kernel.kallsyms] [k] tcp_transmit_skb 1.50% netperf [kernel.kallsyms] [k] tcp_ack Lets use a 100% factor, and remove the loop. 100% is needed anyway for tcp_adv_win_scale=1 default value, and is also the maximum factor. Refs: commit b49960a05e32 ("tcp: change tcp_adv_win_scale and tcp_rmem[2]") Signed-off-by: Eric Dumazet Cc: Neal Cardwell Cc: Yuchung Cheng Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- net/ipv4/tcp_input.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 08bbe6096528..b358e8c98607 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -360,9 +360,7 @@ static void tcp_fixup_rcvbuf(struct sock *sk) if (mss > 1460) icwnd = max_t(u32, (1460 * TCP_DEFAULT_INIT_RCVWND) / mss, 2); - rcvmem = SKB_TRUESIZE(mss + MAX_TCP_HEADER); - while (tcp_win_from_space(rcvmem) < mss) - rcvmem += 128; + rcvmem = 2 * SKB_TRUESIZE(mss + MAX_TCP_HEADER); rcvmem *= icwnd; -- cgit v1.2.3 From d6a98c9680a6f885715ce64daa5de73b33d3edd4 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 16 May 2013 01:15:41 +0000 Subject: drivers/net/ethernet/renesas: don't check resource with devm_ioremap_resource devm_ioremap_resource does sanity checks on the given resource. No need to duplicate this in the driver. Signed-off-by: Wolfram Sang Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/sh_eth.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 33dc6f2418f2..42e9dd05c936 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -2745,11 +2745,6 @@ static int sh_eth_drv_probe(struct platform_device *pdev) if (mdp->cd->tsu) { struct resource *rtsu; rtsu = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!rtsu) { - dev_err(&pdev->dev, "Not found TSU resource\n"); - ret = -ENODEV; - goto out_release; - } mdp->tsu_addr = devm_ioremap_resource(&pdev->dev, rtsu); if (IS_ERR(mdp->tsu_addr)) { ret = PTR_ERR(mdp->tsu_addr); -- cgit v1.2.3 From ed8a83a1f4655a31e0080a19f08d896bb29434a9 Mon Sep 17 00:00:00 2001 From: "govindarajulu.v" Date: Thu, 16 May 2013 06:24:41 +0000 Subject: net: 3com: 3c509: remove unnecessary code This patch removes unnecessary #if 0 code from 3c509.c Signed-off-by: govindarajulu.v Signed-off-by: David S. Miller --- drivers/net/ethernet/3com/3c509.c | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/drivers/net/ethernet/3com/3c509.c b/drivers/net/ethernet/3com/3c509.c index adb4bf5eb4b4..ede8daa68275 100644 --- a/drivers/net/ethernet/3com/3c509.c +++ b/drivers/net/ethernet/3com/3c509.c @@ -723,25 +723,6 @@ el3_start_xmit(struct sk_buff *skb, struct net_device *dev) pr_debug("%s: el3_start_xmit(length = %u) called, status %4.4x.\n", dev->name, skb->len, inw(ioaddr + EL3_STATUS)); } -#if 0 -#ifndef final_version - { /* Error-checking code, delete someday. */ - ushort status = inw(ioaddr + EL3_STATUS); - if (status & 0x0001 && /* IRQ line active, missed one. */ - inw(ioaddr + EL3_STATUS) & 1) { /* Make sure. */ - pr_debug("%s: Missed interrupt, status then %04x now %04x" - " Tx %2.2x Rx %4.4x.\n", dev->name, status, - inw(ioaddr + EL3_STATUS), inb(ioaddr + TX_STATUS), - inw(ioaddr + RX_STATUS)); - /* Fake interrupt trigger by masking, acknowledge interrupts. */ - outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD); - outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq, - ioaddr + EL3_CMD); - outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD); - } - } -#endif -#endif /* * We lock the driver against other processors. Note * we don't need to lock versus the IRQ as we suspended -- cgit v1.2.3 From e44007e0f97fdae45b73cf61e9962493ddcc6114 Mon Sep 17 00:00:00 2001 From: "Chew, Chiau Ee" Date: Thu, 16 May 2013 15:36:12 +0800 Subject: ALSA: hda - add PCI IDs for Intel BayTrail Add HD Audio Device PCI ID for the Intel BayTrail platform. Signed-off-by: Chew, Chiau Ee Signed-off-by: Artem Bityutskiy Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index de18722c4873..ac75975a4276 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -3878,6 +3878,9 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = { /* Oaktrail */ { PCI_DEVICE(0x8086, 0x080a), .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM }, + /* BayTrail */ + { PCI_DEVICE(0x8086, 0x0f04), + .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM }, /* ICH */ { PCI_DEVICE(0x8086, 0x2668), .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC | -- cgit v1.2.3 From fd5f940f82cdc0132438a96559b27dd7fd574875 Mon Sep 17 00:00:00 2001 From: Andrew Bresticker Date: Thu, 16 May 2013 12:03:54 -0700 Subject: ASoC: max98090: add digital mic mux to record path The max98090 driver currently treats the digital mic enable as a supply on the record path, causing the digital mic enable to always be turned on when attempting to record. This is incorrect, however, since the digital mic enable is also a mux control where 0 selects the ADC output as input to the record-path DSP and 1 selects the digital mic. This patch adds a virtual DMIC mux to the reocrd path so that we can switch between the ADC and the digital mic for recording. Signed-off-by: Andrew Bresticker Signed-off-by: Mark Brown --- sound/soc/codecs/max98090.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index ce0d36412c97..cbb272b1f73d 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -857,6 +857,14 @@ static const struct soc_enum mic2_mux_enum = static const struct snd_kcontrol_new max98090_mic2_mux = SOC_DAPM_ENUM("MIC2 Mux", mic2_mux_enum); +static const char *dmic_mux_text[] = { "ADC", "DMIC" }; + +static const struct soc_enum dmic_mux_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(dmic_mux_text), dmic_mux_text); + +static const struct snd_kcontrol_new max98090_dmic_mux = + SOC_DAPM_ENUM_VIRT("DMIC Mux", dmic_mux_enum); + static const char *max98090_micpre_text[] = { "Off", "On" }; static const struct soc_enum max98090_pa1en_enum = @@ -1144,6 +1152,9 @@ static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = { SND_SOC_DAPM_MUX("MIC2 Mux", SND_SOC_NOPM, 0, 0, &max98090_mic2_mux), + SND_SOC_DAPM_VIRT_MUX("DMIC Mux", SND_SOC_NOPM, + 0, 0, &max98090_dmic_mux), + SND_SOC_DAPM_PGA_E("MIC1 Input", M98090_REG_MIC1_INPUT_LEVEL, M98090_MIC_PA1EN_SHIFT, 0, NULL, 0, max98090_micinput_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), @@ -1336,11 +1347,14 @@ static const struct snd_soc_dapm_route max98090_dapm_routes[] = { {"ADCL", NULL, "SHDN"}, {"ADCR", NULL, "SHDN"}, - {"LBENL Mux", "Normal", "ADCL"}, - {"LBENL Mux", "Normal", "DMICL"}, + {"DMIC Mux", "ADC", "ADCL"}, + {"DMIC Mux", "ADC", "ADCR"}, + {"DMIC Mux", "DMIC", "DMICL"}, + {"DMIC Mux", "DMIC", "DMICR"}, + + {"LBENL Mux", "Normal", "DMIC Mux"}, {"LBENL Mux", "Loopback", "LTENL Mux"}, - {"LBENR Mux", "Normal", "ADCR"}, - {"LBENR Mux", "Normal", "DMICR"}, + {"LBENR Mux", "Normal", "DMIC Mux"}, {"LBENR Mux", "Loopback", "LTENR Mux"}, {"AIFOUTL", NULL, "LBENL Mux"}, -- cgit v1.2.3 From 7d0cd22382f80243e7fce16f6bfc0720d5688370 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 17 May 2013 11:26:15 +0200 Subject: ASoC: simplify registration of snd-soc-dummy device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Uwe Kleine-König Signed-off-by: Mark Brown --- sound/soc/soc-utils.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c index 4b3be6c3c91e..29b211e9c060 100644 --- a/sound/soc/soc-utils.c +++ b/sound/soc/soc-utils.c @@ -159,15 +159,10 @@ int __init snd_soc_util_init(void) { int ret; - soc_dummy_dev = platform_device_alloc("snd-soc-dummy", -1); - if (!soc_dummy_dev) - return -ENOMEM; - - ret = platform_device_add(soc_dummy_dev); - if (ret != 0) { - platform_device_put(soc_dummy_dev); - return ret; - } + soc_dummy_dev = + platform_device_register_simple("snd-soc-dummy", -1, NULL, 0); + if (IS_ERR(soc_dummy_dev)) + return PTR_ERR(soc_dummy_dev); ret = platform_driver_register(&soc_dummy_driver); if (ret != 0) -- cgit v1.2.3 From ca76ceb8b9ca1466be9b6de5e4c0fb19b37417ee Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 9 Apr 2013 16:04:35 +0100 Subject: mfd: arizona: Read the device identification information after boot Future devices may not fully report the device identification information until their boot sequence is complete so defer acting on these until that has finished. Signed-off-by: Mark Brown --- drivers/mfd/arizona-core.c | 80 ++++++++++++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 31 deletions(-) diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 6ab03043fd60..81907f9fc92e 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -536,51 +536,22 @@ int arizona_dev_init(struct arizona *arizona) regcache_cache_only(arizona->regmap, false); + /* Verify that this is a chip we know about */ ret = regmap_read(arizona->regmap, ARIZONA_SOFTWARE_RESET, ®); if (ret != 0) { dev_err(dev, "Failed to read ID register: %d\n", ret); goto err_reset; } - ret = regmap_read(arizona->regmap, ARIZONA_DEVICE_REVISION, - &arizona->rev); - if (ret != 0) { - dev_err(dev, "Failed to read revision register: %d\n", ret); - goto err_reset; - } - arizona->rev &= ARIZONA_DEVICE_REVISION_MASK; - switch (reg) { -#ifdef CONFIG_MFD_WM5102 case 0x5102: - type_name = "WM5102"; - if (arizona->type != WM5102) { - dev_err(arizona->dev, "WM5102 registered as %d\n", - arizona->type); - arizona->type = WM5102; - } - apply_patch = wm5102_patch; - arizona->rev &= 0x7; - break; -#endif -#ifdef CONFIG_MFD_WM5110 case 0x5110: - type_name = "WM5110"; - if (arizona->type != WM5110) { - dev_err(arizona->dev, "WM5110 registered as %d\n", - arizona->type); - arizona->type = WM5110; - } - apply_patch = wm5110_patch; break; -#endif default: - dev_err(arizona->dev, "Unknown device ID %x\n", reg); + dev_err(arizona->dev, "Unknown device ID: %x\n", reg); goto err_reset; } - dev_info(dev, "%s revision %c\n", type_name, arizona->rev + 'A'); - /* If we have a /RESET GPIO we'll already be reset */ if (!arizona->pdata.reset) { regcache_mark_dirty(arizona->regmap); @@ -600,6 +571,7 @@ int arizona_dev_init(struct arizona *arizona) } } + /* Ensure device startup is complete */ switch (arizona->type) { case WM5102: ret = regmap_read(arizona->regmap, 0x19, &val); @@ -620,6 +592,52 @@ int arizona_dev_init(struct arizona *arizona) break; } + /* Read the device ID information & do device specific stuff */ + ret = regmap_read(arizona->regmap, ARIZONA_SOFTWARE_RESET, ®); + if (ret != 0) { + dev_err(dev, "Failed to read ID register: %d\n", ret); + goto err_reset; + } + + ret = regmap_read(arizona->regmap, ARIZONA_DEVICE_REVISION, + &arizona->rev); + if (ret != 0) { + dev_err(dev, "Failed to read revision register: %d\n", ret); + goto err_reset; + } + arizona->rev &= ARIZONA_DEVICE_REVISION_MASK; + + switch (reg) { +#ifdef CONFIG_MFD_WM5102 + case 0x5102: + type_name = "WM5102"; + if (arizona->type != WM5102) { + dev_err(arizona->dev, "WM5102 registered as %d\n", + arizona->type); + arizona->type = WM5102; + } + apply_patch = wm5102_patch; + arizona->rev &= 0x7; + break; +#endif +#ifdef CONFIG_MFD_WM5110 + case 0x5110: + type_name = "WM5110"; + if (arizona->type != WM5110) { + dev_err(arizona->dev, "WM5110 registered as %d\n", + arizona->type); + arizona->type = WM5110; + } + apply_patch = wm5110_patch; + break; +#endif + default: + dev_err(arizona->dev, "Unknown device ID %x\n", reg); + goto err_reset; + } + + dev_info(dev, "%s revision %c\n", type_name, arizona->rev + 'A'); + if (apply_patch) { ret = apply_patch(arizona); if (ret != 0) { -- cgit v1.2.3 From d9d03496f6f904a3588bdb8b215853bc4e50132c Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 26 Mar 2013 18:01:49 +0000 Subject: mfd: wm5102: Manually apply register patch Future updates will require us to manually apply the register patch for wm5102. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/mfd/arizona-core.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 81907f9fc92e..25bfd1ec1a65 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -368,6 +368,18 @@ static int arizona_runtime_resume(struct device *dev) break; } + switch (arizona->type) { + case WM5102: + ret = wm5102_patch(arizona); + if (ret != 0) { + dev_err(arizona->dev, "Failed to apply patch: %d\n", + ret); + goto err; + } + default: + break; + } + ret = regcache_sync(arizona->regmap); if (ret != 0) { dev_err(arizona->dev, "Failed to restore register cache\n"); -- cgit v1.2.3 From 1d017b6b36675574ec8a6f7dbcd3fd3bec2dc03f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 26 Mar 2013 12:16:26 +0000 Subject: mfd: arizona: Add missing cleanup on remove We'd forgotten to disable /RESET or the regulators. Practically speaking this code is unlikely to ever be run. Signed-off-by: Mark Brown --- drivers/mfd/arizona-core.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 25bfd1ec1a65..8002e2d2f6fe 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -839,6 +839,11 @@ int arizona_dev_exit(struct arizona *arizona) arizona_free_irq(arizona, ARIZONA_IRQ_CLKGEN_ERR, arizona); pm_runtime_disable(arizona->dev); arizona_irq_exit(arizona); + if (arizona->pdata.reset) + gpio_set_value_cansleep(arizona->pdata.reset, 0); + regulator_disable(arizona->dcvdd); + regulator_bulk_disable(ARRAY_SIZE(arizona->core_supplies), + arizona->core_supplies); return 0; } EXPORT_SYMBOL_GPL(arizona_dev_exit); -- cgit v1.2.3 From 67c992969172473e129984a51ceb77950a2aa16c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 10 Apr 2013 12:40:26 +0100 Subject: mfd: arizona: Disable interrupts during suspend We aren't able to handle interrupts after the device has suspended since we need to runtime resume it in order to do so but the controller may not be available any more. Handle this in the same way as we handle a similar issue on resume. Reported-by: Chuansheng Liu Signed-off-by: Mark Brown --- drivers/mfd/arizona-core.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 8002e2d2f6fe..549db0ad7257 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -409,6 +409,26 @@ static int arizona_runtime_suspend(struct device *dev) #endif #ifdef CONFIG_PM_SLEEP +static int arizona_suspend(struct device *dev) +{ + struct arizona *arizona = dev_get_drvdata(dev); + + dev_dbg(arizona->dev, "Suspend, disabling IRQ\n"); + disable_irq(arizona->irq); + + return 0; +} + +static int arizona_suspend_late(struct device *dev) +{ + struct arizona *arizona = dev_get_drvdata(dev); + + dev_dbg(arizona->dev, "Late suspend, reenabling IRQ\n"); + enable_irq(arizona->irq); + + return 0; +} + static int arizona_resume_noirq(struct device *dev) { struct arizona *arizona = dev_get_drvdata(dev); @@ -434,8 +454,9 @@ const struct dev_pm_ops arizona_pm_ops = { SET_RUNTIME_PM_OPS(arizona_runtime_suspend, arizona_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(NULL, arizona_resume) + SET_SYSTEM_SLEEP_PM_OPS(arizona_suspend, arizona_resume) #ifdef CONFIG_PM_SLEEP + .suspend_late = arizona_suspend_late, .resume_noirq = arizona_resume_noirq, #endif }; -- cgit v1.2.3 From c0ff68f1611d6855a06d672989ad5cfea160a4eb Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Mon, 29 Apr 2013 14:15:51 +0200 Subject: kbuild: fix make headers_install when path is too long If headers_install is executed from a deep/long directory structure, the shell's maximum argument length can be execeeded, which breaks the operation with: | make[2]: execvp: /bin/sh: Argument list too long | make[2]: *** Instead of passing each files name with the entire path, I give only the file name without the source path and give this path as a new argument to headers_install.pl. Because there is three possible paths, I have tree input-files list, one per path. Signed-off-by: Nicolas Dichtel Tested-by: Bruce Ashfield Signed-off-by: Michal Marek --- scripts/Makefile.headersinst | 20 ++++++++++++++------ scripts/headers_install.sh | 7 +++++-- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/scripts/Makefile.headersinst b/scripts/Makefile.headersinst index 182084d728c8..8ccf83056a7a 100644 --- a/scripts/Makefile.headersinst +++ b/scripts/Makefile.headersinst @@ -47,18 +47,24 @@ header-y := $(filter-out $(generic-y), $(header-y)) all-files := $(header-y) $(genhdr-y) $(wrapper-files) output-files := $(addprefix $(installdir)/, $(all-files)) -input-files := $(foreach hdr, $(header-y), \ +input-files1 := $(foreach hdr, $(header-y), \ $(if $(wildcard $(srcdir)/$(hdr)), \ - $(wildcard $(srcdir)/$(hdr)), \ + $(wildcard $(srcdir)/$(hdr))) \ + ) +input-files1-name := $(notdir $(input-files1)) +input-files2 := $(foreach hdr, $(header-y), \ + $(if $(wildcard $(srcdir)/$(hdr)),, \ $(if $(wildcard $(oldsrcdir)/$(hdr)), \ $(wildcard $(oldsrcdir)/$(hdr)), \ $(error Missing UAPI file $(srcdir)/$(hdr))) \ - )) \ - $(foreach hdr, $(genhdr-y), \ + )) +input-files2-name := $(notdir $(input-files2)) +input-files3 := $(foreach hdr, $(genhdr-y), \ $(if $(wildcard $(gendir)/$(hdr)), \ $(wildcard $(gendir)/$(hdr)), \ $(error Missing generated UAPI file $(gendir)/$(hdr)) \ )) +input-files3-name := $(notdir $(input-files3)) # Work out what needs to be removed oldheaders := $(patsubst $(installdir)/%,%,$(wildcard $(installdir)/*.h)) @@ -72,7 +78,9 @@ printdir = $(patsubst $(INSTALL_HDR_PATH)/%/,%,$(dir $@)) quiet_cmd_install = INSTALL $(printdir) ($(words $(all-files))\ file$(if $(word 2, $(all-files)),s)) cmd_install = \ - $(CONFIG_SHELL) $< $(installdir) $(input-files); \ + $(CONFIG_SHELL) $< $(installdir) $(srcdir) $(input-files1-name); \ + $(CONFIG_SHELL) $< $(installdir) $(oldsrcdir) $(input-files2-name); \ + $(CONFIG_SHELL) $< $(installdir) $(gendir) $(input-files3-name); \ for F in $(wrapper-files); do \ echo "\#include " > $(installdir)/$$F; \ done; \ @@ -98,7 +106,7 @@ __headersinst: $(subdirs) $(install-file) @: targets += $(install-file) -$(install-file): scripts/headers_install.sh $(input-files) FORCE +$(install-file): scripts/headers_install.sh $(input-files1) $(input-files2) $(input-files3) FORCE $(if $(unwanted),$(call cmd,remove),) $(if $(wildcard $(dir $@)),,$(shell mkdir -p $(dir $@))) $(call if_changed,install) diff --git a/scripts/headers_install.sh b/scripts/headers_install.sh index 643764f53ea7..5de5660cb708 100644 --- a/scripts/headers_install.sh +++ b/scripts/headers_install.sh @@ -2,7 +2,7 @@ if [ $# -lt 1 ] then - echo "Usage: headers_install.sh OUTDIR [FILES...] + echo "Usage: headers_install.sh OUTDIR SRCDIR [FILES...] echo echo "Prepares kernel header files for use by user space, by removing" echo "all compiler.h definitions and #includes, removing any" @@ -10,6 +10,7 @@ then echo "asm/inline/volatile keywords." echo echo "OUTDIR: directory to write each userspace header FILE to." + echo "SRCDIR: source directory where files are picked." echo "FILES: list of header files to operate on." exit 1 @@ -19,6 +20,8 @@ fi OUTDIR="$1" shift +SRCDIR="$1" +shift # Iterate through files listed on command line @@ -34,7 +37,7 @@ do -e 's/(^|[^a-zA-Z0-9])__packed([^a-zA-Z0-9_]|$)/\1__attribute__((packed))\2/g' \ -e 's/(^|[ \t(])(inline|asm|volatile)([ \t(]|$)/\1__\2__\3/g' \ -e 's@#(ifndef|define|endif[ \t]*/[*])[ \t]*_UAPI@#\1 @' \ - "$i" > "$OUTDIR/$FILE.sed" || exit 1 + "$SRCDIR/$i" > "$OUTDIR/$FILE.sed" || exit 1 scripts/unifdef -U__KERNEL__ -D__EXPORTED_HEADERS__ "$OUTDIR/$FILE.sed" \ > "$OUTDIR/$FILE" [ $? -gt 1 ] && exit 1 -- cgit v1.2.3 From bfd428daaf619d671f6463bd8c7e4df8107e4775 Mon Sep 17 00:00:00 2001 From: Emilio López Date: Fri, 17 May 2013 10:42:53 +0000 Subject: net: ethernet: sun: initialize variables directly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clean up the code a bit to initialize the variables directly when defining them. Signed-off-by: Emilio López Signed-off-by: David S. Miller --- drivers/net/ethernet/sun/sunbmac.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/net/ethernet/sun/sunbmac.c b/drivers/net/ethernet/sun/sunbmac.c index 054975939a18..09b4f8c0b199 100644 --- a/drivers/net/ethernet/sun/sunbmac.c +++ b/drivers/net/ethernet/sun/sunbmac.c @@ -1017,10 +1017,7 @@ static void bigmac_set_multicast(struct net_device *dev) tmp |= BIGMAC_RXCFG_PMISC; sbus_writel(tmp, bregs + BMAC_RXCFG); } else { - u16 hash_table[4]; - - for (i = 0; i < 4; i++) - hash_table[i] = 0; + u16 hash_table[4] = { 0 }; netdev_for_each_mc_addr(ha, dev) { crc = ether_crc_le(6, ha->addr); -- cgit v1.2.3 From 3b0aaef800c0949198fb6304adc7ff1ae7413f02 Mon Sep 17 00:00:00 2001 From: Emilio López Date: Fri, 17 May 2013 10:42:54 +0000 Subject: net: ethernet: apple: initialize variables directly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clean up the code a bit to initialize the variables directly when defining them. Signed-off-by: Emilio López Signed-off-by: David S. Miller --- drivers/net/ethernet/apple/bmac.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/net/ethernet/apple/bmac.c b/drivers/net/ethernet/apple/bmac.c index f36bbd6d5085..714dcfe3a469 100644 --- a/drivers/net/ethernet/apple/bmac.c +++ b/drivers/net/ethernet/apple/bmac.c @@ -1030,14 +1030,12 @@ static void bmac_set_multicast(struct net_device *dev) rx_cfg |= RxPromiscEnable; bmwrite(dev, RXCFG, rx_cfg); } else { - u16 hash_table[4]; + u16 hash_table[4] = { 0 }; rx_cfg = bmread(dev, RXCFG); rx_cfg &= ~RxPromiscEnable; bmwrite(dev, RXCFG, rx_cfg); - for(i = 0; i < 4; i++) hash_table[i] = 0; - netdev_for_each_mc_addr(ha, dev) { crc = ether_crc_le(6, ha->addr); crc >>= 26; -- cgit v1.2.3 From 35e729ac5d5c1a164aee475583cb9830e9707b61 Mon Sep 17 00:00:00 2001 From: Emilio López Date: Fri, 17 May 2013 10:42:55 +0000 Subject: net: ethernet: davicom: dm9000: initialize variables directly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clean up the code a bit to initialize the variables directly when defining them. Signed-off-by: Emilio López Signed-off-by: David S. Miller --- drivers/net/ethernet/davicom/dm9000.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/net/ethernet/davicom/dm9000.c b/drivers/net/ethernet/davicom/dm9000.c index 9105465b2a1a..a2408c84bbdd 100644 --- a/drivers/net/ethernet/davicom/dm9000.c +++ b/drivers/net/ethernet/davicom/dm9000.c @@ -827,7 +827,7 @@ dm9000_hash_table_unlocked(struct net_device *dev) struct netdev_hw_addr *ha; int i, oft; u32 hash_val; - u16 hash_table[4]; + u16 hash_table[4] = { 0, 0, 0, 0x8000 }; /* broadcast address */ u8 rcr = RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN; dm9000_dbg(db, 1, "entering %s\n", __func__); @@ -835,13 +835,6 @@ dm9000_hash_table_unlocked(struct net_device *dev) for (i = 0, oft = DM9000_PAR; i < 6; i++, oft++) iow(db, oft, dev->dev_addr[i]); - /* Clear Hash Table */ - for (i = 0; i < 4; i++) - hash_table[i] = 0x0; - - /* broadcast address */ - hash_table[3] = 0x8000; - if (dev->flags & IFF_PROMISC) rcr |= RCR_PRMSC; -- cgit v1.2.3 From e998fd413e5e7dac750dfe79c79cbf8f417d41b7 Mon Sep 17 00:00:00 2001 From: Emilio López Date: Fri, 17 May 2013 10:42:56 +0000 Subject: net: ethernet: korina: initialize variables directly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clean up the code a bit to initialize the variables directly when defining them. Signed-off-by: Emilio López Signed-off-by: David S. Miller --- drivers/net/ethernet/korina.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/net/ethernet/korina.c b/drivers/net/ethernet/korina.c index 5409fe876a44..0b5708520121 100644 --- a/drivers/net/ethernet/korina.c +++ b/drivers/net/ethernet/korina.c @@ -495,12 +495,9 @@ static void korina_multicast_list(struct net_device *dev) /* Build the hash table */ if (netdev_mc_count(dev) > 4) { - u16 hash_table[4]; + u16 hash_table[4] = { 0 }; u32 crc; - for (i = 0; i < 4; i++) - hash_table[i] = 0; - netdev_for_each_mc_addr(ha, dev) { crc = ether_crc_le(6, ha->addr); crc >>= 26; -- cgit v1.2.3 From 553675fb5e9ce3d71aa6cb527f98cd34793c0dbc Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Thu, 16 May 2013 11:35:20 +0000 Subject: vxlan: listen on multiple ports The commit 823aa873bc782f1c51b1ce8ec6da7cfcaf93836e Author: stephen hemminger Date: Sat Apr 27 11:31:57 2013 +0000 vxlan: allow choosing destination port per vxlan introduced per-vxlan UDP port configuration but only did half of the necessary work. It added per vxlan destination for sending, but overlooked the handling of multiple ports for incoming traffic. This patch changes the listening port management to handle multiple incoming UDP ports. The earlier per-namespace structure is now a hash list per namespace. It is also now possible to define the same virtual network id but with different UDP port values which can be useful for migration. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/vxlan.c | 263 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 181 insertions(+), 82 deletions(-) diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index ba81f3c39a83..80d359c6310b 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -44,6 +44,8 @@ #define VXLAN_VERSION "0.1" +#define PORT_HASH_BITS 8 +#define PORT_HASH_SIZE (1<vni_list[hash_32(id, VNI_HASH_BITS)]; +} + +/* Socket hash table head */ +static inline struct hlist_head *vs_head(struct net *net, __be16 port) { struct vxlan_net *vn = net_generic(net, vxlan_net_id); - return &vn->vni_list[hash_32(id, VNI_HASH_BITS)]; + return &vn->sock_list[hash_32(ntohs(port), PORT_HASH_BITS)]; +} + +/* Find VXLAN socket based on network namespace and UDP port */ +static struct vxlan_sock *vxlan_find_port(struct net *net, __be16 port) +{ + struct vxlan_sock *vs; + + hlist_for_each_entry_rcu(vs, vs_head(net, port), hlist) { + if (inet_sk(vs->sock->sk)->inet_sport == port) + return vs; + } + return NULL; } /* Look up VNI in a per net namespace table */ -static struct vxlan_dev *vxlan_find_vni(struct net *net, u32 id) +static struct vxlan_dev *vxlan_find_vni(struct net *net, u32 id, __be16 port) { + struct vxlan_sock *vs; struct vxlan_dev *vxlan; - hlist_for_each_entry_rcu(vxlan, vni_head(net, id), hlist) { + vs = vxlan_find_port(net, port); + if (!vs) + return NULL; + + hlist_for_each_entry_rcu(vxlan, vni_head(vs, id), hlist) { if (vxlan->default_dst.remote_vni == id) return vxlan; } @@ -592,20 +631,18 @@ static void vxlan_snoop(struct net_device *dev, static bool vxlan_group_used(struct vxlan_net *vn, const struct vxlan_dev *this) { - const struct vxlan_dev *vxlan; - unsigned h; + struct vxlan_dev *vxlan; - for (h = 0; h < VNI_HASH_SIZE; ++h) - hlist_for_each_entry(vxlan, &vn->vni_list[h], hlist) { - if (vxlan == this) - continue; + list_for_each_entry(vxlan, &vn->vxlan_list, next) { + if (vxlan == this) + continue; - if (!netif_running(vxlan->dev)) - continue; + if (!netif_running(vxlan->dev)) + continue; - if (vxlan->default_dst.remote_ip == this->default_dst.remote_ip) - return true; - } + if (vxlan->default_dst.remote_ip == this->default_dst.remote_ip) + return true; + } return false; } @@ -615,7 +652,7 @@ static int vxlan_join_group(struct net_device *dev) { struct vxlan_dev *vxlan = netdev_priv(dev); struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id); - struct sock *sk = vn->sock->sk; + struct sock *sk = vxlan->vn_sock->sock->sk; struct ip_mreqn mreq = { .imr_multiaddr.s_addr = vxlan->default_dst.remote_ip, .imr_ifindex = vxlan->default_dst.remote_ifindex, @@ -643,7 +680,7 @@ static int vxlan_leave_group(struct net_device *dev) struct vxlan_dev *vxlan = netdev_priv(dev); struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id); int err = 0; - struct sock *sk = vn->sock->sk; + struct sock *sk = vxlan->vn_sock->sock->sk; struct ip_mreqn mreq = { .imr_multiaddr.s_addr = vxlan->default_dst.remote_ip, .imr_ifindex = vxlan->default_dst.remote_ifindex, @@ -670,6 +707,7 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb) struct vxlanhdr *vxh; struct vxlan_dev *vxlan; struct pcpu_tstats *stats; + __be16 port; __u32 vni; int err; @@ -693,9 +731,11 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb) /* Is this VNI defined? */ vni = ntohl(vxh->vx_vni) >> 8; - vxlan = vxlan_find_vni(sock_net(sk), vni); + port = inet_sk(sk)->inet_sport; + vxlan = vxlan_find_vni(sock_net(sk), vni, port); if (!vxlan) { - netdev_dbg(skb->dev, "unknown vni %d\n", vni); + netdev_dbg(skb->dev, "unknown vni %d port %u\n", + vni, ntohs(port)); goto drop; } @@ -875,7 +915,7 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb) return false; } -static void vxlan_sock_free(struct sk_buff *skb) +static void vxlan_sock_put(struct sk_buff *skb) { sock_put(skb->sk); } @@ -883,13 +923,13 @@ static void vxlan_sock_free(struct sk_buff *skb) /* On transmit, associate with the tunnel socket */ static void vxlan_set_owner(struct net_device *dev, struct sk_buff *skb) { - struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id); - struct sock *sk = vn->sock->sk; + struct vxlan_dev *vxlan = netdev_priv(dev); + struct sock *sk = vxlan->vn_sock->sock->sk; skb_orphan(skb); sock_hold(sk); skb->sk = sk; - skb->destructor = vxlan_sock_free; + skb->destructor = vxlan_sock_put; } /* Compute source port for outgoing packet @@ -1031,7 +1071,7 @@ static netdev_tx_t vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, struct vxlan_dev *dst_vxlan; ip_rt_put(rt); - dst_vxlan = vxlan_find_vni(dev_net(dev), vni); + dst_vxlan = vxlan_find_vni(dev_net(dev), vni, dst_port); if (!dst_vxlan) goto tx_error; vxlan_encap_bypass(skb, vxlan, dst_vxlan); @@ -1306,6 +1346,7 @@ static void vxlan_setup(struct net_device *dev) dev->priv_flags &= ~IFF_XMIT_DST_RELEASE; dev->priv_flags |= IFF_LIVE_ADDR_CHANGE; + INIT_LIST_HEAD(&vxlan->next); spin_lock_init(&vxlan->hash_lock); init_timer_deferrable(&vxlan->age_timer); @@ -1390,11 +1431,78 @@ static const struct ethtool_ops vxlan_ethtool_ops = { .get_link = ethtool_op_get_link, }; +static void vxlan_del_work(struct work_struct *work) +{ + struct vxlan_sock *vs = container_of(work, struct vxlan_sock, del_work); + + sk_release_kernel(vs->sock->sk); + kfree_rcu(vs, rcu); +} + +/* Create new listen socket if needed */ +static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port) +{ + struct vxlan_sock *vs; + struct sock *sk; + struct sockaddr_in vxlan_addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = htonl(INADDR_ANY), + }; + int rc; + unsigned h; + + vs = kmalloc(sizeof(*vs), GFP_KERNEL); + if (!vs) + return ERR_PTR(-ENOMEM); + + for (h = 0; h < VNI_HASH_SIZE; ++h) + INIT_HLIST_HEAD(&vs->vni_list[h]); + + INIT_WORK(&vs->del_work, vxlan_del_work); + + /* Create UDP socket for encapsulation receive. */ + rc = sock_create_kern(AF_INET, SOCK_DGRAM, IPPROTO_UDP, &vs->sock); + if (rc < 0) { + pr_debug("UDP socket create failed\n"); + kfree(vs); + return ERR_PTR(rc); + } + + /* Put in proper namespace */ + sk = vs->sock->sk; + sk_change_net(sk, net); + + vxlan_addr.sin_port = port; + + rc = kernel_bind(vs->sock, (struct sockaddr *) &vxlan_addr, + sizeof(vxlan_addr)); + if (rc < 0) { + pr_debug("bind for UDP socket %pI4:%u (%d)\n", + &vxlan_addr.sin_addr, ntohs(vxlan_addr.sin_port), rc); + sk_release_kernel(sk); + kfree(vs); + return ERR_PTR(rc); + } + + /* Disable multicast loopback */ + inet_sk(sk)->mc_loop = 0; + + /* Mark socket as an encapsulation socket. */ + udp_sk(sk)->encap_type = 1; + udp_sk(sk)->encap_rcv = vxlan_udp_encap_recv; + udp_encap_enable(); + + vs->refcnt = 1; + return vs; +} + static int vxlan_newlink(struct net *net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { + struct vxlan_net *vn = net_generic(net, vxlan_net_id); struct vxlan_dev *vxlan = netdev_priv(dev); struct vxlan_rdst *dst = &vxlan->default_dst; + struct vxlan_sock *vs; __u32 vni; int err; @@ -1402,10 +1510,6 @@ static int vxlan_newlink(struct net *net, struct net_device *dev, return -EINVAL; vni = nla_get_u32(data[IFLA_VXLAN_ID]); - if (vxlan_find_vni(net, vni)) { - pr_info("duplicate VNI %u\n", vni); - return -EEXIST; - } dst->remote_vni = vni; if (data[IFLA_VXLAN_GROUP]) @@ -1471,22 +1575,58 @@ static int vxlan_newlink(struct net *net, struct net_device *dev, if (data[IFLA_VXLAN_PORT]) vxlan->dst_port = nla_get_be16(data[IFLA_VXLAN_PORT]); + if (vxlan_find_vni(net, vni, vxlan->dst_port)) { + pr_info("duplicate VNI %u\n", vni); + return -EEXIST; + } + + vs = vxlan_find_port(net, vxlan->dst_port); + if (vs) + ++vs->refcnt; + else { + /* Drop lock because socket create acquires RTNL lock */ + rtnl_unlock(); + vs = vxlan_socket_create(net, vxlan->dst_port); + rtnl_lock(); + if (IS_ERR(vs)) + return PTR_ERR(vs); + + hlist_add_head_rcu(&vs->hlist, vs_head(net, vxlan->dst_port)); + } + vxlan->vn_sock = vs; + SET_ETHTOOL_OPS(dev, &vxlan_ethtool_ops); err = register_netdevice(dev); - if (!err) - hlist_add_head_rcu(&vxlan->hlist, vni_head(net, dst->remote_vni)); + if (err) { + if (--vs->refcnt == 0) { + rtnl_unlock(); + sk_release_kernel(vs->sock->sk); + kfree(vs); + rtnl_lock(); + } + return err; + } - return err; + list_add(&vxlan->next, &vn->vxlan_list); + hlist_add_head_rcu(&vxlan->hlist, vni_head(vs, vni)); + + return 0; } static void vxlan_dellink(struct net_device *dev, struct list_head *head) { struct vxlan_dev *vxlan = netdev_priv(dev); + struct vxlan_sock *vs = vxlan->vn_sock; hlist_del_rcu(&vxlan->hlist); - + list_del(&vxlan->next); unregister_netdevice_queue(dev, head); + + if (--vs->refcnt == 0) { + hlist_del_rcu(&vs->hlist); + schedule_work(&vs->del_work); + } } static size_t vxlan_get_size(const struct net_device *dev) @@ -1572,46 +1712,12 @@ static struct rtnl_link_ops vxlan_link_ops __read_mostly = { static __net_init int vxlan_init_net(struct net *net) { struct vxlan_net *vn = net_generic(net, vxlan_net_id); - struct sock *sk; - struct sockaddr_in vxlan_addr = { - .sin_family = AF_INET, - .sin_addr.s_addr = htonl(INADDR_ANY), - }; - int rc; unsigned h; - /* Create UDP socket for encapsulation receive. */ - rc = sock_create_kern(AF_INET, SOCK_DGRAM, IPPROTO_UDP, &vn->sock); - if (rc < 0) { - pr_debug("UDP socket create failed\n"); - return rc; - } - /* Put in proper namespace */ - sk = vn->sock->sk; - sk_change_net(sk, net); - - vxlan_addr.sin_port = htons(vxlan_port); - - rc = kernel_bind(vn->sock, (struct sockaddr *) &vxlan_addr, - sizeof(vxlan_addr)); - if (rc < 0) { - pr_debug("bind for UDP socket %pI4:%u (%d)\n", - &vxlan_addr.sin_addr, ntohs(vxlan_addr.sin_port), rc); - sk_release_kernel(sk); - vn->sock = NULL; - return rc; - } - - /* Disable multicast loopback */ - inet_sk(sk)->mc_loop = 0; + INIT_LIST_HEAD(&vn->vxlan_list); - /* Mark socket as an encapsulation socket. */ - udp_sk(sk)->encap_type = 1; - udp_sk(sk)->encap_rcv = vxlan_udp_encap_recv; - udp_encap_enable(); - - for (h = 0; h < VNI_HASH_SIZE; ++h) - INIT_HLIST_HEAD(&vn->vni_list[h]); + for (h = 0; h < PORT_HASH_SIZE; ++h) + INIT_HLIST_HEAD(&vn->sock_list[h]); return 0; } @@ -1620,18 +1726,11 @@ static __net_exit void vxlan_exit_net(struct net *net) { struct vxlan_net *vn = net_generic(net, vxlan_net_id); struct vxlan_dev *vxlan; - unsigned h; rtnl_lock(); - for (h = 0; h < VNI_HASH_SIZE; ++h) - hlist_for_each_entry(vxlan, &vn->vni_list[h], hlist) - dev_close(vxlan->dev); + list_for_each_entry(vxlan, &vn->vxlan_list, next) + dev_close(vxlan->dev); rtnl_unlock(); - - if (vn->sock) { - sk_release_kernel(vn->sock->sk); - vn->sock = NULL; - } } static struct pernet_operations vxlan_net_ops = { -- cgit v1.2.3 From c2defb5f78e249a1402bc2af969a151af0391de8 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 17 May 2013 15:08:50 -0600 Subject: powerpc/PCI: Use PCI_UNKNOWN for unknown power state Previously we initialized dev->current_state to 4 (PCI_D3cold), but I think we wanted PCI_UNKNOWN (5) here based on the comment and the fact that the generic version of this code, pci_setup_device(), uses PCI_UNKNOWN. Signed-off-by: Bjorn Helgaas Acked-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/pci_of_scan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/pci_of_scan.c b/arch/powerpc/kernel/pci_of_scan.c index 2a67e9baa59f..d2d407d65344 100644 --- a/arch/powerpc/kernel/pci_of_scan.c +++ b/arch/powerpc/kernel/pci_of_scan.c @@ -165,7 +165,7 @@ struct pci_dev *of_create_pci_dev(struct device_node *node, pr_debug(" class: 0x%x\n", dev->class); pr_debug(" revision: 0x%x\n", dev->revision); - dev->current_state = 4; /* unknown power state */ + dev->current_state = PCI_UNKNOWN; /* unknown power state */ dev->error_state = pci_channel_io_normal; dev->dma_mask = 0xffffffff; -- cgit v1.2.3 From 18cf1f1270e7f01be5217203c9f46c5d88a54839 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 16 May 2013 17:48:07 +0000 Subject: net/usb: r8152: Remove redundant version.h header inclusion version.h header inclusion is not necessary as detected by checkversion.pl. Signed-off-by: Sachin Kamat Signed-off-by: David S. Miller --- drivers/net/usb/r8152.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 14e519888631..8e53e4110811 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3 From b4236daa410e4e778a938ac1151bf94d94c55efc Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 16 May 2013 17:48:08 +0000 Subject: net/usb: r8152: Use module_usb_driver() module_usb_driver() eliminates boilerplate and simplifies the code. Signed-off-by: Sachin Kamat Cc: Realtek linux nic maintainers Signed-off-by: David S. Miller --- drivers/net/usb/r8152.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 8e53e4110811..d02bac82fc57 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -1748,18 +1748,7 @@ static struct usb_driver rtl8152_driver = { .resume = rtl8152_resume }; -static int __init usb_rtl8152_init(void) -{ - return usb_register(&rtl8152_driver); -} - -static void __exit usb_rtl8152_exit(void) -{ - usb_deregister(&rtl8152_driver); -} - -module_init(usb_rtl8152_init); -module_exit(usb_rtl8152_exit); +module_usb_driver(rtl8152_driver); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); -- cgit v1.2.3 From 650c8496702f2910c506ccf66685893302e1796e Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 16 May 2013 19:45:30 +0000 Subject: x86: bpf_jit_comp: can call module_free() from any context It looks like we can call module_free()/vfree() from softirq context, so no longer need a wrapper and a work_struct. Signed-off-by: Eric Dumazet Cc: Al Viro Signed-off-by: David S. Miller --- arch/x86/net/bpf_jit_comp.c | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c index f66b54086ce5..c0212db40032 100644 --- a/arch/x86/net/bpf_jit_comp.c +++ b/arch/x86/net/bpf_jit_comp.c @@ -717,9 +717,7 @@ cond_branch: f_offset = addrs[i + filter[i].jf] - addrs[i]; break; } if (proglen == oldproglen) { - image = module_alloc(max_t(unsigned int, - proglen, - sizeof(struct work_struct))); + image = module_alloc(proglen); if (!image) goto out; } @@ -738,20 +736,8 @@ out: return; } -static void jit_free_defer(struct work_struct *arg) -{ - module_free(NULL, arg); -} - -/* run from softirq, we must use a work_struct to call - * module_free() from process context - */ void bpf_jit_free(struct sk_filter *fp) { - if (fp->bpf_func != sk_run_filter) { - struct work_struct *work = (struct work_struct *)fp->bpf_func; - - INIT_WORK(work, jit_free_defer); - schedule_work(work); - } + if (fp->bpf_func != sk_run_filter) + module_free(NULL, fp->bpf_func); } -- cgit v1.2.3 From f1db320ec5788d5fcc05c0285bd0980319185497 Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Thu, 16 May 2013 23:24:28 +0000 Subject: xen-netback: remove dead code The array mmap_pages is never touched in the initialization function. This is remnant of mapping mechanism, which does not exist upstream. In current upstream code this array only tracks usage of pages inside netback. Those pages are allocated when contructing a SKB and passed directly to network subsystem. Signed-off-by: Wei Liu Acked-by: Ian Campbell Signed-off-by: David S. Miller --- drivers/net/xen-netback/netback.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c index 37984e6d4e99..295a9c267df4 100644 --- a/drivers/net/xen-netback/netback.c +++ b/drivers/net/xen-netback/netback.c @@ -1939,10 +1939,6 @@ static int __init netback_init(void) failed_init: while (--group >= 0) { struct xen_netbk *netbk = &xen_netbk[group]; - for (i = 0; i < MAX_PENDING_REQS; i++) { - if (netbk->mmap_pages[i]) - __free_page(netbk->mmap_pages[i]); - } del_timer(&netbk->net_timer); kthread_stop(netbk->task); } -- cgit v1.2.3 From b103f358d9f6f58658f1a6dc08912ab921dd86f1 Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Thu, 16 May 2013 23:26:11 +0000 Subject: xen-netback: enable user to unload netback module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch enables user to unload netback module, which is useful when user wants to upgrade to a newer netback module without rebooting the host. Netfront cannot handle netback removal event. As we cannot fix all possible frontends we add module get / put along with vif get / put to avoid mis-unloading of netback. To unload netback module, user needs to shutdown all VMs or migrate them to another host or unplug all vifs before hand. Signed-off-by: Wei Liu Acked-by: Ian Campbell ¬ Signed-off-by: David S. Miller --- drivers/net/xen-netback/common.h | 1 + drivers/net/xen-netback/interface.c | 19 ++++++++++++++++++- drivers/net/xen-netback/netback.c | 20 ++++++++++++++++++++ drivers/net/xen-netback/xenbus.c | 5 +++++ 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/drivers/net/xen-netback/common.h b/drivers/net/xen-netback/common.h index 9d7f1723dd8f..6102a6c59f4e 100644 --- a/drivers/net/xen-netback/common.h +++ b/drivers/net/xen-netback/common.h @@ -120,6 +120,7 @@ void xenvif_get(struct xenvif *vif); void xenvif_put(struct xenvif *vif); int xenvif_xenbus_init(void); +void xenvif_xenbus_fini(void); int xenvif_schedulable(struct xenvif *vif); diff --git a/drivers/net/xen-netback/interface.c b/drivers/net/xen-netback/interface.c index d98414168485..82202c2b1bd1 100644 --- a/drivers/net/xen-netback/interface.c +++ b/drivers/net/xen-netback/interface.c @@ -316,6 +316,8 @@ int xenvif_connect(struct xenvif *vif, unsigned long tx_ring_ref, if (vif->irq) return 0; + __module_get(THIS_MODULE); + err = xen_netbk_map_frontend_rings(vif, tx_ring_ref, rx_ring_ref); if (err < 0) goto err; @@ -343,6 +345,7 @@ int xenvif_connect(struct xenvif *vif, unsigned long tx_ring_ref, err_unmap: xen_netbk_unmap_frontend_rings(vif); err: + module_put(THIS_MODULE); return err; } @@ -360,18 +363,32 @@ void xenvif_carrier_off(struct xenvif *vif) void xenvif_disconnect(struct xenvif *vif) { + /* Disconnect funtion might get called by generic framework + * even before vif connects, so we need to check if we really + * need to do a module_put. + */ + int need_module_put = 0; + if (netif_carrier_ok(vif->dev)) xenvif_carrier_off(vif); atomic_dec(&vif->refcnt); wait_event(vif->waiting_to_free, atomic_read(&vif->refcnt) == 0); - if (vif->irq) + if (vif->irq) { unbind_from_irqhandler(vif->irq, vif); + /* vif->irq is valid, we had a module_get in + * xenvif_connect. + */ + need_module_put = 1; + } unregister_netdev(vif->dev); xen_netbk_unmap_frontend_rings(vif); free_netdev(vif->dev); + + if (need_module_put) + module_put(THIS_MODULE); } diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c index 295a9c267df4..2d9477fd900f 100644 --- a/drivers/net/xen-netback/netback.c +++ b/drivers/net/xen-netback/netback.c @@ -1949,5 +1949,25 @@ failed_init: module_init(netback_init); +static void __exit netback_fini(void) +{ + int i, j; + + xenvif_xenbus_fini(); + + for (i = 0; i < xen_netbk_group_nr; i++) { + struct xen_netbk *netbk = &xen_netbk[i]; + del_timer_sync(&netbk->net_timer); + kthread_stop(netbk->task); + for (j = 0; j < MAX_PENDING_REQS; j++) { + if (netbk->mmap_pages[i]) + __free_page(netbk->mmap_pages[i]); + } + } + + vfree(xen_netbk); +} +module_exit(netback_fini); + MODULE_LICENSE("Dual BSD/GPL"); MODULE_ALIAS("xen-backend:vif"); diff --git a/drivers/net/xen-netback/xenbus.c b/drivers/net/xen-netback/xenbus.c index 410018c4c528..d230c145660c 100644 --- a/drivers/net/xen-netback/xenbus.c +++ b/drivers/net/xen-netback/xenbus.c @@ -485,3 +485,8 @@ int xenvif_xenbus_init(void) { return xenbus_register_backend(&netback_driver); } + +void xenvif_xenbus_fini(void) +{ + return xenbus_unregister_driver(&netback_driver); +} -- cgit v1.2.3 From 57b354e66b67c4c72468a26d4313d1217ef32e17 Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Thu, 16 May 2013 23:36:32 +0000 Subject: dev: remove duplicate 'skb->dev = dev' in dev_forward_skb() This was added by commit 59b9997baba5 (Revert "net: maintain namespace isolation between vlan and real device"). In fact, before the initial commit - the one that is reverted -, this statement was not present. 'skb->dev = dev' is already done in eth_type_trans(), which is call just after. Spotted-by: Alain Ritoux Signed-off-by: Nicolas Dichtel Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- net/core/dev.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/core/dev.c b/net/core/dev.c index fc1e289397f5..18e9730cc4be 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1629,7 +1629,6 @@ int dev_forward_skb(struct net_device *dev, struct sk_buff *skb) return NET_RX_DROP; } skb->skb_iif = 0; - skb->dev = dev; skb_dst_drop(skb); skb->tstamp.tv64 = 0; skb->pkt_type = PACKET_HOST; -- cgit v1.2.3 From 5199dfe531db19f3ac76542753849877e9854441 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 17 May 2013 12:12:34 +0000 Subject: sparc: bpf_jit_comp: can call module_free() from any context module_free()/vfree() takes care of details, we no longer need a wrapper and a work_struct. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- arch/sparc/net/bpf_jit_comp.c | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/arch/sparc/net/bpf_jit_comp.c b/arch/sparc/net/bpf_jit_comp.c index d36a85ebb5e0..9c7be59e6f5a 100644 --- a/arch/sparc/net/bpf_jit_comp.c +++ b/arch/sparc/net/bpf_jit_comp.c @@ -785,9 +785,7 @@ cond_branch: f_offset = addrs[i + filter[i].jf]; break; } if (proglen == oldproglen) { - image = module_alloc(max_t(unsigned int, - proglen, - sizeof(struct work_struct))); + image = module_alloc(proglen); if (!image) goto out; } @@ -806,20 +804,8 @@ out: return; } -static void jit_free_defer(struct work_struct *arg) -{ - module_free(NULL, arg); -} - -/* run from softirq, we must use a work_struct to call - * module_free() from process context - */ void bpf_jit_free(struct sk_filter *fp) { - if (fp->bpf_func != sk_run_filter) { - struct work_struct *work = (struct work_struct *)fp->bpf_func; - - INIT_WORK(work, jit_free_defer); - schedule_work(work); - } + if (fp->bpf_func != sk_run_filter) + module_free(NULL, fp->bpf_func); } -- cgit v1.2.3 From 571ab6c6f38e44eca40098f58b28e5016c8dbc50 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 15 May 2013 10:09:43 +0300 Subject: ASoC: wm8994: missing break in wm8994_get_fll_config() Smatch complains that: sound/soc/codecs/wm8994.c:2087 wm8994_get_fll_config() warn: missing break? reassigning 'fll->k' Signed-off-by: Dan Carpenter Signed-off-by: Mark Brown --- sound/soc/codecs/wm8994.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 9f32dd8660d5..303d755b9342 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -2080,6 +2080,7 @@ static int wm8994_get_fll_config(struct wm8994 *control, struct fll_div *fll, fll->k = K / 10; pr_debug("N=%x K=%x\n", fll->n, fll->k); + break; default: gcd_fll = gcd(freq_out, freq_in); -- cgit v1.2.3 From 7275acdfe29ba03ad2f6e150386900c4e2d43fb1 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 14 May 2013 14:31:01 +0100 Subject: ARM: KVM: move GIC/timer code to a common location As KVM/arm64 is looming on the horizon, it makes sense to move some of the common code to a single location in order to reduce duplication. The code could live anywhere. Actually, most of KVM is already built with a bunch of ugly ../../.. hacks in the various Makefiles, so we're not exactly talking about style here. But maybe it is time to start moving into a less ugly direction. The include files must be in a "public" location, as they are accessed from non-KVM files (arch/arm/kernel/asm-offsets.c). For this purpose, introduce two new locations: - virt/kvm/arm/ : x86 and ia64 already share the ioapic code in virt/kvm, so this could be seen as a (very ugly) precedent. - include/kvm/ : there is already an include/xen, and while the intent is slightly different, this seems as good a location as any Eventually, we should probably have independant Makefiles at every levels (just like everywhere else in the kernel), but this is just the first step. Signed-off-by: Marc Zyngier Signed-off-by: Gleb Natapov --- arch/arm/include/asm/kvm_arch_timer.h | 85 -- arch/arm/include/asm/kvm_host.h | 4 +- arch/arm/include/asm/kvm_vgic.h | 220 ----- arch/arm/kvm/Makefile | 7 +- arch/arm/kvm/arch_timer.c | 272 ------ arch/arm/kvm/vgic.c | 1499 --------------------------------- include/kvm/arm_arch_timer.h | 85 ++ include/kvm/arm_vgic.h | 220 +++++ virt/kvm/arm/arch_timer.c | 272 ++++++ virt/kvm/arm/vgic.c | 1499 +++++++++++++++++++++++++++++++++ 10 files changed, 2082 insertions(+), 2081 deletions(-) delete mode 100644 arch/arm/include/asm/kvm_arch_timer.h delete mode 100644 arch/arm/include/asm/kvm_vgic.h delete mode 100644 arch/arm/kvm/arch_timer.c delete mode 100644 arch/arm/kvm/vgic.c create mode 100644 include/kvm/arm_arch_timer.h create mode 100644 include/kvm/arm_vgic.h create mode 100644 virt/kvm/arm/arch_timer.c create mode 100644 virt/kvm/arm/vgic.c diff --git a/arch/arm/include/asm/kvm_arch_timer.h b/arch/arm/include/asm/kvm_arch_timer.h deleted file mode 100644 index 68cb9e1dfb81..000000000000 --- a/arch/arm/include/asm/kvm_arch_timer.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2012 ARM Ltd. - * Author: Marc Zyngier - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __ASM_ARM_KVM_ARCH_TIMER_H -#define __ASM_ARM_KVM_ARCH_TIMER_H - -#include -#include -#include - -struct arch_timer_kvm { -#ifdef CONFIG_KVM_ARM_TIMER - /* Is the timer enabled */ - bool enabled; - - /* Virtual offset */ - cycle_t cntvoff; -#endif -}; - -struct arch_timer_cpu { -#ifdef CONFIG_KVM_ARM_TIMER - /* Registers: control register, timer value */ - u32 cntv_ctl; /* Saved/restored */ - cycle_t cntv_cval; /* Saved/restored */ - - /* - * Anything that is not used directly from assembly code goes - * here. - */ - - /* Background timer used when the guest is not running */ - struct hrtimer timer; - - /* Work queued with the above timer expires */ - struct work_struct expired; - - /* Background timer active */ - bool armed; - - /* Timer IRQ */ - const struct kvm_irq_level *irq; -#endif -}; - -#ifdef CONFIG_KVM_ARM_TIMER -int kvm_timer_hyp_init(void); -int kvm_timer_init(struct kvm *kvm); -void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu); -void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu); -void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu); -void kvm_timer_vcpu_terminate(struct kvm_vcpu *vcpu); -#else -static inline int kvm_timer_hyp_init(void) -{ - return 0; -}; - -static inline int kvm_timer_init(struct kvm *kvm) -{ - return 0; -} - -static inline void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu) {} -static inline void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu) {} -static inline void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu) {} -static inline void kvm_timer_vcpu_terminate(struct kvm_vcpu *vcpu) {} -#endif - -#endif diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h index 57cb786a6203..ff5aaf10e6ec 100644 --- a/arch/arm/include/asm/kvm_host.h +++ b/arch/arm/include/asm/kvm_host.h @@ -23,7 +23,7 @@ #include #include #include -#include +#include #define KVM_MAX_VCPUS CONFIG_KVM_ARM_MAX_VCPUS #define KVM_USER_MEM_SLOTS 32 @@ -38,7 +38,7 @@ #define KVM_NR_PAGE_SIZES 1 #define KVM_PAGES_PER_HPAGE(x) (1UL<<31) -#include +#include struct kvm_vcpu; u32 *kvm_vcpu_reg(struct kvm_vcpu *vcpu, u8 reg_num, u32 mode); diff --git a/arch/arm/include/asm/kvm_vgic.h b/arch/arm/include/asm/kvm_vgic.h deleted file mode 100644 index 343744e4809c..000000000000 --- a/arch/arm/include/asm/kvm_vgic.h +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright (C) 2012 ARM Ltd. - * Author: Marc Zyngier - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __ASM_ARM_KVM_VGIC_H -#define __ASM_ARM_KVM_VGIC_H - -#include -#include -#include -#include -#include -#include - -#define VGIC_NR_IRQS 128 -#define VGIC_NR_SGIS 16 -#define VGIC_NR_PPIS 16 -#define VGIC_NR_PRIVATE_IRQS (VGIC_NR_SGIS + VGIC_NR_PPIS) -#define VGIC_NR_SHARED_IRQS (VGIC_NR_IRQS - VGIC_NR_PRIVATE_IRQS) -#define VGIC_MAX_CPUS KVM_MAX_VCPUS -#define VGIC_MAX_LRS (1 << 6) - -/* Sanity checks... */ -#if (VGIC_MAX_CPUS > 8) -#error Invalid number of CPU interfaces -#endif - -#if (VGIC_NR_IRQS & 31) -#error "VGIC_NR_IRQS must be a multiple of 32" -#endif - -#if (VGIC_NR_IRQS > 1024) -#error "VGIC_NR_IRQS must be <= 1024" -#endif - -/* - * The GIC distributor registers describing interrupts have two parts: - * - 32 per-CPU interrupts (SGI + PPI) - * - a bunch of shared interrupts (SPI) - */ -struct vgic_bitmap { - union { - u32 reg[VGIC_NR_PRIVATE_IRQS / 32]; - DECLARE_BITMAP(reg_ul, VGIC_NR_PRIVATE_IRQS); - } percpu[VGIC_MAX_CPUS]; - union { - u32 reg[VGIC_NR_SHARED_IRQS / 32]; - DECLARE_BITMAP(reg_ul, VGIC_NR_SHARED_IRQS); - } shared; -}; - -struct vgic_bytemap { - u32 percpu[VGIC_MAX_CPUS][VGIC_NR_PRIVATE_IRQS / 4]; - u32 shared[VGIC_NR_SHARED_IRQS / 4]; -}; - -struct vgic_dist { -#ifdef CONFIG_KVM_ARM_VGIC - spinlock_t lock; - bool ready; - - /* Virtual control interface mapping */ - void __iomem *vctrl_base; - - /* Distributor and vcpu interface mapping in the guest */ - phys_addr_t vgic_dist_base; - phys_addr_t vgic_cpu_base; - - /* Distributor enabled */ - u32 enabled; - - /* Interrupt enabled (one bit per IRQ) */ - struct vgic_bitmap irq_enabled; - - /* Interrupt 'pin' level */ - struct vgic_bitmap irq_state; - - /* Level-triggered interrupt in progress */ - struct vgic_bitmap irq_active; - - /* Interrupt priority. Not used yet. */ - struct vgic_bytemap irq_priority; - - /* Level/edge triggered */ - struct vgic_bitmap irq_cfg; - - /* Source CPU per SGI and target CPU */ - u8 irq_sgi_sources[VGIC_MAX_CPUS][VGIC_NR_SGIS]; - - /* Target CPU for each IRQ */ - u8 irq_spi_cpu[VGIC_NR_SHARED_IRQS]; - struct vgic_bitmap irq_spi_target[VGIC_MAX_CPUS]; - - /* Bitmap indicating which CPU has something pending */ - unsigned long irq_pending_on_cpu; -#endif -}; - -struct vgic_cpu { -#ifdef CONFIG_KVM_ARM_VGIC - /* per IRQ to LR mapping */ - u8 vgic_irq_lr_map[VGIC_NR_IRQS]; - - /* Pending interrupts on this VCPU */ - DECLARE_BITMAP( pending_percpu, VGIC_NR_PRIVATE_IRQS); - DECLARE_BITMAP( pending_shared, VGIC_NR_SHARED_IRQS); - - /* Bitmap of used/free list registers */ - DECLARE_BITMAP( lr_used, VGIC_MAX_LRS); - - /* Number of list registers on this CPU */ - int nr_lr; - - /* CPU vif control registers for world switch */ - u32 vgic_hcr; - u32 vgic_vmcr; - u32 vgic_misr; /* Saved only */ - u32 vgic_eisr[2]; /* Saved only */ - u32 vgic_elrsr[2]; /* Saved only */ - u32 vgic_apr; - u32 vgic_lr[VGIC_MAX_LRS]; -#endif -}; - -#define LR_EMPTY 0xff - -struct kvm; -struct kvm_vcpu; -struct kvm_run; -struct kvm_exit_mmio; - -#ifdef CONFIG_KVM_ARM_VGIC -int kvm_vgic_set_addr(struct kvm *kvm, unsigned long type, u64 addr); -int kvm_vgic_hyp_init(void); -int kvm_vgic_init(struct kvm *kvm); -int kvm_vgic_create(struct kvm *kvm); -int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu); -void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu); -void kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu); -int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int irq_num, - bool level); -int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu); -bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, - struct kvm_exit_mmio *mmio); - -#define irqchip_in_kernel(k) (!!((k)->arch.vgic.vctrl_base)) -#define vgic_initialized(k) ((k)->arch.vgic.ready) - -#else -static inline int kvm_vgic_hyp_init(void) -{ - return 0; -} - -static inline int kvm_vgic_set_addr(struct kvm *kvm, unsigned long type, u64 addr) -{ - return 0; -} - -static inline int kvm_vgic_init(struct kvm *kvm) -{ - return 0; -} - -static inline int kvm_vgic_create(struct kvm *kvm) -{ - return 0; -} - -static inline int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu) -{ - return 0; -} - -static inline void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu) {} -static inline void kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu) {} - -static inline int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, - unsigned int irq_num, bool level) -{ - return 0; -} - -static inline int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu) -{ - return 0; -} - -static inline bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, - struct kvm_exit_mmio *mmio) -{ - return false; -} - -static inline int irqchip_in_kernel(struct kvm *kvm) -{ - return 0; -} - -static inline bool vgic_initialized(struct kvm *kvm) -{ - return true; -} -#endif - -#endif diff --git a/arch/arm/kvm/Makefile b/arch/arm/kvm/Makefile index 53c5ed83d16f..9184a491d172 100644 --- a/arch/arm/kvm/Makefile +++ b/arch/arm/kvm/Makefile @@ -14,10 +14,11 @@ CFLAGS_mmu.o := -I. AFLAGS_init.o := -Wa,-march=armv7-a$(plus_virt) AFLAGS_interrupts.o := -Wa,-march=armv7-a$(plus_virt) -kvm-arm-y = $(addprefix ../../../virt/kvm/, kvm_main.o coalesced_mmio.o) +KVM := ../../../virt/kvm +kvm-arm-y = $(addprefix $(KVM)/, kvm_main.o coalesced_mmio.o) obj-y += kvm-arm.o init.o interrupts.o obj-y += arm.o handle_exit.o guest.o mmu.o emulate.o reset.o obj-y += coproc.o coproc_a15.o mmio.o psci.o perf.o -obj-$(CONFIG_KVM_ARM_VGIC) += vgic.o -obj-$(CONFIG_KVM_ARM_TIMER) += arch_timer.o +obj-$(CONFIG_KVM_ARM_VGIC) += $(KVM)/arm/vgic.o +obj-$(CONFIG_KVM_ARM_TIMER) += $(KVM)/arm/arch_timer.o diff --git a/arch/arm/kvm/arch_timer.c b/arch/arm/kvm/arch_timer.c deleted file mode 100644 index c55b6089e923..000000000000 --- a/arch/arm/kvm/arch_timer.c +++ /dev/null @@ -1,272 +0,0 @@ -/* - * Copyright (C) 2012 ARM Ltd. - * Author: Marc Zyngier - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -static struct timecounter *timecounter; -static struct workqueue_struct *wqueue; -static struct kvm_irq_level timer_irq = { - .level = 1, -}; - -static cycle_t kvm_phys_timer_read(void) -{ - return timecounter->cc->read(timecounter->cc); -} - -static bool timer_is_armed(struct arch_timer_cpu *timer) -{ - return timer->armed; -} - -/* timer_arm: as in "arm the timer", not as in ARM the company */ -static void timer_arm(struct arch_timer_cpu *timer, u64 ns) -{ - timer->armed = true; - hrtimer_start(&timer->timer, ktime_add_ns(ktime_get(), ns), - HRTIMER_MODE_ABS); -} - -static void timer_disarm(struct arch_timer_cpu *timer) -{ - if (timer_is_armed(timer)) { - hrtimer_cancel(&timer->timer); - cancel_work_sync(&timer->expired); - timer->armed = false; - } -} - -static void kvm_timer_inject_irq(struct kvm_vcpu *vcpu) -{ - struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu; - - timer->cntv_ctl |= ARCH_TIMER_CTRL_IT_MASK; - kvm_vgic_inject_irq(vcpu->kvm, vcpu->vcpu_id, - vcpu->arch.timer_cpu.irq->irq, - vcpu->arch.timer_cpu.irq->level); -} - -static irqreturn_t kvm_arch_timer_handler(int irq, void *dev_id) -{ - struct kvm_vcpu *vcpu = *(struct kvm_vcpu **)dev_id; - - /* - * We disable the timer in the world switch and let it be - * handled by kvm_timer_sync_hwstate(). Getting a timer - * interrupt at this point is a sure sign of some major - * breakage. - */ - pr_warn("Unexpected interrupt %d on vcpu %p\n", irq, vcpu); - return IRQ_HANDLED; -} - -static void kvm_timer_inject_irq_work(struct work_struct *work) -{ - struct kvm_vcpu *vcpu; - - vcpu = container_of(work, struct kvm_vcpu, arch.timer_cpu.expired); - vcpu->arch.timer_cpu.armed = false; - kvm_timer_inject_irq(vcpu); -} - -static enum hrtimer_restart kvm_timer_expire(struct hrtimer *hrt) -{ - struct arch_timer_cpu *timer; - timer = container_of(hrt, struct arch_timer_cpu, timer); - queue_work(wqueue, &timer->expired); - return HRTIMER_NORESTART; -} - -/** - * kvm_timer_flush_hwstate - prepare to move the virt timer to the cpu - * @vcpu: The vcpu pointer - * - * Disarm any pending soft timers, since the world-switch code will write the - * virtual timer state back to the physical CPU. - */ -void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu) -{ - struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu; - - /* - * We're about to run this vcpu again, so there is no need to - * keep the background timer running, as we're about to - * populate the CPU timer again. - */ - timer_disarm(timer); -} - -/** - * kvm_timer_sync_hwstate - sync timer state from cpu - * @vcpu: The vcpu pointer - * - * Check if the virtual timer was armed and either schedule a corresponding - * soft timer or inject directly if already expired. - */ -void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu) -{ - struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu; - cycle_t cval, now; - u64 ns; - - if ((timer->cntv_ctl & ARCH_TIMER_CTRL_IT_MASK) || - !(timer->cntv_ctl & ARCH_TIMER_CTRL_ENABLE)) - return; - - cval = timer->cntv_cval; - now = kvm_phys_timer_read() - vcpu->kvm->arch.timer.cntvoff; - - BUG_ON(timer_is_armed(timer)); - - if (cval <= now) { - /* - * Timer has already expired while we were not - * looking. Inject the interrupt and carry on. - */ - kvm_timer_inject_irq(vcpu); - return; - } - - ns = cyclecounter_cyc2ns(timecounter->cc, cval - now); - timer_arm(timer, ns); -} - -void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu) -{ - struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu; - - INIT_WORK(&timer->expired, kvm_timer_inject_irq_work); - hrtimer_init(&timer->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); - timer->timer.function = kvm_timer_expire; - timer->irq = &timer_irq; -} - -static void kvm_timer_init_interrupt(void *info) -{ - enable_percpu_irq(timer_irq.irq, 0); -} - - -static int kvm_timer_cpu_notify(struct notifier_block *self, - unsigned long action, void *cpu) -{ - switch (action) { - case CPU_STARTING: - case CPU_STARTING_FROZEN: - kvm_timer_init_interrupt(NULL); - break; - case CPU_DYING: - case CPU_DYING_FROZEN: - disable_percpu_irq(timer_irq.irq); - break; - } - - return NOTIFY_OK; -} - -static struct notifier_block kvm_timer_cpu_nb = { - .notifier_call = kvm_timer_cpu_notify, -}; - -static const struct of_device_id arch_timer_of_match[] = { - { .compatible = "arm,armv7-timer", }, - {}, -}; - -int kvm_timer_hyp_init(void) -{ - struct device_node *np; - unsigned int ppi; - int err; - - timecounter = arch_timer_get_timecounter(); - if (!timecounter) - return -ENODEV; - - np = of_find_matching_node(NULL, arch_timer_of_match); - if (!np) { - kvm_err("kvm_arch_timer: can't find DT node\n"); - return -ENODEV; - } - - ppi = irq_of_parse_and_map(np, 2); - if (!ppi) { - kvm_err("kvm_arch_timer: no virtual timer interrupt\n"); - err = -EINVAL; - goto out; - } - - err = request_percpu_irq(ppi, kvm_arch_timer_handler, - "kvm guest timer", kvm_get_running_vcpus()); - if (err) { - kvm_err("kvm_arch_timer: can't request interrupt %d (%d)\n", - ppi, err); - goto out; - } - - timer_irq.irq = ppi; - - err = register_cpu_notifier(&kvm_timer_cpu_nb); - if (err) { - kvm_err("Cannot register timer CPU notifier\n"); - goto out_free; - } - - wqueue = create_singlethread_workqueue("kvm_arch_timer"); - if (!wqueue) { - err = -ENOMEM; - goto out_free; - } - - kvm_info("%s IRQ%d\n", np->name, ppi); - on_each_cpu(kvm_timer_init_interrupt, NULL, 1); - - goto out; -out_free: - free_percpu_irq(ppi, kvm_get_running_vcpus()); -out: - of_node_put(np); - return err; -} - -void kvm_timer_vcpu_terminate(struct kvm_vcpu *vcpu) -{ - struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu; - - timer_disarm(timer); -} - -int kvm_timer_init(struct kvm *kvm) -{ - if (timecounter && wqueue) { - kvm->arch.timer.cntvoff = kvm_phys_timer_read(); - kvm->arch.timer.enabled = 1; - } - - return 0; -} diff --git a/arch/arm/kvm/vgic.c b/arch/arm/kvm/vgic.c deleted file mode 100644 index 17c5ac7d10ed..000000000000 --- a/arch/arm/kvm/vgic.c +++ /dev/null @@ -1,1499 +0,0 @@ -/* - * Copyright (C) 2012 ARM Ltd. - * Author: Marc Zyngier - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -/* - * How the whole thing works (courtesy of Christoffer Dall): - * - * - At any time, the dist->irq_pending_on_cpu is the oracle that knows if - * something is pending - * - VGIC pending interrupts are stored on the vgic.irq_state vgic - * bitmap (this bitmap is updated by both user land ioctls and guest - * mmio ops, and other in-kernel peripherals such as the - * arch. timers) and indicate the 'wire' state. - * - Every time the bitmap changes, the irq_pending_on_cpu oracle is - * recalculated - * - To calculate the oracle, we need info for each cpu from - * compute_pending_for_cpu, which considers: - * - PPI: dist->irq_state & dist->irq_enable - * - SPI: dist->irq_state & dist->irq_enable & dist->irq_spi_target - * - irq_spi_target is a 'formatted' version of the GICD_ICFGR - * registers, stored on each vcpu. We only keep one bit of - * information per interrupt, making sure that only one vcpu can - * accept the interrupt. - * - The same is true when injecting an interrupt, except that we only - * consider a single interrupt at a time. The irq_spi_cpu array - * contains the target CPU for each SPI. - * - * The handling of level interrupts adds some extra complexity. We - * need to track when the interrupt has been EOIed, so we can sample - * the 'line' again. This is achieved as such: - * - * - When a level interrupt is moved onto a vcpu, the corresponding - * bit in irq_active is set. As long as this bit is set, the line - * will be ignored for further interrupts. The interrupt is injected - * into the vcpu with the GICH_LR_EOI bit set (generate a - * maintenance interrupt on EOI). - * - When the interrupt is EOIed, the maintenance interrupt fires, - * and clears the corresponding bit in irq_active. This allow the - * interrupt line to be sampled again. - */ - -#define VGIC_ADDR_UNDEF (-1) -#define IS_VGIC_ADDR_UNDEF(_x) ((_x) == VGIC_ADDR_UNDEF) - -/* Physical address of vgic virtual cpu interface */ -static phys_addr_t vgic_vcpu_base; - -/* Virtual control interface base address */ -static void __iomem *vgic_vctrl_base; - -static struct device_node *vgic_node; - -#define ACCESS_READ_VALUE (1 << 0) -#define ACCESS_READ_RAZ (0 << 0) -#define ACCESS_READ_MASK(x) ((x) & (1 << 0)) -#define ACCESS_WRITE_IGNORED (0 << 1) -#define ACCESS_WRITE_SETBIT (1 << 1) -#define ACCESS_WRITE_CLEARBIT (2 << 1) -#define ACCESS_WRITE_VALUE (3 << 1) -#define ACCESS_WRITE_MASK(x) ((x) & (3 << 1)) - -static void vgic_retire_disabled_irqs(struct kvm_vcpu *vcpu); -static void vgic_update_state(struct kvm *kvm); -static void vgic_kick_vcpus(struct kvm *kvm); -static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg); -static u32 vgic_nr_lr; - -static unsigned int vgic_maint_irq; - -static u32 *vgic_bitmap_get_reg(struct vgic_bitmap *x, - int cpuid, u32 offset) -{ - offset >>= 2; - if (!offset) - return x->percpu[cpuid].reg; - else - return x->shared.reg + offset - 1; -} - -static int vgic_bitmap_get_irq_val(struct vgic_bitmap *x, - int cpuid, int irq) -{ - if (irq < VGIC_NR_PRIVATE_IRQS) - return test_bit(irq, x->percpu[cpuid].reg_ul); - - return test_bit(irq - VGIC_NR_PRIVATE_IRQS, x->shared.reg_ul); -} - -static void vgic_bitmap_set_irq_val(struct vgic_bitmap *x, int cpuid, - int irq, int val) -{ - unsigned long *reg; - - if (irq < VGIC_NR_PRIVATE_IRQS) { - reg = x->percpu[cpuid].reg_ul; - } else { - reg = x->shared.reg_ul; - irq -= VGIC_NR_PRIVATE_IRQS; - } - - if (val) - set_bit(irq, reg); - else - clear_bit(irq, reg); -} - -static unsigned long *vgic_bitmap_get_cpu_map(struct vgic_bitmap *x, int cpuid) -{ - if (unlikely(cpuid >= VGIC_MAX_CPUS)) - return NULL; - return x->percpu[cpuid].reg_ul; -} - -static unsigned long *vgic_bitmap_get_shared_map(struct vgic_bitmap *x) -{ - return x->shared.reg_ul; -} - -static u32 *vgic_bytemap_get_reg(struct vgic_bytemap *x, int cpuid, u32 offset) -{ - offset >>= 2; - BUG_ON(offset > (VGIC_NR_IRQS / 4)); - if (offset < 4) - return x->percpu[cpuid] + offset; - else - return x->shared + offset - 8; -} - -#define VGIC_CFG_LEVEL 0 -#define VGIC_CFG_EDGE 1 - -static bool vgic_irq_is_edge(struct kvm_vcpu *vcpu, int irq) -{ - struct vgic_dist *dist = &vcpu->kvm->arch.vgic; - int irq_val; - - irq_val = vgic_bitmap_get_irq_val(&dist->irq_cfg, vcpu->vcpu_id, irq); - return irq_val == VGIC_CFG_EDGE; -} - -static int vgic_irq_is_enabled(struct kvm_vcpu *vcpu, int irq) -{ - struct vgic_dist *dist = &vcpu->kvm->arch.vgic; - - return vgic_bitmap_get_irq_val(&dist->irq_enabled, vcpu->vcpu_id, irq); -} - -static int vgic_irq_is_active(struct kvm_vcpu *vcpu, int irq) -{ - struct vgic_dist *dist = &vcpu->kvm->arch.vgic; - - return vgic_bitmap_get_irq_val(&dist->irq_active, vcpu->vcpu_id, irq); -} - -static void vgic_irq_set_active(struct kvm_vcpu *vcpu, int irq) -{ - struct vgic_dist *dist = &vcpu->kvm->arch.vgic; - - vgic_bitmap_set_irq_val(&dist->irq_active, vcpu->vcpu_id, irq, 1); -} - -static void vgic_irq_clear_active(struct kvm_vcpu *vcpu, int irq) -{ - struct vgic_dist *dist = &vcpu->kvm->arch.vgic; - - vgic_bitmap_set_irq_val(&dist->irq_active, vcpu->vcpu_id, irq, 0); -} - -static int vgic_dist_irq_is_pending(struct kvm_vcpu *vcpu, int irq) -{ - struct vgic_dist *dist = &vcpu->kvm->arch.vgic; - - return vgic_bitmap_get_irq_val(&dist->irq_state, vcpu->vcpu_id, irq); -} - -static void vgic_dist_irq_set(struct kvm_vcpu *vcpu, int irq) -{ - struct vgic_dist *dist = &vcpu->kvm->arch.vgic; - - vgic_bitmap_set_irq_val(&dist->irq_state, vcpu->vcpu_id, irq, 1); -} - -static void vgic_dist_irq_clear(struct kvm_vcpu *vcpu, int irq) -{ - struct vgic_dist *dist = &vcpu->kvm->arch.vgic; - - vgic_bitmap_set_irq_val(&dist->irq_state, vcpu->vcpu_id, irq, 0); -} - -static void vgic_cpu_irq_set(struct kvm_vcpu *vcpu, int irq) -{ - if (irq < VGIC_NR_PRIVATE_IRQS) - set_bit(irq, vcpu->arch.vgic_cpu.pending_percpu); - else - set_bit(irq - VGIC_NR_PRIVATE_IRQS, - vcpu->arch.vgic_cpu.pending_shared); -} - -static void vgic_cpu_irq_clear(struct kvm_vcpu *vcpu, int irq) -{ - if (irq < VGIC_NR_PRIVATE_IRQS) - clear_bit(irq, vcpu->arch.vgic_cpu.pending_percpu); - else - clear_bit(irq - VGIC_NR_PRIVATE_IRQS, - vcpu->arch.vgic_cpu.pending_shared); -} - -static u32 mmio_data_read(struct kvm_exit_mmio *mmio, u32 mask) -{ - return *((u32 *)mmio->data) & mask; -} - -static void mmio_data_write(struct kvm_exit_mmio *mmio, u32 mask, u32 value) -{ - *((u32 *)mmio->data) = value & mask; -} - -/** - * vgic_reg_access - access vgic register - * @mmio: pointer to the data describing the mmio access - * @reg: pointer to the virtual backing of vgic distributor data - * @offset: least significant 2 bits used for word offset - * @mode: ACCESS_ mode (see defines above) - * - * Helper to make vgic register access easier using one of the access - * modes defined for vgic register access - * (read,raz,write-ignored,setbit,clearbit,write) - */ -static void vgic_reg_access(struct kvm_exit_mmio *mmio, u32 *reg, - phys_addr_t offset, int mode) -{ - int word_offset = (offset & 3) * 8; - u32 mask = (1UL << (mmio->len * 8)) - 1; - u32 regval; - - /* - * Any alignment fault should have been delivered to the guest - * directly (ARM ARM B3.12.7 "Prioritization of aborts"). - */ - - if (reg) { - regval = *reg; - } else { - BUG_ON(mode != (ACCESS_READ_RAZ | ACCESS_WRITE_IGNORED)); - regval = 0; - } - - if (mmio->is_write) { - u32 data = mmio_data_read(mmio, mask) << word_offset; - switch (ACCESS_WRITE_MASK(mode)) { - case ACCESS_WRITE_IGNORED: - return; - - case ACCESS_WRITE_SETBIT: - regval |= data; - break; - - case ACCESS_WRITE_CLEARBIT: - regval &= ~data; - break; - - case ACCESS_WRITE_VALUE: - regval = (regval & ~(mask << word_offset)) | data; - break; - } - *reg = regval; - } else { - switch (ACCESS_READ_MASK(mode)) { - case ACCESS_READ_RAZ: - regval = 0; - /* fall through */ - - case ACCESS_READ_VALUE: - mmio_data_write(mmio, mask, regval >> word_offset); - } - } -} - -static bool handle_mmio_misc(struct kvm_vcpu *vcpu, - struct kvm_exit_mmio *mmio, phys_addr_t offset) -{ - u32 reg; - u32 word_offset = offset & 3; - - switch (offset & ~3) { - case 0: /* CTLR */ - reg = vcpu->kvm->arch.vgic.enabled; - vgic_reg_access(mmio, ®, word_offset, - ACCESS_READ_VALUE | ACCESS_WRITE_VALUE); - if (mmio->is_write) { - vcpu->kvm->arch.vgic.enabled = reg & 1; - vgic_update_state(vcpu->kvm); - return true; - } - break; - - case 4: /* TYPER */ - reg = (atomic_read(&vcpu->kvm->online_vcpus) - 1) << 5; - reg |= (VGIC_NR_IRQS >> 5) - 1; - vgic_reg_access(mmio, ®, word_offset, - ACCESS_READ_VALUE | ACCESS_WRITE_IGNORED); - break; - - case 8: /* IIDR */ - reg = 0x4B00043B; - vgic_reg_access(mmio, ®, word_offset, - ACCESS_READ_VALUE | ACCESS_WRITE_IGNORED); - break; - } - - return false; -} - -static bool handle_mmio_raz_wi(struct kvm_vcpu *vcpu, - struct kvm_exit_mmio *mmio, phys_addr_t offset) -{ - vgic_reg_access(mmio, NULL, offset, - ACCESS_READ_RAZ | ACCESS_WRITE_IGNORED); - return false; -} - -static bool handle_mmio_set_enable_reg(struct kvm_vcpu *vcpu, - struct kvm_exit_mmio *mmio, - phys_addr_t offset) -{ - u32 *reg = vgic_bitmap_get_reg(&vcpu->kvm->arch.vgic.irq_enabled, - vcpu->vcpu_id, offset); - vgic_reg_access(mmio, reg, offset, - ACCESS_READ_VALUE | ACCESS_WRITE_SETBIT); - if (mmio->is_write) { - vgic_update_state(vcpu->kvm); - return true; - } - - return false; -} - -static bool handle_mmio_clear_enable_reg(struct kvm_vcpu *vcpu, - struct kvm_exit_mmio *mmio, - phys_addr_t offset) -{ - u32 *reg = vgic_bitmap_get_reg(&vcpu->kvm->arch.vgic.irq_enabled, - vcpu->vcpu_id, offset); - vgic_reg_access(mmio, reg, offset, - ACCESS_READ_VALUE | ACCESS_WRITE_CLEARBIT); - if (mmio->is_write) { - if (offset < 4) /* Force SGI enabled */ - *reg |= 0xffff; - vgic_retire_disabled_irqs(vcpu); - vgic_update_state(vcpu->kvm); - return true; - } - - return false; -} - -static bool handle_mmio_set_pending_reg(struct kvm_vcpu *vcpu, - struct kvm_exit_mmio *mmio, - phys_addr_t offset) -{ - u32 *reg = vgic_bitmap_get_reg(&vcpu->kvm->arch.vgic.irq_state, - vcpu->vcpu_id, offset); - vgic_reg_access(mmio, reg, offset, - ACCESS_READ_VALUE | ACCESS_WRITE_SETBIT); - if (mmio->is_write) { - vgic_update_state(vcpu->kvm); - return true; - } - - return false; -} - -static bool handle_mmio_clear_pending_reg(struct kvm_vcpu *vcpu, - struct kvm_exit_mmio *mmio, - phys_addr_t offset) -{ - u32 *reg = vgic_bitmap_get_reg(&vcpu->kvm->arch.vgic.irq_state, - vcpu->vcpu_id, offset); - vgic_reg_access(mmio, reg, offset, - ACCESS_READ_VALUE | ACCESS_WRITE_CLEARBIT); - if (mmio->is_write) { - vgic_update_state(vcpu->kvm); - return true; - } - - return false; -} - -static bool handle_mmio_priority_reg(struct kvm_vcpu *vcpu, - struct kvm_exit_mmio *mmio, - phys_addr_t offset) -{ - u32 *reg = vgic_bytemap_get_reg(&vcpu->kvm->arch.vgic.irq_priority, - vcpu->vcpu_id, offset); - vgic_reg_access(mmio, reg, offset, - ACCESS_READ_VALUE | ACCESS_WRITE_VALUE); - return false; -} - -#define GICD_ITARGETSR_SIZE 32 -#define GICD_CPUTARGETS_BITS 8 -#define GICD_IRQS_PER_ITARGETSR (GICD_ITARGETSR_SIZE / GICD_CPUTARGETS_BITS) -static u32 vgic_get_target_reg(struct kvm *kvm, int irq) -{ - struct vgic_dist *dist = &kvm->arch.vgic; - struct kvm_vcpu *vcpu; - int i, c; - unsigned long *bmap; - u32 val = 0; - - irq -= VGIC_NR_PRIVATE_IRQS; - - kvm_for_each_vcpu(c, vcpu, kvm) { - bmap = vgic_bitmap_get_shared_map(&dist->irq_spi_target[c]); - for (i = 0; i < GICD_IRQS_PER_ITARGETSR; i++) - if (test_bit(irq + i, bmap)) - val |= 1 << (c + i * 8); - } - - return val; -} - -static void vgic_set_target_reg(struct kvm *kvm, u32 val, int irq) -{ - struct vgic_dist *dist = &kvm->arch.vgic; - struct kvm_vcpu *vcpu; - int i, c; - unsigned long *bmap; - u32 target; - - irq -= VGIC_NR_PRIVATE_IRQS; - - /* - * Pick the LSB in each byte. This ensures we target exactly - * one vcpu per IRQ. If the byte is null, assume we target - * CPU0. - */ - for (i = 0; i < GICD_IRQS_PER_ITARGETSR; i++) { - int shift = i * GICD_CPUTARGETS_BITS; - target = ffs((val >> shift) & 0xffU); - target = target ? (target - 1) : 0; - dist->irq_spi_cpu[irq + i] = target; - kvm_for_each_vcpu(c, vcpu, kvm) { - bmap = vgic_bitmap_get_shared_map(&dist->irq_spi_target[c]); - if (c == target) - set_bit(irq + i, bmap); - else - clear_bit(irq + i, bmap); - } - } -} - -static bool handle_mmio_target_reg(struct kvm_vcpu *vcpu, - struct kvm_exit_mmio *mmio, - phys_addr_t offset) -{ - u32 reg; - - /* We treat the banked interrupts targets as read-only */ - if (offset < 32) { - u32 roreg = 1 << vcpu->vcpu_id; - roreg |= roreg << 8; - roreg |= roreg << 16; - - vgic_reg_access(mmio, &roreg, offset, - ACCESS_READ_VALUE | ACCESS_WRITE_IGNORED); - return false; - } - - reg = vgic_get_target_reg(vcpu->kvm, offset & ~3U); - vgic_reg_access(mmio, ®, offset, - ACCESS_READ_VALUE | ACCESS_WRITE_VALUE); - if (mmio->is_write) { - vgic_set_target_reg(vcpu->kvm, reg, offset & ~3U); - vgic_update_state(vcpu->kvm); - return true; - } - - return false; -} - -static u32 vgic_cfg_expand(u16 val) -{ - u32 res = 0; - int i; - - /* - * Turn a 16bit value like abcd...mnop into a 32bit word - * a0b0c0d0...m0n0o0p0, which is what the HW cfg register is. - */ - for (i = 0; i < 16; i++) - res |= ((val >> i) & VGIC_CFG_EDGE) << (2 * i + 1); - - return res; -} - -static u16 vgic_cfg_compress(u32 val) -{ - u16 res = 0; - int i; - - /* - * Turn a 32bit word a0b0c0d0...m0n0o0p0 into 16bit value like - * abcd...mnop which is what we really care about. - */ - for (i = 0; i < 16; i++) - res |= ((val >> (i * 2 + 1)) & VGIC_CFG_EDGE) << i; - - return res; -} - -/* - * The distributor uses 2 bits per IRQ for the CFG register, but the - * LSB is always 0. As such, we only keep the upper bit, and use the - * two above functions to compress/expand the bits - */ -static bool handle_mmio_cfg_reg(struct kvm_vcpu *vcpu, - struct kvm_exit_mmio *mmio, phys_addr_t offset) -{ - u32 val; - u32 *reg = vgic_bitmap_get_reg(&vcpu->kvm->arch.vgic.irq_cfg, - vcpu->vcpu_id, offset >> 1); - if (offset & 2) - val = *reg >> 16; - else - val = *reg & 0xffff; - - val = vgic_cfg_expand(val); - vgic_reg_access(mmio, &val, offset, - ACCESS_READ_VALUE | ACCESS_WRITE_VALUE); - if (mmio->is_write) { - if (offset < 4) { - *reg = ~0U; /* Force PPIs/SGIs to 1 */ - return false; - } - - val = vgic_cfg_compress(val); - if (offset & 2) { - *reg &= 0xffff; - *reg |= val << 16; - } else { - *reg &= 0xffff << 16; - *reg |= val; - } - } - - return false; -} - -static bool handle_mmio_sgi_reg(struct kvm_vcpu *vcpu, - struct kvm_exit_mmio *mmio, phys_addr_t offset) -{ - u32 reg; - vgic_reg_access(mmio, ®, offset, - ACCESS_READ_RAZ | ACCESS_WRITE_VALUE); - if (mmio->is_write) { - vgic_dispatch_sgi(vcpu, reg); - vgic_update_state(vcpu->kvm); - return true; - } - - return false; -} - -/* - * I would have liked to use the kvm_bus_io_*() API instead, but it - * cannot cope with banked registers (only the VM pointer is passed - * around, and we need the vcpu). One of these days, someone please - * fix it! - */ -struct mmio_range { - phys_addr_t base; - unsigned long len; - bool (*handle_mmio)(struct kvm_vcpu *vcpu, struct kvm_exit_mmio *mmio, - phys_addr_t offset); -}; - -static const struct mmio_range vgic_ranges[] = { - { - .base = GIC_DIST_CTRL, - .len = 12, - .handle_mmio = handle_mmio_misc, - }, - { - .base = GIC_DIST_IGROUP, - .len = VGIC_NR_IRQS / 8, - .handle_mmio = handle_mmio_raz_wi, - }, - { - .base = GIC_DIST_ENABLE_SET, - .len = VGIC_NR_IRQS / 8, - .handle_mmio = handle_mmio_set_enable_reg, - }, - { - .base = GIC_DIST_ENABLE_CLEAR, - .len = VGIC_NR_IRQS / 8, - .handle_mmio = handle_mmio_clear_enable_reg, - }, - { - .base = GIC_DIST_PENDING_SET, - .len = VGIC_NR_IRQS / 8, - .handle_mmio = handle_mmio_set_pending_reg, - }, - { - .base = GIC_DIST_PENDING_CLEAR, - .len = VGIC_NR_IRQS / 8, - .handle_mmio = handle_mmio_clear_pending_reg, - }, - { - .base = GIC_DIST_ACTIVE_SET, - .len = VGIC_NR_IRQS / 8, - .handle_mmio = handle_mmio_raz_wi, - }, - { - .base = GIC_DIST_ACTIVE_CLEAR, - .len = VGIC_NR_IRQS / 8, - .handle_mmio = handle_mmio_raz_wi, - }, - { - .base = GIC_DIST_PRI, - .len = VGIC_NR_IRQS, - .handle_mmio = handle_mmio_priority_reg, - }, - { - .base = GIC_DIST_TARGET, - .len = VGIC_NR_IRQS, - .handle_mmio = handle_mmio_target_reg, - }, - { - .base = GIC_DIST_CONFIG, - .len = VGIC_NR_IRQS / 4, - .handle_mmio = handle_mmio_cfg_reg, - }, - { - .base = GIC_DIST_SOFTINT, - .len = 4, - .handle_mmio = handle_mmio_sgi_reg, - }, - {} -}; - -static const -struct mmio_range *find_matching_range(const struct mmio_range *ranges, - struct kvm_exit_mmio *mmio, - phys_addr_t base) -{ - const struct mmio_range *r = ranges; - phys_addr_t addr = mmio->phys_addr - base; - - while (r->len) { - if (addr >= r->base && - (addr + mmio->len) <= (r->base + r->len)) - return r; - r++; - } - - return NULL; -} - -/** - * vgic_handle_mmio - handle an in-kernel MMIO access - * @vcpu: pointer to the vcpu performing the access - * @run: pointer to the kvm_run structure - * @mmio: pointer to the data describing the access - * - * returns true if the MMIO access has been performed in kernel space, - * and false if it needs to be emulated in user space. - */ -bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, - struct kvm_exit_mmio *mmio) -{ - const struct mmio_range *range; - struct vgic_dist *dist = &vcpu->kvm->arch.vgic; - unsigned long base = dist->vgic_dist_base; - bool updated_state; - unsigned long offset; - - if (!irqchip_in_kernel(vcpu->kvm) || - mmio->phys_addr < base || - (mmio->phys_addr + mmio->len) > (base + KVM_VGIC_V2_DIST_SIZE)) - return false; - - /* We don't support ldrd / strd or ldm / stm to the emulated vgic */ - if (mmio->len > 4) { - kvm_inject_dabt(vcpu, mmio->phys_addr); - return true; - } - - range = find_matching_range(vgic_ranges, mmio, base); - if (unlikely(!range || !range->handle_mmio)) { - pr_warn("Unhandled access %d %08llx %d\n", - mmio->is_write, mmio->phys_addr, mmio->len); - return false; - } - - spin_lock(&vcpu->kvm->arch.vgic.lock); - offset = mmio->phys_addr - range->base - base; - updated_state = range->handle_mmio(vcpu, mmio, offset); - spin_unlock(&vcpu->kvm->arch.vgic.lock); - kvm_prepare_mmio(run, mmio); - kvm_handle_mmio_return(vcpu, run); - - if (updated_state) - vgic_kick_vcpus(vcpu->kvm); - - return true; -} - -static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg) -{ - struct kvm *kvm = vcpu->kvm; - struct vgic_dist *dist = &kvm->arch.vgic; - int nrcpus = atomic_read(&kvm->online_vcpus); - u8 target_cpus; - int sgi, mode, c, vcpu_id; - - vcpu_id = vcpu->vcpu_id; - - sgi = reg & 0xf; - target_cpus = (reg >> 16) & 0xff; - mode = (reg >> 24) & 3; - - switch (mode) { - case 0: - if (!target_cpus) - return; - - case 1: - target_cpus = ((1 << nrcpus) - 1) & ~(1 << vcpu_id) & 0xff; - break; - - case 2: - target_cpus = 1 << vcpu_id; - break; - } - - kvm_for_each_vcpu(c, vcpu, kvm) { - if (target_cpus & 1) { - /* Flag the SGI as pending */ - vgic_dist_irq_set(vcpu, sgi); - dist->irq_sgi_sources[c][sgi] |= 1 << vcpu_id; - kvm_debug("SGI%d from CPU%d to CPU%d\n", sgi, vcpu_id, c); - } - - target_cpus >>= 1; - } -} - -static int compute_pending_for_cpu(struct kvm_vcpu *vcpu) -{ - struct vgic_dist *dist = &vcpu->kvm->arch.vgic; - unsigned long *pending, *enabled, *pend_percpu, *pend_shared; - unsigned long pending_private, pending_shared; - int vcpu_id; - - vcpu_id = vcpu->vcpu_id; - pend_percpu = vcpu->arch.vgic_cpu.pending_percpu; - pend_shared = vcpu->arch.vgic_cpu.pending_shared; - - pending = vgic_bitmap_get_cpu_map(&dist->irq_state, vcpu_id); - enabled = vgic_bitmap_get_cpu_map(&dist->irq_enabled, vcpu_id); - bitmap_and(pend_percpu, pending, enabled, VGIC_NR_PRIVATE_IRQS); - - pending = vgic_bitmap_get_shared_map(&dist->irq_state); - enabled = vgic_bitmap_get_shared_map(&dist->irq_enabled); - bitmap_and(pend_shared, pending, enabled, VGIC_NR_SHARED_IRQS); - bitmap_and(pend_shared, pend_shared, - vgic_bitmap_get_shared_map(&dist->irq_spi_target[vcpu_id]), - VGIC_NR_SHARED_IRQS); - - pending_private = find_first_bit(pend_percpu, VGIC_NR_PRIVATE_IRQS); - pending_shared = find_first_bit(pend_shared, VGIC_NR_SHARED_IRQS); - return (pending_private < VGIC_NR_PRIVATE_IRQS || - pending_shared < VGIC_NR_SHARED_IRQS); -} - -/* - * Update the interrupt state and determine which CPUs have pending - * interrupts. Must be called with distributor lock held. - */ -static void vgic_update_state(struct kvm *kvm) -{ - struct vgic_dist *dist = &kvm->arch.vgic; - struct kvm_vcpu *vcpu; - int c; - - if (!dist->enabled) { - set_bit(0, &dist->irq_pending_on_cpu); - return; - } - - kvm_for_each_vcpu(c, vcpu, kvm) { - if (compute_pending_for_cpu(vcpu)) { - pr_debug("CPU%d has pending interrupts\n", c); - set_bit(c, &dist->irq_pending_on_cpu); - } - } -} - -#define LR_CPUID(lr) \ - (((lr) & GICH_LR_PHYSID_CPUID) >> GICH_LR_PHYSID_CPUID_SHIFT) -#define MK_LR_PEND(src, irq) \ - (GICH_LR_PENDING_BIT | ((src) << GICH_LR_PHYSID_CPUID_SHIFT) | (irq)) - -/* - * An interrupt may have been disabled after being made pending on the - * CPU interface (the classic case is a timer running while we're - * rebooting the guest - the interrupt would kick as soon as the CPU - * interface gets enabled, with deadly consequences). - * - * The solution is to examine already active LRs, and check the - * interrupt is still enabled. If not, just retire it. - */ -static void vgic_retire_disabled_irqs(struct kvm_vcpu *vcpu) -{ - struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; - int lr; - - for_each_set_bit(lr, vgic_cpu->lr_used, vgic_cpu->nr_lr) { - int irq = vgic_cpu->vgic_lr[lr] & GICH_LR_VIRTUALID; - - if (!vgic_irq_is_enabled(vcpu, irq)) { - vgic_cpu->vgic_irq_lr_map[irq] = LR_EMPTY; - clear_bit(lr, vgic_cpu->lr_used); - vgic_cpu->vgic_lr[lr] &= ~GICH_LR_STATE; - if (vgic_irq_is_active(vcpu, irq)) - vgic_irq_clear_active(vcpu, irq); - } - } -} - -/* - * Queue an interrupt to a CPU virtual interface. Return true on success, - * or false if it wasn't possible to queue it. - */ -static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq) -{ - struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; - int lr; - - /* Sanitize the input... */ - BUG_ON(sgi_source_id & ~7); - BUG_ON(sgi_source_id && irq >= VGIC_NR_SGIS); - BUG_ON(irq >= VGIC_NR_IRQS); - - kvm_debug("Queue IRQ%d\n", irq); - - lr = vgic_cpu->vgic_irq_lr_map[irq]; - - /* Do we have an active interrupt for the same CPUID? */ - if (lr != LR_EMPTY && - (LR_CPUID(vgic_cpu->vgic_lr[lr]) == sgi_source_id)) { - kvm_debug("LR%d piggyback for IRQ%d %x\n", - lr, irq, vgic_cpu->vgic_lr[lr]); - BUG_ON(!test_bit(lr, vgic_cpu->lr_used)); - vgic_cpu->vgic_lr[lr] |= GICH_LR_PENDING_BIT; - return true; - } - - /* Try to use another LR for this interrupt */ - lr = find_first_zero_bit((unsigned long *)vgic_cpu->lr_used, - vgic_cpu->nr_lr); - if (lr >= vgic_cpu->nr_lr) - return false; - - kvm_debug("LR%d allocated for IRQ%d %x\n", lr, irq, sgi_source_id); - vgic_cpu->vgic_lr[lr] = MK_LR_PEND(sgi_source_id, irq); - vgic_cpu->vgic_irq_lr_map[irq] = lr; - set_bit(lr, vgic_cpu->lr_used); - - if (!vgic_irq_is_edge(vcpu, irq)) - vgic_cpu->vgic_lr[lr] |= GICH_LR_EOI; - - return true; -} - -static bool vgic_queue_sgi(struct kvm_vcpu *vcpu, int irq) -{ - struct vgic_dist *dist = &vcpu->kvm->arch.vgic; - unsigned long sources; - int vcpu_id = vcpu->vcpu_id; - int c; - - sources = dist->irq_sgi_sources[vcpu_id][irq]; - - for_each_set_bit(c, &sources, VGIC_MAX_CPUS) { - if (vgic_queue_irq(vcpu, c, irq)) - clear_bit(c, &sources); - } - - dist->irq_sgi_sources[vcpu_id][irq] = sources; - - /* - * If the sources bitmap has been cleared it means that we - * could queue all the SGIs onto link registers (see the - * clear_bit above), and therefore we are done with them in - * our emulated gic and can get rid of them. - */ - if (!sources) { - vgic_dist_irq_clear(vcpu, irq); - vgic_cpu_irq_clear(vcpu, irq); - return true; - } - - return false; -} - -static bool vgic_queue_hwirq(struct kvm_vcpu *vcpu, int irq) -{ - if (vgic_irq_is_active(vcpu, irq)) - return true; /* level interrupt, already queued */ - - if (vgic_queue_irq(vcpu, 0, irq)) { - if (vgic_irq_is_edge(vcpu, irq)) { - vgic_dist_irq_clear(vcpu, irq); - vgic_cpu_irq_clear(vcpu, irq); - } else { - vgic_irq_set_active(vcpu, irq); - } - - return true; - } - - return false; -} - -/* - * Fill the list registers with pending interrupts before running the - * guest. - */ -static void __kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu) -{ - struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; - struct vgic_dist *dist = &vcpu->kvm->arch.vgic; - int i, vcpu_id; - int overflow = 0; - - vcpu_id = vcpu->vcpu_id; - - /* - * We may not have any pending interrupt, or the interrupts - * may have been serviced from another vcpu. In all cases, - * move along. - */ - if (!kvm_vgic_vcpu_pending_irq(vcpu)) { - pr_debug("CPU%d has no pending interrupt\n", vcpu_id); - goto epilog; - } - - /* SGIs */ - for_each_set_bit(i, vgic_cpu->pending_percpu, VGIC_NR_SGIS) { - if (!vgic_queue_sgi(vcpu, i)) - overflow = 1; - } - - /* PPIs */ - for_each_set_bit_from(i, vgic_cpu->pending_percpu, VGIC_NR_PRIVATE_IRQS) { - if (!vgic_queue_hwirq(vcpu, i)) - overflow = 1; - } - - /* SPIs */ - for_each_set_bit(i, vgic_cpu->pending_shared, VGIC_NR_SHARED_IRQS) { - if (!vgic_queue_hwirq(vcpu, i + VGIC_NR_PRIVATE_IRQS)) - overflow = 1; - } - -epilog: - if (overflow) { - vgic_cpu->vgic_hcr |= GICH_HCR_UIE; - } else { - vgic_cpu->vgic_hcr &= ~GICH_HCR_UIE; - /* - * We're about to run this VCPU, and we've consumed - * everything the distributor had in store for - * us. Claim we don't have anything pending. We'll - * adjust that if needed while exiting. - */ - clear_bit(vcpu_id, &dist->irq_pending_on_cpu); - } -} - -static bool vgic_process_maintenance(struct kvm_vcpu *vcpu) -{ - struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; - bool level_pending = false; - - kvm_debug("MISR = %08x\n", vgic_cpu->vgic_misr); - - if (vgic_cpu->vgic_misr & GICH_MISR_EOI) { - /* - * Some level interrupts have been EOIed. Clear their - * active bit. - */ - int lr, irq; - - for_each_set_bit(lr, (unsigned long *)vgic_cpu->vgic_eisr, - vgic_cpu->nr_lr) { - irq = vgic_cpu->vgic_lr[lr] & GICH_LR_VIRTUALID; - - vgic_irq_clear_active(vcpu, irq); - vgic_cpu->vgic_lr[lr] &= ~GICH_LR_EOI; - - /* Any additional pending interrupt? */ - if (vgic_dist_irq_is_pending(vcpu, irq)) { - vgic_cpu_irq_set(vcpu, irq); - level_pending = true; - } else { - vgic_cpu_irq_clear(vcpu, irq); - } - - /* - * Despite being EOIed, the LR may not have - * been marked as empty. - */ - set_bit(lr, (unsigned long *)vgic_cpu->vgic_elrsr); - vgic_cpu->vgic_lr[lr] &= ~GICH_LR_ACTIVE_BIT; - } - } - - if (vgic_cpu->vgic_misr & GICH_MISR_U) - vgic_cpu->vgic_hcr &= ~GICH_HCR_UIE; - - return level_pending; -} - -/* - * Sync back the VGIC state after a guest run. The distributor lock is - * needed so we don't get preempted in the middle of the state processing. - */ -static void __kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu) -{ - struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; - struct vgic_dist *dist = &vcpu->kvm->arch.vgic; - int lr, pending; - bool level_pending; - - level_pending = vgic_process_maintenance(vcpu); - - /* Clear mappings for empty LRs */ - for_each_set_bit(lr, (unsigned long *)vgic_cpu->vgic_elrsr, - vgic_cpu->nr_lr) { - int irq; - - if (!test_and_clear_bit(lr, vgic_cpu->lr_used)) - continue; - - irq = vgic_cpu->vgic_lr[lr] & GICH_LR_VIRTUALID; - - BUG_ON(irq >= VGIC_NR_IRQS); - vgic_cpu->vgic_irq_lr_map[irq] = LR_EMPTY; - } - - /* Check if we still have something up our sleeve... */ - pending = find_first_zero_bit((unsigned long *)vgic_cpu->vgic_elrsr, - vgic_cpu->nr_lr); - if (level_pending || pending < vgic_cpu->nr_lr) - set_bit(vcpu->vcpu_id, &dist->irq_pending_on_cpu); -} - -void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu) -{ - struct vgic_dist *dist = &vcpu->kvm->arch.vgic; - - if (!irqchip_in_kernel(vcpu->kvm)) - return; - - spin_lock(&dist->lock); - __kvm_vgic_flush_hwstate(vcpu); - spin_unlock(&dist->lock); -} - -void kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu) -{ - struct vgic_dist *dist = &vcpu->kvm->arch.vgic; - - if (!irqchip_in_kernel(vcpu->kvm)) - return; - - spin_lock(&dist->lock); - __kvm_vgic_sync_hwstate(vcpu); - spin_unlock(&dist->lock); -} - -int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu) -{ - struct vgic_dist *dist = &vcpu->kvm->arch.vgic; - - if (!irqchip_in_kernel(vcpu->kvm)) - return 0; - - return test_bit(vcpu->vcpu_id, &dist->irq_pending_on_cpu); -} - -static void vgic_kick_vcpus(struct kvm *kvm) -{ - struct kvm_vcpu *vcpu; - int c; - - /* - * We've injected an interrupt, time to find out who deserves - * a good kick... - */ - kvm_for_each_vcpu(c, vcpu, kvm) { - if (kvm_vgic_vcpu_pending_irq(vcpu)) - kvm_vcpu_kick(vcpu); - } -} - -static int vgic_validate_injection(struct kvm_vcpu *vcpu, int irq, int level) -{ - int is_edge = vgic_irq_is_edge(vcpu, irq); - int state = vgic_dist_irq_is_pending(vcpu, irq); - - /* - * Only inject an interrupt if: - * - edge triggered and we have a rising edge - * - level triggered and we change level - */ - if (is_edge) - return level > state; - else - return level != state; -} - -static bool vgic_update_irq_state(struct kvm *kvm, int cpuid, - unsigned int irq_num, bool level) -{ - struct vgic_dist *dist = &kvm->arch.vgic; - struct kvm_vcpu *vcpu; - int is_edge, is_level; - int enabled; - bool ret = true; - - spin_lock(&dist->lock); - - vcpu = kvm_get_vcpu(kvm, cpuid); - is_edge = vgic_irq_is_edge(vcpu, irq_num); - is_level = !is_edge; - - if (!vgic_validate_injection(vcpu, irq_num, level)) { - ret = false; - goto out; - } - - if (irq_num >= VGIC_NR_PRIVATE_IRQS) { - cpuid = dist->irq_spi_cpu[irq_num - VGIC_NR_PRIVATE_IRQS]; - vcpu = kvm_get_vcpu(kvm, cpuid); - } - - kvm_debug("Inject IRQ%d level %d CPU%d\n", irq_num, level, cpuid); - - if (level) - vgic_dist_irq_set(vcpu, irq_num); - else - vgic_dist_irq_clear(vcpu, irq_num); - - enabled = vgic_irq_is_enabled(vcpu, irq_num); - - if (!enabled) { - ret = false; - goto out; - } - - if (is_level && vgic_irq_is_active(vcpu, irq_num)) { - /* - * Level interrupt in progress, will be picked up - * when EOId. - */ - ret = false; - goto out; - } - - if (level) { - vgic_cpu_irq_set(vcpu, irq_num); - set_bit(cpuid, &dist->irq_pending_on_cpu); - } - -out: - spin_unlock(&dist->lock); - - return ret; -} - -/** - * kvm_vgic_inject_irq - Inject an IRQ from a device to the vgic - * @kvm: The VM structure pointer - * @cpuid: The CPU for PPIs - * @irq_num: The IRQ number that is assigned to the device - * @level: Edge-triggered: true: to trigger the interrupt - * false: to ignore the call - * Level-sensitive true: activates an interrupt - * false: deactivates an interrupt - * - * The GIC is not concerned with devices being active-LOW or active-HIGH for - * level-sensitive interrupts. You can think of the level parameter as 1 - * being HIGH and 0 being LOW and all devices being active-HIGH. - */ -int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int irq_num, - bool level) -{ - if (vgic_update_irq_state(kvm, cpuid, irq_num, level)) - vgic_kick_vcpus(kvm); - - return 0; -} - -static irqreturn_t vgic_maintenance_handler(int irq, void *data) -{ - /* - * We cannot rely on the vgic maintenance interrupt to be - * delivered synchronously. This means we can only use it to - * exit the VM, and we perform the handling of EOIed - * interrupts on the exit path (see vgic_process_maintenance). - */ - return IRQ_HANDLED; -} - -int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu) -{ - struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; - struct vgic_dist *dist = &vcpu->kvm->arch.vgic; - int i; - - if (!irqchip_in_kernel(vcpu->kvm)) - return 0; - - if (vcpu->vcpu_id >= VGIC_MAX_CPUS) - return -EBUSY; - - for (i = 0; i < VGIC_NR_IRQS; i++) { - if (i < VGIC_NR_PPIS) - vgic_bitmap_set_irq_val(&dist->irq_enabled, - vcpu->vcpu_id, i, 1); - if (i < VGIC_NR_PRIVATE_IRQS) - vgic_bitmap_set_irq_val(&dist->irq_cfg, - vcpu->vcpu_id, i, VGIC_CFG_EDGE); - - vgic_cpu->vgic_irq_lr_map[i] = LR_EMPTY; - } - - /* - * By forcing VMCR to zero, the GIC will restore the binary - * points to their reset values. Anything else resets to zero - * anyway. - */ - vgic_cpu->vgic_vmcr = 0; - - vgic_cpu->nr_lr = vgic_nr_lr; - vgic_cpu->vgic_hcr = GICH_HCR_EN; /* Get the show on the road... */ - - return 0; -} - -static void vgic_init_maintenance_interrupt(void *info) -{ - enable_percpu_irq(vgic_maint_irq, 0); -} - -static int vgic_cpu_notify(struct notifier_block *self, - unsigned long action, void *cpu) -{ - switch (action) { - case CPU_STARTING: - case CPU_STARTING_FROZEN: - vgic_init_maintenance_interrupt(NULL); - break; - case CPU_DYING: - case CPU_DYING_FROZEN: - disable_percpu_irq(vgic_maint_irq); - break; - } - - return NOTIFY_OK; -} - -static struct notifier_block vgic_cpu_nb = { - .notifier_call = vgic_cpu_notify, -}; - -int kvm_vgic_hyp_init(void) -{ - int ret; - struct resource vctrl_res; - struct resource vcpu_res; - - vgic_node = of_find_compatible_node(NULL, NULL, "arm,cortex-a15-gic"); - if (!vgic_node) { - kvm_err("error: no compatible vgic node in DT\n"); - return -ENODEV; - } - - vgic_maint_irq = irq_of_parse_and_map(vgic_node, 0); - if (!vgic_maint_irq) { - kvm_err("error getting vgic maintenance irq from DT\n"); - ret = -ENXIO; - goto out; - } - - ret = request_percpu_irq(vgic_maint_irq, vgic_maintenance_handler, - "vgic", kvm_get_running_vcpus()); - if (ret) { - kvm_err("Cannot register interrupt %d\n", vgic_maint_irq); - goto out; - } - - ret = register_cpu_notifier(&vgic_cpu_nb); - if (ret) { - kvm_err("Cannot register vgic CPU notifier\n"); - goto out_free_irq; - } - - ret = of_address_to_resource(vgic_node, 2, &vctrl_res); - if (ret) { - kvm_err("Cannot obtain VCTRL resource\n"); - goto out_free_irq; - } - - vgic_vctrl_base = of_iomap(vgic_node, 2); - if (!vgic_vctrl_base) { - kvm_err("Cannot ioremap VCTRL\n"); - ret = -ENOMEM; - goto out_free_irq; - } - - vgic_nr_lr = readl_relaxed(vgic_vctrl_base + GICH_VTR); - vgic_nr_lr = (vgic_nr_lr & 0x3f) + 1; - - ret = create_hyp_io_mappings(vgic_vctrl_base, - vgic_vctrl_base + resource_size(&vctrl_res), - vctrl_res.start); - if (ret) { - kvm_err("Cannot map VCTRL into hyp\n"); - goto out_unmap; - } - - kvm_info("%s@%llx IRQ%d\n", vgic_node->name, - vctrl_res.start, vgic_maint_irq); - on_each_cpu(vgic_init_maintenance_interrupt, NULL, 1); - - if (of_address_to_resource(vgic_node, 3, &vcpu_res)) { - kvm_err("Cannot obtain VCPU resource\n"); - ret = -ENXIO; - goto out_unmap; - } - vgic_vcpu_base = vcpu_res.start; - - goto out; - -out_unmap: - iounmap(vgic_vctrl_base); -out_free_irq: - free_percpu_irq(vgic_maint_irq, kvm_get_running_vcpus()); -out: - of_node_put(vgic_node); - return ret; -} - -int kvm_vgic_init(struct kvm *kvm) -{ - int ret = 0, i; - - mutex_lock(&kvm->lock); - - if (vgic_initialized(kvm)) - goto out; - - if (IS_VGIC_ADDR_UNDEF(kvm->arch.vgic.vgic_dist_base) || - IS_VGIC_ADDR_UNDEF(kvm->arch.vgic.vgic_cpu_base)) { - kvm_err("Need to set vgic cpu and dist addresses first\n"); - ret = -ENXIO; - goto out; - } - - ret = kvm_phys_addr_ioremap(kvm, kvm->arch.vgic.vgic_cpu_base, - vgic_vcpu_base, KVM_VGIC_V2_CPU_SIZE); - if (ret) { - kvm_err("Unable to remap VGIC CPU to VCPU\n"); - goto out; - } - - for (i = VGIC_NR_PRIVATE_IRQS; i < VGIC_NR_IRQS; i += 4) - vgic_set_target_reg(kvm, 0, i); - - kvm_timer_init(kvm); - kvm->arch.vgic.ready = true; -out: - mutex_unlock(&kvm->lock); - return ret; -} - -int kvm_vgic_create(struct kvm *kvm) -{ - int ret = 0; - - mutex_lock(&kvm->lock); - - if (atomic_read(&kvm->online_vcpus) || kvm->arch.vgic.vctrl_base) { - ret = -EEXIST; - goto out; - } - - spin_lock_init(&kvm->arch.vgic.lock); - kvm->arch.vgic.vctrl_base = vgic_vctrl_base; - kvm->arch.vgic.vgic_dist_base = VGIC_ADDR_UNDEF; - kvm->arch.vgic.vgic_cpu_base = VGIC_ADDR_UNDEF; - -out: - mutex_unlock(&kvm->lock); - return ret; -} - -static bool vgic_ioaddr_overlap(struct kvm *kvm) -{ - phys_addr_t dist = kvm->arch.vgic.vgic_dist_base; - phys_addr_t cpu = kvm->arch.vgic.vgic_cpu_base; - - if (IS_VGIC_ADDR_UNDEF(dist) || IS_VGIC_ADDR_UNDEF(cpu)) - return 0; - if ((dist <= cpu && dist + KVM_VGIC_V2_DIST_SIZE > cpu) || - (cpu <= dist && cpu + KVM_VGIC_V2_CPU_SIZE > dist)) - return -EBUSY; - return 0; -} - -static int vgic_ioaddr_assign(struct kvm *kvm, phys_addr_t *ioaddr, - phys_addr_t addr, phys_addr_t size) -{ - int ret; - - if (!IS_VGIC_ADDR_UNDEF(*ioaddr)) - return -EEXIST; - if (addr + size < addr) - return -EINVAL; - - ret = vgic_ioaddr_overlap(kvm); - if (ret) - return ret; - *ioaddr = addr; - return ret; -} - -int kvm_vgic_set_addr(struct kvm *kvm, unsigned long type, u64 addr) -{ - int r = 0; - struct vgic_dist *vgic = &kvm->arch.vgic; - - if (addr & ~KVM_PHYS_MASK) - return -E2BIG; - - if (addr & (SZ_4K - 1)) - return -EINVAL; - - mutex_lock(&kvm->lock); - switch (type) { - case KVM_VGIC_V2_ADDR_TYPE_DIST: - r = vgic_ioaddr_assign(kvm, &vgic->vgic_dist_base, - addr, KVM_VGIC_V2_DIST_SIZE); - break; - case KVM_VGIC_V2_ADDR_TYPE_CPU: - r = vgic_ioaddr_assign(kvm, &vgic->vgic_cpu_base, - addr, KVM_VGIC_V2_CPU_SIZE); - break; - default: - r = -ENODEV; - } - - mutex_unlock(&kvm->lock); - return r; -} diff --git a/include/kvm/arm_arch_timer.h b/include/kvm/arm_arch_timer.h new file mode 100644 index 000000000000..68cb9e1dfb81 --- /dev/null +++ b/include/kvm/arm_arch_timer.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2012 ARM Ltd. + * Author: Marc Zyngier + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __ASM_ARM_KVM_ARCH_TIMER_H +#define __ASM_ARM_KVM_ARCH_TIMER_H + +#include +#include +#include + +struct arch_timer_kvm { +#ifdef CONFIG_KVM_ARM_TIMER + /* Is the timer enabled */ + bool enabled; + + /* Virtual offset */ + cycle_t cntvoff; +#endif +}; + +struct arch_timer_cpu { +#ifdef CONFIG_KVM_ARM_TIMER + /* Registers: control register, timer value */ + u32 cntv_ctl; /* Saved/restored */ + cycle_t cntv_cval; /* Saved/restored */ + + /* + * Anything that is not used directly from assembly code goes + * here. + */ + + /* Background timer used when the guest is not running */ + struct hrtimer timer; + + /* Work queued with the above timer expires */ + struct work_struct expired; + + /* Background timer active */ + bool armed; + + /* Timer IRQ */ + const struct kvm_irq_level *irq; +#endif +}; + +#ifdef CONFIG_KVM_ARM_TIMER +int kvm_timer_hyp_init(void); +int kvm_timer_init(struct kvm *kvm); +void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu); +void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu); +void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu); +void kvm_timer_vcpu_terminate(struct kvm_vcpu *vcpu); +#else +static inline int kvm_timer_hyp_init(void) +{ + return 0; +}; + +static inline int kvm_timer_init(struct kvm *kvm) +{ + return 0; +} + +static inline void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu) {} +static inline void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu) {} +static inline void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu) {} +static inline void kvm_timer_vcpu_terminate(struct kvm_vcpu *vcpu) {} +#endif + +#endif diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h new file mode 100644 index 000000000000..343744e4809c --- /dev/null +++ b/include/kvm/arm_vgic.h @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2012 ARM Ltd. + * Author: Marc Zyngier + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __ASM_ARM_KVM_VGIC_H +#define __ASM_ARM_KVM_VGIC_H + +#include +#include +#include +#include +#include +#include + +#define VGIC_NR_IRQS 128 +#define VGIC_NR_SGIS 16 +#define VGIC_NR_PPIS 16 +#define VGIC_NR_PRIVATE_IRQS (VGIC_NR_SGIS + VGIC_NR_PPIS) +#define VGIC_NR_SHARED_IRQS (VGIC_NR_IRQS - VGIC_NR_PRIVATE_IRQS) +#define VGIC_MAX_CPUS KVM_MAX_VCPUS +#define VGIC_MAX_LRS (1 << 6) + +/* Sanity checks... */ +#if (VGIC_MAX_CPUS > 8) +#error Invalid number of CPU interfaces +#endif + +#if (VGIC_NR_IRQS & 31) +#error "VGIC_NR_IRQS must be a multiple of 32" +#endif + +#if (VGIC_NR_IRQS > 1024) +#error "VGIC_NR_IRQS must be <= 1024" +#endif + +/* + * The GIC distributor registers describing interrupts have two parts: + * - 32 per-CPU interrupts (SGI + PPI) + * - a bunch of shared interrupts (SPI) + */ +struct vgic_bitmap { + union { + u32 reg[VGIC_NR_PRIVATE_IRQS / 32]; + DECLARE_BITMAP(reg_ul, VGIC_NR_PRIVATE_IRQS); + } percpu[VGIC_MAX_CPUS]; + union { + u32 reg[VGIC_NR_SHARED_IRQS / 32]; + DECLARE_BITMAP(reg_ul, VGIC_NR_SHARED_IRQS); + } shared; +}; + +struct vgic_bytemap { + u32 percpu[VGIC_MAX_CPUS][VGIC_NR_PRIVATE_IRQS / 4]; + u32 shared[VGIC_NR_SHARED_IRQS / 4]; +}; + +struct vgic_dist { +#ifdef CONFIG_KVM_ARM_VGIC + spinlock_t lock; + bool ready; + + /* Virtual control interface mapping */ + void __iomem *vctrl_base; + + /* Distributor and vcpu interface mapping in the guest */ + phys_addr_t vgic_dist_base; + phys_addr_t vgic_cpu_base; + + /* Distributor enabled */ + u32 enabled; + + /* Interrupt enabled (one bit per IRQ) */ + struct vgic_bitmap irq_enabled; + + /* Interrupt 'pin' level */ + struct vgic_bitmap irq_state; + + /* Level-triggered interrupt in progress */ + struct vgic_bitmap irq_active; + + /* Interrupt priority. Not used yet. */ + struct vgic_bytemap irq_priority; + + /* Level/edge triggered */ + struct vgic_bitmap irq_cfg; + + /* Source CPU per SGI and target CPU */ + u8 irq_sgi_sources[VGIC_MAX_CPUS][VGIC_NR_SGIS]; + + /* Target CPU for each IRQ */ + u8 irq_spi_cpu[VGIC_NR_SHARED_IRQS]; + struct vgic_bitmap irq_spi_target[VGIC_MAX_CPUS]; + + /* Bitmap indicating which CPU has something pending */ + unsigned long irq_pending_on_cpu; +#endif +}; + +struct vgic_cpu { +#ifdef CONFIG_KVM_ARM_VGIC + /* per IRQ to LR mapping */ + u8 vgic_irq_lr_map[VGIC_NR_IRQS]; + + /* Pending interrupts on this VCPU */ + DECLARE_BITMAP( pending_percpu, VGIC_NR_PRIVATE_IRQS); + DECLARE_BITMAP( pending_shared, VGIC_NR_SHARED_IRQS); + + /* Bitmap of used/free list registers */ + DECLARE_BITMAP( lr_used, VGIC_MAX_LRS); + + /* Number of list registers on this CPU */ + int nr_lr; + + /* CPU vif control registers for world switch */ + u32 vgic_hcr; + u32 vgic_vmcr; + u32 vgic_misr; /* Saved only */ + u32 vgic_eisr[2]; /* Saved only */ + u32 vgic_elrsr[2]; /* Saved only */ + u32 vgic_apr; + u32 vgic_lr[VGIC_MAX_LRS]; +#endif +}; + +#define LR_EMPTY 0xff + +struct kvm; +struct kvm_vcpu; +struct kvm_run; +struct kvm_exit_mmio; + +#ifdef CONFIG_KVM_ARM_VGIC +int kvm_vgic_set_addr(struct kvm *kvm, unsigned long type, u64 addr); +int kvm_vgic_hyp_init(void); +int kvm_vgic_init(struct kvm *kvm); +int kvm_vgic_create(struct kvm *kvm); +int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu); +void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu); +void kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu); +int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int irq_num, + bool level); +int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu); +bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, + struct kvm_exit_mmio *mmio); + +#define irqchip_in_kernel(k) (!!((k)->arch.vgic.vctrl_base)) +#define vgic_initialized(k) ((k)->arch.vgic.ready) + +#else +static inline int kvm_vgic_hyp_init(void) +{ + return 0; +} + +static inline int kvm_vgic_set_addr(struct kvm *kvm, unsigned long type, u64 addr) +{ + return 0; +} + +static inline int kvm_vgic_init(struct kvm *kvm) +{ + return 0; +} + +static inline int kvm_vgic_create(struct kvm *kvm) +{ + return 0; +} + +static inline int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu) +{ + return 0; +} + +static inline void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu) {} +static inline void kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu) {} + +static inline int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, + unsigned int irq_num, bool level) +{ + return 0; +} + +static inline int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu) +{ + return 0; +} + +static inline bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, + struct kvm_exit_mmio *mmio) +{ + return false; +} + +static inline int irqchip_in_kernel(struct kvm *kvm) +{ + return 0; +} + +static inline bool vgic_initialized(struct kvm *kvm) +{ + return true; +} +#endif + +#endif diff --git a/virt/kvm/arm/arch_timer.c b/virt/kvm/arm/arch_timer.c new file mode 100644 index 000000000000..2d00b2925780 --- /dev/null +++ b/virt/kvm/arm/arch_timer.c @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2012 ARM Ltd. + * Author: Marc Zyngier + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +static struct timecounter *timecounter; +static struct workqueue_struct *wqueue; +static struct kvm_irq_level timer_irq = { + .level = 1, +}; + +static cycle_t kvm_phys_timer_read(void) +{ + return timecounter->cc->read(timecounter->cc); +} + +static bool timer_is_armed(struct arch_timer_cpu *timer) +{ + return timer->armed; +} + +/* timer_arm: as in "arm the timer", not as in ARM the company */ +static void timer_arm(struct arch_timer_cpu *timer, u64 ns) +{ + timer->armed = true; + hrtimer_start(&timer->timer, ktime_add_ns(ktime_get(), ns), + HRTIMER_MODE_ABS); +} + +static void timer_disarm(struct arch_timer_cpu *timer) +{ + if (timer_is_armed(timer)) { + hrtimer_cancel(&timer->timer); + cancel_work_sync(&timer->expired); + timer->armed = false; + } +} + +static void kvm_timer_inject_irq(struct kvm_vcpu *vcpu) +{ + struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu; + + timer->cntv_ctl |= ARCH_TIMER_CTRL_IT_MASK; + kvm_vgic_inject_irq(vcpu->kvm, vcpu->vcpu_id, + vcpu->arch.timer_cpu.irq->irq, + vcpu->arch.timer_cpu.irq->level); +} + +static irqreturn_t kvm_arch_timer_handler(int irq, void *dev_id) +{ + struct kvm_vcpu *vcpu = *(struct kvm_vcpu **)dev_id; + + /* + * We disable the timer in the world switch and let it be + * handled by kvm_timer_sync_hwstate(). Getting a timer + * interrupt at this point is a sure sign of some major + * breakage. + */ + pr_warn("Unexpected interrupt %d on vcpu %p\n", irq, vcpu); + return IRQ_HANDLED; +} + +static void kvm_timer_inject_irq_work(struct work_struct *work) +{ + struct kvm_vcpu *vcpu; + + vcpu = container_of(work, struct kvm_vcpu, arch.timer_cpu.expired); + vcpu->arch.timer_cpu.armed = false; + kvm_timer_inject_irq(vcpu); +} + +static enum hrtimer_restart kvm_timer_expire(struct hrtimer *hrt) +{ + struct arch_timer_cpu *timer; + timer = container_of(hrt, struct arch_timer_cpu, timer); + queue_work(wqueue, &timer->expired); + return HRTIMER_NORESTART; +} + +/** + * kvm_timer_flush_hwstate - prepare to move the virt timer to the cpu + * @vcpu: The vcpu pointer + * + * Disarm any pending soft timers, since the world-switch code will write the + * virtual timer state back to the physical CPU. + */ +void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu) +{ + struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu; + + /* + * We're about to run this vcpu again, so there is no need to + * keep the background timer running, as we're about to + * populate the CPU timer again. + */ + timer_disarm(timer); +} + +/** + * kvm_timer_sync_hwstate - sync timer state from cpu + * @vcpu: The vcpu pointer + * + * Check if the virtual timer was armed and either schedule a corresponding + * soft timer or inject directly if already expired. + */ +void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu) +{ + struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu; + cycle_t cval, now; + u64 ns; + + if ((timer->cntv_ctl & ARCH_TIMER_CTRL_IT_MASK) || + !(timer->cntv_ctl & ARCH_TIMER_CTRL_ENABLE)) + return; + + cval = timer->cntv_cval; + now = kvm_phys_timer_read() - vcpu->kvm->arch.timer.cntvoff; + + BUG_ON(timer_is_armed(timer)); + + if (cval <= now) { + /* + * Timer has already expired while we were not + * looking. Inject the interrupt and carry on. + */ + kvm_timer_inject_irq(vcpu); + return; + } + + ns = cyclecounter_cyc2ns(timecounter->cc, cval - now); + timer_arm(timer, ns); +} + +void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu) +{ + struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu; + + INIT_WORK(&timer->expired, kvm_timer_inject_irq_work); + hrtimer_init(&timer->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); + timer->timer.function = kvm_timer_expire; + timer->irq = &timer_irq; +} + +static void kvm_timer_init_interrupt(void *info) +{ + enable_percpu_irq(timer_irq.irq, 0); +} + + +static int kvm_timer_cpu_notify(struct notifier_block *self, + unsigned long action, void *cpu) +{ + switch (action) { + case CPU_STARTING: + case CPU_STARTING_FROZEN: + kvm_timer_init_interrupt(NULL); + break; + case CPU_DYING: + case CPU_DYING_FROZEN: + disable_percpu_irq(timer_irq.irq); + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block kvm_timer_cpu_nb = { + .notifier_call = kvm_timer_cpu_notify, +}; + +static const struct of_device_id arch_timer_of_match[] = { + { .compatible = "arm,armv7-timer", }, + {}, +}; + +int kvm_timer_hyp_init(void) +{ + struct device_node *np; + unsigned int ppi; + int err; + + timecounter = arch_timer_get_timecounter(); + if (!timecounter) + return -ENODEV; + + np = of_find_matching_node(NULL, arch_timer_of_match); + if (!np) { + kvm_err("kvm_arch_timer: can't find DT node\n"); + return -ENODEV; + } + + ppi = irq_of_parse_and_map(np, 2); + if (!ppi) { + kvm_err("kvm_arch_timer: no virtual timer interrupt\n"); + err = -EINVAL; + goto out; + } + + err = request_percpu_irq(ppi, kvm_arch_timer_handler, + "kvm guest timer", kvm_get_running_vcpus()); + if (err) { + kvm_err("kvm_arch_timer: can't request interrupt %d (%d)\n", + ppi, err); + goto out; + } + + timer_irq.irq = ppi; + + err = register_cpu_notifier(&kvm_timer_cpu_nb); + if (err) { + kvm_err("Cannot register timer CPU notifier\n"); + goto out_free; + } + + wqueue = create_singlethread_workqueue("kvm_arch_timer"); + if (!wqueue) { + err = -ENOMEM; + goto out_free; + } + + kvm_info("%s IRQ%d\n", np->name, ppi); + on_each_cpu(kvm_timer_init_interrupt, NULL, 1); + + goto out; +out_free: + free_percpu_irq(ppi, kvm_get_running_vcpus()); +out: + of_node_put(np); + return err; +} + +void kvm_timer_vcpu_terminate(struct kvm_vcpu *vcpu) +{ + struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu; + + timer_disarm(timer); +} + +int kvm_timer_init(struct kvm *kvm) +{ + if (timecounter && wqueue) { + kvm->arch.timer.cntvoff = kvm_phys_timer_read(); + kvm->arch.timer.enabled = 1; + } + + return 0; +} diff --git a/virt/kvm/arm/vgic.c b/virt/kvm/arm/vgic.c new file mode 100644 index 000000000000..17c5ac7d10ed --- /dev/null +++ b/virt/kvm/arm/vgic.c @@ -0,0 +1,1499 @@ +/* + * Copyright (C) 2012 ARM Ltd. + * Author: Marc Zyngier + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +/* + * How the whole thing works (courtesy of Christoffer Dall): + * + * - At any time, the dist->irq_pending_on_cpu is the oracle that knows if + * something is pending + * - VGIC pending interrupts are stored on the vgic.irq_state vgic + * bitmap (this bitmap is updated by both user land ioctls and guest + * mmio ops, and other in-kernel peripherals such as the + * arch. timers) and indicate the 'wire' state. + * - Every time the bitmap changes, the irq_pending_on_cpu oracle is + * recalculated + * - To calculate the oracle, we need info for each cpu from + * compute_pending_for_cpu, which considers: + * - PPI: dist->irq_state & dist->irq_enable + * - SPI: dist->irq_state & dist->irq_enable & dist->irq_spi_target + * - irq_spi_target is a 'formatted' version of the GICD_ICFGR + * registers, stored on each vcpu. We only keep one bit of + * information per interrupt, making sure that only one vcpu can + * accept the interrupt. + * - The same is true when injecting an interrupt, except that we only + * consider a single interrupt at a time. The irq_spi_cpu array + * contains the target CPU for each SPI. + * + * The handling of level interrupts adds some extra complexity. We + * need to track when the interrupt has been EOIed, so we can sample + * the 'line' again. This is achieved as such: + * + * - When a level interrupt is moved onto a vcpu, the corresponding + * bit in irq_active is set. As long as this bit is set, the line + * will be ignored for further interrupts. The interrupt is injected + * into the vcpu with the GICH_LR_EOI bit set (generate a + * maintenance interrupt on EOI). + * - When the interrupt is EOIed, the maintenance interrupt fires, + * and clears the corresponding bit in irq_active. This allow the + * interrupt line to be sampled again. + */ + +#define VGIC_ADDR_UNDEF (-1) +#define IS_VGIC_ADDR_UNDEF(_x) ((_x) == VGIC_ADDR_UNDEF) + +/* Physical address of vgic virtual cpu interface */ +static phys_addr_t vgic_vcpu_base; + +/* Virtual control interface base address */ +static void __iomem *vgic_vctrl_base; + +static struct device_node *vgic_node; + +#define ACCESS_READ_VALUE (1 << 0) +#define ACCESS_READ_RAZ (0 << 0) +#define ACCESS_READ_MASK(x) ((x) & (1 << 0)) +#define ACCESS_WRITE_IGNORED (0 << 1) +#define ACCESS_WRITE_SETBIT (1 << 1) +#define ACCESS_WRITE_CLEARBIT (2 << 1) +#define ACCESS_WRITE_VALUE (3 << 1) +#define ACCESS_WRITE_MASK(x) ((x) & (3 << 1)) + +static void vgic_retire_disabled_irqs(struct kvm_vcpu *vcpu); +static void vgic_update_state(struct kvm *kvm); +static void vgic_kick_vcpus(struct kvm *kvm); +static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg); +static u32 vgic_nr_lr; + +static unsigned int vgic_maint_irq; + +static u32 *vgic_bitmap_get_reg(struct vgic_bitmap *x, + int cpuid, u32 offset) +{ + offset >>= 2; + if (!offset) + return x->percpu[cpuid].reg; + else + return x->shared.reg + offset - 1; +} + +static int vgic_bitmap_get_irq_val(struct vgic_bitmap *x, + int cpuid, int irq) +{ + if (irq < VGIC_NR_PRIVATE_IRQS) + return test_bit(irq, x->percpu[cpuid].reg_ul); + + return test_bit(irq - VGIC_NR_PRIVATE_IRQS, x->shared.reg_ul); +} + +static void vgic_bitmap_set_irq_val(struct vgic_bitmap *x, int cpuid, + int irq, int val) +{ + unsigned long *reg; + + if (irq < VGIC_NR_PRIVATE_IRQS) { + reg = x->percpu[cpuid].reg_ul; + } else { + reg = x->shared.reg_ul; + irq -= VGIC_NR_PRIVATE_IRQS; + } + + if (val) + set_bit(irq, reg); + else + clear_bit(irq, reg); +} + +static unsigned long *vgic_bitmap_get_cpu_map(struct vgic_bitmap *x, int cpuid) +{ + if (unlikely(cpuid >= VGIC_MAX_CPUS)) + return NULL; + return x->percpu[cpuid].reg_ul; +} + +static unsigned long *vgic_bitmap_get_shared_map(struct vgic_bitmap *x) +{ + return x->shared.reg_ul; +} + +static u32 *vgic_bytemap_get_reg(struct vgic_bytemap *x, int cpuid, u32 offset) +{ + offset >>= 2; + BUG_ON(offset > (VGIC_NR_IRQS / 4)); + if (offset < 4) + return x->percpu[cpuid] + offset; + else + return x->shared + offset - 8; +} + +#define VGIC_CFG_LEVEL 0 +#define VGIC_CFG_EDGE 1 + +static bool vgic_irq_is_edge(struct kvm_vcpu *vcpu, int irq) +{ + struct vgic_dist *dist = &vcpu->kvm->arch.vgic; + int irq_val; + + irq_val = vgic_bitmap_get_irq_val(&dist->irq_cfg, vcpu->vcpu_id, irq); + return irq_val == VGIC_CFG_EDGE; +} + +static int vgic_irq_is_enabled(struct kvm_vcpu *vcpu, int irq) +{ + struct vgic_dist *dist = &vcpu->kvm->arch.vgic; + + return vgic_bitmap_get_irq_val(&dist->irq_enabled, vcpu->vcpu_id, irq); +} + +static int vgic_irq_is_active(struct kvm_vcpu *vcpu, int irq) +{ + struct vgic_dist *dist = &vcpu->kvm->arch.vgic; + + return vgic_bitmap_get_irq_val(&dist->irq_active, vcpu->vcpu_id, irq); +} + +static void vgic_irq_set_active(struct kvm_vcpu *vcpu, int irq) +{ + struct vgic_dist *dist = &vcpu->kvm->arch.vgic; + + vgic_bitmap_set_irq_val(&dist->irq_active, vcpu->vcpu_id, irq, 1); +} + +static void vgic_irq_clear_active(struct kvm_vcpu *vcpu, int irq) +{ + struct vgic_dist *dist = &vcpu->kvm->arch.vgic; + + vgic_bitmap_set_irq_val(&dist->irq_active, vcpu->vcpu_id, irq, 0); +} + +static int vgic_dist_irq_is_pending(struct kvm_vcpu *vcpu, int irq) +{ + struct vgic_dist *dist = &vcpu->kvm->arch.vgic; + + return vgic_bitmap_get_irq_val(&dist->irq_state, vcpu->vcpu_id, irq); +} + +static void vgic_dist_irq_set(struct kvm_vcpu *vcpu, int irq) +{ + struct vgic_dist *dist = &vcpu->kvm->arch.vgic; + + vgic_bitmap_set_irq_val(&dist->irq_state, vcpu->vcpu_id, irq, 1); +} + +static void vgic_dist_irq_clear(struct kvm_vcpu *vcpu, int irq) +{ + struct vgic_dist *dist = &vcpu->kvm->arch.vgic; + + vgic_bitmap_set_irq_val(&dist->irq_state, vcpu->vcpu_id, irq, 0); +} + +static void vgic_cpu_irq_set(struct kvm_vcpu *vcpu, int irq) +{ + if (irq < VGIC_NR_PRIVATE_IRQS) + set_bit(irq, vcpu->arch.vgic_cpu.pending_percpu); + else + set_bit(irq - VGIC_NR_PRIVATE_IRQS, + vcpu->arch.vgic_cpu.pending_shared); +} + +static void vgic_cpu_irq_clear(struct kvm_vcpu *vcpu, int irq) +{ + if (irq < VGIC_NR_PRIVATE_IRQS) + clear_bit(irq, vcpu->arch.vgic_cpu.pending_percpu); + else + clear_bit(irq - VGIC_NR_PRIVATE_IRQS, + vcpu->arch.vgic_cpu.pending_shared); +} + +static u32 mmio_data_read(struct kvm_exit_mmio *mmio, u32 mask) +{ + return *((u32 *)mmio->data) & mask; +} + +static void mmio_data_write(struct kvm_exit_mmio *mmio, u32 mask, u32 value) +{ + *((u32 *)mmio->data) = value & mask; +} + +/** + * vgic_reg_access - access vgic register + * @mmio: pointer to the data describing the mmio access + * @reg: pointer to the virtual backing of vgic distributor data + * @offset: least significant 2 bits used for word offset + * @mode: ACCESS_ mode (see defines above) + * + * Helper to make vgic register access easier using one of the access + * modes defined for vgic register access + * (read,raz,write-ignored,setbit,clearbit,write) + */ +static void vgic_reg_access(struct kvm_exit_mmio *mmio, u32 *reg, + phys_addr_t offset, int mode) +{ + int word_offset = (offset & 3) * 8; + u32 mask = (1UL << (mmio->len * 8)) - 1; + u32 regval; + + /* + * Any alignment fault should have been delivered to the guest + * directly (ARM ARM B3.12.7 "Prioritization of aborts"). + */ + + if (reg) { + regval = *reg; + } else { + BUG_ON(mode != (ACCESS_READ_RAZ | ACCESS_WRITE_IGNORED)); + regval = 0; + } + + if (mmio->is_write) { + u32 data = mmio_data_read(mmio, mask) << word_offset; + switch (ACCESS_WRITE_MASK(mode)) { + case ACCESS_WRITE_IGNORED: + return; + + case ACCESS_WRITE_SETBIT: + regval |= data; + break; + + case ACCESS_WRITE_CLEARBIT: + regval &= ~data; + break; + + case ACCESS_WRITE_VALUE: + regval = (regval & ~(mask << word_offset)) | data; + break; + } + *reg = regval; + } else { + switch (ACCESS_READ_MASK(mode)) { + case ACCESS_READ_RAZ: + regval = 0; + /* fall through */ + + case ACCESS_READ_VALUE: + mmio_data_write(mmio, mask, regval >> word_offset); + } + } +} + +static bool handle_mmio_misc(struct kvm_vcpu *vcpu, + struct kvm_exit_mmio *mmio, phys_addr_t offset) +{ + u32 reg; + u32 word_offset = offset & 3; + + switch (offset & ~3) { + case 0: /* CTLR */ + reg = vcpu->kvm->arch.vgic.enabled; + vgic_reg_access(mmio, ®, word_offset, + ACCESS_READ_VALUE | ACCESS_WRITE_VALUE); + if (mmio->is_write) { + vcpu->kvm->arch.vgic.enabled = reg & 1; + vgic_update_state(vcpu->kvm); + return true; + } + break; + + case 4: /* TYPER */ + reg = (atomic_read(&vcpu->kvm->online_vcpus) - 1) << 5; + reg |= (VGIC_NR_IRQS >> 5) - 1; + vgic_reg_access(mmio, ®, word_offset, + ACCESS_READ_VALUE | ACCESS_WRITE_IGNORED); + break; + + case 8: /* IIDR */ + reg = 0x4B00043B; + vgic_reg_access(mmio, ®, word_offset, + ACCESS_READ_VALUE | ACCESS_WRITE_IGNORED); + break; + } + + return false; +} + +static bool handle_mmio_raz_wi(struct kvm_vcpu *vcpu, + struct kvm_exit_mmio *mmio, phys_addr_t offset) +{ + vgic_reg_access(mmio, NULL, offset, + ACCESS_READ_RAZ | ACCESS_WRITE_IGNORED); + return false; +} + +static bool handle_mmio_set_enable_reg(struct kvm_vcpu *vcpu, + struct kvm_exit_mmio *mmio, + phys_addr_t offset) +{ + u32 *reg = vgic_bitmap_get_reg(&vcpu->kvm->arch.vgic.irq_enabled, + vcpu->vcpu_id, offset); + vgic_reg_access(mmio, reg, offset, + ACCESS_READ_VALUE | ACCESS_WRITE_SETBIT); + if (mmio->is_write) { + vgic_update_state(vcpu->kvm); + return true; + } + + return false; +} + +static bool handle_mmio_clear_enable_reg(struct kvm_vcpu *vcpu, + struct kvm_exit_mmio *mmio, + phys_addr_t offset) +{ + u32 *reg = vgic_bitmap_get_reg(&vcpu->kvm->arch.vgic.irq_enabled, + vcpu->vcpu_id, offset); + vgic_reg_access(mmio, reg, offset, + ACCESS_READ_VALUE | ACCESS_WRITE_CLEARBIT); + if (mmio->is_write) { + if (offset < 4) /* Force SGI enabled */ + *reg |= 0xffff; + vgic_retire_disabled_irqs(vcpu); + vgic_update_state(vcpu->kvm); + return true; + } + + return false; +} + +static bool handle_mmio_set_pending_reg(struct kvm_vcpu *vcpu, + struct kvm_exit_mmio *mmio, + phys_addr_t offset) +{ + u32 *reg = vgic_bitmap_get_reg(&vcpu->kvm->arch.vgic.irq_state, + vcpu->vcpu_id, offset); + vgic_reg_access(mmio, reg, offset, + ACCESS_READ_VALUE | ACCESS_WRITE_SETBIT); + if (mmio->is_write) { + vgic_update_state(vcpu->kvm); + return true; + } + + return false; +} + +static bool handle_mmio_clear_pending_reg(struct kvm_vcpu *vcpu, + struct kvm_exit_mmio *mmio, + phys_addr_t offset) +{ + u32 *reg = vgic_bitmap_get_reg(&vcpu->kvm->arch.vgic.irq_state, + vcpu->vcpu_id, offset); + vgic_reg_access(mmio, reg, offset, + ACCESS_READ_VALUE | ACCESS_WRITE_CLEARBIT); + if (mmio->is_write) { + vgic_update_state(vcpu->kvm); + return true; + } + + return false; +} + +static bool handle_mmio_priority_reg(struct kvm_vcpu *vcpu, + struct kvm_exit_mmio *mmio, + phys_addr_t offset) +{ + u32 *reg = vgic_bytemap_get_reg(&vcpu->kvm->arch.vgic.irq_priority, + vcpu->vcpu_id, offset); + vgic_reg_access(mmio, reg, offset, + ACCESS_READ_VALUE | ACCESS_WRITE_VALUE); + return false; +} + +#define GICD_ITARGETSR_SIZE 32 +#define GICD_CPUTARGETS_BITS 8 +#define GICD_IRQS_PER_ITARGETSR (GICD_ITARGETSR_SIZE / GICD_CPUTARGETS_BITS) +static u32 vgic_get_target_reg(struct kvm *kvm, int irq) +{ + struct vgic_dist *dist = &kvm->arch.vgic; + struct kvm_vcpu *vcpu; + int i, c; + unsigned long *bmap; + u32 val = 0; + + irq -= VGIC_NR_PRIVATE_IRQS; + + kvm_for_each_vcpu(c, vcpu, kvm) { + bmap = vgic_bitmap_get_shared_map(&dist->irq_spi_target[c]); + for (i = 0; i < GICD_IRQS_PER_ITARGETSR; i++) + if (test_bit(irq + i, bmap)) + val |= 1 << (c + i * 8); + } + + return val; +} + +static void vgic_set_target_reg(struct kvm *kvm, u32 val, int irq) +{ + struct vgic_dist *dist = &kvm->arch.vgic; + struct kvm_vcpu *vcpu; + int i, c; + unsigned long *bmap; + u32 target; + + irq -= VGIC_NR_PRIVATE_IRQS; + + /* + * Pick the LSB in each byte. This ensures we target exactly + * one vcpu per IRQ. If the byte is null, assume we target + * CPU0. + */ + for (i = 0; i < GICD_IRQS_PER_ITARGETSR; i++) { + int shift = i * GICD_CPUTARGETS_BITS; + target = ffs((val >> shift) & 0xffU); + target = target ? (target - 1) : 0; + dist->irq_spi_cpu[irq + i] = target; + kvm_for_each_vcpu(c, vcpu, kvm) { + bmap = vgic_bitmap_get_shared_map(&dist->irq_spi_target[c]); + if (c == target) + set_bit(irq + i, bmap); + else + clear_bit(irq + i, bmap); + } + } +} + +static bool handle_mmio_target_reg(struct kvm_vcpu *vcpu, + struct kvm_exit_mmio *mmio, + phys_addr_t offset) +{ + u32 reg; + + /* We treat the banked interrupts targets as read-only */ + if (offset < 32) { + u32 roreg = 1 << vcpu->vcpu_id; + roreg |= roreg << 8; + roreg |= roreg << 16; + + vgic_reg_access(mmio, &roreg, offset, + ACCESS_READ_VALUE | ACCESS_WRITE_IGNORED); + return false; + } + + reg = vgic_get_target_reg(vcpu->kvm, offset & ~3U); + vgic_reg_access(mmio, ®, offset, + ACCESS_READ_VALUE | ACCESS_WRITE_VALUE); + if (mmio->is_write) { + vgic_set_target_reg(vcpu->kvm, reg, offset & ~3U); + vgic_update_state(vcpu->kvm); + return true; + } + + return false; +} + +static u32 vgic_cfg_expand(u16 val) +{ + u32 res = 0; + int i; + + /* + * Turn a 16bit value like abcd...mnop into a 32bit word + * a0b0c0d0...m0n0o0p0, which is what the HW cfg register is. + */ + for (i = 0; i < 16; i++) + res |= ((val >> i) & VGIC_CFG_EDGE) << (2 * i + 1); + + return res; +} + +static u16 vgic_cfg_compress(u32 val) +{ + u16 res = 0; + int i; + + /* + * Turn a 32bit word a0b0c0d0...m0n0o0p0 into 16bit value like + * abcd...mnop which is what we really care about. + */ + for (i = 0; i < 16; i++) + res |= ((val >> (i * 2 + 1)) & VGIC_CFG_EDGE) << i; + + return res; +} + +/* + * The distributor uses 2 bits per IRQ for the CFG register, but the + * LSB is always 0. As such, we only keep the upper bit, and use the + * two above functions to compress/expand the bits + */ +static bool handle_mmio_cfg_reg(struct kvm_vcpu *vcpu, + struct kvm_exit_mmio *mmio, phys_addr_t offset) +{ + u32 val; + u32 *reg = vgic_bitmap_get_reg(&vcpu->kvm->arch.vgic.irq_cfg, + vcpu->vcpu_id, offset >> 1); + if (offset & 2) + val = *reg >> 16; + else + val = *reg & 0xffff; + + val = vgic_cfg_expand(val); + vgic_reg_access(mmio, &val, offset, + ACCESS_READ_VALUE | ACCESS_WRITE_VALUE); + if (mmio->is_write) { + if (offset < 4) { + *reg = ~0U; /* Force PPIs/SGIs to 1 */ + return false; + } + + val = vgic_cfg_compress(val); + if (offset & 2) { + *reg &= 0xffff; + *reg |= val << 16; + } else { + *reg &= 0xffff << 16; + *reg |= val; + } + } + + return false; +} + +static bool handle_mmio_sgi_reg(struct kvm_vcpu *vcpu, + struct kvm_exit_mmio *mmio, phys_addr_t offset) +{ + u32 reg; + vgic_reg_access(mmio, ®, offset, + ACCESS_READ_RAZ | ACCESS_WRITE_VALUE); + if (mmio->is_write) { + vgic_dispatch_sgi(vcpu, reg); + vgic_update_state(vcpu->kvm); + return true; + } + + return false; +} + +/* + * I would have liked to use the kvm_bus_io_*() API instead, but it + * cannot cope with banked registers (only the VM pointer is passed + * around, and we need the vcpu). One of these days, someone please + * fix it! + */ +struct mmio_range { + phys_addr_t base; + unsigned long len; + bool (*handle_mmio)(struct kvm_vcpu *vcpu, struct kvm_exit_mmio *mmio, + phys_addr_t offset); +}; + +static const struct mmio_range vgic_ranges[] = { + { + .base = GIC_DIST_CTRL, + .len = 12, + .handle_mmio = handle_mmio_misc, + }, + { + .base = GIC_DIST_IGROUP, + .len = VGIC_NR_IRQS / 8, + .handle_mmio = handle_mmio_raz_wi, + }, + { + .base = GIC_DIST_ENABLE_SET, + .len = VGIC_NR_IRQS / 8, + .handle_mmio = handle_mmio_set_enable_reg, + }, + { + .base = GIC_DIST_ENABLE_CLEAR, + .len = VGIC_NR_IRQS / 8, + .handle_mmio = handle_mmio_clear_enable_reg, + }, + { + .base = GIC_DIST_PENDING_SET, + .len = VGIC_NR_IRQS / 8, + .handle_mmio = handle_mmio_set_pending_reg, + }, + { + .base = GIC_DIST_PENDING_CLEAR, + .len = VGIC_NR_IRQS / 8, + .handle_mmio = handle_mmio_clear_pending_reg, + }, + { + .base = GIC_DIST_ACTIVE_SET, + .len = VGIC_NR_IRQS / 8, + .handle_mmio = handle_mmio_raz_wi, + }, + { + .base = GIC_DIST_ACTIVE_CLEAR, + .len = VGIC_NR_IRQS / 8, + .handle_mmio = handle_mmio_raz_wi, + }, + { + .base = GIC_DIST_PRI, + .len = VGIC_NR_IRQS, + .handle_mmio = handle_mmio_priority_reg, + }, + { + .base = GIC_DIST_TARGET, + .len = VGIC_NR_IRQS, + .handle_mmio = handle_mmio_target_reg, + }, + { + .base = GIC_DIST_CONFIG, + .len = VGIC_NR_IRQS / 4, + .handle_mmio = handle_mmio_cfg_reg, + }, + { + .base = GIC_DIST_SOFTINT, + .len = 4, + .handle_mmio = handle_mmio_sgi_reg, + }, + {} +}; + +static const +struct mmio_range *find_matching_range(const struct mmio_range *ranges, + struct kvm_exit_mmio *mmio, + phys_addr_t base) +{ + const struct mmio_range *r = ranges; + phys_addr_t addr = mmio->phys_addr - base; + + while (r->len) { + if (addr >= r->base && + (addr + mmio->len) <= (r->base + r->len)) + return r; + r++; + } + + return NULL; +} + +/** + * vgic_handle_mmio - handle an in-kernel MMIO access + * @vcpu: pointer to the vcpu performing the access + * @run: pointer to the kvm_run structure + * @mmio: pointer to the data describing the access + * + * returns true if the MMIO access has been performed in kernel space, + * and false if it needs to be emulated in user space. + */ +bool vgic_handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *run, + struct kvm_exit_mmio *mmio) +{ + const struct mmio_range *range; + struct vgic_dist *dist = &vcpu->kvm->arch.vgic; + unsigned long base = dist->vgic_dist_base; + bool updated_state; + unsigned long offset; + + if (!irqchip_in_kernel(vcpu->kvm) || + mmio->phys_addr < base || + (mmio->phys_addr + mmio->len) > (base + KVM_VGIC_V2_DIST_SIZE)) + return false; + + /* We don't support ldrd / strd or ldm / stm to the emulated vgic */ + if (mmio->len > 4) { + kvm_inject_dabt(vcpu, mmio->phys_addr); + return true; + } + + range = find_matching_range(vgic_ranges, mmio, base); + if (unlikely(!range || !range->handle_mmio)) { + pr_warn("Unhandled access %d %08llx %d\n", + mmio->is_write, mmio->phys_addr, mmio->len); + return false; + } + + spin_lock(&vcpu->kvm->arch.vgic.lock); + offset = mmio->phys_addr - range->base - base; + updated_state = range->handle_mmio(vcpu, mmio, offset); + spin_unlock(&vcpu->kvm->arch.vgic.lock); + kvm_prepare_mmio(run, mmio); + kvm_handle_mmio_return(vcpu, run); + + if (updated_state) + vgic_kick_vcpus(vcpu->kvm); + + return true; +} + +static void vgic_dispatch_sgi(struct kvm_vcpu *vcpu, u32 reg) +{ + struct kvm *kvm = vcpu->kvm; + struct vgic_dist *dist = &kvm->arch.vgic; + int nrcpus = atomic_read(&kvm->online_vcpus); + u8 target_cpus; + int sgi, mode, c, vcpu_id; + + vcpu_id = vcpu->vcpu_id; + + sgi = reg & 0xf; + target_cpus = (reg >> 16) & 0xff; + mode = (reg >> 24) & 3; + + switch (mode) { + case 0: + if (!target_cpus) + return; + + case 1: + target_cpus = ((1 << nrcpus) - 1) & ~(1 << vcpu_id) & 0xff; + break; + + case 2: + target_cpus = 1 << vcpu_id; + break; + } + + kvm_for_each_vcpu(c, vcpu, kvm) { + if (target_cpus & 1) { + /* Flag the SGI as pending */ + vgic_dist_irq_set(vcpu, sgi); + dist->irq_sgi_sources[c][sgi] |= 1 << vcpu_id; + kvm_debug("SGI%d from CPU%d to CPU%d\n", sgi, vcpu_id, c); + } + + target_cpus >>= 1; + } +} + +static int compute_pending_for_cpu(struct kvm_vcpu *vcpu) +{ + struct vgic_dist *dist = &vcpu->kvm->arch.vgic; + unsigned long *pending, *enabled, *pend_percpu, *pend_shared; + unsigned long pending_private, pending_shared; + int vcpu_id; + + vcpu_id = vcpu->vcpu_id; + pend_percpu = vcpu->arch.vgic_cpu.pending_percpu; + pend_shared = vcpu->arch.vgic_cpu.pending_shared; + + pending = vgic_bitmap_get_cpu_map(&dist->irq_state, vcpu_id); + enabled = vgic_bitmap_get_cpu_map(&dist->irq_enabled, vcpu_id); + bitmap_and(pend_percpu, pending, enabled, VGIC_NR_PRIVATE_IRQS); + + pending = vgic_bitmap_get_shared_map(&dist->irq_state); + enabled = vgic_bitmap_get_shared_map(&dist->irq_enabled); + bitmap_and(pend_shared, pending, enabled, VGIC_NR_SHARED_IRQS); + bitmap_and(pend_shared, pend_shared, + vgic_bitmap_get_shared_map(&dist->irq_spi_target[vcpu_id]), + VGIC_NR_SHARED_IRQS); + + pending_private = find_first_bit(pend_percpu, VGIC_NR_PRIVATE_IRQS); + pending_shared = find_first_bit(pend_shared, VGIC_NR_SHARED_IRQS); + return (pending_private < VGIC_NR_PRIVATE_IRQS || + pending_shared < VGIC_NR_SHARED_IRQS); +} + +/* + * Update the interrupt state and determine which CPUs have pending + * interrupts. Must be called with distributor lock held. + */ +static void vgic_update_state(struct kvm *kvm) +{ + struct vgic_dist *dist = &kvm->arch.vgic; + struct kvm_vcpu *vcpu; + int c; + + if (!dist->enabled) { + set_bit(0, &dist->irq_pending_on_cpu); + return; + } + + kvm_for_each_vcpu(c, vcpu, kvm) { + if (compute_pending_for_cpu(vcpu)) { + pr_debug("CPU%d has pending interrupts\n", c); + set_bit(c, &dist->irq_pending_on_cpu); + } + } +} + +#define LR_CPUID(lr) \ + (((lr) & GICH_LR_PHYSID_CPUID) >> GICH_LR_PHYSID_CPUID_SHIFT) +#define MK_LR_PEND(src, irq) \ + (GICH_LR_PENDING_BIT | ((src) << GICH_LR_PHYSID_CPUID_SHIFT) | (irq)) + +/* + * An interrupt may have been disabled after being made pending on the + * CPU interface (the classic case is a timer running while we're + * rebooting the guest - the interrupt would kick as soon as the CPU + * interface gets enabled, with deadly consequences). + * + * The solution is to examine already active LRs, and check the + * interrupt is still enabled. If not, just retire it. + */ +static void vgic_retire_disabled_irqs(struct kvm_vcpu *vcpu) +{ + struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; + int lr; + + for_each_set_bit(lr, vgic_cpu->lr_used, vgic_cpu->nr_lr) { + int irq = vgic_cpu->vgic_lr[lr] & GICH_LR_VIRTUALID; + + if (!vgic_irq_is_enabled(vcpu, irq)) { + vgic_cpu->vgic_irq_lr_map[irq] = LR_EMPTY; + clear_bit(lr, vgic_cpu->lr_used); + vgic_cpu->vgic_lr[lr] &= ~GICH_LR_STATE; + if (vgic_irq_is_active(vcpu, irq)) + vgic_irq_clear_active(vcpu, irq); + } + } +} + +/* + * Queue an interrupt to a CPU virtual interface. Return true on success, + * or false if it wasn't possible to queue it. + */ +static bool vgic_queue_irq(struct kvm_vcpu *vcpu, u8 sgi_source_id, int irq) +{ + struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; + int lr; + + /* Sanitize the input... */ + BUG_ON(sgi_source_id & ~7); + BUG_ON(sgi_source_id && irq >= VGIC_NR_SGIS); + BUG_ON(irq >= VGIC_NR_IRQS); + + kvm_debug("Queue IRQ%d\n", irq); + + lr = vgic_cpu->vgic_irq_lr_map[irq]; + + /* Do we have an active interrupt for the same CPUID? */ + if (lr != LR_EMPTY && + (LR_CPUID(vgic_cpu->vgic_lr[lr]) == sgi_source_id)) { + kvm_debug("LR%d piggyback for IRQ%d %x\n", + lr, irq, vgic_cpu->vgic_lr[lr]); + BUG_ON(!test_bit(lr, vgic_cpu->lr_used)); + vgic_cpu->vgic_lr[lr] |= GICH_LR_PENDING_BIT; + return true; + } + + /* Try to use another LR for this interrupt */ + lr = find_first_zero_bit((unsigned long *)vgic_cpu->lr_used, + vgic_cpu->nr_lr); + if (lr >= vgic_cpu->nr_lr) + return false; + + kvm_debug("LR%d allocated for IRQ%d %x\n", lr, irq, sgi_source_id); + vgic_cpu->vgic_lr[lr] = MK_LR_PEND(sgi_source_id, irq); + vgic_cpu->vgic_irq_lr_map[irq] = lr; + set_bit(lr, vgic_cpu->lr_used); + + if (!vgic_irq_is_edge(vcpu, irq)) + vgic_cpu->vgic_lr[lr] |= GICH_LR_EOI; + + return true; +} + +static bool vgic_queue_sgi(struct kvm_vcpu *vcpu, int irq) +{ + struct vgic_dist *dist = &vcpu->kvm->arch.vgic; + unsigned long sources; + int vcpu_id = vcpu->vcpu_id; + int c; + + sources = dist->irq_sgi_sources[vcpu_id][irq]; + + for_each_set_bit(c, &sources, VGIC_MAX_CPUS) { + if (vgic_queue_irq(vcpu, c, irq)) + clear_bit(c, &sources); + } + + dist->irq_sgi_sources[vcpu_id][irq] = sources; + + /* + * If the sources bitmap has been cleared it means that we + * could queue all the SGIs onto link registers (see the + * clear_bit above), and therefore we are done with them in + * our emulated gic and can get rid of them. + */ + if (!sources) { + vgic_dist_irq_clear(vcpu, irq); + vgic_cpu_irq_clear(vcpu, irq); + return true; + } + + return false; +} + +static bool vgic_queue_hwirq(struct kvm_vcpu *vcpu, int irq) +{ + if (vgic_irq_is_active(vcpu, irq)) + return true; /* level interrupt, already queued */ + + if (vgic_queue_irq(vcpu, 0, irq)) { + if (vgic_irq_is_edge(vcpu, irq)) { + vgic_dist_irq_clear(vcpu, irq); + vgic_cpu_irq_clear(vcpu, irq); + } else { + vgic_irq_set_active(vcpu, irq); + } + + return true; + } + + return false; +} + +/* + * Fill the list registers with pending interrupts before running the + * guest. + */ +static void __kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu) +{ + struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; + struct vgic_dist *dist = &vcpu->kvm->arch.vgic; + int i, vcpu_id; + int overflow = 0; + + vcpu_id = vcpu->vcpu_id; + + /* + * We may not have any pending interrupt, or the interrupts + * may have been serviced from another vcpu. In all cases, + * move along. + */ + if (!kvm_vgic_vcpu_pending_irq(vcpu)) { + pr_debug("CPU%d has no pending interrupt\n", vcpu_id); + goto epilog; + } + + /* SGIs */ + for_each_set_bit(i, vgic_cpu->pending_percpu, VGIC_NR_SGIS) { + if (!vgic_queue_sgi(vcpu, i)) + overflow = 1; + } + + /* PPIs */ + for_each_set_bit_from(i, vgic_cpu->pending_percpu, VGIC_NR_PRIVATE_IRQS) { + if (!vgic_queue_hwirq(vcpu, i)) + overflow = 1; + } + + /* SPIs */ + for_each_set_bit(i, vgic_cpu->pending_shared, VGIC_NR_SHARED_IRQS) { + if (!vgic_queue_hwirq(vcpu, i + VGIC_NR_PRIVATE_IRQS)) + overflow = 1; + } + +epilog: + if (overflow) { + vgic_cpu->vgic_hcr |= GICH_HCR_UIE; + } else { + vgic_cpu->vgic_hcr &= ~GICH_HCR_UIE; + /* + * We're about to run this VCPU, and we've consumed + * everything the distributor had in store for + * us. Claim we don't have anything pending. We'll + * adjust that if needed while exiting. + */ + clear_bit(vcpu_id, &dist->irq_pending_on_cpu); + } +} + +static bool vgic_process_maintenance(struct kvm_vcpu *vcpu) +{ + struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; + bool level_pending = false; + + kvm_debug("MISR = %08x\n", vgic_cpu->vgic_misr); + + if (vgic_cpu->vgic_misr & GICH_MISR_EOI) { + /* + * Some level interrupts have been EOIed. Clear their + * active bit. + */ + int lr, irq; + + for_each_set_bit(lr, (unsigned long *)vgic_cpu->vgic_eisr, + vgic_cpu->nr_lr) { + irq = vgic_cpu->vgic_lr[lr] & GICH_LR_VIRTUALID; + + vgic_irq_clear_active(vcpu, irq); + vgic_cpu->vgic_lr[lr] &= ~GICH_LR_EOI; + + /* Any additional pending interrupt? */ + if (vgic_dist_irq_is_pending(vcpu, irq)) { + vgic_cpu_irq_set(vcpu, irq); + level_pending = true; + } else { + vgic_cpu_irq_clear(vcpu, irq); + } + + /* + * Despite being EOIed, the LR may not have + * been marked as empty. + */ + set_bit(lr, (unsigned long *)vgic_cpu->vgic_elrsr); + vgic_cpu->vgic_lr[lr] &= ~GICH_LR_ACTIVE_BIT; + } + } + + if (vgic_cpu->vgic_misr & GICH_MISR_U) + vgic_cpu->vgic_hcr &= ~GICH_HCR_UIE; + + return level_pending; +} + +/* + * Sync back the VGIC state after a guest run. The distributor lock is + * needed so we don't get preempted in the middle of the state processing. + */ +static void __kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu) +{ + struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; + struct vgic_dist *dist = &vcpu->kvm->arch.vgic; + int lr, pending; + bool level_pending; + + level_pending = vgic_process_maintenance(vcpu); + + /* Clear mappings for empty LRs */ + for_each_set_bit(lr, (unsigned long *)vgic_cpu->vgic_elrsr, + vgic_cpu->nr_lr) { + int irq; + + if (!test_and_clear_bit(lr, vgic_cpu->lr_used)) + continue; + + irq = vgic_cpu->vgic_lr[lr] & GICH_LR_VIRTUALID; + + BUG_ON(irq >= VGIC_NR_IRQS); + vgic_cpu->vgic_irq_lr_map[irq] = LR_EMPTY; + } + + /* Check if we still have something up our sleeve... */ + pending = find_first_zero_bit((unsigned long *)vgic_cpu->vgic_elrsr, + vgic_cpu->nr_lr); + if (level_pending || pending < vgic_cpu->nr_lr) + set_bit(vcpu->vcpu_id, &dist->irq_pending_on_cpu); +} + +void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu) +{ + struct vgic_dist *dist = &vcpu->kvm->arch.vgic; + + if (!irqchip_in_kernel(vcpu->kvm)) + return; + + spin_lock(&dist->lock); + __kvm_vgic_flush_hwstate(vcpu); + spin_unlock(&dist->lock); +} + +void kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu) +{ + struct vgic_dist *dist = &vcpu->kvm->arch.vgic; + + if (!irqchip_in_kernel(vcpu->kvm)) + return; + + spin_lock(&dist->lock); + __kvm_vgic_sync_hwstate(vcpu); + spin_unlock(&dist->lock); +} + +int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu) +{ + struct vgic_dist *dist = &vcpu->kvm->arch.vgic; + + if (!irqchip_in_kernel(vcpu->kvm)) + return 0; + + return test_bit(vcpu->vcpu_id, &dist->irq_pending_on_cpu); +} + +static void vgic_kick_vcpus(struct kvm *kvm) +{ + struct kvm_vcpu *vcpu; + int c; + + /* + * We've injected an interrupt, time to find out who deserves + * a good kick... + */ + kvm_for_each_vcpu(c, vcpu, kvm) { + if (kvm_vgic_vcpu_pending_irq(vcpu)) + kvm_vcpu_kick(vcpu); + } +} + +static int vgic_validate_injection(struct kvm_vcpu *vcpu, int irq, int level) +{ + int is_edge = vgic_irq_is_edge(vcpu, irq); + int state = vgic_dist_irq_is_pending(vcpu, irq); + + /* + * Only inject an interrupt if: + * - edge triggered and we have a rising edge + * - level triggered and we change level + */ + if (is_edge) + return level > state; + else + return level != state; +} + +static bool vgic_update_irq_state(struct kvm *kvm, int cpuid, + unsigned int irq_num, bool level) +{ + struct vgic_dist *dist = &kvm->arch.vgic; + struct kvm_vcpu *vcpu; + int is_edge, is_level; + int enabled; + bool ret = true; + + spin_lock(&dist->lock); + + vcpu = kvm_get_vcpu(kvm, cpuid); + is_edge = vgic_irq_is_edge(vcpu, irq_num); + is_level = !is_edge; + + if (!vgic_validate_injection(vcpu, irq_num, level)) { + ret = false; + goto out; + } + + if (irq_num >= VGIC_NR_PRIVATE_IRQS) { + cpuid = dist->irq_spi_cpu[irq_num - VGIC_NR_PRIVATE_IRQS]; + vcpu = kvm_get_vcpu(kvm, cpuid); + } + + kvm_debug("Inject IRQ%d level %d CPU%d\n", irq_num, level, cpuid); + + if (level) + vgic_dist_irq_set(vcpu, irq_num); + else + vgic_dist_irq_clear(vcpu, irq_num); + + enabled = vgic_irq_is_enabled(vcpu, irq_num); + + if (!enabled) { + ret = false; + goto out; + } + + if (is_level && vgic_irq_is_active(vcpu, irq_num)) { + /* + * Level interrupt in progress, will be picked up + * when EOId. + */ + ret = false; + goto out; + } + + if (level) { + vgic_cpu_irq_set(vcpu, irq_num); + set_bit(cpuid, &dist->irq_pending_on_cpu); + } + +out: + spin_unlock(&dist->lock); + + return ret; +} + +/** + * kvm_vgic_inject_irq - Inject an IRQ from a device to the vgic + * @kvm: The VM structure pointer + * @cpuid: The CPU for PPIs + * @irq_num: The IRQ number that is assigned to the device + * @level: Edge-triggered: true: to trigger the interrupt + * false: to ignore the call + * Level-sensitive true: activates an interrupt + * false: deactivates an interrupt + * + * The GIC is not concerned with devices being active-LOW or active-HIGH for + * level-sensitive interrupts. You can think of the level parameter as 1 + * being HIGH and 0 being LOW and all devices being active-HIGH. + */ +int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int irq_num, + bool level) +{ + if (vgic_update_irq_state(kvm, cpuid, irq_num, level)) + vgic_kick_vcpus(kvm); + + return 0; +} + +static irqreturn_t vgic_maintenance_handler(int irq, void *data) +{ + /* + * We cannot rely on the vgic maintenance interrupt to be + * delivered synchronously. This means we can only use it to + * exit the VM, and we perform the handling of EOIed + * interrupts on the exit path (see vgic_process_maintenance). + */ + return IRQ_HANDLED; +} + +int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu) +{ + struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; + struct vgic_dist *dist = &vcpu->kvm->arch.vgic; + int i; + + if (!irqchip_in_kernel(vcpu->kvm)) + return 0; + + if (vcpu->vcpu_id >= VGIC_MAX_CPUS) + return -EBUSY; + + for (i = 0; i < VGIC_NR_IRQS; i++) { + if (i < VGIC_NR_PPIS) + vgic_bitmap_set_irq_val(&dist->irq_enabled, + vcpu->vcpu_id, i, 1); + if (i < VGIC_NR_PRIVATE_IRQS) + vgic_bitmap_set_irq_val(&dist->irq_cfg, + vcpu->vcpu_id, i, VGIC_CFG_EDGE); + + vgic_cpu->vgic_irq_lr_map[i] = LR_EMPTY; + } + + /* + * By forcing VMCR to zero, the GIC will restore the binary + * points to their reset values. Anything else resets to zero + * anyway. + */ + vgic_cpu->vgic_vmcr = 0; + + vgic_cpu->nr_lr = vgic_nr_lr; + vgic_cpu->vgic_hcr = GICH_HCR_EN; /* Get the show on the road... */ + + return 0; +} + +static void vgic_init_maintenance_interrupt(void *info) +{ + enable_percpu_irq(vgic_maint_irq, 0); +} + +static int vgic_cpu_notify(struct notifier_block *self, + unsigned long action, void *cpu) +{ + switch (action) { + case CPU_STARTING: + case CPU_STARTING_FROZEN: + vgic_init_maintenance_interrupt(NULL); + break; + case CPU_DYING: + case CPU_DYING_FROZEN: + disable_percpu_irq(vgic_maint_irq); + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block vgic_cpu_nb = { + .notifier_call = vgic_cpu_notify, +}; + +int kvm_vgic_hyp_init(void) +{ + int ret; + struct resource vctrl_res; + struct resource vcpu_res; + + vgic_node = of_find_compatible_node(NULL, NULL, "arm,cortex-a15-gic"); + if (!vgic_node) { + kvm_err("error: no compatible vgic node in DT\n"); + return -ENODEV; + } + + vgic_maint_irq = irq_of_parse_and_map(vgic_node, 0); + if (!vgic_maint_irq) { + kvm_err("error getting vgic maintenance irq from DT\n"); + ret = -ENXIO; + goto out; + } + + ret = request_percpu_irq(vgic_maint_irq, vgic_maintenance_handler, + "vgic", kvm_get_running_vcpus()); + if (ret) { + kvm_err("Cannot register interrupt %d\n", vgic_maint_irq); + goto out; + } + + ret = register_cpu_notifier(&vgic_cpu_nb); + if (ret) { + kvm_err("Cannot register vgic CPU notifier\n"); + goto out_free_irq; + } + + ret = of_address_to_resource(vgic_node, 2, &vctrl_res); + if (ret) { + kvm_err("Cannot obtain VCTRL resource\n"); + goto out_free_irq; + } + + vgic_vctrl_base = of_iomap(vgic_node, 2); + if (!vgic_vctrl_base) { + kvm_err("Cannot ioremap VCTRL\n"); + ret = -ENOMEM; + goto out_free_irq; + } + + vgic_nr_lr = readl_relaxed(vgic_vctrl_base + GICH_VTR); + vgic_nr_lr = (vgic_nr_lr & 0x3f) + 1; + + ret = create_hyp_io_mappings(vgic_vctrl_base, + vgic_vctrl_base + resource_size(&vctrl_res), + vctrl_res.start); + if (ret) { + kvm_err("Cannot map VCTRL into hyp\n"); + goto out_unmap; + } + + kvm_info("%s@%llx IRQ%d\n", vgic_node->name, + vctrl_res.start, vgic_maint_irq); + on_each_cpu(vgic_init_maintenance_interrupt, NULL, 1); + + if (of_address_to_resource(vgic_node, 3, &vcpu_res)) { + kvm_err("Cannot obtain VCPU resource\n"); + ret = -ENXIO; + goto out_unmap; + } + vgic_vcpu_base = vcpu_res.start; + + goto out; + +out_unmap: + iounmap(vgic_vctrl_base); +out_free_irq: + free_percpu_irq(vgic_maint_irq, kvm_get_running_vcpus()); +out: + of_node_put(vgic_node); + return ret; +} + +int kvm_vgic_init(struct kvm *kvm) +{ + int ret = 0, i; + + mutex_lock(&kvm->lock); + + if (vgic_initialized(kvm)) + goto out; + + if (IS_VGIC_ADDR_UNDEF(kvm->arch.vgic.vgic_dist_base) || + IS_VGIC_ADDR_UNDEF(kvm->arch.vgic.vgic_cpu_base)) { + kvm_err("Need to set vgic cpu and dist addresses first\n"); + ret = -ENXIO; + goto out; + } + + ret = kvm_phys_addr_ioremap(kvm, kvm->arch.vgic.vgic_cpu_base, + vgic_vcpu_base, KVM_VGIC_V2_CPU_SIZE); + if (ret) { + kvm_err("Unable to remap VGIC CPU to VCPU\n"); + goto out; + } + + for (i = VGIC_NR_PRIVATE_IRQS; i < VGIC_NR_IRQS; i += 4) + vgic_set_target_reg(kvm, 0, i); + + kvm_timer_init(kvm); + kvm->arch.vgic.ready = true; +out: + mutex_unlock(&kvm->lock); + return ret; +} + +int kvm_vgic_create(struct kvm *kvm) +{ + int ret = 0; + + mutex_lock(&kvm->lock); + + if (atomic_read(&kvm->online_vcpus) || kvm->arch.vgic.vctrl_base) { + ret = -EEXIST; + goto out; + } + + spin_lock_init(&kvm->arch.vgic.lock); + kvm->arch.vgic.vctrl_base = vgic_vctrl_base; + kvm->arch.vgic.vgic_dist_base = VGIC_ADDR_UNDEF; + kvm->arch.vgic.vgic_cpu_base = VGIC_ADDR_UNDEF; + +out: + mutex_unlock(&kvm->lock); + return ret; +} + +static bool vgic_ioaddr_overlap(struct kvm *kvm) +{ + phys_addr_t dist = kvm->arch.vgic.vgic_dist_base; + phys_addr_t cpu = kvm->arch.vgic.vgic_cpu_base; + + if (IS_VGIC_ADDR_UNDEF(dist) || IS_VGIC_ADDR_UNDEF(cpu)) + return 0; + if ((dist <= cpu && dist + KVM_VGIC_V2_DIST_SIZE > cpu) || + (cpu <= dist && cpu + KVM_VGIC_V2_CPU_SIZE > dist)) + return -EBUSY; + return 0; +} + +static int vgic_ioaddr_assign(struct kvm *kvm, phys_addr_t *ioaddr, + phys_addr_t addr, phys_addr_t size) +{ + int ret; + + if (!IS_VGIC_ADDR_UNDEF(*ioaddr)) + return -EEXIST; + if (addr + size < addr) + return -EINVAL; + + ret = vgic_ioaddr_overlap(kvm); + if (ret) + return ret; + *ioaddr = addr; + return ret; +} + +int kvm_vgic_set_addr(struct kvm *kvm, unsigned long type, u64 addr) +{ + int r = 0; + struct vgic_dist *vgic = &kvm->arch.vgic; + + if (addr & ~KVM_PHYS_MASK) + return -E2BIG; + + if (addr & (SZ_4K - 1)) + return -EINVAL; + + mutex_lock(&kvm->lock); + switch (type) { + case KVM_VGIC_V2_ADDR_TYPE_DIST: + r = vgic_ioaddr_assign(kvm, &vgic->vgic_dist_base, + addr, KVM_VGIC_V2_DIST_SIZE); + break; + case KVM_VGIC_V2_ADDR_TYPE_CPU: + r = vgic_ioaddr_assign(kvm, &vgic->vgic_cpu_base, + addr, KVM_VGIC_V2_CPU_SIZE); + break; + default: + r = -ENODEV; + } + + mutex_unlock(&kvm->lock); + return r; +} -- cgit v1.2.3 From 535cf7b3b13c7faed3dfabafb6598417de1129ca Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 14 May 2013 14:31:02 +0100 Subject: KVM: get rid of $(addprefix ../../../virt/kvm/, ...) in Makefiles As requested by the KVM maintainers, remove the addprefix used to refer to the main KVM code from the arch code, and replace it with a KVM variable that does the same thing. Tested-by: Christian Borntraeger Cc: Paolo Bonzini Cc: Gleb Natapov Cc: Christoffer Dall Acked-by: Xiantao Zhang Cc: Tony Luck Cc: Fenghua Yu Cc: Alexander Graf Cc: Benjamin Herrenschmidt Cc: Christian Borntraeger Cc: Cornelia Huck Signed-off-by: Marc Zyngier Signed-off-by: Gleb Natapov --- arch/arm/kvm/Makefile | 2 +- arch/ia64/kvm/Makefile | 7 ++++--- arch/powerpc/kvm/Makefile | 13 +++++++------ arch/s390/kvm/Makefile | 3 ++- arch/x86/kvm/Makefile | 13 +++++++------ 5 files changed, 21 insertions(+), 17 deletions(-) diff --git a/arch/arm/kvm/Makefile b/arch/arm/kvm/Makefile index 9184a491d172..d99bee4950e5 100644 --- a/arch/arm/kvm/Makefile +++ b/arch/arm/kvm/Makefile @@ -15,7 +15,7 @@ AFLAGS_init.o := -Wa,-march=armv7-a$(plus_virt) AFLAGS_interrupts.o := -Wa,-march=armv7-a$(plus_virt) KVM := ../../../virt/kvm -kvm-arm-y = $(addprefix $(KVM)/, kvm_main.o coalesced_mmio.o) +kvm-arm-y = $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o obj-y += kvm-arm.o init.o interrupts.o obj-y += arm.o handle_exit.o guest.o mmu.o emulate.o reset.o diff --git a/arch/ia64/kvm/Makefile b/arch/ia64/kvm/Makefile index 1a4053789d01..18e45ec49bbf 100644 --- a/arch/ia64/kvm/Makefile +++ b/arch/ia64/kvm/Makefile @@ -47,12 +47,13 @@ FORCE : $(obj)/$(offsets-file) ccflags-y := -Ivirt/kvm -Iarch/ia64/kvm/ asflags-y := -Ivirt/kvm -Iarch/ia64/kvm/ +KVM := ../../../virt/kvm -common-objs = $(addprefix ../../../virt/kvm/, kvm_main.o ioapic.o \ - coalesced_mmio.o irq_comm.o) +common-objs = $(KVM)/kvm_main.o $(KVM)/ioapic.o \ + $(KVM)/coalesced_mmio.o $(KVM)/irq_comm.o ifeq ($(CONFIG_KVM_DEVICE_ASSIGNMENT),y) -common-objs += $(addprefix ../../../virt/kvm/, assigned-dev.o iommu.o) +common-objs += $(KVM)/assigned-dev.o $(KVM)/iommu.o endif kvm-objs := $(common-objs) kvm-ia64.o kvm_fw.o diff --git a/arch/powerpc/kvm/Makefile b/arch/powerpc/kvm/Makefile index 422de3f4d46c..008cd856c5b5 100644 --- a/arch/powerpc/kvm/Makefile +++ b/arch/powerpc/kvm/Makefile @@ -5,9 +5,10 @@ subdir-ccflags-$(CONFIG_PPC_WERROR) := -Werror ccflags-y := -Ivirt/kvm -Iarch/powerpc/kvm +KVM := ../../../virt/kvm -common-objs-y = $(addprefix ../../../virt/kvm/, kvm_main.o coalesced_mmio.o \ - eventfd.o) +common-objs-y = $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o \ + $(KVM)/eventfd.o CFLAGS_44x_tlb.o := -I. CFLAGS_e500_mmu.o := -I. @@ -53,7 +54,7 @@ kvm-e500mc-objs := \ kvm-objs-$(CONFIG_KVM_E500MC) := $(kvm-e500mc-objs) kvm-book3s_64-objs-$(CONFIG_KVM_BOOK3S_64_PR) := \ - ../../../virt/kvm/coalesced_mmio.o \ + $(KVM)/coalesced_mmio.o \ fpu.o \ book3s_paired_singles.o \ book3s_pr.o \ @@ -86,8 +87,8 @@ kvm-book3s_64-objs-$(CONFIG_KVM_XICS) += \ book3s_xics.o kvm-book3s_64-module-objs := \ - ../../../virt/kvm/kvm_main.o \ - ../../../virt/kvm/eventfd.o \ + $(KVM)/kvm_main.o \ + $(KVM)/eventfd.o \ powerpc.o \ emulate.o \ book3s.o \ @@ -111,7 +112,7 @@ kvm-book3s_32-objs := \ kvm-objs-$(CONFIG_KVM_BOOK3S_32) := $(kvm-book3s_32-objs) kvm-objs-$(CONFIG_KVM_MPIC) += mpic.o -kvm-objs-$(CONFIG_HAVE_KVM_IRQ_ROUTING) += $(addprefix ../../../virt/kvm/, irqchip.o) +kvm-objs-$(CONFIG_HAVE_KVM_IRQ_ROUTING) += $(KVM)/irqchip.o kvm-objs := $(kvm-objs-m) $(kvm-objs-y) diff --git a/arch/s390/kvm/Makefile b/arch/s390/kvm/Makefile index 8fe9d65a4585..40b4c6470f88 100644 --- a/arch/s390/kvm/Makefile +++ b/arch/s390/kvm/Makefile @@ -6,7 +6,8 @@ # it under the terms of the GNU General Public License (version 2 only) # as published by the Free Software Foundation. -common-objs = $(addprefix ../../../virt/kvm/, kvm_main.o eventfd.o) +KVM := ../../../virt/kvm +common-objs = $(KVM)/kvm_main.o $(KVM)/eventfd.o ccflags-y := -Ivirt/kvm -Iarch/s390/kvm diff --git a/arch/x86/kvm/Makefile b/arch/x86/kvm/Makefile index d609e1d84048..bf4fb04d0112 100644 --- a/arch/x86/kvm/Makefile +++ b/arch/x86/kvm/Makefile @@ -5,12 +5,13 @@ CFLAGS_x86.o := -I. CFLAGS_svm.o := -I. CFLAGS_vmx.o := -I. -kvm-y += $(addprefix ../../../virt/kvm/, kvm_main.o ioapic.o \ - coalesced_mmio.o irq_comm.o eventfd.o \ - irqchip.o) -kvm-$(CONFIG_KVM_DEVICE_ASSIGNMENT) += $(addprefix ../../../virt/kvm/, \ - assigned-dev.o iommu.o) -kvm-$(CONFIG_KVM_ASYNC_PF) += $(addprefix ../../../virt/kvm/, async_pf.o) +KVM := ../../../virt/kvm + +kvm-y += $(KVM)/kvm_main.o $(KVM)/ioapic.o \ + $(KVM)/coalesced_mmio.o $(KVM)/irq_comm.o \ + $(KVM)/eventfd.o $(KVM)/irqchip.o +kvm-$(CONFIG_KVM_DEVICE_ASSIGNMENT) += $(KVM)/assigned-dev.o $(KVM)/iommu.o +kvm-$(CONFIG_KVM_ASYNC_PF) += $(KVM)/async_pf.o kvm-y += x86.o mmu.o emulate.o i8259.o irq.o lapic.o \ i8254.o cpuid.o pmu.o -- cgit v1.2.3 From caeaba79009c2ee858c3b2bf8caf922cd719fead Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Thu, 16 May 2013 22:32:00 +0000 Subject: ipv6: add support of peer address This patch adds the support of peer address for IPv6. For example, it is possible to specify the remote end of a 6inY tunnel. This was already possible in IPv4: ip addr add ip1 peer ip2 dev dev1 The peer address is specified with IFA_ADDRESS and the local address with IFA_LOCAL (like explained in include/uapi/linux/if_addr.h). Note that the API is not changed, because before this patch, it was not possible to specify two different addresses in IFA_LOCAL and IFA_REMOTE. There is a small change for the dump: if the peer is different from ::, IFA_ADDRESS will contain the peer address instead of the local address. Signed-off-by: Nicolas Dichtel Signed-off-by: David S. Miller --- include/net/if_inet6.h | 1 + net/ipv6/addrconf.c | 64 ++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 48 insertions(+), 17 deletions(-) diff --git a/include/net/if_inet6.h b/include/net/if_inet6.h index 100fb8cec17c..0727d0e4af3b 100644 --- a/include/net/if_inet6.h +++ b/include/net/if_inet6.h @@ -74,6 +74,7 @@ struct inet6_ifaddr { bool tokenized; struct rcu_head rcu; + struct in6_addr peer_addr; }; struct ip6_sf_socklist { diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index d1ab6ab29a55..d684d23bc027 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -2402,6 +2402,7 @@ err_exit: * Manual configuration of address on an interface */ static int inet6_addr_add(struct net *net, int ifindex, const struct in6_addr *pfx, + const struct in6_addr *peer_pfx, unsigned int plen, __u8 ifa_flags, __u32 prefered_lft, __u32 valid_lft) { @@ -2457,6 +2458,8 @@ static int inet6_addr_add(struct net *net, int ifindex, const struct in6_addr *p ifp->valid_lft = valid_lft; ifp->prefered_lft = prefered_lft; ifp->tstamp = jiffies; + if (peer_pfx) + ifp->peer_addr = *peer_pfx; spin_unlock_bh(&ifp->lock); addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev, @@ -2526,7 +2529,7 @@ int addrconf_add_ifaddr(struct net *net, void __user *arg) return -EFAULT; rtnl_lock(); - err = inet6_addr_add(net, ireq.ifr6_ifindex, &ireq.ifr6_addr, + err = inet6_addr_add(net, ireq.ifr6_ifindex, &ireq.ifr6_addr, NULL, ireq.ifr6_prefixlen, IFA_F_PERMANENT, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME); rtnl_unlock(); @@ -3610,18 +3613,20 @@ restart: rcu_read_unlock_bh(); } -static struct in6_addr *extract_addr(struct nlattr *addr, struct nlattr *local) +static struct in6_addr *extract_addr(struct nlattr *addr, struct nlattr *local, + struct in6_addr **peer_pfx) { struct in6_addr *pfx = NULL; + *peer_pfx = NULL; + if (addr) pfx = nla_data(addr); if (local) { if (pfx && nla_memcmp(local, pfx, sizeof(*pfx))) - pfx = NULL; - else - pfx = nla_data(local); + *peer_pfx = pfx; + pfx = nla_data(local); } return pfx; @@ -3639,7 +3644,7 @@ inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh) struct net *net = sock_net(skb->sk); struct ifaddrmsg *ifm; struct nlattr *tb[IFA_MAX+1]; - struct in6_addr *pfx; + struct in6_addr *pfx, *peer_pfx; int err; err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv6_policy); @@ -3647,7 +3652,7 @@ inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh) return err; ifm = nlmsg_data(nlh); - pfx = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL]); + pfx = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL], &peer_pfx); if (pfx == NULL) return -EINVAL; @@ -3705,7 +3710,7 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh) struct net *net = sock_net(skb->sk); struct ifaddrmsg *ifm; struct nlattr *tb[IFA_MAX+1]; - struct in6_addr *pfx; + struct in6_addr *pfx, *peer_pfx; struct inet6_ifaddr *ifa; struct net_device *dev; u32 valid_lft = INFINITY_LIFE_TIME, preferred_lft = INFINITY_LIFE_TIME; @@ -3717,7 +3722,7 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh) return err; ifm = nlmsg_data(nlh); - pfx = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL]); + pfx = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL], &peer_pfx); if (pfx == NULL) return -EINVAL; @@ -3745,7 +3750,7 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh) * It would be best to check for !NLM_F_CREATE here but * userspace alreay relies on not having to provide this. */ - return inet6_addr_add(net, ifm->ifa_index, pfx, + return inet6_addr_add(net, ifm->ifa_index, pfx, peer_pfx, ifm->ifa_prefixlen, ifa_flags, preferred_lft, valid_lft); } @@ -3802,6 +3807,7 @@ static inline int rt_scope(int ifa_scope) static inline int inet6_ifaddr_msgsize(void) { return NLMSG_ALIGN(sizeof(struct ifaddrmsg)) + + nla_total_size(16) /* IFA_LOCAL */ + nla_total_size(16) /* IFA_ADDRESS */ + nla_total_size(sizeof(struct ifa_cacheinfo)); } @@ -3840,13 +3846,22 @@ static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa, valid = INFINITY_LIFE_TIME; } - if (nla_put(skb, IFA_ADDRESS, 16, &ifa->addr) < 0 || - put_cacheinfo(skb, ifa->cstamp, ifa->tstamp, preferred, valid) < 0) { - nlmsg_cancel(skb, nlh); - return -EMSGSIZE; - } + if (ipv6_addr_type(&ifa->peer_addr) != IPV6_ADDR_ANY) { + if (nla_put(skb, IFA_LOCAL, 16, &ifa->addr) < 0 || + nla_put(skb, IFA_ADDRESS, 16, &ifa->peer_addr) < 0) + goto error; + } else + if (nla_put(skb, IFA_ADDRESS, 16, &ifa->addr) < 0) + goto error; + + if (put_cacheinfo(skb, ifa->cstamp, ifa->tstamp, preferred, valid) < 0) + goto error; return nlmsg_end(skb, nlh); + +error: + nlmsg_cancel(skb, nlh); + return -EMSGSIZE; } static int inet6_fill_ifmcaddr(struct sk_buff *skb, struct ifmcaddr6 *ifmca, @@ -4046,7 +4061,7 @@ static int inet6_rtm_getaddr(struct sk_buff *in_skb, struct nlmsghdr *nlh) struct net *net = sock_net(in_skb->sk); struct ifaddrmsg *ifm; struct nlattr *tb[IFA_MAX+1]; - struct in6_addr *addr = NULL; + struct in6_addr *addr = NULL, *peer; struct net_device *dev = NULL; struct inet6_ifaddr *ifa; struct sk_buff *skb; @@ -4056,7 +4071,7 @@ static int inet6_rtm_getaddr(struct sk_buff *in_skb, struct nlmsghdr *nlh) if (err < 0) goto errout; - addr = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL]); + addr = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL], &peer); if (addr == NULL) { err = -EINVAL; goto errout; @@ -4564,11 +4579,26 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp) ip6_ins_rt(ifp->rt); if (ifp->idev->cnf.forwarding) addrconf_join_anycast(ifp); + if (ipv6_addr_type(&ifp->peer_addr) != IPV6_ADDR_ANY) + addrconf_prefix_route(&ifp->peer_addr, 128, + ifp->idev->dev, 0, 0); break; case RTM_DELADDR: if (ifp->idev->cnf.forwarding) addrconf_leave_anycast(ifp); addrconf_leave_solict(ifp->idev, &ifp->addr); + if (ipv6_addr_type(&ifp->peer_addr) != IPV6_ADDR_ANY) { + struct rt6_info *rt; + struct net_device *dev = ifp->idev->dev; + + rt = rt6_lookup(dev_net(dev), &ifp->peer_addr, NULL, + dev->ifindex, 1); + if (rt) { + dst_hold(&rt->dst); + if (ip6_del_rt(rt)) + dst_free(&rt->dst); + } + } dst_hold(&ifp->rt->dst); if (ip6_del_rt(ifp->rt)) -- cgit v1.2.3 From 3cc7587b30032b7c4dd9610a55a77519e84da7db Mon Sep 17 00:00:00 2001 From: Rami Rosen Date: Fri, 17 May 2013 09:10:34 +0000 Subject: Documentation/sysctl/net.txt: fix (attribute removal). This patch removes mentioning the sysfsf net_device weight attribute (class/net//weight) in Documentation/sysctl/net.txt, since the net sysfs weight attribute was removed by the following patch: [NET]: Make NAPI polling independent of struct net_device objects bea3348eef27e6044b6161fd04c3152215f96411 Signed-off-by: Rami Rosen Signed-off-by: David S. Miller --- Documentation/sysctl/net.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Documentation/sysctl/net.txt b/Documentation/sysctl/net.txt index 98335b7a5337..c1f8640c2fc8 100644 --- a/Documentation/sysctl/net.txt +++ b/Documentation/sysctl/net.txt @@ -93,8 +93,7 @@ netdev_budget Maximum number of packets taken from all interfaces in one polling cycle (NAPI poll). In one polling cycle interfaces which are registered to polling are -probed in a round-robin manner. The limit of packets in one such probe can be -set per-device via sysfs class/net//weight . +probed in a round-robin manner. netdev_max_backlog ------------------ -- cgit v1.2.3 From 8b21ab140ec581caf3340a51cdbe91a76c36d9d8 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 20 May 2013 10:27:00 +0930 Subject: virtio_pci: better macro exported in uapi Macro VIRTIO_PCI_CONFIG assumes that userspace actually has a structure with a field named msix_enabled. Add VIRTIO_PCI_CONFIG_OFF that gets the msix_enabled by value instead, to make it useful for userspace. We still keep VIRTIO_PCI_CONFIG around for now, in case some userspace uses it. Signed-off-by: Michael S. Tsirkin Signed-off-by: Rusty Russell --- include/uapi/linux/virtio_pci.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/virtio_pci.h b/include/uapi/linux/virtio_pci.h index ea66f3f60d63..e5ec1caab82a 100644 --- a/include/uapi/linux/virtio_pci.h +++ b/include/uapi/linux/virtio_pci.h @@ -80,7 +80,9 @@ /* The remaining space is defined by each driver as the per-driver * configuration space */ -#define VIRTIO_PCI_CONFIG(dev) ((dev)->msix_enabled ? 24 : 20) +#define VIRTIO_PCI_CONFIG_OFF(msix_enabled) ((msix_enabled) ? 24 : 20) +/* Deprecated: please use VIRTIO_PCI_CONFIG_OFF instead */ +#define VIRTIO_PCI_CONFIG(dev) VIRTIO_PCI_CONFIG_OFF((dev)->msix_enabled) /* Virtio ABI version, this must match exactly */ #define VIRTIO_PCI_ABI_VERSION 0 -- cgit v1.2.3 From 06df44ee41442d83be061c5fd1b1de4f5fc6fbbf Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Mon, 20 May 2013 10:25:38 +0930 Subject: modpost.c: Add .text.unlikely to TEXT_SECTIONS gcc's places cold functions into the .text.unlikely section and we need to check this section as well for section mismatches otherwise we may have false negatives for this test. Cc: Rusty Russell Cc: Greg Kroah-Hartman Cc: linux-kernel@vger.kernel.org Cc: linux-kbuild@vger.kernel.org Signed-off-by: Tom Rini Signed-off-by: Rusty Russell (wording update) --- scripts/mod/modpost.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index a4be8e112bb6..3d155dd27eb6 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -884,7 +884,7 @@ static void check_section(const char *modname, struct elf_info *elf, #define ALL_EXIT_SECTIONS EXIT_SECTIONS, ALL_XXXEXIT_SECTIONS #define DATA_SECTIONS ".data$", ".data.rel$" -#define TEXT_SECTIONS ".text$" +#define TEXT_SECTIONS ".text$", ".text.unlikely$" #define INIT_SECTIONS ".init.*" #define CPU_INIT_SECTIONS ".cpuinit.*" -- cgit v1.2.3 From 1c6264cc67ac8712d84392fbc9293289011407c0 Mon Sep 17 00:00:00 2001 From: Pranavkumar Sawargaonkar Date: Mon, 20 May 2013 10:25:38 +0930 Subject: virtio: console: Add emergency writeonly register to config space This patch adds an emerg_wr register (writeonly) in config space of virtio console device which can be used for debugging. Signed-off-by: Pranavkumar Sawargaonkar Signed-off-by: Anup Patel Acked-by: Amit Shah Signed-off-by: Rusty Russell --- include/uapi/linux/virtio_console.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/uapi/linux/virtio_console.h b/include/uapi/linux/virtio_console.h index ee13ab6c3614..3fd0dce3cb25 100644 --- a/include/uapi/linux/virtio_console.h +++ b/include/uapi/linux/virtio_console.h @@ -38,6 +38,7 @@ /* Feature bits */ #define VIRTIO_CONSOLE_F_SIZE 0 /* Does host provide console size? */ #define VIRTIO_CONSOLE_F_MULTIPORT 1 /* Does host provide multiple ports? */ +#define VIRTIO_CONSOLE_F_EMERG_WRITE 2 /* Does host support emergency write? */ #define VIRTIO_CONSOLE_BAD_ID (~(u32)0) @@ -48,6 +49,8 @@ struct virtio_console_config { __u16 rows; /* max. number of ports this device can hold */ __u32 max_nr_ports; + /* emergency write register */ + __u32 emerg_wr; } __attribute__((packed)); /* -- cgit v1.2.3 From 2a647bfe1bf43af51530862b9ec70032bdd0a44c Mon Sep 17 00:00:00 2001 From: Jonghwan Choi Date: Mon, 20 May 2013 10:25:39 +0930 Subject: virtio_blk: Add missing 'static' qualifiers Add missing 'static' qualifiers Signed-off-by: Jonghwan Choi Acked-by: Michael S. Tsirkin Signed-off-by: Rusty Russell --- drivers/block/virtio_blk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index 64723953e1c9..5cdf88b7ad9e 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -20,7 +20,7 @@ module_param(use_bio, bool, S_IRUGO); static int major; static DEFINE_IDA(vd_index_ida); -struct workqueue_struct *virtblk_wq; +static struct workqueue_struct *virtblk_wq; struct virtio_blk { -- cgit v1.2.3 From 6865b32a865c76bfa02be383b52041b942cb232b Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Mon, 20 May 2013 10:25:39 +0930 Subject: lguest: rename i386_head.S Since commit 9a163ed8e0 (i386: move kernel) kernel/i386_head.S was renamed to kernel/head_32.S. We do the same for lguest/i386_head.S. Signed-off-by: Daniel Baluta Signed-off-by: Rusty Russell --- arch/x86/lguest/Makefile | 2 +- arch/x86/lguest/head_32.S | 196 ++++++++++++++++++++++++++++++++++++++++++++ arch/x86/lguest/i386_head.S | 196 -------------------------------------------- 3 files changed, 197 insertions(+), 197 deletions(-) create mode 100644 arch/x86/lguest/head_32.S delete mode 100644 arch/x86/lguest/i386_head.S diff --git a/arch/x86/lguest/Makefile b/arch/x86/lguest/Makefile index 94e0e54056a9..8f38d577a2fa 100644 --- a/arch/x86/lguest/Makefile +++ b/arch/x86/lguest/Makefile @@ -1,2 +1,2 @@ -obj-y := i386_head.o boot.o +obj-y := head_32.o boot.o CFLAGS_boot.o := $(call cc-option, -fno-stack-protector) diff --git a/arch/x86/lguest/head_32.S b/arch/x86/lguest/head_32.S new file mode 100644 index 000000000000..6ddfe4fc23c3 --- /dev/null +++ b/arch/x86/lguest/head_32.S @@ -0,0 +1,196 @@ +#include +#include +#include +#include +#include +#include + +/*G:020 + + * Our story starts with the bzImage: booting starts at startup_32 in + * arch/x86/boot/compressed/head_32.S. This merely uncompresses the real + * kernel in place and then jumps into it: startup_32 in + * arch/x86/kernel/head_32.S. Both routines expects a boot header in the %esi + * register, which is created by the bootloader (the Launcher in our case). + * + * The startup_32 function does very little: it clears the uninitialized global + * C variables which we expect to be zero (ie. BSS) and then copies the boot + * header and kernel command line somewhere safe, and populates some initial + * page tables. Finally it checks the 'hardware_subarch' field. This was + * introduced in 2.6.24 for lguest and Xen: if it's set to '1' (lguest's + * assigned number), then it calls us here. + * + * WARNING: be very careful here! We're running at addresses equal to physical + * addresses (around 0), not above PAGE_OFFSET as most code expects + * (eg. 0xC0000000). Jumps are relative, so they're OK, but we can't touch any + * data without remembering to subtract __PAGE_OFFSET! + * + * The .section line puts this code in .init.text so it will be discarded after + * boot. + */ +.section .init.text, "ax", @progbits +ENTRY(lguest_entry) + /* + * We make the "initialization" hypercall now to tell the Host where + * our lguest_data struct is. + */ + movl $LHCALL_LGUEST_INIT, %eax + movl $lguest_data - __PAGE_OFFSET, %ebx + int $LGUEST_TRAP_ENTRY + + /* Now turn our pagetables on; setup by arch/x86/kernel/head_32.S. */ + movl $LHCALL_NEW_PGTABLE, %eax + movl $(initial_page_table - __PAGE_OFFSET), %ebx + int $LGUEST_TRAP_ENTRY + + /* Set up the initial stack so we can run C code. */ + movl $(init_thread_union+THREAD_SIZE),%esp + + /* Jumps are relative: we're running __PAGE_OFFSET too low. */ + jmp lguest_init+__PAGE_OFFSET + +/*G:055 + * We create a macro which puts the assembler code between lgstart_ and lgend_ + * markers. These templates are put in the .text section: they can't be + * discarded after boot as we may need to patch modules, too. + */ +.text +#define LGUEST_PATCH(name, insns...) \ + lgstart_##name: insns; lgend_##name:; \ + .globl lgstart_##name; .globl lgend_##name + +LGUEST_PATCH(cli, movl $0, lguest_data+LGUEST_DATA_irq_enabled) +LGUEST_PATCH(pushf, movl lguest_data+LGUEST_DATA_irq_enabled, %eax) + +/*G:033 + * But using those wrappers is inefficient (we'll see why that doesn't matter + * for save_fl and irq_disable later). If we write our routines carefully in + * assembler, we can avoid clobbering any registers and avoid jumping through + * the wrapper functions. + * + * I skipped over our first piece of assembler, but this one is worth studying + * in a bit more detail so I'll describe in easy stages. First, the routine to + * enable interrupts: + */ +ENTRY(lg_irq_enable) + /* + * The reverse of irq_disable, this sets lguest_data.irq_enabled to + * X86_EFLAGS_IF (ie. "Interrupts enabled"). + */ + movl $X86_EFLAGS_IF, lguest_data+LGUEST_DATA_irq_enabled + /* + * But now we need to check if the Host wants to know: there might have + * been interrupts waiting to be delivered, in which case it will have + * set lguest_data.irq_pending to X86_EFLAGS_IF. If it's not zero, we + * jump to send_interrupts, otherwise we're done. + */ + testl $0, lguest_data+LGUEST_DATA_irq_pending + jnz send_interrupts + /* + * One cool thing about x86 is that you can do many things without using + * a register. In this case, the normal path hasn't needed to save or + * restore any registers at all! + */ + ret +send_interrupts: + /* + * OK, now we need a register: eax is used for the hypercall number, + * which is LHCALL_SEND_INTERRUPTS. + * + * We used not to bother with this pending detection at all, which was + * much simpler. Sooner or later the Host would realize it had to + * send us an interrupt. But that turns out to make performance 7 + * times worse on a simple tcp benchmark. So now we do this the hard + * way. + */ + pushl %eax + movl $LHCALL_SEND_INTERRUPTS, %eax + /* This is the actual hypercall trap. */ + int $LGUEST_TRAP_ENTRY + /* Put eax back the way we found it. */ + popl %eax + ret + +/* + * Finally, the "popf" or "restore flags" routine. The %eax register holds the + * flags (in practice, either X86_EFLAGS_IF or 0): if it's X86_EFLAGS_IF we're + * enabling interrupts again, if it's 0 we're leaving them off. + */ +ENTRY(lg_restore_fl) + /* This is just "lguest_data.irq_enabled = flags;" */ + movl %eax, lguest_data+LGUEST_DATA_irq_enabled + /* + * Now, if the %eax value has enabled interrupts and + * lguest_data.irq_pending is set, we want to tell the Host so it can + * deliver any outstanding interrupts. Fortunately, both values will + * be X86_EFLAGS_IF (ie. 512) in that case, and the "testl" + * instruction will AND them together for us. If both are set, we + * jump to send_interrupts. + */ + testl lguest_data+LGUEST_DATA_irq_pending, %eax + jnz send_interrupts + /* Again, the normal path has used no extra registers. Clever, huh? */ + ret +/*:*/ + +/* These demark the EIP range where host should never deliver interrupts. */ +.global lguest_noirq_start +.global lguest_noirq_end + +/*M:004 + * When the Host reflects a trap or injects an interrupt into the Guest, it + * sets the eflags interrupt bit on the stack based on lguest_data.irq_enabled, + * so the Guest iret logic does the right thing when restoring it. However, + * when the Host sets the Guest up for direct traps, such as system calls, the + * processor is the one to push eflags onto the stack, and the interrupt bit + * will be 1 (in reality, interrupts are always enabled in the Guest). + * + * This turns out to be harmless: the only trap which should happen under Linux + * with interrupts disabled is Page Fault (due to our lazy mapping of vmalloc + * regions), which has to be reflected through the Host anyway. If another + * trap *does* go off when interrupts are disabled, the Guest will panic, and + * we'll never get to this iret! +:*/ + +/*G:045 + * There is one final paravirt_op that the Guest implements, and glancing at it + * you can see why I left it to last. It's *cool*! It's in *assembler*! + * + * The "iret" instruction is used to return from an interrupt or trap. The + * stack looks like this: + * old address + * old code segment & privilege level + * old processor flags ("eflags") + * + * The "iret" instruction pops those values off the stack and restores them all + * at once. The only problem is that eflags includes the Interrupt Flag which + * the Guest can't change: the CPU will simply ignore it when we do an "iret". + * So we have to copy eflags from the stack to lguest_data.irq_enabled before + * we do the "iret". + * + * There are two problems with this: firstly, we need to use a register to do + * the copy and secondly, the whole thing needs to be atomic. The first + * problem is easy to solve: push %eax on the stack so we can use it, and then + * restore it at the end just before the real "iret". + * + * The second is harder: copying eflags to lguest_data.irq_enabled will turn + * interrupts on before we're finished, so we could be interrupted before we + * return to userspace or wherever. Our solution to this is to surround the + * code with lguest_noirq_start: and lguest_noirq_end: labels. We tell the + * Host that it is *never* to interrupt us there, even if interrupts seem to be + * enabled. + */ +ENTRY(lguest_iret) + pushl %eax + movl 12(%esp), %eax +lguest_noirq_start: + /* + * Note the %ss: segment prefix here. Normal data accesses use the + * "ds" segment, but that will have already been restored for whatever + * we're returning to (such as userspace): we can't trust it. The %ss: + * prefix makes sure we use the stack segment, which is still valid. + */ + movl %eax,%ss:lguest_data+LGUEST_DATA_irq_enabled + popl %eax + iret +lguest_noirq_end: diff --git a/arch/x86/lguest/i386_head.S b/arch/x86/lguest/i386_head.S deleted file mode 100644 index 6ddfe4fc23c3..000000000000 --- a/arch/x86/lguest/i386_head.S +++ /dev/null @@ -1,196 +0,0 @@ -#include -#include -#include -#include -#include -#include - -/*G:020 - - * Our story starts with the bzImage: booting starts at startup_32 in - * arch/x86/boot/compressed/head_32.S. This merely uncompresses the real - * kernel in place and then jumps into it: startup_32 in - * arch/x86/kernel/head_32.S. Both routines expects a boot header in the %esi - * register, which is created by the bootloader (the Launcher in our case). - * - * The startup_32 function does very little: it clears the uninitialized global - * C variables which we expect to be zero (ie. BSS) and then copies the boot - * header and kernel command line somewhere safe, and populates some initial - * page tables. Finally it checks the 'hardware_subarch' field. This was - * introduced in 2.6.24 for lguest and Xen: if it's set to '1' (lguest's - * assigned number), then it calls us here. - * - * WARNING: be very careful here! We're running at addresses equal to physical - * addresses (around 0), not above PAGE_OFFSET as most code expects - * (eg. 0xC0000000). Jumps are relative, so they're OK, but we can't touch any - * data without remembering to subtract __PAGE_OFFSET! - * - * The .section line puts this code in .init.text so it will be discarded after - * boot. - */ -.section .init.text, "ax", @progbits -ENTRY(lguest_entry) - /* - * We make the "initialization" hypercall now to tell the Host where - * our lguest_data struct is. - */ - movl $LHCALL_LGUEST_INIT, %eax - movl $lguest_data - __PAGE_OFFSET, %ebx - int $LGUEST_TRAP_ENTRY - - /* Now turn our pagetables on; setup by arch/x86/kernel/head_32.S. */ - movl $LHCALL_NEW_PGTABLE, %eax - movl $(initial_page_table - __PAGE_OFFSET), %ebx - int $LGUEST_TRAP_ENTRY - - /* Set up the initial stack so we can run C code. */ - movl $(init_thread_union+THREAD_SIZE),%esp - - /* Jumps are relative: we're running __PAGE_OFFSET too low. */ - jmp lguest_init+__PAGE_OFFSET - -/*G:055 - * We create a macro which puts the assembler code between lgstart_ and lgend_ - * markers. These templates are put in the .text section: they can't be - * discarded after boot as we may need to patch modules, too. - */ -.text -#define LGUEST_PATCH(name, insns...) \ - lgstart_##name: insns; lgend_##name:; \ - .globl lgstart_##name; .globl lgend_##name - -LGUEST_PATCH(cli, movl $0, lguest_data+LGUEST_DATA_irq_enabled) -LGUEST_PATCH(pushf, movl lguest_data+LGUEST_DATA_irq_enabled, %eax) - -/*G:033 - * But using those wrappers is inefficient (we'll see why that doesn't matter - * for save_fl and irq_disable later). If we write our routines carefully in - * assembler, we can avoid clobbering any registers and avoid jumping through - * the wrapper functions. - * - * I skipped over our first piece of assembler, but this one is worth studying - * in a bit more detail so I'll describe in easy stages. First, the routine to - * enable interrupts: - */ -ENTRY(lg_irq_enable) - /* - * The reverse of irq_disable, this sets lguest_data.irq_enabled to - * X86_EFLAGS_IF (ie. "Interrupts enabled"). - */ - movl $X86_EFLAGS_IF, lguest_data+LGUEST_DATA_irq_enabled - /* - * But now we need to check if the Host wants to know: there might have - * been interrupts waiting to be delivered, in which case it will have - * set lguest_data.irq_pending to X86_EFLAGS_IF. If it's not zero, we - * jump to send_interrupts, otherwise we're done. - */ - testl $0, lguest_data+LGUEST_DATA_irq_pending - jnz send_interrupts - /* - * One cool thing about x86 is that you can do many things without using - * a register. In this case, the normal path hasn't needed to save or - * restore any registers at all! - */ - ret -send_interrupts: - /* - * OK, now we need a register: eax is used for the hypercall number, - * which is LHCALL_SEND_INTERRUPTS. - * - * We used not to bother with this pending detection at all, which was - * much simpler. Sooner or later the Host would realize it had to - * send us an interrupt. But that turns out to make performance 7 - * times worse on a simple tcp benchmark. So now we do this the hard - * way. - */ - pushl %eax - movl $LHCALL_SEND_INTERRUPTS, %eax - /* This is the actual hypercall trap. */ - int $LGUEST_TRAP_ENTRY - /* Put eax back the way we found it. */ - popl %eax - ret - -/* - * Finally, the "popf" or "restore flags" routine. The %eax register holds the - * flags (in practice, either X86_EFLAGS_IF or 0): if it's X86_EFLAGS_IF we're - * enabling interrupts again, if it's 0 we're leaving them off. - */ -ENTRY(lg_restore_fl) - /* This is just "lguest_data.irq_enabled = flags;" */ - movl %eax, lguest_data+LGUEST_DATA_irq_enabled - /* - * Now, if the %eax value has enabled interrupts and - * lguest_data.irq_pending is set, we want to tell the Host so it can - * deliver any outstanding interrupts. Fortunately, both values will - * be X86_EFLAGS_IF (ie. 512) in that case, and the "testl" - * instruction will AND them together for us. If both are set, we - * jump to send_interrupts. - */ - testl lguest_data+LGUEST_DATA_irq_pending, %eax - jnz send_interrupts - /* Again, the normal path has used no extra registers. Clever, huh? */ - ret -/*:*/ - -/* These demark the EIP range where host should never deliver interrupts. */ -.global lguest_noirq_start -.global lguest_noirq_end - -/*M:004 - * When the Host reflects a trap or injects an interrupt into the Guest, it - * sets the eflags interrupt bit on the stack based on lguest_data.irq_enabled, - * so the Guest iret logic does the right thing when restoring it. However, - * when the Host sets the Guest up for direct traps, such as system calls, the - * processor is the one to push eflags onto the stack, and the interrupt bit - * will be 1 (in reality, interrupts are always enabled in the Guest). - * - * This turns out to be harmless: the only trap which should happen under Linux - * with interrupts disabled is Page Fault (due to our lazy mapping of vmalloc - * regions), which has to be reflected through the Host anyway. If another - * trap *does* go off when interrupts are disabled, the Guest will panic, and - * we'll never get to this iret! -:*/ - -/*G:045 - * There is one final paravirt_op that the Guest implements, and glancing at it - * you can see why I left it to last. It's *cool*! It's in *assembler*! - * - * The "iret" instruction is used to return from an interrupt or trap. The - * stack looks like this: - * old address - * old code segment & privilege level - * old processor flags ("eflags") - * - * The "iret" instruction pops those values off the stack and restores them all - * at once. The only problem is that eflags includes the Interrupt Flag which - * the Guest can't change: the CPU will simply ignore it when we do an "iret". - * So we have to copy eflags from the stack to lguest_data.irq_enabled before - * we do the "iret". - * - * There are two problems with this: firstly, we need to use a register to do - * the copy and secondly, the whole thing needs to be atomic. The first - * problem is easy to solve: push %eax on the stack so we can use it, and then - * restore it at the end just before the real "iret". - * - * The second is harder: copying eflags to lguest_data.irq_enabled will turn - * interrupts on before we're finished, so we could be interrupted before we - * return to userspace or wherever. Our solution to this is to surround the - * code with lguest_noirq_start: and lguest_noirq_end: labels. We tell the - * Host that it is *never* to interrupt us there, even if interrupts seem to be - * enabled. - */ -ENTRY(lguest_iret) - pushl %eax - movl 12(%esp), %eax -lguest_noirq_start: - /* - * Note the %ss: segment prefix here. Normal data accesses use the - * "ds" segment, but that will have already been restored for whatever - * we're returning to (such as userspace): we can't trust it. The %ss: - * prefix makes sure we use the stack segment, which is still valid. - */ - movl %eax,%ss:lguest_data+LGUEST_DATA_irq_enabled - popl %eax - iret -lguest_noirq_end: -- cgit v1.2.3 From b3087e48ce20be784fae1dbabc2e42e2ad0f21bc Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 20 May 2013 12:15:44 +0930 Subject: virtio: remove virtqueue_add_buf(). All users changed to virtqueue_add_sg() or virtqueue_add_outbuf/inbuf. Signed-off-by: Rusty Russell --- drivers/virtio/virtio_ring.c | 37 +++---------------------------------- include/linux/virtio.h | 7 ------- 2 files changed, 3 insertions(+), 41 deletions(-) diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index 5217baf5528c..c70207478e57 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -295,37 +295,6 @@ add_head: return 0; } -/** - * virtqueue_add_buf - expose buffer to other end - * @vq: the struct virtqueue we're talking about. - * @sg: the description of the buffer(s). - * @out_num: the number of sg readable by other side - * @in_num: the number of sg which are writable (after readable ones) - * @data: the token identifying the buffer. - * @gfp: how to do memory allocations (if necessary). - * - * Caller must ensure we don't call this with other virtqueue operations - * at the same time (except where noted). - * - * Returns zero or a negative error (ie. ENOSPC, ENOMEM). - */ -int virtqueue_add_buf(struct virtqueue *_vq, - struct scatterlist sg[], - unsigned int out, - unsigned int in, - void *data, - gfp_t gfp) -{ - struct scatterlist *sgs[2]; - - sgs[0] = sg; - sgs[1] = sg + out; - - return virtqueue_add(_vq, sgs, sg_next_arr, - out, in, out ? 1 : 0, in ? 1 : 0, data, gfp); -} -EXPORT_SYMBOL_GPL(virtqueue_add_buf); - /** * virtqueue_add_sgs - expose buffers to other end * @vq: the struct virtqueue we're talking about. @@ -473,7 +442,7 @@ EXPORT_SYMBOL_GPL(virtqueue_notify); * virtqueue_kick - update after add_buf * @vq: the struct virtqueue * - * After one or more virtqueue_add_buf calls, invoke this to kick + * After one or more virtqueue_add_* calls, invoke this to kick * the other side. * * Caller must ensure we don't call this with other virtqueue @@ -530,7 +499,7 @@ static inline bool more_used(const struct vring_virtqueue *vq) * operations at the same time (except where noted). * * Returns NULL if there are no used buffers, or the "data" token - * handed to virtqueue_add_buf(). + * handed to virtqueue_add_*(). */ void *virtqueue_get_buf(struct virtqueue *_vq, unsigned int *len) { @@ -685,7 +654,7 @@ EXPORT_SYMBOL_GPL(virtqueue_enable_cb_delayed); * virtqueue_detach_unused_buf - detach first unused buffer * @vq: the struct virtqueue we're talking about. * - * Returns NULL or the "data" token handed to virtqueue_add_buf(). + * Returns NULL or the "data" token handed to virtqueue_add_*(). * This is not valid on an active queue; it is useful only for device * shutdown. */ diff --git a/include/linux/virtio.h b/include/linux/virtio.h index 9ff8645b7e0b..e94c75ded111 100644 --- a/include/linux/virtio.h +++ b/include/linux/virtio.h @@ -34,13 +34,6 @@ struct virtqueue { void *priv; }; -int virtqueue_add_buf(struct virtqueue *vq, - struct scatterlist sg[], - unsigned int out_num, - unsigned int in_num, - void *data, - gfp_t gfp); - int virtqueue_add_outbuf(struct virtqueue *vq, struct scatterlist sg[], unsigned int num, void *data, -- cgit v1.2.3 From 3e59cb0ddfd2c59991f38e89352ad8a3c71b2374 Mon Sep 17 00:00:00 2001 From: Yuchung Cheng Date: Fri, 17 May 2013 13:45:05 +0000 Subject: tcp: remove bad timeout logic in fast recovery tcp_timeout_skb() was intended to trigger fast recovery on timeout, unfortunately in reality it often causes spurious retransmission storms during fast recovery. The particular sign is a fast retransmit over the highest sacked sequence (SND.FACK). Currently the RTO timer re-arming (as in RFC6298) offers a nice cushion to avoid spurious timeout: when SND.UNA advances the sender re-arms RTO and extends the timeout by icsk_rto. The sender does not offset the time elapsed since the packet at SND.UNA was sent. But if the next (DUP)ACK arrives later than ~RTTVAR and triggers tcp_fastretrans_alert(), then tcp_timeout_skb() will mark any packet sent before the icsk_rto interval lost, including one that's above the highest sacked sequence. Most likely a large part of scorebard will be marked. If most packets are not lost then the subsequent DUPACKs with new SACK blocks will cause the sender to continue to retransmit packets beyond SND.FACK spuriously. Even if only one packet is lost the sender may falsely retransmit almost the entire window. The situation becomes common in the world of bufferbloat: the RTT continues to grow as the queue builds up but RTTVAR remains small and close to the minimum 200ms. If a data packet is lost and the DUPACK triggered by the next data packet is slightly delayed, then a spurious retransmission storm forms. As the original comment on tcp_timeout_skb() suggests: the usefulness of this feature is questionable. It also wastes cycles walking the sack scoreboard and is actually harmful because of false recovery. It's time to remove this. Signed-off-by: Yuchung Cheng Acked-by: Eric Dumazet Acked-by: Neal Cardwell Acked-by: Nandita Dukkipati Signed-off-by: David S. Miller --- include/linux/tcp.h | 1 - include/net/tcp.h | 1 - net/ipv4/tcp_input.c | 65 +--------------------------------------------------- 3 files changed, 1 insertion(+), 66 deletions(-) diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 5adbc33d1ab3..472120b4fac5 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -246,7 +246,6 @@ struct tcp_sock { /* from STCP, retrans queue hinting */ struct sk_buff* lost_skb_hint; - struct sk_buff *scoreboard_skb_hint; struct sk_buff *retransmit_skb_hint; struct sk_buff_head out_of_order_queue; /* Out of order segments go here */ diff --git a/include/net/tcp.h b/include/net/tcp.h index 5bba80fbd1d9..e1c3723f161f 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1193,7 +1193,6 @@ static inline void tcp_mib_init(struct net *net) static inline void tcp_clear_retrans_hints_partial(struct tcp_sock *tp) { tp->lost_skb_hint = NULL; - tp->scoreboard_skb_hint = NULL; } static inline void tcp_clear_all_retrans_hints(struct tcp_sock *tp) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index b358e8c98607..d7d369428ae4 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -1255,8 +1255,6 @@ static bool tcp_shifted_skb(struct sock *sk, struct sk_buff *skb, if (skb == tp->retransmit_skb_hint) tp->retransmit_skb_hint = prev; - if (skb == tp->scoreboard_skb_hint) - tp->scoreboard_skb_hint = prev; if (skb == tp->lost_skb_hint) { tp->lost_skb_hint = prev; tp->lost_cnt_hint -= tcp_skb_pcount(prev); @@ -1964,20 +1962,6 @@ static bool tcp_pause_early_retransmit(struct sock *sk, int flag) return true; } -static inline int tcp_skb_timedout(const struct sock *sk, - const struct sk_buff *skb) -{ - return tcp_time_stamp - TCP_SKB_CB(skb)->when > inet_csk(sk)->icsk_rto; -} - -static inline int tcp_head_timedout(const struct sock *sk) -{ - const struct tcp_sock *tp = tcp_sk(sk); - - return tp->packets_out && - tcp_skb_timedout(sk, tcp_write_queue_head(sk)); -} - /* Linux NewReno/SACK/FACK/ECN state machine. * -------------------------------------- * @@ -2084,12 +2068,6 @@ static bool tcp_time_to_recover(struct sock *sk, int flag) if (tcp_dupack_heuristics(tp) > tp->reordering) return true; - /* Trick#3 : when we use RFC2988 timer restart, fast - * retransmit can be triggered by timeout of queue head. - */ - if (tcp_is_fack(tp) && tcp_head_timedout(sk)) - return true; - /* Trick#4: It is still not OK... But will it be useful to delay * recovery more? */ @@ -2126,44 +2104,6 @@ static bool tcp_time_to_recover(struct sock *sk, int flag) return false; } -/* New heuristics: it is possible only after we switched to restart timer - * each time when something is ACKed. Hence, we can detect timed out packets - * during fast retransmit without falling to slow start. - * - * Usefulness of this as is very questionable, since we should know which of - * the segments is the next to timeout which is relatively expensive to find - * in general case unless we add some data structure just for that. The - * current approach certainly won't find the right one too often and when it - * finally does find _something_ it usually marks large part of the window - * right away (because a retransmission with a larger timestamp blocks the - * loop from advancing). -ij - */ -static void tcp_timeout_skbs(struct sock *sk) -{ - struct tcp_sock *tp = tcp_sk(sk); - struct sk_buff *skb; - - if (!tcp_is_fack(tp) || !tcp_head_timedout(sk)) - return; - - skb = tp->scoreboard_skb_hint; - if (tp->scoreboard_skb_hint == NULL) - skb = tcp_write_queue_head(sk); - - tcp_for_write_queue_from(skb, sk) { - if (skb == tcp_send_head(sk)) - break; - if (!tcp_skb_timedout(sk, skb)) - break; - - tcp_skb_mark_lost(tp, skb); - } - - tp->scoreboard_skb_hint = skb; - - tcp_verify_left_out(tp); -} - /* Detect loss in event "A" above by marking head of queue up as lost. * For FACK or non-SACK(Reno) senders, the first "packets" number of segments * are considered lost. For RFC3517 SACK, a segment is considered lost if it @@ -2249,8 +2189,6 @@ static void tcp_update_scoreboard(struct sock *sk, int fast_rexmit) else if (fast_rexmit) tcp_mark_head_lost(sk, 1, 1); } - - tcp_timeout_skbs(sk); } /* CWND moderation, preventing bursts due to too big ACKs @@ -2842,7 +2780,7 @@ static void tcp_fastretrans_alert(struct sock *sk, int pkts_acked, fast_rexmit = 1; } - if (do_lost || (tcp_is_fack(tp) && tcp_head_timedout(sk))) + if (do_lost) tcp_update_scoreboard(sk, fast_rexmit); tcp_cwnd_reduction(sk, newly_acked_sacked, fast_rexmit); tcp_xmit_retransmit_queue(sk); @@ -3075,7 +3013,6 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets, tcp_unlink_write_queue(skb, sk); sk_wmem_free_skb(sk, skb); - tp->scoreboard_skb_hint = NULL; if (skb == tp->retransmit_skb_hint) tp->retransmit_skb_hint = NULL; if (skb == tp->lost_skb_hint) -- cgit v1.2.3 From 314beb9bcabfd6b4542ccbced2402af2c6f6142a Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 17 May 2013 16:37:03 +0000 Subject: x86: bpf_jit_comp: secure bpf jit against spraying attacks hpa bringed into my attention some security related issues with BPF JIT on x86. This patch makes sure the bpf generated code is marked read only, as other kernel text sections. It also splits the unused space (we vmalloc() and only use a fraction of the page) in two parts, so that the generated bpf code not starts at a known offset in the page, but a pseudo random one. Refs: http://mainisusuallyafunction.blogspot.com/2012/11/attacking-hardened-linux-systems-with.html Reported-by: H. Peter Anvin Signed-off-by: Eric Dumazet Reviewed-by: Daniel Borkmann Signed-off-by: David S. Miller --- arch/x86/net/bpf_jit_comp.c | 53 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c index c0212db40032..79c216aa0e2b 100644 --- a/arch/x86/net/bpf_jit_comp.c +++ b/arch/x86/net/bpf_jit_comp.c @@ -12,6 +12,7 @@ #include #include #include +#include /* * Conventions : @@ -144,6 +145,39 @@ static int pkt_type_offset(void) return -1; } +struct bpf_binary_header { + unsigned int pages; + /* Note : for security reasons, bpf code will follow a randomly + * sized amount of int3 instructions + */ + u8 image[]; +}; + +static struct bpf_binary_header *bpf_alloc_binary(unsigned int proglen, + u8 **image_ptr) +{ + unsigned int sz, hole; + struct bpf_binary_header *header; + + /* Most of BPF filters are really small, + * but if some of them fill a page, allow at least + * 128 extra bytes to insert a random section of int3 + */ + sz = round_up(proglen + sizeof(*header) + 128, PAGE_SIZE); + header = module_alloc(sz); + if (!header) + return NULL; + + memset(header, 0xcc, sz); /* fill whole space with int3 instructions */ + + header->pages = sz / PAGE_SIZE; + hole = sz - (proglen + sizeof(*header)); + + /* insert a random number of int3 instructions before BPF code */ + *image_ptr = &header->image[prandom_u32() % hole]; + return header; +} + void bpf_jit_compile(struct sk_filter *fp) { u8 temp[64]; @@ -153,6 +187,7 @@ void bpf_jit_compile(struct sk_filter *fp) int t_offset, f_offset; u8 t_op, f_op, seen = 0, pass; u8 *image = NULL; + struct bpf_binary_header *header = NULL; u8 *func; int pc_ret0 = -1; /* bpf index of first RET #0 instruction (if any) */ unsigned int cleanup_addr; /* epilogue code offset */ @@ -693,7 +728,7 @@ cond_branch: f_offset = addrs[i + filter[i].jf] - addrs[i]; if (unlikely(proglen + ilen > oldproglen)) { pr_err("bpb_jit_compile fatal error\n"); kfree(addrs); - module_free(NULL, image); + module_free(NULL, header); return; } memcpy(image + proglen, temp, ilen); @@ -717,8 +752,8 @@ cond_branch: f_offset = addrs[i + filter[i].jf] - addrs[i]; break; } if (proglen == oldproglen) { - image = module_alloc(proglen); - if (!image) + header = bpf_alloc_binary(proglen, &image); + if (!header) goto out; } oldproglen = proglen; @@ -728,7 +763,8 @@ cond_branch: f_offset = addrs[i + filter[i].jf] - addrs[i]; bpf_jit_dump(flen, proglen, pass, image); if (image) { - bpf_flush_icache(image, image + proglen); + bpf_flush_icache(header, image + proglen); + set_memory_ro((unsigned long)header, header->pages); fp->bpf_func = (void *)image; } out: @@ -738,6 +774,11 @@ out: void bpf_jit_free(struct sk_filter *fp) { - if (fp->bpf_func != sk_run_filter) - module_free(NULL, fp->bpf_func); + if (fp->bpf_func != sk_run_filter) { + unsigned long addr = (unsigned long)fp->bpf_func & PAGE_MASK; + struct bpf_binary_header *header = (void *)addr; + + set_memory_rw(addr, header->pages); + module_free(NULL, header); + } } -- cgit v1.2.3 From 164954454a4b1000eb022415654001cceb9259a7 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 17 May 2013 16:57:37 +0000 Subject: filter: do not output bpf image address for security reason Do not leak starting address of BPF JIT code for non root users, as it might help intruders to perform an attack. Signed-off-by: Eric Dumazet Cc: Ben Hutchings Cc: Daniel Borkmann Signed-off-by: David S. Miller --- include/linux/filter.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/filter.h b/include/linux/filter.h index c050dcc322a4..56a6b7fbb3c6 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -58,10 +58,10 @@ extern void bpf_jit_free(struct sk_filter *fp); static inline void bpf_jit_dump(unsigned int flen, unsigned int proglen, u32 pass, void *image) { - pr_err("flen=%u proglen=%u pass=%u image=%p\n", + pr_err("flen=%u proglen=%u pass=%u image=%pK\n", flen, proglen, pass, image); if (image) - print_hex_dump(KERN_ERR, "JIT code: ", DUMP_PREFIX_ADDRESS, + print_hex_dump(KERN_ERR, "JIT code: ", DUMP_PREFIX_OFFSET, 16, 1, image, proglen, false); } #define SK_RUN_FILTER(FILTER, SKB) (*FILTER->bpf_func)(SKB, FILTER->insns) -- cgit v1.2.3 From 9e2ecbeb250dee67f4bd06a18637c120b48a4865 Mon Sep 17 00:00:00 2001 From: Nithin Sujir Date: Sat, 18 May 2013 06:26:52 +0000 Subject: tg3: Add ethtool_eee struct and tg3_setup_eee() Add an eee structure and update it with eee settings. This will be used for set/get_eee operations. Add common function tg3_setup_eee() that will be used in the subsequent patches. Reviewed-by: Ben Li Signed-off-by: Michael Chan Signed-off-by: Nithin Nayak Sujir Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/tg3.c | 91 +++++++++++++++++++++++-------------- drivers/net/ethernet/broadcom/tg3.h | 1 + 2 files changed, 59 insertions(+), 33 deletions(-) diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 728d42ab2a76..26804db1f884 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -4249,6 +4249,16 @@ static int tg3_phy_autoneg_cfg(struct tg3 *tp, u32 advertise, u32 flowctrl) /* Advertise 1000-BaseT EEE ability */ if (advertise & ADVERTISED_1000baseT_Full) val |= MDIO_AN_EEE_ADV_1000T; + + if (!tp->eee.eee_enabled) { + val = 0; + tp->eee.advertised = 0; + } else { + tp->eee.advertised = advertise & + (ADVERTISED_100baseT_Full | + ADVERTISED_1000baseT_Full); + } + err = tg3_phy_cl45_write(tp, MDIO_MMD_AN, MDIO_AN_EEE_ADV, val); if (err) val = 0; @@ -4613,6 +4623,42 @@ static void tg3_clear_mac_status(struct tg3 *tp) udelay(40); } +static void tg3_setup_eee(struct tg3 *tp) +{ + u32 val; + + val = TG3_CPMU_EEE_LNKIDL_PCIE_NL0 | + TG3_CPMU_EEE_LNKIDL_UART_IDL; + if (tg3_chip_rev_id(tp) == CHIPREV_ID_57765_A0) + val |= TG3_CPMU_EEE_LNKIDL_APE_TX_MT; + + tw32_f(TG3_CPMU_EEE_LNKIDL_CTRL, val); + + tw32_f(TG3_CPMU_EEE_CTRL, + TG3_CPMU_EEE_CTRL_EXIT_20_1_US); + + val = TG3_CPMU_EEEMD_ERLY_L1_XIT_DET | + (tp->eee.tx_lpi_enabled ? TG3_CPMU_EEEMD_LPI_IN_TX : 0) | + TG3_CPMU_EEEMD_LPI_IN_RX | + TG3_CPMU_EEEMD_EEE_ENABLE; + + if (tg3_asic_rev(tp) != ASIC_REV_5717) + val |= TG3_CPMU_EEEMD_SND_IDX_DET_EN; + + if (tg3_flag(tp, ENABLE_APE)) + val |= TG3_CPMU_EEEMD_APE_TX_DET_EN; + + tw32_f(TG3_CPMU_EEE_MODE, tp->eee.eee_enabled ? val : 0); + + tw32_f(TG3_CPMU_EEE_DBTMR1, + TG3_CPMU_DBTMR1_PCIEXIT_2047US | + (tp->eee.tx_lpi_timer & 0xffff)); + + tw32_f(TG3_CPMU_EEE_DBTMR2, + TG3_CPMU_DBTMR2_APE_TX_2047US | + TG3_CPMU_DBTMR2_TXIDXEQ_2047US); +} + static int tg3_setup_copper_phy(struct tg3 *tp, bool force_reset) { bool current_link_up; @@ -9448,38 +9494,8 @@ static int tg3_reset_hw(struct tg3 *tp, bool reset_phy) tg3_abort_hw(tp, 1); /* Enable MAC control of LPI */ - if (tp->phy_flags & TG3_PHYFLG_EEE_CAP) { - val = TG3_CPMU_EEE_LNKIDL_PCIE_NL0 | - TG3_CPMU_EEE_LNKIDL_UART_IDL; - if (tg3_chip_rev_id(tp) == CHIPREV_ID_57765_A0) - val |= TG3_CPMU_EEE_LNKIDL_APE_TX_MT; - - tw32_f(TG3_CPMU_EEE_LNKIDL_CTRL, val); - - tw32_f(TG3_CPMU_EEE_CTRL, - TG3_CPMU_EEE_CTRL_EXIT_20_1_US); - - val = TG3_CPMU_EEEMD_ERLY_L1_XIT_DET | - TG3_CPMU_EEEMD_LPI_IN_TX | - TG3_CPMU_EEEMD_LPI_IN_RX | - TG3_CPMU_EEEMD_EEE_ENABLE; - - if (tg3_asic_rev(tp) != ASIC_REV_5717) - val |= TG3_CPMU_EEEMD_SND_IDX_DET_EN; - - if (tg3_flag(tp, ENABLE_APE)) - val |= TG3_CPMU_EEEMD_APE_TX_DET_EN; - - tw32_f(TG3_CPMU_EEE_MODE, val); - - tw32_f(TG3_CPMU_EEE_DBTMR1, - TG3_CPMU_DBTMR1_PCIEXIT_2047US | - TG3_CPMU_DBTMR1_LNKIDLE_2047US); - - tw32_f(TG3_CPMU_EEE_DBTMR2, - TG3_CPMU_DBTMR2_APE_TX_2047US | - TG3_CPMU_DBTMR2_TXIDXEQ_2047US); - } + if (tp->phy_flags & TG3_PHYFLG_EEE_CAP) + tg3_setup_eee(tp); if ((tp->phy_flags & TG3_PHYFLG_KEEP_LINK_ON_PWRDN) && !(tp->phy_flags & TG3_PHYFLG_USER_CONFIGURED)) { @@ -14946,9 +14962,18 @@ static int tg3_phy_probe(struct tg3 *tp) (tg3_asic_rev(tp) == ASIC_REV_5717 && tg3_chip_rev_id(tp) != CHIPREV_ID_5717_A0) || (tg3_asic_rev(tp) == ASIC_REV_57765 && - tg3_chip_rev_id(tp) != CHIPREV_ID_57765_A0))) + tg3_chip_rev_id(tp) != CHIPREV_ID_57765_A0))) { tp->phy_flags |= TG3_PHYFLG_EEE_CAP; + tp->eee.supported = SUPPORTED_100baseT_Full | + SUPPORTED_1000baseT_Full; + tp->eee.advertised = ADVERTISED_100baseT_Full | + ADVERTISED_1000baseT_Full; + tp->eee.eee_enabled = 1; + tp->eee.tx_lpi_enabled = 1; + tp->eee.tx_lpi_timer = TG3_CPMU_DBTMR1_LNKIDLE_2047US; + } + tg3_phy_init_link_config(tp); if (!(tp->phy_flags & TG3_PHYFLG_KEEP_LINK_ON_PWRDN) && diff --git a/drivers/net/ethernet/broadcom/tg3.h b/drivers/net/ethernet/broadcom/tg3.h index 9b2d3ac2474a..057532c5c56c 100644 --- a/drivers/net/ethernet/broadcom/tg3.h +++ b/drivers/net/ethernet/broadcom/tg3.h @@ -3371,6 +3371,7 @@ struct tg3 { unsigned int irq_cnt; struct ethtool_coalesce coal; + struct ethtool_eee eee; /* firmware info */ const char *fw_needed; -- cgit v1.2.3 From 400dfbaa8d444a29056b051a3d7082dc611e3b12 Mon Sep 17 00:00:00 2001 From: Nithin Sujir Date: Sat, 18 May 2013 06:26:53 +0000 Subject: tg3: Add tg3_eee_pull_config() function Add tg3_eee_pull_config() to pull the settings from the hardware and populate the eee structure. If Link Flap Avoidance is enabled, we pull the eee settings from the hw so as not to cause a phy reset on eee config mismatch later. This requires moving down tg3_setup_eee() below the tg3_pull_config() to not trample existing settings. Reviewed-by: Ben Li Signed-off-by: Michael Chan Signed-off-by: Nithin Nayak Sujir Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/tg3.c | 56 +++++++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 26804db1f884..3e7bc3837ef9 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -2320,6 +2320,46 @@ static void tg3_phy_apply_otp(struct tg3 *tp) tg3_phy_toggle_auxctl_smdsp(tp, false); } +static void tg3_eee_pull_config(struct tg3 *tp, struct ethtool_eee *eee) +{ + u32 val; + struct ethtool_eee *dest = &tp->eee; + + if (!(tp->phy_flags & TG3_PHYFLG_EEE_CAP)) + return; + + if (eee) + dest = eee; + + if (tg3_phy_cl45_read(tp, MDIO_MMD_AN, TG3_CL45_D7_EEERES_STAT, &val)) + return; + + /* Pull eee_active */ + if (val == TG3_CL45_D7_EEERES_STAT_LP_1000T || + val == TG3_CL45_D7_EEERES_STAT_LP_100TX) { + dest->eee_active = 1; + } else + dest->eee_active = 0; + + /* Pull lp advertised settings */ + if (tg3_phy_cl45_read(tp, MDIO_MMD_AN, MDIO_AN_EEE_LPABLE, &val)) + return; + dest->lp_advertised = mmd_eee_adv_to_ethtool_adv_t(val); + + /* Pull advertised and eee_enabled settings */ + if (tg3_phy_cl45_read(tp, MDIO_MMD_AN, MDIO_AN_EEE_ADV, &val)) + return; + dest->eee_enabled = !!val; + dest->advertised = mmd_eee_adv_to_ethtool_adv_t(val); + + /* Pull tx_lpi_enabled */ + val = tr32(TG3_CPMU_EEE_MODE); + dest->tx_lpi_enabled = !!(val & TG3_CPMU_EEEMD_LPI_IN_TX); + + /* Pull lpi timer value */ + dest->tx_lpi_timer = tr32(TG3_CPMU_EEE_DBTMR1) & 0xffff; +} + static void tg3_phy_eee_adjust(struct tg3 *tp, bool current_link_up) { u32 val; @@ -2343,11 +2383,8 @@ static void tg3_phy_eee_adjust(struct tg3 *tp, bool current_link_up) tw32(TG3_CPMU_EEE_CTRL, eeectl); - tg3_phy_cl45_read(tp, MDIO_MMD_AN, - TG3_CL45_D7_EEERES_STAT, &val); - - if (val == TG3_CL45_D7_EEERES_STAT_LP_1000T || - val == TG3_CL45_D7_EEERES_STAT_LP_100TX) + tg3_eee_pull_config(tp, NULL); + if (tp->eee.eee_active) tp->setlpicnt = 2; } @@ -9493,16 +9530,17 @@ static int tg3_reset_hw(struct tg3 *tp, bool reset_phy) if (tg3_flag(tp, INIT_COMPLETE)) tg3_abort_hw(tp, 1); - /* Enable MAC control of LPI */ - if (tp->phy_flags & TG3_PHYFLG_EEE_CAP) - tg3_setup_eee(tp); - if ((tp->phy_flags & TG3_PHYFLG_KEEP_LINK_ON_PWRDN) && !(tp->phy_flags & TG3_PHYFLG_USER_CONFIGURED)) { tg3_phy_pull_config(tp); + tg3_eee_pull_config(tp, NULL); tp->phy_flags |= TG3_PHYFLG_USER_CONFIGURED; } + /* Enable MAC control of LPI */ + if (tp->phy_flags & TG3_PHYFLG_EEE_CAP) + tg3_setup_eee(tp); + if (reset_phy) tg3_phy_reset(tp); -- cgit v1.2.3 From 5b6c273ad6c3886f30c7c5df7009e489043c59f3 Mon Sep 17 00:00:00 2001 From: Nithin Sujir Date: Sat, 18 May 2013 06:26:54 +0000 Subject: tg3: Simplify tg3_phy_eee_config_ok() by reusing tg3_eee_pull_config() eee_config_ok() was checking only for mismatch in advertised settings. This patch expands the scope of eee_config_ok() to check for mismatch in the other eee settings. On mismatch we will require a call to tg3_setup_eee() to push the configured settings to the hardware. Reviewed-by: Ben Li Signed-off-by: Michael Chan Signed-off-by: Nithin Nayak Sujir Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/tg3.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 3e7bc3837ef9..2e4974823853 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -4540,26 +4540,23 @@ static int tg3_init_5401phy_dsp(struct tg3 *tp) static bool tg3_phy_eee_config_ok(struct tg3 *tp) { - u32 val; - u32 tgtadv = 0; - u32 advertising = tp->link_config.advertising; + struct ethtool_eee eee; if (!(tp->phy_flags & TG3_PHYFLG_EEE_CAP)) return true; - if (tg3_phy_cl45_read(tp, MDIO_MMD_AN, MDIO_AN_EEE_ADV, &val)) - return false; - - val &= (MDIO_AN_EEE_ADV_100TX | MDIO_AN_EEE_ADV_1000T); - - - if (advertising & ADVERTISED_100baseT_Full) - tgtadv |= MDIO_AN_EEE_ADV_100TX; - if (advertising & ADVERTISED_1000baseT_Full) - tgtadv |= MDIO_AN_EEE_ADV_1000T; + tg3_eee_pull_config(tp, &eee); - if (val != tgtadv) - return false; + if (tp->eee.eee_enabled) { + if (tp->eee.advertised != eee.advertised || + tp->eee.tx_lpi_timer != eee.tx_lpi_timer || + tp->eee.tx_lpi_enabled != eee.tx_lpi_enabled) + return false; + } else { + /* EEE is disabled but we're advertising */ + if (eee.advertised) + return false; + } return true; } @@ -4862,8 +4859,10 @@ static int tg3_setup_copper_phy(struct tg3 *tp, bool force_reset) */ if (!eee_config_ok && (tp->phy_flags & TG3_PHYFLG_KEEP_LINK_ON_PWRDN) && - !force_reset) + !force_reset) { + tg3_setup_eee(tp); tg3_phy_reset(tp); + } } else { if (!(bmcr & BMCR_ANENABLE) && tp->link_config.speed == current_speed && -- cgit v1.2.3 From 1cbf9eb85a6601b58f01a71ff10299d2bf5d3365 Mon Sep 17 00:00:00 2001 From: Nithin Sujir Date: Sat, 18 May 2013 06:26:55 +0000 Subject: tg3: Implement set/get_eee handlers Reviewed-by: Ben Li Signed-off-by: Michael Chan Signed-off-by: Nithin Nayak Sujir Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/tg3.c | 53 +++++++++++++++++++++++++++++++++++++ drivers/net/ethernet/broadcom/tg3.h | 1 + 2 files changed, 54 insertions(+) diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 2e4974823853..fb06aa120293 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -13618,6 +13618,57 @@ static int tg3_set_coalesce(struct net_device *dev, struct ethtool_coalesce *ec) return 0; } +static int tg3_set_eee(struct net_device *dev, struct ethtool_eee *edata) +{ + struct tg3 *tp = netdev_priv(dev); + + if (!(tp->phy_flags & TG3_PHYFLG_EEE_CAP)) { + netdev_warn(tp->dev, "Board does not support EEE!\n"); + return -EOPNOTSUPP; + } + + if (edata->advertised != tp->eee.advertised) { + netdev_warn(tp->dev, + "Direct manipulation of EEE advertisement is not supported\n"); + return -EINVAL; + } + + if (edata->tx_lpi_timer > TG3_CPMU_DBTMR1_LNKIDLE_MAX) { + netdev_warn(tp->dev, + "Maximal Tx Lpi timer supported is %#x(u)\n", + TG3_CPMU_DBTMR1_LNKIDLE_MAX); + return -EINVAL; + } + + tp->eee = *edata; + + tp->phy_flags |= TG3_PHYFLG_USER_CONFIGURED; + tg3_warn_mgmt_link_flap(tp); + + if (netif_running(tp->dev)) { + tg3_full_lock(tp, 0); + tg3_setup_eee(tp); + tg3_phy_reset(tp); + tg3_full_unlock(tp); + } + + return 0; +} + +static int tg3_get_eee(struct net_device *dev, struct ethtool_eee *edata) +{ + struct tg3 *tp = netdev_priv(dev); + + if (!(tp->phy_flags & TG3_PHYFLG_EEE_CAP)) { + netdev_warn(tp->dev, + "Board does not support EEE!\n"); + return -EOPNOTSUPP; + } + + *edata = tp->eee; + return 0; +} + static const struct ethtool_ops tg3_ethtool_ops = { .get_settings = tg3_get_settings, .set_settings = tg3_set_settings, @@ -13651,6 +13702,8 @@ static const struct ethtool_ops tg3_ethtool_ops = { .get_channels = tg3_get_channels, .set_channels = tg3_set_channels, .get_ts_info = tg3_get_ts_info, + .get_eee = tg3_get_eee, + .set_eee = tg3_set_eee, }; static struct rtnl_link_stats64 *tg3_get_stats64(struct net_device *dev, diff --git a/drivers/net/ethernet/broadcom/tg3.h b/drivers/net/ethernet/broadcom/tg3.h index 057532c5c56c..2530c20dd823 100644 --- a/drivers/net/ethernet/broadcom/tg3.h +++ b/drivers/net/ethernet/broadcom/tg3.h @@ -1175,6 +1175,7 @@ #define TG3_CPMU_EEE_DBTMR1 0x000036b4 #define TG3_CPMU_DBTMR1_PCIEXIT_2047US 0x07ff0000 #define TG3_CPMU_DBTMR1_LNKIDLE_2047US 0x000007ff +#define TG3_CPMU_DBTMR1_LNKIDLE_MAX 0x0000ffff #define TG3_CPMU_EEE_DBTMR2 0x000036b8 #define TG3_CPMU_DBTMR2_APE_TX_2047US 0x07ff0000 #define TG3_CPMU_DBTMR2_TXIDXEQ_2047US 0x000007ff -- cgit v1.2.3 From 8802f5790ef3e2e1d907169557e3dd2e0e77d98f Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sat, 18 May 2013 07:14:53 +0000 Subject: net-bnx2x: dont reload on GRO change bnx2x_set_features() forces a driver reload if GRO setting is changed. A reload makes the ethernet port unresponsive for about 5 seconds. This is not needed in the common case LRO is enabled, as LRO (TPA_ENABLE_FLAG) has precedence over GRO (GRO_ENABLE_FLAG) Tested: Verified that "ethtool -K eth0 gro {on|off}" doesn't blackout the NIC anymore Google-Bug-Id: 8440442 Signed-off-by: Eric Dumazet Cc: Dmitry Kravkov Acked-by: Dmitry Kravkov Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index b8fbe266ab68..6cc5101fa96b 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -4599,6 +4599,7 @@ int bnx2x_set_features(struct net_device *dev, netdev_features_t features) { struct bnx2x *bp = netdev_priv(dev); u32 flags = bp->flags; + u32 changes; bool bnx2x_reload = false; if (features & NETIF_F_LRO) @@ -4623,10 +4624,16 @@ int bnx2x_set_features(struct net_device *dev, netdev_features_t features) } } - if (flags ^ bp->flags) { - bp->flags = flags; + changes = flags ^ bp->flags; + + /* if GRO is changed while LRO is enabled, dont force a reload */ + if ((changes & GRO_ENABLE_FLAG) && (flags & TPA_ENABLE_FLAG)) + changes &= ~GRO_ENABLE_FLAG; + + if (changes) bnx2x_reload = true; - } + + bp->flags = flags; if (bnx2x_reload) { if (bp->recovery_state == BNX2X_RECOVERY_DONE) -- cgit v1.2.3 From 1e18583adccfc122b5d6415cfe4bf1826c370f4e Mon Sep 17 00:00:00 2001 From: Rolf Eike Beer Date: Sat, 18 May 2013 11:50:17 +0000 Subject: ThunderLAN: remove is_eisa flag These 2 places are the only matches for is_eisa in the whole tree. Signed-off-by: Rolf Eike Beer Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/tlan.c | 1 - drivers/net/ethernet/ti/tlan.h | 1 - 2 files changed, 2 deletions(-) diff --git a/drivers/net/ethernet/ti/tlan.c b/drivers/net/ethernet/ti/tlan.c index 60c400f6d01f..59abfbcd0d55 100644 --- a/drivers/net/ethernet/ti/tlan.c +++ b/drivers/net/ethernet/ti/tlan.c @@ -533,7 +533,6 @@ static int tlan_probe1(struct pci_dev *pdev, long ioaddr, int irq, int rev, /* This is a hack. We need to know which board structure * is suited for this adapter */ device_id = inw(ioaddr + EISA_ID2); - priv->is_eisa = 1; if (device_id == 0x20F1) { priv->adapter = &board_info[13]; /* NetFlex-3/E */ priv->adapter_rev = 23; /* TLAN 2.3 */ diff --git a/drivers/net/ethernet/ti/tlan.h b/drivers/net/ethernet/ti/tlan.h index 5fc98a8e4889..2eb33a250788 100644 --- a/drivers/net/ethernet/ti/tlan.h +++ b/drivers/net/ethernet/ti/tlan.h @@ -207,7 +207,6 @@ struct tlan_priv { u8 tlan_full_duplex; spinlock_t lock; u8 link; - u8 is_eisa; struct work_struct tlan_tqueue; u8 neg_be_verbose; }; -- cgit v1.2.3 From 2d31e518a42828df7877bca23a958627d60408bc Mon Sep 17 00:00:00 2001 From: Tim Chen Date: Wed, 1 May 2013 12:52:48 -0700 Subject: crypto: crct10dif - Wrap crc_t10dif function all to use crypto transform framework When CRC T10 DIF is calculated using the crypto transform framework, we wrap the crc_t10dif function call to utilize it. This allows us to take advantage of any accelerated CRC T10 DIF transform that is plugged into the crypto framework. Signed-off-by: Tim Chen Signed-off-by: Herbert Xu --- crypto/Kconfig | 8 ++ crypto/Makefile | 1 + crypto/crct10dif.c | 178 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/crc-t10dif.h | 4 + lib/Kconfig | 2 + lib/crc-t10dif.c | 75 ++++++++----------- 6 files changed, 225 insertions(+), 43 deletions(-) create mode 100644 crypto/crct10dif.c diff --git a/crypto/Kconfig b/crypto/Kconfig index 622d8a48cbe9..ceb3611efe44 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -376,6 +376,14 @@ config CRYPTO_CRC32_PCLMUL which will enable any routine to use the CRC-32-IEEE 802.3 checksum and gain better performance as compared with the table implementation. +config CRYPTO_CRCT10DIF + tristate "CRCT10DIF algorithm" + select CRYPTO_HASH + help + CRC T10 Data Integrity Field computation is being cast as + a crypto transform. This allows for faster crc t10 diff + transforms to be used if they are available. + config CRYPTO_GHASH tristate "GHASH digest algorithm" select CRYPTO_GF128MUL diff --git a/crypto/Makefile b/crypto/Makefile index a8e9b0fefbe9..62af87df8729 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -83,6 +83,7 @@ obj-$(CONFIG_CRYPTO_ZLIB) += zlib.o obj-$(CONFIG_CRYPTO_MICHAEL_MIC) += michael_mic.o obj-$(CONFIG_CRYPTO_CRC32C) += crc32c.o obj-$(CONFIG_CRYPTO_CRC32) += crc32.o +obj-$(CONFIG_CRYPTO_CRCT10DIF) += crct10dif.o obj-$(CONFIG_CRYPTO_AUTHENC) += authenc.o authencesn.o obj-$(CONFIG_CRYPTO_LZO) += lzo.o obj-$(CONFIG_CRYPTO_842) += 842.o diff --git a/crypto/crct10dif.c b/crypto/crct10dif.c new file mode 100644 index 000000000000..92aca96d6b98 --- /dev/null +++ b/crypto/crct10dif.c @@ -0,0 +1,178 @@ +/* + * Cryptographic API. + * + * T10 Data Integrity Field CRC16 Crypto Transform + * + * Copyright (c) 2007 Oracle Corporation. All rights reserved. + * Written by Martin K. Petersen + * Copyright (C) 2013 Intel Corporation + * Author: Tim Chen + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +struct chksum_desc_ctx { + __u16 crc; +}; + +/* Table generated using the following polynomium: + * x^16 + x^15 + x^11 + x^9 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1 + * gt: 0x8bb7 + */ +static const __u16 t10_dif_crc_table[256] = { + 0x0000, 0x8BB7, 0x9CD9, 0x176E, 0xB205, 0x39B2, 0x2EDC, 0xA56B, + 0xEFBD, 0x640A, 0x7364, 0xF8D3, 0x5DB8, 0xD60F, 0xC161, 0x4AD6, + 0x54CD, 0xDF7A, 0xC814, 0x43A3, 0xE6C8, 0x6D7F, 0x7A11, 0xF1A6, + 0xBB70, 0x30C7, 0x27A9, 0xAC1E, 0x0975, 0x82C2, 0x95AC, 0x1E1B, + 0xA99A, 0x222D, 0x3543, 0xBEF4, 0x1B9F, 0x9028, 0x8746, 0x0CF1, + 0x4627, 0xCD90, 0xDAFE, 0x5149, 0xF422, 0x7F95, 0x68FB, 0xE34C, + 0xFD57, 0x76E0, 0x618E, 0xEA39, 0x4F52, 0xC4E5, 0xD38B, 0x583C, + 0x12EA, 0x995D, 0x8E33, 0x0584, 0xA0EF, 0x2B58, 0x3C36, 0xB781, + 0xD883, 0x5334, 0x445A, 0xCFED, 0x6A86, 0xE131, 0xF65F, 0x7DE8, + 0x373E, 0xBC89, 0xABE7, 0x2050, 0x853B, 0x0E8C, 0x19E2, 0x9255, + 0x8C4E, 0x07F9, 0x1097, 0x9B20, 0x3E4B, 0xB5FC, 0xA292, 0x2925, + 0x63F3, 0xE844, 0xFF2A, 0x749D, 0xD1F6, 0x5A41, 0x4D2F, 0xC698, + 0x7119, 0xFAAE, 0xEDC0, 0x6677, 0xC31C, 0x48AB, 0x5FC5, 0xD472, + 0x9EA4, 0x1513, 0x027D, 0x89CA, 0x2CA1, 0xA716, 0xB078, 0x3BCF, + 0x25D4, 0xAE63, 0xB90D, 0x32BA, 0x97D1, 0x1C66, 0x0B08, 0x80BF, + 0xCA69, 0x41DE, 0x56B0, 0xDD07, 0x786C, 0xF3DB, 0xE4B5, 0x6F02, + 0x3AB1, 0xB106, 0xA668, 0x2DDF, 0x88B4, 0x0303, 0x146D, 0x9FDA, + 0xD50C, 0x5EBB, 0x49D5, 0xC262, 0x6709, 0xECBE, 0xFBD0, 0x7067, + 0x6E7C, 0xE5CB, 0xF2A5, 0x7912, 0xDC79, 0x57CE, 0x40A0, 0xCB17, + 0x81C1, 0x0A76, 0x1D18, 0x96AF, 0x33C4, 0xB873, 0xAF1D, 0x24AA, + 0x932B, 0x189C, 0x0FF2, 0x8445, 0x212E, 0xAA99, 0xBDF7, 0x3640, + 0x7C96, 0xF721, 0xE04F, 0x6BF8, 0xCE93, 0x4524, 0x524A, 0xD9FD, + 0xC7E6, 0x4C51, 0x5B3F, 0xD088, 0x75E3, 0xFE54, 0xE93A, 0x628D, + 0x285B, 0xA3EC, 0xB482, 0x3F35, 0x9A5E, 0x11E9, 0x0687, 0x8D30, + 0xE232, 0x6985, 0x7EEB, 0xF55C, 0x5037, 0xDB80, 0xCCEE, 0x4759, + 0x0D8F, 0x8638, 0x9156, 0x1AE1, 0xBF8A, 0x343D, 0x2353, 0xA8E4, + 0xB6FF, 0x3D48, 0x2A26, 0xA191, 0x04FA, 0x8F4D, 0x9823, 0x1394, + 0x5942, 0xD2F5, 0xC59B, 0x4E2C, 0xEB47, 0x60F0, 0x779E, 0xFC29, + 0x4BA8, 0xC01F, 0xD771, 0x5CC6, 0xF9AD, 0x721A, 0x6574, 0xEEC3, + 0xA415, 0x2FA2, 0x38CC, 0xB37B, 0x1610, 0x9DA7, 0x8AC9, 0x017E, + 0x1F65, 0x94D2, 0x83BC, 0x080B, 0xAD60, 0x26D7, 0x31B9, 0xBA0E, + 0xF0D8, 0x7B6F, 0x6C01, 0xE7B6, 0x42DD, 0xC96A, 0xDE04, 0x55B3 +}; + +__u16 crc_t10dif_generic(__u16 crc, const unsigned char *buffer, size_t len) +{ + unsigned int i; + + for (i = 0 ; i < len ; i++) + crc = (crc << 8) ^ t10_dif_crc_table[((crc >> 8) ^ buffer[i]) & 0xff]; + + return crc; +} +EXPORT_SYMBOL(crc_t10dif_generic); + +/* + * Steps through buffer one byte at at time, calculates reflected + * crc using table. + */ + +static int chksum_init(struct shash_desc *desc) +{ + struct chksum_desc_ctx *ctx = shash_desc_ctx(desc); + + ctx->crc = 0; + + return 0; +} + +static int chksum_update(struct shash_desc *desc, const u8 *data, + unsigned int length) +{ + struct chksum_desc_ctx *ctx = shash_desc_ctx(desc); + + ctx->crc = crc_t10dif_generic(ctx->crc, data, length); + return 0; +} + +static int chksum_final(struct shash_desc *desc, u8 *out) +{ + struct chksum_desc_ctx *ctx = shash_desc_ctx(desc); + + *(__u16 *)out = ctx->crc; + return 0; +} + +static int __chksum_finup(__u16 *crcp, const u8 *data, unsigned int len, + u8 *out) +{ + *(__u16 *)out = crc_t10dif_generic(*crcp, data, len); + return 0; +} + +static int chksum_finup(struct shash_desc *desc, const u8 *data, + unsigned int len, u8 *out) +{ + struct chksum_desc_ctx *ctx = shash_desc_ctx(desc); + + return __chksum_finup(&ctx->crc, data, len, out); +} + +static int chksum_digest(struct shash_desc *desc, const u8 *data, + unsigned int length, u8 *out) +{ + struct chksum_desc_ctx *ctx = shash_desc_ctx(desc); + + return __chksum_finup(&ctx->crc, data, length, out); +} + +static struct shash_alg alg = { + .digestsize = CRC_T10DIF_DIGEST_SIZE, + .init = chksum_init, + .update = chksum_update, + .final = chksum_final, + .finup = chksum_finup, + .digest = chksum_digest, + .descsize = sizeof(struct chksum_desc_ctx), + .base = { + .cra_name = "crct10dif", + .cra_driver_name = "crct10dif-generic", + .cra_priority = 100, + .cra_blocksize = CRC_T10DIF_BLOCK_SIZE, + .cra_module = THIS_MODULE, + } +}; + +static int __init crct10dif_mod_init(void) +{ + int ret; + + ret = crypto_register_shash(&alg); + return ret; +} + +static void __exit crct10dif_mod_fini(void) +{ + crypto_unregister_shash(&alg); +} + +module_init(crct10dif_mod_init); +module_exit(crct10dif_mod_fini); + +MODULE_AUTHOR("Tim Chen "); +MODULE_DESCRIPTION("T10 DIF CRC calculation."); +MODULE_LICENSE("GPL"); diff --git a/include/linux/crc-t10dif.h b/include/linux/crc-t10dif.h index a9c96d865ee7..b3cb71f0d3b0 100644 --- a/include/linux/crc-t10dif.h +++ b/include/linux/crc-t10dif.h @@ -3,6 +3,10 @@ #include +#define CRC_T10DIF_DIGEST_SIZE 2 +#define CRC_T10DIF_BLOCK_SIZE 1 + +__u16 crc_t10dif_generic(__u16 crc, const unsigned char *buffer, size_t len); __u16 crc_t10dif(unsigned char const *, size_t); #endif diff --git a/lib/Kconfig b/lib/Kconfig index fe01d418b09a..339d5a4292f8 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -63,6 +63,8 @@ config CRC16 config CRC_T10DIF tristate "CRC calculation for the T10 Data Integrity Field" + select CRYPTO + select CRYPTO_CRCT10DIF help This option is only needed if a module that's not in the kernel tree needs to calculate CRC checks for use with the diff --git a/lib/crc-t10dif.c b/lib/crc-t10dif.c index fbbd66ed86cd..0cb64634f513 100644 --- a/lib/crc-t10dif.c +++ b/lib/crc-t10dif.c @@ -11,57 +11,46 @@ #include #include #include +#include +#include +#include -/* Table generated using the following polynomium: - * x^16 + x^15 + x^11 + x^9 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1 - * gt: 0x8bb7 - */ -static const __u16 t10_dif_crc_table[256] = { - 0x0000, 0x8BB7, 0x9CD9, 0x176E, 0xB205, 0x39B2, 0x2EDC, 0xA56B, - 0xEFBD, 0x640A, 0x7364, 0xF8D3, 0x5DB8, 0xD60F, 0xC161, 0x4AD6, - 0x54CD, 0xDF7A, 0xC814, 0x43A3, 0xE6C8, 0x6D7F, 0x7A11, 0xF1A6, - 0xBB70, 0x30C7, 0x27A9, 0xAC1E, 0x0975, 0x82C2, 0x95AC, 0x1E1B, - 0xA99A, 0x222D, 0x3543, 0xBEF4, 0x1B9F, 0x9028, 0x8746, 0x0CF1, - 0x4627, 0xCD90, 0xDAFE, 0x5149, 0xF422, 0x7F95, 0x68FB, 0xE34C, - 0xFD57, 0x76E0, 0x618E, 0xEA39, 0x4F52, 0xC4E5, 0xD38B, 0x583C, - 0x12EA, 0x995D, 0x8E33, 0x0584, 0xA0EF, 0x2B58, 0x3C36, 0xB781, - 0xD883, 0x5334, 0x445A, 0xCFED, 0x6A86, 0xE131, 0xF65F, 0x7DE8, - 0x373E, 0xBC89, 0xABE7, 0x2050, 0x853B, 0x0E8C, 0x19E2, 0x9255, - 0x8C4E, 0x07F9, 0x1097, 0x9B20, 0x3E4B, 0xB5FC, 0xA292, 0x2925, - 0x63F3, 0xE844, 0xFF2A, 0x749D, 0xD1F6, 0x5A41, 0x4D2F, 0xC698, - 0x7119, 0xFAAE, 0xEDC0, 0x6677, 0xC31C, 0x48AB, 0x5FC5, 0xD472, - 0x9EA4, 0x1513, 0x027D, 0x89CA, 0x2CA1, 0xA716, 0xB078, 0x3BCF, - 0x25D4, 0xAE63, 0xB90D, 0x32BA, 0x97D1, 0x1C66, 0x0B08, 0x80BF, - 0xCA69, 0x41DE, 0x56B0, 0xDD07, 0x786C, 0xF3DB, 0xE4B5, 0x6F02, - 0x3AB1, 0xB106, 0xA668, 0x2DDF, 0x88B4, 0x0303, 0x146D, 0x9FDA, - 0xD50C, 0x5EBB, 0x49D5, 0xC262, 0x6709, 0xECBE, 0xFBD0, 0x7067, - 0x6E7C, 0xE5CB, 0xF2A5, 0x7912, 0xDC79, 0x57CE, 0x40A0, 0xCB17, - 0x81C1, 0x0A76, 0x1D18, 0x96AF, 0x33C4, 0xB873, 0xAF1D, 0x24AA, - 0x932B, 0x189C, 0x0FF2, 0x8445, 0x212E, 0xAA99, 0xBDF7, 0x3640, - 0x7C96, 0xF721, 0xE04F, 0x6BF8, 0xCE93, 0x4524, 0x524A, 0xD9FD, - 0xC7E6, 0x4C51, 0x5B3F, 0xD088, 0x75E3, 0xFE54, 0xE93A, 0x628D, - 0x285B, 0xA3EC, 0xB482, 0x3F35, 0x9A5E, 0x11E9, 0x0687, 0x8D30, - 0xE232, 0x6985, 0x7EEB, 0xF55C, 0x5037, 0xDB80, 0xCCEE, 0x4759, - 0x0D8F, 0x8638, 0x9156, 0x1AE1, 0xBF8A, 0x343D, 0x2353, 0xA8E4, - 0xB6FF, 0x3D48, 0x2A26, 0xA191, 0x04FA, 0x8F4D, 0x9823, 0x1394, - 0x5942, 0xD2F5, 0xC59B, 0x4E2C, 0xEB47, 0x60F0, 0x779E, 0xFC29, - 0x4BA8, 0xC01F, 0xD771, 0x5CC6, 0xF9AD, 0x721A, 0x6574, 0xEEC3, - 0xA415, 0x2FA2, 0x38CC, 0xB37B, 0x1610, 0x9DA7, 0x8AC9, 0x017E, - 0x1F65, 0x94D2, 0x83BC, 0x080B, 0xAD60, 0x26D7, 0x31B9, 0xBA0E, - 0xF0D8, 0x7B6F, 0x6C01, 0xE7B6, 0x42DD, 0xC96A, 0xDE04, 0x55B3 -}; +static struct crypto_shash *crct10dif_tfm; __u16 crc_t10dif(const unsigned char *buffer, size_t len) { - __u16 crc = 0; - unsigned int i; + struct { + struct shash_desc shash; + char ctx[2]; + } desc; + int err; + + desc.shash.tfm = crct10dif_tfm; + desc.shash.flags = 0; + *(__u16 *)desc.ctx = 0; - for (i = 0 ; i < len ; i++) - crc = (crc << 8) ^ t10_dif_crc_table[((crc >> 8) ^ buffer[i]) & 0xff]; + err = crypto_shash_update(&desc.shash, buffer, len); + BUG_ON(err); - return crc; + return *(__u16 *)desc.ctx; } EXPORT_SYMBOL(crc_t10dif); +static int __init crc_t10dif_mod_init(void) +{ + crct10dif_tfm = crypto_alloc_shash("crct10dif", 0, 0); + if (IS_ERR(crct10dif_tfm)) + return PTR_ERR(crct10dif_tfm); + return 0; +} + +static void __exit crc_t10dif_mod_fini(void) +{ + crypto_free_shash(crct10dif_tfm); +} + +module_init(crc_t10dif_mod_init); +module_exit(crc_t10dif_mod_fini); + MODULE_DESCRIPTION("T10 DIF CRC calculation"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 31d939625a9a20b1badd2d4e6bf6fd39fa523405 Mon Sep 17 00:00:00 2001 From: Tim Chen Date: Wed, 1 May 2013 12:52:49 -0700 Subject: crypto: crct10dif - Accelerated CRC T10 DIF computation with PCLMULQDQ instruction This is the x86_64 CRC T10 DIF transform accelerated with the PCLMULQDQ instructions. Details discussing the implementation can be found in the paper: "Fast CRC Computation for Generic Polynomials Using PCLMULQDQ Instruction" http://www.intel.com/content/dam/www/public/us/en/documents/white-papers/fast-crc-computation-generic-polynomials-pclmulqdq-paper.pdf Signed-off-by: Tim Chen Signed-off-by: Herbert Xu --- arch/x86/crypto/crct10dif-pcl-asm_64.S | 643 +++++++++++++++++++++++++++++++++ 1 file changed, 643 insertions(+) create mode 100644 arch/x86/crypto/crct10dif-pcl-asm_64.S diff --git a/arch/x86/crypto/crct10dif-pcl-asm_64.S b/arch/x86/crypto/crct10dif-pcl-asm_64.S new file mode 100644 index 000000000000..35e97569d05f --- /dev/null +++ b/arch/x86/crypto/crct10dif-pcl-asm_64.S @@ -0,0 +1,643 @@ +######################################################################## +# Implement fast CRC-T10DIF computation with SSE and PCLMULQDQ instructions +# +# Copyright (c) 2013, Intel Corporation +# +# Authors: +# Erdinc Ozturk +# Vinodh Gopal +# James Guilford +# Tim Chen +# +# This software is available to you under a choice of one of two +# licenses. You may choose to be licensed under the terms of the GNU +# General Public License (GPL) Version 2, available from the file +# COPYING in the main directory of this source tree, or the +# OpenIB.org BSD license below: +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the +# distribution. +# +# * Neither the name of the Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# +# THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION ""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 INTEL CORPORATION OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +######################################################################## +# Function API: +# UINT16 crc_t10dif_pcl( +# UINT16 init_crc, //initial CRC value, 16 bits +# const unsigned char *buf, //buffer pointer to calculate CRC on +# UINT64 len //buffer length in bytes (64-bit data) +# ); +# +# Reference paper titled "Fast CRC Computation for Generic +# Polynomials Using PCLMULQDQ Instruction" +# URL: http://www.intel.com/content/dam/www/public/us/en/documents +# /white-papers/fast-crc-computation-generic-polynomials-pclmulqdq-paper.pdf +# +# + +#include + +.text + +#define arg1 %rdi +#define arg2 %rsi +#define arg3 %rdx + +#define arg1_low32 %edi + +ENTRY(crc_t10dif_pcl) +.align 16 + + # adjust the 16-bit initial_crc value, scale it to 32 bits + shl $16, arg1_low32 + + # Allocate Stack Space + mov %rsp, %rcx + sub $16*2, %rsp + # align stack to 16 byte boundary + and $~(0x10 - 1), %rsp + + # check if smaller than 256 + cmp $256, arg3 + + # for sizes less than 128, we can't fold 64B at a time... + jl _less_than_128 + + + # load the initial crc value + movd arg1_low32, %xmm10 # initial crc + + # crc value does not need to be byte-reflected, but it needs + # to be moved to the high part of the register. + # because data will be byte-reflected and will align with + # initial crc at correct place. + pslldq $12, %xmm10 + + movdqa SHUF_MASK(%rip), %xmm11 + # receive the initial 64B data, xor the initial crc value + movdqu 16*0(arg2), %xmm0 + movdqu 16*1(arg2), %xmm1 + movdqu 16*2(arg2), %xmm2 + movdqu 16*3(arg2), %xmm3 + movdqu 16*4(arg2), %xmm4 + movdqu 16*5(arg2), %xmm5 + movdqu 16*6(arg2), %xmm6 + movdqu 16*7(arg2), %xmm7 + + pshufb %xmm11, %xmm0 + # XOR the initial_crc value + pxor %xmm10, %xmm0 + pshufb %xmm11, %xmm1 + pshufb %xmm11, %xmm2 + pshufb %xmm11, %xmm3 + pshufb %xmm11, %xmm4 + pshufb %xmm11, %xmm5 + pshufb %xmm11, %xmm6 + pshufb %xmm11, %xmm7 + + movdqa rk3(%rip), %xmm10 #xmm10 has rk3 and rk4 + #imm value of pclmulqdq instruction + #will determine which constant to use + + ################################################################# + # we subtract 256 instead of 128 to save one instruction from the loop + sub $256, arg3 + + # at this section of the code, there is 64*x+y (0<=y<64) bytes of + # buffer. The _fold_64_B_loop will fold 64B at a time + # until we have 64+y Bytes of buffer + + + # fold 64B at a time. This section of the code folds 4 xmm + # registers in parallel +_fold_64_B_loop: + + # update the buffer pointer + add $128, arg2 # buf += 64# + + movdqu 16*0(arg2), %xmm9 + movdqu 16*1(arg2), %xmm12 + pshufb %xmm11, %xmm9 + pshufb %xmm11, %xmm12 + movdqa %xmm0, %xmm8 + movdqa %xmm1, %xmm13 + pclmulqdq $0x0 , %xmm10, %xmm0 + pclmulqdq $0x11, %xmm10, %xmm8 + pclmulqdq $0x0 , %xmm10, %xmm1 + pclmulqdq $0x11, %xmm10, %xmm13 + pxor %xmm9 , %xmm0 + xorps %xmm8 , %xmm0 + pxor %xmm12, %xmm1 + xorps %xmm13, %xmm1 + + movdqu 16*2(arg2), %xmm9 + movdqu 16*3(arg2), %xmm12 + pshufb %xmm11, %xmm9 + pshufb %xmm11, %xmm12 + movdqa %xmm2, %xmm8 + movdqa %xmm3, %xmm13 + pclmulqdq $0x0, %xmm10, %xmm2 + pclmulqdq $0x11, %xmm10, %xmm8 + pclmulqdq $0x0, %xmm10, %xmm3 + pclmulqdq $0x11, %xmm10, %xmm13 + pxor %xmm9 , %xmm2 + xorps %xmm8 , %xmm2 + pxor %xmm12, %xmm3 + xorps %xmm13, %xmm3 + + movdqu 16*4(arg2), %xmm9 + movdqu 16*5(arg2), %xmm12 + pshufb %xmm11, %xmm9 + pshufb %xmm11, %xmm12 + movdqa %xmm4, %xmm8 + movdqa %xmm5, %xmm13 + pclmulqdq $0x0, %xmm10, %xmm4 + pclmulqdq $0x11, %xmm10, %xmm8 + pclmulqdq $0x0, %xmm10, %xmm5 + pclmulqdq $0x11, %xmm10, %xmm13 + pxor %xmm9 , %xmm4 + xorps %xmm8 , %xmm4 + pxor %xmm12, %xmm5 + xorps %xmm13, %xmm5 + + movdqu 16*6(arg2), %xmm9 + movdqu 16*7(arg2), %xmm12 + pshufb %xmm11, %xmm9 + pshufb %xmm11, %xmm12 + movdqa %xmm6 , %xmm8 + movdqa %xmm7 , %xmm13 + pclmulqdq $0x0 , %xmm10, %xmm6 + pclmulqdq $0x11, %xmm10, %xmm8 + pclmulqdq $0x0 , %xmm10, %xmm7 + pclmulqdq $0x11, %xmm10, %xmm13 + pxor %xmm9 , %xmm6 + xorps %xmm8 , %xmm6 + pxor %xmm12, %xmm7 + xorps %xmm13, %xmm7 + + sub $128, arg3 + + # check if there is another 64B in the buffer to be able to fold + jge _fold_64_B_loop + ################################################################## + + + add $128, arg2 + # at this point, the buffer pointer is pointing at the last y Bytes + # of the buffer the 64B of folded data is in 4 of the xmm + # registers: xmm0, xmm1, xmm2, xmm3 + + + # fold the 8 xmm registers to 1 xmm register with different constants + + movdqa rk9(%rip), %xmm10 + movdqa %xmm0, %xmm8 + pclmulqdq $0x11, %xmm10, %xmm0 + pclmulqdq $0x0 , %xmm10, %xmm8 + pxor %xmm8, %xmm7 + xorps %xmm0, %xmm7 + + movdqa rk11(%rip), %xmm10 + movdqa %xmm1, %xmm8 + pclmulqdq $0x11, %xmm10, %xmm1 + pclmulqdq $0x0 , %xmm10, %xmm8 + pxor %xmm8, %xmm7 + xorps %xmm1, %xmm7 + + movdqa rk13(%rip), %xmm10 + movdqa %xmm2, %xmm8 + pclmulqdq $0x11, %xmm10, %xmm2 + pclmulqdq $0x0 , %xmm10, %xmm8 + pxor %xmm8, %xmm7 + pxor %xmm2, %xmm7 + + movdqa rk15(%rip), %xmm10 + movdqa %xmm3, %xmm8 + pclmulqdq $0x11, %xmm10, %xmm3 + pclmulqdq $0x0 , %xmm10, %xmm8 + pxor %xmm8, %xmm7 + xorps %xmm3, %xmm7 + + movdqa rk17(%rip), %xmm10 + movdqa %xmm4, %xmm8 + pclmulqdq $0x11, %xmm10, %xmm4 + pclmulqdq $0x0 , %xmm10, %xmm8 + pxor %xmm8, %xmm7 + pxor %xmm4, %xmm7 + + movdqa rk19(%rip), %xmm10 + movdqa %xmm5, %xmm8 + pclmulqdq $0x11, %xmm10, %xmm5 + pclmulqdq $0x0 , %xmm10, %xmm8 + pxor %xmm8, %xmm7 + xorps %xmm5, %xmm7 + + movdqa rk1(%rip), %xmm10 #xmm10 has rk1 and rk2 + #imm value of pclmulqdq instruction + #will determine which constant to use + movdqa %xmm6, %xmm8 + pclmulqdq $0x11, %xmm10, %xmm6 + pclmulqdq $0x0 , %xmm10, %xmm8 + pxor %xmm8, %xmm7 + pxor %xmm6, %xmm7 + + + # instead of 64, we add 48 to the loop counter to save 1 instruction + # from the loop instead of a cmp instruction, we use the negative + # flag with the jl instruction + add $128-16, arg3 + jl _final_reduction_for_128 + + # now we have 16+y bytes left to reduce. 16 Bytes is in register xmm7 + # and the rest is in memory. We can fold 16 bytes at a time if y>=16 + # continue folding 16B at a time + +_16B_reduction_loop: + movdqa %xmm7, %xmm8 + pclmulqdq $0x11, %xmm10, %xmm7 + pclmulqdq $0x0 , %xmm10, %xmm8 + pxor %xmm8, %xmm7 + movdqu (arg2), %xmm0 + pshufb %xmm11, %xmm0 + pxor %xmm0 , %xmm7 + add $16, arg2 + sub $16, arg3 + # instead of a cmp instruction, we utilize the flags with the + # jge instruction equivalent of: cmp arg3, 16-16 + # check if there is any more 16B in the buffer to be able to fold + jge _16B_reduction_loop + + #now we have 16+z bytes left to reduce, where 0<= z < 16. + #first, we reduce the data in the xmm7 register + + +_final_reduction_for_128: + # check if any more data to fold. If not, compute the CRC of + # the final 128 bits + add $16, arg3 + je _128_done + + # here we are getting data that is less than 16 bytes. + # since we know that there was data before the pointer, we can + # offset the input pointer before the actual point, to receive + # exactly 16 bytes. after that the registers need to be adjusted. +_get_last_two_xmms: + movdqa %xmm7, %xmm2 + + movdqu -16(arg2, arg3), %xmm1 + pshufb %xmm11, %xmm1 + + # get rid of the extra data that was loaded before + # load the shift constant + lea pshufb_shf_table+16(%rip), %rax + sub arg3, %rax + movdqu (%rax), %xmm0 + + # shift xmm2 to the left by arg3 bytes + pshufb %xmm0, %xmm2 + + # shift xmm7 to the right by 16-arg3 bytes + pxor mask1(%rip), %xmm0 + pshufb %xmm0, %xmm7 + pblendvb %xmm2, %xmm1 #xmm0 is implicit + + # fold 16 Bytes + movdqa %xmm1, %xmm2 + movdqa %xmm7, %xmm8 + pclmulqdq $0x11, %xmm10, %xmm7 + pclmulqdq $0x0 , %xmm10, %xmm8 + pxor %xmm8, %xmm7 + pxor %xmm2, %xmm7 + +_128_done: + # compute crc of a 128-bit value + movdqa rk5(%rip), %xmm10 # rk5 and rk6 in xmm10 + movdqa %xmm7, %xmm0 + + #64b fold + pclmulqdq $0x1, %xmm10, %xmm7 + pslldq $8 , %xmm0 + pxor %xmm0, %xmm7 + + #32b fold + movdqa %xmm7, %xmm0 + + pand mask2(%rip), %xmm0 + + psrldq $12, %xmm7 + pclmulqdq $0x10, %xmm10, %xmm7 + pxor %xmm0, %xmm7 + + #barrett reduction +_barrett: + movdqa rk7(%rip), %xmm10 # rk7 and rk8 in xmm10 + movdqa %xmm7, %xmm0 + pclmulqdq $0x01, %xmm10, %xmm7 + pslldq $4, %xmm7 + pclmulqdq $0x11, %xmm10, %xmm7 + + pslldq $4, %xmm7 + pxor %xmm0, %xmm7 + pextrd $1, %xmm7, %eax + +_cleanup: + # scale the result back to 16 bits + shr $16, %eax + mov %rcx, %rsp + ret + +######################################################################## + +.align 16 +_less_than_128: + + # check if there is enough buffer to be able to fold 16B at a time + cmp $32, arg3 + jl _less_than_32 + movdqa SHUF_MASK(%rip), %xmm11 + + # now if there is, load the constants + movdqa rk1(%rip), %xmm10 # rk1 and rk2 in xmm10 + + movd arg1_low32, %xmm0 # get the initial crc value + pslldq $12, %xmm0 # align it to its correct place + movdqu (arg2), %xmm7 # load the plaintext + pshufb %xmm11, %xmm7 # byte-reflect the plaintext + pxor %xmm0, %xmm7 + + + # update the buffer pointer + add $16, arg2 + + # update the counter. subtract 32 instead of 16 to save one + # instruction from the loop + sub $32, arg3 + + jmp _16B_reduction_loop + + +.align 16 +_less_than_32: + # mov initial crc to the return value. this is necessary for + # zero-length buffers. + mov arg1_low32, %eax + test arg3, arg3 + je _cleanup + + movdqa SHUF_MASK(%rip), %xmm11 + + movd arg1_low32, %xmm0 # get the initial crc value + pslldq $12, %xmm0 # align it to its correct place + + cmp $16, arg3 + je _exact_16_left + jl _less_than_16_left + + movdqu (arg2), %xmm7 # load the plaintext + pshufb %xmm11, %xmm7 # byte-reflect the plaintext + pxor %xmm0 , %xmm7 # xor the initial crc value + add $16, arg2 + sub $16, arg3 + movdqa rk1(%rip), %xmm10 # rk1 and rk2 in xmm10 + jmp _get_last_two_xmms + + +.align 16 +_less_than_16_left: + # use stack space to load data less than 16 bytes, zero-out + # the 16B in memory first. + + pxor %xmm1, %xmm1 + mov %rsp, %r11 + movdqa %xmm1, (%r11) + + cmp $4, arg3 + jl _only_less_than_4 + + # backup the counter value + mov arg3, %r9 + cmp $8, arg3 + jl _less_than_8_left + + # load 8 Bytes + mov (arg2), %rax + mov %rax, (%r11) + add $8, %r11 + sub $8, arg3 + add $8, arg2 +_less_than_8_left: + + cmp $4, arg3 + jl _less_than_4_left + + # load 4 Bytes + mov (arg2), %eax + mov %eax, (%r11) + add $4, %r11 + sub $4, arg3 + add $4, arg2 +_less_than_4_left: + + cmp $2, arg3 + jl _less_than_2_left + + # load 2 Bytes + mov (arg2), %ax + mov %ax, (%r11) + add $2, %r11 + sub $2, arg3 + add $2, arg2 +_less_than_2_left: + cmp $1, arg3 + jl _zero_left + + # load 1 Byte + mov (arg2), %al + mov %al, (%r11) +_zero_left: + movdqa (%rsp), %xmm7 + pshufb %xmm11, %xmm7 + pxor %xmm0 , %xmm7 # xor the initial crc value + + # shl r9, 4 + lea pshufb_shf_table+16(%rip), %rax + sub %r9, %rax + movdqu (%rax), %xmm0 + pxor mask1(%rip), %xmm0 + + pshufb %xmm0, %xmm7 + jmp _128_done + +.align 16 +_exact_16_left: + movdqu (arg2), %xmm7 + pshufb %xmm11, %xmm7 + pxor %xmm0 , %xmm7 # xor the initial crc value + + jmp _128_done + +_only_less_than_4: + cmp $3, arg3 + jl _only_less_than_3 + + # load 3 Bytes + mov (arg2), %al + mov %al, (%r11) + + mov 1(arg2), %al + mov %al, 1(%r11) + + mov 2(arg2), %al + mov %al, 2(%r11) + + movdqa (%rsp), %xmm7 + pshufb %xmm11, %xmm7 + pxor %xmm0 , %xmm7 # xor the initial crc value + + psrldq $5, %xmm7 + + jmp _barrett +_only_less_than_3: + cmp $2, arg3 + jl _only_less_than_2 + + # load 2 Bytes + mov (arg2), %al + mov %al, (%r11) + + mov 1(arg2), %al + mov %al, 1(%r11) + + movdqa (%rsp), %xmm7 + pshufb %xmm11, %xmm7 + pxor %xmm0 , %xmm7 # xor the initial crc value + + psrldq $6, %xmm7 + + jmp _barrett +_only_less_than_2: + + # load 1 Byte + mov (arg2), %al + mov %al, (%r11) + + movdqa (%rsp), %xmm7 + pshufb %xmm11, %xmm7 + pxor %xmm0 , %xmm7 # xor the initial crc value + + psrldq $7, %xmm7 + + jmp _barrett + +ENDPROC(crc_t10dif_pcl) + +.data + +# precomputed constants +# these constants are precomputed from the poly: +# 0x8bb70000 (0x8bb7 scaled to 32 bits) +.align 16 +# Q = 0x18BB70000 +# rk1 = 2^(32*3) mod Q << 32 +# rk2 = 2^(32*5) mod Q << 32 +# rk3 = 2^(32*15) mod Q << 32 +# rk4 = 2^(32*17) mod Q << 32 +# rk5 = 2^(32*3) mod Q << 32 +# rk6 = 2^(32*2) mod Q << 32 +# rk7 = floor(2^64/Q) +# rk8 = Q +rk1: +.quad 0x2d56000000000000 +rk2: +.quad 0x06df000000000000 +rk3: +.quad 0x9d9d000000000000 +rk4: +.quad 0x7cf5000000000000 +rk5: +.quad 0x2d56000000000000 +rk6: +.quad 0x1368000000000000 +rk7: +.quad 0x00000001f65a57f8 +rk8: +.quad 0x000000018bb70000 + +rk9: +.quad 0xceae000000000000 +rk10: +.quad 0xbfd6000000000000 +rk11: +.quad 0x1e16000000000000 +rk12: +.quad 0x713c000000000000 +rk13: +.quad 0xf7f9000000000000 +rk14: +.quad 0x80a6000000000000 +rk15: +.quad 0x044c000000000000 +rk16: +.quad 0xe658000000000000 +rk17: +.quad 0xad18000000000000 +rk18: +.quad 0xa497000000000000 +rk19: +.quad 0x6ee3000000000000 +rk20: +.quad 0xe7b5000000000000 + + + +mask1: +.octa 0x80808080808080808080808080808080 +mask2: +.octa 0x00000000FFFFFFFFFFFFFFFFFFFFFFFF + +SHUF_MASK: +.octa 0x000102030405060708090A0B0C0D0E0F + +pshufb_shf_table: +# use these values for shift constants for the pshufb instruction +# different alignments result in values as shown: +# DDQ 0x008f8e8d8c8b8a898887868584838281 # shl 15 (16-1) / shr1 +# DDQ 0x01008f8e8d8c8b8a8988878685848382 # shl 14 (16-3) / shr2 +# DDQ 0x0201008f8e8d8c8b8a89888786858483 # shl 13 (16-4) / shr3 +# DDQ 0x030201008f8e8d8c8b8a898887868584 # shl 12 (16-4) / shr4 +# DDQ 0x04030201008f8e8d8c8b8a8988878685 # shl 11 (16-5) / shr5 +# DDQ 0x0504030201008f8e8d8c8b8a89888786 # shl 10 (16-6) / shr6 +# DDQ 0x060504030201008f8e8d8c8b8a898887 # shl 9 (16-7) / shr7 +# DDQ 0x07060504030201008f8e8d8c8b8a8988 # shl 8 (16-8) / shr8 +# DDQ 0x0807060504030201008f8e8d8c8b8a89 # shl 7 (16-9) / shr9 +# DDQ 0x090807060504030201008f8e8d8c8b8a # shl 6 (16-10) / shr10 +# DDQ 0x0a090807060504030201008f8e8d8c8b # shl 5 (16-11) / shr11 +# DDQ 0x0b0a090807060504030201008f8e8d8c # shl 4 (16-12) / shr12 +# DDQ 0x0c0b0a090807060504030201008f8e8d # shl 3 (16-13) / shr13 +# DDQ 0x0d0c0b0a090807060504030201008f8e # shl 2 (16-14) / shr14 +# DDQ 0x0e0d0c0b0a090807060504030201008f # shl 1 (16-15) / shr15 +.octa 0x8f8e8d8c8b8a89888786858483828100 +.octa 0x000e0d0c0b0a09080706050403020100 -- cgit v1.2.3 From aa672da1b05680f7f087873ae2d02f4a9bf4a829 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 17 May 2013 14:34:48 +0300 Subject: HID: sort IDs for D-WAV eGalax multitouch devices Just sort the list by IDs. There is no functional change. Signed-off-by: Andy Shevchenko Reviewed-By: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-ids.h | 8 ++++---- drivers/hid/hid-multitouch.c | 32 ++++++++++++++++---------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 0b4598ac43d1..5b7e148ca927 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -271,20 +271,20 @@ #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7207 0x7207 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_720C 0x720c #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7224 0x7224 +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7224 0x7224 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_722A 0x722A #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_725E 0x725e #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7262 0x7262 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_726B 0x726b -#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72AA 0x72aa #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72A1 0x72a1 +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72AA 0x72aa +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72C4 0x72c4 +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72D0 0x72d0 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72FA 0x72fa #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7302 0x7302 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7349 0x7349 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_73F7 0x73f7 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001 0xa001 -#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7224 0x7224 -#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72D0 0x72d0 -#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72C4 0x72c4 #define USB_VENDOR_ID_ELECOM 0x056e #define USB_DEVICE_ID_ELECOM_BM084 0x0061 diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 1bea65791e45..590aa379609c 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -1122,33 +1122,42 @@ static const struct hid_device_id mt_devices[] = { USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480E) }, /* eGalax devices (capacitive) */ - { .driver_data = MT_CLS_EGALAX, - MT_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_720C) }, { .driver_data = MT_CLS_EGALAX_SERIAL, MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7207) }, - { .driver_data = MT_CLS_EGALAX_SERIAL, + { .driver_data = MT_CLS_EGALAX, MT_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_725E) }, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_720C) }, { .driver_data = MT_CLS_EGALAX_SERIAL, MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7224) }, + { .driver_data = MT_CLS_EGALAX, + HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7224) }, { .driver_data = MT_CLS_EGALAX_SERIAL, MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_722A) }, - { .driver_data = MT_CLS_EGALAX, + { .driver_data = MT_CLS_EGALAX_SERIAL, MT_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_726B) }, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_725E) }, { .driver_data = MT_CLS_EGALAX_SERIAL, MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7262) }, + { .driver_data = MT_CLS_EGALAX, + MT_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_726B) }, { .driver_data = MT_CLS_EGALAX, MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72A1) }, { .driver_data = MT_CLS_EGALAX_SERIAL, MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72AA) }, + { .driver_data = MT_CLS_EGALAX, + HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72C4) }, + { .driver_data = MT_CLS_EGALAX, + HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72D0) }, { .driver_data = MT_CLS_EGALAX, MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72FA) }, @@ -1164,15 +1173,6 @@ static const struct hid_device_id mt_devices[] = { { .driver_data = MT_CLS_EGALAX_SERIAL, MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001) }, - { .driver_data = MT_CLS_EGALAX, - HID_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7224) }, - { .driver_data = MT_CLS_EGALAX, - HID_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72D0) }, - { .driver_data = MT_CLS_EGALAX, - HID_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72C4) }, /* Elo TouchSystems IntelliTouch Plus panel */ { .driver_data = MT_CLS_DUAL_CONTACT_ID, -- cgit v1.2.3 From adb91aef9f9965341a9252bd610b5d828d5c016f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 17 May 2013 14:34:49 +0300 Subject: HID: remove duplicate ID for D-WAV eGalax 0x7224 This patch also removes the duplicate entry in the hid-multitouch.c as suggested by Benjamin Tissoires. Signed-off-by: Andy Shevchenko Signed-off-by: Jiri Kosina --- drivers/hid/hid-ids.h | 1 - drivers/hid/hid-multitouch.c | 3 --- 2 files changed, 4 deletions(-) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 5b7e148ca927..8add4ae0c4ec 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -271,7 +271,6 @@ #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7207 0x7207 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_720C 0x720c #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7224 0x7224 -#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7224 0x7224 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_722A 0x722A #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_725E 0x725e #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7262 0x7262 diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 590aa379609c..d99b9593b65d 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -1131,9 +1131,6 @@ static const struct hid_device_id mt_devices[] = { { .driver_data = MT_CLS_EGALAX_SERIAL, MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7224) }, - { .driver_data = MT_CLS_EGALAX, - HID_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7224) }, { .driver_data = MT_CLS_EGALAX_SERIAL, MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_722A) }, -- cgit v1.2.3 From d781009ca6bb5b9711c74700242855e0a70ee7a3 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 25 Mar 2013 00:11:27 +0000 Subject: mfd: Add device tree bindings for Arizona class devices Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/mfd/arizona.txt | 62 ++++++++++++++++++++ drivers/mfd/arizona-core.c | 69 +++++++++++++++++++++++ drivers/mfd/arizona-i2c.c | 10 +++- drivers/mfd/arizona-spi.c | 10 +++- drivers/mfd/arizona.h | 12 ++++ 5 files changed, 159 insertions(+), 4 deletions(-) create mode 100644 Documentation/devicetree/bindings/mfd/arizona.txt diff --git a/Documentation/devicetree/bindings/mfd/arizona.txt b/Documentation/devicetree/bindings/mfd/arizona.txt new file mode 100644 index 000000000000..0e295c9d8937 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/arizona.txt @@ -0,0 +1,62 @@ +Wolfson Arizona class audio SoCs + +These devices are audio SoCs with extensive digital capabilites and a range +of analogue I/O. + +Required properties: + + - compatible : one of the following chip-specific strings: + "wlf,wm5102" + "wlf,wm5110" + - reg : I2C slave address when connected using I2C, chip select number when + using SPI. + + - interrupts : The interrupt line the /IRQ signal for the device is + connected to. + - interrupt-controller : Arizona class devices contain interrupt controllers + and may provide interrupt services to other devices. + - interrupt-parent : The parent interrupt controller. + - #interrupt-cells: the number of cells to describe an IRQ, this should be 2. + The first cell is the IRQ number. + The second cell is the flags, encoded as the trigger masks from + Documentation/devicetree/bindings/interrupts.txt + + - gpio-controller : Indicates this device is a GPIO controller. + - #gpio-cells : Must be 2. The first cell is the pin number and the + second cell is used to specify optional parameters (currently unused). + + - AVDD1-supply, DBVDD1-supply, DBVDD2-supply, DBVDD3-supply, CPVDD-supply, + SPKVDDL-supply, SPKVDDR-supply : power supplies for the device, as covered + in Documentation/devicetree/bindings/regulator/regulator.txt + +Optional properties: + + - wlf,reset : GPIO specifier for the GPIO controlling /RESET + - wlf,ldoena : GPIO specifier for the GPIO controlling LDOENA + + - wlf,gpio-defaults : A list of GPIO configuration register values. If + absent, no configuration of these registers is performed. If any + entry has a value that is out of range for a 16 bit register then + the chip default will be used. If present exactly five values must + be specified. + +Example: + +codec: wm5102@1a { + compatible = "wlf,wm5102"; + reg = <0x1a>; + interrupts = <347>; + #interrupt-cells = <2>; + interrupt-parent = <&gic>; + + gpio-controller; + #gpio-cells = <2>; + + wlf,gpio-defaults = < + 0x00000000, /* AIF1TXLRCLK */ + 0xffffffff, + 0xffffffff, + 0xffffffff, + 0xffffffff, + >; +}; diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 549db0ad7257..d8d30c0a488d 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -16,6 +16,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -462,6 +465,70 @@ const struct dev_pm_ops arizona_pm_ops = { }; EXPORT_SYMBOL_GPL(arizona_pm_ops); +#ifdef CONFIG_OF +int arizona_of_get_type(struct device *dev) +{ + const struct of_device_id *id = of_match_device(arizona_of_match, dev); + + if (id) + return (int)id->data; + else + return 0; +} +EXPORT_SYMBOL_GPL(arizona_of_get_type); + +static int arizona_of_get_core_pdata(struct arizona *arizona) +{ + int ret, i; + + arizona->pdata.reset = of_get_named_gpio(arizona->dev->of_node, + "wlf,reset", 0); + if (arizona->pdata.reset < 0) + arizona->pdata.reset = 0; + + arizona->pdata.ldoena = of_get_named_gpio(arizona->dev->of_node, + "wlf,ldoena", 0); + if (arizona->pdata.ldoena < 0) + arizona->pdata.ldoena = 0; + + ret = of_property_read_u32_array(arizona->dev->of_node, + "wlf,gpio-defaults", + arizona->pdata.gpio_defaults, + ARRAY_SIZE(arizona->pdata.gpio_defaults)); + if (ret >= 0) { + /* + * All values are literal except out of range values + * which are chip default, translate into platform + * data which uses 0 as chip default and out of range + * as zero. + */ + for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) { + if (arizona->pdata.gpio_defaults[i] > 0xffff) + arizona->pdata.gpio_defaults[i] = 0; + if (arizona->pdata.gpio_defaults[i] == 0) + arizona->pdata.gpio_defaults[i] = 0x10000; + } + } else { + dev_err(arizona->dev, "Failed to parse GPIO defaults: %d\n", + ret); + } + + return 0; +} + +const struct of_device_id arizona_of_match[] = { + { .compatible = "wlf,wm5102", .data = (void *)WM5102 }, + { .compatible = "wlf,wm5110", .data = (void *)WM5110 }, + {}, +}; +EXPORT_SYMBOL_GPL(arizona_of_match); +#else +static inline int arizona_of_get_core_pdata(struct arizona *arizona) +{ + return 0; +} +#endif + static struct mfd_cell early_devs[] = { { .name = "arizona-ldo1" }, }; @@ -495,6 +562,8 @@ int arizona_dev_init(struct arizona *arizona) dev_set_drvdata(arizona->dev, arizona); mutex_init(&arizona->clk_lock); + arizona_of_get_core_pdata(arizona); + if (dev_get_platdata(arizona->dev)) memcpy(&arizona->pdata, dev_get_platdata(arizona->dev), sizeof(arizona->pdata)); diff --git a/drivers/mfd/arizona-i2c.c b/drivers/mfd/arizona-i2c.c index 44a1bb969841..deb267ebf84e 100644 --- a/drivers/mfd/arizona-i2c.c +++ b/drivers/mfd/arizona-i2c.c @@ -27,9 +27,14 @@ static int arizona_i2c_probe(struct i2c_client *i2c, { struct arizona *arizona; const struct regmap_config *regmap_config; - int ret; + int ret, type; - switch (id->driver_data) { + if (i2c->dev.of_node) + type = arizona_of_get_type(&i2c->dev); + else + type = id->driver_data; + + switch (type) { #ifdef CONFIG_MFD_WM5102 case WM5102: regmap_config = &wm5102_i2c_regmap; @@ -84,6 +89,7 @@ static struct i2c_driver arizona_i2c_driver = { .name = "arizona", .owner = THIS_MODULE, .pm = &arizona_pm_ops, + .of_match_table = of_match_ptr(arizona_of_match), }, .probe = arizona_i2c_probe, .remove = arizona_i2c_remove, diff --git a/drivers/mfd/arizona-spi.c b/drivers/mfd/arizona-spi.c index b57e642d2b4a..47be7b35b5c5 100644 --- a/drivers/mfd/arizona-spi.c +++ b/drivers/mfd/arizona-spi.c @@ -27,9 +27,14 @@ static int arizona_spi_probe(struct spi_device *spi) const struct spi_device_id *id = spi_get_device_id(spi); struct arizona *arizona; const struct regmap_config *regmap_config; - int ret; + int ret, type; - switch (id->driver_data) { + if (spi->dev.of_node) + type = arizona_of_get_type(&spi->dev); + else + type = id->driver_data; + + switch (type) { #ifdef CONFIG_MFD_WM5102 case WM5102: regmap_config = &wm5102_spi_regmap; @@ -84,6 +89,7 @@ static struct spi_driver arizona_spi_driver = { .name = "arizona", .owner = THIS_MODULE, .pm = &arizona_pm_ops, + .of_match_table = of_match_ptr(arizona_of_match), }, .probe = arizona_spi_probe, .remove = arizona_spi_remove, diff --git a/drivers/mfd/arizona.h b/drivers/mfd/arizona.h index 9798ae5da67b..db55d9854a55 100644 --- a/drivers/mfd/arizona.h +++ b/drivers/mfd/arizona.h @@ -13,6 +13,7 @@ #ifndef _WM5102_H #define _WM5102_H +#include #include #include @@ -26,6 +27,8 @@ extern const struct regmap_config wm5110_spi_regmap; extern const struct dev_pm_ops arizona_pm_ops; +extern const struct of_device_id arizona_of_match[]; + extern const struct regmap_irq_chip wm5102_aod; extern const struct regmap_irq_chip wm5102_irq; @@ -37,4 +40,13 @@ int arizona_dev_exit(struct arizona *arizona); int arizona_irq_init(struct arizona *arizona); int arizona_irq_exit(struct arizona *arizona); +#ifdef CONFIG_OF +int arizona_of_get_type(struct device *dev); +#else +static inline int arizona_of_get_type(struct device *dev) +{ + return 0; +} +#endif + #endif -- cgit v1.2.3 From 5927467d0ca274bc3b8eed9fd5db964bbde56e1c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 23 Apr 2013 19:44:16 +0100 Subject: mfd: arizona: Support use of external DCVDD When the device is used with an external DCVDD supply instead of the internal LDO1 then an extra step is required when suspending and resuming the device. Signed-off-by: Mark Brown --- drivers/mfd/arizona-core.c | 43 ++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/arizona/core.h | 2 ++ 2 files changed, 45 insertions(+) diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index d8d30c0a488d..437f199fe5f2 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -347,6 +348,17 @@ static int arizona_runtime_resume(struct device *dev) switch (arizona->type) { case WM5102: + if (arizona->external_dcvdd) { + ret = regmap_update_bits(arizona->regmap, + ARIZONA_ISOLATION_CONTROL, + ARIZONA_ISOLATE_DCVDD1, 0); + if (ret != 0) { + dev_err(arizona->dev, + "Failed to connect DCVDD: %d\n", ret); + goto err; + } + } + ret = wm5102_patch(arizona); if (ret != 0) { dev_err(arizona->dev, "Failed to apply patch: %d\n", @@ -368,6 +380,16 @@ static int arizona_runtime_resume(struct device *dev) goto err; } + if (arizona->external_dcvdd) { + ret = regmap_update_bits(arizona->regmap, + ARIZONA_ISOLATION_CONTROL, + ARIZONA_ISOLATE_DCVDD1, 0); + if (ret != 0) { + dev_err(arizona->dev, + "Failed to connect DCVDD: %d\n", ret); + goto err; + } + } break; } @@ -400,9 +422,22 @@ err: static int arizona_runtime_suspend(struct device *dev) { struct arizona *arizona = dev_get_drvdata(dev); + int ret; dev_dbg(arizona->dev, "Entering AoD mode\n"); + if (arizona->external_dcvdd) { + ret = regmap_update_bits(arizona->regmap, + ARIZONA_ISOLATION_CONTROL, + ARIZONA_ISOLATE_DCVDD1, + ARIZONA_ISOLATE_DCVDD1); + if (ret != 0) { + dev_err(arizona->dev, "Failed to isolate DCVDD: %d\n", + ret); + return ret; + } + } + regulator_disable(arizona->dcvdd); regcache_cache_only(arizona->regmap, true); regcache_mark_dirty(arizona->regmap); @@ -771,6 +806,14 @@ int arizona_dev_init(struct arizona *arizona) arizona->pdata.gpio_defaults[i]); } + /* + * LDO1 can only be used to supply DCVDD so if it has no + * consumers then DCVDD is supplied externally. + */ + if (arizona->pdata.ldo1 && + arizona->pdata.ldo1->num_consumer_supplies == 0) + arizona->external_dcvdd = true; + pm_runtime_set_autosuspend_delay(arizona->dev, 100); pm_runtime_use_autosuspend(arizona->dev); pm_runtime_enable(arizona->dev); diff --git a/include/linux/mfd/arizona/core.h b/include/linux/mfd/arizona/core.h index cc281368dc55..f797bb9b8b56 100644 --- a/include/linux/mfd/arizona/core.h +++ b/include/linux/mfd/arizona/core.h @@ -95,6 +95,8 @@ struct arizona { struct arizona_pdata pdata; + unsigned int external_dcvdd:1; + int irq; struct irq_domain *virq; struct regmap_irq_chip_data *aod_irq_chip; -- cgit v1.2.3 From c28f692c6f5a836dc628fb6990fb4d3d1859f779 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 10 May 2013 08:17:11 +0000 Subject: drivers/thermal: don't check resource with devm_ioremap_resource devm_ioremap_resource does sanity checks on the given resource. No need to duplicate this in the driver. Signed-off-by: Wolfram Sang Acked-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/dove_thermal.c | 11 +---------- drivers/thermal/exynos_thermal.c | 5 ----- drivers/thermal/kirkwood_thermal.c | 7 +------ drivers/thermal/rcar_thermal.c | 6 +----- 4 files changed, 3 insertions(+), 26 deletions(-) diff --git a/drivers/thermal/dove_thermal.c b/drivers/thermal/dove_thermal.c index 4b15a5f270dc..8bf104d8a649 100644 --- a/drivers/thermal/dove_thermal.c +++ b/drivers/thermal/dove_thermal.c @@ -134,25 +134,16 @@ static int dove_thermal_probe(struct platform_device *pdev) struct resource *res; int ret; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "Failed to get platform resource\n"); - return -ENODEV; - } - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); priv->sensor = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(priv->sensor)) return PTR_ERR(priv->sensor); res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!res) { - dev_err(&pdev->dev, "Failed to get platform resource\n"); - return -ENODEV; - } priv->control = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(priv->control)) return PTR_ERR(priv->control); diff --git a/drivers/thermal/exynos_thermal.c b/drivers/thermal/exynos_thermal.c index d20ce9e61403..788b1ddcac6c 100644 --- a/drivers/thermal/exynos_thermal.c +++ b/drivers/thermal/exynos_thermal.c @@ -925,11 +925,6 @@ static int exynos_tmu_probe(struct platform_device *pdev) INIT_WORK(&data->irq_work, exynos_tmu_work); data->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!data->mem) { - dev_err(&pdev->dev, "Failed to get platform resource\n"); - return -ENOENT; - } - data->base = devm_ioremap_resource(&pdev->dev, data->mem); if (IS_ERR(data->base)) return PTR_ERR(data->base); diff --git a/drivers/thermal/kirkwood_thermal.c b/drivers/thermal/kirkwood_thermal.c index dfeceaffbc03..9e4d98eef17a 100644 --- a/drivers/thermal/kirkwood_thermal.c +++ b/drivers/thermal/kirkwood_thermal.c @@ -75,16 +75,11 @@ static int kirkwood_thermal_probe(struct platform_device *pdev) struct kirkwood_thermal_priv *priv; struct resource *res; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "Failed to get platform resource\n"); - return -ENODEV; - } - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); priv->sensor = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(priv->sensor)) return PTR_ERR(priv->sensor); diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 8d7edd4c8228..72f50bc0456c 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -389,11 +389,6 @@ static int rcar_thermal_probe(struct platform_device *pdev) * platform has IRQ support. * Then, drier use common register */ - res = platform_get_resource(pdev, IORESOURCE_MEM, mres++); - if (!res) { - dev_err(dev, "Could not get platform resource\n"); - return -ENODEV; - } ret = devm_request_irq(dev, irq->start, rcar_thermal_irq, 0, dev_name(dev), common); @@ -405,6 +400,7 @@ static int rcar_thermal_probe(struct platform_device *pdev) /* * rcar_has_irq_support() will be enabled */ + res = platform_get_resource(pdev, IORESOURCE_MEM, mres++); common->base = devm_ioremap_resource(dev, res); if (IS_ERR(common->base)) return PTR_ERR(common->base); -- cgit v1.2.3 From 253e3ae170d0852620dd9898807a22401d831dc4 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Thu, 16 May 2013 02:16:20 +0000 Subject: Thermal: spear_thermal: convert to devm_ioremap_resource Use the newly introduced devm_ioremap_resource(). devm_ioremap_resource() provides its own error messages; so all explicit error messages can be removed from the failure code paths. CC: Vincenzo Frascino Signed-off-by: Zhang Rui --- drivers/thermal/spear_thermal.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/thermal/spear_thermal.c b/drivers/thermal/spear_thermal.c index 3c5ee5607977..b9e21611aece 100644 --- a/drivers/thermal/spear_thermal.c +++ b/drivers/thermal/spear_thermal.c @@ -104,7 +104,7 @@ static int spear_thermal_probe(struct platform_device *pdev) struct thermal_zone_device *spear_thermal = NULL; struct spear_thermal_dev *stdev; struct device_node *np = pdev->dev.of_node; - struct resource *stres = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct resource *res; int ret = 0, val; if (!np || !of_property_read_u32(np, "st,thermal-flags", &val)) { @@ -112,23 +112,23 @@ static int spear_thermal_probe(struct platform_device *pdev) return -EINVAL; } - if (!stres) { - dev_err(&pdev->dev, "memory resource missing\n"); - return -ENODEV; - } - stdev = devm_kzalloc(&pdev->dev, sizeof(*stdev), GFP_KERNEL); if (!stdev) { dev_err(&pdev->dev, "kzalloc fail\n"); return -ENOMEM; } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "memory resource missing\n"); + return -ENODEV; + } + /* Enable thermal sensor */ - stdev->thermal_base = devm_ioremap(&pdev->dev, stres->start, - resource_size(stres)); - if (!stdev->thermal_base) { + stdev->thermal_base = devm_ioremap_resource(dev, res); + if (IS_ERR(stdev->thermal_base)) { dev_err(&pdev->dev, "ioremap failed\n"); - return -ENOMEM; + return PTR_ERR(stdev->thermal_base); } stdev->clk = devm_clk_get(&pdev->dev, NULL); -- cgit v1.2.3 From 237ce4665c12376391ddb148509204652f6ab758 Mon Sep 17 00:00:00 2001 From: Qipan Li Date: Sat, 18 May 2013 19:46:06 +0800 Subject: spi: sirf: fix the issue while transferring more than 256 words currently, spi irq handler only does rx processing and fetching data from rx fifo when "FRM_END" irq happens. FRM_END indicates one transfer completes. if rx size is less than 256, it works well. but the problem is that spi rx fifo size is only 256 bytes, then if data size of one frame is more than 256, before FRM_END comes, rx fifo will be filled with RXFIFO_OFLOW overflow interrupt, it will make us lose some data due to fifo overflow. Explicitly we need do fetch work from device rx fifo in irq handler not only in "FRM_END" irq but also in "THD_REACH" irq. THD_REACH means rx fifo has come to its threshold and will come to overflow if we don't take data from it in time. In this patch, we fix this issue. we take data from rx fifo when either FRM_END or RX_THD_REACH irq comes, we put data into tx fifo when either TX_FIFO_EMPTY or TX_THD_REACH irq comes. Signed-off-by: Qipan Li Signed-off-by: Zhiwu Song Signed-off-by: Barry Song Signed-off-by: Mark Brown --- drivers/spi/spi-sirf.c | 43 +++++++++++++------------------------------ 1 file changed, 13 insertions(+), 30 deletions(-) diff --git a/drivers/spi/spi-sirf.c b/drivers/spi/spi-sirf.c index ac2ea8a1bd65..e00b437fa2a2 100644 --- a/drivers/spi/spi-sirf.c +++ b/drivers/spi/spi-sirf.c @@ -140,9 +140,6 @@ struct sirfsoc_spi { unsigned int left_tx_cnt; unsigned int left_rx_cnt; - /* tasklet to push tx msg into FIFO */ - struct tasklet_struct tasklet_tx; - int chipselect[0]; }; @@ -234,17 +231,6 @@ static void spi_sirfsoc_tx_word_u32(struct sirfsoc_spi *sspi) sspi->left_tx_cnt--; } -static void spi_sirfsoc_tasklet_tx(unsigned long arg) -{ - struct sirfsoc_spi *sspi = (struct sirfsoc_spi *)arg; - - /* Fill Tx FIFO while there are left words to be transmitted */ - while (!((readl(sspi->base + SIRFSOC_SPI_TXFIFO_STATUS) & - SIRFSOC_SPI_FIFO_FULL)) && - sspi->left_tx_cnt) - sspi->tx_word(sspi); -} - static irqreturn_t spi_sirfsoc_irq(int irq, void *dev_id) { struct sirfsoc_spi *sspi = dev_id; @@ -259,25 +245,25 @@ static irqreturn_t spi_sirfsoc_irq(int irq, void *dev_id) writel(0x0, sspi->base + SIRFSOC_SPI_INT_EN); } - if (spi_stat & SIRFSOC_SPI_FRM_END) { + if (spi_stat & (SIRFSOC_SPI_FRM_END + | SIRFSOC_SPI_RXFIFO_THD_REACH)) while (!((readl(sspi->base + SIRFSOC_SPI_RXFIFO_STATUS) & SIRFSOC_SPI_FIFO_EMPTY)) && sspi->left_rx_cnt) sspi->rx_word(sspi); - /* Received all words */ - if ((sspi->left_rx_cnt == 0) && (sspi->left_tx_cnt == 0)) { - complete(&sspi->done); - writel(0x0, sspi->base + SIRFSOC_SPI_INT_EN); - } - } - - if (spi_stat & SIRFSOC_SPI_RXFIFO_THD_REACH || - spi_stat & SIRFSOC_SPI_TXFIFO_THD_REACH || - spi_stat & SIRFSOC_SPI_RX_FIFO_FULL || - spi_stat & SIRFSOC_SPI_TXFIFO_EMPTY) - tasklet_schedule(&sspi->tasklet_tx); + if (spi_stat & (SIRFSOC_SPI_FIFO_EMPTY + | SIRFSOC_SPI_TXFIFO_THD_REACH)) + while (!((readl(sspi->base + SIRFSOC_SPI_TXFIFO_STATUS) + & SIRFSOC_SPI_FIFO_FULL)) && + sspi->left_tx_cnt) + sspi->tx_word(sspi); + /* Received all words */ + if ((sspi->left_rx_cnt == 0) && (sspi->left_tx_cnt == 0)) { + complete(&sspi->done); + writel(0x0, sspi->base + SIRFSOC_SPI_INT_EN); + } return IRQ_HANDLED; } @@ -566,9 +552,6 @@ static int spi_sirfsoc_probe(struct platform_device *pdev) init_completion(&sspi->done); - tasklet_init(&sspi->tasklet_tx, spi_sirfsoc_tasklet_tx, - (unsigned long)sspi); - writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_RXFIFO_OP); writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_TXFIFO_OP); writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_RXFIFO_OP); -- cgit v1.2.3 From 435705e89265dec3c641fe75deb748f05e232e59 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 20 May 2013 11:16:10 -0500 Subject: ASoC: wm8994: Handle LRCLK inversion for WM8958 and WM1811A On WM8958 and WM1811A separate control of the LRCLK inversion bit is available for the DAC and ADC LRCLKs which for compatibility reasons is done in a new register bit. Since writes to each scheme have no effect on parts using the other just always write to both for simplicity. Signed-off-by: Mark Brown Acked-by: Vinod Koul Tested-by: Samreen Nilofer --- include/linux/mfd/wm8994/registers.h | 8 ++++++++ sound/soc/codecs/wm8994.c | 14 ++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/include/linux/mfd/wm8994/registers.h b/include/linux/mfd/wm8994/registers.h index 053548961c15..db8cef3d5321 100644 --- a/include/linux/mfd/wm8994/registers.h +++ b/include/linux/mfd/wm8994/registers.h @@ -2668,6 +2668,10 @@ /* * R772 (0x304) - AIF1ADC LRCLK */ +#define WM8958_AIF1_LRCLK_INV 0x1000 /* AIF1_LRCLK_INV */ +#define WM8958_AIF1_LRCLK_INV_MASK 0x1000 /* AIF1_LRCLK_INV */ +#define WM8958_AIF1_LRCLK_INV_SHIFT 12 /* AIF1_LRCLK_INV */ +#define WM8958_AIF1_LRCLK_INV_WIDTH 1 /* AIF1_LRCLK_INV */ #define WM8994_AIF1ADC_LRCLK_DIR 0x0800 /* AIF1ADC_LRCLK_DIR */ #define WM8994_AIF1ADC_LRCLK_DIR_MASK 0x0800 /* AIF1ADC_LRCLK_DIR */ #define WM8994_AIF1ADC_LRCLK_DIR_SHIFT 11 /* AIF1ADC_LRCLK_DIR */ @@ -2679,6 +2683,10 @@ /* * R773 (0x305) - AIF1DAC LRCLK */ +#define WM8958_AIF1_LRCLK_INV 0x1000 /* AIF1_LRCLK_INV */ +#define WM8958_AIF1_LRCLK_INV_MASK 0x1000 /* AIF1_LRCLK_INV */ +#define WM8958_AIF1_LRCLK_INV_SHIFT 12 /* AIF1_LRCLK_INV */ +#define WM8958_AIF1_LRCLK_INV_WIDTH 1 /* AIF1_LRCLK_INV */ #define WM8994_AIF1DAC_LRCLK_DIR 0x0800 /* AIF1DAC_LRCLK_DIR */ #define WM8994_AIF1DAC_LRCLK_DIR_MASK 0x0800 /* AIF1DAC_LRCLK_DIR */ #define WM8994_AIF1DAC_LRCLK_DIR_SHIFT 11 /* AIF1DAC_LRCLK_DIR */ diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 303d755b9342..f1c54af45dcf 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -2574,17 +2574,24 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) struct wm8994 *control = wm8994->wm8994; int ms_reg; int aif1_reg; + int dac_reg; + int adc_reg; int ms = 0; int aif1 = 0; + int lrclk = 0; switch (dai->id) { case 1: ms_reg = WM8994_AIF1_MASTER_SLAVE; aif1_reg = WM8994_AIF1_CONTROL_1; + dac_reg = WM8994_AIF1DAC_LRCLK; + adc_reg = WM8994_AIF1ADC_LRCLK; break; case 2: ms_reg = WM8994_AIF2_MASTER_SLAVE; aif1_reg = WM8994_AIF2_CONTROL_1; + dac_reg = WM8994_AIF1DAC_LRCLK; + adc_reg = WM8994_AIF1ADC_LRCLK; break; default: return -EINVAL; @@ -2603,6 +2610,7 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_DSP_B: aif1 |= WM8994_AIF1_LRCLK_INV; + lrclk |= WM8958_AIF1_LRCLK_INV; case SND_SOC_DAIFMT_DSP_A: aif1 |= 0x18; break; @@ -2641,12 +2649,14 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) break; case SND_SOC_DAIFMT_IB_IF: aif1 |= WM8994_AIF1_BCLK_INV | WM8994_AIF1_LRCLK_INV; + lrclk |= WM8958_AIF1_LRCLK_INV; break; case SND_SOC_DAIFMT_IB_NF: aif1 |= WM8994_AIF1_BCLK_INV; break; case SND_SOC_DAIFMT_NB_IF: aif1 |= WM8994_AIF1_LRCLK_INV; + lrclk |= WM8958_AIF1_LRCLK_INV; break; default: return -EINVAL; @@ -2677,6 +2687,10 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) aif1); snd_soc_update_bits(codec, ms_reg, WM8994_AIF1_MSTR, ms); + snd_soc_update_bits(codec, dac_reg, + WM8958_AIF1_LRCLK_INV, lrclk); + snd_soc_update_bits(codec, adc_reg, + WM8958_AIF1_LRCLK_INV, lrclk); return 0; } -- cgit v1.2.3 From bd1dd8856998408dd72768930958ea2dc84296a9 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 17 May 2013 13:29:03 +0100 Subject: ASoC: arizona: Provide simple DAI ops for autoconfiguring interfaces Signed-off-by: Mark Brown --- sound/soc/codecs/arizona.c | 7 +++++++ sound/soc/codecs/arizona.h | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 389f23253831..de625813c0e6 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -1198,6 +1198,13 @@ const struct snd_soc_dai_ops arizona_dai_ops = { }; EXPORT_SYMBOL_GPL(arizona_dai_ops); +const struct snd_soc_dai_ops arizona_simple_dai_ops = { + .startup = arizona_startup, + .hw_params = arizona_hw_params_rate, + .set_sysclk = arizona_dai_set_sysclk, +}; +EXPORT_SYMBOL_GPL(arizona_simple_dai_ops); + int arizona_init_dai(struct arizona_priv *priv, int id) { struct arizona_dai_priv *dai_priv = &priv->dai[id]; diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index af39f1006427..b60b08ccc1d0 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -57,7 +57,7 @@ #define ARIZONA_CLK_98MHZ 5 #define ARIZONA_CLK_147MHZ 6 -#define ARIZONA_MAX_DAI 4 +#define ARIZONA_MAX_DAI 6 #define ARIZONA_MAX_ADSP 4 struct arizona; @@ -213,6 +213,7 @@ extern int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id, int source, unsigned int freq, int dir); extern const struct snd_soc_dai_ops arizona_dai_ops; +extern const struct snd_soc_dai_ops arizona_simple_dai_ops; #define ARIZONA_FLL_NAME_LEN 20 -- cgit v1.2.3 From 1804aff60d3bfe34223744ec8c301699bc0b0407 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 17 May 2013 13:29:04 +0100 Subject: ASoC: wm5102: Stub hookup for Slimbus interface Signed-off-by: Mark Brown --- sound/soc/codecs/wm5102.c | 179 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index 6e1ee130705a..9a186ff22b55 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -852,6 +852,15 @@ ARIZONA_MIXER_CONTROLS("AIF2TX2", ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("AIF3TX1", ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("AIF3TX2", ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE), + +ARIZONA_MIXER_CONTROLS("SLIMTX1", ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX2", ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX3", ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX4", ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX5", ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX6", ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX7", ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX8", ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE), }; ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE); @@ -898,6 +907,15 @@ ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE); ARIZONA_MIXER_ENUMS(AIF3TX1, ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE); ARIZONA_MIXER_ENUMS(AIF3TX2, ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX1, ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX2, ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX3, ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX4, ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX5, ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX6, ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX7, ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX8, ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE); + ARIZONA_MUX_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE); ARIZONA_MUX_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE); ARIZONA_MUX_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE); @@ -1117,6 +1135,56 @@ SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0, SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 0, ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX7", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX8_ENA_SHIFT, 0), + ARIZONA_DSP_WIDGETS(DSP1, "DSP1"), SND_SOC_DAPM_VALUE_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1, @@ -1188,6 +1256,15 @@ ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"), ARIZONA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"), ARIZONA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"), +ARIZONA_MIXER_WIDGETS(SLIMTX1, "SLIMTX1"), +ARIZONA_MIXER_WIDGETS(SLIMTX2, "SLIMTX2"), +ARIZONA_MIXER_WIDGETS(SLIMTX3, "SLIMTX3"), +ARIZONA_MIXER_WIDGETS(SLIMTX4, "SLIMTX4"), +ARIZONA_MIXER_WIDGETS(SLIMTX5, "SLIMTX5"), +ARIZONA_MIXER_WIDGETS(SLIMTX6, "SLIMTX6"), +ARIZONA_MIXER_WIDGETS(SLIMTX7, "SLIMTX7"), +ARIZONA_MIXER_WIDGETS(SLIMTX8, "SLIMTX8"), + ARIZONA_MUX_WIDGETS(ASRC1L, "ASRC1L"), ARIZONA_MUX_WIDGETS(ASRC1R, "ASRC1R"), ARIZONA_MUX_WIDGETS(ASRC2L, "ASRC2L"), @@ -1248,6 +1325,14 @@ SND_SOC_DAPM_OUTPUT("MICSUPP"), { name, "AIF2RX2", "AIF2RX2" }, \ { name, "AIF3RX1", "AIF3RX1" }, \ { name, "AIF3RX2", "AIF3RX2" }, \ + { name, "SLIMRX1", "SLIMRX1" }, \ + { name, "SLIMRX2", "SLIMRX2" }, \ + { name, "SLIMRX3", "SLIMRX3" }, \ + { name, "SLIMRX4", "SLIMRX4" }, \ + { name, "SLIMRX5", "SLIMRX5" }, \ + { name, "SLIMRX6", "SLIMRX6" }, \ + { name, "SLIMRX7", "SLIMRX7" }, \ + { name, "SLIMRX8", "SLIMRX8" }, \ { name, "EQ1", "EQ1" }, \ { name, "EQ2", "EQ2" }, \ { name, "EQ3", "EQ3" }, \ @@ -1344,13 +1429,41 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = { { "AIF3RX1", NULL, "AIF3 Playback" }, { "AIF3RX2", NULL, "AIF3 Playback" }, + { "Slim1 Capture", NULL, "SLIMTX1" }, + { "Slim1 Capture", NULL, "SLIMTX2" }, + { "Slim1 Capture", NULL, "SLIMTX3" }, + { "Slim1 Capture", NULL, "SLIMTX4" }, + + { "SLIMRX1", NULL, "Slim1 Playback" }, + { "SLIMRX2", NULL, "Slim1 Playback" }, + { "SLIMRX3", NULL, "Slim1 Playback" }, + { "SLIMRX4", NULL, "Slim1 Playback" }, + + { "Slim2 Capture", NULL, "SLIMTX5" }, + { "Slim2 Capture", NULL, "SLIMTX6" }, + + { "SLIMRX5", NULL, "Slim2 Playback" }, + { "SLIMRX6", NULL, "Slim2 Playback" }, + + { "Slim3 Capture", NULL, "SLIMTX7" }, + { "Slim3 Capture", NULL, "SLIMTX8" }, + + { "SLIMRX7", NULL, "Slim3 Playback" }, + { "SLIMRX8", NULL, "Slim3 Playback" }, + { "AIF1 Playback", NULL, "SYSCLK" }, { "AIF2 Playback", NULL, "SYSCLK" }, { "AIF3 Playback", NULL, "SYSCLK" }, + { "Slim1 Playback", NULL, "SYSCLK" }, + { "Slim2 Playback", NULL, "SYSCLK" }, + { "Slim3 Playback", NULL, "SYSCLK" }, { "AIF1 Capture", NULL, "SYSCLK" }, { "AIF2 Capture", NULL, "SYSCLK" }, { "AIF3 Capture", NULL, "SYSCLK" }, + { "Slim1 Capture", NULL, "SYSCLK" }, + { "Slim2 Capture", NULL, "SYSCLK" }, + { "Slim3 Capture", NULL, "SYSCLK" }, { "IN1L PGA", NULL, "IN1L" }, { "IN1R PGA", NULL, "IN1R" }, @@ -1407,6 +1520,15 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = { ARIZONA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"), ARIZONA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"), + ARIZONA_MIXER_ROUTES("SLIMTX1", "SLIMTX1"), + ARIZONA_MIXER_ROUTES("SLIMTX2", "SLIMTX2"), + ARIZONA_MIXER_ROUTES("SLIMTX3", "SLIMTX3"), + ARIZONA_MIXER_ROUTES("SLIMTX4", "SLIMTX4"), + ARIZONA_MIXER_ROUTES("SLIMTX5", "SLIMTX5"), + ARIZONA_MIXER_ROUTES("SLIMTX6", "SLIMTX6"), + ARIZONA_MIXER_ROUTES("SLIMTX7", "SLIMTX7"), + ARIZONA_MIXER_ROUTES("SLIMTX8", "SLIMTX8"), + ARIZONA_MIXER_ROUTES("EQ1", "EQ1"), ARIZONA_MIXER_ROUTES("EQ2", "EQ2"), ARIZONA_MIXER_ROUTES("EQ3", "EQ3"), @@ -1559,6 +1681,63 @@ static struct snd_soc_dai_driver wm5102_dai[] = { .ops = &arizona_dai_ops, .symmetric_rates = 1, }, + { + .name = "wm5102-slim1", + .id = 4, + .playback = { + .stream_name = "Slim1 Playback", + .channels_min = 1, + .channels_max = 4, + .rates = WM5102_RATES, + .formats = WM5102_FORMATS, + }, + .capture = { + .stream_name = "Slim1 Capture", + .channels_min = 1, + .channels_max = 4, + .rates = WM5102_RATES, + .formats = WM5102_FORMATS, + }, + .ops = &arizona_simple_dai_ops, + }, + { + .name = "wm5102-slim2", + .id = 5, + .playback = { + .stream_name = "Slim2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM5102_RATES, + .formats = WM5102_FORMATS, + }, + .capture = { + .stream_name = "Slim2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM5102_RATES, + .formats = WM5102_FORMATS, + }, + .ops = &arizona_simple_dai_ops, + }, + { + .name = "wm5102-slim3", + .id = 6, + .playback = { + .stream_name = "Slim3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM5102_RATES, + .formats = WM5102_FORMATS, + }, + .capture = { + .stream_name = "Slim3 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM5102_RATES, + .formats = WM5102_FORMATS, + }, + .ops = &arizona_simple_dai_ops, + }, }; static int wm5102_codec_probe(struct snd_soc_codec *codec) -- cgit v1.2.3 From d217f9055631fb910f4f2e09ccf6446d93ff6533 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 17 May 2013 13:29:05 +0100 Subject: ASoC: wm5110: Stub hookup for Slimbus interface Signed-off-by: Mark Brown --- sound/soc/codecs/wm5110.c | 179 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index 731884e04776..f53062f8c42e 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -309,6 +309,15 @@ ARIZONA_MIXER_CONTROLS("AIF2TX2", ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("AIF3TX1", ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("AIF3TX2", ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE), + +ARIZONA_MIXER_CONTROLS("SLIMTX1", ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX2", ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX3", ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX4", ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX5", ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX6", ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX7", ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX8", ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE), }; ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE); @@ -360,6 +369,15 @@ ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE); ARIZONA_MIXER_ENUMS(AIF3TX1, ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE); ARIZONA_MIXER_ENUMS(AIF3TX2, ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX1, ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX2, ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX3, ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX4, ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX5, ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX6, ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX7, ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX8, ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE); + ARIZONA_MUX_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE); ARIZONA_MUX_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE); ARIZONA_MUX_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE); @@ -549,6 +567,56 @@ SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0, SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 0, ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX7", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX8_ENA_SHIFT, 0), + SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0, ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX1_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 0, @@ -639,6 +707,15 @@ ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"), ARIZONA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"), ARIZONA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"), +ARIZONA_MIXER_WIDGETS(SLIMTX1, "SLIMTX1"), +ARIZONA_MIXER_WIDGETS(SLIMTX2, "SLIMTX2"), +ARIZONA_MIXER_WIDGETS(SLIMTX3, "SLIMTX3"), +ARIZONA_MIXER_WIDGETS(SLIMTX4, "SLIMTX4"), +ARIZONA_MIXER_WIDGETS(SLIMTX5, "SLIMTX5"), +ARIZONA_MIXER_WIDGETS(SLIMTX6, "SLIMTX6"), +ARIZONA_MIXER_WIDGETS(SLIMTX7, "SLIMTX7"), +ARIZONA_MIXER_WIDGETS(SLIMTX8, "SLIMTX8"), + ARIZONA_MUX_WIDGETS(ASRC1L, "ASRC1L"), ARIZONA_MUX_WIDGETS(ASRC1R, "ASRC1R"), ARIZONA_MUX_WIDGETS(ASRC2L, "ASRC2L"), @@ -689,6 +766,14 @@ SND_SOC_DAPM_OUTPUT("MICSUPP"), { name, "AIF2RX2", "AIF2RX2" }, \ { name, "AIF3RX1", "AIF3RX1" }, \ { name, "AIF3RX2", "AIF3RX2" }, \ + { name, "SLIMRX1", "SLIMRX1" }, \ + { name, "SLIMRX2", "SLIMRX2" }, \ + { name, "SLIMRX3", "SLIMRX3" }, \ + { name, "SLIMRX4", "SLIMRX4" }, \ + { name, "SLIMRX5", "SLIMRX5" }, \ + { name, "SLIMRX6", "SLIMRX6" }, \ + { name, "SLIMRX7", "SLIMRX7" }, \ + { name, "SLIMRX8", "SLIMRX8" }, \ { name, "EQ1", "EQ1" }, \ { name, "EQ2", "EQ2" }, \ { name, "EQ3", "EQ3" }, \ @@ -776,13 +861,41 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = { { "AIF3RX1", NULL, "AIF3 Playback" }, { "AIF3RX2", NULL, "AIF3 Playback" }, + { "Slim1 Capture", NULL, "SLIMTX1" }, + { "Slim1 Capture", NULL, "SLIMTX2" }, + { "Slim1 Capture", NULL, "SLIMTX3" }, + { "Slim1 Capture", NULL, "SLIMTX4" }, + + { "SLIMRX1", NULL, "Slim1 Playback" }, + { "SLIMRX2", NULL, "Slim1 Playback" }, + { "SLIMRX3", NULL, "Slim1 Playback" }, + { "SLIMRX4", NULL, "Slim1 Playback" }, + + { "Slim2 Capture", NULL, "SLIMTX5" }, + { "Slim2 Capture", NULL, "SLIMTX6" }, + + { "SLIMRX5", NULL, "Slim2 Playback" }, + { "SLIMRX6", NULL, "Slim2 Playback" }, + + { "Slim3 Capture", NULL, "SLIMTX7" }, + { "Slim3 Capture", NULL, "SLIMTX8" }, + + { "SLIMRX7", NULL, "Slim3 Playback" }, + { "SLIMRX8", NULL, "Slim3 Playback" }, + { "AIF1 Playback", NULL, "SYSCLK" }, { "AIF2 Playback", NULL, "SYSCLK" }, { "AIF3 Playback", NULL, "SYSCLK" }, + { "Slim1 Playback", NULL, "SYSCLK" }, + { "Slim2 Playback", NULL, "SYSCLK" }, + { "Slim3 Playback", NULL, "SYSCLK" }, { "AIF1 Capture", NULL, "SYSCLK" }, { "AIF2 Capture", NULL, "SYSCLK" }, { "AIF3 Capture", NULL, "SYSCLK" }, + { "Slim1 Capture", NULL, "SYSCLK" }, + { "Slim2 Capture", NULL, "SYSCLK" }, + { "Slim3 Capture", NULL, "SYSCLK" }, { "IN1L PGA", NULL, "IN1L" }, { "IN1R PGA", NULL, "IN1R" }, @@ -828,6 +941,15 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = { ARIZONA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"), ARIZONA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"), + ARIZONA_MIXER_ROUTES("SLIMTX1", "SLIMTX1"), + ARIZONA_MIXER_ROUTES("SLIMTX2", "SLIMTX2"), + ARIZONA_MIXER_ROUTES("SLIMTX3", "SLIMTX3"), + ARIZONA_MIXER_ROUTES("SLIMTX4", "SLIMTX4"), + ARIZONA_MIXER_ROUTES("SLIMTX5", "SLIMTX5"), + ARIZONA_MIXER_ROUTES("SLIMTX6", "SLIMTX6"), + ARIZONA_MIXER_ROUTES("SLIMTX7", "SLIMTX7"), + ARIZONA_MIXER_ROUTES("SLIMTX8", "SLIMTX8"), + ARIZONA_MIXER_ROUTES("EQ1", "EQ1"), ARIZONA_MIXER_ROUTES("EQ2", "EQ2"), ARIZONA_MIXER_ROUTES("EQ3", "EQ3"), @@ -962,6 +1084,63 @@ static struct snd_soc_dai_driver wm5110_dai[] = { .ops = &arizona_dai_ops, .symmetric_rates = 1, }, + { + .name = "wm5110-slim1", + .id = 4, + .playback = { + .stream_name = "Slim1 Playback", + .channels_min = 1, + .channels_max = 4, + .rates = WM5110_RATES, + .formats = WM5110_FORMATS, + }, + .capture = { + .stream_name = "Slim1 Capture", + .channels_min = 1, + .channels_max = 4, + .rates = WM5110_RATES, + .formats = WM5110_FORMATS, + }, + .ops = &arizona_simple_dai_ops, + }, + { + .name = "wm5110-slim2", + .id = 5, + .playback = { + .stream_name = "Slim2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM5110_RATES, + .formats = WM5110_FORMATS, + }, + .capture = { + .stream_name = "Slim2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM5110_RATES, + .formats = WM5110_FORMATS, + }, + .ops = &arizona_simple_dai_ops, + }, + { + .name = "wm5110-slim3", + .id = 6, + .playback = { + .stream_name = "Slim3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM5110_RATES, + .formats = WM5110_FORMATS, + }, + .capture = { + .stream_name = "Slim3 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM5110_RATES, + .formats = WM5110_FORMATS, + }, + .ops = &arizona_simple_dai_ops, + }, }; static int wm5110_codec_probe(struct snd_soc_codec *codec) -- cgit v1.2.3 From 211d022c43cac3aecbe967fcaf9b10156bfa63ad Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 11 Apr 2013 22:09:56 +0200 Subject: xfs: Avoid pathological backwards allocation Writing a large file using direct IO in 16 MB chunks sometimes results in a pathological allocation pattern where 16 MB chunks of large free extent are allocated to a file in a reversed order. So extents of a file look for example as: ext logical physical expected length flags 0 0 13 4550656 1 4550656 188136807 4550668 12562432 2 17113088 200699240 200699238 622592 3 17735680 182046055 201321831 4096 4 17739776 182041959 182050150 4096 5 17743872 182037863 182046054 4096 6 17747968 182033767 182041958 4096 7 17752064 182029671 182037862 4096 ... 6757 45400064 154381644 154389835 4096 6758 45404160 154377548 154385739 4096 6759 45408256 252951571 154381643 73728 eof This happens because XFS_ALLOCTYPE_THIS_BNO allocation fails (the last extent in the file cannot be further extended) so we fall back to XFS_ALLOCTYPE_NEAR_BNO allocation which picks end of a large free extent as the best place to continue the file. Since the chunk at the end of the free extent again cannot be further extended, this behavior repeats until the whole free extent is consumed in a reversed order. For data allocations this backward allocation isn't beneficial so make xfs_alloc_compute_diff() pick start of a free extent instead of its end for them. That avoids the backward allocation pattern. See thread at http://oss.sgi.com/archives/xfs/2013-03/msg00144.html for more details about the reproduction case and why this solution was chosen. Based on idea by Dave Chinner . CC: Dave Chinner Signed-off-by: Jan Kara Reviewed-by: Dave Chinner Reviewed-by: Mark Tinguely Signed-off-by: Ben Myers --- fs/xfs/xfs_alloc.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/fs/xfs/xfs_alloc.c b/fs/xfs/xfs_alloc.c index 5673bcfda2f0..71596e57283a 100644 --- a/fs/xfs/xfs_alloc.c +++ b/fs/xfs/xfs_alloc.c @@ -175,6 +175,7 @@ xfs_alloc_compute_diff( xfs_agblock_t wantbno, /* target starting block */ xfs_extlen_t wantlen, /* target length */ xfs_extlen_t alignment, /* target alignment */ + char userdata, /* are we allocating data? */ xfs_agblock_t freebno, /* freespace's starting block */ xfs_extlen_t freelen, /* freespace's length */ xfs_agblock_t *newbnop) /* result: best start block from free */ @@ -189,7 +190,14 @@ xfs_alloc_compute_diff( ASSERT(freelen >= wantlen); freeend = freebno + freelen; wantend = wantbno + wantlen; - if (freebno >= wantbno) { + /* + * We want to allocate from the start of a free extent if it is past + * the desired block or if we are allocating user data and the free + * extent is before desired block. The second case is there to allow + * for contiguous allocation from the remaining free space if the file + * grows in the short term. + */ + if (freebno >= wantbno || (userdata && freeend < wantend)) { if ((newbno1 = roundup(freebno, alignment)) >= freeend) newbno1 = NULLAGBLOCK; } else if (freeend >= wantend && alignment > 1) { @@ -805,7 +813,8 @@ xfs_alloc_find_best_extent( xfs_alloc_fix_len(args); sdiff = xfs_alloc_compute_diff(args->agbno, args->len, - args->alignment, *sbnoa, + args->alignment, + args->userdata, *sbnoa, *slena, &new); /* @@ -976,7 +985,8 @@ restart: if (args->len < blen) continue; ltdiff = xfs_alloc_compute_diff(args->agbno, args->len, - args->alignment, ltbnoa, ltlena, <new); + args->alignment, args->userdata, ltbnoa, + ltlena, <new); if (ltnew != NULLAGBLOCK && (args->len > blen || ltdiff < bdiff)) { bdiff = ltdiff; @@ -1128,7 +1138,8 @@ restart: args->len = XFS_EXTLEN_MIN(ltlena, args->maxlen); xfs_alloc_fix_len(args); ltdiff = xfs_alloc_compute_diff(args->agbno, args->len, - args->alignment, ltbnoa, ltlena, <new); + args->alignment, args->userdata, ltbnoa, + ltlena, <new); error = xfs_alloc_find_best_extent(args, &bno_cur_lt, &bno_cur_gt, @@ -1144,7 +1155,8 @@ restart: args->len = XFS_EXTLEN_MIN(gtlena, args->maxlen); xfs_alloc_fix_len(args); gtdiff = xfs_alloc_compute_diff(args->agbno, args->len, - args->alignment, gtbnoa, gtlena, >new); + args->alignment, args->userdata, gtbnoa, + gtlena, >new); error = xfs_alloc_find_best_extent(args, &bno_cur_gt, &bno_cur_lt, @@ -1203,7 +1215,7 @@ restart: } rlen = args->len; (void)xfs_alloc_compute_diff(args->agbno, rlen, args->alignment, - ltbnoa, ltlena, <new); + args->userdata, ltbnoa, ltlena, <new); ASSERT(ltnew >= ltbno); ASSERT(ltnew + rlen <= ltbnoa + ltlena); ASSERT(ltnew + rlen <= be32_to_cpu(XFS_BUF_TO_AGF(args->agbp)->agf_length)); -- cgit v1.2.3 From 7711ece9b31f0f3cf15ca16005dfd78988ea8f7e Mon Sep 17 00:00:00 2001 From: Chris Zankel Date: Mon, 20 May 2013 11:09:13 -0700 Subject: xtensa: fix TLB multihit exceptions - set _PAGE_USER in the pte_clear to avoid having TLB multihit exceptions (see following threads for more details); http://lists.linux-xtensa.org/pipermail/linux-xtensa/Week-of-Mon-20130401/ http://lists.linux-xtensa.org/pipermail/linux-xtensa/Week-of-Mon-20130408/ - improved documentation of the PTE layout - fix PTE mapping for present and 'prot_none' pages for T1050 hw and earlier - fix pte_file offset and size - add check for the correct number of bits for swap type CC: piet.delaney@gmail.com CC: jcmvbkbc@gmail.com Signed-off-by: Chris Zankel --- arch/xtensa/include/asm/pgtable.h | 140 ++++++++++++++++++++++---------------- 1 file changed, 83 insertions(+), 57 deletions(-) diff --git a/arch/xtensa/include/asm/pgtable.h b/arch/xtensa/include/asm/pgtable.h index d7546c94da52..7e09f7011370 100644 --- a/arch/xtensa/include/asm/pgtable.h +++ b/arch/xtensa/include/asm/pgtable.h @@ -5,7 +5,7 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * - * Copyright (C) 2001 - 2007 Tensilica Inc. + * Copyright (C) 2001 - 2013 Tensilica Inc. */ #ifndef _XTENSA_PGTABLE_H @@ -64,41 +64,82 @@ * Virtual memory area. We keep a distance to other memory regions to be * on the safe side. We also use this area for cache aliasing. */ - #define VMALLOC_START 0xC0000000 #define VMALLOC_END 0xC7FEFFFF #define TLBTEMP_BASE_1 0xC7FF0000 #define TLBTEMP_BASE_2 0xC7FF8000 /* - * Xtensa Linux config PTE layout (when present): - * 31-12: PPN - * 11-6: Software - * 5-4: RING - * 3-0: CA + * For the Xtensa architecture, the PTE layout is as follows: + * + * 31------12 11 10-9 8-6 5-4 3-2 1-0 + * +-----------------------------------------+ + * | | Software | HARDWARE | + * | PPN | ADW | RI |Attribute| + * +-----------------------------------------+ + * pte_none | MBZ | 01 | 11 | 00 | + * +-----------------------------------------+ + * present | PPN | 0 | 00 | ADW | RI | CA | wx | + * +- - - - - - - - - - - - - - - - - - - - -+ + * (PAGE_NONE)| PPN | 0 | 00 | ADW | 01 | 11 | 11 | + * +-----------------------------------------+ + * swap | index | type | 01 | 11 | 00 | + * +- - - - - - - - - - - - - - - - - - - - -+ + * file | file offset | 01 | 11 | 10 | + * +-----------------------------------------+ + * + * For T1050 hardware and earlier the layout differs for present and (PAGE_NONE) + * +-----------------------------------------+ + * present | PPN | 0 | 00 | ADW | RI | CA | w1 | + * +-----------------------------------------+ + * (PAGE_NONE)| PPN | 0 | 00 | ADW | 01 | 01 | 00 | + * +-----------------------------------------+ * - * Similar to the Alpha and MIPS ports, we need to keep track of the ref - * and mod bits in software. We have a software "you can read - * from this page" bit, and a hardware one which actually lets the - * process read from the page. On the same token we have a software - * writable bit and the real hardware one which actually lets the - * process write to the page. + * Legend: + * PPN Physical Page Number + * ADW software: accessed (young) / dirty / writable + * RI ring (0=privileged, 1=user, 2 and 3 are unused) + * CA cache attribute: 00 bypass, 01 writeback, 10 writethrough + * (11 is invalid and used to mark pages that are not present) + * w page is writable (hw) + * x page is executable (hw) + * index swap offset / PAGE_SIZE (bit 11-31: 21 bits -> 8 GB) + * (note that the index is always non-zero) + * type swap type (5 bits -> 32 types) + * file offset 26-bit offset into the file, in increments of PAGE_SIZE * - * See further below for PTE layout for swapped-out pages. + * Notes: + * - (PROT_NONE) is a special case of 'present' but causes an exception for + * any access (read, write, and execute). + * - 'multihit-exception' has the highest priority of all MMU exceptions, + * so the ring must be set to 'RING_USER' even for 'non-present' pages. + * - on older hardware, the exectuable flag was not supported and + * used as a 'valid' flag, so it needs to be always set. + * - we need to keep track of certain flags in software (dirty and young) + * to do this, we use write exceptions and have a separate software w-flag. + * - attribute value 1101 (and 1111 on T1050 and earlier) is reserved */ +#define _PAGE_ATTRIB_MASK 0xf + #define _PAGE_HW_EXEC (1<<0) /* hardware: page is executable */ #define _PAGE_HW_WRITE (1<<1) /* hardware: page is writable */ -#define _PAGE_FILE (1<<1) /* non-linear mapping, if !present */ -#define _PAGE_PROTNONE (3<<0) /* special case for VM_PROT_NONE */ - -/* None of these cache modes include MP coherency: */ #define _PAGE_CA_BYPASS (0<<2) /* bypass, non-speculative */ #define _PAGE_CA_WB (1<<2) /* write-back */ #define _PAGE_CA_WT (2<<2) /* write-through */ #define _PAGE_CA_MASK (3<<2) -#define _PAGE_INVALID (3<<2) +#define _PAGE_CA_INVALID (3<<2) + +/* We use invalid attribute values to distinguish special pte entries */ +#if XCHAL_HW_VERSION_MAJOR < 2000 +#define _PAGE_HW_VALID 0x01 /* older HW needed this bit set */ +#define _PAGE_NONE 0x04 +#else +#define _PAGE_HW_VALID 0x00 +#define _PAGE_NONE 0x0f +#endif +#define _PAGE_FILE (1<<1) /* file mapped page, only if !present */ #define _PAGE_USER (1<<4) /* user access (ring=1) */ @@ -108,19 +149,12 @@ #define _PAGE_DIRTY (1<<7) /* software: page dirty */ #define _PAGE_ACCESSED (1<<8) /* software: page accessed (read) */ -/* On older HW revisions, we always have to set bit 0 */ -#if XCHAL_HW_VERSION_MAJOR < 2000 -# define _PAGE_VALID (1<<0) -#else -# define _PAGE_VALID 0 -#endif - -#define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY) -#define _PAGE_PRESENT (_PAGE_VALID | _PAGE_CA_WB | _PAGE_ACCESSED) - #ifdef CONFIG_MMU -#define PAGE_NONE __pgprot(_PAGE_INVALID | _PAGE_USER | _PAGE_PROTNONE) +#define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY) +#define _PAGE_PRESENT (_PAGE_HW_VALID | _PAGE_CA_WB | _PAGE_ACCESSED) + +#define PAGE_NONE __pgprot(_PAGE_NONE | _PAGE_USER) #define PAGE_COPY __pgprot(_PAGE_PRESENT | _PAGE_USER) #define PAGE_COPY_EXEC __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_HW_EXEC) #define PAGE_READONLY __pgprot(_PAGE_PRESENT | _PAGE_USER) @@ -132,9 +166,9 @@ #define PAGE_KERNEL_EXEC __pgprot(_PAGE_PRESENT|_PAGE_HW_WRITE|_PAGE_HW_EXEC) #if (DCACHE_WAY_SIZE > PAGE_SIZE) -# define _PAGE_DIRECTORY (_PAGE_VALID | _PAGE_ACCESSED) +# define _PAGE_DIRECTORY (_PAGE_HW_VALID | _PAGE_ACCESSED | _PAGE_CA_BYPASS) #else -# define _PAGE_DIRECTORY (_PAGE_VALID | _PAGE_ACCESSED | _PAGE_CA_WB) +# define _PAGE_DIRECTORY (_PAGE_HW_VALID | _PAGE_ACCESSED | _PAGE_CA_WB) #endif #else /* no mmu */ @@ -202,12 +236,16 @@ static inline void pgtable_cache_init(void) { } /* * pte status. */ -#define pte_none(pte) (pte_val(pte) == _PAGE_INVALID) -#define pte_present(pte) \ - (((pte_val(pte) & _PAGE_CA_MASK) != _PAGE_INVALID) \ - || ((pte_val(pte) & _PAGE_PROTNONE) == _PAGE_PROTNONE)) +# define pte_none(pte) (pte_val(pte) == (_PAGE_CA_INVALID | _PAGE_USER)) +#if XCHAL_HW_VERSION_MAJOR < 2000 +# define pte_present(pte) ((pte_val(pte) & _PAGE_CA_MASK) != _PAGE_CA_INVALID) +#else +# define pte_present(pte) \ + (((pte_val(pte) & _PAGE_CA_MASK) != _PAGE_CA_INVALID) \ + || ((pte_val(pte) & _PAGE_ATTRIB_MASK) == _PAGE_NONE)) +#endif #define pte_clear(mm,addr,ptep) \ - do { update_pte(ptep, __pte(_PAGE_INVALID)); } while(0) + do { update_pte(ptep, __pte(_PAGE_CA_INVALID | _PAGE_USER)); } while (0) #define pmd_none(pmd) (!pmd_val(pmd)) #define pmd_present(pmd) (pmd_val(pmd) & PAGE_MASK) @@ -328,35 +366,23 @@ ptep_set_wrprotect(struct mm_struct *mm, unsigned long addr, pte_t *ptep) /* - * Encode and decode a swap entry. - * - * Format of swap pte: - * bit 0 MBZ - * bit 1 page-file (must be zero) - * bits 2 - 3 page hw access mode (must be 11: _PAGE_INVALID) - * bits 4 - 5 ring protection (must be 01: _PAGE_USER) - * bits 6 - 10 swap type (5 bits -> 32 types) - * bits 11 - 31 swap offset / PAGE_SIZE (21 bits -> 8GB) - - * Format of file pte: - * bit 0 MBZ - * bit 1 page-file (must be one: _PAGE_FILE) - * bits 2 - 3 page hw access mode (must be 11: _PAGE_INVALID) - * bits 4 - 5 ring protection (must be 01: _PAGE_USER) - * bits 6 - 31 file offset / PAGE_SIZE + * Encode and decode a swap and file entry. */ +#define SWP_TYPE_BITS 5 +#define MAX_SWAPFILES_CHECK() BUILD_BUG_ON(MAX_SWAPFILES_SHIFT > SWP_TYPE_BITS) #define __swp_type(entry) (((entry).val >> 6) & 0x1f) #define __swp_offset(entry) ((entry).val >> 11) #define __swp_entry(type,offs) \ - ((swp_entry_t) {((type) << 6) | ((offs) << 11) | _PAGE_INVALID}) + ((swp_entry_t){((type) << 6) | ((offs) << 11) | \ + _PAGE_CA_INVALID | _PAGE_USER}) #define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) }) #define __swp_entry_to_pte(x) ((pte_t) { (x).val }) -#define PTE_FILE_MAX_BITS 28 -#define pte_to_pgoff(pte) (pte_val(pte) >> 4) +#define PTE_FILE_MAX_BITS 26 +#define pte_to_pgoff(pte) (pte_val(pte) >> 6) #define pgoff_to_pte(off) \ - ((pte_t) { ((off) << 4) | _PAGE_INVALID | _PAGE_FILE }) + ((pte_t) { ((off) << 6) | _PAGE_CA_INVALID | _PAGE_FILE | _PAGE_USER }) #endif /* !defined (__ASSEMBLY__) */ -- cgit v1.2.3 From 51fc41a90603eaee7b6d03b6027be8f22fcf8ef9 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Sat, 18 May 2013 23:34:30 +0400 Subject: xtensa: fix fast_store_prohibited _PAGE_WRITABLE_BIT test Before _PAGE_WRITABLE_BIT test fast_store_prohibited must make sure that PTE is present. Otherwise 'writable' bit is undefined and may be reused in the 'file offset' or 'swap type' PTE fields. Signed-off-by: Max Filippov Signed-off-by: Chris Zankel --- arch/xtensa/kernel/entry.S | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/xtensa/kernel/entry.S b/arch/xtensa/kernel/entry.S index 5082507d5631..fa94512ff84d 100644 --- a/arch/xtensa/kernel/entry.S +++ b/arch/xtensa/kernel/entry.S @@ -1792,10 +1792,15 @@ ENTRY(fast_store_prohibited) l32i a0, a0, 0 beqz a0, 2f - /* Note that we assume _PAGE_WRITABLE_BIT is only set if pte is valid.*/ + /* + * Note that we test _PAGE_WRITABLE_BIT only if PTE is present + * and is not PAGE_NONE. See pgtable.h for possible PTE layouts. + */ _PTE_OFFSET(a0, a1, a4) l32i a4, a0, 0 # read pteval + movi a1, _PAGE_CA_INVALID + ball a4, a1, 2f bbci.l a4, _PAGE_WRITABLE_BIT, 2f movi a1, _PAGE_ACCESSED | _PAGE_DIRTY | _PAGE_HW_WRITE -- cgit v1.2.3 From 49b137cbbcc836ef231866c137d24f42c42bb483 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 20 May 2013 09:51:08 +1000 Subject: xfs: fix sub-page blocksize data integrity writes FSX on 512 byte block size filesystems has been failing for some time with corrupted data. The fault dates back to the change in the writeback data integrity algorithm that uses a mark-and-sweep approach to avoid data writeback livelocks. Unfortunately, a side effect of this mark-and-sweep approach is that each page will only be written once for a data integrity sync, and there is a condition in writeback in XFS where a page may require two writeback attempts to be fully written. As a result of the high level change, we now only get a partial page writeback during the integrity sync because the first pass through writeback clears the mark left on the page index to tell writeback that the page needs writeback.... The cause is writing a partial page in the clustering code. This can happen when a mapping boundary falls in the middle of a page - we end up writing back the first part of the page that the mapping covers, but then never revisit the page to have the remainder mapped and written. The fix is simple - if the mapping boundary falls inside a page, then simple abort clustering without touching the page. This means that the next ->writepage entry that write_cache_pages() will make is the page we aborted on, and xfs_vm_writepage() will map all sections of the page correctly. This behaviour is also optimal for non-data integrity writes, as it results in contiguous sequential writeback of the file rather than missing small holes and having to write them a "random" writes in a future pass. With this fix, all the fsx tests in xfstests now pass on a 512 byte block size filesystem on a 4k page machine. Signed-off-by: Dave Chinner Reviewed-by: Brian Foster Signed-off-by: Ben Myers --- fs/xfs/xfs_aops.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c index 2b2691b73428..41a695048be7 100644 --- a/fs/xfs/xfs_aops.c +++ b/fs/xfs/xfs_aops.c @@ -725,6 +725,25 @@ xfs_convert_page( (xfs_off_t)(page->index + 1) << PAGE_CACHE_SHIFT, i_size_read(inode)); + /* + * If the current map does not span the entire page we are about to try + * to write, then give up. The only way we can write a page that spans + * multiple mappings in a single writeback iteration is via the + * xfs_vm_writepage() function. Data integrity writeback requires the + * entire page to be written in a single attempt, otherwise the part of + * the page we don't write here doesn't get written as part of the data + * integrity sync. + * + * For normal writeback, we also don't attempt to write partial pages + * here as it simply means that write_cache_pages() will see it under + * writeback and ignore the page until some point in the future, at + * which time this will be the only page in the file that needs + * writeback. Hence for more optimal IO patterns, we should always + * avoid partial page writeback due to multiple mappings on a page here. + */ + if (!xfs_imap_valid(inode, imap, end_offset)) + goto fail_unlock_page; + len = 1 << inode->i_blkbits; p_offset = min_t(unsigned long, end_offset & (PAGE_CACHE_SIZE - 1), PAGE_CACHE_SIZE); -- cgit v1.2.3 From 28ca489c63e9aceed8801d2f82d731b3c9aa50f5 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 20 May 2013 09:51:09 +1000 Subject: xfs: fix rounding in xfs_free_file_space The offset passed into xfs_free_file_space() needs to be rounded down to a certain size, but the rounding mask is built by a 32 bit variable. Hence the mask will always mask off the upper 32 bits of the offset and lead to incorrect writeback and invalidation ranges. This is not actually exposed as a bug because we writeback and invalidate from the rounded offset to the end of the file, and hence the offset we are actually punching a hole out of will always be covered by the code. This needs fixing, however, if we ever want to use exact ranges for writeback/invalidation here... Signed-off-by: Dave Chinner Reviewed-by: Brian Foster Signed-off-by: Ben Myers --- fs/xfs/xfs_vnodeops.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c index 1501f4fa51a6..0176bb21f09a 100644 --- a/fs/xfs/xfs_vnodeops.c +++ b/fs/xfs/xfs_vnodeops.c @@ -1453,7 +1453,7 @@ xfs_free_file_space( xfs_mount_t *mp; int nimap; uint resblks; - uint rounding; + xfs_off_t rounding; int rt; xfs_fileoff_t startoffset_fsb; xfs_trans_t *tp; @@ -1482,7 +1482,7 @@ xfs_free_file_space( inode_dio_wait(VFS_I(ip)); } - rounding = max_t(uint, 1 << mp->m_sb.sb_blocklog, PAGE_CACHE_SIZE); + rounding = max_t(xfs_off_t, 1 << mp->m_sb.sb_blocklog, PAGE_CACHE_SIZE); ioffset = offset & ~(rounding - 1); error = -filemap_write_and_wait_range(VFS_I(ip)->i_mapping, ioffset, -1); -- cgit v1.2.3 From 52c24ad39ff02d7bd73c92eb0c926fb44984a41d Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 20 May 2013 09:51:10 +1000 Subject: xfs: Don't reference the EFI after it is freed Checking the EFI for whether it is being released from recovery after we've already released the known active reference is a mistake worthy of a brown paper bag. Fix the (now) obvious use after free that it can cause. Reported-by: Dave Jones Signed-off-by: Dave Chinner Reviewed-by: Brian Foster Signed-off-by: Ben Myers --- fs/xfs/xfs_extfree_item.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/xfs/xfs_extfree_item.c b/fs/xfs/xfs_extfree_item.c index c0f375087efc..452920a3f03f 100644 --- a/fs/xfs/xfs_extfree_item.c +++ b/fs/xfs/xfs_extfree_item.c @@ -305,11 +305,12 @@ xfs_efi_release(xfs_efi_log_item_t *efip, { ASSERT(atomic_read(&efip->efi_next_extent) >= nextents); if (atomic_sub_and_test(nextents, &efip->efi_next_extent)) { - __xfs_efi_release(efip); - /* recovery needs us to drop the EFI reference, too */ if (test_bit(XFS_EFI_RECOVERED, &efip->efi_flags)) __xfs_efi_release(efip); + + __xfs_efi_release(efip); + /* efip may now have been freed, do not reference it again. */ } } -- cgit v1.2.3 From 98304ad186296dc1e655399e28d5973c21db6a73 Mon Sep 17 00:00:00 2001 From: "braggle@free.fr" Date: Thu, 16 May 2013 12:57:38 +0200 Subject: drm/i915: add support for dvo Chrontel 7010B This patch add dvo detection for the Chrontel 7010B on some old hardware. References: https://bugzilla.kernel.org/show_bug.cgi?id=55101 Signed-off-by: Braggle [danvet: Fix up whitespace mangling.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/dvo_ch7xxx.c | 28 ++++++++++++++++++++++++++-- drivers/gpu/drm/i915/intel_dvo.c | 7 +++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/dvo_ch7xxx.c b/drivers/gpu/drm/i915/dvo_ch7xxx.c index 3edd981e0770..757e0fa11043 100644 --- a/drivers/gpu/drm/i915/dvo_ch7xxx.c +++ b/drivers/gpu/drm/i915/dvo_ch7xxx.c @@ -32,12 +32,14 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define CH7xxx_REG_DID 0x4b #define CH7011_VID 0x83 /* 7010 as well */ +#define CH7010B_VID 0x05 #define CH7009A_VID 0x84 #define CH7009B_VID 0x85 #define CH7301_VID 0x95 #define CH7xxx_VID 0x84 #define CH7xxx_DID 0x17 +#define CH7010_DID 0x16 #define CH7xxx_NUM_REGS 0x4c @@ -87,11 +89,20 @@ static struct ch7xxx_id_struct { char *name; } ch7xxx_ids[] = { { CH7011_VID, "CH7011" }, + { CH7010B_VID, "CH7010B" }, { CH7009A_VID, "CH7009A" }, { CH7009B_VID, "CH7009B" }, { CH7301_VID, "CH7301" }, }; +static struct ch7xxx_did_struct { + uint8_t did; + char *name; +} ch7xxx_dids[] = { + { CH7xxx_DID, "CH7XXX" }, + { CH7010_DID, "CH7010B" }, +}; + struct ch7xxx_priv { bool quiet; }; @@ -108,6 +119,18 @@ static char *ch7xxx_get_id(uint8_t vid) return NULL; } +static char *ch7xxx_get_did(uint8_t did) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ch7xxx_dids); i++) { + if (ch7xxx_dids[i].did == did) + return ch7xxx_dids[i].name; + } + + return NULL; +} + /** Reads an 8 bit register */ static bool ch7xxx_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch) { @@ -179,7 +202,7 @@ static bool ch7xxx_init(struct intel_dvo_device *dvo, /* this will detect the CH7xxx chip on the specified i2c bus */ struct ch7xxx_priv *ch7xxx; uint8_t vendor, device; - char *name; + char *name, *devid; ch7xxx = kzalloc(sizeof(struct ch7xxx_priv), GFP_KERNEL); if (ch7xxx == NULL) @@ -204,7 +227,8 @@ static bool ch7xxx_init(struct intel_dvo_device *dvo, if (!ch7xxx_readb(dvo, CH7xxx_REG_DID, &device)) goto out; - if (device != CH7xxx_DID) { + devid = ch7xxx_get_did(device); + if (!devid) { DRM_DEBUG_KMS("ch7xxx not detected; got 0x%02x from %s " "slave %d.\n", vendor, adapter->name, dvo->slave_addr); diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c index 00e70dbe82da..ee328ce8e9fe 100644 --- a/drivers/gpu/drm/i915/intel_dvo.c +++ b/drivers/gpu/drm/i915/intel_dvo.c @@ -53,6 +53,13 @@ static const struct intel_dvo_device intel_dvo_devices[] = { .slave_addr = CH7xxx_ADDR, .dev_ops = &ch7xxx_ops, }, + { + .type = INTEL_DVO_CHIP_TMDS, + .name = "ch7xxx", + .dvo_reg = DVOC, + .slave_addr = 0x75, /* For some ch7010 */ + .dev_ops = &ch7xxx_ops, + }, { .type = INTEL_DVO_CHIP_LVDS, .name = "ivch", -- cgit v1.2.3 From 4fc1ad6f4b4d9214505eda434b1ed3b0bfd16981 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Sun, 19 May 2013 10:17:13 +0000 Subject: 3c59x: remove useless VORTEX_PCI() invocations It's suboptimal to invoke quite complex VORTEX_PCI() macro every time we want to get a 'struct pci_dev *' when we already have it in a variable... Signed-off-by: Sergei Shtylyov Signed-off-by: David S. Miller --- drivers/net/ethernet/3com/3c59x.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/3com/3c59x.c b/drivers/net/ethernet/3com/3c59x.c index 072c6f14e8fc..30e74211a755 100644 --- a/drivers/net/ethernet/3com/3c59x.c +++ b/drivers/net/ethernet/3com/3c59x.c @@ -1473,7 +1473,7 @@ static int vortex_probe1(struct device *gendev, void __iomem *ioaddr, int irq, if (pdev) { vp->pm_state_valid = 1; - pci_save_state(VORTEX_PCI(vp)); + pci_save_state(pdev); acpi_set_WOL(dev); } retval = register_netdev(dev); @@ -3233,21 +3233,20 @@ static void vortex_remove_one(struct pci_dev *pdev) vp = netdev_priv(dev); if (vp->cb_fn_base) - pci_iounmap(VORTEX_PCI(vp), vp->cb_fn_base); + pci_iounmap(pdev, vp->cb_fn_base); unregister_netdev(dev); - if (VORTEX_PCI(vp)) { - pci_set_power_state(VORTEX_PCI(vp), PCI_D0); /* Go active */ - if (vp->pm_state_valid) - pci_restore_state(VORTEX_PCI(vp)); - pci_disable_device(VORTEX_PCI(vp)); - } + pci_set_power_state(pdev, PCI_D0); /* Go active */ + if (vp->pm_state_valid) + pci_restore_state(pdev); + pci_disable_device(pdev); + /* Should really use issue_and_wait() here */ iowrite16(TotalReset | ((vp->drv_flags & EEPROM_RESET) ? 0x04 : 0x14), vp->ioaddr + EL3_CMD); - pci_iounmap(VORTEX_PCI(vp), vp->ioaddr); + pci_iounmap(pdev, vp->ioaddr); pci_free_consistent(pdev, sizeof(struct boom_rx_desc) * RX_RING_SIZE -- cgit v1.2.3 From a9683c94fe8c8f87fdc8380a2e76506aa1502dcb Mon Sep 17 00:00:00 2001 From: Tony Prisk Date: Sat, 18 May 2013 09:39:05 +0000 Subject: net: velocity: Rename vptr->dev to vptr->netdev Improve the clarity of the code in preparation for converting the dma functions to generic versions, which require a struct device *. This makes it possible to store a 'struct device *dev' in the velocity_info structure. Signed-off-by: Tony Prisk Signed-off-by: David S. Miller --- drivers/net/ethernet/via/via-velocity.c | 66 ++++++++++++++++----------------- drivers/net/ethernet/via/via-velocity.h | 4 +- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/drivers/net/ethernet/via/via-velocity.c b/drivers/net/ethernet/via/via-velocity.c index fb6248956ee2..187eef33df11 100644 --- a/drivers/net/ethernet/via/via-velocity.c +++ b/drivers/net/ethernet/via/via-velocity.c @@ -998,9 +998,9 @@ static void velocity_print_link_status(struct velocity_info *vptr) { if (vptr->mii_status & VELOCITY_LINK_FAIL) { - VELOCITY_PRT(MSG_LEVEL_INFO, KERN_NOTICE "%s: failed to detect cable link\n", vptr->dev->name); + VELOCITY_PRT(MSG_LEVEL_INFO, KERN_NOTICE "%s: failed to detect cable link\n", vptr->netdev->name); } else if (vptr->options.spd_dpx == SPD_DPX_AUTO) { - VELOCITY_PRT(MSG_LEVEL_INFO, KERN_NOTICE "%s: Link auto-negotiation", vptr->dev->name); + VELOCITY_PRT(MSG_LEVEL_INFO, KERN_NOTICE "%s: Link auto-negotiation", vptr->netdev->name); if (vptr->mii_status & VELOCITY_SPEED_1000) VELOCITY_PRT(MSG_LEVEL_INFO, " speed 1000M bps"); @@ -1014,7 +1014,7 @@ static void velocity_print_link_status(struct velocity_info *vptr) else VELOCITY_PRT(MSG_LEVEL_INFO, " half duplex\n"); } else { - VELOCITY_PRT(MSG_LEVEL_INFO, KERN_NOTICE "%s: Link forced", vptr->dev->name); + VELOCITY_PRT(MSG_LEVEL_INFO, KERN_NOTICE "%s: Link forced", vptr->netdev->name); switch (vptr->options.spd_dpx) { case SPD_DPX_1000_FULL: VELOCITY_PRT(MSG_LEVEL_INFO, " speed 1000M bps full duplex\n"); @@ -1319,7 +1319,7 @@ static void velocity_init_registers(struct velocity_info *vptr, case VELOCITY_INIT_RESET: case VELOCITY_INIT_WOL: - netif_stop_queue(vptr->dev); + netif_stop_queue(vptr->netdev); /* * Reset RX to prevent RX pointer not on the 4X location @@ -1332,7 +1332,7 @@ static void velocity_init_registers(struct velocity_info *vptr, if (velocity_set_media_mode(vptr, mii_status) != VELOCITY_LINK_CHANGE) { velocity_print_link_status(vptr); if (!(vptr->mii_status & VELOCITY_LINK_FAIL)) - netif_wake_queue(vptr->dev); + netif_wake_queue(vptr->netdev); } enable_flow_control_ability(vptr); @@ -1354,7 +1354,7 @@ static void velocity_init_registers(struct velocity_info *vptr, mac_eeprom_reload(regs); for (i = 0; i < 6; i++) - writeb(vptr->dev->dev_addr[i], &(regs->PAR[i])); + writeb(vptr->netdev->dev_addr[i], &(regs->PAR[i])); /* * clear Pre_ACPI bit. @@ -1377,7 +1377,7 @@ static void velocity_init_registers(struct velocity_info *vptr, /* * Set packet filter: Receive directed and broadcast address */ - velocity_set_multi(vptr->dev); + velocity_set_multi(vptr->netdev); /* * Enable MII auto-polling @@ -1404,14 +1404,14 @@ static void velocity_init_registers(struct velocity_info *vptr, writel((CR0_DPOLL | CR0_TXON | CR0_RXON | CR0_STRT), ®s->CR0Set); mii_status = velocity_get_opt_media_mode(vptr); - netif_stop_queue(vptr->dev); + netif_stop_queue(vptr->netdev); mii_init(vptr, mii_status); if (velocity_set_media_mode(vptr, mii_status) != VELOCITY_LINK_CHANGE) { velocity_print_link_status(vptr); if (!(vptr->mii_status & VELOCITY_LINK_FAIL)) - netif_wake_queue(vptr->dev); + netif_wake_queue(vptr->netdev); } enable_flow_control_ability(vptr); @@ -1474,7 +1474,7 @@ static int velocity_init_dma_rings(struct velocity_info *vptr) rx_ring_size, &pool_dma); if (!pool) { dev_err(&pdev->dev, "%s : DMA memory allocation failed.\n", - vptr->dev->name); + vptr->netdev->name); return -ENOMEM; } @@ -1514,7 +1514,7 @@ static int velocity_alloc_rx_buf(struct velocity_info *vptr, int idx) struct rx_desc *rd = &(vptr->rx.ring[idx]); struct velocity_rd_info *rd_info = &(vptr->rx.info[idx]); - rd_info->skb = netdev_alloc_skb(vptr->dev, vptr->rx.buf_sz + 64); + rd_info->skb = netdev_alloc_skb(vptr->netdev, vptr->rx.buf_sz + 64); if (rd_info->skb == NULL) return -ENOMEM; @@ -1620,7 +1620,7 @@ static int velocity_init_rd_ring(struct velocity_info *vptr) if (velocity_rx_refill(vptr) != vptr->options.numrx) { VELOCITY_PRT(MSG_LEVEL_ERR, KERN_ERR - "%s: failed to allocate RX buffer.\n", vptr->dev->name); + "%s: failed to allocate RX buffer.\n", vptr->netdev->name); velocity_free_rd_ring(vptr); goto out; } @@ -1809,7 +1809,7 @@ static void velocity_error(struct velocity_info *vptr, int status) printk(KERN_ERR "TD structure error TDindex=%hx\n", readw(®s->TDIdx[0])); BYTE_REG_BITS_ON(TXESR_TDSTR, ®s->TXESR); writew(TRDCSR_RUN, ®s->TDCSRClr); - netif_stop_queue(vptr->dev); + netif_stop_queue(vptr->netdev); /* FIXME: port over the pci_device_failed code and use it here */ @@ -1850,10 +1850,10 @@ static void velocity_error(struct velocity_info *vptr, int status) if (linked) { vptr->mii_status &= ~VELOCITY_LINK_FAIL; - netif_carrier_on(vptr->dev); + netif_carrier_on(vptr->netdev); } else { vptr->mii_status |= VELOCITY_LINK_FAIL; - netif_carrier_off(vptr->dev); + netif_carrier_off(vptr->netdev); } velocity_print_link_status(vptr); @@ -1867,9 +1867,9 @@ static void velocity_error(struct velocity_info *vptr, int status) enable_mii_autopoll(regs); if (vptr->mii_status & VELOCITY_LINK_FAIL) - netif_stop_queue(vptr->dev); + netif_stop_queue(vptr->netdev); else - netif_wake_queue(vptr->dev); + netif_wake_queue(vptr->netdev); } if (status & ISR_MIBFI) @@ -1894,7 +1894,7 @@ static int velocity_tx_srv(struct velocity_info *vptr) int idx; int works = 0; struct velocity_td_info *tdinfo; - struct net_device_stats *stats = &vptr->dev->stats; + struct net_device_stats *stats = &vptr->netdev->stats; for (qnum = 0; qnum < vptr->tx.numq; qnum++) { for (idx = vptr->tx.tail[qnum]; vptr->tx.used[qnum] > 0; @@ -1939,9 +1939,9 @@ static int velocity_tx_srv(struct velocity_info *vptr) * Look to see if we should kick the transmit network * layer for more work. */ - if (netif_queue_stopped(vptr->dev) && (full == 0) && + if (netif_queue_stopped(vptr->netdev) && (full == 0) && (!(vptr->mii_status & VELOCITY_LINK_FAIL))) { - netif_wake_queue(vptr->dev); + netif_wake_queue(vptr->netdev); } return works; } @@ -1989,7 +1989,7 @@ static int velocity_rx_copy(struct sk_buff **rx_skb, int pkt_size, if (pkt_size < rx_copybreak) { struct sk_buff *new_skb; - new_skb = netdev_alloc_skb_ip_align(vptr->dev, pkt_size); + new_skb = netdev_alloc_skb_ip_align(vptr->netdev, pkt_size); if (new_skb) { new_skb->ip_summed = rx_skb[0]->ip_summed; skb_copy_from_linear_data(*rx_skb, new_skb->data, pkt_size); @@ -2030,14 +2030,14 @@ static inline void velocity_iph_realign(struct velocity_info *vptr, static int velocity_receive_frame(struct velocity_info *vptr, int idx) { void (*pci_action)(struct pci_dev *, dma_addr_t, size_t, int); - struct net_device_stats *stats = &vptr->dev->stats; + struct net_device_stats *stats = &vptr->netdev->stats; struct velocity_rd_info *rd_info = &(vptr->rx.info[idx]); struct rx_desc *rd = &(vptr->rx.ring[idx]); int pkt_len = le16_to_cpu(rd->rdesc0.len) & 0x3fff; struct sk_buff *skb; if (rd->rdesc0.RSR & (RSR_STP | RSR_EDP)) { - VELOCITY_PRT(MSG_LEVEL_VERBOSE, KERN_ERR " %s : the received frame span multple RDs.\n", vptr->dev->name); + VELOCITY_PRT(MSG_LEVEL_VERBOSE, KERN_ERR " %s : the received frame span multple RDs.\n", vptr->netdev->name); stats->rx_length_errors++; return -EINVAL; } @@ -2075,7 +2075,7 @@ static int velocity_receive_frame(struct velocity_info *vptr, int idx) PCI_DMA_FROMDEVICE); skb_put(skb, pkt_len - 4); - skb->protocol = eth_type_trans(skb, vptr->dev); + skb->protocol = eth_type_trans(skb, vptr->netdev); if (rd->rdesc0.RSR & RSR_DETAG) { u16 vid = swab16(le16_to_cpu(rd->rdesc1.PQTAG)); @@ -2100,7 +2100,7 @@ static int velocity_receive_frame(struct velocity_info *vptr, int idx) */ static int velocity_rx_srv(struct velocity_info *vptr, int budget_left) { - struct net_device_stats *stats = &vptr->dev->stats; + struct net_device_stats *stats = &vptr->netdev->stats; int rd_curr = vptr->rx.curr; int works = 0; @@ -2292,7 +2292,7 @@ static int velocity_change_mtu(struct net_device *dev, int new_mtu) if ((new_mtu < VELOCITY_MIN_MTU) || new_mtu > (VELOCITY_MAX_MTU)) { VELOCITY_PRT(MSG_LEVEL_ERR, KERN_NOTICE "%s: Invalid MTU.\n", - vptr->dev->name); + vptr->netdev->name); ret = -EINVAL; goto out_0; } @@ -2314,7 +2314,7 @@ static int velocity_change_mtu(struct net_device *dev, int new_mtu) goto out_0; } - tmp_vptr->dev = dev; + tmp_vptr->netdev = dev; tmp_vptr->pdev = vptr->pdev; tmp_vptr->options = vptr->options; tmp_vptr->tx.numq = vptr->tx.numq; @@ -2692,7 +2692,7 @@ static int velocity_get_pci_info(struct velocity_info *vptr, */ static void velocity_print_info(struct velocity_info *vptr) { - struct net_device *dev = vptr->dev; + struct net_device *dev = vptr->netdev; printk(KERN_INFO "%s: %s\n", dev->name, get_chip_name(vptr->chip_id)); printk(KERN_INFO "%s: Ethernet Address: %pM\n", @@ -2755,7 +2755,7 @@ static int velocity_found1(struct pci_dev *pdev, velocity_init_info(pdev, vptr, info); - vptr->dev = dev; + vptr->netdev = dev; ret = pci_enable_device(pdev); if (ret < 0) @@ -3010,10 +3010,10 @@ static int velocity_suspend(struct pci_dev *pdev, pm_message_t state) struct velocity_info *vptr = netdev_priv(dev); unsigned long flags; - if (!netif_running(vptr->dev)) + if (!netif_running(vptr->netdev)) return 0; - netif_device_detach(vptr->dev); + netif_device_detach(vptr->netdev); spin_lock_irqsave(&vptr->lock, flags); pci_save_state(pdev); @@ -3078,7 +3078,7 @@ static int velocity_resume(struct pci_dev *pdev) unsigned long flags; int i; - if (!netif_running(vptr->dev)) + if (!netif_running(vptr->netdev)) return 0; pci_set_power_state(pdev, PCI_D0); @@ -3101,7 +3101,7 @@ static int velocity_resume(struct pci_dev *pdev) mac_enable_int(vptr->mac_regs); spin_unlock_irqrestore(&vptr->lock, flags); - netif_device_attach(vptr->dev); + netif_device_attach(vptr->netdev); return 0; } diff --git a/drivers/net/ethernet/via/via-velocity.h b/drivers/net/ethernet/via/via-velocity.h index 4cb9f13485e9..ff8d7828aa1b 100644 --- a/drivers/net/ethernet/via/via-velocity.h +++ b/drivers/net/ethernet/via/via-velocity.h @@ -1435,7 +1435,7 @@ struct velocity_opt { struct velocity_info { struct pci_dev *pdev; - struct net_device *dev; + struct net_device *netdev; unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; u8 ip_addr[4]; @@ -1514,7 +1514,7 @@ static inline int velocity_get_ip(struct velocity_info *vptr) int res = -ENOENT; rcu_read_lock(); - in_dev = __in_dev_get_rcu(vptr->dev); + in_dev = __in_dev_get_rcu(vptr->netdev); if (in_dev != NULL) { ifa = (struct in_ifaddr *) in_dev->ifa_list; if (ifa != NULL) { -- cgit v1.2.3 From e2c41f143fb2db14ad9b644a515c98e6c2e5ea2d Mon Sep 17 00:00:00 2001 From: Tony Prisk Date: Sat, 18 May 2013 09:39:06 +0000 Subject: net: velocity: Convert to generic dma functions Remove the pci_* dma functions and replace with the more generic versions. In preparation of adding platform support, a new struct device *dev is added to struct velocity_info which can be used by both the pci and platform code. Signed-off-by: Tony Prisk Signed-off-by: David S. Miller --- drivers/net/ethernet/via/via-velocity.c | 51 ++++++++++++++++----------------- drivers/net/ethernet/via/via-velocity.h | 1 + 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/drivers/net/ethernet/via/via-velocity.c b/drivers/net/ethernet/via/via-velocity.c index 187eef33df11..5996cee0ffa7 100644 --- a/drivers/net/ethernet/via/via-velocity.c +++ b/drivers/net/ethernet/via/via-velocity.c @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -1459,7 +1460,6 @@ static int velocity_init_dma_rings(struct velocity_info *vptr) struct velocity_opt *opt = &vptr->options; const unsigned int rx_ring_size = opt->numrx * sizeof(struct rx_desc); const unsigned int tx_ring_size = opt->numtx * sizeof(struct tx_desc); - struct pci_dev *pdev = vptr->pdev; dma_addr_t pool_dma; void *pool; unsigned int i; @@ -1467,13 +1467,13 @@ static int velocity_init_dma_rings(struct velocity_info *vptr) /* * Allocate all RD/TD rings a single pool. * - * pci_alloc_consistent() fulfills the requirement for 64 bytes + * dma_alloc_coherent() fulfills the requirement for 64 bytes * alignment */ - pool = pci_alloc_consistent(pdev, tx_ring_size * vptr->tx.numq + - rx_ring_size, &pool_dma); + pool = dma_alloc_coherent(vptr->dev, tx_ring_size * vptr->tx.numq + + rx_ring_size, &pool_dma, GFP_ATOMIC); if (!pool) { - dev_err(&pdev->dev, "%s : DMA memory allocation failed.\n", + dev_err(vptr->dev, "%s : DMA memory allocation failed.\n", vptr->netdev->name); return -ENOMEM; } @@ -1524,8 +1524,8 @@ static int velocity_alloc_rx_buf(struct velocity_info *vptr, int idx) */ skb_reserve(rd_info->skb, 64 - ((unsigned long) rd_info->skb->data & 63)); - rd_info->skb_dma = pci_map_single(vptr->pdev, rd_info->skb->data, - vptr->rx.buf_sz, PCI_DMA_FROMDEVICE); + rd_info->skb_dma = dma_map_single(vptr->dev, rd_info->skb->data, + vptr->rx.buf_sz, DMA_FROM_DEVICE); /* * Fill in the descriptor to match @@ -1588,8 +1588,8 @@ static void velocity_free_rd_ring(struct velocity_info *vptr) if (!rd_info->skb) continue; - pci_unmap_single(vptr->pdev, rd_info->skb_dma, vptr->rx.buf_sz, - PCI_DMA_FROMDEVICE); + dma_unmap_single(vptr->dev, rd_info->skb_dma, vptr->rx.buf_sz, + DMA_FROM_DEVICE); rd_info->skb_dma = 0; dev_kfree_skb(rd_info->skb); @@ -1670,7 +1670,7 @@ static void velocity_free_dma_rings(struct velocity_info *vptr) const int size = vptr->options.numrx * sizeof(struct rx_desc) + vptr->options.numtx * sizeof(struct tx_desc) * vptr->tx.numq; - pci_free_consistent(vptr->pdev, size, vptr->rx.ring, vptr->rx.pool_dma); + dma_free_coherent(vptr->dev, size, vptr->rx.ring, vptr->rx.pool_dma); } static int velocity_init_rings(struct velocity_info *vptr, int mtu) @@ -1727,8 +1727,8 @@ static void velocity_free_tx_buf(struct velocity_info *vptr, pktlen = max_t(size_t, pktlen, td->td_buf[i].size & ~TD_QUEUE); - pci_unmap_single(vptr->pdev, tdinfo->skb_dma[i], - le16_to_cpu(pktlen), PCI_DMA_TODEVICE); + dma_unmap_single(vptr->dev, tdinfo->skb_dma[i], + le16_to_cpu(pktlen), DMA_TO_DEVICE); } } dev_kfree_skb_irq(skb); @@ -1750,8 +1750,8 @@ static void velocity_free_td_ring_entry(struct velocity_info *vptr, if (td_info->skb) { for (i = 0; i < td_info->nskb_dma; i++) { if (td_info->skb_dma[i]) { - pci_unmap_single(vptr->pdev, td_info->skb_dma[i], - td_info->skb->len, PCI_DMA_TODEVICE); + dma_unmap_single(vptr->dev, td_info->skb_dma[i], + td_info->skb->len, DMA_TO_DEVICE); td_info->skb_dma[i] = 0; } } @@ -2029,7 +2029,6 @@ static inline void velocity_iph_realign(struct velocity_info *vptr, */ static int velocity_receive_frame(struct velocity_info *vptr, int idx) { - void (*pci_action)(struct pci_dev *, dma_addr_t, size_t, int); struct net_device_stats *stats = &vptr->netdev->stats; struct velocity_rd_info *rd_info = &(vptr->rx.info[idx]); struct rx_desc *rd = &(vptr->rx.ring[idx]); @@ -2047,8 +2046,8 @@ static int velocity_receive_frame(struct velocity_info *vptr, int idx) skb = rd_info->skb; - pci_dma_sync_single_for_cpu(vptr->pdev, rd_info->skb_dma, - vptr->rx.buf_sz, PCI_DMA_FROMDEVICE); + dma_sync_single_for_cpu(vptr->dev, rd_info->skb_dma, + vptr->rx.buf_sz, DMA_FROM_DEVICE); /* * Drop frame not meeting IEEE 802.3 @@ -2061,19 +2060,18 @@ static int velocity_receive_frame(struct velocity_info *vptr, int idx) } } - pci_action = pci_dma_sync_single_for_device; - velocity_rx_csum(rd, skb); if (velocity_rx_copy(&skb, pkt_len, vptr) < 0) { velocity_iph_realign(vptr, skb, pkt_len); - pci_action = pci_unmap_single; rd_info->skb = NULL; + dma_unmap_single(vptr->dev, rd_info->skb_dma, vptr->rx.buf_sz, + DMA_FROM_DEVICE); + } else { + dma_sync_single_for_device(vptr->dev, rd_info->skb_dma, + vptr->rx.buf_sz, DMA_FROM_DEVICE); } - pci_action(vptr->pdev, rd_info->skb_dma, vptr->rx.buf_sz, - PCI_DMA_FROMDEVICE); - skb_put(skb, pkt_len - 4); skb->protocol = eth_type_trans(skb, vptr->netdev); @@ -2550,7 +2548,8 @@ static netdev_tx_t velocity_xmit(struct sk_buff *skb, * add it to the transmit ring. */ tdinfo->skb = skb; - tdinfo->skb_dma[0] = pci_map_single(vptr->pdev, skb->data, pktlen, PCI_DMA_TODEVICE); + tdinfo->skb_dma[0] = dma_map_single(vptr->dev, skb->data, pktlen, + DMA_TO_DEVICE); td_ptr->tdesc0.len = cpu_to_le16(pktlen); td_ptr->td_buf[0].pa_low = cpu_to_le32(tdinfo->skb_dma[0]); td_ptr->td_buf[0].pa_high = 0; @@ -2560,7 +2559,7 @@ static netdev_tx_t velocity_xmit(struct sk_buff *skb, for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; - tdinfo->skb_dma[i + 1] = skb_frag_dma_map(&vptr->pdev->dev, + tdinfo->skb_dma[i + 1] = skb_frag_dma_map(vptr->dev, frag, 0, skb_frag_size(frag), DMA_TO_DEVICE); @@ -2637,6 +2636,7 @@ static void velocity_init_info(struct pci_dev *pdev, struct velocity_info *vptr, { memset(vptr, 0, sizeof(struct velocity_info)); + vptr->dev = &pdev->dev; vptr->pdev = pdev; vptr->chip_id = info->chip_id; vptr->tx.numq = info->txqueue; @@ -2744,7 +2744,6 @@ static int velocity_found1(struct pci_dev *pdev, SET_NETDEV_DEV(dev, &pdev->dev); vptr = netdev_priv(dev); - if (first) { printk(KERN_INFO "%s Ver. %s\n", VELOCITY_FULL_DRV_NAM, VELOCITY_VERSION); diff --git a/drivers/net/ethernet/via/via-velocity.h b/drivers/net/ethernet/via/via-velocity.h index ff8d7828aa1b..c38bbaed4d12 100644 --- a/drivers/net/ethernet/via/via-velocity.h +++ b/drivers/net/ethernet/via/via-velocity.h @@ -1434,6 +1434,7 @@ struct velocity_opt { #define GET_RD_BY_IDX(vptr, idx) (vptr->rd_ring[idx]) struct velocity_info { + struct device *dev; struct pci_dev *pdev; struct net_device *netdev; -- cgit v1.2.3 From 6dffbe53fbdcc7bd5f6379fa18264f060b0cf61d Mon Sep 17 00:00:00 2001 From: Tony Prisk Date: Sat, 18 May 2013 09:39:07 +0000 Subject: net: velocity: Add platform device support to VIA velocity driver Add support for the VIA Velocity network driver to be bound to a OF created platform device. Signed-off-by: Tony Prisk Signed-off-by: David S. Miller --- .../devicetree/bindings/net/via-velocity.txt | 20 + drivers/net/ethernet/via/Kconfig | 3 +- drivers/net/ethernet/via/via-velocity.c | 408 ++++++++++++++------- drivers/net/ethernet/via/via-velocity.h | 3 +- 4 files changed, 305 insertions(+), 129 deletions(-) create mode 100644 Documentation/devicetree/bindings/net/via-velocity.txt diff --git a/Documentation/devicetree/bindings/net/via-velocity.txt b/Documentation/devicetree/bindings/net/via-velocity.txt new file mode 100644 index 000000000000..b3db469b1ad7 --- /dev/null +++ b/Documentation/devicetree/bindings/net/via-velocity.txt @@ -0,0 +1,20 @@ +* VIA Velocity 10/100/1000 Network Controller + +Required properties: +- compatible : Should be "via,velocity-vt6110" +- reg : Address and length of the io space +- interrupts : Should contain the controller interrupt line + +Optional properties: +- no-eeprom : PCI network cards use an external EEPROM to store data. Embedded + devices quite often set this data in uboot and do not provide an eeprom. + Specify this option if you have no external eeprom. + +Examples: + +eth0@d8004000 { + compatible = "via,velocity-vt6110"; + reg = <0xd8004000 0x400>; + interrupts = <10>; + no-eeprom; +}; diff --git a/drivers/net/ethernet/via/Kconfig b/drivers/net/ethernet/via/Kconfig index 68a9ba66feba..6a87097d88c0 100644 --- a/drivers/net/ethernet/via/Kconfig +++ b/drivers/net/ethernet/via/Kconfig @@ -5,7 +5,6 @@ config NET_VENDOR_VIA bool "VIA devices" default y - depends on PCI ---help--- If you have a network (Ethernet) card belonging to this class, say Y and read the Ethernet-HOWTO, available from @@ -45,7 +44,7 @@ config VIA_RHINE_MMIO config VIA_VELOCITY tristate "VIA Velocity support" - depends on PCI + depends on (PCI || USE_OF) select CRC32 select CRC_CCITT select NET_CORE diff --git a/drivers/net/ethernet/via/via-velocity.c b/drivers/net/ethernet/via/via-velocity.c index 5996cee0ffa7..76919948b4ee 100644 --- a/drivers/net/ethernet/via/via-velocity.c +++ b/drivers/net/ethernet/via/via-velocity.c @@ -65,7 +65,11 @@ #include #include #include +#include +#include +#include #include +#include #include #include #include @@ -80,10 +84,24 @@ #include "via-velocity.h" +enum velocity_bus_type { + BUS_PCI, + BUS_PLATFORM, +}; static int velocity_nics; static int msglevel = MSG_LEVEL_INFO; +static void velocity_set_power_state(struct velocity_info *vptr, char state) +{ + void *addr = vptr->mac_regs; + + if (vptr->pdev) + pci_set_power_state(vptr->pdev, state); + else + writeb(state, addr + 0x154); +} + /** * mac_get_cam_mask - Read a CAM mask * @regs: register block for this velocity @@ -362,12 +380,23 @@ static struct velocity_info_tbl chip_info_table[] = { * Describe the PCI device identifiers that we support in this * device driver. Used for hotplug autoloading. */ -static DEFINE_PCI_DEVICE_TABLE(velocity_id_table) = { + +static DEFINE_PCI_DEVICE_TABLE(velocity_pci_id_table) = { { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_612X) }, { } }; -MODULE_DEVICE_TABLE(pci, velocity_id_table); +MODULE_DEVICE_TABLE(pci, velocity_pci_id_table); + +/** + * Describe the OF device identifiers that we support in this + * device driver. Used for devicetree nodes. + */ +static struct of_device_id velocity_of_ids[] = { + { .compatible = "via,velocity-vt6110", .data = &chip_info_table[0] }, + { /* Sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, velocity_of_ids); /** * get_chip_name - identifier to name @@ -385,29 +414,6 @@ static const char *get_chip_name(enum chip_type chip_id) return chip_info_table[i].name; } -/** - * velocity_remove1 - device unplug - * @pdev: PCI device being removed - * - * Device unload callback. Called on an unplug or on module - * unload for each active device that is present. Disconnects - * the device from the network layer and frees all the resources - */ -static void velocity_remove1(struct pci_dev *pdev) -{ - struct net_device *dev = pci_get_drvdata(pdev); - struct velocity_info *vptr = netdev_priv(dev); - - unregister_netdev(dev); - iounmap(vptr->mac_regs); - pci_release_regions(pdev); - pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); - free_netdev(dev); - - velocity_nics--; -} - /** * velocity_set_int_opt - parser for integer options * @opt: pointer to option value @@ -1181,6 +1187,17 @@ static void mii_init(struct velocity_info *vptr, u32 mii_status) u16 BMCR; switch (PHYID_GET_PHY_ID(vptr->phy_id)) { + case PHYID_ICPLUS_IP101A: + MII_REG_BITS_ON((ADVERTISE_PAUSE_ASYM | ADVERTISE_PAUSE_CAP), + MII_ADVERTISE, vptr->mac_regs); + if (vptr->mii_status & VELOCITY_DUPLEX_FULL) + MII_REG_BITS_ON(TCSR_ECHODIS, MII_SREVISION, + vptr->mac_regs); + else + MII_REG_BITS_OFF(TCSR_ECHODIS, MII_SREVISION, + vptr->mac_regs); + MII_REG_BITS_ON(PLED_LALBE, MII_TPISTATUS, vptr->mac_regs); + break; case PHYID_CICADA_CS8201: /* * Reset to hardware default @@ -1312,6 +1329,7 @@ static void velocity_init_registers(struct velocity_info *vptr, enum velocity_init_type type) { struct mac_regs __iomem *regs = vptr->mac_regs; + struct net_device *netdev = vptr->netdev; int i, mii_status; mac_wol_reset(regs); @@ -1320,7 +1338,7 @@ static void velocity_init_registers(struct velocity_info *vptr, case VELOCITY_INIT_RESET: case VELOCITY_INIT_WOL: - netif_stop_queue(vptr->netdev); + netif_stop_queue(netdev); /* * Reset RX to prevent RX pointer not on the 4X location @@ -1333,7 +1351,7 @@ static void velocity_init_registers(struct velocity_info *vptr, if (velocity_set_media_mode(vptr, mii_status) != VELOCITY_LINK_CHANGE) { velocity_print_link_status(vptr); if (!(vptr->mii_status & VELOCITY_LINK_FAIL)) - netif_wake_queue(vptr->netdev); + netif_wake_queue(netdev); } enable_flow_control_ability(vptr); @@ -1353,9 +1371,11 @@ static void velocity_init_registers(struct velocity_info *vptr, velocity_soft_reset(vptr); mdelay(5); - mac_eeprom_reload(regs); - for (i = 0; i < 6; i++) - writeb(vptr->netdev->dev_addr[i], &(regs->PAR[i])); + if (!vptr->no_eeprom) { + mac_eeprom_reload(regs); + for (i = 0; i < 6; i++) + writeb(netdev->dev_addr[i], regs->PAR + i); + } /* * clear Pre_ACPI bit. @@ -1378,7 +1398,7 @@ static void velocity_init_registers(struct velocity_info *vptr, /* * Set packet filter: Receive directed and broadcast address */ - velocity_set_multi(vptr->netdev); + velocity_set_multi(netdev); /* * Enable MII auto-polling @@ -1405,14 +1425,14 @@ static void velocity_init_registers(struct velocity_info *vptr, writel((CR0_DPOLL | CR0_TXON | CR0_RXON | CR0_STRT), ®s->CR0Set); mii_status = velocity_get_opt_media_mode(vptr); - netif_stop_queue(vptr->netdev); + netif_stop_queue(netdev); mii_init(vptr, mii_status); if (velocity_set_media_mode(vptr, mii_status) != VELOCITY_LINK_CHANGE) { velocity_print_link_status(vptr); if (!(vptr->mii_status & VELOCITY_LINK_FAIL)) - netif_wake_queue(vptr->netdev); + netif_wake_queue(netdev); } enable_flow_control_ability(vptr); @@ -2233,15 +2253,15 @@ static int velocity_open(struct net_device *dev) goto out; /* Ensure chip is running */ - pci_set_power_state(vptr->pdev, PCI_D0); + velocity_set_power_state(vptr, PCI_D0); velocity_init_registers(vptr, VELOCITY_INIT_COLD); - ret = request_irq(vptr->pdev->irq, velocity_intr, IRQF_SHARED, + ret = request_irq(dev->irq, velocity_intr, IRQF_SHARED, dev->name, dev); if (ret < 0) { /* Power down the chip */ - pci_set_power_state(vptr->pdev, PCI_D3hot); + velocity_set_power_state(vptr, PCI_D3hot); velocity_free_rings(vptr); goto out; } @@ -2314,6 +2334,7 @@ static int velocity_change_mtu(struct net_device *dev, int new_mtu) tmp_vptr->netdev = dev; tmp_vptr->pdev = vptr->pdev; + tmp_vptr->dev = vptr->dev; tmp_vptr->options = vptr->options; tmp_vptr->tx.numq = vptr->tx.numq; @@ -2413,7 +2434,7 @@ static int velocity_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) saving then we need to bring the device back up to talk to it */ if (!netif_running(dev)) - pci_set_power_state(vptr->pdev, PCI_D0); + velocity_set_power_state(vptr, PCI_D0); switch (cmd) { case SIOCGMIIPHY: /* Get address of MII PHY in use. */ @@ -2426,7 +2447,7 @@ static int velocity_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) ret = -EOPNOTSUPP; } if (!netif_running(dev)) - pci_set_power_state(vptr->pdev, PCI_D3hot); + velocity_set_power_state(vptr, PCI_D3hot); return ret; @@ -2492,7 +2513,7 @@ static int velocity_close(struct net_device *dev) if (vptr->flags & VELOCITY_FLAGS_WOL_ENABLED) velocity_get_ip(vptr); - free_irq(vptr->pdev->irq, dev); + free_irq(dev->irq, dev); velocity_free_rings(vptr); @@ -2631,13 +2652,9 @@ static const struct net_device_ops velocity_netdev_ops = { * Set up the initial velocity_info struct for the device that has been * discovered. */ -static void velocity_init_info(struct pci_dev *pdev, struct velocity_info *vptr, - const struct velocity_info_tbl *info) +static void velocity_init_info(struct velocity_info *vptr, + const struct velocity_info_tbl *info) { - memset(vptr, 0, sizeof(struct velocity_info)); - - vptr->dev = &pdev->dev; - vptr->pdev = pdev; vptr->chip_id = info->chip_id; vptr->tx.numq = info->txqueue; vptr->multicast_limit = MCAM_SIZE; @@ -2652,10 +2669,9 @@ static void velocity_init_info(struct pci_dev *pdev, struct velocity_info *vptr, * Retrieve the PCI configuration space data that interests us from * the kernel PCI layer */ -static int velocity_get_pci_info(struct velocity_info *vptr, - struct pci_dev *pdev) +static int velocity_get_pci_info(struct velocity_info *vptr) { - vptr->rev_id = pdev->revision; + struct pci_dev *pdev = vptr->pdev; pci_set_master(pdev); @@ -2678,7 +2694,37 @@ static int velocity_get_pci_info(struct velocity_info *vptr, dev_err(&pdev->dev, "region #1 is too small.\n"); return -EINVAL; } - vptr->pdev = pdev; + + return 0; +} + +/** + * velocity_get_platform_info - retrieve platform info for device + * @vptr: velocity device + * @pdev: platform device it matches + * + * Retrieve the Platform configuration data that interests us + */ +static int velocity_get_platform_info(struct velocity_info *vptr) +{ + struct resource res; + int ret; + + if (of_get_property(vptr->dev->of_node, "no-eeprom", NULL)) + vptr->no_eeprom = 1; + + ret = of_address_to_resource(vptr->dev->of_node, 0, &res); + if (ret) { + dev_err(vptr->dev, "unable to find memory address\n"); + return ret; + } + + vptr->memaddr = res.start; + + if (resource_size(&res) < VELOCITY_IO_SIZE) { + dev_err(vptr->dev, "memory region is too small.\n"); + return -EINVAL; + } return 0; } @@ -2707,21 +2753,22 @@ static u32 velocity_get_link(struct net_device *dev) } /** - * velocity_found1 - set up discovered velocity card + * velocity_probe - set up discovered velocity device * @pdev: PCI device * @ent: PCI device table entry that matched + * @bustype: bus that device is connected to * * Configure a discovered adapter from scratch. Return a negative * errno error code on failure paths. */ -static int velocity_found1(struct pci_dev *pdev, - const struct pci_device_id *ent) +static int velocity_probe(struct device *dev, int irq, + const struct velocity_info_tbl *info, + enum velocity_bus_type bustype) { static int first = 1; - struct net_device *dev; + struct net_device *netdev; int i; const char *drv_string; - const struct velocity_info_tbl *info = &chip_info_table[ent->driver_data]; struct velocity_info *vptr; struct mac_regs __iomem *regs; int ret = -ENOMEM; @@ -2730,19 +2777,18 @@ static int velocity_found1(struct pci_dev *pdev, * can support more than MAX_UNITS. */ if (velocity_nics >= MAX_UNITS) { - dev_notice(&pdev->dev, "already found %d NICs.\n", - velocity_nics); + dev_notice(dev, "already found %d NICs.\n", velocity_nics); return -ENODEV; } - dev = alloc_etherdev(sizeof(struct velocity_info)); - if (!dev) + netdev = alloc_etherdev(sizeof(struct velocity_info)); + if (!netdev) goto out; /* Chain it all together */ - SET_NETDEV_DEV(dev, &pdev->dev); - vptr = netdev_priv(dev); + SET_NETDEV_DEV(netdev, dev); + vptr = netdev_priv(netdev); if (first) { printk(KERN_INFO "%s Ver. %s\n", @@ -2752,41 +2798,41 @@ static int velocity_found1(struct pci_dev *pdev, first = 0; } - velocity_init_info(pdev, vptr, info); + netdev->irq = irq; + vptr->netdev = netdev; + vptr->dev = dev; - vptr->netdev = dev; + velocity_init_info(vptr, info); - ret = pci_enable_device(pdev); - if (ret < 0) - goto err_free_dev; + if (bustype == BUS_PCI) { + vptr->pdev = to_pci_dev(dev); - ret = velocity_get_pci_info(vptr, pdev); - if (ret < 0) { - /* error message already printed */ - goto err_disable; - } - - ret = pci_request_regions(pdev, VELOCITY_NAME); - if (ret < 0) { - dev_err(&pdev->dev, "No PCI resources.\n"); - goto err_disable; + ret = velocity_get_pci_info(vptr); + if (ret < 0) + goto err_free_dev; + } else { + vptr->pdev = NULL; + ret = velocity_get_platform_info(vptr); + if (ret < 0) + goto err_free_dev; } regs = ioremap(vptr->memaddr, VELOCITY_IO_SIZE); if (regs == NULL) { ret = -EIO; - goto err_release_res; + goto err_free_dev; } vptr->mac_regs = regs; + vptr->rev_id = readb(®s->rev_id); mac_wol_reset(regs); for (i = 0; i < 6; i++) - dev->dev_addr[i] = readb(®s->PAR[i]); + netdev->dev_addr[i] = readb(®s->PAR[i]); - drv_string = dev_driver_string(&pdev->dev); + drv_string = dev_driver_string(dev); velocity_get_options(&vptr->options, velocity_nics, drv_string); @@ -2807,46 +2853,125 @@ static int velocity_found1(struct pci_dev *pdev, vptr->phy_id = MII_GET_PHY_ID(vptr->mac_regs); - dev->netdev_ops = &velocity_netdev_ops; - dev->ethtool_ops = &velocity_ethtool_ops; - netif_napi_add(dev, &vptr->napi, velocity_poll, VELOCITY_NAPI_WEIGHT); + netdev->netdev_ops = &velocity_netdev_ops; + netdev->ethtool_ops = &velocity_ethtool_ops; + netif_napi_add(netdev, &vptr->napi, velocity_poll, + VELOCITY_NAPI_WEIGHT); - dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_SG | + netdev->hw_features = NETIF_F_IP_CSUM | NETIF_F_SG | NETIF_F_HW_VLAN_CTAG_TX; - dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_FILTER | - NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_IP_CSUM; + netdev->features |= NETIF_F_HW_VLAN_CTAG_TX | + NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_VLAN_CTAG_RX | + NETIF_F_IP_CSUM; - ret = register_netdev(dev); + ret = register_netdev(netdev); if (ret < 0) goto err_iounmap; - if (!velocity_get_link(dev)) { - netif_carrier_off(dev); + if (!velocity_get_link(netdev)) { + netif_carrier_off(netdev); vptr->mii_status |= VELOCITY_LINK_FAIL; } velocity_print_info(vptr); - pci_set_drvdata(pdev, dev); + dev_set_drvdata(vptr->dev, netdev); /* and leave the chip powered down */ - pci_set_power_state(pdev, PCI_D3hot); + velocity_set_power_state(vptr, PCI_D3hot); velocity_nics++; out: return ret; err_iounmap: iounmap(regs); -err_release_res: - pci_release_regions(pdev); -err_disable: - pci_disable_device(pdev); err_free_dev: - free_netdev(dev); + free_netdev(netdev); goto out; } -#ifdef CONFIG_PM +/** + * velocity_remove - device unplug + * @dev: device being removed + * + * Device unload callback. Called on an unplug or on module + * unload for each active device that is present. Disconnects + * the device from the network layer and frees all the resources + */ +static int velocity_remove(struct device *dev) +{ + struct net_device *netdev = dev_get_drvdata(dev); + struct velocity_info *vptr = netdev_priv(netdev); + + unregister_netdev(netdev); + iounmap(vptr->mac_regs); + free_netdev(netdev); + velocity_nics--; + + return 0; +} + +static int velocity_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + const struct velocity_info_tbl *info = + &chip_info_table[ent->driver_data]; + int ret; + + ret = pci_enable_device(pdev); + if (ret < 0) + return ret; + + ret = pci_request_regions(pdev, VELOCITY_NAME); + if (ret < 0) { + dev_err(&pdev->dev, "No PCI resources.\n"); + goto fail1; + } + + ret = velocity_probe(&pdev->dev, pdev->irq, info, BUS_PCI); + if (ret == 0) + return 0; + + pci_release_regions(pdev); +fail1: + pci_disable_device(pdev); + return ret; +} + +static void velocity_pci_remove(struct pci_dev *pdev) +{ + velocity_remove(&pdev->dev); + + pci_release_regions(pdev); + pci_disable_device(pdev); +} + +static int velocity_platform_probe(struct platform_device *pdev) +{ + const struct of_device_id *of_id; + const struct velocity_info_tbl *info; + int irq; + + of_id = of_match_device(velocity_of_ids, &pdev->dev); + if (!of_id) + return -EINVAL; + info = of_id->data; + + irq = irq_of_parse_and_map(pdev->dev.of_node, 0); + if (!irq) + return -EINVAL; + + return velocity_probe(&pdev->dev, irq, info, BUS_PLATFORM); +} + +static int velocity_platform_remove(struct platform_device *pdev) +{ + velocity_remove(&pdev->dev); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP /** * wol_calc_crc - WOL CRC * @pattern: data pattern @@ -3003,10 +3128,10 @@ static void velocity_save_context(struct velocity_info *vptr, struct velocity_co } -static int velocity_suspend(struct pci_dev *pdev, pm_message_t state) +static int velocity_suspend(struct device *dev) { - struct net_device *dev = pci_get_drvdata(pdev); - struct velocity_info *vptr = netdev_priv(dev); + struct net_device *netdev = dev_get_drvdata(dev); + struct velocity_info *vptr = netdev_priv(netdev); unsigned long flags; if (!netif_running(vptr->netdev)) @@ -3015,20 +3140,23 @@ static int velocity_suspend(struct pci_dev *pdev, pm_message_t state) netif_device_detach(vptr->netdev); spin_lock_irqsave(&vptr->lock, flags); - pci_save_state(pdev); + if (vptr->pdev) + pci_save_state(vptr->pdev); if (vptr->flags & VELOCITY_FLAGS_WOL_ENABLED) { velocity_get_ip(vptr); velocity_save_context(vptr, &vptr->context); velocity_shutdown(vptr); velocity_set_wol(vptr); - pci_enable_wake(pdev, PCI_D3hot, 1); - pci_set_power_state(pdev, PCI_D3hot); + if (vptr->pdev) + pci_enable_wake(vptr->pdev, PCI_D3hot, 1); + velocity_set_power_state(vptr, PCI_D3hot); } else { velocity_save_context(vptr, &vptr->context); velocity_shutdown(vptr); - pci_disable_device(pdev); - pci_set_power_state(pdev, pci_choose_state(pdev, state)); + if (vptr->pdev) + pci_disable_device(vptr->pdev); + velocity_set_power_state(vptr, PCI_D3hot); } spin_unlock_irqrestore(&vptr->lock, flags); @@ -3070,19 +3198,22 @@ static void velocity_restore_context(struct velocity_info *vptr, struct velocity writeb(*((u8 *) (context->mac_reg + i)), ptr + i); } -static int velocity_resume(struct pci_dev *pdev) +static int velocity_resume(struct device *dev) { - struct net_device *dev = pci_get_drvdata(pdev); - struct velocity_info *vptr = netdev_priv(dev); + struct net_device *netdev = dev_get_drvdata(dev); + struct velocity_info *vptr = netdev_priv(netdev); unsigned long flags; int i; if (!netif_running(vptr->netdev)) return 0; - pci_set_power_state(pdev, PCI_D0); - pci_enable_wake(pdev, 0, 0); - pci_restore_state(pdev); + velocity_set_power_state(vptr, PCI_D0); + + if (vptr->pdev) { + pci_enable_wake(vptr->pdev, 0, 0); + pci_restore_state(vptr->pdev); + } mac_wol_reset(vptr->mac_regs); @@ -3104,23 +3235,34 @@ static int velocity_resume(struct pci_dev *pdev) return 0; } -#endif +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(velocity_pm_ops, velocity_suspend, velocity_resume); /* * Definition for our device driver. The PCI layer interface * uses this to handle all our card discover and plugging */ -static struct pci_driver velocity_driver = { +static struct pci_driver velocity_pci_driver = { .name = VELOCITY_NAME, - .id_table = velocity_id_table, - .probe = velocity_found1, - .remove = velocity_remove1, -#ifdef CONFIG_PM - .suspend = velocity_suspend, - .resume = velocity_resume, -#endif + .id_table = velocity_pci_id_table, + .probe = velocity_pci_probe, + .remove = velocity_pci_remove, + .driver = { + .pm = &velocity_pm_ops, + }, }; +static struct platform_driver velocity_platform_driver = { + .probe = velocity_platform_probe, + .remove = velocity_platform_remove, + .driver = { + .name = "via-velocity", + .owner = THIS_MODULE, + .of_match_table = velocity_of_ids, + .pm = &velocity_pm_ops, + }, +}; /** * velocity_ethtool_up - pre hook for ethtool @@ -3133,7 +3275,7 @@ static int velocity_ethtool_up(struct net_device *dev) { struct velocity_info *vptr = netdev_priv(dev); if (!netif_running(dev)) - pci_set_power_state(vptr->pdev, PCI_D0); + velocity_set_power_state(vptr, PCI_D0); return 0; } @@ -3148,7 +3290,7 @@ static void velocity_ethtool_down(struct net_device *dev) { struct velocity_info *vptr = netdev_priv(dev); if (!netif_running(dev)) - pci_set_power_state(vptr->pdev, PCI_D3hot); + velocity_set_power_state(vptr, PCI_D3hot); } static int velocity_get_settings(struct net_device *dev, @@ -3268,9 +3410,14 @@ static int velocity_set_settings(struct net_device *dev, static void velocity_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct velocity_info *vptr = netdev_priv(dev); + strlcpy(info->driver, VELOCITY_NAME, sizeof(info->driver)); strlcpy(info->version, VELOCITY_VERSION, sizeof(info->version)); - strlcpy(info->bus_info, pci_name(vptr->pdev), sizeof(info->bus_info)); + if (vptr->pdev) + strlcpy(info->bus_info, pci_name(vptr->pdev), + sizeof(info->bus_info)); + else + strlcpy(info->bus_info, "platform", sizeof(info->bus_info)); } static void velocity_ethtool_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) @@ -3560,13 +3707,20 @@ static void velocity_unregister_notifier(void) */ static int __init velocity_init_module(void) { - int ret; + int ret_pci, ret_platform; velocity_register_notifier(); - ret = pci_register_driver(&velocity_driver); - if (ret < 0) + + ret_pci = pci_register_driver(&velocity_pci_driver); + ret_platform = platform_driver_register(&velocity_platform_driver); + + /* if both_registers failed, remove the notifier */ + if ((ret_pci < 0) && (ret_platform < 0)) { velocity_unregister_notifier(); - return ret; + return ret_pci; + } + + return 0; } /** @@ -3580,7 +3734,9 @@ static int __init velocity_init_module(void) static void __exit velocity_cleanup_module(void) { velocity_unregister_notifier(); - pci_unregister_driver(&velocity_driver); + + pci_unregister_driver(&velocity_pci_driver); + platform_driver_unregister(&velocity_platform_driver); } module_init(velocity_init_module); diff --git a/drivers/net/ethernet/via/via-velocity.h b/drivers/net/ethernet/via/via-velocity.h index c38bbaed4d12..9453bfa9324a 100644 --- a/drivers/net/ethernet/via/via-velocity.h +++ b/drivers/net/ethernet/via/via-velocity.h @@ -1265,7 +1265,7 @@ struct velocity_context { #define PHYID_VT3216_64BIT 0x000FC600UL #define PHYID_MARVELL_1000 0x01410C50UL #define PHYID_MARVELL_1000S 0x01410C40UL - +#define PHYID_ICPLUS_IP101A 0x02430C54UL #define PHYID_REV_ID_MASK 0x0000000FUL #define PHYID_GET_PHY_ID(i) ((i) & ~PHYID_REV_ID_MASK) @@ -1437,6 +1437,7 @@ struct velocity_info { struct device *dev; struct pci_dev *pdev; struct net_device *netdev; + int no_eeprom; unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; u8 ip_addr[4]; -- cgit v1.2.3 From 1ca2983aaa01233dc1ea63ef1b483678fcece6c8 Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Mon, 20 May 2013 01:05:12 +0000 Subject: xen-netfront: avoid leaking resources when setup_netfront fails We should correctly free related resources (grant ref, memory page, evtchn) when setup_netfront fails. Signed-off-by: Wei Liu Signed-off-by: David S. Miller --- drivers/net/xen-netfront.c | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index 1db101415069..5770e3b0ba73 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -1532,40 +1532,49 @@ static int setup_netfront(struct xenbus_device *dev, struct netfront_info *info) FRONT_RING_INIT(&info->tx, txs, PAGE_SIZE); err = xenbus_grant_ring(dev, virt_to_mfn(txs)); - if (err < 0) { - free_page((unsigned long)txs); - goto fail; - } + if (err < 0) + goto grant_tx_ring_fail; info->tx_ring_ref = err; rxs = (struct xen_netif_rx_sring *)get_zeroed_page(GFP_NOIO | __GFP_HIGH); if (!rxs) { err = -ENOMEM; xenbus_dev_fatal(dev, err, "allocating rx ring page"); - goto fail; + goto alloc_rx_ring_fail; } SHARED_RING_INIT(rxs); FRONT_RING_INIT(&info->rx, rxs, PAGE_SIZE); err = xenbus_grant_ring(dev, virt_to_mfn(rxs)); - if (err < 0) { - free_page((unsigned long)rxs); - goto fail; - } + if (err < 0) + goto grant_rx_ring_fail; info->rx_ring_ref = err; err = xenbus_alloc_evtchn(dev, &info->evtchn); if (err) - goto fail; + goto alloc_evtchn_fail; err = bind_evtchn_to_irqhandler(info->evtchn, xennet_interrupt, 0, netdev->name, netdev); if (err < 0) - goto fail; + goto bind_fail; netdev->irq = err; return 0; - fail: + /* If we fail to setup netfront, it is safe to just revoke access to + * granted pages because backend is not accessing it at this point. + */ +bind_fail: + xenbus_free_evtchn(dev, info->evtchn); +alloc_evtchn_fail: + gnttab_end_foreign_access_ref(info->rx_ring_ref, 0); +grant_rx_ring_fail: + free_page((unsigned long)rxs); +alloc_rx_ring_fail: + gnttab_end_foreign_access_ref(info->tx_ring_ref, 0); +grant_tx_ring_fail: + free_page((unsigned long)txs); +fail: return err; } -- cgit v1.2.3 From 4a5bddf7ea6b6c5916eccbc2fa1950555073ff48 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 20 May 2013 03:06:17 +0000 Subject: fec: Let device core handle pinctrl Since commit ab78029 (drivers/pinctrl: grab default handles from device core) we can rely on device core for handling pinctrl, so remove devm_pinctrl_get_select_default() from the driver. Signed-off-by: Fabio Estevam Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fec_main.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index ca9825ca88c9..2f4b3e028012 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -53,7 +53,6 @@ #include #include #include -#include #include #include @@ -1841,7 +1840,6 @@ fec_probe(struct platform_device *pdev) struct resource *r; const struct of_device_id *of_id; static int dev_id; - struct pinctrl *pinctrl; struct regulator *reg_phy; of_id = of_match_device(fec_dt_ids, &pdev->dev); @@ -1891,12 +1889,6 @@ fec_probe(struct platform_device *pdev) fep->phy_interface = ret; } - pinctrl = devm_pinctrl_get_select_default(&pdev->dev); - if (IS_ERR(pinctrl)) { - ret = PTR_ERR(pinctrl); - goto failed_pin; - } - fep->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); if (IS_ERR(fep->clk_ipg)) { ret = PTR_ERR(fep->clk_ipg); @@ -1996,7 +1988,6 @@ failed_regulator: clk_disable_unprepare(fep->clk_ipg); clk_disable_unprepare(fep->clk_enet_out); clk_disable_unprepare(fep->clk_ptp); -failed_pin: failed_clk: failed_ioremap: free_netdev(ndev); -- cgit v1.2.3 From 99bbc70741903c063b3ccad90a3e06fc55df9245 Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Mon, 20 May 2013 04:02:32 +0000 Subject: rps: selective flow shedding during softnet overflow A cpu executing the network receive path sheds packets when its input queue grows to netdev_max_backlog. A single high rate flow (such as a spoofed source DoS) can exceed a single cpu processing rate and will degrade throughput of other flows hashed onto the same cpu. This patch adds a more fine grained hashtable. If the netdev backlog is above a threshold, IRQ cpus track the ratio of total traffic of each flow (using 4096 buckets, configurable). The ratio is measured by counting the number of packets per flow over the last 256 packets from the source cpu. Any flow that occupies a large fraction of this (set at 50%) will see packet drop while above the threshold. Tested: Setup is a muli-threaded UDP echo server with network rx IRQ on cpu0, kernel receive (RPS) on cpu0 and application threads on cpus 2--7 each handling 20k req/s. Throughput halves when hit with a 400 kpps antagonist storm. With this patch applied, antagonist overload is dropped and the server processes its complete load. The patch is effective when kernel receive processing is the bottleneck. The above RPS scenario is a extreme, but the same is reached with RFS and sufficient kernel processing (iptables, packet socket tap, ..). Signed-off-by: Willem de Bruijn Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/netdevice.h | 17 ++++++++ net/Kconfig | 12 ++++++ net/core/dev.c | 48 ++++++++++++++++++++- net/core/net-procfs.c | 16 ++++++- net/core/sysctl_net_core.c | 104 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 194 insertions(+), 3 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index a94a5a0ab122..7dd535d4b41e 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1778,6 +1778,19 @@ static inline int unregister_gifconf(unsigned int family) return register_gifconf(family, NULL); } +#ifdef CONFIG_NET_FLOW_LIMIT +#define FLOW_LIMIT_HISTORY (1 << 8) /* must be ^2 */ +struct sd_flow_limit { + u64 count; + unsigned int num_buckets; + unsigned int history_head; + u16 history[FLOW_LIMIT_HISTORY]; + u8 buckets[]; +}; + +extern int netdev_flow_limit_table_len; +#endif /* CONFIG_NET_FLOW_LIMIT */ + /* * Incoming packets are placed on per-cpu queues */ @@ -1807,6 +1820,10 @@ struct softnet_data { unsigned int dropped; struct sk_buff_head input_pkt_queue; struct napi_struct backlog; + +#ifdef CONFIG_NET_FLOW_LIMIT + struct sd_flow_limit *flow_limit; +#endif }; static inline void input_queue_head_incr(struct softnet_data *sd) diff --git a/net/Kconfig b/net/Kconfig index 2ddc9046868e..08de901415ee 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -259,6 +259,18 @@ config BPF_JIT packet sniffing (libpcap/tcpdump). Note : Admin should enable this feature changing /proc/sys/net/core/bpf_jit_enable +config NET_FLOW_LIMIT + boolean + depends on RPS + default y + ---help--- + The network stack has to drop packets when a receive processing CPU's + backlog reaches netdev_max_backlog. If a few out of many active flows + generate the vast majority of load, drop their traffic earlier to + maintain capacity for the other flows. This feature provides servers + with many clients some protection against DoS by a single (spoofed) + flow that greatly exceeds average workload. + menu "Network testing" config NET_PKTGEN diff --git a/net/core/dev.c b/net/core/dev.c index 18e9730cc4be..7229bc30e509 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3064,6 +3064,46 @@ static int rps_ipi_queued(struct softnet_data *sd) return 0; } +#ifdef CONFIG_NET_FLOW_LIMIT +int netdev_flow_limit_table_len __read_mostly = (1 << 12); +#endif + +static bool skb_flow_limit(struct sk_buff *skb, unsigned int qlen) +{ +#ifdef CONFIG_NET_FLOW_LIMIT + struct sd_flow_limit *fl; + struct softnet_data *sd; + unsigned int old_flow, new_flow; + + if (qlen < (netdev_max_backlog >> 1)) + return false; + + sd = &__get_cpu_var(softnet_data); + + rcu_read_lock(); + fl = rcu_dereference(sd->flow_limit); + if (fl) { + new_flow = skb_get_rxhash(skb) & (fl->num_buckets - 1); + old_flow = fl->history[fl->history_head]; + fl->history[fl->history_head] = new_flow; + + fl->history_head++; + fl->history_head &= FLOW_LIMIT_HISTORY - 1; + + if (likely(fl->buckets[old_flow])) + fl->buckets[old_flow]--; + + if (++fl->buckets[new_flow] > (FLOW_LIMIT_HISTORY >> 1)) { + fl->count++; + rcu_read_unlock(); + return true; + } + } + rcu_read_unlock(); +#endif + return false; +} + /* * enqueue_to_backlog is called to queue an skb to a per CPU backlog * queue (may be a remote CPU queue). @@ -3073,13 +3113,15 @@ static int enqueue_to_backlog(struct sk_buff *skb, int cpu, { struct softnet_data *sd; unsigned long flags; + unsigned int qlen; sd = &per_cpu(softnet_data, cpu); local_irq_save(flags); rps_lock(sd); - if (skb_queue_len(&sd->input_pkt_queue) <= netdev_max_backlog) { + qlen = skb_queue_len(&sd->input_pkt_queue); + if (qlen <= netdev_max_backlog && !skb_flow_limit(skb, qlen)) { if (skb_queue_len(&sd->input_pkt_queue)) { enqueue: __skb_queue_tail(&sd->input_pkt_queue, skb); @@ -6269,6 +6311,10 @@ static int __init net_dev_init(void) sd->backlog.weight = weight_p; sd->backlog.gro_list = NULL; sd->backlog.gro_count = 0; + +#ifdef CONFIG_NET_FLOW_LIMIT + sd->flow_limit = NULL; +#endif } dev_boot_phase = 0; diff --git a/net/core/net-procfs.c b/net/core/net-procfs.c index 569d355fec3e..2bf83299600a 100644 --- a/net/core/net-procfs.c +++ b/net/core/net-procfs.c @@ -146,11 +146,23 @@ static void softnet_seq_stop(struct seq_file *seq, void *v) static int softnet_seq_show(struct seq_file *seq, void *v) { struct softnet_data *sd = v; + unsigned int flow_limit_count = 0; - seq_printf(seq, "%08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n", +#ifdef CONFIG_NET_FLOW_LIMIT + struct sd_flow_limit *fl; + + rcu_read_lock(); + fl = rcu_dereference(sd->flow_limit); + if (fl) + flow_limit_count = fl->count; + rcu_read_unlock(); +#endif + + seq_printf(seq, + "%08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n", sd->processed, sd->dropped, sd->time_squeeze, 0, 0, 0, 0, 0, /* was fastroute */ - sd->cpu_collision, sd->received_rps); + sd->cpu_collision, sd->received_rps, flow_limit_count); return 0; } diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c index cfdb46ab3a7f..741db5fc7806 100644 --- a/net/core/sysctl_net_core.c +++ b/net/core/sysctl_net_core.c @@ -87,6 +87,96 @@ static int rps_sock_flow_sysctl(ctl_table *table, int write, } #endif /* CONFIG_RPS */ +#ifdef CONFIG_NET_FLOW_LIMIT +static DEFINE_MUTEX(flow_limit_update_mutex); + +static int flow_limit_cpu_sysctl(ctl_table *table, int write, + void __user *buffer, size_t *lenp, + loff_t *ppos) +{ + struct sd_flow_limit *cur; + struct softnet_data *sd; + cpumask_var_t mask; + int i, len, ret = 0; + + if (!alloc_cpumask_var(&mask, GFP_KERNEL)) + return -ENOMEM; + + if (write) { + ret = cpumask_parse_user(buffer, *lenp, mask); + if (ret) + goto done; + + mutex_lock(&flow_limit_update_mutex); + len = sizeof(*cur) + netdev_flow_limit_table_len; + for_each_possible_cpu(i) { + sd = &per_cpu(softnet_data, i); + cur = rcu_dereference_protected(sd->flow_limit, + lockdep_is_held(&flow_limit_update_mutex)); + if (cur && !cpumask_test_cpu(i, mask)) { + RCU_INIT_POINTER(sd->flow_limit, NULL); + synchronize_rcu(); + kfree(cur); + } else if (!cur && cpumask_test_cpu(i, mask)) { + cur = kzalloc(len, GFP_KERNEL); + if (!cur) { + /* not unwinding previous changes */ + ret = -ENOMEM; + goto write_unlock; + } + cur->num_buckets = netdev_flow_limit_table_len; + rcu_assign_pointer(sd->flow_limit, cur); + } + } +write_unlock: + mutex_unlock(&flow_limit_update_mutex); + } else { + if (*ppos || !*lenp) { + *lenp = 0; + goto done; + } + + cpumask_clear(mask); + rcu_read_lock(); + for_each_possible_cpu(i) { + sd = &per_cpu(softnet_data, i); + if (rcu_dereference(sd->flow_limit)) + cpumask_set_cpu(i, mask); + } + rcu_read_unlock(); + + len = cpumask_scnprintf(buffer, *lenp, mask); + *lenp = len + 1; + *ppos += len + 1; + } + +done: + free_cpumask_var(mask); + return ret; +} + +static int flow_limit_table_len_sysctl(ctl_table *table, int write, + void __user *buffer, size_t *lenp, + loff_t *ppos) +{ + unsigned int old, *ptr; + int ret; + + mutex_lock(&flow_limit_update_mutex); + + ptr = table->data; + old = *ptr; + ret = proc_dointvec(table, write, buffer, lenp, ppos); + if (!ret && write && !is_power_of_2(*ptr)) { + *ptr = old; + ret = -EINVAL; + } + + mutex_unlock(&flow_limit_update_mutex); + return ret; +} +#endif /* CONFIG_NET_FLOW_LIMIT */ + static struct ctl_table net_core_table[] = { #ifdef CONFIG_NET { @@ -180,6 +270,20 @@ static struct ctl_table net_core_table[] = { .proc_handler = rps_sock_flow_sysctl }, #endif +#ifdef CONFIG_NET_FLOW_LIMIT + { + .procname = "flow_limit_cpu_bitmap", + .mode = 0644, + .proc_handler = flow_limit_cpu_sysctl + }, + { + .procname = "flow_limit_table_len", + .data = &netdev_flow_limit_table_len, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = flow_limit_table_len_sysctl + }, +#endif /* CONFIG_NET_FLOW_LIMIT */ #endif /* CONFIG_NET */ { .procname = "netdev_budget", -- cgit v1.2.3 From 168fc21a971e4bc821a7838ccc39b7bdaf316c11 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Mon, 20 May 2013 04:53:38 +0000 Subject: net: ipv6: remove 'next' member from inet6_dev The next pointer within the inet6_dev structure seems not to be used anywhere. So just remove it. Tested with allmodconfig on x86_64. Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/net/if_inet6.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/net/if_inet6.h b/include/net/if_inet6.h index 0727d0e4af3b..e07feb456d19 100644 --- a/include/net/if_inet6.h +++ b/include/net/if_inet6.h @@ -193,7 +193,6 @@ struct inet6_dev { struct in6_addr token; struct neigh_parms *nd_parms; - struct inet6_dev *next; struct ipv6_devconf cnf; struct ipv6_devstat stats; unsigned long tstamp; /* ipv6InterfaceTable update timestamp */ -- cgit v1.2.3 From 71cea17ed39fdf1c0634f530ddc6a2c2fc601c2b Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 20 May 2013 06:52:26 +0000 Subject: tcp: md5: remove spinlock usage in fast path TCP md5 code uses per cpu variables but protects access to them with a shared spinlock, which is a contention point. [ tcp_md5sig_pool_lock is locked twice per incoming packet ] Makes things much simpler, by allocating crypto structures once, first time a socket needs md5 keys, and not deallocating them as they are really small. Next step would be to allow crypto allocations being done in a NUMA aware way. Signed-off-by: Eric Dumazet Cc: Herbert Xu Signed-off-by: David S. Miller --- include/net/tcp.h | 8 ++-- net/ipv4/tcp.c | 98 +++++++++++------------------------------------- net/ipv4/tcp_ipv4.c | 10 +---- net/ipv4/tcp_minisocks.c | 6 +-- 4 files changed, 29 insertions(+), 93 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index e1c3723f161f..bf1cc3dced5e 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1283,11 +1283,13 @@ static inline struct tcp_md5sig_key *tcp_md5_do_lookup(struct sock *sk, #define tcp_twsk_md5_key(twsk) NULL #endif -extern struct tcp_md5sig_pool __percpu *tcp_alloc_md5sig_pool(struct sock *); -extern void tcp_free_md5sig_pool(void); +extern bool tcp_alloc_md5sig_pool(void); extern struct tcp_md5sig_pool *tcp_get_md5sig_pool(void); -extern void tcp_put_md5sig_pool(void); +static inline void tcp_put_md5sig_pool(void) +{ + local_bh_enable(); +} extern int tcp_md5_hash_header(struct tcp_md5sig_pool *, const struct tcphdr *); extern int tcp_md5_hash_skb_data(struct tcp_md5sig_pool *, const struct sk_buff *, diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index dcb116dde216..53d9c120fbb8 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -3095,9 +3095,8 @@ int tcp_gro_complete(struct sk_buff *skb) EXPORT_SYMBOL(tcp_gro_complete); #ifdef CONFIG_TCP_MD5SIG -static unsigned long tcp_md5sig_users; -static struct tcp_md5sig_pool __percpu *tcp_md5sig_pool; -static DEFINE_SPINLOCK(tcp_md5sig_pool_lock); +static struct tcp_md5sig_pool __percpu *tcp_md5sig_pool __read_mostly; +static DEFINE_MUTEX(tcp_md5sig_mutex); static void __tcp_free_md5sig_pool(struct tcp_md5sig_pool __percpu *pool) { @@ -3112,30 +3111,14 @@ static void __tcp_free_md5sig_pool(struct tcp_md5sig_pool __percpu *pool) free_percpu(pool); } -void tcp_free_md5sig_pool(void) -{ - struct tcp_md5sig_pool __percpu *pool = NULL; - - spin_lock_bh(&tcp_md5sig_pool_lock); - if (--tcp_md5sig_users == 0) { - pool = tcp_md5sig_pool; - tcp_md5sig_pool = NULL; - } - spin_unlock_bh(&tcp_md5sig_pool_lock); - if (pool) - __tcp_free_md5sig_pool(pool); -} -EXPORT_SYMBOL(tcp_free_md5sig_pool); - -static struct tcp_md5sig_pool __percpu * -__tcp_alloc_md5sig_pool(struct sock *sk) +static void __tcp_alloc_md5sig_pool(void) { int cpu; struct tcp_md5sig_pool __percpu *pool; pool = alloc_percpu(struct tcp_md5sig_pool); if (!pool) - return NULL; + return; for_each_possible_cpu(cpu) { struct crypto_hash *hash; @@ -3146,53 +3129,27 @@ __tcp_alloc_md5sig_pool(struct sock *sk) per_cpu_ptr(pool, cpu)->md5_desc.tfm = hash; } - return pool; + /* before setting tcp_md5sig_pool, we must commit all writes + * to memory. See ACCESS_ONCE() in tcp_get_md5sig_pool() + */ + smp_wmb(); + tcp_md5sig_pool = pool; + return; out_free: __tcp_free_md5sig_pool(pool); - return NULL; } -struct tcp_md5sig_pool __percpu *tcp_alloc_md5sig_pool(struct sock *sk) +bool tcp_alloc_md5sig_pool(void) { - struct tcp_md5sig_pool __percpu *pool; - bool alloc = false; - -retry: - spin_lock_bh(&tcp_md5sig_pool_lock); - pool = tcp_md5sig_pool; - if (tcp_md5sig_users++ == 0) { - alloc = true; - spin_unlock_bh(&tcp_md5sig_pool_lock); - } else if (!pool) { - tcp_md5sig_users--; - spin_unlock_bh(&tcp_md5sig_pool_lock); - cpu_relax(); - goto retry; - } else - spin_unlock_bh(&tcp_md5sig_pool_lock); - - if (alloc) { - /* we cannot hold spinlock here because this may sleep. */ - struct tcp_md5sig_pool __percpu *p; - - p = __tcp_alloc_md5sig_pool(sk); - spin_lock_bh(&tcp_md5sig_pool_lock); - if (!p) { - tcp_md5sig_users--; - spin_unlock_bh(&tcp_md5sig_pool_lock); - return NULL; - } - pool = tcp_md5sig_pool; - if (pool) { - /* oops, it has already been assigned. */ - spin_unlock_bh(&tcp_md5sig_pool_lock); - __tcp_free_md5sig_pool(p); - } else { - tcp_md5sig_pool = pool = p; - spin_unlock_bh(&tcp_md5sig_pool_lock); - } + if (unlikely(!tcp_md5sig_pool)) { + mutex_lock(&tcp_md5sig_mutex); + + if (!tcp_md5sig_pool) + __tcp_alloc_md5sig_pool(); + + mutex_unlock(&tcp_md5sig_mutex); } - return pool; + return tcp_md5sig_pool != NULL; } EXPORT_SYMBOL(tcp_alloc_md5sig_pool); @@ -3209,28 +3166,15 @@ struct tcp_md5sig_pool *tcp_get_md5sig_pool(void) struct tcp_md5sig_pool __percpu *p; local_bh_disable(); - - spin_lock(&tcp_md5sig_pool_lock); - p = tcp_md5sig_pool; - if (p) - tcp_md5sig_users++; - spin_unlock(&tcp_md5sig_pool_lock); - + p = ACCESS_ONCE(tcp_md5sig_pool); if (p) - return this_cpu_ptr(p); + return __this_cpu_ptr(p); local_bh_enable(); return NULL; } EXPORT_SYMBOL(tcp_get_md5sig_pool); -void tcp_put_md5sig_pool(void) -{ - local_bh_enable(); - tcp_free_md5sig_pool(); -} -EXPORT_SYMBOL(tcp_put_md5sig_pool); - int tcp_md5_hash_header(struct tcp_md5sig_pool *hp, const struct tcphdr *th) { diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 719652305a29..d20ede0c9593 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1026,7 +1026,7 @@ int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, key = sock_kmalloc(sk, sizeof(*key), gfp); if (!key) return -ENOMEM; - if (hlist_empty(&md5sig->head) && !tcp_alloc_md5sig_pool(sk)) { + if (!tcp_alloc_md5sig_pool()) { sock_kfree_s(sk, key, sizeof(*key)); return -ENOMEM; } @@ -1044,9 +1044,7 @@ EXPORT_SYMBOL(tcp_md5_do_add); int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, int family) { - struct tcp_sock *tp = tcp_sk(sk); struct tcp_md5sig_key *key; - struct tcp_md5sig_info *md5sig; key = tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&addr, AF_INET); if (!key) @@ -1054,10 +1052,6 @@ int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, int family) hlist_del_rcu(&key->node); atomic_sub(sizeof(*key), &sk->sk_omem_alloc); kfree_rcu(key, rcu); - md5sig = rcu_dereference_protected(tp->md5sig_info, - sock_owned_by_user(sk)); - if (hlist_empty(&md5sig->head)) - tcp_free_md5sig_pool(); return 0; } EXPORT_SYMBOL(tcp_md5_do_del); @@ -1071,8 +1065,6 @@ static void tcp_clear_md5_list(struct sock *sk) md5sig = rcu_dereference_protected(tp->md5sig_info, 1); - if (!hlist_empty(&md5sig->head)) - tcp_free_md5sig_pool(); hlist_for_each_entry_safe(key, n, &md5sig->head, node) { hlist_del_rcu(&key->node); atomic_sub(sizeof(*key), &sk->sk_omem_alloc); diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 0f0178827259..ab1c08658528 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -317,7 +317,7 @@ void tcp_time_wait(struct sock *sk, int state, int timeo) key = tp->af_specific->md5_lookup(sk, sk); if (key != NULL) { tcptw->tw_md5_key = kmemdup(key, sizeof(*key), GFP_ATOMIC); - if (tcptw->tw_md5_key && tcp_alloc_md5sig_pool(sk) == NULL) + if (tcptw->tw_md5_key && !tcp_alloc_md5sig_pool()) BUG(); } } while (0); @@ -358,10 +358,8 @@ void tcp_twsk_destructor(struct sock *sk) #ifdef CONFIG_TCP_MD5SIG struct tcp_timewait_sock *twsk = tcp_twsk(sk); - if (twsk->tw_md5_key) { - tcp_free_md5sig_pool(); + if (twsk->tw_md5_key) kfree_rcu(twsk->tw_md5_key, rcu); - } #endif } EXPORT_SYMBOL_GPL(tcp_twsk_destructor); -- cgit v1.2.3 From ed900ffb73e3ab154dff1ae20a2393f24da728df Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Mon, 20 May 2013 08:05:50 +0000 Subject: ppc: bpf_jit: can call module_free() from any context Followup patch on module_free()/vfree() that takes care of the rest, so no longer this workaround with work_struct is needed. Signed-off-by: Daniel Borkmann Cc: Al Viro Cc: Matt Evans Signed-off-by: David S. Miller --- arch/powerpc/net/bpf_jit_comp.c | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c index c427ae36374a..bf56e33f8257 100644 --- a/arch/powerpc/net/bpf_jit_comp.c +++ b/arch/powerpc/net/bpf_jit_comp.c @@ -650,8 +650,7 @@ void bpf_jit_compile(struct sk_filter *fp) proglen = cgctx.idx * 4; alloclen = proglen + FUNCTION_DESCR_SIZE; - image = module_alloc(max_t(unsigned int, alloclen, - sizeof(struct work_struct))); + image = module_alloc(alloclen); if (!image) goto out; @@ -688,20 +687,8 @@ out: return; } -static void jit_free_defer(struct work_struct *arg) -{ - module_free(NULL, arg); -} - -/* run from softirq, we must use a work_struct to call - * module_free() from process context - */ void bpf_jit_free(struct sk_filter *fp) { - if (fp->bpf_func != sk_run_filter) { - struct work_struct *work = (struct work_struct *)fp->bpf_func; - - INIT_WORK(work, jit_free_defer); - schedule_work(work); - } + if (fp->bpf_func != sk_run_filter) + module_free(NULL, fp->bpf_func); } -- cgit v1.2.3 From aafc787e41fd8a4d3a4378b028d8d8f8d38d9bb6 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Mon, 20 May 2013 08:05:51 +0000 Subject: arm: bpf_jit: can call module_free() from any context Follow-up on module_free()/vfree() that takes care of the rest, so no longer this workaround with work_struct needed. Signed-off-by: Daniel Borkmann Cc: Al Viro Cc: Mircea Gherzan Signed-off-by: David S. Miller --- arch/arm/net/bpf_jit_32.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/arch/arm/net/bpf_jit_32.c b/arch/arm/net/bpf_jit_32.c index 1a643ee8e082..f50d223a0bd3 100644 --- a/arch/arm/net/bpf_jit_32.c +++ b/arch/arm/net/bpf_jit_32.c @@ -900,8 +900,7 @@ void bpf_jit_compile(struct sk_filter *fp) #endif alloc_size = 4 * ctx.idx; - ctx.target = module_alloc(max(sizeof(struct work_struct), - alloc_size)); + ctx.target = module_alloc(alloc_size); if (unlikely(ctx.target == NULL)) goto out; @@ -927,19 +926,8 @@ out: return; } -static void bpf_jit_free_worker(struct work_struct *work) -{ - module_free(NULL, work); -} - void bpf_jit_free(struct sk_filter *fp) { - struct work_struct *work; - - if (fp->bpf_func != sk_run_filter) { - work = (struct work_struct *)fp->bpf_func; - - INIT_WORK(work, bpf_jit_free_worker); - schedule_work(work); - } + if (fp->bpf_func != sk_run_filter) + module_free(NULL, fp->bpf_func); } -- cgit v1.2.3 From 0b8bf1baabe56f721d541953f083560d0660d78f Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Mon, 20 May 2013 09:16:58 +0000 Subject: net: dm9000: Allow instantiation using device tree This patch adds Device Tree support to dm9000 driver. Signed-off-by: Tomasz Figa Reviewed-by: Sylwester Nawrocki Reviewed-by: Sascha Hauer Signed-off-by: David S. Miller --- .../devicetree/bindings/net/davicom-dm9000.txt | 26 ++++++++++++++ .../devicetree/bindings/vendor-prefixes.txt | 1 + drivers/net/ethernet/davicom/dm9000.c | 42 ++++++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/davicom-dm9000.txt diff --git a/Documentation/devicetree/bindings/net/davicom-dm9000.txt b/Documentation/devicetree/bindings/net/davicom-dm9000.txt new file mode 100644 index 000000000000..2d39c990e641 --- /dev/null +++ b/Documentation/devicetree/bindings/net/davicom-dm9000.txt @@ -0,0 +1,26 @@ +Davicom DM9000 Fast Ethernet controller + +Required properties: +- compatible = "davicom,dm9000"; +- reg : physical addresses and sizes of registers, must contain 2 entries: + first entry : address register, + second entry : data register. +- interrupt-parent : interrupt controller to which the device is connected +- interrupts : interrupt specifier specific to interrupt controller + +Optional properties: +- local-mac-address : A bytestring of 6 bytes specifying Ethernet MAC address + to use (from firmware or bootloader) +- davicom,no-eeprom : Configuration EEPROM is not available +- davicom,ext-phy : Use external PHY + +Example: + + ethernet@18000000 { + compatible = "davicom,dm9000"; + reg = <0x18000000 0x2 0x18000004 0x2>; + interrupt-parent = <&gpn>; + interrupts = <7 4>; + local-mac-address = [00 00 de ad be ef]; + davicom,no-eeprom; + }; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 6931c4348d24..2fe74e6ec209 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -18,6 +18,7 @@ chrp Common Hardware Reference Platform cirrus Cirrus Logic, Inc. cortina Cortina Systems, Inc. dallas Maxim Integrated Products (formerly Dallas Semiconductor) +davicom DAVICOM Semiconductor, Inc. denx Denx Software Engineering emmicro EM Microelectronic epson Seiko Epson Corp. diff --git a/drivers/net/ethernet/davicom/dm9000.c b/drivers/net/ethernet/davicom/dm9000.c index a2408c84bbdd..dd243a1b03e0 100644 --- a/drivers/net/ethernet/davicom/dm9000.c +++ b/drivers/net/ethernet/davicom/dm9000.c @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include #include #include @@ -1351,6 +1353,31 @@ static const struct net_device_ops dm9000_netdev_ops = { #endif }; +static struct dm9000_plat_data *dm9000_parse_dt(struct device *dev) +{ + struct dm9000_plat_data *pdata; + struct device_node *np = dev->of_node; + const void *mac_addr; + + if (!IS_ENABLED(CONFIG_OF) || !np) + return NULL; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + if (of_find_property(np, "davicom,ext-phy", NULL)) + pdata->flags |= DM9000_PLATF_EXT_PHY; + if (of_find_property(np, "davicom,no-eeprom", NULL)) + pdata->flags |= DM9000_PLATF_NO_EEPROM; + + mac_addr = of_get_mac_address(np); + if (mac_addr) + memcpy(pdata->dev_addr, mac_addr, sizeof(pdata->dev_addr)); + + return pdata; +} + /* * Search DM9000 board, allocate space and register it */ @@ -1366,6 +1393,12 @@ dm9000_probe(struct platform_device *pdev) int i; u32 id_val; + if (!pdata) { + pdata = dm9000_parse_dt(&pdev->dev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + } + /* Init network device */ ndev = alloc_etherdev(sizeof(struct board_info)); if (!ndev) @@ -1676,11 +1709,20 @@ dm9000_drv_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id dm9000_of_matches[] = { + { .compatible = "davicom,dm9000", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dm9000_of_matches); +#endif + static struct platform_driver dm9000_driver = { .driver = { .name = "dm9000", .owner = THIS_MODULE, .pm = &dm9000_drv_pm_ops, + .of_match_table = of_match_ptr(dm9000_of_matches), }, .probe = dm9000_probe, .remove = dm9000_drv_remove, -- cgit v1.2.3 From 4b2305826ccd57a8b22d3daff543837ba27cfd43 Mon Sep 17 00:00:00 2001 From: Rasesh Mody Date: Mon, 20 May 2013 10:08:01 +0000 Subject: bna: Clear Driver Config Flags When HW Resets Driver configuration flags are retained across open/stop operations preventing configurations to be set in next open/stop. Setting MTU on a 1020 causes network to fail until a reboot is performed on the host. Clear the flags when configuration resets in hardware. Signed-off-by: Rasesh Mody Signed-off-by: David S. Miller --- drivers/net/ethernet/brocade/bna/bnad.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/brocade/bna/bnad.c b/drivers/net/ethernet/brocade/bna/bnad.c index 07f7ef05c3f2..b78e69e0e52a 100644 --- a/drivers/net/ethernet/brocade/bna/bnad.c +++ b/drivers/net/ethernet/brocade/bna/bnad.c @@ -2624,6 +2624,9 @@ bnad_stop(struct net_device *netdev) bnad_destroy_tx(bnad, 0); bnad_destroy_rx(bnad, 0); + /* These config flags are cleared in the hardware */ + bnad->cfg_flags &= ~(BNAD_CF_ALLMULTI | BNAD_CF_PROMISC); + /* Synchronize mailbox IRQ */ bnad_mbox_irq_sync(bnad); -- cgit v1.2.3 From f489a4ba769ca1d7dd4657a4f68ea93746dd669b Mon Sep 17 00:00:00 2001 From: Rasesh Mody Date: Mon, 20 May 2013 10:08:02 +0000 Subject: bna: Fix Ucast Failure Handling Failure of the UCAST set for base mac address fails when user configures a duplicate mac address that matches that of another vNIC on the same port. The bna does not handle the ucast failure and keeps this address in cache. On disable of the vNIC, bna tries to delete the failed base mac address and the fw asserts. On failure of ucast address, mark ucast address set to false. Signed-off-by: Rasesh Mody Signed-off-by: David S. Miller --- drivers/net/ethernet/brocade/bna/bna.h | 2 ++ drivers/net/ethernet/brocade/bna/bna_enet.c | 7 ++++++- drivers/net/ethernet/brocade/bna/bna_tx_rx.c | 15 +++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/brocade/bna/bna.h b/drivers/net/ethernet/brocade/bna/bna.h index 25dae757e9c4..f1eafc409bbd 100644 --- a/drivers/net/ethernet/brocade/bna/bna.h +++ b/drivers/net/ethernet/brocade/bna/bna.h @@ -455,6 +455,8 @@ void bna_bfi_rx_enet_stop_rsp(struct bna_rx *rx, void bna_bfi_rxf_cfg_rsp(struct bna_rxf *rxf, struct bfi_msgq_mhdr *msghdr); void bna_bfi_rxf_mcast_add_rsp(struct bna_rxf *rxf, struct bfi_msgq_mhdr *msghdr); +void bna_bfi_rxf_ucast_set_rsp(struct bna_rxf *rxf, + struct bfi_msgq_mhdr *msghdr); /* APIs for BNA */ void bna_rx_mod_init(struct bna_rx_mod *rx_mod, struct bna *bna, diff --git a/drivers/net/ethernet/brocade/bna/bna_enet.c b/drivers/net/ethernet/brocade/bna/bna_enet.c index db14f69d63bc..3ca77fad4851 100644 --- a/drivers/net/ethernet/brocade/bna/bna_enet.c +++ b/drivers/net/ethernet/brocade/bna/bna_enet.c @@ -298,7 +298,6 @@ bna_msgq_rsp_handler(void *arg, struct bfi_msgq_mhdr *msghdr) case BFI_ENET_I2H_RSS_ENABLE_RSP: case BFI_ENET_I2H_RX_PROMISCUOUS_RSP: case BFI_ENET_I2H_RX_DEFAULT_RSP: - case BFI_ENET_I2H_MAC_UCAST_SET_RSP: case BFI_ENET_I2H_MAC_UCAST_CLR_RSP: case BFI_ENET_I2H_MAC_UCAST_ADD_RSP: case BFI_ENET_I2H_MAC_UCAST_DEL_RSP: @@ -311,6 +310,12 @@ bna_msgq_rsp_handler(void *arg, struct bfi_msgq_mhdr *msghdr) bna_bfi_rxf_cfg_rsp(&rx->rxf, msghdr); break; + case BFI_ENET_I2H_MAC_UCAST_SET_RSP: + bna_rx_from_rid(bna, msghdr->enet_id, rx); + if (rx) + bna_bfi_rxf_ucast_set_rsp(&rx->rxf, msghdr); + break; + case BFI_ENET_I2H_MAC_MCAST_ADD_RSP: bna_rx_from_rid(bna, msghdr->enet_id, rx); if (rx) diff --git a/drivers/net/ethernet/brocade/bna/bna_tx_rx.c b/drivers/net/ethernet/brocade/bna/bna_tx_rx.c index ea6f4a036401..57cd1bff59f1 100644 --- a/drivers/net/ethernet/brocade/bna/bna_tx_rx.c +++ b/drivers/net/ethernet/brocade/bna/bna_tx_rx.c @@ -710,6 +710,21 @@ bna_bfi_rxf_cfg_rsp(struct bna_rxf *rxf, struct bfi_msgq_mhdr *msghdr) bfa_fsm_send_event(rxf, RXF_E_FW_RESP); } +void +bna_bfi_rxf_ucast_set_rsp(struct bna_rxf *rxf, + struct bfi_msgq_mhdr *msghdr) +{ + struct bfi_enet_rsp *rsp = + (struct bfi_enet_rsp *)msghdr; + + if (rsp->error) { + /* Clear ucast from cache */ + rxf->ucast_active_set = 0; + } + + bfa_fsm_send_event(rxf, RXF_E_FW_RESP); +} + void bna_bfi_rxf_mcast_add_rsp(struct bna_rxf *rxf, struct bfi_msgq_mhdr *msghdr) -- cgit v1.2.3 From 43c07adaebc34d8c23cdc60128fc7f41088515f7 Mon Sep 17 00:00:00 2001 From: Rasesh Mody Date: Mon, 20 May 2013 10:08:03 +0000 Subject: bna: Enahncement to Identify Default IOC Function User should not be allowed to delete base function of eth port. Add a new field to the bfa ioc attributes structure to indicate if the given ioc is default function on the port or not. Signed-off-by: Rasesh Mody Signed-off-by: David S. Miller --- drivers/net/ethernet/brocade/bna/bfa_defs.h | 3 ++- drivers/net/ethernet/brocade/bna/bfa_ioc.c | 7 ++++--- drivers/net/ethernet/brocade/bna/bfa_ioc.h | 2 ++ 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/brocade/bna/bfa_defs.h b/drivers/net/ethernet/brocade/bna/bfa_defs.h index e423f82da490..b7d8127c198f 100644 --- a/drivers/net/ethernet/brocade/bna/bfa_defs.h +++ b/drivers/net/ethernet/brocade/bna/bfa_defs.h @@ -164,7 +164,8 @@ struct bfa_ioc_attr { u8 port_mode; /*!< enum bfa_mode */ u8 cap_bm; /*!< capability */ u8 port_mode_cfg; /*!< enum bfa_mode */ - u8 rsvd[4]; /*!< 64bit align */ + u8 def_fn; /*!< 1 if default fn */ + u8 rsvd[3]; /*!< 64bit align */ }; /* Adapter capability mask definition */ diff --git a/drivers/net/ethernet/brocade/bna/bfa_ioc.c b/drivers/net/ethernet/brocade/bna/bfa_ioc.c index f2b73ffa9122..6f3cac060f29 100644 --- a/drivers/net/ethernet/brocade/bna/bfa_ioc.c +++ b/drivers/net/ethernet/brocade/bna/bfa_ioc.c @@ -2371,7 +2371,7 @@ bfa_nw_ioc_get_attr(struct bfa_ioc *ioc, struct bfa_ioc_attr *ioc_attr) memset((void *)ioc_attr, 0, sizeof(struct bfa_ioc_attr)); ioc_attr->state = bfa_ioc_get_state(ioc); - ioc_attr->port_id = ioc->port_id; + ioc_attr->port_id = bfa_ioc_portid(ioc); ioc_attr->port_mode = ioc->port_mode; ioc_attr->port_mode_cfg = ioc->port_mode_cfg; @@ -2381,8 +2381,9 @@ bfa_nw_ioc_get_attr(struct bfa_ioc *ioc, struct bfa_ioc_attr *ioc_attr) bfa_ioc_get_adapter_attr(ioc, &ioc_attr->adapter_attr); - ioc_attr->pci_attr.device_id = ioc->pcidev.device_id; - ioc_attr->pci_attr.pcifn = ioc->pcidev.pci_func; + ioc_attr->pci_attr.device_id = bfa_ioc_devid(ioc); + ioc_attr->pci_attr.pcifn = bfa_ioc_pcifn(ioc); + ioc_attr->def_fn = bfa_ioc_is_default(ioc); bfa_ioc_get_pci_chip_rev(ioc, ioc_attr->pci_attr.chip_rev); } diff --git a/drivers/net/ethernet/brocade/bna/bfa_ioc.h b/drivers/net/ethernet/brocade/bna/bfa_ioc.h index 63a85e555df8..f04e0aab25b4 100644 --- a/drivers/net/ethernet/brocade/bna/bfa_ioc.h +++ b/drivers/net/ethernet/brocade/bna/bfa_ioc.h @@ -222,6 +222,8 @@ struct bfa_ioc_hwif { #define bfa_ioc_bar0(__ioc) ((__ioc)->pcidev.pci_bar_kva) #define bfa_ioc_portid(__ioc) ((__ioc)->port_id) #define bfa_ioc_asic_gen(__ioc) ((__ioc)->asic_gen) +#define bfa_ioc_is_default(__ioc) \ + (bfa_ioc_pcifn(__ioc) == bfa_ioc_portid(__ioc)) #define bfa_ioc_fetch_stats(__ioc, __stats) \ (((__stats)->drv_stats) = (__ioc)->stats) #define bfa_ioc_clr_stats(__ioc) \ -- cgit v1.2.3 From 45e983414334f217c60bd04d39d6f5ec2d8d7bb4 Mon Sep 17 00:00:00 2001 From: Rasesh Mody Date: Mon, 20 May 2013 10:08:04 +0000 Subject: bna: Driver and Firmware Updated Driver and Firmware versions updated to 3.2.21.1. Signed-off-by: Rasesh Mody Signed-off-by: David S. Miller --- drivers/net/ethernet/brocade/bna/bnad.h | 2 +- drivers/net/ethernet/brocade/bna/cna.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/brocade/bna/bnad.h b/drivers/net/ethernet/brocade/bna/bnad.h index c1d0bc059bfd..aefee77523f2 100644 --- a/drivers/net/ethernet/brocade/bna/bnad.h +++ b/drivers/net/ethernet/brocade/bna/bnad.h @@ -71,7 +71,7 @@ struct bnad_rx_ctrl { #define BNAD_NAME "bna" #define BNAD_NAME_LEN 64 -#define BNAD_VERSION "3.1.2.1" +#define BNAD_VERSION "3.2.21.1" #define BNAD_MAILBOX_MSIX_INDEX 0 #define BNAD_MAILBOX_MSIX_VECTORS 1 diff --git a/drivers/net/ethernet/brocade/bna/cna.h b/drivers/net/ethernet/brocade/bna/cna.h index 14ca9317c915..c37f706d9992 100644 --- a/drivers/net/ethernet/brocade/bna/cna.h +++ b/drivers/net/ethernet/brocade/bna/cna.h @@ -37,8 +37,8 @@ extern char bfa_version[]; -#define CNA_FW_FILE_CT "ctfw-3.1.0.0.bin" -#define CNA_FW_FILE_CT2 "ct2fw-3.1.0.0.bin" +#define CNA_FW_FILE_CT "ctfw-3.2.1.0.bin" +#define CNA_FW_FILE_CT2 "ct2fw-3.2.1.0.bin" #define FC_SYMNAME_MAX 256 /*!< max name server symbolic name size */ #pragma pack(1) -- cgit v1.2.3 From 2c7b49212a86f13697281a4dace2cb96aec71d6b Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Sun, 19 May 2013 22:53:42 +0000 Subject: phy: fix the use of PHY_IGNORE_INTERRUPT When a PHY device is registered with the special IRQ value PHY_IGNORE_INTERRUPT (-2) it will not properly be handled by the PHY library: - it continues to poll its register, while we do not want this because such PHY link events or register changes are serviced by an Ethernet MAC - it will still try to configure PHY interrupts at the PHY level, such interrupts do not exist at the PHY but at the MAC level - the state machine only handles PHY_POLL, but should also handle PHY_IGNORE_INTERRUPT similarly This patch updates the PHY state machine and initialization paths to account for the specific PHY_IGNORE_INTERRUPT. Based on an earlier patch by Thomas Petazzoni, and reworked to add the missing bits. Add a helper phy_interrupt_is_valid() which specifically tests for a PHY interrupt not to be PHY_POLL or PHY_IGNORE_INTERRUPT and use it throughout the code. Signed-off-by: Thomas Petazzoni Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 9 +++++---- drivers/net/phy/phy_device.c | 7 +++++-- include/linux/phy.h | 12 ++++++++++++ 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index c14f14741b3f..3bcf0994d3ba 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -682,7 +682,7 @@ void phy_stop(struct phy_device *phydev) if (PHY_HALTED == phydev->state) goto out_unlock; - if (phydev->irq != PHY_POLL) { + if (phy_interrupt_is_valid(phydev)) { /* Disable PHY Interrupts */ phy_config_interrupt(phydev, PHY_INTERRUPT_DISABLED); @@ -828,8 +828,9 @@ void phy_state_machine(struct work_struct *work) break; case PHY_RUNNING: /* Only register a CHANGE if we are - * polling */ - if (PHY_POLL == phydev->irq) + * polling or ignoring interrupts + */ + if (!phy_interrupt_is_valid(phydev)) phydev->state = PHY_CHANGELINK; break; case PHY_CHANGELINK: @@ -848,7 +849,7 @@ void phy_state_machine(struct work_struct *work) phydev->adjust_link(phydev->attached_dev); - if (PHY_POLL != phydev->irq) + if (phy_interrupt_is_valid(phydev)) err = phy_config_interrupt(phydev, PHY_INTERRUPT_ENABLED); break; diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 3657b4a29124..8e29d22ba113 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -1009,8 +1009,11 @@ static int phy_probe(struct device *dev) phydrv = to_phy_driver(drv); phydev->drv = phydrv; - /* Disable the interrupt if the PHY doesn't support it */ - if (!(phydrv->flags & PHY_HAS_INTERRUPT)) + /* Disable the interrupt if the PHY doesn't support it + * but the interrupt is still a valid one + */ + if (!(phydrv->flags & PHY_HAS_INTERRUPT) && + phy_interrupt_is_valid(phydev)) phydev->irq = PHY_POLL; mutex_lock(&phydev->lock); diff --git a/include/linux/phy.h b/include/linux/phy.h index 9e11039dd7a3..8e4bc8ab692d 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -508,6 +508,18 @@ static inline int phy_write(struct phy_device *phydev, u32 regnum, u16 val) return mdiobus_write(phydev->bus, phydev->addr, regnum, val); } +/** + * phy_interrupt_is_valid - Convenience function for testing a given PHY irq + * @phydev: the phy_device struct + * + * NOTE: must be kept in sync with addition/removal of PHY_POLL and + * PHY_IGNORE_INTERRUPT + */ +static inline bool phy_interrupt_is_valid(struct phy_device *phydev) +{ + return phydev->irq != PHY_POLL && phydev->irq != PHY_IGNORE_INTERRUPT; +} + struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id, bool is_c45, struct phy_c45_device_ids *c45_ids); struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45); -- cgit v1.2.3 From 5ea94e7686a3aa04cc0d01a2d8bd3d0292b3f592 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Sun, 19 May 2013 22:53:43 +0000 Subject: phy: add phy_mac_interrupt() to use with PHY_IGNORE_INTERRUPT There is currently no way for an Ethernet MAC driver servicing PHY link interrupts to notify this to the PHY state machine without defining its own state machine. Since most drivers are not so special, introduce a helper: phy_mac_interrupt() which can be called from a link up/down interrupt routine to update the PHY state machine. To avoid code duplication some refactoring has been done to expose the workqueue and its corresponding callback internally. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 14 +++++++++----- drivers/net/phy/phy_device.c | 1 + include/linux/phy.h | 2 ++ 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 3bcf0994d3ba..2d28a0ef4572 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -419,8 +419,6 @@ out_unlock: EXPORT_SYMBOL(phy_start_aneg); -static void phy_change(struct work_struct *work); - /** * phy_start_machine - start PHY state machine tracking * @phydev: the phy_device struct @@ -565,8 +563,6 @@ int phy_start_interrupts(struct phy_device *phydev) { int err = 0; - INIT_WORK(&phydev->phy_queue, phy_change); - atomic_set(&phydev->irq_disable, 0); if (request_irq(phydev->irq, phy_interrupt, IRQF_SHARED, @@ -623,7 +619,7 @@ EXPORT_SYMBOL(phy_stop_interrupts); * phy_change - Scheduled by the phy_interrupt/timer to handle PHY changes * @work: work_struct that describes the work to be done */ -static void phy_change(struct work_struct *work) +void phy_change(struct work_struct *work) { int err; struct phy_device *phydev = @@ -922,6 +918,14 @@ void phy_state_machine(struct work_struct *work) schedule_delayed_work(&phydev->state_queue, PHY_STATE_TIME * HZ); } +void phy_mac_interrupt(struct phy_device *phydev, int new_link) +{ + cancel_work_sync(&phydev->phy_queue); + phydev->link = new_link; + schedule_work(&phydev->phy_queue); +} +EXPORT_SYMBOL(phy_mac_interrupt); + static inline void mmd_phy_indirect(struct mii_bus *bus, int prtad, int devad, int addr) { diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 8e29d22ba113..b55aa33a5b8b 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -189,6 +189,7 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id, mutex_init(&dev->lock); INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine); + INIT_WORK(&dev->phy_queue, phy_change); /* Request the appropriate module unconditionally; don't bother trying to do so only if it isn't already loaded, diff --git a/include/linux/phy.h b/include/linux/phy.h index 8e4bc8ab692d..fdfa11542974 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -557,6 +557,8 @@ void phy_drivers_unregister(struct phy_driver *drv, int n); int phy_driver_register(struct phy_driver *new_driver); int phy_drivers_register(struct phy_driver *new_driver, int n); void phy_state_machine(struct work_struct *work); +void phy_change(struct work_struct *work); +void phy_mac_interrupt(struct phy_device *phydev, int new_link); void phy_start_machine(struct phy_device *phydev, void (*handler)(struct net_device *)); void phy_stop_machine(struct phy_device *phydev); -- cgit v1.2.3 From ac14876cf9255175bf3bdad645bf8aa2b8fb2d7c Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 20 May 2013 09:51:12 +1000 Subject: xfs: fix missing KM_NOFS tags to keep lockdep happy There are several places where we use KM_SLEEP allocation contexts and use the fact that they are called from transaction context to add KM_NOFS where appropriate. Unfortunately, there are several places where the code makes this assumption but can be called from outside transaction context but with filesystem locks held. These places need explicit KM_NOFS annotations to avoid lockdep complaining about reclaim contexts. Signed-off-by: Dave Chinner Reviewed-by: Ben Myers Signed-off-by: Ben Myers --- fs/xfs/xfs_buf.c | 2 +- fs/xfs/xfs_da_btree.c | 6 ++++-- fs/xfs/xfs_dir2_leaf.c | 2 +- fs/xfs/xfs_log_cil.c | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c index 82b70bda9f47..0d2554299688 100644 --- a/fs/xfs/xfs_buf.c +++ b/fs/xfs/xfs_buf.c @@ -1649,7 +1649,7 @@ xfs_alloc_buftarg( { xfs_buftarg_t *btp; - btp = kmem_zalloc(sizeof(*btp), KM_SLEEP); + btp = kmem_zalloc(sizeof(*btp), KM_SLEEP | KM_NOFS); btp->bt_mount = mp; btp->bt_dev = bdev->bd_dev; diff --git a/fs/xfs/xfs_da_btree.c b/fs/xfs/xfs_da_btree.c index 9b26a99ebfe9..41ea7e14a7b6 100644 --- a/fs/xfs/xfs_da_btree.c +++ b/fs/xfs/xfs_da_btree.c @@ -2464,7 +2464,8 @@ xfs_buf_map_from_irec( ASSERT(nirecs >= 1); if (nirecs > 1) { - map = kmem_zalloc(nirecs * sizeof(struct xfs_buf_map), KM_SLEEP); + map = kmem_zalloc(nirecs * sizeof(struct xfs_buf_map), + KM_SLEEP | KM_NOFS); if (!map) return ENOMEM; *mapp = map; @@ -2520,7 +2521,8 @@ xfs_dabuf_map( * Optimize the one-block case. */ if (nfsb != 1) - irecs = kmem_zalloc(sizeof(irec) * nfsb, KM_SLEEP); + irecs = kmem_zalloc(sizeof(irec) * nfsb, + KM_SLEEP | KM_NOFS); nirecs = nfsb; error = xfs_bmapi_read(dp, (xfs_fileoff_t)bno, nfsb, irecs, diff --git a/fs/xfs/xfs_dir2_leaf.c b/fs/xfs/xfs_dir2_leaf.c index 721ba2fe8e54..da71a1819d78 100644 --- a/fs/xfs/xfs_dir2_leaf.c +++ b/fs/xfs/xfs_dir2_leaf.c @@ -1336,7 +1336,7 @@ xfs_dir2_leaf_getdents( mp->m_sb.sb_blocksize); map_info = kmem_zalloc(offsetof(struct xfs_dir2_leaf_map_info, map) + (length * sizeof(struct xfs_bmbt_irec)), - KM_SLEEP); + KM_SLEEP | KM_NOFS); map_info->map_size = length; /* diff --git a/fs/xfs/xfs_log_cil.c b/fs/xfs/xfs_log_cil.c index e3d0b85d852b..d0833b54e55d 100644 --- a/fs/xfs/xfs_log_cil.c +++ b/fs/xfs/xfs_log_cil.c @@ -139,7 +139,7 @@ xlog_cil_prepare_log_vecs( new_lv = kmem_zalloc(sizeof(*new_lv) + niovecs * sizeof(struct xfs_log_iovec), - KM_SLEEP); + KM_SLEEP|KM_NOFS); /* The allocated iovec region lies beyond the log vector. */ new_lv->lv_iovecp = (struct xfs_log_iovec *)&new_lv[1]; -- cgit v1.2.3 From 72916fb8cbcf0c2928f56cdc2fbe8c7bf5517758 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 20 May 2013 09:51:13 +1000 Subject: xfs: xfs_da3_node_read_verify() doesn't handle XFS_ATTR3_LEAF_MAGIC Signed-off-by: Dave Chinner Reviewed-by: Ben Myers Signed-off-by: Ben Myers --- fs/xfs/xfs_da_btree.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/xfs/xfs_da_btree.c b/fs/xfs/xfs_da_btree.c index 41ea7e14a7b6..0b8b2a13cd24 100644 --- a/fs/xfs/xfs_da_btree.c +++ b/fs/xfs/xfs_da_btree.c @@ -270,6 +270,7 @@ xfs_da3_node_read_verify( break; return; case XFS_ATTR_LEAF_MAGIC: + case XFS_ATTR3_LEAF_MAGIC: bp->b_ops = &xfs_attr3_leaf_buf_ops; bp->b_ops->verify_read(bp); return; -- cgit v1.2.3 From b38958d715316031fe9ea0cc6c22043072a55f49 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 20 May 2013 09:51:14 +1000 Subject: xfs: xfs_attr_shortform_allfit() does not handle attr3 format. xfstests generic/117 fails with: XFS: Assertion failed: leaf->hdr.info.magic == cpu_to_be16(XFS_ATTR_LEAF_MAGIC) indicating a function that does not handle the attr3 format correctly. Fix it. Signed-off-by: Dave Chinner Reviewed-by: Ben Myers Signed-off-by: Ben Myers --- fs/xfs/xfs_attr_leaf.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/fs/xfs/xfs_attr_leaf.c b/fs/xfs/xfs_attr_leaf.c index 08d5457c948e..8eeb88fb3201 100644 --- a/fs/xfs/xfs_attr_leaf.c +++ b/fs/xfs/xfs_attr_leaf.c @@ -931,20 +931,22 @@ xfs_attr_shortform_list(xfs_attr_list_context_t *context) */ int xfs_attr_shortform_allfit( - struct xfs_buf *bp, - struct xfs_inode *dp) + struct xfs_buf *bp, + struct xfs_inode *dp) { - xfs_attr_leafblock_t *leaf; - xfs_attr_leaf_entry_t *entry; + struct xfs_attr_leafblock *leaf; + struct xfs_attr_leaf_entry *entry; xfs_attr_leaf_name_local_t *name_loc; - int bytes, i; + struct xfs_attr3_icleaf_hdr leafhdr; + int bytes; + int i; leaf = bp->b_addr; - ASSERT(leaf->hdr.info.magic == cpu_to_be16(XFS_ATTR_LEAF_MAGIC)); + xfs_attr3_leaf_hdr_from_disk(&leafhdr, leaf); + entry = xfs_attr3_leaf_entryp(leaf); - entry = &leaf->entries[0]; bytes = sizeof(struct xfs_attr_sf_hdr); - for (i = 0; i < be16_to_cpu(leaf->hdr.count); entry++, i++) { + for (i = 0; i < leafhdr.count; entry++, i++) { if (entry->flags & XFS_ATTR_INCOMPLETE) continue; /* don't copy partial entries */ if (!(entry->flags & XFS_ATTR_LOCAL)) @@ -954,15 +956,15 @@ xfs_attr_shortform_allfit( return(0); if (be16_to_cpu(name_loc->valuelen) >= XFS_ATTR_SF_ENTSIZE_MAX) return(0); - bytes += sizeof(struct xfs_attr_sf_entry)-1 + bytes += sizeof(struct xfs_attr_sf_entry) - 1 + name_loc->namelen + be16_to_cpu(name_loc->valuelen); } if ((dp->i_mount->m_flags & XFS_MOUNT_ATTR2) && (dp->i_d.di_format != XFS_DINODE_FMT_BTREE) && (bytes == sizeof(struct xfs_attr_sf_hdr))) - return(-1); - return(xfs_attr_shortform_bytesfit(dp, bytes)); + return -1; + return xfs_attr_shortform_bytesfit(dp, bytes); } /* -- cgit v1.2.3 From e461fcb194172b3f709e0b478d2ac1bdac7ab9a3 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 20 May 2013 09:51:16 +1000 Subject: xfs: remote attribute lookups require the value length When reading a remote attribute, to correctly calculate the length of the data buffer for CRC enable filesystems, we need to know the length of the attribute data. We get this information when we look up the attribute, but we don't store it in the args structure along with the other remote attr information we get from the lookup. Add this information to the args structure so we can use it appropriately. Signed-off-by: Dave Chinner Reviewed-by: Ben Myers Signed-off-by: Ben Myers --- fs/xfs/xfs_attr_leaf.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/xfs/xfs_attr_leaf.c b/fs/xfs/xfs_attr_leaf.c index 8eeb88fb3201..0bce1b348580 100644 --- a/fs/xfs/xfs_attr_leaf.c +++ b/fs/xfs/xfs_attr_leaf.c @@ -2332,9 +2332,10 @@ xfs_attr3_leaf_lookup_int( if (!xfs_attr_namesp_match(args->flags, entry->flags)) continue; args->index = probe; + args->valuelen = be32_to_cpu(name_rmt->valuelen); args->rmtblkno = be32_to_cpu(name_rmt->valueblk); args->rmtblkcnt = XFS_B_TO_FSB(args->dp->i_mount, - be32_to_cpu(name_rmt->valuelen)); + args->valuelen); return XFS_ERROR(EEXIST); } } -- cgit v1.2.3 From 00fd6e6153100ae868975cab67f02df6f16343eb Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Mon, 20 May 2013 12:36:07 +0000 Subject: regulator: lp872x: support the device tree feature This patch enables the DT structure of the LP8720 and LP8725 device. The LP872x platform data is generated when the device tree is configured. Even if the platform data is NULL, it is no issue at all because the driver is configured with the default mode. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Mark Brown --- .../devicetree/bindings/regulator/lp872x.txt | 160 +++++++++++++++++++++ drivers/regulator/lp872x.c | 116 ++++++++++++++- 2 files changed, 274 insertions(+), 2 deletions(-) create mode 100644 Documentation/devicetree/bindings/regulator/lp872x.txt diff --git a/Documentation/devicetree/bindings/regulator/lp872x.txt b/Documentation/devicetree/bindings/regulator/lp872x.txt new file mode 100644 index 000000000000..78183182dad9 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/lp872x.txt @@ -0,0 +1,160 @@ +Binding for TI/National Semiconductor LP872x Driver + +Required properties: + - compatible: "ti,lp8720" or "ti,lp8725" + - reg: I2C slave address. 0x7d = LP8720, 0x7a = LP8725 + +Optional properties: + - ti,general-config: the value of LP872X_GENERAL_CFG register (u8) + (LP8720) + bit[2]: BUCK output voltage control by external DVS pin or register + 1 = external pin, 0 = bit7 of register 08h + bit[1]: sleep control by external DVS pin or register + 1 = external pin, 0 = bit6 of register 08h + bit[0]: time step unit(usec). 1 = 25, 0 = 50 + + (LP8725) + bit[7:6]: time step unit(usec). 00 = 32, 01 = 64, 10 = 128, 11 = 256 + bit[4]: BUCK2 enable control. 1 = enable, 0 = disable + bit[3]: BUCK2 output voltage register address. 1 = 0Ah, 0 = 0Bh + bit[2]: BUCK1 output voltage control by external DVS pin or register + 1 = register 08h, 0 = DVS + bit[1]: LDO sleep control. 1 = sleep mode, 0 = normal + bit[0]: BUCK1 enable control, 1 = enable, 0 = disable + + For more details, please see the datasheet. + + - ti,update-config: define it when LP872X_GENERAL_CFG register should be set + - ti,dvs-gpio: GPIO specifier for external DVS pin control of LP872x devices. + - ti,dvs-vsel: DVS selector. 0 = SEL_V1, 1 = SEL_V2. + - ti,dvs-state: initial DVS pin state. 0 = DVS_LOW, 1 = DVS_HIGH. + + Sub nodes for regulator_init_data + LP8720 has maximum 6 nodes. (child name: ldo1 ~ 5 and buck) + LP8725 has maximum 9 nodes. (child name: ldo1 ~ 5, lilo1,2 and buck1,2) + For more details, please see the following binding document. + (Documentation/devicetree/bindings/regulator/regulator.txt) + +Datasheet + - LP8720: http://www.ti.com/lit/ds/symlink/lp8720.pdf + - LP8725: http://www.ti.com/lit/ds/symlink/lp8725.pdf + +Example 1) LP8720 + +lp8720@7d { + compatible = "ti,lp8720"; + reg = <0x7d>; + + /* external DVS pin used, timestep is 25usec */ + ti,general-config = /bits/ 8 <0x03>; + ti,update-config; + + /* + * The dvs-gpio depends on the processor environment. + * For example, following GPIO specifier means GPIO134 in OMAP4. + */ + ti,dvs-gpio = <&gpio5 6 0>; + ti,dvs-vsel = /bits/ 8 <1>; /* SEL_V2 */ + ti,dvs-state = /bits/ 8 <1>; /* DVS_HIGH */ + + vaf: ldo1 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3300000>; + }; + + vmmc: ldo2 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3300000>; + }; + + vcam_io: ldo3 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + }; + + vcam_core: ldo4 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <2850000>; + regulator-boot-on; + }; + + vcam: ldo5 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3300000>; + }; + + vcc: buck { + regulator-name = "VBUCK"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <2300000>; + }; +}; + +Example 2) LP8725 + +lp8725@7a { + compatible = "ti,lp8725"; + reg = <0x7a>; + + /* Enable BUCK1,2, no DVS, normal LDO mode, timestep is 256usec */ + ti,general-config = /bits/ 8 <0xdd>; + ti,update-config; + + vcam_io: ldo1 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3300000>; + }; + + vcam_core: ldo2 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3300000>; + }; + + vcam: ldo3 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3300000>; + }; + + vcmmb_io: ldo4 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + }; + + vcmmb_core: ldo5 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + }; + + vaux1: lilo1 { + regulator-name = "VAUX1"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <3300000>; + }; + + vaux2: lilo2 { + regulator-name = "VAUX2"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <3300000>; + }; + + vcc1: buck1 { + regulator-name = "VBUCK1"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <3000000>; + regulator-min-microamp = <460000>; + regulator-max-microamp = <1370000>; + regulator-boot-on; + }; + + vcc2: buck2 { + regulator-name = "VBUCK2"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <3000000>; + regulator-min-microamp = <460000>; + regulator-max-microamp = <1370000>; + regulator-boot-on; + }; +}; diff --git a/drivers/regulator/lp872x.c b/drivers/regulator/lp872x.c index f5fc4a142cdf..b16336bcd4d4 100644 --- a/drivers/regulator/lp872x.c +++ b/drivers/regulator/lp872x.c @@ -18,6 +18,9 @@ #include #include #include +#include +#include +#include /* Registers : LP8720/8725 shared */ #define LP872X_GENERAL_CFG 0x00 @@ -723,8 +726,8 @@ static int lp872x_init_dvs(struct lp872x *lp) gpio = dvs->gpio; if (!gpio_is_valid(gpio)) { - dev_err(lp->dev, "invalid gpio: %d\n", gpio); - return -EINVAL; + dev_warn(lp->dev, "invalid gpio: %d\n", gpio); + goto set_default_dvs_mode; } pinstate = dvs->init_state; @@ -829,6 +832,103 @@ static const struct regmap_config lp872x_regmap_config = { .max_register = MAX_REGISTERS, }; +#ifdef CONFIG_OF + +#define LP872X_VALID_OPMODE (REGULATOR_MODE_FAST | REGULATOR_MODE_NORMAL) + +static struct of_regulator_match lp8720_matches[] = { + { .name = "ldo1", .driver_data = (void *)LP8720_ID_LDO1, }, + { .name = "ldo2", .driver_data = (void *)LP8720_ID_LDO2, }, + { .name = "ldo3", .driver_data = (void *)LP8720_ID_LDO3, }, + { .name = "ldo4", .driver_data = (void *)LP8720_ID_LDO4, }, + { .name = "ldo5", .driver_data = (void *)LP8720_ID_LDO5, }, + { .name = "buck", .driver_data = (void *)LP8720_ID_BUCK, }, +}; + +static struct of_regulator_match lp8725_matches[] = { + { .name = "ldo1", .driver_data = (void *)LP8725_ID_LDO1, }, + { .name = "ldo2", .driver_data = (void *)LP8725_ID_LDO2, }, + { .name = "ldo3", .driver_data = (void *)LP8725_ID_LDO3, }, + { .name = "ldo4", .driver_data = (void *)LP8725_ID_LDO4, }, + { .name = "ldo5", .driver_data = (void *)LP8725_ID_LDO5, }, + { .name = "lilo1", .driver_data = (void *)LP8725_ID_LILO1, }, + { .name = "lilo2", .driver_data = (void *)LP8725_ID_LILO2, }, + { .name = "buck1", .driver_data = (void *)LP8725_ID_BUCK1, }, + { .name = "buck2", .driver_data = (void *)LP8725_ID_BUCK2, }, +}; + +static struct lp872x_platform_data +*lp872x_populate_pdata_from_dt(struct device *dev, enum lp872x_id which) +{ + struct device_node *np = dev->of_node; + struct lp872x_platform_data *pdata; + struct of_regulator_match *match; + struct regulator_init_data *d; + int num_matches; + int count; + int i; + u8 dvs_state; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + goto out; + + of_property_read_u8(np, "ti,general-config", &pdata->general_config); + if (of_find_property(np, "ti,update-config", NULL)) + pdata->update_config = true; + + pdata->dvs = devm_kzalloc(dev, sizeof(struct lp872x_dvs), GFP_KERNEL); + if (!pdata->dvs) + goto out; + + pdata->dvs->gpio = of_get_named_gpio(np, "ti,dvs-gpio", 0); + of_property_read_u8(np, "ti,dvs-vsel", (u8 *)&pdata->dvs->vsel); + of_property_read_u8(np, "ti,dvs-state", &dvs_state); + pdata->dvs->init_state = dvs_state ? DVS_HIGH : DVS_LOW; + + if (of_get_child_count(np) == 0) + goto out; + + switch (which) { + case LP8720: + match = lp8720_matches; + num_matches = ARRAY_SIZE(lp8720_matches); + break; + case LP8725: + match = lp8725_matches; + num_matches = ARRAY_SIZE(lp8725_matches); + break; + default: + goto out; + } + + count = of_regulator_match(dev, np, match, num_matches); + if (count <= 0) + goto out; + + for (i = 0; i < num_matches; i++) { + pdata->regulator_data[i].id = (int)match[i].driver_data; + pdata->regulator_data[i].init_data = match[i].init_data; + + /* Operation mode configuration for buck/buck1/buck2 */ + if (strncmp(match[i].name, "buck", 4)) + continue; + + d = pdata->regulator_data[i].init_data; + d->constraints.valid_modes_mask |= LP872X_VALID_OPMODE; + d->constraints.valid_ops_mask |= REGULATOR_CHANGE_MODE; + } +out: + return pdata; +} +#else +static struct lp872x_platform_data +*lp872x_populate_pdata_from_dt(struct device *dev, enum lp872x_id which) +{ + return NULL; +} +#endif + static int lp872x_probe(struct i2c_client *cl, const struct i2c_device_id *id) { struct lp872x *lp; @@ -838,6 +938,10 @@ static int lp872x_probe(struct i2c_client *cl, const struct i2c_device_id *id) [LP8725] = LP8725_NUM_REGULATORS, }; + if (cl->dev.of_node) + cl->dev.platform_data = lp872x_populate_pdata_from_dt(&cl->dev, + (enum lp872x_id)id->driver_data); + lp = devm_kzalloc(&cl->dev, sizeof(struct lp872x), GFP_KERNEL); if (!lp) goto err_mem; @@ -882,6 +986,13 @@ static int lp872x_remove(struct i2c_client *cl) return 0; } +static const struct of_device_id lp872x_dt_ids[] = { + { .compatible = "ti,lp8720", }, + { .compatible = "ti,lp8725", }, + { } +}; +MODULE_DEVICE_TABLE(of, lp872x_dt_ids); + static const struct i2c_device_id lp872x_ids[] = { {"lp8720", LP8720}, {"lp8725", LP8725}, @@ -893,6 +1004,7 @@ static struct i2c_driver lp872x_driver = { .driver = { .name = "lp872x", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(lp872x_dt_ids), }, .probe = lp872x_probe, .remove = lp872x_remove, -- cgit v1.2.3 From de7f2b1bdf02f6b44410dceb7295284c2a75a304 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 20 May 2013 17:15:20 -0600 Subject: sparc/PCI: Use PCI_UNKNOWN for unknown power state Previously we initialized dev->current_state to 4 (PCI_D3cold), but I think we wanted PCI_UNKNOWN (5) here based on the comment and the fact that the generic version of this code, pci_setup_device(), uses PCI_UNKNOWN. Signed-off-by: Bjorn Helgaas Acked-by: David S. Miller --- arch/sparc/kernel/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/sparc/kernel/pci.c b/arch/sparc/kernel/pci.c index baf4366e2d6a..972892a0aa11 100644 --- a/arch/sparc/kernel/pci.c +++ b/arch/sparc/kernel/pci.c @@ -327,7 +327,7 @@ static struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm, if ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE) pci_set_master(dev); - dev->current_state = 4; /* unknown power state */ + dev->current_state = PCI_UNKNOWN; /* unknown power state */ dev->error_state = pci_channel_io_normal; dev->dma_mask = 0xffffffff; -- cgit v1.2.3 From 045ac3b5629d9711531a408e92f9074db6afe7ce Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Tue, 14 May 2013 17:08:26 -0700 Subject: drm/i915: add encoder get_config function v5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can use this for fetching encoder specific pipe_config state, like mode flags, adjusted clock, etc. Just used for mode flags atm, so we can check the pipe config state at mode set time. v2: get_config when checking hw state too v3: fix DVO and LVDS mode flags (Ville) get SDVO DTD for flag fetch (Ville) v4: use input timings (Ville) correct command used (Ville) remove gen4 check (Ville) v5: get DDI flag config too Signed-off-by: Jesse Barnes Reviewed-by: Ville Syrjälä (v4) Tested-by: Paulo Zanoni (the new hsw ddi stuff) Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_crt.c | 23 ++++++++++++++++++++ drivers/gpu/drm/i915/intel_ddi.c | 23 ++++++++++++++++++++ drivers/gpu/drm/i915/intel_display.c | 20 ++++++++++++++--- drivers/gpu/drm/i915/intel_dp.c | 23 ++++++++++++++++++++ drivers/gpu/drm/i915/intel_drv.h | 4 ++++ drivers/gpu/drm/i915/intel_dvo.c | 21 ++++++++++++++++++ drivers/gpu/drm/i915/intel_hdmi.c | 23 ++++++++++++++++++++ drivers/gpu/drm/i915/intel_lvds.c | 26 ++++++++++++++++++++++ drivers/gpu/drm/i915/intel_sdvo.c | 42 ++++++++++++++++++++++++++++++++++++ 9 files changed, 202 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 66a0c6f0bb81..5e9f93e53255 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -84,6 +84,28 @@ static bool intel_crt_get_hw_state(struct intel_encoder *encoder, return true; } +static void intel_crt_get_config(struct intel_encoder *encoder, + struct intel_crtc_config *pipe_config) +{ + struct drm_i915_private *dev_priv = encoder->base.dev->dev_private; + struct intel_crt *crt = intel_encoder_to_crt(encoder); + u32 tmp, flags = 0; + + tmp = I915_READ(crt->adpa_reg); + + if (tmp & ADPA_HSYNC_ACTIVE_HIGH) + flags |= DRM_MODE_FLAG_PHSYNC; + else + flags |= DRM_MODE_FLAG_NHSYNC; + + if (tmp & ADPA_VSYNC_ACTIVE_HIGH) + flags |= DRM_MODE_FLAG_PVSYNC; + else + flags |= DRM_MODE_FLAG_NVSYNC; + + pipe_config->adjusted_mode.flags |= flags; +} + /* Note: The caller is required to filter out dpms modes not supported by the * platform. */ static void intel_crt_set_dpms(struct intel_encoder *encoder, int mode) @@ -778,6 +800,7 @@ void intel_crt_init(struct drm_device *dev) crt->base.compute_config = intel_crt_compute_config; crt->base.disable = intel_disable_crt; crt->base.enable = intel_enable_crt; + crt->base.get_config = intel_crt_get_config; if (I915_HAS_HOTPLUG(dev)) crt->base.hpd_pin = HPD_CRT; if (HAS_DDI(dev)) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 062de679f38f..e66852186751 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -1259,6 +1259,28 @@ static void intel_ddi_hot_plug(struct intel_encoder *intel_encoder) intel_dp_check_link_status(intel_dp); } +static void intel_ddi_get_config(struct intel_encoder *encoder, + struct intel_crtc_config *pipe_config) +{ + struct drm_i915_private *dev_priv = encoder->base.dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc); + enum transcoder cpu_transcoder = intel_crtc->config.cpu_transcoder; + u32 temp, flags = 0; + + temp = I915_READ(TRANS_DDI_FUNC_CTL(cpu_transcoder)); + if (temp & TRANS_DDI_PHSYNC) + flags |= DRM_MODE_FLAG_PHSYNC; + else + flags |= DRM_MODE_FLAG_NHSYNC; + if (temp & TRANS_DDI_PVSYNC) + flags |= DRM_MODE_FLAG_PVSYNC; + else + flags |= DRM_MODE_FLAG_NVSYNC; + + pipe_config->adjusted_mode.flags |= flags; + pipe_config->pixel_multiplier = 1; +} + static void intel_ddi_destroy(struct drm_encoder *encoder) { /* HDMI has nothing special to destroy, so we can go with this. */ @@ -1318,6 +1340,7 @@ void intel_ddi_init(struct drm_device *dev, enum port port) intel_encoder->disable = intel_disable_ddi; intel_encoder->post_disable = intel_ddi_post_disable; intel_encoder->get_hw_state = intel_ddi_get_hw_state; + intel_encoder->get_config = intel_ddi_get_config; intel_dig_port->port = port; intel_dig_port->port_reversal = I915_READ(DDI_BUF_CTL(port)) & diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 2d90594016d9..4196155cf964 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8067,6 +8067,15 @@ intel_pipe_config_compare(struct drm_device *dev, PIPE_CONF_CHECK_FLAGS(adjusted_mode.flags, DRM_MODE_FLAG_INTERLACE); + PIPE_CONF_CHECK_FLAGS(adjusted_mode.flags, + DRM_MODE_FLAG_PHSYNC); + PIPE_CONF_CHECK_FLAGS(adjusted_mode.flags, + DRM_MODE_FLAG_NHSYNC); + PIPE_CONF_CHECK_FLAGS(adjusted_mode.flags, + DRM_MODE_FLAG_PVSYNC); + PIPE_CONF_CHECK_FLAGS(adjusted_mode.flags, + DRM_MODE_FLAG_NVSYNC); + PIPE_CONF_CHECK_I(requested_mode.hdisplay); PIPE_CONF_CHECK_I(requested_mode.vdisplay); @@ -8159,6 +8168,8 @@ intel_modeset_check_state(struct drm_device *dev) bool enabled = false; bool active = false; + memset(&pipe_config, 0, sizeof(pipe_config)); + DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.base.id); @@ -8172,6 +8183,8 @@ intel_modeset_check_state(struct drm_device *dev) enabled = true; if (encoder->connectors_active) active = true; + if (encoder->get_config) + encoder->get_config(encoder, &pipe_config); } WARN(active != crtc->active, "crtc's computed active state doesn't match tracked active state " @@ -8180,7 +8193,6 @@ intel_modeset_check_state(struct drm_device *dev) "crtc's computed enabled state doesn't match tracked enabled state " "(expected %i, found %i)\n", enabled, crtc->base.enabled); - memset(&pipe_config, 0, sizeof(pipe_config)); pipe_config.cpu_transcoder = crtc->config.cpu_transcoder; active = dev_priv->display.get_pipe_config(crtc, &pipe_config); @@ -9589,8 +9601,10 @@ setup_pipes: pipe = 0; if (encoder->get_hw_state(encoder, &pipe)) { - encoder->base.crtc = - dev_priv->pipe_to_crtc_mapping[pipe]; + crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]); + encoder->base.crtc = &crtc->base; + if (encoder->get_config) + encoder->get_config(encoder, &crtc->config); } else { encoder->base.crtc = NULL; } diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 6ba9f09fe21a..7c5eb2f6208f 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1346,6 +1346,28 @@ static bool intel_dp_get_hw_state(struct intel_encoder *encoder, return true; } +static void intel_dp_get_config(struct intel_encoder *encoder, + struct intel_crtc_config *pipe_config) +{ + struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); + struct drm_i915_private *dev_priv = encoder->base.dev->dev_private; + u32 tmp, flags = 0; + + tmp = I915_READ(intel_dp->output_reg); + + if (tmp & DP_SYNC_HS_HIGH) + flags |= DRM_MODE_FLAG_PHSYNC; + else + flags |= DRM_MODE_FLAG_NHSYNC; + + if (tmp & DP_SYNC_VS_HIGH) + flags |= DRM_MODE_FLAG_PVSYNC; + else + flags |= DRM_MODE_FLAG_NVSYNC; + + pipe_config->adjusted_mode.flags |= flags; +} + static void intel_disable_dp(struct intel_encoder *encoder) { struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); @@ -3184,6 +3206,7 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port) intel_encoder->disable = intel_disable_dp; intel_encoder->post_disable = intel_post_disable_dp; intel_encoder->get_hw_state = intel_dp_get_hw_state; + intel_encoder->get_config = intel_dp_get_config; if (IS_VALLEYVIEW(dev)) intel_encoder->pre_pll_enable = intel_dp_pre_pll_enable; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 9b0af7e27c82..a435ce1d075e 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -139,6 +139,10 @@ struct intel_encoder { * the encoder is active. If the encoder is enabled it also set the pipe * it is connected to in the pipe parameter. */ bool (*get_hw_state)(struct intel_encoder *, enum pipe *pipe); + /* Reconstructs the equivalent mode flags for the current hardware + * state. */ + void (*get_config)(struct intel_encoder *, + struct intel_crtc_config *pipe_config); int crtc_mask; enum hpd_pin hpd_pin; }; diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c index 2c0be924e9a9..9e80d487b5cb 100644 --- a/drivers/gpu/drm/i915/intel_dvo.c +++ b/drivers/gpu/drm/i915/intel_dvo.c @@ -136,6 +136,26 @@ static bool intel_dvo_get_hw_state(struct intel_encoder *encoder, return true; } +static void intel_dvo_get_config(struct intel_encoder *encoder, + struct intel_crtc_config *pipe_config) +{ + struct drm_i915_private *dev_priv = encoder->base.dev->dev_private; + struct intel_dvo *intel_dvo = enc_to_intel_dvo(&encoder->base); + u32 tmp, flags = 0; + + tmp = I915_READ(intel_dvo->dev.dvo_reg); + if (tmp & DVO_HSYNC_ACTIVE_HIGH) + flags |= DRM_MODE_FLAG_PHSYNC; + else + flags |= DRM_MODE_FLAG_NHSYNC; + if (tmp & DVO_VSYNC_ACTIVE_HIGH) + flags |= DRM_MODE_FLAG_PVSYNC; + else + flags |= DRM_MODE_FLAG_NVSYNC; + + pipe_config->adjusted_mode.flags |= flags; +} + static void intel_disable_dvo(struct intel_encoder *encoder) { struct drm_i915_private *dev_priv = encoder->base.dev->dev_private; @@ -447,6 +467,7 @@ void intel_dvo_init(struct drm_device *dev) intel_encoder->disable = intel_disable_dvo; intel_encoder->enable = intel_enable_dvo; intel_encoder->get_hw_state = intel_dvo_get_hw_state; + intel_encoder->get_config = intel_dvo_get_config; intel_connector->get_hw_state = intel_dvo_connector_get_hw_state; /* Now, try to find a controller */ diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 2b727f0d201f..18f8ce0404c6 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -658,6 +658,28 @@ static bool intel_hdmi_get_hw_state(struct intel_encoder *encoder, return true; } +static void intel_hdmi_get_config(struct intel_encoder *encoder, + struct intel_crtc_config *pipe_config) +{ + struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base); + struct drm_i915_private *dev_priv = encoder->base.dev->dev_private; + u32 tmp, flags = 0; + + tmp = I915_READ(intel_hdmi->hdmi_reg); + + if (tmp & SDVO_HSYNC_ACTIVE_HIGH) + flags |= DRM_MODE_FLAG_PHSYNC; + else + flags |= DRM_MODE_FLAG_NHSYNC; + + if (tmp & SDVO_VSYNC_ACTIVE_HIGH) + flags |= DRM_MODE_FLAG_PVSYNC; + else + flags |= DRM_MODE_FLAG_NVSYNC; + + pipe_config->adjusted_mode.flags |= flags; +} + static void intel_enable_hdmi(struct intel_encoder *encoder) { struct drm_device *dev = encoder->base.dev; @@ -1216,6 +1238,7 @@ void intel_hdmi_init(struct drm_device *dev, int hdmi_reg, enum port port) intel_encoder->enable = intel_enable_hdmi; intel_encoder->disable = intel_disable_hdmi; intel_encoder->get_hw_state = intel_hdmi_get_hw_state; + intel_encoder->get_config = intel_hdmi_get_config; if (IS_VALLEYVIEW(dev)) { intel_encoder->pre_enable = intel_hdmi_pre_enable; intel_encoder->pre_pll_enable = intel_hdmi_pre_pll_enable; diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 36fe291172ee..655486099b76 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -86,6 +86,31 @@ static bool intel_lvds_get_hw_state(struct intel_encoder *encoder, return true; } +static void intel_lvds_get_config(struct intel_encoder *encoder, + struct intel_crtc_config *pipe_config) +{ + struct drm_device *dev = encoder->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + u32 lvds_reg, tmp, flags = 0; + + if (HAS_PCH_SPLIT(dev)) + lvds_reg = PCH_LVDS; + else + lvds_reg = LVDS; + + tmp = I915_READ(lvds_reg); + if (tmp & LVDS_HSYNC_POLARITY) + flags |= DRM_MODE_FLAG_NHSYNC; + else + flags |= DRM_MODE_FLAG_PHSYNC; + if (tmp & LVDS_VSYNC_POLARITY) + flags |= DRM_MODE_FLAG_NVSYNC; + else + flags |= DRM_MODE_FLAG_PVSYNC; + + pipe_config->adjusted_mode.flags |= flags; +} + /* The LVDS pin pair needs to be on before the DPLLs are enabled. * This is an exception to the general rule that mode_set doesn't turn * things on. @@ -921,6 +946,7 @@ bool intel_lvds_init(struct drm_device *dev) intel_encoder->compute_config = intel_lvds_compute_config; intel_encoder->disable = intel_disable_lvds; intel_encoder->get_hw_state = intel_lvds_get_hw_state; + intel_encoder->get_config = intel_lvds_get_config; intel_connector->get_hw_state = intel_connector_get_hw_state; intel_connector_attach_encoder(intel_connector, intel_encoder); diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 78f0631b1c43..4d4a3f0cadfb 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -712,6 +712,13 @@ static bool intel_sdvo_set_timing(struct intel_sdvo *intel_sdvo, u8 cmd, intel_sdvo_set_value(intel_sdvo, cmd + 1, &dtd->part2, sizeof(dtd->part2)); } +static bool intel_sdvo_get_timing(struct intel_sdvo *intel_sdvo, u8 cmd, + struct intel_sdvo_dtd *dtd) +{ + return intel_sdvo_get_value(intel_sdvo, cmd, &dtd->part1, sizeof(dtd->part1)) && + intel_sdvo_get_value(intel_sdvo, cmd + 1, &dtd->part2, sizeof(dtd->part2)); +} + static bool intel_sdvo_set_input_timing(struct intel_sdvo *intel_sdvo, struct intel_sdvo_dtd *dtd) { @@ -726,6 +733,13 @@ static bool intel_sdvo_set_output_timing(struct intel_sdvo *intel_sdvo, SDVO_CMD_SET_OUTPUT_TIMINGS_PART1, dtd); } +static bool intel_sdvo_get_input_timing(struct intel_sdvo *intel_sdvo, + struct intel_sdvo_dtd *dtd) +{ + return intel_sdvo_get_timing(intel_sdvo, + SDVO_CMD_GET_INPUT_TIMINGS_PART1, dtd); +} + static bool intel_sdvo_create_preferred_input_timing(struct intel_sdvo *intel_sdvo, uint16_t clock, @@ -1295,6 +1309,33 @@ static bool intel_sdvo_get_hw_state(struct intel_encoder *encoder, return true; } +static void intel_sdvo_get_config(struct intel_encoder *encoder, + struct intel_crtc_config *pipe_config) +{ + struct intel_sdvo *intel_sdvo = to_intel_sdvo(&encoder->base); + struct intel_sdvo_dtd dtd; + u32 flags = 0; + bool ret; + + ret = intel_sdvo_get_input_timing(intel_sdvo, &dtd); + if (!ret) { + DRM_DEBUG_DRIVER("failed to retrieve SDVO DTD\n"); + return; + } + + if (dtd.part2.dtd_flags & DTD_FLAG_HSYNC_POSITIVE) + flags |= DRM_MODE_FLAG_PHSYNC; + else + flags |= DRM_MODE_FLAG_NHSYNC; + + if (dtd.part2.dtd_flags & DTD_FLAG_VSYNC_POSITIVE) + flags |= DRM_MODE_FLAG_PVSYNC; + else + flags |= DRM_MODE_FLAG_NVSYNC; + + pipe_config->adjusted_mode.flags |= flags; +} + static void intel_disable_sdvo(struct intel_encoder *encoder) { struct drm_i915_private *dev_priv = encoder->base.dev->dev_private; @@ -2827,6 +2868,7 @@ bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob) intel_encoder->mode_set = intel_sdvo_mode_set; intel_encoder->enable = intel_enable_sdvo; intel_encoder->get_hw_state = intel_sdvo_get_hw_state; + intel_encoder->get_config = intel_sdvo_get_config; /* In default case sdvo lvds is false */ if (!intel_sdvo_get_capabilities(intel_sdvo, &intel_sdvo->caps)) -- cgit v1.2.3 From eed3b1e55be07a057ee41a24f04abffeda7b885f Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Fri, 17 May 2013 14:41:31 +0200 Subject: s390/pgtable: fix ipte notify bit Dont use the same bit as user referenced. Signed-off-by: Christian Borntraeger Acked-by: Martin Schwidefsky Signed-off-by: Gleb Natapov --- arch/s390/include/asm/pgtable.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h index 4105b8221fdd..0f0de30e3e3f 100644 --- a/arch/s390/include/asm/pgtable.h +++ b/arch/s390/include/asm/pgtable.h @@ -306,7 +306,7 @@ extern unsigned long MODULES_END; #define RCP_HC_BIT 0x00200000UL #define RCP_GR_BIT 0x00040000UL #define RCP_GC_BIT 0x00020000UL -#define RCP_IN_BIT 0x00008000UL /* IPTE notify bit */ +#define RCP_IN_BIT 0x00002000UL /* IPTE notify bit */ /* User dirty / referenced bit for KVM's migration feature */ #define KVM_UR_BIT 0x00008000UL @@ -374,7 +374,7 @@ extern unsigned long MODULES_END; #define RCP_HC_BIT 0x0020000000000000UL #define RCP_GR_BIT 0x0004000000000000UL #define RCP_GC_BIT 0x0002000000000000UL -#define RCP_IN_BIT 0x0000800000000000UL /* IPTE notify bit */ +#define RCP_IN_BIT 0x0000200000000000UL /* IPTE notify bit */ /* User dirty / referenced bit for KVM's migration feature */ #define KVM_UR_BIT 0x0000800000000000UL -- cgit v1.2.3 From dfcf7dc64237dbe1acc2147ad3552f793003874b Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Fri, 17 May 2013 14:41:32 +0200 Subject: s390/kvm: fix psw rewinding in handle_skey The PSW can wrap if the guest has been running in the 24 bit or 31 bit addressing mode. Use __rewind_psw to find the correct address. Signed-off-by: Martin Schwidefsky Signed-off-by: Christian Borntraeger Signed-off-by: Gleb Natapov --- arch/s390/kvm/priv.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c index 6bbd7b5a0bbe..ecc58a694df7 100644 --- a/arch/s390/kvm/priv.c +++ b/arch/s390/kvm/priv.c @@ -105,7 +105,8 @@ static int handle_store_cpu_address(struct kvm_vcpu *vcpu) static int handle_skey(struct kvm_vcpu *vcpu) { vcpu->stat.instruction_storage_key++; - vcpu->arch.sie_block->gpsw.addr -= 4; + vcpu->arch.sie_block->gpsw.addr = + __rewind_psw(vcpu->arch.sie_block->gpsw, 4); VCPU_EVENT(vcpu, 4, "%s", "retrying storage key operation"); return 0; } -- cgit v1.2.3 From 0d0dafc1e48fd254c22f75738def870a7ffd2c3e Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Fri, 17 May 2013 14:41:33 +0200 Subject: s390/kvm: rename RCP_xxx defines to PGSTE_xxx The RCP byte is a part of the PGSTE value, the existing RCP_xxx names are inaccurate. As the defines describe bits and pieces of the PGSTE, the names should start with PGSTE_. The KVM_UR_BIT and KVM_UC_BIT are part of the PGSTE as well, give them better names as well. Signed-off-by: Martin Schwidefsky Signed-off-by: Christian Borntraeger Signed-off-by: Gleb Natapov --- arch/s390/include/asm/pgtable.h | 82 ++++++++++++++++++++--------------------- arch/s390/mm/pgtable.c | 2 +- 2 files changed, 40 insertions(+), 44 deletions(-) diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h index 0f0de30e3e3f..1fc68d97be9d 100644 --- a/arch/s390/include/asm/pgtable.h +++ b/arch/s390/include/asm/pgtable.h @@ -299,18 +299,16 @@ extern unsigned long MODULES_END; #define _SEGMENT_ENTRY_EMPTY (_SEGMENT_ENTRY_INV) /* Page status table bits for virtualization */ -#define RCP_ACC_BITS 0xf0000000UL -#define RCP_FP_BIT 0x08000000UL -#define RCP_PCL_BIT 0x00800000UL -#define RCP_HR_BIT 0x00400000UL -#define RCP_HC_BIT 0x00200000UL -#define RCP_GR_BIT 0x00040000UL -#define RCP_GC_BIT 0x00020000UL -#define RCP_IN_BIT 0x00002000UL /* IPTE notify bit */ - -/* User dirty / referenced bit for KVM's migration feature */ -#define KVM_UR_BIT 0x00008000UL -#define KVM_UC_BIT 0x00004000UL +#define PGSTE_ACC_BITS 0xf0000000UL +#define PGSTE_FP_BIT 0x08000000UL +#define PGSTE_PCL_BIT 0x00800000UL +#define PGSTE_HR_BIT 0x00400000UL +#define PGSTE_HC_BIT 0x00200000UL +#define PGSTE_GR_BIT 0x00040000UL +#define PGSTE_GC_BIT 0x00020000UL +#define PGSTE_UR_BIT 0x00008000UL +#define PGSTE_UC_BIT 0x00004000UL /* user dirty (migration) */ +#define PGSTE_IN_BIT 0x00002000UL /* IPTE notify bit */ #else /* CONFIG_64BIT */ @@ -367,18 +365,16 @@ extern unsigned long MODULES_END; | _SEGMENT_ENTRY_SPLIT | _SEGMENT_ENTRY_CO) /* Page status table bits for virtualization */ -#define RCP_ACC_BITS 0xf000000000000000UL -#define RCP_FP_BIT 0x0800000000000000UL -#define RCP_PCL_BIT 0x0080000000000000UL -#define RCP_HR_BIT 0x0040000000000000UL -#define RCP_HC_BIT 0x0020000000000000UL -#define RCP_GR_BIT 0x0004000000000000UL -#define RCP_GC_BIT 0x0002000000000000UL -#define RCP_IN_BIT 0x0000200000000000UL /* IPTE notify bit */ - -/* User dirty / referenced bit for KVM's migration feature */ -#define KVM_UR_BIT 0x0000800000000000UL -#define KVM_UC_BIT 0x0000400000000000UL +#define PGSTE_ACC_BITS 0xf000000000000000UL +#define PGSTE_FP_BIT 0x0800000000000000UL +#define PGSTE_PCL_BIT 0x0080000000000000UL +#define PGSTE_HR_BIT 0x0040000000000000UL +#define PGSTE_HC_BIT 0x0020000000000000UL +#define PGSTE_GR_BIT 0x0004000000000000UL +#define PGSTE_GC_BIT 0x0002000000000000UL +#define PGSTE_UR_BIT 0x0000800000000000UL +#define PGSTE_UC_BIT 0x0000400000000000UL /* user dirty (migration) */ +#define PGSTE_IN_BIT 0x0000200000000000UL /* IPTE notify bit */ #endif /* CONFIG_64BIT */ @@ -618,8 +614,8 @@ static inline pgste_t pgste_get_lock(pte_t *ptep) asm( " lg %0,%2\n" "0: lgr %1,%0\n" - " nihh %0,0xff7f\n" /* clear RCP_PCL_BIT in old */ - " oihh %1,0x0080\n" /* set RCP_PCL_BIT in new */ + " nihh %0,0xff7f\n" /* clear PCL bit in old */ + " oihh %1,0x0080\n" /* set PCL bit in new */ " csg %0,%1,%2\n" " jl 0b\n" : "=&d" (old), "=&d" (new), "=Q" (ptep[PTRS_PER_PTE]) @@ -632,7 +628,7 @@ static inline void pgste_set_unlock(pte_t *ptep, pgste_t pgste) { #ifdef CONFIG_PGSTE asm( - " nihh %1,0xff7f\n" /* clear RCP_PCL_BIT */ + " nihh %1,0xff7f\n" /* clear PCL bit */ " stg %1,%0\n" : "=Q" (ptep[PTRS_PER_PTE]) : "d" (pgste_val(pgste)), "Q" (ptep[PTRS_PER_PTE]) : "cc"); @@ -657,14 +653,14 @@ static inline pgste_t pgste_update_all(pte_t *ptep, pgste_t pgste) else if (bits) page_reset_referenced(address); /* Transfer page changed & referenced bit to guest bits in pgste */ - pgste_val(pgste) |= bits << 48; /* RCP_GR_BIT & RCP_GC_BIT */ + pgste_val(pgste) |= bits << 48; /* GR bit & GC bit */ /* Get host changed & referenced bits from pgste */ - bits |= (pgste_val(pgste) & (RCP_HR_BIT | RCP_HC_BIT)) >> 52; + bits |= (pgste_val(pgste) & (PGSTE_HR_BIT | PGSTE_HC_BIT)) >> 52; /* Transfer page changed & referenced bit to kvm user bits */ - pgste_val(pgste) |= bits << 45; /* KVM_UR_BIT & KVM_UC_BIT */ + pgste_val(pgste) |= bits << 45; /* PGSTE_UR_BIT & PGSTE_UC_BIT */ /* Clear relevant host bits in pgste. */ - pgste_val(pgste) &= ~(RCP_HR_BIT | RCP_HC_BIT); - pgste_val(pgste) &= ~(RCP_ACC_BITS | RCP_FP_BIT); + pgste_val(pgste) &= ~(PGSTE_HR_BIT | PGSTE_HC_BIT); + pgste_val(pgste) &= ~(PGSTE_ACC_BITS | PGSTE_FP_BIT); /* Copy page access key and fetch protection bit to pgste */ pgste_val(pgste) |= (unsigned long) (skey & (_PAGE_ACC_BITS | _PAGE_FP_BIT)) << 56; @@ -685,15 +681,15 @@ static inline pgste_t pgste_update_young(pte_t *ptep, pgste_t pgste) /* Get referenced bit from storage key */ young = page_reset_referenced(pte_val(*ptep) & PAGE_MASK); if (young) - pgste_val(pgste) |= RCP_GR_BIT; + pgste_val(pgste) |= PGSTE_GR_BIT; /* Get host referenced bit from pgste */ - if (pgste_val(pgste) & RCP_HR_BIT) { - pgste_val(pgste) &= ~RCP_HR_BIT; + if (pgste_val(pgste) & PGSTE_HR_BIT) { + pgste_val(pgste) &= ~PGSTE_HR_BIT; young = 1; } /* Transfer referenced bit to kvm user bits and pte */ if (young) { - pgste_val(pgste) |= KVM_UR_BIT; + pgste_val(pgste) |= PGSTE_UR_BIT; pte_val(*ptep) |= _PAGE_SWR; } #endif @@ -712,7 +708,7 @@ static inline void pgste_set_key(pte_t *ptep, pgste_t pgste, pte_t entry) okey = nkey = page_get_storage_key(address); nkey &= ~(_PAGE_ACC_BITS | _PAGE_FP_BIT); /* Set page access key and fetch protection bit from pgste */ - nkey |= (pgste_val(pgste) & (RCP_ACC_BITS | RCP_FP_BIT)) >> 56; + nkey |= (pgste_val(pgste) & (PGSTE_ACC_BITS | PGSTE_FP_BIT)) >> 56; if (okey != nkey) page_set_storage_key(address, nkey, 0); #endif @@ -801,8 +797,8 @@ static inline pgste_t pgste_ipte_notify(struct mm_struct *mm, pte_t *ptep, pgste_t pgste) { #ifdef CONFIG_PGSTE - if (pgste_val(pgste) & RCP_IN_BIT) { - pgste_val(pgste) &= ~RCP_IN_BIT; + if (pgste_val(pgste) & PGSTE_IN_BIT) { + pgste_val(pgste) &= ~PGSTE_IN_BIT; gmap_do_ipte_notify(mm, addr, ptep); } #endif @@ -970,8 +966,8 @@ static inline int ptep_test_and_clear_user_dirty(struct mm_struct *mm, if (mm_has_pgste(mm)) { pgste = pgste_get_lock(ptep); pgste = pgste_update_all(ptep, pgste); - dirty = !!(pgste_val(pgste) & KVM_UC_BIT); - pgste_val(pgste) &= ~KVM_UC_BIT; + dirty = !!(pgste_val(pgste) & PGSTE_UC_BIT); + pgste_val(pgste) &= ~PGSTE_UC_BIT; pgste_set_unlock(ptep, pgste); return dirty; } @@ -990,8 +986,8 @@ static inline int ptep_test_and_clear_user_young(struct mm_struct *mm, if (mm_has_pgste(mm)) { pgste = pgste_get_lock(ptep); pgste = pgste_update_young(ptep, pgste); - young = !!(pgste_val(pgste) & KVM_UR_BIT); - pgste_val(pgste) &= ~KVM_UR_BIT; + young = !!(pgste_val(pgste) & PGSTE_UR_BIT); + pgste_val(pgste) &= ~PGSTE_UR_BIT; pgste_set_unlock(ptep, pgste); } return young; diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c index 7805ddca833d..5ca75683c654 100644 --- a/arch/s390/mm/pgtable.c +++ b/arch/s390/mm/pgtable.c @@ -690,7 +690,7 @@ int gmap_ipte_notify(struct gmap *gmap, unsigned long start, unsigned long len) entry = *ptep; if ((pte_val(entry) & (_PAGE_INVALID | _PAGE_RO)) == 0) { pgste = pgste_get_lock(ptep); - pgste_val(pgste) |= RCP_IN_BIT; + pgste_val(pgste) |= PGSTE_IN_BIT; pgste_set_unlock(ptep, pgste); start += PAGE_SIZE; len -= PAGE_SIZE; -- cgit v1.2.3 From 95d38fd0bcf1996082f5f8762e6f1c849755e0c6 Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Fri, 17 May 2013 14:41:34 +0200 Subject: s390/kvm: Mark if a cpu is in SIE Lets track in a private bit if the sie control block is active. We want to track this as closely as possible, so we also have to instrument the interrupt and program check handler. Lets use the existing HANDLE_SIE_INTERCEPT macro. Signed-off-by: Christian Borntraeger Acked-by: Martin Schwidefsky Signed-off-by: Gleb Natapov --- arch/s390/include/asm/kvm_host.h | 5 ++++- arch/s390/kernel/asm-offsets.c | 2 ++ arch/s390/kernel/entry64.S | 10 +++++++--- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index 16bd5d169cdb..962b92e6cf00 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h @@ -68,7 +68,10 @@ struct sca_block { struct kvm_s390_sie_block { atomic_t cpuflags; /* 0x0000 */ __u32 prefix; /* 0x0004 */ - __u8 reserved8[32]; /* 0x0008 */ + __u8 reserved08[4]; /* 0x0008 */ +#define PROG_IN_SIE (1<<0) + __u32 prog0c; /* 0x000c */ + __u8 reserved10[24]; /* 0x0010 */ __u64 cputm; /* 0x0028 */ __u64 ckc; /* 0x0030 */ __u64 epoch; /* 0x0038 */ diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c index 7a82f9f70100..6456bbe1fbb1 100644 --- a/arch/s390/kernel/asm-offsets.c +++ b/arch/s390/kernel/asm-offsets.c @@ -7,6 +7,7 @@ #define ASM_OFFSETS_C #include +#include #include #include #include @@ -161,6 +162,7 @@ int main(void) DEFINE(__LC_PGM_TDB, offsetof(struct _lowcore, pgm_tdb)); DEFINE(__THREAD_trap_tdb, offsetof(struct task_struct, thread.trap_tdb)); DEFINE(__GMAP_ASCE, offsetof(struct gmap, asce)); + DEFINE(__SIE_PROG0C, offsetof(struct kvm_s390_sie_block, prog0c)); #endif /* CONFIG_32BIT */ return 0; } diff --git a/arch/s390/kernel/entry64.S b/arch/s390/kernel/entry64.S index 4c17eece707e..c2e81b4ea42c 100644 --- a/arch/s390/kernel/entry64.S +++ b/arch/s390/kernel/entry64.S @@ -84,7 +84,7 @@ _TIF_EXIT_SIE = (_TIF_SIGPENDING | _TIF_NEED_RESCHED | _TIF_MCCK_PENDING) .macro HANDLE_SIE_INTERCEPT scratch,pgmcheck #if defined(CONFIG_KVM) || defined(CONFIG_KVM_MODULE) tmhh %r8,0x0001 # interrupting from user ? - jnz .+42 + jnz .+52 lgr \scratch,%r9 slg \scratch,BASED(.Lsie_loop) clg \scratch,BASED(.Lsie_length) @@ -92,12 +92,14 @@ _TIF_EXIT_SIE = (_TIF_SIGPENDING | _TIF_NEED_RESCHED | _TIF_MCCK_PENDING) # Some program interrupts are suppressing (e.g. protection). # We must also check the instruction after SIE in that case. # do_protection_exception will rewind to rewind_pad - jh .+22 + jh .+32 .else - jhe .+22 + jhe .+32 .endif lg %r9,BASED(.Lsie_loop) LPP BASED(.Lhost_id) # set host id + lg %r14,__SF_EMPTY(%r15) # get control block pointer + ni __SIE_PROG0C+3(%r14),0xfe # no longer in SIE #endif .endm @@ -956,10 +958,12 @@ sie_loop: lctlg %c1,%c1,__GMAP_ASCE(%r14) # load primary asce sie_gmap: lg %r14,__SF_EMPTY(%r15) # get control block pointer + oi __SIE_PROG0C+3(%r14),1 # we are in SIE now LPP __SF_EMPTY(%r15) # set guest id sie 0(%r14) sie_done: LPP __SF_EMPTY+16(%r15) # set host id + ni __SIE_PROG0C+3(%r14),0xfe # no longer in SIE lg %r14,__LC_THREAD_INFO # pointer thread_info struct sie_exit: lctlg %c1,%c1,__LC_USER_ASCE # load primary asce -- cgit v1.2.3 From 49b99e1e0dedbd6cc93b2d2776b60fb7151ff3d7 Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Fri, 17 May 2013 14:41:35 +0200 Subject: s390/kvm: Provide a way to prevent reentering SIE Lets provide functions to prevent KVM from reentering SIE and to kick cpus out of SIE. We cannot use the common kvm_vcpu_kick code, since we need to kick out guests in places that hold architecture specific locks (e.g. pgste lock) which might be necessary on the other cpus - so no waiting possible. So lets provide a bit in a private field of the sie control block that acts as a gate keeper, after we claimed we are in SIE. Please note that we do not reuse prog0c, since we want to access that bit without atomic ops. Signed-off-by: Christian Borntraeger Acked-by: Martin Schwidefsky Signed-off-by: Gleb Natapov --- arch/s390/include/asm/kvm_host.h | 5 ++++- arch/s390/kernel/asm-offsets.c | 1 + arch/s390/kernel/entry64.S | 4 +++- arch/s390/kvm/kvm-s390.c | 28 ++++++++++++++++++++++++++++ arch/s390/kvm/kvm-s390.h | 4 ++++ 5 files changed, 40 insertions(+), 2 deletions(-) diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index 962b92e6cf00..9a809f935580 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h @@ -71,7 +71,10 @@ struct kvm_s390_sie_block { __u8 reserved08[4]; /* 0x0008 */ #define PROG_IN_SIE (1<<0) __u32 prog0c; /* 0x000c */ - __u8 reserved10[24]; /* 0x0010 */ + __u8 reserved10[16]; /* 0x0010 */ +#define PROG_BLOCK_SIE 0x00000001 + atomic_t prog20; /* 0x0020 */ + __u8 reserved24[4]; /* 0x0024 */ __u64 cputm; /* 0x0028 */ __u64 ckc; /* 0x0030 */ __u64 epoch; /* 0x0038 */ diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c index 6456bbe1fbb1..78db6338695d 100644 --- a/arch/s390/kernel/asm-offsets.c +++ b/arch/s390/kernel/asm-offsets.c @@ -163,6 +163,7 @@ int main(void) DEFINE(__THREAD_trap_tdb, offsetof(struct task_struct, thread.trap_tdb)); DEFINE(__GMAP_ASCE, offsetof(struct gmap, asce)); DEFINE(__SIE_PROG0C, offsetof(struct kvm_s390_sie_block, prog0c)); + DEFINE(__SIE_PROG20, offsetof(struct kvm_s390_sie_block, prog20)); #endif /* CONFIG_32BIT */ return 0; } diff --git a/arch/s390/kernel/entry64.S b/arch/s390/kernel/entry64.S index c2e81b4ea42c..c7daeefb9864 100644 --- a/arch/s390/kernel/entry64.S +++ b/arch/s390/kernel/entry64.S @@ -958,7 +958,9 @@ sie_loop: lctlg %c1,%c1,__GMAP_ASCE(%r14) # load primary asce sie_gmap: lg %r14,__SF_EMPTY(%r15) # get control block pointer - oi __SIE_PROG0C+3(%r14),1 # we are in SIE now + oi __SIE_PROG0C+3(%r14),1 # we are going into SIE now + tm __SIE_PROG20+3(%r14),1 # last exit... + jnz sie_done LPP __SF_EMPTY(%r15) # set guest id sie 0(%r14) sie_done: diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index c1c7c683fa26..ef4ef21f2c73 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -454,6 +454,34 @@ int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu) return 0; } +void s390_vcpu_block(struct kvm_vcpu *vcpu) +{ + atomic_set_mask(PROG_BLOCK_SIE, &vcpu->arch.sie_block->prog20); +} + +void s390_vcpu_unblock(struct kvm_vcpu *vcpu) +{ + atomic_clear_mask(PROG_BLOCK_SIE, &vcpu->arch.sie_block->prog20); +} + +/* + * Kick a guest cpu out of SIE and wait until SIE is not running. + * If the CPU is not running (e.g. waiting as idle) the function will + * return immediately. */ +void exit_sie(struct kvm_vcpu *vcpu) +{ + atomic_set_mask(CPUSTAT_STOP_INT, &vcpu->arch.sie_block->cpuflags); + while (vcpu->arch.sie_block->prog0c & PROG_IN_SIE) + cpu_relax(); +} + +/* Kick a guest cpu out of SIE and prevent SIE-reentry */ +void exit_sie_sync(struct kvm_vcpu *vcpu) +{ + s390_vcpu_block(vcpu); + exit_sie(vcpu); +} + int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu) { /* kvm common code refers to this, but never calls it */ diff --git a/arch/s390/kvm/kvm-s390.h b/arch/s390/kvm/kvm-s390.h index efc14f687265..7a8abfd26a0f 100644 --- a/arch/s390/kvm/kvm-s390.h +++ b/arch/s390/kvm/kvm-s390.h @@ -133,6 +133,10 @@ int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu); /* implemented in kvm-s390.c */ int kvm_s390_vcpu_store_status(struct kvm_vcpu *vcpu, unsigned long addr); +void s390_vcpu_block(struct kvm_vcpu *vcpu); +void s390_vcpu_unblock(struct kvm_vcpu *vcpu); +void exit_sie(struct kvm_vcpu *vcpu); +void exit_sie_sync(struct kvm_vcpu *vcpu); /* implemented in diag.c */ int kvm_s390_handle_diag(struct kvm_vcpu *vcpu); -- cgit v1.2.3 From 2c70fe4416d5f6d092b20ebf7d7654835e09c109 Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Fri, 17 May 2013 14:41:36 +0200 Subject: s390/kvm: Kick guests out of sie if prefix page host pte is touched The guest prefix pages must be mapped writeable all the time while SIE is running, otherwise the guest might see random behaviour. (pinned at the pte level) Turns out that mlocking is not enough, the page table entry (not the page) might change or become r/o. This patch uses the gmap notifiers to kick guest cpus out of SIE. Signed-off-by: Christian Borntraeger Acked-by: Martin Schwidefsky Signed-off-by: Gleb Natapov --- arch/s390/include/asm/pgtable.h | 1 + arch/s390/kvm/intercept.c | 39 ++------------------------------ arch/s390/kvm/kvm-s390.c | 49 +++++++++++++++++++++++++++++++++++++++++ arch/s390/kvm/kvm-s390.h | 1 + 4 files changed, 53 insertions(+), 37 deletions(-) diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h index 1fc68d97be9d..1d0ad7d2d29a 100644 --- a/arch/s390/include/asm/pgtable.h +++ b/arch/s390/include/asm/pgtable.h @@ -739,6 +739,7 @@ struct gmap { struct mm_struct *mm; unsigned long *table; unsigned long asce; + void *private; struct list_head crst_list; }; diff --git a/arch/s390/kvm/intercept.c b/arch/s390/kvm/intercept.c index b7d1b2edeeb3..f0b8be0cc08d 100644 --- a/arch/s390/kvm/intercept.c +++ b/arch/s390/kvm/intercept.c @@ -174,47 +174,12 @@ static int handle_stop(struct kvm_vcpu *vcpu) static int handle_validity(struct kvm_vcpu *vcpu) { - unsigned long vmaddr; int viwhy = vcpu->arch.sie_block->ipb >> 16; - int rc; vcpu->stat.exit_validity++; trace_kvm_s390_intercept_validity(vcpu, viwhy); - if (viwhy == 0x37) { - vmaddr = gmap_fault(vcpu->arch.sie_block->prefix, - vcpu->arch.gmap); - if (IS_ERR_VALUE(vmaddr)) { - rc = -EOPNOTSUPP; - goto out; - } - rc = fault_in_pages_writeable((char __user *) vmaddr, - PAGE_SIZE); - if (rc) { - /* user will receive sigsegv, exit to user */ - rc = -EOPNOTSUPP; - goto out; - } - vmaddr = gmap_fault(vcpu->arch.sie_block->prefix + PAGE_SIZE, - vcpu->arch.gmap); - if (IS_ERR_VALUE(vmaddr)) { - rc = -EOPNOTSUPP; - goto out; - } - rc = fault_in_pages_writeable((char __user *) vmaddr, - PAGE_SIZE); - if (rc) { - /* user will receive sigsegv, exit to user */ - rc = -EOPNOTSUPP; - goto out; - } - } else - rc = -EOPNOTSUPP; - -out: - if (rc) - VCPU_EVENT(vcpu, 2, "unhandled validity intercept code %d", - viwhy); - return rc; + WARN_ONCE(true, "kvm: unhandled validity intercept 0x%x\n", viwhy); + return -EOPNOTSUPP; } static int handle_instruction(struct kvm_vcpu *vcpu) diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index ef4ef21f2c73..08227c1e816f 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -84,6 +84,7 @@ struct kvm_stats_debugfs_item debugfs_entries[] = { }; static unsigned long long *facilities; +static struct gmap_notifier gmap_notifier; /* Section: not file related */ int kvm_arch_hardware_enable(void *garbage) @@ -96,13 +97,18 @@ void kvm_arch_hardware_disable(void *garbage) { } +static void kvm_gmap_notifier(struct gmap *gmap, unsigned long address); + int kvm_arch_hardware_setup(void) { + gmap_notifier.notifier_call = kvm_gmap_notifier; + gmap_register_ipte_notifier(&gmap_notifier); return 0; } void kvm_arch_hardware_unsetup(void) { + gmap_unregister_ipte_notifier(&gmap_notifier); } void kvm_arch_check_processor_compat(void *rtn) @@ -239,6 +245,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) kvm->arch.gmap = gmap_alloc(current->mm); if (!kvm->arch.gmap) goto out_nogmap; + kvm->arch.gmap->private = kvm; } kvm->arch.css_support = 0; @@ -309,6 +316,7 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu) vcpu->arch.gmap = gmap_alloc(current->mm); if (!vcpu->arch.gmap) return -ENOMEM; + vcpu->arch.gmap->private = vcpu->kvm; return 0; } @@ -482,6 +490,22 @@ void exit_sie_sync(struct kvm_vcpu *vcpu) exit_sie(vcpu); } +static void kvm_gmap_notifier(struct gmap *gmap, unsigned long address) +{ + int i; + struct kvm *kvm = gmap->private; + struct kvm_vcpu *vcpu; + + kvm_for_each_vcpu(i, vcpu, kvm) { + /* match against both prefix pages */ + if (vcpu->arch.sie_block->prefix == (address & ~0x1000UL)) { + VCPU_EVENT(vcpu, 2, "gmap notifier for %lx", address); + kvm_make_request(KVM_REQ_MMU_RELOAD, vcpu); + exit_sie_sync(vcpu); + } + } +} + int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu) { /* kvm common code refers to this, but never calls it */ @@ -634,6 +658,27 @@ int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu, return -EINVAL; /* not implemented yet */ } +static int kvm_s390_handle_requests(struct kvm_vcpu *vcpu) +{ + /* + * We use MMU_RELOAD just to re-arm the ipte notifier for the + * guest prefix page. gmap_ipte_notify will wait on the ptl lock. + * This ensures that the ipte instruction for this request has + * already finished. We might race against a second unmapper that + * wants to set the blocking bit. Lets just retry the request loop. + */ + while (kvm_check_request(KVM_REQ_MMU_RELOAD, vcpu)) { + int rc; + rc = gmap_ipte_notify(vcpu->arch.gmap, + vcpu->arch.sie_block->prefix, + PAGE_SIZE * 2); + if (rc) + return rc; + s390_vcpu_unblock(vcpu); + } + return 0; +} + static int __vcpu_run(struct kvm_vcpu *vcpu) { int rc; @@ -649,6 +694,10 @@ static int __vcpu_run(struct kvm_vcpu *vcpu) if (!kvm_is_ucontrol(vcpu->kvm)) kvm_s390_deliver_pending_interrupts(vcpu); + rc = kvm_s390_handle_requests(vcpu); + if (rc) + return rc; + vcpu->arch.sie_block->icptcode = 0; preempt_disable(); kvm_guest_enter(); diff --git a/arch/s390/kvm/kvm-s390.h b/arch/s390/kvm/kvm-s390.h index 7a8abfd26a0f..269b523d0f6e 100644 --- a/arch/s390/kvm/kvm-s390.h +++ b/arch/s390/kvm/kvm-s390.h @@ -63,6 +63,7 @@ static inline void kvm_s390_set_prefix(struct kvm_vcpu *vcpu, u32 prefix) { vcpu->arch.sie_block->prefix = prefix & 0x7fffe000u; vcpu->arch.sie_block->ihcpu = 0xffff; + kvm_make_request(KVM_REQ_MMU_RELOAD, vcpu); } static inline u64 kvm_s390_get_base_disp_s(struct kvm_vcpu *vcpu) -- cgit v1.2.3 From 7c470539c95630c1f2a10f109e96f249730b75eb Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Fri, 17 May 2013 14:41:37 +0200 Subject: s390/kvm: avoid automatic sie reentry Do not automatically restart the sie instruction in entry64.S after an interrupt, return to the caller with a reason code instead. That allows to deal with RCU and other conditions in C code. Signed-off-by: Martin Schwidefsky Signed-off-by: Christian Borntraeger Signed-off-by: Gleb Natapov --- arch/s390/kernel/entry64.S | 76 ++++++++++++++++++++-------------------------- arch/s390/kvm/kvm-s390.c | 4 ++- 2 files changed, 36 insertions(+), 44 deletions(-) diff --git a/arch/s390/kernel/entry64.S b/arch/s390/kernel/entry64.S index c7daeefb9864..51d99acc16fd 100644 --- a/arch/s390/kernel/entry64.S +++ b/arch/s390/kernel/entry64.S @@ -47,7 +47,6 @@ _TIF_WORK_INT = (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_NEED_RESCHED | \ _TIF_MCCK_PENDING) _TIF_TRACE = (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | _TIF_SECCOMP | \ _TIF_SYSCALL_TRACEPOINT) -_TIF_EXIT_SIE = (_TIF_SIGPENDING | _TIF_NEED_RESCHED | _TIF_MCCK_PENDING) #define BASED(name) name-system_call(%r13) @@ -81,25 +80,27 @@ _TIF_EXIT_SIE = (_TIF_SIGPENDING | _TIF_NEED_RESCHED | _TIF_MCCK_PENDING) #endif .endm - .macro HANDLE_SIE_INTERCEPT scratch,pgmcheck + .macro HANDLE_SIE_INTERCEPT scratch,reason #if defined(CONFIG_KVM) || defined(CONFIG_KVM_MODULE) tmhh %r8,0x0001 # interrupting from user ? - jnz .+52 + jnz .+62 lgr \scratch,%r9 - slg \scratch,BASED(.Lsie_loop) - clg \scratch,BASED(.Lsie_length) - .if \pgmcheck + slg \scratch,BASED(.Lsie_critical) + clg \scratch,BASED(.Lsie_critical_length) + .if \reason==1 # Some program interrupts are suppressing (e.g. protection). # We must also check the instruction after SIE in that case. # do_protection_exception will rewind to rewind_pad - jh .+32 + jh .+42 .else - jhe .+32 + jhe .+42 .endif - lg %r9,BASED(.Lsie_loop) - LPP BASED(.Lhost_id) # set host id - lg %r14,__SF_EMPTY(%r15) # get control block pointer + lg %r14,__SF_EMPTY(%r15) # get control block pointer + LPP __SF_EMPTY+16(%r15) # set host id ni __SIE_PROG0C+3(%r14),0xfe # no longer in SIE + lctlg %c1,%c1,__LC_USER_ASCE # load primary asce + larl %r9,sie_exit # skip forward to sie_exit + mvi __SF_EMPTY+31(%r15),\reason # set exit reason #endif .endm @@ -452,7 +453,7 @@ ENTRY(io_int_handler) lg %r12,__LC_THREAD_INFO larl %r13,system_call lmg %r8,%r9,__LC_IO_OLD_PSW - HANDLE_SIE_INTERCEPT %r14,0 + HANDLE_SIE_INTERCEPT %r14,2 SWITCH_ASYNC __LC_SAVE_AREA_ASYNC,__LC_ASYNC_STACK,STACK_SHIFT tmhh %r8,0x0001 # interrupting from user? jz io_skip @@ -597,7 +598,7 @@ ENTRY(ext_int_handler) lg %r12,__LC_THREAD_INFO larl %r13,system_call lmg %r8,%r9,__LC_EXT_OLD_PSW - HANDLE_SIE_INTERCEPT %r14,0 + HANDLE_SIE_INTERCEPT %r14,3 SWITCH_ASYNC __LC_SAVE_AREA_ASYNC,__LC_ASYNC_STACK,STACK_SHIFT tmhh %r8,0x0001 # interrupting from user ? jz ext_skip @@ -645,7 +646,7 @@ ENTRY(mcck_int_handler) lg %r12,__LC_THREAD_INFO larl %r13,system_call lmg %r8,%r9,__LC_MCK_OLD_PSW - HANDLE_SIE_INTERCEPT %r14,0 + HANDLE_SIE_INTERCEPT %r14,4 tm __LC_MCCK_CODE,0x80 # system damage? jo mcck_panic # yes -> rest of mcck code invalid lghi %r14,__LC_CPU_TIMER_SAVE_AREA @@ -939,19 +940,8 @@ ENTRY(sie64a) stmg %r6,%r14,__SF_GPRS(%r15) # save kernel registers stg %r2,__SF_EMPTY(%r15) # save control block pointer stg %r3,__SF_EMPTY+8(%r15) # save guest register save area - xc __SF_EMPTY+16(8,%r15),__SF_EMPTY+16(%r15) # host id == 0 + xc __SF_EMPTY+16(16,%r15),__SF_EMPTY+16(%r15) # host id & reason lmg %r0,%r13,0(%r3) # load guest gprs 0-13 -# some program checks are suppressing. C code (e.g. do_protection_exception) -# will rewind the PSW by the ILC, which is 4 bytes in case of SIE. Other -# instructions in the sie_loop should not cause program interrupts. So -# lets use a nop (47 00 00 00) as a landing pad. -# See also HANDLE_SIE_INTERCEPT -rewind_pad: - nop 0 -sie_loop: - lg %r14,__LC_THREAD_INFO # pointer thread_info struct - tm __TI_flags+7(%r14),_TIF_EXIT_SIE - jnz sie_exit lg %r14,__LC_GMAP # get gmap pointer ltgr %r14,%r14 jz sie_gmap @@ -966,33 +956,33 @@ sie_gmap: sie_done: LPP __SF_EMPTY+16(%r15) # set host id ni __SIE_PROG0C+3(%r14),0xfe # no longer in SIE - lg %r14,__LC_THREAD_INFO # pointer thread_info struct -sie_exit: lctlg %c1,%c1,__LC_USER_ASCE # load primary asce +# some program checks are suppressing. C code (e.g. do_protection_exception) +# will rewind the PSW by the ILC, which is 4 bytes in case of SIE. Other +# instructions beween sie64a and sie_done should not cause program +# interrupts. So lets use a nop (47 00 00 00) as a landing pad. +# See also HANDLE_SIE_INTERCEPT +rewind_pad: + nop 0 +sie_exit: lg %r14,__SF_EMPTY+8(%r15) # load guest register save area stmg %r0,%r13,0(%r14) # save guest gprs 0-13 lmg %r6,%r14,__SF_GPRS(%r15) # restore kernel registers - lghi %r2,0 + lg %r2,__SF_EMPTY+24(%r15) # return exit reason code br %r14 sie_fault: - lctlg %c1,%c1,__LC_USER_ASCE # load primary asce - lg %r14,__LC_THREAD_INFO # pointer thread_info struct - lg %r14,__SF_EMPTY+8(%r15) # load guest register save area - stmg %r0,%r13,0(%r14) # save guest gprs 0-13 - lmg %r6,%r14,__SF_GPRS(%r15) # restore kernel registers - lghi %r2,-EFAULT - br %r14 + lghi %r14,-EFAULT + stg %r14,__SF_EMPTY+24(%r15) # set exit reason code + j sie_exit .align 8 -.Lsie_loop: - .quad sie_loop -.Lsie_length: - .quad sie_done - sie_loop -.Lhost_id: - .quad 0 +.Lsie_critical: + .quad sie_gmap +.Lsie_critical_length: + .quad sie_done - sie_gmap EX_TABLE(rewind_pad,sie_fault) - EX_TABLE(sie_loop,sie_fault) + EX_TABLE(sie_exit,sie_fault) #endif .section .rodata, "a" diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 08227c1e816f..93444c4dae5a 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -707,7 +707,9 @@ static int __vcpu_run(struct kvm_vcpu *vcpu) trace_kvm_s390_sie_enter(vcpu, atomic_read(&vcpu->arch.sie_block->cpuflags)); rc = sie64a(vcpu->arch.sie_block, vcpu->run->s.regs.gprs); - if (rc) { + if (rc > 0) + rc = 0; + if (rc < 0) { if (kvm_is_ucontrol(vcpu->kvm)) { rc = SIE_INTERCEPT_UCONTROL; } else { -- cgit v1.2.3 From f8b5ff2cff232df052955ef975f7219e1faa217f Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Fri, 17 May 2013 14:41:38 +0200 Subject: s390: fix gmap_ipte_notifier vs. software dirty pages On heavy paging load some guest cpus started to loop in gmap_ipte_notify. This was visible as stalled cpus inside the guest. The gmap_ipte_notifier tries to map a user page and then made sure that the pte is valid and writable. Turns out that with the software change bit tracking the pte can become read-only (and only software writable) if the page is clean. Since we loop in this code, the page would stay clean and, therefore, be never writable again. Let us just use fixup_user_fault, that guarantees to call handle_mm_fault. Signed-off-by: Christian Borntraeger Acked-by: Martin Schwidefsky Signed-off-by: Gleb Natapov --- arch/s390/mm/pgtable.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c index 5ca75683c654..1e0c438dbd65 100644 --- a/arch/s390/mm/pgtable.c +++ b/arch/s390/mm/pgtable.c @@ -677,8 +677,7 @@ int gmap_ipte_notify(struct gmap *gmap, unsigned long start, unsigned long len) break; } /* Get the page mapped */ - if (get_user_pages(current, gmap->mm, addr, 1, 1, 0, - NULL, NULL) != 1) { + if (fixup_user_fault(current, gmap->mm, addr, FAULT_FLAG_WRITE)) { rc = -EFAULT; break; } -- cgit v1.2.3 From 2abbbb63c90ab55ca3f054772c2e5ba7df810c48 Mon Sep 17 00:00:00 2001 From: Gerhard Sittig Date: Tue, 14 May 2013 04:40:53 +0000 Subject: powerpc/mpc512x: move common code to shared.c file - implement all of the init, init early, and setup arch routines in the shared source file for the MPC512x PowerPC platform, and make all MPC512x based boards (ADS, PDM, generic) use those common routines - remove declarations from header files for routines which aren't referenced from external callers any longer this modification concentrates knowledge about the optional FSL DIU support in one spot within the shared code, and makes all boards benefit transparently from future improvements in the shared platform code the change does not modify any behaviour but preserves all code paths Signed-off-by: Gerhard Sittig Signed-off-by: Anatolij Gustschin --- arch/powerpc/include/asm/mpc5121.h | 1 - arch/powerpc/platforms/512x/mpc5121_ads.c | 6 ++---- arch/powerpc/platforms/512x/mpc512x.h | 11 ++--------- arch/powerpc/platforms/512x/mpc512x_generic.c | 4 ++-- arch/powerpc/platforms/512x/mpc512x_shared.c | 14 +++++++++++++- arch/powerpc/platforms/512x/pdm360ng.c | 4 ++-- 6 files changed, 21 insertions(+), 19 deletions(-) diff --git a/arch/powerpc/include/asm/mpc5121.h b/arch/powerpc/include/asm/mpc5121.h index 885c040d6194..8ae133eaf9fa 100644 --- a/arch/powerpc/include/asm/mpc5121.h +++ b/arch/powerpc/include/asm/mpc5121.h @@ -68,6 +68,5 @@ struct mpc512x_lpc { }; int mpc512x_cs_config(unsigned int cs, u32 val); -int __init mpc5121_clk_init(void); #endif /* __ASM_POWERPC_MPC5121_H__ */ diff --git a/arch/powerpc/platforms/512x/mpc5121_ads.c b/arch/powerpc/platforms/512x/mpc5121_ads.c index 0a134e0469ef..3e90ece10ae9 100644 --- a/arch/powerpc/platforms/512x/mpc5121_ads.c +++ b/arch/powerpc/platforms/512x/mpc5121_ads.c @@ -43,9 +43,7 @@ static void __init mpc5121_ads_setup_arch(void) mpc83xx_add_bridge(np); #endif -#if defined(CONFIG_FB_FSL_DIU) || defined(CONFIG_FB_FSL_DIU_MODULE) - mpc512x_setup_diu(); -#endif + mpc512x_setup_arch(); } static void __init mpc5121_ads_init_IRQ(void) @@ -69,7 +67,7 @@ define_machine(mpc5121_ads) { .probe = mpc5121_ads_probe, .setup_arch = mpc5121_ads_setup_arch, .init = mpc512x_init, - .init_early = mpc512x_init_diu, + .init_early = mpc512x_init_early, .init_IRQ = mpc5121_ads_init_IRQ, .get_irq = ipic_get_irq, .calibrate_decr = generic_calibrate_decr, diff --git a/arch/powerpc/platforms/512x/mpc512x.h b/arch/powerpc/platforms/512x/mpc512x.h index 0a8e60023944..fdb4303246a0 100644 --- a/arch/powerpc/platforms/512x/mpc512x.h +++ b/arch/powerpc/platforms/512x/mpc512x.h @@ -12,18 +12,11 @@ #ifndef __MPC512X_H__ #define __MPC512X_H__ extern void __init mpc512x_init_IRQ(void); +extern void __init mpc512x_init_early(void); extern void __init mpc512x_init(void); +extern void __init mpc512x_setup_arch(void); extern int __init mpc5121_clk_init(void); -void __init mpc512x_declare_of_platform_devices(void); extern const char *mpc512x_select_psc_compat(void); extern void mpc512x_restart(char *cmd); -#if defined(CONFIG_FB_FSL_DIU) || defined(CONFIG_FB_FSL_DIU_MODULE) -void mpc512x_init_diu(void); -void mpc512x_setup_diu(void); -#else -#define mpc512x_init_diu NULL -#define mpc512x_setup_diu NULL -#endif - #endif /* __MPC512X_H__ */ diff --git a/arch/powerpc/platforms/512x/mpc512x_generic.c b/arch/powerpc/platforms/512x/mpc512x_generic.c index 5fb919b30924..ce71408781a0 100644 --- a/arch/powerpc/platforms/512x/mpc512x_generic.c +++ b/arch/powerpc/platforms/512x/mpc512x_generic.c @@ -45,8 +45,8 @@ define_machine(mpc512x_generic) { .name = "MPC512x generic", .probe = mpc512x_generic_probe, .init = mpc512x_init, - .init_early = mpc512x_init_diu, - .setup_arch = mpc512x_setup_diu, + .init_early = mpc512x_init_early, + .setup_arch = mpc512x_setup_arch, .init_IRQ = mpc512x_init_IRQ, .get_irq = ipic_get_irq, .calibrate_decr = generic_calibrate_decr, diff --git a/arch/powerpc/platforms/512x/mpc512x_shared.c b/arch/powerpc/platforms/512x/mpc512x_shared.c index 6eb94ab99d39..09622d3323d7 100644 --- a/arch/powerpc/platforms/512x/mpc512x_shared.c +++ b/arch/powerpc/platforms/512x/mpc512x_shared.c @@ -58,7 +58,7 @@ void mpc512x_restart(char *cmd) ; } -#if defined(CONFIG_FB_FSL_DIU) || defined(CONFIG_FB_FSL_DIU_MODULE) +#if IS_ENABLED(CONFIG_FB_FSL_DIU) struct fsl_diu_shared_fb { u8 gamma[0x300]; /* 32-bit aligned! */ @@ -436,6 +436,12 @@ void __init mpc512x_psc_fifo_init(void) } } +void __init mpc512x_init_early(void) +{ + if (IS_ENABLED(CONFIG_FB_FSL_DIU)) + mpc512x_init_diu(); +} + void __init mpc512x_init(void) { mpc5121_clk_init(); @@ -444,6 +450,12 @@ void __init mpc512x_init(void) mpc512x_psc_fifo_init(); } +void __init mpc512x_setup_arch(void) +{ + if (IS_ENABLED(CONFIG_FB_FSL_DIU)) + mpc512x_setup_diu(); +} + /** * mpc512x_cs_config - Setup chip select configuration * @cs: chip select number diff --git a/arch/powerpc/platforms/512x/pdm360ng.c b/arch/powerpc/platforms/512x/pdm360ng.c index 0575e858291c..24b314d7bd5f 100644 --- a/arch/powerpc/platforms/512x/pdm360ng.c +++ b/arch/powerpc/platforms/512x/pdm360ng.c @@ -119,9 +119,9 @@ static int __init pdm360ng_probe(void) define_machine(pdm360ng) { .name = "PDM360NG", .probe = pdm360ng_probe, - .setup_arch = mpc512x_setup_diu, + .setup_arch = mpc512x_setup_arch, .init = pdm360ng_init, - .init_early = mpc512x_init_diu, + .init_early = mpc512x_init_early, .init_IRQ = mpc512x_init_IRQ, .get_irq = ipic_get_irq, .calibrate_decr = generic_calibrate_decr, -- cgit v1.2.3 From a4f4124cf308275b4a2219d1e332dfc01d8bd6b7 Mon Sep 17 00:00:00 2001 From: Gerhard Sittig Date: Tue, 14 May 2013 04:40:54 +0000 Subject: powerpc/mpc512x: initialize board restart earlier move the MPC512x restart initialization from the shared init routine to the shared init_early routine recent problems in the proc(5) filesystem initialization led to the situation where the platform's restart routine was invoked yet the registers required for software reset were not yet available, which made the board hang instead of reboot Signed-off-by: Gerhard Sittig Signed-off-by: Anatolij Gustschin --- arch/powerpc/platforms/512x/mpc512x_shared.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/platforms/512x/mpc512x_shared.c b/arch/powerpc/platforms/512x/mpc512x_shared.c index 09622d3323d7..a8b5110eb298 100644 --- a/arch/powerpc/platforms/512x/mpc512x_shared.c +++ b/arch/powerpc/platforms/512x/mpc512x_shared.c @@ -438,6 +438,7 @@ void __init mpc512x_psc_fifo_init(void) void __init mpc512x_init_early(void) { + mpc512x_restart_init(); if (IS_ENABLED(CONFIG_FB_FSL_DIU)) mpc512x_init_diu(); } @@ -446,7 +447,6 @@ void __init mpc512x_init(void) { mpc5121_clk_init(); mpc512x_declare_of_platform_devices(); - mpc512x_restart_init(); mpc512x_psc_fifo_init(); } -- cgit v1.2.3 From e80bd1d181ff4601d88cf438817a3a7e84fe6912 Mon Sep 17 00:00:00 2001 From: Bruce Allan Date: Wed, 1 May 2013 01:19:46 +0000 Subject: e1000e: cleanup whitespace Signed-off-by: Bruce Allan Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/e1000e/80003es2lan.c | 24 +++++----- drivers/net/ethernet/intel/e1000e/82571.c | 24 +++++----- drivers/net/ethernet/intel/e1000e/ethtool.c | 34 +++++++------- drivers/net/ethernet/intel/e1000e/hw.h | 34 +++++++------- drivers/net/ethernet/intel/e1000e/ich8lan.c | 62 ++++++++++++------------- drivers/net/ethernet/intel/e1000e/netdev.c | 51 ++++++++++---------- drivers/net/ethernet/intel/e1000e/nvm.c | 1 - drivers/net/ethernet/intel/e1000e/phy.c | 22 ++++----- 8 files changed, 125 insertions(+), 127 deletions(-) diff --git a/drivers/net/ethernet/intel/e1000e/80003es2lan.c b/drivers/net/ethernet/intel/e1000e/80003es2lan.c index b71c8502a2b3..895450e9bb3c 100644 --- a/drivers/net/ethernet/intel/e1000e/80003es2lan.c +++ b/drivers/net/ethernet/intel/e1000e/80003es2lan.c @@ -66,17 +66,17 @@ static s32 e1000_init_phy_params_80003es2lan(struct e1000_hw *hw) s32 ret_val; if (hw->phy.media_type != e1000_media_type_copper) { - phy->type = e1000_phy_none; + phy->type = e1000_phy_none; return 0; } else { phy->ops.power_up = e1000_power_up_phy_copper; phy->ops.power_down = e1000_power_down_phy_copper_80003es2lan; } - phy->addr = 1; - phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT; - phy->reset_delay_us = 100; - phy->type = e1000_phy_gg82563; + phy->addr = 1; + phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT; + phy->reset_delay_us = 100; + phy->type = e1000_phy_gg82563; /* This can only be done after all function pointers are setup. */ ret_val = e1000e_get_phy_id(hw); @@ -98,19 +98,19 @@ static s32 e1000_init_nvm_params_80003es2lan(struct e1000_hw *hw) u32 eecd = er32(EECD); u16 size; - nvm->opcode_bits = 8; - nvm->delay_usec = 1; + nvm->opcode_bits = 8; + nvm->delay_usec = 1; switch (nvm->override) { case e1000_nvm_override_spi_large: - nvm->page_size = 32; + nvm->page_size = 32; nvm->address_bits = 16; break; case e1000_nvm_override_spi_small: - nvm->page_size = 8; + nvm->page_size = 8; nvm->address_bits = 8; break; default: - nvm->page_size = eecd & E1000_EECD_ADDR_BITS ? 32 : 8; + nvm->page_size = eecd & E1000_EECD_ADDR_BITS ? 32 : 8; nvm->address_bits = eecd & E1000_EECD_ADDR_BITS ? 16 : 8; break; } @@ -128,7 +128,7 @@ static s32 e1000_init_nvm_params_80003es2lan(struct e1000_hw *hw) /* EEPROM access above 16k is unsupported */ if (size > 14) size = 14; - nvm->word_size = 1 << size; + nvm->word_size = 1 << size; return 0; } @@ -859,7 +859,7 @@ static void e1000_initialize_hw_bits_80003es2lan(struct e1000_hw *hw) /* Transmit Arbitration Control 0 */ reg = er32(TARC(0)); - reg &= ~(0xF << 27); /* 30:27 */ + reg &= ~(0xF << 27); /* 30:27 */ if (hw->phy.media_type != e1000_media_type_copper) reg &= ~(1 << 20); ew32(TARC(0), reg); diff --git a/drivers/net/ethernet/intel/e1000e/82571.c b/drivers/net/ethernet/intel/e1000e/82571.c index 7380442a3829..bef2f103ab03 100644 --- a/drivers/net/ethernet/intel/e1000e/82571.c +++ b/drivers/net/ethernet/intel/e1000e/82571.c @@ -77,24 +77,24 @@ static s32 e1000_init_phy_params_82571(struct e1000_hw *hw) return 0; } - phy->addr = 1; - phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT; - phy->reset_delay_us = 100; + phy->addr = 1; + phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT; + phy->reset_delay_us = 100; - phy->ops.power_up = e1000_power_up_phy_copper; - phy->ops.power_down = e1000_power_down_phy_copper_82571; + phy->ops.power_up = e1000_power_up_phy_copper; + phy->ops.power_down = e1000_power_down_phy_copper_82571; switch (hw->mac.type) { case e1000_82571: case e1000_82572: - phy->type = e1000_phy_igp_2; + phy->type = e1000_phy_igp_2; break; case e1000_82573: - phy->type = e1000_phy_m88; + phy->type = e1000_phy_m88; break; case e1000_82574: case e1000_82583: - phy->type = e1000_phy_bm; + phy->type = e1000_phy_bm; phy->ops.acquire = e1000_get_hw_semaphore_82574; phy->ops.release = e1000_put_hw_semaphore_82574; phy->ops.set_d0_lplu_state = e1000_set_d0_lplu_state_82574; @@ -193,7 +193,7 @@ static s32 e1000_init_nvm_params_82571(struct e1000_hw *hw) /* EEPROM access above 16k is unsupported */ if (size > 14) size = 14; - nvm->word_size = 1 << size; + nvm->word_size = 1 << size; break; } @@ -339,7 +339,7 @@ static s32 e1000_init_mac_params_82571(struct e1000_hw *hw) static s32 e1000_get_variants_82571(struct e1000_adapter *adapter) { struct e1000_hw *hw = &adapter->hw; - static int global_quad_port_a; /* global port a indication */ + static int global_quad_port_a; /* global port a indication */ struct pci_dev *pdev = adapter->pdev; int is_port_b = er32(STATUS) & E1000_STATUS_FUNC_1; s32 rc; @@ -1178,7 +1178,7 @@ static void e1000_initialize_hw_bits_82571(struct e1000_hw *hw) /* Transmit Arbitration Control 0 */ reg = er32(TARC(0)); - reg &= ~(0xF << 27); /* 30:27 */ + reg &= ~(0xF << 27); /* 30:27 */ switch (hw->mac.type) { case e1000_82571: case e1000_82572: @@ -1390,7 +1390,7 @@ bool e1000_check_phy_82574(struct e1000_hw *hw) ret_val = e1e_rphy(hw, E1000_RECEIVE_ERROR_COUNTER, &receive_errors); if (ret_val) return false; - if (receive_errors == E1000_RECEIVE_ERROR_MAX) { + if (receive_errors == E1000_RECEIVE_ERROR_MAX) { ret_val = e1e_rphy(hw, E1000_BASE1000T_STATUS, &status_1kbt); if (ret_val) return false; diff --git a/drivers/net/ethernet/intel/e1000e/ethtool.c b/drivers/net/ethernet/intel/e1000e/ethtool.c index 7c8ca658d553..59c22bf18701 100644 --- a/drivers/net/ethernet/intel/e1000e/ethtool.c +++ b/drivers/net/ethernet/intel/e1000e/ethtool.c @@ -244,7 +244,7 @@ static int e1000_set_spd_dplx(struct e1000_adapter *adapter, u32 spd, u8 dplx) mac->autoneg = 1; adapter->hw.phy.autoneg_advertised = ADVERTISE_1000_FULL; break; - case SPEED_1000 + DUPLEX_HALF: /* not supported */ + case SPEED_1000 + DUPLEX_HALF: /* not supported */ default: goto err_inval; } @@ -416,7 +416,7 @@ static void e1000_set_msglevel(struct net_device *netdev, u32 data) static int e1000_get_regs_len(struct net_device __always_unused *netdev) { -#define E1000_REGS_LEN 32 /* overestimate */ +#define E1000_REGS_LEN 32 /* overestimate */ return E1000_REGS_LEN * sizeof(u32); } @@ -433,22 +433,22 @@ static void e1000_get_regs(struct net_device *netdev, regs->version = (1 << 24) | (adapter->pdev->revision << 16) | adapter->pdev->device; - regs_buff[0] = er32(CTRL); - regs_buff[1] = er32(STATUS); + regs_buff[0] = er32(CTRL); + regs_buff[1] = er32(STATUS); - regs_buff[2] = er32(RCTL); - regs_buff[3] = er32(RDLEN(0)); - regs_buff[4] = er32(RDH(0)); - regs_buff[5] = er32(RDT(0)); - regs_buff[6] = er32(RDTR); + regs_buff[2] = er32(RCTL); + regs_buff[3] = er32(RDLEN(0)); + regs_buff[4] = er32(RDH(0)); + regs_buff[5] = er32(RDT(0)); + regs_buff[6] = er32(RDTR); - regs_buff[7] = er32(TCTL); - regs_buff[8] = er32(TDLEN(0)); - regs_buff[9] = er32(TDH(0)); + regs_buff[7] = er32(TCTL); + regs_buff[8] = er32(TDLEN(0)); + regs_buff[9] = er32(TDH(0)); regs_buff[10] = er32(TDT(0)); regs_buff[11] = er32(TIDV); - regs_buff[12] = adapter->hw.phy.type; /* PHY type (IGP=1, M88=0) */ + regs_buff[12] = adapter->hw.phy.type; /* PHY type (IGP=1, M88=0) */ /* ethtool doesn't use anything past this point, so all this * code is likely legacy junk for apps that may or may not exist @@ -1379,7 +1379,7 @@ static int e1000_integrated_phy_loopback(struct e1000_adapter *adapter) if (hw->phy.media_type == e1000_media_type_copper && hw->phy.type == e1000_phy_m88) { - ctrl_reg |= E1000_CTRL_ILOS; /* Invert Loss of Signal */ + ctrl_reg |= E1000_CTRL_ILOS; /* Invert Loss of Signal */ } else { /* Set the ILOS bit on the fiber Nic if half duplex link is * detected. @@ -1613,7 +1613,7 @@ static int e1000_run_loopback_test(struct e1000_adapter *adapter) ew32(TDT(0), k); e1e_flush(); msleep(200); - time = jiffies; /* set the start time for the receive */ + time = jiffies; /* set the start time for the receive */ good_cnt = 0; /* receive the sent packets */ do { @@ -1636,11 +1636,11 @@ static int e1000_run_loopback_test(struct e1000_adapter *adapter) */ } while ((good_cnt < 64) && !time_after(jiffies, time + 20)); if (good_cnt != 64) { - ret_val = 13; /* ret_val is the same as mis-compare */ + ret_val = 13; /* ret_val is the same as mis-compare */ break; } if (jiffies >= (time + 20)) { - ret_val = 14; /* error code for time out error */ + ret_val = 14; /* error code for time out error */ break; } } diff --git a/drivers/net/ethernet/intel/e1000e/hw.h b/drivers/net/ethernet/intel/e1000e/hw.h index 84850f7a23e4..a6f903a9b773 100644 --- a/drivers/net/ethernet/intel/e1000e/hw.h +++ b/drivers/net/ethernet/intel/e1000e/hw.h @@ -402,13 +402,13 @@ struct e1000_phy_stats { struct e1000_host_mng_dhcp_cookie { u32 signature; - u8 status; - u8 reserved0; + u8 status; + u8 reserved0; u16 vlan_id; u32 reserved1; u16 reserved2; - u8 reserved3; - u8 checksum; + u8 reserved3; + u8 checksum; }; /* Host Interface "Rev 1" */ @@ -427,8 +427,8 @@ struct e1000_host_command_info { /* Host Interface "Rev 2" */ struct e1000_host_mng_command_header { - u8 command_id; - u8 checksum; + u8 command_id; + u8 checksum; u16 reserved1; u16 reserved2; u16 command_length; @@ -549,7 +549,7 @@ struct e1000_mac_info { u32 mta_shadow[MAX_MTA_REG]; u16 rar_entry_count; - u8 forced_speed_duplex; + u8 forced_speed_duplex; bool adaptive_ifs; bool has_fwsm; @@ -577,7 +577,7 @@ struct e1000_phy_info { u32 addr; u32 id; - u32 reset_delay_us; /* in usec */ + u32 reset_delay_us; /* in usec */ u32 revision; enum e1000_media_type media_type; @@ -636,11 +636,11 @@ struct e1000_dev_spec_82571 { }; struct e1000_dev_spec_80003es2lan { - bool mdic_wa_enable; + bool mdic_wa_enable; }; struct e1000_shadow_ram { - u16 value; + u16 value; bool modified; }; @@ -660,17 +660,17 @@ struct e1000_hw { void __iomem *hw_addr; void __iomem *flash_address; - struct e1000_mac_info mac; - struct e1000_fc_info fc; - struct e1000_phy_info phy; - struct e1000_nvm_info nvm; - struct e1000_bus_info bus; + struct e1000_mac_info mac; + struct e1000_fc_info fc; + struct e1000_phy_info phy; + struct e1000_nvm_info nvm; + struct e1000_bus_info bus; struct e1000_host_mng_dhcp_cookie mng_cookie; union { - struct e1000_dev_spec_82571 e82571; + struct e1000_dev_spec_82571 e82571; struct e1000_dev_spec_80003es2lan e80003es2lan; - struct e1000_dev_spec_ich8lan ich8lan; + struct e1000_dev_spec_ich8lan ich8lan; } dev_spec; }; diff --git a/drivers/net/ethernet/intel/e1000e/ich8lan.c b/drivers/net/ethernet/intel/e1000e/ich8lan.c index ad9d8f2dd868..9dde390f7e71 100644 --- a/drivers/net/ethernet/intel/e1000e/ich8lan.c +++ b/drivers/net/ethernet/intel/e1000e/ich8lan.c @@ -101,12 +101,12 @@ union ich8_hws_flash_regacc { /* ICH Flash Protected Region */ union ich8_flash_protected_range { struct ich8_pr { - u32 base:13; /* 0:12 Protected Range Base */ - u32 reserved1:2; /* 13:14 Reserved */ - u32 rpe:1; /* 15 Read Protection Enable */ - u32 limit:13; /* 16:28 Protected Range Limit */ - u32 reserved2:2; /* 29:30 Reserved */ - u32 wpe:1; /* 31 Write Protection Enable */ + u32 base:13; /* 0:12 Protected Range Base */ + u32 reserved1:2; /* 13:14 Reserved */ + u32 rpe:1; /* 15 Read Protection Enable */ + u32 limit:13; /* 16:28 Protected Range Limit */ + u32 reserved2:2; /* 29:30 Reserved */ + u32 wpe:1; /* 31 Write Protection Enable */ } range; u32 regval; }; @@ -362,21 +362,21 @@ static s32 e1000_init_phy_params_pchlan(struct e1000_hw *hw) struct e1000_phy_info *phy = &hw->phy; s32 ret_val; - phy->addr = 1; - phy->reset_delay_us = 100; - - phy->ops.set_page = e1000_set_page_igp; - phy->ops.read_reg = e1000_read_phy_reg_hv; - phy->ops.read_reg_locked = e1000_read_phy_reg_hv_locked; - phy->ops.read_reg_page = e1000_read_phy_reg_page_hv; - phy->ops.set_d0_lplu_state = e1000_set_lplu_state_pchlan; - phy->ops.set_d3_lplu_state = e1000_set_lplu_state_pchlan; - phy->ops.write_reg = e1000_write_phy_reg_hv; - phy->ops.write_reg_locked = e1000_write_phy_reg_hv_locked; - phy->ops.write_reg_page = e1000_write_phy_reg_page_hv; - phy->ops.power_up = e1000_power_up_phy_copper; - phy->ops.power_down = e1000_power_down_phy_copper_ich8lan; - phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT; + phy->addr = 1; + phy->reset_delay_us = 100; + + phy->ops.set_page = e1000_set_page_igp; + phy->ops.read_reg = e1000_read_phy_reg_hv; + phy->ops.read_reg_locked = e1000_read_phy_reg_hv_locked; + phy->ops.read_reg_page = e1000_read_phy_reg_page_hv; + phy->ops.set_d0_lplu_state = e1000_set_lplu_state_pchlan; + phy->ops.set_d3_lplu_state = e1000_set_lplu_state_pchlan; + phy->ops.write_reg = e1000_write_phy_reg_hv; + phy->ops.write_reg_locked = e1000_write_phy_reg_hv_locked; + phy->ops.write_reg_page = e1000_write_phy_reg_page_hv; + phy->ops.power_up = e1000_power_up_phy_copper; + phy->ops.power_down = e1000_power_down_phy_copper_ich8lan; + phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT; phy->id = e1000_phy_unknown; @@ -445,11 +445,11 @@ static s32 e1000_init_phy_params_ich8lan(struct e1000_hw *hw) s32 ret_val; u16 i = 0; - phy->addr = 1; - phy->reset_delay_us = 100; + phy->addr = 1; + phy->reset_delay_us = 100; - phy->ops.power_up = e1000_power_up_phy_copper; - phy->ops.power_down = e1000_power_down_phy_copper_ich8lan; + phy->ops.power_up = e1000_power_up_phy_copper; + phy->ops.power_down = e1000_power_down_phy_copper_ich8lan; /* We may need to do this twice - once for IGP and if that fails, * we'll set BM func pointers and try again @@ -457,7 +457,7 @@ static s32 e1000_init_phy_params_ich8lan(struct e1000_hw *hw) ret_val = e1000e_determine_phy_address(hw); if (ret_val) { phy->ops.write_reg = e1000e_write_phy_reg_bm; - phy->ops.read_reg = e1000e_read_phy_reg_bm; + phy->ops.read_reg = e1000e_read_phy_reg_bm; ret_val = e1000e_determine_phy_address(hw); if (ret_val) { e_dbg("Cannot determine PHY addr. Erroring out\n"); @@ -560,7 +560,7 @@ static s32 e1000_init_nvm_params_ich8lan(struct e1000_hw *hw) /* Clear shadow ram */ for (i = 0; i < nvm->word_size; i++) { dev_spec->shadow_ram[i].modified = false; - dev_spec->shadow_ram[i].value = 0xFFFF; + dev_spec->shadow_ram[i].value = 0xFFFF; } return 0; @@ -1012,7 +1012,7 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw) hw->dev_spec.ich8lan.eee_lp_ability = 0; if (!link) - return 0; /* No link detected */ + return 0; /* No link detected */ mac->get_link_status = false; @@ -2816,7 +2816,7 @@ static s32 e1000_read_flash_data_ich8lan(struct e1000_hw *hw, u32 offset, s32 ret_val = -E1000_ERR_NVM; u8 count = 0; - if (size < 1 || size > 2 || offset > ICH_FLASH_LINEAR_ADDR_MASK) + if (size < 1 || size > 2 || offset > ICH_FLASH_LINEAR_ADDR_MASK) return -E1000_ERR_NVM; flash_linear_addr = ((ICH_FLASH_LINEAR_ADDR_MASK & offset) + @@ -2939,7 +2939,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw) * write to bank 0 etc. We also need to erase the segment that * is going to be written */ - ret_val = e1000_valid_nvm_bank_detect_ich8lan(hw, &bank); + ret_val = e1000_valid_nvm_bank_detect_ich8lan(hw, &bank); if (ret_val) { e_dbg("Could not detect valid bank, assuming bank 0\n"); bank = 0; @@ -4073,7 +4073,7 @@ void e1000e_igp3_phy_powerdown_workaround_ich8lan(struct e1000_hw *hw) { u32 reg; u16 data; - u8 retry = 0; + u8 retry = 0; if (hw->phy.type != e1000_phy_igp_3) return; diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index a27e3bcc3249..ad5f43476924 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -1196,7 +1196,7 @@ static bool e1000_clean_tx_irq(struct e1000_ring *tx_ring) while ((eop_desc->upper.data & cpu_to_le32(E1000_TXD_STAT_DD)) && (count < tx_ring->count)) { bool cleaned = false; - rmb(); /* read buffer_info after eop_desc */ + rmb(); /* read buffer_info after eop_desc */ for (; !cleaned; count++) { tx_desc = E1000_TX_DESC(*tx_ring, i); buffer_info = &tx_ring->buffer_info[i]; @@ -1385,7 +1385,7 @@ static bool e1000_clean_rx_irq_ps(struct e1000_ring *rx_ring, int *work_done, skb_put(skb, l1); goto copydone; - } /* if */ + } /* if */ } for (j = 0; j < PS_PAGE_BUFFERS; j++) { @@ -1800,7 +1800,7 @@ static irqreturn_t e1000_intr(int __always_unused irq, void *data) u32 rctl, icr = er32(ICR); if (!icr || test_bit(__E1000_DOWN, &adapter->state)) - return IRQ_NONE; /* Not our interrupt */ + return IRQ_NONE; /* Not our interrupt */ /* IMS will not auto-mask if INT_ASSERTED is not set, and if it is * not set, then the adapter didn't send an interrupt @@ -2487,7 +2487,7 @@ static unsigned int e1000_update_itr(u16 itr_setting, int packets, int bytes) else if ((packets < 5) && (bytes > 512)) retval = low_latency; break; - case low_latency: /* 50 usec aka 20000 ints/s */ + case low_latency: /* 50 usec aka 20000 ints/s */ if (bytes > 10000) { /* this if handles the TSO accounting */ if (bytes / packets > 8000) @@ -2502,7 +2502,7 @@ static unsigned int e1000_update_itr(u16 itr_setting, int packets, int bytes) retval = lowest_latency; } break; - case bulk_latency: /* 250 usec aka 4000 ints/s */ + case bulk_latency: /* 250 usec aka 4000 ints/s */ if (bytes > 25000) { if (packets > 35) retval = low_latency; @@ -2554,7 +2554,7 @@ static void e1000_set_itr(struct e1000_adapter *adapter) new_itr = 70000; break; case low_latency: - new_itr = 20000; /* aka hwitr = ~200 */ + new_itr = 20000; /* aka hwitr = ~200 */ break; case bulk_latency: new_itr = 4000; @@ -3104,13 +3104,13 @@ static void e1000_setup_rctl(struct e1000_adapter *adapter) /* UPE and MPE will be handled by normal PROMISC logic * in e1000e_set_rx_mode */ - rctl |= (E1000_RCTL_SBP | /* Receive bad packets */ - E1000_RCTL_BAM | /* RX All Bcast Pkts */ - E1000_RCTL_PMCF); /* RX All MAC Ctrl Pkts */ + rctl |= (E1000_RCTL_SBP | /* Receive bad packets */ + E1000_RCTL_BAM | /* RX All Bcast Pkts */ + E1000_RCTL_PMCF); /* RX All MAC Ctrl Pkts */ - rctl &= ~(E1000_RCTL_VFE | /* Disable VLAN filter */ - E1000_RCTL_DPF | /* Allow filtered pause */ - E1000_RCTL_CFIEN); /* Dis VLAN CFIEN Filter */ + rctl &= ~(E1000_RCTL_VFE | /* Disable VLAN filter */ + E1000_RCTL_DPF | /* Allow filtered pause */ + E1000_RCTL_CFIEN); /* Dis VLAN CFIEN Filter */ /* Do not mess with E1000_CTRL_VME, it affects transmit as well, * and that breaks VLANs. */ @@ -3799,7 +3799,7 @@ void e1000e_reset(struct e1000_adapter *adapter) hwm = min(((pba << 10) * 9 / 10), ((pba << 10) - adapter->max_frame_size)); - fc->high_water = hwm & E1000_FCRTH_RTH; /* 8-byte granularity */ + fc->high_water = hwm & E1000_FCRTH_RTH; /* 8-byte granularity */ fc->low_water = fc->high_water - 8; break; case e1000_pchlan: @@ -3808,10 +3808,10 @@ void e1000e_reset(struct e1000_adapter *adapter) */ if (adapter->netdev->mtu > ETH_DATA_LEN) { fc->high_water = 0x3500; - fc->low_water = 0x1500; + fc->low_water = 0x1500; } else { fc->high_water = 0x5000; - fc->low_water = 0x3000; + fc->low_water = 0x3000; } fc->refresh_time = 0x1000; break; @@ -4581,7 +4581,7 @@ static void e1000e_update_stats(struct e1000_adapter *adapter) adapter->stats.crcerrs += er32(CRCERRS); adapter->stats.gprc += er32(GPRC); adapter->stats.gorc += er32(GORCL); - er32(GORCH); /* Clear gorc */ + er32(GORCH); /* Clear gorc */ adapter->stats.bprc += er32(BPRC); adapter->stats.mprc += er32(MPRC); adapter->stats.roc += er32(ROC); @@ -4614,7 +4614,7 @@ static void e1000e_update_stats(struct e1000_adapter *adapter) adapter->stats.xofftxc += er32(XOFFTXC); adapter->stats.gptc += er32(GPTC); adapter->stats.gotc += er32(GOTCL); - er32(GOTCH); /* Clear gotc */ + er32(GOTCH); /* Clear gotc */ adapter->stats.rnbc += er32(RNBC); adapter->stats.ruc += er32(RUC); @@ -5106,13 +5106,13 @@ static int e1000_tso(struct e1000_ring *tx_ring, struct sk_buff *skb) context_desc = E1000_CONTEXT_DESC(*tx_ring, i); buffer_info = &tx_ring->buffer_info[i]; - context_desc->lower_setup.ip_fields.ipcss = ipcss; - context_desc->lower_setup.ip_fields.ipcso = ipcso; - context_desc->lower_setup.ip_fields.ipcse = cpu_to_le16(ipcse); + context_desc->lower_setup.ip_fields.ipcss = ipcss; + context_desc->lower_setup.ip_fields.ipcso = ipcso; + context_desc->lower_setup.ip_fields.ipcse = cpu_to_le16(ipcse); context_desc->upper_setup.tcp_fields.tucss = tucss; context_desc->upper_setup.tcp_fields.tucso = tucso; context_desc->upper_setup.tcp_fields.tucse = 0; - context_desc->tcp_seg_setup.fields.mss = cpu_to_le16(mss); + context_desc->tcp_seg_setup.fields.mss = cpu_to_le16(mss); context_desc->tcp_seg_setup.fields.hdr_len = hdr_len; context_desc->cmd_and_length = cpu_to_le32(cmd_length); @@ -5363,7 +5363,7 @@ static void e1000_tx_queue(struct e1000_ring *tx_ring, int tx_flags, int count) static int e1000_transfer_dhcp_info(struct e1000_adapter *adapter, struct sk_buff *skb) { - struct e1000_hw *hw = &adapter->hw; + struct e1000_hw *hw = &adapter->hw; u16 length, offset; if (vlan_tx_tag_present(skb) && @@ -6259,7 +6259,7 @@ static void e1000_netpoll(struct net_device *netdev) e1000_intr_msi(adapter->pdev->irq, netdev); enable_irq(adapter->pdev->irq); break; - default: /* E1000E_INT_MODE_LEGACY */ + default: /* E1000E_INT_MODE_LEGACY */ disable_irq(adapter->pdev->irq); e1000_intr(adapter->pdev->irq, netdev); enable_irq(adapter->pdev->irq); @@ -6589,9 +6589,9 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) adapter->eee_advert = MDIO_EEE_100TX | MDIO_EEE_1000T; /* construct the net_device struct */ - netdev->netdev_ops = &e1000e_netdev_ops; + netdev->netdev_ops = &e1000e_netdev_ops; e1000e_set_ethtool_ops(netdev); - netdev->watchdog_timeo = 5 * HZ; + netdev->watchdog_timeo = 5 * HZ; netif_napi_add(netdev, &adapter->napi, e1000e_poll, 64); strlcpy(netdev->name, pci_name(pdev), sizeof(netdev->name)); @@ -7034,7 +7034,6 @@ static void __exit e1000_exit_module(void) } module_exit(e1000_exit_module); - MODULE_AUTHOR("Intel Corporation, "); MODULE_DESCRIPTION("Intel(R) PRO/1000 Network Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/intel/e1000e/nvm.c b/drivers/net/ethernet/intel/e1000e/nvm.c index 44ddc0a0ee0e..d70a03906ac0 100644 --- a/drivers/net/ethernet/intel/e1000e/nvm.c +++ b/drivers/net/ethernet/intel/e1000e/nvm.c @@ -117,7 +117,6 @@ static u16 e1000_shift_in_eec_bits(struct e1000_hw *hw, u16 count) u16 data; eecd = er32(EECD); - eecd &= ~(E1000_EECD_DO | E1000_EECD_DI); data = 0; diff --git a/drivers/net/ethernet/intel/e1000e/phy.c b/drivers/net/ethernet/intel/e1000e/phy.c index 59c76a6815a0..da2be59505c0 100644 --- a/drivers/net/ethernet/intel/e1000e/phy.c +++ b/drivers/net/ethernet/intel/e1000e/phy.c @@ -1583,13 +1583,13 @@ s32 e1000e_check_downshift(struct e1000_hw *hw) case e1000_phy_gg82563: case e1000_phy_bm: case e1000_phy_82578: - offset = M88E1000_PHY_SPEC_STATUS; - mask = M88E1000_PSSR_DOWNSHIFT; + offset = M88E1000_PHY_SPEC_STATUS; + mask = M88E1000_PSSR_DOWNSHIFT; break; case e1000_phy_igp_2: case e1000_phy_igp_3: - offset = IGP01E1000_PHY_LINK_HEALTH; - mask = IGP01E1000_PLHR_SS_DOWNGRADE; + offset = IGP01E1000_PHY_LINK_HEALTH; + mask = IGP01E1000_PLHR_SS_DOWNGRADE; break; default: /* speed downshift not supported */ @@ -1653,14 +1653,14 @@ s32 e1000_check_polarity_igp(struct e1000_hw *hw) if ((data & IGP01E1000_PSSR_SPEED_MASK) == IGP01E1000_PSSR_SPEED_1000MBPS) { - offset = IGP01E1000_PHY_PCS_INIT_REG; - mask = IGP01E1000_PHY_POLARITY_MASK; + offset = IGP01E1000_PHY_PCS_INIT_REG; + mask = IGP01E1000_PHY_POLARITY_MASK; } else { /* This really only applies to 10Mbps since * there is no polarity for 100Mbps (always 0). */ - offset = IGP01E1000_PHY_PORT_STATUS; - mask = IGP01E1000_PSSR_POLARITY_REVERSED; + offset = IGP01E1000_PHY_PORT_STATUS; + mask = IGP01E1000_PSSR_POLARITY_REVERSED; } ret_val = e1e_rphy(hw, offset, &data); @@ -1900,7 +1900,7 @@ s32 e1000e_get_cable_length_igp_2(struct e1000_hw *hw) s32 e1000e_get_phy_info_m88(struct e1000_hw *hw) { struct e1000_phy_info *phy = &hw->phy; - s32 ret_val; + s32 ret_val; u16 phy_data; bool link; @@ -2253,7 +2253,7 @@ enum e1000_phy_type e1000e_get_phy_type_from_id(u32 phy_id) case M88E1011_I_PHY_ID: phy_type = e1000_phy_m88; break; - case IGP01E1000_I_PHY_ID: /* IGP 1 & 2 share this */ + case IGP01E1000_I_PHY_ID: /* IGP 1 & 2 share this */ phy_type = e1000_phy_igp_2; break; case GG82563_E_PHY_ID: @@ -2317,7 +2317,7 @@ s32 e1000e_determine_phy_address(struct e1000_hw *hw) /* If phy_type is valid, break - we found our * PHY address */ - if (phy_type != e1000_phy_unknown) + if (phy_type != e1000_phy_unknown) return 0; usleep_range(1000, 2000); -- cgit v1.2.3 From 603cdca9805e4f8001cf7ffbd8c539c9fa6674ce Mon Sep 17 00:00:00 2001 From: Bruce Allan Date: Wed, 1 May 2013 03:48:11 +0000 Subject: e1000e: prevent warning from -Wunused-parameter Signed-off-by: Bruce Allan Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/e1000e/netdev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index ad5f43476924..77f81cbb601a 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -2673,7 +2673,7 @@ static int e1000e_poll(struct napi_struct *napi, int weight) } static int e1000_vlan_rx_add_vid(struct net_device *netdev, - __be16 proto, u16 vid) + __always_unused __be16 proto, u16 vid) { struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; @@ -2699,7 +2699,7 @@ static int e1000_vlan_rx_add_vid(struct net_device *netdev, } static int e1000_vlan_rx_kill_vid(struct net_device *netdev, - __be16 proto, u16 vid) + __always_unused __be16 proto, u16 vid) { struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; -- cgit v1.2.3 From 5a41254eac1137d0335c0aed7b00f1f3138ee9ca Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 3 May 2013 17:23:37 -0300 Subject: drm/i915: ILK, SNB and IVB don't have linetime watermarks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So don't call intel_update_linetime_watermarks from ironlake_crtc_mode_set. Only Haswell has these watermarks. Signed-off-by: Paulo Zanoni Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 4196155cf964..c7bdbcb6a675 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5841,8 +5841,6 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, intel_update_watermarks(dev); - intel_update_linetime_watermarks(dev, pipe, adjusted_mode); - return ret; } -- cgit v1.2.3 From 6c1d8b96d09ed8852f5bc11c42374be3232374ce Mon Sep 17 00:00:00 2001 From: "Akeem G. Abodunrin" Date: Thu, 2 May 2013 02:57:44 +0000 Subject: e1000e: Release mutex lock only if it has been initially acquired This patch fixes the issue of unlocking swflag_mutex for 82574 and 82583 devices regardless of if the hw semaphore has been successfully acquired via e1000_get_hw_semaphore_82574(). With this patch, unlocking mutex now depends on if the hw semaphore was successfully acquired before. And 82574/82583 devices are reset regardless of whether e1000_get_hw_semaphore_82574() returns success or failure. Reported-by: Alexey Khoroshilov Signed-off-by: Akeem G Abodunrin Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/e1000e/82571.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/intel/e1000e/82571.c b/drivers/net/ethernet/intel/e1000e/82571.c index bef2f103ab03..4c303e2a7cb3 100644 --- a/drivers/net/ethernet/intel/e1000e/82571.c +++ b/drivers/net/ethernet/intel/e1000e/82571.c @@ -1003,8 +1003,6 @@ static s32 e1000_reset_hw_82571(struct e1000_hw *hw) default: break; } - if (ret_val) - e_dbg("Cannot acquire MDIO ownership\n"); ctrl = er32(CTRL); @@ -1015,7 +1013,9 @@ static s32 e1000_reset_hw_82571(struct e1000_hw *hw) switch (hw->mac.type) { case e1000_82574: case e1000_82583: - e1000_put_hw_semaphore_82574(hw); + /* Release mutex only if the hw semaphore is acquired */ + if (!ret_val) + e1000_put_hw_semaphore_82574(hw); break; default: break; -- cgit v1.2.3 From 1011d8c4373b229012208b5aedad4f46396bdd94 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Thu, 9 May 2013 16:55:50 -0300 Subject: drm/i915: remove intel_update_linetime_watermarks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The spec says the linetime watermarks must be programmed before enabling any display low power watermarks, but we're currently updating the linetime watermarks after we call intel_update_watermarks (and only at crtc_mode_set, not at crtc_{enable,disable}). So IMHO the best way guarantee the linetime watermarks will be updated before the low power watermarks is inside the update_wm function, because it's the function that enables low power watermarks. And since Haswell is the only platform that has linetime watermarks, let's completely kill the "intel_update_linetime_watermarks" abstraction and just use the intel_update_watermarks abstraction by creating haswell_update_wm. For now haswell_update_wm is still calling sandybridge_update_wm, but in the future I plan to implement a function specific to Haswell. v2: - Rename patch - Disable LP watermarks before changing linetime WMs (Chris) - Add a comment explaining that this is just temporary code. Signed-off-by: Paulo Zanoni Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 2 -- drivers/gpu/drm/i915/intel_display.c | 2 -- drivers/gpu/drm/i915/intel_drv.h | 2 -- drivers/gpu/drm/i915/intel_pm.c | 43 +++++++++++++++++++++++++----------- 4 files changed, 30 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 14817de37405..639ec0b61450 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -316,8 +316,6 @@ struct drm_i915_display_funcs { void (*update_wm)(struct drm_device *dev); void (*update_sprite_wm)(struct drm_device *dev, int pipe, uint32_t sprite_width, int pixel_size); - void (*update_linetime_wm)(struct drm_device *dev, int pipe, - struct drm_display_mode *mode); void (*modeset_global_resources)(struct drm_device *dev); /* Returns the active state of the crtc, and if the crtc is active, * fills out the pipe-config with the hw state. */ diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index c7bdbcb6a675..684ab6417dd4 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6008,8 +6008,6 @@ static int haswell_crtc_mode_set(struct drm_crtc *crtc, intel_update_watermarks(dev); - intel_update_linetime_watermarks(dev, pipe, adjusted_mode); - return ret; } diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index a435ce1d075e..75a7f22262d4 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -732,8 +732,6 @@ extern void intel_update_watermarks(struct drm_device *dev); extern void intel_update_sprite_watermarks(struct drm_device *dev, int pipe, uint32_t sprite_width, int pixel_size); -extern void intel_update_linetime_watermarks(struct drm_device *dev, int pipe, - struct drm_display_mode *mode); extern unsigned long intel_gen4_compute_page_offset(int *x, int *y, unsigned int tiling_mode, diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index e2255ed97894..2ae4e459faa5 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -2073,12 +2073,19 @@ static void ivybridge_update_wm(struct drm_device *dev) } static void -haswell_update_linetime_wm(struct drm_device *dev, int pipe, - struct drm_display_mode *mode) +haswell_update_linetime_wm(struct drm_device *dev, struct drm_crtc *crtc) { struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + enum pipe pipe = intel_crtc->pipe; + struct drm_display_mode *mode = &intel_crtc->config.adjusted_mode; u32 temp; + if (!intel_crtc_active(crtc)) { + I915_WRITE(PIPE_WM_LINETIME(pipe), 0); + return; + } + temp = I915_READ(PIPE_WM_LINETIME(pipe)); temp &= ~PIPE_WM_LINETIME_MASK; @@ -2099,6 +2106,26 @@ haswell_update_linetime_wm(struct drm_device *dev, int pipe, I915_WRITE(PIPE_WM_LINETIME(pipe), temp); } +static void haswell_update_wm(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_crtc *crtc; + enum pipe pipe; + + /* Disable the LP WMs before changine the linetime registers. This is + * just a temporary code that will be replaced soon. */ + I915_WRITE(WM3_LP_ILK, 0); + I915_WRITE(WM2_LP_ILK, 0); + I915_WRITE(WM1_LP_ILK, 0); + + for_each_pipe(pipe) { + crtc = dev_priv->pipe_to_crtc_mapping[pipe]; + haswell_update_linetime_wm(dev, crtc); + } + + sandybridge_update_wm(dev); +} + static bool sandybridge_compute_sprite_wm(struct drm_device *dev, int plane, uint32_t sprite_width, int pixel_size, @@ -2294,15 +2321,6 @@ void intel_update_watermarks(struct drm_device *dev) dev_priv->display.update_wm(dev); } -void intel_update_linetime_watermarks(struct drm_device *dev, - int pipe, struct drm_display_mode *mode) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - - if (dev_priv->display.update_linetime_wm) - dev_priv->display.update_linetime_wm(dev, pipe, mode); -} - void intel_update_sprite_watermarks(struct drm_device *dev, int pipe, uint32_t sprite_width, int pixel_size) { @@ -4624,9 +4642,8 @@ void intel_init_pm(struct drm_device *dev) dev_priv->display.init_clock_gating = ivybridge_init_clock_gating; } else if (IS_HASWELL(dev)) { if (SNB_READ_WM0_LATENCY()) { - dev_priv->display.update_wm = sandybridge_update_wm; + dev_priv->display.update_wm = haswell_update_wm; dev_priv->display.update_sprite_wm = sandybridge_update_sprite_wm; - dev_priv->display.update_linetime_wm = haswell_update_linetime_wm; } else { DRM_DEBUG_KMS("Failed to read display plane latency. " "Disable CxSR\n"); -- cgit v1.2.3 From cf7ed221714c36848b257311b316452e274f7e15 Mon Sep 17 00:00:00 2001 From: "Akeem G. Abodunrin" Date: Fri, 29 Mar 2013 08:22:25 +0000 Subject: igb: Changed LEDs blink mechanism to include designs using cathode This patch addresses the changes needed to make LEDs work properly with negative logic. This implementation uses LED Invert bit to reverse the logic issue that occurred when LEDs are driven by cathode. Keep LEDs blinking for SerDes devices. Also made changes to magic number and the for loop to reduce number of shifts. Signed-off-by: Akeem G Abodunrin Tested-by: Jeff Pieper Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/e1000_defines.h | 6 +++-- drivers/net/ethernet/intel/igb/e1000_mac.c | 37 +++++++++++++++++++------- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/e1000_defines.h b/drivers/net/ethernet/intel/igb/e1000_defines.h index 31a0f82cc650..aa176490ddbc 100644 --- a/drivers/net/ethernet/intel/igb/e1000_defines.h +++ b/drivers/net/ethernet/intel/igb/e1000_defines.h @@ -270,8 +270,10 @@ #define AUTONEG_ADVERTISE_SPEED_DEFAULT E1000_ALL_SPEED_DUPLEX /* LED Control */ -#define E1000_LEDCTL_LED0_MODE_SHIFT 0 -#define E1000_LEDCTL_LED0_BLINK 0x00000080 +#define E1000_LEDCTL_LED0_MODE_SHIFT 0 +#define E1000_LEDCTL_LED0_BLINK 0x00000080 +#define E1000_LEDCTL_LED0_MODE_MASK 0x0000000F +#define E1000_LEDCTL_LED0_IVRT 0x00000040 #define E1000_LEDCTL_MODE_LED_ON 0xE #define E1000_LEDCTL_MODE_LED_OFF 0xF diff --git a/drivers/net/ethernet/intel/igb/e1000_mac.c b/drivers/net/ethernet/intel/igb/e1000_mac.c index 2559d70a2321..660b7addfbb0 100644 --- a/drivers/net/ethernet/intel/igb/e1000_mac.c +++ b/drivers/net/ethernet/intel/igb/e1000_mac.c @@ -1406,15 +1406,34 @@ s32 igb_blink_led(struct e1000_hw *hw) u32 ledctl_blink = 0; u32 i; - /* set the blink bit for each LED that's "on" (0x0E) - * in ledctl_mode2 - */ - ledctl_blink = hw->mac.ledctl_mode2; - for (i = 0; i < 4; i++) - if (((hw->mac.ledctl_mode2 >> (i * 8)) & 0xFF) == - E1000_LEDCTL_MODE_LED_ON) - ledctl_blink |= (E1000_LEDCTL_LED0_BLINK << - (i * 8)); + if (hw->phy.media_type == e1000_media_type_fiber) { + /* always blink LED0 for PCI-E fiber */ + ledctl_blink = E1000_LEDCTL_LED0_BLINK | + (E1000_LEDCTL_MODE_LED_ON << E1000_LEDCTL_LED0_MODE_SHIFT); + } else { + /* Set the blink bit for each LED that's "on" (0x0E) + * (or "off" if inverted) in ledctl_mode2. The blink + * logic in hardware only works when mode is set to "on" + * so it must be changed accordingly when the mode is + * "off" and inverted. + */ + ledctl_blink = hw->mac.ledctl_mode2; + for (i = 0; i < 32; i += 8) { + u32 mode = (hw->mac.ledctl_mode2 >> i) & + E1000_LEDCTL_LED0_MODE_MASK; + u32 led_default = hw->mac.ledctl_default >> i; + + if ((!(led_default & E1000_LEDCTL_LED0_IVRT) && + (mode == E1000_LEDCTL_MODE_LED_ON)) || + ((led_default & E1000_LEDCTL_LED0_IVRT) && + (mode == E1000_LEDCTL_MODE_LED_OFF))) { + ledctl_blink &= + ~(E1000_LEDCTL_LED0_MODE_MASK << i); + ledctl_blink |= (E1000_LEDCTL_LED0_BLINK | + E1000_LEDCTL_MODE_LED_ON) << i; + } + } + } wr32(E1000_LEDCTL, ledctl_blink); -- cgit v1.2.3 From 7366937312d4e406539b1cf70e1562358bdd560e Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 3 May 2013 17:23:39 -0300 Subject: drm/i915: use the mode->htotal to calculate linetime watermarks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ... instead of mode->crtc_display. The spec says "pipe horizontal total number of pixels" and the "Haswell Watermark Calculator" tool uses the "Pipe H Total" instead of "Pipe H Src" as the value. Signed-off-by: Paulo Zanoni Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_pm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 2ae4e459faa5..198d9c9ada68 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -2093,7 +2093,7 @@ haswell_update_linetime_wm(struct drm_device *dev, struct drm_crtc *crtc) * row at the given clock rate, multiplied by 8. * */ temp |= PIPE_WM_LINETIME_TIME( - ((mode->crtc_hdisplay * 1000) / mode->clock) * 8); + ((mode->htotal * 1000) / mode->clock) * 8); /* IPS watermarks are only used by pipe A, and are ignored by * pipes B and C. They are calculated similarly to the common -- cgit v1.2.3 From eaa591ec528ad75bb4c77606c5cb671f05e04db6 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 3 May 2013 17:23:40 -0300 Subject: drm/i915: fix haswell linetime watermarks calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the "*8" calculation to the left side so we don't propagate rounding errors. Also use DIV_ROUND_CLOSEST because that's what the spec says we need to do. Signed-off-by: Paulo Zanoni Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_pm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 198d9c9ada68..2c406ddc6354 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -2093,7 +2093,7 @@ haswell_update_linetime_wm(struct drm_device *dev, struct drm_crtc *crtc) * row at the given clock rate, multiplied by 8. * */ temp |= PIPE_WM_LINETIME_TIME( - ((mode->htotal * 1000) / mode->clock) * 8); + DIV_ROUND_CLOSEST(mode->htotal * 1000 * 8, mode->clock)); /* IPS watermarks are only used by pipe A, and are ignored by * pipes B and C. They are calculated similarly to the common -- cgit v1.2.3 From 20a48412281732ddb75e0ac7d9e0b5406f1b6669 Mon Sep 17 00:00:00 2001 From: Matthew Vick Date: Wed, 24 Apr 2013 07:42:06 +0000 Subject: igb: Add update to last_rx_timestamp in Rx rings In order to support a more accurate check for a PTP Rx hang where the device can no longer timestamp received packets, we need to update, per ring, when the last Rx timestamp was. Because of how the PTP Rx hang logic works, the current logic is valid, but properly updating the ring variable increases the accuracy of the check. Signed-off-by: Matthew Vick Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/igb.h | 9 +++++++-- drivers/net/ethernet/intel/igb/igb_main.c | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h index 9d6c075e232d..a90570755ddc 100644 --- a/drivers/net/ethernet/intel/igb/igb.h +++ b/drivers/net/ethernet/intel/igb/igb.h @@ -514,13 +514,18 @@ extern void igb_ptp_rx_rgtstamp(struct igb_q_vector *q_vector, extern void igb_ptp_rx_pktstamp(struct igb_q_vector *q_vector, unsigned char *va, struct sk_buff *skb); -static inline void igb_ptp_rx_hwtstamp(struct igb_q_vector *q_vector, +static inline void igb_ptp_rx_hwtstamp(struct igb_ring *rx_ring, union e1000_adv_rx_desc *rx_desc, struct sk_buff *skb) { if (igb_test_staterr(rx_desc, E1000_RXDADV_STAT_TS) && !igb_test_staterr(rx_desc, E1000_RXDADV_STAT_TSIP)) - igb_ptp_rx_rgtstamp(q_vector, skb); + igb_ptp_rx_rgtstamp(rx_ring->q_vector, skb); + + /* Update the last_rx_timestamp timer in order to enable watchdog check + * for error case of latched timestamp on a dropped packet. + */ + rx_ring->last_rx_timestamp = jiffies; } extern int igb_ptp_hwtstamp_ioctl(struct net_device *netdev, diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 64cbe0dfe043..2c8f7e04b02b 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -6622,7 +6622,7 @@ static void igb_process_skb_fields(struct igb_ring *rx_ring, igb_rx_checksum(rx_ring, rx_desc, skb); - igb_ptp_rx_hwtstamp(rx_ring->q_vector, rx_desc, skb); + igb_ptp_rx_hwtstamp(rx_ring, rx_desc, skb); if ((dev->features & NETIF_F_HW_VLAN_CTAG_RX) && igb_test_staterr(rx_desc, E1000_RXD_STAT_VP)) { -- cgit v1.2.3 From 641ac5c0cd46919dc9be4c933f95edae1e4e4163 Mon Sep 17 00:00:00 2001 From: "Akeem G. Abodunrin" Date: Wed, 24 Apr 2013 16:54:50 +0000 Subject: igb: Support for SFP modules discovery This patch adds support for SFP modules media type discovery for SGMII, which will enable driver to detect supported external PHYs, including 100baseFXSFP module. Signed-off-by: Akeem G Abodunrin Signed-off-by: Carolyn Wyborny Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/e1000_82575.c | 120 +++++++++++++++++++++++- drivers/net/ethernet/intel/igb/e1000_defines.h | 30 +++--- drivers/net/ethernet/intel/igb/e1000_hw.h | 2 + drivers/net/ethernet/intel/igb/e1000_phy.c | 124 +++++++++++++++++++++++++ drivers/net/ethernet/intel/igb/e1000_phy.h | 20 ++++ drivers/net/ethernet/intel/igb/igb_ethtool.c | 34 +++---- 6 files changed, 291 insertions(+), 39 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.c b/drivers/net/ethernet/intel/igb/e1000_82575.c index ff6a17cb1362..f21a91a299a2 100644 --- a/drivers/net/ethernet/intel/igb/e1000_82575.c +++ b/drivers/net/ethernet/intel/igb/e1000_82575.c @@ -401,12 +401,82 @@ static s32 igb_init_mac_params_82575(struct e1000_hw *hw) return 0; } +/** + * igb_set_sfp_media_type_82575 - derives SFP module media type. + * @hw: pointer to the HW structure + * + * The media type is chosen based on SFP module. + * compatibility flags retrieved from SFP ID EEPROM. + **/ +static s32 igb_set_sfp_media_type_82575(struct e1000_hw *hw) +{ + s32 ret_val = E1000_ERR_CONFIG; + u32 ctrl_ext = 0; + struct e1000_dev_spec_82575 *dev_spec = &hw->dev_spec._82575; + struct e1000_sfp_flags *eth_flags = &dev_spec->eth_flags; + u8 tranceiver_type = 0; + s32 timeout = 3; + + /* Turn I2C interface ON and power on sfp cage */ + ctrl_ext = rd32(E1000_CTRL_EXT); + ctrl_ext &= ~E1000_CTRL_EXT_SDP3_DATA; + wr32(E1000_CTRL_EXT, ctrl_ext | E1000_CTRL_I2C_ENA); + + wrfl(); + + /* Read SFP module data */ + while (timeout) { + ret_val = igb_read_sfp_data_byte(hw, + E1000_I2CCMD_SFP_DATA_ADDR(E1000_SFF_IDENTIFIER_OFFSET), + &tranceiver_type); + if (ret_val == 0) + break; + msleep(100); + timeout--; + } + if (ret_val != 0) + goto out; + + ret_val = igb_read_sfp_data_byte(hw, + E1000_I2CCMD_SFP_DATA_ADDR(E1000_SFF_ETH_FLAGS_OFFSET), + (u8 *)eth_flags); + if (ret_val != 0) + goto out; + + /* Check if there is some SFP module plugged and powered */ + if ((tranceiver_type == E1000_SFF_IDENTIFIER_SFP) || + (tranceiver_type == E1000_SFF_IDENTIFIER_SFF)) { + dev_spec->module_plugged = true; + if (eth_flags->e1000_base_lx || eth_flags->e1000_base_sx) { + hw->phy.media_type = e1000_media_type_internal_serdes; + } else if (eth_flags->e100_base_fx) { + dev_spec->sgmii_active = true; + hw->phy.media_type = e1000_media_type_internal_serdes; + } else if (eth_flags->e1000_base_t) { + dev_spec->sgmii_active = true; + hw->phy.media_type = e1000_media_type_copper; + } else { + hw->phy.media_type = e1000_media_type_unknown; + hw_dbg("PHY module has not been recognized\n"); + goto out; + } + } else { + hw->phy.media_type = e1000_media_type_unknown; + } + ret_val = 0; +out: + /* Restore I2C interface setting */ + wr32(E1000_CTRL_EXT, ctrl_ext); + return ret_val; +} + static s32 igb_get_invariants_82575(struct e1000_hw *hw) { struct e1000_mac_info *mac = &hw->mac; struct e1000_dev_spec_82575 * dev_spec = &hw->dev_spec._82575; s32 ret_val; u32 ctrl_ext = 0; + u32 link_mode = 0; switch (hw->device_id) { case E1000_DEV_ID_82575EB_COPPER: @@ -470,15 +540,55 @@ static s32 igb_get_invariants_82575(struct e1000_hw *hw) */ hw->phy.media_type = e1000_media_type_copper; dev_spec->sgmii_active = false; + dev_spec->module_plugged = false; ctrl_ext = rd32(E1000_CTRL_EXT); - switch (ctrl_ext & E1000_CTRL_EXT_LINK_MODE_MASK) { - case E1000_CTRL_EXT_LINK_MODE_SGMII: - dev_spec->sgmii_active = true; - break; + + link_mode = ctrl_ext & E1000_CTRL_EXT_LINK_MODE_MASK; + switch (link_mode) { case E1000_CTRL_EXT_LINK_MODE_1000BASE_KX: - case E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES: hw->phy.media_type = e1000_media_type_internal_serdes; + break; + case E1000_CTRL_EXT_LINK_MODE_SGMII: + /* Get phy control interface type set (MDIO vs. I2C)*/ + if (igb_sgmii_uses_mdio_82575(hw)) { + hw->phy.media_type = e1000_media_type_copper; + dev_spec->sgmii_active = true; + break; + } + /* fall through for I2C based SGMII */ + case E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES: + /* read media type from SFP EEPROM */ + ret_val = igb_set_sfp_media_type_82575(hw); + if ((ret_val != 0) || + (hw->phy.media_type == e1000_media_type_unknown)) { + /* If media type was not identified then return media + * type defined by the CTRL_EXT settings. + */ + hw->phy.media_type = e1000_media_type_internal_serdes; + + if (link_mode == E1000_CTRL_EXT_LINK_MODE_SGMII) { + hw->phy.media_type = e1000_media_type_copper; + dev_spec->sgmii_active = true; + } + + break; + } + + /* do not change link mode for 100BaseFX */ + if (dev_spec->eth_flags.e100_base_fx) + break; + + /* change current link mode setting */ + ctrl_ext &= ~E1000_CTRL_EXT_LINK_MODE_MASK; + + if (hw->phy.media_type == e1000_media_type_copper) + ctrl_ext |= E1000_CTRL_EXT_LINK_MODE_SGMII; + else + ctrl_ext |= E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES; + + wr32(E1000_CTRL_EXT, ctrl_ext); + break; default: break; diff --git a/drivers/net/ethernet/intel/igb/e1000_defines.h b/drivers/net/ethernet/intel/igb/e1000_defines.h index aa176490ddbc..aa201abb8ad2 100644 --- a/drivers/net/ethernet/intel/igb/e1000_defines.h +++ b/drivers/net/ethernet/intel/igb/e1000_defines.h @@ -61,20 +61,22 @@ /* Clear Interrupt timers after IMS clear */ /* packet buffer parity error detection enabled */ /* descriptor FIFO parity error detection enable */ -#define E1000_CTRL_EXT_PBA_CLR 0x80000000 /* PBA Clear */ -#define E1000_I2CCMD_REG_ADDR_SHIFT 16 -#define E1000_I2CCMD_PHY_ADDR_SHIFT 24 -#define E1000_I2CCMD_OPCODE_READ 0x08000000 -#define E1000_I2CCMD_OPCODE_WRITE 0x00000000 -#define E1000_I2CCMD_READY 0x20000000 -#define E1000_I2CCMD_ERROR 0x80000000 -#define E1000_MAX_SGMII_PHY_REG_ADDR 255 -#define E1000_I2CCMD_PHY_TIMEOUT 200 -#define E1000_IVAR_VALID 0x80 -#define E1000_GPIE_NSICR 0x00000001 -#define E1000_GPIE_MSIX_MODE 0x00000010 -#define E1000_GPIE_EIAME 0x40000000 -#define E1000_GPIE_PBA 0x80000000 +#define E1000_CTRL_EXT_PBA_CLR 0x80000000 /* PBA Clear */ +#define E1000_I2CCMD_REG_ADDR_SHIFT 16 +#define E1000_I2CCMD_PHY_ADDR_SHIFT 24 +#define E1000_I2CCMD_OPCODE_READ 0x08000000 +#define E1000_I2CCMD_OPCODE_WRITE 0x00000000 +#define E1000_I2CCMD_READY 0x20000000 +#define E1000_I2CCMD_ERROR 0x80000000 +#define E1000_I2CCMD_SFP_DATA_ADDR(a) (0x0000 + (a)) +#define E1000_I2CCMD_SFP_DIAG_ADDR(a) (0x0100 + (a)) +#define E1000_MAX_SGMII_PHY_REG_ADDR 255 +#define E1000_I2CCMD_PHY_TIMEOUT 200 +#define E1000_IVAR_VALID 0x80 +#define E1000_GPIE_NSICR 0x00000001 +#define E1000_GPIE_MSIX_MODE 0x00000010 +#define E1000_GPIE_EIAME 0x40000000 +#define E1000_GPIE_PBA 0x80000000 /* Receive Descriptor bit definitions */ #define E1000_RXD_STAT_DD 0x01 /* Descriptor Done */ diff --git a/drivers/net/ethernet/intel/igb/e1000_hw.h b/drivers/net/ethernet/intel/igb/e1000_hw.h index 488abb24a54f..94d7866b9c20 100644 --- a/drivers/net/ethernet/intel/igb/e1000_hw.h +++ b/drivers/net/ethernet/intel/igb/e1000_hw.h @@ -528,6 +528,8 @@ struct e1000_dev_spec_82575 { bool global_device_reset; bool eee_disable; bool clear_semaphore_once; + struct e1000_sfp_flags eth_flags; + bool module_plugged; }; struct e1000_hw { diff --git a/drivers/net/ethernet/intel/igb/e1000_phy.c b/drivers/net/ethernet/intel/igb/e1000_phy.c index 115b0da6e013..1d6a401cc5d4 100644 --- a/drivers/net/ethernet/intel/igb/e1000_phy.c +++ b/drivers/net/ethernet/intel/igb/e1000_phy.c @@ -340,6 +340,130 @@ s32 igb_write_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 data) return 0; } +/** + * igb_read_sfp_data_byte - Reads SFP module data. + * @hw: pointer to the HW structure + * @offset: byte location offset to be read + * @data: read data buffer pointer + * + * Reads one byte from SFP module data stored + * in SFP resided EEPROM memory or SFP diagnostic area. + * Function should be called with + * E1000_I2CCMD_SFP_DATA_ADDR() for SFP module database access + * E1000_I2CCMD_SFP_DIAG_ADDR() for SFP diagnostics parameters + * access + **/ +s32 igb_read_sfp_data_byte(struct e1000_hw *hw, u16 offset, u8 *data) +{ + u32 i = 0; + u32 i2ccmd = 0; + u32 data_local = 0; + + if (offset > E1000_I2CCMD_SFP_DIAG_ADDR(255)) { + hw_dbg("I2CCMD command address exceeds upper limit\n"); + return -E1000_ERR_PHY; + } + + /* Set up Op-code, EEPROM Address,in the I2CCMD + * register. The MAC will take care of interfacing with the + * EEPROM to retrieve the desired data. + */ + i2ccmd = ((offset << E1000_I2CCMD_REG_ADDR_SHIFT) | + E1000_I2CCMD_OPCODE_READ); + + wr32(E1000_I2CCMD, i2ccmd); + + /* Poll the ready bit to see if the I2C read completed */ + for (i = 0; i < E1000_I2CCMD_PHY_TIMEOUT; i++) { + udelay(50); + data_local = rd32(E1000_I2CCMD); + if (data_local & E1000_I2CCMD_READY) + break; + } + if (!(data_local & E1000_I2CCMD_READY)) { + hw_dbg("I2CCMD Read did not complete\n"); + return -E1000_ERR_PHY; + } + if (data_local & E1000_I2CCMD_ERROR) { + hw_dbg("I2CCMD Error bit set\n"); + return -E1000_ERR_PHY; + } + *data = (u8) data_local & 0xFF; + + return 0; +} + +/** + * e1000_write_sfp_data_byte - Writes SFP module data. + * @hw: pointer to the HW structure + * @offset: byte location offset to write to + * @data: data to write + * + * Writes one byte to SFP module data stored + * in SFP resided EEPROM memory or SFP diagnostic area. + * Function should be called with + * E1000_I2CCMD_SFP_DATA_ADDR() for SFP module database access + * E1000_I2CCMD_SFP_DIAG_ADDR() for SFP diagnostics parameters + * access + **/ +s32 e1000_write_sfp_data_byte(struct e1000_hw *hw, u16 offset, u8 data) +{ + u32 i = 0; + u32 i2ccmd = 0; + u32 data_local = 0; + + if (offset > E1000_I2CCMD_SFP_DIAG_ADDR(255)) { + hw_dbg("I2CCMD command address exceeds upper limit\n"); + return -E1000_ERR_PHY; + } + /* The programming interface is 16 bits wide + * so we need to read the whole word first + * then update appropriate byte lane and write + * the updated word back. + */ + /* Set up Op-code, EEPROM Address,in the I2CCMD + * register. The MAC will take care of interfacing + * with an EEPROM to write the data given. + */ + i2ccmd = ((offset << E1000_I2CCMD_REG_ADDR_SHIFT) | + E1000_I2CCMD_OPCODE_READ); + /* Set a command to read single word */ + wr32(E1000_I2CCMD, i2ccmd); + for (i = 0; i < E1000_I2CCMD_PHY_TIMEOUT; i++) { + udelay(50); + /* Poll the ready bit to see if lastly + * launched I2C operation completed + */ + i2ccmd = rd32(E1000_I2CCMD); + if (i2ccmd & E1000_I2CCMD_READY) { + /* Check if this is READ or WRITE phase */ + if ((i2ccmd & E1000_I2CCMD_OPCODE_READ) == + E1000_I2CCMD_OPCODE_READ) { + /* Write the selected byte + * lane and update whole word + */ + data_local = i2ccmd & 0xFF00; + data_local |= data; + i2ccmd = ((offset << + E1000_I2CCMD_REG_ADDR_SHIFT) | + E1000_I2CCMD_OPCODE_WRITE | data_local); + wr32(E1000_I2CCMD, i2ccmd); + } else { + break; + } + } + } + if (!(i2ccmd & E1000_I2CCMD_READY)) { + hw_dbg("I2CCMD Write did not complete\n"); + return -E1000_ERR_PHY; + } + if (i2ccmd & E1000_I2CCMD_ERROR) { + hw_dbg("I2CCMD Error bit set\n"); + return -E1000_ERR_PHY; + } + return 0; +} + /** * igb_read_phy_reg_igp - Read igp PHY register * @hw: pointer to the HW structure diff --git a/drivers/net/ethernet/intel/igb/e1000_phy.h b/drivers/net/ethernet/intel/igb/e1000_phy.h index 784fd1c40989..6a0873f2095a 100644 --- a/drivers/net/ethernet/intel/igb/e1000_phy.h +++ b/drivers/net/ethernet/intel/igb/e1000_phy.h @@ -69,6 +69,8 @@ s32 igb_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data); s32 igb_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data); s32 igb_read_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 *data); s32 igb_write_phy_reg_i2c(struct e1000_hw *hw, u32 offset, u16 data); +s32 igb_read_sfp_data_byte(struct e1000_hw *hw, u16 offset, u8 *data); +s32 e1000_write_sfp_data_byte(struct e1000_hw *hw, u16 offset, u8 data); s32 igb_copper_link_setup_82580(struct e1000_hw *hw); s32 igb_get_phy_info_82580(struct e1000_hw *hw); s32 igb_phy_force_speed_duplex_82580(struct e1000_hw *hw); @@ -157,4 +159,22 @@ s32 igb_check_polarity_m88(struct e1000_hw *hw); #define GS40G_CS_POWER_DOWN 0x0002 #define GS40G_LINE_LB 0x4000 +/* SFP modules ID memory locations */ +#define E1000_SFF_IDENTIFIER_OFFSET 0x00 +#define E1000_SFF_IDENTIFIER_SFF 0x02 +#define E1000_SFF_IDENTIFIER_SFP 0x03 + +#define E1000_SFF_ETH_FLAGS_OFFSET 0x06 +/* Flags for SFP modules compatible with ETH up to 1Gb */ +struct e1000_sfp_flags { + u8 e1000_base_sx:1; + u8 e1000_base_lx:1; + u8 e1000_base_cx:1; + u8 e1000_base_t:1; + u8 e100_base_lx:1; + u8 e100_base_fx:1; + u8 e10_base_bx10:1; + u8 e10_base_px:1; +}; + #endif diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c index 7876240fa74e..4e54f847ac92 100644 --- a/drivers/net/ethernet/intel/igb/igb_ethtool.c +++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c @@ -142,6 +142,8 @@ static int igb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) { struct igb_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; + struct e1000_dev_spec_82575 *dev_spec = &hw->dev_spec._82575; + struct e1000_sfp_flags *eth_flags = &dev_spec->eth_flags; u32 status; if (hw->phy.media_type == e1000_media_type_copper) { @@ -181,30 +183,22 @@ static int igb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) ecmd->phy_address = hw->phy.addr; ecmd->transceiver = XCVR_INTERNAL; } else { - ecmd->supported = (SUPPORTED_1000baseT_Full | - SUPPORTED_100baseT_Full | - SUPPORTED_FIBRE | + ecmd->supported = (SUPPORTED_FIBRE | SUPPORTED_Autoneg | SUPPORTED_Pause); - if (hw->mac.type == e1000_i354) - ecmd->supported |= SUPPORTED_2500baseX_Full; - ecmd->advertising = ADVERTISED_FIBRE; - - switch (adapter->link_speed) { - case SPEED_2500: - ecmd->advertising = ADVERTISED_2500baseX_Full; - break; - case SPEED_1000: - ecmd->advertising = ADVERTISED_1000baseT_Full; - break; - case SPEED_100: - ecmd->advertising = ADVERTISED_100baseT_Full; - break; - default: - break; + if (hw->mac.type == e1000_i354) { + ecmd->supported |= SUPPORTED_2500baseX_Full; + ecmd->advertising |= ADVERTISED_2500baseX_Full; + } + if ((eth_flags->e1000_base_lx) || (eth_flags->e1000_base_sx)) { + ecmd->supported |= SUPPORTED_1000baseT_Full; + ecmd->advertising |= ADVERTISED_1000baseT_Full; + } + if (eth_flags->e100_base_fx) { + ecmd->supported |= SUPPORTED_100baseT_Full; + ecmd->advertising |= ADVERTISED_100baseT_Full; } - if (hw->mac.autoneg == 1) ecmd->advertising |= ADVERTISED_Autoneg; -- cgit v1.2.3 From 373e6978f9c5c05af3b3ec4cd0295b0bfbe11644 Mon Sep 17 00:00:00 2001 From: "Akeem G. Abodunrin" Date: Fri, 29 Mar 2013 15:22:17 +0000 Subject: igb: SerDes flow control setting This path allows users to get appropriate flow control setting on SerDes devices, based on original implementation for Copper devices. Also, since 100baseFX does not support setting flow control, so exclude it from the setting mechanism. Signed-off-by: Akeem G. Abodunrin Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/igb_ethtool.c | 34 ++++++++++++++++------------ 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c index 4e54f847ac92..7b25ee2e81c0 100644 --- a/drivers/net/ethernet/intel/igb/igb_ethtool.c +++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c @@ -164,21 +164,6 @@ static int igb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) ecmd->advertising |= hw->phy.autoneg_advertised; } - if (hw->mac.autoneg != 1) - ecmd->advertising &= ~(ADVERTISED_Pause | - ADVERTISED_Asym_Pause); - - if (hw->fc.requested_mode == e1000_fc_full) - ecmd->advertising |= ADVERTISED_Pause; - else if (hw->fc.requested_mode == e1000_fc_rx_pause) - ecmd->advertising |= (ADVERTISED_Pause | - ADVERTISED_Asym_Pause); - else if (hw->fc.requested_mode == e1000_fc_tx_pause) - ecmd->advertising |= ADVERTISED_Asym_Pause; - else - ecmd->advertising &= ~(ADVERTISED_Pause | - ADVERTISED_Asym_Pause); - ecmd->port = PORT_TP; ecmd->phy_address = hw->phy.addr; ecmd->transceiver = XCVR_INTERNAL; @@ -206,6 +191,21 @@ static int igb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) ecmd->transceiver = XCVR_EXTERNAL; } + if (hw->mac.autoneg != 1) + ecmd->advertising &= ~(ADVERTISED_Pause | + ADVERTISED_Asym_Pause); + + if (hw->fc.requested_mode == e1000_fc_full) + ecmd->advertising |= ADVERTISED_Pause; + else if (hw->fc.requested_mode == e1000_fc_rx_pause) + ecmd->advertising |= (ADVERTISED_Pause | + ADVERTISED_Asym_Pause); + else if (hw->fc.requested_mode == e1000_fc_tx_pause) + ecmd->advertising |= ADVERTISED_Asym_Pause; + else + ecmd->advertising &= ~(ADVERTISED_Pause | + ADVERTISED_Asym_Pause); + status = rd32(E1000_STATUS); if (status & E1000_STATUS_LU) { @@ -386,6 +386,10 @@ static int igb_set_pauseparam(struct net_device *netdev, struct e1000_hw *hw = &adapter->hw; int retval = 0; + /* 100basefx does not support setting link flow control */ + if (hw->dev_spec._82575.eth_flags.e100_base_fx) + return -EINVAL; + adapter->fc_autoneg = pause->autoneg; while (test_and_set_bit(__IGB_RESETTING, &adapter->state)) -- cgit v1.2.3 From 2a0a0f1ea27ac39afd25c741a1ccc53bc5530acf Mon Sep 17 00:00:00 2001 From: Carolyn Wyborny Date: Thu, 25 Apr 2013 17:22:34 +0000 Subject: igb: Fix set_ethtool function to call update nvm for entire image This patch fixes a problem where we were only checking to update checksum on first part of nvm image. Newer parts have multiple checksum fields and checksum function will accommodate that as long as we call it in the first place for any changes made. Signed-off-by: Carolyn Wyborny Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/igb_ethtool.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c index 7b25ee2e81c0..85fe7b52f435 100644 --- a/drivers/net/ethernet/intel/igb/igb_ethtool.c +++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c @@ -811,10 +811,8 @@ static int igb_set_eeprom(struct net_device *netdev, ret_val = hw->nvm.ops.write(hw, first_word, last_word - first_word + 1, eeprom_buff); - /* Update the checksum over the first part of the EEPROM if needed - * and flush shadow RAM for 82573 controllers - */ - if ((ret_val == 0) && ((first_word <= NVM_CHECKSUM_REG))) + /* Update the checksum if nvm write succeeded */ + if (ret_val == 0) hw->nvm.ops.update(hw); igb_set_fw_version(adapter); -- cgit v1.2.3 From b2b877ffe37d699f77f45e993590b66010491c52 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 3 May 2013 17:23:42 -0300 Subject: drm/i915: make intel_ddi_get_cdclk_freq return values in KHz MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With this, that 338 can finally become the correct 337500. Due to the change we need to adjust the intel_dp_aux_ch function to set the correct value, so adjust the division and also use DIV_ROUND_CLOSEST instead of the old "round down" behavior because the spec says the value "should be programmed to get as close as possible to the ideal rate of 2MHz". Quoting Paulo's follow-up to a question from Chris Wilson to explain what exactly will change: I use the 337500 value on the next patch, when setting the ips_linetime value. The correct frequency is 337500, not 338000. ips_linetime = DIV_ROUND_CLOSEST(mode->htotal * 1000 * 8, intel_ddi_get_cdclk_freq); For a mode with htotal of 2640 [0] we'll have: (i) (2640 * 1000 * 8) / 338000 = 62.48, resulting in 62 and (ii) (2640 * 1000 * 8) / 337500 = 62.57 resulting in 63. For the case inside intel_dp.c: Previously we were using 338. So with the old formula we were writing 338/2 = 169 to the register. And 337500 / 169 = 1997.04 (we use 337500 here because it's the real clock value). With the new value of 337500/2000 we'll have 168.75, which is 168 on the round-down case and 169 on the round-closest case. If we write 168 to the register, 337500 / 168 = 2008.92, and 2008.92 is more distant from 2000 than 1997.04. So with this patch we're changing the formula but still writing the same correct value to the DP AUX register. [0]: That's 1920x1080@50Hz on my DP monitor. Signed-off-by: Paulo Zanoni [danvet: Pimp the commit message with Paulo's follow-up.] Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ddi.c | 10 +++++----- drivers/gpu/drm/i915/intel_dp.c | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index e66852186751..1fdd3b0b7040 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -1153,14 +1153,14 @@ static void intel_disable_ddi(struct intel_encoder *intel_encoder) int intel_ddi_get_cdclk_freq(struct drm_i915_private *dev_priv) { if (I915_READ(HSW_FUSE_STRAP) & HSW_CDCLK_LIMIT) - return 450; + return 450000; else if ((I915_READ(LCPLL_CTL) & LCPLL_CLK_FREQ_MASK) == LCPLL_CLK_FREQ_450) - return 450; + return 450000; else if (IS_ULT(dev_priv->dev)) - return 338; + return 337500; else - return 540; + return 540000; } void intel_ddi_pll_init(struct drm_device *dev) @@ -1173,7 +1173,7 @@ void intel_ddi_pll_init(struct drm_device *dev) * Don't even try to turn it on. */ - DRM_DEBUG_KMS("CDCLK running at %dMHz\n", + DRM_DEBUG_KMS("CDCLK running at %dKHz\n", intel_ddi_get_cdclk_freq(dev_priv)); if (val & LCPLL_CD_SOURCE_FCLK) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 7c5eb2f6208f..3426a6183188 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -319,7 +319,8 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, */ if (is_cpu_edp(intel_dp)) { if (HAS_DDI(dev)) - aux_clock_divider = intel_ddi_get_cdclk_freq(dev_priv) >> 1; + aux_clock_divider = DIV_ROUND_CLOSEST( + intel_ddi_get_cdclk_freq(dev_priv), 2000); else if (IS_VALLEYVIEW(dev)) aux_clock_divider = 100; else if (IS_GEN6(dev) || IS_GEN7(dev)) -- cgit v1.2.3 From 41f149a285da21529bc9a0bad323df53b2f17b16 Mon Sep 17 00:00:00 2001 From: Carolyn Wyborny Date: Tue, 30 Apr 2013 00:21:32 +0000 Subject: igb: Fix possible panic caused by Rx traffic arrival while interface is down This patch reorders disabling napi and irqs during igb_down. This is done to avoid possible panic's found in other Intel drivers when Rx traffic arrives while interface is going down. Signed-off-by: Carolyn Wyborny Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/igb_main.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 2c8f7e04b02b..6a0c1b66ce54 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -1667,10 +1667,13 @@ void igb_down(struct igb_adapter *adapter) wrfl(); msleep(10); - for (i = 0; i < adapter->num_q_vectors; i++) + igb_irq_disable(adapter); + + for (i = 0; i < adapter->num_q_vectors; i++) { + napi_synchronize(&(adapter->q_vector[i]->napi)); napi_disable(&(adapter->q_vector[i]->napi)); + } - igb_irq_disable(adapter); del_timer_sync(&adapter->watchdog_timer); del_timer_sync(&adapter->phy_info_timer); -- cgit v1.2.3 From 85a02deb4ca5a7e1e39e8538b6eb3c7066469720 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 3 May 2013 17:23:43 -0300 Subject: drm/i915: set the IPS linetime watermark MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the "placeholder" comment and set the actual value described by the specification. We still don't enable IPS, but it won't hurt to already have the value set here. While at it, fully set the register value instead of just masking the values we're changing. Signed-off-by: Paulo Zanoni Reviewed-by: Ville Syrjälä [danvet: Resolve conflict due to reordered patches.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_pm.c | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 2c406ddc6354..ad1d35526a8f 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -2079,31 +2079,23 @@ haswell_update_linetime_wm(struct drm_device *dev, struct drm_crtc *crtc) struct intel_crtc *intel_crtc = to_intel_crtc(crtc); enum pipe pipe = intel_crtc->pipe; struct drm_display_mode *mode = &intel_crtc->config.adjusted_mode; - u32 temp; + u32 linetime, ips_linetime; if (!intel_crtc_active(crtc)) { I915_WRITE(PIPE_WM_LINETIME(pipe), 0); return; } - temp = I915_READ(PIPE_WM_LINETIME(pipe)); - temp &= ~PIPE_WM_LINETIME_MASK; - /* The WM are computed with base on how long it takes to fill a single * row at the given clock rate, multiplied by 8. * */ - temp |= PIPE_WM_LINETIME_TIME( - DIV_ROUND_CLOSEST(mode->htotal * 1000 * 8, mode->clock)); - - /* IPS watermarks are only used by pipe A, and are ignored by - * pipes B and C. They are calculated similarly to the common - * linetime values, except that we are using CD clock frequency - * in MHz instead of pixel rate for the division. - * - * This is a placeholder for the IPS watermark calculation code. - */ + linetime = DIV_ROUND_CLOSEST(mode->htotal * 1000 * 8, mode->clock); + ips_linetime = DIV_ROUND_CLOSEST(mode->htotal * 1000 * 8, + intel_ddi_get_cdclk_freq(dev_priv)); - I915_WRITE(PIPE_WM_LINETIME(pipe), temp); + I915_WRITE(PIPE_WM_LINETIME(pipe), + PIPE_WM_LINETIME_IPS_LINETIME(ips_linetime) | + PIPE_WM_LINETIME_TIME(linetime)); } static void haswell_update_wm(struct drm_device *dev) -- cgit v1.2.3 From 3e1f72664e0a8a31e9b90c48459deb6642fd52f3 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 3 May 2013 17:23:44 -0300 Subject: drm/i915: MCH_SSKPD is a 64 bit register on Haswell MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit And the SNB_READ_WM0_LATENCY macro is not valid anymore because we have the "New WM0" at 63:56, so the "Old WM0" could maybe be zero if the new one is not zero. Signed-off-by: Paulo Zanoni Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_pm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index ad1d35526a8f..912ab4d8d722 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -4633,7 +4633,7 @@ void intel_init_pm(struct drm_device *dev) } dev_priv->display.init_clock_gating = ivybridge_init_clock_gating; } else if (IS_HASWELL(dev)) { - if (SNB_READ_WM0_LATENCY()) { + if (I915_READ64(MCH_SSKPD)) { dev_priv->display.update_wm = haswell_update_wm; dev_priv->display.update_sprite_wm = sandybridge_update_sprite_wm; } else { -- cgit v1.2.3 From 6f8b916065596d80843bb7d4f601ef72b3223c54 Mon Sep 17 00:00:00 2001 From: "Akeem G. Abodunrin" Date: Wed, 1 May 2013 05:44:45 +0000 Subject: igb: Implementation of i210/i211 LED support This patch fixes LED issues with i210 and i211 devices, due to changes in the device registers. Signed-off-by: Akeem G Abodunrin Tested-by: Jeff Pieper Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/e1000_i210.h | 6 +++--- drivers/net/ethernet/intel/igb/e1000_mac.c | 8 +++++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/e1000_i210.h b/drivers/net/ethernet/intel/igb/e1000_i210.h index bfc08e05c907..5caa332e7556 100644 --- a/drivers/net/ethernet/intel/igb/e1000_i210.h +++ b/drivers/net/ethernet/intel/igb/e1000_i210.h @@ -82,11 +82,11 @@ enum E1000_INVM_STRUCTURE_TYPE { #define E1000_INVM_MAJOR_SHIFT 4 #define ID_LED_DEFAULT_I210 ((ID_LED_OFF1_ON2 << 8) | \ - (ID_LED_OFF1_OFF2 << 4) | \ - (ID_LED_DEF1_DEF2)) + (ID_LED_DEF1_DEF2 << 4) | \ + (ID_LED_OFF1_OFF2)) #define ID_LED_DEFAULT_I210_SERDES ((ID_LED_DEF1_DEF2 << 8) | \ (ID_LED_DEF1_DEF2 << 4) | \ - (ID_LED_DEF1_DEF2)) + (ID_LED_OFF1_ON2)) /* NVM offset defaults for i211 device */ #define NVM_INIT_CTRL_2_DEFAULT_I211 0X7243 diff --git a/drivers/net/ethernet/intel/igb/e1000_mac.c b/drivers/net/ethernet/intel/igb/e1000_mac.c index 660b7addfbb0..bab556a47fcc 100644 --- a/drivers/net/ethernet/intel/igb/e1000_mac.c +++ b/drivers/net/ethernet/intel/igb/e1000_mac.c @@ -1332,7 +1332,13 @@ s32 igb_id_led_init(struct e1000_hw *hw) u16 data, i, temp; const u16 led_mask = 0x0F; - ret_val = igb_valid_led_default(hw, &data); + /* i210 and i211 devices have different LED mechanism */ + if ((hw->mac.type == e1000_i210) || + (hw->mac.type == e1000_i211)) + ret_val = igb_valid_led_default_i210(hw, &data); + else + ret_val = igb_valid_led_default(hw, &data); + if (ret_val) goto out; -- cgit v1.2.3 From f7727c53094bd1122351a14a2087585f46009c04 Mon Sep 17 00:00:00 2001 From: "Akeem G. Abodunrin" Date: Wed, 15 May 2013 07:41:30 +0000 Subject: igb: Removed unused i2c function This patch removes unused i2c function definition. Signed-off-by: Akeem G Abodunrin Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/igb.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h index a90570755ddc..15ea8dc9dad3 100644 --- a/drivers/net/ethernet/intel/igb/igb.h +++ b/drivers/net/ethernet/intel/igb/igb.h @@ -322,11 +322,6 @@ static inline int igb_desc_unused(struct igb_ring *ring) return ring->count + ring->next_to_clean - ring->next_to_use - 1; } -struct igb_i2c_client_list { - struct i2c_client *client; - struct igb_i2c_client_list *next; -}; - #ifdef CONFIG_IGB_HWMON #define IGB_HWMON_TYPE_LOC 0 -- cgit v1.2.3 From e8915bebb4b33c63d6e08161edc3fbda32a840a7 Mon Sep 17 00:00:00 2001 From: Amir Hanania Date: Thu, 18 Apr 2013 04:23:52 +0000 Subject: IXGBE: Set the SW prio_tc values at initialization to the HW setting. Set the SW prio_tc values at initialization to the HW setting. Setting the SW prio_tc default values to be the HW setting by reading the rtrup2tc register. For any TC change we need to reset the device. This will remove the need to reset the device at the first time we call ixgbe_dcbnl_ieee_setets. Signed-off-by: Amir Hanania Tested-by: Jack Morgan Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c | 23 ++++++++++++++++++++++ drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.h | 2 ++ drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h | 1 + drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c | 3 +++ 4 files changed, 29 insertions(+) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c index 1f2c805684dd..e055e000131b 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.c @@ -380,3 +380,26 @@ s32 ixgbe_dcb_hw_ets_config(struct ixgbe_hw *hw, } return 0; } + +static void ixgbe_dcb_read_rtrup2tc_82599(struct ixgbe_hw *hw, u8 *map) +{ + u32 reg, i; + + reg = IXGBE_READ_REG(hw, IXGBE_RTRUP2TC); + for (i = 0; i < MAX_USER_PRIORITY; i++) + map[i] = IXGBE_RTRUP2TC_UP_MASK & + (reg >> (i * IXGBE_RTRUP2TC_UP_SHIFT)); + return; +} + +void ixgbe_dcb_read_rtrup2tc(struct ixgbe_hw *hw, u8 *map) +{ + switch (hw->mac.type) { + case ixgbe_mac_82599EB: + case ixgbe_mac_X540: + ixgbe_dcb_read_rtrup2tc_82599(hw, map); + break; + default: + break; + } +} diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.h index 1634de8b627f..fc0a2dd52499 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb.h @@ -159,6 +159,8 @@ s32 ixgbe_dcb_hw_ets_config(struct ixgbe_hw *hw, u16 *refill, u16 *max, s32 ixgbe_dcb_hw_pfc_config(struct ixgbe_hw *hw, u8 pfc_en, u8 *tc_prio); s32 ixgbe_dcb_hw_config(struct ixgbe_hw *, struct ixgbe_dcb_config *); +void ixgbe_dcb_read_rtrup2tc(struct ixgbe_hw *hw, u8 *map); + /* DCB definitions for credit calculation */ #define DCB_CREDIT_QUANTUM 64 /* DCB Quantum */ #define MAX_CREDIT_REFILL 511 /* 0x1FF * 64B = 32704B */ diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h index a4ef07631d1e..d71d9ce3e394 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_82599.h @@ -45,6 +45,7 @@ /* Receive UP2TC mapping */ #define IXGBE_RTRUP2TC_UP_SHIFT 3 +#define IXGBE_RTRUP2TC_UP_MASK 7 /* Transmit UP2TC mapping */ #define IXGBE_RTTUP2TC_UP_SHIFT 3 diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c index f3d68f9696ba..edd89a1ef27f 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_dcb_nl.c @@ -554,6 +554,9 @@ static int ixgbe_dcbnl_ieee_setets(struct net_device *dev, for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) adapter->ixgbe_ieee_ets->prio_tc[i] = IEEE_8021QAZ_MAX_TCS; + /* if possible update UP2TC mappings from HW */ + ixgbe_dcb_read_rtrup2tc(&adapter->hw, + adapter->ixgbe_ieee_ets->prio_tc); } for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { -- cgit v1.2.3 From fb32b1eda29f2040148b0e172f9cbbd2f07697e4 Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Sat, 9 Feb 2013 11:31:44 +0200 Subject: KVM: x86 emulator: add support for writing back the source operand Some instructions write back the source operand, not just the destination. Add support for doing this via the decode flags. Gleb: add BUG_ON() to prevent source to be memory operand. Signed-off-by: Avi Kivity Signed-off-by: Gleb Natapov --- arch/x86/kvm/emulate.c | 48 +++++++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index 8db0010ed150..a4c266e99e50 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -155,6 +155,7 @@ #define Avx ((u64)1 << 43) /* Advanced Vector Extensions */ #define Fastop ((u64)1 << 44) /* Use opcode::u.fastop */ #define NoWrite ((u64)1 << 45) /* No writeback */ +#define SrcWrite ((u64)1 << 46) /* Write back src operand */ #define X2(x...) x, x #define X3(x...) X2(x), x @@ -1723,45 +1724,42 @@ static void write_register_operand(struct operand *op) } } -static int writeback(struct x86_emulate_ctxt *ctxt) +static int writeback(struct x86_emulate_ctxt *ctxt, struct operand *op) { int rc; - if (ctxt->d & NoWrite) - return X86EMUL_CONTINUE; - - switch (ctxt->dst.type) { + switch (op->type) { case OP_REG: - write_register_operand(&ctxt->dst); + write_register_operand(op); break; case OP_MEM: if (ctxt->lock_prefix) rc = segmented_cmpxchg(ctxt, - ctxt->dst.addr.mem, - &ctxt->dst.orig_val, - &ctxt->dst.val, - ctxt->dst.bytes); + op->addr.mem, + &op->orig_val, + &op->val, + op->bytes); else rc = segmented_write(ctxt, - ctxt->dst.addr.mem, - &ctxt->dst.val, - ctxt->dst.bytes); + op->addr.mem, + &op->val, + op->bytes); if (rc != X86EMUL_CONTINUE) return rc; break; case OP_MEM_STR: rc = segmented_write(ctxt, - ctxt->dst.addr.mem, - ctxt->dst.data, - ctxt->dst.bytes * ctxt->dst.count); + op->addr.mem, + op->data, + op->bytes * op->count); if (rc != X86EMUL_CONTINUE) return rc; break; case OP_XMM: - write_sse_reg(ctxt, &ctxt->dst.vec_val, ctxt->dst.addr.xmm); + write_sse_reg(ctxt, &op->vec_val, op->addr.xmm); break; case OP_MM: - write_mmx_reg(ctxt, &ctxt->dst.mm_val, ctxt->dst.addr.mm); + write_mmx_reg(ctxt, &op->mm_val, op->addr.mm); break; case OP_NONE: /* no writeback */ @@ -4769,9 +4767,17 @@ special_insn: goto done; writeback: - rc = writeback(ctxt); - if (rc != X86EMUL_CONTINUE) - goto done; + if (!(ctxt->d & NoWrite)) { + rc = writeback(ctxt, &ctxt->dst); + if (rc != X86EMUL_CONTINUE) + goto done; + } + if (ctxt->d & SrcWrite) { + BUG_ON(ctxt->src.type == OP_MEM || ctxt->src.type == OP_MEM_STR); + rc = writeback(ctxt, &ctxt->src); + if (rc != X86EMUL_CONTINUE) + goto done; + } /* * restore dst type in case the decoding will be reused -- cgit v1.2.3 From 820207c8fc508be8f104d4d6b19c8f695fe0d5f3 Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Sat, 9 Feb 2013 11:31:45 +0200 Subject: KVM: x86 emulator: decode extended accumulator explicity Single-operand MUL and DIV access an extended accumulator: AX for byte instructions, and DX:AX, EDX:EAX, or RDX:RAX for larger-sized instructions. Add support for fetching the extended accumulator. In order not to change things too much, RDX is loaded into Src2, which is already loaded by fastop(). This avoids increasing register pressure on i386. Gleb: disable src writeback for ByteOp div/mul. Signed-off-by: Avi Kivity Signed-off-by: Gleb Natapov --- arch/x86/kvm/emulate.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index a4c266e99e50..36cb786122fe 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -61,6 +61,8 @@ #define OpMem8 26ull /* 8-bit zero extended memory operand */ #define OpImm64 27ull /* Sign extended 16/32/64-bit immediate */ #define OpXLat 28ull /* memory at BX/EBX/RBX + zero-extended AL */ +#define OpAccLo 29ull /* Low part of extended acc (AX/AX/EAX/RAX) */ +#define OpAccHi 30ull /* High part of extended acc (-/DX/EDX/RDX) */ #define OpBits 5 /* Width of operand field */ #define OpMask ((1ull << OpBits) - 1) @@ -86,6 +88,7 @@ #define DstMem64 (OpMem64 << DstShift) #define DstImmUByte (OpImmUByte << DstShift) #define DstDX (OpDX << DstShift) +#define DstAccLo (OpAccLo << DstShift) #define DstMask (OpMask << DstShift) /* Source operand type. */ #define SrcShift 6 @@ -108,6 +111,7 @@ #define SrcImm64 (OpImm64 << SrcShift) #define SrcDX (OpDX << SrcShift) #define SrcMem8 (OpMem8 << SrcShift) +#define SrcAccHi (OpAccHi << SrcShift) #define SrcMask (OpMask << SrcShift) #define BitOp (1<<11) #define MemAbs (1<<12) /* Memory operand is absolute displacement */ @@ -157,6 +161,8 @@ #define NoWrite ((u64)1 << 45) /* No writeback */ #define SrcWrite ((u64)1 << 46) /* Write back src operand */ +#define DstXacc (DstAccLo | SrcAccHi | SrcWrite) + #define X2(x...) x, x #define X3(x...) X2(x), x #define X4(x...) X2(x), X2(x) @@ -4166,6 +4172,24 @@ static int decode_operand(struct x86_emulate_ctxt *ctxt, struct operand *op, fetch_register_operand(op); op->orig_val = op->val; break; + case OpAccLo: + op->type = OP_REG; + op->bytes = (ctxt->d & ByteOp) ? 2 : ctxt->op_bytes; + op->addr.reg = reg_rmw(ctxt, VCPU_REGS_RAX); + fetch_register_operand(op); + op->orig_val = op->val; + break; + case OpAccHi: + if (ctxt->d & ByteOp) { + op->type = OP_NONE; + break; + } + op->type = OP_REG; + op->bytes = ctxt->op_bytes; + op->addr.reg = reg_rmw(ctxt, VCPU_REGS_RDX); + fetch_register_operand(op); + op->orig_val = op->val; + break; case OpDI: op->type = OP_MEM; op->bytes = (ctxt->d & ByteOp) ? 1 : ctxt->op_bytes; -- cgit v1.2.3 From ab2c5ce66661e07c315a53bef9b507cf766d7905 Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Sat, 9 Feb 2013 11:31:46 +0200 Subject: KVM: x86 emulator: switch MUL/DIV to DstXacc Signed-off-by: Avi Kivity Signed-off-by: Gleb Natapov --- arch/x86/kvm/emulate.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index 36cb786122fe..b9fb89b4ee26 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -142,6 +142,7 @@ /* Source 2 operand type */ #define Src2Shift (31) #define Src2None (OpNone << Src2Shift) +#define Src2Mem (OpMem << Src2Shift) #define Src2CL (OpCL << Src2Shift) #define Src2ImmByte (OpImmByte << Src2Shift) #define Src2One (OpOne << Src2Shift) @@ -548,8 +549,8 @@ FOP_END; #define __emulate_1op_rax_rdx(ctxt, _op, _suffix, _ex) \ do { \ unsigned long _tmp; \ - ulong *rax = reg_rmw((ctxt), VCPU_REGS_RAX); \ - ulong *rdx = reg_rmw((ctxt), VCPU_REGS_RDX); \ + ulong *rax = &ctxt->dst.val; \ + ulong *rdx = &ctxt->src.val; \ \ __asm__ __volatile__ ( \ _PRE_EFLAGS("0", "5", "1") \ @@ -564,7 +565,7 @@ FOP_END; _ASM_EXTABLE(1b, 3b) \ : "=m" ((ctxt)->eflags), "=&r" (_tmp), \ "+a" (*rax), "+d" (*rdx), "+qm"(_ex) \ - : "i" (EFLAGS_MASK), "m" ((ctxt)->src.val)); \ + : "i" (EFLAGS_MASK), "m" ((ctxt)->src2.val)); \ } while (0) /* instruction has only one source operand, destination is implicit (e.g. mul, div, imul, idiv) */ @@ -3735,10 +3736,10 @@ static const struct opcode group3[] = { F(DstMem | SrcImm | NoWrite, em_test), F(DstMem | SrcNone | Lock, em_not), F(DstMem | SrcNone | Lock, em_neg), - I(SrcMem, em_mul_ex), - I(SrcMem, em_imul_ex), - I(SrcMem, em_div_ex), - I(SrcMem, em_idiv_ex), + I(DstXacc | Src2Mem, em_mul_ex), + I(DstXacc | Src2Mem, em_imul_ex), + I(DstXacc | Src2Mem, em_div_ex), + I(DstXacc | Src2Mem, em_idiv_ex), }; static const struct opcode group4[] = { -- cgit v1.2.3 From 017da7b604cf1982c35162c29895fba842282e11 Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Sat, 9 Feb 2013 11:31:47 +0200 Subject: KVM: x86 emulator: Switch fastop src operand to RDX This makes OpAccHi useful. Signed-off-by: Avi Kivity Signed-off-by: Gleb Natapov --- arch/x86/kvm/emulate.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index b9fb89b4ee26..b899d2418c19 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -179,8 +179,8 @@ /* * fastop functions have a special calling convention: * - * dst: [rdx]:rax (in/out) - * src: rbx (in/out) + * dst: rax (in/out) + * src: rdx (in/out) * src2: rcx (in) * flags: rflags (in/out) * @@ -485,19 +485,19 @@ static int fastop(struct x86_emulate_ctxt *ctxt, void (*fop)(struct fastop *)); #define FASTOP2(op) \ FOP_START(op) \ - FOP2E(op##b, al, bl) \ - FOP2E(op##w, ax, bx) \ - FOP2E(op##l, eax, ebx) \ - ON64(FOP2E(op##q, rax, rbx)) \ + FOP2E(op##b, al, dl) \ + FOP2E(op##w, ax, dx) \ + FOP2E(op##l, eax, edx) \ + ON64(FOP2E(op##q, rax, rdx)) \ FOP_END /* 2 operand, word only */ #define FASTOP2W(op) \ FOP_START(op) \ FOPNOP() \ - FOP2E(op##w, ax, bx) \ - FOP2E(op##l, eax, ebx) \ - ON64(FOP2E(op##q, rax, rbx)) \ + FOP2E(op##w, ax, dx) \ + FOP2E(op##l, eax, edx) \ + ON64(FOP2E(op##q, rax, rdx)) \ FOP_END /* 2 operand, src is CL */ @@ -516,9 +516,9 @@ static int fastop(struct x86_emulate_ctxt *ctxt, void (*fop)(struct fastop *)); #define FASTOP3WCL(op) \ FOP_START(op) \ FOPNOP() \ - FOP3E(op##w, ax, bx, cl) \ - FOP3E(op##l, eax, ebx, cl) \ - ON64(FOP3E(op##q, rax, rbx, cl)) \ + FOP3E(op##w, ax, dx, cl) \ + FOP3E(op##l, eax, edx, cl) \ + ON64(FOP3E(op##q, rax, rdx, cl)) \ FOP_END /* Special case for SETcc - 1 instruction per cc */ @@ -4574,7 +4574,7 @@ static int fastop(struct x86_emulate_ctxt *ctxt, void (*fop)(struct fastop *)) ulong flags = (ctxt->eflags & EFLAGS_MASK) | X86_EFLAGS_IF; fop += __ffs(ctxt->dst.bytes) * FASTOP_SIZE; asm("push %[flags]; popf; call *%[fastop]; pushf; pop %[flags]\n" - : "+a"(ctxt->dst.val), "+b"(ctxt->src.val), [flags]"+D"(flags) + : "+a"(ctxt->dst.val), "+d"(ctxt->src.val), [flags]"+D"(flags) : "c"(ctxt->src2.val), [fastop]"S"(fop)); ctxt->eflags = (ctxt->eflags & ~EFLAGS_MASK) | (flags & EFLAGS_MASK); return X86EMUL_CONTINUE; -- cgit v1.2.3 From b9fa409b00a1ed2a372758fd09f3f5df70f8d59a Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Sat, 9 Feb 2013 11:31:48 +0200 Subject: KVM: x86 emulator: convert single-operand MUL/IMUL to fastop Signed-off-by: Avi Kivity Signed-off-by: Gleb Natapov --- arch/x86/kvm/emulate.c | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index b899d2418c19..3a3542a88289 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -480,6 +480,15 @@ static int fastop(struct x86_emulate_ctxt *ctxt, void (*fop)(struct fastop *)); ON64(FOP1E(op##q, rax)) \ FOP_END +/* 1-operand, using src2 (for MUL/DIV r/m) */ +#define FASTOP1SRC2(op, name) \ + FOP_START(name) \ + FOP1E(op, cl) \ + FOP1E(op, cx) \ + FOP1E(op, ecx) \ + ON64(FOP1E(op, rcx)) \ + FOP_END + #define FOP2E(op, dst, src) \ FOP_ALIGN #op " %" #src ", %" #dst " \n\t" FOP_RET @@ -996,6 +1005,9 @@ FASTOP2(xor); FASTOP2(cmp); FASTOP2(test); +FASTOP1SRC2(mul, mul_ex); +FASTOP1SRC2(imul, imul_ex); + FASTOP3WCL(shld); FASTOP3WCL(shrd); @@ -2119,22 +2131,6 @@ static int em_jmp_far(struct x86_emulate_ctxt *ctxt) return X86EMUL_CONTINUE; } -static int em_mul_ex(struct x86_emulate_ctxt *ctxt) -{ - u8 ex = 0; - - emulate_1op_rax_rdx(ctxt, "mul", ex); - return X86EMUL_CONTINUE; -} - -static int em_imul_ex(struct x86_emulate_ctxt *ctxt) -{ - u8 ex = 0; - - emulate_1op_rax_rdx(ctxt, "imul", ex); - return X86EMUL_CONTINUE; -} - static int em_div_ex(struct x86_emulate_ctxt *ctxt) { u8 de = 0; @@ -3736,8 +3732,8 @@ static const struct opcode group3[] = { F(DstMem | SrcImm | NoWrite, em_test), F(DstMem | SrcNone | Lock, em_not), F(DstMem | SrcNone | Lock, em_neg), - I(DstXacc | Src2Mem, em_mul_ex), - I(DstXacc | Src2Mem, em_imul_ex), + F(DstXacc | Src2Mem, em_mul_ex), + F(DstXacc | Src2Mem, em_imul_ex), I(DstXacc | Src2Mem, em_div_ex), I(DstXacc | Src2Mem, em_idiv_ex), }; @@ -4572,7 +4568,8 @@ static void fetch_possible_mmx_operand(struct x86_emulate_ctxt *ctxt, static int fastop(struct x86_emulate_ctxt *ctxt, void (*fop)(struct fastop *)) { ulong flags = (ctxt->eflags & EFLAGS_MASK) | X86_EFLAGS_IF; - fop += __ffs(ctxt->dst.bytes) * FASTOP_SIZE; + if (!(ctxt->d & ByteOp)) + fop += __ffs(ctxt->dst.bytes) * FASTOP_SIZE; asm("push %[flags]; popf; call *%[fastop]; pushf; pop %[flags]\n" : "+a"(ctxt->dst.val), "+d"(ctxt->src.val), [flags]"+D"(flags) : "c"(ctxt->src2.val), [fastop]"S"(fop)); -- cgit v1.2.3 From b8c0b6ae498fe5c3f29966bd2a2d9882911b887b Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Sat, 9 Feb 2013 11:31:49 +0200 Subject: KVM: x86 emulator: convert DIV/IDIV to fastop Since DIV and IDIV can generate exceptions, we need an additional output parameter indicating whether an execption has occured. To avoid increasing register pressure on i386, we use %rsi, which is already allocated for the fastop code pointer. Gleb: added comment about fop usage as exception indication. Signed-off-by: Avi Kivity Signed-off-by: Gleb Natapov --- arch/x86/kvm/emulate.c | 51 +++++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index 3a3542a88289..8404dc350988 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -183,6 +183,7 @@ * src: rdx (in/out) * src2: rcx (in) * flags: rflags (in/out) + * ex: rsi (in:fastop pointer, out:zero if exception) * * Moreover, they are all exactly FASTOP_SIZE bytes long, so functions for * different operand sizes can be reached by calculation, rather than a jump @@ -470,7 +471,10 @@ static int fastop(struct x86_emulate_ctxt *ctxt, void (*fop)(struct fastop *)); #define FOPNOP() FOP_ALIGN FOP_RET #define FOP1E(op, dst) \ - FOP_ALIGN #op " %" #dst " \n\t" FOP_RET + FOP_ALIGN "10: " #op " %" #dst " \n\t" FOP_RET + +#define FOP1EEX(op, dst) \ + FOP1E(op, dst) _ASM_EXTABLE(10b, kvm_fastop_exception) #define FASTOP1(op) \ FOP_START(op) \ @@ -489,6 +493,15 @@ static int fastop(struct x86_emulate_ctxt *ctxt, void (*fop)(struct fastop *)); ON64(FOP1E(op, rcx)) \ FOP_END +/* 1-operand, using src2 (for MUL/DIV r/m), with exceptions */ +#define FASTOP1SRC2EX(op, name) \ + FOP_START(name) \ + FOP1EEX(op, cl) \ + FOP1EEX(op, cx) \ + FOP1EEX(op, ecx) \ + ON64(FOP1EEX(op, rcx)) \ + FOP_END + #define FOP2E(op, dst, src) \ FOP_ALIGN #op " %" #src ", %" #dst " \n\t" FOP_RET @@ -533,6 +546,9 @@ static int fastop(struct x86_emulate_ctxt *ctxt, void (*fop)(struct fastop *)); /* Special case for SETcc - 1 instruction per cc */ #define FOP_SETCC(op) ".align 4; " #op " %al; ret \n\t" +asm(".global kvm_fastop_exception \n" + "kvm_fastop_exception: xor %esi, %esi; ret"); + FOP_START(setcc) FOP_SETCC(seto) FOP_SETCC(setno) @@ -1007,6 +1023,8 @@ FASTOP2(test); FASTOP1SRC2(mul, mul_ex); FASTOP1SRC2(imul, imul_ex); +FASTOP1SRC2EX(div, div_ex); +FASTOP1SRC2EX(idiv, idiv_ex); FASTOP3WCL(shld); FASTOP3WCL(shrd); @@ -2131,26 +2149,6 @@ static int em_jmp_far(struct x86_emulate_ctxt *ctxt) return X86EMUL_CONTINUE; } -static int em_div_ex(struct x86_emulate_ctxt *ctxt) -{ - u8 de = 0; - - emulate_1op_rax_rdx(ctxt, "div", de); - if (de) - return emulate_de(ctxt); - return X86EMUL_CONTINUE; -} - -static int em_idiv_ex(struct x86_emulate_ctxt *ctxt) -{ - u8 de = 0; - - emulate_1op_rax_rdx(ctxt, "idiv", de); - if (de) - return emulate_de(ctxt); - return X86EMUL_CONTINUE; -} - static int em_grp45(struct x86_emulate_ctxt *ctxt) { int rc = X86EMUL_CONTINUE; @@ -3734,8 +3732,8 @@ static const struct opcode group3[] = { F(DstMem | SrcNone | Lock, em_neg), F(DstXacc | Src2Mem, em_mul_ex), F(DstXacc | Src2Mem, em_imul_ex), - I(DstXacc | Src2Mem, em_div_ex), - I(DstXacc | Src2Mem, em_idiv_ex), + F(DstXacc | Src2Mem, em_div_ex), + F(DstXacc | Src2Mem, em_idiv_ex), }; static const struct opcode group4[] = { @@ -4571,9 +4569,12 @@ static int fastop(struct x86_emulate_ctxt *ctxt, void (*fop)(struct fastop *)) if (!(ctxt->d & ByteOp)) fop += __ffs(ctxt->dst.bytes) * FASTOP_SIZE; asm("push %[flags]; popf; call *%[fastop]; pushf; pop %[flags]\n" - : "+a"(ctxt->dst.val), "+d"(ctxt->src.val), [flags]"+D"(flags) - : "c"(ctxt->src2.val), [fastop]"S"(fop)); + : "+a"(ctxt->dst.val), "+d"(ctxt->src.val), [flags]"+D"(flags), + [fastop]"+S"(fop) + : "c"(ctxt->src2.val)); ctxt->eflags = (ctxt->eflags & ~EFLAGS_MASK) | (flags & EFLAGS_MASK); + if (!fop) /* exception is returned in fop variable */ + return emulate_de(ctxt); return X86EMUL_CONTINUE; } -- cgit v1.2.3 From 203831e8e4bd9ab3b2f9f86c73c74fe912e06ac5 Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Sat, 9 Feb 2013 11:31:50 +0200 Subject: KVM: x86 emulator: drop unused old-style inline emulation Signed-off-by: Avi Kivity Signed-off-by: Gleb Natapov --- arch/x86/kvm/emulate.c | 198 ------------------------------------------------- 1 file changed, 198 deletions(-) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index 8404dc350988..d71aac0bf32f 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -284,175 +284,18 @@ static void invalidate_registers(struct x86_emulate_ctxt *ctxt) ctxt->regs_valid = 0; } -/* - * Instruction emulation: - * Most instructions are emulated directly via a fragment of inline assembly - * code. This allows us to save/restore EFLAGS and thus very easily pick up - * any modified flags. - */ - -#if defined(CONFIG_X86_64) -#define _LO32 "k" /* force 32-bit operand */ -#define _STK "%%rsp" /* stack pointer */ -#elif defined(__i386__) -#define _LO32 "" /* force 32-bit operand */ -#define _STK "%%esp" /* stack pointer */ -#endif - /* * These EFLAGS bits are restored from saved value during emulation, and * any changes are written back to the saved value after emulation. */ #define EFLAGS_MASK (EFLG_OF|EFLG_SF|EFLG_ZF|EFLG_AF|EFLG_PF|EFLG_CF) -/* Before executing instruction: restore necessary bits in EFLAGS. */ -#define _PRE_EFLAGS(_sav, _msk, _tmp) \ - /* EFLAGS = (_sav & _msk) | (EFLAGS & ~_msk); _sav &= ~_msk; */ \ - "movl %"_sav",%"_LO32 _tmp"; " \ - "push %"_tmp"; " \ - "push %"_tmp"; " \ - "movl %"_msk",%"_LO32 _tmp"; " \ - "andl %"_LO32 _tmp",("_STK"); " \ - "pushf; " \ - "notl %"_LO32 _tmp"; " \ - "andl %"_LO32 _tmp",("_STK"); " \ - "andl %"_LO32 _tmp","__stringify(BITS_PER_LONG/4)"("_STK"); " \ - "pop %"_tmp"; " \ - "orl %"_LO32 _tmp",("_STK"); " \ - "popf; " \ - "pop %"_sav"; " - -/* After executing instruction: write-back necessary bits in EFLAGS. */ -#define _POST_EFLAGS(_sav, _msk, _tmp) \ - /* _sav |= EFLAGS & _msk; */ \ - "pushf; " \ - "pop %"_tmp"; " \ - "andl %"_msk",%"_LO32 _tmp"; " \ - "orl %"_LO32 _tmp",%"_sav"; " - #ifdef CONFIG_X86_64 #define ON64(x) x #else #define ON64(x) #endif -#define ____emulate_2op(ctxt, _op, _x, _y, _suffix, _dsttype) \ - do { \ - __asm__ __volatile__ ( \ - _PRE_EFLAGS("0", "4", "2") \ - _op _suffix " %"_x"3,%1; " \ - _POST_EFLAGS("0", "4", "2") \ - : "=m" ((ctxt)->eflags), \ - "+q" (*(_dsttype*)&(ctxt)->dst.val), \ - "=&r" (_tmp) \ - : _y ((ctxt)->src.val), "i" (EFLAGS_MASK)); \ - } while (0) - - -/* Raw emulation: instruction has two explicit operands. */ -#define __emulate_2op_nobyte(ctxt,_op,_wx,_wy,_lx,_ly,_qx,_qy) \ - do { \ - unsigned long _tmp; \ - \ - switch ((ctxt)->dst.bytes) { \ - case 2: \ - ____emulate_2op(ctxt,_op,_wx,_wy,"w",u16); \ - break; \ - case 4: \ - ____emulate_2op(ctxt,_op,_lx,_ly,"l",u32); \ - break; \ - case 8: \ - ON64(____emulate_2op(ctxt,_op,_qx,_qy,"q",u64)); \ - break; \ - } \ - } while (0) - -#define __emulate_2op(ctxt,_op,_bx,_by,_wx,_wy,_lx,_ly,_qx,_qy) \ - do { \ - unsigned long _tmp; \ - switch ((ctxt)->dst.bytes) { \ - case 1: \ - ____emulate_2op(ctxt,_op,_bx,_by,"b",u8); \ - break; \ - default: \ - __emulate_2op_nobyte(ctxt, _op, \ - _wx, _wy, _lx, _ly, _qx, _qy); \ - break; \ - } \ - } while (0) - -/* Source operand is byte-sized and may be restricted to just %cl. */ -#define emulate_2op_SrcB(ctxt, _op) \ - __emulate_2op(ctxt, _op, "b", "c", "b", "c", "b", "c", "b", "c") - -/* Source operand is byte, word, long or quad sized. */ -#define emulate_2op_SrcV(ctxt, _op) \ - __emulate_2op(ctxt, _op, "b", "q", "w", "r", _LO32, "r", "", "r") - -/* Source operand is word, long or quad sized. */ -#define emulate_2op_SrcV_nobyte(ctxt, _op) \ - __emulate_2op_nobyte(ctxt, _op, "w", "r", _LO32, "r", "", "r") - -/* Instruction has three operands and one operand is stored in ECX register */ -#define __emulate_2op_cl(ctxt, _op, _suffix, _type) \ - do { \ - unsigned long _tmp; \ - _type _clv = (ctxt)->src2.val; \ - _type _srcv = (ctxt)->src.val; \ - _type _dstv = (ctxt)->dst.val; \ - \ - __asm__ __volatile__ ( \ - _PRE_EFLAGS("0", "5", "2") \ - _op _suffix " %4,%1 \n" \ - _POST_EFLAGS("0", "5", "2") \ - : "=m" ((ctxt)->eflags), "+r" (_dstv), "=&r" (_tmp) \ - : "c" (_clv) , "r" (_srcv), "i" (EFLAGS_MASK) \ - ); \ - \ - (ctxt)->src2.val = (unsigned long) _clv; \ - (ctxt)->src2.val = (unsigned long) _srcv; \ - (ctxt)->dst.val = (unsigned long) _dstv; \ - } while (0) - -#define emulate_2op_cl(ctxt, _op) \ - do { \ - switch ((ctxt)->dst.bytes) { \ - case 2: \ - __emulate_2op_cl(ctxt, _op, "w", u16); \ - break; \ - case 4: \ - __emulate_2op_cl(ctxt, _op, "l", u32); \ - break; \ - case 8: \ - ON64(__emulate_2op_cl(ctxt, _op, "q", ulong)); \ - break; \ - } \ - } while (0) - -#define __emulate_1op(ctxt, _op, _suffix) \ - do { \ - unsigned long _tmp; \ - \ - __asm__ __volatile__ ( \ - _PRE_EFLAGS("0", "3", "2") \ - _op _suffix " %1; " \ - _POST_EFLAGS("0", "3", "2") \ - : "=m" ((ctxt)->eflags), "+m" ((ctxt)->dst.val), \ - "=&r" (_tmp) \ - : "i" (EFLAGS_MASK)); \ - } while (0) - -/* Instruction has only one explicit operand (no source operand). */ -#define emulate_1op(ctxt, _op) \ - do { \ - switch ((ctxt)->dst.bytes) { \ - case 1: __emulate_1op(ctxt, _op, "b"); break; \ - case 2: __emulate_1op(ctxt, _op, "w"); break; \ - case 4: __emulate_1op(ctxt, _op, "l"); break; \ - case 8: ON64(__emulate_1op(ctxt, _op, "q")); break; \ - } \ - } while (0) - static int fastop(struct x86_emulate_ctxt *ctxt, void (*fop)(struct fastop *)); #define FOP_ALIGN ".align " __stringify(FASTOP_SIZE) " \n\t" @@ -571,47 +414,6 @@ FOP_END; FOP_START(salc) "pushf; sbb %al, %al; popf \n\t" FOP_RET FOP_END; -#define __emulate_1op_rax_rdx(ctxt, _op, _suffix, _ex) \ - do { \ - unsigned long _tmp; \ - ulong *rax = &ctxt->dst.val; \ - ulong *rdx = &ctxt->src.val; \ - \ - __asm__ __volatile__ ( \ - _PRE_EFLAGS("0", "5", "1") \ - "1: \n\t" \ - _op _suffix " %6; " \ - "2: \n\t" \ - _POST_EFLAGS("0", "5", "1") \ - ".pushsection .fixup,\"ax\" \n\t" \ - "3: movb $1, %4 \n\t" \ - "jmp 2b \n\t" \ - ".popsection \n\t" \ - _ASM_EXTABLE(1b, 3b) \ - : "=m" ((ctxt)->eflags), "=&r" (_tmp), \ - "+a" (*rax), "+d" (*rdx), "+qm"(_ex) \ - : "i" (EFLAGS_MASK), "m" ((ctxt)->src2.val)); \ - } while (0) - -/* instruction has only one source operand, destination is implicit (e.g. mul, div, imul, idiv) */ -#define emulate_1op_rax_rdx(ctxt, _op, _ex) \ - do { \ - switch((ctxt)->src.bytes) { \ - case 1: \ - __emulate_1op_rax_rdx(ctxt, _op, "b", _ex); \ - break; \ - case 2: \ - __emulate_1op_rax_rdx(ctxt, _op, "w", _ex); \ - break; \ - case 4: \ - __emulate_1op_rax_rdx(ctxt, _op, "l", _ex); \ - break; \ - case 8: ON64( \ - __emulate_1op_rax_rdx(ctxt, _op, "q", _ex)); \ - break; \ - } \ - } while (0) - static int emulator_check_intercept(struct x86_emulate_ctxt *ctxt, enum x86_intercept intercept, enum x86_intercept_stage stage) -- cgit v1.2.3 From e47a5f5fb715b90b40747e9e235de557c6abd56c Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Sat, 9 Feb 2013 11:31:51 +0200 Subject: KVM: x86 emulator: convert XADD to fastop Signed-off-by: Avi Kivity Signed-off-by: Gleb Natapov --- arch/x86/kvm/emulate.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index d71aac0bf32f..9ebe95c5836e 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -853,6 +853,8 @@ FASTOP2W(bts); FASTOP2W(btr); FASTOP2W(btc); +FASTOP2(xadd); + static u8 test_cc(unsigned int condition, unsigned long flags) { u8 rc; @@ -3861,7 +3863,7 @@ static const struct opcode twobyte_table[256] = { F(DstReg | SrcMem | ModRM, em_bsf), F(DstReg | SrcMem | ModRM, em_bsr), D(DstReg | SrcMem8 | ModRM | Mov), D(DstReg | SrcMem16 | ModRM | Mov), /* 0xC0 - 0xC7 */ - D2bv(DstMem | SrcReg | ModRM | Lock), + F2bv(DstMem | SrcReg | ModRM | SrcWrite | Lock, em_xadd), N, D(DstMem | SrcReg | ModRM | Mov), N, N, N, GD(0, &group9), /* 0xC8 - 0xCF */ @@ -4698,12 +4700,6 @@ twobyte_insn: ctxt->dst.val = (ctxt->src.bytes == 1) ? (s8) ctxt->src.val : (s16) ctxt->src.val; break; - case 0xc0 ... 0xc1: /* xadd */ - fastop(ctxt, em_add); - /* Write back the register source. */ - ctxt->src.val = ctxt->dst.orig_val; - write_register_operand(&ctxt->src); - break; case 0xc3: /* movnti */ ctxt->dst.bytes = ctxt->op_bytes; ctxt->dst.val = (ctxt->op_bytes == 4) ? (u32) ctxt->src.val : -- cgit v1.2.3 From b6040f9706c4c81cc50b50855ed70840f022bebb Mon Sep 17 00:00:00 2001 From: chaoting fan Date: Thu, 28 Mar 2013 22:19:45 +0800 Subject: sunrpc: the cache_detail in cache_is_valid is unused any more The cache_detail(*detail) in function cache_is_valid is not used any more. Signed-off-by: fanchaoting Signed-off-by: J. Bruce Fields --- net/sunrpc/cache.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index 80fe5c86efd1..3b3f14fc02c5 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -201,7 +201,7 @@ static int cache_make_upcall(struct cache_detail *cd, struct cache_head *h) return sunrpc_cache_pipe_upcall(cd, h); } -static inline int cache_is_valid(struct cache_detail *detail, struct cache_head *h) +static inline int cache_is_valid(struct cache_head *h) { if (!test_bit(CACHE_VALID, &h->flags)) return -EAGAIN; @@ -227,7 +227,7 @@ static int try_to_negate_entry(struct cache_detail *detail, struct cache_head *h int rv; write_lock(&detail->hash_lock); - rv = cache_is_valid(detail, h); + rv = cache_is_valid(h); if (rv != -EAGAIN) { write_unlock(&detail->hash_lock); return rv; @@ -260,7 +260,7 @@ int cache_check(struct cache_detail *detail, long refresh_age, age; /* First decide return status as best we can */ - rv = cache_is_valid(detail, h); + rv = cache_is_valid(h); /* now see if we want to start an upcall */ refresh_age = (h->expiry_time - h->last_refresh); @@ -293,7 +293,7 @@ int cache_check(struct cache_detail *detail, * Request was not deferred; handle it as best * we can ourselves: */ - rv = cache_is_valid(detail, h); + rv = cache_is_valid(h); if (rv == -EAGAIN) rv = -ETIMEDOUT; } -- cgit v1.2.3 From 1a9357f443d64aa41e9b0dc414953663a6fcca19 Mon Sep 17 00:00:00 2001 From: Jim Rees Date: Fri, 17 May 2013 17:33:00 -0400 Subject: nfsd: avoid undefined signed overflow In C, signed integer overflow results in undefined behavior, but unsigned overflow wraps around. So do the subtraction first, then cast to signed. Reported-by: Joakim Tjernlund Signed-off-by: Jim Rees Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 91ead0ed9f11..72f0c4e9a942 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -3427,7 +3427,7 @@ grace_disallows_io(struct net *net, struct inode *inode) /* Returns true iff a is later than b: */ static bool stateid_generation_after(stateid_t *a, stateid_t *b) { - return (s32)a->si_generation - (s32)b->si_generation > 0; + return (s32)(a->si_generation - b->si_generation) > 0; } static __be32 check_stateid_generation(stateid_t *in, stateid_t *ref, bool has_session) -- cgit v1.2.3 From b296263398f08d21e68d5d7b2afc43228c208b71 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Tue, 21 May 2013 12:04:08 +0200 Subject: ASoC: ab8500-codec: Set tx dai slots from tx_mask Replace hard-coded tx slot numbers from ab8500_codec_set_dai_tdm_slot using the ones requested by the machine driver in tx_mask instead. Signed-off-by: Fabio Baltieri Signed-off-by: Mark Brown --- sound/soc/codecs/ab8500-codec.c | 31 +++++++++++++++++++------------ sound/soc/codecs/ab8500-codec.h | 7 +++++++ 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c index 3126cac7b7c8..bace321a83dd 100644 --- a/sound/soc/codecs/ab8500-codec.c +++ b/sound/soc/codecs/ab8500-codec.c @@ -2236,7 +2236,7 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai, int slots, int slot_width) { struct snd_soc_codec *codec = dai->codec; - unsigned int val, mask, slots_active; + unsigned int val, mask, slot, slots_active; mask = BIT(AB8500_DIGIFCONF2_IF0WL0) | BIT(AB8500_DIGIFCONF2_IF0WL1); @@ -2292,27 +2292,34 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai, snd_soc_update_bits(codec, AB8500_DIGIFCONF1, mask, val); /* Setup TDM DA according to active tx slots */ + + if (tx_mask & ~0xff) + return -EINVAL; + mask = AB8500_DASLOTCONFX_SLTODAX_MASK; + tx_mask = tx_mask << AB8500_DA_DATA0_OFFSET; slots_active = hweight32(tx_mask); + dev_dbg(dai->codec->dev, "%s: Slots, active, TX: %d\n", __func__, slots_active); + switch (slots_active) { case 0: break; case 1: - /* Slot 9 -> DA_IN1 & DA_IN3 */ - snd_soc_update_bits(codec, AB8500_DASLOTCONF1, mask, 11); - snd_soc_update_bits(codec, AB8500_DASLOTCONF3, mask, 11); - snd_soc_update_bits(codec, AB8500_DASLOTCONF2, mask, 11); - snd_soc_update_bits(codec, AB8500_DASLOTCONF4, mask, 11); + slot = find_first_bit((unsigned long *)&tx_mask, 32); + snd_soc_update_bits(codec, AB8500_DASLOTCONF1, mask, slot); + snd_soc_update_bits(codec, AB8500_DASLOTCONF3, mask, slot); + snd_soc_update_bits(codec, AB8500_DASLOTCONF2, mask, slot); + snd_soc_update_bits(codec, AB8500_DASLOTCONF4, mask, slot); break; case 2: - /* Slot 9 -> DA_IN1 & DA_IN3, Slot 11 -> DA_IN2 & DA_IN4 */ - snd_soc_update_bits(codec, AB8500_DASLOTCONF1, mask, 9); - snd_soc_update_bits(codec, AB8500_DASLOTCONF3, mask, 9); - snd_soc_update_bits(codec, AB8500_DASLOTCONF2, mask, 11); - snd_soc_update_bits(codec, AB8500_DASLOTCONF4, mask, 11); - + slot = find_first_bit((unsigned long *)&tx_mask, 32); + snd_soc_update_bits(codec, AB8500_DASLOTCONF1, mask, slot); + snd_soc_update_bits(codec, AB8500_DASLOTCONF3, mask, slot); + slot = find_next_bit((unsigned long *)&tx_mask, 32, slot + 1); + snd_soc_update_bits(codec, AB8500_DASLOTCONF2, mask, slot); + snd_soc_update_bits(codec, AB8500_DASLOTCONF4, mask, slot); break; case 8: dev_dbg(dai->codec->dev, diff --git a/sound/soc/codecs/ab8500-codec.h b/sound/soc/codecs/ab8500-codec.h index 114f69a0c629..4224b525cb2f 100644 --- a/sound/soc/codecs/ab8500-codec.h +++ b/sound/soc/codecs/ab8500-codec.h @@ -24,6 +24,13 @@ #define AB8500_SUPPORTED_RATE (SNDRV_PCM_RATE_48000) #define AB8500_SUPPORTED_FMT (SNDRV_PCM_FMTBIT_S16_LE) +/* AB8500 interface slot offset definitions */ + +#define AB8500_AD_DATA0_OFFSET 0 +#define AB8500_DA_DATA0_OFFSET 8 +#define AB8500_AD_DATA1_OFFSET 16 +#define AB8500_DA_DATA1_OFFSET 24 + /* AB8500 audio bank (0x0d) register definitions */ #define AB8500_POWERUP 0x00 -- cgit v1.2.3 From da33d723bcb3569400685b4e6e75a9894e2f42a7 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Tue, 21 May 2013 12:04:09 +0200 Subject: ASoC: ab8500-codec: Set rx dai slots from rx_mask Replace hard coded rx slot numbers from ab8500_codec_set_dai_tdm_slot using the ones requested by the machine driver in rx_mask instead. Signed-off-by: Fabio Baltieri Signed-off-by: Mark Brown --- sound/soc/codecs/ab8500-codec.c | 29 ++++++++++++++++++++--------- sound/soc/codecs/ab8500-codec.h | 35 +++++++++++++++-------------------- 2 files changed, 35 insertions(+), 29 deletions(-) diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c index bace321a83dd..4ca45b9d9625 100644 --- a/sound/soc/codecs/ab8500-codec.c +++ b/sound/soc/codecs/ab8500-codec.c @@ -2334,25 +2334,36 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai, } /* Setup TDM AD according to active RX-slots */ + + if (rx_mask & ~0xff) + return -EINVAL; + + rx_mask = rx_mask << AB8500_AD_DATA0_OFFSET; slots_active = hweight32(rx_mask); + dev_dbg(dai->codec->dev, "%s: Slots, active, RX: %d\n", __func__, slots_active); + switch (slots_active) { case 0: break; case 1: - /* AD_OUT3 -> slot 0 & 1 */ - snd_soc_update_bits(codec, AB8500_ADSLOTSEL1, AB8500_MASK_ALL, - AB8500_ADSLOTSELX_AD_OUT3_TO_SLOT_EVEN | - AB8500_ADSLOTSELX_AD_OUT3_TO_SLOT_ODD); + slot = find_first_bit((unsigned long *)&rx_mask, 32); + snd_soc_update_bits(codec, AB8500_ADSLOTSEL(slot), + AB8500_MASK_SLOT(slot), + AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(AB8500_AD_OUT3, slot)); break; case 2: - /* AD_OUT3 -> slot 0, AD_OUT2 -> slot 1 */ + slot = find_first_bit((unsigned long *)&rx_mask, 32); + snd_soc_update_bits(codec, + AB8500_ADSLOTSEL(slot), + AB8500_MASK_SLOT(slot), + AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(AB8500_AD_OUT3, slot)); + slot = find_next_bit((unsigned long *)&rx_mask, 32, slot + 1); snd_soc_update_bits(codec, - AB8500_ADSLOTSEL1, - AB8500_MASK_ALL, - AB8500_ADSLOTSELX_AD_OUT3_TO_SLOT_EVEN | - AB8500_ADSLOTSELX_AD_OUT2_TO_SLOT_ODD); + AB8500_ADSLOTSEL(slot), + AB8500_MASK_SLOT(slot), + AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(AB8500_AD_OUT2, slot)); break; case 8: dev_dbg(dai->codec->dev, diff --git a/sound/soc/codecs/ab8500-codec.h b/sound/soc/codecs/ab8500-codec.h index 64c14ce41f69..e2e54425d25e 100644 --- a/sound/soc/codecs/ab8500-codec.h +++ b/sound/soc/codecs/ab8500-codec.h @@ -80,6 +80,7 @@ #define AB8500_ADSLOTSEL14 0x2C #define AB8500_ADSLOTSEL15 0x2D #define AB8500_ADSLOTSEL16 0x2E +#define AB8500_ADSLOTSEL(slot) (AB8500_ADSLOTSEL1 + (slot >> 1)) #define AB8500_ADSLOTHIZCTRL1 0x2F #define AB8500_ADSLOTHIZCTRL2 0x30 #define AB8500_ADSLOTHIZCTRL3 0x31 @@ -151,6 +152,7 @@ #define AB8500_CACHEREGNUM (AB8500_LAST_REG + 1) #define AB8500_MASK_ALL 0xFF +#define AB8500_MASK_SLOT(slot) ((slot & 1) ? 0xF0 : 0x0F) #define AB8500_MASK_NONE 0x00 /* AB8500_POWERUP */ @@ -354,28 +356,21 @@ #define AB8500_DIGIFCONF4_IF1WL0 0 /* AB8500_ADSLOTSELX */ -#define AB8500_ADSLOTSELX_AD_OUT1_TO_SLOT_ODD 0x00 -#define AB8500_ADSLOTSELX_AD_OUT2_TO_SLOT_ODD 0x10 -#define AB8500_ADSLOTSELX_AD_OUT3_TO_SLOT_ODD 0x20 -#define AB8500_ADSLOTSELX_AD_OUT4_TO_SLOT_ODD 0x30 -#define AB8500_ADSLOTSELX_AD_OUT5_TO_SLOT_ODD 0x40 -#define AB8500_ADSLOTSELX_AD_OUT6_TO_SLOT_ODD 0x50 -#define AB8500_ADSLOTSELX_AD_OUT7_TO_SLOT_ODD 0x60 -#define AB8500_ADSLOTSELX_AD_OUT8_TO_SLOT_ODD 0x70 -#define AB8500_ADSLOTSELX_ZEROES_TO_SLOT_ODD 0x80 -#define AB8500_ADSLOTSELX_TRISTATE_TO_SLOT_ODD 0xF0 -#define AB8500_ADSLOTSELX_AD_OUT1_TO_SLOT_EVEN 0x00 -#define AB8500_ADSLOTSELX_AD_OUT2_TO_SLOT_EVEN 0x01 -#define AB8500_ADSLOTSELX_AD_OUT3_TO_SLOT_EVEN 0x02 -#define AB8500_ADSLOTSELX_AD_OUT4_TO_SLOT_EVEN 0x03 -#define AB8500_ADSLOTSELX_AD_OUT5_TO_SLOT_EVEN 0x04 -#define AB8500_ADSLOTSELX_AD_OUT6_TO_SLOT_EVEN 0x05 -#define AB8500_ADSLOTSELX_AD_OUT7_TO_SLOT_EVEN 0x06 -#define AB8500_ADSLOTSELX_AD_OUT8_TO_SLOT_EVEN 0x07 -#define AB8500_ADSLOTSELX_ZEROES_TO_SLOT_EVEN 0x08 -#define AB8500_ADSLOTSELX_TRISTATE_TO_SLOT_EVEN 0x0F +#define AB8500_AD_OUT1 0x0 +#define AB8500_AD_OUT2 0x1 +#define AB8500_AD_OUT3 0x2 +#define AB8500_AD_OUT4 0x3 +#define AB8500_AD_OUT5 0x4 +#define AB8500_AD_OUT6 0x5 +#define AB8500_AD_OUT7 0x6 +#define AB8500_AD_OUT8 0x7 +#define AB8500_ZEROES 0x8 +#define AB8500_TRISTATE 0xF #define AB8500_ADSLOTSELX_EVEN_SHIFT 0 #define AB8500_ADSLOTSELX_ODD_SHIFT 4 +#define AB8500_ADSLOTSELX_AD_OUT_TO_SLOT(out, slot) \ + ((out) << (((slot) & 1) ? \ + AB8500_ADSLOTSELX_ODD_SHIFT : AB8500_ADSLOTSELX_EVEN_SHIFT)) /* AB8500_ADSLOTHIZCTRL1 */ /* AB8500_ADSLOTHIZCTRL2 */ -- cgit v1.2.3 From 2add0ec14c259471d45d6afd051ca02eef0a4a92 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 21 May 2013 10:56:51 -0600 Subject: PCI/ASPM: Warn when driver asks to disable ASPM, but we can't do it Some devices have hardware problems related to using ASPM. Drivers for these devices use pci_disable_link_state() to prevent their device from entering L0s or L1. But on platforms where the OS doesn't have permission to manage ASPM, pci_disable_link_state() doesn't actually disable ASPM. Windows has a similar mechanism ("PciASPMOptOut"), and when the OS doesn't have control of ASPM, it doesn't actually disable ASPM either. This patch just adds a warning in dmesg about the fact that pci_disable_link_state() is doing nothing. Reported-by: Emmanuel Grumbach Reference: https://lkml.kernel.org/r/CANUX_P3F5YhbZX3WGU-j1AGpbXb_T9Bis2ErhvKkFMtDvzatVQ@mail.gmail.com Reference: https://bugzilla.kernel.org/show_bug.cgi?id=57331 Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aspm.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index d320df6375a2..faa83b632a84 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -724,9 +724,6 @@ static void __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem, struct pci_dev *parent = pdev->bus->self; struct pcie_link_state *link; - if (aspm_disabled && !force) - return; - if (!pci_is_pcie(pdev)) return; @@ -736,6 +733,19 @@ static void __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem, if (!parent || !parent->link_state) return; + /* + * A driver requested that ASPM be disabled on this device, but + * if we don't have permission to manage ASPM (e.g., on ACPI + * systems we have to observe the FADT ACPI_FADT_NO_ASPM bit and + * the _OSC method), we can't honor that request. Windows has + * a similar mechanism using "PciASPMOptOut", which is also + * ignored in this situation. + */ + if (aspm_disabled && !force) { + dev_warn(&pdev->dev, "can't disable ASPM; OS doesn't have ASPM control\n"); + return; + } + if (sem) down_read(&pci_bus_sem); mutex_lock(&aspm_lock); -- cgit v1.2.3 From 4fba3c3ba5c6b7803f36db7d56a58fa3226458d9 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 25 Apr 2013 15:07:47 +0800 Subject: tpm_tis: missing platform_driver_unregister() on error in init_tis() Add the missing platform_driver_unregister() before return from init_tis() in the device register error handling case. Signed-off-by: Wei Yongjun Signed-off-by: Kent Yoder --- drivers/char/tpm/tpm_tis.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index 8a41b6be23a0..4519cb332987 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -884,12 +884,19 @@ static int __init init_tis(void) rc = platform_driver_register(&tis_drv); if (rc < 0) return rc; - if (IS_ERR(pdev=platform_device_register_simple("tpm_tis", -1, NULL, 0))) - return PTR_ERR(pdev); - if((rc=tpm_tis_init(&pdev->dev, TIS_MEM_BASE, TIS_MEM_LEN, 0)) != 0) { - platform_device_unregister(pdev); - platform_driver_unregister(&tis_drv); + pdev = platform_device_register_simple("tpm_tis", -1, NULL, 0); + if (IS_ERR(pdev)) { + rc = PTR_ERR(pdev); + goto err_dev; } + rc = tpm_tis_init(&pdev->dev, TIS_MEM_BASE, TIS_MEM_LEN, 0); + if (rc) + goto err_init; + return 0; +err_init: + platform_device_unregister(pdev); +err_dev: + platform_driver_unregister(&tis_drv); return rc; } -- cgit v1.2.3 From 1c16c9636cad24f0e654f19e8f73ede0c7bd5540 Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Tue, 21 May 2013 10:40:47 -0400 Subject: tpm: move TPM_DIGEST_SIZE defintion IMA requires access to TPM_DIGEST_SIZE definition. This patch moves the definition to . Signed-off-by: Mimi Zohar Signed-off-by: Kent Yoder --- drivers/char/tpm/tpm.h | 1 - include/linux/tpm.h | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 0770d1d79366..433423288709 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -272,7 +272,6 @@ typedef union { struct tpm_output_header out; } tpm_cmd_header; -#define TPM_DIGEST_SIZE 20 struct tpm_pcrread_out { u8 pcr_result[TPM_DIGEST_SIZE]; } __packed; diff --git a/include/linux/tpm.h b/include/linux/tpm.h index fcb627ff8d3e..9a9051bb1a03 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -22,6 +22,8 @@ #ifndef __LINUX_TPM_H__ #define __LINUX_TPM_H__ +#define TPM_DIGEST_SIZE 20 /* Max TPM v1.2 PCR size */ + /* * Chip num is this value or a valid tpm idx */ -- cgit v1.2.3 From f773fc6dca4619bdf8da767eaba101a83b766059 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 21 May 2013 14:56:58 +0100 Subject: mfd: arizona: Change fast_start pdata name to better reflect functionality The bit in the register enables MICBIAS fast startup when clear not when set. This patch changes the name of this pdata option to soft_start to better match the functionality. We rename rather than invert the handling to keep the same default functionality, which is fast start active. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/mfd/arizona-core.c | 2 +- include/linux/mfd/arizona/pdata.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 437f199fe5f2..74b4481754fd 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -860,7 +860,7 @@ int arizona_dev_init(struct arizona *arizona) if (arizona->pdata.micbias[i].discharge) val |= ARIZONA_MICB1_DISCH; - if (arizona->pdata.micbias[i].fast_start) + if (arizona->pdata.micbias[i].soft_start) val |= ARIZONA_MICB1_RATE; if (arizona->pdata.micbias[i].bypass) diff --git a/include/linux/mfd/arizona/pdata.h b/include/linux/mfd/arizona/pdata.h index 80dead1f7100..12a5c135c746 100644 --- a/include/linux/mfd/arizona/pdata.h +++ b/include/linux/mfd/arizona/pdata.h @@ -77,7 +77,7 @@ struct arizona_micbias { int mV; /** Regulated voltage */ unsigned int ext_cap:1; /** External capacitor fitted */ unsigned int discharge:1; /** Actively discharge */ - unsigned int fast_start:1; /** Enable aggressive startup ramp rate */ + unsigned int soft_start:1; /** Disable aggressive startup ramp rate */ unsigned int bypass:1; /** Use bypass mode */ }; -- cgit v1.2.3 From 200ceb962f7b00815259bf3cb2df5a0ac15eb99d Mon Sep 17 00:00:00 2001 From: Barry Song <21cnbao@gmail.com> Date: Sat, 18 May 2013 20:25:00 +0800 Subject: ASoC: dfbmcs320: make the driver common for other BT modules DFBM-CS320 is only one of bluetooth modules using CSR bluetooth chips, we don't want everyone to have a seperate codec driver. anyway, the feature of Bluetooth SCO is same on all platforms, so this patch makes the DFBM-CS320 driver become a common BT SCO link driver. Signed-off-by: Barry Song Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 4 +-- sound/soc/codecs/Makefile | 4 +-- sound/soc/codecs/bt-sco.c | 71 ++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/dfbmcs320.c | 62 --------------------------------- sound/soc/samsung/Kconfig | 2 +- sound/soc/samsung/neo1973_wm8753.c | 2 +- 6 files changed, 77 insertions(+), 68 deletions(-) create mode 100644 sound/soc/codecs/bt-sco.c delete mode 100644 sound/soc/codecs/dfbmcs320.c diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 2f45f00e31b0..395fda22ade4 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -40,7 +40,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_DA7213 if I2C select SND_SOC_DA732X if I2C select SND_SOC_DA9055 if I2C - select SND_SOC_DFBMCS320 + select SND_SOC_BT_SCO select SND_SOC_ISABELLE if I2C select SND_SOC_JZ4740_CODEC select SND_SOC_LM4857 if I2C @@ -263,7 +263,7 @@ config SND_SOC_DA732X config SND_SOC_DA9055 tristate -config SND_SOC_DFBMCS320 +config SND_SOC_BT_SCO tristate config SND_SOC_DMIC diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index b9e41c9a1f4c..5ba9be87e472 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -27,7 +27,7 @@ snd-soc-da7210-objs := da7210.o snd-soc-da7213-objs := da7213.o snd-soc-da732x-objs := da732x.o snd-soc-da9055-objs := da9055.o -snd-soc-dfbmcs320-objs := dfbmcs320.o +snd-soc-bt-sco-objs := bt-sco.o snd-soc-dmic-objs := dmic.o snd-soc-isabelle-objs := isabelle.o snd-soc-jz4740-codec-objs := jz4740.o @@ -154,7 +154,7 @@ obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o obj-$(CONFIG_SND_SOC_DA7213) += snd-soc-da7213.o obj-$(CONFIG_SND_SOC_DA732X) += snd-soc-da732x.o obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o -obj-$(CONFIG_SND_SOC_DFBMCS320) += snd-soc-dfbmcs320.o +obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o diff --git a/sound/soc/codecs/bt-sco.c b/sound/soc/codecs/bt-sco.c new file mode 100644 index 000000000000..a081d9fcb166 --- /dev/null +++ b/sound/soc/codecs/bt-sco.c @@ -0,0 +1,71 @@ +/* + * Driver for generic Bluetooth SCO link + * Copyright 2011 Lars-Peter Clausen + * + * 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 +#include +#include + +#include + +static struct snd_soc_dai_driver bt_sco_dai = { + .name = "bt-sco-pcm", + .playback = { + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}; + +static struct snd_soc_codec_driver soc_codec_dev_bt_sco; + +static int bt_sco_probe(struct platform_device *pdev) +{ + return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_bt_sco, + &bt_sco_dai, 1); +} + +static int bt_sco_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + + return 0; +} + +static struct platform_device_id bt_sco_driver_ids[] = { + { + .name = "dfbmcs320", + }, + {}, +}; +MODULE_DEVICE_TABLE(platform, bt_sco_driver_ids); + +static struct platform_driver bt_sco_driver = { + .driver = { + .name = "bt-sco", + .owner = THIS_MODULE, + }, + .probe = bt_sco_probe, + .remove = bt_sco_remove, + .id_table = bt_sco_driver_ids, +}; + +module_platform_driver(bt_sco_driver); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("ASoC generic bluethooth sco link driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/dfbmcs320.c b/sound/soc/codecs/dfbmcs320.c deleted file mode 100644 index 4f4f7f41a7d1..000000000000 --- a/sound/soc/codecs/dfbmcs320.c +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Driver for the DFBM-CS320 bluetooth module - * Copyright 2011 Lars-Peter Clausen - * - * 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 -#include -#include - -#include - -static struct snd_soc_dai_driver dfbmcs320_dai = { - .name = "dfbmcs320-pcm", - .playback = { - .channels_min = 1, - .channels_max = 1, - .rates = SNDRV_PCM_RATE_8000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, - .capture = { - .channels_min = 1, - .channels_max = 1, - .rates = SNDRV_PCM_RATE_8000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, -}; - -static struct snd_soc_codec_driver soc_codec_dev_dfbmcs320; - -static int dfbmcs320_probe(struct platform_device *pdev) -{ - return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_dfbmcs320, - &dfbmcs320_dai, 1); -} - -static int dfbmcs320_remove(struct platform_device *pdev) -{ - snd_soc_unregister_codec(&pdev->dev); - - return 0; -} - -static struct platform_driver dfmcs320_driver = { - .driver = { - .name = "dfbmcs320", - .owner = THIS_MODULE, - }, - .probe = dfbmcs320_probe, - .remove = dfbmcs320_remove, -}; - -module_platform_driver(dfmcs320_driver); - -MODULE_AUTHOR("Lars-Peter Clausen "); -MODULE_DESCRIPTION("ASoC DFBM-CS320 bluethooth module driver"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig index 475fb0d8b3c6..ae0ea87b7d7b 100644 --- a/sound/soc/samsung/Kconfig +++ b/sound/soc/samsung/Kconfig @@ -39,7 +39,7 @@ config SND_SOC_SAMSUNG_NEO1973_WM8753 depends on SND_SOC_SAMSUNG && MACH_NEO1973_GTA02 select SND_S3C24XX_I2S select SND_SOC_WM8753 - select SND_SOC_DFBMCS320 + select SND_SOC_SCO help Say Y here to enable audio support for the Openmoko Neo1973 Smartphones. diff --git a/sound/soc/samsung/neo1973_wm8753.c b/sound/soc/samsung/neo1973_wm8753.c index e591c386917a..807db417d234 100644 --- a/sound/soc/samsung/neo1973_wm8753.c +++ b/sound/soc/samsung/neo1973_wm8753.c @@ -373,7 +373,7 @@ static struct snd_soc_dai_link neo1973_dai[] = { { /* Voice via BT */ .name = "Bluetooth", .stream_name = "Voice", - .cpu_dai_name = "dfbmcs320-pcm", + .cpu_dai_name = "bt-sco-pcm", .codec_dai_name = "wm8753-voice", .codec_name = "wm8753.0-001a", .ops = &neo1973_voice_ops, -- cgit v1.2.3 From f648167f3ac79018c210112508c732ea9bf67c7b Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Tue, 21 May 2013 18:02:00 +1000 Subject: xfs: avoid nesting transactions in xfs_qm_scall_setqlim() Lockdep reports: ============================================= [ INFO: possible recursive locking detected ] 3.9.0+ #3 Not tainted --------------------------------------------- setquota/28368 is trying to acquire lock: (sb_internal){++++.?}, at: [] xfs_trans_alloc+0x26/0x50 but task is already holding lock: (sb_internal){++++.?}, at: [] xfs_trans_alloc+0x26/0x50 from xfs_qm_scall_setqlim()->xfs_dqread() when a dquot needs to be allocated. xfs_qm_scall_setqlim() is starting a transaction and then not passing it into xfs_qm_dqet() and so it starts it's own transaction when allocating the dquot. Splat! Fix this by not allocating the dquot in xfs_qm_scall_setqlim() inside the setqlim transaction. This requires getting the dquot first (and allocating it if necessary) then dropping and relocking the dquot before joining it to the setqlim transaction. Reported-by: Michael L. Semon Signed-off-by: Dave Chinner Reviewed-by: Ben Myers Signed-off-by: Ben Myers --- fs/xfs/xfs_qm_syscalls.c | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/fs/xfs/xfs_qm_syscalls.c b/fs/xfs/xfs_qm_syscalls.c index c41190cad6e9..6cdf6ffc36a1 100644 --- a/fs/xfs/xfs_qm_syscalls.c +++ b/fs/xfs/xfs_qm_syscalls.c @@ -489,31 +489,36 @@ xfs_qm_scall_setqlim( if ((newlim->d_fieldmask & XFS_DQ_MASK) == 0) return 0; - tp = xfs_trans_alloc(mp, XFS_TRANS_QM_SETQLIM); - error = xfs_trans_reserve(tp, 0, XFS_QM_SETQLIM_LOG_RES(mp), - 0, 0, XFS_DEFAULT_LOG_COUNT); - if (error) { - xfs_trans_cancel(tp, 0); - return (error); - } - /* * We don't want to race with a quotaoff so take the quotaoff lock. - * (We don't hold an inode lock, so there's nothing else to stop - * a quotaoff from happening). (XXXThis doesn't currently happen - * because we take the vfslock before calling xfs_qm_sysent). + * We don't hold an inode lock, so there's nothing else to stop + * a quotaoff from happening. */ mutex_lock(&q->qi_quotaofflock); /* - * Get the dquot (locked), and join it to the transaction. - * Allocate the dquot if this doesn't exist. + * Get the dquot (locked) before we start, as we need to do a + * transaction to allocate it if it doesn't exist. Once we have the + * dquot, unlock it so we can start the next transaction safely. We hold + * a reference to the dquot, so it's safe to do this unlock/lock without + * it being reclaimed in the mean time. */ - if ((error = xfs_qm_dqget(mp, NULL, id, type, XFS_QMOPT_DQALLOC, &dqp))) { - xfs_trans_cancel(tp, XFS_TRANS_ABORT); + error = xfs_qm_dqget(mp, NULL, id, type, XFS_QMOPT_DQALLOC, &dqp); + if (error) { ASSERT(error != ENOENT); goto out_unlock; } + xfs_dqunlock(dqp); + + tp = xfs_trans_alloc(mp, XFS_TRANS_QM_SETQLIM); + error = xfs_trans_reserve(tp, 0, XFS_QM_SETQLIM_LOG_RES(mp), + 0, 0, XFS_DEFAULT_LOG_COUNT); + if (error) { + xfs_trans_cancel(tp, 0); + goto out_rele; + } + + xfs_dqlock(dqp); xfs_trans_dqjoin(tp, dqp); ddq = &dqp->q_core; @@ -621,9 +626,10 @@ xfs_qm_scall_setqlim( xfs_trans_log_dquot(tp, dqp); error = xfs_trans_commit(tp, 0); - xfs_qm_dqrele(dqp); - out_unlock: +out_rele: + xfs_qm_dqrele(dqp); +out_unlock: mutex_unlock(&q->qi_quotaofflock); return error; } -- cgit v1.2.3 From 90253cf142469a40f89f989904abf0a1e500e1a6 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Tue, 21 May 2013 18:02:01 +1000 Subject: xfs: remote attribute allocation may be contiguous When CRCs are enabled, there may be multiple allocations made if the headers cause a length overflow. This, however, does not mean that the number of headers required increases, as the second and subsequent extents may be contiguous with the previous extent. Hence when we map the extents to write the attribute data, we may end up with less extents than allocations made. Hence the assertion that we consume the number of headers we calculated in the allocation loop is incorrect and needs to be removed. Signed-off-by: Dave Chinner Reviewed-by: Ben Myers Signed-off-by: Ben Myers --- fs/xfs/xfs_attr_remote.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/xfs/xfs_attr_remote.c b/fs/xfs/xfs_attr_remote.c index dee84466dcc9..aad95b08e76b 100644 --- a/fs/xfs/xfs_attr_remote.c +++ b/fs/xfs/xfs_attr_remote.c @@ -359,6 +359,11 @@ xfs_attr_rmtval_set( * into requiring more blocks. e.g. for 512 byte blocks, we'll * spill for another block every 9 headers we require in this * loop. + * + * Note that this can result in contiguous allocation of blocks, + * so we don't use all the space we allocate for headers as we + * have one less header for each contiguous allocation that + * occurs in the map/write loop below. */ if (crcs && blkcnt == 0) { int total_len; @@ -439,7 +444,6 @@ xfs_attr_rmtval_set( lblkno += map.br_blockcount; } ASSERT(valuelen == 0); - ASSERT(hdrcnt == 0); return 0; } -- cgit v1.2.3 From bdf0eb3a026922dbf57f6839f3184c8d2ecc5f2e Mon Sep 17 00:00:00 2001 From: David Flater Date: Fri, 10 May 2013 14:37:43 +0200 Subject: pnp: restore automatic resolution of DMA conflicts To fix a 5-year-old regression, reverse changes made by commit 7ef3639 (PNP: don't fail device init if no DMA channel available). As an example to show the problem, my sound card provides a prioritized list of PnP "dependent sets" of requested resources: dependent set 0 (preferred) wants DMA 5. dependent set 1 (acceptable) will take DMA 5, 6, or 7. ... dependent set 4 (acceptable) doesn't request a high DMA. If DMA 5 is not available, pnp_assign_dma has to fail on set 0 so that pnp_auto_config_dev will move on to set 1 and get DMA 6 or 7. Instead, pnp_assign_dma adds the resource with flags |= IORESOURCE_DISABLED and returns success. pnp_auto_config_dev just sees success and therefore chooses set 0 with a disabled DMA and never tries the sets that would have resolved the conflict. Furthermore, this mode of "success" is unexpected and unhandled in sound/isa/sb and probably other drivers. sb assumes that the returned DMA is enabled and obliviously uses the invalid DMA number. Observed consequences were sb successfully grabbing a DMA that was expressly forbidden by the kernel parameter pnp_reserve_dma. The only upside to the original change would be as a kludge for devices that can operate in degraded mode without a DMA but that don't provide the corresponding non-preferred dependent set. The right workaround for those devices is to synthesize the missing set in quirks.c; otherwise, you're reinventing PnP fallback functionality at the driver level for that device and all others. Signed-off-by: David Flater Signed-off-by: Rafael J. Wysocki --- drivers/pnp/manager.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/pnp/manager.c b/drivers/pnp/manager.c index 95cebf0185de..9357aa779048 100644 --- a/drivers/pnp/manager.c +++ b/drivers/pnp/manager.c @@ -211,6 +211,12 @@ static int pnp_assign_dma(struct pnp_dev *dev, struct pnp_dma *rule, int idx) res->start = -1; res->end = -1; + if (!rule->map) { + res->flags |= IORESOURCE_DISABLED; + pnp_dbg(&dev->dev, " dma %d disabled\n", idx); + goto __add; + } + for (i = 0; i < 8; i++) { if (rule->map & (1 << xtab[i])) { res->start = res->end = xtab[i]; @@ -218,11 +224,9 @@ static int pnp_assign_dma(struct pnp_dev *dev, struct pnp_dma *rule, int idx) goto __add; } } -#ifdef MAX_DMA_CHANNELS - res->start = res->end = MAX_DMA_CHANNELS; -#endif - res->flags |= IORESOURCE_DISABLED; - pnp_dbg(&dev->dev, " disable dma %d\n", idx); + + pnp_dbg(&dev->dev, " couldn't assign dma %d\n", idx); + return -EBUSY; __add: pnp_add_dma_resource(dev, res->start, res->flags); -- cgit v1.2.3 From 966fbe193f47c68e70a80ec9991098e88e7959cb Mon Sep 17 00:00:00 2001 From: Vincent Pelletier Date: Tue, 21 May 2013 22:30:58 +0200 Subject: libata: Add atapi_dmadir force flag Some device require DMADIR to be enabled, but are not detected as such by atapi_id_dmadir. One such example is "Asus Serillel 2" SATA-host-to-PATA-device bridge: the bridge itself requires DMADIR, even if the bridged device does not. As atapi_dmadir module parameter can cause problems with some devices (as per Tejun Heo's memory), enabling it globally may not be possible depending on the hardware. This patch adds atapi_dmadir in the form of a "force" horkage value, allowing global, per-bus and per-device control. Signed-off-by: Vincent Pelletier Signed-off-by: Tejun Heo --- Documentation/kernel-parameters.txt | 2 ++ drivers/ata/libata-core.c | 3 ++- include/linux/libata.h | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index c3bfacb92910..489815e3e484 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1456,6 +1456,8 @@ bytes respectively. Such letter suffixes can also be entirely omitted. * dump_id: dump IDENTIFY data. + * atapi_dmadir: Enable ATAPI DMADIR bridge support + If there are multiple matching configurations changing the same attribute, the last one is used. diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 5f7d5f9ee820..c97a244c099a 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -2395,7 +2395,7 @@ int ata_dev_configure(struct ata_device *dev) cdb_intr_string = ", CDB intr"; } - if (atapi_dmadir || atapi_id_dmadir(dev->id)) { + if (atapi_dmadir || (dev->horkage & ATA_HORKAGE_ATAPI_DMADIR) || atapi_id_dmadir(dev->id)) { dev->flags |= ATA_DFLAG_DMADIR; dma_dir_string = ", DMADIR"; } @@ -6496,6 +6496,7 @@ static int __init ata_parse_force_one(char **cur, { "nosrst", .lflags = ATA_LFLAG_NO_SRST }, { "norst", .lflags = ATA_LFLAG_NO_HRST | ATA_LFLAG_NO_SRST }, { "rstonce", .lflags = ATA_LFLAG_RST_ONCE }, + { "atapi_dmadir", .horkage_on = ATA_HORKAGE_ATAPI_DMADIR }, }; char *start = *cur, *p = *cur; char *id, *val, *endp; diff --git a/include/linux/libata.h b/include/linux/libata.h index 47e029236f6e..c886dc87aa81 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -399,6 +399,7 @@ enum { ATA_HORKAGE_BROKEN_FPDMA_AA = (1 << 15), /* skip AA */ ATA_HORKAGE_DUMP_ID = (1 << 16), /* dump IDENTIFY data */ ATA_HORKAGE_MAX_SEC_LBA48 = (1 << 17), /* Set max sects to 65535 */ + ATA_HORKAGE_ATAPI_DMADIR = (1 << 18), /* device requires dmadir */ /* DMA mask for user DMA control: User visible values; DO NOT renumber */ -- cgit v1.2.3 From b29900e62598cecd519c9ab2b8e4d03f8ebf702d Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Wed, 22 May 2013 08:53:48 +0900 Subject: AHCI: Make distinct names for ports in /proc/interrupts Currently all interrupts assigned to AHCI ports show up in '/proc/interrupts' as 'ahci'. This fix adds port numbers as suffixes and hence makes the descriptions distinct. Reported-by: Jan Beulich Signed-off-by: Alexander Gordeev Signed-off-by: Tejun Heo --- drivers/ata/ahci.c | 4 +++- drivers/ata/ahci.h | 1 + drivers/ata/libahci.c | 10 ++++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 251e57d38942..4d1c672d1702 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -1142,9 +1142,11 @@ int ahci_host_activate(struct ata_host *host, int irq, unsigned int n_msis) return rc; for (i = 0; i < host->n_ports; i++) { + struct ahci_port_priv *pp = host->ports[i]->private_data; + rc = devm_request_threaded_irq(host->dev, irq + i, ahci_hw_interrupt, ahci_thread_fn, IRQF_SHARED, - dev_driver_string(host->dev), host->ports[i]); + pp->irq_desc, host->ports[i]); if (rc) goto out_free_irqs; } diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h index b830e6c9fe49..05adf29a2814 100644 --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h @@ -306,6 +306,7 @@ struct ahci_port_priv { int fbs_last_dev; /* save FBS.DEV of last FIS */ /* enclosure management info per PM slot */ struct ahci_em_priv em_priv[EM_MAX_SLOTS]; + char *irq_desc; /* desc in /proc/interrupts */ }; struct ahci_host_priv { diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index 34c82167b962..3797a7bba15d 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -2234,6 +2234,16 @@ static int ahci_port_start(struct ata_port *ap) if (!pp) return -ENOMEM; + if (ap->host->n_ports > 1) { + pp->irq_desc = devm_kzalloc(dev, 8, GFP_KERNEL); + if (!pp->irq_desc) { + devm_kfree(dev, pp); + return -ENOMEM; + } + snprintf(pp->irq_desc, 8, + "%s%d", dev_driver_string(dev), ap->port_no); + } + /* check FBS capability */ if ((hpriv->cap & HOST_CAP_FBS) && sata_pmp_supported(ap)) { void __iomem *port_mmio = ahci_port_base(ap); -- cgit v1.2.3 From 2922a8de996956893bb98e4aa91be9774c958336 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Tue, 21 May 2013 20:36:34 -0600 Subject: spi: introduce macros to set bits_per_word_mask Introduce two macros to make setting up spi_master.bits_per_word_mask easier, and avoid mistakes like writing BIT(n) instead of BIT(n - 1). SPI_BPW_MASK is for a single supported value of bits_per_word_mask. SPI_BPW_RANGE_MASK represents a contiguous set of bit lengths. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- include/linux/spi/spi.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 6ff26c8db7b9..173725622252 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -308,6 +308,8 @@ struct spi_master { /* bitmask of supported bits_per_word for transfers */ u32 bits_per_word_mask; +#define SPI_BPW_MASK(bits) BIT((bits) - 1) +#define SPI_BPW_RANGE_MASK(min, max) ((BIT(max) - 1) - (BIT(min) - 1)) /* other constraints relevant to this driver */ u16 flags; -- cgit v1.2.3 From 54ca193310bbd33bdf8b2634b6a3115dd8665013 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Wed, 1 May 2013 17:17:29 +0200 Subject: rt2x00: rt2x00dev: use rt2x00dev->tx->limit The TX data queue is initialized already when the rt2x00lib_probe_hw() function is called. Fetch the number of the queue entries from that instead of using the entry_num field of the data queue descriptor. The two values are the same, and the use of the rt2x00dev->tx->limit value allows us to get rid of a superfluous pointer dereference. Signed-off-by: Gabor Juhos Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 90dc14336980..b2874675186a 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -1077,7 +1077,7 @@ static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev) */ int kfifo_size = roundup_pow_of_two(rt2x00dev->ops->tx_queues * - rt2x00dev->ops->tx->entry_num * + rt2x00dev->tx->limit * sizeof(u32)); status = kfifo_alloc(&rt2x00dev->txstatus_fifo, kfifo_size, -- cgit v1.2.3 From 98cd6c718c6e782d3b01e3fc3da00cf18c114586 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Wed, 1 May 2013 17:17:30 +0200 Subject: rt2x00: rt61pci: use rt2x00dev->tx->limit The TX data queue is initialized already when the rt61pci_txdone() function is called. Fetch the number of the queue entries from that instead of using the entry_num field of the data queue descriptor. The two values are the same, and the use of the rt2x00dev->tx->limit value allows us to get rid of a superfluous pointer dereference. Signed-off-by: Gabor Juhos Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt61pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c index 0dc8180e251b..7e1759b3e49a 100644 --- a/drivers/net/wireless/rt2x00/rt61pci.c +++ b/drivers/net/wireless/rt2x00/rt61pci.c @@ -2175,7 +2175,7 @@ static void rt61pci_txdone(struct rt2x00_dev *rt2x00dev) * that the TX_STA_FIFO stack has a size of 16. We stick to our * tx ring size for now. */ - for (i = 0; i < rt2x00dev->ops->tx->entry_num; i++) { + for (i = 0; i < rt2x00dev->tx->limit; i++) { rt2x00mmio_register_read(rt2x00dev, STA_CSR4, ®); if (!rt2x00_get_field32(reg, STA_CSR4_VALID)) break; -- cgit v1.2.3 From 1cfcbe4cd4033ada0e18051b55e7dcab9bde36e3 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Wed, 1 May 2013 17:17:31 +0200 Subject: rt2x00: rt2800pci: use rt2x00dev->tx->limit The TX data queue is initialized already when the rt2800pci_txstatus_interrupt() function is called. Fetch the number of the queue entries from that instead of using the entry_num field of the data queue descriptor. The two values are the same, and the use of the rt2x00dev->tx->limit value allows us to get rid of a superfluous pointer dereference. Signed-off-by: Gabor Juhos Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c index 6f4a861af336..330f1d25726d 100644 --- a/drivers/net/wireless/rt2x00/rt2800pci.c +++ b/drivers/net/wireless/rt2x00/rt2800pci.c @@ -1014,7 +1014,7 @@ static void rt2800pci_txstatus_interrupt(struct rt2x00_dev *rt2x00dev) * Since we have only one producer and one consumer we don't * need to lock the kfifo. */ - for (i = 0; i < rt2x00dev->ops->tx->entry_num; i++) { + for (i = 0; i < rt2x00dev->tx->limit; i++) { rt2x00mmio_register_read(rt2x00dev, TX_STA_FIFO, &status); if (!rt2x00_get_field32(status, TX_STA_FIFO_VALID)) -- cgit v1.2.3 From 3a28c8ac15ecbb6ee93f7c35745f75dee1018cd7 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Wed, 1 May 2013 17:17:32 +0200 Subject: rt2x00: rt2800usb: use rt2x00dev->rx->limit The RX data queue is initialized already when the rt2800_usb_enable_radio() function is called. Fetch the number of the queue entries from that instead of using the entry_num field of the data queue descriptor. The two values are the same, and the use of the rt2x00dev->rx->limit value allows us to get rid of a superfluous pointer dereference. Signed-off-by: Gabor Juhos Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c index ac854d75bd6c..c71a48da9a31 100644 --- a/drivers/net/wireless/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/rt2x00/rt2800usb.c @@ -327,7 +327,7 @@ static int rt2800usb_enable_radio(struct rt2x00_dev *rt2x00dev) * this limit so reduce the number to prevent errors. */ rt2x00_set_field32(®, USB_DMA_CFG_RX_BULK_AGG_LIMIT, - ((rt2x00dev->ops->rx->entry_num * DATA_FRAME_SIZE) + ((rt2x00dev->rx->limit * DATA_FRAME_SIZE) / 1024) - 3); rt2x00_set_field32(®, USB_DMA_CFG_RX_BULK_EN, 1); rt2x00_set_field32(®, USB_DMA_CFG_TX_BULK_EN, 1); -- cgit v1.2.3 From 0879f8750826f53a9fa1a40695bda01830c9a856 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Wed, 1 May 2013 17:17:33 +0200 Subject: rt2x00: rt2800lib: use rt2x00dev->bcn->winfo_size The beacon data queue is initialized already when the rt2800_clear_beacon_register() function is called. Fetch the size of the TXWI descriptor from that instead of using the winfo_size field of the data queue descriptor. The two values are the same, and the use of the rt2x00dev->bcn->winfo_size value allows us to get rid of a superfluous pointer dereference. Signed-off-by: Gabor Juhos Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index b52d70c75e1a..bddfd6dab3cf 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -840,7 +840,7 @@ static inline void rt2800_clear_beacon_register(struct rt2x00_dev *rt2x00dev, unsigned int beacon_base) { int i; - const int txwi_desc_size = rt2x00dev->ops->bcn->winfo_size; + const int txwi_desc_size = rt2x00dev->bcn->winfo_size; /* * For the Beacon base registers we only need to clear -- cgit v1.2.3 From e9e433032c83b28b3c41470bf91ecf597a78a3fc Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Wed, 1 May 2013 17:17:34 +0200 Subject: rt2x00: rt2x00dev: defer operational mode detection Only do it after the queues are allocated. This will allow to use the 'rt2x00dev->bcn->limit' instead of 'rt2x00dev->ops->bcn->entry_num'. Signed-off-by: Gabor Juhos Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00dev.c | 34 ++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index b2874675186a..6a201725bc50 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -1300,23 +1300,6 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) rt2x00dev->hw->wiphy->addr_mask[ETH_ALEN - 1] = (rt2x00dev->ops->max_ap_intf - 1); - /* - * Determine which operating modes are supported, all modes - * which require beaconing, depend on the availability of - * beacon entries. - */ - rt2x00dev->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); - if (rt2x00dev->ops->bcn->entry_num > 0) - rt2x00dev->hw->wiphy->interface_modes |= - BIT(NL80211_IFTYPE_ADHOC) | - BIT(NL80211_IFTYPE_AP) | -#ifdef CONFIG_MAC80211_MESH - BIT(NL80211_IFTYPE_MESH_POINT) | -#endif - BIT(NL80211_IFTYPE_WDS); - - rt2x00dev->hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; - /* * Initialize work. */ @@ -1347,6 +1330,23 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) if (retval) goto exit; + /* + * Determine which operating modes are supported, all modes + * which require beaconing, depend on the availability of + * beacon entries. + */ + rt2x00dev->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); + if (rt2x00dev->ops->bcn->entry_num > 0) + rt2x00dev->hw->wiphy->interface_modes |= + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_AP) | +#ifdef CONFIG_MAC80211_MESH + BIT(NL80211_IFTYPE_MESH_POINT) | +#endif + BIT(NL80211_IFTYPE_WDS); + + rt2x00dev->hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; + /* * Initialize ieee80211 structure. */ -- cgit v1.2.3 From 9483f40d8d01918b399b4e24d0c1111db0afffeb Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 7 May 2013 00:28:50 +0200 Subject: rt2x00pci: Use PCI MSIs whenever possible All PCIe devices must support MSIs, make use of them. Signed-off-by: Jakub Kicinski Acked-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00pci.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/rt2x00/rt2x00pci.c b/drivers/net/wireless/rt2x00/rt2x00pci.c index dc49e525ae5e..76d95deb274b 100644 --- a/drivers/net/wireless/rt2x00/rt2x00pci.c +++ b/drivers/net/wireless/rt2x00/rt2x00pci.c @@ -105,11 +105,13 @@ int rt2x00pci_probe(struct pci_dev *pci_dev, const struct rt2x00_ops *ops) goto exit_release_regions; } + pci_enable_msi(pci_dev); + hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), ops->hw); if (!hw) { rt2x00_probe_err("Failed to allocate hardware\n"); retval = -ENOMEM; - goto exit_release_regions; + goto exit_disable_msi; } pci_set_drvdata(pci_dev, hw); @@ -150,6 +152,9 @@ exit_free_reg: exit_free_device: ieee80211_free_hw(hw); +exit_disable_msi: + pci_disable_msi(pci_dev); + exit_release_regions: pci_release_regions(pci_dev); @@ -174,6 +179,8 @@ void rt2x00pci_remove(struct pci_dev *pci_dev) rt2x00pci_free_reg(rt2x00dev); ieee80211_free_hw(hw); + pci_disable_msi(pci_dev); + /* * Free the PCI device data. */ -- cgit v1.2.3 From cf657a2bc50dff0191d55fff4e7b31e7a6a542a5 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Wed, 8 May 2013 05:03:30 +0530 Subject: ath9k: Remove MAC_DEBUG This option has not been enabled by default in any distribution, has never been enabled in OpenWrt and no developer has asked for this information in a bug report. Dumping pages of random values doesn't help debugging, remove this option (along with the vmalloc() abuse). Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/Kconfig | 8 - drivers/net/wireless/ath/ath9k/debug.c | 337 --------------------------------- drivers/net/wireless/ath/ath9k/debug.h | 20 -- drivers/net/wireless/ath/ath9k/init.c | 3 - drivers/net/wireless/ath/ath9k/link.c | 1 - drivers/net/wireless/ath/ath9k/main.c | 1 - 6 files changed, 370 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig index 17507dc8a1e7..ec33c8007b29 100644 --- a/drivers/net/wireless/ath/ath9k/Kconfig +++ b/drivers/net/wireless/ath/ath9k/Kconfig @@ -84,14 +84,6 @@ config ATH9K_DFS_CERTIFIED developed. At this point enabling this option won't do anything except increase code size. -config ATH9K_MAC_DEBUG - bool "Atheros MAC statistics" - depends on ATH9K_DEBUGFS - default y - ---help--- - This option enables collection of statistics for Rx/Tx status - data and some other MAC related statistics - config ATH9K_RATE_CONTROL bool "Atheros ath9k rate control" depends on ATH9K diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index e6307b86363a..fc96ad3a135b 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -738,8 +738,6 @@ void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf, struct ath_tx_status *ts, struct ath_txq *txq, unsigned int flags) { -#define TX_SAMP_DBG(c) (sc->debug.bb_mac_samp[sc->debug.sampidx].ts\ - [sc->debug.tsidx].c) int qnum = txq->axq_qnum; TX_STAT_INC(qnum, tx_pkts_all); @@ -771,37 +769,6 @@ void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf, TX_STAT_INC(qnum, data_underrun); if (ts->ts_flags & ATH9K_TX_DELIM_UNDERRUN) TX_STAT_INC(qnum, delim_underrun); - -#ifdef CONFIG_ATH9K_MAC_DEBUG - spin_lock(&sc->debug.samp_lock); - TX_SAMP_DBG(jiffies) = jiffies; - TX_SAMP_DBG(rssi_ctl0) = ts->ts_rssi_ctl0; - TX_SAMP_DBG(rssi_ctl1) = ts->ts_rssi_ctl1; - TX_SAMP_DBG(rssi_ctl2) = ts->ts_rssi_ctl2; - TX_SAMP_DBG(rssi_ext0) = ts->ts_rssi_ext0; - TX_SAMP_DBG(rssi_ext1) = ts->ts_rssi_ext1; - TX_SAMP_DBG(rssi_ext2) = ts->ts_rssi_ext2; - TX_SAMP_DBG(rateindex) = ts->ts_rateindex; - TX_SAMP_DBG(isok) = !!(ts->ts_status & ATH9K_TXERR_MASK); - TX_SAMP_DBG(rts_fail_cnt) = ts->ts_shortretry; - TX_SAMP_DBG(data_fail_cnt) = ts->ts_longretry; - TX_SAMP_DBG(rssi) = ts->ts_rssi; - TX_SAMP_DBG(tid) = ts->tid; - TX_SAMP_DBG(qid) = ts->qid; - - if (ts->ts_flags & ATH9K_TX_BA) { - TX_SAMP_DBG(ba_low) = ts->ba_low; - TX_SAMP_DBG(ba_high) = ts->ba_high; - } else { - TX_SAMP_DBG(ba_low) = 0; - TX_SAMP_DBG(ba_high) = 0; - } - - sc->debug.tsidx = (sc->debug.tsidx + 1) % ATH_DBG_MAX_SAMPLES; - spin_unlock(&sc->debug.samp_lock); -#endif - -#undef TX_SAMP_DBG } static const struct file_operations fops_xmit = { @@ -915,8 +882,6 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf, void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs) { #define RX_PHY_ERR_INC(c) sc->debug.stats.rxstats.phy_err_stats[c]++ -#define RX_SAMP_DBG(c) (sc->debug.bb_mac_samp[sc->debug.sampidx].rs\ - [sc->debug.rsidx].c) RX_STAT_INC(rx_pkts_all); sc->debug.stats.rxstats.rx_bytes_all += rs->rs_datalen; @@ -940,27 +905,7 @@ void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs) RX_PHY_ERR_INC(rs->rs_phyerr); } -#ifdef CONFIG_ATH9K_MAC_DEBUG - spin_lock(&sc->debug.samp_lock); - RX_SAMP_DBG(jiffies) = jiffies; - RX_SAMP_DBG(rssi_ctl0) = rs->rs_rssi_ctl0; - RX_SAMP_DBG(rssi_ctl1) = rs->rs_rssi_ctl1; - RX_SAMP_DBG(rssi_ctl2) = rs->rs_rssi_ctl2; - RX_SAMP_DBG(rssi_ext0) = rs->rs_rssi_ext0; - RX_SAMP_DBG(rssi_ext1) = rs->rs_rssi_ext1; - RX_SAMP_DBG(rssi_ext2) = rs->rs_rssi_ext2; - RX_SAMP_DBG(antenna) = rs->rs_antenna; - RX_SAMP_DBG(rssi) = rs->rs_rssi; - RX_SAMP_DBG(rate) = rs->rs_rate; - RX_SAMP_DBG(is_mybeacon) = rs->is_mybeacon; - - sc->debug.rsidx = (sc->debug.rsidx + 1) % ATH_DBG_MAX_SAMPLES; - spin_unlock(&sc->debug.samp_lock); - -#endif - #undef RX_PHY_ERR_INC -#undef RX_SAMP_DBG } static const struct file_operations fops_recv = { @@ -1485,283 +1430,6 @@ static const struct file_operations fops_modal_eeprom = { .llseek = default_llseek, }; -#ifdef CONFIG_ATH9K_MAC_DEBUG - -void ath9k_debug_samp_bb_mac(struct ath_softc *sc) -{ -#define ATH_SAMP_DBG(c) (sc->debug.bb_mac_samp[sc->debug.sampidx].c) - struct ath_hw *ah = sc->sc_ah; - struct ath_common *common = ath9k_hw_common(ah); - unsigned long flags; - int i; - - ath9k_ps_wakeup(sc); - - spin_lock_bh(&sc->debug.samp_lock); - - spin_lock_irqsave(&common->cc_lock, flags); - ath_hw_cycle_counters_update(common); - - ATH_SAMP_DBG(cc.cycles) = common->cc_ani.cycles; - ATH_SAMP_DBG(cc.rx_busy) = common->cc_ani.rx_busy; - ATH_SAMP_DBG(cc.rx_frame) = common->cc_ani.rx_frame; - ATH_SAMP_DBG(cc.tx_frame) = common->cc_ani.tx_frame; - spin_unlock_irqrestore(&common->cc_lock, flags); - - ATH_SAMP_DBG(noise) = ah->noise; - - REG_WRITE_D(ah, AR_MACMISC, - ((AR_MACMISC_DMA_OBS_LINE_8 << AR_MACMISC_DMA_OBS_S) | - (AR_MACMISC_MISC_OBS_BUS_1 << - AR_MACMISC_MISC_OBS_BUS_MSB_S))); - - for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++) - ATH_SAMP_DBG(dma_dbg_reg_vals[i]) = REG_READ_D(ah, - AR_DMADBG_0 + (i * sizeof(u32))); - - ATH_SAMP_DBG(pcu_obs) = REG_READ_D(ah, AR_OBS_BUS_1); - ATH_SAMP_DBG(pcu_cr) = REG_READ_D(ah, AR_CR); - - memcpy(ATH_SAMP_DBG(nfCalHist), sc->caldata.nfCalHist, - sizeof(ATH_SAMP_DBG(nfCalHist))); - - sc->debug.sampidx = (sc->debug.sampidx + 1) % ATH_DBG_MAX_SAMPLES; - spin_unlock_bh(&sc->debug.samp_lock); - ath9k_ps_restore(sc); - -#undef ATH_SAMP_DBG -} - -static int open_file_bb_mac_samps(struct inode *inode, struct file *file) -{ -#define ATH_SAMP_DBG(c) bb_mac_samp[sampidx].c - struct ath_softc *sc = inode->i_private; - struct ath_hw *ah = sc->sc_ah; - struct ath_common *common = ath9k_hw_common(ah); - struct ieee80211_conf *conf = &common->hw->conf; - struct ath_dbg_bb_mac_samp *bb_mac_samp; - struct ath9k_nfcal_hist *h; - int i, j, qcuOffset = 0, dcuOffset = 0; - u32 *qcuBase, *dcuBase, size = 30000, len = 0; - u32 sampidx = 0; - u8 *buf; - u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask; - u8 nread; - - if (test_bit(SC_OP_INVALID, &sc->sc_flags)) - return -EAGAIN; - - buf = vmalloc(size); - if (!buf) - return -ENOMEM; - bb_mac_samp = vmalloc(sizeof(*bb_mac_samp) * ATH_DBG_MAX_SAMPLES); - if (!bb_mac_samp) { - vfree(buf); - return -ENOMEM; - } - /* Account the current state too */ - ath9k_debug_samp_bb_mac(sc); - - spin_lock_bh(&sc->debug.samp_lock); - memcpy(bb_mac_samp, sc->debug.bb_mac_samp, - sizeof(*bb_mac_samp) * ATH_DBG_MAX_SAMPLES); - len += snprintf(buf + len, size - len, - "Current Sample Index: %d\n", sc->debug.sampidx); - spin_unlock_bh(&sc->debug.samp_lock); - - len += snprintf(buf + len, size - len, - "Raw DMA Debug Dump:\n"); - len += snprintf(buf + len, size - len, "Sample |\t"); - for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++) - len += snprintf(buf + len, size - len, " DMA Reg%d |\t", i); - len += snprintf(buf + len, size - len, "\n"); - - for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) { - len += snprintf(buf + len, size - len, "%d\t", sampidx); - - for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++) - len += snprintf(buf + len, size - len, " %08x\t", - ATH_SAMP_DBG(dma_dbg_reg_vals[i])); - len += snprintf(buf + len, size - len, "\n"); - } - len += snprintf(buf + len, size - len, "\n"); - - len += snprintf(buf + len, size - len, - "Sample Num QCU: chain_st fsp_ok fsp_st DCU: chain_st\n"); - for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) { - qcuBase = &ATH_SAMP_DBG(dma_dbg_reg_vals[0]); - dcuBase = &ATH_SAMP_DBG(dma_dbg_reg_vals[4]); - - for (i = 0; i < ATH9K_NUM_QUEUES; i++, - qcuOffset += 4, dcuOffset += 5) { - if (i == 8) { - qcuOffset = 0; - qcuBase++; - } - - if (i == 6) { - dcuOffset = 0; - dcuBase++; - } - if (!sc->debug.stats.txstats[i].queued) - continue; - - len += snprintf(buf + len, size - len, - "%4d %7d %2x %1x %2x %2x\n", - sampidx, i, - (*qcuBase & (0x7 << qcuOffset)) >> qcuOffset, - (*qcuBase & (0x8 << qcuOffset)) >> - (qcuOffset + 3), - ATH_SAMP_DBG(dma_dbg_reg_vals[2]) & - (0x7 << (i * 3)) >> (i * 3), - (*dcuBase & (0x1f << dcuOffset)) >> dcuOffset); - } - len += snprintf(buf + len, size - len, "\n"); - } - len += snprintf(buf + len, size - len, - "samp qcu_sh qcu_fh qcu_comp dcu_comp dcu_arb dcu_fp " - "ch_idle_dur ch_idle_dur_val txfifo_val0 txfifo_val1 " - "txfifo_dcu0 txfifo_dcu1 pcu_obs AR_CR\n"); - - for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) { - qcuBase = &ATH_SAMP_DBG(dma_dbg_reg_vals[0]); - dcuBase = &ATH_SAMP_DBG(dma_dbg_reg_vals[4]); - - len += snprintf(buf + len, size - len, "%4d %5x %5x ", sampidx, - (ATH_SAMP_DBG(dma_dbg_reg_vals[3]) & 0x003c0000) >> 18, - (ATH_SAMP_DBG(dma_dbg_reg_vals[3]) & 0x03c00000) >> 22); - len += snprintf(buf + len, size - len, "%7x %8x ", - (ATH_SAMP_DBG(dma_dbg_reg_vals[3]) & 0x1c000000) >> 26, - (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x3)); - len += snprintf(buf + len, size - len, "%7x %7x ", - (ATH_SAMP_DBG(dma_dbg_reg_vals[5]) & 0x06000000) >> 25, - (ATH_SAMP_DBG(dma_dbg_reg_vals[5]) & 0x38000000) >> 27); - len += snprintf(buf + len, size - len, "%7d %12d ", - (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x000003fc) >> 2, - (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x00000400) >> 10); - len += snprintf(buf + len, size - len, "%12d %12d ", - (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x00000800) >> 11, - (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x00001000) >> 12); - len += snprintf(buf + len, size - len, "%12d %12d ", - (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x0001e000) >> 13, - (ATH_SAMP_DBG(dma_dbg_reg_vals[6]) & 0x001e0000) >> 17); - len += snprintf(buf + len, size - len, "0x%07x 0x%07x\n", - ATH_SAMP_DBG(pcu_obs), ATH_SAMP_DBG(pcu_cr)); - } - - len += snprintf(buf + len, size - len, - "Sample ChNoise Chain privNF #Reading Readings\n"); - for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) { - h = ATH_SAMP_DBG(nfCalHist); - if (!ATH_SAMP_DBG(noise)) - continue; - - for (i = 0; i < NUM_NF_READINGS; i++) { - if (!(chainmask & (1 << i)) || - ((i >= AR5416_MAX_CHAINS) && !conf_is_ht40(conf))) - continue; - - nread = AR_PHY_CCA_FILTERWINDOW_LENGTH - - h[i].invalidNFcount; - len += snprintf(buf + len, size - len, - "%4d %5d %4d\t %d\t %d\t", - sampidx, ATH_SAMP_DBG(noise), - i, h[i].privNF, nread); - for (j = 0; j < nread; j++) - len += snprintf(buf + len, size - len, - " %d", h[i].nfCalBuffer[j]); - len += snprintf(buf + len, size - len, "\n"); - } - } - len += snprintf(buf + len, size - len, "\nCycle counters:\n" - "Sample Total Rxbusy Rxframes Txframes\n"); - for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) { - if (!ATH_SAMP_DBG(cc.cycles)) - continue; - len += snprintf(buf + len, size - len, - "%4d %08x %08x %08x %08x\n", - sampidx, ATH_SAMP_DBG(cc.cycles), - ATH_SAMP_DBG(cc.rx_busy), - ATH_SAMP_DBG(cc.rx_frame), - ATH_SAMP_DBG(cc.tx_frame)); - } - - len += snprintf(buf + len, size - len, "Tx status Dump :\n"); - len += snprintf(buf + len, size - len, - "Sample rssi:- ctl0 ctl1 ctl2 ext0 ext1 ext2 comb " - "isok rts_fail data_fail rate tid qid " - "ba_low ba_high tx_before(ms)\n"); - for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) { - for (i = 0; i < ATH_DBG_MAX_SAMPLES; i++) { - if (!ATH_SAMP_DBG(ts[i].jiffies)) - continue; - len += snprintf(buf + len, size - len, "%-14d" - "%-4d %-4d %-4d %-4d %-4d %-4d %-4d %-4d %-8d " - "%-9d %-4d %-3d %-3d %08x %08x %-11d\n", - sampidx, - ATH_SAMP_DBG(ts[i].rssi_ctl0), - ATH_SAMP_DBG(ts[i].rssi_ctl1), - ATH_SAMP_DBG(ts[i].rssi_ctl2), - ATH_SAMP_DBG(ts[i].rssi_ext0), - ATH_SAMP_DBG(ts[i].rssi_ext1), - ATH_SAMP_DBG(ts[i].rssi_ext2), - ATH_SAMP_DBG(ts[i].rssi), - ATH_SAMP_DBG(ts[i].isok), - ATH_SAMP_DBG(ts[i].rts_fail_cnt), - ATH_SAMP_DBG(ts[i].data_fail_cnt), - ATH_SAMP_DBG(ts[i].rateindex), - ATH_SAMP_DBG(ts[i].tid), - ATH_SAMP_DBG(ts[i].qid), - ATH_SAMP_DBG(ts[i].ba_low), - ATH_SAMP_DBG(ts[i].ba_high), - jiffies_to_msecs(jiffies - - ATH_SAMP_DBG(ts[i].jiffies))); - } - } - - len += snprintf(buf + len, size - len, "Rx status Dump :\n"); - len += snprintf(buf + len, size - len, "Sample rssi:- ctl0 ctl1 ctl2 " - "ext0 ext1 ext2 comb beacon ant rate rx_before(ms)\n"); - for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) { - for (i = 0; i < ATH_DBG_MAX_SAMPLES; i++) { - if (!ATH_SAMP_DBG(rs[i].jiffies)) - continue; - len += snprintf(buf + len, size - len, "%-14d" - "%-4d %-4d %-4d %-4d %-4d %-4d %-4d %-9s %-2d %02x %-13d\n", - sampidx, - ATH_SAMP_DBG(rs[i].rssi_ctl0), - ATH_SAMP_DBG(rs[i].rssi_ctl1), - ATH_SAMP_DBG(rs[i].rssi_ctl2), - ATH_SAMP_DBG(rs[i].rssi_ext0), - ATH_SAMP_DBG(rs[i].rssi_ext1), - ATH_SAMP_DBG(rs[i].rssi_ext2), - ATH_SAMP_DBG(rs[i].rssi), - ATH_SAMP_DBG(rs[i].is_mybeacon) ? - "True" : "False", - ATH_SAMP_DBG(rs[i].antenna), - ATH_SAMP_DBG(rs[i].rate), - jiffies_to_msecs(jiffies - - ATH_SAMP_DBG(rs[i].jiffies))); - } - } - - vfree(bb_mac_samp); - file->private_data = buf; - - return 0; -#undef ATH_SAMP_DBG -} - -static const struct file_operations fops_samps = { - .open = open_file_bb_mac_samps, - .read = ath9k_debugfs_read_buf, - .release = ath9k_debugfs_release_buf, - .owner = THIS_MODULE, - .llseek = default_llseek, -}; - -#endif - #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT static ssize_t read_file_btcoex(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -2087,11 +1755,6 @@ int ath9k_init_debug(struct ath_hw *ah) debugfs_create_file("spectral_fft_period", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, sc, &fops_spectral_fft_period); - -#ifdef CONFIG_ATH9K_MAC_DEBUG - debugfs_create_file("samples", S_IRUSR, sc->debug.debugfs_phy, sc, - &fops_samps); -#endif debugfs_create_u32("gpio_mask", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, &sc->sc_ah->gpio_mask); debugfs_create_u32("gpio_val", S_IRUSR | S_IWUSR, diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h index 794a7ec83a24..62da19c6f4e0 100644 --- a/drivers/net/wireless/ath/ath9k/debug.h +++ b/drivers/net/wireless/ath/ath9k/debug.h @@ -294,13 +294,6 @@ struct ath9k_debug { struct dentry *debugfs_phy; u32 regidx; struct ath_stats stats; -#ifdef CONFIG_ATH9K_MAC_DEBUG - spinlock_t samp_lock; - struct ath_dbg_bb_mac_samp bb_mac_samp[ATH_DBG_MAX_SAMPLES]; - u8 sampidx; - u8 tsidx; - u8 rsidx; -#endif }; int ath9k_init_debug(struct ath_hw *ah); @@ -359,17 +352,4 @@ static inline void ath_debug_stat_rx(struct ath_softc *sc, #endif /* CONFIG_ATH9K_DEBUGFS */ -#ifdef CONFIG_ATH9K_MAC_DEBUG - -void ath9k_debug_samp_bb_mac(struct ath_softc *sc); - -#else - -static inline void ath9k_debug_samp_bb_mac(struct ath_softc *sc) -{ -} - -#endif - - #endif /* DEBUG_H */ diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 0237b2868961..c7b888f22703 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -613,9 +613,6 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, spin_lock_init(&sc->sc_serial_rw); spin_lock_init(&sc->sc_pm_lock); mutex_init(&sc->mutex); -#ifdef CONFIG_ATH9K_MAC_DEBUG - spin_lock_init(&sc->debug.samp_lock); -#endif tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc); tasklet_init(&sc->bcon_tasklet, ath9k_beacon_tasklet, (unsigned long)sc); diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c index 849259b07370..cc422a4dfa17 100644 --- a/drivers/net/wireless/ath/ath9k/link.c +++ b/drivers/net/wireless/ath/ath9k/link.c @@ -418,7 +418,6 @@ void ath_ani_calibrate(unsigned long data) longcal ? "long" : "", shortcal ? "short" : "", aniflag ? "ani" : "", common->ani.caldone ? "true" : "false"); - ath9k_debug_samp_bb_mac(sc); ath9k_ps_restore(sc); set_timer: diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 6963862a1872..1cbfdd4e4a74 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -193,7 +193,6 @@ static bool ath_prepare_reset(struct ath_softc *sc) ath_stop_ani(sc); del_timer_sync(&sc->rx_poll_timer); - ath9k_debug_samp_bb_mac(sc); ath9k_hw_disable_interrupts(ah); if (!ath_drain_all_txq(sc)) -- cgit v1.2.3 From 73900cb068a2948d636feae0c8bc25cc2d568d6d Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Wed, 8 May 2013 05:03:31 +0530 Subject: ath9k: Use bitops for scan flag Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 2 +- drivers/net/wireless/ath/ath9k/main.c | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 8a1888d02070..579ed9c40b3f 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -646,6 +646,7 @@ enum sc_op_flags { SC_OP_ANI_RUN, SC_OP_PRIM_STA_VIF, SC_OP_HW_RESET, + SC_OP_SCANNING, }; /* Powersave flags */ @@ -759,7 +760,6 @@ struct ath_softc { struct rchan *rfs_chan_spec_scan; enum spectral_mode spectral_mode; struct ath_spec_scan spec_config; - int scanning; #ifdef CONFIG_PM_SLEEP atomic_t wow_got_bmiss_intr; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 1cbfdd4e4a74..ec6524aa5178 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1272,7 +1272,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) curchan->center_freq); } else { /* perform spectral scan if requested. */ - if (sc->scanning && + if (test_bit(SC_OP_SCANNING, &sc->sc_flags) && sc->spectral_mode == SPECTRAL_CHANSCAN) ath9k_spectral_scan_trigger(hw); } @@ -2334,15 +2334,13 @@ static void ath9k_set_wakeup(struct ieee80211_hw *hw, bool enabled) static void ath9k_sw_scan_start(struct ieee80211_hw *hw) { struct ath_softc *sc = hw->priv; - - sc->scanning = 1; + set_bit(SC_OP_SCANNING, &sc->sc_flags); } static void ath9k_sw_scan_complete(struct ieee80211_hw *hw) { struct ath_softc *sc = hw->priv; - - sc->scanning = 0; + clear_bit(SC_OP_SCANNING, &sc->sc_flags); } struct ieee80211_ops ath9k_ops = { -- cgit v1.2.3 From 7ca7c776dbc50cf1394dd95e971e7997a4ec025e Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Wed, 8 May 2013 05:03:32 +0530 Subject: ath9k: Do not use local_bh_disable in ampdu_action This was added during the early conversion of ampdu_action to a sleeping callback. There is no need to do this - instead, use the normal mutex that is acquired for all callbacks. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index ec6524aa5178..e6a30958e944 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1684,7 +1684,7 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw, struct ath_softc *sc = hw->priv; int ret = 0; - local_bh_disable(); + mutex_lock(&sc->mutex); switch (action) { case IEEE80211_AMPDU_RX_START: @@ -1715,7 +1715,7 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw, ath_err(ath9k_hw_common(sc->sc_ah), "Unknown AMPDU action\n"); } - local_bh_enable(); + mutex_unlock(&sc->mutex); return ret; } -- cgit v1.2.3 From 594e65b633e0b76db1d8e7359e4efb2d60fba20d Mon Sep 17 00:00:00 2001 From: Javier Cardona Date: Wed, 8 May 2013 10:16:46 -0700 Subject: ath9k_htc: Add support for mesh interfaces More specifically, enable AP-style beaconing on mesh ifaces and change the hw capabilities to reflect mesh support. Coexistence with a virtual STA interface was tested as working fine. Signed-off-by: Javier Cardona [rebase, add iface combinations] Signed-off-by: Thomas Pedersen Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/htc.h | 7 ++++++ drivers/net/wireless/ath/ath9k/htc_drv_beacon.c | 5 ++++- drivers/net/wireless/ath/ath9k/htc_drv_init.c | 6 +++-- drivers/net/wireless/ath/ath9k/htc_drv_main.c | 29 ++++++++++++++++++------- drivers/net/wireless/ath/ath9k/htc_drv_txrx.c | 2 +- 5 files changed, 37 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h index d3b099d7898b..0085e643132f 100644 --- a/drivers/net/wireless/ath/ath9k/htc.h +++ b/drivers/net/wireless/ath/ath9k/htc.h @@ -208,6 +208,9 @@ struct ath9k_htc_target_rx_stats { case NL80211_IFTYPE_AP: \ _priv->num_ap_vif++; \ break; \ + case NL80211_IFTYPE_MESH_POINT: \ + _priv->num_mbss_vif++; \ + break; \ default: \ break; \ } \ @@ -224,6 +227,9 @@ struct ath9k_htc_target_rx_stats { case NL80211_IFTYPE_AP: \ _priv->num_ap_vif--; \ break; \ + case NL80211_IFTYPE_MESH_POINT: \ + _priv->num_mbss_vif--; \ + break; \ default: \ break; \ } \ @@ -450,6 +456,7 @@ struct ath9k_htc_priv { u8 sta_slot; u8 vif_sta_pos[ATH9K_HTC_MAX_VIF]; u8 num_ibss_vif; + u8 num_mbss_vif; u8 num_sta_vif; u8 num_sta_assoc_vif; u8 num_ap_vif; diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c index f13f458dd656..e0c03bd64182 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c @@ -28,7 +28,8 @@ void ath9k_htc_beaconq_config(struct ath9k_htc_priv *priv) ath9k_hw_get_txq_props(ah, priv->beaconq, &qi); - if (priv->ah->opmode == NL80211_IFTYPE_AP) { + if (priv->ah->opmode == NL80211_IFTYPE_AP || + priv->ah->opmode == NL80211_IFTYPE_MESH_POINT) { qi.tqi_aifs = 1; qi.tqi_cwmin = 0; qi.tqi_cwmax = 0; @@ -628,6 +629,7 @@ void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv, case NL80211_IFTYPE_ADHOC: ath9k_htc_beacon_config_adhoc(priv, cur_conf); break; + case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_AP: ath9k_htc_beacon_config_ap(priv, cur_conf); break; @@ -649,6 +651,7 @@ void ath9k_htc_beacon_reconfig(struct ath9k_htc_priv *priv) case NL80211_IFTYPE_ADHOC: ath9k_htc_beacon_config_adhoc(priv, cur_conf); break; + case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_AP: ath9k_htc_beacon_config_ap(priv, cur_conf); break; diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index a47f5e05fc04..6eb41990f608 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -698,7 +698,8 @@ static const struct ieee80211_iface_limit if_limits[] = { { .max = 2, .types = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_P2P_CLIENT) }, { .max = 2, .types = BIT(NL80211_IFTYPE_AP) | - BIT(NL80211_IFTYPE_P2P_GO) }, + BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_MESH_POINT) }, }; static const struct ieee80211_iface_combination if_comb = { @@ -728,7 +729,8 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv, BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_P2P_GO) | - BIT(NL80211_IFTYPE_P2P_CLIENT); + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_MESH_POINT); hw->wiphy->iface_combinations = &if_comb; hw->wiphy->n_iface_combinations = 1; diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index 0743a47cef8f..2a67b57c3ecb 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -113,7 +113,9 @@ static void ath9k_htc_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) struct ath9k_htc_priv *priv = data; struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; - if ((vif->type == NL80211_IFTYPE_AP) && bss_conf->enable_beacon) + if ((vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_MESH_POINT) && + bss_conf->enable_beacon) priv->reconfig_beacon = true; if (bss_conf->assoc) { @@ -180,6 +182,8 @@ static void ath9k_htc_set_opmode(struct ath9k_htc_priv *priv) priv->ah->opmode = NL80211_IFTYPE_ADHOC; else if (priv->num_ap_vif) priv->ah->opmode = NL80211_IFTYPE_AP; + else if (priv->num_mbss_vif) + priv->ah->opmode = NL80211_IFTYPE_MESH_POINT; else priv->ah->opmode = NL80211_IFTYPE_STATION; @@ -1052,6 +1056,9 @@ static int ath9k_htc_add_interface(struct ieee80211_hw *hw, case NL80211_IFTYPE_AP: hvif.opmode = HTC_M_HOSTAP; break; + case NL80211_IFTYPE_MESH_POINT: + hvif.opmode = HTC_M_WDS; /* close enough */ + break; default: ath_err(common, "Interface type %d not yet supported\n", vif->type); @@ -1084,6 +1091,7 @@ static int ath9k_htc_add_interface(struct ieee80211_hw *hw, INC_VIF(priv, vif->type); if ((vif->type == NL80211_IFTYPE_AP) || + (vif->type == NL80211_IFTYPE_MESH_POINT) || (vif->type == NL80211_IFTYPE_ADHOC)) ath9k_htc_assign_bslot(priv, vif); @@ -1134,6 +1142,7 @@ static void ath9k_htc_remove_interface(struct ieee80211_hw *hw, DEC_VIF(priv, vif->type); if ((vif->type == NL80211_IFTYPE_AP) || + vif->type == NL80211_IFTYPE_MESH_POINT || (vif->type == NL80211_IFTYPE_ADHOC)) ath9k_htc_remove_bslot(priv, vif); @@ -1525,9 +1534,10 @@ static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw, if ((changed & BSS_CHANGED_BEACON_ENABLED) && !bss_conf->enable_beacon) { /* * Disable SWBA interrupt only if there are no - * AP/IBSS interfaces. + * concurrent AP/mesh or IBSS interfaces. */ - if ((priv->num_ap_vif <= 1) || priv->num_ibss_vif) { + if ((priv->num_ap_vif + priv->num_mbss_vif <= 1) || + priv->num_ibss_vif) { ath_dbg(common, CONFIG, "Beacon disabled for BSS: %pM\n", bss_conf->bssid); @@ -1538,12 +1548,15 @@ static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw, if (changed & BSS_CHANGED_BEACON_INT) { /* - * Reset the HW TSF for the first AP interface. + * Reset the HW TSF for the first AP or mesh interface. */ - if ((priv->ah->opmode == NL80211_IFTYPE_AP) && - (priv->nvifs == 1) && - (priv->num_ap_vif == 1) && - (vif->type == NL80211_IFTYPE_AP)) { + if (priv->nvifs == 1 && + ((priv->ah->opmode == NL80211_IFTYPE_AP && + vif->type == NL80211_IFTYPE_AP && + priv->num_ap_vif == 1) || + (priv->ah->opmode == NL80211_IFTYPE_MESH_POINT && + vif->type == NL80211_IFTYPE_MESH_POINT && + priv->num_mbss_vif == 1))) { set_bit(OP_TSF_RESET, &priv->op_flags); } ath_dbg(common, CONFIG, diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c index 6bd0e92ea2aa..e602c9519709 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c @@ -887,7 +887,7 @@ u32 ath9k_htc_calcrxfilter(struct ath9k_htc_priv *priv) if (priv->rxfilter & FIF_PSPOLL) rfilt |= ATH9K_RX_FILTER_PSPOLL; - if (priv->nvifs > 1) + if (priv->nvifs > 1 || priv->rxfilter & FIF_OTHER_BSS) rfilt |= ATH9K_RX_FILTER_MCAST_BCAST_ALL; return rfilt; -- cgit v1.2.3 From bd4a85ee8cd6c80d844d0fc52108aebb9b1120c1 Mon Sep 17 00:00:00 2001 From: Javier Cardona Date: Wed, 8 May 2013 10:16:47 -0700 Subject: ath9k_htc: Claim support for mgmt frame protection Advertise support for management frame protection in hardware. Signed-off-by: Javier Cardona Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/htc_drv_init.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index 6eb41990f608..59f64367e8ca 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -722,6 +722,7 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv, IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_PS_NULLFUNC_STACK | IEEE80211_HW_REPORTS_TX_ACK_STATUS | + IEEE80211_HW_MFP_CAPABLE | IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING; hw->wiphy->interface_modes = -- cgit v1.2.3 From 2664d6665f45ef582583d14929dba1170829042e Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Wed, 8 May 2013 10:16:48 -0700 Subject: ath9k: use ap style beaconing for mesh Chun-Yeow and Javier Lopez contributed these changes to make mesh mode use the more similar AP beaconing mode and queue parameters. Should improve PS performance, interface concurrency (AP modes can coexist), and beacon interval stability. AR9271 (ath9k_htc) mesh interfaces also need to be in AP operating mode. Signed-off-by: Thomas Pedersen Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/beacon.c | 8 +++++--- drivers/net/wireless/ath/ath9k/hw.c | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index 2ff570f7f8ff..fd1eebab8647 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -39,7 +39,8 @@ static void ath9k_beaconq_config(struct ath_softc *sc) ath9k_hw_get_txq_props(ah, sc->beacon.beaconq, &qi); - if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) { + if (sc->sc_ah->opmode == NL80211_IFTYPE_AP || + sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT) { /* Always burst out beacon and CAB traffic. */ qi.tqi_aifs = 1; qi.tqi_cwmin = 0; @@ -273,7 +274,8 @@ static int ath9k_beacon_choose_slot(struct ath_softc *sc) u64 tsf; int slot; - if (sc->sc_ah->opmode != NL80211_IFTYPE_AP) { + if (sc->sc_ah->opmode != NL80211_IFTYPE_AP && + sc->sc_ah->opmode != NL80211_IFTYPE_MESH_POINT) { ath_dbg(common, BEACON, "slot 0, tsf: %llu\n", ath9k_hw_gettsf64(sc->sc_ah)); return 0; @@ -765,10 +767,10 @@ void ath9k_set_beacon(struct ath_softc *sc) switch (sc->sc_ah->opmode) { case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_MESH_POINT: ath9k_beacon_config_ap(sc, cur_conf); break; case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_MESH_POINT: ath9k_beacon_config_adhoc(sc, cur_conf); break; case NL80211_IFTYPE_STATION: diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 7f25da8444fe..a263ccc00a47 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -1245,10 +1245,10 @@ static void ath9k_hw_set_operating_mode(struct ath_hw *ah, int opmode) switch (opmode) { case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_MESH_POINT: set |= AR_STA_ID1_ADHOC; REG_SET_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION); break; + case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_AP: set |= AR_STA_ID1_STA_AP; /* fall through */ @@ -2246,12 +2246,12 @@ void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period) switch (ah->opmode) { case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_MESH_POINT: REG_SET_BIT(ah, AR_TXCFG, AR_TXCFG_ADHOC_BEACON_ATIM_TX_POLICY); REG_WRITE(ah, AR_NEXT_NDP_TIMER, next_beacon + TU_TO_USEC(ah->atim_window ? ah->atim_window : 1)); flags |= AR_NDP_TIMER_EN; + case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_AP: REG_WRITE(ah, AR_NEXT_TBTT_TIMER, next_beacon); REG_WRITE(ah, AR_NEXT_DMA_BEACON_ALERT, next_beacon - -- cgit v1.2.3 From 7e594444448f221cff66a9309cbfd17430e463a0 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Sun, 12 May 2013 14:43:32 +0300 Subject: wil6210: 'length' in Tx/Rx descriptors is little endian Hardware uses little endian for the Tx/Rx descriptors field 'length', do appropriate conversions Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/debugfs.c | 8 +++++++- drivers/net/wireless/ath/wil6210/txrx.c | 33 +++++++++++++++++++----------- drivers/net/wireless/ath/wil6210/txrx.h | 4 ++-- 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index 727b1f53e6ad..e8308ec30970 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -418,9 +418,15 @@ static int wil_txdesc_debugfs_show(struct seq_file *s, void *data) if (skb) { unsigned char printbuf[16 * 3 + 2]; int i = 0; - int len = skb_headlen(skb); + int len = le16_to_cpu(d->dma.length); void *p = skb->data; + if (len != skb_headlen(skb)) { + seq_printf(s, "!!! len: desc = %d skb = %d\n", + len, skb_headlen(skb)); + len = min_t(int, len, skb_headlen(skb)); + } + seq_printf(s, " len = %d\n", len); while (i < len) { diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 797024507c71..6a20f0a18622 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -106,19 +106,21 @@ static void wil_vring_free(struct wil6210_priv *wil, struct vring *vring, size_t sz = vring->size * sizeof(vring->va[0]); while (!wil_vring_is_empty(vring)) { + u16 dmalen; if (tx) { volatile struct vring_tx_desc *d = &vring->va[vring->swtail].tx; dma_addr_t pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32); struct sk_buff *skb = vring->ctx[vring->swtail]; + dmalen = le16_to_cpu(d->dma.length); if (skb) { - dma_unmap_single(dev, pa, d->dma.length, + dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE); dev_kfree_skb_any(skb); vring->ctx[vring->swtail] = NULL; } else { - dma_unmap_page(dev, pa, d->dma.length, + dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE); } vring->swtail = wil_vring_next_tail(vring); @@ -128,8 +130,8 @@ static void wil_vring_free(struct wil6210_priv *wil, struct vring *vring, dma_addr_t pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32); struct sk_buff *skb = vring->ctx[vring->swhead]; - dma_unmap_single(dev, pa, d->dma.length, - DMA_FROM_DEVICE); + dmalen = le16_to_cpu(d->dma.length); + dma_unmap_single(dev, pa, dmalen, DMA_FROM_DEVICE); kfree_skb(skb); wil_vring_advance_head(vring, 1); } @@ -175,7 +177,7 @@ static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring, /* b11 don't care */ /* error don't care */ d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */ - d->dma.length = sz; + d->dma.length = cpu_to_le16(sz); vring->ctx[i] = skb; return 0; @@ -326,6 +328,7 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, struct sk_buff *skb; dma_addr_t pa; unsigned int sz = RX_BUF_LEN; + u16 dmalen; u8 ftype; u8 ds_bits; @@ -343,10 +346,11 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32); skb = vring->ctx[vring->swhead]; dma_unmap_single(dev, pa, sz, DMA_FROM_DEVICE); - skb_trim(skb, d->dma.length); d1 = wil_skb_rxdesc(skb); *d1 = *d; + dmalen = le16_to_cpu(d1->dma.length); + skb_trim(skb, dmalen); wil->stats.last_mcs_rx = wil_rxdesc_mcs(d1); @@ -610,7 +614,7 @@ static int wil_tx_desc_map(volatile struct vring_tx_desc *d, d->dma.b11 = 0/*14 | BIT(7)*/; d->dma.error = 0; d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */ - d->dma.length = len; + d->dma.length = cpu_to_le16((u16)len); d->dma.d0 = 0; d->mac.d[0] = 0; d->mac.d[1] = 0; @@ -705,14 +709,17 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, /* unmap what we have mapped */ /* Note: increment @f to operate with positive index */ for (f++; f > 0; f--) { + u16 dmalen; + i = (swhead + f) % vring->size; d = &(vring->va[i].tx); d->dma.status = TX_DMA_STATUS_DU; pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32); + dmalen = le16_to_cpu(d->dma.length); if (vring->ctx[i]) - dma_unmap_single(dev, pa, d->dma.length, DMA_TO_DEVICE); + dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE); else - dma_unmap_page(dev, pa, d->dma.length, DMA_TO_DEVICE); + dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE); } return -EINVAL; @@ -792,15 +799,17 @@ void wil_tx_complete(struct wil6210_priv *wil, int ringid) struct vring_tx_desc dd, *d = ⅆ dma_addr_t pa; struct sk_buff *skb; + u16 dmalen; dd = *d1; if (!(d->dma.status & TX_DMA_STATUS_DU)) break; + dmalen = le16_to_cpu(d->dma.length); wil_dbg_txrx(wil, "Tx[%3d] : %d bytes, status 0x%02x err 0x%02x\n", - vring->swtail, d->dma.length, d->dma.status, + vring->swtail, dmalen, d->dma.status, d->dma.error); wil_hex_dump_txrx("TxC ", DUMP_PREFIX_NONE, 32, 4, (const void *)d, sizeof(*d), false); @@ -815,11 +824,11 @@ void wil_tx_complete(struct wil6210_priv *wil, int ringid) ndev->stats.tx_errors++; } - dma_unmap_single(dev, pa, d->dma.length, DMA_TO_DEVICE); + dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE); dev_kfree_skb_any(skb); vring->ctx[vring->swtail] = NULL; } else { - dma_unmap_page(dev, pa, d->dma.length, DMA_TO_DEVICE); + dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE); } d->dma.addr_low = 0; d->dma.addr_high = 0; diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h index adef12fb2aee..a40aa0bb8b94 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.h +++ b/drivers/net/wireless/ath/wil6210/txrx.h @@ -222,7 +222,7 @@ struct vring_tx_dma { u8 b11; /* 0..6: mac_length; 7:ip_version */ u8 error; /* 0..2: err; 3..7: reserved; */ u8 status; /* 0: used; 1..7; reserved */ - u16 length; + __le16 length; } __packed; /* @@ -321,7 +321,7 @@ struct vring_rx_dma { u8 b11; u8 error; u8 status; - u16 length; + __le16 length; } __packed; struct vring_tx_desc { -- cgit v1.2.3 From e270045b569cc7030abd29857f3a4e7906524ec0 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Sun, 12 May 2013 14:43:33 +0300 Subject: wil6210: Sanity check for reported DMA length If Rx descriptor contains garbage, it is possible to access memory beyond allocated buffer. Check this condition and drop Rx if reported length is unreasonable large Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/txrx.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 6a20f0a18622..92f18215014c 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -349,7 +349,13 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, d1 = wil_skb_rxdesc(skb); *d1 = *d; + wil_vring_advance_head(vring, 1); dmalen = le16_to_cpu(d1->dma.length); + if (dmalen > sz) { + wil_err(wil, "Rx size too large: %d bytes!\n", dmalen); + kfree(skb); + return NULL; + } skb_trim(skb, dmalen); wil->stats.last_mcs_rx = wil_rxdesc_mcs(d1); @@ -362,8 +368,6 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, wil_hex_dump_txrx("Rx ", DUMP_PREFIX_NONE, 32, 4, (const void *)d, sizeof(*d), false); - wil_vring_advance_head(vring, 1); - /* no extra checks if in sniffer mode */ if (ndev->type != ARPHRD_ETHER) return skb; -- cgit v1.2.3 From c0d37713607de33412a3bbf8ad699d2934427696 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Sun, 12 May 2013 14:43:34 +0300 Subject: wil6210: debug dump packet content right after DMA Move packet dump to the earliest location where it is known to have valid data. Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/txrx.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 92f18215014c..eb90be070e50 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -358,6 +358,10 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, } skb_trim(skb, dmalen); + wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1, + skb->data, skb_headlen(skb), false); + + wil->stats.last_mcs_rx = wil_rxdesc_mcs(d1); /* use radiotap header only if required */ @@ -472,8 +476,6 @@ void wil_rx_handle(struct wil6210_priv *wil) } wil_dbg_txrx(wil, "%s()\n", __func__); while (NULL != (skb = wil_vring_reap_rx(wil, v))) { - wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1, - skb->data, skb_headlen(skb), false); if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) { skb->dev = ndev; -- cgit v1.2.3 From 98658095623109bdace46f21bece028c904fb900 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Sun, 12 May 2013 14:43:35 +0300 Subject: wil6210: trace support Trace the following: - WMI cmd/event - log events - interrupts - Tx/Rx Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/Kconfig | 12 ++ drivers/net/wireless/ath/wil6210/Makefile | 21 ++- drivers/net/wireless/ath/wil6210/debug.c | 69 ++++++++ drivers/net/wireless/ath/wil6210/interrupt.c | 6 + drivers/net/wireless/ath/wil6210/trace.c | 20 +++ drivers/net/wireless/ath/wil6210/trace.h | 235 +++++++++++++++++++++++++++ drivers/net/wireless/ath/wil6210/txrx.c | 10 +- drivers/net/wireless/ath/wil6210/wil6210.h | 10 +- drivers/net/wireless/ath/wil6210/wmi.c | 8 +- 9 files changed, 376 insertions(+), 15 deletions(-) create mode 100644 drivers/net/wireless/ath/wil6210/debug.c create mode 100644 drivers/net/wireless/ath/wil6210/trace.c create mode 100644 drivers/net/wireless/ath/wil6210/trace.h diff --git a/drivers/net/wireless/ath/wil6210/Kconfig b/drivers/net/wireless/ath/wil6210/Kconfig index bac3d98a0cfb..5644ac54facc 100644 --- a/drivers/net/wireless/ath/wil6210/Kconfig +++ b/drivers/net/wireless/ath/wil6210/Kconfig @@ -27,3 +27,15 @@ config WIL6210_ISR_COR self-clear when accessed for debug purposes, it makes such monitoring impossible. Say y unless you debug interrupts + +config ATH6KL_TRACING + bool "wil6210 tracing support" + depends on WIL6210 + depends on EVENT_TRACING + default y + ---help--- + Say Y here to enable tracepoints for the wil6210 driver + using the kernel tracing infrastructure. Select this + option if you are interested in debugging the driver. + + If unsure, say Y to make it easier to debug problems. diff --git a/drivers/net/wireless/ath/wil6210/Makefile b/drivers/net/wireless/ath/wil6210/Makefile index d288eea0a26a..f891d514d881 100644 --- a/drivers/net/wireless/ath/wil6210/Makefile +++ b/drivers/net/wireless/ath/wil6210/Makefile @@ -1,15 +1,20 @@ obj-$(CONFIG_WIL6210) += wil6210.o -wil6210-objs := main.o -wil6210-objs += netdev.o -wil6210-objs += cfg80211.o -wil6210-objs += pcie_bus.o -wil6210-objs += debugfs.o -wil6210-objs += wmi.o -wil6210-objs += interrupt.o -wil6210-objs += txrx.o +wil6210-y := main.o +wil6210-y += netdev.o +wil6210-y += cfg80211.o +wil6210-y += pcie_bus.o +wil6210-y += debugfs.o +wil6210-y += wmi.o +wil6210-y += interrupt.o +wil6210-y += txrx.o +wil6210-y += debug.o +wil6210-$(CONFIG_WIL6210_TRACING) += trace.o ifeq (, $(findstring -W,$(EXTRA_CFLAGS))) subdir-ccflags-y += -Werror endif +# for tracing framework to find trace.h +CFLAGS_trace.o := -I$(src) + subdir-ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/net/wireless/ath/wil6210/debug.c b/drivers/net/wireless/ath/wil6210/debug.c new file mode 100644 index 000000000000..9eeabf4a5879 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/debug.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "wil6210.h" +#include "trace.h" + +int wil_err(struct wil6210_priv *wil, const char *fmt, ...) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct va_format vaf = { + .fmt = fmt, + }; + va_list args; + int ret; + + va_start(args, fmt); + vaf.va = &args; + ret = netdev_err(ndev, "%pV", &vaf); + trace_wil6210_log_err(&vaf); + va_end(args); + + return ret; +} + +int wil_info(struct wil6210_priv *wil, const char *fmt, ...) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct va_format vaf = { + .fmt = fmt, + }; + va_list args; + int ret; + + va_start(args, fmt); + vaf.va = &args; + ret = netdev_info(ndev, "%pV", &vaf); + trace_wil6210_log_info(&vaf); + va_end(args); + + return ret; +} + +int wil_dbg_trace(struct wil6210_priv *wil, const char *fmt, ...) +{ + struct va_format vaf = { + .fmt = fmt, + }; + va_list args; + + va_start(args, fmt); + vaf.va = &args; + trace_wil6210_log_dbg(&vaf); + va_end(args); + + return 0; +} diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c index e3c1e7684f9c..5fe985cd3dfd 100644 --- a/drivers/net/wireless/ath/wil6210/interrupt.c +++ b/drivers/net/wireless/ath/wil6210/interrupt.c @@ -17,6 +17,7 @@ #include #include "wil6210.h" +#include "trace.h" /** * Theory of operation: @@ -168,6 +169,7 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie) HOSTADDR(RGF_DMA_EP_RX_ICR) + offsetof(struct RGF_ICR, ICR)); + trace_wil6210_irq_rx(isr); wil_dbg_irq(wil, "ISR RX 0x%08x\n", isr); if (!isr) { @@ -198,6 +200,7 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie) HOSTADDR(RGF_DMA_EP_TX_ICR) + offsetof(struct RGF_ICR, ICR)); + trace_wil6210_irq_tx(isr); wil_dbg_irq(wil, "ISR TX 0x%08x\n", isr); if (!isr) { @@ -256,6 +259,7 @@ static irqreturn_t wil6210_irq_misc(int irq, void *cookie) HOSTADDR(RGF_DMA_EP_MISC_ICR) + offsetof(struct RGF_ICR, ICR)); + trace_wil6210_irq_misc(isr); wil_dbg_irq(wil, "ISR MISC 0x%08x\n", isr); if (!isr) { @@ -301,6 +305,7 @@ static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie) struct wil6210_priv *wil = cookie; u32 isr = wil->isr_misc; + trace_wil6210_irq_misc_thread(isr); wil_dbg_irq(wil, "Thread ISR MISC 0x%08x\n", isr); if (isr & ISR_MISC_FW_ERROR) { @@ -408,6 +413,7 @@ static irqreturn_t wil6210_hardirq(int irq, void *cookie) if (wil6210_debug_irq_mask(wil, pseudo_cause)) return IRQ_NONE; + trace_wil6210_irq_pseudo(pseudo_cause); wil_dbg_irq(wil, "Pseudo IRQ 0x%08x\n", pseudo_cause); wil6210_mask_irq_pseudo(wil); diff --git a/drivers/net/wireless/ath/wil6210/trace.c b/drivers/net/wireless/ath/wil6210/trace.c new file mode 100644 index 000000000000..cd2534b9c5aa --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/trace.c @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#define CREATE_TRACE_POINTS +#include "trace.h" diff --git a/drivers/net/wireless/ath/wil6210/trace.h b/drivers/net/wireless/ath/wil6210/trace.h new file mode 100644 index 000000000000..eff1239be53a --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/trace.h @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM wil6210 +#if !defined(WIL6210_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define WIL6210_TRACE_H + +#include +#include "wil6210.h" +#include "txrx.h" + +/* create empty functions when tracing is disabled */ +#if !defined(CONFIG_WIL6210_TRACING) || defined(__CHECKER__) + +#undef TRACE_EVENT +#define TRACE_EVENT(name, proto, ...) \ +static inline void trace_ ## name(proto) {} +#undef DECLARE_EVENT_CLASS +#define DECLARE_EVENT_CLASS(...) +#undef DEFINE_EVENT +#define DEFINE_EVENT(evt_class, name, proto, ...) \ +static inline void trace_ ## name(proto) {} +#endif /* !CONFIG_WIL6210_TRACING || defined(__CHECKER__) */ + +DECLARE_EVENT_CLASS(wil6210_wmi, + TP_PROTO(u16 id, void *buf, u16 buf_len), + + TP_ARGS(id, buf, buf_len), + + TP_STRUCT__entry( + __field(u16, id) + __field(u16, buf_len) + __dynamic_array(u8, buf, buf_len) + ), + + TP_fast_assign( + __entry->id = id; + __entry->buf_len = buf_len; + memcpy(__get_dynamic_array(buf), buf, buf_len); + ), + + TP_printk( + "id 0x%04x len %d", + __entry->id, __entry->buf_len + ) +); + +DEFINE_EVENT(wil6210_wmi, wil6210_wmi_cmd, + TP_PROTO(u16 id, void *buf, u16 buf_len), + TP_ARGS(id, buf, buf_len) +); + +DEFINE_EVENT(wil6210_wmi, wil6210_wmi_event, + TP_PROTO(u16 id, void *buf, u16 buf_len), + TP_ARGS(id, buf, buf_len) +); + +#define WIL6210_MSG_MAX (200) + +DECLARE_EVENT_CLASS(wil6210_log_event, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf), + TP_STRUCT__entry( + __dynamic_array(char, msg, WIL6210_MSG_MAX) + ), + TP_fast_assign( + WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg), + WIL6210_MSG_MAX, + vaf->fmt, + *vaf->va) >= WIL6210_MSG_MAX); + ), + TP_printk("%s", __get_str(msg)) +); + +DEFINE_EVENT(wil6210_log_event, wil6210_log_err, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +DEFINE_EVENT(wil6210_log_event, wil6210_log_info, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +DEFINE_EVENT(wil6210_log_event, wil6210_log_dbg, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +#define wil_pseudo_irq_cause(x) __print_flags(x, "|", \ + {BIT_DMA_PSEUDO_CAUSE_RX, "Rx" }, \ + {BIT_DMA_PSEUDO_CAUSE_TX, "Tx" }, \ + {BIT_DMA_PSEUDO_CAUSE_MISC, "Misc" }) + +TRACE_EVENT(wil6210_irq_pseudo, + TP_PROTO(u32 x), + TP_ARGS(x), + TP_STRUCT__entry( + __field(u32, x) + ), + TP_fast_assign( + __entry->x = x; + ), + TP_printk("cause 0x%08x : %s", __entry->x, + wil_pseudo_irq_cause(__entry->x)) +); + +DECLARE_EVENT_CLASS(wil6210_irq, + TP_PROTO(u32 x), + TP_ARGS(x), + TP_STRUCT__entry( + __field(u32, x) + ), + TP_fast_assign( + __entry->x = x; + ), + TP_printk("cause 0x%08x", __entry->x) +); + +DEFINE_EVENT(wil6210_irq, wil6210_irq_rx, + TP_PROTO(u32 x), + TP_ARGS(x) +); + +DEFINE_EVENT(wil6210_irq, wil6210_irq_tx, + TP_PROTO(u32 x), + TP_ARGS(x) +); + +DEFINE_EVENT(wil6210_irq, wil6210_irq_misc, + TP_PROTO(u32 x), + TP_ARGS(x) +); + +DEFINE_EVENT(wil6210_irq, wil6210_irq_misc_thread, + TP_PROTO(u32 x), + TP_ARGS(x) +); + +TRACE_EVENT(wil6210_rx, + TP_PROTO(u16 index, struct vring_rx_desc *d), + TP_ARGS(index, d), + TP_STRUCT__entry( + __field(u16, index) + __field(unsigned int, len) + __field(u8, mid) + __field(u8, cid) + __field(u8, tid) + __field(u8, type) + __field(u8, subtype) + __field(u16, seq) + __field(u8, mcs) + ), + TP_fast_assign( + __entry->index = index; + __entry->len = d->dma.length; + __entry->mid = wil_rxdesc_mid(d); + __entry->cid = wil_rxdesc_cid(d); + __entry->tid = wil_rxdesc_tid(d); + __entry->type = wil_rxdesc_ftype(d); + __entry->subtype = wil_rxdesc_subtype(d); + __entry->seq = wil_rxdesc_seq(d); + __entry->mcs = wil_rxdesc_mcs(d); + ), + TP_printk("index %d len %d mid %d cid %d tid %d mcs %d seq 0x%03x" + " type 0x%1x subtype 0x%1x", __entry->index, __entry->len, + __entry->mid, __entry->cid, __entry->tid, __entry->mcs, + __entry->seq, __entry->type, __entry->subtype) +); + +TRACE_EVENT(wil6210_tx, + TP_PROTO(u8 vring, u16 index, unsigned int len, u8 frags), + TP_ARGS(vring, index, len, frags), + TP_STRUCT__entry( + __field(u8, vring) + __field(u8, frags) + __field(u16, index) + __field(unsigned int, len) + ), + TP_fast_assign( + __entry->vring = vring; + __entry->frags = frags; + __entry->index = index; + __entry->len = len; + ), + TP_printk("vring %d index %d len %d frags %d", + __entry->vring, __entry->index, __entry->len, __entry->frags) +); + +TRACE_EVENT(wil6210_tx_done, + TP_PROTO(u8 vring, u16 index, unsigned int len, u8 err), + TP_ARGS(vring, index, len, err), + TP_STRUCT__entry( + __field(u8, vring) + __field(u8, err) + __field(u16, index) + __field(unsigned int, len) + ), + TP_fast_assign( + __entry->vring = vring; + __entry->index = index; + __entry->len = len; + __entry->err = err; + ), + TP_printk("vring %d index %d len %d err 0x%02x", + __entry->vring, __entry->index, __entry->len, + __entry->err) +); + +#endif /* WIL6210_TRACE_H || TRACE_HEADER_MULTI_READ*/ + +#if defined(CONFIG_WIL6210_TRACING) && !defined(__CHECKER__) +/* we don't want to use include/trace/events */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE trace + +/* This part must be outside protection */ +#include +#endif /* defined(CONFIG_WIL6210_TRACING) && !defined(__CHECKER__) */ diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index eb90be070e50..dc183d573c08 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -22,6 +22,7 @@ #include "wil6210.h" #include "wmi.h" #include "txrx.h" +#include "trace.h" static bool rtap_include_phy_info; module_param(rtap_include_phy_info, bool, S_IRUGO); @@ -368,9 +369,11 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, if (ndev->type == ARPHRD_IEEE80211_RADIOTAP) wil_rx_add_radiotap_header(wil, skb); - wil_dbg_txrx(wil, "Rx[%3d] : %d bytes\n", vring->swhead, d->dma.length); + trace_wil6210_rx(vring->swhead, d1); + wil_dbg_txrx(wil, "Rx[%3d] : %d bytes\n", vring->swhead, + d1->dma.length); wil_hex_dump_txrx("Rx ", DUMP_PREFIX_NONE, 32, 4, - (const void *)d, sizeof(*d), false); + (const void *)d1, sizeof(*d1), false); /* no extra checks if in sniffer mode */ if (ndev->type != ARPHRD_ETHER) @@ -703,6 +706,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, /* advance swhead */ wil_vring_advance_head(vring, nr_frags + 1); wil_dbg_txrx(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead); + trace_wil6210_tx(vring_index, swhead, skb->len, nr_frags); iowrite32(vring->swhead, wil->csr + HOSTADDR(vring->hwtail)); /* hold reference to skb * to prevent skb release before accounting @@ -813,6 +817,8 @@ void wil_tx_complete(struct wil6210_priv *wil, int ringid) break; dmalen = le16_to_cpu(d->dma.length); + trace_wil6210_tx_done(ringid, vring->swtail, dmalen, + d->dma.error); wil_dbg_txrx(wil, "Tx[%3d] : %d bytes, status 0x%02x err 0x%02x\n", vring->swtail, dmalen, d->dma.status, diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 8f76ecd8a7e5..484446e66c5a 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -267,9 +267,13 @@ struct wil6210_priv { #define wil_to_ndev(i) (wil_to_wdev(i)->netdev) #define ndev_to_wil(n) (wdev_to_wil(n->ieee80211_ptr)) -#define wil_dbg(wil, fmt, arg...) netdev_dbg(wil_to_ndev(wil), fmt, ##arg) -#define wil_info(wil, fmt, arg...) netdev_info(wil_to_ndev(wil), fmt, ##arg) -#define wil_err(wil, fmt, arg...) netdev_err(wil_to_ndev(wil), fmt, ##arg) +int wil_dbg_trace(struct wil6210_priv *wil, const char *fmt, ...); +int wil_err(struct wil6210_priv *wil, const char *fmt, ...); +int wil_info(struct wil6210_priv *wil, const char *fmt, ...); +#define wil_dbg(wil, fmt, arg...) do { \ + netdev_dbg(wil_to_ndev(wil), fmt, ##arg); \ + wil_dbg_trace(wil, fmt, ##arg); \ +} while (0) #define wil_dbg_irq(wil, fmt, arg...) wil_dbg(wil, "DBG[ IRQ]" fmt, ##arg) #define wil_dbg_txrx(wil, fmt, arg...) wil_dbg(wil, "DBG[TXRX]" fmt, ##arg) diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 45b04e383f9a..5e01f4ee4de7 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -20,6 +20,7 @@ #include "wil6210.h" #include "txrx.h" #include "wmi.h" +#include "trace.h" /** * WMI event receiving - theory of operations @@ -246,6 +247,8 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) iowrite32(r->head = next_head, wil->csr + HOST_MBOX + offsetof(struct wil6210_mbox_ctl, tx.head)); + trace_wil6210_wmi_cmd(cmdid, buf, len); + /* interrupt to FW */ iowrite32(SW_INT_MBOX, wil->csr + HOST_SW_INT); @@ -635,8 +638,9 @@ void wmi_recv_cmd(struct wil6210_priv *wil) hdr.flags); if ((hdr.type == WIL_MBOX_HDR_TYPE_WMI) && (len >= sizeof(struct wil6210_mbox_hdr_wmi))) { - wil_dbg_wmi(wil, "WMI event 0x%04x\n", - evt->event.wmi.id); + u16 id = le16_to_cpu(evt->event.wmi.id); + wil_dbg_wmi(wil, "WMI event 0x%04x\n", id); + trace_wil6210_wmi_event(id, &evt->event.wmi, len); } wil_hex_dump_wmi("evt ", DUMP_PREFIX_OFFSET, 16, 1, &evt->event.hdr, sizeof(hdr) + len, true); -- cgit v1.2.3 From e0287c4ab87905dd4a2e45cf791f8e0a87fe602e Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Sun, 12 May 2013 14:43:36 +0300 Subject: wil6210: use NAPI Introduce NAPI for Rx and Tx completion. This fixes packet reordering that happens when Rx handled right in the IRQ: netif_rx puts packet in 'percpu' queue, then network stack fetches packets from 'percpu' queues for processing, with different pattern of queue switching. As result, network stack see packets in different order. This causes hard to understand TCP throughput degradation in about 30min Complete polling if only one packet was processed - this eliminates empty polls that would be otherwise done at the end of each burst Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/interrupt.c | 23 +++++------- drivers/net/wireless/ath/wil6210/main.c | 6 ++++ drivers/net/wireless/ath/wil6210/netdev.c | 54 ++++++++++++++++++++++++++++ drivers/net/wireless/ath/wil6210/txrx.c | 23 +++++++----- drivers/net/wireless/ath/wil6210/wil6210.h | 16 ++++++--- 5 files changed, 94 insertions(+), 28 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c index 5fe985cd3dfd..8205d3e4ab66 100644 --- a/drivers/net/wireless/ath/wil6210/interrupt.c +++ b/drivers/net/wireless/ath/wil6210/interrupt.c @@ -104,14 +104,14 @@ static void wil6210_mask_irq_pseudo(struct wil6210_priv *wil) clear_bit(wil_status_irqen, &wil->status); } -static void wil6210_unmask_irq_tx(struct wil6210_priv *wil) +void wil6210_unmask_irq_tx(struct wil6210_priv *wil) { iowrite32(WIL6210_IMC_TX, wil->csr + HOSTADDR(RGF_DMA_EP_TX_ICR) + offsetof(struct RGF_ICR, IMC)); } -static void wil6210_unmask_irq_rx(struct wil6210_priv *wil) +void wil6210_unmask_irq_rx(struct wil6210_priv *wil) { iowrite32(WIL6210_IMC_RX, wil->csr + HOSTADDR(RGF_DMA_EP_RX_ICR) + @@ -182,13 +182,14 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie) if (isr & BIT_DMA_EP_RX_ICR_RX_DONE) { wil_dbg_irq(wil, "RX done\n"); isr &= ~BIT_DMA_EP_RX_ICR_RX_DONE; - wil_rx_handle(wil); + wil_dbg_txrx(wil, "NAPI schedule\n"); + napi_schedule(&wil->napi_rx); } if (isr) wil_err(wil, "un-handled RX ISR bits 0x%08x\n", isr); - wil6210_unmask_irq_rx(wil); + /* Rx IRQ will be enabled when NAPI processing finished */ return IRQ_HANDLED; } @@ -211,23 +212,17 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie) wil6210_mask_irq_tx(wil); if (isr & BIT_DMA_EP_TX_ICR_TX_DONE) { - uint i; wil_dbg_irq(wil, "TX done\n"); + napi_schedule(&wil->napi_tx); isr &= ~BIT_DMA_EP_TX_ICR_TX_DONE; - for (i = 0; i < 24; i++) { - u32 mask = BIT_DMA_EP_TX_ICR_TX_DONE_N(i); - if (isr & mask) { - isr &= ~mask; - wil_dbg_irq(wil, "TX done(%i)\n", i); - wil_tx_complete(wil, i); - } - } + /* clear also all VRING interrupts */ + isr &= ~(BIT(25) - 1UL); } if (isr) wil_err(wil, "un-handled TX ISR bits 0x%08x\n", isr); - wil6210_unmask_irq_tx(wil); + /* Tx IRQ will be enabled when NAPI processing finished */ return IRQ_HANDLED; } diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index a0478e2f6868..ea49c8a18e15 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -365,6 +365,9 @@ static int __wil_up(struct wil6210_priv *wil) /* Rx VRING. After MAC and beacon */ wil_rx_init(wil); + napi_enable(&wil->napi_rx); + napi_enable(&wil->napi_tx); + return 0; } @@ -381,6 +384,9 @@ int wil_up(struct wil6210_priv *wil) static int __wil_down(struct wil6210_priv *wil) { + napi_disable(&wil->napi_rx); + napi_disable(&wil->napi_tx); + if (wil->scan_request) { cfg80211_scan_done(wil->scan_request, true); wil->scan_request = NULL; diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c index 098a8ec6b841..29dd1e58cb17 100644 --- a/drivers/net/wireless/ath/wil6210/netdev.c +++ b/drivers/net/wireless/ath/wil6210/netdev.c @@ -40,6 +40,55 @@ static const struct net_device_ops wil_netdev_ops = { .ndo_validate_addr = eth_validate_addr, }; +static int wil6210_netdev_poll_rx(struct napi_struct *napi, int budget) +{ + struct wil6210_priv *wil = container_of(napi, struct wil6210_priv, + napi_rx); + int quota = budget; + int done; + + wil_rx_handle(wil, "a); + done = budget - quota; + + if (done <= 1) { /* burst ends - only one packet processed */ + napi_complete(napi); + wil6210_unmask_irq_rx(wil); + wil_dbg_txrx(wil, "NAPI RX complete\n"); + } + + wil_dbg_txrx(wil, "NAPI RX poll(%d) done %d\n", budget, done); + + return done; +} + +static int wil6210_netdev_poll_tx(struct napi_struct *napi, int budget) +{ + struct wil6210_priv *wil = container_of(napi, struct wil6210_priv, + napi_tx); + int tx_done = 0; + uint i; + + /* always process ALL Tx complete, regardless budget - it is fast */ + for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) { + struct vring *vring = &wil->vring_tx[i]; + + if (!vring->va) + continue; + + tx_done += wil_tx_complete(wil, i); + } + + if (tx_done <= 1) { /* burst ends - only one packet processed */ + napi_complete(napi); + wil6210_unmask_irq_tx(wil); + wil_dbg_txrx(wil, "NAPI TX complete\n"); + } + + wil_dbg_txrx(wil, "NAPI TX poll(%d) done %d\n", budget, tx_done); + + return min(tx_done, budget); +} + void *wil_if_alloc(struct device *dev, void __iomem *csr) { struct net_device *ndev; @@ -81,6 +130,11 @@ void *wil_if_alloc(struct device *dev, void __iomem *csr) SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy)); wdev->netdev = ndev; + netif_napi_add(ndev, &wil->napi_rx, wil6210_netdev_poll_rx, + WIL6210_NAPI_BUDGET); + netif_napi_add(ndev, &wil->napi_tx, wil6210_netdev_poll_tx, + WIL6210_NAPI_BUDGET); + wil_link_off(wil); return wil; diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index dc183d573c08..bab50117383a 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -440,6 +440,7 @@ static int wil_rx_refill(struct wil6210_priv *wil, int count) /* * Pass Rx packet to the netif. Update statistics. + * Called in softirq context (NAPI poll). */ static void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev) { @@ -448,10 +449,7 @@ static void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev) skb_orphan(skb); - if (in_interrupt()) - rc = netif_rx(skb); - else - rc = netif_rx_ni(skb); + rc = netif_receive_skb(skb); if (likely(rc == NET_RX_SUCCESS)) { ndev->stats.rx_packets++; @@ -465,9 +463,9 @@ static void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev) /** * Proceed all completed skb's from Rx VRING * - * Safe to call from IRQ + * Safe to call from NAPI poll, i.e. softirq with interrupts enabled */ -void wil_rx_handle(struct wil6210_priv *wil) +void wil_rx_handle(struct wil6210_priv *wil, int *quota) { struct net_device *ndev = wil_to_ndev(wil); struct vring *v = &wil->vring_rx; @@ -478,7 +476,8 @@ void wil_rx_handle(struct wil6210_priv *wil) return; } wil_dbg_txrx(wil, "%s()\n", __func__); - while (NULL != (skb = wil_vring_reap_rx(wil, v))) { + while ((*quota > 0) && (NULL != (skb = wil_vring_reap_rx(wil, v)))) { + (*quota)--; if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) { skb->dev = ndev; @@ -788,17 +787,20 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) /** * Clean up transmitted skb's from the Tx VRING * + * Return number of descriptors cleared + * * Safe to call from IRQ */ -void wil_tx_complete(struct wil6210_priv *wil, int ringid) +int wil_tx_complete(struct wil6210_priv *wil, int ringid) { struct net_device *ndev = wil_to_ndev(wil); struct device *dev = wil_to_dev(wil); struct vring *vring = &wil->vring_tx[ringid]; + int done = 0; if (!vring->va) { wil_err(wil, "Tx irq[%d]: vring not initialized\n", ringid); - return; + return 0; } wil_dbg_txrx(wil, "%s(%d)\n", __func__, ringid); @@ -847,7 +849,10 @@ void wil_tx_complete(struct wil6210_priv *wil, int ringid) d->dma.length = 0; d->dma.status = TX_DMA_STATUS_DU; vring->swtail = wil_vring_next_tail(vring); + done++; } if (wil_vring_avail_tx(vring) > vring->size/4) netif_tx_wake_all_queues(wil_to_ndev(wil)); + + return done; } diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 484446e66c5a..2e3c26e1c975 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -34,9 +34,11 @@ static inline u32 WIL_GET_BITS(u32 x, int b0, int b1) #define WIL6210_MEM_SIZE (2*1024*1024UL) -#define WIL6210_RX_RING_SIZE (128) -#define WIL6210_TX_RING_SIZE (128) -#define WIL6210_MAX_TX_RINGS (24) +#define WIL6210_RX_RING_SIZE (128) +#define WIL6210_TX_RING_SIZE (128) +#define WIL6210_MAX_TX_RINGS (24) /* HW limit */ +#define WIL6210_MAX_CID (8) /* HW limit */ +#define WIL6210_NAPI_BUDGET (16) /* arbitrary */ /* Hardware definitions begin */ @@ -239,6 +241,8 @@ struct wil6210_priv { * - consumed in thread by wmi_event_worker */ spinlock_t wmi_ev_lock; + struct napi_struct napi_rx; + struct napi_struct napi_tx; /* DMA related */ struct vring vring_rx; struct vring vring_tx[WIL6210_MAX_TX_RINGS]; @@ -360,10 +364,12 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, void wil_vring_fini_tx(struct wil6210_priv *wil, int id); netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev); -void wil_tx_complete(struct wil6210_priv *wil, int ringid); +int wil_tx_complete(struct wil6210_priv *wil, int ringid); +void wil6210_unmask_irq_tx(struct wil6210_priv *wil); /* RX API */ -void wil_rx_handle(struct wil6210_priv *wil); +void wil_rx_handle(struct wil6210_priv *wil, int *quota); +void wil6210_unmask_irq_rx(struct wil6210_priv *wil); int wil_iftype_nl2wmi(enum nl80211_iftype type); -- cgit v1.2.3 From 68ada71e33e7b79f2ce7c6fff5564750a225234a Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Sun, 12 May 2013 14:43:37 +0300 Subject: wil6210: fix remaining use of non-cached copy of tx/rx descriptors - Introduce common code for Tx/Rx descriptor physical address set/parse - Fix endianness for address fields - consistent descriptor naming: '_d' for non-cached memory, 'd' for cached copy - wil_tx_desc_map now modify cached copy, no need for 'volatile' Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/txrx.c | 106 ++++++++++++++++++-------------- drivers/net/wireless/ath/wil6210/txrx.h | 28 +++++++-- 2 files changed, 84 insertions(+), 50 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index bab50117383a..561314830f21 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -90,8 +90,8 @@ static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring) * we can use any */ for (i = 0; i < vring->size; i++) { - volatile struct vring_tx_desc *d = &(vring->va[i].tx); - d->dma.status = TX_DMA_STATUS_DU; + volatile struct vring_tx_desc *_d = &(vring->va[i].tx); + _d->dma.status = TX_DMA_STATUS_DU; } wil_dbg_misc(wil, "vring[%d] 0x%p:0x%016llx 0x%p\n", vring->size, @@ -107,14 +107,19 @@ static void wil_vring_free(struct wil6210_priv *wil, struct vring *vring, size_t sz = vring->size * sizeof(vring->va[0]); while (!wil_vring_is_empty(vring)) { + dma_addr_t pa; + struct sk_buff *skb; u16 dmalen; + if (tx) { - volatile struct vring_tx_desc *d = + struct vring_tx_desc dd, *d = ⅆ + volatile struct vring_tx_desc *_d = &vring->va[vring->swtail].tx; - dma_addr_t pa = d->dma.addr_low | - ((u64)d->dma.addr_high << 32); - struct sk_buff *skb = vring->ctx[vring->swtail]; + + *d = *_d; + pa = wil_desc_addr(&d->dma.addr); dmalen = le16_to_cpu(d->dma.length); + skb = vring->ctx[vring->swtail]; if (skb) { dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE); @@ -126,12 +131,14 @@ static void wil_vring_free(struct wil6210_priv *wil, struct vring *vring, } vring->swtail = wil_vring_next_tail(vring); } else { /* rx */ - volatile struct vring_rx_desc *d = + struct vring_rx_desc dd, *d = ⅆ + volatile struct vring_rx_desc *_d = &vring->va[vring->swtail].rx; - dma_addr_t pa = d->dma.addr_low | - ((u64)d->dma.addr_high << 32); - struct sk_buff *skb = vring->ctx[vring->swhead]; + + *d = *_d; + pa = wil_desc_addr(&d->dma.addr); dmalen = le16_to_cpu(d->dma.length); + skb = vring->ctx[vring->swhead]; dma_unmap_single(dev, pa, dmalen, DMA_FROM_DEVICE); kfree_skb(skb); wil_vring_advance_head(vring, 1); @@ -154,7 +161,8 @@ static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring, { struct device *dev = wil_to_dev(wil); unsigned int sz = RX_BUF_LEN; - volatile struct vring_rx_desc *d = &(vring->va[i].rx); + struct vring_rx_desc dd, *d = ⅆ + volatile struct vring_rx_desc *_d = &(vring->va[i].rx); dma_addr_t pa; /* TODO align */ @@ -172,13 +180,13 @@ static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring, } d->dma.d0 = BIT(9) | RX_DMA_D0_CMD_DMA_IT; - d->dma.addr_low = lower_32_bits(pa); - d->dma.addr_high = (u16)upper_32_bits(pa); + wil_desc_addr_set(&d->dma.addr, pa); /* ip_length don't care */ /* b11 don't care */ /* error don't care */ d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */ d->dma.length = cpu_to_le16(sz); + *_d = *d; vring->ctx[i] = skb; return 0; @@ -324,8 +332,8 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, { struct device *dev = wil_to_dev(wil); struct net_device *ndev = wil_to_ndev(wil); - volatile struct vring_rx_desc *d; - struct vring_rx_desc *d1; + volatile struct vring_rx_desc *_d; + struct vring_rx_desc *d; struct sk_buff *skb; dma_addr_t pa; unsigned int sz = RX_BUF_LEN; @@ -338,20 +346,27 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, if (wil_vring_is_empty(vring)) return NULL; - d = &(vring->va[vring->swhead].rx); - if (!(d->dma.status & RX_DMA_STATUS_DU)) { + _d = &(vring->va[vring->swhead].rx); + if (!(_d->dma.status & RX_DMA_STATUS_DU)) { /* it is not error, we just reached end of Rx done area */ return NULL; } - pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32); skb = vring->ctx[vring->swhead]; + d = wil_skb_rxdesc(skb); + *d = *_d; + pa = wil_desc_addr(&d->dma.addr); + vring->ctx[vring->swhead] = NULL; + wil_vring_advance_head(vring, 1); + dma_unmap_single(dev, pa, sz, DMA_FROM_DEVICE); + dmalen = le16_to_cpu(d->dma.length); + + trace_wil6210_rx(vring->swhead, d); + wil_dbg_txrx(wil, "Rx[%3d] : %d bytes\n", vring->swhead, dmalen); + wil_hex_dump_txrx("Rx ", DUMP_PREFIX_NONE, 32, 4, + (const void *)d, sizeof(*d), false); - d1 = wil_skb_rxdesc(skb); - *d1 = *d; - wil_vring_advance_head(vring, 1); - dmalen = le16_to_cpu(d1->dma.length); if (dmalen > sz) { wil_err(wil, "Rx size too large: %d bytes!\n", dmalen); kfree(skb); @@ -363,18 +378,12 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, skb->data, skb_headlen(skb), false); - wil->stats.last_mcs_rx = wil_rxdesc_mcs(d1); + wil->stats.last_mcs_rx = wil_rxdesc_mcs(d); /* use radiotap header only if required */ if (ndev->type == ARPHRD_IEEE80211_RADIOTAP) wil_rx_add_radiotap_header(wil, skb); - trace_wil6210_rx(vring->swhead, d1); - wil_dbg_txrx(wil, "Rx[%3d] : %d bytes\n", vring->swhead, - d1->dma.length); - wil_hex_dump_txrx("Rx ", DUMP_PREFIX_NONE, 32, 4, - (const void *)d1, sizeof(*d1), false); - /* no extra checks if in sniffer mode */ if (ndev->type != ARPHRD_ETHER) return skb; @@ -383,7 +392,7 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, * Driver should recognize it by frame type, that is found * in Rx descriptor. If type is not data, it is 802.11 frame as is */ - ftype = wil_rxdesc_ftype(d1) << 2; + ftype = wil_rxdesc_ftype(d) << 2; if (ftype != IEEE80211_FTYPE_DATA) { wil_dbg_txrx(wil, "Non-data frame ftype 0x%08x\n", ftype); /* TODO: process it */ @@ -398,7 +407,7 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, return NULL; } - ds_bits = wil_rxdesc_ds_bits(d1); + ds_bits = wil_rxdesc_ds_bits(d); if (ds_bits == 1) { /* * HW bug - in ToDS mode, i.e. Rx on AP side, @@ -612,11 +621,9 @@ static struct vring *wil_find_tx_vring(struct wil6210_priv *wil, return NULL; } -static int wil_tx_desc_map(volatile struct vring_tx_desc *d, - dma_addr_t pa, u32 len) +static int wil_tx_desc_map(struct vring_tx_desc *d, dma_addr_t pa, u32 len) { - d->dma.addr_low = lower_32_bits(pa); - d->dma.addr_high = (u16)upper_32_bits(pa); + wil_desc_addr_set(&d->dma.addr, pa); d->dma.ip_length = 0; /* 0..6: mac_length; 7:ip_version 0-IP6 1-IP4*/ d->dma.b11 = 0/*14 | BIT(7)*/; @@ -642,7 +649,8 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, struct sk_buff *skb) { struct device *dev = wil_to_dev(wil); - volatile struct vring_tx_desc *d; + struct vring_tx_desc dd, *d = ⅆ + volatile struct vring_tx_desc *_d; u32 swhead = vring->swhead; int avail = wil_vring_avail_tx(vring); int nr_frags = skb_shinfo(skb)->nr_frags; @@ -660,7 +668,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, 1 + nr_frags); return -ENOMEM; } - d = &(vring->va[i].tx); + _d = &(vring->va[i].tx); /* FIXME FW can accept only unicast frames for the peer */ memcpy(skb->data, wil->dst_addr[vring_index], ETH_ALEN); @@ -679,25 +687,30 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, wil_tx_desc_map(d, pa, skb_headlen(skb)); d->mac.d[2] |= ((nr_frags + 1) << MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS); + if (nr_frags) + *_d = *d; + /* middle segments */ for (f = 0; f < nr_frags; f++) { const struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[f]; int len = skb_frag_size(frag); i = (swhead + f + 1) % vring->size; - d = &(vring->va[i].tx); + _d = &(vring->va[i].tx); pa = skb_frag_dma_map(dev, frag, 0, skb_frag_size(frag), DMA_TO_DEVICE); if (unlikely(dma_mapping_error(dev, pa))) goto dma_error; wil_tx_desc_map(d, pa, len); vring->ctx[i] = NULL; + *_d = *d; } /* for the last seg only */ d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_EOP_POS); d->dma.d0 |= BIT(9); /* BUG: undocumented bit */ d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS); d->dma.d0 |= (vring_index << DMA_CFG_DESC_TX_0_QID_POS); + *_d = *d; wil_hex_dump_txrx("Tx ", DUMP_PREFIX_NONE, 32, 4, (const void *)d, sizeof(*d), false); @@ -721,9 +734,10 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, u16 dmalen; i = (swhead + f) % vring->size; - d = &(vring->va[i].tx); - d->dma.status = TX_DMA_STATUS_DU; - pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32); + _d = &(vring->va[i].tx); + *d = *_d; + _d->dma.status = TX_DMA_STATUS_DU; + pa = wil_desc_addr(&d->dma.addr); dmalen = le16_to_cpu(d->dma.length); if (vring->ctx[i]) dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE); @@ -806,14 +820,14 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid) wil_dbg_txrx(wil, "%s(%d)\n", __func__, ringid); while (!wil_vring_is_empty(vring)) { - volatile struct vring_tx_desc *d1 = + volatile struct vring_tx_desc *_d = &vring->va[vring->swtail].tx; struct vring_tx_desc dd, *d = ⅆ dma_addr_t pa; struct sk_buff *skb; u16 dmalen; - dd = *d1; + *d = *_d; if (!(d->dma.status & TX_DMA_STATUS_DU)) break; @@ -828,7 +842,7 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid) wil_hex_dump_txrx("TxC ", DUMP_PREFIX_NONE, 32, 4, (const void *)d, sizeof(*d), false); - pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32); + pa = wil_desc_addr(&d->dma.addr); skb = vring->ctx[vring->swtail]; if (skb) { if (d->dma.error == 0) { @@ -844,8 +858,8 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid) } else { dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE); } - d->dma.addr_low = 0; - d->dma.addr_high = 0; + d->dma.addr.addr_low = 0; + d->dma.addr.addr_high = 0; d->dma.length = 0; d->dma.status = TX_DMA_STATUS_DU; vring->swtail = wil_vring_next_tail(vring); diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h index a40aa0bb8b94..23c0781cdb93 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.h +++ b/drivers/net/wireless/ath/wil6210/txrx.h @@ -27,6 +27,28 @@ #define WIL6210_RTAP_SIZE (128) /* Tx/Rx path */ + +/* + * Common representation of physical address in Vring + */ +struct vring_dma_addr { + __le32 addr_low; + __le16 addr_high; +} __packed; + +static inline dma_addr_t wil_desc_addr(struct vring_dma_addr *addr) +{ + return le32_to_cpu(addr->addr_low) | + ((u64)le16_to_cpu(addr->addr_high) << 32); +} + +static inline void wil_desc_addr_set(struct vring_dma_addr *addr, + dma_addr_t pa) +{ + addr->addr_low = cpu_to_le32(lower_32_bits(pa)); + addr->addr_high = cpu_to_le16((u16)upper_32_bits(pa)); +} + /* * Tx descriptor - MAC part * [dword 0] @@ -216,8 +238,7 @@ struct vring_tx_mac { struct vring_tx_dma { u32 d0; - u32 addr_low; - u16 addr_high; + struct vring_dma_addr addr; u8 ip_length; u8 b11; /* 0..6: mac_length; 7:ip_version */ u8 error; /* 0..2: err; 3..7: reserved; */ @@ -315,8 +336,7 @@ struct vring_rx_mac { struct vring_rx_dma { u32 d0; - u32 addr_low; - u16 addr_high; + struct vring_dma_addr addr; u8 ip_length; u8 b11; u8 error; -- cgit v1.2.3 From 224c9c2366efe7f32496c1b7ef82f9b6424817dd Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Sun, 12 May 2013 14:43:38 +0300 Subject: wil6210: do not stop Tx queue on packet drop Packet drop may be caused by various flows, like disconnect while Tx packets was queued; this should not lead to stopping of the Tx queue, or all Tx get stalled. Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/txrx.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 561314830f21..082f76bdd5e5 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -791,7 +791,6 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) break; /* goto drop; */ } drop: - netif_tx_stop_all_queues(ndev); ndev->stats.tx_dropped++; dev_kfree_skb_any(skb); -- cgit v1.2.3 From 5179ed7c1bc1d4599f9643f9711c090648602f4b Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Mon, 13 May 2013 22:07:51 +0200 Subject: bcma: don't hardcode SPROM length MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass it as an argument to all functions. This is requires as newer SPROM revisions have different lengths. Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/bcma/sprom.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/bcma/sprom.c b/drivers/bcma/sprom.c index 8934298a638d..5386cddba43e 100644 --- a/drivers/bcma/sprom.c +++ b/drivers/bcma/sprom.c @@ -72,12 +72,12 @@ fail: * R/W ops. **************************************************/ -static void bcma_sprom_read(struct bcma_bus *bus, u16 offset, u16 *sprom) +static void bcma_sprom_read(struct bcma_bus *bus, u16 offset, u16 *sprom, + size_t words) { int i; - for (i = 0; i < SSB_SPROMSIZE_WORDS_R4; i++) - sprom[i] = bcma_read16(bus->drv_cc.core, - offset + (i * 2)); + for (i = 0; i < words; i++) + sprom[i] = bcma_read16(bus->drv_cc.core, offset + (i * 2)); } /************************************************** @@ -124,29 +124,29 @@ static inline u8 bcma_crc8(u8 crc, u8 data) return t[crc ^ data]; } -static u8 bcma_sprom_crc(const u16 *sprom) +static u8 bcma_sprom_crc(const u16 *sprom, size_t words) { int word; u8 crc = 0xFF; - for (word = 0; word < SSB_SPROMSIZE_WORDS_R4 - 1; word++) { + for (word = 0; word < words - 1; word++) { crc = bcma_crc8(crc, sprom[word] & 0x00FF); crc = bcma_crc8(crc, (sprom[word] & 0xFF00) >> 8); } - crc = bcma_crc8(crc, sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & 0x00FF); + crc = bcma_crc8(crc, sprom[words - 1] & 0x00FF); crc ^= 0xFF; return crc; } -static int bcma_sprom_check_crc(const u16 *sprom) +static int bcma_sprom_check_crc(const u16 *sprom, size_t words) { u8 crc; u8 expected_crc; u16 tmp; - crc = bcma_sprom_crc(sprom); - tmp = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & SSB_SPROM_REVISION_CRC; + crc = bcma_sprom_crc(sprom, words); + tmp = sprom[words - 1] & SSB_SPROM_REVISION_CRC; expected_crc = tmp >> SSB_SPROM_REVISION_CRC_SHIFT; if (crc != expected_crc) return -EPROTO; @@ -154,16 +154,16 @@ static int bcma_sprom_check_crc(const u16 *sprom) return 0; } -static int bcma_sprom_valid(const u16 *sprom) +static int bcma_sprom_valid(const u16 *sprom, size_t words) { u16 revision; int err; - err = bcma_sprom_check_crc(sprom); + err = bcma_sprom_check_crc(sprom, words); if (err) return err; - revision = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & SSB_SPROM_REVISION_REV; + revision = sprom[words - 1] & SSB_SPROM_REVISION_REV; if (revision != 8 && revision != 9) { pr_err("Unsupported SPROM revision: %d\n", revision); return -ENOENT; @@ -589,13 +589,13 @@ int bcma_sprom_get(struct bcma_bus *bus) bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, false); bcma_debug(bus, "SPROM offset 0x%x\n", offset); - bcma_sprom_read(bus, offset, sprom); + bcma_sprom_read(bus, offset, sprom, SSB_SPROMSIZE_WORDS_R4); if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4331 || bus->chipinfo.id == BCMA_CHIP_ID_BCM43431) bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, true); - err = bcma_sprom_valid(sprom); + err = bcma_sprom_valid(sprom, SSB_SPROMSIZE_WORDS_R4); if (err) { bcma_warn(bus, "invalid sprom read from the PCIe card, try to use fallback sprom\n"); err = bcma_fill_sprom_with_fallback(bus, &bus->sprom); -- cgit v1.2.3 From 92eb164258cfce173060d3fe7aaffa2afaf735d7 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Mon, 13 May 2013 22:07:52 +0200 Subject: bcma: prepare for supporting more SPROM sizes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/bcma/sprom.c | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/drivers/bcma/sprom.c b/drivers/bcma/sprom.c index 5386cddba43e..c96f71c183cd 100644 --- a/drivers/bcma/sprom.c +++ b/drivers/bcma/sprom.c @@ -502,7 +502,6 @@ static bool bcma_sprom_onchip_available(struct bcma_bus *bus) case BCMA_CHIP_ID_BCM4331: present = chip_status & BCMA_CC_CHIPST_4331_OTP_PRESENT; break; - case BCMA_CHIP_ID_BCM43224: case BCMA_CHIP_ID_BCM43225: /* for these chips OTP is always available */ @@ -550,7 +549,8 @@ int bcma_sprom_get(struct bcma_bus *bus) { u16 offset = BCMA_CC_SPROM; u16 *sprom; - int err = 0; + size_t sprom_sizes[] = { SSB_SPROMSIZE_WORDS_R4, }; + int i, err = 0; if (!bus->drv_cc.core) return -EOPNOTSUPP; @@ -579,32 +579,37 @@ int bcma_sprom_get(struct bcma_bus *bus) } } - sprom = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16), - GFP_KERNEL); - if (!sprom) - return -ENOMEM; - if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4331 || bus->chipinfo.id == BCMA_CHIP_ID_BCM43431) bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, false); bcma_debug(bus, "SPROM offset 0x%x\n", offset); - bcma_sprom_read(bus, offset, sprom, SSB_SPROMSIZE_WORDS_R4); + for (i = 0; i < ARRAY_SIZE(sprom_sizes); i++) { + size_t words = sprom_sizes[i]; + + sprom = kcalloc(words, sizeof(u16), GFP_KERNEL); + if (!sprom) + return -ENOMEM; + + bcma_sprom_read(bus, offset, sprom, words); + err = bcma_sprom_valid(sprom, words); + if (!err) + break; + + kfree(sprom); + } if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4331 || bus->chipinfo.id == BCMA_CHIP_ID_BCM43431) bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, true); - err = bcma_sprom_valid(sprom, SSB_SPROMSIZE_WORDS_R4); if (err) { - bcma_warn(bus, "invalid sprom read from the PCIe card, try to use fallback sprom\n"); + bcma_warn(bus, "Invalid SPROM read from the PCIe card, trying to use fallback SPROM\n"); err = bcma_fill_sprom_with_fallback(bus, &bus->sprom); - goto out; + } else { + bcma_sprom_extract_r8(bus, sprom); + kfree(sprom); } - bcma_sprom_extract_r8(bus, sprom); - -out: - kfree(sprom); return err; } -- cgit v1.2.3 From 78e578c5b43c4f274305075c68d055c8d6141fd5 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Mon, 13 May 2013 22:07:53 +0200 Subject: bcma: support SPROM rev 10 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is pretty much the same as rev 9, there are just 2 extra fields we know about, but are not used/stored yet anyway. Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/bcma/sprom.c | 16 +++++++++------- include/linux/ssb/ssb_regs.h | 1 + 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/bcma/sprom.c b/drivers/bcma/sprom.c index c96f71c183cd..de15b4f4b237 100644 --- a/drivers/bcma/sprom.c +++ b/drivers/bcma/sprom.c @@ -154,7 +154,8 @@ static int bcma_sprom_check_crc(const u16 *sprom, size_t words) return 0; } -static int bcma_sprom_valid(const u16 *sprom, size_t words) +static int bcma_sprom_valid(struct bcma_bus *bus, const u16 *sprom, + size_t words) { u16 revision; int err; @@ -164,11 +165,14 @@ static int bcma_sprom_valid(const u16 *sprom, size_t words) return err; revision = sprom[words - 1] & SSB_SPROM_REVISION_REV; - if (revision != 8 && revision != 9) { + if (revision != 8 && revision != 9 && revision != 10) { pr_err("Unsupported SPROM revision: %d\n", revision); return -ENOENT; } + bus->sprom.revision = revision; + bcma_debug(bus, "Found SPROM revision %d\n", revision); + return 0; } @@ -208,9 +212,6 @@ static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom) BUILD_BUG_ON(ARRAY_SIZE(pwr_info_offset) != ARRAY_SIZE(bus->sprom.core_pwr_info)); - bus->sprom.revision = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & - SSB_SPROM_REVISION_REV; - for (i = 0; i < 3; i++) { v = sprom[SPOFF(SSB_SPROM8_IL0MAC) + i]; *(((__be16 *)bus->sprom.il0mac) + i) = cpu_to_be16(v); @@ -549,7 +550,8 @@ int bcma_sprom_get(struct bcma_bus *bus) { u16 offset = BCMA_CC_SPROM; u16 *sprom; - size_t sprom_sizes[] = { SSB_SPROMSIZE_WORDS_R4, }; + size_t sprom_sizes[] = { SSB_SPROMSIZE_WORDS_R4, + SSB_SPROMSIZE_WORDS_R10, }; int i, err = 0; if (!bus->drv_cc.core) @@ -592,7 +594,7 @@ int bcma_sprom_get(struct bcma_bus *bus) return -ENOMEM; bcma_sprom_read(bus, offset, sprom, words); - err = bcma_sprom_valid(sprom, words); + err = bcma_sprom_valid(bus, sprom, words); if (!err) break; diff --git a/include/linux/ssb/ssb_regs.h b/include/linux/ssb/ssb_regs.h index 3a7256955b10..f9f931c89e3e 100644 --- a/include/linux/ssb/ssb_regs.h +++ b/include/linux/ssb/ssb_regs.h @@ -172,6 +172,7 @@ #define SSB_SPROMSIZE_WORDS_R4 220 #define SSB_SPROMSIZE_BYTES_R123 (SSB_SPROMSIZE_WORDS_R123 * sizeof(u16)) #define SSB_SPROMSIZE_BYTES_R4 (SSB_SPROMSIZE_WORDS_R4 * sizeof(u16)) +#define SSB_SPROMSIZE_WORDS_R10 230 #define SSB_SPROM_BASE1 0x1000 #define SSB_SPROM_BASE31 0x0800 #define SSB_SPROM_REVISION 0x007E -- cgit v1.2.3 From c56ecf5a7f0f670c0c445a3d2d0bd6c3eda7015c Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Fri, 17 May 2013 17:50:18 -0700 Subject: mwifiex: rename mwifiex_free_adapter() routine in init.c We have two different static routines with name mwifiex_free_adapter(). The routine in main.c actually frees the adapter structure. We will rename other routine in init.c to mwifiex_adapter_cleanup() to avoid confusion. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/init.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c index 9f44fda19db9..58e151edfa09 100644 --- a/drivers/net/wireless/mwifiex/init.c +++ b/drivers/net/wireless/mwifiex/init.c @@ -447,17 +447,16 @@ static void mwifiex_free_lock_list(struct mwifiex_adapter *adapter) } /* - * This function frees the adapter structure. + * This function performs cleanup for adapter structure. * - * The freeing operation is done recursively, by canceling all - * pending commands, freeing the member buffers previously - * allocated (command buffers, scan table buffer, sleep confirm - * command buffer), stopping the timers and calling the cleanup - * routines for every interface, before the actual adapter - * structure is freed. + * The cleanup is done recursively, by canceling all pending + * commands, freeing the member buffers previously allocated + * (command buffers, scan table buffer, sleep confirm command + * buffer), stopping the timers and calling the cleanup routines + * for every interface. */ static void -mwifiex_free_adapter(struct mwifiex_adapter *adapter) +mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter) { if (!adapter) { pr_err("%s: adapter is NULL\n", __func__); @@ -733,8 +732,7 @@ mwifiex_shutdown_drv(struct mwifiex_adapter *adapter) } } - /* Free adapter structure */ - mwifiex_free_adapter(adapter); + mwifiex_adapter_cleanup(adapter); spin_unlock_irqrestore(&adapter->mwifiex_lock, flags); -- cgit v1.2.3 From 06041118ef0908b9cae7657a7b734699bcf61a6c Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Fri, 17 May 2013 17:50:19 -0700 Subject: mwifiex: scan delay timer cleanup in unload path Return from scan delay timer routine if surprise_removed flag is true. Also, cancel the timer in unload path. This fixes a crash when scan delay timer accesses structures that have been freed already. Tested with "iwlist mlan0 scan & sleep 1; rmmod mwifiex_sdio" Reported-by: Daniel Drake Tested-by: Daniel Drake Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/init.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c index 58e151edfa09..71bbf1204685 100644 --- a/drivers/net/wireless/mwifiex/init.c +++ b/drivers/net/wireless/mwifiex/init.c @@ -59,6 +59,9 @@ static void scan_delay_timer_fn(unsigned long data) struct cmd_ctrl_node *cmd_node, *tmp_node; unsigned long flags; + if (adapter->surprise_removed) + return; + if (adapter->scan_delay_cnt == MWIFIEX_MAX_SCAN_DELAY_CNT) { /* * Abort scan operation by cancelling all pending scan @@ -458,11 +461,18 @@ static void mwifiex_free_lock_list(struct mwifiex_adapter *adapter) static void mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter) { + int i; + if (!adapter) { pr_err("%s: adapter is NULL\n", __func__); return; } + for (i = 0; i < adapter->priv_num; i++) { + if (adapter->priv[i]) + del_timer_sync(&adapter->priv[i]->scan_delay_timer); + } + mwifiex_cancel_all_pending_cmd(adapter); /* Free lock variables */ -- cgit v1.2.3 From 75ab753d7704f0bd34e09d5e4081bc73fdddd775 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Fri, 17 May 2013 17:50:20 -0700 Subject: mwifiex: remove global user_scan_cfg variable As the variable is used only for preparation of internal scan commands, we don't need to keep it allocated until the entire scan completes. We will define it as a local variable and free immediately after it's use. New flag 'scan_aborting' is added to handle race between mwifiex_close() and scan handler. Previously user_scan_cfg pointer used to take care of this. This patch fixes a memory leak in mwifiex_cfg80211_scan after running "iwlist mlan0 scan & sleep 1; rmmod mwifiex_sdio". Reported-by: Daniel Drake Tested-by: Daniel Drake Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/cfg80211.c | 31 +++++++++++++++++-------------- drivers/net/wireless/mwifiex/init.c | 20 +++++++------------- drivers/net/wireless/mwifiex/main.c | 1 + drivers/net/wireless/mwifiex/main.h | 2 +- drivers/net/wireless/mwifiex/scan.c | 22 ++++++++-------------- 5 files changed, 34 insertions(+), 42 deletions(-) diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index d3c8ece980d8..fcd293c64118 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -1859,6 +1859,7 @@ mwifiex_cfg80211_scan(struct wiphy *wiphy, int i, offset, ret; struct ieee80211_channel *chan; struct ieee_types_header *ie; + struct mwifiex_user_scan_cfg *user_scan_cfg; wiphy_dbg(wiphy, "info: received scan request on %s\n", dev->name); @@ -1869,20 +1870,22 @@ mwifiex_cfg80211_scan(struct wiphy *wiphy, return -EBUSY; } - if (priv->user_scan_cfg) { + /* Block scan request if scan operation or scan cleanup when interface + * is disabled is in process + */ + if (priv->scan_request || priv->scan_aborting) { dev_err(priv->adapter->dev, "cmd: Scan already in process..\n"); return -EBUSY; } - priv->user_scan_cfg = kzalloc(sizeof(struct mwifiex_user_scan_cfg), - GFP_KERNEL); - if (!priv->user_scan_cfg) + user_scan_cfg = kzalloc(sizeof(*user_scan_cfg), GFP_KERNEL); + if (!user_scan_cfg) return -ENOMEM; priv->scan_request = request; - priv->user_scan_cfg->num_ssids = request->n_ssids; - priv->user_scan_cfg->ssid_list = request->ssids; + user_scan_cfg->num_ssids = request->n_ssids; + user_scan_cfg->ssid_list = request->ssids; if (request->ie && request->ie_len) { offset = 0; @@ -1902,25 +1905,25 @@ mwifiex_cfg80211_scan(struct wiphy *wiphy, for (i = 0; i < min_t(u32, request->n_channels, MWIFIEX_USER_SCAN_CHAN_MAX); i++) { chan = request->channels[i]; - priv->user_scan_cfg->chan_list[i].chan_number = chan->hw_value; - priv->user_scan_cfg->chan_list[i].radio_type = chan->band; + user_scan_cfg->chan_list[i].chan_number = chan->hw_value; + user_scan_cfg->chan_list[i].radio_type = chan->band; if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) - priv->user_scan_cfg->chan_list[i].scan_type = + user_scan_cfg->chan_list[i].scan_type = MWIFIEX_SCAN_TYPE_PASSIVE; else - priv->user_scan_cfg->chan_list[i].scan_type = + user_scan_cfg->chan_list[i].scan_type = MWIFIEX_SCAN_TYPE_ACTIVE; - priv->user_scan_cfg->chan_list[i].scan_time = 0; + user_scan_cfg->chan_list[i].scan_time = 0; } - ret = mwifiex_scan_networks(priv, priv->user_scan_cfg); + ret = mwifiex_scan_networks(priv, user_scan_cfg); + kfree(user_scan_cfg); if (ret) { dev_err(priv->adapter->dev, "scan failed: %d\n", ret); + priv->scan_aborting = false; priv->scan_request = NULL; - kfree(priv->user_scan_cfg); - priv->user_scan_cfg = NULL; return ret; } diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c index 71bbf1204685..1343725f8c4c 100644 --- a/drivers/net/wireless/mwifiex/init.c +++ b/drivers/net/wireless/mwifiex/init.c @@ -81,19 +81,13 @@ static void scan_delay_timer_fn(unsigned long data) adapter->empty_tx_q_cnt = 0; spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); - if (priv->user_scan_cfg) { - if (priv->scan_request) { - dev_dbg(priv->adapter->dev, - "info: aborting scan\n"); - cfg80211_scan_done(priv->scan_request, 1); - priv->scan_request = NULL; - } else { - dev_dbg(priv->adapter->dev, - "info: scan already aborted\n"); - } - - kfree(priv->user_scan_cfg); - priv->user_scan_cfg = NULL; + if (priv->scan_request) { + dev_dbg(adapter->dev, "info: aborting scan\n"); + cfg80211_scan_done(priv->scan_request, 1); + priv->scan_request = NULL; + } else { + priv->scan_aborting = false; + dev_dbg(adapter->dev, "info: scan already aborted\n"); } goto done; } diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index 121443a0f2a1..eb8518618622 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -436,6 +436,7 @@ mwifiex_close(struct net_device *dev) dev_dbg(priv->adapter->dev, "aborting scan on ndo_stop\n"); cfg80211_scan_done(priv->scan_request, 1); priv->scan_request = NULL; + priv->scan_aborting = true; } return 0; diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 4ef67fca06d3..81251d91491c 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -492,7 +492,6 @@ struct mwifiex_private { struct semaphore async_sem; u8 report_scan_result; struct cfg80211_scan_request *scan_request; - struct mwifiex_user_scan_cfg *user_scan_cfg; u8 cfg_bssid[6]; struct wps wps; u8 scan_block; @@ -510,6 +509,7 @@ struct mwifiex_private { u8 ap_11ac_enabled; u32 mgmt_frame_mask; struct mwifiex_roc_cfg roc_cfg; + bool scan_aborting; }; enum mwifiex_ba_status { diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c index 9cf5d8f07df8..7b2566b308c3 100644 --- a/drivers/net/wireless/mwifiex/scan.c +++ b/drivers/net/wireless/mwifiex/scan.c @@ -1784,22 +1784,16 @@ check_next_scan: if (priv->report_scan_result) priv->report_scan_result = false; - if (priv->user_scan_cfg) { - if (priv->scan_request) { - dev_dbg(priv->adapter->dev, - "info: notifying scan done\n"); - cfg80211_scan_done(priv->scan_request, 0); - priv->scan_request = NULL; - } else { - dev_dbg(priv->adapter->dev, - "info: scan already aborted\n"); - } - - kfree(priv->user_scan_cfg); - priv->user_scan_cfg = NULL; + if (priv->scan_request) { + dev_dbg(adapter->dev, "info: notifying scan done\n"); + cfg80211_scan_done(priv->scan_request, 0); + priv->scan_request = NULL; + } else { + priv->scan_aborting = false; + dev_dbg(adapter->dev, "info: scan already aborted\n"); } } else { - if (priv->user_scan_cfg && !priv->scan_request) { + if (priv->scan_aborting && !priv->scan_request) { spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); adapter->scan_delay_cnt = MWIFIEX_MAX_SCAN_DELAY_CNT; -- cgit v1.2.3 From 4c1079e1583958b83d7355ccbba686ad08b67015 Mon Sep 17 00:00:00 2001 From: Bing Zhao Date: Fri, 17 May 2013 17:50:21 -0700 Subject: mwifiex: abort remaining scan commands when association started A full-channel scan is split to multiple scan commands in driver before they are sent to firmware. When each scan result is back the SSID entries are parsed and informed to cfg80211 directly. It's observed that sometimes userspace may initiate association as soon as the target AP is found. During the 4-way handshake firmware may go off-channel to scan the remaining channels. This causes the 4-way handshake to fail. Fix it by checking 'scan_block' flag and aborting the remaining scan in this case. 'scan_block' flag is set after association and before 4-way handshake. It gets cleared after 4-way handshake is completed. Tested-by: Jason Abele Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/scan.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c index 7b2566b308c3..801b6b728379 100644 --- a/drivers/net/wireless/mwifiex/scan.c +++ b/drivers/net/wireless/mwifiex/scan.c @@ -1793,7 +1793,8 @@ check_next_scan: dev_dbg(adapter->dev, "info: scan already aborted\n"); } } else { - if (priv->scan_aborting && !priv->scan_request) { + if ((priv->scan_aborting && !priv->scan_request) || + priv->scan_block) { spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); adapter->scan_delay_cnt = MWIFIEX_MAX_SCAN_DELAY_CNT; -- cgit v1.2.3 From bdd4d6bf59c046e5a3d1ac67ba7b1c9ea540b6e3 Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Fri, 17 May 2013 17:50:22 -0700 Subject: mwifiex: avoid deleting all stations during mwifiex_del_sta_entry() During deleting a station entry from associated sta_list, we are supposed to delete entry only for this particular mac address. This patch is a bug fix wherein we were deleting all entries from list; fix this by removing list_for_each_entry_safe() call. Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/uap_event.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/mwifiex/uap_event.c b/drivers/net/wireless/mwifiex/uap_event.c index 21c640d3b579..77fb533ae10a 100644 --- a/drivers/net/wireless/mwifiex/uap_event.c +++ b/drivers/net/wireless/mwifiex/uap_event.c @@ -107,18 +107,15 @@ mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies, */ static void mwifiex_del_sta_entry(struct mwifiex_private *priv, u8 *mac) { - struct mwifiex_sta_node *node, *tmp; + struct mwifiex_sta_node *node; unsigned long flags; spin_lock_irqsave(&priv->sta_list_spinlock, flags); node = mwifiex_get_sta_entry(priv, mac); if (node) { - list_for_each_entry_safe(node, tmp, &priv->sta_list, - list) { - list_del(&node->list); - kfree(node); - } + list_del(&node->list); + kfree(node); } spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); -- cgit v1.2.3 From 0f9e9b8ba72bc75ee6189d0e86639f7e7a494a30 Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Fri, 17 May 2013 17:50:23 -0700 Subject: mwifiex: add del_station handler This patch adds cfg80211 del_station handler for mwifiex. If bss is not started or there are no stations in associated stations list, no action is taken. If argument received is null/broadcast mac, all stations in associated station list are deauthenticated. Patch also deletes related RxReorder stream and TxBA stream tables for related station. Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/cfg80211.c | 46 ++++++++++++++++++++++++++++++ drivers/net/wireless/mwifiex/cmdevt.c | 1 + drivers/net/wireless/mwifiex/fw.h | 7 +++++ drivers/net/wireless/mwifiex/main.h | 2 ++ drivers/net/wireless/mwifiex/sta_cmdresp.c | 2 ++ drivers/net/wireless/mwifiex/uap_cmd.c | 21 ++++++++++++++ drivers/net/wireless/mwifiex/uap_event.c | 16 +++++++++++ 7 files changed, 95 insertions(+) diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index fcd293c64118..af79daa331d0 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -1231,6 +1231,51 @@ static int mwifiex_cfg80211_change_beacon(struct wiphy *wiphy, return 0; } +/* cfg80211 operation handler for del_station. + * Function deauthenticates station which value is provided in mac parameter. + * If mac is NULL/broadcast, all stations in associated station list are + * deauthenticated. If bss is not started or there are no stations in + * associated stations list, no action is taken. + */ +static int +mwifiex_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev, + u8 *mac) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + struct mwifiex_sta_node *sta_node; + unsigned long flags; + + if (list_empty(&priv->sta_list) || !priv->bss_started) + return 0; + + if (!mac || is_broadcast_ether_addr(mac)) { + wiphy_dbg(wiphy, "%s: NULL/broadcast mac address\n", __func__); + list_for_each_entry(sta_node, &priv->sta_list, list) { + if (mwifiex_send_cmd_sync(priv, + HostCmd_CMD_UAP_STA_DEAUTH, + HostCmd_ACT_GEN_SET, 0, + sta_node->mac_addr)) + return -1; + mwifiex_uap_del_sta_data(priv, sta_node); + } + } else { + wiphy_dbg(wiphy, "%s: mac address %pM\n", __func__, mac); + spin_lock_irqsave(&priv->sta_list_spinlock, flags); + sta_node = mwifiex_get_sta_entry(priv, mac); + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + if (sta_node) { + if (mwifiex_send_cmd_sync(priv, + HostCmd_CMD_UAP_STA_DEAUTH, + HostCmd_ACT_GEN_SET, 0, + sta_node->mac_addr)) + return -1; + mwifiex_uap_del_sta_data(priv, sta_node); + } + } + + return 0; +} + static int mwifiex_cfg80211_set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant) { @@ -2425,6 +2470,7 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = { .change_beacon = mwifiex_cfg80211_change_beacon, .set_cqm_rssi_config = mwifiex_cfg80211_set_cqm_rssi_config, .set_antenna = mwifiex_cfg80211_set_antenna, + .del_station = mwifiex_cfg80211_del_station, #ifdef CONFIG_PM .suspend = mwifiex_cfg80211_suspend, .resume = mwifiex_cfg80211_resume, diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c index 74db0d24a579..4a32f2795c64 100644 --- a/drivers/net/wireless/mwifiex/cmdevt.c +++ b/drivers/net/wireless/mwifiex/cmdevt.c @@ -570,6 +570,7 @@ int mwifiex_send_cmd_async(struct mwifiex_private *priv, uint16_t cmd_no, case HostCmd_CMD_UAP_SYS_CONFIG: case HostCmd_CMD_UAP_BSS_START: case HostCmd_CMD_UAP_BSS_STOP: + case HostCmd_CMD_UAP_STA_DEAUTH: ret = mwifiex_uap_prepare_cmd(priv, cmd_no, cmd_action, cmd_oid, data_buf, cmd_ptr); diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index 1f7578d553ec..d3c1e01b4fc2 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -279,6 +279,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define HostCmd_CMD_UAP_SYS_CONFIG 0x00b0 #define HostCmd_CMD_UAP_BSS_START 0x00b1 #define HostCmd_CMD_UAP_BSS_STOP 0x00b2 +#define HostCmd_CMD_UAP_STA_DEAUTH 0x00b5 #define HostCmd_CMD_11N_CFG 0x00cd #define HostCmd_CMD_11N_ADDBA_REQ 0x00ce #define HostCmd_CMD_11N_ADDBA_RSP 0x00cf @@ -1197,6 +1198,11 @@ struct host_cmd_ds_amsdu_aggr_ctrl { __le16 curr_buf_size; } __packed; +struct host_cmd_ds_sta_deauth { + u8 mac[ETH_ALEN]; + __le16 reason; +} __packed; + struct mwifiex_ie_types_wmm_param_set { struct mwifiex_ie_types_header header; u8 wmm_ie[1]; @@ -1630,6 +1636,7 @@ struct host_cmd_ds_command { struct host_cmd_ds_802_11_eeprom_access eeprom; struct host_cmd_ds_802_11_subsc_evt subsc_evt; struct host_cmd_ds_sys_config uap_sys_config; + struct host_cmd_ds_sta_deauth sta_deauth; struct host_cmd_11ac_vht_cfg vht_cfg; } params; } __packed; diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 81251d91491c..9cf6852f6da3 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -1115,6 +1115,8 @@ int mwifiex_set_mgmt_ies(struct mwifiex_private *priv, struct cfg80211_beacon_data *data); int mwifiex_del_mgmt_ies(struct mwifiex_private *priv); u8 *mwifiex_11d_code_2_region(u8 code); +void mwifiex_uap_del_sta_data(struct mwifiex_private *priv, + struct mwifiex_sta_node *node); extern const struct ethtool_ops mwifiex_ethtool_ops; diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c index 9f990e14966e..c710a1bb05ce 100644 --- a/drivers/net/wireless/mwifiex/sta_cmdresp.c +++ b/drivers/net/wireless/mwifiex/sta_cmdresp.c @@ -978,6 +978,8 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no, case HostCmd_CMD_UAP_BSS_STOP: priv->bss_started = 0; break; + case HostCmd_CMD_UAP_STA_DEAUTH: + break; case HostCmd_CMD_MEF_CFG: break; default: diff --git a/drivers/net/wireless/mwifiex/uap_cmd.c b/drivers/net/wireless/mwifiex/uap_cmd.c index b04b1db29100..2de882dead0f 100644 --- a/drivers/net/wireless/mwifiex/uap_cmd.c +++ b/drivers/net/wireless/mwifiex/uap_cmd.c @@ -689,6 +689,23 @@ mwifiex_cmd_uap_sys_config(struct host_cmd_ds_command *cmd, u16 cmd_action, return 0; } +/* This function prepares AP specific deauth command with mac supplied in + * function parameter. + */ +static int mwifiex_cmd_uap_sta_deauth(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, u8 *mac) +{ + struct host_cmd_ds_sta_deauth *sta_deauth = &cmd->params.sta_deauth; + + cmd->command = cpu_to_le16(HostCmd_CMD_UAP_STA_DEAUTH); + memcpy(sta_deauth->mac, mac, ETH_ALEN); + sta_deauth->reason = cpu_to_le16(WLAN_REASON_DEAUTH_LEAVING); + + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_sta_deauth) + + S_DS_GEN); + return 0; +} + /* This function prepares the AP specific commands before sending them * to the firmware. * This is a generic function which calls specific command preparation @@ -710,6 +727,10 @@ int mwifiex_uap_prepare_cmd(struct mwifiex_private *priv, u16 cmd_no, cmd->command = cpu_to_le16(cmd_no); cmd->size = cpu_to_le16(S_DS_GEN); break; + case HostCmd_CMD_UAP_STA_DEAUTH: + if (mwifiex_cmd_uap_sta_deauth(priv, cmd, data_buf)) + return -1; + break; default: dev_err(priv->adapter->dev, "PREP_CMD: unknown cmd %#x\n", cmd_no); diff --git a/drivers/net/wireless/mwifiex/uap_event.c b/drivers/net/wireless/mwifiex/uap_event.c index 77fb533ae10a..718066577c6c 100644 --- a/drivers/net/wireless/mwifiex/uap_event.c +++ b/drivers/net/wireless/mwifiex/uap_event.c @@ -292,3 +292,19 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv) return 0; } + +/* This function deletes station entry from associated station list. + * Also if both AP and STA are 11n enabled, RxReorder tables and TxBA stream + * tables created for this station are deleted. + */ +void mwifiex_uap_del_sta_data(struct mwifiex_private *priv, + struct mwifiex_sta_node *node) +{ + if (priv->ap_11n_enabled && node->is_11n_enabled) { + mwifiex_11n_del_rx_reorder_tbl_by_ta(priv, node->mac_addr); + mwifiex_del_tx_ba_stream_tbl_by_ra(priv, node->mac_addr); + } + mwifiex_del_sta_entry(priv, node->mac_addr); + + return; +} -- cgit v1.2.3 From 013a492ecf2e6e1dd424d589fb27170e2481733f Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Fri, 17 May 2013 17:50:24 -0700 Subject: mwifiex: replace spin_lock_irqsave with spin_lock and fix warn_on We see this WARN_ON during PCIe unload: WARNING: at kernel/smp.c:382 smp_call_function_many+0x66/0x1e1() This happens because we are doing PCI iounmap operations while holding spinlock via spin_lock_irqsave(). Holding spinlock this way causes disabling IRQs and hence PCI iounmap shows warning on irqs_disabled() check. Use non-irq variant of spin_lock i.e. spin_lock() instead. Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/init.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c index 1343725f8c4c..c7f11c0c3bb7 100644 --- a/drivers/net/wireless/mwifiex/init.c +++ b/drivers/net/wireless/mwifiex/init.c @@ -687,7 +687,6 @@ mwifiex_shutdown_drv(struct mwifiex_adapter *adapter) int ret = -EINPROGRESS; struct mwifiex_private *priv; s32 i; - unsigned long flags; struct sk_buff *skb; /* mwifiex already shutdown */ @@ -722,7 +721,7 @@ mwifiex_shutdown_drv(struct mwifiex_adapter *adapter) } } - spin_lock_irqsave(&adapter->mwifiex_lock, flags); + spin_lock(&adapter->mwifiex_lock); if (adapter->if_ops.data_complete) { while ((skb = skb_dequeue(&adapter->usb_rx_data_q))) { @@ -738,7 +737,7 @@ mwifiex_shutdown_drv(struct mwifiex_adapter *adapter) mwifiex_adapter_cleanup(adapter); - spin_unlock_irqrestore(&adapter->mwifiex_lock, flags); + spin_unlock(&adapter->mwifiex_lock); /* Notify completion */ ret = mwifiex_shutdown_fw_complete(adapter); -- cgit v1.2.3 From 388ec385d5ce4916f0677918a42e592ba423092d Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Fri, 17 May 2013 17:50:25 -0700 Subject: mwifiex: add calibration data download feature User can provide a text file containing calibration data in hex format while loading mwifiex module. It will be downloaded to firmware. eg. insmod mwifiex.ko cal_data_cfg=cal_data.conf Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/fw.h | 10 +++++ drivers/net/wireless/mwifiex/main.c | 13 +++++++ drivers/net/wireless/mwifiex/main.h | 1 + drivers/net/wireless/mwifiex/sta_cmd.c | 62 ++++++++++++++++++++++++++++++ drivers/net/wireless/mwifiex/sta_cmdresp.c | 15 ++++++++ 5 files changed, 101 insertions(+) diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index d3c1e01b4fc2..d6ada7354c14 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -271,6 +271,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define HostCmd_CMD_802_11_SUBSCRIBE_EVENT 0x0075 #define HostCmd_CMD_802_11_TX_RATE_QUERY 0x007f #define HostCmd_CMD_802_11_IBSS_COALESCING_STATUS 0x0083 +#define HostCmd_CMD_CFG_DATA 0x008f #define HostCmd_CMD_VERSION_EXT 0x0097 #define HostCmd_CMD_MEF_CFG 0x009a #define HostCmd_CMD_RSSI_INFO 0x00a4 @@ -465,6 +466,8 @@ enum P2P_MODES { #define MWIFIEX_CRITERIA_UNICAST BIT(1) #define MWIFIEX_CRITERIA_MULTICAST BIT(3) +#define CFG_DATA_TYPE_CAL 2 + struct mwifiex_ie_types_header { __le16 type; __le16 len; @@ -1579,6 +1582,12 @@ struct mwifiex_ie_list { struct mwifiex_ie ie_list[MAX_MGMT_IE_INDEX]; } __packed; +struct host_cmd_ds_802_11_cfg_data { + __le16 action; + __le16 type; + __le16 data_len; +} __packed; + struct host_cmd_ds_command { __le16 command; __le16 size; @@ -1638,6 +1647,7 @@ struct host_cmd_ds_command { struct host_cmd_ds_sys_config uap_sys_config; struct host_cmd_ds_sta_deauth sta_deauth; struct host_cmd_11ac_vht_cfg vht_cfg; + struct host_cmd_ds_802_11_cfg_data cfg_data; } params; } __packed; diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index eb8518618622..29d83f0c1fb8 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -25,6 +25,8 @@ #define VERSION "1.0" const char driver_version[] = "mwifiex " VERSION " (%s) "; +static char *cal_data_cfg; +module_param(cal_data_cfg, charp, 0); /* * This function registers the device and performs all the necessary @@ -336,6 +338,13 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context) dev_notice(adapter->dev, "WLAN FW is active\n"); + if (cal_data_cfg) { + if ((request_firmware(&adapter->cal_data, cal_data_cfg, + adapter->dev)) < 0) + dev_err(adapter->dev, + "Cal data request_firmware() failed\n"); + } + adapter->init_wait_q_woken = false; ret = mwifiex_init_fw(adapter); if (ret == -1) { @@ -390,6 +399,10 @@ err_init_fw: pr_debug("info: %s: unregister device\n", __func__); adapter->if_ops.unregister_dev(adapter); done: + if (adapter->cal_data) { + release_firmware(adapter->cal_data); + adapter->cal_data = NULL; + } release_firmware(adapter->firmware); complete(&adapter->fw_load); return; diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 9cf6852f6da3..0832c2437daf 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -730,6 +730,7 @@ struct mwifiex_adapter { u16 max_mgmt_ie_index; u8 scan_delay_cnt; u8 empty_tx_q_cnt; + const struct firmware *cal_data; /* 11AC */ u32 is_hw_11ac_capable; diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c index b193e25977d2..8ece48580642 100644 --- a/drivers/net/wireless/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/mwifiex/sta_cmd.c @@ -1134,6 +1134,55 @@ mwifiex_cmd_mef_cfg(struct mwifiex_private *priv, return 0; } +/* This function parse cal data from ASCII to hex */ +static u32 mwifiex_parse_cal_cfg(u8 *src, size_t len, u8 *dst) +{ + u8 *s = src, *d = dst; + + while (s - src < len) { + if (*s && (isspace(*s) || *s == '\t')) { + s++; + continue; + } + if (isxdigit(*s)) { + *d++ = simple_strtol(s, NULL, 16); + s += 2; + } else { + s++; + } + } + + return d - dst; +} + +/* This function prepares command of set_cfg_data. */ +static int mwifiex_cmd_cfg_data(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action) +{ + struct host_cmd_ds_802_11_cfg_data *cfg_data = &cmd->params.cfg_data; + struct mwifiex_adapter *adapter = priv->adapter; + u32 len, cal_data_offset; + u8 *tmp_cmd = (u8 *)cmd; + + cal_data_offset = S_DS_GEN + sizeof(*cfg_data); + if ((adapter->cal_data->data) && (adapter->cal_data->size > 0)) + len = mwifiex_parse_cal_cfg((u8 *)adapter->cal_data->data, + adapter->cal_data->size, + (u8 *)(tmp_cmd + cal_data_offset)); + else + return -1; + + cfg_data->action = cpu_to_le16(cmd_action); + cfg_data->type = cpu_to_le16(CFG_DATA_TYPE_CAL); + cfg_data->data_len = cpu_to_le16(len); + + cmd->command = cpu_to_le16(HostCmd_CMD_CFG_DATA); + cmd->size = cpu_to_le16(S_DS_GEN + sizeof(*cfg_data) + len); + + return 0; +} + /* * This function prepares the commands before sending them to the firmware. * @@ -1152,6 +1201,9 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, case HostCmd_CMD_GET_HW_SPEC: ret = mwifiex_cmd_get_hw_spec(priv, cmd_ptr); break; + case HostCmd_CMD_CFG_DATA: + ret = mwifiex_cmd_cfg_data(priv, cmd_ptr, cmd_action); + break; case HostCmd_CMD_MAC_CONTROL: ret = mwifiex_cmd_mac_control(priv, cmd_ptr, cmd_action, data_buf); @@ -1384,6 +1436,7 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, */ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta) { + struct mwifiex_adapter *adapter = priv->adapter; int ret; u16 enable = true; struct mwifiex_ds_11n_amsdu_aggr_ctrl amsdu_aggr_ctrl; @@ -1404,6 +1457,15 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta) HostCmd_ACT_GEN_SET, 0, NULL); if (ret) return -1; + + /* Download calibration data to firmware */ + if (adapter->cal_data) { + ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_CFG_DATA, + HostCmd_ACT_GEN_SET, 0, NULL); + if (ret) + return -1; + } + /* Read MAC address from HW */ ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_GET_HW_SPEC, HostCmd_ACT_GEN_GET, 0, NULL); diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c index c710a1bb05ce..d85df158cc6c 100644 --- a/drivers/net/wireless/mwifiex/sta_cmdresp.c +++ b/drivers/net/wireless/mwifiex/sta_cmdresp.c @@ -818,6 +818,18 @@ static int mwifiex_ret_subsc_evt(struct mwifiex_private *priv, return 0; } +/* This function handles the command response of set_cfg_data */ +static int mwifiex_ret_cfg_data(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + if (resp->result != HostCmd_RESULT_OK) { + dev_err(priv->adapter->dev, "Cal data cmd resp failed\n"); + return -1; + } + + return 0; +} + /* * This function handles the command responses. * @@ -841,6 +853,9 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no, case HostCmd_CMD_GET_HW_SPEC: ret = mwifiex_ret_get_hw_spec(priv, resp); break; + case HostCmd_CMD_CFG_DATA: + ret = mwifiex_ret_cfg_data(priv, resp); + break; case HostCmd_CMD_MAC_CONTROL: break; case HostCmd_CMD_802_11_MAC_ADDRESS: -- cgit v1.2.3 From 5ac253d50bd6d0b092da3d2476368aa857c10ee4 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Fri, 17 May 2013 17:50:26 -0700 Subject: mwifiex: use u32 variables for SDIO read/write port bitmap Currently supported SDIO chipsets (SD87XX) have 16 ports. This change is a prerequisite for new chipset. Signed-off-by: Amitkumar Karwar Signed-off-by: Yogesh Ashok Powar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/sdio.c | 40 ++++++++++++++++++------------------- drivers/net/wireless/mwifiex/sdio.h | 10 +++++----- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c index 363ba31b58bf..7368ee1ee79f 100644 --- a/drivers/net/wireless/mwifiex/sdio.c +++ b/drivers/net/wireless/mwifiex/sdio.c @@ -486,21 +486,21 @@ static int mwifiex_write_data_to_card(struct mwifiex_adapter *adapter, static int mwifiex_get_rd_port(struct mwifiex_adapter *adapter, u8 *port) { struct sdio_mmc_card *card = adapter->card; - u16 rd_bitmap = card->mp_rd_bitmap; + u32 rd_bitmap = card->mp_rd_bitmap; - dev_dbg(adapter->dev, "data: mp_rd_bitmap=0x%04x\n", rd_bitmap); + dev_dbg(adapter->dev, "data: mp_rd_bitmap=0x%08x\n", rd_bitmap); if (!(rd_bitmap & (CTRL_PORT_MASK | DATA_PORT_MASK))) return -1; if (card->mp_rd_bitmap & CTRL_PORT_MASK) { - card->mp_rd_bitmap &= (u16) (~CTRL_PORT_MASK); + card->mp_rd_bitmap &= (u32) (~CTRL_PORT_MASK); *port = CTRL_PORT; - dev_dbg(adapter->dev, "data: port=%d mp_rd_bitmap=0x%04x\n", + dev_dbg(adapter->dev, "data: port=%d mp_rd_bitmap=0x%08x\n", *port, card->mp_rd_bitmap); } else { if (card->mp_rd_bitmap & (1 << card->curr_rd_port)) { - card->mp_rd_bitmap &= (u16) + card->mp_rd_bitmap &= (u32) (~(1 << card->curr_rd_port)); *port = card->curr_rd_port; @@ -511,7 +511,7 @@ static int mwifiex_get_rd_port(struct mwifiex_adapter *adapter, u8 *port) } dev_dbg(adapter->dev, - "data: port=%d mp_rd_bitmap=0x%04x -> 0x%04x\n", + "data: port=%d mp_rd_bitmap=0x%08x -> 0x%08x\n", *port, rd_bitmap, card->mp_rd_bitmap); } return 0; @@ -527,15 +527,15 @@ static int mwifiex_get_rd_port(struct mwifiex_adapter *adapter, u8 *port) static int mwifiex_get_wr_port_data(struct mwifiex_adapter *adapter, u8 *port) { struct sdio_mmc_card *card = adapter->card; - u16 wr_bitmap = card->mp_wr_bitmap; + u32 wr_bitmap = card->mp_wr_bitmap; - dev_dbg(adapter->dev, "data: mp_wr_bitmap=0x%04x\n", wr_bitmap); + dev_dbg(adapter->dev, "data: mp_wr_bitmap=0x%08x\n", wr_bitmap); if (!(wr_bitmap & card->mp_data_port_mask)) return -1; if (card->mp_wr_bitmap & (1 << card->curr_wr_port)) { - card->mp_wr_bitmap &= (u16) (~(1 << card->curr_wr_port)); + card->mp_wr_bitmap &= (u32) (~(1 << card->curr_wr_port)); *port = card->curr_wr_port; if (++card->curr_wr_port == card->mp_end_port) card->curr_wr_port = 1; @@ -545,14 +545,14 @@ static int mwifiex_get_wr_port_data(struct mwifiex_adapter *adapter, u8 *port) } if (*port == CTRL_PORT) { - dev_err(adapter->dev, "invalid data port=%d cur port=%d" - " mp_wr_bitmap=0x%04x -> 0x%04x\n", + dev_err(adapter->dev, + "invalid data port=%d cur port=%d mp_wr_bitmap=0x%08x -> 0x%08x\n", *port, card->curr_wr_port, wr_bitmap, card->mp_wr_bitmap); return -1; } - dev_dbg(adapter->dev, "data: port=%d mp_wr_bitmap=0x%04x -> 0x%04x\n", + dev_dbg(adapter->dev, "data: port=%d mp_wr_bitmap=0x%08x -> 0x%08x\n", *port, wr_bitmap, card->mp_wr_bitmap); return 0; @@ -1024,7 +1024,7 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter, goto rx_curr_single; } - if (card->mp_rd_bitmap & (~((u16) CTRL_PORT_MASK))) { + if (card->mp_rd_bitmap & (~((u32) CTRL_PORT_MASK))) { /* Some more data RX pending */ dev_dbg(adapter->dev, "info: %s: not last packet\n", __func__); @@ -1185,9 +1185,9 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) return ret; if (sdio_ireg & DN_LD_HOST_INT_STATUS) { - card->mp_wr_bitmap = ((u16) card->mp_regs[WR_BITMAP_U]) << 8; - card->mp_wr_bitmap |= (u16) card->mp_regs[WR_BITMAP_L]; - dev_dbg(adapter->dev, "int: DNLD: wr_bitmap=0x%04x\n", + card->mp_wr_bitmap = ((u32) card->mp_regs[WR_BITMAP_U]) << 8; + card->mp_wr_bitmap |= (u32) card->mp_regs[WR_BITMAP_L]; + dev_dbg(adapter->dev, "int: DNLD: wr_bitmap=0x%08x\n", card->mp_wr_bitmap); if (adapter->data_sent && (card->mp_wr_bitmap & card->mp_data_port_mask)) { @@ -1204,7 +1204,7 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) /* Check if firmware has attach buffer at command port and update just that in wr_bit_map. */ card->mp_wr_bitmap |= - (u16) card->mp_regs[WR_BITMAP_L] & CTRL_PORT_MASK; + (u32) card->mp_regs[WR_BITMAP_L] & CTRL_PORT_MASK; if (card->mp_wr_bitmap & CTRL_PORT_MASK) adapter->cmd_sent = false; } @@ -1212,9 +1212,9 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) dev_dbg(adapter->dev, "info: cmd_sent=%d data_sent=%d\n", adapter->cmd_sent, adapter->data_sent); if (sdio_ireg & UP_LD_HOST_INT_STATUS) { - card->mp_rd_bitmap = ((u16) card->mp_regs[RD_BITMAP_U]) << 8; - card->mp_rd_bitmap |= (u16) card->mp_regs[RD_BITMAP_L]; - dev_dbg(adapter->dev, "int: UPLD: rd_bitmap=0x%04x\n", + card->mp_rd_bitmap = ((u32) card->mp_regs[RD_BITMAP_U]) << 8; + card->mp_rd_bitmap |= (u32) card->mp_regs[RD_BITMAP_L]; + dev_dbg(adapter->dev, "int: UPLD: rd_bitmap=0x%08x\n", card->mp_rd_bitmap); while (true) { diff --git a/drivers/net/wireless/mwifiex/sdio.h b/drivers/net/wireless/mwifiex/sdio.h index 8cc5468654b4..6588069bfe94 100644 --- a/drivers/net/wireless/mwifiex/sdio.h +++ b/drivers/net/wireless/mwifiex/sdio.h @@ -261,7 +261,7 @@ struct mwifiex_sdio_mpa_tx { u8 *buf; u32 buf_len; u32 pkt_cnt; - u16 ports; + u32 ports; u16 start_port; u8 enabled; u32 buf_size; @@ -272,7 +272,7 @@ struct mwifiex_sdio_mpa_rx { u8 *buf; u32 buf_len; u32 pkt_cnt; - u16 ports; + u32 ports; u16 start_port; struct sk_buff *skb_arr[SDIO_MP_AGGR_DEF_PKT_LIMIT]; @@ -290,11 +290,11 @@ struct sdio_mmc_card { struct sdio_func *func; struct mwifiex_adapter *adapter; - u16 mp_rd_bitmap; - u16 mp_wr_bitmap; + u32 mp_rd_bitmap; + u32 mp_wr_bitmap; u16 mp_end_port; - u16 mp_data_port_mask; + u32 mp_data_port_mask; u8 curr_rd_port; u8 curr_wr_port; -- cgit v1.2.3 From 05889f825397805bf2d6c40594195ccb8aca7b75 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Fri, 17 May 2013 17:50:27 -0700 Subject: mwifiex: store SDIO chip specific information in separate structure Register addresses, firmware name and some macros are specific to a chip. They are stored in a new structure. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/sdio.c | 116 +++++++++++++++++++++--------------- drivers/net/wireless/mwifiex/sdio.h | 114 +++++++++++++++++++++++++---------- 2 files changed, 149 insertions(+), 81 deletions(-) diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c index 7368ee1ee79f..022e9fd7197a 100644 --- a/drivers/net/wireless/mwifiex/sdio.c +++ b/drivers/net/wireless/mwifiex/sdio.c @@ -77,6 +77,15 @@ mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE; + if (id->driver_data) { + struct mwifiex_sdio_device *data = (void *)id->driver_data; + + card->firmware = data->firmware; + card->reg = data->reg; + card->max_ports = data->max_ports; + card->mp_agg_pkt_limit = data->mp_agg_pkt_limit; + } + sdio_claim_host(func); ret = sdio_enable_func(func); sdio_release_host(func); @@ -254,9 +263,12 @@ static int mwifiex_sdio_resume(struct device *dev) /* WLAN IDs */ static const struct sdio_device_id mwifiex_ids[] = { - {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8786)}, - {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787)}, - {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797)}, + {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8786), + .driver_data = (unsigned long) &mwifiex_sdio_sd8786}, + {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787), + .driver_data = (unsigned long) &mwifiex_sdio_sd8787}, + {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797), + .driver_data = (unsigned long) &mwifiex_sdio_sd8797}, {}, }; @@ -410,6 +422,7 @@ static int mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter) static int mwifiex_init_sdio_ioport(struct mwifiex_adapter *adapter) { u32 reg; + struct sdio_mmc_card *card = adapter->card; adapter->ioport = 0; @@ -434,13 +447,13 @@ static int mwifiex_init_sdio_ioport(struct mwifiex_adapter *adapter) /* Set Host interrupt reset to read to clear */ if (!mwifiex_read_reg(adapter, HOST_INT_RSR_REG, ®)) mwifiex_write_reg(adapter, HOST_INT_RSR_REG, - reg | SDIO_INT_MASK); + reg | card->reg->sdio_int_mask); else return -1; /* Dnld/Upld ready set to auto reset */ - if (!mwifiex_read_reg(adapter, CARD_MISC_CFG_REG, ®)) - mwifiex_write_reg(adapter, CARD_MISC_CFG_REG, + if (!mwifiex_read_reg(adapter, card->reg->card_misc_cfg_reg, ®)) + mwifiex_write_reg(adapter, card->reg->card_misc_cfg_reg, reg | AUTO_RE_ENABLE_INT); else return -1; @@ -486,11 +499,12 @@ static int mwifiex_write_data_to_card(struct mwifiex_adapter *adapter, static int mwifiex_get_rd_port(struct mwifiex_adapter *adapter, u8 *port) { struct sdio_mmc_card *card = adapter->card; + const struct mwifiex_sdio_card_reg *reg = card->reg; u32 rd_bitmap = card->mp_rd_bitmap; dev_dbg(adapter->dev, "data: mp_rd_bitmap=0x%08x\n", rd_bitmap); - if (!(rd_bitmap & (CTRL_PORT_MASK | DATA_PORT_MASK))) + if (!(rd_bitmap & (CTRL_PORT_MASK | reg->data_port_mask))) return -1; if (card->mp_rd_bitmap & CTRL_PORT_MASK) { @@ -504,8 +518,8 @@ static int mwifiex_get_rd_port(struct mwifiex_adapter *adapter, u8 *port) (~(1 << card->curr_rd_port)); *port = card->curr_rd_port; - if (++card->curr_rd_port == MAX_PORT) - card->curr_rd_port = 1; + if (++card->curr_rd_port == card->max_ports) + card->curr_rd_port = reg->start_rd_port; } else { return -1; } @@ -538,7 +552,7 @@ static int mwifiex_get_wr_port_data(struct mwifiex_adapter *adapter, u8 *port) card->mp_wr_bitmap &= (u32) (~(1 << card->curr_wr_port)); *port = card->curr_wr_port; if (++card->curr_wr_port == card->mp_end_port) - card->curr_wr_port = 1; + card->curr_wr_port = card->reg->start_wr_port; } else { adapter->data_sent = true; return -EBUSY; @@ -564,11 +578,12 @@ static int mwifiex_get_wr_port_data(struct mwifiex_adapter *adapter, u8 *port) static int mwifiex_sdio_poll_card_status(struct mwifiex_adapter *adapter, u8 bits) { + struct sdio_mmc_card *card = adapter->card; u32 tries; u32 cs; for (tries = 0; tries < MAX_POLL_TRIES; tries++) { - if (mwifiex_read_reg(adapter, CARD_STATUS_REG, &cs)) + if (mwifiex_read_reg(adapter, card->reg->poll_reg, &cs)) break; else if ((cs & bits) == bits) return 0; @@ -587,12 +602,14 @@ mwifiex_sdio_poll_card_status(struct mwifiex_adapter *adapter, u8 bits) static int mwifiex_sdio_read_fw_status(struct mwifiex_adapter *adapter, u16 *dat) { + struct sdio_mmc_card *card = adapter->card; + const struct mwifiex_sdio_card_reg *reg = card->reg; u32 fws0, fws1; - if (mwifiex_read_reg(adapter, CARD_FW_STATUS0_REG, &fws0)) + if (mwifiex_read_reg(adapter, reg->status_reg_0, &fws0)) return -1; - if (mwifiex_read_reg(adapter, CARD_FW_STATUS1_REG, &fws1)) + if (mwifiex_read_reg(adapter, reg->status_reg_1, &fws1)) return -1; *dat = (u16) ((fws1 << 8) | fws0); @@ -633,8 +650,11 @@ static int mwifiex_sdio_disable_host_int(struct mwifiex_adapter *adapter) */ static int mwifiex_sdio_enable_host_int(struct mwifiex_adapter *adapter) { + struct sdio_mmc_card *card = adapter->card; + /* Simply write the mask to the register */ - if (mwifiex_write_reg(adapter, HOST_INT_MASK_REG, HOST_INT_ENABLE)) { + if (mwifiex_write_reg(adapter, HOST_INT_MASK_REG, + card->reg->host_int_enable)) { dev_err(adapter->dev, "enable host interrupt failed\n"); return -1; } @@ -686,6 +706,8 @@ static int mwifiex_sdio_card_to_host(struct mwifiex_adapter *adapter, static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, struct mwifiex_fw_image *fw) { + struct sdio_mmc_card *card = adapter->card; + const struct mwifiex_sdio_card_reg *reg = card->reg; int ret; u8 *firmware = fw->fw_buf; u32 firmware_len = fw->fw_len; @@ -727,7 +749,7 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, break; for (tries = 0; tries < MAX_POLL_TRIES; tries++) { - ret = mwifiex_read_reg(adapter, HOST_F1_RD_BASE_0, + ret = mwifiex_read_reg(adapter, reg->base_0_reg, &base0); if (ret) { dev_err(adapter->dev, @@ -736,7 +758,7 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, base0, base0); goto done; } - ret = mwifiex_read_reg(adapter, HOST_F1_RD_BASE_1, + ret = mwifiex_read_reg(adapter, reg->base_1_reg, &base1); if (ret) { dev_err(adapter->dev, @@ -828,6 +850,7 @@ done: static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter, u32 poll_num) { + struct sdio_mmc_card *card = adapter->card; int ret = 0; u16 firmware_stat; u32 tries; @@ -849,7 +872,7 @@ static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter, if (ret) { if (mwifiex_read_reg - (adapter, CARD_FW_STATUS0_REG, &winner_status)) + (adapter, card->reg->status_reg_0, &winner_status)) winner_status = 0; if (winner_status) @@ -869,9 +892,9 @@ static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter) u32 sdio_ireg; unsigned long flags; - if (mwifiex_read_data_sync(adapter, card->mp_regs, MAX_MP_REGS, - REG_PORT | MWIFIEX_SDIO_BYTE_MODE_MASK, - 0)) { + if (mwifiex_read_data_sync(adapter, card->mp_regs, + card->reg->max_mp_regs, + REG_PORT | MWIFIEX_SDIO_BYTE_MODE_MASK, 0)) { dev_err(adapter->dev, "read mp_regs failed\n"); return; } @@ -1167,6 +1190,7 @@ error: static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) { struct sdio_mmc_card *card = adapter->card; + const struct mwifiex_sdio_card_reg *reg = card->reg; int ret = 0; u8 sdio_ireg; struct sk_buff *skb; @@ -1185,8 +1209,10 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) return ret; if (sdio_ireg & DN_LD_HOST_INT_STATUS) { - card->mp_wr_bitmap = ((u32) card->mp_regs[WR_BITMAP_U]) << 8; - card->mp_wr_bitmap |= (u32) card->mp_regs[WR_BITMAP_L]; + card->mp_wr_bitmap = + ((u32) card->mp_regs[reg->wr_bitmap_u]) << 8; + card->mp_wr_bitmap |= + (u32) card->mp_regs[reg->wr_bitmap_l]; dev_dbg(adapter->dev, "int: DNLD: wr_bitmap=0x%08x\n", card->mp_wr_bitmap); if (adapter->data_sent && @@ -1204,7 +1230,7 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) /* Check if firmware has attach buffer at command port and update just that in wr_bit_map. */ card->mp_wr_bitmap |= - (u32) card->mp_regs[WR_BITMAP_L] & CTRL_PORT_MASK; + (u32) card->mp_regs[reg->wr_bitmap_l] & CTRL_PORT_MASK; if (card->mp_wr_bitmap & CTRL_PORT_MASK) adapter->cmd_sent = false; } @@ -1212,8 +1238,9 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) dev_dbg(adapter->dev, "info: cmd_sent=%d data_sent=%d\n", adapter->cmd_sent, adapter->data_sent); if (sdio_ireg & UP_LD_HOST_INT_STATUS) { - card->mp_rd_bitmap = ((u32) card->mp_regs[RD_BITMAP_U]) << 8; - card->mp_rd_bitmap |= (u32) card->mp_regs[RD_BITMAP_L]; + card->mp_rd_bitmap = + ((u32) card->mp_regs[reg->rd_bitmap_u]) << 8; + card->mp_rd_bitmap |= (u32) card->mp_regs[reg->rd_bitmap_l]; dev_dbg(adapter->dev, "int: UPLD: rd_bitmap=0x%08x\n", card->mp_rd_bitmap); @@ -1224,8 +1251,8 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) "info: no more rd_port available\n"); break; } - len_reg_l = RD_LEN_P0_L + (port << 1); - len_reg_u = RD_LEN_P0_U + (port << 1); + len_reg_l = reg->rd_len_p0_l + (port << 1); + len_reg_u = reg->rd_len_p0_u + (port << 1); rx_len = ((u16) card->mp_regs[len_reg_u]) << 8; rx_len |= (u16) card->mp_regs[len_reg_l]; dev_dbg(adapter->dev, "info: RX: port=%d rx_len=%u\n", @@ -1586,18 +1613,7 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter) adapter->dev = &func->dev; - switch (func->device) { - case SDIO_DEVICE_ID_MARVELL_8786: - strcpy(adapter->fw_name, SD8786_DEFAULT_FW_NAME); - break; - case SDIO_DEVICE_ID_MARVELL_8797: - strcpy(adapter->fw_name, SD8797_DEFAULT_FW_NAME); - break; - case SDIO_DEVICE_ID_MARVELL_8787: - default: - strcpy(adapter->fw_name, SD8787_DEFAULT_FW_NAME); - break; - } + strcpy(adapter->fw_name, card->firmware); return 0; @@ -1626,6 +1642,7 @@ disable_func: static int mwifiex_init_sdio(struct mwifiex_adapter *adapter) { struct sdio_mmc_card *card = adapter->card; + const struct mwifiex_sdio_card_reg *reg = card->reg; int ret; u32 sdio_ireg; @@ -1645,27 +1662,27 @@ static int mwifiex_init_sdio(struct mwifiex_adapter *adapter) /* Initialize SDIO variables in card */ card->mp_rd_bitmap = 0; card->mp_wr_bitmap = 0; - card->curr_rd_port = 1; - card->curr_wr_port = 1; + card->curr_rd_port = reg->start_rd_port; + card->curr_wr_port = reg->start_wr_port; - card->mp_data_port_mask = DATA_PORT_MASK; + card->mp_data_port_mask = reg->data_port_mask; card->mpa_tx.buf_len = 0; card->mpa_tx.pkt_cnt = 0; card->mpa_tx.start_port = 0; card->mpa_tx.enabled = 1; - card->mpa_tx.pkt_aggr_limit = SDIO_MP_AGGR_DEF_PKT_LIMIT; + card->mpa_tx.pkt_aggr_limit = card->mp_agg_pkt_limit; card->mpa_rx.buf_len = 0; card->mpa_rx.pkt_cnt = 0; card->mpa_rx.start_port = 0; card->mpa_rx.enabled = 1; - card->mpa_rx.pkt_aggr_limit = SDIO_MP_AGGR_DEF_PKT_LIMIT; + card->mpa_rx.pkt_aggr_limit = card->mp_agg_pkt_limit; /* Allocate buffers for SDIO MP-A */ - card->mp_regs = kzalloc(MAX_MP_REGS, GFP_KERNEL); + card->mp_regs = kzalloc(reg->max_mp_regs, GFP_KERNEL); if (!card->mp_regs) return -ENOMEM; @@ -1716,16 +1733,17 @@ static void mwifiex_update_mp_end_port(struct mwifiex_adapter *adapter, u16 port) { struct sdio_mmc_card *card = adapter->card; + const struct mwifiex_sdio_card_reg *reg = card->reg; int i; card->mp_end_port = port; - card->mp_data_port_mask = DATA_PORT_MASK; + card->mp_data_port_mask = reg->data_port_mask; - for (i = 1; i <= MAX_PORT - card->mp_end_port; i++) - card->mp_data_port_mask &= ~(1 << (MAX_PORT - i)); + for (i = 1; i <= card->max_ports - card->mp_end_port; i++) + card->mp_data_port_mask &= ~(1 << (card->max_ports - i)); - card->curr_wr_port = 1; + card->curr_wr_port = reg->start_wr_port; dev_dbg(adapter->dev, "cmd: mp_end_port %d, data port mask 0x%x\n", port, card->mp_data_port_mask); diff --git a/drivers/net/wireless/mwifiex/sdio.h b/drivers/net/wireless/mwifiex/sdio.h index 6588069bfe94..0d931f729639 100644 --- a/drivers/net/wireless/mwifiex/sdio.h +++ b/drivers/net/wireless/mwifiex/sdio.h @@ -37,12 +37,6 @@ #define BYTE_MODE 0 #define REG_PORT 0 -#define RD_BITMAP_L 0x04 -#define RD_BITMAP_U 0x05 -#define WR_BITMAP_L 0x06 -#define WR_BITMAP_U 0x07 -#define RD_LEN_P0_L 0x08 -#define RD_LEN_P0_U 0x09 #define MWIFIEX_SDIO_IO_PORT_MASK 0xfffff @@ -50,12 +44,8 @@ #define CTRL_PORT 0 #define CTRL_PORT_MASK 0x0001 -#define DATA_PORT_MASK 0xfffe -#define MAX_MP_REGS 64 -#define MAX_PORT 16 - -#define SDIO_MP_AGGR_DEF_PKT_LIMIT 8 +#define SDIO_MP_AGGR_DEF_PKT_LIMIT 8 #define SDIO_MP_TX_AGGR_DEF_BUF_SIZE (8192) /* 8K */ @@ -90,8 +80,6 @@ #define UP_LD_HOST_INT_MASK (0x1U) /* Host Control Registers : Download host interrupt mask */ #define DN_LD_HOST_INT_MASK (0x2U) -/* Enable Host interrupt mask */ -#define HOST_INT_ENABLE (UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK) /* Disable Host interrupt mask */ #define HOST_INT_DISABLE 0xff @@ -106,7 +94,6 @@ #define HOST_INT_RSR_REG 0x01 /* Host Control Registers : Upload host interrupt RSR */ #define UP_LD_HOST_INT_RSR (0x1U) -#define SDIO_INT_MASK 0x3F /* Host Control Registers : Host interrupt status */ #define HOST_INT_STATUS_REG 0x28 @@ -117,8 +104,6 @@ /* Host Control Registers : Download restart */ #define DN_LD_RESTART (0x1U << 0) -/* Card Control Registers : Card status register */ -#define CARD_STATUS_REG 0x30 /* Card Control Registers : Card I/O ready */ #define CARD_IO_READY (0x1U << 3) /* Card Control Registers : CIS card ready */ @@ -153,20 +138,9 @@ /* Card Control Registers : Power down RSR */ #define POWER_DOWN_RSR (0x1U << 3) -/* Card Control Registers : Miscellaneous Configuration Register */ -#define CARD_MISC_CFG_REG 0x6C - -/* Host F1 read base 0 */ -#define HOST_F1_RD_BASE_0 0x0040 -/* Host F1 read base 1 */ -#define HOST_F1_RD_BASE_1 0x0041 /* Host F1 card ready */ #define HOST_F1_CARD_RDY 0x0020 -/* Firmware status 0 register */ -#define CARD_FW_STATUS0_REG 0x60 -/* Firmware status 1 register */ -#define CARD_FW_STATUS1_REG 0x61 /* Rx length register */ #define CARD_RX_LEN_REG 0x62 /* Rx unit register */ @@ -192,7 +166,8 @@ if (a->mpa_tx.start_port <= port) \ a->mpa_tx.ports |= (1<<(a->mpa_tx.pkt_cnt)); \ else \ - a->mpa_tx.ports |= (1<<(a->mpa_tx.pkt_cnt+1+(MAX_PORT - \ + a->mpa_tx.ports |= (1<<(a->mpa_tx.pkt_cnt+1+ \ + (a->max_ports - \ a->mp_end_port))); \ a->mpa_tx.pkt_cnt++; \ } while (0) @@ -203,9 +178,9 @@ /* SDIO Tx aggregation port limit ? */ #define MP_TX_AGGR_PORT_LIMIT_REACHED(a) ((a->curr_wr_port < \ - a->mpa_tx.start_port) && (((MAX_PORT - \ + a->mpa_tx.start_port) && (((a->max_ports -\ a->mpa_tx.start_port) + a->curr_wr_port) >= \ - SDIO_MP_AGGR_DEF_PKT_LIMIT)) + a->mp_agg_pkt_limit)) /* Reset SDIO Tx aggregation buffer parameters */ #define MP_TX_AGGR_BUF_RESET(a) do { \ @@ -221,9 +196,9 @@ /* SDIO Tx aggregation port limit ? */ #define MP_RX_AGGR_PORT_LIMIT_REACHED(a) ((a->curr_rd_port < \ - a->mpa_rx.start_port) && (((MAX_PORT - \ + a->mpa_rx.start_port) && (((a->max_ports -\ a->mpa_rx.start_port) + a->curr_rd_port) >= \ - SDIO_MP_AGGR_DEF_PKT_LIMIT)) + a->mp_agg_pkt_limit)) /* SDIO Rx aggregation in progress ? */ #define MP_RX_AGGR_IN_PROGRESS(a) (a->mpa_rx.pkt_cnt > 0) @@ -286,10 +261,36 @@ struct mwifiex_sdio_mpa_rx { int mwifiex_bus_register(void); void mwifiex_bus_unregister(void); +struct mwifiex_sdio_card_reg { + u8 start_rd_port; + u8 start_wr_port; + u8 base_0_reg; + u8 base_1_reg; + u8 poll_reg; + u8 host_int_enable; + u8 status_reg_0; + u8 status_reg_1; + u8 sdio_int_mask; + u32 data_port_mask; + u8 max_mp_regs; + u8 rd_bitmap_l; + u8 rd_bitmap_u; + u8 wr_bitmap_l; + u8 wr_bitmap_u; + u8 rd_len_p0_l; + u8 rd_len_p0_u; + u8 card_misc_cfg_reg; +}; + struct sdio_mmc_card { struct sdio_func *func; struct mwifiex_adapter *adapter; + const char *firmware; + const struct mwifiex_sdio_card_reg *reg; + u8 max_ports; + u8 mp_agg_pkt_limit; + u32 mp_rd_bitmap; u32 mp_wr_bitmap; @@ -305,6 +306,55 @@ struct sdio_mmc_card { struct mwifiex_sdio_mpa_rx mpa_rx; }; +struct mwifiex_sdio_device { + const char *firmware; + const struct mwifiex_sdio_card_reg *reg; + u8 max_ports; + u8 mp_agg_pkt_limit; +}; + +static const struct mwifiex_sdio_card_reg mwifiex_reg_sd87xx = { + .start_rd_port = 1, + .start_wr_port = 1, + .base_0_reg = 0x0040, + .base_1_reg = 0x0041, + .poll_reg = 0x30, + .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK, + .status_reg_0 = 0x60, + .status_reg_1 = 0x61, + .sdio_int_mask = 0x3f, + .data_port_mask = 0x0000fffe, + .max_mp_regs = 64, + .rd_bitmap_l = 0x04, + .rd_bitmap_u = 0x05, + .wr_bitmap_l = 0x06, + .wr_bitmap_u = 0x07, + .rd_len_p0_l = 0x08, + .rd_len_p0_u = 0x09, + .card_misc_cfg_reg = 0x6c, +}; + +static const struct mwifiex_sdio_device mwifiex_sdio_sd8786 = { + .firmware = SD8786_DEFAULT_FW_NAME, + .reg = &mwifiex_reg_sd87xx, + .max_ports = 16, + .mp_agg_pkt_limit = 8, +}; + +static const struct mwifiex_sdio_device mwifiex_sdio_sd8787 = { + .firmware = SD8787_DEFAULT_FW_NAME, + .reg = &mwifiex_reg_sd87xx, + .max_ports = 16, + .mp_agg_pkt_limit = 8, +}; + +static const struct mwifiex_sdio_device mwifiex_sdio_sd8797 = { + .firmware = SD8797_DEFAULT_FW_NAME, + .reg = &mwifiex_reg_sd87xx, + .max_ports = 16, + .mp_agg_pkt_limit = 8, +}; + /* * .cmdrsp_complete handler */ -- cgit v1.2.3 From ab93d4ff3693ece926ae73f71a5cc8c37579bc6a Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Fri, 17 May 2013 17:53:42 -0700 Subject: mwifiex: replace unnecessary u32 variables with u8 in sdio.c Some u32 variables in sdio.c are used to store/pass u8 values. Replace them with u8 variables. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/sdio.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c index 022e9fd7197a..e4357a6dd8dc 100644 --- a/drivers/net/wireless/mwifiex/sdio.c +++ b/drivers/net/wireless/mwifiex/sdio.c @@ -294,13 +294,13 @@ static struct sdio_driver mwifiex_sdio = { * This function writes data into SDIO card register. */ static int -mwifiex_write_reg(struct mwifiex_adapter *adapter, u32 reg, u32 data) +mwifiex_write_reg(struct mwifiex_adapter *adapter, u32 reg, u8 data) { struct sdio_mmc_card *card = adapter->card; int ret = -1; sdio_claim_host(card->func); - sdio_writeb(card->func, (u8) data, reg, &ret); + sdio_writeb(card->func, data, reg, &ret); sdio_release_host(card->func); return ret; @@ -310,7 +310,7 @@ mwifiex_write_reg(struct mwifiex_adapter *adapter, u32 reg, u32 data) * This function reads data from SDIO card register. */ static int -mwifiex_read_reg(struct mwifiex_adapter *adapter, u32 reg, u32 *data) +mwifiex_read_reg(struct mwifiex_adapter *adapter, u32 reg, u8 *data) { struct sdio_mmc_card *card = adapter->card; int ret = -1; @@ -421,7 +421,7 @@ static int mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter) */ static int mwifiex_init_sdio_ioport(struct mwifiex_adapter *adapter) { - u32 reg; + u8 reg; struct sdio_mmc_card *card = adapter->card; adapter->ioport = 0; @@ -580,7 +580,7 @@ mwifiex_sdio_poll_card_status(struct mwifiex_adapter *adapter, u8 bits) { struct sdio_mmc_card *card = adapter->card; u32 tries; - u32 cs; + u8 cs; for (tries = 0; tries < MAX_POLL_TRIES; tries++) { if (mwifiex_read_reg(adapter, card->reg->poll_reg, &cs)) @@ -604,7 +604,7 @@ mwifiex_sdio_read_fw_status(struct mwifiex_adapter *adapter, u16 *dat) { struct sdio_mmc_card *card = adapter->card; const struct mwifiex_sdio_card_reg *reg = card->reg; - u32 fws0, fws1; + u8 fws0, fws1; if (mwifiex_read_reg(adapter, reg->status_reg_0, &fws0)) return -1; @@ -625,14 +625,14 @@ mwifiex_sdio_read_fw_status(struct mwifiex_adapter *adapter, u16 *dat) */ static int mwifiex_sdio_disable_host_int(struct mwifiex_adapter *adapter) { - u32 host_int_mask; + u8 host_int_mask, host_int_disable = HOST_INT_DISABLE; /* Read back the host_int_mask register */ if (mwifiex_read_reg(adapter, HOST_INT_MASK_REG, &host_int_mask)) return -1; /* Update with the mask and write back to the register */ - host_int_mask &= ~HOST_INT_DISABLE; + host_int_mask &= ~host_int_disable; if (mwifiex_write_reg(adapter, HOST_INT_MASK_REG, host_int_mask)) { dev_err(adapter->dev, "disable host interrupt failed\n"); @@ -712,7 +712,7 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, u8 *firmware = fw->fw_buf; u32 firmware_len = fw->fw_len; u32 offset = 0; - u32 base0, base1; + u8 base0, base1; u8 *fwbuf; u16 len = 0; u32 txlen, tx_blocks = 0, tries; @@ -854,7 +854,7 @@ static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter, int ret = 0; u16 firmware_stat; u32 tries; - u32 winner_status; + u8 winner_status; /* Wait for firmware initialization event */ for (tries = 0; tries < poll_num; tries++) { @@ -889,7 +889,7 @@ static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter, static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter) { struct sdio_mmc_card *card = adapter->card; - u32 sdio_ireg; + u8 sdio_ireg; unsigned long flags; if (mwifiex_read_data_sync(adapter, card->mp_regs, @@ -1284,7 +1284,7 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) if (mwifiex_sdio_card_to_host_mp_aggr(adapter, skb, port)) { - u32 cr = 0; + u8 cr = 0; dev_err(adapter->dev, "card_to_host_mpa failed:" " int status=%#x\n", sdio_ireg); @@ -1644,7 +1644,7 @@ static int mwifiex_init_sdio(struct mwifiex_adapter *adapter) struct sdio_mmc_card *card = adapter->card; const struct mwifiex_sdio_card_reg *reg = card->reg; int ret; - u32 sdio_ireg; + u8 sdio_ireg; /* * Read the HOST_INT_STATUS_REG for ACK the first interrupt got -- cgit v1.2.3 From e6a520304f6562ee3f57b0ba01e49a458c5eeda2 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Fri, 17 May 2013 17:53:56 -0700 Subject: mwifiex: code rearrangement in mwifiex_get_rd_port() Get rid of 'if else' usage by returning in 'if' block. This improves readability by removing indentations. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/sdio.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c index e4357a6dd8dc..a18a5b4998d4 100644 --- a/drivers/net/wireless/mwifiex/sdio.c +++ b/drivers/net/wireless/mwifiex/sdio.c @@ -512,22 +512,23 @@ static int mwifiex_get_rd_port(struct mwifiex_adapter *adapter, u8 *port) *port = CTRL_PORT; dev_dbg(adapter->dev, "data: port=%d mp_rd_bitmap=0x%08x\n", *port, card->mp_rd_bitmap); - } else { - if (card->mp_rd_bitmap & (1 << card->curr_rd_port)) { - card->mp_rd_bitmap &= (u32) - (~(1 << card->curr_rd_port)); - *port = card->curr_rd_port; + return 0; + } - if (++card->curr_rd_port == card->max_ports) - card->curr_rd_port = reg->start_rd_port; - } else { - return -1; - } + if (!(card->mp_rd_bitmap & (1 << card->curr_rd_port))) + return -1; + + /* We are now handling the SDIO data ports */ + card->mp_rd_bitmap &= (u32)(~(1 << card->curr_rd_port)); + *port = card->curr_rd_port; + + if (++card->curr_rd_port == card->max_ports) + card->curr_rd_port = reg->start_rd_port; + + dev_dbg(adapter->dev, + "data: port=%d mp_rd_bitmap=0x%08x -> 0x%08x\n", + *port, rd_bitmap, card->mp_rd_bitmap); - dev_dbg(adapter->dev, - "data: port=%d mp_rd_bitmap=0x%08x -> 0x%08x\n", - *port, rd_bitmap, card->mp_rd_bitmap); - } return 0; } -- cgit v1.2.3 From 0fc0d43b979cecda24a30b6af1185984a654521a Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Fri, 17 May 2013 17:54:23 -0700 Subject: mwifiex: do port calculations separately This patch rearranges the code for better readability Signed-off-by: Amitkumar Karwar Signed-off-by: Yogesh Ashok Powar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/sdio.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c index a18a5b4998d4..99a508b87864 100644 --- a/drivers/net/wireless/mwifiex/sdio.c +++ b/drivers/net/wireless/mwifiex/sdio.c @@ -539,7 +539,7 @@ static int mwifiex_get_rd_port(struct mwifiex_adapter *adapter, u8 *port) * increased (provided it does not reach the maximum limit, in which * case it is reset to 1) */ -static int mwifiex_get_wr_port_data(struct mwifiex_adapter *adapter, u8 *port) +static int mwifiex_get_wr_port_data(struct mwifiex_adapter *adapter, u32 *port) { struct sdio_mmc_card *card = adapter->card; u32 wr_bitmap = card->mp_wr_bitmap; @@ -1027,7 +1027,7 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter, s32 f_aggr_cur = 0; struct sk_buff *skb_deaggr; u32 pind; - u32 pkt_len, pkt_type = 0; + u32 pkt_len, pkt_type, mport; u8 *curr_ptr; u32 rx_len = skb->len; @@ -1100,11 +1100,11 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter, dev_dbg(adapter->dev, "info: do_rx_aggr: num of packets: %d\n", card->mpa_rx.pkt_cnt); + mport = (adapter->ioport | 0x1000 | + (card->mpa_rx.ports << 4)) + card->mpa_rx.start_port; + if (mwifiex_read_data_sync(adapter, card->mpa_rx.buf, - card->mpa_rx.buf_len, - (adapter->ioport | 0x1000 | - (card->mpa_rx.ports << 4)) + - card->mpa_rx.start_port, 1)) + card->mpa_rx.buf_len, mport, 1)) goto error; curr_ptr = card->mpa_rx.buf; @@ -1333,7 +1333,7 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) * and return. */ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter, - u8 *payload, u32 pkt_len, u8 port, + u8 *payload, u32 pkt_len, u32 port, u32 next_pkt_len) { struct sdio_mmc_card *card = adapter->card; @@ -1342,6 +1342,7 @@ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter, s32 f_send_cur_buf = 0; s32 f_precopy_cur_buf = 0; s32 f_postcopy_cur_buf = 0; + u32 mport; if ((!card->mpa_tx.enabled) || (port == CTRL_PORT)) { dev_dbg(adapter->dev, "info: %s: tx aggregation disabled\n", @@ -1418,11 +1419,10 @@ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter, dev_dbg(adapter->dev, "data: %s: send aggr buffer: %d %d\n", __func__, card->mpa_tx.start_port, card->mpa_tx.ports); + mport = (adapter->ioport | 0x1000 | + (card->mpa_tx.ports << 4)) + card->mpa_tx.start_port; ret = mwifiex_write_data_to_card(adapter, card->mpa_tx.buf, - card->mpa_tx.buf_len, - (adapter->ioport | 0x1000 | - (card->mpa_tx.ports << 4)) + - card->mpa_tx.start_port); + card->mpa_tx.buf_len, mport); MP_TX_AGGR_BUF_RESET(card); } @@ -1462,7 +1462,7 @@ static int mwifiex_sdio_host_to_card(struct mwifiex_adapter *adapter, int ret; u32 buf_block_len; u32 blk_size; - u8 port = CTRL_PORT; + u32 port = CTRL_PORT; u8 *payload = (u8 *)skb->data; u32 pkt_len = skb->len; -- cgit v1.2.3 From 248eb4c6533a1409ec7a1eab9359a1befd8bb9c6 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Fri, 17 May 2013 17:54:34 -0700 Subject: mwifiex: define a macro for MPA base address As Multiple-Port Aggregation base address value is fixed, we can have a macro for it. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/sdio.c | 4 ++-- drivers/net/wireless/mwifiex/sdio.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c index 99a508b87864..1892e889d41e 100644 --- a/drivers/net/wireless/mwifiex/sdio.c +++ b/drivers/net/wireless/mwifiex/sdio.c @@ -1100,7 +1100,7 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter, dev_dbg(adapter->dev, "info: do_rx_aggr: num of packets: %d\n", card->mpa_rx.pkt_cnt); - mport = (adapter->ioport | 0x1000 | + mport = (adapter->ioport | SDIO_MPA_ADDR_BASE | (card->mpa_rx.ports << 4)) + card->mpa_rx.start_port; if (mwifiex_read_data_sync(adapter, card->mpa_rx.buf, @@ -1419,7 +1419,7 @@ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter, dev_dbg(adapter->dev, "data: %s: send aggr buffer: %d %d\n", __func__, card->mpa_tx.start_port, card->mpa_tx.ports); - mport = (adapter->ioport | 0x1000 | + mport = (adapter->ioport | SDIO_MPA_ADDR_BASE | (card->mpa_tx.ports << 4)) + card->mpa_tx.start_port; ret = mwifiex_write_data_to_card(adapter, card->mpa_tx.buf, card->mpa_tx.buf_len, mport); diff --git a/drivers/net/wireless/mwifiex/sdio.h b/drivers/net/wireless/mwifiex/sdio.h index 0d931f729639..e4016d2692d4 100644 --- a/drivers/net/wireless/mwifiex/sdio.h +++ b/drivers/net/wireless/mwifiex/sdio.h @@ -42,6 +42,7 @@ #define MWIFIEX_SDIO_BYTE_MODE_MASK 0x80000000 +#define SDIO_MPA_ADDR_BASE 0x1000 #define CTRL_PORT 0 #define CTRL_PORT_MASK 0x0001 -- cgit v1.2.3 From dc468e3d420d9042d18f240196dc72ba0306ee89 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Fri, 17 May 2013 17:54:42 -0700 Subject: mwifiex: remove unnecessary macros in sdio.h They are not used in the code. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/sdio.h | 51 ------------------------------------- 1 file changed, 51 deletions(-) diff --git a/drivers/net/wireless/mwifiex/sdio.h b/drivers/net/wireless/mwifiex/sdio.h index e4016d2692d4..339608ba6171 100644 --- a/drivers/net/wireless/mwifiex/sdio.h +++ b/drivers/net/wireless/mwifiex/sdio.h @@ -66,14 +66,8 @@ /* Host Control Registers : Configuration */ #define CONFIGURATION_REG 0x00 -/* Host Control Registers : Host without Command 53 finish host*/ -#define HOST_TO_CARD_EVENT (0x1U << 3) -/* Host Control Registers : Host without Command 53 finish host */ -#define HOST_WO_CMD53_FINISH_HOST (0x1U << 2) /* Host Control Registers : Host power up */ #define HOST_POWER_UP (0x1U << 1) -/* Host Control Registers : Host power down */ -#define HOST_POWER_DOWN (0x1U << 0) /* Host Control Registers : Host interrupt mask */ #define HOST_INT_MASK_REG 0x02 @@ -93,60 +87,15 @@ /* Host Control Registers : Host interrupt RSR */ #define HOST_INT_RSR_REG 0x01 -/* Host Control Registers : Upload host interrupt RSR */ -#define UP_LD_HOST_INT_RSR (0x1U) /* Host Control Registers : Host interrupt status */ #define HOST_INT_STATUS_REG 0x28 -/* Host Control Registers : Upload CRC error */ -#define UP_LD_CRC_ERR (0x1U << 2) -/* Host Control Registers : Upload restart */ -#define UP_LD_RESTART (0x1U << 1) -/* Host Control Registers : Download restart */ -#define DN_LD_RESTART (0x1U << 0) /* Card Control Registers : Card I/O ready */ #define CARD_IO_READY (0x1U << 3) -/* Card Control Registers : CIS card ready */ -#define CIS_CARD_RDY (0x1U << 2) -/* Card Control Registers : Upload card ready */ -#define UP_LD_CARD_RDY (0x1U << 1) /* Card Control Registers : Download card ready */ #define DN_LD_CARD_RDY (0x1U << 0) -/* Card Control Registers : Host interrupt mask register */ -#define HOST_INTERRUPT_MASK_REG 0x34 -/* Card Control Registers : Host power interrupt mask */ -#define HOST_POWER_INT_MASK (0x1U << 3) -/* Card Control Registers : Abort card interrupt mask */ -#define ABORT_CARD_INT_MASK (0x1U << 2) -/* Card Control Registers : Upload card interrupt mask */ -#define UP_LD_CARD_INT_MASK (0x1U << 1) -/* Card Control Registers : Download card interrupt mask */ -#define DN_LD_CARD_INT_MASK (0x1U << 0) - -/* Card Control Registers : Card interrupt status register */ -#define CARD_INTERRUPT_STATUS_REG 0x38 -/* Card Control Registers : Power up interrupt */ -#define POWER_UP_INT (0x1U << 4) -/* Card Control Registers : Power down interrupt */ -#define POWER_DOWN_INT (0x1U << 3) - -/* Card Control Registers : Card interrupt RSR register */ -#define CARD_INTERRUPT_RSR_REG 0x3c -/* Card Control Registers : Power up RSR */ -#define POWER_UP_RSR (0x1U << 4) -/* Card Control Registers : Power down RSR */ -#define POWER_DOWN_RSR (0x1U << 3) - -/* Host F1 card ready */ -#define HOST_F1_CARD_RDY 0x0020 - -/* Rx length register */ -#define CARD_RX_LEN_REG 0x62 -/* Rx unit register */ -#define CARD_RX_UNIT_REG 0x63 - /* Max retry number of CMD53 write */ #define MAX_WRITE_IOMEM_RETRY 2 -- cgit v1.2.3 From c23b7c8f719317983c59b01873b51394fde26907 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Fri, 17 May 2013 17:54:51 -0700 Subject: mwifiex: code rearrangement in multiport aggregation path There are some macros defined for multiport aggregation calculations. As we may need to add some more code to accomodate new chipsets, we will change them to inline functions. Also, use dynamic allocation for Rx buffer array. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/sdio.c | 17 +++++--- drivers/net/wireless/mwifiex/sdio.h | 83 +++++++++++++++++++++++-------------- 2 files changed, 64 insertions(+), 36 deletions(-) diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c index 1892e889d41e..4b196dc97463 100644 --- a/drivers/net/wireless/mwifiex/sdio.c +++ b/drivers/net/wireless/mwifiex/sdio.c @@ -1084,10 +1084,10 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter, if (f_aggr_cur) { dev_dbg(adapter->dev, "info: current packet aggregation\n"); /* Curr pkt can be aggregated */ - MP_RX_AGGR_SETUP(card, skb, port); + mp_rx_aggr_setup(card, skb, port); if (MP_RX_AGGR_PKT_LIMIT_REACHED(card) || - MP_RX_AGGR_PORT_LIMIT_REACHED(card)) { + mp_rx_aggr_port_limit_reached(card)) { dev_dbg(adapter->dev, "info: %s: aggregated packet " "limit reached\n", __func__); /* No more pkts allowed in Aggr buf, rx it */ @@ -1358,7 +1358,7 @@ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter, __func__); if (MP_TX_AGGR_IN_PROGRESS(card)) { - if (!MP_TX_AGGR_PORT_LIMIT_REACHED(card) && + if (!mp_tx_aggr_port_limit_reached(card) && MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len)) { f_precopy_cur_buf = 1; @@ -1371,7 +1371,7 @@ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter, /* No room in Aggr buf, send it */ f_send_aggr_buf = 1; - if (MP_TX_AGGR_PORT_LIMIT_REACHED(card) || + if (mp_tx_aggr_port_limit_reached(card) || !(card->mp_wr_bitmap & (1 << card->curr_wr_port))) f_send_cur_buf = 1; @@ -1410,7 +1410,7 @@ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter, MP_TX_AGGR_BUF_PUT(card, payload, pkt_len, port); if (MP_TX_AGGR_PKT_LIMIT_REACHED(card) || - MP_TX_AGGR_PORT_LIMIT_REACHED(card)) + mp_tx_aggr_port_limit_reached(card)) /* No more pkts allowed in Aggr buf, send it */ f_send_aggr_buf = 1; } @@ -1687,6 +1687,11 @@ static int mwifiex_init_sdio(struct mwifiex_adapter *adapter) if (!card->mp_regs) return -ENOMEM; + /* Allocate skb pointer buffers */ + card->mpa_rx.skb_arr = kzalloc((sizeof(void *)) * + card->mp_agg_pkt_limit, GFP_KERNEL); + card->mpa_rx.len_arr = kzalloc(sizeof(*card->mpa_rx.len_arr) * + card->mp_agg_pkt_limit, GFP_KERNEL); ret = mwifiex_alloc_sdio_mpa_buffers(adapter, SDIO_MP_TX_AGGR_DEF_BUF_SIZE, SDIO_MP_RX_AGGR_DEF_BUF_SIZE); @@ -1723,6 +1728,8 @@ static void mwifiex_cleanup_sdio(struct mwifiex_adapter *adapter) struct sdio_mmc_card *card = adapter->card; kfree(card->mp_regs); + kfree(card->mpa_rx.skb_arr); + kfree(card->mpa_rx.len_arr); kfree(card->mpa_tx.buf); kfree(card->mpa_rx.buf); } diff --git a/drivers/net/wireless/mwifiex/sdio.h b/drivers/net/wireless/mwifiex/sdio.h index 339608ba6171..597db37a45f9 100644 --- a/drivers/net/wireless/mwifiex/sdio.h +++ b/drivers/net/wireless/mwifiex/sdio.h @@ -46,8 +46,6 @@ #define CTRL_PORT 0 #define CTRL_PORT_MASK 0x0001 -#define SDIO_MP_AGGR_DEF_PKT_LIMIT 8 - #define SDIO_MP_TX_AGGR_DEF_BUF_SIZE (8192) /* 8K */ /* Multi port RX aggregation buffer size */ @@ -126,12 +124,6 @@ #define MP_TX_AGGR_PKT_LIMIT_REACHED(a) \ (a->mpa_tx.pkt_cnt == a->mpa_tx.pkt_aggr_limit) -/* SDIO Tx aggregation port limit ? */ -#define MP_TX_AGGR_PORT_LIMIT_REACHED(a) ((a->curr_wr_port < \ - a->mpa_tx.start_port) && (((a->max_ports -\ - a->mpa_tx.start_port) + a->curr_wr_port) >= \ - a->mp_agg_pkt_limit)) - /* Reset SDIO Tx aggregation buffer parameters */ #define MP_TX_AGGR_BUF_RESET(a) do { \ a->mpa_tx.pkt_cnt = 0; \ @@ -144,12 +136,6 @@ #define MP_RX_AGGR_PKT_LIMIT_REACHED(a) \ (a->mpa_rx.pkt_cnt == a->mpa_rx.pkt_aggr_limit) -/* SDIO Tx aggregation port limit ? */ -#define MP_RX_AGGR_PORT_LIMIT_REACHED(a) ((a->curr_rd_port < \ - a->mpa_rx.start_port) && (((a->max_ports -\ - a->mpa_rx.start_port) + a->curr_rd_port) >= \ - a->mp_agg_pkt_limit)) - /* SDIO Rx aggregation in progress ? */ #define MP_RX_AGGR_IN_PROGRESS(a) (a->mpa_rx.pkt_cnt > 0) @@ -157,20 +143,6 @@ #define MP_RX_AGGR_BUF_HAS_ROOM(a, rx_len) \ ((a->mpa_rx.buf_len+rx_len) <= a->mpa_rx.buf_size) -/* Prepare to copy current packet from card to SDIO Rx aggregation buffer */ -#define MP_RX_AGGR_SETUP(a, skb, port) do { \ - a->mpa_rx.buf_len += skb->len; \ - if (!a->mpa_rx.pkt_cnt) \ - a->mpa_rx.start_port = port; \ - if (a->mpa_rx.start_port <= port) \ - a->mpa_rx.ports |= (1<<(a->mpa_rx.pkt_cnt)); \ - else \ - a->mpa_rx.ports |= (1<<(a->mpa_rx.pkt_cnt+1)); \ - a->mpa_rx.skb_arr[a->mpa_rx.pkt_cnt] = skb; \ - a->mpa_rx.len_arr[a->mpa_rx.pkt_cnt] = skb->len; \ - a->mpa_rx.pkt_cnt++; \ -} while (0) - /* Reset SDIO Rx aggregation buffer parameters */ #define MP_RX_AGGR_BUF_RESET(a) do { \ a->mpa_rx.pkt_cnt = 0; \ @@ -179,7 +151,6 @@ a->mpa_rx.start_port = 0; \ } while (0) - /* data structure for SDIO MPA TX */ struct mwifiex_sdio_mpa_tx { /* multiport tx aggregation buffer pointer */ @@ -200,8 +171,8 @@ struct mwifiex_sdio_mpa_rx { u32 ports; u16 start_port; - struct sk_buff *skb_arr[SDIO_MP_AGGR_DEF_PKT_LIMIT]; - u32 len_arr[SDIO_MP_AGGR_DEF_PKT_LIMIT]; + struct sk_buff **skb_arr; + u32 *len_arr; u8 enabled; u32 buf_size; @@ -325,4 +296,54 @@ static inline int mwifiex_sdio_event_complete(struct mwifiex_adapter *adapter, return 0; } +static inline bool +mp_rx_aggr_port_limit_reached(struct sdio_mmc_card *card) +{ + u8 tmp; + + if (card->curr_rd_port < card->mpa_rx.start_port) { + tmp = card->mp_agg_pkt_limit; + + if (((card->max_ports - card->mpa_rx.start_port) + + card->curr_rd_port) >= tmp) + return true; + } + + return false; +} + +static inline bool +mp_tx_aggr_port_limit_reached(struct sdio_mmc_card *card) +{ + u16 tmp; + + if (card->curr_wr_port < card->mpa_tx.start_port) { + tmp = card->mp_agg_pkt_limit; + + if (((card->max_ports - card->mpa_tx.start_port) + + card->curr_wr_port) >= tmp) + return true; + } + + return false; +} + +/* Prepare to copy current packet from card to SDIO Rx aggregation buffer */ +static inline void mp_rx_aggr_setup(struct sdio_mmc_card *card, + struct sk_buff *skb, u8 port) +{ + card->mpa_rx.buf_len += skb->len; + + if (!card->mpa_rx.pkt_cnt) + card->mpa_rx.start_port = port; + + if (card->mpa_rx.start_port <= port) + card->mpa_rx.ports |= 1 << (card->mpa_rx.pkt_cnt); + else + card->mpa_rx.ports |= 1 << (card->mpa_rx.pkt_cnt + 1); + + card->mpa_rx.skb_arr[card->mpa_rx.pkt_cnt] = skb; + card->mpa_rx.len_arr[card->mpa_rx.pkt_cnt] = skb->len; + card->mpa_rx.pkt_cnt++; +} #endif /* _MWIFIEX_SDIO_H */ -- cgit v1.2.3 From b60186f824330cd85859f5944b9559b0f7df0b4f Mon Sep 17 00:00:00 2001 From: Yogesh Ashok Powar Date: Fri, 17 May 2013 17:54:58 -0700 Subject: mwifiex: add support for Marvell SD8897 chipset Some of the key differences between SD8897 and older chipsets are as follows: a) sdio mpa_rx and mpa_tx ports have been increased from 16 to 32 b) Same is the case with read/write bitmap that one receives from mpa_reg read c) aggregation packet count doubled from 8 to 16 d) Most of key reg addresses are changed e) There is a separate command or control port f) Now command rx/tx_done have new interrupts 1. 'supports_sdio_new_mode' flag is added to handle (a) and (b). 2. (c) and (d) are taken care of by filling chip specific information in global structurei (mwifiex_sdio_sd8897). 3. For older chipsets, port 0 was cmd port and port 1->15 were data port. Therefore we had CTRL_PORT_MASK to differentiate port type. Now these changes are under 'has_control_mask' flag. Signed-off-by: Yogesh Ashok Powar Signed-off-by: Amitkumar Karwar Signed-off-by: Nishant Sarmukadam Signed-off-by: Bing Zhao Signed-off-by: Frank Huang Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/Kconfig | 4 +- drivers/net/wireless/mwifiex/sdio.c | 267 ++++++++++++++++++++++++++++------- drivers/net/wireless/mwifiex/sdio.h | 105 +++++++++++++- 3 files changed, 315 insertions(+), 61 deletions(-) diff --git a/drivers/net/wireless/mwifiex/Kconfig b/drivers/net/wireless/mwifiex/Kconfig index 4f614aad9ded..f7ff4725506a 100644 --- a/drivers/net/wireless/mwifiex/Kconfig +++ b/drivers/net/wireless/mwifiex/Kconfig @@ -3,13 +3,13 @@ config MWIFIEX depends on CFG80211 ---help--- This adds support for wireless adapters based on Marvell - 802.11n chipsets. + 802.11n/ac chipsets. If you choose to build it as a module, it will be called mwifiex. config MWIFIEX_SDIO - tristate "Marvell WiFi-Ex Driver for SD8786/SD8787/SD8797" + tristate "Marvell WiFi-Ex Driver for SD8786/SD8787/SD8797/SD8897" depends on MWIFIEX && MMC select FW_LOADER ---help--- diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c index 4b196dc97463..5ee5ed02eccd 100644 --- a/drivers/net/wireless/mwifiex/sdio.c +++ b/drivers/net/wireless/mwifiex/sdio.c @@ -84,6 +84,8 @@ mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) card->reg = data->reg; card->max_ports = data->max_ports; card->mp_agg_pkt_limit = data->mp_agg_pkt_limit; + card->supports_sdio_new_mode = data->supports_sdio_new_mode; + card->has_control_mask = data->has_control_mask; } sdio_claim_host(func); @@ -260,6 +262,8 @@ static int mwifiex_sdio_resume(struct device *dev) #define SDIO_DEVICE_ID_MARVELL_8787 (0x9119) /* Device ID for SD8797 */ #define SDIO_DEVICE_ID_MARVELL_8797 (0x9129) +/* Device ID for SD8897 */ +#define SDIO_DEVICE_ID_MARVELL_8897 (0x912d) /* WLAN IDs */ static const struct sdio_device_id mwifiex_ids[] = { @@ -269,6 +273,8 @@ static const struct sdio_device_id mwifiex_ids[] = { .driver_data = (unsigned long) &mwifiex_sdio_sd8787}, {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797), .driver_data = (unsigned long) &mwifiex_sdio_sd8797}, + {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8897), + .driver_data = (unsigned long) &mwifiex_sdio_sd8897}, {}, }; @@ -412,7 +418,40 @@ static int mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter) } /* - * This function initializes the IO ports. + * This function is used to initialize IO ports for the + * chipsets supporting SDIO new mode eg SD8897. + */ +static int mwifiex_init_sdio_new_mode(struct mwifiex_adapter *adapter) +{ + u8 reg; + + adapter->ioport = MEM_PORT; + + /* enable sdio new mode */ + if (mwifiex_read_reg(adapter, CARD_CONFIG_2_1_REG, ®)) + return -1; + if (mwifiex_write_reg(adapter, CARD_CONFIG_2_1_REG, + reg | CMD53_NEW_MODE)) + return -1; + + /* Configure cmd port and enable reading rx length from the register */ + if (mwifiex_read_reg(adapter, CMD_CONFIG_0, ®)) + return -1; + if (mwifiex_write_reg(adapter, CMD_CONFIG_0, reg | CMD_PORT_RD_LEN_EN)) + return -1; + + /* Enable Dnld/Upld ready auto reset for cmd port after cmd53 is + * completed + */ + if (mwifiex_read_reg(adapter, CMD_CONFIG_1, ®)) + return -1; + if (mwifiex_write_reg(adapter, CMD_CONFIG_1, reg | CMD_PORT_AUTO_EN)) + return -1; + + return 0; +} + +/* This function initializes the IO ports. * * The following operations are performed - * - Read the IO ports (0, 1 and 2) @@ -426,6 +465,12 @@ static int mwifiex_init_sdio_ioport(struct mwifiex_adapter *adapter) adapter->ioport = 0; + if (card->supports_sdio_new_mode) { + if (mwifiex_init_sdio_new_mode(adapter)) + return -1; + goto cont; + } + /* Read the IO port */ if (!mwifiex_read_reg(adapter, IO_PORT_0_REG, ®)) adapter->ioport |= (reg & 0xff); @@ -441,7 +486,7 @@ static int mwifiex_init_sdio_ioport(struct mwifiex_adapter *adapter) adapter->ioport |= ((reg & 0xff) << 16); else return -1; - +cont: pr_debug("info: SDIO FUNC1 IO port: %#x\n", adapter->ioport); /* Set Host interrupt reset to read to clear */ @@ -504,10 +549,16 @@ static int mwifiex_get_rd_port(struct mwifiex_adapter *adapter, u8 *port) dev_dbg(adapter->dev, "data: mp_rd_bitmap=0x%08x\n", rd_bitmap); - if (!(rd_bitmap & (CTRL_PORT_MASK | reg->data_port_mask))) - return -1; + if (card->supports_sdio_new_mode) { + if (!(rd_bitmap & reg->data_port_mask)) + return -1; + } else { + if (!(rd_bitmap & (CTRL_PORT_MASK | reg->data_port_mask))) + return -1; + } - if (card->mp_rd_bitmap & CTRL_PORT_MASK) { + if ((card->has_control_mask) && + (card->mp_rd_bitmap & CTRL_PORT_MASK)) { card->mp_rd_bitmap &= (u32) (~CTRL_PORT_MASK); *port = CTRL_PORT; dev_dbg(adapter->dev, "data: port=%d mp_rd_bitmap=0x%08x\n", @@ -542,24 +593,34 @@ static int mwifiex_get_rd_port(struct mwifiex_adapter *adapter, u8 *port) static int mwifiex_get_wr_port_data(struct mwifiex_adapter *adapter, u32 *port) { struct sdio_mmc_card *card = adapter->card; + const struct mwifiex_sdio_card_reg *reg = card->reg; u32 wr_bitmap = card->mp_wr_bitmap; dev_dbg(adapter->dev, "data: mp_wr_bitmap=0x%08x\n", wr_bitmap); - if (!(wr_bitmap & card->mp_data_port_mask)) + if (card->supports_sdio_new_mode && + !(wr_bitmap & reg->data_port_mask)) { + adapter->data_sent = true; + return -EBUSY; + } else if (!card->supports_sdio_new_mode && + !(wr_bitmap & card->mp_data_port_mask)) { return -1; + } if (card->mp_wr_bitmap & (1 << card->curr_wr_port)) { card->mp_wr_bitmap &= (u32) (~(1 << card->curr_wr_port)); *port = card->curr_wr_port; - if (++card->curr_wr_port == card->mp_end_port) - card->curr_wr_port = card->reg->start_wr_port; + if (((card->supports_sdio_new_mode) && + (++card->curr_wr_port == card->max_ports)) || + ((!card->supports_sdio_new_mode) && + (++card->curr_wr_port == card->mp_end_port))) + card->curr_wr_port = reg->start_wr_port; } else { adapter->data_sent = true; return -EBUSY; } - if (*port == CTRL_PORT) { + if ((card->has_control_mask) && (*port == CTRL_PORT)) { dev_err(adapter->dev, "invalid data port=%d cur port=%d mp_wr_bitmap=0x%08x -> 0x%08x\n", *port, card->curr_wr_port, wr_bitmap, @@ -904,6 +965,9 @@ static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter) if (sdio_ireg) { /* * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS + * For SDIO new mode CMD port interrupts + * DN_LD_CMD_PORT_HOST_INT_STATUS and/or + * UP_LD_CMD_PORT_HOST_INT_STATUS * Clear the interrupt status register */ dev_dbg(adapter->dev, "int: sdio_ireg = %#x\n", sdio_ireg); @@ -1031,7 +1095,7 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter, u8 *curr_ptr; u32 rx_len = skb->len; - if (port == CTRL_PORT) { + if ((card->has_control_mask) && (port == CTRL_PORT)) { /* Read the command Resp without aggr */ dev_dbg(adapter->dev, "info: %s: no aggregation for cmd " "response\n", __func__); @@ -1048,7 +1112,10 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter, goto rx_curr_single; } - if (card->mp_rd_bitmap & (~((u32) CTRL_PORT_MASK))) { + if ((!card->has_control_mask && (card->mp_rd_bitmap & + card->reg->data_port_mask)) || + (card->has_control_mask && (card->mp_rd_bitmap & + (~((u32) CTRL_PORT_MASK))))) { /* Some more data RX pending */ dev_dbg(adapter->dev, "info: %s: not last packet\n", __func__); @@ -1100,8 +1167,25 @@ static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter, dev_dbg(adapter->dev, "info: do_rx_aggr: num of packets: %d\n", card->mpa_rx.pkt_cnt); - mport = (adapter->ioport | SDIO_MPA_ADDR_BASE | - (card->mpa_rx.ports << 4)) + card->mpa_rx.start_port; + if (card->supports_sdio_new_mode) { + int i; + u32 port_count; + + for (i = 0, port_count = 0; i < card->max_ports; i++) + if (card->mpa_rx.ports & BIT(i)) + port_count++; + + /* Reading data from "start_port + 0" to "start_port + + * port_count -1", so decrease the count by 1 + */ + port_count--; + mport = (adapter->ioport | SDIO_MPA_ADDR_BASE | + (port_count << 8)) + card->mpa_rx.start_port; + } else { + mport = (adapter->ioport | SDIO_MPA_ADDR_BASE | + (card->mpa_rx.ports << 4)) + + card->mpa_rx.start_port; + } if (mwifiex_read_data_sync(adapter, card->mpa_rx.buf, card->mpa_rx.buf_len, mport, 1)) @@ -1200,6 +1284,8 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) u32 rx_blocks; u16 rx_len; unsigned long flags; + u32 bitmap; + u8 cr; spin_lock_irqsave(&adapter->int_lock, flags); sdio_ireg = adapter->int_status; @@ -1209,12 +1295,60 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) if (!sdio_ireg) return ret; + /* Following interrupt is only for SDIO new mode */ + if (sdio_ireg & DN_LD_CMD_PORT_HOST_INT_STATUS && adapter->cmd_sent) + adapter->cmd_sent = false; + + /* Following interrupt is only for SDIO new mode */ + if (sdio_ireg & UP_LD_CMD_PORT_HOST_INT_STATUS) { + u32 pkt_type; + + /* read the len of control packet */ + rx_len = card->mp_regs[CMD_RD_LEN_1] << 8; + rx_len |= (u16) card->mp_regs[CMD_RD_LEN_0]; + rx_blocks = DIV_ROUND_UP(rx_len, MWIFIEX_SDIO_BLOCK_SIZE); + if (rx_len <= INTF_HEADER_LEN || + (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE) > + MWIFIEX_RX_DATA_BUF_SIZE) + return -1; + rx_len = (u16) (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE); + + skb = dev_alloc_skb(rx_len); + if (!skb) + return -1; + + skb_put(skb, rx_len); + + if (mwifiex_sdio_card_to_host(adapter, &pkt_type, skb->data, + skb->len, adapter->ioport | + CMD_PORT_SLCT)) { + dev_err(adapter->dev, + "%s: failed to card_to_host", __func__); + dev_kfree_skb_any(skb); + goto term_cmd; + } + + if ((pkt_type != MWIFIEX_TYPE_CMD) && + (pkt_type != MWIFIEX_TYPE_EVENT)) + dev_err(adapter->dev, + "%s:Received wrong packet on cmd port", + __func__); + + mwifiex_decode_rx_packet(adapter, skb, pkt_type); + } + if (sdio_ireg & DN_LD_HOST_INT_STATUS) { - card->mp_wr_bitmap = - ((u32) card->mp_regs[reg->wr_bitmap_u]) << 8; - card->mp_wr_bitmap |= - (u32) card->mp_regs[reg->wr_bitmap_l]; - dev_dbg(adapter->dev, "int: DNLD: wr_bitmap=0x%08x\n", + bitmap = (u32) card->mp_regs[reg->wr_bitmap_l]; + bitmap |= ((u32) card->mp_regs[reg->wr_bitmap_u]) << 8; + if (card->supports_sdio_new_mode) { + bitmap |= + ((u32) card->mp_regs[reg->wr_bitmap_1l]) << 16; + bitmap |= + ((u32) card->mp_regs[reg->wr_bitmap_1u]) << 24; + } + card->mp_wr_bitmap = bitmap; + + dev_dbg(adapter->dev, "int: DNLD: wr_bitmap=0x%x\n", card->mp_wr_bitmap); if (adapter->data_sent && (card->mp_wr_bitmap & card->mp_data_port_mask)) { @@ -1227,7 +1361,7 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) /* As firmware will not generate download ready interrupt if the port updated is command port only, cmd_sent should be done for any SDIO interrupt. */ - if (adapter->cmd_sent) { + if (card->has_control_mask && adapter->cmd_sent) { /* Check if firmware has attach buffer at command port and update just that in wr_bit_map. */ card->mp_wr_bitmap |= @@ -1239,10 +1373,16 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) dev_dbg(adapter->dev, "info: cmd_sent=%d data_sent=%d\n", adapter->cmd_sent, adapter->data_sent); if (sdio_ireg & UP_LD_HOST_INT_STATUS) { - card->mp_rd_bitmap = - ((u32) card->mp_regs[reg->rd_bitmap_u]) << 8; - card->mp_rd_bitmap |= (u32) card->mp_regs[reg->rd_bitmap_l]; - dev_dbg(adapter->dev, "int: UPLD: rd_bitmap=0x%08x\n", + bitmap = (u32) card->mp_regs[reg->rd_bitmap_l]; + bitmap |= ((u32) card->mp_regs[reg->rd_bitmap_u]) << 8; + if (card->supports_sdio_new_mode) { + bitmap |= + ((u32) card->mp_regs[reg->rd_bitmap_1l]) << 16; + bitmap |= + ((u32) card->mp_regs[reg->rd_bitmap_1u]) << 24; + } + card->mp_rd_bitmap = bitmap; + dev_dbg(adapter->dev, "int: UPLD: rd_bitmap=0x%x\n", card->mp_rd_bitmap); while (true) { @@ -1285,37 +1425,33 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) if (mwifiex_sdio_card_to_host_mp_aggr(adapter, skb, port)) { - u8 cr = 0; - dev_err(adapter->dev, "card_to_host_mpa failed:" " int status=%#x\n", sdio_ireg); - if (mwifiex_read_reg(adapter, - CONFIGURATION_REG, &cr)) - dev_err(adapter->dev, - "read CFG reg failed\n"); - - dev_dbg(adapter->dev, - "info: CFG reg val = %d\n", cr); - if (mwifiex_write_reg(adapter, - CONFIGURATION_REG, - (cr | 0x04))) - dev_err(adapter->dev, - "write CFG reg failed\n"); - - dev_dbg(adapter->dev, "info: write success\n"); - if (mwifiex_read_reg(adapter, - CONFIGURATION_REG, &cr)) - dev_err(adapter->dev, - "read CFG reg failed\n"); - - dev_dbg(adapter->dev, - "info: CFG reg val =%x\n", cr); - return -1; + goto term_cmd; } } } return 0; + +term_cmd: + /* terminate cmd */ + if (mwifiex_read_reg(adapter, CONFIGURATION_REG, &cr)) + dev_err(adapter->dev, "read CFG reg failed\n"); + else + dev_dbg(adapter->dev, "info: CFG reg val = %d\n", cr); + + if (mwifiex_write_reg(adapter, CONFIGURATION_REG, (cr | 0x04))) + dev_err(adapter->dev, "write CFG reg failed\n"); + else + dev_dbg(adapter->dev, "info: write success\n"); + + if (mwifiex_read_reg(adapter, CONFIGURATION_REG, &cr)) + dev_err(adapter->dev, "read CFG reg failed\n"); + else + dev_dbg(adapter->dev, "info: CFG reg val =%x\n", cr); + + return -1; } /* @@ -1344,7 +1480,9 @@ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter, s32 f_postcopy_cur_buf = 0; u32 mport; - if ((!card->mpa_tx.enabled) || (port == CTRL_PORT)) { + if (!card->mpa_tx.enabled || + (card->has_control_mask && (port == CTRL_PORT)) || + (card->supports_sdio_new_mode && (port == CMD_PORT_SLCT))) { dev_dbg(adapter->dev, "info: %s: tx aggregation disabled\n", __func__); @@ -1419,8 +1557,26 @@ static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter, dev_dbg(adapter->dev, "data: %s: send aggr buffer: %d %d\n", __func__, card->mpa_tx.start_port, card->mpa_tx.ports); - mport = (adapter->ioport | SDIO_MPA_ADDR_BASE | - (card->mpa_tx.ports << 4)) + card->mpa_tx.start_port; + if (card->supports_sdio_new_mode) { + u32 port_count; + int i; + + for (i = 0, port_count = 0; i < card->max_ports; i++) + if (card->mpa_tx.ports & BIT(i)) + port_count++; + + /* Writing data from "start_port + 0" to "start_port + + * port_count -1", so decrease the count by 1 + */ + port_count--; + mport = (adapter->ioport | SDIO_MPA_ADDR_BASE | + (port_count << 8)) + card->mpa_tx.start_port; + } else { + mport = (adapter->ioport | SDIO_MPA_ADDR_BASE | + (card->mpa_tx.ports << 4)) + + card->mpa_tx.start_port; + } + ret = mwifiex_write_data_to_card(adapter, card->mpa_tx.buf, card->mpa_tx.buf_len, mport); @@ -1493,6 +1649,9 @@ static int mwifiex_sdio_host_to_card(struct mwifiex_adapter *adapter, pkt_len > MWIFIEX_UPLD_SIZE) dev_err(adapter->dev, "%s: payload=%p, nb=%d\n", __func__, payload, pkt_len); + + if (card->supports_sdio_new_mode) + port = CMD_PORT_SLCT; } /* Transfer data to card */ @@ -1748,8 +1907,11 @@ mwifiex_update_mp_end_port(struct mwifiex_adapter *adapter, u16 port) card->mp_data_port_mask = reg->data_port_mask; - for (i = 1; i <= card->max_ports - card->mp_end_port; i++) - card->mp_data_port_mask &= ~(1 << (card->max_ports - i)); + if (reg->start_wr_port) { + for (i = 1; i <= card->max_ports - card->mp_end_port; i++) + card->mp_data_port_mask &= + ~(1 << (card->max_ports - i)); + } card->curr_wr_port = reg->start_wr_port; @@ -1857,3 +2019,4 @@ MODULE_LICENSE("GPL v2"); MODULE_FIRMWARE(SD8786_DEFAULT_FW_NAME); MODULE_FIRMWARE(SD8787_DEFAULT_FW_NAME); MODULE_FIRMWARE(SD8797_DEFAULT_FW_NAME); +MODULE_FIRMWARE(SD8897_DEFAULT_FW_NAME); diff --git a/drivers/net/wireless/mwifiex/sdio.h b/drivers/net/wireless/mwifiex/sdio.h index 597db37a45f9..6d51dfdd8251 100644 --- a/drivers/net/wireless/mwifiex/sdio.h +++ b/drivers/net/wireless/mwifiex/sdio.h @@ -32,6 +32,7 @@ #define SD8786_DEFAULT_FW_NAME "mrvl/sd8786_uapsta.bin" #define SD8787_DEFAULT_FW_NAME "mrvl/sd8787_uapsta.bin" #define SD8797_DEFAULT_FW_NAME "mrvl/sd8797_uapsta.bin" +#define SD8897_DEFAULT_FW_NAME "mrvl/sd8897_uapsta.bin" #define BLOCK_MODE 1 #define BYTE_MODE 0 @@ -46,6 +47,23 @@ #define CTRL_PORT 0 #define CTRL_PORT_MASK 0x0001 +#define CMD_PORT_UPLD_INT_MASK (0x1U<<6) +#define CMD_PORT_DNLD_INT_MASK (0x1U<<7) +#define HOST_TERM_CMD53 (0x1U << 2) +#define REG_PORT 0 +#define MEM_PORT 0x10000 +#define CMD_RD_LEN_0 0xB4 +#define CMD_RD_LEN_1 0xB5 +#define CARD_CONFIG_2_1_REG 0xCD +#define CMD53_NEW_MODE (0x1U << 0) +#define CMD_CONFIG_0 0xB8 +#define CMD_PORT_RD_LEN_EN (0x1U << 2) +#define CMD_CONFIG_1 0xB9 +#define CMD_PORT_AUTO_EN (0x1U << 0) +#define CMD_PORT_SLCT 0x8000 +#define UP_LD_CMD_PORT_HOST_INT_STATUS (0x40U) +#define DN_LD_CMD_PORT_HOST_INT_STATUS (0x80U) + #define SDIO_MP_TX_AGGR_DEF_BUF_SIZE (8192) /* 8K */ /* Multi port RX aggregation buffer size */ @@ -73,6 +91,7 @@ #define UP_LD_HOST_INT_MASK (0x1U) /* Host Control Registers : Download host interrupt mask */ #define DN_LD_HOST_INT_MASK (0x2U) + /* Disable Host interrupt mask */ #define HOST_INT_DISABLE 0xff @@ -196,8 +215,12 @@ struct mwifiex_sdio_card_reg { u8 max_mp_regs; u8 rd_bitmap_l; u8 rd_bitmap_u; + u8 rd_bitmap_1l; + u8 rd_bitmap_1u; u8 wr_bitmap_l; u8 wr_bitmap_u; + u8 wr_bitmap_1l; + u8 wr_bitmap_1u; u8 rd_len_p0_l; u8 rd_len_p0_u; u8 card_misc_cfg_reg; @@ -211,6 +234,8 @@ struct sdio_mmc_card { const struct mwifiex_sdio_card_reg *reg; u8 max_ports; u8 mp_agg_pkt_limit; + bool supports_sdio_new_mode; + bool has_control_mask; u32 mp_rd_bitmap; u32 mp_wr_bitmap; @@ -232,6 +257,8 @@ struct mwifiex_sdio_device { const struct mwifiex_sdio_card_reg *reg; u8 max_ports; u8 mp_agg_pkt_limit; + bool supports_sdio_new_mode; + bool has_control_mask; }; static const struct mwifiex_sdio_card_reg mwifiex_reg_sd87xx = { @@ -255,11 +282,39 @@ static const struct mwifiex_sdio_card_reg mwifiex_reg_sd87xx = { .card_misc_cfg_reg = 0x6c, }; +static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8897 = { + .start_rd_port = 0, + .start_wr_port = 0, + .base_0_reg = 0x60, + .base_1_reg = 0x61, + .poll_reg = 0x50, + .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK | + CMD_PORT_UPLD_INT_MASK | CMD_PORT_DNLD_INT_MASK, + .status_reg_0 = 0xc0, + .status_reg_1 = 0xc1, + .sdio_int_mask = 0xff, + .data_port_mask = 0xffffffff, + .max_mp_regs = 184, + .rd_bitmap_l = 0x04, + .rd_bitmap_u = 0x05, + .rd_bitmap_1l = 0x06, + .rd_bitmap_1u = 0x07, + .wr_bitmap_l = 0x08, + .wr_bitmap_u = 0x09, + .wr_bitmap_1l = 0x0a, + .wr_bitmap_1u = 0x0b, + .rd_len_p0_l = 0x0c, + .rd_len_p0_u = 0x0d, + .card_misc_cfg_reg = 0xcc, +}; + static const struct mwifiex_sdio_device mwifiex_sdio_sd8786 = { .firmware = SD8786_DEFAULT_FW_NAME, .reg = &mwifiex_reg_sd87xx, .max_ports = 16, .mp_agg_pkt_limit = 8, + .supports_sdio_new_mode = false, + .has_control_mask = true, }; static const struct mwifiex_sdio_device mwifiex_sdio_sd8787 = { @@ -267,6 +322,8 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8787 = { .reg = &mwifiex_reg_sd87xx, .max_ports = 16, .mp_agg_pkt_limit = 8, + .supports_sdio_new_mode = false, + .has_control_mask = true, }; static const struct mwifiex_sdio_device mwifiex_sdio_sd8797 = { @@ -274,6 +331,17 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8797 = { .reg = &mwifiex_reg_sd87xx, .max_ports = 16, .mp_agg_pkt_limit = 8, + .supports_sdio_new_mode = false, + .has_control_mask = true, +}; + +static const struct mwifiex_sdio_device mwifiex_sdio_sd8897 = { + .firmware = SD8897_DEFAULT_FW_NAME, + .reg = &mwifiex_reg_sd8897, + .max_ports = 32, + .mp_agg_pkt_limit = 16, + .supports_sdio_new_mode = true, + .has_control_mask = false, }; /* @@ -302,13 +370,23 @@ mp_rx_aggr_port_limit_reached(struct sdio_mmc_card *card) u8 tmp; if (card->curr_rd_port < card->mpa_rx.start_port) { - tmp = card->mp_agg_pkt_limit; + if (card->supports_sdio_new_mode) + tmp = card->mp_end_port >> 1; + else + tmp = card->mp_agg_pkt_limit; if (((card->max_ports - card->mpa_rx.start_port) + card->curr_rd_port) >= tmp) return true; } + if (!card->supports_sdio_new_mode) + return false; + + if ((card->curr_rd_port - card->mpa_rx.start_port) >= + (card->mp_end_port >> 1)) + return true; + return false; } @@ -318,13 +396,23 @@ mp_tx_aggr_port_limit_reached(struct sdio_mmc_card *card) u16 tmp; if (card->curr_wr_port < card->mpa_tx.start_port) { - tmp = card->mp_agg_pkt_limit; + if (card->supports_sdio_new_mode) + tmp = card->mp_end_port >> 1; + else + tmp = card->mp_agg_pkt_limit; if (((card->max_ports - card->mpa_tx.start_port) + card->curr_wr_port) >= tmp) return true; } + if (!card->supports_sdio_new_mode) + return false; + + if ((card->curr_wr_port - card->mpa_tx.start_port) >= + (card->mp_end_port >> 1)) + return true; + return false; } @@ -337,11 +425,14 @@ static inline void mp_rx_aggr_setup(struct sdio_mmc_card *card, if (!card->mpa_rx.pkt_cnt) card->mpa_rx.start_port = port; - if (card->mpa_rx.start_port <= port) - card->mpa_rx.ports |= 1 << (card->mpa_rx.pkt_cnt); - else - card->mpa_rx.ports |= 1 << (card->mpa_rx.pkt_cnt + 1); - + if (card->supports_sdio_new_mode) { + card->mpa_rx.ports |= (1 << port); + } else { + if (card->mpa_rx.start_port <= port) + card->mpa_rx.ports |= 1 << (card->mpa_rx.pkt_cnt); + else + card->mpa_rx.ports |= 1 << (card->mpa_rx.pkt_cnt + 1); + } card->mpa_rx.skb_arr[card->mpa_rx.pkt_cnt] = skb; card->mpa_rx.len_arr[card->mpa_rx.pkt_cnt] = skb->len; card->mpa_rx.pkt_cnt++; -- cgit v1.2.3 From f91f5f05f99099402b807f2aa346685a0284ba48 Mon Sep 17 00:00:00 2001 From: Libo Chen Date: Tue, 21 May 2013 10:42:40 +0800 Subject: drivers/net/wireless/brcm80211/brcmfmac: add missing platform_driver owner set the owner of platform_driver, to ensure that the caller of driver holds a module refernece Signed-off-by: Libo Chen Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c index 44fa0cdbf97b..11400b39cf0b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c @@ -606,7 +606,8 @@ static int brcmf_sdio_pd_remove(struct platform_device *pdev) static struct platform_driver brcmf_sdio_pd = { .remove = brcmf_sdio_pd_remove, .driver = { - .name = BRCMFMAC_SDIO_PDATA_NAME + .name = BRCMFMAC_SDIO_PDATA_NAME, + .owner = THIS_MODULE, } }; -- cgit v1.2.3 From 61c951503be1935f24760ae6c43483c5c9529646 Mon Sep 17 00:00:00 2001 From: Peter Hüwe Date: Tue, 21 May 2013 12:42:06 +0000 Subject: net/ethernet/silan/sc92031: Use module_pci_driver to register driver Removing some boilerplate by using module_pci_driver instead of calling register and unregister in the otherwise empty init/exit functions. Signed-off-by: Peter Huewe Signed-off-by: David S. Miller --- drivers/net/ethernet/silan/sc92031.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/net/ethernet/silan/sc92031.c b/drivers/net/ethernet/silan/sc92031.c index 28f7268f1b88..5eb933c97bba 100644 --- a/drivers/net/ethernet/silan/sc92031.c +++ b/drivers/net/ethernet/silan/sc92031.c @@ -1578,19 +1578,7 @@ static struct pci_driver sc92031_pci_driver = { .resume = sc92031_resume, }; -static int __init sc92031_init(void) -{ - return pci_register_driver(&sc92031_pci_driver); -} - -static void __exit sc92031_exit(void) -{ - pci_unregister_driver(&sc92031_pci_driver); -} - -module_init(sc92031_init); -module_exit(sc92031_exit); - +module_pci_driver(sc92031_pci_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Cesar Eduardo Barros "); MODULE_DESCRIPTION("Silan SC92031 PCI Fast Ethernet Adapter driver"); -- cgit v1.2.3 From 2c21d6c98813829fad79b17460fd289109a77099 Mon Sep 17 00:00:00 2001 From: Peter Hüwe Date: Tue, 21 May 2013 12:42:07 +0000 Subject: net/ethernet/atheros/atl1c/atl1c_main: Use module_pci_driver to register driver Removing some boilerplate by using module_pci_driver instead of calling register and unregister in the otherwise empty init/exit functions. Signed-off-by: Peter Huewe Signed-off-by: David S. Miller --- drivers/net/ethernet/atheros/atl1c/atl1c_main.c | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c index 0ba900762b13..786a87483298 100644 --- a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c +++ b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c @@ -2755,27 +2755,4 @@ static struct pci_driver atl1c_driver = { .driver.pm = &atl1c_pm_ops, }; -/** - * atl1c_init_module - Driver Registration Routine - * - * atl1c_init_module is the first routine called when the driver is - * loaded. All it does is register with the PCI subsystem. - */ -static int __init atl1c_init_module(void) -{ - return pci_register_driver(&atl1c_driver); -} - -/** - * atl1c_exit_module - Driver Exit Cleanup Routine - * - * atl1c_exit_module is called just before the driver is removed - * from memory. - */ -static void __exit atl1c_exit_module(void) -{ - pci_unregister_driver(&atl1c_driver); -} - -module_init(atl1c_init_module); -module_exit(atl1c_exit_module); +module_pci_driver(atl1c_driver); -- cgit v1.2.3 From 687df5d489cd1ddf7d2d231bd824f6b687bfb26b Mon Sep 17 00:00:00 2001 From: Peter Hüwe Date: Tue, 21 May 2013 12:42:08 +0000 Subject: net/ethernet/atheros/atl1e/atl1e_main: Use module_pci_driver to register driver Removing some boilerplate by using module_pci_driver instead of calling register and unregister in the otherwise empty init/exit functions. Signed-off-by: Peter Huewe Acked-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- drivers/net/ethernet/atheros/atl1e/atl1e_main.c | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/drivers/net/ethernet/atheros/atl1e/atl1e_main.c b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c index 0688bb82b442..895f5377ad1b 100644 --- a/drivers/net/ethernet/atheros/atl1e/atl1e_main.c +++ b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c @@ -2489,27 +2489,4 @@ static struct pci_driver atl1e_driver = { .err_handler = &atl1e_err_handler }; -/** - * atl1e_init_module - Driver Registration Routine - * - * atl1e_init_module is the first routine called when the driver is - * loaded. All it does is register with the PCI subsystem. - */ -static int __init atl1e_init_module(void) -{ - return pci_register_driver(&atl1e_driver); -} - -/** - * atl1e_exit_module - Driver Exit Cleanup Routine - * - * atl1e_exit_module is called just before the driver is removed - * from memory. - */ -static void __exit atl1e_exit_module(void) -{ - pci_unregister_driver(&atl1e_driver); -} - -module_init(atl1e_init_module); -module_exit(atl1e_exit_module); +module_pci_driver(atl1e_driver); -- cgit v1.2.3 From c996f4e8f053ddab28fa196e5b18bdd93e8ee3a2 Mon Sep 17 00:00:00 2001 From: Peter Hüwe Date: Tue, 21 May 2013 12:42:09 +0000 Subject: net/ethernet/atheros/atlx/atl1: Use module_pci_driver to register driver Removing some boilerplate by using module_pci_driver instead of calling register and unregister in the otherwise empty init/exit functions. Signed-off-by: Peter Huewe Signed-off-by: David S. Miller --- drivers/net/ethernet/atheros/atlx/atl1.c | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/drivers/net/ethernet/atheros/atlx/atl1.c b/drivers/net/ethernet/atheros/atlx/atl1.c index fa0915f3999b..538211d6f7d9 100644 --- a/drivers/net/ethernet/atheros/atlx/atl1.c +++ b/drivers/net/ethernet/atheros/atlx/atl1.c @@ -3145,31 +3145,6 @@ static struct pci_driver atl1_driver = { .driver.pm = &atl1_pm_ops, }; -/** - * atl1_exit_module - Driver Exit Cleanup Routine - * - * atl1_exit_module is called just before the driver is removed - * from memory. - */ -static void __exit atl1_exit_module(void) -{ - pci_unregister_driver(&atl1_driver); -} - -/** - * atl1_init_module - Driver Registration Routine - * - * atl1_init_module is the first routine called when the driver is - * loaded. All it does is register with the PCI subsystem. - */ -static int __init atl1_init_module(void) -{ - return pci_register_driver(&atl1_driver); -} - -module_init(atl1_init_module); -module_exit(atl1_exit_module); - struct atl1_stats { char stat_string[ETH_GSTRING_LEN]; int sizeof_stat; @@ -3705,3 +3680,5 @@ static const struct ethtool_ops atl1_ethtool_ops = { .get_ethtool_stats = atl1_get_ethtool_stats, .get_sset_count = atl1_get_sset_count, }; + +module_pci_driver(atl1_driver); -- cgit v1.2.3 From 52a58f9df12ab03bda432b398a4bdfcc9ed2b414 Mon Sep 17 00:00:00 2001 From: Peter Hüwe Date: Tue, 21 May 2013 12:42:10 +0000 Subject: net/ethernet/sis/sis190: Use module_pci_driver to register driver Removing some boilerplate by using module_pci_driver instead of calling register and unregister in the otherwise empty init/exit functions. Signed-off-by: Peter Huewe Signed-off-by: David S. Miller --- drivers/net/ethernet/sis/sis190.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/net/ethernet/sis/sis190.c b/drivers/net/ethernet/sis/sis190.c index 9a9c379420d1..02df0894690d 100644 --- a/drivers/net/ethernet/sis/sis190.c +++ b/drivers/net/ethernet/sis/sis190.c @@ -1934,15 +1934,4 @@ static struct pci_driver sis190_pci_driver = { .remove = sis190_remove_one, }; -static int __init sis190_init_module(void) -{ - return pci_register_driver(&sis190_pci_driver); -} - -static void __exit sis190_cleanup_module(void) -{ - pci_unregister_driver(&sis190_pci_driver); -} - -module_init(sis190_init_module); -module_exit(sis190_cleanup_module); +module_pci_driver(sis190_pci_driver); -- cgit v1.2.3 From 31d60ebfbe6792068e8951741f92f40bdea97837 Mon Sep 17 00:00:00 2001 From: Peter Hüwe Date: Tue, 21 May 2013 12:42:11 +0000 Subject: net/ethernet/dec/tulip/xircom_cb: Use module_pci_driver to register driver Removing some boilerplate by using module_pci_driver instead of calling register and unregister in the otherwise empty init/exit functions. Signed-off-by: Peter Huewe Reviewed-by: Grant Grundler Signed-off-by: David S. Miller --- drivers/net/ethernet/dec/tulip/xircom_cb.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/net/ethernet/dec/tulip/xircom_cb.c b/drivers/net/ethernet/dec/tulip/xircom_cb.c index cdbcd1643141..9b84cb04fe5f 100644 --- a/drivers/net/ethernet/dec/tulip/xircom_cb.c +++ b/drivers/net/ethernet/dec/tulip/xircom_cb.c @@ -1171,16 +1171,4 @@ investigate_write_descriptor(struct net_device *dev, } } -static int __init xircom_init(void) -{ - return pci_register_driver(&xircom_ops); -} - -static void __exit xircom_exit(void) -{ - pci_unregister_driver(&xircom_ops); -} - -module_init(xircom_init) -module_exit(xircom_exit) - +module_pci_driver(xircom_ops); -- cgit v1.2.3 From b6f5721087e72d11ff3daab565afddc5918c024b Mon Sep 17 00:00:00 2001 From: Peter Hüwe Date: Tue, 21 May 2013 12:42:12 +0000 Subject: net/ethernet/toshiba/tc35815: Use module_pci_driver to register driver Removing some boilerplate by using module_pci_driver instead of calling register and unregister in the otherwise empty init/exit functions. Signed-off-by: Peter Huewe Signed-off-by: David S. Miller --- drivers/net/ethernet/toshiba/tc35815.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/net/ethernet/toshiba/tc35815.c b/drivers/net/ethernet/toshiba/tc35815.c index fe256094db35..a971b9cca564 100644 --- a/drivers/net/ethernet/toshiba/tc35815.c +++ b/drivers/net/ethernet/toshiba/tc35815.c @@ -2209,18 +2209,6 @@ MODULE_PARM_DESC(speed, "0:auto, 10:10Mbps, 100:100Mbps"); module_param_named(duplex, options.duplex, int, 0); MODULE_PARM_DESC(duplex, "0:auto, 1:half, 2:full"); -static int __init tc35815_init_module(void) -{ - return pci_register_driver(&tc35815_pci_driver); -} - -static void __exit tc35815_cleanup_module(void) -{ - pci_unregister_driver(&tc35815_pci_driver); -} - -module_init(tc35815_init_module); -module_exit(tc35815_cleanup_module); - +module_pci_driver(tc35815_pci_driver); MODULE_DESCRIPTION("TOSHIBA TC35815 PCI 10M/100M Ethernet driver"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 43d426c3f938b9ac21f452fcd22956ed2f526f4f Mon Sep 17 00:00:00 2001 From: Peter Hüwe Date: Tue, 21 May 2013 12:42:13 +0000 Subject: net/ethernet/icplus/ipg: Use module_pci_driver to register driver Removing some boilerplate by using module_pci_driver instead of calling register and unregister in the otherwise empty init/exit functions. Signed-off-by: Peter Huewe Signed-off-by: David S. Miller --- drivers/net/ethernet/icplus/ipg.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/net/ethernet/icplus/ipg.c b/drivers/net/ethernet/icplus/ipg.c index 068d78151658..1fde90b96685 100644 --- a/drivers/net/ethernet/icplus/ipg.c +++ b/drivers/net/ethernet/icplus/ipg.c @@ -2298,15 +2298,4 @@ static struct pci_driver ipg_pci_driver = { .remove = ipg_remove, }; -static int __init ipg_init_module(void) -{ - return pci_register_driver(&ipg_pci_driver); -} - -static void __exit ipg_exit_module(void) -{ - pci_unregister_driver(&ipg_pci_driver); -} - -module_init(ipg_init_module); -module_exit(ipg_exit_module); +module_pci_driver(ipg_pci_driver); -- cgit v1.2.3 From e6d20fca76cfe4a36f8dbaac6b3847e6dad63e4c Mon Sep 17 00:00:00 2001 From: Peter Hüwe Date: Tue, 21 May 2013 12:42:14 +0000 Subject: net/ethernet/alteon/acenic: Use module_pci_driver to register driver Removing some boilerplate by using module_pci_driver instead of calling register and unregister in the otherwise empty init/exit functions. Signed-off-by: Peter Huewe Signed-off-by: David S. Miller --- drivers/net/ethernet/alteon/acenic.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/alteon/acenic.c b/drivers/net/ethernet/alteon/acenic.c index b7894f8af9d1..219be1bf3cfc 100644 --- a/drivers/net/ethernet/alteon/acenic.c +++ b/drivers/net/ethernet/alteon/acenic.c @@ -702,19 +702,6 @@ static struct pci_driver acenic_pci_driver = { .remove = acenic_remove_one, }; -static int __init acenic_init(void) -{ - return pci_register_driver(&acenic_pci_driver); -} - -static void __exit acenic_exit(void) -{ - pci_unregister_driver(&acenic_pci_driver); -} - -module_init(acenic_init); -module_exit(acenic_exit); - static void ace_free_descriptors(struct net_device *dev) { struct ace_private *ap = netdev_priv(dev); @@ -3199,3 +3186,5 @@ static int read_eeprom_byte(struct net_device *dev, unsigned long offset) ap->name, offset); goto out; } + +module_pci_driver(acenic_pci_driver); -- cgit v1.2.3 From 5a4123f39ef14fc7c043f1611ed3b20b69325146 Mon Sep 17 00:00:00 2001 From: Peter Hüwe Date: Tue, 21 May 2013 12:58:05 +0000 Subject: net/ethernet/broadcom/bnx2: Use module_pci_driver to register driver Removing some boilerplate by using module_pci_driver instead of calling register and unregister in the otherwise empty init/exit functions. Signed-off-by: Peter Huewe Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2.c | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnx2.c b/drivers/net/ethernet/broadcom/bnx2.c index 5d204492c603..1a1b23eb13dc 100644 --- a/drivers/net/ethernet/broadcom/bnx2.c +++ b/drivers/net/ethernet/broadcom/bnx2.c @@ -8764,18 +8764,4 @@ static struct pci_driver bnx2_pci_driver = { .err_handler = &bnx2_err_handler, }; -static int __init bnx2_init(void) -{ - return pci_register_driver(&bnx2_pci_driver); -} - -static void __exit bnx2_cleanup(void) -{ - pci_unregister_driver(&bnx2_pci_driver); -} - -module_init(bnx2_init); -module_exit(bnx2_cleanup); - - - +module_pci_driver(bnx2_pci_driver); -- cgit v1.2.3 From 8dbb0dc2cb16096efe998c576170e41dc6e8b64f Mon Sep 17 00:00:00 2001 From: Peter Hüwe Date: Tue, 21 May 2013 12:58:06 +0000 Subject: net/ethernet/broadcom/tg3: Use module_pci_driver to register driver Removing some boilerplate by using module_pci_driver instead of calling register and unregister in the otherwise empty init/exit functions. Signed-off-by: Peter Huewe Acked-by: Nithin Nayak Sujir Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/tg3.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index fb06aa120293..4f0e3fe99e17 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -17804,15 +17804,4 @@ static struct pci_driver tg3_driver = { .driver.pm = &tg3_pm_ops, }; -static int __init tg3_init(void) -{ - return pci_register_driver(&tg3_driver); -} - -static void __exit tg3_cleanup(void) -{ - pci_unregister_driver(&tg3_driver); -} - -module_init(tg3_init); -module_exit(tg3_cleanup); +module_pci_driver(tg3_driver); -- cgit v1.2.3 From e0fc4441f98e19d8120f2a385c9e75bc55369b81 Mon Sep 17 00:00:00 2001 From: Peter Hüwe Date: Tue, 21 May 2013 12:58:07 +0000 Subject: net/ethernet/sgi/ioc3-eth: Use module_pci_driver to register driver Removing some boilerplate by using module_pci_driver instead of calling register and unregister in the otherwise empty init/exit functions. Signed-off-by: Peter Huewe Signed-off-by: David S. Miller --- drivers/net/ethernet/sgi/ioc3-eth.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/net/ethernet/sgi/ioc3-eth.c b/drivers/net/ethernet/sgi/ioc3-eth.c index 7ed08c32a9c5..ffa78432164d 100644 --- a/drivers/net/ethernet/sgi/ioc3-eth.c +++ b/drivers/net/ethernet/sgi/ioc3-eth.c @@ -1398,16 +1398,6 @@ static struct pci_driver ioc3_driver = { .remove = ioc3_remove_one, }; -static int __init ioc3_init_module(void) -{ - return pci_register_driver(&ioc3_driver); -} - -static void __exit ioc3_cleanup_module(void) -{ - pci_unregister_driver(&ioc3_driver); -} - static int ioc3_start_xmit(struct sk_buff *skb, struct net_device *dev) { unsigned long data; @@ -1677,9 +1667,7 @@ static void ioc3_set_multicast_list(struct net_device *dev) netif_wake_queue(dev); /* Let us get going again. */ } +module_pci_driver(ioc3_driver); MODULE_AUTHOR("Ralf Baechle "); MODULE_DESCRIPTION("SGI IOC3 Ethernet driver"); MODULE_LICENSE("GPL"); - -module_init(ioc3_init_module); -module_exit(ioc3_cleanup_module); -- cgit v1.2.3 From 70a611debe964ae508d67528d82ca7fa0715f5a3 Mon Sep 17 00:00:00 2001 From: Peter Hüwe Date: Tue, 21 May 2013 12:58:08 +0000 Subject: net/ethernet/qlogic/qlge/qlge_main: Use module_pci_driver to register driver Removing some boilerplate by using module_pci_driver instead of calling register and unregister in the otherwise empty init/exit functions. Signed-off-by: Peter Huewe Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qlge/qlge_main.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qlge/qlge_main.c b/drivers/net/ethernet/qlogic/qlge/qlge_main.c index 50235d201592..e04d471acb10 100644 --- a/drivers/net/ethernet/qlogic/qlge/qlge_main.c +++ b/drivers/net/ethernet/qlogic/qlge/qlge_main.c @@ -4945,15 +4945,4 @@ static struct pci_driver qlge_driver = { .err_handler = &qlge_err_handler }; -static int __init qlge_init_module(void) -{ - return pci_register_driver(&qlge_driver); -} - -static void __exit qlge_exit(void) -{ - pci_unregister_driver(&qlge_driver); -} - -module_init(qlge_init_module); -module_exit(qlge_exit); +module_pci_driver(qlge_driver); -- cgit v1.2.3 From 5119ad0b7388641815bad0bacdc5133532d24685 Mon Sep 17 00:00:00 2001 From: Peter Hüwe Date: Tue, 21 May 2013 12:58:09 +0000 Subject: net/ethernet/sun/sungem: Use module_pci_driver to register driver Removing some boilerplate by using module_pci_driver instead of calling register and unregister in the otherwise empty init/exit functions. Signed-off-by: Peter Huewe Signed-off-by: David S. Miller --- drivers/net/ethernet/sun/sungem.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/net/ethernet/sun/sungem.c b/drivers/net/ethernet/sun/sungem.c index 5f3f9d52757d..e62df2b81302 100644 --- a/drivers/net/ethernet/sun/sungem.c +++ b/drivers/net/ethernet/sun/sungem.c @@ -3028,15 +3028,4 @@ static struct pci_driver gem_driver = { #endif /* CONFIG_PM */ }; -static int __init gem_init(void) -{ - return pci_register_driver(&gem_driver); -} - -static void __exit gem_cleanup(void) -{ - pci_unregister_driver(&gem_driver); -} - -module_init(gem_init); -module_exit(gem_cleanup); +module_pci_driver(gem_driver); -- cgit v1.2.3 From a46e6ccdd1cd557cbb9a24022ee027d09a4bd625 Mon Sep 17 00:00:00 2001 From: Peter Hüwe Date: Tue, 21 May 2013 12:58:10 +0000 Subject: net/ethernet/amd/amd8111e: Use module_pci_driver to register driver Removing some boilerplate by using module_pci_driver instead of calling register and unregister in the otherwise empty init/exit functions. Signed-off-by: Peter Huewe Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/amd8111e.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/net/ethernet/amd/amd8111e.c b/drivers/net/ethernet/amd/amd8111e.c index 8e6b665a6726..bc71aec1159d 100644 --- a/drivers/net/ethernet/amd/amd8111e.c +++ b/drivers/net/ethernet/amd/amd8111e.c @@ -1981,15 +1981,4 @@ static struct pci_driver amd8111e_driver = { .resume = amd8111e_resume }; -static int __init amd8111e_init(void) -{ - return pci_register_driver(&amd8111e_driver); -} - -static void __exit amd8111e_cleanup(void) -{ - pci_unregister_driver(&amd8111e_driver); -} - -module_init(amd8111e_init); -module_exit(amd8111e_cleanup); +module_pci_driver(amd8111e_driver); -- cgit v1.2.3 From ff7150550d9e3d9dd9ceadebbe8ee45e74c1ffdb Mon Sep 17 00:00:00 2001 From: Peter Hüwe Date: Tue, 21 May 2013 13:42:53 +0000 Subject: net/hippi/rrunner: Use module_pci_driver to register driver Removing some boilerplate by using module_pci_driver instead of calling register and unregister in the otherwise empty init/exit functions. Signed-off-by: Peter Huewe Signed-off-by: David S. Miller --- drivers/net/hippi/rrunner.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/net/hippi/rrunner.c b/drivers/net/hippi/rrunner.c index 3c4d6274bb9b..00ed75155ce8 100644 --- a/drivers/net/hippi/rrunner.c +++ b/drivers/net/hippi/rrunner.c @@ -1686,15 +1686,4 @@ static struct pci_driver rr_driver = { .remove = rr_remove_one, }; -static int __init rr_init_module(void) -{ - return pci_register_driver(&rr_driver); -} - -static void __exit rr_cleanup_module(void) -{ - pci_unregister_driver(&rr_driver); -} - -module_init(rr_init_module); -module_exit(rr_cleanup_module); +module_pci_driver(rr_driver); -- cgit v1.2.3 From 30d05f9ef7c2caa7efd8ca5c0698ff5e9d5542ae Mon Sep 17 00:00:00 2001 From: Peter Hüwe Date: Tue, 21 May 2013 13:42:54 +0000 Subject: net/fddi/skfp/skfddi: Use module_pci_driver to register driver Removing some boilerplate by using module_pci_driver instead of calling register and unregister in the otherwise empty init/exit functions. Signed-off-by: Peter Huewe Signed-off-by: David S. Miller --- drivers/net/fddi/skfp/skfddi.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/net/fddi/skfp/skfddi.c b/drivers/net/fddi/skfp/skfddi.c index d5bd563ac131..f5d7305a5784 100644 --- a/drivers/net/fddi/skfp/skfddi.c +++ b/drivers/net/fddi/skfp/skfddi.c @@ -2246,15 +2246,4 @@ static struct pci_driver skfddi_pci_driver = { .remove = skfp_remove_one, }; -static int __init skfd_init(void) -{ - return pci_register_driver(&skfddi_pci_driver); -} - -static void __exit skfd_exit(void) -{ - pci_unregister_driver(&skfddi_pci_driver); -} - -module_init(skfd_init); -module_exit(skfd_exit); +module_pci_driver(skfddi_pci_driver); -- cgit v1.2.3 From 65c21912b5b977f32a12336e87fe32f36105b981 Mon Sep 17 00:00:00 2001 From: Peter Hüwe Date: Tue, 21 May 2013 13:42:55 +0000 Subject: net/ethernet/chelsio/cxgb/cxgb2: Use module_pci_driver to register driver Removing some boilerplate by using module_pci_driver instead of calling register and unregister in the otherwise empty init/exit functions. The name of the pci_driver struct had to be changed in order to prevent a build failure. Signed-off-by: Peter Huewe Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb/cxgb2.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/chelsio/cxgb/cxgb2.c b/drivers/net/ethernet/chelsio/cxgb/cxgb2.c index 9624cfe7df57..d7048db9863d 100644 --- a/drivers/net/ethernet/chelsio/cxgb/cxgb2.c +++ b/drivers/net/ethernet/chelsio/cxgb/cxgb2.c @@ -1351,22 +1351,11 @@ static void remove_one(struct pci_dev *pdev) t1_sw_reset(pdev); } -static struct pci_driver driver = { +static struct pci_driver cxgb_pci_driver = { .name = DRV_NAME, .id_table = t1_pci_tbl, .probe = init_one, .remove = remove_one, }; -static int __init t1_init_module(void) -{ - return pci_register_driver(&driver); -} - -static void __exit t1_cleanup_module(void) -{ - pci_unregister_driver(&driver); -} - -module_init(t1_init_module); -module_exit(t1_cleanup_module); +module_pci_driver(cxgb_pci_driver); -- cgit v1.2.3 From 4f45c40f2c018e5c278988521a74fd5ee06fb819 Mon Sep 17 00:00:00 2001 From: Peter Hüwe Date: Tue, 21 May 2013 13:42:56 +0000 Subject: net/ethernet/nvidia/forcedeth: Use module_pci_driver to register driver Removing some boilerplate by using module_pci_driver instead of calling register and unregister in the otherwise empty init/exit functions. The name of the pci_driver struct had to be changed in order to prevent a build failure. Signed-off-by: Peter Huewe Signed-off-by: David S. Miller --- drivers/net/ethernet/nvidia/forcedeth.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/drivers/net/ethernet/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c index b003fe53c8e2..098b96dad66f 100644 --- a/drivers/net/ethernet/nvidia/forcedeth.c +++ b/drivers/net/ethernet/nvidia/forcedeth.c @@ -6340,7 +6340,7 @@ static DEFINE_PCI_DEVICE_TABLE(pci_tbl) = { {0,}, }; -static struct pci_driver driver = { +static struct pci_driver forcedeth_pci_driver = { .name = DRV_NAME, .id_table = pci_tbl, .probe = nv_probe, @@ -6349,16 +6349,6 @@ static struct pci_driver driver = { .driver.pm = NV_PM_OPS, }; -static int __init init_nic(void) -{ - return pci_register_driver(&driver); -} - -static void __exit exit_nic(void) -{ - pci_unregister_driver(&driver); -} - module_param(max_interrupt_work, int, 0); MODULE_PARM_DESC(max_interrupt_work, "forcedeth maximum events handled per interrupt"); module_param(optimization_mode, int, 0); @@ -6379,11 +6369,8 @@ module_param(debug_tx_timeout, bool, 0); MODULE_PARM_DESC(debug_tx_timeout, "Dump tx related registers and ring when tx_timeout happens"); +module_pci_driver(forcedeth_pci_driver); MODULE_AUTHOR("Manfred Spraul "); MODULE_DESCRIPTION("Reverse Engineered nForce ethernet driver"); MODULE_LICENSE("GPL"); - MODULE_DEVICE_TABLE(pci, pci_tbl); - -module_init(init_nic); -module_exit(exit_nic); -- cgit v1.2.3 From 1c8ad5bfa2be5025b0c81e3c2decd0574d453ab1 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Tue, 21 May 2013 21:52:54 +0000 Subject: bridge: use the bridge IP addr as source addr for querier Quote from Adam: "If it is believed that the use of 0.0.0.0 as the IP address is what is causing strange behaviour on other devices then is there a good reason that a bridge rather than a router shouldn't be the active querier? If not then using the bridge IP address and having the querier enabled by default may be a reasonable solution (provided that our querier obeys the election rules and shuts up if it sees a query from a lower IP address that isn't 0.0.0.0). Just because a device is the elected querier for IGMP doesn't appear to mean it is required to perform any other routing functions." And introduce a new troggle for it, as suggested by Herbert. Suggested-by: Adam Baker Cc: Herbert Xu Cc: Stephen Hemminger Cc: "David S. Miller" Cc: Adam Baker Signed-off-by: Cong Wang Acked-by: Herbert Xu Signed-off-by: David S. Miller --- net/bridge/br_multicast.c | 5 ++++- net/bridge/br_private.h | 1 + net/bridge/br_sysfs_br.c | 26 ++++++++++++++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 81f2389f78eb..247514793101 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #if IS_ENABLED(CONFIG_IPV6) #include @@ -381,7 +382,8 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge *br, iph->frag_off = htons(IP_DF); iph->ttl = 1; iph->protocol = IPPROTO_IGMP; - iph->saddr = 0; + iph->saddr = br->multicast_query_use_ifaddr ? + inet_select_addr(br->dev, 0, RT_SCOPE_LINK) : 0; iph->daddr = htonl(INADDR_ALLHOSTS_GROUP); ((u8 *)&iph[1])[0] = IPOPT_RA; ((u8 *)&iph[1])[1] = 4; @@ -1618,6 +1620,7 @@ void br_multicast_init(struct net_bridge *br) br->multicast_router = 1; br->multicast_querier = 0; + br->multicast_query_use_ifaddr = 0; br->multicast_last_member_count = 2; br->multicast_startup_query_count = 2; diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index d2c043a857b6..e260710a01d4 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -249,6 +249,7 @@ struct net_bridge u8 multicast_disabled:1; u8 multicast_querier:1; + u8 multicast_query_use_ifaddr:1; u32 hash_elasticity; u32 hash_max; diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c index 8baa9c08e1a4..394bb96b6087 100644 --- a/net/bridge/br_sysfs_br.c +++ b/net/bridge/br_sysfs_br.c @@ -375,6 +375,31 @@ static ssize_t store_multicast_snooping(struct device *d, static DEVICE_ATTR(multicast_snooping, S_IRUGO | S_IWUSR, show_multicast_snooping, store_multicast_snooping); +static ssize_t show_multicast_query_use_ifaddr(struct device *d, + struct device_attribute *attr, + char *buf) +{ + struct net_bridge *br = to_bridge(d); + return sprintf(buf, "%d\n", br->multicast_query_use_ifaddr); +} + +static int set_query_use_ifaddr(struct net_bridge *br, unsigned long val) +{ + br->multicast_query_use_ifaddr = !!val; + return 0; +} + +static ssize_t +store_multicast_query_use_ifaddr(struct device *d, + struct device_attribute *attr, + const char *buf, size_t len) +{ + return store_bridge_parm(d, buf, len, set_query_use_ifaddr); +} +static DEVICE_ATTR(multicast_query_use_ifaddr, S_IRUGO | S_IWUSR, + show_multicast_query_use_ifaddr, + store_multicast_query_use_ifaddr); + static ssize_t show_multicast_querier(struct device *d, struct device_attribute *attr, char *buf) @@ -734,6 +759,7 @@ static struct attribute *bridge_attrs[] = { &dev_attr_multicast_router.attr, &dev_attr_multicast_snooping.attr, &dev_attr_multicast_querier.attr, + &dev_attr_multicast_query_use_ifaddr.attr, &dev_attr_hash_elasticity.attr, &dev_attr_hash_max.attr, &dev_attr_multicast_last_member_count.attr, -- cgit v1.2.3 From 9f00b2e7cf241fa389733d41b615efdaa2cb0f5b Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Tue, 21 May 2013 21:52:55 +0000 Subject: bridge: only expire the mdb entry when query is received Currently we arm the expire timer when the mdb entry is added, however, this causes problem when there is no querier sent out after that. So we should only arm the timer when a corresponding query is received, as suggested by Herbert. And he also mentioned "if there is no querier then group subscriptions shouldn't expire. There has to be at least one querier in the network for this thing to work. Otherwise it just degenerates into a non-snooping switch, which is OK." Cc: Herbert Xu Cc: Stephen Hemminger Cc: "David S. Miller" Cc: Adam Baker Signed-off-by: Cong Wang Acked-by: Herbert Xu Signed-off-by: David S. Miller --- net/bridge/br_multicast.c | 39 ++++++++++++--------------------------- net/bridge/br_private.h | 1 + 2 files changed, 13 insertions(+), 27 deletions(-) diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 247514793101..40bda804fbd9 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -617,8 +617,6 @@ rehash: mp->br = br; mp->addr = *group; - setup_timer(&mp->timer, br_multicast_group_expired, - (unsigned long)mp); hlist_add_head_rcu(&mp->hlist[mdb->ver], &mdb->mhash[hash]); mdb->size++; @@ -656,7 +654,6 @@ static int br_multicast_add_group(struct net_bridge *br, struct net_bridge_mdb_entry *mp; struct net_bridge_port_group *p; struct net_bridge_port_group __rcu **pp; - unsigned long now = jiffies; int err; spin_lock(&br->multicast_lock); @@ -671,7 +668,6 @@ static int br_multicast_add_group(struct net_bridge *br, if (!port) { mp->mglist = true; - mod_timer(&mp->timer, now + br->multicast_membership_interval); goto out; } @@ -679,7 +675,7 @@ static int br_multicast_add_group(struct net_bridge *br, (p = mlock_dereference(*pp, br)) != NULL; pp = &p->next) { if (p->port == port) - goto found; + goto out; if ((unsigned long)p->port < (unsigned long)port) break; } @@ -690,8 +686,6 @@ static int br_multicast_add_group(struct net_bridge *br, rcu_assign_pointer(*pp, p); br_mdb_notify(br->dev, port, group, RTM_NEWMDB); -found: - mod_timer(&p->timer, now + br->multicast_membership_interval); out: err = 0; @@ -1131,6 +1125,10 @@ static int br_ip4_multicast_query(struct net_bridge *br, if (!mp) goto out; + setup_timer(&mp->timer, br_multicast_group_expired, (unsigned long)mp); + mod_timer(&mp->timer, now + br->multicast_membership_interval); + mp->timer_armed = true; + max_delay *= br->multicast_last_member_count; if (mp->mglist && @@ -1205,6 +1203,10 @@ static int br_ip6_multicast_query(struct net_bridge *br, if (!mp) goto out; + setup_timer(&mp->timer, br_multicast_group_expired, (unsigned long)mp); + mod_timer(&mp->timer, now + br->multicast_membership_interval); + mp->timer_armed = true; + max_delay *= br->multicast_last_member_count; if (mp->mglist && (timer_pending(&mp->timer) ? @@ -1263,7 +1265,7 @@ static void br_multicast_leave_group(struct net_bridge *br, call_rcu_bh(&p->rcu, br_multicast_free_pg); br_mdb_notify(br->dev, port, group, RTM_DELMDB); - if (!mp->ports && !mp->mglist && + if (!mp->ports && !mp->mglist && mp->timer_armed && netif_running(br->dev)) mod_timer(&mp->timer, jiffies); } @@ -1275,30 +1277,12 @@ static void br_multicast_leave_group(struct net_bridge *br, br->multicast_last_member_interval; if (!port) { - if (mp->mglist && + if (mp->mglist && mp->timer_armed && (timer_pending(&mp->timer) ? time_after(mp->timer.expires, time) : try_to_del_timer_sync(&mp->timer) >= 0)) { mod_timer(&mp->timer, time); } - - goto out; - } - - for (p = mlock_dereference(mp->ports, br); - p != NULL; - p = mlock_dereference(p->next, br)) { - if (p->port != port) - continue; - - if (!hlist_unhashed(&p->mglist) && - (timer_pending(&p->timer) ? - time_after(p->timer.expires, time) : - try_to_del_timer_sync(&p->timer) >= 0)) { - mod_timer(&p->timer, time); - } - - break; } out: @@ -1674,6 +1658,7 @@ void br_multicast_stop(struct net_bridge *br) hlist_for_each_entry_safe(mp, n, &mdb->mhash[i], hlist[ver]) { del_timer(&mp->timer); + mp->timer_armed = false; call_rcu_bh(&mp->rcu, br_multicast_free_group); } } diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index e260710a01d4..1b0ac95a5c37 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -112,6 +112,7 @@ struct net_bridge_mdb_entry struct timer_list timer; struct br_ip addr; bool mglist; + bool timer_armed; }; struct net_bridge_mdb_htable -- cgit v1.2.3 From 6b7df111ece130fa979a0c4f58e53674c1e47d3e Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Tue, 21 May 2013 21:52:56 +0000 Subject: bridge: send query as soon as leave is received Continue sending queries when leave is received if the user marks it as a querier. Cc: Herbert Xu Cc: Stephen Hemminger Cc: "David S. Miller" Cc: Adam Baker Signed-off-by: Cong Wang Acked-by: Herbert Xu Signed-off-by: David S. Miller --- net/bridge/br_multicast.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 40bda804fbd9..37a467697967 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -1250,6 +1250,32 @@ static void br_multicast_leave_group(struct net_bridge *br, if (!mp) goto out; + if (br->multicast_querier && + !timer_pending(&br->multicast_querier_timer)) { + __br_multicast_send_query(br, port, &mp->addr); + + time = jiffies + br->multicast_last_member_count * + br->multicast_last_member_interval; + mod_timer(port ? &port->multicast_query_timer : + &br->multicast_query_timer, time); + + for (p = mlock_dereference(mp->ports, br); + p != NULL; + p = mlock_dereference(p->next, br)) { + if (p->port != port) + continue; + + if (!hlist_unhashed(&p->mglist) && + (timer_pending(&p->timer) ? + time_after(p->timer.expires, time) : + try_to_del_timer_sync(&p->timer) >= 0)) { + mod_timer(&p->timer, time); + } + + break; + } + } + if (port && (port->flags & BR_MULTICAST_FAST_LEAVE)) { struct net_bridge_port_group __rcu **pp; -- cgit v1.2.3 From 1cdbcb7957cf9e5f841dbcde9b38fd18a804208b Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Sun, 19 May 2013 15:46:49 +0000 Subject: net: Loosen constraints for recalculating checksum in skb_segment() This is a generic solution to resolve a specific problem that I have observed. If the encapsulation of an skb changes then ability to offload checksums may also change. In particular it may be necessary to perform checksumming in software. An example of such a case is where a non-GRE packet is received but is to be encapsulated and transmitted as GRE. Another example relates to my proposed support for for packets that are non-MPLS when received but MPLS when transmitted. The cost of this change is that the value of the csum variable may be checked when it previously was not. In the case where the csum variable is true this is pure overhead. In the case where the csum variable is false it leads to software checksumming, which I believe also leads to correct checksums in transmitted packets for the cases described above. Further analysis: This patch relies on the return value of can_checksum_protocol() being correct and in turn the return value of skb_network_protocol(), used to provide the protocol parameter of can_checksum_protocol(), being correct. It also relies on the features passed to skb_segment() and in turn to can_checksum_protocol() being correct. I believe that this problem has not been observed for VLANs because it appears that almost all drivers, the exception being xgbe, set vlan_features such that that the checksum offload support for VLAN packets is greater than or equal to that of non-VLAN packets. I wonder if the code in xgbe may be an oversight and the hardware does support checksumming of VLAN packets. If so it may be worth updating the vlan_features of the driver as this patch will force such checksums to be performed in software rather than hardware. Signed-off-by: Simon Horman Signed-off-by: David S. Miller --- net/core/skbuff.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index af9185d0be6a..d6298914f4e7 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -2853,7 +2853,7 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features) doffset + tnl_hlen); if (fskb != skb_shinfo(skb)->frag_list) - continue; + goto perform_csum_check; if (!sg) { nskb->ip_summed = CHECKSUM_NONE; @@ -2917,6 +2917,7 @@ skip_fraglist: nskb->len += nskb->data_len; nskb->truesize += nskb->data_len; +perform_csum_check: if (!csum) { nskb->csum = skb_checksum(nskb, doffset, nskb->len - doffset, 0); -- cgit v1.2.3 From cf1f7c6e8756646db7a0d883013cedd90eb90dd4 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 23 May 2013 00:12:53 +0200 Subject: ASoC: Fix early event callback list iteration The power_list field is used when adding a widget to a power sequence list. Use the same field when iterating the list using list_for_each_entry, otherwise we'll see undefined behavior. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/soc-dapm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 071579be7cb9..35073462d948 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1738,11 +1738,11 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) &async_domain); async_synchronize_full_domain(&async_domain); - list_for_each_entry(w, &down_list, list) { + list_for_each_entry(w, &down_list, power_list) { dapm_seq_check_event(dapm, w, SND_SOC_DAPM_WILL_PMD); } - list_for_each_entry(w, &up_list, list) { + list_for_each_entry(w, &up_list, power_list) { dapm_seq_check_event(dapm, w, SND_SOC_DAPM_WILL_PMU); } -- cgit v1.2.3 From 665ba56fdab1670ef686ec35569aa1de0ec4ef4d Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 13 May 2013 15:39:17 +0100 Subject: ARM: 7717/1: mmc: mmci: Use devm_clk_get API Converting to devm_clk_get simplifies error handling in probe and we can remove other corresponding calls to clk_put. Signed-off-by: Ulf Hansson Signed-off-by: Russell King --- drivers/mmc/host/mmci.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index f4f3038c1df0..d57ce4332f83 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1362,16 +1362,15 @@ static int mmci_probe(struct amba_device *dev, dev_dbg(mmc_dev(mmc), "designer ID = 0x%02x\n", host->hw_designer); dev_dbg(mmc_dev(mmc), "revision = 0x%01x\n", host->hw_revision); - host->clk = clk_get(&dev->dev, NULL); + host->clk = devm_clk_get(&dev->dev, NULL); if (IS_ERR(host->clk)) { ret = PTR_ERR(host->clk); - host->clk = NULL; goto host_free; } ret = clk_prepare_enable(host->clk); if (ret) - goto clk_free; + goto host_free; host->plat = plat; host->variant = variant; @@ -1576,8 +1575,6 @@ static int mmci_probe(struct amba_device *dev, iounmap(host->base); clk_disable: clk_disable_unprepare(host->clk); - clk_free: - clk_put(host->clk); host_free: mmc_free_host(mmc); rel_regions: @@ -1623,7 +1620,6 @@ static int mmci_remove(struct amba_device *dev) iounmap(host->base); clk_disable_unprepare(host->clk); - clk_put(host->clk); mmc_free_host(mmc); -- cgit v1.2.3 From c58a85090c8fbf7274af5ed0fe30828320c40ad6 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 13 May 2013 15:40:03 +0100 Subject: ARM: 7718/1: mmc: mmci: Set actual clock for debug purpose Update cclk to the acutal used value and copy it to the actual_clock variable in the mmc host for debug purpose. Signed-off-by: Fredrik Soderstedt Signed-off-by: Ulf Hansson Signed-off-by: Russell King --- drivers/mmc/host/mmci.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index d57ce4332f83..5f53cf8b8f5a 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -196,6 +196,9 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired) struct variant_data *variant = host->variant; u32 clk = variant->clkreg; + /* Make sure cclk reflects the current calculated clock */ + host->cclk = 0; + if (desired) { if (desired >= host->mclk) { clk = MCI_CLK_BYPASS; @@ -230,6 +233,9 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired) /* clk |= MCI_CLK_PWRSAVE; */ } + /* Set actual clock for debug */ + host->mmc->actual_clock = host->cclk; + if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_4) clk |= MCI_4BIT_BUS; if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_8) -- cgit v1.2.3 From 024629c62ffc25f267d57bf588cc10c96ccc0ce5 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 13 May 2013 15:40:56 +0100 Subject: ARM: 7719/1: mmc: mmci: Support for CMD23 Support added for transmission of CMD23 during multi block read or write. In order to activate this feature, MMC_CAP_CMD23 flag needs to be enabled in the capabilities field. Note that CMD23 support is mandatory to support features like reliable write, data tag, context ID, packed command. This patch is based upon a patch from Saugata Das. Signed-off-by: Ulf Hansson Acked-by: Linus Walleij Signed-off-by: Russell King --- drivers/mmc/host/mmci.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 5f53cf8b8f5a..9df8b84145b1 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -848,7 +848,7 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data, /* The error clause is handled above, success! */ data->bytes_xfered = data->blksz * data->blocks; - if (!data->stop) { + if (!data->stop || host->mrq->sbc) { mmci_request_end(host, data->mrq); } else { mmci_start_command(host, data->stop, 0); @@ -861,6 +861,7 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd, unsigned int status) { void __iomem *base = host->base; + bool sbc = (cmd == host->mrq->sbc); host->cmd = NULL; @@ -875,7 +876,7 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd, cmd->resp[3] = readl(base + MMCIRESPONSE3); } - if (!cmd->data || cmd->error) { + if ((!sbc && !cmd->data) || cmd->error) { if (host->data) { /* Terminate the DMA transfer */ if (dma_inprogress(host)) { @@ -884,7 +885,9 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd, } mmci_stop_data(host); } - mmci_request_end(host, cmd->mrq); + mmci_request_end(host, host->mrq); + } else if (sbc) { + mmci_start_command(host, host->mrq->cmd, 0); } else if (!(cmd->data->flags & MMC_DATA_READ)) { mmci_start_data(host, cmd->data); } @@ -1125,7 +1128,10 @@ static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq) if (mrq->data && mrq->data->flags & MMC_DATA_READ) mmci_start_data(host, mrq->data); - mmci_start_command(host, mrq->cmd, 0); + if (mrq->sbc) + mmci_start_command(host, mrq->sbc, 0); + else + mmci_start_command(host, mrq->cmd, 0); spin_unlock_irqrestore(&host->lock, flags); } -- cgit v1.2.3 From 1fd83f0ecf87e33ab560e8229842cf10f91552ee Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Fri, 3 May 2013 12:51:17 +0100 Subject: ARM: 7713/1: mmc: mmci: Allow MMCI to request channels with information acquired from DT Currently, if DMA information isn't passed from platform data, then DMA will not be used. This patch allows DMA information obtained though Device Tree to be used as well. Cc: Chris Ball Cc: linux-mmc@vger.kernel.org Signed-off-by: Lee Jones Signed-off-by: Russell King --- drivers/mmc/host/mmci.c | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 9df8b84145b1..c6d8b6216069 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -310,10 +310,8 @@ static void mmci_dma_setup(struct mmci_host *host) const char *rxname, *txname; dma_cap_mask_t mask; - if (!plat || !plat->dma_filter) { - dev_info(mmc_dev(host->mmc), "no DMA platform data\n"); - return; - } + host->dma_rx_channel = dma_request_slave_channel(mmc_dev(host->mmc), "rx"); + host->dma_tx_channel = dma_request_slave_channel(mmc_dev(host->mmc), "tx"); /* initialize pre request cookie */ host->next_data.cookie = 1; @@ -322,30 +320,33 @@ static void mmci_dma_setup(struct mmci_host *host) dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); - /* - * If only an RX channel is specified, the driver will - * attempt to use it bidirectionally, however if it is - * is specified but cannot be located, DMA will be disabled. - */ - if (plat->dma_rx_param) { - host->dma_rx_channel = dma_request_channel(mask, + if (plat && plat->dma_filter) { + if (!host->dma_rx_channel && plat->dma_rx_param) { + host->dma_rx_channel = dma_request_channel(mask, plat->dma_filter, plat->dma_rx_param); - /* E.g if no DMA hardware is present */ - if (!host->dma_rx_channel) - dev_err(mmc_dev(host->mmc), "no RX DMA channel\n"); - } + /* E.g if no DMA hardware is present */ + if (!host->dma_rx_channel) + dev_err(mmc_dev(host->mmc), "no RX DMA channel\n"); + } - if (plat->dma_tx_param) { - host->dma_tx_channel = dma_request_channel(mask, + if (!host->dma_tx_channel && plat->dma_tx_param) { + host->dma_tx_channel = dma_request_channel(mask, plat->dma_filter, plat->dma_tx_param); - if (!host->dma_tx_channel) - dev_warn(mmc_dev(host->mmc), "no TX DMA channel\n"); - } else { - host->dma_tx_channel = host->dma_rx_channel; + if (!host->dma_tx_channel) + dev_warn(mmc_dev(host->mmc), "no TX DMA channel\n"); + } } + /* + * If only an RX channel is specified, the driver will + * attempt to use it bidirectionally, however if it is + * is specified but cannot be located, DMA will be disabled. + */ + if (host->dma_rx_channel && !host->dma_tx_channel) + host->dma_tx_channel = host->dma_rx_channel; + if (host->dma_rx_channel) rxname = dma_chan_name(host->dma_rx_channel); else -- cgit v1.2.3 From 7c0136ef773c206e242b9718740377a45747bd70 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 14 May 2013 13:53:10 +0100 Subject: ARM: 7721/1: mmc: mmci: Fixup regulator handling for vqmmc We can not rely on regulator_is_enabled to decide whether to enable|disable the regulator. It would mean that the reference counter for it is not balanced properly. Instead keep track of our internal state by using a new flag in the host struct, so we can take correct decisions. Signed-off-by: Ulf Hansson Signed-off-by: Russell King --- drivers/mmc/host/mmci.c | 10 ++++++---- drivers/mmc/host/mmci.h | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index c6d8b6216069..a8bbdd1c4314 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1156,9 +1156,10 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (!IS_ERR(mmc->supply.vmmc)) mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); - if (!IS_ERR(mmc->supply.vqmmc) && - regulator_is_enabled(mmc->supply.vqmmc)) + if (!IS_ERR(mmc->supply.vqmmc) && host->vqmmc_enabled) { regulator_disable(mmc->supply.vqmmc); + host->vqmmc_enabled = false; + } break; case MMC_POWER_UP: @@ -1174,12 +1175,13 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) break; case MMC_POWER_ON: - if (!IS_ERR(mmc->supply.vqmmc) && - !regulator_is_enabled(mmc->supply.vqmmc)) { + if (!IS_ERR(mmc->supply.vqmmc) && !host->vqmmc_enabled) { ret = regulator_enable(mmc->supply.vqmmc); if (ret < 0) dev_err(mmc_dev(mmc), "failed to enable vqmmc regulator\n"); + else + host->vqmmc_enabled = true; } pwr |= MCI_PWR_ON; diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index 1f33ad5333a0..1383c9ce2646 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -183,6 +183,7 @@ struct mmci_host { unsigned int cclk; u32 pwr_reg; u32 clk_reg; + bool vqmmc_enabled; struct mmci_platform_data *plat; struct variant_data *variant; -- cgit v1.2.3 From 0f3ed7f75cf1a16df9309f3a9ffaf62a3fc1f0bb Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 15 May 2013 20:47:33 +0100 Subject: ARM: 7724/1: mmc: mmci: Support signal voltage switch for UHS cards Add .start_signal_voltage_switch callback to be able to support UHS cards. The voltage switch requires the optional vqmmc regulator to exist since the actual voltage switch will be performed directly on it. Signed-off-by: Ulf Hansson Signed-off-by: Russell King --- drivers/mmc/host/mmci.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index a8bbdd1c4314..cb9e562b3571 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1266,6 +1266,39 @@ static int mmci_get_cd(struct mmc_host *mmc) return status; } +static int mmci_sig_volt_switch(struct mmc_host *mmc, struct mmc_ios *ios) +{ + int ret = 0; + + if (!IS_ERR(mmc->supply.vqmmc)) { + + pm_runtime_get_sync(mmc_dev(mmc)); + + switch (ios->signal_voltage) { + case MMC_SIGNAL_VOLTAGE_330: + ret = regulator_set_voltage(mmc->supply.vqmmc, + 2700000, 3600000); + break; + case MMC_SIGNAL_VOLTAGE_180: + ret = regulator_set_voltage(mmc->supply.vqmmc, + 1700000, 1950000); + break; + case MMC_SIGNAL_VOLTAGE_120: + ret = regulator_set_voltage(mmc->supply.vqmmc, + 1100000, 1300000); + break; + } + + if (ret) + dev_warn(mmc_dev(mmc), "Voltage switch failed\n"); + + pm_runtime_mark_last_busy(mmc_dev(mmc)); + pm_runtime_put_autosuspend(mmc_dev(mmc)); + } + + return ret; +} + static irqreturn_t mmci_cd_irq(int irq, void *dev_id) { struct mmci_host *host = dev_id; @@ -1282,6 +1315,7 @@ static const struct mmc_host_ops mmci_ops = { .set_ios = mmci_set_ios, .get_ro = mmci_get_ro, .get_cd = mmci_get_cd, + .start_signal_voltage_switch = mmci_sig_volt_switch, }; #ifdef CONFIG_OF -- cgit v1.2.3 From 9cc639a20fdc0b935e55d4992f93963f95233ca4 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 15 May 2013 20:48:23 +0100 Subject: ARM: 7725/1: mmc: mmci: Cache MMCIDATACTRL register Add a cache variable in the host struct that reflects the current data in the MMCIDATACTRL register. This patch will not introduce any functional change but instead provide an easy option to keep specific bits in the register between each data transfer. Signed-off-by: Ulf Hansson Signed-off-by: Russell King --- drivers/mmc/host/mmci.c | 17 ++++++++++++++--- drivers/mmc/host/mmci.h | 1 + 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index cb9e562b3571..ccfe0bc62f78 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -188,6 +188,17 @@ static void mmci_write_pwrreg(struct mmci_host *host, u32 pwr) } } +/* + * This must be called with host->lock held + */ +static void mmci_write_datactrlreg(struct mmci_host *host, u32 datactrl) +{ + if (host->datactrl_reg != datactrl) { + host->datactrl_reg = datactrl; + writel(datactrl, host->base + MMCIDATACTRL); + } +} + /* * This must be called with host->lock held */ @@ -281,7 +292,7 @@ static void mmci_set_mask1(struct mmci_host *host, unsigned int mask) static void mmci_stop_data(struct mmci_host *host) { - writel(0, host->base + MMCIDATACTRL); + mmci_write_datactrlreg(host, 0); mmci_set_mask1(host, 0); host->data = NULL; } @@ -559,7 +570,7 @@ static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl) datactrl |= MCI_DPSM_DMAENABLE; /* Trigger the DMA transfer */ - writel(datactrl, host->base + MMCIDATACTRL); + mmci_write_datactrlreg(host, datactrl); /* * Let the MMCI say when the data is ended and it's time @@ -757,7 +768,7 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) irqmask = MCI_TXFIFOHALFEMPTYMASK; } - writel(datactrl, base + MMCIDATACTRL); + mmci_write_datactrlreg(host, datactrl); writel(readl(base + MMCIMASK0) & ~MCI_DATAENDMASK, base + MMCIMASK0); mmci_set_mask1(host, irqmask); } diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index 1383c9ce2646..0b6cc54be966 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -183,6 +183,7 @@ struct mmci_host { unsigned int cclk; u32 pwr_reg; u32 clk_reg; + u32 datactrl_reg; bool vqmmc_enabled; struct mmci_platform_data *plat; struct variant_data *variant; -- cgit v1.2.3 From 0125962000777cd1e2ce53deefb99779d5ee5199 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 15 May 2013 20:53:22 +0100 Subject: ARM: 7726/1: mmc: mmci: Add card_busy function to improve UHS card support To verify a signal voltage switch at initialization of UHS cards the .card_busy callback is used. For some of the ST-variants, card busy detection on the DAT0 pin is supported. We extend the variant struct with a busy_detect flag to indicate support for it. A corresponding busy detect function, which polls the busy status bit, is then set to the .card_busy callback. Signed-off-by: Ulf Hansson Signed-off-by: Russell King --- drivers/mmc/host/mmci.c | 33 ++++++++++++++++++++++++++++++++- drivers/mmc/host/mmci.h | 2 ++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index ccfe0bc62f78..c3785edc0e92 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -61,6 +61,7 @@ static unsigned int fmax = 515633; * @pwrreg_powerup: power up value for MMCIPOWER register * @signal_direction: input/out direction of bus signals can be indicated * @pwrreg_clkgate: MMCIPOWER register must be used to gate the clock + * @busy_detect: true if busy detection on dat0 is supported */ struct variant_data { unsigned int clkreg; @@ -74,6 +75,7 @@ struct variant_data { u32 pwrreg_powerup; bool signal_direction; bool pwrreg_clkgate; + bool busy_detect; }; static struct variant_data variant_arm = { @@ -132,6 +134,7 @@ static struct variant_data variant_ux500 = { .pwrreg_powerup = MCI_PWR_ON, .signal_direction = true, .pwrreg_clkgate = true, + .busy_detect = true, }; static struct variant_data variant_ux500v2 = { @@ -146,8 +149,28 @@ static struct variant_data variant_ux500v2 = { .pwrreg_powerup = MCI_PWR_ON, .signal_direction = true, .pwrreg_clkgate = true, + .busy_detect = true, }; +static int mmci_card_busy(struct mmc_host *mmc) +{ + struct mmci_host *host = mmc_priv(mmc); + unsigned long flags; + int busy = 0; + + pm_runtime_get_sync(mmc_dev(mmc)); + + spin_lock_irqsave(&host->lock, flags); + if (readl(host->base + MMCISTATUS) & MCI_ST_CARDBUSY) + busy = 1; + spin_unlock_irqrestore(&host->lock, flags); + + pm_runtime_mark_last_busy(mmc_dev(mmc)); + pm_runtime_put_autosuspend(mmc_dev(mmc)); + + return busy; +} + /* * Validate mmc prerequisites */ @@ -193,6 +216,9 @@ static void mmci_write_pwrreg(struct mmci_host *host, u32 pwr) */ static void mmci_write_datactrlreg(struct mmci_host *host, u32 datactrl) { + /* Keep ST Micro busy mode if enabled */ + datactrl |= host->datactrl_reg & MCI_ST_DPSM_BUSYMODE; + if (host->datactrl_reg != datactrl) { host->datactrl_reg = datactrl; writel(datactrl, host->base + MMCIDATACTRL); @@ -1319,7 +1345,7 @@ static irqreturn_t mmci_cd_irq(int irq, void *dev_id) return IRQ_HANDLED; } -static const struct mmc_host_ops mmci_ops = { +static struct mmc_host_ops mmci_ops = { .request = mmci_request, .pre_req = mmci_pre_request, .post_req = mmci_post_request, @@ -1455,6 +1481,11 @@ static int mmci_probe(struct amba_device *dev, goto clk_disable; } + if (variant->busy_detect) { + mmci_ops.card_busy = mmci_card_busy; + mmci_write_datactrlreg(host, MCI_ST_DPSM_BUSYMODE); + } + mmc->ops = &mmci_ops; /* * The ARM and ST versions of the block have slightly different diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index 0b6cc54be966..69080fab6375 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -94,6 +94,7 @@ /* Extended status bits for the ST Micro variants */ #define MCI_ST_SDIOIT (1 << 22) #define MCI_ST_CEATAEND (1 << 23) +#define MCI_ST_CARDBUSY (1 << 24) #define MMCICLEAR 0x038 #define MCI_CMDCRCFAILCLR (1 << 0) @@ -110,6 +111,7 @@ /* Extended status bits for the ST Micro variants */ #define MCI_ST_SDIOITC (1 << 22) #define MCI_ST_CEATAENDC (1 << 23) +#define MCI_ST_BUSYENDC (1 << 24) #define MMCIMASK0 0x03c #define MCI_CMDCRCFAILMASK (1 << 0) -- cgit v1.2.3 From 9b97173e785a54c5df0aa23d1e1f680f61e36e43 Mon Sep 17 00:00:00 2001 From: Laura Abbott Date: Thu, 16 May 2013 19:40:22 +0100 Subject: ARM: 7728/1: mm: Use phys_addr_t properly for ioremap functions Several of the ioremap functions use unsigned long in places resulting in truncation if physical addresses greater than 4G are passed in. Change the types of the functions and the callers accordingly. Cc: Krzysztof Halasa Cc: Arnd Bergmann Cc: Stephen Boyd Signed-off-by: Laura Abbott Signed-off-by: Russell King --- arch/arm/include/asm/io.h | 8 ++++---- arch/arm/mach-ebsa110/core.c | 2 +- arch/arm/mach-imx/mm-imx3.c | 2 +- arch/arm/mach-iop13xx/io.c | 2 +- arch/arm/mach-ixp4xx/common.c | 2 +- arch/arm/mach-msm/common.h | 2 +- arch/arm/mach-msm/io.c | 2 +- arch/arm/mm/ioremap.c | 10 +++++----- arch/arm/mm/nommu.c | 6 +++--- 9 files changed, 18 insertions(+), 18 deletions(-) diff --git a/arch/arm/include/asm/io.h b/arch/arm/include/asm/io.h index 652b56086de7..d070741b2b37 100644 --- a/arch/arm/include/asm/io.h +++ b/arch/arm/include/asm/io.h @@ -130,16 +130,16 @@ static inline u32 __raw_readl(const volatile void __iomem *addr) */ extern void __iomem *__arm_ioremap_pfn_caller(unsigned long, unsigned long, size_t, unsigned int, void *); -extern void __iomem *__arm_ioremap_caller(unsigned long, size_t, unsigned int, +extern void __iomem *__arm_ioremap_caller(phys_addr_t, size_t, unsigned int, void *); extern void __iomem *__arm_ioremap_pfn(unsigned long, unsigned long, size_t, unsigned int); -extern void __iomem *__arm_ioremap(unsigned long, size_t, unsigned int); -extern void __iomem *__arm_ioremap_exec(unsigned long, size_t, bool cached); +extern void __iomem *__arm_ioremap(phys_addr_t, size_t, unsigned int); +extern void __iomem *__arm_ioremap_exec(phys_addr_t, size_t, bool cached); extern void __iounmap(volatile void __iomem *addr); extern void __arm_iounmap(volatile void __iomem *addr); -extern void __iomem * (*arch_ioremap_caller)(unsigned long, size_t, +extern void __iomem * (*arch_ioremap_caller)(phys_addr_t, size_t, unsigned int, void *); extern void (*arch_iounmap)(volatile void __iomem *); diff --git a/arch/arm/mach-ebsa110/core.c b/arch/arm/mach-ebsa110/core.c index b13cc74114db..8a53f346cdb3 100644 --- a/arch/arm/mach-ebsa110/core.c +++ b/arch/arm/mach-ebsa110/core.c @@ -116,7 +116,7 @@ static void __init ebsa110_map_io(void) iotable_init(ebsa110_io_desc, ARRAY_SIZE(ebsa110_io_desc)); } -static void __iomem *ebsa110_ioremap_caller(unsigned long cookie, size_t size, +static void __iomem *ebsa110_ioremap_caller(phys_addr_t cookie, size_t size, unsigned int flags, void *caller) { return (void __iomem *)cookie; diff --git a/arch/arm/mach-imx/mm-imx3.c b/arch/arm/mach-imx/mm-imx3.c index e0e69a682174..eed32ca0b8ab 100644 --- a/arch/arm/mach-imx/mm-imx3.c +++ b/arch/arm/mach-imx/mm-imx3.c @@ -65,7 +65,7 @@ static void imx3_idle(void) : "=r" (reg)); } -static void __iomem *imx3_ioremap_caller(unsigned long phys_addr, size_t size, +static void __iomem *imx3_ioremap_caller(phys_addr_t phys_addr, size_t size, unsigned int mtype, void *caller) { if (mtype == MT_DEVICE) { diff --git a/arch/arm/mach-iop13xx/io.c b/arch/arm/mach-iop13xx/io.c index 183dc8b5511b..faaf7d4482c5 100644 --- a/arch/arm/mach-iop13xx/io.c +++ b/arch/arm/mach-iop13xx/io.c @@ -23,7 +23,7 @@ #include "pci.h" -static void __iomem *__iop13xx_ioremap_caller(unsigned long cookie, +static void __iomem *__iop13xx_ioremap_caller(phys_addr_t cookie, size_t size, unsigned int mtype, void *caller) { void __iomem * retval; diff --git a/arch/arm/mach-ixp4xx/common.c b/arch/arm/mach-ixp4xx/common.c index 6600cff6bd92..d7223b3b81f3 100644 --- a/arch/arm/mach-ixp4xx/common.c +++ b/arch/arm/mach-ixp4xx/common.c @@ -559,7 +559,7 @@ void ixp4xx_restart(char mode, const char *cmd) * fallback to the default. */ -static void __iomem *ixp4xx_ioremap_caller(unsigned long addr, size_t size, +static void __iomem *ixp4xx_ioremap_caller(phys_addr_t addr, size_t size, unsigned int mtype, void *caller) { if (!is_pci_memory(addr)) diff --git a/arch/arm/mach-msm/common.h b/arch/arm/mach-msm/common.h index ce8215a269e5..421cf7751a80 100644 --- a/arch/arm/mach-msm/common.h +++ b/arch/arm/mach-msm/common.h @@ -23,7 +23,7 @@ extern void msm_map_msm8x60_io(void); extern void msm_map_msm8960_io(void); extern void msm_map_qsd8x50_io(void); -extern void __iomem *__msm_ioremap_caller(unsigned long phys_addr, size_t size, +extern void __iomem *__msm_ioremap_caller(phys_addr_t phys_addr, size_t size, unsigned int mtype, void *caller); extern struct smp_operations msm_smp_ops; diff --git a/arch/arm/mach-msm/io.c b/arch/arm/mach-msm/io.c index 123ef9cbce1b..fd65b6d42cde 100644 --- a/arch/arm/mach-msm/io.c +++ b/arch/arm/mach-msm/io.c @@ -172,7 +172,7 @@ void __init msm_map_msm7x30_io(void) } #endif /* CONFIG_ARCH_MSM7X30 */ -void __iomem *__msm_ioremap_caller(unsigned long phys_addr, size_t size, +void __iomem *__msm_ioremap_caller(phys_addr_t phys_addr, size_t size, unsigned int mtype, void *caller) { if (mtype == MT_DEVICE) { diff --git a/arch/arm/mm/ioremap.c b/arch/arm/mm/ioremap.c index 04d9006eab1f..f123d6eb074b 100644 --- a/arch/arm/mm/ioremap.c +++ b/arch/arm/mm/ioremap.c @@ -331,10 +331,10 @@ void __iomem * __arm_ioremap_pfn_caller(unsigned long pfn, return (void __iomem *) (offset + addr); } -void __iomem *__arm_ioremap_caller(unsigned long phys_addr, size_t size, +void __iomem *__arm_ioremap_caller(phys_addr_t phys_addr, size_t size, unsigned int mtype, void *caller) { - unsigned long last_addr; + phys_addr_t last_addr; unsigned long offset = phys_addr & ~PAGE_MASK; unsigned long pfn = __phys_to_pfn(phys_addr); @@ -367,12 +367,12 @@ __arm_ioremap_pfn(unsigned long pfn, unsigned long offset, size_t size, } EXPORT_SYMBOL(__arm_ioremap_pfn); -void __iomem * (*arch_ioremap_caller)(unsigned long, size_t, +void __iomem * (*arch_ioremap_caller)(phys_addr_t, size_t, unsigned int, void *) = __arm_ioremap_caller; void __iomem * -__arm_ioremap(unsigned long phys_addr, size_t size, unsigned int mtype) +__arm_ioremap(phys_addr_t phys_addr, size_t size, unsigned int mtype) { return arch_ioremap_caller(phys_addr, size, mtype, __builtin_return_address(0)); @@ -387,7 +387,7 @@ EXPORT_SYMBOL(__arm_ioremap); * CONFIG_GENERIC_ALLOCATOR for allocating external memory. */ void __iomem * -__arm_ioremap_exec(unsigned long phys_addr, size_t size, bool cached) +__arm_ioremap_exec(phys_addr_t phys_addr, size_t size, bool cached) { unsigned int mtype; diff --git a/arch/arm/mm/nommu.c b/arch/arm/mm/nommu.c index d51225f90ae2..31bd77b1d6df 100644 --- a/arch/arm/mm/nommu.c +++ b/arch/arm/mm/nommu.c @@ -81,16 +81,16 @@ void __iomem *__arm_ioremap_pfn_caller(unsigned long pfn, unsigned long offset, return __arm_ioremap_pfn(pfn, offset, size, mtype); } -void __iomem *__arm_ioremap(unsigned long phys_addr, size_t size, +void __iomem *__arm_ioremap(phys_addr_t phys_addr, size_t size, unsigned int mtype) { return (void __iomem *)phys_addr; } EXPORT_SYMBOL(__arm_ioremap); -void __iomem * (*arch_ioremap_caller)(unsigned long, size_t, unsigned int, void *); +void __iomem * (*arch_ioremap_caller)(phys_addr_t, size_t, unsigned int, void *); -void __iomem *__arm_ioremap_caller(unsigned long phys_addr, size_t size, +void __iomem *__arm_ioremap_caller(phys_addr_t phys_addr, size_t size, unsigned int mtype, void *caller) { return __arm_ioremap(phys_addr, size, mtype); -- cgit v1.2.3 From b2a234ed6417cf88bdbf4b22700e0ae6e08653ce Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Sat, 18 May 2013 11:21:36 +0100 Subject: ARM: 7730/1: DMA-mapping: mark all !DMA_TO_DEVICE pages in unmapping as clean It is common for one sg to include many pages, so mark all these pages as clean to avoid unnecessary flushing on them in set_pte_at() or update_mmu_cache(). The patch might improve loading performance of applciation code a bit. On the below test code to read file(~1GByte size) from usb mass storage disk to buffer created with mmap(PROT_READ | PROT_EXEC) on Pandaboard, average ~1% improvement can be observed with the patch on 10 times test. unsigned int sum = 0; static unsigned long tv_diff(struct timeval *tv1, struct timeval *tv2) { return (tv2->tv_sec - tv1->tv_sec) * 1000000 + (tv2->tv_usec - tv1->tv_usec); } int main(int argc, char *argv[]) { char *mbuffer; int fd; int i; unsigned long page_size, size; struct stat stat; struct timeval t1, t2; page_size = getpagesize(); fd = open(argv[1], O_RDONLY); assert(fd >= 0); fstat(fd, &stat); size = stat.st_size; printf("%s: file %s, file size %lu, page size %lun", argv[0], read_filename, size, page_size); gettimeofday(&t1, NULL); mbuffer = mmap(NULL, size, PROT_READ | PROT_EXEC, MAP_SHARED, fd, 0); for (i = 0 ; i < size ; i += page_size) sum += mbuffer[i]; munmap(mbuffer, page_size); gettimeofday(&t2, NULL); printf("tread mmaped time: %luusn", tv_diff(&t1, &t2)); close(fd); } Acked-by: Nicolas Pitre Cc: Catalin Marinas Cc: Marek Szyprowski Signed-off-by: Ming Lei Signed-off-by: Russell King --- arch/arm/mm/dma-mapping.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index ef3e0f3aac96..c038ec0738ac 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -880,10 +880,24 @@ static void __dma_page_dev_to_cpu(struct page *page, unsigned long off, dma_cache_maint_page(page, off, size, dir, dmac_unmap_area); /* - * Mark the D-cache clean for this page to avoid extra flushing. + * Mark the D-cache clean for these pages to avoid extra flushing. */ - if (dir != DMA_TO_DEVICE && off == 0 && size >= PAGE_SIZE) - set_bit(PG_dcache_clean, &page->flags); + if (dir != DMA_TO_DEVICE && size >= PAGE_SIZE) { + unsigned long pfn; + size_t left = size; + + pfn = page_to_pfn(page) + off / PAGE_SIZE; + off %= PAGE_SIZE; + if (off) { + pfn++; + left -= PAGE_SIZE - off; + } + while (left >= PAGE_SIZE) { + page = pfn_to_page(pfn++); + set_bit(PG_dcache_clean, &page->flags); + left -= PAGE_SIZE; + } + } } /** -- cgit v1.2.3 From 5b88e270253db6d817e6a2f61909d1e53620e990 Mon Sep 17 00:00:00 2001 From: Kent Yoder Date: Wed, 22 May 2013 10:56:58 -0500 Subject: maintainers: Remove Kent from maintainers Signed-off-by: Kent Yoder Signed-off-by: James Morris --- MAINTAINERS | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 3d7782b9f90d..60bc1cc8d114 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3971,7 +3971,8 @@ S: Maintained F: arch/ia64/ IBM Power in-Nest Crypto Acceleration -M: Kent Yoder +M: Marcelo Henrique Cerri +M: Fionnuala Gunter L: linux-crypto@vger.kernel.org S: Supported F: drivers/crypto/nx/ @@ -8198,7 +8199,8 @@ S: Odd fixes F: drivers/media/usb/tm6000/ TPM DEVICE DRIVER -M: Kent Yoder +M: Leonidas Da Silva Barbosa +M: Ashley Lai M: Rajiv Andrade W: http://tpmdd.sourceforge.net M: Marcel Selhorst -- cgit v1.2.3 From e5657054ff508c9e547a8ceb931de07838ee3ba0 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 22 May 2013 18:55:25 -0500 Subject: mfd: wm5110: Make DSPn_STATUS_3 readable These registers have been documented since the driver was originally submitted so expose them. Signed-off-by: Mark Brown --- drivers/mfd/wm5110-tables.c | 8 ++++++++ include/linux/mfd/arizona/registers.h | 3 +++ 2 files changed, 11 insertions(+) diff --git a/drivers/mfd/wm5110-tables.c b/drivers/mfd/wm5110-tables.c index c41599815299..2a7972349159 100644 --- a/drivers/mfd/wm5110-tables.c +++ b/drivers/mfd/wm5110-tables.c @@ -2273,18 +2273,22 @@ static bool wm5110_readable_register(struct device *dev, unsigned int reg) case ARIZONA_DSP1_CLOCKING_1: case ARIZONA_DSP1_STATUS_1: case ARIZONA_DSP1_STATUS_2: + case ARIZONA_DSP1_STATUS_3: case ARIZONA_DSP2_CONTROL_1: case ARIZONA_DSP2_CLOCKING_1: case ARIZONA_DSP2_STATUS_1: case ARIZONA_DSP2_STATUS_2: + case ARIZONA_DSP2_STATUS_3: case ARIZONA_DSP3_CONTROL_1: case ARIZONA_DSP3_CLOCKING_1: case ARIZONA_DSP3_STATUS_1: case ARIZONA_DSP3_STATUS_2: + case ARIZONA_DSP3_STATUS_3: case ARIZONA_DSP4_CONTROL_1: case ARIZONA_DSP4_CLOCKING_1: case ARIZONA_DSP4_STATUS_1: case ARIZONA_DSP4_STATUS_2: + case ARIZONA_DSP4_STATUS_3: return true; default: return false; @@ -2334,12 +2338,16 @@ static bool wm5110_volatile_register(struct device *dev, unsigned int reg) case ARIZONA_DSP1_CLOCKING_1: case ARIZONA_DSP1_STATUS_1: case ARIZONA_DSP1_STATUS_2: + case ARIZONA_DSP1_STATUS_3: case ARIZONA_DSP2_STATUS_1: case ARIZONA_DSP2_STATUS_2: + case ARIZONA_DSP2_STATUS_3: case ARIZONA_DSP3_STATUS_1: case ARIZONA_DSP3_STATUS_2: + case ARIZONA_DSP3_STATUS_3: case ARIZONA_DSP4_STATUS_1: case ARIZONA_DSP4_STATUS_2: + case ARIZONA_DSP4_STATUS_3: return true; default: return false; diff --git a/include/linux/mfd/arizona/registers.h b/include/linux/mfd/arizona/registers.h index 715b6ba3d52a..4730b5c576e2 100644 --- a/include/linux/mfd/arizona/registers.h +++ b/include/linux/mfd/arizona/registers.h @@ -1002,6 +1002,7 @@ #define ARIZONA_DSP2_CLOCKING_1 0x1201 #define ARIZONA_DSP2_STATUS_1 0x1204 #define ARIZONA_DSP2_STATUS_2 0x1205 +#define ARIZONA_DSP2_STATUS_3 0x1206 #define ARIZONA_DSP2_SCRATCH_0 0x1240 #define ARIZONA_DSP2_SCRATCH_1 0x1241 #define ARIZONA_DSP2_SCRATCH_2 0x1242 @@ -1010,6 +1011,7 @@ #define ARIZONA_DSP3_CLOCKING_1 0x1301 #define ARIZONA_DSP3_STATUS_1 0x1304 #define ARIZONA_DSP3_STATUS_2 0x1305 +#define ARIZONA_DSP3_STATUS_3 0x1306 #define ARIZONA_DSP3_SCRATCH_0 0x1340 #define ARIZONA_DSP3_SCRATCH_1 0x1341 #define ARIZONA_DSP3_SCRATCH_2 0x1342 @@ -1018,6 +1020,7 @@ #define ARIZONA_DSP4_CLOCKING_1 0x1401 #define ARIZONA_DSP4_STATUS_1 0x1404 #define ARIZONA_DSP4_STATUS_2 0x1405 +#define ARIZONA_DSP4_STATUS_3 0x1406 #define ARIZONA_DSP4_SCRATCH_0 0x1440 #define ARIZONA_DSP4_SCRATCH_1 0x1441 #define ARIZONA_DSP4_SCRATCH_2 0x1442 -- cgit v1.2.3 From f1238261891bea67da845688b7684a3444c61bd9 Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Tue, 21 May 2013 00:22:37 +0000 Subject: net: smsc911x: don't artificially limit build Currently the SMSC911X driver may only be built for a specific set of architectures, being limited to do so by a Kconfig depends line. This means that if a platform wishes to use the driver, its architecture must be added to the list explicitly, introducing pointless churn. This may have been due to the driver's use of the {read,write}s{b,w,l} functions, which have since been replaced with the more standard io{read,write}{8,16,32}_rep. We can instead depend on HAS_IOMEM, which should prevent build issues while allowing the driver to be built for currently unlisted architectures, including x86 and arm64. This patch removes the explicit list of architectures from the driver's depend line, and replaces it with a dependency on HAS_IOMEM. Signed-off-by: Mark Rutland Cc: David S. Miller Cc: Arnd Bergmann Cc: netdev@vger.kernel.org Signed-off-by: David S. Miller --- drivers/net/ethernet/smsc/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/smsc/Kconfig b/drivers/net/ethernet/smsc/Kconfig index bb4c1674ff99..ff9e99474039 100644 --- a/drivers/net/ethernet/smsc/Kconfig +++ b/drivers/net/ethernet/smsc/Kconfig @@ -97,7 +97,7 @@ config SMC911X config SMSC911X tristate "SMSC LAN911x/LAN921x families embedded ethernet support" - depends on (ARM || SUPERH || BLACKFIN || MIPS || MN10300) + depends on HAS_IOMEM select CRC32 select NET_CORE select MII -- cgit v1.2.3 From 9313eb4be3220054c07a7c34c38764ea1ebcd220 Mon Sep 17 00:00:00 2001 From: Jay Fenlason Date: Tue, 21 May 2013 04:21:28 +0000 Subject: cxgb3: Fix warning about using rcu_dereference when not in a rcu-locked section It is about using rcu_dereference() when not in a rcu-locked section. It only happens on initialization hence fix the initialization to not rcu_dereference() Signed-off-by: Jay Fenlason Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c index 0c96e5fe99cc..4058b856eb71 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c +++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c @@ -1246,6 +1246,7 @@ int cxgb3_offload_activate(struct adapter *adapter) struct tid_range stid_range, tid_range; struct mtutab mtutab; unsigned int l2t_capacity; + struct l2t_data *l2td; t = kzalloc(sizeof(*t), GFP_KERNEL); if (!t) @@ -1261,8 +1262,8 @@ int cxgb3_offload_activate(struct adapter *adapter) goto out_free; err = -ENOMEM; - RCU_INIT_POINTER(dev->l2opt, t3_init_l2t(l2t_capacity)); - if (!L2DATA(dev)) + l2td = t3_init_l2t(l2t_capacity); + if (!l2td) goto out_free; natids = min(tid_range.num / 2, MAX_ATIDS); @@ -1279,6 +1280,7 @@ int cxgb3_offload_activate(struct adapter *adapter) INIT_LIST_HEAD(&t->list_node); t->dev = dev; + RCU_INIT_POINTER(dev->l2opt, l2td); T3C_DATA(dev) = t; dev->recv = process_rx; dev->neigh_update = t3_l2t_update; @@ -1294,8 +1296,7 @@ int cxgb3_offload_activate(struct adapter *adapter) return 0; out_free_l2t: - t3_free_l2t(L2DATA(dev)); - RCU_INIT_POINTER(dev->l2opt, NULL); + t3_free_l2t(l2td); out_free: kfree(t); return err; -- cgit v1.2.3 From f83331bab149e29fa2c49cf102c0cd8c3f1ce9f9 Mon Sep 17 00:00:00 2001 From: Santosh Rastapur Date: Tue, 21 May 2013 04:21:29 +0000 Subject: cxgb3: Check and handle the dma mapping errors This patch adds checks at approprate places whether *dma_map*() call has succeeded or not. Signed-off-by: Santosh Rastapur Reviewed-by: Jay Fenlason Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb3/sge.c | 105 ++++++++++++++++++++++++------- 1 file changed, 82 insertions(+), 23 deletions(-) diff --git a/drivers/net/ethernet/chelsio/cxgb3/sge.c b/drivers/net/ethernet/chelsio/cxgb3/sge.c index f12e6b85a653..2fd773e267dc 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb3/sge.c @@ -455,6 +455,11 @@ static int alloc_pg_chunk(struct adapter *adapter, struct sge_fl *q, q->pg_chunk.offset = 0; mapping = pci_map_page(adapter->pdev, q->pg_chunk.page, 0, q->alloc_size, PCI_DMA_FROMDEVICE); + if (unlikely(pci_dma_mapping_error(adapter->pdev, mapping))) { + __free_pages(q->pg_chunk.page, order); + q->pg_chunk.page = NULL; + return -EIO; + } q->pg_chunk.mapping = mapping; } sd->pg_chunk = q->pg_chunk; @@ -949,40 +954,75 @@ static inline unsigned int calc_tx_descs(const struct sk_buff *skb) return flits_to_desc(flits); } + +/* map_skb - map a packet main body and its page fragments + * @pdev: the PCI device + * @skb: the packet + * @addr: placeholder to save the mapped addresses + * + * map the main body of an sk_buff and its page fragments, if any. + */ +static int map_skb(struct pci_dev *pdev, const struct sk_buff *skb, + dma_addr_t *addr) +{ + const skb_frag_t *fp, *end; + const struct skb_shared_info *si; + + *addr = pci_map_single(pdev, skb->data, skb_headlen(skb), + PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(pdev, *addr)) + goto out_err; + + si = skb_shinfo(skb); + end = &si->frags[si->nr_frags]; + + for (fp = si->frags; fp < end; fp++) { + *++addr = skb_frag_dma_map(&pdev->dev, fp, 0, skb_frag_size(fp), + DMA_TO_DEVICE); + if (pci_dma_mapping_error(pdev, *addr)) + goto unwind; + } + return 0; + +unwind: + while (fp-- > si->frags) + dma_unmap_page(&pdev->dev, *--addr, skb_frag_size(fp), + DMA_TO_DEVICE); + + pci_unmap_single(pdev, addr[-1], skb_headlen(skb), PCI_DMA_TODEVICE); +out_err: + return -ENOMEM; +} + /** - * make_sgl - populate a scatter/gather list for a packet + * write_sgl - populate a scatter/gather list for a packet * @skb: the packet * @sgp: the SGL to populate * @start: start address of skb main body data to include in the SGL * @len: length of skb main body data to include in the SGL - * @pdev: the PCI device + * @addr: the list of the mapped addresses * - * Generates a scatter/gather list for the buffers that make up a packet + * Copies the scatter/gather list for the buffers that make up a packet * and returns the SGL size in 8-byte words. The caller must size the SGL * appropriately. */ -static inline unsigned int make_sgl(const struct sk_buff *skb, +static inline unsigned int write_sgl(const struct sk_buff *skb, struct sg_ent *sgp, unsigned char *start, - unsigned int len, struct pci_dev *pdev) + unsigned int len, const dma_addr_t *addr) { - dma_addr_t mapping; - unsigned int i, j = 0, nfrags; + unsigned int i, j = 0, k = 0, nfrags; if (len) { - mapping = pci_map_single(pdev, start, len, PCI_DMA_TODEVICE); sgp->len[0] = cpu_to_be32(len); - sgp->addr[0] = cpu_to_be64(mapping); - j = 1; + sgp->addr[j++] = cpu_to_be64(addr[k++]); } nfrags = skb_shinfo(skb)->nr_frags; for (i = 0; i < nfrags; i++) { const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; - mapping = skb_frag_dma_map(&pdev->dev, frag, 0, skb_frag_size(frag), - DMA_TO_DEVICE); sgp->len[j] = cpu_to_be32(skb_frag_size(frag)); - sgp->addr[j] = cpu_to_be64(mapping); + sgp->addr[j] = cpu_to_be64(addr[k++]); j ^= 1; if (j == 0) ++sgp; @@ -1138,7 +1178,7 @@ static void write_tx_pkt_wr(struct adapter *adap, struct sk_buff *skb, const struct port_info *pi, unsigned int pidx, unsigned int gen, struct sge_txq *q, unsigned int ndesc, - unsigned int compl) + unsigned int compl, const dma_addr_t *addr) { unsigned int flits, sgl_flits, cntrl, tso_info; struct sg_ent *sgp, sgl[MAX_SKB_FRAGS / 2 + 1]; @@ -1196,7 +1236,7 @@ static void write_tx_pkt_wr(struct adapter *adap, struct sk_buff *skb, } sgp = ndesc == 1 ? (struct sg_ent *)&d->flit[flits] : sgl; - sgl_flits = make_sgl(skb, sgp, skb->data, skb_headlen(skb), adap->pdev); + sgl_flits = write_sgl(skb, sgp, skb->data, skb_headlen(skb), addr); write_wr_hdr_sgl(ndesc, skb, d, pidx, q, sgl, flits, sgl_flits, gen, htonl(V_WR_OP(FW_WROPCODE_TUNNEL_TX_PKT) | compl), @@ -1227,6 +1267,7 @@ netdev_tx_t t3_eth_xmit(struct sk_buff *skb, struct net_device *dev) struct netdev_queue *txq; struct sge_qset *qs; struct sge_txq *q; + dma_addr_t addr[MAX_SKB_FRAGS + 1]; /* * The chip min packet length is 9 octets but play safe and reject @@ -1255,6 +1296,11 @@ netdev_tx_t t3_eth_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_BUSY; } + if (unlikely(map_skb(adap->pdev, skb, addr) < 0)) { + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + q->in_use += ndesc; if (unlikely(credits - ndesc < q->stop_thres)) { t3_stop_tx_queue(txq, qs, q); @@ -1312,7 +1358,7 @@ netdev_tx_t t3_eth_xmit(struct sk_buff *skb, struct net_device *dev) if (likely(!skb_shared(skb))) skb_orphan(skb); - write_tx_pkt_wr(adap, skb, pi, pidx, gen, q, ndesc, compl); + write_tx_pkt_wr(adap, skb, pi, pidx, gen, q, ndesc, compl, addr); check_ring_tx_db(adap, q); return NETDEV_TX_OK; } @@ -1578,7 +1624,8 @@ static void setup_deferred_unmapping(struct sk_buff *skb, struct pci_dev *pdev, */ static void write_ofld_wr(struct adapter *adap, struct sk_buff *skb, struct sge_txq *q, unsigned int pidx, - unsigned int gen, unsigned int ndesc) + unsigned int gen, unsigned int ndesc, + const dma_addr_t *addr) { unsigned int sgl_flits, flits; struct work_request_hdr *from; @@ -1599,9 +1646,9 @@ static void write_ofld_wr(struct adapter *adap, struct sk_buff *skb, flits = skb_transport_offset(skb) / 8; sgp = ndesc == 1 ? (struct sg_ent *)&d->flit[flits] : sgl; - sgl_flits = make_sgl(skb, sgp, skb_transport_header(skb), + sgl_flits = write_sgl(skb, sgp, skb_transport_header(skb), skb->tail - skb->transport_header, - adap->pdev); + addr); if (need_skb_unmap()) { setup_deferred_unmapping(skb, adap->pdev, sgp, sgl_flits); skb->destructor = deferred_unmap_destructor; @@ -1659,6 +1706,11 @@ again: reclaim_completed_tx(adap, q, TX_RECLAIM_CHUNK); goto again; } + if (map_skb(adap->pdev, skb, (dma_addr_t *)skb->head)) { + spin_unlock(&q->lock); + return NET_XMIT_SUCCESS; + } + gen = q->gen; q->in_use += ndesc; pidx = q->pidx; @@ -1669,7 +1721,7 @@ again: reclaim_completed_tx(adap, q, TX_RECLAIM_CHUNK); } spin_unlock(&q->lock); - write_ofld_wr(adap, skb, q, pidx, gen, ndesc); + write_ofld_wr(adap, skb, q, pidx, gen, ndesc, (dma_addr_t *)skb->head); check_ring_tx_db(adap, q); return NET_XMIT_SUCCESS; } @@ -1687,6 +1739,7 @@ static void restart_offloadq(unsigned long data) struct sge_txq *q = &qs->txq[TXQ_OFLD]; const struct port_info *pi = netdev_priv(qs->netdev); struct adapter *adap = pi->adapter; + unsigned int written = 0; spin_lock(&q->lock); again: reclaim_completed_tx(adap, q, TX_RECLAIM_CHUNK); @@ -1706,10 +1759,14 @@ again: reclaim_completed_tx(adap, q, TX_RECLAIM_CHUNK); break; } + if (map_skb(adap->pdev, skb, (dma_addr_t *)skb->head)) + break; + gen = q->gen; q->in_use += ndesc; pidx = q->pidx; q->pidx += ndesc; + written += ndesc; if (q->pidx >= q->size) { q->pidx -= q->size; q->gen ^= 1; @@ -1717,7 +1774,8 @@ again: reclaim_completed_tx(adap, q, TX_RECLAIM_CHUNK); __skb_unlink(skb, &q->sendq); spin_unlock(&q->lock); - write_ofld_wr(adap, skb, q, pidx, gen, ndesc); + write_ofld_wr(adap, skb, q, pidx, gen, ndesc, + (dma_addr_t *)skb->head); spin_lock(&q->lock); } spin_unlock(&q->lock); @@ -1727,8 +1785,9 @@ again: reclaim_completed_tx(adap, q, TX_RECLAIM_CHUNK); set_bit(TXQ_LAST_PKT_DB, &q->flags); #endif wmb(); - t3_write_reg(adap, A_SG_KDOORBELL, - F_SELEGRCNTX | V_EGRCNTX(q->cntxt_id)); + if (likely(written)) + t3_write_reg(adap, A_SG_KDOORBELL, + F_SELEGRCNTX | V_EGRCNTX(q->cntxt_id)); } /** -- cgit v1.2.3 From ffed61e6fd3a405e79f4d267f22acaee3267fb37 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 21 May 2013 05:44:26 +0000 Subject: fec: Use DIV_ROUND_UP macro Use the standard DIV_ROUND_UP macro in order to provide better readability. Signed-off-by: Fabio Estevam Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fec_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 2f4b3e028012..6b5dc58c56d8 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -242,7 +242,7 @@ static void *swap_buffer(void *bufaddr, int len) int i; unsigned int *buf = bufaddr; - for (i = 0; i < (len + 3) / 4; i++, buf++) + for (i = 0; i < DIV_ROUND_UP(len, 4); i++, buf++) *buf = cpu_to_be32(*buf); return bufaddr; -- cgit v1.2.3 From e43ac79a4bc6ca90de4ba10983b4ca39cd215b4b Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 21 May 2013 08:16:46 +0000 Subject: sch_tbf: segment too big GSO packets If a GSO packet has a length above tbf burst limit, the packet is currently silently dropped. Current way to handle this is to set the device in non GSO/TSO mode, or setting high bursts, and its sub optimal. We can actually segment too big GSO packets, and send individual segments as tbf parameters allow, allowing for better interoperability. Signed-off-by: Eric Dumazet Cc: Ben Hutchings Cc: Jiri Pirko Cc: Jamal Hadi Salim Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller --- net/sched/sch_tbf.c | 47 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c index c8388f3c3426..38008b0980d9 100644 --- a/net/sched/sch_tbf.c +++ b/net/sched/sch_tbf.c @@ -116,14 +116,57 @@ struct tbf_sched_data { struct qdisc_watchdog watchdog; /* Watchdog timer */ }; + +/* GSO packet is too big, segment it so that tbf can transmit + * each segment in time + */ +static int tbf_segment(struct sk_buff *skb, struct Qdisc *sch) +{ + struct tbf_sched_data *q = qdisc_priv(sch); + struct sk_buff *segs, *nskb; + netdev_features_t features = netif_skb_features(skb); + int ret, nb; + + segs = skb_gso_segment(skb, features & ~NETIF_F_GSO_MASK); + + if (IS_ERR_OR_NULL(segs)) + return qdisc_reshape_fail(skb, sch); + + nb = 0; + while (segs) { + nskb = segs->next; + segs->next = NULL; + if (likely(segs->len <= q->max_size)) { + qdisc_skb_cb(segs)->pkt_len = segs->len; + ret = qdisc_enqueue(segs, q->qdisc); + } else { + ret = qdisc_reshape_fail(skb, sch); + } + if (ret != NET_XMIT_SUCCESS) { + if (net_xmit_drop_count(ret)) + sch->qstats.drops++; + } else { + nb++; + } + segs = nskb; + } + sch->q.qlen += nb; + if (nb > 1) + qdisc_tree_decrease_qlen(sch, 1 - nb); + consume_skb(skb); + return nb > 0 ? NET_XMIT_SUCCESS : NET_XMIT_DROP; +} + static int tbf_enqueue(struct sk_buff *skb, struct Qdisc *sch) { struct tbf_sched_data *q = qdisc_priv(sch); int ret; - if (qdisc_pkt_len(skb) > q->max_size) + if (qdisc_pkt_len(skb) > q->max_size) { + if (skb_is_gso(skb)) + return tbf_segment(skb, sch); return qdisc_reshape_fail(skb, sch); - + } ret = qdisc_enqueue(skb, q->qdisc); if (ret != NET_XMIT_SUCCESS) { if (net_xmit_drop_count(ret)) -- cgit v1.2.3 From 7996c799ae329fab1b9c8d475fd08883f0499ed9 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Wed, 22 May 2013 05:41:06 +0000 Subject: ipv6: use ipv6_addr_any() helper ipv6_addr_any() is a faster way to determine if an addr is ipv6 any addr, no need to compute the addr type. Cc: Nicolas Dichtel Cc: Hideaki YOSHIFUJI Cc: David S. Miller Signed-off-by: Cong Wang Acked-by: Nicolas Dichtel Signed-off-by: David S. Miller --- net/ipv6/addrconf.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index d684d23bc027..e05269647c2b 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -3846,7 +3846,7 @@ static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa, valid = INFINITY_LIFE_TIME; } - if (ipv6_addr_type(&ifa->peer_addr) != IPV6_ADDR_ANY) { + if (!ipv6_addr_any(&ifa->peer_addr)) { if (nla_put(skb, IFA_LOCAL, 16, &ifa->addr) < 0 || nla_put(skb, IFA_ADDRESS, 16, &ifa->peer_addr) < 0) goto error; @@ -4579,7 +4579,7 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp) ip6_ins_rt(ifp->rt); if (ifp->idev->cnf.forwarding) addrconf_join_anycast(ifp); - if (ipv6_addr_type(&ifp->peer_addr) != IPV6_ADDR_ANY) + if (!ipv6_addr_any(&ifp->peer_addr)) addrconf_prefix_route(&ifp->peer_addr, 128, ifp->idev->dev, 0, 0); break; @@ -4587,7 +4587,7 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp) if (ifp->idev->cnf.forwarding) addrconf_leave_anycast(ifp); addrconf_leave_solict(ifp->idev, &ifp->addr); - if (ipv6_addr_type(&ifp->peer_addr) != IPV6_ADDR_ANY) { + if (!ipv6_addr_any(&ifp->peer_addr)) { struct rt6_info *rt; struct net_device *dev = ifp->idev->dev; -- cgit v1.2.3 From 8892475386e819aa50856947948c546ccc964d96 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Wed, 22 May 2013 05:52:22 +0000 Subject: ipv6: use ipv6_addr_scope() helper ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK could be replaced by ipv6_addr_scope(), which is slightly faster. Cc: Hideaki YOSHIFUJI Cc: David S. Miller Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- net/ipv6/addrconf.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index e05269647c2b..432e084b6b62 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -1126,8 +1126,7 @@ retry: ift = !max_addresses || ipv6_count_addresses(idev) < max_addresses ? - ipv6_add_addr(idev, &addr, tmp_plen, - ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, + ipv6_add_addr(idev, &addr, tmp_plen, ipv6_addr_scope(&addr), addr_flags) : NULL; if (IS_ERR_OR_NULL(ift)) { in6_ifa_put(ifp); -- cgit v1.2.3 From 27e7190efd5b2f728686a8293af6d9bd34c4e562 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 22 May 2013 11:10:57 +0000 Subject: netfilter: xt_CT: optimize XT_CT_NOTRACK The percpu untracked ct are not currently used for XT_CT_NOTRACK. xt_ct_tg_check()/xt_ct_target() provides a single ct. Thats not optimal as the ct->ct_general.use cache line will bounce among cpus. Use the intended [1] thing : xt_ct_target() should select the percpu object. [1] Refs : commit 5bfddbd46a95c97 ("netfilter: nf_conntrack: IPS_UNTRACKED bit") commit b3c5163fe0193a7 ("netfilter: nf_conntrack: per_cpu untracking") Signed-off-by: Eric Dumazet Signed-off-by: Pablo Neira Ayuso --- net/netfilter/xt_CT.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/net/netfilter/xt_CT.c b/net/netfilter/xt_CT.c index a60261cb0e80..da35ac06a975 100644 --- a/net/netfilter/xt_CT.c +++ b/net/netfilter/xt_CT.c @@ -26,6 +26,9 @@ static inline int xt_ct_target(struct sk_buff *skb, struct nf_conn *ct) if (skb->nfct != NULL) return XT_CONTINUE; + /* special case the untracked ct : we want the percpu object */ + if (!ct) + ct = nf_ct_untracked_get(); atomic_inc(&ct->ct_general.use); skb->nfct = &ct->ct_general; skb->nfctinfo = IP_CT_NEW; @@ -186,8 +189,7 @@ static int xt_ct_tg_check(const struct xt_tgchk_param *par, int ret = -EOPNOTSUPP; if (info->flags & XT_CT_NOTRACK) { - ct = nf_ct_untracked_get(); - atomic_inc(&ct->ct_general.use); + ct = NULL; goto out; } @@ -311,7 +313,7 @@ static void xt_ct_tg_destroy(const struct xt_tgdtor_param *par, struct nf_conn *ct = info->ct; struct nf_conn_help *help; - if (!nf_ct_is_untracked(ct)) { + if (ct && !nf_ct_is_untracked(ct)) { help = nfct_help(ct); if (help) module_put(help->helper->me); @@ -319,8 +321,8 @@ static void xt_ct_tg_destroy(const struct xt_tgdtor_param *par, nf_ct_l3proto_module_put(par->family); xt_ct_destroy_timeout(ct); + nf_ct_put(info->ct); } - nf_ct_put(info->ct); } static void xt_ct_tg_destroy_v0(const struct xt_tgdtor_param *par) -- cgit v1.2.3 From 00028aa37098168048728acc32ab0206687f2920 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 22 May 2013 11:01:06 +0000 Subject: netfilter: xt_socket: use IP early demux With IP early demux added in linux-3.6, we perform TCP lookup in IP layer before iptables hooks. We can avoid doing a second lookup in xt_socket. Signed-off-by: Eric Dumazet Acked-by: David S. Miller Signed-off-by: Pablo Neira Ayuso --- net/netfilter/xt_socket.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/net/netfilter/xt_socket.c b/net/netfilter/xt_socket.c index 63b2bdb59e95..02704245710e 100644 --- a/net/netfilter/xt_socket.c +++ b/net/netfilter/xt_socket.c @@ -107,7 +107,7 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par, { const struct iphdr *iph = ip_hdr(skb); struct udphdr _hdr, *hp = NULL; - struct sock *sk; + struct sock *sk = skb->sk; __be32 uninitialized_var(daddr), uninitialized_var(saddr); __be16 uninitialized_var(dport), uninitialized_var(sport); u8 uninitialized_var(protocol); @@ -155,9 +155,11 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par, } #endif - sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), protocol, - saddr, daddr, sport, dport, par->in, NFT_LOOKUP_ANY); - if (sk != NULL) { + if (!sk) + sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), protocol, + saddr, daddr, sport, dport, + par->in, NFT_LOOKUP_ANY); + if (sk) { bool wildcard; bool transparent = true; @@ -173,7 +175,8 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par, (sk->sk_state == TCP_TIME_WAIT && inet_twsk(sk)->tw_transparent)); - xt_socket_put_sk(sk); + if (sk != skb->sk) + xt_socket_put_sk(sk); if (wildcard || !transparent) sk = NULL; @@ -260,7 +263,7 @@ socket_mt6_v1(const struct sk_buff *skb, struct xt_action_param *par) { struct ipv6hdr *iph = ipv6_hdr(skb); struct udphdr _hdr, *hp = NULL; - struct sock *sk; + struct sock *sk = skb->sk; struct in6_addr *daddr = NULL, *saddr = NULL; __be16 uninitialized_var(dport), uninitialized_var(sport); int thoff = 0, uninitialized_var(tproto); @@ -291,9 +294,11 @@ socket_mt6_v1(const struct sk_buff *skb, struct xt_action_param *par) return false; } - sk = nf_tproxy_get_sock_v6(dev_net(skb->dev), tproto, - saddr, daddr, sport, dport, par->in, NFT_LOOKUP_ANY); - if (sk != NULL) { + if (!sk) + sk = nf_tproxy_get_sock_v6(dev_net(skb->dev), tproto, + saddr, daddr, sport, dport, + par->in, NFT_LOOKUP_ANY); + if (sk) { bool wildcard; bool transparent = true; @@ -309,7 +314,8 @@ socket_mt6_v1(const struct sk_buff *skb, struct xt_action_param *par) (sk->sk_state == TCP_TIME_WAIT && inet_twsk(sk)->tw_transparent)); - xt_socket_put_sk(sk); + if (sk != skb->sk) + xt_socket_put_sk(sk); if (wildcard || !transparent) sk = NULL; -- cgit v1.2.3 From 8bc14d25ffb9dfc242d3a877bb4fe683adb27692 Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Thu, 16 May 2013 22:07:22 +0000 Subject: bridge: netfilter: using strlcpy() instead of strncpy() 'name' has already set all zero when it is defined, so not need let strncpy() to pad it again. 'name' is a string, better always let is NUL terminated, so use strlcpy() instead of strncpy(). Signed-off-by: Chen Gang Acked-by: Bart De Schuymer Signed-off-by: Pablo Neira Ayuso --- net/bridge/netfilter/ebtables.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 3d110c4fc787..ac7802428384 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -1339,7 +1339,7 @@ static inline int ebt_make_matchname(const struct ebt_entry_match *m, /* ebtables expects 32 bytes long names but xt_match names are 29 bytes long. Copy 29 bytes and fill remaining bytes with zeroes. */ - strncpy(name, m->u.match->name, sizeof(name)); + strlcpy(name, m->u.match->name, sizeof(name)); if (copy_to_user(hlp, name, EBT_FUNCTION_MAXNAMELEN)) return -EFAULT; return 0; @@ -1351,7 +1351,7 @@ static inline int ebt_make_watchername(const struct ebt_entry_watcher *w, char __user *hlp = ubase + ((char *)w - base); char name[EBT_FUNCTION_MAXNAMELEN] = {}; - strncpy(name, w->u.watcher->name, sizeof(name)); + strlcpy(name, w->u.watcher->name, sizeof(name)); if (copy_to_user(hlp , name, EBT_FUNCTION_MAXNAMELEN)) return -EFAULT; return 0; @@ -1377,7 +1377,7 @@ ebt_make_names(struct ebt_entry *e, const char *base, char __user *ubase) ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase); if (ret != 0) return ret; - strncpy(name, t->u.target->name, sizeof(name)); + strlcpy(name, t->u.target->name, sizeof(name)); if (copy_to_user(hlp, name, EBT_FUNCTION_MAXNAMELEN)) return -EFAULT; return 0; -- cgit v1.2.3 From 90a8864320b2a9f91e5b5d561924a4bb70b90dcc Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 3 May 2013 17:23:45 -0300 Subject: drm/i915: set FORCE_ARB_IDLE_PLANES workaround MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 1544d9d57396d5c0c6b7644ed5ae1f4d6caad07a added a workaround inside haswell_init_clock_gating and mentioned it is "a workaround for early silicon revisions and should be removed later". This workaround is documented in bit 31 of PRI_CTL. I asked Arthur and he mentioned that setting FORCE_ARB_IDLE_PLANES replaces that workaround for the newer machines. So use the new one. Also notice that there's still another workaround for PRI_CTL that involves WM_DBG, but it's not the one we're reverting. And notice that we were previously setting WM_DBG_DISALLOW_MULTIPIPE_LP which disables the LP watermarks when more than one pipe is used, and we really don't want this because we need the LP watermarks if we want to reach deeper PC states. Signed-off-by: Paulo Zanoni Reviewed-by: Ville Syrjälä [danvet: Add a comment for the w/a name Ville dug out of Bspec.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 3 +++ drivers/gpu/drm/i915/intel_pm.c | 11 +++-------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index e4cf382f0b75..55caedba53d1 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -3711,6 +3711,9 @@ # define CHICKEN3_DGMG_REQ_OUT_FIX_DISABLE (1 << 5) # define CHICKEN3_DGMG_DONE_FIX_DISABLE (1 << 2) +#define CHICKEN_PAR1_1 0x42080 +#define FORCE_ARB_IDLE_PLANES (1 << 14) + #define DISP_ARB_CTL 0x45000 #define DISP_TILE_SURFACE_SWIZZLING (1<<13) #define DISP_FBC_WM_DIS (1<<15) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 912ab4d8d722..e198f3881f9a 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -4172,14 +4172,9 @@ static void haswell_init_clock_gating(struct drm_device *dev) /* WaSwitchSolVfFArbitrationPriority:hsw */ I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) | HSW_ECOCHK_ARB_PRIO_SOL); - /* XXX: This is a workaround for early silicon revisions and should be - * removed later. - */ - I915_WRITE(WM_DBG, - I915_READ(WM_DBG) | - WM_DBG_DISALLOW_MULTIPLE_LP | - WM_DBG_DISALLOW_SPRITE | - WM_DBG_DISALLOW_MAXFIFO); + /* WaRsPkgCStateDisplayPMReq:hsw */ + I915_WRITE(CHICKEN_PAR1_1, + I915_READ(CHICKEN_PAR1_1) | FORCE_ARB_IDLE_PLANES); lpt_init_clock_gating(dev); } -- cgit v1.2.3 From a36689cb771f06819c3fa8139c3d3716dfdf6d53 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 21 May 2013 16:58:49 +0100 Subject: drm/i915: Be more informative when reporting "too large for aperture" error This should help debugging the truly unexpected cases where it occurs - in particular to see which value is garbage. References: https://bugzilla.kernel.org/show_bug.cgi?id=58511 Signed-off-by: Chris Wilson [danvet: s/%ld/%zd/ as spotted by Wu Fengguang's autobuilder.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index a1a282ce2466..39a9828cdff9 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2975,7 +2975,10 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, */ if (obj->base.size > (map_and_fenceable ? dev_priv->gtt.mappable_end : dev_priv->gtt.total)) { - DRM_ERROR("Attempting to bind an object larger than the aperture\n"); + DRM_ERROR("Attempting to bind an object larger than the aperture: object=%zd > %s aperture=%ld\n", + obj->base.size, + map_and_fenceable ? "mappable" : "total", + map_and_fenceable ? dev_priv->gtt.mappable_end : dev_priv->gtt.total); return -E2BIG; } -- cgit v1.2.3 From df0a67979543e716d411eb11406848dcb50abd0a Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Wed, 22 May 2013 11:36:40 +0300 Subject: drm/i915: Fix WARN_ON() on UP machines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WARN_ON(!spin_is_locked()) is not a good idea on a UP system w/o spinlock debugging. Use WARN_ON_SMP() instead. This check has been added in commit 8ba2d18520ce380cf572e9902d9b3b91ece6c2c0 Author: Jani Nikula Date: Fri Apr 12 15:18:37 2013 +0300 drm/i915: protect backlight registers and data with a spinlock Signed-off-by: Ville Syrjälä Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_panel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index 56f17b2382fc..80bea1d3209f 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -332,7 +332,7 @@ static u32 i915_read_blc_pwm_ctl(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; u32 val; - WARN_ON(!spin_is_locked(&dev_priv->backlight.lock)); + WARN_ON_SMP(!spin_is_locked(&dev_priv->backlight.lock)); /* Restore the CTL value if it lost, e.g. GPU reset */ -- cgit v1.2.3 From 2dc8aae06d53458dd3624dc0accd4f81100ee631 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 22 May 2013 17:08:06 +0100 Subject: drm/i915: Workaround incoherence with fence updates on Valleyview In commit 25ff1195f8a0b3724541ae7bbe331b4296de9c06 Author: Chris Wilson Date: Thu Apr 4 21:31:03 2013 +0100 drm/i915: Workaround incoherence between fences and LLC across multiple CPUs we introduced an empirical workaround for memory corruption when using fences from multiple CPUs. At the time, we did not have any results for Valleyview, so the presumption was that it was limited to recent generations using LLC. Now we have evidence that Valleyview also suffers incoherence and requires a similar but different workaround. For Valleyview, the wbinvd instruction is insufficient and we require the serialising register write per-CPU. Conversely, that serialising register write is not enough for SNB/IVB/HSW. To compromise and keep the code relatively clean, employ both serialisation techniques in the same workaround. Reported-by: Jon Bloomfield Tested-by: Jon Bloomfield Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=62191 Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 39a9828cdff9..1b735a4a2873 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2693,18 +2693,33 @@ static inline int fence_number(struct drm_i915_private *dev_priv, return fence - dev_priv->fence_regs; } +struct write_fence { + struct drm_device *dev; + struct drm_i915_gem_object *obj; + int fence; +}; + static void i915_gem_write_fence__ipi(void *data) { + struct write_fence *args = data; + + /* Required for SNB+ with LLC */ wbinvd(); + + /* Required for VLV */ + i915_gem_write_fence(args->dev, args->fence, args->obj); } static void i915_gem_object_update_fence(struct drm_i915_gem_object *obj, struct drm_i915_fence_reg *fence, bool enable) { - struct drm_device *dev = obj->base.dev; - struct drm_i915_private *dev_priv = dev->dev_private; - int fence_reg = fence_number(dev_priv, fence); + struct drm_i915_private *dev_priv = obj->base.dev->dev_private; + struct write_fence args = { + .dev = obj->base.dev, + .fence = fence_number(dev_priv, fence), + .obj = enable ? obj : NULL, + }; /* In order to fully serialize access to the fenced region and * the update to the fence register we need to take extreme @@ -2715,13 +2730,19 @@ static void i915_gem_object_update_fence(struct drm_i915_gem_object *obj, * SNB+ we need to take a step further and emit an explicit wbinvd() * on each processor in order to manually flush all memory * transactions before updating the fence register. + * + * However, Valleyview complicates matter. There the wbinvd is + * insufficient and unlike SNB/IVB requires the serialising + * register write. (Note that that register write by itself is + * conversely not sufficient for SNB+.) To compromise, we do both. */ - if (HAS_LLC(obj->base.dev)) - on_each_cpu(i915_gem_write_fence__ipi, NULL, 1); - i915_gem_write_fence(dev, fence_reg, enable ? obj : NULL); + if (INTEL_INFO(args.dev)->gen >= 6) + on_each_cpu(i915_gem_write_fence__ipi, &args, 1); + else + i915_gem_write_fence(args.dev, args.fence, args.obj); if (enable) { - obj->fence_reg = fence_reg; + obj->fence_reg = args.fence; fence->obj = obj; list_move_tail(&fence->lru_list, &dev_priv->mm.fence_list); } else { -- cgit v1.2.3 From edbe1581c5f94f7fba39cd9a5b2facd624aab661 Mon Sep 17 00:00:00 2001 From: Thomas Meyer Date: Wed, 22 May 2013 23:07:09 +0200 Subject: drm/i915: Cocci spatch "memdup.spatch" Signed-off-by: Thomas Meyer Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 3426a6183188..9332558fc3dc 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2511,11 +2511,10 @@ intel_dp_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) return NULL; size = (intel_connector->edid->extensions + 1) * EDID_LENGTH; - edid = kmalloc(size, GFP_KERNEL); + edid = kmemdup(intel_connector->edid, size, GFP_KERNEL); if (!edid) return NULL; - memcpy(edid, intel_connector->edid, size); return edid; } -- cgit v1.2.3 From 039735369c8fb105d0a090c949b7f894425121d8 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Wed, 8 May 2013 17:16:45 +0300 Subject: drm: Fix drm_rect documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'struct' keyword was missing so struct drm_rect documentation never ended up in the generated docs. Also move the drm_rect documentations to a new section alognside the various helper functions and add a short description about the intended purpose of drm_rect. v2: Move to new section and add general description Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- Documentation/DocBook/drm.tmpl | 8 ++++++-- include/drm/drm_rect.h | 9 ++++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index 7c7af25b330c..91ee107d5d0e 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -1653,8 +1653,6 @@ void intel_crt_init(struct drm_device *dev) KMS API Functions !Edrivers/gpu/drm/drm_crtc.c -!Edrivers/gpu/drm/drm_rect.c -!Finclude/drm/drm_rect.h @@ -2163,6 +2161,12 @@ void intel_crt_init(struct drm_device *dev) EDID Helper Functions Reference !Edrivers/gpu/drm/drm_edid.c + + Rectangle Utilities Reference +!Pinclude/drm/drm_rect.h rect utils +!Iinclude/drm/drm_rect.h +!Edrivers/gpu/drm/drm_rect.c + diff --git a/include/drm/drm_rect.h b/include/drm/drm_rect.h index 64fa265c6ffb..d1286297567b 100644 --- a/include/drm/drm_rect.h +++ b/include/drm/drm_rect.h @@ -25,7 +25,14 @@ #define DRM_RECT_H /** - * drm_rect - two dimensional rectangle + * DOC: rect utils + * + * Utility functions to help manage rectangular areas for + * clipping, scaling, etc. calculations. + */ + +/** + * struct drm_rect - two dimensional rectangle * @x1: horizontal starting coordinate (inclusive) * @x2: horizontal ending coordinate (exclusive) * @y1: vertical starting coordinate (inclusive) -- cgit v1.2.3 From edc3d8848dc9fe2a470316363dab8ef211d77e01 Mon Sep 17 00:00:00 2001 From: Mika Kuoppala Date: Thu, 23 May 2013 13:55:35 +0300 Subject: drm/i915: avoid big kmallocs on reading error state Sometimes when user is trying to get error state out from debugfs after gpu hang, the memory is low and/or fragmented enough that kmalloc in seq_file will fail. Prevent big kmalloc by avoiding seq_file and instead convert error state to string in smaller chunks. v2: better alloc flags, better truncate, correct locking, and error handling improvements (Chris Wilson) v3: printf annotations (Daniel Vetter) Signed-off-by: Mika Kuoppala Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 242 ++++++++++++++++++++++++++--------- drivers/gpu/drm/i915/i915_drv.h | 16 ++- drivers/gpu/drm/i915/intel_display.c | 54 ++++---- drivers/gpu/drm/i915/intel_overlay.c | 13 +- 4 files changed, 232 insertions(+), 93 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index a55630a80f83..3a8409a31266 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -604,15 +604,80 @@ static const char *purgeable_flag(int purgeable) return purgeable ? " purgeable" : ""; } -static void print_error_buffers(struct seq_file *m, +static void i915_error_vprintf(struct drm_i915_error_state_buf *e, + const char *f, va_list args) +{ + unsigned len; + + if (!e->err && WARN(e->bytes > (e->size - 1), "overflow")) { + e->err = -ENOSPC; + return; + } + + if (e->bytes == e->size - 1 || e->err) + return; + + /* Seek the first printf which is hits start position */ + if (e->pos < e->start) { + len = vsnprintf(NULL, 0, f, args); + if (e->pos + len <= e->start) { + e->pos += len; + return; + } + + /* First vsnprintf needs to fit in full for memmove*/ + if (len >= e->size) { + e->err = -EIO; + return; + } + } + + len = vsnprintf(e->buf + e->bytes, e->size - e->bytes, f, args); + if (len >= e->size - e->bytes) + len = e->size - e->bytes - 1; + + /* If this is first printf in this window, adjust it so that + * start position matches start of the buffer + */ + if (e->pos < e->start) { + const size_t off = e->start - e->pos; + + /* Should not happen but be paranoid */ + if (off > len || e->bytes) { + e->err = -EIO; + return; + } + + memmove(e->buf, e->buf + off, len - off); + e->bytes = len - off; + e->pos = e->start; + return; + } + + e->bytes += len; + e->pos += len; +} + +void i915_error_printf(struct drm_i915_error_state_buf *e, const char *f, ...) +{ + va_list args; + + va_start(args, f); + i915_error_vprintf(e, f, args); + va_end(args); +} + +#define err_printf(e, ...) i915_error_printf(e, __VA_ARGS__) + +static void print_error_buffers(struct drm_i915_error_state_buf *m, const char *name, struct drm_i915_error_buffer *err, int count) { - seq_printf(m, "%s [%d]:\n", name, count); + err_printf(m, "%s [%d]:\n", name, count); while (count--) { - seq_printf(m, " %08x %8u %02x %02x %x %x%s%s%s%s%s%s%s", + err_printf(m, " %08x %8u %02x %02x %x %x%s%s%s%s%s%s%s", err->gtt_offset, err->size, err->read_domains, @@ -627,50 +692,50 @@ static void print_error_buffers(struct seq_file *m, cache_level_str(err->cache_level)); if (err->name) - seq_printf(m, " (name: %d)", err->name); + err_printf(m, " (name: %d)", err->name); if (err->fence_reg != I915_FENCE_REG_NONE) - seq_printf(m, " (fence: %d)", err->fence_reg); + err_printf(m, " (fence: %d)", err->fence_reg); - seq_printf(m, "\n"); + err_printf(m, "\n"); err++; } } -static void i915_ring_error_state(struct seq_file *m, +static void i915_ring_error_state(struct drm_i915_error_state_buf *m, struct drm_device *dev, struct drm_i915_error_state *error, unsigned ring) { BUG_ON(ring >= I915_NUM_RINGS); /* shut up confused gcc */ - seq_printf(m, "%s command stream:\n", ring_str(ring)); - seq_printf(m, " HEAD: 0x%08x\n", error->head[ring]); - seq_printf(m, " TAIL: 0x%08x\n", error->tail[ring]); - seq_printf(m, " CTL: 0x%08x\n", error->ctl[ring]); - seq_printf(m, " ACTHD: 0x%08x\n", error->acthd[ring]); - seq_printf(m, " IPEIR: 0x%08x\n", error->ipeir[ring]); - seq_printf(m, " IPEHR: 0x%08x\n", error->ipehr[ring]); - seq_printf(m, " INSTDONE: 0x%08x\n", error->instdone[ring]); + err_printf(m, "%s command stream:\n", ring_str(ring)); + err_printf(m, " HEAD: 0x%08x\n", error->head[ring]); + err_printf(m, " TAIL: 0x%08x\n", error->tail[ring]); + err_printf(m, " CTL: 0x%08x\n", error->ctl[ring]); + err_printf(m, " ACTHD: 0x%08x\n", error->acthd[ring]); + err_printf(m, " IPEIR: 0x%08x\n", error->ipeir[ring]); + err_printf(m, " IPEHR: 0x%08x\n", error->ipehr[ring]); + err_printf(m, " INSTDONE: 0x%08x\n", error->instdone[ring]); if (ring == RCS && INTEL_INFO(dev)->gen >= 4) - seq_printf(m, " BBADDR: 0x%08llx\n", error->bbaddr); + err_printf(m, " BBADDR: 0x%08llx\n", error->bbaddr); if (INTEL_INFO(dev)->gen >= 4) - seq_printf(m, " INSTPS: 0x%08x\n", error->instps[ring]); - seq_printf(m, " INSTPM: 0x%08x\n", error->instpm[ring]); - seq_printf(m, " FADDR: 0x%08x\n", error->faddr[ring]); + err_printf(m, " INSTPS: 0x%08x\n", error->instps[ring]); + err_printf(m, " INSTPM: 0x%08x\n", error->instpm[ring]); + err_printf(m, " FADDR: 0x%08x\n", error->faddr[ring]); if (INTEL_INFO(dev)->gen >= 6) { - seq_printf(m, " RC PSMI: 0x%08x\n", error->rc_psmi[ring]); - seq_printf(m, " FAULT_REG: 0x%08x\n", error->fault_reg[ring]); - seq_printf(m, " SYNC_0: 0x%08x [last synced 0x%08x]\n", + err_printf(m, " RC PSMI: 0x%08x\n", error->rc_psmi[ring]); + err_printf(m, " FAULT_REG: 0x%08x\n", error->fault_reg[ring]); + err_printf(m, " SYNC_0: 0x%08x [last synced 0x%08x]\n", error->semaphore_mboxes[ring][0], error->semaphore_seqno[ring][0]); - seq_printf(m, " SYNC_1: 0x%08x [last synced 0x%08x]\n", + err_printf(m, " SYNC_1: 0x%08x [last synced 0x%08x]\n", error->semaphore_mboxes[ring][1], error->semaphore_seqno[ring][1]); } - seq_printf(m, " seqno: 0x%08x\n", error->seqno[ring]); - seq_printf(m, " waiting: %s\n", yesno(error->waiting[ring])); - seq_printf(m, " ring->head: 0x%08x\n", error->cpu_ring_head[ring]); - seq_printf(m, " ring->tail: 0x%08x\n", error->cpu_ring_tail[ring]); + err_printf(m, " seqno: 0x%08x\n", error->seqno[ring]); + err_printf(m, " waiting: %s\n", yesno(error->waiting[ring])); + err_printf(m, " ring->head: 0x%08x\n", error->cpu_ring_head[ring]); + err_printf(m, " ring->tail: 0x%08x\n", error->cpu_ring_tail[ring]); } struct i915_error_state_file_priv { @@ -678,9 +743,11 @@ struct i915_error_state_file_priv { struct drm_i915_error_state *error; }; -static int i915_error_state(struct seq_file *m, void *unused) + +static int i915_error_state(struct i915_error_state_file_priv *error_priv, + struct drm_i915_error_state_buf *m) + { - struct i915_error_state_file_priv *error_priv = m->private; struct drm_device *dev = error_priv->dev; drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_error_state *error = error_priv->error; @@ -688,34 +755,35 @@ static int i915_error_state(struct seq_file *m, void *unused) int i, j, page, offset, elt; if (!error) { - seq_printf(m, "no error state collected\n"); + err_printf(m, "no error state collected\n"); return 0; } - seq_printf(m, "Time: %ld s %ld us\n", error->time.tv_sec, + err_printf(m, "Time: %ld s %ld us\n", error->time.tv_sec, error->time.tv_usec); - seq_printf(m, "Kernel: " UTS_RELEASE "\n"); - seq_printf(m, "PCI ID: 0x%04x\n", dev->pci_device); - seq_printf(m, "EIR: 0x%08x\n", error->eir); - seq_printf(m, "IER: 0x%08x\n", error->ier); - seq_printf(m, "PGTBL_ER: 0x%08x\n", error->pgtbl_er); - seq_printf(m, "FORCEWAKE: 0x%08x\n", error->forcewake); - seq_printf(m, "DERRMR: 0x%08x\n", error->derrmr); - seq_printf(m, "CCID: 0x%08x\n", error->ccid); + err_printf(m, "Kernel: " UTS_RELEASE "\n"); + err_printf(m, "PCI ID: 0x%04x\n", dev->pci_device); + err_printf(m, "EIR: 0x%08x\n", error->eir); + err_printf(m, "IER: 0x%08x\n", error->ier); + err_printf(m, "PGTBL_ER: 0x%08x\n", error->pgtbl_er); + err_printf(m, "FORCEWAKE: 0x%08x\n", error->forcewake); + err_printf(m, "DERRMR: 0x%08x\n", error->derrmr); + err_printf(m, "CCID: 0x%08x\n", error->ccid); for (i = 0; i < dev_priv->num_fence_regs; i++) - seq_printf(m, " fence[%d] = %08llx\n", i, error->fence[i]); + err_printf(m, " fence[%d] = %08llx\n", i, error->fence[i]); for (i = 0; i < ARRAY_SIZE(error->extra_instdone); i++) - seq_printf(m, " INSTDONE_%d: 0x%08x\n", i, error->extra_instdone[i]); + err_printf(m, " INSTDONE_%d: 0x%08x\n", i, + error->extra_instdone[i]); if (INTEL_INFO(dev)->gen >= 6) { - seq_printf(m, "ERROR: 0x%08x\n", error->error); - seq_printf(m, "DONE_REG: 0x%08x\n", error->done_reg); + err_printf(m, "ERROR: 0x%08x\n", error->error); + err_printf(m, "DONE_REG: 0x%08x\n", error->done_reg); } if (INTEL_INFO(dev)->gen == 7) - seq_printf(m, "ERR_INT: 0x%08x\n", error->err_int); + err_printf(m, "ERR_INT: 0x%08x\n", error->err_int); for_each_ring(ring, dev_priv, i) i915_ring_error_state(m, dev, error, i); @@ -734,24 +802,25 @@ static int i915_error_state(struct seq_file *m, void *unused) struct drm_i915_error_object *obj; if ((obj = error->ring[i].batchbuffer)) { - seq_printf(m, "%s --- gtt_offset = 0x%08x\n", + err_printf(m, "%s --- gtt_offset = 0x%08x\n", dev_priv->ring[i].name, obj->gtt_offset); offset = 0; for (page = 0; page < obj->page_count; page++) { for (elt = 0; elt < PAGE_SIZE/4; elt++) { - seq_printf(m, "%08x : %08x\n", offset, obj->pages[page][elt]); + err_printf(m, "%08x : %08x\n", offset, + obj->pages[page][elt]); offset += 4; } } } if (error->ring[i].num_requests) { - seq_printf(m, "%s --- %d requests\n", + err_printf(m, "%s --- %d requests\n", dev_priv->ring[i].name, error->ring[i].num_requests); for (j = 0; j < error->ring[i].num_requests; j++) { - seq_printf(m, " seqno 0x%08x, emitted %ld, tail 0x%08x\n", + err_printf(m, " seqno 0x%08x, emitted %ld, tail 0x%08x\n", error->ring[i].requests[j].seqno, error->ring[i].requests[j].jiffies, error->ring[i].requests[j].tail); @@ -759,13 +828,13 @@ static int i915_error_state(struct seq_file *m, void *unused) } if ((obj = error->ring[i].ringbuffer)) { - seq_printf(m, "%s --- ringbuffer = 0x%08x\n", + err_printf(m, "%s --- ringbuffer = 0x%08x\n", dev_priv->ring[i].name, obj->gtt_offset); offset = 0; for (page = 0; page < obj->page_count; page++) { for (elt = 0; elt < PAGE_SIZE/4; elt++) { - seq_printf(m, "%08x : %08x\n", + err_printf(m, "%08x : %08x\n", offset, obj->pages[page][elt]); offset += 4; @@ -775,12 +844,12 @@ static int i915_error_state(struct seq_file *m, void *unused) obj = error->ring[i].ctx; if (obj) { - seq_printf(m, "%s --- HW Context = 0x%08x\n", + err_printf(m, "%s --- HW Context = 0x%08x\n", dev_priv->ring[i].name, obj->gtt_offset); offset = 0; for (elt = 0; elt < PAGE_SIZE/16; elt += 4) { - seq_printf(m, "[%04x] %08x %08x %08x %08x\n", + err_printf(m, "[%04x] %08x %08x %08x %08x\n", offset, obj->pages[0][elt], obj->pages[0][elt+1], @@ -806,8 +875,7 @@ i915_error_state_write(struct file *filp, size_t cnt, loff_t *ppos) { - struct seq_file *m = filp->private_data; - struct i915_error_state_file_priv *error_priv = m->private; + struct i915_error_state_file_priv *error_priv = filp->private_data; struct drm_device *dev = error_priv->dev; int ret; @@ -842,25 +910,81 @@ static int i915_error_state_open(struct inode *inode, struct file *file) kref_get(&error_priv->error->ref); spin_unlock_irqrestore(&dev_priv->gpu_error.lock, flags); - return single_open(file, i915_error_state, error_priv); + file->private_data = error_priv; + + return 0; } static int i915_error_state_release(struct inode *inode, struct file *file) { - struct seq_file *m = file->private_data; - struct i915_error_state_file_priv *error_priv = m->private; + struct i915_error_state_file_priv *error_priv = file->private_data; if (error_priv->error) kref_put(&error_priv->error->ref, i915_error_state_free); kfree(error_priv); - return single_release(inode, file); + return 0; +} + +static ssize_t i915_error_state_read(struct file *file, char __user *userbuf, + size_t count, loff_t *pos) +{ + struct i915_error_state_file_priv *error_priv = file->private_data; + struct drm_i915_error_state_buf error_str; + loff_t tmp_pos = 0; + ssize_t ret_count = 0; + int ret = 0; + + memset(&error_str, 0, sizeof(error_str)); + + /* We need to have enough room to store any i915_error_state printf + * so that we can move it to start position. + */ + error_str.size = count + 1 > PAGE_SIZE ? count + 1 : PAGE_SIZE; + error_str.buf = kmalloc(error_str.size, + GFP_TEMPORARY | __GFP_NORETRY | __GFP_NOWARN); + + if (error_str.buf == NULL) { + error_str.size = PAGE_SIZE; + error_str.buf = kmalloc(error_str.size, GFP_TEMPORARY); + } + + if (error_str.buf == NULL) { + error_str.size = 128; + error_str.buf = kmalloc(error_str.size, GFP_TEMPORARY); + } + + if (error_str.buf == NULL) + return -ENOMEM; + + error_str.start = *pos; + + ret = i915_error_state(error_priv, &error_str); + if (ret) + goto out; + + if (error_str.bytes == 0 && error_str.err) { + ret = error_str.err; + goto out; + } + + ret_count = simple_read_from_buffer(userbuf, count, &tmp_pos, + error_str.buf, + error_str.bytes); + + if (ret_count < 0) + ret = ret_count; + else + *pos = error_str.start + ret_count; +out: + kfree(error_str.buf); + return ret ?: ret_count; } static const struct file_operations i915_error_state_fops = { .owner = THIS_MODULE, .open = i915_error_state_open, - .read = seq_read, + .read = i915_error_state_read, .write = i915_error_state_write, .llseek = default_llseek, .release = i915_error_state_release, diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 639ec0b61450..7772bb6f73ba 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -826,6 +826,15 @@ struct i915_gem_mm { u32 object_count; }; +struct drm_i915_error_state_buf { + unsigned bytes; + unsigned size; + int err; + u8 *buf; + loff_t start; + loff_t pos; +}; + struct i915_gpu_error { /* For hangcheck timer */ #define DRM_I915_HANGCHECK_PERIOD 1500 /* in ms */ @@ -1818,6 +1827,8 @@ void i915_gem_dump_object(struct drm_i915_gem_object *obj, int len, /* i915_debugfs.c */ int i915_debugfs_init(struct drm_minor *minor); void i915_debugfs_cleanup(struct drm_minor *minor); +__printf(2, 3) +void i915_error_printf(struct drm_i915_error_state_buf *e, const char *f, ...); /* i915_suspend.c */ extern int i915_save_state(struct drm_device *dev); @@ -1899,10 +1910,11 @@ int i915_reg_read_ioctl(struct drm_device *dev, void *data, /* 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); +extern void intel_overlay_print_error_state(struct drm_i915_error_state_buf *e, + struct intel_overlay_error_state *error); extern struct intel_display_error_state *intel_display_capture_error_state(struct drm_device *dev); -extern void intel_display_print_error_state(struct seq_file *m, +extern void intel_display_print_error_state(struct drm_i915_error_state_buf *e, struct drm_device *dev, struct intel_display_error_state *error); #endif diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 684ab6417dd4..1c0d6d3bfe83 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -9858,48 +9858,50 @@ intel_display_capture_error_state(struct drm_device *dev) return error; } +#define err_printf(e, ...) i915_error_printf(e, __VA_ARGS__) + void -intel_display_print_error_state(struct seq_file *m, +intel_display_print_error_state(struct drm_i915_error_state_buf *m, struct drm_device *dev, struct intel_display_error_state *error) { int i; - seq_printf(m, "Num Pipes: %d\n", INTEL_INFO(dev)->num_pipes); + err_printf(m, "Num Pipes: %d\n", INTEL_INFO(dev)->num_pipes); if (HAS_POWER_WELL(dev)) - seq_printf(m, "PWR_WELL_CTL2: %08x\n", + err_printf(m, "PWR_WELL_CTL2: %08x\n", error->power_well_driver); for_each_pipe(i) { - seq_printf(m, "Pipe [%d]:\n", i); - seq_printf(m, " CPU transcoder: %c\n", + err_printf(m, "Pipe [%d]:\n", i); + err_printf(m, " CPU transcoder: %c\n", transcoder_name(error->pipe[i].cpu_transcoder)); - seq_printf(m, " CONF: %08x\n", error->pipe[i].conf); - seq_printf(m, " SRC: %08x\n", error->pipe[i].source); - seq_printf(m, " HTOTAL: %08x\n", error->pipe[i].htotal); - seq_printf(m, " HBLANK: %08x\n", error->pipe[i].hblank); - seq_printf(m, " HSYNC: %08x\n", error->pipe[i].hsync); - seq_printf(m, " VTOTAL: %08x\n", error->pipe[i].vtotal); - seq_printf(m, " VBLANK: %08x\n", error->pipe[i].vblank); - seq_printf(m, " VSYNC: %08x\n", error->pipe[i].vsync); - - seq_printf(m, "Plane [%d]:\n", i); - seq_printf(m, " CNTR: %08x\n", error->plane[i].control); - seq_printf(m, " STRIDE: %08x\n", error->plane[i].stride); + err_printf(m, " CONF: %08x\n", error->pipe[i].conf); + err_printf(m, " SRC: %08x\n", error->pipe[i].source); + err_printf(m, " HTOTAL: %08x\n", error->pipe[i].htotal); + err_printf(m, " HBLANK: %08x\n", error->pipe[i].hblank); + err_printf(m, " HSYNC: %08x\n", error->pipe[i].hsync); + err_printf(m, " VTOTAL: %08x\n", error->pipe[i].vtotal); + err_printf(m, " VBLANK: %08x\n", error->pipe[i].vblank); + err_printf(m, " VSYNC: %08x\n", error->pipe[i].vsync); + + err_printf(m, "Plane [%d]:\n", i); + err_printf(m, " CNTR: %08x\n", error->plane[i].control); + err_printf(m, " STRIDE: %08x\n", error->plane[i].stride); if (INTEL_INFO(dev)->gen <= 3) { - seq_printf(m, " SIZE: %08x\n", error->plane[i].size); - seq_printf(m, " POS: %08x\n", error->plane[i].pos); + err_printf(m, " SIZE: %08x\n", error->plane[i].size); + err_printf(m, " POS: %08x\n", error->plane[i].pos); } if (INTEL_INFO(dev)->gen <= 7 && !IS_HASWELL(dev)) - seq_printf(m, " ADDR: %08x\n", error->plane[i].addr); + err_printf(m, " ADDR: %08x\n", error->plane[i].addr); if (INTEL_INFO(dev)->gen >= 4) { - seq_printf(m, " SURF: %08x\n", error->plane[i].surface); - seq_printf(m, " TILEOFF: %08x\n", error->plane[i].tile_offset); + err_printf(m, " SURF: %08x\n", error->plane[i].surface); + err_printf(m, " TILEOFF: %08x\n", error->plane[i].tile_offset); } - seq_printf(m, "Cursor [%d]:\n", i); - seq_printf(m, " CNTR: %08x\n", error->cursor[i].control); - seq_printf(m, " POS: %08x\n", error->cursor[i].position); - seq_printf(m, " BASE: %08x\n", error->cursor[i].base); + err_printf(m, "Cursor [%d]:\n", i); + err_printf(m, " CNTR: %08x\n", error->cursor[i].control); + err_printf(m, " POS: %08x\n", error->cursor[i].position); + err_printf(m, " BASE: %08x\n", error->cursor[i].base); } } #endif diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c index 67a2501d519d..836794b68fc6 100644 --- a/drivers/gpu/drm/i915/intel_overlay.c +++ b/drivers/gpu/drm/i915/intel_overlay.c @@ -1485,14 +1485,15 @@ err: } void -intel_overlay_print_error_state(struct seq_file *m, struct intel_overlay_error_state *error) +intel_overlay_print_error_state(struct drm_i915_error_state_buf *m, + struct intel_overlay_error_state *error) { - seq_printf(m, "Overlay, status: 0x%08x, interrupt: 0x%08x\n", - error->dovsta, error->isr); - seq_printf(m, " Register file at 0x%08lx:\n", - error->base); + i915_error_printf(m, "Overlay, status: 0x%08x, interrupt: 0x%08x\n", + error->dovsta, error->isr); + i915_error_printf(m, " Register file at 0x%08lx:\n", + error->base); -#define P(x) seq_printf(m, " " #x ": 0x%08x\n", error->regs.x) +#define P(x) i915_error_printf(m, " " #x ": 0x%08x\n", error->regs.x) P(OBUF_0Y); P(OBUF_1Y); P(OBUF_0U); -- cgit v1.2.3 From 31b9779cb292e2ea3312b15c5eda96b69edbb4da Mon Sep 17 00:00:00 2001 From: Vincent Palatin Date: Wed, 22 May 2013 14:39:50 -0700 Subject: HID: ignore Jabra speakerphones HID interface Add a quirk to ignore Jabra speakerphone 410 and 510 devices HID interface. On those devices, the USB audio interface is working nicely, but the HID interface is not working with the kernel usbhid driver, and it requires a specific userspace program. We could unbind it from userspace but just attaching the usbhid driver has sometimes nasty effects: either confusing the device state machine or triggering a storm of volume key events making eventual sound UI blinking like crazy. Signed-off-by: Vincent Palatin Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 2 ++ drivers/hid/hid-ids.h | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 264f55099940..5d2ef6626615 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2042,6 +2042,8 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1006) }, { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1007) }, { HID_USB_DEVICE(USB_VENDOR_ID_IMATION, USB_DEVICE_ID_DISC_STAKKA) }, + { HID_USB_DEVICE(USB_VENDOR_ID_JABRA, USB_DEVICE_ID_JABRA_SPEAK_410) }, + { HID_USB_DEVICE(USB_VENDOR_ID_JABRA, USB_DEVICE_ID_JABRA_SPEAK_510) }, { HID_USB_DEVICE(USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO) }, { HID_USB_DEVICE(USB_VENDOR_ID_KWORLD, USB_DEVICE_ID_KWORLD_RADIO_FM700) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_GPEN_560) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 38535c9243d5..533815b12ebc 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -447,6 +447,10 @@ #define USB_VENDOR_ID_IRTOUCHSYSTEMS 0x6615 #define USB_DEVICE_ID_IRTOUCH_INFRARED_USB 0x0070 +#define USB_VENDOR_ID_JABRA 0x0b0e +#define USB_DEVICE_ID_JABRA_SPEAK_410 0x0412 +#define USB_DEVICE_ID_JABRA_SPEAK_510 0x0420 + #define USB_VENDOR_ID_JESS 0x0c45 #define USB_DEVICE_ID_JESS_YUREX 0x1010 -- cgit v1.2.3 From 6d11cfdba52af08b889fd6d3ee4212930493eb38 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 22 May 2013 22:42:36 +0000 Subject: netfilter: don't panic on error while walking through the init path Don't panic if we hit an error while adding the nf_log or pernet netfilter support, just bail out. Signed-off-by: Pablo Neira Ayuso Acked-by: Gao feng --- include/linux/netfilter.h | 2 +- net/netfilter/core.c | 21 +++++++++++++++------ net/netfilter/nf_log.c | 5 +---- net/socket.c | 4 +++- 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index 0060fde3160e..de70f7b45b68 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -35,7 +35,7 @@ static inline void nf_inet_addr_mask(const union nf_inet_addr *a1, result->all[3] = a1->all[3] & mask->all[3]; } -extern void netfilter_init(void); +extern int netfilter_init(void); /* Largest hook number + 1 */ #define NF_MAX_HOOKS 8 diff --git a/net/netfilter/core.c b/net/netfilter/core.c index 07c865a31a3d..300539db7bb1 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -302,17 +302,26 @@ static struct pernet_operations netfilter_net_ops = { .exit = netfilter_net_exit, }; -void __init netfilter_init(void) +int __init netfilter_init(void) { - int i, h; + int i, h, ret; + for (i = 0; i < ARRAY_SIZE(nf_hooks); i++) { for (h = 0; h < NF_MAX_HOOKS; h++) INIT_LIST_HEAD(&nf_hooks[i][h]); } - if (register_pernet_subsys(&netfilter_net_ops) < 0) - panic("cannot create netfilter proc entry"); + ret = register_pernet_subsys(&netfilter_net_ops); + if (ret < 0) + goto err; + + ret = netfilter_log_init(); + if (ret < 0) + goto err_pernet; - if (netfilter_log_init() < 0) - panic("cannot initialize nf_log"); + return 0; +err_pernet: + unregister_pernet_subsys(&netfilter_net_ops); +err: + return ret; } diff --git a/net/netfilter/nf_log.c b/net/netfilter/nf_log.c index 388656d5a9ec..bd5474adcabc 100644 --- a/net/netfilter/nf_log.c +++ b/net/netfilter/nf_log.c @@ -368,10 +368,7 @@ static int __net_init nf_log_net_init(struct net *net) return 0; out_sysctl: - /* For init_net: errors will trigger panic, don't unroll on error. */ - if (!net_eq(net, &init_net)) - remove_proc_entry("nf_log", net->nf.proc_netfilter); - + remove_proc_entry("nf_log", net->nf.proc_netfilter); return ret; } diff --git a/net/socket.c b/net/socket.c index 6b94633ca61d..734194d36242 100644 --- a/net/socket.c +++ b/net/socket.c @@ -2612,7 +2612,9 @@ static int __init sock_init(void) */ #ifdef CONFIG_NETFILTER - netfilter_init(); + err = netfilter_init(); + if (err) + goto out; #endif #ifdef CONFIG_NETWORK_PHY_TIMESTAMPING -- cgit v1.2.3 From de94c4591bd606729af1b913d6e98c6c449e42df Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 22 May 2013 22:42:37 +0000 Subject: netfilter: {ipt,ebt}_ULOG: rise warning on deprecation This target has been superseded by NFLOG. Spot a warning so we prepare removal in a couple of years. Signed-off-by: Pablo Neira Ayuso Acked-by: Gao feng --- include/net/netns/x_tables.h | 6 ++++++ net/bridge/netfilter/ebt_ulog.c | 6 ++++++ net/ipv4/netfilter/Kconfig | 2 +- net/ipv4/netfilter/ipt_ULOG.c | 6 ++++++ 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/include/net/netns/x_tables.h b/include/net/netns/x_tables.h index c24060ee411e..02fe40f8c8fd 100644 --- a/include/net/netns/x_tables.h +++ b/include/net/netns/x_tables.h @@ -15,5 +15,11 @@ struct netns_xt { struct ebt_table *frame_filter; struct ebt_table *frame_nat; #endif +#if IS_ENABLED(CONFIG_IP_NF_TARGET_ULOG) + bool ulog_warn_deprecated; +#endif +#if IS_ENABLED(CONFIG_BRIDGE_EBT_ULOG) + bool ebt_ulog_warn_deprecated; +#endif }; #endif diff --git a/net/bridge/netfilter/ebt_ulog.c b/net/bridge/netfilter/ebt_ulog.c index fc1905c51417..2ec6c19ff903 100644 --- a/net/bridge/netfilter/ebt_ulog.c +++ b/net/bridge/netfilter/ebt_ulog.c @@ -267,6 +267,12 @@ static int ebt_ulog_tg_check(const struct xt_tgchk_param *par) { struct ebt_ulog_info *uloginfo = par->targinfo; + if (!par->net->xt.ebt_ulog_warn_deprecated) { + pr_info("ebt_ulog is deprecated and it will be removed soon, " + "use ebt_nflog instead\n"); + par->net->xt.ebt_ulog_warn_deprecated = true; + } + if (uloginfo->nlgroup > 31) return -EINVAL; diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig index e7916c193932..4e9028017428 100644 --- a/net/ipv4/netfilter/Kconfig +++ b/net/ipv4/netfilter/Kconfig @@ -111,7 +111,7 @@ config IP_NF_TARGET_REJECT To compile it as a module, choose M here. If unsure, say N. config IP_NF_TARGET_ULOG - tristate "ULOG target support" + tristate "ULOG target support (obsolete)" default m if NETFILTER_ADVANCED=n ---help--- diff --git a/net/ipv4/netfilter/ipt_ULOG.c b/net/ipv4/netfilter/ipt_ULOG.c index f8a222cb6448..c1953d07e2f4 100644 --- a/net/ipv4/netfilter/ipt_ULOG.c +++ b/net/ipv4/netfilter/ipt_ULOG.c @@ -325,6 +325,12 @@ static int ulog_tg_check(const struct xt_tgchk_param *par) { const struct ipt_ulog_info *loginfo = par->targinfo; + if (!par->net->xt.ulog_warn_deprecated) { + pr_info("ULOG is deprecated and it will be removed soon, " + "use NFLOG instead\n"); + par->net->xt.ulog_warn_deprecated = true; + } + if (loginfo->prefix[sizeof(loginfo->prefix) - 1] != '\0') { pr_debug("prefix not null-terminated\n"); return -EINVAL; -- cgit v1.2.3 From f6f3c437d09e2f62533034e67bfb4385191e992c Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Wed, 22 May 2013 14:50:31 +0900 Subject: sched: add cond_resched_rcu() helper This is intended for use in loops which read data protected by RCU and may have a large number of iterations. Such an example is dumping the list of connections known to IPVS: ip_vs_conn_array() and ip_vs_conn_seq_next(). The benefits are for CONFIG_PREEMPT_RCU=y where we save CPU cycles by moving rcu_read_lock and rcu_read_unlock out of large loops but still allowing the current task to be preempted after every loop iteration for the CONFIG_PREEMPT_RCU=n case. The call to cond_resched() is not needed when CONFIG_PREEMPT_RCU=y. Thanks to Paul E. McKenney for explaining this and for the final version that checks the context with CONFIG_DEBUG_ATOMIC_SLEEP=y for all possible configurations. The function can be empty in the CONFIG_PREEMPT_RCU case, rcu_read_lock and rcu_read_unlock are not needed in this case because the task can be preempted on indication from scheduler. Thanks to Peter Zijlstra for catching this and for his help in trying a solution that changes __might_sleep. Initial cond_resched_rcu_lock() function suggested by Eric Dumazet. Tested-by: Julian Anastasov Signed-off-by: Julian Anastasov Signed-off-by: Simon Horman Acked-by: Peter Zijlstra Signed-off-by: Pablo Neira Ayuso --- include/linux/sched.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/linux/sched.h b/include/linux/sched.h index 178a8d909f14..4ff8da189253 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2444,6 +2444,15 @@ extern int __cond_resched_softirq(void); __cond_resched_softirq(); \ }) +static inline void cond_resched_rcu(void) +{ +#if defined(CONFIG_DEBUG_ATOMIC_SLEEP) || !defined(CONFIG_PREEMPT_RCU) + rcu_read_unlock(); + cond_resched(); + rcu_read_lock(); +#endif +} + /* * Does a critical section need to be broken due to another * task waiting?: (technically does not depend on CONFIG_PREEMPT, -- cgit v1.2.3 From a38e5e230e3f4e7bc9195d3e7a81567c888257ca Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Wed, 22 May 2013 14:50:32 +0900 Subject: ipvs: use cond_resched_rcu() helper when walking connections This avoids the situation where walking of a large number of connections may prevent scheduling for a long time while also avoiding excessive calls to rcu_read_unlock() and rcu_read_lock(). Note that in the case of !CONFIG_PREEMPT_RCU this will add a call to cond_resched(). Signed-off-by: Julian Anastasov Signed-off-by: Simon Horman Acked-by: Peter Zijlstra Signed-off-by: Pablo Neira Ayuso --- net/netfilter/ipvs/ip_vs_conn.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index a083bda322b6..c8c52a98590b 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -975,8 +975,7 @@ static void *ip_vs_conn_array(struct seq_file *seq, loff_t pos) return cp; } } - rcu_read_unlock(); - rcu_read_lock(); + cond_resched_rcu(); } return NULL; @@ -1015,8 +1014,7 @@ static void *ip_vs_conn_seq_next(struct seq_file *seq, void *v, loff_t *pos) iter->l = &ip_vs_conn_tab[idx]; return cp; } - rcu_read_unlock(); - rcu_read_lock(); + cond_resched_rcu(); } iter->l = NULL; return NULL; @@ -1206,17 +1204,13 @@ void ip_vs_random_dropentry(struct net *net) int idx; struct ip_vs_conn *cp, *cp_c; + rcu_read_lock(); /* * Randomly scan 1/32 of the whole table every second */ for (idx = 0; idx < (ip_vs_conn_tab_size>>5); idx++) { unsigned int hash = net_random() & ip_vs_conn_tab_mask; - /* - * Lock is actually needed in this loop. - */ - rcu_read_lock(); - hlist_for_each_entry_rcu(cp, &ip_vs_conn_tab[hash], c_list) { if (cp->flags & IP_VS_CONN_F_TEMPLATE) /* connection template */ @@ -1252,8 +1246,9 @@ void ip_vs_random_dropentry(struct net *net) __ip_vs_conn_put(cp); } } - rcu_read_unlock(); + cond_resched_rcu(); } + rcu_read_unlock(); } @@ -1267,11 +1262,8 @@ static void ip_vs_conn_flush(struct net *net) struct netns_ipvs *ipvs = net_ipvs(net); flush_again: + rcu_read_lock(); for (idx = 0; idx < ip_vs_conn_tab_size; idx++) { - /* - * Lock is actually needed in this loop. - */ - rcu_read_lock(); hlist_for_each_entry_rcu(cp, &ip_vs_conn_tab[idx], c_list) { if (!ip_vs_conn_net_eq(cp, net)) @@ -1286,8 +1278,9 @@ flush_again: __ip_vs_conn_put(cp); } } - rcu_read_unlock(); + cond_resched_rcu(); } + rcu_read_unlock(); /* the counter may be not NULL, because maybe some conn entries are run by slow timer handler or unhashed but still referred */ -- cgit v1.2.3 From 54d672e922cdb7e015a22a3a2c096dcac5ba284f Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 20 May 2013 14:08:34 +0530 Subject: ALSA: pxa2xx-ac97: Remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Cc: Eric Miao Signed-off-by: Takashi Iwai --- sound/arm/pxa2xx-ac97.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c index ec54be4efff0..ce431e6e07cf 100644 --- a/sound/arm/pxa2xx-ac97.c +++ b/sound/arm/pxa2xx-ac97.c @@ -230,7 +230,6 @@ static int pxa2xx_ac97_remove(struct platform_device *dev) if (card) { snd_card_free(card); - platform_set_drvdata(dev, NULL); pxa2xx_ac97_hw_remove(dev); } -- cgit v1.2.3 From 5ed5824bf43da9e2931fb94043a2942d73bec283 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 20 May 2013 14:08:35 +0530 Subject: ALSA: aloop: Remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Cc: Jaroslav Kysela Signed-off-by: Takashi Iwai --- sound/drivers/aloop.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c index 6f78de9c6fb6..f7589923effa 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c @@ -1183,7 +1183,6 @@ static int loopback_probe(struct platform_device *devptr) static int loopback_remove(struct platform_device *devptr) { snd_card_free(platform_get_drvdata(devptr)); - platform_set_drvdata(devptr, NULL); return 0; } -- cgit v1.2.3 From 186c1821f94719a02e570cf45e2047ffb6ceaddf Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 20 May 2013 14:08:36 +0530 Subject: ALSA: ml403-ac97cr: Remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Takashi Iwai --- sound/drivers/ml403-ac97cr.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/drivers/ml403-ac97cr.c b/sound/drivers/ml403-ac97cr.c index 8125a7e95ee4..95ea4a153ea4 100644 --- a/sound/drivers/ml403-ac97cr.c +++ b/sound/drivers/ml403-ac97cr.c @@ -1325,7 +1325,6 @@ static int snd_ml403_ac97cr_probe(struct platform_device *pfdev) static int snd_ml403_ac97cr_remove(struct platform_device *pfdev) { snd_card_free(platform_get_drvdata(pfdev)); - platform_set_drvdata(pfdev, NULL); return 0; } -- cgit v1.2.3 From 984ef60cb148c468925f5b1a15a3f425c9d6bf6c Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 20 May 2013 14:08:37 +0530 Subject: ALSA: mpu401: Remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Takashi Iwai --- sound/drivers/mpu401/mpu401.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/drivers/mpu401/mpu401.c b/sound/drivers/mpu401/mpu401.c index da1a29bfc85d..90a3a7b38a2a 100644 --- a/sound/drivers/mpu401/mpu401.c +++ b/sound/drivers/mpu401/mpu401.c @@ -129,7 +129,6 @@ static int snd_mpu401_probe(struct platform_device *devptr) static int snd_mpu401_remove(struct platform_device *devptr) { snd_card_free(platform_get_drvdata(devptr)); - platform_set_drvdata(devptr, NULL); return 0; } -- cgit v1.2.3 From a204341dae68bee125f4e7f22c5640cb4e3aae16 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 20 May 2013 14:08:38 +0530 Subject: ALSA: mtpav: Remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Takashi Iwai --- sound/drivers/mtpav.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/drivers/mtpav.c b/sound/drivers/mtpav.c index 9f1815b99a15..e5ec7eb27dec 100644 --- a/sound/drivers/mtpav.c +++ b/sound/drivers/mtpav.c @@ -749,7 +749,6 @@ static int snd_mtpav_probe(struct platform_device *dev) static int snd_mtpav_remove(struct platform_device *devptr) { snd_card_free(platform_get_drvdata(devptr)); - platform_set_drvdata(devptr, NULL); return 0; } -- cgit v1.2.3 From a4302ede92d143dd6c0f3dfbe25bcce852249e61 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 20 May 2013 14:08:39 +0530 Subject: ALSA: pcsp: Remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Cc: Stas Sergeev Signed-off-by: Takashi Iwai --- sound/drivers/pcsp/pcsp.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/drivers/pcsp/pcsp.c b/sound/drivers/pcsp/pcsp.c index 7a5fdb9b0afc..1c19cd7ad26e 100644 --- a/sound/drivers/pcsp/pcsp.c +++ b/sound/drivers/pcsp/pcsp.c @@ -189,7 +189,6 @@ static int pcsp_remove(struct platform_device *dev) struct snd_pcsp *chip = platform_get_drvdata(dev); alsa_card_pcsp_exit(chip); pcspkr_input_remove(chip->input_dev); - platform_set_drvdata(dev, NULL); return 0; } -- cgit v1.2.3 From 45837cb214a7fd076ae66a87a9425588156d3a78 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 20 May 2013 14:08:40 +0530 Subject: ALSA: serial-u16550: Remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Takashi Iwai --- sound/drivers/serial-u16550.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/drivers/serial-u16550.c b/sound/drivers/serial-u16550.c index 7425dd8c1f09..e0bf5e77b43a 100644 --- a/sound/drivers/serial-u16550.c +++ b/sound/drivers/serial-u16550.c @@ -985,7 +985,6 @@ static int snd_serial_probe(struct platform_device *devptr) static int snd_serial_remove(struct platform_device *devptr) { snd_card_free(platform_get_drvdata(devptr)); - platform_set_drvdata(devptr, NULL); return 0; } -- cgit v1.2.3 From 50c7d0da64e031c072fdc51b30d05c98761e3ebc Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 20 May 2013 14:08:41 +0530 Subject: ALSA: virmidi: Remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Takashi Iwai --- sound/drivers/virmidi.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/drivers/virmidi.c b/sound/drivers/virmidi.c index cc4be88d7318..ace3879e8d96 100644 --- a/sound/drivers/virmidi.c +++ b/sound/drivers/virmidi.c @@ -132,7 +132,6 @@ static int snd_virmidi_probe(struct platform_device *devptr) static int snd_virmidi_remove(struct platform_device *devptr) { snd_card_free(platform_get_drvdata(devptr)); - platform_set_drvdata(devptr, NULL); return 0; } -- cgit v1.2.3 From 464ede3ce59ba6144ff117ffa6427cac77fc6807 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 20 May 2013 14:08:42 +0530 Subject: ALSA: powermac: Remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Takashi Iwai --- sound/ppc/powermac.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/ppc/powermac.c b/sound/ppc/powermac.c index 09fc848d32ec..8abb521b4814 100644 --- a/sound/ppc/powermac.c +++ b/sound/ppc/powermac.c @@ -139,7 +139,6 @@ __error: static int snd_pmac_remove(struct platform_device *devptr) { snd_card_free(platform_get_drvdata(devptr)); - platform_set_drvdata(devptr, NULL); return 0; } -- cgit v1.2.3 From 2bc594a2764983532887c2c606172bd262c60644 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 20 May 2013 14:08:43 +0530 Subject: ALSA: sh: aica: Remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Cc: Adrian McMenamin Signed-off-by: Takashi Iwai --- sound/sh/aica.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/sh/aica.c b/sound/sh/aica.c index e59a73a9bc42..78a369785a9e 100644 --- a/sound/sh/aica.c +++ b/sound/sh/aica.c @@ -598,7 +598,6 @@ static int snd_aica_remove(struct platform_device *devptr) return -ENODEV; snd_card_free(dreamcastcard->card); kfree(dreamcastcard); - platform_set_drvdata(devptr, NULL); return 0; } -- cgit v1.2.3 From 3cf981484a002fd02c410741d30b234227f89568 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 20 May 2013 14:08:44 +0530 Subject: ALSA: sh_dac_audio: Remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Cc: Rafael Ignacio Zurita Signed-off-by: Takashi Iwai --- sound/sh/sh_dac_audio.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/sh/sh_dac_audio.c b/sound/sh/sh_dac_audio.c index e68c4fc91a03..7c9422c4fc0f 100644 --- a/sound/sh/sh_dac_audio.c +++ b/sound/sh/sh_dac_audio.c @@ -290,8 +290,6 @@ static int snd_sh_dac_pcm(struct snd_sh_dac *chip, int device) static int snd_sh_dac_remove(struct platform_device *devptr) { snd_card_free(platform_get_drvdata(devptr)); - platform_set_drvdata(devptr, NULL); - return 0; } -- cgit v1.2.3 From 47f5b692e04a8d7989ee14591a61be26e340a17b Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 23 May 2013 13:58:00 +0200 Subject: ASoC: adau1701: refactor firmware loading function Pass a struct i2c_client * to adau1701_load_firmware directly to make the code more readable. Signed-off-by: Daniel Mack Acked-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/codecs/adau1701.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c index dafdbe87edeb..95e1677665e9 100644 --- a/sound/soc/codecs/adau1701.c +++ b/sound/soc/codecs/adau1701.c @@ -180,9 +180,9 @@ static unsigned int adau1701_read(struct snd_soc_codec *codec, unsigned int reg) return value; } -static int adau1701_load_firmware(struct snd_soc_codec *codec) +static int adau1701_load_firmware(struct i2c_client *client) { - return process_sigma_firmware(codec->control_data, ADAU1701_FIRMWARE); + return process_sigma_firmware(client, ADAU1701_FIRMWARE); } static int adau1701_set_capture_pcm_format(struct snd_soc_codec *codec, @@ -455,10 +455,11 @@ static struct snd_soc_dai_driver adau1701_dai = { static int adau1701_probe(struct snd_soc_codec *codec) { int ret; + struct i2c_client *client = to_i2c_client(codec->dev); - codec->control_data = to_i2c_client(codec->dev); + codec->control_data = client; - ret = adau1701_load_firmware(codec); + ret = adau1701_load_firmware(client); if (ret) dev_warn(codec->dev, "Failed to load firmware\n"); -- cgit v1.2.3 From 81485f5220770c381ac076573642ac44f13723af Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 23 May 2013 15:06:15 +0200 Subject: regmap: regcache: Fixup locking for custom lock callbacks The parameter passed to the regmap lock/unlock callbacks needs to be map->lock_arg, regcache passes just map. This works fine in the case that no custom locking callbacks are used, since in this case map->lock_arg equals map, but will break when custom locking callbacks are used. The issue was introduced in commit 0d4529c5 ("regmap: make lock/unlock functions customizable") and is fixed by this patch. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- drivers/base/regmap/regcache-rbtree.c | 4 ++-- drivers/base/regmap/regcache.c | 24 ++++++++++++------------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index 4e131c5df844..69b443f204ac 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -143,7 +143,7 @@ static int rbtree_show(struct seq_file *s, void *ignored) int registers = 0; int this_registers, average; - map->lock(map); + map->lock(map->lock_arg); mem_size = sizeof(*rbtree_ctx); mem_size += BITS_TO_LONGS(map->cache_present_nbits) * sizeof(long); @@ -170,7 +170,7 @@ static int rbtree_show(struct seq_file *s, void *ignored) seq_printf(s, "%d nodes, %d registers, average %d registers, used %zu bytes\n", nodes, registers, average, mem_size); - map->unlock(map); + map->unlock(map->lock_arg); return 0; } diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 8a0ab5fa75f5..4bfa219ef37d 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -270,7 +270,7 @@ int regcache_sync(struct regmap *map) BUG_ON(!map->cache_ops || !map->cache_ops->sync); - map->lock(map); + map->lock(map->lock_arg); /* Remember the initial bypass state */ bypass = map->cache_bypass; dev_dbg(map->dev, "Syncing %s cache\n", @@ -306,7 +306,7 @@ out: trace_regcache_sync(map->dev, name, "stop"); /* Restore the bypass state */ map->cache_bypass = bypass; - map->unlock(map); + map->unlock(map->lock_arg); return ret; } @@ -333,7 +333,7 @@ int regcache_sync_region(struct regmap *map, unsigned int min, BUG_ON(!map->cache_ops || !map->cache_ops->sync); - map->lock(map); + map->lock(map->lock_arg); /* Remember the initial bypass state */ bypass = map->cache_bypass; @@ -352,7 +352,7 @@ out: trace_regcache_sync(map->dev, name, "stop region"); /* Restore the bypass state */ map->cache_bypass = bypass; - map->unlock(map); + map->unlock(map->lock_arg); return ret; } @@ -378,7 +378,7 @@ int regcache_drop_region(struct regmap *map, unsigned int min, if (!map->cache_present && !(map->cache_ops && map->cache_ops->drop)) return -EINVAL; - map->lock(map); + map->lock(map->lock_arg); trace_regcache_drop_region(map->dev, min, max); @@ -389,7 +389,7 @@ int regcache_drop_region(struct regmap *map, unsigned int min, if (map->cache_ops && map->cache_ops->drop) ret = map->cache_ops->drop(map, min, max); - map->unlock(map); + map->unlock(map->lock_arg); return ret; } @@ -409,11 +409,11 @@ EXPORT_SYMBOL_GPL(regcache_drop_region); */ void regcache_cache_only(struct regmap *map, bool enable) { - map->lock(map); + map->lock(map->lock_arg); WARN_ON(map->cache_bypass && enable); map->cache_only = enable; trace_regmap_cache_only(map->dev, enable); - map->unlock(map); + map->unlock(map->lock_arg); } EXPORT_SYMBOL_GPL(regcache_cache_only); @@ -428,9 +428,9 @@ EXPORT_SYMBOL_GPL(regcache_cache_only); */ void regcache_mark_dirty(struct regmap *map) { - map->lock(map); + map->lock(map->lock_arg); map->cache_dirty = true; - map->unlock(map); + map->unlock(map->lock_arg); } EXPORT_SYMBOL_GPL(regcache_mark_dirty); @@ -447,11 +447,11 @@ EXPORT_SYMBOL_GPL(regcache_mark_dirty); */ void regcache_cache_bypass(struct regmap *map, bool enable) { - map->lock(map); + map->lock(map->lock_arg); WARN_ON(map->cache_only && enable); map->cache_bypass = enable; trace_regmap_cache_bypass(map->dev, enable); - map->unlock(map); + map->unlock(map->lock_arg); } EXPORT_SYMBOL_GPL(regcache_cache_bypass); -- cgit v1.2.3 From 24b5a82cf5709a4bc577f42fdaa61b23a7f58f08 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 23 May 2013 19:20:40 +0900 Subject: spi: use platform_{get,set}_drvdata() Use the wrapper functions for getting and setting the driver data using platform_device instead of using dev_{get,set}_drvdata() with &pdev->dev, so we can directly pass a struct platform_device. Signed-off-by: Jingoo Han Signed-off-by: Mark Brown --- drivers/spi/spi-davinci.c | 4 ++-- drivers/spi/spi-fsl-spi.c | 2 +- drivers/spi/spi-mpc52xx-psc.c | 2 +- drivers/spi/spi-mpc52xx.c | 4 ++-- drivers/spi/spi-omap-100k.c | 4 ++-- drivers/spi/spi-omap-uwire.c | 4 ++-- drivers/spi/spi-omap2-mcspi.c | 4 ++-- drivers/spi/spi-orion.c | 4 ++-- drivers/spi/spi-ppc4xx.c | 6 ++---- drivers/spi/spi-rspi.c | 4 ++-- drivers/spi/spi-sh-hspi.c | 4 ++-- drivers/spi/spi-sh.c | 4 ++-- drivers/spi/spi-tegra114.c | 4 ++-- drivers/spi/spi-tegra20-sflash.c | 4 ++-- drivers/spi/spi-tegra20-slink.c | 4 ++-- 15 files changed, 28 insertions(+), 30 deletions(-) diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c index 50b13c9b1ab6..968e36416cfa 100644 --- a/drivers/spi/spi-davinci.c +++ b/drivers/spi/spi-davinci.c @@ -865,7 +865,7 @@ static int davinci_spi_probe(struct platform_device *pdev) goto err; } - dev_set_drvdata(&pdev->dev, master); + platform_set_drvdata(pdev, master); dspi = spi_master_get_devdata(master); if (dspi == NULL) { @@ -1044,7 +1044,7 @@ static int davinci_spi_remove(struct platform_device *pdev) struct spi_master *master; struct resource *r; - master = dev_get_drvdata(&pdev->dev); + master = platform_get_drvdata(pdev); dspi = spi_master_get_devdata(master); spi_bitbang_stop(&dspi->bitbang); diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c index 14e202ee7036..41e89c3e3edc 100644 --- a/drivers/spi/spi-fsl-spi.c +++ b/drivers/spi/spi-fsl-spi.c @@ -853,7 +853,7 @@ err: static int of_fsl_spi_remove(struct platform_device *ofdev) { - struct spi_master *master = dev_get_drvdata(&ofdev->dev); + struct spi_master *master = platform_get_drvdata(ofdev); struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(master); int ret; diff --git a/drivers/spi/spi-mpc52xx-psc.c b/drivers/spi/spi-mpc52xx-psc.c index 291120b37dbb..fed0571d4dec 100644 --- a/drivers/spi/spi-mpc52xx-psc.c +++ b/drivers/spi/spi-mpc52xx-psc.c @@ -481,7 +481,7 @@ static int mpc52xx_psc_spi_of_probe(struct platform_device *op) static int mpc52xx_psc_spi_of_remove(struct platform_device *op) { - struct spi_master *master = spi_master_get(dev_get_drvdata(&op->dev)); + struct spi_master *master = spi_master_get(platform_get_drvdata(op)); struct mpc52xx_psc_spi *mps = spi_master_get_devdata(master); flush_workqueue(mps->workqueue); diff --git a/drivers/spi/spi-mpc52xx.c b/drivers/spi/spi-mpc52xx.c index 29f77056eedc..7c675fe83101 100644 --- a/drivers/spi/spi-mpc52xx.c +++ b/drivers/spi/spi-mpc52xx.c @@ -438,7 +438,7 @@ static int mpc52xx_spi_probe(struct platform_device *op) master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST; master->dev.of_node = op->dev.of_node; - dev_set_drvdata(&op->dev, master); + platform_set_drvdata(op, master); ms = spi_master_get_devdata(master); ms->master = master; @@ -529,7 +529,7 @@ static int mpc52xx_spi_probe(struct platform_device *op) static int mpc52xx_spi_remove(struct platform_device *op) { - struct spi_master *master = spi_master_get(dev_get_drvdata(&op->dev)); + struct spi_master *master = spi_master_get(platform_get_drvdata(op)); struct mpc52xx_spi *ms = spi_master_get_devdata(master); int i; diff --git a/drivers/spi/spi-omap-100k.c b/drivers/spi/spi-omap-100k.c index 78d29a18dcc4..9236764861a9 100644 --- a/drivers/spi/spi-omap-100k.c +++ b/drivers/spi/spi-omap-100k.c @@ -510,7 +510,7 @@ static int omap1_spi100k_probe(struct platform_device *pdev) master->num_chipselect = 2; master->mode_bits = MODEBITS; - dev_set_drvdata(&pdev->dev, master); + platform_set_drvdata(pdev, master); spi100k = spi_master_get_devdata(master); spi100k->master = master; @@ -569,7 +569,7 @@ static int omap1_spi100k_remove(struct platform_device *pdev) unsigned long flags; int status = 0; - master = dev_get_drvdata(&pdev->dev); + master = platform_get_drvdata(pdev); spi100k = spi_master_get_devdata(master); spin_lock_irqsave(&spi100k->lock, flags); diff --git a/drivers/spi/spi-omap-uwire.c b/drivers/spi/spi-omap-uwire.c index 102b233b50c4..a6a8f0961750 100644 --- a/drivers/spi/spi-omap-uwire.c +++ b/drivers/spi/spi-omap-uwire.c @@ -495,7 +495,7 @@ static int uwire_probe(struct platform_device *pdev) return -ENOMEM; } - dev_set_drvdata(&pdev->dev, uwire); + platform_set_drvdata(pdev, uwire); uwire->ck = clk_get(&pdev->dev, "fck"); if (IS_ERR(uwire->ck)) { @@ -538,7 +538,7 @@ static int uwire_probe(struct platform_device *pdev) static int uwire_remove(struct platform_device *pdev) { - struct uwire_spi *uwire = dev_get_drvdata(&pdev->dev); + struct uwire_spi *uwire = platform_get_drvdata(pdev); int status; // FIXME remove all child devices, somewhere ... diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 86d2158946bb..1a75aefd1504 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -1204,7 +1204,7 @@ static int omap2_mcspi_probe(struct platform_device *pdev) master->cleanup = omap2_mcspi_cleanup; master->dev.of_node = node; - dev_set_drvdata(&pdev->dev, master); + platform_set_drvdata(pdev, master); mcspi = spi_master_get_devdata(master); mcspi->master = master; @@ -1318,7 +1318,7 @@ static int omap2_mcspi_remove(struct platform_device *pdev) struct omap2_mcspi *mcspi; struct omap2_mcspi_dma *dma_channels; - master = dev_get_drvdata(&pdev->dev); + master = platform_get_drvdata(pdev); mcspi = spi_master_get_devdata(master); dma_channels = mcspi->dma_channels; diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c index 66a5f82cf138..5d90bebaa0fa 100644 --- a/drivers/spi/spi-orion.c +++ b/drivers/spi/spi-orion.c @@ -428,7 +428,7 @@ static int orion_spi_probe(struct platform_device *pdev) master->transfer_one_message = orion_spi_transfer_one_message; master->num_chipselect = ORION_NUM_CHIPSELECTS; - dev_set_drvdata(&pdev->dev, master); + platform_set_drvdata(pdev, master); spi = spi_master_get_devdata(master); spi->master = master; @@ -485,7 +485,7 @@ static int orion_spi_remove(struct platform_device *pdev) struct resource *r; struct orion_spi *spi; - master = dev_get_drvdata(&pdev->dev); + master = platform_get_drvdata(pdev); spi = spi_master_get_devdata(master); clk_disable_unprepare(spi->clk); diff --git a/drivers/spi/spi-ppc4xx.c b/drivers/spi/spi-ppc4xx.c index 357f183a4fb7..8548e574749d 100644 --- a/drivers/spi/spi-ppc4xx.c +++ b/drivers/spi/spi-ppc4xx.c @@ -406,7 +406,7 @@ static int spi_ppc4xx_of_probe(struct platform_device *op) if (master == NULL) return -ENOMEM; master->dev.of_node = np; - dev_set_drvdata(dev, master); + platform_set_drvdata(op, master); hw = spi_master_get_devdata(master); hw->master = spi_master_get(master); hw->dev = dev; @@ -553,7 +553,6 @@ request_mem_error: free_gpios: free_gpios(hw); free_master: - dev_set_drvdata(dev, NULL); spi_master_put(master); dev_err(dev, "initialization failed\n"); @@ -562,11 +561,10 @@ free_master: static int spi_ppc4xx_of_remove(struct platform_device *op) { - struct spi_master *master = dev_get_drvdata(&op->dev); + struct spi_master *master = platform_get_drvdata(op); struct ppc4xx_spi *hw = spi_master_get_devdata(master); spi_bitbang_stop(&hw->bitbang); - dev_set_drvdata(&op->dev, NULL); release_mem_region(hw->mapbase, hw->mapsize); free_irq(hw->irqnum, hw); iounmap(hw->regs); diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 902f2fb902db..b44a6ac3cec9 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -719,7 +719,7 @@ static void rspi_release_dma(struct rspi_data *rspi) static int rspi_remove(struct platform_device *pdev) { - struct rspi_data *rspi = dev_get_drvdata(&pdev->dev); + struct rspi_data *rspi = platform_get_drvdata(pdev); spi_unregister_master(rspi->master); rspi_release_dma(rspi); @@ -759,7 +759,7 @@ static int rspi_probe(struct platform_device *pdev) } rspi = spi_master_get_devdata(master); - dev_set_drvdata(&pdev->dev, rspi); + platform_set_drvdata(pdev, rspi); rspi->master = master; rspi->addr = ioremap(res->start, resource_size(res)); diff --git a/drivers/spi/spi-sh-hspi.c b/drivers/spi/spi-sh-hspi.c index 60cfae51c713..d3a62b9cfff3 100644 --- a/drivers/spi/spi-sh-hspi.c +++ b/drivers/spi/spi-sh-hspi.c @@ -297,7 +297,7 @@ static int hspi_probe(struct platform_device *pdev) } hspi = spi_master_get_devdata(master); - dev_set_drvdata(&pdev->dev, hspi); + platform_set_drvdata(pdev, hspi); /* init hspi */ hspi->master = master; @@ -341,7 +341,7 @@ static int hspi_probe(struct platform_device *pdev) static int hspi_remove(struct platform_device *pdev) { - struct hspi_priv *hspi = dev_get_drvdata(&pdev->dev); + struct hspi_priv *hspi = platform_get_drvdata(pdev); pm_runtime_disable(&pdev->dev); diff --git a/drivers/spi/spi-sh.c b/drivers/spi/spi-sh.c index 3c3600a994bd..c120a70094f2 100644 --- a/drivers/spi/spi-sh.c +++ b/drivers/spi/spi-sh.c @@ -434,7 +434,7 @@ static irqreturn_t spi_sh_irq(int irq, void *_ss) static int spi_sh_remove(struct platform_device *pdev) { - struct spi_sh_data *ss = dev_get_drvdata(&pdev->dev); + struct spi_sh_data *ss = platform_get_drvdata(pdev); spi_unregister_master(ss->master); destroy_workqueue(ss->workqueue); @@ -471,7 +471,7 @@ static int spi_sh_probe(struct platform_device *pdev) } ss = spi_master_get_devdata(master); - dev_set_drvdata(&pdev->dev, ss); + platform_set_drvdata(pdev, ss); switch (res->flags & IORESOURCE_MEM_TYPE_MASK) { case IORESOURCE_MEM_8BIT: diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index 598eb45e8008..e8f542ab8935 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -1041,7 +1041,7 @@ static int tegra_spi_probe(struct platform_device *pdev) dev_err(&pdev->dev, "master allocation failed\n"); return -ENOMEM; } - dev_set_drvdata(&pdev->dev, master); + platform_set_drvdata(pdev, master); tspi = spi_master_get_devdata(master); /* Parse DT */ @@ -1152,7 +1152,7 @@ exit_free_master: static int tegra_spi_remove(struct platform_device *pdev) { - struct spi_master *master = dev_get_drvdata(&pdev->dev); + struct spi_master *master = platform_get_drvdata(pdev); struct tegra_spi_data *tspi = spi_master_get_devdata(master); free_irq(tspi->irq, tspi); diff --git a/drivers/spi/spi-tegra20-sflash.c b/drivers/spi/spi-tegra20-sflash.c index 09df8e22dba0..c1d5d95e70ea 100644 --- a/drivers/spi/spi-tegra20-sflash.c +++ b/drivers/spi/spi-tegra20-sflash.c @@ -480,7 +480,7 @@ static int tegra_sflash_probe(struct platform_device *pdev) master->num_chipselect = MAX_CHIP_SELECT; master->bus_num = -1; - dev_set_drvdata(&pdev->dev, master); + platform_set_drvdata(pdev, master); tsd = spi_master_get_devdata(master); tsd->master = master; tsd->dev = &pdev->dev; @@ -555,7 +555,7 @@ exit_free_master: static int tegra_sflash_remove(struct platform_device *pdev) { - struct spi_master *master = dev_get_drvdata(&pdev->dev); + struct spi_master *master = platform_get_drvdata(pdev); struct tegra_sflash_data *tsd = spi_master_get_devdata(master); free_irq(tsd->irq, tsd); diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c index 3faf88d003de..80490cc11ce5 100644 --- a/drivers/spi/spi-tegra20-slink.c +++ b/drivers/spi/spi-tegra20-slink.c @@ -1089,7 +1089,7 @@ static int tegra_slink_probe(struct platform_device *pdev) master->num_chipselect = MAX_CHIP_SELECT; master->bus_num = -1; - dev_set_drvdata(&pdev->dev, master); + platform_set_drvdata(pdev, master); tspi = spi_master_get_devdata(master); tspi->master = master; tspi->dev = &pdev->dev; @@ -1193,7 +1193,7 @@ exit_free_master: static int tegra_slink_remove(struct platform_device *pdev) { - struct spi_master *master = dev_get_drvdata(&pdev->dev); + struct spi_master *master = platform_get_drvdata(pdev); struct tegra_slink_data *tspi = spi_master_get_devdata(master); free_irq(tspi->irq, tspi); -- cgit v1.2.3 From 35c4b1227e1f3ea8b8536834aaa96c2cc7c0cfa0 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 23 May 2013 09:20:21 -0700 Subject: Input: misc - use platform_{get,set}_drvdata() Use the wrapper functions for getting and setting the driver data using platform_device instead of using dev_{get,set}_drvdata() with &pdev->dev, so we can directly pass a struct platform_device. Also, unnecessary dev_set_drvdata() is removed, because the driver core clears the driver data to NULL after device_release or on probe failure. Signed-off-by: Jingoo Han Signed-off-by: Dmitry Torokhov --- drivers/input/misc/sgi_btns.c | 7 ++----- drivers/input/misc/sparcspkr.c | 14 +++++--------- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/drivers/input/misc/sgi_btns.c b/drivers/input/misc/sgi_btns.c index ad6415ceaf5f..95cf299ef9a3 100644 --- a/drivers/input/misc/sgi_btns.c +++ b/drivers/input/misc/sgi_btns.c @@ -128,7 +128,7 @@ static int sgi_buttons_probe(struct platform_device *pdev) __clear_bit(KEY_RESERVED, input->keybit); bdev->poll_dev = poll_dev; - dev_set_drvdata(&pdev->dev, bdev); + platform_set_drvdata(pdev, bdev); error = input_register_polled_device(poll_dev); if (error) @@ -139,19 +139,16 @@ static int sgi_buttons_probe(struct platform_device *pdev) err_free_mem: input_free_polled_device(poll_dev); kfree(bdev); - dev_set_drvdata(&pdev->dev, NULL); return error; } static int sgi_buttons_remove(struct platform_device *pdev) { - struct device *dev = &pdev->dev; - struct buttons_dev *bdev = dev_get_drvdata(dev); + struct buttons_dev *bdev = platform_get_drvdata(pdev); input_unregister_polled_device(bdev->poll_dev); input_free_polled_device(bdev->poll_dev); kfree(bdev); - dev_set_drvdata(dev, NULL); return 0; } diff --git a/drivers/input/misc/sparcspkr.c b/drivers/input/misc/sparcspkr.c index a53586a7fbdb..65fd3150919b 100644 --- a/drivers/input/misc/sparcspkr.c +++ b/drivers/input/misc/sparcspkr.c @@ -175,7 +175,7 @@ static int sparcspkr_probe(struct device *dev) static void sparcspkr_shutdown(struct platform_device *dev) { - struct sparcspkr_state *state = dev_get_drvdata(&dev->dev); + struct sparcspkr_state *state = platform_get_drvdata(dev); struct input_dev *input_dev = state->input_dev; /* turn off the speaker */ @@ -211,7 +211,7 @@ static int bbc_beep_probe(struct platform_device *op) if (!info->regs) goto out_free; - dev_set_drvdata(&op->dev, state); + platform_set_drvdata(op, state); err = sparcspkr_probe(&op->dev); if (err) @@ -220,7 +220,6 @@ static int bbc_beep_probe(struct platform_device *op) return 0; out_clear_drvdata: - dev_set_drvdata(&op->dev, NULL); of_iounmap(&op->resource[0], info->regs, 6); out_free: @@ -231,7 +230,7 @@ out_err: static int bbc_remove(struct platform_device *op) { - struct sparcspkr_state *state = dev_get_drvdata(&op->dev); + struct sparcspkr_state *state = platform_get_drvdata(op); struct input_dev *input_dev = state->input_dev; struct bbc_beep_info *info = &state->u.bbc; @@ -242,7 +241,6 @@ static int bbc_remove(struct platform_device *op) of_iounmap(&op->resource[0], info->regs, 6); - dev_set_drvdata(&op->dev, NULL); kfree(state); return 0; @@ -290,7 +288,7 @@ static int grover_beep_probe(struct platform_device *op) if (!info->enable_reg) goto out_unmap_freq_regs; - dev_set_drvdata(&op->dev, state); + platform_set_drvdata(op, state); err = sparcspkr_probe(&op->dev); if (err) @@ -299,7 +297,6 @@ static int grover_beep_probe(struct platform_device *op) return 0; out_clear_drvdata: - dev_set_drvdata(&op->dev, NULL); of_iounmap(&op->resource[3], info->enable_reg, 1); out_unmap_freq_regs: @@ -312,7 +309,7 @@ out_err: static int grover_remove(struct platform_device *op) { - struct sparcspkr_state *state = dev_get_drvdata(&op->dev); + struct sparcspkr_state *state = platform_get_drvdata(op); struct grover_beep_info *info = &state->u.grover; struct input_dev *input_dev = state->input_dev; @@ -324,7 +321,6 @@ static int grover_remove(struct platform_device *op) of_iounmap(&op->resource[3], info->enable_reg, 1); of_iounmap(&op->resource[2], info->freq_regs, 2); - dev_set_drvdata(&op->dev, NULL); kfree(state); return 0; -- cgit v1.2.3 From edbbee595d42f501cb1325d31ab3481d2bc4be0d Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 23 May 2013 09:20:26 -0700 Subject: Input: touchscreen - use platform_{get,set}_drvdata() Use the wrapper functions for getting and setting the driver data using platform_device instead of using dev_{get,set}_drvdata() with &pdev->dev, so we can directly pass a struct platform_device. Signed-off-by: Jingoo Han Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/88pm860x-ts.c | 2 +- drivers/input/touchscreen/atmel_tsadcc.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/input/touchscreen/88pm860x-ts.c b/drivers/input/touchscreen/88pm860x-ts.c index e8d457266b71..f7de14a268bf 100644 --- a/drivers/input/touchscreen/88pm860x-ts.c +++ b/drivers/input/touchscreen/88pm860x-ts.c @@ -237,7 +237,7 @@ static int pm860x_touch_probe(struct platform_device *pdev) touch = kzalloc(sizeof(struct pm860x_touch), GFP_KERNEL); if (touch == NULL) return -ENOMEM; - dev_set_drvdata(&pdev->dev, touch); + platform_set_drvdata(pdev, touch); touch->idev = input_allocate_device(); if (touch->idev == NULL) { diff --git a/drivers/input/touchscreen/atmel_tsadcc.c b/drivers/input/touchscreen/atmel_tsadcc.c index 95f6785a94b0..75a0693a3f91 100644 --- a/drivers/input/touchscreen/atmel_tsadcc.c +++ b/drivers/input/touchscreen/atmel_tsadcc.c @@ -325,7 +325,7 @@ err_free_mem: static int atmel_tsadcc_remove(struct platform_device *pdev) { - struct atmel_tsadcc *ts_dev = dev_get_drvdata(&pdev->dev); + struct atmel_tsadcc *ts_dev = platform_get_drvdata(pdev); struct resource *res; free_irq(ts_dev->irq, ts_dev); -- cgit v1.2.3 From d7ecfff18451750bc3d41ca3e1d5422c5dbce796 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 23 May 2013 09:29:59 -0700 Subject: Input: ep93xx_keypad - pass correct pointer to free_irq() free_irq() expects the same pointer that was passed to request_irq(), otherwise the IRQ is not freed. The issue was found using the following coccinelle script: @r1@ type T; T devid; @@ request_irq(..., devid) @r2@ type r1.T; T devid; position p; @@ free_irq@p(..., devid) @@ position p != r2.p; @@ *free_irq@p(...) Signed-off-by: Lars-Peter Clausen Acked-by: H Hartley Sweeten Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/ep93xx_keypad.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/input/keyboard/ep93xx_keypad.c b/drivers/input/keyboard/ep93xx_keypad.c index a8d5aacfb98b..47206bdba411 100644 --- a/drivers/input/keyboard/ep93xx_keypad.c +++ b/drivers/input/keyboard/ep93xx_keypad.c @@ -329,7 +329,7 @@ static int ep93xx_keypad_probe(struct platform_device *pdev) return 0; failed_free_irq: - free_irq(keypad->irq, pdev); + free_irq(keypad->irq, keypad); failed_free_dev: input_free_device(input_dev); failed_put_clk: @@ -350,7 +350,7 @@ static int ep93xx_keypad_remove(struct platform_device *pdev) struct ep93xx_keypad *keypad = platform_get_drvdata(pdev); struct resource *res; - free_irq(keypad->irq, pdev); + free_irq(keypad->irq, keypad); if (keypad->enabled) clk_disable(keypad->clk); -- cgit v1.2.3 From 8c7f5f5833815af06872bd6151604e0e18e24c99 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 23 May 2013 09:30:04 -0700 Subject: Input: pxa27x_keypad - pass correct pointer to free_irq() free_irq() expects the same pointer that was passed to request_irq(), otherwise the IRQ is not freed. The issue was found using the following coccinelle script: @r1@ type T; T devid; @@ request_irq(..., devid) @r2@ type r1.T; T devid; position p; @@ free_irq@p(..., devid) @@ position p != r2.p; @@ *free_irq@p(...) Signed-off-by: Lars-Peter Clausen Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/pxa27x_keypad.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/input/keyboard/pxa27x_keypad.c b/drivers/input/keyboard/pxa27x_keypad.c index 74e30cccbeab..b674e7aca404 100644 --- a/drivers/input/keyboard/pxa27x_keypad.c +++ b/drivers/input/keyboard/pxa27x_keypad.c @@ -582,7 +582,7 @@ static int pxa27x_keypad_probe(struct platform_device *pdev) return 0; failed_free_irq: - free_irq(irq, pdev); + free_irq(irq, keypad); failed_put_clk: clk_put(keypad->clk); failed_free_io: @@ -600,7 +600,7 @@ static int pxa27x_keypad_remove(struct platform_device *pdev) struct pxa27x_keypad *keypad = platform_get_drvdata(pdev); struct resource *res; - free_irq(keypad->irq, pdev); + free_irq(keypad->irq, keypad); clk_put(keypad->clk); input_unregister_device(keypad->input_dev); -- cgit v1.2.3 From 994c755a197df16aa8fd2a5f2de48500142494a0 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 23 May 2013 09:30:08 -0700 Subject: Input: twl4030_keypad - pass correct pointer to free_irq() free_irq() expects the same pointer that was passed to request_threaded_irq(), otherwise the IRQ is not freed. The issue was found using the following coccinelle script: @r1@ type T; T devid; @@ request_threaded_irq(..., devid) @r2@ type r1.T; T devid; position p; @@ free_irq@p(..., devid) @@ position p != r2.p; @@ *free_irq@p(...) Signed-off-by: Lars-Peter Clausen Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/twl4030_keypad.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/keyboard/twl4030_keypad.c b/drivers/input/keyboard/twl4030_keypad.c index f05ee1db7e64..d2d178c84ea7 100644 --- a/drivers/input/keyboard/twl4030_keypad.c +++ b/drivers/input/keyboard/twl4030_keypad.c @@ -422,7 +422,7 @@ static int twl4030_kp_probe(struct platform_device *pdev) err3: /* mask all events - we don't care about the result */ (void) twl4030_kpwrite_u8(kp, 0xff, KEYP_IMR1); - free_irq(kp->irq, NULL); + free_irq(kp->irq, kp); err2: input_unregister_device(input); input = NULL; -- cgit v1.2.3 From f0886a66295ccd9c81eb0cceb596e5fbc19dc2ac Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 23 May 2013 09:30:12 -0700 Subject: Input: w90p910_keypad - pass correct pointer to free_irq() free_irq() expects the same pointer that was passed to request_irq(), otherwise the IRQ is not freed. The issue was found using the following coccinelle script: @r1@ type T; T devid; @@ request_irq(..., devid) @r2@ type r1.T; T devid; position p; @@ free_irq@p(..., devid) @@ position p != r2.p; @@ *free_irq@p(...) Signed-off-by: Lars-Peter Clausen Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/w90p910_keypad.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/input/keyboard/w90p910_keypad.c b/drivers/input/keyboard/w90p910_keypad.c index 72e6a56a2eba..7b039162a3f8 100644 --- a/drivers/input/keyboard/w90p910_keypad.c +++ b/drivers/input/keyboard/w90p910_keypad.c @@ -221,7 +221,7 @@ static int w90p910_keypad_probe(struct platform_device *pdev) return 0; failed_free_irq: - free_irq(irq, pdev); + free_irq(irq, keypad); failed_put_clk: clk_put(keypad->clk); failed_free_io: @@ -239,7 +239,7 @@ static int w90p910_keypad_remove(struct platform_device *pdev) struct w90p910_keypad *keypad = platform_get_drvdata(pdev); struct resource *res; - free_irq(keypad->irq, pdev); + free_irq(keypad->irq, keypad); clk_put(keypad->clk); -- cgit v1.2.3 From 25c9b7bd332584724fb2bb9031377903159537d3 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 23 May 2013 09:30:15 -0700 Subject: Input: ixp4xx-beeper - pass correct pointer to free_irq() free_irq() expects the same pointer that was passed to request_irq(), otherwise the IRQ is not freed. The issue was found using the following coccinelle script: @r1@ type T; T devid; @@ request_irq(..., devid) @r2@ type r1.T; T devid; position p; @@ free_irq@p(..., devid) @@ position p != r2.p; @@ *free_irq@p(...) Signed-off-by: Lars-Peter Clausen Signed-off-by: Dmitry Torokhov --- drivers/input/misc/ixp4xx-beeper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/input/misc/ixp4xx-beeper.c b/drivers/input/misc/ixp4xx-beeper.c index 6ab3decc86e6..deab4a11350a 100644 --- a/drivers/input/misc/ixp4xx-beeper.c +++ b/drivers/input/misc/ixp4xx-beeper.c @@ -125,7 +125,7 @@ static int ixp4xx_spkr_probe(struct platform_device *dev) return 0; err_free_irq: - free_irq(IRQ_IXP4XX_TIMER2, dev); + free_irq(IRQ_IXP4XX_TIMER2, (void *)dev->id); err_free_device: input_free_device(input_dev); @@ -144,7 +144,7 @@ static int ixp4xx_spkr_remove(struct platform_device *dev) disable_irq(IRQ_IXP4XX_TIMER2); ixp4xx_spkr_control(pin, 0); - free_irq(IRQ_IXP4XX_TIMER2, dev); + free_irq(IRQ_IXP4XX_TIMER2, (void *)dev->id); return 0; } -- cgit v1.2.3 From 139097a0a7cf0f63c1806cdca197f22b6f48501c Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 23 May 2013 09:30:19 -0700 Subject: Input: pmic8xxx-pwrkey - pass correct pointer to free_irq() free_irq() expects the same pointer that was passed to request_irq(), otherwise the IRQ is not freed. The issue was found using the following coccinelle script: @r1@ type T; T devid; @@ request_irq(..., devid) @r2@ type r1.T; T devid; position p; @@ free_irq@p(..., devid) @@ position p != r2.p; @@ *free_irq@p(...) Signed-off-by: Lars-Peter Clausen Signed-off-by: Dmitry Torokhov --- drivers/input/misc/pmic8xxx-pwrkey.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/misc/pmic8xxx-pwrkey.c b/drivers/input/misc/pmic8xxx-pwrkey.c index afd0af9c4850..b49b738aa9c6 100644 --- a/drivers/input/misc/pmic8xxx-pwrkey.c +++ b/drivers/input/misc/pmic8xxx-pwrkey.c @@ -175,7 +175,7 @@ static int pmic8xxx_pwrkey_probe(struct platform_device *pdev) return 0; free_press_irq: - free_irq(key_press_irq, NULL); + free_irq(key_press_irq, pwrkey); unreg_input_dev: input_unregister_device(pwr); pwr = NULL; -- cgit v1.2.3 From 737f644c279b65b46ca9fe9ad149c713ca8b2663 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 23 May 2013 09:30:23 -0700 Subject: Input: navpoint - pass correct pointer to free_irq() free_irq() expects the same pointer that was passed to request_irq(), otherwise the IRQ is not freed. The issue was found using the following coccinelle script: @r1@ type T; T devid; @@ request_irq(..., devid) @r2@ type r1.T; T devid; position p; @@ free_irq@p(..., devid) @@ position p != r2.p; @@ *free_irq@p(...) Signed-off-by: Lars-Peter Clausen Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/navpoint.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/mouse/navpoint.c b/drivers/input/mouse/navpoint.c index 8e1b98ea5648..0b8d33591dee 100644 --- a/drivers/input/mouse/navpoint.c +++ b/drivers/input/mouse/navpoint.c @@ -287,7 +287,7 @@ static int navpoint_probe(struct platform_device *pdev) return 0; err_free_irq: - free_irq(ssp->irq, &pdev->dev); + free_irq(ssp->irq, navpoint); err_free_mem: input_free_device(input); kfree(navpoint); -- cgit v1.2.3 From eebfc9394ee31b3ef162692c0cd483c1318a4395 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 23 May 2013 22:17:50 +0200 Subject: iwlwifi: mvm: remove P2P_DEVICE support Unfortunately, advertising P2P_DEVICE support was a little premature, a number of issues came up in testing and have been fixed for 3.10. Rather than try to backport all the different fixes, disable P2P_DEVICE support in the drivers using it. For iwlmvm that implies disabling P2P completely as it can't support P2P operation w/o P2P Device. Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index dd158ec571fb..11dc7df8bdf9 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -84,15 +84,6 @@ static const struct ieee80211_iface_limit iwl_mvm_limits[] = { .types = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP), }, - { - .max = 1, - .types = BIT(NL80211_IFTYPE_P2P_CLIENT) | - BIT(NL80211_IFTYPE_P2P_GO), - }, - { - .max = 1, - .types = BIT(NL80211_IFTYPE_P2P_DEVICE), - }, }; static const struct ieee80211_iface_combination iwl_mvm_iface_combinations[] = { @@ -161,10 +152,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) hw->chanctx_data_size = sizeof(struct iwl_mvm_phy_ctxt); hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_P2P_CLIENT) | - BIT(NL80211_IFTYPE_AP) | - BIT(NL80211_IFTYPE_P2P_GO) | - BIT(NL80211_IFTYPE_P2P_DEVICE); + BIT(NL80211_IFTYPE_AP); hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY | WIPHY_FLAG_DISABLE_BEACON_HINTS | -- cgit v1.2.3 From e3ee68b7b049c5bbfcb78a179c00c373a38ed58c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 23 May 2013 22:17:50 +0200 Subject: mac80211_hwsim: remove P2P_DEVICE support Unfortunately, advertising P2P_DEVICE support was a little premature, a number of issues came up in testing and have been fixed for 3.10. Rather than try to backport all the different fixes, disable P2P_DEVICE support in the drivers using it. Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index cb34c7895f2a..69bbf6fdd2d3 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -2169,7 +2169,6 @@ static const struct ieee80211_iface_limit hwsim_if_limits[] = { #endif BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_P2P_GO) }, - { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_DEVICE) }, }; static struct ieee80211_iface_combination hwsim_if_comb = { @@ -2295,8 +2294,7 @@ static int __init init_mac80211_hwsim(void) BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_ADHOC) | - BIT(NL80211_IFTYPE_MESH_POINT) | - BIT(NL80211_IFTYPE_P2P_DEVICE); + BIT(NL80211_IFTYPE_MESH_POINT); hw->flags = IEEE80211_HW_MFP_CAPABLE | IEEE80211_HW_SIGNAL_DBM | -- cgit v1.2.3 From 5f38a11274f0e74ec0e499bc779d355510b39790 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 23 May 2013 23:09:56 +0200 Subject: mac80211: assign AP_VLAN hw queues correctly A lot of code in mac80211 assumes that the hw queues are set up correctly for all interfaces (except for monitor) but this isn't true for AP_VLAN interfaces. Fix this by copying the AP master configuration when an AP VLAN is brought up, after this the AP interface can't change its configuration any more and needs to be brought down to change it, which also forces AP_VLAN interfaces down, so just copying in open() is sufficient. Reported-by: Jouni Malinen Signed-off-by: Johannes Berg --- net/mac80211/iface.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 68f51c3af49f..00e2238355f0 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -474,6 +474,9 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) master->control_port_protocol; sdata->control_port_no_encrypt = master->control_port_no_encrypt; + sdata->vif.cab_queue = master->vif.cab_queue; + memcpy(sdata->vif.hw_queue, master->vif.hw_queue, + sizeof(sdata->vif.hw_queue)); break; } case NL80211_IFTYPE_AP: -- cgit v1.2.3 From 59de08136f0c8d91bfd607d03cf722c5b6c60d1b Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 22 May 2013 15:36:16 +0300 Subject: drm/i915: group sideband register accessors to a new file Group both the HSW/LPT SBI interface and VLV IOSF sideband register accessor functions into a new file. No functional changes. v2: also move intel_sbi_{read,write} (Daniel) Signed-off-by: Jani Nikula Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/Makefile | 1 + drivers/gpu/drm/i915/i915_drv.h | 8 ++ drivers/gpu/drm/i915/intel_display.c | 98 ------------------ drivers/gpu/drm/i915/intel_drv.h | 4 - drivers/gpu/drm/i915/intel_pm.c | 60 ----------- drivers/gpu/drm/i915/intel_sideband.c | 183 ++++++++++++++++++++++++++++++++++ 6 files changed, 192 insertions(+), 162 deletions(-) create mode 100644 drivers/gpu/drm/i915/intel_sideband.c diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index 91f3ac6cef35..40034ecefd3b 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -36,6 +36,7 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o \ intel_overlay.o \ intel_sprite.o \ intel_opregion.o \ + intel_sideband.o \ dvo_ch7xxx.o \ dvo_ch7017.o \ dvo_ivch.o \ diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 7772bb6f73ba..5a0dfca88668 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1929,9 +1929,17 @@ int __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv); int sandybridge_pcode_read(struct drm_i915_private *dev_priv, u8 mbox, u32 *val); int sandybridge_pcode_write(struct drm_i915_private *dev_priv, u8 mbox, u32 val); + +/* intel_sideband.c */ int valleyview_punit_read(struct drm_i915_private *dev_priv, u8 addr, u32 *val); int valleyview_punit_write(struct drm_i915_private *dev_priv, u8 addr, u32 val); int valleyview_nc_read(struct drm_i915_private *dev_priv, u8 addr, u32 *val); +u32 intel_dpio_read(struct drm_i915_private *dev_priv, int reg); +void intel_dpio_write(struct drm_i915_private *dev_priv, int reg, u32 val); +u32 intel_sbi_read(struct drm_i915_private *dev_priv, u16 reg, + enum intel_sbi_destination destination); +void intel_sbi_write(struct drm_i915_private *dev_priv, u16 reg, u32 value, + enum intel_sbi_destination destination); int vlv_gpu_freq(int ddr_freq, int val); int vlv_freq_opcode(int ddr_freq, int val); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 1c0d6d3bfe83..e07ee4c73592 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -381,43 +381,6 @@ static const intel_limit_t intel_limits_vlv_dp = { .find_pll = intel_vlv_find_best_pll, }; -u32 intel_dpio_read(struct drm_i915_private *dev_priv, int reg) -{ - WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); - - if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) { - DRM_ERROR("DPIO idle wait timed out\n"); - return 0; - } - - I915_WRITE(DPIO_REG, reg); - I915_WRITE(DPIO_PKT, DPIO_RID | DPIO_OP_READ | DPIO_PORTID | - DPIO_BYTE); - if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) { - DRM_ERROR("DPIO read wait timed out\n"); - return 0; - } - - return I915_READ(DPIO_DATA); -} - -void intel_dpio_write(struct drm_i915_private *dev_priv, int reg, u32 val) -{ - WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); - - if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) { - DRM_ERROR("DPIO idle wait timed out\n"); - return; - } - - I915_WRITE(DPIO_DATA, val); - I915_WRITE(DPIO_REG, reg); - I915_WRITE(DPIO_PKT, DPIO_RID | DPIO_OP_WRITE | DPIO_PORTID | - DPIO_BYTE); - if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) - DRM_ERROR("DPIO write wait timed out\n"); -} - static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc, int refclk) { @@ -1404,67 +1367,6 @@ static void intel_disable_pll(struct drm_i915_private *dev_priv, enum pipe pipe) POSTING_READ(reg); } -/* SBI access */ -static void -intel_sbi_write(struct drm_i915_private *dev_priv, u16 reg, u32 value, - enum intel_sbi_destination destination) -{ - u32 tmp; - - WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); - - if (wait_for((I915_READ(SBI_CTL_STAT) & SBI_BUSY) == 0, - 100)) { - DRM_ERROR("timeout waiting for SBI to become ready\n"); - return; - } - - I915_WRITE(SBI_ADDR, (reg << 16)); - I915_WRITE(SBI_DATA, value); - - if (destination == SBI_ICLK) - tmp = SBI_CTL_DEST_ICLK | SBI_CTL_OP_CRWR; - else - tmp = SBI_CTL_DEST_MPHY | SBI_CTL_OP_IOWR; - I915_WRITE(SBI_CTL_STAT, SBI_BUSY | tmp); - - if (wait_for((I915_READ(SBI_CTL_STAT) & (SBI_BUSY | SBI_RESPONSE_FAIL)) == 0, - 100)) { - DRM_ERROR("timeout waiting for SBI to complete write transaction\n"); - return; - } -} - -static u32 -intel_sbi_read(struct drm_i915_private *dev_priv, u16 reg, - enum intel_sbi_destination destination) -{ - u32 value = 0; - WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); - - if (wait_for((I915_READ(SBI_CTL_STAT) & SBI_BUSY) == 0, - 100)) { - DRM_ERROR("timeout waiting for SBI to become ready\n"); - return 0; - } - - I915_WRITE(SBI_ADDR, (reg << 16)); - - if (destination == SBI_ICLK) - value = SBI_CTL_DEST_ICLK | SBI_CTL_OP_CRRD; - else - value = SBI_CTL_DEST_MPHY | SBI_CTL_OP_IORD; - I915_WRITE(SBI_CTL_STAT, value | SBI_BUSY); - - if (wait_for((I915_READ(SBI_CTL_STAT) & (SBI_BUSY | SBI_RESPONSE_FAIL)) == 0, - 100)) { - DRM_ERROR("timeout waiting for SBI to complete read transaction\n"); - return 0; - } - - return I915_READ(SBI_DATA); -} - void vlv_wait_port_ready(struct drm_i915_private *dev_priv, int port) { u32 port_mask; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 75a7f22262d4..83b4fe4b7be0 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -743,10 +743,6 @@ extern int intel_sprite_set_colorkey(struct drm_device *dev, void *data, extern int intel_sprite_get_colorkey(struct drm_device *dev, void *data, struct drm_file *file_priv); -extern u32 intel_dpio_read(struct drm_i915_private *dev_priv, int reg); -extern void intel_dpio_write(struct drm_i915_private *dev_priv, int reg, - u32 val); - /* Power-related functions, located in intel_pm.c */ extern void intel_init_pm(struct drm_device *dev); /* FBC */ diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index e198f3881f9a..cd5bd88980bb 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -4956,66 +4956,6 @@ int sandybridge_pcode_write(struct drm_i915_private *dev_priv, u8 mbox, u32 val) return 0; } -static int vlv_punit_rw(struct drm_i915_private *dev_priv, u32 port, u8 opcode, - u8 addr, u32 *val) -{ - u32 cmd, devfn, be, bar; - - bar = 0; - be = 0xf; - devfn = PCI_DEVFN(2, 0); - - cmd = (devfn << IOSF_DEVFN_SHIFT) | (opcode << IOSF_OPCODE_SHIFT) | - (port << IOSF_PORT_SHIFT) | (be << IOSF_BYTE_ENABLES_SHIFT) | - (bar << IOSF_BAR_SHIFT); - - WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock)); - - if (I915_READ(VLV_IOSF_DOORBELL_REQ) & IOSF_SB_BUSY) { - DRM_DEBUG_DRIVER("warning: pcode (%s) mailbox access failed\n", - opcode == PUNIT_OPCODE_REG_READ ? - "read" : "write"); - return -EAGAIN; - } - - I915_WRITE(VLV_IOSF_ADDR, addr); - if (opcode == PUNIT_OPCODE_REG_WRITE) - I915_WRITE(VLV_IOSF_DATA, *val); - I915_WRITE(VLV_IOSF_DOORBELL_REQ, cmd); - - if (wait_for((I915_READ(VLV_IOSF_DOORBELL_REQ) & IOSF_SB_BUSY) == 0, - 5)) { - DRM_ERROR("timeout waiting for pcode %s (%d) to finish\n", - opcode == PUNIT_OPCODE_REG_READ ? "read" : "write", - addr); - return -ETIMEDOUT; - } - - if (opcode == PUNIT_OPCODE_REG_READ) - *val = I915_READ(VLV_IOSF_DATA); - I915_WRITE(VLV_IOSF_DATA, 0); - - return 0; -} - -int valleyview_punit_read(struct drm_i915_private *dev_priv, u8 addr, u32 *val) -{ - return vlv_punit_rw(dev_priv, IOSF_PORT_PUNIT, PUNIT_OPCODE_REG_READ, - addr, val); -} - -int valleyview_punit_write(struct drm_i915_private *dev_priv, u8 addr, u32 val) -{ - return vlv_punit_rw(dev_priv, IOSF_PORT_PUNIT, PUNIT_OPCODE_REG_WRITE, - addr, &val); -} - -int valleyview_nc_read(struct drm_i915_private *dev_priv, u8 addr, u32 *val) -{ - return vlv_punit_rw(dev_priv, IOSF_PORT_NC, PUNIT_OPCODE_REG_READ, - addr, val); -} - int vlv_gpu_freq(int ddr_freq, int val) { int mult, base; diff --git a/drivers/gpu/drm/i915/intel_sideband.c b/drivers/gpu/drm/i915/intel_sideband.c new file mode 100644 index 000000000000..81af8857857d --- /dev/null +++ b/drivers/gpu/drm/i915/intel_sideband.c @@ -0,0 +1,183 @@ +/* + * Copyright © 2013 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#include "i915_drv.h" +#include "intel_drv.h" + +/* IOSF sideband */ +static int vlv_punit_rw(struct drm_i915_private *dev_priv, u32 port, u8 opcode, + u8 addr, u32 *val) +{ + u32 cmd, devfn, be, bar; + + bar = 0; + be = 0xf; + devfn = PCI_DEVFN(2, 0); + + cmd = (devfn << IOSF_DEVFN_SHIFT) | (opcode << IOSF_OPCODE_SHIFT) | + (port << IOSF_PORT_SHIFT) | (be << IOSF_BYTE_ENABLES_SHIFT) | + (bar << IOSF_BAR_SHIFT); + + WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock)); + + if (I915_READ(VLV_IOSF_DOORBELL_REQ) & IOSF_SB_BUSY) { + DRM_DEBUG_DRIVER("warning: pcode (%s) mailbox access failed\n", + opcode == PUNIT_OPCODE_REG_READ ? + "read" : "write"); + return -EAGAIN; + } + + I915_WRITE(VLV_IOSF_ADDR, addr); + if (opcode == PUNIT_OPCODE_REG_WRITE) + I915_WRITE(VLV_IOSF_DATA, *val); + I915_WRITE(VLV_IOSF_DOORBELL_REQ, cmd); + + if (wait_for((I915_READ(VLV_IOSF_DOORBELL_REQ) & IOSF_SB_BUSY) == 0, + 5)) { + DRM_ERROR("timeout waiting for pcode %s (%d) to finish\n", + opcode == PUNIT_OPCODE_REG_READ ? "read" : "write", + addr); + return -ETIMEDOUT; + } + + if (opcode == PUNIT_OPCODE_REG_READ) + *val = I915_READ(VLV_IOSF_DATA); + I915_WRITE(VLV_IOSF_DATA, 0); + + return 0; +} + +int valleyview_punit_read(struct drm_i915_private *dev_priv, u8 addr, u32 *val) +{ + return vlv_punit_rw(dev_priv, IOSF_PORT_PUNIT, PUNIT_OPCODE_REG_READ, + addr, val); +} + +int valleyview_punit_write(struct drm_i915_private *dev_priv, u8 addr, u32 val) +{ + return vlv_punit_rw(dev_priv, IOSF_PORT_PUNIT, PUNIT_OPCODE_REG_WRITE, + addr, &val); +} + +int valleyview_nc_read(struct drm_i915_private *dev_priv, u8 addr, u32 *val) +{ + return vlv_punit_rw(dev_priv, IOSF_PORT_NC, PUNIT_OPCODE_REG_READ, + addr, val); +} + +u32 intel_dpio_read(struct drm_i915_private *dev_priv, int reg) +{ + WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); + + if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) { + DRM_ERROR("DPIO idle wait timed out\n"); + return 0; + } + + I915_WRITE(DPIO_REG, reg); + I915_WRITE(DPIO_PKT, DPIO_RID | DPIO_OP_READ | DPIO_PORTID | + DPIO_BYTE); + if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) { + DRM_ERROR("DPIO read wait timed out\n"); + return 0; + } + + return I915_READ(DPIO_DATA); +} + +void intel_dpio_write(struct drm_i915_private *dev_priv, int reg, u32 val) +{ + WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); + + if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) { + DRM_ERROR("DPIO idle wait timed out\n"); + return; + } + + I915_WRITE(DPIO_DATA, val); + I915_WRITE(DPIO_REG, reg); + I915_WRITE(DPIO_PKT, DPIO_RID | DPIO_OP_WRITE | DPIO_PORTID | + DPIO_BYTE); + if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) + DRM_ERROR("DPIO write wait timed out\n"); +} + +/* SBI access */ +u32 intel_sbi_read(struct drm_i915_private *dev_priv, u16 reg, + enum intel_sbi_destination destination) +{ + u32 value = 0; + WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); + + if (wait_for((I915_READ(SBI_CTL_STAT) & SBI_BUSY) == 0, + 100)) { + DRM_ERROR("timeout waiting for SBI to become ready\n"); + return 0; + } + + I915_WRITE(SBI_ADDR, (reg << 16)); + + if (destination == SBI_ICLK) + value = SBI_CTL_DEST_ICLK | SBI_CTL_OP_CRRD; + else + value = SBI_CTL_DEST_MPHY | SBI_CTL_OP_IORD; + I915_WRITE(SBI_CTL_STAT, value | SBI_BUSY); + + if (wait_for((I915_READ(SBI_CTL_STAT) & (SBI_BUSY | SBI_RESPONSE_FAIL)) == 0, + 100)) { + DRM_ERROR("timeout waiting for SBI to complete read transaction\n"); + return 0; + } + + return I915_READ(SBI_DATA); +} + +void intel_sbi_write(struct drm_i915_private *dev_priv, u16 reg, u32 value, + enum intel_sbi_destination destination) +{ + u32 tmp; + + WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); + + if (wait_for((I915_READ(SBI_CTL_STAT) & SBI_BUSY) == 0, + 100)) { + DRM_ERROR("timeout waiting for SBI to become ready\n"); + return; + } + + I915_WRITE(SBI_ADDR, (reg << 16)); + I915_WRITE(SBI_DATA, value); + + if (destination == SBI_ICLK) + tmp = SBI_CTL_DEST_ICLK | SBI_CTL_OP_CRWR; + else + tmp = SBI_CTL_DEST_MPHY | SBI_CTL_OP_IOWR; + I915_WRITE(SBI_CTL_STAT, SBI_BUSY | tmp); + + if (wait_for((I915_READ(SBI_CTL_STAT) & (SBI_BUSY | SBI_RESPONSE_FAIL)) == 0, + 100)) { + DRM_ERROR("timeout waiting for SBI to complete write transaction\n"); + return; + } +} -- cgit v1.2.3 From 5a09ae9fd509d7dded34e0d599e1afa5142c6987 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 22 May 2013 15:36:17 +0300 Subject: drm/i915: refactor VLV IOSF sideband accessors to use one helper Both the intel_dpio_{read,write} and valleyview_{punit,nc}_{read,write} use the IOSF sideband interface. They access the same registers and do mostly the same stuff, but no shared code. There are even duplicate register defines for the same registers. Both have locking, but the former use dpio_lock and the latter rps.hw_lock. It's racy. This patch refactors the sideband access to a single function that expects dpio_lock to be held. The dpio_lock is only used for sideband stuff, so it's a better match than rps.hw_lock for the purpose. The rps stuff still needs rps.hw_lock, since it's used to protect more than just the register access, so rps code will need to hold both locks. Based on the work by Shobhit Kumar and Yogesh Mohan Marimuthu . Signed-off-by: Jani Nikula Acked-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 93 ++++++++++++++----------------- drivers/gpu/drm/i915/intel_sideband.c | 102 +++++++++++++++++----------------- 2 files changed, 93 insertions(+), 102 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 55caedba53d1..dbd9de5abde0 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -342,27 +342,54 @@ #define DEBUG_RESET_DISPLAY (1<<9) /* - * DPIO - a special bus for various display related registers to hide behind: - * 0x800c: m1, m2, n, p1, p2, k dividers - * 0x8014: REF and SFR select - * 0x8014: N divider, VCO select - * 0x801c/3c: core clock bits - * 0x8048/68: low pass filter coefficients - * 0x8100: fast clock controls + * IOSF sideband + */ +#define VLV_IOSF_DOORBELL_REQ (VLV_DISPLAY_BASE + 0x2100) +#define IOSF_DEVFN_SHIFT 24 +#define IOSF_OPCODE_SHIFT 16 +#define IOSF_PORT_SHIFT 8 +#define IOSF_BYTE_ENABLES_SHIFT 4 +#define IOSF_BAR_SHIFT 1 +#define IOSF_SB_BUSY (1<<0) +#define IOSF_PORT_PUNIT 0x4 +#define IOSF_PORT_NC 0x11 +#define IOSF_PORT_DPIO 0x12 +#define VLV_IOSF_DATA (VLV_DISPLAY_BASE + 0x2104) +#define VLV_IOSF_ADDR (VLV_DISPLAY_BASE + 0x2108) + +#define PUNIT_OPCODE_REG_READ 6 +#define PUNIT_OPCODE_REG_WRITE 7 + +#define PUNIT_REG_GPU_LFM 0xd3 +#define PUNIT_REG_GPU_FREQ_REQ 0xd4 +#define PUNIT_REG_GPU_FREQ_STS 0xd8 +#define PUNIT_REG_MEDIA_TURBO_FREQ_REQ 0xdc + +#define PUNIT_FUSE_BUS2 0xf6 /* bits 47:40 */ +#define PUNIT_FUSE_BUS1 0xf5 /* bits 55:48 */ + +#define IOSF_NC_FB_GFX_FREQ_FUSE 0x1c +#define FB_GFX_MAX_FREQ_FUSE_SHIFT 3 +#define FB_GFX_MAX_FREQ_FUSE_MASK 0x000007f8 +#define FB_GFX_FGUARANTEED_FREQ_FUSE_SHIFT 11 +#define FB_GFX_FGUARANTEED_FREQ_FUSE_MASK 0x0007f800 +#define IOSF_NC_FB_GFX_FMAX_FUSE_HI 0x34 +#define FB_FMAX_VMIN_FREQ_HI_MASK 0x00000007 +#define IOSF_NC_FB_GFX_FMAX_FUSE_LO 0x30 +#define FB_FMAX_VMIN_FREQ_LO_SHIFT 27 +#define FB_FMAX_VMIN_FREQ_LO_MASK 0xf8000000 + +/* + * DPIO - a special bus for various display related registers to hide behind * * DPIO is VLV only. * * Note: digital port B is DDI0, digital pot C is DDI1 */ -#define DPIO_PKT (VLV_DISPLAY_BASE + 0x2100) -#define DPIO_RID (0<<24) -#define DPIO_OP_WRITE (1<<16) -#define DPIO_OP_READ (0<<16) -#define DPIO_PORTID (0x12<<8) -#define DPIO_BYTE (0xf<<4) -#define DPIO_BUSY (1<<0) /* status only */ -#define DPIO_DATA (VLV_DISPLAY_BASE + 0x2104) -#define DPIO_REG (VLV_DISPLAY_BASE + 0x2108) +#define DPIO_DEVFN 0 +#define DPIO_OPCODE_REG_WRITE 1 +#define DPIO_OPCODE_REG_READ 0 + #define DPIO_CTL (VLV_DISPLAY_BASE + 0x2110) #define DPIO_MODSEL1 (1<<3) /* if ref clk b == 27 */ #define DPIO_MODSEL0 (1<<2) /* if ref clk a == 27 */ @@ -4541,40 +4568,6 @@ #define GEN6_PCODE_FREQ_IA_RATIO_SHIFT 8 #define GEN6_PCODE_FREQ_RING_RATIO_SHIFT 16 -#define VLV_IOSF_DOORBELL_REQ 0x182100 -#define IOSF_DEVFN_SHIFT 24 -#define IOSF_OPCODE_SHIFT 16 -#define IOSF_PORT_SHIFT 8 -#define IOSF_BYTE_ENABLES_SHIFT 4 -#define IOSF_BAR_SHIFT 1 -#define IOSF_SB_BUSY (1<<0) -#define IOSF_PORT_PUNIT 0x4 -#define IOSF_PORT_NC 0x11 -#define VLV_IOSF_DATA 0x182104 -#define VLV_IOSF_ADDR 0x182108 - -#define PUNIT_OPCODE_REG_READ 6 -#define PUNIT_OPCODE_REG_WRITE 7 - -#define PUNIT_REG_GPU_LFM 0xd3 -#define PUNIT_REG_GPU_FREQ_REQ 0xd4 -#define PUNIT_REG_GPU_FREQ_STS 0xd8 -#define PUNIT_REG_MEDIA_TURBO_FREQ_REQ 0xdc - -#define PUNIT_FUSE_BUS2 0xf6 /* bits 47:40 */ -#define PUNIT_FUSE_BUS1 0xf5 /* bits 55:48 */ - -#define IOSF_NC_FB_GFX_FREQ_FUSE 0x1c -#define FB_GFX_MAX_FREQ_FUSE_SHIFT 3 -#define FB_GFX_MAX_FREQ_FUSE_MASK 0x000007f8 -#define FB_GFX_FGUARANTEED_FREQ_FUSE_SHIFT 11 -#define FB_GFX_FGUARANTEED_FREQ_FUSE_MASK 0x0007f800 -#define IOSF_NC_FB_GFX_FMAX_FUSE_HI 0x34 -#define FB_FMAX_VMIN_FREQ_HI_MASK 0x00000007 -#define IOSF_NC_FB_GFX_FMAX_FUSE_LO 0x30 -#define FB_FMAX_VMIN_FREQ_LO_SHIFT 27 -#define FB_FMAX_VMIN_FREQ_LO_MASK 0xf8000000 - #define GEN6_GT_CORE_STATUS 0x138060 #define GEN6_CORE_CPD_STATE_MASK (7<<4) #define GEN6_RCn_MASK 7 diff --git a/drivers/gpu/drm/i915/intel_sideband.c b/drivers/gpu/drm/i915/intel_sideband.c index 81af8857857d..a7c4b61e9c30 100644 --- a/drivers/gpu/drm/i915/intel_sideband.c +++ b/drivers/gpu/drm/i915/intel_sideband.c @@ -26,42 +26,37 @@ #include "intel_drv.h" /* IOSF sideband */ -static int vlv_punit_rw(struct drm_i915_private *dev_priv, u32 port, u8 opcode, - u8 addr, u32 *val) +static int vlv_sideband_rw(struct drm_i915_private *dev_priv, u32 devfn, + u32 port, u32 opcode, u32 addr, u32 *val) { - u32 cmd, devfn, be, bar; - - bar = 0; - be = 0xf; - devfn = PCI_DEVFN(2, 0); + u32 cmd, be = 0xf, bar = 0; + bool is_read = (opcode == PUNIT_OPCODE_REG_READ || + opcode == DPIO_OPCODE_REG_READ); cmd = (devfn << IOSF_DEVFN_SHIFT) | (opcode << IOSF_OPCODE_SHIFT) | (port << IOSF_PORT_SHIFT) | (be << IOSF_BYTE_ENABLES_SHIFT) | (bar << IOSF_BAR_SHIFT); - WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock)); + WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); - if (I915_READ(VLV_IOSF_DOORBELL_REQ) & IOSF_SB_BUSY) { - DRM_DEBUG_DRIVER("warning: pcode (%s) mailbox access failed\n", - opcode == PUNIT_OPCODE_REG_READ ? - "read" : "write"); + if (wait_for((I915_READ(VLV_IOSF_DOORBELL_REQ) & IOSF_SB_BUSY) == 0, 5)) { + DRM_DEBUG_DRIVER("IOSF sideband idle wait (%s) timed out\n", + is_read ? "read" : "write"); return -EAGAIN; } I915_WRITE(VLV_IOSF_ADDR, addr); - if (opcode == PUNIT_OPCODE_REG_WRITE) + if (!is_read) I915_WRITE(VLV_IOSF_DATA, *val); I915_WRITE(VLV_IOSF_DOORBELL_REQ, cmd); - if (wait_for((I915_READ(VLV_IOSF_DOORBELL_REQ) & IOSF_SB_BUSY) == 0, - 5)) { - DRM_ERROR("timeout waiting for pcode %s (%d) to finish\n", - opcode == PUNIT_OPCODE_REG_READ ? "read" : "write", - addr); + if (wait_for((I915_READ(VLV_IOSF_DOORBELL_REQ) & IOSF_SB_BUSY) == 0, 5)) { + DRM_DEBUG_DRIVER("IOSF sideband finish wait (%s) timed out\n", + is_read ? "read" : "write"); return -ETIMEDOUT; } - if (opcode == PUNIT_OPCODE_REG_READ) + if (is_read) *val = I915_READ(VLV_IOSF_DATA); I915_WRITE(VLV_IOSF_DATA, 0); @@ -70,57 +65,60 @@ static int vlv_punit_rw(struct drm_i915_private *dev_priv, u32 port, u8 opcode, int valleyview_punit_read(struct drm_i915_private *dev_priv, u8 addr, u32 *val) { - return vlv_punit_rw(dev_priv, IOSF_PORT_PUNIT, PUNIT_OPCODE_REG_READ, - addr, val); + int ret; + + WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock)); + + mutex_lock(&dev_priv->dpio_lock); + ret = vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_PUNIT, + PUNIT_OPCODE_REG_READ, addr, val); + mutex_unlock(&dev_priv->dpio_lock); + + return ret; } int valleyview_punit_write(struct drm_i915_private *dev_priv, u8 addr, u32 val) { - return vlv_punit_rw(dev_priv, IOSF_PORT_PUNIT, PUNIT_OPCODE_REG_WRITE, - addr, &val); + int ret; + + WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock)); + + mutex_lock(&dev_priv->dpio_lock); + ret = vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_PUNIT, + PUNIT_OPCODE_REG_WRITE, addr, &val); + mutex_unlock(&dev_priv->dpio_lock); + + return ret; } int valleyview_nc_read(struct drm_i915_private *dev_priv, u8 addr, u32 *val) { - return vlv_punit_rw(dev_priv, IOSF_PORT_NC, PUNIT_OPCODE_REG_READ, - addr, val); + int ret; + + WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock)); + + mutex_lock(&dev_priv->dpio_lock); + ret = vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_NC, + PUNIT_OPCODE_REG_READ, addr, val); + mutex_unlock(&dev_priv->dpio_lock); + + return ret; } u32 intel_dpio_read(struct drm_i915_private *dev_priv, int reg) { - WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); + u32 val = 0; - if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) { - DRM_ERROR("DPIO idle wait timed out\n"); - return 0; - } + vlv_sideband_rw(dev_priv, DPIO_DEVFN, IOSF_PORT_DPIO, + DPIO_OPCODE_REG_READ, reg, &val); - I915_WRITE(DPIO_REG, reg); - I915_WRITE(DPIO_PKT, DPIO_RID | DPIO_OP_READ | DPIO_PORTID | - DPIO_BYTE); - if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) { - DRM_ERROR("DPIO read wait timed out\n"); - return 0; - } - - return I915_READ(DPIO_DATA); + return val; } void intel_dpio_write(struct drm_i915_private *dev_priv, int reg, u32 val) { - WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); - - if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) { - DRM_ERROR("DPIO idle wait timed out\n"); - return; - } - - I915_WRITE(DPIO_DATA, val); - I915_WRITE(DPIO_REG, reg); - I915_WRITE(DPIO_PKT, DPIO_RID | DPIO_OP_WRITE | DPIO_PORTID | - DPIO_BYTE); - if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) - DRM_ERROR("DPIO write wait timed out\n"); + vlv_sideband_rw(dev_priv, DPIO_DEVFN, IOSF_PORT_DPIO, + DPIO_OPCODE_REG_WRITE, reg, &val); } /* SBI access */ -- cgit v1.2.3 From a1ca802d98acbc5fd87cc399b6aaf38f54be33e1 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 22 May 2013 15:36:18 +0300 Subject: drm/i915: drop redundant warnings on not holding dpio_lock The lower level sideband read/write functions already do this. Signed-off-by: Jani Nikula Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 6 ------ drivers/gpu/drm/i915/intel_hdmi.c | 4 ---- 2 files changed, 10 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 9332558fc3dc..098e7c25f4ef 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1442,8 +1442,6 @@ static void intel_pre_enable_dp(struct intel_encoder *encoder) int pipe = intel_crtc->pipe; u32 val; - WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); - val = intel_dpio_read(dev_priv, DPIO_DATA_LANE_A(port)); val = 0; if (pipe) @@ -1470,8 +1468,6 @@ static void intel_dp_pre_pll_enable(struct intel_encoder *encoder) if (!IS_VALLEYVIEW(dev)) return; - WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); - /* Program Tx lane resets to default */ intel_dpio_write(dev_priv, DPIO_PCS_TX(port), DPIO_PCS_TX_LANE2_RESET | @@ -1622,8 +1618,6 @@ static uint32_t intel_vlv_signal_levels(struct intel_dp *intel_dp) uint8_t train_set = intel_dp->train_set[0]; int port = vlv_dport_to_channel(dport); - WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); - switch (train_set & DP_TRAIN_PRE_EMPHASIS_MASK) { case DP_TRAIN_PRE_EMPHASIS_0: preemph_reg_value = 0x0004000; diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 18f8ce0404c6..83b63d72af83 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -1018,8 +1018,6 @@ static void intel_hdmi_pre_enable(struct intel_encoder *encoder) if (!IS_VALLEYVIEW(dev)) return; - WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); - /* Enable clock channels for this port */ val = intel_dpio_read(dev_priv, DPIO_DATA_LANE_A(port)); val = 0; @@ -1063,8 +1061,6 @@ static void intel_hdmi_pre_pll_enable(struct intel_encoder *encoder) if (!IS_VALLEYVIEW(dev)) return; - WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); - /* Program Tx lane resets to default */ intel_dpio_write(dev_priv, DPIO_PCS_TX(port), DPIO_PCS_TX_LANE2_RESET | -- cgit v1.2.3 From ae99258f02fe189c008af94f26140ed691258e9f Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 22 May 2013 15:36:19 +0300 Subject: drm/i915: rename VLV IOSF sideband functions logically Rename all VLV IOSF sideband register accessor functions to vlv__{read,write}. No functional changes. Signed-off-by: Jani Nikula Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 24 +++++++++--------- drivers/gpu/drm/i915/i915_drv.h | 10 ++++---- drivers/gpu/drm/i915/i915_sysfs.c | 2 +- drivers/gpu/drm/i915/intel_display.c | 46 +++++++++++++++++------------------ drivers/gpu/drm/i915/intel_dp.c | 32 ++++++++++++------------ drivers/gpu/drm/i915/intel_hdmi.c | 42 ++++++++++++++++---------------- drivers/gpu/drm/i915/intel_pm.c | 16 ++++++------ drivers/gpu/drm/i915/intel_sideband.c | 10 ++++---- 8 files changed, 91 insertions(+), 91 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 3a8409a31266..bc0f6a55c74b 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1137,16 +1137,16 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused) u32 freq_sts, val; mutex_lock(&dev_priv->rps.hw_lock); - valleyview_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS, + vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS, &freq_sts); seq_printf(m, "PUNIT_REG_GPU_FREQ_STS: 0x%08x\n", freq_sts); seq_printf(m, "DDR freq: %d MHz\n", dev_priv->mem_freq); - valleyview_punit_read(dev_priv, PUNIT_FUSE_BUS1, &val); + vlv_punit_read(dev_priv, PUNIT_FUSE_BUS1, &val); seq_printf(m, "max GPU freq: %d MHz\n", vlv_gpu_freq(dev_priv->mem_freq, val)); - valleyview_punit_read(dev_priv, PUNIT_REG_GPU_LFM, &val); + vlv_punit_read(dev_priv, PUNIT_REG_GPU_LFM, &val); seq_printf(m, "min GPU freq: %d MHz\n", vlv_gpu_freq(dev_priv->mem_freq, val)); @@ -1787,27 +1787,27 @@ static int i915_dpio_info(struct seq_file *m, void *data) seq_printf(m, "DPIO_CTL: 0x%08x\n", I915_READ(DPIO_CTL)); seq_printf(m, "DPIO_DIV_A: 0x%08x\n", - intel_dpio_read(dev_priv, _DPIO_DIV_A)); + vlv_dpio_read(dev_priv, _DPIO_DIV_A)); seq_printf(m, "DPIO_DIV_B: 0x%08x\n", - intel_dpio_read(dev_priv, _DPIO_DIV_B)); + vlv_dpio_read(dev_priv, _DPIO_DIV_B)); seq_printf(m, "DPIO_REFSFR_A: 0x%08x\n", - intel_dpio_read(dev_priv, _DPIO_REFSFR_A)); + vlv_dpio_read(dev_priv, _DPIO_REFSFR_A)); seq_printf(m, "DPIO_REFSFR_B: 0x%08x\n", - intel_dpio_read(dev_priv, _DPIO_REFSFR_B)); + vlv_dpio_read(dev_priv, _DPIO_REFSFR_B)); seq_printf(m, "DPIO_CORE_CLK_A: 0x%08x\n", - intel_dpio_read(dev_priv, _DPIO_CORE_CLK_A)); + vlv_dpio_read(dev_priv, _DPIO_CORE_CLK_A)); seq_printf(m, "DPIO_CORE_CLK_B: 0x%08x\n", - intel_dpio_read(dev_priv, _DPIO_CORE_CLK_B)); + vlv_dpio_read(dev_priv, _DPIO_CORE_CLK_B)); seq_printf(m, "DPIO_LFP_COEFF_A: 0x%08x\n", - intel_dpio_read(dev_priv, _DPIO_LFP_COEFF_A)); + vlv_dpio_read(dev_priv, _DPIO_LFP_COEFF_A)); seq_printf(m, "DPIO_LFP_COEFF_B: 0x%08x\n", - intel_dpio_read(dev_priv, _DPIO_LFP_COEFF_B)); + vlv_dpio_read(dev_priv, _DPIO_LFP_COEFF_B)); seq_printf(m, "DPIO_FASTCLK_DISABLE: 0x%08x\n", - intel_dpio_read(dev_priv, DPIO_FASTCLK_DISABLE)); + vlv_dpio_read(dev_priv, DPIO_FASTCLK_DISABLE)); mutex_unlock(&dev_priv->dpio_lock); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 5a0dfca88668..28d14d6cf4cc 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1931,11 +1931,11 @@ int sandybridge_pcode_read(struct drm_i915_private *dev_priv, u8 mbox, u32 *val) int sandybridge_pcode_write(struct drm_i915_private *dev_priv, u8 mbox, u32 val); /* intel_sideband.c */ -int valleyview_punit_read(struct drm_i915_private *dev_priv, u8 addr, u32 *val); -int valleyview_punit_write(struct drm_i915_private *dev_priv, u8 addr, u32 val); -int valleyview_nc_read(struct drm_i915_private *dev_priv, u8 addr, u32 *val); -u32 intel_dpio_read(struct drm_i915_private *dev_priv, int reg); -void intel_dpio_write(struct drm_i915_private *dev_priv, int reg, u32 val); +int vlv_punit_read(struct drm_i915_private *dev_priv, u8 addr, u32 *val); +int vlv_punit_write(struct drm_i915_private *dev_priv, u8 addr, u32 val); +int vlv_nc_read(struct drm_i915_private *dev_priv, u8 addr, u32 *val); +u32 vlv_dpio_read(struct drm_i915_private *dev_priv, int reg); +void vlv_dpio_write(struct drm_i915_private *dev_priv, int reg, u32 val); u32 intel_sbi_read(struct drm_i915_private *dev_priv, u16 reg, enum intel_sbi_destination destination); void intel_sbi_write(struct drm_i915_private *dev_priv, u16 reg, u32 value, diff --git a/drivers/gpu/drm/i915/i915_sysfs.c b/drivers/gpu/drm/i915/i915_sysfs.c index c0d7875b475c..588fa00e6938 100644 --- a/drivers/gpu/drm/i915/i915_sysfs.c +++ b/drivers/gpu/drm/i915/i915_sysfs.c @@ -214,7 +214,7 @@ static ssize_t gt_cur_freq_mhz_show(struct device *kdev, mutex_lock(&dev_priv->rps.hw_lock); if (IS_VALLEYVIEW(dev_priv->dev)) { u32 freq; - valleyview_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS, &freq); + vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS, &freq); ret = vlv_gpu_freq(dev_priv->mem_freq, (freq >> 8) & 0xff); } else { ret = dev_priv->rps.cur_delay * GT_FREQUENCY_MULTIPLIER; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index e07ee4c73592..2a9d0671f8c3 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4245,24 +4245,24 @@ static void vlv_pllb_recal_opamp(struct drm_i915_private *dev_priv) * PLLB opamp always calibrates to max value of 0x3f, force enable it * and set it to a reasonable value instead. */ - reg_val = intel_dpio_read(dev_priv, DPIO_IREF(1)); + reg_val = vlv_dpio_read(dev_priv, DPIO_IREF(1)); reg_val &= 0xffffff00; reg_val |= 0x00000030; - intel_dpio_write(dev_priv, DPIO_IREF(1), reg_val); + vlv_dpio_write(dev_priv, DPIO_IREF(1), reg_val); - reg_val = intel_dpio_read(dev_priv, DPIO_CALIBRATION); + reg_val = vlv_dpio_read(dev_priv, DPIO_CALIBRATION); reg_val &= 0x8cffffff; reg_val = 0x8c000000; - intel_dpio_write(dev_priv, DPIO_CALIBRATION, reg_val); + vlv_dpio_write(dev_priv, DPIO_CALIBRATION, reg_val); - reg_val = intel_dpio_read(dev_priv, DPIO_IREF(1)); + reg_val = vlv_dpio_read(dev_priv, DPIO_IREF(1)); reg_val &= 0xffffff00; - intel_dpio_write(dev_priv, DPIO_IREF(1), reg_val); + vlv_dpio_write(dev_priv, DPIO_IREF(1), reg_val); - reg_val = intel_dpio_read(dev_priv, DPIO_CALIBRATION); + reg_val = vlv_dpio_read(dev_priv, DPIO_CALIBRATION); reg_val &= 0x00ffffff; reg_val |= 0xb0000000; - intel_dpio_write(dev_priv, DPIO_CALIBRATION, reg_val); + vlv_dpio_write(dev_priv, DPIO_CALIBRATION, reg_val); } static void intel_pch_transcoder_set_m_n(struct intel_crtc *crtc, @@ -4337,15 +4337,15 @@ static void vlv_update_pll(struct intel_crtc *crtc) vlv_pllb_recal_opamp(dev_priv); /* Set up Tx target for periodic Rcomp update */ - intel_dpio_write(dev_priv, DPIO_IREF_BCAST, 0x0100000f); + vlv_dpio_write(dev_priv, DPIO_IREF_BCAST, 0x0100000f); /* Disable target IRef on PLL */ - reg_val = intel_dpio_read(dev_priv, DPIO_IREF_CTL(pipe)); + reg_val = vlv_dpio_read(dev_priv, DPIO_IREF_CTL(pipe)); reg_val &= 0x00ffffff; - intel_dpio_write(dev_priv, DPIO_IREF_CTL(pipe), reg_val); + vlv_dpio_write(dev_priv, DPIO_IREF_CTL(pipe), reg_val); /* Disable fast lock */ - intel_dpio_write(dev_priv, DPIO_FASTCLK_DISABLE, 0x610); + vlv_dpio_write(dev_priv, DPIO_FASTCLK_DISABLE, 0x610); /* Set idtafcrecal before PLL is enabled */ mdiv = ((bestm1 << DPIO_M1DIV_SHIFT) | (bestm2 & DPIO_M2DIV_MASK)); @@ -4359,47 +4359,47 @@ static void vlv_update_pll(struct intel_crtc *crtc) * Note: don't use the DAC post divider as it seems unstable. */ mdiv |= (DPIO_POST_DIV_HDMIDP << DPIO_POST_DIV_SHIFT); - intel_dpio_write(dev_priv, DPIO_DIV(pipe), mdiv); + vlv_dpio_write(dev_priv, DPIO_DIV(pipe), mdiv); mdiv |= DPIO_ENABLE_CALIBRATION; - intel_dpio_write(dev_priv, DPIO_DIV(pipe), mdiv); + vlv_dpio_write(dev_priv, DPIO_DIV(pipe), mdiv); /* Set HBR and RBR LPF coefficients */ if (adjusted_mode->clock == 162000 || intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_HDMI)) - intel_dpio_write(dev_priv, DPIO_LFP_COEFF(pipe), + vlv_dpio_write(dev_priv, DPIO_LFP_COEFF(pipe), 0x005f0021); else - intel_dpio_write(dev_priv, DPIO_LFP_COEFF(pipe), + vlv_dpio_write(dev_priv, DPIO_LFP_COEFF(pipe), 0x00d0000f); if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_EDP) || intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_DISPLAYPORT)) { /* Use SSC source */ if (!pipe) - intel_dpio_write(dev_priv, DPIO_REFSFR(pipe), + vlv_dpio_write(dev_priv, DPIO_REFSFR(pipe), 0x0df40000); else - intel_dpio_write(dev_priv, DPIO_REFSFR(pipe), + vlv_dpio_write(dev_priv, DPIO_REFSFR(pipe), 0x0df70000); } else { /* HDMI or VGA */ /* Use bend source */ if (!pipe) - intel_dpio_write(dev_priv, DPIO_REFSFR(pipe), + vlv_dpio_write(dev_priv, DPIO_REFSFR(pipe), 0x0df70000); else - intel_dpio_write(dev_priv, DPIO_REFSFR(pipe), + vlv_dpio_write(dev_priv, DPIO_REFSFR(pipe), 0x0df40000); } - coreclk = intel_dpio_read(dev_priv, DPIO_CORE_CLK(pipe)); + coreclk = vlv_dpio_read(dev_priv, DPIO_CORE_CLK(pipe)); coreclk = (coreclk & 0x0000ff00) | 0x01c00000; if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_DISPLAYPORT) || intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_EDP)) coreclk |= 0x01000000; - intel_dpio_write(dev_priv, DPIO_CORE_CLK(pipe), coreclk); + vlv_dpio_write(dev_priv, DPIO_CORE_CLK(pipe), coreclk); - intel_dpio_write(dev_priv, DPIO_PLL_CML(pipe), 0x87871000); + vlv_dpio_write(dev_priv, DPIO_PLL_CML(pipe), 0x87871000); for_each_encoder_on_crtc(dev, &crtc->base, encoder) if (encoder->pre_pll_enable) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 098e7c25f4ef..cdb1c2a6d2b2 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1442,18 +1442,18 @@ static void intel_pre_enable_dp(struct intel_encoder *encoder) int pipe = intel_crtc->pipe; u32 val; - val = intel_dpio_read(dev_priv, DPIO_DATA_LANE_A(port)); + val = vlv_dpio_read(dev_priv, DPIO_DATA_LANE_A(port)); val = 0; if (pipe) val |= (1<<21); else val &= ~(1<<21); val |= 0x001000c4; - intel_dpio_write(dev_priv, DPIO_DATA_CHANNEL(port), val); + vlv_dpio_write(dev_priv, DPIO_DATA_CHANNEL(port), val); - intel_dpio_write(dev_priv, DPIO_PCS_CLOCKBUF0(port), + vlv_dpio_write(dev_priv, DPIO_PCS_CLOCKBUF0(port), 0x00760018); - intel_dpio_write(dev_priv, DPIO_PCS_CLOCKBUF8(port), + vlv_dpio_write(dev_priv, DPIO_PCS_CLOCKBUF8(port), 0x00400888); } } @@ -1469,19 +1469,19 @@ static void intel_dp_pre_pll_enable(struct intel_encoder *encoder) return; /* Program Tx lane resets to default */ - intel_dpio_write(dev_priv, DPIO_PCS_TX(port), + vlv_dpio_write(dev_priv, DPIO_PCS_TX(port), DPIO_PCS_TX_LANE2_RESET | DPIO_PCS_TX_LANE1_RESET); - intel_dpio_write(dev_priv, DPIO_PCS_CLK(port), + vlv_dpio_write(dev_priv, DPIO_PCS_CLK(port), DPIO_PCS_CLK_CRI_RXEB_EIOS_EN | DPIO_PCS_CLK_CRI_RXDIGFILTSG_EN | (1<dpio_lock); - intel_dpio_write(dev_priv, DPIO_PCS_TX(port), 0x00000000); - intel_dpio_write(dev_priv, DPIO_PCS_CLK(port), 0x00e00060); + vlv_dpio_write(dev_priv, DPIO_PCS_TX(port), 0x00000000); + vlv_dpio_write(dev_priv, DPIO_PCS_CLK(port), 0x00e00060); mutex_unlock(&dev_priv->dpio_lock); } diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index cd5bd88980bb..52f1b39148c0 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -2566,10 +2566,10 @@ void valleyview_set_rps(struct drm_device *dev, u8 val) if (val == dev_priv->rps.cur_delay) return; - valleyview_punit_write(dev_priv, PUNIT_REG_GPU_FREQ_REQ, val); + vlv_punit_write(dev_priv, PUNIT_REG_GPU_FREQ_REQ, val); do { - valleyview_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS, &pval); + vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS, &pval); if (time_after(jiffies, timeout)) { DRM_DEBUG_DRIVER("timed out waiting for Punit\n"); break; @@ -2577,7 +2577,7 @@ void valleyview_set_rps(struct drm_device *dev, u8 val) udelay(10); } while (pval & 1); - valleyview_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS, &pval); + vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS, &pval); if ((pval >> 8) != val) DRM_DEBUG_DRIVER("punit overrode freq: %d requested, but got %d\n", val, pval >> 8); @@ -2882,7 +2882,7 @@ int valleyview_rps_max_freq(struct drm_i915_private *dev_priv) { u32 val, rp0; - valleyview_nc_read(dev_priv, IOSF_NC_FB_GFX_FREQ_FUSE, &val); + vlv_nc_read(dev_priv, IOSF_NC_FB_GFX_FREQ_FUSE, &val); rp0 = (val & FB_GFX_MAX_FREQ_FUSE_MASK) >> FB_GFX_MAX_FREQ_FUSE_SHIFT; /* Clamp to max */ @@ -2895,9 +2895,9 @@ static int valleyview_rps_rpe_freq(struct drm_i915_private *dev_priv) { u32 val, rpe; - valleyview_nc_read(dev_priv, IOSF_NC_FB_GFX_FMAX_FUSE_LO, &val); + vlv_nc_read(dev_priv, IOSF_NC_FB_GFX_FMAX_FUSE_LO, &val); rpe = (val & FB_FMAX_VMIN_FREQ_LO_MASK) >> FB_FMAX_VMIN_FREQ_LO_SHIFT; - valleyview_nc_read(dev_priv, IOSF_NC_FB_GFX_FMAX_FUSE_HI, &val); + vlv_nc_read(dev_priv, IOSF_NC_FB_GFX_FMAX_FUSE_HI, &val); rpe |= (val & FB_FMAX_VMIN_FREQ_HI_MASK) << 5; return rpe; @@ -2907,7 +2907,7 @@ int valleyview_rps_min_freq(struct drm_i915_private *dev_priv) { u32 val; - valleyview_punit_read(dev_priv, PUNIT_REG_GPU_LFM, &val); + vlv_punit_read(dev_priv, PUNIT_REG_GPU_LFM, &val); return val & 0xff; } @@ -3018,7 +3018,7 @@ static void valleyview_enable_rps(struct drm_device *dev) I915_WRITE(GEN6_RC_CONTROL, GEN7_RC_CTL_TO_MODE); - valleyview_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS, &val); + vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS, &val); switch ((val >> 6) & 3) { case 0: case 1: diff --git a/drivers/gpu/drm/i915/intel_sideband.c b/drivers/gpu/drm/i915/intel_sideband.c index a7c4b61e9c30..d150972da048 100644 --- a/drivers/gpu/drm/i915/intel_sideband.c +++ b/drivers/gpu/drm/i915/intel_sideband.c @@ -63,7 +63,7 @@ static int vlv_sideband_rw(struct drm_i915_private *dev_priv, u32 devfn, return 0; } -int valleyview_punit_read(struct drm_i915_private *dev_priv, u8 addr, u32 *val) +int vlv_punit_read(struct drm_i915_private *dev_priv, u8 addr, u32 *val) { int ret; @@ -77,7 +77,7 @@ int valleyview_punit_read(struct drm_i915_private *dev_priv, u8 addr, u32 *val) return ret; } -int valleyview_punit_write(struct drm_i915_private *dev_priv, u8 addr, u32 val) +int vlv_punit_write(struct drm_i915_private *dev_priv, u8 addr, u32 val) { int ret; @@ -91,7 +91,7 @@ int valleyview_punit_write(struct drm_i915_private *dev_priv, u8 addr, u32 val) return ret; } -int valleyview_nc_read(struct drm_i915_private *dev_priv, u8 addr, u32 *val) +int vlv_nc_read(struct drm_i915_private *dev_priv, u8 addr, u32 *val) { int ret; @@ -105,7 +105,7 @@ int valleyview_nc_read(struct drm_i915_private *dev_priv, u8 addr, u32 *val) return ret; } -u32 intel_dpio_read(struct drm_i915_private *dev_priv, int reg) +u32 vlv_dpio_read(struct drm_i915_private *dev_priv, int reg) { u32 val = 0; @@ -115,7 +115,7 @@ u32 intel_dpio_read(struct drm_i915_private *dev_priv, int reg) return val; } -void intel_dpio_write(struct drm_i915_private *dev_priv, int reg, u32 val) +void vlv_dpio_write(struct drm_i915_private *dev_priv, int reg, u32 val) { vlv_sideband_rw(dev_priv, DPIO_DEVFN, IOSF_PORT_DPIO, DPIO_OPCODE_REG_WRITE, reg, &val); -- cgit v1.2.3 From 64936258d7e426bee5f2392269b1b20172db9ffb Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 22 May 2013 15:36:20 +0300 Subject: drm/i915: change VLV IOSF sideband accessors to not return error code We never check the return values, and there's not much we could do on errors anyway. Just simplify the signatures. No functional changes. Signed-off-by: Jani Nikula Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 7 +++---- drivers/gpu/drm/i915/i915_drv.h | 6 +++--- drivers/gpu/drm/i915/i915_sysfs.c | 2 +- drivers/gpu/drm/i915/intel_pm.c | 18 +++++++----------- drivers/gpu/drm/i915/intel_sideband.c | 30 +++++++++++++----------------- 5 files changed, 27 insertions(+), 36 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index bc0f6a55c74b..2eb572afbcd3 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1137,16 +1137,15 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused) u32 freq_sts, val; mutex_lock(&dev_priv->rps.hw_lock); - vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS, - &freq_sts); + freq_sts = vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS); seq_printf(m, "PUNIT_REG_GPU_FREQ_STS: 0x%08x\n", freq_sts); seq_printf(m, "DDR freq: %d MHz\n", dev_priv->mem_freq); - vlv_punit_read(dev_priv, PUNIT_FUSE_BUS1, &val); + val = vlv_punit_read(dev_priv, PUNIT_FUSE_BUS1); seq_printf(m, "max GPU freq: %d MHz\n", vlv_gpu_freq(dev_priv->mem_freq, val)); - vlv_punit_read(dev_priv, PUNIT_REG_GPU_LFM, &val); + val = vlv_punit_read(dev_priv, PUNIT_REG_GPU_LFM); seq_printf(m, "min GPU freq: %d MHz\n", vlv_gpu_freq(dev_priv->mem_freq, val)); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 28d14d6cf4cc..f6419f40df38 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1931,9 +1931,9 @@ int sandybridge_pcode_read(struct drm_i915_private *dev_priv, u8 mbox, u32 *val) int sandybridge_pcode_write(struct drm_i915_private *dev_priv, u8 mbox, u32 val); /* intel_sideband.c */ -int vlv_punit_read(struct drm_i915_private *dev_priv, u8 addr, u32 *val); -int vlv_punit_write(struct drm_i915_private *dev_priv, u8 addr, u32 val); -int vlv_nc_read(struct drm_i915_private *dev_priv, u8 addr, u32 *val); +u32 vlv_punit_read(struct drm_i915_private *dev_priv, u8 addr); +void vlv_punit_write(struct drm_i915_private *dev_priv, u8 addr, u32 val); +u32 vlv_nc_read(struct drm_i915_private *dev_priv, u8 addr); u32 vlv_dpio_read(struct drm_i915_private *dev_priv, int reg); void vlv_dpio_write(struct drm_i915_private *dev_priv, int reg, u32 val); u32 intel_sbi_read(struct drm_i915_private *dev_priv, u16 reg, diff --git a/drivers/gpu/drm/i915/i915_sysfs.c b/drivers/gpu/drm/i915/i915_sysfs.c index 588fa00e6938..6875b5654c63 100644 --- a/drivers/gpu/drm/i915/i915_sysfs.c +++ b/drivers/gpu/drm/i915/i915_sysfs.c @@ -214,7 +214,7 @@ static ssize_t gt_cur_freq_mhz_show(struct device *kdev, mutex_lock(&dev_priv->rps.hw_lock); if (IS_VALLEYVIEW(dev_priv->dev)) { u32 freq; - vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS, &freq); + freq = vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS); ret = vlv_gpu_freq(dev_priv->mem_freq, (freq >> 8) & 0xff); } else { ret = dev_priv->rps.cur_delay * GT_FREQUENCY_MULTIPLIER; diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 52f1b39148c0..fa4c818d4b00 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -2569,7 +2569,7 @@ void valleyview_set_rps(struct drm_device *dev, u8 val) vlv_punit_write(dev_priv, PUNIT_REG_GPU_FREQ_REQ, val); do { - vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS, &pval); + pval = vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS); if (time_after(jiffies, timeout)) { DRM_DEBUG_DRIVER("timed out waiting for Punit\n"); break; @@ -2577,7 +2577,7 @@ void valleyview_set_rps(struct drm_device *dev, u8 val) udelay(10); } while (pval & 1); - vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS, &pval); + pval = vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS); if ((pval >> 8) != val) DRM_DEBUG_DRIVER("punit overrode freq: %d requested, but got %d\n", val, pval >> 8); @@ -2882,7 +2882,7 @@ int valleyview_rps_max_freq(struct drm_i915_private *dev_priv) { u32 val, rp0; - vlv_nc_read(dev_priv, IOSF_NC_FB_GFX_FREQ_FUSE, &val); + val = vlv_nc_read(dev_priv, IOSF_NC_FB_GFX_FREQ_FUSE); rp0 = (val & FB_GFX_MAX_FREQ_FUSE_MASK) >> FB_GFX_MAX_FREQ_FUSE_SHIFT; /* Clamp to max */ @@ -2895,9 +2895,9 @@ static int valleyview_rps_rpe_freq(struct drm_i915_private *dev_priv) { u32 val, rpe; - vlv_nc_read(dev_priv, IOSF_NC_FB_GFX_FMAX_FUSE_LO, &val); + val = vlv_nc_read(dev_priv, IOSF_NC_FB_GFX_FMAX_FUSE_LO); rpe = (val & FB_FMAX_VMIN_FREQ_LO_MASK) >> FB_FMAX_VMIN_FREQ_LO_SHIFT; - vlv_nc_read(dev_priv, IOSF_NC_FB_GFX_FMAX_FUSE_HI, &val); + val = vlv_nc_read(dev_priv, IOSF_NC_FB_GFX_FMAX_FUSE_HI); rpe |= (val & FB_FMAX_VMIN_FREQ_HI_MASK) << 5; return rpe; @@ -2905,11 +2905,7 @@ static int valleyview_rps_rpe_freq(struct drm_i915_private *dev_priv) int valleyview_rps_min_freq(struct drm_i915_private *dev_priv) { - u32 val; - - vlv_punit_read(dev_priv, PUNIT_REG_GPU_LFM, &val); - - return val & 0xff; + return vlv_punit_read(dev_priv, PUNIT_REG_GPU_LFM) & 0xff; } static void vlv_rps_timer_work(struct work_struct *work) @@ -3018,7 +3014,7 @@ static void valleyview_enable_rps(struct drm_device *dev) I915_WRITE(GEN6_RC_CONTROL, GEN7_RC_CTL_TO_MODE); - vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS, &val); + val = vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS); switch ((val >> 6) & 3) { case 0: case 1: diff --git a/drivers/gpu/drm/i915/intel_sideband.c b/drivers/gpu/drm/i915/intel_sideband.c index d150972da048..9a0e6c5ea540 100644 --- a/drivers/gpu/drm/i915/intel_sideband.c +++ b/drivers/gpu/drm/i915/intel_sideband.c @@ -63,46 +63,42 @@ static int vlv_sideband_rw(struct drm_i915_private *dev_priv, u32 devfn, return 0; } -int vlv_punit_read(struct drm_i915_private *dev_priv, u8 addr, u32 *val) +u32 vlv_punit_read(struct drm_i915_private *dev_priv, u8 addr) { - int ret; + u32 val = 0; WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock)); mutex_lock(&dev_priv->dpio_lock); - ret = vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_PUNIT, - PUNIT_OPCODE_REG_READ, addr, val); + vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_PUNIT, + PUNIT_OPCODE_REG_READ, addr, &val); mutex_unlock(&dev_priv->dpio_lock); - return ret; + return val; } -int vlv_punit_write(struct drm_i915_private *dev_priv, u8 addr, u32 val) +void vlv_punit_write(struct drm_i915_private *dev_priv, u8 addr, u32 val) { - int ret; - WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock)); mutex_lock(&dev_priv->dpio_lock); - ret = vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_PUNIT, - PUNIT_OPCODE_REG_WRITE, addr, &val); + vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_PUNIT, + PUNIT_OPCODE_REG_WRITE, addr, &val); mutex_unlock(&dev_priv->dpio_lock); - - return ret; } -int vlv_nc_read(struct drm_i915_private *dev_priv, u8 addr, u32 *val) +u32 vlv_nc_read(struct drm_i915_private *dev_priv, u8 addr) { - int ret; + u32 val = 0; WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock)); mutex_lock(&dev_priv->dpio_lock); - ret = vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_NC, - PUNIT_OPCODE_REG_READ, addr, val); + vlv_sideband_rw(dev_priv, PCI_DEVFN(2, 0), IOSF_PORT_NC, + PUNIT_OPCODE_REG_READ, addr, &val); mutex_unlock(&dev_priv->dpio_lock); - return ret; + return val; } u32 vlv_dpio_read(struct drm_i915_private *dev_priv, int reg) -- cgit v1.2.3 From 913e96bc292e1bb248854686c79d6545ef3ee720 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Tue, 21 May 2013 18:02:02 +1000 Subject: xfs: remote attribute read too short Reading a maximally size remote attribute fails when CRCs are enabled with this verification error: XFS (vdb): remote attribute header does not match required off/len/owner) There are two reasons for this, the first being that the length of the buffer being read is determined from the args->rmtblkcnt which doesn't take into account CRC headers. Hence the mapped length ends up being too short and so we need to calculate it directly from the value length. The second is that the byte count of valid data within a buffer is capped by the length of the data and so doesn't take into account that the buffer might be longer due to headers. Hence we need to calculate the data space in the buffer first before calculating the actual byte count of data. Signed-off-by: Dave Chinner Reviewed-by: Ben Myers Signed-off-by: Ben Myers --- fs/xfs/xfs_attr_remote.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/fs/xfs/xfs_attr_remote.c b/fs/xfs/xfs_attr_remote.c index aad95b08e76b..bcdc07c4e8f4 100644 --- a/fs/xfs/xfs_attr_remote.c +++ b/fs/xfs/xfs_attr_remote.c @@ -52,9 +52,11 @@ xfs_attr3_rmt_blocks( struct xfs_mount *mp, int attrlen) { - int buflen = XFS_ATTR3_RMT_BUF_SPACE(mp, - mp->m_sb.sb_blocksize); - return (attrlen + buflen - 1) / buflen; + if (xfs_sb_version_hascrc(&mp->m_sb)) { + int buflen = XFS_ATTR3_RMT_BUF_SPACE(mp, mp->m_sb.sb_blocksize); + return (attrlen + buflen - 1) / buflen; + } + return XFS_B_TO_FSB(mp, attrlen); } static bool @@ -206,8 +208,9 @@ xfs_attr_rmtval_get( while (valuelen > 0) { nmap = ATTR_RMTVALUE_MAPSIZE; + blkcnt = xfs_attr3_rmt_blocks(mp, valuelen); error = xfs_bmapi_read(args->dp, (xfs_fileoff_t)lblkno, - args->rmtblkcnt, map, &nmap, + blkcnt, map, &nmap, XFS_BMAPI_ATTRFORK); if (error) return error; @@ -227,8 +230,8 @@ xfs_attr_rmtval_get( if (error) return error; - byte_cnt = min_t(int, valuelen, BBTOB(bp->b_length)); - byte_cnt = XFS_ATTR3_RMT_BUF_SPACE(mp, byte_cnt); + byte_cnt = XFS_ATTR3_RMT_BUF_SPACE(mp, BBTOB(bp->b_length)); + byte_cnt = min_t(int, valuelen, byte_cnt); src = bp->b_addr; if (xfs_sb_version_hascrc(&mp->m_sb)) { -- cgit v1.2.3 From 4af3644c9a53eb2f1ecf69cc53576561b64be4c6 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Tue, 21 May 2013 18:02:03 +1000 Subject: xfs: remote attribute tail zeroing does too much When an attribute data does not fill then entire remote block, we zero the remaining part of the buffer. This, however, needs to take into account that the buffer has a header, and so the offset where zeroing starts and the length of zeroing need to take this into account. Otherwise we end up with zeros over the end of the attribute value when CRCs are enabled. While there, make sure we only ask to map an extent that covers the remaining range of the attribute, rather than asking every time for the full length of remote data. If the remote attribute blocks are contiguous with other parts of the attribute tree, it will map those blocks as well and we can potentially zero them incorrectly. We can also get buffer size mistmatches when trying to read or remove the remote attribute, and this can lead to not finding the correct buffer when looking it up in cache. Signed-off-by: Dave Chinner Reviewed-by: Ben Myers Signed-off-by: Ben Myers --- fs/xfs/xfs_attr_remote.c | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/fs/xfs/xfs_attr_remote.c b/fs/xfs/xfs_attr_remote.c index bcdc07c4e8f4..e207bf0004b6 100644 --- a/fs/xfs/xfs_attr_remote.c +++ b/fs/xfs/xfs_attr_remote.c @@ -296,10 +296,7 @@ xfs_attr_rmtval_set( * and we may not need that many, so we have to handle this when * allocating the blocks below. */ - if (!crcs) - blkcnt = XFS_B_TO_FSB(mp, args->valuelen); - else - blkcnt = xfs_attr3_rmt_blocks(mp, args->valuelen); + blkcnt = xfs_attr3_rmt_blocks(mp, args->valuelen); error = xfs_bmap_first_unused(args->trans, args->dp, blkcnt, &lfileoff, XFS_ATTR_FORK); @@ -394,8 +391,11 @@ xfs_attr_rmtval_set( */ lblkno = args->rmtblkno; valuelen = args->valuelen; + blkcnt = args->rmtblkcnt; while (valuelen > 0) { int byte_cnt; + int hdr_size; + int dblkcnt; char *buf; /* @@ -404,7 +404,7 @@ xfs_attr_rmtval_set( xfs_bmap_init(args->flist, args->firstblock); nmap = 1; error = xfs_bmapi_read(dp, (xfs_fileoff_t)lblkno, - args->rmtblkcnt, &map, &nmap, + blkcnt, &map, &nmap, XFS_BMAPI_ATTRFORK); if (error) return(error); @@ -413,26 +413,25 @@ xfs_attr_rmtval_set( (map.br_startblock != HOLESTARTBLOCK)); dblkno = XFS_FSB_TO_DADDR(mp, map.br_startblock), - blkcnt = XFS_FSB_TO_BB(mp, map.br_blockcount); + dblkcnt = XFS_FSB_TO_BB(mp, map.br_blockcount); - bp = xfs_buf_get(mp->m_ddev_targp, dblkno, blkcnt, 0); + bp = xfs_buf_get(mp->m_ddev_targp, dblkno, dblkcnt, 0); if (!bp) return ENOMEM; bp->b_ops = &xfs_attr3_rmt_buf_ops; - - byte_cnt = BBTOB(bp->b_length); - byte_cnt = XFS_ATTR3_RMT_BUF_SPACE(mp, byte_cnt); - if (valuelen < byte_cnt) - byte_cnt = valuelen; - buf = bp->b_addr; - buf += xfs_attr3_rmt_hdr_set(mp, dp->i_ino, offset, + + byte_cnt = XFS_ATTR3_RMT_BUF_SPACE(mp, BBTOB(bp->b_length)); + byte_cnt = min_t(int, valuelen, byte_cnt); + hdr_size = xfs_attr3_rmt_hdr_set(mp, dp->i_ino, offset, byte_cnt, bp); - memcpy(buf, src, byte_cnt); + ASSERT(hdr_size + byte_cnt <= BBTOB(bp->b_length)); - if (byte_cnt < BBTOB(bp->b_length)) - xfs_buf_zero(bp, byte_cnt, - BBTOB(bp->b_length) - byte_cnt); + memcpy(buf + hdr_size, src, byte_cnt); + + if (byte_cnt + hdr_size < BBTOB(bp->b_length)) + xfs_buf_zero(bp, byte_cnt + hdr_size, + BBTOB(bp->b_length) - byte_cnt - hdr_size); error = xfs_bwrite(bp); /* GROT: NOTE: synchronous write */ xfs_buf_relse(bp); @@ -442,9 +441,9 @@ xfs_attr_rmtval_set( src += byte_cnt; valuelen -= byte_cnt; offset += byte_cnt; - hdrcnt--; lblkno += map.br_blockcount; + blkcnt -= map.br_blockcount; } ASSERT(valuelen == 0); return 0; -- cgit v1.2.3 From 6863ef8449f1908c19f43db572e4474f24a1e9da Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Tue, 21 May 2013 18:02:04 +1000 Subject: xfs: correctly map remote attr buffers during removal If we don't map the buffers correctly (same as for get/set operations) then the incore buffer lookup will fail. If a block number matches but a length is wrong, then debug kernels will ASSERT fail in _xfs_buf_find() due to the length mismatch. Ensure that we map the buffers correctly by basing the length of the buffer on the attribute data length rather than the remote block count. Signed-off-by: Dave Chinner Reviewed-by: Ben Myers Signed-off-by: Ben Myers --- fs/xfs/xfs_attr_remote.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/fs/xfs/xfs_attr_remote.c b/fs/xfs/xfs_attr_remote.c index e207bf0004b6..d8bcb2d742d1 100644 --- a/fs/xfs/xfs_attr_remote.c +++ b/fs/xfs/xfs_attr_remote.c @@ -468,19 +468,25 @@ xfs_attr_rmtval_remove(xfs_da_args_t *args) mp = args->dp->i_mount; /* - * Roll through the "value", invalidating the attribute value's - * blocks. + * Roll through the "value", invalidating the attribute value's blocks. + * Note that args->rmtblkcnt is the minimum number of data blocks we'll + * see for a CRC enabled remote attribute. Each extent will have a + * header, and so we may have more blocks than we realise here. If we + * fail to map the blocks correctly, we'll have problems with the buffer + * lookups. */ lblkno = args->rmtblkno; - valuelen = args->rmtblkcnt; + valuelen = args->valuelen; + blkcnt = xfs_attr3_rmt_blocks(mp, valuelen); while (valuelen > 0) { + int dblkcnt; + /* * Try to remember where we decided to put the value. */ nmap = 1; error = xfs_bmapi_read(args->dp, (xfs_fileoff_t)lblkno, - args->rmtblkcnt, &map, &nmap, - XFS_BMAPI_ATTRFORK); + blkcnt, &map, &nmap, XFS_BMAPI_ATTRFORK); if (error) return(error); ASSERT(nmap == 1); @@ -488,28 +494,31 @@ xfs_attr_rmtval_remove(xfs_da_args_t *args) (map.br_startblock != HOLESTARTBLOCK)); dblkno = XFS_FSB_TO_DADDR(mp, map.br_startblock), - blkcnt = XFS_FSB_TO_BB(mp, map.br_blockcount); + dblkcnt = XFS_FSB_TO_BB(mp, map.br_blockcount); /* * If the "remote" value is in the cache, remove it. */ - bp = xfs_incore(mp->m_ddev_targp, dblkno, blkcnt, XBF_TRYLOCK); + bp = xfs_incore(mp->m_ddev_targp, dblkno, dblkcnt, XBF_TRYLOCK); if (bp) { xfs_buf_stale(bp); xfs_buf_relse(bp); bp = NULL; } - valuelen -= map.br_blockcount; + valuelen -= XFS_ATTR3_RMT_BUF_SPACE(mp, + XFS_FSB_TO_B(mp, map.br_blockcount)); lblkno += map.br_blockcount; + blkcnt -= map.br_blockcount; + blkcnt = max(blkcnt, xfs_attr3_rmt_blocks(mp, valuelen)); } /* * Keep de-allocating extents until the remote-value region is gone. */ + blkcnt = lblkno - args->rmtblkno; lblkno = args->rmtblkno; - blkcnt = args->rmtblkcnt; done = 0; while (!done) { xfs_bmap_init(args->flist, args->firstblock); -- cgit v1.2.3 From 8517de2a81da830f5d90da66b4799f4040c76dc9 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Tue, 21 May 2013 18:02:05 +1000 Subject: xfs: fully initialise temp leaf in xfs_attr3_leaf_unbalance xfs_attr3_leaf_unbalance() uses a temporary buffer for recombining the entries in two leaves when the destination leaf requires compaction. The temporary buffer ends up being copied back over the original destination buffer, so the header in the temporary buffer needs to contain all the information that is in the destination buffer. To make sure the temporary buffer is fully initialised, once we've set up the temporary incore header appropriately, write is back to the temporary buffer before starting to move entries around. Signed-off-by: Dave Chinner Reviewed-by: Ben Myers Signed-off-by: Ben Myers --- fs/xfs/xfs_attr_leaf.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/fs/xfs/xfs_attr_leaf.c b/fs/xfs/xfs_attr_leaf.c index 0bce1b348580..79ece72976ae 100644 --- a/fs/xfs/xfs_attr_leaf.c +++ b/fs/xfs/xfs_attr_leaf.c @@ -2181,14 +2181,24 @@ xfs_attr3_leaf_unbalance( struct xfs_attr_leafblock *tmp_leaf; struct xfs_attr3_icleaf_hdr tmphdr; - tmp_leaf = kmem_alloc(state->blocksize, KM_SLEEP); - memset(tmp_leaf, 0, state->blocksize); - memset(&tmphdr, 0, sizeof(tmphdr)); + tmp_leaf = kmem_zalloc(state->blocksize, KM_SLEEP); + + /* + * Copy the header into the temp leaf so that all the stuff + * not in the incore header is present and gets copied back in + * once we've moved all the entries. + */ + memcpy(tmp_leaf, save_leaf, xfs_attr3_leaf_hdr_size(save_leaf)); + memset(&tmphdr, 0, sizeof(tmphdr)); tmphdr.magic = savehdr.magic; tmphdr.forw = savehdr.forw; tmphdr.back = savehdr.back; tmphdr.firstused = state->blocksize; + + /* write the header to the temp buffer to initialise it */ + xfs_attr3_leaf_hdr_to_disk(tmp_leaf, &tmphdr); + if (xfs_attr3_leaf_order(save_blk->bp, &savehdr, drop_blk->bp, &drophdr)) { xfs_attr3_leaf_moveents(drop_leaf, &drophdr, 0, -- cgit v1.2.3 From d4c712bcf26a25c2b67c90e44e0b74c7993b5334 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Tue, 21 May 2013 18:02:06 +1000 Subject: xfs: fully initialise temp leaf in xfs_attr3_leaf_compact xfs_attr3_leaf_compact() uses a temporary buffer for compacting the the entries in a leaf. It copies the the original buffer into the temporary buffer, then zeros the original buffer completely. It then copies the entries back into the original buffer. However, the original buffer has not been correctly initialised, and so the movement of the entries goes horribly wrong. Make sure the zeroed destination buffer is fully initialised, and once we've set up the destination incore header appropriately, write is back to the buffer before starting to move entries around. While debugging this, the _d/_s prefixes weren't sufficient to remind me what buffer was what, so rename then all _src/_dst. Signed-off-by: Dave Chinner Reviewed-by: Ben Myers Signed-off-by: Ben Myers --- fs/xfs/xfs_attr_leaf.c | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/fs/xfs/xfs_attr_leaf.c b/fs/xfs/xfs_attr_leaf.c index 79ece72976ae..5b03d15b707b 100644 --- a/fs/xfs/xfs_attr_leaf.c +++ b/fs/xfs/xfs_attr_leaf.c @@ -1445,11 +1445,12 @@ xfs_attr3_leaf_add_work( STATIC void xfs_attr3_leaf_compact( struct xfs_da_args *args, - struct xfs_attr3_icleaf_hdr *ichdr_d, + struct xfs_attr3_icleaf_hdr *ichdr_dst, struct xfs_buf *bp) { - xfs_attr_leafblock_t *leaf_s, *leaf_d; - struct xfs_attr3_icleaf_hdr ichdr_s; + struct xfs_attr_leafblock *leaf_src; + struct xfs_attr_leafblock *leaf_dst; + struct xfs_attr3_icleaf_hdr ichdr_src; struct xfs_trans *trans = args->trans; struct xfs_mount *mp = trans->t_mountp; char *tmpbuffer; @@ -1457,29 +1458,38 @@ xfs_attr3_leaf_compact( trace_xfs_attr_leaf_compact(args); tmpbuffer = kmem_alloc(XFS_LBSIZE(mp), KM_SLEEP); - ASSERT(tmpbuffer != NULL); memcpy(tmpbuffer, bp->b_addr, XFS_LBSIZE(mp)); memset(bp->b_addr, 0, XFS_LBSIZE(mp)); + leaf_src = (xfs_attr_leafblock_t *)tmpbuffer; + leaf_dst = bp->b_addr; /* - * Copy basic information + * Copy the on-disk header back into the destination buffer to ensure + * all the information in the header that is not part of the incore + * header structure is preserved. */ - leaf_s = (xfs_attr_leafblock_t *)tmpbuffer; - leaf_d = bp->b_addr; - ichdr_s = *ichdr_d; /* struct copy */ - ichdr_d->firstused = XFS_LBSIZE(mp); - ichdr_d->usedbytes = 0; - ichdr_d->count = 0; - ichdr_d->holes = 0; - ichdr_d->freemap[0].base = xfs_attr3_leaf_hdr_size(leaf_s); - ichdr_d->freemap[0].size = ichdr_d->firstused - ichdr_d->freemap[0].base; + memcpy(bp->b_addr, tmpbuffer, xfs_attr3_leaf_hdr_size(leaf_src)); + + /* Initialise the incore headers */ + ichdr_src = *ichdr_dst; /* struct copy */ + ichdr_dst->firstused = XFS_LBSIZE(mp); + ichdr_dst->usedbytes = 0; + ichdr_dst->count = 0; + ichdr_dst->holes = 0; + ichdr_dst->freemap[0].base = xfs_attr3_leaf_hdr_size(leaf_src); + ichdr_dst->freemap[0].size = ichdr_dst->firstused - + ichdr_dst->freemap[0].base; + + + /* write the header back to initialise the underlying buffer */ + xfs_attr3_leaf_hdr_to_disk(leaf_dst, ichdr_dst); /* * Copy all entry's in the same (sorted) order, * but allocate name/value pairs packed and in sequence. */ - xfs_attr3_leaf_moveents(leaf_s, &ichdr_s, 0, leaf_d, ichdr_d, 0, - ichdr_s.count, mp); + xfs_attr3_leaf_moveents(leaf_src, &ichdr_src, 0, leaf_dst, ichdr_dst, 0, + ichdr_src.count, mp); /* * this logs the entire buffer, but the caller must write the header * back to the buffer when it is finished modifying it. -- cgit v1.2.3 From ad1858d77771172e08016890f0eb2faedec3ecee Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Tue, 21 May 2013 18:02:08 +1000 Subject: xfs: rework remote attr CRCs Note: this changes the on-disk remote attribute format. I assert that this is OK to do as CRCs are marked experimental and the first kernel it is included in has not yet reached release yet. Further, the userspace utilities are still evolving and so anyone using this stuff right now is a developer or tester using volatile filesystems for testing this feature. Hence changing the format right now to save longer term pain is the right thing to do. The fundamental change is to move from a header per extent in the attribute to a header per filesytem block in the attribute. This means there are more header blocks and the parsing of the attribute data is slightly more complex, but it has the advantage that we always know the size of the attribute on disk based on the length of the data it contains. This is where the header-per-extent method has problems. We don't know the size of the attribute on disk without first knowing how many extents are used to hold it. And we can't tell from a mapping lookup, either, because remote attributes can be allocated contiguously with other attribute blocks and so there is no obvious way of determining the actual size of the atribute on disk short of walking and mapping buffers. The problem with this approach is that if we map a buffer incorrectly (e.g. we make the last buffer for the attribute data too long), we then get buffer cache lookup failure when we map it correctly. i.e. we get a size mismatch on lookup. This is not necessarily fatal, but it's a cache coherency problem that can lead to returning the wrong data to userspace or writing the wrong data to disk. And debug kernels will assert fail if this occurs. I found lots of niggly little problems trying to fix this issue on a 4k block size filesystem, finally getting it to pass with lots of fixes. The thing is, 1024 byte filesystems still failed, and it was getting really complex handling all the corner cases that were showing up. And there were clearly more that I hadn't found yet. It is complex, fragile code, and if we don't fix it now, it will be complex, fragile code forever more. Hence the simple fix is to add a header to each filesystem block. This gives us the same relationship between the attribute data length and the number of blocks on disk as we have without CRCs - it's a linear mapping and doesn't require us to guess anything. It is simple to implement, too - the remote block count calculated at lookup time can be used by the remote attribute set/get/remove code without modification for both CRC and non-CRC filesystems. The world becomes sane again. Because the copy-in and copy-out now need to iterate over each filesystem block, I moved them into helper functions so we separate the block mapping and buffer manupulations from the attribute data and CRC header manipulations. The code becomes much clearer as a result, and it is a lot easier to understand and debug. It also appears to be much more robust - once it worked on 4k block size filesystems, it has worked without failure on 1k block size filesystems, too. Signed-off-by: Dave Chinner Reviewed-by: Ben Myers Signed-off-by: Ben Myers --- fs/xfs/xfs_attr_leaf.c | 13 +- fs/xfs/xfs_attr_remote.c | 381 ++++++++++++++++++++++++++++------------------- fs/xfs/xfs_attr_remote.h | 10 ++ fs/xfs/xfs_buf.c | 1 + 4 files changed, 247 insertions(+), 158 deletions(-) diff --git a/fs/xfs/xfs_attr_leaf.c b/fs/xfs/xfs_attr_leaf.c index 5b03d15b707b..d788302e506a 100644 --- a/fs/xfs/xfs_attr_leaf.c +++ b/fs/xfs/xfs_attr_leaf.c @@ -1412,7 +1412,7 @@ xfs_attr3_leaf_add_work( name_rmt->valuelen = 0; name_rmt->valueblk = 0; args->rmtblkno = 1; - args->rmtblkcnt = XFS_B_TO_FSB(mp, args->valuelen); + args->rmtblkcnt = xfs_attr3_rmt_blocks(mp, args->valuelen); } xfs_trans_log_buf(args->trans, bp, XFS_DA_LOGRANGE(leaf, xfs_attr3_leaf_name(leaf, args->index), @@ -2354,8 +2354,9 @@ xfs_attr3_leaf_lookup_int( args->index = probe; args->valuelen = be32_to_cpu(name_rmt->valuelen); args->rmtblkno = be32_to_cpu(name_rmt->valueblk); - args->rmtblkcnt = XFS_B_TO_FSB(args->dp->i_mount, - args->valuelen); + args->rmtblkcnt = xfs_attr3_rmt_blocks( + args->dp->i_mount, + args->valuelen); return XFS_ERROR(EEXIST); } } @@ -2406,7 +2407,8 @@ xfs_attr3_leaf_getvalue( ASSERT(memcmp(args->name, name_rmt->name, args->namelen) == 0); valuelen = be32_to_cpu(name_rmt->valuelen); args->rmtblkno = be32_to_cpu(name_rmt->valueblk); - args->rmtblkcnt = XFS_B_TO_FSB(args->dp->i_mount, valuelen); + args->rmtblkcnt = xfs_attr3_rmt_blocks(args->dp->i_mount, + valuelen); if (args->flags & ATTR_KERNOVAL) { args->valuelen = valuelen; return 0; @@ -2732,7 +2734,8 @@ xfs_attr3_leaf_list_int( args.valuelen = valuelen; args.value = kmem_alloc(valuelen, KM_SLEEP | KM_NOFS); args.rmtblkno = be32_to_cpu(name_rmt->valueblk); - args.rmtblkcnt = XFS_B_TO_FSB(args.dp->i_mount, valuelen); + args.rmtblkcnt = xfs_attr3_rmt_blocks( + args.dp->i_mount, valuelen); retval = xfs_attr_rmtval_get(&args); if (retval) return retval; diff --git a/fs/xfs/xfs_attr_remote.c b/fs/xfs/xfs_attr_remote.c index d8bcb2d742d1..ef6b0c124528 100644 --- a/fs/xfs/xfs_attr_remote.c +++ b/fs/xfs/xfs_attr_remote.c @@ -47,7 +47,7 @@ * Each contiguous block has a header, so it is not just a simple attribute * length to FSB conversion. */ -static int +int xfs_attr3_rmt_blocks( struct xfs_mount *mp, int attrlen) @@ -59,12 +59,43 @@ xfs_attr3_rmt_blocks( return XFS_B_TO_FSB(mp, attrlen); } +/* + * Checking of the remote attribute header is split into two parts. The verifier + * does CRC, location and bounds checking, the unpacking function checks the + * attribute parameters and owner. + */ +static bool +xfs_attr3_rmt_hdr_ok( + struct xfs_mount *mp, + void *ptr, + xfs_ino_t ino, + uint32_t offset, + uint32_t size, + xfs_daddr_t bno) +{ + struct xfs_attr3_rmt_hdr *rmt = ptr; + + if (bno != be64_to_cpu(rmt->rm_blkno)) + return false; + if (offset != be32_to_cpu(rmt->rm_offset)) + return false; + if (size != be32_to_cpu(rmt->rm_bytes)) + return false; + if (ino != be64_to_cpu(rmt->rm_owner)) + return false; + + /* ok */ + return true; +} + static bool xfs_attr3_rmt_verify( - struct xfs_buf *bp) + struct xfs_mount *mp, + void *ptr, + int fsbsize, + xfs_daddr_t bno) { - struct xfs_mount *mp = bp->b_target->bt_mount; - struct xfs_attr3_rmt_hdr *rmt = bp->b_addr; + struct xfs_attr3_rmt_hdr *rmt = ptr; if (!xfs_sb_version_hascrc(&mp->m_sb)) return false; @@ -72,7 +103,9 @@ xfs_attr3_rmt_verify( return false; if (!uuid_equal(&rmt->rm_uuid, &mp->m_sb.sb_uuid)) return false; - if (bp->b_bn != be64_to_cpu(rmt->rm_blkno)) + if (be64_to_cpu(rmt->rm_blkno) != bno) + return false; + if (be32_to_cpu(rmt->rm_bytes) > fsbsize - sizeof(*rmt)) return false; if (be32_to_cpu(rmt->rm_offset) + be32_to_cpu(rmt->rm_bytes) >= XATTR_SIZE_MAX) @@ -88,17 +121,40 @@ xfs_attr3_rmt_read_verify( struct xfs_buf *bp) { struct xfs_mount *mp = bp->b_target->bt_mount; + char *ptr; + int len; + bool corrupt = false; + xfs_daddr_t bno; /* no verification of non-crc buffers */ if (!xfs_sb_version_hascrc(&mp->m_sb)) return; - if (!xfs_verify_cksum(bp->b_addr, BBTOB(bp->b_length), - XFS_ATTR3_RMT_CRC_OFF) || - !xfs_attr3_rmt_verify(bp)) { + ptr = bp->b_addr; + bno = bp->b_bn; + len = BBTOB(bp->b_length); + ASSERT(len >= XFS_LBSIZE(mp)); + + while (len > 0) { + if (!xfs_verify_cksum(ptr, XFS_LBSIZE(mp), + XFS_ATTR3_RMT_CRC_OFF)) { + corrupt = true; + break; + } + if (!xfs_attr3_rmt_verify(mp, ptr, XFS_LBSIZE(mp), bno)) { + corrupt = true; + break; + } + len -= XFS_LBSIZE(mp); + ptr += XFS_LBSIZE(mp); + bno += mp->m_bsize; + } + + if (corrupt) { XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr); xfs_buf_ioerror(bp, EFSCORRUPTED); - } + } else + ASSERT(len == 0); } static void @@ -107,23 +163,39 @@ xfs_attr3_rmt_write_verify( { struct xfs_mount *mp = bp->b_target->bt_mount; struct xfs_buf_log_item *bip = bp->b_fspriv; + char *ptr; + int len; + xfs_daddr_t bno; /* no verification of non-crc buffers */ if (!xfs_sb_version_hascrc(&mp->m_sb)) return; - if (!xfs_attr3_rmt_verify(bp)) { - XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr); - xfs_buf_ioerror(bp, EFSCORRUPTED); - return; - } + ptr = bp->b_addr; + bno = bp->b_bn; + len = BBTOB(bp->b_length); + ASSERT(len >= XFS_LBSIZE(mp)); + + while (len > 0) { + if (!xfs_attr3_rmt_verify(mp, ptr, XFS_LBSIZE(mp), bno)) { + XFS_CORRUPTION_ERROR(__func__, + XFS_ERRLEVEL_LOW, mp, bp->b_addr); + xfs_buf_ioerror(bp, EFSCORRUPTED); + return; + } + if (bip) { + struct xfs_attr3_rmt_hdr *rmt; - if (bip) { - struct xfs_attr3_rmt_hdr *rmt = bp->b_addr; - rmt->rm_lsn = cpu_to_be64(bip->bli_item.li_lsn); + rmt = (struct xfs_attr3_rmt_hdr *)ptr; + rmt->rm_lsn = cpu_to_be64(bip->bli_item.li_lsn); + } + xfs_update_cksum(ptr, XFS_LBSIZE(mp), XFS_ATTR3_RMT_CRC_OFF); + + len -= XFS_LBSIZE(mp); + ptr += XFS_LBSIZE(mp); + bno += mp->m_bsize; } - xfs_update_cksum(bp->b_addr, BBTOB(bp->b_length), - XFS_ATTR3_RMT_CRC_OFF); + ASSERT(len == 0); } const struct xfs_buf_ops xfs_attr3_rmt_buf_ops = { @@ -131,15 +203,16 @@ const struct xfs_buf_ops xfs_attr3_rmt_buf_ops = { .verify_write = xfs_attr3_rmt_write_verify, }; -static int +STATIC int xfs_attr3_rmt_hdr_set( struct xfs_mount *mp, + void *ptr, xfs_ino_t ino, uint32_t offset, uint32_t size, - struct xfs_buf *bp) + xfs_daddr_t bno) { - struct xfs_attr3_rmt_hdr *rmt = bp->b_addr; + struct xfs_attr3_rmt_hdr *rmt = ptr; if (!xfs_sb_version_hascrc(&mp->m_sb)) return 0; @@ -149,36 +222,107 @@ xfs_attr3_rmt_hdr_set( rmt->rm_bytes = cpu_to_be32(size); uuid_copy(&rmt->rm_uuid, &mp->m_sb.sb_uuid); rmt->rm_owner = cpu_to_be64(ino); - rmt->rm_blkno = cpu_to_be64(bp->b_bn); - bp->b_ops = &xfs_attr3_rmt_buf_ops; + rmt->rm_blkno = cpu_to_be64(bno); return sizeof(struct xfs_attr3_rmt_hdr); } /* - * Checking of the remote attribute header is split into two parts. the verifier - * does CRC, location and bounds checking, the unpacking function checks the - * attribute parameters and owner. + * Helper functions to copy attribute data in and out of the one disk extents */ -static bool -xfs_attr3_rmt_hdr_ok( - struct xfs_mount *mp, - xfs_ino_t ino, - uint32_t offset, - uint32_t size, - struct xfs_buf *bp) +STATIC int +xfs_attr_rmtval_copyout( + struct xfs_mount *mp, + struct xfs_buf *bp, + xfs_ino_t ino, + int *offset, + int *valuelen, + char **dst) { - struct xfs_attr3_rmt_hdr *rmt = bp->b_addr; + char *src = bp->b_addr; + xfs_daddr_t bno = bp->b_bn; + int len = BBTOB(bp->b_length); - if (offset != be32_to_cpu(rmt->rm_offset)) - return false; - if (size != be32_to_cpu(rmt->rm_bytes)) - return false; - if (ino != be64_to_cpu(rmt->rm_owner)) - return false; + ASSERT(len >= XFS_LBSIZE(mp)); - /* ok */ - return true; + while (len > 0 && *valuelen > 0) { + int hdr_size = 0; + int byte_cnt = XFS_ATTR3_RMT_BUF_SPACE(mp, XFS_LBSIZE(mp)); + + byte_cnt = min_t(int, *valuelen, byte_cnt); + + if (xfs_sb_version_hascrc(&mp->m_sb)) { + if (!xfs_attr3_rmt_hdr_ok(mp, src, ino, *offset, + byte_cnt, bno)) { + xfs_alert(mp, +"remote attribute header mismatch bno/off/len/owner (0x%llx/0x%x/Ox%x/0x%llx)", + bno, *offset, byte_cnt, ino); + return EFSCORRUPTED; + } + hdr_size = sizeof(struct xfs_attr3_rmt_hdr); + } + + memcpy(*dst, src + hdr_size, byte_cnt); + + /* roll buffer forwards */ + len -= XFS_LBSIZE(mp); + src += XFS_LBSIZE(mp); + bno += mp->m_bsize; + + /* roll attribute data forwards */ + *valuelen -= byte_cnt; + *dst += byte_cnt; + *offset += byte_cnt; + } + return 0; +} + +STATIC void +xfs_attr_rmtval_copyin( + struct xfs_mount *mp, + struct xfs_buf *bp, + xfs_ino_t ino, + int *offset, + int *valuelen, + char **src) +{ + char *dst = bp->b_addr; + xfs_daddr_t bno = bp->b_bn; + int len = BBTOB(bp->b_length); + + ASSERT(len >= XFS_LBSIZE(mp)); + + while (len > 0 && *valuelen > 0) { + int hdr_size; + int byte_cnt = XFS_ATTR3_RMT_BUF_SPACE(mp, XFS_LBSIZE(mp)); + + byte_cnt = min(*valuelen, byte_cnt); + hdr_size = xfs_attr3_rmt_hdr_set(mp, dst, ino, *offset, + byte_cnt, bno); + + memcpy(dst + hdr_size, *src, byte_cnt); + + /* + * If this is the last block, zero the remainder of it. + * Check that we are actually the last block, too. + */ + if (byte_cnt + hdr_size < XFS_LBSIZE(mp)) { + ASSERT(*valuelen - byte_cnt == 0); + ASSERT(len == XFS_LBSIZE(mp)); + memset(dst + hdr_size + byte_cnt, 0, + XFS_LBSIZE(mp) - hdr_size - byte_cnt); + } + + /* roll buffer forwards */ + len -= XFS_LBSIZE(mp); + dst += XFS_LBSIZE(mp); + bno += mp->m_bsize; + + /* roll attribute data forwards */ + *valuelen -= byte_cnt; + *src += byte_cnt; + *offset += byte_cnt; + } } /* @@ -192,13 +336,12 @@ xfs_attr_rmtval_get( struct xfs_bmbt_irec map[ATTR_RMTVALUE_MAPSIZE]; struct xfs_mount *mp = args->dp->i_mount; struct xfs_buf *bp; - xfs_daddr_t dblkno; xfs_dablk_t lblkno = args->rmtblkno; - void *dst = args->value; + char *dst = args->value; int valuelen = args->valuelen; int nmap; int error; - int blkcnt; + int blkcnt = args->rmtblkcnt; int i; int offset = 0; @@ -208,7 +351,6 @@ xfs_attr_rmtval_get( while (valuelen > 0) { nmap = ATTR_RMTVALUE_MAPSIZE; - blkcnt = xfs_attr3_rmt_blocks(mp, valuelen); error = xfs_bmapi_read(args->dp, (xfs_fileoff_t)lblkno, blkcnt, map, &nmap, XFS_BMAPI_ATTRFORK); @@ -217,45 +359,29 @@ xfs_attr_rmtval_get( ASSERT(nmap >= 1); for (i = 0; (i < nmap) && (valuelen > 0); i++) { - int byte_cnt; - char *src; + xfs_daddr_t dblkno; + int dblkcnt; ASSERT((map[i].br_startblock != DELAYSTARTBLOCK) && (map[i].br_startblock != HOLESTARTBLOCK)); dblkno = XFS_FSB_TO_DADDR(mp, map[i].br_startblock); - blkcnt = XFS_FSB_TO_BB(mp, map[i].br_blockcount); + dblkcnt = XFS_FSB_TO_BB(mp, map[i].br_blockcount); error = xfs_trans_read_buf(mp, NULL, mp->m_ddev_targp, - dblkno, blkcnt, 0, &bp, + dblkno, dblkcnt, 0, &bp, &xfs_attr3_rmt_buf_ops); if (error) return error; - byte_cnt = XFS_ATTR3_RMT_BUF_SPACE(mp, BBTOB(bp->b_length)); - byte_cnt = min_t(int, valuelen, byte_cnt); - - src = bp->b_addr; - if (xfs_sb_version_hascrc(&mp->m_sb)) { - if (!xfs_attr3_rmt_hdr_ok(mp, args->dp->i_ino, - offset, byte_cnt, bp)) { - xfs_alert(mp, -"remote attribute header does not match required off/len/owner (0x%x/Ox%x,0x%llx)", - offset, byte_cnt, args->dp->i_ino); - xfs_buf_relse(bp); - return EFSCORRUPTED; - - } - - src += sizeof(struct xfs_attr3_rmt_hdr); - } - - memcpy(dst, src, byte_cnt); + error = xfs_attr_rmtval_copyout(mp, bp, args->dp->i_ino, + &offset, &valuelen, + &dst); xfs_buf_relse(bp); + if (error) + return error; - offset += byte_cnt; - dst += byte_cnt; - valuelen -= byte_cnt; - + /* roll attribute extent map forwards */ lblkno += map[i].br_blockcount; + blkcnt -= map[i].br_blockcount; } } ASSERT(valuelen == 0); @@ -273,17 +399,13 @@ xfs_attr_rmtval_set( struct xfs_inode *dp = args->dp; struct xfs_mount *mp = dp->i_mount; struct xfs_bmbt_irec map; - struct xfs_buf *bp; - xfs_daddr_t dblkno; xfs_dablk_t lblkno; xfs_fileoff_t lfileoff = 0; - void *src = args->value; + char *src = args->value; int blkcnt; int valuelen; int nmap; int error; - int hdrcnt = 0; - bool crcs = xfs_sb_version_hascrc(&mp->m_sb); int offset = 0; trace_xfs_attr_rmtval_set(args); @@ -292,21 +414,14 @@ xfs_attr_rmtval_set( * Find a "hole" in the attribute address space large enough for * us to drop the new attribute's value into. Because CRC enable * attributes have headers, we can't just do a straight byte to FSB - * conversion. We calculate the worst case block count in this case - * and we may not need that many, so we have to handle this when - * allocating the blocks below. + * conversion and have to take the header space into account. */ blkcnt = xfs_attr3_rmt_blocks(mp, args->valuelen); - error = xfs_bmap_first_unused(args->trans, args->dp, blkcnt, &lfileoff, XFS_ATTR_FORK); if (error) return error; - /* Start with the attribute data. We'll allocate the rest afterwards. */ - if (crcs) - blkcnt = XFS_B_TO_FSB(mp, args->valuelen); - args->rmtblkno = lblkno = (xfs_dablk_t)lfileoff; args->rmtblkcnt = blkcnt; @@ -349,31 +464,6 @@ xfs_attr_rmtval_set( (map.br_startblock != HOLESTARTBLOCK)); lblkno += map.br_blockcount; blkcnt -= map.br_blockcount; - hdrcnt++; - - /* - * If we have enough blocks for the attribute data, calculate - * how many extra blocks we need for headers. We might run - * through this multiple times in the case that the additional - * headers in the blocks needed for the data fragments spills - * into requiring more blocks. e.g. for 512 byte blocks, we'll - * spill for another block every 9 headers we require in this - * loop. - * - * Note that this can result in contiguous allocation of blocks, - * so we don't use all the space we allocate for headers as we - * have one less header for each contiguous allocation that - * occurs in the map/write loop below. - */ - if (crcs && blkcnt == 0) { - int total_len; - - total_len = args->valuelen + - hdrcnt * sizeof(struct xfs_attr3_rmt_hdr); - blkcnt = XFS_B_TO_FSB(mp, total_len); - blkcnt -= args->rmtblkcnt; - args->rmtblkcnt += blkcnt; - } /* * Start the next trans in the chain. @@ -390,17 +480,15 @@ xfs_attr_rmtval_set( * the INCOMPLETE flag. */ lblkno = args->rmtblkno; - valuelen = args->valuelen; blkcnt = args->rmtblkcnt; + valuelen = args->valuelen; while (valuelen > 0) { - int byte_cnt; - int hdr_size; - int dblkcnt; - char *buf; + struct xfs_buf *bp; + xfs_daddr_t dblkno; + int dblkcnt; + + ASSERT(blkcnt > 0); - /* - * Try to remember where we decided to put the value. - */ xfs_bmap_init(args->flist, args->firstblock); nmap = 1; error = xfs_bmapi_read(dp, (xfs_fileoff_t)lblkno, @@ -419,29 +507,17 @@ xfs_attr_rmtval_set( if (!bp) return ENOMEM; bp->b_ops = &xfs_attr3_rmt_buf_ops; - buf = bp->b_addr; - - byte_cnt = XFS_ATTR3_RMT_BUF_SPACE(mp, BBTOB(bp->b_length)); - byte_cnt = min_t(int, valuelen, byte_cnt); - hdr_size = xfs_attr3_rmt_hdr_set(mp, dp->i_ino, offset, - byte_cnt, bp); - ASSERT(hdr_size + byte_cnt <= BBTOB(bp->b_length)); - memcpy(buf + hdr_size, src, byte_cnt); - - if (byte_cnt + hdr_size < BBTOB(bp->b_length)) - xfs_buf_zero(bp, byte_cnt + hdr_size, - BBTOB(bp->b_length) - byte_cnt - hdr_size); + xfs_attr_rmtval_copyin(mp, bp, args->dp->i_ino, &offset, + &valuelen, &src); error = xfs_bwrite(bp); /* GROT: NOTE: synchronous write */ xfs_buf_relse(bp); if (error) return error; - src += byte_cnt; - valuelen -= byte_cnt; - offset += byte_cnt; + /* roll attribute extent map forwards */ lblkno += map.br_blockcount; blkcnt -= map.br_blockcount; } @@ -454,19 +530,17 @@ xfs_attr_rmtval_set( * out-of-line buffer that it is stored on. */ int -xfs_attr_rmtval_remove(xfs_da_args_t *args) +xfs_attr_rmtval_remove( + struct xfs_da_args *args) { - xfs_mount_t *mp; - xfs_bmbt_irec_t map; - xfs_buf_t *bp; - xfs_daddr_t dblkno; - xfs_dablk_t lblkno; - int valuelen, blkcnt, nmap, error, done, committed; + struct xfs_mount *mp = args->dp->i_mount; + xfs_dablk_t lblkno; + int blkcnt; + int error; + int done; trace_xfs_attr_rmtval_remove(args); - mp = args->dp->i_mount; - /* * Roll through the "value", invalidating the attribute value's blocks. * Note that args->rmtblkcnt is the minimum number of data blocks we'll @@ -476,10 +550,13 @@ xfs_attr_rmtval_remove(xfs_da_args_t *args) * lookups. */ lblkno = args->rmtblkno; - valuelen = args->valuelen; - blkcnt = xfs_attr3_rmt_blocks(mp, valuelen); - while (valuelen > 0) { - int dblkcnt; + blkcnt = args->rmtblkcnt; + while (blkcnt > 0) { + struct xfs_bmbt_irec map; + struct xfs_buf *bp; + xfs_daddr_t dblkno; + int dblkcnt; + int nmap; /* * Try to remember where we decided to put the value. @@ -506,21 +583,19 @@ xfs_attr_rmtval_remove(xfs_da_args_t *args) bp = NULL; } - valuelen -= XFS_ATTR3_RMT_BUF_SPACE(mp, - XFS_FSB_TO_B(mp, map.br_blockcount)); - lblkno += map.br_blockcount; blkcnt -= map.br_blockcount; - blkcnt = max(blkcnt, xfs_attr3_rmt_blocks(mp, valuelen)); } /* * Keep de-allocating extents until the remote-value region is gone. */ - blkcnt = lblkno - args->rmtblkno; lblkno = args->rmtblkno; + blkcnt = args->rmtblkcnt; done = 0; while (!done) { + int committed; + xfs_bmap_init(args->flist, args->firstblock); error = xfs_bunmapi(args->trans, args->dp, lblkno, blkcnt, XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA, diff --git a/fs/xfs/xfs_attr_remote.h b/fs/xfs/xfs_attr_remote.h index c7cca60a062a..92a8fd7977cc 100644 --- a/fs/xfs/xfs_attr_remote.h +++ b/fs/xfs/xfs_attr_remote.h @@ -20,6 +20,14 @@ #define XFS_ATTR3_RMT_MAGIC 0x5841524d /* XARM */ +/* + * There is one of these headers per filesystem block in a remote attribute. + * This is done to ensure there is a 1:1 mapping between the attribute value + * length and the number of blocks needed to store the attribute. This makes the + * verification of a buffer a little more complex, but greatly simplifies the + * allocation, reading and writing of these attributes as we don't have to guess + * the number of blocks needed to store the attribute data. + */ struct xfs_attr3_rmt_hdr { __be32 rm_magic; __be32 rm_offset; @@ -39,6 +47,8 @@ struct xfs_attr3_rmt_hdr { extern const struct xfs_buf_ops xfs_attr3_rmt_buf_ops; +int xfs_attr3_rmt_blocks(struct xfs_mount *mp, int attrlen); + int xfs_attr_rmtval_get(struct xfs_da_args *args); int xfs_attr_rmtval_set(struct xfs_da_args *args); int xfs_attr_rmtval_remove(struct xfs_da_args *args); diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c index 0d2554299688..1b2472a46e46 100644 --- a/fs/xfs/xfs_buf.c +++ b/fs/xfs/xfs_buf.c @@ -513,6 +513,7 @@ _xfs_buf_find( xfs_alert(btp->bt_mount, "%s: Block out of range: block 0x%llx, EOFS 0x%llx ", __func__, blkno, eofs); + WARN_ON(1); return NULL; } -- cgit v1.2.3 From 4c8a9d4bfaf7dbc7d2168494904d79d22cc01db7 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 24 May 2013 01:06:09 +0200 Subject: mac80211: close AP_VLAN interfaces before unregistering all Since Eric's commit efe117ab8 ("Speedup ieee80211_remove_interfaces") there's a bug in mac80211 when it unregisters with AP_VLAN interfaces up. If the AP_VLAN interface was registered after the AP it belongs to (which is the typical case) and then we get into this code path, unregister_netdevice_many() will crash because it isn't prepared to deal with interfaces being closed in the middle of it. Exactly this happens though, because we iterate the list, find the AP master this AP_VLAN belongs to and dev_close() the dependent VLANs. After this, unregister_netdevice_many() won't pick up the fact that the AP_VLAN is already down and will do it again, causing a crash. Cc: stable@vger.kernel.org [2.6.33+] Cc: Eric Dumazet Signed-off-by: Johannes Berg --- net/mac80211/iface.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 00e2238355f0..ceef64426a8d 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1703,6 +1703,15 @@ void ieee80211_remove_interfaces(struct ieee80211_local *local) ASSERT_RTNL(); + /* + * Close all AP_VLAN interfaces first, as otherwise they + * might be closed while the AP interface they belong to + * is closed, causing unregister_netdevice_many() to crash. + */ + list_for_each_entry(sdata, &local->interfaces, list) + if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) + dev_close(sdata->dev); + mutex_lock(&local->iflist_mtx); list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) { list_del(&sdata->list); -- cgit v1.2.3 From d89995db5f238e618389604b848b431da240eb69 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 23 May 2013 19:41:21 +0900 Subject: ata: use platform_{get,set}_drvdata() Use the wrapper functions for getting and setting the driver data using platform_device instead of using dev_{get,set}_drvdata() with &pdev->dev, so we can directly pass a struct platform_device. Also, unnecessary dev_set_drvdata() is removed, because the driver core clears the driver data to NULL after device_release or on probe failure. Signed-off-by: Jingoo Han Signed-off-by: Tejun Heo --- drivers/ata/pata_arasan_cf.c | 2 +- drivers/ata/pata_at91.c | 2 +- drivers/ata/pata_bf54x.c | 10 ++++------ drivers/ata/pata_imx.c | 2 +- drivers/ata/pata_mpc52xx.c | 4 ++-- drivers/ata/pata_pxa.c | 2 +- drivers/ata/sata_fsl.c | 14 +++++--------- drivers/ata/sata_rcar.c | 2 +- 8 files changed, 16 insertions(+), 22 deletions(-) diff --git a/drivers/ata/pata_arasan_cf.c b/drivers/ata/pata_arasan_cf.c index 7638121cb5d1..848ed3254ddd 100644 --- a/drivers/ata/pata_arasan_cf.c +++ b/drivers/ata/pata_arasan_cf.c @@ -908,7 +908,7 @@ free_clk: static int arasan_cf_remove(struct platform_device *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = platform_get_drvdata(pdev); struct arasan_cf_dev *acdev = host->ports[0]->private_data; ata_host_detach(host); diff --git a/drivers/ata/pata_at91.c b/drivers/ata/pata_at91.c index 033f3f4c20ad..5364f97b42c6 100644 --- a/drivers/ata/pata_at91.c +++ b/drivers/ata/pata_at91.c @@ -422,7 +422,7 @@ err_put: static int pata_at91_remove(struct platform_device *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = platform_get_drvdata(pdev); struct at91_ide_info *info; if (!host) diff --git a/drivers/ata/pata_bf54x.c b/drivers/ata/pata_bf54x.c index 8d43510c6bec..ba0d8a29dc23 100644 --- a/drivers/ata/pata_bf54x.c +++ b/drivers/ata/pata_bf54x.c @@ -1596,7 +1596,7 @@ static int bfin_atapi_probe(struct platform_device *pdev) return -ENODEV; } - dev_set_drvdata(&pdev->dev, host); + platform_set_drvdata(pdev, host); return 0; } @@ -1610,11 +1610,9 @@ static int bfin_atapi_probe(struct platform_device *pdev) */ static int bfin_atapi_remove(struct platform_device *pdev) { - struct device *dev = &pdev->dev; - struct ata_host *host = dev_get_drvdata(dev); + struct ata_host *host = platform_get_drvdata(pdev); ata_host_detach(host); - dev_set_drvdata(&pdev->dev, NULL); peripheral_free_list(atapi_io_port); @@ -1624,7 +1622,7 @@ static int bfin_atapi_remove(struct platform_device *pdev) #ifdef CONFIG_PM static int bfin_atapi_suspend(struct platform_device *pdev, pm_message_t state) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = platform_get_drvdata(pdev); if (host) return ata_host_suspend(host, state); else @@ -1633,7 +1631,7 @@ static int bfin_atapi_suspend(struct platform_device *pdev, pm_message_t state) static int bfin_atapi_resume(struct platform_device *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = platform_get_drvdata(pdev); int ret; if (host) { diff --git a/drivers/ata/pata_imx.c b/drivers/ata/pata_imx.c index aa3d166e02eb..4ec7c04b3f82 100644 --- a/drivers/ata/pata_imx.c +++ b/drivers/ata/pata_imx.c @@ -177,7 +177,7 @@ err: static int pata_imx_remove(struct platform_device *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = platform_get_drvdata(pdev); struct pata_imx_priv *priv = host->private_data; ata_host_detach(host); diff --git a/drivers/ata/pata_mpc52xx.c b/drivers/ata/pata_mpc52xx.c index 3a8fb28b71f2..0024ced3e200 100644 --- a/drivers/ata/pata_mpc52xx.c +++ b/drivers/ata/pata_mpc52xx.c @@ -825,7 +825,7 @@ mpc52xx_ata_remove(struct platform_device *op) static int mpc52xx_ata_suspend(struct platform_device *op, pm_message_t state) { - struct ata_host *host = dev_get_drvdata(&op->dev); + struct ata_host *host = platform_get_drvdata(op); return ata_host_suspend(host, state); } @@ -833,7 +833,7 @@ mpc52xx_ata_suspend(struct platform_device *op, pm_message_t state) static int mpc52xx_ata_resume(struct platform_device *op) { - struct ata_host *host = dev_get_drvdata(&op->dev); + struct ata_host *host = platform_get_drvdata(op); struct mpc52xx_ata_priv *priv = host->private_data; int rv; diff --git a/drivers/ata/pata_pxa.c b/drivers/ata/pata_pxa.c index b0ac9e0c5e01..942ef94b29e6 100644 --- a/drivers/ata/pata_pxa.c +++ b/drivers/ata/pata_pxa.c @@ -371,7 +371,7 @@ static int pxa_ata_probe(struct platform_device *pdev) static int pxa_ata_remove(struct platform_device *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = platform_get_drvdata(pdev); struct pata_pxa_data *data = host->ports[0]->private_data; pxa_free_dma(data->dma_channel); diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c index d40e403e82dd..19720a0a4a65 100644 --- a/drivers/ata/sata_fsl.c +++ b/drivers/ata/sata_fsl.c @@ -1532,7 +1532,7 @@ static int sata_fsl_probe(struct platform_device *ofdev) ata_host_activate(host, irq, sata_fsl_interrupt, SATA_FSL_IRQ_FLAG, &sata_fsl_sht); - dev_set_drvdata(&ofdev->dev, host); + platform_set_drvdata(ofdev, host); host_priv->intr_coalescing.show = fsl_sata_intr_coalescing_show; host_priv->intr_coalescing.store = fsl_sata_intr_coalescing_store; @@ -1558,10 +1558,8 @@ static int sata_fsl_probe(struct platform_device *ofdev) error_exit_with_cleanup: - if (host) { - dev_set_drvdata(&ofdev->dev, NULL); + if (host) ata_host_detach(host); - } if (hcr_base) iounmap(hcr_base); @@ -1572,7 +1570,7 @@ error_exit_with_cleanup: static int sata_fsl_remove(struct platform_device *ofdev) { - struct ata_host *host = dev_get_drvdata(&ofdev->dev); + struct ata_host *host = platform_get_drvdata(ofdev); struct sata_fsl_host_priv *host_priv = host->private_data; device_remove_file(&ofdev->dev, &host_priv->intr_coalescing); @@ -1580,8 +1578,6 @@ static int sata_fsl_remove(struct platform_device *ofdev) ata_host_detach(host); - dev_set_drvdata(&ofdev->dev, NULL); - irq_dispose_mapping(host_priv->irq); iounmap(host_priv->hcr_base); kfree(host_priv); @@ -1592,13 +1588,13 @@ static int sata_fsl_remove(struct platform_device *ofdev) #ifdef CONFIG_PM static int sata_fsl_suspend(struct platform_device *op, pm_message_t state) { - struct ata_host *host = dev_get_drvdata(&op->dev); + struct ata_host *host = platform_get_drvdata(op); return ata_host_suspend(host, state); } static int sata_fsl_resume(struct platform_device *op) { - struct ata_host *host = dev_get_drvdata(&op->dev); + struct ata_host *host = platform_get_drvdata(op); struct sata_fsl_host_priv *host_priv = host->private_data; int ret; void __iomem *hcr_base = host_priv->hcr_base; diff --git a/drivers/ata/sata_rcar.c b/drivers/ata/sata_rcar.c index 4799868bd733..889c25a6337e 100644 --- a/drivers/ata/sata_rcar.c +++ b/drivers/ata/sata_rcar.c @@ -825,7 +825,7 @@ cleanup: static int sata_rcar_remove(struct platform_device *pdev) { - struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct ata_host *host = platform_get_drvdata(pdev); struct sata_rcar_priv *priv = host->private_data; ata_host_detach(host); -- cgit v1.2.3 From e1f00a69ec26e3eb9847c61c665b8fb3f0c6b477 Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Wed, 22 May 2013 06:34:45 +0000 Subject: xen-netback: split event channels support for Xen backend driver Netback and netfront only use one event channel to do TX / RX notification, which may cause unnecessary wake-up of processing routines. This patch adds a new feature called feature-split-event-channels to netback, enabling it to handle TX and RX events separately. Netback will use tx_irq to notify guest for TX completion, rx_irq for RX notification. If frontend doesn't support this feature, tx_irq equals to rx_irq. Signed-off-by: Wei Liu Signed-off-by: David S. Miller --- drivers/net/xen-netback/common.h | 13 ++++-- drivers/net/xen-netback/interface.c | 85 ++++++++++++++++++++++++++++++------- drivers/net/xen-netback/netback.c | 14 ++++-- drivers/net/xen-netback/xenbus.c | 41 +++++++++++++++--- 4 files changed, 124 insertions(+), 29 deletions(-) diff --git a/drivers/net/xen-netback/common.h b/drivers/net/xen-netback/common.h index 6102a6c59f4e..8a4d77ee9c5b 100644 --- a/drivers/net/xen-netback/common.h +++ b/drivers/net/xen-netback/common.h @@ -57,8 +57,12 @@ struct xenvif { u8 fe_dev_addr[6]; - /* Physical parameters of the comms window. */ - unsigned int irq; + /* When feature-split-event-channels = 0, tx_irq = rx_irq. */ + unsigned int tx_irq; + unsigned int rx_irq; + /* Only used when feature-split-event-channels = 1 */ + char tx_irq_name[IFNAMSIZ+4]; /* DEVNAME-tx */ + char rx_irq_name[IFNAMSIZ+4]; /* DEVNAME-rx */ /* List of frontends to notify after a batch of frames sent. */ struct list_head notify_list; @@ -113,7 +117,8 @@ struct xenvif *xenvif_alloc(struct device *parent, unsigned int handle); int xenvif_connect(struct xenvif *vif, unsigned long tx_ring_ref, - unsigned long rx_ring_ref, unsigned int evtchn); + unsigned long rx_ring_ref, unsigned int tx_evtchn, + unsigned int rx_evtchn); void xenvif_disconnect(struct xenvif *vif); void xenvif_get(struct xenvif *vif); @@ -158,4 +163,6 @@ void xenvif_carrier_off(struct xenvif *vif); /* Returns number of ring slots required to send an skb to the frontend */ unsigned int xen_netbk_count_skb_slots(struct xenvif *vif, struct sk_buff *skb); +extern bool separate_tx_rx_irq; + #endif /* __XEN_NETBACK__COMMON_H__ */ diff --git a/drivers/net/xen-netback/interface.c b/drivers/net/xen-netback/interface.c index 82202c2b1bd1..087d2db0389d 100644 --- a/drivers/net/xen-netback/interface.c +++ b/drivers/net/xen-netback/interface.c @@ -60,21 +60,39 @@ static int xenvif_rx_schedulable(struct xenvif *vif) return xenvif_schedulable(vif) && !xen_netbk_rx_ring_full(vif); } -static irqreturn_t xenvif_interrupt(int irq, void *dev_id) +static irqreturn_t xenvif_tx_interrupt(int irq, void *dev_id) { struct xenvif *vif = dev_id; if (vif->netbk == NULL) - return IRQ_NONE; + return IRQ_HANDLED; xen_netbk_schedule_xenvif(vif); + return IRQ_HANDLED; +} + +static irqreturn_t xenvif_rx_interrupt(int irq, void *dev_id) +{ + struct xenvif *vif = dev_id; + + if (vif->netbk == NULL) + return IRQ_HANDLED; + if (xenvif_rx_schedulable(vif)) netif_wake_queue(vif->dev); return IRQ_HANDLED; } +static irqreturn_t xenvif_interrupt(int irq, void *dev_id) +{ + xenvif_tx_interrupt(irq, dev_id); + xenvif_rx_interrupt(irq, dev_id); + + return IRQ_HANDLED; +} + static int xenvif_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct xenvif *vif = netdev_priv(dev); @@ -125,13 +143,17 @@ static struct net_device_stats *xenvif_get_stats(struct net_device *dev) static void xenvif_up(struct xenvif *vif) { xen_netbk_add_xenvif(vif); - enable_irq(vif->irq); + enable_irq(vif->tx_irq); + if (vif->tx_irq != vif->rx_irq) + enable_irq(vif->rx_irq); xen_netbk_check_rx_xenvif(vif); } static void xenvif_down(struct xenvif *vif) { - disable_irq(vif->irq); + disable_irq(vif->tx_irq); + if (vif->tx_irq != vif->rx_irq) + disable_irq(vif->rx_irq); del_timer_sync(&vif->credit_timeout); xen_netbk_deschedule_xenvif(vif); xen_netbk_remove_xenvif(vif); @@ -308,12 +330,13 @@ struct xenvif *xenvif_alloc(struct device *parent, domid_t domid, } int xenvif_connect(struct xenvif *vif, unsigned long tx_ring_ref, - unsigned long rx_ring_ref, unsigned int evtchn) + unsigned long rx_ring_ref, unsigned int tx_evtchn, + unsigned int rx_evtchn) { int err = -ENOMEM; /* Already connected through? */ - if (vif->irq) + if (vif->tx_irq) return 0; __module_get(THIS_MODULE); @@ -322,13 +345,37 @@ int xenvif_connect(struct xenvif *vif, unsigned long tx_ring_ref, if (err < 0) goto err; - err = bind_interdomain_evtchn_to_irqhandler( - vif->domid, evtchn, xenvif_interrupt, 0, - vif->dev->name, vif); - if (err < 0) - goto err_unmap; - vif->irq = err; - disable_irq(vif->irq); + if (tx_evtchn == rx_evtchn) { + /* feature-split-event-channels == 0 */ + err = bind_interdomain_evtchn_to_irqhandler( + vif->domid, tx_evtchn, xenvif_interrupt, 0, + vif->dev->name, vif); + if (err < 0) + goto err_unmap; + vif->tx_irq = vif->rx_irq = err; + disable_irq(vif->tx_irq); + } else { + /* feature-split-event-channels == 1 */ + snprintf(vif->tx_irq_name, sizeof(vif->tx_irq_name), + "%s-tx", vif->dev->name); + err = bind_interdomain_evtchn_to_irqhandler( + vif->domid, tx_evtchn, xenvif_tx_interrupt, 0, + vif->tx_irq_name, vif); + if (err < 0) + goto err_unmap; + vif->tx_irq = err; + disable_irq(vif->tx_irq); + + snprintf(vif->rx_irq_name, sizeof(vif->rx_irq_name), + "%s-rx", vif->dev->name); + err = bind_interdomain_evtchn_to_irqhandler( + vif->domid, rx_evtchn, xenvif_rx_interrupt, 0, + vif->rx_irq_name, vif); + if (err < 0) + goto err_tx_unbind; + vif->rx_irq = err; + disable_irq(vif->rx_irq); + } xenvif_get(vif); @@ -342,6 +389,9 @@ int xenvif_connect(struct xenvif *vif, unsigned long tx_ring_ref, rtnl_unlock(); return 0; +err_tx_unbind: + unbind_from_irqhandler(vif->tx_irq, vif); + vif->tx_irq = 0; err_unmap: xen_netbk_unmap_frontend_rings(vif); err: @@ -375,8 +425,13 @@ void xenvif_disconnect(struct xenvif *vif) atomic_dec(&vif->refcnt); wait_event(vif->waiting_to_free, atomic_read(&vif->refcnt) == 0); - if (vif->irq) { - unbind_from_irqhandler(vif->irq, vif); + if (vif->tx_irq) { + if (vif->tx_irq == vif->rx_irq) + unbind_from_irqhandler(vif->tx_irq, vif); + else { + unbind_from_irqhandler(vif->tx_irq, vif); + unbind_from_irqhandler(vif->rx_irq, vif); + } /* vif->irq is valid, we had a module_get in * xenvif_connect. */ diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c index 2d9477fd900f..82576fffb452 100644 --- a/drivers/net/xen-netback/netback.c +++ b/drivers/net/xen-netback/netback.c @@ -47,6 +47,13 @@ #include #include +/* Provide an option to disable split event channels at load time as + * event channels are limited resource. Split event channels are + * enabled by default. + */ +bool separate_tx_rx_irq = 1; +module_param(separate_tx_rx_irq, bool, 0644); + /* * This is the maximum slots a skb can have. If a guest sends a skb * which exceeds this limit it is considered malicious. @@ -662,7 +669,7 @@ static void xen_netbk_rx_action(struct xen_netbk *netbk) { struct xenvif *vif = NULL, *tmp; s8 status; - u16 irq, flags; + u16 flags; struct xen_netif_rx_response *resp; struct sk_buff_head rxq; struct sk_buff *skb; @@ -771,7 +778,6 @@ static void xen_netbk_rx_action(struct xen_netbk *netbk) sco->meta_slots_used); RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&vif->rx, ret); - irq = vif->irq; if (ret && list_empty(&vif->notify_list)) list_add_tail(&vif->notify_list, ¬ify); @@ -783,7 +789,7 @@ static void xen_netbk_rx_action(struct xen_netbk *netbk) } list_for_each_entry_safe(vif, tmp, ¬ify, notify_list) { - notify_remote_via_irq(vif->irq); + notify_remote_via_irq(vif->rx_irq); list_del_init(&vif->notify_list); } @@ -1762,7 +1768,7 @@ static void make_tx_response(struct xenvif *vif, vif->tx.rsp_prod_pvt = ++i; RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&vif->tx, notify); if (notify) - notify_remote_via_irq(vif->irq); + notify_remote_via_irq(vif->tx_irq); } static struct xen_netif_rx_response *make_rx_response(struct xenvif *vif, diff --git a/drivers/net/xen-netback/xenbus.c b/drivers/net/xen-netback/xenbus.c index d230c145660c..04bd860d16a9 100644 --- a/drivers/net/xen-netback/xenbus.c +++ b/drivers/net/xen-netback/xenbus.c @@ -122,6 +122,16 @@ static int netback_probe(struct xenbus_device *dev, goto fail; } + /* + * Split event channels support, this is optional so it is not + * put inside the above loop. + */ + err = xenbus_printf(XBT_NIL, dev->nodename, + "feature-split-event-channels", + "%u", separate_tx_rx_irq); + if (err) + pr_debug("Error writing feature-split-event-channels"); + err = xenbus_switch_state(dev, XenbusStateInitWait); if (err) goto fail; @@ -393,21 +403,36 @@ static int connect_rings(struct backend_info *be) struct xenvif *vif = be->vif; struct xenbus_device *dev = be->dev; unsigned long tx_ring_ref, rx_ring_ref; - unsigned int evtchn, rx_copy; + unsigned int tx_evtchn, rx_evtchn, rx_copy; int err; int val; err = xenbus_gather(XBT_NIL, dev->otherend, "tx-ring-ref", "%lu", &tx_ring_ref, - "rx-ring-ref", "%lu", &rx_ring_ref, - "event-channel", "%u", &evtchn, NULL); + "rx-ring-ref", "%lu", &rx_ring_ref, NULL); if (err) { xenbus_dev_fatal(dev, err, - "reading %s/ring-ref and event-channel", + "reading %s/ring-ref", dev->otherend); return err; } + /* Try split event channels first, then single event channel. */ + err = xenbus_gather(XBT_NIL, dev->otherend, + "event-channel-tx", "%u", &tx_evtchn, + "event-channel-rx", "%u", &rx_evtchn, NULL); + if (err < 0) { + err = xenbus_scanf(XBT_NIL, dev->otherend, + "event-channel", "%u", &tx_evtchn); + if (err < 0) { + xenbus_dev_fatal(dev, err, + "reading %s/event-channel(-tx/rx)", + dev->otherend); + return err; + } + rx_evtchn = tx_evtchn; + } + err = xenbus_scanf(XBT_NIL, dev->otherend, "request-rx-copy", "%u", &rx_copy); if (err == -ENOENT) { @@ -454,11 +479,13 @@ static int connect_rings(struct backend_info *be) vif->csum = !val; /* Map the shared frame, irq etc. */ - err = xenvif_connect(vif, tx_ring_ref, rx_ring_ref, evtchn); + err = xenvif_connect(vif, tx_ring_ref, rx_ring_ref, + tx_evtchn, rx_evtchn); if (err) { xenbus_dev_fatal(dev, err, - "mapping shared-frames %lu/%lu port %u", - tx_ring_ref, rx_ring_ref, evtchn); + "mapping shared-frames %lu/%lu port tx %u rx %u", + tx_ring_ref, rx_ring_ref, + tx_evtchn, rx_evtchn); return err; } return 0; -- cgit v1.2.3 From d634bf2cb96639b7eddb646dc1fc17b1bc6e567f Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Wed, 22 May 2013 06:34:46 +0000 Subject: xen-netfront: split event channels support for Xen frontend driver This patch adds a new feature called feature-split-event-channels for netfront, enabling it to handle TX and RX events separately. If netback does not support this feature, it falls back to use single event channel. If netfront fails to setup split event channels, it will try falling back to single event channel. Signed-off-by: Wei Liu Reviewed-by: David Vrabel Signed-off-by: David S. Miller --- drivers/net/xen-netfront.c | 178 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 149 insertions(+), 29 deletions(-) diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index 5770e3b0ba73..62238a08cb51 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -85,7 +85,15 @@ struct netfront_info { struct napi_struct napi; - unsigned int evtchn; + /* Split event channels support, tx_* == rx_* when using + * single event channel. + */ + unsigned int tx_evtchn, rx_evtchn; + unsigned int tx_irq, rx_irq; + /* Only used when split event channels support is enabled */ + char tx_irq_name[IFNAMSIZ+4]; /* DEVNAME-tx */ + char rx_irq_name[IFNAMSIZ+4]; /* DEVNAME-rx */ + struct xenbus_device *xbdev; spinlock_t tx_lock; @@ -330,7 +338,7 @@ no_skb: push: RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&np->rx, notify); if (notify) - notify_remote_via_irq(np->netdev->irq); + notify_remote_via_irq(np->rx_irq); } static int xennet_open(struct net_device *dev) @@ -623,7 +631,7 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev) RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&np->tx, notify); if (notify) - notify_remote_via_irq(np->netdev->irq); + notify_remote_via_irq(np->tx_irq); u64_stats_update_begin(&stats->syncp); stats->tx_bytes += skb->len; @@ -1254,23 +1262,35 @@ static int xennet_set_features(struct net_device *dev, return 0; } -static irqreturn_t xennet_interrupt(int irq, void *dev_id) +static irqreturn_t xennet_tx_interrupt(int irq, void *dev_id) { - struct net_device *dev = dev_id; - struct netfront_info *np = netdev_priv(dev); + struct netfront_info *np = dev_id; + struct net_device *dev = np->netdev; unsigned long flags; spin_lock_irqsave(&np->tx_lock, flags); + xennet_tx_buf_gc(dev); + spin_unlock_irqrestore(&np->tx_lock, flags); - if (likely(netif_carrier_ok(dev))) { - xennet_tx_buf_gc(dev); - /* Under tx_lock: protects access to rx shared-ring indexes. */ - if (RING_HAS_UNCONSUMED_RESPONSES(&np->rx)) + return IRQ_HANDLED; +} + +static irqreturn_t xennet_rx_interrupt(int irq, void *dev_id) +{ + struct netfront_info *np = dev_id; + struct net_device *dev = np->netdev; + + if (likely(netif_carrier_ok(dev) && + RING_HAS_UNCONSUMED_RESPONSES(&np->rx))) napi_schedule(&np->napi); - } - spin_unlock_irqrestore(&np->tx_lock, flags); + return IRQ_HANDLED; +} +static irqreturn_t xennet_interrupt(int irq, void *dev_id) +{ + xennet_tx_interrupt(irq, dev_id); + xennet_rx_interrupt(irq, dev_id); return IRQ_HANDLED; } @@ -1451,9 +1471,14 @@ static void xennet_disconnect_backend(struct netfront_info *info) spin_unlock_irq(&info->tx_lock); spin_unlock_bh(&info->rx_lock); - if (info->netdev->irq) - unbind_from_irqhandler(info->netdev->irq, info->netdev); - info->evtchn = info->netdev->irq = 0; + if (info->tx_irq && (info->tx_irq == info->rx_irq)) + unbind_from_irqhandler(info->tx_irq, info); + if (info->tx_irq && (info->tx_irq != info->rx_irq)) { + unbind_from_irqhandler(info->tx_irq, info); + unbind_from_irqhandler(info->rx_irq, info); + } + info->tx_evtchn = info->rx_evtchn = 0; + info->tx_irq = info->rx_irq = 0; /* End access and free the pages */ xennet_end_access(info->tx_ring_ref, info->tx.sring); @@ -1503,12 +1528,82 @@ static int xen_net_read_mac(struct xenbus_device *dev, u8 mac[]) return 0; } +static int setup_netfront_single(struct netfront_info *info) +{ + int err; + + err = xenbus_alloc_evtchn(info->xbdev, &info->tx_evtchn); + if (err < 0) + goto fail; + + err = bind_evtchn_to_irqhandler(info->tx_evtchn, + xennet_interrupt, + 0, info->netdev->name, info); + if (err < 0) + goto bind_fail; + info->rx_evtchn = info->tx_evtchn; + info->rx_irq = info->tx_irq = err; + + return 0; + +bind_fail: + xenbus_free_evtchn(info->xbdev, info->tx_evtchn); + info->tx_evtchn = 0; +fail: + return err; +} + +static int setup_netfront_split(struct netfront_info *info) +{ + int err; + + err = xenbus_alloc_evtchn(info->xbdev, &info->tx_evtchn); + if (err < 0) + goto fail; + err = xenbus_alloc_evtchn(info->xbdev, &info->rx_evtchn); + if (err < 0) + goto alloc_rx_evtchn_fail; + + snprintf(info->tx_irq_name, sizeof(info->tx_irq_name), + "%s-tx", info->netdev->name); + err = bind_evtchn_to_irqhandler(info->tx_evtchn, + xennet_tx_interrupt, + 0, info->tx_irq_name, info); + if (err < 0) + goto bind_tx_fail; + info->tx_irq = err; + + snprintf(info->rx_irq_name, sizeof(info->rx_irq_name), + "%s-rx", info->netdev->name); + err = bind_evtchn_to_irqhandler(info->rx_evtchn, + xennet_rx_interrupt, + 0, info->rx_irq_name, info); + if (err < 0) + goto bind_rx_fail; + info->rx_irq = err; + + return 0; + +bind_rx_fail: + unbind_from_irqhandler(info->tx_irq, info); + info->tx_irq = 0; +bind_tx_fail: + xenbus_free_evtchn(info->xbdev, info->rx_evtchn); + info->rx_evtchn = 0; +alloc_rx_evtchn_fail: + xenbus_free_evtchn(info->xbdev, info->tx_evtchn); + info->tx_evtchn = 0; +fail: + return err; +} + static int setup_netfront(struct xenbus_device *dev, struct netfront_info *info) { struct xen_netif_tx_sring *txs; struct xen_netif_rx_sring *rxs; int err; struct net_device *netdev = info->netdev; + unsigned int feature_split_evtchn; info->tx_ring_ref = GRANT_INVALID_REF; info->rx_ring_ref = GRANT_INVALID_REF; @@ -1516,6 +1611,12 @@ static int setup_netfront(struct xenbus_device *dev, struct netfront_info *info) info->tx.sring = NULL; netdev->irq = 0; + err = xenbus_scanf(XBT_NIL, info->xbdev->otherend, + "feature-split-event-channels", "%u", + &feature_split_evtchn); + if (err < 0) + feature_split_evtchn = 0; + err = xen_net_read_mac(dev, netdev->dev_addr); if (err) { xenbus_dev_fatal(dev, err, "parsing %s/mac", dev->nodename); @@ -1550,22 +1651,23 @@ static int setup_netfront(struct xenbus_device *dev, struct netfront_info *info) goto grant_rx_ring_fail; info->rx_ring_ref = err; - err = xenbus_alloc_evtchn(dev, &info->evtchn); + if (feature_split_evtchn) + err = setup_netfront_split(info); + /* setup single event channel if + * a) feature-split-event-channels == 0 + * b) feature-split-event-channels == 1 but failed to setup + */ + if (!feature_split_evtchn || (feature_split_evtchn && err)) + err = setup_netfront_single(info); + if (err) goto alloc_evtchn_fail; - err = bind_evtchn_to_irqhandler(info->evtchn, xennet_interrupt, - 0, netdev->name, netdev); - if (err < 0) - goto bind_fail; - netdev->irq = err; return 0; /* If we fail to setup netfront, it is safe to just revoke access to * granted pages because backend is not accessing it at this point. */ -bind_fail: - xenbus_free_evtchn(dev, info->evtchn); alloc_evtchn_fail: gnttab_end_foreign_access_ref(info->rx_ring_ref, 0); grant_rx_ring_fail: @@ -1610,11 +1712,27 @@ again: message = "writing rx ring-ref"; goto abort_transaction; } - err = xenbus_printf(xbt, dev->nodename, - "event-channel", "%u", info->evtchn); - if (err) { - message = "writing event-channel"; - goto abort_transaction; + + if (info->tx_evtchn == info->rx_evtchn) { + err = xenbus_printf(xbt, dev->nodename, + "event-channel", "%u", info->tx_evtchn); + if (err) { + message = "writing event-channel"; + goto abort_transaction; + } + } else { + err = xenbus_printf(xbt, dev->nodename, + "event-channel-tx", "%u", info->tx_evtchn); + if (err) { + message = "writing event-channel-tx"; + goto abort_transaction; + } + err = xenbus_printf(xbt, dev->nodename, + "event-channel-rx", "%u", info->rx_evtchn); + if (err) { + message = "writing event-channel-rx"; + goto abort_transaction; + } } err = xenbus_printf(xbt, dev->nodename, "request-rx-copy", "%u", @@ -1727,7 +1845,9 @@ static int xennet_connect(struct net_device *dev) * packets. */ netif_carrier_on(np->netdev); - notify_remote_via_irq(np->netdev->irq); + notify_remote_via_irq(np->tx_irq); + if (np->tx_irq != np->rx_irq) + notify_remote_via_irq(np->rx_irq); xennet_tx_buf_gc(dev); xennet_alloc_rx_buffers(dev); -- cgit v1.2.3 From a5560a6c17a4af4e8f45d5c1215ba7d5bb0ff109 Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Wed, 22 May 2013 06:34:47 +0000 Subject: xen: netif.h: document feature-split-event-channels This patch synchronises documentation for feature-split-event-channels from Xen canonical header file. Signed-off-by: Wei Liu Signed-off-by: David S. Miller --- include/xen/interface/io/netif.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/include/xen/interface/io/netif.h b/include/xen/interface/io/netif.h index 3ef3fe05ee99..eb262e3324d2 100644 --- a/include/xen/interface/io/netif.h +++ b/include/xen/interface/io/netif.h @@ -38,6 +38,18 @@ * that it cannot safely queue packets (as it may not be kicked to send them). */ + /* + * "feature-split-event-channels" is introduced to separate guest TX + * and RX notificaion. Backend either doesn't support this feature or + * advertise it via xenstore as 0 (disabled) or 1 (enabled). + * + * To make use of this feature, frontend should allocate two event + * channels for TX and RX, advertise them to backend as + * "event-channel-tx" and "event-channel-rx" respectively. If frontend + * doesn't want to use this feature, it just writes "event-channel" + * node as before. + */ + /* * This is the 'wire' format for packets: * Request 1: xen_netif_tx_request -- XEN_NETTXF_* (any flags) -- cgit v1.2.3 From 3521b419cc21339537266cb1ab9934b041ca08e9 Mon Sep 17 00:00:00 2001 From: Yuval Mintz Date: Wed, 22 May 2013 21:21:49 +0000 Subject: bnx2x: Add Private Flags Support Utilize ethtool's callback `get_priv_flags' - shed more light on the feasibility of devices as storage interfaces. CC: Ben Hutchings Signed-off-by: Yuval Mintz Signed-off-by: Ariel Elior Signed-off-by: Eilon Greenstein Acked-by: Ben Hutchings Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x.h | 2 + .../net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c | 49 ++++++++++++++++++---- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h index 3dba2a70a00e..3f4291056e80 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h @@ -2137,6 +2137,8 @@ void bnx2x_igu_clear_sb_gen(struct bnx2x *bp, u8 func, u8 idu_sb_id, #define ATTN_HARD_WIRED_MASK 0xff00 #define ATTENTION_ID 4 +#define IS_MF_STORAGE_ONLY(bp) (IS_MF_STORAGE_SD(bp) || \ + IS_MF_FCOE_AFEX(bp)) /* stuff added to make the code fit 80Col */ diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index ce1a91618677..9e311c4b69f3 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -1921,6 +1921,19 @@ static const char bnx2x_tests_str_arr[BNX2X_NUM_TESTS_SF][ETH_GSTRING_LEN] = { "link_test (online) " }; +enum { + BNX2X_PRI_FLAG_ISCSI, + BNX2X_PRI_FLAG_FCOE, + BNX2X_PRI_FLAG_STORAGE, + BNX2X_PRI_FLAG_LEN, +}; + +static const char bnx2x_private_arr[BNX2X_PRI_FLAG_LEN][ETH_GSTRING_LEN] = { + "iSCSI offload support", + "FCoE offload support", + "Storage only interface" +}; + static u32 bnx2x_eee_to_adv(u32 eee_adv) { u32 modes = 0; @@ -2978,32 +2991,47 @@ static int bnx2x_num_stat_queues(struct bnx2x *bp) static int bnx2x_get_sset_count(struct net_device *dev, int stringset) { struct bnx2x *bp = netdev_priv(dev); - int i, num_stats; + int i, num_strings = 0; switch (stringset) { case ETH_SS_STATS: if (is_multi(bp)) { - num_stats = bnx2x_num_stat_queues(bp) * - BNX2X_NUM_Q_STATS; + num_strings = bnx2x_num_stat_queues(bp) * + BNX2X_NUM_Q_STATS; } else - num_stats = 0; + num_strings = 0; if (IS_MF_MODE_STAT(bp)) { for (i = 0; i < BNX2X_NUM_STATS; i++) if (IS_FUNC_STAT(i)) - num_stats++; + num_strings++; } else - num_stats += BNX2X_NUM_STATS; + num_strings += BNX2X_NUM_STATS; - return num_stats; + return num_strings; case ETH_SS_TEST: return BNX2X_NUM_TESTS(bp); + case ETH_SS_PRIV_FLAGS: + return BNX2X_PRI_FLAG_LEN; + default: return -EINVAL; } } +static u32 bnx2x_get_private_flags(struct net_device *dev) +{ + struct bnx2x *bp = netdev_priv(dev); + u32 flags = 0; + + flags |= (!(bp->flags & NO_ISCSI_FLAG) ? 1 : 0) << BNX2X_PRI_FLAG_ISCSI; + flags |= (!(bp->flags & NO_FCOE_FLAG) ? 1 : 0) << BNX2X_PRI_FLAG_FCOE; + flags |= (!!IS_MF_STORAGE_ONLY(bp)) << BNX2X_PRI_FLAG_STORAGE; + + return flags; +} + static void bnx2x_get_strings(struct net_device *dev, u32 stringset, u8 *buf) { struct bnx2x *bp = netdev_priv(dev); @@ -3045,6 +3073,12 @@ static void bnx2x_get_strings(struct net_device *dev, u32 stringset, u8 *buf) start = 4; memcpy(buf, bnx2x_tests_str_arr + start, ETH_GSTRING_LEN * BNX2X_NUM_TESTS(bp)); + break; + + case ETH_SS_PRIV_FLAGS: + memcpy(buf, bnx2x_private_arr, + ETH_GSTRING_LEN * BNX2X_PRI_FLAG_LEN); + break; } } @@ -3445,6 +3479,7 @@ static const struct ethtool_ops bnx2x_ethtool_ops = { .set_pauseparam = bnx2x_set_pauseparam, .self_test = bnx2x_self_test, .get_sset_count = bnx2x_get_sset_count, + .get_priv_flags = bnx2x_get_private_flags, .get_strings = bnx2x_get_strings, .set_phys_id = bnx2x_set_phys_id, .get_ethtool_stats = bnx2x_get_ethtool_stats, -- cgit v1.2.3 From 178135c11611c02161c5b64aeba341d777f2d64d Mon Sep 17 00:00:00 2001 From: Dmitry Kravkov Date: Wed, 22 May 2013 21:21:50 +0000 Subject: bnx2x: Link-flap avoidance in switch dependent mode As part of the previous unload flow, probed devices will reset the chip in order to clean the remains of the UNDI driver. As a result, it's possible for the FW to toggle the link. This toggling can prove fatal, as long periods without link can cause the filesystem mount to fail as the storage protocol timeouts. This has been observed against particular switches with long link re-establishment time. This patch informs FW during the reset period that the link should not be toggled - the FW will keep it alive until some interface will load and claim the link as its own. Signed-off-by: Dmitry Kravkov Signed-off-by: Yuval Mintz Signed-off-by: Ariel Elior Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x.h | 2 ++ drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 10 ++++++++-- drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h | 2 ++ drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 19 ++++++++++++++++--- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h index 3f4291056e80..946450d6d988 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h @@ -1937,6 +1937,8 @@ int bnx2x_sp_post(struct bnx2x *bp, int command, int cid, void bnx2x_update_coalesce(struct bnx2x *bp); int bnx2x_get_cur_phy_idx(struct bnx2x *bp); +bool bnx2x_port_after_undi(struct bnx2x *bp); + static inline u32 reg_poll(struct bnx2x *bp, u32 reg, u32 expected, int ms, int wait) { diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index 6cc5101fa96b..f4d1c08c67d3 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -2183,6 +2183,8 @@ alloc_mem_err: /* send load request to mcp and analyze response */ static int bnx2x_nic_load_request(struct bnx2x *bp, u32 *load_code) { + u32 param; + /* init fw_seq */ bp->fw_seq = (SHMEM_RD(bp, func_mb[BP_FW_MB_IDX(bp)].drv_mb_header) & @@ -2195,9 +2197,13 @@ static int bnx2x_nic_load_request(struct bnx2x *bp, u32 *load_code) DRV_PULSE_SEQ_MASK); BNX2X_DEV_INFO("drv_pulse 0x%x\n", bp->fw_drv_pulse_wr_seq); + param = DRV_MSG_CODE_LOAD_REQ_WITH_LFA; + + if (IS_MF_SD(bp) && bnx2x_port_after_undi(bp)) + param |= DRV_MSG_CODE_LOAD_REQ_FORCE_LFA; + /* load request */ - (*load_code) = bnx2x_fw_command(bp, DRV_MSG_CODE_LOAD_REQ, - DRV_MSG_CODE_LOAD_REQ_WITH_LFA); + (*load_code) = bnx2x_fw_command(bp, DRV_MSG_CODE_LOAD_REQ, param); /* if mcp fails to respond we must abort */ if (!(*load_code)) { diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h index 12f00a40cdf0..5ef3f964e544 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h @@ -1323,6 +1323,8 @@ struct drv_func_mb { #define DRV_MSG_CODE_UNLOAD_SKIP_LINK_RESET 0x00000002 #define DRV_MSG_CODE_LOAD_REQ_WITH_LFA 0x0000100a + #define DRV_MSG_CODE_LOAD_REQ_FORCE_LFA 0x00002000 + u32 fw_mb_header; #define FW_MSG_CODE_MASK 0xffff0000 #define FW_MSG_CODE_DRV_LOAD_COMMON 0x10100000 diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index b4c9dea93a53..649880d2bb1b 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -9780,6 +9780,21 @@ static bool bnx2x_prev_is_path_marked(struct bnx2x *bp) return rc; } +bool bnx2x_port_after_undi(struct bnx2x *bp) +{ + struct bnx2x_prev_path_list *entry; + bool val; + + down(&bnx2x_prev_sem); + + entry = bnx2x_prev_path_get_entry(bp); + val = !!(entry && (entry->undi & (1 << BP_PORT(bp)))); + + up(&bnx2x_prev_sem); + + return val; +} + static int bnx2x_prev_mark_path(struct bnx2x *bp, bool after_undi) { struct bnx2x_prev_path_list *tmp_list; @@ -10036,7 +10051,6 @@ static int bnx2x_prev_unload(struct bnx2x *bp) { int time_counter = 10; u32 rc, fw, hw_lock_reg, hw_lock_val; - struct bnx2x_prev_path_list *prev_list; BNX2X_DEV_INFO("Entering Previous Unload Flow\n"); /* clear hw from errors which may have resulted from an interrupted @@ -10107,8 +10121,7 @@ static int bnx2x_prev_unload(struct bnx2x *bp) } /* Mark function if its port was used to boot from SAN */ - prev_list = bnx2x_prev_path_get_entry(bp); - if (prev_list && (prev_list->undi & (1 << BP_PORT(bp)))) + if (bnx2x_port_after_undi(bp)) bp->link_params.feature_config_flags |= FEATURE_CONFIG_BOOT_FROM_SAN; -- cgit v1.2.3 From e68072ef46bcfb609ea16d45f115896a0dfbd3ff Mon Sep 17 00:00:00 2001 From: Yuval Mintz Date: Wed, 22 May 2013 21:21:51 +0000 Subject: bnx2x: Wait for MCP validity during AER During PCIe advanced error recovery, the secondary bus reset will cause FW to reset; This will cause the shared memory between it and the driver to be invalidated. During the driver's recovery flow, the driver should not make any assumption on the validity of that memory and instead re-initialize it. This also removes a redundant re-initialization of a previously initialized mutex. Signed-off-by: Yuval Mintz Signed-off-by: Ariel Elior Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 649880d2bb1b..7ed9cdfa115e 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -12760,19 +12760,6 @@ static int bnx2x_eeh_nic_unload(struct bnx2x *bp) return 0; } -static void bnx2x_eeh_recover(struct bnx2x *bp) -{ - u32 val; - - mutex_init(&bp->port.phy_mutex); - - - val = SHMEM_RD(bp, validity_map[BP_PORT(bp)]); - if ((val & (SHR_MEM_VALIDITY_DEV_INFO | SHR_MEM_VALIDITY_MB)) - != (SHR_MEM_VALIDITY_DEV_INFO | SHR_MEM_VALIDITY_MB)) - BNX2X_ERR("BAD MCP validity signature\n"); -} - /** * bnx2x_io_error_detected - called when PCI error is detected * @pdev: Pointer to PCI device @@ -12841,6 +12828,10 @@ static pci_ers_result_t bnx2x_io_slot_reset(struct pci_dev *pdev) if (netif_running(dev)) { BNX2X_ERR("IO slot reset --> driver unload\n"); + + /* MCP should have been reset; Need to wait for validity */ + bnx2x_init_shmem(bp); + if (IS_PF(bp) && SHMEM2_HAS(bp, drv_capabilities_flag)) { u32 v; @@ -12899,8 +12890,6 @@ static void bnx2x_io_resume(struct pci_dev *pdev) rtnl_lock(); - bnx2x_eeh_recover(bp); - bp->fw_seq = SHMEM_RD(bp, func_mb[BP_FW_MB_IDX(bp)].drv_mb_header) & DRV_MSG_SEQ_NUMBER_MASK; -- cgit v1.2.3 From 84b6f7456e8b88507dd85f988a9d350eb5af0e46 Mon Sep 17 00:00:00 2001 From: Yaniv Rosner Date: Wed, 22 May 2013 21:21:52 +0000 Subject: bnx2x: Enable `set_phys_id' for all functions In Multi-function mode, all functions should be able to utilize said function; There's no reason why only the link owner should be able to do so. Signed-off-by: Yaniv Rosner Signed-off-by: Yuval Mintz Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index 9e311c4b69f3..32aa88f520dc 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -3146,11 +3146,6 @@ static int bnx2x_set_phys_id(struct net_device *dev, return -EAGAIN; } - if (!bp->port.pmf) { - DP(BNX2X_MSG_ETHTOOL, "Interface is not pmf\n"); - return -EOPNOTSUPP; - } - switch (state) { case ETHTOOL_ID_ACTIVE: return 1; /* cycle on/off once per second */ -- cgit v1.2.3 From 161f65ba3583b84b4714f21dbee263f99824c516 Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Wed, 22 May 2013 07:49:34 +0000 Subject: bridge: Set vlan_features to allow offloads on vlans. When vlan device is configured on top of the brige, it does not support any offload capabilities because the bridge device does not initiliaze vlan_fatures. Set vlan_fatures to be equivalent to hw_fatures. Signed-off-by: Vlad Yasevich Signed-off-by: David S. Miller --- net/bridge/br_device.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 967312803e41..75f3239130f8 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -22,6 +22,9 @@ #include #include "br_private.h" +#define COMMON_FEATURES (NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA | \ + NETIF_F_GSO_MASK | NETIF_F_HW_CSUM) + /* net device transmit always called with BH disabled */ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) { @@ -346,12 +349,10 @@ void br_dev_setup(struct net_device *dev) dev->tx_queue_len = 0; dev->priv_flags = IFF_EBRIDGE; - dev->features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA | - NETIF_F_GSO_MASK | NETIF_F_HW_CSUM | NETIF_F_LLTX | - NETIF_F_NETNS_LOCAL | NETIF_F_HW_VLAN_CTAG_TX; - dev->hw_features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA | - NETIF_F_GSO_MASK | NETIF_F_HW_CSUM | - NETIF_F_HW_VLAN_CTAG_TX; + dev->features = COMMON_FEATURES | NETIF_F_LLTX | NETIF_F_NETNS_LOCAL | + NETIF_F_HW_VLAN_CTAG_TX; + dev->hw_features = COMMON_FEATURES | NETIF_F_HW_VLAN_CTAG_TX; + dev->vlan_features = COMMON_FEATURES; br->dev = dev; spin_lock_init(&br->lock); -- cgit v1.2.3 From 191cb1f21afd9a7fbaa085ad9b86cb307e9a3891 Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Wed, 22 May 2013 07:54:40 +0000 Subject: rps: document flow limit in scaling.txt Explain the mechanism and API of the recently merged rps flow limit patch. Signed-off-by: Willem de Bruijn Signed-off-by: David S. Miller --- Documentation/networking/scaling.txt | 58 ++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/Documentation/networking/scaling.txt b/Documentation/networking/scaling.txt index 579994afbe06..ca6977f5b2ed 100644 --- a/Documentation/networking/scaling.txt +++ b/Documentation/networking/scaling.txt @@ -163,6 +163,64 @@ and unnecessary. If there are fewer hardware queues than CPUs, then RPS might be beneficial if the rps_cpus for each queue are the ones that share the same memory domain as the interrupting CPU for that queue. +==== RPS Flow Limit + +RPS scales kernel receive processing across CPUs without introducing +reordering. The trade-off to sending all packets from the same flow +to the same CPU is CPU load imbalance if flows vary in packet rate. +In the extreme case a single flow dominates traffic. Especially on +common server workloads with many concurrent connections, such +behavior indicates a problem such as a misconfiguration or spoofed +source Denial of Service attack. + +Flow Limit is an optional RPS feature that prioritizes small flows +during CPU contention by dropping packets from large flows slightly +ahead of those from small flows. It is active only when an RPS or RFS +destination CPU approaches saturation. Once a CPU's input packet +queue exceeds half the maximum queue length (as set by sysctl +net.core.netdev_max_backlog), the kernel starts a per-flow packet +count over the last 256 packets. If a flow exceeds a set ratio (by +default, half) of these packets when a new packet arrives, then the +new packet is dropped. Packets from other flows are still only +dropped once the input packet queue reaches netdev_max_backlog. +No packets are dropped when the input packet queue length is below +the threshold, so flow limit does not sever connections outright: +even large flows maintain connectivity. + +== Interface + +Flow limit is compiled in by default (CONFIG_NET_FLOW_LIMIT), but not +turned on. It is implemented for each CPU independently (to avoid lock +and cache contention) and toggled per CPU by setting the relevant bit +in sysctl net.core.flow_limit_cpu_bitmap. It exposes the same CPU +bitmap interface as rps_cpus (see above) when called from procfs: + + /proc/sys/net/core/flow_limit_cpu_bitmap + +Per-flow rate is calculated by hashing each packet into a hashtable +bucket and incrementing a per-bucket counter. The hash function is +the same that selects a CPU in RPS, but as the number of buckets can +be much larger than the number of CPUs, flow limit has finer-grained +identification of large flows and fewer false positives. The default +table has 4096 buckets. This value can be modified through sysctl + + net.core.flow_limit_table_len + +The value is only consulted when a new table is allocated. Modifying +it does not update active tables. + +== Suggested Configuration + +Flow limit is useful on systems with many concurrent connections, +where a single connection taking up 50% of a CPU indicates a problem. +In such environments, enable the feature on all CPUs that handle +network rx interrupts (as set in /proc/irq/N/smp_affinity). + +The feature depends on the input packet queue length to exceed +the flow limit threshold (50%) + the flow history length (256). +Setting net.core.netdev_max_backlog to either 1000 or 10000 +performed well in experiments. + RFS: Receive Flow Steering ========================== -- cgit v1.2.3 From bdc7119f1bdd0632d42f435941dc290216a436e7 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 24 May 2013 10:55:38 +0900 Subject: cgroup: make cgroup_is_removed() static cgroup_is_removed() no longer has external users and it shouldn't grow any - controllers should deal with cgroup_subsys_state on/offline state instead of cgroup removal state. Make it static. While at it, make it return bool. Signed-off-by: Tejun Heo --- include/linux/cgroup.h | 1 - kernel/cgroup.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 1df5f699be61..8d9f3c911fca 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -538,7 +538,6 @@ static inline const char *cgroup_name(const struct cgroup *cgrp) int cgroup_add_cftypes(struct cgroup_subsys *ss, struct cftype *cfts); int cgroup_rm_cftypes(struct cgroup_subsys *ss, struct cftype *cfts); -int cgroup_is_removed(const struct cgroup *cgrp); bool cgroup_is_descendant(struct cgroup *cgrp, struct cgroup *ancestor); int cgroup_path(const struct cgroup *cgrp, char *buf, int buflen); diff --git a/kernel/cgroup.c b/kernel/cgroup.c index a19419f4af1a..501974823b33 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -226,7 +226,7 @@ static int css_refcnt(struct cgroup_subsys_state *css) } /* convenient tests for these bits */ -inline int cgroup_is_removed(const struct cgroup *cgrp) +static inline bool cgroup_is_removed(const struct cgroup *cgrp) { return test_bit(CGRP_REMOVED, &cgrp->flags); } -- cgit v1.2.3 From 53fa5261747a90746531e8a1c81eeb78fedc2f71 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 24 May 2013 10:55:38 +0900 Subject: cgroup: add cgroup->serial_nr and implement cgroup_next_sibling() Currently, there's no easy way to find out the next sibling cgroup unless it's known that the current cgroup is accessed from the parent's children list in a single RCU critical section. This in turn forces all iterators to require whole iteration to be enclosed in a single RCU critical section, which sometimes is too restrictive. This patch implements cgroup_next_sibling() which can reliably determine the next sibling regardless of the state of the current cgroup as long as it's accessible. It currently is impossible to determine the next sibling after dropping RCU read lock because the cgroup being iterated could be removed anytime and if RCU read lock is dropped, nothing guarantess its ->sibling.next pointer is accessible. A removed cgroup would continue to point to its next sibling for RCU accesses but stop receiving updates from the sibling. IOW, the next sibling could be removed and then complete its grace period while RCU read lock is dropped, making it unsafe to dereference ->sibling.next after dropping and re-acquiring RCU read lock. This can be solved by adding a way to traverse to the next sibling without dereferencing ->sibling.next. This patch adds a monotonically increasing cgroup serial number, cgroup->serial_nr, which guarantees that all cgroup->children lists are kept in increasing serial_nr order. A new function, cgroup_next_sibling(), is implemented, which, if CGRP_REMOVED is not set on the current cgroup, follows ->sibling.next; otherwise, traverses the parent's ->children list until it sees a sibling with higher ->serial_nr. This allows the function to always return the next sibling regardless of the state of the current cgroup without adding overhead in the fast path. Further patches will update the iterators to use cgroup_next_sibling() so that they allow dropping RCU read lock and blocking while iteration is in progress which in turn will be used to simplify controllers. v2: Typo fix as per Serge. Signed-off-by: Tejun Heo Acked-by: Serge E. Hallyn --- include/linux/cgroup.h | 10 ++++++++ kernel/cgroup.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 8d9f3c911fca..ee041a01a67e 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -188,6 +188,14 @@ struct cgroup { struct cgroup *parent; /* my parent */ struct dentry *dentry; /* cgroup fs entry, RCU protected */ + /* + * Monotonically increasing unique serial number which defines a + * uniform order among all cgroups. It's guaranteed that all + * ->children lists are in the ascending order of ->serial_nr. + * It's used to allow interrupting and resuming iterations. + */ + u64 serial_nr; + /* * This is a copy of dentry->d_name, and it's needed because * we can't use dentry->d_name in cgroup_path(). @@ -675,6 +683,8 @@ static inline struct cgroup* task_cgroup(struct task_struct *task, return task_subsys_state(task, subsys_id)->cgroup; } +struct cgroup *cgroup_next_sibling(struct cgroup *pos); + /** * cgroup_for_each_child - iterate through children of a cgroup * @pos: the cgroup * to use as the loop cursor diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 501974823b33..b87c7a5a5497 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -2975,6 +2975,55 @@ static void cgroup_enable_task_cg_lists(void) write_unlock(&css_set_lock); } +/** + * cgroup_next_sibling - find the next sibling of a given cgroup + * @pos: the current cgroup + * + * This function returns the next sibling of @pos and should be called + * under RCU read lock. The only requirement is that @pos is accessible. + * The next sibling is guaranteed to be returned regardless of @pos's + * state. + */ +struct cgroup *cgroup_next_sibling(struct cgroup *pos) +{ + struct cgroup *next; + + WARN_ON_ONCE(!rcu_read_lock_held()); + + /* + * @pos could already have been removed. Once a cgroup is removed, + * its ->sibling.next is no longer updated when its next sibling + * changes. As CGRP_REMOVED is set on removal which is fully + * serialized, if we see it unasserted, it's guaranteed that the + * next sibling hasn't finished its grace period even if it's + * already removed, and thus safe to dereference from this RCU + * critical section. If ->sibling.next is inaccessible, + * cgroup_is_removed() is guaranteed to be visible as %true here. + */ + if (likely(!cgroup_is_removed(pos))) { + next = list_entry_rcu(pos->sibling.next, struct cgroup, sibling); + if (&next->sibling != &pos->parent->children) + return next; + return NULL; + } + + /* + * Can't dereference the next pointer. Each cgroup is given a + * monotonically increasing unique serial number and always + * appended to the sibling list, so the next one can be found by + * walking the parent's children until we see a cgroup with higher + * serial number than @pos's. + * + * While this path can be slow, it's taken only when either the + * current cgroup is removed or iteration and removal race. + */ + list_for_each_entry_rcu(next, &pos->parent->children, sibling) + if (next->serial_nr > pos->serial_nr) + return next; + return NULL; +} +EXPORT_SYMBOL_GPL(cgroup_next_sibling); + /** * cgroup_next_descendant_pre - find the next descendant for pre-order walk * @pos: the current position (%NULL to initiate traversal) @@ -4137,6 +4186,7 @@ static void offline_css(struct cgroup_subsys *ss, struct cgroup *cgrp) static long cgroup_create(struct cgroup *parent, struct dentry *dentry, umode_t mode) { + static atomic64_t serial_nr_cursor = ATOMIC64_INIT(0); struct cgroup *cgrp; struct cgroup_name *name; struct cgroupfs_root *root = parent->root; @@ -4217,6 +4267,14 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry, goto err_free_all; lockdep_assert_held(&dentry->d_inode->i_mutex); + /* + * Assign a monotonically increasing serial number. With the list + * appending below, it guarantees that sibling cgroups are always + * sorted in the ascending serial number order on the parent's + * ->children. + */ + cgrp->serial_nr = atomic64_inc_return(&serial_nr_cursor); + /* allocation complete, commit to creation */ list_add_tail(&cgrp->allcg_node, &root->allcg_list); list_add_tail_rcu(&cgrp->sibling, &cgrp->parent->children); @@ -4304,6 +4362,10 @@ static int cgroup_destroy_locked(struct cgroup *cgrp) * removed. This makes future css_tryget() and child creation * attempts fail thus maintaining the removal conditions verified * above. + * + * Note that CGRP_REMVOED clearing is depended upon by + * cgroup_next_sibling() to resume iteration after dropping RCU + * read lock. See cgroup_next_sibling() for details. */ for_each_subsys(cgrp->root, ss) { struct cgroup_subsys_state *css = cgrp->subsys[ss->subsys_id]; -- cgit v1.2.3 From 75501a6d59e989e5c286716e5b3b66ace4660e83 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 24 May 2013 10:55:38 +0900 Subject: cgroup: update iterators to use cgroup_next_sibling() This patch converts cgroup_for_each_child(), cgroup_next_descendant_pre/post() and thus cgroup_for_each_descendant_pre/post() to use cgroup_next_sibling() instead of manually dereferencing ->sibling.next. The only reason the iterators couldn't allow dropping RCU read lock while iteration is in progress was because they couldn't determine the next sibling safely once RCU read lock is dropped. Using cgroup_next_sibling() removes that problem and enables all iterators to allow dropping RCU read lock in the middle. Comments are updated accordingly. This makes the iterators easier to use and will simplify controllers. Note that @cgroup argument is renamed to @cgrp in cgroup_for_each_child() because it conflicts with "struct cgroup" used in the new macro body. Signed-off-by: Tejun Heo Acked-by: Serge E. Hallyn Reviewed-by: Michal Hocko --- include/linux/cgroup.h | 18 ++++++++++++++---- kernel/cgroup.c | 25 +++++++++++++++++++------ 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index ee041a01a67e..d0ad3794b947 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -688,9 +688,9 @@ struct cgroup *cgroup_next_sibling(struct cgroup *pos); /** * cgroup_for_each_child - iterate through children of a cgroup * @pos: the cgroup * to use as the loop cursor - * @cgroup: cgroup whose children to walk + * @cgrp: cgroup whose children to walk * - * Walk @cgroup's children. Must be called under rcu_read_lock(). A child + * Walk @cgrp's children. Must be called under rcu_read_lock(). A child * cgroup which hasn't finished ->css_online() or already has finished * ->css_offline() may show up during traversal and it's each subsystem's * responsibility to verify that each @pos is alive. @@ -698,9 +698,15 @@ struct cgroup *cgroup_next_sibling(struct cgroup *pos); * If a subsystem synchronizes against the parent in its ->css_online() and * before starting iterating, a cgroup which finished ->css_online() is * guaranteed to be visible in the future iterations. + * + * It is allowed to temporarily drop RCU read lock during iteration. The + * caller is responsible for ensuring that @pos remains accessible until + * the start of the next iteration by, for example, bumping the css refcnt. */ -#define cgroup_for_each_child(pos, cgroup) \ - list_for_each_entry_rcu(pos, &(cgroup)->children, sibling) +#define cgroup_for_each_child(pos, cgrp) \ + for ((pos) = list_first_or_null_rcu(&(cgrp)->children, \ + struct cgroup, sibling); \ + (pos); (pos) = cgroup_next_sibling((pos))) struct cgroup *cgroup_next_descendant_pre(struct cgroup *pos, struct cgroup *cgroup); @@ -759,6 +765,10 @@ struct cgroup *cgroup_rightmost_descendant(struct cgroup *pos); * Alternatively, a subsystem may choose to use a single global lock to * synchronize ->css_online() and ->css_offline() against tree-walking * operations. + * + * It is allowed to temporarily drop RCU read lock during iteration. The + * caller is responsible for ensuring that @pos remains accessible until + * the start of the next iteration by, for example, bumping the css refcnt. */ #define cgroup_for_each_descendant_pre(pos, cgroup) \ for (pos = cgroup_next_descendant_pre(NULL, (cgroup)); (pos); \ diff --git a/kernel/cgroup.c b/kernel/cgroup.c index b87c7a5a5497..fefc41c1a147 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -3031,6 +3031,11 @@ EXPORT_SYMBOL_GPL(cgroup_next_sibling); * * To be used by cgroup_for_each_descendant_pre(). Find the next * descendant to visit for pre-order traversal of @cgroup's descendants. + * + * While this function requires RCU read locking, it doesn't require the + * whole traversal to be contained in a single RCU critical section. This + * function will return the correct next descendant as long as both @pos + * and @cgroup are accessible and @pos is a descendant of @cgroup. */ struct cgroup *cgroup_next_descendant_pre(struct cgroup *pos, struct cgroup *cgroup) @@ -3050,11 +3055,9 @@ struct cgroup *cgroup_next_descendant_pre(struct cgroup *pos, /* no child, visit my or the closest ancestor's next sibling */ while (pos != cgroup) { - next = list_entry_rcu(pos->sibling.next, struct cgroup, - sibling); - if (&next->sibling != &pos->parent->children) + next = cgroup_next_sibling(pos); + if (next) return next; - pos = pos->parent; } @@ -3069,6 +3072,11 @@ EXPORT_SYMBOL_GPL(cgroup_next_descendant_pre); * Return the rightmost descendant of @pos. If there's no descendant, * @pos is returned. This can be used during pre-order traversal to skip * subtree of @pos. + * + * While this function requires RCU read locking, it doesn't require the + * whole traversal to be contained in a single RCU critical section. This + * function will return the correct rightmost descendant as long as @pos is + * accessible. */ struct cgroup *cgroup_rightmost_descendant(struct cgroup *pos) { @@ -3108,6 +3116,11 @@ static struct cgroup *cgroup_leftmost_descendant(struct cgroup *pos) * * To be used by cgroup_for_each_descendant_post(). Find the next * descendant to visit for post-order traversal of @cgroup's descendants. + * + * While this function requires RCU read locking, it doesn't require the + * whole traversal to be contained in a single RCU critical section. This + * function will return the correct next descendant as long as both @pos + * and @cgroup are accessible and @pos is a descendant of @cgroup. */ struct cgroup *cgroup_next_descendant_post(struct cgroup *pos, struct cgroup *cgroup) @@ -3123,8 +3136,8 @@ struct cgroup *cgroup_next_descendant_post(struct cgroup *pos, } /* if there's an unvisited sibling, visit its leftmost descendant */ - next = list_entry_rcu(pos->sibling.next, struct cgroup, sibling); - if (&next->sibling != &pos->parent->children) + next = cgroup_next_sibling(pos); + if (next) return cgroup_leftmost_descendant(next); /* no sibling left, visit parent */ -- cgit v1.2.3 From d591fb56618f4f93160b477dfa25bbb1e31b0e85 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 24 May 2013 10:55:38 +0900 Subject: device_cgroup: simplify cgroup tree walk in propagate_exception() During a config change, propagate_exception() needs to traverse the subtree to update config on the subtree. Because such config updates need to allocate memory, it couldn't directly use cgroup_for_each_descendant_pre() which required the whole iteration to be contained in a single RCU read critical section. To work around the limitation, propagate_exception() built a linked list of descendant cgroups while read-locking RCU and then walked the list afterwards, which is safe as the whole iteration is protected by devcgroup_mutex. This works but is cumbersome. With the recent updates, cgroup iterators now allow dropping RCU read lock while iteration is in progress making this workaround no longer necessary. This patch replaces dev_cgroup->propagate_pending list and get_online_devcg() with direct cgroup_for_each_descendant_pre() walk. Signed-off-by: Tejun Heo Cc: Aristeu Rozanski Acked-by: Serge E. Hallyn Reviewed-by: Michal Hocko --- security/device_cgroup.c | 56 ++++++++++++++++-------------------------------- 1 file changed, 18 insertions(+), 38 deletions(-) diff --git a/security/device_cgroup.c b/security/device_cgroup.c index dd0dc574d78d..e8aad69f0d69 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c @@ -49,8 +49,6 @@ struct dev_cgroup { struct cgroup_subsys_state css; struct list_head exceptions; enum devcg_behavior behavior; - /* temporary list for pending propagation operations */ - struct list_head propagate_pending; }; static inline struct dev_cgroup *css_to_devcgroup(struct cgroup_subsys_state *s) @@ -241,7 +239,6 @@ static struct cgroup_subsys_state *devcgroup_css_alloc(struct cgroup *cgroup) if (!dev_cgroup) return ERR_PTR(-ENOMEM); INIT_LIST_HEAD(&dev_cgroup->exceptions); - INIT_LIST_HEAD(&dev_cgroup->propagate_pending); dev_cgroup->behavior = DEVCG_DEFAULT_NONE; return &dev_cgroup->css; @@ -444,34 +441,6 @@ static void revalidate_active_exceptions(struct dev_cgroup *devcg) } } -/** - * get_online_devcg - walks the cgroup tree and fills a list with the online - * groups - * @root: cgroup used as starting point - * @online: list that will be filled with online groups - * - * Must be called with devcgroup_mutex held. Grabs RCU lock. - * Because devcgroup_mutex is held, no devcg will become online or offline - * during the tree walk (see devcgroup_online, devcgroup_offline) - * A separated list is needed because propagate_behavior() and - * propagate_exception() need to allocate memory and can block. - */ -static void get_online_devcg(struct cgroup *root, struct list_head *online) -{ - struct cgroup *pos; - struct dev_cgroup *devcg; - - lockdep_assert_held(&devcgroup_mutex); - - rcu_read_lock(); - cgroup_for_each_descendant_pre(pos, root) { - devcg = cgroup_to_devcgroup(pos); - if (is_devcg_online(devcg)) - list_add_tail(&devcg->propagate_pending, online); - } - rcu_read_unlock(); -} - /** * propagate_exception - propagates a new exception to the children * @devcg_root: device cgroup that added a new exception @@ -482,15 +451,24 @@ static void get_online_devcg(struct cgroup *root, struct list_head *online) static int propagate_exception(struct dev_cgroup *devcg_root, struct dev_exception_item *ex) { - struct cgroup *root = devcg_root->css.cgroup; - struct dev_cgroup *devcg, *parent, *tmp; + struct cgroup *root = devcg_root->css.cgroup, *pos; int rc = 0; - LIST_HEAD(pending); - get_online_devcg(root, &pending); + rcu_read_lock(); - list_for_each_entry_safe(devcg, tmp, &pending, propagate_pending) { - parent = cgroup_to_devcgroup(devcg->css.cgroup->parent); + cgroup_for_each_descendant_pre(pos, root) { + struct dev_cgroup *devcg = cgroup_to_devcgroup(pos); + + /* + * Because devcgroup_mutex is held, no devcg will become + * online or offline during the tree walk (see on/offline + * methods), and online ones are safe to access outside RCU + * read lock without bumping refcnt. + */ + if (!is_devcg_online(devcg)) + continue; + + rcu_read_unlock(); /* * in case both root's behavior and devcg is allow, a new @@ -512,8 +490,10 @@ static int propagate_exception(struct dev_cgroup *devcg_root, } revalidate_active_exceptions(devcg); - list_del_init(&devcg->propagate_pending); + rcu_read_lock(); } + + rcu_read_unlock(); return rc; } -- cgit v1.2.3 From ee9c799c231324de681eb21e06d8bf4842768b75 Mon Sep 17 00:00:00 2001 From: Sathya Perla Date: Wed, 22 May 2013 23:04:55 +0000 Subject: be2net: refactor HW workarounds in be_xmit() Refactor all TX HW workarounds into a separate routine called be_xmit_workarounds(). Signed-off-by: Sathya Perla Signed-off-by: David S. Miller --- drivers/net/ethernet/emulex/benet/be_main.c | 62 ++++++++++++++++++----------- 1 file changed, 38 insertions(+), 24 deletions(-) diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index a444110b060f..7892361af0ac 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -842,32 +842,27 @@ static int be_vlan_tag_tx_chk(struct be_adapter *adapter, struct sk_buff *skb) return vlan_tx_tag_present(skb) || adapter->pvid || adapter->qnq_vid; } -static int be_ipv6_tx_stall_chk(struct be_adapter *adapter, struct sk_buff *skb) +static int be_ipv6_tx_stall_chk(struct be_adapter *adapter, + struct sk_buff *skb) { - return BE3_chip(adapter) && - be_ipv6_exthdr_check(skb); + return BE3_chip(adapter) && be_ipv6_exthdr_check(skb); } -static netdev_tx_t be_xmit(struct sk_buff *skb, - struct net_device *netdev) +static struct sk_buff *be_xmit_workarounds(struct be_adapter *adapter, + struct sk_buff *skb, + bool *skip_hw_vlan) { - struct be_adapter *adapter = netdev_priv(netdev); - struct be_tx_obj *txo = &adapter->tx_obj[skb_get_queue_mapping(skb)]; - struct be_queue_info *txq = &txo->q; - struct iphdr *ip = NULL; - u32 wrb_cnt = 0, copied = 0; - u32 start = txq->head, eth_hdr_len; - bool dummy_wrb, stopped = false; - bool skip_hw_vlan = false; struct vlan_ethhdr *veh = (struct vlan_ethhdr *)skb->data; - - eth_hdr_len = ntohs(skb->protocol) == ETH_P_8021Q ? - VLAN_ETH_HLEN : ETH_HLEN; + unsigned int eth_hdr_len; + struct iphdr *ip; /* For padded packets, BE HW modifies tot_len field in IP header * incorrecly when VLAN tag is inserted by HW. */ - if (skb->len <= 60 && vlan_tx_tag_present(skb) && is_ipv4_pkt(skb)) { + eth_hdr_len = ntohs(skb->protocol) == ETH_P_8021Q ? + VLAN_ETH_HLEN : ETH_HLEN; + if (skb->len <= 60 && vlan_tx_tag_present(skb) && + is_ipv4_pkt(skb)) { ip = (struct iphdr *)ip_hdr(skb); pskb_trim(skb, eth_hdr_len + ntohs(ip->tot_len)); } @@ -877,15 +872,15 @@ static netdev_tx_t be_xmit(struct sk_buff *skb, */ if ((adapter->function_mode & UMC_ENABLED) && veh->h_vlan_proto == htons(ETH_P_8021Q)) - skip_hw_vlan = true; + *skip_hw_vlan = true; /* HW has a bug wherein it will calculate CSUM for VLAN * pkts even though it is disabled. * Manually insert VLAN in pkt. */ if (skb->ip_summed != CHECKSUM_PARTIAL && - vlan_tx_tag_present(skb)) { - skb = be_insert_vlan_in_pkt(adapter, skb, &skip_hw_vlan); + vlan_tx_tag_present(skb)) { + skb = be_insert_vlan_in_pkt(adapter, skb, skip_hw_vlan); if (unlikely(!skb)) goto tx_drop; } @@ -895,8 +890,8 @@ static netdev_tx_t be_xmit(struct sk_buff *skb, * skip HW tagging is not enabled by FW. */ if (unlikely(be_ipv6_tx_stall_chk(adapter, skb) && - (adapter->pvid || adapter->qnq_vid) && - !qnq_async_evt_rcvd(adapter))) + (adapter->pvid || adapter->qnq_vid) && + !qnq_async_evt_rcvd(adapter))) goto tx_drop; /* Manual VLAN tag insertion to prevent: @@ -907,11 +902,31 @@ static netdev_tx_t be_xmit(struct sk_buff *skb, */ if (be_ipv6_tx_stall_chk(adapter, skb) && be_vlan_tag_tx_chk(adapter, skb)) { - skb = be_insert_vlan_in_pkt(adapter, skb, &skip_hw_vlan); + skb = be_insert_vlan_in_pkt(adapter, skb, skip_hw_vlan); if (unlikely(!skb)) goto tx_drop; } + return skb; +tx_drop: + dev_kfree_skb_any(skb); + return NULL; +} + +static netdev_tx_t be_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + struct be_adapter *adapter = netdev_priv(netdev); + struct be_tx_obj *txo = &adapter->tx_obj[skb_get_queue_mapping(skb)]; + struct be_queue_info *txq = &txo->q; + bool dummy_wrb, stopped = false; + u32 wrb_cnt = 0, copied = 0; + bool skip_hw_vlan = false; + u32 start = txq->head; + + skb = be_xmit_workarounds(adapter, skb, &skip_hw_vlan); + if (!skb) + return NETDEV_TX_OK; + wrb_cnt = wrb_cnt_for_skb(adapter, skb, &dummy_wrb); copied = make_tx_wrbs(adapter, txq, skb, wrb_cnt, dummy_wrb, @@ -941,7 +956,6 @@ static netdev_tx_t be_xmit(struct sk_buff *skb, txq->head = start; dev_kfree_skb_any(skb); } -tx_drop: return NETDEV_TX_OK; } -- cgit v1.2.3 From 5c23341ff66c9280c2e76dc9795cd14497ea780f Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Fri, 24 May 2013 11:05:59 +0900 Subject: libata: do not limit R-Car SATA driver to shmobile The motivation for this is to allow the driver to be used with the r8a7790 SoC. I believe that rather than adding another SoC to the list of allowed SoCs it is better to simply remove the dependency of the driver on shmobile all together. Signed-off-by: Simon Horman Signed-off-by: Tejun Heo --- drivers/ata/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index a5a3ebcbdd2c..aba6e93b0502 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -263,7 +263,6 @@ config SATA_PROMISE config SATA_RCAR tristate "Renesas R-Car SATA support" - depends on ARCH_SHMOBILE && ARCH_R8A7779 help This option enables support for Renesas R-Car Serial ATA. -- cgit v1.2.3 From 0b95a7f85718adcbba36407ef88bba0a7379ed03 Mon Sep 17 00:00:00 2001 From: Tim Chen Date: Wed, 1 May 2013 12:52:50 -0700 Subject: crypto: crct10dif - Glue code to cast accelerated CRCT10DIF assembly as a crypto transform Glue code that plugs the PCLMULQDQ accelerated CRC T10 DIF hash into the crypto framework. The config CRYPTO_CRCT10DIF_PCLMUL should be turned on to enable the feature. The crc_t10dif crypto library function will use this faster algorithm when crct10dif_pclmul module is loaded. Signed-off-by: Tim Chen Signed-off-by: Herbert Xu --- arch/x86/crypto/Makefile | 2 + arch/x86/crypto/crct10dif-pclmul_glue.c | 151 ++++++++++++++++++++++++++++++++ crypto/Kconfig | 11 +++ 3 files changed, 164 insertions(+) create mode 100644 arch/x86/crypto/crct10dif-pclmul_glue.c diff --git a/arch/x86/crypto/Makefile b/arch/x86/crypto/Makefile index a3a0ed80f17c..94cb151adc1d 100644 --- a/arch/x86/crypto/Makefile +++ b/arch/x86/crypto/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_CRYPTO_SHA1_SSSE3) += sha1-ssse3.o obj-$(CONFIG_CRYPTO_CRC32_PCLMUL) += crc32-pclmul.o obj-$(CONFIG_CRYPTO_SHA256_SSSE3) += sha256-ssse3.o obj-$(CONFIG_CRYPTO_SHA512_SSSE3) += sha512-ssse3.o +obj-$(CONFIG_CRYPTO_CRCT10DIF_PCLMUL) += crct10dif-pclmul.o # These modules require assembler to support AVX. ifeq ($(avx_supported),yes) @@ -87,3 +88,4 @@ crc32c-intel-$(CONFIG_64BIT) += crc32c-pcl-intel-asm_64.o crc32-pclmul-y := crc32-pclmul_asm.o crc32-pclmul_glue.o sha256-ssse3-y := sha256-ssse3-asm.o sha256-avx-asm.o sha256-avx2-asm.o sha256_ssse3_glue.o sha512-ssse3-y := sha512-ssse3-asm.o sha512-avx-asm.o sha512-avx2-asm.o sha512_ssse3_glue.o +crct10dif-pclmul-y := crct10dif-pcl-asm_64.o crct10dif-pclmul_glue.o diff --git a/arch/x86/crypto/crct10dif-pclmul_glue.c b/arch/x86/crypto/crct10dif-pclmul_glue.c new file mode 100644 index 000000000000..7845d7fd54c0 --- /dev/null +++ b/arch/x86/crypto/crct10dif-pclmul_glue.c @@ -0,0 +1,151 @@ +/* + * Cryptographic API. + * + * T10 Data Integrity Field CRC16 Crypto Transform using PCLMULQDQ Instructions + * + * Copyright (C) 2013 Intel Corporation + * Author: Tim Chen + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +asmlinkage __u16 crc_t10dif_pcl(__u16 crc, const unsigned char *buf, + size_t len); + +struct chksum_desc_ctx { + __u16 crc; +}; + +/* + * Steps through buffer one byte at at time, calculates reflected + * crc using table. + */ + +static int chksum_init(struct shash_desc *desc) +{ + struct chksum_desc_ctx *ctx = shash_desc_ctx(desc); + + ctx->crc = 0; + + return 0; +} + +static int chksum_update(struct shash_desc *desc, const u8 *data, + unsigned int length) +{ + struct chksum_desc_ctx *ctx = shash_desc_ctx(desc); + + if (irq_fpu_usable()) { + kernel_fpu_begin(); + ctx->crc = crc_t10dif_pcl(ctx->crc, data, length); + kernel_fpu_end(); + } else + ctx->crc = crc_t10dif_generic(ctx->crc, data, length); + return 0; +} + +static int chksum_final(struct shash_desc *desc, u8 *out) +{ + struct chksum_desc_ctx *ctx = shash_desc_ctx(desc); + + *(__u16 *)out = ctx->crc; + return 0; +} + +static int __chksum_finup(__u16 *crcp, const u8 *data, unsigned int len, + u8 *out) +{ + if (irq_fpu_usable()) { + kernel_fpu_begin(); + *(__u16 *)out = crc_t10dif_pcl(*crcp, data, len); + kernel_fpu_end(); + } else + *(__u16 *)out = crc_t10dif_generic(*crcp, data, len); + return 0; +} + +static int chksum_finup(struct shash_desc *desc, const u8 *data, + unsigned int len, u8 *out) +{ + struct chksum_desc_ctx *ctx = shash_desc_ctx(desc); + + return __chksum_finup(&ctx->crc, data, len, out); +} + +static int chksum_digest(struct shash_desc *desc, const u8 *data, + unsigned int length, u8 *out) +{ + struct chksum_desc_ctx *ctx = shash_desc_ctx(desc); + + return __chksum_finup(&ctx->crc, data, length, out); +} + +static struct shash_alg alg = { + .digestsize = CRC_T10DIF_DIGEST_SIZE, + .init = chksum_init, + .update = chksum_update, + .final = chksum_final, + .finup = chksum_finup, + .digest = chksum_digest, + .descsize = sizeof(struct chksum_desc_ctx), + .base = { + .cra_name = "crct10dif", + .cra_driver_name = "crct10dif-pclmul", + .cra_priority = 200, + .cra_blocksize = CRC_T10DIF_BLOCK_SIZE, + .cra_module = THIS_MODULE, + } +}; + +static const struct x86_cpu_id crct10dif_cpu_id[] = { + X86_FEATURE_MATCH(X86_FEATURE_PCLMULQDQ), + {} +}; +MODULE_DEVICE_TABLE(x86cpu, crct10dif_cpu_id); + +static int __init crct10dif_intel_mod_init(void) +{ + if (!x86_match_cpu(crct10dif_cpu_id)) + return -ENODEV; + + return crypto_register_shash(&alg); +} + +static void __exit crct10dif_intel_mod_fini(void) +{ + crypto_unregister_shash(&alg); +} + +module_init(crct10dif_intel_mod_init); +module_exit(crct10dif_intel_mod_fini); + +MODULE_AUTHOR("Tim Chen "); +MODULE_DESCRIPTION("T10 DIF CRC calculation accelerated with PCLMULQDQ."); +MODULE_LICENSE("GPL"); + +MODULE_ALIAS("crct10dif"); +MODULE_ALIAS("crct10dif-pclmul"); diff --git a/crypto/Kconfig b/crypto/Kconfig index ceb3611efe44..d1ca6312d798 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -384,6 +384,17 @@ config CRYPTO_CRCT10DIF a crypto transform. This allows for faster crc t10 diff transforms to be used if they are available. +config CRYPTO_CRCT10DIF_PCLMUL + tristate "CRCT10DIF PCLMULQDQ hardware acceleration" + depends on X86 && 64BIT && CRC_T10DIF + select CRYPTO_HASH + help + For x86_64 processors with SSE4.2 and PCLMULQDQ supported, + CRC T10 DIF PCLMULQDQ computation can be hardware + accelerated PCLMULQDQ instruction. This option will create + 'crct10dif-plcmul' module, which is faster when computing the + crct10dif checksum as compared with the generic table implementation. + config CRYPTO_GHASH tristate "GHASH digest algorithm" select CRYPTO_GF128MUL -- cgit v1.2.3 From 39761214eefc6b070f29402aa1165f24d789b3f7 Mon Sep 17 00:00:00 2001 From: Tim Chen Date: Wed, 1 May 2013 12:52:51 -0700 Subject: crypto: crct10dif - Simple correctness and speed test for CRCT10DIF hash These are simple tests to do sanity check of CRC T10 DIF hash. The correctness of the transform can be checked with the command modprobe tcrypt mode=47 The speed of the transform can be evaluated with the command modprobe tcrypt mode=320 Set the cpu frequency to constant and turn turbo off when running the speed test so the frequency governor will not tweak the frequency and affects the measurements. Signed-off-by: Tim Chen Signed-off-by: Herbert Xu --- crypto/tcrypt.c | 8 ++++++++ crypto/testmgr.c | 10 ++++++++++ crypto/testmgr.h | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/crypto/tcrypt.c b/crypto/tcrypt.c index 66d254ce0d11..25a5934f0e50 100644 --- a/crypto/tcrypt.c +++ b/crypto/tcrypt.c @@ -1174,6 +1174,10 @@ static int do_test(int m) ret += tcrypt_test("ghash"); break; + case 47: + ret += tcrypt_test("crct10dif"); + break; + case 100: ret += tcrypt_test("hmac(md5)"); break; @@ -1498,6 +1502,10 @@ static int do_test(int m) test_hash_speed("crc32c", sec, generic_hash_speed_template); if (mode > 300 && mode < 400) break; + case 320: + test_hash_speed("crct10dif", sec, generic_hash_speed_template); + if (mode > 300 && mode < 400) break; + case 399: break; diff --git a/crypto/testmgr.c b/crypto/testmgr.c index 5823735cf381..f19a392ade78 100644 --- a/crypto/testmgr.c +++ b/crypto/testmgr.c @@ -1973,6 +1973,16 @@ static const struct alg_test_desc alg_test_descs[] = { .count = CRC32C_TEST_VECTORS } } + }, { + .alg = "crct10dif", + .test = alg_test_hash, + .fips_allowed = 1, + .suite = { + .hash = { + .vecs = crct10dif_tv_template, + .count = CRCT10DIF_TEST_VECTORS + } + } }, { .alg = "cryptd(__driver-cbc-aes-aesni)", .test = alg_test_null, diff --git a/crypto/testmgr.h b/crypto/testmgr.h index 1e701bc075b9..7d44aa3d6b44 100644 --- a/crypto/testmgr.h +++ b/crypto/testmgr.h @@ -450,6 +450,39 @@ static struct hash_testvec rmd320_tv_template[] = { } }; +#define CRCT10DIF_TEST_VECTORS 3 +static struct hash_testvec crct10dif_tv_template[] = { + { + .plaintext = "abc", + .psize = 3, +#ifdef __LITTLE_ENDIAN + .digest = "\x3b\x44", +#else + .digest = "\x44\x3b", +#endif + }, { + .plaintext = "1234567890123456789012345678901234567890" + "123456789012345678901234567890123456789", + .psize = 79, +#ifdef __LITTLE_ENDIAN + .digest = "\x70\x4b", +#else + .digest = "\x4b\x70", +#endif + }, { + .plaintext = + "abcddddddddddddddddddddddddddddddddddddddddddddddddddddd", + .psize = 56, +#ifdef __LITTLE_ENDIAN + .digest = "\xe3\x9c", +#else + .digest = "\x9c\xe3", +#endif + .np = 2, + .tap = { 28, 28 } + } +}; + /* * SHA1 test vectors from from FIPS PUB 180-1 * Long vector from CAVS 5.0 -- cgit v1.2.3 From 30862281f717835ec06eca9aeb2b3ddc3da33bc1 Mon Sep 17 00:00:00 2001 From: Laurent Navet Date: Thu, 2 May 2013 14:00:38 +0200 Subject: drivers: crypto: use devm_ioremap_resource() Replace calls to deprecated devm_request_and_ioremap by devm_ioremap_resource. Found with coccicheck and this semantic patch: scripts/coccinelle/api/devm_request_and_ioremap.cocci. Signed-off-by: Laurent Navet Signed-off-by: Herbert Xu --- drivers/crypto/omap-aes.c | 7 +++---- drivers/crypto/omap-sham.c | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/crypto/omap-aes.c b/drivers/crypto/omap-aes.c index ee15b0f7849a..305a2aacf9b3 100644 --- a/drivers/crypto/omap-aes.c +++ b/drivers/crypto/omap-aes.c @@ -1125,10 +1125,9 @@ static int omap_aes_probe(struct platform_device *pdev) if (err) goto err_res; - dd->io_base = devm_request_and_ioremap(dev, &res); - if (!dd->io_base) { - dev_err(dev, "can't ioremap\n"); - err = -ENOMEM; + dd->io_base = devm_ioremap_resource(dev, &res); + if (IS_ERR(dd->io_base)) { + err = PTR_ERR(dd->io_base); goto err_res; } dd->phys_base = res.start; diff --git a/drivers/crypto/omap-sham.c b/drivers/crypto/omap-sham.c index a1e1b4756ee5..4bb67652c200 100644 --- a/drivers/crypto/omap-sham.c +++ b/drivers/crypto/omap-sham.c @@ -1686,10 +1686,9 @@ static int omap_sham_probe(struct platform_device *pdev) if (err) goto res_err; - dd->io_base = devm_request_and_ioremap(dev, &res); - if (!dd->io_base) { - dev_err(dev, "can't ioremap\n"); - err = -ENOMEM; + dd->io_base = devm_ioremap_resource(dev, &res); + if (IS_ERR(dd->io_base)) { + err = PTR_ERR(dd->io_base); goto res_err; } dd->phys_base = res.start; -- cgit v1.2.3 From 928e47ec84c1f889de996b64c4a4687df8d2709a Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 3 May 2013 14:48:14 +0530 Subject: crypto: mv_cesa: Remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Cc: Sebastian Andrzej Siewior Signed-off-by: Herbert Xu --- drivers/crypto/mv_cesa.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/crypto/mv_cesa.c b/drivers/crypto/mv_cesa.c index ce6290e5471a..3374a3ebe4c7 100644 --- a/drivers/crypto/mv_cesa.c +++ b/drivers/crypto/mv_cesa.c @@ -1146,7 +1146,6 @@ err_unmap_reg: err: kfree(cp); cpg = NULL; - platform_set_drvdata(pdev, NULL); return ret; } -- cgit v1.2.3 From 612dde34f3f2d0341fefb914a40b512521ddc3d4 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 3 May 2013 14:48:15 +0530 Subject: crypto: s5p-sss: Remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Cc: Vladimir Zapolskiy Signed-off-by: Herbert Xu --- drivers/crypto/s5p-sss.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/crypto/s5p-sss.c b/drivers/crypto/s5p-sss.c index 4b314326f48a..cf149b19ff47 100644 --- a/drivers/crypto/s5p-sss.c +++ b/drivers/crypto/s5p-sss.c @@ -647,7 +647,6 @@ static int s5p_aes_probe(struct platform_device *pdev) clk_disable(pdata->clk); s5p_dev = NULL; - platform_set_drvdata(pdev, NULL); return err; } @@ -668,7 +667,6 @@ static int s5p_aes_remove(struct platform_device *pdev) clk_disable(pdata->clk); s5p_dev = NULL; - platform_set_drvdata(pdev, NULL); return 0; } -- cgit v1.2.3 From 19e21b1d6d51a14c79ee3bc80352d665c7772b1c Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 6 May 2013 13:37:13 +0900 Subject: hwrng: atmel - remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Herbert Xu --- drivers/char/hw_random/atmel-rng.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/char/hw_random/atmel-rng.c b/drivers/char/hw_random/atmel-rng.c index 7c73d4aca36b..bf9fc6b79328 100644 --- a/drivers/char/hw_random/atmel-rng.c +++ b/drivers/char/hw_random/atmel-rng.c @@ -108,8 +108,6 @@ static int atmel_trng_remove(struct platform_device *pdev) clk_disable(trng->clk); clk_put(trng->clk); - platform_set_drvdata(pdev, NULL); - return 0; } -- cgit v1.2.3 From 938ae2f83b7f94cf44b66757d63e56e9865ffdf9 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 6 May 2013 13:38:27 +0900 Subject: hwrng: bcm63xx - remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Herbert Xu --- drivers/char/hw_random/bcm63xx-rng.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/char/hw_random/bcm63xx-rng.c b/drivers/char/hw_random/bcm63xx-rng.c index f343b7d0dfa1..36581ea562cb 100644 --- a/drivers/char/hw_random/bcm63xx-rng.c +++ b/drivers/char/hw_random/bcm63xx-rng.c @@ -137,7 +137,6 @@ static int bcm63xx_rng_probe(struct platform_device *pdev) out_clk_disable: clk_disable(clk); out_free_rng: - platform_set_drvdata(pdev, NULL); kfree(rng); out_free_priv: kfree(priv); @@ -154,7 +153,6 @@ static int bcm63xx_rng_remove(struct platform_device *pdev) clk_disable(priv->clk); kfree(priv); kfree(rng); - platform_set_drvdata(pdev, NULL); return 0; } -- cgit v1.2.3 From 08d63a22ced6e410dfceaa3792b79f1e01d594c7 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 6 May 2013 13:39:38 +0900 Subject: hwrng: timeriomem - remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Herbert Xu --- drivers/char/hw_random/timeriomem-rng.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/char/hw_random/timeriomem-rng.c b/drivers/char/hw_random/timeriomem-rng.c index 3e75737f5fe1..d2120ba8f3f9 100644 --- a/drivers/char/hw_random/timeriomem-rng.c +++ b/drivers/char/hw_random/timeriomem-rng.c @@ -192,7 +192,6 @@ out_release_io: out_timer: del_timer_sync(&priv->timer); out_free: - platform_set_drvdata(pdev, NULL); kfree(priv); return err; } @@ -209,7 +208,6 @@ static int timeriomem_rng_remove(struct platform_device *pdev) del_timer_sync(&priv->timer); iounmap(priv->io_base); release_mem_region(res->start, resource_size(res)); - platform_set_drvdata(pdev, NULL); kfree(priv); return 0; -- cgit v1.2.3 From 1ef997dca4ebeaca56783430de848a8d207ec879 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 6 May 2013 13:40:21 +0900 Subject: hwrng: tx4939 - remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Herbert Xu --- drivers/char/hw_random/tx4939-rng.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/char/hw_random/tx4939-rng.c b/drivers/char/hw_random/tx4939-rng.c index d34a24a0d484..00593c847cf0 100644 --- a/drivers/char/hw_random/tx4939-rng.c +++ b/drivers/char/hw_random/tx4939-rng.c @@ -154,7 +154,6 @@ static int __exit tx4939_rng_remove(struct platform_device *dev) struct tx4939_rng *rngdev = platform_get_drvdata(dev); hwrng_unregister(&rngdev->rng); - platform_set_drvdata(dev, NULL); return 0; } -- cgit v1.2.3 From 74d24d838be0ee64c1085558238e5c51e1659817 Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Sun, 12 May 2013 13:57:19 +0200 Subject: crypto: sahara - remove dependency on EXPERIMENTAL The Kconfig symbol EXPERIMENTAL was removed in v3.9. So this dependency makes it impossible to set CRYPTO_DEV_SAHARA. It's unlikely that this is what is intended, so let's remove this dependency. Signed-off-by: Paul Bolle Signed-off-by: Herbert Xu --- drivers/crypto/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index dffb85525368..47c6f4dcc675 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -278,7 +278,7 @@ config CRYPTO_DEV_PICOXCELL config CRYPTO_DEV_SAHARA tristate "Support for SAHARA crypto accelerator" - depends on ARCH_MXC && EXPERIMENTAL && OF + depends on ARCH_MXC && OF select CRYPTO_BLKCIPHER select CRYPTO_AES select CRYPTO_ECB -- cgit v1.2.3 From 786677d100600b7f6089bae0d3967c1b901a6141 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Fri, 24 May 2013 12:05:45 +0200 Subject: mac80211: add STBC flag for radiotap Some chips can tell us if received frame was encoded with STBC or not. To make this information available in user space we can use updated radiotap specification: http://www.radiotap.org/defined-fields/MCS This patch will set number of STBC encoded spatial streams (Nss). The HAVE_STBC flag should be provided by driver. Signed-off-by: Oleksij Rempel Signed-off-by: Johannes Berg --- include/net/ieee80211_radiotap.h | 7 +++++++ include/net/mac80211.h | 4 ++++ net/mac80211/rx.c | 4 ++++ 3 files changed, 15 insertions(+) diff --git a/include/net/ieee80211_radiotap.h b/include/net/ieee80211_radiotap.h index c3999632e616..c6d07cb074bc 100644 --- a/include/net/ieee80211_radiotap.h +++ b/include/net/ieee80211_radiotap.h @@ -269,6 +269,7 @@ enum ieee80211_radiotap_type { #define IEEE80211_RADIOTAP_MCS_HAVE_GI 0x04 #define IEEE80211_RADIOTAP_MCS_HAVE_FMT 0x08 #define IEEE80211_RADIOTAP_MCS_HAVE_FEC 0x10 +#define IEEE80211_RADIOTAP_MCS_HAVE_STBC 0x20 #define IEEE80211_RADIOTAP_MCS_BW_MASK 0x03 #define IEEE80211_RADIOTAP_MCS_BW_20 0 @@ -278,6 +279,12 @@ enum ieee80211_radiotap_type { #define IEEE80211_RADIOTAP_MCS_SGI 0x04 #define IEEE80211_RADIOTAP_MCS_FMT_GF 0x08 #define IEEE80211_RADIOTAP_MCS_FEC_LDPC 0x10 +#define IEEE80211_RADIOTAP_MCS_STBC_MASK 0x60 +#define IEEE80211_RADIOTAP_MCS_STBC_1 1 +#define IEEE80211_RADIOTAP_MCS_STBC_2 2 +#define IEEE80211_RADIOTAP_MCS_STBC_3 3 + +#define IEEE80211_RADIOTAP_MCS_STBC_SHIFT 5 /* For IEEE80211_RADIOTAP_AMPDU_STATUS */ #define IEEE80211_RADIOTAP_AMPDU_REPORT_ZEROLEN 0x0001 diff --git a/include/net/mac80211.h b/include/net/mac80211.h index c36535e7b291..c9e6fb7417f6 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -805,6 +805,7 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info) * on this subframe * @RX_FLAG_AMPDU_DELIM_CRC_KNOWN: The delimiter CRC field is known (the CRC * is stored in the @ampdu_delimiter_crc field) + * @RX_FLAG_STBC_MASK: STBC 2 bit bitmask. 1 - Nss=1, 2 - Nss=2, 3 - Nss=3 */ enum mac80211_rx_flags { RX_FLAG_MMIC_ERROR = BIT(0), @@ -832,8 +833,11 @@ enum mac80211_rx_flags { RX_FLAG_80MHZ = BIT(23), RX_FLAG_80P80MHZ = BIT(24), RX_FLAG_160MHZ = BIT(25), + RX_FLAG_STBC_MASK = BIT(26) | BIT(27), }; +#define RX_FLAG_STBC_SHIFT 26 + /** * struct ieee80211_rx_status - receive status * diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 6e2c8c5236c4..7507f7cdd68c 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -258,6 +258,8 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, pos += 2; if (status->flag & RX_FLAG_HT) { + unsigned int stbc; + rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_MCS); *pos++ = local->hw.radiotap_mcs_details; *pos = 0; @@ -267,6 +269,8 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, *pos |= IEEE80211_RADIOTAP_MCS_BW_40; if (status->flag & RX_FLAG_HT_GF) *pos |= IEEE80211_RADIOTAP_MCS_FMT_GF; + stbc = (status->flag & RX_FLAG_STBC_MASK) >> RX_FLAG_STBC_SHIFT; + *pos |= stbc << IEEE80211_RADIOTAP_MCS_STBC_SHIFT; pos++; *pos++ = status->rate_idx; } -- cgit v1.2.3 From e6c2e7eb27fc512af6875d7f2cf313e29c61be0b Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 24 May 2013 15:18:10 +0200 Subject: ALSA: Constify the snd_pcm_substream struct ops field The ops field of the snd_pcm_substream struct is never modified inside the ALSA core. Making it const allows drivers to declare their snd_pcm_ops struct as const. Signed-off-by: Lars-Peter Clausen Signed-off-by: Takashi Iwai --- include/sound/pcm.h | 5 +++-- sound/core/pcm_lib.c | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/include/sound/pcm.h b/include/sound/pcm.h index b48792fe386b..84b10f9a2832 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -384,7 +384,7 @@ struct snd_pcm_substream { unsigned int dma_buf_id; size_t dma_max; /* -- hardware operations -- */ - struct snd_pcm_ops *ops; + const struct snd_pcm_ops *ops; /* -- runtime information -- */ struct snd_pcm_runtime *runtime; /* -- timer section -- */ @@ -871,7 +871,8 @@ const unsigned char *snd_pcm_format_silence_64(snd_pcm_format_t format); int snd_pcm_format_set_silence(snd_pcm_format_t format, void *buf, unsigned int frames); snd_pcm_format_t snd_pcm_build_linear_format(int width, int unsigned, int big_endian); -void snd_pcm_set_ops(struct snd_pcm * pcm, int direction, struct snd_pcm_ops *ops); +void snd_pcm_set_ops(struct snd_pcm * pcm, int direction, + const struct snd_pcm_ops *ops); void snd_pcm_set_sync(struct snd_pcm_substream *substream); int snd_pcm_lib_interleave_len(struct snd_pcm_substream *substream); int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream, diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 41b3dfe68698..82bb029d4414 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -568,7 +568,8 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) * * Sets the given PCM operators to the pcm instance. */ -void snd_pcm_set_ops(struct snd_pcm *pcm, int direction, struct snd_pcm_ops *ops) +void snd_pcm_set_ops(struct snd_pcm *pcm, int direction, + const struct snd_pcm_ops *ops) { struct snd_pcm_str *stream = &pcm->streams[direction]; struct snd_pcm_substream *substream; -- cgit v1.2.3 From 8edbb198a62e2c3d0bea06ce50a4d45a009849b6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 24 May 2013 16:30:39 +0200 Subject: ALSA: Fix the default suffix string with high card number ALSA core tries to add a suffix as "_1" automatically when the given id string conflicts. The current code assumes implicitly that the max card number is 16 so that the single hex "_X" suffix can be put. However, with the dynamic device management, the card can be at most 32, so it can put even a non-hex character there. Also, when the max card number is increased in future, this would result in worse. This patch rewrites the code to add the suffix string in a simpler (thus cleaner) way. It can support up to three digits, so it should suffice for most requirements. Signed-off-by: Takashi Iwai --- sound/core/init.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/sound/core/init.c b/sound/core/init.c index 6ef06400dfc8..ed4a4811b6a1 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -549,7 +549,6 @@ static void snd_card_set_id_no_lock(struct snd_card *card, const char *src, const char *nid) { int len, loops; - bool with_suffix; bool is_default = false; char *id; @@ -565,26 +564,23 @@ static void snd_card_set_id_no_lock(struct snd_card *card, const char *src, is_default = true; } - with_suffix = false; + len = strlen(id); for (loops = 0; loops < SNDRV_CARDS; loops++) { + char *spos; + char sfxstr[5]; /* "_012" */ + int sfxlen; + if (card_id_ok(card, id)) return; /* OK */ - len = strlen(id); - if (!with_suffix) { - /* add the "_X" suffix */ - char *spos = id + len; - if (len > sizeof(card->id) - 3) - spos = id + sizeof(card->id) - 3; - strcpy(spos, "_1"); - with_suffix = true; - } else { - /* modify the existing suffix */ - if (id[len - 1] != '9') - id[len - 1]++; - else - id[len - 1] = 'A'; - } + /* Add _XYZ suffix */ + sprintf(sfxstr, "_%X", loops + 1); + sfxlen = strlen(sfxstr); + if (len + sfxlen >= sizeof(card->id)) + spos = id + sizeof(card->id) - sfxlen - 1; + else + spos = id + len; + strcpy(spos, sfxstr); } /* fallback to the default id */ if (!is_default) { -- cgit v1.2.3 From 7bb2491b35a254fe6fd592c32a142a2f2f31fe6e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 15 May 2013 08:46:39 +0200 Subject: ALSA: Add kconfig to specify the max card numbers Currently ALSA supports up to 32 card instances when the dynamic minor is used. While 32 cards are usually big enough for normal use cases, there are sometimes weird requirements with more card support. Actually, this limitation, 32, comes from the index option, where you can pass the bit mask to assign the card. Other than that, we can actually give more cards up to the minor number limits (currently 256, which can be extended more, too). This patch adds a new Kconfig to specify the max card numbers, and changes a few places to accept more than 32 cards. The only incompatibility with high card numbers would be the handling of index option. The index option can be still used to pass the bitmask for card assignments, but this works only up to 32 slots. More than 32, no bitmask style option is available but only a single slot can be specified via index option. Signed-off-by: Takashi Iwai --- include/sound/core.h | 2 +- sound/core/Kconfig | 9 +++++++++ sound/core/init.c | 25 ++++++++++++++++--------- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/include/sound/core.h b/include/sound/core.h index 5bfe5136441c..c586617cfa0d 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -30,7 +30,7 @@ /* number of supported soundcards */ #ifdef CONFIG_SND_DYNAMIC_MINORS -#define SNDRV_CARDS 32 +#define SNDRV_CARDS CONFIG_SND_MAX_CARDS #else #define SNDRV_CARDS 8 /* don't change - minor numbers */ #endif diff --git a/sound/core/Kconfig b/sound/core/Kconfig index b413ed05e74d..c0c2f57a0d6f 100644 --- a/sound/core/Kconfig +++ b/sound/core/Kconfig @@ -157,6 +157,15 @@ config SND_DYNAMIC_MINORS If you are unsure about this, say N here. +config SND_MAX_CARDS + int "Max number of sound cards" + range 4 256 + default 32 + depends on SND_DYNAMIC_MINORS + help + Specify the max number of sound cards that can be assigned + on a single machine. + config SND_SUPPORT_OLD_API bool "Support old ALSA API" default y diff --git a/sound/core/init.c b/sound/core/init.c index ed4a4811b6a1..6b9087115da2 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -46,7 +46,8 @@ static LIST_HEAD(shutdown_files); static const struct file_operations snd_shutdown_f_ops; -static unsigned int snd_cards_lock; /* locked for registering/using */ +/* locked for registering/using */ +static DECLARE_BITMAP(snd_cards_lock, SNDRV_CARDS); struct snd_card *snd_cards[SNDRV_CARDS]; EXPORT_SYMBOL(snd_cards); @@ -167,29 +168,35 @@ int snd_card_create(int idx, const char *xid, err = 0; mutex_lock(&snd_card_mutex); if (idx < 0) { - for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++) + for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++) { /* idx == -1 == 0xffff means: take any free slot */ - if (~snd_cards_lock & idx & 1<= SNDRV_CARDS) err = -ENODEV; @@ -199,7 +206,7 @@ int snd_card_create(int idx, const char *xid, idx, snd_ecards_limit - 1, err); goto __error; } - snd_cards_lock |= 1 << idx; /* lock it */ + set_bit(idx, snd_cards_lock); /* lock it */ if (idx >= snd_ecards_limit) snd_ecards_limit = idx + 1; /* increase the limit */ mutex_unlock(&snd_card_mutex); @@ -249,7 +256,7 @@ int snd_card_locked(int card) int locked; mutex_lock(&snd_card_mutex); - locked = snd_cards_lock & (1 << card); + locked = test_bit(card, snd_cards_lock); mutex_unlock(&snd_card_mutex); return locked; } @@ -361,7 +368,7 @@ int snd_card_disconnect(struct snd_card *card) /* phase 1: disable fops (user space) operations for ALSA API */ mutex_lock(&snd_card_mutex); snd_cards[card->number] = NULL; - snd_cards_lock &= ~(1 << card->number); + clear_bit(card->number, snd_cards_lock); mutex_unlock(&snd_card_mutex); /* phase 2: replace file->f_op with special dummy operations */ -- cgit v1.2.3 From a1ef50398deff2f02bad7635fbadda96e8f462e7 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Sat, 18 May 2013 14:03:24 +0200 Subject: rt2800: make rt2800_init_bbp return void This function can not fail, we always return 0. Signed-off-by: Stanislaw Gruszka Acked-by: Helmut Schaa Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index bddfd6dab3cf..8de40d63d9b9 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -4027,20 +4027,16 @@ static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 103, 0xc0); } -static int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) +static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) { unsigned int i; u16 eeprom; u8 reg_id; u8 value; - if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev) || - rt2800_wait_bbp_ready(rt2x00dev))) - return -EACCES; - if (rt2x00_rt(rt2x00dev, RT5592)) { rt2800_init_bbp_5592(rt2x00dev); - return 0; + return; } if (rt2x00_rt(rt2x00dev, RT3352)) { @@ -4337,8 +4333,6 @@ static int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, reg_id, value); } } - - return 0; } static void rt2800_led_open_drain_enable(struct rt2x00_dev *rt2x00dev) @@ -5189,9 +5183,11 @@ int rt2800_enable_radio(struct rt2x00_dev *rt2x00dev) } msleep(1); - if (unlikely(rt2800_init_bbp(rt2x00dev))) + if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev) || + rt2800_wait_bbp_ready(rt2x00dev))) return -EIO; + rt2800_init_bbp(rt2x00dev); rt2800_init_rfcsr(rt2x00dev); if (rt2x00_is_usb(rt2x00dev) && -- cgit v1.2.3 From 39ab3e8b4579384068c5081934464fd3a0dfc344 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Sat, 18 May 2013 14:03:25 +0200 Subject: rt2800: prepare for rt2800_init_bbp spit Signed-off-by: Stanislaw Gruszka Acked-by: Helmut Schaa Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 57 ++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 8de40d63d9b9..7e16771b1b1a 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -3953,6 +3953,34 @@ static void rt2800_init_bbp_early(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 106, 0x35); } +static void rt2800_init_bbp_28xx(struct rt2x00_dev *rt2x00dev) +{ +} + +static void rt2800_init_bbp_30xx(struct rt2x00_dev *rt2x00dev) +{ +} + +static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) +{ +} + +static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) +{ +} + +static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) +{ +} + +static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev) +{ +} + +static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) +{ +} + static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev) { int ant, div_mode; @@ -4034,7 +4062,34 @@ static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) u8 reg_id; u8 value; - if (rt2x00_rt(rt2x00dev, RT5592)) { + switch (rt2x00dev->chip.rt) { + case RT2860: + case RT2872: + case RT2883: + rt2800_init_bbp_28xx(rt2x00dev); + break; + case RT3070: + case RT3071: + case RT3090: + rt2800_init_bbp_30xx(rt2x00dev); + break; + case RT3290: + rt2800_init_bbp_3290(rt2x00dev); + break; + case RT3352: + rt2800_init_bbp_3352(rt2x00dev); + break; + case RT3390: + rt2800_init_bbp_3390(rt2x00dev); + break; + case RT3572: + rt2800_init_bbp_3572(rt2x00dev); + break; + case RT5390: + case RT5392: + rt2800_init_bbp_53xx(rt2x00dev); + break; + case RT5592: rt2800_init_bbp_5592(rt2x00dev); return; } -- cgit v1.2.3 From dae62957ff6d6c45bba02b00623f56e44e62e286 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Sat, 18 May 2013 14:03:26 +0200 Subject: rt2800: add rt2800_init_bbp_305x_soc subroutine New routine for SOC specific BBP initialization, empty for now. Signed-off-by: Stanislaw Gruszka Acked-by: Helmut Schaa Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 7e16771b1b1a..ba1a989b6d3b 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -3953,6 +3953,10 @@ static void rt2800_init_bbp_early(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 106, 0x35); } +static void rt2800_init_bbp_305x_soc(struct rt2x00_dev *rt2x00dev) +{ +} + static void rt2800_init_bbp_28xx(struct rt2x00_dev *rt2x00dev) { } @@ -4062,6 +4066,9 @@ static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) u8 reg_id; u8 value; + if (rt2800_is_305x_soc(rt2x00dev)) + rt2800_init_bbp_305x_soc(rt2x00dev); + switch (rt2x00dev->chip.rt) { case RT2860: case RT2872: -- cgit v1.2.3 From 29f3a58b9025683e088205f3db9e51d5294957b7 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Sat, 18 May 2013 14:03:27 +0200 Subject: rt2800: move 3352 bbp specific code Signed-off-by: Stanislaw Gruszka Acked-by: Helmut Schaa Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index ba1a989b6d3b..11030ff57bab 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -3971,6 +3971,8 @@ static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) { + rt2800_bbp_write(rt2x00dev, 3, 0x00); + rt2800_bbp_write(rt2x00dev, 4, 0x50); } static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) @@ -4101,11 +4103,6 @@ static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) return; } - if (rt2x00_rt(rt2x00dev, RT3352)) { - rt2800_bbp_write(rt2x00dev, 3, 0x00); - rt2800_bbp_write(rt2x00dev, 4, 0x50); - } - if (rt2x00_rt(rt2x00dev, RT3290) || rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392)) -- cgit v1.2.3 From c3223573520e73c4aee303a122a9ea6a9342a010 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Sat, 18 May 2013 14:03:28 +0200 Subject: rt2800: move rt2800_bbp4_mac_if_ctrl to proper subroutines Signed-off-by: Stanislaw Gruszka Acked-by: Helmut Schaa Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 11030ff57bab..687abd539dff 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -3967,6 +3967,7 @@ static void rt2800_init_bbp_30xx(struct rt2x00_dev *rt2x00dev) static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) { + rt2800_bbp4_mac_if_ctrl(rt2x00dev); } static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) @@ -3985,6 +3986,7 @@ static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev) static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) { + rt2800_bbp4_mac_if_ctrl(rt2x00dev); } static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev) @@ -4103,11 +4105,6 @@ static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) return; } - if (rt2x00_rt(rt2x00dev, RT3290) || - rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) - rt2800_bbp4_mac_if_ctrl(rt2x00dev); - if (rt2800_is_305x_soc(rt2x00dev) || rt2x00_rt(rt2x00dev, RT3290) || rt2x00_rt(rt2x00dev, RT3352) || -- cgit v1.2.3 From b2f8e0bd2624d458cfa4d46a4b49803ef55f7b3f Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Sat, 18 May 2013 14:03:29 +0200 Subject: rt2800: initialize BBP_R31 on proper subroutines Signed-off-by: Stanislaw Gruszka Acked-by: Helmut Schaa Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 687abd539dff..c74105822fd4 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -3955,6 +3955,7 @@ static void rt2800_init_bbp_early(struct rt2x00_dev *rt2x00dev) static void rt2800_init_bbp_305x_soc(struct rt2x00_dev *rt2x00dev) { + rt2800_bbp_write(rt2x00dev, 31, 0x08); } static void rt2800_init_bbp_28xx(struct rt2x00_dev *rt2x00dev) @@ -3968,12 +3969,16 @@ static void rt2800_init_bbp_30xx(struct rt2x00_dev *rt2x00dev) static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) { rt2800_bbp4_mac_if_ctrl(rt2x00dev); + + rt2800_bbp_write(rt2x00dev, 31, 0x08); } static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) { rt2800_bbp_write(rt2x00dev, 3, 0x00); rt2800_bbp_write(rt2x00dev, 4, 0x50); + + rt2800_bbp_write(rt2x00dev, 31, 0x08); } static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) @@ -3982,11 +3987,14 @@ static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev) { + rt2800_bbp_write(rt2x00dev, 31, 0x08); } static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) { rt2800_bbp4_mac_if_ctrl(rt2x00dev); + + rt2800_bbp_write(rt2x00dev, 31, 0x08); } static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev) @@ -4105,14 +4113,6 @@ static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) return; } - if (rt2800_is_305x_soc(rt2x00dev) || - rt2x00_rt(rt2x00dev, RT3290) || - rt2x00_rt(rt2x00dev, RT3352) || - rt2x00_rt(rt2x00dev, RT3572) || - rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) - rt2800_bbp_write(rt2x00dev, 31, 0x08); - if (rt2x00_rt(rt2x00dev, RT3352)) rt2800_bbp_write(rt2x00dev, 47, 0x48); -- cgit v1.2.3 From 3420f797536a52fd78ad8b692045bd7fa6a2ab32 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Sat, 18 May 2013 14:03:30 +0200 Subject: rt2800: initialize BBP_R47 on 3352 subroutine Signed-off-by: Stanislaw Gruszka Acked-by: Helmut Schaa Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index c74105822fd4..5259936e2592 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -3979,6 +3979,8 @@ static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 4, 0x50); rt2800_bbp_write(rt2x00dev, 31, 0x08); + + rt2800_bbp_write(rt2x00dev, 47, 0x48); } static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) @@ -4113,9 +4115,6 @@ static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) return; } - if (rt2x00_rt(rt2x00dev, RT3352)) - rt2800_bbp_write(rt2x00dev, 47, 0x48); - rt2800_bbp_write(rt2x00dev, 65, 0x2c); rt2800_bbp_write(rt2x00dev, 66, 0x38); -- cgit v1.2.3 From e379de1297a1edc05250c408af7f474b2a1cbacc Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Sat, 18 May 2013 14:03:31 +0200 Subject: rt2800: initialize BBP_R65 & BBP_R66 on all subroutines (except 5592) Signed-off-by: Stanislaw Gruszka Acked-by: Helmut Schaa Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 5259936e2592..18c5925e2a61 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -3956,14 +3956,21 @@ static void rt2800_init_bbp_early(struct rt2x00_dev *rt2x00dev) static void rt2800_init_bbp_305x_soc(struct rt2x00_dev *rt2x00dev) { rt2800_bbp_write(rt2x00dev, 31, 0x08); + + rt2800_bbp_write(rt2x00dev, 65, 0x2c); + rt2800_bbp_write(rt2x00dev, 66, 0x38); } static void rt2800_init_bbp_28xx(struct rt2x00_dev *rt2x00dev) { + rt2800_bbp_write(rt2x00dev, 65, 0x2c); + rt2800_bbp_write(rt2x00dev, 66, 0x38); } static void rt2800_init_bbp_30xx(struct rt2x00_dev *rt2x00dev) { + rt2800_bbp_write(rt2x00dev, 65, 0x2c); + rt2800_bbp_write(rt2x00dev, 66, 0x38); } static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) @@ -3971,6 +3978,9 @@ static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) rt2800_bbp4_mac_if_ctrl(rt2x00dev); rt2800_bbp_write(rt2x00dev, 31, 0x08); + + rt2800_bbp_write(rt2x00dev, 65, 0x2c); + rt2800_bbp_write(rt2x00dev, 66, 0x38); } static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) @@ -3981,15 +3991,23 @@ static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 31, 0x08); rt2800_bbp_write(rt2x00dev, 47, 0x48); + + rt2800_bbp_write(rt2x00dev, 65, 0x2c); + rt2800_bbp_write(rt2x00dev, 66, 0x38); } static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) { + rt2800_bbp_write(rt2x00dev, 65, 0x2c); + rt2800_bbp_write(rt2x00dev, 66, 0x38); } static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev) { rt2800_bbp_write(rt2x00dev, 31, 0x08); + + rt2800_bbp_write(rt2x00dev, 65, 0x2c); + rt2800_bbp_write(rt2x00dev, 66, 0x38); } static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) @@ -3997,6 +4015,9 @@ static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp4_mac_if_ctrl(rt2x00dev); rt2800_bbp_write(rt2x00dev, 31, 0x08); + + rt2800_bbp_write(rt2x00dev, 65, 0x2c); + rt2800_bbp_write(rt2x00dev, 66, 0x38); } static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev) @@ -4115,9 +4136,6 @@ static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) return; } - rt2800_bbp_write(rt2x00dev, 65, 0x2c); - rt2800_bbp_write(rt2x00dev, 66, 0x38); - if (rt2x00_rt(rt2x00dev, RT3290) || rt2x00_rt(rt2x00dev, RT3352) || rt2x00_rt(rt2x00dev, RT5390) || -- cgit v1.2.3 From 59dcabb5c12148c2e4d57dfaeea3af2098bd02d2 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Sat, 18 May 2013 14:03:32 +0200 Subject: rt2800: initialize BBP_R68 on proper subroutines Signed-off-by: Stanislaw Gruszka Acked-by: Helmut Schaa Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 18c5925e2a61..54a7690a5031 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -3981,6 +3981,8 @@ static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 65, 0x2c); rt2800_bbp_write(rt2x00dev, 66, 0x38); + + rt2800_bbp_write(rt2x00dev, 68, 0x0b); } static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) @@ -3994,6 +3996,8 @@ static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 65, 0x2c); rt2800_bbp_write(rt2x00dev, 66, 0x38); + + rt2800_bbp_write(rt2x00dev, 68, 0x0b); } static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) @@ -4018,6 +4022,8 @@ static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 65, 0x2c); rt2800_bbp_write(rt2x00dev, 66, 0x38); + + rt2800_bbp_write(rt2x00dev, 68, 0x0b); } static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev) @@ -4136,12 +4142,6 @@ static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) return; } - if (rt2x00_rt(rt2x00dev, RT3290) || - rt2x00_rt(rt2x00dev, RT3352) || - rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) - rt2800_bbp_write(rt2x00dev, 68, 0x0b); - if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860C)) { rt2800_bbp_write(rt2x00dev, 69, 0x16); rt2800_bbp_write(rt2x00dev, 73, 0x12); -- cgit v1.2.3 From 72ffe1426b47275b464aa946d47b4de10b5e3a8f Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Sat, 18 May 2013 14:03:33 +0200 Subject: rt2800: initialize BBP_R69 - BBP_R77 on proper subroutines Signed-off-by: Stanislaw Gruszka Acked-by: Helmut Schaa Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 62 ++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 54a7690a5031..26feb99a0aa8 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -3959,18 +3959,32 @@ static void rt2800_init_bbp_305x_soc(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 65, 0x2c); rt2800_bbp_write(rt2x00dev, 66, 0x38); + + rt2800_bbp_write(rt2x00dev, 69, 0x12); + rt2800_bbp_write(rt2x00dev, 73, 0x10); } static void rt2800_init_bbp_28xx(struct rt2x00_dev *rt2x00dev) { rt2800_bbp_write(rt2x00dev, 65, 0x2c); rt2800_bbp_write(rt2x00dev, 66, 0x38); + + if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860C)) { + rt2800_bbp_write(rt2x00dev, 69, 0x16); + rt2800_bbp_write(rt2x00dev, 73, 0x12); + } else { + rt2800_bbp_write(rt2x00dev, 69, 0x12); + rt2800_bbp_write(rt2x00dev, 73, 0x10); + } } static void rt2800_init_bbp_30xx(struct rt2x00_dev *rt2x00dev) { rt2800_bbp_write(rt2x00dev, 65, 0x2c); rt2800_bbp_write(rt2x00dev, 66, 0x38); + + rt2800_bbp_write(rt2x00dev, 69, 0x12); + rt2800_bbp_write(rt2x00dev, 73, 0x10); } static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) @@ -3983,6 +3997,13 @@ static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 66, 0x38); rt2800_bbp_write(rt2x00dev, 68, 0x0b); + + rt2800_bbp_write(rt2x00dev, 69, 0x12); + rt2800_bbp_write(rt2x00dev, 73, 0x13); + rt2800_bbp_write(rt2x00dev, 75, 0x46); + rt2800_bbp_write(rt2x00dev, 76, 0x28); + + rt2800_bbp_write(rt2x00dev, 77, 0x58); } static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) @@ -3998,12 +4019,22 @@ static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 66, 0x38); rt2800_bbp_write(rt2x00dev, 68, 0x0b); + + rt2800_bbp_write(rt2x00dev, 69, 0x12); + rt2800_bbp_write(rt2x00dev, 73, 0x13); + rt2800_bbp_write(rt2x00dev, 75, 0x46); + rt2800_bbp_write(rt2x00dev, 76, 0x28); + + rt2800_bbp_write(rt2x00dev, 77, 0x59); } static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) { rt2800_bbp_write(rt2x00dev, 65, 0x2c); rt2800_bbp_write(rt2x00dev, 66, 0x38); + + rt2800_bbp_write(rt2x00dev, 69, 0x12); + rt2800_bbp_write(rt2x00dev, 73, 0x10); } static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev) @@ -4012,6 +4043,9 @@ static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 65, 0x2c); rt2800_bbp_write(rt2x00dev, 66, 0x38); + + rt2800_bbp_write(rt2x00dev, 69, 0x12); + rt2800_bbp_write(rt2x00dev, 73, 0x10); } static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) @@ -4024,6 +4058,13 @@ static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 66, 0x38); rt2800_bbp_write(rt2x00dev, 68, 0x0b); + + rt2800_bbp_write(rt2x00dev, 69, 0x12); + rt2800_bbp_write(rt2x00dev, 73, 0x13); + rt2800_bbp_write(rt2x00dev, 75, 0x46); + rt2800_bbp_write(rt2x00dev, 76, 0x28); + + rt2800_bbp_write(rt2x00dev, 77, 0x59); } static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev) @@ -4142,27 +4183,6 @@ static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) return; } - if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860C)) { - rt2800_bbp_write(rt2x00dev, 69, 0x16); - rt2800_bbp_write(rt2x00dev, 73, 0x12); - } else if (rt2x00_rt(rt2x00dev, RT3290) || - rt2x00_rt(rt2x00dev, RT3352) || - rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) { - rt2800_bbp_write(rt2x00dev, 69, 0x12); - rt2800_bbp_write(rt2x00dev, 73, 0x13); - rt2800_bbp_write(rt2x00dev, 75, 0x46); - rt2800_bbp_write(rt2x00dev, 76, 0x28); - - if (rt2x00_rt(rt2x00dev, RT3290)) - rt2800_bbp_write(rt2x00dev, 77, 0x58); - else - rt2800_bbp_write(rt2x00dev, 77, 0x59); - } else { - rt2800_bbp_write(rt2x00dev, 69, 0x12); - rt2800_bbp_write(rt2x00dev, 73, 0x10); - } - rt2800_bbp_write(rt2x00dev, 70, 0x0a); if (rt2x00_rt(rt2x00dev, RT3070) || -- cgit v1.2.3 From 8d97be386415aa771bbe923072da773ff09ad997 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Sat, 18 May 2013 14:03:34 +0200 Subject: rt2800: initialize BBP_R70 on all subroutines (except 5592) Signed-off-by: Stanislaw Gruszka Acked-by: Helmut Schaa Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 26feb99a0aa8..8ad20dbb2613 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -3962,6 +3962,8 @@ static void rt2800_init_bbp_305x_soc(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 69, 0x12); rt2800_bbp_write(rt2x00dev, 73, 0x10); + + rt2800_bbp_write(rt2x00dev, 70, 0x0a); } static void rt2800_init_bbp_28xx(struct rt2x00_dev *rt2x00dev) @@ -3976,6 +3978,8 @@ static void rt2800_init_bbp_28xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 69, 0x12); rt2800_bbp_write(rt2x00dev, 73, 0x10); } + + rt2800_bbp_write(rt2x00dev, 70, 0x0a); } static void rt2800_init_bbp_30xx(struct rt2x00_dev *rt2x00dev) @@ -3985,6 +3989,8 @@ static void rt2800_init_bbp_30xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 69, 0x12); rt2800_bbp_write(rt2x00dev, 73, 0x10); + + rt2800_bbp_write(rt2x00dev, 70, 0x0a); } static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) @@ -4004,6 +4010,8 @@ static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 76, 0x28); rt2800_bbp_write(rt2x00dev, 77, 0x58); + + rt2800_bbp_write(rt2x00dev, 70, 0x0a); } static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) @@ -4026,6 +4034,8 @@ static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 76, 0x28); rt2800_bbp_write(rt2x00dev, 77, 0x59); + + rt2800_bbp_write(rt2x00dev, 70, 0x0a); } static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) @@ -4035,6 +4045,8 @@ static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 69, 0x12); rt2800_bbp_write(rt2x00dev, 73, 0x10); + + rt2800_bbp_write(rt2x00dev, 70, 0x0a); } static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev) @@ -4046,6 +4058,8 @@ static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 69, 0x12); rt2800_bbp_write(rt2x00dev, 73, 0x10); + + rt2800_bbp_write(rt2x00dev, 70, 0x0a); } static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) @@ -4065,6 +4079,8 @@ static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 76, 0x28); rt2800_bbp_write(rt2x00dev, 77, 0x59); + + rt2800_bbp_write(rt2x00dev, 70, 0x0a); } static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev) @@ -4183,8 +4199,6 @@ static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) return; } - rt2800_bbp_write(rt2x00dev, 70, 0x0a); - if (rt2x00_rt(rt2x00dev, RT3070) || rt2x00_rt(rt2x00dev, RT3071) || rt2x00_rt(rt2x00dev, RT3090) || -- cgit v1.2.3 From 43f535e2f998c617bbe1622240b2f3290f577d1e Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Sat, 18 May 2013 14:03:35 +0200 Subject: rt2800: initialize BBP_R74 - BBP_R80 on all subroutines (except 5592) Signed-off-by: Stanislaw Gruszka Acked-by: Helmut Schaa Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 56 ++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 8ad20dbb2613..508c9c4dedb4 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -3964,6 +3964,9 @@ static void rt2800_init_bbp_305x_soc(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 73, 0x10); rt2800_bbp_write(rt2x00dev, 70, 0x0a); + + rt2800_bbp_write(rt2x00dev, 78, 0x0e); + rt2800_bbp_write(rt2x00dev, 80, 0x08); } static void rt2800_init_bbp_28xx(struct rt2x00_dev *rt2x00dev) @@ -3980,6 +3983,8 @@ static void rt2800_init_bbp_28xx(struct rt2x00_dev *rt2x00dev) } rt2800_bbp_write(rt2x00dev, 70, 0x0a); + + rt2800_bbp_write(rt2x00dev, 81, 0x37); } static void rt2800_init_bbp_30xx(struct rt2x00_dev *rt2x00dev) @@ -3991,6 +3996,10 @@ static void rt2800_init_bbp_30xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 73, 0x10); rt2800_bbp_write(rt2x00dev, 70, 0x0a); + + rt2800_bbp_write(rt2x00dev, 79, 0x13); + rt2800_bbp_write(rt2x00dev, 80, 0x05); + rt2800_bbp_write(rt2x00dev, 81, 0x33); } static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) @@ -4012,6 +4021,11 @@ static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 77, 0x58); rt2800_bbp_write(rt2x00dev, 70, 0x0a); + + rt2800_bbp_write(rt2x00dev, 74, 0x0b); + rt2800_bbp_write(rt2x00dev, 79, 0x18); + rt2800_bbp_write(rt2x00dev, 80, 0x09); + rt2800_bbp_write(rt2x00dev, 81, 0x33); } static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) @@ -4036,6 +4050,10 @@ static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 77, 0x59); rt2800_bbp_write(rt2x00dev, 70, 0x0a); + + rt2800_bbp_write(rt2x00dev, 78, 0x0e); + rt2800_bbp_write(rt2x00dev, 80, 0x08); + rt2800_bbp_write(rt2x00dev, 81, 0x37); } static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) @@ -4047,6 +4065,10 @@ static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 73, 0x10); rt2800_bbp_write(rt2x00dev, 70, 0x0a); + + rt2800_bbp_write(rt2x00dev, 79, 0x13); + rt2800_bbp_write(rt2x00dev, 80, 0x05); + rt2800_bbp_write(rt2x00dev, 81, 0x33); } static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev) @@ -4060,6 +4082,10 @@ static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 73, 0x10); rt2800_bbp_write(rt2x00dev, 70, 0x0a); + + rt2800_bbp_write(rt2x00dev, 79, 0x13); + rt2800_bbp_write(rt2x00dev, 80, 0x05); + rt2800_bbp_write(rt2x00dev, 81, 0x33); } static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) @@ -4081,6 +4107,10 @@ static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 77, 0x59); rt2800_bbp_write(rt2x00dev, 70, 0x0a); + + rt2800_bbp_write(rt2x00dev, 79, 0x13); + rt2800_bbp_write(rt2x00dev, 80, 0x05); + rt2800_bbp_write(rt2x00dev, 81, 0x33); } static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev) @@ -4199,32 +4229,6 @@ static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) return; } - if (rt2x00_rt(rt2x00dev, RT3070) || - rt2x00_rt(rt2x00dev, RT3071) || - rt2x00_rt(rt2x00dev, RT3090) || - rt2x00_rt(rt2x00dev, RT3390) || - rt2x00_rt(rt2x00dev, RT3572) || - rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) { - rt2800_bbp_write(rt2x00dev, 79, 0x13); - rt2800_bbp_write(rt2x00dev, 80, 0x05); - rt2800_bbp_write(rt2x00dev, 81, 0x33); - } else if (rt2800_is_305x_soc(rt2x00dev)) { - rt2800_bbp_write(rt2x00dev, 78, 0x0e); - rt2800_bbp_write(rt2x00dev, 80, 0x08); - } else if (rt2x00_rt(rt2x00dev, RT3290)) { - rt2800_bbp_write(rt2x00dev, 74, 0x0b); - rt2800_bbp_write(rt2x00dev, 79, 0x18); - rt2800_bbp_write(rt2x00dev, 80, 0x09); - rt2800_bbp_write(rt2x00dev, 81, 0x33); - } else if (rt2x00_rt(rt2x00dev, RT3352)) { - rt2800_bbp_write(rt2x00dev, 78, 0x0e); - rt2800_bbp_write(rt2x00dev, 80, 0x08); - rt2800_bbp_write(rt2x00dev, 81, 0x37); - } else { - rt2800_bbp_write(rt2x00dev, 81, 0x37); - } - rt2800_bbp_write(rt2x00dev, 82, 0x62); if (rt2x00_rt(rt2x00dev, RT3290) || rt2x00_rt(rt2x00dev, RT5390) || -- cgit v1.2.3 From fa1e34243ff3fb1532ca3b817fd9129ee62d6d06 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Sat, 18 May 2013 14:03:36 +0200 Subject: rt2800: initialize BBP_R82 on all subroutines (except 5592) Signed-off-by: Stanislaw Gruszka Acked-by: Helmut Schaa Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 508c9c4dedb4..aad340f5750e 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -3967,6 +3967,8 @@ static void rt2800_init_bbp_305x_soc(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 78, 0x0e); rt2800_bbp_write(rt2x00dev, 80, 0x08); + + rt2800_bbp_write(rt2x00dev, 82, 0x62); } static void rt2800_init_bbp_28xx(struct rt2x00_dev *rt2x00dev) @@ -3985,6 +3987,8 @@ static void rt2800_init_bbp_28xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 70, 0x0a); rt2800_bbp_write(rt2x00dev, 81, 0x37); + + rt2800_bbp_write(rt2x00dev, 82, 0x62); } static void rt2800_init_bbp_30xx(struct rt2x00_dev *rt2x00dev) @@ -4000,6 +4004,8 @@ static void rt2800_init_bbp_30xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 79, 0x13); rt2800_bbp_write(rt2x00dev, 80, 0x05); rt2800_bbp_write(rt2x00dev, 81, 0x33); + + rt2800_bbp_write(rt2x00dev, 82, 0x62); } static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) @@ -4026,6 +4032,8 @@ static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 79, 0x18); rt2800_bbp_write(rt2x00dev, 80, 0x09); rt2800_bbp_write(rt2x00dev, 81, 0x33); + + rt2800_bbp_write(rt2x00dev, 82, 0x62); } static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) @@ -4054,6 +4062,8 @@ static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 78, 0x0e); rt2800_bbp_write(rt2x00dev, 80, 0x08); rt2800_bbp_write(rt2x00dev, 81, 0x37); + + rt2800_bbp_write(rt2x00dev, 82, 0x62); } static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) @@ -4069,6 +4079,8 @@ static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 79, 0x13); rt2800_bbp_write(rt2x00dev, 80, 0x05); rt2800_bbp_write(rt2x00dev, 81, 0x33); + + rt2800_bbp_write(rt2x00dev, 82, 0x62); } static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev) @@ -4086,6 +4098,8 @@ static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 79, 0x13); rt2800_bbp_write(rt2x00dev, 80, 0x05); rt2800_bbp_write(rt2x00dev, 81, 0x33); + + rt2800_bbp_write(rt2x00dev, 82, 0x62); } static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) @@ -4111,6 +4125,8 @@ static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 79, 0x13); rt2800_bbp_write(rt2x00dev, 80, 0x05); rt2800_bbp_write(rt2x00dev, 81, 0x33); + + rt2800_bbp_write(rt2x00dev, 82, 0x62); } static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev) @@ -4229,7 +4245,6 @@ static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) return; } - rt2800_bbp_write(rt2x00dev, 82, 0x62); if (rt2x00_rt(rt2x00dev, RT3290) || rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392)) -- cgit v1.2.3 From 885f2414361709f53ed5566e66f3bd88c9ebe2b5 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Sat, 18 May 2013 14:03:37 +0200 Subject: rt2800: initialize BBP_R83 on all subroutines (except 5592) Signed-off-by: Stanislaw Gruszka Acked-by: Helmut Schaa Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index aad340f5750e..79a95a44e575 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -3969,6 +3969,8 @@ static void rt2800_init_bbp_305x_soc(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 80, 0x08); rt2800_bbp_write(rt2x00dev, 82, 0x62); + + rt2800_bbp_write(rt2x00dev, 83, 0x6a); } static void rt2800_init_bbp_28xx(struct rt2x00_dev *rt2x00dev) @@ -3989,6 +3991,8 @@ static void rt2800_init_bbp_28xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 81, 0x37); rt2800_bbp_write(rt2x00dev, 82, 0x62); + + rt2800_bbp_write(rt2x00dev, 83, 0x6a); } static void rt2800_init_bbp_30xx(struct rt2x00_dev *rt2x00dev) @@ -4006,6 +4010,8 @@ static void rt2800_init_bbp_30xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 81, 0x33); rt2800_bbp_write(rt2x00dev, 82, 0x62); + + rt2800_bbp_write(rt2x00dev, 83, 0x6a); } static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) @@ -4034,6 +4040,8 @@ static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 81, 0x33); rt2800_bbp_write(rt2x00dev, 82, 0x62); + + rt2800_bbp_write(rt2x00dev, 83, 0x7a); } static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) @@ -4064,6 +4072,8 @@ static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 81, 0x37); rt2800_bbp_write(rt2x00dev, 82, 0x62); + + rt2800_bbp_write(rt2x00dev, 83, 0x6a); } static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) @@ -4081,6 +4091,8 @@ static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 81, 0x33); rt2800_bbp_write(rt2x00dev, 82, 0x62); + + rt2800_bbp_write(rt2x00dev, 83, 0x6a); } static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev) @@ -4100,6 +4112,8 @@ static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 81, 0x33); rt2800_bbp_write(rt2x00dev, 82, 0x62); + + rt2800_bbp_write(rt2x00dev, 83, 0x6a); } static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) @@ -4127,6 +4141,8 @@ static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 81, 0x33); rt2800_bbp_write(rt2x00dev, 82, 0x62); + + rt2800_bbp_write(rt2x00dev, 83, 0x7a); } static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev) @@ -4245,13 +4261,6 @@ static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) return; } - if (rt2x00_rt(rt2x00dev, RT3290) || - rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) - rt2800_bbp_write(rt2x00dev, 83, 0x7a); - else - rt2800_bbp_write(rt2x00dev, 83, 0x6a); - if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860D)) rt2800_bbp_write(rt2x00dev, 84, 0x19); else if (rt2x00_rt(rt2x00dev, RT3290) || -- cgit v1.2.3 From 3c20a1229dce70b16f7e0f444d23f9b16e15a7e2 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Sat, 18 May 2013 14:03:38 +0200 Subject: rt2800: initialize BBP_R84 on all subroutines (except 5592) Signed-off-by: Stanislaw Gruszka Acked-by: Helmut Schaa Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 79a95a44e575..2f14ede9e959 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -3971,6 +3971,8 @@ static void rt2800_init_bbp_305x_soc(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 82, 0x62); rt2800_bbp_write(rt2x00dev, 83, 0x6a); + + rt2800_bbp_write(rt2x00dev, 84, 0x99); } static void rt2800_init_bbp_28xx(struct rt2x00_dev *rt2x00dev) @@ -3993,6 +3995,11 @@ static void rt2800_init_bbp_28xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 82, 0x62); rt2800_bbp_write(rt2x00dev, 83, 0x6a); + + if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860D)) + rt2800_bbp_write(rt2x00dev, 84, 0x19); + else + rt2800_bbp_write(rt2x00dev, 84, 0x99); } static void rt2800_init_bbp_30xx(struct rt2x00_dev *rt2x00dev) @@ -4012,6 +4019,8 @@ static void rt2800_init_bbp_30xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 82, 0x62); rt2800_bbp_write(rt2x00dev, 83, 0x6a); + + rt2800_bbp_write(rt2x00dev, 84, 0x99); } static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) @@ -4042,6 +4051,8 @@ static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 82, 0x62); rt2800_bbp_write(rt2x00dev, 83, 0x7a); + + rt2800_bbp_write(rt2x00dev, 84, 0x9a); } static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) @@ -4074,6 +4085,8 @@ static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 82, 0x62); rt2800_bbp_write(rt2x00dev, 83, 0x6a); + + rt2800_bbp_write(rt2x00dev, 84, 0x99); } static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) @@ -4093,6 +4106,8 @@ static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 82, 0x62); rt2800_bbp_write(rt2x00dev, 83, 0x6a); + + rt2800_bbp_write(rt2x00dev, 84, 0x99); } static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev) @@ -4114,6 +4129,8 @@ static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 82, 0x62); rt2800_bbp_write(rt2x00dev, 83, 0x6a); + + rt2800_bbp_write(rt2x00dev, 84, 0x99); } static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) @@ -4143,6 +4160,8 @@ static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 82, 0x62); rt2800_bbp_write(rt2x00dev, 83, 0x7a); + + rt2800_bbp_write(rt2x00dev, 84, 0x9a); } static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev) @@ -4261,15 +4280,6 @@ static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) return; } - if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860D)) - rt2800_bbp_write(rt2x00dev, 84, 0x19); - else if (rt2x00_rt(rt2x00dev, RT3290) || - rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) - rt2800_bbp_write(rt2x00dev, 84, 0x9a); - else - rt2800_bbp_write(rt2x00dev, 84, 0x99); - if (rt2x00_rt(rt2x00dev, RT3290) || rt2x00_rt(rt2x00dev, RT3352) || rt2x00_rt(rt2x00dev, RT5390) || -- cgit v1.2.3 From aef9f38b8d1d729935fab916b4581af669baa849 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Sat, 18 May 2013 14:03:39 +0200 Subject: rt2800: initialize BBP_R86 on all subroutines (except 5592) Signed-off-by: Stanislaw Gruszka Acked-by: Helmut Schaa Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 2f14ede9e959..dafb52b349fb 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -3973,6 +3973,8 @@ static void rt2800_init_bbp_305x_soc(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 83, 0x6a); rt2800_bbp_write(rt2x00dev, 84, 0x99); + + rt2800_bbp_write(rt2x00dev, 86, 0x00); } static void rt2800_init_bbp_28xx(struct rt2x00_dev *rt2x00dev) @@ -4000,6 +4002,8 @@ static void rt2800_init_bbp_28xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 84, 0x19); else rt2800_bbp_write(rt2x00dev, 84, 0x99); + + rt2800_bbp_write(rt2x00dev, 86, 0x00); } static void rt2800_init_bbp_30xx(struct rt2x00_dev *rt2x00dev) @@ -4021,6 +4025,8 @@ static void rt2800_init_bbp_30xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 83, 0x6a); rt2800_bbp_write(rt2x00dev, 84, 0x99); + + rt2800_bbp_write(rt2x00dev, 86, 0x00); } static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) @@ -4053,6 +4059,8 @@ static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 83, 0x7a); rt2800_bbp_write(rt2x00dev, 84, 0x9a); + + rt2800_bbp_write(rt2x00dev, 86, 0x38); } static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) @@ -4087,6 +4095,8 @@ static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 83, 0x6a); rt2800_bbp_write(rt2x00dev, 84, 0x99); + + rt2800_bbp_write(rt2x00dev, 86, 0x38); } static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) @@ -4108,6 +4118,8 @@ static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 83, 0x6a); rt2800_bbp_write(rt2x00dev, 84, 0x99); + + rt2800_bbp_write(rt2x00dev, 86, 0x00); } static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev) @@ -4131,6 +4143,8 @@ static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 83, 0x6a); rt2800_bbp_write(rt2x00dev, 84, 0x99); + + rt2800_bbp_write(rt2x00dev, 86, 0x00); } static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) @@ -4162,6 +4176,8 @@ static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 83, 0x7a); rt2800_bbp_write(rt2x00dev, 84, 0x9a); + + rt2800_bbp_write(rt2x00dev, 86, 0x38); } static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev) @@ -4280,14 +4296,6 @@ static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) return; } - if (rt2x00_rt(rt2x00dev, RT3290) || - rt2x00_rt(rt2x00dev, RT3352) || - rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) - rt2800_bbp_write(rt2x00dev, 86, 0x38); - else - rt2800_bbp_write(rt2x00dev, 86, 0x00); - if (rt2x00_rt(rt2x00dev, RT3352) || rt2x00_rt(rt2x00dev, RT5392)) rt2800_bbp_write(rt2x00dev, 88, 0x90); -- cgit v1.2.3 From 9400fa8745d01e4e0e0ec893620800a96c6138ea Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Sat, 18 May 2013 14:03:40 +0200 Subject: rt2800: initialize BBP_R88 on proper subroutines Signed-off-by: Stanislaw Gruszka Acked-by: Helmut Schaa Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index dafb52b349fb..1f50bfbfbc25 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -4097,6 +4097,8 @@ static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 84, 0x99); rt2800_bbp_write(rt2x00dev, 86, 0x38); + + rt2800_bbp_write(rt2x00dev, 88, 0x90); } static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) @@ -4178,6 +4180,9 @@ static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 84, 0x9a); rt2800_bbp_write(rt2x00dev, 86, 0x38); + + if (rt2x00_rt(rt2x00dev, RT5392)) + rt2800_bbp_write(rt2x00dev, 88, 0x90); } static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev) @@ -4296,10 +4301,6 @@ static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) return; } - if (rt2x00_rt(rt2x00dev, RT3352) || - rt2x00_rt(rt2x00dev, RT5392)) - rt2800_bbp_write(rt2x00dev, 88, 0x90); - rt2800_bbp_write(rt2x00dev, 91, 0x04); if (rt2x00_rt(rt2x00dev, RT3290) || -- cgit v1.2.3 From 7af987420e9cb7d371adda02e880fcb9b9a6affd Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Sat, 18 May 2013 14:03:41 +0200 Subject: rt2800: initialize BBP_R91 on all subroutines (except 5592) Signed-off-by: Stanislaw Gruszka Acked-by: Helmut Schaa Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 1f50bfbfbc25..c5a2ca25b07d 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -3975,6 +3975,8 @@ static void rt2800_init_bbp_305x_soc(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 84, 0x99); rt2800_bbp_write(rt2x00dev, 86, 0x00); + + rt2800_bbp_write(rt2x00dev, 91, 0x04); } static void rt2800_init_bbp_28xx(struct rt2x00_dev *rt2x00dev) @@ -4004,6 +4006,8 @@ static void rt2800_init_bbp_28xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 84, 0x99); rt2800_bbp_write(rt2x00dev, 86, 0x00); + + rt2800_bbp_write(rt2x00dev, 91, 0x04); } static void rt2800_init_bbp_30xx(struct rt2x00_dev *rt2x00dev) @@ -4027,6 +4031,8 @@ static void rt2800_init_bbp_30xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 84, 0x99); rt2800_bbp_write(rt2x00dev, 86, 0x00); + + rt2800_bbp_write(rt2x00dev, 91, 0x04); } static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) @@ -4061,6 +4067,8 @@ static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 84, 0x9a); rt2800_bbp_write(rt2x00dev, 86, 0x38); + + rt2800_bbp_write(rt2x00dev, 91, 0x04); } static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) @@ -4099,6 +4107,8 @@ static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 86, 0x38); rt2800_bbp_write(rt2x00dev, 88, 0x90); + + rt2800_bbp_write(rt2x00dev, 91, 0x04); } static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) @@ -4122,6 +4132,8 @@ static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 84, 0x99); rt2800_bbp_write(rt2x00dev, 86, 0x00); + + rt2800_bbp_write(rt2x00dev, 91, 0x04); } static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev) @@ -4147,6 +4159,8 @@ static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 84, 0x99); rt2800_bbp_write(rt2x00dev, 86, 0x00); + + rt2800_bbp_write(rt2x00dev, 91, 0x04); } static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) @@ -4183,6 +4197,8 @@ static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) if (rt2x00_rt(rt2x00dev, RT5392)) rt2800_bbp_write(rt2x00dev, 88, 0x90); + + rt2800_bbp_write(rt2x00dev, 91, 0x04); } static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev) @@ -4301,8 +4317,6 @@ static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) return; } - rt2800_bbp_write(rt2x00dev, 91, 0x04); - if (rt2x00_rt(rt2x00dev, RT3290) || rt2x00_rt(rt2x00dev, RT3352) || rt2x00_rt(rt2x00dev, RT5390) || -- cgit v1.2.3 From b4e121d1d2a6a9fb59a900c755d6c1924334cf79 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Sat, 18 May 2013 14:03:42 +0200 Subject: rt2800: initialize BBP_R92 on all subroutines (except 5592) Signed-off-by: Stanislaw Gruszka Acked-by: Helmut Schaa Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index c5a2ca25b07d..354384726a6c 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -3977,6 +3977,8 @@ static void rt2800_init_bbp_305x_soc(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 86, 0x00); rt2800_bbp_write(rt2x00dev, 91, 0x04); + + rt2800_bbp_write(rt2x00dev, 92, 0x00); } static void rt2800_init_bbp_28xx(struct rt2x00_dev *rt2x00dev) @@ -4008,6 +4010,8 @@ static void rt2800_init_bbp_28xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 86, 0x00); rt2800_bbp_write(rt2x00dev, 91, 0x04); + + rt2800_bbp_write(rt2x00dev, 92, 0x00); } static void rt2800_init_bbp_30xx(struct rt2x00_dev *rt2x00dev) @@ -4033,6 +4037,8 @@ static void rt2800_init_bbp_30xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 86, 0x00); rt2800_bbp_write(rt2x00dev, 91, 0x04); + + rt2800_bbp_write(rt2x00dev, 92, 0x00); } static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) @@ -4069,6 +4075,8 @@ static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 86, 0x38); rt2800_bbp_write(rt2x00dev, 91, 0x04); + + rt2800_bbp_write(rt2x00dev, 92, 0x02); } static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) @@ -4109,6 +4117,8 @@ static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 88, 0x90); rt2800_bbp_write(rt2x00dev, 91, 0x04); + + rt2800_bbp_write(rt2x00dev, 92, 0x02); } static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) @@ -4134,6 +4144,8 @@ static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 86, 0x00); rt2800_bbp_write(rt2x00dev, 91, 0x04); + + rt2800_bbp_write(rt2x00dev, 92, 0x00); } static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev) @@ -4161,6 +4173,8 @@ static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 86, 0x00); rt2800_bbp_write(rt2x00dev, 91, 0x04); + + rt2800_bbp_write(rt2x00dev, 92, 0x00); } static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) @@ -4199,6 +4213,8 @@ static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 88, 0x90); rt2800_bbp_write(rt2x00dev, 91, 0x04); + + rt2800_bbp_write(rt2x00dev, 92, 0x02); } static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev) @@ -4317,14 +4333,6 @@ static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) return; } - if (rt2x00_rt(rt2x00dev, RT3290) || - rt2x00_rt(rt2x00dev, RT3352) || - rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) - rt2800_bbp_write(rt2x00dev, 92, 0x02); - else - rt2800_bbp_write(rt2x00dev, 92, 0x00); - if (rt2x00_rt(rt2x00dev, RT5392)) { rt2800_bbp_write(rt2x00dev, 95, 0x9a); rt2800_bbp_write(rt2x00dev, 98, 0x12); -- cgit v1.2.3 From 90fed535614b1fab52f088b98a7dadb45a18d463 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Sat, 18 May 2013 14:03:43 +0200 Subject: rt2800: move initialization of BBP_95 & BBP_98 to 53xx subroutine Signed-off-by: Stanislaw Gruszka Acked-by: Helmut Schaa Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 354384726a6c..d985711df7e7 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -4215,6 +4215,11 @@ static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 91, 0x04); rt2800_bbp_write(rt2x00dev, 92, 0x02); + + if (rt2x00_rt(rt2x00dev, RT5392)) { + rt2800_bbp_write(rt2x00dev, 95, 0x9a); + rt2800_bbp_write(rt2x00dev, 98, 0x12); + } } static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev) @@ -4333,11 +4338,6 @@ static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) return; } - if (rt2x00_rt(rt2x00dev, RT5392)) { - rt2800_bbp_write(rt2x00dev, 95, 0x9a); - rt2800_bbp_write(rt2x00dev, 98, 0x12); - } - if (rt2x00_rt_rev_gte(rt2x00dev, RT3070, REV_RT3070F) || rt2x00_rt_rev_gte(rt2x00dev, RT3071, REV_RT3071E) || rt2x00_rt_rev_gte(rt2x00dev, RT3090, REV_RT3090E) || -- cgit v1.2.3 From 672d11889834d7f34c70d79831d23020aed9c7ad Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Sat, 18 May 2013 14:03:44 +0200 Subject: rt2800: initialize BBP_R103 on all subroutines (except 5592) Signed-off-by: Stanislaw Gruszka Acked-by: Helmut Schaa Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 38 +++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index d985711df7e7..291542f4c84f 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -3979,6 +3979,8 @@ static void rt2800_init_bbp_305x_soc(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 91, 0x04); rt2800_bbp_write(rt2x00dev, 92, 0x00); + + rt2800_bbp_write(rt2x00dev, 103, 0xc0); } static void rt2800_init_bbp_28xx(struct rt2x00_dev *rt2x00dev) @@ -4012,6 +4014,8 @@ static void rt2800_init_bbp_28xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 91, 0x04); rt2800_bbp_write(rt2x00dev, 92, 0x00); + + rt2800_bbp_write(rt2x00dev, 103, 0x00); } static void rt2800_init_bbp_30xx(struct rt2x00_dev *rt2x00dev) @@ -4039,6 +4043,13 @@ static void rt2800_init_bbp_30xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 91, 0x04); rt2800_bbp_write(rt2x00dev, 92, 0x00); + + if (rt2x00_rt_rev_gte(rt2x00dev, RT3070, REV_RT3070F) || + rt2x00_rt_rev_gte(rt2x00dev, RT3071, REV_RT3071E) || + rt2x00_rt_rev_gte(rt2x00dev, RT3090, REV_RT3090E)) + rt2800_bbp_write(rt2x00dev, 103, 0xc0); + else + rt2800_bbp_write(rt2x00dev, 103, 0x00); } static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) @@ -4077,6 +4088,8 @@ static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 91, 0x04); rt2800_bbp_write(rt2x00dev, 92, 0x02); + + rt2800_bbp_write(rt2x00dev, 103, 0xc0); } static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) @@ -4119,6 +4132,8 @@ static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 91, 0x04); rt2800_bbp_write(rt2x00dev, 92, 0x02); + + rt2800_bbp_write(rt2x00dev, 103, 0xc0); } static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) @@ -4146,6 +4161,11 @@ static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 91, 0x04); rt2800_bbp_write(rt2x00dev, 92, 0x00); + + if (rt2x00_rt_rev_gte(rt2x00dev, RT3390, REV_RT3390E)) + rt2800_bbp_write(rt2x00dev, 103, 0xc0); + else + rt2800_bbp_write(rt2x00dev, 103, 0x00); } static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev) @@ -4175,6 +4195,8 @@ static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 91, 0x04); rt2800_bbp_write(rt2x00dev, 92, 0x00); + + rt2800_bbp_write(rt2x00dev, 103, 0xc0); } static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) @@ -4220,6 +4242,8 @@ static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 95, 0x9a); rt2800_bbp_write(rt2x00dev, 98, 0x12); } + + rt2800_bbp_write(rt2x00dev, 103, 0xc0); } static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev) @@ -4338,20 +4362,6 @@ static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) return; } - if (rt2x00_rt_rev_gte(rt2x00dev, RT3070, REV_RT3070F) || - rt2x00_rt_rev_gte(rt2x00dev, RT3071, REV_RT3071E) || - rt2x00_rt_rev_gte(rt2x00dev, RT3090, REV_RT3090E) || - rt2x00_rt_rev_gte(rt2x00dev, RT3390, REV_RT3390E) || - rt2x00_rt(rt2x00dev, RT3290) || - rt2x00_rt(rt2x00dev, RT3352) || - rt2x00_rt(rt2x00dev, RT3572) || - rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392) || - rt2800_is_305x_soc(rt2x00dev)) - rt2800_bbp_write(rt2x00dev, 103, 0xc0); - else - rt2800_bbp_write(rt2x00dev, 103, 0x00); - if (rt2x00_rt(rt2x00dev, RT3290) || rt2x00_rt(rt2x00dev, RT3352) || rt2x00_rt(rt2x00dev, RT5390) || -- cgit v1.2.3 From 1ad4408a1ea766774a6d4e73ed6a23bcaf9d1d32 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Sat, 18 May 2013 14:03:45 +0200 Subject: rt2800: initialize BBP_R104 on proper subroutines Signed-off-by: Stanislaw Gruszka Acked-by: Helmut Schaa Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 291542f4c84f..238221372a0e 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -4090,6 +4090,8 @@ static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 92, 0x02); rt2800_bbp_write(rt2x00dev, 103, 0xc0); + + rt2800_bbp_write(rt2x00dev, 104, 0x92); } static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) @@ -4134,6 +4136,8 @@ static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 92, 0x02); rt2800_bbp_write(rt2x00dev, 103, 0xc0); + + rt2800_bbp_write(rt2x00dev, 104, 0x92); } static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) @@ -4244,6 +4248,8 @@ static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) } rt2800_bbp_write(rt2x00dev, 103, 0xc0); + + rt2800_bbp_write(rt2x00dev, 104, 0x92); } static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev) @@ -4362,12 +4368,6 @@ static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) return; } - if (rt2x00_rt(rt2x00dev, RT3290) || - rt2x00_rt(rt2x00dev, RT3352) || - rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) - rt2800_bbp_write(rt2x00dev, 104, 0x92); - if (rt2800_is_305x_soc(rt2x00dev)) rt2800_bbp_write(rt2x00dev, 105, 0x01); else if (rt2x00_rt(rt2x00dev, RT3290)) -- cgit v1.2.3 From 49d611186a2dfdf8c10e090450a570ba929bd393 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Sat, 18 May 2013 14:03:46 +0200 Subject: rt2800: initialize BBP_R105 on all subroutines (except 5592) Signed-off-by: Stanislaw Gruszka Acked-by: Helmut Schaa Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 238221372a0e..0970e40a7cfb 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -3981,6 +3981,8 @@ static void rt2800_init_bbp_305x_soc(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 92, 0x00); rt2800_bbp_write(rt2x00dev, 103, 0xc0); + + rt2800_bbp_write(rt2x00dev, 105, 0x01); } static void rt2800_init_bbp_28xx(struct rt2x00_dev *rt2x00dev) @@ -4016,6 +4018,8 @@ static void rt2800_init_bbp_28xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 92, 0x00); rt2800_bbp_write(rt2x00dev, 103, 0x00); + + rt2800_bbp_write(rt2x00dev, 105, 0x05); } static void rt2800_init_bbp_30xx(struct rt2x00_dev *rt2x00dev) @@ -4050,6 +4054,8 @@ static void rt2800_init_bbp_30xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 103, 0xc0); else rt2800_bbp_write(rt2x00dev, 103, 0x00); + + rt2800_bbp_write(rt2x00dev, 105, 0x05); } static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) @@ -4092,6 +4098,8 @@ static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 103, 0xc0); rt2800_bbp_write(rt2x00dev, 104, 0x92); + + rt2800_bbp_write(rt2x00dev, 105, 0x1c); } static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) @@ -4138,6 +4146,8 @@ static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 103, 0xc0); rt2800_bbp_write(rt2x00dev, 104, 0x92); + + rt2800_bbp_write(rt2x00dev, 105, 0x34); } static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) @@ -4170,6 +4180,8 @@ static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 103, 0xc0); else rt2800_bbp_write(rt2x00dev, 103, 0x00); + + rt2800_bbp_write(rt2x00dev, 105, 0x05); } static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev) @@ -4201,6 +4213,8 @@ static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 92, 0x00); rt2800_bbp_write(rt2x00dev, 103, 0xc0); + + rt2800_bbp_write(rt2x00dev, 105, 0x05); } static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) @@ -4250,6 +4264,8 @@ static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 103, 0xc0); rt2800_bbp_write(rt2x00dev, 104, 0x92); + + rt2800_bbp_write(rt2x00dev, 105, 0x3c); } static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev) @@ -4368,18 +4384,6 @@ static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) return; } - if (rt2800_is_305x_soc(rt2x00dev)) - rt2800_bbp_write(rt2x00dev, 105, 0x01); - else if (rt2x00_rt(rt2x00dev, RT3290)) - rt2800_bbp_write(rt2x00dev, 105, 0x1c); - else if (rt2x00_rt(rt2x00dev, RT3352)) - rt2800_bbp_write(rt2x00dev, 105, 0x34); - else if (rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) - rt2800_bbp_write(rt2x00dev, 105, 0x3c); - else - rt2800_bbp_write(rt2x00dev, 105, 0x05); - if (rt2x00_rt(rt2x00dev, RT3290) || rt2x00_rt(rt2x00dev, RT5390)) rt2800_bbp_write(rt2x00dev, 106, 0x03); -- cgit v1.2.3 From f867085edeab37d5cce62e266615e540cb85ae43 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Sat, 18 May 2013 14:03:47 +0200 Subject: rt2800: initialize BBP_R106 on all subroutines (except 5592) Signed-off-by: Stanislaw Gruszka Acked-by: Helmut Schaa Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 0970e40a7cfb..eb44ba80c661 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -3983,6 +3983,8 @@ static void rt2800_init_bbp_305x_soc(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 103, 0xc0); rt2800_bbp_write(rt2x00dev, 105, 0x01); + + rt2800_bbp_write(rt2x00dev, 106, 0x35); } static void rt2800_init_bbp_28xx(struct rt2x00_dev *rt2x00dev) @@ -4020,6 +4022,8 @@ static void rt2800_init_bbp_28xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 103, 0x00); rt2800_bbp_write(rt2x00dev, 105, 0x05); + + rt2800_bbp_write(rt2x00dev, 106, 0x35); } static void rt2800_init_bbp_30xx(struct rt2x00_dev *rt2x00dev) @@ -4056,6 +4060,8 @@ static void rt2800_init_bbp_30xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 103, 0x00); rt2800_bbp_write(rt2x00dev, 105, 0x05); + + rt2800_bbp_write(rt2x00dev, 106, 0x35); } static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) @@ -4100,6 +4106,8 @@ static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 104, 0x92); rt2800_bbp_write(rt2x00dev, 105, 0x1c); + + rt2800_bbp_write(rt2x00dev, 106, 0x03); } static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) @@ -4148,6 +4156,8 @@ static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 104, 0x92); rt2800_bbp_write(rt2x00dev, 105, 0x34); + + rt2800_bbp_write(rt2x00dev, 106, 0x05); } static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) @@ -4182,6 +4192,8 @@ static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 103, 0x00); rt2800_bbp_write(rt2x00dev, 105, 0x05); + + rt2800_bbp_write(rt2x00dev, 106, 0x35); } static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev) @@ -4215,6 +4227,8 @@ static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 103, 0xc0); rt2800_bbp_write(rt2x00dev, 105, 0x05); + + rt2800_bbp_write(rt2x00dev, 106, 0x35); } static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) @@ -4266,6 +4280,13 @@ static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 104, 0x92); rt2800_bbp_write(rt2x00dev, 105, 0x3c); + + if (rt2x00_rt(rt2x00dev, RT5390)) + rt2800_bbp_write(rt2x00dev, 106, 0x03); + else if (rt2x00_rt(rt2x00dev, RT5392)) + rt2800_bbp_write(rt2x00dev, 106, 0x12); + else + WARN_ON(1); } static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev) @@ -4384,16 +4405,6 @@ static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) return; } - if (rt2x00_rt(rt2x00dev, RT3290) || - rt2x00_rt(rt2x00dev, RT5390)) - rt2800_bbp_write(rt2x00dev, 106, 0x03); - else if (rt2x00_rt(rt2x00dev, RT3352)) - rt2800_bbp_write(rt2x00dev, 106, 0x05); - else if (rt2x00_rt(rt2x00dev, RT5392)) - rt2800_bbp_write(rt2x00dev, 106, 0x12); - else - rt2800_bbp_write(rt2x00dev, 106, 0x35); - if (rt2x00_rt(rt2x00dev, RT3352)) rt2800_bbp_write(rt2x00dev, 120, 0x50); -- cgit v1.2.3 From 46b90d3216af851fe8c1b97a2cac95ee129443a9 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Sat, 18 May 2013 14:03:48 +0200 Subject: rt2800: initialize BBP_R120 on 3352 subroutine Signed-off-by: Stanislaw Gruszka Acked-by: Helmut Schaa Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index eb44ba80c661..b860abcfe136 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -4158,6 +4158,8 @@ static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 105, 0x34); rt2800_bbp_write(rt2x00dev, 106, 0x05); + + rt2800_bbp_write(rt2x00dev, 120, 0x50); } static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) @@ -4405,9 +4407,6 @@ static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) return; } - if (rt2x00_rt(rt2x00dev, RT3352)) - rt2800_bbp_write(rt2x00dev, 120, 0x50); - if (rt2x00_rt(rt2x00dev, RT3290) || rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392)) -- cgit v1.2.3 From f2b6777cb631c6d8ec4f8479e34eaec1133659d4 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Sat, 18 May 2013 14:03:49 +0200 Subject: rt2800: initialize BBP_R128 on proper subroutines Signed-off-by: Stanislaw Gruszka Acked-by: Helmut Schaa Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index b860abcfe136..0d2c46f42227 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -4108,6 +4108,8 @@ static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 105, 0x1c); rt2800_bbp_write(rt2x00dev, 106, 0x03); + + rt2800_bbp_write(rt2x00dev, 128, 0x12); } static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) @@ -4289,6 +4291,8 @@ static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 106, 0x12); else WARN_ON(1); + + rt2800_bbp_write(rt2x00dev, 128, 0x12); } static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev) @@ -4407,11 +4411,6 @@ static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) return; } - if (rt2x00_rt(rt2x00dev, RT3290) || - rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) - rt2800_bbp_write(rt2x00dev, 128, 0x12); - if (rt2x00_rt(rt2x00dev, RT5392)) { rt2800_bbp_write(rt2x00dev, 134, 0xd0); rt2800_bbp_write(rt2x00dev, 135, 0xf6); -- cgit v1.2.3 From 7291714099627183f0f439de68c9111e1c26c50e Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Sat, 18 May 2013 14:03:50 +0200 Subject: rt2800: initialize BBP_R134 & BBP_R135 on 53xx subroutine Signed-off-by: Stanislaw Gruszka Acked-by: Helmut Schaa Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 0d2c46f42227..f54d04e60729 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -4293,6 +4293,11 @@ static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) WARN_ON(1); rt2800_bbp_write(rt2x00dev, 128, 0x12); + + if (rt2x00_rt(rt2x00dev, RT5392)) { + rt2800_bbp_write(rt2x00dev, 134, 0xd0); + rt2800_bbp_write(rt2x00dev, 135, 0xf6); + } } static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev) @@ -4411,11 +4416,6 @@ static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) return; } - if (rt2x00_rt(rt2x00dev, RT5392)) { - rt2800_bbp_write(rt2x00dev, 134, 0xd0); - rt2800_bbp_write(rt2x00dev, 135, 0xf6); - } - if (rt2x00_rt(rt2x00dev, RT3352)) rt2800_bbp_write(rt2x00dev, 137, 0x0f); -- cgit v1.2.3 From b7feb9ba64c53f93412b61351e00663bb24959ef Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Sat, 18 May 2013 14:03:51 +0200 Subject: rt2800: initialize BBP_R104 on 3352 subroutine Signed-off-by: Stanislaw Gruszka Acked-by: Helmut Schaa Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index f54d04e60729..abaa685b25da 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -4162,6 +4162,8 @@ static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 106, 0x05); rt2800_bbp_write(rt2x00dev, 120, 0x50); + + rt2800_bbp_write(rt2x00dev, 137, 0x0f); } static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) @@ -4416,9 +4418,6 @@ static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) return; } - if (rt2x00_rt(rt2x00dev, RT3352)) - rt2800_bbp_write(rt2x00dev, 137, 0x0f); - if (rt2x00_rt(rt2x00dev, RT3071) || rt2x00_rt(rt2x00dev, RT3090) || rt2x00_rt(rt2x00dev, RT3390) || -- cgit v1.2.3 From 5df1ff3a22ebfd2749092227d7d1ea4599488c4e Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Sat, 18 May 2013 14:03:52 +0200 Subject: rt2800: initialize BBP_R108 on proper subroutines Create helper function and move initialization to subroutines. Signed-off-by: Stanislaw Gruszka Acked-by: Helmut Schaa Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 41 +++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index abaa685b25da..d0d65dd23746 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -3953,6 +3953,20 @@ static void rt2800_init_bbp_early(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 106, 0x35); } +static void rt2800_disable_unused_dac_adc(struct rt2x00_dev *rt2x00dev) +{ + u16 eeprom; + u8 value; + + rt2800_bbp_read(rt2x00dev, 138, &value); + rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom); + if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) == 1) + value |= 0x20; + if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) == 1) + value &= ~0x02; + rt2800_bbp_write(rt2x00dev, 138, value); +} + static void rt2800_init_bbp_305x_soc(struct rt2x00_dev *rt2x00dev) { rt2800_bbp_write(rt2x00dev, 31, 0x08); @@ -4062,6 +4076,10 @@ static void rt2800_init_bbp_30xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 105, 0x05); rt2800_bbp_write(rt2x00dev, 106, 0x35); + + if (rt2x00_rt(rt2x00dev, RT3071) || + rt2x00_rt(rt2x00dev, RT3090)) + rt2800_disable_unused_dac_adc(rt2x00dev); } static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) @@ -4200,6 +4218,8 @@ static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 105, 0x05); rt2800_bbp_write(rt2x00dev, 106, 0x35); + + rt2800_disable_unused_dac_adc(rt2x00dev); } static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev) @@ -4235,6 +4255,8 @@ static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 105, 0x05); rt2800_bbp_write(rt2x00dev, 106, 0x35); + + rt2800_disable_unused_dac_adc(rt2x00dev); } static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) @@ -4300,6 +4322,8 @@ static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 134, 0xd0); rt2800_bbp_write(rt2x00dev, 135, 0xf6); } + + rt2800_disable_unused_dac_adc(rt2x00dev); } static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev) @@ -4418,23 +4442,6 @@ static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) return; } - if (rt2x00_rt(rt2x00dev, RT3071) || - rt2x00_rt(rt2x00dev, RT3090) || - rt2x00_rt(rt2x00dev, RT3390) || - rt2x00_rt(rt2x00dev, RT3572) || - rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) { - rt2800_bbp_read(rt2x00dev, 138, &value); - - rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom); - if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) == 1) - value |= 0x20; - if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) == 1) - value &= ~0x02; - - rt2800_bbp_write(rt2x00dev, 138, value); - } - if (rt2x00_rt(rt2x00dev, RT3290)) { rt2800_bbp_write(rt2x00dev, 67, 0x24); rt2800_bbp_write(rt2x00dev, 143, 0x04); -- cgit v1.2.3 From c2da5273aefef4dc1a6d114ddc4eb807ea2bea3a Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Sat, 18 May 2013 14:03:53 +0200 Subject: rt2800: move 3352 specific bbp initialization Signed-off-by: Stanislaw Gruszka Acked-by: Helmut Schaa Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 42 ++++++++++++++++----------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index d0d65dd23746..2d00fd88075c 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -4182,6 +4182,26 @@ static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 120, 0x50); rt2800_bbp_write(rt2x00dev, 137, 0x0f); + + rt2800_bbp_write(rt2x00dev, 163, 0xbd); + /* Set ITxBF timeout to 0x9c40=1000msec */ + rt2800_bbp_write(rt2x00dev, 179, 0x02); + rt2800_bbp_write(rt2x00dev, 180, 0x00); + rt2800_bbp_write(rt2x00dev, 182, 0x40); + rt2800_bbp_write(rt2x00dev, 180, 0x01); + rt2800_bbp_write(rt2x00dev, 182, 0x9c); + rt2800_bbp_write(rt2x00dev, 179, 0x00); + /* Reprogram the inband interface to put right values in RXWI */ + rt2800_bbp_write(rt2x00dev, 142, 0x04); + rt2800_bbp_write(rt2x00dev, 143, 0x3b); + rt2800_bbp_write(rt2x00dev, 142, 0x06); + rt2800_bbp_write(rt2x00dev, 143, 0xa0); + rt2800_bbp_write(rt2x00dev, 142, 0x07); + rt2800_bbp_write(rt2x00dev, 143, 0xa1); + rt2800_bbp_write(rt2x00dev, 142, 0x08); + rt2800_bbp_write(rt2x00dev, 143, 0xa2); + + rt2800_bbp_write(rt2x00dev, 148, 0xc8); } static void rt2800_init_bbp_3390(struct rt2x00_dev *rt2x00dev) @@ -4465,28 +4485,6 @@ static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 3, value); } - if (rt2x00_rt(rt2x00dev, RT3352)) { - rt2800_bbp_write(rt2x00dev, 163, 0xbd); - /* Set ITxBF timeout to 0x9c40=1000msec */ - rt2800_bbp_write(rt2x00dev, 179, 0x02); - rt2800_bbp_write(rt2x00dev, 180, 0x00); - rt2800_bbp_write(rt2x00dev, 182, 0x40); - rt2800_bbp_write(rt2x00dev, 180, 0x01); - rt2800_bbp_write(rt2x00dev, 182, 0x9c); - rt2800_bbp_write(rt2x00dev, 179, 0x00); - /* Reprogram the inband interface to put right values in RXWI */ - rt2800_bbp_write(rt2x00dev, 142, 0x04); - rt2800_bbp_write(rt2x00dev, 143, 0x3b); - rt2800_bbp_write(rt2x00dev, 142, 0x06); - rt2800_bbp_write(rt2x00dev, 143, 0xa0); - rt2800_bbp_write(rt2x00dev, 142, 0x07); - rt2800_bbp_write(rt2x00dev, 143, 0xa1); - rt2800_bbp_write(rt2x00dev, 142, 0x08); - rt2800_bbp_write(rt2x00dev, 143, 0xa2); - - rt2800_bbp_write(rt2x00dev, 148, 0xc8); - } - if (rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392)) { int ant, div_mode; -- cgit v1.2.3 From 6addb24eefd36ea4d69dfa6587300943bb037b5d Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Sat, 18 May 2013 14:03:54 +0200 Subject: rt2800: move 3290 specific bbp initialization Signed-off-by: Stanislaw Gruszka Acked-by: Helmut Schaa Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 46 ++++++++++++++++----------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 2d00fd88075c..527c11478d9d 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -4084,6 +4084,8 @@ static void rt2800_init_bbp_30xx(struct rt2x00_dev *rt2x00dev) static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) { + u8 value; + rt2800_bbp4_mac_if_ctrl(rt2x00dev); rt2800_bbp_write(rt2x00dev, 31, 0x08); @@ -4128,6 +4130,27 @@ static void rt2800_init_bbp_3290(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 106, 0x03); rt2800_bbp_write(rt2x00dev, 128, 0x12); + + rt2800_bbp_write(rt2x00dev, 67, 0x24); + rt2800_bbp_write(rt2x00dev, 143, 0x04); + rt2800_bbp_write(rt2x00dev, 142, 0x99); + rt2800_bbp_write(rt2x00dev, 150, 0x30); + rt2800_bbp_write(rt2x00dev, 151, 0x2e); + rt2800_bbp_write(rt2x00dev, 152, 0x20); + rt2800_bbp_write(rt2x00dev, 153, 0x34); + rt2800_bbp_write(rt2x00dev, 154, 0x40); + rt2800_bbp_write(rt2x00dev, 155, 0x3b); + rt2800_bbp_write(rt2x00dev, 253, 0x04); + + rt2800_bbp_read(rt2x00dev, 47, &value); + rt2x00_set_field8(&value, BBP47_TSSI_ADC6, 1); + rt2800_bbp_write(rt2x00dev, 47, value); + + /* Use 5-bit ADC for Acquisition and 8-bit ADC for data */ + rt2800_bbp_read(rt2x00dev, 3, &value); + rt2x00_set_field8(&value, BBP3_ADC_MODE_SWITCH, 1); + rt2x00_set_field8(&value, BBP3_ADC_INIT_MODE, 1); + rt2800_bbp_write(rt2x00dev, 3, value); } static void rt2800_init_bbp_3352(struct rt2x00_dev *rt2x00dev) @@ -4462,29 +4485,6 @@ static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) return; } - if (rt2x00_rt(rt2x00dev, RT3290)) { - rt2800_bbp_write(rt2x00dev, 67, 0x24); - rt2800_bbp_write(rt2x00dev, 143, 0x04); - rt2800_bbp_write(rt2x00dev, 142, 0x99); - rt2800_bbp_write(rt2x00dev, 150, 0x30); - rt2800_bbp_write(rt2x00dev, 151, 0x2e); - rt2800_bbp_write(rt2x00dev, 152, 0x20); - rt2800_bbp_write(rt2x00dev, 153, 0x34); - rt2800_bbp_write(rt2x00dev, 154, 0x40); - rt2800_bbp_write(rt2x00dev, 155, 0x3b); - rt2800_bbp_write(rt2x00dev, 253, 0x04); - - rt2800_bbp_read(rt2x00dev, 47, &value); - rt2x00_set_field8(&value, BBP47_TSSI_ADC6, 1); - rt2800_bbp_write(rt2x00dev, 47, value); - - /* Use 5-bit ADC for Acquisition and 8-bit ADC for data */ - rt2800_bbp_read(rt2x00dev, 3, &value); - rt2x00_set_field8(&value, BBP3_ADC_MODE_SWITCH, 1); - rt2x00_set_field8(&value, BBP3_ADC_INIT_MODE, 1); - rt2800_bbp_write(rt2x00dev, 3, value); - } - if (rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392)) { int ant, div_mode; -- cgit v1.2.3 From 32ef8f499262309478f0225f5fc33910e1c8e199 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Sat, 18 May 2013 14:03:55 +0200 Subject: rt2800: move 53xx specific bbp initialization Signed-off-by: Stanislaw Gruszka Acked-by: Helmut Schaa Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800lib.c | 83 ++++++++++++++++----------------- 1 file changed, 41 insertions(+), 42 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 527c11478d9d..ead3a3e746a2 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -4304,6 +4304,10 @@ static void rt2800_init_bbp_3572(struct rt2x00_dev *rt2x00dev) static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) { + int ant, div_mode; + u16 eeprom; + u8 value; + rt2800_bbp4_mac_if_ctrl(rt2x00dev); rt2800_bbp_write(rt2x00dev, 31, 0x08); @@ -4367,6 +4371,43 @@ static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) } rt2800_disable_unused_dac_adc(rt2x00dev); + + rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom); + div_mode = rt2x00_get_field16(eeprom, + EEPROM_NIC_CONF1_ANT_DIVERSITY); + ant = (div_mode == 3) ? 1 : 0; + + /* check if this is a Bluetooth combo card */ + if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) { + u32 reg; + + rt2800_register_read(rt2x00dev, GPIO_CTRL, ®); + rt2x00_set_field32(®, GPIO_CTRL_DIR3, 0); + rt2x00_set_field32(®, GPIO_CTRL_DIR6, 0); + rt2x00_set_field32(®, GPIO_CTRL_VAL3, 0); + rt2x00_set_field32(®, GPIO_CTRL_VAL6, 0); + if (ant == 0) + rt2x00_set_field32(®, GPIO_CTRL_VAL3, 1); + else if (ant == 1) + rt2x00_set_field32(®, GPIO_CTRL_VAL6, 1); + rt2800_register_write(rt2x00dev, GPIO_CTRL, reg); + } + + /* This chip has hardware antenna diversity*/ + if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390R)) { + rt2800_bbp_write(rt2x00dev, 150, 0); /* Disable Antenna Software OFDM */ + rt2800_bbp_write(rt2x00dev, 151, 0); /* Disable Antenna Software CCK */ + rt2800_bbp_write(rt2x00dev, 154, 0); /* Clear previously selected antenna */ + } + + rt2800_bbp_read(rt2x00dev, 152, &value); + if (ant == 0) + rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 1); + else + rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 0); + rt2800_bbp_write(rt2x00dev, 152, value); + + rt2800_init_freq_calibration(rt2x00dev); } static void rt2800_init_bbp_5592(struct rt2x00_dev *rt2x00dev) @@ -4485,48 +4526,6 @@ static void rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) return; } - if (rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) { - int ant, div_mode; - - rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom); - div_mode = rt2x00_get_field16(eeprom, - EEPROM_NIC_CONF1_ANT_DIVERSITY); - ant = (div_mode == 3) ? 1 : 0; - - /* check if this is a Bluetooth combo card */ - if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) { - u32 reg; - - rt2800_register_read(rt2x00dev, GPIO_CTRL, ®); - rt2x00_set_field32(®, GPIO_CTRL_DIR3, 0); - rt2x00_set_field32(®, GPIO_CTRL_DIR6, 0); - rt2x00_set_field32(®, GPIO_CTRL_VAL3, 0); - rt2x00_set_field32(®, GPIO_CTRL_VAL6, 0); - if (ant == 0) - rt2x00_set_field32(®, GPIO_CTRL_VAL3, 1); - else if (ant == 1) - rt2x00_set_field32(®, GPIO_CTRL_VAL6, 1); - rt2800_register_write(rt2x00dev, GPIO_CTRL, reg); - } - - /* This chip has hardware antenna diversity*/ - if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390R)) { - rt2800_bbp_write(rt2x00dev, 150, 0); /* Disable Antenna Software OFDM */ - rt2800_bbp_write(rt2x00dev, 151, 0); /* Disable Antenna Software CCK */ - rt2800_bbp_write(rt2x00dev, 154, 0); /* Clear previously selected antenna */ - } - - rt2800_bbp_read(rt2x00dev, 152, &value); - if (ant == 0) - rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 1); - else - rt2x00_set_field8(&value, BBP152_RX_DEFAULT_ANT, 0); - rt2800_bbp_write(rt2x00dev, 152, value); - - rt2800_init_freq_calibration(rt2x00dev); - } - for (i = 0; i < EEPROM_BBP_SIZE; i++) { rt2x00_eeprom_read(rt2x00dev, EEPROM_BBP_START + i, &eeprom); -- cgit v1.2.3 From 110dea0008c40a20f1ce280b636f87fff58a7403 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 23 May 2013 17:10:43 +0800 Subject: wil6210: use kfree_skb() instead of kfree() Use kfree_skb() instead of kfree() to free sk_buff. Introduced by commit e270045b569cc7030abd29857f3a4e7906524ec0 (wil6210: Sanity check for reported DMA length) Signed-off-by: Wei Yongjun Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/txrx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 082f76bdd5e5..00dffeda983e 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -369,7 +369,7 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, if (dmalen > sz) { wil_err(wil, "Rx size too large: %d bytes!\n", dmalen); - kfree(skb); + kfree_skb(skb); return NULL; } skb_trim(skb, dmalen); -- cgit v1.2.3 From bbf71a8f35cf52e97ba129401f82ccefd249c757 Mon Sep 17 00:00:00 2001 From: Nishant Sarmukadam Date: Fri, 24 May 2013 14:42:25 +0530 Subject: mwl8k: Fix the firmware hang issue for 8764 The firmware hang issue is not seen very often, though it is still seen sometimes (once in 12 hours in local tests). The changes in the driver ,to interrupt the firmware, are needed when we detect that firmware is stuck and when the host queues are full and we begin to drop packets. This is to ensure that the firmware does not miss any PPA_RDY interrupts to cause the firmware restart dont miss PPA_READY interrupt for SC2 Signed-off-by: Nishant Sarmukadam Signed-off-by: Yogesh Ashok Powar Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 6820fce4016b..a3707fd4ef62 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -1548,7 +1548,7 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw) if (!priv->pending_tx_pkts) return 0; - retry = 0; + retry = 1; rc = 0; spin_lock_bh(&priv->tx_lock); @@ -1572,13 +1572,19 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw) spin_lock_bh(&priv->tx_lock); - if (timeout) { + if (timeout || !priv->pending_tx_pkts) { WARN_ON(priv->pending_tx_pkts); if (retry) wiphy_notice(hw->wiphy, "tx rings drained\n"); break; } + if (retry) { + mwl8k_tx_start(priv); + retry = 0; + continue; + } + if (priv->pending_tx_pkts < oldcount) { wiphy_notice(hw->wiphy, "waiting for tx rings to drain (%d -> %d pkts)\n", @@ -2055,6 +2061,7 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, mwl8k_remove_stream(hw, stream); spin_unlock(&priv->stream_lock); } + mwl8k_tx_start(priv); spin_unlock_bh(&priv->tx_lock); pci_unmap_single(priv->pdev, dma, skb->len, PCI_DMA_TODEVICE); -- cgit v1.2.3 From 5e4b6f5698421d94226cc2f80eae6d613c9acef8 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 16 May 2013 20:11:08 +0300 Subject: cfg80211: Allow TDLS peer AID to be configured for VHT VHT uses peer AID in the PARTIAL_AID field in TDLS frames. The current design for TDLS is to first add a dummy STA entry before completing TDLS Setup and then update information on this STA entry based on what was received from the peer during the setup exchange. In theory, this could use NL80211_ATTR_STA_AID to set the peer AID just like this is used in AP mode to set the AID of an association station. However, existing cfg80211 validation rules prevent this attribute from being used with set_station operation. To avoid interoperability issues between different kernel and user space version combinations, introduce a new nl80211 attribute for the purpose of setting TDLS peer AID. This attribute can be used in both the new_station and set_station operations. It is not supposed to be allowed to change the AID value during the lifetime of the STA entry, but that validation is left for drivers to do in the change_station callback. Signed-off-by: Jouni Malinen Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 7 +++++++ net/wireless/nl80211.c | 11 +++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 06320713e9c9..32b060ea5266 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1431,6 +1431,11 @@ enum nl80211_commands { * @NL80211_ATTR_MAX_CRIT_PROT_DURATION: duration in milliseconds in which * the connection should have increased reliability (u16). * + * @NL80211_ATTR_PEER_AID: Association ID for the peer TDLS station (u16). + * This is similar to @NL80211_ATTR_STA_AID but with a difference of being + * allowed to be used with the first @NL80211_CMD_SET_STATION command to + * update a TDLS peer STA entry. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1729,6 +1734,8 @@ enum nl80211_attrs { NL80211_ATTR_CRIT_PROT_ID, NL80211_ATTR_MAX_CRIT_PROT_DURATION, + NL80211_ATTR_PEER_AID, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 5f10f7acfa06..14276af7964b 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -378,6 +378,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_MDID] = { .type = NLA_U16 }, [NL80211_ATTR_IE_RIC] = { .type = NLA_BINARY, .len = IEEE80211_MAX_DATA_LEN }, + [NL80211_ATTR_PEER_AID] = { .type = NLA_U16 }, }; /* policy for the key attributes */ @@ -3872,6 +3873,8 @@ static int nl80211_set_station_tdls(struct genl_info *info, struct station_parameters *params) { /* Dummy STA entry gets updated once the peer capabilities are known */ + if (info->attrs[NL80211_ATTR_PEER_AID]) + params->aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]); if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) params->ht_capa = nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]); @@ -4012,7 +4015,8 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) return -EINVAL; - if (!info->attrs[NL80211_ATTR_STA_AID]) + if (!info->attrs[NL80211_ATTR_STA_AID] && + !info->attrs[NL80211_ATTR_PEER_AID]) return -EINVAL; mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); @@ -4023,7 +4027,10 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) params.listen_interval = nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); - params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]); + if (info->attrs[NL80211_ATTR_STA_AID]) + params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]); + else + params.aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]); if (!params.aid || params.aid > IEEE80211_MAX_AID) return -EINVAL; -- cgit v1.2.3 From d4a5a48976d12ab340ed34605b5f5049b123d868 Mon Sep 17 00:00:00 2001 From: Ashok Nagarajan Date: Mon, 13 May 2013 17:08:04 -0700 Subject: mac80211: Move mesh estab_plinks outside mesh_stats debug group As estab_plinks is not a statistics member, don't show its debug information along with other mesh stat members Signed-off-by: Ashok Nagarajan Signed-off-by: Johannes Berg --- net/mac80211/debugfs_netdev.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index 14abcf44f974..f83074fe6670 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -471,6 +471,8 @@ __IEEE80211_IF_FILE_W(tsf); IEEE80211_IF_FILE(peer, u.wds.remote_addr, MAC); #ifdef CONFIG_MAC80211_MESH +IEEE80211_IF_FILE(estab_plinks, u.mesh.estab_plinks, ATOMIC); + /* Mesh stats attributes */ IEEE80211_IF_FILE(fwded_mcast, u.mesh.mshstats.fwded_mcast, DEC); IEEE80211_IF_FILE(fwded_unicast, u.mesh.mshstats.fwded_unicast, DEC); @@ -480,7 +482,6 @@ IEEE80211_IF_FILE(dropped_frames_congestion, u.mesh.mshstats.dropped_frames_congestion, DEC); IEEE80211_IF_FILE(dropped_frames_no_route, u.mesh.mshstats.dropped_frames_no_route, DEC); -IEEE80211_IF_FILE(estab_plinks, u.mesh.estab_plinks, ATOMIC); /* Mesh parameters */ IEEE80211_IF_FILE(dot11MeshMaxRetries, @@ -583,6 +584,7 @@ static void add_wds_files(struct ieee80211_sub_if_data *sdata) static void add_mesh_files(struct ieee80211_sub_if_data *sdata) { DEBUGFS_ADD_MODE(tsf, 0600); + DEBUGFS_ADD_MODE(estab_plinks, 0400); } static void add_mesh_stats(struct ieee80211_sub_if_data *sdata) @@ -598,7 +600,6 @@ static void add_mesh_stats(struct ieee80211_sub_if_data *sdata) MESHSTATS_ADD(dropped_frames_ttl); MESHSTATS_ADD(dropped_frames_no_route); MESHSTATS_ADD(dropped_frames_congestion); - MESHSTATS_ADD(estab_plinks); #undef MESHSTATS_ADD } -- cgit v1.2.3 From b422c6cd7e93bb613030f14d7d8a0cc73f115629 Mon Sep 17 00:00:00 2001 From: Ashok Nagarajan Date: Fri, 10 May 2013 17:50:51 -0700 Subject: {cfg,mac}80211: move mandatory rates calculation to cfg80211 Move mandatory rates calculation to cfg80211, shared with non mac80211 drivers. Signed-off-by: Ashok Nagarajan [extend documentation] Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 9 +++++++++ net/mac80211/ibss.c | 10 +++++++--- net/mac80211/ieee80211_i.h | 3 --- net/mac80211/mesh.c | 5 +++-- net/mac80211/util.c | 26 -------------------------- net/wireless/util.c | 23 +++++++++++++++++++++++ 6 files changed, 42 insertions(+), 34 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 32a2f1b20861..58f6302da145 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3005,6 +3005,15 @@ struct ieee80211_rate * ieee80211_get_response_rate(struct ieee80211_supported_band *sband, u32 basic_rates, int bitrate); +/** + * ieee80211_mandatory_rates - get mandatory rates for a given band + * @sband: the band to look for rates in + * + * This function returns a bitmap of the mandatory rates for the given + * band, bits are set according to the rate position in the bitrates array. + */ +u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband); + /* * Radiotap parsing functions -- for controlled injection support * diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 170f9a7fa319..956ba6316da5 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -341,6 +341,7 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, struct ieee80211_local *local = sdata->local; struct sta_info *sta; struct ieee80211_chanctx_conf *chanctx_conf; + struct ieee80211_supported_band *sband; int band; /* @@ -380,8 +381,9 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, sta->last_rx = jiffies; /* make sure mandatory rates are always added */ + sband = local->hw.wiphy->bands[band]; sta->sta.supp_rates[band] = supp_rates | - ieee80211_mandatory_rates(local, band); + ieee80211_mandatory_rates(sband); return ieee80211_ibss_finish_sta(sta, auth); } @@ -492,7 +494,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, prev_rates = sta->sta.supp_rates[band]; /* make sure mandatory rates are always added */ sta->sta.supp_rates[band] = supp_rates | - ieee80211_mandatory_rates(local, band); + ieee80211_mandatory_rates(sband); if (sta->sta.supp_rates[band] != prev_rates) { ibss_dbg(sdata, @@ -624,6 +626,7 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata, struct ieee80211_local *local = sdata->local; struct sta_info *sta; struct ieee80211_chanctx_conf *chanctx_conf; + struct ieee80211_supported_band *sband; int band; /* @@ -658,8 +661,9 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata, sta->last_rx = jiffies; /* make sure mandatory rates are always added */ + sband = local->hw.wiphy->bands[band]; sta->sta.supp_rates[band] = supp_rates | - ieee80211_mandatory_rates(local, band); + ieee80211_mandatory_rates(sband); spin_lock(&ifibss->incomplete_lock); list_add(&sta->list, &ifibss->incomplete_stations); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 158e6eb188d3..b7cbd4ebf0e0 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1505,9 +1505,6 @@ static inline void ieee802_11_parse_elems(u8 *start, size_t len, bool action, ieee802_11_parse_elems_crc(start, len, action, elems, 0, 0); } -u32 ieee80211_mandatory_rates(struct ieee80211_local *local, - enum ieee80211_band band); - void ieee80211_dynamic_ps_enable_work(struct work_struct *work); void ieee80211_dynamic_ps_disable_work(struct work_struct *work); void ieee80211_dynamic_ps_timer(unsigned long data); diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index c13db9ad394b..c14bb816c6a3 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -741,6 +741,8 @@ int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) BSS_CHANGED_BASIC_RATES | BSS_CHANGED_BEACON_INT; enum ieee80211_band band = ieee80211_get_sdata_band(sdata); + struct ieee80211_supported_band *sband = + sdata->local->hw.wiphy->bands[band]; local->fif_other_bss++; /* mesh ifaces must set allmulti to forward mcast traffic */ @@ -758,8 +760,7 @@ int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) sdata->vif.bss_conf.ht_operation_mode = ifmsh->mshcfg.ht_opmode; sdata->vif.bss_conf.enable_beacon = true; - sdata->vif.bss_conf.basic_rates = - ieee80211_mandatory_rates(local, band); + sdata->vif.bss_conf.basic_rates = ieee80211_mandatory_rates(sband); changed |= ieee80211_mps_local_status_update(sdata); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 3f87fa468b1f..707953fd8324 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1072,32 +1072,6 @@ void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata, ieee80211_set_wmm_default(sdata, true); } -u32 ieee80211_mandatory_rates(struct ieee80211_local *local, - enum ieee80211_band band) -{ - struct ieee80211_supported_band *sband; - struct ieee80211_rate *bitrates; - u32 mandatory_rates; - enum ieee80211_rate_flags mandatory_flag; - int i; - - sband = local->hw.wiphy->bands[band]; - if (WARN_ON(!sband)) - return 1; - - if (band == IEEE80211_BAND_2GHZ) - mandatory_flag = IEEE80211_RATE_MANDATORY_B; - else - mandatory_flag = IEEE80211_RATE_MANDATORY_A; - - bitrates = sband->bitrates; - mandatory_rates = 0; - for (i = 0; i < sband->n_bitrates; i++) - if (bitrates[i].flags & mandatory_flag) - mandatory_rates |= BIT(i); - return mandatory_rates; -} - void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, u16 transaction, u16 auth_alg, u16 status, const u8 *extra, size_t extra_len, const u8 *da, diff --git a/net/wireless/util.c b/net/wireless/util.c index b11052be09be..0962f107f57f 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -33,6 +33,29 @@ ieee80211_get_response_rate(struct ieee80211_supported_band *sband, } EXPORT_SYMBOL(ieee80211_get_response_rate); +u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband) +{ + struct ieee80211_rate *bitrates; + u32 mandatory_rates = 0; + enum ieee80211_rate_flags mandatory_flag; + int i; + + if (WARN_ON(!sband)) + return 1; + + if (sband->band == IEEE80211_BAND_2GHZ) + mandatory_flag = IEEE80211_RATE_MANDATORY_B; + else + mandatory_flag = IEEE80211_RATE_MANDATORY_A; + + bitrates = sband->bitrates; + for (i = 0; i < sband->n_bitrates; i++) + if (bitrates[i].flags & mandatory_flag) + mandatory_rates |= BIT(i); + return mandatory_rates; +} +EXPORT_SYMBOL(ieee80211_mandatory_rates); + int ieee80211_channel_to_frequency(int chan, enum ieee80211_band band) { /* see 802.11 17.3.8.3.2 and Annex J -- cgit v1.2.3 From 9f419f3851041e0c8170629f0639813dbfc79d5e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 8 May 2013 21:34:22 +0200 Subject: cfg80211: move cfg80211_get_dev_from_ifindex under wext The function is only used and needed by the wext code for scanning, so move it there. Signed-off-by: Johannes Berg --- net/wireless/core.c | 21 --------------------- net/wireless/core.h | 4 ---- net/wireless/scan.c | 21 +++++++++++++++++++++ 3 files changed, 21 insertions(+), 25 deletions(-) diff --git a/net/wireless/core.c b/net/wireless/core.c index 58e69d691601..cc49cf11c7a7 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -90,27 +90,6 @@ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx) return &rdev->wiphy; } -struct cfg80211_registered_device * -cfg80211_get_dev_from_ifindex(struct net *net, int ifindex) -{ - struct cfg80211_registered_device *rdev = ERR_PTR(-ENODEV); - struct net_device *dev; - - mutex_lock(&cfg80211_mutex); - dev = dev_get_by_index(net, ifindex); - if (!dev) - goto out; - if (dev->ieee80211_ptr) { - rdev = wiphy_to_dev(dev->ieee80211_ptr->wiphy); - mutex_lock(&rdev->mtx); - } else - rdev = ERR_PTR(-ENODEV); - dev_put(dev); - out: - mutex_unlock(&cfg80211_mutex); - return rdev; -} - /* requires cfg80211_mutex to be held */ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, char *newname) diff --git a/net/wireless/core.h b/net/wireless/core.h index fd35dae547c4..95b29075a9c8 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -164,10 +164,6 @@ int get_wiphy_idx(struct wiphy *wiphy); /* requires cfg80211_rdev_mutex to be held! */ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx); -/* identical to cfg80211_get_dev_from_info but only operate on ifindex */ -extern struct cfg80211_registered_device * -cfg80211_get_dev_from_ifindex(struct net *net, int ifindex); - int cfg80211_switch_netns(struct cfg80211_registered_device *rdev, struct net *net); diff --git a/net/wireless/scan.c b/net/wireless/scan.c index fd99ea495b7e..2ce44a712f13 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -1040,6 +1040,27 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) EXPORT_SYMBOL(cfg80211_unlink_bss); #ifdef CONFIG_CFG80211_WEXT +static struct cfg80211_registered_device * +cfg80211_get_dev_from_ifindex(struct net *net, int ifindex) +{ + struct cfg80211_registered_device *rdev = ERR_PTR(-ENODEV); + struct net_device *dev; + + mutex_lock(&cfg80211_mutex); + dev = dev_get_by_index(net, ifindex); + if (!dev) + goto out; + if (dev->ieee80211_ptr) { + rdev = wiphy_to_dev(dev->ieee80211_ptr->wiphy); + mutex_lock(&rdev->mtx); + } else + rdev = ERR_PTR(-ENODEV); + dev_put(dev); + out: + mutex_unlock(&cfg80211_mutex); + return rdev; +} + int cfg80211_wext_siwscan(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) -- cgit v1.2.3 From 73810b77def898b43a97638478692922b7f820eb Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 8 May 2013 21:49:02 +0200 Subject: cfg80211: use atomic_t for wiphy counter There's no need to lock, we can just use an atomic_t. Signed-off-by: Johannes Berg --- net/wireless/core.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/net/wireless/core.c b/net/wireless/core.c index cc49cf11c7a7..9416b8f55f50 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -289,7 +289,7 @@ static void cfg80211_event_work(struct work_struct *work) struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) { - static int wiphy_counter; + static atomic_t wiphy_counter = ATOMIC_INIT(0); struct cfg80211_registered_device *rdev; int alloc_size; @@ -311,20 +311,15 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) rdev->ops = ops; - mutex_lock(&cfg80211_mutex); - - rdev->wiphy_idx = wiphy_counter++; + rdev->wiphy_idx = atomic_inc_return(&wiphy_counter); if (unlikely(rdev->wiphy_idx < 0)) { - wiphy_counter--; - mutex_unlock(&cfg80211_mutex); /* ugh, wrapped! */ + atomic_dec(&wiphy_counter); kfree(rdev); return NULL; } - mutex_unlock(&cfg80211_mutex); - /* give it a proper name */ dev_set_name(&rdev->wiphy.dev, PHY_NAME "%d", rdev->wiphy_idx); -- cgit v1.2.3 From 5fe231e873729fa2f57cdc417d5c1f80871e2d7d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 8 May 2013 21:45:15 +0200 Subject: cfg80211: vastly simplify locking Virtually all code paths in cfg80211 already (need to) hold the RTNL. As such, there's little point in having another four mutexes for various parts of the code, they just cause lock ordering issues (and much of the time, the RTNL and a few of the others need thus be held.) Simplify all this by getting rid of the extra four mutexes and just use the RTNL throughout. Only a few code changes were needed to do this and we can get rid of a work struct for bonus points. Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 5 +- net/wireless/core.c | 166 ++++++++---------------------------------- net/wireless/core.h | 32 +------- net/wireless/debugfs.c | 4 +- net/wireless/ibss.c | 10 +-- net/wireless/mesh.c | 2 - net/wireless/mlme.c | 12 +-- net/wireless/nl80211.c | 178 +++++++++------------------------------------ net/wireless/reg.c | 36 +++------ net/wireless/scan.c | 42 +++++------ net/wireless/sme.c | 26 +------ net/wireless/util.c | 11 --- net/wireless/wext-compat.c | 22 +----- net/wireless/wext-sme.c | 18 ----- 14 files changed, 117 insertions(+), 447 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 58f6302da145..5430f70c63b3 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1257,6 +1257,7 @@ struct cfg80211_ssid { * @scan_start: time (in jiffies) when the scan started * @wdev: the wireless device to scan for * @aborted: (internal) scan request was notified as aborted + * @notified: (internal) scan request was notified as done or aborted * @no_cck: used to send probe requests at non CCK rate in 2GHz band */ struct cfg80211_scan_request { @@ -1274,7 +1275,7 @@ struct cfg80211_scan_request { /* internal */ struct wiphy *wiphy; unsigned long scan_start; - bool aborted; + bool aborted, notified; bool no_cck; /* keep last */ @@ -2874,8 +2875,6 @@ struct wireless_dev { struct mutex mtx; - struct work_struct cleanup_work; - bool use_4addr, p2p_started; u8 address[ETH_ALEN] __aligned(sizeof(u16)); diff --git a/net/wireless/core.c b/net/wireless/core.c index 9416b8f55f50..5fc642d4071b 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -36,12 +36,10 @@ MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("wireless configuration support"); MODULE_ALIAS_GENL_FAMILY(NL80211_GENL_NAME); -/* RCU-protected (and cfg80211_mutex for writers) */ +/* RCU-protected (and RTNL for writers) */ LIST_HEAD(cfg80211_rdev_list); int cfg80211_rdev_list_generation; -DEFINE_MUTEX(cfg80211_mutex); - /* for debugfs */ static struct dentry *ieee80211_debugfs_dir; @@ -53,12 +51,11 @@ module_param(cfg80211_disable_40mhz_24ghz, bool, 0644); MODULE_PARM_DESC(cfg80211_disable_40mhz_24ghz, "Disable 40MHz support in the 2.4GHz band"); -/* requires cfg80211_mutex to be held! */ struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx) { struct cfg80211_registered_device *result = NULL, *rdev; - assert_cfg80211_lock(); + ASSERT_RTNL(); list_for_each_entry(rdev, &cfg80211_rdev_list, list) { if (rdev->wiphy_idx == wiphy_idx) { @@ -77,12 +74,11 @@ int get_wiphy_idx(struct wiphy *wiphy) return rdev->wiphy_idx; } -/* requires cfg80211_rdev_mutex to be held! */ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx) { struct cfg80211_registered_device *rdev; - assert_cfg80211_lock(); + ASSERT_RTNL(); rdev = cfg80211_rdev_by_wiphy_idx(wiphy_idx); if (!rdev) @@ -90,14 +86,13 @@ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx) return &rdev->wiphy; } -/* requires cfg80211_mutex to be held */ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, char *newname) { struct cfg80211_registered_device *rdev2; int wiphy_idx, taken = -1, result, digits; - assert_cfg80211_lock(); + ASSERT_RTNL(); /* prohibit calling the thing phy%d when %d is not its number */ sscanf(newname, PHY_NAME "%d%n", &wiphy_idx, &taken); @@ -195,8 +190,7 @@ static void cfg80211_rfkill_poll(struct rfkill *rfkill, void *data) void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev) { - lockdep_assert_held(&rdev->devlist_mtx); - lockdep_assert_held(&rdev->sched_scan_mtx); + ASSERT_RTNL(); if (WARN_ON(wdev->iftype != NL80211_IFTYPE_P2P_DEVICE)) return; @@ -235,8 +229,6 @@ static int cfg80211_rfkill_set_block(void *data, bool blocked) rtnl_lock(); - /* read-only iteration need not hold the devlist_mtx */ - list_for_each_entry(wdev, &rdev->wdev_list, list) { if (wdev->netdev) { dev_close(wdev->netdev); @@ -245,12 +237,7 @@ static int cfg80211_rfkill_set_block(void *data, bool blocked) /* otherwise, check iftype */ switch (wdev->iftype) { case NL80211_IFTYPE_P2P_DEVICE: - /* but this requires it */ - mutex_lock(&rdev->devlist_mtx); - mutex_lock(&rdev->sched_scan_mtx); cfg80211_stop_p2p_device(rdev, wdev); - mutex_unlock(&rdev->sched_scan_mtx); - mutex_unlock(&rdev->devlist_mtx); break; default: break; @@ -278,10 +265,7 @@ static void cfg80211_event_work(struct work_struct *work) event_work); rtnl_lock(); - cfg80211_lock_rdev(rdev); - cfg80211_process_rdev_events(rdev); - cfg80211_unlock_rdev(rdev); rtnl_unlock(); } @@ -323,9 +307,6 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) /* give it a proper name */ dev_set_name(&rdev->wiphy.dev, PHY_NAME "%d", rdev->wiphy_idx); - mutex_init(&rdev->mtx); - mutex_init(&rdev->devlist_mtx); - mutex_init(&rdev->sched_scan_mtx); INIT_LIST_HEAD(&rdev->wdev_list); INIT_LIST_HEAD(&rdev->beacon_registrations); spin_lock_init(&rdev->beacon_registrations_lock); @@ -573,11 +554,11 @@ int wiphy_register(struct wiphy *wiphy) /* check and set up bitrates */ ieee80211_set_bitrate_flags(wiphy); - mutex_lock(&cfg80211_mutex); + rtnl_lock(); res = device_add(&rdev->wiphy.dev); if (res) { - mutex_unlock(&cfg80211_mutex); + rtnl_unlock(); return res; } @@ -606,25 +587,18 @@ int wiphy_register(struct wiphy *wiphy) } cfg80211_debugfs_rdev_add(rdev); - mutex_unlock(&cfg80211_mutex); - /* - * due to a locking dependency this has to be outside of the - * cfg80211_mutex lock - */ res = rfkill_register(rdev->rfkill); if (res) { device_del(&rdev->wiphy.dev); - mutex_lock(&cfg80211_mutex); debugfs_remove_recursive(rdev->wiphy.debugfsdir); list_del_rcu(&rdev->list); wiphy_regulatory_deregister(wiphy); - mutex_unlock(&cfg80211_mutex); + rtnl_unlock(); return res; } - rtnl_lock(); rdev->wiphy.registered = true; rtnl_unlock(); return 0; @@ -654,25 +628,19 @@ void wiphy_unregister(struct wiphy *wiphy) { struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - rtnl_lock(); - rdev->wiphy.registered = false; - rtnl_unlock(); - - rfkill_unregister(rdev->rfkill); - - /* protect the device list */ - mutex_lock(&cfg80211_mutex); - wait_event(rdev->dev_wait, ({ int __count; - mutex_lock(&rdev->devlist_mtx); + rtnl_lock(); __count = rdev->opencount; - mutex_unlock(&rdev->devlist_mtx); + rtnl_unlock(); __count == 0; })); - mutex_lock(&rdev->devlist_mtx); + rtnl_lock(); + rdev->wiphy.registered = false; + + rfkill_unregister(rdev->rfkill); + BUG_ON(!list_empty(&rdev->wdev_list)); - mutex_unlock(&rdev->devlist_mtx); /* * First remove the hardware from everywhere, this makes @@ -682,20 +650,6 @@ void wiphy_unregister(struct wiphy *wiphy) list_del_rcu(&rdev->list); synchronize_rcu(); - /* - * Try to grab rdev->mtx. If a command is still in progress, - * hopefully the driver will refuse it since it's tearing - * down the device already. We wait for this command to complete - * before unlinking the item from the list. - * Note: as codified by the BUG_ON above we cannot get here if - * a virtual interface is still present. Hence, we can only get - * to lock contention here if userspace issues a command that - * identified the hardware by wiphy index. - */ - cfg80211_lock_rdev(rdev); - /* nothing */ - cfg80211_unlock_rdev(rdev); - /* * If this device got a regulatory hint tell core its * free to listen now to a new shiny device regulatory hint @@ -705,7 +659,7 @@ void wiphy_unregister(struct wiphy *wiphy) cfg80211_rdev_list_generation++; device_del(&rdev->wiphy.dev); - mutex_unlock(&cfg80211_mutex); + rtnl_unlock(); flush_work(&rdev->scan_done_wk); cancel_work_sync(&rdev->conn_work); @@ -723,9 +677,6 @@ void cfg80211_dev_free(struct cfg80211_registered_device *rdev) struct cfg80211_internal_bss *scan, *tmp; struct cfg80211_beacon_registration *reg, *treg; rfkill_destroy(rdev->rfkill); - mutex_destroy(&rdev->mtx); - mutex_destroy(&rdev->devlist_mtx); - mutex_destroy(&rdev->sched_scan_mtx); list_for_each_entry_safe(reg, treg, &rdev->beacon_registrations, list) { list_del(®->list); kfree(reg); @@ -750,36 +701,6 @@ void wiphy_rfkill_set_hw_state(struct wiphy *wiphy, bool blocked) } EXPORT_SYMBOL(wiphy_rfkill_set_hw_state); -static void wdev_cleanup_work(struct work_struct *work) -{ - struct wireless_dev *wdev; - struct cfg80211_registered_device *rdev; - - wdev = container_of(work, struct wireless_dev, cleanup_work); - rdev = wiphy_to_dev(wdev->wiphy); - - mutex_lock(&rdev->sched_scan_mtx); - - if (WARN_ON(rdev->scan_req && rdev->scan_req->wdev == wdev)) { - rdev->scan_req->aborted = true; - ___cfg80211_scan_done(rdev, true); - } - - if (WARN_ON(rdev->sched_scan_req && - rdev->sched_scan_req->dev == wdev->netdev)) { - __cfg80211_stop_sched_scan(rdev, false); - } - - mutex_unlock(&rdev->sched_scan_mtx); - - mutex_lock(&rdev->devlist_mtx); - rdev->opencount--; - mutex_unlock(&rdev->devlist_mtx); - wake_up(&rdev->dev_wait); - - dev_put(wdev->netdev); -} - void cfg80211_unregister_wdev(struct wireless_dev *wdev) { struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); @@ -789,8 +710,6 @@ void cfg80211_unregister_wdev(struct wireless_dev *wdev) if (WARN_ON(wdev->netdev)) return; - mutex_lock(&rdev->devlist_mtx); - mutex_lock(&rdev->sched_scan_mtx); list_del_rcu(&wdev->list); rdev->devlist_generation++; @@ -802,8 +721,6 @@ void cfg80211_unregister_wdev(struct wireless_dev *wdev) WARN_ON_ONCE(1); break; } - mutex_unlock(&rdev->sched_scan_mtx); - mutex_unlock(&rdev->devlist_mtx); } EXPORT_SYMBOL(cfg80211_unregister_wdev); @@ -822,7 +739,7 @@ void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, } void cfg80211_leave(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev) + struct wireless_dev *wdev) { struct net_device *dev = wdev->netdev; @@ -832,9 +749,7 @@ void cfg80211_leave(struct cfg80211_registered_device *rdev, break; case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_STATION: - mutex_lock(&rdev->sched_scan_mtx); __cfg80211_stop_sched_scan(rdev, false); - mutex_unlock(&rdev->sched_scan_mtx); wdev_lock(wdev); #ifdef CONFIG_CFG80211_WEXT @@ -887,13 +802,11 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, * are added with nl80211. */ mutex_init(&wdev->mtx); - INIT_WORK(&wdev->cleanup_work, wdev_cleanup_work); INIT_LIST_HEAD(&wdev->event_list); spin_lock_init(&wdev->event_lock); INIT_LIST_HEAD(&wdev->mgmt_registrations); spin_lock_init(&wdev->mgmt_registrations_lock); - mutex_lock(&rdev->devlist_mtx); wdev->identifier = ++rdev->wdev_id; list_add_rcu(&wdev->list, &rdev->wdev_list); rdev->devlist_generation++; @@ -906,7 +819,6 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, } wdev->netdev = dev; wdev->sme_state = CFG80211_SME_IDLE; - mutex_unlock(&rdev->devlist_mtx); #ifdef CONFIG_CFG80211_WEXT wdev->wext.default_key = -1; wdev->wext.default_mgmt_key = -1; @@ -932,26 +844,22 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, break; case NETDEV_DOWN: cfg80211_update_iface_num(rdev, wdev->iftype, -1); - dev_hold(dev); - queue_work(cfg80211_wq, &wdev->cleanup_work); + if (rdev->scan_req && rdev->scan_req->wdev == wdev) { + if (WARN_ON(!rdev->scan_req->notified)) + rdev->scan_req->aborted = true; + ___cfg80211_scan_done(rdev, true); + } + + if (WARN_ON(rdev->sched_scan_req && + rdev->sched_scan_req->dev == wdev->netdev)) { + __cfg80211_stop_sched_scan(rdev, false); + } + + rdev->opencount--; + wake_up(&rdev->dev_wait); break; case NETDEV_UP: - /* - * If we have a really quick DOWN/UP succession we may - * have this work still pending ... cancel it and see - * if it was pending, in which case we need to account - * for some of the work it would have done. - */ - if (cancel_work_sync(&wdev->cleanup_work)) { - mutex_lock(&rdev->devlist_mtx); - rdev->opencount--; - mutex_unlock(&rdev->devlist_mtx); - dev_put(dev); - } cfg80211_update_iface_num(rdev, wdev->iftype, 1); - cfg80211_lock_rdev(rdev); - mutex_lock(&rdev->devlist_mtx); - mutex_lock(&rdev->sched_scan_mtx); wdev_lock(wdev); switch (wdev->iftype) { #ifdef CONFIG_CFG80211_WEXT @@ -983,10 +891,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, break; } wdev_unlock(wdev); - mutex_unlock(&rdev->sched_scan_mtx); rdev->opencount++; - mutex_unlock(&rdev->devlist_mtx); - cfg80211_unlock_rdev(rdev); /* * Configure power management to the driver here so that its @@ -1002,12 +907,6 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, } break; case NETDEV_UNREGISTER: - /* - * NB: cannot take rdev->mtx here because this may be - * called within code protected by it when interfaces - * are removed with nl80211. - */ - mutex_lock(&rdev->devlist_mtx); /* * It is possible to get NETDEV_UNREGISTER * multiple times. To detect that, check @@ -1024,7 +923,6 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, kfree(wdev->wext.keys); #endif } - mutex_unlock(&rdev->devlist_mtx); /* * synchronise (so that we won't find this netdev * from other code any more) and then clear the list @@ -1044,9 +942,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, return notifier_from_errno(-EOPNOTSUPP); if (rfkill_blocked(rdev->rfkill)) return notifier_from_errno(-ERFKILL); - mutex_lock(&rdev->devlist_mtx); ret = cfg80211_can_add_interface(rdev, wdev->iftype); - mutex_unlock(&rdev->devlist_mtx); if (ret) return notifier_from_errno(ret); break; @@ -1064,12 +960,10 @@ static void __net_exit cfg80211_pernet_exit(struct net *net) struct cfg80211_registered_device *rdev; rtnl_lock(); - mutex_lock(&cfg80211_mutex); list_for_each_entry(rdev, &cfg80211_rdev_list, list) { if (net_eq(wiphy_net(&rdev->wiphy), net)) WARN_ON(cfg80211_switch_netns(rdev, &init_net)); } - mutex_unlock(&cfg80211_mutex); rtnl_unlock(); } diff --git a/net/wireless/core.h b/net/wireless/core.h index 95b29075a9c8..d21a0fc01401 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -5,7 +5,6 @@ */ #ifndef __NET_WIRELESS_CORE_H #define __NET_WIRELESS_CORE_H -#include #include #include #include @@ -23,11 +22,6 @@ struct cfg80211_registered_device { const struct cfg80211_ops *ops; struct list_head list; - /* we hold this mutex during any call so that - * we cannot do multiple calls at once, and also - * to avoid the deregister call to proceed while - * any call is in progress */ - struct mutex mtx; /* rfkill support */ struct rfkill_ops rfkill_ops; @@ -49,9 +43,7 @@ struct cfg80211_registered_device { /* wiphy index, internal only */ int wiphy_idx; - /* associated wireless interfaces */ - struct mutex devlist_mtx; - /* protected by devlist_mtx or RCU */ + /* associated wireless interfaces, protected by rtnl or RCU */ struct list_head wdev_list; int devlist_generation, wdev_id; int opencount; /* also protected by devlist_mtx */ @@ -75,8 +67,6 @@ struct cfg80211_registered_device { struct work_struct scan_done_wk; struct work_struct sched_scan_results_wk; - struct mutex sched_scan_mtx; - #ifdef CONFIG_NL80211_TESTMODE struct genl_info *testmode_info; #endif @@ -120,15 +110,9 @@ cfg80211_rdev_free_wowlan(struct cfg80211_registered_device *rdev) } extern struct workqueue_struct *cfg80211_wq; -extern struct mutex cfg80211_mutex; extern struct list_head cfg80211_rdev_list; extern int cfg80211_rdev_list_generation; -static inline void assert_cfg80211_lock(void) -{ - lockdep_assert_held(&cfg80211_mutex); -} - struct cfg80211_internal_bss { struct list_head list; struct list_head hidden_list; @@ -161,23 +145,11 @@ static inline void cfg80211_unhold_bss(struct cfg80211_internal_bss *bss) struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx); int get_wiphy_idx(struct wiphy *wiphy); -/* requires cfg80211_rdev_mutex to be held! */ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx); int cfg80211_switch_netns(struct cfg80211_registered_device *rdev, struct net *net); -static inline void cfg80211_lock_rdev(struct cfg80211_registered_device *rdev) -{ - mutex_lock(&rdev->mtx); -} - -static inline void cfg80211_unlock_rdev(struct cfg80211_registered_device *rdev) -{ - BUG_ON(IS_ERR(rdev) || !rdev); - mutex_unlock(&rdev->mtx); -} - static inline void wdev_lock(struct wireless_dev *wdev) __acquires(wdev) { @@ -192,7 +164,7 @@ static inline void wdev_unlock(struct wireless_dev *wdev) mutex_unlock(&wdev->mtx); } -#define ASSERT_RDEV_LOCK(rdev) lockdep_assert_held(&(rdev)->mtx) +#define ASSERT_RDEV_LOCK(rdev) ASSERT_RTNL() #define ASSERT_WDEV_LOCK(wdev) lockdep_assert_held(&(wdev)->mtx) static inline bool cfg80211_has_monitors_only(struct cfg80211_registered_device *rdev) diff --git a/net/wireless/debugfs.c b/net/wireless/debugfs.c index 920cabe0461b..90d050036624 100644 --- a/net/wireless/debugfs.c +++ b/net/wireless/debugfs.c @@ -74,7 +74,7 @@ static ssize_t ht40allow_map_read(struct file *file, if (!buf) return -ENOMEM; - mutex_lock(&cfg80211_mutex); + rtnl_lock(); for (band = 0; band < IEEE80211_NUM_BANDS; band++) { sband = wiphy->bands[band]; @@ -85,7 +85,7 @@ static ssize_t ht40allow_map_read(struct file *file, buf, buf_size, offset); } - mutex_unlock(&cfg80211_mutex); + rtnl_unlock(); r = simple_read_from_buffer(user_buf, count, ppos, buf, offset); diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index d80e47194d49..5449c5a6de84 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -152,11 +152,11 @@ int cfg80211_join_ibss(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev = dev->ieee80211_ptr; int err; - mutex_lock(&rdev->devlist_mtx); + ASSERT_RTNL(); + wdev_lock(wdev); err = __cfg80211_join_ibss(rdev, dev, params, connkeys); wdev_unlock(wdev); - mutex_unlock(&rdev->devlist_mtx); return err; } @@ -359,11 +359,9 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev, wdev->wext.ibss.channel_fixed = false; } - mutex_lock(&rdev->devlist_mtx); wdev_lock(wdev); err = cfg80211_ibss_wext_join(rdev, wdev); wdev_unlock(wdev); - mutex_unlock(&rdev->devlist_mtx); return err; } @@ -429,11 +427,9 @@ int cfg80211_ibss_wext_siwessid(struct net_device *dev, memcpy(wdev->wext.ibss.ssid, ssid, len); wdev->wext.ibss.ssid_len = len; - mutex_lock(&rdev->devlist_mtx); wdev_lock(wdev); err = cfg80211_ibss_wext_join(rdev, wdev); wdev_unlock(wdev); - mutex_unlock(&rdev->devlist_mtx); return err; } @@ -512,11 +508,9 @@ int cfg80211_ibss_wext_siwap(struct net_device *dev, } else wdev->wext.ibss.bssid = NULL; - mutex_lock(&rdev->devlist_mtx); wdev_lock(wdev); err = cfg80211_ibss_wext_join(rdev, wdev); wdev_unlock(wdev); - mutex_unlock(&rdev->devlist_mtx); return err; } diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 9546ad210550..5dfb289ab761 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -186,11 +186,9 @@ int cfg80211_join_mesh(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev = dev->ieee80211_ptr; int err; - mutex_lock(&rdev->devlist_mtx); wdev_lock(wdev); err = __cfg80211_join_mesh(rdev, dev, setup, conf); wdev_unlock(wdev); - mutex_unlock(&rdev->devlist_mtx); return err; } diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index c21e32f9549c..68b40f21bc38 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -313,14 +313,14 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, { int err; - mutex_lock(&rdev->devlist_mtx); + ASSERT_RTNL(); + wdev_lock(dev->ieee80211_ptr); err = __cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid, ssid, ssid_len, ie, ie_len, key, key_len, key_idx, sae_data, sae_data_len); wdev_unlock(dev->ieee80211_ptr); - mutex_unlock(&rdev->devlist_mtx); return err; } @@ -424,12 +424,12 @@ int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev = dev->ieee80211_ptr; int err; - mutex_lock(&rdev->devlist_mtx); + ASSERT_RTNL(); + wdev_lock(wdev); err = __cfg80211_mlme_assoc(rdev, dev, chan, bssid, ssid, ssid_len, req); wdev_unlock(wdev); - mutex_unlock(&rdev->devlist_mtx); return err; } @@ -844,7 +844,7 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work) dfs_update_channels_wk); wiphy = &rdev->wiphy; - mutex_lock(&cfg80211_mutex); + rtnl_lock(); for (bandid = 0; bandid < IEEE80211_NUM_BANDS; bandid++) { sband = wiphy->bands[bandid]; if (!sband) @@ -877,7 +877,7 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work) check_again = true; } } - mutex_unlock(&cfg80211_mutex); + rtnl_unlock(); /* reschedule if there are other channels waiting to be cleared again */ if (check_again) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 5bcf3a5b6465..74cdb1a0cf31 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -59,7 +59,7 @@ __cfg80211_wdev_from_attrs(struct net *netns, struct nlattr **attrs) int wiphy_idx = -1; int ifidx = -1; - assert_cfg80211_lock(); + ASSERT_RTNL(); if (!have_ifidx && !have_wdev_id) return ERR_PTR(-EINVAL); @@ -80,7 +80,6 @@ __cfg80211_wdev_from_attrs(struct net *netns, struct nlattr **attrs) if (have_wdev_id && rdev->wiphy_idx != wiphy_idx) continue; - mutex_lock(&rdev->devlist_mtx); list_for_each_entry(wdev, &rdev->wdev_list, list) { if (have_ifidx && wdev->netdev && wdev->netdev->ifindex == ifidx) { @@ -92,7 +91,6 @@ __cfg80211_wdev_from_attrs(struct net *netns, struct nlattr **attrs) break; } } - mutex_unlock(&rdev->devlist_mtx); if (result) break; @@ -109,7 +107,7 @@ __cfg80211_rdev_from_attrs(struct net *netns, struct nlattr **attrs) struct cfg80211_registered_device *rdev = NULL, *tmp; struct net_device *netdev; - assert_cfg80211_lock(); + ASSERT_RTNL(); if (!attrs[NL80211_ATTR_WIPHY] && !attrs[NL80211_ATTR_IFINDEX] && @@ -128,14 +126,12 @@ __cfg80211_rdev_from_attrs(struct net *netns, struct nlattr **attrs) tmp = cfg80211_rdev_by_wiphy_idx(wdev_id >> 32); if (tmp) { /* make sure wdev exists */ - mutex_lock(&tmp->devlist_mtx); list_for_each_entry(wdev, &tmp->wdev_list, list) { if (wdev->identifier != (u32)wdev_id) continue; found = true; break; } - mutex_unlock(&tmp->devlist_mtx); if (!found) tmp = NULL; @@ -182,19 +178,6 @@ __cfg80211_rdev_from_attrs(struct net *netns, struct nlattr **attrs) /* * This function returns a pointer to the driver * that the genl_info item that is passed refers to. - * If successful, it returns non-NULL and also locks - * the driver's mutex! - * - * This means that you need to call cfg80211_unlock_rdev() - * before being allowed to acquire &cfg80211_mutex! - * - * This is necessary because we need to lock the global - * mutex to get an item off the list safely, and then - * we lock the rdev mutex so it doesn't go away under us. - * - * We don't want to keep cfg80211_mutex locked - * for all the time in order to allow requests on - * other interfaces to go through at the same time. * * The result of this can be a PTR_ERR and hence must * be checked with IS_ERR() for errors. @@ -202,20 +185,7 @@ __cfg80211_rdev_from_attrs(struct net *netns, struct nlattr **attrs) static struct cfg80211_registered_device * cfg80211_get_dev_from_info(struct net *netns, struct genl_info *info) { - struct cfg80211_registered_device *rdev; - - mutex_lock(&cfg80211_mutex); - rdev = __cfg80211_rdev_from_attrs(netns, info->attrs); - - /* if it is not an error we grab the lock on - * it to assure it won't be going away while - * we operate on it */ - if (!IS_ERR(rdev)) - mutex_lock(&rdev->mtx); - - mutex_unlock(&cfg80211_mutex); - - return rdev; + return __cfg80211_rdev_from_attrs(netns, info->attrs); } /* policy for the attributes */ @@ -456,7 +426,6 @@ static int nl80211_prepare_wdev_dump(struct sk_buff *skb, int err; rtnl_lock(); - mutex_lock(&cfg80211_mutex); if (!cb->args[0]) { err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, @@ -485,14 +454,12 @@ static int nl80211_prepare_wdev_dump(struct sk_buff *skb, *rdev = wiphy_to_dev(wiphy); *wdev = NULL; - mutex_lock(&(*rdev)->devlist_mtx); list_for_each_entry(tmp, &(*rdev)->wdev_list, list) { if (tmp->identifier == cb->args[1]) { *wdev = tmp; break; } } - mutex_unlock(&(*rdev)->devlist_mtx); if (!*wdev) { err = -ENODEV; @@ -500,19 +467,14 @@ static int nl80211_prepare_wdev_dump(struct sk_buff *skb, } } - cfg80211_lock_rdev(*rdev); - - mutex_unlock(&cfg80211_mutex); return 0; out_unlock: - mutex_unlock(&cfg80211_mutex); rtnl_unlock(); return err; } static void nl80211_finish_wdev_dump(struct cfg80211_registered_device *rdev) { - cfg80211_unlock_rdev(rdev); rtnl_unlock(); } @@ -1568,7 +1530,7 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb) struct nlattr **tb = nl80211_fam.attrbuf; int res; - mutex_lock(&cfg80211_mutex); + rtnl_lock(); res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, tb, nl80211_fam.maxattr, nl80211_policy); if (res == 0) { @@ -1582,10 +1544,8 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb) int ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); netdev = dev_get_by_index(sock_net(skb->sk), ifidx); - if (!netdev) { - mutex_unlock(&cfg80211_mutex); + if (!netdev) return -ENODEV; - } if (netdev->ieee80211_ptr) { dev = wiphy_to_dev( netdev->ieee80211_ptr->wiphy); @@ -1629,7 +1589,6 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb) !skb->len && cb->min_dump_alloc < 4096) { cb->min_dump_alloc = 4096; - mutex_unlock(&cfg80211_mutex); return 1; } idx--; @@ -1638,7 +1597,7 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb) } while (cb->args[1] > 0); break; } - mutex_unlock(&cfg80211_mutex); + rtnl_unlock(); cb->args[0] = idx; @@ -1793,7 +1752,6 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev, if (result) return result; - mutex_lock(&rdev->devlist_mtx); switch (iftype) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: @@ -1817,7 +1775,6 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev, default: result = -EINVAL; } - mutex_unlock(&rdev->devlist_mtx); return result; } @@ -1866,6 +1823,8 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) u32 frag_threshold = 0, rts_threshold = 0; u8 coverage_class = 0; + ASSERT_RTNL(); + /* * Try to find the wiphy and netdev. Normally this * function shouldn't need the netdev, but this is @@ -1875,31 +1834,25 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) * also passed a netdev to set_wiphy, so that it is * possible to let that go to the right netdev! */ - mutex_lock(&cfg80211_mutex); if (info->attrs[NL80211_ATTR_IFINDEX]) { int ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]); netdev = dev_get_by_index(genl_info_net(info), ifindex); - if (netdev && netdev->ieee80211_ptr) { + if (netdev && netdev->ieee80211_ptr) rdev = wiphy_to_dev(netdev->ieee80211_ptr->wiphy); - mutex_lock(&rdev->mtx); - } else + else netdev = NULL; } if (!netdev) { rdev = __cfg80211_rdev_from_attrs(genl_info_net(info), info->attrs); - if (IS_ERR(rdev)) { - mutex_unlock(&cfg80211_mutex); + if (IS_ERR(rdev)) return PTR_ERR(rdev); - } wdev = NULL; netdev = NULL; result = 0; - - mutex_lock(&rdev->mtx); } else wdev = netdev->ieee80211_ptr; @@ -1912,8 +1865,6 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) result = cfg80211_dev_rename( rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME])); - mutex_unlock(&cfg80211_mutex); - if (result) goto bad_res; @@ -2120,7 +2071,6 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) } bad_res: - mutex_unlock(&rdev->mtx); if (netdev) dev_put(netdev); return result; @@ -2218,7 +2168,7 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback * struct cfg80211_registered_device *rdev; struct wireless_dev *wdev; - mutex_lock(&cfg80211_mutex); + rtnl_lock(); list_for_each_entry(rdev, &cfg80211_rdev_list, list) { if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk))) continue; @@ -2228,7 +2178,6 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback * } if_idx = 0; - mutex_lock(&rdev->devlist_mtx); list_for_each_entry(wdev, &rdev->wdev_list, list) { if (if_idx < if_start) { if_idx++; @@ -2237,17 +2186,15 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback * if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, rdev, wdev) < 0) { - mutex_unlock(&rdev->devlist_mtx); goto out; } if_idx++; } - mutex_unlock(&rdev->devlist_mtx); wp_idx++; } out: - mutex_unlock(&cfg80211_mutex); + rtnl_unlock(); cb->args[0] = wp_idx; cb->args[1] = if_idx; @@ -2480,11 +2427,9 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) INIT_LIST_HEAD(&wdev->mgmt_registrations); spin_lock_init(&wdev->mgmt_registrations_lock); - mutex_lock(&rdev->devlist_mtx); wdev->identifier = ++rdev->wdev_id; list_add_rcu(&wdev->list, &rdev->wdev_list); rdev->devlist_generation++; - mutex_unlock(&rdev->devlist_mtx); break; default: break; @@ -2993,8 +2938,6 @@ static bool nl80211_get_ap_channel(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev; bool ret = false; - mutex_lock(&rdev->devlist_mtx); - list_for_each_entry(wdev, &rdev->wdev_list, list) { if (wdev->iftype != NL80211_IFTYPE_AP && wdev->iftype != NL80211_IFTYPE_P2P_GO) @@ -3008,8 +2951,6 @@ static bool nl80211_get_ap_channel(struct cfg80211_registered_device *rdev, break; } - mutex_unlock(&rdev->devlist_mtx); - return ret; } @@ -3171,13 +3112,10 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) params.radar_required = true; } - mutex_lock(&rdev->devlist_mtx); err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype, params.chandef.chan, CHAN_MODE_SHARED, radar_detect_width); - mutex_unlock(&rdev->devlist_mtx); - if (err) return err; @@ -4914,18 +4852,13 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) void *hdr = NULL; struct nlattr *nl_reg_rules; unsigned int i; - int err = -EINVAL; - - mutex_lock(&cfg80211_mutex); if (!cfg80211_regdomain) - goto out; + return -EINVAL; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) { - err = -ENOBUFS; - goto out; - } + if (!msg) + return -ENOBUFS; hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0, NL80211_CMD_GET_REG); @@ -4984,8 +4917,7 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) nla_nest_end(msg, nl_reg_rules); genlmsg_end(msg, hdr); - err = genlmsg_reply(msg, info); - goto out; + return genlmsg_reply(msg, info); nla_put_failure_rcu: rcu_read_unlock(); @@ -4993,10 +4925,7 @@ nla_put_failure: genlmsg_cancel(msg, hdr); put_failure: nlmsg_free(msg); - err = -EMSGSIZE; -out: - mutex_unlock(&cfg80211_mutex); - return err; + return -EMSGSIZE; } static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) @@ -5062,12 +4991,9 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) } } - mutex_lock(&cfg80211_mutex); - r = set_regdom(rd); /* set_regdom took ownership */ rd = NULL; - mutex_unlock(&cfg80211_mutex); bad_reg: kfree(rd); @@ -5117,7 +5043,6 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) if (!rdev->ops->scan) return -EOPNOTSUPP; - mutex_lock(&rdev->sched_scan_mtx); if (rdev->scan_req) { err = -EBUSY; goto unlock; @@ -5303,7 +5228,6 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) } unlock: - mutex_unlock(&rdev->sched_scan_mtx); return err; } @@ -5375,8 +5299,6 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, if (ie_len > wiphy->max_sched_scan_ie_len) return -EINVAL; - mutex_lock(&rdev->sched_scan_mtx); - if (rdev->sched_scan_req) { err = -EINPROGRESS; goto out; @@ -5544,7 +5466,6 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, out_free: kfree(request); out: - mutex_unlock(&rdev->sched_scan_mtx); return err; } @@ -5552,17 +5473,12 @@ static int nl80211_stop_sched_scan(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; - int err; if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) || !rdev->ops->sched_scan_stop) return -EOPNOTSUPP; - mutex_lock(&rdev->sched_scan_mtx); - err = __cfg80211_stop_sched_scan(rdev, false); - mutex_unlock(&rdev->sched_scan_mtx); - - return err; + return __cfg80211_stop_sched_scan(rdev, false); } static int nl80211_start_radar_detection(struct sk_buff *skb, @@ -5594,12 +5510,11 @@ static int nl80211_start_radar_detection(struct sk_buff *skb, if (!rdev->ops->start_radar_detection) return -EOPNOTSUPP; - mutex_lock(&rdev->devlist_mtx); err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype, chandef.chan, CHAN_MODE_SHARED, BIT(chandef.width)); if (err) - goto err_locked; + return err; err = rdev->ops->start_radar_detection(&rdev->wiphy, dev, &chandef); if (!err) { @@ -5607,9 +5522,6 @@ static int nl80211_start_radar_detection(struct sk_buff *skb, wdev->cac_started = true; wdev->cac_start_time = jiffies; } -err_locked: - mutex_unlock(&rdev->devlist_mtx); - return err; } @@ -6472,6 +6384,8 @@ static int nl80211_testmode_dump(struct sk_buff *skb, void *data = NULL; int data_len = 0; + rtnl_lock(); + if (cb->args[0]) { /* * 0 is a valid index, but not valid for args[0], @@ -6483,18 +6397,16 @@ static int nl80211_testmode_dump(struct sk_buff *skb, nl80211_fam.attrbuf, nl80211_fam.maxattr, nl80211_policy); if (err) - return err; + goto out_err; - mutex_lock(&cfg80211_mutex); rdev = __cfg80211_rdev_from_attrs(sock_net(skb->sk), nl80211_fam.attrbuf); if (IS_ERR(rdev)) { - mutex_unlock(&cfg80211_mutex); - return PTR_ERR(rdev); + err = PTR_ERR(rdev); + goto out_err; } phy_idx = rdev->wiphy_idx; rdev = NULL; - mutex_unlock(&cfg80211_mutex); if (nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA]) cb->args[1] = @@ -6506,14 +6418,11 @@ static int nl80211_testmode_dump(struct sk_buff *skb, data_len = nla_len((void *)cb->args[1]); } - mutex_lock(&cfg80211_mutex); rdev = cfg80211_rdev_by_wiphy_idx(phy_idx); if (!rdev) { - mutex_unlock(&cfg80211_mutex); - return -ENOENT; + err = -ENOENT; + goto out_err; } - cfg80211_lock_rdev(rdev); - mutex_unlock(&cfg80211_mutex); if (!rdev->ops->testmode_dump) { err = -EOPNOTSUPP; @@ -6554,7 +6463,7 @@ static int nl80211_testmode_dump(struct sk_buff *skb, /* see above */ cb->args[0] = phy_idx + 1; out_err: - cfg80211_unlock_rdev(rdev); + rtnl_unlock(); return err; } @@ -8189,9 +8098,7 @@ static int nl80211_start_p2p_device(struct sk_buff *skb, struct genl_info *info) if (wdev->p2p_started) return 0; - mutex_lock(&rdev->devlist_mtx); err = cfg80211_can_add_interface(rdev, wdev->iftype); - mutex_unlock(&rdev->devlist_mtx); if (err) return err; @@ -8200,9 +8107,7 @@ static int nl80211_start_p2p_device(struct sk_buff *skb, struct genl_info *info) return err; wdev->p2p_started = true; - mutex_lock(&rdev->devlist_mtx); rdev->opencount++; - mutex_unlock(&rdev->devlist_mtx); return 0; } @@ -8218,11 +8123,7 @@ static int nl80211_stop_p2p_device(struct sk_buff *skb, struct genl_info *info) if (!rdev->ops->stop_p2p_device) return -EOPNOTSUPP; - mutex_lock(&rdev->devlist_mtx); - mutex_lock(&rdev->sched_scan_mtx); cfg80211_stop_p2p_device(rdev, wdev); - mutex_unlock(&rdev->sched_scan_mtx); - mutex_unlock(&rdev->devlist_mtx); return 0; } @@ -8365,11 +8266,11 @@ static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb, info->user_ptr[0] = rdev; } else if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV || ops->internal_flags & NL80211_FLAG_NEED_WDEV) { - mutex_lock(&cfg80211_mutex); + ASSERT_RTNL(); + wdev = __cfg80211_wdev_from_attrs(genl_info_net(info), info->attrs); if (IS_ERR(wdev)) { - mutex_unlock(&cfg80211_mutex); if (rtnl) rtnl_unlock(); return PTR_ERR(wdev); @@ -8380,7 +8281,6 @@ static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb, if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV) { if (!dev) { - mutex_unlock(&cfg80211_mutex); if (rtnl) rtnl_unlock(); return -EINVAL; @@ -8394,7 +8294,6 @@ static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb, if (dev) { if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP && !netif_running(dev)) { - mutex_unlock(&cfg80211_mutex); if (rtnl) rtnl_unlock(); return -ENETDOWN; @@ -8403,17 +8302,12 @@ static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb, dev_hold(dev); } else if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP) { if (!wdev->p2p_started) { - mutex_unlock(&cfg80211_mutex); if (rtnl) rtnl_unlock(); return -ENETDOWN; } } - cfg80211_lock_rdev(rdev); - - mutex_unlock(&cfg80211_mutex); - info->user_ptr[0] = rdev; } @@ -8423,8 +8317,6 @@ static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb, static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb, struct genl_info *info) { - if (info->user_ptr[0]) - cfg80211_unlock_rdev(info->user_ptr[0]); if (info->user_ptr[1]) { if (ops->internal_flags & NL80211_FLAG_NEED_WDEV) { struct wireless_dev *wdev = info->user_ptr[1]; @@ -8446,7 +8338,8 @@ static struct genl_ops nl80211_ops[] = { .dumpit = nl80211_dump_wiphy, .policy = nl80211_policy, /* can be retrieved by unprivileged users */ - .internal_flags = NL80211_FLAG_NEED_WIPHY, + .internal_flags = NL80211_FLAG_NEED_WIPHY | + NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_SET_WIPHY, @@ -8461,7 +8354,8 @@ static struct genl_ops nl80211_ops[] = { .dumpit = nl80211_dump_interface, .policy = nl80211_policy, /* can be retrieved by unprivileged users */ - .internal_flags = NL80211_FLAG_NEED_WDEV, + .internal_flags = NL80211_FLAG_NEED_WDEV | + NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_SET_INTERFACE, @@ -8620,6 +8514,7 @@ static struct genl_ops nl80211_ops[] = { .cmd = NL80211_CMD_GET_REG, .doit = nl80211_get_reg, .policy = nl80211_policy, + .internal_flags = NL80211_FLAG_NEED_RTNL, /* can be retrieved by unprivileged users */ }, { @@ -8627,6 +8522,7 @@ static struct genl_ops nl80211_ops[] = { .doit = nl80211_set_reg, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_RTNL, }, { .cmd = NL80211_CMD_REQ_SET_REG, @@ -9082,8 +8978,6 @@ static int nl80211_add_scan_req(struct sk_buff *msg, struct nlattr *nest; int i; - lockdep_assert_held(&rdev->sched_scan_mtx); - if (WARN_ON(!req)) return 0; diff --git a/net/wireless/reg.c b/net/wireless/reg.c index cc35fbaa4578..e76559618588 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -377,7 +377,7 @@ static void reg_regdb_search(struct work_struct *work) const struct ieee80211_regdomain *curdom, *regdom = NULL; int i; - mutex_lock(&cfg80211_mutex); + rtnl_lock(); mutex_lock(®_regdb_search_mutex); while (!list_empty(®_regdb_search_list)) { @@ -402,7 +402,7 @@ static void reg_regdb_search(struct work_struct *work) if (!IS_ERR_OR_NULL(regdom)) set_regdom(regdom); - mutex_unlock(&cfg80211_mutex); + rtnl_unlock(); } static DECLARE_WORK(reg_regdb_work, reg_regdb_search); @@ -1225,7 +1225,7 @@ static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) struct cfg80211_registered_device *rdev; struct wiphy *wiphy; - assert_cfg80211_lock(); + ASSERT_RTNL(); list_for_each_entry(rdev, &cfg80211_rdev_list, list) { wiphy = &rdev->wiphy; @@ -1570,21 +1570,19 @@ static void reg_process_pending_hints(void) { struct regulatory_request *reg_request, *lr; - mutex_lock(&cfg80211_mutex); - mutex_lock(®_mutex); lr = get_last_request(); /* When last_request->processed becomes true this will be rescheduled */ if (lr && !lr->processed) { REG_DBG_PRINT("Pending regulatory request, waiting for it to be processed...\n"); - goto out; + return; } spin_lock(®_requests_lock); if (list_empty(®_requests_list)) { spin_unlock(®_requests_lock); - goto out; + return; } reg_request = list_first_entry(®_requests_list, @@ -1595,10 +1593,6 @@ static void reg_process_pending_hints(void) spin_unlock(®_requests_lock); reg_process_hint(reg_request, reg_request->initiator); - -out: - mutex_unlock(®_mutex); - mutex_unlock(&cfg80211_mutex); } /* Processes beacon hints -- this has nothing to do with country IEs */ @@ -1607,9 +1601,6 @@ static void reg_process_pending_beacon_hints(void) struct cfg80211_registered_device *rdev; struct reg_beacon *pending_beacon, *tmp; - mutex_lock(&cfg80211_mutex); - mutex_lock(®_mutex); - /* This goes through the _pending_ beacon list */ spin_lock_bh(®_pending_beacons_lock); @@ -1626,14 +1617,16 @@ static void reg_process_pending_beacon_hints(void) } spin_unlock_bh(®_pending_beacons_lock); - mutex_unlock(®_mutex); - mutex_unlock(&cfg80211_mutex); } static void reg_todo(struct work_struct *work) { + rtnl_lock(); + mutex_lock(®_mutex); reg_process_pending_hints(); reg_process_pending_beacon_hints(); + mutex_unlock(®_mutex); + rtnl_unlock(); } static void queue_regulatory_request(struct regulatory_request *request) @@ -1717,10 +1710,6 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2) } EXPORT_SYMBOL(regulatory_hint); -/* - * We hold wdev_lock() here so we cannot hold cfg80211_mutex() and - * therefore cannot iterate over the rdev list here. - */ void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band, const u8 *country_ie, u8 country_ie_len) { @@ -1752,7 +1741,7 @@ void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band, /* * We will run this only upon a successful connection on cfg80211. * We leave conflict resolution to the workqueue, where can hold - * cfg80211_mutex. + * the RTNL. */ if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && lr->wiphy_idx != WIPHY_IDX_INVALID) @@ -1858,7 +1847,8 @@ static void restore_regulatory_settings(bool reset_user) LIST_HEAD(tmp_reg_req_list); struct cfg80211_registered_device *rdev; - mutex_lock(&cfg80211_mutex); + ASSERT_RTNL(); + mutex_lock(®_mutex); reset_regdomains(true, &world_regdom); @@ -1915,7 +1905,6 @@ static void restore_regulatory_settings(bool reset_user) spin_unlock(®_requests_lock); mutex_unlock(®_mutex); - mutex_unlock(&cfg80211_mutex); REG_DBG_PRINT("Kicking the queue\n"); @@ -2297,7 +2286,6 @@ void wiphy_regulatory_register(struct wiphy *wiphy) mutex_unlock(®_mutex); } -/* Caller must hold cfg80211_mutex */ void wiphy_regulatory_deregister(struct wiphy *wiphy) { struct wiphy *request_wiphy = NULL; diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 2ce44a712f13..dd01b58fa78c 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -169,7 +169,7 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak) union iwreq_data wrqu; #endif - lockdep_assert_held(&rdev->sched_scan_mtx); + ASSERT_RTNL(); request = rdev->scan_req; @@ -230,9 +230,9 @@ void __cfg80211_scan_done(struct work_struct *wk) rdev = container_of(wk, struct cfg80211_registered_device, scan_done_wk); - mutex_lock(&rdev->sched_scan_mtx); + rtnl_lock(); ___cfg80211_scan_done(rdev, false); - mutex_unlock(&rdev->sched_scan_mtx); + rtnl_unlock(); } void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) @@ -241,6 +241,7 @@ void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req); request->aborted = aborted; + request->notified = true; queue_work(cfg80211_wq, &wiphy_to_dev(request->wiphy)->scan_done_wk); } EXPORT_SYMBOL(cfg80211_scan_done); @@ -255,7 +256,7 @@ void __cfg80211_sched_scan_results(struct work_struct *wk) request = rdev->sched_scan_req; - mutex_lock(&rdev->sched_scan_mtx); + rtnl_lock(); /* we don't have sched_scan_req anymore if the scan is stopping */ if (request) { @@ -270,7 +271,7 @@ void __cfg80211_sched_scan_results(struct work_struct *wk) nl80211_send_sched_scan_results(rdev, request->dev); } - mutex_unlock(&rdev->sched_scan_mtx); + rtnl_unlock(); } void cfg80211_sched_scan_results(struct wiphy *wiphy) @@ -289,9 +290,9 @@ void cfg80211_sched_scan_stopped(struct wiphy *wiphy) trace_cfg80211_sched_scan_stopped(wiphy); - mutex_lock(&rdev->sched_scan_mtx); + rtnl_lock(); __cfg80211_stop_sched_scan(rdev, true); - mutex_unlock(&rdev->sched_scan_mtx); + rtnl_unlock(); } EXPORT_SYMBOL(cfg80211_sched_scan_stopped); @@ -300,7 +301,7 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, { struct net_device *dev; - lockdep_assert_held(&rdev->sched_scan_mtx); + ASSERT_RTNL(); if (!rdev->sched_scan_req) return -ENOENT; @@ -1043,21 +1044,19 @@ EXPORT_SYMBOL(cfg80211_unlink_bss); static struct cfg80211_registered_device * cfg80211_get_dev_from_ifindex(struct net *net, int ifindex) { - struct cfg80211_registered_device *rdev = ERR_PTR(-ENODEV); + struct cfg80211_registered_device *rdev; struct net_device *dev; - mutex_lock(&cfg80211_mutex); + ASSERT_RTNL(); + dev = dev_get_by_index(net, ifindex); if (!dev) - goto out; - if (dev->ieee80211_ptr) { + return ERR_PTR(-ENODEV); + if (dev->ieee80211_ptr) rdev = wiphy_to_dev(dev->ieee80211_ptr->wiphy); - mutex_lock(&rdev->mtx); - } else + else rdev = ERR_PTR(-ENODEV); dev_put(dev); - out: - mutex_unlock(&cfg80211_mutex); return rdev; } @@ -1083,7 +1082,6 @@ int cfg80211_wext_siwscan(struct net_device *dev, if (IS_ERR(rdev)) return PTR_ERR(rdev); - mutex_lock(&rdev->sched_scan_mtx); if (rdev->scan_req) { err = -EBUSY; goto out; @@ -1190,9 +1188,7 @@ int cfg80211_wext_siwscan(struct net_device *dev, dev_hold(dev); } out: - mutex_unlock(&rdev->sched_scan_mtx); kfree(creq); - cfg80211_unlock_rdev(rdev); return err; } EXPORT_SYMBOL_GPL(cfg80211_wext_siwscan); @@ -1491,10 +1487,8 @@ int cfg80211_wext_giwscan(struct net_device *dev, if (IS_ERR(rdev)) return PTR_ERR(rdev); - if (rdev->scan_req) { - res = -EAGAIN; - goto out; - } + if (rdev->scan_req) + return -EAGAIN; res = ieee80211_scan_results(rdev, info, extra, data->length); data->length = 0; @@ -1503,8 +1497,6 @@ int cfg80211_wext_giwscan(struct net_device *dev, res = 0; } - out: - cfg80211_unlock_rdev(rdev); return res; } EXPORT_SYMBOL_GPL(cfg80211_wext_giwscan); diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 3ed35c345cae..4dbf31407a56 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -43,35 +43,29 @@ static bool cfg80211_is_all_idle(void) struct wireless_dev *wdev; bool is_all_idle = true; - mutex_lock(&cfg80211_mutex); - /* * All devices must be idle as otherwise if you are actively * scanning some new beacon hints could be learned and would * count as new regulatory hints. */ list_for_each_entry(rdev, &cfg80211_rdev_list, list) { - cfg80211_lock_rdev(rdev); list_for_each_entry(wdev, &rdev->wdev_list, list) { wdev_lock(wdev); if (wdev->sme_state != CFG80211_SME_IDLE) is_all_idle = false; wdev_unlock(wdev); } - cfg80211_unlock_rdev(rdev); } - mutex_unlock(&cfg80211_mutex); - return is_all_idle; } static void disconnect_work(struct work_struct *work) { - if (!cfg80211_is_all_idle()) - return; - - regulatory_hint_disconnect(); + rtnl_lock(); + if (cfg80211_is_all_idle()) + regulatory_hint_disconnect(); + rtnl_unlock(); } static DECLARE_WORK(cfg80211_disconnect_work, disconnect_work); @@ -85,7 +79,6 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev) ASSERT_RTNL(); ASSERT_RDEV_LOCK(rdev); ASSERT_WDEV_LOCK(wdev); - lockdep_assert_held(&rdev->sched_scan_mtx); if (rdev->scan_req) return -EBUSY; @@ -226,9 +219,6 @@ void cfg80211_conn_work(struct work_struct *work) u8 bssid_buf[ETH_ALEN], *bssid = NULL; rtnl_lock(); - cfg80211_lock_rdev(rdev); - mutex_lock(&rdev->devlist_mtx); - mutex_lock(&rdev->sched_scan_mtx); list_for_each_entry(wdev, &rdev->wdev_list, list) { if (!wdev->netdev) @@ -256,9 +246,6 @@ void cfg80211_conn_work(struct work_struct *work) wdev_unlock(wdev); } - mutex_unlock(&rdev->sched_scan_mtx); - mutex_unlock(&rdev->devlist_mtx); - cfg80211_unlock_rdev(rdev); rtnl_unlock(); } @@ -931,14 +918,9 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, { int err; - mutex_lock(&rdev->devlist_mtx); - /* might request scan - scan_mtx -> wdev_mtx dependency */ - mutex_lock(&rdev->sched_scan_mtx); wdev_lock(dev->ieee80211_ptr); err = __cfg80211_connect(rdev, dev, connect, connkeys, NULL); wdev_unlock(dev->ieee80211_ptr); - mutex_unlock(&rdev->sched_scan_mtx); - mutex_unlock(&rdev->devlist_mtx); return err; } diff --git a/net/wireless/util.c b/net/wireless/util.c index 0962f107f57f..501724257af5 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -808,12 +808,8 @@ void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev) ASSERT_RTNL(); ASSERT_RDEV_LOCK(rdev); - mutex_lock(&rdev->devlist_mtx); - list_for_each_entry(wdev, &rdev->wdev_list, list) cfg80211_process_wdev_events(wdev); - - mutex_unlock(&rdev->devlist_mtx); } int cfg80211_change_iface(struct cfg80211_registered_device *rdev, @@ -845,10 +841,8 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, return -EBUSY; if (ntype != otype && netif_running(dev)) { - mutex_lock(&rdev->devlist_mtx); err = cfg80211_can_change_interface(rdev, dev->ieee80211_ptr, ntype); - mutex_unlock(&rdev->devlist_mtx); if (err) return err; @@ -1210,8 +1204,6 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, if (!beacon_int) return -EINVAL; - mutex_lock(&rdev->devlist_mtx); - list_for_each_entry(wdev, &rdev->wdev_list, list) { if (!wdev->beacon_interval) continue; @@ -1221,8 +1213,6 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, } } - mutex_unlock(&rdev->devlist_mtx); - return res; } @@ -1246,7 +1236,6 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, int i, j; ASSERT_RTNL(); - lockdep_assert_held(&rdev->devlist_mtx); if (WARN_ON(hweight32(radar_detect) > 1)) return -EINVAL; diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index d997d0f0c54a..e7c6e862580d 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -72,7 +72,6 @@ int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info, struct cfg80211_registered_device *rdev; struct vif_params vifparams; enum nl80211_iftype type; - int ret; rdev = wiphy_to_dev(wdev->wiphy); @@ -98,11 +97,7 @@ int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info, memset(&vifparams, 0, sizeof(vifparams)); - cfg80211_lock_rdev(rdev); - ret = cfg80211_change_iface(rdev, dev, type, NULL, &vifparams); - cfg80211_unlock_rdev(rdev); - - return ret; + return cfg80211_change_iface(rdev, dev, type, NULL, &vifparams); } EXPORT_SYMBOL_GPL(cfg80211_wext_siwmode); @@ -579,13 +574,10 @@ static int cfg80211_set_encryption(struct cfg80211_registered_device *rdev, { int err; - /* devlist mutex needed for possible IBSS re-join */ - mutex_lock(&rdev->devlist_mtx); wdev_lock(dev->ieee80211_ptr); err = __cfg80211_set_encryption(rdev, dev, pairwise, addr, remove, tx_key, idx, params); wdev_unlock(dev->ieee80211_ptr); - mutex_unlock(&rdev->devlist_mtx); return err; } @@ -787,7 +779,7 @@ static int cfg80211_wext_siwfreq(struct net_device *dev, struct cfg80211_chan_def chandef = { .width = NL80211_CHAN_WIDTH_20_NOHT, }; - int freq, err; + int freq; switch (wdev->iftype) { case NL80211_IFTYPE_STATION: @@ -804,10 +796,7 @@ static int cfg80211_wext_siwfreq(struct net_device *dev, chandef.chan = ieee80211_get_channel(&rdev->wiphy, freq); if (!chandef.chan) return -EINVAL; - mutex_lock(&rdev->devlist_mtx); - err = cfg80211_set_monitor_channel(rdev, &chandef); - mutex_unlock(&rdev->devlist_mtx); - return err; + return cfg80211_set_monitor_channel(rdev, &chandef); case NL80211_IFTYPE_MESH_POINT: freq = cfg80211_wext_freq(wdev->wiphy, wextfreq); if (freq < 0) @@ -818,10 +807,7 @@ static int cfg80211_wext_siwfreq(struct net_device *dev, chandef.chan = ieee80211_get_channel(&rdev->wiphy, freq); if (!chandef.chan) return -EINVAL; - mutex_lock(&rdev->devlist_mtx); - err = cfg80211_set_mesh_channel(rdev, wdev, &chandef); - mutex_unlock(&rdev->devlist_mtx); - return err; + return cfg80211_set_mesh_channel(rdev, wdev, &chandef); default: return -EOPNOTSUPP; } diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index e79cb5c0655a..aeefd6817189 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c @@ -87,9 +87,6 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev, return -EINVAL; } - cfg80211_lock_rdev(rdev); - mutex_lock(&rdev->devlist_mtx); - mutex_lock(&rdev->sched_scan_mtx); wdev_lock(wdev); if (wdev->sme_state != CFG80211_SME_IDLE) { @@ -136,9 +133,6 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev, err = cfg80211_mgd_wext_connect(rdev, wdev); out: wdev_unlock(wdev); - mutex_unlock(&rdev->sched_scan_mtx); - mutex_unlock(&rdev->devlist_mtx); - cfg80211_unlock_rdev(rdev); return err; } @@ -190,9 +184,6 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev, if (len > 0 && ssid[len - 1] == '\0') len--; - cfg80211_lock_rdev(rdev); - mutex_lock(&rdev->devlist_mtx); - mutex_lock(&rdev->sched_scan_mtx); wdev_lock(wdev); err = 0; @@ -226,9 +217,6 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev, err = cfg80211_mgd_wext_connect(rdev, wdev); out: wdev_unlock(wdev); - mutex_unlock(&rdev->sched_scan_mtx); - mutex_unlock(&rdev->devlist_mtx); - cfg80211_unlock_rdev(rdev); return err; } @@ -287,9 +275,6 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev, if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid)) bssid = NULL; - cfg80211_lock_rdev(rdev); - mutex_lock(&rdev->devlist_mtx); - mutex_lock(&rdev->sched_scan_mtx); wdev_lock(wdev); if (wdev->sme_state != CFG80211_SME_IDLE) { @@ -318,9 +303,6 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev, err = cfg80211_mgd_wext_connect(rdev, wdev); out: wdev_unlock(wdev); - mutex_unlock(&rdev->sched_scan_mtx); - mutex_unlock(&rdev->devlist_mtx); - cfg80211_unlock_rdev(rdev); return err; } -- cgit v1.2.3 From 8d61ffa5e01c5f676431d12caba17db164a48a86 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 10 May 2013 12:32:47 +0200 Subject: cfg80211/mac80211: use cfg80211 wdev mutex in mac80211 Using separate locks in cfg80211 and mac80211 has always caused issues, for example having to unlock in places in mac80211 to call cfg80211, which even needed a framework to make cfg80211 calls after some functions returned etc. Additionally, I suspect some issues people have reported with the cfg80211 state getting confused could be due to such issues, when cfg80211 is asking mac80211 to change state but mac80211 is in the process of telling cfg80211 that the state changed (in another way.) Signed-off-by: Johannes Berg --- Documentation/DocBook/80211.tmpl | 2 - include/net/cfg80211.h | 51 +++--- net/mac80211/cfg.c | 4 +- net/mac80211/debugfs_netdev.c | 10 +- net/mac80211/ht.c | 4 +- net/mac80211/ibss.c | 39 ++--- net/mac80211/ieee80211_i.h | 25 ++- net/mac80211/main.c | 4 +- net/mac80211/mesh.c | 32 ++-- net/mac80211/mesh_plink.c | 7 +- net/mac80211/mlme.c | 341 ++++++++++++++------------------------- net/mac80211/util.c | 4 +- net/wireless/mlme.c | 48 +----- net/wireless/trace.h | 4 +- 14 files changed, 220 insertions(+), 355 deletions(-) diff --git a/Documentation/DocBook/80211.tmpl b/Documentation/DocBook/80211.tmpl index 0f6a3edcd44b..ebe89694cf81 100644 --- a/Documentation/DocBook/80211.tmpl +++ b/Documentation/DocBook/80211.tmpl @@ -132,9 +132,7 @@ !Finclude/net/cfg80211.h cfg80211_send_rx_assoc !Finclude/net/cfg80211.h cfg80211_send_assoc_timeout !Finclude/net/cfg80211.h cfg80211_send_deauth -!Finclude/net/cfg80211.h __cfg80211_send_deauth !Finclude/net/cfg80211.h cfg80211_send_disassoc -!Finclude/net/cfg80211.h __cfg80211_send_disassoc !Finclude/net/cfg80211.h cfg80211_ibss_joined !Finclude/net/cfg80211.h cfg80211_connect_result !Finclude/net/cfg80211.h cfg80211_roamed diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 5430f70c63b3..9f45d74ce3c2 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1867,7 +1867,9 @@ struct cfg80211_update_ft_ies_params { * @get_mpath: get a mesh path for the given parameters * @dump_mpath: dump mesh path callback -- resume dump at index @idx * @join_mesh: join the mesh network with the specified parameters + * (invoked with the wireless_dev mutex held) * @leave_mesh: leave the current mesh network + * (invoked with the wireless_dev mutex held) * * @get_mesh_config: Get the current mesh configuration * @@ -1894,20 +1896,28 @@ struct cfg80211_update_ft_ies_params { * the scan/scan_done bracket too. * * @auth: Request to authenticate with the specified peer + * (invoked with the wireless_dev mutex held) * @assoc: Request to (re)associate with the specified peer + * (invoked with the wireless_dev mutex held) * @deauth: Request to deauthenticate from the specified peer + * (invoked with the wireless_dev mutex held) * @disassoc: Request to disassociate from the specified peer + * (invoked with the wireless_dev mutex held) * * @connect: Connect to the ESS with the specified parameters. When connected, * call cfg80211_connect_result() with status code %WLAN_STATUS_SUCCESS. * If the connection fails for some reason, call cfg80211_connect_result() * with the status from the AP. + * (invoked with the wireless_dev mutex held) * @disconnect: Disconnect from the BSS/ESS. + * (invoked with the wireless_dev mutex held) * * @join_ibss: Join the specified IBSS (or create if necessary). Once done, call * cfg80211_ibss_joined(), also call that function when changing BSSID due * to a merge. + * (invoked with the wireless_dev mutex held) * @leave_ibss: Leave the IBSS. + * (invoked with the wireless_dev mutex held) * * @set_mcast_rate: Set the specified multicast rate (only if vif is in ADHOC or * MESH mode) @@ -2851,7 +2861,8 @@ struct cfg80211_cached_keys; * by cfg80211 on change_interface * @mgmt_registrations: list of registrations for management frames * @mgmt_registrations_lock: lock for the list - * @mtx: mutex used to lock data in this struct + * @mtx: mutex used to lock data in this struct, may be used by drivers + * and some API functions require it held * @cleanup_work: work struct used for cleanup that can't be done directly * @beacon_interval: beacon interval used on this device for transmitting * beacons, 0 when not valid @@ -3424,7 +3435,8 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *bss); * This function is called whenever an authentication has been processed in * station mode. The driver is required to call either this function or * cfg80211_send_auth_timeout() to indicate the result of cfg80211_ops::auth() - * call. This function may sleep. + * call. This function may sleep. The caller must hold the corresponding wdev's + * mutex. */ void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len); @@ -3433,7 +3445,8 @@ void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len); * @dev: network device * @addr: The MAC address of the device with which the authentication timed out * - * This function may sleep. + * This function may sleep. The caller must hold the corresponding wdev's + * mutex. */ void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr); @@ -3448,7 +3461,8 @@ void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr); * This function is called whenever a (re)association response has been * processed in station mode. The driver is required to call either this * function or cfg80211_send_assoc_timeout() to indicate the result of - * cfg80211_ops::assoc() call. This function may sleep. + * cfg80211_ops::assoc() call. This function may sleep. The caller must hold + * the corresponding wdev's mutex. */ void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss, const u8 *buf, size_t len); @@ -3458,7 +3472,7 @@ void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss, * @dev: network device * @addr: The MAC address of the device with which the association timed out * - * This function may sleep. + * This function may sleep. The caller must hold the corresponding wdev's mutex. */ void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr); @@ -3470,20 +3484,11 @@ void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr); * * This function is called whenever deauthentication has been processed in * station mode. This includes both received deauthentication frames and - * locally generated ones. This function may sleep. + * locally generated ones. This function may sleep. The caller must hold the + * corresponding wdev's mutex. */ void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len); -/** - * __cfg80211_send_deauth - notification of processed deauthentication - * @dev: network device - * @buf: deauthentication frame (header + body) - * @len: length of the frame data - * - * Like cfg80211_send_deauth(), but doesn't take the wdev lock. - */ -void __cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len); - /** * cfg80211_send_disassoc - notification of processed disassociation * @dev: network device @@ -3492,21 +3497,11 @@ void __cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len); * * This function is called whenever disassociation has been processed in * station mode. This includes both received disassociation frames and locally - * generated ones. This function may sleep. + * generated ones. This function may sleep. The caller must hold the + * corresponding wdev's mutex. */ void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len); -/** - * __cfg80211_send_disassoc - notification of processed disassociation - * @dev: network device - * @buf: disassociation response frame (header + body) - * @len: length of the frame data - * - * Like cfg80211_send_disassoc(), but doesn't take the wdev lock. - */ -void __cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, - size_t len); - /** * cfg80211_send_unprot_deauth - notification of unprotected deauthentication * @dev: network device diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index eb4219051043..232edf78d5a9 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2318,7 +2318,7 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, enum ieee80211_smps_mode old_req; int err; - lockdep_assert_held(&sdata->u.mgd.mtx); + lockdep_assert_held(&sdata->wdev.mtx); old_req = sdata->u.mgd.req_smps; sdata->u.mgd.req_smps = smps_mode; @@ -2375,9 +2375,7 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, local->dynamic_ps_forced_timeout = timeout; /* no change, but if automatic follow powersave */ - mutex_lock(&sdata->u.mgd.mtx); __ieee80211_request_smps(sdata, sdata->u.mgd.req_smps); - mutex_unlock(&sdata->u.mgd.mtx); if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS) ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index f83074fe6670..cafe614ef93d 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -228,9 +228,9 @@ static int ieee80211_set_smps(struct ieee80211_sub_if_data *sdata, if (sdata->vif.type != NL80211_IFTYPE_STATION) return -EOPNOTSUPP; - mutex_lock(&sdata->u.mgd.mtx); + sdata_lock(sdata); err = __ieee80211_request_smps(sdata, smps_mode); - mutex_unlock(&sdata->u.mgd.mtx); + sdata_unlock(sdata); return err; } @@ -313,16 +313,16 @@ static ssize_t ieee80211_if_parse_tkip_mic_test( case NL80211_IFTYPE_STATION: fc |= cpu_to_le16(IEEE80211_FCTL_TODS); /* BSSID SA DA */ - mutex_lock(&sdata->u.mgd.mtx); + sdata_lock(sdata); if (!sdata->u.mgd.associated) { - mutex_unlock(&sdata->u.mgd.mtx); + sdata_unlock(sdata); dev_kfree_skb(skb); return -ENOTCONN; } memcpy(hdr->addr1, sdata->u.mgd.associated->bssid, ETH_ALEN); memcpy(hdr->addr2, sdata->vif.addr, ETH_ALEN); memcpy(hdr->addr3, addr, ETH_ALEN); - mutex_unlock(&sdata->u.mgd.mtx); + sdata_unlock(sdata); break; default: dev_kfree_skb(skb); diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index af8cee06e4f3..75dff338f581 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -429,9 +429,9 @@ void ieee80211_request_smps_work(struct work_struct *work) container_of(work, struct ieee80211_sub_if_data, u.mgd.request_smps_work); - mutex_lock(&sdata->u.mgd.mtx); + sdata_lock(sdata); __ieee80211_request_smps(sdata, sdata->u.mgd.driver_smps_mode); - mutex_unlock(&sdata->u.mgd.mtx); + sdata_unlock(sdata); } void ieee80211_request_smps(struct ieee80211_vif *vif, diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 956ba6316da5..caa4b4f7f6e4 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -54,7 +54,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, struct beacon_data *presp; int frame_len; - lockdep_assert_held(&ifibss->mtx); + sdata_assert_lock(sdata); /* Reset own TSF to allow time synchronization work. */ drv_reset_tsf(local, sdata); @@ -74,7 +74,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, } presp = rcu_dereference_protected(ifibss->presp, - lockdep_is_held(&ifibss->mtx)); + lockdep_is_held(&sdata->wdev.mtx)); rcu_assign_pointer(ifibss->presp, NULL); if (presp) kfree_rcu(presp, rcu_head); @@ -263,7 +263,7 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, const struct cfg80211_bss_ies *ies; u64 tsf; - lockdep_assert_held(&sdata->u.ibss.mtx); + sdata_assert_lock(sdata); if (beacon_int < 10) beacon_int = 10; @@ -410,7 +410,7 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata, struct sta_info *sta; u8 deauth_frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; - lockdep_assert_held(&sdata->u.ibss.mtx); + sdata_assert_lock(sdata); if (len < 24 + 6) return; @@ -677,7 +677,7 @@ static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata) int active = 0; struct sta_info *sta; - lockdep_assert_held(&sdata->u.ibss.mtx); + sdata_assert_lock(sdata); rcu_read_lock(); @@ -703,7 +703,7 @@ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; - lockdep_assert_held(&ifibss->mtx); + sdata_assert_lock(sdata); mod_timer(&ifibss->timer, round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL)); @@ -734,7 +734,7 @@ static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata) u16 capability; int i; - lockdep_assert_held(&ifibss->mtx); + sdata_assert_lock(sdata); if (ifibss->fixed_bssid) { memcpy(bssid, ifibss->bssid, ETH_ALEN); @@ -777,7 +777,7 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata) int active_ibss; u16 capability; - lockdep_assert_held(&ifibss->mtx); + sdata_assert_lock(sdata); active_ibss = ieee80211_sta_active_ibss(sdata); ibss_dbg(sdata, "sta_find_ibss (active_ibss=%d)\n", active_ibss); @@ -847,10 +847,10 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata, struct beacon_data *presp; u8 *pos, *end; - lockdep_assert_held(&ifibss->mtx); + sdata_assert_lock(sdata); presp = rcu_dereference_protected(ifibss->presp, - lockdep_is_held(&ifibss->mtx)); + lockdep_is_held(&sdata->wdev.mtx)); if (ifibss->state != IEEE80211_IBSS_MLME_JOINED || len < 24 + 2 || !presp) @@ -934,7 +934,7 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, mgmt = (struct ieee80211_mgmt *) skb->data; fc = le16_to_cpu(mgmt->frame_control); - mutex_lock(&sdata->u.ibss.mtx); + sdata_lock(sdata); if (!sdata->u.ibss.ssid_len) goto mgmt_out; /* not ready to merge yet */ @@ -957,7 +957,7 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, } mgmt_out: - mutex_unlock(&sdata->u.ibss.mtx); + sdata_unlock(sdata); } void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata) @@ -965,7 +965,7 @@ void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata) struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; struct sta_info *sta; - mutex_lock(&ifibss->mtx); + sdata_lock(sdata); /* * Work could be scheduled after scan or similar @@ -1001,7 +1001,7 @@ void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata) } out: - mutex_unlock(&ifibss->mtx); + sdata_unlock(sdata); } static void ieee80211_ibss_timer(unsigned long data) @@ -1018,7 +1018,6 @@ void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata) setup_timer(&ifibss->timer, ieee80211_ibss_timer, (unsigned long) sdata); - mutex_init(&ifibss->mtx); INIT_LIST_HEAD(&ifibss->incomplete_stations); spin_lock_init(&ifibss->incomplete_lock); } @@ -1045,8 +1044,6 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, { u32 changed = 0; - mutex_lock(&sdata->u.ibss.mtx); - if (params->bssid) { memcpy(sdata->u.ibss.bssid, params->bssid, ETH_ALEN); sdata->u.ibss.fixed_bssid = true; @@ -1079,8 +1076,6 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, memcpy(sdata->u.ibss.ssid, params->ssid, params->ssid_len); sdata->u.ibss.ssid_len = params->ssid_len; - mutex_unlock(&sdata->u.ibss.mtx); - /* * 802.11n-2009 9.13.3.1: In an IBSS, the HT Protection field is * reserved, but an HT STA shall protect HT transmissions as though @@ -1116,8 +1111,6 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata) struct sta_info *sta; struct beacon_data *presp; - mutex_lock(&sdata->u.ibss.mtx); - active_ibss = ieee80211_sta_active_ibss(sdata); if (!active_ibss && !is_zero_ether_addr(ifibss->bssid)) { @@ -1161,7 +1154,7 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata) /* remove beacon */ kfree(sdata->u.ibss.ie); presp = rcu_dereference_protected(ifibss->presp, - lockdep_is_held(&sdata->u.ibss.mtx)); + lockdep_is_held(&sdata->wdev.mtx)); RCU_INIT_POINTER(sdata->u.ibss.presp, NULL); sdata->vif.bss_conf.ibss_joined = false; sdata->vif.bss_conf.ibss_creator = false; @@ -1177,7 +1170,5 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata) del_timer_sync(&sdata->u.ibss.timer); - mutex_unlock(&sdata->u.ibss.mtx); - return 0; } diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index ba3cd284d104..9eed6f1d1614 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -394,7 +394,6 @@ struct ieee80211_if_managed { bool nullfunc_failed; bool connection_loss; - struct mutex mtx; struct cfg80211_bss *associated; struct ieee80211_mgd_auth_data *auth_data; struct ieee80211_mgd_assoc_data *assoc_data; @@ -488,8 +487,6 @@ struct ieee80211_if_managed { struct ieee80211_if_ibss { struct timer_list timer; - struct mutex mtx; - unsigned long last_scan_completed; u32 basic_rates; @@ -580,8 +577,6 @@ struct ieee80211_if_mesh { bool accepting_plinks; int num_gates; struct beacon_data __rcu *beacon; - /* just protects beacon updates for now */ - struct mutex mtx; const u8 *ie; u8 ie_len; enum { @@ -778,6 +773,26 @@ struct ieee80211_sub_if_data *vif_to_sdata(struct ieee80211_vif *p) return container_of(p, struct ieee80211_sub_if_data, vif); } +static inline void sdata_lock(struct ieee80211_sub_if_data *sdata) + __acquires(&sdata->wdev.mtx) +{ + mutex_lock(&sdata->wdev.mtx); + __acquire(&sdata->wdev.mtx); +} + +static inline void sdata_unlock(struct ieee80211_sub_if_data *sdata) + __releases(&sdata->wdev.mtx) +{ + mutex_unlock(&sdata->wdev.mtx); + __release(&sdata->wdev.mtx); +} + +static inline void +sdata_assert_lock(struct ieee80211_sub_if_data *sdata) +{ + lockdep_assert_held(&sdata->wdev.mtx); +} + static inline enum ieee80211_band ieee80211_get_sdata_band(struct ieee80211_sub_if_data *sdata) { diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 8a7bfc47d577..1998f1475267 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -331,7 +331,7 @@ static int ieee80211_ifa_changed(struct notifier_block *nb, return NOTIFY_DONE; ifmgd = &sdata->u.mgd; - mutex_lock(&ifmgd->mtx); + sdata_lock(sdata); /* Copy the addresses to the bss_conf list */ ifa = idev->ifa_list; @@ -349,7 +349,7 @@ static int ieee80211_ifa_changed(struct notifier_block *nb, ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_ARP_FILTER); - mutex_unlock(&ifmgd->mtx); + sdata_unlock(sdata); return NOTIFY_DONE; } diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index c14bb816c6a3..b3d1fdd46368 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -161,8 +161,11 @@ void mesh_sta_cleanup(struct sta_info *sta) del_timer_sync(&sta->plink_timer); } - if (changed) + if (changed) { + sdata_lock(sdata); ieee80211_mbss_info_change_notify(sdata, changed); + sdata_unlock(sdata); + } } int mesh_rmc_init(struct ieee80211_sub_if_data *sdata) @@ -577,7 +580,9 @@ static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata) mesh_path_expire(sdata); changed = mesh_accept_plinks_update(sdata); + sdata_lock(sdata); ieee80211_mbss_info_change_notify(sdata, changed); + sdata_unlock(sdata); mod_timer(&ifmsh->housekeeping_timer, round_jiffies(jiffies + @@ -697,25 +702,21 @@ out_free: } static int -ieee80211_mesh_rebuild_beacon(struct ieee80211_if_mesh *ifmsh) +ieee80211_mesh_rebuild_beacon(struct ieee80211_sub_if_data *sdata) { struct beacon_data *old_bcn; int ret; - mutex_lock(&ifmsh->mtx); - - old_bcn = rcu_dereference_protected(ifmsh->beacon, - lockdep_is_held(&ifmsh->mtx)); - ret = ieee80211_mesh_build_beacon(ifmsh); + old_bcn = rcu_dereference_protected(sdata->u.mesh.beacon, + lockdep_is_held(&sdata->wdev.mtx)); + ret = ieee80211_mesh_build_beacon(&sdata->u.mesh); if (ret) /* just reuse old beacon */ - goto out; + return ret; if (old_bcn) kfree_rcu(old_bcn, rcu_head); -out: - mutex_unlock(&ifmsh->mtx); - return ret; + return 0; } void ieee80211_mbss_info_change_notify(struct ieee80211_sub_if_data *sdata, @@ -726,7 +727,7 @@ void ieee80211_mbss_info_change_notify(struct ieee80211_sub_if_data *sdata, BSS_CHANGED_HT | BSS_CHANGED_BASIC_RATES | BSS_CHANGED_BEACON_INT))) - if (ieee80211_mesh_rebuild_beacon(&sdata->u.mesh)) + if (ieee80211_mesh_rebuild_beacon(sdata)) return; ieee80211_bss_info_change_notify(sdata, changed); } @@ -788,12 +789,12 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata) sdata->vif.bss_conf.enable_beacon = false; clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); - mutex_lock(&ifmsh->mtx); + sdata_lock(sdata); bcn = rcu_dereference_protected(ifmsh->beacon, - lockdep_is_held(&ifmsh->mtx)); + lockdep_is_held(&sdata->wdev.mtx)); rcu_assign_pointer(ifmsh->beacon, NULL); kfree_rcu(bcn, rcu_head); - mutex_unlock(&ifmsh->mtx); + sdata_unlock(sdata); /* flush STAs and mpaths on this iface */ sta_info_flush(sdata); @@ -1041,7 +1042,6 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata) spin_lock_init(&ifmsh->mesh_preq_queue_lock); spin_lock_init(&ifmsh->sync_offset_lock); RCU_INIT_POINTER(ifmsh->beacon, NULL); - mutex_init(&ifmsh->mtx); sdata->vif.bss_conf.bssid = zero_addr; } diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 09bebed99416..6c4da99bc4fb 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -517,7 +517,9 @@ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata, ieee80211_mps_frame_release(sta, elems); out: rcu_read_unlock(); + sdata_lock(sdata); ieee80211_mbss_info_change_notify(sdata, changed); + sdata_unlock(sdata); } static void mesh_plink_timer(unsigned long data) @@ -1068,6 +1070,9 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, rcu_read_unlock(); - if (changed) + if (changed) { + sdata_lock(sdata); ieee80211_mbss_info_change_notify(sdata, changed); + sdata_unlock(sdata); + } } diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 1da3d6be8e11..f44f4caa69ee 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -90,41 +90,6 @@ MODULE_PARM_DESC(probe_wait_ms, */ #define IEEE80211_SIGNAL_AVE_MIN_COUNT 4 -/* - * All cfg80211 functions have to be called outside a locked - * section so that they can acquire a lock themselves... This - * is much simpler than queuing up things in cfg80211, but we - * do need some indirection for that here. - */ -enum rx_mgmt_action { - /* no action required */ - RX_MGMT_NONE, - - /* caller must call cfg80211_send_deauth() */ - RX_MGMT_CFG80211_DEAUTH, - - /* caller must call cfg80211_send_disassoc() */ - RX_MGMT_CFG80211_DISASSOC, - - /* caller must call cfg80211_send_rx_auth() */ - RX_MGMT_CFG80211_RX_AUTH, - - /* caller must call cfg80211_send_rx_assoc() */ - RX_MGMT_CFG80211_RX_ASSOC, - - /* caller must call cfg80211_send_assoc_timeout() */ - RX_MGMT_CFG80211_ASSOC_TIMEOUT, - - /* used when a processed beacon causes a deauth */ - RX_MGMT_CFG80211_TX_DEAUTH, -}; - -/* utils */ -static inline void ASSERT_MGD_MTX(struct ieee80211_if_managed *ifmgd) -{ - lockdep_assert_held(&ifmgd->mtx); -} - /* * We can have multiple work items (and connection probing) * scheduling this timer, but we need to take care to only @@ -135,13 +100,14 @@ static inline void ASSERT_MGD_MTX(struct ieee80211_if_managed *ifmgd) * has happened -- the work that runs from this timer will * do that. */ -static void run_again(struct ieee80211_if_managed *ifmgd, unsigned long timeout) +static void run_again(struct ieee80211_sub_if_data *sdata, + unsigned long timeout) { - ASSERT_MGD_MTX(ifmgd); + sdata_assert_lock(sdata); - if (!timer_pending(&ifmgd->timer) || - time_before(timeout, ifmgd->timer.expires)) - mod_timer(&ifmgd->timer, timeout); + if (!timer_pending(&sdata->u.mgd.timer) || + time_before(timeout, sdata->u.mgd.timer.expires)) + mod_timer(&sdata->u.mgd.timer, timeout); } void ieee80211_sta_reset_beacon_monitor(struct ieee80211_sub_if_data *sdata) @@ -652,7 +618,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) struct ieee80211_channel *chan; u32 rates = 0; - lockdep_assert_held(&ifmgd->mtx); + sdata_assert_lock(sdata); rcu_read_lock(); chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); @@ -962,7 +928,7 @@ static void ieee80211_chswitch_work(struct work_struct *work) if (!ieee80211_sdata_running(sdata)) return; - mutex_lock(&ifmgd->mtx); + sdata_lock(sdata); if (!ifmgd->associated) goto out; @@ -985,7 +951,7 @@ static void ieee80211_chswitch_work(struct work_struct *work) IEEE80211_QUEUE_STOP_REASON_CSA); out: ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED; - mutex_unlock(&ifmgd->mtx); + sdata_unlock(sdata); } void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success) @@ -1036,7 +1002,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, const struct ieee80211_ht_operation *ht_oper; int secondary_channel_offset = -1; - ASSERT_MGD_MTX(ifmgd); + sdata_assert_lock(sdata); if (!cbss) return; @@ -1845,7 +1811,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, struct ieee80211_local *local = sdata->local; u32 changed = 0; - ASSERT_MGD_MTX(ifmgd); + sdata_assert_lock(sdata); if (WARN_ON_ONCE(tx && !frame_buf)) return; @@ -2054,7 +2020,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata) } ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms); - run_again(ifmgd, ifmgd->probe_timeout); + run_again(sdata, ifmgd->probe_timeout); if (sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) ieee80211_flush_queues(sdata->local, sdata); } @@ -2068,7 +2034,7 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata, if (!ieee80211_sdata_running(sdata)) return; - mutex_lock(&ifmgd->mtx); + sdata_lock(sdata); if (!ifmgd->associated) goto out; @@ -2122,7 +2088,7 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata, ifmgd->probe_send_count = 0; ieee80211_mgd_probe_ap_send(sdata); out: - mutex_unlock(&ifmgd->mtx); + sdata_unlock(sdata); } struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw, @@ -2138,7 +2104,7 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw, if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION)) return NULL; - ASSERT_MGD_MTX(ifmgd); + sdata_assert_lock(sdata); if (ifmgd->associated) cbss = ifmgd->associated; @@ -2171,9 +2137,9 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; - mutex_lock(&ifmgd->mtx); + sdata_lock(sdata); if (!ifmgd->associated) { - mutex_unlock(&ifmgd->mtx); + sdata_unlock(sdata); return; } @@ -2184,13 +2150,9 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) ieee80211_wake_queues_by_reason(&sdata->local->hw, IEEE80211_MAX_QUEUE_MAP, IEEE80211_QUEUE_STOP_REASON_CSA); - mutex_unlock(&ifmgd->mtx); - /* - * must be outside lock due to cfg80211, - * but that's not a problem. - */ cfg80211_send_deauth(sdata->dev, frame_buf, IEEE80211_DEAUTH_FRAME_LEN); + sdata_unlock(sdata); } static void ieee80211_beacon_connection_loss_work(struct work_struct *work) @@ -2257,7 +2219,7 @@ static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata, { struct ieee80211_mgd_auth_data *auth_data = sdata->u.mgd.auth_data; - lockdep_assert_held(&sdata->u.mgd.mtx); + sdata_assert_lock(sdata); if (!assoc) { sta_info_destroy_addr(sdata, auth_data->bss->bssid); @@ -2298,27 +2260,26 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata, auth_data->key_idx, tx_flags); } -static enum rx_mgmt_action __must_check -ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *mgmt, size_t len) +static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, size_t len) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; u8 bssid[ETH_ALEN]; u16 auth_alg, auth_transaction, status_code; struct sta_info *sta; - lockdep_assert_held(&ifmgd->mtx); + sdata_assert_lock(sdata); if (len < 24 + 6) - return RX_MGMT_NONE; + return; if (!ifmgd->auth_data || ifmgd->auth_data->done) - return RX_MGMT_NONE; + return; memcpy(bssid, ifmgd->auth_data->bss->bssid, ETH_ALEN); if (!ether_addr_equal(bssid, mgmt->bssid)) - return RX_MGMT_NONE; + return; auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg); auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction); @@ -2330,14 +2291,15 @@ ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, mgmt->sa, auth_alg, ifmgd->auth_data->algorithm, auth_transaction, ifmgd->auth_data->expected_transaction); - return RX_MGMT_NONE; + return; } if (status_code != WLAN_STATUS_SUCCESS) { sdata_info(sdata, "%pM denied authentication (status %d)\n", mgmt->sa, status_code); ieee80211_destroy_auth_data(sdata, false); - return RX_MGMT_CFG80211_RX_AUTH; + cfg80211_send_rx_auth(sdata->dev, (u8 *)mgmt, len); + return; } switch (ifmgd->auth_data->algorithm) { @@ -2350,20 +2312,20 @@ ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, if (ifmgd->auth_data->expected_transaction != 4) { ieee80211_auth_challenge(sdata, mgmt, len); /* need another frame */ - return RX_MGMT_NONE; + return; } break; default: WARN_ONCE(1, "invalid auth alg %d", ifmgd->auth_data->algorithm); - return RX_MGMT_NONE; + return; } sdata_info(sdata, "authenticated\n"); ifmgd->auth_data->done = true; ifmgd->auth_data->timeout = jiffies + IEEE80211_AUTH_WAIT_ASSOC; ifmgd->auth_data->timeout_started = true; - run_again(ifmgd, ifmgd->auth_data->timeout); + run_again(sdata, ifmgd->auth_data->timeout); if (ifmgd->auth_data->algorithm == WLAN_AUTH_SAE && ifmgd->auth_data->expected_transaction != 2) { @@ -2371,7 +2333,8 @@ ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, * Report auth frame to user space for processing since another * round of Authentication frames is still needed. */ - return RX_MGMT_CFG80211_RX_AUTH; + cfg80211_send_rx_auth(sdata->dev, (u8 *)mgmt, len); + return; } /* move station state to auth */ @@ -2387,30 +2350,29 @@ ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, } mutex_unlock(&sdata->local->sta_mtx); - return RX_MGMT_CFG80211_RX_AUTH; + cfg80211_send_rx_auth(sdata->dev, (u8 *)mgmt, len); + return; out_err: mutex_unlock(&sdata->local->sta_mtx); /* ignore frame -- wait for timeout */ - return RX_MGMT_NONE; } -static enum rx_mgmt_action __must_check -ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *mgmt, size_t len) +static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, size_t len) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; const u8 *bssid = NULL; u16 reason_code; - lockdep_assert_held(&ifmgd->mtx); + sdata_assert_lock(sdata); if (len < 24 + 2) - return RX_MGMT_NONE; + return; if (!ifmgd->associated || !ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid)) - return RX_MGMT_NONE; + return; bssid = ifmgd->associated->bssid; @@ -2421,25 +2383,24 @@ ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, ieee80211_set_disassoc(sdata, 0, 0, false, NULL); - return RX_MGMT_CFG80211_DEAUTH; + cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, len); } -static enum rx_mgmt_action __must_check -ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *mgmt, size_t len) +static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, size_t len) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; u16 reason_code; - lockdep_assert_held(&ifmgd->mtx); + sdata_assert_lock(sdata); if (len < 24 + 2) - return RX_MGMT_NONE; + return; if (!ifmgd->associated || !ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid)) - return RX_MGMT_NONE; + return; reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code); @@ -2448,7 +2409,7 @@ ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, ieee80211_set_disassoc(sdata, 0, 0, false, NULL); - return RX_MGMT_CFG80211_DISASSOC; + cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, len); } static void ieee80211_get_rates(struct ieee80211_supported_band *sband, @@ -2498,7 +2459,7 @@ static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata, { struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data; - lockdep_assert_held(&sdata->u.mgd.mtx); + sdata_assert_lock(sdata); if (!assoc) { sta_info_destroy_addr(sdata, assoc_data->bss->bssid); @@ -2679,10 +2640,9 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, return true; } -static enum rx_mgmt_action __must_check -ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *mgmt, size_t len, - struct cfg80211_bss **bss) +static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, + size_t len) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data; @@ -2690,13 +2650,14 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, struct ieee802_11_elems elems; u8 *pos; bool reassoc; + struct cfg80211_bss *bss; - lockdep_assert_held(&ifmgd->mtx); + sdata_assert_lock(sdata); if (!assoc_data) - return RX_MGMT_NONE; + return; if (!ether_addr_equal(assoc_data->bss->bssid, mgmt->bssid)) - return RX_MGMT_NONE; + return; /* * AssocResp and ReassocResp have identical structure, so process both @@ -2704,7 +2665,7 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, */ if (len < 24 + 6) - return RX_MGMT_NONE; + return; reassoc = ieee80211_is_reassoc_req(mgmt->frame_control); capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info); @@ -2731,22 +2692,23 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, assoc_data->timeout = jiffies + msecs_to_jiffies(ms); assoc_data->timeout_started = true; if (ms > IEEE80211_ASSOC_TIMEOUT) - run_again(ifmgd, assoc_data->timeout); - return RX_MGMT_NONE; + run_again(sdata, assoc_data->timeout); + return; } - *bss = assoc_data->bss; + bss = assoc_data->bss; if (status_code != WLAN_STATUS_SUCCESS) { sdata_info(sdata, "%pM denied association (code=%d)\n", mgmt->sa, status_code); ieee80211_destroy_assoc_data(sdata, false); } else { - if (!ieee80211_assoc_success(sdata, *bss, mgmt, len)) { + if (!ieee80211_assoc_success(sdata, bss, mgmt, len)) { /* oops -- internal error -- send timeout for now */ ieee80211_destroy_assoc_data(sdata, false); - cfg80211_put_bss(sdata->local->hw.wiphy, *bss); - return RX_MGMT_CFG80211_ASSOC_TIMEOUT; + cfg80211_put_bss(sdata->local->hw.wiphy, bss); + cfg80211_send_assoc_timeout(sdata->dev, mgmt->bssid); + return; } sdata_info(sdata, "associated\n"); @@ -2758,7 +2720,7 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, ieee80211_destroy_assoc_data(sdata, true); } - return RX_MGMT_CFG80211_RX_ASSOC; + cfg80211_send_rx_assoc(sdata->dev, bss, (u8 *)mgmt, len); } static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, @@ -2772,7 +2734,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, struct ieee80211_channel *channel; bool need_ps = false; - lockdep_assert_held(&sdata->u.mgd.mtx); + sdata_assert_lock(sdata); if ((sdata->u.mgd.associated && ether_addr_equal(mgmt->bssid, sdata->u.mgd.associated->bssid)) || @@ -2831,7 +2793,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, ifmgd = &sdata->u.mgd; - ASSERT_MGD_MTX(ifmgd); + sdata_assert_lock(sdata); if (!ether_addr_equal(mgmt->da, sdata->vif.addr)) return; /* ignore ProbeResp to foreign address */ @@ -2856,7 +2818,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, ifmgd->auth_data->tries = 0; ifmgd->auth_data->timeout = jiffies; ifmgd->auth_data->timeout_started = true; - run_again(ifmgd, ifmgd->auth_data->timeout); + run_again(sdata, ifmgd->auth_data->timeout); } } @@ -2881,10 +2843,9 @@ static const u64 care_about_ies = (1ULL << WLAN_EID_HT_CAPABILITY) | (1ULL << WLAN_EID_HT_OPERATION); -static enum rx_mgmt_action -ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *mgmt, size_t len, - u8 *deauth_buf, struct ieee80211_rx_status *rx_status) +static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, size_t len, + struct ieee80211_rx_status *rx_status) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf; @@ -2899,24 +2860,25 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, u8 erp_value = 0; u32 ncrc; u8 *bssid; + u8 deauth_buf[IEEE80211_DEAUTH_FRAME_LEN]; - lockdep_assert_held(&ifmgd->mtx); + sdata_assert_lock(sdata); /* Process beacon from the current BSS */ baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt; if (baselen > len) - return RX_MGMT_NONE; + return; rcu_read_lock(); chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); if (!chanctx_conf) { rcu_read_unlock(); - return RX_MGMT_NONE; + return; } if (rx_status->freq != chanctx_conf->def.chan->center_freq) { rcu_read_unlock(); - return RX_MGMT_NONE; + return; } chan = chanctx_conf->def.chan; rcu_read_unlock(); @@ -2943,13 +2905,13 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, /* continue assoc process */ ifmgd->assoc_data->timeout = jiffies; ifmgd->assoc_data->timeout_started = true; - run_again(ifmgd, ifmgd->assoc_data->timeout); - return RX_MGMT_NONE; + run_again(sdata, ifmgd->assoc_data->timeout); + return; } if (!ifmgd->associated || !ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid)) - return RX_MGMT_NONE; + return; bssid = ifmgd->associated->bssid; /* Track average RSSI from the Beacon frames of the current AP */ @@ -3095,7 +3057,7 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, } if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid) - return RX_MGMT_NONE; + return; ifmgd->beacon_crc = ncrc; ifmgd->beacon_crc_valid = true; @@ -3151,7 +3113,9 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, WLAN_REASON_DEAUTH_LEAVING, true, deauth_buf); - return RX_MGMT_CFG80211_TX_DEAUTH; + cfg80211_send_deauth(sdata->dev, deauth_buf, + sizeof(deauth_buf)); + return; } if (sta && elems.opmode_notif) @@ -3168,19 +3132,13 @@ ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, elems.pwr_constr_elem); ieee80211_bss_info_change_notify(sdata, changed); - - return RX_MGMT_NONE; } void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_rx_status *rx_status; struct ieee80211_mgmt *mgmt; - struct cfg80211_bss *bss = NULL; - enum rx_mgmt_action rma = RX_MGMT_NONE; - u8 deauth_buf[IEEE80211_DEAUTH_FRAME_LEN]; u16 fc; struct ieee802_11_elems elems; int ies_len; @@ -3189,28 +3147,27 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, mgmt = (struct ieee80211_mgmt *) skb->data; fc = le16_to_cpu(mgmt->frame_control); - mutex_lock(&ifmgd->mtx); + sdata_lock(sdata); switch (fc & IEEE80211_FCTL_STYPE) { case IEEE80211_STYPE_BEACON: - rma = ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len, - deauth_buf, rx_status); + ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len, rx_status); break; case IEEE80211_STYPE_PROBE_RESP: ieee80211_rx_mgmt_probe_resp(sdata, skb); break; case IEEE80211_STYPE_AUTH: - rma = ieee80211_rx_mgmt_auth(sdata, mgmt, skb->len); + ieee80211_rx_mgmt_auth(sdata, mgmt, skb->len); break; case IEEE80211_STYPE_DEAUTH: - rma = ieee80211_rx_mgmt_deauth(sdata, mgmt, skb->len); + ieee80211_rx_mgmt_deauth(sdata, mgmt, skb->len); break; case IEEE80211_STYPE_DISASSOC: - rma = ieee80211_rx_mgmt_disassoc(sdata, mgmt, skb->len); + ieee80211_rx_mgmt_disassoc(sdata, mgmt, skb->len); break; case IEEE80211_STYPE_ASSOC_RESP: case IEEE80211_STYPE_REASSOC_RESP: - rma = ieee80211_rx_mgmt_assoc_resp(sdata, mgmt, skb->len, &bss); + ieee80211_rx_mgmt_assoc_resp(sdata, mgmt, skb->len); break; case IEEE80211_STYPE_ACTION: if (mgmt->u.action.category == WLAN_CATEGORY_SPECTRUM_MGMT) { @@ -3256,34 +3213,7 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, } break; } - mutex_unlock(&ifmgd->mtx); - - switch (rma) { - case RX_MGMT_NONE: - /* no action */ - break; - case RX_MGMT_CFG80211_DEAUTH: - cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len); - break; - case RX_MGMT_CFG80211_DISASSOC: - cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len); - break; - case RX_MGMT_CFG80211_RX_AUTH: - cfg80211_send_rx_auth(sdata->dev, (u8 *)mgmt, skb->len); - break; - case RX_MGMT_CFG80211_RX_ASSOC: - cfg80211_send_rx_assoc(sdata->dev, bss, (u8 *)mgmt, skb->len); - break; - case RX_MGMT_CFG80211_ASSOC_TIMEOUT: - cfg80211_send_assoc_timeout(sdata->dev, mgmt->bssid); - break; - case RX_MGMT_CFG80211_TX_DEAUTH: - cfg80211_send_deauth(sdata->dev, deauth_buf, - sizeof(deauth_buf)); - break; - default: - WARN(1, "unexpected: %d", rma); - } + sdata_unlock(sdata); } static void ieee80211_sta_timer(unsigned long data) @@ -3297,20 +3227,12 @@ static void ieee80211_sta_timer(unsigned long data) static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata, u8 *bssid, u8 reason, bool tx) { - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, reason, tx, frame_buf); - mutex_unlock(&ifmgd->mtx); - /* - * must be outside lock due to cfg80211, - * but that's not a problem. - */ cfg80211_send_deauth(sdata->dev, frame_buf, IEEE80211_DEAUTH_FRAME_LEN); - - mutex_lock(&ifmgd->mtx); } static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata) @@ -3320,7 +3242,7 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata) struct ieee80211_mgd_auth_data *auth_data = ifmgd->auth_data; u32 tx_flags = 0; - lockdep_assert_held(&ifmgd->mtx); + sdata_assert_lock(sdata); if (WARN_ON_ONCE(!auth_data)) return -EINVAL; @@ -3393,7 +3315,7 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata) if (tx_flags == 0) { auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; ifmgd->auth_data->timeout_started = true; - run_again(ifmgd, auth_data->timeout); + run_again(sdata, auth_data->timeout); } else { auth_data->timeout_started = false; } @@ -3406,7 +3328,7 @@ static int ieee80211_do_assoc(struct ieee80211_sub_if_data *sdata) struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data; struct ieee80211_local *local = sdata->local; - lockdep_assert_held(&sdata->u.mgd.mtx); + sdata_assert_lock(sdata); assoc_data->tries++; if (assoc_data->tries > IEEE80211_ASSOC_MAX_TRIES) { @@ -3430,7 +3352,7 @@ static int ieee80211_do_assoc(struct ieee80211_sub_if_data *sdata) if (!(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) { assoc_data->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT; assoc_data->timeout_started = true; - run_again(&sdata->u.mgd, assoc_data->timeout); + run_again(sdata, assoc_data->timeout); } else { assoc_data->timeout_started = false; } @@ -3455,7 +3377,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - mutex_lock(&ifmgd->mtx); + sdata_lock(sdata); if (ifmgd->status_received) { __le16 fc = ifmgd->status_fc; @@ -3467,7 +3389,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) if (status_acked) { ifmgd->auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT_SHORT; - run_again(ifmgd, ifmgd->auth_data->timeout); + run_again(sdata, ifmgd->auth_data->timeout); } else { ifmgd->auth_data->timeout = jiffies - 1; } @@ -3478,7 +3400,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) if (status_acked) { ifmgd->assoc_data->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT_SHORT; - run_again(ifmgd, ifmgd->assoc_data->timeout); + run_again(sdata, ifmgd->assoc_data->timeout); } else { ifmgd->assoc_data->timeout = jiffies - 1; } @@ -3501,12 +3423,10 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) ieee80211_destroy_auth_data(sdata, false); - mutex_unlock(&ifmgd->mtx); cfg80211_send_auth_timeout(sdata->dev, bssid); - mutex_lock(&ifmgd->mtx); } } else if (ifmgd->auth_data && ifmgd->auth_data->timeout_started) - run_again(ifmgd, ifmgd->auth_data->timeout); + run_again(sdata, ifmgd->auth_data->timeout); if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started && time_after(jiffies, ifmgd->assoc_data->timeout)) { @@ -3519,12 +3439,10 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) ieee80211_destroy_assoc_data(sdata, false); - mutex_unlock(&ifmgd->mtx); cfg80211_send_assoc_timeout(sdata->dev, bssid); - mutex_lock(&ifmgd->mtx); } } else if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started) - run_again(ifmgd, ifmgd->assoc_data->timeout); + run_again(sdata, ifmgd->assoc_data->timeout); if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL | IEEE80211_STA_CONNECTION_POLL) && @@ -3558,7 +3476,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) false); } } else if (time_is_after_jiffies(ifmgd->probe_timeout)) - run_again(ifmgd, ifmgd->probe_timeout); + run_again(sdata, ifmgd->probe_timeout); else if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) { mlme_dbg(sdata, "Failed to send nullfunc to AP %pM after %dms, disconnecting\n", @@ -3587,7 +3505,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) } } - mutex_unlock(&ifmgd->mtx); + sdata_unlock(sdata); } static void ieee80211_sta_bcn_mon_timer(unsigned long data) @@ -3648,9 +3566,9 @@ void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - mutex_lock(&ifmgd->mtx); + sdata_lock(sdata); if (!ifmgd->associated) { - mutex_unlock(&ifmgd->mtx); + sdata_unlock(sdata); return; } @@ -3661,10 +3579,10 @@ void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata) ifmgd->associated->bssid, WLAN_REASON_UNSPECIFIED, true); - mutex_unlock(&ifmgd->mtx); + sdata_unlock(sdata); return; } - mutex_unlock(&ifmgd->mtx); + sdata_unlock(sdata); } #endif @@ -3696,8 +3614,6 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) ifmgd->uapsd_max_sp_len = sdata->local->hw.uapsd_max_sp_len; ifmgd->p2p_noa_index = -1; - mutex_init(&ifmgd->mtx); - if (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS) ifmgd->req_smps = IEEE80211_SMPS_AUTOMATIC; else @@ -4053,8 +3969,6 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, /* try to authenticate/probe */ - mutex_lock(&ifmgd->mtx); - if ((ifmgd->auth_data && !ifmgd->auth_data->done) || ifmgd->assoc_data) { err = -EBUSY; @@ -4074,8 +3988,8 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, WLAN_REASON_UNSPECIFIED, false, frame_buf); - __cfg80211_send_deauth(sdata->dev, frame_buf, - sizeof(frame_buf)); + cfg80211_send_deauth(sdata->dev, frame_buf, + sizeof(frame_buf)); } sdata_info(sdata, "authenticate with %pM\n", req->bss->bssid); @@ -4092,8 +4006,7 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, /* hold our own reference */ cfg80211_ref_bss(local->hw.wiphy, auth_data->bss); - err = 0; - goto out_unlock; + return 0; err_clear: memset(ifmgd->bssid, 0, ETH_ALEN); @@ -4101,9 +4014,6 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, ifmgd->auth_data = NULL; err_free: kfree(auth_data); - out_unlock: - mutex_unlock(&ifmgd->mtx); - return err; } @@ -4134,8 +4044,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, assoc_data->ssid_len = ssidie[1]; rcu_read_unlock(); - mutex_lock(&ifmgd->mtx); - if (ifmgd->associated) { u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; @@ -4143,8 +4051,8 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, WLAN_REASON_UNSPECIFIED, false, frame_buf); - __cfg80211_send_deauth(sdata->dev, frame_buf, - sizeof(frame_buf)); + cfg80211_send_deauth(sdata->dev, frame_buf, + sizeof(frame_buf)); } if (ifmgd->auth_data && !ifmgd->auth_data->done) { @@ -4338,7 +4246,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, } rcu_read_unlock(); - run_again(ifmgd, assoc_data->timeout); + run_again(sdata, assoc_data->timeout); if (bss->corrupt_data) { char *corrupt_type = "data"; @@ -4354,17 +4262,13 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, corrupt_type); } - err = 0; - goto out; + return 0; err_clear: memset(ifmgd->bssid, 0, ETH_ALEN); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID); ifmgd->assoc_data = NULL; err_free: kfree(assoc_data); - out: - mutex_unlock(&ifmgd->mtx); - return err; } @@ -4376,8 +4280,6 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, bool tx = !req->local_state_change; bool report_frame = false; - mutex_lock(&ifmgd->mtx); - sdata_info(sdata, "deauthenticating from %pM by local choice (reason=%d)\n", req->bssid, req->reason_code); @@ -4389,7 +4291,6 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, req->reason_code, tx, frame_buf); ieee80211_destroy_auth_data(sdata, false); - mutex_unlock(&ifmgd->mtx); report_frame = true; goto out; @@ -4401,12 +4302,11 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, req->reason_code, tx, frame_buf); report_frame = true; } - mutex_unlock(&ifmgd->mtx); out: if (report_frame) - __cfg80211_send_deauth(sdata->dev, frame_buf, - IEEE80211_DEAUTH_FRAME_LEN); + cfg80211_send_deauth(sdata->dev, frame_buf, + IEEE80211_DEAUTH_FRAME_LEN); return 0; } @@ -4418,18 +4318,14 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, u8 bssid[ETH_ALEN]; u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; - mutex_lock(&ifmgd->mtx); - /* * cfg80211 should catch this ... but it's racy since * we can receive a disassoc frame, process it, hand it * to cfg80211 while that's in a locked section already * trying to tell us that the user wants to disconnect. */ - if (ifmgd->associated != req->bss) { - mutex_unlock(&ifmgd->mtx); + if (ifmgd->associated != req->bss) return -ENOLINK; - } sdata_info(sdata, "disassociating from %pM by local choice (reason=%d)\n", @@ -4439,10 +4335,9 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DISASSOC, req->reason_code, !req->local_state_change, frame_buf); - mutex_unlock(&ifmgd->mtx); - __cfg80211_send_disassoc(sdata->dev, frame_buf, - IEEE80211_DEAUTH_FRAME_LEN); + cfg80211_send_disassoc(sdata->dev, frame_buf, + IEEE80211_DEAUTH_FRAME_LEN); return 0; } @@ -4462,13 +4357,13 @@ void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata) cancel_work_sync(&ifmgd->csa_connection_drop_work); cancel_work_sync(&ifmgd->chswitch_work); - mutex_lock(&ifmgd->mtx); + sdata_lock(sdata); if (ifmgd->assoc_data) ieee80211_destroy_assoc_data(sdata, false); if (ifmgd->auth_data) ieee80211_destroy_auth_data(sdata, false); del_timer_sync(&ifmgd->timer); - mutex_unlock(&ifmgd->mtx); + sdata_unlock(sdata); } void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif, diff --git a/net/mac80211/util.c b/net/mac80211/util.c index ffdfe4bc89ad..2a8d759324c2 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1581,9 +1581,9 @@ int ieee80211_reconfig(struct ieee80211_local *local) if (sdata->u.mgd.dtim_period) changed |= BSS_CHANGED_DTIM_PERIOD; - mutex_lock(&sdata->u.mgd.mtx); + sdata_lock(sdata); ieee80211_bss_info_change_notify(sdata, changed); - mutex_unlock(&sdata->u.mgd.mtx); + sdata_unlock(sdata); break; case NL80211_IFTYPE_ADHOC: changed |= BSS_CHANGED_IBSS; diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 68b40f21bc38..80ffb0138919 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -25,12 +25,9 @@ void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len) struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); trace_cfg80211_send_rx_auth(dev); - wdev_lock(wdev); nl80211_send_rx_auth(rdev, dev, buf, len, GFP_KERNEL); cfg80211_sme_rx_auth(dev, buf, len); - - wdev_unlock(wdev); } EXPORT_SYMBOL(cfg80211_send_rx_auth); @@ -46,7 +43,6 @@ void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss, int ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable); trace_cfg80211_send_rx_assoc(dev, bss); - wdev_lock(wdev); status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); @@ -59,7 +55,7 @@ void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss, if (status_code != WLAN_STATUS_SUCCESS && wdev->conn && cfg80211_sme_failed_reassoc(wdev)) { cfg80211_put_bss(wiphy, bss); - goto out; + return; } nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL); @@ -71,7 +67,7 @@ void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss, * sme will schedule work that does it later. */ cfg80211_put_bss(wiphy, bss); - goto out; + return; } if (!wdev->conn && wdev->sme_state == CFG80211_SME_IDLE) { @@ -87,13 +83,11 @@ void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss, __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs, status_code, status_code == WLAN_STATUS_SUCCESS, bss); - out: - wdev_unlock(wdev); } EXPORT_SYMBOL(cfg80211_send_rx_assoc); -void __cfg80211_send_deauth(struct net_device *dev, - const u8 *buf, size_t len) +void cfg80211_send_deauth(struct net_device *dev, + const u8 *buf, size_t len) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; @@ -102,7 +96,7 @@ void __cfg80211_send_deauth(struct net_device *dev, const u8 *bssid = mgmt->bssid; bool was_current = false; - trace___cfg80211_send_deauth(dev); + trace_cfg80211_send_deauth(dev); ASSERT_WDEV_LOCK(wdev); if (wdev->current_bss && @@ -129,20 +123,10 @@ void __cfg80211_send_deauth(struct net_device *dev, false, NULL); } } -EXPORT_SYMBOL(__cfg80211_send_deauth); - -void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - wdev_lock(wdev); - __cfg80211_send_deauth(dev, buf, len); - wdev_unlock(wdev); -} EXPORT_SYMBOL(cfg80211_send_deauth); -void __cfg80211_send_disassoc(struct net_device *dev, - const u8 *buf, size_t len) +void cfg80211_send_disassoc(struct net_device *dev, + const u8 *buf, size_t len) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; @@ -152,7 +136,7 @@ void __cfg80211_send_disassoc(struct net_device *dev, u16 reason_code; bool from_ap; - trace___cfg80211_send_disassoc(dev); + trace_cfg80211_send_disassoc(dev); ASSERT_WDEV_LOCK(wdev); nl80211_send_disassoc(rdev, dev, buf, len, GFP_KERNEL); @@ -175,16 +159,6 @@ void __cfg80211_send_disassoc(struct net_device *dev, from_ap = !ether_addr_equal(mgmt->sa, dev->dev_addr); __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap); } -EXPORT_SYMBOL(__cfg80211_send_disassoc); - -void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - wdev_lock(wdev); - __cfg80211_send_disassoc(dev, buf, len); - wdev_unlock(wdev); -} EXPORT_SYMBOL(cfg80211_send_disassoc); void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr) @@ -194,15 +168,12 @@ void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr) struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); trace_cfg80211_send_auth_timeout(dev, addr); - wdev_lock(wdev); nl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL); if (wdev->sme_state == CFG80211_SME_CONNECTING) __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, false, NULL); - - wdev_unlock(wdev); } EXPORT_SYMBOL(cfg80211_send_auth_timeout); @@ -213,15 +184,12 @@ void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr) struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); trace_cfg80211_send_assoc_timeout(dev, addr); - wdev_lock(wdev); nl80211_send_assoc_timeout(rdev, dev, addr, GFP_KERNEL); if (wdev->sme_state == CFG80211_SME_CONNECTING) __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, false, NULL); - - wdev_unlock(wdev); } EXPORT_SYMBOL(cfg80211_send_assoc_timeout); diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 5755bc14abbd..23fafeae8a10 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -1911,12 +1911,12 @@ TRACE_EVENT(cfg80211_send_rx_assoc, NETDEV_PR_ARG, MAC_PR_ARG(bssid), CHAN_PR_ARG) ); -DEFINE_EVENT(netdev_evt_only, __cfg80211_send_deauth, +DEFINE_EVENT(netdev_evt_only, cfg80211_send_deauth, TP_PROTO(struct net_device *netdev), TP_ARGS(netdev) ); -DEFINE_EVENT(netdev_evt_only, __cfg80211_send_disassoc, +DEFINE_EVENT(netdev_evt_only, cfg80211_send_disassoc, TP_PROTO(struct net_device *netdev), TP_ARGS(netdev) ); -- cgit v1.2.3 From 1cdd59ce8dcfa850ebb8ac2ab000a2ea572d1d69 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 10 May 2013 18:58:00 +0200 Subject: cfg80211: simplify and correct P2P-Device scan check If the driver for some reason successfully finishes scanning while in p2p_stop_device(), cfg80211 will still set it to aborted. Simplify this code using the new 'notified' value and only mark it aborted in case the driver didn't notify cfg80211 at all (in which case we also leak the request to not crash, this is a driver bug.) Signed-off-by: Johannes Berg --- net/wireless/core.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/net/wireless/core.c b/net/wireless/core.c index 5fc642d4071b..afcb9ec70adb 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -204,18 +204,15 @@ void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev, rdev->opencount--; if (rdev->scan_req && rdev->scan_req->wdev == wdev) { - bool busy = work_busy(&rdev->scan_done_wk); - /* - * If the work isn't pending or running (in which case it would - * be waiting for the lock we hold) the driver didn't properly - * cancel the scan when the interface was removed. In this case - * warn and leak the scan request object to not crash later. + * If the scan request wasn't notified as done, set it + * to aborted and leak it after a warning. The driver + * should have notified us that it ended at the latest + * during rdev_stop_p2p_device(). */ - WARN_ON(!busy); - - rdev->scan_req->aborted = true; - ___cfg80211_scan_done(rdev, !busy); + if (WARN_ON(!rdev->scan_req->notified)) + rdev->scan_req->aborted = true; + ___cfg80211_scan_done(rdev, !rdev->scan_req->notified); } } -- cgit v1.2.3 From db2424c58e5962a87888d25d29ceb0873eef6348 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 10 May 2013 19:07:52 +0200 Subject: regulatory: use RCU in regulatory_hint_11d() Since it just does a quick check of the last regulatory request, the function doesn't have to hold the reg mutex but can use RCU instead. Signed-off-by: Johannes Berg --- net/wireless/reg.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index e76559618588..17e5eccb42c8 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1715,20 +1715,18 @@ void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band, { char alpha2[2]; enum environment_cap env = ENVIRON_ANY; - struct regulatory_request *request, *lr; - - mutex_lock(®_mutex); - lr = get_last_request(); - - if (unlikely(!lr)) - goto out; + struct regulatory_request *request = NULL, *lr; /* IE len must be evenly divisible by 2 */ if (country_ie_len & 0x01) - goto out; + return; if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) - goto out; + return; + + request = kzalloc(sizeof(*request), GFP_KERNEL); + if (!request) + return; alpha2[0] = country_ie[0]; alpha2[1] = country_ie[1]; @@ -1738,6 +1736,12 @@ void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band, else if (country_ie[2] == 'O') env = ENVIRON_OUTDOOR; + rcu_read_lock(); + lr = get_last_request(); + + if (unlikely(!lr)) + goto out; + /* * We will run this only upon a successful connection on cfg80211. * We leave conflict resolution to the workqueue, where can hold @@ -1747,10 +1751,6 @@ void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band, lr->wiphy_idx != WIPHY_IDX_INVALID) goto out; - request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); - if (!request) - goto out; - request->wiphy_idx = get_wiphy_idx(wiphy); request->alpha2[0] = alpha2[0]; request->alpha2[1] = alpha2[1]; @@ -1758,8 +1758,10 @@ void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band, request->country_ie_env = env; queue_regulatory_request(request); + request = NULL; out: - mutex_unlock(®_mutex); + kfree(request); + rcu_read_unlock(); } static void restore_alpha2(char *alpha2, bool reset_user) -- cgit v1.2.3 From 38fd2143fa653f80729800c1d61d4207b91dca42 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 10 May 2013 19:17:17 +0200 Subject: regulatory: remove reg_mutex The reg_mutex is similar to the ones I just removed in cfg80211 but even less useful since it protects global data, and we hold the RTNL in all places (except module unload) already. Signed-off-by: Johannes Berg --- net/wireless/reg.c | 76 ++++++++++++++---------------------------------------- 1 file changed, 19 insertions(+), 57 deletions(-) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 17e5eccb42c8..e1d6749234c6 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -81,7 +81,10 @@ static struct regulatory_request core_request_world = { .country_ie_env = ENVIRON_ANY, }; -/* Receipt of information from last regulatory request */ +/* + * Receipt of information from last regulatory request, + * protected by RTNL (and can be accessed with RCU protection) + */ static struct regulatory_request __rcu *last_request = (void __rcu *)&core_request_world; @@ -96,39 +99,25 @@ static struct device_type reg_device_type = { * Central wireless core regulatory domains, we only need two, * the current one and a world regulatory domain in case we have no * information to give us an alpha2. + * (protected by RTNL, can be read under RCU) */ const struct ieee80211_regdomain __rcu *cfg80211_regdomain; -/* - * Protects static reg.c components: - * - cfg80211_regdomain (if not used with RCU) - * - cfg80211_world_regdom - * - last_request (if not used with RCU) - * - reg_num_devs_support_basehint - */ -static DEFINE_MUTEX(reg_mutex); - /* * Number of devices that registered to the core * that support cellular base station regulatory hints + * (protected by RTNL) */ static int reg_num_devs_support_basehint; -static inline void assert_reg_lock(void) -{ - lockdep_assert_held(®_mutex); -} - static const struct ieee80211_regdomain *get_cfg80211_regdom(void) { - return rcu_dereference_protected(cfg80211_regdomain, - lockdep_is_held(®_mutex)); + return rtnl_dereference(cfg80211_regdomain); } static const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy) { - return rcu_dereference_protected(wiphy->regd, - lockdep_is_held(®_mutex)); + return rtnl_dereference(wiphy->regd); } static void rcu_free_regdom(const struct ieee80211_regdomain *r) @@ -140,8 +129,7 @@ static void rcu_free_regdom(const struct ieee80211_regdomain *r) static struct regulatory_request *get_last_request(void) { - return rcu_dereference_check(last_request, - lockdep_is_held(®_mutex)); + return rcu_dereference_rtnl(last_request); } /* Used to queue up regulatory hints */ @@ -200,6 +188,7 @@ static const struct ieee80211_regdomain world_regdom = { } }; +/* protected by RTNL */ static const struct ieee80211_regdomain *cfg80211_world_regdom = &world_regdom; @@ -215,7 +204,7 @@ static void reset_regdomains(bool full_reset, const struct ieee80211_regdomain *r; struct regulatory_request *lr; - assert_reg_lock(); + ASSERT_RTNL(); r = get_cfg80211_regdom(); @@ -936,13 +925,7 @@ static bool reg_request_cell_base(struct regulatory_request *request) bool reg_last_request_cell_base(void) { - bool val; - - mutex_lock(®_mutex); - val = reg_request_cell_base(get_last_request()); - mutex_unlock(®_mutex); - - return val; + return reg_request_cell_base(get_last_request()); } #ifdef CONFIG_CFG80211_CERTIFICATION_ONUS @@ -1444,8 +1427,6 @@ static void reg_set_request_processed(void) * what it believes should be the current regulatory domain. * * Returns one of the different reg request treatment values. - * - * Caller must hold ®_mutex */ static enum reg_request_treatment __regulatory_hint(struct wiphy *wiphy, @@ -1622,10 +1603,8 @@ static void reg_process_pending_beacon_hints(void) static void reg_todo(struct work_struct *work) { rtnl_lock(); - mutex_lock(®_mutex); reg_process_pending_hints(); reg_process_pending_beacon_hints(); - mutex_unlock(®_mutex); rtnl_unlock(); } @@ -1851,8 +1830,6 @@ static void restore_regulatory_settings(bool reset_user) ASSERT_RTNL(); - mutex_lock(®_mutex); - reset_regdomains(true, &world_regdom); restore_alpha2(alpha2, reset_user); @@ -1906,8 +1883,6 @@ static void restore_regulatory_settings(bool reset_user) list_splice_tail_init(&tmp_reg_req_list, ®_requests_list); spin_unlock(®_requests_lock); - mutex_unlock(®_mutex); - REG_DBG_PRINT("Kicking the queue\n"); schedule_work(®_work); @@ -2222,7 +2197,6 @@ int set_regdom(const struct ieee80211_regdomain *rd) struct regulatory_request *lr; int r; - mutex_lock(®_mutex); lr = get_last_request(); /* Note that this doesn't update the wiphys, this is done below */ @@ -2232,14 +2206,12 @@ int set_regdom(const struct ieee80211_regdomain *rd) reg_set_request_processed(); kfree(rd); - goto out; + return r; } /* This would make this whole thing pointless */ - if (WARN_ON(!lr->intersect && rd != get_cfg80211_regdom())) { - r = -EINVAL; - goto out; - } + if (WARN_ON(!lr->intersect && rd != get_cfg80211_regdom())) + return -EINVAL; /* update all wiphys now with the new established regulatory domain */ update_all_wiphy_regulatory(lr->initiator); @@ -2250,10 +2222,7 @@ int set_regdom(const struct ieee80211_regdomain *rd) reg_set_request_processed(); - out: - mutex_unlock(®_mutex); - - return r; + return 0; } int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env) @@ -2278,14 +2247,10 @@ int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env) void wiphy_regulatory_register(struct wiphy *wiphy) { - mutex_lock(®_mutex); - if (!reg_dev_ignore_cell_hint(wiphy)) reg_num_devs_support_basehint++; wiphy_update_regulatory(wiphy, NL80211_REGDOM_SET_BY_CORE); - - mutex_unlock(®_mutex); } void wiphy_regulatory_deregister(struct wiphy *wiphy) @@ -2293,7 +2258,6 @@ void wiphy_regulatory_deregister(struct wiphy *wiphy) struct wiphy *request_wiphy = NULL; struct regulatory_request *lr; - mutex_lock(®_mutex); lr = get_last_request(); if (!reg_dev_ignore_cell_hint(wiphy)) @@ -2306,12 +2270,10 @@ void wiphy_regulatory_deregister(struct wiphy *wiphy) request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx); if (!request_wiphy || request_wiphy != wiphy) - goto out; + return; lr->wiphy_idx = WIPHY_IDX_INVALID; lr->country_ie_env = ENVIRON_ANY; -out: - mutex_unlock(®_mutex); } static void reg_timeout_work(struct work_struct *work) @@ -2375,9 +2337,9 @@ void regulatory_exit(void) cancel_delayed_work_sync(®_timeout); /* Lock to suppress warnings */ - mutex_lock(®_mutex); + rtnl_lock(); reset_regdomains(true, NULL); - mutex_unlock(®_mutex); + rtnl_unlock(); dev_set_uevent_suppress(®_pdev->dev, true); -- cgit v1.2.3 From 91bf9b26fc95c505846bc2f744a73b51f2aaee1d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 15 May 2013 17:44:01 +0200 Subject: cfg80211: remove some locked wrappers from mlme API By making all the API functions require wdev locking we can clean up the API a bit, getting rid of the locking version of each function. This also decreases the size of cfg80211 by a small amount. Signed-off-by: Johannes Berg --- net/wireless/core.h | 25 ++--------- net/wireless/mlme.c | 120 ++++++++++--------------------------------------- net/wireless/nl80211.c | 34 +++++++++----- net/wireless/sme.c | 40 ++++++++--------- 4 files changed, 70 insertions(+), 149 deletions(-) diff --git a/net/wireless/core.h b/net/wireless/core.h index d21a0fc01401..c8f87dfd50d3 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -282,38 +282,21 @@ int cfg80211_stop_ap(struct cfg80211_registered_device *rdev, struct net_device *dev); /* MLME */ -int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct ieee80211_channel *chan, - enum nl80211_auth_type auth_type, - const u8 *bssid, - const u8 *ssid, int ssid_len, - const u8 *ie, int ie_len, - const u8 *key, int key_len, int key_idx, - const u8 *sae_data, int sae_data_len); int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, - struct net_device *dev, struct ieee80211_channel *chan, - enum nl80211_auth_type auth_type, const u8 *bssid, + struct net_device *dev, + struct ieee80211_channel *chan, + enum nl80211_auth_type auth_type, + const u8 *bssid, const u8 *ssid, int ssid_len, const u8 *ie, int ie_len, const u8 *key, int key_len, int key_idx, const u8 *sae_data, int sae_data_len); -int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct ieee80211_channel *chan, - const u8 *bssid, - const u8 *ssid, int ssid_len, - struct cfg80211_assoc_request *req); int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, struct net_device *dev, struct ieee80211_channel *chan, const u8 *bssid, const u8 *ssid, int ssid_len, struct cfg80211_assoc_request *req); -int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, - struct net_device *dev, const u8 *bssid, - const u8 *ie, int ie_len, u16 reason, - bool local_state_change); int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, struct net_device *dev, const u8 *bssid, const u8 *ie, int ie_len, u16 reason, diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 80ffb0138919..7bde5d9c0003 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -221,15 +221,15 @@ void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr, EXPORT_SYMBOL(cfg80211_michael_mic_failure); /* some MLME handling for userspace SME */ -int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct ieee80211_channel *chan, - enum nl80211_auth_type auth_type, - const u8 *bssid, - const u8 *ssid, int ssid_len, - const u8 *ie, int ie_len, - const u8 *key, int key_len, int key_idx, - const u8 *sae_data, int sae_data_len) +int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct ieee80211_channel *chan, + enum nl80211_auth_type auth_type, + const u8 *bssid, + const u8 *ssid, int ssid_len, + const u8 *ie, int ie_len, + const u8 *key, int key_len, int key_idx, + const u8 *sae_data, int sae_data_len) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_auth_request req = { @@ -271,28 +271,6 @@ out: return err; } -int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, - struct net_device *dev, struct ieee80211_channel *chan, - enum nl80211_auth_type auth_type, const u8 *bssid, - const u8 *ssid, int ssid_len, - const u8 *ie, int ie_len, - const u8 *key, int key_len, int key_idx, - const u8 *sae_data, int sae_data_len) -{ - int err; - - ASSERT_RTNL(); - - wdev_lock(dev->ieee80211_ptr); - err = __cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid, - ssid, ssid_len, ie, ie_len, - key, key_len, key_idx, - sae_data, sae_data_len); - wdev_unlock(dev->ieee80211_ptr); - - return err; -} - /* Do a logical ht_capa &= ht_capa_mask. */ void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa, const struct ieee80211_ht_cap *ht_capa_mask) @@ -327,12 +305,12 @@ void cfg80211_oper_and_vht_capa(struct ieee80211_vht_cap *vht_capa, p1[i] &= p2[i]; } -int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct ieee80211_channel *chan, - const u8 *bssid, - const u8 *ssid, int ssid_len, - struct cfg80211_assoc_request *req) +int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct ieee80211_channel *chan, + const u8 *bssid, + const u8 *ssid, int ssid_len, + struct cfg80211_assoc_request *req) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; @@ -382,30 +360,10 @@ out: return err; } -int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct ieee80211_channel *chan, - const u8 *bssid, - const u8 *ssid, int ssid_len, - struct cfg80211_assoc_request *req) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - int err; - - ASSERT_RTNL(); - - wdev_lock(wdev); - err = __cfg80211_mlme_assoc(rdev, dev, chan, bssid, - ssid, ssid_len, req); - wdev_unlock(wdev); - - return err; -} - -int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, - struct net_device *dev, const u8 *bssid, - const u8 *ie, int ie_len, u16 reason, - bool local_state_change) +int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *bssid, + const u8 *ie, int ie_len, u16 reason, + bool local_state_change) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_deauth_request req = { @@ -425,26 +383,10 @@ int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, return rdev_deauth(rdev, dev, &req); } -int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, - struct net_device *dev, const u8 *bssid, - const u8 *ie, int ie_len, u16 reason, - bool local_state_change) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - int err; - - wdev_lock(wdev); - err = __cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason, - local_state_change); - wdev_unlock(wdev); - - return err; -} - -static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, - struct net_device *dev, const u8 *bssid, - const u8 *ie, int ie_len, u16 reason, - bool local_state_change) +int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *bssid, + const u8 *ie, int ie_len, u16 reason, + bool local_state_change) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_disassoc_request req = { @@ -470,22 +412,6 @@ static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, return rdev_disassoc(rdev, dev, &req); } -int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, - struct net_device *dev, const u8 *bssid, - const u8 *ie, int ie_len, u16 reason, - bool local_state_change) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - int err; - - wdev_lock(wdev); - err = __cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason, - local_state_change); - wdev_unlock(wdev); - - return err; -} - void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, struct net_device *dev) { diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 74cdb1a0cf31..49c2f2f511dc 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -5904,10 +5904,13 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) if (local_state_change) return 0; - return cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid, - ssid, ssid_len, ie, ie_len, - key.p.key, key.p.key_len, key.idx, - sae_data, sae_data_len); + wdev_lock(dev->ieee80211_ptr); + err = cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid, + ssid, ssid_len, ie, ie_len, + key.p.key, key.p.key_len, key.idx, + sae_data, sae_data_len); + wdev_unlock(dev->ieee80211_ptr); + return err; } static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev, @@ -6074,9 +6077,12 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) } err = nl80211_crypto_settings(rdev, info, &req.crypto, 1); - if (!err) + if (!err) { + wdev_lock(dev->ieee80211_ptr); err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, ssid, ssid_len, &req); + wdev_unlock(dev->ieee80211_ptr); + } return err; } @@ -6086,7 +6092,7 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; const u8 *ie = NULL, *bssid; - int ie_len = 0; + int ie_len = 0, err; u16 reason_code; bool local_state_change; @@ -6121,8 +6127,11 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE]; - return cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code, - local_state_change); + wdev_lock(dev->ieee80211_ptr); + err = cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code, + local_state_change); + wdev_unlock(dev->ieee80211_ptr); + return err; } static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) @@ -6130,7 +6139,7 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; const u8 *ie = NULL, *bssid; - int ie_len = 0; + int ie_len = 0, err; u16 reason_code; bool local_state_change; @@ -6165,8 +6174,11 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE]; - return cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code, - local_state_change); + wdev_lock(dev->ieee80211_ptr); + err = cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code, + local_state_change); + wdev_unlock(dev->ieee80211_ptr); + return err; } static bool diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 4dbf31407a56..31d67add7424 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -169,13 +169,13 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) case CFG80211_CONN_AUTHENTICATE_NEXT: BUG_ON(!rdev->ops->auth); wdev->conn->state = CFG80211_CONN_AUTHENTICATING; - return __cfg80211_mlme_auth(rdev, wdev->netdev, - params->channel, params->auth_type, - params->bssid, - params->ssid, params->ssid_len, - NULL, 0, - params->key, params->key_len, - params->key_idx, NULL, 0); + return cfg80211_mlme_auth(rdev, wdev->netdev, + params->channel, params->auth_type, + params->bssid, + params->ssid, params->ssid_len, + NULL, 0, + params->key, params->key_len, + params->key_idx, NULL, 0); case CFG80211_CONN_ASSOCIATE_NEXT: BUG_ON(!rdev->ops->assoc); wdev->conn->state = CFG80211_CONN_ASSOCIATING; @@ -191,19 +191,19 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) req.vht_capa = params->vht_capa; req.vht_capa_mask = params->vht_capa_mask; - err = __cfg80211_mlme_assoc(rdev, wdev->netdev, params->channel, - params->bssid, params->ssid, - params->ssid_len, &req); + err = cfg80211_mlme_assoc(rdev, wdev->netdev, params->channel, + params->bssid, params->ssid, + params->ssid_len, &req); if (err) - __cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, - NULL, 0, - WLAN_REASON_DEAUTH_LEAVING, - false); + cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, + NULL, 0, + WLAN_REASON_DEAUTH_LEAVING, + false); return err; case CFG80211_CONN_DEAUTH_ASSOC_FAIL: - __cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, - NULL, 0, - WLAN_REASON_DEAUTH_LEAVING, false); + cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, + NULL, 0, + WLAN_REASON_DEAUTH_LEAVING, false); /* return an error so that we call __cfg80211_connect_result() */ return -EINVAL; default: @@ -961,7 +961,7 @@ int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, } /* wdev->conn->params.bssid must be set if > SCANNING */ - err = __cfg80211_mlme_deauth(rdev, dev, + err = cfg80211_mlme_deauth(rdev, dev, wdev->conn->params.bssid, NULL, 0, reason, false); if (err) @@ -1018,6 +1018,6 @@ void cfg80211_sme_disassoc(struct net_device *dev, memcpy(bssid, bss->pub.bssid, ETH_ALEN); - __cfg80211_mlme_deauth(rdev, dev, bssid, NULL, 0, - WLAN_REASON_DEAUTH_LEAVING, false); + cfg80211_mlme_deauth(rdev, dev, bssid, NULL, 0, + WLAN_REASON_DEAUTH_LEAVING, false); } -- cgit v1.2.3 From 83739b03de97049181d711c95200b94a14d3f693 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 15 May 2013 17:44:01 +0200 Subject: cfg80211: remove some locked wrappers from sme API By making all the API functions require wdev locking we can clean up the API a bit, getting rid of the locking version of each function. This also decreases the size of cfg80211 by a small amount. Signed-off-by: Johannes Berg --- net/wireless/core.c | 4 ++-- net/wireless/core.h | 11 ++--------- net/wireless/nl80211.c | 10 ++++++++-- net/wireless/sme.c | 41 +++++++---------------------------------- net/wireless/util.c | 2 ++ net/wireless/wext-sme.c | 23 +++++++++++------------ 6 files changed, 32 insertions(+), 59 deletions(-) diff --git a/net/wireless/core.c b/net/wireless/core.c index afcb9ec70adb..ee422871fe9e 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -755,8 +755,8 @@ void cfg80211_leave(struct cfg80211_registered_device *rdev, wdev->wext.ie_len = 0; wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; #endif - __cfg80211_disconnect(rdev, dev, - WLAN_REASON_DEAUTH_LEAVING, true); + cfg80211_disconnect(rdev, dev, + WLAN_REASON_DEAUTH_LEAVING, true); wdev_unlock(wdev); break; case NL80211_IFTYPE_MESH_POINT: diff --git a/net/wireless/core.h b/net/wireless/core.h index c8f87dfd50d3..b4b4a566626b 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -328,18 +328,11 @@ void cfg80211_oper_and_vht_capa(struct ieee80211_vht_cap *vht_capa, const struct ieee80211_vht_cap *vht_capa_mask); /* SME */ -int __cfg80211_connect(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct cfg80211_connect_params *connect, - struct cfg80211_cached_keys *connkeys, - const u8 *prev_bssid); int cfg80211_connect(struct cfg80211_registered_device *rdev, struct net_device *dev, struct cfg80211_connect_params *connect, - struct cfg80211_cached_keys *connkeys); -int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, - struct net_device *dev, u16 reason, - bool wextev); + struct cfg80211_cached_keys *connkeys, + const u8 *prev_bssid); int cfg80211_disconnect(struct cfg80211_registered_device *rdev, struct net_device *dev, u16 reason, bool wextev); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 49c2f2f511dc..a09f36bb957c 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -6683,7 +6683,9 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) sizeof(connect.vht_capa)); } - err = cfg80211_connect(rdev, dev, &connect, connkeys); + wdev_lock(dev->ieee80211_ptr); + err = cfg80211_connect(rdev, dev, &connect, connkeys, NULL); + wdev_unlock(dev->ieee80211_ptr); if (err) kfree(connkeys); return err; @@ -6694,6 +6696,7 @@ static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info) struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; u16 reason; + int ret; if (!info->attrs[NL80211_ATTR_REASON_CODE]) reason = WLAN_REASON_DEAUTH_LEAVING; @@ -6707,7 +6710,10 @@ static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info) dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) return -EOPNOTSUPP; - return cfg80211_disconnect(rdev, dev, reason, true); + wdev_lock(dev->ieee80211_ptr); + ret = cfg80211_disconnect(rdev, dev, reason, true); + wdev_unlock(dev->ieee80211_ptr); + return ret; } static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info) diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 31d67add7424..81be95f3be74 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -760,11 +760,11 @@ void cfg80211_disconnected(struct net_device *dev, u16 reason, } EXPORT_SYMBOL(cfg80211_disconnected); -int __cfg80211_connect(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct cfg80211_connect_params *connect, - struct cfg80211_cached_keys *connkeys, - const u8 *prev_bssid) +int cfg80211_connect(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_connect_params *connect, + struct cfg80211_cached_keys *connkeys, + const u8 *prev_bssid) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_bss *bss = NULL; @@ -911,22 +911,8 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev, } } -int cfg80211_connect(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct cfg80211_connect_params *connect, - struct cfg80211_cached_keys *connkeys) -{ - int err; - - wdev_lock(dev->ieee80211_ptr); - err = __cfg80211_connect(rdev, dev, connect, connkeys, NULL); - wdev_unlock(dev->ieee80211_ptr); - - return err; -} - -int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, - struct net_device *dev, u16 reason, bool wextev) +int cfg80211_disconnect(struct cfg80211_registered_device *rdev, + struct net_device *dev, u16 reason, bool wextev) { struct wireless_dev *wdev = dev->ieee80211_ptr; int err; @@ -983,19 +969,6 @@ int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, return 0; } -int cfg80211_disconnect(struct cfg80211_registered_device *rdev, - struct net_device *dev, - u16 reason, bool wextev) -{ - int err; - - wdev_lock(dev->ieee80211_ptr); - err = __cfg80211_disconnect(rdev, dev, reason, wextev); - wdev_unlock(dev->ieee80211_ptr); - - return err; -} - void cfg80211_sme_disassoc(struct net_device *dev, struct cfg80211_internal_bss *bss) { diff --git a/net/wireless/util.c b/net/wireless/util.c index 501724257af5..74458b7f61eb 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -858,8 +858,10 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, break; case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT: + wdev_lock(dev->ieee80211_ptr); cfg80211_disconnect(rdev, dev, WLAN_REASON_DEAUTH_LEAVING, true); + wdev_unlock(dev->ieee80211_ptr); break; case NL80211_IFTYPE_MESH_POINT: /* mesh should be handled? */ diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index aeefd6817189..a53f8404f451 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c @@ -54,8 +54,8 @@ int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, if (wdev->wext.prev_bssid_valid) prev_bssid = wdev->wext.prev_bssid; - err = __cfg80211_connect(rdev, wdev->netdev, - &wdev->wext.connect, ck, prev_bssid); + err = cfg80211_connect(rdev, wdev->netdev, + &wdev->wext.connect, ck, prev_bssid); if (err) kfree(ck); @@ -100,8 +100,8 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev, /* if SSID set, we'll try right again, avoid event */ if (wdev->wext.connect.ssid_len) event = false; - err = __cfg80211_disconnect(rdev, dev, - WLAN_REASON_DEAUTH_LEAVING, event); + err = cfg80211_disconnect(rdev, dev, + WLAN_REASON_DEAUTH_LEAVING, event); if (err) goto out; } @@ -199,8 +199,8 @@ int cfg80211_mgd_wext_siwessid(struct net_device *dev, /* if SSID set now, we'll try to connect, avoid event */ if (len) event = false; - err = __cfg80211_disconnect(rdev, dev, - WLAN_REASON_DEAUTH_LEAVING, event); + err = cfg80211_disconnect(rdev, dev, + WLAN_REASON_DEAUTH_LEAVING, event); if (err) goto out; } @@ -288,8 +288,8 @@ int cfg80211_mgd_wext_siwap(struct net_device *dev, ether_addr_equal(bssid, wdev->wext.connect.bssid)) goto out; - err = __cfg80211_disconnect(rdev, dev, - WLAN_REASON_DEAUTH_LEAVING, false); + err = cfg80211_disconnect(rdev, dev, + WLAN_REASON_DEAUTH_LEAVING, false); if (err) goto out; } @@ -365,8 +365,8 @@ int cfg80211_wext_siwgenie(struct net_device *dev, wdev->wext.ie_len = ie_len; if (wdev->sme_state != CFG80211_SME_IDLE) { - err = __cfg80211_disconnect(rdev, dev, - WLAN_REASON_DEAUTH_LEAVING, false); + err = cfg80211_disconnect(rdev, dev, + WLAN_REASON_DEAUTH_LEAVING, false); if (err) goto out; } @@ -402,8 +402,7 @@ int cfg80211_wext_siwmlme(struct net_device *dev, switch (mlme->cmd) { case IW_MLME_DEAUTH: case IW_MLME_DISASSOC: - err = __cfg80211_disconnect(rdev, dev, mlme->reason_code, - true); + err = cfg80211_disconnect(rdev, dev, mlme->reason_code, true); break; default: err = -EOPNOTSUPP; -- cgit v1.2.3 From 2e460fc07ef0ebadfb593b564a8c7fa6559ac3d7 Mon Sep 17 00:00:00 2001 From: Nithin Sujir Date: Thu, 23 May 2013 11:11:22 +0000 Subject: tg3: Split APE driver state change out of boot reset signature update Unlike the boot signature that needs to be set before every reset, the ape state only needs to be updated to tell the firmware that the driver is now taking/releasing control of the hardware. Move the calls to tg3_ape_driver_state_change() to better, more appropriate places. Also, the firmware does not distinguish between SUSPEND and START states anymore. Remove the SUSPEND case in the switch. Signed-off-by: Nithin Nayak Sujir Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/tg3.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index c15a92d012ef..ae808b03affe 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -965,9 +965,6 @@ static void tg3_ape_driver_state_change(struct tg3 *tp, int kind) event = APE_EVENT_STATUS_STATE_UNLOAD; break; - case RESET_KIND_SUSPEND: - event = APE_EVENT_STATUS_STATE_SUSPEND; - break; default: return; } @@ -1739,10 +1736,6 @@ static void tg3_write_sig_pre_reset(struct tg3 *tp, int kind) break; } } - - if (kind == RESET_KIND_INIT || - kind == RESET_KIND_SUSPEND) - tg3_ape_driver_state_change(tp, kind); } /* tp->lock is held. */ @@ -1764,9 +1757,6 @@ static void tg3_write_sig_post_reset(struct tg3 *tp, int kind) break; } } - - if (kind == RESET_KIND_SHUTDOWN) - tg3_ape_driver_state_change(tp, kind); } /* tp->lock is held. */ @@ -4206,6 +4196,8 @@ static int tg3_power_down_prepare(struct tg3 *tp) tg3_write_sig_post_reset(tp, RESET_KIND_SHUTDOWN); + tg3_ape_driver_state_change(tp, RESET_KIND_SHUTDOWN); + return 0; } @@ -11244,6 +11236,9 @@ static int tg3_start(struct tg3 *tp, bool reset_phy, bool test_irq, tg3_full_lock(tp, 0); + if (init) + tg3_ape_driver_state_change(tp, RESET_KIND_INIT); + err = tg3_init_hw(tp, reset_phy); if (err) { tg3_halt(tp, RESET_KIND_SHUTDOWN, 1); @@ -13360,11 +13355,13 @@ static void tg3_self_test(struct net_device *dev, struct ethtool_test *etest, struct tg3 *tp = netdev_priv(dev); bool doextlpbk = etest->flags & ETH_TEST_FL_EXTERNAL_LB; - if ((tp->phy_flags & TG3_PHYFLG_IS_LOW_POWER) && - tg3_power_up(tp)) { - etest->flags |= ETH_TEST_FL_FAILED; - memset(data, 1, sizeof(u64) * TG3_NUM_TEST); - return; + if (tp->phy_flags & TG3_PHYFLG_IS_LOW_POWER) { + if (tg3_power_up(tp)) { + etest->flags |= ETH_TEST_FL_FAILED; + memset(data, 1, sizeof(u64) * TG3_NUM_TEST); + return; + } + tg3_ape_driver_state_change(tp, RESET_KIND_INIT); } memset(data, 0, sizeof(u64) * TG3_NUM_TEST); @@ -17670,6 +17667,8 @@ static int tg3_resume(struct device *device) tg3_full_lock(tp, 0); + tg3_ape_driver_state_change(tp, RESET_KIND_INIT); + tg3_flag_set(tp, INIT_COMPLETE); err = tg3_restart_hw(tp, !(tp->phy_flags & TG3_PHYFLG_KEEP_LINK_ON_PWRDN)); @@ -17804,6 +17803,7 @@ static void tg3_io_resume(struct pci_dev *pdev) goto done; tg3_full_lock(tp, 0); + tg3_ape_driver_state_change(tp, RESET_KIND_INIT); tg3_flag_set(tp, INIT_COMPLETE); err = tg3_restart_hw(tp, true); if (err) { -- cgit v1.2.3 From 32ba19efc08a4bd63590310f05da5ce4d8773508 Mon Sep 17 00:00:00 2001 From: Nithin Sujir Date: Thu, 23 May 2013 11:11:23 +0000 Subject: tg3: Simplify ring control block setup The current code calls tg3_set_bdinfo() separately on napi0, followed by a loop that does napi1+. Simplify it by setting bdinfo in the loop for all napi contexts. Signed-off-by: Nithin Nayak Sujir Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/tg3.c | 79 +++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 33 deletions(-) diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index ae808b03affe..bb1d34c109f5 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -9239,6 +9239,48 @@ static void __tg3_set_coalesce(struct tg3 *tp, struct ethtool_coalesce *ec) } } +/* tp->lock is held. */ +static void tg3_tx_rcbs_init(struct tg3 *tp) +{ + int i = 0; + u32 txrcb = NIC_SRAM_SEND_RCB; + + if (tg3_flag(tp, ENABLE_TSS)) + i++; + + for (; i < tp->irq_max; i++, txrcb += TG3_BDINFO_SIZE) { + struct tg3_napi *tnapi = &tp->napi[i]; + + if (!tnapi->tx_ring) + continue; + + tg3_set_bdinfo(tp, txrcb, tnapi->tx_desc_mapping, + (TG3_TX_RING_SIZE << BDINFO_FLAGS_MAXLEN_SHIFT), + NIC_SRAM_TX_BUFFER_DESC); + } +} + +/* tp->lock is held. */ +static void tg3_rx_ret_rcbs_init(struct tg3 *tp) +{ + int i = 0; + u32 rxrcb = NIC_SRAM_RCV_RET_RCB; + + if (tg3_flag(tp, ENABLE_RSS)) + i++; + + for (; i < tp->irq_max; i++, rxrcb += TG3_BDINFO_SIZE) { + struct tg3_napi *tnapi = &tp->napi[i]; + + if (!tnapi->rx_rcb) + continue; + + tg3_set_bdinfo(tp, rxrcb, tnapi->rx_rcb_mapping, + (tp->rx_ret_ring_mask + 1) << + BDINFO_FLAGS_MAXLEN_SHIFT, 0); + } +} + /* tp->lock is held. */ static void tg3_rings_reset(struct tg3 *tp) { @@ -9315,9 +9357,6 @@ static void tg3_rings_reset(struct tg3 *tp) tw32_tx_mbox(mbox + i * 8, 0); } - txrcb = NIC_SRAM_SEND_RCB; - rxrcb = NIC_SRAM_RCV_RET_RCB; - /* Clear status block in ram. */ memset(tnapi->hw_status, 0, TG3_HW_STATUS_SIZE); @@ -9327,46 +9366,20 @@ static void tg3_rings_reset(struct tg3 *tp) tw32(HOSTCC_STATUS_BLK_HOST_ADDR + TG3_64BIT_REG_LOW, ((u64) tnapi->status_mapping & 0xffffffff)); - if (tnapi->tx_ring) { - tg3_set_bdinfo(tp, txrcb, tnapi->tx_desc_mapping, - (TG3_TX_RING_SIZE << - BDINFO_FLAGS_MAXLEN_SHIFT), - NIC_SRAM_TX_BUFFER_DESC); - txrcb += TG3_BDINFO_SIZE; - } - - if (tnapi->rx_rcb) { - tg3_set_bdinfo(tp, rxrcb, tnapi->rx_rcb_mapping, - (tp->rx_ret_ring_mask + 1) << - BDINFO_FLAGS_MAXLEN_SHIFT, 0); - rxrcb += TG3_BDINFO_SIZE; - } - stblk = HOSTCC_STATBLCK_RING1; for (i = 1, tnapi++; i < tp->irq_cnt; i++, tnapi++) { u64 mapping = (u64)tnapi->status_mapping; tw32(stblk + TG3_64BIT_REG_HIGH, mapping >> 32); tw32(stblk + TG3_64BIT_REG_LOW, mapping & 0xffffffff); + stblk += 8; /* Clear status block in ram. */ memset(tnapi->hw_status, 0, TG3_HW_STATUS_SIZE); - - if (tnapi->tx_ring) { - tg3_set_bdinfo(tp, txrcb, tnapi->tx_desc_mapping, - (TG3_TX_RING_SIZE << - BDINFO_FLAGS_MAXLEN_SHIFT), - NIC_SRAM_TX_BUFFER_DESC); - txrcb += TG3_BDINFO_SIZE; - } - - tg3_set_bdinfo(tp, rxrcb, tnapi->rx_rcb_mapping, - ((tp->rx_ret_ring_mask + 1) << - BDINFO_FLAGS_MAXLEN_SHIFT), 0); - - stblk += 8; - rxrcb += TG3_BDINFO_SIZE; } + + tg3_tx_rcbs_init(tp); + tg3_rx_ret_rcbs_init(tp); } static void tg3_setup_rxbd_thresholds(struct tg3 *tp) -- cgit v1.2.3 From 328947ff2609989fefe6dfd1143cc0787a5ec862 Mon Sep 17 00:00:00 2001 From: Nithin Sujir Date: Thu, 23 May 2013 11:11:24 +0000 Subject: tg3: Make tg3_rings_reset() more concise Simplify the rings reset function and increase readability by moving the control block disable code into separate functions. Signed-off-by: Nithin Nayak Sujir Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/tg3.c | 81 ++++++++++++++++++++++--------------- 1 file changed, 48 insertions(+), 33 deletions(-) diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index bb1d34c109f5..693c5f23c3ea 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -9239,6 +9239,28 @@ static void __tg3_set_coalesce(struct tg3 *tp, struct ethtool_coalesce *ec) } } +/* tp->lock is held. */ +static void tg3_tx_rcbs_disable(struct tg3 *tp) +{ + u32 txrcb, limit; + + /* Disable all transmit rings but the first. */ + if (!tg3_flag(tp, 5705_PLUS)) + limit = NIC_SRAM_SEND_RCB + TG3_BDINFO_SIZE * 16; + else if (tg3_flag(tp, 5717_PLUS)) + limit = NIC_SRAM_SEND_RCB + TG3_BDINFO_SIZE * 4; + else if (tg3_flag(tp, 57765_CLASS) || + tg3_asic_rev(tp) == ASIC_REV_5762) + limit = NIC_SRAM_SEND_RCB + TG3_BDINFO_SIZE * 2; + else + limit = NIC_SRAM_SEND_RCB + TG3_BDINFO_SIZE; + + for (txrcb = NIC_SRAM_SEND_RCB + TG3_BDINFO_SIZE; + txrcb < limit; txrcb += TG3_BDINFO_SIZE) + tg3_write_mem(tp, txrcb + TG3_BDINFO_MAXLEN_FLAGS, + BDINFO_FLAGS_DISABLED); +} + /* tp->lock is held. */ static void tg3_tx_rcbs_init(struct tg3 *tp) { @@ -9260,6 +9282,29 @@ static void tg3_tx_rcbs_init(struct tg3 *tp) } } +/* tp->lock is held. */ +static void tg3_rx_ret_rcbs_disable(struct tg3 *tp) +{ + u32 rxrcb, limit; + + /* Disable all receive return rings but the first. */ + if (tg3_flag(tp, 5717_PLUS)) + limit = NIC_SRAM_RCV_RET_RCB + TG3_BDINFO_SIZE * 17; + else if (!tg3_flag(tp, 5705_PLUS)) + limit = NIC_SRAM_RCV_RET_RCB + TG3_BDINFO_SIZE * 16; + else if (tg3_asic_rev(tp) == ASIC_REV_5755 || + tg3_asic_rev(tp) == ASIC_REV_5762 || + tg3_flag(tp, 57765_CLASS)) + limit = NIC_SRAM_RCV_RET_RCB + TG3_BDINFO_SIZE * 4; + else + limit = NIC_SRAM_RCV_RET_RCB + TG3_BDINFO_SIZE; + + for (rxrcb = NIC_SRAM_RCV_RET_RCB + TG3_BDINFO_SIZE; + rxrcb < limit; rxrcb += TG3_BDINFO_SIZE) + tg3_write_mem(tp, rxrcb + TG3_BDINFO_MAXLEN_FLAGS, + BDINFO_FLAGS_DISABLED); +} + /* tp->lock is held. */ static void tg3_rx_ret_rcbs_init(struct tg3 *tp) { @@ -9285,42 +9330,12 @@ static void tg3_rx_ret_rcbs_init(struct tg3 *tp) static void tg3_rings_reset(struct tg3 *tp) { int i; - u32 stblk, txrcb, rxrcb, limit; + u32 stblk; struct tg3_napi *tnapi = &tp->napi[0]; - /* Disable all transmit rings but the first. */ - if (!tg3_flag(tp, 5705_PLUS)) - limit = NIC_SRAM_SEND_RCB + TG3_BDINFO_SIZE * 16; - else if (tg3_flag(tp, 5717_PLUS)) - limit = NIC_SRAM_SEND_RCB + TG3_BDINFO_SIZE * 4; - else if (tg3_flag(tp, 57765_CLASS) || - tg3_asic_rev(tp) == ASIC_REV_5762) - limit = NIC_SRAM_SEND_RCB + TG3_BDINFO_SIZE * 2; - else - limit = NIC_SRAM_SEND_RCB + TG3_BDINFO_SIZE; - - for (txrcb = NIC_SRAM_SEND_RCB + TG3_BDINFO_SIZE; - txrcb < limit; txrcb += TG3_BDINFO_SIZE) - tg3_write_mem(tp, txrcb + TG3_BDINFO_MAXLEN_FLAGS, - BDINFO_FLAGS_DISABLED); - - - /* Disable all receive return rings but the first. */ - if (tg3_flag(tp, 5717_PLUS)) - limit = NIC_SRAM_RCV_RET_RCB + TG3_BDINFO_SIZE * 17; - else if (!tg3_flag(tp, 5705_PLUS)) - limit = NIC_SRAM_RCV_RET_RCB + TG3_BDINFO_SIZE * 16; - else if (tg3_asic_rev(tp) == ASIC_REV_5755 || - tg3_asic_rev(tp) == ASIC_REV_5762 || - tg3_flag(tp, 57765_CLASS)) - limit = NIC_SRAM_RCV_RET_RCB + TG3_BDINFO_SIZE * 4; - else - limit = NIC_SRAM_RCV_RET_RCB + TG3_BDINFO_SIZE; + tg3_tx_rcbs_disable(tp); - for (rxrcb = NIC_SRAM_RCV_RET_RCB + TG3_BDINFO_SIZE; - rxrcb < limit; rxrcb += TG3_BDINFO_SIZE) - tg3_write_mem(tp, rxrcb + TG3_BDINFO_MAXLEN_FLAGS, - BDINFO_FLAGS_DISABLED); + tg3_rx_ret_rcbs_disable(tp); /* Disable interrupts */ tw32_mailbox_f(tp->napi[0].int_mbox, 1); -- cgit v1.2.3 From 4a5f46f2fef13ecfc1f396d0caf75031a7a15422 Mon Sep 17 00:00:00 2001 From: Nithin Sujir Date: Thu, 23 May 2013 11:11:25 +0000 Subject: tg3: Use descriptive label names in tg3_start Signed-off-by: Nithin Nayak Sujir Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/tg3.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 693c5f23c3ea..6ab02d6a0eb3 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -11244,7 +11244,7 @@ static int tg3_start(struct tg3 *tp, bool reset_phy, bool test_irq, */ err = tg3_alloc_consistent(tp); if (err) - goto err_out1; + goto out_ints_fini; tg3_napi_init(tp); @@ -11258,7 +11258,7 @@ static int tg3_start(struct tg3 *tp, bool reset_phy, bool test_irq, tnapi = &tp->napi[i]; free_irq(tnapi->irq_vec, tnapi); } - goto err_out2; + goto out_napi_fini; } } @@ -11276,7 +11276,7 @@ static int tg3_start(struct tg3 *tp, bool reset_phy, bool test_irq, tg3_full_unlock(tp); if (err) - goto err_out3; + goto out_free_irq; if (test_irq && tg3_flag(tp, USING_MSI)) { err = tg3_test_msi(tp); @@ -11287,7 +11287,7 @@ static int tg3_start(struct tg3 *tp, bool reset_phy, bool test_irq, tg3_free_rings(tp); tg3_full_unlock(tp); - goto err_out2; + goto out_napi_fini; } if (!tg3_flag(tp, 57765_PLUS) && tg3_flag(tp, USING_MSI)) { @@ -11327,18 +11327,18 @@ static int tg3_start(struct tg3 *tp, bool reset_phy, bool test_irq, return 0; -err_out3: +out_free_irq: for (i = tp->irq_cnt - 1; i >= 0; i--) { struct tg3_napi *tnapi = &tp->napi[i]; free_irq(tnapi->irq_vec, tnapi); } -err_out2: +out_napi_fini: tg3_napi_disable(tp); tg3_napi_fini(tp); tg3_free_consistent(tp); -err_out1: +out_ints_fini: tg3_ints_fini(tp); return err; -- cgit v1.2.3 From 7c10ee32f32644c9efbb3cfebf5fb7cea4b48a2c Mon Sep 17 00:00:00 2001 From: Nithin Sujir Date: Thu, 23 May 2013 11:11:26 +0000 Subject: tg3: Fix misplaced empty line Signed-off-by: Nithin Nayak Sujir Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/tg3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 6ab02d6a0eb3..bd5917d8ffb5 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -1311,8 +1311,8 @@ static int tg3_phy_toggle_auxctl_smdsp(struct tg3 *tp, bool enable) if (err) return err; - if (enable) + if (enable) val |= MII_TG3_AUXCTL_ACTL_SMDSP_ENA; else val &= ~MII_TG3_AUXCTL_ACTL_SMDSP_ENA; -- cgit v1.2.3 From f2068b80ca59d63ca21ee1da829c9fe09f25b08e Mon Sep 17 00:00:00 2001 From: Nithin Sujir Date: Thu, 23 May 2013 11:11:27 +0000 Subject: tg3: Remove unnecessary lock around tg3_flag_set The spinlock was needed when flags used to be a u32 and set/cleared using bit operations. Now that we use the atomic set_bit, this lock isn't needed. Signed-off-by: Nithin Nayak Sujir Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/tg3.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index bd5917d8ffb5..d7755d849e53 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -6386,9 +6386,7 @@ static void tg3_tx_recover(struct tg3 *tp) "Please report the problem to the driver maintainer " "and include system chipset information.\n"); - spin_lock(&tp->lock); tg3_flag_set(tp, TX_RECOVERY_PENDING); - spin_unlock(&tp->lock); } static inline u32 tg3_tx_avail(struct tg3_napi *tnapi) -- cgit v1.2.3 From a80be5a58ebf29e7ca8a2915debe42295608a20a Mon Sep 17 00:00:00 2001 From: Rajesh Borundia Date: Thu, 23 May 2013 21:04:25 +0000 Subject: qlcnic: Support spoof check config. o Add support for spoof check configuration per VF using iproute2 tool. Signed-off-by: Rajesh Borundia Signed-off-by: Shahed Shaikh Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 1 + drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h | 2 ++ .../ethernet/qlogic/qlcnic/qlcnic_sriov_common.c | 1 + .../net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c | 36 ++++++++++++++++++++++ 4 files changed, 40 insertions(+) diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index aeb26a850679..1e196713c7d3 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -449,6 +449,7 @@ static const struct net_device_ops qlcnic_netdev_ops = { .ndo_set_vf_tx_rate = qlcnic_sriov_set_vf_tx_rate, .ndo_get_vf_config = qlcnic_sriov_get_vf_config, .ndo_set_vf_vlan = qlcnic_sriov_set_vf_vlan, + .ndo_set_vf_spoofchk = qlcnic_sriov_set_vf_spoofchk, #endif }; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h index d85fbb57c25b..9176cb015732 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h @@ -129,6 +129,7 @@ struct qlcnic_vport { u8 vlan_mode; u16 vlan; u8 qos; + bool spoofchk; u8 mac[6]; }; @@ -225,6 +226,7 @@ int qlcnic_sriov_set_vf_tx_rate(struct net_device *, int, int); int qlcnic_sriov_get_vf_config(struct net_device *, int , struct ifla_vf_info *); int qlcnic_sriov_set_vf_vlan(struct net_device *, int, u16, u8); +int qlcnic_sriov_set_vf_spoofchk(struct net_device *, int, bool); #else static inline void qlcnic_sriov_pf_disable(struct qlcnic_adapter *adapter) {} static inline void qlcnic_sriov_pf_cleanup(struct qlcnic_adapter *adapter) {} diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c index 196b2d100407..6262c71bc18f 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c @@ -187,6 +187,7 @@ int qlcnic_sriov_init(struct qlcnic_adapter *adapter, int num_vfs) } sriov->vf_info[i].vp = vp; vp->max_tx_bw = MAX_BW; + vp->spoofchk = true; random_ether_addr(vp->mac); dev_info(&adapter->pdev->dev, "MAC Address %pM is configured for VF %d\n", diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c index 1a66ccded235..ee0c1d307966 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c @@ -580,6 +580,7 @@ static int qlcnic_sriov_set_vf_acl(struct qlcnic_adapter *adapter, u8 func) struct qlcnic_cmd_args cmd; struct qlcnic_vport *vp; int err, id; + u8 *mac; id = qlcnic_sriov_func_to_index(adapter, func); if (id < 0) @@ -591,6 +592,14 @@ static int qlcnic_sriov_set_vf_acl(struct qlcnic_adapter *adapter, u8 func) return err; cmd.req.arg[1] = 0x3 | func << 16; + if (vp->spoofchk == true) { + mac = vp->mac; + cmd.req.arg[2] |= BIT_1 | BIT_3 | BIT_8; + cmd.req.arg[4] = mac[5] | mac[4] << 8 | mac[3] << 16 | + mac[2] << 24; + cmd.req.arg[5] = mac[1] | mac[0] << 8; + } + if (vp->vlan_mode == QLC_PVID_MODE) { cmd.req.arg[2] |= BIT_6; cmd.req.arg[3] |= vp->vlan << 8; @@ -1767,6 +1776,7 @@ int qlcnic_sriov_get_vf_config(struct net_device *netdev, memcpy(&ivi->mac, vp->mac, ETH_ALEN); ivi->vlan = vp->vlan; ivi->qos = vp->qos; + ivi->spoofchk = vp->spoofchk; if (vp->max_tx_bw == MAX_BW) ivi->tx_rate = 0; else @@ -1775,3 +1785,29 @@ int qlcnic_sriov_get_vf_config(struct net_device *netdev, ivi->vf = vf; return 0; } + +int qlcnic_sriov_set_vf_spoofchk(struct net_device *netdev, int vf, bool chk) +{ + struct qlcnic_adapter *adapter = netdev_priv(netdev); + struct qlcnic_sriov *sriov = adapter->ahw->sriov; + struct qlcnic_vf_info *vf_info; + struct qlcnic_vport *vp; + + if (!qlcnic_sriov_pf_check(adapter)) + return -EOPNOTSUPP; + + if (vf >= sriov->num_vfs) + return -EINVAL; + + vf_info = &sriov->vf_info[vf]; + vp = vf_info->vp; + if (test_bit(QLC_BC_VF_STATE, &vf_info->state)) { + netdev_err(netdev, + "Spoof check change failed for VF %d, as VF driver is loaded. Please unload VF driver and retry the operation\n", + vf); + return -EOPNOTSUPP; + } + + vp->spoofchk = chk; + return 0; +} -- cgit v1.2.3 From 45ef92ed5815883087c984356a85aa3efdb5bcab Mon Sep 17 00:00:00 2001 From: Himanshu Madhani Date: Thu, 23 May 2013 21:04:26 +0000 Subject: qlcnic: Disable INT-x interrupt for 83xx on driver unload o Set HW mask for 8300 Series adapter, in INT-x mode, to stop generating interrupt during driver unload. Signed-off-by: Himanshu Madhani Signed-off-by: Shahed Shaikh Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c index b4ff1e35a11d..9b91d6a231cb 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c @@ -312,6 +312,11 @@ inline void qlcnic_83xx_clear_legacy_intr_mask(struct qlcnic_adapter *adapter) writel(0, adapter->tgt_mask_reg); } +inline void qlcnic_83xx_set_legacy_intr_mask(struct qlcnic_adapter *adapter) +{ + writel(1, adapter->tgt_mask_reg); +} + /* Enable MSI-x and INT-x interrupts */ void qlcnic_83xx_enable_intr(struct qlcnic_adapter *adapter, struct qlcnic_host_sds_ring *sds_ring) @@ -458,6 +463,9 @@ void qlcnic_83xx_free_mbx_intr(struct qlcnic_adapter *adapter) { u32 num_msix; + if (!(adapter->flags & QLCNIC_MSIX_ENABLED)) + qlcnic_83xx_set_legacy_intr_mask(adapter); + qlcnic_83xx_disable_mbx_intr(adapter); if (adapter->flags & QLCNIC_MSIX_ENABLED) -- cgit v1.2.3 From aa2a80340ce0b4a148ae8878142d17bc793ba2ab Mon Sep 17 00:00:00 2001 From: Himanshu Madhani Date: Thu, 23 May 2013 21:04:27 +0000 Subject: qlcnic: Update IRQ name for 8200 and 8300 Series adapter. o Updated IRQ name for 8200 and 8300 Series adapter as per format used by other multiqueue drivers. Signed-off-by: Himanshu Madhani Signed-off-by: Shahed Shaikh Signed-off-by: David S. Miller --- .../net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c | 5 +---- drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 23 ++++++++++++++-------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c index 9b91d6a231cb..9f5683fda801 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c @@ -482,7 +482,6 @@ int qlcnic_83xx_setup_mbx_intr(struct qlcnic_adapter *adapter) { irq_handler_t handler; u32 val; - char name[32]; int err = 0; unsigned long flags = 0; @@ -493,9 +492,7 @@ int qlcnic_83xx_setup_mbx_intr(struct qlcnic_adapter *adapter) if (adapter->flags & QLCNIC_MSIX_ENABLED) { handler = qlcnic_83xx_handle_aen; val = adapter->msix_entries[adapter->ahw->num_msix - 1].vector; - snprintf(name, (IFNAMSIZ + 4), - "%s[%s]", "qlcnic", "aen"); - err = request_irq(val, handler, flags, name, adapter); + err = request_irq(val, handler, flags, "qlcnic-MB", adapter); if (err) { dev_err(&adapter->pdev->dev, "failed to register MBX interrupt\n"); diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index 1e196713c7d3..76b95b48c815 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -1396,16 +1396,23 @@ qlcnic_request_irq(struct qlcnic_adapter *adapter) for (ring = 0; ring < num_sds_rings; ring++) { sds_ring = &recv_ctx->sds_rings[ring]; if (qlcnic_82xx_check(adapter) && - (ring == (num_sds_rings - 1))) + (ring == (num_sds_rings - 1))) { + if (!(adapter->flags & + QLCNIC_MSIX_ENABLED)) + snprintf(sds_ring->name, + sizeof(sds_ring->name), + "qlcnic"); + else + snprintf(sds_ring->name, + sizeof(sds_ring->name), + "%s-tx-0-rx-%d", + netdev->name, ring); + } else { snprintf(sds_ring->name, sizeof(sds_ring->name), - "qlcnic-%s[Tx0+Rx%d]", - netdev->name, ring); - else - snprintf(sds_ring->name, - sizeof(sds_ring->name), - "qlcnic-%s[Rx%d]", + "%s-rx-%d", netdev->name, ring); + } err = request_irq(sds_ring->irq, handler, flags, sds_ring->name, sds_ring); if (err) @@ -1420,7 +1427,7 @@ qlcnic_request_irq(struct qlcnic_adapter *adapter) ring++) { tx_ring = &adapter->tx_ring[ring]; snprintf(tx_ring->name, sizeof(tx_ring->name), - "qlcnic-%s[Tx%d]", netdev->name, ring); + "%s-tx-%d", netdev->name, ring); err = request_irq(tx_ring->irq, handler, flags, tx_ring->name, tx_ring); if (err) -- cgit v1.2.3 From 7e8fd003c96dda2920a4ba0c32d54780d15dfefc Mon Sep 17 00:00:00 2001 From: Shahed Shaikh Date: Thu, 23 May 2013 21:04:28 +0000 Subject: qlcnic: Remove qlcnic_config_npars module parameter qlcnic_config_npars module parameter is used to configure NPAR operational modes where NPAR function is used for PCI passthrough. Removing this paramter as PCI passthrough is not supported for NPAR functions. Signed-off-by: Shahed Shaikh Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qlcnic/qlcnic.h | 1 - .../net/ethernet/qlogic/qlcnic/qlcnic_83xx_vnic.c | 21 ++++++------------- drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 24 +++++----------------- 3 files changed, 11 insertions(+), 35 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h index c1b693cb3df3..60f310a542da 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h @@ -303,7 +303,6 @@ extern int qlcnic_use_msi; extern int qlcnic_use_msi_x; extern int qlcnic_auto_fw_reset; extern int qlcnic_load_fw_file; -extern int qlcnic_config_npars; /* Number of status descriptors to handle per interrupt */ #define MAX_STATUS_HANDLE (64) diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_vnic.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_vnic.c index b0c3de9ede03..55e5e1b5ad4f 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_vnic.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_vnic.c @@ -42,27 +42,18 @@ int qlcnic_83xx_disable_vnic_mode(struct qlcnic_adapter *adapter, int lock) static int qlcnic_83xx_set_vnic_opmode(struct qlcnic_adapter *adapter) { u8 id; - int i, ret = -EBUSY; + int ret = -EBUSY; u32 data = QLCNIC_MGMT_FUNC; struct qlcnic_hardware_context *ahw = adapter->ahw; if (qlcnic_83xx_lock_driver(adapter)) return ret; - if (qlcnic_config_npars) { - for (i = 0; i < ahw->act_pci_func; i++) { - id = adapter->npars[i].pci_func; - if (id == ahw->pci_func) - continue; - data |= qlcnic_config_npars & - QLC_83XX_SET_FUNC_OPMODE(0x3, id); - } - } else { - data = QLCRDX(adapter->ahw, QLC_83XX_DRV_OP_MODE); - data = (data & ~QLC_83XX_SET_FUNC_OPMODE(0x3, ahw->pci_func)) | - QLC_83XX_SET_FUNC_OPMODE(QLCNIC_MGMT_FUNC, - ahw->pci_func); - } + id = ahw->pci_func; + data = QLCRDX(adapter->ahw, QLC_83XX_DRV_OP_MODE); + data = (data & ~QLC_83XX_SET_FUNC_OPMODE(0x3, id)) | + QLC_83XX_SET_FUNC_OPMODE(QLCNIC_MGMT_FUNC, id); + QLCWRX(adapter->ahw, QLC_83XX_DRV_OP_MODE, data); qlcnic_83xx_unlock_driver(adapter); diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index 76b95b48c815..5b18b0accb06 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -52,10 +52,6 @@ int qlcnic_load_fw_file; MODULE_PARM_DESC(load_fw_file, "Load firmware from (0=flash, 1=file)"); module_param_named(load_fw_file, qlcnic_load_fw_file, int, 0444); -int qlcnic_config_npars; -module_param(qlcnic_config_npars, int, 0444); -MODULE_PARM_DESC(qlcnic_config_npars, "Configure NPARs (0=disabled, 1=enabled)"); - static int qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent); static void qlcnic_remove(struct pci_dev *pdev); static int qlcnic_open(struct net_device *netdev); @@ -769,7 +765,7 @@ static int qlcnic_set_function_modes(struct qlcnic_adapter *adapter) { u8 id; - int i, ret = 1; + int ret; u32 data = QLCNIC_MGMT_FUNC; struct qlcnic_hardware_context *ahw = adapter->ahw; @@ -777,20 +773,10 @@ qlcnic_set_function_modes(struct qlcnic_adapter *adapter) if (ret) goto err_lock; - if (qlcnic_config_npars) { - for (i = 0; i < ahw->act_pci_func; i++) { - id = adapter->npars[i].pci_func; - if (id == ahw->pci_func) - continue; - data |= (qlcnic_config_npars & - QLC_DEV_SET_DRV(0xf, id)); - } - } else { - data = QLC_SHARED_REG_RD32(adapter, QLCNIC_DRV_OP_MODE); - data = (data & ~QLC_DEV_SET_DRV(0xf, ahw->pci_func)) | - (QLC_DEV_SET_DRV(QLCNIC_MGMT_FUNC, - ahw->pci_func)); - } + id = ahw->pci_func; + data = QLC_SHARED_REG_RD32(adapter, QLCNIC_DRV_OP_MODE); + data = (data & ~QLC_DEV_SET_DRV(0xf, id)) | + QLC_DEV_SET_DRV(QLCNIC_MGMT_FUNC, id); QLC_SHARED_REG_WR32(adapter, QLCNIC_DRV_OP_MODE, data); qlcnic_api_unlock(adapter); err_lock: -- cgit v1.2.3 From 1267ff962fc04d33d30b52c5c09201df2b1c1d76 Mon Sep 17 00:00:00 2001 From: Sucheta Chakraborty Date: Thu, 23 May 2013 21:04:29 +0000 Subject: qlcnic: Initialize trans_work and idc_aen_work at VF probe. o work_struct should be initialized before cancel_delayed_work call to destroy it. Signed-off-by: Sucheta Chakraborty Signed-off-by: Shahed Shaikh Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c index 6262c71bc18f..bcd200eff981 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c @@ -35,6 +35,7 @@ static void qlcnic_sriov_vf_cancel_fw_work(struct qlcnic_adapter *); static void qlcnic_sriov_cleanup_transaction(struct qlcnic_bc_trans *); static int qlcnic_sriov_vf_mbx_op(struct qlcnic_adapter *, struct qlcnic_cmd_args *); +static void qlcnic_sriov_process_bc_cmd(struct work_struct *); static struct qlcnic_hardware_ops qlcnic_sriov_vf_hw_ops = { .read_crb = qlcnic_83xx_read_crb, @@ -179,6 +180,8 @@ int qlcnic_sriov_init(struct qlcnic_adapter *adapter, int num_vfs) spin_lock_init(&vf->rcv_pend.lock); init_completion(&vf->ch_free_cmpl); + INIT_WORK(&vf->trans_work, qlcnic_sriov_process_bc_cmd); + if (qlcnic_sriov_pf_check(adapter)) { vp = kzalloc(sizeof(struct qlcnic_vport), GFP_KERNEL); if (!vp) { @@ -653,6 +656,8 @@ int qlcnic_sriov_vf_init(struct qlcnic_adapter *adapter, int pci_using_dac) if (qlcnic_read_mac_addr(adapter)) dev_warn(&adapter->pdev->dev, "failed to read mac addr\n"); + INIT_DELAYED_WORK(&adapter->idc_aen_work, qlcnic_83xx_idc_aen_work); + clear_bit(__QLCNIC_RESETTING, &adapter->state); return 0; } @@ -865,7 +870,6 @@ static void qlcnic_sriov_schedule_bc_cmd(struct qlcnic_sriov *sriov, vf->adapter->need_fw_reset) return; - INIT_WORK(&vf->trans_work, func); queue_work(sriov->bc.bc_trans_wq, &vf->trans_work); } -- cgit v1.2.3 From b17f2ccaeea10f30322b9706d8d9b5b39d21f427 Mon Sep 17 00:00:00 2001 From: Jitendra Kalsaria Date: Thu, 23 May 2013 21:04:30 +0000 Subject: qlcnic: Convert nested if-else to switch-case Signed-off-by: Jitendra Kalsaria Signed-off-by: Shahed Shaikh Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_vnic.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_vnic.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_vnic.c index 55e5e1b5ad4f..b5054e1d1710 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_vnic.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_vnic.c @@ -187,20 +187,24 @@ int qlcnic_83xx_config_vnic_opmode(struct qlcnic_adapter *adapter) else priv_level = QLC_83XX_GET_FUNC_PRIVILEGE(op_mode, ahw->pci_func); - - if (priv_level == QLCNIC_NON_PRIV_FUNC) { + switch (priv_level) { + case QLCNIC_NON_PRIV_FUNC: ahw->op_mode = QLCNIC_NON_PRIV_FUNC; ahw->idc.state_entry = qlcnic_83xx_idc_ready_state_entry; nic_ops->init_driver = qlcnic_83xx_init_non_privileged_vnic; - } else if (priv_level == QLCNIC_PRIV_FUNC) { + break; + case QLCNIC_PRIV_FUNC: ahw->op_mode = QLCNIC_PRIV_FUNC; ahw->idc.state_entry = qlcnic_83xx_idc_vnic_pf_entry; nic_ops->init_driver = qlcnic_83xx_init_privileged_vnic; - } else if (priv_level == QLCNIC_MGMT_FUNC) { + break; + case QLCNIC_MGMT_FUNC: ahw->op_mode = QLCNIC_MGMT_FUNC; ahw->idc.state_entry = qlcnic_83xx_idc_ready_state_entry; nic_ops->init_driver = qlcnic_83xx_init_mgmt_vnic; - } else { + break; + default: + dev_err(&adapter->pdev->dev, "Invalid Virtual NIC opmode\n"); return -EIO; } @@ -209,8 +213,8 @@ int qlcnic_83xx_config_vnic_opmode(struct qlcnic_adapter *adapter) else adapter->flags &= ~QLCNIC_ESWITCH_ENABLED; - adapter->ahw->idc.vnic_state = QLCNIC_DEV_NPAR_NON_OPER; - adapter->ahw->idc.vnic_wait_limit = QLCNIC_DEV_NPAR_OPER_TIMEO; + ahw->idc.vnic_state = QLCNIC_DEV_NPAR_NON_OPER; + ahw->idc.vnic_wait_limit = QLCNIC_DEV_NPAR_OPER_TIMEO; return 0; } -- cgit v1.2.3 From 4690a7e48ac55cf84fcf214279baf133304d71e4 Mon Sep 17 00:00:00 2001 From: Sony Chacko Date: Thu, 23 May 2013 21:04:31 +0000 Subject: qlcnic: diagnostics routine changes Test and set diagnostics mode bit before starting diagnostics tests. Stop diagnostics tests if adapter is resetting. Signed-off-by: Sony Chacko Signed-off-by: Shahed Shaikh Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qlcnic/qlcnic.h | 12 ++++++++++ .../net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c | 28 +++++++++++++++------- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h index 60f310a542da..8affec5e6eb8 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h @@ -931,6 +931,7 @@ struct qlcnic_ipaddr { #define __QLCNIC_SRIOV_ENABLE 10 #define __QLCNIC_SRIOV_CAPABLE 11 #define __QLCNIC_MBX_POLL_ENABLE 12 +#define __QLCNIC_DIAG_MODE 13 #define QLCNIC_INTERRUPT_TEST 1 #define QLCNIC_LOOPBACK_TEST 2 @@ -1476,6 +1477,7 @@ int qlcnic_send_lro_cleanup(struct qlcnic_adapter *adapter); void qlcnic_update_cmd_producer(struct qlcnic_host_tx_ring *); /* Functions from qlcnic_ethtool.c */ + int qlcnic_check_loopback_buff(unsigned char *, u8 []); int qlcnic_do_lb_test(struct qlcnic_adapter *, u8); int qlcnic_loopback_test(struct net_device *, u8); @@ -1885,6 +1887,16 @@ static inline void qlcnic_enable_int(struct qlcnic_host_sds_ring *sds_ring) writel(0xfbff, adapter->tgt_mask_reg); } +static inline int qlcnic_get_diag_lock(struct qlcnic_adapter *adapter) +{ + return test_and_set_bit(__QLCNIC_DIAG_MODE, &adapter->state); +} + +static inline void qlcnic_release_diag_lock(struct qlcnic_adapter *adapter) +{ + clear_bit(__QLCNIC_DIAG_MODE, &adapter->state); +} + extern const struct ethtool_ops qlcnic_sriov_vf_ethtool_ops; extern const struct ethtool_ops qlcnic_ethtool_ops; extern const struct ethtool_ops qlcnic_ethtool_failed_ops; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c index 9f5683fda801..1e699e6f29c8 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c @@ -1593,16 +1593,24 @@ int qlcnic_83xx_loopback_test(struct net_device *netdev, u8 mode) struct qlcnic_hardware_context *ahw = adapter->ahw; int ret = 0, loop = 0, max_sds_rings = adapter->max_sds_rings; - QLCDB(adapter, DRV, "%s loopback test in progress\n", - mode == QLCNIC_ILB_MODE ? "internal" : "external"); if (ahw->op_mode == QLCNIC_NON_PRIV_FUNC) { - dev_warn(&adapter->pdev->dev, - "Loopback test not supported for non privilege function\n"); + netdev_warn(netdev, + "Loopback test not supported in non privileged mode\n"); return ret; } - if (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state)) + if (test_bit(__QLCNIC_RESETTING, &adapter->state)) { + netdev_info(netdev, "Device is resetting\n"); return -EBUSY; + } + + if (qlcnic_get_diag_lock(adapter)) { + netdev_info(netdev, "Device is in diagnostics mode\n"); + return -EBUSY; + } + + netdev_info(netdev, "%s loopback test in progress\n", + mode == QLCNIC_ILB_MODE ? "internal" : "external"); ret = qlcnic_83xx_diag_alloc_res(netdev, QLCNIC_LOOPBACK_TEST, max_sds_rings); @@ -1643,7 +1651,7 @@ free_diag_res: fail_diag_alloc: adapter->max_sds_rings = max_sds_rings; - clear_bit(__QLCNIC_RESETTING, &adapter->state); + qlcnic_release_diag_lock(adapter); return ret; } @@ -3118,8 +3126,10 @@ int qlcnic_83xx_interrupt_test(struct net_device *netdev) u8 val; int ret, max_sds_rings = adapter->max_sds_rings; - if (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state)) - return -EIO; + if (qlcnic_get_diag_lock(adapter)) { + netdev_info(netdev, "Device in diagnostics mode\n"); + return -EBUSY; + } ret = qlcnic_83xx_diag_alloc_res(netdev, QLCNIC_INTERRUPT_TEST, max_sds_rings); @@ -3161,7 +3171,7 @@ done: fail_diag_irq: adapter->max_sds_rings = max_sds_rings; - clear_bit(__QLCNIC_RESETTING, &adapter->state); + qlcnic_release_diag_lock(adapter); return ret; } -- cgit v1.2.3 From 099907fac61c47a67a02edb85d551e91959ecc5b Mon Sep 17 00:00:00 2001 From: Sony Chacko Date: Thu, 23 May 2013 21:04:32 +0000 Subject: qlcnic: modify reset recovery path in diag mode Provide diagnostics routines enough time to unwind before proceeding with reset recovery. Signed-off-by: Sony Chacko Signed-off-by: Shahed Shaikh Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qlcnic/qlcnic.h | 6 +++- .../net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h | 1 + .../net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c | 35 +++++++++++++++++----- 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h index 8affec5e6eb8..4ff0625a0cef 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h @@ -1477,7 +1477,6 @@ int qlcnic_send_lro_cleanup(struct qlcnic_adapter *adapter); void qlcnic_update_cmd_producer(struct qlcnic_host_tx_ring *); /* Functions from qlcnic_ethtool.c */ - int qlcnic_check_loopback_buff(unsigned char *, u8 []); int qlcnic_do_lb_test(struct qlcnic_adapter *, u8); int qlcnic_loopback_test(struct net_device *, u8); @@ -1897,6 +1896,11 @@ static inline void qlcnic_release_diag_lock(struct qlcnic_adapter *adapter) clear_bit(__QLCNIC_DIAG_MODE, &adapter->state); } +static inline int qlcnic_check_diag_status(struct qlcnic_adapter *adapter) +{ + return test_bit(__QLCNIC_DIAG_MODE, &adapter->state); +} + extern const struct ethtool_ops qlcnic_sriov_vf_ethtool_ops; extern const struct ethtool_ops qlcnic_ethtool_ops; extern const struct ethtool_ops qlcnic_ethtool_failed_ops; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h index f5db67fc9f55..1bfe283a9412 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h @@ -314,6 +314,7 @@ struct qlc_83xx_idc { u8 vnic_state; u8 vnic_wait_limit; u8 quiesce_req; + u8 delay_reset; char **name; }; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c index 5e7fb1dfb97b..aa26250d7374 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c @@ -649,6 +649,7 @@ static void qlcnic_83xx_idc_update_idc_params(struct qlcnic_adapter *adapter) ahw->idc.collect_dump = 0; ahw->reset_context = 0; adapter->tx_timeo_cnt = 0; + ahw->idc.delay_reset = 0; clear_bit(__QLCNIC_RESETTING, &adapter->state); } @@ -883,21 +884,41 @@ static int qlcnic_83xx_idc_need_reset_state(struct qlcnic_adapter *adapter) int ret = 0; if (adapter->ahw->idc.prev_state != QLC_83XX_IDC_DEV_NEED_RESET) { - qlcnic_83xx_idc_update_drv_ack_reg(adapter, 1, 1); qlcnic_83xx_idc_update_audit_reg(adapter, 0, 1); set_bit(__QLCNIC_RESETTING, &adapter->state); clear_bit(QLC_83XX_MBX_READY, &adapter->ahw->idc.status); if (adapter->ahw->nic_mode == QLC_83XX_VIRTUAL_NIC_MODE) qlcnic_83xx_disable_vnic_mode(adapter, 1); - qlcnic_83xx_idc_detach_driver(adapter); + + if (qlcnic_check_diag_status(adapter)) { + dev_info(&adapter->pdev->dev, + "%s: Wait for diag completion\n", __func__); + adapter->ahw->idc.delay_reset = 1; + return 0; + } else { + qlcnic_83xx_idc_update_drv_ack_reg(adapter, 1, 1); + qlcnic_83xx_idc_detach_driver(adapter); + } } - /* Check ACK from other functions */ - ret = qlcnic_83xx_idc_check_reset_ack_reg(adapter); - if (ret) { + if (qlcnic_check_diag_status(adapter)) { dev_info(&adapter->pdev->dev, - "%s: Waiting for reset ACK\n", __func__); - return 0; + "%s: Wait for diag completion\n", __func__); + return -1; + } else { + if (adapter->ahw->idc.delay_reset) { + qlcnic_83xx_idc_update_drv_ack_reg(adapter, 1, 1); + qlcnic_83xx_idc_detach_driver(adapter); + adapter->ahw->idc.delay_reset = 0; + } + + /* Check for ACK from other functions */ + ret = qlcnic_83xx_idc_check_reset_ack_reg(adapter); + if (ret) { + dev_info(&adapter->pdev->dev, + "%s: Waiting for reset ACK\n", __func__); + return -1; + } } /* Transit to INIT state and restart the HW */ -- cgit v1.2.3 From 487042af928c16d2a5b4dd52a9639a03b8b40761 Mon Sep 17 00:00:00 2001 From: Himanshu Madhani Date: Thu, 23 May 2013 21:04:33 +0000 Subject: qlcnic: Implement GET_LED_STATUS command for 82xx adapter. o GET_LED_STATUS command will be used by driver to get current beacon state from 82xx adapter. o Refactored qlcnic_store_beacon() to split 8200 and 8300 specific calls. Signed-off-by: Himanshu Madhani Signed-off-by: Shahed Shaikh Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qlcnic/qlcnic.h | 5 + drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c | 1 + drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c | 15 +++ drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h | 1 + drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c | 122 ++++++++++++++-------- 5 files changed, 98 insertions(+), 46 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h index 4ff0625a0cef..e6f8beccbf3e 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h @@ -815,6 +815,7 @@ struct qlcnic_mac_list_s { #define QLCNIC_FW_CAPABILITY_2_LRO_MAX_TCP_SEG BIT_2 #define QLCNIC_FW_CAP2_HW_LRO_IPV6 BIT_3 #define QLCNIC_FW_CAPABILITY_2_OCBB BIT_5 +#define QLCNIC_FW_CAPABILITY_2_BEACON BIT_7 /* module types */ #define LINKEVENT_MODULE_NOT_PRESENT 1 @@ -912,6 +913,9 @@ struct qlcnic_ipaddr { #define QLCNIC_IS_TSO_CAPABLE(adapter) \ ((adapter)->ahw->capabilities & QLCNIC_FW_CAPABILITY_TSO) +#define QLCNIC_BEACON_EANBLE 0xC +#define QLCNIC_BEACON_DISABLE 0xD + #define QLCNIC_DEF_NUM_STS_DESC_RINGS 4 #define QLCNIC_MSIX_TBL_SPACE 8192 #define QLCNIC_PCI_REG_MSIX_TBL 0x44 @@ -1543,6 +1547,7 @@ int qlcnic_set_default_offload_settings(struct qlcnic_adapter *); int qlcnic_reset_npar_config(struct qlcnic_adapter *); int qlcnic_set_eswitch_port_config(struct qlcnic_adapter *); void qlcnic_add_lb_filter(struct qlcnic_adapter *, struct sk_buff *, int, u16); +int qlcnic_get_beacon_state(struct qlcnic_adapter *, u8 *); int qlcnic_83xx_configure_opmode(struct qlcnic_adapter *adapter); int qlcnic_read_mac_addr(struct qlcnic_adapter *); int qlcnic_setup_netdev(struct qlcnic_adapter *, struct net_device *, int); diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c index 43562c256379..d63b5f728d75 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c @@ -37,6 +37,7 @@ static const struct qlcnic_mailbox_metadata qlcnic_mbx_tbl[] = { {QLCNIC_CMD_TEMP_SIZE, 4, 4}, {QLCNIC_CMD_GET_TEMP_HDR, 4, 1}, {QLCNIC_CMD_SET_DRV_VER, 4, 1}, + {QLCNIC_CMD_GET_LED_STATUS, 4, 2}, }; static inline u32 qlcnic_get_cmd_signature(struct qlcnic_hardware_context *ahw) diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c index 106a12f2a02f..218978db2963 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c @@ -1503,6 +1503,21 @@ int qlcnic_82xx_config_led(struct qlcnic_adapter *adapter, u32 state, u32 rate) return rv; } +int qlcnic_get_beacon_state(struct qlcnic_adapter *adapter, u8 *h_state) +{ + struct qlcnic_cmd_args cmd; + int err; + + err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_LED_STATUS); + if (!err) { + err = qlcnic_issue_cmd(adapter, &cmd); + if (!err) + *h_state = cmd.rsp.arg[1]; + } + qlcnic_free_mbx_args(&cmd); + return err; +} + void qlcnic_82xx_get_func_no(struct qlcnic_adapter *adapter) { void __iomem *msix_base_addr; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h index b6818f4356b9..812fd07baef3 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h @@ -87,6 +87,7 @@ enum qlcnic_regs { #define QLCNIC_CMD_CONFIG_VPORT 0x32 #define QLCNIC_CMD_GET_MAC_STATS 0x37 #define QLCNIC_CMD_SET_DRV_VER 0x38 +#define QLCNIC_CMD_GET_LED_STATUS 0x3C #define QLCNIC_CMD_CONFIGURE_RSS 0x41 #define QLCNIC_CMD_CONFIG_INTR_COAL 0x43 #define QLCNIC_CMD_CONFIGURE_LED 0x44 diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c index e7a2fe21b649..025a7192289d 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c @@ -114,57 +114,51 @@ static int qlcnic_validate_beacon(struct qlcnic_adapter *adapter, u16 beacon, return 0; } -static ssize_t qlcnic_store_beacon(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) +static int qlcnic_83xx_store_beacon(struct qlcnic_adapter *adapter, + const char *buf, size_t len) { - struct qlcnic_adapter *adapter = dev_get_drvdata(dev); struct qlcnic_hardware_context *ahw = adapter->ahw; - int err, max_sds_rings = adapter->max_sds_rings; - u16 beacon; - u8 b_state, b_rate; unsigned long h_beacon; + int err; - if (adapter->ahw->op_mode == QLCNIC_NON_PRIV_FUNC) { - dev_warn(dev, - "LED test not supported in non privileged mode\n"); - return -EOPNOTSUPP; - } + if (test_bit(__QLCNIC_RESETTING, &adapter->state)) + return -EIO; - if (qlcnic_83xx_check(adapter) && - !test_bit(__QLCNIC_RESETTING, &adapter->state)) { - if (kstrtoul(buf, 2, &h_beacon)) - return -EINVAL; + if (kstrtoul(buf, 2, &h_beacon)) + return -EINVAL; - if (ahw->beacon_state == h_beacon) - return len; + if (ahw->beacon_state == h_beacon) + return len; - rtnl_lock(); - if (!ahw->beacon_state) { - if (test_and_set_bit(__QLCNIC_LED_ENABLE, - &adapter->state)) { - rtnl_unlock(); - return -EBUSY; - } - } - if (h_beacon) { - err = qlcnic_83xx_config_led(adapter, 1, h_beacon); - if (err) - goto beacon_err; - } else { - err = qlcnic_83xx_config_led(adapter, 0, !h_beacon); - if (err) - goto beacon_err; + rtnl_lock(); + if (!ahw->beacon_state) { + if (test_and_set_bit(__QLCNIC_LED_ENABLE, &adapter->state)) { + rtnl_unlock(); + return -EBUSY; } - /* set the current beacon state */ + } + + if (h_beacon) + err = qlcnic_83xx_config_led(adapter, 1, h_beacon); + else + err = qlcnic_83xx_config_led(adapter, 0, !h_beacon); + if (!err) ahw->beacon_state = h_beacon; -beacon_err: - if (!ahw->beacon_state) - clear_bit(__QLCNIC_LED_ENABLE, &adapter->state); - rtnl_unlock(); - return len; - } + if (!ahw->beacon_state) + clear_bit(__QLCNIC_LED_ENABLE, &adapter->state); + + rtnl_unlock(); + return len; +} + +static int qlcnic_82xx_store_beacon(struct qlcnic_adapter *adapter, + const char *buf, size_t len) +{ + struct qlcnic_hardware_context *ahw = adapter->ahw; + int err, max_sds_rings = adapter->max_sds_rings; + u16 beacon; + u8 h_beacon_state, b_state, b_rate; if (len != sizeof(u16)) return QL_STATUS_INVALID_PARAM; @@ -174,16 +168,29 @@ beacon_err: if (err) return err; - if (adapter->ahw->beacon_state == b_state) + if ((ahw->capabilities2 & QLCNIC_FW_CAPABILITY_2_BEACON)) { + err = qlcnic_get_beacon_state(adapter, &h_beacon_state); + if (!err) { + dev_info(&adapter->pdev->dev, + "Failed to get current beacon state\n"); + } else { + if (h_beacon_state == QLCNIC_BEACON_DISABLE) + ahw->beacon_state = 0; + else if (h_beacon_state == QLCNIC_BEACON_EANBLE) + ahw->beacon_state = 2; + } + } + + if (ahw->beacon_state == b_state) return len; rtnl_lock(); - - if (!adapter->ahw->beacon_state) + if (!ahw->beacon_state) { if (test_and_set_bit(__QLCNIC_LED_ENABLE, &adapter->state)) { rtnl_unlock(); return -EBUSY; } + } if (test_bit(__QLCNIC_RESETTING, &adapter->state)) { err = -EIO; @@ -206,14 +213,37 @@ beacon_err: if (test_and_clear_bit(__QLCNIC_DIAG_RES_ALLOC, &adapter->state)) qlcnic_diag_free_res(adapter->netdev, max_sds_rings); - out: - if (!adapter->ahw->beacon_state) +out: + if (!ahw->beacon_state) clear_bit(__QLCNIC_LED_ENABLE, &adapter->state); rtnl_unlock(); return err; } +static ssize_t qlcnic_store_beacon(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct qlcnic_adapter *adapter = dev_get_drvdata(dev); + int err = 0; + + if (adapter->ahw->op_mode == QLCNIC_NON_PRIV_FUNC) { + dev_warn(dev, + "LED test not supported in non privileged mode\n"); + return -EOPNOTSUPP; + } + + if (qlcnic_82xx_check(adapter)) + err = qlcnic_82xx_store_beacon(adapter, buf, len); + else if (qlcnic_83xx_check(adapter)) + err = qlcnic_83xx_store_beacon(adapter, buf, len); + else + return -EIO; + + return err; +} + static ssize_t qlcnic_show_beacon(struct device *dev, struct device_attribute *attr, char *buf) { -- cgit v1.2.3 From 2343f06a8c9549af27b885d36e26c59771d697f1 Mon Sep 17 00:00:00 2001 From: Manish Chopra Date: Thu, 23 May 2013 21:04:34 +0000 Subject: qlcnic: qlcnic_get_board_name() function cleanup Signed-off-by: Manish Chopra Signed-off-by: Shahed Shaikh Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 37 ++++++++++++++++++------ 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index 5b18b0accb06..da82f2eb73b4 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -862,6 +862,27 @@ static int qlcnic_setup_pci_map(struct pci_dev *pdev, return 0; } +static inline bool qlcnic_validate_subsystem_id(struct qlcnic_adapter *adapter, + int index) +{ + struct pci_dev *pdev = adapter->pdev; + unsigned short subsystem_vendor; + bool ret = true; + + subsystem_vendor = pdev->subsystem_vendor; + + if (pdev->device == PCI_DEVICE_ID_QLOGIC_QLE824X || + pdev->device == PCI_DEVICE_ID_QLOGIC_QLE834X) { + if (qlcnic_boards[index].sub_vendor == subsystem_vendor && + qlcnic_boards[index].sub_device == pdev->subsystem_device) + ret = true; + else + ret = false; + } + + return ret; +} + static void qlcnic_get_board_name(struct qlcnic_adapter *adapter, char *name) { struct pci_dev *pdev = adapter->pdev; @@ -869,20 +890,18 @@ static void qlcnic_get_board_name(struct qlcnic_adapter *adapter, char *name) for (i = 0; i < NUM_SUPPORTED_BOARDS; ++i) { if (qlcnic_boards[i].vendor == pdev->vendor && - qlcnic_boards[i].device == pdev->device && - qlcnic_boards[i].sub_vendor == pdev->subsystem_vendor && - qlcnic_boards[i].sub_device == pdev->subsystem_device) { - sprintf(name, "%pM: %s" , - adapter->mac_addr, - qlcnic_boards[i].short_name); - found = 1; - break; + qlcnic_boards[i].device == pdev->device && + qlcnic_validate_subsystem_id(adapter, i)) { + found = 1; + break; } - } if (!found) sprintf(name, "%pM Gigabit Ethernet", adapter->mac_addr); + else + sprintf(name, "%pM: %s" , adapter->mac_addr, + qlcnic_boards[i].short_name); } static void -- cgit v1.2.3 From ee9e8b6c6e167e2400efd26b92b300ff1d3f8e8b Mon Sep 17 00:00:00 2001 From: Manish Chopra Date: Thu, 23 May 2013 21:04:35 +0000 Subject: qlcnic: Enhance virtual NIC logging Signed-off-by: Manish Chopra Signed-off-by: Shahed Shaikh Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qlcnic/qlcnic.h | 1 + .../net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c | 36 +++++++++--------- drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c | 44 +++++++++++----------- 3 files changed, 41 insertions(+), 40 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h index e6f8beccbf3e..00a09573faab 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h @@ -442,6 +442,7 @@ struct qlcnic_hardware_context { u16 max_mtu; u32 msg_enable; u16 act_pci_func; + u16 max_pci_func; u32 capabilities; u32 capabilities2; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c index 1e699e6f29c8..f63a69570256 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c @@ -2134,26 +2134,25 @@ out: int qlcnic_83xx_get_pci_info(struct qlcnic_adapter *adapter, struct qlcnic_pci_info *pci_info) { + struct qlcnic_hardware_context *ahw = adapter->ahw; + struct device *dev = &adapter->pdev->dev; + struct qlcnic_cmd_args cmd; int i, err = 0, j = 0; u32 temp; - struct qlcnic_cmd_args cmd; qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_PCI_INFO); err = qlcnic_issue_cmd(adapter, &cmd); - adapter->ahw->act_pci_func = 0; + ahw->act_pci_func = 0; if (err == QLCNIC_RCODE_SUCCESS) { - pci_info->func_count = cmd.rsp.arg[1] & 0xFF; - dev_info(&adapter->pdev->dev, - "%s: total functions = %d\n", - __func__, pci_info->func_count); + ahw->max_pci_func = cmd.rsp.arg[1] & 0xFF; for (i = 2, j = 0; j < QLCNIC_MAX_PCI_FUNC; j++, pci_info++) { pci_info->id = cmd.rsp.arg[i] & 0xFFFF; pci_info->active = (cmd.rsp.arg[i] & 0xFFFF0000) >> 16; i++; pci_info->type = cmd.rsp.arg[i] & 0xFFFF; if (pci_info->type == QLCNIC_TYPE_NIC) - adapter->ahw->act_pci_func++; + ahw->act_pci_func++; temp = (cmd.rsp.arg[i] & 0xFFFF0000) >> 16; pci_info->default_port = temp; i++; @@ -2165,18 +2164,21 @@ int qlcnic_83xx_get_pci_info(struct qlcnic_adapter *adapter, i++; memcpy(pci_info->mac + sizeof(u32), &cmd.rsp.arg[i], 2); i = i + 3; - - dev_info(&adapter->pdev->dev, "%s:\n" - "\tid = %d active = %d type = %d\n" - "\tport = %d min bw = %d max bw = %d\n" - "\tmac_addr = %pM\n", __func__, - pci_info->id, pci_info->active, pci_info->type, - pci_info->default_port, pci_info->tx_min_bw, - pci_info->tx_max_bw, pci_info->mac); + if (ahw->op_mode == QLCNIC_MGMT_FUNC) + dev_info(dev, "id = %d active = %d type = %d\n" + "\tport = %d min bw = %d max bw = %d\n" + "\tmac_addr = %pM\n", pci_info->id, + pci_info->active, pci_info->type, + pci_info->default_port, + pci_info->tx_min_bw, + pci_info->tx_max_bw, pci_info->mac); } + if (ahw->op_mode == QLCNIC_MGMT_FUNC) + dev_info(dev, "Max vNIC functions = %d, active vNIC functions = %d\n", + ahw->max_pci_func, ahw->act_pci_func); + } else { - dev_err(&adapter->pdev->dev, "Failed to get PCI Info%d\n", - err); + dev_err(dev, "Failed to get PCI Info, error = %d\n", err); err = -EIO; } diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c index d63b5f728d75..9d0ae11589ce 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c @@ -883,11 +883,12 @@ int qlcnic_82xx_get_pci_info(struct qlcnic_adapter *adapter, /* Configure eSwitch for port mirroring */ int qlcnic_config_port_mirroring(struct qlcnic_adapter *adapter, u8 id, - u8 enable_mirroring, u8 pci_func) + u8 enable_mirroring, u8 pci_func) { + struct device *dev = &adapter->pdev->dev; + struct qlcnic_cmd_args cmd; int err = -EIO; u32 arg1; - struct qlcnic_cmd_args cmd; if (adapter->ahw->op_mode != QLCNIC_MGMT_FUNC || !(adapter->eswitch[id].flags & QLCNIC_SWITCH_ENABLE)) @@ -901,13 +902,11 @@ int qlcnic_config_port_mirroring(struct qlcnic_adapter *adapter, u8 id, err = qlcnic_issue_cmd(adapter, &cmd); if (err != QLCNIC_RCODE_SUCCESS) - dev_err(&adapter->pdev->dev, - "Failed to configure port mirroring%d on eswitch:%d\n", + dev_err(dev, "Failed to configure port mirroring for vNIC function %d on eSwitch %d\n", pci_func, id); else - dev_info(&adapter->pdev->dev, - "Configured eSwitch %d for port mirroring:%d\n", - id, pci_func); + dev_info(dev, "Configured port mirroring for vNIC function %d on eSwitch %d\n", + pci_func, id); qlcnic_free_mbx_args(&cmd); return err; @@ -1122,14 +1121,13 @@ err_ret: return -EIO; } -static int -__qlcnic_get_eswitch_port_config(struct qlcnic_adapter *adapter, - u32 *arg1, u32 *arg2) +static int __qlcnic_get_eswitch_port_config(struct qlcnic_adapter *adapter, + u32 *arg1, u32 *arg2) { - int err = -EIO; + struct device *dev = &adapter->pdev->dev; struct qlcnic_cmd_args cmd; - u8 pci_func; - pci_func = (*arg1 >> 8); + u8 pci_func = *arg1 >> 8; + int err = -EIO; qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_ESWITCH_PORT_CONFIG); @@ -1140,12 +1138,11 @@ __qlcnic_get_eswitch_port_config(struct qlcnic_adapter *adapter, qlcnic_free_mbx_args(&cmd); if (err == QLCNIC_RCODE_SUCCESS) - dev_info(&adapter->pdev->dev, - "eSwitch port config for pci func %d\n", pci_func); + dev_info(dev, "Get eSwitch port config for vNIC function %d\n", + pci_func); else - dev_err(&adapter->pdev->dev, - "Failed to get eswitch port config for pci func %d\n", - pci_func); + dev_err(dev, "Failed to get eswitch port config for vNIC function %d\n", + pci_func); return err; } /* Configure eSwitch port @@ -1158,9 +1155,10 @@ op_type = 1 for port vlan_id int qlcnic_config_switch_port(struct qlcnic_adapter *adapter, struct qlcnic_esw_func_cfg *esw_cfg) { + struct device *dev = &adapter->pdev->dev; + struct qlcnic_cmd_args cmd; int err = -EIO, index; u32 arg1, arg2 = 0; - struct qlcnic_cmd_args cmd; u8 pci_func; if (adapter->ahw->op_mode != QLCNIC_MGMT_FUNC) @@ -1217,11 +1215,11 @@ int qlcnic_config_switch_port(struct qlcnic_adapter *adapter, qlcnic_free_mbx_args(&cmd); if (err != QLCNIC_RCODE_SUCCESS) - dev_err(&adapter->pdev->dev, - "Failed to configure eswitch pci func %d\n", pci_func); + dev_err(dev, "Failed to configure eswitch for vNIC function %d\n", + pci_func); else - dev_info(&adapter->pdev->dev, - "Configured eSwitch for pci func %d\n", pci_func); + dev_info(dev, "Configured eSwitch for vNIC function %d\n", + pci_func); return err; } -- cgit v1.2.3 From 6a531b7b1989ddef5ee45c619970037eb2374c0d Mon Sep 17 00:00:00 2001 From: Shahed Shaikh Date: Thu, 23 May 2013 21:04:36 +0000 Subject: qlcnic: Update version to 5.2.43 Signed-off-by: Shahed Shaikh Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qlcnic/qlcnic.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h index 00a09573faab..534e36ed855a 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h @@ -38,8 +38,8 @@ #define _QLCNIC_LINUX_MAJOR 5 #define _QLCNIC_LINUX_MINOR 2 -#define _QLCNIC_LINUX_SUBVERSION 42 -#define QLCNIC_LINUX_VERSIONID "5.2.42" +#define _QLCNIC_LINUX_SUBVERSION 43 +#define QLCNIC_LINUX_VERSIONID "5.2.43" #define QLCNIC_DRV_IDC_VER 0x01 #define QLCNIC_DRIVER_VERSION ((_QLCNIC_LINUX_MAJOR << 16) |\ (_QLCNIC_LINUX_MINOR << 8) | (_QLCNIC_LINUX_SUBVERSION)) -- cgit v1.2.3 From 2678aeba5fe1a799788cce2a34003d8fa6ad7153 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 18 Mar 2013 11:09:13 +0100 Subject: drm/tegra: Don't disable unused planes When a plane isn't in use it isn't attached to a CRTC and therefore the DC registers aren't available for programming. Signed-off-by: Thierry Reding --- drivers/gpu/host1x/drm/dc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/host1x/drm/dc.c b/drivers/gpu/host1x/drm/dc.c index 8c04943f82e3..98f60d827558 100644 --- a/drivers/gpu/host1x/drm/dc.c +++ b/drivers/gpu/host1x/drm/dc.c @@ -79,6 +79,9 @@ static int tegra_plane_disable(struct drm_plane *plane) struct tegra_plane *p = to_tegra_plane(plane); unsigned long value; + if (!plane->crtc) + return 0; + value = WINDOW_A_SELECT << p->index; tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER); -- cgit v1.2.3 From 603f0cc9482e5e5996b84d3ffb5578526974809c Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 22 Apr 2013 21:22:14 +0200 Subject: drm/tegra: Explicitly set irq_enabled Since the Tegra DRM driver doesn't use the drm_irq_install() helper, the irq_enabled flag needs to be set manually in order to make functionality such as the DRM_IOCTL_WAIT_VBLANK work properly. Signed-off-by: Thierry Reding --- drivers/gpu/host1x/drm/drm.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/gpu/host1x/drm/drm.c b/drivers/gpu/host1x/drm/drm.c index 2b561c9118c6..78b07db37cf8 100644 --- a/drivers/gpu/host1x/drm/drm.c +++ b/drivers/gpu/host1x/drm/drm.c @@ -257,6 +257,13 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) if (err < 0) return err; + /* + * We don't use the drm_irq_install() helpers provided by the DRM + * core, so we need to set this manually in order to allow the + * DRM_IOCTL_WAIT_VBLANK to operate correctly. + */ + drm->irq_enabled = 1; + err = drm_vblank_init(drm, drm->mode_config.num_crtc); if (err < 0) return err; -- cgit v1.2.3 From ed683aead1962f2242c8dc8863517bcd3089703d Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 22 Apr 2013 21:31:15 +0200 Subject: drm/tegra: Honor pixel-format changes When using a base mode-set, honor changes in pixel-format since the core doesn't explicitly check for them as long as they use the same depth. Signed-off-by: Thierry Reding --- drivers/gpu/host1x/drm/dc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/host1x/drm/dc.c b/drivers/gpu/host1x/drm/dc.c index 98f60d827558..5360e5a57ecc 100644 --- a/drivers/gpu/host1x/drm/dc.c +++ b/drivers/gpu/host1x/drm/dc.c @@ -143,6 +143,7 @@ static int tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc) static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y, struct drm_framebuffer *fb) { + unsigned int format = tegra_dc_format(fb->pixel_format); struct tegra_bo *bo = tegra_fb_get_plane(fb, 0); unsigned long value; @@ -153,6 +154,7 @@ static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y, tegra_dc_writel(dc, bo->paddr + value, DC_WINBUF_START_ADDR); tegra_dc_writel(dc, fb->pitches[0], DC_WIN_LINE_STRIDE); + tegra_dc_writel(dc, format, DC_WIN_COLOR_DEPTH); value = GENERAL_UPDATE | WIN_A_UPDATE; tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); -- cgit v1.2.3 From b6f2056f3b259960b0cb6a6cf440b89f2567d586 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 24 Apr 2013 10:48:23 +0800 Subject: drm/tegra: fix missing unlock on error Add the missing unlock before return from function host1x_drm_init() and host1x_drm_exit() in the error handling case. Signed-off-by: Wei Yongjun Signed-off-by: Thierry Reding --- drivers/gpu/host1x/drm/drm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/host1x/drm/drm.c b/drivers/gpu/host1x/drm/drm.c index 78b07db37cf8..66629f398f11 100644 --- a/drivers/gpu/host1x/drm/drm.c +++ b/drivers/gpu/host1x/drm/drm.c @@ -148,6 +148,7 @@ int host1x_drm_init(struct host1x_drm *host1x, struct drm_device *drm) dev_err(host1x->dev, "DRM setup failed for %s: %d\n", dev_name(client->dev), err); + mutex_unlock(&host1x->clients_lock); return err; } } @@ -175,6 +176,7 @@ int host1x_drm_exit(struct host1x_drm *host1x) dev_err(host1x->dev, "DRM cleanup failed for %s: %d\n", dev_name(client->dev), err); + mutex_unlock(&host1x->clients_lock); return err; } } -- cgit v1.2.3 From 2639f2bf68e4c1bafafc0feca3e16cdad2333a82 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 24 Apr 2013 14:50:51 +0800 Subject: drm/tegra: fix error return code in gr2d_submit() Fix to return -ENOENT in the host1x_bo lookup error handling case instead of 0, as done elsewhere in this function. Signed-off-by: Wei Yongjun Signed-off-by: Thierry Reding --- drivers/gpu/host1x/drm/gr2d.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/host1x/drm/gr2d.c b/drivers/gpu/host1x/drm/gr2d.c index 6a45ae090ee7..aca72fc5e2a2 100644 --- a/drivers/gpu/host1x/drm/gr2d.c +++ b/drivers/gpu/host1x/drm/gr2d.c @@ -135,8 +135,10 @@ static int gr2d_submit(struct host1x_drm_context *context, goto fail; bo = host1x_bo_lookup(drm, file, cmdbuf.handle); - if (!bo) + if (!bo) { + err = -ENOENT; goto fail; + } host1x_job_add_gather(job, bo, cmdbuf.words, cmdbuf.offset); num_cmdbufs--; @@ -158,8 +160,10 @@ static int gr2d_submit(struct host1x_drm_context *context, reloc->cmdbuf = cmdbuf; reloc->target = target; - if (!reloc->target || !reloc->cmdbuf) + if (!reloc->target || !reloc->cmdbuf) { + err = -ENOENT; goto fail; + } } err = copy_from_user(job->waitchk, waitchks, -- cgit v1.2.3 From b6b5e76bb8bb22ecff90a7840dc4845d63328289 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 23 May 2013 20:14:50 +0200 Subject: ASoC: Add ssm2518 support This patch adds a ASoC CODEC driver for the SSM2516. The SSM2516 is a stereo Class-D audio amplifier with an I2S interface for audio in and a built-in dynamic range control processor. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/ssm2518.txt | 20 + include/linux/platform_data/ssm2518.h | 22 + sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/ssm2518.c | 856 +++++++++++++++++++++ sound/soc/codecs/ssm2518.h | 20 + 6 files changed, 924 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/ssm2518.txt create mode 100644 include/linux/platform_data/ssm2518.h create mode 100644 sound/soc/codecs/ssm2518.c create mode 100644 sound/soc/codecs/ssm2518.h diff --git a/Documentation/devicetree/bindings/sound/ssm2518.txt b/Documentation/devicetree/bindings/sound/ssm2518.txt new file mode 100644 index 000000000000..59381a778c79 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ssm2518.txt @@ -0,0 +1,20 @@ +SSM2518 audio amplifier + +This device supports I2C only. + +Required properties: + - compatible : Must be "adi,ssm2518" + - reg : the I2C address of the device. This will either be 0x34 (ADDR pin low) + or 0x35 (ADDR pin high) + +Optional properties: + - gpios : GPIO connected to the nSD pin. If the property is not present it is + assumed that the nSD pin is hardwired to always on. + +Example: + + ssm2518: ssm2518@34 { + compatible = "adi,ssm2518"; + reg = <0x34>; + gpios = <&gpio 5 0>; + }; diff --git a/include/linux/platform_data/ssm2518.h b/include/linux/platform_data/ssm2518.h new file mode 100644 index 000000000000..9a8e3ea287e3 --- /dev/null +++ b/include/linux/platform_data/ssm2518.h @@ -0,0 +1,22 @@ +/* + * SSM2518 amplifier audio driver + * + * Copyright 2013 Analog Devices Inc. + * Author: Lars-Peter Clausen + * + * Licensed under the GPL-2. + */ + +#ifndef __LINUX_PLATFORM_DATA_SSM2518_H__ +#define __LINUX_PLATFORM_DATA_SSM2518_H__ + +/** + * struct ssm2518_platform_data - Platform data for the ssm2518 driver + * @enable_gpio: GPIO connected to the nSD pin. Set to -1 if the nSD pin is + * hardwired. + */ +struct ssm2518_platform_data { + int enable_gpio; +}; + +#endif diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 2f45f00e31b0..d76609adb85b 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -60,6 +60,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_SI476X if MFD_SI476X_CORE select SND_SOC_SN95031 if INTEL_SCU_IPC select SND_SOC_SPDIF + select SND_SOC_SSM2518 if I2C select SND_SOC_SSM2602 if SND_SOC_I2C_AND_SPI select SND_SOC_STA32X if I2C select SND_SOC_STA529 if I2C @@ -313,6 +314,9 @@ config SND_SOC_SN95031 config SND_SOC_SPDIF tristate +config SND_SOC_SSM2518 + tristate + config SND_SOC_SSM2602 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index b9e41c9a1f4c..d85be48a6c07 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -52,6 +52,7 @@ snd-soc-si476x-objs := si476x.o snd-soc-sn95031-objs := sn95031.o snd-soc-spdif-tx-objs := spdif_transciever.o snd-soc-spdif-rx-objs := spdif_receiver.o +snd-soc-ssm2518-objs := ssm2518.o snd-soc-ssm2602-objs := ssm2602.o snd-soc-sta32x-objs := sta32x.o snd-soc-sta529-objs := sta529.o @@ -176,6 +177,7 @@ obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o obj-$(CONFIG_SND_SOC_SI476X) += snd-soc-si476x.o obj-$(CONFIG_SND_SOC_SN95031) +=snd-soc-sn95031.o obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif-rx.o snd-soc-spdif-tx.o +obj-$(CONFIG_SND_SOC_SSM2518) += snd-soc-ssm2518.o obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o obj-$(CONFIG_SND_SOC_STA32X) += snd-soc-sta32x.o obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o diff --git a/sound/soc/codecs/ssm2518.c b/sound/soc/codecs/ssm2518.c new file mode 100644 index 000000000000..3139a1bde295 --- /dev/null +++ b/sound/soc/codecs/ssm2518.c @@ -0,0 +1,856 @@ +/* + * SSM2518 amplifier audio driver + * + * Copyright 2013 Analog Devices Inc. + * Author: Lars-Peter Clausen + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ssm2518.h" + +#define SSM2518_REG_POWER1 0x00 +#define SSM2518_REG_CLOCK 0x01 +#define SSM2518_REG_SAI_CTRL1 0x02 +#define SSM2518_REG_SAI_CTRL2 0x03 +#define SSM2518_REG_CHAN_MAP 0x04 +#define SSM2518_REG_LEFT_VOL 0x05 +#define SSM2518_REG_RIGHT_VOL 0x06 +#define SSM2518_REG_MUTE_CTRL 0x07 +#define SSM2518_REG_FAULT_CTRL 0x08 +#define SSM2518_REG_POWER2 0x09 +#define SSM2518_REG_DRC_1 0x0a +#define SSM2518_REG_DRC_2 0x0b +#define SSM2518_REG_DRC_3 0x0c +#define SSM2518_REG_DRC_4 0x0d +#define SSM2518_REG_DRC_5 0x0e +#define SSM2518_REG_DRC_6 0x0f +#define SSM2518_REG_DRC_7 0x10 +#define SSM2518_REG_DRC_8 0x11 +#define SSM2518_REG_DRC_9 0x12 + +#define SSM2518_POWER1_RESET BIT(7) +#define SSM2518_POWER1_NO_BCLK BIT(5) +#define SSM2518_POWER1_MCS_MASK (0xf << 1) +#define SSM2518_POWER1_MCS_64FS (0x0 << 1) +#define SSM2518_POWER1_MCS_128FS (0x1 << 1) +#define SSM2518_POWER1_MCS_256FS (0x2 << 1) +#define SSM2518_POWER1_MCS_384FS (0x3 << 1) +#define SSM2518_POWER1_MCS_512FS (0x4 << 1) +#define SSM2518_POWER1_MCS_768FS (0x5 << 1) +#define SSM2518_POWER1_MCS_100FS (0x6 << 1) +#define SSM2518_POWER1_MCS_200FS (0x7 << 1) +#define SSM2518_POWER1_MCS_400FS (0x8 << 1) +#define SSM2518_POWER1_SPWDN BIT(0) + +#define SSM2518_CLOCK_ASR BIT(0) + +#define SSM2518_SAI_CTRL1_FMT_MASK (0x3 << 5) +#define SSM2518_SAI_CTRL1_FMT_I2S (0x0 << 5) +#define SSM2518_SAI_CTRL1_FMT_LJ (0x1 << 5) +#define SSM2518_SAI_CTRL1_FMT_RJ_24BIT (0x2 << 5) +#define SSM2518_SAI_CTRL1_FMT_RJ_16BIT (0x3 << 5) + +#define SSM2518_SAI_CTRL1_SAI_MASK (0x7 << 2) +#define SSM2518_SAI_CTRL1_SAI_I2S (0x0 << 2) +#define SSM2518_SAI_CTRL1_SAI_TDM_2 (0x1 << 2) +#define SSM2518_SAI_CTRL1_SAI_TDM_4 (0x2 << 2) +#define SSM2518_SAI_CTRL1_SAI_TDM_8 (0x3 << 2) +#define SSM2518_SAI_CTRL1_SAI_TDM_16 (0x4 << 2) +#define SSM2518_SAI_CTRL1_SAI_MONO (0x5 << 2) + +#define SSM2518_SAI_CTRL1_FS_MASK (0x3) +#define SSM2518_SAI_CTRL1_FS_8000_12000 (0x0) +#define SSM2518_SAI_CTRL1_FS_16000_24000 (0x1) +#define SSM2518_SAI_CTRL1_FS_32000_48000 (0x2) +#define SSM2518_SAI_CTRL1_FS_64000_96000 (0x3) + +#define SSM2518_SAI_CTRL2_BCLK_INTERAL BIT(7) +#define SSM2518_SAI_CTRL2_LRCLK_PULSE BIT(6) +#define SSM2518_SAI_CTRL2_LRCLK_INVERT BIT(5) +#define SSM2518_SAI_CTRL2_MSB BIT(4) +#define SSM2518_SAI_CTRL2_SLOT_WIDTH_MASK (0x3 << 2) +#define SSM2518_SAI_CTRL2_SLOT_WIDTH_32 (0x0 << 2) +#define SSM2518_SAI_CTRL2_SLOT_WIDTH_24 (0x1 << 2) +#define SSM2518_SAI_CTRL2_SLOT_WIDTH_16 (0x2 << 2) +#define SSM2518_SAI_CTRL2_BCLK_INVERT BIT(1) + +#define SSM2518_CHAN_MAP_RIGHT_SLOT_OFFSET 4 +#define SSM2518_CHAN_MAP_RIGHT_SLOT_MASK 0xf0 +#define SSM2518_CHAN_MAP_LEFT_SLOT_OFFSET 0 +#define SSM2518_CHAN_MAP_LEFT_SLOT_MASK 0x0f + +#define SSM2518_MUTE_CTRL_ANA_GAIN BIT(5) +#define SSM2518_MUTE_CTRL_MUTE_MASTER BIT(0) + +#define SSM2518_POWER2_APWDN BIT(0) + +#define SSM2518_DAC_MUTE BIT(6) +#define SSM2518_DAC_FS_MASK 0x07 +#define SSM2518_DAC_FS_8000 0x00 +#define SSM2518_DAC_FS_16000 0x01 +#define SSM2518_DAC_FS_32000 0x02 +#define SSM2518_DAC_FS_64000 0x03 +#define SSM2518_DAC_FS_128000 0x04 + +struct ssm2518 { + struct regmap *regmap; + bool right_j; + + unsigned int sysclk; + const struct snd_pcm_hw_constraint_list *constraints; + + int enable_gpio; +}; + +static const struct reg_default ssm2518_reg_defaults[] = { + { 0x00, 0x05 }, + { 0x01, 0x00 }, + { 0x02, 0x02 }, + { 0x03, 0x00 }, + { 0x04, 0x10 }, + { 0x05, 0x40 }, + { 0x06, 0x40 }, + { 0x07, 0x81 }, + { 0x08, 0x0c }, + { 0x09, 0x99 }, + { 0x0a, 0x7c }, + { 0x0b, 0x5b }, + { 0x0c, 0x57 }, + { 0x0d, 0x89 }, + { 0x0e, 0x8c }, + { 0x0f, 0x77 }, + { 0x10, 0x26 }, + { 0x11, 0x1c }, + { 0x12, 0x97 }, +}; + +static const DECLARE_TLV_DB_MINMAX_MUTE(ssm2518_vol_tlv, -7125, 2400); +static const DECLARE_TLV_DB_SCALE(ssm2518_compressor_tlv, -3400, 200, 0); +static const DECLARE_TLV_DB_SCALE(ssm2518_expander_tlv, -8100, 300, 0); +static const DECLARE_TLV_DB_SCALE(ssm2518_noise_gate_tlv, -9600, 300, 0); +static const DECLARE_TLV_DB_SCALE(ssm2518_post_drc_tlv, -2400, 300, 0); + +static const DECLARE_TLV_DB_RANGE(ssm2518_limiter_tlv, + 0, 7, TLV_DB_SCALE_ITEM(-2200, 200, 0), + 7, 15, TLV_DB_SCALE_ITEM(-800, 100, 0), +); + +static const char * const ssm2518_drc_peak_detector_attack_time_text[] = { + "0 ms", "0.1 ms", "0.19 ms", "0.37 ms", "0.75 ms", "1.5 ms", "3 ms", + "6 ms", "12 ms", "24 ms", "48 ms", "96 ms", "192 ms", "384 ms", + "768 ms", "1536 ms", +}; + +static const char * const ssm2518_drc_peak_detector_release_time_text[] = { + "0 ms", "1.5 ms", "3 ms", "6 ms", "12 ms", "24 ms", "48 ms", "96 ms", + "192 ms", "384 ms", "768 ms", "1536 ms", "3072 ms", "6144 ms", + "12288 ms", "24576 ms" +}; + +static const char * const ssm2518_drc_hold_time_text[] = { + "0 ms", "0.67 ms", "1.33 ms", "2.67 ms", "5.33 ms", "10.66 ms", + "21.32 ms", "42.64 ms", "85.28 ms", "170.56 ms", "341.12 ms", + "682.24 ms", "1364 ms", +}; + +static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_peak_detector_attack_time_enum, + SSM2518_REG_DRC_2, 4, ssm2518_drc_peak_detector_attack_time_text); +static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_peak_detector_release_time_enum, + SSM2518_REG_DRC_2, 0, ssm2518_drc_peak_detector_release_time_text); +static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_attack_time_enum, + SSM2518_REG_DRC_6, 4, ssm2518_drc_peak_detector_attack_time_text); +static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_decay_time_enum, + SSM2518_REG_DRC_6, 0, ssm2518_drc_peak_detector_release_time_text); +static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_hold_time_enum, + SSM2518_REG_DRC_7, 4, ssm2518_drc_hold_time_text); +static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_noise_gate_hold_time_enum, + SSM2518_REG_DRC_7, 0, ssm2518_drc_hold_time_text); +static const SOC_ENUM_SINGLE_DECL(ssm2518_drc_rms_averaging_time_enum, + SSM2518_REG_DRC_9, 0, ssm2518_drc_peak_detector_release_time_text); + +static const struct snd_kcontrol_new ssm2518_snd_controls[] = { + SOC_SINGLE("Playback De-emphasis Switch", SSM2518_REG_MUTE_CTRL, + 4, 1, 0), + SOC_DOUBLE_R_TLV("Master Playback Volume", SSM2518_REG_LEFT_VOL, + SSM2518_REG_RIGHT_VOL, 0, 0xff, 1, ssm2518_vol_tlv), + SOC_DOUBLE("Master Playback Switch", SSM2518_REG_MUTE_CTRL, 2, 1, 1, 1), + + SOC_SINGLE("Amp Low Power Mode Switch", SSM2518_REG_POWER2, 4, 1, 0), + SOC_SINGLE("DAC Low Power Mode Switch", SSM2518_REG_POWER2, 3, 1, 0), + + SOC_SINGLE("DRC Limiter Switch", SSM2518_REG_DRC_1, 5, 1, 0), + SOC_SINGLE("DRC Compressor Switch", SSM2518_REG_DRC_1, 4, 1, 0), + SOC_SINGLE("DRC Expander Switch", SSM2518_REG_DRC_1, 3, 1, 0), + SOC_SINGLE("DRC Noise Gate Switch", SSM2518_REG_DRC_1, 2, 1, 0), + SOC_DOUBLE("DRC Switch", SSM2518_REG_DRC_1, 0, 1, 1, 0), + + SOC_SINGLE_TLV("DRC Limiter Threshold Volume", + SSM2518_REG_DRC_3, 4, 15, 1, ssm2518_limiter_tlv), + SOC_SINGLE_TLV("DRC Compressor Lower Threshold Volume", + SSM2518_REG_DRC_3, 0, 15, 1, ssm2518_compressor_tlv), + SOC_SINGLE_TLV("DRC Expander Upper Threshold Volume", SSM2518_REG_DRC_4, + 4, 15, 1, ssm2518_expander_tlv), + SOC_SINGLE_TLV("DRC Noise Gate Threshold Volume", + SSM2518_REG_DRC_4, 0, 15, 1, ssm2518_noise_gate_tlv), + SOC_SINGLE_TLV("DRC Upper Output Threshold Volume", + SSM2518_REG_DRC_5, 4, 15, 1, ssm2518_limiter_tlv), + SOC_SINGLE_TLV("DRC Lower Output Threshold Volume", + SSM2518_REG_DRC_5, 0, 15, 1, ssm2518_noise_gate_tlv), + SOC_SINGLE_TLV("DRC Post Volume", SSM2518_REG_DRC_8, + 2, 15, 1, ssm2518_post_drc_tlv), + + SOC_ENUM("DRC Peak Detector Attack Time", + ssm2518_drc_peak_detector_attack_time_enum), + SOC_ENUM("DRC Peak Detector Release Time", + ssm2518_drc_peak_detector_release_time_enum), + SOC_ENUM("DRC Attack Time", ssm2518_drc_attack_time_enum), + SOC_ENUM("DRC Decay Time", ssm2518_drc_decay_time_enum), + SOC_ENUM("DRC Hold Time", ssm2518_drc_hold_time_enum), + SOC_ENUM("DRC Noise Gate Hold Time", + ssm2518_drc_noise_gate_hold_time_enum), + SOC_ENUM("DRC RMS Averaging Time", ssm2518_drc_rms_averaging_time_enum), +}; + +static const struct snd_soc_dapm_widget ssm2518_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DACL", "HiFi Playback", SSM2518_REG_POWER2, 1, 1), + SND_SOC_DAPM_DAC("DACR", "HiFi Playback", SSM2518_REG_POWER2, 2, 1), + + SND_SOC_DAPM_OUTPUT("OUTL"), + SND_SOC_DAPM_OUTPUT("OUTR"), +}; + +static const struct snd_soc_dapm_route ssm2518_routes[] = { + { "OUTL", NULL, "DACL" }, + { "OUTR", NULL, "DACR" }, +}; + +struct ssm2518_mcs_lut { + unsigned int rate; + const unsigned int *sysclks; +}; + +static const unsigned int ssm2518_sysclks_2048000[] = { + 2048000, 4096000, 8192000, 12288000, 16384000, 24576000, + 3200000, 6400000, 12800000, 0 +}; + +static const unsigned int ssm2518_sysclks_2822000[] = { + 2822000, 5644800, 11289600, 16934400, 22579200, 33868800, + 4410000, 8820000, 17640000, 0 +}; + +static const unsigned int ssm2518_sysclks_3072000[] = { + 3072000, 6144000, 12288000, 16384000, 24576000, 38864000, + 4800000, 9600000, 19200000, 0 +}; + +static const struct ssm2518_mcs_lut ssm2518_mcs_lut[] = { + { 8000, ssm2518_sysclks_2048000, }, + { 11025, ssm2518_sysclks_2822000, }, + { 12000, ssm2518_sysclks_3072000, }, + { 16000, ssm2518_sysclks_2048000, }, + { 24000, ssm2518_sysclks_3072000, }, + { 22050, ssm2518_sysclks_2822000, }, + { 32000, ssm2518_sysclks_2048000, }, + { 44100, ssm2518_sysclks_2822000, }, + { 48000, ssm2518_sysclks_3072000, }, + { 96000, ssm2518_sysclks_3072000, }, +}; + +static const unsigned int ssm2518_rates_2048000[] = { + 8000, 16000, 32000, +}; + +static const struct snd_pcm_hw_constraint_list ssm2518_constraints_2048000 = { + .list = ssm2518_rates_2048000, + .count = ARRAY_SIZE(ssm2518_rates_2048000), +}; + +static const unsigned int ssm2518_rates_2822000[] = { + 11025, 22050, 44100, +}; + +static const struct snd_pcm_hw_constraint_list ssm2518_constraints_2822000 = { + .list = ssm2518_rates_2822000, + .count = ARRAY_SIZE(ssm2518_rates_2822000), +}; + +static const unsigned int ssm2518_rates_3072000[] = { + 12000, 24000, 48000, 96000, +}; + +static const struct snd_pcm_hw_constraint_list ssm2518_constraints_3072000 = { + .list = ssm2518_rates_3072000, + .count = ARRAY_SIZE(ssm2518_rates_3072000), +}; + +static const unsigned int ssm2518_rates_12288000[] = { + 8000, 12000, 16000, 24000, 32000, 48000, 96000, +}; + +static const struct snd_pcm_hw_constraint_list ssm2518_constraints_12288000 = { + .list = ssm2518_rates_12288000, + .count = ARRAY_SIZE(ssm2518_rates_12288000), +}; + +static unsigned int ssm2518_lookup_mcs(struct ssm2518 *ssm2518, + unsigned int rate) +{ + const unsigned int *sysclks = NULL; + int i; + + for (i = 0; i < ARRAY_SIZE(ssm2518_mcs_lut); i++) { + if (ssm2518_mcs_lut[i].rate == rate) { + sysclks = ssm2518_mcs_lut[i].sysclks; + break; + } + } + + if (!sysclks) + return -EINVAL; + + for (i = 0; sysclks[i]; i++) { + if (sysclks[i] == ssm2518->sysclk) + return i; + } + + return -EINVAL; +} + +static int ssm2518_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(codec); + unsigned int rate = params_rate(params); + unsigned int ctrl1, ctrl1_mask; + int mcs; + int ret; + + mcs = ssm2518_lookup_mcs(ssm2518, rate); + if (mcs < 0) + return mcs; + + ctrl1_mask = SSM2518_SAI_CTRL1_FS_MASK; + + if (rate >= 8000 && rate <= 12000) + ctrl1 = SSM2518_SAI_CTRL1_FS_8000_12000; + else if (rate >= 16000 && rate <= 24000) + ctrl1 = SSM2518_SAI_CTRL1_FS_16000_24000; + else if (rate >= 32000 && rate <= 48000) + ctrl1 = SSM2518_SAI_CTRL1_FS_32000_48000; + else if (rate >= 64000 && rate <= 96000) + ctrl1 = SSM2518_SAI_CTRL1_FS_64000_96000; + else + return -EINVAL; + + if (ssm2518->right_j) { + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + ctrl1 |= SSM2518_SAI_CTRL1_FMT_RJ_16BIT; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ctrl1 |= SSM2518_SAI_CTRL1_FMT_RJ_24BIT; + break; + default: + return -EINVAL; + } + ctrl1_mask |= SSM2518_SAI_CTRL1_FMT_MASK; + } + + /* Disable auto samplerate detection */ + ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_CLOCK, + SSM2518_CLOCK_ASR, SSM2518_CLOCK_ASR); + if (ret < 0) + return ret; + + ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_SAI_CTRL1, + ctrl1_mask, ctrl1); + if (ret < 0) + return ret; + + return regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER1, + SSM2518_POWER1_MCS_MASK, mcs << 1); +} + +static int ssm2518_mute(struct snd_soc_dai *dai, int mute) +{ + struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(dai->codec); + unsigned int val; + + if (mute) + val = SSM2518_MUTE_CTRL_MUTE_MASTER; + else + val = 0; + + return regmap_update_bits(ssm2518->regmap, SSM2518_REG_MUTE_CTRL, + SSM2518_MUTE_CTRL_MUTE_MASTER, val); +} + +static int ssm2518_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(dai->codec); + unsigned int ctrl1 = 0, ctrl2 = 0; + bool invert_fclk; + int ret; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + invert_fclk = false; + break; + case SND_SOC_DAIFMT_IB_NF: + ctrl2 |= SSM2518_SAI_CTRL2_BCLK_INVERT; + invert_fclk = false; + break; + case SND_SOC_DAIFMT_NB_IF: + invert_fclk = true; + break; + case SND_SOC_DAIFMT_IB_IF: + ctrl2 |= SSM2518_SAI_CTRL2_BCLK_INVERT; + invert_fclk = true; + break; + default: + return -EINVAL; + } + + ssm2518->right_j = false; + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + ctrl1 |= SSM2518_SAI_CTRL1_FMT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + ctrl1 |= SSM2518_SAI_CTRL1_FMT_LJ; + invert_fclk = !invert_fclk; + break; + case SND_SOC_DAIFMT_RIGHT_J: + ctrl1 |= SSM2518_SAI_CTRL1_FMT_RJ_24BIT; + ssm2518->right_j = true; + invert_fclk = !invert_fclk; + break; + case SND_SOC_DAIFMT_DSP_A: + ctrl2 |= SSM2518_SAI_CTRL2_LRCLK_PULSE; + ctrl1 |= SSM2518_SAI_CTRL1_FMT_I2S; + invert_fclk = false; + break; + case SND_SOC_DAIFMT_DSP_B: + ctrl2 |= SSM2518_SAI_CTRL2_LRCLK_PULSE; + ctrl1 |= SSM2518_SAI_CTRL1_FMT_LJ; + invert_fclk = false; + break; + default: + return -EINVAL; + } + + if (invert_fclk) + ctrl2 |= SSM2518_SAI_CTRL2_LRCLK_INVERT; + + ret = regmap_write(ssm2518->regmap, SSM2518_REG_SAI_CTRL1, ctrl1); + if (ret) + return ret; + + return regmap_write(ssm2518->regmap, SSM2518_REG_SAI_CTRL2, ctrl2); +} + +static int ssm2518_set_power(struct ssm2518 *ssm2518, bool enable) +{ + int ret = 0; + + if (!enable) { + ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER1, + SSM2518_POWER1_SPWDN, SSM2518_POWER1_SPWDN); + regcache_mark_dirty(ssm2518->regmap); + } + + if (gpio_is_valid(ssm2518->enable_gpio)) + gpio_set_value(ssm2518->enable_gpio, enable); + + regcache_cache_only(ssm2518->regmap, !enable); + + if (enable) { + ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER1, + SSM2518_POWER1_SPWDN | SSM2518_POWER1_RESET, 0x00); + regcache_sync(ssm2518->regmap); + } + + return ret; +} + +static int ssm2518_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) + ret = ssm2518_set_power(ssm2518, true); + break; + case SND_SOC_BIAS_OFF: + ret = ssm2518_set_power(ssm2518, false); + break; + } + + if (ret) + return ret; + + codec->dapm.bias_level = level; + + return 0; +} + +static int ssm2518_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int width) +{ + struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(dai->codec); + unsigned int ctrl1, ctrl2; + int left_slot, right_slot; + int ret; + + if (slots == 0) + return regmap_update_bits(ssm2518->regmap, + SSM2518_REG_SAI_CTRL1, SSM2518_SAI_CTRL1_SAI_MASK, + SSM2518_SAI_CTRL1_SAI_I2S); + + if (tx_mask == 0 || tx_mask != 0) + return -EINVAL; + + if (slots == 1) { + if (tx_mask != 1) + return -EINVAL; + left_slot = 0; + right_slot = 0; + } else { + /* We assume the left channel < right channel */ + left_slot = ffs(tx_mask); + tx_mask &= ~(1 << tx_mask); + if (tx_mask == 0) { + right_slot = left_slot; + } else { + right_slot = ffs(tx_mask); + tx_mask &= ~(1 << tx_mask); + } + } + + if (tx_mask != 0 || left_slot >= slots || right_slot >= slots) + return -EINVAL; + + switch (width) { + case 16: + ctrl2 = SSM2518_SAI_CTRL2_SLOT_WIDTH_16; + break; + case 24: + ctrl2 = SSM2518_SAI_CTRL2_SLOT_WIDTH_24; + break; + case 32: + ctrl2 = SSM2518_SAI_CTRL2_SLOT_WIDTH_32; + break; + default: + return -EINVAL; + } + + switch (slots) { + case 1: + ctrl1 = SSM2518_SAI_CTRL1_SAI_MONO; + break; + case 2: + ctrl1 = SSM2518_SAI_CTRL1_SAI_TDM_2; + break; + case 4: + ctrl1 = SSM2518_SAI_CTRL1_SAI_TDM_4; + break; + case 8: + ctrl1 = SSM2518_SAI_CTRL1_SAI_TDM_8; + break; + case 16: + ctrl1 = SSM2518_SAI_CTRL1_SAI_TDM_16; + break; + default: + return -EINVAL; + } + + ret = regmap_write(ssm2518->regmap, SSM2518_REG_CHAN_MAP, + (left_slot << SSM2518_CHAN_MAP_LEFT_SLOT_OFFSET) | + (right_slot << SSM2518_CHAN_MAP_RIGHT_SLOT_OFFSET)); + if (ret) + return ret; + + ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_SAI_CTRL1, + SSM2518_SAI_CTRL1_SAI_MASK, ctrl1); + if (ret) + return ret; + + return regmap_update_bits(ssm2518->regmap, SSM2518_REG_SAI_CTRL2, + SSM2518_SAI_CTRL2_SLOT_WIDTH_MASK, ctrl2); +} + +static int ssm2518_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(dai->codec); + + if (ssm2518->constraints) + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, ssm2518->constraints); + + return 0; +} + +#define SSM2518_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32) + +static const struct snd_soc_dai_ops ssm2518_dai_ops = { + .startup = ssm2518_startup, + .hw_params = ssm2518_hw_params, + .digital_mute = ssm2518_mute, + .set_fmt = ssm2518_set_dai_fmt, + .set_tdm_slot = ssm2518_set_tdm_slot, +}; + +static struct snd_soc_dai_driver ssm2518_dai = { + .name = "ssm2518-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SSM2518_FORMATS, + }, + .ops = &ssm2518_dai_ops, +}; + +static int ssm2518_probe(struct snd_soc_codec *codec) +{ + struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(codec); + int ret; + + codec->control_data = ssm2518->regmap; + ret = snd_soc_codec_set_cache_io(codec, 0, 0, SND_SOC_REGMAP); + if (ret < 0) { + dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); + return ret; + } + + return ssm2518_set_bias_level(codec, SND_SOC_BIAS_OFF); +} + +static int ssm2518_remove(struct snd_soc_codec *codec) +{ + ssm2518_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static int ssm2518_set_sysclk(struct snd_soc_codec *codec, int clk_id, + int source, unsigned int freq, int dir) +{ + struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(codec); + unsigned int val; + + if (clk_id != SSM2518_SYSCLK) + return -EINVAL; + + switch (source) { + case SSM2518_SYSCLK_SRC_MCLK: + val = 0; + break; + case SSM2518_SYSCLK_SRC_BCLK: + /* In this case the bitclock is used as the system clock, and + * the bitclock signal needs to be connected to the MCLK pin and + * the BCLK pin is left unconnected */ + val = SSM2518_POWER1_NO_BCLK; + break; + default: + return -EINVAL; + } + + switch (freq) { + case 0: + ssm2518->constraints = NULL; + break; + case 2048000: + case 4096000: + case 8192000: + case 3200000: + case 6400000: + case 12800000: + ssm2518->constraints = &ssm2518_constraints_2048000; + break; + case 2822000: + case 5644800: + case 11289600: + case 16934400: + case 22579200: + case 33868800: + case 4410000: + case 8820000: + case 17640000: + ssm2518->constraints = &ssm2518_constraints_2822000; + break; + case 3072000: + case 6144000: + case 38864000: + case 4800000: + case 9600000: + case 19200000: + ssm2518->constraints = &ssm2518_constraints_3072000; + break; + case 12288000: + case 16384000: + case 24576000: + ssm2518->constraints = &ssm2518_constraints_12288000; + break; + default: + return -EINVAL; + } + + ssm2518->sysclk = freq; + + return regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER1, + SSM2518_POWER1_NO_BCLK, val); +} + +static struct snd_soc_codec_driver ssm2518_codec_driver = { + .probe = ssm2518_probe, + .remove = ssm2518_remove, + .set_bias_level = ssm2518_set_bias_level, + .set_sysclk = ssm2518_set_sysclk, + .idle_bias_off = true, + + .controls = ssm2518_snd_controls, + .num_controls = ARRAY_SIZE(ssm2518_snd_controls), + .dapm_widgets = ssm2518_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ssm2518_dapm_widgets), + .dapm_routes = ssm2518_routes, + .num_dapm_routes = ARRAY_SIZE(ssm2518_routes), +}; + +static bool ssm2518_register_volatile(struct device *dev, unsigned int reg) +{ + return false; +} + +static const struct regmap_config ssm2518_regmap_config = { + .val_bits = 8, + .reg_bits = 8, + + .max_register = SSM2518_REG_DRC_9, + .volatile_reg = ssm2518_register_volatile, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = ssm2518_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(ssm2518_reg_defaults), +}; + +static int ssm2518_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct ssm2518_platform_data *pdata = i2c->dev.platform_data; + struct ssm2518 *ssm2518; + int ret; + + ssm2518 = devm_kzalloc(&i2c->dev, sizeof(*ssm2518), GFP_KERNEL); + if (ssm2518 == NULL) + return -ENOMEM; + + if (pdata) { + ssm2518->enable_gpio = pdata->enable_gpio; + } else if (i2c->dev.of_node) { + ssm2518->enable_gpio = of_get_gpio(i2c->dev.of_node, 0); + if (ssm2518->enable_gpio < 0 && ssm2518->enable_gpio != -ENOENT) + return ssm2518->enable_gpio; + } else { + ssm2518->enable_gpio = -1; + } + + if (gpio_is_valid(ssm2518->enable_gpio)) { + ret = devm_gpio_request_one(&i2c->dev, ssm2518->enable_gpio, + GPIOF_OUT_INIT_HIGH, "SSM2518 nSD"); + if (ret) + return ret; + } + + i2c_set_clientdata(i2c, ssm2518); + + ssm2518->regmap = devm_regmap_init_i2c(i2c, &ssm2518_regmap_config); + if (IS_ERR(ssm2518->regmap)) + return PTR_ERR(ssm2518->regmap); + + /* + * The reset bit is obviously volatile, but we need to be able to cache + * the other bits in the register, so we can't just mark the whole + * register as volatile. Since this is the only place where we'll ever + * touch the reset bit just bypass the cache for this operation. + */ + regcache_cache_bypass(ssm2518->regmap, true); + ret = regmap_write(ssm2518->regmap, SSM2518_REG_POWER1, + SSM2518_POWER1_RESET); + regcache_cache_bypass(ssm2518->regmap, false); + if (ret) + return ret; + + ret = regmap_update_bits(ssm2518->regmap, SSM2518_REG_POWER2, + SSM2518_POWER2_APWDN, 0x00); + if (ret) + return ret; + + ret = ssm2518_set_power(ssm2518, false); + if (ret) + return ret; + + return snd_soc_register_codec(&i2c->dev, &ssm2518_codec_driver, + &ssm2518_dai, 1); +} + +static int ssm2518_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static const struct i2c_device_id ssm2518_i2c_ids[] = { + { "ssm2518", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ssm2518_i2c_ids); + +static struct i2c_driver ssm2518_driver = { + .driver = { + .name = "ssm2518", + .owner = THIS_MODULE, + }, + .probe = ssm2518_i2c_probe, + .remove = ssm2518_i2c_remove, + .id_table = ssm2518_i2c_ids, +}; +module_i2c_driver(ssm2518_driver); + +MODULE_DESCRIPTION("ASoC SSM2518 driver"); +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ssm2518.h b/sound/soc/codecs/ssm2518.h new file mode 100644 index 000000000000..62511d80518e --- /dev/null +++ b/sound/soc/codecs/ssm2518.h @@ -0,0 +1,20 @@ +/* + * SSM2518 amplifier audio driver + * + * Copyright 2013 Analog Devices Inc. + * Author: Lars-Peter Clausen + * + * Licensed under the GPL-2. + */ + +#ifndef __SND_SOC_CODECS_SSM2518_H__ +#define __SND_SOC_CODECS_SSM2518_H__ + +#define SSM2518_SYSCLK 0 + +enum ssm2518_sysclk_src { + SSM2518_SYSCLK_SRC_MCLK = 0, + SSM2518_SYSCLK_SRC_BCLK = 1, +}; + +#endif -- cgit v1.2.3 From 04561eacaa6ccd1988e468cdcbf4acc475ae2221 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 23 May 2013 15:46:05 +0200 Subject: ASoC: codecs: adau1701: add DT bindings Apart from pure matching, the bindings also support setting the the reset gpio line. Signed-off-by: Daniel Mack Acked-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/adi,adau1701.txt | 23 ++++++++++++++ sound/soc/codecs/adau1701.c | 35 +++++++++++++++++++++- 2 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/sound/adi,adau1701.txt diff --git a/Documentation/devicetree/bindings/sound/adi,adau1701.txt b/Documentation/devicetree/bindings/sound/adi,adau1701.txt new file mode 100644 index 000000000000..3afeda77b5b9 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/adi,adau1701.txt @@ -0,0 +1,23 @@ +Analog Devices ADAU1701 + +Required properties: + + - compatible: Should contain "adi,adau1701" + - reg: The i2c address. Value depends on the state of ADDR0 + and ADDR1, as wired in hardware. + +Optional properties: + + - reset-gpio: A GPIO spec to define which pin is connected to the + chip's !RESET pin. If specified, the driver will + assert a hardware reset at probe time. + +Examples: + + i2c_bus { + adau1701@34 { + compatible = "adi,adau1701"; + reg = <0x34>; + reset-gpio = <&gpio 23 0>; + }; + }; diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c index 95e1677665e9..3fc176387351 100644 --- a/sound/soc/codecs/adau1701.c +++ b/sound/soc/codecs/adau1701.c @@ -13,6 +13,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -452,6 +455,14 @@ static struct snd_soc_dai_driver adau1701_dai = { .symmetric_rates = 1, }; +#ifdef CONFIG_OF +static const struct of_device_id adau1701_dt_ids[] = { + { .compatible = "adi,adau1701", }, + { } +}; +MODULE_DEVICE_TABLE(of, adau1701_dt_ids); +#endif + static int adau1701_probe(struct snd_soc_codec *codec) { int ret; @@ -494,12 +505,33 @@ static int adau1701_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct adau1701 *adau1701; + struct device *dev = &client->dev; + int gpio_nreset = -EINVAL; int ret; - adau1701 = devm_kzalloc(&client->dev, sizeof(*adau1701), GFP_KERNEL); + adau1701 = devm_kzalloc(dev, sizeof(*adau1701), GFP_KERNEL); if (!adau1701) return -ENOMEM; + if (dev->of_node) { + gpio_nreset = of_get_named_gpio(dev->of_node, "reset-gpio", 0); + if (gpio_nreset < 0 && gpio_nreset != -ENOENT) + return gpio_nreset; + } + + if (gpio_is_valid(gpio_nreset)) { + ret = devm_gpio_request_one(dev, gpio_nreset, GPIOF_OUT_INIT_LOW, + "ADAU1701 Reset"); + if (ret < 0) + return ret; + + /* minimum reset time is 20ns */ + udelay(1); + gpio_set_value(gpio_nreset, 1); + /* power-up time may be as long as 85ms */ + mdelay(85); + } + i2c_set_clientdata(client, adau1701); ret = snd_soc_register_codec(&client->dev, &adau1701_codec_drv, &adau1701_dai, 1); @@ -522,6 +554,7 @@ static struct i2c_driver adau1701_i2c_driver = { .driver = { .name = "adau1701", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(adau1701_dt_ids), }, .probe = adau1701_i2c_probe, .remove = adau1701_i2c_remove, -- cgit v1.2.3 From 92ab1aab59c61b3e05200b9aa0e05ab770059142 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 24 May 2013 10:29:22 +0200 Subject: regmap: Make regmap-mmio usable from atomic contexts regmap-mmio uses a spinlock with spin_lock() and spin_unlock() for locking. To be able to use the regmap API from different contexts (atomic vs non-atomic), without the risk of race conditions, we need to use spin_lock_irqsave() and spin_lock_irqrestore() instead. A new field, the spinlock_flags field, is added to regmap struct to store the flags between regmap_{,un}lock_spinlock(). The spinlock_flags field itself is also protected by the spinlock. Thanks to Stephen Warren for the suggestion of this particular solution. Signed-off-by: Lars-Peter Clausen Reviewed-by: Stephen Warren Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 1 + drivers/base/regmap/regmap.c | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index b33a4ff67adf..ae23d8391aa0 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -52,6 +52,7 @@ struct regmap_async { struct regmap { struct mutex mutex; spinlock_t spinlock; + unsigned long spinlock_flags; regmap_lock lock; regmap_unlock unlock; void *lock_arg; /* This is passed to lock/unlock functions */ diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 307f5a1c1fe8..1a01553189b3 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -302,13 +302,16 @@ static void regmap_unlock_mutex(void *__map) static void regmap_lock_spinlock(void *__map) { struct regmap *map = __map; - spin_lock(&map->spinlock); + unsigned long flags; + + spin_lock_irqsave(&map->spinlock, flags); + map->spinlock_flags = flags; } static void regmap_unlock_spinlock(void *__map) { struct regmap *map = __map; - spin_unlock(&map->spinlock); + spin_unlock_irqrestore(&map->spinlock, map->spinlock_flags); } static void dev_get_regmap_release(struct device *dev, void *res) -- cgit v1.2.3 From 33963e308e98064ce89d961ffeede2fb055f8ffc Mon Sep 17 00:00:00 2001 From: Kevin Hao Date: Sat, 25 May 2013 19:36:25 +0800 Subject: PCI: Add 0x prefix to BAR register position in __pci_read_base() We print the BAR register's position in hexadecimal format, so it is more readable if 0x prefix is added. [bhelgaas: keep dev_printk(), not dev_dbg(), so this is always in dmesg] Signed-off-by: Kevin Hao Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 70f10fa3c1b2..d40cd05bbf64 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -278,9 +278,9 @@ out: pci_write_config_word(dev, PCI_COMMAND, orig_cmd); if (bar_too_big) - dev_err(&dev->dev, "reg %x: can't handle 64-bit BAR\n", pos); + dev_err(&dev->dev, "reg 0x%x: can't handle 64-bit BAR\n", pos); if (res->flags && !bar_disabled) - dev_printk(KERN_DEBUG, &dev->dev, "reg %x: %pR\n", pos, res); + dev_printk(KERN_DEBUG, &dev->dev, "reg 0x%x: %pR\n", pos, res); return (res->flags & IORESOURCE_MEM_64) ? 1 : 0; } -- cgit v1.2.3 From 96ddef25b24a6159e78fb53c1b13336914ff1154 Mon Sep 17 00:00:00 2001 From: Kevin Hao Date: Sat, 25 May 2013 19:36:26 +0800 Subject: PCI: Consolidate calls to pcibios_bus_to_resource() in __pci_read_base() Since we will invoke pcibios_bus_to_resource() unconditionally if we don't goto fail, move it out of if/else wrap. No function change. Signed-off-by: Kevin Hao Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index d40cd05bbf64..cd7b6de9376c 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -250,12 +250,10 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, pci_write_config_dword(dev, pos + 4, 0); region.start = 0; region.end = sz64; - pcibios_bus_to_resource(dev, res, ®ion); bar_disabled = true; } else { region.start = l64; region.end = l64 + sz64; - pcibios_bus_to_resource(dev, res, ®ion); } } else { sz = pci_size(l, sz, mask); @@ -265,9 +263,10 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, region.start = l; region.end = l + sz; - pcibios_bus_to_resource(dev, res, ®ion); } + pcibios_bus_to_resource(dev, res, ®ion); + goto out; -- cgit v1.2.3 From cf4d1cf5ac5e7d2b886af6ed906ea0dcdc5b6855 Mon Sep 17 00:00:00 2001 From: Kevin Hao Date: Sat, 25 May 2013 19:36:27 +0800 Subject: PCI: Unset resource if initial BAR value is invalid The initial BAR value in the following example is invalid: pci_bus 0000:00: root bus resource [mem 0xa0000000-0xbfffffff] (bus address [0xe0000000-0xffffffff]) pci 0000:01:00.0: reg 10: initial BAR value: 0xa0000000 pci 0000:01:00.0: reg 10: [mem 0xa0000000-0xa000007f 64bit] bus_to_resource(0xa0000000) yields 0xa0000000 because there's no host bridge window whose bus address range contains 0xa0000000. But CPU accesses to 0xa0000000 appear on the bus at 0xe0000000, so they will not be claimed if the BAR contains 0xa0000000. If we find a BAR where resource_to_bus(bus_to_resource(A)) != A, we can work around this problem by reassigning the BAR. [bhelgaas: changelog, comment] Reference: https://lkml.kernel.org/r/1368536876-27307-3-git-send-email-haokexin@gmail.com Signed-off-by: Kevin Hao Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index cd7b6de9376c..fe5b50bd7536 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -170,7 +170,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, { u32 l, sz, mask; u16 orig_cmd; - struct pci_bus_region region; + struct pci_bus_region region, inverted_region; bool bar_too_big = false, bar_disabled = false; mask = type ? PCI_ROM_ADDRESS_MASK : ~0; @@ -266,6 +266,26 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, } pcibios_bus_to_resource(dev, res, ®ion); + pcibios_resource_to_bus(dev, &inverted_region, res); + + /* + * If "A" is a BAR value (a bus address), "bus_to_resource(A)" is + * the corresponding resource address (the physical address used by + * the CPU. Converting that resource address back to a bus address + * should yield the original BAR value: + * + * resource_to_bus(bus_to_resource(A)) == A + * + * If it doesn't, CPU accesses to "bus_to_resource(A)" will not + * be claimed by the device. + */ + if (inverted_region.start != region.start) { + dev_info(&dev->dev, "reg 0x%x: initial BAR value %pa invalid; forcing reassignment\n", + pos, ®ion.start); + res->flags |= IORESOURCE_UNSET; + res->end -= res->start; + res->start = 0; + } goto out; -- cgit v1.2.3 From 6ee0b4b0ef871632b067f216b3032bf8db93c510 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Fri, 24 May 2013 12:39:15 +0200 Subject: ASoC: ux500: Drop pinctrl sleep support Drop pinctrl default/sleep state switching code, as it was breaking the capture interface by putting the I2S pins in hi-z mode regardless of its usage status, and not giving any real benefit. Pinctrl default mode configuration is already managed automatically by a specific pinctrl hog. Signed-off-by: Fabio Baltieri Acked-by: Linus Walleij Signed-off-by: Mark Brown --- sound/soc/ux500/ux500_msp_i2s.c | 56 ++--------------------------------------- sound/soc/ux500/ux500_msp_i2s.h | 6 ----- 2 files changed, 2 insertions(+), 60 deletions(-) diff --git a/sound/soc/ux500/ux500_msp_i2s.c b/sound/soc/ux500/ux500_msp_i2s.c index f2db6c90a9e2..b029b2d673d7 100644 --- a/sound/soc/ux500/ux500_msp_i2s.c +++ b/sound/soc/ux500/ux500_msp_i2s.c @@ -15,7 +15,6 @@ #include #include -#include #include #include #include @@ -26,9 +25,6 @@ #include "ux500_msp_i2s.h" -/* MSP1/3 Tx/Rx usage protection */ -static DEFINE_SPINLOCK(msp_rxtx_lock); - /* Protocol desciptors */ static const struct msp_protdesc prot_descs[] = { { /* I2S */ @@ -356,24 +352,8 @@ static int configure_multichannel(struct ux500_msp *msp, static int enable_msp(struct ux500_msp *msp, struct ux500_msp_config *config) { - int status = 0, retval = 0; + int status = 0; u32 reg_val_DMACR, reg_val_GCR; - unsigned long flags; - - /* Check msp state whether in RUN or CONFIGURED Mode */ - if (msp->msp_state == MSP_STATE_IDLE) { - spin_lock_irqsave(&msp_rxtx_lock, flags); - if (msp->pinctrl_rxtx_ref == 0 && - !(IS_ERR(msp->pinctrl_p) || IS_ERR(msp->pinctrl_def))) { - retval = pinctrl_select_state(msp->pinctrl_p, - msp->pinctrl_def); - if (retval) - pr_err("could not set MSP defstate\n"); - } - if (!retval) - msp->pinctrl_rxtx_ref++; - spin_unlock_irqrestore(&msp_rxtx_lock, flags); - } /* Configure msp with protocol dependent settings */ configure_protocol(msp, config); @@ -630,8 +610,7 @@ int ux500_msp_i2s_trigger(struct ux500_msp *msp, int cmd, int direction) int ux500_msp_i2s_close(struct ux500_msp *msp, unsigned int dir) { - int status = 0, retval = 0; - unsigned long flags; + int status = 0; dev_dbg(msp->dev, "%s: Enter (dir = 0x%01x).\n", __func__, dir); @@ -643,18 +622,6 @@ int ux500_msp_i2s_close(struct ux500_msp *msp, unsigned int dir) (~(FRAME_GEN_ENABLE | SRG_ENABLE))), msp->registers + MSP_GCR); - spin_lock_irqsave(&msp_rxtx_lock, flags); - WARN_ON(!msp->pinctrl_rxtx_ref); - msp->pinctrl_rxtx_ref--; - if (msp->pinctrl_rxtx_ref == 0 && - !(IS_ERR(msp->pinctrl_p) || IS_ERR(msp->pinctrl_sleep))) { - retval = pinctrl_select_state(msp->pinctrl_p, - msp->pinctrl_sleep); - if (retval) - pr_err("could not set MSP sleepstate\n"); - } - spin_unlock_irqrestore(&msp_rxtx_lock, flags); - writel(0, msp->registers + MSP_GCR); writel(0, msp->registers + MSP_TCF); writel(0, msp->registers + MSP_RCF); @@ -743,25 +710,6 @@ int ux500_msp_i2s_init_msp(struct platform_device *pdev, dev_dbg(&pdev->dev, "I2S device-name: '%s'\n", i2s_cont->name); msp->i2s_cont = i2s_cont; - msp->pinctrl_p = pinctrl_get(msp->dev); - if (IS_ERR(msp->pinctrl_p)) - dev_err(&pdev->dev, "could not get MSP pinctrl\n"); - else { - msp->pinctrl_def = pinctrl_lookup_state(msp->pinctrl_p, - PINCTRL_STATE_DEFAULT); - if (IS_ERR(msp->pinctrl_def)) { - dev_err(&pdev->dev, - "could not get MSP defstate (%li)\n", - PTR_ERR(msp->pinctrl_def)); - } - msp->pinctrl_sleep = pinctrl_lookup_state(msp->pinctrl_p, - PINCTRL_STATE_SLEEP); - if (IS_ERR(msp->pinctrl_sleep)) - dev_err(&pdev->dev, - "could not get MSP idlestate (%li)\n", - PTR_ERR(msp->pinctrl_def)); - } - return 0; } diff --git a/sound/soc/ux500/ux500_msp_i2s.h b/sound/soc/ux500/ux500_msp_i2s.h index e5cd105c90f9..8ce014eb37e8 100644 --- a/sound/soc/ux500/ux500_msp_i2s.h +++ b/sound/soc/ux500/ux500_msp_i2s.h @@ -528,12 +528,6 @@ struct ux500_msp { int loopback_enable; u32 backup_regs[MAX_MSP_BACKUP_REGS]; unsigned int f_bitclk; - /* Pin modes */ - struct pinctrl *pinctrl_p; - struct pinctrl_state *pinctrl_def; - struct pinctrl_state *pinctrl_sleep; - /* Reference Count */ - int pinctrl_rxtx_ref; }; struct ux500_msp_dma_params { -- cgit v1.2.3 From 7f92581b21707bfe09e14410283692b658b9ef10 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Fri, 24 May 2013 12:39:16 +0200 Subject: ASoC: ab8500-codec: Move codec ops on a separate structure Define ab8500 codec operations structure on its own rather than inline with snd_soc_dai_drivers to clean up the code and make the style coherent with other codec drivers. Signed-off-by: Fabio Baltieri Signed-off-by: Mark Brown --- sound/soc/codecs/ab8500-codec.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c index 4ca45b9d9625..b8ba0adacfce 100644 --- a/sound/soc/codecs/ab8500-codec.c +++ b/sound/soc/codecs/ab8500-codec.c @@ -2380,6 +2380,11 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai, return 0; } +static const struct snd_soc_dai_ops ab8500_codec_ops = { + .set_fmt = ab8500_codec_set_dai_fmt, + .set_tdm_slot = ab8500_codec_set_dai_tdm_slot, +}; + static struct snd_soc_dai_driver ab8500_codec_dai[] = { { .name = "ab8500-codec-dai.0", @@ -2391,12 +2396,7 @@ static struct snd_soc_dai_driver ab8500_codec_dai[] = { .rates = AB8500_SUPPORTED_RATE, .formats = AB8500_SUPPORTED_FMT, }, - .ops = (struct snd_soc_dai_ops[]) { - { - .set_tdm_slot = ab8500_codec_set_dai_tdm_slot, - .set_fmt = ab8500_codec_set_dai_fmt, - } - }, + .ops = &ab8500_codec_ops, .symmetric_rates = 1 }, { @@ -2409,12 +2409,7 @@ static struct snd_soc_dai_driver ab8500_codec_dai[] = { .rates = AB8500_SUPPORTED_RATE, .formats = AB8500_SUPPORTED_FMT, }, - .ops = (struct snd_soc_dai_ops[]) { - { - .set_tdm_slot = ab8500_codec_set_dai_tdm_slot, - .set_fmt = ab8500_codec_set_dai_fmt, - } - }, + .ops = &ab8500_codec_ops, .symmetric_rates = 1 } }; -- cgit v1.2.3 From b7230d7e4c1f6a87ddb96dbc106435e0dfee0f37 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Fri, 24 May 2013 12:39:17 +0200 Subject: ASoC: ux500: Drop dangling struct i2s_controller Drop struct i2s_controller from the ux500 ASoC driver as right now it is instantiated but not used anywhere. Also drop a mismatched device_unregister in the process. Signed-off-by: Fabio Baltieri Signed-off-by: Mark Brown --- sound/soc/ux500/ux500_msp_i2s.c | 19 ------------------- sound/soc/ux500/ux500_msp_i2s.h | 12 ------------ 2 files changed, 31 deletions(-) diff --git a/sound/soc/ux500/ux500_msp_i2s.c b/sound/soc/ux500/ux500_msp_i2s.c index b029b2d673d7..cba0e86833e9 100644 --- a/sound/soc/ux500/ux500_msp_i2s.c +++ b/sound/soc/ux500/ux500_msp_i2s.c @@ -649,7 +649,6 @@ int ux500_msp_i2s_init_msp(struct platform_device *pdev, struct msp_i2s_platform_data *platform_data) { struct resource *res = NULL; - struct i2s_controller *i2s_cont; struct device_node *np = pdev->dev.of_node; struct ux500_msp *msp; @@ -694,22 +693,6 @@ int ux500_msp_i2s_init_msp(struct platform_device *pdev, msp->msp_state = MSP_STATE_IDLE; msp->loopback_enable = 0; - /* I2S-controller is allocated and added in I2S controller class. */ - i2s_cont = devm_kzalloc(&pdev->dev, sizeof(*i2s_cont), GFP_KERNEL); - if (!i2s_cont) { - dev_err(&pdev->dev, - "%s: ERROR: Failed to allocate I2S-controller!\n", - __func__); - return -ENOMEM; - } - i2s_cont->dev.parent = &pdev->dev; - i2s_cont->data = (void *)msp; - i2s_cont->id = (s16)msp->id; - snprintf(i2s_cont->name, sizeof(i2s_cont->name), "ux500-msp-i2s.%04x", - msp->id); - dev_dbg(&pdev->dev, "I2S device-name: '%s'\n", i2s_cont->name); - msp->i2s_cont = i2s_cont; - return 0; } @@ -717,8 +700,6 @@ void ux500_msp_i2s_cleanup_msp(struct platform_device *pdev, struct ux500_msp *msp) { dev_dbg(msp->dev, "%s: Enter (id = %d).\n", __func__, msp->id); - - device_unregister(&msp->i2s_cont->dev); } MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/ux500/ux500_msp_i2s.h b/sound/soc/ux500/ux500_msp_i2s.h index 8ce014eb37e8..ccfcc32b1c2b 100644 --- a/sound/soc/ux500/ux500_msp_i2s.h +++ b/sound/soc/ux500/ux500_msp_i2s.h @@ -469,17 +469,6 @@ struct i2s_message { size_t period_len; }; -struct i2s_controller { - struct module *owner; - unsigned int id; - unsigned int class; - const struct i2s_algorithm *algo; /* the algorithm to access the bus */ - void *data; - struct mutex bus_lock; - struct device dev; /* the controller device */ - char name[48]; -}; - struct ux500_msp_config { unsigned int f_inputclk; unsigned int rx_clk_sel; @@ -515,7 +504,6 @@ struct ux500_msp { enum enum_i2s_controller id; void __iomem *registers; struct device *dev; - struct i2s_controller *i2s_cont; struct stedma40_chan_cfg *dma_cfg_rx; struct stedma40_chan_cfg *dma_cfg_tx; struct dma_chan *tx_pipeid; -- cgit v1.2.3 From 2a357137fac4e2e92d13d37b161a6ff4535eecc6 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Fri, 24 May 2013 12:39:18 +0200 Subject: ASoC: ux500: Drop unused code from msp headers Drop unused fields and structures from ux500_msp_i2s header file, as those looks like leftover from a previous implementation of the driver. Signed-off-by: Fabio Baltieri Signed-off-by: Mark Brown --- sound/soc/ux500/ux500_msp_dai.h | 2 -- sound/soc/ux500/ux500_msp_i2s.h | 31 ------------------------------- 2 files changed, 33 deletions(-) diff --git a/sound/soc/ux500/ux500_msp_dai.h b/sound/soc/ux500/ux500_msp_dai.h index f53104359f15..c7212825fe4c 100644 --- a/sound/soc/ux500/ux500_msp_dai.h +++ b/sound/soc/ux500/ux500_msp_dai.h @@ -58,8 +58,6 @@ struct ux500_msp_i2s_drvdata { unsigned int rx_mask; int slots; int slot_width; - u8 configured; - int data_delay; /* Clocks */ unsigned int master_clk; diff --git a/sound/soc/ux500/ux500_msp_i2s.h b/sound/soc/ux500/ux500_msp_i2s.h index ccfcc32b1c2b..d5e41763c9c7 100644 --- a/sound/soc/ux500/ux500_msp_i2s.h +++ b/sound/soc/ux500/ux500_msp_i2s.h @@ -341,11 +341,6 @@ enum msp_compress_mode { MSP_COMPRESS_MODE_A_LAW = 3 }; -enum msp_spi_burst_mode { - MSP_SPI_BURST_MODE_DISABLE = 0, - MSP_SPI_BURST_MODE_ENABLE = 1 -}; - enum msp_expand_mode { MSP_EXPAND_MODE_LINEAR = 0, MSP_EXPAND_MODE_LINEAR_SIGNED = 1, @@ -454,21 +449,6 @@ struct msp_protdesc { u32 clocks_per_frame; }; -struct i2s_message { - enum i2s_direction_t i2s_direction; - void *txdata; - void *rxdata; - size_t txbytes; - size_t rxbytes; - int dma_flag; - int tx_offset; - int rx_offset; - bool cyclic_dma; - dma_addr_t buf_addr; - size_t buf_len; - size_t period_len; -}; - struct ux500_msp_config { unsigned int f_inputclk; unsigned int rx_clk_sel; @@ -480,8 +460,6 @@ struct ux500_msp_config { unsigned int tx_fsync_sel; unsigned int rx_fifo_config; unsigned int tx_fifo_config; - unsigned int spi_clk_mode; - unsigned int spi_burst_mode; unsigned int loopback_enable; unsigned int tx_data_enable; unsigned int default_protdesc; @@ -491,13 +469,9 @@ struct ux500_msp_config { unsigned int direction; unsigned int protocol; unsigned int frame_freq; - unsigned int frame_size; enum msp_data_size data_size; unsigned int def_elem_len; unsigned int iodelay; - void (*handler) (void *data); - void *tx_callback_data; - void *rx_callback_data; }; struct ux500_msp { @@ -506,15 +480,10 @@ struct ux500_msp { struct device *dev; struct stedma40_chan_cfg *dma_cfg_rx; struct stedma40_chan_cfg *dma_cfg_tx; - struct dma_chan *tx_pipeid; - struct dma_chan *rx_pipeid; enum msp_state msp_state; - int (*transfer) (struct ux500_msp *msp, struct i2s_message *message); - struct timer_list notify_timer; int def_elem_len; unsigned int dir_busy; int loopback_enable; - u32 backup_regs[MAX_MSP_BACKUP_REGS]; unsigned int f_bitclk; }; -- cgit v1.2.3 From 06b9671ee69d48a1436077805903e3d9c1ed9662 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Fri, 24 May 2013 12:39:19 +0200 Subject: ASoC: ux500: Add missing mop500_ab8500.h include Add a missing include that was resulting in some sparse warning for non-static structure without forward declaration. Signed-off-by: Fabio Baltieri Signed-off-by: Mark Brown --- sound/soc/ux500/mop500_ab8500.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/ux500/mop500_ab8500.c b/sound/soc/ux500/mop500_ab8500.c index 884a36224fb1..5e0f14634271 100644 --- a/sound/soc/ux500/mop500_ab8500.c +++ b/sound/soc/ux500/mop500_ab8500.c @@ -24,6 +24,7 @@ #include "ux500_pcm.h" #include "ux500_msp_dai.h" +#include "mop500_ab8500.h" #include "../codecs/ab8500-codec.h" #define TX_SLOT_MONO 0x0008 -- cgit v1.2.3 From f82030f978ae21ee790a90610ff21ce72667958e Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Fri, 24 May 2013 12:39:20 +0200 Subject: ASoC: ux500: Drop redundant msp id enumerations Ux500 has two equivalent enum for device id, one in platform_data and one in a local header. Fix this by dropping the local one. Signed-off-by: Fabio Baltieri Signed-off-by: Mark Brown --- sound/soc/ux500/ux500_msp_i2s.h | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/sound/soc/ux500/ux500_msp_i2s.h b/sound/soc/ux500/ux500_msp_i2s.h index d5e41763c9c7..189a3751993b 100644 --- a/sound/soc/ux500/ux500_msp_i2s.h +++ b/sound/soc/ux500/ux500_msp_i2s.h @@ -16,6 +16,7 @@ #define UX500_MSP_I2S_H #include +#include #define MSP_INPUT_FREQ_APB 48000000 @@ -365,13 +366,6 @@ enum msp_protocol { */ #define MAX_MSP_BACKUP_REGS 36 -enum enum_i2s_controller { - MSP_0_I2S_CONTROLLER = 0, - MSP_1_I2S_CONTROLLER, - MSP_2_I2S_CONTROLLER, - MSP_3_I2S_CONTROLLER, -}; - enum i2s_direction_t { MSP_DIR_TX = 0x01, MSP_DIR_RX = 0x02, @@ -475,7 +469,7 @@ struct ux500_msp_config { }; struct ux500_msp { - enum enum_i2s_controller id; + enum msp_i2s_id id; void __iomem *registers; struct device *dev; struct stedma40_chan_cfg *dma_cfg_rx; -- cgit v1.2.3 From 079956742452494326081349a66942654498cafa Mon Sep 17 00:00:00 2001 From: Zhang Yanfei Date: Mon, 29 Apr 2013 11:55:10 -0700 Subject: ipvs: change type of netns_ipvs->sysctl_sync_qlen_max This member of struct netns_ipvs is calculated from nr_free_buffer_pages so change its type to unsigned long in case of overflow. Also, type of its related proc var sync_qlen_max and the return type of function sysctl_sync_qlen_max() should be changed to unsigned long, too. Besides, the type of ipvs_master_sync_state->sync_queue_len should be changed to unsigned long accordingly. Signed-off-by: Zhang Yanfei Cc: Julian Anastasov Cc: David Miller Signed-off-by: Andrew Morton Signed-off-by: Simon Horman --- include/net/ip_vs.h | 8 ++++---- net/netfilter/ipvs/ip_vs_ctl.c | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 4c062ccff9aa..4405886980c7 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -905,7 +905,7 @@ struct ip_vs_app { struct ipvs_master_sync_state { struct list_head sync_queue; struct ip_vs_sync_buff *sync_buff; - int sync_queue_len; + unsigned long sync_queue_len; unsigned int sync_queue_delay; struct task_struct *master_thread; struct delayed_work master_wakeup_work; @@ -998,7 +998,7 @@ struct netns_ipvs { int sysctl_snat_reroute; int sysctl_sync_ver; int sysctl_sync_ports; - int sysctl_sync_qlen_max; + unsigned long sysctl_sync_qlen_max; int sysctl_sync_sock_size; int sysctl_cache_bypass; int sysctl_expire_nodest_conn; @@ -1085,7 +1085,7 @@ static inline int sysctl_sync_ports(struct netns_ipvs *ipvs) return ACCESS_ONCE(ipvs->sysctl_sync_ports); } -static inline int sysctl_sync_qlen_max(struct netns_ipvs *ipvs) +static inline unsigned long sysctl_sync_qlen_max(struct netns_ipvs *ipvs) { return ipvs->sysctl_sync_qlen_max; } @@ -1138,7 +1138,7 @@ static inline int sysctl_sync_ports(struct netns_ipvs *ipvs) return 1; } -static inline int sysctl_sync_qlen_max(struct netns_ipvs *ipvs) +static inline unsigned long sysctl_sync_qlen_max(struct netns_ipvs *ipvs) { return IPVS_SYNC_QLEN_MAX; } diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 5b142fb16480..70146496e73a 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -1716,9 +1716,9 @@ static struct ctl_table vs_vars[] = { }, { .procname = "sync_qlen_max", - .maxlen = sizeof(int), + .maxlen = sizeof(unsigned long), .mode = 0644, - .proc_handler = proc_dointvec, + .proc_handler = proc_doulongvec_minmax, }, { .procname = "sync_sock_size", -- cgit v1.2.3 From 6d0bfe22611602f36617bc7aa2ffa1bbb2f54c67 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Wed, 22 May 2013 20:17:31 +0000 Subject: net: ipv6: Add IPv6 support to the ping socket. This adds the ability to send ICMPv6 echo requests without a raw socket. The equivalent ability for ICMPv4 was added in 2011. Instead of having separate code paths for IPv4 and IPv6, make most of the code in net/ipv4/ping.c dual-stack and only add a few IPv6-specific bits (like the protocol definition) to a new net/ipv6/ping.c. Hopefully this will reduce divergence and/or duplication of bugs in the future. Caveats: - Setting options via ancillary data (e.g., using IPV6_PKTINFO to specify the outgoing interface) is not yet supported. - There are no separate security settings for IPv4 and IPv6; everything is controlled by /proc/net/ipv4/ping_group_range. - The proc interface does not yet display IPv6 ping sockets properly. Tested with a patched copy of ping6 and using raw socket calls. Compiles and works with all of CONFIG_IPV6={n,m,y}. Signed-off-by: Lorenzo Colitti Signed-off-by: David S. Miller --- include/net/ipv6.h | 6 + include/net/ping.h | 49 ++++- include/net/transp_v6.h | 3 + net/ipv4/icmp.c | 5 +- net/ipv4/ping.c | 557 ++++++++++++++++++++++++++++++++++-------------- net/ipv6/Makefile | 2 +- net/ipv6/af_inet6.c | 12 ++ net/ipv6/icmp.c | 19 +- net/ipv6/ping.c | 216 +++++++++++++++++++ 9 files changed, 698 insertions(+), 171 deletions(-) create mode 100644 net/ipv6/ping.c diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 0810aa57c780..ab47582f6c0b 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -260,6 +260,12 @@ static inline void fl6_sock_release(struct ip6_flowlabel *fl) extern void icmpv6_notify(struct sk_buff *skb, u8 type, u8 code, __be32 info); +int icmpv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6, + struct icmp6hdr *thdr, int len); + +struct dst_entry *icmpv6_route_lookup(struct net *net, struct sk_buff *skb, + struct sock *sk, struct flowi6 *fl6); + extern int ip6_ra_control(struct sock *sk, int sel); extern int ipv6_parse_hopopts(struct sk_buff *skb); diff --git a/include/net/ping.h b/include/net/ping.h index 682b5ae9af51..9242fa090d3d 100644 --- a/include/net/ping.h +++ b/include/net/ping.h @@ -13,6 +13,7 @@ #ifndef _PING_H #define _PING_H +#include #include /* PING_HTABLE_SIZE must be power of 2 */ @@ -28,6 +29,18 @@ */ #define GID_T_MAX (((gid_t)~0U) >> 1) +/* Compatibility glue so we can support IPv6 when it's compiled as a module */ +struct pingv6_ops { + int (*ipv6_recv_error)(struct sock *sk, struct msghdr *msg, int len); + int (*ip6_datagram_recv_ctl)(struct sock *sk, struct msghdr *msg, + struct sk_buff *skb); + int (*icmpv6_err_convert)(u8 type, u8 code, int *err); + void (*ipv6_icmp_error)(struct sock *sk, struct sk_buff *skb, int err, + __be16 port, u32 info, u8 *payload); + int (*ipv6_chk_addr)(struct net *net, const struct in6_addr *addr, + struct net_device *dev, int strict); +}; + struct ping_table { struct hlist_nulls_head hash[PING_HTABLE_SIZE]; rwlock_t lock; @@ -39,10 +52,39 @@ struct ping_iter_state { }; extern struct proto ping_prot; +extern struct ping_table ping_table; +#if IS_ENABLED(CONFIG_IPV6) +extern struct pingv6_ops pingv6_ops; +#endif +struct pingfakehdr { + struct icmphdr icmph; + struct iovec *iov; + sa_family_t family; + __wsum wcheck; +}; -extern void ping_rcv(struct sk_buff *); -extern void ping_err(struct sk_buff *, u32 info); +int ping_get_port(struct sock *sk, unsigned short ident); +void ping_hash(struct sock *sk); +void ping_unhash(struct sock *sk); + +int ping_init_sock(struct sock *sk); +void ping_close(struct sock *sk, long timeout); +int ping_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len); +void ping_err(struct sk_buff *skb, int offset, u32 info); +int ping_getfrag(void *from, char *to, int offset, int fraglen, int odd, + struct sk_buff *); + +int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, + size_t len, int noblock, int flags, int *addr_len); +int ping_common_sendmsg(int family, struct msghdr *msg, size_t len, + void *user_icmph, size_t icmph_len); +int ping_v4_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, + size_t len); +int ping_v6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, + size_t len); +int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb); +void ping_rcv(struct sk_buff *skb); #ifdef CONFIG_PROC_FS extern int __init ping_proc_init(void); @@ -50,6 +92,7 @@ extern void ping_proc_exit(void); #endif void __init ping_init(void); - +int __init pingv6_init(void); +void pingv6_exit(void); #endif /* _PING_H */ diff --git a/include/net/transp_v6.h b/include/net/transp_v6.h index 938b7fd11204..eb40e71ff2ee 100644 --- a/include/net/transp_v6.h +++ b/include/net/transp_v6.h @@ -11,6 +11,7 @@ extern struct proto rawv6_prot; extern struct proto udpv6_prot; extern struct proto udplitev6_prot; extern struct proto tcpv6_prot; +extern struct proto pingv6_prot; struct flowi6; @@ -21,6 +22,8 @@ extern int ipv6_frag_init(void); extern void ipv6_frag_exit(void); /* transport protocols */ +extern int pingv6_init(void); +extern void pingv6_exit(void); extern int rawv6_init(void); extern void rawv6_exit(void); extern int udpv6_init(void); diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 76e10b47e053..562efd91f457 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -939,7 +939,8 @@ error: void icmp_err(struct sk_buff *skb, u32 info) { struct iphdr *iph = (struct iphdr *)skb->data; - struct icmphdr *icmph = (struct icmphdr *)(skb->data+(iph->ihl<<2)); + int offset = iph->ihl<<2; + struct icmphdr *icmph = (struct icmphdr *)(skb->data + offset); int type = icmp_hdr(skb)->type; int code = icmp_hdr(skb)->code; struct net *net = dev_net(skb->dev); @@ -949,7 +950,7 @@ void icmp_err(struct sk_buff *skb, u32 info) * triggered by ICMP_ECHOREPLY which sent from kernel. */ if (icmph->type != ICMP_ECHOREPLY) { - ping_err(skb, info); + ping_err(skb, offset, info); return; } diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index 7d93d62cd5fd..71f6ad02fa67 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c @@ -33,7 +33,6 @@ #include #include #include -#include #include #include #include @@ -46,8 +45,18 @@ #include #include +#if IS_ENABLED(CONFIG_IPV6) +#include +#include +#include +#include +#include +#endif -static struct ping_table ping_table; + +struct ping_table ping_table; +struct pingv6_ops pingv6_ops; +EXPORT_SYMBOL_GPL(pingv6_ops); static u16 ping_port_rover; @@ -58,6 +67,7 @@ static inline int ping_hashfn(struct net *net, unsigned int num, unsigned int ma pr_debug("hash(%d) = %d\n", num, res); return res; } +EXPORT_SYMBOL_GPL(ping_hash); static inline struct hlist_nulls_head *ping_hashslot(struct ping_table *table, struct net *net, unsigned int num) @@ -65,7 +75,7 @@ static inline struct hlist_nulls_head *ping_hashslot(struct ping_table *table, return &table->hash[ping_hashfn(net, num, PING_HTABLE_MASK)]; } -static int ping_v4_get_port(struct sock *sk, unsigned short ident) +int ping_get_port(struct sock *sk, unsigned short ident) { struct hlist_nulls_node *node; struct hlist_nulls_head *hlist; @@ -103,6 +113,10 @@ next_port: ping_portaddr_for_each_entry(sk2, node, hlist) { isk2 = inet_sk(sk2); + /* BUG? Why is this reuse and not reuseaddr? ping.c + * doesn't turn off SO_REUSEADDR, and it doesn't expect + * that other ping processes can steal its packets. + */ if ((isk2->inet_num == ident) && (sk2 != sk) && (!sk2->sk_reuse || !sk->sk_reuse)) @@ -125,17 +139,18 @@ fail: write_unlock_bh(&ping_table.lock); return 1; } +EXPORT_SYMBOL_GPL(ping_get_port); -static void ping_v4_hash(struct sock *sk) +void ping_hash(struct sock *sk) { - pr_debug("ping_v4_hash(sk->port=%u)\n", inet_sk(sk)->inet_num); + pr_debug("ping_hash(sk->port=%u)\n", inet_sk(sk)->inet_num); BUG(); /* "Please do not press this button again." */ } -static void ping_v4_unhash(struct sock *sk) +void ping_unhash(struct sock *sk) { struct inet_sock *isk = inet_sk(sk); - pr_debug("ping_v4_unhash(isk=%p,isk->num=%u)\n", isk, isk->inet_num); + pr_debug("ping_unhash(isk=%p,isk->num=%u)\n", isk, isk->inet_num); if (sk_hashed(sk)) { write_lock_bh(&ping_table.lock); hlist_nulls_del(&sk->sk_nulls_node); @@ -146,31 +161,61 @@ static void ping_v4_unhash(struct sock *sk) write_unlock_bh(&ping_table.lock); } } +EXPORT_SYMBOL_GPL(ping_unhash); -static struct sock *ping_v4_lookup(struct net *net, __be32 saddr, __be32 daddr, - u16 ident, int dif) +static struct sock *ping_lookup(struct net *net, struct sk_buff *skb, u16 ident) { struct hlist_nulls_head *hslot = ping_hashslot(&ping_table, net, ident); struct sock *sk = NULL; struct inet_sock *isk; struct hlist_nulls_node *hnode; + int dif = skb->dev->ifindex; + + if (skb->protocol == htons(ETH_P_IP)) { + pr_debug("try to find: num = %d, daddr = %pI4, dif = %d\n", + (int)ident, &ip_hdr(skb)->daddr, dif); +#if IS_ENABLED(CONFIG_IPV6) + } else if (skb->protocol == htons(ETH_P_IPV6)) { + pr_debug("try to find: num = %d, daddr = %pI6c, dif = %d\n", + (int)ident, &ipv6_hdr(skb)->daddr, dif); +#endif + } - pr_debug("try to find: num = %d, daddr = %pI4, dif = %d\n", - (int)ident, &daddr, dif); read_lock_bh(&ping_table.lock); ping_portaddr_for_each_entry(sk, hnode, hslot) { isk = inet_sk(sk); - pr_debug("found: %p: num = %d, daddr = %pI4, dif = %d\n", sk, - (int)isk->inet_num, &isk->inet_rcv_saddr, - sk->sk_bound_dev_if); - pr_debug("iterate\n"); if (isk->inet_num != ident) continue; - if (isk->inet_rcv_saddr && isk->inet_rcv_saddr != daddr) - continue; + + if (skb->protocol == htons(ETH_P_IP) && + sk->sk_family == AF_INET) { + pr_debug("found: %p: num=%d, daddr=%pI4, dif=%d\n", sk, + (int) isk->inet_num, &isk->inet_rcv_saddr, + sk->sk_bound_dev_if); + + if (isk->inet_rcv_saddr && + isk->inet_rcv_saddr != ip_hdr(skb)->daddr) + continue; +#if IS_ENABLED(CONFIG_IPV6) + } else if (skb->protocol == htons(ETH_P_IPV6) && + sk->sk_family == AF_INET6) { + struct ipv6_pinfo *np = inet6_sk(sk); + + pr_debug("found: %p: num=%d, daddr=%pI6c, dif=%d\n", sk, + (int) isk->inet_num, + &inet6_sk(sk)->rcv_saddr, + sk->sk_bound_dev_if); + + if (!ipv6_addr_any(&np->rcv_saddr) && + !ipv6_addr_equal(&np->rcv_saddr, + &ipv6_hdr(skb)->daddr)) + continue; +#endif + } + if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif) continue; @@ -200,7 +245,7 @@ static void inet_get_ping_group_range_net(struct net *net, kgid_t *low, } -static int ping_init_sock(struct sock *sk) +int ping_init_sock(struct sock *sk) { struct net *net = sock_net(sk); kgid_t group = current_egid(); @@ -225,8 +270,9 @@ static int ping_init_sock(struct sock *sk) return -EACCES; } +EXPORT_SYMBOL_GPL(ping_init_sock); -static void ping_close(struct sock *sk, long timeout) +void ping_close(struct sock *sk, long timeout) { pr_debug("ping_close(sk=%p,sk->num=%u)\n", inet_sk(sk), inet_sk(sk)->inet_num); @@ -234,36 +280,122 @@ static void ping_close(struct sock *sk, long timeout) sk_common_release(sk); } +EXPORT_SYMBOL_GPL(ping_close); + +/* Checks the bind address and possibly modifies sk->sk_bound_dev_if. */ +int ping_check_bind_addr(struct sock *sk, struct inet_sock *isk, + struct sockaddr *uaddr, int addr_len) { + struct net *net = sock_net(sk); + if (sk->sk_family == AF_INET) { + struct sockaddr_in *addr = (struct sockaddr_in *) uaddr; + int chk_addr_ret; + + if (addr_len < sizeof(*addr)) + return -EINVAL; + + pr_debug("ping_check_bind_addr(sk=%p,addr=%pI4,port=%d)\n", + sk, &addr->sin_addr.s_addr, ntohs(addr->sin_port)); + + chk_addr_ret = inet_addr_type(net, addr->sin_addr.s_addr); + + if (addr->sin_addr.s_addr == htonl(INADDR_ANY)) + chk_addr_ret = RTN_LOCAL; + + if ((sysctl_ip_nonlocal_bind == 0 && + isk->freebind == 0 && isk->transparent == 0 && + chk_addr_ret != RTN_LOCAL) || + chk_addr_ret == RTN_MULTICAST || + chk_addr_ret == RTN_BROADCAST) + return -EADDRNOTAVAIL; + +#if IS_ENABLED(CONFIG_IPV6) + } else if (sk->sk_family == AF_INET6) { + struct sockaddr_in6 *addr = (struct sockaddr_in6 *) uaddr; + int addr_type, scoped, has_addr; + struct net_device *dev = NULL; + + if (addr_len < sizeof(*addr)) + return -EINVAL; + + pr_debug("ping_check_bind_addr(sk=%p,addr=%pI6c,port=%d)\n", + sk, addr->sin6_addr.s6_addr, ntohs(addr->sin6_port)); + + addr_type = ipv6_addr_type(&addr->sin6_addr); + scoped = __ipv6_addr_needs_scope_id(addr_type); + if ((addr_type != IPV6_ADDR_ANY && + !(addr_type & IPV6_ADDR_UNICAST)) || + (scoped && !addr->sin6_scope_id)) + return -EINVAL; + + rcu_read_lock(); + if (addr->sin6_scope_id) { + dev = dev_get_by_index_rcu(net, addr->sin6_scope_id); + if (!dev) { + rcu_read_unlock(); + return -ENODEV; + } + } + has_addr = pingv6_ops.ipv6_chk_addr(net, &addr->sin6_addr, dev, + scoped); + rcu_read_unlock(); + + if (!(isk->freebind || isk->transparent || has_addr || + addr_type == IPV6_ADDR_ANY)) + return -EADDRNOTAVAIL; + + if (scoped) + sk->sk_bound_dev_if = addr->sin6_scope_id; +#endif + } else { + return -EAFNOSUPPORT; + } + return 0; +} +void ping_set_saddr(struct sock *sk, struct sockaddr *saddr) +{ + if (saddr->sa_family == AF_INET) { + struct inet_sock *isk = inet_sk(sk); + struct sockaddr_in *addr = (struct sockaddr_in *) saddr; + isk->inet_rcv_saddr = isk->inet_saddr = addr->sin_addr.s_addr; +#if IS_ENABLED(CONFIG_IPV6) + } else if (saddr->sa_family == AF_INET6) { + struct sockaddr_in6 *addr = (struct sockaddr_in6 *) saddr; + struct ipv6_pinfo *np = inet6_sk(sk); + np->rcv_saddr = np->saddr = addr->sin6_addr; +#endif + } +} + +void ping_clear_saddr(struct sock *sk, int dif) +{ + sk->sk_bound_dev_if = dif; + if (sk->sk_family == AF_INET) { + struct inet_sock *isk = inet_sk(sk); + isk->inet_rcv_saddr = isk->inet_saddr = 0; +#if IS_ENABLED(CONFIG_IPV6) + } else if (sk->sk_family == AF_INET6) { + struct ipv6_pinfo *np = inet6_sk(sk); + memset(&np->rcv_saddr, 0, sizeof(np->rcv_saddr)); + memset(&np->saddr, 0, sizeof(np->saddr)); +#endif + } +} /* * We need our own bind because there are no privileged id's == local ports. * Moreover, we don't allow binding to multi- and broadcast addresses. */ -static int ping_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) +int ping_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) { - struct sockaddr_in *addr = (struct sockaddr_in *)uaddr; struct inet_sock *isk = inet_sk(sk); unsigned short snum; - int chk_addr_ret; int err; + int dif = sk->sk_bound_dev_if; - if (addr_len < sizeof(struct sockaddr_in)) - return -EINVAL; - - pr_debug("ping_v4_bind(sk=%p,sa_addr=%08x,sa_port=%d)\n", - sk, addr->sin_addr.s_addr, ntohs(addr->sin_port)); - - chk_addr_ret = inet_addr_type(sock_net(sk), addr->sin_addr.s_addr); - if (addr->sin_addr.s_addr == htonl(INADDR_ANY)) - chk_addr_ret = RTN_LOCAL; - - if ((sysctl_ip_nonlocal_bind == 0 && - isk->freebind == 0 && isk->transparent == 0 && - chk_addr_ret != RTN_LOCAL) || - chk_addr_ret == RTN_MULTICAST || - chk_addr_ret == RTN_BROADCAST) - return -EADDRNOTAVAIL; + err = ping_check_bind_addr(sk, isk, uaddr, addr_len); + if (err) + return err; lock_sock(sk); @@ -272,42 +404,50 @@ static int ping_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) goto out; err = -EADDRINUSE; - isk->inet_rcv_saddr = isk->inet_saddr = addr->sin_addr.s_addr; - snum = ntohs(addr->sin_port); - if (ping_v4_get_port(sk, snum) != 0) { - isk->inet_saddr = isk->inet_rcv_saddr = 0; + ping_set_saddr(sk, uaddr); + snum = ntohs(((struct sockaddr_in *)uaddr)->sin_port); + if (ping_get_port(sk, snum) != 0) { + ping_clear_saddr(sk, dif); goto out; } - pr_debug("after bind(): num = %d, daddr = %pI4, dif = %d\n", + pr_debug("after bind(): num = %d, dif = %d\n", (int)isk->inet_num, - &isk->inet_rcv_saddr, (int)sk->sk_bound_dev_if); err = 0; - if (isk->inet_rcv_saddr) + if ((sk->sk_family == AF_INET && isk->inet_rcv_saddr) || + (sk->sk_family == AF_INET6 && + !ipv6_addr_any(&inet6_sk(sk)->rcv_saddr))) sk->sk_userlocks |= SOCK_BINDADDR_LOCK; + if (snum) sk->sk_userlocks |= SOCK_BINDPORT_LOCK; isk->inet_sport = htons(isk->inet_num); isk->inet_daddr = 0; isk->inet_dport = 0; + +#if IS_ENABLED(CONFIG_IPV6) + if (sk->sk_family == AF_INET6) + memset(&inet6_sk(sk)->daddr, 0, sizeof(inet6_sk(sk)->daddr)); +#endif + sk_dst_reset(sk); out: release_sock(sk); pr_debug("ping_v4_bind -> %d\n", err); return err; } +EXPORT_SYMBOL_GPL(ping_bind); /* * Is this a supported type of ICMP message? */ -static inline int ping_supported(int type, int code) +static inline int ping_supported(int family, int type, int code) { - if (type == ICMP_ECHO && code == 0) - return 1; - return 0; + return (family == AF_INET && type == ICMP_ECHO && code == 0) || + (family == AF_INET6 && type == ICMPV6_ECHO_REQUEST && code == 0); } /* @@ -315,30 +455,42 @@ static inline int ping_supported(int type, int code) * sort of error condition. */ -static int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb); - -void ping_err(struct sk_buff *skb, u32 info) +void ping_err(struct sk_buff *skb, int offset, u32 info) { - struct iphdr *iph = (struct iphdr *)skb->data; - struct icmphdr *icmph = (struct icmphdr *)(skb->data+(iph->ihl<<2)); + int family; + struct icmphdr *icmph; struct inet_sock *inet_sock; - int type = icmp_hdr(skb)->type; - int code = icmp_hdr(skb)->code; + int type; + int code; struct net *net = dev_net(skb->dev); struct sock *sk; int harderr; int err; + if (skb->protocol == htons(ETH_P_IP)) { + family = AF_INET; + type = icmp_hdr(skb)->type; + code = icmp_hdr(skb)->code; + icmph = (struct icmphdr *)(skb->data + offset); + } else if (skb->protocol == htons(ETH_P_IPV6)) { + family = AF_INET6; + type = icmp6_hdr(skb)->icmp6_type; + code = icmp6_hdr(skb)->icmp6_code; + icmph = (struct icmphdr *) (skb->data + offset); + } else { + BUG(); + } + /* We assume the packet has already been checked by icmp_unreach */ - if (!ping_supported(icmph->type, icmph->code)) + if (!ping_supported(family, icmph->type, icmph->code)) return; - pr_debug("ping_err(type=%04x,code=%04x,id=%04x,seq=%04x)\n", type, - code, ntohs(icmph->un.echo.id), ntohs(icmph->un.echo.sequence)); + pr_debug("ping_err(proto=0x%x,type=%d,code=%d,id=%04x,seq=%04x)\n", + skb->protocol, type, code, ntohs(icmph->un.echo.id), + ntohs(icmph->un.echo.sequence)); - sk = ping_v4_lookup(net, iph->daddr, iph->saddr, - ntohs(icmph->un.echo.id), skb->dev->ifindex); + sk = ping_lookup(net, skb, ntohs(icmph->un.echo.id)); if (sk == NULL) { pr_debug("no socket, dropping\n"); return; /* No socket for error */ @@ -349,72 +501,83 @@ void ping_err(struct sk_buff *skb, u32 info) harderr = 0; inet_sock = inet_sk(sk); - switch (type) { - default: - case ICMP_TIME_EXCEEDED: - err = EHOSTUNREACH; - break; - case ICMP_SOURCE_QUENCH: - /* This is not a real error but ping wants to see it. - * Report it with some fake errno. */ - err = EREMOTEIO; - break; - case ICMP_PARAMETERPROB: - err = EPROTO; - harderr = 1; - break; - case ICMP_DEST_UNREACH: - if (code == ICMP_FRAG_NEEDED) { /* Path MTU discovery */ - ipv4_sk_update_pmtu(skb, sk, info); - if (inet_sock->pmtudisc != IP_PMTUDISC_DONT) { - err = EMSGSIZE; - harderr = 1; - break; + if (skb->protocol == htons(ETH_P_IP)) { + switch (type) { + default: + case ICMP_TIME_EXCEEDED: + err = EHOSTUNREACH; + break; + case ICMP_SOURCE_QUENCH: + /* This is not a real error but ping wants to see it. + * Report it with some fake errno. + */ + err = EREMOTEIO; + break; + case ICMP_PARAMETERPROB: + err = EPROTO; + harderr = 1; + break; + case ICMP_DEST_UNREACH: + if (code == ICMP_FRAG_NEEDED) { /* Path MTU discovery */ + ipv4_sk_update_pmtu(skb, sk, info); + if (inet_sock->pmtudisc != IP_PMTUDISC_DONT) { + err = EMSGSIZE; + harderr = 1; + break; + } + goto out; } - goto out; - } - err = EHOSTUNREACH; - if (code <= NR_ICMP_UNREACH) { - harderr = icmp_err_convert[code].fatal; - err = icmp_err_convert[code].errno; + err = EHOSTUNREACH; + if (code <= NR_ICMP_UNREACH) { + harderr = icmp_err_convert[code].fatal; + err = icmp_err_convert[code].errno; + } + break; + case ICMP_REDIRECT: + /* See ICMP_SOURCE_QUENCH */ + ipv4_sk_redirect(skb, sk); + err = EREMOTEIO; + break; } - break; - case ICMP_REDIRECT: - /* See ICMP_SOURCE_QUENCH */ - ipv4_sk_redirect(skb, sk); - err = EREMOTEIO; - break; +#if IS_ENABLED(CONFIG_IPV6) + } else if (skb->protocol == htons(ETH_P_IPV6)) { + harderr = pingv6_ops.icmpv6_err_convert(type, code, &err); +#endif } /* * RFC1122: OK. Passes ICMP errors back to application, as per * 4.1.3.3. */ - if (!inet_sock->recverr) { + if ((family == AF_INET && !inet_sock->recverr) || + (family == AF_INET6 && !inet6_sk(sk)->recverr)) { if (!harderr || sk->sk_state != TCP_ESTABLISHED) goto out; } else { - ip_icmp_error(sk, skb, err, 0 /* no remote port */, - info, (u8 *)icmph); + if (family == AF_INET) { + ip_icmp_error(sk, skb, err, 0 /* no remote port */, + info, (u8 *)icmph); +#if IS_ENABLED(CONFIG_IPV6) + } else if (family == AF_INET6) { + pingv6_ops.ipv6_icmp_error(sk, skb, err, 0, + info, (u8 *)icmph); +#endif + } } sk->sk_err = err; sk->sk_error_report(sk); out: sock_put(sk); } +EXPORT_SYMBOL_GPL(ping_err); /* - * Copy and checksum an ICMP Echo packet from user space into a buffer. + * Copy and checksum an ICMP Echo packet from user space into a buffer + * starting from the payload. */ -struct pingfakehdr { - struct icmphdr icmph; - struct iovec *iov; - __wsum wcheck; -}; - -static int ping_getfrag(void *from, char *to, - int offset, int fraglen, int odd, struct sk_buff *skb) +int ping_getfrag(void *from, char *to, + int offset, int fraglen, int odd, struct sk_buff *skb) { struct pingfakehdr *pfh = (struct pingfakehdr *)from; @@ -425,20 +588,33 @@ static int ping_getfrag(void *from, char *to, pfh->iov, 0, fraglen - sizeof(struct icmphdr), &pfh->wcheck)) return -EFAULT; + } else if (offset < sizeof(struct icmphdr)) { + BUG(); + } else { + if (csum_partial_copy_fromiovecend + (to, pfh->iov, offset - sizeof(struct icmphdr), + fraglen, &pfh->wcheck)) + return -EFAULT; + } - return 0; +#if IS_ENABLED(CONFIG_IPV6) + /* For IPv6, checksum each skb as we go along, as expected by + * icmpv6_push_pending_frames. For IPv4, accumulate the checksum in + * wcheck, it will be finalized in ping_v4_push_pending_frames. + */ + if (pfh->family == AF_INET6) { + skb->csum = pfh->wcheck; + skb->ip_summed = CHECKSUM_NONE; + pfh->wcheck = 0; } - if (offset < sizeof(struct icmphdr)) - BUG(); - if (csum_partial_copy_fromiovecend - (to, pfh->iov, offset - sizeof(struct icmphdr), - fraglen, &pfh->wcheck)) - return -EFAULT; +#endif + return 0; } +EXPORT_SYMBOL_GPL(ping_getfrag); -static int ping_push_pending_frames(struct sock *sk, struct pingfakehdr *pfh, - struct flowi4 *fl4) +static int ping_v4_push_pending_frames(struct sock *sk, struct pingfakehdr *pfh, + struct flowi4 *fl4) { struct sk_buff *skb = skb_peek(&sk->sk_write_queue); @@ -450,24 +626,9 @@ static int ping_push_pending_frames(struct sock *sk, struct pingfakehdr *pfh, return ip_push_pending_frames(sk, fl4); } -static int ping_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, - size_t len) -{ - struct net *net = sock_net(sk); - struct flowi4 fl4; - struct inet_sock *inet = inet_sk(sk); - struct ipcm_cookie ipc; - struct icmphdr user_icmph; - struct pingfakehdr pfh; - struct rtable *rt = NULL; - struct ip_options_data opt_copy; - int free = 0; - __be32 saddr, daddr, faddr; - u8 tos; - int err; - - pr_debug("ping_sendmsg(sk=%p,sk->num=%u)\n", inet, inet->inet_num); - +int ping_common_sendmsg(int family, struct msghdr *msg, size_t len, + void *user_icmph, size_t icmph_len) { + u8 type, code; if (len > 0xFFFF) return -EMSGSIZE; @@ -482,15 +643,53 @@ static int ping_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, /* * Fetch the ICMP header provided by the userland. - * iovec is modified! + * iovec is modified! The ICMP header is consumed. */ - - if (memcpy_fromiovec((u8 *)&user_icmph, msg->msg_iov, - sizeof(struct icmphdr))) + if (memcpy_fromiovec(user_icmph, msg->msg_iov, icmph_len)) return -EFAULT; - if (!ping_supported(user_icmph.type, user_icmph.code)) + + if (family == AF_INET) { + type = ((struct icmphdr *) user_icmph)->type; + code = ((struct icmphdr *) user_icmph)->code; +#if IS_ENABLED(CONFIG_IPV6) + } else if (family == AF_INET6) { + type = ((struct icmp6hdr *) user_icmph)->icmp6_type; + code = ((struct icmp6hdr *) user_icmph)->icmp6_code; +#endif + } else { + BUG(); + } + + if (!ping_supported(family, type, code)) return -EINVAL; + return 0; +} +EXPORT_SYMBOL_GPL(ping_common_sendmsg); + +int ping_v4_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, + size_t len) +{ + struct net *net = sock_net(sk); + struct flowi4 fl4; + struct inet_sock *inet = inet_sk(sk); + struct ipcm_cookie ipc; + struct icmphdr user_icmph; + struct pingfakehdr pfh; + struct rtable *rt = NULL; + struct ip_options_data opt_copy; + int free = 0; + __be32 saddr, daddr, faddr; + u8 tos; + int err; + + pr_debug("ping_v4_sendmsg(sk=%p,sk->num=%u)\n", inet, inet->inet_num); + + err = ping_common_sendmsg(AF_INET, msg, len, &user_icmph, + sizeof(user_icmph)); + if (err) + return err; + /* * Get and verify the address. */ @@ -595,13 +794,14 @@ back_from_confirm: pfh.icmph.un.echo.sequence = user_icmph.un.echo.sequence; pfh.iov = msg->msg_iov; pfh.wcheck = 0; + pfh.family = AF_INET; err = ip_append_data(sk, &fl4, ping_getfrag, &pfh, len, 0, &ipc, &rt, msg->msg_flags); if (err) ip_flush_pending_frames(sk); else - err = ping_push_pending_frames(sk, &pfh, &fl4); + err = ping_v4_push_pending_frames(sk, &pfh, &fl4); release_sock(sk); out: @@ -622,11 +822,13 @@ do_confirm: goto out; } -static int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, - size_t len, int noblock, int flags, int *addr_len) +int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, + size_t len, int noblock, int flags, int *addr_len) { struct inet_sock *isk = inet_sk(sk); - struct sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name; + int family = sk->sk_family; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; struct sk_buff *skb; int copied, err; @@ -636,11 +838,22 @@ static int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, if (flags & MSG_OOB) goto out; - if (addr_len) - *addr_len = sizeof(*sin); + if (addr_len) { + if (family == AF_INET) + *addr_len = sizeof(*sin); + else if (family == AF_INET6 && addr_len) + *addr_len = sizeof(*sin6); + } - if (flags & MSG_ERRQUEUE) - return ip_recv_error(sk, msg, len); + if (flags & MSG_ERRQUEUE) { + if (family == AF_INET) { + return ip_recv_error(sk, msg, len); +#if IS_ENABLED(CONFIG_IPV6) + } else if (family == AF_INET6) { + return pingv6_ops.ipv6_recv_error(sk, msg, len); +#endif + } + } skb = skb_recv_datagram(sk, flags, noblock, &err); if (!skb) @@ -659,15 +872,40 @@ static int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, sock_recv_timestamp(msg, sk, skb); - /* Copy the address. */ - if (sin) { + /* Copy the address and add cmsg data. */ + if (family == AF_INET) { + sin = (struct sockaddr_in *) msg->msg_name; sin->sin_family = AF_INET; sin->sin_port = 0 /* skb->h.uh->source */; sin->sin_addr.s_addr = ip_hdr(skb)->saddr; memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); + + if (isk->cmsg_flags) + ip_cmsg_recv(msg, skb); + +#if IS_ENABLED(CONFIG_IPV6) + } else if (family == AF_INET6) { + struct ipv6_pinfo *np = inet6_sk(sk); + struct ipv6hdr *ip6 = ipv6_hdr(skb); + sin6 = (struct sockaddr_in6 *) msg->msg_name; + sin6->sin6_family = AF_INET6; + sin6->sin6_port = 0; + sin6->sin6_addr = ip6->saddr; + + if (np->sndflow) + sin6->sin6_flowinfo = ip6_flowinfo(ip6); + + if (__ipv6_addr_needs_scope_id( + ipv6_addr_type(&sin6->sin6_addr))) + sin6->sin6_scope_id = IP6CB(skb)->iif; + + if (inet6_sk(sk)->rxopt.all) + pingv6_ops.ip6_datagram_recv_ctl(sk, msg, skb); +#endif + } else { + BUG(); } - if (isk->cmsg_flags) - ip_cmsg_recv(msg, skb); + err = copied; done: @@ -676,8 +914,9 @@ out: pr_debug("ping_recvmsg -> %d\n", err); return err; } +EXPORT_SYMBOL_GPL(ping_recvmsg); -static int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) +int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) { pr_debug("ping_queue_rcv_skb(sk=%p,sk->num=%d,skb=%p)\n", inet_sk(sk), inet_sk(sk)->inet_num, skb); @@ -688,6 +927,7 @@ static int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) } return 0; } +EXPORT_SYMBOL_GPL(ping_queue_rcv_skb); /* @@ -698,10 +938,7 @@ void ping_rcv(struct sk_buff *skb) { struct sock *sk; struct net *net = dev_net(skb->dev); - struct iphdr *iph = ip_hdr(skb); struct icmphdr *icmph = icmp_hdr(skb); - __be32 saddr = iph->saddr; - __be32 daddr = iph->daddr; /* We assume the packet has already been checked by icmp_rcv */ @@ -711,8 +948,7 @@ void ping_rcv(struct sk_buff *skb) /* Push ICMP header back */ skb_push(skb, skb->data - (u8 *)icmph); - sk = ping_v4_lookup(net, saddr, daddr, ntohs(icmph->un.echo.id), - skb->dev->ifindex); + sk = ping_lookup(net, skb, ntohs(icmph->un.echo.id)); if (sk != NULL) { pr_debug("rcv on socket %p\n", sk); ping_queue_rcv_skb(sk, skb_get(skb)); @@ -723,6 +959,7 @@ void ping_rcv(struct sk_buff *skb) /* We're called from icmp_rcv(). kfree_skb() is done there. */ } +EXPORT_SYMBOL_GPL(ping_rcv); struct proto ping_prot = { .name = "PING", @@ -733,14 +970,14 @@ struct proto ping_prot = { .disconnect = udp_disconnect, .setsockopt = ip_setsockopt, .getsockopt = ip_getsockopt, - .sendmsg = ping_sendmsg, + .sendmsg = ping_v4_sendmsg, .recvmsg = ping_recvmsg, .bind = ping_bind, .backlog_rcv = ping_queue_rcv_skb, .release_cb = ip4_datagram_release_cb, - .hash = ping_v4_hash, - .unhash = ping_v4_unhash, - .get_port = ping_v4_get_port, + .hash = ping_hash, + .unhash = ping_unhash, + .get_port = ping_get_port, .obj_size = sizeof(struct inet_sock), }; EXPORT_SYMBOL(ping_prot); diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile index 9af088d2cdaa..470a9c008e9b 100644 --- a/net/ipv6/Makefile +++ b/net/ipv6/Makefile @@ -7,7 +7,7 @@ obj-$(CONFIG_IPV6) += ipv6.o ipv6-objs := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o \ addrlabel.o \ route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o udplite.o \ - raw.o icmp.o mcast.o reassembly.o tcp_ipv6.o \ + raw.o icmp.o mcast.o reassembly.o tcp_ipv6.o ping.o \ exthdrs.o datagram.o ip6_flowlabel.o inet6_connection_sock.o ipv6-offload := ip6_offload.o tcpv6_offload.o udp_offload.o exthdrs_offload.o diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index ab5c7ad482cd..a5ac969aeefe 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -840,6 +841,9 @@ static int __init inet6_init(void) if (err) goto out_unregister_udplite_proto; + err = proto_register(&pingv6_prot, 1); + if (err) + goto out_unregister_ping_proto; /* We MUST register RAW sockets before we create the ICMP6, * IGMP6, or NDISC control sockets. @@ -930,6 +934,10 @@ static int __init inet6_init(void) if (err) goto ipv6_packet_fail; + err = pingv6_init(); + if (err) + goto pingv6_fail; + #ifdef CONFIG_SYSCTL err = ipv6_sysctl_register(); if (err) @@ -942,6 +950,8 @@ out: sysctl_fail: ipv6_packet_cleanup(); #endif +pingv6_fail: + pingv6_exit(); ipv6_packet_fail: tcpv6_exit(); tcpv6_fail: @@ -985,6 +995,8 @@ register_pernet_fail: rtnl_unregister_all(PF_INET6); out_sock_register_fail: rawv6_exit(); +out_unregister_ping_proto: + proto_unregister(&pingv6_prot); out_unregister_raw_proto: proto_unregister(&rawv6_prot); out_unregister_udplite_proto: diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index b4ff0a42b8c7..1d2902e61786 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -57,6 +57,7 @@ #include #include +#include #include #include #include @@ -84,12 +85,18 @@ static inline struct sock *icmpv6_sk(struct net *net) static void icmpv6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, u8 type, u8 code, int offset, __be32 info) { + /* icmpv6_notify checks 8 bytes can be pulled, icmp6hdr is 8 bytes */ + struct icmp6hdr *icmp6 = (struct icmp6hdr *) (skb->data + offset); struct net *net = dev_net(skb->dev); if (type == ICMPV6_PKT_TOOBIG) ip6_update_pmtu(skb, net, info, 0, 0); else if (type == NDISC_REDIRECT) ip6_redirect(skb, net, 0, 0); + + if (!(type & ICMPV6_INFOMSG_MASK)) + if (icmp6->icmp6_type == ICMPV6_ECHO_REQUEST) + ping_err(skb, offset, info); } static int icmpv6_rcv(struct sk_buff *skb); @@ -224,7 +231,8 @@ static bool opt_unrec(struct sk_buff *skb, __u32 offset) return (*op & 0xC0) == 0x80; } -static int icmpv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6, struct icmp6hdr *thdr, int len) +int icmpv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6, + struct icmp6hdr *thdr, int len) { struct sk_buff *skb; struct icmp6hdr *icmp6h; @@ -307,8 +315,8 @@ static void mip6_addr_swap(struct sk_buff *skb) static inline void mip6_addr_swap(struct sk_buff *skb) {} #endif -static struct dst_entry *icmpv6_route_lookup(struct net *net, struct sk_buff *skb, - struct sock *sk, struct flowi6 *fl6) +struct dst_entry *icmpv6_route_lookup(struct net *net, struct sk_buff *skb, + struct sock *sk, struct flowi6 *fl6) { struct dst_entry *dst, *dst2; struct flowi6 fl2; @@ -697,7 +705,8 @@ static int icmpv6_rcv(struct sk_buff *skb) skb->csum = ~csum_unfold(csum_ipv6_magic(saddr, daddr, skb->len, IPPROTO_ICMPV6, 0)); if (__skb_checksum_complete(skb)) { - LIMIT_NETDEBUG(KERN_DEBUG "ICMPv6 checksum failed [%pI6 > %pI6]\n", + LIMIT_NETDEBUG(KERN_DEBUG + "ICMPv6 checksum failed [%pI6c > %pI6c]\n", saddr, daddr); goto csum_error; } @@ -718,7 +727,7 @@ static int icmpv6_rcv(struct sk_buff *skb) break; case ICMPV6_ECHO_REPLY: - /* we couldn't care less */ + ping_rcv(skb); break; case ICMPV6_PKT_TOOBIG: diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c new file mode 100644 index 000000000000..a6462d657c15 --- /dev/null +++ b/net/ipv6/ping.c @@ -0,0 +1,216 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * "Ping" sockets + * + * 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. + * + * Based on ipv4/ping.c code. + * + * Authors: Lorenzo Colitti (IPv6 support) + * Vasiliy Kulikov / Openwall (IPv4 implementation, for Linux 2.6), + * Pavel Kankovsky (IPv4 implementation, for Linux 2.4.32) + * + */ + +#include +#include +#include +#include +#include +#include +#include + +struct proto pingv6_prot = { + .name = "PINGv6", + .owner = THIS_MODULE, + .init = ping_init_sock, + .close = ping_close, + .connect = ip6_datagram_connect, + .disconnect = udp_disconnect, + .setsockopt = ipv6_setsockopt, + .getsockopt = ipv6_getsockopt, + .sendmsg = ping_v6_sendmsg, + .recvmsg = ping_recvmsg, + .bind = ping_bind, + .backlog_rcv = ping_queue_rcv_skb, + .hash = ping_hash, + .unhash = ping_unhash, + .get_port = ping_get_port, + .obj_size = sizeof(struct raw6_sock), +}; +EXPORT_SYMBOL_GPL(pingv6_prot); + +static struct inet_protosw pingv6_protosw = { + .type = SOCK_DGRAM, + .protocol = IPPROTO_ICMPV6, + .prot = &pingv6_prot, + .ops = &inet6_dgram_ops, + .no_check = UDP_CSUM_DEFAULT, + .flags = INET_PROTOSW_REUSE, +}; + + +/* Compatibility glue so we can support IPv6 when it's compiled as a module */ +int dummy_ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len) +{ + return -EAFNOSUPPORT; +} +int dummy_ip6_datagram_recv_ctl(struct sock *sk, struct msghdr *msg, + struct sk_buff *skb) +{ + return -EAFNOSUPPORT; +} +int dummy_icmpv6_err_convert(u8 type, u8 code, int *err) +{ + return -EAFNOSUPPORT; +} +void dummy_ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err, + __be16 port, u32 info, u8 *payload) {} +int dummy_ipv6_chk_addr(struct net *net, const struct in6_addr *addr, + struct net_device *dev, int strict) +{ + return 0; +} + +int __init pingv6_init(void) +{ + pingv6_ops.ipv6_recv_error = ipv6_recv_error; + pingv6_ops.ip6_datagram_recv_ctl = ip6_datagram_recv_ctl; + pingv6_ops.icmpv6_err_convert = icmpv6_err_convert; + pingv6_ops.ipv6_icmp_error = ipv6_icmp_error; + pingv6_ops.ipv6_chk_addr = ipv6_chk_addr; + return inet6_register_protosw(&pingv6_protosw); +} + +/* This never gets called because it's not possible to unload the ipv6 module, + * but just in case. + */ +void pingv6_exit(void) +{ + pingv6_ops.ipv6_recv_error = dummy_ipv6_recv_error; + pingv6_ops.ip6_datagram_recv_ctl = dummy_ip6_datagram_recv_ctl; + pingv6_ops.icmpv6_err_convert = dummy_icmpv6_err_convert; + pingv6_ops.ipv6_icmp_error = dummy_ipv6_icmp_error; + pingv6_ops.ipv6_chk_addr = dummy_ipv6_chk_addr; + inet6_unregister_protosw(&pingv6_protosw); +} + +int ping_v6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, + size_t len) +{ + struct inet_sock *inet = inet_sk(sk); + struct ipv6_pinfo *np = inet6_sk(sk); + struct icmp6hdr user_icmph; + int addr_type; + struct in6_addr *daddr; + int iif = 0; + struct flowi6 fl6; + int err; + int hlimit; + struct dst_entry *dst; + struct rt6_info *rt; + struct pingfakehdr pfh; + + pr_debug("ping_v6_sendmsg(sk=%p,sk->num=%u)\n", inet, inet->inet_num); + + err = ping_common_sendmsg(AF_INET6, msg, len, &user_icmph, + sizeof(user_icmph)); + if (err) + return err; + + if (msg->msg_name) { + struct sockaddr_in6 *u = (struct sockaddr_in6 *) msg->msg_name; + if (msg->msg_namelen < sizeof(struct sockaddr_in6) || + u->sin6_family != AF_INET6) { + return -EINVAL; + } + if (sk->sk_bound_dev_if && + sk->sk_bound_dev_if != u->sin6_scope_id) { + return -EINVAL; + } + daddr = &(u->sin6_addr); + iif = u->sin6_scope_id; + } else { + if (sk->sk_state != TCP_ESTABLISHED) + return -EDESTADDRREQ; + daddr = &np->daddr; + } + + if (!iif) + iif = sk->sk_bound_dev_if; + + addr_type = ipv6_addr_type(daddr); + if (__ipv6_addr_needs_scope_id(addr_type) && !iif) + return -EINVAL; + if (addr_type & IPV6_ADDR_MAPPED) + return -EINVAL; + + /* TODO: use ip6_datagram_send_ctl to get options from cmsg */ + + memset(&fl6, 0, sizeof(fl6)); + + fl6.flowi6_proto = IPPROTO_ICMPV6; + fl6.saddr = np->saddr; + fl6.daddr = *daddr; + fl6.fl6_icmp_type = user_icmph.icmp6_type; + fl6.fl6_icmp_code = user_icmph.icmp6_code; + security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); + + if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr)) + fl6.flowi6_oif = np->mcast_oif; + else if (!fl6.flowi6_oif) + fl6.flowi6_oif = np->ucast_oif; + + dst = ip6_sk_dst_lookup_flow(sk, &fl6, daddr, 1); + if (IS_ERR(dst)) + return PTR_ERR(dst); + rt = (struct rt6_info *) dst; + + np = inet6_sk(sk); + if (!np) + return -EBADF; + + if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr)) + fl6.flowi6_oif = np->mcast_oif; + else if (!fl6.flowi6_oif) + fl6.flowi6_oif = np->ucast_oif; + + pfh.icmph.type = user_icmph.icmp6_type; + pfh.icmph.code = user_icmph.icmp6_code; + pfh.icmph.checksum = 0; + pfh.icmph.un.echo.id = inet->inet_sport; + pfh.icmph.un.echo.sequence = user_icmph.icmp6_sequence; + pfh.iov = msg->msg_iov; + pfh.wcheck = 0; + pfh.family = AF_INET6; + + if (ipv6_addr_is_multicast(&fl6.daddr)) + hlimit = np->mcast_hops; + else + hlimit = np->hop_limit; + if (hlimit < 0) + hlimit = ip6_dst_hoplimit(dst); + + err = ip6_append_data(sk, ping_getfrag, &pfh, len, + 0, hlimit, + np->tclass, NULL, &fl6, rt, + MSG_DONTWAIT, np->dontfrag); + + if (err) { + ICMP6_INC_STATS_BH(sock_net(sk), rt->rt6i_idev, + ICMP6_MIB_OUTERRORS); + ip6_flush_pending_frames(sk); + } else { + err = icmpv6_push_pending_frames(sk, &fl6, + (struct icmp6hdr *) &pfh.icmph, + len); + } + + return err; +} -- cgit v1.2.3 From 8513fbd880093f00a47e85a552f14ca2de8d84d6 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 23 May 2013 00:52:31 +0000 Subject: net: ethernet: use platform_{get,set}_drvdata() Use the wrapper functions for getting and setting the driver data using platform_device instead of using dev_{get,set}_drvdata() with &pdev->dev, so we can directly pass a struct platform_device. Also, unnecessary dev_set_drvdata() is removed, because the driver core clears the driver data to NULL after device_release or on probe failure. Signed-off-by: Jingoo Han Signed-off-by: David S. Miller --- drivers/net/ethernet/aeroflex/greth.c | 4 +--- drivers/net/ethernet/amd/sunlance.c | 6 ++---- drivers/net/ethernet/broadcom/sb1250-mac.c | 9 ++++----- drivers/net/ethernet/freescale/fec_mpc52xx.c | 9 ++++----- drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c | 5 ++--- drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c | 6 ++---- drivers/net/ethernet/freescale/fs_enet/mii-fec.c | 6 ++---- drivers/net/ethernet/freescale/gianfar.c | 6 ++---- drivers/net/ethernet/freescale/gianfar_ptp.c | 4 ++-- drivers/net/ethernet/freescale/ucc_geth.c | 4 ++-- drivers/net/ethernet/freescale/xgmac_mdio.c | 4 ++-- drivers/net/ethernet/ibm/ehea/ehea_main.c | 4 ++-- drivers/net/ethernet/ibm/emac/mal.c | 6 ++---- drivers/net/ethernet/ibm/emac/rgmii.c | 18 ++++++++---------- drivers/net/ethernet/ibm/emac/tah.c | 14 ++++++-------- drivers/net/ethernet/ibm/emac/zmii.c | 18 ++++++++---------- drivers/net/ethernet/netx-eth.c | 4 +--- drivers/net/ethernet/octeon/octeon_mgmt.c | 4 ++-- drivers/net/ethernet/sun/niu.c | 5 ++--- drivers/net/ethernet/sun/sunhme.c | 4 ++-- drivers/net/ethernet/sun/sunqe.c | 10 ++++------ drivers/net/ethernet/xilinx/ll_temac_main.c | 5 ++--- drivers/net/ethernet/xilinx/xilinx_axienet_main.c | 6 ++---- 23 files changed, 66 insertions(+), 95 deletions(-) diff --git a/drivers/net/ethernet/aeroflex/greth.c b/drivers/net/ethernet/aeroflex/greth.c index 269295403fc4..7ff4b30d55ea 100644 --- a/drivers/net/ethernet/aeroflex/greth.c +++ b/drivers/net/ethernet/aeroflex/greth.c @@ -1565,7 +1565,7 @@ error1: static int greth_of_remove(struct platform_device *of_dev) { - struct net_device *ndev = dev_get_drvdata(&of_dev->dev); + struct net_device *ndev = platform_get_drvdata(of_dev); struct greth_private *greth = netdev_priv(ndev); /* Free descriptor areas */ @@ -1573,8 +1573,6 @@ static int greth_of_remove(struct platform_device *of_dev) dma_free_coherent(&of_dev->dev, 1024, greth->tx_bd_base, greth->tx_bd_base_phys); - dev_set_drvdata(&of_dev->dev, NULL); - if (greth->phy) phy_stop(greth->phy); mdiobus_unregister(greth->mdio); diff --git a/drivers/net/ethernet/amd/sunlance.c b/drivers/net/ethernet/amd/sunlance.c index f47b780892e9..ece56831a647 100644 --- a/drivers/net/ethernet/amd/sunlance.c +++ b/drivers/net/ethernet/amd/sunlance.c @@ -1470,7 +1470,7 @@ no_link_test: goto fail; } - dev_set_drvdata(&op->dev, lp); + platform_set_drvdata(op, lp); printk(KERN_INFO "%s: LANCE %pM\n", dev->name, dev->dev_addr); @@ -1501,7 +1501,7 @@ static int sunlance_sbus_probe(struct platform_device *op) static int sunlance_sbus_remove(struct platform_device *op) { - struct lance_private *lp = dev_get_drvdata(&op->dev); + struct lance_private *lp = platform_get_drvdata(op); struct net_device *net_dev = lp->dev; unregister_netdev(net_dev); @@ -1510,8 +1510,6 @@ static int sunlance_sbus_remove(struct platform_device *op) free_netdev(net_dev); - dev_set_drvdata(&op->dev, NULL); - return 0; } diff --git a/drivers/net/ethernet/broadcom/sb1250-mac.c b/drivers/net/ethernet/broadcom/sb1250-mac.c index e80bfb60c3ef..c2777712da99 100644 --- a/drivers/net/ethernet/broadcom/sb1250-mac.c +++ b/drivers/net/ethernet/broadcom/sb1250-mac.c @@ -2197,7 +2197,7 @@ static const struct net_device_ops sbmac_netdev_ops = { static int sbmac_init(struct platform_device *pldev, long long base) { - struct net_device *dev = dev_get_drvdata(&pldev->dev); + struct net_device *dev = platform_get_drvdata(pldev); int idx = pldev->id; struct sbmac_softc *sc = netdev_priv(dev); unsigned char *eaddr; @@ -2275,7 +2275,7 @@ static int sbmac_init(struct platform_device *pldev, long long base) dev->name); goto free_mdio; } - dev_set_drvdata(&pldev->dev, sc->mii_bus); + platform_set_drvdata(pldev, sc->mii_bus); err = register_netdev(dev); if (err) { @@ -2300,7 +2300,6 @@ static int sbmac_init(struct platform_device *pldev, long long base) return 0; unreg_mdio: mdiobus_unregister(sc->mii_bus); - dev_set_drvdata(&pldev->dev, NULL); free_mdio: mdiobus_free(sc->mii_bus); uninit_ctx: @@ -2624,7 +2623,7 @@ static int sbmac_probe(struct platform_device *pldev) goto out_unmap; } - dev_set_drvdata(&pldev->dev, dev); + platform_set_drvdata(pldev, dev); SET_NETDEV_DEV(dev, &pldev->dev); sc = netdev_priv(dev); @@ -2649,7 +2648,7 @@ out_out: static int __exit sbmac_remove(struct platform_device *pldev) { - struct net_device *dev = dev_get_drvdata(&pldev->dev); + struct net_device *dev = platform_get_drvdata(pldev); struct sbmac_softc *sc = netdev_priv(dev); unregister_netdev(dev); diff --git a/drivers/net/ethernet/freescale/fec_mpc52xx.c b/drivers/net/ethernet/freescale/fec_mpc52xx.c index 9bc15e2365bb..9947765e90c5 100644 --- a/drivers/net/ethernet/freescale/fec_mpc52xx.c +++ b/drivers/net/ethernet/freescale/fec_mpc52xx.c @@ -981,7 +981,7 @@ static int mpc52xx_fec_probe(struct platform_device *op) goto err_node; /* We're done ! */ - dev_set_drvdata(&op->dev, ndev); + platform_set_drvdata(op, ndev); netdev_info(ndev, "%s MAC %pM\n", op->dev.of_node->full_name, ndev->dev_addr); @@ -1010,7 +1010,7 @@ mpc52xx_fec_remove(struct platform_device *op) struct net_device *ndev; struct mpc52xx_fec_priv *priv; - ndev = dev_get_drvdata(&op->dev); + ndev = platform_get_drvdata(op); priv = netdev_priv(ndev); unregister_netdev(ndev); @@ -1030,14 +1030,13 @@ mpc52xx_fec_remove(struct platform_device *op) free_netdev(ndev); - dev_set_drvdata(&op->dev, NULL); return 0; } #ifdef CONFIG_PM static int mpc52xx_fec_of_suspend(struct platform_device *op, pm_message_t state) { - struct net_device *dev = dev_get_drvdata(&op->dev); + struct net_device *dev = platform_get_drvdata(op); if (netif_running(dev)) mpc52xx_fec_close(dev); @@ -1047,7 +1046,7 @@ static int mpc52xx_fec_of_suspend(struct platform_device *op, pm_message_t state static int mpc52xx_fec_of_resume(struct platform_device *op) { - struct net_device *dev = dev_get_drvdata(&op->dev); + struct net_device *dev = platform_get_drvdata(op); mpc52xx_fec_hw_init(dev); mpc52xx_fec_reset_stats(dev); diff --git a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c index edc120094c34..8de53a14a6f4 100644 --- a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c +++ b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c @@ -1048,7 +1048,7 @@ static int fs_enet_probe(struct platform_device *ofdev) } SET_NETDEV_DEV(ndev, &ofdev->dev); - dev_set_drvdata(&ofdev->dev, ndev); + platform_set_drvdata(ofdev, ndev); fep = netdev_priv(ndev); fep->dev = &ofdev->dev; @@ -1106,7 +1106,6 @@ out_cleanup_data: fep->ops->cleanup_data(ndev); out_free_dev: free_netdev(ndev); - dev_set_drvdata(&ofdev->dev, NULL); out_put: of_node_put(fpi->phy_node); out_free_fpi: @@ -1116,7 +1115,7 @@ out_free_fpi: static int fs_enet_remove(struct platform_device *ofdev) { - struct net_device *ndev = dev_get_drvdata(&ofdev->dev); + struct net_device *ndev = platform_get_drvdata(ofdev); struct fs_enet_private *fep = netdev_priv(ndev); unregister_netdev(ndev); diff --git a/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c b/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c index 2bafbd37c247..844ecfa84d17 100644 --- a/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c +++ b/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c @@ -179,7 +179,7 @@ static int fs_enet_mdio_probe(struct platform_device *ofdev) } new_bus->parent = &ofdev->dev; - dev_set_drvdata(&ofdev->dev, new_bus); + platform_set_drvdata(ofdev, new_bus); ret = of_mdiobus_register(new_bus, ofdev->dev.of_node); if (ret) @@ -188,7 +188,6 @@ static int fs_enet_mdio_probe(struct platform_device *ofdev) return 0; out_free_irqs: - dev_set_drvdata(&ofdev->dev, NULL); kfree(new_bus->irq); out_unmap_regs: iounmap(bitbang->dir); @@ -202,11 +201,10 @@ out: static int fs_enet_mdio_remove(struct platform_device *ofdev) { - struct mii_bus *bus = dev_get_drvdata(&ofdev->dev); + struct mii_bus *bus = platform_get_drvdata(ofdev); struct bb_info *bitbang = bus->priv; mdiobus_unregister(bus); - dev_set_drvdata(&ofdev->dev, NULL); kfree(bus->irq); free_mdio_bitbang(bus); iounmap(bitbang->dir); diff --git a/drivers/net/ethernet/freescale/fs_enet/mii-fec.c b/drivers/net/ethernet/freescale/fs_enet/mii-fec.c index 18e8ef203736..2f1c46a12f05 100644 --- a/drivers/net/ethernet/freescale/fs_enet/mii-fec.c +++ b/drivers/net/ethernet/freescale/fs_enet/mii-fec.c @@ -180,7 +180,7 @@ static int fs_enet_mdio_probe(struct platform_device *ofdev) } new_bus->parent = &ofdev->dev; - dev_set_drvdata(&ofdev->dev, new_bus); + platform_set_drvdata(ofdev, new_bus); ret = of_mdiobus_register(new_bus, ofdev->dev.of_node); if (ret) @@ -189,7 +189,6 @@ static int fs_enet_mdio_probe(struct platform_device *ofdev) return 0; out_free_irqs: - dev_set_drvdata(&ofdev->dev, NULL); kfree(new_bus->irq); out_unmap_regs: iounmap(fec->fecp); @@ -204,11 +203,10 @@ out: static int fs_enet_mdio_remove(struct platform_device *ofdev) { - struct mii_bus *bus = dev_get_drvdata(&ofdev->dev); + struct mii_bus *bus = platform_get_drvdata(ofdev); struct fec_info *fec = bus->priv; mdiobus_unregister(bus); - dev_set_drvdata(&ofdev->dev, NULL); kfree(bus->irq); iounmap(fec->fecp); kfree(fec); diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index 2375a01715a0..14f06947e70d 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -1000,7 +1000,7 @@ static int gfar_probe(struct platform_device *ofdev) spin_lock_init(&priv->bflock); INIT_WORK(&priv->reset_task, gfar_reset_task); - dev_set_drvdata(&ofdev->dev, priv); + platform_set_drvdata(ofdev, priv); regs = priv->gfargrp[0].regs; gfar_detect_errata(priv); @@ -1240,15 +1240,13 @@ register_fail: static int gfar_remove(struct platform_device *ofdev) { - struct gfar_private *priv = dev_get_drvdata(&ofdev->dev); + struct gfar_private *priv = platform_get_drvdata(ofdev); if (priv->phy_node) of_node_put(priv->phy_node); if (priv->tbi_node) of_node_put(priv->tbi_node); - dev_set_drvdata(&ofdev->dev, NULL); - unregister_netdev(priv->ndev); unmap_group_regs(priv); free_gfar_dev(priv); diff --git a/drivers/net/ethernet/freescale/gianfar_ptp.c b/drivers/net/ethernet/freescale/gianfar_ptp.c index 083ea2b4d20a..098f133908ae 100644 --- a/drivers/net/ethernet/freescale/gianfar_ptp.c +++ b/drivers/net/ethernet/freescale/gianfar_ptp.c @@ -519,7 +519,7 @@ static int gianfar_ptp_probe(struct platform_device *dev) } gfar_phc_index = ptp_clock_index(etsects->clock); - dev_set_drvdata(&dev->dev, etsects); + platform_set_drvdata(dev, etsects); return 0; @@ -537,7 +537,7 @@ no_memory: static int gianfar_ptp_remove(struct platform_device *dev) { - struct etsects *etsects = dev_get_drvdata(&dev->dev); + struct etsects *etsects = platform_get_drvdata(dev); gfar_write(&etsects->regs->tmr_temask, 0); gfar_write(&etsects->regs->tmr_ctrl, 0); diff --git a/drivers/net/ethernet/freescale/ucc_geth.c b/drivers/net/ethernet/freescale/ucc_geth.c index e04c59818f60..3c43dac894ec 100644 --- a/drivers/net/ethernet/freescale/ucc_geth.c +++ b/drivers/net/ethernet/freescale/ucc_geth.c @@ -3564,7 +3564,7 @@ static void ucc_geth_timeout(struct net_device *dev) static int ucc_geth_suspend(struct platform_device *ofdev, pm_message_t state) { - struct net_device *ndev = dev_get_drvdata(&ofdev->dev); + struct net_device *ndev = platform_get_drvdata(ofdev); struct ucc_geth_private *ugeth = netdev_priv(ndev); if (!netif_running(ndev)) @@ -3592,7 +3592,7 @@ static int ucc_geth_suspend(struct platform_device *ofdev, pm_message_t state) static int ucc_geth_resume(struct platform_device *ofdev) { - struct net_device *ndev = dev_get_drvdata(&ofdev->dev); + struct net_device *ndev = platform_get_drvdata(ofdev); struct ucc_geth_private *ugeth = netdev_priv(ndev); int err; diff --git a/drivers/net/ethernet/freescale/xgmac_mdio.c b/drivers/net/ethernet/freescale/xgmac_mdio.c index 418068b941b1..c1b6e7e31aac 100644 --- a/drivers/net/ethernet/freescale/xgmac_mdio.c +++ b/drivers/net/ethernet/freescale/xgmac_mdio.c @@ -227,7 +227,7 @@ static int xgmac_mdio_probe(struct platform_device *pdev) goto err_registration; } - dev_set_drvdata(&pdev->dev, bus); + platform_set_drvdata(pdev, bus); return 0; @@ -242,7 +242,7 @@ err_ioremap: static int xgmac_mdio_remove(struct platform_device *pdev) { - struct mii_bus *bus = dev_get_drvdata(&pdev->dev); + struct mii_bus *bus = platform_get_drvdata(pdev); mdiobus_unregister(bus); iounmap(bus->priv); diff --git a/drivers/net/ethernet/ibm/ehea/ehea_main.c b/drivers/net/ethernet/ibm/ehea/ehea_main.c index 90ea0b1673ca..0605e76c7edd 100644 --- a/drivers/net/ethernet/ibm/ehea/ehea_main.c +++ b/drivers/net/ethernet/ibm/ehea/ehea_main.c @@ -3289,7 +3289,7 @@ static int ehea_probe_adapter(struct platform_device *dev, adapter->pd = EHEA_PD_ID; - dev_set_drvdata(&dev->dev, adapter); + platform_set_drvdata(dev, adapter); /* initialize adapter and ports */ @@ -3360,7 +3360,7 @@ out: static int ehea_remove(struct platform_device *dev) { - struct ehea_adapter *adapter = dev_get_drvdata(&dev->dev); + struct ehea_adapter *adapter = platform_get_drvdata(dev); int i; for (i = 0; i < EHEA_MAX_PORTS; i++) diff --git a/drivers/net/ethernet/ibm/emac/mal.c b/drivers/net/ethernet/ibm/emac/mal.c index 610ed223d1db..856ea66c9223 100644 --- a/drivers/net/ethernet/ibm/emac/mal.c +++ b/drivers/net/ethernet/ibm/emac/mal.c @@ -696,7 +696,7 @@ static int mal_probe(struct platform_device *ofdev) /* Advertise this instance to the rest of the world */ wmb(); - dev_set_drvdata(&ofdev->dev, mal); + platform_set_drvdata(ofdev, mal); mal_dbg_register(mal); @@ -722,7 +722,7 @@ static int mal_probe(struct platform_device *ofdev) static int mal_remove(struct platform_device *ofdev) { - struct mal_instance *mal = dev_get_drvdata(&ofdev->dev); + struct mal_instance *mal = platform_get_drvdata(ofdev); MAL_DBG(mal, "remove" NL); @@ -735,8 +735,6 @@ static int mal_remove(struct platform_device *ofdev) "mal%d: commac list is not empty on remove!\n", mal->index); - dev_set_drvdata(&ofdev->dev, NULL); - free_irq(mal->serr_irq, mal); free_irq(mal->txde_irq, mal); free_irq(mal->txeob_irq, mal); diff --git a/drivers/net/ethernet/ibm/emac/rgmii.c b/drivers/net/ethernet/ibm/emac/rgmii.c index 39251765b55d..c47e23d6eeaa 100644 --- a/drivers/net/ethernet/ibm/emac/rgmii.c +++ b/drivers/net/ethernet/ibm/emac/rgmii.c @@ -95,7 +95,7 @@ static inline u32 rgmii_mode_mask(int mode, int input) int rgmii_attach(struct platform_device *ofdev, int input, int mode) { - struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev); + struct rgmii_instance *dev = platform_get_drvdata(ofdev); struct rgmii_regs __iomem *p = dev->base; RGMII_DBG(dev, "attach(%d)" NL, input); @@ -124,7 +124,7 @@ int rgmii_attach(struct platform_device *ofdev, int input, int mode) void rgmii_set_speed(struct platform_device *ofdev, int input, int speed) { - struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev); + struct rgmii_instance *dev = platform_get_drvdata(ofdev); struct rgmii_regs __iomem *p = dev->base; u32 ssr; @@ -146,7 +146,7 @@ void rgmii_set_speed(struct platform_device *ofdev, int input, int speed) void rgmii_get_mdio(struct platform_device *ofdev, int input) { - struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev); + struct rgmii_instance *dev = platform_get_drvdata(ofdev); struct rgmii_regs __iomem *p = dev->base; u32 fer; @@ -167,7 +167,7 @@ void rgmii_get_mdio(struct platform_device *ofdev, int input) void rgmii_put_mdio(struct platform_device *ofdev, int input) { - struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev); + struct rgmii_instance *dev = platform_get_drvdata(ofdev); struct rgmii_regs __iomem *p = dev->base; u32 fer; @@ -188,7 +188,7 @@ void rgmii_put_mdio(struct platform_device *ofdev, int input) void rgmii_detach(struct platform_device *ofdev, int input) { - struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev); + struct rgmii_instance *dev = platform_get_drvdata(ofdev); struct rgmii_regs __iomem *p; BUG_ON(!dev || dev->users == 0); @@ -214,7 +214,7 @@ int rgmii_get_regs_len(struct platform_device *ofdev) void *rgmii_dump_regs(struct platform_device *ofdev, void *buf) { - struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev); + struct rgmii_instance *dev = platform_get_drvdata(ofdev); struct emac_ethtool_regs_subhdr *hdr = buf; struct rgmii_regs *regs = (struct rgmii_regs *)(hdr + 1); @@ -279,7 +279,7 @@ static int rgmii_probe(struct platform_device *ofdev) (dev->flags & EMAC_RGMII_FLAG_HAS_MDIO) ? "" : "out"); wmb(); - dev_set_drvdata(&ofdev->dev, dev); + platform_set_drvdata(ofdev, dev); return 0; @@ -291,9 +291,7 @@ static int rgmii_probe(struct platform_device *ofdev) static int rgmii_remove(struct platform_device *ofdev) { - struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev); - - dev_set_drvdata(&ofdev->dev, NULL); + struct rgmii_instance *dev = platform_get_drvdata(ofdev); WARN_ON(dev->users != 0); diff --git a/drivers/net/ethernet/ibm/emac/tah.c b/drivers/net/ethernet/ibm/emac/tah.c index 795f1393e2b6..c231a4a32c4d 100644 --- a/drivers/net/ethernet/ibm/emac/tah.c +++ b/drivers/net/ethernet/ibm/emac/tah.c @@ -25,7 +25,7 @@ int tah_attach(struct platform_device *ofdev, int channel) { - struct tah_instance *dev = dev_get_drvdata(&ofdev->dev); + struct tah_instance *dev = platform_get_drvdata(ofdev); mutex_lock(&dev->lock); /* Reset has been done at probe() time... nothing else to do for now */ @@ -37,7 +37,7 @@ int tah_attach(struct platform_device *ofdev, int channel) void tah_detach(struct platform_device *ofdev, int channel) { - struct tah_instance *dev = dev_get_drvdata(&ofdev->dev); + struct tah_instance *dev = platform_get_drvdata(ofdev); mutex_lock(&dev->lock); --dev->users; @@ -46,7 +46,7 @@ void tah_detach(struct platform_device *ofdev, int channel) void tah_reset(struct platform_device *ofdev) { - struct tah_instance *dev = dev_get_drvdata(&ofdev->dev); + struct tah_instance *dev = platform_get_drvdata(ofdev); struct tah_regs __iomem *p = dev->base; int n; @@ -74,7 +74,7 @@ int tah_get_regs_len(struct platform_device *ofdev) void *tah_dump_regs(struct platform_device *ofdev, void *buf) { - struct tah_instance *dev = dev_get_drvdata(&ofdev->dev); + struct tah_instance *dev = platform_get_drvdata(ofdev); struct emac_ethtool_regs_subhdr *hdr = buf; struct tah_regs *regs = (struct tah_regs *)(hdr + 1); @@ -118,7 +118,7 @@ static int tah_probe(struct platform_device *ofdev) goto err_free; } - dev_set_drvdata(&ofdev->dev, dev); + platform_set_drvdata(ofdev, dev); /* Initialize TAH and enable IPv4 checksum verification, no TSO yet */ tah_reset(ofdev); @@ -137,9 +137,7 @@ static int tah_probe(struct platform_device *ofdev) static int tah_remove(struct platform_device *ofdev) { - struct tah_instance *dev = dev_get_drvdata(&ofdev->dev); - - dev_set_drvdata(&ofdev->dev, NULL); + struct tah_instance *dev = platform_get_drvdata(ofdev); WARN_ON(dev->users != 0); diff --git a/drivers/net/ethernet/ibm/emac/zmii.c b/drivers/net/ethernet/ibm/emac/zmii.c index f91202f42125..4cdf286f7ee3 100644 --- a/drivers/net/ethernet/ibm/emac/zmii.c +++ b/drivers/net/ethernet/ibm/emac/zmii.c @@ -84,7 +84,7 @@ static inline u32 zmii_mode_mask(int mode, int input) int zmii_attach(struct platform_device *ofdev, int input, int *mode) { - struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev); + struct zmii_instance *dev = platform_get_drvdata(ofdev); struct zmii_regs __iomem *p = dev->base; ZMII_DBG(dev, "init(%d, %d)" NL, input, *mode); @@ -150,7 +150,7 @@ int zmii_attach(struct platform_device *ofdev, int input, int *mode) void zmii_get_mdio(struct platform_device *ofdev, int input) { - struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev); + struct zmii_instance *dev = platform_get_drvdata(ofdev); u32 fer; ZMII_DBG2(dev, "get_mdio(%d)" NL, input); @@ -163,7 +163,7 @@ void zmii_get_mdio(struct platform_device *ofdev, int input) void zmii_put_mdio(struct platform_device *ofdev, int input) { - struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev); + struct zmii_instance *dev = platform_get_drvdata(ofdev); ZMII_DBG2(dev, "put_mdio(%d)" NL, input); mutex_unlock(&dev->lock); @@ -172,7 +172,7 @@ void zmii_put_mdio(struct platform_device *ofdev, int input) void zmii_set_speed(struct platform_device *ofdev, int input, int speed) { - struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev); + struct zmii_instance *dev = platform_get_drvdata(ofdev); u32 ssr; mutex_lock(&dev->lock); @@ -193,7 +193,7 @@ void zmii_set_speed(struct platform_device *ofdev, int input, int speed) void zmii_detach(struct platform_device *ofdev, int input) { - struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev); + struct zmii_instance *dev = platform_get_drvdata(ofdev); BUG_ON(!dev || dev->users == 0); @@ -218,7 +218,7 @@ int zmii_get_regs_len(struct platform_device *ofdev) void *zmii_dump_regs(struct platform_device *ofdev, void *buf) { - struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev); + struct zmii_instance *dev = platform_get_drvdata(ofdev); struct emac_ethtool_regs_subhdr *hdr = buf; struct zmii_regs *regs = (struct zmii_regs *)(hdr + 1); @@ -272,7 +272,7 @@ static int zmii_probe(struct platform_device *ofdev) printk(KERN_INFO "ZMII %s initialized\n", ofdev->dev.of_node->full_name); wmb(); - dev_set_drvdata(&ofdev->dev, dev); + platform_set_drvdata(ofdev, dev); return 0; @@ -284,9 +284,7 @@ static int zmii_probe(struct platform_device *ofdev) static int zmii_remove(struct platform_device *ofdev) { - struct zmii_instance *dev = dev_get_drvdata(&ofdev->dev); - - dev_set_drvdata(&ofdev->dev, NULL); + struct zmii_instance *dev = platform_get_drvdata(ofdev); WARN_ON(dev->users != 0); diff --git a/drivers/net/ethernet/netx-eth.c b/drivers/net/ethernet/netx-eth.c index cb9e63831500..5da3ffb61062 100644 --- a/drivers/net/ethernet/netx-eth.c +++ b/drivers/net/ethernet/netx-eth.c @@ -430,11 +430,9 @@ exit: static int netx_eth_drv_remove(struct platform_device *pdev) { - struct net_device *ndev = dev_get_drvdata(&pdev->dev); + struct net_device *ndev = platform_get_drvdata(pdev); struct netx_eth_priv *priv = netdev_priv(ndev); - platform_set_drvdata(pdev, NULL); - unregister_netdev(ndev); xc_stop(priv->xc); free_xc(priv->xc); diff --git a/drivers/net/ethernet/octeon/octeon_mgmt.c b/drivers/net/ethernet/octeon/octeon_mgmt.c index 921729f9c85c..e6e029237a63 100644 --- a/drivers/net/ethernet/octeon/octeon_mgmt.c +++ b/drivers/net/ethernet/octeon/octeon_mgmt.c @@ -1437,7 +1437,7 @@ static int octeon_mgmt_probe(struct platform_device *pdev) SET_NETDEV_DEV(netdev, &pdev->dev); - dev_set_drvdata(&pdev->dev, netdev); + platform_set_drvdata(pdev, netdev); p = netdev_priv(netdev); netif_napi_add(netdev, &p->napi, octeon_mgmt_napi_poll, OCTEON_MGMT_NAPI_WEIGHT); @@ -1559,7 +1559,7 @@ err: static int octeon_mgmt_remove(struct platform_device *pdev) { - struct net_device *netdev = dev_get_drvdata(&pdev->dev); + struct net_device *netdev = platform_get_drvdata(pdev); unregister_netdev(netdev); free_netdev(netdev); diff --git a/drivers/net/ethernet/sun/niu.c b/drivers/net/ethernet/sun/niu.c index 95cff98d8a34..fa322409bff3 100644 --- a/drivers/net/ethernet/sun/niu.c +++ b/drivers/net/ethernet/sun/niu.c @@ -10108,7 +10108,7 @@ static int niu_of_probe(struct platform_device *op) goto err_out_iounmap; } - dev_set_drvdata(&op->dev, dev); + platform_set_drvdata(op, dev); niu_device_announce(np); @@ -10145,7 +10145,7 @@ err_out: static int niu_of_remove(struct platform_device *op) { - struct net_device *dev = dev_get_drvdata(&op->dev); + struct net_device *dev = platform_get_drvdata(op); if (dev) { struct niu *np = netdev_priv(dev); @@ -10175,7 +10175,6 @@ static int niu_of_remove(struct platform_device *op) niu_put_parent(np); free_netdev(dev); - dev_set_drvdata(&op->dev, NULL); } return 0; } diff --git a/drivers/net/ethernet/sun/sunhme.c b/drivers/net/ethernet/sun/sunhme.c index 436fa9d5a071..171f5b0809c4 100644 --- a/drivers/net/ethernet/sun/sunhme.c +++ b/drivers/net/ethernet/sun/sunhme.c @@ -2506,7 +2506,7 @@ static struct quattro *quattro_sbus_find(struct platform_device *child) struct quattro *qp; op = to_platform_device(parent); - qp = dev_get_drvdata(&op->dev); + qp = platform_get_drvdata(op); if (qp) return qp; @@ -2521,7 +2521,7 @@ static struct quattro *quattro_sbus_find(struct platform_device *child) qp->next = qfe_sbus_list; qfe_sbus_list = qp; - dev_set_drvdata(&op->dev, qp); + platform_set_drvdata(op, qp); } return qp; } diff --git a/drivers/net/ethernet/sun/sunqe.c b/drivers/net/ethernet/sun/sunqe.c index 8182591bc187..b072f4dba033 100644 --- a/drivers/net/ethernet/sun/sunqe.c +++ b/drivers/net/ethernet/sun/sunqe.c @@ -767,7 +767,7 @@ static struct sunqec *get_qec(struct platform_device *child) struct platform_device *op = to_platform_device(child->dev.parent); struct sunqec *qecp; - qecp = dev_get_drvdata(&op->dev); + qecp = platform_get_drvdata(op); if (!qecp) { qecp = kzalloc(sizeof(struct sunqec), GFP_KERNEL); if (qecp) { @@ -801,7 +801,7 @@ static struct sunqec *get_qec(struct platform_device *child) goto fail; } - dev_set_drvdata(&op->dev, qecp); + platform_set_drvdata(op, qecp); qecp->next_module = root_qec_dev; root_qec_dev = qecp; @@ -902,7 +902,7 @@ static int qec_ether_init(struct platform_device *op) if (res) goto fail; - dev_set_drvdata(&op->dev, qe); + platform_set_drvdata(op, qe); printk(KERN_INFO "%s: qe channel[%d] %pM\n", dev->name, qe->channel, dev->dev_addr); @@ -934,7 +934,7 @@ static int qec_sbus_probe(struct platform_device *op) static int qec_sbus_remove(struct platform_device *op) { - struct sunqe *qp = dev_get_drvdata(&op->dev); + struct sunqe *qp = platform_get_drvdata(op); struct net_device *net_dev = qp->dev; unregister_netdev(net_dev); @@ -948,8 +948,6 @@ static int qec_sbus_remove(struct platform_device *op) free_netdev(net_dev); - dev_set_drvdata(&op->dev, NULL); - return 0; } diff --git a/drivers/net/ethernet/xilinx/ll_temac_main.c b/drivers/net/ethernet/xilinx/ll_temac_main.c index 57c2e5ef2804..58eb4488beff 100644 --- a/drivers/net/ethernet/xilinx/ll_temac_main.c +++ b/drivers/net/ethernet/xilinx/ll_temac_main.c @@ -1007,7 +1007,7 @@ static int temac_of_probe(struct platform_device *op) return -ENOMEM; ether_setup(ndev); - dev_set_drvdata(&op->dev, ndev); + platform_set_drvdata(op, ndev); SET_NETDEV_DEV(ndev, &op->dev); ndev->flags &= ~IFF_MULTICAST; /* clear multicast */ ndev->features = NETIF_F_SG | NETIF_F_FRAGLIST; @@ -1136,7 +1136,7 @@ static int temac_of_probe(struct platform_device *op) static int temac_of_remove(struct platform_device *op) { - struct net_device *ndev = dev_get_drvdata(&op->dev); + struct net_device *ndev = platform_get_drvdata(op); struct temac_local *lp = netdev_priv(ndev); temac_mdio_teardown(lp); @@ -1145,7 +1145,6 @@ static int temac_of_remove(struct platform_device *op) if (lp->phy_node) of_node_put(lp->phy_node); lp->phy_node = NULL; - dev_set_drvdata(&op->dev, NULL); iounmap(lp->regs); if (lp->sdma_regs) iounmap(lp->sdma_regs); diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c index 24748e8367a1..fb7d1c28a2ea 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c @@ -1484,7 +1484,7 @@ static int axienet_of_probe(struct platform_device *op) return -ENOMEM; ether_setup(ndev); - dev_set_drvdata(&op->dev, ndev); + platform_set_drvdata(op, ndev); SET_NETDEV_DEV(ndev, &op->dev); ndev->flags &= ~IFF_MULTICAST; /* clear multicast */ @@ -1622,7 +1622,7 @@ nodev: static int axienet_of_remove(struct platform_device *op) { - struct net_device *ndev = dev_get_drvdata(&op->dev); + struct net_device *ndev = platform_get_drvdata(op); struct axienet_local *lp = netdev_priv(ndev); axienet_mdio_teardown(lp); @@ -1632,8 +1632,6 @@ static int axienet_of_remove(struct platform_device *op) of_node_put(lp->phy_node); lp->phy_node = NULL; - dev_set_drvdata(&op->dev, NULL); - iounmap(lp->regs); if (lp->dma_regs) iounmap(lp->dma_regs); -- cgit v1.2.3 From 42e52bf9e3ae80fd44b21ddfcd64c54e6db2ff76 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Sat, 25 May 2013 04:12:10 +0000 Subject: net: add netnotifier event for upper device change Now when upper device is changed, event is not propagated via RT Netlink to userspace. Userspace might never now about the change. Fix this by adding upper-device-change notifier event. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/linux/netdevice.h | 1 + net/core/dev.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 0ebd63ae2cc8..ea7b6bce9ea0 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1593,6 +1593,7 @@ struct packet_offload { #define NETDEV_RELEASE 0x0012 #define NETDEV_NOTIFY_PEERS 0x0013 #define NETDEV_JOIN 0x0014 +#define NETDEV_CHANGEUPPER 0x0015 extern int register_netdevice_notifier(struct notifier_block *nb); extern int unregister_netdevice_notifier(struct notifier_block *nb); diff --git a/net/core/dev.c b/net/core/dev.c index 7229bc30e509..50c02ded1d69 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4411,7 +4411,7 @@ static int __netdev_upper_dev_link(struct net_device *dev, else list_add_tail_rcu(&upper->list, &dev->upper_dev_list); dev_hold(upper_dev); - + call_netdevice_notifiers(NETDEV_CHANGEUPPER, dev); return 0; } @@ -4471,6 +4471,7 @@ void netdev_upper_dev_unlink(struct net_device *dev, list_del_rcu(&upper->list); dev_put(upper_dev); kfree_rcu(upper, rcu); + call_netdevice_notifiers(NETDEV_CHANGEUPPER, dev); } EXPORT_SYMBOL(netdev_upper_dev_unlink); -- cgit v1.2.3 From 1f6afc81088a1f5a472b272408730c73b72c68aa Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 24 May 2013 15:03:54 +0000 Subject: tcp: remove one indentation level in tcp_rcv_state_process() Remove one level of indentation 'introduced' in commit c3ae62af8e75 (tcp: should drop incoming frames without ACK flag set) if (true) { ... } @acceptable variable is a boolean. This patch is a pure cleanup. Signed-off-by: Eric Dumazet Cc: Yuchung Cheng Signed-off-by: David S. Miller --- net/ipv4/tcp_input.c | 269 +++++++++++++++++++++++++-------------------------- 1 file changed, 133 insertions(+), 136 deletions(-) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 8230cd6243aa..40614257d2c5 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5536,6 +5536,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, struct inet_connection_sock *icsk = inet_csk(sk); struct request_sock *req; int queued = 0; + bool acceptable; tp->rx_opt.saw_tstamp = 0; @@ -5606,157 +5607,153 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, return 0; /* step 5: check the ACK field */ - if (true) { - int acceptable = tcp_ack(sk, skb, FLAG_SLOWPATH | - FLAG_UPDATE_TS_RECENT) > 0; - - switch (sk->sk_state) { - case TCP_SYN_RECV: - if (acceptable) { - /* Once we leave TCP_SYN_RECV, we no longer - * need req so release it. - */ - if (req) { - tcp_synack_rtt_meas(sk, req); - tp->total_retrans = req->num_retrans; - - reqsk_fastopen_remove(sk, req, false); - } else { - /* Make sure socket is routed, for - * correct metrics. - */ - icsk->icsk_af_ops->rebuild_header(sk); - tcp_init_congestion_control(sk); + acceptable = tcp_ack(sk, skb, FLAG_SLOWPATH | + FLAG_UPDATE_TS_RECENT) > 0; - tcp_mtup_init(sk); - tcp_init_buffer_space(sk); - tp->copied_seq = tp->rcv_nxt; - } - smp_mb(); - tcp_set_state(sk, TCP_ESTABLISHED); - sk->sk_state_change(sk); + switch (sk->sk_state) { + case TCP_SYN_RECV: + if (acceptable) { + /* Once we leave TCP_SYN_RECV, we no longer + * need req so release it. + */ + if (req) { + tcp_synack_rtt_meas(sk, req); + tp->total_retrans = req->num_retrans; - /* Note, that this wakeup is only for marginal - * crossed SYN case. Passively open sockets - * are not waked up, because sk->sk_sleep == - * NULL and sk->sk_socket == NULL. + reqsk_fastopen_remove(sk, req, false); + } else { + /* Make sure socket is routed, for + * correct metrics. */ - if (sk->sk_socket) - sk_wake_async(sk, - SOCK_WAKE_IO, POLL_OUT); - - tp->snd_una = TCP_SKB_CB(skb)->ack_seq; - tp->snd_wnd = ntohs(th->window) << - tp->rx_opt.snd_wscale; - tcp_init_wl(tp, TCP_SKB_CB(skb)->seq); - - if (tp->rx_opt.tstamp_ok) - tp->advmss -= TCPOLEN_TSTAMP_ALIGNED; - - if (req) { - /* Re-arm the timer because data may - * have been sent out. This is similar - * to the regular data transmission case - * when new data has just been ack'ed. - * - * (TFO) - we could try to be more - * aggressive and retranmitting any data - * sooner based on when they were sent - * out. - */ - tcp_rearm_rto(sk); - } else - tcp_init_metrics(sk); + icsk->icsk_af_ops->rebuild_header(sk); + tcp_init_congestion_control(sk); - /* Prevent spurious tcp_cwnd_restart() on - * first data packet. + tcp_mtup_init(sk); + tcp_init_buffer_space(sk); + tp->copied_seq = tp->rcv_nxt; + } + smp_mb(); + tcp_set_state(sk, TCP_ESTABLISHED); + sk->sk_state_change(sk); + + /* Note, that this wakeup is only for marginal + * crossed SYN case. Passively open sockets + * are not waked up, because sk->sk_sleep == + * NULL and sk->sk_socket == NULL. + */ + if (sk->sk_socket) + sk_wake_async(sk, SOCK_WAKE_IO, POLL_OUT); + + tp->snd_una = TCP_SKB_CB(skb)->ack_seq; + tp->snd_wnd = ntohs(th->window) << + tp->rx_opt.snd_wscale; + tcp_init_wl(tp, TCP_SKB_CB(skb)->seq); + + if (tp->rx_opt.tstamp_ok) + tp->advmss -= TCPOLEN_TSTAMP_ALIGNED; + + if (req) { + /* Re-arm the timer because data may + * have been sent out. This is similar + * to the regular data transmission case + * when new data has just been ack'ed. + * + * (TFO) - we could try to be more aggressive + * and retransmitting any data sooner based + * on when they are sent out. */ - tp->lsndtime = tcp_time_stamp; + tcp_rearm_rto(sk); + } else + tcp_init_metrics(sk); - tcp_initialize_rcv_mss(sk); - tcp_fast_path_on(tp); - } else { - return 1; - } - break; + /* Prevent spurious tcp_cwnd_restart() on + * first data packet. + */ + tp->lsndtime = tcp_time_stamp; - case TCP_FIN_WAIT1: - /* If we enter the TCP_FIN_WAIT1 state and we are a - * Fast Open socket and this is the first acceptable - * ACK we have received, this would have acknowledged - * our SYNACK so stop the SYNACK timer. + tcp_initialize_rcv_mss(sk); + tcp_fast_path_on(tp); + } else { + return 1; + } + break; + + case TCP_FIN_WAIT1: + /* If we enter the TCP_FIN_WAIT1 state and we are a + * Fast Open socket and this is the first acceptable + * ACK we have received, this would have acknowledged + * our SYNACK so stop the SYNACK timer. + */ + if (req != NULL) { + /* Return RST if ack_seq is invalid. + * Note that RFC793 only says to generate a + * DUPACK for it but for TCP Fast Open it seems + * better to treat this case like TCP_SYN_RECV + * above. */ - if (req != NULL) { - /* Return RST if ack_seq is invalid. - * Note that RFC793 only says to generate a - * DUPACK for it but for TCP Fast Open it seems - * better to treat this case like TCP_SYN_RECV - * above. - */ - if (!acceptable) + if (!acceptable) + return 1; + /* We no longer need the request sock. */ + reqsk_fastopen_remove(sk, req, false); + tcp_rearm_rto(sk); + } + if (tp->snd_una == tp->write_seq) { + struct dst_entry *dst; + + tcp_set_state(sk, TCP_FIN_WAIT2); + sk->sk_shutdown |= SEND_SHUTDOWN; + + dst = __sk_dst_get(sk); + if (dst) + dst_confirm(dst); + + if (!sock_flag(sk, SOCK_DEAD)) { + /* Wake up lingering close() */ + sk->sk_state_change(sk); + } else { + int tmo; + + if (tp->linger2 < 0 || + (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq && + after(TCP_SKB_CB(skb)->end_seq - th->fin, tp->rcv_nxt))) { + tcp_done(sk); + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPABORTONDATA); return 1; - /* We no longer need the request sock. */ - reqsk_fastopen_remove(sk, req, false); - tcp_rearm_rto(sk); - } - if (tp->snd_una == tp->write_seq) { - struct dst_entry *dst; - - tcp_set_state(sk, TCP_FIN_WAIT2); - sk->sk_shutdown |= SEND_SHUTDOWN; - - dst = __sk_dst_get(sk); - if (dst) - dst_confirm(dst); - - if (!sock_flag(sk, SOCK_DEAD)) - /* Wake up lingering close() */ - sk->sk_state_change(sk); - else { - int tmo; - - if (tp->linger2 < 0 || - (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq && - after(TCP_SKB_CB(skb)->end_seq - th->fin, tp->rcv_nxt))) { - tcp_done(sk); - NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPABORTONDATA); - return 1; - } + } - tmo = tcp_fin_time(sk); - if (tmo > TCP_TIMEWAIT_LEN) { - inet_csk_reset_keepalive_timer(sk, tmo - TCP_TIMEWAIT_LEN); - } else if (th->fin || sock_owned_by_user(sk)) { - /* Bad case. We could lose such FIN otherwise. - * It is not a big problem, but it looks confusing - * and not so rare event. We still can lose it now, - * if it spins in bh_lock_sock(), but it is really - * marginal case. - */ - inet_csk_reset_keepalive_timer(sk, tmo); - } else { - tcp_time_wait(sk, TCP_FIN_WAIT2, tmo); - goto discard; - } + tmo = tcp_fin_time(sk); + if (tmo > TCP_TIMEWAIT_LEN) { + inet_csk_reset_keepalive_timer(sk, tmo - TCP_TIMEWAIT_LEN); + } else if (th->fin || sock_owned_by_user(sk)) { + /* Bad case. We could lose such FIN otherwise. + * It is not a big problem, but it looks confusing + * and not so rare event. We still can lose it now, + * if it spins in bh_lock_sock(), but it is really + * marginal case. + */ + inet_csk_reset_keepalive_timer(sk, tmo); + } else { + tcp_time_wait(sk, TCP_FIN_WAIT2, tmo); + goto discard; } } - break; + } + break; - case TCP_CLOSING: - if (tp->snd_una == tp->write_seq) { - tcp_time_wait(sk, TCP_TIME_WAIT, 0); - goto discard; - } - break; + case TCP_CLOSING: + if (tp->snd_una == tp->write_seq) { + tcp_time_wait(sk, TCP_TIME_WAIT, 0); + goto discard; + } + break; - case TCP_LAST_ACK: - if (tp->snd_una == tp->write_seq) { - tcp_update_metrics(sk); - tcp_done(sk); - goto discard; - } - break; + case TCP_LAST_ACK: + if (tp->snd_una == tp->write_seq) { + tcp_update_metrics(sk); + tcp_done(sk); + goto discard; } + break; } /* step 6: check the URG bit */ -- cgit v1.2.3 From 61eb900352ff731d990d5415ce9f04e4af6a6136 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Fri, 24 May 2013 18:36:13 +0000 Subject: tcp: Remove another indentation level in tcp_rcv_state_process case TCP_SYN_RECV: can have another indentation level removed by converting if (acceptable) { ...; } else { return 1; } to if (!acceptable) return 1; ...; Reflow code and comments to fit 80 columns. Another pure cleanup patch. Signed-off-by: Joe Perches Improved-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv4/tcp_input.c | 110 ++++++++++++++++++++++++--------------------------- 1 file changed, 51 insertions(+), 59 deletions(-) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 40614257d2c5..413b480b9329 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5612,70 +5612,62 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, switch (sk->sk_state) { case TCP_SYN_RECV: - if (acceptable) { - /* Once we leave TCP_SYN_RECV, we no longer - * need req so release it. - */ - if (req) { - tcp_synack_rtt_meas(sk, req); - tp->total_retrans = req->num_retrans; + if (!acceptable) + return 1; - reqsk_fastopen_remove(sk, req, false); - } else { - /* Make sure socket is routed, for - * correct metrics. - */ - icsk->icsk_af_ops->rebuild_header(sk); - tcp_init_congestion_control(sk); + /* Once we leave TCP_SYN_RECV, we no longer need req + * so release it. + */ + if (req) { + tcp_synack_rtt_meas(sk, req); + tp->total_retrans = req->num_retrans; - tcp_mtup_init(sk); - tcp_init_buffer_space(sk); - tp->copied_seq = tp->rcv_nxt; - } - smp_mb(); - tcp_set_state(sk, TCP_ESTABLISHED); - sk->sk_state_change(sk); - - /* Note, that this wakeup is only for marginal - * crossed SYN case. Passively open sockets - * are not waked up, because sk->sk_sleep == - * NULL and sk->sk_socket == NULL. - */ - if (sk->sk_socket) - sk_wake_async(sk, SOCK_WAKE_IO, POLL_OUT); - - tp->snd_una = TCP_SKB_CB(skb)->ack_seq; - tp->snd_wnd = ntohs(th->window) << - tp->rx_opt.snd_wscale; - tcp_init_wl(tp, TCP_SKB_CB(skb)->seq); - - if (tp->rx_opt.tstamp_ok) - tp->advmss -= TCPOLEN_TSTAMP_ALIGNED; - - if (req) { - /* Re-arm the timer because data may - * have been sent out. This is similar - * to the regular data transmission case - * when new data has just been ack'ed. - * - * (TFO) - we could try to be more aggressive - * and retransmitting any data sooner based - * on when they are sent out. - */ - tcp_rearm_rto(sk); - } else - tcp_init_metrics(sk); + reqsk_fastopen_remove(sk, req, false); + } else { + /* Make sure socket is routed, for correct metrics. */ + icsk->icsk_af_ops->rebuild_header(sk); + tcp_init_congestion_control(sk); + + tcp_mtup_init(sk); + tcp_init_buffer_space(sk); + tp->copied_seq = tp->rcv_nxt; + } + smp_mb(); + tcp_set_state(sk, TCP_ESTABLISHED); + sk->sk_state_change(sk); - /* Prevent spurious tcp_cwnd_restart() on - * first data packet. + /* Note, that this wakeup is only for marginal crossed SYN case. + * Passively open sockets are not waked up, because + * sk->sk_sleep == NULL and sk->sk_socket == NULL. + */ + if (sk->sk_socket) + sk_wake_async(sk, SOCK_WAKE_IO, POLL_OUT); + + tp->snd_una = TCP_SKB_CB(skb)->ack_seq; + tp->snd_wnd = ntohs(th->window) << tp->rx_opt.snd_wscale; + tcp_init_wl(tp, TCP_SKB_CB(skb)->seq); + + if (tp->rx_opt.tstamp_ok) + tp->advmss -= TCPOLEN_TSTAMP_ALIGNED; + + if (req) { + /* Re-arm the timer because data may have been sent out. + * This is similar to the regular data transmission case + * when new data has just been ack'ed. + * + * (TFO) - we could try to be more aggressive and + * retransmitting any data sooner based on when they + * are sent out. */ - tp->lsndtime = tcp_time_stamp; + tcp_rearm_rto(sk); + } else + tcp_init_metrics(sk); - tcp_initialize_rcv_mss(sk); - tcp_fast_path_on(tp); - } else { - return 1; - } + /* Prevent spurious tcp_cwnd_restart() on first data packet */ + tp->lsndtime = tcp_time_stamp; + + tcp_initialize_rcv_mss(sk); + tcp_fast_path_on(tp); break; case TCP_FIN_WAIT1: -- cgit v1.2.3 From c48b22daa6062fff9eded311b4d6974c29b40487 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Fri, 24 May 2013 18:06:58 +0000 Subject: tcp: Remove 2 indentation levels in tcp_rcv_state_process case TCP_FIN_WAIT1 can also be simplified by reversing tests and adding breaks; Add braces after case and move automatic definitions. Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- net/ipv4/tcp_input.c | 76 +++++++++++++++++++++++++++------------------------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 413b480b9329..9579e1a5a144 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5670,7 +5670,10 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, tcp_fast_path_on(tp); break; - case TCP_FIN_WAIT1: + case TCP_FIN_WAIT1: { + struct dst_entry *dst; + int tmo; + /* If we enter the TCP_FIN_WAIT1 state and we are a * Fast Open socket and this is the first acceptable * ACK we have received, this would have acknowledged @@ -5689,48 +5692,47 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, reqsk_fastopen_remove(sk, req, false); tcp_rearm_rto(sk); } - if (tp->snd_una == tp->write_seq) { - struct dst_entry *dst; + if (tp->snd_una != tp->write_seq) + break; - tcp_set_state(sk, TCP_FIN_WAIT2); - sk->sk_shutdown |= SEND_SHUTDOWN; + tcp_set_state(sk, TCP_FIN_WAIT2); + sk->sk_shutdown |= SEND_SHUTDOWN; - dst = __sk_dst_get(sk); - if (dst) - dst_confirm(dst); + dst = __sk_dst_get(sk); + if (dst) + dst_confirm(dst); - if (!sock_flag(sk, SOCK_DEAD)) { - /* Wake up lingering close() */ - sk->sk_state_change(sk); - } else { - int tmo; - - if (tp->linger2 < 0 || - (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq && - after(TCP_SKB_CB(skb)->end_seq - th->fin, tp->rcv_nxt))) { - tcp_done(sk); - NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPABORTONDATA); - return 1; - } + if (!sock_flag(sk, SOCK_DEAD)) { + /* Wake up lingering close() */ + sk->sk_state_change(sk); + break; + } - tmo = tcp_fin_time(sk); - if (tmo > TCP_TIMEWAIT_LEN) { - inet_csk_reset_keepalive_timer(sk, tmo - TCP_TIMEWAIT_LEN); - } else if (th->fin || sock_owned_by_user(sk)) { - /* Bad case. We could lose such FIN otherwise. - * It is not a big problem, but it looks confusing - * and not so rare event. We still can lose it now, - * if it spins in bh_lock_sock(), but it is really - * marginal case. - */ - inet_csk_reset_keepalive_timer(sk, tmo); - } else { - tcp_time_wait(sk, TCP_FIN_WAIT2, tmo); - goto discard; - } - } + if (tp->linger2 < 0 || + (TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq && + after(TCP_SKB_CB(skb)->end_seq - th->fin, tp->rcv_nxt))) { + tcp_done(sk); + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPABORTONDATA); + return 1; + } + + tmo = tcp_fin_time(sk); + if (tmo > TCP_TIMEWAIT_LEN) { + inet_csk_reset_keepalive_timer(sk, tmo - TCP_TIMEWAIT_LEN); + } else if (th->fin || sock_owned_by_user(sk)) { + /* Bad case. We could lose such FIN otherwise. + * It is not a big problem, but it looks confusing + * and not so rare event. We still can lose it now, + * if it spins in bh_lock_sock(), but it is really + * marginal case. + */ + inet_csk_reset_keepalive_timer(sk, tmo); + } else { + tcp_time_wait(sk, TCP_FIN_WAIT2, tmo); + goto discard; } break; + } case TCP_CLOSING: if (tp->snd_una == tp->write_seq) { -- cgit v1.2.3 From 810e08ee425f8d7327e4b30069a9b2b3dccfc67d Mon Sep 17 00:00:00 2001 From: Fredrik Soderstedt Date: Wed, 17 Apr 2013 13:50:53 +0200 Subject: mmc: core: Only execute tuning for SDR50 and SDR104 Only execute tuning for sd and sdio devices that are using SDR50 or SDR104. Make sure clock is hold during tuning for sdio devices. Signed-off-by: Fredrik Soderstedt Acked-by: Johan Rudholm Acked-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/core/sd.c | 9 +++++++-- drivers/mmc/core/sdio.c | 12 ++++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 9e645e19cec6..8373d2288bb1 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -646,8 +646,13 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card) if (err) goto out; - /* SPI mode doesn't define CMD19 */ - if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning) { + /* + * SPI mode doesn't define CMD19 and tuning is only valid for SDR50 and + * SDR104 mode SD-cards. Note that tuning is mandatory for SDR104. + */ + if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning && + (card->sd_bus_speed == UHS_SDR50_BUS_SPEED || + card->sd_bus_speed == UHS_SDR104_BUS_SPEED)) { mmc_host_clk_hold(card->host); err = card->host->ops->execute_tuning(card->host, MMC_SEND_TUNING_BLOCK); diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 6889a821c1da..444668c210b2 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -563,10 +563,18 @@ static int mmc_sdio_init_uhs_card(struct mmc_card *card) if (err) goto out; - /* Initialize and start re-tuning timer */ - if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning) + /* + * SPI mode doesn't define CMD19 and tuning is only valid for SDR50 and + * SDR104 mode SD-cards. Note that tuning is mandatory for SDR104. + */ + if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning && + ((card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR50) || + (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR104))) { + mmc_host_clk_hold(card->host); err = card->host->ops->execute_tuning(card->host, MMC_SEND_TUNING_BLOCK); + mmc_host_clk_release(card->host); + } out: -- cgit v1.2.3 From b689167984bc14ed06c8bcff52ef5eb1fd9cf83b Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 18 Apr 2013 11:02:07 +0200 Subject: mmc: core: Re-use code for MMC_CAP2_DETECT_ON_ERR in polling mode Previously the MMC_CAP2_DETECT_ON_ERR was invented for detecting slow card removal. In was never a realy good solution and a proper fix has been merged using gpio debouncing instead. We remove this cap in this patch. Although when using polling card detect mode, the code invented for MMC_CAP2_DETECT_ON_ERR is re-used to complete card removal in an earlier phase. There are no need waiting for the polling timeout to elapse in this case. Signed-off-by: Ulf Hansson Reviewed-by: Kevin Liu Signed-off-by: Chris Ball --- drivers/mmc/core/core.c | 5 ++--- include/linux/mmc/host.h | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index c40396f23202..6e4d04df37e0 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2325,14 +2325,13 @@ int mmc_detect_card_removed(struct mmc_host *host) * The card will be considered unchanged unless we have been asked to * detect a change or host requires polling to provide card detection. */ - if (!host->detect_change && !(host->caps & MMC_CAP_NEEDS_POLL) && - !(host->caps2 & MMC_CAP2_DETECT_ON_ERR)) + if (!host->detect_change && !(host->caps & MMC_CAP_NEEDS_POLL)) return ret; host->detect_change = 0; if (!ret) { ret = _mmc_detect_card_removed(host); - if (ret && (host->caps2 & MMC_CAP2_DETECT_ON_ERR)) { + if (ret && (host->caps & MMC_CAP_NEEDS_POLL)) { /* * Schedule a detect work as soon as possible to let a * rescan handle the card removal. diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index e326ae2882a0..38f60a97b218 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -272,7 +272,6 @@ struct mmc_host { #define MMC_CAP2_HS200 (MMC_CAP2_HS200_1_8V_SDR | \ MMC_CAP2_HS200_1_2V_SDR) #define MMC_CAP2_BROKEN_VOLTAGE (1 << 7) /* Use the broken voltage */ -#define MMC_CAP2_DETECT_ON_ERR (1 << 8) /* On I/O err check card removal */ #define MMC_CAP2_HC_ERASE_SZ (1 << 9) /* High-capacity erase size */ #define MMC_CAP2_CD_ACTIVE_HIGH (1 << 10) /* Card-detect signal active high */ #define MMC_CAP2_RO_ACTIVE_HIGH (1 << 11) /* Write-protect signal active high */ -- cgit v1.2.3 From 775a9362b5d7e006ff6bbec5cb9c9c9d5a751696 Mon Sep 17 00:00:00 2001 From: Maya Erez Date: Thu, 18 Apr 2013 15:41:55 +0300 Subject: mmc: card: Adding support for sanitize in eMMC 4.5 The sanitize support is added as a user-app ioctl call, and was removed from the block-device request, since its purpose is to be invoked not via File-System but by a user. This feature deletes the unmap memory region of the eMMC card, by writing to a specific register in the EXT_CSD. unmap region is the memory region that was previously deleted (by erase, trim or discard operation). In order to avoid timeout when sanitizing large-scale cards, the timeout for sanitize operation is 240 seconds. Signed-off-by: Yaniv Gardi Signed-off-by: Maya Erez Signed-off-by: Chris Ball --- drivers/mmc/card/block.c | 68 ++++++++++++++++++++++++++++++++-------------- drivers/mmc/card/queue.c | 2 +- drivers/mmc/core/core.c | 19 +++++++++++++ drivers/mmc/core/mmc_ops.c | 2 ++ include/linux/mmc/core.h | 2 ++ include/linux/mmc/host.h | 1 + 6 files changed, 72 insertions(+), 22 deletions(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index dd27b0783d52..80b05b280241 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -58,6 +58,8 @@ MODULE_ALIAS("mmc:block"); #define INAND_CMD38_ARG_SECTRIM1 0x81 #define INAND_CMD38_ARG_SECTRIM2 0x88 #define MMC_BLK_TIMEOUT_MS (10 * 60 * 1000) /* 10 minute timeout */ +#define MMC_SANITIZE_REQ_TIMEOUT 240000 +#define MMC_EXTRACT_INDEX_FROM_ARG(x) ((x & 0x00FF0000) >> 16) #define mmc_req_rel_wr(req) (((req->cmd_flags & REQ_FUA) || \ (req->cmd_flags & REQ_META)) && \ @@ -408,6 +410,35 @@ static int ioctl_rpmb_card_status_poll(struct mmc_card *card, u32 *status, return err; } +static int ioctl_do_sanitize(struct mmc_card *card) +{ + int err; + + if (!(mmc_can_sanitize(card) && + (card->host->caps2 & MMC_CAP2_SANITIZE))) { + pr_warn("%s: %s - SANITIZE is not supported\n", + mmc_hostname(card->host), __func__); + err = -EOPNOTSUPP; + goto out; + } + + pr_debug("%s: %s - SANITIZE IN PROGRESS...\n", + mmc_hostname(card->host), __func__); + + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_SANITIZE_START, 1, + MMC_SANITIZE_REQ_TIMEOUT); + + if (err) + pr_err("%s: %s - EXT_CSD_SANITIZE_START failed. err=%d\n", + mmc_hostname(card->host), __func__, err); + + pr_debug("%s: %s - SANITIZE COMPLETED\n", mmc_hostname(card->host), + __func__); +out: + return err; +} + static int mmc_blk_ioctl_cmd(struct block_device *bdev, struct mmc_ioc_cmd __user *ic_ptr) { @@ -510,6 +541,16 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, goto cmd_rel_host; } + if (MMC_EXTRACT_INDEX_FROM_ARG(cmd.arg) == EXT_CSD_SANITIZE_START) { + err = ioctl_do_sanitize(card); + + if (err) + pr_err("%s: ioctl_do_sanitize() failed. err = %d", + __func__, err); + + goto cmd_rel_host; + } + mmc_wait_for_req(card->host, &mrq); if (cmd.error) { @@ -939,10 +980,10 @@ static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq, { struct mmc_blk_data *md = mq->data; struct mmc_card *card = md->queue.card; - unsigned int from, nr, arg, trim_arg, erase_arg; + unsigned int from, nr, arg; int err = 0, type = MMC_BLK_SECDISCARD; - if (!(mmc_can_secure_erase_trim(card) || mmc_can_sanitize(card))) { + if (!(mmc_can_secure_erase_trim(card))) { err = -EOPNOTSUPP; goto out; } @@ -950,23 +991,11 @@ static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq, from = blk_rq_pos(req); nr = blk_rq_sectors(req); - /* The sanitize operation is supported at v4.5 only */ - if (mmc_can_sanitize(card)) { - erase_arg = MMC_ERASE_ARG; - trim_arg = MMC_TRIM_ARG; - } else { - erase_arg = MMC_SECURE_ERASE_ARG; - trim_arg = MMC_SECURE_TRIM1_ARG; - } + if (mmc_can_trim(card) && !mmc_erase_group_aligned(card, from, nr)) + arg = MMC_SECURE_TRIM1_ARG; + else + arg = MMC_SECURE_ERASE_ARG; - if (mmc_erase_group_aligned(card, from, nr)) - arg = erase_arg; - else if (mmc_can_trim(card)) - arg = trim_arg; - else { - err = -EINVAL; - goto out; - } retry: if (card->quirks & MMC_QUIRK_INAND_CMD38) { err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, @@ -1002,9 +1031,6 @@ retry: goto out; } - if (mmc_can_sanitize(card)) - err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_SANITIZE_START, 1, 0); out_retry: if (err && !mmc_blk_reset(md, card->host, type)) goto retry; diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c index 9447a0e970d1..fa9632eb63f1 100644 --- a/drivers/mmc/card/queue.c +++ b/drivers/mmc/card/queue.c @@ -173,7 +173,7 @@ static void mmc_queue_setup_discard(struct request_queue *q, /* granularity must not be greater than max. discard */ if (card->pref_erase > max_discard) q->limits.discard_granularity = 0; - if (mmc_can_secure_erase_trim(card) || mmc_can_sanitize(card)) + if (mmc_can_secure_erase_trim(card)) queue_flag_set_unlocked(QUEUE_FLAG_SECDISCARD, q); } diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 6e4d04df37e0..48b9fec34737 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -402,6 +402,7 @@ static int mmc_wait_for_data_req_done(struct mmc_host *host, context_info->is_done_rcv = false; context_info->is_new_req = false; cmd = mrq->cmd; + if (!cmd->error || !cmd->retries || mmc_card_removed(host->card)) { err = host->areq->err_check(host->card, @@ -436,6 +437,24 @@ static void mmc_wait_for_req_done(struct mmc_host *host, wait_for_completion(&mrq->completion); cmd = mrq->cmd; + + /* + * If host has timed out waiting for the sanitize + * to complete, card might be still in programming state + * so let's try to bring the card out of programming + * state. + */ + if (cmd->sanitize_busy && cmd->error == -ETIMEDOUT) { + if (!mmc_interrupt_hpi(host->card)) { + pr_warning("%s: %s: Interrupted sanitize\n", + mmc_hostname(host), __func__); + cmd->error = 0; + break; + } else { + pr_err("%s: %s: Failed to interrupt sanitize\n", + mmc_hostname(host), __func__); + } + } if (!cmd->error || !cmd->retries || mmc_card_removed(host->card)) break; diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 49f04bc9d0eb..124af5238d0a 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -431,6 +431,8 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, cmd.cmd_timeout_ms = timeout_ms; + if (index == EXT_CSD_SANITIZE_START) + cmd.sanitize_busy = true; err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); if (err) diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index 39613b9a6fc5..bd06ff566bfc 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -96,6 +96,8 @@ struct mmc_command { */ unsigned int cmd_timeout_ms; /* in milliseconds */ + /* Set this flag only for blocking sanitize request */ + bool sanitize_busy; struct mmc_data *data; /* data segment associated with cmd */ struct mmc_request *mrq; /* associated request */ diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 38f60a97b218..6f3851533de0 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -280,6 +280,7 @@ struct mmc_host { #define MMC_CAP2_PACKED_CMD (MMC_CAP2_PACKED_RD | \ MMC_CAP2_PACKED_WR) #define MMC_CAP2_NO_PRESCAN_POWERUP (1 << 14) /* Don't power up before scan */ +#define MMC_CAP2_SANITIZE (1 << 15) /* Support Sanitize */ mmc_pm_flag_t pm_caps; /* supported pm features */ -- cgit v1.2.3 From ef7aef9ab41d9aa95e9c2e2fdd4b5dbbf890f1d7 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Fri, 19 Apr 2013 09:25:45 +0800 Subject: mmc: dw_mmc: fix error return code in dw_mci_probe() Fix to return -ENOMEM in alloc workqueue error case instead of 0, as done elsewhere in this function. Signed-off-by: Wei Yongjun Acked-by: Seungwon Jeon Signed-off-by: Chris Ball --- drivers/mmc/host/dw_mmc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index bc3a1bc4940f..0652690f2d66 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -2321,8 +2321,10 @@ int dw_mci_probe(struct dw_mci *host) tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host); host->card_workqueue = alloc_workqueue("dw-mci-card", WQ_MEM_RECLAIM | WQ_NON_REENTRANT, 1); - if (!host->card_workqueue) + if (!host->card_workqueue) { + ret = -ENOMEM; goto err_dmaunmap; + } INIT_WORK(&host->card_work, dw_mci_work_routine_card); ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt, host->irq_flags, "dw-mci", host); -- cgit v1.2.3 From 0c5ce16bc11ae3b80d2e3f6caf4483162acb1f62 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Fri, 19 Apr 2013 10:11:11 +0800 Subject: mmc: mxs-mmc: fix error return code in mxs_mmc_probe() Fix to return -ENODEV in the request dma error case instead of 0, as done elsewhere in this function. Signed-off-by: Wei Yongjun Reviewed-by: Marek Vasut Signed-off-by: Chris Ball --- drivers/mmc/host/mxs-mmc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index 4278a1787d08..a09ba6e22136 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -639,6 +639,7 @@ static int mxs_mmc_probe(struct platform_device *pdev) if (!ssp->dmach) { dev_err(mmc_dev(host->mmc), "%s: failed to request dma\n", __func__); + ret = -ENODEV; goto out_clk_put; } -- cgit v1.2.3 From 39b9431b0f371294dd2d1a492cf77f2f17390a1f Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 2 May 2013 14:02:36 +0200 Subject: mmc: core: Stop bkops for eMMC only from mmc suspend Move mmc suspend specific operations to be executed from the .suspend callback in the mmc bus_ops. This simplifies the mmc_suspend_host function which is supposed to handle nothing but common suspend tasks. Since eMMC can be considered non-removable there are no need to check for ongoing bkops at PM_SUSPEND_PREPARE notification so remove it. Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/core/core.c | 22 +--------------------- drivers/mmc/core/mmc.c | 6 ++++++ 2 files changed, 7 insertions(+), 21 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 48b9fec34737..d856871ed6cb 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2651,14 +2651,8 @@ int mmc_suspend_host(struct mmc_host *host) mmc_bus_get(host); if (host->bus_ops && !host->bus_dead) { - if (host->bus_ops->suspend) { - if (mmc_card_doing_bkops(host->card)) { - err = mmc_stop_bkops(host->card); - if (err) - goto out; - } + if (host->bus_ops->suspend) err = host->bus_ops->suspend(host); - } if (err == -ENOSYS || !host->bus_ops->resume) { /* @@ -2682,10 +2676,8 @@ int mmc_suspend_host(struct mmc_host *host) if (!err && !mmc_card_keep_power(host)) mmc_power_off(host); -out: return err; } - EXPORT_SYMBOL(mmc_suspend_host); /** @@ -2740,22 +2732,10 @@ int mmc_pm_notify(struct notifier_block *notify_block, struct mmc_host *host = container_of( notify_block, struct mmc_host, pm_notify); unsigned long flags; - int err = 0; switch (mode) { case PM_HIBERNATION_PREPARE: case PM_SUSPEND_PREPARE: - if (host->card && mmc_card_mmc(host->card) && - mmc_card_doing_bkops(host->card)) { - err = mmc_stop_bkops(host->card); - if (err) { - pr_err("%s: didn't stop bkops\n", - mmc_hostname(host)); - return err; - } - mmc_card_clr_doing_bkops(host->card); - } - spin_lock_irqsave(&host->lock, flags); host->rescan_disable = 1; spin_unlock_irqrestore(&host->lock, flags); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 0cbd1effe960..a0469cf7933f 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1411,6 +1411,12 @@ static int mmc_suspend(struct mmc_host *host) mmc_claim_host(host); + if (mmc_card_doing_bkops(host->card)) { + err = mmc_stop_bkops(host->card); + if (err) + goto out; + } + err = mmc_cache_ctrl(host, 0); if (err) goto out; -- cgit v1.2.3 From 12d01d0b813b93e7bde1b5f468b5c85aa8b33590 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 2 May 2013 14:02:37 +0200 Subject: mmc: core: Add bus_ops for runtime pm callbacks SDIO is the only protocol that uses runtime pm for the card device right now. To provide the option for sd and mmc to use runtime pm as well the bus_ops callback are extended with two new functions. One for runtime_suspend and one for runtime_resume. This patch will also implement the callbacks for SDIO to make sure existing functionality is maintained. It also prepares to move away from using the mmc_power_restore_host API, since it is not needed when using runtime PM. Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/core/bus.c | 14 ++++++++++++-- drivers/mmc/core/core.c | 2 +- drivers/mmc/core/core.h | 3 +++ drivers/mmc/core/sdio.c | 16 ++++++++++++++++ 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index e219c97a02a4..d9e8c2b7f4c5 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -151,15 +151,25 @@ static int mmc_bus_resume(struct device *dev) static int mmc_runtime_suspend(struct device *dev) { struct mmc_card *card = mmc_dev_to_card(dev); + struct mmc_host *host = card->host; + int ret = 0; + + if (host->bus_ops->runtime_suspend) + ret = host->bus_ops->runtime_suspend(host); - return mmc_power_save_host(card->host); + return ret; } static int mmc_runtime_resume(struct device *dev) { struct mmc_card *card = mmc_dev_to_card(dev); + struct mmc_host *host = card->host; + int ret = 0; + + if (host->bus_ops->runtime_resume) + ret = host->bus_ops->runtime_resume(host); - return mmc_power_restore_host(card->host); + return ret; } static int mmc_runtime_idle(struct device *dev) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index d856871ed6cb..dc0cb5929c64 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1478,7 +1478,7 @@ void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type) * If a host does all the power sequencing itself, ignore the * initial MMC_POWER_UP stage. */ -static void mmc_power_up(struct mmc_host *host) +void mmc_power_up(struct mmc_host *host) { int bit; diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index b9f18a2a8874..6242ffb789c4 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -22,6 +22,8 @@ struct mmc_bus_ops { void (*detect)(struct mmc_host *); int (*suspend)(struct mmc_host *); int (*resume)(struct mmc_host *); + int (*runtime_suspend)(struct mmc_host *); + int (*runtime_resume)(struct mmc_host *); int (*power_save)(struct mmc_host *); int (*power_restore)(struct mmc_host *); int (*alive)(struct mmc_host *); @@ -44,6 +46,7 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage); int __mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage); void mmc_set_timing(struct mmc_host *host, unsigned int timing); void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type); +void mmc_power_up(struct mmc_host *host); void mmc_power_off(struct mmc_host *host); void mmc_power_cycle(struct mmc_host *host); diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 444668c210b2..1fbbd1bc55ec 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -1059,11 +1059,27 @@ out: return ret; } +static int mmc_sdio_runtime_suspend(struct mmc_host *host) +{ + /* No references to the card, cut the power to it. */ + mmc_power_off(host); + return 0; +} + +static int mmc_sdio_runtime_resume(struct mmc_host *host) +{ + /* Restore power and re-initialize. */ + mmc_power_up(host); + return mmc_sdio_power_restore(host); +} + static const struct mmc_bus_ops mmc_sdio_ops = { .remove = mmc_sdio_remove, .detect = mmc_sdio_detect, .suspend = mmc_sdio_suspend, .resume = mmc_sdio_resume, + .runtime_suspend = mmc_sdio_runtime_suspend, + .runtime_resume = mmc_sdio_runtime_resume, .power_restore = mmc_sdio_power_restore, .alive = mmc_sdio_alive, }; -- cgit v1.2.3 From e94cfef698aae6b209d8918dd319312e4b02118d Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 2 May 2013 14:02:38 +0200 Subject: mmc: block: Enable runtime pm for mmc blkdevice Once the mmc blkdevice is being probed, runtime pm will be enabled. By using runtime autosuspend, the power save operations can be done when request inactivity occurs for a certain time. Right now the selected timeout value is set to 3 s. Obviously this value will likely need to be configurable somehow since it needs to be trimmed depending on the power save algorithm. For SD-combo cards, we are still leaving the enablement of runtime PM to the SDIO init sequence since it depends on the capabilities of the SDIO func driver. Moreover, when the blk device is being suspended, we make sure the device will be runtime resumed. The reason for doing this is that we want the host suspend sequence to be unaware of any runtime power save operations done for the card in this phase. Thus it can just handle the suspend as the card is fully powered from a runtime perspective. Finally, this patch prepares to make it possible to move BKOPS handling into the runtime callbacks for the mmc bus_ops. Thus IDLE BKOPS can be accomplished. Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/card/block.c | 32 ++++++++++++++++++++++++++------ drivers/mmc/core/core.c | 23 +++++++++++++++++++++++ drivers/mmc/core/debugfs.c | 8 ++++---- drivers/mmc/core/mmc.c | 4 ++-- drivers/mmc/core/sd.c | 4 ++-- include/linux/mmc/core.h | 3 +++ 6 files changed, 60 insertions(+), 14 deletions(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 80b05b280241..c900d2818aa7 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -224,7 +225,7 @@ static ssize_t power_ro_lock_store(struct device *dev, md = mmc_blk_get(dev_to_disk(dev)); card = md->queue.card; - mmc_claim_host(card->host); + mmc_get_card(card); ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BOOT_WP, card->ext_csd.boot_ro_lock | @@ -235,7 +236,7 @@ static ssize_t power_ro_lock_store(struct device *dev, else card->ext_csd.boot_ro_lock |= EXT_CSD_BOOT_WP_B_PWR_WP_EN; - mmc_release_host(card->host); + mmc_put_card(card); if (!ret) { pr_info("%s: Locking boot partition ro until next power on\n", @@ -522,7 +523,7 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, mrq.cmd = &cmd; - mmc_claim_host(card->host); + mmc_get_card(card); err = mmc_blk_part_switch(card, md); if (err) @@ -599,7 +600,7 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, } cmd_rel_host: - mmc_release_host(card->host); + mmc_put_card(card); cmd_done: mmc_blk_put(md); @@ -1921,7 +1922,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) if (req && !mq->mqrq_prev->req) /* claim host only for the first request */ - mmc_claim_host(card->host); + mmc_get_card(card); ret = mmc_blk_part_switch(card, md); if (ret) { @@ -1965,7 +1966,7 @@ out: * In case sepecial request, there is no reentry to * the 'mmc_blk_issue_rq' with 'mqrq_prev->req'. */ - mmc_release_host(card->host); + mmc_put_card(card); return ret; } @@ -2362,6 +2363,19 @@ static int mmc_blk_probe(struct mmc_card *card) if (mmc_add_disk(part_md)) goto out; } + + pm_runtime_set_autosuspend_delay(&card->dev, 3000); + pm_runtime_use_autosuspend(&card->dev); + + /* + * Don't enable runtime PM for SD-combo cards here. Leave that + * decision to be taken during the SDIO init sequence instead. + */ + if (card->type != MMC_TYPE_SD_COMBO) { + pm_runtime_set_active(&card->dev); + pm_runtime_enable(&card->dev); + } + return 0; out: @@ -2375,9 +2389,13 @@ static void mmc_blk_remove(struct mmc_card *card) struct mmc_blk_data *md = mmc_get_drvdata(card); mmc_blk_remove_parts(card, md); + pm_runtime_get_sync(&card->dev); mmc_claim_host(card->host); mmc_blk_part_switch(card, md); mmc_release_host(card->host); + if (card->type != MMC_TYPE_SD_COMBO) + pm_runtime_disable(&card->dev); + pm_runtime_put_noidle(&card->dev); mmc_blk_remove_req(md); mmc_set_drvdata(card, NULL); } @@ -2389,6 +2407,7 @@ static int mmc_blk_suspend(struct mmc_card *card) struct mmc_blk_data *md = mmc_get_drvdata(card); if (md) { + pm_runtime_get_sync(&card->dev); mmc_queue_suspend(&md->queue); list_for_each_entry(part_md, &md->part, part) { mmc_queue_suspend(&part_md->queue); @@ -2412,6 +2431,7 @@ static int mmc_blk_resume(struct mmc_card *card) list_for_each_entry(part_md, &md->part, part) { mmc_queue_resume(&part_md->queue); } + pm_runtime_put(&card->dev); } return 0; } diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index dc0cb5929c64..0f86144b0c51 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -970,6 +970,29 @@ void mmc_release_host(struct mmc_host *host) } EXPORT_SYMBOL(mmc_release_host); +/* + * This is a helper function, which fetches a runtime pm reference for the + * card device and also claims the host. + */ +void mmc_get_card(struct mmc_card *card) +{ + pm_runtime_get_sync(&card->dev); + mmc_claim_host(card->host); +} +EXPORT_SYMBOL(mmc_get_card); + +/* + * This is a helper function, which releases the host and drops the runtime + * pm reference for the card device. + */ +void mmc_put_card(struct mmc_card *card) +{ + mmc_release_host(card->host); + pm_runtime_mark_last_busy(&card->dev); + pm_runtime_put_autosuspend(&card->dev); +} +EXPORT_SYMBOL(mmc_put_card); + /* * Internal function that does the actual ios call to the host driver, * optionally printing some debug output. diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c index 35c2f85b1956..54829c0ed000 100644 --- a/drivers/mmc/core/debugfs.c +++ b/drivers/mmc/core/debugfs.c @@ -258,13 +258,13 @@ static int mmc_dbg_card_status_get(void *data, u64 *val) u32 status; int ret; - mmc_claim_host(card->host); + mmc_get_card(card); ret = mmc_send_status(data, &status); if (!ret) *val = status; - mmc_release_host(card->host); + mmc_put_card(card); return ret; } @@ -291,9 +291,9 @@ static int mmc_ext_csd_open(struct inode *inode, struct file *filp) goto out_free; } - mmc_claim_host(card->host); + mmc_get_card(card); err = mmc_send_ext_csd(card, ext_csd); - mmc_release_host(card->host); + mmc_put_card(card); if (err) goto out_free; diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index a0469cf7933f..903f38150180 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1380,14 +1380,14 @@ static void mmc_detect(struct mmc_host *host) BUG_ON(!host); BUG_ON(!host->card); - mmc_claim_host(host); + mmc_get_card(host->card); /* * Just check if our card has been removed. */ err = _mmc_detect_card_removed(host); - mmc_release_host(host); + mmc_put_card(host->card); if (err) { mmc_remove(host); diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 8373d2288bb1..17fa2d271dd4 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -1042,14 +1042,14 @@ static void mmc_sd_detect(struct mmc_host *host) BUG_ON(!host); BUG_ON(!host->card); - mmc_claim_host(host); + mmc_get_card(host->card); /* * Just check if our card has been removed. */ err = _mmc_detect_card_removed(host); - mmc_release_host(host); + mmc_put_card(host->card); if (err) { mmc_sd_remove(host); diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index bd06ff566bfc..443243b241d5 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -190,6 +190,9 @@ extern int __mmc_claim_host(struct mmc_host *host, atomic_t *abort); extern void mmc_release_host(struct mmc_host *host); extern int mmc_try_claim_host(struct mmc_host *host); +extern void mmc_get_card(struct mmc_card *card); +extern void mmc_put_card(struct mmc_card *card); + extern int mmc_flush_cache(struct mmc_card *); extern int mmc_detect_card_removed(struct mmc_host *host); -- cgit v1.2.3 From c4d770d72492df510077b277f21ac5f0dad9e5eb Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 2 May 2013 14:02:39 +0200 Subject: mmc: core: Support aggressive power management for (e)MMC/SD Aggressive power management is suitable when saving power is essential. At request inactivity timeout, aka pm runtime autosuspend timeout, the card will be suspended. Once a new request arrives, the card will be re-initalized and thus the first request will suffer from a latency. This latency is card-specific, experiments has shown in general that SD-cards has quite poor initialization time, around 300ms-1100ms. eMMC is not surprisingly far better but still a couple of hundreds of ms has been observed. Except for the request latency, it is important to know that suspending the card will also prevent the card from executing internal house-keeping operations in idle mode. This could mean degradation in performance. To use this feature make sure the request inactivity timeout is chosen carefully. This has not been done as a part of this patch. Enable this feature by using host cap MMC_CAP_AGGRESSIVE_PM and by setting CONFIG_MMC_UNSAFE_RESUME. Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/core/mmc.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/mmc/core/sd.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/mmc/host.h | 2 +- 3 files changed, 100 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 903f38150180..506f4ee84e12 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1454,6 +1454,54 @@ static int mmc_resume(struct mmc_host *host) return err; } + +/* + * Callback for runtime_suspend. + */ +static int mmc_runtime_suspend(struct mmc_host *host) +{ + int err; + + if (!(host->caps & MMC_CAP_AGGRESSIVE_PM)) + return 0; + + mmc_claim_host(host); + + err = mmc_suspend(host); + if (err) { + pr_err("%s: error %d doing aggessive suspend\n", + mmc_hostname(host), err); + goto out; + } + mmc_power_off(host); + +out: + mmc_release_host(host); + return err; +} + +/* + * Callback for runtime_resume. + */ +static int mmc_runtime_resume(struct mmc_host *host) +{ + int err; + + if (!(host->caps & MMC_CAP_AGGRESSIVE_PM)) + return 0; + + mmc_claim_host(host); + + mmc_power_up(host); + err = mmc_resume(host); + if (err) + pr_err("%s: error %d doing aggessive resume\n", + mmc_hostname(host), err); + + mmc_release_host(host); + return 0; +} + static int mmc_power_restore(struct mmc_host *host) { int ret; @@ -1514,6 +1562,8 @@ static const struct mmc_bus_ops mmc_ops_unsafe = { .detect = mmc_detect, .suspend = mmc_suspend, .resume = mmc_resume, + .runtime_suspend = mmc_runtime_suspend, + .runtime_resume = mmc_runtime_resume, .power_restore = mmc_power_restore, .alive = mmc_alive, }; diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 17fa2d271dd4..aeaae7c3b22b 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -1100,6 +1100,53 @@ static int mmc_sd_resume(struct mmc_host *host) return err; } +/* + * Callback for runtime_suspend. + */ +static int mmc_sd_runtime_suspend(struct mmc_host *host) +{ + int err; + + if (!(host->caps & MMC_CAP_AGGRESSIVE_PM)) + return 0; + + mmc_claim_host(host); + + err = mmc_sd_suspend(host); + if (err) { + pr_err("%s: error %d doing aggessive suspend\n", + mmc_hostname(host), err); + goto out; + } + mmc_power_off(host); + +out: + mmc_release_host(host); + return err; +} + +/* + * Callback for runtime_resume. + */ +static int mmc_sd_runtime_resume(struct mmc_host *host) +{ + int err; + + if (!(host->caps & MMC_CAP_AGGRESSIVE_PM)) + return 0; + + mmc_claim_host(host); + + mmc_power_up(host); + err = mmc_sd_resume(host); + if (err) + pr_err("%s: error %d doing aggessive resume\n", + mmc_hostname(host), err); + + mmc_release_host(host); + return 0; +} + static int mmc_sd_power_restore(struct mmc_host *host) { int ret; @@ -1124,6 +1171,8 @@ static const struct mmc_bus_ops mmc_sd_ops = { static const struct mmc_bus_ops mmc_sd_ops_unsafe = { .remove = mmc_sd_remove, .detect = mmc_sd_detect, + .runtime_suspend = mmc_sd_runtime_suspend, + .runtime_resume = mmc_sd_runtime_resume, .suspend = mmc_sd_suspend, .resume = mmc_sd_resume, .power_restore = mmc_sd_power_restore, diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 6f3851533de0..374098bae831 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -239,7 +239,7 @@ struct mmc_host { #define MMC_CAP_SPI (1 << 4) /* Talks only SPI protocols */ #define MMC_CAP_NEEDS_POLL (1 << 5) /* Needs polling for card-detection */ #define MMC_CAP_8_BIT_DATA (1 << 6) /* Can the host do 8 bit transfers */ - +#define MMC_CAP_AGGRESSIVE_PM (1 << 7) /* Suspend (e)MMC/SD at idle */ #define MMC_CAP_NONREMOVABLE (1 << 8) /* Nonremovable e.g. eMMC */ #define MMC_CAP_WAIT_WHILE_BUSY (1 << 9) /* Waits while card is busy */ #define MMC_CAP_ERASE (1 << 10) /* Allow erase/trim commands */ -- cgit v1.2.3 From 07a682160866e302d696f5c76d74024d575fb79d Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 19 Apr 2013 15:12:11 +0200 Subject: mmc: core: Restructure and simplify code for mmc sleep|awake The mmc_card_sleep|awake APIs are not being used since the support is already properly encapsulated within the suspend sequence. Sleep|awake command is also specific for eMMC. We remove the sleep|awake bus_ops, the mmc_card_sleep|awake APIs and move the code into the mmc specific core instead. This also includes the mmc ops function, mmc_sleepawake. All releated functions have then become static and we have got far less code to maintain. Additionally this patch also simplifies the code from mmc_sleepawake, since it is only used to put the card to sleep and not awake. Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/core/core.c | 46 --------------------------- drivers/mmc/core/core.h | 2 -- drivers/mmc/core/mmc.c | 77 ++++++++++++++++++++++++---------------------- drivers/mmc/core/mmc_ops.c | 34 -------------------- drivers/mmc/core/mmc_ops.h | 1 - include/linux/mmc/host.h | 4 --- 6 files changed, 41 insertions(+), 123 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 0f86144b0c51..e9a104b9e4d3 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2550,52 +2550,6 @@ int mmc_power_restore_host(struct mmc_host *host) } EXPORT_SYMBOL(mmc_power_restore_host); -int mmc_card_awake(struct mmc_host *host) -{ - int err = -ENOSYS; - - if (host->caps2 & MMC_CAP2_NO_SLEEP_CMD) - return 0; - - mmc_bus_get(host); - - if (host->bus_ops && !host->bus_dead && host->bus_ops->awake) - err = host->bus_ops->awake(host); - - mmc_bus_put(host); - - return err; -} -EXPORT_SYMBOL(mmc_card_awake); - -int mmc_card_sleep(struct mmc_host *host) -{ - int err = -ENOSYS; - - if (host->caps2 & MMC_CAP2_NO_SLEEP_CMD) - return 0; - - mmc_bus_get(host); - - if (host->bus_ops && !host->bus_dead && host->bus_ops->sleep) - err = host->bus_ops->sleep(host); - - mmc_bus_put(host); - - return err; -} -EXPORT_SYMBOL(mmc_card_sleep); - -int mmc_card_can_sleep(struct mmc_host *host) -{ - struct mmc_card *card = host->card; - - if (card && mmc_card_mmc(card) && card->ext_csd.rev >= 3) - return 1; - return 0; -} -EXPORT_SYMBOL(mmc_card_can_sleep); - /* * Flush the cache to the non-volatile storage. */ diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 6242ffb789c4..52a3650307af 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -16,8 +16,6 @@ #define MMC_CMD_RETRIES 3 struct mmc_bus_ops { - int (*awake)(struct mmc_host *); - int (*sleep)(struct mmc_host *); void (*remove)(struct mmc_host *); void (*detect)(struct mmc_host *); int (*suspend)(struct mmc_host *); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 506f4ee84e12..dd6810eebd3f 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1321,6 +1321,45 @@ err: return err; } +static int mmc_can_sleep(struct mmc_card *card) +{ + return (card && card->ext_csd.rev >= 3); +} + +static int mmc_sleep(struct mmc_host *host) +{ + struct mmc_command cmd = {0}; + struct mmc_card *card = host->card; + int err; + + if (host->caps2 & MMC_CAP2_NO_SLEEP_CMD) + return 0; + + err = mmc_deselect_cards(host); + if (err) + return err; + + cmd.opcode = MMC_SLEEP_AWAKE; + cmd.arg = card->rca << 16; + cmd.arg |= 1 << 15; + + cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; + err = mmc_wait_for_cmd(host, &cmd, 0); + if (err) + return err; + + /* + * If the host does not wait while the card signals busy, then we will + * will have to wait the sleep/awake timeout. Note, we cannot use the + * SEND_STATUS command to poll the status because that command (and most + * others) is invalid while the card sleeps. + */ + if (!(host->caps & MMC_CAP_WAIT_WHILE_BUSY)) + mmc_delay(DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000)); + + return err; +} + static int mmc_can_poweroff_notify(const struct mmc_card *card) { return card && @@ -1423,8 +1462,8 @@ static int mmc_suspend(struct mmc_host *host) if (mmc_can_poweroff_notify(host->card)) err = mmc_poweroff_notify(host->card, EXT_CSD_POWER_OFF_SHORT); - else if (mmc_card_can_sleep(host)) - err = mmc_card_sleep(host); + else if (mmc_can_sleep(host->card)) + err = mmc_sleep(host); else if (!mmc_host_is_spi(host)) err = mmc_deselect_cards(host); host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200); @@ -1514,39 +1553,7 @@ static int mmc_power_restore(struct mmc_host *host) return ret; } -static int mmc_sleep(struct mmc_host *host) -{ - struct mmc_card *card = host->card; - int err = -ENOSYS; - - if (card && card->ext_csd.rev >= 3) { - err = mmc_card_sleepawake(host, 1); - if (err < 0) - pr_debug("%s: Error %d while putting card into sleep", - mmc_hostname(host), err); - } - - return err; -} - -static int mmc_awake(struct mmc_host *host) -{ - struct mmc_card *card = host->card; - int err = -ENOSYS; - - if (card && card->ext_csd.rev >= 3) { - err = mmc_card_sleepawake(host, 0); - if (err < 0) - pr_debug("%s: Error %d while awaking sleeping card", - mmc_hostname(host), err); - } - - return err; -} - static const struct mmc_bus_ops mmc_ops = { - .awake = mmc_awake, - .sleep = mmc_sleep, .remove = mmc_remove, .detect = mmc_detect, .suspend = NULL, @@ -1556,8 +1563,6 @@ static const struct mmc_bus_ops mmc_ops = { }; static const struct mmc_bus_ops mmc_ops_unsafe = { - .awake = mmc_awake, - .sleep = mmc_sleep, .remove = mmc_remove, .detect = mmc_detect, .suspend = mmc_suspend, diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 124af5238d0a..837fc7386e23 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -59,40 +59,6 @@ int mmc_deselect_cards(struct mmc_host *host) return _mmc_select_card(host, NULL); } -int mmc_card_sleepawake(struct mmc_host *host, int sleep) -{ - struct mmc_command cmd = {0}; - struct mmc_card *card = host->card; - int err; - - if (sleep) - mmc_deselect_cards(host); - - cmd.opcode = MMC_SLEEP_AWAKE; - cmd.arg = card->rca << 16; - if (sleep) - cmd.arg |= 1 << 15; - - cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; - err = mmc_wait_for_cmd(host, &cmd, 0); - if (err) - return err; - - /* - * If the host does not wait while the card signals busy, then we will - * will have to wait the sleep/awake timeout. Note, we cannot use the - * SEND_STATUS command to poll the status because that command (and most - * others) is invalid while the card sleeps. - */ - if (!(host->caps & MMC_CAP_WAIT_WHILE_BUSY)) - mmc_delay(DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000)); - - if (!sleep) - err = mmc_select_card(card); - - return err; -} - int mmc_go_idle(struct mmc_host *host) { int err; diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h index 3dd8941c2980..80ae9f4e0293 100644 --- a/drivers/mmc/core/mmc_ops.h +++ b/drivers/mmc/core/mmc_ops.h @@ -24,7 +24,6 @@ int mmc_send_status(struct mmc_card *card, u32 *status); int mmc_send_cid(struct mmc_host *host, u32 *cid); int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp); int mmc_spi_set_crc(struct mmc_host *host, int use_crc); -int mmc_card_sleepawake(struct mmc_host *host, int sleep); int mmc_bus_test(struct mmc_card *card, u8 bus_width); int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status); diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 374098bae831..2e34ee5cefce 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -425,10 +425,6 @@ static inline int mmc_regulator_get_supply(struct mmc_host *mmc) } #endif -int mmc_card_awake(struct mmc_host *host); -int mmc_card_sleep(struct mmc_host *host); -int mmc_card_can_sleep(struct mmc_host *host); - int mmc_pm_notify(struct notifier_block *notify_block, unsigned long, void *); /* Module parameter */ -- cgit v1.2.3 From 60443712195bbcbbff9af189bdd9d2c1ef0a5cae Mon Sep 17 00:00:00 2001 From: Fredrik Soderstedt Date: Tue, 23 Apr 2013 16:27:07 +0200 Subject: mmc: core: Fix select power class after resume Use the saved values in card->ext_csd when selecting power class. By doing this the power class will be selected even if mmc_init_card is called with oldcard != NULL, which is the case after a suspend/resume. Today ext_csd is NULL if mmc_init_card is called with oldcard != NULL and power class will not be selected. According to the eMMC specification the POWER_CLASS value is reset after power failure, H/W reset assertion and any CMD0 reset. Signed-off-by: Fredrik Soderstedt Reviewed-by: Johan Rudholm Acked By: Girish K S Signed-off-by: Chris Ball --- drivers/mmc/core/mmc.c | 74 ++++++++++++++++++++++++++++++++---------------- include/linux/mmc/card.h | 10 ++++++- 2 files changed, 59 insertions(+), 25 deletions(-) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index dd6810eebd3f..3a69b947130b 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -461,6 +461,24 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) */ card->ext_csd.boot_ro_lock = ext_csd[EXT_CSD_BOOT_WP]; card->ext_csd.boot_ro_lockable = true; + + /* Save power class values */ + card->ext_csd.raw_pwr_cl_52_195 = + ext_csd[EXT_CSD_PWR_CL_52_195]; + card->ext_csd.raw_pwr_cl_26_195 = + ext_csd[EXT_CSD_PWR_CL_26_195]; + card->ext_csd.raw_pwr_cl_52_360 = + ext_csd[EXT_CSD_PWR_CL_52_360]; + card->ext_csd.raw_pwr_cl_26_360 = + ext_csd[EXT_CSD_PWR_CL_26_360]; + card->ext_csd.raw_pwr_cl_200_195 = + ext_csd[EXT_CSD_PWR_CL_200_195]; + card->ext_csd.raw_pwr_cl_200_360 = + ext_csd[EXT_CSD_PWR_CL_200_360]; + card->ext_csd.raw_pwr_cl_ddr_52_195 = + ext_csd[EXT_CSD_PWR_CL_DDR_52_195]; + card->ext_csd.raw_pwr_cl_ddr_52_360 = + ext_csd[EXT_CSD_PWR_CL_DDR_52_360]; } if (card->ext_csd.rev >= 5) { @@ -607,7 +625,23 @@ static int mmc_compare_ext_csds(struct mmc_card *card, unsigned bus_width) (card->ext_csd.raw_sectors[2] == bw_ext_csd[EXT_CSD_SEC_CNT + 2]) && (card->ext_csd.raw_sectors[3] == - bw_ext_csd[EXT_CSD_SEC_CNT + 3])); + bw_ext_csd[EXT_CSD_SEC_CNT + 3]) && + (card->ext_csd.raw_pwr_cl_52_195 == + bw_ext_csd[EXT_CSD_PWR_CL_52_195]) && + (card->ext_csd.raw_pwr_cl_26_195 == + bw_ext_csd[EXT_CSD_PWR_CL_26_195]) && + (card->ext_csd.raw_pwr_cl_52_360 == + bw_ext_csd[EXT_CSD_PWR_CL_52_360]) && + (card->ext_csd.raw_pwr_cl_26_360 == + bw_ext_csd[EXT_CSD_PWR_CL_26_360]) && + (card->ext_csd.raw_pwr_cl_200_195 == + bw_ext_csd[EXT_CSD_PWR_CL_200_195]) && + (card->ext_csd.raw_pwr_cl_200_360 == + bw_ext_csd[EXT_CSD_PWR_CL_200_360]) && + (card->ext_csd.raw_pwr_cl_ddr_52_195 == + bw_ext_csd[EXT_CSD_PWR_CL_DDR_52_195]) && + (card->ext_csd.raw_pwr_cl_ddr_52_360 == + bw_ext_csd[EXT_CSD_PWR_CL_DDR_52_360])); if (err) err = -EINVAL; @@ -676,11 +710,10 @@ static struct device_type mmc_type = { * mmc_switch command. */ static int mmc_select_powerclass(struct mmc_card *card, - unsigned int bus_width, u8 *ext_csd) + unsigned int bus_width) { int err = 0; - unsigned int pwrclass_val; - unsigned int index = 0; + unsigned int pwrclass_val = 0; struct mmc_host *host; BUG_ON(!card); @@ -688,9 +721,6 @@ static int mmc_select_powerclass(struct mmc_card *card, host = card->host; BUG_ON(!host); - if (ext_csd == NULL) - return 0; - /* Power class selection is supported for versions >= 4.0 */ if (card->csd.mmca_vsn < CSD_SPEC_VER_4) return 0; @@ -702,13 +732,13 @@ static int mmc_select_powerclass(struct mmc_card *card, switch (1 << host->ios.vdd) { case MMC_VDD_165_195: if (host->ios.clock <= 26000000) - index = EXT_CSD_PWR_CL_26_195; + pwrclass_val = card->ext_csd.raw_pwr_cl_26_195; else if (host->ios.clock <= 52000000) - index = (bus_width <= EXT_CSD_BUS_WIDTH_8) ? - EXT_CSD_PWR_CL_52_195 : - EXT_CSD_PWR_CL_DDR_52_195; + pwrclass_val = (bus_width <= EXT_CSD_BUS_WIDTH_8) ? + card->ext_csd.raw_pwr_cl_52_195 : + card->ext_csd.raw_pwr_cl_ddr_52_195; else if (host->ios.clock <= 200000000) - index = EXT_CSD_PWR_CL_200_195; + pwrclass_val = card->ext_csd.raw_pwr_cl_200_195; break; case MMC_VDD_27_28: case MMC_VDD_28_29: @@ -720,13 +750,13 @@ static int mmc_select_powerclass(struct mmc_card *card, case MMC_VDD_34_35: case MMC_VDD_35_36: if (host->ios.clock <= 26000000) - index = EXT_CSD_PWR_CL_26_360; + pwrclass_val = card->ext_csd.raw_pwr_cl_26_360; else if (host->ios.clock <= 52000000) - index = (bus_width <= EXT_CSD_BUS_WIDTH_8) ? - EXT_CSD_PWR_CL_52_360 : - EXT_CSD_PWR_CL_DDR_52_360; + pwrclass_val = (bus_width <= EXT_CSD_BUS_WIDTH_8) ? + card->ext_csd.raw_pwr_cl_52_360 : + card->ext_csd.raw_pwr_cl_ddr_52_360; else if (host->ios.clock <= 200000000) - index = EXT_CSD_PWR_CL_200_360; + pwrclass_val = card->ext_csd.raw_pwr_cl_200_360; break; default: pr_warning("%s: Voltage range not supported " @@ -734,8 +764,6 @@ static int mmc_select_powerclass(struct mmc_card *card, return -EINVAL; } - pwrclass_val = ext_csd[index]; - if (bus_width & (EXT_CSD_BUS_WIDTH_8 | EXT_CSD_DDR_BUS_WIDTH_8)) pwrclass_val = (pwrclass_val & EXT_CSD_PWR_CL_8BIT_MASK) >> EXT_CSD_PWR_CL_8BIT_SHIFT; @@ -1131,7 +1159,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ? EXT_CSD_BUS_WIDTH_8 : EXT_CSD_BUS_WIDTH_4; - err = mmc_select_powerclass(card, ext_csd_bits, ext_csd); + err = mmc_select_powerclass(card, ext_csd_bits); if (err) pr_warning("%s: power class selection to bus width %d" " failed\n", mmc_hostname(card->host), @@ -1164,8 +1192,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, bus_width = bus_widths[idx]; if (bus_width == MMC_BUS_WIDTH_1) ddr = 0; /* no DDR for 1-bit width */ - err = mmc_select_powerclass(card, ext_csd_bits[idx][0], - ext_csd); + err = mmc_select_powerclass(card, ext_csd_bits[idx][0]); if (err) pr_warning("%s: power class selection to " "bus width %d failed\n", @@ -1195,8 +1222,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } if (!err && ddr) { - err = mmc_select_powerclass(card, ext_csd_bits[idx][1], - ext_csd); + err = mmc_select_powerclass(card, ext_csd_bits[idx][1]); if (err) pr_warning("%s: power class selection to " "bus width %d ddr %d failed\n", diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index f31725ba49f3..6a98f32670b8 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -94,7 +94,11 @@ struct mmc_ext_csd { u8 raw_ext_csd_structure; /* 194 */ u8 raw_card_type; /* 196 */ u8 out_of_int_time; /* 198 */ - u8 raw_s_a_timeout; /* 217 */ + u8 raw_pwr_cl_52_195; /* 200 */ + u8 raw_pwr_cl_26_195; /* 201 */ + u8 raw_pwr_cl_52_360; /* 202 */ + u8 raw_pwr_cl_26_360; /* 203 */ + u8 raw_s_a_timeout; /* 217 */ u8 raw_hc_erase_gap_size; /* 221 */ u8 raw_erase_timeout_mult; /* 223 */ u8 raw_hc_erase_grp_size; /* 224 */ @@ -102,6 +106,10 @@ struct mmc_ext_csd { u8 raw_sec_erase_mult; /* 230 */ u8 raw_sec_feature_support;/* 231 */ u8 raw_trim_mult; /* 232 */ + u8 raw_pwr_cl_200_195; /* 236 */ + u8 raw_pwr_cl_200_360; /* 237 */ + u8 raw_pwr_cl_ddr_52_195; /* 238 */ + u8 raw_pwr_cl_ddr_52_360; /* 239 */ u8 raw_bkops_status; /* 246 */ u8 raw_sectors[4]; /* 212 - 4 bytes */ -- cgit v1.2.3 From fc79a4d6dfa736672281aedbe384ece1f0044756 Mon Sep 17 00:00:00 2001 From: Joonyoung Shim Date: Fri, 26 Apr 2013 15:35:22 +0900 Subject: mmc: dw_mmc: clear IDSTS register when initialize IDMAC If pending interrupt for IDMAC exists when initialize IDMAC, it will call interrupt handler unnecessarily. Signed-off-by: Joonyoung Shim Acked-by: Seungwon Jeon Reviewed-by: Jaehoon Chung Signed-off-by: Chris Ball --- drivers/mmc/host/dw_mmc.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 0652690f2d66..b10e5e12b2ae 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -51,6 +51,11 @@ #define DW_MCI_DMA_THRESHOLD 16 #ifdef CONFIG_MMC_DW_IDMAC +#define IDMAC_INT_CLR (SDMMC_IDMAC_INT_AI | SDMMC_IDMAC_INT_NI | \ + SDMMC_IDMAC_INT_CES | SDMMC_IDMAC_INT_DU | \ + SDMMC_IDMAC_INT_FBE | SDMMC_IDMAC_INT_RI | \ + SDMMC_IDMAC_INT_TI) + struct idmac_desc { u32 des0; /* Control Descriptor */ #define IDMAC_DES0_DIC BIT(1) @@ -433,6 +438,7 @@ static int dw_mci_idmac_init(struct dw_mci *host) mci_writel(host, BMOD, SDMMC_IDMAC_SWRESET); /* Mask out interrupts - get Tx & Rx complete only */ + mci_writel(host, IDSTS, IDMAC_INT_CLR); mci_writel(host, IDINTEN, SDMMC_IDMAC_INT_NI | SDMMC_IDMAC_INT_RI | SDMMC_IDMAC_INT_TI); -- cgit v1.2.3 From 03a0675b2a112038a8a5078d8815e3f7356c7064 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 26 Apr 2013 17:47:17 +0200 Subject: mmc: sdhi/tmio: make DMA filter implementation specific So far only the SDHI implementation uses TMIO MMC with DMA. That way a DMA channel filter function, defined in the TMIO driver wasn't a problem. However, such a filter function is DMA controller specific. Since the SDHI glue is only running on systems with the SHDMA DMA controller, the filter function can safely be provided by it. Move it into SDHI. Signed-off-by: Guennadi Liakhovetski Acked-by: Samuel Ortiz Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mobile_sdhi.c | 9 +++++++++ drivers/mmc/host/tmio_mmc_dma.c | 12 ++---------- include/linux/mfd/tmio.h | 3 +++ 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index fe90853900b4..e0088d7f5f85 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -124,6 +125,13 @@ static void sh_mobile_sdhi_cd_wakeup(const struct platform_device *pdev) mmc_detect_change(dev_get_drvdata(&pdev->dev), msecs_to_jiffies(100)); } +static bool sh_mobile_sdhi_filter(struct dma_chan *chan, void *arg) +{ + dev_dbg(chan->device->dev, "%s: slave data %p\n", __func__, arg); + chan->private = arg; + return true; +} + static const struct sh_mobile_sdhi_ops sdhi_ops = { .cd_wakeup = sh_mobile_sdhi_cd_wakeup, }; @@ -191,6 +199,7 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) priv->dma_priv.chan_priv_tx = &priv->param_tx.shdma_slave; priv->dma_priv.chan_priv_rx = &priv->param_rx.shdma_slave; priv->dma_priv.alignment_shift = 1; /* 2-byte alignment */ + priv->dma_priv.filter = sh_mobile_sdhi_filter; mmc_data->dma = &priv->dma_priv; } } diff --git a/drivers/mmc/host/tmio_mmc_dma.c b/drivers/mmc/host/tmio_mmc_dma.c index fff928604859..dc4b10b9c72a 100644 --- a/drivers/mmc/host/tmio_mmc_dma.c +++ b/drivers/mmc/host/tmio_mmc_dma.c @@ -261,14 +261,6 @@ out: spin_unlock_irq(&host->lock); } -/* It might be necessary to make filter MFD specific */ -static bool tmio_mmc_filter(struct dma_chan *chan, void *arg) -{ - dev_dbg(chan->device->dev, "%s: slave data %p\n", __func__, arg); - chan->private = arg; - return true; -} - void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdata) { /* We can only either use DMA for both Tx and Rx or not use it at all */ @@ -281,7 +273,7 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); - host->chan_tx = dma_request_channel(mask, tmio_mmc_filter, + host->chan_tx = dma_request_channel(mask, pdata->dma->filter, pdata->dma->chan_priv_tx); dev_dbg(&host->pdev->dev, "%s: TX: got channel %p\n", __func__, host->chan_tx); @@ -289,7 +281,7 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat if (!host->chan_tx) return; - host->chan_rx = dma_request_channel(mask, tmio_mmc_filter, + host->chan_rx = dma_request_channel(mask, pdata->dma->filter, pdata->dma->chan_priv_rx); dev_dbg(&host->pdev->dev, "%s: RX: got channel %p\n", __func__, host->chan_rx); diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h index 99bf3e665997..0990d8a2dbd7 100644 --- a/include/linux/mfd/tmio.h +++ b/include/linux/mfd/tmio.h @@ -81,10 +81,13 @@ int tmio_core_mmc_resume(void __iomem *cnf, int shift, unsigned long base); void tmio_core_mmc_pwr(void __iomem *cnf, int shift, int state); void tmio_core_mmc_clk_div(void __iomem *cnf, int shift, int state); +struct dma_chan; + struct tmio_mmc_dma { void *chan_priv_tx; void *chan_priv_rx; int alignment_shift; + bool (*filter)(struct dma_chan *chan, void *arg); }; struct tmio_mmc_host; -- cgit v1.2.3 From eec95ee22611f2207bd991d63a07884de28e6f56 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 26 Apr 2013 17:47:18 +0200 Subject: mmc: sdhi/tmio: switch to using dmaengine_slave_config() This removes the deprecated use of the .private member of struct dma_chan and switches the sdhi / tmio mmc driver to using the dmaengine_slave_config() channel configuration method. Signed-off-by: Guennadi Liakhovetski Acked-by: Samuel Ortiz Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mobile_sdhi.c | 33 ++++++++++++++++----------------- drivers/mmc/host/tmio_mmc_dma.c | 25 +++++++++++++++++++++++++ include/linux/mfd/tmio.h | 2 ++ 3 files changed, 43 insertions(+), 17 deletions(-) diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index e0088d7f5f85..7f45f62808d4 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -20,7 +20,6 @@ #include #include -#include #include #include #include @@ -47,8 +46,6 @@ static const struct sh_mobile_sdhi_of_data sh_mobile_sdhi_of_cfg[] = { struct sh_mobile_sdhi { struct clk *clk; struct tmio_mmc_data mmc_data; - struct sh_dmae_slave param_tx; - struct sh_dmae_slave param_rx; struct tmio_mmc_dma dma_priv; }; @@ -125,13 +122,6 @@ static void sh_mobile_sdhi_cd_wakeup(const struct platform_device *pdev) mmc_detect_change(dev_get_drvdata(&pdev->dev), msecs_to_jiffies(100)); } -static bool sh_mobile_sdhi_filter(struct dma_chan *chan, void *arg) -{ - dev_dbg(chan->device->dev, "%s: slave data %p\n", __func__, arg); - chan->private = arg; - return true; -} - static const struct sh_mobile_sdhi_ops sdhi_ops = { .cd_wakeup = sh_mobile_sdhi_cd_wakeup, }; @@ -194,13 +184,22 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) mmc_data->get_cd = sh_mobile_sdhi_get_cd; if (p->dma_slave_tx > 0 && p->dma_slave_rx > 0) { - priv->param_tx.shdma_slave.slave_id = p->dma_slave_tx; - priv->param_rx.shdma_slave.slave_id = p->dma_slave_rx; - priv->dma_priv.chan_priv_tx = &priv->param_tx.shdma_slave; - priv->dma_priv.chan_priv_rx = &priv->param_rx.shdma_slave; - priv->dma_priv.alignment_shift = 1; /* 2-byte alignment */ - priv->dma_priv.filter = sh_mobile_sdhi_filter; - mmc_data->dma = &priv->dma_priv; + struct tmio_mmc_dma *dma_priv = &priv->dma_priv; + + /* + * Yes, we have to provide slave IDs twice to TMIO: + * once as a filter parameter and once for channel + * configuration as an explicit slave ID + */ + dma_priv->chan_priv_tx = (void *)p->dma_slave_tx; + dma_priv->chan_priv_rx = (void *)p->dma_slave_rx; + dma_priv->slave_id_tx = p->dma_slave_tx; + dma_priv->slave_id_rx = p->dma_slave_rx; + + dma_priv->alignment_shift = 1; /* 2-byte alignment */ + dma_priv->filter = shdma_chan_filter; + + mmc_data->dma = dma_priv; } } diff --git a/drivers/mmc/host/tmio_mmc_dma.c b/drivers/mmc/host/tmio_mmc_dma.c index dc4b10b9c72a..5fcf14f21d20 100644 --- a/drivers/mmc/host/tmio_mmc_dma.c +++ b/drivers/mmc/host/tmio_mmc_dma.c @@ -268,7 +268,14 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat return; if (!host->chan_tx && !host->chan_rx) { + struct resource *res = platform_get_resource(host->pdev, + IORESOURCE_MEM, 0); + struct dma_slave_config cfg = {}; dma_cap_mask_t mask; + int ret; + + if (!res) + return; dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); @@ -281,6 +288,14 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat if (!host->chan_tx) return; + cfg.slave_id = pdata->dma->slave_id_tx; + cfg.direction = DMA_MEM_TO_DEV; + cfg.dst_addr = res->start + (CTL_SD_DATA_PORT << host->bus_shift); + cfg.src_addr = 0; + ret = dmaengine_slave_config(host->chan_tx, &cfg); + if (ret < 0) + goto ecfgtx; + host->chan_rx = dma_request_channel(mask, pdata->dma->filter, pdata->dma->chan_priv_rx); dev_dbg(&host->pdev->dev, "%s: RX: got channel %p\n", __func__, @@ -289,6 +304,14 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat if (!host->chan_rx) goto ereqrx; + cfg.slave_id = pdata->dma->slave_id_rx; + cfg.direction = DMA_DEV_TO_MEM; + cfg.src_addr = cfg.dst_addr; + cfg.dst_addr = 0; + ret = dmaengine_slave_config(host->chan_rx, &cfg); + if (ret < 0) + goto ecfgrx; + host->bounce_buf = (u8 *)__get_free_page(GFP_KERNEL | GFP_DMA); if (!host->bounce_buf) goto ebouncebuf; @@ -302,9 +325,11 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat return; ebouncebuf: +ecfgrx: dma_release_channel(host->chan_rx); host->chan_rx = NULL; ereqrx: +ecfgtx: dma_release_channel(host->chan_tx); host->chan_tx = NULL; } diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h index 0990d8a2dbd7..ce3511326f80 100644 --- a/include/linux/mfd/tmio.h +++ b/include/linux/mfd/tmio.h @@ -86,6 +86,8 @@ struct dma_chan; struct tmio_mmc_dma { void *chan_priv_tx; void *chan_priv_rx; + int slave_id_tx; + int slave_id_rx; int alignment_shift; bool (*filter)(struct dma_chan *chan, void *arg); }; -- cgit v1.2.3 From 87ae7bbebd9c9b32ad49dde1742aa68b5a86caf8 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 26 Apr 2013 17:47:19 +0200 Subject: mmc: sdhi/tmio: add DT DMA support Add support for initialising DMA from the Device Tree. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mobile_sdhi.c | 14 +++++++------- drivers/mmc/host/tmio_mmc_dma.c | 19 ++++++++++++------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index 7f45f62808d4..cc4c872cc924 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -144,6 +144,7 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) struct tmio_mmc_host *host; int irq, ret, i = 0; bool multiplexed_isr = true; + struct tmio_mmc_dma *dma_priv; priv = devm_kzalloc(&pdev->dev, sizeof(struct sh_mobile_sdhi), GFP_KERNEL); if (priv == NULL) { @@ -152,6 +153,7 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) } mmc_data = &priv->mmc_data; + dma_priv = &priv->dma_priv; if (p) { if (p->init) { @@ -184,8 +186,6 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) mmc_data->get_cd = sh_mobile_sdhi_get_cd; if (p->dma_slave_tx > 0 && p->dma_slave_rx > 0) { - struct tmio_mmc_dma *dma_priv = &priv->dma_priv; - /* * Yes, we have to provide slave IDs twice to TMIO: * once as a filter parameter and once for channel @@ -195,14 +195,14 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) dma_priv->chan_priv_rx = (void *)p->dma_slave_rx; dma_priv->slave_id_tx = p->dma_slave_tx; dma_priv->slave_id_rx = p->dma_slave_rx; - - dma_priv->alignment_shift = 1; /* 2-byte alignment */ - dma_priv->filter = shdma_chan_filter; - - mmc_data->dma = dma_priv; } } + dma_priv->alignment_shift = 1; /* 2-byte alignment */ + dma_priv->filter = shdma_chan_filter; + + mmc_data->dma = dma_priv; + /* * All SDHI blocks support 2-byte and larger block sizes in 4-bit * bus width mode. diff --git a/drivers/mmc/host/tmio_mmc_dma.c b/drivers/mmc/host/tmio_mmc_dma.c index 5fcf14f21d20..47bdb8fa341b 100644 --- a/drivers/mmc/host/tmio_mmc_dma.c +++ b/drivers/mmc/host/tmio_mmc_dma.c @@ -264,7 +264,8 @@ out: void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdata) { /* We can only either use DMA for both Tx and Rx or not use it at all */ - if (!pdata->dma) + if (!pdata->dma || (!host->pdev->dev.of_node && + (!pdata->dma->chan_priv_tx || !pdata->dma->chan_priv_rx))) return; if (!host->chan_tx && !host->chan_rx) { @@ -280,15 +281,17 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); - host->chan_tx = dma_request_channel(mask, pdata->dma->filter, - pdata->dma->chan_priv_tx); + host->chan_tx = dma_request_slave_channel_compat(mask, + pdata->dma->filter, pdata->dma->chan_priv_tx, + &host->pdev->dev, "tx"); dev_dbg(&host->pdev->dev, "%s: TX: got channel %p\n", __func__, host->chan_tx); if (!host->chan_tx) return; - cfg.slave_id = pdata->dma->slave_id_tx; + if (pdata->dma->chan_priv_tx) + cfg.slave_id = pdata->dma->slave_id_tx; cfg.direction = DMA_MEM_TO_DEV; cfg.dst_addr = res->start + (CTL_SD_DATA_PORT << host->bus_shift); cfg.src_addr = 0; @@ -296,15 +299,17 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat if (ret < 0) goto ecfgtx; - host->chan_rx = dma_request_channel(mask, pdata->dma->filter, - pdata->dma->chan_priv_rx); + host->chan_rx = dma_request_slave_channel_compat(mask, + pdata->dma->filter, pdata->dma->chan_priv_rx, + &host->pdev->dev, "rx"); dev_dbg(&host->pdev->dev, "%s: RX: got channel %p\n", __func__, host->chan_rx); if (!host->chan_rx) goto ereqrx; - cfg.slave_id = pdata->dma->slave_id_rx; + if (pdata->dma->chan_priv_rx) + cfg.slave_id = pdata->dma->slave_id_rx; cfg.direction = DMA_DEV_TO_MEM; cfg.src_addr = cfg.dst_addr; cfg.dst_addr = 0; -- cgit v1.2.3 From 5a942b6fee81c6876044099b7622a817e4a74c03 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 29 Apr 2013 17:56:16 +0900 Subject: mmc: atmel-mci: add CONFIG_PM_SLEEP to suspend/resume functions Add CONFIG_PM_SLEEP to suspend/resume functions to fix the following build warning when CONFIG_PM_SLEEP is not selected. This is because sleep PM callbacks defined by SIMPLE_DEV_PM_OPS are only used when the CONFIG_PM_SLEEP is enabled. drivers/mmc/host/atmel-mci.c:2509:12: warning: 'atmci_suspend' defined but not used [-Wunused-function] drivers/mmc/host/atmel-mci.c:2539:12: warning: 'atmci_resume' defined but not used [-Wunused-function] Signed-off-by: Jingoo Han Acked-by: Ludovic Desroches Signed-off-by: Chris Ball --- drivers/mmc/host/atmel-mci.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index aca59d93d5a9..7d8e87ad6d6d 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -2504,7 +2504,7 @@ static int __exit atmci_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int atmci_suspend(struct device *dev) { struct atmel_mci *host = dev_get_drvdata(dev); @@ -2559,17 +2559,15 @@ static int atmci_resume(struct device *dev) return ret; } -static SIMPLE_DEV_PM_OPS(atmci_pm, atmci_suspend, atmci_resume); -#define ATMCI_PM_OPS (&atmci_pm) -#else -#define ATMCI_PM_OPS NULL #endif +static SIMPLE_DEV_PM_OPS(atmci_pm, atmci_suspend, atmci_resume); + static struct platform_driver atmci_driver = { .remove = __exit_p(atmci_remove), .driver = { .name = "atmel_mci", - .pm = ATMCI_PM_OPS, + .pm = &atmci_pm, .of_match_table = of_match_ptr(atmci_dt_ids), }, }; -- cgit v1.2.3 From ad82ab65d8abd060b60d99b8559da99c31515a31 Mon Sep 17 00:00:00 2001 From: Al Cooper Date: Sun, 26 May 2013 13:59:26 -0400 Subject: mmc: sdhci-pltfm: Allow drivers to set quirks2 from platform data Signed-off-by: Al Cooper Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-pltfm.c | 5 ++++- drivers/mmc/host/sdhci-pltfm.h | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index cd0f1f68e261..77821869e66e 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -150,8 +150,11 @@ struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev, host->ops = pdata->ops; else host->ops = &sdhci_pltfm_ops; - if (pdata) + if (pdata) { host->quirks = pdata->quirks; + host->quirks2 = pdata->quirks2; + } + host->irq = platform_get_irq(pdev, 0); if (!request_mem_region(iomem->start, resource_size(iomem), diff --git a/drivers/mmc/host/sdhci-pltfm.h b/drivers/mmc/host/sdhci-pltfm.h index 1210ed1b0c60..83d42c60a06f 100644 --- a/drivers/mmc/host/sdhci-pltfm.h +++ b/drivers/mmc/host/sdhci-pltfm.h @@ -18,6 +18,7 @@ struct sdhci_pltfm_data { const struct sdhci_ops *ops; unsigned int quirks; + unsigned int quirks2; }; struct sdhci_pltfm_host { -- cgit v1.2.3 From 113a87f868b2f2e086790a68e8b9e41d8f0c3295 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 6 May 2013 15:05:21 +0900 Subject: mmc: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Acked-by: Sonic Zhang Acked-by: Seungwon Jeon Acked-by: Shawn Guo Acked-by: Adrian Hunter Acked-by: Haojian Zhuang Acked-by: Jaehoon Chung Acked-by: Viresh Kumar Acked-by: Tony Prisk Signed-off-by: Chris Ball --- drivers/mmc/host/android-goldfish.c | 2 -- drivers/mmc/host/atmel-mci.c | 2 -- drivers/mmc/host/au1xmmc.c | 1 - drivers/mmc/host/bfin_sdh.c | 2 -- drivers/mmc/host/davinci_mmc.c | 1 - drivers/mmc/host/dw_mmc-pltfm.c | 1 - drivers/mmc/host/jz4740_mmc.c | 2 -- drivers/mmc/host/mvsdio.c | 1 - drivers/mmc/host/mxcmmc.c | 2 -- drivers/mmc/host/mxs-mmc.c | 2 -- drivers/mmc/host/omap.c | 2 -- drivers/mmc/host/omap_hsmmc.c | 2 -- drivers/mmc/host/pxamci.c | 2 -- drivers/mmc/host/rtsx_pci_sdmmc.c | 2 -- drivers/mmc/host/sdhci-acpi.c | 2 -- drivers/mmc/host/sdhci-pltfm.c | 1 - drivers/mmc/host/sdhci-pxav2.c | 2 -- drivers/mmc/host/sdhci-pxav3.c | 2 -- drivers/mmc/host/sdhci-s3c.c | 1 - drivers/mmc/host/sdhci-spear.c | 2 -- drivers/mmc/host/sh_mmcif.c | 2 -- drivers/mmc/host/tmio_mmc.c | 2 -- drivers/mmc/host/wmt-sdmmc.c | 2 -- 23 files changed, 40 deletions(-) diff --git a/drivers/mmc/host/android-goldfish.c b/drivers/mmc/host/android-goldfish.c index 7780c14704c4..8b4e20a3f16c 100644 --- a/drivers/mmc/host/android-goldfish.c +++ b/drivers/mmc/host/android-goldfish.c @@ -546,8 +546,6 @@ static int goldfish_mmc_remove(struct platform_device *pdev) { struct goldfish_mmc_host *host = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); - BUG_ON(host == NULL); mmc_remove_host(host->mmc); diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 7d8e87ad6d6d..4aa20531e5f2 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -2475,8 +2475,6 @@ static int __exit atmci_remove(struct platform_device *pdev) struct atmel_mci *host = platform_get_drvdata(pdev); unsigned int i; - platform_set_drvdata(pdev, NULL); - if (host->buffer) dma_free_coherent(&pdev->dev, host->buf_size, host->buffer, host->buf_phys_addr); diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c index 127a8fade4da..df9becdd2e99 100644 --- a/drivers/mmc/host/au1xmmc.c +++ b/drivers/mmc/host/au1xmmc.c @@ -1149,7 +1149,6 @@ static int au1xmmc_remove(struct platform_device *pdev) kfree(host->ioarea); mmc_free_host(host->mmc); - platform_set_drvdata(pdev, NULL); } return 0; } diff --git a/drivers/mmc/host/bfin_sdh.c b/drivers/mmc/host/bfin_sdh.c index fb4348c5b6ac..94fae2f1baaf 100644 --- a/drivers/mmc/host/bfin_sdh.c +++ b/drivers/mmc/host/bfin_sdh.c @@ -621,8 +621,6 @@ static int sdh_remove(struct platform_device *pdev) { struct mmc_host *mmc = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); - if (mmc) { struct sdh_host *host = mmc_priv(mmc); diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c index 3946a0eb3a03..b1b8202242ea 100644 --- a/drivers/mmc/host/davinci_mmc.c +++ b/drivers/mmc/host/davinci_mmc.c @@ -1406,7 +1406,6 @@ static int __exit davinci_mmcsd_remove(struct platform_device *pdev) { struct mmc_davinci_host *host = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); if (host) { mmc_davinci_cpufreq_deregister(host); diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c index 41c27b74b003..37873f101cdb 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.c +++ b/drivers/mmc/host/dw_mmc-pltfm.c @@ -72,7 +72,6 @@ static int dw_mci_pltfm_remove(struct platform_device *pdev) { struct dw_mci *host = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); dw_mci_remove(host); return 0; } diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c index 2391c6b7a4bb..1c47b3473ce3 100644 --- a/drivers/mmc/host/jz4740_mmc.c +++ b/drivers/mmc/host/jz4740_mmc.c @@ -932,7 +932,6 @@ err_release_mem_region: err_clk_put: clk_put(host->clk); err_free_host: - platform_set_drvdata(pdev, NULL); mmc_free_host(mmc); return ret; @@ -960,7 +959,6 @@ static int jz4740_mmc_remove(struct platform_device *pdev) clk_put(host->clk); - platform_set_drvdata(pdev, NULL); mmc_free_host(host->mmc); return 0; diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c index 8960fc846c77..d08fe6ae9bf8 100644 --- a/drivers/mmc/host/mvsdio.c +++ b/drivers/mmc/host/mvsdio.c @@ -827,7 +827,6 @@ static int __exit mvsd_remove(struct platform_device *pdev) clk_disable_unprepare(host->clk); mmc_free_host(mmc); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c index d5036353bddc..786448a10f15 100644 --- a/drivers/mmc/host/mxcmmc.c +++ b/drivers/mmc/host/mxcmmc.c @@ -1219,8 +1219,6 @@ static int mxcmci_remove(struct platform_device *pdev) struct mmc_host *mmc = platform_get_drvdata(pdev); struct mxcmci_host *host = mmc_priv(mmc); - platform_set_drvdata(pdev, NULL); - mmc_remove_host(mmc); if (host->vcc) diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index a09ba6e22136..c26280469b05 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -709,8 +709,6 @@ static int mxs_mmc_remove(struct platform_device *pdev) mmc_remove_host(mmc); - platform_set_drvdata(pdev, NULL); - if (ssp->dmach) dma_release_channel(ssp->dmach); diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index 4254975f931d..4b3e0eb77fe0 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -1500,8 +1500,6 @@ static int mmc_omap_remove(struct platform_device *pdev) struct mmc_omap_host *host = platform_get_drvdata(pdev); int i; - platform_set_drvdata(pdev, NULL); - BUG_ON(host == NULL); for (i = 0; i < host->nr_slots; i++) diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index eccedc7d06a4..1865321465c4 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -2047,7 +2047,6 @@ err_irq: } err1: iounmap(host->base); - platform_set_drvdata(pdev, NULL); mmc_free_host(mmc); err_alloc: omap_hsmmc_gpio_free(pdata); @@ -2093,7 +2092,6 @@ static int omap_hsmmc_remove(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res) release_mem_region(res->start, resource_size(res)); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index 2b2f65ada22e..847b1996ce8e 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -834,8 +834,6 @@ static int pxamci_remove(struct platform_device *pdev) struct mmc_host *mmc = platform_get_drvdata(pdev); int gpio_cd = -1, gpio_ro = -1, gpio_power = -1; - platform_set_drvdata(pdev, NULL); - if (mmc) { struct pxamci_host *host = mmc_priv(mmc); diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index ad13f4240c49..82a35b91cdbc 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -1316,8 +1316,6 @@ static int rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev) mmc_remove_host(mmc); mmc_free_host(mmc); - platform_set_drvdata(pdev, NULL); - dev_dbg(&(pdev->dev), ": Realtek PCI-E SDMMC controller has been removed\n"); diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index 706d9cb1a49e..b1a6588b50fa 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -262,7 +262,6 @@ static int sdhci_acpi_probe(struct platform_device *pdev) return 0; err_free: - platform_set_drvdata(pdev, NULL); sdhci_free_host(c->host); return err; } @@ -281,7 +280,6 @@ static int sdhci_acpi_remove(struct platform_device *pdev) dead = (sdhci_readl(c->host, SDHCI_INT_STATUS) == ~0); sdhci_remove_host(c->host, dead); - platform_set_drvdata(pdev, NULL); sdhci_free_host(c->host); return 0; diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index 77821869e66e..e7762e5e9048 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -200,7 +200,6 @@ void sdhci_pltfm_free(struct platform_device *pdev) iounmap(host->ioaddr); release_mem_region(iomem->start, resource_size(iomem)); sdhci_free_host(host); - platform_set_drvdata(pdev, NULL); } EXPORT_SYMBOL_GPL(sdhci_pltfm_free); diff --git a/drivers/mmc/host/sdhci-pxav2.c b/drivers/mmc/host/sdhci-pxav2.c index 6a3f702a38a6..98b5145cef7c 100644 --- a/drivers/mmc/host/sdhci-pxav2.c +++ b/drivers/mmc/host/sdhci-pxav2.c @@ -253,8 +253,6 @@ static int sdhci_pxav2_remove(struct platform_device *pdev) sdhci_pltfm_free(pdev); kfree(pxa); - platform_set_drvdata(pdev, NULL); - return 0; } diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index 1ae358e0662d..90ee262def69 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -339,8 +339,6 @@ static int sdhci_pxav3_remove(struct platform_device *pdev) sdhci_pltfm_free(pdev); kfree(pxa); - platform_set_drvdata(pdev, NULL); - return 0; } diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index c6f6246a4933..926aaf6acc67 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -745,7 +745,6 @@ static int sdhci_s3c_remove(struct platform_device *pdev) clk_disable_unprepare(sc->clk_io); sdhci_free_host(host); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/mmc/host/sdhci-spear.c b/drivers/mmc/host/sdhci-spear.c index 7ae5b3ae7bad..2dba9f8d1760 100644 --- a/drivers/mmc/host/sdhci-spear.c +++ b/drivers/mmc/host/sdhci-spear.c @@ -258,7 +258,6 @@ static int sdhci_probe(struct platform_device *pdev) return 0; set_drvdata: - platform_set_drvdata(pdev, NULL); sdhci_remove_host(host, 1); free_host: sdhci_free_host(host); @@ -278,7 +277,6 @@ static int sdhci_remove(struct platform_device *pdev) int dead = 0; u32 scratch; - platform_set_drvdata(pdev, NULL); scratch = readl(host->ioaddr + SDHCI_INT_STATUS); if (scratch == (u32)-1) dead = 1; diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index ba76a532ae30..117a1f774720 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -1501,8 +1501,6 @@ static int sh_mmcif_remove(struct platform_device *pdev) if (irq[1] >= 0) free_irq(irq[1], host); - platform_set_drvdata(pdev, NULL); - clk_disable(host->hclk); mmc_free_host(host->mmc); pm_runtime_put_sync(&pdev->dev); diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c index 139212e79cde..8860d4d2bc22 100644 --- a/drivers/mmc/host/tmio_mmc.c +++ b/drivers/mmc/host/tmio_mmc.c @@ -112,8 +112,6 @@ static int tmio_mmc_remove(struct platform_device *pdev) const struct mfd_cell *cell = mfd_get_cell(pdev); struct mmc_host *mmc = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); - if (mmc) { struct tmio_mmc_host *host = mmc_priv(mmc); free_irq(platform_get_irq(pdev, 0), host); diff --git a/drivers/mmc/host/wmt-sdmmc.c b/drivers/mmc/host/wmt-sdmmc.c index 442f5766ffca..34231d5168fc 100644 --- a/drivers/mmc/host/wmt-sdmmc.c +++ b/drivers/mmc/host/wmt-sdmmc.c @@ -927,8 +927,6 @@ static int wmt_mci_remove(struct platform_device *pdev) mmc_free_host(mmc); - platform_set_drvdata(pdev, NULL); - dev_info(&pdev->dev, "WMT MCI device removed\n"); return 0; -- cgit v1.2.3 From f0710a557cb17746b09234f01073a2cdafe4f4a5 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 6 May 2013 12:17:32 +0300 Subject: mmc: sdhci: add ability to stay runtime-resumed if the card is powered up If card power is dependent on SD bus power then the host controller must not be runtime suspended while the card is powered up. Add the ability to stay runtime-resumed in that case and enable it with a new quirk SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON. Signed-off-by: Adrian Hunter Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 35 ++++++++++++++++++++++++++++++++++- include/linux/mmc/sdhci.h | 2 ++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 2ea429c27714..c81c2a289dbd 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -58,6 +58,8 @@ static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable); #ifdef CONFIG_PM_RUNTIME static int sdhci_runtime_pm_get(struct sdhci_host *host); static int sdhci_runtime_pm_put(struct sdhci_host *host); +static void sdhci_runtime_pm_bus_on(struct sdhci_host *host); +static void sdhci_runtime_pm_bus_off(struct sdhci_host *host); #else static inline int sdhci_runtime_pm_get(struct sdhci_host *host) { @@ -67,6 +69,12 @@ static inline int sdhci_runtime_pm_put(struct sdhci_host *host) { return 0; } +static void sdhci_runtime_pm_bus_on(struct sdhci_host *host) +{ +} +static void sdhci_runtime_pm_bus_off(struct sdhci_host *host) +{ +} #endif static void sdhci_dumpregs(struct sdhci_host *host) @@ -192,8 +200,12 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask) sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET); - if (mask & SDHCI_RESET_ALL) + if (mask & SDHCI_RESET_ALL) { host->clock = 0; + /* Reset-all turns off SD Bus Power */ + if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON) + sdhci_runtime_pm_bus_off(host); + } /* Wait max 100 ms */ timeout = 100; @@ -1268,6 +1280,8 @@ static int sdhci_set_power(struct sdhci_host *host, unsigned short power) if (pwr == 0) { sdhci_writeb(host, 0, SDHCI_POWER_CONTROL); + if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON) + sdhci_runtime_pm_bus_off(host); return 0; } @@ -1289,6 +1303,9 @@ static int sdhci_set_power(struct sdhci_host *host, unsigned short power) sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); + if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON) + sdhci_runtime_pm_bus_on(host); + /* * Some controllers need an extra 10ms delay of 10ms before they * can apply clock after applying power @@ -2625,6 +2642,22 @@ static int sdhci_runtime_pm_put(struct sdhci_host *host) return pm_runtime_put_autosuspend(host->mmc->parent); } +static void sdhci_runtime_pm_bus_on(struct sdhci_host *host) +{ + if (host->runtime_suspended || host->bus_on) + return; + host->bus_on = true; + pm_runtime_get_noresume(host->mmc->parent); +} + +static void sdhci_runtime_pm_bus_off(struct sdhci_host *host) +{ + if (host->runtime_suspended || !host->bus_on) + return; + host->bus_on = false; + pm_runtime_put_noidle(host->mmc->parent); +} + int sdhci_runtime_suspend_host(struct sdhci_host *host) { unsigned long flags; diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h index b838ffc49e4a..ba35bdb87d99 100644 --- a/include/linux/mmc/sdhci.h +++ b/include/linux/mmc/sdhci.h @@ -95,6 +95,7 @@ struct sdhci_host { /* The system physically doesn't support 1.8v, even if the host does */ #define SDHCI_QUIRK2_NO_1_8_V (1<<2) #define SDHCI_QUIRK2_PRESET_VALUE_BROKEN (1<<3) +#define SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON (1<<4) int irq; /* Device IRQ */ void __iomem *ioaddr; /* Mapped address */ @@ -139,6 +140,7 @@ struct sdhci_host { u8 pwr; /* Current voltage */ bool runtime_suspended; /* Host is runtime suspended */ + bool bus_on; /* Bus power prevents runtime suspend */ struct mmc_request *mrq; /* Current request */ struct mmc_command *cmd; /* Current command */ -- cgit v1.2.3 From a61abe6eebfda1add8cb54e6e10384ea747d68a5 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 6 May 2013 12:17:33 +0300 Subject: mmc: sdhci-acpi: support runtime PM for ACPI HID 80860F14 SD cards Enable runtime PM for ACPI HID 80860F14 SD cards, adding support for card detect GPIO. Signed-off-by: Adrian Hunter Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-acpi.c | 64 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index b1a6588b50fa..a51e603acbc5 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -31,8 +31,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -101,6 +103,8 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = { }; static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sd = { + .flags = SDHCI_ACPI_SD_CD | SDHCI_ACPI_RUNTIME_PM, + .quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON, }; struct sdhci_acpi_uid_slot { @@ -161,6 +165,57 @@ static const struct sdhci_acpi_slot *sdhci_acpi_get_slot(acpi_handle handle, return slot; } +#ifdef CONFIG_PM_RUNTIME + +static irqreturn_t sdhci_acpi_sd_cd(int irq, void *dev_id) +{ + mmc_detect_change(dev_id, msecs_to_jiffies(200)); + return IRQ_HANDLED; +} + +static int sdhci_acpi_add_own_cd(struct device *dev, int gpio, + struct mmc_host *mmc) +{ + unsigned long flags; + int err, irq; + + if (gpio < 0) { + err = gpio; + goto out; + } + + err = devm_gpio_request_one(dev, gpio, GPIOF_DIR_IN, "sd_cd"); + if (err) + goto out; + + irq = gpio_to_irq(gpio); + if (irq < 0) + goto out_free; + + flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; + err = devm_request_irq(dev, irq, sdhci_acpi_sd_cd, flags, "sd_cd", mmc); + if (err) + goto out_free; + + return 0; + +out_free: + devm_gpio_free(dev, gpio); +out: + dev_warn(dev, "failed to setup card detect wake up\n"); + return err; +} + +#else + +static int sdhci_acpi_add_own_cd(struct device *dev, int gpio, + struct mmc_host *mmc) +{ + return 0; +} + +#endif + static int sdhci_acpi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -171,7 +226,7 @@ static int sdhci_acpi_probe(struct platform_device *pdev) struct resource *iomem; resource_size_t len; const char *hid; - int err; + int err, gpio; if (acpi_bus_get_device(handle, &device)) return -ENODEV; @@ -196,6 +251,8 @@ static int sdhci_acpi_probe(struct platform_device *pdev) if (IS_ERR(host)) return PTR_ERR(host); + gpio = acpi_get_gpio_by_index(dev, 0, NULL); + c = sdhci_priv(host); c->host = host; c->slot = sdhci_acpi_get_slot(handle, hid); @@ -251,6 +308,11 @@ static int sdhci_acpi_probe(struct platform_device *pdev) if (err) goto err_free; + if (sdhci_acpi_flag(c, SDHCI_ACPI_SD_CD)) { + if (sdhci_acpi_add_own_cd(dev, gpio, host->mmc)) + c->use_runtime_pm = false; + } + if (c->use_runtime_pm) { pm_runtime_set_active(dev); pm_suspend_ignore_children(dev, 1); -- cgit v1.2.3 From 7396e318b497cd46eb156effa5278126582ddde7 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 6 May 2013 12:17:34 +0300 Subject: mmc: sdhci-pci: support runtime PM for BYT SD cards Add support for runtime PM for BYT SD cards. Signed-off-by: Adrian Hunter Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-pci.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 701d06d0e1fb..611331a39fe5 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -332,6 +332,8 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = { }; static const struct sdhci_pci_fixes sdhci_intel_byt_sd = { + .quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON, + .allow_runtime_pm = true, }; /* O2Micro extra registers */ -- cgit v1.2.3 From 9d5242b19269432ea388d766312ed49f184f83fd Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sat, 25 May 2013 01:46:10 +0000 Subject: netfilter: nfnetlink_queue: avoid peer_portid test The portid is set to NETLINK_CB(skb).portid at create time. The run-time check will always be false. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nfnetlink_queue_core.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/net/netfilter/nfnetlink_queue_core.c b/net/netfilter/nfnetlink_queue_core.c index 2e0e835baf72..cff4449f01d2 100644 --- a/net/netfilter/nfnetlink_queue_core.c +++ b/net/netfilter/nfnetlink_queue_core.c @@ -509,10 +509,6 @@ __nfqnl_enqueue_packet(struct net *net, struct nfqnl_instance *queue, } spin_lock_bh(&queue->lock); - if (!queue->peer_portid) { - err = -EINVAL; - goto err_out_free_nskb; - } if (queue->queue_total >= queue->queue_maxlen) { if (queue->flags & NFQA_CFG_F_FAIL_OPEN) { failopen = 1; -- cgit v1.2.3 From 8ade62857ef77bdf639185410fbcd811aa700cb2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 17 May 2013 12:01:26 +0200 Subject: iwlwifi: move D3_CFG_COMPLETE handling into DVM The MVM firmware doesn't communicate this way, it instead assumes D3 configuration is complete after a specific host command (which must be last) has been sent. Handling this bit thus belongs into the firmware API code, i.e. DVM. Signed-off-by: Johannes Berg Reviewed-by: Emmanuel Grumbach --- drivers/net/wireless/iwlwifi/dvm/mac80211.c | 8 ++++++++ drivers/net/wireless/iwlwifi/pcie/trans.c | 7 ------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index cab23af0be9e..700215324af8 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -426,6 +426,10 @@ static int iwlagn_mac_suspend(struct ieee80211_hw *hw, if (ret) goto error; + /* let the ucode operate on its own */ + iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_SET, + CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE); + iwl_trans_d3_suspend(priv->trans); goto out; @@ -509,6 +513,10 @@ static int iwlagn_mac_resume(struct ieee80211_hw *hw) goto out_unlock; } + /* uCode is no longer operating by itself */ + iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR, + CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE); + base = priv->device_pointers.error_event_table; if (!iwlagn_hw_valid_rtc_data_addr(base)) { IWL_WARN(priv, "Invalid error table during resume!\n"); diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index a785f49df9b1..0b021305eedf 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -580,10 +580,6 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans) { - /* let the ucode operate on its own */ - iwl_write32(trans, CSR_UCODE_DRV_GP1_SET, - CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE); - iwl_disable_interrupts(trans); iwl_pcie_disable_ict(trans); @@ -643,9 +639,6 @@ static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans, return ret; } - iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, - CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE); - *status = IWL_D3_STATUS_ALIVE; return 0; } -- cgit v1.2.3 From ee4d5471333c323693f79cdb7b145b40e12baa77 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 17 May 2013 09:58:27 +0200 Subject: iwlwifi: dvm: rename iwl_lib_ops to iwl_dvm_cfg The next patches will move some more configuration data that isn't needed by mvm into this struct, so rename it now since it won't just be ops. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/agn.h | 14 +++++++------- drivers/net/wireless/iwlwifi/dvm/dev.h | 4 ++-- drivers/net/wireless/iwlwifi/dvm/devices.c | 14 +++++++------- drivers/net/wireless/iwlwifi/dvm/main.c | 14 +++++++------- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/dvm/agn.h b/drivers/net/wireless/iwlwifi/dvm/agn.h index 48545ab00311..ff47fce6c58e 100644 --- a/drivers/net/wireless/iwlwifi/dvm/agn.h +++ b/drivers/net/wireless/iwlwifi/dvm/agn.h @@ -76,13 +76,13 @@ #define IWL_INVALID_STATION 255 /* device operations */ -extern struct iwl_lib_ops iwl1000_lib; -extern struct iwl_lib_ops iwl2000_lib; -extern struct iwl_lib_ops iwl2030_lib; -extern struct iwl_lib_ops iwl5000_lib; -extern struct iwl_lib_ops iwl5150_lib; -extern struct iwl_lib_ops iwl6000_lib; -extern struct iwl_lib_ops iwl6030_lib; +extern struct iwl_dvm_cfg iwl_dvm_1000_cfg; +extern struct iwl_dvm_cfg iwl_dvm_2000_cfg; +extern struct iwl_dvm_cfg iwl_dvm_2030_cfg; +extern struct iwl_dvm_cfg iwl_dvm_5000_cfg; +extern struct iwl_dvm_cfg iwl_dvm_5150_cfg; +extern struct iwl_dvm_cfg iwl_dvm_6000_cfg; +extern struct iwl_dvm_cfg iwl_dvm_6030_cfg; #define TIME_UNIT 1024 diff --git a/drivers/net/wireless/iwlwifi/dvm/dev.h b/drivers/net/wireless/iwlwifi/dvm/dev.h index 71ea77576d22..beb525cb907a 100644 --- a/drivers/net/wireless/iwlwifi/dvm/dev.h +++ b/drivers/net/wireless/iwlwifi/dvm/dev.h @@ -568,7 +568,7 @@ struct iwl_hw_params { const struct iwl_sensitivity_ranges *sens; }; -struct iwl_lib_ops { +struct iwl_dvm_cfg { /* set hw dependent parameters */ void (*set_hw_params)(struct iwl_priv *priv); int (*set_channel_switch)(struct iwl_priv *priv, @@ -610,7 +610,7 @@ struct iwl_priv { struct device *dev; /* for debug prints only */ const struct iwl_cfg *cfg; const struct iwl_fw *fw; - const struct iwl_lib_ops *lib; + const struct iwl_dvm_cfg *lib; unsigned long status; spinlock_t sta_lock; diff --git a/drivers/net/wireless/iwlwifi/dvm/devices.c b/drivers/net/wireless/iwlwifi/dvm/devices.c index c48907c8ab43..5878bbbfc3dd 100644 --- a/drivers/net/wireless/iwlwifi/dvm/devices.c +++ b/drivers/net/wireless/iwlwifi/dvm/devices.c @@ -174,7 +174,7 @@ static void iwl1000_hw_set_hw_params(struct iwl_priv *priv) priv->hw_params.sens = &iwl1000_sensitivity; } -struct iwl_lib_ops iwl1000_lib = { +struct iwl_dvm_cfg iwl_dvm_1000_cfg = { .set_hw_params = iwl1000_hw_set_hw_params, .nic_config = iwl1000_nic_config, .temperature = iwlagn_temperature, @@ -232,13 +232,13 @@ static void iwl2000_hw_set_hw_params(struct iwl_priv *priv) priv->hw_params.sens = &iwl2000_sensitivity; } -struct iwl_lib_ops iwl2000_lib = { +struct iwl_dvm_cfg iwl_dvm_2000_cfg = { .set_hw_params = iwl2000_hw_set_hw_params, .nic_config = iwl2000_nic_config, .temperature = iwlagn_temperature, }; -struct iwl_lib_ops iwl2030_lib = { +struct iwl_dvm_cfg iwl_dvm_2030_cfg = { .set_hw_params = iwl2000_hw_set_hw_params, .nic_config = iwl2000_nic_config, .temperature = iwlagn_temperature, @@ -420,13 +420,13 @@ static int iwl5000_hw_channel_switch(struct iwl_priv *priv, return iwl_dvm_send_cmd(priv, &hcmd); } -struct iwl_lib_ops iwl5000_lib = { +struct iwl_dvm_cfg iwl_dvm_5000_cfg = { .set_hw_params = iwl5000_hw_set_hw_params, .set_channel_switch = iwl5000_hw_channel_switch, .temperature = iwlagn_temperature, }; -struct iwl_lib_ops iwl5150_lib = { +struct iwl_dvm_cfg iwl_dvm_5150_cfg = { .set_hw_params = iwl5150_hw_set_hw_params, .set_channel_switch = iwl5000_hw_channel_switch, .temperature = iwl5150_temperature, @@ -584,14 +584,14 @@ static int iwl6000_hw_channel_switch(struct iwl_priv *priv, return err; } -struct iwl_lib_ops iwl6000_lib = { +struct iwl_dvm_cfg iwl_dvm_6000_cfg = { .set_hw_params = iwl6000_hw_set_hw_params, .set_channel_switch = iwl6000_hw_channel_switch, .nic_config = iwl6000_nic_config, .temperature = iwlagn_temperature, }; -struct iwl_lib_ops iwl6030_lib = { +struct iwl_dvm_cfg iwl_dvm_6030_cfg = { .set_hw_params = iwl6000_hw_set_hw_params, .set_channel_switch = iwl6000_hw_channel_switch, .nic_config = iwl6000_nic_config, diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c index 74d7572e7091..0c77222c2c64 100644 --- a/drivers/net/wireless/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/iwlwifi/dvm/main.c @@ -1264,31 +1264,31 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, switch (priv->cfg->device_family) { case IWL_DEVICE_FAMILY_1000: case IWL_DEVICE_FAMILY_100: - priv->lib = &iwl1000_lib; + priv->lib = &iwl_dvm_1000_cfg; break; case IWL_DEVICE_FAMILY_2000: case IWL_DEVICE_FAMILY_105: - priv->lib = &iwl2000_lib; + priv->lib = &iwl_dvm_2000_cfg; break; case IWL_DEVICE_FAMILY_2030: case IWL_DEVICE_FAMILY_135: - priv->lib = &iwl2030_lib; + priv->lib = &iwl_dvm_2030_cfg; break; case IWL_DEVICE_FAMILY_5000: - priv->lib = &iwl5000_lib; + priv->lib = &iwl_dvm_5000_cfg; break; case IWL_DEVICE_FAMILY_5150: - priv->lib = &iwl5150_lib; + priv->lib = &iwl_dvm_5150_cfg; break; case IWL_DEVICE_FAMILY_6000: case IWL_DEVICE_FAMILY_6005: case IWL_DEVICE_FAMILY_6000i: case IWL_DEVICE_FAMILY_6050: case IWL_DEVICE_FAMILY_6150: - priv->lib = &iwl6000_lib; + priv->lib = &iwl_dvm_6000_cfg; break; case IWL_DEVICE_FAMILY_6030: - priv->lib = &iwl6030_lib; + priv->lib = &iwl_dvm_6030_cfg; break; default: break; -- cgit v1.2.3 From 5d69030db5c93d0521174ef0d3d5d3bceeb04e42 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 14 May 2013 19:08:50 +0530 Subject: cpufreq: tegra: Don't initialize .index field of cpufreq_frequency_table The Tegra cpufreq driver doesn't use .index field of cpufreq_frequency_table and so we don't need to initialize it. Don't initialize it. Signed-off-by: Viresh Kumar Acked-by: Stephen Warren Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/tegra-cpufreq.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/drivers/cpufreq/tegra-cpufreq.c b/drivers/cpufreq/tegra-cpufreq.c index c74c0e130ef4..81561be17e8c 100644 --- a/drivers/cpufreq/tegra-cpufreq.c +++ b/drivers/cpufreq/tegra-cpufreq.c @@ -28,17 +28,16 @@ #include #include -/* Frequency table index must be sequential starting at 0 */ static struct cpufreq_frequency_table freq_table[] = { - { 0, 216000 }, - { 1, 312000 }, - { 2, 456000 }, - { 3, 608000 }, - { 4, 760000 }, - { 5, 816000 }, - { 6, 912000 }, - { 7, 1000000 }, - { 8, CPUFREQ_TABLE_END }, + { .frequency = 216000 }, + { .frequency = 312000 }, + { .frequency = 456000 }, + { .frequency = 608000 }, + { .frequency = 760000 }, + { .frequency = 816000 }, + { .frequency = 912000 }, + { .frequency = 1000000 }, + { .frequency = CPUFREQ_TABLE_END }, }; #define NUM_CPUS 2 -- cgit v1.2.3 From 0d8877a10d65f3c9bb84ad150e524d95ebd377fb Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 17 May 2013 10:36:29 +0200 Subject: iwlwifi: move some configuration parameters into DVM There are a number of parameters that aren't really hardware specific but rather define how the DVM firmware is used. Move these into the DVM configuration. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/agn.h | 7 ++- drivers/net/wireless/iwlwifi/dvm/calib.c | 8 +-- drivers/net/wireless/iwlwifi/dvm/dev.h | 53 ++++++++++++++-- drivers/net/wireless/iwlwifi/dvm/devices.c | 93 +++++++++++++++++++++++++++++ drivers/net/wireless/iwlwifi/dvm/lib.c | 20 +++---- drivers/net/wireless/iwlwifi/dvm/mac80211.c | 6 +- drivers/net/wireless/iwlwifi/dvm/main.c | 35 ++++++----- drivers/net/wireless/iwlwifi/dvm/power.c | 6 +- drivers/net/wireless/iwlwifi/dvm/rs.c | 8 +-- drivers/net/wireless/iwlwifi/dvm/rx.c | 2 +- drivers/net/wireless/iwlwifi/dvm/scan.c | 12 ++-- drivers/net/wireless/iwlwifi/dvm/tt.c | 2 +- drivers/net/wireless/iwlwifi/dvm/tx.c | 12 ++-- drivers/net/wireless/iwlwifi/dvm/ucode.c | 10 ++-- drivers/net/wireless/iwlwifi/iwl-1000.c | 3 - drivers/net/wireless/iwlwifi/iwl-2000.c | 39 +----------- drivers/net/wireless/iwlwifi/iwl-5000.c | 4 -- drivers/net/wireless/iwlwifi/iwl-6000.c | 32 +--------- drivers/net/wireless/iwlwifi/iwl-7000.c | 9 +-- drivers/net/wireless/iwlwifi/iwl-config.h | 43 +------------ 20 files changed, 217 insertions(+), 187 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/dvm/agn.h b/drivers/net/wireless/iwlwifi/dvm/agn.h index ff47fce6c58e..df64a546f52d 100644 --- a/drivers/net/wireless/iwlwifi/dvm/agn.h +++ b/drivers/net/wireless/iwlwifi/dvm/agn.h @@ -78,10 +78,13 @@ /* device operations */ extern struct iwl_dvm_cfg iwl_dvm_1000_cfg; extern struct iwl_dvm_cfg iwl_dvm_2000_cfg; +extern struct iwl_dvm_cfg iwl_dvm_105_cfg; extern struct iwl_dvm_cfg iwl_dvm_2030_cfg; extern struct iwl_dvm_cfg iwl_dvm_5000_cfg; extern struct iwl_dvm_cfg iwl_dvm_5150_cfg; extern struct iwl_dvm_cfg iwl_dvm_6000_cfg; +extern struct iwl_dvm_cfg iwl_dvm_6005_cfg; +extern struct iwl_dvm_cfg iwl_dvm_6050_cfg; extern struct iwl_dvm_cfg iwl_dvm_6030_cfg; @@ -291,8 +294,8 @@ void iwlagn_bt_adjust_rssi_monitor(struct iwl_priv *priv, bool rssi_ena); static inline bool iwl_advanced_bt_coexist(struct iwl_priv *priv) { - return priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist; + return priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist; } #ifdef CONFIG_IWLWIFI_DEBUG diff --git a/drivers/net/wireless/iwlwifi/dvm/calib.c b/drivers/net/wireless/iwlwifi/dvm/calib.c index d6c4cf2ad7c5..1b0f0d502568 100644 --- a/drivers/net/wireless/iwlwifi/dvm/calib.c +++ b/drivers/net/wireless/iwlwifi/dvm/calib.c @@ -521,7 +521,7 @@ static int iwl_enhance_sensitivity_write(struct iwl_priv *priv) iwl_prepare_legacy_sensitivity_tbl(priv, data, &cmd.enhance_table[0]); - if (priv->cfg->base_params->hd_v2) { + if (priv->lib->hd_v2) { cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX] = HD_INA_NON_SQUARE_DET_OFDM_DATA_V2; cmd.enhance_table[HD_INA_NON_SQUARE_DET_CCK_INDEX] = @@ -895,7 +895,7 @@ static void iwlagn_gain_computation(struct iwl_priv *priv, continue; } - delta_g = (priv->cfg->base_params->chain_noise_scale * + delta_g = (priv->lib->chain_noise_scale * ((s32)average_noise[default_chain] - (s32)average_noise[i])) / 1500; @@ -1051,8 +1051,8 @@ void iwl_chain_noise_calibration(struct iwl_priv *priv) return; /* Analyze signal for disconnected antenna */ - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) { + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist) { /* Disable disconnected antenna algorithm for advanced bt coex, assuming valid antennas are connected */ data->active_chains = priv->nvm_data->valid_rx_ant; diff --git a/drivers/net/wireless/iwlwifi/dvm/dev.h b/drivers/net/wireless/iwlwifi/dvm/dev.h index beb525cb907a..f1b8df16dbba 100644 --- a/drivers/net/wireless/iwlwifi/dvm/dev.h +++ b/drivers/net/wireless/iwlwifi/dvm/dev.h @@ -568,16 +568,61 @@ struct iwl_hw_params { const struct iwl_sensitivity_ranges *sens; }; +/** + * struct iwl_dvm_bt_params - DVM specific BT (coex) parameters + * @advanced_bt_coexist: support advanced bt coexist + * @bt_init_traffic_load: specify initial bt traffic load + * @bt_prio_boost: default bt priority boost value + * @agg_time_limit: maximum number of uSec in aggregation + * @bt_sco_disable: uCode should not response to BT in SCO/ESCO mode + */ +struct iwl_dvm_bt_params { + bool advanced_bt_coexist; + u8 bt_init_traffic_load; + u32 bt_prio_boost; + u16 agg_time_limit; + bool bt_sco_disable; + bool bt_session_2; +}; + +/** + * struct iwl_dvm_cfg - DVM firmware specific device configuration + * @set_hw_params: set hardware parameters + * @set_channel_switch: send channel switch command + * @nic_config: apply device specific configuration + * @temperature: read temperature + * @adv_thermal_throttle: support advance thermal throttle + * @support_ct_kill_exit: support ct kill exit condition + * @plcp_delta_threshold: plcp error rate threshold used to trigger + * radio tuning when there is a high receiving plcp error rate + * @chain_noise_scale: default chain noise scale used for gain computation + * @hd_v2: v2 of enhanced sensitivity value, used for 2000 series and up + * @no_idle_support: do not support idle mode + * @bt_params: pointer to BT parameters + * @need_temp_offset_calib: need to perform temperature offset calibration + * @no_xtal_calib: some devices do not need crystal calibration data, + * don't send it to those + * @temp_offset_v2: support v2 of temperature offset calibration + * @adv_pm: advanced power management + */ struct iwl_dvm_cfg { - /* set hw dependent parameters */ void (*set_hw_params)(struct iwl_priv *priv); int (*set_channel_switch)(struct iwl_priv *priv, struct ieee80211_channel_switch *ch_switch); - /* device specific configuration */ void (*nic_config)(struct iwl_priv *priv); - - /* temperature */ void (*temperature)(struct iwl_priv *priv); + + const struct iwl_dvm_bt_params *bt_params; + s32 chain_noise_scale; + u8 plcp_delta_threshold; + bool adv_thermal_throttle; + bool support_ct_kill_exit; + bool hd_v2; + bool no_idle_support; + bool need_temp_offset_calib; + bool no_xtal_calib; + bool temp_offset_v2; + bool adv_pm; }; struct iwl_wipan_noa_data { diff --git a/drivers/net/wireless/iwlwifi/dvm/devices.c b/drivers/net/wireless/iwlwifi/dvm/devices.c index 5878bbbfc3dd..5a1c2186ea34 100644 --- a/drivers/net/wireless/iwlwifi/dvm/devices.c +++ b/drivers/net/wireless/iwlwifi/dvm/devices.c @@ -178,6 +178,9 @@ struct iwl_dvm_cfg iwl_dvm_1000_cfg = { .set_hw_params = iwl1000_hw_set_hw_params, .nic_config = iwl1000_nic_config, .temperature = iwlagn_temperature, + .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF, + .chain_noise_scale = 1000, }; @@ -236,12 +239,52 @@ struct iwl_dvm_cfg iwl_dvm_2000_cfg = { .set_hw_params = iwl2000_hw_set_hw_params, .nic_config = iwl2000_nic_config, .temperature = iwlagn_temperature, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1000, + .hd_v2 = true, + .need_temp_offset_calib = true, + .temp_offset_v2 = true, +}; + +struct iwl_dvm_cfg iwl_dvm_105_cfg = { + .set_hw_params = iwl2000_hw_set_hw_params, + .nic_config = iwl2000_nic_config, + .temperature = iwlagn_temperature, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1000, + .hd_v2 = true, + .need_temp_offset_calib = true, + .temp_offset_v2 = true, + .adv_pm = true, +}; + +static const struct iwl_dvm_bt_params iwl2030_bt_params = { + /* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */ + .advanced_bt_coexist = true, + .agg_time_limit = BT_AGG_THRESHOLD_DEF, + .bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE, + .bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT32, + .bt_sco_disable = true, + .bt_session_2 = true, }; struct iwl_dvm_cfg iwl_dvm_2030_cfg = { .set_hw_params = iwl2000_hw_set_hw_params, .nic_config = iwl2000_nic_config, .temperature = iwlagn_temperature, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1000, + .hd_v2 = true, + .bt_params = &iwl2030_bt_params, + .need_temp_offset_calib = true, + .temp_offset_v2 = true, + .adv_pm = true, }; /* @@ -424,12 +467,19 @@ struct iwl_dvm_cfg iwl_dvm_5000_cfg = { .set_hw_params = iwl5000_hw_set_hw_params, .set_channel_switch = iwl5000_hw_channel_switch, .temperature = iwlagn_temperature, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, + .chain_noise_scale = 1000, + .no_idle_support = true, }; struct iwl_dvm_cfg iwl_dvm_5150_cfg = { .set_hw_params = iwl5150_hw_set_hw_params, .set_channel_switch = iwl5000_hw_channel_switch, .temperature = iwl5150_temperature, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, + .chain_noise_scale = 1000, + .no_idle_support = true, + .no_xtal_calib = true, }; @@ -589,6 +639,42 @@ struct iwl_dvm_cfg iwl_dvm_6000_cfg = { .set_channel_switch = iwl6000_hw_channel_switch, .nic_config = iwl6000_nic_config, .temperature = iwlagn_temperature, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1000, +}; + +const struct iwl_dvm_cfg iwl_dvm_6005_cfg = { + .set_hw_params = iwl6000_hw_set_hw_params, + .set_channel_switch = iwl6000_hw_channel_switch, + .nic_config = iwl6000_nic_config, + .temperature = iwlagn_temperature, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1000, + .need_temp_offset_calib = true, +}; + +struct iwl_dvm_cfg iwl_dvm_6050_cfg = { + .set_hw_params = iwl6000_hw_set_hw_params, + .set_channel_switch = iwl6000_hw_channel_switch, + .nic_config = iwl6000_nic_config, + .temperature = iwlagn_temperature, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1500, +}; + +static const struct iwl_dvm_bt_params iwl6000_bt_params = { + /* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */ + .advanced_bt_coexist = true, + .agg_time_limit = BT_AGG_THRESHOLD_DEF, + .bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE, + .bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT, + .bt_sco_disable = true, }; struct iwl_dvm_cfg iwl_dvm_6030_cfg = { @@ -596,4 +682,11 @@ struct iwl_dvm_cfg iwl_dvm_6030_cfg = { .set_channel_switch = iwl6000_hw_channel_switch, .nic_config = iwl6000_nic_config, .temperature = iwlagn_temperature, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1000, + .bt_params = &iwl6000_bt_params, + .need_temp_offset_calib = true, + .adv_pm = true, }; diff --git a/drivers/net/wireless/iwlwifi/dvm/lib.c b/drivers/net/wireless/iwlwifi/dvm/lib.c index 54f553380aa8..9879550a0fea 100644 --- a/drivers/net/wireless/iwlwifi/dvm/lib.c +++ b/drivers/net/wireless/iwlwifi/dvm/lib.c @@ -254,23 +254,23 @@ void iwlagn_send_advance_bt_config(struct iwl_priv *priv) BUILD_BUG_ON(sizeof(iwlagn_def_3w_lookup) != sizeof(basic.bt3_lookup_table)); - if (priv->cfg->bt_params) { + if (priv->lib->bt_params) { /* * newer generation of devices (2000 series and newer) * use the version 2 of the bt command * we need to make sure sending the host command * with correct data structure to avoid uCode assert */ - if (priv->cfg->bt_params->bt_session_2) { + if (priv->lib->bt_params->bt_session_2) { bt_cmd_v2.prio_boost = cpu_to_le32( - priv->cfg->bt_params->bt_prio_boost); + priv->lib->bt_params->bt_prio_boost); bt_cmd_v2.tx_prio_boost = 0; bt_cmd_v2.rx_prio_boost = 0; } else { /* older version only has 8 bits */ - WARN_ON(priv->cfg->bt_params->bt_prio_boost & ~0xFF); + WARN_ON(priv->lib->bt_params->bt_prio_boost & ~0xFF); bt_cmd_v1.prio_boost = - priv->cfg->bt_params->bt_prio_boost; + priv->lib->bt_params->bt_prio_boost; bt_cmd_v1.tx_prio_boost = 0; bt_cmd_v1.rx_prio_boost = 0; } @@ -330,7 +330,7 @@ void iwlagn_send_advance_bt_config(struct iwl_priv *priv) priv->bt_full_concurrent ? "full concurrency" : "3-wire"); - if (priv->cfg->bt_params->bt_session_2) { + if (priv->lib->bt_params->bt_session_2) { memcpy(&bt_cmd_v2.basic, &basic, sizeof(basic)); ret = iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG, @@ -758,8 +758,8 @@ static bool is_single_rx_stream(struct iwl_priv *priv) */ static int iwl_get_active_rx_chain_count(struct iwl_priv *priv) { - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist && + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist && (priv->bt_full_concurrent || priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) { /* @@ -830,8 +830,8 @@ void iwlagn_set_rxon_chain(struct iwl_priv *priv, struct iwl_rxon_context *ctx) else active_chains = priv->nvm_data->valid_rx_ant; - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist && + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist && (priv->bt_full_concurrent || priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) { /* diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index 700215324af8..ac7ed3f809a0 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -1284,8 +1284,8 @@ static void iwlagn_mac_rssi_callback(struct ieee80211_hw *hw, IWL_DEBUG_MAC80211(priv, "enter\n"); mutex_lock(&priv->mutex); - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) { + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist) { if (rssi_event == RSSI_EVENT_LOW) priv->bt_enable_pspoll = true; else if (rssi_event == RSSI_EVENT_HIGH) @@ -1395,7 +1395,7 @@ static int iwl_setup_interface(struct iwl_priv *priv, return err; } - if (priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist && + if (priv->lib->bt_params && priv->lib->bt_params->advanced_bt_coexist && vif->type == NL80211_IFTYPE_ADHOC) { /* * pretend to have high BT traffic as long as we diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c index 0c77222c2c64..68f754659570 100644 --- a/drivers/net/wireless/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/iwlwifi/dvm/main.c @@ -615,7 +615,7 @@ static void iwl_rf_kill_ct_config(struct iwl_priv *priv) priv->thermal_throttle.ct_kill_toggle = false; - if (priv->cfg->base_params->support_ct_kill_exit) { + if (priv->lib->support_ct_kill_exit) { adv_cmd.critical_temperature_enter = cpu_to_le32(priv->hw_params.ct_kill_threshold); adv_cmd.critical_temperature_exit = @@ -732,10 +732,10 @@ int iwl_alive_start(struct iwl_priv *priv) } /* download priority table before any calibration request */ - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) { + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist) { /* Configure Bluetooth device coexistence support */ - if (priv->cfg->bt_params->bt_sco_disable) + if (priv->lib->bt_params->bt_sco_disable) priv->bt_enable_pspoll = false; else priv->bt_enable_pspoll = true; @@ -873,9 +873,9 @@ void iwl_down(struct iwl_priv *priv) priv->bt_status = 0; priv->cur_rssi_ctx = NULL; priv->bt_is_sco = 0; - if (priv->cfg->bt_params) + if (priv->lib->bt_params) priv->bt_traffic_load = - priv->cfg->bt_params->bt_init_traffic_load; + priv->lib->bt_params->bt_init_traffic_load; else priv->bt_traffic_load = 0; priv->bt_full_concurrent = false; @@ -1058,7 +1058,7 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv) iwl_setup_scan_deferred_work(priv); - if (priv->cfg->bt_params) + if (priv->lib->bt_params) iwlagn_bt_setup_deferred_work(priv); init_timer(&priv->statistics_periodic); @@ -1072,7 +1072,7 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv) void iwl_cancel_deferred_work(struct iwl_priv *priv) { - if (priv->cfg->bt_params) + if (priv->lib->bt_params) iwlagn_bt_cancel_deferred_work(priv); cancel_work_sync(&priv->run_time_calib_work); @@ -1098,8 +1098,7 @@ static int iwl_init_drv(struct iwl_priv *priv) priv->band = IEEE80211_BAND_2GHZ; - priv->plcp_delta_threshold = - priv->cfg->base_params->plcp_delta_threshold; + priv->plcp_delta_threshold = priv->lib->plcp_delta_threshold; priv->iw_mode = NL80211_IFTYPE_STATION; priv->current_ht_config.smps = IEEE80211_SMPS_STATIC; @@ -1116,8 +1115,8 @@ static int iwl_init_drv(struct iwl_priv *priv) iwl_init_scan_params(priv); /* init bt coex */ - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) { + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist) { priv->kill_ack_mask = IWLAGN_BT_KILL_ACK_MASK_DEFAULT; priv->kill_cts_mask = IWLAGN_BT_KILL_CTS_MASK_DEFAULT; priv->bt_valid = IWLAGN_BT_ALL_VALID_MSK; @@ -1267,9 +1266,11 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, priv->lib = &iwl_dvm_1000_cfg; break; case IWL_DEVICE_FAMILY_2000: - case IWL_DEVICE_FAMILY_105: priv->lib = &iwl_dvm_2000_cfg; break; + case IWL_DEVICE_FAMILY_105: + priv->lib = &iwl_dvm_105_cfg; + break; case IWL_DEVICE_FAMILY_2030: case IWL_DEVICE_FAMILY_135: priv->lib = &iwl_dvm_2030_cfg; @@ -1281,11 +1282,15 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, priv->lib = &iwl_dvm_5150_cfg; break; case IWL_DEVICE_FAMILY_6000: - case IWL_DEVICE_FAMILY_6005: case IWL_DEVICE_FAMILY_6000i: + priv->lib = &iwl_dvm_6000_cfg; + break; + case IWL_DEVICE_FAMILY_6005: + priv->lib = &iwl_dvm_6005_cfg; + break; case IWL_DEVICE_FAMILY_6050: case IWL_DEVICE_FAMILY_6150: - priv->lib = &iwl_dvm_6000_cfg; + priv->lib = &iwl_dvm_6050_cfg; break; case IWL_DEVICE_FAMILY_6030: priv->lib = &iwl_dvm_6030_cfg; diff --git a/drivers/net/wireless/iwlwifi/dvm/power.c b/drivers/net/wireless/iwlwifi/dvm/power.c index bd69018d07a9..77cb59712235 100644 --- a/drivers/net/wireless/iwlwifi/dvm/power.c +++ b/drivers/net/wireless/iwlwifi/dvm/power.c @@ -163,7 +163,7 @@ static void iwl_static_sleep_cmd(struct iwl_priv *priv, u8 skip; u32 slp_itrvl; - if (priv->cfg->adv_pm) { + if (priv->lib->adv_pm) { table = apm_range_2; if (period <= IWL_DTIM_RANGE_1_MAX) table = apm_range_1; @@ -217,7 +217,7 @@ static void iwl_static_sleep_cmd(struct iwl_priv *priv, cmd->flags &= ~IWL_POWER_SHADOW_REG_ENA; if (iwl_advanced_bt_coexist(priv)) { - if (!priv->cfg->bt_params->bt_sco_disable) + if (!priv->lib->bt_params->bt_sco_disable) cmd->flags |= IWL_POWER_BT_SCO_ENA; else cmd->flags &= ~IWL_POWER_BT_SCO_ENA; @@ -293,7 +293,7 @@ static void iwl_power_build_cmd(struct iwl_priv *priv, if (priv->wowlan) iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, dtimper); - else if (!priv->cfg->base_params->no_idle_support && + else if (!priv->lib->no_idle_support && priv->hw->conf.flags & IEEE80211_CONF_IDLE) iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, 20); else if (iwl_tt_is_low_power_state(priv)) { diff --git a/drivers/net/wireless/iwlwifi/dvm/rs.c b/drivers/net/wireless/iwlwifi/dvm/rs.c index 907bd6e50aad..94314a8e1029 100644 --- a/drivers/net/wireless/iwlwifi/dvm/rs.c +++ b/drivers/net/wireless/iwlwifi/dvm/rs.c @@ -1088,7 +1088,7 @@ done: (priv->tm_fixed_rate != lq_sta->dbg_fixed_rate)) rs_program_fix_rate(priv, lq_sta); #endif - if (priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist) + if (priv->lib->bt_params && priv->lib->bt_params->advanced_bt_coexist) rs_bt_update_lq(priv, ctx, lq_sta); } @@ -3064,11 +3064,11 @@ static void rs_fill_link_cmd(struct iwl_priv *priv, * overwrite if needed, pass aggregation time limit * to uCode in uSec */ - if (priv && priv->cfg->bt_params && - priv->cfg->bt_params->agg_time_limit && + if (priv && priv->lib->bt_params && + priv->lib->bt_params->agg_time_limit && priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) lq_cmd->agg_params.agg_time_limit = - cpu_to_le16(priv->cfg->bt_params->agg_time_limit); + cpu_to_le16(priv->lib->bt_params->agg_time_limit); } static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) diff --git a/drivers/net/wireless/iwlwifi/dvm/rx.c b/drivers/net/wireless/iwlwifi/dvm/rx.c index a4eed2055fdb..2f3fd160ab44 100644 --- a/drivers/net/wireless/iwlwifi/dvm/rx.c +++ b/drivers/net/wireless/iwlwifi/dvm/rx.c @@ -1102,7 +1102,7 @@ void iwl_setup_rx_handlers(struct iwl_priv *priv) iwl_notification_wait_init(&priv->notif_wait); /* Set up BT Rx handlers */ - if (priv->cfg->bt_params) + if (priv->lib->bt_params) iwlagn_bt_rx_handler_setup(priv); } diff --git a/drivers/net/wireless/iwlwifi/dvm/scan.c b/drivers/net/wireless/iwlwifi/dvm/scan.c index d69b55866714..8c686a5b90ac 100644 --- a/drivers/net/wireless/iwlwifi/dvm/scan.c +++ b/drivers/net/wireless/iwlwifi/dvm/scan.c @@ -801,8 +801,8 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) * Internal scans are passive, so we can indiscriminately set * the BT ignore flag on 2.4 GHz since it applies to TX only. */ - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist) scan->tx_cmd.tx_flags |= TX_CMD_FLG_IGNORE_BT; break; case IEEE80211_BAND_5GHZ: @@ -844,8 +844,8 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) band = priv->scan_band; if (band == IEEE80211_BAND_2GHZ && - priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) { + priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist) { /* transmit 2.4 GHz probes only on first antenna */ scan_tx_antennas = first_antenna(scan_tx_antennas); } @@ -873,8 +873,8 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) rx_ant = first_antenna(active_chains); } - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist && + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist && priv->bt_full_concurrent) { /* operated as 1x1 in full concurrency mode */ rx_ant = first_antenna(rx_ant); diff --git a/drivers/net/wireless/iwlwifi/dvm/tt.c b/drivers/net/wireless/iwlwifi/dvm/tt.c index 03f9bc01c0cc..fbeee081ee2f 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tt.c +++ b/drivers/net/wireless/iwlwifi/dvm/tt.c @@ -627,7 +627,7 @@ void iwl_tt_initialize(struct iwl_priv *priv) INIT_WORK(&priv->ct_enter, iwl_bg_ct_enter); INIT_WORK(&priv->ct_exit, iwl_bg_ct_exit); - if (priv->cfg->base_params->adv_thermal_throttle) { + if (priv->lib->adv_thermal_throttle) { IWL_DEBUG_TEMP(priv, "Advanced Thermal Throttling\n"); tt->restriction = kcalloc(IWL_TI_STATE_MAX, sizeof(struct iwl_tt_restriction), diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c index a900aaf47790..353a053b4eb1 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tx.c +++ b/drivers/net/wireless/iwlwifi/dvm/tx.c @@ -83,8 +83,8 @@ static void iwlagn_tx_cmd_build_basic(struct iwl_priv *priv, else if (ieee80211_is_back_req(fc)) tx_flags |= TX_CMD_FLG_ACK_MSK | TX_CMD_FLG_IMM_BA_RSP_MASK; else if (info->band == IEEE80211_BAND_2GHZ && - priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist && + priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist && (ieee80211_is_auth(fc) || ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc) || skb->protocol == cpu_to_be16(ETH_P_PAE))) @@ -202,8 +202,8 @@ static void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv, rate_flags |= RATE_MCS_CCK_MSK; /* Set up antennas */ - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist && + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist && priv->bt_full_concurrent) { /* operated as 1x1 in full concurrency mode */ priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant, @@ -986,8 +986,8 @@ static void iwl_rx_reply_tx_agg(struct iwl_priv *priv, * notification again. */ if (tx_resp->bt_kill_count && tx_resp->frame_count == 1 && - priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) { + priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist) { IWL_DEBUG_COEX(priv, "receive reply tx w/ bt_kill\n"); } diff --git a/drivers/net/wireless/iwlwifi/dvm/ucode.c b/drivers/net/wireless/iwlwifi/dvm/ucode.c index 0a1cdc5e856b..86270b69cd02 100644 --- a/drivers/net/wireless/iwlwifi/dvm/ucode.c +++ b/drivers/net/wireless/iwlwifi/dvm/ucode.c @@ -132,8 +132,8 @@ int iwl_init_alive_start(struct iwl_priv *priv) { int ret; - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) { + if (priv->lib->bt_params && + priv->lib->bt_params->advanced_bt_coexist) { /* * Tell uCode we are ready to perform calibration * need to perform this before any calibration @@ -155,8 +155,8 @@ int iwl_init_alive_start(struct iwl_priv *priv) * temperature offset calibration is only needed for runtime ucode, * so prepare the value now. */ - if (priv->cfg->need_temp_offset_calib) { - if (priv->cfg->temp_offset_v2) + if (priv->lib->need_temp_offset_calib) { + if (priv->lib->temp_offset_v2) return iwl_set_temperature_offset_calib_v2(priv); else return iwl_set_temperature_offset_calib(priv); @@ -277,7 +277,7 @@ static int iwl_alive_notify(struct iwl_priv *priv) if (ret) return ret; - if (!priv->cfg->no_xtal_calib) { + if (!priv->lib->no_xtal_calib) { ret = iwl_set_Xtal_calib(priv); if (ret) return ret; diff --git a/drivers/net/wireless/iwlwifi/iwl-1000.c b/drivers/net/wireless/iwlwifi/iwl-1000.c index c080ae3070b2..0d2afe098afc 100644 --- a/drivers/net/wireless/iwlwifi/iwl-1000.c +++ b/drivers/net/wireless/iwlwifi/iwl-1000.c @@ -60,9 +60,6 @@ static const struct iwl_base_params iwl1000_base_params = { .max_ll_items = OTP_MAX_LL_ITEMS_1000, .shadow_ram_support = false, .led_compensation = 51, - .support_ct_kill_exit = true, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF, - .chain_noise_scale = 1000, .wd_timeout = IWL_WATCHDOG_DISABLED, .max_event_log_size = 128, }; diff --git a/drivers/net/wireless/iwlwifi/iwl-2000.c b/drivers/net/wireless/iwlwifi/iwl-2000.c index a6ddd2f9fba0..c727ec7c90a6 100644 --- a/drivers/net/wireless/iwlwifi/iwl-2000.c +++ b/drivers/net/wireless/iwlwifi/iwl-2000.c @@ -72,14 +72,9 @@ static const struct iwl_base_params iwl2000_base_params = { .max_ll_items = OTP_MAX_LL_ITEMS_2x00, .shadow_ram_support = true, .led_compensation = 51, - .adv_thermal_throttle = true, - .support_ct_kill_exit = true, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, - .chain_noise_scale = 1000, .wd_timeout = IWL_DEF_WD_TIMEOUT, .max_event_log_size = 512, .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ - .hd_v2 = true, }; @@ -90,14 +85,9 @@ static const struct iwl_base_params iwl2030_base_params = { .max_ll_items = OTP_MAX_LL_ITEMS_2x00, .shadow_ram_support = true, .led_compensation = 57, - .adv_thermal_throttle = true, - .support_ct_kill_exit = true, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, - .chain_noise_scale = 1000, .wd_timeout = IWL_LONG_WD_TIMEOUT, .max_event_log_size = 512, .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ - .hd_v2 = true, }; static const struct iwl_ht_params iwl2000_ht_params = { @@ -106,16 +96,6 @@ static const struct iwl_ht_params iwl2000_ht_params = { .ht40_bands = BIT(IEEE80211_BAND_2GHZ), }; -static const struct iwl_bt_params iwl2030_bt_params = { - /* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */ - .advanced_bt_coexist = true, - .agg_time_limit = BT_AGG_THRESHOLD_DEF, - .bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE, - .bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT32, - .bt_sco_disable = true, - .bt_session_2 = true, -}; - static const struct iwl_eeprom_params iwl20x0_eeprom_params = { .regulatory_bands = { EEPROM_REG_BAND_1_CHANNELS, @@ -137,12 +117,10 @@ static const struct iwl_eeprom_params iwl20x0_eeprom_params = { .device_family = IWL_DEVICE_FAMILY_2000, \ .max_inst_size = IWL60_RTC_INST_SIZE, \ .max_data_size = IWL60_RTC_DATA_SIZE, \ - .nvm_ver = EEPROM_2000_EEPROM_VERSION, \ - .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ + .nvm_ver = EEPROM_2000_EEPROM_VERSION, \ + .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ .base_params = &iwl2000_base_params, \ .eeprom_params = &iwl20x0_eeprom_params, \ - .need_temp_offset_calib = true, \ - .temp_offset_v2 = true, \ .led_mode = IWL_LED_RF_STATE const struct iwl_cfg iwl2000_2bgn_cfg = { @@ -168,12 +146,8 @@ const struct iwl_cfg iwl2000_2bgn_d_cfg = { .nvm_ver = EEPROM_2000_EEPROM_VERSION, \ .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ .base_params = &iwl2030_base_params, \ - .bt_params = &iwl2030_bt_params, \ .eeprom_params = &iwl20x0_eeprom_params, \ - .need_temp_offset_calib = true, \ - .temp_offset_v2 = true, \ - .led_mode = IWL_LED_RF_STATE, \ - .adv_pm = true + .led_mode = IWL_LED_RF_STATE const struct iwl_cfg iwl2030_2bgn_cfg = { .name = "Intel(R) Centrino(R) Wireless-N 2230 BGN", @@ -193,10 +167,7 @@ const struct iwl_cfg iwl2030_2bgn_cfg = { .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ .base_params = &iwl2000_base_params, \ .eeprom_params = &iwl20x0_eeprom_params, \ - .need_temp_offset_calib = true, \ - .temp_offset_v2 = true, \ .led_mode = IWL_LED_RF_STATE, \ - .adv_pm = true, \ .rx_with_siso_diversity = true const struct iwl_cfg iwl105_bgn_cfg = { @@ -222,12 +193,8 @@ const struct iwl_cfg iwl105_bgn_d_cfg = { .nvm_ver = EEPROM_2000_EEPROM_VERSION, \ .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ .base_params = &iwl2030_base_params, \ - .bt_params = &iwl2030_bt_params, \ .eeprom_params = &iwl20x0_eeprom_params, \ - .need_temp_offset_calib = true, \ - .temp_offset_v2 = true, \ .led_mode = IWL_LED_RF_STATE, \ - .adv_pm = true, \ .rx_with_siso_diversity = true const struct iwl_cfg iwl135_bgn_cfg = { diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index 403f3f224bf6..ecc01e1a61a1 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -59,11 +59,8 @@ static const struct iwl_base_params iwl5000_base_params = { .num_of_queues = IWLAGN_NUM_QUEUES, .pll_cfg_val = CSR50_ANA_PLL_CFG_VAL, .led_compensation = 51, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, - .chain_noise_scale = 1000, .wd_timeout = IWL_WATCHDOG_DISABLED, .max_event_log_size = 512, - .no_idle_support = true, }; static const struct iwl_ht_params iwl5000_ht_params = { @@ -159,7 +156,6 @@ const struct iwl_cfg iwl5350_agn_cfg = { .nvm_calib_ver = EEPROM_5050_TX_POWER_VERSION, \ .base_params = &iwl5000_base_params, \ .eeprom_params = &iwl5000_eeprom_params, \ - .no_xtal_calib = true, \ .led_mode = IWL_LED_BLINK, \ .internal_wimax_coex = true diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c index b5ab8d1bcac0..30d45e2fc193 100644 --- a/drivers/net/wireless/iwlwifi/iwl-6000.c +++ b/drivers/net/wireless/iwlwifi/iwl-6000.c @@ -82,10 +82,6 @@ static const struct iwl_base_params iwl6000_base_params = { .max_ll_items = OTP_MAX_LL_ITEMS_6x00, .shadow_ram_support = true, .led_compensation = 51, - .adv_thermal_throttle = true, - .support_ct_kill_exit = true, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, - .chain_noise_scale = 1000, .wd_timeout = IWL_DEF_WD_TIMEOUT, .max_event_log_size = 512, .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ @@ -98,10 +94,6 @@ static const struct iwl_base_params iwl6050_base_params = { .max_ll_items = OTP_MAX_LL_ITEMS_6x50, .shadow_ram_support = true, .led_compensation = 51, - .adv_thermal_throttle = true, - .support_ct_kill_exit = true, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, - .chain_noise_scale = 1500, .wd_timeout = IWL_DEF_WD_TIMEOUT, .max_event_log_size = 1024, .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ @@ -114,10 +106,6 @@ static const struct iwl_base_params iwl6000_g2_base_params = { .max_ll_items = OTP_MAX_LL_ITEMS_6x00, .shadow_ram_support = true, .led_compensation = 57, - .adv_thermal_throttle = true, - .support_ct_kill_exit = true, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, - .chain_noise_scale = 1000, .wd_timeout = IWL_LONG_WD_TIMEOUT, .max_event_log_size = 512, .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ @@ -129,15 +117,6 @@ static const struct iwl_ht_params iwl6000_ht_params = { .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ), }; -static const struct iwl_bt_params iwl6000_bt_params = { - /* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */ - .advanced_bt_coexist = true, - .agg_time_limit = BT_AGG_THRESHOLD_DEF, - .bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE, - .bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT, - .bt_sco_disable = true, -}; - static const struct iwl_eeprom_params iwl6000_eeprom_params = { .regulatory_bands = { EEPROM_REG_BAND_1_CHANNELS, @@ -163,7 +142,6 @@ static const struct iwl_eeprom_params iwl6000_eeprom_params = { .nvm_calib_ver = EEPROM_6005_TX_POWER_VERSION, \ .base_params = &iwl6000_g2_base_params, \ .eeprom_params = &iwl6000_eeprom_params, \ - .need_temp_offset_calib = true, \ .led_mode = IWL_LED_RF_STATE const struct iwl_cfg iwl6005_2agn_cfg = { @@ -217,11 +195,8 @@ const struct iwl_cfg iwl6005_2agn_mow2_cfg = { .nvm_ver = EEPROM_6030_EEPROM_VERSION, \ .nvm_calib_ver = EEPROM_6030_TX_POWER_VERSION, \ .base_params = &iwl6000_g2_base_params, \ - .bt_params = &iwl6000_bt_params, \ .eeprom_params = &iwl6000_eeprom_params, \ - .need_temp_offset_calib = true, \ - .led_mode = IWL_LED_RF_STATE, \ - .adv_pm = true \ + .led_mode = IWL_LED_RF_STATE const struct iwl_cfg iwl6030_2agn_cfg = { .name = "Intel(R) Centrino(R) Advanced-N 6230 AGN", @@ -256,11 +231,8 @@ const struct iwl_cfg iwl6030_2bg_cfg = { .nvm_ver = EEPROM_6030_EEPROM_VERSION, \ .nvm_calib_ver = EEPROM_6030_TX_POWER_VERSION, \ .base_params = &iwl6000_g2_base_params, \ - .bt_params = &iwl6000_bt_params, \ .eeprom_params = &iwl6000_eeprom_params, \ - .need_temp_offset_calib = true, \ - .led_mode = IWL_LED_RF_STATE, \ - .adv_pm = true + .led_mode = IWL_LED_RF_STATE const struct iwl_cfg iwl6035_2agn_cfg = { .name = "Intel(R) Centrino(R) Advanced-N 6235 AGN", diff --git a/drivers/net/wireless/iwlwifi/iwl-7000.c b/drivers/net/wireless/iwlwifi/iwl-7000.c index c9aae2a2bbbb..d4f3b4864ab1 100644 --- a/drivers/net/wireless/iwlwifi/iwl-7000.c +++ b/drivers/net/wireless/iwlwifi/iwl-7000.c @@ -96,10 +96,6 @@ static const struct iwl_base_params iwl7000_base_params = { .pll_cfg_val = 0, .shadow_ram_support = true, .led_compensation = 57, - .adv_thermal_throttle = true, - .support_ct_kill_exit = true, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, - .chain_noise_scale = 1000, .wd_timeout = IWL_LONG_WD_TIMEOUT, .max_event_log_size = 512, .shadow_reg_enable = true, @@ -118,10 +114,7 @@ static const struct iwl_ht_params iwl7000_ht_params = { .max_inst_size = IWL60_RTC_INST_SIZE, \ .max_data_size = IWL60_RTC_DATA_SIZE, \ .base_params = &iwl7000_base_params, \ - /* TODO: .bt_params? */ \ - .need_temp_offset_calib = true, \ - .led_mode = IWL_LED_RF_STATE, \ - .adv_pm = true \ + .led_mode = IWL_LED_RF_STATE const struct iwl_cfg iwl7260_2ac_cfg = { diff --git a/drivers/net/wireless/iwlwifi/iwl-config.h b/drivers/net/wireless/iwlwifi/iwl-config.h index c3c9268affab..a193832fc790 100644 --- a/drivers/net/wireless/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/iwlwifi/iwl-config.h @@ -136,17 +136,9 @@ enum iwl_led_mode { * @led_compensation: compensate on the led on/off time per HW according * to the deviation to achieve the desired led frequency. * The detail algorithm is described in iwl-led.c - * @chain_noise_num_beacons: number of beacons used to compute chain noise - * @adv_thermal_throttle: support advance thermal throttle - * @support_ct_kill_exit: support ct kill exit condition - * @plcp_delta_threshold: plcp error rate threshold used to trigger - * radio tuning when there is a high receiving plcp error rate - * @chain_noise_scale: default chain noise scale used for gain computation * @wd_timeout: TX queues watchdog timeout * @max_event_log_size: size of event log buffer size for ucode event logging * @shadow_reg_enable: HW shadow register support - * @hd_v2: v2 of enhanced sensitivity value, used for 2000 series and up - * @no_idle_support: do not support idle mode */ struct iwl_base_params { int eeprom_size; @@ -157,31 +149,9 @@ struct iwl_base_params { const u16 max_ll_items; const bool shadow_ram_support; u16 led_compensation; - bool adv_thermal_throttle; - bool support_ct_kill_exit; - u8 plcp_delta_threshold; - s32 chain_noise_scale; unsigned int wd_timeout; u32 max_event_log_size; const bool shadow_reg_enable; - const bool hd_v2; - const bool no_idle_support; -}; - -/* - * @advanced_bt_coexist: support advanced bt coexist - * @bt_init_traffic_load: specify initial bt traffic load - * @bt_prio_boost: default bt priority boost value - * @agg_time_limit: maximum number of uSec in aggregation - * @bt_sco_disable: uCode should not response to BT in SCO/ESCO mode - */ -struct iwl_bt_params { - bool advanced_bt_coexist; - u8 bt_init_traffic_load; - u32 bt_prio_boost; - u16 agg_time_limit; - bool bt_sco_disable; - bool bt_session_2; }; /* @@ -231,16 +201,10 @@ struct iwl_eeprom_params { * @nvm_calib_ver: NVM calibration version * @lib: pointer to the lib ops * @base_params: pointer to basic parameters - * @ht_params: point to ht patameters - * @bt_params: pointer to bt parameters - * @need_temp_offset_calib: need to perform temperature offset calibration - * @no_xtal_calib: some devices do not need crystal calibration data, - * don't send it to those + * @ht_params: point to ht parameters * @led_mode: 0=blinking, 1=On(RF On)/Off(RF Off) - * @adv_pm: advance power management * @rx_with_siso_diversity: 1x1 device with rx antenna diversity * @internal_wimax_coex: internal wifi/wimax combo device - * @temp_offset_v2: support v2 of temperature offset calibration * * We enable the driver to be backward compatible wrt. hardware features. * API differences in uCode shouldn't be handled here but through TLVs @@ -264,15 +228,10 @@ struct iwl_cfg { const struct iwl_base_params *base_params; /* params likely to change within a device family */ const struct iwl_ht_params *ht_params; - const struct iwl_bt_params *bt_params; const struct iwl_eeprom_params *eeprom_params; - const bool need_temp_offset_calib; /* if used set to true */ - const bool no_xtal_calib; enum iwl_led_mode led_mode; - const bool adv_pm; const bool rx_with_siso_diversity; const bool internal_wimax_coex; - const bool temp_offset_v2; }; /* -- cgit v1.2.3 From 129219c0fd234164ebc19e8694641927317eda13 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 17 May 2013 10:38:21 +0200 Subject: iwlwifi: dvm: constify configuration structs The pointer that gets used is already const, so the structs can obviously be const as well. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/agn.h | 20 ++++++++++---------- drivers/net/wireless/iwlwifi/dvm/devices.c | 18 +++++++++--------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/dvm/agn.h b/drivers/net/wireless/iwlwifi/dvm/agn.h index df64a546f52d..de2c9514bef6 100644 --- a/drivers/net/wireless/iwlwifi/dvm/agn.h +++ b/drivers/net/wireless/iwlwifi/dvm/agn.h @@ -76,16 +76,16 @@ #define IWL_INVALID_STATION 255 /* device operations */ -extern struct iwl_dvm_cfg iwl_dvm_1000_cfg; -extern struct iwl_dvm_cfg iwl_dvm_2000_cfg; -extern struct iwl_dvm_cfg iwl_dvm_105_cfg; -extern struct iwl_dvm_cfg iwl_dvm_2030_cfg; -extern struct iwl_dvm_cfg iwl_dvm_5000_cfg; -extern struct iwl_dvm_cfg iwl_dvm_5150_cfg; -extern struct iwl_dvm_cfg iwl_dvm_6000_cfg; -extern struct iwl_dvm_cfg iwl_dvm_6005_cfg; -extern struct iwl_dvm_cfg iwl_dvm_6050_cfg; -extern struct iwl_dvm_cfg iwl_dvm_6030_cfg; +extern const struct iwl_dvm_cfg iwl_dvm_1000_cfg; +extern const struct iwl_dvm_cfg iwl_dvm_2000_cfg; +extern const struct iwl_dvm_cfg iwl_dvm_105_cfg; +extern const struct iwl_dvm_cfg iwl_dvm_2030_cfg; +extern const struct iwl_dvm_cfg iwl_dvm_5000_cfg; +extern const struct iwl_dvm_cfg iwl_dvm_5150_cfg; +extern const struct iwl_dvm_cfg iwl_dvm_6000_cfg; +extern const struct iwl_dvm_cfg iwl_dvm_6005_cfg; +extern const struct iwl_dvm_cfg iwl_dvm_6050_cfg; +extern const struct iwl_dvm_cfg iwl_dvm_6030_cfg; #define TIME_UNIT 1024 diff --git a/drivers/net/wireless/iwlwifi/dvm/devices.c b/drivers/net/wireless/iwlwifi/dvm/devices.c index 5a1c2186ea34..352c6cb7b4f1 100644 --- a/drivers/net/wireless/iwlwifi/dvm/devices.c +++ b/drivers/net/wireless/iwlwifi/dvm/devices.c @@ -174,7 +174,7 @@ static void iwl1000_hw_set_hw_params(struct iwl_priv *priv) priv->hw_params.sens = &iwl1000_sensitivity; } -struct iwl_dvm_cfg iwl_dvm_1000_cfg = { +const struct iwl_dvm_cfg iwl_dvm_1000_cfg = { .set_hw_params = iwl1000_hw_set_hw_params, .nic_config = iwl1000_nic_config, .temperature = iwlagn_temperature, @@ -235,7 +235,7 @@ static void iwl2000_hw_set_hw_params(struct iwl_priv *priv) priv->hw_params.sens = &iwl2000_sensitivity; } -struct iwl_dvm_cfg iwl_dvm_2000_cfg = { +const struct iwl_dvm_cfg iwl_dvm_2000_cfg = { .set_hw_params = iwl2000_hw_set_hw_params, .nic_config = iwl2000_nic_config, .temperature = iwlagn_temperature, @@ -248,7 +248,7 @@ struct iwl_dvm_cfg iwl_dvm_2000_cfg = { .temp_offset_v2 = true, }; -struct iwl_dvm_cfg iwl_dvm_105_cfg = { +const struct iwl_dvm_cfg iwl_dvm_105_cfg = { .set_hw_params = iwl2000_hw_set_hw_params, .nic_config = iwl2000_nic_config, .temperature = iwlagn_temperature, @@ -272,7 +272,7 @@ static const struct iwl_dvm_bt_params iwl2030_bt_params = { .bt_session_2 = true, }; -struct iwl_dvm_cfg iwl_dvm_2030_cfg = { +const struct iwl_dvm_cfg iwl_dvm_2030_cfg = { .set_hw_params = iwl2000_hw_set_hw_params, .nic_config = iwl2000_nic_config, .temperature = iwlagn_temperature, @@ -463,7 +463,7 @@ static int iwl5000_hw_channel_switch(struct iwl_priv *priv, return iwl_dvm_send_cmd(priv, &hcmd); } -struct iwl_dvm_cfg iwl_dvm_5000_cfg = { +const struct iwl_dvm_cfg iwl_dvm_5000_cfg = { .set_hw_params = iwl5000_hw_set_hw_params, .set_channel_switch = iwl5000_hw_channel_switch, .temperature = iwlagn_temperature, @@ -472,7 +472,7 @@ struct iwl_dvm_cfg iwl_dvm_5000_cfg = { .no_idle_support = true, }; -struct iwl_dvm_cfg iwl_dvm_5150_cfg = { +const struct iwl_dvm_cfg iwl_dvm_5150_cfg = { .set_hw_params = iwl5150_hw_set_hw_params, .set_channel_switch = iwl5000_hw_channel_switch, .temperature = iwl5150_temperature, @@ -634,7 +634,7 @@ static int iwl6000_hw_channel_switch(struct iwl_priv *priv, return err; } -struct iwl_dvm_cfg iwl_dvm_6000_cfg = { +const struct iwl_dvm_cfg iwl_dvm_6000_cfg = { .set_hw_params = iwl6000_hw_set_hw_params, .set_channel_switch = iwl6000_hw_channel_switch, .nic_config = iwl6000_nic_config, @@ -657,7 +657,7 @@ const struct iwl_dvm_cfg iwl_dvm_6005_cfg = { .need_temp_offset_calib = true, }; -struct iwl_dvm_cfg iwl_dvm_6050_cfg = { +const struct iwl_dvm_cfg iwl_dvm_6050_cfg = { .set_hw_params = iwl6000_hw_set_hw_params, .set_channel_switch = iwl6000_hw_channel_switch, .nic_config = iwl6000_nic_config, @@ -677,7 +677,7 @@ static const struct iwl_dvm_bt_params iwl6000_bt_params = { .bt_sco_disable = true, }; -struct iwl_dvm_cfg iwl_dvm_6030_cfg = { +const struct iwl_dvm_cfg iwl_dvm_6030_cfg = { .set_hw_params = iwl6000_hw_set_hw_params, .set_channel_switch = iwl6000_hw_channel_switch, .nic_config = iwl6000_nic_config, -- cgit v1.2.3 From 3f869d6d41d032392abafe17ea5257a2514a24a7 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 16 May 2013 05:09:56 +0000 Subject: cpufreq: Add EXPORT_SYMBOL_GPL for have_governor_per_policy This patch adds: EXPORT_SYMBOL_GPL(have_governor_per_policy), so that this routine can be used by modules too. Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 2d53f47d1747..2f254691ed1f 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -132,6 +132,7 @@ bool have_governor_per_policy(void) { return cpufreq_driver->have_governor_per_policy; } +EXPORT_SYMBOL_GPL(have_governor_per_policy); static struct cpufreq_policy *__cpufreq_cpu_get(unsigned int cpu, bool sysfs) { -- cgit v1.2.3 From 944e9a0316e60bc5bc122e46c1fde36e5f6e9f56 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 16 May 2013 05:09:57 +0000 Subject: cpufreq: governors: Move get_governor_parent_kobj() to cpufreq.c get_governor_parent_kobj() can be used by any governor, generic cpufreq governors or platform specific ones and so must be present in cpufreq.c instead of cpufreq_governor.c. This patch moves it to cpufreq.c. This also adds EXPORT_SYMBOL_GPL(get_governor_parent_kobj) so that modules can use this function too. Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq.c | 9 +++++++++ drivers/cpufreq/cpufreq_governor.c | 8 -------- include/linux/cpufreq.h | 1 + 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 2f254691ed1f..3faf62bdbad0 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -134,6 +134,15 @@ bool have_governor_per_policy(void) } EXPORT_SYMBOL_GPL(have_governor_per_policy); +struct kobject *get_governor_parent_kobj(struct cpufreq_policy *policy) +{ + if (have_governor_per_policy()) + return &policy->kobj; + else + return cpufreq_global_kobject; +} +EXPORT_SYMBOL_GPL(get_governor_parent_kobj); + static struct cpufreq_policy *__cpufreq_cpu_get(unsigned int cpu, bool sysfs) { struct cpufreq_policy *data; diff --git a/drivers/cpufreq/cpufreq_governor.c b/drivers/cpufreq/cpufreq_governor.c index 5af40ad82d23..d1421b498c76 100644 --- a/drivers/cpufreq/cpufreq_governor.c +++ b/drivers/cpufreq/cpufreq_governor.c @@ -29,14 +29,6 @@ #include "cpufreq_governor.h" -static struct kobject *get_governor_parent_kobj(struct cpufreq_policy *policy) -{ - if (have_governor_per_policy()) - return &policy->kobj; - else - return cpufreq_global_kobject; -} - static struct attribute_group *get_sysfs_attr(struct dbs_data *dbs_data) { if (have_governor_per_policy()) diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 037d36ae63e5..dd1a5d41357b 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -340,6 +340,7 @@ const char *cpufreq_get_current_driver(void); int cpufreq_get_policy(struct cpufreq_policy *policy, unsigned int cpu); int cpufreq_update_policy(unsigned int cpu); bool have_governor_per_policy(void); +struct kobject *get_governor_parent_kobj(struct cpufreq_policy *policy); #ifdef CONFIG_CPU_FREQ /* query the current CPU frequency (in kHz). If zero, cpufreq couldn't detect it */ -- cgit v1.2.3 From 72a4ce340a7ebf39e1c6fdc8f5feb4f974d6c635 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 17 May 2013 11:26:32 +0000 Subject: cpufreq: Move get_cpu_idle_time() to cpufreq.c Governors other than ondemand and conservative can also use get_cpu_idle_time() and they aren't required to compile cpufreq_governor.c. So, move these independent routines to cpufreq.c instead. Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq.c | 38 ++++++++++++++++++++++++++++++++++++++ drivers/cpufreq/cpufreq_governor.c | 36 ------------------------------------ drivers/cpufreq/cpufreq_governor.h | 1 - include/linux/cpufreq.h | 1 + 4 files changed, 39 insertions(+), 37 deletions(-) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 3faf62bdbad0..c6ab21880c07 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -17,7 +17,9 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include +#include #include #include #include @@ -25,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -143,6 +146,41 @@ struct kobject *get_governor_parent_kobj(struct cpufreq_policy *policy) } EXPORT_SYMBOL_GPL(get_governor_parent_kobj); +static inline u64 get_cpu_idle_time_jiffy(unsigned int cpu, u64 *wall) +{ + u64 idle_time; + u64 cur_wall_time; + u64 busy_time; + + cur_wall_time = jiffies64_to_cputime64(get_jiffies_64()); + + busy_time = kcpustat_cpu(cpu).cpustat[CPUTIME_USER]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_SYSTEM]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_IRQ]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_SOFTIRQ]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_STEAL]; + busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_NICE]; + + idle_time = cur_wall_time - busy_time; + if (wall) + *wall = cputime_to_usecs(cur_wall_time); + + return cputime_to_usecs(idle_time); +} + +u64 get_cpu_idle_time(unsigned int cpu, u64 *wall, int io_busy) +{ + u64 idle_time = get_cpu_idle_time_us(cpu, io_busy ? wall : NULL); + + if (idle_time == -1ULL) + return get_cpu_idle_time_jiffy(cpu, wall); + else if (!io_busy) + idle_time += get_cpu_iowait_time_us(cpu, wall); + + return idle_time; +} +EXPORT_SYMBOL_GPL(get_cpu_idle_time); + static struct cpufreq_policy *__cpufreq_cpu_get(unsigned int cpu, bool sysfs) { struct cpufreq_policy *data; diff --git a/drivers/cpufreq/cpufreq_governor.c b/drivers/cpufreq/cpufreq_governor.c index d1421b498c76..b6cfd55d8266 100644 --- a/drivers/cpufreq/cpufreq_governor.c +++ b/drivers/cpufreq/cpufreq_governor.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include @@ -37,41 +36,6 @@ static struct attribute_group *get_sysfs_attr(struct dbs_data *dbs_data) return dbs_data->cdata->attr_group_gov_sys; } -static inline u64 get_cpu_idle_time_jiffy(unsigned int cpu, u64 *wall) -{ - u64 idle_time; - u64 cur_wall_time; - u64 busy_time; - - cur_wall_time = jiffies64_to_cputime64(get_jiffies_64()); - - busy_time = kcpustat_cpu(cpu).cpustat[CPUTIME_USER]; - busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_SYSTEM]; - busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_IRQ]; - busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_SOFTIRQ]; - busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_STEAL]; - busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_NICE]; - - idle_time = cur_wall_time - busy_time; - if (wall) - *wall = cputime_to_usecs(cur_wall_time); - - return cputime_to_usecs(idle_time); -} - -u64 get_cpu_idle_time(unsigned int cpu, u64 *wall, int io_busy) -{ - u64 idle_time = get_cpu_idle_time_us(cpu, io_busy ? wall : NULL); - - if (idle_time == -1ULL) - return get_cpu_idle_time_jiffy(cpu, wall); - else if (!io_busy) - idle_time += get_cpu_iowait_time_us(cpu, wall); - - return idle_time; -} -EXPORT_SYMBOL_GPL(get_cpu_idle_time); - void dbs_check_cpu(struct dbs_data *dbs_data, int cpu) { struct cpu_dbs_common_info *cdbs = dbs_data->cdata->get_cpu_cdbs(cpu); diff --git a/drivers/cpufreq/cpufreq_governor.h b/drivers/cpufreq/cpufreq_governor.h index e16a96130cb3..e7bbf767380d 100644 --- a/drivers/cpufreq/cpufreq_governor.h +++ b/drivers/cpufreq/cpufreq_governor.h @@ -256,7 +256,6 @@ static ssize_t show_sampling_rate_min_gov_pol \ return sprintf(buf, "%u\n", dbs_data->min_sampling_rate); \ } -u64 get_cpu_idle_time(unsigned int cpu, u64 *wall, int io_busy); void dbs_check_cpu(struct dbs_data *dbs_data, int cpu); bool need_load_eval(struct cpu_dbs_common_info *cdbs, unsigned int sampling_rate); diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index dd1a5d41357b..fbf392aaa02e 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -337,6 +337,7 @@ const char *cpufreq_get_current_driver(void); /********************************************************************* * CPUFREQ 2.6. INTERFACE * *********************************************************************/ +u64 get_cpu_idle_time(unsigned int cpu, u64 *wall, int io_busy); int cpufreq_get_policy(struct cpufreq_policy *policy, unsigned int cpu); int cpufreq_update_policy(unsigned int cpu); bool have_governor_per_policy(void); -- cgit v1.2.3 From 2361be23666232dbb4851a527f466c4cbf5340fc Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 17 May 2013 16:09:09 +0530 Subject: cpufreq: Don't create empty /sys/devices/system/cpu/cpufreq directory When we don't have any file in cpu/cpufreq directory we shouldn't create it. Specially with the introduction of per-policy governor instance patchset, even governors are moved to cpu/cpu*/cpufreq/governor-name directory and so this directory is just not required. Lets have it only when required. Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/acpi-cpufreq.c | 4 ++-- drivers/cpufreq/cpufreq.c | 48 ++++++++++++++++++++++++++++++++++---- drivers/cpufreq/cpufreq_governor.c | 6 +++++ include/linux/cpufreq.h | 4 ++++ 4 files changed, 56 insertions(+), 6 deletions(-) diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c index 11b8b4b54ceb..8c02622b35a4 100644 --- a/drivers/cpufreq/acpi-cpufreq.c +++ b/drivers/cpufreq/acpi-cpufreq.c @@ -947,7 +947,7 @@ static void __init acpi_cpufreq_boost_init(void) /* We create the boost file in any case, though for systems without * hardware support it will be read-only and hardwired to return 0. */ - if (sysfs_create_file(cpufreq_global_kobject, &(global_boost.attr))) + if (cpufreq_sysfs_create_file(&(global_boost.attr))) pr_warn(PFX "could not register global boost sysfs file\n"); else pr_debug("registered global boost sysfs file\n"); @@ -955,7 +955,7 @@ static void __init acpi_cpufreq_boost_init(void) static void __exit acpi_cpufreq_boost_exit(void) { - sysfs_remove_file(cpufreq_global_kobject, &(global_boost.attr)); + cpufreq_sysfs_remove_file(&(global_boost.attr)); if (msrs) { unregister_cpu_notifier(&boost_nb); diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index c6ab21880c07..ce9273a7b4e3 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -678,9 +678,6 @@ static struct attribute *default_attrs[] = { NULL }; -struct kobject *cpufreq_global_kobject; -EXPORT_SYMBOL(cpufreq_global_kobject); - #define to_policy(k) container_of(k, struct cpufreq_policy, kobj) #define to_attr(a) container_of(a, struct freq_attr, attr) @@ -751,6 +748,49 @@ static struct kobj_type ktype_cpufreq = { .release = cpufreq_sysfs_release, }; +struct kobject *cpufreq_global_kobject; +EXPORT_SYMBOL(cpufreq_global_kobject); + +static int cpufreq_global_kobject_usage; + +int cpufreq_get_global_kobject(void) +{ + if (!cpufreq_global_kobject_usage++) + return kobject_add(cpufreq_global_kobject, + &cpu_subsys.dev_root->kobj, "%s", "cpufreq"); + + return 0; +} +EXPORT_SYMBOL(cpufreq_get_global_kobject); + +void cpufreq_put_global_kobject(void) +{ + if (!--cpufreq_global_kobject_usage) + kobject_del(cpufreq_global_kobject); +} +EXPORT_SYMBOL(cpufreq_put_global_kobject); + +int cpufreq_sysfs_create_file(const struct attribute *attr) +{ + int ret = cpufreq_get_global_kobject(); + + if (!ret) { + ret = sysfs_create_file(cpufreq_global_kobject, attr); + if (ret) + cpufreq_put_global_kobject(); + } + + return ret; +} +EXPORT_SYMBOL(cpufreq_sysfs_create_file); + +void cpufreq_sysfs_remove_file(const struct attribute *attr) +{ + sysfs_remove_file(cpufreq_global_kobject, attr); + cpufreq_put_global_kobject(); +} +EXPORT_SYMBOL(cpufreq_sysfs_remove_file); + /* symlink affected CPUs */ static int cpufreq_add_dev_symlink(unsigned int cpu, struct cpufreq_policy *policy) @@ -2020,7 +2060,7 @@ static int __init cpufreq_core_init(void) init_rwsem(&per_cpu(cpu_policy_rwsem, cpu)); } - cpufreq_global_kobject = kobject_create_and_add("cpufreq", &cpu_subsys.dev_root->kobj); + cpufreq_global_kobject = kobject_create(); BUG_ON(!cpufreq_global_kobject); register_syscore_ops(&cpufreq_syscore_ops); diff --git a/drivers/cpufreq/cpufreq_governor.c b/drivers/cpufreq/cpufreq_governor.c index b6cfd55d8266..7532570c42b4 100644 --- a/drivers/cpufreq/cpufreq_governor.c +++ b/drivers/cpufreq/cpufreq_governor.c @@ -231,6 +231,9 @@ int cpufreq_governor_dbs(struct cpufreq_policy *policy, return rc; } + if (!have_governor_per_policy()) + WARN_ON(cpufreq_get_global_kobject()); + rc = sysfs_create_group(get_governor_parent_kobj(policy), get_sysfs_attr(dbs_data)); if (rc) { @@ -269,6 +272,9 @@ int cpufreq_governor_dbs(struct cpufreq_policy *policy, sysfs_remove_group(get_governor_parent_kobj(policy), get_sysfs_attr(dbs_data)); + if (!have_governor_per_policy()) + cpufreq_put_global_kobject(); + if ((dbs_data->cdata->governor == GOV_CONSERVATIVE) && (policy->governor->initialized == 1)) { struct cs_ops *cs_ops = dbs_data->cdata->gov_ops; diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index fbf392aaa02e..1b5b5efa3e3a 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -71,6 +71,10 @@ struct cpufreq_governor; /* /sys/devices/system/cpu/cpufreq: entry point for global variables */ extern struct kobject *cpufreq_global_kobject; +int cpufreq_get_global_kobject(void); +void cpufreq_put_global_kobject(void); +int cpufreq_sysfs_create_file(const struct attribute *attr); +void cpufreq_sysfs_remove_file(const struct attribute *attr); #define CPUFREQ_ETERNAL (-1) struct cpufreq_cpuinfo { -- cgit v1.2.3 From 071d4990fd9fe6fe27b47f1587ba11db3111c3fd Mon Sep 17 00:00:00 2001 From: Alexander Bondar Date: Mon, 6 May 2013 13:03:59 +0300 Subject: iwlwifi: mvm: Add beacon abort enablement Beacon abort is used by device to increase idle dwell time when system is idle. This algorithm is on top of beacon filtering feature. Enable beacon abort only if power management is enabled. Signed-off-by: Alexander Bondar Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/mvm/fw-api-power.h | 9 ++ drivers/net/wireless/iwlwifi/mvm/mvm.h | 2 + drivers/net/wireless/iwlwifi/mvm/power.c | 121 +++++++++++++++--------- 3 files changed, 86 insertions(+), 46 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h index 05e51927b848..b6bdfd36bebe 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h @@ -193,4 +193,13 @@ struct iwl_beacon_filter_cmd { #define IWL_BA_ENABLE_BEACON_ABORT_DEFAULT 1 +#define IWL_BF_CMD_CONFIG_DEFAULTS \ + .bf_energy_delta = IWL_BF_ENERGY_DELTA_DEFAULT, \ + .bf_roaming_energy_delta = IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT, \ + .bf_roaming_state = IWL_BF_ROAMING_STATE_DEFAULT, \ + .bf_temperature_delta = IWL_BF_TEMPERATURE_DELTA_DEFAULT, \ + .bf_debug_flag = IWL_BF_DEBUG_FLAG_DEFAULT, \ + .bf_escape_timer = cpu_to_le32(IWL_BF_ESCAPE_TIMER_DEFAULT), \ + .ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_DEFAULT) + #endif diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index 712c39ae748f..02ba8303a09f 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -173,6 +173,8 @@ struct iwl_mvm_vif { bool uploaded; bool ap_active; bool monitor_active; + /* indicate whether beacon filtering is enabled */ + bool bf_enabled; u32 ap_beacon_time; diff --git a/drivers/net/wireless/iwlwifi/mvm/power.c b/drivers/net/wireless/iwlwifi/mvm/power.c index f5bdfb71c790..c818d6d6aee6 100644 --- a/drivers/net/wireless/iwlwifi/mvm/power.c +++ b/drivers/net/wireless/iwlwifi/mvm/power.c @@ -75,6 +75,53 @@ #define POWER_KEEP_ALIVE_PERIOD_SEC 25 +static int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm, + struct iwl_beacon_filter_cmd *cmd) +{ + int ret; + + ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_BEACON_FILTERING_CMD, CMD_SYNC, + sizeof(struct iwl_beacon_filter_cmd), cmd); + + if (!ret) { + IWL_DEBUG_POWER(mvm, "ba_enable_beacon_abort is: %d\n", + cmd->ba_enable_beacon_abort); + IWL_DEBUG_POWER(mvm, "ba_escape_timer is: %d\n", + cmd->ba_escape_timer); + IWL_DEBUG_POWER(mvm, "bf_debug_flag is: %d\n", + cmd->bf_debug_flag); + IWL_DEBUG_POWER(mvm, "bf_enable_beacon_filter is: %d\n", + cmd->bf_enable_beacon_filter); + IWL_DEBUG_POWER(mvm, "bf_energy_delta is: %d\n", + cmd->bf_energy_delta); + IWL_DEBUG_POWER(mvm, "bf_escape_timer is: %d\n", + cmd->bf_escape_timer); + IWL_DEBUG_POWER(mvm, "bf_roaming_energy_delta is: %d\n", + cmd->bf_roaming_energy_delta); + IWL_DEBUG_POWER(mvm, "bf_roaming_state is: %d\n", + cmd->bf_roaming_state); + IWL_DEBUG_POWER(mvm, "bf_temperature_delta is: %d\n", + cmd->bf_temperature_delta); + } + return ret; +} + +static int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, bool enable) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_beacon_filter_cmd cmd = { + IWL_BF_CMD_CONFIG_DEFAULTS, + .bf_enable_beacon_filter = 1, + .ba_enable_beacon_abort = enable, + }; + + if (!mvmvif->bf_enabled) + return 0; + + return iwl_mvm_beacon_filter_send_cmd(mvm, &cmd); +} + static void iwl_mvm_power_log(struct iwl_mvm *mvm, struct iwl_powertable_cmd *cmd) { @@ -162,6 +209,8 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { + int ret; + bool ba_enable; struct iwl_powertable_cmd cmd = {}; if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) @@ -170,8 +219,15 @@ int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif) iwl_mvm_power_build_cmd(mvm, vif, &cmd); iwl_mvm_power_log(mvm, &cmd); - return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC, - sizeof(cmd), &cmd); + ret = iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC, + sizeof(cmd), &cmd); + if (ret) + return ret; + + ba_enable = !!(cmd.flags & + cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)); + + return iwl_mvm_update_beacon_abort(mvm, vif, ba_enable); } int iwl_mvm_power_disable(struct iwl_mvm *mvm, struct ieee80211_vif *vif) @@ -190,69 +246,42 @@ int iwl_mvm_power_disable(struct iwl_mvm *mvm, struct ieee80211_vif *vif) sizeof(cmd), &cmd); } -static int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm, - struct iwl_beacon_filter_cmd *cmd) -{ - int ret; - - ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_BEACON_FILTERING_CMD, CMD_SYNC, - sizeof(struct iwl_beacon_filter_cmd), cmd); - - if (!ret) { - IWL_DEBUG_POWER(mvm, "ba_enable_beacon_abort is: %d\n", - cmd->ba_enable_beacon_abort); - IWL_DEBUG_POWER(mvm, "ba_escape_timer is: %d\n", - cmd->ba_escape_timer); - IWL_DEBUG_POWER(mvm, "bf_debug_flag is: %d\n", - cmd->bf_debug_flag); - IWL_DEBUG_POWER(mvm, "bf_enable_beacon_filter is: %d\n", - cmd->bf_enable_beacon_filter); - IWL_DEBUG_POWER(mvm, "bf_energy_delta is: %d\n", - cmd->bf_energy_delta); - IWL_DEBUG_POWER(mvm, "bf_escape_timer is: %d\n", - cmd->bf_escape_timer); - IWL_DEBUG_POWER(mvm, "bf_roaming_energy_delta is: %d\n", - cmd->bf_roaming_energy_delta); - IWL_DEBUG_POWER(mvm, "bf_roaming_state is: %d\n", - cmd->bf_roaming_state); - IWL_DEBUG_POWER(mvm, "bf_temperature_delta is: %d\n", - cmd->bf_temperature_delta); - } - return ret; -} - int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_beacon_filter_cmd cmd = { + IWL_BF_CMD_CONFIG_DEFAULTS, .bf_enable_beacon_filter = 1, - .bf_energy_delta = IWL_BF_ENERGY_DELTA_DEFAULT, - .bf_roaming_energy_delta = IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT, - .bf_roaming_state = IWL_BF_ROAMING_STATE_DEFAULT, - .bf_temperature_delta = IWL_BF_TEMPERATURE_DELTA_DEFAULT, - .bf_debug_flag = IWL_BF_DEBUG_FLAG_DEFAULT, - .bf_escape_timer = cpu_to_le32(IWL_BF_ESCAPE_TIMER_DEFAULT), - .ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_DEFAULT), - .ba_enable_beacon_abort = IWL_BA_ENABLE_BEACON_ABORT_DEFAULT, }; + int ret; if (mvmvif != mvm->bf_allowed_vif || vif->type != NL80211_IFTYPE_STATION || vif->p2p) return 0; - return iwl_mvm_beacon_filter_send_cmd(mvm, &cmd); + ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd); + + if (!ret) + mvmvif->bf_enabled = true; + + return ret; } int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { - struct iwl_beacon_filter_cmd cmd = { - .bf_enable_beacon_filter = 0, - }; + struct iwl_beacon_filter_cmd cmd = {}; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + int ret; if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) return 0; - return iwl_mvm_beacon_filter_send_cmd(mvm, &cmd); + ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd); + + if (!ret) + mvmvif->bf_enabled = false; + + return ret; } -- cgit v1.2.3 From f68d18f202d50f60746a8bcce1dc965b2f5035c0 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 20 May 2013 08:59:31 +0300 Subject: iwlwifi: mvm: use proper scan type for P2P It was set to be FORCED because of a firmware bug which has been fixed. Signed-off-by: Emmanuel Grumbach Reviewed-by: Ilan Peer Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/mvm/scan.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c index 2476e43799d5..2157b0f8ced5 100644 --- a/drivers/net/wireless/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/iwlwifi/mvm/scan.c @@ -298,12 +298,6 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm, else cmd->type = cpu_to_le32(SCAN_TYPE_FORCED); - /* - * TODO: This is a WA due to a bug in the FW AUX framework that does not - * properly handle time events that fail to be scheduled - */ - cmd->type = cpu_to_le32(SCAN_TYPE_FORCED); - cmd->repeats = cpu_to_le32(1); /* -- cgit v1.2.3 From 4e7dba99c9e606e304f104ce4071d8b5ba93957e Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Wed, 22 May 2013 14:59:10 +0200 Subject: netfilter: Implement RFC 1123 for FTP conntrack The FTP conntrack code currently only accepts the following format for the 227 response for PASV: 227 Entering Passive Mode (148,100,81,40,31,161). It doesn't accept the following format from an obscure server: 227 Data transfer will passively listen to 67,218,99,134,50,144 From RFC 1123: The format of the 227 reply to a PASV command is not well standardized. In particular, an FTP client cannot assume that the parentheses shown on page 40 of RFC-959 will be present (and in fact, Figure 3 on page 43 omits them). Therefore, a User-FTP program that interprets the PASV reply must scan the reply for the first digit of the host and port numbers. This patch adds support for the RFC 1123 clarification by: - Allowing a search filter to specify NUL as the terminator so that try_number will return successfully if the array of numbers has been filled when an unexpected character is encountered. - Using space as the separator for the 227 reply and then scanning for the first digit of the number sequence. The number sequence is parsed out using the existing try_rfc959 but with a NUL terminator. References: https://bugzilla.novell.com/show_bug.cgi?id=466279 References: http://bugzilla.netfilter.org/show_bug.cgi?id=574 Reported-by: Mark Post Signed-off-by: Jeff Mahoney Signed-off-by: Jiri Slaby Cc: Pablo Neira Ayuso Cc: Patrick McHardy Cc: "David S. Miller" Cc: netfilter-devel@vger.kernel.org Cc: netfilter@vger.kernel.org Cc: coreteam@netfilter.org Cc: netdev@vger.kernel.org Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conntrack_ftp.c | 73 +++++++++++++++++++++++++++++----------- 1 file changed, 54 insertions(+), 19 deletions(-) diff --git a/net/netfilter/nf_conntrack_ftp.c b/net/netfilter/nf_conntrack_ftp.c index 6b217074237b..b8a0924064ef 100644 --- a/net/netfilter/nf_conntrack_ftp.c +++ b/net/netfilter/nf_conntrack_ftp.c @@ -55,10 +55,14 @@ unsigned int (*nf_nat_ftp_hook)(struct sk_buff *skb, struct nf_conntrack_expect *exp); EXPORT_SYMBOL_GPL(nf_nat_ftp_hook); -static int try_rfc959(const char *, size_t, struct nf_conntrack_man *, char); -static int try_eprt(const char *, size_t, struct nf_conntrack_man *, char); +static int try_rfc959(const char *, size_t, struct nf_conntrack_man *, + char, unsigned int *); +static int try_rfc1123(const char *, size_t, struct nf_conntrack_man *, + char, unsigned int *); +static int try_eprt(const char *, size_t, struct nf_conntrack_man *, + char, unsigned int *); static int try_epsv_response(const char *, size_t, struct nf_conntrack_man *, - char); + char, unsigned int *); static struct ftp_search { const char *pattern; @@ -66,7 +70,7 @@ static struct ftp_search { char skip; char term; enum nf_ct_ftp_type ftptype; - int (*getnum)(const char *, size_t, struct nf_conntrack_man *, char); + int (*getnum)(const char *, size_t, struct nf_conntrack_man *, char, unsigned int *); } search[IP_CT_DIR_MAX][2] = { [IP_CT_DIR_ORIGINAL] = { { @@ -90,10 +94,8 @@ static struct ftp_search { { .pattern = "227 ", .plen = sizeof("227 ") - 1, - .skip = '(', - .term = ')', .ftptype = NF_CT_FTP_PASV, - .getnum = try_rfc959, + .getnum = try_rfc1123, }, { .pattern = "229 ", @@ -132,8 +134,9 @@ static int try_number(const char *data, size_t dlen, u_int32_t array[], i++; else { /* Unexpected character; true if it's the - terminator and we're finished. */ - if (*data == term && i == array_size - 1) + terminator (or we don't care about one) + and we're finished. */ + if ((*data == term || !term) && i == array_size - 1) return len; pr_debug("Char %u (got %u nums) `%u' unexpected\n", @@ -148,7 +151,8 @@ static int try_number(const char *data, size_t dlen, u_int32_t array[], /* Returns 0, or length of numbers: 192,168,1,1,5,6 */ static int try_rfc959(const char *data, size_t dlen, - struct nf_conntrack_man *cmd, char term) + struct nf_conntrack_man *cmd, char term, + unsigned int *offset) { int length; u_int32_t array[6]; @@ -163,6 +167,33 @@ static int try_rfc959(const char *data, size_t dlen, return length; } +/* + * From RFC 1123: + * The format of the 227 reply to a PASV command is not + * well standardized. In particular, an FTP client cannot + * assume that the parentheses shown on page 40 of RFC-959 + * will be present (and in fact, Figure 3 on page 43 omits + * them). Therefore, a User-FTP program that interprets + * the PASV reply must scan the reply for the first digit + * of the host and port numbers. + */ +static int try_rfc1123(const char *data, size_t dlen, + struct nf_conntrack_man *cmd, char term, + unsigned int *offset) +{ + int i; + for (i = 0; i < dlen; i++) + if (isdigit(data[i])) + break; + + if (i == dlen) + return 0; + + *offset += i; + + return try_rfc959(data + i, dlen - i, cmd, 0, offset); +} + /* Grab port: number up to delimiter */ static int get_port(const char *data, int start, size_t dlen, char delim, __be16 *port) @@ -191,7 +222,7 @@ static int get_port(const char *data, int start, size_t dlen, char delim, /* Returns 0, or length of numbers: |1|132.235.1.2|6275| or |2|3ffe::1|6275| */ static int try_eprt(const char *data, size_t dlen, struct nf_conntrack_man *cmd, - char term) + char term, unsigned int *offset) { char delim; int length; @@ -239,7 +270,8 @@ static int try_eprt(const char *data, size_t dlen, struct nf_conntrack_man *cmd, /* Returns 0, or length of numbers: |||6446| */ static int try_epsv_response(const char *data, size_t dlen, - struct nf_conntrack_man *cmd, char term) + struct nf_conntrack_man *cmd, char term, + unsigned int *offset) { char delim; @@ -261,9 +293,10 @@ static int find_pattern(const char *data, size_t dlen, unsigned int *numlen, struct nf_conntrack_man *cmd, int (*getnum)(const char *, size_t, - struct nf_conntrack_man *, char)) + struct nf_conntrack_man *, char, + unsigned int *)) { - size_t i; + size_t i = plen; pr_debug("find_pattern `%s': dlen = %Zu\n", pattern, dlen); if (dlen == 0) @@ -293,16 +326,18 @@ static int find_pattern(const char *data, size_t dlen, pr_debug("Pattern matches!\n"); /* Now we've found the constant string, try to skip to the 'skip' character */ - for (i = plen; data[i] != skip; i++) - if (i == dlen - 1) return -1; + if (skip) { + for (i = plen; data[i] != skip; i++) + if (i == dlen - 1) return -1; - /* Skip over the last character */ - i++; + /* Skip over the last character */ + i++; + } pr_debug("Skipped up to `%c'!\n", skip); *numoff = i; - *numlen = getnum(data + i, dlen - i, cmd, term); + *numlen = getnum(data + i, dlen - i, cmd, term, numoff); if (!*numlen) return -1; -- cgit v1.2.3 From 939d3c6a6c56d1db6ab7e44ddf11de60f0122d1a Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 27 May 2013 09:46:53 +0100 Subject: ASoC: bells: Hookup DMICs for Bells Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/samsung/bells.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/sound/soc/samsung/bells.c b/sound/soc/samsung/bells.c index ceed466af9ff..29e246803626 100644 --- a/sound/soc/samsung/bells.c +++ b/sound/soc/samsung/bells.c @@ -350,8 +350,16 @@ static struct snd_soc_codec_conf bells_codec_conf[] = { }, }; +static struct snd_soc_dapm_widget bells_widgets[] = { + SND_SOC_DAPM_MIC("DMIC", NULL), +}; + static struct snd_soc_dapm_route bells_routes[] = { { "Sub CLK_SYS", NULL, "OPCLK" }, + + { "DMIC", NULL, "MICBIAS2" }, + { "IN2L", NULL, "DMIC" }, + { "IN2R", NULL, "DMIC" }, }; static struct snd_soc_card bells_cards[] = { @@ -365,6 +373,8 @@ static struct snd_soc_card bells_cards[] = { .late_probe = bells_late_probe, + .dapm_widgets = bells_widgets, + .num_dapm_widgets = ARRAY_SIZE(bells_widgets), .dapm_routes = bells_routes, .num_dapm_routes = ARRAY_SIZE(bells_routes), @@ -383,6 +393,8 @@ static struct snd_soc_card bells_cards[] = { .late_probe = bells_late_probe, + .dapm_widgets = bells_widgets, + .num_dapm_widgets = ARRAY_SIZE(bells_widgets), .dapm_routes = bells_routes, .num_dapm_routes = ARRAY_SIZE(bells_routes), @@ -401,6 +413,8 @@ static struct snd_soc_card bells_cards[] = { .late_probe = bells_late_probe, + .dapm_widgets = bells_widgets, + .num_dapm_widgets = ARRAY_SIZE(bells_widgets), .dapm_routes = bells_routes, .num_dapm_routes = ARRAY_SIZE(bells_routes), -- cgit v1.2.3 From 2af0af67354b7c9b17d28e6ab47a8177afec125d Mon Sep 17 00:00:00 2001 From: Nikolay Balandin Date: Sat, 25 May 2013 14:50:40 -0700 Subject: regulator: lp397x: use devm_kzalloc() to make cleanup paths simpler Signed-off-by: Nikolay Balandin Signed-off-by: Mark Brown --- drivers/regulator/lp3971.c | 11 +++-------- drivers/regulator/lp3972.c | 11 +++-------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/drivers/regulator/lp3971.c b/drivers/regulator/lp3971.c index d8af9e773310..3809b4381606 100644 --- a/drivers/regulator/lp3971.c +++ b/drivers/regulator/lp3971.c @@ -434,7 +434,7 @@ static int lp3971_i2c_probe(struct i2c_client *i2c, return -ENODEV; } - lp3971 = kzalloc(sizeof(struct lp3971), GFP_KERNEL); + lp3971 = devm_kzalloc(&i2c->dev, sizeof(struct lp3971), GFP_KERNEL); if (lp3971 == NULL) return -ENOMEM; @@ -449,19 +449,15 @@ static int lp3971_i2c_probe(struct i2c_client *i2c, ret = -ENODEV; if (ret < 0) { dev_err(&i2c->dev, "failed to detect device\n"); - goto err_detect; + return ret; } ret = setup_regulators(lp3971, pdata); if (ret < 0) - goto err_detect; + return ret; i2c_set_clientdata(i2c, lp3971); return 0; - -err_detect: - kfree(lp3971); - return ret; } static int lp3971_i2c_remove(struct i2c_client *i2c) @@ -473,7 +469,6 @@ static int lp3971_i2c_remove(struct i2c_client *i2c) regulator_unregister(lp3971->rdev[i]); kfree(lp3971->rdev); - kfree(lp3971); return 0; } diff --git a/drivers/regulator/lp3972.c b/drivers/regulator/lp3972.c index 61e4cf9edf6e..573024039ca0 100644 --- a/drivers/regulator/lp3972.c +++ b/drivers/regulator/lp3972.c @@ -528,7 +528,7 @@ static int lp3972_i2c_probe(struct i2c_client *i2c, return -ENODEV; } - lp3972 = kzalloc(sizeof(struct lp3972), GFP_KERNEL); + lp3972 = devm_kzalloc(&i2c->dev, sizeof(struct lp3972), GFP_KERNEL); if (!lp3972) return -ENOMEM; @@ -546,19 +546,15 @@ static int lp3972_i2c_probe(struct i2c_client *i2c, } if (ret < 0) { dev_err(&i2c->dev, "failed to detect device. ret = %d\n", ret); - goto err_detect; + return ret; } ret = setup_regulators(lp3972, pdata); if (ret < 0) - goto err_detect; + return ret; i2c_set_clientdata(i2c, lp3972); return 0; - -err_detect: - kfree(lp3972); - return ret; } static int lp3972_i2c_remove(struct i2c_client *i2c) @@ -569,7 +565,6 @@ static int lp3972_i2c_remove(struct i2c_client *i2c) for (i = 0; i < lp3972->num_regulators; i++) regulator_unregister(lp3972->rdev[i]); kfree(lp3972->rdev); - kfree(lp3972); return 0; } -- cgit v1.2.3 From 6abb9cb99f33b20c2f32f18a3ae9cc7543e46edb Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 15 May 2013 09:30:07 +0200 Subject: cfg80211: make WoWLAN configuration available to drivers Make the current WoWLAN configuration available to drivers at runtime. This isn't really useful for the normal WoWLAN behaviour and accessing it can also be racy, but drivers may use it for testing the WoWLAN device behaviour while the host stays up & running to observe the device. Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 4 ++++ net/wireless/core.c | 4 +++- net/wireless/core.h | 21 ++++++++++---------- net/wireless/nl80211.c | 53 ++++++++++++++++++++++++++------------------------ net/wireless/sysfs.c | 8 ++++++-- 5 files changed, 52 insertions(+), 38 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 9f45d74ce3c2..b3b076a46d50 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2583,6 +2583,9 @@ struct wiphy_wowlan_support { * may request, if implemented. * * @wowlan: WoWLAN support information + * @wowlan_config: current WoWLAN configuration; this should usually not be + * used since access to it is necessarily racy, use the parameter passed + * to the suspend() operation instead. * * @ap_sme_capa: AP SME capabilities, flags from &enum nl80211_ap_sme_features. * @ht_capa_mod_mask: Specify what ht_cap values can be over-ridden. @@ -2650,6 +2653,7 @@ struct wiphy { #ifdef CONFIG_PM struct wiphy_wowlan_support wowlan; + struct cfg80211_wowlan *wowlan_config; #endif u16 max_remain_on_channel_duration; diff --git a/net/wireless/core.c b/net/wireless/core.c index ee422871fe9e..41cec1776f4f 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -663,8 +663,10 @@ void wiphy_unregister(struct wiphy *wiphy) flush_work(&rdev->event_work); cancel_delayed_work_sync(&rdev->dfs_update_channels_wk); - if (rdev->wowlan && rdev->ops->set_wakeup) +#ifdef CONFIG_PM + if (rdev->wiphy.wowlan_config && rdev->ops->set_wakeup) rdev_set_wakeup(rdev, false); +#endif cfg80211_rdev_free_wowlan(rdev); } EXPORT_SYMBOL(wiphy_unregister); diff --git a/net/wireless/core.h b/net/wireless/core.h index b4b4a566626b..a65eaf8a84c1 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -74,8 +74,6 @@ struct cfg80211_registered_device { struct work_struct conn_work; struct work_struct event_work; - struct cfg80211_wowlan *wowlan; - struct delayed_work dfs_update_channels_wk; /* netlink port which started critical protocol (0 means not started) */ @@ -96,17 +94,20 @@ struct cfg80211_registered_device *wiphy_to_dev(struct wiphy *wiphy) static inline void cfg80211_rdev_free_wowlan(struct cfg80211_registered_device *rdev) { +#ifdef CONFIG_PM int i; - if (!rdev->wowlan) + if (!rdev->wiphy.wowlan_config) return; - for (i = 0; i < rdev->wowlan->n_patterns; i++) - kfree(rdev->wowlan->patterns[i].mask); - kfree(rdev->wowlan->patterns); - if (rdev->wowlan->tcp && rdev->wowlan->tcp->sock) - sock_release(rdev->wowlan->tcp->sock); - kfree(rdev->wowlan->tcp); - kfree(rdev->wowlan); + for (i = 0; i < rdev->wiphy.wowlan_config->n_patterns; i++) + kfree(rdev->wiphy.wowlan_config->patterns[i].mask); + kfree(rdev->wiphy.wowlan_config->patterns); + if (rdev->wiphy.wowlan_config->tcp && + rdev->wiphy.wowlan_config->tcp->sock) + sock_release(rdev->wiphy.wowlan_config->tcp->sock); + kfree(rdev->wiphy.wowlan_config->tcp); + kfree(rdev->wiphy.wowlan_config); +#endif } extern struct workqueue_struct *cfg80211_wq; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a09f36bb957c..fb6abcb359a1 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -7489,28 +7489,29 @@ static int nl80211_leave_mesh(struct sk_buff *skb, struct genl_info *info) static int nl80211_send_wowlan_patterns(struct sk_buff *msg, struct cfg80211_registered_device *rdev) { + struct cfg80211_wowlan *wowlan = rdev->wiphy.wowlan_config; struct nlattr *nl_pats, *nl_pat; int i, pat_len; - if (!rdev->wowlan->n_patterns) + if (!wowlan->n_patterns) return 0; nl_pats = nla_nest_start(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN); if (!nl_pats) return -ENOBUFS; - for (i = 0; i < rdev->wowlan->n_patterns; i++) { + for (i = 0; i < wowlan->n_patterns; i++) { nl_pat = nla_nest_start(msg, i + 1); if (!nl_pat) return -ENOBUFS; - pat_len = rdev->wowlan->patterns[i].pattern_len; + pat_len = wowlan->patterns[i].pattern_len; if (nla_put(msg, NL80211_WOWLAN_PKTPAT_MASK, DIV_ROUND_UP(pat_len, 8), - rdev->wowlan->patterns[i].mask) || + wowlan->patterns[i].mask) || nla_put(msg, NL80211_WOWLAN_PKTPAT_PATTERN, - pat_len, rdev->wowlan->patterns[i].pattern) || + pat_len, wowlan->patterns[i].pattern) || nla_put_u32(msg, NL80211_WOWLAN_PKTPAT_OFFSET, - rdev->wowlan->patterns[i].pkt_offset)) + wowlan->patterns[i].pkt_offset)) return -ENOBUFS; nla_nest_end(msg, nl_pat); } @@ -7573,12 +7574,12 @@ static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info) !rdev->wiphy.wowlan.tcp) return -EOPNOTSUPP; - if (rdev->wowlan && rdev->wowlan->tcp) { + if (rdev->wiphy.wowlan_config && rdev->wiphy.wowlan_config->tcp) { /* adjust size to have room for all the data */ - size += rdev->wowlan->tcp->tokens_size + - rdev->wowlan->tcp->payload_len + - rdev->wowlan->tcp->wake_len + - rdev->wowlan->tcp->wake_len / 8; + size += rdev->wiphy.wowlan_config->tcp->tokens_size + + rdev->wiphy.wowlan_config->tcp->payload_len + + rdev->wiphy.wowlan_config->tcp->wake_len + + rdev->wiphy.wowlan_config->tcp->wake_len / 8; } msg = nlmsg_new(size, GFP_KERNEL); @@ -7590,33 +7591,34 @@ static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info) if (!hdr) goto nla_put_failure; - if (rdev->wowlan) { + if (rdev->wiphy.wowlan_config) { struct nlattr *nl_wowlan; nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS); if (!nl_wowlan) goto nla_put_failure; - if ((rdev->wowlan->any && + if ((rdev->wiphy.wowlan_config->any && nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) || - (rdev->wowlan->disconnect && + (rdev->wiphy.wowlan_config->disconnect && nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) || - (rdev->wowlan->magic_pkt && + (rdev->wiphy.wowlan_config->magic_pkt && nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) || - (rdev->wowlan->gtk_rekey_failure && + (rdev->wiphy.wowlan_config->gtk_rekey_failure && nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) || - (rdev->wowlan->eap_identity_req && + (rdev->wiphy.wowlan_config->eap_identity_req && nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) || - (rdev->wowlan->four_way_handshake && + (rdev->wiphy.wowlan_config->four_way_handshake && nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) || - (rdev->wowlan->rfkill_release && + (rdev->wiphy.wowlan_config->rfkill_release && nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE))) goto nla_put_failure; if (nl80211_send_wowlan_patterns(msg, rdev)) goto nla_put_failure; - if (nl80211_send_wowlan_tcp(msg, rdev->wowlan->tcp)) + if (nl80211_send_wowlan_tcp(msg, + rdev->wiphy.wowlan_config->tcp)) goto nla_put_failure; nla_nest_end(msg, nl_wowlan); @@ -7783,7 +7785,7 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) struct cfg80211_wowlan *ntrig; struct wiphy_wowlan_support *wowlan = &rdev->wiphy.wowlan; int err, i; - bool prev_enabled = rdev->wowlan; + bool prev_enabled = rdev->wiphy.wowlan_config; if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns && !rdev->wiphy.wowlan.tcp) @@ -7791,7 +7793,7 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) { cfg80211_rdev_free_wowlan(rdev); - rdev->wowlan = NULL; + rdev->wiphy.wowlan_config = NULL; goto set_wakeup; } @@ -7927,11 +7929,12 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) goto error; } cfg80211_rdev_free_wowlan(rdev); - rdev->wowlan = ntrig; + rdev->wiphy.wowlan_config = ntrig; set_wakeup: - if (rdev->ops->set_wakeup && prev_enabled != !!rdev->wowlan) - rdev_set_wakeup(rdev, rdev->wowlan); + if (rdev->ops->set_wakeup && + prev_enabled != !!rdev->wiphy.wowlan_config) + rdev_set_wakeup(rdev, rdev->wiphy.wowlan_config); return 0; error: diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c index 8f28b9f798d8..360a42c6f694 100644 --- a/net/wireless/sysfs.c +++ b/net/wireless/sysfs.c @@ -91,6 +91,7 @@ static void cfg80211_leave_all(struct cfg80211_registered_device *rdev) cfg80211_leave(rdev, wdev); } +#ifdef CONFIG_PM static int wiphy_suspend(struct device *dev, pm_message_t state) { struct cfg80211_registered_device *rdev = dev_to_rdev(dev); @@ -100,10 +101,10 @@ static int wiphy_suspend(struct device *dev, pm_message_t state) rtnl_lock(); if (rdev->wiphy.registered) { - if (!rdev->wowlan) + if (!rdev->wiphy.wowlan_config) cfg80211_leave_all(rdev); if (rdev->ops->suspend) - ret = rdev_suspend(rdev, rdev->wowlan); + ret = rdev_suspend(rdev, rdev->wiphy.wowlan_config); if (ret == 1) { /* Driver refuse to configure wowlan */ cfg80211_leave_all(rdev); @@ -132,6 +133,7 @@ static int wiphy_resume(struct device *dev) return ret; } +#endif static const void *wiphy_namespace(struct device *d) { @@ -146,8 +148,10 @@ struct class ieee80211_class = { .dev_release = wiphy_dev_release, .dev_attrs = ieee80211_dev_attrs, .dev_uevent = wiphy_uevent, +#ifdef CONFIG_PM .suspend = wiphy_suspend, .resume = wiphy_resume, +#endif .ns_type = &net_ns_type_operations, .namespace = wiphy_namespace, }; -- cgit v1.2.3 From aa3495f7922a51c893b0e4a376a225f02da74c76 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 6 May 2013 12:50:36 +0200 Subject: MAINTAINERS: Update PWM subsystem entry Change email to my private address and use the brand new linux-pwm instead of the generic linux-kernel mailing list. Signed-off-by: Thierry Reding --- MAINTAINERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 829c0321108b..3200369af150 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6458,8 +6458,8 @@ S: Maintained F: drivers/media/usb/pwc/* PWM SUBSYSTEM -M: Thierry Reding -L: linux-kernel@vger.kernel.org +M: Thierry Reding +L: linux-pwm@vger.kernel.org S: Maintained W: http://gitorious.org/linux-pwm T: git git://gitorious.org/linux-pwm/linux-pwm.git -- cgit v1.2.3 From e7927141ed51d94d6e1267f2ff0362a1f255e007 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 6 May 2013 15:05:54 -0300 Subject: pwm: mxs: Let device core handle pinctrl Since commit ab78029 (drivers/pinctrl: grab default handles from device core), we can rely on device core for handling pinctrl. So remove devm_pinctrl_get_select_default() from the driver. Cc: Thierry Reding Cc: Signed-off-by: Fabio Estevam Tested-by: Shawn Guo Signed-off-by: Thierry Reding --- drivers/pwm/pwm-mxs.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/pwm/pwm-mxs.c b/drivers/pwm/pwm-mxs.c index 3febdddf71f9..6489a4b75943 100644 --- a/drivers/pwm/pwm-mxs.c +++ b/drivers/pwm/pwm-mxs.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -130,7 +129,6 @@ static int mxs_pwm_probe(struct platform_device *pdev) struct device_node *np = pdev->dev.of_node; struct mxs_pwm_chip *mxs; struct resource *res; - struct pinctrl *pinctrl; int ret; mxs = devm_kzalloc(&pdev->dev, sizeof(*mxs), GFP_KERNEL); @@ -142,10 +140,6 @@ static int mxs_pwm_probe(struct platform_device *pdev) if (IS_ERR(mxs->base)) return PTR_ERR(mxs->base); - pinctrl = devm_pinctrl_get_select_default(&pdev->dev); - if (IS_ERR(pinctrl)) - return PTR_ERR(pinctrl); - mxs->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(mxs->clk)) return PTR_ERR(mxs->clk); -- cgit v1.2.3 From e2fa3d799b7471f00adae59ff36260ad8237929f Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Mon, 27 May 2013 21:39:57 +0200 Subject: tpm: fix regression caused by section type conflict of tpm_dev_release() in ppc builds The 8119807 commit reintroduced a regression (error: __ksymtab_tpm_dev_release causes a section type conflict) that was fixed by commit cbb2ed4. Fix it for good by adding the prototype to tpm.h so sparse doesn't complain about it anymore. Reported-by: Tony Camuso Signed-off-by: Peter Huewe --- drivers/char/tpm/tpm.c | 2 +- drivers/char/tpm/tpm.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 7c3b3dcbfbc8..e3c974a6c522 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -1472,7 +1472,7 @@ EXPORT_SYMBOL_GPL(tpm_dev_vendor_release); * Once all references to platform device are down to 0, * release all allocated structures. */ -static void tpm_dev_release(struct device *dev) +void tpm_dev_release(struct device *dev) { struct tpm_chip *chip = dev_get_drvdata(dev); diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 433423288709..a7bfc176ed43 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -332,6 +332,7 @@ extern struct tpm_chip* tpm_register_hardware(struct device *, const struct tpm_vendor_specific *); extern int tpm_open(struct inode *, struct file *); extern int tpm_release(struct inode *, struct file *); +extern void tpm_dev_release(struct device *dev); extern void tpm_dev_vendor_release(struct tpm_chip *); extern ssize_t tpm_write(struct file *, const char __user *, size_t, loff_t *); -- cgit v1.2.3 From fe830ef62ac6d8814e27b7e2f632848694b0e5c7 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Sat, 25 May 2013 21:48:29 +0800 Subject: PCI: Introduce pci_bus_{get|put}() to manage PCI bus reference count Introduce helper functions pci_bus_{get|put}() to manage PCI bus reference count. Signed-off-by: Jiang Liu Signed-off-by: Yijing Wang Signed-off-by: Gu Zheng Signed-off-by: Bjorn Helgaas --- drivers/pci/bus.c | 15 +++++++++++++++ include/linux/pci.h | 2 ++ 2 files changed, 17 insertions(+) diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 32e66a6f12d9..b1ff02ab4f13 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -283,6 +283,21 @@ void pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *), } EXPORT_SYMBOL_GPL(pci_walk_bus); +struct pci_bus *pci_bus_get(struct pci_bus *bus) +{ + if (bus) + get_device(&bus->dev); + return bus; +} +EXPORT_SYMBOL(pci_bus_get); + +void pci_bus_put(struct pci_bus *bus) +{ + if (bus) + put_device(&bus->dev); +} +EXPORT_SYMBOL(pci_bus_put); + EXPORT_SYMBOL(pci_bus_alloc_resource); EXPORT_SYMBOL_GPL(pci_bus_add_device); EXPORT_SYMBOL(pci_bus_add_devices); diff --git a/include/linux/pci.h b/include/linux/pci.h index 3a24e4ff3248..7556c590ddfd 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1018,6 +1018,8 @@ int pci_request_selected_regions_exclusive(struct pci_dev *, int, const char *); void pci_release_selected_regions(struct pci_dev *, int); /* drivers/pci/bus.c */ +struct pci_bus *pci_bus_get(struct pci_bus *bus); +void pci_bus_put(struct pci_bus *bus); void pci_add_resource(struct list_head *resources, struct resource *res); void pci_add_resource_offset(struct list_head *resources, struct resource *res, resource_size_t offset); -- cgit v1.2.3 From 3c6e6ae770f338ef3e54c5823c21063204f53537 Mon Sep 17 00:00:00 2001 From: Gu Zheng Date: Sat, 25 May 2013 21:48:30 +0800 Subject: PCI: Introduce pci_alloc_dev(struct pci_bus*) to replace alloc_pci_dev() Here we introduce a new interface to replace alloc_pci_dev(): struct pci_dev *pci_alloc_dev(struct pci_bus *bus) It takes a "struct pci_bus *" argument, so we can alloc a PCI device on a target PCI bus, and it acquires a reference on the pci_bus. We use pci_alloc_dev(NULL) to simplify the old alloc_pci_dev(), and keep it for a while but mark it as __deprecated. Holding a reference to the pci_bus ensures that referencing pci_dev->bus is valid as long as the pci_dev is valid. [bhelgaas: keep existing "return error early" structure in pci_alloc_dev()] Signed-off-by: Gu Zheng Signed-off-by: Jiang Liu Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 9 ++++++++- include/linux/pci.h | 3 ++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 70f10fa3c1b2..d47ce1400c26 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1200,7 +1200,7 @@ static void pci_release_bus_bridge_dev(struct device *dev) kfree(bridge); } -struct pci_dev *alloc_pci_dev(void) +struct pci_dev *pci_alloc_dev(struct pci_bus *bus) { struct pci_dev *dev; @@ -1210,9 +1210,16 @@ struct pci_dev *alloc_pci_dev(void) INIT_LIST_HEAD(&dev->bus_list); dev->dev.type = &pci_dev_type; + dev->bus = pci_bus_get(bus); return dev; } +EXPORT_SYMBOL(pci_alloc_dev); + +struct pci_dev *alloc_pci_dev(void) +{ + return pci_alloc_dev(NULL); +} EXPORT_SYMBOL(alloc_pci_dev); bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l, diff --git a/include/linux/pci.h b/include/linux/pci.h index 7556c590ddfd..b0f4a8264118 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -364,7 +364,8 @@ static inline struct pci_dev *pci_physfn(struct pci_dev *dev) return dev; } -struct pci_dev *alloc_pci_dev(void); +struct pci_dev *pci_alloc_dev(struct pci_bus *bus); +struct pci_dev * __deprecated alloc_pci_dev(void); #define to_pci_dev(n) container_of(n, struct pci_dev, dev) #define for_each_pci_dev(d) while ((d = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, d)) != NULL) -- cgit v1.2.3 From 54a582382d997ec142a4700f9171cc28e3a9b8f6 Mon Sep 17 00:00:00 2001 From: Libo Chen Date: Mon, 27 May 2013 10:28:53 +0800 Subject: PCI: Convert ioapic.c to module_pci_driver Use module_pci_driver instead of init/exit, make code clean. Signed-off-by: Libo Chen Signed-off-by: Bjorn Helgaas --- drivers/pci/ioapic.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/pci/ioapic.c b/drivers/pci/ioapic.c index 3c6bbdd059a4..1b90579b233a 100644 --- a/drivers/pci/ioapic.c +++ b/drivers/pci/ioapic.c @@ -113,17 +113,6 @@ static struct pci_driver ioapic_driver = { .remove = ioapic_remove, }; -static int __init ioapic_init(void) -{ - return pci_register_driver(&ioapic_driver); -} - -static void __exit ioapic_exit(void) -{ - pci_unregister_driver(&ioapic_driver); -} - -module_init(ioapic_init); -module_exit(ioapic_exit); +module_pci_driver(ioapic_driver); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 333279c82b984f3eac61feff2b76a8b79e3db6c8 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Tue, 28 May 2013 02:43:23 +0400 Subject: sata_rcar: kill superfluous code in sata_rcar_bmdma_fill_sg() I've modified sata_rcar_bmdma_fill_sg() to take care of splitting long scatter/ gather segments due to the descriptor table transfer counter being only 28 bits wide (bit 1 to bit 28) but that was in vain as even if 'sata_rcar_sht' specified a correct 'dma_boundary' field, the DMA and block layers would have split the S/G segments on the necassary boundaries. Since the driver uses ATA_BMDMA_SHT() to initilaize 'sata_rcar_sht', the boundary is much smaller, only 0xFFFF, so the code I've added is even more useless, and it's better to just remove it. Signed-off-by: Sergei Shtylyov Signed-off-by: Tejun Heo --- drivers/ata/sata_rcar.c | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/drivers/ata/sata_rcar.c b/drivers/ata/sata_rcar.c index 889c25a6337e..629b07c452c5 100644 --- a/drivers/ata/sata_rcar.c +++ b/drivers/ata/sata_rcar.c @@ -474,11 +474,10 @@ static void sata_rcar_bmdma_fill_sg(struct ata_queued_cmd *qc) struct ata_port *ap = qc->ap; struct ata_bmdma_prd *prd = ap->bmdma_prd; struct scatterlist *sg; - unsigned int si, pi; + unsigned int si; - pi = 0; for_each_sg(qc->sg, sg, qc->n_elem, si) { - u32 addr, sg_len, len; + u32 addr, sg_len; /* * Note: h/w doesn't support 64-bit, so we unconditionally @@ -487,24 +486,13 @@ static void sata_rcar_bmdma_fill_sg(struct ata_queued_cmd *qc) addr = (u32)sg_dma_address(sg); sg_len = sg_dma_len(sg); - /* H/w transfer count is only 29 bits long, let's be careful */ - while (sg_len) { - len = sg_len; - if (len > 0x1ffffffe) - len = 0x1ffffffe; - - prd[pi].addr = cpu_to_le32(addr); - prd[pi].flags_len = cpu_to_le32(len); - VPRINTK("PRD[%u] = (0x%X, 0x%X)\n", pi, addr, len); - - pi++; - sg_len -= len; - addr += len; - } + prd[si].addr = cpu_to_le32(addr); + prd[si].flags_len = cpu_to_le32(sg_len); + VPRINTK("PRD[%u] = (0x%X, 0x%X)\n", si, addr, sg_len); } /* end-of-table flag */ - prd[pi - 1].addr |= cpu_to_le32(SATA_RCAR_DTEND); + prd[si - 1].addr |= cpu_to_le32(SATA_RCAR_DTEND); } static void sata_rcar_qc_prep(struct ata_queued_cmd *qc) -- cgit v1.2.3 From 8bfbeed58665dbbf63813017712bd0c8e978379e Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Tue, 28 May 2013 02:45:08 +0400 Subject: sata_rcar: correct 'sata_rcar_sht' Using ATA_BMDMA_SHT() to intialize 'sata_rcar_sht' was suboptimal as the R-Car descriptor table transfer counter is 28 bits wide (bit 1 to bit 28), so that the 'dma_boundary' field of 0xFFFF is just too small, as well as the 'sg_tablesize' field of 128. Use ATA_BASE_SHT() to initialize 'sata_rcar_sht' instead and give proper values to the 'dma_boundary' and 'sg_tablesize' fields explicitly. Signed-off-by: Sergei Shtylyov Signed-off-by: Tejun Heo --- drivers/ata/sata_rcar.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/ata/sata_rcar.c b/drivers/ata/sata_rcar.c index 629b07c452c5..178165be0c32 100644 --- a/drivers/ata/sata_rcar.c +++ b/drivers/ata/sata_rcar.c @@ -121,6 +121,8 @@ /* Descriptor table word 0 bit (when DTA32M = 1) */ #define SATA_RCAR_DTEND BIT(0) +#define SATA_RCAR_DMA_BOUNDARY 0x1FFFFFFEUL + struct sata_rcar_priv { void __iomem *base; struct clk *clk; @@ -575,7 +577,14 @@ static u8 sata_rcar_bmdma_status(struct ata_port *ap) } static struct scsi_host_template sata_rcar_sht = { - ATA_BMDMA_SHT(DRV_NAME), + ATA_BASE_SHT(DRV_NAME), + /* + * This controller allows transfer chunks up to 512MB which cross 64KB + * boundaries, therefore the DMA limits are more relaxed than standard + * ATA SFF. + */ + .sg_tablesize = ATA_MAX_PRD, + .dma_boundary = SATA_RCAR_DMA_BOUNDARY, }; static struct ata_port_operations sata_rcar_port_ops = { -- cgit v1.2.3 From 1b20f6a9adaa4b88d520d279c3d605f65b898625 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Tue, 28 May 2013 02:46:41 +0400 Subject: sata_rcar: add 'base' local variable to some functions The 'base' field of 'struct sata_rcar_priv' is used very often throughout the driver, so it seems worth loading it into a local variable if it's used more than once in a function. While at it, put some unitialized variables after intialized ones for aesthetic reasons. :-) Signed-off-by: Sergei Shtylyov Signed-off-by: Tejun Heo --- drivers/ata/sata_rcar.c | 102 +++++++++++++++++++++++++++--------------------- 1 file changed, 57 insertions(+), 45 deletions(-) diff --git a/drivers/ata/sata_rcar.c b/drivers/ata/sata_rcar.c index 178165be0c32..f0d3e43570ee 100644 --- a/drivers/ata/sata_rcar.c +++ b/drivers/ata/sata_rcar.c @@ -130,41 +130,44 @@ struct sata_rcar_priv { static void sata_rcar_phy_initialize(struct sata_rcar_priv *priv) { + void __iomem *base = priv->base; + /* idle state */ - iowrite32(0, priv->base + SATAPHYADDR_REG); + iowrite32(0, base + SATAPHYADDR_REG); /* reset */ - iowrite32(SATAPHYRESET_PHYRST, priv->base + SATAPHYRESET_REG); + iowrite32(SATAPHYRESET_PHYRST, base + SATAPHYRESET_REG); udelay(10); /* deassert reset */ - iowrite32(0, priv->base + SATAPHYRESET_REG); + iowrite32(0, base + SATAPHYRESET_REG); } static void sata_rcar_phy_write(struct sata_rcar_priv *priv, u16 reg, u32 val, int group) { + void __iomem *base = priv->base; int timeout; /* deassert reset */ - iowrite32(0, priv->base + SATAPHYRESET_REG); + iowrite32(0, base + SATAPHYRESET_REG); /* lane 1 */ - iowrite32(SATAPHYACCEN_PHYLANE, priv->base + SATAPHYACCEN_REG); + iowrite32(SATAPHYACCEN_PHYLANE, base + SATAPHYACCEN_REG); /* write phy register value */ - iowrite32(val, priv->base + SATAPHYWDATA_REG); + iowrite32(val, base + SATAPHYWDATA_REG); /* set register group */ if (group) reg |= SATAPHYADDR_PHYRATEMODE; /* write command */ - iowrite32(SATAPHYADDR_PHYCMD_WRITE | reg, priv->base + SATAPHYADDR_REG); + iowrite32(SATAPHYADDR_PHYCMD_WRITE | reg, base + SATAPHYADDR_REG); /* wait for ack */ for (timeout = 0; timeout < 100; timeout++) { - val = ioread32(priv->base + SATAPHYACK_REG); + val = ioread32(base + SATAPHYACK_REG); if (val & SATAPHYACK_PHYACK) break; } if (timeout >= 100) pr_err("%s timeout\n", __func__); /* idle state */ - iowrite32(0, priv->base + SATAPHYADDR_REG); + iowrite32(0, base + SATAPHYADDR_REG); } static void sata_rcar_freeze(struct ata_port *ap) @@ -180,14 +183,15 @@ static void sata_rcar_freeze(struct ata_port *ap) static void sata_rcar_thaw(struct ata_port *ap) { struct sata_rcar_priv *priv = ap->host->private_data; + void __iomem *base = priv->base; /* ack */ - iowrite32(~SATA_RCAR_INT_MASK, priv->base + SATAINTSTAT_REG); + iowrite32(~SATA_RCAR_INT_MASK, base + SATAINTSTAT_REG); ata_sff_thaw(ap); /* unmask */ - iowrite32(0x7ff & ~SATA_RCAR_INT_MASK, priv->base + SATAINTMASK_REG); + iowrite32(0x7ff & ~SATA_RCAR_INT_MASK, base + SATAINTMASK_REG); } static void sata_rcar_ioread16_rep(void __iomem *reg, void *buffer, int count) @@ -509,15 +513,16 @@ static void sata_rcar_bmdma_setup(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; unsigned int rw = qc->tf.flags & ATA_TFLAG_WRITE; - u32 dmactl; struct sata_rcar_priv *priv = ap->host->private_data; + void __iomem *base = priv->base; + u32 dmactl; /* load PRD table addr. */ mb(); /* make sure PRD table writes are visible to controller */ - iowrite32(ap->bmdma_prd_dma, priv->base + ATAPI_DTB_ADR_REG); + iowrite32(ap->bmdma_prd_dma, base + ATAPI_DTB_ADR_REG); /* specify data direction, triple-check start bit is clear */ - dmactl = ioread32(priv->base + ATAPI_CONTROL1_REG); + dmactl = ioread32(base + ATAPI_CONTROL1_REG); dmactl &= ~(ATAPI_CONTROL1_RW | ATAPI_CONTROL1_STOP); if (dmactl & ATAPI_CONTROL1_START) { dmactl &= ~ATAPI_CONTROL1_START; @@ -525,7 +530,7 @@ static void sata_rcar_bmdma_setup(struct ata_queued_cmd *qc) } if (!rw) dmactl |= ATAPI_CONTROL1_RW; - iowrite32(dmactl, priv->base + ATAPI_CONTROL1_REG); + iowrite32(dmactl, base + ATAPI_CONTROL1_REG); /* issue r/w command */ ap->ops->sff_exec_command(ap, &qc->tf); @@ -534,27 +539,29 @@ static void sata_rcar_bmdma_setup(struct ata_queued_cmd *qc) static void sata_rcar_bmdma_start(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; - u32 dmactl; struct sata_rcar_priv *priv = ap->host->private_data; + void __iomem *base = priv->base; + u32 dmactl; /* start host DMA transaction */ - dmactl = ioread32(priv->base + ATAPI_CONTROL1_REG); + dmactl = ioread32(base + ATAPI_CONTROL1_REG); dmactl |= ATAPI_CONTROL1_START; - iowrite32(dmactl, priv->base + ATAPI_CONTROL1_REG); + iowrite32(dmactl, base + ATAPI_CONTROL1_REG); } static void sata_rcar_bmdma_stop(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; struct sata_rcar_priv *priv = ap->host->private_data; + void __iomem *base = priv->base; u32 dmactl; /* force termination of DMA transfer if active */ - dmactl = ioread32(priv->base + ATAPI_CONTROL1_REG); + dmactl = ioread32(base + ATAPI_CONTROL1_REG); if (dmactl & ATAPI_CONTROL1_START) { dmactl &= ~ATAPI_CONTROL1_START; dmactl |= ATAPI_CONTROL1_STOP; - iowrite32(dmactl, priv->base + ATAPI_CONTROL1_REG); + iowrite32(dmactl, base + ATAPI_CONTROL1_REG); } /* one-PIO-cycle guaranteed wait, per spec, for HDMA1:0 transition */ @@ -564,8 +571,8 @@ static void sata_rcar_bmdma_stop(struct ata_queued_cmd *qc) static u8 sata_rcar_bmdma_status(struct ata_port *ap) { struct sata_rcar_priv *priv = ap->host->private_data; - u32 status; u8 host_stat = 0; + u32 status; status = ioread32(priv->base + ATAPI_STATUS_REG); if (status & ATAPI_STATUS_DEVINT) @@ -666,19 +673,19 @@ static irqreturn_t sata_rcar_interrupt(int irq, void *dev_instance) { struct ata_host *host = dev_instance; struct sata_rcar_priv *priv = host->private_data; - struct ata_port *ap; + void __iomem *base = priv->base; unsigned int handled = 0; + struct ata_port *ap; u32 sataintstat; unsigned long flags; spin_lock_irqsave(&host->lock, flags); - sataintstat = ioread32(priv->base + SATAINTSTAT_REG); + sataintstat = ioread32(base + SATAINTSTAT_REG); if (!sataintstat) goto done; /* ack */ - iowrite32(sataintstat & ~SATA_RCAR_INT_MASK, - priv->base + SATAINTSTAT_REG); + iowrite32(sataintstat & ~SATA_RCAR_INT_MASK, base + SATAINTSTAT_REG); ap = host->ports[0]; @@ -699,15 +706,16 @@ static void sata_rcar_setup_port(struct ata_host *host) struct ata_port *ap = host->ports[0]; struct ata_ioports *ioaddr = &ap->ioaddr; struct sata_rcar_priv *priv = host->private_data; + void __iomem *base = priv->base; ap->ops = &sata_rcar_port_ops; ap->pio_mask = ATA_PIO4; ap->udma_mask = ATA_UDMA6; ap->flags |= ATA_FLAG_SATA; - ioaddr->cmd_addr = priv->base + SDATA_REG; - ioaddr->ctl_addr = priv->base + SSDEVCON_REG; - ioaddr->scr_addr = priv->base + SCRSSTS_REG; + ioaddr->cmd_addr = base + SDATA_REG; + ioaddr->ctl_addr = base + SSDEVCON_REG; + ioaddr->scr_addr = base + SCRSSTS_REG; ioaddr->altstatus_addr = ioaddr->ctl_addr; ioaddr->data_addr = ioaddr->cmd_addr + (ATA_REG_DATA << 2); @@ -725,6 +733,7 @@ static void sata_rcar_setup_port(struct ata_host *host) static void sata_rcar_init_controller(struct ata_host *host) { struct sata_rcar_priv *priv = host->private_data; + void __iomem *base = priv->base; u32 val; /* reset and setup phy */ @@ -737,27 +746,27 @@ static void sata_rcar_init_controller(struct ata_host *host) sata_rcar_phy_write(priv, SATAPCTLR4_REG, 0x28E80000, 0); /* SATA-IP reset state */ - val = ioread32(priv->base + ATAPI_CONTROL1_REG); + val = ioread32(base + ATAPI_CONTROL1_REG); val |= ATAPI_CONTROL1_RESET; - iowrite32(val, priv->base + ATAPI_CONTROL1_REG); + iowrite32(val, base + ATAPI_CONTROL1_REG); /* ISM mode, PRD mode, DTEND flag at bit 0 */ - val = ioread32(priv->base + ATAPI_CONTROL1_REG); + val = ioread32(base + ATAPI_CONTROL1_REG); val |= ATAPI_CONTROL1_ISM; val |= ATAPI_CONTROL1_DESE; val |= ATAPI_CONTROL1_DTA32M; - iowrite32(val, priv->base + ATAPI_CONTROL1_REG); + iowrite32(val, base + ATAPI_CONTROL1_REG); /* Release the SATA-IP from the reset state */ - val = ioread32(priv->base + ATAPI_CONTROL1_REG); + val = ioread32(base + ATAPI_CONTROL1_REG); val &= ~ATAPI_CONTROL1_RESET; - iowrite32(val, priv->base + ATAPI_CONTROL1_REG); + iowrite32(val, base + ATAPI_CONTROL1_REG); /* ack and mask */ - iowrite32(0, priv->base + SATAINTSTAT_REG); - iowrite32(0x7ff, priv->base + SATAINTMASK_REG); + iowrite32(0, base + SATAINTSTAT_REG); + iowrite32(0x7ff, base + SATAINTMASK_REG); /* enable interrupts */ - iowrite32(ATAPI_INT_ENABLE_SATAINT, priv->base + ATAPI_INT_ENABLE_REG); + iowrite32(ATAPI_INT_ENABLE_SATAINT, base + ATAPI_INT_ENABLE_REG); } static int sata_rcar_probe(struct platform_device *pdev) @@ -824,14 +833,15 @@ static int sata_rcar_remove(struct platform_device *pdev) { struct ata_host *host = platform_get_drvdata(pdev); struct sata_rcar_priv *priv = host->private_data; + void __iomem *base = priv->base; ata_host_detach(host); /* disable interrupts */ - iowrite32(0, priv->base + ATAPI_INT_ENABLE_REG); + iowrite32(0, base + ATAPI_INT_ENABLE_REG); /* ack and mask */ - iowrite32(0, priv->base + SATAINTSTAT_REG); - iowrite32(0x7ff, priv->base + SATAINTMASK_REG); + iowrite32(0, base + SATAINTSTAT_REG); + iowrite32(0x7ff, base + SATAINTMASK_REG); clk_disable(priv->clk); @@ -843,14 +853,15 @@ static int sata_rcar_suspend(struct device *dev) { struct ata_host *host = dev_get_drvdata(dev); struct sata_rcar_priv *priv = host->private_data; + void __iomem *base = priv->base; int ret; ret = ata_host_suspend(host, PMSG_SUSPEND); if (!ret) { /* disable interrupts */ - iowrite32(0, priv->base + ATAPI_INT_ENABLE_REG); + iowrite32(0, base + ATAPI_INT_ENABLE_REG); /* mask */ - iowrite32(0x7ff, priv->base + SATAINTMASK_REG); + iowrite32(0x7ff, base + SATAINTMASK_REG); clk_disable(priv->clk); } @@ -862,14 +873,15 @@ static int sata_rcar_resume(struct device *dev) { struct ata_host *host = dev_get_drvdata(dev); struct sata_rcar_priv *priv = host->private_data; + void __iomem *base = priv->base; clk_enable(priv->clk); /* ack and mask */ - iowrite32(0, priv->base + SATAINTSTAT_REG); - iowrite32(0x7ff, priv->base + SATAINTMASK_REG); + iowrite32(0, base + SATAINTSTAT_REG); + iowrite32(0x7ff, base + SATAINTMASK_REG); /* enable interrupts */ - iowrite32(ATAPI_INT_ENABLE_SATAINT, priv->base + ATAPI_INT_ENABLE_REG); + iowrite32(ATAPI_INT_ENABLE_SATAINT, base + ATAPI_INT_ENABLE_REG); ata_host_resume(host); -- cgit v1.2.3 From 3db46c939677e32e311d354b619fd552ceafd123 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 14 May 2013 23:00:32 +0000 Subject: thermal: rcar: Fix typo in probe information message Signed-off-by: Laurent Pinchart Acked-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/rcar_thermal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 8d7edd4c8228..3eaca06df617 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -458,7 +458,7 @@ static int rcar_thermal_probe(struct platform_device *pdev) platform_set_drvdata(pdev, common); - dev_info(dev, "%d sensor proved\n", i); + dev_info(dev, "%d sensor probed\n", i); return 0; -- cgit v1.2.3 From 42a5bf507d7f7ecbf606eb392dd9d2e4d009c36b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 17 May 2013 11:52:02 +0000 Subject: thermal: cut the spaces when user sets policy Setting policy results in invalid value error. % echo "step_wise" > policy % echo: write error: Invalid argument Need clean up of the buffer which "echo" may add based on the arguments, before comparing aganist list of governor names. Signed-off-by: Andy Shevchenko Reported-by: Srinivas Pandruvada Acked-by: Eduardo Valentin Tested-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index d755440791b7..1067fb0107b9 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -713,10 +714,13 @@ policy_store(struct device *dev, struct device_attribute *attr, int ret = -EINVAL; struct thermal_zone_device *tz = to_thermal_zone(dev); struct thermal_governor *gov; + char name[THERMAL_NAME_LENGTH]; + + snprintf(name, sizeof(name), "%s", buf); mutex_lock(&thermal_governor_lock); - gov = __find_governor(buf); + gov = __find_governor(strim(name)); if (!gov) goto exit; -- cgit v1.2.3 From 83720d0b79618cd07c955ef1204c9cb0acb614a5 Mon Sep 17 00:00:00 2001 From: Jonghwa Lee Date: Sat, 18 May 2013 09:50:26 +0000 Subject: Thermal: core: Ask .get_trip_temp() to register thermal zone device. This patch adds a requirement needing .get_trip_temp() callback function for registering thermal zone device. This function is used when thermal zone is updated and essential where thermal core handles thermal trip based only polling way not hw interrupt. Signed-off-by: Jonghwa Lee Signed-off-by: MyungJoo Ham Acked-by: Durgadoss R Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 1067fb0107b9..e56ded5984b5 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -1628,7 +1628,7 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, if (!ops || !ops->get_temp) return ERR_PTR(-EINVAL); - if (trips > 0 && !ops->get_trip_type) + if (trips > 0 && (!ops->get_trip_type || !ops->get_trip_temp)) return ERR_PTR(-EINVAL); tz = kzalloc(sizeof(struct thermal_zone_device), GFP_KERNEL); -- cgit v1.2.3 From c21bec86b6e36143a1fc0be60bbd9dfaebcb88a0 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Thu, 16 May 2013 02:16:21 +0000 Subject: Thermal: don't check resource with devm_ioremap_resource devm_ioremap_resource does sanity checks on the given resource. No need to duplicate this in the driver. CC: Ezequiel Garcia CC: Vincenzo Frascino Signed-off-by: Zhang Rui Acked-by: Ezequiel Garcia --- drivers/thermal/armada_thermal.c | 10 ---------- drivers/thermal/spear_thermal.c | 13 +++---------- 2 files changed, 3 insertions(+), 20 deletions(-) diff --git a/drivers/thermal/armada_thermal.c b/drivers/thermal/armada_thermal.c index 5b4d75fd7b49..54ffd64ca3f7 100644 --- a/drivers/thermal/armada_thermal.c +++ b/drivers/thermal/armada_thermal.c @@ -169,21 +169,11 @@ static int armada_thermal_probe(struct platform_device *pdev) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "Failed to get platform resource\n"); - return -ENODEV; - } - priv->sensor = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(priv->sensor)) return PTR_ERR(priv->sensor); res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!res) { - dev_err(&pdev->dev, "Failed to get platform resource\n"); - return -ENODEV; - } - priv->control = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(priv->control)) return PTR_ERR(priv->control); diff --git a/drivers/thermal/spear_thermal.c b/drivers/thermal/spear_thermal.c index b9e21611aece..0f37c7279c96 100644 --- a/drivers/thermal/spear_thermal.c +++ b/drivers/thermal/spear_thermal.c @@ -118,18 +118,11 @@ static int spear_thermal_probe(struct platform_device *pdev) return -ENOMEM; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "memory resource missing\n"); - return -ENODEV; - } - /* Enable thermal sensor */ - stdev->thermal_base = devm_ioremap_resource(dev, res); - if (IS_ERR(stdev->thermal_base)) { - dev_err(&pdev->dev, "ioremap failed\n"); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + stdev->thermal_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(stdev->thermal_base)) return PTR_ERR(stdev->thermal_base); - } stdev->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(stdev->clk)) { -- cgit v1.2.3 From eb982001dbd8546f273f444868a1031cc78b4250 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Wed, 15 May 2013 15:46:00 +0000 Subject: thermal: introduce TI SoC thermal driver This patch moves the ti-soc-thermal driver out of the staging tree to the thermal tree. Cc: Grant Likely Cc: Rob Herring Cc: Rob Landley Cc: Greg Kroah-Hartman Cc: Zhang Rui Cc: Eduardo Valentin Cc: J Keerthy Cc: Radhesh Fadnis Cc: Cyril Roelandt Cc: devicetree-discuss@lists.ozlabs.org Cc: linux-doc@vger.kernel.org Cc: linux-kernel@vger.kernel.org Cc: devel@driverdev.osuosl.org Cc: linux-pm@vger.kernel.org Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- .../devicetree/bindings/thermal/ti_soc_thermal.txt | 60 + drivers/staging/Kconfig | 2 - drivers/staging/Makefile | 1 - drivers/staging/ti-soc-thermal/Kconfig | 48 - drivers/staging/ti-soc-thermal/Makefile | 5 - drivers/staging/ti-soc-thermal/TODO | 12 - .../staging/ti-soc-thermal/omap4-thermal-data.c | 267 ---- drivers/staging/ti-soc-thermal/omap4xxx-bandgap.h | 175 --- .../staging/ti-soc-thermal/omap5-thermal-data.c | 359 ----- drivers/staging/ti-soc-thermal/omap5xxx-bandgap.h | 200 --- drivers/staging/ti-soc-thermal/ti-bandgap.c | 1546 -------------------- drivers/staging/ti-soc-thermal/ti-bandgap.h | 403 ----- drivers/staging/ti-soc-thermal/ti-thermal-common.c | 367 ----- drivers/staging/ti-soc-thermal/ti-thermal.h | 117 -- drivers/staging/ti-soc-thermal/ti_soc_thermal.txt | 60 - drivers/thermal/Kconfig | 3 + drivers/thermal/Makefile | 2 +- drivers/thermal/ti-soc-thermal/Kconfig | 48 + drivers/thermal/ti-soc-thermal/Makefile | 5 + drivers/thermal/ti-soc-thermal/TODO | 12 + .../thermal/ti-soc-thermal/omap4-thermal-data.c | 267 ++++ drivers/thermal/ti-soc-thermal/omap4xxx-bandgap.h | 175 +++ .../thermal/ti-soc-thermal/omap5-thermal-data.c | 359 +++++ drivers/thermal/ti-soc-thermal/omap5xxx-bandgap.h | 200 +++ drivers/thermal/ti-soc-thermal/ti-bandgap.c | 1546 ++++++++++++++++++++ drivers/thermal/ti-soc-thermal/ti-bandgap.h | 403 +++++ drivers/thermal/ti-soc-thermal/ti-thermal-common.c | 367 +++++ drivers/thermal/ti-soc-thermal/ti-thermal.h | 117 ++ 28 files changed, 3563 insertions(+), 3563 deletions(-) create mode 100644 Documentation/devicetree/bindings/thermal/ti_soc_thermal.txt delete mode 100644 drivers/staging/ti-soc-thermal/Kconfig delete mode 100644 drivers/staging/ti-soc-thermal/Makefile delete mode 100644 drivers/staging/ti-soc-thermal/TODO delete mode 100644 drivers/staging/ti-soc-thermal/omap4-thermal-data.c delete mode 100644 drivers/staging/ti-soc-thermal/omap4xxx-bandgap.h delete mode 100644 drivers/staging/ti-soc-thermal/omap5-thermal-data.c delete mode 100644 drivers/staging/ti-soc-thermal/omap5xxx-bandgap.h delete mode 100644 drivers/staging/ti-soc-thermal/ti-bandgap.c delete mode 100644 drivers/staging/ti-soc-thermal/ti-bandgap.h delete mode 100644 drivers/staging/ti-soc-thermal/ti-thermal-common.c delete mode 100644 drivers/staging/ti-soc-thermal/ti-thermal.h delete mode 100644 drivers/staging/ti-soc-thermal/ti_soc_thermal.txt create mode 100644 drivers/thermal/ti-soc-thermal/Kconfig create mode 100644 drivers/thermal/ti-soc-thermal/Makefile create mode 100644 drivers/thermal/ti-soc-thermal/TODO create mode 100644 drivers/thermal/ti-soc-thermal/omap4-thermal-data.c create mode 100644 drivers/thermal/ti-soc-thermal/omap4xxx-bandgap.h create mode 100644 drivers/thermal/ti-soc-thermal/omap5-thermal-data.c create mode 100644 drivers/thermal/ti-soc-thermal/omap5xxx-bandgap.h create mode 100644 drivers/thermal/ti-soc-thermal/ti-bandgap.c create mode 100644 drivers/thermal/ti-soc-thermal/ti-bandgap.h create mode 100644 drivers/thermal/ti-soc-thermal/ti-thermal-common.c create mode 100644 drivers/thermal/ti-soc-thermal/ti-thermal.h diff --git a/Documentation/devicetree/bindings/thermal/ti_soc_thermal.txt b/Documentation/devicetree/bindings/thermal/ti_soc_thermal.txt new file mode 100644 index 000000000000..a4a33d1a0746 --- /dev/null +++ b/Documentation/devicetree/bindings/thermal/ti_soc_thermal.txt @@ -0,0 +1,60 @@ +* Texas Instrument OMAP SCM bandgap bindings + +In the System Control Module, OMAP supplies a voltage reference +and a temperature sensor feature that are gathered in the band +gap voltage and temperature sensor (VBGAPTS) module. The band +gap provides current and voltage reference for its internal +circuits and other analog IP blocks. The analog-to-digital +converter (ADC) produces an output value that is proportional +to the silicon temperature. + +Required properties: +- compatible : Should be: + - "ti,omap4430-bandgap" : for OMAP4430 bandgap + - "ti,omap4460-bandgap" : for OMAP4460 bandgap + - "ti,omap4470-bandgap" : for OMAP4470 bandgap + - "ti,omap5430-bandgap" : for OMAP5430 bandgap +- interrupts : this entry should indicate which interrupt line +the talert signal is routed to; +Specific: +- ti,tshut-gpio : this entry should be used to inform which GPIO +line the tshut signal is routed to; +- regs : this entry must also be specified and it is specific +to each bandgap version, because the mapping may change from +soc to soc, apart of depending on available features. + +Example: +OMAP4430: +bandgap { + reg = <0x4a002260 0x4 0x4a00232C 0x4>; + compatible = "ti,omap4430-bandgap"; +}; + +OMAP4460: +bandgap { + reg = <0x4a002260 0x4 + 0x4a00232C 0x4 + 0x4a002378 0x18>; + compatible = "ti,omap4460-bandgap"; + interrupts = <0 126 4>; /* talert */ + ti,tshut-gpio = <86>; +}; + +OMAP4470: +bandgap { + reg = <0x4a002260 0x4 + 0x4a00232C 0x4 + 0x4a002378 0x18>; + compatible = "ti,omap4470-bandgap"; + interrupts = <0 126 4>; /* talert */ + ti,tshut-gpio = <86>; +}; + +OMAP5430: +bandgap { + reg = <0x4a0021e0 0xc + 0x4a00232c 0xc + 0x4a002380 0x2c + 0x4a0023C0 0x3c>; + compatible = "ti,omap5430-bandgap"; +}; diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 4e8a1794f50a..79701de74855 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -118,8 +118,6 @@ source "drivers/staging/gdm72xx/Kconfig" source "drivers/staging/csr/Kconfig" -source "drivers/staging/ti-soc-thermal/Kconfig" - source "drivers/staging/silicom/Kconfig" source "drivers/staging/ced1401/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 415772ea306d..f8b740c4ea7e 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -52,7 +52,6 @@ obj-$(CONFIG_ANDROID) += android/ obj-$(CONFIG_USB_WPAN_HCD) += ozwpan/ obj-$(CONFIG_WIMAX_GDM72XX) += gdm72xx/ obj-$(CONFIG_CSR_WIFI) += csr/ -obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/ obj-$(CONFIG_NET_VENDOR_SILICOM) += silicom/ obj-$(CONFIG_CED1401) += ced1401/ obj-$(CONFIG_DRM_IMX) += imx-drm/ diff --git a/drivers/staging/ti-soc-thermal/Kconfig b/drivers/staging/ti-soc-thermal/Kconfig deleted file mode 100644 index e81375fb2155..000000000000 --- a/drivers/staging/ti-soc-thermal/Kconfig +++ /dev/null @@ -1,48 +0,0 @@ -config TI_SOC_THERMAL - tristate "Texas Instruments SoCs temperature sensor driver" - depends on THERMAL - depends on ARCH_HAS_BANDGAP - help - If you say yes here you get support for the Texas Instruments - OMAP4460+ on die bandgap temperature sensor support. The register - set is part of system control module. - - This includes alert interrupts generation and also the TSHUT - support. - -config TI_THERMAL - bool "Texas Instruments SoCs thermal framework support" - depends on TI_SOC_THERMAL - depends on CPU_THERMAL - help - If you say yes here you want to get support for generic thermal - framework for the Texas Instruments on die bandgap temperature sensor. - - This includes trip points definitions, extrapolation rules and - CPU cooling device bindings. - -config OMAP4_THERMAL - bool "Texas Instruments OMAP4 thermal support" - depends on TI_SOC_THERMAL - depends on ARCH_OMAP4 - help - If you say yes here you get thermal support for the Texas Instruments - OMAP4 SoC family. The current chip supported are: - - OMAP4430 - - OMAP4460 - - OMAP4470 - - This includes alert interrupts generation and also the TSHUT - support. - -config OMAP5_THERMAL - bool "Texas Instruments OMAP5 thermal support" - depends on TI_SOC_THERMAL - depends on SOC_OMAP5 - help - If you say yes here you get thermal support for the Texas Instruments - OMAP5 SoC family. The current chip supported are: - - OMAP5430 - - This includes alert interrupts generation and also the TSHUT - support. diff --git a/drivers/staging/ti-soc-thermal/Makefile b/drivers/staging/ti-soc-thermal/Makefile deleted file mode 100644 index 0ca034fb419d..000000000000 --- a/drivers/staging/ti-soc-thermal/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal.o -ti-soc-thermal-y := ti-bandgap.o -ti-soc-thermal-$(CONFIG_TI_THERMAL) += ti-thermal-common.o -ti-soc-thermal-$(CONFIG_OMAP4_THERMAL) += omap4-thermal-data.o -ti-soc-thermal-$(CONFIG_OMAP5_THERMAL) += omap5-thermal-data.o diff --git a/drivers/staging/ti-soc-thermal/TODO b/drivers/staging/ti-soc-thermal/TODO deleted file mode 100644 index 7da787d19241..000000000000 --- a/drivers/staging/ti-soc-thermal/TODO +++ /dev/null @@ -1,12 +0,0 @@ -List of TODOs (by Eduardo Valentin) - -on ti-bandgap.c: -- Revisit PM support - -on ti-thermal-common.c/ti-thermal.h: -- Revisit need for locking - -generally: -- make sure this code works on OMAP4430, OMAP4460 and OMAP5430 - -Copy patches to Eduardo Valentin diff --git a/drivers/staging/ti-soc-thermal/omap4-thermal-data.c b/drivers/staging/ti-soc-thermal/omap4-thermal-data.c deleted file mode 100644 index d255d33da9eb..000000000000 --- a/drivers/staging/ti-soc-thermal/omap4-thermal-data.c +++ /dev/null @@ -1,267 +0,0 @@ -/* - * OMAP4 thermal driver. - * - * Copyright (C) 2011-2012 Texas Instruments Inc. - * Contact: - * Eduardo Valentin - * - * 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 "ti-thermal.h" -#include "ti-bandgap.h" -#include "omap4xxx-bandgap.h" - -/* - * OMAP4430 has one instance of thermal sensor for MPU - * need to describe the individual bit fields - */ -static struct temp_sensor_registers -omap4430_mpu_temp_sensor_registers = { - .temp_sensor_ctrl = OMAP4430_TEMP_SENSOR_CTRL_OFFSET, - .bgap_tempsoff_mask = OMAP4430_BGAP_TEMPSOFF_MASK, - .bgap_soc_mask = OMAP4430_BGAP_TEMP_SENSOR_SOC_MASK, - .bgap_eocz_mask = OMAP4430_BGAP_TEMP_SENSOR_EOCZ_MASK, - .bgap_dtemp_mask = OMAP4430_BGAP_TEMP_SENSOR_DTEMP_MASK, - - .bgap_mode_ctrl = OMAP4430_TEMP_SENSOR_CTRL_OFFSET, - .mode_ctrl_mask = OMAP4430_SINGLE_MODE_MASK, - - .bgap_efuse = OMAP4430_FUSE_OPP_BGAP, -}; - -/* Thresholds and limits for OMAP4430 MPU temperature sensor */ -static struct temp_sensor_data omap4430_mpu_temp_sensor_data = { - .min_freq = OMAP4430_MIN_FREQ, - .max_freq = OMAP4430_MAX_FREQ, - .max_temp = OMAP4430_MAX_TEMP, - .min_temp = OMAP4430_MIN_TEMP, - .hyst_val = OMAP4430_HYST_VAL, -}; - -/* - * Temperature values in milli degree celsius - * ADC code values from 530 to 923 - */ -static const int -omap4430_adc_to_temp[OMAP4430_ADC_END_VALUE - OMAP4430_ADC_START_VALUE + 1] = { - -38000, -35000, -34000, -32000, -30000, -28000, -26000, -24000, -22000, - -20000, -18000, -17000, -15000, -13000, -12000, -10000, -8000, -6000, - -5000, -3000, -1000, 0, 2000, 3000, 5000, 6000, 8000, 10000, 12000, - 13000, 15000, 17000, 19000, 21000, 23000, 25000, 27000, 28000, 30000, - 32000, 33000, 35000, 37000, 38000, 40000, 42000, 43000, 45000, 47000, - 48000, 50000, 52000, 53000, 55000, 57000, 58000, 60000, 62000, 64000, - 66000, 68000, 70000, 71000, 73000, 75000, 77000, 78000, 80000, 82000, - 83000, 85000, 87000, 88000, 90000, 92000, 93000, 95000, 97000, 98000, - 100000, 102000, 103000, 105000, 107000, 109000, 111000, 113000, 115000, - 117000, 118000, 120000, 122000, 123000, -}; - -/* OMAP4430 data */ -const struct ti_bandgap_data omap4430_data = { - .features = TI_BANDGAP_FEATURE_MODE_CONFIG | - TI_BANDGAP_FEATURE_CLK_CTRL | - TI_BANDGAP_FEATURE_POWER_SWITCH, - .fclock_name = "bandgap_fclk", - .div_ck_name = "bandgap_fclk", - .conv_table = omap4430_adc_to_temp, - .adc_start_val = OMAP4430_ADC_START_VALUE, - .adc_end_val = OMAP4430_ADC_END_VALUE, - .expose_sensor = ti_thermal_expose_sensor, - .remove_sensor = ti_thermal_remove_sensor, - .sensors = { - { - .registers = &omap4430_mpu_temp_sensor_registers, - .ts_data = &omap4430_mpu_temp_sensor_data, - .domain = "cpu", - .slope = OMAP_GRADIENT_SLOPE_4430, - .constant = OMAP_GRADIENT_CONST_4430, - .slope_pcb = OMAP_GRADIENT_SLOPE_W_PCB_4430, - .constant_pcb = OMAP_GRADIENT_CONST_W_PCB_4430, - .register_cooling = ti_thermal_register_cpu_cooling, - .unregister_cooling = ti_thermal_unregister_cpu_cooling, - }, - }, - .sensor_count = 1, -}; -/* - * OMAP4460 has one instance of thermal sensor for MPU - * need to describe the individual bit fields - */ -static struct temp_sensor_registers -omap4460_mpu_temp_sensor_registers = { - .temp_sensor_ctrl = OMAP4460_TEMP_SENSOR_CTRL_OFFSET, - .bgap_tempsoff_mask = OMAP4460_BGAP_TEMPSOFF_MASK, - .bgap_soc_mask = OMAP4460_BGAP_TEMP_SENSOR_SOC_MASK, - .bgap_eocz_mask = OMAP4460_BGAP_TEMP_SENSOR_EOCZ_MASK, - .bgap_dtemp_mask = OMAP4460_BGAP_TEMP_SENSOR_DTEMP_MASK, - - .bgap_mask_ctrl = OMAP4460_BGAP_CTRL_OFFSET, - .mask_hot_mask = OMAP4460_MASK_HOT_MASK, - .mask_cold_mask = OMAP4460_MASK_COLD_MASK, - - .bgap_mode_ctrl = OMAP4460_BGAP_CTRL_OFFSET, - .mode_ctrl_mask = OMAP4460_SINGLE_MODE_MASK, - - .bgap_counter = OMAP4460_BGAP_COUNTER_OFFSET, - .counter_mask = OMAP4460_COUNTER_MASK, - - .bgap_threshold = OMAP4460_BGAP_THRESHOLD_OFFSET, - .threshold_thot_mask = OMAP4460_T_HOT_MASK, - .threshold_tcold_mask = OMAP4460_T_COLD_MASK, - - .tshut_threshold = OMAP4460_BGAP_TSHUT_OFFSET, - .tshut_hot_mask = OMAP4460_TSHUT_HOT_MASK, - .tshut_cold_mask = OMAP4460_TSHUT_COLD_MASK, - - .bgap_status = OMAP4460_BGAP_STATUS_OFFSET, - .status_clean_stop_mask = OMAP4460_CLEAN_STOP_MASK, - .status_bgap_alert_mask = OMAP4460_BGAP_ALERT_MASK, - .status_hot_mask = OMAP4460_HOT_FLAG_MASK, - .status_cold_mask = OMAP4460_COLD_FLAG_MASK, - - .bgap_efuse = OMAP4460_FUSE_OPP_BGAP, -}; - -/* Thresholds and limits for OMAP4460 MPU temperature sensor */ -static struct temp_sensor_data omap4460_mpu_temp_sensor_data = { - .tshut_hot = OMAP4460_TSHUT_HOT, - .tshut_cold = OMAP4460_TSHUT_COLD, - .t_hot = OMAP4460_T_HOT, - .t_cold = OMAP4460_T_COLD, - .min_freq = OMAP4460_MIN_FREQ, - .max_freq = OMAP4460_MAX_FREQ, - .max_temp = OMAP4460_MAX_TEMP, - .min_temp = OMAP4460_MIN_TEMP, - .hyst_val = OMAP4460_HYST_VAL, - .update_int1 = 1000, - .update_int2 = 2000, -}; - -/* - * Temperature values in milli degree celsius - * ADC code values from 530 to 923 - */ -static const int -omap4460_adc_to_temp[OMAP4460_ADC_END_VALUE - OMAP4460_ADC_START_VALUE + 1] = { - -40000, -40000, -40000, -40000, -39800, -39400, -39000, -38600, -38200, - -37800, -37300, -36800, -36400, -36000, -35600, -35200, -34800, - -34300, -33800, -33400, -33000, -32600, -32200, -31800, -31300, - -30800, -30400, -30000, -29600, -29200, -28700, -28200, -27800, - -27400, -27000, -26600, -26200, -25700, -25200, -24800, -24400, - -24000, -23600, -23200, -22700, -22200, -21800, -21400, -21000, - -20600, -20200, -19700, -19200, -18800, -18400, -18000, -17600, - -17200, -16700, -16200, -15800, -15400, -15000, -14600, -14200, - -13700, -13200, -12800, -12400, -12000, -11600, -11200, -10700, - -10200, -9800, -9400, -9000, -8600, -8200, -7700, -7200, -6800, - -6400, -6000, -5600, -5200, -4800, -4300, -3800, -3400, -3000, - -2600, -2200, -1800, -1300, -800, -400, 0, 400, 800, 1200, 1600, - 2100, 2600, 3000, 3400, 3800, 4200, 4600, 5100, 5600, 6000, 6400, - 6800, 7200, 7600, 8000, 8500, 9000, 9400, 9800, 10200, 10600, 11000, - 11400, 11900, 12400, 12800, 13200, 13600, 14000, 14400, 14800, - 15300, 15800, 16200, 16600, 17000, 17400, 17800, 18200, 18700, - 19200, 19600, 20000, 20400, 20800, 21200, 21600, 22100, 22600, - 23000, 23400, 23800, 24200, 24600, 25000, 25400, 25900, 26400, - 26800, 27200, 27600, 28000, 28400, 28800, 29300, 29800, 30200, - 30600, 31000, 31400, 31800, 32200, 32600, 33100, 33600, 34000, - 34400, 34800, 35200, 35600, 36000, 36400, 36800, 37300, 37800, - 38200, 38600, 39000, 39400, 39800, 40200, 40600, 41100, 41600, - 42000, 42400, 42800, 43200, 43600, 44000, 44400, 44800, 45300, - 45800, 46200, 46600, 47000, 47400, 47800, 48200, 48600, 49000, - 49500, 50000, 50400, 50800, 51200, 51600, 52000, 52400, 52800, - 53200, 53700, 54200, 54600, 55000, 55400, 55800, 56200, 56600, - 57000, 57400, 57800, 58200, 58700, 59200, 59600, 60000, 60400, - 60800, 61200, 61600, 62000, 62400, 62800, 63300, 63800, 64200, - 64600, 65000, 65400, 65800, 66200, 66600, 67000, 67400, 67800, - 68200, 68700, 69200, 69600, 70000, 70400, 70800, 71200, 71600, - 72000, 72400, 72800, 73200, 73600, 74100, 74600, 75000, 75400, - 75800, 76200, 76600, 77000, 77400, 77800, 78200, 78600, 79000, - 79400, 79800, 80300, 80800, 81200, 81600, 82000, 82400, 82800, - 83200, 83600, 84000, 84400, 84800, 85200, 85600, 86000, 86400, - 86800, 87300, 87800, 88200, 88600, 89000, 89400, 89800, 90200, - 90600, 91000, 91400, 91800, 92200, 92600, 93000, 93400, 93800, - 94200, 94600, 95000, 95500, 96000, 96400, 96800, 97200, 97600, - 98000, 98400, 98800, 99200, 99600, 100000, 100400, 100800, 101200, - 101600, 102000, 102400, 102800, 103200, 103600, 104000, 104400, - 104800, 105200, 105600, 106100, 106600, 107000, 107400, 107800, - 108200, 108600, 109000, 109400, 109800, 110200, 110600, 111000, - 111400, 111800, 112200, 112600, 113000, 113400, 113800, 114200, - 114600, 115000, 115400, 115800, 116200, 116600, 117000, 117400, - 117800, 118200, 118600, 119000, 119400, 119800, 120200, 120600, - 121000, 121400, 121800, 122200, 122600, 123000, 123400, 123800, 124200, - 124600, 124900, 125000, 125000, 125000, 125000 -}; - -/* OMAP4460 data */ -const struct ti_bandgap_data omap4460_data = { - .features = TI_BANDGAP_FEATURE_TSHUT | - TI_BANDGAP_FEATURE_TSHUT_CONFIG | - TI_BANDGAP_FEATURE_TALERT | - TI_BANDGAP_FEATURE_MODE_CONFIG | - TI_BANDGAP_FEATURE_POWER_SWITCH | - TI_BANDGAP_FEATURE_CLK_CTRL | - TI_BANDGAP_FEATURE_COUNTER, - .fclock_name = "bandgap_ts_fclk", - .div_ck_name = "div_ts_ck", - .conv_table = omap4460_adc_to_temp, - .adc_start_val = OMAP4460_ADC_START_VALUE, - .adc_end_val = OMAP4460_ADC_END_VALUE, - .expose_sensor = ti_thermal_expose_sensor, - .remove_sensor = ti_thermal_remove_sensor, - .report_temperature = ti_thermal_report_sensor_temperature, - .sensors = { - { - .registers = &omap4460_mpu_temp_sensor_registers, - .ts_data = &omap4460_mpu_temp_sensor_data, - .domain = "cpu", - .slope = OMAP_GRADIENT_SLOPE_4460, - .constant = OMAP_GRADIENT_CONST_4460, - .slope_pcb = OMAP_GRADIENT_SLOPE_W_PCB_4460, - .constant_pcb = OMAP_GRADIENT_CONST_W_PCB_4460, - .register_cooling = ti_thermal_register_cpu_cooling, - .unregister_cooling = ti_thermal_unregister_cpu_cooling, - }, - }, - .sensor_count = 1, -}; - -/* OMAP4470 data */ -const struct ti_bandgap_data omap4470_data = { - .features = TI_BANDGAP_FEATURE_TSHUT | - TI_BANDGAP_FEATURE_TSHUT_CONFIG | - TI_BANDGAP_FEATURE_TALERT | - TI_BANDGAP_FEATURE_MODE_CONFIG | - TI_BANDGAP_FEATURE_POWER_SWITCH | - TI_BANDGAP_FEATURE_CLK_CTRL | - TI_BANDGAP_FEATURE_COUNTER, - .fclock_name = "bandgap_ts_fclk", - .div_ck_name = "div_ts_ck", - .conv_table = omap4460_adc_to_temp, - .adc_start_val = OMAP4460_ADC_START_VALUE, - .adc_end_val = OMAP4460_ADC_END_VALUE, - .expose_sensor = ti_thermal_expose_sensor, - .remove_sensor = ti_thermal_remove_sensor, - .report_temperature = ti_thermal_report_sensor_temperature, - .sensors = { - { - .registers = &omap4460_mpu_temp_sensor_registers, - .ts_data = &omap4460_mpu_temp_sensor_data, - .domain = "cpu", - .slope = OMAP_GRADIENT_SLOPE_4470, - .constant = OMAP_GRADIENT_CONST_4470, - .slope_pcb = OMAP_GRADIENT_SLOPE_W_PCB_4470, - .constant_pcb = OMAP_GRADIENT_CONST_W_PCB_4470, - .register_cooling = ti_thermal_register_cpu_cooling, - .unregister_cooling = ti_thermal_unregister_cpu_cooling, - }, - }, - .sensor_count = 1, -}; diff --git a/drivers/staging/ti-soc-thermal/omap4xxx-bandgap.h b/drivers/staging/ti-soc-thermal/omap4xxx-bandgap.h deleted file mode 100644 index 6f2de3a3356d..000000000000 --- a/drivers/staging/ti-soc-thermal/omap4xxx-bandgap.h +++ /dev/null @@ -1,175 +0,0 @@ -/* - * OMAP4xxx bandgap registers, bitfields and temperature definitions - * - * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/ - * Contact: - * Eduardo Valentin - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ -#ifndef __OMAP4XXX_BANDGAP_H -#define __OMAP4XXX_BANDGAP_H - -/** - * *** OMAP4430 *** - * - * Below, in sequence, are the Register definitions, - * the bitfields and the temperature definitions for OMAP4430. - */ - -/** - * OMAP4430 register definitions - * - * Registers are defined as offsets. The offsets are - * relative to FUSE_OPP_BGAP on 4430. - */ - -/* OMAP4430.FUSE_OPP_BGAP */ -#define OMAP4430_FUSE_OPP_BGAP 0x0 - -/* OMAP4430.TEMP_SENSOR */ -#define OMAP4430_TEMP_SENSOR_CTRL_OFFSET 0xCC - -/** - * Register and bit definitions for OMAP4430 - * - * All the macros bellow define the required bits for - * controlling temperature on OMAP4430. Bit defines are - * grouped by register. - */ - -/* OMAP4430.TEMP_SENSOR bits */ -#define OMAP4430_BGAP_TEMPSOFF_MASK BIT(12) -#define OMAP4430_BGAP_TSHUT_MASK BIT(11) -#define OMAP4430_SINGLE_MODE_MASK BIT(10) -#define OMAP4430_BGAP_TEMP_SENSOR_SOC_MASK BIT(9) -#define OMAP4430_BGAP_TEMP_SENSOR_EOCZ_MASK BIT(8) -#define OMAP4430_BGAP_TEMP_SENSOR_DTEMP_MASK (0xff << 0) - -/** - * Temperature limits and thresholds for OMAP4430 - * - * All the macros bellow are definitions for handling the - * ADC conversions and representation of temperature limits - * and thresholds for OMAP4430. - */ - -/* ADC conversion table limits */ -#define OMAP4430_ADC_START_VALUE 0 -#define OMAP4430_ADC_END_VALUE 127 -/* bandgap clock limits (no control on 4430) */ -#define OMAP4430_MAX_FREQ 32768 -#define OMAP4430_MIN_FREQ 32768 -/* sensor limits */ -#define OMAP4430_MIN_TEMP -40000 -#define OMAP4430_MAX_TEMP 125000 -#define OMAP4430_HYST_VAL 5000 - -/** - * *** OMAP4460 *** Applicable for OMAP4470 - * - * Below, in sequence, are the Register definitions, - * the bitfields and the temperature definitions for OMAP4460. - */ - -/** - * OMAP4460 register definitions - * - * Registers are defined as offsets. The offsets are - * relative to FUSE_OPP_BGAP on 4460. - */ - -/* OMAP4460.FUSE_OPP_BGAP */ -#define OMAP4460_FUSE_OPP_BGAP 0x0 - -/* OMAP4460.TEMP_SENSOR */ -#define OMAP4460_TEMP_SENSOR_CTRL_OFFSET 0xCC - -/* OMAP4460.BANDGAP_CTRL */ -#define OMAP4460_BGAP_CTRL_OFFSET 0x118 - -/* OMAP4460.BANDGAP_COUNTER */ -#define OMAP4460_BGAP_COUNTER_OFFSET 0x11C - -/* OMAP4460.BANDGAP_THRESHOLD */ -#define OMAP4460_BGAP_THRESHOLD_OFFSET 0x120 - -/* OMAP4460.TSHUT_THRESHOLD */ -#define OMAP4460_BGAP_TSHUT_OFFSET 0x124 - -/* OMAP4460.BANDGAP_STATUS */ -#define OMAP4460_BGAP_STATUS_OFFSET 0x128 - -/** - * Register bitfields for OMAP4460 - * - * All the macros bellow define the required bits for - * controlling temperature on OMAP4460. Bit defines are - * grouped by register. - */ -/* OMAP4460.TEMP_SENSOR bits */ -#define OMAP4460_BGAP_TEMPSOFF_MASK BIT(13) -#define OMAP4460_BGAP_TEMP_SENSOR_SOC_MASK BIT(11) -#define OMAP4460_BGAP_TEMP_SENSOR_EOCZ_MASK BIT(10) -#define OMAP4460_BGAP_TEMP_SENSOR_DTEMP_MASK (0x3ff << 0) - -/* OMAP4460.BANDGAP_CTRL bits */ -#define OMAP4460_SINGLE_MODE_MASK BIT(31) -#define OMAP4460_MASK_HOT_MASK BIT(1) -#define OMAP4460_MASK_COLD_MASK BIT(0) - -/* OMAP4460.BANDGAP_COUNTER bits */ -#define OMAP4460_COUNTER_MASK (0xffffff << 0) - -/* OMAP4460.BANDGAP_THRESHOLD bits */ -#define OMAP4460_T_HOT_MASK (0x3ff << 16) -#define OMAP4460_T_COLD_MASK (0x3ff << 0) - -/* OMAP4460.TSHUT_THRESHOLD bits */ -#define OMAP4460_TSHUT_HOT_MASK (0x3ff << 16) -#define OMAP4460_TSHUT_COLD_MASK (0x3ff << 0) - -/* OMAP4460.BANDGAP_STATUS bits */ -#define OMAP4460_CLEAN_STOP_MASK BIT(3) -#define OMAP4460_BGAP_ALERT_MASK BIT(2) -#define OMAP4460_HOT_FLAG_MASK BIT(1) -#define OMAP4460_COLD_FLAG_MASK BIT(0) - -/** - * Temperature limits and thresholds for OMAP4460 - * - * All the macros bellow are definitions for handling the - * ADC conversions and representation of temperature limits - * and thresholds for OMAP4460. - */ - -/* ADC conversion table limits */ -#define OMAP4460_ADC_START_VALUE 530 -#define OMAP4460_ADC_END_VALUE 932 -/* bandgap clock limits */ -#define OMAP4460_MAX_FREQ 1500000 -#define OMAP4460_MIN_FREQ 1000000 -/* sensor limits */ -#define OMAP4460_MIN_TEMP -40000 -#define OMAP4460_MAX_TEMP 123000 -#define OMAP4460_HYST_VAL 5000 -/* interrupts thresholds */ -#define OMAP4460_TSHUT_HOT 900 /* 122 deg C */ -#define OMAP4460_TSHUT_COLD 895 /* 100 deg C */ -#define OMAP4460_T_HOT 800 /* 73 deg C */ -#define OMAP4460_T_COLD 795 /* 71 deg C */ - -#endif /* __OMAP4XXX_BANDGAP_H */ diff --git a/drivers/staging/ti-soc-thermal/omap5-thermal-data.c b/drivers/staging/ti-soc-thermal/omap5-thermal-data.c deleted file mode 100644 index eff0c80fd4af..000000000000 --- a/drivers/staging/ti-soc-thermal/omap5-thermal-data.c +++ /dev/null @@ -1,359 +0,0 @@ -/* - * OMAP5 thermal driver. - * - * Copyright (C) 2011-2012 Texas Instruments Inc. - * Contact: - * Eduardo Valentin - * - * 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 "ti-thermal.h" -#include "ti-bandgap.h" -#include "omap5xxx-bandgap.h" - -/* - * OMAP5430 has three instances of thermal sensor for MPU, GPU & CORE, - * need to describe the individual registers and bit fields. - */ - -/* - * OMAP5430 MPU thermal sensor register offset and bit-fields - */ -static struct temp_sensor_registers -omap5430_mpu_temp_sensor_registers = { - .temp_sensor_ctrl = OMAP5430_TEMP_SENSOR_MPU_OFFSET, - .bgap_tempsoff_mask = OMAP5430_BGAP_TEMPSOFF_MASK, - .bgap_eocz_mask = OMAP5430_BGAP_TEMP_SENSOR_EOCZ_MASK, - .bgap_dtemp_mask = OMAP5430_BGAP_TEMP_SENSOR_DTEMP_MASK, - - .bgap_mask_ctrl = OMAP5430_BGAP_CTRL_OFFSET, - .mask_hot_mask = OMAP5430_MASK_HOT_MPU_MASK, - .mask_cold_mask = OMAP5430_MASK_COLD_MPU_MASK, - .mask_sidlemode_mask = OMAP5430_MASK_SIDLEMODE_MASK, - .mask_counter_delay_mask = OMAP5430_MASK_COUNTER_DELAY_MASK, - .mask_freeze_mask = OMAP5430_MASK_FREEZE_MPU_MASK, - .mask_clear_mask = OMAP5430_MASK_CLEAR_MPU_MASK, - .mask_clear_accum_mask = OMAP5430_MASK_CLEAR_ACCUM_MPU_MASK, - - - .bgap_counter = OMAP5430_BGAP_CTRL_OFFSET, - .counter_mask = OMAP5430_COUNTER_MASK, - - .bgap_threshold = OMAP5430_BGAP_THRESHOLD_MPU_OFFSET, - .threshold_thot_mask = OMAP5430_T_HOT_MASK, - .threshold_tcold_mask = OMAP5430_T_COLD_MASK, - - .tshut_threshold = OMAP5430_BGAP_TSHUT_MPU_OFFSET, - .tshut_hot_mask = OMAP5430_TSHUT_HOT_MASK, - .tshut_cold_mask = OMAP5430_TSHUT_COLD_MASK, - - .bgap_status = OMAP5430_BGAP_STATUS_OFFSET, - .status_clean_stop_mask = 0x0, - .status_bgap_alert_mask = OMAP5430_BGAP_ALERT_MASK, - .status_hot_mask = OMAP5430_HOT_MPU_FLAG_MASK, - .status_cold_mask = OMAP5430_COLD_MPU_FLAG_MASK, - - .bgap_cumul_dtemp = OMAP5430_BGAP_CUMUL_DTEMP_MPU_OFFSET, - .ctrl_dtemp_0 = OMAP5430_BGAP_DTEMP_MPU_0_OFFSET, - .ctrl_dtemp_1 = OMAP5430_BGAP_DTEMP_MPU_1_OFFSET, - .ctrl_dtemp_2 = OMAP5430_BGAP_DTEMP_MPU_2_OFFSET, - .ctrl_dtemp_3 = OMAP5430_BGAP_DTEMP_MPU_3_OFFSET, - .ctrl_dtemp_4 = OMAP5430_BGAP_DTEMP_MPU_4_OFFSET, - .bgap_efuse = OMAP5430_FUSE_OPP_BGAP_MPU, -}; - -/* - * OMAP5430 GPU thermal sensor register offset and bit-fields - */ -static struct temp_sensor_registers -omap5430_gpu_temp_sensor_registers = { - .temp_sensor_ctrl = OMAP5430_TEMP_SENSOR_GPU_OFFSET, - .bgap_tempsoff_mask = OMAP5430_BGAP_TEMPSOFF_MASK, - .bgap_eocz_mask = OMAP5430_BGAP_TEMP_SENSOR_EOCZ_MASK, - .bgap_dtemp_mask = OMAP5430_BGAP_TEMP_SENSOR_DTEMP_MASK, - - .bgap_mask_ctrl = OMAP5430_BGAP_CTRL_OFFSET, - .mask_hot_mask = OMAP5430_MASK_HOT_GPU_MASK, - .mask_cold_mask = OMAP5430_MASK_COLD_GPU_MASK, - .mask_sidlemode_mask = OMAP5430_MASK_SIDLEMODE_MASK, - .mask_counter_delay_mask = OMAP5430_MASK_COUNTER_DELAY_MASK, - .mask_freeze_mask = OMAP5430_MASK_FREEZE_GPU_MASK, - .mask_clear_mask = OMAP5430_MASK_CLEAR_GPU_MASK, - .mask_clear_accum_mask = OMAP5430_MASK_CLEAR_ACCUM_GPU_MASK, - - .bgap_counter = OMAP5430_BGAP_CTRL_OFFSET, - .counter_mask = OMAP5430_COUNTER_MASK, - - .bgap_threshold = OMAP5430_BGAP_THRESHOLD_GPU_OFFSET, - .threshold_thot_mask = OMAP5430_T_HOT_MASK, - .threshold_tcold_mask = OMAP5430_T_COLD_MASK, - - .tshut_threshold = OMAP5430_BGAP_TSHUT_GPU_OFFSET, - .tshut_hot_mask = OMAP5430_TSHUT_HOT_MASK, - .tshut_cold_mask = OMAP5430_TSHUT_COLD_MASK, - - .bgap_status = OMAP5430_BGAP_STATUS_OFFSET, - .status_clean_stop_mask = 0x0, - .status_bgap_alert_mask = OMAP5430_BGAP_ALERT_MASK, - .status_hot_mask = OMAP5430_HOT_GPU_FLAG_MASK, - .status_cold_mask = OMAP5430_COLD_GPU_FLAG_MASK, - - .bgap_cumul_dtemp = OMAP5430_BGAP_CUMUL_DTEMP_GPU_OFFSET, - .ctrl_dtemp_0 = OMAP5430_BGAP_DTEMP_GPU_0_OFFSET, - .ctrl_dtemp_1 = OMAP5430_BGAP_DTEMP_GPU_1_OFFSET, - .ctrl_dtemp_2 = OMAP5430_BGAP_DTEMP_GPU_2_OFFSET, - .ctrl_dtemp_3 = OMAP5430_BGAP_DTEMP_GPU_3_OFFSET, - .ctrl_dtemp_4 = OMAP5430_BGAP_DTEMP_GPU_4_OFFSET, - - .bgap_efuse = OMAP5430_FUSE_OPP_BGAP_GPU, -}; - -/* - * OMAP5430 CORE thermal sensor register offset and bit-fields - */ -static struct temp_sensor_registers -omap5430_core_temp_sensor_registers = { - .temp_sensor_ctrl = OMAP5430_TEMP_SENSOR_CORE_OFFSET, - .bgap_tempsoff_mask = OMAP5430_BGAP_TEMPSOFF_MASK, - .bgap_eocz_mask = OMAP5430_BGAP_TEMP_SENSOR_EOCZ_MASK, - .bgap_dtemp_mask = OMAP5430_BGAP_TEMP_SENSOR_DTEMP_MASK, - - .bgap_mask_ctrl = OMAP5430_BGAP_CTRL_OFFSET, - .mask_hot_mask = OMAP5430_MASK_HOT_CORE_MASK, - .mask_cold_mask = OMAP5430_MASK_COLD_CORE_MASK, - .mask_sidlemode_mask = OMAP5430_MASK_SIDLEMODE_MASK, - .mask_counter_delay_mask = OMAP5430_MASK_COUNTER_DELAY_MASK, - .mask_freeze_mask = OMAP5430_MASK_FREEZE_CORE_MASK, - .mask_clear_mask = OMAP5430_MASK_CLEAR_CORE_MASK, - .mask_clear_accum_mask = OMAP5430_MASK_CLEAR_ACCUM_CORE_MASK, - - .bgap_counter = OMAP5430_BGAP_CTRL_OFFSET, - .counter_mask = OMAP5430_COUNTER_MASK, - - .bgap_threshold = OMAP5430_BGAP_THRESHOLD_CORE_OFFSET, - .threshold_thot_mask = OMAP5430_T_HOT_MASK, - .threshold_tcold_mask = OMAP5430_T_COLD_MASK, - - .tshut_threshold = OMAP5430_BGAP_TSHUT_CORE_OFFSET, - .tshut_hot_mask = OMAP5430_TSHUT_HOT_MASK, - .tshut_cold_mask = OMAP5430_TSHUT_COLD_MASK, - - .bgap_status = OMAP5430_BGAP_STATUS_OFFSET, - .status_clean_stop_mask = 0x0, - .status_bgap_alert_mask = OMAP5430_BGAP_ALERT_MASK, - .status_hot_mask = OMAP5430_HOT_CORE_FLAG_MASK, - .status_cold_mask = OMAP5430_COLD_CORE_FLAG_MASK, - - .bgap_cumul_dtemp = OMAP5430_BGAP_CUMUL_DTEMP_CORE_OFFSET, - .ctrl_dtemp_0 = OMAP5430_BGAP_DTEMP_CORE_0_OFFSET, - .ctrl_dtemp_1 = OMAP5430_BGAP_DTEMP_CORE_1_OFFSET, - .ctrl_dtemp_2 = OMAP5430_BGAP_DTEMP_CORE_2_OFFSET, - .ctrl_dtemp_3 = OMAP5430_BGAP_DTEMP_CORE_3_OFFSET, - .ctrl_dtemp_4 = OMAP5430_BGAP_DTEMP_CORE_4_OFFSET, - - .bgap_efuse = OMAP5430_FUSE_OPP_BGAP_CORE, -}; - -/* Thresholds and limits for OMAP5430 MPU temperature sensor */ -static struct temp_sensor_data omap5430_mpu_temp_sensor_data = { - .tshut_hot = OMAP5430_MPU_TSHUT_HOT, - .tshut_cold = OMAP5430_MPU_TSHUT_COLD, - .t_hot = OMAP5430_MPU_T_HOT, - .t_cold = OMAP5430_MPU_T_COLD, - .min_freq = OMAP5430_MPU_MIN_FREQ, - .max_freq = OMAP5430_MPU_MAX_FREQ, - .max_temp = OMAP5430_MPU_MAX_TEMP, - .min_temp = OMAP5430_MPU_MIN_TEMP, - .hyst_val = OMAP5430_MPU_HYST_VAL, - .update_int1 = 1000, - .update_int2 = 2000, -}; - -/* Thresholds and limits for OMAP5430 GPU temperature sensor */ -static struct temp_sensor_data omap5430_gpu_temp_sensor_data = { - .tshut_hot = OMAP5430_GPU_TSHUT_HOT, - .tshut_cold = OMAP5430_GPU_TSHUT_COLD, - .t_hot = OMAP5430_GPU_T_HOT, - .t_cold = OMAP5430_GPU_T_COLD, - .min_freq = OMAP5430_GPU_MIN_FREQ, - .max_freq = OMAP5430_GPU_MAX_FREQ, - .max_temp = OMAP5430_GPU_MAX_TEMP, - .min_temp = OMAP5430_GPU_MIN_TEMP, - .hyst_val = OMAP5430_GPU_HYST_VAL, - .update_int1 = 1000, - .update_int2 = 2000, -}; - -/* Thresholds and limits for OMAP5430 CORE temperature sensor */ -static struct temp_sensor_data omap5430_core_temp_sensor_data = { - .tshut_hot = OMAP5430_CORE_TSHUT_HOT, - .tshut_cold = OMAP5430_CORE_TSHUT_COLD, - .t_hot = OMAP5430_CORE_T_HOT, - .t_cold = OMAP5430_CORE_T_COLD, - .min_freq = OMAP5430_CORE_MIN_FREQ, - .max_freq = OMAP5430_CORE_MAX_FREQ, - .max_temp = OMAP5430_CORE_MAX_TEMP, - .min_temp = OMAP5430_CORE_MIN_TEMP, - .hyst_val = OMAP5430_CORE_HYST_VAL, - .update_int1 = 1000, - .update_int2 = 2000, -}; - -/* - * OMAP54xx ES2.0 : Temperature values in milli degree celsius - * ADC code values from 540 to 945 - */ -static int -omap5430_adc_to_temp[ - OMAP5430_ADC_END_VALUE - OMAP5430_ADC_START_VALUE + 1] = { - /* Index 540 - 549 */ - -40000, -40000, -40000, -40000, -39800, -39400, -39000, -38600, -38200, - -37800, - /* Index 550 - 559 */ - -37400, -37000, -36600, -36200, -35800, -35300, -34700, -34200, -33800, - -33400, - /* Index 560 - 569 */ - -33000, -32600, -32200, -31800, -31400, -31000, -30600, -30200, -29800, - -29400, - /* Index 570 - 579 */ - -29000, -28600, -28200, -27700, -27100, -26600, -26200, -25800, -25400, - -25000, - /* Index 580 - 589 */ - -24600, -24200, -23800, -23400, -23000, -22600, -22200, -21600, -21400, - -21000, - /* Index 590 - 599 */ - -20500, -19900, -19400, -19000, -18600, -18200, -17800, -17400, -17000, - -16600, - /* Index 600 - 609 */ - -16200, -15800, -15400, -15000, -14600, -14200, -13800, -13400, -13000, - -12500, - /* Index 610 - 619 */ - -11900, -11400, -11000, -10600, -10200, -9800, -9400, -9000, -8600, - -8200, - /* Index 620 - 629 */ - -7800, -7400, -7000, -6600, -6200, -5800, -5400, -5000, -4500, -3900, - /* Index 630 - 639 */ - -3400, -3000, -2600, -2200, -1800, -1400, -1000, -600, -200, 200, - /* Index 640 - 649 */ - 600, 1000, 1400, 1800, 2200, 2600, 3000, 3400, 3900, 4500, - /* Index 650 - 659 */ - 5000, 5400, 5800, 6200, 6600, 7000, 7400, 7800, 8200, 8600, - /* Index 660 - 669 */ - 9000, 9400, 9800, 10200, 10600, 11000, 11400, 11800, 12200, 12700, - /* Index 670 - 679 */ - 13300, 13800, 14200, 14600, 15000, 15400, 15800, 16200, 16600, 17000, - /* Index 680 - 689 */ - 17400, 17800, 18200, 18600, 19000, 19400, 19800, 20200, 20600, 21100, - /* Index 690 - 699 */ - 21400, 21900, 22500, 23000, 23400, 23800, 24200, 24600, 25000, 25400, - /* Index 700 - 709 */ - 25800, 26200, 26600, 27000, 27400, 27800, 28200, 28600, 29000, 29400, - /* Index 710 - 719 */ - 29800, 30200, 30600, 31000, 31400, 31900, 32500, 33000, 33400, 33800, - /* Index 720 - 729 */ - 34200, 34600, 35000, 35400, 35800, 36200, 36600, 37000, 37400, 37800, - /* Index 730 - 739 */ - 38200, 38600, 39000, 39400, 39800, 40200, 40600, 41000, 41400, 41800, - /* Index 740 - 749 */ - 42200, 42600, 43100, 43700, 44200, 44600, 45000, 45400, 45800, 46200, - /* Index 750 - 759 */ - 46600, 47000, 47400, 47800, 48200, 48600, 49000, 49400, 49800, 50200, - /* Index 760 - 769 */ - 50600, 51000, 51400, 51800, 52200, 52600, 53000, 53400, 53800, 54200, - /* Index 770 - 779 */ - 54600, 55000, 55400, 55900, 56500, 57000, 57400, 57800, 58200, 58600, - /* Index 780 - 789 */ - 59000, 59400, 59800, 60200, 60600, 61000, 61400, 61800, 62200, 62600, - /* Index 790 - 799 */ - 63000, 63400, 63800, 64200, 64600, 65000, 65400, 65800, 66200, 66600, - /* Index 800 - 809 */ - 67000, 67400, 67800, 68200, 68600, 69000, 69400, 69800, 70200, 70600, - /* Index 810 - 819 */ - 71000, 71500, 72100, 72600, 73000, 73400, 73800, 74200, 74600, 75000, - /* Index 820 - 829 */ - 75400, 75800, 76200, 76600, 77000, 77400, 77800, 78200, 78600, 79000, - /* Index 830 - 839 */ - 79400, 79800, 80200, 80600, 81000, 81400, 81800, 82200, 82600, 83000, - /* Index 840 - 849 */ - 83400, 83800, 84200, 84600, 85000, 85400, 85800, 86200, 86600, 87000, - /* Index 850 - 859 */ - 87400, 87800, 88200, 88600, 89000, 89400, 89800, 90200, 90600, 91000, - /* Index 860 - 869 */ - 91400, 91800, 92200, 92600, 93000, 93400, 93800, 94200, 94600, 95000, - /* Index 870 - 879 */ - 95400, 95800, 96200, 96600, 97000, 97500, 98100, 98600, 99000, 99400, - /* Index 880 - 889 */ - 99800, 100200, 100600, 101000, 101400, 101800, 102200, 102600, 103000, - 103400, - /* Index 890 - 899 */ - 103800, 104200, 104600, 105000, 105400, 105800, 106200, 106600, 107000, - 107400, - /* Index 900 - 909 */ - 107800, 108200, 108600, 109000, 109400, 109800, 110200, 110600, 111000, - 111400, - /* Index 910 - 919 */ - 111800, 112200, 112600, 113000, 113400, 113800, 114200, 114600, 115000, - 115400, - /* Index 920 - 929 */ - 115800, 116200, 116600, 117000, 117400, 117800, 118200, 118600, 119000, - 119400, - /* Index 930 - 939 */ - 119800, 120200, 120600, 121000, 121400, 121800, 122400, 122600, 123000, - 123400, - /* Index 940 - 945 */ - 123800, 1242000, 124600, 124900, 125000, 125000, -}; - -/* OMAP54xx ES2.0 data */ -const struct ti_bandgap_data omap5430_data = { - .features = TI_BANDGAP_FEATURE_TSHUT_CONFIG | - TI_BANDGAP_FEATURE_FREEZE_BIT | - TI_BANDGAP_FEATURE_TALERT | - TI_BANDGAP_FEATURE_COUNTER_DELAY | - TI_BANDGAP_FEATURE_HISTORY_BUFFER, - .fclock_name = "l3instr_ts_gclk_div", - .div_ck_name = "l3instr_ts_gclk_div", - .conv_table = omap5430_adc_to_temp, - .adc_start_val = OMAP5430_ADC_START_VALUE, - .adc_end_val = OMAP5430_ADC_END_VALUE, - .expose_sensor = ti_thermal_expose_sensor, - .remove_sensor = ti_thermal_remove_sensor, - .report_temperature = ti_thermal_report_sensor_temperature, - .sensors = { - { - .registers = &omap5430_mpu_temp_sensor_registers, - .ts_data = &omap5430_mpu_temp_sensor_data, - .domain = "cpu", - .register_cooling = ti_thermal_register_cpu_cooling, - .unregister_cooling = ti_thermal_unregister_cpu_cooling, - .slope = OMAP_GRADIENT_SLOPE_5430_CPU, - .constant = OMAP_GRADIENT_CONST_5430_CPU, - .slope_pcb = OMAP_GRADIENT_SLOPE_W_PCB_5430_CPU, - .constant_pcb = OMAP_GRADIENT_CONST_W_PCB_5430_CPU, - }, - { - .registers = &omap5430_gpu_temp_sensor_registers, - .ts_data = &omap5430_gpu_temp_sensor_data, - .domain = "gpu", - .slope = OMAP_GRADIENT_SLOPE_5430_GPU, - .constant = OMAP_GRADIENT_CONST_5430_GPU, - .slope_pcb = OMAP_GRADIENT_SLOPE_W_PCB_5430_GPU, - .constant_pcb = OMAP_GRADIENT_CONST_W_PCB_5430_GPU, - }, - { - .registers = &omap5430_core_temp_sensor_registers, - .ts_data = &omap5430_core_temp_sensor_data, - .domain = "core", - }, - }, - .sensor_count = 3, -}; diff --git a/drivers/staging/ti-soc-thermal/omap5xxx-bandgap.h b/drivers/staging/ti-soc-thermal/omap5xxx-bandgap.h deleted file mode 100644 index 400b55dffadd..000000000000 --- a/drivers/staging/ti-soc-thermal/omap5xxx-bandgap.h +++ /dev/null @@ -1,200 +0,0 @@ -/* - * OMAP5xxx bandgap registers, bitfields and temperature definitions - * - * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/ - * Contact: - * Eduardo Valentin - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ -#ifndef __OMAP5XXX_BANDGAP_H -#define __OMAP5XXX_BANDGAP_H - -/** - * *** OMAP5430 *** - * - * Below, in sequence, are the Register definitions, - * the bitfields and the temperature definitions for OMAP5430. - */ - -/** - * OMAP5430 register definitions - * - * Registers are defined as offsets. The offsets are - * relative to FUSE_OPP_BGAP_GPU on 5430. - * - * Register below are grouped by domain (not necessarily in offset order) - */ - -/* OMAP5430.GPU register offsets */ -#define OMAP5430_FUSE_OPP_BGAP_GPU 0x0 -#define OMAP5430_TEMP_SENSOR_GPU_OFFSET 0x150 -#define OMAP5430_BGAP_THRESHOLD_GPU_OFFSET 0x1A8 -#define OMAP5430_BGAP_TSHUT_GPU_OFFSET 0x1B4 -#define OMAP5430_BGAP_CUMUL_DTEMP_GPU_OFFSET 0x1C0 -#define OMAP5430_BGAP_DTEMP_GPU_0_OFFSET 0x1F4 -#define OMAP5430_BGAP_DTEMP_GPU_1_OFFSET 0x1F8 -#define OMAP5430_BGAP_DTEMP_GPU_2_OFFSET 0x1FC -#define OMAP5430_BGAP_DTEMP_GPU_3_OFFSET 0x200 -#define OMAP5430_BGAP_DTEMP_GPU_4_OFFSET 0x204 - -/* OMAP5430.MPU register offsets */ -#define OMAP5430_FUSE_OPP_BGAP_MPU 0x4 -#define OMAP5430_TEMP_SENSOR_MPU_OFFSET 0x14C -#define OMAP5430_BGAP_THRESHOLD_MPU_OFFSET 0x1A4 -#define OMAP5430_BGAP_TSHUT_MPU_OFFSET 0x1B0 -#define OMAP5430_BGAP_CUMUL_DTEMP_MPU_OFFSET 0x1BC -#define OMAP5430_BGAP_DTEMP_MPU_0_OFFSET 0x1E0 -#define OMAP5430_BGAP_DTEMP_MPU_1_OFFSET 0x1E4 -#define OMAP5430_BGAP_DTEMP_MPU_2_OFFSET 0x1E8 -#define OMAP5430_BGAP_DTEMP_MPU_3_OFFSET 0x1EC -#define OMAP5430_BGAP_DTEMP_MPU_4_OFFSET 0x1F0 - -/* OMAP5430.MPU register offsets */ -#define OMAP5430_FUSE_OPP_BGAP_CORE 0x8 -#define OMAP5430_TEMP_SENSOR_CORE_OFFSET 0x154 -#define OMAP5430_BGAP_THRESHOLD_CORE_OFFSET 0x1AC -#define OMAP5430_BGAP_TSHUT_CORE_OFFSET 0x1B8 -#define OMAP5430_BGAP_CUMUL_DTEMP_CORE_OFFSET 0x1C4 -#define OMAP5430_BGAP_DTEMP_CORE_0_OFFSET 0x208 -#define OMAP5430_BGAP_DTEMP_CORE_1_OFFSET 0x20C -#define OMAP5430_BGAP_DTEMP_CORE_2_OFFSET 0x210 -#define OMAP5430_BGAP_DTEMP_CORE_3_OFFSET 0x214 -#define OMAP5430_BGAP_DTEMP_CORE_4_OFFSET 0x218 - -/* OMAP5430.common register offsets */ -#define OMAP5430_BGAP_CTRL_OFFSET 0x1A0 -#define OMAP5430_BGAP_STATUS_OFFSET 0x1C8 - -/** - * Register bitfields for OMAP5430 - * - * All the macros bellow define the required bits for - * controlling temperature on OMAP5430. Bit defines are - * grouped by register. - */ - -/* OMAP5430.TEMP_SENSOR */ -#define OMAP5430_BGAP_TEMP_SENSOR_SOC_MASK BIT(12) -#define OMAP5430_BGAP_TEMPSOFF_MASK BIT(11) -#define OMAP5430_BGAP_TEMP_SENSOR_EOCZ_MASK BIT(10) -#define OMAP5430_BGAP_TEMP_SENSOR_DTEMP_MASK (0x3ff << 0) - -/* OMAP5430.BANDGAP_CTRL */ -#define OMAP5430_MASK_SIDLEMODE_MASK (0x3 << 30) -#define OMAP5430_MASK_COUNTER_DELAY_MASK (0x7 << 27) -#define OMAP5430_MASK_FREEZE_CORE_MASK BIT(23) -#define OMAP5430_MASK_FREEZE_GPU_MASK BIT(22) -#define OMAP5430_MASK_FREEZE_MPU_MASK BIT(21) -#define OMAP5430_MASK_CLEAR_CORE_MASK BIT(20) -#define OMAP5430_MASK_CLEAR_GPU_MASK BIT(19) -#define OMAP5430_MASK_CLEAR_MPU_MASK BIT(18) -#define OMAP5430_MASK_CLEAR_ACCUM_CORE_MASK BIT(17) -#define OMAP5430_MASK_CLEAR_ACCUM_GPU_MASK BIT(16) -#define OMAP5430_MASK_CLEAR_ACCUM_MPU_MASK BIT(15) -#define OMAP5430_MASK_HOT_CORE_MASK BIT(5) -#define OMAP5430_MASK_COLD_CORE_MASK BIT(4) -#define OMAP5430_MASK_HOT_GPU_MASK BIT(3) -#define OMAP5430_MASK_COLD_GPU_MASK BIT(2) -#define OMAP5430_MASK_HOT_MPU_MASK BIT(1) -#define OMAP5430_MASK_COLD_MPU_MASK BIT(0) - -/* OMAP5430.BANDGAP_COUNTER */ -#define OMAP5430_COUNTER_MASK (0xffffff << 0) - -/* OMAP5430.BANDGAP_THRESHOLD */ -#define OMAP5430_T_HOT_MASK (0x3ff << 16) -#define OMAP5430_T_COLD_MASK (0x3ff << 0) - -/* OMAP5430.TSHUT_THRESHOLD */ -#define OMAP5430_TSHUT_HOT_MASK (0x3ff << 16) -#define OMAP5430_TSHUT_COLD_MASK (0x3ff << 0) - -/* OMAP5430.BANDGAP_CUMUL_DTEMP_MPU */ -#define OMAP5430_CUMUL_DTEMP_MPU_MASK (0xffffffff << 0) - -/* OMAP5430.BANDGAP_CUMUL_DTEMP_GPU */ -#define OMAP5430_CUMUL_DTEMP_GPU_MASK (0xffffffff << 0) - -/* OMAP5430.BANDGAP_CUMUL_DTEMP_CORE */ -#define OMAP5430_CUMUL_DTEMP_CORE_MASK (0xffffffff << 0) - -/* OMAP5430.BANDGAP_STATUS */ -#define OMAP5430_BGAP_ALERT_MASK BIT(31) -#define OMAP5430_HOT_CORE_FLAG_MASK BIT(5) -#define OMAP5430_COLD_CORE_FLAG_MASK BIT(4) -#define OMAP5430_HOT_GPU_FLAG_MASK BIT(3) -#define OMAP5430_COLD_GPU_FLAG_MASK BIT(2) -#define OMAP5430_HOT_MPU_FLAG_MASK BIT(1) -#define OMAP5430_COLD_MPU_FLAG_MASK BIT(0) - -/** - * Temperature limits and thresholds for OMAP5430 - * - * All the macros bellow are definitions for handling the - * ADC conversions and representation of temperature limits - * and thresholds for OMAP5430. Definitions are grouped - * by temperature domain. - */ - -/* OMAP5430.common temperature definitions */ -/* ADC conversion table limits */ -#define OMAP5430_ADC_START_VALUE 540 -#define OMAP5430_ADC_END_VALUE 945 - -/* OMAP5430.GPU temperature definitions */ -/* bandgap clock limits */ -#define OMAP5430_GPU_MAX_FREQ 1500000 -#define OMAP5430_GPU_MIN_FREQ 1000000 -/* sensor limits */ -#define OMAP5430_GPU_MIN_TEMP -40000 -#define OMAP5430_GPU_MAX_TEMP 125000 -#define OMAP5430_GPU_HYST_VAL 5000 -/* interrupts thresholds */ -#define OMAP5430_GPU_TSHUT_HOT 915 -#define OMAP5430_GPU_TSHUT_COLD 900 -#define OMAP5430_GPU_T_HOT 800 -#define OMAP5430_GPU_T_COLD 795 - -/* OMAP5430.MPU temperature definitions */ -/* bandgap clock limits */ -#define OMAP5430_MPU_MAX_FREQ 1500000 -#define OMAP5430_MPU_MIN_FREQ 1000000 -/* sensor limits */ -#define OMAP5430_MPU_MIN_TEMP -40000 -#define OMAP5430_MPU_MAX_TEMP 125000 -#define OMAP5430_MPU_HYST_VAL 5000 -/* interrupts thresholds */ -#define OMAP5430_MPU_TSHUT_HOT 915 -#define OMAP5430_MPU_TSHUT_COLD 900 -#define OMAP5430_MPU_T_HOT 800 -#define OMAP5430_MPU_T_COLD 795 - -/* OMAP5430.CORE temperature definitions */ -/* bandgap clock limits */ -#define OMAP5430_CORE_MAX_FREQ 1500000 -#define OMAP5430_CORE_MIN_FREQ 1000000 -/* sensor limits */ -#define OMAP5430_CORE_MIN_TEMP -40000 -#define OMAP5430_CORE_MAX_TEMP 125000 -#define OMAP5430_CORE_HYST_VAL 5000 -/* interrupts thresholds */ -#define OMAP5430_CORE_TSHUT_HOT 915 -#define OMAP5430_CORE_TSHUT_COLD 900 -#define OMAP5430_CORE_T_HOT 800 -#define OMAP5430_CORE_T_COLD 795 - -#endif /* __OMAP5XXX_BANDGAP_H */ diff --git a/drivers/staging/ti-soc-thermal/ti-bandgap.c b/drivers/staging/ti-soc-thermal/ti-bandgap.c deleted file mode 100644 index f20c1cfe9800..000000000000 --- a/drivers/staging/ti-soc-thermal/ti-bandgap.c +++ /dev/null @@ -1,1546 +0,0 @@ -/* - * TI Bandgap temperature sensor driver - * - * Copyright (C) 2011-2012 Texas Instruments Incorporated - http://www.ti.com/ - * Author: J Keerthy - * Author: Moiz Sonasath - * Couple of fixes, DT and MFD adaptation: - * Eduardo Valentin - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ti-bandgap.h" - -/*** Helper functions to access registers and their bitfields ***/ - -/** - * ti_bandgap_readl() - simple read helper function - * @bgp: pointer to ti_bandgap structure - * @reg: desired register (offset) to be read - * - * Helper function to read bandgap registers. It uses the io remapped area. - * Return: the register value. - */ -static u32 ti_bandgap_readl(struct ti_bandgap *bgp, u32 reg) -{ - return readl(bgp->base + reg); -} - -/** - * ti_bandgap_writel() - simple write helper function - * @bgp: pointer to ti_bandgap structure - * @val: desired register value to be written - * @reg: desired register (offset) to be written - * - * Helper function to write bandgap registers. It uses the io remapped area. - */ -static void ti_bandgap_writel(struct ti_bandgap *bgp, u32 val, u32 reg) -{ - writel(val, bgp->base + reg); -} - -/** - * DOC: macro to update bits. - * - * RMW_BITS() - used to read, modify and update bandgap bitfields. - * The value passed will be shifted. - */ -#define RMW_BITS(bgp, id, reg, mask, val) \ -do { \ - struct temp_sensor_registers *t; \ - u32 r; \ - \ - t = bgp->conf->sensors[(id)].registers; \ - r = ti_bandgap_readl(bgp, t->reg); \ - r &= ~t->mask; \ - r |= (val) << __ffs(t->mask); \ - ti_bandgap_writel(bgp, r, t->reg); \ -} while (0) - -/*** Basic helper functions ***/ - -/** - * ti_bandgap_power() - controls the power state of a bandgap device - * @bgp: pointer to ti_bandgap structure - * @on: desired power state (1 - on, 0 - off) - * - * Used to power on/off a bandgap device instance. Only used on those - * that features tempsoff bit. - * - * Return: 0 on success, -ENOTSUPP if tempsoff is not supported. - */ -static int ti_bandgap_power(struct ti_bandgap *bgp, bool on) -{ - int i, ret = 0; - - if (!TI_BANDGAP_HAS(bgp, POWER_SWITCH)) { - ret = -ENOTSUPP; - goto exit; - } - - for (i = 0; i < bgp->conf->sensor_count; i++) - /* active on 0 */ - RMW_BITS(bgp, i, temp_sensor_ctrl, bgap_tempsoff_mask, !on); - -exit: - return ret; -} - -/** - * ti_bandgap_read_temp() - helper function to read sensor temperature - * @bgp: pointer to ti_bandgap structure - * @id: bandgap sensor id - * - * Function to concentrate the steps to read sensor temperature register. - * This function is desired because, depending on bandgap device version, - * it might be needed to freeze the bandgap state machine, before fetching - * the register value. - * - * Return: temperature in ADC values. - */ -static u32 ti_bandgap_read_temp(struct ti_bandgap *bgp, int id) -{ - struct temp_sensor_registers *tsr; - u32 temp, reg; - - tsr = bgp->conf->sensors[id].registers; - reg = tsr->temp_sensor_ctrl; - - if (TI_BANDGAP_HAS(bgp, FREEZE_BIT)) { - RMW_BITS(bgp, id, bgap_mask_ctrl, mask_freeze_mask, 1); - /* - * In case we cannot read from cur_dtemp / dtemp_0, - * then we read from the last valid temp read - */ - reg = tsr->ctrl_dtemp_1; - } - - /* read temperature */ - temp = ti_bandgap_readl(bgp, reg); - temp &= tsr->bgap_dtemp_mask; - - if (TI_BANDGAP_HAS(bgp, FREEZE_BIT)) - RMW_BITS(bgp, id, bgap_mask_ctrl, mask_freeze_mask, 0); - - return temp; -} - -/*** IRQ handlers ***/ - -/** - * ti_bandgap_talert_irq_handler() - handles Temperature alert IRQs - * @irq: IRQ number - * @data: private data (struct ti_bandgap *) - * - * This is the Talert handler. Use it only if bandgap device features - * HAS(TALERT). This handler goes over all sensors and checks their - * conditions and acts accordingly. In case there are events pending, - * it will reset the event mask to wait for the opposite event (next event). - * Every time there is a new event, it will be reported to thermal layer. - * - * Return: IRQ_HANDLED - */ -static irqreturn_t ti_bandgap_talert_irq_handler(int irq, void *data) -{ - struct ti_bandgap *bgp = data; - struct temp_sensor_registers *tsr; - u32 t_hot = 0, t_cold = 0, ctrl; - int i; - - spin_lock(&bgp->lock); - for (i = 0; i < bgp->conf->sensor_count; i++) { - tsr = bgp->conf->sensors[i].registers; - ctrl = ti_bandgap_readl(bgp, tsr->bgap_status); - - /* Read the status of t_hot */ - t_hot = ctrl & tsr->status_hot_mask; - - /* Read the status of t_cold */ - t_cold = ctrl & tsr->status_cold_mask; - - if (!t_cold && !t_hot) - continue; - - ctrl = ti_bandgap_readl(bgp, tsr->bgap_mask_ctrl); - /* - * One TALERT interrupt: Two sources - * If the interrupt is due to t_hot then mask t_hot and - * and unmask t_cold else mask t_cold and unmask t_hot - */ - if (t_hot) { - ctrl &= ~tsr->mask_hot_mask; - ctrl |= tsr->mask_cold_mask; - } else if (t_cold) { - ctrl &= ~tsr->mask_cold_mask; - ctrl |= tsr->mask_hot_mask; - } - - ti_bandgap_writel(bgp, ctrl, tsr->bgap_mask_ctrl); - - dev_dbg(bgp->dev, - "%s: IRQ from %s sensor: hotevent %d coldevent %d\n", - __func__, bgp->conf->sensors[i].domain, - t_hot, t_cold); - - /* report temperature to whom may concern */ - if (bgp->conf->report_temperature) - bgp->conf->report_temperature(bgp, i); - } - spin_unlock(&bgp->lock); - - return IRQ_HANDLED; -} - -/** - * ti_bandgap_tshut_irq_handler() - handles Temperature shutdown signal - * @irq: IRQ number - * @data: private data (unused) - * - * This is the Tshut handler. Use it only if bandgap device features - * HAS(TSHUT). If any sensor fires the Tshut signal, we simply shutdown - * the system. - * - * Return: IRQ_HANDLED - */ -static irqreturn_t ti_bandgap_tshut_irq_handler(int irq, void *data) -{ - pr_emerg("%s: TSHUT temperature reached. Needs shut down...\n", - __func__); - - orderly_poweroff(true); - - return IRQ_HANDLED; -} - -/*** Helper functions which manipulate conversion ADC <-> mi Celsius ***/ - -/** - * ti_bandgap_adc_to_mcelsius() - converts an ADC value to mCelsius scale - * @bgp: struct ti_bandgap pointer - * @adc_val: value in ADC representation - * @t: address where to write the resulting temperature in mCelsius - * - * Simple conversion from ADC representation to mCelsius. In case the ADC value - * is out of the ADC conv table range, it returns -ERANGE, 0 on success. - * The conversion table is indexed by the ADC values. - * - * Return: 0 if conversion was successful, else -ERANGE in case the @adc_val - * argument is out of the ADC conv table range. - */ -static -int ti_bandgap_adc_to_mcelsius(struct ti_bandgap *bgp, int adc_val, int *t) -{ - const struct ti_bandgap_data *conf = bgp->conf; - int ret = 0; - - /* look up for temperature in the table and return the temperature */ - if (adc_val < conf->adc_start_val || adc_val > conf->adc_end_val) { - ret = -ERANGE; - goto exit; - } - - *t = bgp->conf->conv_table[adc_val - conf->adc_start_val]; - -exit: - return ret; -} - -/** - * ti_bandgap_mcelsius_to_adc() - converts a mCelsius value to ADC scale - * @bgp: struct ti_bandgap pointer - * @temp: value in mCelsius - * @adc: address where to write the resulting temperature in ADC representation - * - * Simple conversion from mCelsius to ADC values. In case the temp value - * is out of the ADC conv table range, it returns -ERANGE, 0 on success. - * The conversion table is indexed by the ADC values. - * - * Return: 0 if conversion was successful, else -ERANGE in case the @temp - * argument is out of the ADC conv table range. - */ -static -int ti_bandgap_mcelsius_to_adc(struct ti_bandgap *bgp, long temp, int *adc) -{ - const struct ti_bandgap_data *conf = bgp->conf; - const int *conv_table = bgp->conf->conv_table; - int high, low, mid, ret = 0; - - low = 0; - high = conf->adc_end_val - conf->adc_start_val; - mid = (high + low) / 2; - - if (temp < conv_table[low] || temp > conv_table[high]) { - ret = -ERANGE; - goto exit; - } - - while (low < high) { - if (temp < conv_table[mid]) - high = mid - 1; - else - low = mid + 1; - mid = (low + high) / 2; - } - - *adc = conf->adc_start_val + low; - -exit: - return ret; -} - -/** - * ti_bandgap_add_hyst() - add hysteresis (in mCelsius) to an ADC value - * @bgp: struct ti_bandgap pointer - * @adc_val: temperature value in ADC representation - * @hyst_val: hysteresis value in mCelsius - * @sum: address where to write the resulting temperature (in ADC scale) - * - * Adds an hysteresis value (in mCelsius) to a ADC temperature value. - * - * Return: 0 on success, -ERANGE otherwise. - */ -static -int ti_bandgap_add_hyst(struct ti_bandgap *bgp, int adc_val, int hyst_val, - u32 *sum) -{ - int temp, ret; - - /* - * Need to add in the mcelsius domain, so we have a temperature - * the conv_table range - */ - ret = ti_bandgap_adc_to_mcelsius(bgp, adc_val, &temp); - if (ret < 0) - goto exit; - - temp += hyst_val; - - ret = ti_bandgap_mcelsius_to_adc(bgp, temp, sum); - -exit: - return ret; -} - -/*** Helper functions handling device Alert/Shutdown signals ***/ - -/** - * ti_bandgap_unmask_interrupts() - unmasks the events of thot & tcold - * @bgp: struct ti_bandgap pointer - * @id: bandgap sensor id - * @t_hot: hot temperature value to trigger alert signal - * @t_cold: cold temperature value to trigger alert signal - * - * Checks the requested t_hot and t_cold values and configures the IRQ event - * masks accordingly. Call this function only if bandgap features HAS(TALERT). - */ -static void ti_bandgap_unmask_interrupts(struct ti_bandgap *bgp, int id, - u32 t_hot, u32 t_cold) -{ - struct temp_sensor_registers *tsr; - u32 temp, reg_val; - - /* Read the current on die temperature */ - temp = ti_bandgap_read_temp(bgp, id); - - tsr = bgp->conf->sensors[id].registers; - reg_val = ti_bandgap_readl(bgp, tsr->bgap_mask_ctrl); - - if (temp < t_hot) - reg_val |= tsr->mask_hot_mask; - else - reg_val &= ~tsr->mask_hot_mask; - - if (t_cold < temp) - reg_val |= tsr->mask_cold_mask; - else - reg_val &= ~tsr->mask_cold_mask; - ti_bandgap_writel(bgp, reg_val, tsr->bgap_mask_ctrl); -} - -/** - * ti_bandgap_update_alert_threshold() - sequence to update thresholds - * @bgp: struct ti_bandgap pointer - * @id: bandgap sensor id - * @val: value (ADC) of a new threshold - * @hot: desired threshold to be updated. true if threshold hot, false if - * threshold cold - * - * It will program the required thresholds (hot and cold) for TALERT signal. - * This function can be used to update t_hot or t_cold, depending on @hot value. - * It checks the resulting t_hot and t_cold values, based on the new passed @val - * and configures the thresholds so that t_hot is always greater than t_cold. - * Call this function only if bandgap features HAS(TALERT). - * - * Return: 0 if no error, else corresponding error - */ -static int ti_bandgap_update_alert_threshold(struct ti_bandgap *bgp, int id, - int val, bool hot) -{ - struct temp_sensor_data *ts_data = bgp->conf->sensors[id].ts_data; - struct temp_sensor_registers *tsr; - u32 thresh_val, reg_val, t_hot, t_cold; - int err = 0; - - tsr = bgp->conf->sensors[id].registers; - - /* obtain the current value */ - thresh_val = ti_bandgap_readl(bgp, tsr->bgap_threshold); - t_cold = (thresh_val & tsr->threshold_tcold_mask) >> - __ffs(tsr->threshold_tcold_mask); - t_hot = (thresh_val & tsr->threshold_thot_mask) >> - __ffs(tsr->threshold_thot_mask); - if (hot) - t_hot = val; - else - t_cold = val; - - if (t_cold > t_hot) { - if (hot) - err = ti_bandgap_add_hyst(bgp, t_hot, - -ts_data->hyst_val, - &t_cold); - else - err = ti_bandgap_add_hyst(bgp, t_cold, - ts_data->hyst_val, - &t_hot); - } - - /* write the new threshold values */ - reg_val = thresh_val & - ~(tsr->threshold_thot_mask | tsr->threshold_tcold_mask); - reg_val |= (t_hot << __ffs(tsr->threshold_thot_mask)) | - (t_cold << __ffs(tsr->threshold_tcold_mask)); - ti_bandgap_writel(bgp, reg_val, tsr->bgap_threshold); - - if (err) { - dev_err(bgp->dev, "failed to reprogram thot threshold\n"); - err = -EIO; - goto exit; - } - - ti_bandgap_unmask_interrupts(bgp, id, t_hot, t_cold); -exit: - return err; -} - -/** - * ti_bandgap_validate() - helper to check the sanity of a struct ti_bandgap - * @bgp: struct ti_bandgap pointer - * @id: bandgap sensor id - * - * Checks if the bandgap pointer is valid and if the sensor id is also - * applicable. - * - * Return: 0 if no errors, -EINVAL for invalid @bgp pointer or -ERANGE if - * @id cannot index @bgp sensors. - */ -static inline int ti_bandgap_validate(struct ti_bandgap *bgp, int id) -{ - int ret = 0; - - if (IS_ERR_OR_NULL(bgp)) { - pr_err("%s: invalid bandgap pointer\n", __func__); - ret = -EINVAL; - goto exit; - } - - if ((id < 0) || (id >= bgp->conf->sensor_count)) { - dev_err(bgp->dev, "%s: sensor id out of range (%d)\n", - __func__, id); - ret = -ERANGE; - } - -exit: - return ret; -} - -/** - * _ti_bandgap_write_threshold() - helper to update TALERT t_cold or t_hot - * @bgp: struct ti_bandgap pointer - * @id: bandgap sensor id - * @val: value (mCelsius) of a new threshold - * @hot: desired threshold to be updated. true if threshold hot, false if - * threshold cold - * - * It will update the required thresholds (hot and cold) for TALERT signal. - * This function can be used to update t_hot or t_cold, depending on @hot value. - * Validates the mCelsius range and update the requested threshold. - * Call this function only if bandgap features HAS(TALERT). - * - * Return: 0 if no error, else corresponding error value. - */ -static int _ti_bandgap_write_threshold(struct ti_bandgap *bgp, int id, int val, - bool hot) -{ - struct temp_sensor_data *ts_data; - struct temp_sensor_registers *tsr; - u32 adc_val; - int ret; - - ret = ti_bandgap_validate(bgp, id); - if (ret) - goto exit; - - if (!TI_BANDGAP_HAS(bgp, TALERT)) { - ret = -ENOTSUPP; - goto exit; - } - - ts_data = bgp->conf->sensors[id].ts_data; - tsr = bgp->conf->sensors[id].registers; - if (hot) { - if (val < ts_data->min_temp + ts_data->hyst_val) - ret = -EINVAL; - } else { - if (val > ts_data->max_temp + ts_data->hyst_val) - ret = -EINVAL; - } - - if (ret) - goto exit; - - ret = ti_bandgap_mcelsius_to_adc(bgp, val, &adc_val); - if (ret < 0) - goto exit; - - spin_lock(&bgp->lock); - ret = ti_bandgap_update_alert_threshold(bgp, id, adc_val, hot); - spin_unlock(&bgp->lock); - -exit: - return ret; -} - -/** - * _ti_bandgap_read_threshold() - helper to read TALERT t_cold or t_hot - * @bgp: struct ti_bandgap pointer - * @id: bandgap sensor id - * @val: value (mCelsius) of a threshold - * @hot: desired threshold to be read. true if threshold hot, false if - * threshold cold - * - * It will fetch the required thresholds (hot and cold) for TALERT signal. - * This function can be used to read t_hot or t_cold, depending on @hot value. - * Call this function only if bandgap features HAS(TALERT). - * - * Return: 0 if no error, -ENOTSUPP if it has no TALERT support, or the - * corresponding error value if some operation fails. - */ -static int _ti_bandgap_read_threshold(struct ti_bandgap *bgp, int id, - int *val, bool hot) -{ - struct temp_sensor_registers *tsr; - u32 temp, mask; - int ret = 0; - - ret = ti_bandgap_validate(bgp, id); - if (ret) - goto exit; - - if (!TI_BANDGAP_HAS(bgp, TALERT)) { - ret = -ENOTSUPP; - goto exit; - } - - tsr = bgp->conf->sensors[id].registers; - if (hot) - mask = tsr->threshold_thot_mask; - else - mask = tsr->threshold_tcold_mask; - - temp = ti_bandgap_readl(bgp, tsr->bgap_threshold); - temp = (temp & mask) >> __ffs(mask); - ret |= ti_bandgap_adc_to_mcelsius(bgp, temp, &temp); - if (ret) { - dev_err(bgp->dev, "failed to read thot\n"); - ret = -EIO; - goto exit; - } - - *val = temp; - -exit: - return ret; -} - -/*** Exposed APIs ***/ - -/** - * ti_bandgap_read_thot() - reads sensor current thot - * @bgp: pointer to bandgap instance - * @id: sensor id - * @thot: resulting current thot value - * - * Return: 0 on success or the proper error code - */ -int ti_bandgap_read_thot(struct ti_bandgap *bgp, int id, int *thot) -{ - return _ti_bandgap_read_threshold(bgp, id, thot, true); -} - -/** - * ti_bandgap_write_thot() - sets sensor current thot - * @bgp: pointer to bandgap instance - * @id: sensor id - * @val: desired thot value - * - * Return: 0 on success or the proper error code - */ -int ti_bandgap_write_thot(struct ti_bandgap *bgp, int id, int val) -{ - return _ti_bandgap_write_threshold(bgp, id, val, true); -} - -/** - * ti_bandgap_read_tcold() - reads sensor current tcold - * @bgp: pointer to bandgap instance - * @id: sensor id - * @tcold: resulting current tcold value - * - * Return: 0 on success or the proper error code - */ -int ti_bandgap_read_tcold(struct ti_bandgap *bgp, int id, int *tcold) -{ - return _ti_bandgap_read_threshold(bgp, id, tcold, false); -} - -/** - * ti_bandgap_write_tcold() - sets the sensor tcold - * @bgp: pointer to bandgap instance - * @id: sensor id - * @val: desired tcold value - * - * Return: 0 on success or the proper error code - */ -int ti_bandgap_write_tcold(struct ti_bandgap *bgp, int id, int val) -{ - return _ti_bandgap_write_threshold(bgp, id, val, false); -} - -/** - * ti_bandgap_read_counter() - read the sensor counter - * @bgp: pointer to bandgap instance - * @id: sensor id - * @interval: resulting update interval in miliseconds - */ -static void ti_bandgap_read_counter(struct ti_bandgap *bgp, int id, - int *interval) -{ - struct temp_sensor_registers *tsr; - int time; - - tsr = bgp->conf->sensors[id].registers; - time = ti_bandgap_readl(bgp, tsr->bgap_counter); - time = (time & tsr->counter_mask) >> - __ffs(tsr->counter_mask); - time = time * 1000 / bgp->clk_rate; - *interval = time; -} - -/** - * ti_bandgap_read_counter_delay() - read the sensor counter delay - * @bgp: pointer to bandgap instance - * @id: sensor id - * @interval: resulting update interval in miliseconds - */ -static void ti_bandgap_read_counter_delay(struct ti_bandgap *bgp, int id, - int *interval) -{ - struct temp_sensor_registers *tsr; - int reg_val; - - tsr = bgp->conf->sensors[id].registers; - - reg_val = ti_bandgap_readl(bgp, tsr->bgap_mask_ctrl); - reg_val = (reg_val & tsr->mask_counter_delay_mask) >> - __ffs(tsr->mask_counter_delay_mask); - switch (reg_val) { - case 0: - *interval = 0; - break; - case 1: - *interval = 1; - break; - case 2: - *interval = 10; - break; - case 3: - *interval = 100; - break; - case 4: - *interval = 250; - break; - case 5: - *interval = 500; - break; - default: - dev_warn(bgp->dev, "Wrong counter delay value read from register %X", - reg_val); - } -} - -/** - * ti_bandgap_read_update_interval() - read the sensor update interval - * @bgp: pointer to bandgap instance - * @id: sensor id - * @interval: resulting update interval in miliseconds - * - * Return: 0 on success or the proper error code - */ -int ti_bandgap_read_update_interval(struct ti_bandgap *bgp, int id, - int *interval) -{ - int ret = 0; - - ret = ti_bandgap_validate(bgp, id); - if (ret) - goto exit; - - if (!TI_BANDGAP_HAS(bgp, COUNTER) && - !TI_BANDGAP_HAS(bgp, COUNTER_DELAY)) { - ret = -ENOTSUPP; - goto exit; - } - - if (TI_BANDGAP_HAS(bgp, COUNTER)) { - ti_bandgap_read_counter(bgp, id, interval); - goto exit; - } - - ti_bandgap_read_counter_delay(bgp, id, interval); -exit: - return ret; -} - -/** - * ti_bandgap_write_counter_delay() - set the counter_delay - * @bgp: pointer to bandgap instance - * @id: sensor id - * @interval: desired update interval in miliseconds - * - * Return: 0 on success or the proper error code - */ -static int ti_bandgap_write_counter_delay(struct ti_bandgap *bgp, int id, - u32 interval) -{ - int rval; - - switch (interval) { - case 0: /* Immediate conversion */ - rval = 0x0; - break; - case 1: /* Conversion after ever 1ms */ - rval = 0x1; - break; - case 10: /* Conversion after ever 10ms */ - rval = 0x2; - break; - case 100: /* Conversion after ever 100ms */ - rval = 0x3; - break; - case 250: /* Conversion after ever 250ms */ - rval = 0x4; - break; - case 500: /* Conversion after ever 500ms */ - rval = 0x5; - break; - default: - dev_warn(bgp->dev, "Delay %d ms is not supported\n", interval); - return -EINVAL; - } - - spin_lock(&bgp->lock); - RMW_BITS(bgp, id, bgap_mask_ctrl, mask_counter_delay_mask, rval); - spin_unlock(&bgp->lock); - - return 0; -} - -/** - * ti_bandgap_write_counter() - set the bandgap sensor counter - * @bgp: pointer to bandgap instance - * @id: sensor id - * @interval: desired update interval in miliseconds - */ -static void ti_bandgap_write_counter(struct ti_bandgap *bgp, int id, - u32 interval) -{ - interval = interval * bgp->clk_rate / 1000; - spin_lock(&bgp->lock); - RMW_BITS(bgp, id, bgap_counter, counter_mask, interval); - spin_unlock(&bgp->lock); -} - -/** - * ti_bandgap_write_update_interval() - set the update interval - * @bgp: pointer to bandgap instance - * @id: sensor id - * @interval: desired update interval in miliseconds - * - * Return: 0 on success or the proper error code - */ -int ti_bandgap_write_update_interval(struct ti_bandgap *bgp, - int id, u32 interval) -{ - int ret = ti_bandgap_validate(bgp, id); - if (ret) - goto exit; - - if (!TI_BANDGAP_HAS(bgp, COUNTER) && - !TI_BANDGAP_HAS(bgp, COUNTER_DELAY)) { - ret = -ENOTSUPP; - goto exit; - } - - if (TI_BANDGAP_HAS(bgp, COUNTER)) { - ti_bandgap_write_counter(bgp, id, interval); - goto exit; - } - - ret = ti_bandgap_write_counter_delay(bgp, id, interval); -exit: - return ret; -} - -/** - * ti_bandgap_read_temperature() - report current temperature - * @bgp: pointer to bandgap instance - * @id: sensor id - * @temperature: resulting temperature - * - * Return: 0 on success or the proper error code - */ -int ti_bandgap_read_temperature(struct ti_bandgap *bgp, int id, - int *temperature) -{ - u32 temp; - int ret; - - ret = ti_bandgap_validate(bgp, id); - if (ret) - return ret; - - spin_lock(&bgp->lock); - temp = ti_bandgap_read_temp(bgp, id); - spin_unlock(&bgp->lock); - - ret |= ti_bandgap_adc_to_mcelsius(bgp, temp, &temp); - if (ret) - return -EIO; - - *temperature = temp; - - return 0; -} - -/** - * ti_bandgap_set_sensor_data() - helper function to store thermal - * framework related data. - * @bgp: pointer to bandgap instance - * @id: sensor id - * @data: thermal framework related data to be stored - * - * Return: 0 on success or the proper error code - */ -int ti_bandgap_set_sensor_data(struct ti_bandgap *bgp, int id, void *data) -{ - int ret = ti_bandgap_validate(bgp, id); - if (ret) - return ret; - - bgp->regval[id].data = data; - - return 0; -} - -/** - * ti_bandgap_get_sensor_data() - helper function to get thermal - * framework related data. - * @bgp: pointer to bandgap instance - * @id: sensor id - * - * Return: data stored by set function with sensor id on success or NULL - */ -void *ti_bandgap_get_sensor_data(struct ti_bandgap *bgp, int id) -{ - int ret = ti_bandgap_validate(bgp, id); - if (ret) - return ERR_PTR(ret); - - return bgp->regval[id].data; -} - -/*** Helper functions used during device initialization ***/ - -/** - * ti_bandgap_force_single_read() - executes 1 single ADC conversion - * @bgp: pointer to struct ti_bandgap - * @id: sensor id which it is desired to read 1 temperature - * - * Used to initialize the conversion state machine and set it to a valid - * state. Called during device initialization and context restore events. - * - * Return: 0 - */ -static int -ti_bandgap_force_single_read(struct ti_bandgap *bgp, int id) -{ - u32 temp = 0, counter = 1000; - - /* Select single conversion mode */ - if (TI_BANDGAP_HAS(bgp, MODE_CONFIG)) - RMW_BITS(bgp, id, bgap_mode_ctrl, mode_ctrl_mask, 0); - - /* Start of Conversion = 1 */ - RMW_BITS(bgp, id, temp_sensor_ctrl, bgap_soc_mask, 1); - /* Wait until DTEMP is updated */ - temp = ti_bandgap_read_temp(bgp, id); - - while ((temp == 0) && --counter) - temp = ti_bandgap_read_temp(bgp, id); - /* REVISIT: Check correct condition for end of conversion */ - - /* Start of Conversion = 0 */ - RMW_BITS(bgp, id, temp_sensor_ctrl, bgap_soc_mask, 0); - - return 0; -} - -/** - * ti_bandgap_set_continous_mode() - One time enabling of continuous mode - * @bgp: pointer to struct ti_bandgap - * - * Call this function only if HAS(MODE_CONFIG) is set. As this driver may - * be used for junction temperature monitoring, it is desirable that the - * sensors are operational all the time, so that alerts are generated - * properly. - * - * Return: 0 - */ -static int ti_bandgap_set_continuous_mode(struct ti_bandgap *bgp) -{ - int i; - - for (i = 0; i < bgp->conf->sensor_count; i++) { - /* Perform a single read just before enabling continuous */ - ti_bandgap_force_single_read(bgp, i); - RMW_BITS(bgp, i, bgap_mode_ctrl, mode_ctrl_mask, 1); - } - - return 0; -} - -/** - * ti_bandgap_get_trend() - To fetch the temperature trend of a sensor - * @bgp: pointer to struct ti_bandgap - * @id: id of the individual sensor - * @trend: Pointer to trend. - * - * This function needs to be called to fetch the temperature trend of a - * Particular sensor. The function computes the difference in temperature - * w.r.t time. For the bandgaps with built in history buffer the temperatures - * are read from the buffer and for those without the Buffer -ENOTSUPP is - * returned. - * - * Return: 0 if no error, else return corresponding error. If no - * error then the trend value is passed on to trend parameter - */ -int ti_bandgap_get_trend(struct ti_bandgap *bgp, int id, int *trend) -{ - struct temp_sensor_registers *tsr; - u32 temp1, temp2, reg1, reg2; - int t1, t2, interval, ret = 0; - - ret = ti_bandgap_validate(bgp, id); - if (ret) - goto exit; - - if (!TI_BANDGAP_HAS(bgp, HISTORY_BUFFER) || - !TI_BANDGAP_HAS(bgp, FREEZE_BIT)) { - ret = -ENOTSUPP; - goto exit; - } - - tsr = bgp->conf->sensors[id].registers; - - /* Freeze and read the last 2 valid readings */ - reg1 = tsr->ctrl_dtemp_1; - reg2 = tsr->ctrl_dtemp_2; - - /* read temperature from history buffer */ - temp1 = ti_bandgap_readl(bgp, reg1); - temp1 &= tsr->bgap_dtemp_mask; - - temp2 = ti_bandgap_readl(bgp, reg2); - temp2 &= tsr->bgap_dtemp_mask; - - /* Convert from adc values to mCelsius temperature */ - ret = ti_bandgap_adc_to_mcelsius(bgp, temp1, &t1); - if (ret) - goto exit; - - ret = ti_bandgap_adc_to_mcelsius(bgp, temp2, &t2); - if (ret) - goto exit; - - /* Fetch the update interval */ - ret = ti_bandgap_read_update_interval(bgp, id, &interval); - if (ret || !interval) - goto exit; - - *trend = (t1 - t2) / interval; - - dev_dbg(bgp->dev, "The temperatures are t1 = %d and t2 = %d and trend =%d\n", - t1, t2, *trend); - -exit: - return ret; -} - -/** - * ti_bandgap_tshut_init() - setup and initialize tshut handling - * @bgp: pointer to struct ti_bandgap - * @pdev: pointer to device struct platform_device - * - * Call this function only in case the bandgap features HAS(TSHUT). - * In this case, the driver needs to handle the TSHUT signal as an IRQ. - * The IRQ is wired as a GPIO, and for this purpose, it is required - * to specify which GPIO line is used. TSHUT IRQ is fired anytime - * one of the bandgap sensors violates the TSHUT high/hot threshold. - * And in that case, the system must go off. - * - * Return: 0 if no error, else error status - */ -static int ti_bandgap_tshut_init(struct ti_bandgap *bgp, - struct platform_device *pdev) -{ - int gpio_nr = bgp->tshut_gpio; - int status; - - /* Request for gpio_86 line */ - status = gpio_request(gpio_nr, "tshut"); - if (status < 0) { - dev_err(bgp->dev, "Could not request for TSHUT GPIO:%i\n", 86); - return status; - } - status = gpio_direction_input(gpio_nr); - if (status) { - dev_err(bgp->dev, "Cannot set input TSHUT GPIO %d\n", gpio_nr); - return status; - } - - status = request_irq(gpio_to_irq(gpio_nr), ti_bandgap_tshut_irq_handler, - IRQF_TRIGGER_RISING, "tshut", NULL); - if (status) { - gpio_free(gpio_nr); - dev_err(bgp->dev, "request irq failed for TSHUT"); - } - - return 0; -} - -/** - * ti_bandgap_alert_init() - setup and initialize talert handling - * @bgp: pointer to struct ti_bandgap - * @pdev: pointer to device struct platform_device - * - * Call this function only in case the bandgap features HAS(TALERT). - * In this case, the driver needs to handle the TALERT signals as an IRQs. - * TALERT is a normal IRQ and it is fired any time thresholds (hot or cold) - * are violated. In these situation, the driver must reprogram the thresholds, - * accordingly to specified policy. - * - * Return: 0 if no error, else return corresponding error. - */ -static int ti_bandgap_talert_init(struct ti_bandgap *bgp, - struct platform_device *pdev) -{ - int ret; - - bgp->irq = platform_get_irq(pdev, 0); - if (bgp->irq < 0) { - dev_err(&pdev->dev, "get_irq failed\n"); - return bgp->irq; - } - ret = request_threaded_irq(bgp->irq, NULL, - ti_bandgap_talert_irq_handler, - IRQF_TRIGGER_HIGH | IRQF_ONESHOT, - "talert", bgp); - if (ret) { - dev_err(&pdev->dev, "Request threaded irq failed.\n"); - return ret; - } - - return 0; -} - -static const struct of_device_id of_ti_bandgap_match[]; -/** - * ti_bandgap_build() - parse DT and setup a struct ti_bandgap - * @pdev: pointer to device struct platform_device - * - * Used to read the device tree properties accordingly to the bandgap - * matching version. Based on bandgap version and its capabilities it - * will build a struct ti_bandgap out of the required DT entries. - * - * Return: valid bandgap structure if successful, else returns ERR_PTR - * return value must be verified with IS_ERR. - */ -static struct ti_bandgap *ti_bandgap_build(struct platform_device *pdev) -{ - struct device_node *node = pdev->dev.of_node; - const struct of_device_id *of_id; - struct ti_bandgap *bgp; - struct resource *res; - u32 prop; - int i; - - /* just for the sake */ - if (!node) { - dev_err(&pdev->dev, "no platform information available\n"); - return ERR_PTR(-EINVAL); - } - - bgp = devm_kzalloc(&pdev->dev, sizeof(*bgp), GFP_KERNEL); - if (!bgp) { - dev_err(&pdev->dev, "Unable to allocate mem for driver ref\n"); - return ERR_PTR(-ENOMEM); - } - - of_id = of_match_device(of_ti_bandgap_match, &pdev->dev); - if (of_id) - bgp->conf = of_id->data; - - /* register shadow for context save and restore */ - bgp->regval = devm_kzalloc(&pdev->dev, sizeof(*bgp->regval) * - bgp->conf->sensor_count, GFP_KERNEL); - if (!bgp) { - dev_err(&pdev->dev, "Unable to allocate mem for driver ref\n"); - return ERR_PTR(-ENOMEM); - } - - i = 0; - do { - void __iomem *chunk; - - res = platform_get_resource(pdev, IORESOURCE_MEM, i); - if (!res) - break; - chunk = devm_ioremap_resource(&pdev->dev, res); - if (i == 0) - bgp->base = chunk; - if (IS_ERR(chunk)) - return ERR_CAST(chunk); - - i++; - } while (res); - - if (TI_BANDGAP_HAS(bgp, TSHUT)) { - if (of_property_read_u32(node, "ti,tshut-gpio", &prop) < 0) { - dev_err(&pdev->dev, "missing tshut gpio in device tree\n"); - return ERR_PTR(-EINVAL); - } - bgp->tshut_gpio = prop; - if (!gpio_is_valid(bgp->tshut_gpio)) { - dev_err(&pdev->dev, "invalid gpio for tshut (%d)\n", - bgp->tshut_gpio); - return ERR_PTR(-EINVAL); - } - } - - return bgp; -} - -/*** Device driver call backs ***/ - -static -int ti_bandgap_probe(struct platform_device *pdev) -{ - struct ti_bandgap *bgp; - int clk_rate, ret = 0, i; - - bgp = ti_bandgap_build(pdev); - if (IS_ERR_OR_NULL(bgp)) { - dev_err(&pdev->dev, "failed to fetch platform data\n"); - return PTR_ERR(bgp); - } - bgp->dev = &pdev->dev; - - if (TI_BANDGAP_HAS(bgp, TSHUT)) { - ret = ti_bandgap_tshut_init(bgp, pdev); - if (ret) { - dev_err(&pdev->dev, - "failed to initialize system tshut IRQ\n"); - return ret; - } - } - - bgp->fclock = clk_get(NULL, bgp->conf->fclock_name); - ret = IS_ERR_OR_NULL(bgp->fclock); - if (ret) { - dev_err(&pdev->dev, "failed to request fclock reference\n"); - goto free_irqs; - } - - bgp->div_clk = clk_get(NULL, bgp->conf->div_ck_name); - ret = IS_ERR_OR_NULL(bgp->div_clk); - if (ret) { - dev_err(&pdev->dev, - "failed to request div_ts_ck clock ref\n"); - goto free_irqs; - } - - for (i = 0; i < bgp->conf->sensor_count; i++) { - struct temp_sensor_registers *tsr; - u32 val; - - tsr = bgp->conf->sensors[i].registers; - /* - * check if the efuse has a non-zero value if not - * it is an untrimmed sample and the temperatures - * may not be accurate - */ - val = ti_bandgap_readl(bgp, tsr->bgap_efuse); - if (ret || !val) - dev_info(&pdev->dev, - "Non-trimmed BGAP, Temp not accurate\n"); - } - - clk_rate = clk_round_rate(bgp->div_clk, - bgp->conf->sensors[0].ts_data->max_freq); - if (clk_rate < bgp->conf->sensors[0].ts_data->min_freq || - clk_rate == 0xffffffff) { - ret = -ENODEV; - dev_err(&pdev->dev, "wrong clock rate (%d)\n", clk_rate); - goto put_clks; - } - - ret = clk_set_rate(bgp->div_clk, clk_rate); - if (ret) - dev_err(&pdev->dev, "Cannot re-set clock rate. Continuing\n"); - - bgp->clk_rate = clk_rate; - if (TI_BANDGAP_HAS(bgp, CLK_CTRL)) - clk_prepare_enable(bgp->fclock); - - - spin_lock_init(&bgp->lock); - bgp->dev = &pdev->dev; - platform_set_drvdata(pdev, bgp); - - ti_bandgap_power(bgp, true); - - /* Set default counter to 1 for now */ - if (TI_BANDGAP_HAS(bgp, COUNTER)) - for (i = 0; i < bgp->conf->sensor_count; i++) - RMW_BITS(bgp, i, bgap_counter, counter_mask, 1); - - /* Set default thresholds for alert and shutdown */ - for (i = 0; i < bgp->conf->sensor_count; i++) { - struct temp_sensor_data *ts_data; - - ts_data = bgp->conf->sensors[i].ts_data; - - if (TI_BANDGAP_HAS(bgp, TALERT)) { - /* Set initial Talert thresholds */ - RMW_BITS(bgp, i, bgap_threshold, - threshold_tcold_mask, ts_data->t_cold); - RMW_BITS(bgp, i, bgap_threshold, - threshold_thot_mask, ts_data->t_hot); - /* Enable the alert events */ - RMW_BITS(bgp, i, bgap_mask_ctrl, mask_hot_mask, 1); - RMW_BITS(bgp, i, bgap_mask_ctrl, mask_cold_mask, 1); - } - - if (TI_BANDGAP_HAS(bgp, TSHUT_CONFIG)) { - /* Set initial Tshut thresholds */ - RMW_BITS(bgp, i, tshut_threshold, - tshut_hot_mask, ts_data->tshut_hot); - RMW_BITS(bgp, i, tshut_threshold, - tshut_cold_mask, ts_data->tshut_cold); - } - } - - if (TI_BANDGAP_HAS(bgp, MODE_CONFIG)) - ti_bandgap_set_continuous_mode(bgp); - - /* Set .250 seconds time as default counter */ - if (TI_BANDGAP_HAS(bgp, COUNTER)) - for (i = 0; i < bgp->conf->sensor_count; i++) - RMW_BITS(bgp, i, bgap_counter, counter_mask, - bgp->clk_rate / 4); - - /* Every thing is good? Then expose the sensors */ - for (i = 0; i < bgp->conf->sensor_count; i++) { - char *domain; - - if (bgp->conf->sensors[i].register_cooling) { - ret = bgp->conf->sensors[i].register_cooling(bgp, i); - if (ret) - goto remove_sensors; - } - - if (bgp->conf->expose_sensor) { - domain = bgp->conf->sensors[i].domain; - ret = bgp->conf->expose_sensor(bgp, i, domain); - if (ret) - goto remove_last_cooling; - } - } - - /* - * Enable the Interrupts once everything is set. Otherwise irq handler - * might be called as soon as it is enabled where as rest of framework - * is still getting initialised. - */ - if (TI_BANDGAP_HAS(bgp, TALERT)) { - ret = ti_bandgap_talert_init(bgp, pdev); - if (ret) { - dev_err(&pdev->dev, "failed to initialize Talert IRQ\n"); - i = bgp->conf->sensor_count; - goto disable_clk; - } - } - - return 0; - -remove_last_cooling: - if (bgp->conf->sensors[i].unregister_cooling) - bgp->conf->sensors[i].unregister_cooling(bgp, i); -remove_sensors: - for (i--; i >= 0; i--) { - if (bgp->conf->sensors[i].unregister_cooling) - bgp->conf->sensors[i].unregister_cooling(bgp, i); - if (bgp->conf->remove_sensor) - bgp->conf->remove_sensor(bgp, i); - } - ti_bandgap_power(bgp, false); -disable_clk: - if (TI_BANDGAP_HAS(bgp, CLK_CTRL)) - clk_disable_unprepare(bgp->fclock); -put_clks: - clk_put(bgp->fclock); - clk_put(bgp->div_clk); -free_irqs: - if (TI_BANDGAP_HAS(bgp, TSHUT)) { - free_irq(gpio_to_irq(bgp->tshut_gpio), NULL); - gpio_free(bgp->tshut_gpio); - } - - return ret; -} - -static -int ti_bandgap_remove(struct platform_device *pdev) -{ - struct ti_bandgap *bgp = platform_get_drvdata(pdev); - int i; - - /* First thing is to remove sensor interfaces */ - for (i = 0; i < bgp->conf->sensor_count; i++) { - if (bgp->conf->sensors[i].unregister_cooling) - bgp->conf->sensors[i].unregister_cooling(bgp, i); - - if (bgp->conf->remove_sensor) - bgp->conf->remove_sensor(bgp, i); - } - - ti_bandgap_power(bgp, false); - - if (TI_BANDGAP_HAS(bgp, CLK_CTRL)) - clk_disable_unprepare(bgp->fclock); - clk_put(bgp->fclock); - clk_put(bgp->div_clk); - - if (TI_BANDGAP_HAS(bgp, TALERT)) - free_irq(bgp->irq, bgp); - - if (TI_BANDGAP_HAS(bgp, TSHUT)) { - free_irq(gpio_to_irq(bgp->tshut_gpio), NULL); - gpio_free(bgp->tshut_gpio); - } - - return 0; -} - -#ifdef CONFIG_PM -static int ti_bandgap_save_ctxt(struct ti_bandgap *bgp) -{ - int i; - - for (i = 0; i < bgp->conf->sensor_count; i++) { - struct temp_sensor_registers *tsr; - struct temp_sensor_regval *rval; - - rval = &bgp->regval[i]; - tsr = bgp->conf->sensors[i].registers; - - if (TI_BANDGAP_HAS(bgp, MODE_CONFIG)) - rval->bg_mode_ctrl = ti_bandgap_readl(bgp, - tsr->bgap_mode_ctrl); - if (TI_BANDGAP_HAS(bgp, COUNTER)) - rval->bg_counter = ti_bandgap_readl(bgp, - tsr->bgap_counter); - if (TI_BANDGAP_HAS(bgp, TALERT)) { - rval->bg_threshold = ti_bandgap_readl(bgp, - tsr->bgap_threshold); - rval->bg_ctrl = ti_bandgap_readl(bgp, - tsr->bgap_mask_ctrl); - } - - if (TI_BANDGAP_HAS(bgp, TSHUT_CONFIG)) - rval->tshut_threshold = ti_bandgap_readl(bgp, - tsr->tshut_threshold); - } - - return 0; -} - -static int ti_bandgap_restore_ctxt(struct ti_bandgap *bgp) -{ - int i; - - for (i = 0; i < bgp->conf->sensor_count; i++) { - struct temp_sensor_registers *tsr; - struct temp_sensor_regval *rval; - u32 val = 0; - - rval = &bgp->regval[i]; - tsr = bgp->conf->sensors[i].registers; - - if (TI_BANDGAP_HAS(bgp, COUNTER)) - val = ti_bandgap_readl(bgp, tsr->bgap_counter); - - if (TI_BANDGAP_HAS(bgp, TSHUT_CONFIG)) - ti_bandgap_writel(bgp, rval->tshut_threshold, - tsr->tshut_threshold); - /* Force immediate temperature measurement and update - * of the DTEMP field - */ - ti_bandgap_force_single_read(bgp, i); - - if (TI_BANDGAP_HAS(bgp, COUNTER)) - ti_bandgap_writel(bgp, rval->bg_counter, - tsr->bgap_counter); - if (TI_BANDGAP_HAS(bgp, MODE_CONFIG)) - ti_bandgap_writel(bgp, rval->bg_mode_ctrl, - tsr->bgap_mode_ctrl); - if (TI_BANDGAP_HAS(bgp, TALERT)) { - ti_bandgap_writel(bgp, rval->bg_threshold, - tsr->bgap_threshold); - ti_bandgap_writel(bgp, rval->bg_ctrl, - tsr->bgap_mask_ctrl); - } - } - - return 0; -} - -static int ti_bandgap_suspend(struct device *dev) -{ - struct ti_bandgap *bgp = dev_get_drvdata(dev); - int err; - - err = ti_bandgap_save_ctxt(bgp); - ti_bandgap_power(bgp, false); - - if (TI_BANDGAP_HAS(bgp, CLK_CTRL)) - clk_disable_unprepare(bgp->fclock); - - return err; -} - -static int ti_bandgap_resume(struct device *dev) -{ - struct ti_bandgap *bgp = dev_get_drvdata(dev); - - if (TI_BANDGAP_HAS(bgp, CLK_CTRL)) - clk_prepare_enable(bgp->fclock); - - ti_bandgap_power(bgp, true); - - return ti_bandgap_restore_ctxt(bgp); -} -static const struct dev_pm_ops ti_bandgap_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(ti_bandgap_suspend, - ti_bandgap_resume) -}; - -#define DEV_PM_OPS (&ti_bandgap_dev_pm_ops) -#else -#define DEV_PM_OPS NULL -#endif - -static const struct of_device_id of_ti_bandgap_match[] = { -#ifdef CONFIG_OMAP4_THERMAL - { - .compatible = "ti,omap4430-bandgap", - .data = (void *)&omap4430_data, - }, - { - .compatible = "ti,omap4460-bandgap", - .data = (void *)&omap4460_data, - }, - { - .compatible = "ti,omap4470-bandgap", - .data = (void *)&omap4470_data, - }, -#endif -#ifdef CONFIG_OMAP5_THERMAL - { - .compatible = "ti,omap5430-bandgap", - .data = (void *)&omap5430_data, - }, -#endif - /* Sentinel */ - { }, -}; -MODULE_DEVICE_TABLE(of, of_ti_bandgap_match); - -static struct platform_driver ti_bandgap_sensor_driver = { - .probe = ti_bandgap_probe, - .remove = ti_bandgap_remove, - .driver = { - .name = "ti-soc-thermal", - .pm = DEV_PM_OPS, - .of_match_table = of_ti_bandgap_match, - }, -}; - -module_platform_driver(ti_bandgap_sensor_driver); - -MODULE_DESCRIPTION("OMAP4+ bandgap temperature sensor driver"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:ti-soc-thermal"); -MODULE_AUTHOR("Texas Instrument Inc."); diff --git a/drivers/staging/ti-soc-thermal/ti-bandgap.h b/drivers/staging/ti-soc-thermal/ti-bandgap.h deleted file mode 100644 index 5f4794abf583..000000000000 --- a/drivers/staging/ti-soc-thermal/ti-bandgap.h +++ /dev/null @@ -1,403 +0,0 @@ -/* - * OMAP4 Bandgap temperature sensor driver - * - * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ - * Contact: - * Eduardo Valentin - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ -#ifndef __TI_BANDGAP_H -#define __TI_BANDGAP_H - -#include -#include -#include - -/** - * DOC: bandgap driver data structure - * ================================== - * - * +----------+----------------+ - * | struct temp_sensor_regval | - * +---------------------------+ - * * (Array of) - * | - * | - * +-------------------+ +-----------------+ - * | struct ti_bandgap |-->| struct device * | - * +----------+--------+ +-----------------+ - * | - * | - * V - * +------------------------+ - * | struct ti_bandgap_data | - * +------------------------+ - * | - * | - * * (Array of) - * +------------+------------------------------------------------------+ - * | +----------+------------+ +-------------------------+ | - * | | struct ti_temp_sensor |-->| struct temp_sensor_data | | - * | +-----------------------+ +------------+------------+ | - * | | | - * | + | - * | V | - * | +----------+-------------------+ | - * | | struct temp_sensor_registers | | - * | +------------------------------+ | - * | | - * +-------------------------------------------------------------------+ - * - * Above is a simple diagram describing how the data structure below - * are organized. For each bandgap device there should be a ti_bandgap_data - * containing the device instance configuration, as well as, an array of - * sensors, representing every sensor instance present in this bandgap. - */ - -/** - * struct temp_sensor_registers - descriptor to access registers and bitfields - * @temp_sensor_ctrl: TEMP_SENSOR_CTRL register offset - * @bgap_tempsoff_mask: mask to temp_sensor_ctrl.tempsoff - * @bgap_soc_mask: mask to temp_sensor_ctrl.soc - * @bgap_eocz_mask: mask to temp_sensor_ctrl.eocz - * @bgap_dtemp_mask: mask to temp_sensor_ctrl.dtemp - * @bgap_mask_ctrl: BANDGAP_MASK_CTRL register offset - * @mask_hot_mask: mask to bandgap_mask_ctrl.mask_hot - * @mask_cold_mask: mask to bandgap_mask_ctrl.mask_cold - * @mask_sidlemode_mask: mask to bandgap_mask_ctrl.mask_sidlemode - * @mask_counter_delay_mask: mask to bandgap_mask_ctrl.mask_counter_delay - * @mask_freeze_mask: mask to bandgap_mask_ctrl.mask_free - * @mask_clear_mask: mask to bandgap_mask_ctrl.mask_clear - * @mask_clear_accum_mask: mask to bandgap_mask_ctrl.mask_clear_accum - * @bgap_mode_ctrl: BANDGAP_MODE_CTRL register offset - * @mode_ctrl_mask: mask to bandgap_mode_ctrl.mode_ctrl - * @bgap_counter: BANDGAP_COUNTER register offset - * @counter_mask: mask to bandgap_counter.counter - * @bgap_threshold: BANDGAP_THRESHOLD register offset (TALERT thresholds) - * @threshold_thot_mask: mask to bandgap_threhold.thot - * @threshold_tcold_mask: mask to bandgap_threhold.tcold - * @tshut_threshold: TSHUT_THRESHOLD register offset (TSHUT thresholds) - * @tshut_efuse_mask: mask to tshut_threshold.tshut_efuse - * @tshut_efuse_shift: shift to tshut_threshold.tshut_efuse - * @tshut_hot_mask: mask to tshut_threhold.thot - * @tshut_cold_mask: mask to tshut_threhold.thot - * @bgap_status: BANDGAP_STATUS register offset - * @status_clean_stop_mask: mask to bandgap_status.clean_stop - * @status_bgap_alert_mask: mask to bandgap_status.bandgap_alert - * @status_hot_mask: mask to bandgap_status.hot - * @status_cold_mask: mask to bandgap_status.cold - * @bgap_cumul_dtemp: BANDGAP_CUMUL_DTEMP register offset - * @ctrl_dtemp_0: CTRL_DTEMP0 register offset - * @ctrl_dtemp_1: CTRL_DTEMP1 register offset - * @ctrl_dtemp_2: CTRL_DTEMP2 register offset - * @ctrl_dtemp_3: CTRL_DTEMP3 register offset - * @ctrl_dtemp_4: CTRL_DTEMP4 register offset - * @bgap_efuse: BANDGAP_EFUSE register offset - * - * The register offsets and bitfields might change across - * OMAP and variants versions. Hence this struct serves as a - * descriptor map on how to access the registers and the bitfields. - * - * This descriptor contains registers of all versions of bandgap chips. - * Not all versions will use all registers, depending on the available - * features. Please read TRMs for descriptive explanation on each bitfield. - */ - -struct temp_sensor_registers { - u32 temp_sensor_ctrl; - u32 bgap_tempsoff_mask; - u32 bgap_soc_mask; - u32 bgap_eocz_mask; /* not used: but needs revisit */ - u32 bgap_dtemp_mask; - - u32 bgap_mask_ctrl; - u32 mask_hot_mask; - u32 mask_cold_mask; - u32 mask_sidlemode_mask; /* not used: but may be needed for pm */ - u32 mask_counter_delay_mask; - u32 mask_freeze_mask; - u32 mask_clear_mask; /* not used: but needed for trending */ - u32 mask_clear_accum_mask; /* not used: but needed for trending */ - - u32 bgap_mode_ctrl; - u32 mode_ctrl_mask; - - u32 bgap_counter; - u32 counter_mask; - - u32 bgap_threshold; - u32 threshold_thot_mask; - u32 threshold_tcold_mask; - - u32 tshut_threshold; - u32 tshut_efuse_mask; /* not used */ - u32 tshut_efuse_shift; /* not used */ - u32 tshut_hot_mask; - u32 tshut_cold_mask; - - u32 bgap_status; - u32 status_clean_stop_mask; /* not used: but needed for trending */ - u32 status_bgap_alert_mask; /* not used */ - u32 status_hot_mask; - u32 status_cold_mask; - - u32 bgap_cumul_dtemp; /* not used: but needed for trending */ - u32 ctrl_dtemp_0; /* not used: but needed for trending */ - u32 ctrl_dtemp_1; /* not used: but needed for trending */ - u32 ctrl_dtemp_2; /* not used: but needed for trending */ - u32 ctrl_dtemp_3; /* not used: but needed for trending */ - u32 ctrl_dtemp_4; /* not used: but needed for trending */ - u32 bgap_efuse; -}; - -/** - * struct temp_sensor_data - The thresholds and limits for temperature sensors. - * @tshut_hot: temperature to trigger a thermal reset (initial value) - * @tshut_cold: temp to get the plat out of reset due to thermal (init val) - * @t_hot: temperature to trigger a thermal alert (high initial value) - * @t_cold: temperature to trigger a thermal alert (low initial value) - * @min_freq: sensor minimum clock rate - * @max_freq: sensor maximum clock rate - * @max_temp: sensor maximum temperature - * @min_temp: sensor minimum temperature - * @hyst_val: temperature hysteresis considered while converting ADC values - * @update_int1: update interval - * @update_int2: update interval - * - * This data structure will hold the required thresholds and temperature limits - * for a specific temperature sensor, like shutdown temperature, alert - * temperature, clock / rate used, ADC conversion limits and update intervals - */ -struct temp_sensor_data { - u32 tshut_hot; - u32 tshut_cold; - u32 t_hot; - u32 t_cold; - u32 min_freq; - u32 max_freq; - int max_temp; - int min_temp; - int hyst_val; - u32 update_int1; /* not used */ - u32 update_int2; /* not used */ -}; - -struct ti_bandgap_data; - -/** - * struct temp_sensor_regval - temperature sensor register values and priv data - * @bg_mode_ctrl: temp sensor control register value - * @bg_ctrl: bandgap ctrl register value - * @bg_counter: bandgap counter value - * @bg_threshold: bandgap threshold register value - * @tshut_threshold: bandgap tshut register value - * @data: private data - * - * Data structure to save and restore bandgap register set context. Only - * required registers are shadowed, when needed. - */ -struct temp_sensor_regval { - u32 bg_mode_ctrl; - u32 bg_ctrl; - u32 bg_counter; - u32 bg_threshold; - u32 tshut_threshold; - void *data; -}; - -/** - * struct ti_bandgap - bandgap device structure - * @dev: struct device pointer - * @base: io memory base address - * @conf: struct with bandgap configuration set (# sensors, conv_table, etc) - * @regval: temperature sensor register values - * @fclock: pointer to functional clock of temperature sensor - * @div_clk: pointer to divider clock of temperature sensor fclk - * @lock: spinlock for ti_bandgap structure - * @irq: MPU IRQ number for thermal alert - * @tshut_gpio: GPIO where Tshut signal is routed - * @clk_rate: Holds current clock rate - * - * The bandgap device structure representing the bandgap device instance. - * It holds most of the dynamic stuff. Configurations and sensor specific - * entries are inside the @conf structure. - */ -struct ti_bandgap { - struct device *dev; - void __iomem *base; - const struct ti_bandgap_data *conf; - struct temp_sensor_regval *regval; - struct clk *fclock; - struct clk *div_clk; - spinlock_t lock; /* shields this struct */ - int irq; - int tshut_gpio; - u32 clk_rate; -}; - -/** - * struct ti_temp_sensor - bandgap temperature sensor configuration data - * @ts_data: pointer to struct with thresholds, limits of temperature sensor - * @registers: pointer to the list of register offsets and bitfields - * @domain: the name of the domain where the sensor is located - * @slope: sensor gradient slope info for hotspot extrapolation equation - * @constant: sensor gradient const info for hotspot extrapolation equation - * @slope_pcb: sensor gradient slope info for hotspot extrapolation equation - * with no external influence - * @constant_pcb: sensor gradient const info for hotspot extrapolation equation - * with no external influence - * @register_cooling: function to describe how this sensor is going to be cooled - * @unregister_cooling: function to release cooling data - * - * Data structure to describe a temperature sensor handled by a bandgap device. - * It should provide configuration details on this sensor, such as how to - * access the registers affecting this sensor, shadow register buffer, how to - * assess the gradient from hotspot, how to cooldown the domain when sensor - * reports too hot temperature. - */ -struct ti_temp_sensor { - struct temp_sensor_data *ts_data; - struct temp_sensor_registers *registers; - char *domain; - /* for hotspot extrapolation */ - const int slope; - const int constant; - const int slope_pcb; - const int constant_pcb; - int (*register_cooling)(struct ti_bandgap *bgp, int id); - int (*unregister_cooling)(struct ti_bandgap *bgp, int id); -}; - -/** - * DOC: ti bandgap feature types - * - * TI_BANDGAP_FEATURE_TSHUT - used when the thermal shutdown signal output - * of a bandgap device instance is routed to the processor. This means - * the system must react and perform the shutdown by itself (handle an - * IRQ, for instance). - * - * TI_BANDGAP_FEATURE_TSHUT_CONFIG - used when the bandgap device has control - * over the thermal shutdown configuration. This means that the thermal - * shutdown thresholds are programmable, for instance. - * - * TI_BANDGAP_FEATURE_TALERT - used when the bandgap device instance outputs - * a signal representing violation of programmable alert thresholds. - * - * TI_BANDGAP_FEATURE_MODE_CONFIG - used when it is possible to choose which - * mode, continuous or one shot, the bandgap device instance will operate. - * - * TI_BANDGAP_FEATURE_COUNTER - used when the bandgap device instance allows - * programming the update interval of its internal state machine. - * - * TI_BANDGAP_FEATURE_POWER_SWITCH - used when the bandgap device allows - * itself to be switched on/off. - * - * TI_BANDGAP_FEATURE_CLK_CTRL - used when the clocks feeding the bandgap - * device are gateable or not. - * - * TI_BANDGAP_FEATURE_FREEZE_BIT - used when the bandgap device features - * a history buffer that its update can be freezed/unfreezed. - * - * TI_BANDGAP_FEATURE_COUNTER_DELAY - used when the bandgap device features - * a delay programming based on distinct values. - * - * TI_BANDGAP_FEATURE_HISTORY_BUFFER - used when the bandgap device features - * a history buffer of temperatures. - * - * TI_BANDGAP_HAS(b, f) - macro to check if a bandgap device is capable of a - * specific feature (above) or not. Return non-zero, if yes. - */ -#define TI_BANDGAP_FEATURE_TSHUT BIT(0) -#define TI_BANDGAP_FEATURE_TSHUT_CONFIG BIT(1) -#define TI_BANDGAP_FEATURE_TALERT BIT(2) -#define TI_BANDGAP_FEATURE_MODE_CONFIG BIT(3) -#define TI_BANDGAP_FEATURE_COUNTER BIT(4) -#define TI_BANDGAP_FEATURE_POWER_SWITCH BIT(5) -#define TI_BANDGAP_FEATURE_CLK_CTRL BIT(6) -#define TI_BANDGAP_FEATURE_FREEZE_BIT BIT(7) -#define TI_BANDGAP_FEATURE_COUNTER_DELAY BIT(8) -#define TI_BANDGAP_FEATURE_HISTORY_BUFFER BIT(9) -#define TI_BANDGAP_HAS(b, f) \ - ((b)->conf->features & TI_BANDGAP_FEATURE_ ## f) - -/** - * struct ti_bandgap_data - ti bandgap data configuration structure - * @features: a bitwise flag set to describe the device features - * @conv_table: Pointer to ADC to temperature conversion table - * @adc_start_val: ADC conversion table starting value - * @adc_end_val: ADC conversion table ending value - * @fclock_name: clock name of the functional clock - * @div_ck_name: clock name of the clock divisor - * @sensor_count: count of temperature sensor within this bandgap device - * @report_temperature: callback to report thermal alert to thermal API - * @expose_sensor: callback to export sensor to thermal API - * @remove_sensor: callback to destroy sensor from thermal API - * @sensors: array of sensors present in this bandgap instance - * - * This is a data structure which should hold most of the static configuration - * of a bandgap device instance. It should describe which features this instance - * is capable of, the clock names to feed this device, the amount of sensors and - * their configuration representation, and how to export and unexport them to - * a thermal API. - */ -struct ti_bandgap_data { - unsigned int features; - const int *conv_table; - u32 adc_start_val; - u32 adc_end_val; - char *fclock_name; - char *div_ck_name; - int sensor_count; - int (*report_temperature)(struct ti_bandgap *bgp, int id); - int (*expose_sensor)(struct ti_bandgap *bgp, int id, char *domain); - int (*remove_sensor)(struct ti_bandgap *bgp, int id); - - /* this needs to be at the end */ - struct ti_temp_sensor sensors[]; -}; - -int ti_bandgap_read_thot(struct ti_bandgap *bgp, int id, int *thot); -int ti_bandgap_write_thot(struct ti_bandgap *bgp, int id, int val); -int ti_bandgap_read_tcold(struct ti_bandgap *bgp, int id, int *tcold); -int ti_bandgap_write_tcold(struct ti_bandgap *bgp, int id, int val); -int ti_bandgap_read_update_interval(struct ti_bandgap *bgp, int id, - int *interval); -int ti_bandgap_write_update_interval(struct ti_bandgap *bgp, int id, - u32 interval); -int ti_bandgap_read_temperature(struct ti_bandgap *bgp, int id, - int *temperature); -int ti_bandgap_set_sensor_data(struct ti_bandgap *bgp, int id, void *data); -void *ti_bandgap_get_sensor_data(struct ti_bandgap *bgp, int id); -int ti_bandgap_get_trend(struct ti_bandgap *bgp, int id, int *trend); - -#ifdef CONFIG_OMAP4_THERMAL -extern const struct ti_bandgap_data omap4430_data; -extern const struct ti_bandgap_data omap4460_data; -extern const struct ti_bandgap_data omap4470_data; -#else -#define omap4430_data NULL -#define omap4460_data NULL -#define omap4470_data NULL -#endif - -#ifdef CONFIG_OMAP5_THERMAL -extern const struct ti_bandgap_data omap5430_data; -#else -#define omap5430_data NULL -#endif - -#endif diff --git a/drivers/staging/ti-soc-thermal/ti-thermal-common.c b/drivers/staging/ti-soc-thermal/ti-thermal-common.c deleted file mode 100644 index e3c5e677eaa5..000000000000 --- a/drivers/staging/ti-soc-thermal/ti-thermal-common.c +++ /dev/null @@ -1,367 +0,0 @@ -/* - * OMAP thermal driver interface - * - * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ - * Contact: - * Eduardo Valentin - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ti-thermal.h" -#include "ti-bandgap.h" - -/* common data structures */ -struct ti_thermal_data { - struct thermal_zone_device *ti_thermal; - struct thermal_cooling_device *cool_dev; - struct ti_bandgap *bgp; - enum thermal_device_mode mode; - struct work_struct thermal_wq; - int sensor_id; -}; - -static void ti_thermal_work(struct work_struct *work) -{ - struct ti_thermal_data *data = container_of(work, - struct ti_thermal_data, thermal_wq); - - thermal_zone_device_update(data->ti_thermal); - - dev_dbg(&data->ti_thermal->device, "updated thermal zone %s\n", - data->ti_thermal->type); -} - -/** - * ti_thermal_hotspot_temperature - returns sensor extrapolated temperature - * @t: omap sensor temperature - * @s: omap sensor slope value - * @c: omap sensor const value - */ -static inline int ti_thermal_hotspot_temperature(int t, int s, int c) -{ - int delta = t * s / 1000 + c; - - if (delta < 0) - delta = 0; - - return t + delta; -} - -/* thermal zone ops */ -/* Get temperature callback function for thermal zone*/ -static inline int ti_thermal_get_temp(struct thermal_zone_device *thermal, - unsigned long *temp) -{ - struct ti_thermal_data *data = thermal->devdata; - struct ti_bandgap *bgp; - const struct ti_temp_sensor *s; - int ret, tmp, pcb_temp, slope, constant; - - if (!data) - return 0; - - bgp = data->bgp; - s = &bgp->conf->sensors[data->sensor_id]; - - ret = ti_bandgap_read_temperature(bgp, data->sensor_id, &tmp); - if (ret) - return ret; - - pcb_temp = 0; - /* TODO: Introduce pcb temperature lookup */ - /* In case pcb zone is available, use the extrapolation rule with it */ - if (pcb_temp) { - tmp -= pcb_temp; - slope = s->slope_pcb; - constant = s->constant_pcb; - } else { - slope = s->slope; - constant = s->constant; - } - *temp = ti_thermal_hotspot_temperature(tmp, slope, constant); - - return ret; -} - -/* Bind callback functions for thermal zone */ -static int ti_thermal_bind(struct thermal_zone_device *thermal, - struct thermal_cooling_device *cdev) -{ - struct ti_thermal_data *data = thermal->devdata; - int id; - - if (IS_ERR_OR_NULL(data)) - return -ENODEV; - - /* check if this is the cooling device we registered */ - if (data->cool_dev != cdev) - return 0; - - id = data->sensor_id; - - /* Simple thing, two trips, one passive another critical */ - return thermal_zone_bind_cooling_device(thermal, 0, cdev, - /* bind with min and max states defined by cpu_cooling */ - THERMAL_NO_LIMIT, - THERMAL_NO_LIMIT); -} - -/* Unbind callback functions for thermal zone */ -static int ti_thermal_unbind(struct thermal_zone_device *thermal, - struct thermal_cooling_device *cdev) -{ - struct ti_thermal_data *data = thermal->devdata; - - if (IS_ERR_OR_NULL(data)) - return -ENODEV; - - /* check if this is the cooling device we registered */ - if (data->cool_dev != cdev) - return 0; - - /* Simple thing, two trips, one passive another critical */ - return thermal_zone_unbind_cooling_device(thermal, 0, cdev); -} - -/* Get mode callback functions for thermal zone */ -static int ti_thermal_get_mode(struct thermal_zone_device *thermal, - enum thermal_device_mode *mode) -{ - struct ti_thermal_data *data = thermal->devdata; - - if (data) - *mode = data->mode; - - return 0; -} - -/* Set mode callback functions for thermal zone */ -static int ti_thermal_set_mode(struct thermal_zone_device *thermal, - enum thermal_device_mode mode) -{ - struct ti_thermal_data *data = thermal->devdata; - - if (!data->ti_thermal) { - dev_notice(&thermal->device, "thermal zone not registered\n"); - return 0; - } - - mutex_lock(&data->ti_thermal->lock); - - if (mode == THERMAL_DEVICE_ENABLED) - data->ti_thermal->polling_delay = FAST_TEMP_MONITORING_RATE; - else - data->ti_thermal->polling_delay = 0; - - mutex_unlock(&data->ti_thermal->lock); - - data->mode = mode; - thermal_zone_device_update(data->ti_thermal); - dev_dbg(&thermal->device, "thermal polling set for duration=%d msec\n", - data->ti_thermal->polling_delay); - - return 0; -} - -/* Get trip type callback functions for thermal zone */ -static int ti_thermal_get_trip_type(struct thermal_zone_device *thermal, - int trip, enum thermal_trip_type *type) -{ - if (!ti_thermal_is_valid_trip(trip)) - return -EINVAL; - - if (trip + 1 == OMAP_TRIP_NUMBER) - *type = THERMAL_TRIP_CRITICAL; - else - *type = THERMAL_TRIP_PASSIVE; - - return 0; -} - -/* Get trip temperature callback functions for thermal zone */ -static int ti_thermal_get_trip_temp(struct thermal_zone_device *thermal, - int trip, unsigned long *temp) -{ - if (!ti_thermal_is_valid_trip(trip)) - return -EINVAL; - - *temp = ti_thermal_get_trip_value(trip); - - return 0; -} - -/* Get the temperature trend callback functions for thermal zone */ -static int ti_thermal_get_trend(struct thermal_zone_device *thermal, - int trip, enum thermal_trend *trend) -{ - struct ti_thermal_data *data = thermal->devdata; - struct ti_bandgap *bgp; - int id, tr, ret = 0; - - bgp = data->bgp; - id = data->sensor_id; - - ret = ti_bandgap_get_trend(bgp, id, &tr); - if (ret) - return ret; - - if (tr > 0) - *trend = THERMAL_TREND_RAISING; - else if (tr < 0) - *trend = THERMAL_TREND_DROPPING; - else - *trend = THERMAL_TREND_STABLE; - - return 0; -} - -/* Get critical temperature callback functions for thermal zone */ -static int ti_thermal_get_crit_temp(struct thermal_zone_device *thermal, - unsigned long *temp) -{ - /* shutdown zone */ - return ti_thermal_get_trip_temp(thermal, OMAP_TRIP_NUMBER - 1, temp); -} - -static struct thermal_zone_device_ops ti_thermal_ops = { - .get_temp = ti_thermal_get_temp, - .get_trend = ti_thermal_get_trend, - .bind = ti_thermal_bind, - .unbind = ti_thermal_unbind, - .get_mode = ti_thermal_get_mode, - .set_mode = ti_thermal_set_mode, - .get_trip_type = ti_thermal_get_trip_type, - .get_trip_temp = ti_thermal_get_trip_temp, - .get_crit_temp = ti_thermal_get_crit_temp, -}; - -static struct ti_thermal_data -*ti_thermal_build_data(struct ti_bandgap *bgp, int id) -{ - struct ti_thermal_data *data; - - data = devm_kzalloc(bgp->dev, sizeof(*data), GFP_KERNEL); - if (!data) { - dev_err(bgp->dev, "kzalloc fail\n"); - return NULL; - } - data->sensor_id = id; - data->bgp = bgp; - data->mode = THERMAL_DEVICE_ENABLED; - INIT_WORK(&data->thermal_wq, ti_thermal_work); - - return data; -} - -int ti_thermal_expose_sensor(struct ti_bandgap *bgp, int id, - char *domain) -{ - struct ti_thermal_data *data; - - data = ti_bandgap_get_sensor_data(bgp, id); - - if (IS_ERR_OR_NULL(data)) - data = ti_thermal_build_data(bgp, id); - - if (!data) - return -EINVAL; - - /* Create thermal zone */ - data->ti_thermal = thermal_zone_device_register(domain, - OMAP_TRIP_NUMBER, 0, data, &ti_thermal_ops, - NULL, FAST_TEMP_MONITORING_RATE, - FAST_TEMP_MONITORING_RATE); - if (IS_ERR_OR_NULL(data->ti_thermal)) { - dev_err(bgp->dev, "thermal zone device is NULL\n"); - return PTR_ERR(data->ti_thermal); - } - data->ti_thermal->polling_delay = FAST_TEMP_MONITORING_RATE; - ti_bandgap_set_sensor_data(bgp, id, data); - - return 0; -} - -int ti_thermal_remove_sensor(struct ti_bandgap *bgp, int id) -{ - struct ti_thermal_data *data; - - data = ti_bandgap_get_sensor_data(bgp, id); - - thermal_zone_device_unregister(data->ti_thermal); - - return 0; -} - -int ti_thermal_report_sensor_temperature(struct ti_bandgap *bgp, int id) -{ - struct ti_thermal_data *data; - - data = ti_bandgap_get_sensor_data(bgp, id); - - schedule_work(&data->thermal_wq); - - return 0; -} - -int ti_thermal_register_cpu_cooling(struct ti_bandgap *bgp, int id) -{ - struct ti_thermal_data *data; - - data = ti_bandgap_get_sensor_data(bgp, id); - if (IS_ERR_OR_NULL(data)) - data = ti_thermal_build_data(bgp, id); - - if (!data) - return -EINVAL; - - if (!cpufreq_get_current_driver()) { - dev_dbg(bgp->dev, "no cpufreq driver yet\n"); - return -EPROBE_DEFER; - } - - /* Register cooling device */ - data->cool_dev = cpufreq_cooling_register(cpu_present_mask); - if (IS_ERR_OR_NULL(data->cool_dev)) { - dev_err(bgp->dev, - "Failed to register cpufreq cooling device\n"); - return PTR_ERR(data->cool_dev); - } - ti_bandgap_set_sensor_data(bgp, id, data); - - return 0; -} - -int ti_thermal_unregister_cpu_cooling(struct ti_bandgap *bgp, int id) -{ - struct ti_thermal_data *data; - - data = ti_bandgap_get_sensor_data(bgp, id); - cpufreq_cooling_unregister(data->cool_dev); - - return 0; -} diff --git a/drivers/staging/ti-soc-thermal/ti-thermal.h b/drivers/staging/ti-soc-thermal/ti-thermal.h deleted file mode 100644 index 5055777727cc..000000000000 --- a/drivers/staging/ti-soc-thermal/ti-thermal.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * OMAP thermal definitions - * - * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ - * Contact: - * Eduardo Valentin - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ -#ifndef __TI_THERMAL_H -#define __TI_THERMAL_H - -#include "ti-bandgap.h" - -/* sensors gradient and offsets */ -#define OMAP_GRADIENT_SLOPE_4430 0 -#define OMAP_GRADIENT_CONST_4430 20000 -#define OMAP_GRADIENT_SLOPE_4460 348 -#define OMAP_GRADIENT_CONST_4460 -9301 -#define OMAP_GRADIENT_SLOPE_4470 308 -#define OMAP_GRADIENT_CONST_4470 -7896 - -#define OMAP_GRADIENT_SLOPE_5430_CPU 65 -#define OMAP_GRADIENT_CONST_5430_CPU -1791 -#define OMAP_GRADIENT_SLOPE_5430_GPU 117 -#define OMAP_GRADIENT_CONST_5430_GPU -2992 - -/* PCB sensor calculation constants */ -#define OMAP_GRADIENT_SLOPE_W_PCB_4430 0 -#define OMAP_GRADIENT_CONST_W_PCB_4430 20000 -#define OMAP_GRADIENT_SLOPE_W_PCB_4460 1142 -#define OMAP_GRADIENT_CONST_W_PCB_4460 -393 -#define OMAP_GRADIENT_SLOPE_W_PCB_4470 1063 -#define OMAP_GRADIENT_CONST_W_PCB_4470 -477 - -#define OMAP_GRADIENT_SLOPE_W_PCB_5430_CPU 100 -#define OMAP_GRADIENT_CONST_W_PCB_5430_CPU 484 -#define OMAP_GRADIENT_SLOPE_W_PCB_5430_GPU 464 -#define OMAP_GRADIENT_CONST_W_PCB_5430_GPU -5102 - -/* trip points of interest in milicelsius (at hotspot level) */ -#define OMAP_TRIP_COLD 100000 -#define OMAP_TRIP_HOT 110000 -#define OMAP_TRIP_SHUTDOWN 125000 -#define OMAP_TRIP_NUMBER 2 -#define OMAP_TRIP_STEP \ - ((OMAP_TRIP_SHUTDOWN - OMAP_TRIP_HOT) / (OMAP_TRIP_NUMBER - 1)) - -/* Update rates */ -#define FAST_TEMP_MONITORING_RATE 250 - -/* helper macros */ -/** - * ti_thermal_get_trip_value - returns trip temperature based on index - * @i: trip index - */ -#define ti_thermal_get_trip_value(i) \ - (OMAP_TRIP_HOT + ((i) * OMAP_TRIP_STEP)) - -/** - * ti_thermal_is_valid_trip - check for trip index - * @i: trip index - */ -#define ti_thermal_is_valid_trip(trip) \ - ((trip) >= 0 && (trip) < OMAP_TRIP_NUMBER) - -#ifdef CONFIG_TI_THERMAL -int ti_thermal_expose_sensor(struct ti_bandgap *bgp, int id, char *domain); -int ti_thermal_remove_sensor(struct ti_bandgap *bgp, int id); -int ti_thermal_report_sensor_temperature(struct ti_bandgap *bgp, int id); -int ti_thermal_register_cpu_cooling(struct ti_bandgap *bgp, int id); -int ti_thermal_unregister_cpu_cooling(struct ti_bandgap *bgp, int id); -#else -static inline -int ti_thermal_expose_sensor(struct ti_bandgap *bgp, int id, char *domain) -{ - return 0; -} - -static inline -int ti_thermal_remove_sensor(struct ti_bandgap *bgp, int id) -{ - return 0; -} - -static inline -int ti_thermal_report_sensor_temperature(struct ti_bandgap *bgp, int id) -{ - return 0; -} - -static inline -int ti_thermal_register_cpu_cooling(struct ti_bandgap *bgp, int id) -{ - return 0; -} - -static inline -int ti_thermal_unregister_cpu_cooling(struct ti_bandgap *bgp, int id) -{ - return 0; -} -#endif -#endif diff --git a/drivers/staging/ti-soc-thermal/ti_soc_thermal.txt b/drivers/staging/ti-soc-thermal/ti_soc_thermal.txt deleted file mode 100644 index a4a33d1a0746..000000000000 --- a/drivers/staging/ti-soc-thermal/ti_soc_thermal.txt +++ /dev/null @@ -1,60 +0,0 @@ -* Texas Instrument OMAP SCM bandgap bindings - -In the System Control Module, OMAP supplies a voltage reference -and a temperature sensor feature that are gathered in the band -gap voltage and temperature sensor (VBGAPTS) module. The band -gap provides current and voltage reference for its internal -circuits and other analog IP blocks. The analog-to-digital -converter (ADC) produces an output value that is proportional -to the silicon temperature. - -Required properties: -- compatible : Should be: - - "ti,omap4430-bandgap" : for OMAP4430 bandgap - - "ti,omap4460-bandgap" : for OMAP4460 bandgap - - "ti,omap4470-bandgap" : for OMAP4470 bandgap - - "ti,omap5430-bandgap" : for OMAP5430 bandgap -- interrupts : this entry should indicate which interrupt line -the talert signal is routed to; -Specific: -- ti,tshut-gpio : this entry should be used to inform which GPIO -line the tshut signal is routed to; -- regs : this entry must also be specified and it is specific -to each bandgap version, because the mapping may change from -soc to soc, apart of depending on available features. - -Example: -OMAP4430: -bandgap { - reg = <0x4a002260 0x4 0x4a00232C 0x4>; - compatible = "ti,omap4430-bandgap"; -}; - -OMAP4460: -bandgap { - reg = <0x4a002260 0x4 - 0x4a00232C 0x4 - 0x4a002378 0x18>; - compatible = "ti,omap4460-bandgap"; - interrupts = <0 126 4>; /* talert */ - ti,tshut-gpio = <86>; -}; - -OMAP4470: -bandgap { - reg = <0x4a002260 0x4 - 0x4a00232C 0x4 - 0x4a002378 0x18>; - compatible = "ti,omap4470-bandgap"; - interrupts = <0 126 4>; /* talert */ - ti,tshut-gpio = <86>; -}; - -OMAP5430: -bandgap { - reg = <0x4a0021e0 0xc - 0x4a00232c 0xc - 0x4a002380 0x2c - 0x4a0023C0 0x3c>; - compatible = "ti,omap5430-bandgap"; -}; diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 5e3c02554d99..7205c70a46a3 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -169,4 +169,7 @@ config INTEL_POWERCLAMP enforce idle time which results in more package C-state residency. The user interface is exposed via generic thermal framework. +menu "Texas Instruments thermal drivers" +source "drivers/thermal/ti-soc-thermal/Kconfig" +endmenu endif diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index c054d410ac3f..85693941fda0 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -23,4 +23,4 @@ obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o - +obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/ diff --git a/drivers/thermal/ti-soc-thermal/Kconfig b/drivers/thermal/ti-soc-thermal/Kconfig new file mode 100644 index 000000000000..e81375fb2155 --- /dev/null +++ b/drivers/thermal/ti-soc-thermal/Kconfig @@ -0,0 +1,48 @@ +config TI_SOC_THERMAL + tristate "Texas Instruments SoCs temperature sensor driver" + depends on THERMAL + depends on ARCH_HAS_BANDGAP + help + If you say yes here you get support for the Texas Instruments + OMAP4460+ on die bandgap temperature sensor support. The register + set is part of system control module. + + This includes alert interrupts generation and also the TSHUT + support. + +config TI_THERMAL + bool "Texas Instruments SoCs thermal framework support" + depends on TI_SOC_THERMAL + depends on CPU_THERMAL + help + If you say yes here you want to get support for generic thermal + framework for the Texas Instruments on die bandgap temperature sensor. + + This includes trip points definitions, extrapolation rules and + CPU cooling device bindings. + +config OMAP4_THERMAL + bool "Texas Instruments OMAP4 thermal support" + depends on TI_SOC_THERMAL + depends on ARCH_OMAP4 + help + If you say yes here you get thermal support for the Texas Instruments + OMAP4 SoC family. The current chip supported are: + - OMAP4430 + - OMAP4460 + - OMAP4470 + + This includes alert interrupts generation and also the TSHUT + support. + +config OMAP5_THERMAL + bool "Texas Instruments OMAP5 thermal support" + depends on TI_SOC_THERMAL + depends on SOC_OMAP5 + help + If you say yes here you get thermal support for the Texas Instruments + OMAP5 SoC family. The current chip supported are: + - OMAP5430 + + This includes alert interrupts generation and also the TSHUT + support. diff --git a/drivers/thermal/ti-soc-thermal/Makefile b/drivers/thermal/ti-soc-thermal/Makefile new file mode 100644 index 000000000000..0ca034fb419d --- /dev/null +++ b/drivers/thermal/ti-soc-thermal/Makefile @@ -0,0 +1,5 @@ +obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal.o +ti-soc-thermal-y := ti-bandgap.o +ti-soc-thermal-$(CONFIG_TI_THERMAL) += ti-thermal-common.o +ti-soc-thermal-$(CONFIG_OMAP4_THERMAL) += omap4-thermal-data.o +ti-soc-thermal-$(CONFIG_OMAP5_THERMAL) += omap5-thermal-data.o diff --git a/drivers/thermal/ti-soc-thermal/TODO b/drivers/thermal/ti-soc-thermal/TODO new file mode 100644 index 000000000000..7da787d19241 --- /dev/null +++ b/drivers/thermal/ti-soc-thermal/TODO @@ -0,0 +1,12 @@ +List of TODOs (by Eduardo Valentin) + +on ti-bandgap.c: +- Revisit PM support + +on ti-thermal-common.c/ti-thermal.h: +- Revisit need for locking + +generally: +- make sure this code works on OMAP4430, OMAP4460 and OMAP5430 + +Copy patches to Eduardo Valentin diff --git a/drivers/thermal/ti-soc-thermal/omap4-thermal-data.c b/drivers/thermal/ti-soc-thermal/omap4-thermal-data.c new file mode 100644 index 000000000000..d255d33da9eb --- /dev/null +++ b/drivers/thermal/ti-soc-thermal/omap4-thermal-data.c @@ -0,0 +1,267 @@ +/* + * OMAP4 thermal driver. + * + * Copyright (C) 2011-2012 Texas Instruments Inc. + * Contact: + * Eduardo Valentin + * + * 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 "ti-thermal.h" +#include "ti-bandgap.h" +#include "omap4xxx-bandgap.h" + +/* + * OMAP4430 has one instance of thermal sensor for MPU + * need to describe the individual bit fields + */ +static struct temp_sensor_registers +omap4430_mpu_temp_sensor_registers = { + .temp_sensor_ctrl = OMAP4430_TEMP_SENSOR_CTRL_OFFSET, + .bgap_tempsoff_mask = OMAP4430_BGAP_TEMPSOFF_MASK, + .bgap_soc_mask = OMAP4430_BGAP_TEMP_SENSOR_SOC_MASK, + .bgap_eocz_mask = OMAP4430_BGAP_TEMP_SENSOR_EOCZ_MASK, + .bgap_dtemp_mask = OMAP4430_BGAP_TEMP_SENSOR_DTEMP_MASK, + + .bgap_mode_ctrl = OMAP4430_TEMP_SENSOR_CTRL_OFFSET, + .mode_ctrl_mask = OMAP4430_SINGLE_MODE_MASK, + + .bgap_efuse = OMAP4430_FUSE_OPP_BGAP, +}; + +/* Thresholds and limits for OMAP4430 MPU temperature sensor */ +static struct temp_sensor_data omap4430_mpu_temp_sensor_data = { + .min_freq = OMAP4430_MIN_FREQ, + .max_freq = OMAP4430_MAX_FREQ, + .max_temp = OMAP4430_MAX_TEMP, + .min_temp = OMAP4430_MIN_TEMP, + .hyst_val = OMAP4430_HYST_VAL, +}; + +/* + * Temperature values in milli degree celsius + * ADC code values from 530 to 923 + */ +static const int +omap4430_adc_to_temp[OMAP4430_ADC_END_VALUE - OMAP4430_ADC_START_VALUE + 1] = { + -38000, -35000, -34000, -32000, -30000, -28000, -26000, -24000, -22000, + -20000, -18000, -17000, -15000, -13000, -12000, -10000, -8000, -6000, + -5000, -3000, -1000, 0, 2000, 3000, 5000, 6000, 8000, 10000, 12000, + 13000, 15000, 17000, 19000, 21000, 23000, 25000, 27000, 28000, 30000, + 32000, 33000, 35000, 37000, 38000, 40000, 42000, 43000, 45000, 47000, + 48000, 50000, 52000, 53000, 55000, 57000, 58000, 60000, 62000, 64000, + 66000, 68000, 70000, 71000, 73000, 75000, 77000, 78000, 80000, 82000, + 83000, 85000, 87000, 88000, 90000, 92000, 93000, 95000, 97000, 98000, + 100000, 102000, 103000, 105000, 107000, 109000, 111000, 113000, 115000, + 117000, 118000, 120000, 122000, 123000, +}; + +/* OMAP4430 data */ +const struct ti_bandgap_data omap4430_data = { + .features = TI_BANDGAP_FEATURE_MODE_CONFIG | + TI_BANDGAP_FEATURE_CLK_CTRL | + TI_BANDGAP_FEATURE_POWER_SWITCH, + .fclock_name = "bandgap_fclk", + .div_ck_name = "bandgap_fclk", + .conv_table = omap4430_adc_to_temp, + .adc_start_val = OMAP4430_ADC_START_VALUE, + .adc_end_val = OMAP4430_ADC_END_VALUE, + .expose_sensor = ti_thermal_expose_sensor, + .remove_sensor = ti_thermal_remove_sensor, + .sensors = { + { + .registers = &omap4430_mpu_temp_sensor_registers, + .ts_data = &omap4430_mpu_temp_sensor_data, + .domain = "cpu", + .slope = OMAP_GRADIENT_SLOPE_4430, + .constant = OMAP_GRADIENT_CONST_4430, + .slope_pcb = OMAP_GRADIENT_SLOPE_W_PCB_4430, + .constant_pcb = OMAP_GRADIENT_CONST_W_PCB_4430, + .register_cooling = ti_thermal_register_cpu_cooling, + .unregister_cooling = ti_thermal_unregister_cpu_cooling, + }, + }, + .sensor_count = 1, +}; +/* + * OMAP4460 has one instance of thermal sensor for MPU + * need to describe the individual bit fields + */ +static struct temp_sensor_registers +omap4460_mpu_temp_sensor_registers = { + .temp_sensor_ctrl = OMAP4460_TEMP_SENSOR_CTRL_OFFSET, + .bgap_tempsoff_mask = OMAP4460_BGAP_TEMPSOFF_MASK, + .bgap_soc_mask = OMAP4460_BGAP_TEMP_SENSOR_SOC_MASK, + .bgap_eocz_mask = OMAP4460_BGAP_TEMP_SENSOR_EOCZ_MASK, + .bgap_dtemp_mask = OMAP4460_BGAP_TEMP_SENSOR_DTEMP_MASK, + + .bgap_mask_ctrl = OMAP4460_BGAP_CTRL_OFFSET, + .mask_hot_mask = OMAP4460_MASK_HOT_MASK, + .mask_cold_mask = OMAP4460_MASK_COLD_MASK, + + .bgap_mode_ctrl = OMAP4460_BGAP_CTRL_OFFSET, + .mode_ctrl_mask = OMAP4460_SINGLE_MODE_MASK, + + .bgap_counter = OMAP4460_BGAP_COUNTER_OFFSET, + .counter_mask = OMAP4460_COUNTER_MASK, + + .bgap_threshold = OMAP4460_BGAP_THRESHOLD_OFFSET, + .threshold_thot_mask = OMAP4460_T_HOT_MASK, + .threshold_tcold_mask = OMAP4460_T_COLD_MASK, + + .tshut_threshold = OMAP4460_BGAP_TSHUT_OFFSET, + .tshut_hot_mask = OMAP4460_TSHUT_HOT_MASK, + .tshut_cold_mask = OMAP4460_TSHUT_COLD_MASK, + + .bgap_status = OMAP4460_BGAP_STATUS_OFFSET, + .status_clean_stop_mask = OMAP4460_CLEAN_STOP_MASK, + .status_bgap_alert_mask = OMAP4460_BGAP_ALERT_MASK, + .status_hot_mask = OMAP4460_HOT_FLAG_MASK, + .status_cold_mask = OMAP4460_COLD_FLAG_MASK, + + .bgap_efuse = OMAP4460_FUSE_OPP_BGAP, +}; + +/* Thresholds and limits for OMAP4460 MPU temperature sensor */ +static struct temp_sensor_data omap4460_mpu_temp_sensor_data = { + .tshut_hot = OMAP4460_TSHUT_HOT, + .tshut_cold = OMAP4460_TSHUT_COLD, + .t_hot = OMAP4460_T_HOT, + .t_cold = OMAP4460_T_COLD, + .min_freq = OMAP4460_MIN_FREQ, + .max_freq = OMAP4460_MAX_FREQ, + .max_temp = OMAP4460_MAX_TEMP, + .min_temp = OMAP4460_MIN_TEMP, + .hyst_val = OMAP4460_HYST_VAL, + .update_int1 = 1000, + .update_int2 = 2000, +}; + +/* + * Temperature values in milli degree celsius + * ADC code values from 530 to 923 + */ +static const int +omap4460_adc_to_temp[OMAP4460_ADC_END_VALUE - OMAP4460_ADC_START_VALUE + 1] = { + -40000, -40000, -40000, -40000, -39800, -39400, -39000, -38600, -38200, + -37800, -37300, -36800, -36400, -36000, -35600, -35200, -34800, + -34300, -33800, -33400, -33000, -32600, -32200, -31800, -31300, + -30800, -30400, -30000, -29600, -29200, -28700, -28200, -27800, + -27400, -27000, -26600, -26200, -25700, -25200, -24800, -24400, + -24000, -23600, -23200, -22700, -22200, -21800, -21400, -21000, + -20600, -20200, -19700, -19200, -18800, -18400, -18000, -17600, + -17200, -16700, -16200, -15800, -15400, -15000, -14600, -14200, + -13700, -13200, -12800, -12400, -12000, -11600, -11200, -10700, + -10200, -9800, -9400, -9000, -8600, -8200, -7700, -7200, -6800, + -6400, -6000, -5600, -5200, -4800, -4300, -3800, -3400, -3000, + -2600, -2200, -1800, -1300, -800, -400, 0, 400, 800, 1200, 1600, + 2100, 2600, 3000, 3400, 3800, 4200, 4600, 5100, 5600, 6000, 6400, + 6800, 7200, 7600, 8000, 8500, 9000, 9400, 9800, 10200, 10600, 11000, + 11400, 11900, 12400, 12800, 13200, 13600, 14000, 14400, 14800, + 15300, 15800, 16200, 16600, 17000, 17400, 17800, 18200, 18700, + 19200, 19600, 20000, 20400, 20800, 21200, 21600, 22100, 22600, + 23000, 23400, 23800, 24200, 24600, 25000, 25400, 25900, 26400, + 26800, 27200, 27600, 28000, 28400, 28800, 29300, 29800, 30200, + 30600, 31000, 31400, 31800, 32200, 32600, 33100, 33600, 34000, + 34400, 34800, 35200, 35600, 36000, 36400, 36800, 37300, 37800, + 38200, 38600, 39000, 39400, 39800, 40200, 40600, 41100, 41600, + 42000, 42400, 42800, 43200, 43600, 44000, 44400, 44800, 45300, + 45800, 46200, 46600, 47000, 47400, 47800, 48200, 48600, 49000, + 49500, 50000, 50400, 50800, 51200, 51600, 52000, 52400, 52800, + 53200, 53700, 54200, 54600, 55000, 55400, 55800, 56200, 56600, + 57000, 57400, 57800, 58200, 58700, 59200, 59600, 60000, 60400, + 60800, 61200, 61600, 62000, 62400, 62800, 63300, 63800, 64200, + 64600, 65000, 65400, 65800, 66200, 66600, 67000, 67400, 67800, + 68200, 68700, 69200, 69600, 70000, 70400, 70800, 71200, 71600, + 72000, 72400, 72800, 73200, 73600, 74100, 74600, 75000, 75400, + 75800, 76200, 76600, 77000, 77400, 77800, 78200, 78600, 79000, + 79400, 79800, 80300, 80800, 81200, 81600, 82000, 82400, 82800, + 83200, 83600, 84000, 84400, 84800, 85200, 85600, 86000, 86400, + 86800, 87300, 87800, 88200, 88600, 89000, 89400, 89800, 90200, + 90600, 91000, 91400, 91800, 92200, 92600, 93000, 93400, 93800, + 94200, 94600, 95000, 95500, 96000, 96400, 96800, 97200, 97600, + 98000, 98400, 98800, 99200, 99600, 100000, 100400, 100800, 101200, + 101600, 102000, 102400, 102800, 103200, 103600, 104000, 104400, + 104800, 105200, 105600, 106100, 106600, 107000, 107400, 107800, + 108200, 108600, 109000, 109400, 109800, 110200, 110600, 111000, + 111400, 111800, 112200, 112600, 113000, 113400, 113800, 114200, + 114600, 115000, 115400, 115800, 116200, 116600, 117000, 117400, + 117800, 118200, 118600, 119000, 119400, 119800, 120200, 120600, + 121000, 121400, 121800, 122200, 122600, 123000, 123400, 123800, 124200, + 124600, 124900, 125000, 125000, 125000, 125000 +}; + +/* OMAP4460 data */ +const struct ti_bandgap_data omap4460_data = { + .features = TI_BANDGAP_FEATURE_TSHUT | + TI_BANDGAP_FEATURE_TSHUT_CONFIG | + TI_BANDGAP_FEATURE_TALERT | + TI_BANDGAP_FEATURE_MODE_CONFIG | + TI_BANDGAP_FEATURE_POWER_SWITCH | + TI_BANDGAP_FEATURE_CLK_CTRL | + TI_BANDGAP_FEATURE_COUNTER, + .fclock_name = "bandgap_ts_fclk", + .div_ck_name = "div_ts_ck", + .conv_table = omap4460_adc_to_temp, + .adc_start_val = OMAP4460_ADC_START_VALUE, + .adc_end_val = OMAP4460_ADC_END_VALUE, + .expose_sensor = ti_thermal_expose_sensor, + .remove_sensor = ti_thermal_remove_sensor, + .report_temperature = ti_thermal_report_sensor_temperature, + .sensors = { + { + .registers = &omap4460_mpu_temp_sensor_registers, + .ts_data = &omap4460_mpu_temp_sensor_data, + .domain = "cpu", + .slope = OMAP_GRADIENT_SLOPE_4460, + .constant = OMAP_GRADIENT_CONST_4460, + .slope_pcb = OMAP_GRADIENT_SLOPE_W_PCB_4460, + .constant_pcb = OMAP_GRADIENT_CONST_W_PCB_4460, + .register_cooling = ti_thermal_register_cpu_cooling, + .unregister_cooling = ti_thermal_unregister_cpu_cooling, + }, + }, + .sensor_count = 1, +}; + +/* OMAP4470 data */ +const struct ti_bandgap_data omap4470_data = { + .features = TI_BANDGAP_FEATURE_TSHUT | + TI_BANDGAP_FEATURE_TSHUT_CONFIG | + TI_BANDGAP_FEATURE_TALERT | + TI_BANDGAP_FEATURE_MODE_CONFIG | + TI_BANDGAP_FEATURE_POWER_SWITCH | + TI_BANDGAP_FEATURE_CLK_CTRL | + TI_BANDGAP_FEATURE_COUNTER, + .fclock_name = "bandgap_ts_fclk", + .div_ck_name = "div_ts_ck", + .conv_table = omap4460_adc_to_temp, + .adc_start_val = OMAP4460_ADC_START_VALUE, + .adc_end_val = OMAP4460_ADC_END_VALUE, + .expose_sensor = ti_thermal_expose_sensor, + .remove_sensor = ti_thermal_remove_sensor, + .report_temperature = ti_thermal_report_sensor_temperature, + .sensors = { + { + .registers = &omap4460_mpu_temp_sensor_registers, + .ts_data = &omap4460_mpu_temp_sensor_data, + .domain = "cpu", + .slope = OMAP_GRADIENT_SLOPE_4470, + .constant = OMAP_GRADIENT_CONST_4470, + .slope_pcb = OMAP_GRADIENT_SLOPE_W_PCB_4470, + .constant_pcb = OMAP_GRADIENT_CONST_W_PCB_4470, + .register_cooling = ti_thermal_register_cpu_cooling, + .unregister_cooling = ti_thermal_unregister_cpu_cooling, + }, + }, + .sensor_count = 1, +}; diff --git a/drivers/thermal/ti-soc-thermal/omap4xxx-bandgap.h b/drivers/thermal/ti-soc-thermal/omap4xxx-bandgap.h new file mode 100644 index 000000000000..6f2de3a3356d --- /dev/null +++ b/drivers/thermal/ti-soc-thermal/omap4xxx-bandgap.h @@ -0,0 +1,175 @@ +/* + * OMAP4xxx bandgap registers, bitfields and temperature definitions + * + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/ + * Contact: + * Eduardo Valentin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ +#ifndef __OMAP4XXX_BANDGAP_H +#define __OMAP4XXX_BANDGAP_H + +/** + * *** OMAP4430 *** + * + * Below, in sequence, are the Register definitions, + * the bitfields and the temperature definitions for OMAP4430. + */ + +/** + * OMAP4430 register definitions + * + * Registers are defined as offsets. The offsets are + * relative to FUSE_OPP_BGAP on 4430. + */ + +/* OMAP4430.FUSE_OPP_BGAP */ +#define OMAP4430_FUSE_OPP_BGAP 0x0 + +/* OMAP4430.TEMP_SENSOR */ +#define OMAP4430_TEMP_SENSOR_CTRL_OFFSET 0xCC + +/** + * Register and bit definitions for OMAP4430 + * + * All the macros bellow define the required bits for + * controlling temperature on OMAP4430. Bit defines are + * grouped by register. + */ + +/* OMAP4430.TEMP_SENSOR bits */ +#define OMAP4430_BGAP_TEMPSOFF_MASK BIT(12) +#define OMAP4430_BGAP_TSHUT_MASK BIT(11) +#define OMAP4430_SINGLE_MODE_MASK BIT(10) +#define OMAP4430_BGAP_TEMP_SENSOR_SOC_MASK BIT(9) +#define OMAP4430_BGAP_TEMP_SENSOR_EOCZ_MASK BIT(8) +#define OMAP4430_BGAP_TEMP_SENSOR_DTEMP_MASK (0xff << 0) + +/** + * Temperature limits and thresholds for OMAP4430 + * + * All the macros bellow are definitions for handling the + * ADC conversions and representation of temperature limits + * and thresholds for OMAP4430. + */ + +/* ADC conversion table limits */ +#define OMAP4430_ADC_START_VALUE 0 +#define OMAP4430_ADC_END_VALUE 127 +/* bandgap clock limits (no control on 4430) */ +#define OMAP4430_MAX_FREQ 32768 +#define OMAP4430_MIN_FREQ 32768 +/* sensor limits */ +#define OMAP4430_MIN_TEMP -40000 +#define OMAP4430_MAX_TEMP 125000 +#define OMAP4430_HYST_VAL 5000 + +/** + * *** OMAP4460 *** Applicable for OMAP4470 + * + * Below, in sequence, are the Register definitions, + * the bitfields and the temperature definitions for OMAP4460. + */ + +/** + * OMAP4460 register definitions + * + * Registers are defined as offsets. The offsets are + * relative to FUSE_OPP_BGAP on 4460. + */ + +/* OMAP4460.FUSE_OPP_BGAP */ +#define OMAP4460_FUSE_OPP_BGAP 0x0 + +/* OMAP4460.TEMP_SENSOR */ +#define OMAP4460_TEMP_SENSOR_CTRL_OFFSET 0xCC + +/* OMAP4460.BANDGAP_CTRL */ +#define OMAP4460_BGAP_CTRL_OFFSET 0x118 + +/* OMAP4460.BANDGAP_COUNTER */ +#define OMAP4460_BGAP_COUNTER_OFFSET 0x11C + +/* OMAP4460.BANDGAP_THRESHOLD */ +#define OMAP4460_BGAP_THRESHOLD_OFFSET 0x120 + +/* OMAP4460.TSHUT_THRESHOLD */ +#define OMAP4460_BGAP_TSHUT_OFFSET 0x124 + +/* OMAP4460.BANDGAP_STATUS */ +#define OMAP4460_BGAP_STATUS_OFFSET 0x128 + +/** + * Register bitfields for OMAP4460 + * + * All the macros bellow define the required bits for + * controlling temperature on OMAP4460. Bit defines are + * grouped by register. + */ +/* OMAP4460.TEMP_SENSOR bits */ +#define OMAP4460_BGAP_TEMPSOFF_MASK BIT(13) +#define OMAP4460_BGAP_TEMP_SENSOR_SOC_MASK BIT(11) +#define OMAP4460_BGAP_TEMP_SENSOR_EOCZ_MASK BIT(10) +#define OMAP4460_BGAP_TEMP_SENSOR_DTEMP_MASK (0x3ff << 0) + +/* OMAP4460.BANDGAP_CTRL bits */ +#define OMAP4460_SINGLE_MODE_MASK BIT(31) +#define OMAP4460_MASK_HOT_MASK BIT(1) +#define OMAP4460_MASK_COLD_MASK BIT(0) + +/* OMAP4460.BANDGAP_COUNTER bits */ +#define OMAP4460_COUNTER_MASK (0xffffff << 0) + +/* OMAP4460.BANDGAP_THRESHOLD bits */ +#define OMAP4460_T_HOT_MASK (0x3ff << 16) +#define OMAP4460_T_COLD_MASK (0x3ff << 0) + +/* OMAP4460.TSHUT_THRESHOLD bits */ +#define OMAP4460_TSHUT_HOT_MASK (0x3ff << 16) +#define OMAP4460_TSHUT_COLD_MASK (0x3ff << 0) + +/* OMAP4460.BANDGAP_STATUS bits */ +#define OMAP4460_CLEAN_STOP_MASK BIT(3) +#define OMAP4460_BGAP_ALERT_MASK BIT(2) +#define OMAP4460_HOT_FLAG_MASK BIT(1) +#define OMAP4460_COLD_FLAG_MASK BIT(0) + +/** + * Temperature limits and thresholds for OMAP4460 + * + * All the macros bellow are definitions for handling the + * ADC conversions and representation of temperature limits + * and thresholds for OMAP4460. + */ + +/* ADC conversion table limits */ +#define OMAP4460_ADC_START_VALUE 530 +#define OMAP4460_ADC_END_VALUE 932 +/* bandgap clock limits */ +#define OMAP4460_MAX_FREQ 1500000 +#define OMAP4460_MIN_FREQ 1000000 +/* sensor limits */ +#define OMAP4460_MIN_TEMP -40000 +#define OMAP4460_MAX_TEMP 123000 +#define OMAP4460_HYST_VAL 5000 +/* interrupts thresholds */ +#define OMAP4460_TSHUT_HOT 900 /* 122 deg C */ +#define OMAP4460_TSHUT_COLD 895 /* 100 deg C */ +#define OMAP4460_T_HOT 800 /* 73 deg C */ +#define OMAP4460_T_COLD 795 /* 71 deg C */ + +#endif /* __OMAP4XXX_BANDGAP_H */ diff --git a/drivers/thermal/ti-soc-thermal/omap5-thermal-data.c b/drivers/thermal/ti-soc-thermal/omap5-thermal-data.c new file mode 100644 index 000000000000..eff0c80fd4af --- /dev/null +++ b/drivers/thermal/ti-soc-thermal/omap5-thermal-data.c @@ -0,0 +1,359 @@ +/* + * OMAP5 thermal driver. + * + * Copyright (C) 2011-2012 Texas Instruments Inc. + * Contact: + * Eduardo Valentin + * + * 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 "ti-thermal.h" +#include "ti-bandgap.h" +#include "omap5xxx-bandgap.h" + +/* + * OMAP5430 has three instances of thermal sensor for MPU, GPU & CORE, + * need to describe the individual registers and bit fields. + */ + +/* + * OMAP5430 MPU thermal sensor register offset and bit-fields + */ +static struct temp_sensor_registers +omap5430_mpu_temp_sensor_registers = { + .temp_sensor_ctrl = OMAP5430_TEMP_SENSOR_MPU_OFFSET, + .bgap_tempsoff_mask = OMAP5430_BGAP_TEMPSOFF_MASK, + .bgap_eocz_mask = OMAP5430_BGAP_TEMP_SENSOR_EOCZ_MASK, + .bgap_dtemp_mask = OMAP5430_BGAP_TEMP_SENSOR_DTEMP_MASK, + + .bgap_mask_ctrl = OMAP5430_BGAP_CTRL_OFFSET, + .mask_hot_mask = OMAP5430_MASK_HOT_MPU_MASK, + .mask_cold_mask = OMAP5430_MASK_COLD_MPU_MASK, + .mask_sidlemode_mask = OMAP5430_MASK_SIDLEMODE_MASK, + .mask_counter_delay_mask = OMAP5430_MASK_COUNTER_DELAY_MASK, + .mask_freeze_mask = OMAP5430_MASK_FREEZE_MPU_MASK, + .mask_clear_mask = OMAP5430_MASK_CLEAR_MPU_MASK, + .mask_clear_accum_mask = OMAP5430_MASK_CLEAR_ACCUM_MPU_MASK, + + + .bgap_counter = OMAP5430_BGAP_CTRL_OFFSET, + .counter_mask = OMAP5430_COUNTER_MASK, + + .bgap_threshold = OMAP5430_BGAP_THRESHOLD_MPU_OFFSET, + .threshold_thot_mask = OMAP5430_T_HOT_MASK, + .threshold_tcold_mask = OMAP5430_T_COLD_MASK, + + .tshut_threshold = OMAP5430_BGAP_TSHUT_MPU_OFFSET, + .tshut_hot_mask = OMAP5430_TSHUT_HOT_MASK, + .tshut_cold_mask = OMAP5430_TSHUT_COLD_MASK, + + .bgap_status = OMAP5430_BGAP_STATUS_OFFSET, + .status_clean_stop_mask = 0x0, + .status_bgap_alert_mask = OMAP5430_BGAP_ALERT_MASK, + .status_hot_mask = OMAP5430_HOT_MPU_FLAG_MASK, + .status_cold_mask = OMAP5430_COLD_MPU_FLAG_MASK, + + .bgap_cumul_dtemp = OMAP5430_BGAP_CUMUL_DTEMP_MPU_OFFSET, + .ctrl_dtemp_0 = OMAP5430_BGAP_DTEMP_MPU_0_OFFSET, + .ctrl_dtemp_1 = OMAP5430_BGAP_DTEMP_MPU_1_OFFSET, + .ctrl_dtemp_2 = OMAP5430_BGAP_DTEMP_MPU_2_OFFSET, + .ctrl_dtemp_3 = OMAP5430_BGAP_DTEMP_MPU_3_OFFSET, + .ctrl_dtemp_4 = OMAP5430_BGAP_DTEMP_MPU_4_OFFSET, + .bgap_efuse = OMAP5430_FUSE_OPP_BGAP_MPU, +}; + +/* + * OMAP5430 GPU thermal sensor register offset and bit-fields + */ +static struct temp_sensor_registers +omap5430_gpu_temp_sensor_registers = { + .temp_sensor_ctrl = OMAP5430_TEMP_SENSOR_GPU_OFFSET, + .bgap_tempsoff_mask = OMAP5430_BGAP_TEMPSOFF_MASK, + .bgap_eocz_mask = OMAP5430_BGAP_TEMP_SENSOR_EOCZ_MASK, + .bgap_dtemp_mask = OMAP5430_BGAP_TEMP_SENSOR_DTEMP_MASK, + + .bgap_mask_ctrl = OMAP5430_BGAP_CTRL_OFFSET, + .mask_hot_mask = OMAP5430_MASK_HOT_GPU_MASK, + .mask_cold_mask = OMAP5430_MASK_COLD_GPU_MASK, + .mask_sidlemode_mask = OMAP5430_MASK_SIDLEMODE_MASK, + .mask_counter_delay_mask = OMAP5430_MASK_COUNTER_DELAY_MASK, + .mask_freeze_mask = OMAP5430_MASK_FREEZE_GPU_MASK, + .mask_clear_mask = OMAP5430_MASK_CLEAR_GPU_MASK, + .mask_clear_accum_mask = OMAP5430_MASK_CLEAR_ACCUM_GPU_MASK, + + .bgap_counter = OMAP5430_BGAP_CTRL_OFFSET, + .counter_mask = OMAP5430_COUNTER_MASK, + + .bgap_threshold = OMAP5430_BGAP_THRESHOLD_GPU_OFFSET, + .threshold_thot_mask = OMAP5430_T_HOT_MASK, + .threshold_tcold_mask = OMAP5430_T_COLD_MASK, + + .tshut_threshold = OMAP5430_BGAP_TSHUT_GPU_OFFSET, + .tshut_hot_mask = OMAP5430_TSHUT_HOT_MASK, + .tshut_cold_mask = OMAP5430_TSHUT_COLD_MASK, + + .bgap_status = OMAP5430_BGAP_STATUS_OFFSET, + .status_clean_stop_mask = 0x0, + .status_bgap_alert_mask = OMAP5430_BGAP_ALERT_MASK, + .status_hot_mask = OMAP5430_HOT_GPU_FLAG_MASK, + .status_cold_mask = OMAP5430_COLD_GPU_FLAG_MASK, + + .bgap_cumul_dtemp = OMAP5430_BGAP_CUMUL_DTEMP_GPU_OFFSET, + .ctrl_dtemp_0 = OMAP5430_BGAP_DTEMP_GPU_0_OFFSET, + .ctrl_dtemp_1 = OMAP5430_BGAP_DTEMP_GPU_1_OFFSET, + .ctrl_dtemp_2 = OMAP5430_BGAP_DTEMP_GPU_2_OFFSET, + .ctrl_dtemp_3 = OMAP5430_BGAP_DTEMP_GPU_3_OFFSET, + .ctrl_dtemp_4 = OMAP5430_BGAP_DTEMP_GPU_4_OFFSET, + + .bgap_efuse = OMAP5430_FUSE_OPP_BGAP_GPU, +}; + +/* + * OMAP5430 CORE thermal sensor register offset and bit-fields + */ +static struct temp_sensor_registers +omap5430_core_temp_sensor_registers = { + .temp_sensor_ctrl = OMAP5430_TEMP_SENSOR_CORE_OFFSET, + .bgap_tempsoff_mask = OMAP5430_BGAP_TEMPSOFF_MASK, + .bgap_eocz_mask = OMAP5430_BGAP_TEMP_SENSOR_EOCZ_MASK, + .bgap_dtemp_mask = OMAP5430_BGAP_TEMP_SENSOR_DTEMP_MASK, + + .bgap_mask_ctrl = OMAP5430_BGAP_CTRL_OFFSET, + .mask_hot_mask = OMAP5430_MASK_HOT_CORE_MASK, + .mask_cold_mask = OMAP5430_MASK_COLD_CORE_MASK, + .mask_sidlemode_mask = OMAP5430_MASK_SIDLEMODE_MASK, + .mask_counter_delay_mask = OMAP5430_MASK_COUNTER_DELAY_MASK, + .mask_freeze_mask = OMAP5430_MASK_FREEZE_CORE_MASK, + .mask_clear_mask = OMAP5430_MASK_CLEAR_CORE_MASK, + .mask_clear_accum_mask = OMAP5430_MASK_CLEAR_ACCUM_CORE_MASK, + + .bgap_counter = OMAP5430_BGAP_CTRL_OFFSET, + .counter_mask = OMAP5430_COUNTER_MASK, + + .bgap_threshold = OMAP5430_BGAP_THRESHOLD_CORE_OFFSET, + .threshold_thot_mask = OMAP5430_T_HOT_MASK, + .threshold_tcold_mask = OMAP5430_T_COLD_MASK, + + .tshut_threshold = OMAP5430_BGAP_TSHUT_CORE_OFFSET, + .tshut_hot_mask = OMAP5430_TSHUT_HOT_MASK, + .tshut_cold_mask = OMAP5430_TSHUT_COLD_MASK, + + .bgap_status = OMAP5430_BGAP_STATUS_OFFSET, + .status_clean_stop_mask = 0x0, + .status_bgap_alert_mask = OMAP5430_BGAP_ALERT_MASK, + .status_hot_mask = OMAP5430_HOT_CORE_FLAG_MASK, + .status_cold_mask = OMAP5430_COLD_CORE_FLAG_MASK, + + .bgap_cumul_dtemp = OMAP5430_BGAP_CUMUL_DTEMP_CORE_OFFSET, + .ctrl_dtemp_0 = OMAP5430_BGAP_DTEMP_CORE_0_OFFSET, + .ctrl_dtemp_1 = OMAP5430_BGAP_DTEMP_CORE_1_OFFSET, + .ctrl_dtemp_2 = OMAP5430_BGAP_DTEMP_CORE_2_OFFSET, + .ctrl_dtemp_3 = OMAP5430_BGAP_DTEMP_CORE_3_OFFSET, + .ctrl_dtemp_4 = OMAP5430_BGAP_DTEMP_CORE_4_OFFSET, + + .bgap_efuse = OMAP5430_FUSE_OPP_BGAP_CORE, +}; + +/* Thresholds and limits for OMAP5430 MPU temperature sensor */ +static struct temp_sensor_data omap5430_mpu_temp_sensor_data = { + .tshut_hot = OMAP5430_MPU_TSHUT_HOT, + .tshut_cold = OMAP5430_MPU_TSHUT_COLD, + .t_hot = OMAP5430_MPU_T_HOT, + .t_cold = OMAP5430_MPU_T_COLD, + .min_freq = OMAP5430_MPU_MIN_FREQ, + .max_freq = OMAP5430_MPU_MAX_FREQ, + .max_temp = OMAP5430_MPU_MAX_TEMP, + .min_temp = OMAP5430_MPU_MIN_TEMP, + .hyst_val = OMAP5430_MPU_HYST_VAL, + .update_int1 = 1000, + .update_int2 = 2000, +}; + +/* Thresholds and limits for OMAP5430 GPU temperature sensor */ +static struct temp_sensor_data omap5430_gpu_temp_sensor_data = { + .tshut_hot = OMAP5430_GPU_TSHUT_HOT, + .tshut_cold = OMAP5430_GPU_TSHUT_COLD, + .t_hot = OMAP5430_GPU_T_HOT, + .t_cold = OMAP5430_GPU_T_COLD, + .min_freq = OMAP5430_GPU_MIN_FREQ, + .max_freq = OMAP5430_GPU_MAX_FREQ, + .max_temp = OMAP5430_GPU_MAX_TEMP, + .min_temp = OMAP5430_GPU_MIN_TEMP, + .hyst_val = OMAP5430_GPU_HYST_VAL, + .update_int1 = 1000, + .update_int2 = 2000, +}; + +/* Thresholds and limits for OMAP5430 CORE temperature sensor */ +static struct temp_sensor_data omap5430_core_temp_sensor_data = { + .tshut_hot = OMAP5430_CORE_TSHUT_HOT, + .tshut_cold = OMAP5430_CORE_TSHUT_COLD, + .t_hot = OMAP5430_CORE_T_HOT, + .t_cold = OMAP5430_CORE_T_COLD, + .min_freq = OMAP5430_CORE_MIN_FREQ, + .max_freq = OMAP5430_CORE_MAX_FREQ, + .max_temp = OMAP5430_CORE_MAX_TEMP, + .min_temp = OMAP5430_CORE_MIN_TEMP, + .hyst_val = OMAP5430_CORE_HYST_VAL, + .update_int1 = 1000, + .update_int2 = 2000, +}; + +/* + * OMAP54xx ES2.0 : Temperature values in milli degree celsius + * ADC code values from 540 to 945 + */ +static int +omap5430_adc_to_temp[ + OMAP5430_ADC_END_VALUE - OMAP5430_ADC_START_VALUE + 1] = { + /* Index 540 - 549 */ + -40000, -40000, -40000, -40000, -39800, -39400, -39000, -38600, -38200, + -37800, + /* Index 550 - 559 */ + -37400, -37000, -36600, -36200, -35800, -35300, -34700, -34200, -33800, + -33400, + /* Index 560 - 569 */ + -33000, -32600, -32200, -31800, -31400, -31000, -30600, -30200, -29800, + -29400, + /* Index 570 - 579 */ + -29000, -28600, -28200, -27700, -27100, -26600, -26200, -25800, -25400, + -25000, + /* Index 580 - 589 */ + -24600, -24200, -23800, -23400, -23000, -22600, -22200, -21600, -21400, + -21000, + /* Index 590 - 599 */ + -20500, -19900, -19400, -19000, -18600, -18200, -17800, -17400, -17000, + -16600, + /* Index 600 - 609 */ + -16200, -15800, -15400, -15000, -14600, -14200, -13800, -13400, -13000, + -12500, + /* Index 610 - 619 */ + -11900, -11400, -11000, -10600, -10200, -9800, -9400, -9000, -8600, + -8200, + /* Index 620 - 629 */ + -7800, -7400, -7000, -6600, -6200, -5800, -5400, -5000, -4500, -3900, + /* Index 630 - 639 */ + -3400, -3000, -2600, -2200, -1800, -1400, -1000, -600, -200, 200, + /* Index 640 - 649 */ + 600, 1000, 1400, 1800, 2200, 2600, 3000, 3400, 3900, 4500, + /* Index 650 - 659 */ + 5000, 5400, 5800, 6200, 6600, 7000, 7400, 7800, 8200, 8600, + /* Index 660 - 669 */ + 9000, 9400, 9800, 10200, 10600, 11000, 11400, 11800, 12200, 12700, + /* Index 670 - 679 */ + 13300, 13800, 14200, 14600, 15000, 15400, 15800, 16200, 16600, 17000, + /* Index 680 - 689 */ + 17400, 17800, 18200, 18600, 19000, 19400, 19800, 20200, 20600, 21100, + /* Index 690 - 699 */ + 21400, 21900, 22500, 23000, 23400, 23800, 24200, 24600, 25000, 25400, + /* Index 700 - 709 */ + 25800, 26200, 26600, 27000, 27400, 27800, 28200, 28600, 29000, 29400, + /* Index 710 - 719 */ + 29800, 30200, 30600, 31000, 31400, 31900, 32500, 33000, 33400, 33800, + /* Index 720 - 729 */ + 34200, 34600, 35000, 35400, 35800, 36200, 36600, 37000, 37400, 37800, + /* Index 730 - 739 */ + 38200, 38600, 39000, 39400, 39800, 40200, 40600, 41000, 41400, 41800, + /* Index 740 - 749 */ + 42200, 42600, 43100, 43700, 44200, 44600, 45000, 45400, 45800, 46200, + /* Index 750 - 759 */ + 46600, 47000, 47400, 47800, 48200, 48600, 49000, 49400, 49800, 50200, + /* Index 760 - 769 */ + 50600, 51000, 51400, 51800, 52200, 52600, 53000, 53400, 53800, 54200, + /* Index 770 - 779 */ + 54600, 55000, 55400, 55900, 56500, 57000, 57400, 57800, 58200, 58600, + /* Index 780 - 789 */ + 59000, 59400, 59800, 60200, 60600, 61000, 61400, 61800, 62200, 62600, + /* Index 790 - 799 */ + 63000, 63400, 63800, 64200, 64600, 65000, 65400, 65800, 66200, 66600, + /* Index 800 - 809 */ + 67000, 67400, 67800, 68200, 68600, 69000, 69400, 69800, 70200, 70600, + /* Index 810 - 819 */ + 71000, 71500, 72100, 72600, 73000, 73400, 73800, 74200, 74600, 75000, + /* Index 820 - 829 */ + 75400, 75800, 76200, 76600, 77000, 77400, 77800, 78200, 78600, 79000, + /* Index 830 - 839 */ + 79400, 79800, 80200, 80600, 81000, 81400, 81800, 82200, 82600, 83000, + /* Index 840 - 849 */ + 83400, 83800, 84200, 84600, 85000, 85400, 85800, 86200, 86600, 87000, + /* Index 850 - 859 */ + 87400, 87800, 88200, 88600, 89000, 89400, 89800, 90200, 90600, 91000, + /* Index 860 - 869 */ + 91400, 91800, 92200, 92600, 93000, 93400, 93800, 94200, 94600, 95000, + /* Index 870 - 879 */ + 95400, 95800, 96200, 96600, 97000, 97500, 98100, 98600, 99000, 99400, + /* Index 880 - 889 */ + 99800, 100200, 100600, 101000, 101400, 101800, 102200, 102600, 103000, + 103400, + /* Index 890 - 899 */ + 103800, 104200, 104600, 105000, 105400, 105800, 106200, 106600, 107000, + 107400, + /* Index 900 - 909 */ + 107800, 108200, 108600, 109000, 109400, 109800, 110200, 110600, 111000, + 111400, + /* Index 910 - 919 */ + 111800, 112200, 112600, 113000, 113400, 113800, 114200, 114600, 115000, + 115400, + /* Index 920 - 929 */ + 115800, 116200, 116600, 117000, 117400, 117800, 118200, 118600, 119000, + 119400, + /* Index 930 - 939 */ + 119800, 120200, 120600, 121000, 121400, 121800, 122400, 122600, 123000, + 123400, + /* Index 940 - 945 */ + 123800, 1242000, 124600, 124900, 125000, 125000, +}; + +/* OMAP54xx ES2.0 data */ +const struct ti_bandgap_data omap5430_data = { + .features = TI_BANDGAP_FEATURE_TSHUT_CONFIG | + TI_BANDGAP_FEATURE_FREEZE_BIT | + TI_BANDGAP_FEATURE_TALERT | + TI_BANDGAP_FEATURE_COUNTER_DELAY | + TI_BANDGAP_FEATURE_HISTORY_BUFFER, + .fclock_name = "l3instr_ts_gclk_div", + .div_ck_name = "l3instr_ts_gclk_div", + .conv_table = omap5430_adc_to_temp, + .adc_start_val = OMAP5430_ADC_START_VALUE, + .adc_end_val = OMAP5430_ADC_END_VALUE, + .expose_sensor = ti_thermal_expose_sensor, + .remove_sensor = ti_thermal_remove_sensor, + .report_temperature = ti_thermal_report_sensor_temperature, + .sensors = { + { + .registers = &omap5430_mpu_temp_sensor_registers, + .ts_data = &omap5430_mpu_temp_sensor_data, + .domain = "cpu", + .register_cooling = ti_thermal_register_cpu_cooling, + .unregister_cooling = ti_thermal_unregister_cpu_cooling, + .slope = OMAP_GRADIENT_SLOPE_5430_CPU, + .constant = OMAP_GRADIENT_CONST_5430_CPU, + .slope_pcb = OMAP_GRADIENT_SLOPE_W_PCB_5430_CPU, + .constant_pcb = OMAP_GRADIENT_CONST_W_PCB_5430_CPU, + }, + { + .registers = &omap5430_gpu_temp_sensor_registers, + .ts_data = &omap5430_gpu_temp_sensor_data, + .domain = "gpu", + .slope = OMAP_GRADIENT_SLOPE_5430_GPU, + .constant = OMAP_GRADIENT_CONST_5430_GPU, + .slope_pcb = OMAP_GRADIENT_SLOPE_W_PCB_5430_GPU, + .constant_pcb = OMAP_GRADIENT_CONST_W_PCB_5430_GPU, + }, + { + .registers = &omap5430_core_temp_sensor_registers, + .ts_data = &omap5430_core_temp_sensor_data, + .domain = "core", + }, + }, + .sensor_count = 3, +}; diff --git a/drivers/thermal/ti-soc-thermal/omap5xxx-bandgap.h b/drivers/thermal/ti-soc-thermal/omap5xxx-bandgap.h new file mode 100644 index 000000000000..400b55dffadd --- /dev/null +++ b/drivers/thermal/ti-soc-thermal/omap5xxx-bandgap.h @@ -0,0 +1,200 @@ +/* + * OMAP5xxx bandgap registers, bitfields and temperature definitions + * + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/ + * Contact: + * Eduardo Valentin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ +#ifndef __OMAP5XXX_BANDGAP_H +#define __OMAP5XXX_BANDGAP_H + +/** + * *** OMAP5430 *** + * + * Below, in sequence, are the Register definitions, + * the bitfields and the temperature definitions for OMAP5430. + */ + +/** + * OMAP5430 register definitions + * + * Registers are defined as offsets. The offsets are + * relative to FUSE_OPP_BGAP_GPU on 5430. + * + * Register below are grouped by domain (not necessarily in offset order) + */ + +/* OMAP5430.GPU register offsets */ +#define OMAP5430_FUSE_OPP_BGAP_GPU 0x0 +#define OMAP5430_TEMP_SENSOR_GPU_OFFSET 0x150 +#define OMAP5430_BGAP_THRESHOLD_GPU_OFFSET 0x1A8 +#define OMAP5430_BGAP_TSHUT_GPU_OFFSET 0x1B4 +#define OMAP5430_BGAP_CUMUL_DTEMP_GPU_OFFSET 0x1C0 +#define OMAP5430_BGAP_DTEMP_GPU_0_OFFSET 0x1F4 +#define OMAP5430_BGAP_DTEMP_GPU_1_OFFSET 0x1F8 +#define OMAP5430_BGAP_DTEMP_GPU_2_OFFSET 0x1FC +#define OMAP5430_BGAP_DTEMP_GPU_3_OFFSET 0x200 +#define OMAP5430_BGAP_DTEMP_GPU_4_OFFSET 0x204 + +/* OMAP5430.MPU register offsets */ +#define OMAP5430_FUSE_OPP_BGAP_MPU 0x4 +#define OMAP5430_TEMP_SENSOR_MPU_OFFSET 0x14C +#define OMAP5430_BGAP_THRESHOLD_MPU_OFFSET 0x1A4 +#define OMAP5430_BGAP_TSHUT_MPU_OFFSET 0x1B0 +#define OMAP5430_BGAP_CUMUL_DTEMP_MPU_OFFSET 0x1BC +#define OMAP5430_BGAP_DTEMP_MPU_0_OFFSET 0x1E0 +#define OMAP5430_BGAP_DTEMP_MPU_1_OFFSET 0x1E4 +#define OMAP5430_BGAP_DTEMP_MPU_2_OFFSET 0x1E8 +#define OMAP5430_BGAP_DTEMP_MPU_3_OFFSET 0x1EC +#define OMAP5430_BGAP_DTEMP_MPU_4_OFFSET 0x1F0 + +/* OMAP5430.MPU register offsets */ +#define OMAP5430_FUSE_OPP_BGAP_CORE 0x8 +#define OMAP5430_TEMP_SENSOR_CORE_OFFSET 0x154 +#define OMAP5430_BGAP_THRESHOLD_CORE_OFFSET 0x1AC +#define OMAP5430_BGAP_TSHUT_CORE_OFFSET 0x1B8 +#define OMAP5430_BGAP_CUMUL_DTEMP_CORE_OFFSET 0x1C4 +#define OMAP5430_BGAP_DTEMP_CORE_0_OFFSET 0x208 +#define OMAP5430_BGAP_DTEMP_CORE_1_OFFSET 0x20C +#define OMAP5430_BGAP_DTEMP_CORE_2_OFFSET 0x210 +#define OMAP5430_BGAP_DTEMP_CORE_3_OFFSET 0x214 +#define OMAP5430_BGAP_DTEMP_CORE_4_OFFSET 0x218 + +/* OMAP5430.common register offsets */ +#define OMAP5430_BGAP_CTRL_OFFSET 0x1A0 +#define OMAP5430_BGAP_STATUS_OFFSET 0x1C8 + +/** + * Register bitfields for OMAP5430 + * + * All the macros bellow define the required bits for + * controlling temperature on OMAP5430. Bit defines are + * grouped by register. + */ + +/* OMAP5430.TEMP_SENSOR */ +#define OMAP5430_BGAP_TEMP_SENSOR_SOC_MASK BIT(12) +#define OMAP5430_BGAP_TEMPSOFF_MASK BIT(11) +#define OMAP5430_BGAP_TEMP_SENSOR_EOCZ_MASK BIT(10) +#define OMAP5430_BGAP_TEMP_SENSOR_DTEMP_MASK (0x3ff << 0) + +/* OMAP5430.BANDGAP_CTRL */ +#define OMAP5430_MASK_SIDLEMODE_MASK (0x3 << 30) +#define OMAP5430_MASK_COUNTER_DELAY_MASK (0x7 << 27) +#define OMAP5430_MASK_FREEZE_CORE_MASK BIT(23) +#define OMAP5430_MASK_FREEZE_GPU_MASK BIT(22) +#define OMAP5430_MASK_FREEZE_MPU_MASK BIT(21) +#define OMAP5430_MASK_CLEAR_CORE_MASK BIT(20) +#define OMAP5430_MASK_CLEAR_GPU_MASK BIT(19) +#define OMAP5430_MASK_CLEAR_MPU_MASK BIT(18) +#define OMAP5430_MASK_CLEAR_ACCUM_CORE_MASK BIT(17) +#define OMAP5430_MASK_CLEAR_ACCUM_GPU_MASK BIT(16) +#define OMAP5430_MASK_CLEAR_ACCUM_MPU_MASK BIT(15) +#define OMAP5430_MASK_HOT_CORE_MASK BIT(5) +#define OMAP5430_MASK_COLD_CORE_MASK BIT(4) +#define OMAP5430_MASK_HOT_GPU_MASK BIT(3) +#define OMAP5430_MASK_COLD_GPU_MASK BIT(2) +#define OMAP5430_MASK_HOT_MPU_MASK BIT(1) +#define OMAP5430_MASK_COLD_MPU_MASK BIT(0) + +/* OMAP5430.BANDGAP_COUNTER */ +#define OMAP5430_COUNTER_MASK (0xffffff << 0) + +/* OMAP5430.BANDGAP_THRESHOLD */ +#define OMAP5430_T_HOT_MASK (0x3ff << 16) +#define OMAP5430_T_COLD_MASK (0x3ff << 0) + +/* OMAP5430.TSHUT_THRESHOLD */ +#define OMAP5430_TSHUT_HOT_MASK (0x3ff << 16) +#define OMAP5430_TSHUT_COLD_MASK (0x3ff << 0) + +/* OMAP5430.BANDGAP_CUMUL_DTEMP_MPU */ +#define OMAP5430_CUMUL_DTEMP_MPU_MASK (0xffffffff << 0) + +/* OMAP5430.BANDGAP_CUMUL_DTEMP_GPU */ +#define OMAP5430_CUMUL_DTEMP_GPU_MASK (0xffffffff << 0) + +/* OMAP5430.BANDGAP_CUMUL_DTEMP_CORE */ +#define OMAP5430_CUMUL_DTEMP_CORE_MASK (0xffffffff << 0) + +/* OMAP5430.BANDGAP_STATUS */ +#define OMAP5430_BGAP_ALERT_MASK BIT(31) +#define OMAP5430_HOT_CORE_FLAG_MASK BIT(5) +#define OMAP5430_COLD_CORE_FLAG_MASK BIT(4) +#define OMAP5430_HOT_GPU_FLAG_MASK BIT(3) +#define OMAP5430_COLD_GPU_FLAG_MASK BIT(2) +#define OMAP5430_HOT_MPU_FLAG_MASK BIT(1) +#define OMAP5430_COLD_MPU_FLAG_MASK BIT(0) + +/** + * Temperature limits and thresholds for OMAP5430 + * + * All the macros bellow are definitions for handling the + * ADC conversions and representation of temperature limits + * and thresholds for OMAP5430. Definitions are grouped + * by temperature domain. + */ + +/* OMAP5430.common temperature definitions */ +/* ADC conversion table limits */ +#define OMAP5430_ADC_START_VALUE 540 +#define OMAP5430_ADC_END_VALUE 945 + +/* OMAP5430.GPU temperature definitions */ +/* bandgap clock limits */ +#define OMAP5430_GPU_MAX_FREQ 1500000 +#define OMAP5430_GPU_MIN_FREQ 1000000 +/* sensor limits */ +#define OMAP5430_GPU_MIN_TEMP -40000 +#define OMAP5430_GPU_MAX_TEMP 125000 +#define OMAP5430_GPU_HYST_VAL 5000 +/* interrupts thresholds */ +#define OMAP5430_GPU_TSHUT_HOT 915 +#define OMAP5430_GPU_TSHUT_COLD 900 +#define OMAP5430_GPU_T_HOT 800 +#define OMAP5430_GPU_T_COLD 795 + +/* OMAP5430.MPU temperature definitions */ +/* bandgap clock limits */ +#define OMAP5430_MPU_MAX_FREQ 1500000 +#define OMAP5430_MPU_MIN_FREQ 1000000 +/* sensor limits */ +#define OMAP5430_MPU_MIN_TEMP -40000 +#define OMAP5430_MPU_MAX_TEMP 125000 +#define OMAP5430_MPU_HYST_VAL 5000 +/* interrupts thresholds */ +#define OMAP5430_MPU_TSHUT_HOT 915 +#define OMAP5430_MPU_TSHUT_COLD 900 +#define OMAP5430_MPU_T_HOT 800 +#define OMAP5430_MPU_T_COLD 795 + +/* OMAP5430.CORE temperature definitions */ +/* bandgap clock limits */ +#define OMAP5430_CORE_MAX_FREQ 1500000 +#define OMAP5430_CORE_MIN_FREQ 1000000 +/* sensor limits */ +#define OMAP5430_CORE_MIN_TEMP -40000 +#define OMAP5430_CORE_MAX_TEMP 125000 +#define OMAP5430_CORE_HYST_VAL 5000 +/* interrupts thresholds */ +#define OMAP5430_CORE_TSHUT_HOT 915 +#define OMAP5430_CORE_TSHUT_COLD 900 +#define OMAP5430_CORE_T_HOT 800 +#define OMAP5430_CORE_T_COLD 795 + +#endif /* __OMAP5XXX_BANDGAP_H */ diff --git a/drivers/thermal/ti-soc-thermal/ti-bandgap.c b/drivers/thermal/ti-soc-thermal/ti-bandgap.c new file mode 100644 index 000000000000..f20c1cfe9800 --- /dev/null +++ b/drivers/thermal/ti-soc-thermal/ti-bandgap.c @@ -0,0 +1,1546 @@ +/* + * TI Bandgap temperature sensor driver + * + * Copyright (C) 2011-2012 Texas Instruments Incorporated - http://www.ti.com/ + * Author: J Keerthy + * Author: Moiz Sonasath + * Couple of fixes, DT and MFD adaptation: + * Eduardo Valentin + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ti-bandgap.h" + +/*** Helper functions to access registers and their bitfields ***/ + +/** + * ti_bandgap_readl() - simple read helper function + * @bgp: pointer to ti_bandgap structure + * @reg: desired register (offset) to be read + * + * Helper function to read bandgap registers. It uses the io remapped area. + * Return: the register value. + */ +static u32 ti_bandgap_readl(struct ti_bandgap *bgp, u32 reg) +{ + return readl(bgp->base + reg); +} + +/** + * ti_bandgap_writel() - simple write helper function + * @bgp: pointer to ti_bandgap structure + * @val: desired register value to be written + * @reg: desired register (offset) to be written + * + * Helper function to write bandgap registers. It uses the io remapped area. + */ +static void ti_bandgap_writel(struct ti_bandgap *bgp, u32 val, u32 reg) +{ + writel(val, bgp->base + reg); +} + +/** + * DOC: macro to update bits. + * + * RMW_BITS() - used to read, modify and update bandgap bitfields. + * The value passed will be shifted. + */ +#define RMW_BITS(bgp, id, reg, mask, val) \ +do { \ + struct temp_sensor_registers *t; \ + u32 r; \ + \ + t = bgp->conf->sensors[(id)].registers; \ + r = ti_bandgap_readl(bgp, t->reg); \ + r &= ~t->mask; \ + r |= (val) << __ffs(t->mask); \ + ti_bandgap_writel(bgp, r, t->reg); \ +} while (0) + +/*** Basic helper functions ***/ + +/** + * ti_bandgap_power() - controls the power state of a bandgap device + * @bgp: pointer to ti_bandgap structure + * @on: desired power state (1 - on, 0 - off) + * + * Used to power on/off a bandgap device instance. Only used on those + * that features tempsoff bit. + * + * Return: 0 on success, -ENOTSUPP if tempsoff is not supported. + */ +static int ti_bandgap_power(struct ti_bandgap *bgp, bool on) +{ + int i, ret = 0; + + if (!TI_BANDGAP_HAS(bgp, POWER_SWITCH)) { + ret = -ENOTSUPP; + goto exit; + } + + for (i = 0; i < bgp->conf->sensor_count; i++) + /* active on 0 */ + RMW_BITS(bgp, i, temp_sensor_ctrl, bgap_tempsoff_mask, !on); + +exit: + return ret; +} + +/** + * ti_bandgap_read_temp() - helper function to read sensor temperature + * @bgp: pointer to ti_bandgap structure + * @id: bandgap sensor id + * + * Function to concentrate the steps to read sensor temperature register. + * This function is desired because, depending on bandgap device version, + * it might be needed to freeze the bandgap state machine, before fetching + * the register value. + * + * Return: temperature in ADC values. + */ +static u32 ti_bandgap_read_temp(struct ti_bandgap *bgp, int id) +{ + struct temp_sensor_registers *tsr; + u32 temp, reg; + + tsr = bgp->conf->sensors[id].registers; + reg = tsr->temp_sensor_ctrl; + + if (TI_BANDGAP_HAS(bgp, FREEZE_BIT)) { + RMW_BITS(bgp, id, bgap_mask_ctrl, mask_freeze_mask, 1); + /* + * In case we cannot read from cur_dtemp / dtemp_0, + * then we read from the last valid temp read + */ + reg = tsr->ctrl_dtemp_1; + } + + /* read temperature */ + temp = ti_bandgap_readl(bgp, reg); + temp &= tsr->bgap_dtemp_mask; + + if (TI_BANDGAP_HAS(bgp, FREEZE_BIT)) + RMW_BITS(bgp, id, bgap_mask_ctrl, mask_freeze_mask, 0); + + return temp; +} + +/*** IRQ handlers ***/ + +/** + * ti_bandgap_talert_irq_handler() - handles Temperature alert IRQs + * @irq: IRQ number + * @data: private data (struct ti_bandgap *) + * + * This is the Talert handler. Use it only if bandgap device features + * HAS(TALERT). This handler goes over all sensors and checks their + * conditions and acts accordingly. In case there are events pending, + * it will reset the event mask to wait for the opposite event (next event). + * Every time there is a new event, it will be reported to thermal layer. + * + * Return: IRQ_HANDLED + */ +static irqreturn_t ti_bandgap_talert_irq_handler(int irq, void *data) +{ + struct ti_bandgap *bgp = data; + struct temp_sensor_registers *tsr; + u32 t_hot = 0, t_cold = 0, ctrl; + int i; + + spin_lock(&bgp->lock); + for (i = 0; i < bgp->conf->sensor_count; i++) { + tsr = bgp->conf->sensors[i].registers; + ctrl = ti_bandgap_readl(bgp, tsr->bgap_status); + + /* Read the status of t_hot */ + t_hot = ctrl & tsr->status_hot_mask; + + /* Read the status of t_cold */ + t_cold = ctrl & tsr->status_cold_mask; + + if (!t_cold && !t_hot) + continue; + + ctrl = ti_bandgap_readl(bgp, tsr->bgap_mask_ctrl); + /* + * One TALERT interrupt: Two sources + * If the interrupt is due to t_hot then mask t_hot and + * and unmask t_cold else mask t_cold and unmask t_hot + */ + if (t_hot) { + ctrl &= ~tsr->mask_hot_mask; + ctrl |= tsr->mask_cold_mask; + } else if (t_cold) { + ctrl &= ~tsr->mask_cold_mask; + ctrl |= tsr->mask_hot_mask; + } + + ti_bandgap_writel(bgp, ctrl, tsr->bgap_mask_ctrl); + + dev_dbg(bgp->dev, + "%s: IRQ from %s sensor: hotevent %d coldevent %d\n", + __func__, bgp->conf->sensors[i].domain, + t_hot, t_cold); + + /* report temperature to whom may concern */ + if (bgp->conf->report_temperature) + bgp->conf->report_temperature(bgp, i); + } + spin_unlock(&bgp->lock); + + return IRQ_HANDLED; +} + +/** + * ti_bandgap_tshut_irq_handler() - handles Temperature shutdown signal + * @irq: IRQ number + * @data: private data (unused) + * + * This is the Tshut handler. Use it only if bandgap device features + * HAS(TSHUT). If any sensor fires the Tshut signal, we simply shutdown + * the system. + * + * Return: IRQ_HANDLED + */ +static irqreturn_t ti_bandgap_tshut_irq_handler(int irq, void *data) +{ + pr_emerg("%s: TSHUT temperature reached. Needs shut down...\n", + __func__); + + orderly_poweroff(true); + + return IRQ_HANDLED; +} + +/*** Helper functions which manipulate conversion ADC <-> mi Celsius ***/ + +/** + * ti_bandgap_adc_to_mcelsius() - converts an ADC value to mCelsius scale + * @bgp: struct ti_bandgap pointer + * @adc_val: value in ADC representation + * @t: address where to write the resulting temperature in mCelsius + * + * Simple conversion from ADC representation to mCelsius. In case the ADC value + * is out of the ADC conv table range, it returns -ERANGE, 0 on success. + * The conversion table is indexed by the ADC values. + * + * Return: 0 if conversion was successful, else -ERANGE in case the @adc_val + * argument is out of the ADC conv table range. + */ +static +int ti_bandgap_adc_to_mcelsius(struct ti_bandgap *bgp, int adc_val, int *t) +{ + const struct ti_bandgap_data *conf = bgp->conf; + int ret = 0; + + /* look up for temperature in the table and return the temperature */ + if (adc_val < conf->adc_start_val || adc_val > conf->adc_end_val) { + ret = -ERANGE; + goto exit; + } + + *t = bgp->conf->conv_table[adc_val - conf->adc_start_val]; + +exit: + return ret; +} + +/** + * ti_bandgap_mcelsius_to_adc() - converts a mCelsius value to ADC scale + * @bgp: struct ti_bandgap pointer + * @temp: value in mCelsius + * @adc: address where to write the resulting temperature in ADC representation + * + * Simple conversion from mCelsius to ADC values. In case the temp value + * is out of the ADC conv table range, it returns -ERANGE, 0 on success. + * The conversion table is indexed by the ADC values. + * + * Return: 0 if conversion was successful, else -ERANGE in case the @temp + * argument is out of the ADC conv table range. + */ +static +int ti_bandgap_mcelsius_to_adc(struct ti_bandgap *bgp, long temp, int *adc) +{ + const struct ti_bandgap_data *conf = bgp->conf; + const int *conv_table = bgp->conf->conv_table; + int high, low, mid, ret = 0; + + low = 0; + high = conf->adc_end_val - conf->adc_start_val; + mid = (high + low) / 2; + + if (temp < conv_table[low] || temp > conv_table[high]) { + ret = -ERANGE; + goto exit; + } + + while (low < high) { + if (temp < conv_table[mid]) + high = mid - 1; + else + low = mid + 1; + mid = (low + high) / 2; + } + + *adc = conf->adc_start_val + low; + +exit: + return ret; +} + +/** + * ti_bandgap_add_hyst() - add hysteresis (in mCelsius) to an ADC value + * @bgp: struct ti_bandgap pointer + * @adc_val: temperature value in ADC representation + * @hyst_val: hysteresis value in mCelsius + * @sum: address where to write the resulting temperature (in ADC scale) + * + * Adds an hysteresis value (in mCelsius) to a ADC temperature value. + * + * Return: 0 on success, -ERANGE otherwise. + */ +static +int ti_bandgap_add_hyst(struct ti_bandgap *bgp, int adc_val, int hyst_val, + u32 *sum) +{ + int temp, ret; + + /* + * Need to add in the mcelsius domain, so we have a temperature + * the conv_table range + */ + ret = ti_bandgap_adc_to_mcelsius(bgp, adc_val, &temp); + if (ret < 0) + goto exit; + + temp += hyst_val; + + ret = ti_bandgap_mcelsius_to_adc(bgp, temp, sum); + +exit: + return ret; +} + +/*** Helper functions handling device Alert/Shutdown signals ***/ + +/** + * ti_bandgap_unmask_interrupts() - unmasks the events of thot & tcold + * @bgp: struct ti_bandgap pointer + * @id: bandgap sensor id + * @t_hot: hot temperature value to trigger alert signal + * @t_cold: cold temperature value to trigger alert signal + * + * Checks the requested t_hot and t_cold values and configures the IRQ event + * masks accordingly. Call this function only if bandgap features HAS(TALERT). + */ +static void ti_bandgap_unmask_interrupts(struct ti_bandgap *bgp, int id, + u32 t_hot, u32 t_cold) +{ + struct temp_sensor_registers *tsr; + u32 temp, reg_val; + + /* Read the current on die temperature */ + temp = ti_bandgap_read_temp(bgp, id); + + tsr = bgp->conf->sensors[id].registers; + reg_val = ti_bandgap_readl(bgp, tsr->bgap_mask_ctrl); + + if (temp < t_hot) + reg_val |= tsr->mask_hot_mask; + else + reg_val &= ~tsr->mask_hot_mask; + + if (t_cold < temp) + reg_val |= tsr->mask_cold_mask; + else + reg_val &= ~tsr->mask_cold_mask; + ti_bandgap_writel(bgp, reg_val, tsr->bgap_mask_ctrl); +} + +/** + * ti_bandgap_update_alert_threshold() - sequence to update thresholds + * @bgp: struct ti_bandgap pointer + * @id: bandgap sensor id + * @val: value (ADC) of a new threshold + * @hot: desired threshold to be updated. true if threshold hot, false if + * threshold cold + * + * It will program the required thresholds (hot and cold) for TALERT signal. + * This function can be used to update t_hot or t_cold, depending on @hot value. + * It checks the resulting t_hot and t_cold values, based on the new passed @val + * and configures the thresholds so that t_hot is always greater than t_cold. + * Call this function only if bandgap features HAS(TALERT). + * + * Return: 0 if no error, else corresponding error + */ +static int ti_bandgap_update_alert_threshold(struct ti_bandgap *bgp, int id, + int val, bool hot) +{ + struct temp_sensor_data *ts_data = bgp->conf->sensors[id].ts_data; + struct temp_sensor_registers *tsr; + u32 thresh_val, reg_val, t_hot, t_cold; + int err = 0; + + tsr = bgp->conf->sensors[id].registers; + + /* obtain the current value */ + thresh_val = ti_bandgap_readl(bgp, tsr->bgap_threshold); + t_cold = (thresh_val & tsr->threshold_tcold_mask) >> + __ffs(tsr->threshold_tcold_mask); + t_hot = (thresh_val & tsr->threshold_thot_mask) >> + __ffs(tsr->threshold_thot_mask); + if (hot) + t_hot = val; + else + t_cold = val; + + if (t_cold > t_hot) { + if (hot) + err = ti_bandgap_add_hyst(bgp, t_hot, + -ts_data->hyst_val, + &t_cold); + else + err = ti_bandgap_add_hyst(bgp, t_cold, + ts_data->hyst_val, + &t_hot); + } + + /* write the new threshold values */ + reg_val = thresh_val & + ~(tsr->threshold_thot_mask | tsr->threshold_tcold_mask); + reg_val |= (t_hot << __ffs(tsr->threshold_thot_mask)) | + (t_cold << __ffs(tsr->threshold_tcold_mask)); + ti_bandgap_writel(bgp, reg_val, tsr->bgap_threshold); + + if (err) { + dev_err(bgp->dev, "failed to reprogram thot threshold\n"); + err = -EIO; + goto exit; + } + + ti_bandgap_unmask_interrupts(bgp, id, t_hot, t_cold); +exit: + return err; +} + +/** + * ti_bandgap_validate() - helper to check the sanity of a struct ti_bandgap + * @bgp: struct ti_bandgap pointer + * @id: bandgap sensor id + * + * Checks if the bandgap pointer is valid and if the sensor id is also + * applicable. + * + * Return: 0 if no errors, -EINVAL for invalid @bgp pointer or -ERANGE if + * @id cannot index @bgp sensors. + */ +static inline int ti_bandgap_validate(struct ti_bandgap *bgp, int id) +{ + int ret = 0; + + if (IS_ERR_OR_NULL(bgp)) { + pr_err("%s: invalid bandgap pointer\n", __func__); + ret = -EINVAL; + goto exit; + } + + if ((id < 0) || (id >= bgp->conf->sensor_count)) { + dev_err(bgp->dev, "%s: sensor id out of range (%d)\n", + __func__, id); + ret = -ERANGE; + } + +exit: + return ret; +} + +/** + * _ti_bandgap_write_threshold() - helper to update TALERT t_cold or t_hot + * @bgp: struct ti_bandgap pointer + * @id: bandgap sensor id + * @val: value (mCelsius) of a new threshold + * @hot: desired threshold to be updated. true if threshold hot, false if + * threshold cold + * + * It will update the required thresholds (hot and cold) for TALERT signal. + * This function can be used to update t_hot or t_cold, depending on @hot value. + * Validates the mCelsius range and update the requested threshold. + * Call this function only if bandgap features HAS(TALERT). + * + * Return: 0 if no error, else corresponding error value. + */ +static int _ti_bandgap_write_threshold(struct ti_bandgap *bgp, int id, int val, + bool hot) +{ + struct temp_sensor_data *ts_data; + struct temp_sensor_registers *tsr; + u32 adc_val; + int ret; + + ret = ti_bandgap_validate(bgp, id); + if (ret) + goto exit; + + if (!TI_BANDGAP_HAS(bgp, TALERT)) { + ret = -ENOTSUPP; + goto exit; + } + + ts_data = bgp->conf->sensors[id].ts_data; + tsr = bgp->conf->sensors[id].registers; + if (hot) { + if (val < ts_data->min_temp + ts_data->hyst_val) + ret = -EINVAL; + } else { + if (val > ts_data->max_temp + ts_data->hyst_val) + ret = -EINVAL; + } + + if (ret) + goto exit; + + ret = ti_bandgap_mcelsius_to_adc(bgp, val, &adc_val); + if (ret < 0) + goto exit; + + spin_lock(&bgp->lock); + ret = ti_bandgap_update_alert_threshold(bgp, id, adc_val, hot); + spin_unlock(&bgp->lock); + +exit: + return ret; +} + +/** + * _ti_bandgap_read_threshold() - helper to read TALERT t_cold or t_hot + * @bgp: struct ti_bandgap pointer + * @id: bandgap sensor id + * @val: value (mCelsius) of a threshold + * @hot: desired threshold to be read. true if threshold hot, false if + * threshold cold + * + * It will fetch the required thresholds (hot and cold) for TALERT signal. + * This function can be used to read t_hot or t_cold, depending on @hot value. + * Call this function only if bandgap features HAS(TALERT). + * + * Return: 0 if no error, -ENOTSUPP if it has no TALERT support, or the + * corresponding error value if some operation fails. + */ +static int _ti_bandgap_read_threshold(struct ti_bandgap *bgp, int id, + int *val, bool hot) +{ + struct temp_sensor_registers *tsr; + u32 temp, mask; + int ret = 0; + + ret = ti_bandgap_validate(bgp, id); + if (ret) + goto exit; + + if (!TI_BANDGAP_HAS(bgp, TALERT)) { + ret = -ENOTSUPP; + goto exit; + } + + tsr = bgp->conf->sensors[id].registers; + if (hot) + mask = tsr->threshold_thot_mask; + else + mask = tsr->threshold_tcold_mask; + + temp = ti_bandgap_readl(bgp, tsr->bgap_threshold); + temp = (temp & mask) >> __ffs(mask); + ret |= ti_bandgap_adc_to_mcelsius(bgp, temp, &temp); + if (ret) { + dev_err(bgp->dev, "failed to read thot\n"); + ret = -EIO; + goto exit; + } + + *val = temp; + +exit: + return ret; +} + +/*** Exposed APIs ***/ + +/** + * ti_bandgap_read_thot() - reads sensor current thot + * @bgp: pointer to bandgap instance + * @id: sensor id + * @thot: resulting current thot value + * + * Return: 0 on success or the proper error code + */ +int ti_bandgap_read_thot(struct ti_bandgap *bgp, int id, int *thot) +{ + return _ti_bandgap_read_threshold(bgp, id, thot, true); +} + +/** + * ti_bandgap_write_thot() - sets sensor current thot + * @bgp: pointer to bandgap instance + * @id: sensor id + * @val: desired thot value + * + * Return: 0 on success or the proper error code + */ +int ti_bandgap_write_thot(struct ti_bandgap *bgp, int id, int val) +{ + return _ti_bandgap_write_threshold(bgp, id, val, true); +} + +/** + * ti_bandgap_read_tcold() - reads sensor current tcold + * @bgp: pointer to bandgap instance + * @id: sensor id + * @tcold: resulting current tcold value + * + * Return: 0 on success or the proper error code + */ +int ti_bandgap_read_tcold(struct ti_bandgap *bgp, int id, int *tcold) +{ + return _ti_bandgap_read_threshold(bgp, id, tcold, false); +} + +/** + * ti_bandgap_write_tcold() - sets the sensor tcold + * @bgp: pointer to bandgap instance + * @id: sensor id + * @val: desired tcold value + * + * Return: 0 on success or the proper error code + */ +int ti_bandgap_write_tcold(struct ti_bandgap *bgp, int id, int val) +{ + return _ti_bandgap_write_threshold(bgp, id, val, false); +} + +/** + * ti_bandgap_read_counter() - read the sensor counter + * @bgp: pointer to bandgap instance + * @id: sensor id + * @interval: resulting update interval in miliseconds + */ +static void ti_bandgap_read_counter(struct ti_bandgap *bgp, int id, + int *interval) +{ + struct temp_sensor_registers *tsr; + int time; + + tsr = bgp->conf->sensors[id].registers; + time = ti_bandgap_readl(bgp, tsr->bgap_counter); + time = (time & tsr->counter_mask) >> + __ffs(tsr->counter_mask); + time = time * 1000 / bgp->clk_rate; + *interval = time; +} + +/** + * ti_bandgap_read_counter_delay() - read the sensor counter delay + * @bgp: pointer to bandgap instance + * @id: sensor id + * @interval: resulting update interval in miliseconds + */ +static void ti_bandgap_read_counter_delay(struct ti_bandgap *bgp, int id, + int *interval) +{ + struct temp_sensor_registers *tsr; + int reg_val; + + tsr = bgp->conf->sensors[id].registers; + + reg_val = ti_bandgap_readl(bgp, tsr->bgap_mask_ctrl); + reg_val = (reg_val & tsr->mask_counter_delay_mask) >> + __ffs(tsr->mask_counter_delay_mask); + switch (reg_val) { + case 0: + *interval = 0; + break; + case 1: + *interval = 1; + break; + case 2: + *interval = 10; + break; + case 3: + *interval = 100; + break; + case 4: + *interval = 250; + break; + case 5: + *interval = 500; + break; + default: + dev_warn(bgp->dev, "Wrong counter delay value read from register %X", + reg_val); + } +} + +/** + * ti_bandgap_read_update_interval() - read the sensor update interval + * @bgp: pointer to bandgap instance + * @id: sensor id + * @interval: resulting update interval in miliseconds + * + * Return: 0 on success or the proper error code + */ +int ti_bandgap_read_update_interval(struct ti_bandgap *bgp, int id, + int *interval) +{ + int ret = 0; + + ret = ti_bandgap_validate(bgp, id); + if (ret) + goto exit; + + if (!TI_BANDGAP_HAS(bgp, COUNTER) && + !TI_BANDGAP_HAS(bgp, COUNTER_DELAY)) { + ret = -ENOTSUPP; + goto exit; + } + + if (TI_BANDGAP_HAS(bgp, COUNTER)) { + ti_bandgap_read_counter(bgp, id, interval); + goto exit; + } + + ti_bandgap_read_counter_delay(bgp, id, interval); +exit: + return ret; +} + +/** + * ti_bandgap_write_counter_delay() - set the counter_delay + * @bgp: pointer to bandgap instance + * @id: sensor id + * @interval: desired update interval in miliseconds + * + * Return: 0 on success or the proper error code + */ +static int ti_bandgap_write_counter_delay(struct ti_bandgap *bgp, int id, + u32 interval) +{ + int rval; + + switch (interval) { + case 0: /* Immediate conversion */ + rval = 0x0; + break; + case 1: /* Conversion after ever 1ms */ + rval = 0x1; + break; + case 10: /* Conversion after ever 10ms */ + rval = 0x2; + break; + case 100: /* Conversion after ever 100ms */ + rval = 0x3; + break; + case 250: /* Conversion after ever 250ms */ + rval = 0x4; + break; + case 500: /* Conversion after ever 500ms */ + rval = 0x5; + break; + default: + dev_warn(bgp->dev, "Delay %d ms is not supported\n", interval); + return -EINVAL; + } + + spin_lock(&bgp->lock); + RMW_BITS(bgp, id, bgap_mask_ctrl, mask_counter_delay_mask, rval); + spin_unlock(&bgp->lock); + + return 0; +} + +/** + * ti_bandgap_write_counter() - set the bandgap sensor counter + * @bgp: pointer to bandgap instance + * @id: sensor id + * @interval: desired update interval in miliseconds + */ +static void ti_bandgap_write_counter(struct ti_bandgap *bgp, int id, + u32 interval) +{ + interval = interval * bgp->clk_rate / 1000; + spin_lock(&bgp->lock); + RMW_BITS(bgp, id, bgap_counter, counter_mask, interval); + spin_unlock(&bgp->lock); +} + +/** + * ti_bandgap_write_update_interval() - set the update interval + * @bgp: pointer to bandgap instance + * @id: sensor id + * @interval: desired update interval in miliseconds + * + * Return: 0 on success or the proper error code + */ +int ti_bandgap_write_update_interval(struct ti_bandgap *bgp, + int id, u32 interval) +{ + int ret = ti_bandgap_validate(bgp, id); + if (ret) + goto exit; + + if (!TI_BANDGAP_HAS(bgp, COUNTER) && + !TI_BANDGAP_HAS(bgp, COUNTER_DELAY)) { + ret = -ENOTSUPP; + goto exit; + } + + if (TI_BANDGAP_HAS(bgp, COUNTER)) { + ti_bandgap_write_counter(bgp, id, interval); + goto exit; + } + + ret = ti_bandgap_write_counter_delay(bgp, id, interval); +exit: + return ret; +} + +/** + * ti_bandgap_read_temperature() - report current temperature + * @bgp: pointer to bandgap instance + * @id: sensor id + * @temperature: resulting temperature + * + * Return: 0 on success or the proper error code + */ +int ti_bandgap_read_temperature(struct ti_bandgap *bgp, int id, + int *temperature) +{ + u32 temp; + int ret; + + ret = ti_bandgap_validate(bgp, id); + if (ret) + return ret; + + spin_lock(&bgp->lock); + temp = ti_bandgap_read_temp(bgp, id); + spin_unlock(&bgp->lock); + + ret |= ti_bandgap_adc_to_mcelsius(bgp, temp, &temp); + if (ret) + return -EIO; + + *temperature = temp; + + return 0; +} + +/** + * ti_bandgap_set_sensor_data() - helper function to store thermal + * framework related data. + * @bgp: pointer to bandgap instance + * @id: sensor id + * @data: thermal framework related data to be stored + * + * Return: 0 on success or the proper error code + */ +int ti_bandgap_set_sensor_data(struct ti_bandgap *bgp, int id, void *data) +{ + int ret = ti_bandgap_validate(bgp, id); + if (ret) + return ret; + + bgp->regval[id].data = data; + + return 0; +} + +/** + * ti_bandgap_get_sensor_data() - helper function to get thermal + * framework related data. + * @bgp: pointer to bandgap instance + * @id: sensor id + * + * Return: data stored by set function with sensor id on success or NULL + */ +void *ti_bandgap_get_sensor_data(struct ti_bandgap *bgp, int id) +{ + int ret = ti_bandgap_validate(bgp, id); + if (ret) + return ERR_PTR(ret); + + return bgp->regval[id].data; +} + +/*** Helper functions used during device initialization ***/ + +/** + * ti_bandgap_force_single_read() - executes 1 single ADC conversion + * @bgp: pointer to struct ti_bandgap + * @id: sensor id which it is desired to read 1 temperature + * + * Used to initialize the conversion state machine and set it to a valid + * state. Called during device initialization and context restore events. + * + * Return: 0 + */ +static int +ti_bandgap_force_single_read(struct ti_bandgap *bgp, int id) +{ + u32 temp = 0, counter = 1000; + + /* Select single conversion mode */ + if (TI_BANDGAP_HAS(bgp, MODE_CONFIG)) + RMW_BITS(bgp, id, bgap_mode_ctrl, mode_ctrl_mask, 0); + + /* Start of Conversion = 1 */ + RMW_BITS(bgp, id, temp_sensor_ctrl, bgap_soc_mask, 1); + /* Wait until DTEMP is updated */ + temp = ti_bandgap_read_temp(bgp, id); + + while ((temp == 0) && --counter) + temp = ti_bandgap_read_temp(bgp, id); + /* REVISIT: Check correct condition for end of conversion */ + + /* Start of Conversion = 0 */ + RMW_BITS(bgp, id, temp_sensor_ctrl, bgap_soc_mask, 0); + + return 0; +} + +/** + * ti_bandgap_set_continous_mode() - One time enabling of continuous mode + * @bgp: pointer to struct ti_bandgap + * + * Call this function only if HAS(MODE_CONFIG) is set. As this driver may + * be used for junction temperature monitoring, it is desirable that the + * sensors are operational all the time, so that alerts are generated + * properly. + * + * Return: 0 + */ +static int ti_bandgap_set_continuous_mode(struct ti_bandgap *bgp) +{ + int i; + + for (i = 0; i < bgp->conf->sensor_count; i++) { + /* Perform a single read just before enabling continuous */ + ti_bandgap_force_single_read(bgp, i); + RMW_BITS(bgp, i, bgap_mode_ctrl, mode_ctrl_mask, 1); + } + + return 0; +} + +/** + * ti_bandgap_get_trend() - To fetch the temperature trend of a sensor + * @bgp: pointer to struct ti_bandgap + * @id: id of the individual sensor + * @trend: Pointer to trend. + * + * This function needs to be called to fetch the temperature trend of a + * Particular sensor. The function computes the difference in temperature + * w.r.t time. For the bandgaps with built in history buffer the temperatures + * are read from the buffer and for those without the Buffer -ENOTSUPP is + * returned. + * + * Return: 0 if no error, else return corresponding error. If no + * error then the trend value is passed on to trend parameter + */ +int ti_bandgap_get_trend(struct ti_bandgap *bgp, int id, int *trend) +{ + struct temp_sensor_registers *tsr; + u32 temp1, temp2, reg1, reg2; + int t1, t2, interval, ret = 0; + + ret = ti_bandgap_validate(bgp, id); + if (ret) + goto exit; + + if (!TI_BANDGAP_HAS(bgp, HISTORY_BUFFER) || + !TI_BANDGAP_HAS(bgp, FREEZE_BIT)) { + ret = -ENOTSUPP; + goto exit; + } + + tsr = bgp->conf->sensors[id].registers; + + /* Freeze and read the last 2 valid readings */ + reg1 = tsr->ctrl_dtemp_1; + reg2 = tsr->ctrl_dtemp_2; + + /* read temperature from history buffer */ + temp1 = ti_bandgap_readl(bgp, reg1); + temp1 &= tsr->bgap_dtemp_mask; + + temp2 = ti_bandgap_readl(bgp, reg2); + temp2 &= tsr->bgap_dtemp_mask; + + /* Convert from adc values to mCelsius temperature */ + ret = ti_bandgap_adc_to_mcelsius(bgp, temp1, &t1); + if (ret) + goto exit; + + ret = ti_bandgap_adc_to_mcelsius(bgp, temp2, &t2); + if (ret) + goto exit; + + /* Fetch the update interval */ + ret = ti_bandgap_read_update_interval(bgp, id, &interval); + if (ret || !interval) + goto exit; + + *trend = (t1 - t2) / interval; + + dev_dbg(bgp->dev, "The temperatures are t1 = %d and t2 = %d and trend =%d\n", + t1, t2, *trend); + +exit: + return ret; +} + +/** + * ti_bandgap_tshut_init() - setup and initialize tshut handling + * @bgp: pointer to struct ti_bandgap + * @pdev: pointer to device struct platform_device + * + * Call this function only in case the bandgap features HAS(TSHUT). + * In this case, the driver needs to handle the TSHUT signal as an IRQ. + * The IRQ is wired as a GPIO, and for this purpose, it is required + * to specify which GPIO line is used. TSHUT IRQ is fired anytime + * one of the bandgap sensors violates the TSHUT high/hot threshold. + * And in that case, the system must go off. + * + * Return: 0 if no error, else error status + */ +static int ti_bandgap_tshut_init(struct ti_bandgap *bgp, + struct platform_device *pdev) +{ + int gpio_nr = bgp->tshut_gpio; + int status; + + /* Request for gpio_86 line */ + status = gpio_request(gpio_nr, "tshut"); + if (status < 0) { + dev_err(bgp->dev, "Could not request for TSHUT GPIO:%i\n", 86); + return status; + } + status = gpio_direction_input(gpio_nr); + if (status) { + dev_err(bgp->dev, "Cannot set input TSHUT GPIO %d\n", gpio_nr); + return status; + } + + status = request_irq(gpio_to_irq(gpio_nr), ti_bandgap_tshut_irq_handler, + IRQF_TRIGGER_RISING, "tshut", NULL); + if (status) { + gpio_free(gpio_nr); + dev_err(bgp->dev, "request irq failed for TSHUT"); + } + + return 0; +} + +/** + * ti_bandgap_alert_init() - setup and initialize talert handling + * @bgp: pointer to struct ti_bandgap + * @pdev: pointer to device struct platform_device + * + * Call this function only in case the bandgap features HAS(TALERT). + * In this case, the driver needs to handle the TALERT signals as an IRQs. + * TALERT is a normal IRQ and it is fired any time thresholds (hot or cold) + * are violated. In these situation, the driver must reprogram the thresholds, + * accordingly to specified policy. + * + * Return: 0 if no error, else return corresponding error. + */ +static int ti_bandgap_talert_init(struct ti_bandgap *bgp, + struct platform_device *pdev) +{ + int ret; + + bgp->irq = platform_get_irq(pdev, 0); + if (bgp->irq < 0) { + dev_err(&pdev->dev, "get_irq failed\n"); + return bgp->irq; + } + ret = request_threaded_irq(bgp->irq, NULL, + ti_bandgap_talert_irq_handler, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "talert", bgp); + if (ret) { + dev_err(&pdev->dev, "Request threaded irq failed.\n"); + return ret; + } + + return 0; +} + +static const struct of_device_id of_ti_bandgap_match[]; +/** + * ti_bandgap_build() - parse DT and setup a struct ti_bandgap + * @pdev: pointer to device struct platform_device + * + * Used to read the device tree properties accordingly to the bandgap + * matching version. Based on bandgap version and its capabilities it + * will build a struct ti_bandgap out of the required DT entries. + * + * Return: valid bandgap structure if successful, else returns ERR_PTR + * return value must be verified with IS_ERR. + */ +static struct ti_bandgap *ti_bandgap_build(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + const struct of_device_id *of_id; + struct ti_bandgap *bgp; + struct resource *res; + u32 prop; + int i; + + /* just for the sake */ + if (!node) { + dev_err(&pdev->dev, "no platform information available\n"); + return ERR_PTR(-EINVAL); + } + + bgp = devm_kzalloc(&pdev->dev, sizeof(*bgp), GFP_KERNEL); + if (!bgp) { + dev_err(&pdev->dev, "Unable to allocate mem for driver ref\n"); + return ERR_PTR(-ENOMEM); + } + + of_id = of_match_device(of_ti_bandgap_match, &pdev->dev); + if (of_id) + bgp->conf = of_id->data; + + /* register shadow for context save and restore */ + bgp->regval = devm_kzalloc(&pdev->dev, sizeof(*bgp->regval) * + bgp->conf->sensor_count, GFP_KERNEL); + if (!bgp) { + dev_err(&pdev->dev, "Unable to allocate mem for driver ref\n"); + return ERR_PTR(-ENOMEM); + } + + i = 0; + do { + void __iomem *chunk; + + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (!res) + break; + chunk = devm_ioremap_resource(&pdev->dev, res); + if (i == 0) + bgp->base = chunk; + if (IS_ERR(chunk)) + return ERR_CAST(chunk); + + i++; + } while (res); + + if (TI_BANDGAP_HAS(bgp, TSHUT)) { + if (of_property_read_u32(node, "ti,tshut-gpio", &prop) < 0) { + dev_err(&pdev->dev, "missing tshut gpio in device tree\n"); + return ERR_PTR(-EINVAL); + } + bgp->tshut_gpio = prop; + if (!gpio_is_valid(bgp->tshut_gpio)) { + dev_err(&pdev->dev, "invalid gpio for tshut (%d)\n", + bgp->tshut_gpio); + return ERR_PTR(-EINVAL); + } + } + + return bgp; +} + +/*** Device driver call backs ***/ + +static +int ti_bandgap_probe(struct platform_device *pdev) +{ + struct ti_bandgap *bgp; + int clk_rate, ret = 0, i; + + bgp = ti_bandgap_build(pdev); + if (IS_ERR_OR_NULL(bgp)) { + dev_err(&pdev->dev, "failed to fetch platform data\n"); + return PTR_ERR(bgp); + } + bgp->dev = &pdev->dev; + + if (TI_BANDGAP_HAS(bgp, TSHUT)) { + ret = ti_bandgap_tshut_init(bgp, pdev); + if (ret) { + dev_err(&pdev->dev, + "failed to initialize system tshut IRQ\n"); + return ret; + } + } + + bgp->fclock = clk_get(NULL, bgp->conf->fclock_name); + ret = IS_ERR_OR_NULL(bgp->fclock); + if (ret) { + dev_err(&pdev->dev, "failed to request fclock reference\n"); + goto free_irqs; + } + + bgp->div_clk = clk_get(NULL, bgp->conf->div_ck_name); + ret = IS_ERR_OR_NULL(bgp->div_clk); + if (ret) { + dev_err(&pdev->dev, + "failed to request div_ts_ck clock ref\n"); + goto free_irqs; + } + + for (i = 0; i < bgp->conf->sensor_count; i++) { + struct temp_sensor_registers *tsr; + u32 val; + + tsr = bgp->conf->sensors[i].registers; + /* + * check if the efuse has a non-zero value if not + * it is an untrimmed sample and the temperatures + * may not be accurate + */ + val = ti_bandgap_readl(bgp, tsr->bgap_efuse); + if (ret || !val) + dev_info(&pdev->dev, + "Non-trimmed BGAP, Temp not accurate\n"); + } + + clk_rate = clk_round_rate(bgp->div_clk, + bgp->conf->sensors[0].ts_data->max_freq); + if (clk_rate < bgp->conf->sensors[0].ts_data->min_freq || + clk_rate == 0xffffffff) { + ret = -ENODEV; + dev_err(&pdev->dev, "wrong clock rate (%d)\n", clk_rate); + goto put_clks; + } + + ret = clk_set_rate(bgp->div_clk, clk_rate); + if (ret) + dev_err(&pdev->dev, "Cannot re-set clock rate. Continuing\n"); + + bgp->clk_rate = clk_rate; + if (TI_BANDGAP_HAS(bgp, CLK_CTRL)) + clk_prepare_enable(bgp->fclock); + + + spin_lock_init(&bgp->lock); + bgp->dev = &pdev->dev; + platform_set_drvdata(pdev, bgp); + + ti_bandgap_power(bgp, true); + + /* Set default counter to 1 for now */ + if (TI_BANDGAP_HAS(bgp, COUNTER)) + for (i = 0; i < bgp->conf->sensor_count; i++) + RMW_BITS(bgp, i, bgap_counter, counter_mask, 1); + + /* Set default thresholds for alert and shutdown */ + for (i = 0; i < bgp->conf->sensor_count; i++) { + struct temp_sensor_data *ts_data; + + ts_data = bgp->conf->sensors[i].ts_data; + + if (TI_BANDGAP_HAS(bgp, TALERT)) { + /* Set initial Talert thresholds */ + RMW_BITS(bgp, i, bgap_threshold, + threshold_tcold_mask, ts_data->t_cold); + RMW_BITS(bgp, i, bgap_threshold, + threshold_thot_mask, ts_data->t_hot); + /* Enable the alert events */ + RMW_BITS(bgp, i, bgap_mask_ctrl, mask_hot_mask, 1); + RMW_BITS(bgp, i, bgap_mask_ctrl, mask_cold_mask, 1); + } + + if (TI_BANDGAP_HAS(bgp, TSHUT_CONFIG)) { + /* Set initial Tshut thresholds */ + RMW_BITS(bgp, i, tshut_threshold, + tshut_hot_mask, ts_data->tshut_hot); + RMW_BITS(bgp, i, tshut_threshold, + tshut_cold_mask, ts_data->tshut_cold); + } + } + + if (TI_BANDGAP_HAS(bgp, MODE_CONFIG)) + ti_bandgap_set_continuous_mode(bgp); + + /* Set .250 seconds time as default counter */ + if (TI_BANDGAP_HAS(bgp, COUNTER)) + for (i = 0; i < bgp->conf->sensor_count; i++) + RMW_BITS(bgp, i, bgap_counter, counter_mask, + bgp->clk_rate / 4); + + /* Every thing is good? Then expose the sensors */ + for (i = 0; i < bgp->conf->sensor_count; i++) { + char *domain; + + if (bgp->conf->sensors[i].register_cooling) { + ret = bgp->conf->sensors[i].register_cooling(bgp, i); + if (ret) + goto remove_sensors; + } + + if (bgp->conf->expose_sensor) { + domain = bgp->conf->sensors[i].domain; + ret = bgp->conf->expose_sensor(bgp, i, domain); + if (ret) + goto remove_last_cooling; + } + } + + /* + * Enable the Interrupts once everything is set. Otherwise irq handler + * might be called as soon as it is enabled where as rest of framework + * is still getting initialised. + */ + if (TI_BANDGAP_HAS(bgp, TALERT)) { + ret = ti_bandgap_talert_init(bgp, pdev); + if (ret) { + dev_err(&pdev->dev, "failed to initialize Talert IRQ\n"); + i = bgp->conf->sensor_count; + goto disable_clk; + } + } + + return 0; + +remove_last_cooling: + if (bgp->conf->sensors[i].unregister_cooling) + bgp->conf->sensors[i].unregister_cooling(bgp, i); +remove_sensors: + for (i--; i >= 0; i--) { + if (bgp->conf->sensors[i].unregister_cooling) + bgp->conf->sensors[i].unregister_cooling(bgp, i); + if (bgp->conf->remove_sensor) + bgp->conf->remove_sensor(bgp, i); + } + ti_bandgap_power(bgp, false); +disable_clk: + if (TI_BANDGAP_HAS(bgp, CLK_CTRL)) + clk_disable_unprepare(bgp->fclock); +put_clks: + clk_put(bgp->fclock); + clk_put(bgp->div_clk); +free_irqs: + if (TI_BANDGAP_HAS(bgp, TSHUT)) { + free_irq(gpio_to_irq(bgp->tshut_gpio), NULL); + gpio_free(bgp->tshut_gpio); + } + + return ret; +} + +static +int ti_bandgap_remove(struct platform_device *pdev) +{ + struct ti_bandgap *bgp = platform_get_drvdata(pdev); + int i; + + /* First thing is to remove sensor interfaces */ + for (i = 0; i < bgp->conf->sensor_count; i++) { + if (bgp->conf->sensors[i].unregister_cooling) + bgp->conf->sensors[i].unregister_cooling(bgp, i); + + if (bgp->conf->remove_sensor) + bgp->conf->remove_sensor(bgp, i); + } + + ti_bandgap_power(bgp, false); + + if (TI_BANDGAP_HAS(bgp, CLK_CTRL)) + clk_disable_unprepare(bgp->fclock); + clk_put(bgp->fclock); + clk_put(bgp->div_clk); + + if (TI_BANDGAP_HAS(bgp, TALERT)) + free_irq(bgp->irq, bgp); + + if (TI_BANDGAP_HAS(bgp, TSHUT)) { + free_irq(gpio_to_irq(bgp->tshut_gpio), NULL); + gpio_free(bgp->tshut_gpio); + } + + return 0; +} + +#ifdef CONFIG_PM +static int ti_bandgap_save_ctxt(struct ti_bandgap *bgp) +{ + int i; + + for (i = 0; i < bgp->conf->sensor_count; i++) { + struct temp_sensor_registers *tsr; + struct temp_sensor_regval *rval; + + rval = &bgp->regval[i]; + tsr = bgp->conf->sensors[i].registers; + + if (TI_BANDGAP_HAS(bgp, MODE_CONFIG)) + rval->bg_mode_ctrl = ti_bandgap_readl(bgp, + tsr->bgap_mode_ctrl); + if (TI_BANDGAP_HAS(bgp, COUNTER)) + rval->bg_counter = ti_bandgap_readl(bgp, + tsr->bgap_counter); + if (TI_BANDGAP_HAS(bgp, TALERT)) { + rval->bg_threshold = ti_bandgap_readl(bgp, + tsr->bgap_threshold); + rval->bg_ctrl = ti_bandgap_readl(bgp, + tsr->bgap_mask_ctrl); + } + + if (TI_BANDGAP_HAS(bgp, TSHUT_CONFIG)) + rval->tshut_threshold = ti_bandgap_readl(bgp, + tsr->tshut_threshold); + } + + return 0; +} + +static int ti_bandgap_restore_ctxt(struct ti_bandgap *bgp) +{ + int i; + + for (i = 0; i < bgp->conf->sensor_count; i++) { + struct temp_sensor_registers *tsr; + struct temp_sensor_regval *rval; + u32 val = 0; + + rval = &bgp->regval[i]; + tsr = bgp->conf->sensors[i].registers; + + if (TI_BANDGAP_HAS(bgp, COUNTER)) + val = ti_bandgap_readl(bgp, tsr->bgap_counter); + + if (TI_BANDGAP_HAS(bgp, TSHUT_CONFIG)) + ti_bandgap_writel(bgp, rval->tshut_threshold, + tsr->tshut_threshold); + /* Force immediate temperature measurement and update + * of the DTEMP field + */ + ti_bandgap_force_single_read(bgp, i); + + if (TI_BANDGAP_HAS(bgp, COUNTER)) + ti_bandgap_writel(bgp, rval->bg_counter, + tsr->bgap_counter); + if (TI_BANDGAP_HAS(bgp, MODE_CONFIG)) + ti_bandgap_writel(bgp, rval->bg_mode_ctrl, + tsr->bgap_mode_ctrl); + if (TI_BANDGAP_HAS(bgp, TALERT)) { + ti_bandgap_writel(bgp, rval->bg_threshold, + tsr->bgap_threshold); + ti_bandgap_writel(bgp, rval->bg_ctrl, + tsr->bgap_mask_ctrl); + } + } + + return 0; +} + +static int ti_bandgap_suspend(struct device *dev) +{ + struct ti_bandgap *bgp = dev_get_drvdata(dev); + int err; + + err = ti_bandgap_save_ctxt(bgp); + ti_bandgap_power(bgp, false); + + if (TI_BANDGAP_HAS(bgp, CLK_CTRL)) + clk_disable_unprepare(bgp->fclock); + + return err; +} + +static int ti_bandgap_resume(struct device *dev) +{ + struct ti_bandgap *bgp = dev_get_drvdata(dev); + + if (TI_BANDGAP_HAS(bgp, CLK_CTRL)) + clk_prepare_enable(bgp->fclock); + + ti_bandgap_power(bgp, true); + + return ti_bandgap_restore_ctxt(bgp); +} +static const struct dev_pm_ops ti_bandgap_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(ti_bandgap_suspend, + ti_bandgap_resume) +}; + +#define DEV_PM_OPS (&ti_bandgap_dev_pm_ops) +#else +#define DEV_PM_OPS NULL +#endif + +static const struct of_device_id of_ti_bandgap_match[] = { +#ifdef CONFIG_OMAP4_THERMAL + { + .compatible = "ti,omap4430-bandgap", + .data = (void *)&omap4430_data, + }, + { + .compatible = "ti,omap4460-bandgap", + .data = (void *)&omap4460_data, + }, + { + .compatible = "ti,omap4470-bandgap", + .data = (void *)&omap4470_data, + }, +#endif +#ifdef CONFIG_OMAP5_THERMAL + { + .compatible = "ti,omap5430-bandgap", + .data = (void *)&omap5430_data, + }, +#endif + /* Sentinel */ + { }, +}; +MODULE_DEVICE_TABLE(of, of_ti_bandgap_match); + +static struct platform_driver ti_bandgap_sensor_driver = { + .probe = ti_bandgap_probe, + .remove = ti_bandgap_remove, + .driver = { + .name = "ti-soc-thermal", + .pm = DEV_PM_OPS, + .of_match_table = of_ti_bandgap_match, + }, +}; + +module_platform_driver(ti_bandgap_sensor_driver); + +MODULE_DESCRIPTION("OMAP4+ bandgap temperature sensor driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:ti-soc-thermal"); +MODULE_AUTHOR("Texas Instrument Inc."); diff --git a/drivers/thermal/ti-soc-thermal/ti-bandgap.h b/drivers/thermal/ti-soc-thermal/ti-bandgap.h new file mode 100644 index 000000000000..5f4794abf583 --- /dev/null +++ b/drivers/thermal/ti-soc-thermal/ti-bandgap.h @@ -0,0 +1,403 @@ +/* + * OMAP4 Bandgap temperature sensor driver + * + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ + * Contact: + * Eduardo Valentin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ +#ifndef __TI_BANDGAP_H +#define __TI_BANDGAP_H + +#include +#include +#include + +/** + * DOC: bandgap driver data structure + * ================================== + * + * +----------+----------------+ + * | struct temp_sensor_regval | + * +---------------------------+ + * * (Array of) + * | + * | + * +-------------------+ +-----------------+ + * | struct ti_bandgap |-->| struct device * | + * +----------+--------+ +-----------------+ + * | + * | + * V + * +------------------------+ + * | struct ti_bandgap_data | + * +------------------------+ + * | + * | + * * (Array of) + * +------------+------------------------------------------------------+ + * | +----------+------------+ +-------------------------+ | + * | | struct ti_temp_sensor |-->| struct temp_sensor_data | | + * | +-----------------------+ +------------+------------+ | + * | | | + * | + | + * | V | + * | +----------+-------------------+ | + * | | struct temp_sensor_registers | | + * | +------------------------------+ | + * | | + * +-------------------------------------------------------------------+ + * + * Above is a simple diagram describing how the data structure below + * are organized. For each bandgap device there should be a ti_bandgap_data + * containing the device instance configuration, as well as, an array of + * sensors, representing every sensor instance present in this bandgap. + */ + +/** + * struct temp_sensor_registers - descriptor to access registers and bitfields + * @temp_sensor_ctrl: TEMP_SENSOR_CTRL register offset + * @bgap_tempsoff_mask: mask to temp_sensor_ctrl.tempsoff + * @bgap_soc_mask: mask to temp_sensor_ctrl.soc + * @bgap_eocz_mask: mask to temp_sensor_ctrl.eocz + * @bgap_dtemp_mask: mask to temp_sensor_ctrl.dtemp + * @bgap_mask_ctrl: BANDGAP_MASK_CTRL register offset + * @mask_hot_mask: mask to bandgap_mask_ctrl.mask_hot + * @mask_cold_mask: mask to bandgap_mask_ctrl.mask_cold + * @mask_sidlemode_mask: mask to bandgap_mask_ctrl.mask_sidlemode + * @mask_counter_delay_mask: mask to bandgap_mask_ctrl.mask_counter_delay + * @mask_freeze_mask: mask to bandgap_mask_ctrl.mask_free + * @mask_clear_mask: mask to bandgap_mask_ctrl.mask_clear + * @mask_clear_accum_mask: mask to bandgap_mask_ctrl.mask_clear_accum + * @bgap_mode_ctrl: BANDGAP_MODE_CTRL register offset + * @mode_ctrl_mask: mask to bandgap_mode_ctrl.mode_ctrl + * @bgap_counter: BANDGAP_COUNTER register offset + * @counter_mask: mask to bandgap_counter.counter + * @bgap_threshold: BANDGAP_THRESHOLD register offset (TALERT thresholds) + * @threshold_thot_mask: mask to bandgap_threhold.thot + * @threshold_tcold_mask: mask to bandgap_threhold.tcold + * @tshut_threshold: TSHUT_THRESHOLD register offset (TSHUT thresholds) + * @tshut_efuse_mask: mask to tshut_threshold.tshut_efuse + * @tshut_efuse_shift: shift to tshut_threshold.tshut_efuse + * @tshut_hot_mask: mask to tshut_threhold.thot + * @tshut_cold_mask: mask to tshut_threhold.thot + * @bgap_status: BANDGAP_STATUS register offset + * @status_clean_stop_mask: mask to bandgap_status.clean_stop + * @status_bgap_alert_mask: mask to bandgap_status.bandgap_alert + * @status_hot_mask: mask to bandgap_status.hot + * @status_cold_mask: mask to bandgap_status.cold + * @bgap_cumul_dtemp: BANDGAP_CUMUL_DTEMP register offset + * @ctrl_dtemp_0: CTRL_DTEMP0 register offset + * @ctrl_dtemp_1: CTRL_DTEMP1 register offset + * @ctrl_dtemp_2: CTRL_DTEMP2 register offset + * @ctrl_dtemp_3: CTRL_DTEMP3 register offset + * @ctrl_dtemp_4: CTRL_DTEMP4 register offset + * @bgap_efuse: BANDGAP_EFUSE register offset + * + * The register offsets and bitfields might change across + * OMAP and variants versions. Hence this struct serves as a + * descriptor map on how to access the registers and the bitfields. + * + * This descriptor contains registers of all versions of bandgap chips. + * Not all versions will use all registers, depending on the available + * features. Please read TRMs for descriptive explanation on each bitfield. + */ + +struct temp_sensor_registers { + u32 temp_sensor_ctrl; + u32 bgap_tempsoff_mask; + u32 bgap_soc_mask; + u32 bgap_eocz_mask; /* not used: but needs revisit */ + u32 bgap_dtemp_mask; + + u32 bgap_mask_ctrl; + u32 mask_hot_mask; + u32 mask_cold_mask; + u32 mask_sidlemode_mask; /* not used: but may be needed for pm */ + u32 mask_counter_delay_mask; + u32 mask_freeze_mask; + u32 mask_clear_mask; /* not used: but needed for trending */ + u32 mask_clear_accum_mask; /* not used: but needed for trending */ + + u32 bgap_mode_ctrl; + u32 mode_ctrl_mask; + + u32 bgap_counter; + u32 counter_mask; + + u32 bgap_threshold; + u32 threshold_thot_mask; + u32 threshold_tcold_mask; + + u32 tshut_threshold; + u32 tshut_efuse_mask; /* not used */ + u32 tshut_efuse_shift; /* not used */ + u32 tshut_hot_mask; + u32 tshut_cold_mask; + + u32 bgap_status; + u32 status_clean_stop_mask; /* not used: but needed for trending */ + u32 status_bgap_alert_mask; /* not used */ + u32 status_hot_mask; + u32 status_cold_mask; + + u32 bgap_cumul_dtemp; /* not used: but needed for trending */ + u32 ctrl_dtemp_0; /* not used: but needed for trending */ + u32 ctrl_dtemp_1; /* not used: but needed for trending */ + u32 ctrl_dtemp_2; /* not used: but needed for trending */ + u32 ctrl_dtemp_3; /* not used: but needed for trending */ + u32 ctrl_dtemp_4; /* not used: but needed for trending */ + u32 bgap_efuse; +}; + +/** + * struct temp_sensor_data - The thresholds and limits for temperature sensors. + * @tshut_hot: temperature to trigger a thermal reset (initial value) + * @tshut_cold: temp to get the plat out of reset due to thermal (init val) + * @t_hot: temperature to trigger a thermal alert (high initial value) + * @t_cold: temperature to trigger a thermal alert (low initial value) + * @min_freq: sensor minimum clock rate + * @max_freq: sensor maximum clock rate + * @max_temp: sensor maximum temperature + * @min_temp: sensor minimum temperature + * @hyst_val: temperature hysteresis considered while converting ADC values + * @update_int1: update interval + * @update_int2: update interval + * + * This data structure will hold the required thresholds and temperature limits + * for a specific temperature sensor, like shutdown temperature, alert + * temperature, clock / rate used, ADC conversion limits and update intervals + */ +struct temp_sensor_data { + u32 tshut_hot; + u32 tshut_cold; + u32 t_hot; + u32 t_cold; + u32 min_freq; + u32 max_freq; + int max_temp; + int min_temp; + int hyst_val; + u32 update_int1; /* not used */ + u32 update_int2; /* not used */ +}; + +struct ti_bandgap_data; + +/** + * struct temp_sensor_regval - temperature sensor register values and priv data + * @bg_mode_ctrl: temp sensor control register value + * @bg_ctrl: bandgap ctrl register value + * @bg_counter: bandgap counter value + * @bg_threshold: bandgap threshold register value + * @tshut_threshold: bandgap tshut register value + * @data: private data + * + * Data structure to save and restore bandgap register set context. Only + * required registers are shadowed, when needed. + */ +struct temp_sensor_regval { + u32 bg_mode_ctrl; + u32 bg_ctrl; + u32 bg_counter; + u32 bg_threshold; + u32 tshut_threshold; + void *data; +}; + +/** + * struct ti_bandgap - bandgap device structure + * @dev: struct device pointer + * @base: io memory base address + * @conf: struct with bandgap configuration set (# sensors, conv_table, etc) + * @regval: temperature sensor register values + * @fclock: pointer to functional clock of temperature sensor + * @div_clk: pointer to divider clock of temperature sensor fclk + * @lock: spinlock for ti_bandgap structure + * @irq: MPU IRQ number for thermal alert + * @tshut_gpio: GPIO where Tshut signal is routed + * @clk_rate: Holds current clock rate + * + * The bandgap device structure representing the bandgap device instance. + * It holds most of the dynamic stuff. Configurations and sensor specific + * entries are inside the @conf structure. + */ +struct ti_bandgap { + struct device *dev; + void __iomem *base; + const struct ti_bandgap_data *conf; + struct temp_sensor_regval *regval; + struct clk *fclock; + struct clk *div_clk; + spinlock_t lock; /* shields this struct */ + int irq; + int tshut_gpio; + u32 clk_rate; +}; + +/** + * struct ti_temp_sensor - bandgap temperature sensor configuration data + * @ts_data: pointer to struct with thresholds, limits of temperature sensor + * @registers: pointer to the list of register offsets and bitfields + * @domain: the name of the domain where the sensor is located + * @slope: sensor gradient slope info for hotspot extrapolation equation + * @constant: sensor gradient const info for hotspot extrapolation equation + * @slope_pcb: sensor gradient slope info for hotspot extrapolation equation + * with no external influence + * @constant_pcb: sensor gradient const info for hotspot extrapolation equation + * with no external influence + * @register_cooling: function to describe how this sensor is going to be cooled + * @unregister_cooling: function to release cooling data + * + * Data structure to describe a temperature sensor handled by a bandgap device. + * It should provide configuration details on this sensor, such as how to + * access the registers affecting this sensor, shadow register buffer, how to + * assess the gradient from hotspot, how to cooldown the domain when sensor + * reports too hot temperature. + */ +struct ti_temp_sensor { + struct temp_sensor_data *ts_data; + struct temp_sensor_registers *registers; + char *domain; + /* for hotspot extrapolation */ + const int slope; + const int constant; + const int slope_pcb; + const int constant_pcb; + int (*register_cooling)(struct ti_bandgap *bgp, int id); + int (*unregister_cooling)(struct ti_bandgap *bgp, int id); +}; + +/** + * DOC: ti bandgap feature types + * + * TI_BANDGAP_FEATURE_TSHUT - used when the thermal shutdown signal output + * of a bandgap device instance is routed to the processor. This means + * the system must react and perform the shutdown by itself (handle an + * IRQ, for instance). + * + * TI_BANDGAP_FEATURE_TSHUT_CONFIG - used when the bandgap device has control + * over the thermal shutdown configuration. This means that the thermal + * shutdown thresholds are programmable, for instance. + * + * TI_BANDGAP_FEATURE_TALERT - used when the bandgap device instance outputs + * a signal representing violation of programmable alert thresholds. + * + * TI_BANDGAP_FEATURE_MODE_CONFIG - used when it is possible to choose which + * mode, continuous or one shot, the bandgap device instance will operate. + * + * TI_BANDGAP_FEATURE_COUNTER - used when the bandgap device instance allows + * programming the update interval of its internal state machine. + * + * TI_BANDGAP_FEATURE_POWER_SWITCH - used when the bandgap device allows + * itself to be switched on/off. + * + * TI_BANDGAP_FEATURE_CLK_CTRL - used when the clocks feeding the bandgap + * device are gateable or not. + * + * TI_BANDGAP_FEATURE_FREEZE_BIT - used when the bandgap device features + * a history buffer that its update can be freezed/unfreezed. + * + * TI_BANDGAP_FEATURE_COUNTER_DELAY - used when the bandgap device features + * a delay programming based on distinct values. + * + * TI_BANDGAP_FEATURE_HISTORY_BUFFER - used when the bandgap device features + * a history buffer of temperatures. + * + * TI_BANDGAP_HAS(b, f) - macro to check if a bandgap device is capable of a + * specific feature (above) or not. Return non-zero, if yes. + */ +#define TI_BANDGAP_FEATURE_TSHUT BIT(0) +#define TI_BANDGAP_FEATURE_TSHUT_CONFIG BIT(1) +#define TI_BANDGAP_FEATURE_TALERT BIT(2) +#define TI_BANDGAP_FEATURE_MODE_CONFIG BIT(3) +#define TI_BANDGAP_FEATURE_COUNTER BIT(4) +#define TI_BANDGAP_FEATURE_POWER_SWITCH BIT(5) +#define TI_BANDGAP_FEATURE_CLK_CTRL BIT(6) +#define TI_BANDGAP_FEATURE_FREEZE_BIT BIT(7) +#define TI_BANDGAP_FEATURE_COUNTER_DELAY BIT(8) +#define TI_BANDGAP_FEATURE_HISTORY_BUFFER BIT(9) +#define TI_BANDGAP_HAS(b, f) \ + ((b)->conf->features & TI_BANDGAP_FEATURE_ ## f) + +/** + * struct ti_bandgap_data - ti bandgap data configuration structure + * @features: a bitwise flag set to describe the device features + * @conv_table: Pointer to ADC to temperature conversion table + * @adc_start_val: ADC conversion table starting value + * @adc_end_val: ADC conversion table ending value + * @fclock_name: clock name of the functional clock + * @div_ck_name: clock name of the clock divisor + * @sensor_count: count of temperature sensor within this bandgap device + * @report_temperature: callback to report thermal alert to thermal API + * @expose_sensor: callback to export sensor to thermal API + * @remove_sensor: callback to destroy sensor from thermal API + * @sensors: array of sensors present in this bandgap instance + * + * This is a data structure which should hold most of the static configuration + * of a bandgap device instance. It should describe which features this instance + * is capable of, the clock names to feed this device, the amount of sensors and + * their configuration representation, and how to export and unexport them to + * a thermal API. + */ +struct ti_bandgap_data { + unsigned int features; + const int *conv_table; + u32 adc_start_val; + u32 adc_end_val; + char *fclock_name; + char *div_ck_name; + int sensor_count; + int (*report_temperature)(struct ti_bandgap *bgp, int id); + int (*expose_sensor)(struct ti_bandgap *bgp, int id, char *domain); + int (*remove_sensor)(struct ti_bandgap *bgp, int id); + + /* this needs to be at the end */ + struct ti_temp_sensor sensors[]; +}; + +int ti_bandgap_read_thot(struct ti_bandgap *bgp, int id, int *thot); +int ti_bandgap_write_thot(struct ti_bandgap *bgp, int id, int val); +int ti_bandgap_read_tcold(struct ti_bandgap *bgp, int id, int *tcold); +int ti_bandgap_write_tcold(struct ti_bandgap *bgp, int id, int val); +int ti_bandgap_read_update_interval(struct ti_bandgap *bgp, int id, + int *interval); +int ti_bandgap_write_update_interval(struct ti_bandgap *bgp, int id, + u32 interval); +int ti_bandgap_read_temperature(struct ti_bandgap *bgp, int id, + int *temperature); +int ti_bandgap_set_sensor_data(struct ti_bandgap *bgp, int id, void *data); +void *ti_bandgap_get_sensor_data(struct ti_bandgap *bgp, int id); +int ti_bandgap_get_trend(struct ti_bandgap *bgp, int id, int *trend); + +#ifdef CONFIG_OMAP4_THERMAL +extern const struct ti_bandgap_data omap4430_data; +extern const struct ti_bandgap_data omap4460_data; +extern const struct ti_bandgap_data omap4470_data; +#else +#define omap4430_data NULL +#define omap4460_data NULL +#define omap4470_data NULL +#endif + +#ifdef CONFIG_OMAP5_THERMAL +extern const struct ti_bandgap_data omap5430_data; +#else +#define omap5430_data NULL +#endif + +#endif diff --git a/drivers/thermal/ti-soc-thermal/ti-thermal-common.c b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c new file mode 100644 index 000000000000..e3c5e677eaa5 --- /dev/null +++ b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c @@ -0,0 +1,367 @@ +/* + * OMAP thermal driver interface + * + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * Contact: + * Eduardo Valentin + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ti-thermal.h" +#include "ti-bandgap.h" + +/* common data structures */ +struct ti_thermal_data { + struct thermal_zone_device *ti_thermal; + struct thermal_cooling_device *cool_dev; + struct ti_bandgap *bgp; + enum thermal_device_mode mode; + struct work_struct thermal_wq; + int sensor_id; +}; + +static void ti_thermal_work(struct work_struct *work) +{ + struct ti_thermal_data *data = container_of(work, + struct ti_thermal_data, thermal_wq); + + thermal_zone_device_update(data->ti_thermal); + + dev_dbg(&data->ti_thermal->device, "updated thermal zone %s\n", + data->ti_thermal->type); +} + +/** + * ti_thermal_hotspot_temperature - returns sensor extrapolated temperature + * @t: omap sensor temperature + * @s: omap sensor slope value + * @c: omap sensor const value + */ +static inline int ti_thermal_hotspot_temperature(int t, int s, int c) +{ + int delta = t * s / 1000 + c; + + if (delta < 0) + delta = 0; + + return t + delta; +} + +/* thermal zone ops */ +/* Get temperature callback function for thermal zone*/ +static inline int ti_thermal_get_temp(struct thermal_zone_device *thermal, + unsigned long *temp) +{ + struct ti_thermal_data *data = thermal->devdata; + struct ti_bandgap *bgp; + const struct ti_temp_sensor *s; + int ret, tmp, pcb_temp, slope, constant; + + if (!data) + return 0; + + bgp = data->bgp; + s = &bgp->conf->sensors[data->sensor_id]; + + ret = ti_bandgap_read_temperature(bgp, data->sensor_id, &tmp); + if (ret) + return ret; + + pcb_temp = 0; + /* TODO: Introduce pcb temperature lookup */ + /* In case pcb zone is available, use the extrapolation rule with it */ + if (pcb_temp) { + tmp -= pcb_temp; + slope = s->slope_pcb; + constant = s->constant_pcb; + } else { + slope = s->slope; + constant = s->constant; + } + *temp = ti_thermal_hotspot_temperature(tmp, slope, constant); + + return ret; +} + +/* Bind callback functions for thermal zone */ +static int ti_thermal_bind(struct thermal_zone_device *thermal, + struct thermal_cooling_device *cdev) +{ + struct ti_thermal_data *data = thermal->devdata; + int id; + + if (IS_ERR_OR_NULL(data)) + return -ENODEV; + + /* check if this is the cooling device we registered */ + if (data->cool_dev != cdev) + return 0; + + id = data->sensor_id; + + /* Simple thing, two trips, one passive another critical */ + return thermal_zone_bind_cooling_device(thermal, 0, cdev, + /* bind with min and max states defined by cpu_cooling */ + THERMAL_NO_LIMIT, + THERMAL_NO_LIMIT); +} + +/* Unbind callback functions for thermal zone */ +static int ti_thermal_unbind(struct thermal_zone_device *thermal, + struct thermal_cooling_device *cdev) +{ + struct ti_thermal_data *data = thermal->devdata; + + if (IS_ERR_OR_NULL(data)) + return -ENODEV; + + /* check if this is the cooling device we registered */ + if (data->cool_dev != cdev) + return 0; + + /* Simple thing, two trips, one passive another critical */ + return thermal_zone_unbind_cooling_device(thermal, 0, cdev); +} + +/* Get mode callback functions for thermal zone */ +static int ti_thermal_get_mode(struct thermal_zone_device *thermal, + enum thermal_device_mode *mode) +{ + struct ti_thermal_data *data = thermal->devdata; + + if (data) + *mode = data->mode; + + return 0; +} + +/* Set mode callback functions for thermal zone */ +static int ti_thermal_set_mode(struct thermal_zone_device *thermal, + enum thermal_device_mode mode) +{ + struct ti_thermal_data *data = thermal->devdata; + + if (!data->ti_thermal) { + dev_notice(&thermal->device, "thermal zone not registered\n"); + return 0; + } + + mutex_lock(&data->ti_thermal->lock); + + if (mode == THERMAL_DEVICE_ENABLED) + data->ti_thermal->polling_delay = FAST_TEMP_MONITORING_RATE; + else + data->ti_thermal->polling_delay = 0; + + mutex_unlock(&data->ti_thermal->lock); + + data->mode = mode; + thermal_zone_device_update(data->ti_thermal); + dev_dbg(&thermal->device, "thermal polling set for duration=%d msec\n", + data->ti_thermal->polling_delay); + + return 0; +} + +/* Get trip type callback functions for thermal zone */ +static int ti_thermal_get_trip_type(struct thermal_zone_device *thermal, + int trip, enum thermal_trip_type *type) +{ + if (!ti_thermal_is_valid_trip(trip)) + return -EINVAL; + + if (trip + 1 == OMAP_TRIP_NUMBER) + *type = THERMAL_TRIP_CRITICAL; + else + *type = THERMAL_TRIP_PASSIVE; + + return 0; +} + +/* Get trip temperature callback functions for thermal zone */ +static int ti_thermal_get_trip_temp(struct thermal_zone_device *thermal, + int trip, unsigned long *temp) +{ + if (!ti_thermal_is_valid_trip(trip)) + return -EINVAL; + + *temp = ti_thermal_get_trip_value(trip); + + return 0; +} + +/* Get the temperature trend callback functions for thermal zone */ +static int ti_thermal_get_trend(struct thermal_zone_device *thermal, + int trip, enum thermal_trend *trend) +{ + struct ti_thermal_data *data = thermal->devdata; + struct ti_bandgap *bgp; + int id, tr, ret = 0; + + bgp = data->bgp; + id = data->sensor_id; + + ret = ti_bandgap_get_trend(bgp, id, &tr); + if (ret) + return ret; + + if (tr > 0) + *trend = THERMAL_TREND_RAISING; + else if (tr < 0) + *trend = THERMAL_TREND_DROPPING; + else + *trend = THERMAL_TREND_STABLE; + + return 0; +} + +/* Get critical temperature callback functions for thermal zone */ +static int ti_thermal_get_crit_temp(struct thermal_zone_device *thermal, + unsigned long *temp) +{ + /* shutdown zone */ + return ti_thermal_get_trip_temp(thermal, OMAP_TRIP_NUMBER - 1, temp); +} + +static struct thermal_zone_device_ops ti_thermal_ops = { + .get_temp = ti_thermal_get_temp, + .get_trend = ti_thermal_get_trend, + .bind = ti_thermal_bind, + .unbind = ti_thermal_unbind, + .get_mode = ti_thermal_get_mode, + .set_mode = ti_thermal_set_mode, + .get_trip_type = ti_thermal_get_trip_type, + .get_trip_temp = ti_thermal_get_trip_temp, + .get_crit_temp = ti_thermal_get_crit_temp, +}; + +static struct ti_thermal_data +*ti_thermal_build_data(struct ti_bandgap *bgp, int id) +{ + struct ti_thermal_data *data; + + data = devm_kzalloc(bgp->dev, sizeof(*data), GFP_KERNEL); + if (!data) { + dev_err(bgp->dev, "kzalloc fail\n"); + return NULL; + } + data->sensor_id = id; + data->bgp = bgp; + data->mode = THERMAL_DEVICE_ENABLED; + INIT_WORK(&data->thermal_wq, ti_thermal_work); + + return data; +} + +int ti_thermal_expose_sensor(struct ti_bandgap *bgp, int id, + char *domain) +{ + struct ti_thermal_data *data; + + data = ti_bandgap_get_sensor_data(bgp, id); + + if (IS_ERR_OR_NULL(data)) + data = ti_thermal_build_data(bgp, id); + + if (!data) + return -EINVAL; + + /* Create thermal zone */ + data->ti_thermal = thermal_zone_device_register(domain, + OMAP_TRIP_NUMBER, 0, data, &ti_thermal_ops, + NULL, FAST_TEMP_MONITORING_RATE, + FAST_TEMP_MONITORING_RATE); + if (IS_ERR_OR_NULL(data->ti_thermal)) { + dev_err(bgp->dev, "thermal zone device is NULL\n"); + return PTR_ERR(data->ti_thermal); + } + data->ti_thermal->polling_delay = FAST_TEMP_MONITORING_RATE; + ti_bandgap_set_sensor_data(bgp, id, data); + + return 0; +} + +int ti_thermal_remove_sensor(struct ti_bandgap *bgp, int id) +{ + struct ti_thermal_data *data; + + data = ti_bandgap_get_sensor_data(bgp, id); + + thermal_zone_device_unregister(data->ti_thermal); + + return 0; +} + +int ti_thermal_report_sensor_temperature(struct ti_bandgap *bgp, int id) +{ + struct ti_thermal_data *data; + + data = ti_bandgap_get_sensor_data(bgp, id); + + schedule_work(&data->thermal_wq); + + return 0; +} + +int ti_thermal_register_cpu_cooling(struct ti_bandgap *bgp, int id) +{ + struct ti_thermal_data *data; + + data = ti_bandgap_get_sensor_data(bgp, id); + if (IS_ERR_OR_NULL(data)) + data = ti_thermal_build_data(bgp, id); + + if (!data) + return -EINVAL; + + if (!cpufreq_get_current_driver()) { + dev_dbg(bgp->dev, "no cpufreq driver yet\n"); + return -EPROBE_DEFER; + } + + /* Register cooling device */ + data->cool_dev = cpufreq_cooling_register(cpu_present_mask); + if (IS_ERR_OR_NULL(data->cool_dev)) { + dev_err(bgp->dev, + "Failed to register cpufreq cooling device\n"); + return PTR_ERR(data->cool_dev); + } + ti_bandgap_set_sensor_data(bgp, id, data); + + return 0; +} + +int ti_thermal_unregister_cpu_cooling(struct ti_bandgap *bgp, int id) +{ + struct ti_thermal_data *data; + + data = ti_bandgap_get_sensor_data(bgp, id); + cpufreq_cooling_unregister(data->cool_dev); + + return 0; +} diff --git a/drivers/thermal/ti-soc-thermal/ti-thermal.h b/drivers/thermal/ti-soc-thermal/ti-thermal.h new file mode 100644 index 000000000000..5055777727cc --- /dev/null +++ b/drivers/thermal/ti-soc-thermal/ti-thermal.h @@ -0,0 +1,117 @@ +/* + * OMAP thermal definitions + * + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * Contact: + * Eduardo Valentin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ +#ifndef __TI_THERMAL_H +#define __TI_THERMAL_H + +#include "ti-bandgap.h" + +/* sensors gradient and offsets */ +#define OMAP_GRADIENT_SLOPE_4430 0 +#define OMAP_GRADIENT_CONST_4430 20000 +#define OMAP_GRADIENT_SLOPE_4460 348 +#define OMAP_GRADIENT_CONST_4460 -9301 +#define OMAP_GRADIENT_SLOPE_4470 308 +#define OMAP_GRADIENT_CONST_4470 -7896 + +#define OMAP_GRADIENT_SLOPE_5430_CPU 65 +#define OMAP_GRADIENT_CONST_5430_CPU -1791 +#define OMAP_GRADIENT_SLOPE_5430_GPU 117 +#define OMAP_GRADIENT_CONST_5430_GPU -2992 + +/* PCB sensor calculation constants */ +#define OMAP_GRADIENT_SLOPE_W_PCB_4430 0 +#define OMAP_GRADIENT_CONST_W_PCB_4430 20000 +#define OMAP_GRADIENT_SLOPE_W_PCB_4460 1142 +#define OMAP_GRADIENT_CONST_W_PCB_4460 -393 +#define OMAP_GRADIENT_SLOPE_W_PCB_4470 1063 +#define OMAP_GRADIENT_CONST_W_PCB_4470 -477 + +#define OMAP_GRADIENT_SLOPE_W_PCB_5430_CPU 100 +#define OMAP_GRADIENT_CONST_W_PCB_5430_CPU 484 +#define OMAP_GRADIENT_SLOPE_W_PCB_5430_GPU 464 +#define OMAP_GRADIENT_CONST_W_PCB_5430_GPU -5102 + +/* trip points of interest in milicelsius (at hotspot level) */ +#define OMAP_TRIP_COLD 100000 +#define OMAP_TRIP_HOT 110000 +#define OMAP_TRIP_SHUTDOWN 125000 +#define OMAP_TRIP_NUMBER 2 +#define OMAP_TRIP_STEP \ + ((OMAP_TRIP_SHUTDOWN - OMAP_TRIP_HOT) / (OMAP_TRIP_NUMBER - 1)) + +/* Update rates */ +#define FAST_TEMP_MONITORING_RATE 250 + +/* helper macros */ +/** + * ti_thermal_get_trip_value - returns trip temperature based on index + * @i: trip index + */ +#define ti_thermal_get_trip_value(i) \ + (OMAP_TRIP_HOT + ((i) * OMAP_TRIP_STEP)) + +/** + * ti_thermal_is_valid_trip - check for trip index + * @i: trip index + */ +#define ti_thermal_is_valid_trip(trip) \ + ((trip) >= 0 && (trip) < OMAP_TRIP_NUMBER) + +#ifdef CONFIG_TI_THERMAL +int ti_thermal_expose_sensor(struct ti_bandgap *bgp, int id, char *domain); +int ti_thermal_remove_sensor(struct ti_bandgap *bgp, int id); +int ti_thermal_report_sensor_temperature(struct ti_bandgap *bgp, int id); +int ti_thermal_register_cpu_cooling(struct ti_bandgap *bgp, int id); +int ti_thermal_unregister_cpu_cooling(struct ti_bandgap *bgp, int id); +#else +static inline +int ti_thermal_expose_sensor(struct ti_bandgap *bgp, int id, char *domain) +{ + return 0; +} + +static inline +int ti_thermal_remove_sensor(struct ti_bandgap *bgp, int id) +{ + return 0; +} + +static inline +int ti_thermal_report_sensor_temperature(struct ti_bandgap *bgp, int id) +{ + return 0; +} + +static inline +int ti_thermal_register_cpu_cooling(struct ti_bandgap *bgp, int id) +{ + return 0; +} + +static inline +int ti_thermal_unregister_cpu_cooling(struct ti_bandgap *bgp, int id) +{ + return 0; +} +#endif +#endif -- cgit v1.2.3 From 794b2e2548ec41d81c5272f883011a13612f2fda Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Wed, 15 May 2013 15:46:01 +0000 Subject: MAINTAINERS: update TI SoC thermal driver entry Update driver path and status for TI SoC thermal drivers on MAINTAINERS file. Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- MAINTAINERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 3d7782b9f90d..311fb4512826 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8056,8 +8056,8 @@ F: drivers/platform/x86/thinkpad_acpi.c TI BANDGAP AND THERMAL DRIVER M: Eduardo Valentin L: linux-pm@vger.kernel.org -S: Maintained -F: drivers/staging/omap-thermal/ +S: Supported +F: drivers/thermal/ti-soc-thermal/ TI FLASH MEDIA INTERFACE DRIVER M: Alex Dubov -- cgit v1.2.3 From 1d089e09936420611781e2ad09519f9521381fb0 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 16 May 2013 10:28:08 +0000 Subject: Thermal: armada: Remove redundant use of of_match_ptr 'armada_thermal_id_table' is always compiled in and the driver is dependent on OF. Hence use of of_match_ptr is unnecessary. Signed-off-by: Sachin Kamat Cc: Ezequiel Garcia Acked-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/armada_thermal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thermal/armada_thermal.c b/drivers/thermal/armada_thermal.c index 5b4d75fd7b49..7c603b854e87 100644 --- a/drivers/thermal/armada_thermal.c +++ b/drivers/thermal/armada_thermal.c @@ -221,7 +221,7 @@ static struct platform_driver armada_thermal_driver = { .driver = { .name = "armada_thermal", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(armada_thermal_id_table), + .of_match_table = armada_thermal_id_table, }, }; -- cgit v1.2.3 From e0d68afa92df693928f8e28eb7ae03bd68c50d3b Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 16 May 2013 10:28:09 +0000 Subject: Thermal: dove: Remove redundant use of of_match_ptr 'dove_thermal_id_table' is always compiled in and the driver is dependent on OF. Hence use of of_match_ptr is unnecessary. Signed-off-by: Sachin Kamat Cc: Andrew Lunn Acked-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/dove_thermal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thermal/dove_thermal.c b/drivers/thermal/dove_thermal.c index 4b15a5f270dc..d5c029f9344d 100644 --- a/drivers/thermal/dove_thermal.c +++ b/drivers/thermal/dove_thermal.c @@ -195,7 +195,7 @@ static struct platform_driver dove_thermal_driver = { .driver = { .name = "dove_thermal", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(dove_thermal_id_table), + .of_match_table = dove_thermal_id_table, }, }; -- cgit v1.2.3 From 90c3194e474fe00240a441a7321b1fe37d3441aa Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 16 May 2013 10:28:10 +0000 Subject: Thermal: kirkwood: Remove redundant use of of_match_ptr 'kirkwood_thermal_id_table' is always compiled in and the driver is dependent on OF. Hence use of of_match_ptr is unnecessary. Signed-off-by: Sachin Kamat Cc: Nobuhiro Iwamatsu Acked-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/kirkwood_thermal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thermal/kirkwood_thermal.c b/drivers/thermal/kirkwood_thermal.c index dfeceaffbc03..61bbc2605b02 100644 --- a/drivers/thermal/kirkwood_thermal.c +++ b/drivers/thermal/kirkwood_thermal.c @@ -121,7 +121,7 @@ static struct platform_driver kirkwood_thermal_driver = { .driver = { .name = "kirkwood_thermal", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(kirkwood_thermal_id_table), + .of_match_table = kirkwood_thermal_id_table, }, }; -- cgit v1.2.3 From 5454f211ddc2a8ebc1eb20aa3866c4e9cf11405f Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 16 May 2013 10:28:11 +0000 Subject: Thermal: spear: Remove redundant use of of_match_ptr 'spear_thermal_id_table' is always compiled in and the driver is dependent on OF. Hence use of of_match_ptr is unnecessary. Signed-off-by: Sachin Kamat Cc: Vincenzo Frascino Cc: Viresh Kumar Acked-by: Viresh Kumar Acked-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/spear_thermal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thermal/spear_thermal.c b/drivers/thermal/spear_thermal.c index 3c5ee5607977..838aff29b9d8 100644 --- a/drivers/thermal/spear_thermal.c +++ b/drivers/thermal/spear_thermal.c @@ -198,7 +198,7 @@ static struct platform_driver spear_thermal_driver = { .name = "spear_thermal", .owner = THIS_MODULE, .pm = &spear_thermal_pm_ops, - .of_match_table = of_match_ptr(spear_thermal_id_table), + .of_match_table = spear_thermal_id_table, }, }; -- cgit v1.2.3 From e09b74d0195496fdc2dbe106eaa55da48873876e Mon Sep 17 00:00:00 2001 From: Ariel Elior Date: Mon, 27 May 2013 04:08:26 +0000 Subject: bnx2x: Zero VFs starting MACs Hypervisor/Supervisor should set the VF's MAC prior to its load; Using a randomly generated MAC as a default is a bad practice. Signed-off-by: Ariel Elior Signed-off-by: Yuval Mintz Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 7ed9cdfa115e..80982c38ae69 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -11406,7 +11406,7 @@ static int bnx2x_init_bp(struct bnx2x *bp) if (rc) return rc; } else { - random_ether_addr(bp->dev->dev_addr); + eth_zero_addr(bp->dev->dev_addr); } bnx2x_set_modes_bitmap(bp); @@ -11865,6 +11865,10 @@ static int bnx2x_validate_addr(struct net_device *dev) { struct bnx2x *bp = netdev_priv(dev); + /* query the bulletin board for mac address configured by the PF */ + if (IS_VF(bp)) + bnx2x_sample_bulletin(bp); + if (!bnx2x_is_valid_ether_addr(bp, dev->dev_addr)) { BNX2X_ERR("Non-valid Ethernet address\n"); return -EADDRNOTAVAIL; -- cgit v1.2.3 From ca1ee4b259ea8c4f91c84ee4c737338658711272 Mon Sep 17 00:00:00 2001 From: Dmitry Kravkov Date: Mon, 27 May 2013 04:08:27 +0000 Subject: bnx2x: Add and correct PCI link speed prints This adds the print of the PCI gen3 link speed (8GHz), as well as correcting the same print for 57712 boards (the print erroneously showed a 2.5GHz speed). Signed-off-by: Dmitry Kravkov Signed-off-by: Yuval Mintz Signed-off-by: Ariel Elior Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x.h | 5 +++ drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 40 +++++++++++++++--------- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h index 946450d6d988..47b06fc22b97 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h @@ -2342,4 +2342,9 @@ enum { #define NUM_MACS 8 +enum bnx2x_pci_bus_speed { + BNX2X_PCI_LINK_SPEED_2500 = 2500, + BNX2X_PCI_LINK_SPEED_5000 = 5000, + BNX2X_PCI_LINK_SPEED_8000 = 8000 +}; #endif /* bnx2x.h */ diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 80982c38ae69..04fa5105ca52 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -12111,15 +12111,26 @@ err_out: return rc; } -static void bnx2x_get_pcie_width_speed(struct bnx2x *bp, int *width, int *speed) +static void bnx2x_get_pcie_width_speed(struct bnx2x *bp, int *width, + enum bnx2x_pci_bus_speed *speed) { - u32 val = 0; + u32 link_speed, val = 0; pci_read_config_dword(bp->pdev, PCICFG_LINK_CONTROL, &val); *width = (val & PCICFG_LINK_WIDTH) >> PCICFG_LINK_WIDTH_SHIFT; - /* return value of 1=2.5GHz 2=5GHz */ - *speed = (val & PCICFG_LINK_SPEED) >> PCICFG_LINK_SPEED_SHIFT; + link_speed = (val & PCICFG_LINK_SPEED) >> PCICFG_LINK_SPEED_SHIFT; + + switch (link_speed) { + case 3: + *speed = BNX2X_PCI_LINK_SPEED_8000; + break; + case 2: + *speed = BNX2X_PCI_LINK_SPEED_5000; + break; + default: + *speed = BNX2X_PCI_LINK_SPEED_2500; + } } static int bnx2x_check_firmware(struct bnx2x *bp) @@ -12482,7 +12493,8 @@ static int bnx2x_init_one(struct pci_dev *pdev, { struct net_device *dev = NULL; struct bnx2x *bp; - int pcie_width, pcie_speed; + int pcie_width; + enum bnx2x_pci_bus_speed pcie_speed; int rc, max_non_def_sbs; int rx_count, tx_count, rss_count, doorbell_size; int max_cos_est; @@ -12634,15 +12646,15 @@ static int bnx2x_init_one(struct pci_dev *pdev, BNX2X_DEV_INFO("got pcie width %d and speed %d\n", pcie_width, pcie_speed); - BNX2X_DEV_INFO( - "%s (%c%d) PCI-E x%d %s found at mem %lx, IRQ %d, node addr %pM\n", - board_info[ent->driver_data].name, - (CHIP_REV(bp) >> 12) + 'A', (CHIP_METAL(bp) >> 4), - pcie_width, - ((!CHIP_IS_E2(bp) && pcie_speed == 2) || - (CHIP_IS_E2(bp) && pcie_speed == 1)) ? - "5GHz (Gen2)" : "2.5GHz", - dev->base_addr, bp->pdev->irq, dev->dev_addr); + BNX2X_DEV_INFO("%s (%c%d) PCI-E x%d %s found at mem %lx, IRQ %d, node addr %pM\n", + board_info[ent->driver_data].name, + (CHIP_REV(bp) >> 12) + 'A', (CHIP_METAL(bp) >> 4), + pcie_width, + pcie_speed == BNX2X_PCI_LINK_SPEED_2500 ? "2.5GHz" : + pcie_speed == BNX2X_PCI_LINK_SPEED_5000 ? "5.0GHz" : + pcie_speed == BNX2X_PCI_LINK_SPEED_8000 ? "8.0GHz" : + "Unknown", + dev->base_addr, bp->pdev->irq, dev->dev_addr); return 0; -- cgit v1.2.3 From 70ca5d746cf0c16576cee43fea6a19116ab49d9c Mon Sep 17 00:00:00 2001 From: Ariel Elior Date: Mon, 27 May 2013 04:08:28 +0000 Subject: bnx2x: Ack unknown VF messages A PF should ack the firmware when receiving unknown messages through the VF's mailbox. This prevents the VF mailbox from being stuck in case of a VF sending a message unknown to the PF (e.g. VF with more advanced version). Signed-off-by: Ariel Elior Signed-off-by: Yuval Mintz Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c index 928b074d7d80..282606677bca 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c @@ -1605,8 +1605,11 @@ static void bnx2x_vf_mbx_request(struct bnx2x *bp, struct bnx2x_virtf *vf, bnx2x_vf_mbx_resp(bp, vf); } else { /* can't send a response since this VF is unknown to us - * just unlock the channel and be done with. + * just ack the FW to release the mailbox and unlock + * the channel. */ + storm_memset_vf_mbx_ack(bp, vf->abs_vfid); + mmiowb(); bnx2x_unlock_vf_pf_channel(bp, vf, mbx->first_tlv.tl.type); } -- cgit v1.2.3 From 868001946e39627ec439921eeb0f5fa020e1e31d Mon Sep 17 00:00:00 2001 From: Dmitry Kravkov Date: Mon, 27 May 2013 04:08:29 +0000 Subject: bnx2x: Count number of possible FCoE interfaces Commit 0eb43b4bb081a1a22574daab9c05286a600dd7fe "bnx2x, bnx2fc: Use per port max exchange resources" has changed the number of available FCoE exchanges, even in scenarios when some of the functions has no FCoE support; This needlessly degraded the available resources. Remedy this by calculating the maximal number of functions that may actually utilize said connection. Signed-off-by: Dmitry Kravkov CC: Bhanu Prakash Gollapudi Signed-off-by: Yuval Mintz Signed-off-by: Ariel Elior Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 51 ++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 04fa5105ca52..0a3bb53f3eee 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -10795,12 +10795,56 @@ static void bnx2x_get_ext_wwn_info(struct bnx2x *bp, int func) bp->cnic_eth_dev.fcoe_wwn_node_name_lo = MF_CFG_RD(bp, func_ext_config[func].fcoe_wwn_node_name_lower); } + +static int bnx2x_shared_fcoe_funcs(struct bnx2x *bp) +{ + u8 count = 0; + + if (IS_MF(bp)) { + u8 fid; + + /* iterate over absolute function ids for this path: */ + for (fid = BP_PATH(bp); fid < E2_FUNC_MAX * 2; fid += 2) { + if (IS_MF_SD(bp)) { + u32 cfg = MF_CFG_RD(bp, + func_mf_config[fid].config); + + if (!(cfg & FUNC_MF_CFG_FUNC_HIDE) && + ((cfg & FUNC_MF_CFG_PROTOCOL_MASK) == + FUNC_MF_CFG_PROTOCOL_FCOE)) + count++; + } else { + u32 cfg = MF_CFG_RD(bp, + func_ext_config[fid]. + func_cfg); + + if ((cfg & MACP_FUNC_CFG_FLAGS_ENABLED) && + (cfg & MACP_FUNC_CFG_FLAGS_FCOE_OFFLOAD)) + count++; + } + } + } else { /* SF */ + int port, port_cnt = CHIP_MODE_IS_4_PORT(bp) ? 2 : 1; + + for (port = 0; port < port_cnt; port++) { + u32 lic = SHMEM_RD(bp, + drv_lic_key[port].max_fcoe_conn) ^ + FW_ENCODE_32BIT_PATTERN; + if (lic) + count++; + } + } + + return count; +} + static void bnx2x_get_fcoe_info(struct bnx2x *bp) { int port = BP_PORT(bp); int func = BP_ABS_FUNC(bp); u32 max_fcoe_conn = FW_ENCODE_32BIT_PATTERN ^ SHMEM_RD(bp, drv_lic_key[port].max_fcoe_conn); + u8 num_fcoe_func = bnx2x_shared_fcoe_funcs(bp); if (!CNIC_SUPPORT(bp)) { bp->flags |= NO_FCOE_FLAG; @@ -10814,9 +10858,10 @@ static void bnx2x_get_fcoe_info(struct bnx2x *bp) /* Calculate the number of maximum allowed FCoE tasks */ bp->cnic_eth_dev.max_fcoe_exchanges = MAX_NUM_FCOE_TASKS_PER_ENGINE; - if (IS_MF(bp) || CHIP_MODE_IS_4_PORT(bp)) - bp->cnic_eth_dev.max_fcoe_exchanges /= - MAX_FCOE_FUNCS_PER_ENGINE; + + /* check if FCoE resources must be shared between different functions */ + if (num_fcoe_func) + bp->cnic_eth_dev.max_fcoe_exchanges /= num_fcoe_func; /* Read the WWN: */ if (!IS_MF(bp)) { -- cgit v1.2.3 From b030ed2fdc8a396dba71e4d550236a0f1bb38b40 Mon Sep 17 00:00:00 2001 From: Yuval Mintz Date: Mon, 27 May 2013 04:08:30 +0000 Subject: bnx2x: Implement PCI shutdown Implement the PCI shutdown callback to support un-orderly shutdown, i.e., shutdowns in which the remove callback will not be called. Due to the lack of this functionality, when an un-orderly shutdown occurred, it was possible for wake-on-lan to remain disabled. This is now fixed as the callback introduces the correct place in which `system_state' can be queried and wake-on-lan be configured accordingly. Signed-off-by: Yuval Mintz Signed-off-by: Ariel Elior Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 71 ++++++++++++++++++++---- 1 file changed, 59 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 0a3bb53f3eee..13f814485a36 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -12721,17 +12721,11 @@ init_one_exit: return rc; } -static void bnx2x_remove_one(struct pci_dev *pdev) +static void __bnx2x_remove(struct pci_dev *pdev, + struct net_device *dev, + struct bnx2x *bp, + bool remove_netdev) { - struct net_device *dev = pci_get_drvdata(pdev); - struct bnx2x *bp; - - if (!dev) { - dev_err(&pdev->dev, "BAD net device from bnx2x_init_one\n"); - return; - } - bp = netdev_priv(dev); - /* Delete storage MAC address */ if (!NO_FCOE(bp)) { rtnl_lock(); @@ -12744,7 +12738,15 @@ static void bnx2x_remove_one(struct pci_dev *pdev) bnx2x_dcbnl_update_applist(bp, true); #endif - unregister_netdev(dev); + /* Close the interface - either directly or implicitly */ + if (remove_netdev) { + unregister_netdev(dev); + } else { + rtnl_lock(); + if (netif_running(dev)) + bnx2x_close(dev); + rtnl_unlock(); + } /* Power on: we can't let PCI layer write to us while we are in D3 */ if (IS_PF(bp)) @@ -12766,6 +12768,12 @@ static void bnx2x_remove_one(struct pci_dev *pdev) if (IS_VF(bp)) bnx2x_vfpf_release(bp); + /* Assumes no further PCIe PM changes will occur */ + if (system_state == SYSTEM_POWER_OFF) { + pci_wake_from_d3(pdev, bp->wol); + pci_set_power_state(pdev, PCI_D3hot); + } + if (bp->regview) iounmap(bp->regview); @@ -12780,7 +12788,8 @@ static void bnx2x_remove_one(struct pci_dev *pdev) } bnx2x_free_mem_bp(bp); - free_netdev(dev); + if (remove_netdev) + free_netdev(dev); if (atomic_read(&pdev->enable_cnt) == 1) pci_release_regions(pdev); @@ -12789,6 +12798,20 @@ static void bnx2x_remove_one(struct pci_dev *pdev) pci_set_drvdata(pdev, NULL); } +static void bnx2x_remove_one(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct bnx2x *bp; + + if (!dev) { + dev_err(&pdev->dev, "BAD net device from bnx2x_init_one\n"); + return; + } + bp = netdev_priv(dev); + + __bnx2x_remove(pdev, dev, bp, true); +} + static int bnx2x_eeh_nic_unload(struct bnx2x *bp) { bp->state = BNX2X_STATE_CLOSING_WAIT4_HALT; @@ -12968,6 +12991,29 @@ static const struct pci_error_handlers bnx2x_err_handler = { .resume = bnx2x_io_resume, }; +static void bnx2x_shutdown(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct bnx2x *bp; + + if (!dev) + return; + + bp = netdev_priv(dev); + if (!bp) + return; + + rtnl_lock(); + netif_device_detach(dev); + rtnl_unlock(); + + /* Don't remove the netdevice, as there are scenarios which will cause + * the kernel to hang, e.g., when trying to remove bnx2i while the + * rootfs is mounted from SAN. + */ + __bnx2x_remove(pdev, dev, bp, false); +} + static struct pci_driver bnx2x_pci_driver = { .name = DRV_MODULE_NAME, .id_table = bnx2x_pci_tbl, @@ -12979,6 +13025,7 @@ static struct pci_driver bnx2x_pci_driver = { #ifdef CONFIG_BNX2X_SRIOV .sriov_configure = bnx2x_sriov_configure, #endif + .shutdown = bnx2x_shutdown, }; static int __init bnx2x_init(void) -- cgit v1.2.3 From 3fb43eb20aec3f5ceed5813a035f200dafb51257 Mon Sep 17 00:00:00 2001 From: Yuval Mintz Date: Mon, 27 May 2013 04:08:31 +0000 Subject: bnx2x: Change to D3hot only on removal This changes the PCI power management scheme of the bnx2x driver to be similar to those of most network drivers - the driver will now changes the power state into D3hot whenever the driver will be removed, instead of whenever an interface is unloaded. This change enables the driver to access its eeprom via ethtool callbacks even when interfaces are unloaded (such access requires the function to be in D0active). Signed-off-by: Yuval Mintz Signed-off-by: Ariel Elior Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- .../net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c | 31 +++++++++++++++++----- drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 3 --- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index 32aa88f520dc..263950cddfe2 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -1381,12 +1381,28 @@ static int bnx2x_nvram_read32(struct bnx2x *bp, u32 offset, u32 *buf, return rc; } +static bool bnx2x_is_nvm_accessible(struct bnx2x *bp) +{ + int rc = 1; + u16 pm = 0; + struct net_device *dev = pci_get_drvdata(bp->pdev); + + if (bp->pm_cap) + rc = pci_read_config_word(bp->pdev, + bp->pm_cap + PCI_PM_CTRL, &pm); + + if ((rc && !netif_running(dev)) || (!rc && ((pm & PCI_D0) != PCI_D0))) + return false; + + return true; +} + static int bnx2x_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, u8 *eebuf) { struct bnx2x *bp = netdev_priv(dev); - if (!netif_running(dev)) { + if (!bnx2x_is_nvm_accessible(bp)) { DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM, "cannot access eeprom when the interface is down\n"); return -EAGAIN; @@ -1411,7 +1427,7 @@ static int bnx2x_get_module_eeprom(struct net_device *dev, u8 *user_data = data; unsigned int start_addr = ee->offset, xfer_size = 0; - if (!netif_running(dev)) { + if (!bnx2x_is_nvm_accessible(bp)) { DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM, "cannot access eeprom when the interface is down\n"); return -EAGAIN; @@ -1474,7 +1490,7 @@ static int bnx2x_get_module_info(struct net_device *dev, int phy_idx, rc; u8 sff8472_comp, diag_type; - if (!netif_running(dev)) { + if (!bnx2x_is_nvm_accessible(bp)) { DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM, "cannot access eeprom when the interface is down\n"); return -EAGAIN; @@ -1676,7 +1692,8 @@ static int bnx2x_set_eeprom(struct net_device *dev, int port = BP_PORT(bp); int rc = 0; u32 ext_phy_config; - if (!netif_running(dev)) { + + if (!bnx2x_is_nvm_accessible(bp)) { DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM, "cannot access eeprom when the interface is down\n"); return -EAGAIN; @@ -2173,7 +2190,7 @@ static int bnx2x_test_registers(struct bnx2x *bp) { BNX2X_CHIP_MASK_ALL, 0xffffffff, 0, 0x00000000 } }; - if (!netif_running(bp->dev)) { + if (!bnx2x_is_nvm_accessible(bp)) { DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM, "cannot access eeprom when the interface is down\n"); return rc; @@ -2277,7 +2294,7 @@ static int bnx2x_test_memory(struct bnx2x *bp) { NULL, 0xffffffff, {0, 0, 0, 0} } }; - if (!netif_running(bp->dev)) { + if (!bnx2x_is_nvm_accessible(bp)) { DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM, "cannot access eeprom when the interface is down\n"); return rc; @@ -3140,7 +3157,7 @@ static int bnx2x_set_phys_id(struct net_device *dev, { struct bnx2x *bp = netdev_priv(dev); - if (!netif_running(dev)) { + if (!bnx2x_is_nvm_accessible(bp)) { DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM, "cannot access eeprom when the interface is down\n"); return -EAGAIN; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 13f814485a36..f6d0e209c44e 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -11643,9 +11643,6 @@ static int bnx2x_close(struct net_device *dev) /* Unload the driver, release IRQs */ bnx2x_nic_unload(bp, UNLOAD_CLOSE, false); - /* Power off */ - bnx2x_set_power_state(bp, PCI_D3hot); - return 0; } -- cgit v1.2.3 From eeb65cedd78ebd375608d71d703e6b0b9296efbd Mon Sep 17 00:00:00 2001 From: Somnath Kotur Date: Sun, 26 May 2013 21:08:36 +0000 Subject: be2net: cleanup be_get_drvinfo() Removing the be_cmd_get_fw_ver() query from be_get_drvinfo() and invoking the same from be_setup() and/or post firmware download. Signed-off-by: Kalesh AP Signed-off-by: Somnath Kotur Signed-off-by: David S. Miller --- drivers/net/ethernet/emulex/benet/be.h | 1 + drivers/net/ethernet/emulex/benet/be_ethtool.c | 8 ++------ drivers/net/ethernet/emulex/benet/be_main.c | 6 +++++- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/emulex/benet/be.h b/drivers/net/ethernet/emulex/benet/be.h index f544b297c9ab..edce38bf83c9 100644 --- a/drivers/net/ethernet/emulex/benet/be.h +++ b/drivers/net/ethernet/emulex/benet/be.h @@ -397,6 +397,7 @@ struct be_adapter { u32 cmd_privileges; /* Ethtool knobs and info */ char fw_ver[FW_VER_LEN]; + char fw_on_flash[FW_VER_LEN]; int if_handle; /* Used to configure filtering */ u32 *pmac_id; /* MAC addr handle used by BE card */ u32 beacon_state; /* for set_phys_id */ diff --git a/drivers/net/ethernet/emulex/benet/be_ethtool.c b/drivers/net/ethernet/emulex/benet/be_ethtool.c index 3d4461adb3b4..f3ee07758198 100644 --- a/drivers/net/ethernet/emulex/benet/be_ethtool.c +++ b/drivers/net/ethernet/emulex/benet/be_ethtool.c @@ -177,19 +177,15 @@ static void be_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) { struct be_adapter *adapter = netdev_priv(netdev); - char fw_on_flash[FW_VER_LEN]; - - memset(fw_on_flash, 0 , sizeof(fw_on_flash)); - be_cmd_get_fw_ver(adapter, adapter->fw_ver, fw_on_flash); strlcpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver)); strlcpy(drvinfo->version, DRV_VER, sizeof(drvinfo->version)); - if (!memcmp(adapter->fw_ver, fw_on_flash, FW_VER_LEN)) + if (!memcmp(adapter->fw_ver, adapter->fw_on_flash, FW_VER_LEN)) strlcpy(drvinfo->fw_version, adapter->fw_ver, sizeof(drvinfo->fw_version)); else snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), - "%s [%s]", adapter->fw_ver, fw_on_flash); + "%s [%s]", adapter->fw_ver, adapter->fw_on_flash); strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 3d5e1a8929ae..d1580823c286 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -3193,7 +3193,7 @@ static int be_setup(struct be_adapter *adapter) if (status) goto err; - be_cmd_get_fw_ver(adapter, adapter->fw_ver, NULL); + be_cmd_get_fw_ver(adapter, adapter->fw_ver, adapter->fw_on_flash); if (adapter->vlans_added) be_vid_config(adapter); @@ -3785,6 +3785,10 @@ int be_load_fw(struct be_adapter *adapter, u8 *fw_file) else status = be_fw_download(adapter, fw); + if (!status) + be_cmd_get_fw_ver(adapter, adapter->fw_ver, + adapter->fw_on_flash); + fw_exit: release_firmware(fw); return status; -- cgit v1.2.3 From 48265667a756ab1c2750048caee3da719a86ebb2 Mon Sep 17 00:00:00 2001 From: Somnath Kotur Date: Sun, 26 May 2013 21:08:47 +0000 Subject: be2net: Pad skb to meet min Tx pkt size in lancer In Lancer, packets that are 32 bytes or less may cause a transmit stall. The work-around is to pad such packets to a 36 byte length. Signed-off-by: Kalesh AP Signed-off-by: Somnath Kotur Signed-off-by: David S. Miller --- drivers/net/ethernet/emulex/benet/be_main.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index d1580823c286..26e222f81433 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -848,6 +848,16 @@ static struct sk_buff *be_xmit_workarounds(struct be_adapter *adapter, unsigned int eth_hdr_len; struct iphdr *ip; + /* Lancer ASIC has a bug wherein packets that are 32 bytes or less + * may cause a transmit stall on that port. So the work-around is to + * pad such packets to a 36-byte length. + */ + if (unlikely(lancer_chip(adapter) && skb->len <= 32)) { + if (skb_padto(skb, 36)) + goto tx_drop; + skb->len = 36; + } + /* For padded packets, BE HW modifies tot_len field in IP header * incorrecly when VLAN tag is inserted by HW. */ -- cgit v1.2.3 From 3904dcc4a7af8e28e45a8d123cef3f7653847a51 Mon Sep 17 00:00:00 2001 From: Somnath Kotur Date: Sun, 26 May 2013 21:09:06 +0000 Subject: be2net: Trim padded packets for Lancer For padded packets, Lancer computes incorrect checksum. The workaround is to trim such packets. This workaround is mainly for IPv4 packets. Signed-off-by: Kalesh AP Signed-off-by: Somnath Kotur Signed-off-by: David S. Miller --- drivers/net/ethernet/emulex/benet/be_main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 26e222f81433..1140a86ff5ea 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -860,10 +860,12 @@ static struct sk_buff *be_xmit_workarounds(struct be_adapter *adapter, /* For padded packets, BE HW modifies tot_len field in IP header * incorrecly when VLAN tag is inserted by HW. + * For padded packets, Lancer computes incorrect checksum. */ eth_hdr_len = ntohs(skb->protocol) == ETH_P_8021Q ? VLAN_ETH_HLEN : ETH_HLEN; - if (skb->len <= 60 && vlan_tx_tag_present(skb) && + if (skb->len <= 60 && + (lancer_chip(adapter) || vlan_tx_tag_present(skb)) && is_ipv4_pkt(skb)) { ip = (struct iphdr *)ip_hdr(skb); pskb_trim(skb, eth_hdr_len + ntohs(ip->tot_len)); -- cgit v1.2.3 From f4e9f3d2fdb141d920c9fd7bd5ea7db348f6d3be Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 27 May 2013 03:48:29 +0000 Subject: fec: Place the phy regulator in the private structure Instead of using a local reg_phy structure, let's put it inside the private structure, so that we are able to have access to the regulator structure even when we are outside fec_probe(). This is in preparation for controlling the FEC PHY regulator in the suspend and resume functions. Signed-off-by: Fabio Estevam Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fec.h | 1 + drivers/net/ethernet/freescale/fec_main.c | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h index 9ce5b7185fda..b11cdbcc04ff 100644 --- a/drivers/net/ethernet/freescale/fec.h +++ b/drivers/net/ethernet/freescale/fec.h @@ -272,6 +272,7 @@ struct fec_enet_private { int hwts_tx_en; struct timer_list time_keep; struct fec_enet_delayed_work delay_work; + struct regulator *reg_phy; }; void fec_ptp_init(struct net_device *ndev, struct platform_device *pdev); diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 0936b26b5e1e..57ac7c3453d6 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -1840,7 +1840,6 @@ fec_probe(struct platform_device *pdev) struct resource *r; const struct of_device_id *of_id; static int dev_id; - struct regulator *reg_phy; of_id = of_match_device(fec_dt_ids, &pdev->dev); if (of_id) @@ -1919,9 +1918,9 @@ fec_probe(struct platform_device *pdev) clk_prepare_enable(fep->clk_enet_out); clk_prepare_enable(fep->clk_ptp); - reg_phy = devm_regulator_get(&pdev->dev, "phy"); - if (!IS_ERR(reg_phy)) { - ret = regulator_enable(reg_phy); + fep->reg_phy = devm_regulator_get(&pdev->dev, "phy"); + if (!IS_ERR(fep->reg_phy)) { + ret = regulator_enable(fep->reg_phy); if (ret) { dev_err(&pdev->dev, "Failed to enable phy regulator: %d\n", ret); -- cgit v1.2.3 From 7a2bbd8d8e36c4fae7610bb1be96c2ba5ff7553a Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 27 May 2013 03:48:30 +0000 Subject: fec: Invert the order of error path sequence Currently when fec_enet_init fails it jumps to 'failed_init' error path, which will attemp to free the interrupts. This is wrong because at this point the interrupts have not even been acquired. Swap failed_init/failed_irq to fix the error path. Signed-off-by: Fabio Estevam Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fec_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 57ac7c3453d6..dc7388ed40e7 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -1975,13 +1975,13 @@ fec_probe(struct platform_device *pdev) failed_register: fec_enet_mii_remove(fep); failed_mii_init: -failed_init: +failed_irq: for (i = 0; i < FEC_IRQ_NUM; i++) { irq = platform_get_irq(pdev, i); if (irq > 0) free_irq(irq, ndev); } -failed_irq: +failed_init: failed_regulator: clk_disable_unprepare(fep->clk_ahb); clk_disable_unprepare(fep->clk_ipg); -- cgit v1.2.3 From f6a4d607b3fdfaf305f3e78837cb8969d62c249a Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 27 May 2013 03:48:31 +0000 Subject: fec: Disable the PHY regulator on error and removal In the case of error during probe, disable the PHY regulator. Do the same in fec_drv_remove(). Signed-off-by: Fabio Estevam Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fec_main.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index dc7388ed40e7..58ac0c76441f 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -1926,6 +1926,8 @@ fec_probe(struct platform_device *pdev) "Failed to enable phy regulator: %d\n", ret); goto failed_regulator; } + } else { + fep->reg_phy = NULL; } fec_reset_phy(pdev); @@ -1982,6 +1984,8 @@ failed_irq: free_irq(irq, ndev); } failed_init: + if (fep->reg_phy) + regulator_disable(fep->reg_phy); failed_regulator: clk_disable_unprepare(fep->clk_ahb); clk_disable_unprepare(fep->clk_ipg); @@ -2005,6 +2009,8 @@ fec_drv_remove(struct platform_device *pdev) unregister_netdev(ndev); fec_enet_mii_remove(fep); del_timer_sync(&fep->time_keep); + if (fep->reg_phy) + regulator_disable(fep->reg_phy); clk_disable_unprepare(fep->clk_ptp); if (fep->ptp_clock) ptp_clock_unregister(fep->ptp_clock); -- cgit v1.2.3 From c55284e4ddf3779f52d730bedfa264d35d42e539 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 27 May 2013 03:48:32 +0000 Subject: fec: Remove irqs first During probe the clocks are enabled prior than the acquiring the interrupts. In the remove function we need to do the opposite: first remove the interrupts and then disable the clocks. Signed-off-by: Fabio Estevam Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fec_main.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 58ac0c76441f..cac667db61be 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -2009,6 +2009,11 @@ fec_drv_remove(struct platform_device *pdev) unregister_netdev(ndev); fec_enet_mii_remove(fep); del_timer_sync(&fep->time_keep); + for (i = 0; i < FEC_IRQ_NUM; i++) { + int irq = platform_get_irq(pdev, i); + if (irq > 0) + free_irq(irq, ndev); + } if (fep->reg_phy) regulator_disable(fep->reg_phy); clk_disable_unprepare(fep->clk_ptp); @@ -2017,11 +2022,6 @@ fec_drv_remove(struct platform_device *pdev) clk_disable_unprepare(fep->clk_enet_out); clk_disable_unprepare(fep->clk_ahb); clk_disable_unprepare(fep->clk_ipg); - for (i = 0; i < FEC_IRQ_NUM; i++) { - int irq = platform_get_irq(pdev, i); - if (irq > 0) - free_irq(irq, ndev); - } free_netdev(ndev); platform_set_drvdata(pdev, NULL); -- cgit v1.2.3 From 238f7bc74cde96e285e59a84e0c7bbd37faa8c07 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 27 May 2013 03:48:33 +0000 Subject: fec: Handle the regulator in suspend/resume In order to save power, let's disable the regulator in the suspend function and enable it in resume. Tested on a mx28evk board. Signed-off-by: Fabio Estevam Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fec_main.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index cac667db61be..866b92220c15 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -2044,6 +2044,9 @@ fec_suspend(struct device *dev) clk_disable_unprepare(fep->clk_ahb); clk_disable_unprepare(fep->clk_ipg); + if (fep->reg_phy) + regulator_disable(fep->reg_phy); + return 0; } @@ -2052,6 +2055,13 @@ fec_resume(struct device *dev) { struct net_device *ndev = dev_get_drvdata(dev); struct fec_enet_private *fep = netdev_priv(ndev); + int ret; + + if (fep->reg_phy) { + ret = regulator_enable(fep->reg_phy); + if (ret) + return ret; + } clk_prepare_enable(fep->clk_enet_out); clk_prepare_enable(fep->clk_ahb); -- cgit v1.2.3 From dfd93c977d84fef77404b689ef95bc716b313533 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 27 May 2013 19:01:12 +0000 Subject: net: ethernet: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Acked-by: Nicolas Ferre Acked-by: Rob Herring Acked-by: Roland Stigge Acked-by: Mugunthan V N Reviewed-by: H Hartley Sweeten Tested-by: Roland Stigge Signed-off-by: David S. Miller --- drivers/net/ethernet/8390/ne.c | 1 - drivers/net/ethernet/adi/bfin_mac.c | 4 ---- drivers/net/ethernet/amd/au1000_eth.c | 2 -- drivers/net/ethernet/broadcom/bcm63xx_enet.c | 1 - drivers/net/ethernet/cadence/at91_ether.c | 1 - drivers/net/ethernet/cadence/macb.c | 2 -- drivers/net/ethernet/calxeda/xgmac.c | 2 -- drivers/net/ethernet/cirrus/ep93xx_eth.c | 1 - drivers/net/ethernet/davicom/dm9000.c | 2 -- drivers/net/ethernet/ethoc.c | 2 -- drivers/net/ethernet/faraday/ftgmac100.c | 2 -- drivers/net/ethernet/faraday/ftmac100.c | 2 -- drivers/net/ethernet/freescale/fec_main.c | 2 -- drivers/net/ethernet/korina.c | 1 - drivers/net/ethernet/marvell/mv643xx_eth.c | 2 -- drivers/net/ethernet/marvell/mvneta.c | 2 -- drivers/net/ethernet/marvell/pxa168_eth.c | 1 - drivers/net/ethernet/micrel/ks8695net.c | 1 - drivers/net/ethernet/micrel/ks8842.c | 1 - drivers/net/ethernet/micrel/ks8851_mll.c | 1 - drivers/net/ethernet/netx-eth.c | 1 - drivers/net/ethernet/nuvoton/w90p910_ether.c | 2 -- drivers/net/ethernet/nxp/lpc_eth.c | 2 -- drivers/net/ethernet/renesas/sh_eth.c | 1 - drivers/net/ethernet/s6gmac.c | 1 - drivers/net/ethernet/seeq/sgiseeq.c | 1 - drivers/net/ethernet/sgi/meth.c | 1 - drivers/net/ethernet/smsc/smc911x.c | 2 -- drivers/net/ethernet/smsc/smc91x.c | 3 --- drivers/net/ethernet/smsc/smsc911x.c | 2 -- drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c | 2 -- drivers/net/ethernet/ti/cpsw.c | 1 - drivers/net/ethernet/ti/davinci_emac.c | 2 -- drivers/net/ethernet/tundra/tsi108_eth.c | 1 - drivers/net/ethernet/wiznet/w5100.c | 2 -- drivers/net/ethernet/wiznet/w5300.c | 2 -- drivers/net/ethernet/xscale/ixp4xx_eth.c | 2 -- 37 files changed, 61 deletions(-) diff --git a/drivers/net/ethernet/8390/ne.c b/drivers/net/ethernet/8390/ne.c index 47618e505355..b2e840513735 100644 --- a/drivers/net/ethernet/8390/ne.c +++ b/drivers/net/ethernet/8390/ne.c @@ -849,7 +849,6 @@ static int ne_drv_remove(struct platform_device *pdev) free_irq(dev->irq, dev); release_region(dev->base_addr, NE_IO_EXTENT); free_netdev(dev); - platform_set_drvdata(pdev, NULL); } return 0; } diff --git a/drivers/net/ethernet/adi/bfin_mac.c b/drivers/net/ethernet/adi/bfin_mac.c index dada66bfe0d6..e904b3838dcc 100644 --- a/drivers/net/ethernet/adi/bfin_mac.c +++ b/drivers/net/ethernet/adi/bfin_mac.c @@ -1719,7 +1719,6 @@ out_err_mii_probe: mdiobus_unregister(lp->mii_bus); mdiobus_free(lp->mii_bus); out_err_probe_mac: - platform_set_drvdata(pdev, NULL); free_netdev(ndev); return rc; @@ -1732,8 +1731,6 @@ static int bfin_mac_remove(struct platform_device *pdev) bfin_phc_release(lp); - platform_set_drvdata(pdev, NULL); - lp->mii_bus->priv = NULL; unregister_netdev(ndev); @@ -1868,7 +1865,6 @@ static int bfin_mii_bus_remove(struct platform_device *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); diff --git a/drivers/net/ethernet/amd/au1000_eth.c b/drivers/net/ethernet/amd/au1000_eth.c index 688aede742c7..ceb45bc963a9 100644 --- a/drivers/net/ethernet/amd/au1000_eth.c +++ b/drivers/net/ethernet/amd/au1000_eth.c @@ -1301,8 +1301,6 @@ static int au1000_remove(struct platform_device *pdev) int i; struct resource *base, *macen; - platform_set_drvdata(pdev, NULL); - unregister_netdev(dev); mdiobus_unregister(aup->mii_bus); mdiobus_free(aup->mii_bus); diff --git a/drivers/net/ethernet/broadcom/bcm63xx_enet.c b/drivers/net/ethernet/broadcom/bcm63xx_enet.c index 0b3e23ec37f7..e46466cb3627 100644 --- a/drivers/net/ethernet/broadcom/bcm63xx_enet.c +++ b/drivers/net/ethernet/broadcom/bcm63xx_enet.c @@ -1847,7 +1847,6 @@ static int bcm_enet_remove(struct platform_device *pdev) clk_disable_unprepare(priv->mac_clk); clk_put(priv->mac_clk); - platform_set_drvdata(pdev, NULL); free_netdev(dev); return 0; } diff --git a/drivers/net/ethernet/cadence/at91_ether.c b/drivers/net/ethernet/cadence/at91_ether.c index cc9a185f0abb..3f1957158a3b 100644 --- a/drivers/net/ethernet/cadence/at91_ether.c +++ b/drivers/net/ethernet/cadence/at91_ether.c @@ -435,7 +435,6 @@ static int at91ether_remove(struct platform_device *pdev) unregister_netdev(dev); clk_disable(lp->pclk); free_netdev(dev); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c index c89aa41dd448..4465e27ba9d1 100644 --- a/drivers/net/ethernet/cadence/macb.c +++ b/drivers/net/ethernet/cadence/macb.c @@ -1649,7 +1649,6 @@ err_out_put_pclk: err_out_free_dev: free_netdev(dev); err_out: - platform_set_drvdata(pdev, NULL); return err; } @@ -1675,7 +1674,6 @@ static int __exit macb_remove(struct platform_device *pdev) clk_disable_unprepare(bp->pclk); clk_put(bp->pclk); free_netdev(dev); - platform_set_drvdata(pdev, NULL); } return 0; diff --git a/drivers/net/ethernet/calxeda/xgmac.c b/drivers/net/ethernet/calxeda/xgmac.c index 4a1f2fa812ab..7cb148c495c9 100644 --- a/drivers/net/ethernet/calxeda/xgmac.c +++ b/drivers/net/ethernet/calxeda/xgmac.c @@ -1790,7 +1790,6 @@ err_io: free_netdev(ndev); err_alloc: release_mem_region(res->start, resource_size(res)); - platform_set_drvdata(pdev, NULL); return ret; } @@ -1813,7 +1812,6 @@ static int xgmac_remove(struct platform_device *pdev) free_irq(ndev->irq, ndev); free_irq(priv->pmt_irq, ndev); - platform_set_drvdata(pdev, NULL); unregister_netdev(ndev); netif_napi_del(&priv->napi); diff --git a/drivers/net/ethernet/cirrus/ep93xx_eth.c b/drivers/net/ethernet/cirrus/ep93xx_eth.c index 67b0388b6e68..e3d4ec836f8b 100644 --- a/drivers/net/ethernet/cirrus/ep93xx_eth.c +++ b/drivers/net/ethernet/cirrus/ep93xx_eth.c @@ -783,7 +783,6 @@ static int ep93xx_eth_remove(struct platform_device *pdev) dev = platform_get_drvdata(pdev); if (dev == NULL) return 0; - platform_set_drvdata(pdev, NULL); ep = netdev_priv(dev); diff --git a/drivers/net/ethernet/davicom/dm9000.c b/drivers/net/ethernet/davicom/dm9000.c index dd243a1b03e0..a13b312b50f2 100644 --- a/drivers/net/ethernet/davicom/dm9000.c +++ b/drivers/net/ethernet/davicom/dm9000.c @@ -1699,8 +1699,6 @@ dm9000_drv_remove(struct platform_device *pdev) { struct net_device *ndev = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); - unregister_netdev(ndev); dm9000_release_board(pdev, netdev_priv(ndev)); free_netdev(ndev); /* free device structure */ diff --git a/drivers/net/ethernet/ethoc.c b/drivers/net/ethernet/ethoc.c index 5722bc61fa58..cf579fb39bc5 100644 --- a/drivers/net/ethernet/ethoc.c +++ b/drivers/net/ethernet/ethoc.c @@ -1147,8 +1147,6 @@ static int ethoc_remove(struct platform_device *pdev) struct net_device *netdev = platform_get_drvdata(pdev); struct ethoc *priv = netdev_priv(netdev); - platform_set_drvdata(pdev, NULL); - if (netdev) { netif_napi_del(&priv->napi); phy_disconnect(priv->phy); diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 21b85fb7d05f..934e1ae279f0 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -1311,7 +1311,6 @@ err_ioremap: release_resource(priv->res); err_req_mem: netif_napi_del(&priv->napi); - platform_set_drvdata(pdev, NULL); free_netdev(netdev); err_alloc_etherdev: return err; @@ -1335,7 +1334,6 @@ static int __exit ftgmac100_remove(struct platform_device *pdev) release_resource(priv->res); netif_napi_del(&priv->napi); - platform_set_drvdata(pdev, NULL); free_netdev(netdev); return 0; } diff --git a/drivers/net/ethernet/faraday/ftmac100.c b/drivers/net/ethernet/faraday/ftmac100.c index a6eda8d83138..4658f4cc1969 100644 --- a/drivers/net/ethernet/faraday/ftmac100.c +++ b/drivers/net/ethernet/faraday/ftmac100.c @@ -1149,7 +1149,6 @@ err_ioremap: release_resource(priv->res); err_req_mem: netif_napi_del(&priv->napi); - platform_set_drvdata(pdev, NULL); free_netdev(netdev); err_alloc_etherdev: return err; @@ -1169,7 +1168,6 @@ static int __exit ftmac100_remove(struct platform_device *pdev) release_resource(priv->res); netif_napi_del(&priv->napi); - platform_set_drvdata(pdev, NULL); free_netdev(netdev); return 0; } diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 866b92220c15..9b0c6472bf04 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -2024,8 +2024,6 @@ fec_drv_remove(struct platform_device *pdev) clk_disable_unprepare(fep->clk_ipg); free_netdev(ndev); - platform_set_drvdata(pdev, NULL); - return 0; } diff --git a/drivers/net/ethernet/korina.c b/drivers/net/ethernet/korina.c index 0b5708520121..64646eb39e8b 100644 --- a/drivers/net/ethernet/korina.c +++ b/drivers/net/ethernet/korina.c @@ -1211,7 +1211,6 @@ static int korina_remove(struct platform_device *pdev) iounmap(lp->rx_dma_regs); iounmap(lp->tx_dma_regs); - platform_set_drvdata(pdev, NULL); unregister_netdev(bif->dev); free_netdev(bif->dev); diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index 2ad1494efbb3..afb8bcbe29f8 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -2813,8 +2813,6 @@ static int mv643xx_eth_remove(struct platform_device *pdev) free_netdev(mp->dev); - platform_set_drvdata(pdev, NULL); - return 0; } diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index c96678555233..cd0ea204ac24 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -2804,8 +2804,6 @@ static int mvneta_remove(struct platform_device *pdev) irq_dispose_mapping(dev->irq); free_netdev(dev); - platform_set_drvdata(pdev, NULL); - return 0; } diff --git a/drivers/net/ethernet/marvell/pxa168_eth.c b/drivers/net/ethernet/marvell/pxa168_eth.c index 339bb323cb0c..2602cf7ba642 100644 --- a/drivers/net/ethernet/marvell/pxa168_eth.c +++ b/drivers/net/ethernet/marvell/pxa168_eth.c @@ -1602,7 +1602,6 @@ static int pxa168_eth_remove(struct platform_device *pdev) unregister_netdev(dev); cancel_work_sync(&pep->tx_timeout_task); free_netdev(dev); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/net/ethernet/micrel/ks8695net.c b/drivers/net/ethernet/micrel/ks8695net.c index b6c60fdef4ff..106eb972f2ac 100644 --- a/drivers/net/ethernet/micrel/ks8695net.c +++ b/drivers/net/ethernet/micrel/ks8695net.c @@ -1600,7 +1600,6 @@ ks8695_drv_remove(struct platform_device *pdev) struct net_device *ndev = platform_get_drvdata(pdev); struct ks8695_priv *ksp = netdev_priv(ndev); - platform_set_drvdata(pdev, NULL); netif_napi_del(&ksp->napi); unregister_netdev(ndev); diff --git a/drivers/net/ethernet/micrel/ks8842.c b/drivers/net/ethernet/micrel/ks8842.c index fbcb9e74d7fc..e393d998be89 100644 --- a/drivers/net/ethernet/micrel/ks8842.c +++ b/drivers/net/ethernet/micrel/ks8842.c @@ -1250,7 +1250,6 @@ static int ks8842_remove(struct platform_device *pdev) iounmap(adapter->hw_addr); free_netdev(netdev); release_mem_region(iomem->start, resource_size(iomem)); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/net/ethernet/micrel/ks8851_mll.c b/drivers/net/ethernet/micrel/ks8851_mll.c index ddaf138ce0d4..e9b1a830d582 100644 --- a/drivers/net/ethernet/micrel/ks8851_mll.c +++ b/drivers/net/ethernet/micrel/ks8851_mll.c @@ -1671,7 +1671,6 @@ static int ks8851_remove(struct platform_device *pdev) iounmap(ks->hw_addr); free_netdev(netdev); release_mem_region(iomem->start, resource_size(iomem)); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/net/ethernet/netx-eth.c b/drivers/net/ethernet/netx-eth.c index 5da3ffb61062..dc2c6f561e9a 100644 --- a/drivers/net/ethernet/netx-eth.c +++ b/drivers/net/ethernet/netx-eth.c @@ -422,7 +422,6 @@ exit_free_pfifo: exit_free_xc: free_xc(priv->xc); exit_free_netdev: - platform_set_drvdata(pdev, NULL); free_netdev(ndev); exit: return ret; diff --git a/drivers/net/ethernet/nuvoton/w90p910_ether.c b/drivers/net/ethernet/nuvoton/w90p910_ether.c index 3df8287b7452..e88bdb1aa669 100644 --- a/drivers/net/ethernet/nuvoton/w90p910_ether.c +++ b/drivers/net/ethernet/nuvoton/w90p910_ether.c @@ -1051,7 +1051,6 @@ failed_put_clk: clk_put(ether->clk); failed_free_rxirq: free_irq(ether->rxirq, pdev); - platform_set_drvdata(pdev, NULL); failed_free_txirq: free_irq(ether->txirq, pdev); failed_free_io: @@ -1080,7 +1079,6 @@ static int w90p910_ether_remove(struct platform_device *pdev) free_irq(ether->rxirq, dev); del_timer_sync(ðer->check_timer); - platform_set_drvdata(pdev, NULL); free_netdev(dev); return 0; diff --git a/drivers/net/ethernet/nxp/lpc_eth.c b/drivers/net/ethernet/nxp/lpc_eth.c index 55a5548d6add..a061b93efe66 100644 --- a/drivers/net/ethernet/nxp/lpc_eth.c +++ b/drivers/net/ethernet/nxp/lpc_eth.c @@ -1483,7 +1483,6 @@ static int lpc_eth_drv_probe(struct platform_device *pdev) return 0; err_out_unregister_netdev: - platform_set_drvdata(pdev, NULL); unregister_netdev(ndev); err_out_dma_unmap: if (!use_iram_for_net(&pldat->pdev->dev) || @@ -1511,7 +1510,6 @@ static int lpc_eth_drv_remove(struct platform_device *pdev) struct netdata_local *pldat = netdev_priv(ndev); unregister_netdev(ndev); - platform_set_drvdata(pdev, NULL); if (!use_iram_for_net(&pldat->pdev->dev) || pldat->dma_buff_size > lpc32xx_return_iram_size()) diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 42e9dd05c936..399a7c968e0d 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -2803,7 +2803,6 @@ static int sh_eth_drv_remove(struct platform_device *pdev) unregister_netdev(ndev); pm_runtime_disable(&pdev->dev); free_netdev(ndev); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/net/ethernet/s6gmac.c b/drivers/net/ethernet/s6gmac.c index b6739afeaca1..a99739c5142c 100644 --- a/drivers/net/ethernet/s6gmac.c +++ b/drivers/net/ethernet/s6gmac.c @@ -1040,7 +1040,6 @@ static int s6gmac_remove(struct platform_device *pdev) unregister_netdev(dev); free_irq(dev->irq, dev); free_netdev(dev); - platform_set_drvdata(pdev, NULL); } return 0; } diff --git a/drivers/net/ethernet/seeq/sgiseeq.c b/drivers/net/ethernet/seeq/sgiseeq.c index 0ad5694b41f8..856e523ac936 100644 --- a/drivers/net/ethernet/seeq/sgiseeq.c +++ b/drivers/net/ethernet/seeq/sgiseeq.c @@ -818,7 +818,6 @@ static int __exit sgiseeq_remove(struct platform_device *pdev) dma_free_noncoherent(&pdev->dev, sizeof(*sp->srings), sp->srings, sp->srings_dma); free_netdev(dev); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/net/ethernet/sgi/meth.c b/drivers/net/ethernet/sgi/meth.c index 4bdbaad9932d..9f5f35e041ac 100644 --- a/drivers/net/ethernet/sgi/meth.c +++ b/drivers/net/ethernet/sgi/meth.c @@ -863,7 +863,6 @@ static int __exit meth_remove(struct platform_device *pdev) unregister_netdev(dev); free_netdev(dev); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/net/ethernet/smsc/smc911x.c b/drivers/net/ethernet/smsc/smc911x.c index 9dd842dbb859..345558fe7367 100644 --- a/drivers/net/ethernet/smsc/smc911x.c +++ b/drivers/net/ethernet/smsc/smc911x.c @@ -2087,7 +2087,6 @@ static int smc911x_drv_probe(struct platform_device *pdev) ndev->base_addr = res->start; ret = smc911x_probe(ndev); if (ret != 0) { - platform_set_drvdata(pdev, NULL); iounmap(addr); release_both: free_netdev(ndev); @@ -2113,7 +2112,6 @@ static int smc911x_drv_remove(struct platform_device *pdev) struct resource *res; DBG(SMC_DEBUG_FUNC, "--> %s\n", __func__); - platform_set_drvdata(pdev, NULL); unregister_netdev(ndev); diff --git a/drivers/net/ethernet/smsc/smc91x.c b/drivers/net/ethernet/smsc/smc91x.c index dfbf978315df..cde13be7c7de 100644 --- a/drivers/net/ethernet/smsc/smc91x.c +++ b/drivers/net/ethernet/smsc/smc91x.c @@ -2299,7 +2299,6 @@ static int smc_drv_probe(struct platform_device *pdev) return 0; out_iounmap: - platform_set_drvdata(pdev, NULL); iounmap(addr); out_release_attrib: smc_release_attrib(pdev, ndev); @@ -2319,8 +2318,6 @@ static int smc_drv_remove(struct platform_device *pdev) struct smc_local *lp = netdev_priv(ndev); struct resource *res; - platform_set_drvdata(pdev, NULL); - unregister_netdev(ndev); free_irq(ndev->irq, ndev); diff --git a/drivers/net/ethernet/smsc/smsc911x.c b/drivers/net/ethernet/smsc/smsc911x.c index 3663b9e04a31..a1419211585b 100644 --- a/drivers/net/ethernet/smsc/smsc911x.c +++ b/drivers/net/ethernet/smsc/smsc911x.c @@ -2284,7 +2284,6 @@ static int smsc911x_drv_remove(struct platform_device *pdev) mdiobus_unregister(pdata->mii_bus); mdiobus_free(pdata->mii_bus); - platform_set_drvdata(pdev, NULL); unregister_netdev(dev); free_irq(dev->irq, dev); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, @@ -2539,7 +2538,6 @@ out_disable_resources: out_enable_resources_fail: smsc911x_free_resources(pdev); out_request_resources_fail: - platform_set_drvdata(pdev, NULL); iounmap(pdata->ioaddr); free_netdev(dev); out_release_io_1: diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index 1d3780f55ba2..17bc7827e7ca 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -171,8 +171,6 @@ static int stmmac_pltfr_remove(struct platform_device *pdev) if (priv->plat->exit) priv->plat->exit(pdev); - platform_set_drvdata(pdev, NULL); - return ret; } diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 21a5b291b4b3..89a4c40d6d83 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -1940,7 +1940,6 @@ static int cpsw_remove(struct platform_device *pdev) struct cpsw_priv *priv = netdev_priv(ndev); int i; - platform_set_drvdata(pdev, NULL); if (priv->data.dual_emac) unregister_netdev(cpsw_get_slave_ndev(priv, 1)); unregister_netdev(ndev); diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c index 860e15ddfbcb..efb6f65a8be3 100644 --- a/drivers/net/ethernet/ti/davinci_emac.c +++ b/drivers/net/ethernet/ti/davinci_emac.c @@ -2037,8 +2037,6 @@ static int davinci_emac_remove(struct platform_device *pdev) dev_notice(&ndev->dev, "DaVinci EMAC: davinci_emac_remove()\n"); - platform_set_drvdata(pdev, NULL); - if (priv->txchan) cpdma_chan_destroy(priv->txchan); if (priv->rxchan) diff --git a/drivers/net/ethernet/tundra/tsi108_eth.c b/drivers/net/ethernet/tundra/tsi108_eth.c index 3c69a0460832..01bdc6ca0755 100644 --- a/drivers/net/ethernet/tundra/tsi108_eth.c +++ b/drivers/net/ethernet/tundra/tsi108_eth.c @@ -1682,7 +1682,6 @@ static int tsi108_ether_remove(struct platform_device *pdev) unregister_netdev(dev); tsi108_stop_ethernet(dev); - platform_set_drvdata(pdev, NULL); iounmap(priv->regs); iounmap(priv->phyregs); free_netdev(dev); diff --git a/drivers/net/ethernet/wiznet/w5100.c b/drivers/net/ethernet/wiznet/w5100.c index a518dcab396e..30fed08d1674 100644 --- a/drivers/net/ethernet/wiznet/w5100.c +++ b/drivers/net/ethernet/wiznet/w5100.c @@ -734,7 +734,6 @@ err_hw_probe: unregister_netdev(ndev); err_register: free_netdev(ndev); - platform_set_drvdata(pdev, NULL); return err; } @@ -750,7 +749,6 @@ static int w5100_remove(struct platform_device *pdev) unregister_netdev(ndev); free_netdev(ndev); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/net/ethernet/wiznet/w5300.c b/drivers/net/ethernet/wiznet/w5300.c index 6e00e3f94ce4..e92884564e1e 100644 --- a/drivers/net/ethernet/wiznet/w5300.c +++ b/drivers/net/ethernet/wiznet/w5300.c @@ -646,7 +646,6 @@ err_hw_probe: unregister_netdev(ndev); err_register: free_netdev(ndev); - platform_set_drvdata(pdev, NULL); return err; } @@ -662,7 +661,6 @@ static int w5300_remove(struct platform_device *pdev) unregister_netdev(ndev); free_netdev(ndev); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/net/ethernet/xscale/ixp4xx_eth.c b/drivers/net/ethernet/xscale/ixp4xx_eth.c index 6958a5e87703..3d689fcb7917 100644 --- a/drivers/net/ethernet/xscale/ixp4xx_eth.c +++ b/drivers/net/ethernet/xscale/ixp4xx_eth.c @@ -1472,7 +1472,6 @@ err_phy_dis: phy_disconnect(port->phydev); err_free_mem: npe_port_tab[NPE_ID(port->id)] = NULL; - platform_set_drvdata(pdev, NULL); release_resource(port->mem_res); err_npe_rel: npe_release(port->npe); @@ -1489,7 +1488,6 @@ static int eth_remove_one(struct platform_device *pdev) unregister_netdev(dev); phy_disconnect(port->phydev); npe_port_tab[NPE_ID(port->id)] = NULL; - platform_set_drvdata(pdev, NULL); npe_release(port->npe); release_resource(port->mem_res); free_netdev(dev); -- cgit v1.2.3 From bf3a33cec8d8b380647c9bbf17439e0c1695ba43 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 27 May 2013 19:03:49 +0000 Subject: net: irda: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Acked-by: Kuninori Morimoto Signed-off-by: David S. Miller --- drivers/net/irda/bfin_sir.c | 1 - drivers/net/irda/sh_irda.c | 1 - drivers/net/irda/sh_sir.c | 1 - 3 files changed, 3 deletions(-) diff --git a/drivers/net/irda/bfin_sir.c b/drivers/net/irda/bfin_sir.c index 22b4527321b1..c74f384c87d5 100644 --- a/drivers/net/irda/bfin_sir.c +++ b/drivers/net/irda/bfin_sir.c @@ -794,7 +794,6 @@ static int bfin_sir_remove(struct platform_device *pdev) kfree(self->rx_buff.head); free_netdev(dev); kfree(sir_port); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/net/irda/sh_irda.c b/drivers/net/irda/sh_irda.c index 9448587de453..4455425f1c77 100644 --- a/drivers/net/irda/sh_irda.c +++ b/drivers/net/irda/sh_irda.c @@ -838,7 +838,6 @@ static int sh_irda_remove(struct platform_device *pdev) sh_irda_remove_iobuf(self); iounmap(self->membase); free_netdev(ndev); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/net/irda/sh_sir.c b/drivers/net/irda/sh_sir.c index 24aefcd84065..89682b49900f 100644 --- a/drivers/net/irda/sh_sir.c +++ b/drivers/net/irda/sh_sir.c @@ -796,7 +796,6 @@ static int sh_sir_remove(struct platform_device *pdev) sh_sir_remove_iobuf(self); iounmap(self->membase); free_netdev(ndev); - platform_set_drvdata(pdev, NULL); return 0; } -- cgit v1.2.3 From 785ec305b26ee23e0efb834b5cd0dd24070ba1bf Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 27 May 2013 19:07:11 +0000 Subject: net: wireless: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: David S. Miller --- drivers/net/wireless/ath/ath5k/ahb.c | 2 -- drivers/net/wireless/ath/ath9k/ahb.c | 2 -- 2 files changed, 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath5k/ahb.c b/drivers/net/wireless/ath/ath5k/ahb.c index 8e8bcc7a4805..e9bc9e616b69 100644 --- a/drivers/net/wireless/ath/ath5k/ahb.c +++ b/drivers/net/wireless/ath/ath5k/ahb.c @@ -185,7 +185,6 @@ static int ath_ahb_probe(struct platform_device *pdev) err_free_hw: ieee80211_free_hw(hw); - platform_set_drvdata(pdev, NULL); err_iounmap: iounmap(mem); err_out: @@ -221,7 +220,6 @@ static int ath_ahb_remove(struct platform_device *pdev) ath5k_deinit_ah(ah); iounmap(ah->iobase); - platform_set_drvdata(pdev, NULL); ieee80211_free_hw(hw); return 0; diff --git a/drivers/net/wireless/ath/ath9k/ahb.c b/drivers/net/wireless/ath/ath9k/ahb.c index d1ff3c246a12..072e4b531067 100644 --- a/drivers/net/wireless/ath/ath9k/ahb.c +++ b/drivers/net/wireless/ath/ath9k/ahb.c @@ -150,7 +150,6 @@ static int ath_ahb_probe(struct platform_device *pdev) free_irq(irq, sc); err_free_hw: ieee80211_free_hw(hw); - platform_set_drvdata(pdev, NULL); return ret; } @@ -164,7 +163,6 @@ static int ath_ahb_remove(struct platform_device *pdev) ath9k_deinit_device(sc); free_irq(sc->irq, sc); ieee80211_free_hw(sc->hw); - platform_set_drvdata(pdev, NULL); } return 0; -- cgit v1.2.3 From 1010dcceed9d45dc8a07c50ec93e5d0c1ef7cc24 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 27 May 2013 19:08:17 +0000 Subject: net: wan: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: David S. Miller --- drivers/net/wan/ixp4xx_hss.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wan/ixp4xx_hss.c b/drivers/net/wan/ixp4xx_hss.c index fc9d11d74d60..e7bbdb7af53a 100644 --- a/drivers/net/wan/ixp4xx_hss.c +++ b/drivers/net/wan/ixp4xx_hss.c @@ -1384,7 +1384,6 @@ static int hss_remove_one(struct platform_device *pdev) unregister_hdlc_device(port->netdev); free_netdev(port->netdev); npe_release(port->npe); - platform_set_drvdata(pdev, NULL); kfree(port); return 0; } -- cgit v1.2.3 From 7ec872114251b618adf5a2688de0ca00de63635d Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 23 May 2013 01:11:11 +0000 Subject: net: ethtool: disambiguate XCVR_* meaning Add a comment which explains the real meaning of XCVR_INTERNAL (PHY and Ethernet MAC in the same package/die) and XCVR_EXTERNAL (PHY and Ethernet MAC in a different package/die). Most if not all of the drivers setting their transceiver type already do it the way the comment describes it. Signed-off-by: Florian Fainelli Reviewed-by: Ben Hutchings Signed-off-by: David S. Miller --- include/uapi/linux/ethtool.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h index 0c9b44871df0..38dbafaa5341 100644 --- a/include/uapi/linux/ethtool.h +++ b/include/uapi/linux/ethtool.h @@ -993,8 +993,8 @@ enum ethtool_sfeatures_retval_bits { #define PORT_OTHER 0xff /* Which transceiver to use. */ -#define XCVR_INTERNAL 0x00 -#define XCVR_EXTERNAL 0x01 +#define XCVR_INTERNAL 0x00 /* PHY and MAC are in the same package */ +#define XCVR_EXTERNAL 0x01 /* PHY and MAC are in different packages */ #define XCVR_DUMMY1 0x02 #define XCVR_DUMMY2 0x03 #define XCVR_DUMMY3 0x04 -- cgit v1.2.3 From 4284b6a535a9aab33e5f3c37929143508dd2ee60 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 23 May 2013 01:11:12 +0000 Subject: phy: allow drivers to flag a PHY device as internal libphy currently always reports a PHY as an external transceiver from the ethtool output. This is inaccurate, because some drivers should be able to tell that a PHY device is an internal transceiver of an Ethernet MAC. Add a new flag (PHY_IS_INTERNAL) which can be set by PHY drivers just like other flags, and a corresponding helper: phy_is_internal() which can be used by networking drivers to query if a given PHY device is internal. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 3 ++- drivers/net/phy/phy_device.c | 3 +++ include/linux/phy.h | 12 ++++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 2d28a0ef4572..b2a94e436ed8 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -294,7 +294,8 @@ int phy_ethtool_gset(struct phy_device *phydev, struct ethtool_cmd *cmd) cmd->duplex = phydev->duplex; cmd->port = PORT_MII; cmd->phy_address = phydev->addr; - cmd->transceiver = XCVR_EXTERNAL; + cmd->transceiver = phy_is_internal(phydev) ? + XCVR_INTERNAL : XCVR_EXTERNAL; cmd->autoneg = phydev->autoneg; return 0; diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index b55aa33a5b8b..74630e94fa3b 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -1017,6 +1017,9 @@ static int phy_probe(struct device *dev) phy_interrupt_is_valid(phydev)) phydev->irq = PHY_POLL; + if (phydrv->flags & PHY_IS_INTERNAL) + phydev->is_internal = true; + mutex_lock(&phydev->lock); /* Start out supporting everything. Eventually, diff --git a/include/linux/phy.h b/include/linux/phy.h index fdfa11542974..ee411b0eef5d 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -49,6 +49,7 @@ #define PHY_HAS_INTERRUPT 0x00000001 #define PHY_HAS_MAGICANEG 0x00000002 +#define PHY_IS_INTERNAL 0x00000004 /* Interface Mode definitions */ typedef enum { @@ -261,6 +262,7 @@ struct phy_c45_device_ids { * phy_id: UID for this device found during discovery * c45_ids: 802.3-c45 Device Identifers if is_c45. * is_c45: Set to true if this phy uses clause 45 addressing. + * is_internal: Set to true if this phy is internal to a MAC. * state: state of the PHY for management purposes * dev_flags: Device-specific flags used by the PHY driver. * addr: Bus address of PHY @@ -298,6 +300,7 @@ struct phy_device { struct phy_c45_device_ids c45_ids; bool is_c45; + bool is_internal; enum phy_state state; @@ -520,6 +523,15 @@ static inline bool phy_interrupt_is_valid(struct phy_device *phydev) return phydev->irq != PHY_POLL && phydev->irq != PHY_IGNORE_INTERRUPT; } +/** + * phy_is_internal - Convenience function for testing if a PHY is internal + * @phydev: the phy_device struct + */ +static inline bool phy_is_internal(struct phy_device *phydev) +{ + return phydev->is_internal; +} + struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id, bool is_c45, struct phy_c45_device_ids *c45_ids); struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45); -- cgit v1.2.3 From 50ab731e967d65549b11b808f129329dd614e8d9 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 23 May 2013 01:11:13 +0000 Subject: phy: bcm63xx: report Broadcom BCM63xx PHYs as internal The Broadcom BCM63xx PHY driver is for the SoC internal PHYs, flag these as internal PHY devices. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/bcm63xx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/phy/bcm63xx.c b/drivers/net/phy/bcm63xx.c index 84c7a39b1c65..ac55b0807853 100644 --- a/drivers/net/phy/bcm63xx.c +++ b/drivers/net/phy/bcm63xx.c @@ -78,7 +78,7 @@ static struct phy_driver bcm63xx_driver[] = { .name = "Broadcom BCM63XX (1)", /* ASYM_PAUSE bit is marked RO in datasheet, so don't cheat */ .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause), - .flags = PHY_HAS_INTERRUPT, + .flags = PHY_HAS_INTERRUPT | PHY_IS_INTERNAL, .config_init = bcm63xx_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, @@ -91,7 +91,7 @@ static struct phy_driver bcm63xx_driver[] = { .phy_id_mask = 0xfffffc00, .name = "Broadcom BCM63XX (2)", .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause), - .flags = PHY_HAS_INTERRUPT, + .flags = PHY_HAS_INTERRUPT | PHY_IS_INTERNAL, .config_init = bcm63xx_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, -- cgit v1.2.3 From 1a37e412a0225fcba5587f24c0dfc7636efc8b69 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Thu, 23 May 2013 21:02:51 +0000 Subject: net: Use 16bits for *_headers fields of struct skbuff In order to mitigate ongoing incresase in the size of struct skbuff use 16 bit integer offsets rather than pointers for inner_*_headers. This appears to reduce the size of struct skbuff from 0xd0 to 0xc0 bytes on x86_64 with the following all unset. CONFIG_XFRM CONFIG_NF_CONNTRACK CONFIG_NF_CONNTRACK_MODULE NET_SKBUFF_NF_DEFRAG_NEEDED CONFIG_BRIDGE_NETFILTER CONFIG_NET_SCHED CONFIG_IPV6_NDISC_NODETYPE CONFIG_NET_DMA CONFIG_NETWORK_SECMARK Signed-off-by: Simon Horman Signed-off-by: David S. Miller --- include/linux/skbuff.h | 119 +++---------------------------------------------- 1 file changed, 6 insertions(+), 113 deletions(-) diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 2e0ced1af3b1..5663e3592784 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -509,12 +509,12 @@ struct sk_buff { __u32 reserved_tailroom; }; - sk_buff_data_t inner_transport_header; - sk_buff_data_t inner_network_header; - sk_buff_data_t inner_mac_header; - sk_buff_data_t transport_header; - sk_buff_data_t network_header; - sk_buff_data_t mac_header; + __u16 inner_transport_header; + __u16 inner_network_header; + __u16 inner_mac_header; + __u16 transport_header; + __u16 network_header; + __u16 mac_header; /* These elements must be at the end, see alloc_skb() for details. */ sk_buff_data_t tail; sk_buff_data_t end; @@ -1527,7 +1527,6 @@ static inline void skb_reset_mac_len(struct sk_buff *skb) skb->mac_len = skb->network_header - skb->mac_header; } -#ifdef NET_SKBUFF_DATA_USES_OFFSET static inline unsigned char *skb_inner_transport_header(const struct sk_buff *skb) { @@ -1638,112 +1637,6 @@ static inline void skb_set_mac_header(struct sk_buff *skb, const int offset) skb->mac_header += offset; } -#else /* NET_SKBUFF_DATA_USES_OFFSET */ -static inline unsigned char *skb_inner_transport_header(const struct sk_buff - *skb) -{ - return skb->inner_transport_header; -} - -static inline void skb_reset_inner_transport_header(struct sk_buff *skb) -{ - skb->inner_transport_header = skb->data; -} - -static inline void skb_set_inner_transport_header(struct sk_buff *skb, - const int offset) -{ - skb->inner_transport_header = skb->data + offset; -} - -static inline unsigned char *skb_inner_network_header(const struct sk_buff *skb) -{ - return skb->inner_network_header; -} - -static inline void skb_reset_inner_network_header(struct sk_buff *skb) -{ - skb->inner_network_header = skb->data; -} - -static inline void skb_set_inner_network_header(struct sk_buff *skb, - const int offset) -{ - skb->inner_network_header = skb->data + offset; -} - -static inline unsigned char *skb_inner_mac_header(const struct sk_buff *skb) -{ - return skb->inner_mac_header; -} - -static inline void skb_reset_inner_mac_header(struct sk_buff *skb) -{ - skb->inner_mac_header = skb->data; -} - -static inline void skb_set_inner_mac_header(struct sk_buff *skb, - const int offset) -{ - skb->inner_mac_header = skb->data + offset; -} -static inline bool skb_transport_header_was_set(const struct sk_buff *skb) -{ - return skb->transport_header != NULL; -} - -static inline unsigned char *skb_transport_header(const struct sk_buff *skb) -{ - return skb->transport_header; -} - -static inline void skb_reset_transport_header(struct sk_buff *skb) -{ - skb->transport_header = skb->data; -} - -static inline void skb_set_transport_header(struct sk_buff *skb, - const int offset) -{ - skb->transport_header = skb->data + offset; -} - -static inline unsigned char *skb_network_header(const struct sk_buff *skb) -{ - return skb->network_header; -} - -static inline void skb_reset_network_header(struct sk_buff *skb) -{ - skb->network_header = skb->data; -} - -static inline void skb_set_network_header(struct sk_buff *skb, const int offset) -{ - skb->network_header = skb->data + offset; -} - -static inline unsigned char *skb_mac_header(const struct sk_buff *skb) -{ - return skb->mac_header; -} - -static inline int skb_mac_header_was_set(const struct sk_buff *skb) -{ - return skb->mac_header != NULL; -} - -static inline void skb_reset_mac_header(struct sk_buff *skb) -{ - skb->mac_header = skb->data; -} - -static inline void skb_set_mac_header(struct sk_buff *skb, const int offset) -{ - skb->mac_header = skb->data + offset; -} -#endif /* NET_SKBUFF_DATA_USES_OFFSET */ - static inline void skb_probe_transport_header(struct sk_buff *skb, const int offset_hint) { -- cgit v1.2.3 From 0d89d2035fe063461a5ddb609b2c12e7fb006e44 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Thu, 23 May 2013 21:02:52 +0000 Subject: MPLS: Add limited GSO support In the case where a non-MPLS packet is received and an MPLS stack is added it may well be the case that the original skb is GSO but the NIC used for transmit does not support GSO of MPLS packets. The aim of this code is to provide GSO in software for MPLS packets whose skbs are GSO. SKB Usage: When an implementation adds an MPLS stack to a non-MPLS packet it should do the following to skb metadata: * Set skb->inner_protocol to the old non-MPLS ethertype of the packet. skb->inner_protocol is added by this patch. * Set skb->protocol to the new MPLS ethertype of the packet. * Set skb->network_header to correspond to the end of the L3 header, including the MPLS label stack. I have posted a patch, "[PATCH v3.29] datapath: Add basic MPLS support to kernel" which adds MPLS support to the kernel datapath of Open vSwtich. That patch sets the above requirements in datapath/actions.c:push_mpls() and was used to exercise this code. The datapath patch is against the Open vSwtich tree but it is intended that it be added to the Open vSwtich code present in the mainline Linux kernel at some point. Features: I believe that the approach that I have taken is at least partially consistent with the handling of other protocols. Jesse, I understand that you have some ideas here. I am more than happy to change my implementation. This patch adds dev->mpls_features which may be used by devices to advertise features supported for MPLS packets. A new NETIF_F_MPLS_GSO feature is added for devices which support hardware MPLS GSO offload. Currently no devices support this and MPLS GSO always falls back to software. Alternate Implementation: One possible alternate implementation is to teach netif_skb_features() and skb_network_protocol() about MPLS, in a similar way to their understanding of VLANs. I believe this would avoid the need for net/mpls/mpls_gso.c and in particular the calls to __skb_push() and __skb_push() in mpls_gso_segment(). I have decided on the implementation in this patch as it should not introduce any overhead in the case where mpls_gso is not compiled into the kernel or inserted as a module. MPLS GSO suggested by Jesse Gross. Based in part on "v4 GRE: Add TCP segmentation offload for GRE" by Pravin B Shelar. Cc: Jesse Gross Cc: Pravin B Shelar Signed-off-by: Simon Horman Signed-off-by: David S. Miller --- include/linux/netdev_features.h | 4 +- include/linux/netdevice.h | 2 + include/linux/skbuff.h | 4 ++ net/Kconfig | 1 + net/Makefile | 1 + net/core/dev.c | 4 ++ net/core/ethtool.c | 1 + net/ipv4/af_inet.c | 1 + net/ipv4/tcp.c | 1 + net/ipv4/udp.c | 2 +- net/ipv6/ip6_offload.c | 1 + net/ipv6/udp_offload.c | 3 +- net/mpls/Kconfig | 9 ++++ net/mpls/Makefile | 4 ++ net/mpls/mpls_gso.c | 108 ++++++++++++++++++++++++++++++++++++++++ 15 files changed, 143 insertions(+), 3 deletions(-) create mode 100644 net/mpls/Kconfig create mode 100644 net/mpls/Makefile create mode 100644 net/mpls/mpls_gso.c diff --git a/include/linux/netdev_features.h b/include/linux/netdev_features.h index 09906b7ca47d..a2a89a5c7be5 100644 --- a/include/linux/netdev_features.h +++ b/include/linux/netdev_features.h @@ -43,8 +43,9 @@ enum { NETIF_F_FSO_BIT, /* ... FCoE segmentation */ NETIF_F_GSO_GRE_BIT, /* ... GRE with TSO */ NETIF_F_GSO_UDP_TUNNEL_BIT, /* ... UDP TUNNEL with TSO */ + NETIF_F_GSO_MPLS_BIT, /* ... MPLS segmentation */ /**/NETIF_F_GSO_LAST = /* last bit, see GSO_MASK */ - NETIF_F_GSO_UDP_TUNNEL_BIT, + NETIF_F_GSO_MPLS_BIT, NETIF_F_FCOE_CRC_BIT, /* FCoE CRC32 */ NETIF_F_SCTP_CSUM_BIT, /* SCTP checksum offload */ @@ -107,6 +108,7 @@ enum { #define NETIF_F_RXALL __NETIF_F(RXALL) #define NETIF_F_GSO_GRE __NETIF_F(GSO_GRE) #define NETIF_F_GSO_UDP_TUNNEL __NETIF_F(GSO_UDP_TUNNEL) +#define NETIF_F_GSO_MPLS __NETIF_F(GSO_MPLS) #define NETIF_F_HW_VLAN_STAG_FILTER __NETIF_F(HW_VLAN_STAG_FILTER) #define NETIF_F_HW_VLAN_STAG_RX __NETIF_F(HW_VLAN_STAG_RX) #define NETIF_F_HW_VLAN_STAG_TX __NETIF_F(HW_VLAN_STAG_TX) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index ea7b6bce9ea0..6b2bb460d1d7 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1088,6 +1088,8 @@ struct net_device { * need to set them appropriately. */ netdev_features_t hw_enc_features; + /* mask of fetures inheritable by MPLS */ + netdev_features_t mpls_features; /* Interface index. Unique device identifier */ int ifindex; diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 5663e3592784..8f2b830772a8 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -319,6 +319,8 @@ enum { SKB_GSO_GRE = 1 << 6, SKB_GSO_UDP_TUNNEL = 1 << 7, + + SKB_GSO_MPLS = 1 << 8, }; #if BITS_PER_LONG > 32 @@ -389,6 +391,7 @@ typedef unsigned char *sk_buff_data_t; * @dropcount: total number of sk_receive_queue overflows * @vlan_proto: vlan encapsulation protocol * @vlan_tci: vlan tag control information + * @inner_protocol: Protocol (encapsulation) * @inner_transport_header: Inner transport layer header (encapsulation) * @inner_network_header: Network layer header (encapsulation) * @inner_mac_header: Link layer header (encapsulation) @@ -509,6 +512,7 @@ struct sk_buff { __u32 reserved_tailroom; }; + __be16 inner_protocol; __u16 inner_transport_header; __u16 inner_network_header; __u16 inner_mac_header; diff --git a/net/Kconfig b/net/Kconfig index 08de901415ee..523e43e6da1b 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -218,6 +218,7 @@ source "net/batman-adv/Kconfig" source "net/openvswitch/Kconfig" source "net/vmw_vsock/Kconfig" source "net/netlink/Kconfig" +source "net/mpls/Kconfig" config RPS boolean diff --git a/net/Makefile b/net/Makefile index 091e7b04f301..9492e8cb64e9 100644 --- a/net/Makefile +++ b/net/Makefile @@ -70,3 +70,4 @@ obj-$(CONFIG_BATMAN_ADV) += batman-adv/ obj-$(CONFIG_NFC) += nfc/ obj-$(CONFIG_OPENVSWITCH) += openvswitch/ obj-$(CONFIG_VSOCKETS) += vmw_vsock/ +obj-$(CONFIG_NET_MPLS_GSO) += mpls/ diff --git a/net/core/dev.c b/net/core/dev.c index 50c02ded1d69..2f09cb29cc95 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5277,6 +5277,10 @@ int register_netdevice(struct net_device *dev) */ dev->hw_enc_features |= NETIF_F_SG; + /* Make NETIF_F_SG inheritable to MPLS. + */ + dev->mpls_features |= NETIF_F_SG; + ret = call_netdevice_notifiers(NETDEV_POST_INIT, dev); ret = notifier_to_errno(ret); if (ret) diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 22efdaa76ebf..4e6f63ade741 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -82,6 +82,7 @@ static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN] [NETIF_F_FSO_BIT] = "tx-fcoe-segmentation", [NETIF_F_GSO_GRE_BIT] = "tx-gre-segmentation", [NETIF_F_GSO_UDP_TUNNEL_BIT] = "tx-udp_tnl-segmentation", + [NETIF_F_GSO_MPLS_BIT] = "tx-mpls-segmentation", [NETIF_F_FCOE_CRC_BIT] = "tx-checksum-fcoe-crc", [NETIF_F_SCTP_CSUM_BIT] = "tx-checksum-sctp", diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index d01be2a3ae53..b05ae96aec44 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1295,6 +1295,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, SKB_GSO_GRE | SKB_GSO_TCPV6 | SKB_GSO_UDP_TUNNEL | + SKB_GSO_MPLS | 0))) goto out; diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index d87ce72ca8aa..ba4186e1dca9 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2917,6 +2917,7 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb, SKB_GSO_TCP_ECN | SKB_GSO_TCPV6 | SKB_GSO_GRE | + SKB_GSO_MPLS | SKB_GSO_UDP_TUNNEL | 0) || !(type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)))) diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 0bf5d399a03c..aa5eff46d137 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -2381,7 +2381,7 @@ struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY | SKB_GSO_UDP_TUNNEL | - SKB_GSO_GRE) || + SKB_GSO_GRE | SKB_GSO_MPLS) || !(type & (SKB_GSO_UDP)))) goto out; diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c index 71b766ee821d..a263b990ee11 100644 --- a/net/ipv6/ip6_offload.c +++ b/net/ipv6/ip6_offload.c @@ -98,6 +98,7 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, SKB_GSO_TCP_ECN | SKB_GSO_GRE | SKB_GSO_UDP_TUNNEL | + SKB_GSO_MPLS | SKB_GSO_TCPV6 | 0))) goto out; diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c index 3bb3a891a424..76d401a93c7a 100644 --- a/net/ipv6/udp_offload.c +++ b/net/ipv6/udp_offload.c @@ -63,7 +63,8 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb, if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY | SKB_GSO_UDP_TUNNEL | - SKB_GSO_GRE) || + SKB_GSO_GRE | + SKB_GSO_MPLS) || !(type & (SKB_GSO_UDP)))) goto out; diff --git a/net/mpls/Kconfig b/net/mpls/Kconfig new file mode 100644 index 000000000000..37421db88965 --- /dev/null +++ b/net/mpls/Kconfig @@ -0,0 +1,9 @@ +# +# MPLS configuration +# +config NET_MPLS_GSO + tristate "MPLS: GSO support" + help + This is helper module to allow segmentation of non-MPLS GSO packets + that have had MPLS stack entries pushed onto them and thus + become MPLS GSO packets. diff --git a/net/mpls/Makefile b/net/mpls/Makefile new file mode 100644 index 000000000000..0a3c171be537 --- /dev/null +++ b/net/mpls/Makefile @@ -0,0 +1,4 @@ +# +# Makefile for MPLS. +# +obj-y += mpls_gso.o diff --git a/net/mpls/mpls_gso.c b/net/mpls/mpls_gso.c new file mode 100644 index 000000000000..1bec1219ab81 --- /dev/null +++ b/net/mpls/mpls_gso.c @@ -0,0 +1,108 @@ +/* + * MPLS GSO Support + * + * Authors: Simon Horman (horms@verge.net.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. + * + * Based on: GSO portions of net/ipv4/gre.c + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include + +static struct sk_buff *mpls_gso_segment(struct sk_buff *skb, + netdev_features_t features) +{ + struct sk_buff *segs = ERR_PTR(-EINVAL); + netdev_features_t mpls_features; + __be16 mpls_protocol; + + if (unlikely(skb_shinfo(skb)->gso_type & + ~(SKB_GSO_TCPV4 | + SKB_GSO_TCPV6 | + SKB_GSO_UDP | + SKB_GSO_DODGY | + SKB_GSO_TCP_ECN | + SKB_GSO_GRE | + SKB_GSO_MPLS))) + goto out; + + /* Setup inner SKB. */ + mpls_protocol = skb->protocol; + skb->protocol = skb->inner_protocol; + + /* Push back the mac header that skb_mac_gso_segment() has pulled. + * It will be re-pulled by the call to skb_mac_gso_segment() below + */ + __skb_push(skb, skb->mac_len); + + /* Segment inner packet. */ + mpls_features = skb->dev->mpls_features & netif_skb_features(skb); + segs = skb_mac_gso_segment(skb, mpls_features); + + + /* Restore outer protocol. */ + skb->protocol = mpls_protocol; + + /* Re-pull the mac header that the call to skb_mac_gso_segment() + * above pulled. It will be re-pushed after returning + * skb_mac_gso_segment(), an indirect caller of this function. + */ + __skb_push(skb, skb->data - skb_mac_header(skb)); + +out: + return segs; +} + +static int mpls_gso_send_check(struct sk_buff *skb) +{ + return 0; +} + +static struct packet_offload mpls_mc_offload = { + .type = cpu_to_be16(ETH_P_MPLS_MC), + .callbacks = { + .gso_send_check = mpls_gso_send_check, + .gso_segment = mpls_gso_segment, + }, +}; + +static struct packet_offload mpls_uc_offload = { + .type = cpu_to_be16(ETH_P_MPLS_UC), + .callbacks = { + .gso_send_check = mpls_gso_send_check, + .gso_segment = mpls_gso_segment, + }, +}; + +static int __init mpls_gso_init(void) +{ + pr_info("MPLS GSO support\n"); + + dev_add_offload(&mpls_uc_offload); + dev_add_offload(&mpls_mc_offload); + + return 0; +} + +static void __exit mpls_gso_exit(void) +{ + dev_remove_offload(&mpls_uc_offload); + dev_remove_offload(&mpls_mc_offload); +} + +module_init(mpls_gso_init); +module_exit(mpls_gso_exit); + +MODULE_DESCRIPTION("MPLS GSO support"); +MODULE_AUTHOR("Simon Horman (horms@verge.net.au)"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 87227b8b2d4d556a6924ad9af87450fdc3fcd7e3 Mon Sep 17 00:00:00 2001 From: Jean-Christophe PLAGNIOL-VILLARD Date: Thu, 23 May 2013 23:01:22 +0000 Subject: net: micrel : ks8851-ml: add dt support Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Cc: netdev@vger.kernel.org Signed-off-by: David S. Miller --- .../devicetree/bindings/net/micrel-ks8851.txt | 9 ++++++ drivers/net/ethernet/micrel/ks8851_mll.c | 33 +++++++++++++++++----- 2 files changed, 35 insertions(+), 7 deletions(-) create mode 100644 Documentation/devicetree/bindings/net/micrel-ks8851.txt diff --git a/Documentation/devicetree/bindings/net/micrel-ks8851.txt b/Documentation/devicetree/bindings/net/micrel-ks8851.txt new file mode 100644 index 000000000000..11ace3c3d805 --- /dev/null +++ b/Documentation/devicetree/bindings/net/micrel-ks8851.txt @@ -0,0 +1,9 @@ +Micrel KS8851 Ethernet mac + +Required properties: +- compatible = "micrel,ks8851-ml" of parallel interface +- reg : 2 physical address and size of registers for data and command +- interrupts : interrupt connection + +Optional properties: +- local-mac-address : Ethernet mac address to use diff --git a/drivers/net/ethernet/micrel/ks8851_mll.c b/drivers/net/ethernet/micrel/ks8851_mll.c index e9b1a830d582..ac20098b542a 100644 --- a/drivers/net/ethernet/micrel/ks8851_mll.c +++ b/drivers/net/ethernet/micrel/ks8851_mll.c @@ -35,6 +35,9 @@ #include #include #include +#include +#include +#include #define DRV_NAME "ks8851_mll" @@ -1524,6 +1527,13 @@ static int ks_hw_init(struct ks_net *ks) return true; } +#if defined(CONFIG_OF) +static const struct of_device_id ks8851_ml_dt_ids[] = { + { .compatible = "micrel,ks8851-mll" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ks8851_ml_dt_ids); +#endif static int ks8851_probe(struct platform_device *pdev) { @@ -1532,7 +1542,7 @@ static int ks8851_probe(struct platform_device *pdev) struct net_device *netdev; struct ks_net *ks; u16 id, data; - struct ks8851_mll_platform_data *pdata; + const char *mac; io_d = platform_get_resource(pdev, IORESOURCE_MEM, 0); io_c = platform_get_resource(pdev, IORESOURCE_MEM, 1); @@ -1619,13 +1629,21 @@ static int ks8851_probe(struct platform_device *pdev) ks_wrreg16(ks, KS_OBCR, data | OBCR_ODS_16MA); /* overwriting the default MAC address */ - pdata = pdev->dev.platform_data; - if (!pdata) { - netdev_err(netdev, "No platform data\n"); - err = -ENODEV; - goto err_pdata; + if (pdev->dev.of_node) { + mac = of_get_mac_address(pdev->dev.of_node); + if (mac) + memcpy(ks->mac_addr, mac, ETH_ALEN); + } else { + struct ks8851_mll_platform_data *pdata; + + pdata = pdev->dev.platform_data; + if (!pdata) { + netdev_err(netdev, "No platform data\n"); + err = -ENODEV; + goto err_pdata; + } + memcpy(ks->mac_addr, pdata->mac_addr, ETH_ALEN); } - memcpy(ks->mac_addr, pdata->mac_addr, 6); if (!is_valid_ether_addr(ks->mac_addr)) { /* Use random MAC address if none passed */ eth_random_addr(ks->mac_addr); @@ -1679,6 +1697,7 @@ static struct platform_driver ks8851_platform_driver = { .driver = { .name = DRV_NAME, .owner = THIS_MODULE, + .of_match_table = of_match_ptr(ks8851_ml_dt_ids), }, .probe = ks8851_probe, .remove = ks8851_remove, -- cgit v1.2.3 From da6e378ba918cd0feeb90eeb84d8b42148bb0c82 Mon Sep 17 00:00:00 2001 From: dingtianhong Date: Mon, 27 May 2013 19:53:31 +0000 Subject: netpoll: remove return value from netpoll_rx_disable() The netpoll_rx_disable() will always return 0, it is no use and looks wordy, so remove the unnecessary code and get rid of it in _dev_open and _dev_close. Signed-off-by: Ding Tianhong Signed-off-by: David S. Miller --- include/linux/netpoll.h | 4 ++-- net/core/dev.c | 15 ++++----------- net/core/netpoll.c | 3 +-- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h index fa2cb76a7029..f3c7c24bec1c 100644 --- a/include/linux/netpoll.h +++ b/include/linux/netpoll.h @@ -53,10 +53,10 @@ struct netpoll_info { }; #ifdef CONFIG_NETPOLL -extern int netpoll_rx_disable(struct net_device *dev); +extern void netpoll_rx_disable(struct net_device *dev); extern void netpoll_rx_enable(struct net_device *dev); #else -static inline int netpoll_rx_disable(struct net_device *dev) { return 0; } +static inline void netpoll_rx_disable(struct net_device *dev) { return; } static inline void netpoll_rx_enable(struct net_device *dev) { return; } #endif diff --git a/net/core/dev.c b/net/core/dev.c index 2f09cb29cc95..5f747974ac58 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1198,9 +1198,7 @@ static int __dev_open(struct net_device *dev) * If we don't do this there is a chance ndo_poll_controller * or ndo_poll may be running while we open the device */ - ret = netpoll_rx_disable(dev); - if (ret) - return ret; + netpoll_rx_disable(dev); ret = call_netdevice_notifiers(NETDEV_PRE_UP, dev); ret = notifier_to_errno(ret); @@ -1309,9 +1307,7 @@ static int __dev_close(struct net_device *dev) LIST_HEAD(single); /* Temporarily disable netpoll until the interface is down */ - retval = netpoll_rx_disable(dev); - if (retval) - return retval; + netpoll_rx_disable(dev); list_add(&dev->unreg_list, &single); retval = __dev_close_many(&single); @@ -1353,14 +1349,11 @@ static int dev_close_many(struct list_head *head) */ int dev_close(struct net_device *dev) { - int ret = 0; if (dev->flags & IFF_UP) { LIST_HEAD(single); /* Block netpoll rx while the interface is going down */ - ret = netpoll_rx_disable(dev); - if (ret) - return ret; + netpoll_rx_disable(dev); list_add(&dev->unreg_list, &single); dev_close_many(&single); @@ -1368,7 +1361,7 @@ int dev_close(struct net_device *dev) netpoll_rx_enable(dev); } - return ret; + return 0; } EXPORT_SYMBOL(dev_close); diff --git a/net/core/netpoll.c b/net/core/netpoll.c index cec074be8c43..37deedd48bcc 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -247,7 +247,7 @@ static void netpoll_poll_dev(struct net_device *dev) zap_completion_queue(); } -int netpoll_rx_disable(struct net_device *dev) +void netpoll_rx_disable(struct net_device *dev) { struct netpoll_info *ni; int idx; @@ -257,7 +257,6 @@ int netpoll_rx_disable(struct net_device *dev) if (ni) down(&ni->dev_lock); srcu_read_unlock(&netpoll_srcu, idx); - return 0; } EXPORT_SYMBOL(netpoll_rx_disable); -- cgit v1.2.3 From 1a9561a3bd0faaffa14dc20430805a46e311d00e Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 27 May 2013 01:51:39 +0000 Subject: net/phy: Use module_spi_driver in spi_ks8995.c module_spi_driver() removes some boilerplate and makes the code simpler. Signed-off-by: Sachin Kamat Signed-off-by: David S. Miller --- drivers/net/phy/spi_ks8995.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/net/phy/spi_ks8995.c b/drivers/net/phy/spi_ks8995.c index d11c93e69e03..f3bea1346021 100644 --- a/drivers/net/phy/spi_ks8995.c +++ b/drivers/net/phy/spi_ks8995.c @@ -354,19 +354,7 @@ static struct spi_driver ks8995_driver = { .remove = ks8995_remove, }; -static int __init ks8995_init(void) -{ - pr_info(DRV_DESC " version " DRV_VERSION "\n"); - - return spi_register_driver(&ks8995_driver); -} -module_init(ks8995_init); - -static void __exit ks8995_exit(void) -{ - spi_unregister_driver(&ks8995_driver); -} -module_exit(ks8995_exit); +module_spi_driver(ks8995_driver); MODULE_DESCRIPTION(DRV_DESC); MODULE_VERSION(DRV_VERSION); -- cgit v1.2.3 From 53edee2cfbcd869371cb720f1c00d85ba7f2566c Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Fri, 24 May 2013 00:59:47 +0000 Subject: bonding: allow xmit hash policy change while bond dev is up Since the xmit_hash_policy pointer is always valid and not dependent on anything, we can change it while the bond device is up and running. The only downside would be the out of order packets but that is a small price to pay. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- drivers/net/bonding/bond_sysfs.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c index d7434e0a610e..3d269a52789c 100644 --- a/drivers/net/bonding/bond_sysfs.c +++ b/drivers/net/bonding/bond_sysfs.c @@ -383,20 +383,12 @@ static ssize_t bonding_store_xmit_hash(struct device *d, int new_value, ret = count; struct bonding *bond = to_bond(d); - if (bond->dev->flags & IFF_UP) { - pr_err("%s: Interface is up. Unable to update xmit policy.\n", - bond->dev->name); - ret = -EPERM; - goto out; - } - new_value = bond_parse_parm(buf, xmit_hashtype_tbl); if (new_value < 0) { pr_err("%s: Ignoring invalid xmit hash policy value %.*s.\n", bond->dev->name, (int)strlen(buf) - 1, buf); ret = -EINVAL; - goto out; } else { bond->params.xmit_policy = new_value; bond_set_mode_ops(bond, bond->params.mode); @@ -404,7 +396,7 @@ static ssize_t bonding_store_xmit_hash(struct device *d, bond->dev->name, xmit_hashtype_tbl[new_value].modename, new_value); } -out: + return ret; } static DEVICE_ATTR(xmit_hash_policy, S_IRUGO | S_IWUSR, -- cgit v1.2.3 From 3dd17edea018bf37ca1c33685ca0256270ccdb2c Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Fri, 24 May 2013 07:05:59 +0000 Subject: doc:networking: Fix typo in documentation/networking Correct spelling typo Signed-off-by: Masanari Iida Signed-off-by: David S. Miller --- Documentation/networking/ifenslave.c | 2 +- Documentation/networking/ip-sysctl.txt | 2 +- Documentation/networking/netlink_mmap.txt | 14 +++++++------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Documentation/networking/ifenslave.c b/Documentation/networking/ifenslave.c index ac5debb2f16c..9f6a8fff9a78 100644 --- a/Documentation/networking/ifenslave.c +++ b/Documentation/networking/ifenslave.c @@ -501,7 +501,7 @@ out: static short mif_flags; -/* Get the inteface configuration from the kernel. */ +/* Get the interface configuration from the kernel. */ static int if_getconfig(char *ifname) { struct ifreq ifr; diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index f98ca633b528..398d0fb1dd0e 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -183,7 +183,7 @@ tcp_early_retrans - INTEGER for triggering fast retransmit when the amount of outstanding data is small and when no previously unsent data can be transmitted (such that limited transmit could be used). Also controls the use of - Tail loss probe (TLP) that converts RTOs occuring due to tail + Tail loss probe (TLP) that converts RTOs occurring due to tail losses into fast recovery (draft-dukkipati-tcpm-tcp-loss-probe-01). Possible values: 0 disables ER diff --git a/Documentation/networking/netlink_mmap.txt b/Documentation/networking/netlink_mmap.txt index 1c2dab409625..e6088baf109d 100644 --- a/Documentation/networking/netlink_mmap.txt +++ b/Documentation/networking/netlink_mmap.txt @@ -54,7 +54,7 @@ it will use an allocated socket buffer as usual and the contents will be copied to the ring on transmission, nullifying most of the performance gains. Dumps of kernel databases automatically support memory mapped I/O. -Conversion of the transmit path involves changing message contruction to +Conversion of the transmit path involves changing message construction to use memory from the TX ring instead of (usually) a buffer declared on the stack and setting up the frame header approriately. Optionally poll() can be used to wait for free frames in the TX ring. @@ -65,8 +65,8 @@ Structured and definitions for using memory mapped I/O are contained in RX and TX rings ---------------- -Each ring contains a number of continous memory blocks, containing frames of -fixed size dependant on the parameters used for ring setup. +Each ring contains a number of continuous memory blocks, containing frames of +fixed size dependent on the parameters used for ring setup. Ring: [ block 0 ] [ frame 0 ] @@ -80,7 +80,7 @@ Ring: [ block 0 ] [ frame 2 * n + 1 ] The blocks are only visible to the kernel, from the point of view of user-space -the ring just contains the frames in a continous memory zone. +the ring just contains the frames in a continuous memory zone. The ring parameters used for setting up the ring are defined as follows: @@ -91,7 +91,7 @@ struct nl_mmap_req { unsigned int nm_frame_nr; }; -Frames are grouped into blocks, where each block is a continous region of memory +Frames are grouped into blocks, where each block is a continuous region of memory and holds nm_block_size / nm_frame_size frames. The total number of frames in the ring is nm_frame_nr. The following invariants hold: @@ -113,8 +113,8 @@ Some parameters are constrained, specifically: - nm_frame_nr must equal the actual number of frames as specified above. -When the kernel can't allocate phsyically continous memory for a ring block, -it will fall back to use physically discontinous memory. This might affect +When the kernel can't allocate phsyically continuous memory for a ring block, +it will fall back to use physically discontinuous memory. This might affect performance negatively, in order to avoid this the nm_frame_size parameter should be chosen to be as small as possible for the required frame size and the number of blocks should be increased instead. -- cgit v1.2.3 From b1098bbe1b24d5d90cff92fbd716d2ef4bed2cff Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Mon, 27 May 2013 15:49:16 +0000 Subject: bonding: remove ifenslave.c from kernel source As Stephen proposed: Since bonding supports configuration via iproute (netlink) and sysfs, I think it is time to purge the old ifenslave code out of Documentation/networking and update the documentation. Suggested-by: Stephen Hemminger Cc: Stephen Hemminger Cc: Jay Vosburgh Cc: "David S. Miller" Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- Documentation/networking/.gitignore | 1 - Documentation/networking/00-INDEX | 2 - Documentation/networking/Makefile | 5 - Documentation/networking/bonding.txt | 54 +- Documentation/networking/ifenslave.c | 1105 ---------------------------------- 5 files changed, 10 insertions(+), 1157 deletions(-) delete mode 100644 Documentation/networking/ifenslave.c diff --git a/Documentation/networking/.gitignore b/Documentation/networking/.gitignore index 286a5680f490..e69de29bb2d1 100644 --- a/Documentation/networking/.gitignore +++ b/Documentation/networking/.gitignore @@ -1 +0,0 @@ -ifenslave diff --git a/Documentation/networking/00-INDEX b/Documentation/networking/00-INDEX index 258d9b92c36f..32dfbd924121 100644 --- a/Documentation/networking/00-INDEX +++ b/Documentation/networking/00-INDEX @@ -88,8 +88,6 @@ gianfar.txt - Gianfar Ethernet Driver. ieee802154.txt - Linux IEEE 802.15.4 implementation, API and drivers -ifenslave.c - - Configure network interfaces for parallel routing (bonding). igb.txt - README for the Intel Gigabit Ethernet Driver (igb). igbvf.txt diff --git a/Documentation/networking/Makefile b/Documentation/networking/Makefile index 24c308dd3fd1..0aa1ac98fc2b 100644 --- a/Documentation/networking/Makefile +++ b/Documentation/networking/Makefile @@ -1,11 +1,6 @@ # kbuild trick to avoid linker error. Can be omitted if a module is built. obj- := dummy.o -# List of programs to build -hostprogs-y := ifenslave - -HOSTCFLAGS_ifenslave.o += -I$(objtree)/usr/include - # Tell kbuild to always build the programs always := $(hostprogs-y) diff --git a/Documentation/networking/bonding.txt b/Documentation/networking/bonding.txt index 10a015c384b8..e7454fcc9176 100644 --- a/Documentation/networking/bonding.txt +++ b/Documentation/networking/bonding.txt @@ -104,8 +104,7 @@ Table of Contents ============================== Most popular distro kernels ship with the bonding driver -already available as a module and the ifenslave user level control -program installed and ready for use. If your distro does not, or you +already available as a module. If your distro does not, or you have need to compile bonding from source (e.g., configuring and installing a mainline kernel from kernel.org), you'll need to perform the following steps: @@ -124,46 +123,13 @@ device support" section. It is recommended that you configure the driver as module since it is currently the only way to pass parameters to the driver or configure more than one bonding device. - Build and install the new kernel and modules, then continue -below to install ifenslave. + Build and install the new kernel and modules. -1.2 Install ifenslave Control Utility +1.2 Bonding Control Utility ------------------------------------- - The ifenslave user level control program is included in the -kernel source tree, in the file Documentation/networking/ifenslave.c. -It is generally recommended that you use the ifenslave that -corresponds to the kernel that you are using (either from the same -source tree or supplied with the distro), however, ifenslave -executables from older kernels should function (but features newer -than the ifenslave release are not supported). Running an ifenslave -that is newer than the kernel is not supported, and may or may not -work. - - To install ifenslave, do the following: - -# gcc -Wall -O -I/usr/src/linux/include ifenslave.c -o ifenslave -# cp ifenslave /sbin/ifenslave - - If your kernel source is not in "/usr/src/linux," then replace -"/usr/src/linux/include" in the above with the location of your kernel -source include directory. - - You may wish to back up any existing /sbin/ifenslave, or, for -testing or informal use, tag the ifenslave to the kernel version -(e.g., name the ifenslave executable /sbin/ifenslave-2.6.10). - -IMPORTANT NOTE: - - If you omit the "-I" or specify an incorrect directory, you -may end up with an ifenslave that is incompatible with the kernel -you're trying to build it for. Some distros (e.g., Red Hat from 7.1 -onwards) do not have /usr/include/linux symbolically linked to the -default kernel source include directory. - -SECOND IMPORTANT NOTE: - If you plan to configure bonding using sysfs or using the -/etc/network/interfaces file, you do not need to use ifenslave. + It is recommended to configure bonding via iproute2 (netlink) +or sysfs, the old ifenslave control utility is obsolete. 2. Bonding Driver Options ========================= @@ -851,7 +817,7 @@ resend_igmp ============================== You can configure bonding using either your distro's network -initialization scripts, or manually using either ifenslave or the +initialization scripts, or manually using either iproute2 or the sysfs interface. Distros generally use one of three packages for the network initialization scripts: initscripts, sysconfig or interfaces. Recent versions of these packages have support for bonding, while older @@ -1160,7 +1126,7 @@ not support this method for specifying multiple bonding interfaces; for those instances, see the "Configuring Multiple Bonds Manually" section, below. -3.3 Configuring Bonding Manually with Ifenslave +3.3 Configuring Bonding Manually with iproute2 ----------------------------------------------- This section applies to distros whose network initialization @@ -1171,7 +1137,7 @@ version 8. The general method for these systems is to place the bonding module parameters into a config file in /etc/modprobe.d/ (as appropriate for the installed distro), then add modprobe and/or -ifenslave commands to the system's global init script. The name of +`ip link` commands to the system's global init script. The name of the global init script differs; for sysconfig, it is /etc/init.d/boot.local and for initscripts it is /etc/rc.d/rc.local. @@ -1183,8 +1149,8 @@ reboots, edit the appropriate file (/etc/init.d/boot.local or modprobe bonding mode=balance-alb miimon=100 modprobe e100 ifconfig bond0 192.168.1.1 netmask 255.255.255.0 up -ifenslave bond0 eth0 -ifenslave bond0 eth1 +ip link set eth0 master bond0 +ip link set eth1 master bond0 Replace the example bonding module parameters and bond0 network configuration (IP address, netmask, etc) with the appropriate diff --git a/Documentation/networking/ifenslave.c b/Documentation/networking/ifenslave.c deleted file mode 100644 index 9f6a8fff9a78..000000000000 --- a/Documentation/networking/ifenslave.c +++ /dev/null @@ -1,1105 +0,0 @@ -/* Mode: C; - * ifenslave.c: Configure network interfaces for parallel routing. - * - * This program controls the Linux implementation of running multiple - * network interfaces in parallel. - * - * Author: Donald Becker - * Copyright 1994-1996 Donald Becker - * - * 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. - * - * The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O - * Center of Excellence in Space Data and Information Sciences - * Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 - * - * Changes : - * - 2000/10/02 Willy Tarreau : - * - few fixes. Master's MAC address is now correctly taken from - * the first device when not previously set ; - * - detach support : call BOND_RELEASE to detach an enslaved interface. - * - give a mini-howto from command-line help : # ifenslave -h - * - * - 2001/02/16 Chad N. Tindel : - * - Master is now brought down before setting the MAC address. In - * the 2.4 kernel you can't change the MAC address while the device is - * up because you get EBUSY. - * - * - 2001/09/13 Takao Indoh - * - Added the ability to change the active interface on a mode 1 bond - * at runtime. - * - * - 2001/10/23 Chad N. Tindel : - * - No longer set the MAC address of the master. The bond device will - * take care of this itself - * - Try the SIOC*** versions of the bonding ioctls before using the - * old versions - * - 2002/02/18 Erik Habbinga : - * - ifr2.ifr_flags was not initialized in the hwaddr_notset case, - * SIOCGIFFLAGS now called before hwaddr_notset test - * - * - 2002/10/31 Tony Cureington : - * - If the master does not have a hardware address when the first slave - * is enslaved, the master is assigned the hardware address of that - * slave - there is a comment in bonding.c stating "ifenslave takes - * care of this now." This corrects the problem of slaves having - * different hardware addresses in active-backup mode when - * multiple interfaces are specified on a single ifenslave command - * (ifenslave bond0 eth0 eth1). - * - * - 2003/03/18 - Tsippy Mendelson and - * Shmulik Hen - * - Moved setting the slave's mac address and openning it, from - * the application to the driver. This enables support of modes - * that need to use the unique mac address of each slave. - * The driver also takes care of closing the slave and restoring its - * original mac address upon release. - * In addition, block possibility of enslaving before the master is up. - * This prevents putting the system in an undefined state. - * - * - 2003/05/01 - Amir Noam - * - Added ABI version control to restore compatibility between - * new/old ifenslave and new/old bonding. - * - Prevent adding an adapter that is already a slave. - * Fixes the problem of stalling the transmission and leaving - * the slave in a down state. - * - * - 2003/05/01 - Shmulik Hen - * - Prevent enslaving if the bond device is down. - * Fixes the problem of leaving the system in unstable state and - * halting when trying to remove the module. - * - Close socket on all abnormal exists. - * - Add versioning scheme that follows that of the bonding driver. - * current version is 1.0.0 as a base line. - * - * - 2003/05/22 - Jay Vosburgh - * - ifenslave -c was broken; it's now fixed - * - Fixed problem with routes vanishing from master during enslave - * processing. - * - * - 2003/05/27 - Amir Noam - * - Fix backward compatibility issues: - * For drivers not using ABI versions, slave was set down while - * it should be left up before enslaving. - * Also, master was not set down and the default set_mac_address() - * would fail and generate an error message in the system log. - * - For opt_c: slave should not be set to the master's setting - * while it is running. It was already set during enslave. To - * simplify things, it is now handled separately. - * - * - 2003/12/01 - Shmulik Hen - * - Code cleanup and style changes - * set version to 1.1.0 - */ - -#define APP_VERSION "1.1.0" -#define APP_RELDATE "December 1, 2003" -#define APP_NAME "ifenslave" - -static char *version = -APP_NAME ".c:v" APP_VERSION " (" APP_RELDATE ")\n" -"o Donald Becker (becker@cesdis.gsfc.nasa.gov).\n" -"o Detach support added on 2000/10/02 by Willy Tarreau (willy at meta-x.org).\n" -"o 2.4 kernel support added on 2001/02/16 by Chad N. Tindel\n" -" (ctindel at ieee dot org).\n"; - -static const char *usage_msg = -"Usage: ifenslave [-f] [...]\n" -" ifenslave -d [...]\n" -" ifenslave -c \n" -" ifenslave --help\n"; - -static const char *help_msg = -"\n" -" To create a bond device, simply follow these three steps :\n" -" - ensure that the required drivers are properly loaded :\n" -" # modprobe bonding ; modprobe <3c59x|eepro100|pcnet32|tulip|...>\n" -" - assign an IP address to the bond device :\n" -" # ifconfig bond0 netmask broadcast \n" -" - attach all the interfaces you need to the bond device :\n" -" # ifenslave [{-f|--force}] bond0 eth0 [eth1 [eth2]...]\n" -" If bond0 didn't have a MAC address, it will take eth0's. Then, all\n" -" interfaces attached AFTER this assignment will get the same MAC addr.\n" -" (except for ALB/TLB modes)\n" -"\n" -" To set the bond device down and automatically release all the slaves :\n" -" # ifconfig bond0 down\n" -"\n" -" To detach a dead interface without setting the bond device down :\n" -" # ifenslave {-d|--detach} bond0 eth0 [eth1 [eth2]...]\n" -"\n" -" To change active slave :\n" -" # ifenslave {-c|--change-active} bond0 eth0\n" -"\n" -" To show master interface info\n" -" # ifenslave bond0\n" -"\n" -" To show all interfaces info\n" -" # ifenslave {-a|--all-interfaces}\n" -"\n" -" To be more verbose\n" -" # ifenslave {-v|--verbose} ...\n" -"\n" -" # ifenslave {-u|--usage} Show usage\n" -" # ifenslave {-V|--version} Show version\n" -" # ifenslave {-h|--help} This message\n" -"\n"; - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -typedef unsigned long long u64; /* hack, so we may include kernel's ethtool.h */ -typedef __uint32_t u32; /* ditto */ -typedef __uint16_t u16; /* ditto */ -typedef __uint8_t u8; /* ditto */ -#include - -struct option longopts[] = { - /* { name has_arg *flag val } */ - {"all-interfaces", 0, 0, 'a'}, /* Show all interfaces. */ - {"change-active", 0, 0, 'c'}, /* Change the active slave. */ - {"detach", 0, 0, 'd'}, /* Detach a slave interface. */ - {"force", 0, 0, 'f'}, /* Force the operation. */ - {"help", 0, 0, 'h'}, /* Give help */ - {"usage", 0, 0, 'u'}, /* Give usage */ - {"verbose", 0, 0, 'v'}, /* Report each action taken. */ - {"version", 0, 0, 'V'}, /* Emit version information. */ - { 0, 0, 0, 0} -}; - -/* Command-line flags. */ -unsigned int -opt_a = 0, /* Show-all-interfaces flag. */ -opt_c = 0, /* Change-active-slave flag. */ -opt_d = 0, /* Detach a slave interface. */ -opt_f = 0, /* Force the operation. */ -opt_h = 0, /* Help */ -opt_u = 0, /* Usage */ -opt_v = 0, /* Verbose flag. */ -opt_V = 0; /* Version */ - -int skfd = -1; /* AF_INET socket for ioctl() calls.*/ -int abi_ver = 0; /* userland - kernel ABI version */ -int hwaddr_set = 0; /* Master's hwaddr is set */ -int saved_errno; - -struct ifreq master_mtu, master_flags, master_hwaddr; -struct ifreq slave_mtu, slave_flags, slave_hwaddr; - -struct dev_ifr { - struct ifreq *req_ifr; - char *req_name; - int req_type; -}; - -struct dev_ifr master_ifra[] = { - {&master_mtu, "SIOCGIFMTU", SIOCGIFMTU}, - {&master_flags, "SIOCGIFFLAGS", SIOCGIFFLAGS}, - {&master_hwaddr, "SIOCGIFHWADDR", SIOCGIFHWADDR}, - {NULL, "", 0} -}; - -struct dev_ifr slave_ifra[] = { - {&slave_mtu, "SIOCGIFMTU", SIOCGIFMTU}, - {&slave_flags, "SIOCGIFFLAGS", SIOCGIFFLAGS}, - {&slave_hwaddr, "SIOCGIFHWADDR", SIOCGIFHWADDR}, - {NULL, "", 0} -}; - -static void if_print(char *ifname); -static int get_drv_info(char *master_ifname); -static int get_if_settings(char *ifname, struct dev_ifr ifra[]); -static int get_slave_flags(char *slave_ifname); -static int set_master_hwaddr(char *master_ifname, struct sockaddr *hwaddr); -static int set_slave_hwaddr(char *slave_ifname, struct sockaddr *hwaddr); -static int set_slave_mtu(char *slave_ifname, int mtu); -static int set_if_flags(char *ifname, short flags); -static int set_if_up(char *ifname, short flags); -static int set_if_down(char *ifname, short flags); -static int clear_if_addr(char *ifname); -static int set_if_addr(char *master_ifname, char *slave_ifname); -static int change_active(char *master_ifname, char *slave_ifname); -static int enslave(char *master_ifname, char *slave_ifname); -static int release(char *master_ifname, char *slave_ifname); -#define v_print(fmt, args...) \ - if (opt_v) \ - fprintf(stderr, fmt, ## args ) - -int main(int argc, char *argv[]) -{ - char **spp, *master_ifname, *slave_ifname; - int c, i, rv; - int res = 0; - int exclusive = 0; - - while ((c = getopt_long(argc, argv, "acdfhuvV", longopts, 0)) != EOF) { - switch (c) { - case 'a': opt_a++; exclusive++; break; - case 'c': opt_c++; exclusive++; break; - case 'd': opt_d++; exclusive++; break; - case 'f': opt_f++; exclusive++; break; - case 'h': opt_h++; exclusive++; break; - case 'u': opt_u++; exclusive++; break; - case 'v': opt_v++; break; - case 'V': opt_V++; exclusive++; break; - - case '?': - fprintf(stderr, "%s", usage_msg); - res = 2; - goto out; - } - } - - /* options check */ - if (exclusive > 1) { - fprintf(stderr, "%s", usage_msg); - res = 2; - goto out; - } - - if (opt_v || opt_V) { - printf("%s", version); - if (opt_V) { - res = 0; - goto out; - } - } - - if (opt_u) { - printf("%s", usage_msg); - res = 0; - goto out; - } - - if (opt_h) { - printf("%s", usage_msg); - printf("%s", help_msg); - res = 0; - goto out; - } - - /* Open a basic socket */ - if ((skfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { - perror("socket"); - res = 1; - goto out; - } - - if (opt_a) { - if (optind == argc) { - /* No remaining args */ - /* show all interfaces */ - if_print((char *)NULL); - goto out; - } else { - /* Just show usage */ - fprintf(stderr, "%s", usage_msg); - res = 2; - goto out; - } - } - - /* Copy the interface name */ - spp = argv + optind; - master_ifname = *spp++; - - if (master_ifname == NULL) { - fprintf(stderr, "%s", usage_msg); - res = 2; - goto out; - } - - /* exchange abi version with bonding module */ - res = get_drv_info(master_ifname); - if (res) { - fprintf(stderr, - "Master '%s': Error: handshake with driver failed. " - "Aborting\n", - master_ifname); - goto out; - } - - slave_ifname = *spp++; - - if (slave_ifname == NULL) { - if (opt_d || opt_c) { - fprintf(stderr, "%s", usage_msg); - res = 2; - goto out; - } - - /* A single arg means show the - * configuration for this interface - */ - if_print(master_ifname); - goto out; - } - - res = get_if_settings(master_ifname, master_ifra); - if (res) { - /* Probably a good reason not to go on */ - fprintf(stderr, - "Master '%s': Error: get settings failed: %s. " - "Aborting\n", - master_ifname, strerror(res)); - goto out; - } - - /* check if master is indeed a master; - * if not then fail any operation - */ - if (!(master_flags.ifr_flags & IFF_MASTER)) { - fprintf(stderr, - "Illegal operation; the specified interface '%s' " - "is not a master. Aborting\n", - master_ifname); - res = 1; - goto out; - } - - /* check if master is up; if not then fail any operation */ - if (!(master_flags.ifr_flags & IFF_UP)) { - fprintf(stderr, - "Illegal operation; the specified master interface " - "'%s' is not up.\n", - master_ifname); - res = 1; - goto out; - } - - /* Only for enslaving */ - if (!opt_c && !opt_d) { - sa_family_t master_family = master_hwaddr.ifr_hwaddr.sa_family; - unsigned char *hwaddr = - (unsigned char *)master_hwaddr.ifr_hwaddr.sa_data; - - /* The family '1' is ARPHRD_ETHER for ethernet. */ - if (master_family != 1 && !opt_f) { - fprintf(stderr, - "Illegal operation: The specified master " - "interface '%s' is not ethernet-like.\n " - "This program is designed to work with " - "ethernet-like network interfaces.\n " - "Use the '-f' option to force the " - "operation.\n", - master_ifname); - res = 1; - goto out; - } - - /* Check master's hw addr */ - for (i = 0; i < 6; i++) { - if (hwaddr[i] != 0) { - hwaddr_set = 1; - break; - } - } - - if (hwaddr_set) { - v_print("current hardware address of master '%s' " - "is %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, " - "type %d\n", - master_ifname, - hwaddr[0], hwaddr[1], - hwaddr[2], hwaddr[3], - hwaddr[4], hwaddr[5], - master_family); - } - } - - /* Accepts only one slave */ - if (opt_c) { - /* change active slave */ - res = get_slave_flags(slave_ifname); - if (res) { - fprintf(stderr, - "Slave '%s': Error: get flags failed. " - "Aborting\n", - slave_ifname); - goto out; - } - res = change_active(master_ifname, slave_ifname); - if (res) { - fprintf(stderr, - "Master '%s', Slave '%s': Error: " - "Change active failed\n", - master_ifname, slave_ifname); - } - } else { - /* Accept multiple slaves */ - do { - if (opt_d) { - /* detach a slave interface from the master */ - rv = get_slave_flags(slave_ifname); - if (rv) { - /* Can't work with this slave. */ - /* remember the error and skip it*/ - fprintf(stderr, - "Slave '%s': Error: get flags " - "failed. Skipping\n", - slave_ifname); - res = rv; - continue; - } - rv = release(master_ifname, slave_ifname); - if (rv) { - fprintf(stderr, - "Master '%s', Slave '%s': Error: " - "Release failed\n", - master_ifname, slave_ifname); - res = rv; - } - } else { - /* attach a slave interface to the master */ - rv = get_if_settings(slave_ifname, slave_ifra); - if (rv) { - /* Can't work with this slave. */ - /* remember the error and skip it*/ - fprintf(stderr, - "Slave '%s': Error: get " - "settings failed: %s. " - "Skipping\n", - slave_ifname, strerror(rv)); - res = rv; - continue; - } - rv = enslave(master_ifname, slave_ifname); - if (rv) { - fprintf(stderr, - "Master '%s', Slave '%s': Error: " - "Enslave failed\n", - master_ifname, slave_ifname); - res = rv; - } - } - } while ((slave_ifname = *spp++) != NULL); - } - -out: - if (skfd >= 0) { - close(skfd); - } - - return res; -} - -static short mif_flags; - -/* Get the interface configuration from the kernel. */ -static int if_getconfig(char *ifname) -{ - struct ifreq ifr; - int metric, mtu; /* Parameters of the master interface. */ - struct sockaddr dstaddr, broadaddr, netmask; - unsigned char *hwaddr; - - strcpy(ifr.ifr_name, ifname); - if (ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0) - return -1; - mif_flags = ifr.ifr_flags; - printf("The result of SIOCGIFFLAGS on %s is %x.\n", - ifname, ifr.ifr_flags); - - strcpy(ifr.ifr_name, ifname); - if (ioctl(skfd, SIOCGIFADDR, &ifr) < 0) - return -1; - printf("The result of SIOCGIFADDR is %2.2x.%2.2x.%2.2x.%2.2x.\n", - ifr.ifr_addr.sa_data[0], ifr.ifr_addr.sa_data[1], - ifr.ifr_addr.sa_data[2], ifr.ifr_addr.sa_data[3]); - - strcpy(ifr.ifr_name, ifname); - if (ioctl(skfd, SIOCGIFHWADDR, &ifr) < 0) - return -1; - - /* Gotta convert from 'char' to unsigned for printf(). */ - hwaddr = (unsigned char *)ifr.ifr_hwaddr.sa_data; - printf("The result of SIOCGIFHWADDR is type %d " - "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x.\n", - ifr.ifr_hwaddr.sa_family, hwaddr[0], hwaddr[1], - hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5]); - - strcpy(ifr.ifr_name, ifname); - if (ioctl(skfd, SIOCGIFMETRIC, &ifr) < 0) { - metric = 0; - } else - metric = ifr.ifr_metric; - printf("The result of SIOCGIFMETRIC is %d\n", metric); - - strcpy(ifr.ifr_name, ifname); - if (ioctl(skfd, SIOCGIFMTU, &ifr) < 0) - mtu = 0; - else - mtu = ifr.ifr_mtu; - printf("The result of SIOCGIFMTU is %d\n", mtu); - - strcpy(ifr.ifr_name, ifname); - if (ioctl(skfd, SIOCGIFDSTADDR, &ifr) < 0) { - memset(&dstaddr, 0, sizeof(struct sockaddr)); - } else - dstaddr = ifr.ifr_dstaddr; - - strcpy(ifr.ifr_name, ifname); - if (ioctl(skfd, SIOCGIFBRDADDR, &ifr) < 0) { - memset(&broadaddr, 0, sizeof(struct sockaddr)); - } else - broadaddr = ifr.ifr_broadaddr; - - strcpy(ifr.ifr_name, ifname); - if (ioctl(skfd, SIOCGIFNETMASK, &ifr) < 0) { - memset(&netmask, 0, sizeof(struct sockaddr)); - } else - netmask = ifr.ifr_netmask; - - return 0; -} - -static void if_print(char *ifname) -{ - char buff[1024]; - struct ifconf ifc; - struct ifreq *ifr; - int i; - - if (ifname == (char *)NULL) { - ifc.ifc_len = sizeof(buff); - ifc.ifc_buf = buff; - if (ioctl(skfd, SIOCGIFCONF, &ifc) < 0) { - perror("SIOCGIFCONF failed"); - return; - } - - ifr = ifc.ifc_req; - for (i = ifc.ifc_len / sizeof(struct ifreq); --i >= 0; ifr++) { - if (if_getconfig(ifr->ifr_name) < 0) { - fprintf(stderr, - "%s: unknown interface.\n", - ifr->ifr_name); - continue; - } - - if (((mif_flags & IFF_UP) == 0) && !opt_a) continue; - /*ife_print(&ife);*/ - } - } else { - if (if_getconfig(ifname) < 0) { - fprintf(stderr, - "%s: unknown interface.\n", ifname); - } - } -} - -static int get_drv_info(char *master_ifname) -{ - struct ifreq ifr; - struct ethtool_drvinfo info; - char *endptr; - - memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); - ifr.ifr_data = (caddr_t)&info; - - info.cmd = ETHTOOL_GDRVINFO; - strncpy(info.driver, "ifenslave", 32); - snprintf(info.fw_version, 32, "%d", BOND_ABI_VERSION); - - if (ioctl(skfd, SIOCETHTOOL, &ifr) < 0) { - if (errno == EOPNOTSUPP) { - goto out; - } - - saved_errno = errno; - v_print("Master '%s': Error: get bonding info failed %s\n", - master_ifname, strerror(saved_errno)); - return 1; - } - - abi_ver = strtoul(info.fw_version, &endptr, 0); - if (*endptr) { - v_print("Master '%s': Error: got invalid string as an ABI " - "version from the bonding module\n", - master_ifname); - return 1; - } - -out: - v_print("ABI ver is %d\n", abi_ver); - - return 0; -} - -static int change_active(char *master_ifname, char *slave_ifname) -{ - struct ifreq ifr; - int res = 0; - - if (!(slave_flags.ifr_flags & IFF_SLAVE)) { - fprintf(stderr, - "Illegal operation: The specified slave interface " - "'%s' is not a slave\n", - slave_ifname); - return 1; - } - - strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); - strncpy(ifr.ifr_slave, slave_ifname, IFNAMSIZ); - if ((ioctl(skfd, SIOCBONDCHANGEACTIVE, &ifr) < 0) && - (ioctl(skfd, BOND_CHANGE_ACTIVE_OLD, &ifr) < 0)) { - saved_errno = errno; - v_print("Master '%s': Error: SIOCBONDCHANGEACTIVE failed: " - "%s\n", - master_ifname, strerror(saved_errno)); - res = 1; - } - - return res; -} - -static int enslave(char *master_ifname, char *slave_ifname) -{ - struct ifreq ifr; - int res = 0; - - if (slave_flags.ifr_flags & IFF_SLAVE) { - fprintf(stderr, - "Illegal operation: The specified slave interface " - "'%s' is already a slave\n", - slave_ifname); - return 1; - } - - res = set_if_down(slave_ifname, slave_flags.ifr_flags); - if (res) { - fprintf(stderr, - "Slave '%s': Error: bring interface down failed\n", - slave_ifname); - return res; - } - - if (abi_ver < 2) { - /* Older bonding versions would panic if the slave has no IP - * address, so get the IP setting from the master. - */ - set_if_addr(master_ifname, slave_ifname); - } else { - res = clear_if_addr(slave_ifname); - if (res) { - fprintf(stderr, - "Slave '%s': Error: clear address failed\n", - slave_ifname); - return res; - } - } - - if (master_mtu.ifr_mtu != slave_mtu.ifr_mtu) { - res = set_slave_mtu(slave_ifname, master_mtu.ifr_mtu); - if (res) { - fprintf(stderr, - "Slave '%s': Error: set MTU failed\n", - slave_ifname); - return res; - } - } - - if (hwaddr_set) { - /* Master already has an hwaddr - * so set it's hwaddr to the slave - */ - if (abi_ver < 1) { - /* The driver is using an old ABI, so - * the application sets the slave's - * hwaddr - */ - res = set_slave_hwaddr(slave_ifname, - &(master_hwaddr.ifr_hwaddr)); - if (res) { - fprintf(stderr, - "Slave '%s': Error: set hw address " - "failed\n", - slave_ifname); - goto undo_mtu; - } - - /* For old ABI the application needs to bring the - * slave back up - */ - res = set_if_up(slave_ifname, slave_flags.ifr_flags); - if (res) { - fprintf(stderr, - "Slave '%s': Error: bring interface " - "down failed\n", - slave_ifname); - goto undo_slave_mac; - } - } - /* The driver is using a new ABI, - * so the driver takes care of setting - * the slave's hwaddr and bringing - * it up again - */ - } else { - /* No hwaddr for master yet, so - * set the slave's hwaddr to it - */ - if (abi_ver < 1) { - /* For old ABI, the master needs to be - * down before setting its hwaddr - */ - res = set_if_down(master_ifname, master_flags.ifr_flags); - if (res) { - fprintf(stderr, - "Master '%s': Error: bring interface " - "down failed\n", - master_ifname); - goto undo_mtu; - } - } - - res = set_master_hwaddr(master_ifname, - &(slave_hwaddr.ifr_hwaddr)); - if (res) { - fprintf(stderr, - "Master '%s': Error: set hw address " - "failed\n", - master_ifname); - goto undo_mtu; - } - - if (abi_ver < 1) { - /* For old ABI, bring the master - * back up - */ - res = set_if_up(master_ifname, master_flags.ifr_flags); - if (res) { - fprintf(stderr, - "Master '%s': Error: bring interface " - "up failed\n", - master_ifname); - goto undo_master_mac; - } - } - - hwaddr_set = 1; - } - - /* Do the real thing */ - strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); - strncpy(ifr.ifr_slave, slave_ifname, IFNAMSIZ); - if ((ioctl(skfd, SIOCBONDENSLAVE, &ifr) < 0) && - (ioctl(skfd, BOND_ENSLAVE_OLD, &ifr) < 0)) { - saved_errno = errno; - v_print("Master '%s': Error: SIOCBONDENSLAVE failed: %s\n", - master_ifname, strerror(saved_errno)); - res = 1; - } - - if (res) { - goto undo_master_mac; - } - - return 0; - -/* rollback (best effort) */ -undo_master_mac: - set_master_hwaddr(master_ifname, &(master_hwaddr.ifr_hwaddr)); - hwaddr_set = 0; - goto undo_mtu; -undo_slave_mac: - set_slave_hwaddr(slave_ifname, &(slave_hwaddr.ifr_hwaddr)); -undo_mtu: - set_slave_mtu(slave_ifname, slave_mtu.ifr_mtu); - return res; -} - -static int release(char *master_ifname, char *slave_ifname) -{ - struct ifreq ifr; - int res = 0; - - if (!(slave_flags.ifr_flags & IFF_SLAVE)) { - fprintf(stderr, - "Illegal operation: The specified slave interface " - "'%s' is not a slave\n", - slave_ifname); - return 1; - } - - strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); - strncpy(ifr.ifr_slave, slave_ifname, IFNAMSIZ); - if ((ioctl(skfd, SIOCBONDRELEASE, &ifr) < 0) && - (ioctl(skfd, BOND_RELEASE_OLD, &ifr) < 0)) { - saved_errno = errno; - v_print("Master '%s': Error: SIOCBONDRELEASE failed: %s\n", - master_ifname, strerror(saved_errno)); - return 1; - } else if (abi_ver < 1) { - /* The driver is using an old ABI, so we'll set the interface - * down to avoid any conflicts due to same MAC/IP - */ - res = set_if_down(slave_ifname, slave_flags.ifr_flags); - if (res) { - fprintf(stderr, - "Slave '%s': Error: bring interface " - "down failed\n", - slave_ifname); - } - } - - /* set to default mtu */ - set_slave_mtu(slave_ifname, 1500); - - return res; -} - -static int get_if_settings(char *ifname, struct dev_ifr ifra[]) -{ - int i; - int res = 0; - - for (i = 0; ifra[i].req_ifr; i++) { - strncpy(ifra[i].req_ifr->ifr_name, ifname, IFNAMSIZ); - res = ioctl(skfd, ifra[i].req_type, ifra[i].req_ifr); - if (res < 0) { - saved_errno = errno; - v_print("Interface '%s': Error: %s failed: %s\n", - ifname, ifra[i].req_name, - strerror(saved_errno)); - - return saved_errno; - } - } - - return 0; -} - -static int get_slave_flags(char *slave_ifname) -{ - int res = 0; - - strncpy(slave_flags.ifr_name, slave_ifname, IFNAMSIZ); - res = ioctl(skfd, SIOCGIFFLAGS, &slave_flags); - if (res < 0) { - saved_errno = errno; - v_print("Slave '%s': Error: SIOCGIFFLAGS failed: %s\n", - slave_ifname, strerror(saved_errno)); - } else { - v_print("Slave %s: flags %04X.\n", - slave_ifname, slave_flags.ifr_flags); - } - - return res; -} - -static int set_master_hwaddr(char *master_ifname, struct sockaddr *hwaddr) -{ - unsigned char *addr = (unsigned char *)hwaddr->sa_data; - struct ifreq ifr; - int res = 0; - - strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); - memcpy(&(ifr.ifr_hwaddr), hwaddr, sizeof(struct sockaddr)); - res = ioctl(skfd, SIOCSIFHWADDR, &ifr); - if (res < 0) { - saved_errno = errno; - v_print("Master '%s': Error: SIOCSIFHWADDR failed: %s\n", - master_ifname, strerror(saved_errno)); - return res; - } else { - v_print("Master '%s': hardware address set to " - "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x.\n", - master_ifname, addr[0], addr[1], addr[2], - addr[3], addr[4], addr[5]); - } - - return res; -} - -static int set_slave_hwaddr(char *slave_ifname, struct sockaddr *hwaddr) -{ - unsigned char *addr = (unsigned char *)hwaddr->sa_data; - struct ifreq ifr; - int res = 0; - - strncpy(ifr.ifr_name, slave_ifname, IFNAMSIZ); - memcpy(&(ifr.ifr_hwaddr), hwaddr, sizeof(struct sockaddr)); - res = ioctl(skfd, SIOCSIFHWADDR, &ifr); - if (res < 0) { - saved_errno = errno; - - v_print("Slave '%s': Error: SIOCSIFHWADDR failed: %s\n", - slave_ifname, strerror(saved_errno)); - - if (saved_errno == EBUSY) { - v_print(" The device is busy: it must be idle " - "before running this command.\n"); - } else if (saved_errno == EOPNOTSUPP) { - v_print(" The device does not support setting " - "the MAC address.\n" - " Your kernel likely does not support slave " - "devices.\n"); - } else if (saved_errno == EINVAL) { - v_print(" The device's address type does not match " - "the master's address type.\n"); - } - return res; - } else { - v_print("Slave '%s': hardware address set to " - "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x.\n", - slave_ifname, addr[0], addr[1], addr[2], - addr[3], addr[4], addr[5]); - } - - return res; -} - -static int set_slave_mtu(char *slave_ifname, int mtu) -{ - struct ifreq ifr; - int res = 0; - - ifr.ifr_mtu = mtu; - strncpy(ifr.ifr_name, slave_ifname, IFNAMSIZ); - - res = ioctl(skfd, SIOCSIFMTU, &ifr); - if (res < 0) { - saved_errno = errno; - v_print("Slave '%s': Error: SIOCSIFMTU failed: %s\n", - slave_ifname, strerror(saved_errno)); - } else { - v_print("Slave '%s': MTU set to %d.\n", slave_ifname, mtu); - } - - return res; -} - -static int set_if_flags(char *ifname, short flags) -{ - struct ifreq ifr; - int res = 0; - - ifr.ifr_flags = flags; - strncpy(ifr.ifr_name, ifname, IFNAMSIZ); - - res = ioctl(skfd, SIOCSIFFLAGS, &ifr); - if (res < 0) { - saved_errno = errno; - v_print("Interface '%s': Error: SIOCSIFFLAGS failed: %s\n", - ifname, strerror(saved_errno)); - } else { - v_print("Interface '%s': flags set to %04X.\n", ifname, flags); - } - - return res; -} - -static int set_if_up(char *ifname, short flags) -{ - return set_if_flags(ifname, flags | IFF_UP); -} - -static int set_if_down(char *ifname, short flags) -{ - return set_if_flags(ifname, flags & ~IFF_UP); -} - -static int clear_if_addr(char *ifname) -{ - struct ifreq ifr; - int res = 0; - - strncpy(ifr.ifr_name, ifname, IFNAMSIZ); - ifr.ifr_addr.sa_family = AF_INET; - memset(ifr.ifr_addr.sa_data, 0, sizeof(ifr.ifr_addr.sa_data)); - - res = ioctl(skfd, SIOCSIFADDR, &ifr); - if (res < 0) { - saved_errno = errno; - v_print("Interface '%s': Error: SIOCSIFADDR failed: %s\n", - ifname, strerror(saved_errno)); - } else { - v_print("Interface '%s': address cleared\n", ifname); - } - - return res; -} - -static int set_if_addr(char *master_ifname, char *slave_ifname) -{ - struct ifreq ifr; - int res; - unsigned char *ipaddr; - int i; - struct { - char *req_name; - char *desc; - int g_ioctl; - int s_ioctl; - } ifra[] = { - {"IFADDR", "addr", SIOCGIFADDR, SIOCSIFADDR}, - {"DSTADDR", "destination addr", SIOCGIFDSTADDR, SIOCSIFDSTADDR}, - {"BRDADDR", "broadcast addr", SIOCGIFBRDADDR, SIOCSIFBRDADDR}, - {"NETMASK", "netmask", SIOCGIFNETMASK, SIOCSIFNETMASK}, - {NULL, NULL, 0, 0}, - }; - - for (i = 0; ifra[i].req_name; i++) { - strncpy(ifr.ifr_name, master_ifname, IFNAMSIZ); - res = ioctl(skfd, ifra[i].g_ioctl, &ifr); - if (res < 0) { - int saved_errno = errno; - - v_print("Interface '%s': Error: SIOCG%s failed: %s\n", - master_ifname, ifra[i].req_name, - strerror(saved_errno)); - - ifr.ifr_addr.sa_family = AF_INET; - memset(ifr.ifr_addr.sa_data, 0, - sizeof(ifr.ifr_addr.sa_data)); - } - - strncpy(ifr.ifr_name, slave_ifname, IFNAMSIZ); - res = ioctl(skfd, ifra[i].s_ioctl, &ifr); - if (res < 0) { - int saved_errno = errno; - - v_print("Interface '%s': Error: SIOCS%s failed: %s\n", - slave_ifname, ifra[i].req_name, - strerror(saved_errno)); - - } - - ipaddr = (unsigned char *)ifr.ifr_addr.sa_data; - v_print("Interface '%s': set IP %s to %d.%d.%d.%d\n", - slave_ifname, ifra[i].desc, - ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]); - } - - return 0; -} - -/* - * Local variables: - * version-control: t - * kept-new-versions: 5 - * c-indent-level: 4 - * c-basic-offset: 4 - * tab-width: 4 - * compile-command: "gcc -Wall -Wstrict-prototypes -O -I/usr/src/linux/include ifenslave.c -o ifenslave" - * End: - */ - -- cgit v1.2.3 From 24c7a381720843f17efb42de81f7e85aefd6f616 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Tue, 28 May 2013 06:22:32 +0000 Subject: thermal: cpu_cooling: fix 'descend' check in get_property() The variable 'descend' is initialized as -1 in function get_property(), and will never get any chance to be updated by the following code. if (freq != CPUFREQ_ENTRY_INVALID && descend != -1) descend = !!(freq > table[i].frequency); This makes function get_property() return the wrong frequency for given cooling level if the frequency table is sorted in ascending. Fix it by correcting the 'descend' check in if-condition to 'descend == -1'. Signed-off-by: Shawn Guo Signed-off-by: Zhang Rui --- drivers/thermal/cpu_cooling.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index c94bf2e5de62..82e15dbb3ac7 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -167,7 +167,7 @@ static int get_property(unsigned int cpu, unsigned long input, continue; /* get the frequency order */ - if (freq != CPUFREQ_ENTRY_INVALID && descend != -1) + if (freq != CPUFREQ_ENTRY_INVALID && descend == -1) descend = !!(freq > table[i].frequency); freq = table[i].frequency; -- cgit v1.2.3 From c7e99fc75de8882bc4104455ace366d9d3599a96 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 28 May 2013 09:28:02 +0200 Subject: clockevents: Define CS_NAME_LEN unconditionally Unbreak architectures which do not use clockevents, but require to build some of the core timekeeping infrastructure Reported-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- kernel/time/tick-internal.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/time/tick-internal.h b/kernel/time/tick-internal.h index be1690eaecff..bc906cad709b 100644 --- a/kernel/time/tick-internal.h +++ b/kernel/time/tick-internal.h @@ -6,13 +6,13 @@ extern seqlock_t jiffies_lock; +#define CS_NAME_LEN 32 + #ifdef CONFIG_GENERIC_CLOCKEVENTS_BUILD #define TICK_DO_TIMER_NONE -1 #define TICK_DO_TIMER_BOOT -2 -#define CS_NAME_LEN 32 - DECLARE_PER_CPU(struct tick_device, tick_cpu_device); extern ktime_t tick_next_period; extern ktime_t tick_period; -- cgit v1.2.3 From e2d4ea9444024e38217e4b7e380c6b9f39f9a5f2 Mon Sep 17 00:00:00 2001 From: Andrei Varvara Date: Tue, 28 May 2013 15:37:05 +0800 Subject: crypto: caam - fix SEQ IN PTR command when RTO or PRE bit is set SEQ IN PTR command does not require pointer if RTO or PRE bit is set Updated desc_constr.h accordingly. Signed-off-by: Andrei Varvara Reviewed-by: Phillips Kim-R1AAHA Reviewed-by: Fleming Andrew-AFLEMING Signed-off-by: Herbert Xu --- drivers/crypto/caam/desc.h | 4 ++-- drivers/crypto/caam/desc_constr.h | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/crypto/caam/desc.h b/drivers/crypto/caam/desc.h index f7f833be8c67..89fa3d045612 100644 --- a/drivers/crypto/caam/desc.h +++ b/drivers/crypto/caam/desc.h @@ -1294,10 +1294,10 @@ struct sec4_sg_entry { #define SQOUT_SGF 0x01000000 /* Appends to a previous pointer */ -#define SQOUT_PRE 0x00800000 +#define SQOUT_PRE SQIN_PRE /* Restore sequence with pointer/length */ -#define SQOUT_RTO 0x00200000 +#define SQOUT_RTO SQIN_RTO /* Use extended length following pointer */ #define SQOUT_EXT 0x00400000 diff --git a/drivers/crypto/caam/desc_constr.h b/drivers/crypto/caam/desc_constr.h index c85c1f058401..19501b548db4 100644 --- a/drivers/crypto/caam/desc_constr.h +++ b/drivers/crypto/caam/desc_constr.h @@ -122,7 +122,8 @@ static inline void append_cmd_ptr_extlen(u32 *desc, dma_addr_t ptr, unsigned int len, u32 command) { append_cmd(desc, command); - append_ptr(desc, ptr); + if (!(command & (SQIN_RTO | SQIN_PRE))) + append_ptr(desc, ptr); append_cmd(desc, len); } @@ -186,7 +187,10 @@ static inline void append_seq_##cmd##_ptr_intlen(u32 *desc, dma_addr_t ptr, \ u32 options) \ { \ PRINT_POS; \ - append_cmd_ptr(desc, ptr, len, CMD_SEQ_##op##_PTR | options); \ + if (options & (SQIN_RTO | SQIN_PRE)) \ + append_cmd(desc, CMD_SEQ_##op##_PTR | len | options); \ + else \ + append_cmd_ptr(desc, ptr, len, CMD_SEQ_##op##_PTR | options); \ } APPEND_SEQ_PTR_INTLEN(in, IN) APPEND_SEQ_PTR_INTLEN(out, OUT) -- cgit v1.2.3 From 524f1bd9a3d401cca16bca16582cd71c0abc8bd9 Mon Sep 17 00:00:00 2001 From: Andrei Varvara Date: Tue, 28 May 2013 15:37:06 +0800 Subject: crypto: caam - Fix STORE command to support overwriting Shared Descriptor's memory In case Store command is used with overwrite Shared Descriptor feature there is no need for pointer, it is using the address from which the Shared Descriptor was fetched. Signed-off-by: Andrei Varvara Reviewed-by: Phillips Kim-R1AAHA Reviewed-by: Fleming Andrew-AFLEMING Signed-off-by: Herbert Xu --- drivers/crypto/caam/desc_constr.h | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/crypto/caam/desc_constr.h b/drivers/crypto/caam/desc_constr.h index 19501b548db4..fc4470a2c337 100644 --- a/drivers/crypto/caam/desc_constr.h +++ b/drivers/crypto/caam/desc_constr.h @@ -177,10 +177,26 @@ static inline void append_##cmd(u32 *desc, dma_addr_t ptr, unsigned int len, \ } APPEND_CMD_PTR(key, KEY) APPEND_CMD_PTR(load, LOAD) -APPEND_CMD_PTR(store, STORE) APPEND_CMD_PTR(fifo_load, FIFO_LOAD) APPEND_CMD_PTR(fifo_store, FIFO_STORE) +static inline void append_store(u32 *desc, dma_addr_t ptr, unsigned int len, + u32 options) +{ + u32 cmd_src; + + cmd_src = options & LDST_SRCDST_MASK; + + append_cmd(desc, CMD_STORE | options | len); + + /* The following options do not require pointer */ + if (!(cmd_src == LDST_SRCDST_WORD_DESCBUF_SHARED || + cmd_src == LDST_SRCDST_WORD_DESCBUF_JOB || + cmd_src == LDST_SRCDST_WORD_DESCBUF_JOB_WE || + cmd_src == LDST_SRCDST_WORD_DESCBUF_SHARED_WE)) + append_ptr(desc, ptr); +} + #define APPEND_SEQ_PTR_INTLEN(cmd, op) \ static inline void append_seq_##cmd##_ptr_intlen(u32 *desc, dma_addr_t ptr, \ unsigned int len, \ -- cgit v1.2.3 From d179333b0c6298291c384a0b0ba51ee67d983758 Mon Sep 17 00:00:00 2001 From: Andrei Varvara Date: Tue, 28 May 2013 15:37:06 +0800 Subject: crypto: caam - Add MATH command to support shld function Perform 32-bit left shift of DEST and concatenate with left 32 bits of SRC1. {DEST[31:0],SRC1[63:32]} Signed-off-by: Andrei Varvara Acked-by: Mihai Serb Reviewed-by: Phillips Kim-R1AAHA Reviewed-by: Fleming Andrew-AFLEMING Signed-off-by: Herbert Xu --- drivers/crypto/caam/desc_constr.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/crypto/caam/desc_constr.h b/drivers/crypto/caam/desc_constr.h index fc4470a2c337..08c95a192234 100644 --- a/drivers/crypto/caam/desc_constr.h +++ b/drivers/crypto/caam/desc_constr.h @@ -299,6 +299,8 @@ append_cmd(desc, CMD_MATH | MATH_FUN_##op | MATH_DEST_##dest | \ APPEND_MATH(LSHIFT, desc, dest, src0, src1, len) #define append_math_rshift(desc, dest, src0, src1, len) \ APPEND_MATH(RSHIFT, desc, dest, src0, src1, len) +#define append_math_ldshift(desc, dest, src0, src1, len) \ + APPEND_MATH(SHLD, desc, dest, src0, src1, len) /* Exactly one source is IMM. Data is passed in as u32 value */ #define APPEND_MATH_IMM_u32(op, desc, dest, src_0, src_1, data) \ -- cgit v1.2.3 From 3ebfa92f49a61a00e967112632ad10e92998bb75 Mon Sep 17 00:00:00 2001 From: Andrei Varvara Date: Tue, 28 May 2013 15:37:07 +0800 Subject: crypto: caam - Add new macros for building extended SEC descriptors (> 64 words) added all supported math funtion on 8 byte boundary with immediate flag bit set automatically added MATH_SRC0_DPOVRD & MATH_SRC1_DPOVRD The function/defines above are needed for creating descriptors longer than 64 words Signed-off-by: Andrei Varvara Reviewed-by: Phillips Kim-R1AAHA Reviewed-by: Fleming Andrew-AFLEMING Signed-off-by: Herbert Xu --- drivers/crypto/caam/desc.h | 4 +++ drivers/crypto/caam/desc_constr.h | 53 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/drivers/crypto/caam/desc.h b/drivers/crypto/caam/desc.h index 89fa3d045612..7d8b14723465 100644 --- a/drivers/crypto/caam/desc.h +++ b/drivers/crypto/caam/desc.h @@ -366,6 +366,7 @@ struct sec4_sg_entry { #define FIFOLD_TYPE_LAST2FLUSH1 (0x05 << FIFOLD_TYPE_SHIFT) #define FIFOLD_TYPE_LASTBOTH (0x06 << FIFOLD_TYPE_SHIFT) #define FIFOLD_TYPE_LASTBOTHFL (0x07 << FIFOLD_TYPE_SHIFT) +#define FIFOLD_TYPE_NOINFOFIFO (0x0F << FIFOLD_TYPE_SHIFT) #define FIFOLDST_LEN_MASK 0xffff #define FIFOLDST_EXT_LEN_MASK 0xffffffff @@ -1359,6 +1360,7 @@ struct sec4_sg_entry { #define MOVE_DEST_MATH3 (0x07 << MOVE_DEST_SHIFT) #define MOVE_DEST_CLASS1INFIFO (0x08 << MOVE_DEST_SHIFT) #define MOVE_DEST_CLASS2INFIFO (0x09 << MOVE_DEST_SHIFT) +#define MOVE_DEST_INFIFO_NOINFO (0x0a << MOVE_DEST_SHIFT) #define MOVE_DEST_PK_A (0x0c << MOVE_DEST_SHIFT) #define MOVE_DEST_CLASS1KEY (0x0d << MOVE_DEST_SHIFT) #define MOVE_DEST_CLASS2KEY (0x0e << MOVE_DEST_SHIFT) @@ -1411,6 +1413,7 @@ struct sec4_sg_entry { #define MATH_SRC0_REG2 (0x02 << MATH_SRC0_SHIFT) #define MATH_SRC0_REG3 (0x03 << MATH_SRC0_SHIFT) #define MATH_SRC0_IMM (0x04 << MATH_SRC0_SHIFT) +#define MATH_SRC0_DPOVRD (0x07 << MATH_SRC0_SHIFT) #define MATH_SRC0_SEQINLEN (0x08 << MATH_SRC0_SHIFT) #define MATH_SRC0_SEQOUTLEN (0x09 << MATH_SRC0_SHIFT) #define MATH_SRC0_VARSEQINLEN (0x0a << MATH_SRC0_SHIFT) @@ -1425,6 +1428,7 @@ struct sec4_sg_entry { #define MATH_SRC1_REG2 (0x02 << MATH_SRC1_SHIFT) #define MATH_SRC1_REG3 (0x03 << MATH_SRC1_SHIFT) #define MATH_SRC1_IMM (0x04 << MATH_SRC1_SHIFT) +#define MATH_SRC1_DPOVRD (0x07 << MATH_SRC0_SHIFT) #define MATH_SRC1_INFIFO (0x0a << MATH_SRC1_SHIFT) #define MATH_SRC1_OUTFIFO (0x0b << MATH_SRC1_SHIFT) #define MATH_SRC1_ONE (0x0c << MATH_SRC1_SHIFT) diff --git a/drivers/crypto/caam/desc_constr.h b/drivers/crypto/caam/desc_constr.h index 08c95a192234..fe3bfd1b08ca 100644 --- a/drivers/crypto/caam/desc_constr.h +++ b/drivers/crypto/caam/desc_constr.h @@ -110,6 +110,26 @@ static inline void append_cmd(u32 *desc, u32 command) (*desc)++; } +#define append_u32 append_cmd + +static inline void append_u64(u32 *desc, u64 data) +{ + u32 *offset = desc_end(desc); + + *offset = upper_32_bits(data); + *(++offset) = lower_32_bits(data); + + (*desc) += 2; +} + +/* Write command without affecting header, and return pointer to next word */ +static inline u32 *write_cmd(u32 *desc, u32 command) +{ + *desc = command; + + return desc + 1; +} + static inline void append_cmd_ptr(u32 *desc, dma_addr_t ptr, int len, u32 command) { @@ -279,7 +299,7 @@ APPEND_CMD_RAW_IMM(load, LOAD, u32); */ #define APPEND_MATH(op, desc, dest, src_0, src_1, len) \ append_cmd(desc, CMD_MATH | MATH_FUN_##op | MATH_DEST_##dest | \ - MATH_SRC0_##src_0 | MATH_SRC1_##src_1 | (u32) (len & MATH_LEN_MASK)); + MATH_SRC0_##src_0 | MATH_SRC1_##src_1 | (u32)len); #define append_math_add(desc, dest, src0, src1, len) \ APPEND_MATH(ADD, desc, dest, src0, src1, len) @@ -327,3 +347,34 @@ do { \ APPEND_MATH_IMM_u32(LSHIFT, desc, dest, src0, src1, data) #define append_math_rshift_imm_u32(desc, dest, src0, src1, data) \ APPEND_MATH_IMM_u32(RSHIFT, desc, dest, src0, src1, data) + +/* Exactly one source is IMM. Data is passed in as u64 value */ +#define APPEND_MATH_IMM_u64(op, desc, dest, src_0, src_1, data) \ +do { \ + u32 upper = (data >> 16) >> 16; \ + APPEND_MATH(op, desc, dest, src_0, src_1, CAAM_CMD_SZ * 2 | \ + (upper ? 0 : MATH_IFB)); \ + if (upper) \ + append_u64(desc, data); \ + else \ + append_u32(desc, data); \ +} while (0) + +#define append_math_add_imm_u64(desc, dest, src0, src1, data) \ + APPEND_MATH_IMM_u64(ADD, desc, dest, src0, src1, data) +#define append_math_sub_imm_u64(desc, dest, src0, src1, data) \ + APPEND_MATH_IMM_u64(SUB, desc, dest, src0, src1, data) +#define append_math_add_c_imm_u64(desc, dest, src0, src1, data) \ + APPEND_MATH_IMM_u64(ADDC, desc, dest, src0, src1, data) +#define append_math_sub_b_imm_u64(desc, dest, src0, src1, data) \ + APPEND_MATH_IMM_u64(SUBB, desc, dest, src0, src1, data) +#define append_math_and_imm_u64(desc, dest, src0, src1, data) \ + APPEND_MATH_IMM_u64(AND, desc, dest, src0, src1, data) +#define append_math_or_imm_u64(desc, dest, src0, src1, data) \ + APPEND_MATH_IMM_u64(OR, desc, dest, src0, src1, data) +#define append_math_xor_imm_u64(desc, dest, src0, src1, data) \ + APPEND_MATH_IMM_u64(XOR, desc, dest, src0, src1, data) +#define append_math_lshift_imm_u64(desc, dest, src0, src1, data) \ + APPEND_MATH_IMM_u64(LSHIFT, desc, dest, src0, src1, data) +#define append_math_rshift_imm_u64(desc, dest, src0, src1, data) \ + APPEND_MATH_IMM_u64(RSHIFT, desc, dest, src0, src1, data) -- cgit v1.2.3 From 1f50be97f630012e096968d79f5b2fbfebadaf81 Mon Sep 17 00:00:00 2001 From: Andrei Varvara Date: Tue, 28 May 2013 15:37:07 +0800 Subject: crypto: caam - Add defines for overwriting Descriptor's memory Store command has options to overwrite the Job Desc, Shared Desc or the entire Descriptor in memory, using the address from which the Descriptor was fetched. Signed-off-by: Andrei Varvara Reviewed-by: Phillips Kim-R1AAHA Reviewed-by: Fleming Andrew-AFLEMING Signed-off-by: Herbert Xu --- drivers/crypto/caam/desc.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/crypto/caam/desc.h b/drivers/crypto/caam/desc.h index 7d8b14723465..972ad145d76c 100644 --- a/drivers/crypto/caam/desc.h +++ b/drivers/crypto/caam/desc.h @@ -232,6 +232,10 @@ struct sec4_sg_entry { #define LDST_SRCDST_WORD_PKHA_N_SZ (0x12 << LDST_SRCDST_SHIFT) #define LDST_SRCDST_WORD_PKHA_E_SZ (0x13 << LDST_SRCDST_SHIFT) #define LDST_SRCDST_WORD_DESCBUF (0x40 << LDST_SRCDST_SHIFT) +#define LDST_SRCDST_WORD_DESCBUF_JOB (0x41 << LDST_SRCDST_SHIFT) +#define LDST_SRCDST_WORD_DESCBUF_SHARED (0x42 << LDST_SRCDST_SHIFT) +#define LDST_SRCDST_WORD_DESCBUF_JOB_WE (0x45 << LDST_SRCDST_SHIFT) +#define LDST_SRCDST_WORD_DESCBUF_SHARED_WE (0x46 << LDST_SRCDST_SHIFT) #define LDST_SRCDST_WORD_INFO_FIFO (0x7a << LDST_SRCDST_SHIFT) /* Offset in source/destination */ -- cgit v1.2.3 From 590f9667a2307b24e9b56138d901dd2f228db6e0 Mon Sep 17 00:00:00 2001 From: Andrei Varvara Date: Tue, 28 May 2013 15:37:08 +0800 Subject: crypto: caam - Add defines for CAAM commands add defines for: append load immediate command setting SEQ LIODN equal to the Non-SEQ LIODN for the job replace job descriptor command Signed-off-by: Andrei Varvara Reviewed-by: Phillips Kim-R1AAHA Reviewed-by: Fleming Andrew-AFLEMING Signed-off-by: Herbert Xu --- drivers/crypto/caam/desc.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/crypto/caam/desc.h b/drivers/crypto/caam/desc.h index 972ad145d76c..8214dc1c49e8 100644 --- a/drivers/crypto/caam/desc.h +++ b/drivers/crypto/caam/desc.h @@ -1608,4 +1608,13 @@ struct sec4_sg_entry { #define NFIFOENTRY_PLEN_SHIFT 0 #define NFIFOENTRY_PLEN_MASK (0xFF << NFIFOENTRY_PLEN_SHIFT) +/* Append Load Immediate Command */ +#define FD_CMD_APPEND_LOAD_IMMEDIATE 0x80000000 + +/* Set SEQ LIODN equal to the Non-SEQ LIODN for the job */ +#define FD_CMD_SET_SEQ_LIODN_EQUAL_NONSEQ_LIODN 0x40000000 + +/* Frame Descriptor Command for Replacement Job Descriptor */ +#define FD_CMD_REPLACE_JOB_DESC 0x20000000 + #endif /* DESC_H */ -- cgit v1.2.3 From da64e35810e0717ec63ca25a46118f48e320caff Mon Sep 17 00:00:00 2001 From: Andrei Varvara Date: Tue, 28 May 2013 15:37:08 +0800 Subject: crypto: caam - Add define for Adjust Output Frame Length in PDB Add define for "Adjust Output Frame Length" in order to set the AOFL bit in the IPsec ESP Decapsulation PDB. Signed-off-by: Anca-Jeanina Floarea Signed-off-by: Andrei Varvara Reviewed-by: Phillips Kim-R1AAHA Reviewed-by: Fleming Andrew-AFLEMING Signed-off-by: Herbert Xu --- drivers/crypto/caam/pdb.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/crypto/caam/pdb.h b/drivers/crypto/caam/pdb.h index 62950d22ac13..3a87c0cf879a 100644 --- a/drivers/crypto/caam/pdb.h +++ b/drivers/crypto/caam/pdb.h @@ -44,6 +44,7 @@ #define PDBOPTS_ESP_IPHDRSRC 0x08 /* IP header comes from PDB (encap) */ #define PDBOPTS_ESP_INCIPHDR 0x04 /* Prepend IP header to output frame */ #define PDBOPTS_ESP_IPVSN 0x02 /* process IPv6 header */ +#define PDBOPTS_ESP_AOFL 0x04 /* adjust out frame len (decap, SEC>=5.3)*/ #define PDBOPTS_ESP_TUNNEL 0x01 /* tunnel mode next-header byte */ #define PDBOPTS_ESP_IPV6 0x02 /* ip header version is V6 */ #define PDBOPTS_ESP_DIFFSERV 0x40 /* copy TOS/TC from inner iphdr */ -- cgit v1.2.3 From 91dc363a86142afcbc4e9c3ab31b0f4563b0f269 Mon Sep 17 00:00:00 2001 From: Andrei Varvara Date: Tue, 28 May 2013 15:37:08 +0800 Subject: crypto: caam - add missing flag for the LOAD/STORE commands Add Class Context SRC / DEST flags for the LOAD & STORE commands Signed-off-by: Andrei Varvara Reviewed-by: Phillips Kim-R1AAHA Reviewed-by: Fleming Andrew-AFLEMING Signed-off-by: Herbert Xu --- drivers/crypto/caam/desc.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/crypto/caam/desc.h b/drivers/crypto/caam/desc.h index 8214dc1c49e8..53b296f78b0d 100644 --- a/drivers/crypto/caam/desc.h +++ b/drivers/crypto/caam/desc.h @@ -231,6 +231,7 @@ struct sec4_sg_entry { #define LDST_SRCDST_WORD_PKHA_B_SZ (0x11 << LDST_SRCDST_SHIFT) #define LDST_SRCDST_WORD_PKHA_N_SZ (0x12 << LDST_SRCDST_SHIFT) #define LDST_SRCDST_WORD_PKHA_E_SZ (0x13 << LDST_SRCDST_SHIFT) +#define LDST_SRCDST_WORD_CLASS_CTX (0x20 << LDST_SRCDST_SHIFT) #define LDST_SRCDST_WORD_DESCBUF (0x40 << LDST_SRCDST_SHIFT) #define LDST_SRCDST_WORD_DESCBUF_JOB (0x41 << LDST_SRCDST_SHIFT) #define LDST_SRCDST_WORD_DESCBUF_SHARED (0x42 << LDST_SRCDST_SHIFT) -- cgit v1.2.3 From 519d8b1a9d81be7e4ffad8aa6b0e3ea03984bb86 Mon Sep 17 00:00:00 2001 From: Tobias Rauter Date: Sun, 19 May 2013 21:59:38 +0200 Subject: crypto: dcp - Added support for Freescale's DCP co-processor This patch enables the DCP crypto functionality on imx28. Currently, only aes-128-cbc is supported. Moreover, the dcpboot misc-device, which is used by Freescale's SDK tools and uses a non-software-readable OTP-key, is added. Changes of v2: - ring buffer for hardware-descriptors - use of ablkcipher walk - OTP key encryption/decryption via misc-device (compatible to Freescale-SDK) - overall cleanup The DCP is also capable of sha1/sha256 but I won't be able to add that anytime soon. Tested with built-in runtime-self-test, tcrypt and openssl via cryptodev 1.6 on imx28-evk and a custom built imx28-board. Signed-off-by: Tobias Rauter Signed-off-by: Herbert Xu --- arch/arm/boot/dts/imx28.dtsi | 2 +- drivers/crypto/Kconfig | 10 + drivers/crypto/Makefile | 1 + drivers/crypto/dcp.c | 925 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 937 insertions(+), 1 deletion(-) create mode 100644 drivers/crypto/dcp.c diff --git a/arch/arm/boot/dts/imx28.dtsi b/arch/arm/boot/dts/imx28.dtsi index 600f7cb51f3e..077d0eb1a8b3 100644 --- a/arch/arm/boot/dts/imx28.dtsi +++ b/arch/arm/boot/dts/imx28.dtsi @@ -699,7 +699,7 @@ dcp@80028000 { reg = <0x80028000 0x2000>; interrupts = <52 53 54>; - status = "disabled"; + compatible = "fsl-dcp"; }; pxp@8002a000 { diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index 47c6f4dcc675..8ff7c230d82e 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -286,6 +286,16 @@ config CRYPTO_DEV_SAHARA This option enables support for the SAHARA HW crypto accelerator found in some Freescale i.MX chips. +config CRYPTO_DEV_DCP + tristate "Support for the DCP engine" + depends on ARCH_MXS && OF + select CRYPTO_BLKCIPHER + select CRYPTO_AES + select CRYPTO_CBC + help + This options enables support for the hardware crypto-acceleration + capabilities of the DCP co-processor + config CRYPTO_DEV_S5P tristate "Support for Samsung S5PV210 crypto accelerator" depends on ARCH_S5PV210 diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile index 38ce13d3b79b..b4946ddd2550 100644 --- a/drivers/crypto/Makefile +++ b/drivers/crypto/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_CRYPTO_DEV_OMAP_SHAM) += omap-sham.o obj-$(CONFIG_CRYPTO_DEV_OMAP_AES) += omap-aes.o obj-$(CONFIG_CRYPTO_DEV_PICOXCELL) += picoxcell_crypto.o obj-$(CONFIG_CRYPTO_DEV_SAHARA) += sahara.o +obj-$(CONFIG_CRYPTO_DEV_DCP) += dcp.o obj-$(CONFIG_CRYPTO_DEV_S5P) += s5p-sss.o obj-$(CONFIG_CRYPTO_DEV_TEGRA_AES) += tegra-aes.o obj-$(CONFIG_CRYPTO_DEV_UX500) += ux500/ diff --git a/drivers/crypto/dcp.c b/drivers/crypto/dcp.c new file mode 100644 index 000000000000..eea194c34527 --- /dev/null +++ b/drivers/crypto/dcp.c @@ -0,0 +1,925 @@ +/* + * Cryptographic API. + * + * Support for DCP cryptographic accelerator. + * + * Copyright (c) 2013 + * Author: Tobias Rauter + * + * 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. + * + * Based on tegra-aes.c, dcp.c (from freescale SDK) and sahara.c + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +/* IOCTL for DCP OTP Key AES - taken from Freescale's SDK*/ +#define DBS_IOCTL_BASE 'd' +#define DBS_ENC _IOW(DBS_IOCTL_BASE, 0x00, uint8_t[16]) +#define DBS_DEC _IOW(DBS_IOCTL_BASE, 0x01, uint8_t[16]) + +/* DCP channel used for AES */ +#define USED_CHANNEL 1 +/* Ring Buffers' maximum size */ +#define DCP_MAX_PKG 20 + +/* Control Register */ +#define DCP_REG_CTRL 0x000 +#define DCP_CTRL_SFRST (1<<31) +#define DCP_CTRL_CLKGATE (1<<30) +#define DCP_CTRL_CRYPTO_PRESENT (1<<29) +#define DCP_CTRL_SHA_PRESENT (1<<28) +#define DCP_CTRL_GATHER_RES_WRITE (1<<23) +#define DCP_CTRL_ENABLE_CONTEXT_CACHE (1<<22) +#define DCP_CTRL_ENABLE_CONTEXT_SWITCH (1<<21) +#define DCP_CTRL_CH_IRQ_E_0 0x01 +#define DCP_CTRL_CH_IRQ_E_1 0x02 +#define DCP_CTRL_CH_IRQ_E_2 0x04 +#define DCP_CTRL_CH_IRQ_E_3 0x08 + +/* Status register */ +#define DCP_REG_STAT 0x010 +#define DCP_STAT_OTP_KEY_READY (1<<28) +#define DCP_STAT_CUR_CHANNEL(stat) ((stat>>24)&0x0F) +#define DCP_STAT_READY_CHANNEL(stat) ((stat>>16)&0x0F) +#define DCP_STAT_IRQ(stat) (stat&0x0F) +#define DCP_STAT_CHAN_0 (0x01) +#define DCP_STAT_CHAN_1 (0x02) +#define DCP_STAT_CHAN_2 (0x04) +#define DCP_STAT_CHAN_3 (0x08) + +/* Channel Control Register */ +#define DCP_REG_CHAN_CTRL 0x020 +#define DCP_CHAN_CTRL_CH0_IRQ_MERGED (1<<16) +#define DCP_CHAN_CTRL_HIGH_PRIO_0 (0x0100) +#define DCP_CHAN_CTRL_HIGH_PRIO_1 (0x0200) +#define DCP_CHAN_CTRL_HIGH_PRIO_2 (0x0400) +#define DCP_CHAN_CTRL_HIGH_PRIO_3 (0x0800) +#define DCP_CHAN_CTRL_ENABLE_0 (0x01) +#define DCP_CHAN_CTRL_ENABLE_1 (0x02) +#define DCP_CHAN_CTRL_ENABLE_2 (0x04) +#define DCP_CHAN_CTRL_ENABLE_3 (0x08) + +/* + * Channel Registers: + * The DCP has 4 channels. Each of this channels + * has 4 registers (command pointer, semaphore, status and options). + * The address of register REG of channel CHAN is obtained by + * dcp_chan_reg(REG, CHAN) + */ +#define DCP_REG_CHAN_PTR 0x00000100 +#define DCP_REG_CHAN_SEMA 0x00000110 +#define DCP_REG_CHAN_STAT 0x00000120 +#define DCP_REG_CHAN_OPT 0x00000130 + +#define DCP_CHAN_STAT_NEXT_CHAIN_IS_0 0x010000 +#define DCP_CHAN_STAT_NO_CHAIN 0x020000 +#define DCP_CHAN_STAT_CONTEXT_ERROR 0x030000 +#define DCP_CHAN_STAT_PAYLOAD_ERROR 0x040000 +#define DCP_CHAN_STAT_INVALID_MODE 0x050000 +#define DCP_CHAN_STAT_PAGEFAULT 0x40 +#define DCP_CHAN_STAT_DST 0x20 +#define DCP_CHAN_STAT_SRC 0x10 +#define DCP_CHAN_STAT_PACKET 0x08 +#define DCP_CHAN_STAT_SETUP 0x04 +#define DCP_CHAN_STAT_MISMATCH 0x02 + +/* hw packet control*/ + +#define DCP_PKT_PAYLOAD_KEY (1<<11) +#define DCP_PKT_OTP_KEY (1<<10) +#define DCP_PKT_CIPHER_INIT (1<<9) +#define DCP_PKG_CIPHER_ENCRYPT (1<<8) +#define DCP_PKT_CIPHER_ENABLE (1<<5) +#define DCP_PKT_DECR_SEM (1<<1) +#define DCP_PKT_CHAIN (1<<2) +#define DCP_PKT_IRQ 1 + +#define DCP_PKT_MODE_CBC (1<<4) +#define DCP_PKT_KEYSELECT_OTP (0xFF<<8) + +/* cipher flags */ +#define DCP_ENC 0x0001 +#define DCP_DEC 0x0002 +#define DCP_ECB 0x0004 +#define DCP_CBC 0x0008 +#define DCP_CBC_INIT 0x0010 +#define DCP_NEW_KEY 0x0040 +#define DCP_OTP_KEY 0x0080 +#define DCP_AES 0x1000 + +/* DCP Flags */ +#define DCP_FLAG_BUSY 0x01 +#define DCP_FLAG_PRODUCING 0x02 + +/* clock defines */ +#define CLOCK_ON 1 +#define CLOCK_OFF 0 + +struct dcp_dev_req_ctx { + int mode; +}; + +struct dcp_op { + unsigned int flags; + u8 key[AES_KEYSIZE_128]; + int keylen; + + struct ablkcipher_request *req; + struct crypto_ablkcipher *fallback; + + uint32_t stat; + uint32_t pkt1; + uint32_t pkt2; + struct ablkcipher_walk walk; +}; + +struct dcp_dev { + struct device *dev; + void __iomem *dcp_regs_base; + + int dcp_vmi_irq; + int dcp_irq; + + spinlock_t queue_lock; + struct crypto_queue queue; + + uint32_t pkt_produced; + uint32_t pkt_consumed; + + struct dcp_hw_packet *hw_pkg[DCP_MAX_PKG]; + dma_addr_t hw_phys_pkg; + + /* [KEY][IV] Both with 16 Bytes */ + u8 *payload_base; + dma_addr_t payload_base_dma; + + + struct tasklet_struct done_task; + struct tasklet_struct queue_task; + struct timer_list watchdog; + + unsigned long flags; + + struct dcp_op *ctx; + + struct miscdevice dcp_bootstream_misc; +}; + +struct dcp_hw_packet { + uint32_t next; + uint32_t pkt1; + uint32_t pkt2; + uint32_t src; + uint32_t dst; + uint32_t size; + uint32_t payload; + uint32_t stat; +}; + +struct dcp_dev *global_dev; + +static inline u32 dcp_chan_reg(u32 reg, int chan) +{ + return reg + (chan) * 0x40; +} + +static inline void dcp_write(struct dcp_dev *dev, u32 data, u32 reg) +{ + writel(data, dev->dcp_regs_base + reg); +} + +static inline void dcp_set(struct dcp_dev *dev, u32 data, u32 reg) +{ + writel(data, dev->dcp_regs_base + (reg | 0x04)); +} + +static inline void dcp_clear(struct dcp_dev *dev, u32 data, u32 reg) +{ + writel(data, dev->dcp_regs_base + (reg | 0x08)); +} + +static inline void dcp_toggle(struct dcp_dev *dev, u32 data, u32 reg) +{ + writel(data, dev->dcp_regs_base + (reg | 0x0C)); +} + +static inline unsigned int dcp_read(struct dcp_dev *dev, u32 reg) +{ + return readl(dev->dcp_regs_base + reg); +} + +void dcp_dma_unmap(struct dcp_dev *dev, struct dcp_hw_packet *pkt) +{ + dma_unmap_page(dev->dev, pkt->src, pkt->size, DMA_TO_DEVICE); + dma_unmap_page(dev->dev, pkt->dst, pkt->size, DMA_FROM_DEVICE); + dev_dbg(dev->dev, "unmap packet %x", (unsigned int) pkt); +} + +int dcp_dma_map(struct dcp_dev *dev, + struct ablkcipher_walk *walk, struct dcp_hw_packet *pkt) +{ + dev_dbg(dev->dev, "map packet %x", (unsigned int) pkt); + /* align to length = 16 */ + pkt->size = walk->nbytes - (walk->nbytes % 16); + + pkt->src = dma_map_page(dev->dev, walk->src.page, walk->src.offset, + pkt->size, DMA_TO_DEVICE); + + if (pkt->src == 0) { + dev_err(dev->dev, "Unable to map src"); + return -ENOMEM; + } + + pkt->dst = dma_map_page(dev->dev, walk->dst.page, walk->dst.offset, + pkt->size, DMA_FROM_DEVICE); + + if (pkt->dst == 0) { + dev_err(dev->dev, "Unable to map dst"); + dma_unmap_page(dev->dev, pkt->src, pkt->size, DMA_TO_DEVICE); + return -ENOMEM; + } + + return 0; +} + +static void dcp_op_one(struct dcp_dev *dev, struct dcp_hw_packet *pkt, + uint8_t last) +{ + struct dcp_op *ctx = dev->ctx; + pkt->pkt1 = ctx->pkt1; + pkt->pkt2 = ctx->pkt2; + + pkt->payload = (u32) dev->payload_base_dma; + pkt->stat = 0; + + if (ctx->flags & DCP_CBC_INIT) { + pkt->pkt1 |= DCP_PKT_CIPHER_INIT; + ctx->flags &= ~DCP_CBC_INIT; + } + + mod_timer(&dev->watchdog, jiffies + msecs_to_jiffies(500)); + pkt->pkt1 |= DCP_PKT_IRQ; + if (!last) + pkt->pkt1 |= DCP_PKT_CHAIN; + + dev->pkt_produced++; + + dcp_write(dev, 1, + dcp_chan_reg(DCP_REG_CHAN_SEMA, USED_CHANNEL)); +} + +static void dcp_op_proceed(struct dcp_dev *dev) +{ + struct dcp_op *ctx = dev->ctx; + struct dcp_hw_packet *pkt; + + while (ctx->walk.nbytes) { + int err = 0; + + pkt = dev->hw_pkg[dev->pkt_produced % DCP_MAX_PKG]; + err = dcp_dma_map(dev, &ctx->walk, pkt); + if (err) { + dev->ctx->stat |= err; + /* start timer to wait for already set up calls */ + mod_timer(&dev->watchdog, + jiffies + msecs_to_jiffies(500)); + break; + } + + + err = ctx->walk.nbytes - pkt->size; + ablkcipher_walk_done(dev->ctx->req, &dev->ctx->walk, err); + + dcp_op_one(dev, pkt, ctx->walk.nbytes == 0); + /* we have to wait if no space is left in buffer */ + if (dev->pkt_produced - dev->pkt_consumed == DCP_MAX_PKG) + break; + } + clear_bit(DCP_FLAG_PRODUCING, &dev->flags); +} + +static void dcp_op_start(struct dcp_dev *dev, uint8_t use_walk) +{ + struct dcp_op *ctx = dev->ctx; + + if (ctx->flags & DCP_NEW_KEY) { + memcpy(dev->payload_base, ctx->key, ctx->keylen); + ctx->flags &= ~DCP_NEW_KEY; + } + + ctx->pkt1 = 0; + ctx->pkt1 |= DCP_PKT_CIPHER_ENABLE; + ctx->pkt1 |= DCP_PKT_DECR_SEM; + + if (ctx->flags & DCP_OTP_KEY) + ctx->pkt1 |= DCP_PKT_OTP_KEY; + else + ctx->pkt1 |= DCP_PKT_PAYLOAD_KEY; + + if (ctx->flags & DCP_ENC) + ctx->pkt1 |= DCP_PKG_CIPHER_ENCRYPT; + + ctx->pkt2 = 0; + if (ctx->flags & DCP_CBC) + ctx->pkt2 |= DCP_PKT_MODE_CBC; + + dev->pkt_produced = 0; + dev->pkt_consumed = 0; + + ctx->stat = 0; + dcp_clear(dev, -1, dcp_chan_reg(DCP_REG_CHAN_STAT, USED_CHANNEL)); + dcp_write(dev, (u32) dev->hw_phys_pkg, + dcp_chan_reg(DCP_REG_CHAN_PTR, USED_CHANNEL)); + + set_bit(DCP_FLAG_PRODUCING, &dev->flags); + + if (use_walk) { + ablkcipher_walk_init(&ctx->walk, ctx->req->dst, + ctx->req->src, ctx->req->nbytes); + ablkcipher_walk_phys(ctx->req, &ctx->walk); + dcp_op_proceed(dev); + } else { + dcp_op_one(dev, dev->hw_pkg[0], 1); + clear_bit(DCP_FLAG_PRODUCING, &dev->flags); + } +} + +static void dcp_done_task(unsigned long data) +{ + struct dcp_dev *dev = (struct dcp_dev *)data; + struct dcp_hw_packet *last_packet; + int fin; + fin = 0; + + for (last_packet = dev->hw_pkg[(dev->pkt_consumed) % DCP_MAX_PKG]; + last_packet->stat == 1; + last_packet = + dev->hw_pkg[++(dev->pkt_consumed) % DCP_MAX_PKG]) { + + dcp_dma_unmap(dev, last_packet); + last_packet->stat = 0; + fin++; + } + /* the last call of this function already consumed this IRQ's packet */ + if (fin == 0) + return; + + dev_dbg(dev->dev, + "Packet(s) done with status %x; finished: %d, produced:%d, complete consumed: %d", + dev->ctx->stat, fin, dev->pkt_produced, dev->pkt_consumed); + + last_packet = dev->hw_pkg[(dev->pkt_consumed - 1) % DCP_MAX_PKG]; + if (!dev->ctx->stat && last_packet->pkt1 & DCP_PKT_CHAIN) { + if (!test_and_set_bit(DCP_FLAG_PRODUCING, &dev->flags)) + dcp_op_proceed(dev); + return; + } + + while (unlikely(dev->pkt_consumed < dev->pkt_produced)) { + dcp_dma_unmap(dev, + dev->hw_pkg[dev->pkt_consumed++ % DCP_MAX_PKG]); + } + + if (dev->ctx->flags & DCP_OTP_KEY) { + /* we used the miscdevice, no walk to finish */ + clear_bit(DCP_FLAG_BUSY, &dev->flags); + return; + } + + ablkcipher_walk_complete(&dev->ctx->walk); + dev->ctx->req->base.complete(&dev->ctx->req->base, + dev->ctx->stat); + dev->ctx->req = 0; + /* in case there are other requests in the queue */ + tasklet_schedule(&dev->queue_task); +} + +void dcp_watchdog(unsigned long data) +{ + struct dcp_dev *dev = (struct dcp_dev *)data; + dev->ctx->stat |= dcp_read(dev, + dcp_chan_reg(DCP_REG_CHAN_STAT, USED_CHANNEL)); + + dev_err(dev->dev, "Timeout, Channel status: %x", dev->ctx->stat); + + if (!dev->ctx->stat) + dev->ctx->stat = -ETIMEDOUT; + + dcp_done_task(data); +} + + +static irqreturn_t dcp_common_irq(int irq, void *context) +{ + u32 msk; + struct dcp_dev *dev = (struct dcp_dev *) context; + + del_timer(&dev->watchdog); + + msk = DCP_STAT_IRQ(dcp_read(dev, DCP_REG_STAT)); + dcp_clear(dev, msk, DCP_REG_STAT); + if (msk == 0) + return IRQ_NONE; + + dev->ctx->stat |= dcp_read(dev, + dcp_chan_reg(DCP_REG_CHAN_STAT, USED_CHANNEL)); + + if (msk & DCP_STAT_CHAN_1) + tasklet_schedule(&dev->done_task); + + return IRQ_HANDLED; +} + +static irqreturn_t dcp_vmi_irq(int irq, void *context) +{ + return dcp_common_irq(irq, context); +} + +static irqreturn_t dcp_irq(int irq, void *context) +{ + return dcp_common_irq(irq, context); +} + +static void dcp_crypt(struct dcp_dev *dev, struct dcp_op *ctx) +{ + dev->ctx = ctx; + + if ((ctx->flags & DCP_CBC) && ctx->req->info) { + ctx->flags |= DCP_CBC_INIT; + memcpy(dev->payload_base + AES_KEYSIZE_128, + ctx->req->info, AES_KEYSIZE_128); + } + + dcp_op_start(dev, 1); +} + +static void dcp_queue_task(unsigned long data) +{ + struct dcp_dev *dev = (struct dcp_dev *) data; + struct crypto_async_request *async_req, *backlog; + struct crypto_ablkcipher *tfm; + struct dcp_op *ctx; + struct dcp_dev_req_ctx *rctx; + struct ablkcipher_request *req; + unsigned long flags; + + spin_lock_irqsave(&dev->queue_lock, flags); + + backlog = crypto_get_backlog(&dev->queue); + async_req = crypto_dequeue_request(&dev->queue); + + spin_unlock_irqrestore(&dev->queue_lock, flags); + + if (!async_req) + goto ret_nothing_done; + + if (backlog) + backlog->complete(backlog, -EINPROGRESS); + + req = ablkcipher_request_cast(async_req); + tfm = crypto_ablkcipher_reqtfm(req); + rctx = ablkcipher_request_ctx(req); + ctx = crypto_ablkcipher_ctx(tfm); + + if (!req->src || !req->dst) + goto ret_nothing_done; + + ctx->flags |= rctx->mode; + ctx->req = req; + + dcp_crypt(dev, ctx); + + return; + +ret_nothing_done: + clear_bit(DCP_FLAG_BUSY, &dev->flags); +} + + +static int dcp_cra_init(struct crypto_tfm *tfm) +{ + const char *name = tfm->__crt_alg->cra_name; + struct dcp_op *ctx = crypto_tfm_ctx(tfm); + + tfm->crt_ablkcipher.reqsize = sizeof(struct dcp_dev_req_ctx); + + ctx->fallback = crypto_alloc_ablkcipher(name, 0, + CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK); + + if (IS_ERR(ctx->fallback)) { + dev_err(global_dev->dev, "Error allocating fallback algo %s\n", + name); + return PTR_ERR(ctx->fallback); + } + + return 0; +} + +static void dcp_cra_exit(struct crypto_tfm *tfm) +{ + struct dcp_op *ctx = crypto_tfm_ctx(tfm); + + if (ctx->fallback) + crypto_free_ablkcipher(ctx->fallback); + + ctx->fallback = NULL; +} + +/* async interface */ +static int dcp_aes_setkey(struct crypto_ablkcipher *tfm, const u8 *key, + unsigned int len) +{ + struct dcp_op *ctx = crypto_ablkcipher_ctx(tfm); + unsigned int ret = 0; + ctx->keylen = len; + ctx->flags = 0; + if (len == AES_KEYSIZE_128) { + if (memcmp(ctx->key, key, AES_KEYSIZE_128)) { + memcpy(ctx->key, key, len); + ctx->flags |= DCP_NEW_KEY; + } + return 0; + } + + ctx->fallback->base.crt_flags &= ~CRYPTO_TFM_REQ_MASK; + ctx->fallback->base.crt_flags |= + (tfm->base.crt_flags & CRYPTO_TFM_REQ_MASK); + + ret = crypto_ablkcipher_setkey(ctx->fallback, key, len); + if (ret) { + struct crypto_tfm *tfm_aux = crypto_ablkcipher_tfm(tfm); + + tfm_aux->crt_flags &= ~CRYPTO_TFM_RES_MASK; + tfm_aux->crt_flags |= + (ctx->fallback->base.crt_flags & CRYPTO_TFM_RES_MASK); + } + return ret; +} + +static int dcp_aes_cbc_crypt(struct ablkcipher_request *req, int mode) +{ + struct dcp_dev_req_ctx *rctx = ablkcipher_request_ctx(req); + struct dcp_dev *dev = global_dev; + unsigned long flags; + int err = 0; + + if (!IS_ALIGNED(req->nbytes, AES_BLOCK_SIZE)) + return -EINVAL; + + rctx->mode = mode; + + spin_lock_irqsave(&dev->queue_lock, flags); + err = ablkcipher_enqueue_request(&dev->queue, req); + spin_unlock_irqrestore(&dev->queue_lock, flags); + + flags = test_and_set_bit(DCP_FLAG_BUSY, &dev->flags); + + if (!(flags & DCP_FLAG_BUSY)) + tasklet_schedule(&dev->queue_task); + + return err; +} + +static int dcp_aes_cbc_encrypt(struct ablkcipher_request *req) +{ + struct crypto_tfm *tfm = + crypto_ablkcipher_tfm(crypto_ablkcipher_reqtfm(req)); + struct dcp_op *ctx = crypto_ablkcipher_ctx( + crypto_ablkcipher_reqtfm(req)); + + if (unlikely(ctx->keylen != AES_KEYSIZE_128)) { + int err = 0; + ablkcipher_request_set_tfm(req, ctx->fallback); + err = crypto_ablkcipher_encrypt(req); + ablkcipher_request_set_tfm(req, __crypto_ablkcipher_cast(tfm)); + return err; + } + + return dcp_aes_cbc_crypt(req, DCP_AES | DCP_ENC | DCP_CBC); +} + +static int dcp_aes_cbc_decrypt(struct ablkcipher_request *req) +{ + struct crypto_tfm *tfm = + crypto_ablkcipher_tfm(crypto_ablkcipher_reqtfm(req)); + struct dcp_op *ctx = crypto_ablkcipher_ctx( + crypto_ablkcipher_reqtfm(req)); + + if (unlikely(ctx->keylen != AES_KEYSIZE_128)) { + int err = 0; + ablkcipher_request_set_tfm(req, ctx->fallback); + err = crypto_ablkcipher_decrypt(req); + ablkcipher_request_set_tfm(req, __crypto_ablkcipher_cast(tfm)); + return err; + } + return dcp_aes_cbc_crypt(req, DCP_AES | DCP_DEC | DCP_CBC); +} + +static struct crypto_alg algs[] = { + { + .cra_name = "cbc(aes)", + .cra_driver_name = "dcp-cbc-aes", + .cra_alignmask = 3, + .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = AES_KEYSIZE_128, + .cra_type = &crypto_ablkcipher_type, + .cra_priority = 300, + .cra_u.ablkcipher = { + .min_keysize = AES_KEYSIZE_128, + .max_keysize = AES_KEYSIZE_128, + .setkey = dcp_aes_setkey, + .encrypt = dcp_aes_cbc_encrypt, + .decrypt = dcp_aes_cbc_decrypt, + .ivsize = AES_KEYSIZE_128, + } + + }, +}; + +/* DCP bootstream verification interface: uses OTP key for crypto */ +static int dcp_bootstream_open(struct inode *inode, struct file *file) +{ + file->private_data = container_of((file->private_data), + struct dcp_dev, dcp_bootstream_misc); + return 0; +} + +static long dcp_bootstream_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct dcp_dev *dev = (struct dcp_dev *) file->private_data; + void __user *argp = (void __user *)arg; + int ret; + + if (dev == NULL) + return -EBADF; + + if (cmd != DBS_ENC && cmd != DBS_DEC) + return -EINVAL; + + if (copy_from_user(dev->payload_base, argp, 16)) + return -EFAULT; + + if (test_and_set_bit(DCP_FLAG_BUSY, &dev->flags)) + return -EAGAIN; + + dev->ctx = kzalloc(sizeof(struct dcp_op), GFP_KERNEL); + if (!dev->ctx) { + dev_err(dev->dev, + "cannot allocate context for OTP crypto"); + clear_bit(DCP_FLAG_BUSY, &dev->flags); + return -ENOMEM; + } + + dev->ctx->flags = DCP_AES | DCP_ECB | DCP_OTP_KEY | DCP_CBC_INIT; + dev->ctx->flags |= (cmd == DBS_ENC) ? DCP_ENC : DCP_DEC; + dev->hw_pkg[0]->src = dev->payload_base_dma; + dev->hw_pkg[0]->dst = dev->payload_base_dma; + dev->hw_pkg[0]->size = 16; + + dcp_op_start(dev, 0); + + while (test_bit(DCP_FLAG_BUSY, &dev->flags)) + cpu_relax(); + + ret = dev->ctx->stat; + if (!ret && copy_to_user(argp, dev->payload_base, 16)) + ret = -EFAULT; + + kfree(dev->ctx); + + return ret; +} + +static const struct file_operations dcp_bootstream_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = dcp_bootstream_ioctl, + .open = dcp_bootstream_open, +}; + +static int dcp_probe(struct platform_device *pdev) +{ + struct dcp_dev *dev = NULL; + struct resource *r; + int i, ret, j; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + dev_err(&pdev->dev, "Failed to allocate structure\n"); + ret = -ENOMEM; + goto err; + } + global_dev = dev; + dev->dev = &pdev->dev; + + platform_set_drvdata(pdev, dev); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "failed to get IORESOURCE_MEM\n"); + ret = -ENXIO; + goto err_dev; + } + dev->dcp_regs_base = ioremap(r->start, resource_size(r)); + + + dcp_set(dev, DCP_CTRL_SFRST, DCP_REG_CTRL); + udelay(10); + dcp_clear(dev, DCP_CTRL_SFRST | DCP_CTRL_CLKGATE, DCP_REG_CTRL); + + dcp_write(dev, DCP_CTRL_GATHER_RES_WRITE | + DCP_CTRL_ENABLE_CONTEXT_CACHE | DCP_CTRL_CH_IRQ_E_1, + DCP_REG_CTRL); + + dcp_write(dev, DCP_CHAN_CTRL_ENABLE_1, DCP_REG_CHAN_CTRL); + + for (i = 0; i < 4; i++) + dcp_clear(dev, -1, dcp_chan_reg(DCP_REG_CHAN_STAT, i)); + + dcp_clear(dev, -1, DCP_REG_STAT); + + + r = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!r) { + dev_err(&pdev->dev, "can't get IRQ resource (0)\n"); + ret = -EIO; + goto err_unmap_mem; + } + dev->dcp_vmi_irq = r->start; + ret = request_irq(dev->dcp_vmi_irq, dcp_vmi_irq, 0, "dcp", dev); + if (ret != 0) { + dev_err(&pdev->dev, "can't request_irq (0)\n"); + ret = -EIO; + goto err_unmap_mem; + } + + r = platform_get_resource(pdev, IORESOURCE_IRQ, 1); + if (!r) { + dev_err(&pdev->dev, "can't get IRQ resource (1)\n"); + ret = -EIO; + goto err_free_irq0; + } + dev->dcp_irq = r->start; + ret = request_irq(dev->dcp_irq, dcp_irq, 0, "dcp", dev); + if (ret != 0) { + dev_err(&pdev->dev, "can't request_irq (1)\n"); + ret = -EIO; + goto err_free_irq0; + } + + dev->hw_pkg[0] = dma_alloc_coherent(&pdev->dev, + DCP_MAX_PKG * sizeof(struct dcp_hw_packet), + &dev->hw_phys_pkg, + GFP_KERNEL); + if (!dev->hw_pkg[0]) { + dev_err(&pdev->dev, "Could not allocate hw descriptors\n"); + ret = -ENOMEM; + goto err_free_irq1; + } + + for (i = 1; i < DCP_MAX_PKG; i++) { + dev->hw_pkg[i - 1]->next = dev->hw_phys_pkg + + i * sizeof(struct dcp_hw_packet); + dev->hw_pkg[i] = dev->hw_pkg[i - 1] + 1; + } + dev->hw_pkg[i - 1]->next = dev->hw_phys_pkg; + + + dev->payload_base = dma_alloc_coherent(&pdev->dev, 2 * AES_KEYSIZE_128, + &dev->payload_base_dma, GFP_KERNEL); + if (!dev->payload_base) { + dev_err(&pdev->dev, "Could not allocate memory for key\n"); + ret = -ENOMEM; + goto err_free_hw_packet; + } + tasklet_init(&dev->queue_task, dcp_queue_task, + (unsigned long) dev); + tasklet_init(&dev->done_task, dcp_done_task, + (unsigned long) dev); + spin_lock_init(&dev->queue_lock); + + crypto_init_queue(&dev->queue, 10); + + init_timer(&dev->watchdog); + dev->watchdog.function = &dcp_watchdog; + dev->watchdog.data = (unsigned long)dev; + + dev->dcp_bootstream_misc.minor = MISC_DYNAMIC_MINOR, + dev->dcp_bootstream_misc.name = "dcpboot", + dev->dcp_bootstream_misc.fops = &dcp_bootstream_fops, + ret = misc_register(&dev->dcp_bootstream_misc); + if (ret != 0) { + dev_err(dev->dev, "Unable to register misc device\n"); + goto err_free_key_iv; + } + + for (i = 0; i < ARRAY_SIZE(algs); i++) { + algs[i].cra_priority = 300; + algs[i].cra_ctxsize = sizeof(struct dcp_op); + algs[i].cra_module = THIS_MODULE; + algs[i].cra_init = dcp_cra_init; + algs[i].cra_exit = dcp_cra_exit; + if (crypto_register_alg(&algs[i])) { + dev_err(&pdev->dev, "register algorithm failed\n"); + ret = -ENOMEM; + goto err_unregister; + } + } + dev_notice(&pdev->dev, "DCP crypto enabled.!\n"); + + return 0; + +err_unregister: + for (j = 0; j < i; j++) + crypto_unregister_alg(&algs[j]); +err_free_key_iv: + dma_free_coherent(&pdev->dev, 2 * AES_KEYSIZE_128, dev->payload_base, + dev->payload_base_dma); +err_free_hw_packet: + dma_free_coherent(&pdev->dev, DCP_MAX_PKG * + sizeof(struct dcp_hw_packet), dev->hw_pkg[0], + dev->hw_phys_pkg); +err_free_irq1: + free_irq(dev->dcp_irq, dev); +err_free_irq0: + free_irq(dev->dcp_vmi_irq, dev); +err_unmap_mem: + iounmap((void *) dev->dcp_regs_base); +err_dev: + kfree(dev); +err: + return ret; +} + +static int dcp_remove(struct platform_device *pdev) +{ + struct dcp_dev *dev; + int j; + dev = platform_get_drvdata(pdev); + platform_set_drvdata(pdev, NULL); + + dma_free_coherent(&pdev->dev, + DCP_MAX_PKG * sizeof(struct dcp_hw_packet), + dev->hw_pkg[0], dev->hw_phys_pkg); + + dma_free_coherent(&pdev->dev, 2 * AES_KEYSIZE_128, dev->payload_base, + dev->payload_base_dma); + + free_irq(dev->dcp_irq, dev); + free_irq(dev->dcp_vmi_irq, dev); + + tasklet_kill(&dev->done_task); + tasklet_kill(&dev->queue_task); + + iounmap((void *) dev->dcp_regs_base); + + for (j = 0; j < ARRAY_SIZE(algs); j++) + crypto_unregister_alg(&algs[j]); + + misc_deregister(&dev->dcp_bootstream_misc); + + kfree(dev); + return 0; +} + +static struct of_device_id fs_dcp_of_match[] = { + { .compatible = "fsl-dcp"}, + {}, +}; + +static struct platform_driver fs_dcp_driver = { + .probe = dcp_probe, + .remove = dcp_remove, + .driver = { + .name = "fsl-dcp", + .owner = THIS_MODULE, + .of_match_table = fs_dcp_of_match + } +}; + +module_platform_driver(fs_dcp_driver); + + +MODULE_AUTHOR("Tobias Rauter "); +MODULE_DESCRIPTION("Freescale DCP Crypto Driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From b02266531f3e7f9b3ce8fc95c06a15b99fd13b7f Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 20 May 2013 19:14:50 +0200 Subject: crypto: hifn_795x - Pass correct pointer to free_irq() free_irq() expects the same pointer that was passed to request_irq(), otherwise the IRQ is not freed. The issue was found using the following coccinelle script: @r1@ type T; T devid; @@ request_irq(..., devid) @r2@ type r1.T; T devid; position p; @@ free_irq@p(..., devid) @@ position p != r2.p; @@ *free_irq@p(...) Signed-off-by: Lars-Peter Clausen Signed-off-by: Herbert Xu --- drivers/crypto/hifn_795x.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/crypto/hifn_795x.c b/drivers/crypto/hifn_795x.c index ebf130e894b5..12fea3e22348 100644 --- a/drivers/crypto/hifn_795x.c +++ b/drivers/crypto/hifn_795x.c @@ -2676,7 +2676,7 @@ err_out_stop_device: hifn_reset_dma(dev, 1); hifn_stop_device(dev); err_out_free_irq: - free_irq(dev->irq, dev->name); + free_irq(dev->irq, dev); tasklet_kill(&dev->tasklet); err_out_free_desc: pci_free_consistent(pdev, sizeof(struct hifn_dma), @@ -2711,7 +2711,7 @@ static void hifn_remove(struct pci_dev *pdev) hifn_reset_dma(dev, 1); hifn_stop_device(dev); - free_irq(dev->irq, dev->name); + free_irq(dev->irq, dev); tasklet_kill(&dev->tasklet); hifn_flush(dev); -- cgit v1.2.3 From d329581493802cede83ca672093588f399d02a65 Mon Sep 17 00:00:00 2001 From: Jussi Kivilinna Date: Tue, 21 May 2013 17:10:10 +0300 Subject: crypto: sha512_generic - set cra_driver_name 'sha512_generic' should set driver name now that there is alternative sha512 provider (sha512_ssse3). Signed-off-by: Jussi Kivilinna Signed-off-by: Herbert Xu --- crypto/sha512_generic.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crypto/sha512_generic.c b/crypto/sha512_generic.c index 4c5862095679..6ed124f3ea0f 100644 --- a/crypto/sha512_generic.c +++ b/crypto/sha512_generic.c @@ -251,6 +251,7 @@ static struct shash_alg sha512_algs[2] = { { .descsize = sizeof(struct sha512_state), .base = { .cra_name = "sha512", + .cra_driver_name = "sha512-generic", .cra_flags = CRYPTO_ALG_TYPE_SHASH, .cra_blocksize = SHA512_BLOCK_SIZE, .cra_module = THIS_MODULE, @@ -263,6 +264,7 @@ static struct shash_alg sha512_algs[2] = { { .descsize = sizeof(struct sha512_state), .base = { .cra_name = "sha384", + .cra_driver_name = "sha384-generic", .cra_flags = CRYPTO_ALG_TYPE_SHASH, .cra_blocksize = SHA384_BLOCK_SIZE, .cra_module = THIS_MODULE, -- cgit v1.2.3 From 340991e30ccef7b983cf2814ecea610504f5d059 Mon Sep 17 00:00:00 2001 From: Jussi Kivilinna Date: Tue, 21 May 2013 17:10:44 +0300 Subject: crypto: sha512_ssse3 - add sha384 support Add sha384 implementation to sha512_ssse3 module. This also fixes sha512_ssse3 module autoloading issue when 'sha384' is used before 'sha512'. Previously in such case, just sha512_generic was loaded and not sha512_ssse3 (since it did not provide sha384). Now if 'sha512' was used after 'sha384' usage, sha512_ssse3 would remain unloaded. For example, this happens with tcrypt testing module since it tests 'sha384' before 'sha512'. Cc: Tim Chen Signed-off-by: Jussi Kivilinna Signed-off-by: Herbert Xu --- arch/x86/crypto/sha512_ssse3_glue.c | 58 +++++++++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/arch/x86/crypto/sha512_ssse3_glue.c b/arch/x86/crypto/sha512_ssse3_glue.c index 6cbd8df348d2..f30cd10293f0 100644 --- a/arch/x86/crypto/sha512_ssse3_glue.c +++ b/arch/x86/crypto/sha512_ssse3_glue.c @@ -194,7 +194,37 @@ static int sha512_ssse3_import(struct shash_desc *desc, const void *in) return 0; } -static struct shash_alg alg = { +static int sha384_ssse3_init(struct shash_desc *desc) +{ + struct sha512_state *sctx = shash_desc_ctx(desc); + + sctx->state[0] = SHA384_H0; + sctx->state[1] = SHA384_H1; + sctx->state[2] = SHA384_H2; + sctx->state[3] = SHA384_H3; + sctx->state[4] = SHA384_H4; + sctx->state[5] = SHA384_H5; + sctx->state[6] = SHA384_H6; + sctx->state[7] = SHA384_H7; + + sctx->count[0] = sctx->count[1] = 0; + + return 0; +} + +static int sha384_ssse3_final(struct shash_desc *desc, u8 *hash) +{ + u8 D[SHA512_DIGEST_SIZE]; + + sha512_ssse3_final(desc, D); + + memcpy(hash, D, SHA384_DIGEST_SIZE); + memset(D, 0, SHA512_DIGEST_SIZE); + + return 0; +} + +static struct shash_alg algs[] = { { .digestsize = SHA512_DIGEST_SIZE, .init = sha512_ssse3_init, .update = sha512_ssse3_update, @@ -211,7 +241,24 @@ static struct shash_alg alg = { .cra_blocksize = SHA512_BLOCK_SIZE, .cra_module = THIS_MODULE, } -}; +}, { + .digestsize = SHA384_DIGEST_SIZE, + .init = sha384_ssse3_init, + .update = sha512_ssse3_update, + .final = sha384_ssse3_final, + .export = sha512_ssse3_export, + .import = sha512_ssse3_import, + .descsize = sizeof(struct sha512_state), + .statesize = sizeof(struct sha512_state), + .base = { + .cra_name = "sha384", + .cra_driver_name = "sha384-ssse3", + .cra_priority = 150, + .cra_flags = CRYPTO_ALG_TYPE_SHASH, + .cra_blocksize = SHA384_BLOCK_SIZE, + .cra_module = THIS_MODULE, + } +} }; #ifdef CONFIG_AS_AVX static bool __init avx_usable(void) @@ -234,7 +281,7 @@ static bool __init avx_usable(void) static int __init sha512_ssse3_mod_init(void) { - /* test for SSE3 first */ + /* test for SSSE3 first */ if (cpu_has_ssse3) sha512_transform_asm = sha512_transform_ssse3; @@ -261,7 +308,7 @@ static int __init sha512_ssse3_mod_init(void) else #endif pr_info("Using SSSE3 optimized SHA-512 implementation\n"); - return crypto_register_shash(&alg); + return crypto_register_shashes(algs, ARRAY_SIZE(algs)); } pr_info("Neither AVX nor SSSE3 is available/usable.\n"); @@ -270,7 +317,7 @@ static int __init sha512_ssse3_mod_init(void) static void __exit sha512_ssse3_mod_fini(void) { - crypto_unregister_shash(&alg); + crypto_unregister_shashes(algs, ARRAY_SIZE(algs)); } module_init(sha512_ssse3_mod_init); @@ -280,3 +327,4 @@ MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("SHA512 Secure Hash Algorithm, Supplemental SSE3 accelerated"); MODULE_ALIAS("sha512"); +MODULE_ALIAS("sha384"); -- cgit v1.2.3 From a710f761fc9ae5728765a5917f8beabb49f98483 Mon Sep 17 00:00:00 2001 From: Jussi Kivilinna Date: Tue, 21 May 2013 17:10:49 +0300 Subject: crypto: sha256_ssse3 - add sha224 support Add sha224 implementation to sha256_ssse3 module. This also fixes sha256_ssse3 module autoloading issue when 'sha224' is used before 'sha256'. Previously in such case, just sha256_generic was loaded and not sha256_ssse3 (since it did not provide sha224). Now if 'sha256' was used after 'sha224' usage, sha256_ssse3 would remain unloaded. Cc: Tim Chen Signed-off-by: Jussi Kivilinna Signed-off-by: Herbert Xu --- arch/x86/crypto/sha256_ssse3_glue.c | 57 +++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/arch/x86/crypto/sha256_ssse3_glue.c b/arch/x86/crypto/sha256_ssse3_glue.c index 597d4da69656..50226c4b86ed 100644 --- a/arch/x86/crypto/sha256_ssse3_glue.c +++ b/arch/x86/crypto/sha256_ssse3_glue.c @@ -187,7 +187,36 @@ static int sha256_ssse3_import(struct shash_desc *desc, const void *in) return 0; } -static struct shash_alg alg = { +static int sha224_ssse3_init(struct shash_desc *desc) +{ + struct sha256_state *sctx = shash_desc_ctx(desc); + + sctx->state[0] = SHA224_H0; + sctx->state[1] = SHA224_H1; + sctx->state[2] = SHA224_H2; + sctx->state[3] = SHA224_H3; + sctx->state[4] = SHA224_H4; + sctx->state[5] = SHA224_H5; + sctx->state[6] = SHA224_H6; + sctx->state[7] = SHA224_H7; + sctx->count = 0; + + return 0; +} + +static int sha224_ssse3_final(struct shash_desc *desc, u8 *hash) +{ + u8 D[SHA256_DIGEST_SIZE]; + + sha256_ssse3_final(desc, D); + + memcpy(hash, D, SHA224_DIGEST_SIZE); + memset(D, 0, SHA256_DIGEST_SIZE); + + return 0; +} + +static struct shash_alg algs[] = { { .digestsize = SHA256_DIGEST_SIZE, .init = sha256_ssse3_init, .update = sha256_ssse3_update, @@ -204,7 +233,24 @@ static struct shash_alg alg = { .cra_blocksize = SHA256_BLOCK_SIZE, .cra_module = THIS_MODULE, } -}; +}, { + .digestsize = SHA224_DIGEST_SIZE, + .init = sha224_ssse3_init, + .update = sha256_ssse3_update, + .final = sha224_ssse3_final, + .export = sha256_ssse3_export, + .import = sha256_ssse3_import, + .descsize = sizeof(struct sha256_state), + .statesize = sizeof(struct sha256_state), + .base = { + .cra_name = "sha224", + .cra_driver_name = "sha224-ssse3", + .cra_priority = 150, + .cra_flags = CRYPTO_ALG_TYPE_SHASH, + .cra_blocksize = SHA224_BLOCK_SIZE, + .cra_module = THIS_MODULE, + } +} }; #ifdef CONFIG_AS_AVX static bool __init avx_usable(void) @@ -227,7 +273,7 @@ static bool __init avx_usable(void) static int __init sha256_ssse3_mod_init(void) { - /* test for SSE3 first */ + /* test for SSSE3 first */ if (cpu_has_ssse3) sha256_transform_asm = sha256_transform_ssse3; @@ -254,7 +300,7 @@ static int __init sha256_ssse3_mod_init(void) else #endif pr_info("Using SSSE3 optimized SHA-256 implementation\n"); - return crypto_register_shash(&alg); + return crypto_register_shashes(algs, ARRAY_SIZE(algs)); } pr_info("Neither AVX nor SSSE3 is available/usable.\n"); @@ -263,7 +309,7 @@ static int __init sha256_ssse3_mod_init(void) static void __exit sha256_ssse3_mod_fini(void) { - crypto_unregister_shash(&alg); + crypto_unregister_shashes(algs, ARRAY_SIZE(algs)); } module_init(sha256_ssse3_mod_init); @@ -273,3 +319,4 @@ MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("SHA256 Secure Hash Algorithm, Supplemental SSE3 accelerated"); MODULE_ALIAS("sha256"); +MODULE_ALIAS("sha384"); -- cgit v1.2.3 From 1eaff67266b6b6c97bbd33cf2c20577822836413 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 28 May 2013 09:48:46 +0200 Subject: clocksource: Implement clocksource_select_fallback() for CONFIG_ARCH_USES_GETTIMEOFFSET=y commit 7eaeb34305 (clocksource: Provide unbind interface in sysfs) implemented clocksource_select_fallback() which is not defined for CONFIG_ARCH_USES_GETTIMEOFFSET=y. Add an empty inline function for that. Reported-by: Ingo Molnar Reported-by: fengguang.wu@intel.com Signed-off-by: Thomas Gleixner --- kernel/time/clocksource.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index 6d05b00410cc..e713ef7d19a7 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c @@ -632,6 +632,7 @@ static void clocksource_select_fallback(void) #else /* !CONFIG_ARCH_USES_GETTIMEOFFSET */ static inline void clocksource_select(void) { } +static inline void clocksource_select_fallback(void) { } #endif -- cgit v1.2.3 From 3e32e0b8ebb4fd1939fd672b6a16f884357b4411 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 27 May 2013 23:39:41 -0700 Subject: Input: ixp4xx-beeper - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Dmitry Torokhov --- drivers/input/misc/ixp4xx-beeper.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/input/misc/ixp4xx-beeper.c b/drivers/input/misc/ixp4xx-beeper.c index deab4a11350a..f34beb228d36 100644 --- a/drivers/input/misc/ixp4xx-beeper.c +++ b/drivers/input/misc/ixp4xx-beeper.c @@ -138,7 +138,6 @@ static int ixp4xx_spkr_remove(struct platform_device *dev) unsigned int pin = (unsigned int) input_get_drvdata(input_dev); input_unregister_device(input_dev); - platform_set_drvdata(dev, NULL); /* turn the speaker off */ disable_irq(IRQ_IXP4XX_TIMER2); -- cgit v1.2.3 From 30a6aa1f6e9f3482ec89197d06e91c828f7a4b24 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 27 May 2013 23:40:01 -0700 Subject: Input: m68kspkr - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Dmitry Torokhov --- drivers/input/misc/m68kspkr.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/input/misc/m68kspkr.c b/drivers/input/misc/m68kspkr.c index b40ee4b47f4f..def21dc84522 100644 --- a/drivers/input/misc/m68kspkr.c +++ b/drivers/input/misc/m68kspkr.c @@ -85,7 +85,6 @@ static int m68kspkr_remove(struct platform_device *dev) struct input_dev *input_dev = platform_get_drvdata(dev); input_unregister_device(input_dev); - platform_set_drvdata(dev, NULL); /* turn off the speaker */ m68kspkr_event(NULL, EV_SND, SND_BELL, 0); -- cgit v1.2.3 From 16e263aca0cecef9522558b63b149e966aa43654 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 27 May 2013 23:40:15 -0700 Subject: Input: pcspkr - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Dmitry Torokhov --- drivers/input/misc/pcspkr.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/input/misc/pcspkr.c b/drivers/input/misc/pcspkr.c index 199db78acc4f..7288b267613d 100644 --- a/drivers/input/misc/pcspkr.c +++ b/drivers/input/misc/pcspkr.c @@ -100,7 +100,6 @@ static int pcspkr_remove(struct platform_device *dev) struct input_dev *pcspkr_dev = platform_get_drvdata(dev); input_unregister_device(pcspkr_dev); - platform_set_drvdata(dev, NULL); /* turn off the speaker */ pcspkr_event(NULL, EV_SND, SND_BELL, 0); -- cgit v1.2.3 From f6e63f8032571bb58d054071edfd35d88f9dd07a Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 27 May 2013 23:40:33 -0700 Subject: Input: xilinx_ps2 - remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Signed-off-by: Dmitry Torokhov --- drivers/input/serio/xilinx_ps2.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/input/serio/xilinx_ps2.c b/drivers/input/serio/xilinx_ps2.c index 17be85948ffd..4b7662a17ae9 100644 --- a/drivers/input/serio/xilinx_ps2.c +++ b/drivers/input/serio/xilinx_ps2.c @@ -349,8 +349,6 @@ static int xps2_of_remove(struct platform_device *of_dev) kfree(drvdata); - platform_set_drvdata(of_dev, NULL); - return 0; } -- cgit v1.2.3 From b96516220870d8571fc12da980668687c122976e Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Tue, 28 May 2013 09:52:05 +0200 Subject: rcu: Fix Documentation typo 'CONFIG_RCU_NOCB_CPUS' Signed-off-by: Paul Bolle Signed-off-by: Jiri Kosina --- Documentation/kernel-per-CPU-kthreads.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/kernel-per-CPU-kthreads.txt b/Documentation/kernel-per-CPU-kthreads.txt index cbf7ae412da4..d0fcc5bb9227 100644 --- a/Documentation/kernel-per-CPU-kthreads.txt +++ b/Documentation/kernel-per-CPU-kthreads.txt @@ -185,7 +185,7 @@ Purpose: Offload RCU callbacks from the corresponding CPU. To reduce its OS jitter, do at least one of the following: 1. Use affinity, cgroups, or other mechanism to force these kthreads to execute on some other CPU. -2. Build with CONFIG_RCU_NOCB_CPUS=n, which will prevent these +2. Build with CONFIG_RCU_NOCB_CPU=n, which will prevent these kthreads from being created in the first place. However, please note that this will not eliminate OS jitter, but will instead shift it to RCU_SOFTIRQ. -- cgit v1.2.3 From f04d51404f51947d3feabf2518495ba5aa3bb2c4 Mon Sep 17 00:00:00 2001 From: Colin Leitner Date: Mon, 27 May 2013 23:41:05 +0200 Subject: HID: driver for PS2/3 Buzz controllers This patch adds support for PS2/3 Buzz controllers into hid-sony It has been tested on Debian 7 with kernel version 3.10.0-rc2. Unfortunately I can't test the patch with a regular six-axis controller myself. Signed-off-by: Colin Leitner Cc: Jiri Kosina Cc: linux-input@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 9 +- drivers/hid/hid-core.c | 2 + drivers/hid/hid-ids.h | 2 + drivers/hid/hid-sony.c | 310 +++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 312 insertions(+), 11 deletions(-) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index fb52f3f6de80..53caf0b115cb 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -594,10 +594,15 @@ config HID_SAMSUNG Support for Samsung InfraRed remote control or keyboards. config HID_SONY - tristate "Sony PS3 controller" + tristate "Sony PS2/3 accessories" depends on USB_HID + select NEW_LEDS + select LEDS_CLASS ---help--- - Support for Sony PS3 6-axis controllers. + Support for + + * Sony PS3 6-axis controllers + * Buzz controllers Support for the Sony PS3 BD Remote is provided by HID_PS3REMOTE. diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 264f55099940..ecd1107a5488 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1680,6 +1680,8 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SKYCABLE, USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_BUZZ_CONTROLLER) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_BDREMOTE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 38535c9243d5..508c0072b630 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -734,6 +734,8 @@ #define USB_DEVICE_ID_SONY_PS3_BDREMOTE 0x0306 #define USB_DEVICE_ID_SONY_PS3_CONTROLLER 0x0268 #define USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER 0x042f +#define USB_DEVICE_ID_SONY_BUZZ_CONTROLLER 0x0002 +#define USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER 0x1000 #define USB_VENDOR_ID_SOUNDGRAPH 0x15c2 #define USB_DEVICE_ID_SOUNDGRAPH_IMON_FIRST 0x0034 diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 312098e4af4f..41e829c8bc48 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -6,6 +6,7 @@ * Copyright (c) 2005 Michael Haboustak for Concept2, Inc * Copyright (c) 2008 Jiri Slaby * Copyright (c) 2006-2008 Jiri Kosina + * Copyright (c) 2013 Colin Leitner */ /* @@ -26,6 +27,7 @@ #define VAIO_RDESC_CONSTANT (1 << 0) #define SIXAXIS_CONTROLLER_USB (1 << 1) #define SIXAXIS_CONTROLLER_BT (1 << 2) +#define BUZZ_CONTROLLER (1 << 3) static const u8 sixaxis_rdesc_fixup[] = { 0x95, 0x13, 0x09, 0x01, 0x81, 0x02, 0x95, 0x0C, @@ -55,8 +57,56 @@ static const u8 sixaxis_rdesc_fixup2[] = { 0xb1, 0x02, 0xc0, 0xc0, }; +static const unsigned int buzz_keymap[] = { + /* The controller has 4 remote buzzers, each with one LED and 5 + * buttons. + * + * We use the mapping chosen by the controller, which is: + * + * Key Offset + * ------------------- + * Buzz 1 + * Blue 5 + * Orange 4 + * Green 3 + * Yellow 2 + * + * So, for example, the orange button on the third buzzer is mapped to + * BTN_TRIGGER_HAPPY14 + */ + [ 1] = BTN_TRIGGER_HAPPY1, + [ 2] = BTN_TRIGGER_HAPPY2, + [ 3] = BTN_TRIGGER_HAPPY3, + [ 4] = BTN_TRIGGER_HAPPY4, + [ 5] = BTN_TRIGGER_HAPPY5, + [ 6] = BTN_TRIGGER_HAPPY6, + [ 7] = BTN_TRIGGER_HAPPY7, + [ 8] = BTN_TRIGGER_HAPPY8, + [ 9] = BTN_TRIGGER_HAPPY9, + [10] = BTN_TRIGGER_HAPPY10, + [11] = BTN_TRIGGER_HAPPY11, + [12] = BTN_TRIGGER_HAPPY12, + [13] = BTN_TRIGGER_HAPPY13, + [14] = BTN_TRIGGER_HAPPY14, + [15] = BTN_TRIGGER_HAPPY15, + [16] = BTN_TRIGGER_HAPPY16, + [17] = BTN_TRIGGER_HAPPY17, + [18] = BTN_TRIGGER_HAPPY18, + [19] = BTN_TRIGGER_HAPPY19, + [20] = BTN_TRIGGER_HAPPY20, +}; + struct sony_sc { unsigned long quirks; + + void *extra; +}; + +struct buzz_extra { +#ifdef CONFIG_LEDS_CLASS + int led_state; + struct led_classdev *leds[4]; +#endif }; /* Sony Vaio VGX has wrongly mouse pointer declared as constant */ @@ -117,6 +167,38 @@ static int sony_raw_event(struct hid_device *hdev, struct hid_report *report, return 0; } +static int sony_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + struct sony_sc *sc = hid_get_drvdata(hdev); + + if (sc->quirks & BUZZ_CONTROLLER) { + unsigned int key = usage->hid & HID_USAGE; + + if ((usage->hid & HID_USAGE_PAGE) != HID_UP_BUTTON) + return -1; + + switch (usage->collection_index) { + case 1: + if (key >= ARRAY_SIZE(buzz_keymap)) + return -1; + + key = buzz_keymap[key]; + if (!key) + return -1; + break; + default: + return -1; + } + + hid_map_usage_clear(hi, usage, bit, max, EV_KEY, key); + return 1; + } + + return -1; +} + /* * The Sony Sixaxis does not handle HID Output Reports on the Interrupt EP * like it should according to usbhid/hid-core.c::usbhid_output_raw_report() @@ -192,11 +274,201 @@ static int sixaxis_set_operational_bt(struct hid_device *hdev) return hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT); } +#ifdef CONFIG_LEDS_CLASS +static void buzz_set_leds(struct hid_device *hdev, int leds) +{ + struct list_head *report_list = + &hdev->report_enum[HID_OUTPUT_REPORT].report_list; + struct hid_report *report = list_entry(report_list->next, + struct hid_report, list); + __s32 *value = report->field[0]->value; + + value[0] = 0x00; + value[1] = (leds & 1) ? 0xff : 0x00; + value[2] = (leds & 2) ? 0xff : 0x00; + value[3] = (leds & 4) ? 0xff : 0x00; + value[4] = (leds & 8) ? 0xff : 0x00; + value[5] = 0x00; + value[6] = 0x00; + hid_hw_request(hdev, report, HID_REQ_SET_REPORT); +} + +static void buzz_led_set_brightness(struct led_classdev *led, + enum led_brightness value) +{ + struct device *dev = led->dev->parent; + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct sony_sc *drv_data; + struct buzz_extra *buzz; + + int n; + + drv_data = hid_get_drvdata(hdev); + if (!drv_data || !drv_data->extra) { + hid_err(hdev, "No device data\n"); + return; + } + buzz = drv_data->extra; + + for (n = 0; n < 4; n++) { + if (led == buzz->leds[n]) { + int on = !! (buzz->led_state & (1 << n)); + if (value == LED_OFF && on) { + buzz->led_state &= ~(1 << n); + buzz_set_leds(hdev, buzz->led_state); + } else if (value != LED_OFF && !on) { + buzz->led_state |= (1 << n); + buzz_set_leds(hdev, buzz->led_state); + } + break; + } + } +} + +static enum led_brightness buzz_led_get_brightness(struct led_classdev *led) +{ + struct device *dev = led->dev->parent; + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct sony_sc *drv_data; + struct buzz_extra *buzz; + + int n; + int on = 0; + + drv_data = hid_get_drvdata(hdev); + if (!drv_data || !drv_data->extra) { + hid_err(hdev, "No device data\n"); + return LED_OFF; + } + buzz = drv_data->extra; + + for (n = 0; n < 4; n++) { + if (led == buzz->leds[n]) { + on = !! (buzz->led_state & (1 << n)); + break; + } + } + + return on ? LED_FULL : LED_OFF; +} +#endif + +static int buzz_init(struct hid_device *hdev) +{ + struct sony_sc *drv_data; + struct buzz_extra *buzz; + int ret = 0; + + drv_data = hid_get_drvdata(hdev); + BUG_ON(!(drv_data->quirks & BUZZ_CONTROLLER)); + + buzz = kzalloc(sizeof(*buzz), GFP_KERNEL); + if (!buzz) { + hid_err(hdev, "Insufficient memory, cannot allocate driver data\n"); + return -ENOMEM; + } + drv_data->extra = buzz; + + /* Clear LEDs as we have no way of reading their initial state. This is + * only relevant if the driver is loaded after somebody actively set the + * LEDs to on */ + buzz_set_leds(hdev, 0x00); + +#ifdef CONFIG_LEDS_CLASS + { + int n; + struct led_classdev *led; + size_t name_sz; + char *name; + + name_sz = strlen(dev_name(&hdev->dev)) + strlen("::buzz#") + 1; + + for (n = 0; n < 4; n++) { + led = kzalloc(sizeof(struct led_classdev) + name_sz, GFP_KERNEL); + if (!led) { + hid_err(hdev, "Couldn't allocate memory for LED %d\n", n); + goto error_leds; + } + + name = (void *)(&led[1]); + snprintf(name, name_sz, "%s::buzz%d", dev_name(&hdev->dev), n + 1); + led->name = name; + led->brightness = 0; + led->max_brightness = 1; + led->brightness_get = buzz_led_get_brightness; + led->brightness_set = buzz_led_set_brightness; + + if (led_classdev_register(&hdev->dev, led)) { + hid_err(hdev, "Failed to register LED %d\n", n); + kfree(led); + goto error_leds; + } + + buzz->leds[n] = led; + } + } +#endif + + return ret; + +#ifdef CONFIG_LEDS_CLASS +error_leds: + { + int n; + struct led_classdev *led; + + for (n = 0; n < 4; n++) { + led = buzz->leds[n]; + buzz->leds[n] = NULL; + if (!led) + continue; + led_classdev_unregister(led); + kfree(led); + } + } + + kfree(drv_data->extra); + drv_data->extra = NULL; + return ret; +#endif +} + +static void buzz_remove(struct hid_device *hdev) +{ + struct sony_sc *drv_data; + struct buzz_extra *buzz; + + drv_data = hid_get_drvdata(hdev); + BUG_ON(!(drv_data->quirks & BUZZ_CONTROLLER)); + + buzz = drv_data->extra; + +#ifdef CONFIG_LEDS_CLASS + { + int n; + struct led_classdev *led; + + for (n = 0; n < 4; n++) { + led = buzz->leds[n]; + buzz->leds[n] = NULL; + if (!led) + continue; + led_classdev_unregister(led); + kfree(led); + } + } +#endif + + kfree(drv_data->extra); + drv_data->extra = NULL; +} + static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) { int ret; unsigned long quirks = id->driver_data; struct sony_sc *sc; + unsigned int connect_mask = HID_CONNECT_DEFAULT; sc = kzalloc(sizeof(*sc), GFP_KERNEL); if (sc == NULL) { @@ -213,8 +485,14 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) goto err_free; } - ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT | - HID_CONNECT_HIDDEV_FORCE); + if (sc->quirks & VAIO_RDESC_CONSTANT) + connect_mask |= HID_CONNECT_HIDDEV_FORCE; + else if (sc->quirks & SIXAXIS_CONTROLLER_USB) + connect_mask |= HID_CONNECT_HIDDEV_FORCE; + else if (sc->quirks & SIXAXIS_CONTROLLER_BT) + connect_mask |= HID_CONNECT_HIDDEV_FORCE; + + ret = hid_hw_start(hdev, connect_mask); if (ret) { hid_err(hdev, "hw start failed\n"); goto err_free; @@ -226,6 +504,8 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) } else if (sc->quirks & SIXAXIS_CONTROLLER_BT) ret = sixaxis_set_operational_bt(hdev); + else if (sc->quirks & BUZZ_CONTROLLER) + ret = buzz_init(hdev); else ret = 0; @@ -242,8 +522,13 @@ err_free: static void sony_remove(struct hid_device *hdev) { + struct sony_sc *sc = hid_get_drvdata(hdev); + + if (sc->quirks & BUZZ_CONTROLLER) + buzz_remove(hdev); + hid_hw_stop(hdev); - kfree(hid_get_drvdata(hdev)); + kfree(sc); } static const struct hid_device_id sony_devices[] = { @@ -257,17 +542,24 @@ static const struct hid_device_id sony_devices[] = { .driver_data = VAIO_RDESC_CONSTANT }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE), .driver_data = VAIO_RDESC_CONSTANT }, + /* Wired Buzz Controller. Reported as Sony Hub from its USB ID and as + * Logitech joystick from the device descriptor. */ + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_BUZZ_CONTROLLER), + .driver_data = BUZZ_CONTROLLER }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER), + .driver_data = BUZZ_CONTROLLER }, { } }; MODULE_DEVICE_TABLE(hid, sony_devices); static struct hid_driver sony_driver = { - .name = "sony", - .id_table = sony_devices, - .probe = sony_probe, - .remove = sony_remove, - .report_fixup = sony_report_fixup, - .raw_event = sony_raw_event + .name = "sony", + .id_table = sony_devices, + .input_mapping = sony_mapping, + .probe = sony_probe, + .remove = sony_remove, + .report_fixup = sony_report_fixup, + .raw_event = sony_raw_event }; module_hid_driver(sony_driver); -- cgit v1.2.3 From bbb85b25688fcdc70b895711870c23de7b12721b Mon Sep 17 00:00:00 2001 From: Thomas Meyer Date: Wed, 22 May 2013 21:38:40 +0200 Subject: crypto: ux500 - Cocci spatch "resource_size.spatch" Signed-off-by: Thomas Meyer Signed-off-by: Herbert Xu --- drivers/crypto/ux500/cryp/cryp_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/crypto/ux500/cryp/cryp_core.c b/drivers/crypto/ux500/cryp/cryp_core.c index 32f480622b97..3f1f9825fadb 100644 --- a/drivers/crypto/ux500/cryp/cryp_core.c +++ b/drivers/crypto/ux500/cryp/cryp_core.c @@ -1600,7 +1600,7 @@ static int ux500_cryp_remove(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res) - release_mem_region(res->start, res->end - res->start + 1); + release_mem_region(res->start, resource_size(res)); kfree(device_data); -- cgit v1.2.3 From 40e32ee6e475852f532c847ed1804b87f6531a67 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Tue, 28 May 2013 11:22:09 +0200 Subject: HID: sony: fix leds dependency The newly added support for Buzz controller - introduced Kconfig selection of LEDS_CLASS - introduced conditional preprocessor checking for CONFIG_LEDS_CLASS This has multiple problems -- namely select doesn't work transitively, so it shouldn't be used. On the other hand the code assumed that LEDS_CLASS is enabled in some places, but not everywhere. Put LEDS_CLASS as a Kconfig dependency for hid-sony and remove all the CONFIG_LEDS_CLASS conditionals from hid-sony. Reported-by: fengguang.wu@intel.com Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 4 +- drivers/hid/hid-sony.c | 105 ++++++++++++++++++++----------------------------- 2 files changed, 44 insertions(+), 65 deletions(-) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 53caf0b115cb..5bad26ef227a 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -596,8 +596,8 @@ config HID_SAMSUNG config HID_SONY tristate "Sony PS2/3 accessories" depends on USB_HID - select NEW_LEDS - select LEDS_CLASS + depends on NEW_LEDS + depends on LEDS_CLASS ---help--- Support for diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 41e829c8bc48..f1c906176615 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "hid-ids.h" @@ -103,10 +104,8 @@ struct sony_sc { }; struct buzz_extra { -#ifdef CONFIG_LEDS_CLASS int led_state; struct led_classdev *leds[4]; -#endif }; /* Sony Vaio VGX has wrongly mouse pointer declared as constant */ @@ -274,7 +273,6 @@ static int sixaxis_set_operational_bt(struct hid_device *hdev) return hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT); } -#ifdef CONFIG_LEDS_CLASS static void buzz_set_leds(struct hid_device *hdev, int leds) { struct list_head *report_list = @@ -351,13 +349,15 @@ static enum led_brightness buzz_led_get_brightness(struct led_classdev *led) return on ? LED_FULL : LED_OFF; } -#endif static int buzz_init(struct hid_device *hdev) { struct sony_sc *drv_data; struct buzz_extra *buzz; - int ret = 0; + int n, ret = 0; + struct led_classdev *led; + size_t name_sz; + char *name; drv_data = hid_get_drvdata(hdev); BUG_ON(!(drv_data->quirks & BUZZ_CONTROLLER)); @@ -374,90 +374,69 @@ static int buzz_init(struct hid_device *hdev) * LEDs to on */ buzz_set_leds(hdev, 0x00); -#ifdef CONFIG_LEDS_CLASS - { - int n; - struct led_classdev *led; - size_t name_sz; - char *name; + name_sz = strlen(dev_name(&hdev->dev)) + strlen("::buzz#") + 1; - name_sz = strlen(dev_name(&hdev->dev)) + strlen("::buzz#") + 1; - - for (n = 0; n < 4; n++) { - led = kzalloc(sizeof(struct led_classdev) + name_sz, GFP_KERNEL); - if (!led) { - hid_err(hdev, "Couldn't allocate memory for LED %d\n", n); - goto error_leds; - } + for (n = 0; n < 4; n++) { + led = kzalloc(sizeof(struct led_classdev) + name_sz, GFP_KERNEL); + if (!led) { + hid_err(hdev, "Couldn't allocate memory for LED %d\n", n); + goto error_leds; + } - name = (void *)(&led[1]); - snprintf(name, name_sz, "%s::buzz%d", dev_name(&hdev->dev), n + 1); - led->name = name; - led->brightness = 0; - led->max_brightness = 1; - led->brightness_get = buzz_led_get_brightness; - led->brightness_set = buzz_led_set_brightness; - - if (led_classdev_register(&hdev->dev, led)) { - hid_err(hdev, "Failed to register LED %d\n", n); - kfree(led); - goto error_leds; - } + name = (void *)(&led[1]); + snprintf(name, name_sz, "%s::buzz%d", dev_name(&hdev->dev), n + 1); + led->name = name; + led->brightness = 0; + led->max_brightness = 1; + led->brightness_get = buzz_led_get_brightness; + led->brightness_set = buzz_led_set_brightness; - buzz->leds[n] = led; + if (led_classdev_register(&hdev->dev, led)) { + hid_err(hdev, "Failed to register LED %d\n", n); + kfree(led); + goto error_leds; } + + buzz->leds[n] = led; } -#endif return ret; -#ifdef CONFIG_LEDS_CLASS error_leds: - { - int n; - struct led_classdev *led; - - for (n = 0; n < 4; n++) { - led = buzz->leds[n]; - buzz->leds[n] = NULL; - if (!led) - continue; - led_classdev_unregister(led); - kfree(led); - } + for (n = 0; n < 4; n++) { + led = buzz->leds[n]; + buzz->leds[n] = NULL; + if (!led) + continue; + led_classdev_unregister(led); + kfree(led); } kfree(drv_data->extra); drv_data->extra = NULL; return ret; -#endif } static void buzz_remove(struct hid_device *hdev) { struct sony_sc *drv_data; struct buzz_extra *buzz; + struct led_classdev *led; + int n; drv_data = hid_get_drvdata(hdev); BUG_ON(!(drv_data->quirks & BUZZ_CONTROLLER)); buzz = drv_data->extra; - -#ifdef CONFIG_LEDS_CLASS - { - int n; - struct led_classdev *led; - - for (n = 0; n < 4; n++) { - led = buzz->leds[n]; - buzz->leds[n] = NULL; - if (!led) - continue; - led_classdev_unregister(led); - kfree(led); - } + + for (n = 0; n < 4; n++) { + led = buzz->leds[n]; + buzz->leds[n] = NULL; + if (!led) + continue; + led_classdev_unregister(led); + kfree(led); } -#endif kfree(drv_data->extra); drv_data->extra = NULL; -- cgit v1.2.3 From 8ef726cb23717b8b064ed4634202c9f43776a547 Mon Sep 17 00:00:00 2001 From: Thorsten Glaser Date: Tue, 23 Apr 2013 14:19:55 +0000 Subject: update AMD powerflags comments from http://www.flounder.com/cpuid80000007amd.gif and http://support.amd.com/us/Embedded_TechDocs/25481.pdf Signed-off-by: Thorsten Glaser Signed-off-by: Jiri Kosina --- arch/x86/kernel/cpu/powerflags.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/x86/kernel/cpu/powerflags.c b/arch/x86/kernel/cpu/powerflags.c index 7b3fe56b1c21..31f0f335ed22 100644 --- a/arch/x86/kernel/cpu/powerflags.c +++ b/arch/x86/kernel/cpu/powerflags.c @@ -11,10 +11,10 @@ const char *const x86_power_flags[32] = { "fid", /* frequency id control */ "vid", /* voltage id control */ "ttp", /* thermal trip */ - "tm", - "stc", - "100mhzsteps", - "hwpstate", + "tm", /* hardware thermal control */ + "stc", /* software thermal control */ + "100mhzsteps", /* 100 MHz multiplier control */ + "hwpstate", /* hardware P-state control */ "", /* tsc invariant mapped to constant_tsc */ "cpb", /* core performance boost */ "eff_freq_ro", /* Readonly aperf/mperf */ -- cgit v1.2.3 From bcf312cf762f3ff02c0cbd218564e6241f0d1f9f Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Thu, 2 May 2013 11:26:13 -0400 Subject: tracing: Put trace_puts() comment above trace_puts() macro for kernel doc Kernel-doc gives the following warning: DOCPROC Documentation/DocBook/kernel-api.xml Warning(/include/linux/kernel.h:590): No description found for parameter 'ip' Warning(/include/linux/kernel.h:590): No description found for parameter 'ip' Due to the externs between the the comment and the trace_puts() macro. This is fixed by moving the externs below the macro and keeping the macro and comment directly together. Reported-by: Robert P. J. Day Signed-off-by: Steven Rostedt Signed-off-by: Jiri Kosina --- include/linux/kernel.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/include/linux/kernel.h b/include/linux/kernel.h index e9ef6d6b51d5..bd95d1face2f 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -562,9 +562,6 @@ int __trace_bprintk(unsigned long ip, const char *fmt, ...); extern __printf(2, 3) int __trace_printk(unsigned long ip, const char *fmt, ...); -extern int __trace_bputs(unsigned long ip, const char *str); -extern int __trace_puts(unsigned long ip, const char *str, int size); - /** * trace_puts - write a string into the ftrace buffer * @str: the string to record @@ -600,6 +597,8 @@ extern int __trace_puts(unsigned long ip, const char *str, int size); else \ __trace_puts(_THIS_IP_, str, strlen(str)); \ }) +extern int __trace_bputs(unsigned long ip, const char *str); +extern int __trace_puts(unsigned long ip, const char *str, int size); extern void trace_dump_stack(int skip); -- cgit v1.2.3 From 01462c23e63391c39687b4e4898f5ed38c6b1799 Mon Sep 17 00:00:00 2001 From: "Robert P. J. Day" Date: Thu, 2 May 2013 11:27:03 -0400 Subject: kernel-doc: Update references to SGML to refs to XML instead. Since the "sgmldocs" target is simply a pointer to the newer "xmldocs" target, update the documentation to reflect this. Signed-off-by: Robert P. J. Day Signed-off-by: Jiri Kosina --- Documentation/kernel-doc-nano-HOWTO.txt | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Documentation/kernel-doc-nano-HOWTO.txt b/Documentation/kernel-doc-nano-HOWTO.txt index 99b57abddf8a..acbc1a3d0d91 100644 --- a/Documentation/kernel-doc-nano-HOWTO.txt +++ b/Documentation/kernel-doc-nano-HOWTO.txt @@ -142,9 +142,10 @@ are: - Makefile - The targets 'sgmldocs', 'psdocs', 'pdfdocs', and 'htmldocs' are used - to build DocBook files, PostScript files, PDF files, and html files - in Documentation/DocBook. + The targets 'xmldocs', 'psdocs', 'pdfdocs', and 'htmldocs' are used + to build XML DocBook files, PostScript files, PDF files, and html files + in Documentation/DocBook. The older target 'sgmldocs' is equivalent + to 'xmldocs'. - Documentation/DocBook/Makefile @@ -158,8 +159,8 @@ If you just want to read the ready-made books on the various subsystems (see Documentation/DocBook/*.tmpl), just type 'make psdocs', or 'make pdfdocs', or 'make htmldocs', depending on your preference. If you would rather read a different format, you can type -'make sgmldocs' and then use DocBook tools to convert -Documentation/DocBook/*.sgml to a format of your choice (for example, +'make xmldocs' and then use DocBook tools to convert +Documentation/DocBook/*.xml to a format of your choice (for example, 'db2html ...' if 'make htmldocs' was not defined). If you want to see man pages instead, you can do this: -- cgit v1.2.3 From 611a75e1874c96b06517dfc5357b0b15926e1251 Mon Sep 17 00:00:00 2001 From: "Robert P. J. Day" Date: Fri, 3 May 2013 06:45:48 -0400 Subject: include/linux/cpu.h: Update comments to reflect reality Two minor changes to comments: * Remove reference to drivers/base/sys.c, removed in 0a962657. * CPUs are now exported by sysfs via devices/system/cpu. Signed-off-by: Robert P. J. Day Signed-off-by: Jiri Kosina --- include/linux/cpu.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/linux/cpu.h b/include/linux/cpu.h index c6f6e0839b61..0d868d37bb8e 100644 --- a/include/linux/cpu.h +++ b/include/linux/cpu.h @@ -6,9 +6,8 @@ * definitions of processors. * * Basic handling of the devices is done in drivers/base/cpu.c - * and system devices are handled in drivers/base/sys.c. * - * CPUs are exported via sysfs in the class/cpu/devices/ + * CPUs are exported via sysfs in the devices/system/cpu * directory. */ #ifndef _LINUX_CPU_H_ -- cgit v1.2.3 From 7e21f14d179ee8973a9b18552854c9934fcbe370 Mon Sep 17 00:00:00 2001 From: Stefan Behrens Date: Tue, 7 May 2013 12:23:30 +0200 Subject: btrfs: fix btrfs_extend_item() comment The size parameter to btrfs_extend_item() is the number of bytes to add to the item, not the size of the item after the operation (like it is for btrfs_truncate_item(), there the size parameter is not the number of bytes to take away, but the total size of the item after truncation). Fix it in the comment. Signed-off-by: Stefan Behrens Signed-off-by: Jiri Kosina --- fs/btrfs/ctree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index 02fae7f7e42c..17dffe33e8d0 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -4430,7 +4430,7 @@ void btrfs_truncate_item(struct btrfs_root *root, struct btrfs_path *path, } /* - * make the item pointed to by the path bigger, data_size is the new size. + * make the item pointed to by the path bigger, data_size is the added size. */ void btrfs_extend_item(struct btrfs_root *root, struct btrfs_path *path, u32 data_size) -- cgit v1.2.3 From f884ab15afdc5514e88105c92a4e2e1e6539869a Mon Sep 17 00:00:00 2001 From: Anatol Pomozov Date: Wed, 8 May 2013 16:56:16 -0700 Subject: doc: fix misspellings with 'codespell' tool Signed-off-by: Anatol Pomozov Signed-off-by: Jiri Kosina --- Documentation/DocBook/drm.tmpl | 6 +++--- Documentation/DocBook/media/dvb/frontend.xml | 2 +- Documentation/DocBook/media/v4l/controls.xml | 2 +- Documentation/DocBook/writing_usb_driver.tmpl | 2 +- Documentation/bcache.txt | 10 +++++----- Documentation/block/queue-sysfs.txt | 2 +- Documentation/cgroups/memory.txt | 2 +- Documentation/device-mapper/cache.txt | 2 +- .../devicetree/bindings/arm/samsung/interrupt-combiner.txt | 6 +++--- Documentation/devicetree/bindings/arm/spear/shirq.txt | 2 +- Documentation/devicetree/bindings/clock/silabs,si5351.txt | 2 +- Documentation/devicetree/bindings/mmc/synopsis-dw-mshc.txt | 2 +- Documentation/devicetree/bindings/powerpc/4xx/emac.txt | 2 +- Documentation/devicetree/bindings/spi/brcm,bcm2835-spi.txt | 2 +- .../devicetree/bindings/timer/samsung,exynos4210-mct.txt | 2 +- Documentation/devicetree/bindings/usb/am33xx-usb.txt | 2 +- Documentation/devicetree/bindings/usb/omap-usb.txt | 2 +- Documentation/dynamic-debug-howto.txt | 2 +- Documentation/fb/cirrusfb.txt | 2 +- Documentation/filesystems/jfs.txt | 2 +- Documentation/filesystems/qnx6.txt | 2 +- Documentation/filesystems/vfat.txt | 2 +- Documentation/laptops/dslm.c | 2 +- Documentation/media-framework.txt | 2 +- Documentation/metag/kernel-ABI.txt | 2 +- Documentation/misc-devices/mei/mei.txt | 2 +- Documentation/networking/ip-sysctl.txt | 2 +- Documentation/networking/netlink_mmap.txt | 12 ++++++------ Documentation/pinctrl.txt | 2 +- Documentation/thermal/exynos_thermal_emulation | 2 +- Documentation/virtual/kvm/api.txt | 6 +++--- Documentation/vm/pagemap.txt | 2 +- Documentation/w1/slaves/w1_ds28e04 | 2 +- 33 files changed, 48 insertions(+), 48 deletions(-) diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index f9df3b872c16..6dd8d10d6b7e 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -434,7 +434,7 @@ char *date; The DRM core includes two memory managers, namely Translation Table Maps (TTM) and Graphics Execution Manager (GEM). TTM was the first DRM memory manager to be developed and tried to be a one-size-fits-them all - solution. It provides a single userspace API to accomodate the need of + solution. It provides a single userspace API to accommodate the need of all hardware, supporting both Unified Memory Architecture (UMA) devices and devices with dedicated video RAM (i.e. most discrete video cards). This resulted in a large, complex piece of code that turned out to be @@ -701,7 +701,7 @@ char *date; Similar to global names, GEM file descriptors are also used to share GEM objects across processes. They offer additional security: as file - descriptors must be explictly sent over UNIX domain sockets to be shared + descriptors must be explicitly sent over UNIX domain sockets to be shared between applications, they can't be guessed like the globally unique GEM names. @@ -1154,7 +1154,7 @@ int max_width, max_height; The page_flip operation schedules a page flip. - Once any pending rendering targetting the new frame buffer has + Once any pending rendering targeting the new frame buffer has completed, the CRTC will be reprogrammed to display that frame buffer after the next vertical refresh. The operation must return immediately without waiting for rendering or page flip to complete and must block diff --git a/Documentation/DocBook/media/dvb/frontend.xml b/Documentation/DocBook/media/dvb/frontend.xml index df39ba395df0..0d6e81bd9ed2 100644 --- a/Documentation/DocBook/media/dvb/frontend.xml +++ b/Documentation/DocBook/media/dvb/frontend.xml @@ -233,7 +233,7 @@ typedef enum fe_status { The frontend FEC inner coding (Viterbi, LDPC or other inner code) is stable FE_HAS_SYNC -Syncronization bytes was found +Synchronization bytes was found FE_HAS_LOCK The DVB were locked and everything is working diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index 8d7a77928d49..c2fc9ec1417e 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -3147,7 +3147,7 @@ giving priority to the center of the metered area. A multi-zone metering. The light intensity is measured in several points of the frame and the the results are combined. The algorithm of the zones selection and their significance in calculating the -final value is device dependant. +final value is device dependent. diff --git a/Documentation/DocBook/writing_usb_driver.tmpl b/Documentation/DocBook/writing_usb_driver.tmpl index bd97a13fa5ae..3210dcf741c9 100644 --- a/Documentation/DocBook/writing_usb_driver.tmpl +++ b/Documentation/DocBook/writing_usb_driver.tmpl @@ -83,7 +83,7 @@ Because each different protocol causes a new driver to be created, I have - written a generic USB driver skeleton, modeled after the pci-skeleton.c + written a generic USB driver skeleton, modelled after the pci-skeleton.c file in the kernel source tree upon which many PCI network drivers have been based. This USB skeleton can be found at drivers/usb/usb-skeleton.c in the kernel source tree. In this article I will walk through the basics diff --git a/Documentation/bcache.txt b/Documentation/bcache.txt index 77db8809bd96..2cdd4cfc6912 100644 --- a/Documentation/bcache.txt +++ b/Documentation/bcache.txt @@ -181,7 +181,7 @@ want for getting the best possible numbers when benchmarking. In practice this isn't an issue because as soon as a write comes along it'll cause the btree node to be split, and you need almost no write traffic for - this to not show up enough to be noticable (especially since bcache's btree + this to not show up enough to be noticeable (especially since bcache's btree nodes are huge and index large regions of the device). But when you're benchmarking, if you're trying to warm the cache by reading a bunch of data and there's no other traffic - that can be a problem. @@ -222,7 +222,7 @@ running it's in passthrough mode or caching). sequential_cutoff - A sequential IO will bypass the cache once it passes this threshhold; the + A sequential IO will bypass the cache once it passes this threshold; the most recent 128 IOs are tracked so sequential IO can be detected even when it isn't all done at once. @@ -296,7 +296,7 @@ cache_miss_collisions since the synchronization for cache misses was rewritten) cache_readaheads - Count of times readahead occured. + Count of times readahead occurred. SYSFS - CACHE SET: @@ -359,7 +359,7 @@ unregister SYSFS - CACHE SET INTERNAL: This directory also exposes timings for a number of internal operations, with -separate files for average duration, average frequency, last occurence and max +separate files for average duration, average frequency, last occurrence and max duration: garbage collection, btree read, btree node sorts and btree splits. active_journal_entries @@ -414,7 +414,7 @@ freelist_percent space. io_errors - Number of errors that have occured, decayed by io_error_halflife. + Number of errors that have occurred, decayed by io_error_halflife. metadata_written Sum of all non data writes (btree writes and all other metadata). diff --git a/Documentation/block/queue-sysfs.txt b/Documentation/block/queue-sysfs.txt index e54ac1d53403..7d2d046c265f 100644 --- a/Documentation/block/queue-sysfs.txt +++ b/Documentation/block/queue-sysfs.txt @@ -93,7 +93,7 @@ To avoid priority inversion through request starvation, a request queue maintains a separate request pool per each cgroup when CONFIG_BLK_CGROUP is enabled, and this parameter applies to each such per-block-cgroup request pool. IOW, if there are N block cgroups, -each request queue may have upto N request pools, each independently +each request queue may have up to N request pools, each independently regulated by nr_requests. optimal_io_size (RO) diff --git a/Documentation/cgroups/memory.txt b/Documentation/cgroups/memory.txt index ddf4f93967a9..3aaf7870a93e 100644 --- a/Documentation/cgroups/memory.txt +++ b/Documentation/cgroups/memory.txt @@ -304,7 +304,7 @@ kernel memory, we prevent new processes from being created when the kernel memory usage is too high. * slab pages: pages allocated by the SLAB or SLUB allocator are tracked. A copy -of each kmem_cache is created everytime the cache is touched by the first time +of each kmem_cache is created every time the cache is touched by the first time from inside the memcg. The creation is done lazily, so some objects can still be skipped while the cache is being created. All objects in a slab page should belong to the same memcg. This only fails to hold when a task is migrated to a diff --git a/Documentation/device-mapper/cache.txt b/Documentation/device-mapper/cache.txt index f50470abe241..e8cdf7241b66 100644 --- a/Documentation/device-mapper/cache.txt +++ b/Documentation/device-mapper/cache.txt @@ -87,7 +87,7 @@ Migration throttling Migrating data between the origin and cache device uses bandwidth. The user can set a throttle to prevent more than a certain amount of -migration occuring at any one time. Currently we're not taking any +migration occurring at any one time. Currently we're not taking any account of normal io traffic going to the devices. More work needs doing here to avoid migrating during those peak io moments. diff --git a/Documentation/devicetree/bindings/arm/samsung/interrupt-combiner.txt b/Documentation/devicetree/bindings/arm/samsung/interrupt-combiner.txt index f2f2171e530e..9e5f73412cd7 100644 --- a/Documentation/devicetree/bindings/arm/samsung/interrupt-combiner.txt +++ b/Documentation/devicetree/bindings/arm/samsung/interrupt-combiner.txt @@ -5,7 +5,7 @@ can combine interrupt sources as a group and provide a single interrupt request for the group. The interrupt request from each group are connected to a parent interrupt controller, such as GIC in case of Exynos4210. -The interrupt combiner controller consists of multiple combiners. Upto eight +The interrupt combiner controller consists of multiple combiners. Up to eight interrupt sources can be connected to a combiner. The combiner outputs one combined interrupt for its eight interrupt sources. The combined interrupt is usually connected to a parent interrupt controller. @@ -14,8 +14,8 @@ A single node in the device tree is used to describe the interrupt combiner controller module (which includes multiple combiners). A combiner in the interrupt controller module shares config/control registers with other combiners. For example, a 32-bit interrupt enable/disable config register -can accommodate upto 4 interrupt combiners (with each combiner supporting -upto 8 interrupt sources). +can accommodate up to 4 interrupt combiners (with each combiner supporting +up to 8 interrupt sources). Required properties: - compatible: should be "samsung,exynos4210-combiner". diff --git a/Documentation/devicetree/bindings/arm/spear/shirq.txt b/Documentation/devicetree/bindings/arm/spear/shirq.txt index 13fbb8866bd6..715a013ed4bd 100644 --- a/Documentation/devicetree/bindings/arm/spear/shirq.txt +++ b/Documentation/devicetree/bindings/arm/spear/shirq.txt @@ -14,7 +14,7 @@ A single node in the device tree is used to describe the shared interrupt multiplexor (one node for all groups). A group in the interrupt controller shares config/control registers with other groups. For example, a 32-bit interrupt enable/disable config register can -accommodate upto 4 interrupt groups. +accommodate up to 4 interrupt groups. Required properties: - compatible: should be, either of diff --git a/Documentation/devicetree/bindings/clock/silabs,si5351.txt b/Documentation/devicetree/bindings/clock/silabs,si5351.txt index cc374651662c..04feb13d6b29 100644 --- a/Documentation/devicetree/bindings/clock/silabs,si5351.txt +++ b/Documentation/devicetree/bindings/clock/silabs,si5351.txt @@ -4,7 +4,7 @@ Reference [1] Si5351A/B/C Data Sheet http://www.silabs.com/Support%20Documents/TechnicalDocs/Si5351.pdf -The Si5351a/b/c are programmable i2c clock generators with upto 8 output +The Si5351a/b/c are programmable i2c clock generators with up to 8 output clocks. Si5351a also has a reduced pin-count package (MSOP10) where only 3 output clocks are accessible. The internal structure of the clock generators can be found in [1]. diff --git a/Documentation/devicetree/bindings/mmc/synopsis-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/synopsis-dw-mshc.txt index 726fd2122a13..1180d7814af8 100644 --- a/Documentation/devicetree/bindings/mmc/synopsis-dw-mshc.txt +++ b/Documentation/devicetree/bindings/mmc/synopsis-dw-mshc.txt @@ -51,7 +51,7 @@ Optional properties: * card-detect-delay: Delay in milli-seconds before detecting card after card insert event. The default value is 0. -* supports-highspeed: Enables support for high speed cards (upto 50MHz) +* supports-highspeed: Enables support for high speed cards (up to 50MHz) * broken-cd: as documented in mmc core bindings. diff --git a/Documentation/devicetree/bindings/powerpc/4xx/emac.txt b/Documentation/devicetree/bindings/powerpc/4xx/emac.txt index 2161334a7ca5..712baf6c3e24 100644 --- a/Documentation/devicetree/bindings/powerpc/4xx/emac.txt +++ b/Documentation/devicetree/bindings/powerpc/4xx/emac.txt @@ -1,7 +1,7 @@ 4xx/Axon EMAC ethernet nodes The EMAC ethernet controller in IBM and AMCC 4xx chips, and also - the Axon bridge. To operate this needs to interact with a ths + the Axon bridge. To operate this needs to interact with a this special McMAL DMA controller, and sometimes an RGMII or ZMII interface. In addition to the nodes and properties described below, the node for the OPB bus on which the EMAC sits must have a diff --git a/Documentation/devicetree/bindings/spi/brcm,bcm2835-spi.txt b/Documentation/devicetree/bindings/spi/brcm,bcm2835-spi.txt index 8bf89c643640..f11f295c8450 100644 --- a/Documentation/devicetree/bindings/spi/brcm,bcm2835-spi.txt +++ b/Documentation/devicetree/bindings/spi/brcm,bcm2835-spi.txt @@ -2,7 +2,7 @@ Broadcom BCM2835 SPI0 controller The BCM2835 contains two forms of SPI master controller, one known simply as SPI0, and the other known as the "Universal SPI Master"; part of the -auxilliary block. This binding applies to the SPI0 controller. +auxiliary block. This binding applies to the SPI0 controller. Required properties: - compatible: Should be "brcm,bcm2835-spi". diff --git a/Documentation/devicetree/bindings/timer/samsung,exynos4210-mct.txt b/Documentation/devicetree/bindings/timer/samsung,exynos4210-mct.txt index cb47bfbcaeea..b5a86d20ee36 100644 --- a/Documentation/devicetree/bindings/timer/samsung,exynos4210-mct.txt +++ b/Documentation/devicetree/bindings/timer/samsung,exynos4210-mct.txt @@ -44,7 +44,7 @@ Example 1: In this example, the system uses only the first global timer }; Example 2: In this example, the MCT global and local timer interrupts are - connected to two seperate interrupt controllers. Hence, an + connected to two separate interrupt controllers. Hence, an interrupt-map is created to map the interrupts to the respective interrupt controllers. diff --git a/Documentation/devicetree/bindings/usb/am33xx-usb.txt b/Documentation/devicetree/bindings/usb/am33xx-usb.txt index ea840f7f9258..dc9dc8c87f15 100644 --- a/Documentation/devicetree/bindings/usb/am33xx-usb.txt +++ b/Documentation/devicetree/bindings/usb/am33xx-usb.txt @@ -12,7 +12,7 @@ AM33XX MUSB GLUE represents PERIPHERAL. - port1-mode : Should be "1" to represent HOST. "3" signifies OTG and "2" represents PERIPHERAL. - - power : Should be "250". This signifies the controller can supply upto + - power : Should be "250". This signifies the controller can supply up to 500mA when operating in host mode. Example: diff --git a/Documentation/devicetree/bindings/usb/omap-usb.txt b/Documentation/devicetree/bindings/usb/omap-usb.txt index d4769f343d6c..57e71f6817d0 100644 --- a/Documentation/devicetree/bindings/usb/omap-usb.txt +++ b/Documentation/devicetree/bindings/usb/omap-usb.txt @@ -16,7 +16,7 @@ OMAP MUSB GLUE specifying ULPI and UTMI respectively. - mode : Should be "3" to represent OTG. "1" signifies HOST and "2" represents PERIPHERAL. - - power : Should be "50". This signifies the controller can supply upto + - power : Should be "50". This signifies the controller can supply up to 100mA when operating in host mode. - usb-phy : the phandle for the PHY device diff --git a/Documentation/dynamic-debug-howto.txt b/Documentation/dynamic-debug-howto.txt index 72322c6d7352..1bbdcfcf1f13 100644 --- a/Documentation/dynamic-debug-howto.txt +++ b/Documentation/dynamic-debug-howto.txt @@ -279,7 +279,7 @@ The dyndbg option is a "fake" module parameter, which means: - modules do not need to define it explicitly - every module gets it tacitly, whether they use pr_debug or not -- it doesnt appear in /sys/module/$module/parameters/ +- it doesn't appear in /sys/module/$module/parameters/ To see it, grep the control file, or inspect /proc/cmdline. For CONFIG_DYNAMIC_DEBUG kernels, any settings given at boot-time (or diff --git a/Documentation/fb/cirrusfb.txt b/Documentation/fb/cirrusfb.txt index f9436843e998..f75950d330a4 100644 --- a/Documentation/fb/cirrusfb.txt +++ b/Documentation/fb/cirrusfb.txt @@ -55,7 +55,7 @@ Version 1.9.4.4 * Overhaul color register routines. * Associated with the above, console colors are now obtained from a LUT called 'palette' instead of from the VGA registers. This code was - modeled after that in atyfb and matroxfb. + modelled after that in atyfb and matroxfb. * Code cleanup, add comments. * Overhaul SR07 handling. * Bug fixes. diff --git a/Documentation/filesystems/jfs.txt b/Documentation/filesystems/jfs.txt index f7433355394a..41fd757997b3 100644 --- a/Documentation/filesystems/jfs.txt +++ b/Documentation/filesystems/jfs.txt @@ -42,7 +42,7 @@ nodiscard(*) block device when blocks are freed. This is useful for SSD devices and sparse/thinly-provisioned LUNs. The FITRIM ioctl command is also available together with the nodiscard option. The value of minlen specifies the minimum blockcount, when - a TRIM command to the block device is considered usefull. + a TRIM command to the block device is considered useful. When no value is given to the discard option, it defaults to 64 blocks, which means 256KiB in JFS. The minlen value of discard overrides the minlen value given diff --git a/Documentation/filesystems/qnx6.txt b/Documentation/filesystems/qnx6.txt index e59f2f09f56e..99e90184a72f 100644 --- a/Documentation/filesystems/qnx6.txt +++ b/Documentation/filesystems/qnx6.txt @@ -148,7 +148,7 @@ smaller than addressing space in the bitmap. Bitmap system area ------------------ -The bitmap itself is devided into three parts. +The bitmap itself is divided into three parts. First the system area, that is split into two halfs. Then userspace. diff --git a/Documentation/filesystems/vfat.txt b/Documentation/filesystems/vfat.txt index 4a93e98b290a..aa1f459fa6cf 100644 --- a/Documentation/filesystems/vfat.txt +++ b/Documentation/filesystems/vfat.txt @@ -307,7 +307,7 @@ the following: - + diff --git a/Documentation/laptops/dslm.c b/Documentation/laptops/dslm.c index 72ff290c5fc6..d5dd2d4b04d8 100644 --- a/Documentation/laptops/dslm.c +++ b/Documentation/laptops/dslm.c @@ -2,7 +2,7 @@ * dslm.c * Simple Disk Sleep Monitor * by Bartek Kania - * Licenced under the GPL + * Licensed under the GPL */ #include #include diff --git a/Documentation/media-framework.txt b/Documentation/media-framework.txt index 77bd0a42f19d..eeced24e56af 100644 --- a/Documentation/media-framework.txt +++ b/Documentation/media-framework.txt @@ -18,7 +18,7 @@ Abstract media device model Discovering a device internal topology, and configuring it at runtime, is one of the goals of the media framework. To achieve this, hardware devices are -modeled as an oriented graph of building blocks called entities connected +modelled as an oriented graph of building blocks called entities connected through pads. An entity is a basic media hardware building block. It can correspond to diff --git a/Documentation/metag/kernel-ABI.txt b/Documentation/metag/kernel-ABI.txt index 7b8dee83b9c1..628216603198 100644 --- a/Documentation/metag/kernel-ABI.txt +++ b/Documentation/metag/kernel-ABI.txt @@ -189,7 +189,7 @@ call: 64-bit arguments are placed in matching pairs of registers (i.e. the same register number in both D0 and D1 units), with the least significant half in D0 -and the most significant half in D1, leaving a gap where necessary. Futher +and the most significant half in D1, leaving a gap where necessary. Further arguments are stored on the stack in reverse order (earlier arguments at higher addresses): diff --git a/Documentation/misc-devices/mei/mei.txt b/Documentation/misc-devices/mei/mei.txt index 6ec702950719..15bba1aeba9a 100644 --- a/Documentation/misc-devices/mei/mei.txt +++ b/Documentation/misc-devices/mei/mei.txt @@ -120,7 +120,7 @@ The Intel MEI Driver supports the following IOCTL command: Notes: max_msg_length (MTU) in client properties describes the maximum data that can be sent or received. (e.g. if MTU=2K, can send - requests up to bytes 2k and received responses upto 2k bytes). + requests up to bytes 2k and received responses up to 2k bytes). Intel ME Applications: ============== diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index f98ca633b528..398d0fb1dd0e 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -183,7 +183,7 @@ tcp_early_retrans - INTEGER for triggering fast retransmit when the amount of outstanding data is small and when no previously unsent data can be transmitted (such that limited transmit could be used). Also controls the use of - Tail loss probe (TLP) that converts RTOs occuring due to tail + Tail loss probe (TLP) that converts RTOs occurring due to tail losses into fast recovery (draft-dukkipati-tcpm-tcp-loss-probe-01). Possible values: 0 disables ER diff --git a/Documentation/networking/netlink_mmap.txt b/Documentation/networking/netlink_mmap.txt index 1c2dab409625..9bd0f5211e9a 100644 --- a/Documentation/networking/netlink_mmap.txt +++ b/Documentation/networking/netlink_mmap.txt @@ -54,7 +54,7 @@ it will use an allocated socket buffer as usual and the contents will be copied to the ring on transmission, nullifying most of the performance gains. Dumps of kernel databases automatically support memory mapped I/O. -Conversion of the transmit path involves changing message contruction to +Conversion of the transmit path involves changing message construction to use memory from the TX ring instead of (usually) a buffer declared on the stack and setting up the frame header approriately. Optionally poll() can be used to wait for free frames in the TX ring. @@ -65,8 +65,8 @@ Structured and definitions for using memory mapped I/O are contained in RX and TX rings ---------------- -Each ring contains a number of continous memory blocks, containing frames of -fixed size dependant on the parameters used for ring setup. +Each ring contains a number of continuous memory blocks, containing frames of +fixed size dependent on the parameters used for ring setup. Ring: [ block 0 ] [ frame 0 ] @@ -80,7 +80,7 @@ Ring: [ block 0 ] [ frame 2 * n + 1 ] The blocks are only visible to the kernel, from the point of view of user-space -the ring just contains the frames in a continous memory zone. +the ring just contains the frames in a continuous memory zone. The ring parameters used for setting up the ring are defined as follows: @@ -91,7 +91,7 @@ struct nl_mmap_req { unsigned int nm_frame_nr; }; -Frames are grouped into blocks, where each block is a continous region of memory +Frames are grouped into blocks, where each block is a continuous region of memory and holds nm_block_size / nm_frame_size frames. The total number of frames in the ring is nm_frame_nr. The following invariants hold: @@ -113,7 +113,7 @@ Some parameters are constrained, specifically: - nm_frame_nr must equal the actual number of frames as specified above. -When the kernel can't allocate phsyically continous memory for a ring block, +When the kernel can't allocate physically continuous memory for a ring block, it will fall back to use physically discontinous memory. This might affect performance negatively, in order to avoid this the nm_frame_size parameter should be chosen to be as small as possible for the required frame size and diff --git a/Documentation/pinctrl.txt b/Documentation/pinctrl.txt index 447fd4cd54ec..d3c6d3dd7d4d 100644 --- a/Documentation/pinctrl.txt +++ b/Documentation/pinctrl.txt @@ -298,7 +298,7 @@ Since the pin controller subsystem have its pinspace local to the pin controller we need a mapping so that the pin control subsystem can figure out which pin controller handles control of a certain GPIO pin. Since a single pin controller may be muxing several GPIO ranges (typically SoCs that have -one set of pins but internally several GPIO silicon blocks, each modeled as +one set of pins but internally several GPIO silicon blocks, each modelled as a struct gpio_chip) any number of GPIO ranges can be added to a pin controller instance like this: diff --git a/Documentation/thermal/exynos_thermal_emulation b/Documentation/thermal/exynos_thermal_emulation index 36a3e79c1203..b15efec6ca28 100644 --- a/Documentation/thermal/exynos_thermal_emulation +++ b/Documentation/thermal/exynos_thermal_emulation @@ -20,7 +20,7 @@ When it's enabled, sysfs node will be created as The sysfs node, 'emul_node', will contain value 0 for the initial state. When you input any temperature you want to update to sysfs node, it automatically enable emulation mode and current temperature will be changed into it. -(Exynos also supports user changable delay time which would be used to delay of +(Exynos also supports user changeable delay time which would be used to delay of changing temperature. However, this node only uses same delay of real sensing time, 938us.) Exynos emulation mode requires synchronous of value changing and enabling. It means when you diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index 5f91eda91647..c2db6e3fb459 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -1683,7 +1683,7 @@ The parameter is defined like this: This ioctl maps the memory at "user_addr" with the length "length" to the vcpu's address space starting at "vcpu_addr". All parameters need to -be alligned by 1 megabyte. +be aligned by 1 megabyte. 4.66 KVM_S390_UCAS_UNMAP @@ -1703,7 +1703,7 @@ The parameter is defined like this: This ioctl unmaps the memory in the vcpu's address space starting at "vcpu_addr" with the length "length". The field "user_addr" is ignored. -All parameters need to be alligned by 1 megabyte. +All parameters need to be aligned by 1 megabyte. 4.67 KVM_S390_VCPU_FAULT @@ -2019,7 +2019,7 @@ be OR'ed into the "vsid" argument of the slbmte instruction. The "enc" array is a list which for each of those segment base page size provides the list of supported actual page sizes (which can be only larger or equal to the base page size), along with the -corresponding encoding in the hash PTE. Similarily, the array is +corresponding encoding in the hash PTE. Similarly, the array is 8 entries sorted by increasing sizes and an entry with a "0" shift is an empty entry and a terminator: diff --git a/Documentation/vm/pagemap.txt b/Documentation/vm/pagemap.txt index 7587493c67f1..5ef3dd38bb64 100644 --- a/Documentation/vm/pagemap.txt +++ b/Documentation/vm/pagemap.txt @@ -147,5 +147,5 @@ once. Other notes: Reading from any of the files will return -EINVAL if you are not starting -the read on an 8-byte boundary (e.g., if you seeked an odd number of bytes +the read on an 8-byte boundary (e.g., if you sought an odd number of bytes into the file), or if the size of the read is not a multiple of 8 bytes. diff --git a/Documentation/w1/slaves/w1_ds28e04 b/Documentation/w1/slaves/w1_ds28e04 index 85bc9a7e02fe..7819b65cfa48 100644 --- a/Documentation/w1/slaves/w1_ds28e04 +++ b/Documentation/w1/slaves/w1_ds28e04 @@ -24,7 +24,7 @@ Memory Access A write operation on the "eeprom" file writes the given byte sequence to the EEPROM of the DS28E04. If CRC checking mode is enabled only - fully alligned blocks of 32 bytes with valid CRC16 values (in bytes 30 + fully aligned blocks of 32 bytes with valid CRC16 values (in bytes 30 and 31) are allowed to be written. PIO Access -- cgit v1.2.3 From 6403792248af2cef6300945c498dc6042c6953b2 Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Mon, 13 May 2013 00:10:10 +0200 Subject: exynos4-is: Fix Kconfig comment Signed-off-by: Paul Bolle Signed-off-by: Jiri Kosina --- drivers/media/platform/exynos4-is/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/exynos4-is/Kconfig b/drivers/media/platform/exynos4-is/Kconfig index 6ff99b5849f9..d6d950e35c01 100644 --- a/drivers/media/platform/exynos4-is/Kconfig +++ b/drivers/media/platform/exynos4-is/Kconfig @@ -58,4 +58,4 @@ config VIDEO_EXYNOS4_FIMC_IS To compile this driver as a module, choose M here: the module will be called exynos4-fimc-is. -endif # VIDEO_SAMSUNG_S5P_FIMC +endif # VIDEO_SAMSUNG_EXYNOS4_IS -- cgit v1.2.3 From c2b3ebd0d21a10c7c5c44f4f3bd55d0065a86798 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 17 May 2013 13:22:29 +0200 Subject: scsi: Spelling hsot -> host Signed-off-by: Geert Uytterhoeven Signed-off-by: Jiri Kosina --- drivers/scsi/aic7xxx_old/aic7xxx.seq | 2 +- drivers/scsi/scsi_error.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/aic7xxx_old/aic7xxx.seq b/drivers/scsi/aic7xxx_old/aic7xxx.seq index 823ff2873229..dc3bb81cff0c 100644 --- a/drivers/scsi/aic7xxx_old/aic7xxx.seq +++ b/drivers/scsi/aic7xxx_old/aic7xxx.seq @@ -693,7 +693,7 @@ p_status: * it's own message. * * If MSG_OUT is == HOST_MSG, also interrupt the host and take a message. - * This is done to allow the hsot to send messages outside of an identify + * This is done to allow the host to send messages outside of an identify * sequence while protecting the seqencer from testing the MK_MESSAGE bit * on an SCB that might not be for the current nexus. (For example, a * BDR message in response to a bad reselection would leave us pointed to diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index f43de1e56420..ade98e23fb2b 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -538,7 +538,7 @@ static void scsi_eh_done(struct scsi_cmnd *scmd) /** * scsi_try_host_reset - ask host adapter to reset itself - * @scmd: SCSI cmd to send hsot reset. + * @scmd: SCSI cmd to send host reset. */ static int scsi_try_host_reset(struct scsi_cmnd *scmd) { -- cgit v1.2.3 From 9d1936cf86be8dc0cc27365bd8f1efdf23941961 Mon Sep 17 00:00:00 2001 From: Zhang Yanfei Date: Fri, 17 May 2013 22:10:38 +0800 Subject: mm/sparse: Remove unused ret in sparse_index_init The ret variable is not used in the function, so remove it and directly return 0 at the end of the function. Signed-off-by: Zhang Yanfei Signed-off-by: Jiri Kosina --- mm/sparse.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mm/sparse.c b/mm/sparse.c index 1c91f0d3f6ab..9ac2f743f723 100644 --- a/mm/sparse.c +++ b/mm/sparse.c @@ -79,7 +79,6 @@ static int __meminit sparse_index_init(unsigned long section_nr, int nid) { unsigned long root = SECTION_NR_TO_ROOT(section_nr); struct mem_section *section; - int ret = 0; if (mem_section[root]) return -EEXIST; @@ -90,7 +89,7 @@ static int __meminit sparse_index_init(unsigned long section_nr, int nid) mem_section[root] = section; - return ret; + return 0; } #else /* !SPARSEMEM_EXTREME */ static inline int sparse_index_init(unsigned long section_nr, int nid) -- cgit v1.2.3 From 8b513d0cf603c0a9ccf86a92cb22931f05a7bc86 Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Tue, 21 May 2013 23:13:12 +0900 Subject: treewide: Fix typo in printk Correct spelling typo in various part of drivers Signed-off-by: Masanari Iida Signed-off-by: Jiri Kosina --- drivers/gpu/drm/drm_fb_cma_helper.c | 4 ++-- drivers/gpu/drm/radeon/radeon_fb.c | 2 +- drivers/infiniband/ulp/isert/ib_isert.c | 2 +- drivers/media/platform/s5p-mfc/s5p_mfc_dec.c | 2 +- drivers/misc/dummy-irq.c | 2 +- drivers/misc/lattice-ecp3-config.c | 2 +- drivers/misc/mei/hbm.c | 2 +- drivers/misc/mei/init.c | 2 +- drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c | 2 +- drivers/nfc/pn533.c | 2 +- drivers/scsi/pm8001/pm80xx_hwi.c | 2 +- drivers/tty/serial/serial-tegra.c | 2 +- drivers/usb/musb/musb_dsps.c | 2 +- drivers/usb/musb/omap2430.c | 2 +- drivers/video/omap2/displays/panel-n8x0.c | 2 +- fs/btrfs/free-space-cache.c | 2 +- tools/testing/ktest/ktest.pl | 2 +- 17 files changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index 0b5af7d0edb1..c385cc5e730e 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c @@ -92,7 +92,7 @@ static struct drm_fb_cma *drm_fb_cma_alloc(struct drm_device *dev, ret = drm_framebuffer_init(dev, &fb_cma->fb, &drm_fb_cma_funcs); if (ret) { - dev_err(dev->dev, "Failed to initalize framebuffer: %d\n", ret); + dev_err(dev->dev, "Failed to initialize framebuffer: %d\n", ret); kfree(fb_cma); return ERR_PTR(ret); } @@ -376,7 +376,7 @@ struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev, ret = drm_fb_helper_initial_config(helper, preferred_bpp); if (ret < 0) { - dev_err(dev->dev, "Failed to set inital hw configuration.\n"); + dev_err(dev->dev, "Failed to set initial hw configuration.\n"); goto err_drm_fb_helper_fini; } diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c index b1746741bc59..665ced3b7313 100644 --- a/drivers/gpu/drm/radeon/radeon_fb.c +++ b/drivers/gpu/drm/radeon/radeon_fb.c @@ -230,7 +230,7 @@ static int radeonfb_create(struct drm_fb_helper *helper, ret = radeon_framebuffer_init(rdev->ddev, &rfbdev->rfb, &mode_cmd, gobj); if (ret) { - DRM_ERROR("failed to initalise framebuffer %d\n", ret); + DRM_ERROR("failed to initialize framebuffer %d\n", ret); goto out_unref; } diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index 41712f096515..2693129055c1 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c @@ -1587,7 +1587,7 @@ isert_put_nopin(struct iscsi_cmd *cmd, struct iscsi_conn *conn, isert_init_tx_hdrs(isert_conn, &isert_cmd->tx_desc); isert_init_send_wr(isert_cmd, send_wr); - pr_debug("Posting NOPIN Reponse IB_WR_SEND >>>>>>>>>>>>>>>>>>>>>>\n"); + pr_debug("Posting NOPIN Response IB_WR_SEND >>>>>>>>>>>>>>>>>>>>>>\n"); return isert_post_response(isert_conn, isert_cmd); } diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c index 4af53bd2f182..0fa1e9b38fc7 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c @@ -954,7 +954,7 @@ static int s5p_mfc_queue_setup(struct vb2_queue *vq, psize[0] = ctx->dec_src_buf_size; allocators[0] = ctx->dev->alloc_ctx[MFC_BANK1_ALLOC_CTX]; } else { - mfc_err("This video node is dedicated to decoding. Decoding not initalised\n"); + mfc_err("This video node is dedicated to decoding. Decoding not initialized\n"); return -EINVAL; } return 0; diff --git a/drivers/misc/dummy-irq.c b/drivers/misc/dummy-irq.c index c37eeedfe215..4d0db15df115 100644 --- a/drivers/misc/dummy-irq.c +++ b/drivers/misc/dummy-irq.c @@ -26,7 +26,7 @@ static irqreturn_t dummy_interrupt(int irq, void *dev_id) static int count = 0; if (count == 0) { - printk(KERN_INFO "dummy-irq: interrupt occured on IRQ %d\n", + printk(KERN_INFO "dummy-irq: interrupt occurred on IRQ %d\n", irq); count++; } diff --git a/drivers/misc/lattice-ecp3-config.c b/drivers/misc/lattice-ecp3-config.c index bb26f086bd8b..61fbe6acabef 100644 --- a/drivers/misc/lattice-ecp3-config.c +++ b/drivers/misc/lattice-ecp3-config.c @@ -170,7 +170,7 @@ static void firmware_load(const struct firmware *fw, void *context) /* Check result */ if (status & FPGA_STATUS_DONE) - dev_info(&spi->dev, "FPGA succesfully configured!\n"); + dev_info(&spi->dev, "FPGA successfully configured!\n"); else dev_info(&spi->dev, "FPGA not configured (DONE not set)\n"); diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index 6916045166eb..54ffcbc24c70 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -139,7 +139,7 @@ int mei_hbm_start_wait(struct mei_device *dev) if (ret <= 0 && (dev->hbm_state <= MEI_HBM_START)) { dev->hbm_state = MEI_HBM_IDLE; - dev_err(&dev->pdev->dev, "wating for mei start failed\n"); + dev_err(&dev->pdev->dev, "waiting for mei start failed\n"); return -ETIMEDOUT; } return 0; diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 713d89fedc46..422d9c37dec9 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -38,7 +38,7 @@ const char *mei_dev_state_str(int state) MEI_DEV_STATE(POWER_DOWN); MEI_DEV_STATE(POWER_UP); default: - return "unkown"; + return "unknown"; } #undef MEI_DEV_STATE } diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c index 196b2d100407..8b59a710a4a5 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c @@ -1675,7 +1675,7 @@ static int qlcnic_sriov_vf_handle_dev_ready(struct qlcnic_adapter *adapter) qlcnic_sriov_vf_attach(adapter); adapter->fw_fail_cnt = 0; dev_info(dev, - "%s: Reinitalization of VF 0x%x done after FW reset\n", + "%s: Reinitialization of VF 0x%x done after FW reset\n", __func__, func); } else { dev_err(dev, diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index 8f6f2baa930d..ec269e6f0375 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -1697,7 +1697,7 @@ static int pn533_poll_complete(struct pn533 *dev, void *arg, goto done; if (!dev->poll_mod_count) { - nfc_dev_dbg(&dev->interface->dev, "Polling has been stoped."); + nfc_dev_dbg(&dev->interface->dev, "Polling has been stopped."); goto done; } diff --git a/drivers/scsi/pm8001/pm80xx_hwi.c b/drivers/scsi/pm8001/pm80xx_hwi.c index 302514d8157b..1c5e7d233e78 100644 --- a/drivers/scsi/pm8001/pm80xx_hwi.c +++ b/drivers/scsi/pm8001/pm80xx_hwi.c @@ -3204,7 +3204,7 @@ static void process_one_iomb(struct pm8001_hba_info *pm8001_ha, void *piomb) break; case OPC_OUB_DEREG_DEV: PM8001_MSG_DBG(pm8001_ha, - pm8001_printk("unresgister the deviece\n")); + pm8001_printk("unregister the device\n")); pm8001_mpi_dereg_resp(pm8001_ha, piomb); break; case OPC_OUB_GET_DEV_HANDLE: diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index 9799d043a9bd..ee7c8123c374 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -1377,7 +1377,7 @@ static int __init tegra_uart_init(void) ret = platform_driver_register(&tegra_uart_platform_driver); if (ret < 0) { - pr_err("Uart platfrom driver register failed, e = %d\n", ret); + pr_err("Uart platform driver register failed, e = %d\n", ret); uart_unregister_driver(&tegra_uart_driver); return ret; } diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index e1b661d04021..5233804d66b1 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -551,7 +551,7 @@ static int dsps_create_musb_pdev(struct dsps_glue *glue, u8 id) pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) { dev_err(&pdev->dev, - "failed to allocate musb platfrom data\n"); + "failed to allocate musb platform data\n"); ret = -ENOMEM; goto err2; } diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 628b93fe5ccc..2325ef6bdeed 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -513,7 +513,7 @@ static int omap2430_probe(struct platform_device *pdev) pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) { dev_err(&pdev->dev, - "failed to allocate musb platfrom data\n"); + "failed to allocate musb platform data\n"); goto err2; } diff --git a/drivers/video/omap2/displays/panel-n8x0.c b/drivers/video/omap2/displays/panel-n8x0.c index f94ead6a3183..860b18014ad7 100644 --- a/drivers/video/omap2/displays/panel-n8x0.c +++ b/drivers/video/omap2/displays/panel-n8x0.c @@ -527,7 +527,7 @@ static int n8x0_panel_update(struct omap_dss_device *dssdev, dh = dssdev->panel.timings.y_res; if (x != 0 || y != 0 || w != dw || h != dh) { - dev_err(&dssdev->dev, "invaid update region %d, %d, %d, %d\n", + dev_err(&dssdev->dev, "invalid update region %d, %d, %d, %d\n", x, y, w, h); return -EINVAL; } diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c index e53009657f0e..2750b5023526 100644 --- a/fs/btrfs/free-space-cache.c +++ b/fs/btrfs/free-space-cache.c @@ -3200,7 +3200,7 @@ static int test_extents(struct btrfs_block_group_cache *cache) ret = btrfs_remove_free_space(cache, 2 * 1024 * 1024, 4096); if (ret) { - printk(KERN_ERR "Error removing middle peice %d\n", ret); + printk(KERN_ERR "Error removing middle piece %d\n", ret); return ret; } diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 0d7fd8b51544..999eab1bc64f 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -1796,7 +1796,7 @@ sub monitor { # We already booted into the kernel we are testing, # but now we booted into another kernel? # Consider this a triple fault. - doprint "Aleady booted in Linux kernel $version, but now\n"; + doprint "Already booted in Linux kernel $version, but now\n"; doprint "we booted into Linux kernel $1.\n"; doprint "Assuming that this is a triple fault.\n"; doprint "To disable this: set DETECT_TRIPLE_FAULT to 0\n"; -- cgit v1.2.3 From a7d4e022ca350bd6a0b45382bb58d907092139dd Mon Sep 17 00:00:00 2001 From: Zhang Yanfei Date: Thu, 23 May 2013 16:30:15 +0800 Subject: Documentation/kdump: Remove TODO in this document We have already had the relocatable kernel, so just remove the TODO in the kdump document. Signed-off-by: Zhang Yanfei Acked-by: Rob Landley Acked-by: Vivek Goyal Signed-off-by: Jiri Kosina --- Documentation/kdump/kdump.txt | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Documentation/kdump/kdump.txt b/Documentation/kdump/kdump.txt index 9c7fd988e299..a8b291d1f94e 100644 --- a/Documentation/kdump/kdump.txt +++ b/Documentation/kdump/kdump.txt @@ -461,14 +461,6 @@ format. Crash is available on Dave Anderson's site at the following URL: http://people.redhat.com/~anderson/ -To Do -===== - -1) Provide relocatable kernels for all architectures to help in maintaining - multiple kernels for crash_dump, and the same kernel as the system kernel - can be used to capture the dump. - - Contact ======= -- cgit v1.2.3 From 41de326eafa6eff71c6ca00ae27c6be235c65a6d Mon Sep 17 00:00:00 2001 From: Christian Ohm Date: Tue, 21 May 2013 01:31:08 +0200 Subject: HID: Add driver for Holtek gaming mouse 04d9:a067 This mouse is sold as Sharkoon Drakonia and Perixx MX-2000 and reports a too high usage maximum and logical maximum. This driver fixes the report descriptor so those values don't exceed HID_MAX_USAGES. Signed-off-by: Christian Ohm Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 1 + drivers/hid/Makefile | 1 + drivers/hid/hid-holtek-mouse.c | 72 ++++++++++++++++++++++++++++++++++++++++++ drivers/hid/hid-ids.h | 1 + 4 files changed, 75 insertions(+) create mode 100644 drivers/hid/hid-holtek-mouse.c diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index fb52f3f6de80..a0e19dc69daf 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -231,6 +231,7 @@ config HID_HOLTEK Support for Holtek based devices: - Holtek On Line Grip based game controller - Trust GXT 18 Gaming Keyboard + - Sharkoon Drakonia / Perixx MX-2000 gaming mice config HOLTEK_FF bool "Holtek On Line Grip force feedback support" diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 2065694f57ab..1fc048b04aaa 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -51,6 +51,7 @@ obj-$(CONFIG_HID_ELECOM) += hid-elecom.o obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o obj-$(CONFIG_HID_GYRATION) += hid-gyration.o obj-$(CONFIG_HID_HOLTEK) += hid-holtek-kbd.o +obj-$(CONFIG_HID_HOLTEK) += hid-holtek-mouse.o obj-$(CONFIG_HID_HOLTEK) += hid-holtekff.o obj-$(CONFIG_HID_HYPERV_MOUSE) += hid-hyperv.o obj-$(CONFIG_HID_ICADE) += hid-icade.o diff --git a/drivers/hid/hid-holtek-mouse.c b/drivers/hid/hid-holtek-mouse.c new file mode 100644 index 000000000000..f32e29aea11a --- /dev/null +++ b/drivers/hid/hid-holtek-mouse.c @@ -0,0 +1,72 @@ +/* + * HID driver for Holtek gaming mice + * Copyright (c) 2013 Christian Ohm + * Heavily inspired by various other HID drivers that adjust the report + * descriptor. +*/ + +/* + * 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 +#include +#include + +#include "hid-ids.h" + +/* + * The report descriptor of some Holtek based gaming mice specifies an + * excessively large number of consumer usages (2^15), which is more than + * HID_MAX_USAGES. This prevents proper parsing of the report descriptor. + * + * This driver fixes the report descriptor for USB ID 04d9:a067, sold as + * Sharkoon Drakonia and Perixx MX-2000. + */ + +static __u8 *holtek_mouse_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + + if (intf->cur_altsetting->desc.bInterfaceNumber == 1) { + /* Change usage maximum and logical maximum from 0x7fff to + * 0x2fff, so they don't exceed HID_MAX_USAGES */ + if (*rsize >= 122 && rdesc[115] == 0xff && rdesc[116] == 0x7f + && rdesc[120] == 0xff && rdesc[121] == 0x7f) { + hid_info(hdev, "Fixing up report descriptor\n"); + rdesc[116] = rdesc[121] = 0x2f; + } + } + return rdesc; +} + +static const struct hid_device_id holtek_mouse_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, + USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067) }, + { } +}; +MODULE_DEVICE_TABLE(hid, holtek_mouse_devices); + +static struct hid_driver holtek_mouse_driver = { + .name = "holtek_mouse", + .id_table = holtek_mouse_devices, + .report_fixup = holtek_mouse_report_fixup, +}; + +static int __init holtek_mouse_init(void) +{ + return hid_register_driver(&holtek_mouse_driver); +} + +static void __exit holtek_mouse_exit(void) +{ + hid_unregister_driver(&holtek_mouse_driver); +} + +module_exit(holtek_mouse_exit); +module_init(holtek_mouse_init); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 38535c9243d5..59766dfdf761 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -440,6 +440,7 @@ #define USB_VENDOR_ID_HOLTEK_ALT 0x04d9 #define USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD 0xa055 +#define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067 0xa067 #define USB_VENDOR_ID_IMATION 0x0718 #define USB_DEVICE_ID_DISC_STAKKA 0xd000 -- cgit v1.2.3 From d4f5189052c68f5bae3aac62b357577ecb862fee Mon Sep 17 00:00:00 2001 From: Christian Ohm Date: Tue, 21 May 2013 01:31:09 +0200 Subject: HID: Add support for Holtek gaming mouse 04d9:a04a MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This mouse is sold as Tracer Sniper TRM-503, NOVA Gaming Slider X200 and Zalman ZM-GM1, and reports too high usage maximum and logical maximum (like 04d9:a067, but its report descriptor is different). This patch adds its USB ID and fixes the report descriptor in the same way. Note: I don't actually have such a mouse to test, I took the report descriptor posted at https://bugzilla.novell.com/show_bug.cgi?id=774676, compared it to the one from 04d9:a067 and changed the offsets accordingly (all numbers minus 9, since it is 9 bytes shorter, and the difference is before the values that need changing). That Surely Works™. Signed-off-by: Christian Ohm Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 2 ++ drivers/hid/hid-holtek-mouse.c | 28 ++++++++++++++++++++++------ drivers/hid/hid-ids.h | 1 + 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index a0e19dc69daf..79e02682b2df 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -232,6 +232,8 @@ config HID_HOLTEK - Holtek On Line Grip based game controller - Trust GXT 18 Gaming Keyboard - Sharkoon Drakonia / Perixx MX-2000 gaming mice + - Tracer Sniper TRM-503 / NOVA Gaming Slider X200 / + Zalman ZM-GM1 config HOLTEK_FF bool "Holtek On Line Grip force feedback support" diff --git a/drivers/hid/hid-holtek-mouse.c b/drivers/hid/hid-holtek-mouse.c index f32e29aea11a..6a23ee678138 100644 --- a/drivers/hid/hid-holtek-mouse.c +++ b/drivers/hid/hid-holtek-mouse.c @@ -23,8 +23,10 @@ * excessively large number of consumer usages (2^15), which is more than * HID_MAX_USAGES. This prevents proper parsing of the report descriptor. * - * This driver fixes the report descriptor for USB ID 04d9:a067, sold as - * Sharkoon Drakonia and Perixx MX-2000. + * This driver fixes the report descriptor for: + * - USB ID 04d9:a067, sold as Sharkoon Drakonia and Perixx MX-2000 + * - USB ID 04d9:a04a, sold as Tracer Sniper TRM-503, NOVA Gaming Slider X200 + * and Zalman ZM-GM1 */ static __u8 *holtek_mouse_report_fixup(struct hid_device *hdev, __u8 *rdesc, @@ -35,11 +37,23 @@ static __u8 *holtek_mouse_report_fixup(struct hid_device *hdev, __u8 *rdesc, if (intf->cur_altsetting->desc.bInterfaceNumber == 1) { /* Change usage maximum and logical maximum from 0x7fff to * 0x2fff, so they don't exceed HID_MAX_USAGES */ - if (*rsize >= 122 && rdesc[115] == 0xff && rdesc[116] == 0x7f - && rdesc[120] == 0xff && rdesc[121] == 0x7f) { - hid_info(hdev, "Fixing up report descriptor\n"); - rdesc[116] = rdesc[121] = 0x2f; + switch (hdev->product) { + case USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067: + if (*rsize >= 122 && rdesc[115] == 0xff && rdesc[116] == 0x7f + && rdesc[120] == 0xff && rdesc[121] == 0x7f) { + hid_info(hdev, "Fixing up report descriptor\n"); + rdesc[116] = rdesc[121] = 0x2f; + } + break; + case USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A04A: + if (*rsize >= 113 && rdesc[106] == 0xff && rdesc[107] == 0x7f + && rdesc[111] == 0xff && rdesc[112] == 0x7f) { + hid_info(hdev, "Fixing up report descriptor\n"); + rdesc[107] = rdesc[112] = 0x2f; + } + break; } + } return rdesc; } @@ -47,6 +61,8 @@ static __u8 *holtek_mouse_report_fixup(struct hid_device *hdev, __u8 *rdesc, static const struct hid_device_id holtek_mouse_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067) }, + { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, + USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A04A) }, { } }; MODULE_DEVICE_TABLE(hid, holtek_mouse_devices); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 59766dfdf761..a9fcb9ea6c16 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -441,6 +441,7 @@ #define USB_VENDOR_ID_HOLTEK_ALT 0x04d9 #define USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD 0xa055 #define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067 0xa067 +#define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A04A 0xa04a #define USB_VENDOR_ID_IMATION 0x0718 #define USB_DEVICE_ID_DISC_STAKKA 0xd000 -- cgit v1.2.3 From 40d3597fc988dbe3bc902c501f88a73391c7da0d Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Tue, 28 May 2013 12:18:34 +0200 Subject: HID: holtek: PIDs 0xa04a and 0xa067 need to be in hid_have_special_driver[] Add device IDs of devices driven by hid-holtek-mouse to hid_have_special_driver[]. Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 264f55099940..3c014488b3c8 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1584,6 +1584,8 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_3) }, { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK, USB_DEVICE_ID_HOLTEK_ON_LINE_GRIP) }, { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD) }, + { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A04A) }, + { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067) }, { HID_USB_DEVICE(USB_VENDOR_ID_JESS2, USB_DEVICE_ID_JESS2_COLOR_RUMBLE_PAD) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ION, USB_DEVICE_ID_ICADE) }, { HID_USB_DEVICE(USB_VENDOR_ID_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) }, -- cgit v1.2.3 From bbc79089ae2bd0306db7f8dce85d56f9be65b205 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 22 Apr 2013 11:55:54 +0200 Subject: video: ssd1307fb: Add support for SSD1306 OLED controller The Solomon SSD1306 OLED controller is very similar to the SSD1307, except for the fact that the power is given through an external PWM for the 1307, and while the 1306 can generate its own power without any PWM. Signed-off-by: Maxime Ripard Signed-off-by: Tomi Valkeinen --- .../devicetree/bindings/video/ssd1307fb.txt | 10 +- drivers/video/ssd1307fb.c | 273 +++++++++++++++------ 2 files changed, 209 insertions(+), 74 deletions(-) diff --git a/Documentation/devicetree/bindings/video/ssd1307fb.txt b/Documentation/devicetree/bindings/video/ssd1307fb.txt index 3d0060cff062..7a125427ff4b 100644 --- a/Documentation/devicetree/bindings/video/ssd1307fb.txt +++ b/Documentation/devicetree/bindings/video/ssd1307fb.txt @@ -1,13 +1,17 @@ * Solomon SSD1307 Framebuffer Driver Required properties: - - compatible: Should be "solomon,ssd1307fb-". The only supported bus for - now is i2c. + - compatible: Should be "solomon,fb-". The only supported bus for + now is i2c, and the supported chips are ssd1306 and ssd1307. - reg: Should contain address of the controller on the I2C bus. Most likely 0x3c or 0x3d - pwm: Should contain the pwm to use according to the OF device tree PWM - specification [0] + specification [0]. Only required for the ssd1307. - reset-gpios: Should contain the GPIO used to reset the OLED display + - solomon,height: Height in pixel of the screen driven by the controller + - solomon,width: Width in pixel of the screen driven by the controller + - solomon,page-offset: Offset of pages (band of 8 pixels) that the screen is + mapped to. Optional properties: - reset-active-low: Is the reset gpio is active on physical low? diff --git a/drivers/video/ssd1307fb.c b/drivers/video/ssd1307fb.c index 9ef05d3ef68a..a0d6f96ec4e4 100644 --- a/drivers/video/ssd1307fb.c +++ b/drivers/video/ssd1307fb.c @@ -16,24 +16,39 @@ #include #include -#define SSD1307FB_WIDTH 96 -#define SSD1307FB_HEIGHT 16 - #define SSD1307FB_DATA 0x40 #define SSD1307FB_COMMAND 0x80 #define SSD1307FB_CONTRAST 0x81 +#define SSD1307FB_CHARGE_PUMP 0x8d #define SSD1307FB_SEG_REMAP_ON 0xa1 #define SSD1307FB_DISPLAY_OFF 0xae +#define SSD1307FB_SET_MULTIPLEX_RATIO 0xa8 #define SSD1307FB_DISPLAY_ON 0xaf #define SSD1307FB_START_PAGE_ADDRESS 0xb0 +#define SSD1307FB_SET_DISPLAY_OFFSET 0xd3 +#define SSD1307FB_SET_CLOCK_FREQ 0xd5 +#define SSD1307FB_SET_PRECHARGE_PERIOD 0xd9 +#define SSD1307FB_SET_COM_PINS_CONFIG 0xda +#define SSD1307FB_SET_VCOMH 0xdb + +struct ssd1307fb_par; + +struct ssd1307fb_ops { + int (*init)(struct ssd1307fb_par *); + int (*remove)(struct ssd1307fb_par *); +}; struct ssd1307fb_par { struct i2c_client *client; + u32 height; struct fb_info *info; + struct ssd1307fb_ops *ops; + u32 page_offset; struct pwm_device *pwm; u32 pwm_period; int reset; + u32 width; }; static struct fb_fix_screeninfo ssd1307fb_fix = { @@ -43,15 +58,10 @@ static struct fb_fix_screeninfo ssd1307fb_fix = { .xpanstep = 0, .ypanstep = 0, .ywrapstep = 0, - .line_length = SSD1307FB_WIDTH / 8, .accel = FB_ACCEL_NONE, }; static struct fb_var_screeninfo ssd1307fb_var = { - .xres = SSD1307FB_WIDTH, - .yres = SSD1307FB_HEIGHT, - .xres_virtual = SSD1307FB_WIDTH, - .yres_virtual = SSD1307FB_HEIGHT, .bits_per_pixel = 1, }; @@ -134,16 +144,17 @@ static void ssd1307fb_update_display(struct ssd1307fb_par *par) * (5) A4 B4 C4 D4 E4 F4 G4 H4 */ - for (i = 0; i < (SSD1307FB_HEIGHT / 8); i++) { - ssd1307fb_write_cmd(par->client, SSD1307FB_START_PAGE_ADDRESS + (i + 1)); + for (i = 0; i < (par->height / 8); i++) { + ssd1307fb_write_cmd(par->client, + SSD1307FB_START_PAGE_ADDRESS + i + par->page_offset); ssd1307fb_write_cmd(par->client, 0x00); ssd1307fb_write_cmd(par->client, 0x10); - for (j = 0; j < SSD1307FB_WIDTH; j++) { + for (j = 0; j < par->width; j++) { u8 buf = 0; for (k = 0; k < 8; k++) { - u32 page_length = SSD1307FB_WIDTH * i; - u32 index = page_length + (SSD1307FB_WIDTH * k + j) / 8; + u32 page_length = par->width * i; + u32 index = page_length + (par->width * k + j) / 8; u8 byte = *(vmem + index); u8 bit = byte & (1 << (j % 8)); bit = bit >> (j % 8); @@ -227,16 +238,147 @@ static struct fb_deferred_io ssd1307fb_defio = { .deferred_io = ssd1307fb_deferred_io, }; +static int ssd1307fb_ssd1307_init(struct ssd1307fb_par *par) +{ + int ret; + + par->pwm = pwm_get(&par->client->dev, NULL); + if (IS_ERR(par->pwm)) { + dev_err(&par->client->dev, "Could not get PWM from device tree!\n"); + return PTR_ERR(par->pwm); + } + + par->pwm_period = pwm_get_period(par->pwm); + /* Enable the PWM */ + pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period); + pwm_enable(par->pwm); + + dev_dbg(&par->client->dev, "Using PWM%d with a %dns period.\n", + par->pwm->pwm, par->pwm_period); + + /* Map column 127 of the OLED to segment 0 */ + ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON); + if (ret < 0) + return ret; + + /* Turn on the display */ + ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON); + if (ret < 0) + return ret; + + return 0; +} + +static int ssd1307fb_ssd1307_remove(struct ssd1307fb_par *par) +{ + pwm_disable(par->pwm); + pwm_put(par->pwm); + return 0; +} + +static struct ssd1307fb_ops ssd1307fb_ssd1307_ops = { + .init = ssd1307fb_ssd1307_init, + .remove = ssd1307fb_ssd1307_remove, +}; + +static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par) +{ + int ret; + + /* Set initial contrast */ + ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CONTRAST); + ret = ret & ssd1307fb_write_cmd(par->client, 0x7f); + if (ret < 0) + return ret; + + /* Set COM direction */ + ret = ssd1307fb_write_cmd(par->client, 0xc8); + if (ret < 0) + return ret; + + /* Set segment re-map */ + ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SEG_REMAP_ON); + if (ret < 0) + return ret; + + /* Set multiplex ratio value */ + ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_MULTIPLEX_RATIO); + ret = ret & ssd1307fb_write_cmd(par->client, par->height - 1); + if (ret < 0) + return ret; + + /* set display offset value */ + ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_DISPLAY_OFFSET); + ret = ssd1307fb_write_cmd(par->client, 0x20); + if (ret < 0) + return ret; + + /* Set clock frequency */ + ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_CLOCK_FREQ); + ret = ret & ssd1307fb_write_cmd(par->client, 0xf0); + if (ret < 0) + return ret; + + /* Set precharge period in number of ticks from the internal clock */ + ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PRECHARGE_PERIOD); + ret = ret & ssd1307fb_write_cmd(par->client, 0x22); + if (ret < 0) + return ret; + + /* Set COM pins configuration */ + ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COM_PINS_CONFIG); + ret = ret & ssd1307fb_write_cmd(par->client, 0x22); + if (ret < 0) + return ret; + + /* Set VCOMH */ + ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_VCOMH); + ret = ret & ssd1307fb_write_cmd(par->client, 0x49); + if (ret < 0) + return ret; + + /* Turn on the DC-DC Charge Pump */ + ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CHARGE_PUMP); + ret = ret & ssd1307fb_write_cmd(par->client, 0x14); + if (ret < 0) + return ret; + + /* Turn on the display */ + ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON); + if (ret < 0) + return ret; + + return 0; +} + +static struct ssd1307fb_ops ssd1307fb_ssd1306_ops = { + .init = ssd1307fb_ssd1306_init, +}; + +static const struct of_device_id ssd1307fb_of_match[] = { + { + .compatible = "solomon,ssd1306fb-i2c", + .data = (void *)&ssd1307fb_ssd1306_ops, + }, + { + .compatible = "solomon,ssd1307fb-i2c", + .data = (void *)&ssd1307fb_ssd1307_ops, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, ssd1307fb_of_match); + static int ssd1307fb_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct fb_info *info; - u32 vmem_size = SSD1307FB_WIDTH * SSD1307FB_HEIGHT / 8; + struct device_node *node = client->dev.of_node; + u32 vmem_size; struct ssd1307fb_par *par; u8 *vmem; int ret; - if (!client->dev.of_node) { + if (!node) { dev_err(&client->dev, "No device tree data found!\n"); return -EINVAL; } @@ -247,6 +389,31 @@ static int ssd1307fb_probe(struct i2c_client *client, return -ENOMEM; } + par = info->par; + par->info = info; + par->client = client; + + par->ops = (struct ssd1307fb_ops *)of_match_device(ssd1307fb_of_match, + &client->dev)->data; + + par->reset = of_get_named_gpio(client->dev.of_node, + "reset-gpios", 0); + if (!gpio_is_valid(par->reset)) { + ret = -EINVAL; + goto fb_alloc_error; + } + + if (of_property_read_u32(node, "solomon,width", &par->width)) + par->width = 96; + + if (of_property_read_u32(node, "solomon,height", &par->height)) + par->width = 16; + + if (of_property_read_u32(node, "solomon,page-offset", &par->page_offset)) + par->page_offset = 1; + + vmem_size = par->width * par->height / 8; + vmem = devm_kzalloc(&client->dev, vmem_size, GFP_KERNEL); if (!vmem) { dev_err(&client->dev, "Couldn't allocate graphical memory.\n"); @@ -256,9 +423,15 @@ static int ssd1307fb_probe(struct i2c_client *client, info->fbops = &ssd1307fb_ops; info->fix = ssd1307fb_fix; + info->fix.line_length = par->width / 8; info->fbdefio = &ssd1307fb_defio; info->var = ssd1307fb_var; + info->var.xres = par->width; + info->var.xres_virtual = par->width; + info->var.yres = par->height; + info->var.yres_virtual = par->height; + info->var.red.length = 1; info->var.red.offset = 0; info->var.green.length = 1; @@ -272,17 +445,6 @@ static int ssd1307fb_probe(struct i2c_client *client, fb_deferred_io_init(info); - par = info->par; - par->info = info; - par->client = client; - - par->reset = of_get_named_gpio(client->dev.of_node, - "reset-gpios", 0); - if (!gpio_is_valid(par->reset)) { - ret = -EINVAL; - goto reset_oled_error; - } - ret = devm_gpio_request_one(&client->dev, par->reset, GPIOF_OUT_INIT_HIGH, "oled-reset"); @@ -293,23 +455,6 @@ static int ssd1307fb_probe(struct i2c_client *client, goto reset_oled_error; } - par->pwm = pwm_get(&client->dev, NULL); - if (IS_ERR(par->pwm)) { - dev_err(&client->dev, "Could not get PWM from device tree!\n"); - ret = PTR_ERR(par->pwm); - goto pwm_error; - } - - par->pwm_period = pwm_get_period(par->pwm); - - dev_dbg(&client->dev, "Using PWM%d with a %dns period.\n", par->pwm->pwm, par->pwm_period); - - ret = register_framebuffer(info); - if (ret) { - dev_err(&client->dev, "Couldn't register the framebuffer\n"); - goto fbreg_error; - } - i2c_set_clientdata(client, info); /* Reset the screen */ @@ -318,34 +463,25 @@ static int ssd1307fb_probe(struct i2c_client *client, gpio_set_value(par->reset, 1); udelay(4); - /* Enable the PWM */ - pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period); - pwm_enable(par->pwm); - - /* Map column 127 of the OLED to segment 0 */ - ret = ssd1307fb_write_cmd(client, SSD1307FB_SEG_REMAP_ON); - if (ret < 0) { - dev_err(&client->dev, "Couldn't remap the screen.\n"); - goto remap_error; + if (par->ops->init) { + ret = par->ops->init(par); + if (ret) + goto reset_oled_error; } - /* Turn on the display */ - ret = ssd1307fb_write_cmd(client, SSD1307FB_DISPLAY_ON); - if (ret < 0) { - dev_err(&client->dev, "Couldn't turn the display on.\n"); - goto remap_error; + ret = register_framebuffer(info); + if (ret) { + dev_err(&client->dev, "Couldn't register the framebuffer\n"); + goto panel_init_error; } dev_info(&client->dev, "fb%d: %s framebuffer device registered, using %d bytes of video memory\n", info->node, info->fix.id, vmem_size); return 0; -remap_error: - unregister_framebuffer(info); - pwm_disable(par->pwm); -fbreg_error: - pwm_put(par->pwm); -pwm_error: +panel_init_error: + if (par->ops->remove) + par->ops->remove(par); reset_oled_error: fb_deferred_io_cleanup(info); fb_alloc_error: @@ -359,8 +495,8 @@ static int ssd1307fb_remove(struct i2c_client *client) struct ssd1307fb_par *par = info->par; unregister_framebuffer(info); - pwm_disable(par->pwm); - pwm_put(par->pwm); + if (par->ops->remove) + par->ops->remove(par); fb_deferred_io_cleanup(info); framebuffer_release(info); @@ -368,17 +504,12 @@ static int ssd1307fb_remove(struct i2c_client *client) } static const struct i2c_device_id ssd1307fb_i2c_id[] = { + { "ssd1306fb", 0 }, { "ssd1307fb", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, ssd1307fb_i2c_id); -static const struct of_device_id ssd1307fb_of_match[] = { - { .compatible = "solomon,ssd1307fb-i2c" }, - {}, -}; -MODULE_DEVICE_TABLE(of, ssd1307fb_of_match); - static struct i2c_driver ssd1307fb_driver = { .probe = ssd1307fb_probe, .remove = ssd1307fb_remove, -- cgit v1.2.3 From 9f7714d4638382d5f84fefd322983925935da742 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 22 Apr 2013 12:02:23 +0200 Subject: video: ssd1307fb: Rework the communication functions To efficiently send a whole page to the display, we need to be able to manipulate more easily the data arrays that has to be sent to the OLED controller. As such, this patch introduces a ssd1307fb_array structure that handles both the small header to be sent over i2c, which contains the type of information sent, and the raw bytes after that. Signed-off-by: Maxime Ripard Signed-off-by: Tomi Valkeinen --- drivers/video/ssd1307fb.c | 77 ++++++++++++++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 27 deletions(-) diff --git a/drivers/video/ssd1307fb.c b/drivers/video/ssd1307fb.c index a0d6f96ec4e4..9daf058917a7 100644 --- a/drivers/video/ssd1307fb.c +++ b/drivers/video/ssd1307fb.c @@ -51,6 +51,11 @@ struct ssd1307fb_par { u32 width; }; +struct ssd1307fb_array { + u8 type; + u8 data[0]; +}; + static struct fb_fix_screeninfo ssd1307fb_fix = { .id = "Solomon SSD1307", .type = FB_TYPE_PACKED_PIXELS, @@ -65,49 +70,67 @@ static struct fb_var_screeninfo ssd1307fb_var = { .bits_per_pixel = 1, }; -static int ssd1307fb_write_array(struct i2c_client *client, u8 type, u8 *cmd, u32 len) +static struct ssd1307fb_array *ssd1307fb_alloc_array(u32 len, u8 type) { - u8 *buf; - int ret = 0; - - buf = kzalloc(len + 1, GFP_KERNEL); - if (!buf) { - dev_err(&client->dev, "Couldn't allocate sending buffer.\n"); - return -ENOMEM; - } + struct ssd1307fb_array *array; - buf[0] = type; - memcpy(buf + 1, cmd, len); + array = kzalloc(sizeof(struct ssd1307fb_array) + len, GFP_KERNEL); + if (!array) + return NULL; - ret = i2c_master_send(client, buf, len + 1); - if (ret != len + 1) { - dev_err(&client->dev, "Couldn't send I2C command.\n"); - goto error; - } + array->type = type; -error: - kfree(buf); - return ret; + return array; } -static inline int ssd1307fb_write_cmd_array(struct i2c_client *client, u8 *cmd, u32 len) +static int ssd1307fb_write_array(struct i2c_client *client, + struct ssd1307fb_array *array, u32 len) { - return ssd1307fb_write_array(client, SSD1307FB_COMMAND, cmd, len); + int ret; + + len += sizeof(struct ssd1307fb_array); + + ret = i2c_master_send(client, (u8 *)array, len); + if (ret != len) { + dev_err(&client->dev, "Couldn't send I2C command.\n"); + return ret; + } + + return 0; } static inline int ssd1307fb_write_cmd(struct i2c_client *client, u8 cmd) { - return ssd1307fb_write_cmd_array(client, &cmd, 1); -} + struct ssd1307fb_array *array; + int ret; -static inline int ssd1307fb_write_data_array(struct i2c_client *client, u8 *cmd, u32 len) -{ - return ssd1307fb_write_array(client, SSD1307FB_DATA, cmd, len); + array = ssd1307fb_alloc_array(1, SSD1307FB_COMMAND); + if (!array) + return -ENOMEM; + + array->data[0] = cmd; + + ret = ssd1307fb_write_array(client, array, 1); + kfree(array); + + return ret; } static inline int ssd1307fb_write_data(struct i2c_client *client, u8 data) { - return ssd1307fb_write_data_array(client, &data, 1); + struct ssd1307fb_array *array; + int ret; + + array = ssd1307fb_alloc_array(1, SSD1307FB_DATA); + if (!array) + return -ENOMEM; + + array->data[0] = data; + + ret = ssd1307fb_write_array(client, array, 1); + kfree(array); + + return ret; } static void ssd1307fb_update_display(struct ssd1307fb_par *par) -- cgit v1.2.3 From 3394e645a88c722396fc1b03c31a3ffc158744ad Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 22 Apr 2013 12:02:24 +0200 Subject: video: ssd1307fb: Speed up the communication with the controller The code until now was sending only 1pixel-wide page segment at once, and started a new transfer every time. It has proven very inefficient, because for one byte to display on the screen, we had to actually send 3 bytes over I2C: the address, the type of data that was going to the controller, and then the actual data. This patches changes that by sending a whole page at once, avoiding most of this expensive overhead. Signed-off-by: Maxime Ripard Signed-off-by: Tomi Valkeinen --- drivers/video/ssd1307fb.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/video/ssd1307fb.c b/drivers/video/ssd1307fb.c index 9daf058917a7..04a435886855 100644 --- a/drivers/video/ssd1307fb.c +++ b/drivers/video/ssd1307fb.c @@ -168,23 +168,28 @@ static void ssd1307fb_update_display(struct ssd1307fb_par *par) */ for (i = 0; i < (par->height / 8); i++) { + struct ssd1307fb_array *array; ssd1307fb_write_cmd(par->client, SSD1307FB_START_PAGE_ADDRESS + i + par->page_offset); ssd1307fb_write_cmd(par->client, 0x00); ssd1307fb_write_cmd(par->client, 0x10); + array = ssd1307fb_alloc_array(par->width, SSD1307FB_DATA); + for (j = 0; j < par->width; j++) { - u8 buf = 0; + array->data[j] = 0; for (k = 0; k < 8; k++) { u32 page_length = par->width * i; u32 index = page_length + (par->width * k + j) / 8; u8 byte = *(vmem + index); u8 bit = byte & (1 << (j % 8)); bit = bit >> (j % 8); - buf |= bit << k; + array->data[j] |= bit << k; } - ssd1307fb_write_data(par->client, buf); } + + ssd1307fb_write_array(par->client, array, par->width); + kfree(array); } } -- cgit v1.2.3 From 301bc0675b677a98475187050d56cd2b39ff0acf Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 22 Apr 2013 12:02:25 +0200 Subject: video: ssd1307fb: Make use of horizontal addressing mode By default, the ssd1307 controller uses an addressing mode called page addressing. This mode only increments the column cursor in memory when writing data but will not increments the page cursor when we are at the end of the page. However, the controller supports another addressing mode, called horizontal addressing, that will maintain both the page and column cursors when writing data to the controller. That means that we can just remove the code that increments the current page address and reset the column cursor when reaching the end of the line, allowing to have a lower data overhead, and a simpler driver. Signed-off-by: Maxime Ripard Signed-off-by: Tomi Valkeinen --- drivers/video/ssd1307fb.c | 51 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/drivers/video/ssd1307fb.c b/drivers/video/ssd1307fb.c index 04a435886855..44967c8fef2b 100644 --- a/drivers/video/ssd1307fb.c +++ b/drivers/video/ssd1307fb.c @@ -19,6 +19,12 @@ #define SSD1307FB_DATA 0x40 #define SSD1307FB_COMMAND 0x80 +#define SSD1307FB_SET_ADDRESS_MODE 0x20 +#define SSD1307FB_SET_ADDRESS_MODE_HORIZONTAL (0x00) +#define SSD1307FB_SET_ADDRESS_MODE_VERTICAL (0x01) +#define SSD1307FB_SET_ADDRESS_MODE_PAGE (0x02) +#define SSD1307FB_SET_COL_RANGE 0x21 +#define SSD1307FB_SET_PAGE_RANGE 0x22 #define SSD1307FB_CONTRAST 0x81 #define SSD1307FB_CHARGE_PUMP 0x8d #define SSD1307FB_SEG_REMAP_ON 0xa1 @@ -135,9 +141,15 @@ static inline int ssd1307fb_write_data(struct i2c_client *client, u8 data) static void ssd1307fb_update_display(struct ssd1307fb_par *par) { + struct ssd1307fb_array *array; u8 *vmem = par->info->screen_base; int i, j, k; + array = ssd1307fb_alloc_array(par->width * par->height / 8, + SSD1307FB_DATA); + if (!array) + return; + /* * The screen is divided in pages, each having a height of 8 * pixels, and the width of the screen. When sending a byte of @@ -168,29 +180,22 @@ static void ssd1307fb_update_display(struct ssd1307fb_par *par) */ for (i = 0; i < (par->height / 8); i++) { - struct ssd1307fb_array *array; - ssd1307fb_write_cmd(par->client, - SSD1307FB_START_PAGE_ADDRESS + i + par->page_offset); - ssd1307fb_write_cmd(par->client, 0x00); - ssd1307fb_write_cmd(par->client, 0x10); - - array = ssd1307fb_alloc_array(par->width, SSD1307FB_DATA); - for (j = 0; j < par->width; j++) { - array->data[j] = 0; + u32 array_idx = i * par->width + j; + array->data[array_idx] = 0; for (k = 0; k < 8; k++) { u32 page_length = par->width * i; u32 index = page_length + (par->width * k + j) / 8; u8 byte = *(vmem + index); u8 bit = byte & (1 << (j % 8)); bit = bit >> (j % 8); - array->data[j] |= bit << k; + array->data[array_idx] |= bit << k; } } - - ssd1307fb_write_array(par->client, array, par->width); - kfree(array); } + + ssd1307fb_write_array(par->client, array, par->width * par->height / 8); + kfree(array); } @@ -371,6 +376,26 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par) if (ret < 0) return ret; + /* Switch to horizontal addressing mode */ + ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_ADDRESS_MODE); + ret = ret & ssd1307fb_write_cmd(par->client, + SSD1307FB_SET_ADDRESS_MODE_HORIZONTAL); + if (ret < 0) + return ret; + + ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COL_RANGE); + ret = ret & ssd1307fb_write_cmd(par->client, 0x0); + ret = ret & ssd1307fb_write_cmd(par->client, par->width - 1); + if (ret < 0) + return ret; + + ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PAGE_RANGE); + ret = ret & ssd1307fb_write_cmd(par->client, 0x0); + ret = ret & ssd1307fb_write_cmd(par->client, + par->page_offset + (par->height / 8) - 1); + if (ret < 0) + return ret; + /* Turn on the display */ ret = ssd1307fb_write_cmd(par->client, SSD1307FB_DISPLAY_ON); if (ret < 0) -- cgit v1.2.3 From fcf7e6e5bd84b561eca4f7977c2a547f724f5942 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Thu, 16 May 2013 15:29:06 +0300 Subject: videomode: don't allocate mem in of_get_display_timing() Move the allocation of display_timing memory from of_get_display_timing() to of_get_display_timings(). This allows us to use of_get_display_timing() in a way that doesn't require dynamic memory allocation. Signed-off-by: Tomi Valkeinen Cc: Steffen Trumtrar Cc: Laurent Pinchart Cc: Philipp Zabel --- drivers/video/of_display_timing.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/drivers/video/of_display_timing.c b/drivers/video/of_display_timing.c index 56009bc02b02..0e8102344b2e 100644 --- a/drivers/video/of_display_timing.c +++ b/drivers/video/of_display_timing.c @@ -56,18 +56,13 @@ static int parse_timing_property(struct device_node *np, const char *name, * of_get_display_timing - parse display_timing entry from device_node * @np: device_node with the properties **/ -static struct display_timing *of_get_display_timing(struct device_node *np) +static int of_get_display_timing(struct device_node *np, + struct display_timing *dt) { - struct display_timing *dt; u32 val = 0; int ret = 0; - dt = kzalloc(sizeof(*dt), GFP_KERNEL); - if (!dt) { - pr_err("%s: could not allocate display_timing struct\n", - of_node_full_name(np)); - return NULL; - } + memset(dt, 0, sizeof(*dt)); ret |= parse_timing_property(np, "hback-porch", &dt->hback_porch); ret |= parse_timing_property(np, "hfront-porch", &dt->hfront_porch); @@ -101,11 +96,10 @@ static struct display_timing *of_get_display_timing(struct device_node *np) if (ret) { pr_err("%s: error reading timing properties\n", of_node_full_name(np)); - kfree(dt); - return NULL; + return -EINVAL; } - return dt; + return 0; } /** @@ -174,9 +168,17 @@ struct display_timings *of_get_display_timings(struct device_node *np) for_each_child_of_node(timings_np, entry) { struct display_timing *dt; + int r; - dt = of_get_display_timing(entry); + dt = kzalloc(sizeof(*dt), GFP_KERNEL); if (!dt) { + pr_err("%s: could not allocate display_timing struct\n", + of_node_full_name(np)); + goto timingfail; + } + + r = of_get_display_timing(entry, dt); + if (r) { /* * to not encourage wrong devicetrees, fail in case of * an error -- cgit v1.2.3 From ffa3fd21de8ab0db7962b612d4c6e17c0d88e9c2 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Thu, 16 May 2013 15:36:38 +0300 Subject: videomode: implement public of_get_display_timing() The current of_get_display_timings() reads multiple display timings, allocating memory for the entries. However, most of the time when parsing display timings from DT data is needed, there's only one display timing as it's not common for a LCD panel to support multiple videomodes. This patch creates a new function: int of_get_display_timing(struct device_node *np, const char *name, struct display_timing *dt); which can be used to parse a single display timing entry from the given node name. Signed-off-by: Tomi Valkeinen Cc: Steffen Trumtrar Cc: Laurent Pinchart Cc: Philipp Zabel --- drivers/video/of_display_timing.c | 33 ++++++++++++++++++++++++++++++--- include/video/of_display_timing.h | 2 ++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/drivers/video/of_display_timing.c b/drivers/video/of_display_timing.c index 0e8102344b2e..9c0f17b2e6fb 100644 --- a/drivers/video/of_display_timing.c +++ b/drivers/video/of_display_timing.c @@ -53,10 +53,10 @@ static int parse_timing_property(struct device_node *np, const char *name, } /** - * of_get_display_timing - parse display_timing entry from device_node + * of_parse_display_timing - parse display_timing entry from device_node * @np: device_node with the properties **/ -static int of_get_display_timing(struct device_node *np, +static int of_parse_display_timing(struct device_node *np, struct display_timing *dt) { u32 val = 0; @@ -102,6 +102,33 @@ static int of_get_display_timing(struct device_node *np, return 0; } +/** + * of_get_display_timing - parse a display_timing entry + * @np: device_node with the timing subnode + * @name: name of the timing node + * @dt: display_timing struct to fill + **/ +int of_get_display_timing(struct device_node *np, const char *name, + struct display_timing *dt) +{ + struct device_node *timing_np; + + if (!np) { + pr_err("%s: no devicenode given\n", of_node_full_name(np)); + return -EINVAL; + } + + timing_np = of_find_node_by_name(np, name); + if (!timing_np) { + pr_err("%s: could not find node '%s'\n", + of_node_full_name(np), name); + return -ENOENT; + } + + return of_parse_display_timing(timing_np, dt); +} +EXPORT_SYMBOL_GPL(of_get_display_timing); + /** * of_get_display_timings - parse all display_timing entries from a device_node * @np: device_node with the subnodes @@ -177,7 +204,7 @@ struct display_timings *of_get_display_timings(struct device_node *np) goto timingfail; } - r = of_get_display_timing(entry, dt); + r = of_parse_display_timing(entry, dt); if (r) { /* * to not encourage wrong devicetrees, fail in case of diff --git a/include/video/of_display_timing.h b/include/video/of_display_timing.h index 8016eb727cf3..6562ad965889 100644 --- a/include/video/of_display_timing.h +++ b/include/video/of_display_timing.h @@ -14,6 +14,8 @@ struct display_timings; #define OF_USE_NATIVE_MODE -1 +int of_get_display_timing(struct device_node *np, const char *name, + struct display_timing *dt); struct display_timings *of_get_display_timings(struct device_node *np); int of_display_timings_exist(struct device_node *np); -- cgit v1.2.3 From 68e353fe476e7dab4644b9e7b4979b72726397ae Mon Sep 17 00:00:00 2001 From: Martin Rusko Date: Tue, 28 May 2013 14:25:15 +0200 Subject: HID: add support for Huion 580 tablet Add hid-huion.c with support for Huion 580 tablet, which is simple 8x5" tablet with 4000LPI resolution and 2048 levels pressure-sensitive pen manufactured by the Chinese company Huion. The driver fixes incorrect report descriptor sent by the device, performs custom initialization required to switch the tablet into its native resolution mode and inverts the in-range bit. Signed-off-by: Martin Rusko Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 6 ++ drivers/hid/Makefile | 1 + drivers/hid/hid-core.c | 1 + drivers/hid/hid-huion.c | 177 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/hid/hid-ids.h | 3 + 5 files changed, 188 insertions(+) create mode 100644 drivers/hid/hid-huion.c diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index fb52f3f6de80..015e6156caaf 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -240,6 +240,12 @@ config HOLTEK_FF Say Y here if you have a Holtek On Line Grip based game controller and want to have force feedback support for it. +config HID_HUION + tristate "Huion tablets" + depends on USB_HID + ---help--- + Support for Huion 580 tablet. + config HID_KEYTOUCH tristate "Keytouch HID devices" depends on HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 2065694f57ab..b15602bfaccf 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -52,6 +52,7 @@ obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o obj-$(CONFIG_HID_GYRATION) += hid-gyration.o obj-$(CONFIG_HID_HOLTEK) += hid-holtek-kbd.o obj-$(CONFIG_HID_HOLTEK) += hid-holtekff.o +obj-$(CONFIG_HID_HUION) += hid-huion.o obj-$(CONFIG_HID_HYPERV_MOUSE) += hid-hyperv.o obj-$(CONFIG_HID_ICADE) += hid-icade.o obj-$(CONFIG_HID_KENSINGTON) += hid-kensington.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 5d2ef6626615..c27207860dfb 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1584,6 +1584,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_3) }, { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK, USB_DEVICE_ID_HOLTEK_ON_LINE_GRIP) }, { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD) }, + { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_580) }, { HID_USB_DEVICE(USB_VENDOR_ID_JESS2, USB_DEVICE_ID_JESS2_COLOR_RUMBLE_PAD) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ION, USB_DEVICE_ID_ICADE) }, { HID_USB_DEVICE(USB_VENDOR_ID_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) }, diff --git a/drivers/hid/hid-huion.c b/drivers/hid/hid-huion.c new file mode 100644 index 000000000000..cbf4da4689ba --- /dev/null +++ b/drivers/hid/hid-huion.c @@ -0,0 +1,177 @@ +/* + * HID driver for Huion devices not fully compliant with HID standard + * + * Copyright (c) 2013 Martin Rusko + */ + +/* + * 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 +#include +#include +#include +#include "usbhid/usbhid.h" + +#include "hid-ids.h" + +/* Original Huion 580 report descriptor size */ +#define HUION_580_RDESC_ORIG_SIZE 177 + +/* Fixed Huion 580 report descriptor */ +static __u8 huion_580_rdesc_fixed[] = { + 0x05, 0x0D, /* Usage Page (Digitizer), */ + 0x09, 0x02, /* Usage (Pen), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x07, /* Report ID (7), */ + 0x09, 0x20, /* Usage (Stylus), */ + 0xA0, /* Collection (Physical), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x75, 0x01, /* Report Size (1), */ + 0x09, 0x42, /* Usage (Tip Switch), */ + 0x09, 0x44, /* Usage (Barrel Switch), */ + 0x09, 0x46, /* Usage (Tablet Pick), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x09, 0x32, /* Usage (In Range), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x75, 0x10, /* Report Size (16), */ + 0x95, 0x01, /* Report Count (1), */ + 0xA4, /* Push, */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x65, 0x13, /* Unit (Inch), */ + 0x55, 0xFD, /* Unit Exponent (-3), */ + 0x34, /* Physical Minimum (0), */ + 0x09, 0x30, /* Usage (X), */ + 0x46, 0x40, 0x1F, /* Physical Maximum (8000), */ + 0x26, 0x00, 0x7D, /* Logical Maximum (32000), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x31, /* Usage (Y), */ + 0x46, 0x88, 0x13, /* Physical Maximum (5000), */ + 0x26, 0x20, 0x4E, /* Logical Maximum (20000), */ + 0x81, 0x02, /* Input (Variable), */ + 0xB4, /* Pop, */ + 0x09, 0x30, /* Usage (Tip Pressure), */ + 0x26, 0xFF, 0x07, /* Logical Maximum (2047), */ + 0x81, 0x02, /* Input (Variable), */ + 0xC0, /* End Collection, */ + 0xC0 /* End Collection */ +}; + +static __u8 *huion_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) +{ + switch (hdev->product) { + case USB_DEVICE_ID_HUION_580: + if (*rsize == HUION_580_RDESC_ORIG_SIZE) { + rdesc = huion_580_rdesc_fixed; + *rsize = sizeof(huion_580_rdesc_fixed); + } + break; + } + return rdesc; +} + +/** + * Enable fully-functional tablet mode by reading special string + * descriptor. + * + * @hdev: HID device + * + * The specific string descriptor and data were discovered by sniffing + * the Windows driver traffic. + */ +static int huion_tablet_enable(struct hid_device *hdev) +{ + int rc; + char buf[22]; + + rc = usb_string(hid_to_usb_dev(hdev), 0x64, buf, sizeof(buf)); + if (rc < 0) + return rc; + + return 0; +} + +static int huion_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + int ret; + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + + /* Ignore interfaces 1 (mouse) and 2 (keyboard) for Huion 580 tablet, + * as they are not used + */ + switch (id->product) { + case USB_DEVICE_ID_HUION_580: + if (intf->cur_altsetting->desc.bInterfaceNumber != 0x00) + return -ENODEV; + break; + } + + ret = hid_parse(hdev); + if (ret) { + hid_err(hdev, "parse failed\n"); + goto err; + } + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (ret) { + hid_err(hdev, "hw start failed\n"); + goto err; + } + + switch (id->product) { + case USB_DEVICE_ID_HUION_580: + ret = huion_tablet_enable(hdev); + if (ret) { + hid_err(hdev, "tablet enabling failed\n"); + goto enabling_err; + } + break; + } + + return 0; +enabling_err: + hid_hw_stop(hdev); +err: + return ret; +} + +static int huion_raw_event(struct hid_device *hdev, struct hid_report *report, + u8 *data, int size) +{ + /* If this is a pen input report then invert the in-range bit */ + if (report->type == HID_INPUT_REPORT && report->id == 0x07 && size >= 2) + data[1] ^= 0x40; + + return 0; +} + +static const struct hid_device_id huion_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_580) }, + { } +}; +MODULE_DEVICE_TABLE(hid, huion_devices); + +static struct hid_driver huion_driver = { + .name = "huion", + .id_table = huion_devices, + .probe = huion_probe, + .report_fixup = huion_report_fixup, + .raw_event = huion_raw_event, +}; +module_hid_driver(huion_driver); + +MODULE_AUTHOR("Martin Rusko"); +MODULE_DESCRIPTION("Huion HID driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 533815b12ebc..3da75dd4c323 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -425,6 +425,9 @@ #define USB_DEVICE_ID_UGCI_FLYING 0x0020 #define USB_DEVICE_ID_UGCI_FIGHTING 0x0030 +#define USB_VENDOR_ID_HUION 0x256c +#define USB_DEVICE_ID_HUION_580 0x006e + #define USB_VENDOR_ID_IDEACOM 0x1cb6 #define USB_DEVICE_ID_IDEACOM_IDC6650 0x6650 #define USB_DEVICE_ID_IDEACOM_IDC6651 0x6651 -- cgit v1.2.3 From 4059a42c51f45566a7eb69f0c7af3ff65bee7a52 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 28 May 2013 14:53:40 +0200 Subject: ALSA: hda - Enable mic-mute LED on more HP laptops The newer HP laptops have SSID 103c:20xx and 103c:21xx, and these usually have the mic-mute LED on Fn-F8. Let's enable it, too. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_sigmatel.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 1d9d6427e0bf..9b6cb270dbe5 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -2233,6 +2233,10 @@ static const struct snd_pci_quirk stac92hd83xxx_fixup_tbl[] = { "HP Folio", STAC_92HD83XXX_HP_MIC_LED), SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xff00, 0x1900, "HP", STAC_92HD83XXX_HP_MIC_LED), + SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xff00, 0x2000, + "HP", STAC_92HD83XXX_HP_MIC_LED), + SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xff00, 0x2100, + "HP", STAC_92HD83XXX_HP_MIC_LED), SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3388, "HP", STAC_92HD83XXX_HP_cNB11_INTQUAD), SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3389, -- cgit v1.2.3 From a130243b96622e16af7c1ac7ba903b4cec7aa81b Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Tue, 28 May 2013 16:16:39 +0200 Subject: ASoC: ux500: Ensure consistent configuration between DAIs Current implementation of mop500_ab8500 allows for inconsistent sample rate and channel count configuration between the playback and recording interfaces, through in the hardware the two MSP controllers share common clock and frame sync signals. This patch adds the necessary code to ensure that the two device are configure consistently. The check is added at machine driver level, as how to lock DAI configuration depend of the actual hardware implementation. Signed-off-by: Fabio Baltieri Signed-off-by: Mark Brown --- sound/soc/ux500/mop500_ab8500.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/sound/soc/ux500/mop500_ab8500.c b/sound/soc/ux500/mop500_ab8500.c index 5e0f14634271..7e923ecf8901 100644 --- a/sound/soc/ux500/mop500_ab8500.c +++ b/sound/soc/ux500/mop500_ab8500.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -44,6 +45,12 @@ static unsigned int tx_slots = DEF_TX_SLOTS; static unsigned int rx_slots = DEF_RX_SLOTS; +/* Configuration consistency parameters */ +static DEFINE_MUTEX(mop500_ab8500_params_lock); +static unsigned long mop500_ab8500_usage; +static int mop500_ab8500_rate; +static int mop500_ab8500_channels; + /* Clocks */ static const char * const enum_mclk[] = { "SYSCLK", @@ -231,6 +238,21 @@ static int mop500_ab8500_hw_params(struct snd_pcm_substream *substream, substream->name, substream->number); + /* Ensure configuration consistency between DAIs */ + mutex_lock(&mop500_ab8500_params_lock); + if (mop500_ab8500_usage) { + if (mop500_ab8500_rate != params_rate(params) || + mop500_ab8500_channels != params_channels(params)) { + mutex_unlock(&mop500_ab8500_params_lock); + return -EBUSY; + } + } else { + mop500_ab8500_rate = params_rate(params); + mop500_ab8500_channels = params_channels(params); + } + __set_bit(cpu_dai->id, &mop500_ab8500_usage); + mutex_unlock(&mop500_ab8500_params_lock); + channels = params_channels(params); switch (params_format(params)) { @@ -329,9 +351,22 @@ static int mop500_ab8500_hw_params(struct snd_pcm_substream *substream, return 0; } +static int mop500_ab8500_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + + mutex_lock(&mop500_ab8500_params_lock); + __clear_bit(cpu_dai->id, &mop500_ab8500_usage); + mutex_unlock(&mop500_ab8500_params_lock); + + return 0; +} + struct snd_soc_ops mop500_ab8500_ops[] = { { .hw_params = mop500_ab8500_hw_params, + .hw_free = mop500_ab8500_hw_free, .startup = mop500_ab8500_startup, .shutdown = mop500_ab8500_shutdown, } -- cgit v1.2.3 From 095e7999c09afa09345db864427cb4bb4c98ae1c Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Mon, 20 May 2013 12:20:54 +0530 Subject: net/9p: Make 9P2000.L the default protocol for 9p file system If we dont' specify a protocol version default to 9P2000.L. 9P2000.L have better support for posix semantic and is where all the recent development is happening. Signed-off-by: Aneesh Kumar K.V Signed-off-by: Eric Van Hensbergen --- net/9p/client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/9p/client.c b/net/9p/client.c index 8eb75425e6e6..812a4cdf5302 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -127,7 +127,7 @@ static int parse_opts(char *opts, struct p9_client *clnt) char *s; int ret = 0; - clnt->proto_version = p9_proto_2000u; + clnt->proto_version = p9_proto_2000L; clnt->msize = 8192; if (!opts) -- cgit v1.2.3 From 535bcd3c4e8d09a62a89b2f1f3b3a80e1b3fce4b Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Mon, 20 May 2013 12:20:55 +0530 Subject: net/9p: Use virtio transpart as the default transport Make the default 9p experience better by defaulting to virtio transport if present. These days most of the users are using 9p in a virtualized setup Signed-off-by: Aneesh Kumar K.V Signed-off-by: Eric Van Hensbergen --- net/9p/client.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/9p/client.c b/net/9p/client.c index 812a4cdf5302..5e94dabddb68 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -1014,6 +1014,9 @@ struct p9_client *p9_client_create(const char *dev_name, char *options) if (err < 0) goto destroy_tagpool; + if (!clnt->trans_mod) + clnt->trans_mod = v9fs_get_trans_by_name("virtio"); + if (!clnt->trans_mod) clnt->trans_mod = v9fs_get_default_trans(); -- cgit v1.2.3 From 42fe6484c639e8f79e09f81cb89f4f69db224997 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Mon, 20 May 2013 23:05:15 +0530 Subject: net/9p: Handle error in zero copy request correctly for 9p2000.u For zero copy request, error will be encoded in the user space buffer. So copy the error code correctly using copy_from_user. Here we use the extra bytes we allocate for zero copy request. If total error details are more than P9_ZC_HDR_SZ - 7 bytes, we return -EFAULT. The patch also avoid a memory allocation in the error path. Signed-off-by: Aneesh Kumar K.V Signed-off-by: Eric Van Hensbergen --- net/9p/client.c | 55 ++++++++++++++++++------------------------------------- 1 file changed, 18 insertions(+), 37 deletions(-) diff --git a/net/9p/client.c b/net/9p/client.c index 5e94dabddb68..01f1779eba80 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -562,36 +562,19 @@ static int p9_check_zc_errors(struct p9_client *c, struct p9_req_t *req, if (!p9_is_proto_dotl(c)) { /* Error is reported in string format */ - uint16_t len; - /* 7 = header size for RERROR, 2 is the size of string len; */ - int inline_len = in_hdrlen - (7 + 2); + int len; + /* 7 = header size for RERROR; */ + int inline_len = in_hdrlen - 7; - /* Read the size of error string */ - err = p9pdu_readf(req->rc, c->proto_version, "w", &len); - if (err) - goto out_err; - - ename = kmalloc(len + 1, GFP_NOFS); - if (!ename) { - err = -ENOMEM; + len = req->rc->size - req->rc->offset; + if (len > (P9_ZC_HDR_SZ - 7)) { + err = -EFAULT; goto out_err; } - if (len <= inline_len) { - /* We have error in protocol buffer itself */ - if (pdu_read(req->rc, ename, len)) { - err = -EFAULT; - goto out_free; - } - } else { - /* - * Part of the data is in user space buffer. - */ - if (pdu_read(req->rc, ename, inline_len)) { - err = -EFAULT; - goto out_free; - - } + ename = &req->rc->sdata[req->rc->offset]; + if (len > inline_len) { + /* We have error in external buffer */ if (kern_buf) { memcpy(ename + inline_len, uidata, len - inline_len); @@ -600,19 +583,19 @@ static int p9_check_zc_errors(struct p9_client *c, struct p9_req_t *req, uidata, len - inline_len); if (err) { err = -EFAULT; - goto out_free; + goto out_err; } } } - ename[len] = 0; - if (p9_is_proto_dotu(c)) { - /* For dotu we also have error code */ - err = p9pdu_readf(req->rc, - c->proto_version, "d", &ecode); - if (err) - goto out_free; + ename = NULL; + err = p9pdu_readf(req->rc, c->proto_version, "s?d", + &ename, &ecode); + if (err) + goto out_err; + + if (p9_is_proto_dotu(c)) err = -ecode; - } + if (!err || !IS_ERR_VALUE(err)) { err = p9_errstr2errno(ename, strlen(ename)); @@ -628,8 +611,6 @@ static int p9_check_zc_errors(struct p9_client *c, struct p9_req_t *req, } return err; -out_free: - kfree(ename); out_err: p9_debug(P9_DEBUG_ERROR, "couldn't parse error%d\n", err); return err; -- cgit v1.2.3 From 0c2e3f3420bb790a4e5bc14d3d50a722964ad73e Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Tue, 28 May 2013 12:01:50 +0100 Subject: ASoC: wm_adsp: Ensure set controls are synced on each boot Rename `dirty' to `set' as it is a bit more descriptive. A set control is any control that has been set by the user. We need to ensure that everytime we boot the DSP we sync out any controls that were set. We could at some point start keeping track of the default values of the controls to suppress some of the device I/O. Signed-off-by: Dimitris Papastamos Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index d715c8ede772..ddba3fea39eb 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -242,7 +242,7 @@ struct wm_coeff_ctl { struct list_head list; void *cache; size_t len; - unsigned int dirty:1; + unsigned int set:1; struct snd_kcontrol *kcontrol; }; @@ -424,7 +424,7 @@ static int wm_coeff_put(struct snd_kcontrol *kcontrol, memcpy(ctl->cache, p, ctl->len); if (!ctl->enabled) { - ctl->dirty = 1; + ctl->set = 1; return 0; } @@ -760,7 +760,7 @@ static int wm_coeff_init_control_caches(struct wm_coeff *wm_coeff) list_for_each_entry(ctl, &wm_coeff->ctl_list, list) { - if (!ctl->enabled || ctl->dirty) + if (!ctl->enabled || ctl->set) continue; ret = wm_coeff_read_control(ctl->kcontrol, ctl->cache, @@ -781,13 +781,12 @@ static int wm_coeff_sync_controls(struct wm_coeff *wm_coeff) list) { if (!ctl->enabled) continue; - if (ctl->dirty) { + if (ctl->set) { ret = wm_coeff_write_control(ctl->kcontrol, ctl->cache, ctl->len); if (ret < 0) return ret; - ctl->dirty = 0; } } @@ -864,7 +863,7 @@ static int wm_adsp_create_control(struct snd_soc_codec *codec, goto err_ctl; } ctl->enabled = 1; - ctl->dirty = 0; + ctl->set = 0; ctl->ops.xget = wm_coeff_get; ctl->ops.xput = wm_coeff_put; ctl->card = codec->card->snd_card; @@ -1434,12 +1433,12 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, if (ret != 0) goto err; - /* Initialize caches for enabled and non-dirty controls */ + /* Initialize caches for enabled and unset controls */ ret = wm_coeff_init_control_caches(dsp->wm_coeff); if (ret != 0) goto err; - /* Sync dirty controls */ + /* Sync set controls */ ret = wm_coeff_sync_controls(dsp->wm_coeff); if (ret != 0) goto err; @@ -1591,12 +1590,12 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, if (ret != 0) goto err; - /* Initialize caches for enabled and non-dirty controls */ + /* Initialize caches for enabled and unset controls */ ret = wm_coeff_init_control_caches(dsp->wm_coeff); if (ret != 0) goto err; - /* Sync dirty controls */ + /* Sync set controls */ ret = wm_coeff_sync_controls(dsp->wm_coeff); if (ret != 0) goto err; -- cgit v1.2.3 From c375b2d7eff01d6423b95b2d44e8466beae0a15a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 28 May 2013 00:55:12 -0700 Subject: ASoC: fsi: fixup sparse errors This patch fixup below sparse errors ${LINUX}/sound/soc/sh/fsi.c:1459:9: \ error: incompatible types in conditional expression (different base types) ${LINUX}/sound/soc/sh/fsi.c:1634:25: \ error: incompatible types in conditional expression (different base types) ${LINUX}/sound/soc/sh/fsi.c:1639:17: \ error: incompatible types in conditional expression (different base types) ${LINUX}/sound/soc/sh/fsi.c:2093:9: \ error: incompatible types in conditional expression (different base types) ${LINUX}/sound/soc/sh/fsi.c:2105:9: \ error: incompatible types in conditional expression (different base types) Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/fsi.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index f830c41f97dd..30390260bb67 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -276,7 +276,7 @@ struct fsi_stream_handler { int (*probe)(struct fsi_priv *fsi, struct fsi_stream *io, struct device *dev); int (*transfer)(struct fsi_priv *fsi, struct fsi_stream *io); int (*remove)(struct fsi_priv *fsi, struct fsi_stream *io); - void (*start_stop)(struct fsi_priv *fsi, struct fsi_stream *io, + int (*start_stop)(struct fsi_priv *fsi, struct fsi_stream *io, int enable); }; #define fsi_stream_handler_call(io, func, args...) \ @@ -1188,7 +1188,7 @@ static int fsi_pio_push(struct fsi_priv *fsi, struct fsi_stream *io) samples); } -static void fsi_pio_start_stop(struct fsi_priv *fsi, struct fsi_stream *io, +static int fsi_pio_start_stop(struct fsi_priv *fsi, struct fsi_stream *io, int enable) { struct fsi_master *master = fsi_get_master(fsi); @@ -1201,6 +1201,8 @@ static void fsi_pio_start_stop(struct fsi_priv *fsi, struct fsi_stream *io, if (fsi_is_clk_master(fsi)) fsi_master_mask_set(master, CLK_RST, clk, (enable) ? clk : 0); + + return 0; } static int fsi_pio_push_init(struct fsi_priv *fsi, struct fsi_stream *io) @@ -1409,7 +1411,7 @@ static int fsi_dma_transfer(struct fsi_priv *fsi, struct fsi_stream *io) return 0; } -static void fsi_dma_push_start_stop(struct fsi_priv *fsi, struct fsi_stream *io, +static int fsi_dma_push_start_stop(struct fsi_priv *fsi, struct fsi_stream *io, int start) { struct fsi_master *master = fsi_get_master(fsi); @@ -1422,6 +1424,8 @@ static void fsi_dma_push_start_stop(struct fsi_priv *fsi, struct fsi_stream *io, if (fsi_is_clk_master(fsi)) fsi_master_mask_set(master, CLK_RST, clk, (enable) ? clk : 0); + + return 0; } static int fsi_dma_probe(struct fsi_priv *fsi, struct fsi_stream *io, struct device *dev) -- cgit v1.2.3 From 5b547a75405184d54d148132b9e4600516c49c6e Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 23 May 2013 20:54:37 +0200 Subject: spi: pl022: use DMA by default when probing from DT In the past we controlled the selection of DMA for a certain host by a boolean switch in the platform data. Currently there is no way to enable DMA on a PL022 probed from the device tree. Let's default to trying to obtain DMA channels in the DT case, and then we can always fail (and thus fall back to PIO mode). Signed-off-by: Linus Walleij Signed-off-by: Mark Brown --- drivers/spi/spi-pl022.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 371cc66f1a0e..3b246543282f 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -2083,6 +2083,7 @@ pl022_platform_data_dt_get(struct device *dev) } pd->bus_id = -1; + pd->enable_dma = 1; of_property_read_u32(np, "num-cs", &tmp); pd->num_chipselect = tmp; of_property_read_u32(np, "pl022,autosuspend-delay", -- cgit v1.2.3 From 287d03e9cd41a5f60bf96f43f8efea454f1cf74e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 28 May 2013 12:52:07 +0100 Subject: ASoC: wm8994: Remove restore of DAC enable state It's not been needed since the regmap conversion. Signed-off-by: Mark Brown --- sound/soc/codecs/wm8994.c | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index f1c54af45dcf..a265fd42b700 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -3131,22 +3131,6 @@ static int wm8994_codec_resume(struct snd_soc_codec *codec) struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); struct wm8994 *control = wm8994->wm8994; int i, ret; - unsigned int val, mask; - - if (control->revision < 4) { - /* force a HW read */ - ret = regmap_read(control->regmap, - WM8994_POWER_MANAGEMENT_5, &val); - - /* modify the cache only */ - codec->cache_only = 1; - mask = WM8994_DAC1R_ENA | WM8994_DAC1L_ENA | - WM8994_DAC2R_ENA | WM8994_DAC2L_ENA; - val &= mask; - snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_5, - mask, val); - codec->cache_only = 0; - } for (i = 0; i < ARRAY_SIZE(wm8994->fll); i++) { if (!wm8994->fll_suspend[i].out) -- cgit v1.2.3 From f7dbd399efff631203be9f09c07f128df18a3ee4 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 28 May 2013 12:52:09 +0100 Subject: ASoC: wm8994: Ensure lambda is zeroed for WM8994 Signed-off-by: Mark Brown --- sound/soc/codecs/wm8994.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index a265fd42b700..0805d6ff9ff7 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -2078,6 +2078,7 @@ static int wm8994_get_fll_config(struct wm8994 *control, struct fll_div *fll, /* Move down to proper range now rounding is done */ fll->k = K / 10; + fll->lambda = 0; pr_debug("N=%x K=%x\n", fll->n, fll->k); break; -- cgit v1.2.3 From bb5c2de268f0d01fb3bb7683037d9b2bebcba3d5 Mon Sep 17 00:00:00 2001 From: Wang Sheng-Hui Date: Tue, 28 May 2013 11:17:41 +0800 Subject: PCI: Fix INTC comment typo for pci_swizzle_interrupt_pin() The INTx pin should be INIT[ABCD]. Fix the typo "3=INTC". Signed-off-by: Wang Sheng-Hui Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index a899d8bb190d..e5f4e55d407d 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2421,7 +2421,7 @@ bool pci_acs_path_enabled(struct pci_dev *start, /** * pci_swizzle_interrupt_pin - swizzle INTx for device behind bridge * @dev: the PCI device - * @pin: the INTx pin (1=INTA, 2=INTB, 3=INTD, 4=INTD) + * @pin: the INTx pin (1=INTA, 2=INTB, 3=INTC, 4=INTD) * * Perform INTx swizzling for a device behind one level of bridge. This is * required by section 9.1 of the PCI-to-PCI bridge specification for devices -- cgit v1.2.3 From c673944347edfd4362b10eea11ac384a582b1cf5 Mon Sep 17 00:00:00 2001 From: Casey Schaufler Date: Wed, 22 May 2013 18:42:56 -0700 Subject: Smack: Local IPv6 port based controls Smack does not provide access controls on IPv6 communications. This patch introduces a mechanism for maintaining Smack lables for local IPv6 communications. It is based on labeling local ports. The behavior should be compatible with any future "real" IPv6 support as it provides no interfaces for users to manipulate the labeling. Remote IPv6 connections use the ambient label the same way that unlabeled IPv4 packets are treated. Targeted for git://git.gitorious.org/smack-next/kernel.git Signed-off-by: Casey Schaufler --- security/smack/smack.h | 11 ++ security/smack/smack_lsm.c | 348 +++++++++++++++++++++++++++++++++++++++------ 2 files changed, 319 insertions(+), 40 deletions(-) diff --git a/security/smack/smack.h b/security/smack/smack.h index 8ad30955e15d..bb28e099abfe 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -93,6 +93,17 @@ struct smk_netlbladdr { char *smk_label; /* label */ }; +/* + * An entry in the table identifying ports. + */ +struct smk_port_label { + struct list_head list; + struct sock *smk_sock; /* socket initialized on */ + unsigned short smk_port; /* the port number */ + char *smk_in; /* incoming label */ + char *smk_out; /* outgoing label */ +}; + /* * This is the repository for labels seen so that it is * not necessary to keep allocating tiny chuncks of memory diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index d52c780bdb78..609e89de3c24 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -27,10 +27,13 @@ #include #include #include +#include #include #include #include #include +#include +#include #include #include #include @@ -45,6 +48,12 @@ #define TRANS_TRUE "TRUE" #define TRANS_TRUE_SIZE 4 +#define SMK_CONNECTING 0 +#define SMK_RECEIVING 1 +#define SMK_SENDING 2 + +LIST_HEAD(smk_ipv6_port_list); + /** * smk_fetch - Fetch the smack label from a file. * @ip: a pointer to the inode @@ -1877,6 +1886,155 @@ static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap) return smack_netlabel(sk, sk_lbl); } +/** + * smk_ipv6_port_label - Smack port access table management + * @sock: socket + * @address: address + * + * Create or update the port list entry + */ +static void smk_ipv6_port_label(struct socket *sock, struct sockaddr *address) +{ + struct sock *sk = sock->sk; + struct sockaddr_in6 *addr6; + struct socket_smack *ssp = sock->sk->sk_security; + struct smk_port_label *spp; + unsigned short port = 0; + + if (address == NULL) { + /* + * This operation is changing the Smack information + * on the bound socket. Take the changes to the port + * as well. + */ + list_for_each_entry(spp, &smk_ipv6_port_list, list) { + if (sk != spp->smk_sock) + continue; + spp->smk_in = ssp->smk_in; + spp->smk_out = ssp->smk_out; + return; + } + /* + * A NULL address is only used for updating existing + * bound entries. If there isn't one, it's OK. + */ + return; + } + + addr6 = (struct sockaddr_in6 *)address; + port = ntohs(addr6->sin6_port); + /* + * This is a special case that is safely ignored. + */ + if (port == 0) + return; + + /* + * Look for an existing port list entry. + * This is an indication that a port is getting reused. + */ + list_for_each_entry(spp, &smk_ipv6_port_list, list) { + if (spp->smk_port != port) + continue; + spp->smk_port = port; + spp->smk_sock = sk; + spp->smk_in = ssp->smk_in; + spp->smk_out = ssp->smk_out; + return; + } + + /* + * A new port entry is required. + */ + spp = kzalloc(sizeof(*spp), GFP_KERNEL); + if (spp == NULL) + return; + + spp->smk_port = port; + spp->smk_sock = sk; + spp->smk_in = ssp->smk_in; + spp->smk_out = ssp->smk_out; + + list_add(&spp->list, &smk_ipv6_port_list); + return; +} + +/** + * smk_ipv6_port_check - check Smack port access + * @sock: socket + * @address: address + * + * Create or update the port list entry + */ +static int smk_ipv6_port_check(struct sock *sk, struct sockaddr *address, + int act) +{ + __be16 *bep; + __be32 *be32p; + struct sockaddr_in6 *addr6; + struct smk_port_label *spp; + struct socket_smack *ssp = sk->sk_security; + unsigned short port = 0; + char *subject; + char *object; + struct smk_audit_info ad; +#ifdef CONFIG_AUDIT + struct lsm_network_audit net; +#endif + + if (act == SMK_RECEIVING) { + subject = smack_net_ambient; + object = ssp->smk_in; + } else { + subject = ssp->smk_out; + object = smack_net_ambient; + } + + /* + * Get the IP address and port from the address. + */ + addr6 = (struct sockaddr_in6 *)address; + port = ntohs(addr6->sin6_port); + bep = (__be16 *)(&addr6->sin6_addr); + be32p = (__be32 *)(&addr6->sin6_addr); + + /* + * It's remote, so port lookup does no good. + */ + if (be32p[0] || be32p[1] || be32p[2] || bep[6] || ntohs(bep[7]) != 1) + goto auditout; + + /* + * It's local so the send check has to have passed. + */ + if (act == SMK_RECEIVING) { + subject = smack_known_web.smk_known; + goto auditout; + } + + list_for_each_entry(spp, &smk_ipv6_port_list, list) { + if (spp->smk_port != port) + continue; + object = spp->smk_in; + if (act == SMK_CONNECTING) + ssp->smk_packet = spp->smk_out; + break; + } + +auditout: + +#ifdef CONFIG_AUDIT + smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net); + ad.a.u.net->family = sk->sk_family; + ad.a.u.net->dport = port; + if (act == SMK_RECEIVING) + ad.a.u.net->v6info.saddr = addr6->sin6_addr; + else + ad.a.u.net->v6info.daddr = addr6->sin6_addr; +#endif + return smk_access(subject, object, MAY_WRITE, &ad); +} + /** * smack_inode_setsecurity - set smack xattrs * @inode: the object @@ -1926,7 +2084,7 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name, ssp->smk_in = sp; else if (strcmp(name, XATTR_SMACK_IPOUT) == 0) { ssp->smk_out = sp; - if (sock->sk->sk_family != PF_UNIX) { + if (sock->sk->sk_family == PF_INET) { rc = smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET); if (rc != 0) printk(KERN_WARNING @@ -1936,6 +2094,9 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name, } else return -EOPNOTSUPP; + if (sock->sk->sk_family == PF_INET6) + smk_ipv6_port_label(sock, NULL); + return 0; } @@ -1962,6 +2123,25 @@ static int smack_socket_post_create(struct socket *sock, int family, return smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET); } +/** + * smack_socket_bind - record port binding information. + * @sock: the socket + * @address: the port address + * @addrlen: size of the address + * + * Records the label bound to a port. + * + * Returns 0 + */ +static int smack_socket_bind(struct socket *sock, struct sockaddr *address, + int addrlen) +{ + if (sock->sk != NULL && sock->sk->sk_family == PF_INET6) + smk_ipv6_port_label(sock, address); + + return 0; +} + /** * smack_socket_connect - connect access check * @sock: the socket @@ -1975,12 +2155,24 @@ static int smack_socket_post_create(struct socket *sock, int family, static int smack_socket_connect(struct socket *sock, struct sockaddr *sap, int addrlen) { - if (sock->sk == NULL || sock->sk->sk_family != PF_INET) + int rc = 0; + + if (sock->sk == NULL) return 0; - if (addrlen < sizeof(struct sockaddr_in)) - return -EINVAL; - return smack_netlabel_send(sock->sk, (struct sockaddr_in *)sap); + switch (sock->sk->sk_family) { + case PF_INET: + if (addrlen < sizeof(struct sockaddr_in)) + return -EINVAL; + rc = smack_netlabel_send(sock->sk, (struct sockaddr_in *)sap); + break; + case PF_INET6: + if (addrlen < sizeof(struct sockaddr_in6)) + return -EINVAL; + rc = smk_ipv6_port_check(sock->sk, sap, SMK_CONNECTING); + break; + } + return rc; } /** @@ -2792,22 +2984,32 @@ static int smack_unix_may_send(struct socket *sock, struct socket *other) * @msg: the message * @size: the size of the message * - * Return 0 if the current subject can write to the destination - * host. This is only a question if the destination is a single - * label host. + * Return 0 if the current subject can write to the destination host. + * For IPv4 this is only a question if the destination is a single label host. + * For IPv6 this is a check against the label of the port. */ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size) { struct sockaddr_in *sip = (struct sockaddr_in *) msg->msg_name; + struct sockaddr *sap = (struct sockaddr *) msg->msg_name; + int rc = 0; /* * Perfectly reasonable for this to be NULL */ - if (sip == NULL || sip->sin_family != AF_INET) + if (sip == NULL) return 0; - return smack_netlabel_send(sock->sk, sip); + switch (sip->sin_family) { + case AF_INET: + rc = smack_netlabel_send(sock->sk, sip); + break; + case AF_INET6: + rc = smk_ipv6_port_check(sock->sk, sap, SMK_SENDING); + break; + } + return rc; } /** @@ -2878,6 +3080,54 @@ static char *smack_from_secattr(struct netlbl_lsm_secattr *sap, return smack_net_ambient; } +static int smk_skb_to_addr_ipv6(struct sk_buff *skb, struct sockaddr *sap) +{ + struct sockaddr_in6 *sip = (struct sockaddr_in6 *)sap; + u8 nexthdr; + int offset; + int proto = -EINVAL; + struct ipv6hdr _ipv6h; + struct ipv6hdr *ip6; + __be16 frag_off; + struct tcphdr _tcph, *th; + struct udphdr _udph, *uh; + struct dccp_hdr _dccph, *dh; + + sip->sin6_port = 0; + + offset = skb_network_offset(skb); + ip6 = skb_header_pointer(skb, offset, sizeof(_ipv6h), &_ipv6h); + if (ip6 == NULL) + return -EINVAL; + sip->sin6_addr = ip6->saddr; + + nexthdr = ip6->nexthdr; + offset += sizeof(_ipv6h); + offset = ipv6_skip_exthdr(skb, offset, &nexthdr, &frag_off); + if (offset < 0) + return -EINVAL; + + proto = nexthdr; + switch (proto) { + case IPPROTO_TCP: + th = skb_header_pointer(skb, offset, sizeof(_tcph), &_tcph); + if (th != NULL) + sip->sin6_port = th->source; + break; + case IPPROTO_UDP: + uh = skb_header_pointer(skb, offset, sizeof(_udph), &_udph); + if (uh != NULL) + sip->sin6_port = uh->source; + break; + case IPPROTO_DCCP: + dh = skb_header_pointer(skb, offset, sizeof(_dccph), &_dccph); + if (dh != NULL) + sip->sin6_port = dh->dccph_sport; + break; + } + return proto; +} + /** * smack_socket_sock_rcv_skb - Smack packet delivery access check * @sk: socket @@ -2889,43 +3139,52 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) { struct netlbl_lsm_secattr secattr; struct socket_smack *ssp = sk->sk_security; + struct sockaddr sadd; char *csp; - int rc; + int rc = 0; struct smk_audit_info ad; #ifdef CONFIG_AUDIT struct lsm_network_audit net; #endif - if (sk->sk_family != PF_INET && sk->sk_family != PF_INET6) - return 0; - - /* - * Translate what netlabel gave us. - */ - netlbl_secattr_init(&secattr); + switch (sk->sk_family) { + case PF_INET: + /* + * Translate what netlabel gave us. + */ + netlbl_secattr_init(&secattr); - rc = netlbl_skbuff_getattr(skb, sk->sk_family, &secattr); - if (rc == 0) - csp = smack_from_secattr(&secattr, ssp); - else - csp = smack_net_ambient; + rc = netlbl_skbuff_getattr(skb, sk->sk_family, &secattr); + if (rc == 0) + csp = smack_from_secattr(&secattr, ssp); + else + csp = smack_net_ambient; - netlbl_secattr_destroy(&secattr); + netlbl_secattr_destroy(&secattr); #ifdef CONFIG_AUDIT - smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net); - ad.a.u.net->family = sk->sk_family; - ad.a.u.net->netif = skb->skb_iif; - ipv4_skb_to_auditdata(skb, &ad.a, NULL); + smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net); + ad.a.u.net->family = sk->sk_family; + ad.a.u.net->netif = skb->skb_iif; + ipv4_skb_to_auditdata(skb, &ad.a, NULL); #endif - /* - * Receiving a packet requires that the other end - * be able to write here. Read access is not required. - * This is the simplist possible security model - * for networking. - */ - rc = smk_access(csp, ssp->smk_in, MAY_WRITE, &ad); - if (rc != 0) - netlbl_skbuff_err(skb, rc, 0); + /* + * Receiving a packet requires that the other end + * be able to write here. Read access is not required. + * This is the simplist possible security model + * for networking. + */ + rc = smk_access(csp, ssp->smk_in, MAY_WRITE, &ad); + if (rc != 0) + netlbl_skbuff_err(skb, rc, 0); + break; + case PF_INET6: + rc = smk_skb_to_addr_ipv6(skb, &sadd); + if (rc == IPPROTO_UDP || rc == IPPROTO_TCP) + rc = smk_ipv6_port_check(sk, &sadd, SMK_RECEIVING); + else + rc = 0; + break; + } return rc; } @@ -3063,9 +3322,17 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, struct lsm_network_audit net; #endif - /* handle mapped IPv4 packets arriving via IPv6 sockets */ - if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP)) - family = PF_INET; + if (family == PF_INET6) { + /* + * Handle mapped IPv4 packets arriving + * via IPv6 sockets. Don't set up netlabel + * processing on IPv6. + */ + if (skb->protocol == htons(ETH_P_IP)) + family = PF_INET; + else + return 0; + } netlbl_secattr_init(&secattr); rc = netlbl_skbuff_getattr(skb, family, &secattr); @@ -3498,6 +3765,7 @@ struct security_operations smack_ops = { .unix_may_send = smack_unix_may_send, .socket_post_create = smack_socket_post_create, + .socket_bind = smack_socket_bind, .socket_connect = smack_socket_connect, .socket_sendmsg = smack_socket_sendmsg, .socket_sock_rcv_skb = smack_socket_sock_rcv_skb, -- cgit v1.2.3 From 2f823ff8bec03a1e6f9e11fd0c4d54e4c7d09532 Mon Sep 17 00:00:00 2001 From: Casey Schaufler Date: Wed, 22 May 2013 18:43:03 -0700 Subject: Smack: Improve access check performance Each Smack label that the kernel has seen is added to a list of labels. The list of access rules for a given subject label hangs off of the label list entry for the label. This patch changes the structures that contain subject labels to point at the label list entry rather that the label itself. Doing so removes a label list lookup in smk_access() that was accounting for the largest single chunk of Smack overhead. Targeted for git://git.gitorious.org/smack-next/kernel.git Signed-off-by: Casey Schaufler --- security/smack/smack.h | 108 +++++++------- security/smack/smack_access.c | 41 +++--- security/smack/smack_lsm.c | 331 +++++++++++++++++++++++------------------- security/smack/smackfs.c | 51 +++---- 4 files changed, 282 insertions(+), 249 deletions(-) diff --git a/security/smack/smack.h b/security/smack/smack.h index bb28e099abfe..159f25bfcf45 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -28,6 +28,38 @@ #define SMK_LABELLEN 24 #define SMK_LONGLABEL 256 +/* + * This is the repository for labels seen so that it is + * not necessary to keep allocating tiny chuncks of memory + * and so that they can be shared. + * + * Labels are never modified in place. Anytime a label + * is imported (e.g. xattrset on a file) the list is checked + * for it and it is added if it doesn't exist. The address + * is passed out in either case. Entries are added, but + * never deleted. + * + * Since labels are hanging around anyway it doesn't + * hurt to maintain a secid for those awkward situations + * where kernel components that ought to use LSM independent + * interfaces don't. The secid should go away when all of + * these components have been repaired. + * + * The cipso value associated with the label gets stored here, too. + * + * Keep the access rules for this subject label here so that + * the entire set of rules does not need to be examined every + * time. + */ +struct smack_known { + struct list_head list; + char *smk_known; + u32 smk_secid; + struct netlbl_lsm_secattr smk_netlabel; /* on wire labels */ + struct list_head smk_rules; /* access rules */ + struct mutex smk_rules_lock; /* lock for rules */ +}; + /* * Maximum number of bytes for the levels in a CIPSO IP option. * Why 23? CIPSO is constrained to 30, so a 32 byte buffer is @@ -46,25 +78,25 @@ struct superblock_smack { }; struct socket_smack { - char *smk_out; /* outbound label */ - char *smk_in; /* inbound label */ - char *smk_packet; /* TCP peer label */ + struct smack_known *smk_out; /* outbound label */ + char *smk_in; /* inbound label */ + char *smk_packet; /* TCP peer label */ }; /* * Inode smack data */ struct inode_smack { - char *smk_inode; /* label of the fso */ - char *smk_task; /* label of the task */ - char *smk_mmap; /* label of the mmap domain */ - struct mutex smk_lock; /* initialization lock */ - int smk_flags; /* smack inode flags */ + char *smk_inode; /* label of the fso */ + struct smack_known *smk_task; /* label of the task */ + struct smack_known *smk_mmap; /* label of the mmap domain */ + struct mutex smk_lock; /* initialization lock */ + int smk_flags; /* smack inode flags */ }; struct task_smack { - char *smk_task; /* label for access control */ - char *smk_forked; /* label when forked */ + struct smack_known *smk_task; /* label for access control */ + struct smack_known *smk_forked; /* label when forked */ struct list_head smk_rules; /* per task access rules */ struct mutex smk_rules_lock; /* lock for the rules */ }; @@ -78,7 +110,7 @@ struct task_smack { */ struct smack_rule { struct list_head list; - char *smk_subject; + struct smack_known *smk_subject; char *smk_object; int smk_access; }; @@ -101,39 +133,7 @@ struct smk_port_label { struct sock *smk_sock; /* socket initialized on */ unsigned short smk_port; /* the port number */ char *smk_in; /* incoming label */ - char *smk_out; /* outgoing label */ -}; - -/* - * This is the repository for labels seen so that it is - * not necessary to keep allocating tiny chuncks of memory - * and so that they can be shared. - * - * Labels are never modified in place. Anytime a label - * is imported (e.g. xattrset on a file) the list is checked - * for it and it is added if it doesn't exist. The address - * is passed out in either case. Entries are added, but - * never deleted. - * - * Since labels are hanging around anyway it doesn't - * hurt to maintain a secid for those awkward situations - * where kernel components that ought to use LSM independent - * interfaces don't. The secid should go away when all of - * these components have been repaired. - * - * The cipso value associated with the label gets stored here, too. - * - * Keep the access rules for this subject label here so that - * the entire set of rules does not need to be examined every - * time. - */ -struct smack_known { - struct list_head list; - char *smk_known; - u32 smk_secid; - struct netlbl_lsm_secattr smk_netlabel; /* on wire labels */ - struct list_head smk_rules; /* access rules */ - struct mutex smk_rules_lock; /* lock for rules */ + struct smack_known *smk_out; /* outgoing label */ }; /* @@ -214,9 +214,9 @@ struct inode_smack *new_inode_smack(char *); * These functions are in smack_access.c */ int smk_access_entry(char *, char *, struct list_head *); -int smk_access(char *, char *, int, struct smk_audit_info *); +int smk_access(struct smack_known *, char *, int, struct smk_audit_info *); int smk_curacc(char *, u32, struct smk_audit_info *); -char *smack_from_secid(const u32); +struct smack_known *smack_from_secid(const u32); char *smk_parse_smack(const char *string, int len); int smk_netlbl_mls(int, char *, struct netlbl_lsm_secattr *, int); char *smk_import(const char *, int); @@ -229,7 +229,7 @@ u32 smack_to_secid(const char *); */ extern int smack_cipso_direct; extern int smack_cipso_mapped; -extern char *smack_net_ambient; +extern struct smack_known *smack_net_ambient; extern char *smack_onlycap; extern const char *smack_cipso_option; @@ -265,17 +265,17 @@ static inline char *smk_of_inode(const struct inode *isp) } /* - * Present a pointer to the smack label in an task blob. + * Present a pointer to the smack label entry in an task blob. */ -static inline char *smk_of_task(const struct task_smack *tsp) +static inline struct smack_known *smk_of_task(const struct task_smack *tsp) { return tsp->smk_task; } /* - * Present a pointer to the forked smack label in an task blob. + * Present a pointer to the forked smack label entry in an task blob. */ -static inline char *smk_of_forked(const struct task_smack *tsp) +static inline struct smack_known *smk_of_forked(const struct task_smack *tsp) { return tsp->smk_forked; } @@ -283,7 +283,7 @@ static inline char *smk_of_forked(const struct task_smack *tsp) /* * Present a pointer to the smack label in the current task blob. */ -static inline char *smk_of_current(void) +static inline struct smack_known *smk_of_current(void) { return smk_of_task(current_security()); } @@ -294,9 +294,11 @@ static inline char *smk_of_current(void) */ static inline int smack_privileged(int cap) { + struct smack_known *skp = smk_of_current(); + if (!capable(cap)) return 0; - if (smack_onlycap == NULL || smack_onlycap == smk_of_current()) + if (smack_onlycap == NULL || smack_onlycap == skp->smk_known) return 1; return 0; } diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index 2e397a88d410..53f2327a592f 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -93,7 +93,7 @@ int smk_access_entry(char *subject_label, char *object_label, list_for_each_entry_rcu(srp, rule_list, list) { if (srp->smk_object == object_label && - srp->smk_subject == subject_label) { + srp->smk_subject->smk_known == subject_label) { may = srp->smk_access; break; } @@ -104,7 +104,7 @@ int smk_access_entry(char *subject_label, char *object_label, /** * smk_access - determine if a subject has a specific access to an object - * @subject_label: a pointer to the subject's Smack label + * @subject_known: a pointer to the subject's Smack label entry * @object_label: a pointer to the object's Smack label * @request: the access requested, in "MAY" format * @a : a pointer to the audit data @@ -115,10 +115,9 @@ int smk_access_entry(char *subject_label, char *object_label, * * Smack labels are shared on smack_list */ -int smk_access(char *subject_label, char *object_label, int request, - struct smk_audit_info *a) +int smk_access(struct smack_known *subject_known, char *object_label, + int request, struct smk_audit_info *a) { - struct smack_known *skp; int may = MAY_NOT; int rc = 0; @@ -127,7 +126,7 @@ int smk_access(char *subject_label, char *object_label, int request, * * A star subject can't access any object. */ - if (subject_label == smack_known_star.smk_known) { + if (subject_known == &smack_known_star) { rc = -EACCES; goto out_audit; } @@ -137,7 +136,7 @@ int smk_access(char *subject_label, char *object_label, int request, * An internet subject can access any object. */ if (object_label == smack_known_web.smk_known || - subject_label == smack_known_web.smk_known) + subject_known == &smack_known_web) goto out_audit; /* * A star object can be accessed by any subject. @@ -148,7 +147,7 @@ int smk_access(char *subject_label, char *object_label, int request, * An object can be accessed in any way by a subject * with the same label. */ - if (subject_label == object_label) + if (subject_known->smk_known == object_label) goto out_audit; /* * A hat subject can read any object. @@ -157,7 +156,7 @@ int smk_access(char *subject_label, char *object_label, int request, if ((request & MAY_ANYREAD) == request) { if (object_label == smack_known_floor.smk_known) goto out_audit; - if (subject_label == smack_known_hat.smk_known) + if (subject_known == &smack_known_hat) goto out_audit; } /* @@ -167,9 +166,9 @@ int smk_access(char *subject_label, char *object_label, int request, * good. A negative response from smk_access_entry() * indicates there is no entry for this pair. */ - skp = smk_find_entry(subject_label); rcu_read_lock(); - may = smk_access_entry(subject_label, object_label, &skp->smk_rules); + may = smk_access_entry(subject_known->smk_known, object_label, + &subject_known->smk_rules); rcu_read_unlock(); if (may > 0 && (request & may) == request) @@ -179,7 +178,8 @@ int smk_access(char *subject_label, char *object_label, int request, out_audit: #ifdef CONFIG_AUDIT if (a) - smack_log(subject_label, object_label, request, rc, a); + smack_log(subject_known->smk_known, object_label, request, + rc, a); #endif return rc; } @@ -198,20 +198,21 @@ out_audit: int smk_curacc(char *obj_label, u32 mode, struct smk_audit_info *a) { struct task_smack *tsp = current_security(); - char *sp = smk_of_task(tsp); + struct smack_known *skp = smk_of_task(tsp); int may; int rc; /* * Check the global rule list */ - rc = smk_access(sp, obj_label, mode, NULL); + rc = smk_access(skp, obj_label, mode, NULL); if (rc == 0) { /* * If there is an entry in the task's rule list * it can further restrict access. */ - may = smk_access_entry(sp, obj_label, &tsp->smk_rules); + may = smk_access_entry(skp->smk_known, obj_label, + &tsp->smk_rules); if (may < 0) goto out_audit; if ((mode & may) == mode) @@ -228,7 +229,7 @@ int smk_curacc(char *obj_label, u32 mode, struct smk_audit_info *a) out_audit: #ifdef CONFIG_AUDIT if (a) - smack_log(sp, obj_label, mode, rc, a); + smack_log(skp->smk_known, obj_label, mode, rc, a); #endif return rc; } @@ -513,10 +514,10 @@ char *smk_import(const char *string, int len) * smack_from_secid - find the Smack label associated with a secid * @secid: an integer that might be associated with a Smack label * - * Returns a pointer to the appropriate Smack label if there is one, + * Returns a pointer to the appropriate Smack label entry if there is one, * otherwise a pointer to the invalid Smack label. */ -char *smack_from_secid(const u32 secid) +struct smack_known *smack_from_secid(const u32 secid) { struct smack_known *skp; @@ -524,7 +525,7 @@ char *smack_from_secid(const u32 secid) list_for_each_entry_rcu(skp, &smack_known_list, list) { if (skp->smk_secid == secid) { rcu_read_unlock(); - return skp->smk_known; + return skp; } } @@ -533,7 +534,7 @@ char *smack_from_secid(const u32 secid) * of a secid that is not on the list. */ rcu_read_unlock(); - return smack_known_invalid.smk_known; + return &smack_known_invalid; } /** diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 609e89de3c24..3669d9f9824e 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -62,11 +62,12 @@ LIST_HEAD(smk_ipv6_port_list); * Returns a pointer to the master list entry for the Smack label * or NULL if there was no label to fetch. */ -static char *smk_fetch(const char *name, struct inode *ip, struct dentry *dp) +static struct smack_known *smk_fetch(const char *name, struct inode *ip, + struct dentry *dp) { int rc; char *buffer; - char *result = NULL; + struct smack_known *skp = NULL; if (ip->i_op->getxattr == NULL) return NULL; @@ -77,11 +78,11 @@ static char *smk_fetch(const char *name, struct inode *ip, struct dentry *dp) rc = ip->i_op->getxattr(dp, name, buffer, SMK_LONGLABEL); if (rc > 0) - result = smk_import(buffer, rc); + skp = smk_import_entry(buffer, rc); kfree(buffer); - return result; + return skp; } /** @@ -111,7 +112,8 @@ struct inode_smack *new_inode_smack(char *smack) * * Returns the new blob or NULL if there's no memory available */ -static struct task_smack *new_task_smack(char *task, char *forked, gfp_t gfp) +static struct task_smack *new_task_smack(struct smack_known *task, + struct smack_known *forked, gfp_t gfp) { struct task_smack *tsp; @@ -173,17 +175,17 @@ static int smack_ptrace_access_check(struct task_struct *ctp, unsigned int mode) { int rc; struct smk_audit_info ad; - char *tsp; + struct smack_known *skp; rc = cap_ptrace_access_check(ctp, mode); if (rc != 0) return rc; - tsp = smk_of_task(task_security(ctp)); + skp = smk_of_task(task_security(ctp)); smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK); smk_ad_setfield_u_tsk(&ad, ctp); - rc = smk_curacc(tsp, MAY_READWRITE, &ad); + rc = smk_curacc(skp->smk_known, MAY_READWRITE, &ad); return rc; } @@ -199,17 +201,17 @@ static int smack_ptrace_traceme(struct task_struct *ptp) { int rc; struct smk_audit_info ad; - char *tsp; + struct smack_known *skp; rc = cap_ptrace_traceme(ptp); if (rc != 0) return rc; - tsp = smk_of_task(task_security(ptp)); + skp = smk_of_task(task_security(ptp)); smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK); smk_ad_setfield_u_tsk(&ad, ptp); - rc = smk_curacc(tsp, MAY_READWRITE, &ad); + rc = smk_curacc(skp->smk_known, MAY_READWRITE, &ad); return rc; } @@ -224,12 +226,12 @@ static int smack_ptrace_traceme(struct task_struct *ptp) static int smack_syslog(int typefrom_file) { int rc = 0; - char *sp = smk_of_current(); + struct smack_known *skp = smk_of_current(); if (smack_privileged(CAP_MAC_OVERRIDE)) return 0; - if (sp != smack_known_floor.smk_known) + if (skp != &smack_known_floor) rc = -EACCES; return rc; @@ -533,7 +535,9 @@ static int smack_bprm_secureexec(struct linux_binprm *bprm) */ static int smack_inode_alloc_security(struct inode *inode) { - inode->i_security = new_inode_smack(smk_of_current()); + struct smack_known *skp = smk_of_current(); + + inode->i_security = new_inode_smack(skp->smk_known); if (inode->i_security == NULL) return -ENOMEM; return 0; @@ -566,9 +570,8 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir, const struct qstr *qstr, char **name, void **value, size_t *len) { - struct smack_known *skp; struct inode_smack *issp = inode->i_security; - char *csp = smk_of_current(); + struct smack_known *skp = smk_of_current(); char *isp = smk_of_inode(inode); char *dsp = smk_of_inode(dir); int may; @@ -580,9 +583,8 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir, } if (value) { - skp = smk_find_entry(csp); rcu_read_lock(); - may = smk_access_entry(csp, dsp, &skp->smk_rules); + may = smk_access_entry(skp->smk_known, dsp, &skp->smk_rules); rcu_read_unlock(); /* @@ -871,29 +873,31 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name, static void smack_inode_post_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) { - char *nsp; + struct smack_known *skp; struct inode_smack *isp = dentry->d_inode->i_security; + if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) { + isp->smk_flags |= SMK_INODE_TRANSMUTE; + return; + } + + skp = smk_import_entry(value, size); if (strcmp(name, XATTR_NAME_SMACK) == 0) { - nsp = smk_import(value, size); - if (nsp != NULL) - isp->smk_inode = nsp; + if (skp != NULL) + isp->smk_inode = skp->smk_known; else isp->smk_inode = smack_known_invalid.smk_known; } else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0) { - nsp = smk_import(value, size); - if (nsp != NULL) - isp->smk_task = nsp; + if (skp != NULL) + isp->smk_task = skp; else - isp->smk_task = smack_known_invalid.smk_known; + isp->smk_task = &smack_known_invalid; } else if (strcmp(name, XATTR_NAME_SMACKMMAP) == 0) { - nsp = smk_import(value, size); - if (nsp != NULL) - isp->smk_mmap = nsp; + if (skp != NULL) + isp->smk_mmap = skp; else - isp->smk_mmap = smack_known_invalid.smk_known; - } else if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) - isp->smk_flags |= SMK_INODE_TRANSMUTE; + isp->smk_mmap = &smack_known_invalid; + } return; } @@ -999,7 +1003,7 @@ static int smack_inode_getsecurity(const struct inode *inode, if (strcmp(name, XATTR_SMACK_IPIN) == 0) isp = ssp->smk_in; else if (strcmp(name, XATTR_SMACK_IPOUT) == 0) - isp = ssp->smk_out; + isp = ssp->smk_out->smk_known; else return -EOPNOTSUPP; @@ -1079,7 +1083,9 @@ static int smack_file_permission(struct file *file, int mask) */ static int smack_file_alloc_security(struct file *file) { - file->f_security = smk_of_current(); + struct smack_known *skp = smk_of_current(); + + file->f_security = skp->smk_known; return 0; } @@ -1190,10 +1196,9 @@ static int smack_mmap_file(struct file *file, unsigned long flags) { struct smack_known *skp; + struct smack_known *mkp; struct smack_rule *srp; struct task_smack *tsp; - char *sp; - char *msmack; char *osmack; struct inode_smack *isp; int may; @@ -1207,11 +1212,10 @@ static int smack_mmap_file(struct file *file, isp = file_inode(file)->i_security; if (isp->smk_mmap == NULL) return 0; - msmack = isp->smk_mmap; + mkp = isp->smk_mmap; tsp = current_security(); - sp = smk_of_current(); - skp = smk_find_entry(sp); + skp = smk_of_current(); rc = 0; rcu_read_lock(); @@ -1225,13 +1229,13 @@ static int smack_mmap_file(struct file *file, /* * Matching labels always allows access. */ - if (msmack == osmack) + if (mkp->smk_known == osmack) continue; /* * If there is a matching local rule take * that into account as well. */ - may = smk_access_entry(srp->smk_subject, osmack, + may = smk_access_entry(srp->smk_subject->smk_known, osmack, &tsp->smk_rules); if (may == -ENOENT) may = srp->smk_access; @@ -1249,8 +1253,8 @@ static int smack_mmap_file(struct file *file, * If there isn't one a SMACK64MMAP subject * can't have as much access as current. */ - skp = smk_find_entry(msmack); - mmay = smk_access_entry(msmack, osmack, &skp->smk_rules); + mmay = smk_access_entry(mkp->smk_known, osmack, + &mkp->smk_rules); if (mmay == -ENOENT) { rc = -EACCES; break; @@ -1259,7 +1263,8 @@ static int smack_mmap_file(struct file *file, * If there is a local entry it modifies the * potential access, too. */ - tmay = smk_access_entry(msmack, osmack, &tsp->smk_rules); + tmay = smk_access_entry(mkp->smk_known, osmack, + &tsp->smk_rules); if (tmay != -ENOENT) mmay &= tmay; @@ -1288,7 +1293,9 @@ static int smack_mmap_file(struct file *file, */ static int smack_file_set_fowner(struct file *file) { - file->f_security = smk_of_current(); + struct smack_known *skp = smk_of_current(); + + file->f_security = skp->smk_known; return 0; } @@ -1306,9 +1313,10 @@ static int smack_file_set_fowner(struct file *file) static int smack_file_send_sigiotask(struct task_struct *tsk, struct fown_struct *fown, int signum) { + struct smack_known *skp; + struct smack_known *tkp = smk_of_task(tsk->cred->security); struct file *file; int rc; - char *tsp = smk_of_task(tsk->cred->security); struct smk_audit_info ad; /* @@ -1317,13 +1325,14 @@ static int smack_file_send_sigiotask(struct task_struct *tsk, file = container_of(fown, struct file, f_owner); /* we don't log here as rc can be overriden */ - rc = smk_access(file->f_security, tsp, MAY_WRITE, NULL); + skp = smk_find_entry(file->f_security); + rc = smk_access(skp, tkp->smk_known, MAY_WRITE, NULL); if (rc != 0 && has_capability(tsk, CAP_MAC_OVERRIDE)) rc = 0; smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK); smk_ad_setfield_u_tsk(&ad, tsk); - smack_log(file->f_security, tsp, MAY_WRITE, rc, &ad); + smack_log(file->f_security, tkp->smk_known, MAY_WRITE, rc, &ad); return rc; } @@ -1478,12 +1487,12 @@ static void smack_cred_transfer(struct cred *new, const struct cred *old) static int smack_kernel_act_as(struct cred *new, u32 secid) { struct task_smack *new_tsp = new->security; - char *smack = smack_from_secid(secid); + struct smack_known *skp = smack_from_secid(secid); - if (smack == NULL) + if (skp == NULL) return -EINVAL; - new_tsp->smk_task = smack; + new_tsp->smk_task = skp; return 0; } @@ -1501,8 +1510,8 @@ static int smack_kernel_create_files_as(struct cred *new, struct inode_smack *isp = inode->i_security; struct task_smack *tsp = new->security; - tsp->smk_forked = isp->smk_inode; - tsp->smk_task = isp->smk_inode; + tsp->smk_forked = smk_find_entry(isp->smk_inode); + tsp->smk_task = tsp->smk_forked; return 0; } @@ -1518,10 +1527,11 @@ static int smk_curacc_on_task(struct task_struct *p, int access, const char *caller) { struct smk_audit_info ad; + struct smack_known *skp = smk_of_task(task_security(p)); smk_ad_init(&ad, caller, LSM_AUDIT_DATA_TASK); smk_ad_setfield_u_tsk(&ad, p); - return smk_curacc(smk_of_task(task_security(p)), access, &ad); + return smk_curacc(skp->smk_known, access, &ad); } /** @@ -1567,7 +1577,9 @@ static int smack_task_getsid(struct task_struct *p) */ static void smack_task_getsecid(struct task_struct *p, u32 *secid) { - *secid = smack_to_secid(smk_of_task(task_security(p))); + struct smack_known *skp = smk_of_task(task_security(p)); + + *secid = skp->smk_secid; } /** @@ -1671,6 +1683,8 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info, int sig, u32 secid) { struct smk_audit_info ad; + struct smack_known *skp; + struct smack_known *tkp = smk_of_task(task_security(p)); smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK); smk_ad_setfield_u_tsk(&ad, p); @@ -1679,15 +1693,14 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info, * can write the receiver. */ if (secid == 0) - return smk_curacc(smk_of_task(task_security(p)), MAY_WRITE, - &ad); + return smk_curacc(tkp->smk_known, MAY_WRITE, &ad); /* * If the secid isn't 0 we're dealing with some USB IO * specific behavior. This is not clean. For one thing * we can't take privilege into account. */ - return smk_access(smack_from_secid(secid), - smk_of_task(task_security(p)), MAY_WRITE, &ad); + skp = smack_from_secid(secid); + return smk_access(skp, tkp->smk_known, MAY_WRITE, &ad); } /** @@ -1719,7 +1732,9 @@ static int smack_task_wait(struct task_struct *p) static void smack_task_to_inode(struct task_struct *p, struct inode *inode) { struct inode_smack *isp = inode->i_security; - isp->smk_inode = smk_of_task(task_security(p)); + struct smack_known *skp = smk_of_task(task_security(p)); + + isp->smk_inode = skp->smk_known; } /* @@ -1738,15 +1753,15 @@ static void smack_task_to_inode(struct task_struct *p, struct inode *inode) */ static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags) { - char *csp = smk_of_current(); + struct smack_known *skp = smk_of_current(); struct socket_smack *ssp; ssp = kzalloc(sizeof(struct socket_smack), gfp_flags); if (ssp == NULL) return -ENOMEM; - ssp->smk_in = csp; - ssp->smk_out = csp; + ssp->smk_in = skp->smk_known; + ssp->smk_out = skp; ssp->smk_packet = NULL; sk->sk_security = ssp; @@ -1833,7 +1848,7 @@ static int smack_netlabel(struct sock *sk, int labeled) labeled == SMACK_UNLABELED_SOCKET) netlbl_sock_delattr(sk); else { - skp = smk_find_entry(ssp->smk_out); + skp = ssp->smk_out; rc = netlbl_sock_setattr(sk, sk->sk_family, &skp->smk_netlabel); } @@ -1856,6 +1871,7 @@ static int smack_netlabel(struct sock *sk, int labeled) */ static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap) { + struct smack_known *skp; int rc; int sk_lbl; char *hostsp; @@ -1874,7 +1890,8 @@ static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap) ad.a.u.net->v4info.daddr = sap->sin_addr.s_addr; #endif sk_lbl = SMACK_UNLABELED_SOCKET; - rc = smk_access(ssp->smk_out, hostsp, MAY_WRITE, &ad); + skp = ssp->smk_out; + rc = smk_access(skp, hostsp, MAY_WRITE, &ad); } else { sk_lbl = SMACK_CIPSO_SOCKET; rc = 0; @@ -1974,8 +1991,8 @@ static int smk_ipv6_port_check(struct sock *sk, struct sockaddr *address, struct sockaddr_in6 *addr6; struct smk_port_label *spp; struct socket_smack *ssp = sk->sk_security; + struct smack_known *skp; unsigned short port = 0; - char *subject; char *object; struct smk_audit_info ad; #ifdef CONFIG_AUDIT @@ -1983,11 +2000,11 @@ static int smk_ipv6_port_check(struct sock *sk, struct sockaddr *address, #endif if (act == SMK_RECEIVING) { - subject = smack_net_ambient; + skp = smack_net_ambient; object = ssp->smk_in; } else { - subject = ssp->smk_out; - object = smack_net_ambient; + skp = ssp->smk_out; + object = smack_net_ambient->smk_known; } /* @@ -2008,7 +2025,7 @@ static int smk_ipv6_port_check(struct sock *sk, struct sockaddr *address, * It's local so the send check has to have passed. */ if (act == SMK_RECEIVING) { - subject = smack_known_web.smk_known; + skp = &smack_known_web; goto auditout; } @@ -2017,7 +2034,7 @@ static int smk_ipv6_port_check(struct sock *sk, struct sockaddr *address, continue; object = spp->smk_in; if (act == SMK_CONNECTING) - ssp->smk_packet = spp->smk_out; + ssp->smk_packet = spp->smk_out->smk_known; break; } @@ -2032,7 +2049,7 @@ auditout: else ad.a.u.net->v6info.daddr = addr6->sin6_addr; #endif - return smk_access(subject, object, MAY_WRITE, &ad); + return smk_access(skp, object, MAY_WRITE, &ad); } /** @@ -2050,7 +2067,7 @@ auditout: static int smack_inode_setsecurity(struct inode *inode, const char *name, const void *value, size_t size, int flags) { - char *sp; + struct smack_known *skp; struct inode_smack *nsp = inode->i_security; struct socket_smack *ssp; struct socket *sock; @@ -2059,12 +2076,12 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name, if (value == NULL || size > SMK_LONGLABEL || size == 0) return -EACCES; - sp = smk_import(value, size); - if (sp == NULL) + skp = smk_import_entry(value, size); + if (skp == NULL) return -EINVAL; if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) { - nsp->smk_inode = sp; + nsp->smk_inode = skp->smk_known; nsp->smk_flags |= SMK_INODE_INSTANT; return 0; } @@ -2081,9 +2098,9 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name, ssp = sock->sk->sk_security; if (strcmp(name, XATTR_SMACK_IPIN) == 0) - ssp->smk_in = sp; + ssp->smk_in = skp->smk_known; else if (strcmp(name, XATTR_SMACK_IPOUT) == 0) { - ssp->smk_out = sp; + ssp->smk_out = skp; if (sock->sk->sk_family == PF_INET) { rc = smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET); if (rc != 0) @@ -2203,7 +2220,9 @@ static int smack_flags_to_may(int flags) */ static int smack_msg_msg_alloc_security(struct msg_msg *msg) { - msg->security = smk_of_current(); + struct smack_known *skp = smk_of_current(); + + msg->security = skp->smk_known; return 0; } @@ -2238,8 +2257,9 @@ static char *smack_of_shm(struct shmid_kernel *shp) static int smack_shm_alloc_security(struct shmid_kernel *shp) { struct kern_ipc_perm *isp = &shp->shm_perm; + struct smack_known *skp = smk_of_current(); - isp->security = smk_of_current(); + isp->security = skp->smk_known; return 0; } @@ -2361,8 +2381,9 @@ static char *smack_of_sem(struct sem_array *sma) static int smack_sem_alloc_security(struct sem_array *sma) { struct kern_ipc_perm *isp = &sma->sem_perm; + struct smack_known *skp = smk_of_current(); - isp->security = smk_of_current(); + isp->security = skp->smk_known; return 0; } @@ -2479,8 +2500,9 @@ static int smack_sem_semop(struct sem_array *sma, struct sembuf *sops, static int smack_msg_queue_alloc_security(struct msg_queue *msq) { struct kern_ipc_perm *kisp = &msq->q_perm; + struct smack_known *skp = smk_of_current(); - kisp->security = smk_of_current(); + kisp->security = skp->smk_known; return 0; } @@ -2652,8 +2674,8 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) struct super_block *sbp; struct superblock_smack *sbsp; struct inode_smack *isp; - char *csp = smk_of_current(); - char *fetched; + struct smack_known *skp; + struct smack_known *ckp = smk_of_current(); char *final; char trattr[TRANS_TRUE_SIZE]; int transflag = 0; @@ -2720,7 +2742,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) * Programs that change smack have to treat the * pty with respect. */ - final = csp; + final = ckp->smk_known; break; case SOCKFS_MAGIC: /* @@ -2775,9 +2797,9 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) * Get the dentry for xattr. */ dp = dget(opt_dentry); - fetched = smk_fetch(XATTR_NAME_SMACK, inode, dp); - if (fetched != NULL) - final = fetched; + skp = smk_fetch(XATTR_NAME_SMACK, inode, dp); + if (skp != NULL) + final = skp->smk_known; /* * Transmuting directory @@ -2817,7 +2839,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) } if (final == NULL) - isp->smk_inode = csp; + isp->smk_inode = ckp->smk_known; else isp->smk_inode = final; @@ -2840,13 +2862,14 @@ unlockandout: */ static int smack_getprocattr(struct task_struct *p, char *name, char **value) { + struct smack_known *skp = smk_of_task(task_security(p)); char *cp; int slen; if (strcmp(name, "current") != 0) return -EINVAL; - cp = kstrdup(smk_of_task(task_security(p)), GFP_KERNEL); + cp = kstrdup(skp->smk_known, GFP_KERNEL); if (cp == NULL) return -ENOMEM; @@ -2872,7 +2895,7 @@ static int smack_setprocattr(struct task_struct *p, char *name, { struct task_smack *tsp; struct cred *new; - char *newsmack; + struct smack_known *skp; /* * Changing another process' Smack value is too dangerous @@ -2890,14 +2913,14 @@ static int smack_setprocattr(struct task_struct *p, char *name, if (strcmp(name, "current") != 0) return -EINVAL; - newsmack = smk_import(value, size); - if (newsmack == NULL) + skp = smk_import_entry(value, size); + if (skp == NULL) return -EINVAL; /* * No process is ever allowed the web ("@") label. */ - if (newsmack == smack_known_web.smk_known) + if (skp == &smack_known_web) return -EPERM; new = prepare_creds(); @@ -2905,7 +2928,7 @@ static int smack_setprocattr(struct task_struct *p, char *name, return -ENOMEM; tsp = new->security; - tsp->smk_task = newsmack; + tsp->smk_task = skp; commit_creds(new); return size; @@ -2923,6 +2946,7 @@ static int smack_setprocattr(struct task_struct *p, char *name, static int smack_unix_stream_connect(struct sock *sock, struct sock *other, struct sock *newsk) { + struct smack_known *skp; struct socket_smack *ssp = sock->sk_security; struct socket_smack *osp = other->sk_security; struct socket_smack *nsp = newsk->sk_security; @@ -2936,15 +2960,17 @@ static int smack_unix_stream_connect(struct sock *sock, smk_ad_setfield_u_net_sk(&ad, other); #endif - if (!smack_privileged(CAP_MAC_OVERRIDE)) - rc = smk_access(ssp->smk_out, osp->smk_in, MAY_WRITE, &ad); + if (!smack_privileged(CAP_MAC_OVERRIDE)) { + skp = ssp->smk_out; + rc = smk_access(skp, osp->smk_in, MAY_WRITE, &ad); + } /* * Cross reference the peer labels for SO_PEERSEC. */ if (rc == 0) { - nsp->smk_packet = ssp->smk_out; - ssp->smk_packet = osp->smk_out; + nsp->smk_packet = ssp->smk_out->smk_known; + ssp->smk_packet = osp->smk_out->smk_known; } return rc; @@ -2962,8 +2988,8 @@ static int smack_unix_may_send(struct socket *sock, struct socket *other) { struct socket_smack *ssp = sock->sk->sk_security; struct socket_smack *osp = other->sk->sk_security; + struct smack_known *skp; struct smk_audit_info ad; - int rc = 0; #ifdef CONFIG_AUDIT struct lsm_network_audit net; @@ -2972,10 +2998,11 @@ static int smack_unix_may_send(struct socket *sock, struct socket *other) smk_ad_setfield_u_net_sk(&ad, other->sk); #endif - if (!smack_privileged(CAP_MAC_OVERRIDE)) - rc = smk_access(ssp->smk_out, osp->smk_in, MAY_WRITE, &ad); + if (smack_privileged(CAP_MAC_OVERRIDE)) + return 0; - return rc; + skp = ssp->smk_out; + return smk_access(skp, osp->smk_in, MAY_WRITE, &ad); } /** @@ -3017,13 +3044,12 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg, * @sap: netlabel secattr * @ssp: socket security information * - * Returns a pointer to a Smack label found on the label list. + * Returns a pointer to a Smack label entry found on the label list. */ -static char *smack_from_secattr(struct netlbl_lsm_secattr *sap, - struct socket_smack *ssp) +static struct smack_known *smack_from_secattr(struct netlbl_lsm_secattr *sap, + struct socket_smack *ssp) { - struct smack_known *kp; - char *sp; + struct smack_known *skp; int found = 0; if ((sap->flags & NETLBL_SECATTR_MLS_LVL) != 0) { @@ -3038,11 +3064,11 @@ static char *smack_from_secattr(struct netlbl_lsm_secattr *sap, * ambient value. */ rcu_read_lock(); - list_for_each_entry(kp, &smack_known_list, list) { - if (sap->attr.mls.lvl != kp->smk_netlabel.attr.mls.lvl) + list_for_each_entry(skp, &smack_known_list, list) { + if (sap->attr.mls.lvl != skp->smk_netlabel.attr.mls.lvl) continue; if (memcmp(sap->attr.mls.cat, - kp->smk_netlabel.attr.mls.cat, + skp->smk_netlabel.attr.mls.cat, SMK_CIPSOLEN) != 0) continue; found = 1; @@ -3051,17 +3077,17 @@ static char *smack_from_secattr(struct netlbl_lsm_secattr *sap, rcu_read_unlock(); if (found) - return kp->smk_known; + return skp; if (ssp != NULL && ssp->smk_in == smack_known_star.smk_known) - return smack_known_web.smk_known; - return smack_known_star.smk_known; + return &smack_known_web; + return &smack_known_star; } if ((sap->flags & NETLBL_SECATTR_SECID) != 0) { /* * Looks like a fallback, which gives us a secid. */ - sp = smack_from_secid(sap->attr.secid); + skp = smack_from_secid(sap->attr.secid); /* * This has got to be a bug because it is * impossible to specify a fallback without @@ -3069,8 +3095,8 @@ static char *smack_from_secattr(struct netlbl_lsm_secattr *sap, * it has a secid, and the only way to get a * secid is from a fallback. */ - BUG_ON(sp == NULL); - return sp; + BUG_ON(skp == NULL); + return skp; } /* * Without guidance regarding the smack value @@ -3139,8 +3165,8 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) { struct netlbl_lsm_secattr secattr; struct socket_smack *ssp = sk->sk_security; + struct smack_known *skp; struct sockaddr sadd; - char *csp; int rc = 0; struct smk_audit_info ad; #ifdef CONFIG_AUDIT @@ -3155,9 +3181,9 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) rc = netlbl_skbuff_getattr(skb, sk->sk_family, &secattr); if (rc == 0) - csp = smack_from_secattr(&secattr, ssp); + skp = smack_from_secattr(&secattr, ssp); else - csp = smack_net_ambient; + skp = smack_net_ambient; netlbl_secattr_destroy(&secattr); @@ -3173,7 +3199,7 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) * This is the simplist possible security model * for networking. */ - rc = smk_access(csp, ssp->smk_in, MAY_WRITE, &ad); + rc = smk_access(skp, ssp->smk_in, MAY_WRITE, &ad); if (rc != 0) netlbl_skbuff_err(skb, rc, 0); break; @@ -3238,7 +3264,7 @@ static int smack_socket_getpeersec_dgram(struct socket *sock, { struct netlbl_lsm_secattr secattr; struct socket_smack *ssp = NULL; - char *sp; + struct smack_known *skp; int family = PF_UNSPEC; u32 s = 0; /* 0 is the invalid secid */ int rc; @@ -3254,7 +3280,7 @@ static int smack_socket_getpeersec_dgram(struct socket *sock, if (family == PF_UNIX) { ssp = sock->sk->sk_security; - s = smack_to_secid(ssp->smk_out); + s = ssp->smk_out->smk_secid; } else if (family == PF_INET || family == PF_INET6) { /* * Translate what netlabel gave us. @@ -3264,8 +3290,8 @@ static int smack_socket_getpeersec_dgram(struct socket *sock, netlbl_secattr_init(&secattr); rc = netlbl_skbuff_getattr(skb, family, &secattr); if (rc == 0) { - sp = smack_from_secattr(&secattr, ssp); - s = smack_to_secid(sp); + skp = smack_from_secattr(&secattr, ssp); + s = skp->smk_secid; } netlbl_secattr_destroy(&secattr); } @@ -3286,13 +3312,15 @@ static int smack_socket_getpeersec_dgram(struct socket *sock, static void smack_sock_graft(struct sock *sk, struct socket *parent) { struct socket_smack *ssp; + struct smack_known *skp = smk_of_current(); if (sk == NULL || (sk->sk_family != PF_INET && sk->sk_family != PF_INET6)) return; ssp = sk->sk_security; - ssp->smk_in = ssp->smk_out = smk_of_current(); + ssp->smk_in = skp->smk_known; + ssp->smk_out = skp; /* cssp->smk_packet is already set in smack_inet_csk_clone() */ } @@ -3314,7 +3342,6 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, struct netlbl_lsm_secattr secattr; struct sockaddr_in addr; struct iphdr *hdr; - char *sp; char *hsp; int rc; struct smk_audit_info ad; @@ -3337,9 +3364,9 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, netlbl_secattr_init(&secattr); rc = netlbl_skbuff_getattr(skb, family, &secattr); if (rc == 0) - sp = smack_from_secattr(&secattr, ssp); + skp = smack_from_secattr(&secattr, ssp); else - sp = smack_known_huh.smk_known; + skp = &smack_known_huh; netlbl_secattr_destroy(&secattr); #ifdef CONFIG_AUDIT @@ -3352,7 +3379,7 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, * Receiving a packet requires that the other end be able to write * here. Read access is not required. */ - rc = smk_access(sp, ssp->smk_in, MAY_WRITE, &ad); + rc = smk_access(skp, ssp->smk_in, MAY_WRITE, &ad); if (rc != 0) return rc; @@ -3360,7 +3387,7 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, * Save the peer's label in the request_sock so we can later setup * smk_packet in the child socket so that SO_PEERCRED can report it. */ - req->peer_secid = smack_to_secid(sp); + req->peer_secid = skp->smk_secid; /* * We need to decide if we want to label the incoming connection here @@ -3373,10 +3400,9 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, hsp = smack_host_label(&addr); rcu_read_unlock(); - if (hsp == NULL) { - skp = smk_find_entry(sp); + if (hsp == NULL) rc = netlbl_req_setattr(req, &skp->smk_netlabel); - } else + else netlbl_req_delattr(req); return rc; @@ -3393,10 +3419,12 @@ static void smack_inet_csk_clone(struct sock *sk, const struct request_sock *req) { struct socket_smack *ssp = sk->sk_security; + struct smack_known *skp; - if (req->peer_secid != 0) - ssp->smk_packet = smack_from_secid(req->peer_secid); - else + if (req->peer_secid != 0) { + skp = smack_from_secid(req->peer_secid); + ssp->smk_packet = skp->smk_known; + } else ssp->smk_packet = NULL; } @@ -3422,7 +3450,9 @@ static void smack_inet_csk_clone(struct sock *sk, static int smack_key_alloc(struct key *key, const struct cred *cred, unsigned long flags) { - key->security = smk_of_task(cred->security); + struct smack_known *skp = smk_of_task(cred->security); + + key->security = skp->smk_known; return 0; } @@ -3451,7 +3481,7 @@ static int smack_key_permission(key_ref_t key_ref, { struct key *keyp; struct smk_audit_info ad; - char *tsp = smk_of_task(cred->security); + struct smack_known *tkp = smk_of_task(cred->security); keyp = key_ref_to_ptr(key_ref); if (keyp == NULL) @@ -3465,15 +3495,14 @@ static int smack_key_permission(key_ref_t key_ref, /* * This should not occur */ - if (tsp == NULL) + if (tkp == NULL) return -EACCES; #ifdef CONFIG_AUDIT smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_KEY); ad.a.u.key_struct.key = keyp->serial; ad.a.u.key_struct.key_desc = keyp->description; #endif - return smk_access(tsp, keyp->security, - MAY_READWRITE, &ad); + return smk_access(tkp, keyp->security, MAY_READWRITE, &ad); } #endif /* CONFIG_KEYS */ @@ -3555,7 +3584,7 @@ static int smack_audit_rule_known(struct audit_krule *krule) static int smack_audit_rule_match(u32 secid, u32 field, u32 op, void *vrule, struct audit_context *actx) { - char *smack; + struct smack_known *skp; char *rule = vrule; if (!rule) { @@ -3567,7 +3596,7 @@ static int smack_audit_rule_match(u32 secid, u32 field, u32 op, void *vrule, if (field != AUDIT_SUBJ_USER && field != AUDIT_OBJ_USER) return 0; - smack = smack_from_secid(secid); + skp = smack_from_secid(secid); /* * No need to do string comparisons. If a match occurs, @@ -3575,9 +3604,9 @@ static int smack_audit_rule_match(u32 secid, u32 field, u32 op, void *vrule, * label. */ if (op == Audit_equal) - return (rule == smack); + return (rule == skp->smk_known); if (op == Audit_not_equal) - return (rule != smack); + return (rule != skp->smk_known); return 0; } @@ -3605,11 +3634,11 @@ static void smack_audit_rule_free(void *vrule) */ static int smack_secid_to_secctx(u32 secid, char **secdata, u32 *seclen) { - char *sp = smack_from_secid(secid); + struct smack_known *skp = smack_from_secid(secid); if (secdata) - *secdata = sp; - *seclen = strlen(sp); + *secdata = skp->smk_known; + *seclen = strlen(skp->smk_known); return 0; } @@ -3845,8 +3874,8 @@ static __init int smack_init(void) if (!security_module_enable(&smack_ops)) return 0; - tsp = new_task_smack(smack_known_floor.smk_known, - smack_known_floor.smk_known, GFP_KERNEL); + tsp = new_task_smack(&smack_known_floor, &smack_known_floor, + GFP_KERNEL); if (tsp == NULL) return -ENOMEM; diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 53a08b85bda4..3c79cba5fa4a 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -66,7 +66,7 @@ static DEFINE_MUTEX(smk_netlbladdr_lock); * If it isn't somehow marked, use this. * It can be reset via smackfs/ambient */ -char *smack_net_ambient; +struct smack_known *smack_net_ambient; /* * This is the level in a CIPSO header that indicates a @@ -112,7 +112,7 @@ struct smack_master_list { LIST_HEAD(smack_rule_list); struct smack_parsed_rule { - char *smk_subject; + struct smack_known *smk_subject; char *smk_object; int smk_access1; int smk_access2; @@ -163,9 +163,11 @@ static inline void smack_catset_bit(unsigned int cat, char *catsetp) */ static void smk_netlabel_audit_set(struct netlbl_audit *nap) { + struct smack_known *skp = smk_of_current(); + nap->loginuid = audit_get_loginuid(current); nap->sessionid = audit_get_sessionid(current); - nap->secid = smack_to_secid(smk_of_current()); + nap->secid = skp->smk_secid; } /* @@ -306,7 +308,7 @@ static int smk_fill_rule(const char *subject, const char *object, struct smack_known *skp; if (import) { - rule->smk_subject = smk_import(subject, len); + rule->smk_subject = smk_import_entry(subject, len); if (rule->smk_subject == NULL) return -1; @@ -321,7 +323,7 @@ static int smk_fill_rule(const char *subject, const char *object, kfree(cp); if (skp == NULL) return -1; - rule->smk_subject = skp->smk_known; + rule->smk_subject = skp; cp = smk_parse_smack(object, len); if (cp == NULL) @@ -445,7 +447,6 @@ static ssize_t smk_write_rules_list(struct file *file, const char __user *buf, struct list_head *rule_list, struct mutex *rule_lock, int format) { - struct smack_known *skp; struct smack_parsed_rule *rule; char *data; int datalen; @@ -505,12 +506,10 @@ static ssize_t smk_write_rules_list(struct file *file, const char __user *buf, goto out_free_rule; } - if (rule_list == NULL) { load = 1; - skp = smk_find_entry(rule->smk_subject); - rule_list = &skp->smk_rules; - rule_lock = &skp->smk_rules_lock; + rule_list = &rule->smk_subject->smk_rules; + rule_lock = &rule->smk_subject->smk_rules_lock; } rc = smk_set_access(rule, rule_list, rule_lock, load); @@ -579,13 +578,14 @@ static void smk_rule_show(struct seq_file *s, struct smack_rule *srp, int max) * because you should expect to be able to write * anything you read back. */ - if (strlen(srp->smk_subject) >= max || strlen(srp->smk_object) >= max) + if (strlen(srp->smk_subject->smk_known) >= max || + strlen(srp->smk_object) >= max) return; if (srp->smk_access == 0) return; - seq_printf(s, "%s %s", srp->smk_subject, srp->smk_object); + seq_printf(s, "%s %s", srp->smk_subject->smk_known, srp->smk_object); seq_putc(s, ' '); @@ -738,9 +738,9 @@ static void smk_unlbl_ambient(char *oldambient) __func__, __LINE__, rc); } if (smack_net_ambient == NULL) - smack_net_ambient = smack_known_floor.smk_known; + smack_net_ambient = &smack_known_floor; - rc = netlbl_cfg_unlbl_map_add(smack_net_ambient, PF_INET, + rc = netlbl_cfg_unlbl_map_add(smack_net_ambient->smk_known, PF_INET, NULL, NULL, &nai); if (rc != 0) printk(KERN_WARNING "%s:%d add rc = %d\n", @@ -1535,11 +1535,12 @@ static ssize_t smk_read_ambient(struct file *filp, char __user *buf, */ mutex_lock(&smack_ambient_lock); - asize = strlen(smack_net_ambient) + 1; + asize = strlen(smack_net_ambient->smk_known) + 1; if (cn >= asize) rc = simple_read_from_buffer(buf, cn, ppos, - smack_net_ambient, asize); + smack_net_ambient->smk_known, + asize); else rc = -EINVAL; @@ -1560,8 +1561,8 @@ static ssize_t smk_read_ambient(struct file *filp, char __user *buf, static ssize_t smk_write_ambient(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { + struct smack_known *skp; char *oldambient; - char *smack = NULL; char *data; int rc = count; @@ -1577,16 +1578,16 @@ static ssize_t smk_write_ambient(struct file *file, const char __user *buf, goto out; } - smack = smk_import(data, count); - if (smack == NULL) { + skp = smk_import_entry(data, count); + if (skp == NULL) { rc = -EINVAL; goto out; } mutex_lock(&smack_ambient_lock); - oldambient = smack_net_ambient; - smack_net_ambient = smack; + oldambient = smack_net_ambient->smk_known; + smack_net_ambient = skp; smk_unlbl_ambient(oldambient); mutex_unlock(&smack_ambient_lock); @@ -1645,7 +1646,7 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { char *data; - char *sp = smk_of_task(current->cred->security); + struct smack_known *skp = smk_of_task(current->cred->security); int rc = count; if (!smack_privileged(CAP_MAC_ADMIN)) @@ -1656,7 +1657,7 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf, * explicitly for clarity. The smk_access() implementation * would use smk_access(smack_onlycap, MAY_WRITE) */ - if (smack_onlycap != NULL && smack_onlycap != sp) + if (smack_onlycap != NULL && smack_onlycap != skp->smk_known) return -EPERM; data = kzalloc(count, GFP_KERNEL); @@ -1866,8 +1867,8 @@ static ssize_t smk_user_access(struct file *file, const char __user *buf, if (res) return -EINVAL; - res = smk_access(rule.smk_subject, rule.smk_object, rule.smk_access1, - NULL); + res = smk_access(rule.smk_subject, rule.smk_object, + rule.smk_access1, NULL); data[0] = res == 0 ? '1' : '0'; data[1] = '\0'; -- cgit v1.2.3 From e830b39412ca2bbedd7508243f21c04d57ad543c Mon Sep 17 00:00:00 2001 From: Casey Schaufler Date: Wed, 22 May 2013 18:43:07 -0700 Subject: Smack: Add smkfstransmute mount option Suppliment the smkfsroot mount option with another, smkfstransmute, that does the same thing but also marks the root inode as transmutting. This allows a freshly created filesystem to be mounted with a transmutting heirarchy. Targeted for git://git.gitorious.org/smack-next/kernel.git Signed-off-by: Casey Schaufler --- security/smack/smack.h | 1 + security/smack/smack_lsm.c | 25 ++++++++++++++++++++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/security/smack/smack.h b/security/smack/smack.h index 159f25bfcf45..339614c76e63 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -143,6 +143,7 @@ struct smk_port_label { #define SMK_FSFLOOR "smackfsfloor=" #define SMK_FSHAT "smackfshat=" #define SMK_FSROOT "smackfsroot=" +#define SMK_FSTRANS "smackfstransmute=" #define SMACK_CIPSO_OPTION "-CIPSO" diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 3669d9f9824e..6a083303501d 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -261,8 +261,9 @@ static int smack_sb_alloc_security(struct super_block *sb) sbsp->smk_default = smack_known_floor.smk_known; sbsp->smk_floor = smack_known_floor.smk_known; sbsp->smk_hat = smack_known_hat.smk_known; - sbsp->smk_initialized = 0; - + /* + * smk_initialized will be zero from kzalloc. + */ sb->s_security = sbsp; return 0; @@ -306,6 +307,8 @@ static int smack_sb_copy_data(char *orig, char *smackopts) dp = smackopts; else if (strstr(cp, SMK_FSROOT) == cp) dp = smackopts; + else if (strstr(cp, SMK_FSTRANS) == cp) + dp = smackopts; else dp = otheropts; @@ -341,8 +344,9 @@ static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data) char *op; char *commap; char *nsp; + int transmute = 0; - if (sp->smk_initialized != 0) + if (sp->smk_initialized) return 0; sp->smk_initialized = 1; @@ -373,6 +377,13 @@ static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data) nsp = smk_import(op, 0); if (nsp != NULL) sp->smk_root = nsp; + } else if (strncmp(op, SMK_FSTRANS, strlen(SMK_FSTRANS)) == 0) { + op += strlen(SMK_FSTRANS); + nsp = smk_import(op, 0); + if (nsp != NULL) { + sp->smk_root = nsp; + transmute = 1; + } } } @@ -380,11 +391,15 @@ static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data) * Initialize the root inode. */ isp = inode->i_security; - if (isp == NULL) + if (inode->i_security == NULL) { inode->i_security = new_inode_smack(sp->smk_root); - else + isp = inode->i_security; + } else isp->smk_inode = sp->smk_root; + if (transmute) + isp->smk_flags |= SMK_INODE_TRANSMUTE; + return 0; } -- cgit v1.2.3 From ea221e64140f6fa543c29e31349001167b9f8ad0 Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Tue, 28 May 2013 10:55:09 +0800 Subject: x86/PCI: Increase info->res_num before checking pci_use_crs We should increase info->res_num before we checking pci_use_crs return when pci=nocrs set. No functional change, since we don't use res_num and res_offset[] in the "!pci_use_crs" case anyway, but this makes the code read better. Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas Cc: Yinghai Lu Cc: Jiang Liu Cc: "Rafael J. Wysocki" Cc: Feng Tang --- arch/x86/pci/acpi.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index 3e724256dbee..d641897a1f4e 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c @@ -324,14 +324,11 @@ setup_resource(struct acpi_resource *acpi_res, void *data) res->start = start; res->end = end; info->res_offset[info->res_num] = addr.translation_offset; + info->res_num++; - if (!pci_use_crs) { + if (!pci_use_crs) dev_printk(KERN_DEBUG, &info->bridge->dev, "host bridge window %pR (ignored)\n", res); - return AE_OK; - } - - info->res_num++; return AE_OK; } -- cgit v1.2.3 From 8cd77a0bd4b4a7d02c2a6926a69585d8088ee721 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Mon, 27 May 2013 20:11:27 +0900 Subject: Smack: Fix possible NULL pointer dereference at smk_netlbl_mls() netlbl_secattr_catmap_alloc(GFP_ATOMIC) can return NULL. Signed-off-by: Tetsuo Handa --- security/smack/smack_access.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index 53f2327a592f..6a0377f38620 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -403,6 +403,8 @@ int smk_netlbl_mls(int level, char *catset, struct netlbl_lsm_secattr *sap, sap->flags |= NETLBL_SECATTR_MLS_CAT; sap->attr.mls.lvl = level; sap->attr.mls.cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC); + if (!sap->attr.mls.cat) + return -ENOMEM; sap->attr.mls.cat->startbit = 0; for (cat = 1, cp = catset, byte = 0; byte < len; cp++, byte++) -- cgit v1.2.3 From 2dfca877b3c599ec081c23939b34ee22576e0833 Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Tue, 28 May 2013 16:03:22 +0800 Subject: PCI: Fix kerneldoc for pci_disable_link_state() Fix kerneldoc for pci_disable_link_state(). [bhelgaas: expand comment, fix typos] Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aspm.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index faa83b632a84..403a44374ed5 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -714,10 +714,6 @@ void pcie_aspm_powersave_config_link(struct pci_dev *pdev) up_read(&pci_bus_sem); } -/* - * pci_disable_link_state - disable pci device's link state, so the link will - * never enter specific states - */ static void __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem, bool force) { @@ -771,6 +767,15 @@ void pci_disable_link_state_locked(struct pci_dev *pdev, int state) } EXPORT_SYMBOL(pci_disable_link_state_locked); +/** + * pci_disable_link_state - Disable device's link state, so the link will + * never enter specific states. Note that if the BIOS didn't grant ASPM + * control to the OS, this does nothing because we can't touch the LNKCTL + * register. + * + * @pdev: PCI device + * @state: ASPM link state to disable + */ void pci_disable_link_state(struct pci_dev *pdev, int state) { __pci_disable_link_state(pdev, state, true, false); -- cgit v1.2.3 From e7d4515209db5246245eb5667a94ad86ba9a12cc Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Tue, 28 May 2013 16:03:46 +0800 Subject: PCI: Replace printks with appropriate pr_*() Replace deprecated printk(KERN_ERR...) with pr_err() in pci-acpi.c Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas --- drivers/pci/pci-acpi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index e4b1fb2c0f5d..6c15d6a96935 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -376,12 +376,12 @@ static int __init acpi_pci_init(void) int ret; if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_MSI) { - printk(KERN_INFO"ACPI FADT declares the system doesn't support MSI, so disable it\n"); + pr_info("ACPI FADT declares the system doesn't support MSI, so disable it\n"); pci_no_msi(); } if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_ASPM) { - printk(KERN_INFO"ACPI FADT declares the system doesn't support PCIe ASPM, so disable it\n"); + pr_info("ACPI FADT declares the system doesn't support PCIe ASPM, so disable it\n"); pcie_no_aspm(); } -- cgit v1.2.3 From 65f6ae66a6fb44f614a1226c398fcb38e94b3c59 Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Mon, 13 May 2013 11:05:48 +0200 Subject: PCI: Allocate only as many MSI vectors as requested by driver Because of the encoding of the "Multiple Message Capable" and "Multiple Message Enable" fields, a device can only advertise that it's capable of a power-of-two number of vectors, and the OS can only enable a power-of-two number. For example, a device that's limited internally to using 18 vectors would have to advertise that it's capable of 32. The 14 extra vectors consume vector numbers and IRQ descriptors even though the device can't actually use them. This fix introduces a 'msi_desc::nvec_used' field to address this issue. When non-zero, it is the actual number of MSIs the device will send, as requested by the device driver. This value should be used by architectures to set up and tear down only as many interrupt resources as the device will actually use. Note, although the existing 'msi_desc::multiple' field might seem redundant, in fact it is not. The number of MSIs advertised need not be the smallest power-of-two larger than the number of MSIs the device will send. Thus, it is not always possible to derive the former from the latter, so we need to keep them both to handle this case. [bhelgaas: changelog, rename to "nvec_used"] Signed-off-by: Alexander Gordeev Signed-off-by: Bjorn Helgaas --- drivers/pci/msi.c | 10 ++++++++-- include/linux/msi.h | 1 + 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 2c1075213bec..aca7578b05e5 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -81,7 +81,10 @@ void default_teardown_msi_irqs(struct pci_dev *dev) int i, nvec; if (entry->irq == 0) continue; - nvec = 1 << entry->msi_attrib.multiple; + if (entry->nvec_used) + nvec = entry->nvec_used; + else + nvec = 1 << entry->msi_attrib.multiple; for (i = 0; i < nvec; i++) arch_teardown_msi_irq(entry->irq + i); } @@ -336,7 +339,10 @@ static void free_msi_irqs(struct pci_dev *dev) int i, nvec; if (!entry->irq) continue; - nvec = 1 << entry->msi_attrib.multiple; + if (entry->nvec_used) + nvec = entry->nvec_used; + else + nvec = 1 << entry->msi_attrib.multiple; #ifdef CONFIG_GENERIC_HARDIRQS for (i = 0; i < nvec; i++) BUG_ON(irq_has_action(entry->irq + i)); diff --git a/include/linux/msi.h b/include/linux/msi.h index 20c2d6dd5d25..ee66f3a12fb6 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -35,6 +35,7 @@ struct msi_desc { u32 masked; /* mask bits */ unsigned int irq; + unsigned int nvec_used; /* number of messages */ struct list_head list; union { -- cgit v1.2.3 From cfdadef30739301fdcc4411e8260d88c249dc45a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 22 Apr 2013 16:01:53 +0300 Subject: 9p: trace: use %*ph to dump buffer Additionally change cast from long to unsigned long to follow specificator. Signed-off-by: Andy Shevchenko Signed-off-by: Eric Van Hensbergen --- include/trace/events/9p.h | 28 +++------------------------- 1 file changed, 3 insertions(+), 25 deletions(-) diff --git a/include/trace/events/9p.h b/include/trace/events/9p.h index beeaed8398ec..a0666362c111 100644 --- a/include/trace/events/9p.h +++ b/include/trace/events/9p.h @@ -143,31 +143,9 @@ TRACE_EVENT(9p_protocol_dump, __entry->tag = pdu->tag; memcpy(__entry->line, pdu->sdata, P9_PROTO_DUMP_SZ); ), - TP_printk("clnt %lu %s(tag = %d)\n%.3x: " - "%02x %02x %02x %02x %02x %02x %02x %02x " - "%02x %02x %02x %02x %02x %02x %02x %02x\n" - "%.3x: " - "%02x %02x %02x %02x %02x %02x %02x %02x " - "%02x %02x %02x %02x %02x %02x %02x %02x\n", - (long)__entry->clnt, show_9p_op(__entry->type), - __entry->tag, 0, - __entry->line[0], __entry->line[1], - __entry->line[2], __entry->line[3], - __entry->line[4], __entry->line[5], - __entry->line[6], __entry->line[7], - __entry->line[8], __entry->line[9], - __entry->line[10], __entry->line[11], - __entry->line[12], __entry->line[13], - __entry->line[14], __entry->line[15], - 16, - __entry->line[16], __entry->line[17], - __entry->line[18], __entry->line[19], - __entry->line[20], __entry->line[21], - __entry->line[22], __entry->line[23], - __entry->line[24], __entry->line[25], - __entry->line[26], __entry->line[27], - __entry->line[28], __entry->line[29], - __entry->line[30], __entry->line[31]) + TP_printk("clnt %lu %s(tag = %d)\n%.3x: %16ph\n%.3x: %16ph\n", + (unsigned long)__entry->clnt, show_9p_op(__entry->type), + __entry->tag, 0, __entry->line, 16, __entry->line + 16) ); #endif /* _TRACE_9P_H */ -- cgit v1.2.3 From 2315cb14010c4cb0eb7c1d19fcf90475e4688207 Mon Sep 17 00:00:00 2001 From: Rob Landley Date: Tue, 28 May 2013 13:47:58 -0500 Subject: 9p: Add rest of 9p files to MAINTAINERS entry It was missing net and include directories. Signed-off-by: Eric Van Hensbergen --- MAINTAINERS | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 8bdd7a7ef2f4..c6ed9df8b999 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -178,6 +178,11 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/ericvh/v9fs.git S: Maintained F: Documentation/filesystems/9p.txt F: fs/9p/ +F: net/9p/ +F: include/net/9p/ +F: include/uapi/linux/virtio_9p.h +F: include/trace/events/9p.h + A8293 MEDIA DRIVER M: Antti Palosaari -- cgit v1.2.3 From 351638e7deeed2ec8ce451b53d33921b3da68f83 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 28 May 2013 01:30:21 +0000 Subject: net: pass info struct via netdevice notifier So far, only net_device * could be passed along with netdevice notifier event. This patch provides a possibility to pass custom structure able to provide info that event listener needs to know. Signed-off-by: Jiri Pirko v2->v3: fix typo on simeth shortened dev_getter shortened notifier_info struct name v1->v2: fix notifier_call parameter in call_netdevice_notifier() Signed-off-by: David S. Miller --- arch/ia64/hp/sim/simeth.c | 2 +- arch/mips/txx9/generic/setup_tx4939.c | 3 +- drivers/infiniband/core/cma.c | 4 +- drivers/infiniband/hw/mlx4/main.c | 2 +- drivers/net/bonding/bond_main.c | 2 +- drivers/net/can/led.c | 4 +- drivers/net/ethernet/broadcom/cnic.c | 2 +- drivers/net/ethernet/marvell/skge.c | 2 +- drivers/net/ethernet/marvell/sky2.c | 2 +- .../net/ethernet/qlogic/netxen/netxen_nic_main.c | 2 +- drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 2 +- drivers/net/ethernet/sfc/efx.c | 2 +- drivers/net/hamradio/bpqether.c | 7 +-- drivers/net/macvlan.c | 2 +- drivers/net/macvtap.c | 2 +- drivers/net/netconsole.c | 5 +- drivers/net/ppp/pppoe.c | 2 +- drivers/net/team/team.c | 2 +- drivers/net/wan/dlci.c | 2 +- drivers/net/wan/hdlc.c | 2 +- drivers/net/wan/lapbether.c | 2 +- drivers/scsi/fcoe/fcoe.c | 2 +- drivers/scsi/fcoe/fcoe_transport.c | 2 +- drivers/staging/csr/netdev.c | 2 +- drivers/staging/ft1000/ft1000-pcmcia/ft1000_proc.c | 2 +- drivers/staging/ft1000/ft1000-usb/ft1000_proc.c | 2 +- drivers/staging/silicom/bpctl_mod.c | 2 +- include/linux/netdevice.h | 13 +++++ net/8021q/vlan.c | 2 +- net/appletalk/aarp.c | 2 +- net/appletalk/ddp.c | 2 +- net/atm/clip.c | 4 +- net/atm/mpc.c | 6 +-- net/ax25/af_ax25.c | 6 +-- net/batman-adv/hard-interface.c | 2 +- net/bridge/br_notify.c | 2 +- net/caif/caif_dev.c | 4 +- net/caif/caif_usb.c | 4 +- net/can/af_can.c | 4 +- net/can/bcm.c | 4 +- net/can/gw.c | 4 +- net/can/raw.c | 4 +- net/core/dev.c | 56 ++++++++++++++++++---- net/core/drop_monitor.c | 4 +- net/core/dst.c | 2 +- net/core/fib_rules.c | 4 +- net/core/netprio_cgroup.c | 2 +- net/core/pktgen.c | 2 +- net/core/rtnetlink.c | 2 +- net/decnet/af_decnet.c | 4 +- net/ieee802154/6lowpan.c | 5 +- net/ipv4/arp.c | 2 +- net/ipv4/devinet.c | 2 +- net/ipv4/fib_frontend.c | 2 +- net/ipv4/ipmr.c | 2 +- net/ipv4/netfilter/ipt_MASQUERADE.c | 2 +- net/ipv6/addrconf.c | 4 +- net/ipv6/ip6mr.c | 2 +- net/ipv6/ndisc.c | 2 +- net/ipv6/netfilter/ip6t_MASQUERADE.c | 2 +- net/ipv6/route.c | 4 +- net/ipx/af_ipx.c | 2 +- net/iucv/af_iucv.c | 2 +- net/mac80211/iface.c | 5 +- net/netfilter/ipvs/ip_vs_ctl.c | 4 +- net/netfilter/nfnetlink_queue_core.c | 2 +- net/netfilter/xt_TEE.c | 2 +- net/netlabel/netlabel_unlabeled.c | 7 ++- net/netrom/af_netrom.c | 2 +- net/openvswitch/dp_notify.c | 2 +- net/packet/af_packet.c | 5 +- net/phonet/pn_dev.c | 4 +- net/rose/af_rose.c | 6 +-- net/sched/act_mirred.c | 2 +- net/tipc/eth_media.c | 4 +- net/tipc/ib_media.c | 4 +- net/wireless/core.c | 5 +- net/x25/af_x25.c | 2 +- net/xfrm/xfrm_policy.c | 2 +- security/selinux/netif.c | 2 +- 80 files changed, 172 insertions(+), 127 deletions(-) diff --git a/arch/ia64/hp/sim/simeth.c b/arch/ia64/hp/sim/simeth.c index c13064e422df..d1b04c4c95e3 100644 --- a/arch/ia64/hp/sim/simeth.c +++ b/arch/ia64/hp/sim/simeth.c @@ -268,7 +268,7 @@ static __inline__ int dev_is_ethdev(struct net_device *dev) static int simeth_device_event(struct notifier_block *this,unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct simeth_local *local; struct in_device *in_dev; struct in_ifaddr **ifap = NULL; diff --git a/arch/mips/txx9/generic/setup_tx4939.c b/arch/mips/txx9/generic/setup_tx4939.c index 729a50991780..b7eccbd17bf7 100644 --- a/arch/mips/txx9/generic/setup_tx4939.c +++ b/arch/mips/txx9/generic/setup_tx4939.c @@ -331,7 +331,8 @@ static int tx4939_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); + if (event == NETDEV_CHANGE && netif_carrier_ok(dev)) { __u64 bit = 0; if (dev->irq == TXX9_IRQ_BASE + TX4939_IR_ETH(0)) diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index 71c2c7116802..34fbc2f60a09 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -3269,9 +3269,9 @@ static int cma_netdev_change(struct net_device *ndev, struct rdma_id_private *id } static int cma_netdev_callback(struct notifier_block *self, unsigned long event, - void *ctx) + void *ptr) { - struct net_device *ndev = (struct net_device *)ctx; + struct net_device *ndev = netdev_notifier_info_to_dev(ptr); struct cma_device *cma_dev; struct rdma_id_private *id_priv; int ret = NOTIFY_DONE; diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index 23d734349d8e..a188d3178559 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -1161,7 +1161,7 @@ static void netdev_removed(struct mlx4_ib_dev *dev, int port) static int mlx4_ib_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct mlx4_ib_dev *ibdev; struct net_device *oldnd; struct mlx4_ib_iboe *iboe; diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 29b846cbfb48..f4489d65bf33 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -3277,7 +3277,7 @@ static int bond_slave_netdev_event(unsigned long event, static int bond_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) { - struct net_device *event_dev = (struct net_device *)ptr; + struct net_device *event_dev = netdev_notifier_info_to_dev(ptr); pr_debug("event_dev: %s, event: %lx\n", event_dev ? event_dev->name : "None", diff --git a/drivers/net/can/led.c b/drivers/net/can/led.c index f27fca65dc4a..a3d99a8fd2d1 100644 --- a/drivers/net/can/led.c +++ b/drivers/net/can/led.c @@ -88,9 +88,9 @@ EXPORT_SYMBOL_GPL(devm_can_led_init); /* NETDEV rename notifier to rename the associated led triggers too */ static int can_led_notifier(struct notifier_block *nb, unsigned long msg, - void *data) + void *ptr) { - struct net_device *netdev = data; + struct net_device *netdev = netdev_notifier_info_to_dev(ptr); struct can_priv *priv = safe_candev_priv(netdev); char name[CAN_LED_NAME_SZ]; diff --git a/drivers/net/ethernet/broadcom/cnic.c b/drivers/net/ethernet/broadcom/cnic.c index 6b0dc131b20e..d78d4cf140ed 100644 --- a/drivers/net/ethernet/broadcom/cnic.c +++ b/drivers/net/ethernet/broadcom/cnic.c @@ -5622,7 +5622,7 @@ static void cnic_rcv_netevent(struct cnic_local *cp, unsigned long event, static int cnic_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) { - struct net_device *netdev = ptr; + struct net_device *netdev = netdev_notifier_info_to_dev(ptr); struct cnic_dev *dev; int new_dev = 0; diff --git a/drivers/net/ethernet/marvell/skge.c b/drivers/net/ethernet/marvell/skge.c index 171f4b3dda07..c896079728e1 100644 --- a/drivers/net/ethernet/marvell/skge.c +++ b/drivers/net/ethernet/marvell/skge.c @@ -3706,7 +3706,7 @@ static const struct file_operations skge_debug_fops = { static int skge_device_event(struct notifier_block *unused, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct skge_port *skge; struct dentry *d; diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c index d175bbd3ffd3..e09a8c6f8536 100644 --- a/drivers/net/ethernet/marvell/sky2.c +++ b/drivers/net/ethernet/marvell/sky2.c @@ -4642,7 +4642,7 @@ static const struct file_operations sky2_debug_fops = { static int sky2_device_event(struct notifier_block *unused, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct sky2_port *sky2 = netdev_priv(dev); if (dev->netdev_ops->ndo_open != sky2_open || !sky2_debug) diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c index af951f343ff6..51e13d92761e 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c @@ -3311,7 +3311,7 @@ static int netxen_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) { struct netxen_adapter *adapter; - struct net_device *dev = (struct net_device *)ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct net_device *orig_dev = dev; struct net_device *slave; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index da82f2eb73b4..6bb56d43614b 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -3530,7 +3530,7 @@ static int qlcnic_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) { struct qlcnic_adapter *adapter; - struct net_device *dev = (struct net_device *)ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); recheck: if (dev == NULL) diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c index 39e4cb39de29..46cc11d5e205 100644 --- a/drivers/net/ethernet/sfc/efx.c +++ b/drivers/net/ethernet/sfc/efx.c @@ -2120,7 +2120,7 @@ static void efx_update_name(struct efx_nic *efx) static int efx_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) { - struct net_device *net_dev = ptr; + struct net_device *net_dev = netdev_notifier_info_to_dev(ptr); if (net_dev->netdev_ops == &efx_netdev_ops && event == NETDEV_CHANGENAME) diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c index 02de6c891670..f91bf0ddf031 100644 --- a/drivers/net/hamradio/bpqether.c +++ b/drivers/net/hamradio/bpqether.c @@ -103,7 +103,7 @@ static struct packet_type bpq_packet_type __read_mostly = { }; static struct notifier_block bpq_dev_notifier = { - .notifier_call =bpq_device_event, + .notifier_call = bpq_device_event, }; @@ -544,9 +544,10 @@ static void bpq_free_device(struct net_device *ndev) /* * Handle device status changes. */ -static int bpq_device_event(struct notifier_block *this,unsigned long event, void *ptr) +static int bpq_device_event(struct notifier_block *this, + unsigned long event, void *ptr) { - struct net_device *dev = (struct net_device *)ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); if (!net_eq(dev_net(dev), &init_net)) return NOTIFY_DONE; diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 1c502bb0c916..edfddc5f61b4 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -921,7 +921,7 @@ static struct rtnl_link_ops macvlan_link_ops = { static int macvlan_device_event(struct notifier_block *unused, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct macvlan_dev *vlan, *next; struct macvlan_port *port; LIST_HEAD(list_kill); diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index 59e9605de316..68efb91a5633 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -1053,7 +1053,7 @@ EXPORT_SYMBOL_GPL(macvtap_get_socket); static int macvtap_device_event(struct notifier_block *unused, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct macvlan_dev *vlan; struct device *classdev; dev_t devt; diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index 59ac143dec25..1d1d0a12765c 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -653,12 +653,11 @@ static struct configfs_subsystem netconsole_subsys = { /* Handle network interface device notifications */ static int netconsole_netdev_event(struct notifier_block *this, - unsigned long event, - void *ptr) + unsigned long event, void *ptr) { unsigned long flags; struct netconsole_target *nt; - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); bool stopped = false; if (!(event == NETDEV_CHANGENAME || event == NETDEV_UNREGISTER || diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c index bb07ba94c3aa..5f66e30d9823 100644 --- a/drivers/net/ppp/pppoe.c +++ b/drivers/net/ppp/pppoe.c @@ -338,7 +338,7 @@ static void pppoe_flush_dev(struct net_device *dev) static int pppoe_device_event(struct notifier_block *this, unsigned long event, void *ptr) { - struct net_device *dev = (struct net_device *)ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); /* Only look at sockets that are using this specific device. */ switch (event) { diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 7c43261975bd..9273f48a512b 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -2647,7 +2647,7 @@ static void team_port_change_check(struct team_port *port, bool linkup) static int team_device_event(struct notifier_block *unused, unsigned long event, void *ptr) { - struct net_device *dev = (struct net_device *) ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct team_port *port; port = team_port_get_rtnl(dev); diff --git a/drivers/net/wan/dlci.c b/drivers/net/wan/dlci.c index 147614ed86aa..70ac59929f80 100644 --- a/drivers/net/wan/dlci.c +++ b/drivers/net/wan/dlci.c @@ -477,7 +477,7 @@ static void dlci_setup(struct net_device *dev) static int dlci_dev_event(struct notifier_block *unused, unsigned long event, void *ptr) { - struct net_device *dev = (struct net_device *) ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); if (dev_net(dev) != &init_net) return NOTIFY_DONE; diff --git a/drivers/net/wan/hdlc.c b/drivers/net/wan/hdlc.c index a0a932c63d0a..9c33ca918e19 100644 --- a/drivers/net/wan/hdlc.c +++ b/drivers/net/wan/hdlc.c @@ -99,7 +99,7 @@ static inline void hdlc_proto_stop(struct net_device *dev) static int hdlc_device_event(struct notifier_block *this, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); hdlc_device *hdlc; unsigned long flags; int on; diff --git a/drivers/net/wan/lapbether.c b/drivers/net/wan/lapbether.c index a73b49eb87e3..a33a46fa88dd 100644 --- a/drivers/net/wan/lapbether.c +++ b/drivers/net/wan/lapbether.c @@ -370,7 +370,7 @@ static int lapbeth_device_event(struct notifier_block *this, unsigned long event, void *ptr) { struct lapbethdev *lapbeth; - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); if (dev_net(dev) != &init_net) return NOTIFY_DONE; diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 292b24f9bf93..ee721b6cbcdf 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -1975,7 +1975,7 @@ static int fcoe_device_notification(struct notifier_block *notifier, { struct fcoe_ctlr_device *cdev; struct fc_lport *lport = NULL; - struct net_device *netdev = ptr; + struct net_device *netdev = netdev_notifier_info_to_dev(ptr); struct fcoe_ctlr *ctlr; struct fcoe_interface *fcoe; struct fcoe_port *port; diff --git a/drivers/scsi/fcoe/fcoe_transport.c b/drivers/scsi/fcoe/fcoe_transport.c index f3a5a53e8631..01adbe0ec53b 100644 --- a/drivers/scsi/fcoe/fcoe_transport.c +++ b/drivers/scsi/fcoe/fcoe_transport.c @@ -704,7 +704,7 @@ static struct net_device *fcoe_if_to_netdev(const char *buffer) static int libfcoe_device_notification(struct notifier_block *notifier, ulong event, void *ptr) { - struct net_device *netdev = ptr; + struct net_device *netdev = netdev_notifier_info_to_dev(ptr); switch (event) { case NETDEV_UNREGISTER: diff --git a/drivers/staging/csr/netdev.c b/drivers/staging/csr/netdev.c index a0177d998978..d49cdf84a496 100644 --- a/drivers/staging/csr/netdev.c +++ b/drivers/staging/csr/netdev.c @@ -2891,7 +2891,7 @@ void uf_net_get_name(struct net_device *dev, char *name, int len) */ static int uf_netdev_event(struct notifier_block *notif, unsigned long event, void* ptr) { - struct net_device *netdev = ptr; + struct net_device *netdev = netdev_notifier_info_to_dev(ptr); netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(netdev); unifi_priv_t *priv = NULL; static const CsrWifiMacAddress broadcast_address = {{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}; diff --git a/drivers/staging/ft1000/ft1000-pcmcia/ft1000_proc.c b/drivers/staging/ft1000/ft1000-pcmcia/ft1000_proc.c index 94e426e4d98b..b2330f1df7e7 100644 --- a/drivers/staging/ft1000/ft1000-pcmcia/ft1000_proc.c +++ b/drivers/staging/ft1000/ft1000-pcmcia/ft1000_proc.c @@ -164,7 +164,7 @@ static const struct file_operations ft1000_proc_fops = { static int ft1000NotifyProc(struct notifier_block *this, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct ft1000_info *info; info = netdev_priv(dev); diff --git a/drivers/staging/ft1000/ft1000-usb/ft1000_proc.c b/drivers/staging/ft1000/ft1000-usb/ft1000_proc.c index eca6f0292b4b..5ead942be680 100644 --- a/drivers/staging/ft1000/ft1000-usb/ft1000_proc.c +++ b/drivers/staging/ft1000/ft1000-usb/ft1000_proc.c @@ -166,7 +166,7 @@ static const struct file_operations ft1000_proc_fops = { static int ft1000NotifyProc(struct notifier_block *this, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct ft1000_info *info; struct proc_dir_entry *ft1000_proc_file; diff --git a/drivers/staging/silicom/bpctl_mod.c b/drivers/staging/silicom/bpctl_mod.c index b7e570ccb759..c8ddb99e8526 100644 --- a/drivers/staging/silicom/bpctl_mod.c +++ b/drivers/staging/silicom/bpctl_mod.c @@ -133,7 +133,7 @@ static unsigned long str_to_hex(char *p); static int bp_device_event(struct notifier_block *unused, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); static bpctl_dev_t *pbpctl_dev = NULL, *pbpctl_dev_m = NULL; int dev_num = 0, ret = 0, ret_d = 0, time_left = 0; /* printk("BP_PROC_SUPPORT event =%d %s %d\n", event,dev->name, dev->ifindex ); */ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 6b2bb460d1d7..13a34848b5e1 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1599,6 +1599,19 @@ struct packet_offload { extern int register_netdevice_notifier(struct notifier_block *nb); extern int unregister_netdevice_notifier(struct notifier_block *nb); + +struct netdev_notifier_info { + struct net_device *dev; +}; + +static inline struct net_device * +netdev_notifier_info_to_dev(const struct netdev_notifier_info *info) +{ + return info->dev; +} + +extern int call_netdevice_notifiers_info(unsigned long val, struct net_device *dev, + struct netdev_notifier_info *info); extern int call_netdevice_notifiers(unsigned long val, struct net_device *dev); diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index 9424f3718ea7..2fb2d88e8c2e 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -341,7 +341,7 @@ static void __vlan_device_event(struct net_device *dev, unsigned long event) static int vlan_device_event(struct notifier_block *unused, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct vlan_group *grp; struct vlan_info *vlan_info; int i, flgs; diff --git a/net/appletalk/aarp.c b/net/appletalk/aarp.c index 173a2e82f486..690356fa52b9 100644 --- a/net/appletalk/aarp.c +++ b/net/appletalk/aarp.c @@ -332,7 +332,7 @@ static void aarp_expire_timeout(unsigned long unused) static int aarp_device_event(struct notifier_block *this, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); int ct; if (!net_eq(dev_net(dev), &init_net)) diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c index ef12839a7cfe..7fee50d637f9 100644 --- a/net/appletalk/ddp.c +++ b/net/appletalk/ddp.c @@ -644,7 +644,7 @@ static inline void atalk_dev_down(struct net_device *dev) static int ddp_device_event(struct notifier_block *this, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); if (!net_eq(dev_net(dev), &init_net)) return NOTIFY_DONE; diff --git a/net/atm/clip.c b/net/atm/clip.c index 8ae3a7879335..cce241eb01d9 100644 --- a/net/atm/clip.c +++ b/net/atm/clip.c @@ -539,9 +539,9 @@ static int clip_create(int number) } static int clip_device_event(struct notifier_block *this, unsigned long event, - void *arg) + void *ptr) { - struct net_device *dev = arg; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); if (!net_eq(dev_net(dev), &init_net)) return NOTIFY_DONE; diff --git a/net/atm/mpc.c b/net/atm/mpc.c index d4cc1be5c364..3af12755cd04 100644 --- a/net/atm/mpc.c +++ b/net/atm/mpc.c @@ -998,14 +998,12 @@ int msg_to_mpoad(struct k_message *mesg, struct mpoa_client *mpc) } static int mpoa_event_listener(struct notifier_block *mpoa_notifier, - unsigned long event, void *dev_ptr) + unsigned long event, void *ptr) { - struct net_device *dev; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct mpoa_client *mpc; struct lec_priv *priv; - dev = dev_ptr; - if (!net_eq(dev_net(dev), &init_net)) return NOTIFY_DONE; diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c index e277e38f736b..4b4d2b779ec1 100644 --- a/net/ax25/af_ax25.c +++ b/net/ax25/af_ax25.c @@ -111,9 +111,9 @@ again: * Handle device status changes. */ static int ax25_device_event(struct notifier_block *this, unsigned long event, - void *ptr) + void *ptr) { - struct net_device *dev = (struct net_device *)ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); if (!net_eq(dev_net(dev), &init_net)) return NOTIFY_DONE; @@ -1974,7 +1974,7 @@ static struct packet_type ax25_packet_type __read_mostly = { }; static struct notifier_block ax25_dev_notifier = { - .notifier_call =ax25_device_event, + .notifier_call = ax25_device_event, }; static int __init ax25_init(void) diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index 522243aff2f3..b6504eac0ed8 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -595,7 +595,7 @@ void batadv_hardif_remove_interfaces(void) static int batadv_hard_if_event(struct notifier_block *this, unsigned long event, void *ptr) { - struct net_device *net_dev = ptr; + struct net_device *net_dev = netdev_notifier_info_to_dev(ptr); struct batadv_hard_iface *hard_iface; struct batadv_hard_iface *primary_if = NULL; struct batadv_priv *bat_priv; diff --git a/net/bridge/br_notify.c b/net/bridge/br_notify.c index 1644b3e1f947..3a3f371b2841 100644 --- a/net/bridge/br_notify.c +++ b/net/bridge/br_notify.c @@ -31,7 +31,7 @@ struct notifier_block br_device_notifier = { */ static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct net_bridge_port *p; struct net_bridge *br; bool changed_addr; diff --git a/net/caif/caif_dev.c b/net/caif/caif_dev.c index 1f9ece1a9c34..4dca159435cf 100644 --- a/net/caif/caif_dev.c +++ b/net/caif/caif_dev.c @@ -352,9 +352,9 @@ EXPORT_SYMBOL(caif_enroll_dev); /* notify Caif of device events */ static int caif_device_notify(struct notifier_block *me, unsigned long what, - void *arg) + void *ptr) { - struct net_device *dev = arg; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct caif_device_entry *caifd = NULL; struct caif_dev_common *caifdev; struct cfcnfg *cfg; diff --git a/net/caif/caif_usb.c b/net/caif/caif_usb.c index 942e00a425fd..75ed04b78fa4 100644 --- a/net/caif/caif_usb.c +++ b/net/caif/caif_usb.c @@ -121,9 +121,9 @@ static struct packet_type caif_usb_type __read_mostly = { }; static int cfusbl_device_notify(struct notifier_block *me, unsigned long what, - void *arg) + void *ptr) { - struct net_device *dev = arg; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct caif_dev_common common; struct cflayer *layer, *link_support; struct usbnet *usbnet; diff --git a/net/can/af_can.c b/net/can/af_can.c index c4e50852c9f4..3ab8dd2e1282 100644 --- a/net/can/af_can.c +++ b/net/can/af_can.c @@ -794,9 +794,9 @@ EXPORT_SYMBOL(can_proto_unregister); * af_can notifier to create/remove CAN netdevice specific structs */ static int can_notifier(struct notifier_block *nb, unsigned long msg, - void *data) + void *ptr) { - struct net_device *dev = (struct net_device *)data; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct dev_rcv_lists *d; if (!net_eq(dev_net(dev), &init_net)) diff --git a/net/can/bcm.c b/net/can/bcm.c index 8f113e6ff327..46f20bfafc0e 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -1350,9 +1350,9 @@ static int bcm_sendmsg(struct kiocb *iocb, struct socket *sock, * notification handler for netdevice status changes */ static int bcm_notifier(struct notifier_block *nb, unsigned long msg, - void *data) + void *ptr) { - struct net_device *dev = (struct net_device *)data; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct bcm_sock *bo = container_of(nb, struct bcm_sock, notifier); struct sock *sk = &bo->sk; struct bcm_op *op; diff --git a/net/can/gw.c b/net/can/gw.c index 3ee690e8c7d3..2f291f961a17 100644 --- a/net/can/gw.c +++ b/net/can/gw.c @@ -445,9 +445,9 @@ static inline void cgw_unregister_filter(struct cgw_job *gwj) } static int cgw_notifier(struct notifier_block *nb, - unsigned long msg, void *data) + unsigned long msg, void *ptr) { - struct net_device *dev = (struct net_device *)data; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); if (!net_eq(dev_net(dev), &init_net)) return NOTIFY_DONE; diff --git a/net/can/raw.c b/net/can/raw.c index 1085e65f848e..641e1c895123 100644 --- a/net/can/raw.c +++ b/net/can/raw.c @@ -239,9 +239,9 @@ static int raw_enable_allfilters(struct net_device *dev, struct sock *sk) } static int raw_notifier(struct notifier_block *nb, - unsigned long msg, void *data) + unsigned long msg, void *ptr) { - struct net_device *dev = (struct net_device *)data; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct raw_sock *ro = container_of(nb, struct raw_sock, notifier); struct sock *sk = &ro->sk; diff --git a/net/core/dev.c b/net/core/dev.c index 5f747974ac58..54fce6006a83 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1391,6 +1391,20 @@ void dev_disable_lro(struct net_device *dev) } EXPORT_SYMBOL(dev_disable_lro); +static void netdev_notifier_info_init(struct netdev_notifier_info *info, + struct net_device *dev) +{ + info->dev = dev; +} + +static int call_netdevice_notifier(struct notifier_block *nb, unsigned long val, + struct net_device *dev) +{ + struct netdev_notifier_info info; + + netdev_notifier_info_init(&info, dev); + return nb->notifier_call(nb, val, &info); +} static int dev_boot_phase = 1; @@ -1423,7 +1437,7 @@ int register_netdevice_notifier(struct notifier_block *nb) goto unlock; for_each_net(net) { for_each_netdev(net, dev) { - err = nb->notifier_call(nb, NETDEV_REGISTER, dev); + err = call_netdevice_notifier(nb, NETDEV_REGISTER, dev); err = notifier_to_errno(err); if (err) goto rollback; @@ -1431,7 +1445,7 @@ int register_netdevice_notifier(struct notifier_block *nb) if (!(dev->flags & IFF_UP)) continue; - nb->notifier_call(nb, NETDEV_UP, dev); + call_netdevice_notifier(nb, NETDEV_UP, dev); } } @@ -1447,10 +1461,11 @@ rollback: goto outroll; if (dev->flags & IFF_UP) { - nb->notifier_call(nb, NETDEV_GOING_DOWN, dev); - nb->notifier_call(nb, NETDEV_DOWN, dev); + call_netdevice_notifier(nb, NETDEV_GOING_DOWN, + dev); + call_netdevice_notifier(nb, NETDEV_DOWN, dev); } - nb->notifier_call(nb, NETDEV_UNREGISTER, dev); + call_netdevice_notifier(nb, NETDEV_UNREGISTER, dev); } } @@ -1488,10 +1503,11 @@ int unregister_netdevice_notifier(struct notifier_block *nb) for_each_net(net) { for_each_netdev(net, dev) { if (dev->flags & IFF_UP) { - nb->notifier_call(nb, NETDEV_GOING_DOWN, dev); - nb->notifier_call(nb, NETDEV_DOWN, dev); + call_netdevice_notifier(nb, NETDEV_GOING_DOWN, + dev); + call_netdevice_notifier(nb, NETDEV_DOWN, dev); } - nb->notifier_call(nb, NETDEV_UNREGISTER, dev); + call_netdevice_notifier(nb, NETDEV_UNREGISTER, dev); } } unlock: @@ -1500,6 +1516,25 @@ unlock: } EXPORT_SYMBOL(unregister_netdevice_notifier); +/** + * call_netdevice_notifiers_info - call all network notifier blocks + * @val: value passed unmodified to notifier function + * @dev: net_device pointer passed unmodified to notifier function + * @info: notifier information data + * + * Call all network notifier blocks. Parameters and return value + * are as for raw_notifier_call_chain(). + */ + +int call_netdevice_notifiers_info(unsigned long val, struct net_device *dev, + struct netdev_notifier_info *info) +{ + ASSERT_RTNL(); + netdev_notifier_info_init(info, dev); + return raw_notifier_call_chain(&netdev_chain, val, info); +} +EXPORT_SYMBOL(call_netdevice_notifiers_info); + /** * call_netdevice_notifiers - call all network notifier blocks * @val: value passed unmodified to notifier function @@ -1511,8 +1546,9 @@ EXPORT_SYMBOL(unregister_netdevice_notifier); int call_netdevice_notifiers(unsigned long val, struct net_device *dev) { - ASSERT_RTNL(); - return raw_notifier_call_chain(&netdev_chain, val, dev); + struct netdev_notifier_info info; + + return call_netdevice_notifiers_info(val, dev, &info); } EXPORT_SYMBOL(call_netdevice_notifiers); diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c index d23b6682f4e9..5e78d44333b9 100644 --- a/net/core/drop_monitor.c +++ b/net/core/drop_monitor.c @@ -295,9 +295,9 @@ static int net_dm_cmd_trace(struct sk_buff *skb, } static int dropmon_net_event(struct notifier_block *ev_block, - unsigned long event, void *ptr) + unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct dm_hw_stat_delta *new_stat = NULL; struct dm_hw_stat_delta *tmp; diff --git a/net/core/dst.c b/net/core/dst.c index df9cc810ec8e..ca4231ec7347 100644 --- a/net/core/dst.c +++ b/net/core/dst.c @@ -372,7 +372,7 @@ static void dst_ifdown(struct dst_entry *dst, struct net_device *dev, static int dst_dev_event(struct notifier_block *this, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct dst_entry *dst, *last = NULL; switch (event) { diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index d5a9f8ead0d8..21735440c44a 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -705,9 +705,9 @@ static void detach_rules(struct list_head *rules, struct net_device *dev) static int fib_rules_event(struct notifier_block *this, unsigned long event, - void *ptr) + void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct net *net = dev_net(dev); struct fib_rules_ops *ops; diff --git a/net/core/netprio_cgroup.c b/net/core/netprio_cgroup.c index 0777d0aa18c3..e533259dce3c 100644 --- a/net/core/netprio_cgroup.c +++ b/net/core/netprio_cgroup.c @@ -261,7 +261,7 @@ struct cgroup_subsys net_prio_subsys = { static int netprio_device_event(struct notifier_block *unused, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct netprio_map *old; /* diff --git a/net/core/pktgen.c b/net/core/pktgen.c index 11f2704c3810..795498fd4587 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -1921,7 +1921,7 @@ static void pktgen_change_name(const struct pktgen_net *pn, struct net_device *d static int pktgen_device_event(struct notifier_block *unused, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct pktgen_net *pn = net_generic(dev_net(dev), pg_net_id); if (pn->pktgen_exiting) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index a08bd2b7fe3f..49c14451d8ab 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -2667,7 +2667,7 @@ static void rtnetlink_rcv(struct sk_buff *skb) static int rtnetlink_event(struct notifier_block *this, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); switch (event) { case NETDEV_UP: diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c index c21f200eed93..dd4d506ef923 100644 --- a/net/decnet/af_decnet.c +++ b/net/decnet/af_decnet.c @@ -2078,9 +2078,9 @@ out_err: } static int dn_device_event(struct notifier_block *this, unsigned long event, - void *ptr) + void *ptr) { - struct net_device *dev = (struct net_device *)ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); if (!net_eq(dev_net(dev), &init_net)) return NOTIFY_DONE; diff --git a/net/ieee802154/6lowpan.c b/net/ieee802154/6lowpan.c index 55e1fd5b3e56..3b9d5f20bd1c 100644 --- a/net/ieee802154/6lowpan.c +++ b/net/ieee802154/6lowpan.c @@ -1352,10 +1352,9 @@ static inline void lowpan_netlink_fini(void) } static int lowpan_device_event(struct notifier_block *unused, - unsigned long event, - void *ptr) + unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); LIST_HEAD(del_list); struct lowpan_dev_record *entry, *tmp; diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 247ec1951c35..bf574029a183 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -1234,7 +1234,7 @@ out: static int arp_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); switch (event) { case NETDEV_CHANGEADDR: diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index dfc39d4d48b7..b047e2d8a614 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -1333,7 +1333,7 @@ static void inetdev_send_gratuitous_arp(struct net_device *dev, static int inetdev_event(struct notifier_block *this, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct in_device *in_dev = __in_dev_get_rtnl(dev); ASSERT_RTNL(); diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index c7629a209f9d..05a4888dede9 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -1038,7 +1038,7 @@ static int fib_inetaddr_event(struct notifier_block *this, unsigned long event, static int fib_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct in_device *in_dev; struct net *net = dev_net(dev); diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 9d9610ae7855..f975399f3522 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -1609,7 +1609,7 @@ int ipmr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg) static int ipmr_device_event(struct notifier_block *this, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct net *net = dev_net(dev); struct mr_table *mrt; struct vif_device *v; diff --git a/net/ipv4/netfilter/ipt_MASQUERADE.c b/net/ipv4/netfilter/ipt_MASQUERADE.c index 5d5d4d1be9c2..dd5508bde799 100644 --- a/net/ipv4/netfilter/ipt_MASQUERADE.c +++ b/net/ipv4/netfilter/ipt_MASQUERADE.c @@ -108,7 +108,7 @@ static int masq_device_event(struct notifier_block *this, unsigned long event, void *ptr) { - const struct net_device *dev = ptr; + const struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct net *net = dev_net(dev); if (event == NETDEV_DOWN) { diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 432e084b6b62..bce073b4bbd4 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -2826,9 +2826,9 @@ static void addrconf_ip6_tnl_config(struct net_device *dev) } static int addrconf_notify(struct notifier_block *this, unsigned long event, - void *data) + void *ptr) { - struct net_device *dev = (struct net_device *) data; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct inet6_dev *idev = __in6_dev_get(dev); int run_pending = 0; int err; diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index 241fb8ad9fcf..583e8d435f9a 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -1319,7 +1319,7 @@ static int ip6mr_mfc_delete(struct mr6_table *mrt, struct mf6cctl *mfc, static int ip6mr_device_event(struct notifier_block *this, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct net *net = dev_net(dev); struct mr6_table *mrt; struct mif_device *v; diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 2712ab22a174..a0962697a257 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -1568,7 +1568,7 @@ int ndisc_rcv(struct sk_buff *skb) static int ndisc_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct net *net = dev_net(dev); struct inet6_dev *idev; diff --git a/net/ipv6/netfilter/ip6t_MASQUERADE.c b/net/ipv6/netfilter/ip6t_MASQUERADE.c index 60e9053bab05..b76257cd7e1e 100644 --- a/net/ipv6/netfilter/ip6t_MASQUERADE.c +++ b/net/ipv6/netfilter/ip6t_MASQUERADE.c @@ -71,7 +71,7 @@ static int device_cmp(struct nf_conn *ct, void *ifindex) static int masq_device_event(struct notifier_block *this, unsigned long event, void *ptr) { - const struct net_device *dev = ptr; + const struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct net *net = dev_net(dev); if (event == NETDEV_DOWN) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index ad0aa6b0b86a..194c3cde1536 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -2681,9 +2681,9 @@ errout: } static int ip6_route_dev_notify(struct notifier_block *this, - unsigned long event, void *data) + unsigned long event, void *ptr) { - struct net_device *dev = (struct net_device *)data; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct net *net = dev_net(dev); if (event == NETDEV_REGISTER && (dev->flags & IFF_LOOPBACK)) { diff --git a/net/ipx/af_ipx.c b/net/ipx/af_ipx.c index f547a47d381c..7a1e0fc1bd4d 100644 --- a/net/ipx/af_ipx.c +++ b/net/ipx/af_ipx.c @@ -330,7 +330,7 @@ static __inline__ void __ipxitf_put(struct ipx_interface *intrfc) static int ipxitf_device_event(struct notifier_block *notifier, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct ipx_interface *i, *tmp; if (!net_eq(dev_net(dev), &init_net)) diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c index ae691651b721..168aff5e60de 100644 --- a/net/iucv/af_iucv.c +++ b/net/iucv/af_iucv.c @@ -2293,7 +2293,7 @@ out_unlock: static int afiucv_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) { - struct net_device *event_dev = (struct net_device *)ptr; + struct net_device *event_dev = netdev_notifier_info_to_dev(ptr); struct sock *sk; struct iucv_sock *iucv; diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 60f1ce5e5e52..d2c3fd178dbe 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1717,10 +1717,9 @@ void ieee80211_remove_interfaces(struct ieee80211_local *local) } static int netdev_notify(struct notifier_block *nb, - unsigned long state, - void *ndev) + unsigned long state, void *ptr) { - struct net_device *dev = ndev; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct ieee80211_sub_if_data *sdata; if (state != NETDEV_CHANGENAME) diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 5b142fb16480..7c3ed429789e 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -1487,9 +1487,9 @@ ip_vs_forget_dev(struct ip_vs_dest *dest, struct net_device *dev) * Currently only NETDEV_DOWN is handled to release refs to cached dsts */ static int ip_vs_dst_event(struct notifier_block *this, unsigned long event, - void *ptr) + void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct net *net = dev_net(dev); struct netns_ipvs *ipvs = net_ipvs(net); struct ip_vs_service *svc; diff --git a/net/netfilter/nfnetlink_queue_core.c b/net/netfilter/nfnetlink_queue_core.c index 4e27fa035814..0f2ac8f2e7b7 100644 --- a/net/netfilter/nfnetlink_queue_core.c +++ b/net/netfilter/nfnetlink_queue_core.c @@ -800,7 +800,7 @@ static int nfqnl_rcv_dev_event(struct notifier_block *this, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); /* Drop any packets associated with the downed device */ if (event == NETDEV_DOWN) diff --git a/net/netfilter/xt_TEE.c b/net/netfilter/xt_TEE.c index bd93e51d30ac..292934d23482 100644 --- a/net/netfilter/xt_TEE.c +++ b/net/netfilter/xt_TEE.c @@ -200,7 +200,7 @@ tee_tg6(struct sk_buff *skb, const struct xt_action_param *par) static int tee_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct xt_tee_priv *priv; priv = container_of(this, struct xt_tee_priv, notifier); diff --git a/net/netlabel/netlabel_unlabeled.c b/net/netlabel/netlabel_unlabeled.c index 8a6c6ea466d8..af3531926ee0 100644 --- a/net/netlabel/netlabel_unlabeled.c +++ b/net/netlabel/netlabel_unlabeled.c @@ -708,7 +708,7 @@ unlhsh_remove_return: * netlbl_unlhsh_netdev_handler - Network device notification handler * @this: notifier block * @event: the event - * @ptr: the network device (cast to void) + * @ptr: the netdevice notifier info (cast to void) * * Description: * Handle network device events, although at present all we care about is a @@ -717,10 +717,9 @@ unlhsh_remove_return: * */ static int netlbl_unlhsh_netdev_handler(struct notifier_block *this, - unsigned long event, - void *ptr) + unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct netlbl_unlhsh_iface *iface = NULL; if (!net_eq(dev_net(dev), &init_net)) diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c index ec0c80fde69f..698814bfa7ad 100644 --- a/net/netrom/af_netrom.c +++ b/net/netrom/af_netrom.c @@ -117,7 +117,7 @@ static void nr_kill_by_device(struct net_device *dev) */ static int nr_device_event(struct notifier_block *this, unsigned long event, void *ptr) { - struct net_device *dev = (struct net_device *)ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); if (!net_eq(dev_net(dev), &init_net)) return NOTIFY_DONE; diff --git a/net/openvswitch/dp_notify.c b/net/openvswitch/dp_notify.c index ef4feec6cd84..c3235675f359 100644 --- a/net/openvswitch/dp_notify.c +++ b/net/openvswitch/dp_notify.c @@ -78,7 +78,7 @@ static int dp_device_event(struct notifier_block *unused, unsigned long event, void *ptr) { struct ovs_net *ovs_net; - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct vport *vport = NULL; if (!ovs_is_internal_dev(dev)) diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 8ec1bca7f859..79fe63246b27 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -3331,10 +3331,11 @@ static int packet_getsockopt(struct socket *sock, int level, int optname, } -static int packet_notifier(struct notifier_block *this, unsigned long msg, void *data) +static int packet_notifier(struct notifier_block *this, + unsigned long msg, void *ptr) { struct sock *sk; - struct net_device *dev = data; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct net *net = dev_net(dev); rcu_read_lock(); diff --git a/net/phonet/pn_dev.c b/net/phonet/pn_dev.c index 45a7df6575de..56a6146ac94b 100644 --- a/net/phonet/pn_dev.c +++ b/net/phonet/pn_dev.c @@ -292,9 +292,9 @@ static void phonet_route_autodel(struct net_device *dev) /* notify Phonet of device events */ static int phonet_device_notify(struct notifier_block *me, unsigned long what, - void *arg) + void *ptr) { - struct net_device *dev = arg; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); switch (what) { case NETDEV_REGISTER: diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c index 9c8347451597..e98fcfbe6007 100644 --- a/net/rose/af_rose.c +++ b/net/rose/af_rose.c @@ -202,10 +202,10 @@ static void rose_kill_by_device(struct net_device *dev) /* * Handle device status changes. */ -static int rose_device_event(struct notifier_block *this, unsigned long event, - void *ptr) +static int rose_device_event(struct notifier_block *this, + unsigned long event, void *ptr) { - struct net_device *dev = (struct net_device *)ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); if (!net_eq(dev_net(dev), &init_net)) return NOTIFY_DONE; diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c index 5d676edc22a6..977c10e0631b 100644 --- a/net/sched/act_mirred.c +++ b/net/sched/act_mirred.c @@ -243,7 +243,7 @@ nla_put_failure: static int mirred_device_event(struct notifier_block *unused, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct tcf_mirred *m; if (event == NETDEV_UNREGISTER) diff --git a/net/tipc/eth_media.c b/net/tipc/eth_media.c index 120a676a3360..fc60bea63169 100644 --- a/net/tipc/eth_media.c +++ b/net/tipc/eth_media.c @@ -251,9 +251,9 @@ static void disable_bearer(struct tipc_bearer *tb_ptr) * specified device. */ static int recv_notification(struct notifier_block *nb, unsigned long evt, - void *dv) + void *ptr) { - struct net_device *dev = (struct net_device *)dv; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct eth_bearer *eb_ptr = ð_bearers[0]; struct eth_bearer *stop = ð_bearers[MAX_ETH_BEARERS]; diff --git a/net/tipc/ib_media.c b/net/tipc/ib_media.c index 2a2864c25e15..baa9df4327d9 100644 --- a/net/tipc/ib_media.c +++ b/net/tipc/ib_media.c @@ -244,9 +244,9 @@ static void disable_bearer(struct tipc_bearer *tb_ptr) * specified device. */ static int recv_notification(struct notifier_block *nb, unsigned long evt, - void *dv) + void *ptr) { - struct net_device *dev = (struct net_device *)dv; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct ib_bearer *ib_ptr = &ib_bearers[0]; struct ib_bearer *stop = &ib_bearers[MAX_IB_BEARERS]; diff --git a/net/wireless/core.c b/net/wireless/core.c index 73405e00c800..01e41191f1bf 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -886,10 +886,9 @@ void cfg80211_leave(struct cfg80211_registered_device *rdev, } static int cfg80211_netdev_notifier_call(struct notifier_block *nb, - unsigned long state, - void *ndev) + unsigned long state, void *ptr) { - struct net_device *dev = ndev; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev; int ret; diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c index 37ca9694aabe..1d964e23853f 100644 --- a/net/x25/af_x25.c +++ b/net/x25/af_x25.c @@ -224,7 +224,7 @@ static void x25_kill_by_device(struct net_device *dev) static int x25_device_event(struct notifier_block *this, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct x25_neigh *nb; if (!net_eq(dev_net(dev), &init_net)) diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 23cea0f74336..536ccc95de89 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -2784,7 +2784,7 @@ static void __net_init xfrm_dst_ops_init(struct net *net) static int xfrm_dev_event(struct notifier_block *this, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); switch (event) { case NETDEV_DOWN: diff --git a/security/selinux/netif.c b/security/selinux/netif.c index 47a49d1a6f6a..694e9e43855f 100644 --- a/security/selinux/netif.c +++ b/security/selinux/netif.c @@ -264,7 +264,7 @@ static int sel_netif_avc_callback(u32 event) static int sel_netif_netdev_notifier_handler(struct notifier_block *this, unsigned long event, void *ptr) { - struct net_device *dev = ptr; + struct net_device *dev = netdev_notifier_info_to_dev(ptr); if (dev_net(dev) != &init_net) return NOTIFY_DONE; -- cgit v1.2.3 From be9efd3653284f2827fd82861e8e9db9a8f726e1 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 28 May 2013 01:30:22 +0000 Subject: net: pass changed flags along with NETDEV_CHANGE event MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use new netdevice notifier infrastructure to pass along changed flags. Signed-off-by: Timo Teräs Signed-off-by: Jiri Pirko v2->v3: shortened notifier_info struct name Signed-off-by: David S. Miller --- include/linux/netdevice.h | 5 +++++ net/core/dev.c | 9 +++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 13a34848b5e1..850271809a9e 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1604,6 +1604,11 @@ struct netdev_notifier_info { struct net_device *dev; }; +struct netdev_notifier_change_info { + struct netdev_notifier_info info; /* must be first */ + unsigned int flags_changed; +}; + static inline struct net_device * netdev_notifier_info_to_dev(const struct netdev_notifier_info *info) { diff --git a/net/core/dev.c b/net/core/dev.c index 54fce6006a83..6eb621cc3b81 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4771,8 +4771,13 @@ void __dev_notify_flags(struct net_device *dev, unsigned int old_flags) } if (dev->flags & IFF_UP && - (changes & ~(IFF_UP | IFF_PROMISC | IFF_ALLMULTI | IFF_VOLATILE))) - call_netdevice_notifiers(NETDEV_CHANGE, dev); + (changes & ~(IFF_UP | IFF_PROMISC | IFF_ALLMULTI | IFF_VOLATILE))) { + struct netdev_notifier_change_info change_info; + + change_info.flags_changed = changes; + call_netdevice_notifiers_info(NETDEV_CHANGE, dev, + &change_info.info); + } } /** -- cgit v1.2.3 From 6c8b4e3ff81b82fc153625e81e60af1d89de2c32 Mon Sep 17 00:00:00 2001 From: Timo Teräs Date: Tue, 28 May 2013 01:30:23 +0000 Subject: arp: flush arp cache on IFF_NOARP change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit IFF_NOARP affects what kind of neighbor entries are created (nud NOARP or nud INCOMPLETE). If the flag changes, flush the arp cache to refresh all entries. Signed-off-by: Timo Teräs Signed-off-by: Jiri Pirko v2->v3: shortened notifier_info struct name Signed-off-by: David S. Miller --- net/ipv4/arp.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index bf574029a183..4429b013f269 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -1235,12 +1235,18 @@ static int arp_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) { struct net_device *dev = netdev_notifier_info_to_dev(ptr); + struct netdev_notifier_change_info *change_info; switch (event) { case NETDEV_CHANGEADDR: neigh_changeaddr(&arp_tbl, dev); rt_cache_flush(dev_net(dev)); break; + case NETDEV_CHANGE: + change_info = ptr; + if (change_info->flags_changed & IFF_NOARP) + neigh_changeaddr(&arp_tbl, dev); + break; default: break; } -- cgit v1.2.3 From 06ecf24bdf2b7afc6c8fd13de6dba2a96dd331b6 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 28 May 2013 13:15:50 -0700 Subject: net: Fix build warnings after mac_header and transport_header became __u16. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit net/core/skbuff.c: In function ‘__alloc_skb_head’: net/core/skbuff.c:203:2: warning: large integer implicitly truncated to unsigned type [-Woverflow] net/core/skbuff.c: In function ‘__alloc_skb’: net/core/skbuff.c:279:2: warning: large integer implicitly truncated to unsigned type [-Woverflow] net/core/skbuff.c:280:2: warning: large integer implicitly truncated to unsigned type [-Woverflow] net/core/skbuff.c: In function ‘build_skb’: net/core/skbuff.c:348:2: warning: large integer implicitly truncated to unsigned type [-Woverflow] net/core/skbuff.c:349:2: warning: large integer implicitly truncated to unsigned type [-Woverflow] Signed-off-by: David S. Miller --- net/core/skbuff.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index d6298914f4e7..f45de077ab9e 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -200,7 +200,7 @@ struct sk_buff *__alloc_skb_head(gfp_t gfp_mask, int node) atomic_set(&skb->users, 1); #ifdef NET_SKBUFF_DATA_USES_OFFSET - skb->mac_header = ~0U; + skb->mac_header = (__u16) ~0U; #endif out: return skb; @@ -276,8 +276,8 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask, skb_reset_tail_pointer(skb); skb->end = skb->tail + size; #ifdef NET_SKBUFF_DATA_USES_OFFSET - skb->mac_header = ~0U; - skb->transport_header = ~0U; + skb->mac_header = (__u16) ~0U; + skb->transport_header = (__u16) ~0U; #endif /* make sure we initialize shinfo sequentially */ @@ -345,8 +345,8 @@ struct sk_buff *build_skb(void *data, unsigned int frag_size) skb_reset_tail_pointer(skb); skb->end = skb->tail + size; #ifdef NET_SKBUFF_DATA_USES_OFFSET - skb->mac_header = ~0U; - skb->transport_header = ~0U; + skb->mac_header = (__u16) ~0U; + skb->transport_header = (__u16) ~0U; #endif /* make sure we initialize shinfo sequentially */ -- cgit v1.2.3 From a44b8bd6072ac82fc49af941de8c1ac85d94852d Mon Sep 17 00:00:00 2001 From: Liu Ying Date: Tue, 7 May 2013 16:38:40 +0800 Subject: ktime: Use macro NSEC_PER_USEC where appropriate We've got the macro NSEC_PER_USEC defined in header file include/linux/time.h. To make the code decent, this patch replaces the immediate number 1000 to convert bewteen a time value in microseconds and one in nanoseconds with the macro NSEC_PER_USEC. Signed-off-by: Liu Ying Cc: David S. Miller Cc: Daniel Borkmann Cc: Thomas Gleixner Cc: John Stultz Signed-off-by: John Stultz --- include/linux/ktime.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/include/linux/ktime.h b/include/linux/ktime.h index bbca12804d12..608728a164a9 100644 --- a/include/linux/ktime.h +++ b/include/linux/ktime.h @@ -229,7 +229,8 @@ static inline ktime_t timespec_to_ktime(const struct timespec ts) static inline ktime_t timeval_to_ktime(const struct timeval tv) { return (ktime_t) { .tv = { .sec = (s32)tv.tv_sec, - .nsec = (s32)tv.tv_usec * 1000 } }; + .nsec = (s32)(tv.tv_usec * + NSEC_PER_USEC) } }; } /** @@ -320,12 +321,12 @@ static inline s64 ktime_us_delta(const ktime_t later, const ktime_t earlier) static inline ktime_t ktime_add_us(const ktime_t kt, const u64 usec) { - return ktime_add_ns(kt, usec * 1000); + return ktime_add_ns(kt, usec * NSEC_PER_USEC); } static inline ktime_t ktime_sub_us(const ktime_t kt, const u64 usec) { - return ktime_sub_ns(kt, usec * 1000); + return ktime_sub_ns(kt, usec * NSEC_PER_USEC); } extern ktime_t ktime_add_safe(const ktime_t lhs, const ktime_t rhs); -- cgit v1.2.3 From 35b210855069ae34450eeaafc0f1b5bfa4f71c87 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Thu, 16 May 2013 15:47:49 +0200 Subject: ktime: Add __must_check prefix to ktime_to_timespec_cond The function is currently mainly used in the networking code and if others start using it, they must check the result, otherwise it cannot be determined if the timespec conversion suceeded. Currently no user lacks this check, but make future users aware of a possible misusage. Signed-off-by: Daniel Borkmann Signed-off-by: John Stultz --- include/linux/ktime.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/linux/ktime.h b/include/linux/ktime.h index 608728a164a9..fc66b301b648 100644 --- a/include/linux/ktime.h +++ b/include/linux/ktime.h @@ -339,7 +339,8 @@ extern ktime_t ktime_add_safe(const ktime_t lhs, const ktime_t rhs); * * Returns true if there was a successful conversion, false if kt was 0. */ -static inline bool ktime_to_timespec_cond(const ktime_t kt, struct timespec *ts) +static inline __must_check bool ktime_to_timespec_cond(const ktime_t kt, + struct timespec *ts) { if (kt.tv64) { *ts = ktime_to_timespec(kt); -- cgit v1.2.3 From 55a68c23e0a675b2b8ac2656fd6edbf98b78e4c6 Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Tue, 7 May 2013 22:11:26 +0200 Subject: dw_apb_timer_of.c: Remove parts that were picoxcell-specific It seems we made a mistake when creating dw_apb_timer_of.c: picoxcell sched_clock had parts that were not related to dw_apb_timer, yet we moved them to dw_apb_timer_of, and tried to use them on socfpga. This results in system where user/system time is not measured properly, as demonstrated by time dd if=/dev/urandom of=/dev/zero bs=100000 count=100 So this patch switches sched_clock to hardware that exists on both platforms, and adds missing of_node_put() in dw_apb_timer_init(). Signed-off-by: Pavel Machek Acked-by: Jamie Iles Signed-off-by: John Stultz --- arch/arm/mach-picoxcell/common.h | 2 -- drivers/clocksource/dw_apb_timer.c | 6 ---- drivers/clocksource/dw_apb_timer_of.c | 52 ++++++++++++++++------------------- include/linux/dw_apb_timer.h | 6 ++++ 4 files changed, 29 insertions(+), 37 deletions(-) diff --git a/arch/arm/mach-picoxcell/common.h b/arch/arm/mach-picoxcell/common.h index 481b42a4ef15..237fb3bcbd04 100644 --- a/arch/arm/mach-picoxcell/common.h +++ b/arch/arm/mach-picoxcell/common.h @@ -12,6 +12,4 @@ #include -extern void dw_apb_timer_init(void); - #endif /* __PICOXCELL_COMMON_H__ */ diff --git a/drivers/clocksource/dw_apb_timer.c b/drivers/clocksource/dw_apb_timer.c index e54ca1062d8e..e7042bc5c7d2 100644 --- a/drivers/clocksource/dw_apb_timer.c +++ b/drivers/clocksource/dw_apb_timer.c @@ -21,12 +21,6 @@ #define APBT_MIN_PERIOD 4 #define APBT_MIN_DELTA_USEC 200 -#define APBTMR_N_LOAD_COUNT 0x00 -#define APBTMR_N_CURRENT_VALUE 0x04 -#define APBTMR_N_CONTROL 0x08 -#define APBTMR_N_EOI 0x0c -#define APBTMR_N_INT_STATUS 0x10 - #define APBTMRS_INT_STATUS 0xa0 #define APBTMRS_EOI 0xa4 #define APBTMRS_RAW_INT_STATUS 0xa8 diff --git a/drivers/clocksource/dw_apb_timer_of.c b/drivers/clocksource/dw_apb_timer_of.c index ab09ed3742ee..44a3b9163c46 100644 --- a/drivers/clocksource/dw_apb_timer_of.c +++ b/drivers/clocksource/dw_apb_timer_of.c @@ -57,6 +57,15 @@ static void add_clockevent(struct device_node *event_timer) dw_apb_clockevent_register(ced); } +static void __iomem *sched_io_base; + +/* This is actually same as __apbt_read_clocksource(), but with + different interface */ +static u32 read_sched_clock_sptimer(void) +{ + return ~__raw_readl(sched_io_base + APBTMR_N_CURRENT_VALUE); +} + static void add_clocksource(struct device_node *source_timer) { void __iomem *iobase; @@ -71,41 +80,27 @@ static void add_clocksource(struct device_node *source_timer) dw_apb_clocksource_start(cs); dw_apb_clocksource_register(cs); -} -static void __iomem *sched_io_base; - -static u32 read_sched_clock(void) -{ - return __raw_readl(sched_io_base); + sched_io_base = iobase; + setup_sched_clock(read_sched_clock_sptimer, 32, rate); } -static const struct of_device_id sptimer_ids[] __initconst = { - { .compatible = "picochip,pc3x2-rtc" }, +static const struct of_device_id osctimer_ids[] __initconst = { + { .compatible = "picochip,pc3x2-timer" }, + { .compatible = "snps,dw-apb-timer-osc" }, { .compatible = "snps,dw-apb-timer-sp" }, - { /* Sentinel */ }, + { /* Sentinel */ }, }; -static void init_sched_clock(void) -{ - struct device_node *sched_timer; - u32 rate; - - sched_timer = of_find_matching_node(NULL, sptimer_ids); - if (!sched_timer) - panic("No RTC for sched clock to use"); +/* + You don't have to use dw_apb_timer for scheduler clock, + this should also work fine on arm: - timer_get_base_and_rate(sched_timer, &sched_io_base, &rate); - of_node_put(sched_timer); + twd_local_timer_of_register(); + arch_timer_of_register(); + arch_timer_sched_clock_init(); +*/ - setup_sched_clock(read_sched_clock, 32, rate); -} - -static const struct of_device_id osctimer_ids[] __initconst = { - { .compatible = "picochip,pc3x2-timer" }, - { .compatible = "snps,dw-apb-timer-osc" }, - {}, -}; void __init dw_apb_timer_init(void) { @@ -121,7 +116,6 @@ void __init dw_apb_timer_init(void) panic("No timer for clocksource"); add_clocksource(source_timer); + of_node_put(event_timer); of_node_put(source_timer); - - init_sched_clock(); } diff --git a/include/linux/dw_apb_timer.h b/include/linux/dw_apb_timer.h index b1cd9597c241..de0904e38f33 100644 --- a/include/linux/dw_apb_timer.h +++ b/include/linux/dw_apb_timer.h @@ -17,6 +17,12 @@ #include #include +#define APBTMR_N_LOAD_COUNT 0x00 +#define APBTMR_N_CURRENT_VALUE 0x04 +#define APBTMR_N_CONTROL 0x08 +#define APBTMR_N_EOI 0x0c +#define APBTMR_N_INT_STATUS 0x10 + #define APBTMRS_REG_SIZE 0x14 struct dw_apb_timer { -- cgit v1.2.3 From 0a0a7e66fa269de78975ea8d4e825a66d92b8d70 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Sun, 26 May 2013 15:12:46 +0300 Subject: clocksource: dw_apb: Remove unused header The time.h header seems not to be used by current code. Removing this include allows the driver to build on other architecture that do not have this header. Cc: Ingo Molnar Cc: Peter Zijlstra Cc: John Stultz Cc: Thomas Gleixner Cc: Jamie Iles Cc: Dinh Nguyen Acked-by: Jamie Iles Signed-off-by: Baruch Siach [tweaked commit message and header] Signed-off-by: John Stultz --- drivers/clocksource/dw_apb_timer_of.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/clocksource/dw_apb_timer_of.c b/drivers/clocksource/dw_apb_timer_of.c index 44a3b9163c46..8d2f809a0967 100644 --- a/drivers/clocksource/dw_apb_timer_of.c +++ b/drivers/clocksource/dw_apb_timer_of.c @@ -21,7 +21,6 @@ #include #include -#include #include static void timer_get_base_and_rate(struct device_node *np, -- cgit v1.2.3 From 3565184ed0c1ea46bea5b792da5f72a83c43e49b Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Mon, 13 May 2013 18:56:06 +0100 Subject: x86: Increase precision of x86_platform.get/set_wallclock() All the virtualized platforms (KVM, lguest and Xen) have persistent wallclocks that have more than one second of precision. read_persistent_wallclock() and update_persistent_wallclock() allow for nanosecond precision but their implementation on x86 with x86_platform.get/set_wallclock() only allows for one second precision. This means guests may see a wallclock time that is off by up to 1 second. Make set_wallclock() and get_wallclock() take a struct timespec parameter (which allows for nanosecond precision) so KVM and Xen guests may start with a more accurate wallclock time and a Xen dom0 can maintain a more accurate wallclock for guests. Signed-off-by: David Vrabel Signed-off-by: John Stultz --- arch/x86/include/asm/mc146818rtc.h | 4 ++-- arch/x86/include/asm/x86_init.h | 6 ++++-- arch/x86/kernel/kvmclock.c | 9 +++------ arch/x86/kernel/rtc.c | 17 +++++++---------- arch/x86/lguest/boot.c | 4 ++-- arch/x86/platform/efi/efi.c | 10 ++++++---- arch/x86/xen/time.c | 19 ++++++------------- include/linux/efi.h | 4 ++-- 8 files changed, 32 insertions(+), 41 deletions(-) diff --git a/arch/x86/include/asm/mc146818rtc.h b/arch/x86/include/asm/mc146818rtc.h index d354fb781c57..a55c7efcc4ed 100644 --- a/arch/x86/include/asm/mc146818rtc.h +++ b/arch/x86/include/asm/mc146818rtc.h @@ -95,8 +95,8 @@ static inline unsigned char current_lock_cmos_reg(void) unsigned char rtc_cmos_read(unsigned char addr); void rtc_cmos_write(unsigned char val, unsigned char addr); -extern int mach_set_rtc_mmss(unsigned long nowtime); -extern unsigned long mach_get_cmos_time(void); +extern int mach_set_rtc_mmss(const struct timespec *now); +extern void mach_get_cmos_time(struct timespec *now); #define RTC_IRQ 8 diff --git a/arch/x86/include/asm/x86_init.h b/arch/x86/include/asm/x86_init.h index d8d99222b36a..828a1565ba57 100644 --- a/arch/x86/include/asm/x86_init.h +++ b/arch/x86/include/asm/x86_init.h @@ -142,6 +142,8 @@ struct x86_cpuinit_ops { void (*fixup_cpu_id)(struct cpuinfo_x86 *c, int node); }; +struct timespec; + /** * struct x86_platform_ops - platform specific runtime functions * @calibrate_tsc: calibrate TSC @@ -156,8 +158,8 @@ struct x86_cpuinit_ops { */ struct x86_platform_ops { unsigned long (*calibrate_tsc)(void); - unsigned long (*get_wallclock)(void); - int (*set_wallclock)(unsigned long nowtime); + void (*get_wallclock)(struct timespec *ts); + int (*set_wallclock)(const struct timespec *ts); void (*iommu_shutdown)(void); bool (*is_untracked_pat_range)(u64 start, u64 end); void (*nmi_init)(void); diff --git a/arch/x86/kernel/kvmclock.c b/arch/x86/kernel/kvmclock.c index d2c381280e3c..0db81ab511cc 100644 --- a/arch/x86/kernel/kvmclock.c +++ b/arch/x86/kernel/kvmclock.c @@ -48,10 +48,9 @@ static struct pvclock_wall_clock wall_clock; * have elapsed since the hypervisor wrote the data. So we try to account for * that with system time */ -static unsigned long kvm_get_wallclock(void) +static void kvm_get_wallclock(struct timespec *now) { struct pvclock_vcpu_time_info *vcpu_time; - struct timespec ts; int low, high; int cpu; @@ -64,14 +63,12 @@ static unsigned long kvm_get_wallclock(void) cpu = smp_processor_id(); vcpu_time = &hv_clock[cpu].pvti; - pvclock_read_wallclock(&wall_clock, vcpu_time, &ts); + pvclock_read_wallclock(&wall_clock, vcpu_time, now); preempt_enable(); - - return ts.tv_sec; } -static int kvm_set_wallclock(unsigned long now) +static int kvm_set_wallclock(const struct timespec *now) { return -1; } diff --git a/arch/x86/kernel/rtc.c b/arch/x86/kernel/rtc.c index 198eb201ed3b..0aa29394ed6f 100644 --- a/arch/x86/kernel/rtc.c +++ b/arch/x86/kernel/rtc.c @@ -38,8 +38,9 @@ EXPORT_SYMBOL(rtc_lock); * jump to the next second precisely 500 ms later. Check the Motorola * MC146818A or Dallas DS12887 data sheet for details. */ -int mach_set_rtc_mmss(unsigned long nowtime) +int mach_set_rtc_mmss(const struct timespec *now) { + unsigned long nowtime = now->tv_sec; struct rtc_time tm; int retval = 0; @@ -58,7 +59,7 @@ int mach_set_rtc_mmss(unsigned long nowtime) return retval; } -unsigned long mach_get_cmos_time(void) +void mach_get_cmos_time(struct timespec *now) { unsigned int status, year, mon, day, hour, min, sec, century = 0; unsigned long flags; @@ -107,7 +108,8 @@ unsigned long mach_get_cmos_time(void) } else year += CMOS_YEARS_OFFS; - return mktime(year, mon, day, hour, min, sec); + now->tv_sec = mktime(year, mon, day, hour, min, sec); + now->tv_nsec = 0; } /* Routines for accessing the CMOS RAM/RTC. */ @@ -135,18 +137,13 @@ EXPORT_SYMBOL(rtc_cmos_write); int update_persistent_clock(struct timespec now) { - return x86_platform.set_wallclock(now.tv_sec); + return x86_platform.set_wallclock(&now); } /* not static: needed by APM */ void read_persistent_clock(struct timespec *ts) { - unsigned long retval; - - retval = x86_platform.get_wallclock(); - - ts->tv_sec = retval; - ts->tv_nsec = 0; + x86_platform.get_wallclock(ts); } diff --git a/arch/x86/lguest/boot.c b/arch/x86/lguest/boot.c index 7114c63f047d..8424d5adcfa2 100644 --- a/arch/x86/lguest/boot.c +++ b/arch/x86/lguest/boot.c @@ -882,9 +882,9 @@ int lguest_setup_irq(unsigned int irq) * It would be far better for everyone if the Guest had its own clock, but * until then the Host gives us the time on every interrupt. */ -static unsigned long lguest_get_wallclock(void) +static void lguest_get_wallclock(struct timespec *now) { - return lguest_data.time.tv_sec; + *now = lguest_data.time; } /* diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 55856b2310d3..dd3b82530145 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -352,8 +352,9 @@ static efi_status_t __init phys_efi_get_time(efi_time_t *tm, return status; } -int efi_set_rtc_mmss(unsigned long nowtime) +int efi_set_rtc_mmss(const struct timespec *now) { + unsigned long nowtime = now->tv_sec; efi_status_t status; efi_time_t eft; efi_time_cap_t cap; @@ -388,7 +389,7 @@ int efi_set_rtc_mmss(unsigned long nowtime) return 0; } -unsigned long efi_get_time(void) +void efi_get_time(struct timespec *now) { efi_status_t status; efi_time_t eft; @@ -398,8 +399,9 @@ unsigned long efi_get_time(void) if (status != EFI_SUCCESS) pr_err("Oops: efitime: can't read time!\n"); - return mktime(eft.year, eft.month, eft.day, eft.hour, - eft.minute, eft.second); + now->tv_sec = mktime(eft.year, eft.month, eft.day, eft.hour, + eft.minute, eft.second); + now->tv_nsec = 0; } /* diff --git a/arch/x86/xen/time.c b/arch/x86/xen/time.c index 3d88bfdf9e1c..a1947ac2da82 100644 --- a/arch/x86/xen/time.c +++ b/arch/x86/xen/time.c @@ -191,32 +191,25 @@ static void xen_read_wallclock(struct timespec *ts) put_cpu_var(xen_vcpu); } -static unsigned long xen_get_wallclock(void) +static void xen_get_wallclock(struct timespec *now) { - struct timespec ts; - - xen_read_wallclock(&ts); - return ts.tv_sec; + xen_read_wallclock(now); } -static int xen_set_wallclock(unsigned long now) +static int xen_set_wallclock(const struct timespec *now) { struct xen_platform_op op; - int rc; /* do nothing for domU */ if (!xen_initial_domain()) return -1; op.cmd = XENPF_settime; - op.u.settime.secs = now; - op.u.settime.nsecs = 0; + op.u.settime.secs = now->tv_sec; + op.u.settime.nsecs = now->tv_nsec; op.u.settime.system_time = xen_clocksource_read(); - rc = HYPERVISOR_dom0_op(&op); - WARN(rc != 0, "XENPF_settime failed: now=%ld\n", now); - - return rc; + return HYPERVISOR_dom0_op(&op); } static struct clocksource xen_clocksource __read_mostly = { diff --git a/include/linux/efi.h b/include/linux/efi.h index 2bc0ad78d058..0068bba6f8b6 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -594,8 +594,8 @@ extern u64 efi_mem_attribute (unsigned long phys_addr, unsigned long size); extern int __init efi_uart_console_only (void); extern void efi_initialize_iomem_resources(struct resource *code_resource, struct resource *data_resource, struct resource *bss_resource); -extern unsigned long efi_get_time(void); -extern int efi_set_rtc_mmss(unsigned long nowtime); +extern void efi_get_time(struct timespec *now); +extern int efi_set_rtc_mmss(const struct timespec *now); extern void efi_reserve_boot_services(void); extern struct efi_memory_map memmap; -- cgit v1.2.3 From d23efc19478ac7fb517038922b920a6979cbd958 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 7 May 2013 11:40:58 +0200 Subject: HID: add driver for ELO 4000/4500 This is a driver for ELO 4000/4500 devices which report themselves as HID devices, but do not really send HID events on touch. So we introduce a new HID 'quirk' driver with a raw_event handler where we take care of those events. What we need additionally is an input_configured hook, because the device does not mention anything about PRESSURE and TOUCH in its report descriptor, but it actually generate those. So we set the bits in the corresponding input_dev in that hook. Thanks to Petr Ostadal who was willing to test the driver. The rest of Cc's listed below had something to do with that driver over the years in our enterprise tree. Signed-off-by: Jiri Slaby Tested-by: Petr Ostadal Cc: Oliver Neukum Cc: Vojtech Pavlik Cc: Egbert Eich Cc: Libor Pechacek Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 7 +++ drivers/hid/Makefile | 1 + drivers/hid/hid-core.c | 2 + drivers/hid/hid-elo.c | 131 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 141 insertions(+) create mode 100644 drivers/hid/hid-elo.c diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index fb52f3f6de80..dd4fdb67c974 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -217,6 +217,13 @@ config HID_ELECOM ---help--- Support for the ELECOM BM084 (bluetooth mouse). +config HID_ELO + tristate "ELO USB 4000/4500 touchscreen" + depends on USB_HID + ---help--- + Support for the ELO USB 4000/4500 touchscreens. Note that this is for + different devices than those handled by CONFIG_TOUCHSCREEN_USB_ELO. + config HID_EZKEY tristate "Ezkey BTC 8193 keyboard" if EXPERT depends on HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 2065694f57ab..d1841ce0a30b 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o obj-$(CONFIG_HID_DRAGONRISE) += hid-dr.o obj-$(CONFIG_HID_EMS_FF) += hid-emsff.o obj-$(CONFIG_HID_ELECOM) += hid-elecom.o +obj-$(CONFIG_HID_ELO) += hid-elo.o obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o obj-$(CONFIG_HID_GYRATION) += hid-gyration.o obj-$(CONFIG_HID_HOLTEK) += hid-holtek-kbd.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 264f55099940..b06bac2f04ed 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1573,6 +1573,8 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) }, { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0011) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELO, 0x0009) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELO, 0x0030) }, { HID_USB_DEVICE(USB_VENDOR_ID_EMS, USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II) }, { HID_USB_DEVICE(USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193) }, { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR) }, diff --git a/drivers/hid/hid-elo.c b/drivers/hid/hid-elo.c new file mode 100644 index 000000000000..56143e011f22 --- /dev/null +++ b/drivers/hid/hid-elo.c @@ -0,0 +1,131 @@ +/* + * HID driver for ELO usb touchscreen 4000/4500 + * + * Copyright (c) 2013 Jiri Slaby + * + * Data parsing taken from elousb driver by Vojtech Pavlik. + * + * This driver is licensed under the terms of GPLv2. + */ + +#include +#include +#include + +#include "hid-ids.h" + +static void elo_input_configured(struct hid_device *hdev, + struct hid_input *hidinput) +{ + struct input_dev *input = hidinput->input; + + set_bit(BTN_TOUCH, input->keybit); + set_bit(ABS_PRESSURE, input->absbit); + input_set_abs_params(input, ABS_PRESSURE, 0, 256, 0, 0); +} + +static void elo_process_data(struct input_dev *input, const u8 *data, int size) +{ + int press; + + input_report_abs(input, ABS_X, (data[3] << 8) | data[2]); + input_report_abs(input, ABS_Y, (data[5] << 8) | data[4]); + + press = 0; + if (data[1] & 0x80) + press = (data[7] << 8) | data[6]; + input_report_abs(input, ABS_PRESSURE, press); + + if (data[1] & 0x03) { + input_report_key(input, BTN_TOUCH, 1); + input_sync(input); + } + + if (data[1] & 0x04) + input_report_key(input, BTN_TOUCH, 0); + + input_sync(input); +} + +static int elo_raw_event(struct hid_device *hdev, struct hid_report *report, + u8 *data, int size) +{ + struct hid_input *hidinput; + + if (!(hdev->claimed & HID_CLAIMED_INPUT) || list_empty(&hdev->inputs)) + return 0; + + hidinput = list_first_entry(&hdev->inputs, struct hid_input, list); + + switch (report->id) { + case 0: + if (data[0] == 'T') { /* Mandatory ELO packet marker */ + elo_process_data(hidinput->input, data, size); + return 1; + } + break; + default: /* unknown report */ + /* Unknown report type; pass upstream */ + hid_info(hdev, "unknown report type %d\n", report->id); + break; + } + + return 0; +} + +static int elo_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + int ret; + + ret = hid_parse(hdev); + if (ret) { + hid_err(hdev, "parse failed\n"); + goto err_free; + } + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (ret) { + hid_err(hdev, "hw start failed\n"); + goto err_free; + } + + return 0; +err_free: + return ret; +} + +static void elo_remove(struct hid_device *hdev) +{ + hid_hw_stop(hdev); +} + +static const struct hid_device_id elo_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ELO, 0x0009), }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELO, 0x0030), }, + { } +}; +MODULE_DEVICE_TABLE(hid, elo_devices); + +static struct hid_driver elo_driver = { + .name = "elo", + .id_table = elo_devices, + .probe = elo_probe, + .remove = elo_remove, + .raw_event = elo_raw_event, + .input_configured = elo_input_configured, +}; + +static int __init elo_driver_init(void) +{ + return hid_register_driver(&elo_driver); +} +module_init(elo_driver_init); + +static void __exit elo_driver_exit(void) +{ + hid_unregister_driver(&elo_driver); +} +module_exit(elo_driver_exit); + +MODULE_AUTHOR("Jiri Slaby "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From d8e5aec8d9e8754e4b4e12d9b61dc89fe229349b Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 7 May 2013 11:40:59 +0200 Subject: HID: elo: add quirks for broken firmware One firmare version in the devices the driver takes care of is completely broken and needs periodic pokes from our side. We implemented this as a periodic delayed queue. The idea of the pokes was taken from the suse enterprise kernel, in particular from Libor's "Elo touchscreen firmware M workaround". I am quoting him here: This patch adds periodic polling of the Elo USB touchscreens. Needed as a workaround for devices with M-level firmware, otherwise these devices are known to misbehave (as reported by Elo developers). Signed-off-by: Jiri Slaby Tested-by: Petr Ostadal Cc: Oliver Neukum Cc: Vojtech Pavlik Cc: Egbert Eich Cc: Libor Pechacek Signed-off-by: Jiri Kosina --- drivers/hid/hid-elo.c | 144 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 143 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-elo.c b/drivers/hid/hid-elo.c index 56143e011f22..f042a6cf8b18 100644 --- a/drivers/hid/hid-elo.c +++ b/drivers/hid/hid-elo.c @@ -11,9 +11,32 @@ #include #include #include +#include +#include #include "hid-ids.h" +#define ELO_PERIODIC_READ_INTERVAL HZ +#define ELO_SMARTSET_CMD_TIMEOUT 2000 /* msec */ + +/* Elo SmartSet commands */ +#define ELO_FLUSH_SMARTSET_RESPONSES 0x02 /* Flush all pending smartset responses */ +#define ELO_SEND_SMARTSET_COMMAND 0x05 /* Send a smartset command */ +#define ELO_GET_SMARTSET_RESPONSE 0x06 /* Get a smartset response */ +#define ELO_DIAG 0x64 /* Diagnostics command */ +#define ELO_SMARTSET_PACKET_SIZE 8 + +struct elo_priv { + struct usb_device *usbdev; + struct delayed_work work; + unsigned char buffer[ELO_SMARTSET_PACKET_SIZE]; +}; + +static struct workqueue_struct *wq; +static bool use_fw_quirk = true; +module_param(use_fw_quirk, bool, S_IRUGO); +MODULE_PARM_DESC(use_fw_quirk, "Do periodic pokes for broken M firmwares (default = true)"); + static void elo_input_configured(struct hid_device *hdev, struct hid_input *hidinput) { @@ -73,10 +96,108 @@ static int elo_raw_event(struct hid_device *hdev, struct hid_report *report, return 0; } +static int elo_smartset_send_get(struct usb_device *dev, u8 command, + void *data) +{ + unsigned int pipe; + u8 dir; + + if (command == ELO_SEND_SMARTSET_COMMAND) { + pipe = usb_sndctrlpipe(dev, 0); + dir = USB_DIR_OUT; + } else if (command == ELO_GET_SMARTSET_RESPONSE) { + pipe = usb_rcvctrlpipe(dev, 0); + dir = USB_DIR_IN; + } else + return -EINVAL; + + return usb_control_msg(dev, pipe, command, + dir | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, 0, data, ELO_SMARTSET_PACKET_SIZE, + ELO_SMARTSET_CMD_TIMEOUT); +} + +static int elo_flush_smartset_responses(struct usb_device *dev) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + ELO_FLUSH_SMARTSET_RESPONSES, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); +} + +static void elo_work(struct work_struct *work) +{ + struct elo_priv *priv = container_of(work, struct elo_priv, work.work); + struct usb_device *dev = priv->usbdev; + unsigned char *buffer = priv->buffer; + int ret; + + ret = elo_flush_smartset_responses(dev); + if (ret < 0) { + dev_err(&dev->dev, "initial FLUSH_SMARTSET_RESPONSES failed, error %d\n", + ret); + goto fail; + } + + /* send Diagnostics command */ + *buffer = ELO_DIAG; + ret = elo_smartset_send_get(dev, ELO_SEND_SMARTSET_COMMAND, buffer); + if (ret < 0) { + dev_err(&dev->dev, "send Diagnostics Command failed, error %d\n", + ret); + goto fail; + } + + /* get the result */ + ret = elo_smartset_send_get(dev, ELO_GET_SMARTSET_RESPONSE, buffer); + if (ret < 0) { + dev_err(&dev->dev, "get Diagnostics Command response failed, error %d\n", + ret); + goto fail; + } + + /* read the ack */ + if (*buffer != 'A') { + ret = elo_smartset_send_get(dev, ELO_GET_SMARTSET_RESPONSE, + buffer); + if (ret < 0) { + dev_err(&dev->dev, "get acknowledge response failed, error %d\n", + ret); + goto fail; + } + } + +fail: + ret = elo_flush_smartset_responses(dev); + if (ret < 0) + dev_err(&dev->dev, "final FLUSH_SMARTSET_RESPONSES failed, error %d\n", + ret); + queue_delayed_work(wq, &priv->work, ELO_PERIODIC_READ_INTERVAL); +} + +/* + * Not all Elo devices need the periodic HID descriptor reads. + * Only firmware version M needs this. + */ +static bool elo_broken_firmware(struct usb_device *dev) +{ + return use_fw_quirk && le16_to_cpu(dev->descriptor.bcdDevice) == 0x10d; +} + static int elo_probe(struct hid_device *hdev, const struct hid_device_id *id) { + struct elo_priv *priv; int ret; + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + INIT_DELAYED_WORK(&priv->work, elo_work); + priv->usbdev = interface_to_usbdev(to_usb_interface(hdev->dev.parent)); + + hid_set_drvdata(hdev, priv); + ret = hid_parse(hdev); if (ret) { hid_err(hdev, "parse failed\n"); @@ -89,14 +210,24 @@ static int elo_probe(struct hid_device *hdev, const struct hid_device_id *id) goto err_free; } + if (elo_broken_firmware(priv->usbdev)) { + hid_info(hdev, "broken firmware found, installing workaround\n"); + queue_delayed_work(wq, &priv->work, ELO_PERIODIC_READ_INTERVAL); + } + return 0; err_free: + kfree(priv); return ret; } static void elo_remove(struct hid_device *hdev) { + struct elo_priv *priv = hid_get_drvdata(hdev); + hid_hw_stop(hdev); + flush_workqueue(wq); + kfree(priv); } static const struct hid_device_id elo_devices[] = { @@ -117,13 +248,24 @@ static struct hid_driver elo_driver = { static int __init elo_driver_init(void) { - return hid_register_driver(&elo_driver); + int ret; + + wq = create_singlethread_workqueue("elousb"); + if (!wq) + return -ENOMEM; + + ret = hid_register_driver(&elo_driver); + if (ret) + destroy_workqueue(wq); + + return ret; } module_init(elo_driver_init); static void __exit elo_driver_exit(void) { hid_unregister_driver(&elo_driver); + destroy_workqueue(wq); } module_exit(elo_driver_exit); -- cgit v1.2.3 From 360603a1be2c7dc940105575f7dd1b8b82d1c3b3 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 28 May 2013 15:47:39 -0400 Subject: sprintf: hex_string(): fix comment hex_string() had a typo in a comment. Signed-off-by: Steven Rostedt Signed-off-by: Jiri Kosina --- lib/vsprintf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/vsprintf.c b/lib/vsprintf.c index e149c6416384..7d8467645d2e 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -670,7 +670,7 @@ static noinline_for_stack char *hex_string(char *buf, char *end, u8 *addr, struct printf_spec spec, const char *fmt) { - int i, len = 1; /* if we pass '%ph[CDN]', field witdh remains + int i, len = 1; /* if we pass '%ph[CDN]', field width remains negative value, fallback to the default */ char separator; -- cgit v1.2.3 From de68d1003d9eb0a5f7d4714315614e4bc956f68e Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sat, 9 Mar 2013 23:14:22 +0100 Subject: batman-adv: split batadv_is_wifi_iface() into two functions Previously batadv_is_wifi_iface() did two things at once: looking up a net_device from an interface index, and determining if it is a wifi device. The second part is useful itself when the caller already has a net_device reference. Signed-off-by: Matthias Schiffer Signed-off-by: Marek Lindner Signed-off-by: Antonio Quartulli --- net/batman-adv/hard-interface.c | 84 +++++++++++++++++++++++++---------------- 1 file changed, 52 insertions(+), 32 deletions(-) diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index b6504eac0ed8..d5ec67b63253 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -117,6 +117,58 @@ static int batadv_is_valid_iface(const struct net_device *net_dev) return 1; } +/** + * batadv_is_wifi_netdev - check if the given net_device struct is a wifi + * interface + * @net_device: the device to check + * + * Returns true if the net device is a 802.11 wireless device, false otherwise. + */ +static bool batadv_is_wifi_netdev(struct net_device *net_device) +{ +#ifdef CONFIG_WIRELESS_EXT + /* pre-cfg80211 drivers have to implement WEXT, so it is possible to + * check for wireless_handlers != NULL + */ + if (net_device->wireless_handlers) + return true; +#endif + + /* cfg80211 drivers have to set ieee80211_ptr */ + if (net_device->ieee80211_ptr) + return true; + + return false; +} + +/** + * batadv_is_wifi_iface - check if the given interface represented by ifindex + * is a wifi interface + * @ifindex: interface index to check + * + * Returns true if the interface represented by ifindex is a 802.11 wireless + * device, false otherwise. + */ +bool batadv_is_wifi_iface(int ifindex) +{ + struct net_device *net_device = NULL; + bool ret = false; + + if (ifindex == BATADV_NULL_IFINDEX) + goto out; + + net_device = dev_get_by_index(&init_net, ifindex); + if (!net_device) + goto out; + + ret = batadv_is_wifi_netdev(net_device); + +out: + if (net_device) + dev_put(net_device); + return ret; +} + static struct batadv_hard_iface * batadv_hardif_get_active(const struct net_device *soft_iface) { @@ -657,38 +709,6 @@ out: return NOTIFY_DONE; } -/* This function returns true if the interface represented by ifindex is a - * 802.11 wireless device - */ -bool batadv_is_wifi_iface(int ifindex) -{ - struct net_device *net_device = NULL; - bool ret = false; - - if (ifindex == BATADV_NULL_IFINDEX) - goto out; - - net_device = dev_get_by_index(&init_net, ifindex); - if (!net_device) - goto out; - -#ifdef CONFIG_WIRELESS_EXT - /* pre-cfg80211 drivers have to implement WEXT, so it is possible to - * check for wireless_handlers != NULL - */ - if (net_device->wireless_handlers) - ret = true; - else -#endif - /* cfg80211 drivers have to set ieee80211_ptr */ - if (net_device->ieee80211_ptr) - ret = true; -out: - if (net_device) - dev_put(net_device); - return ret; -} - struct notifier_block batadv_hard_if_notifier = { .notifier_call = batadv_hard_if_event, }; -- cgit v1.2.3 From caf65bfcc5dbabd7222fa45fdcd42ce0783d7a42 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sat, 9 Mar 2013 23:14:23 +0100 Subject: batman-adv: send each broadcast only once on non-wireless interfaces While it makes sense to send each broadcast thrice on 802.11 (WLAN) interfaces as broadcasts are often unreliable on these, there is no reason to do so on other interface types. The increased the overhead can be harmful on low-bandwidth links like VPN connections over slow internet lines, therefore it is better to reduce the number of broadcast packets sent on non-wireless links to one. Signed-off-by: Matthias Schiffer Signed-off-by: Marek Lindner Signed-off-by: Antonio Quartulli --- net/batman-adv/hard-interface.c | 4 ++++ net/batman-adv/main.h | 5 +++++ net/batman-adv/send.c | 5 ++++- net/batman-adv/types.h | 2 ++ 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index d5ec67b63253..4a76ed654c92 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -593,6 +593,10 @@ batadv_hardif_add_interface(struct net_device *net_dev) INIT_WORK(&hard_iface->cleanup_work, batadv_hardif_remove_interface_finish); + hard_iface->num_bcasts = BATADV_NUM_BCASTS_DEFAULT; + if (batadv_is_wifi_netdev(net_dev)) + hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS; + /* extra reference for return */ atomic_set(&hard_iface->refcount, 2); diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h index 59a0d6af15c8..ea1a3bafe9c3 100644 --- a/net/batman-adv/main.h +++ b/net/batman-adv/main.h @@ -76,6 +76,11 @@ #define BATADV_LOG_BUF_LEN 8192 /* has to be a power of 2 */ +/* number of packets to send for broadcasts on different interface types */ +#define BATADV_NUM_BCASTS_DEFAULT 1 +#define BATADV_NUM_BCASTS_WIRELESS 3 +#define BATADV_NUM_BCASTS_MAX 3 + /* msecs after which an ARP_REQUEST is sent in broadcast as fallback */ #define ARP_REQ_DELAY 250 /* numbers of originator to contact for any PUT/GET DHT operation */ diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c index 263cfd1ccee7..eb16b04d4bee 100644 --- a/net/batman-adv/send.c +++ b/net/batman-adv/send.c @@ -260,6 +260,9 @@ static void batadv_send_outstanding_bcast_packet(struct work_struct *work) if (hard_iface->soft_iface != soft_iface) continue; + if (forw_packet->num_packets >= hard_iface->num_bcasts) + continue; + /* send a copy of the saved skb */ skb1 = skb_clone(forw_packet->skb, GFP_ATOMIC); if (skb1) @@ -271,7 +274,7 @@ static void batadv_send_outstanding_bcast_packet(struct work_struct *work) forw_packet->num_packets++; /* if we still have some more bcasts to send */ - if (forw_packet->num_packets < 3) { + if (forw_packet->num_packets < BATADV_NUM_BCASTS_MAX) { _batadv_add_bcast_packet_to_list(bat_priv, forw_packet, msecs_to_jiffies(5)); return; diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index aba8364c3689..5f542bdd9a4d 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -61,6 +61,7 @@ struct batadv_hard_iface_bat_iv { * @if_status: status of the interface for batman-adv * @net_dev: pointer to the net_device * @frag_seqno: last fragment sequence number sent by this interface + * @num_bcasts: number of payload re-broadcasts on this interface (ARQ) * @hardif_obj: kobject of the per interface sysfs "mesh" directory * @refcount: number of contexts the object is used * @batman_adv_ptype: packet type describing packets that should be processed by @@ -76,6 +77,7 @@ struct batadv_hard_iface { char if_status; struct net_device *net_dev; atomic_t frag_seqno; + uint8_t num_bcasts; struct kobject *hardif_obj; atomic_t refcount; struct packet_type batman_adv_ptype; -- cgit v1.2.3 From 93178018eb35aaa6ebcb5a136dce7eb3add011ab Mon Sep 17 00:00:00 2001 From: Marek Lindner Date: Sun, 10 Mar 2013 19:29:15 +0800 Subject: batman-adv: fix typos in kernel doc & comments Signed-off-by: Marek Lindner Signed-off-by: Antonio Quartulli --- net/batman-adv/distributed-arp-table.c | 82 ++++++++++++++++------------------ 1 file changed, 39 insertions(+), 43 deletions(-) diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index 239992021b1d..06345d401588 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -45,9 +45,9 @@ static void batadv_dat_start_timer(struct batadv_priv *bat_priv) } /** - * batadv_dat_entry_free_ref - decrements the dat_entry refcounter and possibly + * batadv_dat_entry_free_ref - decrement the dat_entry refcounter and possibly * free it - * @dat_entry: the oentry to free + * @dat_entry: the entry to free */ static void batadv_dat_entry_free_ref(struct batadv_dat_entry *dat_entry) { @@ -56,10 +56,10 @@ static void batadv_dat_entry_free_ref(struct batadv_dat_entry *dat_entry) } /** - * batadv_dat_to_purge - checks whether a dat_entry has to be purged or not + * batadv_dat_to_purge - check whether a dat_entry has to be purged or not * @dat_entry: the entry to check * - * Returns true if the entry has to be purged now, false otherwise + * Returns true if the entry has to be purged now, false otherwise. */ static bool batadv_dat_to_purge(struct batadv_dat_entry *dat_entry) { @@ -75,8 +75,8 @@ static bool batadv_dat_to_purge(struct batadv_dat_entry *dat_entry) * returns a boolean value: true is the entry has to be deleted, * false otherwise * - * Loops over each entry in the DAT local storage and delete it if and only if - * the to_purge function passed as argument returns true + * Loops over each entry in the DAT local storage and deletes it if and only if + * the to_purge function passed as argument returns true. */ static void __batadv_dat_purge(struct batadv_priv *bat_priv, bool (*to_purge)(struct batadv_dat_entry *)) @@ -97,7 +97,7 @@ static void __batadv_dat_purge(struct batadv_priv *bat_priv, spin_lock_bh(list_lock); hlist_for_each_entry_safe(dat_entry, node_tmp, head, hash_entry) { - /* if an helper function has been passed as parameter, + /* if a helper function has been passed as parameter, * ask it if the entry has to be purged or not */ if (to_purge && !to_purge(dat_entry)) @@ -134,7 +134,7 @@ static void batadv_dat_purge(struct work_struct *work) * @node: node in the local table * @data2: second object to compare the node to * - * Returns 1 if the two entry are the same, 0 otherwise + * Returns 1 if the two entries are the same, 0 otherwise. */ static int batadv_compare_dat(const struct hlist_node *node, const void *data2) { @@ -149,7 +149,7 @@ static int batadv_compare_dat(const struct hlist_node *node, const void *data2) * @skb: ARP packet * @hdr_size: size of the possible header before the ARP packet * - * Returns the value of the hw_src field in the ARP packet + * Returns the value of the hw_src field in the ARP packet. */ static uint8_t *batadv_arp_hw_src(struct sk_buff *skb, int hdr_size) { @@ -166,7 +166,7 @@ static uint8_t *batadv_arp_hw_src(struct sk_buff *skb, int hdr_size) * @skb: ARP packet * @hdr_size: size of the possible header before the ARP packet * - * Returns the value of the ip_src field in the ARP packet + * Returns the value of the ip_src field in the ARP packet. */ static __be32 batadv_arp_ip_src(struct sk_buff *skb, int hdr_size) { @@ -178,7 +178,7 @@ static __be32 batadv_arp_ip_src(struct sk_buff *skb, int hdr_size) * @skb: ARP packet * @hdr_size: size of the possible header before the ARP packet * - * Returns the value of the hw_dst field in the ARP packet + * Returns the value of the hw_dst field in the ARP packet. */ static uint8_t *batadv_arp_hw_dst(struct sk_buff *skb, int hdr_size) { @@ -190,7 +190,7 @@ static uint8_t *batadv_arp_hw_dst(struct sk_buff *skb, int hdr_size) * @skb: ARP packet * @hdr_size: size of the possible header before the ARP packet * - * Returns the value of the ip_dst field in the ARP packet + * Returns the value of the ip_dst field in the ARP packet. */ static __be32 batadv_arp_ip_dst(struct sk_buff *skb, int hdr_size) { @@ -202,7 +202,7 @@ static __be32 batadv_arp_ip_dst(struct sk_buff *skb, int hdr_size) * @data: data to hash * @size: size of the hash table * - * Returns the selected index in the hash table for the given data + * Returns the selected index in the hash table for the given data. */ static uint32_t batadv_hash_dat(const void *data, uint32_t size) { @@ -224,12 +224,12 @@ static uint32_t batadv_hash_dat(const void *data, uint32_t size) } /** - * batadv_dat_entry_hash_find - looks for a given dat_entry in the local hash + * batadv_dat_entry_hash_find - look for a given dat_entry in the local hash * table * @bat_priv: the bat priv with all the soft interface information * @ip: search key * - * Returns the dat_entry if found, NULL otherwise + * Returns the dat_entry if found, NULL otherwise. */ static struct batadv_dat_entry * batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip) @@ -343,9 +343,6 @@ static void batadv_dbg_arp(struct batadv_priv *bat_priv, struct sk_buff *skb, if (hdr_size == 0) return; - /* if the ARP packet is encapsulated in a batman packet, let's print - * some debug messages - */ unicast_4addr_packet = (struct batadv_unicast_4addr_packet *)skb->data; switch (unicast_4addr_packet->u.header.packet_type) { @@ -409,7 +406,8 @@ static void batadv_dbg_arp(struct batadv_priv *bat_priv, struct sk_buff *skb, * @candidate: orig_node under evaluation * @max_orig_node: last selected candidate * - * Returns true if the node has been elected as next candidate or false othrwise + * Returns true if the node has been elected as next candidate or false + * otherwise. */ static bool batadv_is_orig_node_eligible(struct batadv_dat_candidate *res, int select, batadv_dat_addr_t tmp_max, @@ -472,7 +470,7 @@ static void batadv_choose_next_candidate(struct batadv_priv *bat_priv, */ cands[select].type = BATADV_DAT_CANDIDATE_NOT_FOUND; - /* iterate over the originator list and find the node with closest + /* iterate over the originator list and find the node with the closest * dat_address which has not been selected yet */ for (i = 0; i < hash->size; i++) { @@ -480,7 +478,7 @@ static void batadv_choose_next_candidate(struct batadv_priv *bat_priv, rcu_read_lock(); hlist_for_each_entry_rcu(orig_node, head, hash_entry) { - /* the dht space is a ring and addresses are unsigned */ + /* the dht space is a ring using unsigned addresses */ tmp_max = BATADV_DAT_ADDR_MAX - orig_node->dat_addr + ip_key; @@ -512,7 +510,7 @@ static void batadv_choose_next_candidate(struct batadv_priv *bat_priv, } /** - * batadv_dat_select_candidates - selects the nodes which the DHT message has to + * batadv_dat_select_candidates - select the nodes which the DHT message has to * be sent to * @bat_priv: the bat priv with all the soft interface information * @ip_dst: ipv4 to look up in the DHT @@ -521,7 +519,7 @@ static void batadv_choose_next_candidate(struct batadv_priv *bat_priv, * closest values (from the LEFT, with wrap around if needed) then the hash * value of the key. ip_dst is the key. * - * Returns the candidate array of size BATADV_DAT_CANDIDATE_NUM + * Returns the candidate array of size BATADV_DAT_CANDIDATE_NUM. */ static struct batadv_dat_candidate * batadv_dat_select_candidates(struct batadv_priv *bat_priv, __be32 ip_dst) @@ -558,10 +556,11 @@ batadv_dat_select_candidates(struct batadv_priv *bat_priv, __be32 ip_dst) * @ip: the DHT key * @packet_subtype: unicast4addr packet subtype to use * - * In this function the skb is copied by means of pskb_copy() and is sent as - * unicast packet to each of the selected candidates + * This function copies the skb with pskb_copy() and is sent as unicast packet + * to each of the selected candidates. * - * Returns true if the packet is sent to at least one candidate, false otherwise + * Returns true if the packet is sent to at least one candidate, false + * otherwise. */ static bool batadv_dat_send_data(struct batadv_priv *bat_priv, struct sk_buff *skb, __be32 ip, @@ -727,7 +726,7 @@ out: * @skb: packet to analyse * @hdr_size: size of the possible header before the ARP packet in the skb * - * Returns the ARP type if the skb contains a valid ARP packet, 0 otherwise + * Returns the ARP type if the skb contains a valid ARP packet, 0 otherwise. */ static uint16_t batadv_arp_get_type(struct batadv_priv *bat_priv, struct sk_buff *skb, int hdr_size) @@ -754,9 +753,7 @@ static uint16_t batadv_arp_get_type(struct batadv_priv *bat_priv, arphdr = (struct arphdr *)(skb->data + hdr_size + ETH_HLEN); - /* Check whether the ARP packet carries a valid - * IP information - */ + /* check whether the ARP packet carries a valid IP information */ if (arphdr->ar_hrd != htons(ARPHRD_ETHER)) goto out; @@ -784,7 +781,7 @@ static uint16_t batadv_arp_get_type(struct batadv_priv *bat_priv, if (is_zero_ether_addr(hw_src) || is_multicast_ether_addr(hw_src)) goto out; - /* we don't care about the destination MAC address in ARP requests */ + /* don't care about the destination MAC address in ARP requests */ if (arphdr->ar_op != htons(ARPOP_REQUEST)) { hw_dst = batadv_arp_hw_dst(skb, hdr_size); if (is_zero_ether_addr(hw_dst) || @@ -804,8 +801,8 @@ out: * @skb: packet to check * * Returns true if the message has been sent to the dht candidates, false - * otherwise. In case of true the message has to be enqueued to permit the - * fallback + * otherwise. In case of a positive return value the message has to be enqueued + * to permit the fallback. */ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv, struct sk_buff *skb) @@ -867,7 +864,7 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv, batadv_dbg(BATADV_DBG_DAT, bat_priv, "ARP request replied locally\n"); ret = true; } else { - /* Send the request on the DHT */ + /* Send the request to the DHT */ ret = batadv_dat_send_data(bat_priv, skb, ip_dst, BATADV_P_DAT_DHT_GET); } @@ -884,7 +881,7 @@ out: * @skb: packet to check * @hdr_size: size of the encapsulation header * - * Returns true if the request has been answered, false otherwise + * Returns true if the request has been answered, false otherwise. */ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv, struct sk_buff *skb, int hdr_size) @@ -924,10 +921,9 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv, if (!skb_new) goto out; - /* to preserve backwards compatibility, here the node has to answer - * using the same packet type it received for the request. This is due - * to that if a node is not using the 4addr packet format it may not - * support it. + /* To preserve backwards compatibility, the node has choose the outgoing + * format based on the incoming request packet type. The assumption is + * that a node not using the 4addr packet format doesn't support it. */ if (hdr_size == sizeof(struct batadv_unicast_4addr_packet)) err = batadv_unicast_4addr_send_skb(bat_priv, skb_new, @@ -977,7 +973,7 @@ void batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv, batadv_dat_entry_add(bat_priv, ip_dst, hw_dst); /* Send the ARP reply to the candidates for both the IP addresses that - * the node got within the ARP reply + * the node obtained from the ARP reply */ batadv_dat_send_data(bat_priv, skb, ip_src, BATADV_P_DAT_DHT_PUT); batadv_dat_send_data(bat_priv, skb, ip_dst, BATADV_P_DAT_DHT_PUT); @@ -987,7 +983,7 @@ void batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv, * DAT storage only * @bat_priv: the bat priv with all the soft interface information * @skb: packet to check - * @hdr_size: siaze of the encapsulation header + * @hdr_size: size of the encapsulation header */ bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv, struct sk_buff *skb, int hdr_size) @@ -1031,11 +1027,11 @@ out: /** * batadv_dat_drop_broadcast_packet - check if an ARP request has to be dropped - * (because the node has already got the reply via DAT) or not + * (because the node has already obtained the reply via DAT) or not * @bat_priv: the bat priv with all the soft interface information * @forw_packet: the broadcast packet * - * Returns true if the node can drop the packet, false otherwise + * Returns true if the node can drop the packet, false otherwise. */ bool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv, struct batadv_forw_packet *forw_packet) -- cgit v1.2.3 From 863dd7a82a2fba3e1a094c3d10a9cc8d1afd10d6 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Mon, 25 Mar 2013 13:49:46 +0100 Subject: batman-adv: drop useless argument seqno in neighbor creation the sequence number is not stored in struct neigh_node, therefore there is no need to pass such value to the neigh_node creation procedure. At the moment the value is only used by a debug message, but given the fact that the seqno is not related to the neighbor object, it is better to print it elsewhere. Signed-off-by: Antonio Quartulli Signed-off-by: Marek Lindner --- net/batman-adv/bat_iv_ogm.c | 11 ++++------- net/batman-adv/originator.c | 5 ++--- net/batman-adv/originator.h | 2 +- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index 071f288b77a8..da239c5424b8 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -33,12 +33,11 @@ static struct batadv_neigh_node * batadv_iv_ogm_neigh_new(struct batadv_hard_iface *hard_iface, const uint8_t *neigh_addr, struct batadv_orig_node *orig_node, - struct batadv_orig_node *orig_neigh, __be32 seqno) + struct batadv_orig_node *orig_neigh) { struct batadv_neigh_node *neigh_node; - neigh_node = batadv_neigh_node_new(hard_iface, neigh_addr, - ntohl(seqno)); + neigh_node = batadv_neigh_node_new(hard_iface, neigh_addr); if (!neigh_node) goto out; @@ -696,8 +695,7 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv, neigh_node = batadv_iv_ogm_neigh_new(if_incoming, ethhdr->h_source, - orig_node, orig_tmp, - batadv_ogm_packet->seqno); + orig_node, orig_tmp); batadv_orig_node_free_ref(orig_tmp); if (!neigh_node) @@ -829,8 +827,7 @@ static int batadv_iv_ogm_calc_tq(struct batadv_orig_node *orig_node, neigh_node = batadv_iv_ogm_neigh_new(if_incoming, orig_neigh_node->orig, orig_neigh_node, - orig_neigh_node, - batadv_ogm_packet->seqno); + orig_neigh_node); if (!neigh_node) goto out; diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c index fad1a2093e15..ddf563484798 100644 --- a/net/batman-adv/originator.c +++ b/net/batman-adv/originator.c @@ -92,7 +92,7 @@ batadv_orig_node_get_router(struct batadv_orig_node *orig_node) struct batadv_neigh_node * batadv_neigh_node_new(struct batadv_hard_iface *hard_iface, - const uint8_t *neigh_addr, uint32_t seqno) + const uint8_t *neigh_addr) { struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); struct batadv_neigh_node *neigh_node; @@ -110,8 +110,7 @@ batadv_neigh_node_new(struct batadv_hard_iface *hard_iface, atomic_set(&neigh_node->refcount, 2); batadv_dbg(BATADV_DBG_BATMAN, bat_priv, - "Creating new neighbor %pM, initial seqno %d\n", - neigh_addr, seqno); + "Creating new neighbor %pM\n", neigh_addr); out: return neigh_node; diff --git a/net/batman-adv/originator.h b/net/batman-adv/originator.h index 734e5a3d8a5b..7887b84a9af4 100644 --- a/net/batman-adv/originator.h +++ b/net/batman-adv/originator.h @@ -31,7 +31,7 @@ struct batadv_orig_node *batadv_get_orig_node(struct batadv_priv *bat_priv, const uint8_t *addr); struct batadv_neigh_node * batadv_neigh_node_new(struct batadv_hard_iface *hard_iface, - const uint8_t *neigh_addr, uint32_t seqno); + const uint8_t *neigh_addr); void batadv_neigh_node_free_ref(struct batadv_neigh_node *neigh_node); struct batadv_neigh_node * batadv_orig_node_get_router(struct batadv_orig_node *orig_node); -- cgit v1.2.3 From d1dc30739c587fe65f4120c045258ab01c79db1b Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Mon, 25 Mar 2013 13:54:45 +0100 Subject: batman-adv: slightly improve neighbor creation debug message print the interface along with the new neighbor mac address Signed-off-by: Antonio Quartulli Signed-off-by: Marek Lindner --- net/batman-adv/originator.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c index ddf563484798..f50553a7de62 100644 --- a/net/batman-adv/originator.c +++ b/net/batman-adv/originator.c @@ -110,7 +110,8 @@ batadv_neigh_node_new(struct batadv_hard_iface *hard_iface, atomic_set(&neigh_node->refcount, 2); batadv_dbg(BATADV_DBG_BATMAN, bat_priv, - "Creating new neighbor %pM\n", neigh_addr); + "Creating new neighbor %pM on interface %s\n", neigh_addr, + hard_iface->net_dev->name); out: return neigh_node; -- cgit v1.2.3 From 281581d3e79eaacfcdc0827e9bf990422252ba5c Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Mon, 25 Mar 2013 22:27:00 +0100 Subject: batman-adv: don't check the source address twice The source address has already been checked in batadv_check_management_packet() upon packet reception and therefore it does not need to be checked again in ogm_process() Signed-off-by: Antonio Quartulli Signed-off-by: Marek Lindner --- net/batman-adv/bat_iv_ogm.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index da239c5424b8..6d62e2992dfa 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -988,7 +988,7 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr, struct batadv_neigh_node *orig_neigh_router = NULL; int has_directlink_flag; int is_my_addr = 0, is_my_orig = 0, is_my_oldorig = 0; - int is_broadcast = 0, is_bidirect; + int is_bidirect; bool is_single_hop_neigh = false; bool is_from_best_next_hop = false; int is_duplicate, sameseq, simlar_ttl; @@ -1051,9 +1051,6 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr, if (batadv_compare_eth(batadv_ogm_packet->prev_sender, hard_iface->net_dev->dev_addr)) is_my_oldorig = 1; - - if (is_broadcast_ether_addr(ethhdr->h_source)) - is_broadcast = 1; } rcu_read_unlock(); @@ -1071,13 +1068,6 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr, return; } - if (is_broadcast) { - batadv_dbg(BATADV_DBG_BATMAN, bat_priv, - "Drop packet: ignoring all packets with broadcast source addr (sender: %pM)\n", - ethhdr->h_source); - return; - } - if (is_my_orig) { unsigned long *word; int offset; -- cgit v1.2.3 From a3b81b67de6ae0475c0c34c552c059f7bea2f520 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Thu, 28 Mar 2013 14:21:12 +0100 Subject: batman-adv: don't check compat version twice Compatibility version is checked upon packet reception before calling any handler. For this reason it does need to be checked once more in the handler itself. Signed-off-by: Antonio Quartulli Signed-off-by: Marek Lindner --- net/batman-adv/bat_iv_ogm.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index 6d62e2992dfa..38183dc6965d 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -1054,13 +1054,6 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr, } rcu_read_unlock(); - if (batadv_ogm_packet->header.version != BATADV_COMPAT_VERSION) { - batadv_dbg(BATADV_DBG_BATMAN, bat_priv, - "Drop packet: incompatible batman version (%i)\n", - batadv_ogm_packet->header.version); - return; - } - if (is_my_addr) { batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "Drop packet: received my own broadcast (sender: %pM)\n", -- cgit v1.2.3 From 38dc40ef52d882e08b2af71fc1b5413ac7009952 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Sat, 30 Mar 2013 17:22:00 +0100 Subject: batman-adv: do not silently ignore wrong condition Only one neigh_node per orig_node should match a given neighbor address, therefore, if more than one matching neigh_node is found, a WARNING has to be triggered to let the user know that something is wrong in the originator state instead of silently skipping the error. Signed-off-by: Antonio Quartulli Signed-off-by: Marek Lindner --- net/batman-adv/bat_iv_ogm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index 38183dc6965d..bd50e0d76aea 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -669,7 +669,7 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv, if (batadv_compare_eth(neigh_addr, ethhdr->h_source) && tmp_neigh_node->if_incoming == if_incoming && atomic_inc_not_zero(&tmp_neigh_node->refcount)) { - if (neigh_node) + if (WARN(neigh_node, "too many matching neigh_nodes")) batadv_neigh_node_free_ref(neigh_node); neigh_node = tmp_neigh_node; continue; -- cgit v1.2.3 From 7db3fc291bb22bf43667b009dd0e701ed4eb7c96 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Tue, 2 Apr 2013 12:16:53 +0200 Subject: batman-adv: don't initialise batman_iv private members in hard-interface.c hard-interface.c has to do not contain any routing algorithm specific code. Allocate the hard-interface with kzalloc() and remove any useless and algorithm specific member initialisation Signed-off-by: Antonio Quartulli Signed-off-by: Marek Lindner --- net/batman-adv/hard-interface.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index 4a76ed654c92..c478e6bcf89b 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -577,7 +577,7 @@ batadv_hardif_add_interface(struct net_device *net_dev) dev_hold(net_dev); - hard_iface = kmalloc(sizeof(*hard_iface), GFP_ATOMIC); + hard_iface = kzalloc(sizeof(*hard_iface), GFP_ATOMIC); if (!hard_iface) goto release_dev; @@ -603,12 +603,6 @@ batadv_hardif_add_interface(struct net_device *net_dev) batadv_check_known_mac_addr(hard_iface->net_dev); list_add_tail_rcu(&hard_iface->list, &batadv_hardif_list); - /* This can't be called via a bat_priv callback because - * we have no bat_priv yet. - */ - atomic_set(&hard_iface->bat_iv.ogm_seqno, 1); - hard_iface->bat_iv.ogm_buff = NULL; - return hard_iface; free_if: -- cgit v1.2.3 From 7ed4be9523455a061e62236dc3caa9211cd7edda Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Mon, 8 Apr 2013 15:08:18 +0200 Subject: batman-adv: use eth_hdr() when it makes sense Instead of casting the result of skb_mac_header() to "struct ethhdr *" every time, the eth_hdr inline function can be use to beautify the code and improve its readability. Signed-off-by: Antonio Quartulli Signed-off-by: Marek Lindner --- net/batman-adv/bat_iv_ogm.c | 2 +- net/batman-adv/bridge_loop_avoidance.c | 8 ++++---- net/batman-adv/network-coding.c | 10 +++++----- net/batman-adv/routing.c | 12 ++++++------ net/batman-adv/send.c | 2 +- net/batman-adv/soft-interface.c | 2 +- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index bd50e0d76aea..ef41be49b314 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -1268,7 +1268,7 @@ static int batadv_iv_ogm_receive(struct sk_buff *skb, skb->len + ETH_HLEN); packet_len = skb_headlen(skb); - ethhdr = (struct ethhdr *)skb_mac_header(skb); + ethhdr = eth_hdr(skb); packet_buff = skb->data; batadv_ogm_packet = (struct batadv_ogm_packet *)packet_buff; diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c index 379061c72549..082189e2e40d 100644 --- a/net/batman-adv/bridge_loop_avoidance.c +++ b/net/batman-adv/bridge_loop_avoidance.c @@ -864,7 +864,7 @@ static int batadv_bla_process_claim(struct batadv_priv *bat_priv, short vid = -1; int ret; - ethhdr = (struct ethhdr *)skb_mac_header(skb); + ethhdr = eth_hdr(skb); if (ntohs(ethhdr->h_proto) == ETH_P_8021Q) { vhdr = (struct vlan_ethhdr *)ethhdr; @@ -885,7 +885,7 @@ static int batadv_bla_process_claim(struct batadv_priv *bat_priv, return 0; /* pskb_may_pull() may have modified the pointers, get ethhdr again */ - ethhdr = (struct ethhdr *)skb_mac_header(skb); + ethhdr = eth_hdr(skb); arphdr = (struct arphdr *)((uint8_t *)ethhdr + headlen); /* Check whether the ARP frame carries a valid @@ -1432,7 +1432,7 @@ int batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb, short vid, struct batadv_hard_iface *primary_if; int ret; - ethhdr = (struct ethhdr *)skb_mac_header(skb); + ethhdr = eth_hdr(skb); primary_if = batadv_primary_if_get_selected(bat_priv); if (!primary_if) @@ -1539,7 +1539,7 @@ int batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb, short vid) if (batadv_bla_process_claim(bat_priv, primary_if, skb)) goto handled; - ethhdr = (struct ethhdr *)skb_mac_header(skb); + ethhdr = eth_hdr(skb); if (unlikely(atomic_read(&bat_priv->bla.num_requests))) /* don't allow broadcasts while requests are in flight */ diff --git a/net/batman-adv/network-coding.c b/net/batman-adv/network-coding.c index e84629ece9b7..0787a34609b9 100644 --- a/net/batman-adv/network-coding.c +++ b/net/batman-adv/network-coding.c @@ -1245,7 +1245,7 @@ static void batadv_nc_skb_store_before_coding(struct batadv_priv *bat_priv, return; /* Set the mac header as if we actually sent the packet uncoded */ - ethhdr = (struct ethhdr *)skb_mac_header(skb); + ethhdr = eth_hdr(skb); memcpy(ethhdr->h_source, ethhdr->h_dest, ETH_ALEN); memcpy(ethhdr->h_dest, eth_dst_new, ETH_ALEN); @@ -1423,7 +1423,7 @@ void batadv_nc_skb_store_for_decoding(struct batadv_priv *bat_priv, { struct batadv_unicast_packet *packet; struct batadv_nc_path *nc_path; - struct ethhdr *ethhdr = (struct ethhdr *)skb_mac_header(skb); + struct ethhdr *ethhdr = eth_hdr(skb); __be32 packet_id; u8 *payload; @@ -1482,7 +1482,7 @@ out: void batadv_nc_skb_store_sniffed_unicast(struct batadv_priv *bat_priv, struct sk_buff *skb) { - struct ethhdr *ethhdr = (struct ethhdr *)skb_mac_header(skb); + struct ethhdr *ethhdr = eth_hdr(skb); if (batadv_is_my_mac(bat_priv, ethhdr->h_dest)) return; @@ -1533,7 +1533,7 @@ batadv_nc_skb_decode_packet(struct batadv_priv *bat_priv, struct sk_buff *skb, skb_reset_network_header(skb); /* Reconstruct original mac header */ - ethhdr = (struct ethhdr *)skb_mac_header(skb); + ethhdr = eth_hdr(skb); memcpy(ethhdr, ðhdr_tmp, sizeof(*ethhdr)); /* Select the correct unicast header information based on the location @@ -1677,7 +1677,7 @@ static int batadv_nc_recv_coded_packet(struct sk_buff *skb, return NET_RX_DROP; coded_packet = (struct batadv_coded_packet *)skb->data; - ethhdr = (struct ethhdr *)skb_mac_header(skb); + ethhdr = eth_hdr(skb); /* Verify frame is destined for us */ if (!batadv_is_my_mac(bat_priv, ethhdr->h_dest) && diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index b27a4d792d15..beeab2e8cd66 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -256,7 +256,7 @@ bool batadv_check_management_packet(struct sk_buff *skb, if (unlikely(!pskb_may_pull(skb, header_len))) return false; - ethhdr = (struct ethhdr *)skb_mac_header(skb); + ethhdr = eth_hdr(skb); /* packet with broadcast indication but unicast recipient */ if (!is_broadcast_ether_addr(ethhdr->h_dest)) @@ -392,7 +392,7 @@ int batadv_recv_icmp_packet(struct sk_buff *skb, if (unlikely(!pskb_may_pull(skb, hdr_size))) goto out; - ethhdr = (struct ethhdr *)skb_mac_header(skb); + ethhdr = eth_hdr(skb); /* packet with unicast indication but broadcast recipient */ if (is_broadcast_ether_addr(ethhdr->h_dest)) @@ -569,7 +569,7 @@ static int batadv_check_unicast_packet(struct batadv_priv *bat_priv, if (unlikely(!pskb_may_pull(skb, hdr_size))) return -ENODATA; - ethhdr = (struct ethhdr *)skb_mac_header(skb); + ethhdr = eth_hdr(skb); /* packet with unicast indication but broadcast recipient */ if (is_broadcast_ether_addr(ethhdr->h_dest)) @@ -803,7 +803,7 @@ static int batadv_route_unicast_packet(struct sk_buff *skb, struct batadv_orig_node *orig_node = NULL; struct batadv_neigh_node *neigh_node = NULL; struct batadv_unicast_packet *unicast_packet; - struct ethhdr *ethhdr = (struct ethhdr *)skb_mac_header(skb); + struct ethhdr *ethhdr = eth_hdr(skb); int ret = NET_RX_DROP; struct sk_buff *new_skb; @@ -1165,7 +1165,7 @@ int batadv_recv_bcast_packet(struct sk_buff *skb, if (unlikely(!pskb_may_pull(skb, hdr_size))) goto out; - ethhdr = (struct ethhdr *)skb_mac_header(skb); + ethhdr = eth_hdr(skb); /* packet with broadcast indication but unicast recipient */ if (!is_broadcast_ether_addr(ethhdr->h_dest)) @@ -1265,7 +1265,7 @@ int batadv_recv_vis_packet(struct sk_buff *skb, return NET_RX_DROP; vis_packet = (struct batadv_vis_packet *)skb->data; - ethhdr = (struct ethhdr *)skb_mac_header(skb); + ethhdr = eth_hdr(skb); /* not for me */ if (!batadv_is_my_mac(bat_priv, ethhdr->h_dest)) diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c index eb16b04d4bee..ed7072ad84e8 100644 --- a/net/batman-adv/send.c +++ b/net/batman-adv/send.c @@ -61,7 +61,7 @@ int batadv_send_skb_packet(struct sk_buff *skb, skb_reset_mac_header(skb); - ethhdr = (struct ethhdr *)skb_mac_header(skb); + ethhdr = eth_hdr(skb); memcpy(ethhdr->h_source, hard_iface->net_dev->dev_addr, ETH_ALEN); memcpy(ethhdr->h_dest, dst_addr, ETH_ALEN); ethhdr->h_proto = __constant_htons(ETH_P_BATMAN); diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index 819dfb006cdf..b26a6cdb934c 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -316,7 +316,7 @@ void batadv_interface_rx(struct net_device *soft_iface, skb_pull_rcsum(skb, hdr_size); skb_reset_mac_header(skb); - ethhdr = (struct ethhdr *)skb_mac_header(skb); + ethhdr = eth_hdr(skb); switch (ntohs(ethhdr->h_proto)) { case ETH_P_8021Q: -- cgit v1.2.3 From 24a5deeb8a198f0a26ae04485d9976c5e414f723 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Mon, 8 Apr 2013 09:38:12 +0200 Subject: batman-adv: move ring_buffer helper functions in bat_iv_ogm the two lonely ring_buffer helper functions are used by the bat_iv_ogm module only and therefore they can be moved inside it. Reported-by: Marek Lindner Signed-off-by: Antonio Quartulli Signed-off-by: Marek Lindner --- net/batman-adv/Makefile | 1 - net/batman-adv/bat_iv_ogm.c | 43 ++++++++++++++++++++++++++++++++++++- net/batman-adv/ring_buffer.c | 51 -------------------------------------------- net/batman-adv/ring_buffer.h | 27 ----------------------- 4 files changed, 42 insertions(+), 80 deletions(-) delete mode 100644 net/batman-adv/ring_buffer.c delete mode 100644 net/batman-adv/ring_buffer.h diff --git a/net/batman-adv/Makefile b/net/batman-adv/Makefile index acbac2a9c62f..489bb36f1b94 100644 --- a/net/batman-adv/Makefile +++ b/net/batman-adv/Makefile @@ -32,7 +32,6 @@ batman-adv-y += icmp_socket.o batman-adv-y += main.o batman-adv-$(CONFIG_BATMAN_ADV_NC) += network-coding.o batman-adv-y += originator.o -batman-adv-y += ring_buffer.o batman-adv-y += routing.o batman-adv-y += send.o batman-adv-y += soft-interface.o diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index ef41be49b314..31c2891c2cd0 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -19,7 +19,6 @@ #include "main.h" #include "translation-table.h" -#include "ring_buffer.h" #include "originator.h" #include "routing.h" #include "gateway_common.h" @@ -29,6 +28,48 @@ #include "bat_algo.h" #include "network-coding.h" +/** + * batadv_ring_buffer_set - update the ring buffer with the given value + * @lq_recv: pointer to the ring buffer + * @lq_index: index to store the value at + * @value: value to store in the ring buffer + */ +static void batadv_ring_buffer_set(uint8_t lq_recv[], uint8_t *lq_index, + uint8_t value) +{ + lq_recv[*lq_index] = value; + *lq_index = (*lq_index + 1) % BATADV_TQ_GLOBAL_WINDOW_SIZE; +} + +/** + * batadv_ring_buffer_set - compute the average of all non-zero values stored + * in the given ring buffer + * @lq_recv: pointer to the ring buffer + * + * Returns computed average value. + */ +static uint8_t batadv_ring_buffer_avg(const uint8_t lq_recv[]) +{ + const uint8_t *ptr; + uint16_t count = 0, i = 0, sum = 0; + + ptr = lq_recv; + + while (i < BATADV_TQ_GLOBAL_WINDOW_SIZE) { + if (*ptr != 0) { + count++; + sum += *ptr; + } + + i++; + ptr++; + } + + if (count == 0) + return 0; + + return (uint8_t)(sum / count); +} static struct batadv_neigh_node * batadv_iv_ogm_neigh_new(struct batadv_hard_iface *hard_iface, const uint8_t *neigh_addr, diff --git a/net/batman-adv/ring_buffer.c b/net/batman-adv/ring_buffer.c deleted file mode 100644 index ccab0bbdbb59..000000000000 --- a/net/batman-adv/ring_buffer.c +++ /dev/null @@ -1,51 +0,0 @@ -/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: - * - * Marek Lindner - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of version 2 of the GNU General Public - * License as published by the Free Software Foundation. - * - * 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 "main.h" -#include "ring_buffer.h" - -void batadv_ring_buffer_set(uint8_t lq_recv[], uint8_t *lq_index, - uint8_t value) -{ - lq_recv[*lq_index] = value; - *lq_index = (*lq_index + 1) % BATADV_TQ_GLOBAL_WINDOW_SIZE; -} - -uint8_t batadv_ring_buffer_avg(const uint8_t lq_recv[]) -{ - const uint8_t *ptr; - uint16_t count = 0, i = 0, sum = 0; - - ptr = lq_recv; - - while (i < BATADV_TQ_GLOBAL_WINDOW_SIZE) { - if (*ptr != 0) { - count++; - sum += *ptr; - } - - i++; - ptr++; - } - - if (count == 0) - return 0; - - return (uint8_t)(sum / count); -} diff --git a/net/batman-adv/ring_buffer.h b/net/batman-adv/ring_buffer.h deleted file mode 100644 index 3f92ae248e83..000000000000 --- a/net/batman-adv/ring_buffer.h +++ /dev/null @@ -1,27 +0,0 @@ -/* Copyright (C) 2007-2013 B.A.T.M.A.N. contributors: - * - * Marek Lindner - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of version 2 of the GNU General Public - * License as published by the Free Software Foundation. - * - * 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 _NET_BATMAN_ADV_RING_BUFFER_H_ -#define _NET_BATMAN_ADV_RING_BUFFER_H_ - -void batadv_ring_buffer_set(uint8_t lq_recv[], uint8_t *lq_index, - uint8_t value); -uint8_t batadv_ring_buffer_avg(const uint8_t lq_recv[]); - -#endif /* _NET_BATMAN_ADV_RING_BUFFER_H_ */ -- cgit v1.2.3 From d98966173213704873864c4e5057d823996ae95d Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Wed, 17 Apr 2013 17:44:43 +0200 Subject: batman-adv: move batadv_slide_own_bcast_window to bat_iv_ogm.c batadv_slide_own_bcast_window() is used only in bat_iv_ogm.c and it is currently touching only batman_iv specific attributes. Move it into bat_iv_ogm.c and make it static. Signed-off-by: Antonio Quartulli Signed-off-by: Marek Lindner --- net/batman-adv/bat_iv_ogm.c | 37 ++++++++++++++++++++++++++++++++++++- net/batman-adv/routing.c | 29 ----------------------------- net/batman-adv/routing.h | 1 - 3 files changed, 36 insertions(+), 31 deletions(-) diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index 31c2891c2cd0..42b7a94d61b3 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -630,6 +630,41 @@ static void batadv_iv_ogm_forward(struct batadv_orig_node *orig_node, if_incoming, 0, batadv_iv_ogm_fwd_send_time()); } +/** + * batadv_iv_ogm_slide_own_bcast_window - bitshift own OGM broadcast windows for + * the given interface + * @hard_iface: the interface for which the windows have to be shifted + */ +static void +batadv_iv_ogm_slide_own_bcast_window(struct batadv_hard_iface *hard_iface) +{ + struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); + struct batadv_hashtable *hash = bat_priv->orig_hash; + struct hlist_head *head; + struct batadv_orig_node *orig_node; + unsigned long *word; + uint32_t i; + size_t word_index; + uint8_t *w; + + for (i = 0; i < hash->size; i++) { + head = &hash->table[i]; + + rcu_read_lock(); + hlist_for_each_entry_rcu(orig_node, head, hash_entry) { + spin_lock_bh(&orig_node->ogm_cnt_lock); + word_index = hard_iface->if_num * BATADV_NUM_WORDS; + word = &(orig_node->bcast_own[word_index]); + + batadv_bit_get_packet(bat_priv, word, 1, 0); + w = &orig_node->bcast_own_sum[hard_iface->if_num]; + *w = bitmap_weight(word, BATADV_TQ_LOCAL_WINDOW_SIZE); + spin_unlock_bh(&orig_node->ogm_cnt_lock); + } + rcu_read_unlock(); + } +} + static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface) { struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); @@ -674,7 +709,7 @@ static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface) batadv_ogm_packet->gw_flags = BATADV_NO_FLAGS; } - batadv_slide_own_bcast_window(hard_iface); + batadv_iv_ogm_slide_own_bcast_window(hard_iface); batadv_iv_ogm_queue_add(bat_priv, hard_iface->bat_iv.ogm_buff, hard_iface->bat_iv.ogm_buff_len, hard_iface, 1, batadv_iv_ogm_emit_send_time(bat_priv)); diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index beeab2e8cd66..fad08469767d 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -34,35 +34,6 @@ static int batadv_route_unicast_packet(struct sk_buff *skb, struct batadv_hard_iface *recv_if); -void batadv_slide_own_bcast_window(struct batadv_hard_iface *hard_iface) -{ - struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); - struct batadv_hashtable *hash = bat_priv->orig_hash; - struct hlist_head *head; - struct batadv_orig_node *orig_node; - unsigned long *word; - uint32_t i; - size_t word_index; - uint8_t *w; - - for (i = 0; i < hash->size; i++) { - head = &hash->table[i]; - - rcu_read_lock(); - hlist_for_each_entry_rcu(orig_node, head, hash_entry) { - spin_lock_bh(&orig_node->ogm_cnt_lock); - word_index = hard_iface->if_num * BATADV_NUM_WORDS; - word = &(orig_node->bcast_own[word_index]); - - batadv_bit_get_packet(bat_priv, word, 1, 0); - w = &orig_node->bcast_own_sum[hard_iface->if_num]; - *w = bitmap_weight(word, BATADV_TQ_LOCAL_WINDOW_SIZE); - spin_unlock_bh(&orig_node->ogm_cnt_lock); - } - rcu_read_unlock(); - } -} - static void _batadv_update_route(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, struct batadv_neigh_node *neigh_node) diff --git a/net/batman-adv/routing.h b/net/batman-adv/routing.h index 99eeafaba407..72a29bde2010 100644 --- a/net/batman-adv/routing.h +++ b/net/batman-adv/routing.h @@ -20,7 +20,6 @@ #ifndef _NET_BATMAN_ADV_ROUTING_H_ #define _NET_BATMAN_ADV_ROUTING_H_ -void batadv_slide_own_bcast_window(struct batadv_hard_iface *hard_iface); bool batadv_check_management_packet(struct sk_buff *skb, struct batadv_hard_iface *hard_iface, int header_len); -- cgit v1.2.3 From 3abe4adbfb293e37d2d6f4fe22366534dc2675d9 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Wed, 3 Apr 2013 11:15:33 +0200 Subject: batman-adv: refactor batadv_tt_local_event() Instead of passing a generic combination of flags as argument, it is easier to pass the entire tt_common structure (containing the flags already set) plus a bitfield of event flags that will be unified with the already existing ones before inserting the client in the event queue. In this way invocations of the modified function can be simplified. Signed-off-by: Antonio Quartulli Signed-off-by: Marek Lindner --- net/batman-adv/translation-table.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 9e8748575845..d35b73904e04 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -163,10 +163,19 @@ batadv_tt_orig_list_entry_free_ref(struct batadv_tt_orig_list_entry *orig_entry) call_rcu(&orig_entry->rcu, batadv_tt_orig_list_entry_free_rcu); } +/** + * batadv_tt_local_event - store a local TT event (ADD/DEL) + * @bat_priv: the bat priv with all the soft interface information + * @tt_local_entry: the TT entry involved in the event + * @event_flags: flags to store in the event structure + */ static void batadv_tt_local_event(struct batadv_priv *bat_priv, - const uint8_t *addr, uint8_t flags) + struct batadv_tt_local_entry *tt_local_entry, + uint8_t event_flags) { struct batadv_tt_change_node *tt_change_node, *entry, *safe; + struct batadv_tt_common_entry *common = &tt_local_entry->common; + uint8_t flags = common->flags | event_flags; bool event_removed = false; bool del_op_requested, del_op_entry; @@ -176,7 +185,7 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv, return; tt_change_node->change.flags = flags; - memcpy(tt_change_node->change.addr, addr, ETH_ALEN); + memcpy(tt_change_node->change.addr, common->addr, ETH_ALEN); del_op_requested = flags & BATADV_TT_CLIENT_DEL; @@ -184,7 +193,7 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv, spin_lock_bh(&bat_priv->tt.changes_list_lock); list_for_each_entry_safe(entry, safe, &bat_priv->tt.changes_list, list) { - if (!batadv_compare_eth(entry->change.addr, addr)) + if (!batadv_compare_eth(entry->change.addr, common->addr)) continue; /* DEL+ADD in the same orig interval have no effect and can be @@ -332,7 +341,7 @@ void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr, } add_event: - batadv_tt_local_event(bat_priv, addr, tt_local->common.flags); + batadv_tt_local_event(bat_priv, tt_local, BATADV_NO_FLAGS); check_roaming: /* Check whether it is a roaming, but don't do anything if the roaming @@ -529,8 +538,7 @@ batadv_tt_local_set_pending(struct batadv_priv *bat_priv, struct batadv_tt_local_entry *tt_local_entry, uint16_t flags, const char *message) { - batadv_tt_local_event(bat_priv, tt_local_entry->common.addr, - tt_local_entry->common.flags | flags); + batadv_tt_local_event(bat_priv, tt_local_entry, flags); /* The local client has to be marked as "pending to be removed" but has * to be kept in the table in order to send it in a full table @@ -584,8 +592,7 @@ uint16_t batadv_tt_local_remove(struct batadv_priv *bat_priv, /* if this client has been added right now, it is possible to * immediately purge it */ - batadv_tt_local_event(bat_priv, tt_local_entry->common.addr, - curr_flags | BATADV_TT_CLIENT_DEL); + batadv_tt_local_event(bat_priv, tt_local_entry, BATADV_TT_CLIENT_DEL); hlist_del_rcu(&tt_local_entry->common.hash_entry); batadv_tt_local_entry_free_ref(tt_local_entry); -- cgit v1.2.3 From 41ab6c4891ed4cdd855ae569924acb1da424a614 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Tue, 2 Apr 2013 22:28:44 +0200 Subject: batman-adv: don't deal with NET_IP_ALIGN manually Instead of dealing with NET_IP_ALIGN during allocation and headroom reservation, it is possible to use netdev_alloc_skb_ip_align() which transparently allocate and reserve the correct amount of data Signed-off-by: Antonio Quartulli Signed-off-by: Marek Lindner --- net/batman-adv/bat_iv_ogm.c | 6 +++--- net/batman-adv/icmp_socket.c | 4 ++-- net/batman-adv/translation-table.c | 20 ++++++++++---------- net/batman-adv/vis.c | 12 ++++++------ 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index 42b7a94d61b3..5b0a043c47c8 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -453,16 +453,16 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff, else skb_size = packet_len; - skb_size += ETH_HLEN + NET_IP_ALIGN; + skb_size += ETH_HLEN; - forw_packet_aggr->skb = dev_alloc_skb(skb_size); + forw_packet_aggr->skb = netdev_alloc_skb_ip_align(NULL, skb_size); if (!forw_packet_aggr->skb) { if (!own_packet) atomic_inc(&bat_priv->batman_queue_left); kfree(forw_packet_aggr); goto out; } - skb_reserve(forw_packet_aggr->skb, ETH_HLEN + NET_IP_ALIGN); + skb_reserve(forw_packet_aggr->skb, ETH_HLEN); INIT_HLIST_NODE(&forw_packet_aggr->list); diff --git a/net/batman-adv/icmp_socket.c b/net/batman-adv/icmp_socket.c index 0ba6c899b2d3..b27508b8085c 100644 --- a/net/batman-adv/icmp_socket.c +++ b/net/batman-adv/icmp_socket.c @@ -177,13 +177,13 @@ static ssize_t batadv_socket_write(struct file *file, const char __user *buff, if (len >= sizeof(struct batadv_icmp_packet_rr)) packet_len = sizeof(struct batadv_icmp_packet_rr); - skb = dev_alloc_skb(packet_len + ETH_HLEN + NET_IP_ALIGN); + skb = netdev_alloc_skb_ip_align(NULL, packet_len + ETH_HLEN); if (!skb) { len = -ENOMEM; goto out; } - skb_reserve(skb, ETH_HLEN + NET_IP_ALIGN); + skb_reserve(skb, ETH_HLEN); icmp_packet = (struct batadv_icmp_packet_rr *)skb_put(skb, packet_len); if (copy_from_user(icmp_packet, buff, packet_len)) { diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index d35b73904e04..52808c4ae081 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -1607,11 +1607,11 @@ batadv_tt_response_fill_table(uint16_t tt_len, uint8_t ttvn, tt_tot = tt_len / sizeof(struct batadv_tt_change); len = tt_query_size + tt_len; - skb = dev_alloc_skb(len + ETH_HLEN + NET_IP_ALIGN); + skb = netdev_alloc_skb_ip_align(NULL, len + ETH_HLEN); if (!skb) goto out; - skb_reserve(skb, ETH_HLEN + NET_IP_ALIGN); + skb_reserve(skb, ETH_HLEN); tt_response = (struct batadv_tt_query_packet *)skb_put(skb, len); tt_response->ttvn = ttvn; @@ -1672,11 +1672,11 @@ static int batadv_send_tt_request(struct batadv_priv *bat_priv, if (!tt_req_node) goto out; - skb = dev_alloc_skb(sizeof(*tt_request) + ETH_HLEN + NET_IP_ALIGN); + skb = netdev_alloc_skb_ip_align(NULL, sizeof(*tt_request) + ETH_HLEN); if (!skb) goto out; - skb_reserve(skb, ETH_HLEN + NET_IP_ALIGN); + skb_reserve(skb, ETH_HLEN); tt_req_len = sizeof(*tt_request); tt_request = (struct batadv_tt_query_packet *)skb_put(skb, tt_req_len); @@ -1769,11 +1769,11 @@ batadv_send_other_tt_response(struct batadv_priv *bat_priv, tt_tot = tt_len / sizeof(struct batadv_tt_change); len = sizeof(*tt_response) + tt_len; - skb = dev_alloc_skb(len + ETH_HLEN + NET_IP_ALIGN); + skb = netdev_alloc_skb_ip_align(NULL, len + ETH_HLEN); if (!skb) goto unlock; - skb_reserve(skb, ETH_HLEN + NET_IP_ALIGN); + skb_reserve(skb, ETH_HLEN); packet_pos = skb_put(skb, len); tt_response = (struct batadv_tt_query_packet *)packet_pos; tt_response->ttvn = req_ttvn; @@ -1885,11 +1885,11 @@ batadv_send_my_tt_response(struct batadv_priv *bat_priv, tt_tot = tt_len / sizeof(struct batadv_tt_change); len = sizeof(*tt_response) + tt_len; - skb = dev_alloc_skb(len + ETH_HLEN + NET_IP_ALIGN); + skb = netdev_alloc_skb_ip_align(NULL, len + ETH_HLEN); if (!skb) goto unlock; - skb_reserve(skb, ETH_HLEN + NET_IP_ALIGN); + skb_reserve(skb, ETH_HLEN); packet_pos = skb_put(skb, len); tt_response = (struct batadv_tt_query_packet *)packet_pos; tt_response->ttvn = req_ttvn; @@ -2219,11 +2219,11 @@ static void batadv_send_roam_adv(struct batadv_priv *bat_priv, uint8_t *client, if (!batadv_tt_check_roam_count(bat_priv, client)) goto out; - skb = dev_alloc_skb(sizeof(*roam_adv_packet) + ETH_HLEN + NET_IP_ALIGN); + skb = netdev_alloc_skb_ip_align(NULL, len + ETH_HLEN); if (!skb) goto out; - skb_reserve(skb, ETH_HLEN + NET_IP_ALIGN); + skb_reserve(skb, ETH_HLEN); roam_adv_packet = (struct batadv_roam_adv_packet *)skb_put(skb, len); diff --git a/net/batman-adv/vis.c b/net/batman-adv/vis.c index 1625e5793a89..94eaeb521c17 100644 --- a/net/batman-adv/vis.c +++ b/net/batman-adv/vis.c @@ -392,12 +392,12 @@ batadv_add_packet(struct batadv_priv *bat_priv, return NULL; len = sizeof(*packet) + vis_info_len; - info->skb_packet = dev_alloc_skb(len + ETH_HLEN + NET_IP_ALIGN); + info->skb_packet = netdev_alloc_skb_ip_align(NULL, len + ETH_HLEN); if (!info->skb_packet) { kfree(info); return NULL; } - skb_reserve(info->skb_packet, ETH_HLEN + NET_IP_ALIGN); + skb_reserve(info->skb_packet, ETH_HLEN); packet = (struct batadv_vis_packet *)skb_put(info->skb_packet, len); kref_init(&info->refcount); @@ -854,13 +854,13 @@ int batadv_vis_init(struct batadv_priv *bat_priv) if (!bat_priv->vis.my_info) goto err; - len = sizeof(*packet) + BATADV_MAX_VIS_PACKET_SIZE; - len += ETH_HLEN + NET_IP_ALIGN; - bat_priv->vis.my_info->skb_packet = dev_alloc_skb(len); + len = sizeof(*packet) + BATADV_MAX_VIS_PACKET_SIZE + ETH_HLEN; + bat_priv->vis.my_info->skb_packet = netdev_alloc_skb_ip_align(NULL, + len); if (!bat_priv->vis.my_info->skb_packet) goto free_info; - skb_reserve(bat_priv->vis.my_info->skb_packet, ETH_HLEN + NET_IP_ALIGN); + skb_reserve(bat_priv->vis.my_info->skb_packet, ETH_HLEN); tmp_skb = bat_priv->vis.my_info->skb_packet; packet = (struct batadv_vis_packet *)skb_put(tmp_skb, sizeof(*packet)); -- cgit v1.2.3 From d4ff40f683221d46c351cd9ab61f37a6ea5d2444 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Thu, 18 Apr 2013 15:13:01 +0200 Subject: batman-adv: pass a 16bit long flag argument to tt_global_add() it may be the case that we want to store some local TT client flags in a global entry, therefore the tt_global_add needs to get a proper argument for this Signed-off-by: Antonio Quartulli Signed-off-by: Marek Lindner --- net/batman-adv/translation-table.c | 19 +++++++++++++++++-- net/batman-adv/translation-table.h | 2 +- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 52808c4ae081..e272f68e1b08 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -798,10 +798,25 @@ out: batadv_tt_orig_list_entry_free_ref(orig_entry); } -/* caller must hold orig_node refcount */ +/** + * batadv_tt_global_add - add a new TT global entry or update an existing one + * @bat_priv: the bat priv with all the soft interface information + * @orig_node: the originator announcing the client + * @tt_addr: the mac address of the non-mesh client + * @flags: TT flags that have to be set for this non-mesh client + * @ttvn: the tt version number ever announcing this non-mesh client + * + * Add a new TT global entry for the given originator. If the entry already + * exists add a new reference to the given originator (a global entry can have + * references to multiple originators) and adjust the flags attribute to reflect + * the function argument. + * If a TT local entry exists for this non-mesh client remove it. + * + * The caller must hold orig_node refcount. + */ int batadv_tt_global_add(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, - const unsigned char *tt_addr, uint8_t flags, + const unsigned char *tt_addr, uint16_t flags, uint8_t ttvn) { struct batadv_tt_global_entry *tt_global_entry; diff --git a/net/batman-adv/translation-table.h b/net/batman-adv/translation-table.h index ab8e683b402f..659a3bb759ce 100644 --- a/net/batman-adv/translation-table.h +++ b/net/batman-adv/translation-table.h @@ -33,7 +33,7 @@ void batadv_tt_global_add_orig(struct batadv_priv *bat_priv, const unsigned char *tt_buff, int tt_buff_len); int batadv_tt_global_add(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, - const unsigned char *addr, uint8_t flags, + const unsigned char *addr, uint16_t flags, uint8_t ttvn); int batadv_tt_global_seq_print_text(struct seq_file *seq, void *offset); void batadv_tt_global_del_orig(struct batadv_priv *bat_priv, -- cgit v1.2.3 From e54c77f08ec62434ac8b24e402aa7b787cf42198 Mon Sep 17 00:00:00 2001 From: Linus Lüssing Date: Fri, 19 Apr 2013 12:06:56 +0200 Subject: batman-adv: Remove unnecessary INIT_HLIST_NODE() calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There's no need to for an explicit hlist_node initialization if it is added to a list right away, like it's the case with the hlist_add_head()s here. Signed-off-by: Linus Lüssing Signed-off-by: Marek Lindner Signed-off-by: Antonio Quartulli --- net/batman-adv/bat_iv_ogm.c | 2 -- net/batman-adv/send.c | 2 -- 2 files changed, 4 deletions(-) diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index 5b0a043c47c8..d07323b3e9b8 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -464,8 +464,6 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff, } skb_reserve(forw_packet_aggr->skb, ETH_HLEN); - INIT_HLIST_NODE(&forw_packet_aggr->list); - skb_buff = skb_put(forw_packet_aggr->skb, packet_len); forw_packet_aggr->packet_len = packet_len; memcpy(skb_buff, packet_buff, packet_len); diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c index ed7072ad84e8..ce69f458a754 100644 --- a/net/batman-adv/send.c +++ b/net/batman-adv/send.c @@ -152,8 +152,6 @@ _batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv, struct batadv_forw_packet *forw_packet, unsigned long send_time) { - INIT_HLIST_NODE(&forw_packet->list); - /* add new packet to packet list */ spin_lock_bh(&bat_priv->forw_bcast_list_lock); hlist_add_head(&forw_packet->list, &bat_priv->forw_bcast_list); -- cgit v1.2.3 From aa27c31265f111ff73d948a5846a3f193376491e Mon Sep 17 00:00:00 2001 From: Marek Lindner Date: Thu, 18 Apr 2013 04:56:03 +0800 Subject: batman-adv: do not print orig nodes without nc neighbors on nc table print Signed-off-by: Marek Lindner Signed-off-by: Antonio Quartulli --- net/batman-adv/network-coding.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/net/batman-adv/network-coding.c b/net/batman-adv/network-coding.c index 0787a34609b9..22cd51fea730 100644 --- a/net/batman-adv/network-coding.c +++ b/net/batman-adv/network-coding.c @@ -1763,6 +1763,13 @@ int batadv_nc_nodes_seq_print_text(struct seq_file *seq, void *offset) /* For each orig_node in this bin */ rcu_read_lock(); hlist_for_each_entry_rcu(orig_node, head, hash_entry) { + /* no need to print the orig node if it does not have + * network coding neighbors + */ + if (list_empty(&orig_node->in_coding_list) && + list_empty(&orig_node->out_coding_list)) + continue; + seq_printf(seq, "Node: %pM\n", orig_node->orig); seq_puts(seq, " Ingoing: "); -- cgit v1.2.3 From eb2deb6b39b1597577c1635e9ebf319f1ae02213 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Fri, 19 Apr 2013 18:07:00 +0200 Subject: batman-adv: change VID semantic in the BLA code In order to make batman-adv fully vlan aware later, the semantic used for variables storing the VLAN ID values has to be changed in order to be adapted to the new one which will be used batman-adv wide. In particular, the VID has to be an "_unsigned_ short int" and its 4 MSB will be used as a flag bitfield, while the remaining 12 bits are used to store the real VID value Signed-off-by: Antonio Quartulli Signed-off-by: Marek Lindner Acked-by: Simon Wunderlich --- net/batman-adv/bridge_loop_avoidance.c | 51 ++++++++++++++++++---------------- net/batman-adv/bridge_loop_avoidance.h | 12 ++++---- net/batman-adv/main.h | 11 ++++++++ net/batman-adv/soft-interface.c | 4 +-- net/batman-adv/types.h | 4 +-- 5 files changed, 49 insertions(+), 33 deletions(-) diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c index 082189e2e40d..7354063567bd 100644 --- a/net/batman-adv/bridge_loop_avoidance.c +++ b/net/batman-adv/bridge_loop_avoidance.c @@ -180,7 +180,7 @@ static struct batadv_bla_claim */ static struct batadv_bla_backbone_gw * batadv_backbone_hash_find(struct batadv_priv *bat_priv, - uint8_t *addr, short vid) + uint8_t *addr, unsigned short vid) { struct batadv_hashtable *hash = bat_priv->bla.backbone_hash; struct hlist_head *head; @@ -257,7 +257,7 @@ batadv_bla_del_backbone_claims(struct batadv_bla_backbone_gw *backbone_gw) * @claimtype: the type of the claim (CLAIM, UNCLAIM, ANNOUNCE, ...) */ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, uint8_t *mac, - short vid, int claimtype) + unsigned short vid, int claimtype) { struct sk_buff *skb; struct ethhdr *ethhdr; @@ -335,13 +335,14 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, uint8_t *mac, memcpy(hw_src, mac, ETH_ALEN); memcpy(ethhdr->h_dest, mac, ETH_ALEN); batadv_dbg(BATADV_DBG_BLA, bat_priv, - "bla_send_claim(): REQUEST of %pM to %pMon vid %d\n", + "bla_send_claim(): REQUEST of %pM to %pM on vid %d\n", ethhdr->h_source, ethhdr->h_dest, vid); break; } - if (vid != -1) - skb = vlan_insert_tag(skb, htons(ETH_P_8021Q), vid); + if (vid & BATADV_VLAN_HAS_TAG) + skb = vlan_insert_tag(skb, htons(ETH_P_8021Q), + vid & VLAN_VID_MASK); skb_reset_mac_header(skb); skb->protocol = eth_type_trans(skb, soft_iface); @@ -367,7 +368,7 @@ out: */ static struct batadv_bla_backbone_gw * batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, uint8_t *orig, - short vid, bool own_backbone) + unsigned short vid, bool own_backbone) { struct batadv_bla_backbone_gw *entry; struct batadv_orig_node *orig_node; @@ -434,7 +435,7 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, uint8_t *orig, static void batadv_bla_update_own_backbone_gw(struct batadv_priv *bat_priv, struct batadv_hard_iface *primary_if, - short vid) + unsigned short vid) { struct batadv_bla_backbone_gw *backbone_gw; @@ -456,7 +457,7 @@ batadv_bla_update_own_backbone_gw(struct batadv_priv *bat_priv, */ static void batadv_bla_answer_request(struct batadv_priv *bat_priv, struct batadv_hard_iface *primary_if, - short vid) + unsigned short vid) { struct hlist_head *head; struct batadv_hashtable *hash; @@ -547,7 +548,7 @@ static void batadv_bla_send_announce(struct batadv_priv *bat_priv, * @backbone_gw: the backbone gateway which claims it */ static void batadv_bla_add_claim(struct batadv_priv *bat_priv, - const uint8_t *mac, const short vid, + const uint8_t *mac, const unsigned short vid, struct batadv_bla_backbone_gw *backbone_gw) { struct batadv_bla_claim *claim; @@ -611,7 +612,7 @@ claim_free_ref: * given mac address and vid. */ static void batadv_bla_del_claim(struct batadv_priv *bat_priv, - const uint8_t *mac, const short vid) + const uint8_t *mac, const unsigned short vid) { struct batadv_bla_claim search_claim, *claim; @@ -637,7 +638,7 @@ static void batadv_bla_del_claim(struct batadv_priv *bat_priv, /* check for ANNOUNCE frame, return 1 if handled */ static int batadv_handle_announce(struct batadv_priv *bat_priv, uint8_t *an_addr, uint8_t *backbone_addr, - short vid) + unsigned short vid) { struct batadv_bla_backbone_gw *backbone_gw; uint16_t crc; @@ -685,7 +686,7 @@ static int batadv_handle_announce(struct batadv_priv *bat_priv, static int batadv_handle_request(struct batadv_priv *bat_priv, struct batadv_hard_iface *primary_if, uint8_t *backbone_addr, - struct ethhdr *ethhdr, short vid) + struct ethhdr *ethhdr, unsigned short vid) { /* check for REQUEST frame */ if (!batadv_compare_eth(backbone_addr, ethhdr->h_dest)) @@ -709,7 +710,7 @@ static int batadv_handle_request(struct batadv_priv *bat_priv, static int batadv_handle_unclaim(struct batadv_priv *bat_priv, struct batadv_hard_iface *primary_if, uint8_t *backbone_addr, - uint8_t *claim_addr, short vid) + uint8_t *claim_addr, unsigned short vid) { struct batadv_bla_backbone_gw *backbone_gw; @@ -738,7 +739,7 @@ static int batadv_handle_unclaim(struct batadv_priv *bat_priv, static int batadv_handle_claim(struct batadv_priv *bat_priv, struct batadv_hard_iface *primary_if, uint8_t *backbone_addr, uint8_t *claim_addr, - short vid) + unsigned short vid) { struct batadv_bla_backbone_gw *backbone_gw; @@ -861,7 +862,7 @@ static int batadv_bla_process_claim(struct batadv_priv *bat_priv, struct batadv_bla_claim_dst *bla_dst; uint16_t proto; int headlen; - short vid = -1; + unsigned short vid = BATADV_NO_FLAGS; int ret; ethhdr = eth_hdr(skb); @@ -869,6 +870,7 @@ static int batadv_bla_process_claim(struct batadv_priv *bat_priv, if (ntohs(ethhdr->h_proto) == ETH_P_8021Q) { vhdr = (struct vlan_ethhdr *)ethhdr; vid = ntohs(vhdr->h_vlan_TCI) & VLAN_VID_MASK; + vid |= BATADV_VLAN_HAS_TAG; proto = ntohs(vhdr->h_vlan_encapsulated_proto); headlen = sizeof(*vhdr); } else { @@ -1358,7 +1360,7 @@ int batadv_bla_is_backbone_gw(struct sk_buff *skb, struct ethhdr *ethhdr; struct vlan_ethhdr *vhdr; struct batadv_bla_backbone_gw *backbone_gw; - short vid = -1; + unsigned short vid = BATADV_NO_FLAGS; if (!atomic_read(&orig_node->bat_priv->bridge_loop_avoidance)) return 0; @@ -1375,6 +1377,7 @@ int batadv_bla_is_backbone_gw(struct sk_buff *skb, vhdr = (struct vlan_ethhdr *)(skb->data + hdr_size); vid = ntohs(vhdr->h_vlan_TCI) & VLAN_VID_MASK; + vid |= BATADV_VLAN_HAS_TAG; } /* see if this originator is a backbone gw for this VLAN */ @@ -1424,8 +1427,8 @@ void batadv_bla_free(struct batadv_priv *bat_priv) * returns 1, otherwise it returns 0 and the caller shall further * process the skb. */ -int batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb, short vid, - bool is_bcast) +int batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb, + unsigned short vid, bool is_bcast) { struct ethhdr *ethhdr; struct batadv_bla_claim search_claim, *claim = NULL; @@ -1519,7 +1522,8 @@ out: * returns 1, otherwise it returns 0 and the caller shall further * process the skb. */ -int batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb, short vid) +int batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb, + unsigned short vid) { struct ethhdr *ethhdr; struct batadv_bla_claim search_claim, *claim = NULL; @@ -1623,7 +1627,7 @@ int batadv_bla_claim_table_seq_print_text(struct seq_file *seq, void *offset) hlist_for_each_entry_rcu(claim, head, hash_entry) { is_own = batadv_compare_eth(claim->backbone_gw->orig, primary_addr); - seq_printf(seq, " * %pM on % 5d by %pM [%c] (%#.4x)\n", + seq_printf(seq, " * %pM on %5d by %pM [%c] (%#.4x)\n", claim->addr, claim->vid, claim->backbone_gw->orig, (is_own ? 'x' : ' '), @@ -1676,10 +1680,9 @@ int batadv_bla_backbone_table_seq_print_text(struct seq_file *seq, void *offset) if (is_own) continue; - seq_printf(seq, - " * %pM on % 5d % 4i.%03is (%#.4x)\n", - backbone_gw->orig, backbone_gw->vid, - secs, msecs, backbone_gw->crc); + seq_printf(seq, " * %pM on %5d %4i.%03is (%#.4x)\n", + backbone_gw->orig, backbone_gw->vid, secs, + msecs, backbone_gw->crc); } rcu_read_unlock(); } diff --git a/net/batman-adv/bridge_loop_avoidance.h b/net/batman-adv/bridge_loop_avoidance.h index dea2fbc5d98d..4b102e71e5bd 100644 --- a/net/batman-adv/bridge_loop_avoidance.h +++ b/net/batman-adv/bridge_loop_avoidance.h @@ -21,9 +21,10 @@ #define _NET_BATMAN_ADV_BLA_H_ #ifdef CONFIG_BATMAN_ADV_BLA -int batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb, short vid, - bool is_bcast); -int batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb, short vid); +int batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb, + unsigned short vid, bool is_bcast); +int batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb, + unsigned short vid); int batadv_bla_is_backbone_gw(struct sk_buff *skb, struct batadv_orig_node *orig_node, int hdr_size); int batadv_bla_claim_table_seq_print_text(struct seq_file *seq, void *offset); @@ -42,13 +43,14 @@ void batadv_bla_free(struct batadv_priv *bat_priv); #else /* ifdef CONFIG_BATMAN_ADV_BLA */ static inline int batadv_bla_rx(struct batadv_priv *bat_priv, - struct sk_buff *skb, short vid, bool is_bcast) + struct sk_buff *skb, unsigned short vid, + bool is_bcast) { return 0; } static inline int batadv_bla_tx(struct batadv_priv *bat_priv, - struct sk_buff *skb, short vid) + struct sk_buff *skb, unsigned short vid) { return 0; } diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h index ea1a3bafe9c3..6f25ef29f305 100644 --- a/net/batman-adv/main.h +++ b/net/batman-adv/main.h @@ -162,6 +162,17 @@ enum batadv_uev_type { #include #include "types.h" +/** + * batadv_vlan_flags - flags for the four MSB of any vlan ID field + * @BATADV_VLAN_HAS_TAG: whether the field contains a valid vlan tag or not + */ +enum batadv_vlan_flags { + BATADV_VLAN_HAS_TAG = BIT(15), +}; + +#define BATADV_PRINT_VID(vid) (vid & BATADV_VLAN_HAS_TAG ? \ + (int)(vid & VLAN_VID_MASK) : -1) + extern char batadv_routing_algo[]; extern struct list_head batadv_hardif_list; diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index b26a6cdb934c..700d0b49742d 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -154,7 +154,7 @@ static int batadv_interface_tx(struct sk_buff *skb, 0x00, 0x00}; unsigned int header_len = 0; int data_len = skb->len, ret; - short vid __maybe_unused = -1; + unsigned short vid __maybe_unused = BATADV_NO_FLAGS; bool do_bcast = false; uint32_t seqno; unsigned long brd_delay = 1; @@ -303,7 +303,7 @@ void batadv_interface_rx(struct net_device *soft_iface, struct ethhdr *ethhdr; struct vlan_ethhdr *vhdr; struct batadv_header *batadv_header = (struct batadv_header *)skb->data; - short vid __maybe_unused = -1; + unsigned short vid __maybe_unused = BATADV_NO_FLAGS; __be16 ethertype = __constant_htons(ETH_P_BATMAN); bool is_bcast; diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 5f542bdd9a4d..b2c94e139319 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -642,7 +642,7 @@ struct batadv_socket_packet { #ifdef CONFIG_BATMAN_ADV_BLA struct batadv_bla_backbone_gw { uint8_t orig[ETH_ALEN]; - short vid; + unsigned short vid; struct hlist_node hash_entry; struct batadv_priv *bat_priv; unsigned long lasttime; @@ -665,7 +665,7 @@ struct batadv_bla_backbone_gw { */ struct batadv_bla_claim { uint8_t addr[ETH_ALEN]; - short vid; + unsigned short vid; struct batadv_bla_backbone_gw *backbone_gw; unsigned long lasttime; struct hlist_node hash_entry; -- cgit v1.2.3 From 5f80df6705fcd8153f93bd0e82109dbeb7ff535b Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Fri, 19 Apr 2013 18:07:01 +0200 Subject: batman-adv: print the VID properly Since the MSB bits of any vid variable are now used for storing flags, print the vid properly by taking the flags away and printing -1 in case of VID representing no real VLAN. Signed-off-by: Antonio Quartulli Signed-off-by: Marek Lindner --- net/batman-adv/bridge_loop_avoidance.c | 37 +++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c index 7354063567bd..e9d8e0b3c3d0 100644 --- a/net/batman-adv/bridge_loop_avoidance.c +++ b/net/batman-adv/bridge_loop_avoidance.c @@ -307,7 +307,8 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, uint8_t *mac, */ memcpy(ethhdr->h_source, mac, ETH_ALEN); batadv_dbg(BATADV_DBG_BLA, bat_priv, - "bla_send_claim(): CLAIM %pM on vid %d\n", mac, vid); + "bla_send_claim(): CLAIM %pM on vid %d\n", mac, + BATADV_PRINT_VID(vid)); break; case BATADV_CLAIM_TYPE_UNCLAIM: /* unclaim frame @@ -316,7 +317,7 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, uint8_t *mac, memcpy(hw_src, mac, ETH_ALEN); batadv_dbg(BATADV_DBG_BLA, bat_priv, "bla_send_claim(): UNCLAIM %pM on vid %d\n", mac, - vid); + BATADV_PRINT_VID(vid)); break; case BATADV_CLAIM_TYPE_ANNOUNCE: /* announcement frame @@ -325,7 +326,7 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, uint8_t *mac, memcpy(hw_src, mac, ETH_ALEN); batadv_dbg(BATADV_DBG_BLA, bat_priv, "bla_send_claim(): ANNOUNCE of %pM on vid %d\n", - ethhdr->h_source, vid); + ethhdr->h_source, BATADV_PRINT_VID(vid)); break; case BATADV_CLAIM_TYPE_REQUEST: /* request frame @@ -336,7 +337,8 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, uint8_t *mac, memcpy(ethhdr->h_dest, mac, ETH_ALEN); batadv_dbg(BATADV_DBG_BLA, bat_priv, "bla_send_claim(): REQUEST of %pM to %pM on vid %d\n", - ethhdr->h_source, ethhdr->h_dest, vid); + ethhdr->h_source, ethhdr->h_dest, + BATADV_PRINT_VID(vid)); break; } @@ -381,7 +383,7 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, uint8_t *orig, batadv_dbg(BATADV_DBG_BLA, bat_priv, "bla_get_backbone_gw(): not found (%pM, %d), creating new entry\n", - orig, vid); + orig, BATADV_PRINT_VID(vid)); entry = kzalloc(sizeof(*entry), GFP_ATOMIC); if (!entry) @@ -573,7 +575,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv, atomic_set(&claim->refcount, 2); batadv_dbg(BATADV_DBG_BLA, bat_priv, "bla_add_claim(): adding new entry %pM, vid %d to hash ...\n", - mac, vid); + mac, BATADV_PRINT_VID(vid)); hash_added = batadv_hash_add(bat_priv->bla.claim_hash, batadv_compare_claim, batadv_choose_claim, claim, @@ -592,7 +594,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv, batadv_dbg(BATADV_DBG_BLA, bat_priv, "bla_add_claim(): changing ownership for %pM, vid %d\n", - mac, vid); + mac, BATADV_PRINT_VID(vid)); claim->backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN); batadv_backbone_gw_free_ref(claim->backbone_gw); @@ -623,7 +625,7 @@ static void batadv_bla_del_claim(struct batadv_priv *bat_priv, return; batadv_dbg(BATADV_DBG_BLA, bat_priv, "bla_del_claim(): %pM, vid %d\n", - mac, vid); + mac, BATADV_PRINT_VID(vid)); batadv_hash_remove(bat_priv->bla.claim_hash, batadv_compare_claim, batadv_choose_claim, claim); @@ -659,12 +661,13 @@ static int batadv_handle_announce(struct batadv_priv *bat_priv, batadv_dbg(BATADV_DBG_BLA, bat_priv, "handle_announce(): ANNOUNCE vid %d (sent by %pM)... CRC = %#.4x\n", - vid, backbone_gw->orig, crc); + BATADV_PRINT_VID(vid), backbone_gw->orig, crc); if (backbone_gw->crc != crc) { batadv_dbg(BATADV_DBG_BLA, backbone_gw->bat_priv, "handle_announce(): CRC FAILED for %pM/%d (my = %#.4x, sent = %#.4x)\n", - backbone_gw->orig, backbone_gw->vid, + backbone_gw->orig, + BATADV_PRINT_VID(backbone_gw->vid), backbone_gw->crc, crc); batadv_bla_send_request(backbone_gw); @@ -700,7 +703,7 @@ static int batadv_handle_request(struct batadv_priv *bat_priv, batadv_dbg(BATADV_DBG_BLA, bat_priv, "handle_request(): REQUEST vid %d (sent by %pM)...\n", - vid, ethhdr->h_source); + BATADV_PRINT_VID(vid), ethhdr->h_source); batadv_bla_answer_request(bat_priv, primary_if, vid); return 1; @@ -728,7 +731,7 @@ static int batadv_handle_unclaim(struct batadv_priv *bat_priv, /* this must be an UNCLAIM frame */ batadv_dbg(BATADV_DBG_BLA, bat_priv, "handle_unclaim(): UNCLAIM %pM on vid %d (sent by %pM)...\n", - claim_addr, vid, backbone_gw->orig); + claim_addr, BATADV_PRINT_VID(vid), backbone_gw->orig); batadv_bla_del_claim(bat_priv, claim_addr, vid); batadv_backbone_gw_free_ref(backbone_gw); @@ -912,7 +915,8 @@ static int batadv_bla_process_claim(struct batadv_priv *bat_priv, if (ret == 1) batadv_dbg(BATADV_DBG_BLA, bat_priv, "bla_process_claim(): received a claim frame from another group. From: %pM on vid %d ...(hw_src %pM, hw_dst %pM)\n", - ethhdr->h_source, vid, hw_src, hw_dst); + ethhdr->h_source, BATADV_PRINT_VID(vid), hw_src, + hw_dst); if (ret < 2) return ret; @@ -947,7 +951,7 @@ static int batadv_bla_process_claim(struct batadv_priv *bat_priv, batadv_dbg(BATADV_DBG_BLA, bat_priv, "bla_process_claim(): ERROR - this looks like a claim frame, but is useless. eth src %pM on vid %d ...(hw_src %pM, hw_dst %pM)\n", - ethhdr->h_source, vid, hw_src, hw_dst); + ethhdr->h_source, BATADV_PRINT_VID(vid), hw_src, hw_dst); return 1; } @@ -1628,7 +1632,7 @@ int batadv_bla_claim_table_seq_print_text(struct seq_file *seq, void *offset) is_own = batadv_compare_eth(claim->backbone_gw->orig, primary_addr); seq_printf(seq, " * %pM on %5d by %pM [%c] (%#.4x)\n", - claim->addr, claim->vid, + claim->addr, BATADV_PRINT_VID(claim->vid), claim->backbone_gw->orig, (is_own ? 'x' : ' '), claim->backbone_gw->crc); @@ -1681,7 +1685,8 @@ int batadv_bla_backbone_table_seq_print_text(struct seq_file *seq, void *offset) continue; seq_printf(seq, " * %pM on %5d %4i.%03is (%#.4x)\n", - backbone_gw->orig, backbone_gw->vid, secs, + backbone_gw->orig, + BATADV_PRINT_VID(backbone_gw->vid), secs, msecs, backbone_gw->crc); } rcu_read_unlock(); -- cgit v1.2.3 From e91ecfc64ad691176be119e627e36cec8564f44b Mon Sep 17 00:00:00 2001 From: Martin Hundebøll Date: Sat, 20 Apr 2013 13:54:39 +0200 Subject: batman-adv: Move call to batadv_nc_skb_forward() from routing.c to send.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The call to batadv_nc_skb_forward() fits better in batadv_send_skb_to_orig(), as this is where the actual next hop is looked up. To let the caller of batadv_send_skb_to_orig() know wether the skb is transmitted, buffered or failed, the return value is changed from boolean to int. Signed-off-by: Martin Hundebøll Signed-off-by: Marek Lindner Signed-off-by: Antonio Quartulli --- net/batman-adv/network-coding.c | 5 ++--- net/batman-adv/network-coding.h | 6 ++---- net/batman-adv/routing.c | 23 +++++++++++++---------- net/batman-adv/send.c | 27 +++++++++++++++++++-------- net/batman-adv/send.h | 6 +++--- net/batman-adv/translation-table.c | 12 +++++++----- net/batman-adv/unicast.c | 2 +- net/batman-adv/vis.c | 7 ++++--- 8 files changed, 51 insertions(+), 37 deletions(-) diff --git a/net/batman-adv/network-coding.c b/net/batman-adv/network-coding.c index 22cd51fea730..a487d46e0aec 100644 --- a/net/batman-adv/network-coding.c +++ b/net/batman-adv/network-coding.c @@ -1359,18 +1359,17 @@ static bool batadv_nc_skb_add_to_path(struct sk_buff *skb, * buffer * @skb: data skb to forward * @neigh_node: next hop to forward packet to - * @ethhdr: pointer to the ethernet header inside the skb * * Returns true if the skb was consumed (encoded packet sent) or false otherwise */ bool batadv_nc_skb_forward(struct sk_buff *skb, - struct batadv_neigh_node *neigh_node, - struct ethhdr *ethhdr) + struct batadv_neigh_node *neigh_node) { const struct net_device *netdev = neigh_node->if_incoming->soft_iface; struct batadv_priv *bat_priv = netdev_priv(netdev); struct batadv_unicast_packet *packet; struct batadv_nc_path *nc_path; + struct ethhdr *ethhdr = eth_hdr(skb); __be32 packet_id; u8 *payload; diff --git a/net/batman-adv/network-coding.h b/net/batman-adv/network-coding.h index 4fa6d0caddbd..85a4ec81ad50 100644 --- a/net/batman-adv/network-coding.h +++ b/net/batman-adv/network-coding.h @@ -36,8 +36,7 @@ void batadv_nc_purge_orig(struct batadv_priv *bat_priv, void batadv_nc_init_bat_priv(struct batadv_priv *bat_priv); void batadv_nc_init_orig(struct batadv_orig_node *orig_node); bool batadv_nc_skb_forward(struct sk_buff *skb, - struct batadv_neigh_node *neigh_node, - struct ethhdr *ethhdr); + struct batadv_neigh_node *neigh_node); void batadv_nc_skb_store_for_decoding(struct batadv_priv *bat_priv, struct sk_buff *skb); void batadv_nc_skb_store_sniffed_unicast(struct batadv_priv *bat_priv, @@ -87,8 +86,7 @@ static inline void batadv_nc_init_orig(struct batadv_orig_node *orig_node) } static inline bool batadv_nc_skb_forward(struct sk_buff *skb, - struct batadv_neigh_node *neigh_node, - struct ethhdr *ethhdr) + struct batadv_neigh_node *neigh_node) { return false; } diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index fad08469767d..2f0bd3ffe6e8 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -285,7 +285,7 @@ static int batadv_recv_my_icmp_packet(struct batadv_priv *bat_priv, icmp_packet->msg_type = BATADV_ECHO_REPLY; icmp_packet->header.ttl = BATADV_TTL; - if (batadv_send_skb_to_orig(skb, orig_node, NULL)) + if (batadv_send_skb_to_orig(skb, orig_node, NULL) != NET_XMIT_DROP) ret = NET_RX_SUCCESS; out: @@ -333,7 +333,7 @@ static int batadv_recv_icmp_ttl_exceeded(struct batadv_priv *bat_priv, icmp_packet->msg_type = BATADV_TTL_EXCEEDED; icmp_packet->header.ttl = BATADV_TTL; - if (batadv_send_skb_to_orig(skb, orig_node, NULL)) + if (batadv_send_skb_to_orig(skb, orig_node, NULL) != NET_XMIT_DROP) ret = NET_RX_SUCCESS; out: @@ -410,7 +410,7 @@ int batadv_recv_icmp_packet(struct sk_buff *skb, icmp_packet->header.ttl--; /* route it */ - if (batadv_send_skb_to_orig(skb, orig_node, recv_if)) + if (batadv_send_skb_to_orig(skb, orig_node, recv_if) != NET_XMIT_DROP) ret = NET_RX_SUCCESS; out: @@ -775,7 +775,7 @@ static int batadv_route_unicast_packet(struct sk_buff *skb, struct batadv_neigh_node *neigh_node = NULL; struct batadv_unicast_packet *unicast_packet; struct ethhdr *ethhdr = eth_hdr(skb); - int ret = NET_RX_DROP; + int res, ret = NET_RX_DROP; struct sk_buff *new_skb; unicast_packet = (struct batadv_unicast_packet *)skb->data; @@ -835,16 +835,19 @@ static int batadv_route_unicast_packet(struct sk_buff *skb, /* decrement ttl */ unicast_packet->header.ttl--; - /* network code packet if possible */ - if (batadv_nc_skb_forward(skb, neigh_node, ethhdr)) { - ret = NET_RX_SUCCESS; - } else if (batadv_send_skb_to_orig(skb, orig_node, recv_if)) { - ret = NET_RX_SUCCESS; + res = batadv_send_skb_to_orig(skb, orig_node, recv_if); - /* Update stats counter */ + /* translate transmit result into receive result */ + if (res == NET_XMIT_SUCCESS) { + /* skb was transmitted and consumed */ batadv_inc_counter(bat_priv, BATADV_CNT_FORWARD); batadv_add_counter(bat_priv, BATADV_CNT_FORWARD_BYTES, skb->len + ETH_HLEN); + + ret = NET_RX_SUCCESS; + } else if (res == NET_XMIT_POLICED) { + /* skb was buffered and consumed */ + ret = NET_RX_SUCCESS; } out: diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c index ce69f458a754..e9ff8d801201 100644 --- a/net/batman-adv/send.c +++ b/net/batman-adv/send.c @@ -96,26 +96,37 @@ send_skb_err: * host, NULL can be passed as recv_if and no interface alternating is * attempted. * - * Returns TRUE on success; FALSE otherwise. + * Returns NET_XMIT_SUCCESS on success, NET_XMIT_DROP on failure, or + * NET_XMIT_POLICED if the skb is buffered for later transmit. */ -bool batadv_send_skb_to_orig(struct sk_buff *skb, - struct batadv_orig_node *orig_node, - struct batadv_hard_iface *recv_if) +int batadv_send_skb_to_orig(struct sk_buff *skb, + struct batadv_orig_node *orig_node, + struct batadv_hard_iface *recv_if) { struct batadv_priv *bat_priv = orig_node->bat_priv; struct batadv_neigh_node *neigh_node; + int ret = NET_XMIT_DROP; /* batadv_find_router() increases neigh_nodes refcount if found. */ neigh_node = batadv_find_router(bat_priv, orig_node, recv_if); if (!neigh_node) - return false; + return ret; - /* route it */ - batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); + /* try to network code the packet, if it is received on an interface + * (i.e. being forwarded). If the packet originates from this node or if + * network coding fails, then send the packet as usual. + */ + if (recv_if && batadv_nc_skb_forward(skb, neigh_node)) { + ret = NET_XMIT_POLICED; + } else { + batadv_send_skb_packet(skb, neigh_node->if_incoming, + neigh_node->addr); + ret = NET_XMIT_SUCCESS; + } batadv_neigh_node_free_ref(neigh_node); - return true; + return ret; } void batadv_schedule_bat_ogm(struct batadv_hard_iface *hard_iface) diff --git a/net/batman-adv/send.h b/net/batman-adv/send.h index 38e662f619ac..e7b17880fca4 100644 --- a/net/batman-adv/send.h +++ b/net/batman-adv/send.h @@ -23,9 +23,9 @@ int batadv_send_skb_packet(struct sk_buff *skb, struct batadv_hard_iface *hard_iface, const uint8_t *dst_addr); -bool batadv_send_skb_to_orig(struct sk_buff *skb, - struct batadv_orig_node *orig_node, - struct batadv_hard_iface *recv_if); +int batadv_send_skb_to_orig(struct sk_buff *skb, + struct batadv_orig_node *orig_node, + struct batadv_hard_iface *recv_if); void batadv_schedule_bat_ogm(struct batadv_hard_iface *hard_iface); int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv, const struct sk_buff *skb, diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index e272f68e1b08..429aeef3d8b2 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -1713,7 +1713,7 @@ static int batadv_send_tt_request(struct batadv_priv *bat_priv, batadv_inc_counter(bat_priv, BATADV_CNT_TT_REQUEST_TX); - if (batadv_send_skb_to_orig(skb, dst_orig_node, NULL)) + if (batadv_send_skb_to_orig(skb, dst_orig_node, NULL) != NET_XMIT_DROP) ret = 0; out: @@ -1737,7 +1737,7 @@ batadv_send_other_tt_response(struct batadv_priv *bat_priv, struct batadv_orig_node *req_dst_orig_node; struct batadv_orig_node *res_dst_orig_node = NULL; uint8_t orig_ttvn, req_ttvn, ttvn; - int ret = false; + int res, ret = false; unsigned char *tt_buff; bool full_table; uint16_t tt_len, tt_tot; @@ -1832,8 +1832,10 @@ batadv_send_other_tt_response(struct batadv_priv *bat_priv, batadv_inc_counter(bat_priv, BATADV_CNT_TT_RESPONSE_TX); - if (batadv_send_skb_to_orig(skb, res_dst_orig_node, NULL)) + res = batadv_send_skb_to_orig(skb, res_dst_orig_node, NULL); + if (res != NET_XMIT_DROP) ret = true; + goto out; unlock: @@ -1947,7 +1949,7 @@ batadv_send_my_tt_response(struct batadv_priv *bat_priv, batadv_inc_counter(bat_priv, BATADV_CNT_TT_RESPONSE_TX); - if (batadv_send_skb_to_orig(skb, orig_node, NULL)) + if (batadv_send_skb_to_orig(skb, orig_node, NULL) != NET_XMIT_DROP) ret = true; goto out; @@ -2260,7 +2262,7 @@ static void batadv_send_roam_adv(struct batadv_priv *bat_priv, uint8_t *client, batadv_inc_counter(bat_priv, BATADV_CNT_TT_ROAM_ADV_TX); - if (batadv_send_skb_to_orig(skb, orig_node, NULL)) + if (batadv_send_skb_to_orig(skb, orig_node, NULL) != NET_XMIT_DROP) ret = 0; out: diff --git a/net/batman-adv/unicast.c b/net/batman-adv/unicast.c index 0bb3b5982f94..dc8b5d4dd636 100644 --- a/net/batman-adv/unicast.c +++ b/net/batman-adv/unicast.c @@ -464,7 +464,7 @@ find_router: goto out; } - if (batadv_send_skb_to_orig(skb, orig_node, NULL)) + if (batadv_send_skb_to_orig(skb, orig_node, NULL) != NET_XMIT_DROP) ret = 0; out: diff --git a/net/batman-adv/vis.c b/net/batman-adv/vis.c index 94eaeb521c17..4983340f1943 100644 --- a/net/batman-adv/vis.c +++ b/net/batman-adv/vis.c @@ -697,7 +697,7 @@ static void batadv_broadcast_vis_packet(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node; struct batadv_vis_packet *packet; struct sk_buff *skb; - uint32_t i; + uint32_t i, res; packet = (struct batadv_vis_packet *)info->skb_packet->data; @@ -724,7 +724,8 @@ static void batadv_broadcast_vis_packet(struct batadv_priv *bat_priv, if (!skb) continue; - if (!batadv_send_skb_to_orig(skb, orig_node, NULL)) + res = batadv_send_skb_to_orig(skb, orig_node, NULL); + if (res == NET_XMIT_DROP) kfree_skb(skb); } rcu_read_unlock(); @@ -748,7 +749,7 @@ static void batadv_unicast_vis_packet(struct batadv_priv *bat_priv, if (!skb) goto out; - if (!batadv_send_skb_to_orig(skb, orig_node, NULL)) + if (batadv_send_skb_to_orig(skb, orig_node, NULL) == NET_XMIT_DROP) kfree_skb(skb); out: -- cgit v1.2.3 From 6715fd3f0538e805b6a769d66823ec16b8b647ac Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Sat, 20 Apr 2013 17:15:09 +0200 Subject: batman-adv: Start new development cycle Signed-off-by: Simon Wunderlich Signed-off-by: Antonio Quartulli --- net/batman-adv/main.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h index 6f25ef29f305..5e9aebb7d56b 100644 --- a/net/batman-adv/main.h +++ b/net/batman-adv/main.h @@ -26,7 +26,7 @@ #define BATADV_DRIVER_DEVICE "batman-adv" #ifndef BATADV_SOURCE_VERSION -#define BATADV_SOURCE_VERSION "2013.2.0" +#define BATADV_SOURCE_VERSION "2013.3.0" #endif /* B.A.T.M.A.N. parameters */ -- cgit v1.2.3 From 75538c2b85cf22eb9af6adfaf26ed7219025adeb Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Wed, 29 May 2013 11:30:50 +0800 Subject: net: always pass struct netdev_notifier_info to netdevice notifiers commit 351638e7deeed2ec8ce451b53d3 (net: pass info struct via netdevice notifier) breaks booting of my KVM guest, this is due to we still forget to pass struct netdev_notifier_info in several places. This patch completes it. Cc: Jiri Pirko Cc: David S. Miller Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- include/linux/netdevice.h | 6 ++++++ net/atm/clip.c | 4 +++- net/core/dev.c | 6 ------ net/ipv4/netfilter/ipt_MASQUERADE.c | 5 ++++- net/ipv6/addrconf.c | 7 +++++-- net/ipv6/netfilter/ip6t_MASQUERADE.c | 4 +++- 6 files changed, 21 insertions(+), 11 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 850271809a9e..8f967e34142b 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1609,6 +1609,12 @@ struct netdev_notifier_change_info { unsigned int flags_changed; }; +static inline void netdev_notifier_info_init(struct netdev_notifier_info *info, + struct net_device *dev) +{ + info->dev = dev; +} + static inline struct net_device * netdev_notifier_info_to_dev(const struct netdev_notifier_info *info) { diff --git a/net/atm/clip.c b/net/atm/clip.c index cce241eb01d9..8215f7cb170b 100644 --- a/net/atm/clip.c +++ b/net/atm/clip.c @@ -575,6 +575,7 @@ static int clip_inet_event(struct notifier_block *this, unsigned long event, void *ifa) { struct in_device *in_dev; + struct netdev_notifier_info info; in_dev = ((struct in_ifaddr *)ifa)->ifa_dev; /* @@ -583,7 +584,8 @@ static int clip_inet_event(struct notifier_block *this, unsigned long event, */ if (event != NETDEV_UP) return NOTIFY_DONE; - return clip_device_event(this, NETDEV_CHANGE, in_dev->dev); + netdev_notifier_info_init(&info, in_dev->dev); + return clip_device_event(this, NETDEV_CHANGE, &info); } static struct notifier_block clip_dev_notifier = { diff --git a/net/core/dev.c b/net/core/dev.c index 6eb621cc3b81..b2e9057be3bf 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1391,12 +1391,6 @@ void dev_disable_lro(struct net_device *dev) } EXPORT_SYMBOL(dev_disable_lro); -static void netdev_notifier_info_init(struct netdev_notifier_info *info, - struct net_device *dev) -{ - info->dev = dev; -} - static int call_netdevice_notifier(struct notifier_block *nb, unsigned long val, struct net_device *dev) { diff --git a/net/ipv4/netfilter/ipt_MASQUERADE.c b/net/ipv4/netfilter/ipt_MASQUERADE.c index dd5508bde799..30e4de940567 100644 --- a/net/ipv4/netfilter/ipt_MASQUERADE.c +++ b/net/ipv4/netfilter/ipt_MASQUERADE.c @@ -129,7 +129,10 @@ static int masq_inet_event(struct notifier_block *this, void *ptr) { struct net_device *dev = ((struct in_ifaddr *)ptr)->ifa_dev->dev; - return masq_device_event(this, event, dev); + struct netdev_notifier_info info; + + netdev_notifier_info_init(&info, dev); + return masq_device_event(this, event, &info); } static struct notifier_block masq_dev_notifier = { diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index bce073b4bbd4..7b34f06af344 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -4645,13 +4645,16 @@ int addrconf_sysctl_forward(ctl_table *ctl, int write, static void dev_disable_change(struct inet6_dev *idev) { + struct netdev_notifier_info info; + if (!idev || !idev->dev) return; + netdev_notifier_info_init(&info, idev->dev); if (idev->cnf.disable_ipv6) - addrconf_notify(NULL, NETDEV_DOWN, idev->dev); + addrconf_notify(NULL, NETDEV_DOWN, &info); else - addrconf_notify(NULL, NETDEV_UP, idev->dev); + addrconf_notify(NULL, NETDEV_UP, &info); } static void addrconf_disable_change(struct net *net, __s32 newf) diff --git a/net/ipv6/netfilter/ip6t_MASQUERADE.c b/net/ipv6/netfilter/ip6t_MASQUERADE.c index b76257cd7e1e..47bff6107519 100644 --- a/net/ipv6/netfilter/ip6t_MASQUERADE.c +++ b/net/ipv6/netfilter/ip6t_MASQUERADE.c @@ -89,8 +89,10 @@ static int masq_inet_event(struct notifier_block *this, unsigned long event, void *ptr) { struct inet6_ifaddr *ifa = ptr; + struct netdev_notifier_info info; - return masq_device_event(this, event, ifa->idev->dev); + netdev_notifier_info_init(&info, ifa->idev->dev); + return masq_device_event(this, event, &info); } static struct notifier_block masq_inet_notifier = { -- cgit v1.2.3 From 555eae97358082be3a46572464829c27b96ed8f1 Mon Sep 17 00:00:00 2001 From: Tang Yuantian Date: Tue, 9 Apr 2013 16:46:26 +0800 Subject: clk: add PowerPC corenet clock driver support This adds the clock driver for Freescale PowerPC corenet series SoCs using common clock infrastructure. Signed-off-by: Tang Yuantian Signed-off-by: Li Yang Signed-off-by: Mike Turquette --- arch/powerpc/platforms/Kconfig.cputype | 1 + drivers/clk/Kconfig | 7 + drivers/clk/Makefile | 1 + drivers/clk/clk-ppc-corenet.c | 280 +++++++++++++++++++++++++++++++++ 4 files changed, 289 insertions(+) create mode 100644 drivers/clk/clk-ppc-corenet.c diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platforms/Kconfig.cputype index 54f3936001aa..7819c40a6bc3 100644 --- a/arch/powerpc/platforms/Kconfig.cputype +++ b/arch/powerpc/platforms/Kconfig.cputype @@ -158,6 +158,7 @@ config E500 config PPC_E500MC bool "e500mc Support" select PPC_FPU + select COMMON_CLK depends on E500 help This must be enabled for running on e500mc (and derivatives diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 0357ac44638b..30fc2b48447c 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -81,6 +81,13 @@ config COMMON_CLK_AXI_CLKGEN Support for the Analog Devices axi-clkgen pcore clock generator for Xilinx FPGAs. It is commonly used in Analog Devices' reference designs. +config CLK_PPC_CORENET + bool "Clock driver for PowerPC corenet platforms" + depends on PPC_E500MC && OF + ---help--- + This adds the clock driver support for Freescale PowerPC corenet + platforms using common clock framework. + endmenu source "drivers/clk/mvebu/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 137d3e730f86..3a26115bb0f9 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -39,3 +39,4 @@ obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o +obj-$(CONFIG_CLK_PPC_CORENET) += clk-ppc-corenet.o diff --git a/drivers/clk/clk-ppc-corenet.c b/drivers/clk/clk-ppc-corenet.c new file mode 100644 index 000000000000..a2d483fd9134 --- /dev/null +++ b/drivers/clk/clk-ppc-corenet.c @@ -0,0 +1,280 @@ +/* + * Copyright 2013 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * clock driver for Freescale PowerPC corenet SoCs. + */ +#include +#include +#include +#include +#include +#include +#include + +struct cmux_clk { + struct clk_hw hw; + void __iomem *reg; + u32 flags; +}; + +#define PLL_KILL BIT(31) +#define CLKSEL_SHIFT 27 +#define CLKSEL_ADJUST BIT(0) +#define to_cmux_clk(p) container_of(p, struct cmux_clk, hw) + +static void __iomem *base; +static unsigned int clocks_per_pll; + +static int cmux_set_parent(struct clk_hw *hw, u8 idx) +{ + struct cmux_clk *clk = to_cmux_clk(hw); + u32 clksel; + + clksel = ((idx / clocks_per_pll) << 2) + idx % clocks_per_pll; + if (clk->flags & CLKSEL_ADJUST) + clksel += 8; + clksel = (clksel & 0xf) << CLKSEL_SHIFT; + iowrite32be(clksel, clk->reg); + + return 0; +} + +static u8 cmux_get_parent(struct clk_hw *hw) +{ + struct cmux_clk *clk = to_cmux_clk(hw); + u32 clksel; + + clksel = ioread32be(clk->reg); + clksel = (clksel >> CLKSEL_SHIFT) & 0xf; + if (clk->flags & CLKSEL_ADJUST) + clksel -= 8; + clksel = (clksel >> 2) * clocks_per_pll + clksel % 4; + + return clksel; +} + +const struct clk_ops cmux_ops = { + .get_parent = cmux_get_parent, + .set_parent = cmux_set_parent, +}; + +static void __init core_mux_init(struct device_node *np) +{ + struct clk *clk; + struct clk_init_data init; + struct cmux_clk *cmux_clk; + struct device_node *node; + int rc, count, i; + u32 offset; + const char *clk_name; + const char **parent_names; + + rc = of_property_read_u32(np, "reg", &offset); + if (rc) { + pr_err("%s: could not get reg property\n", np->name); + return; + } + + /* get the input clock source count */ + count = of_property_count_strings(np, "clock-names"); + if (count < 0) { + pr_err("%s: get clock count error\n", np->name); + return; + } + parent_names = kzalloc((sizeof(char *) * count), GFP_KERNEL); + if (!parent_names) { + pr_err("%s: could not allocate parent_names\n", __func__); + return; + } + + for (i = 0; i < count; i++) + parent_names[i] = of_clk_get_parent_name(np, i); + + cmux_clk = kzalloc(sizeof(struct cmux_clk), GFP_KERNEL); + if (!cmux_clk) { + pr_err("%s: could not allocate cmux_clk\n", __func__); + goto err_name; + } + cmux_clk->reg = base + offset; + + node = of_find_compatible_node(NULL, NULL, "fsl,p4080-clockgen"); + if (node && (offset >= 0x80)) + cmux_clk->flags = CLKSEL_ADJUST; + + rc = of_property_read_string_index(np, "clock-output-names", + 0, &clk_name); + if (rc) { + pr_err("%s: read clock names error\n", np->name); + goto err_clk; + } + + init.name = clk_name; + init.ops = &cmux_ops; + init.parent_names = parent_names; + init.num_parents = count; + init.flags = 0; + cmux_clk->hw.init = &init; + + clk = clk_register(NULL, &cmux_clk->hw); + if (IS_ERR(clk)) { + pr_err("%s: could not register clock\n", clk_name); + goto err_clk; + } + + rc = of_clk_add_provider(np, of_clk_src_simple_get, clk); + if (rc) { + pr_err("Could not register clock provider for node:%s\n", + np->name); + goto err_clk; + } + goto err_name; + +err_clk: + kfree(cmux_clk); +err_name: + /* free *_names because they are reallocated when registered */ + kfree(parent_names); +} + +static void __init core_pll_init(struct device_node *np) +{ + u32 offset, mult; + int i, rc, count; + const char *clk_name, *parent_name; + struct clk_onecell_data *onecell_data; + struct clk **subclks; + + rc = of_property_read_u32(np, "reg", &offset); + if (rc) { + pr_err("%s: could not get reg property\n", np->name); + return; + } + + /* get the multiple of PLL */ + mult = ioread32be(base + offset); + + /* check if this PLL is disabled */ + if (mult & PLL_KILL) { + pr_debug("PLL:%s is disabled\n", np->name); + return; + } + mult = (mult >> 1) & 0x3f; + + parent_name = of_clk_get_parent_name(np, 0); + if (!parent_name) { + pr_err("PLL: %s must have a parent\n", np->name); + return; + } + + count = of_property_count_strings(np, "clock-output-names"); + if (count < 0 || count > 4) { + pr_err("%s: clock is not supported\n", np->name); + return; + } + + /* output clock number per PLL */ + clocks_per_pll = count; + + subclks = kzalloc(sizeof(struct clk *) * count, GFP_KERNEL); + if (!subclks) { + pr_err("%s: could not allocate subclks\n", __func__); + return; + } + + onecell_data = kzalloc(sizeof(struct clk_onecell_data), GFP_KERNEL); + if (!onecell_data) { + pr_err("%s: could not allocate onecell_data\n", __func__); + goto err_clks; + } + + for (i = 0; i < count; i++) { + rc = of_property_read_string_index(np, "clock-output-names", + i, &clk_name); + if (rc) { + pr_err("%s: could not get clock names\n", np->name); + goto err_cell; + } + + /* + * when count == 4, there are 4 output clocks: + * /1, /2, /3, /4 respectively + * when count < 4, there are at least 2 output clocks: + * /1, /2, (/4, if count == 3) respectively. + */ + if (count == 4) + subclks[i] = clk_register_fixed_factor(NULL, clk_name, + parent_name, 0, mult, 1 + i); + else + + subclks[i] = clk_register_fixed_factor(NULL, clk_name, + parent_name, 0, mult, 1 << i); + + if (IS_ERR(subclks[i])) { + pr_err("%s: could not register clock\n", clk_name); + goto err_cell; + } + } + + onecell_data->clks = subclks; + onecell_data->clk_num = count; + + rc = of_clk_add_provider(np, of_clk_src_onecell_get, onecell_data); + if (rc) { + pr_err("Could not register clk provider for node:%s\n", + np->name); + goto err_cell; + } + + return; +err_cell: + kfree(onecell_data); +err_clks: + kfree(subclks); +} + +static const struct of_device_id clk_match[] __initconst = { + { .compatible = "fixed-clock", .data = of_fixed_clk_setup, }, + { .compatible = "fsl,core-pll-clock", .data = core_pll_init, }, + { .compatible = "fsl,core-mux-clock", .data = core_mux_init, }, + {} +}; + +static int __init ppc_corenet_clk_probe(struct platform_device *pdev) +{ + struct device_node *np; + + np = pdev->dev.of_node; + base = of_iomap(np, 0); + if (!base) { + dev_err(&pdev->dev, "iomap error\n"); + return -ENOMEM; + } + of_clk_init(clk_match); + + return 0; +} + +static const struct of_device_id ppc_clk_ids[] __initconst = { + { .compatible = "fsl,qoriq-clockgen-1.0", }, + { .compatible = "fsl,qoriq-clockgen-2", }, + {} +}; + +static struct platform_driver ppc_corenet_clk_driver = { + .driver = { + .name = "ppc_corenet_clock", + .owner = THIS_MODULE, + .of_match_table = ppc_clk_ids, + }, + .probe = ppc_corenet_clk_probe, +}; + +static int __init ppc_corenet_clk_init(void) +{ + return platform_driver_register(&ppc_corenet_clk_driver); +} +subsys_initcall(ppc_corenet_clk_init); -- cgit v1.2.3 From 1a0483d2a4c2c5e218d415c90d1a62b3b917d34e Mon Sep 17 00:00:00 2001 From: Sebastian Hesselbarth Date: Fri, 3 May 2013 07:33:27 +0200 Subject: clk: si5351: Allow user to define disabled state for every clock output This patch adds platform data and DT bindings to allow to overwrite the stored disabled state for each clock output. Signed-off-by: Marek Belisko Signed-off-by: Sebastian Hesselbarth Signed-off-by: Mike Turquette --- .../devicetree/bindings/clock/silabs,si5351.txt | 5 ++ drivers/clk/clk-si5351.c | 74 +++++++++++++++++++++- drivers/clk/clk-si5351.h | 1 + include/linux/platform_data/si5351.h | 18 ++++++ 4 files changed, 95 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/clock/silabs,si5351.txt b/Documentation/devicetree/bindings/clock/silabs,si5351.txt index cc374651662c..66c75b2d6158 100644 --- a/Documentation/devicetree/bindings/clock/silabs,si5351.txt +++ b/Documentation/devicetree/bindings/clock/silabs,si5351.txt @@ -44,6 +44,11 @@ Optional child node properties: - silabs,multisynth-source: source pll A(0) or B(1) of corresponding multisynth divider. - silabs,pll-master: boolean, multisynth can change pll frequency. +- silabs,disable-state : clock output disable state, shall be + 0 = clock output is driven LOW when disabled + 1 = clock output is driven HIGH when disabled + 2 = clock output is FLOATING (HIGH-Z) when disabled + 3 = clock output is NEVER disabled ==Example== diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c index 892728412e9d..efc6d5e9268b 100644 --- a/drivers/clk/clk-si5351.c +++ b/drivers/clk/clk-si5351.c @@ -851,6 +851,41 @@ static int _si5351_clkout_set_drive_strength( return 0; } +static int _si5351_clkout_set_disable_state( + struct si5351_driver_data *drvdata, int num, + enum si5351_disable_state state) +{ + u8 reg = (num < 4) ? SI5351_CLK3_0_DISABLE_STATE : + SI5351_CLK7_4_DISABLE_STATE; + u8 shift = (num < 4) ? (2 * num) : (2 * (num-4)); + u8 mask = SI5351_CLK_DISABLE_STATE_MASK << shift; + u8 val; + + if (num > 8) + return -EINVAL; + + switch (state) { + case SI5351_DISABLE_LOW: + val = SI5351_CLK_DISABLE_STATE_LOW; + break; + case SI5351_DISABLE_HIGH: + val = SI5351_CLK_DISABLE_STATE_HIGH; + break; + case SI5351_DISABLE_FLOATING: + val = SI5351_CLK_DISABLE_STATE_FLOAT; + break; + case SI5351_DISABLE_NEVER: + val = SI5351_CLK_DISABLE_STATE_NEVER; + break; + default: + return 0; + } + + si5351_set_bits(drvdata, reg, mask, val << shift); + + return 0; +} + static int si5351_clkout_prepare(struct clk_hw *hw) { struct si5351_hw_data *hwdata = @@ -1225,6 +1260,33 @@ static int si5351_dt_parse(struct i2c_client *client) } } + if (!of_property_read_u32(child, "silabs,disable-state", + &val)) { + switch (val) { + case 0: + pdata->clkout[num].disable_state = + SI5351_DISABLE_LOW; + break; + case 1: + pdata->clkout[num].disable_state = + SI5351_DISABLE_HIGH; + break; + case 2: + pdata->clkout[num].disable_state = + SI5351_DISABLE_FLOATING; + break; + case 3: + pdata->clkout[num].disable_state = + SI5351_DISABLE_NEVER; + break; + default: + dev_err(&client->dev, + "invalid disable state %d for clkout %d\n", + val, num); + return -EINVAL; + } + } + if (!of_property_read_u32(child, "clock-frequency", &val)) pdata->clkout[num].rate = val; @@ -1281,9 +1343,6 @@ static int si5351_i2c_probe(struct i2c_client *client, /* Disable interrupts */ si5351_reg_write(drvdata, SI5351_INTERRUPT_MASK, 0xf0); - /* Set disabled output drivers to drive low */ - si5351_reg_write(drvdata, SI5351_CLK3_0_DISABLE_STATE, 0x00); - si5351_reg_write(drvdata, SI5351_CLK7_4_DISABLE_STATE, 0x00); /* Ensure pll select is on XTAL for Si5351A/B */ if (drvdata->variant != SI5351_VARIANT_C) si5351_set_bits(drvdata, SI5351_PLL_INPUT_SOURCE, @@ -1327,6 +1386,15 @@ static int si5351_i2c_probe(struct i2c_client *client, n, pdata->clkout[n].drive); return ret; } + + ret = _si5351_clkout_set_disable_state(drvdata, n, + pdata->clkout[n].disable_state); + if (ret) { + dev_err(&client->dev, + "failed set disable state of clkout%d to %d\n", + n, pdata->clkout[n].disable_state); + return ret; + } } /* register xtal input clock gate */ diff --git a/drivers/clk/clk-si5351.h b/drivers/clk/clk-si5351.h index af41b5080f43..c0dbf2676872 100644 --- a/drivers/clk/clk-si5351.h +++ b/drivers/clk/clk-si5351.h @@ -81,6 +81,7 @@ #define SI5351_CLK3_0_DISABLE_STATE 24 #define SI5351_CLK7_4_DISABLE_STATE 25 +#define SI5351_CLK_DISABLE_STATE_MASK 3 #define SI5351_CLK_DISABLE_STATE_LOW 0 #define SI5351_CLK_DISABLE_STATE_HIGH 1 #define SI5351_CLK_DISABLE_STATE_FLOAT 2 diff --git a/include/linux/platform_data/si5351.h b/include/linux/platform_data/si5351.h index 92dabcaf6499..54334393ab92 100644 --- a/include/linux/platform_data/si5351.h +++ b/include/linux/platform_data/si5351.h @@ -78,6 +78,23 @@ enum si5351_drive_strength { SI5351_DRIVE_8MA = 8, }; +/** + * enum si5351_disable_state - Si5351 clock output disable state + * @SI5351_DISABLE_DEFAULT: default, do not change eeprom config + * @SI5351_DISABLE_LOW: CLKx is set to a LOW state when disabled + * @SI5351_DISABLE_HIGH: CLKx is set to a HIGH state when disabled + * @SI5351_DISABLE_FLOATING: CLKx is set to a FLOATING state when + * disabled + * @SI5351_DISABLE_NEVER: CLKx is NEVER disabled + */ +enum si5351_disable_state { + SI5351_DISABLE_DEFAULT = 0, + SI5351_DISABLE_LOW, + SI5351_DISABLE_HIGH, + SI5351_DISABLE_FLOATING, + SI5351_DISABLE_NEVER, +}; + /** * struct si5351_clkout_config - Si5351 clock output configuration * @clkout: clkout number @@ -91,6 +108,7 @@ struct si5351_clkout_config { enum si5351_multisynth_src multisynth_src; enum si5351_clkout_src clkout_src; enum si5351_drive_strength drive; + enum si5351_disable_state disable_state; bool pll_master; unsigned long rate; }; -- cgit v1.2.3 From 20faa59e0f3e3e6a6afeeaa7d976debacfce4fe3 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Fri, 26 Apr 2013 10:14:52 +0200 Subject: clk: ux500: abx500-clk: rename ux500 audio codec aliases Change soc-audio related clk_register_clkdev() device names to reflect the ones actually used in current snd-soc-mop500 and ab8500-codec drivers. Cc: Ulf Hansson Signed-off-by: Fabio Baltieri Signed-off-by: Mike Turquette --- drivers/clk/ux500/abx500-clk.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/clk/ux500/abx500-clk.c b/drivers/clk/ux500/abx500-clk.c index a0fca004abc1..e7bd62cf60b3 100644 --- a/drivers/clk/ux500/abx500-clk.c +++ b/drivers/clk/ux500/abx500-clk.c @@ -45,7 +45,7 @@ static int ab8500_reg_clks(struct device *dev) CLK_IS_ROOT); clk_register_clkdev(clk, "sysclk", "ab8500-usb.0"); clk_register_clkdev(clk, "sysclk", "ab-iddet.0"); - clk_register_clkdev(clk, "sysclk", "ab85xx-codec.0"); + clk_register_clkdev(clk, "sysclk", "snd-soc-mop500.0"); clk_register_clkdev(clk, "sysclk", "shrm_bus"); /* ab8500_sysclk2 */ @@ -70,19 +70,19 @@ static int ab8500_reg_clks(struct device *dev) AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_ULPCLKREQ, AB8500_SYSULPCLKCTRL1_ULPCLKREQ, 38400000, 9000, CLK_IS_ROOT); - clk_register_clkdev(clk, "ulpclk", "ab85xx-codec.0"); + clk_register_clkdev(clk, "ulpclk", "snd-soc-mop500.0"); /* ab8500_intclk */ clk = clk_reg_sysctrl_set_parent(dev , "intclk", intclk_parents, 2, intclk_reg_sel, intclk_reg_mask, intclk_reg_bits, 0); - clk_register_clkdev(clk, "intclk", "ab85xx-codec.0"); + clk_register_clkdev(clk, "intclk", "snd-soc-mop500.0"); clk_register_clkdev(clk, NULL, "ab8500-pwm.1"); /* ab8500_audioclk */ clk = clk_reg_sysctrl_gate(dev , "audioclk", "intclk", AB8500_SYSULPCLKCTRL1, AB8500_SYSULPCLKCTRL1_AUDIOCLKENA, AB8500_SYSULPCLKCTRL1_AUDIOCLKENA, 0, 0); - clk_register_clkdev(clk, "audioclk", "ab85xx-codec.0"); + clk_register_clkdev(clk, "audioclk", "ab8500-codec.0"); return 0; } -- cgit v1.2.3 From 4f985b4c800e824cd4fdde00c9575dd573a7b933 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 30 Apr 2013 11:56:22 +0200 Subject: clk: sun5i: Add compatibles for Allwinner A13 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The A13 has a lot less clocks than the one found in the Allwinner A10. Add these stripped down clocks to the clock driver and in the documentation. Signed-off-by: Maxime Ripard Acked-by: Emilio López Signed-off-by: Mike Turquette --- Documentation/devicetree/bindings/clock/sunxi.txt | 117 +++------------------ .../bindings/clock/sunxi/sun4i-a10-gates.txt | 93 ++++++++++++++++ .../bindings/clock/sunxi/sun5i-a13-gates.txt | 58 ++++++++++ drivers/clk/sunxi/clk-sunxi.c | 31 ++++-- 4 files changed, 187 insertions(+), 112 deletions(-) create mode 100644 Documentation/devicetree/bindings/clock/sunxi/sun4i-a10-gates.txt create mode 100644 Documentation/devicetree/bindings/clock/sunxi/sun5i-a13-gates.txt diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt index 729f52426fe1..d495521a79d2 100644 --- a/Documentation/devicetree/bindings/clock/sunxi.txt +++ b/Documentation/devicetree/bindings/clock/sunxi.txt @@ -12,22 +12,30 @@ Required properties: "allwinner,sun4i-axi-clk" - for the AXI clock "allwinner,sun4i-axi-gates-clk" - for the AXI gates "allwinner,sun4i-ahb-clk" - for the AHB clock - "allwinner,sun4i-ahb-gates-clk" - for the AHB gates + "allwinner,sun4i-ahb-gates-clk" - for the AHB gates on A10 + "allwinner,sun5i-a13-ahb-gates-clk" - for the AHB gates on A13 "allwinner,sun4i-apb0-clk" - for the APB0 clock - "allwinner,sun4i-apb0-gates-clk" - for the APB0 gates + "allwinner,sun4i-apb0-gates-clk" - for the APB0 gates on A10 + "allwinner,sun5i-a13-apb0-gates-clk" - for the APB0 gates on A13 "allwinner,sun4i-apb1-clk" - for the APB1 clock "allwinner,sun4i-apb1-mux-clk" - for the APB1 clock muxing - "allwinner,sun4i-apb1-gates-clk" - for the APB1 gates + "allwinner,sun4i-apb1-gates-clk" - for the APB1 gates on A10 + "allwinner,sun5i-a13-apb1-gates-clk" - for the APB1 gates on A13 Required properties for all clocks: - reg : shall be the control register address for the clock. - clocks : shall be the input parent clock(s) phandle for the clock - #clock-cells : from common clock binding; shall be set to 0 except for - "allwinner,sun4i-*-gates-clk" where it shall be set to 1 + "allwinner,*-gates-clk" where it shall be set to 1 -Additionally, "allwinner,sun4i-*-gates-clk" clocks require: +Additionally, "allwinner,*-gates-clk" clocks require: - clock-output-names : the corresponding gate names that the clock controls +Clock consumers should specify the desired clocks they use with a +"clocks" phandle cell. Consumers that are using a gated clock should +provide an additional ID in their clock property. The values of this +ID are documented in sunxi/-gates.txt. + For example: osc24M: osc24M@01c20050 { @@ -50,102 +58,3 @@ cpu: cpu@01c20054 { reg = <0x01c20054 0x4>; clocks = <&osc32k>, <&osc24M>, <&pll1>; }; - - - -Gate clock outputs - -The "allwinner,sun4i-*-gates-clk" clocks provide several gatable outputs; -their corresponding offsets as present on sun4i are listed below. Note that -some of these gates are not present on sun5i. - - * AXI gates ("allwinner,sun4i-axi-gates-clk") - - DRAM 0 - - * AHB gates ("allwinner,sun4i-ahb-gates-clk") - - USB0 0 - EHCI0 1 - OHCI0 2* - EHCI1 3 - OHCI1 4* - SS 5 - DMA 6 - BIST 7 - MMC0 8 - MMC1 9 - MMC2 10 - MMC3 11 - MS 12** - NAND 13 - SDRAM 14 - - ACE 16 - EMAC 17 - TS 18 - - SPI0 20 - SPI1 21 - SPI2 22 - SPI3 23 - PATA 24 - SATA 25** - GPS 26* - - VE 32 - TVD 33 - TVE0 34 - TVE1 35 - LCD0 36 - LCD1 37 - - CSI0 40 - CSI1 41 - - HDMI 43 - DE_BE0 44 - DE_BE1 45 - DE_FE0 46 - DE_FE1 47 - - MP 50 - - MALI400 52 - - * APB0 gates ("allwinner,sun4i-apb0-gates-clk") - - CODEC 0 - SPDIF 1* - AC97 2 - IIS 3 - - PIO 5 - IR0 6 - IR1 7 - - KEYPAD 10 - - * APB1 gates ("allwinner,sun4i-apb1-gates-clk") - - I2C0 0 - I2C1 1 - I2C2 2 - - CAN 4 - SCR 5 - PS20 6 - PS21 7 - - UART0 16 - UART1 17 - UART2 18 - UART3 19 - UART4 20 - UART5 21 - UART6 22 - UART7 23 - -Notation: - [*]: The datasheet didn't mention these, but they are present on AW code - [**]: The datasheet had this marked as "NC" but they are used on AW code diff --git a/Documentation/devicetree/bindings/clock/sunxi/sun4i-a10-gates.txt b/Documentation/devicetree/bindings/clock/sunxi/sun4i-a10-gates.txt new file mode 100644 index 000000000000..6a03475bbfe2 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/sunxi/sun4i-a10-gates.txt @@ -0,0 +1,93 @@ +Gate clock outputs +------------------ + + * AXI gates ("allwinner,sun4i-axi-gates-clk") + + DRAM 0 + + * AHB gates ("allwinner,sun4i-ahb-gates-clk") + + USB0 0 + EHCI0 1 + OHCI0 2* + EHCI1 3 + OHCI1 4* + SS 5 + DMA 6 + BIST 7 + MMC0 8 + MMC1 9 + MMC2 10 + MMC3 11 + MS 12** + NAND 13 + SDRAM 14 + + ACE 16 + EMAC 17 + TS 18 + + SPI0 20 + SPI1 21 + SPI2 22 + SPI3 23 + PATA 24 + SATA 25** + GPS 26* + + VE 32 + TVD 33 + TVE0 34 + TVE1 35 + LCD0 36 + LCD1 37 + + CSI0 40 + CSI1 41 + + HDMI 43 + DE_BE0 44 + DE_BE1 45 + DE_FE1 46 + DE_FE1 47 + + MP 50 + + MALI400 52 + + * APB0 gates ("allwinner,sun4i-apb0-gates-clk") + + CODEC 0 + SPDIF 1* + AC97 2 + IIS 3 + + PIO 5 + IR0 6 + IR1 7 + + KEYPAD 10 + + * APB1 gates ("allwinner,sun4i-apb1-gates-clk") + + I2C0 0 + I2C1 1 + I2C2 2 + + CAN 4 + SCR 5 + PS20 6 + PS21 7 + + UART0 16 + UART1 17 + UART2 18 + UART3 19 + UART4 20 + UART5 21 + UART6 22 + UART7 23 + +Notation: + [*]: The datasheet didn't mention these, but they are present on AW code + [**]: The datasheet had this marked as "NC" but they are used on AW code diff --git a/Documentation/devicetree/bindings/clock/sunxi/sun5i-a13-gates.txt b/Documentation/devicetree/bindings/clock/sunxi/sun5i-a13-gates.txt new file mode 100644 index 000000000000..006b6dfc4703 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/sunxi/sun5i-a13-gates.txt @@ -0,0 +1,58 @@ +Gate clock outputs +------------------ + + * AXI gates ("allwinner,sun4i-axi-gates-clk") + + DRAM 0 + + * AHB gates ("allwinner,sun5i-a13-ahb-gates-clk") + + USBOTG 0 + EHCI 1 + OHCI 2 + + SS 5 + DMA 6 + BIST 7 + MMC0 8 + MMC1 9 + MMC2 10 + + NAND 13 + SDRAM 14 + + SPI0 20 + SPI1 21 + SPI2 22 + + STIMER 28 + + VE 32 + + LCD 36 + + CSI 40 + + DE_BE 44 + + DE_FE 46 + + IEP 51 + MALI400 52 + + * APB0 gates ("allwinner,sun5i-a13-apb0-gates-clk") + + CODEC 0 + + PIO 5 + IR 6 + + * APB1 gates ("allwinner,sun5i-a13-apb1-gates-clk") + + I2C0 0 + I2C1 1 + I2C2 2 + + UART1 17 + + UART3 19 diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 8492ad1d5360..930d36fdb1c9 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -333,22 +333,34 @@ struct gates_data { DECLARE_BITMAP(mask, SUNXI_GATES_MAX_SIZE); }; -static const __initconst struct gates_data axi_gates_data = { +static const __initconst struct gates_data sun4i_axi_gates_data = { .mask = {1}, }; -static const __initconst struct gates_data ahb_gates_data = { +static const __initconst struct gates_data sun4i_ahb_gates_data = { .mask = {0x7F77FFF, 0x14FB3F}, }; -static const __initconst struct gates_data apb0_gates_data = { +static const __initconst struct gates_data sun5i_a13_ahb_gates_data = { + .mask = {0x107067e7, 0x185111}, +}; + +static const __initconst struct gates_data sun4i_apb0_gates_data = { .mask = {0x4EF}, }; -static const __initconst struct gates_data apb1_gates_data = { +static const __initconst struct gates_data sun5i_a13_apb0_gates_data = { + .mask = {0x61}, +}; + +static const __initconst struct gates_data sun4i_apb1_gates_data = { .mask = {0xFF00F7}, }; +static const __initconst struct gates_data sun5i_a13_apb1_gates_data = { + .mask = {0xa0007}, +}; + static void __init sunxi_gates_clk_setup(struct device_node *node, struct gates_data *data) { @@ -428,10 +440,13 @@ static const __initconst struct of_device_id clk_mux_match[] = { /* Matches for gate clocks */ static const __initconst struct of_device_id clk_gates_match[] = { - {.compatible = "allwinner,sun4i-axi-gates-clk", .data = &axi_gates_data,}, - {.compatible = "allwinner,sun4i-ahb-gates-clk", .data = &ahb_gates_data,}, - {.compatible = "allwinner,sun4i-apb0-gates-clk", .data = &apb0_gates_data,}, - {.compatible = "allwinner,sun4i-apb1-gates-clk", .data = &apb1_gates_data,}, + {.compatible = "allwinner,sun4i-axi-gates-clk", .data = &sun4i_axi_gates_data,}, + {.compatible = "allwinner,sun4i-ahb-gates-clk", .data = &sun4i_ahb_gates_data,}, + {.compatible = "allwinner,sun5i-a13-ahb-gates-clk", .data = &sun5i_a13_ahb_gates_data,}, + {.compatible = "allwinner,sun4i-apb0-gates-clk", .data = &sun4i_apb0_gates_data,}, + {.compatible = "allwinner,sun5i-a13-apb0-gates-clk", .data = &sun5i_a13_apb0_gates_data,}, + {.compatible = "allwinner,sun4i-apb1-gates-clk", .data = &sun4i_apb1_gates_data,}, + {.compatible = "allwinner,sun5i-a13-apb1-gates-clk", .data = &sun5i_a13_apb1_gates_data,}, {} }; -- cgit v1.2.3 From 9807362bfe1748d9bb48eecb9261f1b1aaafea1c Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Thu, 16 May 2013 17:49:16 +0200 Subject: clk: si5351: declare all device IDs for module loading When the si5351 driver is a kernel module, it is loaded into memory from its i2c device IDs, but not from its DT compatible properties. This patch declares the i2c device IDs of all chip variants. Signed-off-by: Jean-Francois Moine Acked-by: Jason Cooper Signed-off-by: Mike Turquette --- drivers/clk/clk-si5351.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c index efc6d5e9268b..e4460d3bf599 100644 --- a/drivers/clk/clk-si5351.c +++ b/drivers/clk/clk-si5351.c @@ -1558,7 +1558,10 @@ static int si5351_i2c_probe(struct i2c_client *client, } static const struct i2c_device_id si5351_i2c_ids[] = { - { "silabs,si5351", 0 }, + { "si5351a", 0 }, + { "si5351a-msop", 0 }, + { "si5351b", 0 }, + { "si5351c", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, si5351_i2c_ids); -- cgit v1.2.3 From f8aa0bd5c9b28f80bf372d0f486737b13ff01910 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Wed, 15 May 2013 21:07:24 -0700 Subject: clk: Fix race condition between clk_set_parent and clk_enable() Without this patch, the following race condition is possible. * clk-A has two parents - clk-X and clk-Y. * All three are disabled and clk-X is current parent. * Thread A: clk_set_parent(clk-A, clk-Y). * Thread A: * Thread A: Grabs enable lock. * Thread A: Sees enable count of clk-A is 0, so doesn't enable clk-Y. * Thread A: Updates clk-A SW parent to clk-Y * Thread A: Releases enable lock. * Thread B: clk_enable(clk-A). * Thread B: clk_enable() enables clk-Y, then enabled clk-A and returns. clk-A is now enabled in software, but not clocking in hardware since the hardware parent is still clk-X. The only way to avoid race conditions between clk_set_parent() and clk_enable/disable() is to ensure that clk_enable/disable() calls don't require changes to hardware enable state between changes to software clock topology and hardware clock topology. The options to achieve the above are: 1. Grab the enable lock before changing software/hardware topology and release it afterwards. 2. Keep the clock enabled for the duration of software/hardware topology change so that any additional enable/disable calls don't try to change the hardware state. Once the topology change is complete, the clock can be put back in its original enable state. Option (1) is not an acceptable solution since the set_parent() ops might need to sleep. Therefore, this patch implements option (2). This patch doesn't violate any API semantics. clk_disable() doesn't guarantee that the clock is actually disabled. So, no clients of a clock can assume that a clock is disabled after their last call to clk_disable(). So, enabling the clock during a parent change is not a violation of any API semantics. This also has the nice side effect of simplifying the error handling code. Signed-off-by: Saravana Kannan Acked-by: Ulf Hansson Signed-off-by: Mike Turquette [mturquette@linaro.org: fixed up whitespace issue] --- drivers/clk/clk.c | 89 +++++++++++++++++++++++++++---------------------------- 1 file changed, 44 insertions(+), 45 deletions(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 934cfd18f72d..399b0d8ac562 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1377,23 +1377,33 @@ static int __clk_set_parent(struct clk *clk, struct clk *parent, u8 p_index) unsigned long flags; int ret = 0; struct clk *old_parent = clk->parent; - bool migrated_enable = false; - /* migrate prepare */ - if (clk->prepare_count) + /* + * Migrate prepare state between parents and prevent race with + * clk_enable(). + * + * If the clock is not prepared, then a race with + * clk_enable/disable() is impossible since we already have the + * prepare lock (future calls to clk_enable() need to be preceded by + * a clk_prepare()). + * + * If the clock is prepared, migrate the prepared state to the new + * parent and also protect against a race with clk_enable() by + * forcing the clock and the new parent on. This ensures that all + * future calls to clk_enable() are practically NOPs with respect to + * hardware and software states. + * + * See also: Comment for clk_set_parent() below. + */ + if (clk->prepare_count) { __clk_prepare(parent); - - flags = clk_enable_lock(); - - /* migrate enable */ - if (clk->enable_count) { - __clk_enable(parent); - migrated_enable = true; + clk_enable(parent); + clk_enable(clk); } /* update the clk tree topology */ + flags = clk_enable_lock(); clk_reparent(clk, parent); - clk_enable_unlock(flags); /* change clock input source */ @@ -1401,43 +1411,27 @@ static int __clk_set_parent(struct clk *clk, struct clk *parent, u8 p_index) ret = clk->ops->set_parent(clk->hw, p_index); if (ret) { - /* - * The error handling is tricky due to that we need to release - * the spinlock while issuing the .set_parent callback. This - * means the new parent might have been enabled/disabled in - * between, which must be considered when doing rollback. - */ flags = clk_enable_lock(); - clk_reparent(clk, old_parent); - - if (migrated_enable && clk->enable_count) { - __clk_disable(parent); - } else if (migrated_enable && (clk->enable_count == 0)) { - __clk_disable(old_parent); - } else if (!migrated_enable && clk->enable_count) { - __clk_disable(parent); - __clk_enable(old_parent); - } - clk_enable_unlock(flags); - if (clk->prepare_count) + if (clk->prepare_count) { + clk_disable(clk); + clk_disable(parent); __clk_unprepare(parent); - + } return ret; } - /* clean up enable for old parent if migration was done */ - if (migrated_enable) { - flags = clk_enable_lock(); - __clk_disable(old_parent); - clk_enable_unlock(flags); - } - - /* clean up prepare for old parent if migration was done */ - if (clk->prepare_count) + /* + * Finish the migration of prepare state and undo the changes done + * for preventing a race with clk_enable(). + */ + if (clk->prepare_count) { + clk_disable(clk); + clk_disable(old_parent); __clk_unprepare(old_parent); + } /* update debugfs with new clk tree topology */ clk_debug_reparent(clk, parent); @@ -1449,12 +1443,17 @@ static int __clk_set_parent(struct clk *clk, struct clk *parent, u8 p_index) * @clk: the mux clk whose input we are switching * @parent: the new input to clk * - * Re-parent clk to use parent as it's new input source. If clk has the - * CLK_SET_PARENT_GATE flag set then clk must be gated for this - * operation to succeed. After successfully changing clk's parent - * clk_set_parent will update the clk topology, sysfs topology and - * propagate rate recalculation via __clk_recalc_rates. Returns 0 on - * success, -EERROR otherwise. + * Re-parent clk to use parent as its new input source. If clk is in + * prepared state, the clk will get enabled for the duration of this call. If + * that's not acceptable for a specific clk (Eg: the consumer can't handle + * that, the reparenting is glitchy in hardware, etc), use the + * CLK_SET_PARENT_GATE flag to allow reparenting only when clk is unprepared. + * + * After successfully changing clk's parent clk_set_parent will update the + * clk topology, sysfs topology and propagate rate recalculation via + * __clk_recalc_rates. + * + * Returns 0 on success, -EERROR otherwise. */ int clk_set_parent(struct clk *clk, struct clk *parent) { -- cgit v1.2.3 From 0b151debc31df089ddc07a3343031d8f51f988a3 Mon Sep 17 00:00:00 2001 From: Sebastian Hesselbarth Date: Wed, 1 May 2013 02:58:28 +0200 Subject: clk: add non CONFIG_OF routines for clk-provider Some drivers that are shared between architectures have HAVE_CLK selected but don't have OF. To remove compilation errors for drivers that provide clocks on DT with of_clk_add_provider we would have to enclose these calls within #ifdef CONFIG_OF, #endif. This patch adds some stubs for OF related clk-provider functions that either do nothing or return appropriate values if CONFIG_OF is not set. So, definition of these routines will always be available. Signed-off-by: Sebastian Hesselbarth Acked-by: Arnd Bergmann Signed-off-by: Mike Turquette --- include/linux/clk-provider.h | 47 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 11860985fecb..265f384f1e01 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -423,6 +423,17 @@ struct of_device_id; typedef void (*of_clk_init_cb_t)(struct device_node *); +struct clk_onecell_data { + struct clk **clks; + unsigned int clk_num; +}; + +#define CLK_OF_DECLARE(name, compat, fn) \ + static const struct of_device_id __clk_of_table_##name \ + __used __section(__clk_of_table) \ + = { .compatible = compat, .data = fn }; + +#ifdef CONFIG_OF int of_clk_add_provider(struct device_node *np, struct clk *(*clk_src_get)(struct of_phandle_args *args, void *data), @@ -430,19 +441,39 @@ int of_clk_add_provider(struct device_node *np, void of_clk_del_provider(struct device_node *np); struct clk *of_clk_src_simple_get(struct of_phandle_args *clkspec, void *data); -struct clk_onecell_data { - struct clk **clks; - unsigned int clk_num; -}; struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data); const char *of_clk_get_parent_name(struct device_node *np, int index); void of_clk_init(const struct of_device_id *matches); -#define CLK_OF_DECLARE(name, compat, fn) \ - static const struct of_device_id __clk_of_table_##name \ - __used __section(__clk_of_table) \ - = { .compatible = compat, .data = fn }; +#else /* !CONFIG_OF */ +static inline int of_clk_add_provider(struct device_node *np, + struct clk *(*clk_src_get)(struct of_phandle_args *args, + void *data), + void *data) +{ + return 0; +} +#define of_clk_del_provider(np) \ + { while (0); } +static inline struct clk *of_clk_src_simple_get( + struct of_phandle_args *clkspec, void *data) +{ + return ERR_PTR(-ENOENT); +} +static inline struct clk *of_clk_src_onecell_get( + struct of_phandle_args *clkspec, void *data) +{ + return ERR_PTR(-ENOENT); +} +static inline const char *of_clk_get_parent_name(struct device_node *np, + int index) +{ + return NULL; +} +#define of_clk_init(matches) \ + { while (0); } +#endif /* CONFIG_OF */ #endif /* CONFIG_COMMON_CLK */ #endif /* CLK_PROVIDER_H */ -- cgit v1.2.3 From 4564dbdadc1814a9ee300b6d3c5b7179bc8a8c80 Mon Sep 17 00:00:00 2001 From: Sebastian Hesselbarth Date: Wed, 1 May 2013 02:58:29 +0200 Subject: clk: si5351: Allow to build without CONFIG_OF With of_clk_provider stubs for CONFIG_OF not set, we can now also enable clk-si5351 on those architectures. Signed-off-by: Sebastian Hesselbarth Signed-off-by: Mike Turquette --- drivers/clk/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 30fc2b48447c..185527ffbb29 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -58,7 +58,6 @@ config COMMON_CLK_MAX77686 config COMMON_CLK_SI5351 tristate "Clock driver for SiLabs 5351A/B/C" depends on I2C - depends on OF select REGMAP_I2C select RATIONAL ---help--- -- cgit v1.2.3 From 25b6e14881e7861bb720a4624dcaf5f37854fc70 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Tue, 28 May 2013 20:34:23 +0000 Subject: isdn: Correct comparison of skb->tail and skb-transport_header This corrects an regression introduced by "net: Use 16bits for *_headers fields of struct skbuff" when NET_SKBUFF_DATA_USES_OFFSET is not set. In that case skb->tail will be a pointer whereas skb->transport_header will be an offset from head. This is corrected by using wrappers that ensure that the comparison is always between pointers. Signed-off-by: Simon Horman Signed-off-by: David S. Miller --- drivers/isdn/i4l/isdn_net.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/isdn/i4l/isdn_net.c b/drivers/isdn/i4l/isdn_net.c index 88d657dff474..8b98d53d9976 100644 --- a/drivers/isdn/i4l/isdn_net.c +++ b/drivers/isdn/i4l/isdn_net.c @@ -885,7 +885,7 @@ isdn_net_log_skb(struct sk_buff *skb, isdn_net_local *lp) addinfo[0] = '\0'; /* This check stolen from 2.1.72 dev_queue_xmit_nit() */ - if (p < skb->data || skb->network_header >= skb->tail) { + if (p < skb->data || skb_network_header(skb) >= skb_tail_pointer(skb)) { /* fall back to old isdn_net_log_packet method() */ char *buf = skb->data; -- cgit v1.2.3 From be8b678c6c63010cd2b0f4f602bd3066f7827fb2 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Tue, 28 May 2013 20:34:24 +0000 Subject: cxgb3: Correct comparisons and calculations using skb->tail and skb-transport_header This corrects an regression introduced by "net: Use 16bits for *_headers fields of struct skbuff" when NET_SKBUFF_DATA_USES_OFFSET is not set. In that case skb->tail will be a pointer whereas skb->transport_header will be an offset from head. This is corrected by using wrappers that ensure that comparisons and calculations are always made using pointers. Signed-off-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb3/sge.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/chelsio/cxgb3/sge.c b/drivers/net/ethernet/chelsio/cxgb3/sge.c index 2fd773e267dc..46d1efc17527 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb3/sge.c @@ -1584,9 +1584,8 @@ static void deferred_unmap_destructor(struct sk_buff *skb) p = dui->addr; if (skb->tail - skb->transport_header) - pci_unmap_single(dui->pdev, *p++, - skb->tail - skb->transport_header, - PCI_DMA_TODEVICE); + pci_unmap_single(dui->pdev, *p++, skb_tail_pointer(skb) - + skb_transport_header(skb), PCI_DMA_TODEVICE); si = skb_shinfo(skb); for (i = 0; i < si->nr_frags; i++) @@ -1647,8 +1646,8 @@ static void write_ofld_wr(struct adapter *adap, struct sk_buff *skb, flits = skb_transport_offset(skb) / 8; sgp = ndesc == 1 ? (struct sg_ent *)&d->flit[flits] : sgl; sgl_flits = write_sgl(skb, sgp, skb_transport_header(skb), - skb->tail - skb->transport_header, - addr); + skb_tail_pointer(skb) - + skb_transport_header(skb), addr); if (need_skb_unmap()) { setup_deferred_unmapping(skb, adap->pdev, sgp, sgl_flits); skb->destructor = deferred_unmap_destructor; @@ -1674,7 +1673,7 @@ static inline unsigned int calc_tx_descs_ofld(const struct sk_buff *skb) flits = skb_transport_offset(skb) / 8; /* headers */ cnt = skb_shinfo(skb)->nr_frags; - if (skb->tail != skb->transport_header) + if (skb_tail_pointer(skb) != skb_transport_header(skb)) cnt++; return flits_to_desc(flits + sgl_len(cnt)); } -- cgit v1.2.3 From ced14f6804a979d1972415bc23f2f8ddb18595dd Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Tue, 28 May 2013 20:34:25 +0000 Subject: net: Correct comparisons and calculations using skb->tail and skb-transport_header This corrects an regression introduced by "net: Use 16bits for *_headers fields of struct skbuff" when NET_SKBUFF_DATA_USES_OFFSET is not set. In that case skb->tail will be a pointer whereas skb->transport_header will be an offset from head. This is corrected by using wrappers that ensure that comparisons and calculations are always made using pointers. Signed-off-by: Simon Horman Signed-off-by: David S. Miller --- include/net/inet_ecn.h | 6 ++++-- net/core/dev.c | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/include/net/inet_ecn.h b/include/net/inet_ecn.h index aab73757bc4d..3bd22795c3e2 100644 --- a/include/net/inet_ecn.h +++ b/include/net/inet_ecn.h @@ -134,12 +134,14 @@ static inline int INET_ECN_set_ce(struct sk_buff *skb) { switch (skb->protocol) { case cpu_to_be16(ETH_P_IP): - if (skb->network_header + sizeof(struct iphdr) <= skb->tail) + if (skb_network_header(skb) + sizeof(struct iphdr) <= + skb_tail_pointer(skb)) return IP_ECN_set_ce(ip_hdr(skb)); break; case cpu_to_be16(ETH_P_IPV6): - if (skb->network_header + sizeof(struct ipv6hdr) <= skb->tail) + if (skb_network_header(skb) + sizeof(struct ipv6hdr) <= + skb_tail_pointer(skb)) return IP6_ECN_set_ce(ipv6_hdr(skb)); break; } diff --git a/net/core/dev.c b/net/core/dev.c index b2e9057be3bf..d4d874a25e45 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1724,7 +1724,7 @@ static void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev) skb_reset_mac_header(skb2); if (skb_network_header(skb2) < skb2->data || - skb2->network_header > skb2->tail) { + skb_network_header(skb2) > skb_tail_pointer(skb2)) { net_crit_ratelimited("protocol %04x is buggy, dev %s\n", ntohs(skb2->protocol), dev->name); @@ -3892,7 +3892,7 @@ static void skb_gro_reset_offset(struct sk_buff *skb) NAPI_GRO_CB(skb)->frag0 = NULL; NAPI_GRO_CB(skb)->frag0_len = 0; - if (skb->mac_header == skb->tail && + if (skb_mac_header(skb) == skb_tail_pointer(skb) && pinfo->nr_frags && !PageHighMem(skb_frag_page(frag0))) { NAPI_GRO_CB(skb)->frag0 = skb_frag_address(frag0); -- cgit v1.2.3 From 29a3cad5c6ae9e7fbf1509d01d39c3c3c38f11f9 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Tue, 28 May 2013 20:34:26 +0000 Subject: ipv6: Correct comparisons and calculations using skb->tail and skb-transport_header This corrects an regression introduced by "net: Use 16bits for *_headers fields of struct skbuff" when NET_SKBUFF_DATA_USES_OFFSET is not set. In that case skb->tail will be a pointer whereas skb->transport_header will be an offset from head. This is corrected by using wrappers that ensure that comparisons and calculations are always made using pointers. Signed-off-by: Simon Horman Signed-off-by: David S. Miller --- net/ipv6/exthdrs_core.c | 2 +- net/ipv6/icmp.c | 2 +- net/ipv6/mcast.c | 5 +++-- net/ipv6/mip6.c | 6 ++++-- net/ipv6/ndisc.c | 9 +++++---- net/ipv6/output_core.c | 3 ++- net/ipv6/raw.c | 3 ++- net/ipv6/route.c | 2 +- 8 files changed, 19 insertions(+), 13 deletions(-) diff --git a/net/ipv6/exthdrs_core.c b/net/ipv6/exthdrs_core.c index c5e83fae4df4..140748debc4a 100644 --- a/net/ipv6/exthdrs_core.c +++ b/net/ipv6/exthdrs_core.c @@ -115,7 +115,7 @@ EXPORT_SYMBOL(ipv6_skip_exthdr); int ipv6_find_tlv(struct sk_buff *skb, int offset, int type) { const unsigned char *nh = skb_network_header(skb); - int packet_len = skb->tail - skb->network_header; + int packet_len = skb_tail_pointer(skb) - skb_network_header(skb); struct ipv6_opt_hdr *hdr; int len; diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 1d2902e61786..4b4890bbe16d 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -399,7 +399,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) int err = 0; if ((u8 *)hdr < skb->head || - (skb->network_header + sizeof(*hdr)) > skb->tail) + (skb_network_header(skb) + sizeof(*hdr)) > skb_tail_pointer(skb)) return; /* diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index bfa6cc36ef2a..72c8bfe06bb4 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -1409,8 +1409,9 @@ static void mld_sendpack(struct sk_buff *skb) idev = __in6_dev_get(skb->dev); IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len); - payload_len = (skb->tail - skb->network_header) - sizeof(*pip6); - mldlen = skb->tail - skb->transport_header; + payload_len = (skb_tail_pointer(skb) - skb_network_header(skb)) - + sizeof(*pip6); + mldlen = skb_tail_pointer(skb) - skb_transport_header(skb); pip6->payload_len = htons(payload_len); pmr->mld2r_cksum = csum_ipv6_magic(&pip6->saddr, &pip6->daddr, mldlen, diff --git a/net/ipv6/mip6.c b/net/ipv6/mip6.c index 0f9bdc5ee9f3..9ac01dc9402e 100644 --- a/net/ipv6/mip6.c +++ b/net/ipv6/mip6.c @@ -268,7 +268,8 @@ static int mip6_destopt_offset(struct xfrm_state *x, struct sk_buff *skb, struct ipv6_opt_hdr *exthdr = (struct ipv6_opt_hdr *)(ipv6_hdr(skb) + 1); const unsigned char *nh = skb_network_header(skb); - unsigned int packet_len = skb->tail - skb->network_header; + unsigned int packet_len = skb_tail_pointer(skb) - + skb_network_header(skb); int found_rhdr = 0; *nexthdr = &ipv6_hdr(skb)->nexthdr; @@ -404,7 +405,8 @@ static int mip6_rthdr_offset(struct xfrm_state *x, struct sk_buff *skb, struct ipv6_opt_hdr *exthdr = (struct ipv6_opt_hdr *)(ipv6_hdr(skb) + 1); const unsigned char *nh = skb_network_header(skb); - unsigned int packet_len = skb->tail - skb->network_header; + unsigned int packet_len = skb_tail_pointer(skb) - + skb_network_header(skb); int found_rhdr = 0; *nexthdr = &ipv6_hdr(skb)->nexthdr; diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index a0962697a257..781dd3c99680 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -693,7 +693,7 @@ static void ndisc_recv_ns(struct sk_buff *skb) const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr; const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr; u8 *lladdr = NULL; - u32 ndoptlen = skb->tail - (skb->transport_header + + u32 ndoptlen = skb_tail_pointer(skb) - (skb_transport_header(skb) + offsetof(struct nd_msg, opt)); struct ndisc_options ndopts; struct net_device *dev = skb->dev; @@ -853,7 +853,7 @@ static void ndisc_recv_na(struct sk_buff *skb) const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr; const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr; u8 *lladdr = NULL; - u32 ndoptlen = skb->tail - (skb->transport_header + + u32 ndoptlen = skb_tail_pointer(skb) - (skb_transport_header(skb) + offsetof(struct nd_msg, opt)); struct ndisc_options ndopts; struct net_device *dev = skb->dev; @@ -1069,7 +1069,8 @@ static void ndisc_router_discovery(struct sk_buff *skb) __u8 * opt = (__u8 *)(ra_msg + 1); - optlen = (skb->tail - skb->transport_header) - sizeof(struct ra_msg); + optlen = (skb_tail_pointer(skb) - skb_transport_header(skb)) - + sizeof(struct ra_msg); if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) { ND_PRINTK(2, warn, "RA: source address is not link-local\n"); @@ -1346,7 +1347,7 @@ static void ndisc_redirect_rcv(struct sk_buff *skb) u8 *hdr; struct ndisc_options ndopts; struct rd_msg *msg = (struct rd_msg *)skb_transport_header(skb); - u32 ndoptlen = skb->tail - (skb->transport_header + + u32 ndoptlen = skb_tail_pointer(skb) - (skb_transport_header(skb) + offsetof(struct rd_msg, opt)); #ifdef CONFIG_IPV6_NDISC_NODETYPE diff --git a/net/ipv6/output_core.c b/net/ipv6/output_core.c index c2e73e647e44..ab92a3673fbb 100644 --- a/net/ipv6/output_core.c +++ b/net/ipv6/output_core.c @@ -40,7 +40,8 @@ int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr) u16 offset = sizeof(struct ipv6hdr); struct ipv6_opt_hdr *exthdr = (struct ipv6_opt_hdr *)(ipv6_hdr(skb) + 1); - unsigned int packet_len = skb->tail - skb->network_header; + unsigned int packet_len = skb_tail_pointer(skb) - + skb_network_header(skb); int found_rhdr = 0; *nexthdr = &ipv6_hdr(skb)->nexthdr; diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index eedff8ccded5..4f8886aa8429 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -1132,7 +1132,8 @@ static int rawv6_ioctl(struct sock *sk, int cmd, unsigned long arg) spin_lock_bh(&sk->sk_receive_queue.lock); skb = skb_peek(&sk->sk_receive_queue); if (skb != NULL) - amount = skb->tail - skb->transport_header; + amount = skb_tail_pointer(skb) - + skb_transport_header(skb); spin_unlock_bh(&sk->sk_receive_queue.lock); return put_user(amount, (int __user *)arg); } diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 194c3cde1536..2b874185ebb2 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -1649,7 +1649,7 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu int optlen, on_link; u8 *lladdr; - optlen = skb->tail - skb->transport_header; + optlen = skb_tail_pointer(skb) - skb_transport_header(skb); optlen -= sizeof(*msg); if (optlen < 0) { -- cgit v1.2.3 From f7c0c2ae843b74f8dba55820cb0a3de19c976703 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Tue, 28 May 2013 20:34:27 +0000 Subject: ipv4: Correct comparisons and calculations using skb->tail and skb-transport_header This corrects an regression introduced by "net: Use 16bits for *_headers fields of struct skbuff" when NET_SKBUFF_DATA_USES_OFFSET is not set. In that case skb->tail will be a pointer whereas skb->transport_header will be an offset from head. This is corrected by using wrappers that ensure that comparisons and calculations are always made using pointers. Signed-off-by: Simon Horman Signed-off-by: David S. Miller --- net/ipv4/icmp.c | 3 ++- net/ipv4/igmp.c | 2 +- net/ipv4/tcp.c | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 562efd91f457..5d0d379b0152 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -503,7 +503,8 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) iph = ip_hdr(skb_in); if ((u8 *)iph < skb_in->head || - (skb_in->network_header + sizeof(*iph)) > skb_in->tail) + (skb_network_header(skb_in) + sizeof(*iph)) > + skb_tail_pointer(skb_in)) goto out; /* diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index d8c232794bcb..450f625361e4 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -363,7 +363,7 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size) static int igmpv3_sendpack(struct sk_buff *skb) { struct igmphdr *pig = igmp_hdr(skb); - const int igmplen = skb->tail - skb->transport_header; + const int igmplen = skb_tail_pointer(skb) - skb_transport_header(skb); pig->csum = ip_compute_csum(igmp_hdr(skb), igmplen); diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index ba4186e1dca9..1f58594d5a85 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2989,7 +2989,8 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb, swap(gso_skb->truesize, skb->truesize); } - delta = htonl(oldlen + (skb->tail - skb->transport_header) + + delta = htonl(oldlen + (skb_tail_pointer(skb) - + skb_transport_header(skb)) + skb->data_len); th->check = ~csum_fold((__force __wsum)((__force u32)th->check + (__force u32)delta)); -- cgit v1.2.3 From 158874cac61245b84e939c92c53db7000122b7b0 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Tue, 28 May 2013 20:34:28 +0000 Subject: sctp: Correct access to skb->{network, transport}_header This corrects an regression introduced by "net: Use 16bits for *_headers fields of struct skbuff" when NET_SKBUFF_DATA_USES_OFFSET is not set. In that case sk_buff_data_t will be a pointer, however, skb->{network,transport}_header is now __u16. Signed-off-by: Simon Horman Signed-off-by: David S. Miller --- net/sctp/input.c | 2 +- net/sctp/ipv6.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/net/sctp/input.c b/net/sctp/input.c index 4b2c83146aa7..e328fe8f93f4 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -589,7 +589,7 @@ void sctp_v4_err(struct sk_buff *skb, __u32 info) struct sctp_association *asoc = NULL; struct sctp_transport *transport; struct inet_sock *inet; - sk_buff_data_t saveip, savesctp; + __be16 saveip, savesctp; int err; struct net *net = dev_net(skb->dev); diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index 391a245d5203..8ee553b499ce 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -153,7 +153,7 @@ SCTP_STATIC void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, struct sctp_association *asoc; struct sctp_transport *transport; struct ipv6_pinfo *np; - sk_buff_data_t saveip, savesctp; + __be16 saveip, savesctp; int err; struct net *net = dev_net(skb->dev); -- cgit v1.2.3 From 7cc461900549fc480eb133948649a1edb7eaaa6f Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Tue, 28 May 2013 20:34:29 +0000 Subject: net, ipv4, ipv6: Correct assignment of skb->network_header to skb->tail This corrects an regression introduced by "net: Use 16bits for *_headers fields of struct skbuff" when NET_SKBUFF_DATA_USES_OFFSET is not set. In that case skb->tail will be a pointer however skb->network_header is now an offset. This patch corrects the problem by adding a wrapper to return skb tail as an offset regardless of the value of NET_SKBUFF_DATA_USES_OFFSET. It seems that skb->tail that this offset may be more than 64k and some care has been taken to treat such cases as an error. Signed-off-by: Simon Horman Signed-off-by: David S. Miller --- include/linux/skbuff.h | 9 +++++++++ net/core/netpoll.c | 9 ++++++++- net/core/pktgen.c | 16 ++++++++++++++-- net/ipv4/ipmr.c | 8 +++++++- 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 8f2b830772a8..5f931191cf57 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1391,6 +1391,11 @@ static inline void skb_set_tail_pointer(struct sk_buff *skb, const int offset) skb_reset_tail_pointer(skb); skb->tail += offset; } + +static inline unsigned long skb_tail_offset(const struct sk_buff *skb) +{ + return skb->tail; +} #else /* NET_SKBUFF_DATA_USES_OFFSET */ static inline unsigned char *skb_tail_pointer(const struct sk_buff *skb) { @@ -1407,6 +1412,10 @@ static inline void skb_set_tail_pointer(struct sk_buff *skb, const int offset) skb->tail = skb->data + offset; } +static inline unsigned long skb_tail_offset(const struct sk_buff *skb) +{ + return skb->tail - skb->head; +} #endif /* NET_SKBUFF_DATA_USES_OFFSET */ /* diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 37deedd48bcc..688517c7ff17 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -676,6 +676,8 @@ static void netpoll_neigh_reply(struct sk_buff *skb, struct netpoll_info *npinfo spin_lock_irqsave(&npinfo->rx_lock, flags); list_for_each_entry_safe(np, tmp, &npinfo->rx_np, rx) { + unsigned long tail_offset; + if (!ipv6_addr_equal(daddr, &np->local_ip.in6)) continue; @@ -700,7 +702,12 @@ static void netpoll_neigh_reply(struct sk_buff *skb, struct netpoll_info *npinfo hdr->saddr = *saddr; hdr->daddr = *daddr; - send_skb->transport_header = send_skb->tail; + tail_offset = skb_tail_offset(skb); + if (tail_offset > 0xffff) { + kfree_skb(send_skb); + continue; + } + skb_set_network_header(send_skb, tail_offset); skb_put(send_skb, size); icmp6h = (struct icmp6hdr *)skb_transport_header(skb); diff --git a/net/core/pktgen.c b/net/core/pktgen.c index 795498fd4587..d2ede89662be 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -2642,6 +2642,7 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev, __be16 *svlan_tci = NULL; /* Encapsulates priority and SVLAN ID */ __be16 *svlan_encapsulated_proto = NULL; /* packet type ID field (or len) for SVLAN tag */ u16 queue_map; + unsigned long tail_offset; if (pkt_dev->nr_labels) protocol = htons(ETH_P_MPLS_UC); @@ -2708,7 +2709,12 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev, *vlan_encapsulated_proto = htons(ETH_P_IP); } - skb->network_header = skb->tail; + tail_offset = skb_tail_offset(skb); + if (tail_offset > 0xffff) { + kfree_skb(skb); + return NULL; + } + skb_set_network_header(skb, tail_offset); skb->transport_header = skb->network_header + sizeof(struct iphdr); skb_put(skb, sizeof(struct iphdr) + sizeof(struct udphdr)); skb_set_queue_mapping(skb, queue_map); @@ -2775,6 +2781,7 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev, __be16 *svlan_tci = NULL; /* Encapsulates priority and SVLAN ID */ __be16 *svlan_encapsulated_proto = NULL; /* packet type ID field (or len) for SVLAN tag */ u16 queue_map; + unsigned long tail_offset; if (pkt_dev->nr_labels) protocol = htons(ETH_P_MPLS_UC); @@ -2822,7 +2829,12 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev, *vlan_encapsulated_proto = htons(ETH_P_IPV6); } - skb->network_header = skb->tail; + tail_offset = skb_tail_offset(skb); + if (tail_offset > 0xffff) { + kfree_skb(skb); + return NULL; + } + skb_set_network_header(skb, tail_offset); skb->transport_header = skb->network_header + sizeof(struct ipv6hdr); skb_put(skb, sizeof(struct ipv6hdr) + sizeof(struct udphdr)); skb_set_queue_mapping(skb, queue_map); diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index f975399f3522..df97f0ac1a1c 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -945,6 +945,7 @@ static int ipmr_cache_report(struct mr_table *mrt, struct igmpmsg *msg; struct sock *mroute_sk; int ret; + unsigned long tail_offset; #ifdef CONFIG_IP_PIMSM if (assert == IGMPMSG_WHOLEPKT) @@ -980,7 +981,12 @@ static int ipmr_cache_report(struct mr_table *mrt, /* Copy the IP header */ - skb->network_header = skb->tail; + tail_offset = skb_tail_offset(skb); + if (tail_offset > 0xffff) { + kfree_skb(skb); + return -EINVAL; + } + skb_set_network_header(skb, tail_offset); skb_put(skb, ihl); skb_copy_to_linear_data(skb, pkt->data, ihl); ip_hdr(skb)->protocol = 0; /* Flag to the kernel this is a route add */ -- cgit v1.2.3 From 6f8b314583c9407bda9f215f25d7b316ae8a7058 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 1 May 2013 23:31:01 +0800 Subject: clk: wm831x: Fix update wrong register for enable/disable FLL According to the datasheet, FLL Enable is controlled by R16530 (4092h) FLL Control1 BIT 0: FLL Enable 0 = Disable 1 = Enable Thus the code should update WM831X_FLL_CONTROL_1 register rather than WM831X_FLL_CONTROL_2 register. Also fixes a trivial typo in dev_crit message. Signed-off-by: Axel Lin Acked-by: Mark Brown Signed-off-by: Mike Turquette --- drivers/clk/clk-wm831x.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/clk/clk-wm831x.c b/drivers/clk/clk-wm831x.c index 16ed06808554..4bdf0b0cab92 100644 --- a/drivers/clk/clk-wm831x.c +++ b/drivers/clk/clk-wm831x.c @@ -97,7 +97,7 @@ static int wm831x_fll_prepare(struct clk_hw *hw) struct wm831x *wm831x = clkdata->wm831x; int ret; - ret = wm831x_set_bits(wm831x, WM831X_FLL_CONTROL_2, + ret = wm831x_set_bits(wm831x, WM831X_FLL_CONTROL_1, WM831X_FLL_ENA, WM831X_FLL_ENA); if (ret != 0) dev_crit(wm831x->dev, "Failed to enable FLL: %d\n", ret); @@ -114,9 +114,9 @@ static void wm831x_fll_unprepare(struct clk_hw *hw) struct wm831x *wm831x = clkdata->wm831x; int ret; - ret = wm831x_set_bits(wm831x, WM831X_FLL_CONTROL_2, WM831X_FLL_ENA, 0); + ret = wm831x_set_bits(wm831x, WM831X_FLL_CONTROL_1, WM831X_FLL_ENA, 0); if (ret != 0) - dev_crit(wm831x->dev, "Failed to disaable FLL: %d\n", ret); + dev_crit(wm831x->dev, "Failed to disable FLL: %d\n", ret); } static unsigned long wm831x_fll_recalc_rate(struct clk_hw *hw, -- cgit v1.2.3 From bcc7fd20e2e9df6f29db7d524623e0c7023dffc6 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 1 May 2013 23:32:05 +0800 Subject: clk: wm831x: Fix wm831x_clkout_get_parent Current code looks strange because calling wm831x_clkout_set_parent() with 0 as parent parameter, wm831x_clkout_get_parent() will return 1. According to the datasheet: R16528 (4090h) Clock Control1 BIT 0: CLKOUT output source select 0 = FLL output 1 = 32.768kHz oscillator Thus fix the entry order in wm831x_clkout_parents[] to make it has the same meaning as the datasheet and make the return value of wm831x_clkout_get_parent() consistent with the parent pass to wm831x_clkout_set_parent(). Signed-off-by: Axel Lin Acked-by: Mark Brown Signed-off-by: Mike Turquette --- drivers/clk/clk-wm831x.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/clk/clk-wm831x.c b/drivers/clk/clk-wm831x.c index 4bdf0b0cab92..58d08a640275 100644 --- a/drivers/clk/clk-wm831x.c +++ b/drivers/clk/clk-wm831x.c @@ -299,8 +299,8 @@ static void wm831x_clkout_unprepare(struct clk_hw *hw) } static const char *wm831x_clkout_parents[] = { - "xtal", "fll", + "xtal", }; static u8 wm831x_clkout_get_parent(struct clk_hw *hw) @@ -318,9 +318,9 @@ static u8 wm831x_clkout_get_parent(struct clk_hw *hw) } if (ret & WM831X_CLKOUT_SRC) - return 0; - else return 1; + else + return 0; } static int wm831x_clkout_set_parent(struct clk_hw *hw, u8 parent) -- cgit v1.2.3 From 784e4616a40166fc60a1741a2c64155e99fe9247 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Mon, 27 May 2013 22:35:51 +0000 Subject: vxlan: remove the unused rcu head from struct vxlan_rdst Cc: Stephen Hemminger Cc: David S. Miller Signed-off-by: Cong Wang Acked-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/vxlan.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 5ed64d496f83..289d79a48c82 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -97,7 +97,6 @@ struct vxlan_net { }; struct vxlan_rdst { - struct rcu_head rcu; __be32 remote_ip; __be16 remote_port; u32 remote_vni; -- cgit v1.2.3 From 31fec5aa21d166cf81702a669c1398784b513b8a Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Mon, 27 May 2013 22:35:52 +0000 Subject: vxlan: use unsigned int instead of unsigned 'unsigned int' is slightly better. Cc: Stephen Hemminger Cc: David S. Miller Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- drivers/net/vxlan.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 289d79a48c82..6fc962042046 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -1269,7 +1269,7 @@ static int vxlan_open(struct net_device *dev) /* Purge the forwarding table */ static void vxlan_flush(struct vxlan_dev *vxlan) { - unsigned h; + unsigned int h; spin_lock_bh(&vxlan->hash_lock); for (h = 0; h < FDB_HASH_SIZE; ++h) { @@ -1333,7 +1333,7 @@ static void vxlan_free(struct net_device *dev) static void vxlan_setup(struct net_device *dev) { struct vxlan_dev *vxlan = netdev_priv(dev); - unsigned h; + unsigned int h; int low, high; eth_hw_addr_random(dev); @@ -1459,7 +1459,7 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port) .sin_addr.s_addr = htonl(INADDR_ANY), }; int rc; - unsigned h; + unsigned int h; vs = kmalloc(sizeof(*vs), GFP_KERNEL); if (!vs) @@ -1722,7 +1722,7 @@ static struct rtnl_link_ops vxlan_link_ops __read_mostly = { static __net_init int vxlan_init_net(struct net *net) { struct vxlan_net *vn = net_generic(net, vxlan_net_id); - unsigned h; + unsigned int h; INIT_LIST_HEAD(&vn->vxlan_list); -- cgit v1.2.3 From 7332a13b038be05cf44fcf0cacfdd8aade77b16f Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Mon, 27 May 2013 22:35:53 +0000 Subject: vxlan: defer vxlan init as late as possible When vxlan is compiled as builtin, its init code runs before IPv6 init, this could cause problems if we create IPv6 socket in the latter patch. Cc: Stephen Hemminger Cc: David S. Miller Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- drivers/net/vxlan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 6fc962042046..8111565c35fc 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -1771,7 +1771,7 @@ out2: out1: return rc; } -module_init(vxlan_init_module); +late_initcall(vxlan_init_module); static void __exit vxlan_cleanup_module(void) { -- cgit v1.2.3 From 43547ea669926b2ef8ba62a9def7b2c5728b3cdd Mon Sep 17 00:00:00 2001 From: Veaceslav Falico Date: Mon, 27 May 2013 23:14:51 +0000 Subject: bonding: trivial: remove unused parameter from alb_swap_mac_addr() After b924551 ("bonding: fix enslaving in alb mode when link down") we don't need the bond parameter in alb_swap_mac_addr(), so remove it. Signed-off-by: Veaceslav Falico Signed-off-by: David S. Miller --- drivers/net/bonding/bond_alb.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index e02cc265723a..a236234d2c27 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -1056,7 +1056,7 @@ static int alb_set_slave_mac_addr(struct slave *slave, u8 addr[]) * */ -static void alb_swap_mac_addr(struct bonding *bond, struct slave *slave1, struct slave *slave2) +static void alb_swap_mac_addr(struct slave *slave1, struct slave *slave2) { u8 tmp_mac_addr[ETH_ALEN]; @@ -1149,7 +1149,7 @@ static void alb_change_hw_addr_on_detach(struct bonding *bond, struct slave *sla if (found) { /* locking: needs RTNL and nothing else */ - alb_swap_mac_addr(bond, slave, tmp_slave); + alb_swap_mac_addr(slave, tmp_slave); alb_fasten_mac_swap(bond, slave, tmp_slave); } } @@ -1750,7 +1750,7 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave /* curr_active_slave must be set before calling alb_swap_mac_addr */ if (swap_slave) { /* swap mac address */ - alb_swap_mac_addr(bond, swap_slave, new_slave); + alb_swap_mac_addr(swap_slave, new_slave); } else { /* set the new_slave to the bond mac address */ alb_set_slave_mac_addr(new_slave, bond->dev->dev_addr); @@ -1810,7 +1810,7 @@ int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr) } if (swap_slave) { - alb_swap_mac_addr(bond, swap_slave, bond->curr_active_slave); + alb_swap_mac_addr(swap_slave, bond->curr_active_slave); alb_fasten_mac_swap(bond, swap_slave, bond->curr_active_slave); } else { alb_set_slave_mac_addr(bond->curr_active_slave, bond_dev->dev_addr); -- cgit v1.2.3 From d6641ccff9b4d3b0d526e754dceaa4c430f5d24f Mon Sep 17 00:00:00 2001 From: Veaceslav Falico Date: Tue, 28 May 2013 01:26:13 +0000 Subject: bonding: trivial: update the comments to reflect the reality Signed-off-by: Veaceslav Falico Signed-off-by: David S. Miller --- drivers/net/bonding/bond_sysfs.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c index 3d269a52789c..f8bee4c0cbf1 100644 --- a/drivers/net/bonding/bond_sysfs.c +++ b/drivers/net/bonding/bond_sysfs.c @@ -231,8 +231,7 @@ static ssize_t bonding_show_slaves(struct device *d, } /* - * Set the slaves in the current bond. The bond interface must be - * up for this to succeed. + * Set the slaves in the current bond. * This is supposed to be only thin wrapper for bond_enslave and bond_release. * All hard work should be done there. */ @@ -363,7 +362,6 @@ static DEVICE_ATTR(mode, S_IRUGO | S_IWUSR, /* * Show and set the bonding transmit hash method. - * The bond interface must be down to change the xmit hash policy. */ static ssize_t bonding_show_xmit_hash(struct device *d, struct device_attribute *attr, -- cgit v1.2.3 From 9ee718aa9269cf56040cf12f0f6ac6e0057397b2 Mon Sep 17 00:00:00 2001 From: Eytan Lifshitz Date: Sun, 19 May 2013 19:14:41 +0300 Subject: iwlwifi: mvm: add thermal throttling and CT kill In order to avoid NIC destruction due to high temperature, CT kill will power down the NIC. To avoid this, thermal throttling will decrease throughput to prevent the NIC from reaching the temperature at which CT kill is performed. Signed-off-by: Eytan Lifshitz Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/iwl-csr.h | 19 ++ drivers/net/wireless/iwlwifi/iwl-prph.h | 12 + drivers/net/wireless/iwlwifi/mvm/Makefile | 2 +- drivers/net/wireless/iwlwifi/mvm/bt-coex.c | 6 +- drivers/net/wireless/iwlwifi/mvm/fw-api.h | 211 ++++++++++++ drivers/net/wireless/iwlwifi/mvm/fw.c | 2 +- drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c | 5 +- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 4 +- drivers/net/wireless/iwlwifi/mvm/mvm.h | 101 +++++- drivers/net/wireless/iwlwifi/mvm/ops.c | 18 +- drivers/net/wireless/iwlwifi/mvm/rs.c | 26 ++ drivers/net/wireless/iwlwifi/mvm/rs.h | 5 + drivers/net/wireless/iwlwifi/mvm/rx.c | 22 ++ drivers/net/wireless/iwlwifi/mvm/sta.c | 13 +- drivers/net/wireless/iwlwifi/mvm/sta.h | 6 + drivers/net/wireless/iwlwifi/mvm/tt.c | 509 ++++++++++++++++++++++++++++ drivers/net/wireless/iwlwifi/mvm/utils.c | 31 ++ 17 files changed, 974 insertions(+), 18 deletions(-) create mode 100644 drivers/net/wireless/iwlwifi/mvm/tt.c diff --git a/drivers/net/wireless/iwlwifi/iwl-csr.h b/drivers/net/wireless/iwlwifi/iwl-csr.h index 20e845d4da04..a276af476e2d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-csr.h +++ b/drivers/net/wireless/iwlwifi/iwl-csr.h @@ -472,4 +472,23 @@ #define IWL_HOST_INT_CALIB_TIMEOUT_DEF (0x10) #define IWL_HOST_INT_CALIB_TIMEOUT_MIN (0x0) +/***************************************************************************** + * 7000/3000 series SHR DTS addresses * + *****************************************************************************/ + +/* Diode Results Register Structure: */ +enum dtd_diode_reg { + DTS_DIODE_REG_DIG_VAL = 0x000000FF, /* bits [7:0] */ + DTS_DIODE_REG_VREF_LOW = 0x0000FF00, /* bits [15:8] */ + DTS_DIODE_REG_VREF_HIGH = 0x00FF0000, /* bits [23:16] */ + DTS_DIODE_REG_VREF_ID = 0x03000000, /* bits [25:24] */ + DTS_DIODE_REG_PASS_ONCE = 0x80000000, /* bits [31:31] */ + DTS_DIODE_REG_FLAGS_MSK = 0xFF000000, /* bits [31:24] */ +/* Those are the masks INSIDE the flags bit-field: */ + DTS_DIODE_REG_FLAGS_VREFS_ID_POS = 0, + DTS_DIODE_REG_FLAGS_VREFS_ID = 0x00000003, /* bits [1:0] */ + DTS_DIODE_REG_FLAGS_PASS_ONCE_POS = 7, + DTS_DIODE_REG_FLAGS_PASS_ONCE = 0x00000080, /* bits [7:7] */ +}; + #endif /* !__iwl_csr_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h index 386f2a7c87cb..ff8cc75c189d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/iwlwifi/iwl-prph.h @@ -100,6 +100,18 @@ /* Device system time */ #define DEVICE_SYSTEM_TIME_REG 0xA0206C +/***************************************************************************** + * 7000/3000 series SHR DTS addresses * + *****************************************************************************/ + +#define SHR_MISC_WFM_DTS_EN (0x00a10024) +#define DTSC_CFG_MODE (0x00a10604) +#define DTSC_VREF_AVG (0x00a10648) +#define DTSC_VREF5_AVG (0x00a1064c) +#define DTSC_CFG_MODE_PERIODIC (0x2) +#define DTSC_PTAT_AVG (0x00a10650) + + /** * Tx Scheduler * diff --git a/drivers/net/wireless/iwlwifi/mvm/Makefile b/drivers/net/wireless/iwlwifi/mvm/Makefile index 2acc44b40986..ff856e543ae8 100644 --- a/drivers/net/wireless/iwlwifi/mvm/Makefile +++ b/drivers/net/wireless/iwlwifi/mvm/Makefile @@ -3,7 +3,7 @@ iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o iwlmvm-y += scan.o time-event.o rs.o iwlmvm-y += power.o bt-coex.o -iwlmvm-y += led.o +iwlmvm-y += led.o tt.o iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o iwlmvm-$(CONFIG_PM_SLEEP) += d3.o diff --git a/drivers/net/wireless/iwlwifi/mvm/bt-coex.c b/drivers/net/wireless/iwlwifi/mvm/bt-coex.c index 810bfa5f6de0..f03655f303aa 100644 --- a/drivers/net/wireless/iwlwifi/mvm/bt-coex.c +++ b/drivers/net/wireless/iwlwifi/mvm/bt-coex.c @@ -351,6 +351,7 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, enum ieee80211_band band; int ave_rssi; + lockdep_assert_held(&mvm->mutex); if (vif->type != NL80211_IFTYPE_STATION) return; @@ -365,7 +366,8 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, smps_mode = IEEE80211_SMPS_AUTOMATIC; if (band != IEEE80211_BAND_2GHZ) { - ieee80211_request_smps(vif, smps_mode); + iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, + smps_mode); return; } @@ -380,7 +382,7 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, mvmvif->id, data->notif->bt_status, data->notif->bt_traffic_load, smps_mode); - ieee80211_request_smps(vif, smps_mode); + iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, smps_mode); /* don't reduce the Tx power if in loose scheme */ if (is_loose_coex()) diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h index 733153c3b66c..db6f47406ac2 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h @@ -139,6 +139,9 @@ enum { /* Power */ POWER_TABLE_CMD = 0x77, + /* Thermal Throttling*/ + REPLY_THERMAL_MNG_BACKOFF = 0x7e, + /* Scanning */ SCAN_REQUEST_CMD = 0x80, SCAN_ABORT_CMD = 0x81, @@ -977,4 +980,212 @@ struct iwl_mcast_filter_cmd { u8 addr_list[0]; } __packed; /* MCAST_FILTERING_CMD_API_S_VER_1 */ +struct mvm_statistics_dbg { + __le32 burst_check; + __le32 burst_count; + __le32 wait_for_silence_timeout_cnt; + __le32 reserved[3]; +} __packed; /* STATISTICS_DEBUG_API_S_VER_2 */ + +struct mvm_statistics_div { + __le32 tx_on_a; + __le32 tx_on_b; + __le32 exec_time; + __le32 probe_time; + __le32 rssi_ant; + __le32 reserved2; +} __packed; /* STATISTICS_SLOW_DIV_API_S_VER_2 */ + +struct mvm_statistics_general_common { + __le32 temperature; /* radio temperature */ + __le32 temperature_m; /* radio voltage */ + struct mvm_statistics_dbg dbg; + __le32 sleep_time; + __le32 slots_out; + __le32 slots_idle; + __le32 ttl_timestamp; + struct mvm_statistics_div div; + __le32 rx_enable_counter; + /* + * num_of_sos_states: + * count the number of times we have to re-tune + * in order to get out of bad PHY status + */ + __le32 num_of_sos_states; +} __packed; /* STATISTICS_GENERAL_API_S_VER_5 */ + +struct mvm_statistics_rx_non_phy { + __le32 bogus_cts; /* CTS received when not expecting CTS */ + __le32 bogus_ack; /* ACK received when not expecting ACK */ + __le32 non_bssid_frames; /* number of frames with BSSID that + * doesn't belong to the STA BSSID */ + __le32 filtered_frames; /* count frames that were dumped in the + * filtering process */ + __le32 non_channel_beacons; /* beacons with our bss id but not on + * our serving channel */ + __le32 channel_beacons; /* beacons with our bss id and in our + * serving channel */ + __le32 num_missed_bcon; /* number of missed beacons */ + __le32 adc_rx_saturation_time; /* count in 0.8us units the time the + * ADC was in saturation */ + __le32 ina_detection_search_time;/* total time (in 0.8us) searched + * for INA */ + __le32 beacon_silence_rssi_a; /* RSSI silence after beacon frame */ + __le32 beacon_silence_rssi_b; /* RSSI silence after beacon frame */ + __le32 beacon_silence_rssi_c; /* RSSI silence after beacon frame */ + __le32 interference_data_flag; /* flag for interference data + * availability. 1 when data is + * available. */ + __le32 channel_load; /* counts RX Enable time in uSec */ + __le32 dsp_false_alarms; /* DSP false alarm (both OFDM + * and CCK) counter */ + __le32 beacon_rssi_a; + __le32 beacon_rssi_b; + __le32 beacon_rssi_c; + __le32 beacon_energy_a; + __le32 beacon_energy_b; + __le32 beacon_energy_c; + __le32 num_bt_kills; + __le32 mac_id; + __le32 directed_data_mpdu; +} __packed; /* STATISTICS_RX_NON_PHY_API_S_VER_3 */ + +struct mvm_statistics_rx_phy { + __le32 ina_cnt; + __le32 fina_cnt; + __le32 plcp_err; + __le32 crc32_err; + __le32 overrun_err; + __le32 early_overrun_err; + __le32 crc32_good; + __le32 false_alarm_cnt; + __le32 fina_sync_err_cnt; + __le32 sfd_timeout; + __le32 fina_timeout; + __le32 unresponded_rts; + __le32 rxe_frame_limit_overrun; + __le32 sent_ack_cnt; + __le32 sent_cts_cnt; + __le32 sent_ba_rsp_cnt; + __le32 dsp_self_kill; + __le32 mh_format_err; + __le32 re_acq_main_rssi_sum; + __le32 reserved; +} __packed; /* STATISTICS_RX_PHY_API_S_VER_2 */ + +struct mvm_statistics_rx_ht_phy { + __le32 plcp_err; + __le32 overrun_err; + __le32 early_overrun_err; + __le32 crc32_good; + __le32 crc32_err; + __le32 mh_format_err; + __le32 agg_crc32_good; + __le32 agg_mpdu_cnt; + __le32 agg_cnt; + __le32 unsupport_mcs; +} __packed; /* STATISTICS_HT_RX_PHY_API_S_VER_1 */ + +#define MAX_CHAINS 3 + +struct mvm_statistics_tx_non_phy_agg { + __le32 ba_timeout; + __le32 ba_reschedule_frames; + __le32 scd_query_agg_frame_cnt; + __le32 scd_query_no_agg; + __le32 scd_query_agg; + __le32 scd_query_mismatch; + __le32 frame_not_ready; + __le32 underrun; + __le32 bt_prio_kill; + __le32 rx_ba_rsp_cnt; + __s8 txpower[MAX_CHAINS]; + __s8 reserved; + __le32 reserved2; +} __packed; /* STATISTICS_TX_NON_PHY_AGG_API_S_VER_1 */ + +struct mvm_statistics_tx_channel_width { + __le32 ext_cca_narrow_ch20[1]; + __le32 ext_cca_narrow_ch40[2]; + __le32 ext_cca_narrow_ch80[3]; + __le32 ext_cca_narrow_ch160[4]; + __le32 last_tx_ch_width_indx; + __le32 rx_detected_per_ch_width[4]; + __le32 success_per_ch_width[4]; + __le32 fail_per_ch_width[4]; +}; /* STATISTICS_TX_CHANNEL_WIDTH_API_S_VER_1 */ + +struct mvm_statistics_tx { + __le32 preamble_cnt; + __le32 rx_detected_cnt; + __le32 bt_prio_defer_cnt; + __le32 bt_prio_kill_cnt; + __le32 few_bytes_cnt; + __le32 cts_timeout; + __le32 ack_timeout; + __le32 expected_ack_cnt; + __le32 actual_ack_cnt; + __le32 dump_msdu_cnt; + __le32 burst_abort_next_frame_mismatch_cnt; + __le32 burst_abort_missing_next_frame_cnt; + __le32 cts_timeout_collision; + __le32 ack_or_ba_timeout_collision; + struct mvm_statistics_tx_non_phy_agg agg; + struct mvm_statistics_tx_channel_width channel_width; +} __packed; /* STATISTICS_TX_API_S_VER_4 */ + + +struct mvm_statistics_bt_activity { + __le32 hi_priority_tx_req_cnt; + __le32 hi_priority_tx_denied_cnt; + __le32 lo_priority_tx_req_cnt; + __le32 lo_priority_tx_denied_cnt; + __le32 hi_priority_rx_req_cnt; + __le32 hi_priority_rx_denied_cnt; + __le32 lo_priority_rx_req_cnt; + __le32 lo_priority_rx_denied_cnt; +} __packed; /* STATISTICS_BT_ACTIVITY_API_S_VER_1 */ + +struct mvm_statistics_general { + struct mvm_statistics_general_common common; + __le32 beacon_filtered; + __le32 missed_beacons; + __s8 beacon_filter_everage_energy; + __s8 beacon_filter_reason; + __s8 beacon_filter_current_energy; + __s8 beacon_filter_reserved; + __le32 beacon_filter_delta_time; + struct mvm_statistics_bt_activity bt_activity; +} __packed; /* STATISTICS_GENERAL_API_S_VER_5 */ + +struct mvm_statistics_rx { + struct mvm_statistics_rx_phy ofdm; + struct mvm_statistics_rx_phy cck; + struct mvm_statistics_rx_non_phy general; + struct mvm_statistics_rx_ht_phy ofdm_ht; +} __packed; /* STATISTICS_RX_API_S_VER_3 */ + +/* + * STATISTICS_NOTIFICATION = 0x9d (notification only, not a command) + * + * By default, uCode issues this notification after receiving a beacon + * while associated. To disable this behavior, set DISABLE_NOTIF flag in the + * REPLY_STATISTICS_CMD 0x9c, above. + * + * Statistics counters continue to increment beacon after beacon, but are + * cleared when changing channels or when driver issues REPLY_STATISTICS_CMD + * 0x9c with CLEAR_STATS bit set (see above). + * + * uCode also issues this notification during scans. uCode clears statistics + * appropriately so that each notification contains statistics for only the + * one channel that has just been scanned. + */ + +struct iwl_notif_statistics { /* STATISTICS_NTFY_API_S_VER_8 */ + __le32 flag; + struct mvm_statistics_rx rx; + struct mvm_statistics_tx tx; + struct mvm_statistics_general general; +} __packed; + #endif /* __fw_api_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c index 20ee2812cc9b..cd7c0032cc58 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/iwlwifi/mvm/fw.c @@ -330,7 +330,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) * abort after reading the nvm in case RF Kill is on, we will complete * the init seq later when RF kill will switch to off */ - if (test_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status)) { + if (iwl_mvm_is_radio_killed(mvm)) { IWL_DEBUG_RF_KILL(mvm, "jump over all phy activities due to RF kill\n"); iwl_remove_notification(&mvm->notif_wait, &calib_wait); diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c index b2cc3d98e0f7..dc5f4ef50198 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c @@ -227,7 +227,7 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm, .found_vif = false, }; u32 ac; - int ret; + int ret, i; /* * Allocate a MAC ID and a TSF for this MAC, along with the queues @@ -335,6 +335,9 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm, mvmvif->bcast_sta.sta_id = IWL_MVM_STATION_COUNT; mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT; + for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) + mvmvif->smps_requests[i] = IEEE80211_SMPS_AUTOMATIC; + return 0; exit_fail: diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index c9924c12e0fe..c26f6b504460 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -265,8 +265,8 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw, { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - if (test_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status)) { - IWL_DEBUG_DROP(mvm, "Dropping - RF KILL\n"); + if (iwl_mvm_is_radio_killed(mvm)) { + IWL_DEBUG_DROP(mvm, "Dropping - RF/CT KILL\n"); goto drop; } diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index 4e87a321e107..a288552491b3 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -150,6 +150,12 @@ enum iwl_power_scheme { #define IWL_CONN_MAX_LISTEN_INTERVAL 70 +enum iwl_mvm_smps_type_request { + IWL_MVM_SMPS_REQ_BT_COEX, + IWL_MVM_SMPS_REQ_TT, + NUM_IWL_MVM_SMPS_REQ, +}; + /** * struct iwl_mvm_vif - data per Virtual Interface, it is a MAC context * @id: between 0 and 3 @@ -164,6 +170,8 @@ enum iwl_power_scheme { * @bcast_sta: station used for broadcast packets. Used by the following * vifs: P2P_DEVICE, GO and AP. * @beacon_skb: the skb used to hold the AP/GO beacon template + * @smps_requests: the requests of of differents parts of the driver, regard + the desired smps mode. */ struct iwl_mvm_vif { u16 id; @@ -218,6 +226,8 @@ struct iwl_mvm_vif { struct dentry *dbgfs_slink; void *dbgfs_data; #endif + + enum ieee80211_smps_mode smps_requests[NUM_IWL_MVM_SMPS_REQ]; }; static inline struct iwl_mvm_vif * @@ -226,12 +236,6 @@ iwl_mvm_vif_from_mac80211(struct ieee80211_vif *vif) return (void *)vif->drv_priv; } -enum iwl_mvm_status { - IWL_MVM_STATUS_HW_RFKILL, - IWL_MVM_STATUS_ROC_RUNNING, - IWL_MVM_STATUS_IN_HW_RESTART, -}; - enum iwl_scan_status { IWL_MVM_SCAN_NONE, IWL_MVM_SCAN_OS, @@ -249,6 +253,63 @@ struct iwl_nvm_section { const u8 *data; }; +/* + * Tx-backoff threshold + * @temperature: The threshold in Celsius + * @backoff: The tx-backoff in uSec + */ +struct iwl_tt_tx_backoff { + s32 temperature; + u32 backoff; +}; + +#define TT_TX_BACKOFF_SIZE 6 + +/** + * struct iwl_tt_params - thermal throttling parameters + * @ct_kill_entry: CT Kill entry threshold + * @ct_kill_exit: CT Kill exit threshold + * @ct_kill_duration: The time intervals (in uSec) in which the driver needs + * to checks whether to exit CT Kill. + * @dynamic_smps_entry: Dynamic SMPS entry threshold + * @dynamic_smps_exit: Dynamic SMPS exit threshold + * @tx_protection_entry: TX protection entry threshold + * @tx_protection_exit: TX protection exit threshold + * @tx_backoff: Array of thresholds for tx-backoff , in ascending order. + * @support_ct_kill: Support CT Kill? + * @support_dynamic_smps: Support dynamic SMPS? + * @support_tx_protection: Support tx protection? + * @support_tx_backoff: Support tx-backoff? + */ +struct iwl_tt_params { + s32 ct_kill_entry; + s32 ct_kill_exit; + u32 ct_kill_duration; + s32 dynamic_smps_entry; + s32 dynamic_smps_exit; + s32 tx_protection_entry; + s32 tx_protection_exit; + struct iwl_tt_tx_backoff tx_backoff[TT_TX_BACKOFF_SIZE]; + bool support_ct_kill; + bool support_dynamic_smps; + bool support_tx_protection; + bool support_tx_backoff; +}; + +/** + * struct iwl_mvm_tt_mgnt - Thermal Throttling Management structure + * @ct_kill_exit: worker to exit thermal kill + * @dynamic_smps: Is thermal throttling enabled dynamic_smps? + * @tx_backoff: The current thremal throttling tx backoff in uSec. + * @params: Parameters to configure the thermal throttling algorithm. + */ +struct iwl_mvm_tt_mgmt { + struct delayed_work ct_kill_exit; + bool dynamic_smps; + u32 tx_backoff; + const struct iwl_tt_params *params; +}; + struct iwl_mvm { /* for logger access */ struct device *dev; @@ -356,6 +417,10 @@ struct iwl_mvm { /* BT-Coex */ u8 bt_kill_msk; struct iwl_bt_coex_profile_notif last_bt_notif; + + /* Thermal Throttling and CTkill */ + struct iwl_mvm_tt_mgmt thermal_throttle; + s32 temperature; /* Celsius */ }; /* Extract MVM priv from op_mode and _hw */ @@ -365,6 +430,19 @@ struct iwl_mvm { #define IWL_MAC80211_GET_MVM(_hw) \ IWL_OP_MODE_GET_MVM((struct iwl_op_mode *)((_hw)->priv)) +enum iwl_mvm_status { + IWL_MVM_STATUS_HW_RFKILL, + IWL_MVM_STATUS_HW_CTKILL, + IWL_MVM_STATUS_ROC_RUNNING, + IWL_MVM_STATUS_IN_HW_RESTART, +}; + +static inline bool iwl_mvm_is_radio_killed(struct iwl_mvm *mvm) +{ + return test_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status) || + test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status); +} + extern const u8 iwl_mvm_ac_to_tx_fifo[]; struct iwl_rate_info { @@ -555,4 +633,15 @@ int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +/* SMPS */ +void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + enum iwl_mvm_smps_type_request req_type, + enum ieee80211_smps_mode smps_request); + +/* Thermal management and CT-kill */ +void iwl_mvm_tt_handler(struct iwl_mvm *mvm); +void iwl_mvm_tt_initialize(struct iwl_mvm *mvm); +void iwl_mvm_tt_exit(struct iwl_mvm *mvm); +void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state); + #endif /* __IWL_MVM_H__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 7998baca5a8b..8431637e38f2 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -222,6 +222,7 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { RX_HANDLER(BT_PROFILE_NOTIFICATION, iwl_mvm_rx_bt_coex_notif, true), RX_HANDLER(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif, false), + RX_HANDLER(STATISTICS_NOTIFICATION, iwl_mvm_rx_statistics, true), RX_HANDLER(RADIO_VERSION_NOTIFICATION, iwl_mvm_rx_radio_ver, false), RX_HANDLER(CARD_STATE_NOTIFICATION, iwl_mvm_rx_card_state_notif, false), @@ -294,6 +295,7 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = { CMD(BT_CONFIG), CMD(MCAST_FILTER_CMD), CMD(REPLY_BEACON_FILTERING_CMD), + CMD(REPLY_THERMAL_MNG_BACKOFF), }; #undef CMD @@ -394,6 +396,8 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, if (err) goto out_free; + iwl_mvm_tt_initialize(mvm); + mutex_lock(&mvm->mutex); err = iwl_run_init_mvm_ucode(mvm, true); mutex_unlock(&mvm->mutex); @@ -441,6 +445,8 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode) iwl_mvm_leds_exit(mvm); + iwl_mvm_tt_exit(mvm); + ieee80211_unregister_hw(mvm->hw); kfree(mvm->scan_cmd); @@ -595,6 +601,16 @@ static void iwl_mvm_wake_sw_queue(struct iwl_op_mode *op_mode, int queue) ieee80211_wake_queue(mvm->hw, mq); } +void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state) +{ + if (state) + set_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status); + else + clear_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status); + + wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm)); +} + static void iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state) { struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); @@ -604,7 +620,7 @@ static void iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state) else clear_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status); - wiphy_rfkill_set_hw_state(mvm->hw->wiphy, state); + wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm)); } static void iwl_mvm_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb) diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index 55334d542e26..6a050c69e7d0 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/iwlwifi/mvm/rs.c @@ -3080,3 +3080,29 @@ void iwl_mvm_rate_control_unregister(void) { ieee80211_rate_control_unregister(&rs_mvm_ops); } + +/** + * iwl_mvm_tx_protection - Gets LQ command, change it to enable/disable + * Tx protection, according to this rquest and previous requests, + * and send the LQ command. + * @lq: The LQ command + * @mvmsta: The station + * @enable: Enable Tx protection? + */ +int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, + struct iwl_mvm_sta *mvmsta, bool enable) +{ + lockdep_assert_held(&mvm->mutex); + + if (enable) { + if (mvmsta->tx_protection == 0) + lq->flags |= LQ_FLAG_SET_STA_TLC_RTS_MSK; + mvmsta->tx_protection++; + } else { + mvmsta->tx_protection--; + if (mvmsta->tx_protection == 0) + lq->flags &= ~LQ_FLAG_SET_STA_TLC_RTS_MSK; + } + + return iwl_mvm_send_lq_cmd(mvm, lq, CMD_ASYNC, false); +} diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.h b/drivers/net/wireless/iwlwifi/mvm/rs.h index 219c6857cc0f..f66155a57238 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.h +++ b/drivers/net/wireless/iwlwifi/mvm/rs.h @@ -390,4 +390,9 @@ extern int iwl_mvm_rate_control_register(void); */ extern void iwl_mvm_rate_control_unregister(void); +struct iwl_mvm_sta; + +int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, + struct iwl_mvm_sta *mvmsta, bool enable); + #endif /* __rs__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/rx.c b/drivers/net/wireless/iwlwifi/mvm/rx.c index 4dfc21a3e83e..e4930d5027d2 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/iwlwifi/mvm/rx.c @@ -363,3 +363,25 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, rxb, &rx_status); return 0; } + +/* + * iwl_mvm_rx_statistics - STATISTICS_NOTIFICATION handler + * + * TODO: This handler is implemented partially. + * It only gets the NIC's temperature. + */ +int iwl_mvm_rx_statistics(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_notif_statistics *stats = (void *)&pkt->data; + struct mvm_statistics_general_common *common = &stats->general.common; + + if (mvm->temperature != le32_to_cpu(common->temperature)) { + mvm->temperature = le32_to_cpu(common->temperature); + iwl_mvm_tt_handler(mvm); + } + + return 0; +} diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c index 5c664ed54400..2278858d5658 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/iwlwifi/mvm/sta.c @@ -64,6 +64,7 @@ #include "mvm.h" #include "sta.h" +#include "rs.h" static int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm) { @@ -217,6 +218,8 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm, mvmvif->color); mvm_sta->vif = vif; mvm_sta->max_agg_bufsize = LINK_QUAL_AGG_FRAME_LIMIT_DEF; + mvm_sta->tx_protection = 0; + mvm_sta->tt_tx_protection = false; /* HW restart, don't assume the memory has been zeroed */ atomic_set(&mvm->pending_frames[sta_id], 0); @@ -798,21 +801,23 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif, min(mvmsta->max_agg_bufsize, buf_size); mvmsta->lq_sta.lq.agg_frame_cnt_limit = mvmsta->max_agg_bufsize; + IWL_DEBUG_HT(mvm, "Tx aggregation enabled on ra = %pM tid = %d\n", + sta->addr, tid); + if (mvm->cfg->ht_params->use_rts_for_aggregation) { /* * switch to RTS/CTS if it is the prefer protection * method for HT traffic + * this function also sends the LQ command */ - mvmsta->lq_sta.lq.flags |= LQ_FLAG_SET_STA_TLC_RTS_MSK; + return iwl_mvm_tx_protection(mvm, &mvmsta->lq_sta.lq, + mvmsta, true); /* * TODO: remove the TLC_RTS flag when we tear down the last * AGG session (agg_tids_count in DVM) */ } - IWL_DEBUG_HT(mvm, "Tx aggregation enabled on ra = %pM tid = %d\n", - sta->addr, tid); - return iwl_mvm_send_lq_cmd(mvm, &mvmsta->lq_sta.lq, CMD_ASYNC, false); } diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.h b/drivers/net/wireless/iwlwifi/mvm/sta.h index a4ddce77aaae..3efa0a0cc987 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/iwlwifi/mvm/sta.h @@ -275,6 +275,8 @@ struct iwl_mvm_tid_data { * @lock: lock to protect the whole struct. Since %tid_data is access from Tx * and from Tx response flow, it needs a spinlock. * @tid_data: per tid data. Look at %iwl_mvm_tid_data. + * @tx_protection: reference counter for controlling the Tx protection. + * @tt_tx_protection: is thermal throttling enable Tx protection? * * When mac80211 creates a station it reserves some space (hw->sta_data_size) * in the structure for use by driver. This structure is placed in that @@ -296,6 +298,10 @@ struct iwl_mvm_sta { #ifdef CONFIG_PM_SLEEP u16 last_seq_ctl; #endif + + /* Temporary, until the new TLC will control the Tx protection */ + s8 tx_protection; + bool tt_tx_protection; }; /** diff --git a/drivers/net/wireless/iwlwifi/mvm/tt.c b/drivers/net/wireless/iwlwifi/mvm/tt.c new file mode 100644 index 000000000000..4665fc033c17 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/mvm/tt.c @@ -0,0 +1,509 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2013 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#include "mvm.h" +#include "iwl-config.h" +#include "iwl-io.h" +#include "iwl-csr.h" +#include "iwl-prph.h" + +#define OTP_DTS_DIODE_DEVIATION 96 /*in words*/ +/* VBG - Voltage Band Gap error data (temperature offset) */ +#define OTP_WP_DTS_VBG (OTP_DTS_DIODE_DEVIATION + 2) +#define MEAS_VBG_MIN_VAL 2300 +#define MEAS_VBG_MAX_VAL 3000 +#define MEAS_VBG_DEFAULT_VAL 2700 +#define DTS_DIODE_VALID(flags) (flags & DTS_DIODE_REG_FLAGS_PASS_ONCE) +#define MIN_TEMPERATURE 0 +#define MAX_TEMPERATURE 125 +#define TEMPERATURE_ERROR (MAX_TEMPERATURE + 1) +#define PTAT_DIGITAL_VALUE_MIN_VALUE 0 +#define PTAT_DIGITAL_VALUE_MAX_VALUE 0xFF +#define DTS_VREFS_NUM 5 +static inline u32 DTS_DIODE_GET_VREFS_ID(u32 flags) +{ + return (flags & DTS_DIODE_REG_FLAGS_VREFS_ID) >> + DTS_DIODE_REG_FLAGS_VREFS_ID_POS; +} + +#define CALC_VREFS_MIN_DIFF 43 +#define CALC_VREFS_MAX_DIFF 51 +#define CALC_LUT_SIZE (1 + CALC_VREFS_MAX_DIFF - CALC_VREFS_MIN_DIFF) +#define CALC_LUT_INDEX_OFFSET CALC_VREFS_MIN_DIFF +#define CALC_TEMPERATURE_RESULT_SHIFT_OFFSET 23 + +/* + * @digital_value: The diode's digital-value sampled (temperature/voltage) + * @vref_low: The lower voltage-reference (the vref just below the diode's + * sampled digital-value) + * @vref_high: The higher voltage-reference (the vref just above the diode's + * sampled digital-value) + * @flags: bits[1:0]: The ID of the Vrefs pair (lowVref,highVref) + * bits[6:2]: Reserved. + * bits[7:7]: Indicates completion of at least 1 successful sample + * since last DTS reset. + */ +struct iwl_mvm_dts_diode_bits { + u8 digital_value; + u8 vref_low; + u8 vref_high; + u8 flags; +} __packed; + +union dts_diode_results { + u32 reg_value; + struct iwl_mvm_dts_diode_bits bits; +} __packed; + +static s16 iwl_mvm_dts_get_volt_band_gap(struct iwl_mvm *mvm) +{ + struct iwl_nvm_section calib_sec; + const __le16 *calib; + u16 vbg; + + /* TODO: move parsing to NVM code */ + calib_sec = mvm->nvm_sections[NVM_SECTION_TYPE_CALIBRATION]; + calib = (__le16 *)calib_sec.data; + + vbg = le16_to_cpu(calib[OTP_WP_DTS_VBG]); + + if (vbg < MEAS_VBG_MIN_VAL || vbg > MEAS_VBG_MAX_VAL) + vbg = MEAS_VBG_DEFAULT_VAL; + + return vbg; +} + +static u16 iwl_mvm_dts_get_ptat_deviation_offset(struct iwl_mvm *mvm) +{ + const u8 *calib; + u8 ptat, pa1, pa2, median; + + /* TODO: move parsing to NVM code */ + calib = mvm->nvm_sections[NVM_SECTION_TYPE_CALIBRATION].data; + ptat = calib[OTP_DTS_DIODE_DEVIATION]; + pa1 = calib[OTP_DTS_DIODE_DEVIATION + 1]; + pa2 = calib[OTP_DTS_DIODE_DEVIATION + 2]; + + /* get the median: */ + if (ptat > pa1) { + if (ptat > pa2) + median = (pa1 > pa2) ? pa1 : pa2; + else + median = ptat; + } else { + if (pa1 > pa2) + median = (ptat > pa2) ? ptat : pa2; + else + median = pa1; + } + + return ptat - median; +} + +static u8 iwl_mvm_dts_calibrate_ptat_deviation(struct iwl_mvm *mvm, u8 value) +{ + /* Calibrate the PTAT digital value, based on PTAT deviation data: */ + s16 new_val = value - iwl_mvm_dts_get_ptat_deviation_offset(mvm); + + if (new_val > PTAT_DIGITAL_VALUE_MAX_VALUE) + new_val = PTAT_DIGITAL_VALUE_MAX_VALUE; + else if (new_val < PTAT_DIGITAL_VALUE_MIN_VALUE) + new_val = PTAT_DIGITAL_VALUE_MIN_VALUE; + + return new_val; +} + +static bool dts_get_adjacent_vrefs(struct iwl_mvm *mvm, + union dts_diode_results *avg_ptat) +{ + u8 vrefs_results[DTS_VREFS_NUM]; + u8 low_vref_index = 0, flags; + u32 reg; + + reg = iwl_read_prph(mvm->trans, DTSC_VREF_AVG); + memcpy(vrefs_results, ®, sizeof(reg)); + reg = iwl_read_prph(mvm->trans, DTSC_VREF5_AVG); + vrefs_results[4] = reg & 0xff; + + if (avg_ptat->bits.digital_value < vrefs_results[0] || + avg_ptat->bits.digital_value > vrefs_results[4]) + return false; + + if (avg_ptat->bits.digital_value > vrefs_results[3]) + low_vref_index = 3; + else if (avg_ptat->bits.digital_value > vrefs_results[2]) + low_vref_index = 2; + else if (avg_ptat->bits.digital_value > vrefs_results[1]) + low_vref_index = 1; + + avg_ptat->bits.vref_low = vrefs_results[low_vref_index]; + avg_ptat->bits.vref_high = vrefs_results[low_vref_index + 1]; + flags = avg_ptat->bits.flags; + avg_ptat->bits.flags = + (flags & ~DTS_DIODE_REG_FLAGS_VREFS_ID) | + (low_vref_index & DTS_DIODE_REG_FLAGS_VREFS_ID); + return true; +} + +/* + * return true it the results are valid, and false otherwise. + */ +static bool dts_read_ptat_avg_results(struct iwl_mvm *mvm, + union dts_diode_results *avg_ptat) +{ + u32 reg; + u8 tmp; + + /* fill the diode value and pass_once with avg-reg results */ + reg = iwl_read_prph(mvm->trans, DTSC_PTAT_AVG); + reg &= DTS_DIODE_REG_DIG_VAL | DTS_DIODE_REG_PASS_ONCE; + avg_ptat->reg_value = reg; + + /* calibrate the PTAT digital value */ + tmp = avg_ptat->bits.digital_value; + tmp = iwl_mvm_dts_calibrate_ptat_deviation(mvm, tmp); + avg_ptat->bits.digital_value = tmp; + + /* + * fill vrefs fields, based on the avgVrefs results + * and the diode value + */ + return dts_get_adjacent_vrefs(mvm, avg_ptat) && + DTS_DIODE_VALID(avg_ptat->bits.flags); +} + +static s32 calculate_nic_temperature(union dts_diode_results avg_ptat, + u16 volt_band_gap) +{ + u32 tmp_result; + u8 vrefs_diff; + /* + * For temperature calculation (at the end, shift right by 23) + * LUT[(D2-D1)] = ROUND{ 2^23 / ((D2-D1)*9*10) } + * (D2-D1) == 43 44 45 46 47 48 49 50 51 + */ + static const u16 calc_lut[CALC_LUT_SIZE] = { + 2168, 2118, 2071, 2026, 1983, 1942, 1902, 1864, 1828, + }; + + /* + * The diff between the high and low voltage-references is assumed + * to be strictly be in range of [60,68] + */ + vrefs_diff = avg_ptat.bits.vref_high - avg_ptat.bits.vref_low; + + if (vrefs_diff < CALC_VREFS_MIN_DIFF || + vrefs_diff > CALC_VREFS_MAX_DIFF) + return TEMPERATURE_ERROR; + + /* calculate the result: */ + tmp_result = + vrefs_diff * (DTS_DIODE_GET_VREFS_ID(avg_ptat.bits.flags) + 9); + tmp_result += avg_ptat.bits.digital_value; + tmp_result -= avg_ptat.bits.vref_high; + + /* multiply by the LUT value (based on the diff) */ + tmp_result *= calc_lut[vrefs_diff - CALC_LUT_INDEX_OFFSET]; + + /* + * Get the BandGap (the voltage refereces source) error data + * (temperature offset) + */ + tmp_result *= volt_band_gap; + + /* + * here, tmp_result value can be up to 32-bits. We want to right-shift + * it *without* sign-extend. + */ + tmp_result = tmp_result >> CALC_TEMPERATURE_RESULT_SHIFT_OFFSET; + + /* + * at this point, tmp_result should be in the range: + * 200 <= tmp_result <= 365 + */ + return (s16)tmp_result - 240; +} + +static s32 check_nic_temperature(struct iwl_mvm *mvm) +{ + u16 volt_band_gap; + union dts_diode_results avg_ptat; + + volt_band_gap = iwl_mvm_dts_get_volt_band_gap(mvm); + + /* disable DTS */ + iwl_write_prph(mvm->trans, SHR_MISC_WFM_DTS_EN, 0); + + /* SV initialization */ + iwl_write_prph(mvm->trans, SHR_MISC_WFM_DTS_EN, 1); + iwl_write_prph(mvm->trans, DTSC_CFG_MODE, + DTSC_CFG_MODE_PERIODIC); + + /* wait for results */ + msleep(100); + if (!dts_read_ptat_avg_results(mvm, &avg_ptat)) + return TEMPERATURE_ERROR; + + /* disable DTS */ + iwl_write_prph(mvm->trans, SHR_MISC_WFM_DTS_EN, 0); + + return calculate_nic_temperature(avg_ptat, volt_band_gap); +} + +static void iwl_mvm_enter_ctkill(struct iwl_mvm *mvm) +{ + u32 duration = mvm->thermal_throttle.params->ct_kill_duration; + + IWL_ERR(mvm, "Enter CT Kill\n"); + iwl_mvm_set_hw_ctkill_state(mvm, true); + schedule_delayed_work(&mvm->thermal_throttle.ct_kill_exit, + round_jiffies_relative(duration * HZ)); +} + +static void iwl_mvm_exit_ctkill(struct iwl_mvm *mvm) +{ + IWL_ERR(mvm, "Exit CT Kill\n"); + iwl_mvm_set_hw_ctkill_state(mvm, false); +} + +static void check_exit_ctkill(struct work_struct *work) +{ + struct iwl_mvm_tt_mgmt *tt; + struct iwl_mvm *mvm; + u32 duration; + s32 temp; + + tt = container_of(work, struct iwl_mvm_tt_mgmt, ct_kill_exit.work); + mvm = container_of(tt, struct iwl_mvm, thermal_throttle); + + duration = tt->params->ct_kill_duration; + + iwl_trans_start_hw(mvm->trans); + temp = check_nic_temperature(mvm); + iwl_trans_stop_hw(mvm->trans, false); + + if (temp < MIN_TEMPERATURE || temp > MAX_TEMPERATURE) { + IWL_DEBUG_TEMP(mvm, "Failed to measure NIC temperature\n"); + goto reschedule; + } + IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", temp); + + if (temp <= tt->params->ct_kill_exit) { + iwl_mvm_exit_ctkill(mvm); + return; + } + +reschedule: + schedule_delayed_work(&mvm->thermal_throttle.ct_kill_exit, + round_jiffies(duration * HZ)); +} + +static void iwl_mvm_tt_smps_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm *mvm = _data; + enum ieee80211_smps_mode smps_mode; + + lockdep_assert_held(&mvm->mutex); + + if (mvm->thermal_throttle.dynamic_smps) + smps_mode = IEEE80211_SMPS_DYNAMIC; + else + smps_mode = IEEE80211_SMPS_AUTOMATIC; + + iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_TT, smps_mode); +} + +static void iwl_mvm_tt_tx_protection(struct iwl_mvm *mvm, bool enable) +{ + struct ieee80211_sta *sta; + struct iwl_mvm_sta *mvmsta; + int i, err; + + for (i = 0; i < IWL_MVM_STATION_COUNT; i++) { + sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i], + lockdep_is_held(&mvm->mutex)); + if (IS_ERR_OR_NULL(sta)) + continue; + mvmsta = (void *)sta->drv_priv; + if (enable == mvmsta->tt_tx_protection) + continue; + err = iwl_mvm_tx_protection(mvm, &mvmsta->lq_sta.lq, + mvmsta, enable); + if (err) { + IWL_ERR(mvm, "Failed to %s Tx protection\n", + enable ? "enable" : "disable"); + } else { + IWL_DEBUG_TEMP(mvm, "%s Tx protection\n", + enable ? "Enable" : "Disable"); + mvmsta->tt_tx_protection = enable; + } + } +} + +static void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff) +{ + struct iwl_host_cmd cmd = { + .id = REPLY_THERMAL_MNG_BACKOFF, + .len = { sizeof(u32), }, + .data = { &backoff, }, + .flags = CMD_SYNC, + }; + + if (iwl_mvm_send_cmd(mvm, &cmd) == 0) { + IWL_DEBUG_TEMP(mvm, "Set Thermal Tx backoff to: %u\n", + backoff); + mvm->thermal_throttle.tx_backoff = backoff; + } else { + IWL_ERR(mvm, "Failed to change Thermal Tx backoff\n"); + } +} + +void iwl_mvm_tt_handler(struct iwl_mvm *mvm) +{ + const struct iwl_tt_params *params = mvm->thermal_throttle.params; + struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle; + s32 temperature = mvm->temperature; + int i; + u32 tx_backoff; + + IWL_DEBUG_TEMP(mvm, "NIC temperature: %d\n", mvm->temperature); + + if (params->support_ct_kill && temperature >= params->ct_kill_entry) { + iwl_mvm_enter_ctkill(mvm); + return; + } + + if (params->support_dynamic_smps) { + if (!tt->dynamic_smps && + temperature >= params->dynamic_smps_entry) { + IWL_DEBUG_TEMP(mvm, "Enable dynamic SMPS\n"); + tt->dynamic_smps = true; + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_tt_smps_iterator, mvm); + } else if (tt->dynamic_smps && + temperature <= params->dynamic_smps_exit) { + IWL_DEBUG_TEMP(mvm, "Disable dynamic SMPS\n"); + tt->dynamic_smps = false; + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_tt_smps_iterator, mvm); + } + } + + if (params->support_tx_protection) { + if (temperature >= params->tx_protection_entry) + iwl_mvm_tt_tx_protection(mvm, true); + else if (temperature <= params->tx_protection_exit) + iwl_mvm_tt_tx_protection(mvm, false); + } + + if (params->support_tx_backoff) { + tx_backoff = 0; + for (i = 0; i < TT_TX_BACKOFF_SIZE; i++) { + if (temperature < params->tx_backoff[i].temperature) + break; + tx_backoff = params->tx_backoff[i].backoff; + } + if (tt->tx_backoff != tx_backoff) + iwl_mvm_tt_tx_backoff(mvm, tx_backoff); + } +} + +static const struct iwl_tt_params iwl7000_tt_params = { + .ct_kill_entry = 118, + .ct_kill_exit = 96, + .ct_kill_duration = 5, + .dynamic_smps_entry = 114, + .dynamic_smps_exit = 110, + .tx_protection_entry = 114, + .tx_protection_exit = 108, + .tx_backoff = { + {.temperature = 112, .backoff = 200}, + {.temperature = 113, .backoff = 600}, + {.temperature = 114, .backoff = 1200}, + {.temperature = 115, .backoff = 2000}, + {.temperature = 116, .backoff = 4000}, + {.temperature = 117, .backoff = 10000}, + }, + .support_ct_kill = true, + .support_dynamic_smps = true, + .support_tx_protection = true, + .support_tx_backoff = true, +}; + +void iwl_mvm_tt_initialize(struct iwl_mvm *mvm) +{ + struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle; + + IWL_DEBUG_TEMP(mvm, "Initialize Thermal Throttling\n"); + tt->params = &iwl7000_tt_params; + INIT_DELAYED_WORK(&tt->ct_kill_exit, check_exit_ctkill); +} + +void iwl_mvm_tt_exit(struct iwl_mvm *mvm) +{ + cancel_delayed_work_sync(&mvm->thermal_throttle.ct_kill_exit); + IWL_DEBUG_TEMP(mvm, "Exit Thermal Throttling\n"); +} diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c index 687b34e387ac..c9b44ab4af07 100644 --- a/drivers/net/wireless/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/iwlwifi/mvm/utils.c @@ -471,3 +471,34 @@ int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, return iwl_mvm_send_cmd(mvm, &cmd); } + +/** + * iwl_mvm_update_smps - Get a requst to change the SMPS mode + * @req_type: The part of the driver who call for a change. + * @smps_requests: The request to change the SMPS mode. + * + * Get a requst to change the SMPS mode, + * and change it according to all other requests in the driver. + */ +void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + enum iwl_mvm_smps_type_request req_type, + enum ieee80211_smps_mode smps_request) +{ + struct iwl_mvm_vif *mvmvif; + enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_AUTOMATIC; + int i; + + lockdep_assert_held(&mvm->mutex); + mvmvif = iwl_mvm_vif_from_mac80211(vif); + mvmvif->smps_requests[req_type] = smps_request; + for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) { + if (mvmvif->smps_requests[i] == IEEE80211_SMPS_STATIC) { + smps_mode = IEEE80211_SMPS_STATIC; + break; + } + if (mvmvif->smps_requests[i] == IEEE80211_SMPS_DYNAMIC) + smps_mode = IEEE80211_SMPS_DYNAMIC; + } + + ieee80211_request_smps(vif, smps_mode); +} -- cgit v1.2.3 From 1bd2d1755054c0cc13e2dc7766f2f2ead7d15444 Mon Sep 17 00:00:00 2001 From: Alexander Bondar Date: Tue, 21 May 2013 14:49:09 +0300 Subject: iwlwifi: mvm: Change number of DTIMs to skip semantics If skip over DTIMs is enabled the driver can specify number of DTIMs to skip. This parameter in host-device API implies number of DTIM periods to skip. For example, to skip one DTIM means sleep over two DTIM periods. Change semantics accordingly. Change this parameter's default value. Signed-off-by: Alexander Bondar Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/mvm/fw-api-power.h | 6 ++++-- drivers/net/wireless/iwlwifi/mvm/power.c | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h index b6bdfd36bebe..d8e19290b0f3 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h @@ -101,7 +101,9 @@ enum iwl_power_flags { * @tx_data_timeout: Minimum time (usec) from last Tx packet for AM to * PSM transition - legacy PM * @sleep_interval: not in use - * @num_skip_dtim: Number of DTIMs to skip if Skip over DTIM flag is set + * @skip_dtim_periods: Number of DTIM periods to skip if Skip over DTIM flag + * is set. For example, if it is required to skip over + * one DTIM, this value need to be set to 2 (DTIM periods). * @lprx_rssi_threshold: Signal strength up to which LP RX can be enabled. * Default: 80dbm */ @@ -113,7 +115,7 @@ struct iwl_powertable_cmd { __le32 rx_data_timeout; __le32 tx_data_timeout; __le32 sleep_interval[IWL_POWER_VEC_SIZE]; - __le32 num_skip_dtim; + __le32 skip_dtim_periods; __le32 lprx_rssi_threshold; } __packed; diff --git a/drivers/net/wireless/iwlwifi/mvm/power.c b/drivers/net/wireless/iwlwifi/mvm/power.c index c818d6d6aee6..516e64e7706f 100644 --- a/drivers/net/wireless/iwlwifi/mvm/power.c +++ b/drivers/net/wireless/iwlwifi/mvm/power.c @@ -139,8 +139,8 @@ static void iwl_mvm_power_log(struct iwl_mvm *mvm, IWL_DEBUG_POWER(mvm, "LP RX RSSI threshold = %u\n", cmd->lprx_rssi_threshold); if (cmd->flags & cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) - IWL_DEBUG_POWER(mvm, "DTIMs to skip = %u\n", - le32_to_cpu(cmd->num_skip_dtim)); + IWL_DEBUG_POWER(mvm, "DTIM periods to skip = %u\n", + le32_to_cpu(cmd->skip_dtim_periods)); } } @@ -188,7 +188,7 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP || mvm->cur_ucode == IWL_UCODE_WOWLAN)) { cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK); - cmd->num_skip_dtim = cpu_to_le32(2); + cmd->skip_dtim_periods = cpu_to_le32(3); } /* Check that keep alive period is at least 3 * DTIM */ -- cgit v1.2.3 From 9ce4fa7291ff797f04849075a2ee3cf60f752aee Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 22 May 2013 13:16:23 +0300 Subject: iwlwifi: take valid ant for HT caps from TLV I forgot to take them from TLV and took them from the NVM instead. Signed-off-by: Emmanuel Grumbach Reviewed-by: Ilan Peer Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c | 15 ++++++++------- drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h | 3 ++- drivers/net/wireless/iwlwifi/iwl-nvm-parse.c | 14 ++++++++------ drivers/net/wireless/iwlwifi/iwl-nvm-parse.h | 2 +- drivers/net/wireless/iwlwifi/mvm/nvm.c | 4 +++- 5 files changed, 22 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c index 600c9fdd7f71..4c887f365908 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c @@ -732,17 +732,16 @@ int iwl_init_sband_channels(struct iwl_nvm_data *data, void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg, struct iwl_nvm_data *data, struct ieee80211_sta_ht_cap *ht_info, - enum ieee80211_band band) + enum ieee80211_band band, + u8 tx_chains, u8 rx_chains) { int max_bit_rate = 0; - u8 rx_chains; - u8 tx_chains; - tx_chains = hweight8(data->valid_tx_ant); + tx_chains = hweight8(tx_chains); if (cfg->rx_with_siso_diversity) rx_chains = 1; else - rx_chains = hweight8(data->valid_rx_ant); + rx_chains = hweight8(rx_chains); if (!(data->sku_cap_11n_enable) || !cfg->ht_params) { ht_info->ht_supported = false; @@ -806,7 +805,8 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, sband->n_bitrates = N_RATES_24; n_used += iwl_init_sband_channels(data, sband, n_channels, IEEE80211_BAND_2GHZ); - iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_2GHZ); + iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_2GHZ, + data->valid_tx_ant, data->valid_rx_ant); sband = &data->bands[IEEE80211_BAND_5GHZ]; sband->band = IEEE80211_BAND_5GHZ; @@ -814,7 +814,8 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, sband->n_bitrates = N_RATES_52; n_used += iwl_init_sband_channels(data, sband, n_channels, IEEE80211_BAND_5GHZ); - iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ); + iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ, + data->valid_tx_ant, data->valid_rx_ant); if (n_channels != n_used) IWL_ERR_DEV(dev, "EEPROM: used only %d of %d channels\n", diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h index 37f115390b19..d73304a23ec2 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h @@ -133,6 +133,7 @@ int iwl_init_sband_channels(struct iwl_nvm_data *data, void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg, struct iwl_nvm_data *data, struct ieee80211_sta_ht_cap *ht_info, - enum ieee80211_band band); + enum ieee80211_band band, + u8 tx_chains, u8 rx_chains); #endif /* __iwl_eeprom_parse_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c index 8f2a4e922346..acd2665afb8c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c @@ -292,7 +292,7 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg, static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, struct iwl_nvm_data *data, const __le16 *nvm_sw, - bool enable_vht) + bool enable_vht, u8 tx_chains, u8 rx_chains) { int n_channels = iwl_init_channel_map(dev, cfg, data, &nvm_sw[NVM_CHANNELS]); @@ -305,7 +305,8 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, sband->n_bitrates = N_RATES_24; n_used += iwl_init_sband_channels(data, sband, n_channels, IEEE80211_BAND_2GHZ); - iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_2GHZ); + iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_2GHZ, + tx_chains, rx_chains); sband = &data->bands[IEEE80211_BAND_5GHZ]; sband->band = IEEE80211_BAND_5GHZ; @@ -313,7 +314,8 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, sband->n_bitrates = N_RATES_52; n_used += iwl_init_sband_channels(data, sband, n_channels, IEEE80211_BAND_5GHZ); - iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ); + iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ, + tx_chains, rx_chains); if (enable_vht) iwl_init_vht_hw_capab(cfg, data, &sband->vht_cap); @@ -325,7 +327,7 @@ static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, struct iwl_nvm_data * iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, const __le16 *nvm_hw, const __le16 *nvm_sw, - const __le16 *nvm_calib) + const __le16 *nvm_calib, u8 tx_chains, u8 rx_chains) { struct iwl_nvm_data *data; u8 hw_addr[ETH_ALEN]; @@ -381,8 +383,8 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, data->hw_addr[4] = hw_addr[5]; data->hw_addr[5] = hw_addr[4]; - iwl_init_sbands(dev, cfg, data, nvm_sw, - sku & NVM_SKU_CAP_11AC_ENABLE); + iwl_init_sbands(dev, cfg, data, nvm_sw, sku & NVM_SKU_CAP_11AC_ENABLE, + tx_chains, rx_chains); data->calib_version = 255; /* TODO: this value will prevent some checks from diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h index e57fb989661e..3325059c52d4 100644 --- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h +++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h @@ -75,6 +75,6 @@ struct iwl_nvm_data * iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, const __le16 *nvm_hw, const __le16 *nvm_sw, - const __le16 *nvm_calib); + const __le16 *nvm_calib, u8 tx_chains, u8 rx_chains); #endif /* __iwl_nvm_parse_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c index 2cd669c3c47a..edb94ea31654 100644 --- a/drivers/net/wireless/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/iwlwifi/mvm/nvm.c @@ -251,7 +251,9 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm) hw = (const __le16 *)sections[NVM_SECTION_TYPE_HW].data; sw = (const __le16 *)sections[NVM_SECTION_TYPE_SW].data; calib = (const __le16 *)sections[NVM_SECTION_TYPE_CALIBRATION].data; - return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib); + return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib, + iwl_fw_valid_tx_ant(mvm->fw), + iwl_fw_valid_rx_ant(mvm->fw)); } #define MAX_NVM_FILE_LEN 16384 -- cgit v1.2.3 From d64048edcd714095a5cf2d7019769d0d94e19892 Mon Sep 17 00:00:00 2001 From: Hila Gonen Date: Wed, 13 Mar 2013 18:00:03 +0200 Subject: iwlwifi: mvm: Add support for connection monitor offload The firmware supports periodic keep alive and beacon monitoring, so advertise connection monitor offload capability by setting IEEE80211_HW_CONNECTION_MONITOR flag. Implement missed beacons notification handler. Call ieee80211_beacon_loss in case of missed beacons, so AP probing by mac80211 can be triggered. Signed-off-by: Hila Gonen Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/mvm/fw-api.h | 20 ++++++++++++++++++++ drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c | 25 +++++++++++++++++++++++++ drivers/net/wireless/iwlwifi/mvm/mac80211.c | 3 ++- drivers/net/wireless/iwlwifi/mvm/mvm.h | 3 +++ drivers/net/wireless/iwlwifi/mvm/ops.c | 4 ++++ 5 files changed, 54 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h index db6f47406ac2..cbfb3beae783 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h @@ -164,6 +164,8 @@ enum { CARD_STATE_CMD = 0xa0, CARD_STATE_NOTIFICATION = 0xa1, + MISSED_BEACONS_NOTIFICATION = 0xa2, + REPLY_RX_PHY_CMD = 0xc0, REPLY_RX_MPDU_CMD = 0xc1, BA_NOTIF = 0xc5, @@ -942,6 +944,24 @@ struct iwl_card_state_notif { __le32 flags; } __packed; /* CARD_STATE_NTFY_API_S_VER_1 */ +/** + * struct iwl_missed_beacons_notif - information on missed beacons + * ( MISSED_BEACONS_NOTIFICATION = 0xa2 ) + * @mac_id: interface ID + * @consec_missed_beacons_since_last_rx: number of consecutive missed + * beacons since last RX. + * @consec_missed_beacons: number of consecutive missed beacons + * @num_expected_beacons: + * @num_recvd_beacons: + */ +struct iwl_missed_beacons_notif { + __le32 mac_id; + __le32 consec_missed_beacons_since_last_rx; + __le32 consec_missed_beacons; + __le32 num_expected_beacons; + __le32 num_recvd_beacons; +} __packed; /* MISSED_BEACON_NTFY_API_S_VER_3 */ + /** * struct iwl_set_calib_default_cmd - set default value for calibration. * ( SET_CALIB_DEFAULT_CMD = 0x8e ) diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c index dc5f4ef50198..b4e8e597d2b7 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c @@ -1050,3 +1050,28 @@ int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm, rate); return 0; } + +static void iwl_mvm_beacon_loss_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + u16 *id = _data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + if (mvmvif->id == *id) + ieee80211_beacon_loss(vif); +} + +int iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_missed_beacons_notif *missed_beacons = (void *)pkt->data; + u16 id = (u16)le32_to_cpu(missed_beacons->mac_id); + + ieee80211_iterate_active_interfaces_atomic(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_beacon_loss_iterator, + &id); + return 0; +} diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index c26f6b504460..dc50020d746d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -152,7 +152,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_DYNAMIC_PS | IEEE80211_HW_AMPDU_AGGREGATION | - IEEE80211_HW_TIMING_BEACON_ONLY; + IEEE80211_HW_TIMING_BEACON_ONLY | + IEEE80211_HW_CONNECTION_MONITOR; hw->queues = IWL_MVM_FIRST_AGG_QUEUE; hw->offchannel_tx_hw_queue = IWL_MVM_OFFCHANNEL_QUEUE; diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index a288552491b3..0e7450372f2c 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -552,6 +552,9 @@ int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm, int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd); +int iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd); /* Bindings */ int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif); diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 8431637e38f2..d7a199b1cdef 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -227,6 +227,9 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { RX_HANDLER(RADIO_VERSION_NOTIFICATION, iwl_mvm_rx_radio_ver, false), RX_HANDLER(CARD_STATE_NOTIFICATION, iwl_mvm_rx_card_state_notif, false), + RX_HANDLER(MISSED_BEACONS_NOTIFICATION, iwl_mvm_rx_missed_beacons_notif, + false), + RX_HANDLER(REPLY_ERROR, iwl_mvm_rx_fw_error, false), }; #undef RX_HANDLER @@ -289,6 +292,7 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = { CMD(NET_DETECT_HOTSPOTS_CMD), CMD(NET_DETECT_HOTSPOTS_QUERY_CMD), CMD(CARD_STATE_NOTIFICATION), + CMD(MISSED_BEACONS_NOTIFICATION), CMD(BT_COEX_PRIO_TABLE), CMD(BT_COEX_PROT_ENV), CMD(BT_PROFILE_NOTIFICATION), -- cgit v1.2.3 From b571a69745dc90aec3d0505a7ceac2702a93861b Mon Sep 17 00:00:00 2001 From: Alexander Bondar Date: Tue, 21 May 2013 14:49:09 +0300 Subject: iwlwifi: mvm: add debugfs for powersave Add debugfs files to control powersave parameters for testing. Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/mvm/debugfs.c | 337 +++++++++++++++++++++++++++++ drivers/net/wireless/iwlwifi/mvm/mvm.h | 65 ++++++ drivers/net/wireless/iwlwifi/mvm/power.c | 66 ++++++ 3 files changed, 468 insertions(+) diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c index 95871b2d63d1..69e0806075a2 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c @@ -300,6 +300,146 @@ static ssize_t iwl_dbgfs_power_down_d3_allow_write(struct file *file, return count; } +static void iwl_dbgfs_update_pm(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + enum iwl_dbgfs_pm_mask param, int val) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_dbgfs_pm *dbgfs_pm = &mvmvif->dbgfs_pm; + + dbgfs_pm->mask |= param; + + switch (param) { + case MVM_DEBUGFS_PM_KEEP_ALIVE: { + struct ieee80211_hw *hw = mvm->hw; + int dtimper = hw->conf.ps_dtim_period ?: 1; + int dtimper_msec = dtimper * vif->bss_conf.beacon_int; + + IWL_DEBUG_POWER(mvm, "debugfs: set keep_alive= %d sec\n", val); + if (val * MSEC_PER_SEC < 3 * dtimper_msec) { + IWL_WARN(mvm, + "debugfs: keep alive period (%ld msec) is less than minimum required (%d msec)\n", + val * MSEC_PER_SEC, 3 * dtimper_msec); + } + dbgfs_pm->keep_alive_seconds = val; + break; + } + case MVM_DEBUGFS_PM_SKIP_OVER_DTIM: + IWL_DEBUG_POWER(mvm, "skip_over_dtim %s\n", + val ? "enabled" : "disabled"); + dbgfs_pm->skip_over_dtim = val; + break; + case MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS: + IWL_DEBUG_POWER(mvm, "skip_dtim_periods=%d\n", val); + dbgfs_pm->skip_dtim_periods = val; + break; + case MVM_DEBUGFS_PM_RX_DATA_TIMEOUT: + IWL_DEBUG_POWER(mvm, "rx_data_timeout=%d\n", val); + dbgfs_pm->rx_data_timeout = val; + break; + case MVM_DEBUGFS_PM_TX_DATA_TIMEOUT: + IWL_DEBUG_POWER(mvm, "tx_data_timeout=%d\n", val); + dbgfs_pm->tx_data_timeout = val; + break; + case MVM_DEBUGFS_PM_DISABLE_POWER_OFF: + IWL_DEBUG_POWER(mvm, "disable_power_off=%d\n", val); + dbgfs_pm->disable_power_off = val; + break; + } +} + +static ssize_t iwl_dbgfs_pm_params_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_vif *vif = file->private_data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->dbgfs_data; + enum iwl_dbgfs_pm_mask param; + char buf[32] = {}; + int val; + int ret; + + if (copy_from_user(buf, user_buf, sizeof(buf))) + return -EFAULT; + + if (!strncmp("keep_alive=", buf, 11)) { + if (sscanf(buf + 11, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_KEEP_ALIVE; + } else if (!strncmp("skip_over_dtim=", buf, 15)) { + if (sscanf(buf + 15, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_SKIP_OVER_DTIM; + } else if (!strncmp("skip_dtim_periods=", buf, 18)) { + if (sscanf(buf + 18, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS; + } else if (!strncmp("rx_data_timeout=", buf, 16)) { + if (sscanf(buf + 16, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_RX_DATA_TIMEOUT; + } else if (!strncmp("tx_data_timeout=", buf, 16)) { + if (sscanf(buf + 16, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_TX_DATA_TIMEOUT; + } else if (!strncmp("disable_power_off=", buf, 18)) { + if (sscanf(buf + 18, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_DISABLE_POWER_OFF; + } else { + return -EINVAL; + } + + mutex_lock(&mvm->mutex); + iwl_dbgfs_update_pm(mvm, vif, param, val); + ret = iwl_mvm_power_update_mode(mvm, vif); + mutex_unlock(&mvm->mutex); + + return ret ?: count; +} + +static ssize_t iwl_dbgfs_pm_params_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_vif *vif = file->private_data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->dbgfs_data; + struct iwl_powertable_cmd cmd = {}; + char buf[256]; + int bufsz = sizeof(buf); + int pos = 0; + + iwl_mvm_power_build_cmd(mvm, vif, &cmd); + + pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off = %d\n", + (cmd.flags & + cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK)) ? + 0 : 1); + pos += scnprintf(buf+pos, bufsz-pos, "skip_dtim_periods = %d\n", + le32_to_cpu(cmd.skip_dtim_periods)); + pos += scnprintf(buf+pos, bufsz-pos, "power_scheme = %d\n", + iwlmvm_mod_params.power_scheme); + pos += scnprintf(buf+pos, bufsz-pos, "flags = %d\n", + le16_to_cpu(cmd.flags)); + pos += scnprintf(buf+pos, bufsz-pos, "keep_alive = %d\n", + cmd.keep_alive_seconds); + + if (cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) { + pos += scnprintf(buf+pos, bufsz-pos, "skip_over_dtim = %d\n", + (cmd.flags & + cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) ? + 1 : 0); + pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout = %d\n", + le32_to_cpu(cmd.rx_data_timeout)); + pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout = %d\n", + le32_to_cpu(cmd.tx_data_timeout)); + } + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + static ssize_t iwl_dbgfs_mac_params_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -481,6 +621,191 @@ static ssize_t iwl_dbgfs_fw_restart_write(struct file *file, return count; } +static void iwl_dbgfs_update_bf(struct ieee80211_vif *vif, + enum iwl_dbgfs_bf_mask param, int value) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf; + + dbgfs_bf->mask |= param; + + switch (param) { + case MVM_DEBUGFS_BF_ENERGY_DELTA: + dbgfs_bf->bf_energy_delta = value; + break; + case MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA: + dbgfs_bf->bf_roaming_energy_delta = value; + break; + case MVM_DEBUGFS_BF_ROAMING_STATE: + dbgfs_bf->bf_roaming_state = value; + break; + case MVM_DEBUGFS_BF_TEMPERATURE_DELTA: + dbgfs_bf->bf_temperature_delta = value; + break; + case MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER: + dbgfs_bf->bf_enable_beacon_filter = value; + break; + case MVM_DEBUGFS_BF_DEBUG_FLAG: + dbgfs_bf->bf_debug_flag = value; + break; + case MVM_DEBUGFS_BF_ESCAPE_TIMER: + dbgfs_bf->bf_escape_timer = value; + break; + case MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT: + dbgfs_bf->ba_enable_beacon_abort = value; + break; + case MVM_DEBUGFS_BA_ESCAPE_TIMER: + dbgfs_bf->ba_escape_timer = value; + break; + } +} + +static ssize_t iwl_dbgfs_bf_params_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_vif *vif = file->private_data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->dbgfs_data; + enum iwl_dbgfs_bf_mask param; + char buf[256]; + int buf_size; + int value; + int ret = 0; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + if (!strncmp("bf_energy_delta=", buf, 16)) { + if (sscanf(buf+16, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BF_ENERGY_DELTA_MIN || + value > IWL_BF_ENERGY_DELTA_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BF_ENERGY_DELTA; + } else if (!strncmp("bf_roaming_energy_delta=", buf, 24)) { + if (sscanf(buf+24, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BF_ROAMING_ENERGY_DELTA_MIN || + value > IWL_BF_ROAMING_ENERGY_DELTA_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA; + } else if (!strncmp("bf_roaming_state=", buf, 17)) { + if (sscanf(buf+17, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BF_ROAMING_STATE_MIN || + value > IWL_BF_ROAMING_STATE_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BF_ROAMING_STATE; + } else if (!strncmp("bf_temperature_delta=", buf, 21)) { + if (sscanf(buf+21, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BF_TEMPERATURE_DELTA_MIN || + value > IWL_BF_TEMPERATURE_DELTA_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BF_TEMPERATURE_DELTA; + } else if (!strncmp("bf_enable_beacon_filter=", buf, 24)) { + if (sscanf(buf+24, "%d", &value) != 1) + return -EINVAL; + if (value < 0 || value > 1) + return -EINVAL; + param = MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER; + } else if (!strncmp("bf_debug_flag=", buf, 14)) { + if (sscanf(buf+14, "%d", &value) != 1) + return -EINVAL; + if (value < 0 || value > 1) + return -EINVAL; + param = MVM_DEBUGFS_BF_DEBUG_FLAG; + } else if (!strncmp("bf_escape_timer=", buf, 16)) { + if (sscanf(buf+16, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BF_ESCAPE_TIMER_MIN || + value > IWL_BF_ESCAPE_TIMER_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BF_ESCAPE_TIMER; + } else if (!strncmp("ba_escape_timer=", buf, 16)) { + if (sscanf(buf+16, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BA_ESCAPE_TIMER_MIN || + value > IWL_BA_ESCAPE_TIMER_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BA_ESCAPE_TIMER; + } else if (!strncmp("ba_enable_beacon_abort=", buf, 23)) { + if (sscanf(buf+23, "%d", &value) != 1) + return -EINVAL; + if (value < 0 || value > 1) + return -EINVAL; + param = MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT; + } else { + return -EINVAL; + } + + mutex_lock(&mvm->mutex); + iwl_dbgfs_update_bf(vif, param, value); + if (param == MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER && !value) { + ret = iwl_mvm_disable_beacon_filter(mvm, vif); + } else { + if (mvmvif->bf_enabled) + ret = iwl_mvm_enable_beacon_filter(mvm, vif); + else + ret = iwl_mvm_disable_beacon_filter(mvm, vif); + } + mutex_unlock(&mvm->mutex); + + return ret ?: count; +} + +static ssize_t iwl_dbgfs_bf_params_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_vif *vif = file->private_data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + char buf[256]; + int pos = 0; + const size_t bufsz = sizeof(buf); + struct iwl_beacon_filter_cmd cmd = { + .bf_energy_delta = IWL_BF_ENERGY_DELTA_DEFAULT, + .bf_roaming_energy_delta = IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT, + .bf_roaming_state = IWL_BF_ROAMING_STATE_DEFAULT, + .bf_temperature_delta = IWL_BF_TEMPERATURE_DELTA_DEFAULT, + .bf_enable_beacon_filter = IWL_BF_ENABLE_BEACON_FILTER_DEFAULT, + .bf_debug_flag = IWL_BF_DEBUG_FLAG_DEFAULT, + .bf_escape_timer = cpu_to_le32(IWL_BF_ESCAPE_TIMER_DEFAULT), + .ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_DEFAULT), + .ba_enable_beacon_abort = IWL_BA_ENABLE_BEACON_ABORT_DEFAULT, + }; + + iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd); + if (mvmvif->bf_enabled) + cmd.bf_enable_beacon_filter = 1; + else + cmd.bf_enable_beacon_filter = 0; + + pos += scnprintf(buf+pos, bufsz-pos, "bf_energy_delta = %d\n", + cmd.bf_energy_delta); + pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_energy_delta = %d\n", + cmd.bf_roaming_energy_delta); + pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_state = %d\n", + cmd.bf_roaming_state); + pos += scnprintf(buf+pos, bufsz-pos, "bf_temperature_delta = %d\n", + cmd.bf_temperature_delta); + pos += scnprintf(buf+pos, bufsz-pos, "bf_enable_beacon_filter = %d\n", + cmd.bf_enable_beacon_filter); + pos += scnprintf(buf+pos, bufsz-pos, "bf_debug_flag = %d\n", + cmd.bf_debug_flag); + pos += scnprintf(buf+pos, bufsz-pos, "bf_escape_timer = %d\n", + cmd.bf_escape_timer); + pos += scnprintf(buf+pos, bufsz-pos, "ba_escape_timer = %d\n", + cmd.ba_escape_timer); + pos += scnprintf(buf+pos, bufsz-pos, "ba_enable_beacon_abort = %d\n", + cmd.ba_enable_beacon_abort); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + #ifdef CONFIG_PM_SLEEP static ssize_t iwl_dbgfs_d3_sram_write(struct file *file, const char __user *user_buf, @@ -594,6 +919,8 @@ MVM_DEBUGFS_READ_WRITE_FILE_OPS(d3_sram); /* Interface specific debugfs entries */ MVM_DEBUGFS_READ_FILE_OPS(mac_params); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(pm_params); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(bf_params); int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) { @@ -647,9 +974,19 @@ void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif) return; } + if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM && + vif->type == NL80211_IFTYPE_STATION && !vif->p2p) + MVM_DEBUGFS_ADD_FILE_VIF(pm_params, mvmvif->dbgfs_dir, S_IWUSR | + S_IRUSR); + MVM_DEBUGFS_ADD_FILE_VIF(mac_params, mvmvif->dbgfs_dir, S_IRUSR); + if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p && + mvmvif == mvm->bf_allowed_vif) + MVM_DEBUGFS_ADD_FILE_VIF(bf_params, mvmvif->dbgfs_dir, + S_IRUSR | S_IWUSR); + /* * Create symlink for convenience pointing to interface specific * debugfs entries for the driver. For example, under diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index 0e7450372f2c..6a3220b6273e 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -150,6 +150,54 @@ enum iwl_power_scheme { #define IWL_CONN_MAX_LISTEN_INTERVAL 70 +#ifdef CONFIG_IWLWIFI_DEBUGFS +enum iwl_dbgfs_pm_mask { + MVM_DEBUGFS_PM_KEEP_ALIVE = BIT(0), + MVM_DEBUGFS_PM_SKIP_OVER_DTIM = BIT(1), + MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS = BIT(2), + MVM_DEBUGFS_PM_RX_DATA_TIMEOUT = BIT(3), + MVM_DEBUGFS_PM_TX_DATA_TIMEOUT = BIT(4), + MVM_DEBUGFS_PM_DISABLE_POWER_OFF = BIT(5), +}; + +struct iwl_dbgfs_pm { + u8 keep_alive_seconds; + u32 rx_data_timeout; + u32 tx_data_timeout; + bool skip_over_dtim; + u8 skip_dtim_periods; + bool disable_power_off; + int mask; +}; + +/* beacon filtering */ + +enum iwl_dbgfs_bf_mask { + MVM_DEBUGFS_BF_ENERGY_DELTA = BIT(0), + MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA = BIT(1), + MVM_DEBUGFS_BF_ROAMING_STATE = BIT(2), + MVM_DEBUGFS_BF_TEMPERATURE_DELTA = BIT(3), + MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER = BIT(4), + MVM_DEBUGFS_BF_DEBUG_FLAG = BIT(5), + MVM_DEBUGFS_BF_ESCAPE_TIMER = BIT(6), + MVM_DEBUGFS_BA_ESCAPE_TIMER = BIT(7), + MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT = BIT(8), +}; + +struct iwl_dbgfs_bf { + u8 bf_energy_delta; + u8 bf_roaming_energy_delta; + u8 bf_roaming_state; + u8 bf_temperature_delta; + u8 bf_enable_beacon_filter; + u8 bf_debug_flag; + u32 bf_escape_timer; + u32 ba_escape_timer; + u8 ba_enable_beacon_abort; + int mask; +}; +#endif + enum iwl_mvm_smps_type_request { IWL_MVM_SMPS_REQ_BT_COEX, IWL_MVM_SMPS_REQ_TT, @@ -225,6 +273,8 @@ struct iwl_mvm_vif { struct dentry *dbgfs_dir; struct dentry *dbgfs_slink; void *dbgfs_data; + struct iwl_dbgfs_pm dbgfs_pm; + struct iwl_dbgfs_bf dbgfs_bf; #endif enum ieee80211_smps_mode smps_requests[NUM_IWL_MVM_SMPS_REQ]; @@ -631,6 +681,21 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, void iwl_mvm_bt_coex_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif); /* beacon filtering */ +#ifdef CONFIG_IWLWIFI_DEBUGFS +void +iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif, + struct iwl_beacon_filter_cmd *cmd); +int iwl_mvm_dbgfs_set_fw_dbg_log(struct iwl_mvm *mvm); +#else +static inline void +iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif, + struct iwl_beacon_filter_cmd *cmd) +{} +static inline int iwl_mvm_dbgfs_set_fw_dbg_log(struct iwl_mvm *mvm) +{ + return 0; +} +#endif int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm, diff --git a/drivers/net/wireless/iwlwifi/mvm/power.c b/drivers/net/wireless/iwlwifi/mvm/power.c index 516e64e7706f..67cf24aa72f9 100644 --- a/drivers/net/wireless/iwlwifi/mvm/power.c +++ b/drivers/net/wireless/iwlwifi/mvm/power.c @@ -119,6 +119,7 @@ static int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm, if (!mvmvif->bf_enabled) return 0; + iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd); return iwl_mvm_beacon_filter_send_cmd(mvm, &cmd); } @@ -153,6 +154,8 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, int dtimper, dtimper_msec; int keep_alive; bool radar_detect = false; + struct iwl_mvm_vif *mvmvif __maybe_unused = + iwl_mvm_vif_from_mac80211(vif); /* * Regardless of power management state the driver must set @@ -166,6 +169,11 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK); +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF && + mvmvif->dbgfs_pm.disable_power_off) + cmd->flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK); +#endif if (!vif->bss_conf.ps) return; @@ -205,6 +213,28 @@ void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, cmd->rx_data_timeout = cpu_to_le32(10 * USEC_PER_MSEC); cmd->tx_data_timeout = cpu_to_le32(10 * USEC_PER_MSEC); } + +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_KEEP_ALIVE) + cmd->keep_alive_seconds = mvmvif->dbgfs_pm.keep_alive_seconds; + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_OVER_DTIM) { + if (mvmvif->dbgfs_pm.skip_over_dtim) + cmd->flags |= + cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK); + else + cmd->flags &= + cpu_to_le16(~POWER_FLAGS_SKIP_OVER_DTIM_MSK); + } + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_RX_DATA_TIMEOUT) + cmd->rx_data_timeout = + cpu_to_le32(mvmvif->dbgfs_pm.rx_data_timeout); + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_TX_DATA_TIMEOUT) + cmd->tx_data_timeout = + cpu_to_le32(mvmvif->dbgfs_pm.tx_data_timeout); + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS) + cmd->skip_dtim_periods = + cpu_to_le32(mvmvif->dbgfs_pm.skip_dtim_periods); +#endif /* CONFIG_IWLWIFI_DEBUGFS */ } int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif) @@ -233,6 +263,8 @@ int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif) int iwl_mvm_power_disable(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { struct iwl_powertable_cmd cmd = {}; + struct iwl_mvm_vif *mvmvif __maybe_unused = + iwl_mvm_vif_from_mac80211(vif); if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) return 0; @@ -240,12 +272,45 @@ int iwl_mvm_power_disable(struct iwl_mvm *mvm, struct ieee80211_vif *vif) if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM) cmd.flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK); +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF && + mvmvif->dbgfs_pm.disable_power_off) + cmd.flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK); +#endif iwl_mvm_power_log(mvm, &cmd); return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_ASYNC, sizeof(cmd), &cmd); } +#ifdef CONFIG_IWLWIFI_DEBUGFS +void +iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif, + struct iwl_beacon_filter_cmd *cmd) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf; + + if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ENERGY_DELTA) + cmd->bf_energy_delta = dbgfs_bf->bf_energy_delta; + if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA) + cmd->bf_roaming_energy_delta = + dbgfs_bf->bf_roaming_energy_delta; + if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_STATE) + cmd->bf_roaming_state = dbgfs_bf->bf_roaming_state; + if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMPERATURE_DELTA) + cmd->bf_temperature_delta = dbgfs_bf->bf_temperature_delta; + if (dbgfs_bf->mask & MVM_DEBUGFS_BF_DEBUG_FLAG) + cmd->bf_debug_flag = dbgfs_bf->bf_debug_flag; + if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ESCAPE_TIMER) + cmd->bf_escape_timer = cpu_to_le32(dbgfs_bf->bf_escape_timer); + if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ESCAPE_TIMER) + cmd->ba_escape_timer = cpu_to_le32(dbgfs_bf->ba_escape_timer); + if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT) + cmd->ba_enable_beacon_abort = dbgfs_bf->ba_enable_beacon_abort; +} +#endif + int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { @@ -260,6 +325,7 @@ int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, vif->type != NL80211_IFTYPE_STATION || vif->p2p) return 0; + iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd); ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd); if (!ret) -- cgit v1.2.3 From e057d3c31bdf87616b415c4b2cbf7310f54b9219 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Tue, 28 May 2013 13:01:52 +0200 Subject: cfg80211: support an active monitor interface flag An active monitor interface is one that is used for communication (via injection). It is expected to ACK incoming unicast packets. This is useful for running various 802.11 testing utilities that associate to an AP via injection and manage the state in user space. Signed-off-by: Felix Fietkau Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 ++ include/uapi/linux/nl80211.h | 4 ++++ net/wireless/nl80211.c | 10 ++++++++++ 3 files changed, 16 insertions(+) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index b3b076a46d50..13b247d26544 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -961,6 +961,7 @@ struct station_info { * @MONITOR_FLAG_CONTROL: pass control frames * @MONITOR_FLAG_OTHER_BSS: disable BSSID filtering * @MONITOR_FLAG_COOK_FRAMES: report frames after processing + * @MONITOR_FLAG_ACTIVE: active monitor, ACKs frames on its MAC address */ enum monitor_flags { MONITOR_FLAG_FCSFAIL = 1<wiphy.features & NL80211_FEATURE_ACTIVE_MONITOR)) + return -EOPNOTSUPP; + if (change) err = cfg80211_change_iface(rdev, dev, ntype, flags, ¶ms); else @@ -2395,6 +2400,11 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ? info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL, &flags); + + if (!err && (flags & NL80211_MNTR_FLAG_ACTIVE) && + !(rdev->wiphy.features & NL80211_FEATURE_ACTIVE_MONITOR)) + return -EOPNOTSUPP; + wdev = rdev_add_virtual_intf(rdev, nla_data(info->attrs[NL80211_ATTR_IFNAME]), type, err ? NULL : &flags, ¶ms); -- cgit v1.2.3 From 31eba5bc56a9324f056d28569a4f89f39c1c3f70 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Tue, 28 May 2013 13:01:53 +0200 Subject: mac80211: support active monitor interfaces Support them only if the driver advertises support for them via IEEE80211_HW_SUPPORTS_ACTIVE_MONITOR. Unlike normal monitor interfaces, they are added to the driver, along with their MAC address. Signed-off-by: Felix Fietkau Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 11 +++++++---- net/mac80211/driver-ops.h | 3 ++- net/mac80211/iface.c | 29 +++++++++++++++++++++++------ net/mac80211/util.c | 6 ++++++ 4 files changed, 38 insertions(+), 11 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 232edf78d5a9..9034da16cf1b 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -73,16 +73,19 @@ static int ieee80211_change_iface(struct wiphy *wiphy, struct ieee80211_local *local = sdata->local; if (ieee80211_sdata_running(sdata)) { + u32 mask = MONITOR_FLAG_COOK_FRAMES | + MONITOR_FLAG_ACTIVE; + /* - * Prohibit MONITOR_FLAG_COOK_FRAMES to be - * changed while the interface is up. + * Prohibit MONITOR_FLAG_COOK_FRAMES and + * MONITOR_FLAG_ACTIVE to be changed while the + * interface is up. * Else we would need to add a lot of cruft * to update everything: * cooked_mntrs, monitor and all fif_* counters * reconfigure hardware */ - if ((*flags & MONITOR_FLAG_COOK_FRAMES) != - (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES)) + if ((*flags & mask) != (sdata->u.mntr_flags & mask)) return -EBUSY; ieee80211_adjust_monitor_flags(sdata, -1); diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 169664c122e2..b931c96a596f 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -146,7 +146,8 @@ static inline int drv_add_interface(struct ieee80211_local *local, if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN || (sdata->vif.type == NL80211_IFTYPE_MONITOR && - !(local->hw.flags & IEEE80211_HW_WANT_MONITOR_VIF)))) + !(local->hw.flags & IEEE80211_HW_WANT_MONITOR_VIF) && + !(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE)))) return -EINVAL; trace_drv_add_interface(local, sdata); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index ceef64426a8d..7cabaf261fed 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -159,7 +159,8 @@ static int ieee80211_change_mtu(struct net_device *dev, int new_mtu) return 0; } -static int ieee80211_verify_mac(struct ieee80211_local *local, u8 *addr) +static int ieee80211_verify_mac(struct ieee80211_local *local, u8 *addr, + bool check_dup) { struct ieee80211_sub_if_data *sdata; u64 new, mask, tmp; @@ -179,10 +180,13 @@ static int ieee80211_verify_mac(struct ieee80211_local *local, u8 *addr) ((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) | ((u64)m[4] << 1*8) | ((u64)m[5] << 0*8); + if (!check_dup) + return ret; mutex_lock(&local->iflist_mtx); list_for_each_entry(sdata, &local->interfaces, list) { - if (sdata->vif.type == NL80211_IFTYPE_MONITOR) + if (sdata->vif.type == NL80211_IFTYPE_MONITOR && + !(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE)) continue; m = sdata->vif.addr; @@ -204,12 +208,17 @@ static int ieee80211_change_mac(struct net_device *dev, void *addr) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct sockaddr *sa = addr; + bool check_dup = true; int ret; if (ieee80211_sdata_running(sdata)) return -EBUSY; - ret = ieee80211_verify_mac(sdata->local, sa->sa_data); + if (sdata->vif.type == NL80211_IFTYPE_MONITOR && + !(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE)) + check_dup = false; + + ret = ieee80211_verify_mac(sdata->local, sa->sa_data, check_dup); if (ret) return ret; @@ -541,7 +550,11 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) break; } - if (local->monitors == 0 && local->open_count == 0) { + if (sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE) { + res = drv_add_interface(local, sdata); + if (res) + goto err_stop; + } else if (local->monitors == 0 && local->open_count == 0) { res = ieee80211_add_virtual_monitor(local); if (res) goto err_stop; @@ -919,7 +932,11 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, mutex_lock(&local->mtx); ieee80211_recalc_idle(local); mutex_unlock(&local->mtx); - break; + + if (!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE)) + break; + + /* fall through */ default: if (going_down) drv_remove_interface(local, sdata); @@ -1068,7 +1085,7 @@ static const struct net_device_ops ieee80211_monitorif_ops = { .ndo_start_xmit = ieee80211_monitor_start_xmit, .ndo_set_rx_mode = ieee80211_set_multicast_list, .ndo_change_mtu = ieee80211_change_mtu, - .ndo_set_mac_address = eth_mac_addr, + .ndo_set_mac_address = ieee80211_change_mac, .ndo_select_queue = ieee80211_monitor_select_queue, }; diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 2a8d759324c2..89a83770d152 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -560,6 +560,9 @@ void ieee80211_iterate_active_interfaces( list_for_each_entry(sdata, &local->interfaces, list) { switch (sdata->vif.type) { case NL80211_IFTYPE_MONITOR: + if (!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE)) + continue; + break; case NL80211_IFTYPE_AP_VLAN: continue; default: @@ -598,6 +601,9 @@ void ieee80211_iterate_active_interfaces_atomic( list_for_each_entry_rcu(sdata, &local->interfaces, list) { switch (sdata->vif.type) { case NL80211_IFTYPE_MONITOR: + if (!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE)) + continue; + break; case NL80211_IFTYPE_AP_VLAN: continue; default: -- cgit v1.2.3 From bd5e14fb77d9d1dd15f9102759e8c8b31c667488 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 29 May 2013 09:08:05 +0200 Subject: cfg80211: remove cleanup_work kernel-doc I evidently forgot this when removing the work itself. Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 13b247d26544..6dd19593e333 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2869,7 +2869,6 @@ struct cfg80211_cached_keys; * @mgmt_registrations_lock: lock for the list * @mtx: mutex used to lock data in this struct, may be used by drivers * and some API functions require it held - * @cleanup_work: work struct used for cleanup that can't be done directly * @beacon_interval: beacon interval used on this device for transmitting * beacons, 0 when not valid * @address: The address for this device, valid only if @netdev is %NULL -- cgit v1.2.3 From cbb963deed7bbf206f5077ab5742bee00ceefa46 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 29 May 2013 10:22:30 +0300 Subject: net/usb/kalmia: use %*phC to dump small buffers Instead of dereferencing pointer and put values on stack we could use nice %*phC specifier. Signed-off-by: Andy Shevchenko Signed-off-by: David S. Miller --- drivers/net/usb/kalmia.c | 45 +++++++++++++++------------------------------ 1 file changed, 15 insertions(+), 30 deletions(-) diff --git a/drivers/net/usb/kalmia.c b/drivers/net/usb/kalmia.c index 0192073e53a3..6866eae3e388 100644 --- a/drivers/net/usb/kalmia.c +++ b/drivers/net/usb/kalmia.c @@ -221,12 +221,9 @@ done: memset(skb_put(skb, padlen), 0, padlen); } - netdev_dbg( - dev->net, - "Sending package with length %i and padding %i. Header: %02x:%02x:%02x:%02x:%02x:%02x.", - content_len, padlen, header_start[0], header_start[1], - header_start[2], header_start[3], header_start[4], - header_start[5]); + netdev_dbg(dev->net, + "Sending package with length %i and padding %i. Header: %6phC.", + content_len, padlen, header_start); return skb; } @@ -263,32 +260,23 @@ kalmia_rx_fixup(struct usbnet *dev, struct sk_buff *skb) sizeof(EXPECTED_UNKNOWN_HEADER_1)) || !memcmp( header_start, EXPECTED_UNKNOWN_HEADER_2, sizeof(EXPECTED_UNKNOWN_HEADER_2))) { - netdev_dbg( - dev->net, - "Received expected unknown frame header: %02x:%02x:%02x:%02x:%02x:%02x. Package length: %i\n", - header_start[0], header_start[1], - header_start[2], header_start[3], - header_start[4], header_start[5], + netdev_dbg(dev->net, + "Received expected unknown frame header: %6phC. Package length: %i\n", + header_start, skb->len - KALMIA_HEADER_LENGTH); } else { - netdev_err( - dev->net, - "Received unknown frame header: %02x:%02x:%02x:%02x:%02x:%02x. Package length: %i\n", - header_start[0], header_start[1], - header_start[2], header_start[3], - header_start[4], header_start[5], + netdev_err(dev->net, + "Received unknown frame header: %6phC. Package length: %i\n", + header_start, skb->len - KALMIA_HEADER_LENGTH); return 0; } } else - netdev_dbg( - dev->net, - "Received header: %02x:%02x:%02x:%02x:%02x:%02x. Package length: %i\n", - header_start[0], header_start[1], header_start[2], - header_start[3], header_start[4], header_start[5], - skb->len - KALMIA_HEADER_LENGTH); + netdev_dbg(dev->net, + "Received header: %6phC. Package length: %i\n", + header_start, skb->len - KALMIA_HEADER_LENGTH); /* subtract start header and end header */ usb_packet_length = skb->len - (2 * KALMIA_HEADER_LENGTH); @@ -310,12 +298,9 @@ kalmia_rx_fixup(struct usbnet *dev, struct sk_buff *skb) sizeof(HEADER_END_OF_USB_PACKET)) == 0); if (!is_last) { header_start = skb->data + ether_packet_length; - netdev_dbg( - dev->net, - "End header: %02x:%02x:%02x:%02x:%02x:%02x. Package length: %i\n", - header_start[0], header_start[1], - header_start[2], header_start[3], - header_start[4], header_start[5], + netdev_dbg(dev->net, + "End header: %6phC. Package length: %i\n", + header_start, skb->len - KALMIA_HEADER_LENGTH); } } -- cgit v1.2.3 From d41d5805875a628bdef75b624bab245da436f816 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Thu, 9 May 2013 11:35:01 -0700 Subject: clk: Disable unused clocks after deferred probing is done With deferred probing, late_initcall() is too soon to declare a clock as unused. Wait for deferred probing to finish before declaring a clock as unused. Since deferred probing is done in late_initcall(), do the unused check to late_initcall_sync. Signed-off-by: Saravana Kannan Signed-off-by: Mike Turquette --- drivers/clk/clk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 399b0d8ac562..af0dbccb375d 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -534,7 +534,7 @@ static int clk_disable_unused(void) return 0; } -late_initcall(clk_disable_unused); +late_initcall_sync(clk_disable_unused); /*** helper functions ***/ -- cgit v1.2.3 From f4d57941bf89997bad3294f94987caebf2771a33 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 28 May 2013 17:24:15 +0200 Subject: mac80211: always send multicast on CAB queue If the driver advertised support for a CAB queue, then we should put all multicast frames there, otherwise sending them can be racy with clients going to sleep while we TX a frame. To avoid this, always TX multicast frames on the multicast queue. It seems like even drivers not using the queue framework might want to do this which would mean also moving the IEEE80211_TX_CTL_SEND_AFTER_DTIM flag assignment, but it also seems that drivers behave differently here so that just moving it wouldn't be a good idea. It'd be better to modify those drivers to use the queue framework. Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 9972e07a2f96..34be9336b5d1 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -398,13 +398,14 @@ ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx) if (ieee80211_has_order(hdr->frame_control)) return TX_CONTINUE; + if (tx->local->hw.flags & IEEE80211_HW_QUEUE_CONTROL) + info->hw_queue = tx->sdata->vif.cab_queue; + /* no stations in PS mode */ if (!atomic_read(&ps->num_sta_ps)) return TX_CONTINUE; info->flags |= IEEE80211_TX_CTL_SEND_AFTER_DTIM; - if (tx->local->hw.flags & IEEE80211_HW_QUEUE_CONTROL) - info->hw_queue = tx->sdata->vif.cab_queue; /* device releases frame after DTIM beacon */ if (!(tx->local->hw.flags & IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING)) -- cgit v1.2.3 From 20a24225d8f94fc56f74a9068684869d6deebea5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 29 May 2013 12:37:32 +0200 Subject: ALSA: PCI: Remove superfluous pci_set_drvdata(pci, NULL) at remove As drvdata is cleared to NULL at probe failure or at removal by the driver core, we don't have to call pci_set_drvdata(pci, NULL) any longer in each driver. The only remaining pci_set_drvdata(NULL) is in azx_firmware_cb() in hda_intel.c. Since this function itself releases the card instance, we need to clear drvdata here as well, so that it won't be released doubly in the remove callback. Signed-off-by: Takashi Iwai --- sound/pci/ad1889.c | 1 - sound/pci/ali5451/ali5451.c | 1 - sound/pci/als300.c | 1 - sound/pci/als4000.c | 1 - sound/pci/asihpi/hpioctl.c | 1 - sound/pci/atiixp.c | 1 - sound/pci/atiixp_modem.c | 1 - sound/pci/au88x0/au88x0.c | 1 - sound/pci/aw2/aw2-alsa.c | 1 - sound/pci/azt3328.c | 1 - sound/pci/bt87x.c | 1 - sound/pci/ca0106/ca0106_main.c | 1 - sound/pci/cmipci.c | 1 - sound/pci/cs4281.c | 1 - sound/pci/cs46xx/cs46xx.c | 1 - sound/pci/cs5530.c | 1 - sound/pci/cs5535audio/cs5535audio.c | 1 - sound/pci/ctxfi/xfi.c | 1 - sound/pci/echoaudio/echoaudio.c | 1 - sound/pci/emu10k1/emu10k1.c | 1 - sound/pci/emu10k1/emu10k1x.c | 1 - sound/pci/ens1370.c | 1 - sound/pci/es1938.c | 1 - sound/pci/es1968.c | 1 - sound/pci/fm801.c | 1 - sound/pci/hda/hda_intel.c | 2 -- sound/pci/ice1712/ice1712.c | 1 - sound/pci/ice1712/ice1724.c | 1 - sound/pci/intel8x0.c | 1 - sound/pci/intel8x0m.c | 1 - sound/pci/korg1212/korg1212.c | 1 - sound/pci/lola/lola.c | 1 - sound/pci/lx6464es/lx6464es.c | 1 - sound/pci/maestro3.c | 1 - sound/pci/mixart/mixart.c | 1 - sound/pci/nm256/nm256.c | 1 - sound/pci/oxygen/oxygen_lib.c | 1 - sound/pci/pcxhr/pcxhr.c | 1 - sound/pci/riptide/riptide.c | 2 -- sound/pci/rme32.c | 1 - sound/pci/rme96.c | 1 - sound/pci/rme9652/hdsp.c | 1 - sound/pci/rme9652/hdspm.c | 1 - sound/pci/rme9652/rme9652.c | 1 - sound/pci/sis7019.c | 1 - sound/pci/sonicvibes.c | 1 - sound/pci/trident/trident.c | 1 - sound/pci/via82xx.c | 1 - sound/pci/via82xx_modem.c | 1 - sound/pci/vx222/vx222.c | 1 - sound/pci/ymfpci/ymfpci.c | 1 - 51 files changed, 53 deletions(-) diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c index ad8a31173939..d2b9d617aee5 100644 --- a/sound/pci/ad1889.c +++ b/sound/pci/ad1889.c @@ -1046,7 +1046,6 @@ static void snd_ad1889_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } static DEFINE_PCI_DEVICE_TABLE(snd_ad1889_ids) = { diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c index 53754f5edeb1..3dfa12b670eb 100644 --- a/sound/pci/ali5451/ali5451.c +++ b/sound/pci/ali5451/ali5451.c @@ -2298,7 +2298,6 @@ static int snd_ali_probe(struct pci_dev *pci, static void snd_ali_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } static struct pci_driver ali5451_driver = { diff --git a/sound/pci/als300.c b/sound/pci/als300.c index 864c4310366b..591efb6eef05 100644 --- a/sound/pci/als300.c +++ b/sound/pci/als300.c @@ -282,7 +282,6 @@ static void snd_als300_remove(struct pci_dev *pci) { snd_als300_dbgcallenter(); snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); snd_als300_dbgcallleave(); } diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c index 61efda2a4d94..ffc821b0139e 100644 --- a/sound/pci/als4000.c +++ b/sound/pci/als4000.c @@ -984,7 +984,6 @@ out: static void snd_card_als4000_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } #ifdef CONFIG_PM_SLEEP diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c index ef5019fe5193..7f0272032fbb 100644 --- a/sound/pci/asihpi/hpioctl.c +++ b/sound/pci/asihpi/hpioctl.c @@ -445,7 +445,6 @@ void asihpi_adapter_remove(struct pci_dev *pci_dev) if (pa->p_buffer) vfree(pa->p_buffer); - pci_set_drvdata(pci_dev, NULL); if (1) dev_info(&pci_dev->dev, "remove %04x:%04x,%04x:%04x,%04x, HPI index %d\n", diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c index 6e78c6789858..fe4c61bdb8ba 100644 --- a/sound/pci/atiixp.c +++ b/sound/pci/atiixp.c @@ -1714,7 +1714,6 @@ static int snd_atiixp_probe(struct pci_dev *pci, static void snd_atiixp_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } static struct pci_driver atiixp_driver = { diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c index d0bec7ba3b0d..cf29b9a1d65d 100644 --- a/sound/pci/atiixp_modem.c +++ b/sound/pci/atiixp_modem.c @@ -1334,7 +1334,6 @@ static int snd_atiixp_probe(struct pci_dev *pci, static void snd_atiixp_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } static struct pci_driver atiixp_modem_driver = { diff --git a/sound/pci/au88x0/au88x0.c b/sound/pci/au88x0/au88x0.c index b157e1fadd8f..7059dd69e5e6 100644 --- a/sound/pci/au88x0/au88x0.c +++ b/sound/pci/au88x0/au88x0.c @@ -371,7 +371,6 @@ snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) static void snd_vortex_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } // pci_driver definition diff --git a/sound/pci/aw2/aw2-alsa.c b/sound/pci/aw2/aw2-alsa.c index 08e9a4702cbc..2925220d3fcf 100644 --- a/sound/pci/aw2/aw2-alsa.c +++ b/sound/pci/aw2/aw2-alsa.c @@ -392,7 +392,6 @@ static int snd_aw2_probe(struct pci_dev *pci, static void snd_aw2_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } /* open callback */ diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index 1204a0fa3368..c8e121611593 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c @@ -2725,7 +2725,6 @@ snd_azf3328_remove(struct pci_dev *pci) { snd_azf3328_dbgcallenter(); snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); snd_azf3328_dbgcallleave(); } diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c index 9febe5509748..18802039497a 100644 --- a/sound/pci/bt87x.c +++ b/sound/pci/bt87x.c @@ -953,7 +953,6 @@ _error: static void snd_bt87x_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } /* default entries for all Bt87x cards - it's not exported */ diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c index 1610a5705970..f4db5587e86e 100644 --- a/sound/pci/ca0106/ca0106_main.c +++ b/sound/pci/ca0106/ca0106_main.c @@ -1896,7 +1896,6 @@ static int snd_ca0106_probe(struct pci_dev *pci, static void snd_ca0106_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } #ifdef CONFIG_PM_SLEEP diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index c617435db6e6..2755ec5bcc25 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -3317,7 +3317,6 @@ static int snd_cmipci_probe(struct pci_dev *pci, static void snd_cmipci_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c index 6a8695069941..64659facd155 100644 --- a/sound/pci/cs4281.c +++ b/sound/pci/cs4281.c @@ -1971,7 +1971,6 @@ static int snd_cs4281_probe(struct pci_dev *pci, static void snd_cs4281_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } /* diff --git a/sound/pci/cs46xx/cs46xx.c b/sound/pci/cs46xx/cs46xx.c index 6b0d8b50a305..b03498325d66 100644 --- a/sound/pci/cs46xx/cs46xx.c +++ b/sound/pci/cs46xx/cs46xx.c @@ -158,7 +158,6 @@ static int snd_card_cs46xx_probe(struct pci_dev *pci, static void snd_card_cs46xx_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } static struct pci_driver cs46xx_driver = { diff --git a/sound/pci/cs5530.c b/sound/pci/cs5530.c index dace827b45d1..c6b82c85e044 100644 --- a/sound/pci/cs5530.c +++ b/sound/pci/cs5530.c @@ -91,7 +91,6 @@ static int snd_cs5530_dev_free(struct snd_device *device) static void snd_cs5530_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } static u8 snd_cs5530_mixer_read(unsigned long io, u8 reg) diff --git a/sound/pci/cs5535audio/cs5535audio.c b/sound/pci/cs5535audio/cs5535audio.c index 7e4b13e2d12a..902bebd3b3fb 100644 --- a/sound/pci/cs5535audio/cs5535audio.c +++ b/sound/pci/cs5535audio/cs5535audio.c @@ -391,7 +391,6 @@ static void snd_cs5535audio_remove(struct pci_dev *pci) { olpc_quirks_cleanup(); snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } static struct pci_driver cs5535audio_driver = { diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c index d01ffcb2b2f5..d464ad2fc7b7 100644 --- a/sound/pci/ctxfi/xfi.c +++ b/sound/pci/ctxfi/xfi.c @@ -122,7 +122,6 @@ error: static void ct_card_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } #ifdef CONFIG_PM_SLEEP diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c index 760cbff53210..05cfe551ce42 100644 --- a/sound/pci/echoaudio/echoaudio.c +++ b/sound/pci/echoaudio/echoaudio.c @@ -2323,7 +2323,6 @@ static void snd_echo_remove(struct pci_dev *pci) chip = pci_get_drvdata(pci); if (chip) snd_card_free(chip->card); - pci_set_drvdata(pci, NULL); } diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c index 8c5010f7889c..9e1bd0c39a8c 100644 --- a/sound/pci/emu10k1/emu10k1.c +++ b/sound/pci/emu10k1/emu10k1.c @@ -202,7 +202,6 @@ static int snd_card_emu10k1_probe(struct pci_dev *pci, static void snd_card_emu10k1_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c index cdff11d48ebd..56ad9d6f200d 100644 --- a/sound/pci/emu10k1/emu10k1x.c +++ b/sound/pci/emu10k1/emu10k1x.c @@ -1623,7 +1623,6 @@ static int snd_emu10k1x_probe(struct pci_dev *pci, static void snd_emu10k1x_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } // PCI IDs diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index db2dc835171d..372f8ea91fca 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -2497,7 +2497,6 @@ static int snd_audiopci_probe(struct pci_dev *pci, static void snd_audiopci_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } static struct pci_driver ens137x_driver = { diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index 8423403954ab..9213fb38921c 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -1881,7 +1881,6 @@ static int snd_es1938_probe(struct pci_dev *pci, static void snd_es1938_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } static struct pci_driver es1938_driver = { diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index a1f32b5ae0d1..714525154605 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -2909,7 +2909,6 @@ static int snd_es1968_probe(struct pci_dev *pci, static void snd_es1968_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } static struct pci_driver es1968_driver = { diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index 4f07fda5adf2..706c5b67b708 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -1370,7 +1370,6 @@ static int snd_card_fm801_probe(struct pci_dev *pci, static void snd_card_fm801_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } #ifdef CONFIG_PM_SLEEP diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index ac75975a4276..49dfad4a099e 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -3764,7 +3764,6 @@ static int azx_probe(struct pci_dev *pci, out_free: snd_card_free(card); - pci_set_drvdata(pci, NULL); return err; } @@ -3834,7 +3833,6 @@ static void azx_remove(struct pci_dev *pci) if (card) snd_card_free(card); - pci_set_drvdata(pci, NULL); } /* PCI IDs */ diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index 806407a3973e..28ec872e54c0 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -2807,7 +2807,6 @@ static void snd_ice1712_remove(struct pci_dev *pci) if (ice->card_info && ice->card_info->chip_exit) ice->card_info->chip_exit(ice); snd_card_free(card); - pci_set_drvdata(pci, NULL); } static struct pci_driver ice1712_driver = { diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index ce70e7f113e0..500471778291 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -2800,7 +2800,6 @@ static void snd_vt1724_remove(struct pci_dev *pci) if (ice->card_info && ice->card_info->chip_exit) ice->card_info->chip_exit(ice); snd_card_free(card); - pci_set_drvdata(pci, NULL); } #ifdef CONFIG_PM_SLEEP diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index b8fe40531b9c..59c8aaebb91e 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -3364,7 +3364,6 @@ static int snd_intel8x0_probe(struct pci_dev *pci, static void snd_intel8x0_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } static struct pci_driver intel8x0_driver = { diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c index fea09e8ea608..3573c1193665 100644 --- a/sound/pci/intel8x0m.c +++ b/sound/pci/intel8x0m.c @@ -1328,7 +1328,6 @@ static int snd_intel8x0m_probe(struct pci_dev *pci, static void snd_intel8x0m_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } static struct pci_driver intel8x0m_driver = { diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c index 43b4228d9afe..9cf9829555d4 100644 --- a/sound/pci/korg1212/korg1212.c +++ b/sound/pci/korg1212/korg1212.c @@ -2473,7 +2473,6 @@ snd_korg1212_probe(struct pci_dev *pci, static void snd_korg1212_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } static struct pci_driver korg1212_driver = { diff --git a/sound/pci/lola/lola.c b/sound/pci/lola/lola.c index 322b638e8ec4..7307d97186cb 100644 --- a/sound/pci/lola/lola.c +++ b/sound/pci/lola/lola.c @@ -759,7 +759,6 @@ out_free: static void lola_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } /* PCI IDs */ diff --git a/sound/pci/lx6464es/lx6464es.c b/sound/pci/lx6464es/lx6464es.c index 298bc9b72991..3230e57f246c 100644 --- a/sound/pci/lx6464es/lx6464es.c +++ b/sound/pci/lx6464es/lx6464es.c @@ -1139,7 +1139,6 @@ out_free: static void snd_lx6464es_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index c76ac1411210..d5417360f51f 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -2775,7 +2775,6 @@ snd_m3_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) static void snd_m3_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } static struct pci_driver m3_driver = { diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c index 934dec98e2ce..1e0f6ee193f0 100644 --- a/sound/pci/mixart/mixart.c +++ b/sound/pci/mixart/mixart.c @@ -1377,7 +1377,6 @@ static int snd_mixart_probe(struct pci_dev *pci, static void snd_mixart_remove(struct pci_dev *pci) { snd_mixart_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } static struct pci_driver mixart_driver = { diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c index 6febedb05936..fe79fff4c6dc 100644 --- a/sound/pci/nm256/nm256.c +++ b/sound/pci/nm256/nm256.c @@ -1746,7 +1746,6 @@ static int snd_nm256_probe(struct pci_dev *pci, static void snd_nm256_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c index 9562dc63ba60..b0cb48adddc7 100644 --- a/sound/pci/oxygen/oxygen_lib.c +++ b/sound/pci/oxygen/oxygen_lib.c @@ -722,7 +722,6 @@ EXPORT_SYMBOL(oxygen_pci_probe); void oxygen_pci_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } EXPORT_SYMBOL(oxygen_pci_remove); diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c index b97384ad946d..d379b284955b 100644 --- a/sound/pci/pcxhr/pcxhr.c +++ b/sound/pci/pcxhr/pcxhr.c @@ -1691,7 +1691,6 @@ static int pcxhr_probe(struct pci_dev *pci, static void pcxhr_remove(struct pci_dev *pci) { pcxhr_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } static struct pci_driver pcxhr_driver = { diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c index 63c1c8041554..56cc891e395e 100644 --- a/sound/pci/riptide/riptide.c +++ b/sound/pci/riptide/riptide.c @@ -2066,7 +2066,6 @@ static void snd_riptide_joystick_remove(struct pci_dev *pci) if (gameport) { release_region(gameport->io, 8); gameport_unregister_port(gameport); - pci_set_drvdata(pci, NULL); } } #endif @@ -2179,7 +2178,6 @@ snd_card_riptide_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) static void snd_card_riptide_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } static struct pci_driver driver = { diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c index 0ecd4100713e..cc26346ae66b 100644 --- a/sound/pci/rme32.c +++ b/sound/pci/rme32.c @@ -1981,7 +1981,6 @@ snd_rme32_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) static void snd_rme32_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } static struct pci_driver rme32_driver = { diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index 5fb88ac82aa9..2a8ad9d1a2ae 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -2390,7 +2390,6 @@ snd_rme96_probe(struct pci_dev *pci, static void snd_rme96_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } static struct pci_driver rme96_driver = { diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 94084cdb130c..4f255dfee450 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -5412,7 +5412,6 @@ static int snd_hdsp_probe(struct pci_dev *pci, static void snd_hdsp_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } static struct pci_driver hdsp_driver = { diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 9ea05e956474..ef3cbc044f0c 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -6737,7 +6737,6 @@ static int snd_hdspm_probe(struct pci_dev *pci, static void snd_hdspm_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } static struct pci_driver hdspm_driver = { diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c index 773a67fff4cd..b96d9e1adf6d 100644 --- a/sound/pci/rme9652/rme9652.c +++ b/sound/pci/rme9652/rme9652.c @@ -2628,7 +2628,6 @@ static int snd_rme9652_probe(struct pci_dev *pci, static void snd_rme9652_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } static struct pci_driver rme9652_driver = { diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c index d59abe1682c5..f2639a0c5a65 100644 --- a/sound/pci/sis7019.c +++ b/sound/pci/sis7019.c @@ -1481,7 +1481,6 @@ error_out: static void snd_sis7019_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } static struct pci_driver sis7019_driver = { diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c index a2e7686e7ae3..2a46bf98af30 100644 --- a/sound/pci/sonicvibes.c +++ b/sound/pci/sonicvibes.c @@ -1528,7 +1528,6 @@ static int snd_sonic_probe(struct pci_dev *pci, static void snd_sonic_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } static struct pci_driver sonicvibes_driver = { diff --git a/sound/pci/trident/trident.c b/sound/pci/trident/trident.c index 1aefd6204a63..b3b588bc94c3 100644 --- a/sound/pci/trident/trident.c +++ b/sound/pci/trident/trident.c @@ -169,7 +169,6 @@ static int snd_trident_probe(struct pci_dev *pci, static void snd_trident_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } static struct pci_driver trident_driver = { diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index d756a3562706..3c511d0caf9e 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -2646,7 +2646,6 @@ static int snd_via82xx_probe(struct pci_dev *pci, static void snd_via82xx_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } static struct pci_driver via82xx_driver = { diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c index 4f5fd80b7e56..ca190283cbd7 100644 --- a/sound/pci/via82xx_modem.c +++ b/sound/pci/via82xx_modem.c @@ -1227,7 +1227,6 @@ static int snd_via82xx_probe(struct pci_dev *pci, static void snd_via82xx_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } static struct pci_driver via82xx_modem_driver = { diff --git a/sound/pci/vx222/vx222.c b/sound/pci/vx222/vx222.c index e2f1ab37e154..ab8a9b1bfb8e 100644 --- a/sound/pci/vx222/vx222.c +++ b/sound/pci/vx222/vx222.c @@ -254,7 +254,6 @@ static int snd_vx222_probe(struct pci_dev *pci, static void snd_vx222_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } #ifdef CONFIG_PM_SLEEP diff --git a/sound/pci/ymfpci/ymfpci.c b/sound/pci/ymfpci/ymfpci.c index 01c49655a3c1..e8932b2e4a5d 100644 --- a/sound/pci/ymfpci/ymfpci.c +++ b/sound/pci/ymfpci/ymfpci.c @@ -347,7 +347,6 @@ static int snd_card_ymfpci_probe(struct pci_dev *pci, static void snd_card_ymfpci_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } static struct pci_driver ymfpci_driver = { -- cgit v1.2.3 From d2c69807156b8c162ce3f1e37b220d03d57af063 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 29 May 2013 12:40:04 +0200 Subject: sound: OSS: Remove superfluous pci_set_dvdata(pci, NULL) Only kahlua.c has it. Signed-off-by: Takashi Iwai --- sound/oss/kahlua.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/oss/kahlua.c b/sound/oss/kahlua.c index 2a44cc106459..12be1fb512dd 100644 --- a/sound/oss/kahlua.c +++ b/sound/oss/kahlua.c @@ -178,7 +178,6 @@ static int probe_one(struct pci_dev *pdev, const struct pci_device_id *ent) return 0; err_out_free: - pci_set_drvdata(pdev, NULL); kfree(hw_config); return 1; } @@ -187,7 +186,6 @@ static void remove_one(struct pci_dev *pdev) { struct address_info *hw_config = pci_get_drvdata(pdev); sb_dsp_unload(hw_config, 0); - pci_set_drvdata(pdev, NULL); kfree(hw_config); } -- cgit v1.2.3 From 8b5a1f9c46c2b78716794b8762edf659ec25a87d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 29 May 2013 12:49:32 +0200 Subject: ALSA: ISA: Remove superfluous *_set_drvdata(NULL) calls Similarly like the previous commit for PCI drivers, remove dev_set_drvdata(NULL) and pnp_set_drvdata(NULL) calls in ISA drivers now. Signed-off-by: Takashi Iwai --- sound/isa/ad1848/ad1848.c | 1 - sound/isa/adlib.c | 1 - sound/isa/cmi8328.c | 1 - sound/isa/cmi8330.c | 1 - sound/isa/cs423x/cs4231.c | 1 - sound/isa/cs423x/cs4236.c | 2 -- sound/isa/es1688/es1688.c | 1 - sound/isa/es18xx.c | 2 -- sound/isa/galaxy/galaxy.c | 1 - sound/isa/gus/gusclassic.c | 1 - sound/isa/gus/gusextreme.c | 1 - sound/isa/gus/gusmax.c | 1 - sound/isa/gus/interwave.c | 1 - sound/isa/msnd/msnd_pinnacle.c | 1 - sound/isa/opl3sa2.c | 2 -- sound/isa/opti9xx/miro.c | 1 - sound/isa/opti9xx/opti92x-ad1848.c | 1 - sound/isa/sb/jazz16.c | 1 - sound/isa/sb/sb16.c | 1 - sound/isa/sb/sb8.c | 1 - sound/isa/sc6000.c | 1 - sound/isa/sscape.c | 1 - sound/isa/wavefront/wavefront.c | 1 - 23 files changed, 26 deletions(-) diff --git a/sound/isa/ad1848/ad1848.c b/sound/isa/ad1848/ad1848.c index c214ecf45400..e3f455bd85cd 100644 --- a/sound/isa/ad1848/ad1848.c +++ b/sound/isa/ad1848/ad1848.c @@ -135,7 +135,6 @@ out: snd_card_free(card); static int snd_ad1848_remove(struct device *dev, unsigned int n) { snd_card_free(dev_get_drvdata(dev)); - dev_set_drvdata(dev, NULL); return 0; } diff --git a/sound/isa/adlib.c b/sound/isa/adlib.c index d26545543732..35659218710f 100644 --- a/sound/isa/adlib.c +++ b/sound/isa/adlib.c @@ -101,7 +101,6 @@ out: snd_card_free(card); static int snd_adlib_remove(struct device *dev, unsigned int n) { snd_card_free(dev_get_drvdata(dev)); - dev_set_drvdata(dev, NULL); return 0; } diff --git a/sound/isa/cmi8328.c b/sound/isa/cmi8328.c index a7369fe19a6f..f84f073fc1e8 100644 --- a/sound/isa/cmi8328.c +++ b/sound/isa/cmi8328.c @@ -418,7 +418,6 @@ static int snd_cmi8328_remove(struct device *pdev, unsigned int dev) snd_cmi8328_cfg_write(cmi->port, CFG2, 0); snd_cmi8328_cfg_write(cmi->port, CFG3, 0); snd_card_free(card); - dev_set_drvdata(pdev, NULL); return 0; } diff --git a/sound/isa/cmi8330.c b/sound/isa/cmi8330.c index c707c52268ab..270b9659ef7f 100644 --- a/sound/isa/cmi8330.c +++ b/sound/isa/cmi8330.c @@ -651,7 +651,6 @@ static int snd_cmi8330_isa_remove(struct device *devptr, unsigned int dev) { snd_card_free(dev_get_drvdata(devptr)); - dev_set_drvdata(devptr, NULL); return 0; } diff --git a/sound/isa/cs423x/cs4231.c b/sound/isa/cs423x/cs4231.c index aa7a5d86e480..ba9a74eff3e0 100644 --- a/sound/isa/cs423x/cs4231.c +++ b/sound/isa/cs423x/cs4231.c @@ -151,7 +151,6 @@ out: snd_card_free(card); static int snd_cs4231_remove(struct device *dev, unsigned int n) { snd_card_free(dev_get_drvdata(dev)); - dev_set_drvdata(dev, NULL); return 0; } diff --git a/sound/isa/cs423x/cs4236.c b/sound/isa/cs423x/cs4236.c index 252e9fb37db3..69614acb2052 100644 --- a/sound/isa/cs423x/cs4236.c +++ b/sound/isa/cs423x/cs4236.c @@ -504,7 +504,6 @@ static int snd_cs423x_isa_remove(struct device *pdev, unsigned int dev) { snd_card_free(dev_get_drvdata(pdev)); - dev_set_drvdata(pdev, NULL); return 0; } @@ -600,7 +599,6 @@ static int snd_cs423x_pnpbios_detect(struct pnp_dev *pdev, static void snd_cs423x_pnp_remove(struct pnp_dev *pdev) { snd_card_free(pnp_get_drvdata(pdev)); - pnp_set_drvdata(pdev, NULL); } #ifdef CONFIG_PM diff --git a/sound/isa/es1688/es1688.c b/sound/isa/es1688/es1688.c index 102874a703d4..cdcfb57f1f0a 100644 --- a/sound/isa/es1688/es1688.c +++ b/sound/isa/es1688/es1688.c @@ -213,7 +213,6 @@ out: static int snd_es1688_isa_remove(struct device *dev, unsigned int n) { snd_card_free(dev_get_drvdata(dev)); - dev_set_drvdata(dev, NULL); return 0; } diff --git a/sound/isa/es18xx.c b/sound/isa/es18xx.c index 24380efe31a1..12978b864c3a 100644 --- a/sound/isa/es18xx.c +++ b/sound/isa/es18xx.c @@ -2235,7 +2235,6 @@ static int snd_es18xx_isa_remove(struct device *devptr, unsigned int dev) { snd_card_free(dev_get_drvdata(devptr)); - dev_set_drvdata(devptr, NULL); return 0; } @@ -2305,7 +2304,6 @@ static int snd_audiodrive_pnp_detect(struct pnp_dev *pdev, static void snd_audiodrive_pnp_remove(struct pnp_dev *pdev) { snd_card_free(pnp_get_drvdata(pdev)); - pnp_set_drvdata(pdev, NULL); } #ifdef CONFIG_PM diff --git a/sound/isa/galaxy/galaxy.c b/sound/isa/galaxy/galaxy.c index 672184e3221a..81244e7cea5b 100644 --- a/sound/isa/galaxy/galaxy.c +++ b/sound/isa/galaxy/galaxy.c @@ -623,7 +623,6 @@ error: static int snd_galaxy_remove(struct device *dev, unsigned int n) { snd_card_free(dev_get_drvdata(dev)); - dev_set_drvdata(dev, NULL); return 0; } diff --git a/sound/isa/gus/gusclassic.c b/sound/isa/gus/gusclassic.c index 16bca4e96c08..1adc1b924f39 100644 --- a/sound/isa/gus/gusclassic.c +++ b/sound/isa/gus/gusclassic.c @@ -215,7 +215,6 @@ out: snd_card_free(card); static int snd_gusclassic_remove(struct device *dev, unsigned int n) { snd_card_free(dev_get_drvdata(dev)); - dev_set_drvdata(dev, NULL); return 0; } diff --git a/sound/isa/gus/gusextreme.c b/sound/isa/gus/gusextreme.c index 0b9c2426b49f..38e1e3260c24 100644 --- a/sound/isa/gus/gusextreme.c +++ b/sound/isa/gus/gusextreme.c @@ -344,7 +344,6 @@ out: snd_card_free(card); static int snd_gusextreme_remove(struct device *dev, unsigned int n) { snd_card_free(dev_get_drvdata(dev)); - dev_set_drvdata(dev, NULL); return 0; } diff --git a/sound/isa/gus/gusmax.c b/sound/isa/gus/gusmax.c index c309a5d0e7e1..652d5d834620 100644 --- a/sound/isa/gus/gusmax.c +++ b/sound/isa/gus/gusmax.c @@ -357,7 +357,6 @@ static int snd_gusmax_probe(struct device *pdev, unsigned int dev) static int snd_gusmax_remove(struct device *devptr, unsigned int dev) { snd_card_free(dev_get_drvdata(devptr)); - dev_set_drvdata(devptr, NULL); return 0; } diff --git a/sound/isa/gus/interwave.c b/sound/isa/gus/interwave.c index 78bc5744e89a..9942691cc0ca 100644 --- a/sound/isa/gus/interwave.c +++ b/sound/isa/gus/interwave.c @@ -849,7 +849,6 @@ static int snd_interwave_isa_probe(struct device *pdev, static int snd_interwave_isa_remove(struct device *devptr, unsigned int dev) { snd_card_free(dev_get_drvdata(devptr)); - dev_set_drvdata(devptr, NULL); return 0; } diff --git a/sound/isa/msnd/msnd_pinnacle.c b/sound/isa/msnd/msnd_pinnacle.c index ddabb406b14c..81aeb934261a 100644 --- a/sound/isa/msnd/msnd_pinnacle.c +++ b/sound/isa/msnd/msnd_pinnacle.c @@ -1064,7 +1064,6 @@ cfg_error: static int snd_msnd_isa_remove(struct device *pdev, unsigned int dev) { snd_msnd_unload(dev_get_drvdata(pdev)); - dev_set_drvdata(pdev, NULL); return 0; } diff --git a/sound/isa/opl3sa2.c b/sound/isa/opl3sa2.c index 075777a6cf0b..cc01c419b7e9 100644 --- a/sound/isa/opl3sa2.c +++ b/sound/isa/opl3sa2.c @@ -757,7 +757,6 @@ static int snd_opl3sa2_pnp_detect(struct pnp_dev *pdev, static void snd_opl3sa2_pnp_remove(struct pnp_dev *pdev) { snd_card_free(pnp_get_drvdata(pdev)); - pnp_set_drvdata(pdev, NULL); } #ifdef CONFIG_PM @@ -900,7 +899,6 @@ static int snd_opl3sa2_isa_remove(struct device *devptr, unsigned int dev) { snd_card_free(dev_get_drvdata(devptr)); - dev_set_drvdata(devptr, NULL); return 0; } diff --git a/sound/isa/opti9xx/miro.c b/sound/isa/opti9xx/miro.c index c3da1df9371d..619753d96ca5 100644 --- a/sound/isa/opti9xx/miro.c +++ b/sound/isa/opti9xx/miro.c @@ -1495,7 +1495,6 @@ static int snd_miro_isa_remove(struct device *devptr, unsigned int dev) { snd_card_free(dev_get_drvdata(devptr)); - dev_set_drvdata(devptr, NULL); return 0; } diff --git a/sound/isa/opti9xx/opti92x-ad1848.c b/sound/isa/opti9xx/opti92x-ad1848.c index b41ed8661b23..103b33373fd4 100644 --- a/sound/isa/opti9xx/opti92x-ad1848.c +++ b/sound/isa/opti9xx/opti92x-ad1848.c @@ -1035,7 +1035,6 @@ static int snd_opti9xx_isa_remove(struct device *devptr, unsigned int dev) { snd_card_free(dev_get_drvdata(devptr)); - dev_set_drvdata(devptr, NULL); return 0; } diff --git a/sound/isa/sb/jazz16.c b/sound/isa/sb/jazz16.c index 4961da4e627c..356a6308392f 100644 --- a/sound/isa/sb/jazz16.c +++ b/sound/isa/sb/jazz16.c @@ -345,7 +345,6 @@ static int snd_jazz16_remove(struct device *devptr, unsigned int dev) { struct snd_card *card = dev_get_drvdata(devptr); - dev_set_drvdata(devptr, NULL); snd_card_free(card); return 0; } diff --git a/sound/isa/sb/sb16.c b/sound/isa/sb/sb16.c index 50dbec454f98..a4130993955f 100644 --- a/sound/isa/sb/sb16.c +++ b/sound/isa/sb/sb16.c @@ -566,7 +566,6 @@ static int snd_sb16_isa_probe(struct device *pdev, unsigned int dev) static int snd_sb16_isa_remove(struct device *pdev, unsigned int dev) { snd_card_free(dev_get_drvdata(pdev)); - dev_set_drvdata(pdev, NULL); return 0; } diff --git a/sound/isa/sb/sb8.c b/sound/isa/sb/sb8.c index 237d964ff8a6..a806ae90a944 100644 --- a/sound/isa/sb/sb8.c +++ b/sound/isa/sb/sb8.c @@ -208,7 +208,6 @@ static int snd_sb8_probe(struct device *pdev, unsigned int dev) static int snd_sb8_remove(struct device *pdev, unsigned int dev) { snd_card_free(dev_get_drvdata(pdev)); - dev_set_drvdata(pdev, NULL); return 0; } diff --git a/sound/isa/sc6000.c b/sound/isa/sc6000.c index 5376ebff845e..09d481b3ba7f 100644 --- a/sound/isa/sc6000.c +++ b/sound/isa/sc6000.c @@ -698,7 +698,6 @@ static int snd_sc6000_remove(struct device *devptr, unsigned int dev) release_region(port[dev], 0x10); release_region(mss_port[dev], 4); - dev_set_drvdata(devptr, NULL); snd_card_free(card); return 0; } diff --git a/sound/isa/sscape.c b/sound/isa/sscape.c index 42a009720b29..57b338973ede 100644 --- a/sound/isa/sscape.c +++ b/sound/isa/sscape.c @@ -1200,7 +1200,6 @@ _release_card: static int snd_sscape_remove(struct device *devptr, unsigned int dev) { snd_card_free(dev_get_drvdata(devptr)); - dev_set_drvdata(devptr, NULL); return 0; } diff --git a/sound/isa/wavefront/wavefront.c b/sound/isa/wavefront/wavefront.c index fe5dd982bd23..82dd76939fa0 100644 --- a/sound/isa/wavefront/wavefront.c +++ b/sound/isa/wavefront/wavefront.c @@ -581,7 +581,6 @@ static int snd_wavefront_isa_remove(struct device *devptr, unsigned int dev) { snd_card_free(dev_get_drvdata(devptr)); - dev_set_drvdata(devptr, NULL); return 0; } -- cgit v1.2.3 From f35e839a3ce730063174caaab8bf63432be553cf Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 29 May 2013 12:50:59 +0200 Subject: ALSA: Remove the rest of *_set_drvdata(NULL) calls A few calls are still left in parport drivers after this commit, which I'm not quite sure yet. Signed-off-by: Takashi Iwai --- sound/arm/aaci.c | 2 -- sound/drivers/dummy.c | 1 - sound/parisc/harmony.c | 3 --- sound/sparc/dbri.c | 2 -- sound/spi/at73c213.c | 1 - 5 files changed, 9 deletions(-) diff --git a/sound/arm/aaci.c b/sound/arm/aaci.c index aa5d8034890b..1ca8dc2ccb89 100644 --- a/sound/arm/aaci.c +++ b/sound/arm/aaci.c @@ -1076,8 +1076,6 @@ static int aaci_remove(struct amba_device *dev) { struct snd_card *card = amba_get_drvdata(dev); - amba_set_drvdata(dev, NULL); - if (card) { struct aaci *aaci = card->private_data; writel(0, aaci->base + AACI_MAINCR); diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c index fd798f753609..11048cc744d0 100644 --- a/sound/drivers/dummy.c +++ b/sound/drivers/dummy.c @@ -1129,7 +1129,6 @@ static int snd_dummy_probe(struct platform_device *devptr) static int snd_dummy_remove(struct platform_device *devptr) { snd_card_free(platform_get_drvdata(devptr)); - platform_set_drvdata(devptr, NULL); return 0; } diff --git a/sound/parisc/harmony.c b/sound/parisc/harmony.c index 0e66ba48d453..67f56a2cee6a 100644 --- a/sound/parisc/harmony.c +++ b/sound/parisc/harmony.c @@ -902,8 +902,6 @@ snd_harmony_free(struct snd_harmony *h) if (h->iobase) iounmap(h->iobase); - parisc_set_drvdata(h->dev, NULL); - kfree(h); return 0; } @@ -1016,7 +1014,6 @@ static int snd_harmony_remove(struct parisc_device *padev) { snd_card_free(parisc_get_drvdata(padev)); - parisc_set_drvdata(padev, NULL); return 0; } diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c index 75e6016d3efe..eee7afcae375 100644 --- a/sound/sparc/dbri.c +++ b/sound/sparc/dbri.c @@ -2670,8 +2670,6 @@ static int dbri_remove(struct platform_device *op) snd_dbri_free(card->private_data); snd_card_free(card); - dev_set_drvdata(&op->dev, NULL); - return 0; } diff --git a/sound/spi/at73c213.c b/sound/spi/at73c213.c index a1a24b979ed2..8e3d9a6c7a3b 100644 --- a/sound/spi/at73c213.c +++ b/sound/spi/at73c213.c @@ -1070,7 +1070,6 @@ out: ssc_free(chip->ssc); snd_card_free(card); - dev_set_drvdata(&spi->dev, NULL); return 0; } -- cgit v1.2.3 From 95f9a4d27eadcc1e76c9196e9d90cf41e9bba6ba Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Wed, 1 May 2013 23:42:47 -0400 Subject: UBI: document UBI_IOCVOLUP better in user header The current ioctl define implies that this func expects to be passed a 64bit number directly rather than a pointer to a 64bit. The code that processes this ioctl shows that it clearly expects a pointer. It'd be best if we could change the type to "__s64*", but that would change the generated ioctl number thus breaking the userland ABI. So just add a comment for intrepid developers. Signed-off-by: Mike Frysinger Signed-off-by: Artem Bityutskiy --- include/uapi/mtd/ubi-user.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/uapi/mtd/ubi-user.h b/include/uapi/mtd/ubi-user.h index 53cae1e11e57..723c324590c1 100644 --- a/include/uapi/mtd/ubi-user.h +++ b/include/uapi/mtd/ubi-user.h @@ -173,7 +173,10 @@ #define UBI_VOL_IOC_MAGIC 'O' -/* Start UBI volume update */ +/* Start UBI volume update + * Note: This actually takes a pointer (__s64*), but we can't change + * that without breaking the ABI on 32bit systems + */ #define UBI_IOCVOLUP _IOW(UBI_VOL_IOC_MAGIC, 0, __s64) /* LEB erasure command, used for debugging, disabled by default */ #define UBI_IOCEBER _IOW(UBI_VOL_IOC_MAGIC, 1, __s32) -- cgit v1.2.3 From c99f81887850cb593ea02bd14a68062547db8261 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 29 May 2013 18:09:22 +0800 Subject: HID: holtek-mouse: use module_hid_driver() to simplify the code module_hid_driver() makes the code simpler by eliminating boilerplate code. Reported-by: Andy Shevchenko Signed-off-by: Wei Yongjun Signed-off-by: Jiri Kosina --- drivers/hid/hid-holtek-mouse.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/hid/hid-holtek-mouse.c b/drivers/hid/hid-holtek-mouse.c index 6a23ee678138..7e6db3cf46f9 100644 --- a/drivers/hid/hid-holtek-mouse.c +++ b/drivers/hid/hid-holtek-mouse.c @@ -73,16 +73,5 @@ static struct hid_driver holtek_mouse_driver = { .report_fixup = holtek_mouse_report_fixup, }; -static int __init holtek_mouse_init(void) -{ - return hid_register_driver(&holtek_mouse_driver); -} - -static void __exit holtek_mouse_exit(void) -{ - hid_unregister_driver(&holtek_mouse_driver); -} - -module_exit(holtek_mouse_exit); -module_init(holtek_mouse_init); +module_hid_driver(holtek_mouse_driver); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 8b05537e03cb0e89abf51c72d26861db6df3933f Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Wed, 29 May 2013 09:18:28 +0200 Subject: Fix warning typo "CONFIG_RELCOATABLE" Fix typo "CONFIG_RELCOATABLE" in a warning message. While we're at it, also make that warning an actual sentence and fix comparable typos in some comments. Signed-off-by: Paul Bolle Signed-off-by: Jiri Kosina --- arch/powerpc/relocs_check.pl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/powerpc/relocs_check.pl b/arch/powerpc/relocs_check.pl index 7f5b83808862..3f46e8b9c56d 100755 --- a/arch/powerpc/relocs_check.pl +++ b/arch/powerpc/relocs_check.pl @@ -7,7 +7,7 @@ # as published by the Free Software Foundation; either version # 2 of the License, or (at your option) any later version. -# This script checks the relcoations of a vmlinux for "suspicious" +# This script checks the relocations of a vmlinux for "suspicious" # relocations. use strict; @@ -28,7 +28,7 @@ open(FD, "$objdump -R $vmlinux|") or die; while () { study $_; - # Only look at relcoation lines. + # Only look at relocation lines. next if (!/\s+R_/); # These relocations are okay @@ -45,7 +45,7 @@ while () { /\bR_PPC_ADDR16_HA\b/ or /\bR_PPC_RELATIVE\b/ or /\bR_PPC_NONE\b/); - # If we see this type of relcoation it's an idication that + # If we see this type of relocation it's an idication that # we /may/ be using an old version of binutils. if (/R_PPC64_UADDR64/) { $old_binutils++; @@ -61,6 +61,6 @@ if ($bad_relocs_count) { } if ($old_binutils) { - print "WARNING: You need at binutils >= 2.19 to build a ". - "CONFIG_RELCOATABLE kernel\n"; + print "WARNING: You need at least binutils >= 2.19 to build a ". + "CONFIG_RELOCATABLE kernel\n"; } -- cgit v1.2.3 From e3d1848f29d6f02d74ea978e1a699bde5802d7d4 Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Wed, 29 May 2013 09:30:19 +0200 Subject: lguest: fix CONFIG_PAE -> CONFIG_x86_PAE in comment Signed-off-by: Paul Bolle Signed-off-by: Jiri Kosina --- drivers/lguest/page_tables.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/lguest/page_tables.c b/drivers/lguest/page_tables.c index 5b9ac32801c7..a35d8d100165 100644 --- a/drivers/lguest/page_tables.c +++ b/drivers/lguest/page_tables.c @@ -70,7 +70,7 @@ /*H:320 * The page table code is curly enough to need helper functions to keep it * clear and clean. The kernel itself provides many of them; one advantage - * of insisting that the Guest and Host use the same CONFIG_PAE setting. + * of insisting that the Guest and Host use the same CONFIG_X86_PAE setting. * * There are two functions which return pointers to the shadow (aka "real") * page tables. -- cgit v1.2.3 From 4e713cdffba8d486e58eefe2125041eb5df9aa3a Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Thu, 23 May 2013 13:10:25 +0200 Subject: HID: Bluetooth: hidp: register HID devices async While l2cap_user callbacks are running, the whole hci_dev is locked. Even if we would add more fine-grained locking to HCI core, it would still be called from the non-reentrant rx work-queue and thus block the event processing. However, if we want to perform synchronous I/O during HID device registration (eg., to perform device-detection), we need the HCI core to be able to dispatch incoming data. Therefore, we now move device-registration to a separate worker. The HCI core can continue running and we add devices asynchronously in another kernel thread. Device removal is synchronized and waits for the worker to exit before calling the usual device removal functions. If l2cap_user->remove is called before the thread registered the devices, we set "terminate" to true and the thread will skip it. If l2cap_user->remove is called after it, we notice this as the device is no longer in HIDP_SESSION_PREPARING state and simply unregister the device as we did before. There is no new deadlock as we now call hidp_session_add_dev() with one lock less held (the HCI lock) and it cannot itself call back into HCI as it was called with the HCI-lock held before. One might wonder whether this can block during device unregistration. But we set "terminate" to true and wake the HIDP thread up _before_ unregistering the HID/input devices. Therefore, all pending HID I/O operations are canceled. All further I/O attempts will fail with ENODEV or EIO. So all latency we can get are few context-switches, but no timeouts or blocking I/O waits! This change also prepares for a long standing HID bug. All HID devices that register power_supply devices need to be able to handle callbacks during registration (a power_supply oddity that cannot easily be fixed). So with this patch available, we can allow HID I/O during registration by calling the recently introduced hid_device_io_start/stop helpers, which currently are a no-op for bluetooth due to this locking. Note that we cannot do the same for input devices. input-core doesn't allow us to call input_event() asynchronously to input_register_device(), which HID-core kindly allows (for good reasons). Fixing input-core to allow this isn't as easy as it sounds and is, beside simplifying HIDP, not really an improvement. Hence, we still register input devices synchronously as we did before. Only HID devices are registered asynchronously. Signed-off-by: David Herrmann Acked-by: Jiri Kosina Acked-by: Gustavo Padovan Tested-by: Daniel Nicoletti Signed-off-by: Jiri Kosina --- net/bluetooth/hidp/core.c | 56 +++++++++++++++++++++++++++++++++++++++-------- net/bluetooth/hidp/hidp.h | 2 ++ 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 940f5acb6694..46c6a148f0b3 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -850,6 +850,29 @@ static void hidp_session_dev_del(struct hidp_session *session) input_unregister_device(session->input); } +/* + * Asynchronous device registration + * HID device drivers might want to perform I/O during initialization to + * detect device types. Therefore, call device registration in a separate + * worker so the HIDP thread can schedule I/O operations. + * Note that this must be called after the worker thread was initialized + * successfully. This will then add the devices and increase session state + * on success, otherwise it will terminate the session thread. + */ +static void hidp_session_dev_work(struct work_struct *work) +{ + struct hidp_session *session = container_of(work, + struct hidp_session, + dev_init); + int ret; + + ret = hidp_session_dev_add(session); + if (!ret) + atomic_inc(&session->state); + else + hidp_session_terminate(session); +} + /* * Create new session object * Allocate session object, initialize static fields, copy input data into the @@ -897,6 +920,7 @@ static int hidp_session_new(struct hidp_session **out, const bdaddr_t *bdaddr, session->idle_to = req->idle_to; /* device management */ + INIT_WORK(&session->dev_init, hidp_session_dev_work); setup_timer(&session->timer, hidp_idle_timeout, (unsigned long)session); @@ -1035,8 +1059,8 @@ static void hidp_session_terminate(struct hidp_session *session) * Probe HIDP session * This is called from the l2cap_conn core when our l2cap_user object is bound * to the hci-connection. We get the session via the \user object and can now - * start the session thread, register the HID/input devices and link it into - * the global session list. + * start the session thread, link it into the global session list and + * schedule HID/input device registration. * The global session-list owns its own reference to the session object so you * can drop your own reference after registering the l2cap_user object. */ @@ -1058,21 +1082,30 @@ static int hidp_session_probe(struct l2cap_conn *conn, goto out_unlock; } + if (session->input) { + ret = hidp_session_dev_add(session); + if (ret) + goto out_unlock; + } + ret = hidp_session_start_sync(session); if (ret) - goto out_unlock; + goto out_del; - ret = hidp_session_dev_add(session); - if (ret) - goto out_stop; + /* HID device registration is async to allow I/O during probe */ + if (session->input) + atomic_inc(&session->state); + else + schedule_work(&session->dev_init); hidp_session_get(session); list_add(&session->list, &hidp_session_list); ret = 0; goto out_unlock; -out_stop: - hidp_session_terminate(session); +out_del: + if (session->input) + hidp_session_dev_del(session); out_unlock: up_write(&hidp_session_sem); return ret; @@ -1102,7 +1135,12 @@ static void hidp_session_remove(struct l2cap_conn *conn, down_write(&hidp_session_sem); hidp_session_terminate(session); - hidp_session_dev_del(session); + + cancel_work_sync(&session->dev_init); + if (session->input || + atomic_read(&session->state) > HIDP_SESSION_PREPARING) + hidp_session_dev_del(session); + list_del(&session->list); up_write(&hidp_session_sem); diff --git a/net/bluetooth/hidp/hidp.h b/net/bluetooth/hidp/hidp.h index 6162ce8606ac..9e6cc3553105 100644 --- a/net/bluetooth/hidp/hidp.h +++ b/net/bluetooth/hidp/hidp.h @@ -128,6 +128,7 @@ int hidp_get_conninfo(struct hidp_conninfo *ci); enum hidp_session_state { HIDP_SESSION_IDLING, + HIDP_SESSION_PREPARING, HIDP_SESSION_RUNNING, }; @@ -156,6 +157,7 @@ struct hidp_session { unsigned long idle_to; /* device management */ + struct work_struct dev_init; struct input_dev *input; struct hid_device *hid; struct timer_list timer; -- cgit v1.2.3 From d0a934b764c67b4bf626f5b7cf725a6e3066afd2 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Mon, 13 May 2013 17:01:30 +0200 Subject: HID: input: return ENODATA if reading battery attrs fails power_supply core has the bad habit of calling our battery callbacks from within power_supply_register(). Furthermore, if the callbacks fail with an unhandled error code, it will skip any uevent that it might currently process. So if HID-core registers battery devices, an "add" uevent is generated and the battery callbacks are called. These will gracefully fail due to timeouts as they might still hold locks on event processing. One could argue that this should be fixed in power_supply core, but the least we can do is to signal ENODATA so power_supply core will just skip the property and continue with the uevent. This fixes a bug where "add" and "remove" uevents are skipped for battery devices. upower is unable to track these devices and currently needs to ignore them. This patch also overwrites any other error code. I cannot see any reason why we should forward protocol- or I/O-errors to the power_supply core. We handle these errors in hid_ll_driver later, anyway, so just skip them. power_supply core cannot do anything useful with them, anyway, and we avoid skipping important uevents and confusing user-space. Thanks a lot to Daniel Nicoletti for pushing and investigating on this. Cc: Jiri Kosina Cc: Anton Vorontsov Cc: David Woodhouse Reported-by: Daniel Nicoletti Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 945b8158ec4c..c526a3ccbe5b 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -354,10 +354,10 @@ static int hidinput_get_battery_property(struct power_supply *psy, dev->battery_report_type); if (ret != 2) { - if (ret >= 0) - ret = -EINVAL; + ret = -ENODATA; break; } + ret = 0; if (dev->battery_min < dev->battery_max && buf[1] >= dev->battery_min && -- cgit v1.2.3 From 584d4623357db8f79029db76ec2b4953418018f0 Mon Sep 17 00:00:00 2001 From: Brian Pomerantz Date: Wed, 1 May 2013 17:10:44 -0700 Subject: UBI: fastmap break out of used PEB search While searching for PEB matches for each volume in the used PEB list, the search fails to stop when the PEB is found. This patch adds a break in the inner loop to stop the search when it is matched. Signed-off-by: Brian Pomerantz Acked-by: Richard Weinberger Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/fastmap.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/ubi/fastmap.c b/drivers/mtd/ubi/fastmap.c index 0648c6996d43..154275182b4b 100644 --- a/drivers/mtd/ubi/fastmap.c +++ b/drivers/mtd/ubi/fastmap.c @@ -727,8 +727,10 @@ static int ubi_attach_fastmap(struct ubi_device *ubi, aeb = NULL; list_for_each_entry(tmp_aeb, &used, u.list) { - if (tmp_aeb->pnum == pnum) + if (tmp_aeb->pnum == pnum) { aeb = tmp_aeb; + break; + } } /* This can happen if a PEB is already in an EBA known -- cgit v1.2.3 From adc2d1225bed4bbf68473e6b5c4b91d2445609cb Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Tue, 28 May 2013 15:17:52 +0300 Subject: wil6210: channel off by 1 WMI commands wants channel index, that is channel - 1 Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/wmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 5e01f4ee4de7..a091eb11e392 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -728,7 +728,7 @@ int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, u8 chan) .bcon_interval = cpu_to_le16(bi), .network_type = wmi_nettype, .disable_sec_offload = 1, - .channel = chan, + .channel = chan - 1, }; struct { struct wil6210_mbox_hdr_wmi wmi; -- cgit v1.2.3 From b338f74e33e33616e8fe498b5b09da8a84a7b218 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Tue, 28 May 2013 15:17:53 +0300 Subject: wil6210: Don't use wdev->sme_state sme_state is private wdev's variable. Track connection state internally Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/cfg80211.c | 4 ++++ drivers/net/wireless/ath/wil6210/main.c | 18 ++++++------------ drivers/net/wireless/ath/wil6210/wil6210.h | 1 + drivers/net/wireless/ath/wil6210/wmi.c | 3 ++- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index c5d4a87abaaf..4eb05d0818c3 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -322,12 +322,16 @@ static int wil_cfg80211_connect(struct wiphy *wiphy, * FW don't support scan after connection attempt */ set_bit(wil_status_dontscan, &wil->status); + set_bit(wil_status_fwconnecting, &wil->status); rc = wmi_send(wil, WMI_CONNECT_CMDID, &conn, sizeof(conn)); if (rc == 0) { /* Connect can take lots of time */ mod_timer(&wil->connect_timer, jiffies + msecs_to_jiffies(2000)); + } else { + clear_bit(wil_status_dontscan, &wil->status); + clear_bit(wil_status_fwconnecting, &wil->status); } out: diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index ea49c8a18e15..c97b864667c5 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -56,27 +56,21 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid) { uint i; struct net_device *ndev = wil_to_ndev(wil); - struct wireless_dev *wdev = wil->wdev; wil_dbg_misc(wil, "%s()\n", __func__); wil_link_off(wil); - clear_bit(wil_status_fwconnected, &wil->status); - - switch (wdev->sme_state) { - case CFG80211_SME_CONNECTED: - cfg80211_disconnected(ndev, WLAN_STATUS_UNSPECIFIED_FAILURE, + if (test_bit(wil_status_fwconnected, &wil->status)) { + clear_bit(wil_status_fwconnected, &wil->status); + cfg80211_disconnected(ndev, + WLAN_STATUS_UNSPECIFIED_FAILURE, NULL, 0, GFP_KERNEL); - break; - case CFG80211_SME_CONNECTING: + } else if (test_bit(wil_status_fwconnecting, &wil->status)) { cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, GFP_KERNEL); - break; - default: - break; } - + clear_bit(wil_status_fwconnecting, &wil->status); for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) wil_vring_fini_tx(wil, i); diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 2e3c26e1c975..373cf656f5b0 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -186,6 +186,7 @@ struct vring { enum { /* for wil6210_priv.status */ wil_status_fwready = 0, + wil_status_fwconnecting, wil_status_fwconnected, wil_status_dontscan, wil_status_reset_done, diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index a091eb11e392..527ffb543821 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -409,7 +409,7 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len) if ((wdev->iftype == NL80211_IFTYPE_STATION) || (wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) { - if (wdev->sme_state != CFG80211_SME_CONNECTING) { + if (!test_bit(wil_status_fwconnecting, &wil->status)) { wil_err(wil, "Not in connecting state\n"); return; } @@ -433,6 +433,7 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len) cfg80211_new_sta(ndev, evt->bssid, &sinfo, GFP_KERNEL); } + clear_bit(wil_status_fwconnecting, &wil->status); set_bit(wil_status_fwconnected, &wil->status); /* FIXME FW can transmit only ucast frames to peer */ -- cgit v1.2.3 From 9c8426fc79eef00464d33e774085395cac41d091 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Tue, 28 May 2013 18:04:44 +0200 Subject: ath9k_hw: fix PA predistortion miscalibration If any bins from the training data are skipped (i != max_index), the calculated compensation curve gets distorted, and the signal will be wildly overamplified. This may be the cause of the reported hardware damage that was caused by PA predistortion (because of which PAPRD was disabled by default). When calculating the x_est, Y, theta values, the use of max_index and i was reversed. i points to the bin index whereas max_index refers to the index of the calculated arrays. Note that PA predistortion is still disabled, it will be re-enabled after it has been properly validated. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_paprd.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c index 09c1f9da67a0..6343cc91953e 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c @@ -454,6 +454,8 @@ static bool create_pa_curve(u32 *data_L, u32 *data_U, u32 *pa_table, u16 *gain) if (accum_cnt <= thresh_accum_cnt) continue; + max_index++; + /* sum(tx amplitude) */ accum_tx = ((data_L[i] >> 16) & 0xffff) | ((data_U[i] & 0x7ff) << 16); @@ -468,20 +470,21 @@ static bool create_pa_curve(u32 *data_L, u32 *data_U, u32 *pa_table, u16 *gain) accum_tx <<= scale_factor; accum_rx <<= scale_factor; - x_est[i + 1] = (((accum_tx + accum_cnt) / accum_cnt) + 32) >> - scale_factor; + x_est[max_index] = + (((accum_tx + accum_cnt) / accum_cnt) + 32) >> + scale_factor; - Y[i + 1] = ((((accum_rx + accum_cnt) / accum_cnt) + 32) >> + Y[max_index] = + ((((accum_rx + accum_cnt) / accum_cnt) + 32) >> scale_factor) + - (1 << scale_factor) * max_index + 16; + (1 << scale_factor) * i + 16; if (accum_ang >= (1 << 26)) accum_ang -= 1 << 27; - theta[i + 1] = ((accum_ang * (1 << scale_factor)) + accum_cnt) / - accum_cnt; - - max_index++; + theta[max_index] = + ((accum_ang * (1 << scale_factor)) + accum_cnt) / + accum_cnt; } /* -- cgit v1.2.3 From b6c32f88740013b78eee68a604678b548750510f Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 29 May 2013 15:31:40 +0530 Subject: net: wireless: p54spi: Use module_spi_driver macro module_spi_driver() eliminates some boiler plate and makes code simpler. Signed-off-by: Sachin Kamat Signed-off-by: John W. Linville --- drivers/net/wireless/p54/p54spi.c | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/drivers/net/wireless/p54/p54spi.c b/drivers/net/wireless/p54/p54spi.c index 978e7eb26567..146e6530ae29 100644 --- a/drivers/net/wireless/p54/p54spi.c +++ b/drivers/net/wireless/p54/p54spi.c @@ -713,27 +713,7 @@ static struct spi_driver p54spi_driver = { .remove = p54spi_remove, }; -static int __init p54spi_init(void) -{ - int ret; - - ret = spi_register_driver(&p54spi_driver); - if (ret < 0) { - printk(KERN_ERR "failed to register SPI driver: %d", ret); - goto out; - } - -out: - return ret; -} - -static void __exit p54spi_exit(void) -{ - spi_unregister_driver(&p54spi_driver); -} - -module_init(p54spi_init); -module_exit(p54spi_exit); +module_spi_driver(p54spi_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Christian Lamparter "); -- cgit v1.2.3 From acba7bb3c246618c8af895a548ec1b0d3da050d0 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 29 May 2013 15:31:41 +0530 Subject: net: wireless: p54spi: Fix commenting style Make the commenting style consistent with networking block comment style as suggested by checkpatch. Signed-off-by: Sachin Kamat Signed-off-by: John W. Linville --- drivers/net/wireless/p54/p54spi.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/p54/p54spi.c b/drivers/net/wireless/p54/p54spi.c index 146e6530ae29..7fc46f26cf2b 100644 --- a/drivers/net/wireless/p54/p54spi.c +++ b/drivers/net/wireless/p54/p54spi.c @@ -42,8 +42,7 @@ MODULE_FIRMWARE("3826.arm"); -/* - * gpios should be handled in board files and provided via platform data, +/* gpios should be handled in board files and provided via platform data, * but because it's currently impossible for p54spi to have a header file * in include/linux, let's use module paramaters for now */ @@ -191,8 +190,7 @@ static int p54spi_request_eeprom(struct ieee80211_hw *dev) const struct firmware *eeprom; int ret; - /* - * allow users to customize their eeprom. + /* allow users to customize their eeprom. */ ret = request_firmware(&eeprom, "3826.eeprom", &priv->spi->dev); @@ -285,8 +283,7 @@ static void p54spi_power_on(struct p54s_priv *priv) gpio_set_value(p54spi_gpio_power, 1); enable_irq(gpio_to_irq(p54spi_gpio_irq)); - /* - * need to wait a while before device can be accessed, the length + /* need to wait a while before device can be accessed, the length * is just a guess */ msleep(10); @@ -365,7 +362,8 @@ static int p54spi_rx(struct p54s_priv *priv) /* Firmware may insert up to 4 padding bytes after the lmac header, * but it does not amend the size of SPI data transfer. * Such packets has correct data size in header, thus referencing - * past the end of allocated skb. Reserve extra 4 bytes for this case */ + * past the end of allocated skb. Reserve extra 4 bytes for this case + */ skb = dev_alloc_skb(len + 4); if (!skb) { p54spi_sleep(priv); @@ -383,7 +381,8 @@ static int p54spi_rx(struct p54s_priv *priv) } p54spi_sleep(priv); /* Put additional bytes to compensate for the possible - * alignment-caused truncation */ + * alignment-caused truncation + */ skb_put(skb, 4); if (p54_rx(priv->hw, skb) == 0) -- cgit v1.2.3 From 5f07d15a77e3098e7286b016247ecca20a0209d4 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 29 May 2013 13:04:20 +0300 Subject: mwifiex: dump small buffers with help of %*ph Signed-off-by: Andy Shevchenko Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/join.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/mwifiex/join.c b/drivers/net/wireless/mwifiex/join.c index 6bcb66e6e97c..122175af18c6 100644 --- a/drivers/net/wireless/mwifiex/join.c +++ b/drivers/net/wireless/mwifiex/join.c @@ -919,9 +919,8 @@ mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv, memcpy(&priv->curr_bss_params.data_rates, &adhoc_start->data_rate, priv->curr_bss_params.num_of_rates); - dev_dbg(adapter->dev, "info: ADHOC_S_CMD: rates=%02x %02x %02x %02x\n", - adhoc_start->data_rate[0], adhoc_start->data_rate[1], - adhoc_start->data_rate[2], adhoc_start->data_rate[3]); + dev_dbg(adapter->dev, "info: ADHOC_S_CMD: rates=%4ph\n", + adhoc_start->data_rate); dev_dbg(adapter->dev, "info: ADHOC_S_CMD: AD-HOC Start command is ready\n"); -- cgit v1.2.3 From a910e4a94f6923c8c988565525f017f687bf7205 Mon Sep 17 00:00:00 2001 From: Solomon Peachy Date: Fri, 24 May 2013 20:04:38 -0400 Subject: cw1200: add driver for the ST-E CW1100 & CW1200 WLAN chipsets Signed-off-by: Solomon Peachy Signed-off-by: John W. Linville --- MAINTAINERS | 5 + drivers/net/wireless/Kconfig | 1 + drivers/net/wireless/Makefile | 2 + drivers/net/wireless/cw1200/Kconfig | 46 + drivers/net/wireless/cw1200/Makefile | 24 + drivers/net/wireless/cw1200/bh.c | 616 +++++++ drivers/net/wireless/cw1200/bh.h | 28 + drivers/net/wireless/cw1200/cw1200.h | 332 ++++ drivers/net/wireless/cw1200/cw1200_sagrad.c | 145 ++ drivers/net/wireless/cw1200/cw1200_sdio.c | 409 +++++ drivers/net/wireless/cw1200/cw1200_spi.c | 480 ++++++ drivers/net/wireless/cw1200/debug.c | 664 ++++++++ drivers/net/wireless/cw1200/debug.h | 98 ++ drivers/net/wireless/cw1200/fwio.c | 525 ++++++ drivers/net/wireless/cw1200/fwio.h | 39 + drivers/net/wireless/cw1200/hwio.c | 311 ++++ drivers/net/wireless/cw1200/hwio.h | 247 +++ drivers/net/wireless/cw1200/itp.c | 730 ++++++++ drivers/net/wireless/cw1200/itp.h | 144 ++ drivers/net/wireless/cw1200/main.c | 618 +++++++ drivers/net/wireless/cw1200/pm.c | 367 ++++ drivers/net/wireless/cw1200/pm.h | 38 + drivers/net/wireless/cw1200/queue.c | 583 +++++++ drivers/net/wireless/cw1200/queue.h | 116 ++ drivers/net/wireless/cw1200/sbus.h | 37 + drivers/net/wireless/cw1200/scan.c | 461 +++++ drivers/net/wireless/cw1200/scan.h | 56 + drivers/net/wireless/cw1200/sta.c | 2406 +++++++++++++++++++++++++++ drivers/net/wireless/cw1200/sta.h | 123 ++ drivers/net/wireless/cw1200/txrx.c | 1474 ++++++++++++++++ drivers/net/wireless/cw1200/txrx.h | 106 ++ drivers/net/wireless/cw1200/wsm.c | 1884 +++++++++++++++++++++ drivers/net/wireless/cw1200/wsm.h | 1879 +++++++++++++++++++++ include/linux/cw1200_platform.h | 46 + 34 files changed, 15040 insertions(+) create mode 100644 drivers/net/wireless/cw1200/Kconfig create mode 100644 drivers/net/wireless/cw1200/Makefile create mode 100644 drivers/net/wireless/cw1200/bh.c create mode 100644 drivers/net/wireless/cw1200/bh.h create mode 100644 drivers/net/wireless/cw1200/cw1200.h create mode 100644 drivers/net/wireless/cw1200/cw1200_sagrad.c create mode 100644 drivers/net/wireless/cw1200/cw1200_sdio.c create mode 100644 drivers/net/wireless/cw1200/cw1200_spi.c create mode 100644 drivers/net/wireless/cw1200/debug.c create mode 100644 drivers/net/wireless/cw1200/debug.h create mode 100644 drivers/net/wireless/cw1200/fwio.c create mode 100644 drivers/net/wireless/cw1200/fwio.h create mode 100644 drivers/net/wireless/cw1200/hwio.c create mode 100644 drivers/net/wireless/cw1200/hwio.h create mode 100644 drivers/net/wireless/cw1200/itp.c create mode 100644 drivers/net/wireless/cw1200/itp.h create mode 100644 drivers/net/wireless/cw1200/main.c create mode 100644 drivers/net/wireless/cw1200/pm.c create mode 100644 drivers/net/wireless/cw1200/pm.h create mode 100644 drivers/net/wireless/cw1200/queue.c create mode 100644 drivers/net/wireless/cw1200/queue.h create mode 100644 drivers/net/wireless/cw1200/sbus.h create mode 100644 drivers/net/wireless/cw1200/scan.c create mode 100644 drivers/net/wireless/cw1200/scan.h create mode 100644 drivers/net/wireless/cw1200/sta.c create mode 100644 drivers/net/wireless/cw1200/sta.h create mode 100644 drivers/net/wireless/cw1200/txrx.c create mode 100644 drivers/net/wireless/cw1200/txrx.h create mode 100644 drivers/net/wireless/cw1200/wsm.c create mode 100644 drivers/net/wireless/cw1200/wsm.h create mode 100644 include/linux/cw1200_platform.h diff --git a/MAINTAINERS b/MAINTAINERS index 3d7782b9f90d..e5069c223391 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2299,6 +2299,11 @@ M: Jaya Kumar S: Maintained F: sound/pci/cs5535audio/ +CW1200 WLAN driver +M: Solomon Peachy +S: Maintained +F: drivers/net/wireless/cw1200/ + CX18 VIDEO4LINUX DRIVER M: Andy Walls L: ivtv-devel@ivtvdriver.org (moderated for non-subscribers) diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index f8f0156dff4e..200020eb3005 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -280,5 +280,6 @@ source "drivers/net/wireless/rtlwifi/Kconfig" source "drivers/net/wireless/ti/Kconfig" source "drivers/net/wireless/zd1211rw/Kconfig" source "drivers/net/wireless/mwifiex/Kconfig" +source "drivers/net/wireless/cw1200/Kconfig" endif # WLAN diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 67156efe14c4..0fab227025be 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -57,3 +57,5 @@ obj-$(CONFIG_MWIFIEX) += mwifiex/ obj-$(CONFIG_BRCMFMAC) += brcm80211/ obj-$(CONFIG_BRCMSMAC) += brcm80211/ + +obj-$(CONFIG_CW1200) += cw1200/ diff --git a/drivers/net/wireless/cw1200/Kconfig b/drivers/net/wireless/cw1200/Kconfig new file mode 100644 index 000000000000..13e36119ae9f --- /dev/null +++ b/drivers/net/wireless/cw1200/Kconfig @@ -0,0 +1,46 @@ +config CW1200 + tristate "CW1200 WLAN support" + depends on MAC80211 && CFG80211 + help + This is a driver for the ST-E CW1100 & CW1200 WLAN chipsets. + This option just enables the driver core, see below for + specific bus support. + +if CW1200 + +config CW1200_WLAN_SDIO + tristate "Support SDIO platforms" + depends on CW1200 && MMC + help + Enable support for the CW1200 connected via an SDIO bus. + +config CW1200_WLAN_SPI + tristate "Support SPI platforms" + depends on CW1200 && SPI + help + Enables support for the CW1200 connected via a SPI bus. + +config CW1200_WLAN_SAGRAD + tristate "Support Sagrad SG901-1091/1098 modules" + depends on CW1200_WLAN_SDIO + help + This provides the platform data glue to support the + Sagrad SG901-1091/1098 modules in their standard SDIO EVK. + It also includes example SPI platform data. + +menu "Driver debug features" + depends on CW1200 && DEBUG_FS + +config CW1200_ETF + bool "Enable CW1200 Engineering Test Framework hooks" + help + If you don't know what this is, just say N. + +config CW1200_ITP + bool "Enable ITP access" + help + If you don't know what this is, just say N. + +endmenu + +endif diff --git a/drivers/net/wireless/cw1200/Makefile b/drivers/net/wireless/cw1200/Makefile new file mode 100644 index 000000000000..c19737276be2 --- /dev/null +++ b/drivers/net/wireless/cw1200/Makefile @@ -0,0 +1,24 @@ +cw1200_core-y := \ + fwio.o \ + txrx.o \ + main.o \ + queue.o \ + hwio.o \ + bh.o \ + wsm.o \ + sta.o \ + scan.o \ + pm.o \ + debug.o +cw1200_core-$(CONFIG_CW1200_ITP) += itp.o + +# CFLAGS_sta.o += -DDEBUG + +cw1200_wlan_sdio-y := cw1200_sdio.o +cw1200_wlan_spi-y := cw1200_spi.o +cw1200_wlan_sagrad-y := cw1200_sagrad.o + +obj-$(CONFIG_CW1200) += cw1200_core.o +obj-$(CONFIG_CW1200_WLAN_SDIO) += cw1200_wlan_sdio.o +obj-$(CONFIG_CW1200_WLAN_SPI) += cw1200_wlan_spi.o +obj-$(CONFIG_CW1200_WLAN_SAGRAD) += cw1200_wlan_sagrad.o diff --git a/drivers/net/wireless/cw1200/bh.c b/drivers/net/wireless/cw1200/bh.c new file mode 100644 index 000000000000..cf7375f92fb3 --- /dev/null +++ b/drivers/net/wireless/cw1200/bh.c @@ -0,0 +1,616 @@ +/* + * Device handling thread implementation for mac80211 ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * Based on: + * ST-Ericsson UMAC CW1200 driver, which is + * Copyright (c) 2010, ST-Ericsson + * Author: Ajitpal Singh + * + * 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 +#include +#include +#include + +#include "cw1200.h" +#include "bh.h" +#include "hwio.h" +#include "wsm.h" +#include "sbus.h" +#include "debug.h" +#include "fwio.h" + +static int cw1200_bh(void *arg); + +#define DOWNLOAD_BLOCK_SIZE_WR (0x1000 - 4) +/* an SPI message cannot be bigger than (2"12-1)*2 bytes + * "*2" to cvt to bytes */ +#define MAX_SZ_RD_WR_BUFFERS (DOWNLOAD_BLOCK_SIZE_WR*2) +#define PIGGYBACK_CTRL_REG (2) +#define EFFECTIVE_BUF_SIZE (MAX_SZ_RD_WR_BUFFERS - PIGGYBACK_CTRL_REG) + +/* Suspend state privates */ +enum cw1200_bh_pm_state { + CW1200_BH_RESUMED = 0, + CW1200_BH_SUSPEND, + CW1200_BH_SUSPENDED, + CW1200_BH_RESUME, +}; + +typedef int (*cw1200_wsm_handler)(struct cw1200_common *priv, + u8 *data, size_t size); + +static void cw1200_bh_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, bh_work); + cw1200_bh(priv); +} + +int cw1200_register_bh(struct cw1200_common *priv) +{ + int err = 0; + /* Realtime workqueue */ + priv->bh_workqueue = alloc_workqueue("cw1200_bh", + WQ_MEM_RECLAIM | WQ_HIGHPRI + | WQ_CPU_INTENSIVE, 1); + + if (!priv->bh_workqueue) + return -ENOMEM; + + INIT_WORK(&priv->bh_work, cw1200_bh_work); + + pr_debug("[BH] register.\n"); + + atomic_set(&priv->bh_rx, 0); + atomic_set(&priv->bh_tx, 0); + atomic_set(&priv->bh_term, 0); + atomic_set(&priv->bh_suspend, CW1200_BH_RESUMED); + priv->bh_error = 0; + priv->hw_bufs_used = 0; + priv->buf_id_tx = 0; + priv->buf_id_rx = 0; + init_waitqueue_head(&priv->bh_wq); + init_waitqueue_head(&priv->bh_evt_wq); + + err = !queue_work(priv->bh_workqueue, &priv->bh_work); + WARN_ON(err); + return err; +} + +void cw1200_unregister_bh(struct cw1200_common *priv) +{ + atomic_add(1, &priv->bh_term); + wake_up(&priv->bh_wq); + + flush_workqueue(priv->bh_workqueue); + + destroy_workqueue(priv->bh_workqueue); + priv->bh_workqueue = NULL; + + pr_debug("[BH] unregistered.\n"); +} + +void cw1200_irq_handler(struct cw1200_common *priv) +{ + pr_debug("[BH] irq.\n"); + + /* Disable Interrupts! */ + /* NOTE: sbus_ops->lock already held */ + __cw1200_irq_enable(priv, 0); + + if (/* WARN_ON */(priv->bh_error)) + return; + + if (atomic_add_return(1, &priv->bh_rx) == 1) + wake_up(&priv->bh_wq); +} +EXPORT_SYMBOL_GPL(cw1200_irq_handler); + +void cw1200_bh_wakeup(struct cw1200_common *priv) +{ + pr_debug("[BH] wakeup.\n"); + if (priv->bh_error) { + pr_err("[BH] wakeup failed (BH error)\n"); + return; + } + + if (atomic_add_return(1, &priv->bh_tx) == 1) + wake_up(&priv->bh_wq); +} + +int cw1200_bh_suspend(struct cw1200_common *priv) +{ + pr_debug("[BH] suspend.\n"); + if (priv->bh_error) { + wiphy_warn(priv->hw->wiphy, "BH error -- can't suspend\n"); + return -EINVAL; + } + + atomic_set(&priv->bh_suspend, CW1200_BH_SUSPEND); + wake_up(&priv->bh_wq); + return wait_event_timeout(priv->bh_evt_wq, priv->bh_error || + (CW1200_BH_SUSPENDED == atomic_read(&priv->bh_suspend)), + 1 * HZ) ? 0 : -ETIMEDOUT; +} + +int cw1200_bh_resume(struct cw1200_common *priv) +{ + pr_debug("[BH] resume.\n"); + if (priv->bh_error) { + wiphy_warn(priv->hw->wiphy, "BH error -- can't resume\n"); + return -EINVAL; + } + + atomic_set(&priv->bh_suspend, CW1200_BH_RESUME); + wake_up(&priv->bh_wq); + return wait_event_timeout(priv->bh_evt_wq, priv->bh_error || + (CW1200_BH_RESUMED == atomic_read(&priv->bh_suspend)), + 1 * HZ) ? 0 : -ETIMEDOUT; +} + +static inline void wsm_alloc_tx_buffer(struct cw1200_common *priv) +{ + ++priv->hw_bufs_used; +} + +int wsm_release_tx_buffer(struct cw1200_common *priv, int count) +{ + int ret = 0; + int hw_bufs_used = priv->hw_bufs_used; + + priv->hw_bufs_used -= count; + if (WARN_ON(priv->hw_bufs_used < 0)) + ret = -1; + else if (hw_bufs_used >= priv->wsm_caps.input_buffers) + ret = 1; + if (!priv->hw_bufs_used) + wake_up(&priv->bh_evt_wq); + return ret; +} + +static int cw1200_bh_read_ctrl_reg(struct cw1200_common *priv, + u16 *ctrl_reg) +{ + int ret; + + ret = cw1200_reg_read_16(priv, + ST90TDS_CONTROL_REG_ID, ctrl_reg); + if (ret) { + ret = cw1200_reg_read_16(priv, + ST90TDS_CONTROL_REG_ID, ctrl_reg); + if (ret) + pr_err("[BH] Failed to read control register.\n"); + } + + return ret; +} + +static int cw1200_device_wakeup(struct cw1200_common *priv) +{ + u16 ctrl_reg; + int ret; + + pr_debug("[BH] Device wakeup.\n"); + + /* First, set the dpll register */ + ret = cw1200_reg_write_32(priv, ST90TDS_TSET_GEN_R_W_REG_ID, + cw1200_dpll_from_clk(priv->hw_refclk)); + if (WARN_ON(ret)) + return ret; + + /* To force the device to be always-on, the host sets WLAN_UP to 1 */ + ret = cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID, + ST90TDS_CONT_WUP_BIT); + if (WARN_ON(ret)) + return ret; + + ret = cw1200_bh_read_ctrl_reg(priv, &ctrl_reg); + if (WARN_ON(ret)) + return ret; + + /* If the device returns WLAN_RDY as 1, the device is active and will + * remain active. */ + if (ctrl_reg & ST90TDS_CONT_RDY_BIT) { + pr_debug("[BH] Device awake.\n"); + return 1; + } + + return 0; +} + +/* Must be called from BH thraed. */ +void cw1200_enable_powersave(struct cw1200_common *priv, + bool enable) +{ + pr_debug("[BH] Powerave is %s.\n", + enable ? "enabled" : "disabled"); + priv->powersave_enabled = enable; +} + +static int cw1200_bh_rx_helper(struct cw1200_common *priv, + uint16_t *ctrl_reg, + int *tx) +{ + size_t read_len = 0; + struct sk_buff *skb_rx = NULL; + struct wsm_hdr *wsm; + size_t wsm_len; + u16 wsm_id; + u8 wsm_seq; + int rx_resync = 1; + + size_t alloc_len; + u8 *data; + + read_len = (*ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) * 2; + if (!read_len) + return 0; /* No more work */ + + if (WARN_ON((read_len < sizeof(struct wsm_hdr)) || + (read_len > EFFECTIVE_BUF_SIZE))) { + pr_debug("Invalid read len: %zu (%04x)", + read_len, *ctrl_reg); + goto err; + } + + /* Add SIZE of PIGGYBACK reg (CONTROL Reg) + * to the NEXT Message length + 2 Bytes for SKB */ + read_len = read_len + 2; + + alloc_len = priv->sbus_ops->align_size( + priv->sbus_priv, read_len); + + /* Check if not exceeding CW1200 capabilities */ + if (WARN_ON_ONCE(alloc_len > EFFECTIVE_BUF_SIZE)) { + pr_debug("Read aligned len: %zu\n", + alloc_len); + } + + skb_rx = dev_alloc_skb(alloc_len); + if (WARN_ON(!skb_rx)) + goto err; + + skb_trim(skb_rx, 0); + skb_put(skb_rx, read_len); + data = skb_rx->data; + if (WARN_ON(!data)) + goto err; + + if (WARN_ON(cw1200_data_read(priv, data, alloc_len))) { + pr_err("rx blew up, len %zu\n", alloc_len); + goto err; + } + + /* Piggyback */ + *ctrl_reg = __le16_to_cpu( + ((__le16 *)data)[alloc_len / 2 - 1]); + + wsm = (struct wsm_hdr *)data; + wsm_len = __le16_to_cpu(wsm->len); + if (WARN_ON(wsm_len > read_len)) + goto err; + + if (priv->wsm_enable_wsm_dumps) + print_hex_dump_bytes("<-- ", + DUMP_PREFIX_NONE, + data, wsm_len); + + wsm_id = __le16_to_cpu(wsm->id) & 0xFFF; + wsm_seq = (__le16_to_cpu(wsm->id) >> 13) & 7; + + skb_trim(skb_rx, wsm_len); + + if (wsm_id == 0x0800) { + wsm_handle_exception(priv, + &data[sizeof(*wsm)], + wsm_len - sizeof(*wsm)); + goto err; + } else if (!rx_resync) { + if (WARN_ON(wsm_seq != priv->wsm_rx_seq)) + goto err; + } + priv->wsm_rx_seq = (wsm_seq + 1) & 7; + rx_resync = 0; + + if (wsm_id & 0x0400) { + int rc = wsm_release_tx_buffer(priv, 1); + if (WARN_ON(rc < 0)) + return rc; + else if (rc > 0) + *tx = 1; + } + + /* cw1200_wsm_rx takes care on SKB livetime */ + if (WARN_ON(wsm_handle_rx(priv, wsm_id, wsm, &skb_rx))) + goto err; + + if (skb_rx) { + dev_kfree_skb(skb_rx); + skb_rx = NULL; + } + + return 0; + +err: + if (skb_rx) { + dev_kfree_skb(skb_rx); + skb_rx = NULL; + } + return -1; +} + +static int cw1200_bh_tx_helper(struct cw1200_common *priv, + int *pending_tx, + int *tx_burst) +{ + size_t tx_len; + u8 *data; + int ret; + struct wsm_hdr *wsm; + + if (priv->device_can_sleep) { + ret = cw1200_device_wakeup(priv); + if (WARN_ON(ret < 0)) { /* Error in wakeup */ + *pending_tx = 1; + return 0; + } else if (ret) { /* Woke up */ + priv->device_can_sleep = false; + } else { /* Did not awake */ + *pending_tx = 1; + return 0; + } + } + + wsm_alloc_tx_buffer(priv); + ret = wsm_get_tx(priv, &data, &tx_len, tx_burst); + if (ret <= 0) { + wsm_release_tx_buffer(priv, 1); + if (WARN_ON(ret < 0)) + return ret; /* Error */ + return 0; /* No work */ + } + + wsm = (struct wsm_hdr *)data; + BUG_ON(tx_len < sizeof(*wsm)); + BUG_ON(__le16_to_cpu(wsm->len) != tx_len); + + atomic_add(1, &priv->bh_tx); + + tx_len = priv->sbus_ops->align_size( + priv->sbus_priv, tx_len); + + /* Check if not exceeding CW1200 capabilities */ + if (WARN_ON_ONCE(tx_len > EFFECTIVE_BUF_SIZE)) + pr_debug("Write aligned len: %zu\n", tx_len); + + wsm->id &= __cpu_to_le16(0xffff ^ WSM_TX_SEQ(WSM_TX_SEQ_MAX)); + wsm->id |= __cpu_to_le16(WSM_TX_SEQ(priv->wsm_tx_seq)); + + if (WARN_ON(cw1200_data_write(priv, data, tx_len))) { + pr_err("tx blew up, len %zu\n", tx_len); + wsm_release_tx_buffer(priv, 1); + return -1; /* Error */ + } + + if (priv->wsm_enable_wsm_dumps) + print_hex_dump_bytes("--> ", + DUMP_PREFIX_NONE, + data, + __le16_to_cpu(wsm->len)); + + wsm_txed(priv, data); + priv->wsm_tx_seq = (priv->wsm_tx_seq + 1) & WSM_TX_SEQ_MAX; + + if (*tx_burst > 1) { + cw1200_debug_tx_burst(priv); + return 1; /* Work remains */ + } + + return 0; +} + +static int cw1200_bh(void *arg) +{ + struct cw1200_common *priv = arg; + int rx, tx, term, suspend; + u16 ctrl_reg = 0; + int tx_allowed; + int pending_tx = 0; + int tx_burst; + long status; + u32 dummy; + int ret; + + for (;;) { + if (!priv->hw_bufs_used && + priv->powersave_enabled && + !priv->device_can_sleep && + !atomic_read(&priv->recent_scan)) { + status = 1 * HZ; + pr_debug("[BH] Device wakedown. No data.\n"); + cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID, 0); + priv->device_can_sleep = true; + } else if (priv->hw_bufs_used) { + /* Interrupt loss detection */ + status = 1 * HZ; + } else { + status = MAX_SCHEDULE_TIMEOUT; + } + + /* Dummy Read for SDIO retry mechanism*/ + if ((priv->hw_type != -1) && + (atomic_read(&priv->bh_rx) == 0) && + (atomic_read(&priv->bh_tx) == 0)) + cw1200_reg_read(priv, ST90TDS_CONFIG_REG_ID, + &dummy, sizeof(dummy)); + + pr_debug("[BH] waiting ...\n"); + status = wait_event_interruptible_timeout(priv->bh_wq, ({ + rx = atomic_xchg(&priv->bh_rx, 0); + tx = atomic_xchg(&priv->bh_tx, 0); + term = atomic_xchg(&priv->bh_term, 0); + suspend = pending_tx ? + 0 : atomic_read(&priv->bh_suspend); + (rx || tx || term || suspend || priv->bh_error); + }), status); + + pr_debug("[BH] - rx: %d, tx: %d, term: %d, suspend: %d, status: %ld\n", + rx, tx, term, suspend, status); + + /* Did an error occur? */ + if ((status < 0 && status != -ERESTARTSYS) || + term || priv->bh_error) { + break; + } + if (!status) { /* wait_event timed out */ + unsigned long timestamp = jiffies; + long timeout; + int pending = 0; + int i; + + /* Check to see if we have any outstanding frames */ + if (priv->hw_bufs_used && (!rx || !tx)) { + wiphy_warn(priv->hw->wiphy, + "Missed interrupt? (%d frames outstanding)\n", + priv->hw_bufs_used); + rx = 1; + + /* Get a timestamp of "oldest" frame */ + for (i = 0; i < 4; ++i) + pending += cw1200_queue_get_xmit_timestamp( + &priv->tx_queue[i], + ×tamp, + priv->pending_frame_id); + + /* Check if frame transmission is timed out. + * Add an extra second with respect to possible + * interrupt loss. + */ + timeout = timestamp + + WSM_CMD_LAST_CHANCE_TIMEOUT + + 1 * HZ - + jiffies; + + /* And terminate BH thread if the frame is "stuck" */ + if (pending && timeout < 0) { + wiphy_warn(priv->hw->wiphy, + "Timeout waiting for TX confirm (%d/%d pending, %ld vs %lu).\n", + priv->hw_bufs_used, pending, + timestamp, jiffies); + break; + } + } else if (!priv->device_can_sleep && + !atomic_read(&priv->recent_scan)) { + pr_debug("[BH] Device wakedown. Timeout.\n"); + cw1200_reg_write_16(priv, + ST90TDS_CONTROL_REG_ID, 0); + priv->device_can_sleep = true; + } + goto done; + } else if (suspend) { + pr_debug("[BH] Device suspend.\n"); + if (priv->powersave_enabled) { + pr_debug("[BH] Device wakedown. Suspend.\n"); + cw1200_reg_write_16(priv, + ST90TDS_CONTROL_REG_ID, 0); + priv->device_can_sleep = true; + } + + atomic_set(&priv->bh_suspend, CW1200_BH_SUSPENDED); + wake_up(&priv->bh_evt_wq); + status = wait_event_interruptible(priv->bh_wq, + CW1200_BH_RESUME == atomic_read(&priv->bh_suspend)); + if (status < 0) { + wiphy_err(priv->hw->wiphy, + "Failed to wait for resume: %ld.\n", + status); + break; + } + pr_debug("[BH] Device resume.\n"); + atomic_set(&priv->bh_suspend, CW1200_BH_RESUMED); + wake_up(&priv->bh_evt_wq); + atomic_add(1, &priv->bh_rx); + goto done; + } + + rx: + tx += pending_tx; + pending_tx = 0; + + if (cw1200_bh_read_ctrl_reg(priv, &ctrl_reg)) + break; + + /* Don't bother trying to rx unless we have data to read */ + if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) { + ret = cw1200_bh_rx_helper(priv, &ctrl_reg, &tx); + if (ret < 0) + break; + /* Double up here if there's more data.. */ + if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) { + ret = cw1200_bh_rx_helper(priv, &ctrl_reg, &tx); + if (ret < 0) + break; + } + } + + tx: + if (tx) { + tx = 0; + + BUG_ON(priv->hw_bufs_used > priv->wsm_caps.input_buffers); + tx_burst = priv->wsm_caps.input_buffers - priv->hw_bufs_used; + tx_allowed = tx_burst > 0; + + if (!tx_allowed) { + /* Buffers full. Ensure we process tx + * after we handle rx.. + */ + pending_tx = tx; + goto done_rx; + } + ret = cw1200_bh_tx_helper(priv, &pending_tx, &tx_burst); + if (ret < 0) + break; + if (ret > 0) /* More to transmit */ + tx = ret; + + /* Re-read ctrl reg */ + if (cw1200_bh_read_ctrl_reg(priv, &ctrl_reg)) + break; + } + + done_rx: + if (priv->bh_error) + break; + if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) + goto rx; + if (tx) + goto tx; + + done: + /* Re-enable device interrupts */ + priv->sbus_ops->lock(priv->sbus_priv); + __cw1200_irq_enable(priv, 1); + priv->sbus_ops->unlock(priv->sbus_priv); + } + + /* Explicitly disable device interrupts */ + priv->sbus_ops->lock(priv->sbus_priv); + __cw1200_irq_enable(priv, 0); + priv->sbus_ops->unlock(priv->sbus_priv); + + if (!term) { + pr_err("[BH] Fatal error, exiting.\n"); + priv->bh_error = 1; + /* TODO: schedule_work(recovery) */ + } + return 0; +} diff --git a/drivers/net/wireless/cw1200/bh.h b/drivers/net/wireless/cw1200/bh.h new file mode 100644 index 000000000000..af6a4853728f --- /dev/null +++ b/drivers/net/wireless/cw1200/bh.h @@ -0,0 +1,28 @@ +/* + * Device handling thread interface for mac80211 ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef CW1200_BH_H +#define CW1200_BH_H + +/* extern */ struct cw1200_common; + +int cw1200_register_bh(struct cw1200_common *priv); +void cw1200_unregister_bh(struct cw1200_common *priv); +void cw1200_irq_handler(struct cw1200_common *priv); +void cw1200_bh_wakeup(struct cw1200_common *priv); +int cw1200_bh_suspend(struct cw1200_common *priv); +int cw1200_bh_resume(struct cw1200_common *priv); +/* Must be called from BH thread. */ +void cw1200_enable_powersave(struct cw1200_common *priv, + bool enable); +int wsm_release_tx_buffer(struct cw1200_common *priv, int count); + +#endif /* CW1200_BH_H */ diff --git a/drivers/net/wireless/cw1200/cw1200.h b/drivers/net/wireless/cw1200/cw1200.h new file mode 100644 index 000000000000..2aa17ca60ba8 --- /dev/null +++ b/drivers/net/wireless/cw1200/cw1200.h @@ -0,0 +1,332 @@ +/* + * Common private data for ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * Based on the mac80211 Prism54 code, which is + * Copyright (c) 2006, Michael Wu + * + * Based on the islsm (softmac prism54) driver, which is: + * Copyright 2004-2006 Jean-Baptiste Note , et al. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef CW1200_H +#define CW1200_H + +#include +#include +#include +#include +#include + +#include "queue.h" +#include "wsm.h" +#include "scan.h" +#include "txrx.h" +#include "pm.h" + +/* Forward declarations */ +struct sbus_ops; +struct task_struct; +struct cw1200_debug_priv; +struct firmware; + +#ifdef CONFIG_CW1200_ETF +extern int etf_mode; +extern char *etf_firmware; +#endif + +#define CW1200_MAX_CTRL_FRAME_LEN (0x1000) + +#define CW1200_MAX_STA_IN_AP_MODE (5) +#define CW1200_LINK_ID_AFTER_DTIM (CW1200_MAX_STA_IN_AP_MODE + 1) +#define CW1200_LINK_ID_UAPSD (CW1200_MAX_STA_IN_AP_MODE + 2) +#define CW1200_LINK_ID_MAX (CW1200_MAX_STA_IN_AP_MODE + 3) +#define CW1200_MAX_REQUEUE_ATTEMPTS (5) + +#define CW1200_MAX_TID (8) + +#define CW1200_BLOCK_ACK_CNT (30) +#define CW1200_BLOCK_ACK_THLD (800) +#define CW1200_BLOCK_ACK_HIST (3) +#define CW1200_BLOCK_ACK_INTERVAL (1 * HZ / CW1200_BLOCK_ACK_HIST) + +#define CW1200_JOIN_TIMEOUT (1 * HZ) +#define CW1200_AUTH_TIMEOUT (5 * HZ) + +struct cw1200_ht_info { + struct ieee80211_sta_ht_cap ht_cap; + enum nl80211_channel_type channel_type; + u16 operation_mode; +}; + +/* Please keep order */ +enum cw1200_join_status { + CW1200_JOIN_STATUS_PASSIVE = 0, + CW1200_JOIN_STATUS_MONITOR, + CW1200_JOIN_STATUS_JOINING, + CW1200_JOIN_STATUS_PRE_STA, + CW1200_JOIN_STATUS_STA, + CW1200_JOIN_STATUS_IBSS, + CW1200_JOIN_STATUS_AP, +}; + +enum cw1200_link_status { + CW1200_LINK_OFF, + CW1200_LINK_RESERVE, + CW1200_LINK_SOFT, + CW1200_LINK_HARD, + CW1200_LINK_RESET, + CW1200_LINK_RESET_REMAP, +}; + +extern int cw1200_power_mode; +extern const char * const cw1200_fw_types[]; + +struct cw1200_link_entry { + unsigned long timestamp; + enum cw1200_link_status status; + enum cw1200_link_status prev_status; + u8 mac[ETH_ALEN]; + u8 buffered[CW1200_MAX_TID]; + struct sk_buff_head rx_queue; +}; + +struct cw1200_common { + /* interfaces to the rest of the stack */ + struct ieee80211_hw *hw; + struct ieee80211_vif *vif; + struct device *pdev; + + /* Statistics */ + struct ieee80211_low_level_stats stats; + + /* Our macaddr */ + u8 mac_addr[ETH_ALEN]; + + /* Hardware interface */ + const struct sbus_ops *sbus_ops; + struct sbus_priv *sbus_priv; + + /* Hardware information */ + enum { + HIF_9000_SILICON_VERSATILE = 0, + HIF_8601_VERSATILE, + HIF_8601_SILICON, + } hw_type; + enum { + CW1200_HW_REV_CUT10 = 10, + CW1200_HW_REV_CUT11 = 11, + CW1200_HW_REV_CUT20 = 20, + CW1200_HW_REV_CUT22 = 22, + CW1X60_HW_REV = 40, + } hw_revision; + int hw_refclk; + bool hw_have_5ghz; + const struct firmware *sdd; + char *sdd_path; + + struct cw1200_debug_priv *debug; + + struct workqueue_struct *workqueue; + struct mutex conf_mutex; + + struct cw1200_queue tx_queue[4]; + struct cw1200_queue_stats tx_queue_stats; + int tx_burst_idx; + + /* firmware/hardware info */ + unsigned int tx_hdr_len; + + /* Radio data */ + int output_power; + + /* BBP/MAC state */ + struct ieee80211_rate *rates; + struct ieee80211_rate *mcs_rates; + struct ieee80211_channel *channel; + struct wsm_edca_params edca; + struct wsm_tx_queue_params tx_queue_params; + struct wsm_mib_association_mode association_mode; + struct wsm_set_bss_params bss_params; + struct cw1200_ht_info ht_info; + struct wsm_set_pm powersave_mode; + struct wsm_set_pm firmware_ps_mode; + int cqm_rssi_thold; + unsigned cqm_rssi_hyst; + bool cqm_use_rssi; + int cqm_beacon_loss_count; + int channel_switch_in_progress; + wait_queue_head_t channel_switch_done; + u8 long_frame_max_tx_count; + u8 short_frame_max_tx_count; + int mode; + bool enable_beacon; + int beacon_int; + bool listening; + struct wsm_rx_filter rx_filter; + struct wsm_mib_multicast_filter multicast_filter; + bool has_multicast_subscription; + bool disable_beacon_filter; + struct work_struct update_filtering_work; + struct work_struct set_beacon_wakeup_period_work; + + u8 ba_rx_tid_mask; + u8 ba_tx_tid_mask; + + struct cw1200_pm_state pm_state; + + struct wsm_p2p_ps_modeinfo p2p_ps_modeinfo; + struct wsm_uapsd_info uapsd_info; + bool setbssparams_done; + bool bt_present; + u8 conf_listen_interval; + u32 listen_interval; + u32 erp_info; + u32 rts_threshold; + + /* BH */ + atomic_t bh_rx; + atomic_t bh_tx; + atomic_t bh_term; + atomic_t bh_suspend; + + struct workqueue_struct *bh_workqueue; + struct work_struct bh_work; + + int bh_error; + wait_queue_head_t bh_wq; + wait_queue_head_t bh_evt_wq; + u8 buf_id_tx; + u8 buf_id_rx; + u8 wsm_rx_seq; + u8 wsm_tx_seq; + int hw_bufs_used; + bool powersave_enabled; + bool device_can_sleep; + + /* Scan status */ + struct cw1200_scan scan; + /* Keep cw1200 awake (WUP = 1) 1 second after each scan to avoid + * FW issue with sleeping/waking up. */ + atomic_t recent_scan; + struct delayed_work clear_recent_scan_work; + + /* WSM */ + struct wsm_startup_ind wsm_caps; + struct mutex wsm_cmd_mux; + struct wsm_buf wsm_cmd_buf; + struct wsm_cmd wsm_cmd; + wait_queue_head_t wsm_cmd_wq; + wait_queue_head_t wsm_startup_done; + int firmware_ready; + atomic_t tx_lock; + + /* WSM debug */ + int wsm_enable_wsm_dumps; + + /* WSM Join */ + enum cw1200_join_status join_status; + u32 pending_frame_id; + bool join_pending; + struct delayed_work join_timeout; + struct work_struct unjoin_work; + struct work_struct join_complete_work; + int join_complete_status; + int join_dtim_period; + bool delayed_unjoin; + + /* TX/RX and security */ + s8 wep_default_key_id; + struct work_struct wep_key_work; + u32 key_map; + struct wsm_add_key keys[WSM_KEY_MAX_INDEX + 1]; + + /* AP powersave */ + u32 link_id_map; + struct cw1200_link_entry link_id_db[CW1200_MAX_STA_IN_AP_MODE]; + struct work_struct link_id_work; + struct delayed_work link_id_gc_work; + u32 sta_asleep_mask; + u32 pspoll_mask; + bool aid0_bit_set; + spinlock_t ps_state_lock; /* Protect power save state */ + bool buffered_multicasts; + bool tx_multicast; + struct work_struct set_tim_work; + struct work_struct set_cts_work; + struct work_struct multicast_start_work; + struct work_struct multicast_stop_work; + struct timer_list mcast_timeout; + + /* WSM events and CQM implementation */ + spinlock_t event_queue_lock; /* Protect event queue */ + struct list_head event_queue; + struct work_struct event_handler; + + struct delayed_work bss_loss_work; + spinlock_t bss_loss_lock; /* Protect BSS loss state */ + int bss_loss_state; + int bss_loss_confirm_id; + int delayed_link_loss; + struct work_struct bss_params_work; + + /* TX rate policy cache */ + struct tx_policy_cache tx_policy_cache; + struct work_struct tx_policy_upload_work; + + /* legacy PS mode switch in suspend */ + int ps_mode_switch_in_progress; + wait_queue_head_t ps_mode_switch_done; + + /* Workaround for WFD testcase 6.1.10*/ + struct work_struct linkid_reset_work; + u8 action_frame_sa[ETH_ALEN]; + u8 action_linkid; + +#ifdef CONFIG_CW1200_ETF + struct sk_buff_head etf_q; +#endif +}; + +struct cw1200_sta_priv { + int link_id; +}; + +/* interfaces for the drivers */ +int cw1200_core_probe(const struct sbus_ops *sbus_ops, + struct sbus_priv *sbus, + struct device *pdev, + struct cw1200_common **pself, + int ref_clk, const u8 *macaddr, + const char *sdd_path, bool have_5ghz); +void cw1200_core_release(struct cw1200_common *self); + +#define FWLOAD_BLOCK_SIZE (1024) + +static inline int cw1200_is_ht(const struct cw1200_ht_info *ht_info) +{ + return ht_info->channel_type != NL80211_CHAN_NO_HT; +} + +static inline int cw1200_ht_greenfield(const struct cw1200_ht_info *ht_info) +{ + return cw1200_is_ht(ht_info) && + (ht_info->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) && + !(ht_info->operation_mode & + IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); +} + +static inline int cw1200_ht_ampdu_density(const struct cw1200_ht_info *ht_info) +{ + if (!cw1200_is_ht(ht_info)) + return 0; + return ht_info->ht_cap.ampdu_density; +} + +#endif /* CW1200_H */ diff --git a/drivers/net/wireless/cw1200/cw1200_sagrad.c b/drivers/net/wireless/cw1200/cw1200_sagrad.c new file mode 100644 index 000000000000..a5ada0eda1dd --- /dev/null +++ b/drivers/net/wireless/cw1200/cw1200_sagrad.c @@ -0,0 +1,145 @@ +/* + * Platform glue data for ST-Ericsson CW1200 driver + * + * Copyright (c) 2013, Sagrad, Inc + * Author: Solomon Peachy + * + * 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 +#include + +MODULE_AUTHOR("Solomon Peachy "); +MODULE_DESCRIPTION("ST-Ericsson CW1200 Platform glue driver"); +MODULE_LICENSE("GPL"); + +/* Define just one of these. Feel free to customize as needed */ +#define SAGRAD_1091_1098_EVK_SDIO +/* #define SAGRAD_1091_1098_EVK_SPI */ + +#ifdef SAGRAD_1091_1098_EVK_SDIO +#if 0 +static struct resource cw1200_href_resources[] = { + { + .start = 215, /* fix me as appropriate */ + .end = 215, /* ditto */ + .flags = IORESOURCE_IO, + .name = "cw1200_wlan_reset", + }, + { + .start = 216, /* fix me as appropriate */ + .end = 216, /* ditto */ + .flags = IORESOURCE_IO, + .name = "cw1200_wlan_powerup", + }, + { + .start = NOMADIK_GPIO_TO_IRQ(216), /* fix me as appropriate */ + .end = NOMADIK_GPIO_TO_IRQ(216), /* ditto */ + .flags = IORESOURCE_IRQ, + .name = "cw1200_wlan_irq", + }, +}; +#endif + +static int cw1200_power_ctrl(const struct cw1200_platform_data_sdio *pdata, + bool enable) +{ + /* Control 3v3 and 1v8 to hardware as appropriate */ + /* Note this is not needed if it's controlled elsewhere or always on */ + + /* May require delay for power to stabilize */ + return 0; +} + +static int cw1200_clk_ctrl(const struct cw1200_platform_data_sdio *pdata, + bool enable) +{ + /* Turn CLK_32K off and on as appropriate. */ + /* Note this is not needed if it's always on */ + + /* May require delay for clock to stabilize */ + return 0; +} + +static struct cw1200_platform_data_sdio cw1200_platform_data = { + .ref_clk = 38400, + .have_5ghz = false, +#if 0 + .reset = &cw1200_href_resources[0], + .powerup = &cw1200_href_resources[1], + .irq = &cw1200_href_resources[2], +#endif + .power_ctrl = cw1200_power_ctrl, + .clk_ctrl = cw1200_clk_ctrl, +/* .macaddr = ??? */ + .sdd_file = "sdd_sagrad_1091_1098.bin", +}; +#endif + +#ifdef SAGRAD_1091_1098_EVK_SPI +/* Note that this is an example of integrating into your board support file */ +static struct resource cw1200_href_resources[] = { + { + .start = GPIO_RF_RESET, + .end = GPIO_RF_RESET, + .flags = IORESOURCE_IO, + .name = "cw1200_wlan_reset", + }, + { + .start = GPIO_RF_POWERUP, + .end = GPIO_RF_POWERUP, + .flags = IORESOURCE_IO, + .name = "cw1200_wlan_powerup", + }, +}; + +static int cw1200_power_ctrl(const struct cw1200_platform_data_spi *pdata, + bool enable) +{ + /* Control 3v3 and 1v8 to hardware as appropriate */ + /* Note this is not needed if it's controlled elsewhere or always on */ + + /* May require delay for power to stabilize */ + return 0; +} +static int cw1200_clk_ctrl(const struct cw1200_platform_data_spi *pdata, + bool enable) +{ + /* Turn CLK_32K off and on as appropriate. */ + /* Note this is not needed if it's always on */ + + /* May require delay for clock to stabilize */ + return 0; +} + +static struct cw1200_platform_data_spi cw1200_platform_data = { + .ref_clk = 38400, + .spi_bits_per_word = 16, + .reset = &cw1200_href_resources[0], + .powerup = &cw1200_href_resources[1], + .power_ctrl = cw1200_power_ctrl, + .clk_ctrl = cw1200_clk_ctrl, +/* .macaddr = ??? */ + .sdd_file = "sdd_sagrad_1091_1098.bin", +}; +static struct spi_board_info myboard_spi_devices[] __initdata = { + { + .modalias = "cw1200_wlan_spi", + .max_speed_hz = 10000000, /* 52MHz Max */ + .bus_num = 0, + .irq = WIFI_IRQ, + .platform_data = &cw1200_platform_data, + .chip_select = 0, + }, +}; +#endif + + +const void *cw1200_get_platform_data(void) +{ + return &cw1200_platform_data; +} +EXPORT_SYMBOL_GPL(cw1200_get_platform_data); diff --git a/drivers/net/wireless/cw1200/cw1200_sdio.c b/drivers/net/wireless/cw1200/cw1200_sdio.c new file mode 100644 index 000000000000..f6e22192d80a --- /dev/null +++ b/drivers/net/wireless/cw1200/cw1200_sdio.c @@ -0,0 +1,409 @@ +/* + * Mac80211 SDIO driver for ST-Ericsson CW1200 device + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cw1200.h" +#include "sbus.h" +#include +#include "hwio.h" + +MODULE_AUTHOR("Dmitry Tarnyagin "); +MODULE_DESCRIPTION("mac80211 ST-Ericsson CW1200 SDIO driver"); +MODULE_LICENSE("GPL"); + +#define SDIO_BLOCK_SIZE (512) + +struct sbus_priv { + struct sdio_func *func; + struct cw1200_common *core; + const struct cw1200_platform_data_sdio *pdata; +}; + +#ifndef SDIO_VENDOR_ID_STE +#define SDIO_VENDOR_ID_STE 0x0020 +#endif + +#ifndef SDIO_DEVICE_ID_STE_CW1200 +#define SDIO_DEVICE_ID_STE_CW1200 0x2280 +#endif + +static const struct sdio_device_id cw1200_sdio_ids[] = { + { SDIO_DEVICE(SDIO_VENDOR_ID_STE, SDIO_DEVICE_ID_STE_CW1200) }, + { /* end: all zeroes */ }, +}; + +/* sbus_ops implemetation */ + +static int cw1200_sdio_memcpy_fromio(struct sbus_priv *self, + unsigned int addr, + void *dst, int count) +{ + return sdio_memcpy_fromio(self->func, dst, addr, count); +} + +static int cw1200_sdio_memcpy_toio(struct sbus_priv *self, + unsigned int addr, + const void *src, int count) +{ + return sdio_memcpy_toio(self->func, addr, (void *)src, count); +} + +static void cw1200_sdio_lock(struct sbus_priv *self) +{ + sdio_claim_host(self->func); +} + +static void cw1200_sdio_unlock(struct sbus_priv *self) +{ + sdio_release_host(self->func); +} + +static void cw1200_sdio_irq_handler(struct sdio_func *func) +{ + struct sbus_priv *self = sdio_get_drvdata(func); + + /* note: sdio_host already claimed here. */ + if (self->core) + cw1200_irq_handler(self->core); +} + +static irqreturn_t cw1200_gpio_hardirq(int irq, void *dev_id) +{ + return IRQ_WAKE_THREAD; +} + +static irqreturn_t cw1200_gpio_irq(int irq, void *dev_id) +{ + struct sbus_priv *self = dev_id; + + if (self->core) { + sdio_claim_host(self->func); + cw1200_irq_handler(self->core); + sdio_release_host(self->func); + return IRQ_HANDLED; + } else { + return IRQ_NONE; + } +} + +static int cw1200_request_irq(struct sbus_priv *self) +{ + int ret; + const struct resource *irq = self->pdata->irq; + u8 cccr; + + cccr = sdio_f0_readb(self->func, SDIO_CCCR_IENx, &ret); + if (WARN_ON(ret)) + goto err; + + /* Master interrupt enable ... */ + cccr |= BIT(0); + + /* ... for our function */ + cccr |= BIT(self->func->num); + + sdio_f0_writeb(self->func, cccr, SDIO_CCCR_IENx, &ret); + if (WARN_ON(ret)) + goto err; + + ret = enable_irq_wake(irq->start); + if (WARN_ON(ret)) + goto err; + + /* Request the IRQ */ + ret = request_threaded_irq(irq->start, cw1200_gpio_hardirq, + cw1200_gpio_irq, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + irq->name, self); + if (WARN_ON(ret)) + goto err; + + return 0; + +err: + return ret; +} + +static int cw1200_sdio_irq_subscribe(struct sbus_priv *self) +{ + int ret = 0; + + pr_debug("SW IRQ subscribe\n"); + sdio_claim_host(self->func); + if (self->pdata->irq) + ret = cw1200_request_irq(self); + else + ret = sdio_claim_irq(self->func, cw1200_sdio_irq_handler); + + sdio_release_host(self->func); + return ret; +} + +static int cw1200_sdio_irq_unsubscribe(struct sbus_priv *self) +{ + int ret = 0; + + pr_debug("SW IRQ unsubscribe\n"); + + if (self->pdata->irq) { + disable_irq_wake(self->pdata->irq->start); + free_irq(self->pdata->irq->start, self); + } else { + sdio_claim_host(self->func); + ret = sdio_release_irq(self->func); + sdio_release_host(self->func); + } + return ret; +} + +static int cw1200_sdio_off(const struct cw1200_platform_data_sdio *pdata) +{ + const struct resource *reset = pdata->reset; + + if (reset) { + gpio_set_value(reset->start, 0); + msleep(30); /* Min is 2 * CLK32K cycles */ + gpio_free(reset->start); + } + + if (pdata->power_ctrl) + pdata->power_ctrl(pdata, false); + if (pdata->clk_ctrl) + pdata->clk_ctrl(pdata, false); + + return 0; +} + +static int cw1200_sdio_on(const struct cw1200_platform_data_sdio *pdata) +{ + const struct resource *reset = pdata->reset; + const struct resource *powerup = pdata->reset; + + /* Ensure I/Os are pulled low */ + if (reset) { + gpio_request(reset->start, reset->name); + gpio_direction_output(reset->start, 0); + } + if (powerup) { + gpio_request(powerup->start, powerup->name); + gpio_direction_output(powerup->start, 0); + } + if (reset || powerup) + msleep(50); /* Settle time */ + + /* Enable 3v3 and 1v8 to hardware */ + if (pdata->power_ctrl) { + if (pdata->power_ctrl(pdata, true)) { + pr_err("power_ctrl() failed!\n"); + return -1; + } + } + + /* Enable CLK32K */ + if (pdata->clk_ctrl) { + if (pdata->clk_ctrl(pdata, true)) { + pr_err("clk_ctrl() failed!\n"); + return -1; + } + msleep(10); /* Delay until clock is stable for 2 cycles */ + } + + /* Enable POWERUP signal */ + if (powerup) { + gpio_set_value(powerup->start, 1); + msleep(250); /* or more..? */ + } + /* Enable RSTn signal */ + if (reset) { + gpio_set_value(reset->start, 1); + msleep(50); /* Or more..? */ + } + return 0; +} + +static size_t cw1200_sdio_align_size(struct sbus_priv *self, size_t size) +{ + if (self->pdata->no_nptb) + size = round_up(size, SDIO_BLOCK_SIZE); + else + size = sdio_align_size(self->func, size); + + return size; +} + +static int cw1200_sdio_pm(struct sbus_priv *self, bool suspend) +{ + int ret = 0; + + if (self->pdata->irq) + ret = irq_set_irq_wake(self->pdata->irq->start, suspend); + return ret; +} + +static struct sbus_ops cw1200_sdio_sbus_ops = { + .sbus_memcpy_fromio = cw1200_sdio_memcpy_fromio, + .sbus_memcpy_toio = cw1200_sdio_memcpy_toio, + .lock = cw1200_sdio_lock, + .unlock = cw1200_sdio_unlock, + .align_size = cw1200_sdio_align_size, + .power_mgmt = cw1200_sdio_pm, +}; + +/* Probe Function to be called by SDIO stack when device is discovered */ +static int cw1200_sdio_probe(struct sdio_func *func, + const struct sdio_device_id *id) +{ + struct sbus_priv *self; + int status; + + pr_info("cw1200_wlan_sdio: Probe called\n"); + + /* We are only able to handle the wlan function */ + if (func->num != 0x01) + return -ENODEV; + + self = kzalloc(sizeof(*self), GFP_KERNEL); + if (!self) { + pr_err("Can't allocate SDIO sbus_priv.\n"); + return -ENOMEM; + } + + func->card->quirks |= MMC_QUIRK_LENIENT_FN0; + + self->pdata = cw1200_get_platform_data(); + self->func = func; + sdio_set_drvdata(func, self); + sdio_claim_host(func); + sdio_enable_func(func); + sdio_release_host(func); + + status = cw1200_sdio_irq_subscribe(self); + + status = cw1200_core_probe(&cw1200_sdio_sbus_ops, + self, &func->dev, &self->core, + self->pdata->ref_clk, + self->pdata->macaddr, + self->pdata->sdd_file, + self->pdata->have_5ghz); + if (status) { + cw1200_sdio_irq_unsubscribe(self); + sdio_claim_host(func); + sdio_disable_func(func); + sdio_release_host(func); + sdio_set_drvdata(func, NULL); + kfree(self); + } + + return status; +} + +/* Disconnect Function to be called by SDIO stack when + * device is disconnected */ +static void cw1200_sdio_disconnect(struct sdio_func *func) +{ + struct sbus_priv *self = sdio_get_drvdata(func); + + if (self) { + cw1200_sdio_irq_unsubscribe(self); + if (self->core) { + cw1200_core_release(self->core); + self->core = NULL; + } + sdio_claim_host(func); + sdio_disable_func(func); + sdio_release_host(func); + sdio_set_drvdata(func, NULL); + kfree(self); + } +} + +static int cw1200_sdio_suspend(struct device *dev) +{ + int ret; + struct sdio_func *func = dev_to_sdio_func(dev); + struct sbus_priv *self = sdio_get_drvdata(func); + + if (!cw1200_can_suspend(self->core)) + return -EAGAIN; + + /* Notify SDIO that CW1200 will remain powered during suspend */ + ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); + if (ret) + pr_err("Error setting SDIO pm flags: %i\n", ret); + + return ret; +} + +static int cw1200_sdio_resume(struct device *dev) +{ + return 0; +} + +static const struct dev_pm_ops cw1200_pm_ops = { + .suspend = cw1200_sdio_suspend, + .resume = cw1200_sdio_resume, +}; + +static struct sdio_driver sdio_driver = { + .name = "cw1200_wlan_sdio", + .id_table = cw1200_sdio_ids, + .probe = cw1200_sdio_probe, + .remove = cw1200_sdio_disconnect, + .drv = { + .pm = &cw1200_pm_ops, + } +}; + +/* Init Module function -> Called by insmod */ +static int __init cw1200_sdio_init(void) +{ + const struct cw1200_platform_data_sdio *pdata; + int ret; + + pdata = cw1200_get_platform_data(); + + if (cw1200_sdio_on(pdata)) { + ret = -1; + goto err; + } + + ret = sdio_register_driver(&sdio_driver); + if (ret) + goto err; + + return 0; + +err: + cw1200_sdio_off(pdata); + return ret; +} + +/* Called at Driver Unloading */ +static void __exit cw1200_sdio_exit(void) +{ + const struct cw1200_platform_data_sdio *pdata; + pdata = cw1200_get_platform_data(); + sdio_unregister_driver(&sdio_driver); + cw1200_sdio_off(pdata); +} + + +module_init(cw1200_sdio_init); +module_exit(cw1200_sdio_exit); diff --git a/drivers/net/wireless/cw1200/cw1200_spi.c b/drivers/net/wireless/cw1200/cw1200_spi.c new file mode 100644 index 000000000000..04af68534854 --- /dev/null +++ b/drivers/net/wireless/cw1200/cw1200_spi.c @@ -0,0 +1,480 @@ +/* + * Mac80211 SPI driver for ST-Ericsson CW1200 device + * + * Copyright (c) 2011, Sagrad Inc. + * Author: Solomon Peachy + * + * Based on cw1200_sdio.c + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "cw1200.h" +#include "sbus.h" +#include +#include "hwio.h" + +MODULE_AUTHOR("Solomon Peachy "); +MODULE_DESCRIPTION("mac80211 ST-Ericsson CW1200 SPI driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:cw1200_wlan_spi"); + +/* #define SPI_DEBUG */ + +struct sbus_priv { + struct spi_device *func; + struct cw1200_common *core; + const struct cw1200_platform_data_spi *pdata; + spinlock_t lock; /* Serialize all bus operations */ + int claimed; +}; + +#define SDIO_TO_SPI_ADDR(addr) ((addr & 0x1f)>>2) +#define SET_WRITE 0x7FFF /* usage: and operation */ +#define SET_READ 0x8000 /* usage: or operation */ + +/* + Notes on byte ordering: + LE: B0 B1 B2 B3 + BE: B3 B2 B1 B0 + + Hardware expects 32-bit data to be written as 16-bit BE words: + + B1 B0 B3 B2 + +*/ + +static int cw1200_spi_memcpy_fromio(struct sbus_priv *self, + unsigned int addr, + void *dst, int count) +{ + int ret, i; + uint16_t regaddr; + struct spi_message m; + + struct spi_transfer t_addr = { + .tx_buf = ®addr, + .len = sizeof(regaddr), + }; + struct spi_transfer t_msg = { + .rx_buf = dst, + .len = count, + }; + + regaddr = (SDIO_TO_SPI_ADDR(addr))<<12; + regaddr |= SET_READ; + regaddr |= (count>>1); + regaddr = cpu_to_le16(regaddr); + +#ifdef SPI_DEBUG + pr_info("READ : %04d from 0x%02x (%04x)\n", count, addr, + le16_to_cpu(regaddr)); +#endif + +#if defined(__LITTLE_ENDIAN) + /* We have to byteswap if the SPI bus is limited to 8b operation */ + if (self->func->bits_per_word == 8) +#endif + regaddr = swab16(regaddr); + + spi_message_init(&m); + spi_message_add_tail(&t_addr, &m); + spi_message_add_tail(&t_msg, &m); + ret = spi_sync(self->func, &m); + +#ifdef SPI_DEBUG + pr_info("READ : "); + for (i = 0; i < t_addr.len; i++) + printk("%02x ", ((u8 *)t_addr.tx_buf)[i]); + printk(" : "); + for (i = 0; i < t_msg.len; i++) + printk("%02x ", ((u8 *)t_msg.rx_buf)[i]); + printk("\n"); +#endif + +#if defined(__LITTLE_ENDIAN) + /* We have to byteswap if the SPI bus is limited to 8b operation */ + if (self->func->bits_per_word == 8) +#endif + { + uint16_t *buf = (uint16_t *)dst; + for (i = 0; i < ((count + 1) >> 1); i++) + buf[i] = swab16(buf[i]); + } + + return ret; +} + +static int cw1200_spi_memcpy_toio(struct sbus_priv *self, + unsigned int addr, + const void *src, int count) +{ + int rval, i; + uint16_t regaddr; + struct spi_transfer t_addr = { + .tx_buf = ®addr, + .len = sizeof(regaddr), + }; + struct spi_transfer t_msg = { + .tx_buf = src, + .len = count, + }; + struct spi_message m; + + regaddr = (SDIO_TO_SPI_ADDR(addr))<<12; + regaddr &= SET_WRITE; + regaddr |= (count>>1); + regaddr = cpu_to_le16(regaddr); + +#ifdef SPI_DEBUG + pr_info("WRITE: %04d to 0x%02x (%04x)\n", count, addr, + le16_to_cpu(regaddr)); +#endif + +#if defined(__LITTLE_ENDIAN) + /* We have to byteswap if the SPI bus is limited to 8b operation */ + if (self->func->bits_per_word == 8) +#endif + { + uint16_t *buf = (uint16_t *)src; + regaddr = swab16(regaddr); + for (i = 0; i < ((count + 1) >> 1); i++) + buf[i] = swab16(buf[i]); + } + +#ifdef SPI_DEBUG + pr_info("WRITE: "); + for (i = 0; i < t_addr.len; i++) + printk("%02x ", ((u8 *)t_addr.tx_buf)[i]); + printk(" : "); + for (i = 0; i < t_msg.len; i++) + printk("%02x ", ((u8 *)t_msg.tx_buf)[i]); + printk("\n"); +#endif + + spi_message_init(&m); + spi_message_add_tail(&t_addr, &m); + spi_message_add_tail(&t_msg, &m); + rval = spi_sync(self->func, &m); + +#ifdef SPI_DEBUG + pr_info("WROTE: %d\n", m.actual_length); +#endif + +#if defined(__LITTLE_ENDIAN) + /* We have to byteswap if the SPI bus is limited to 8b operation */ + if (self->func->bits_per_word == 8) +#endif + { + uint16_t *buf = (uint16_t *)src; + for (i = 0; i < ((count + 1) >> 1); i++) + buf[i] = swab16(buf[i]); + } + return rval; +} + +static void cw1200_spi_lock(struct sbus_priv *self) +{ + unsigned long flags; + + might_sleep(); + + spin_lock_irqsave(&self->lock, flags); + while (1) { + set_current_state(TASK_UNINTERRUPTIBLE); + if (!self->claimed) + break; + spin_unlock_irqrestore(&self->lock, flags); + schedule(); + spin_lock_irqsave(&self->lock, flags); + } + set_current_state(TASK_RUNNING); + self->claimed = 1; + spin_unlock_irqrestore(&self->lock, flags); + + return; +} + +static void cw1200_spi_unlock(struct sbus_priv *self) +{ + unsigned long flags; + + spin_lock_irqsave(&self->lock, flags); + self->claimed = 0; + spin_unlock_irqrestore(&self->lock, flags); + return; +} + +static irqreturn_t cw1200_spi_irq_handler(int irq, void *dev_id) +{ + struct sbus_priv *self = dev_id; + + if (self->core) { + cw1200_irq_handler(self->core); + return IRQ_HANDLED; + } else { + return IRQ_NONE; + } +} + +static int cw1200_spi_irq_subscribe(struct sbus_priv *self) +{ + int ret; + + pr_debug("SW IRQ subscribe\n"); + + ret = request_any_context_irq(self->func->irq, cw1200_spi_irq_handler, + IRQF_TRIGGER_HIGH, + "cw1200_wlan_irq", self); + if (WARN_ON(ret < 0)) + goto exit; + + ret = enable_irq_wake(self->func->irq); + if (WARN_ON(ret)) + goto free_irq; + + return 0; + +free_irq: + free_irq(self->func->irq, self); +exit: + return ret; +} + +static int cw1200_spi_irq_unsubscribe(struct sbus_priv *self) +{ + int ret = 0; + + pr_debug("SW IRQ unsubscribe\n"); + disable_irq_wake(self->func->irq); + free_irq(self->func->irq, self); + + return ret; +} + +static int cw1200_spi_off(const struct cw1200_platform_data_spi *pdata) +{ + const struct resource *reset = pdata->reset; + + if (reset) { + gpio_set_value(reset->start, 0); + msleep(30); /* Min is 2 * CLK32K cycles */ + gpio_free(reset->start); + } + + if (pdata->power_ctrl) + pdata->power_ctrl(pdata, false); + if (pdata->clk_ctrl) + pdata->clk_ctrl(pdata, false); + + return 0; +} + +static int cw1200_spi_on(const struct cw1200_platform_data_spi *pdata) +{ + const struct resource *reset = pdata->reset; + const struct resource *powerup = pdata->reset; + + /* Ensure I/Os are pulled low */ + if (reset) { + gpio_request(reset->start, reset->name); + gpio_direction_output(reset->start, 0); + } + if (powerup) { + gpio_request(powerup->start, powerup->name); + gpio_direction_output(powerup->start, 0); + } + if (reset || powerup) + msleep(10); /* Settle time? */ + + /* Enable 3v3 and 1v8 to hardware */ + if (pdata->power_ctrl) { + if (pdata->power_ctrl(pdata, true)) { + pr_err("power_ctrl() failed!\n"); + return -1; + } + } + + /* Enable CLK32K */ + if (pdata->clk_ctrl) { + if (pdata->clk_ctrl(pdata, true)) { + pr_err("clk_ctrl() failed!\n"); + return -1; + } + msleep(10); /* Delay until clock is stable for 2 cycles */ + } + + /* Enable POWERUP signal */ + if (powerup) { + gpio_set_value(powerup->start, 1); + msleep(250); /* or more..? */ + } + /* Enable RSTn signal */ + if (reset) { + gpio_set_value(reset->start, 1); + msleep(50); /* Or more..? */ + } + return 0; +} + +static size_t cw1200_spi_align_size(struct sbus_priv *self, size_t size) +{ + return size & 1 ? size + 1 : size; +} + +static int cw1200_spi_pm(struct sbus_priv *self, bool suspend) +{ + return irq_set_irq_wake(self->func->irq, suspend); +} + +static struct sbus_ops cw1200_spi_sbus_ops = { + .sbus_memcpy_fromio = cw1200_spi_memcpy_fromio, + .sbus_memcpy_toio = cw1200_spi_memcpy_toio, + .lock = cw1200_spi_lock, + .unlock = cw1200_spi_unlock, + .align_size = cw1200_spi_align_size, + .power_mgmt = cw1200_spi_pm, +}; + +/* Probe Function to be called by SPI stack when device is discovered */ +static int cw1200_spi_probe(struct spi_device *func) +{ + const struct cw1200_platform_data_spi *plat_data = + func->dev.platform_data; + struct sbus_priv *self; + int status; + + /* Sanity check speed */ + if (func->max_speed_hz > 52000000) + func->max_speed_hz = 52000000; + if (func->max_speed_hz < 1000000) + func->max_speed_hz = 1000000; + + /* Fix up transfer size */ + if (plat_data->spi_bits_per_word) + func->bits_per_word = plat_data->spi_bits_per_word; + if (!func->bits_per_word) + func->bits_per_word = 16; + + /* And finally.. */ + func->mode = SPI_MODE_0; + + pr_info("cw1200_wlan_spi: Probe called (CS %d M %d BPW %d CLK %d)\n", + func->chip_select, func->mode, func->bits_per_word, + func->max_speed_hz); + + if (cw1200_spi_on(plat_data)) { + pr_err("spi_on() failed!\n"); + return -1; + } + + if (spi_setup(func)) { + pr_err("spi_setup() failed!\n"); + return -1; + } + + self = kzalloc(sizeof(*self), GFP_KERNEL); + if (!self) { + pr_err("Can't allocate SPI sbus_priv."); + return -ENOMEM; + } + + self->pdata = plat_data; + self->func = func; + spin_lock_init(&self->lock); + + spi_set_drvdata(func, self); + + status = cw1200_spi_irq_subscribe(self); + + status = cw1200_core_probe(&cw1200_spi_sbus_ops, + self, &func->dev, &self->core, + self->pdata->ref_clk, + self->pdata->macaddr, + self->pdata->sdd_file, + self->pdata->have_5ghz); + + if (status) { + cw1200_spi_irq_unsubscribe(self); + cw1200_spi_off(plat_data); + kfree(self); + } + + return status; +} + +/* Disconnect Function to be called by SPI stack when device is disconnected */ +static int cw1200_spi_disconnect(struct spi_device *func) +{ + struct sbus_priv *self = spi_get_drvdata(func); + + if (self) { + cw1200_spi_irq_unsubscribe(self); + if (self->core) { + cw1200_core_release(self->core); + self->core = NULL; + } + kfree(self); + } + cw1200_spi_off(func->dev.platform_data); + + return 0; +} + +static int cw1200_spi_suspend(struct device *dev, pm_message_t state) +{ + struct sbus_priv *self = spi_get_drvdata(to_spi_device(dev)); + + if (!cw1200_can_suspend(self->core)) + return -EAGAIN; + + /* XXX notify host that we have to keep CW1200 powered on? */ + return 0; +} + +static int cw1200_spi_resume(struct device *dev) +{ + return 0; +} + +static struct spi_driver spi_driver = { + .probe = cw1200_spi_probe, + .remove = cw1200_spi_disconnect, + .driver = { + .name = "cw1200_wlan_spi", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + .suspend = cw1200_spi_suspend, + .resume = cw1200_spi_resume, + }, +}; + +/* Init Module function -> Called by insmod */ +static int __init cw1200_spi_init(void) +{ + return spi_register_driver(&spi_driver); +} + +/* Called at Driver Unloading */ +static void __exit cw1200_spi_exit(void) +{ + spi_unregister_driver(&spi_driver); +} + +module_init(cw1200_spi_init); +module_exit(cw1200_spi_exit); diff --git a/drivers/net/wireless/cw1200/debug.c b/drivers/net/wireless/cw1200/debug.c new file mode 100644 index 000000000000..b815181802e0 --- /dev/null +++ b/drivers/net/wireless/cw1200/debug.c @@ -0,0 +1,664 @@ +/* + * mac80211 glue code for mac80211 ST-Ericsson CW1200 drivers + * DebugFS code + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * 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 +#include +#include +#include "cw1200.h" +#include "debug.h" +#include "fwio.h" + +/* join_status */ +static const char * const cw1200_debug_join_status[] = { + "passive", + "monitor", + "station (joining)", + "station (not authenticated yet)", + "station", + "adhoc", + "access point", +}; + +/* WSM_JOIN_PREAMBLE_... */ +static const char * const cw1200_debug_preamble[] = { + "long", + "short", + "long on 1 and 2 Mbps", +}; + + +static const char * const cw1200_debug_link_id[] = { + "OFF", + "REQ", + "SOFT", + "HARD", +}; + +static const char *cw1200_debug_mode(int mode) +{ + switch (mode) { + case NL80211_IFTYPE_UNSPECIFIED: + return "unspecified"; + case NL80211_IFTYPE_MONITOR: + return "monitor"; + case NL80211_IFTYPE_STATION: + return "station"; + case NL80211_IFTYPE_ADHOC: + return "adhoc"; + case NL80211_IFTYPE_MESH_POINT: + return "mesh point"; + case NL80211_IFTYPE_AP: + return "access point"; + case NL80211_IFTYPE_P2P_CLIENT: + return "p2p client"; + case NL80211_IFTYPE_P2P_GO: + return "p2p go"; + default: + return "unsupported"; + } +} + +static void cw1200_queue_status_show(struct seq_file *seq, + struct cw1200_queue *q) +{ + int i; + seq_printf(seq, "Queue %d:\n", q->queue_id); + seq_printf(seq, " capacity: %zu\n", q->capacity); + seq_printf(seq, " queued: %zu\n", q->num_queued); + seq_printf(seq, " pending: %zu\n", q->num_pending); + seq_printf(seq, " sent: %zu\n", q->num_sent); + seq_printf(seq, " locked: %s\n", q->tx_locked_cnt ? "yes" : "no"); + seq_printf(seq, " overfull: %s\n", q->overfull ? "yes" : "no"); + seq_puts(seq, " link map: 0-> "); + for (i = 0; i < q->stats->map_capacity; ++i) + seq_printf(seq, "%.2d ", q->link_map_cache[i]); + seq_printf(seq, "<-%zu\n", q->stats->map_capacity); +} + +static void cw1200_debug_print_map(struct seq_file *seq, + struct cw1200_common *priv, + const char *label, + u32 map) +{ + int i; + seq_printf(seq, "%s0-> ", label); + for (i = 0; i < priv->tx_queue_stats.map_capacity; ++i) + seq_printf(seq, "%s ", (map & BIT(i)) ? "**" : ".."); + seq_printf(seq, "<-%zu\n", priv->tx_queue_stats.map_capacity - 1); +} + +static int cw1200_status_show(struct seq_file *seq, void *v) +{ + int i; + struct list_head *item; + struct cw1200_common *priv = seq->private; + struct cw1200_debug_priv *d = priv->debug; + + seq_puts(seq, "CW1200 Wireless LAN driver status\n"); + seq_printf(seq, "Hardware: %d.%d\n", + priv->wsm_caps.hw_id, + priv->wsm_caps.hw_subid); + seq_printf(seq, "Firmware: %s %d.%d\n", + cw1200_fw_types[priv->wsm_caps.fw_type], + priv->wsm_caps.fw_ver, + priv->wsm_caps.fw_build); + seq_printf(seq, "FW API: %d\n", + priv->wsm_caps.fw_api); + seq_printf(seq, "FW caps: 0x%.4X\n", + priv->wsm_caps.fw_cap); + seq_printf(seq, "FW label: '%s'\n", + priv->wsm_caps.fw_label); + seq_printf(seq, "Mode: %s%s\n", + cw1200_debug_mode(priv->mode), + priv->listening ? " (listening)" : ""); + seq_printf(seq, "Join state: %s\n", + cw1200_debug_join_status[priv->join_status]); + if (priv->channel) + seq_printf(seq, "Channel: %d%s\n", + priv->channel->hw_value, + priv->channel_switch_in_progress ? + " (switching)" : ""); + if (priv->rx_filter.promiscuous) + seq_puts(seq, "Filter: promisc\n"); + else if (priv->rx_filter.fcs) + seq_puts(seq, "Filter: fcs\n"); + if (priv->rx_filter.bssid) + seq_puts(seq, "Filter: bssid\n"); + if (!priv->disable_beacon_filter) + seq_puts(seq, "Filter: beacons\n"); + + if (priv->enable_beacon || + priv->mode == NL80211_IFTYPE_AP || + priv->mode == NL80211_IFTYPE_ADHOC || + priv->mode == NL80211_IFTYPE_MESH_POINT || + priv->mode == NL80211_IFTYPE_P2P_GO) + seq_printf(seq, "Beaconing: %s\n", + priv->enable_beacon ? + "enabled" : "disabled"); + + for (i = 0; i < 4; ++i) + seq_printf(seq, "EDCA(%d): %d, %d, %d, %d, %d\n", i, + priv->edca.params[i].cwmin, + priv->edca.params[i].cwmax, + priv->edca.params[i].aifns, + priv->edca.params[i].txop_limit, + priv->edca.params[i].max_rx_lifetime); + + if (priv->join_status == CW1200_JOIN_STATUS_STA) { + static const char *pm_mode = "unknown"; + switch (priv->powersave_mode.mode) { + case WSM_PSM_ACTIVE: + pm_mode = "off"; + break; + case WSM_PSM_PS: + pm_mode = "on"; + break; + case WSM_PSM_FAST_PS: + pm_mode = "dynamic"; + break; + } + seq_printf(seq, "Preamble: %s\n", + cw1200_debug_preamble[priv->association_mode.preamble]); + seq_printf(seq, "AMPDU spcn: %d\n", + priv->association_mode.mpdu_start_spacing); + seq_printf(seq, "Basic rate: 0x%.8X\n", + le32_to_cpu(priv->association_mode.basic_rate_set)); + seq_printf(seq, "Bss lost: %d beacons\n", + priv->bss_params.beacon_lost_count); + seq_printf(seq, "AID: %d\n", + priv->bss_params.aid); + seq_printf(seq, "Rates: 0x%.8X\n", + priv->bss_params.operational_rate_set); + seq_printf(seq, "Powersave: %s\n", pm_mode); + } + seq_printf(seq, "HT: %s\n", + cw1200_is_ht(&priv->ht_info) ? "on" : "off"); + if (cw1200_is_ht(&priv->ht_info)) { + seq_printf(seq, "Greenfield: %s\n", + cw1200_ht_greenfield(&priv->ht_info) ? "yes" : "no"); + seq_printf(seq, "AMPDU dens: %d\n", + cw1200_ht_ampdu_density(&priv->ht_info)); + } + seq_printf(seq, "RSSI thold: %d\n", + priv->cqm_rssi_thold); + seq_printf(seq, "RSSI hyst: %d\n", + priv->cqm_rssi_hyst); + seq_printf(seq, "Long retr: %d\n", + priv->long_frame_max_tx_count); + seq_printf(seq, "Short retr: %d\n", + priv->short_frame_max_tx_count); + spin_lock_bh(&priv->tx_policy_cache.lock); + i = 0; + list_for_each(item, &priv->tx_policy_cache.used) + ++i; + spin_unlock_bh(&priv->tx_policy_cache.lock); + seq_printf(seq, "RC in use: %d\n", i); + + seq_puts(seq, "\n"); + for (i = 0; i < 4; ++i) { + cw1200_queue_status_show(seq, &priv->tx_queue[i]); + seq_puts(seq, "\n"); + } + + cw1200_debug_print_map(seq, priv, "Link map: ", + priv->link_id_map); + cw1200_debug_print_map(seq, priv, "Asleep map: ", + priv->sta_asleep_mask); + cw1200_debug_print_map(seq, priv, "PSPOLL map: ", + priv->pspoll_mask); + + seq_puts(seq, "\n"); + + for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) { + if (priv->link_id_db[i].status) { + seq_printf(seq, "Link %d: %s, %pM\n", + i + 1, + cw1200_debug_link_id[priv->link_id_db[i].status], + priv->link_id_db[i].mac); + } + } + + seq_puts(seq, "\n"); + + seq_printf(seq, "BH status: %s\n", + atomic_read(&priv->bh_term) ? "terminated" : "alive"); + seq_printf(seq, "Pending RX: %d\n", + atomic_read(&priv->bh_rx)); + seq_printf(seq, "Pending TX: %d\n", + atomic_read(&priv->bh_tx)); + if (priv->bh_error) + seq_printf(seq, "BH errcode: %d\n", + priv->bh_error); + seq_printf(seq, "TX bufs: %d x %d bytes\n", + priv->wsm_caps.input_buffers, + priv->wsm_caps.input_buffer_size); + seq_printf(seq, "Used bufs: %d\n", + priv->hw_bufs_used); + seq_printf(seq, "Powermgmt: %s\n", + priv->powersave_enabled ? "on" : "off"); + seq_printf(seq, "Device: %s\n", + priv->device_can_sleep ? "asleep" : "awake"); + + spin_lock(&priv->wsm_cmd.lock); + seq_printf(seq, "WSM status: %s\n", + priv->wsm_cmd.done ? "idle" : "active"); + seq_printf(seq, "WSM cmd: 0x%.4X (%td bytes)\n", + priv->wsm_cmd.cmd, priv->wsm_cmd.len); + seq_printf(seq, "WSM retval: %d\n", + priv->wsm_cmd.ret); + spin_unlock(&priv->wsm_cmd.lock); + + seq_printf(seq, "Datapath: %s\n", + atomic_read(&priv->tx_lock) ? "locked" : "unlocked"); + if (atomic_read(&priv->tx_lock)) + seq_printf(seq, "TXlock cnt: %d\n", + atomic_read(&priv->tx_lock)); + + seq_printf(seq, "TXed: %d\n", + d->tx); + seq_printf(seq, "AGG TXed: %d\n", + d->tx_agg); + seq_printf(seq, "MULTI TXed: %d (%d)\n", + d->tx_multi, d->tx_multi_frames); + seq_printf(seq, "RXed: %d\n", + d->rx); + seq_printf(seq, "AGG RXed: %d\n", + d->rx_agg); + seq_printf(seq, "TX miss: %d\n", + d->tx_cache_miss); + seq_printf(seq, "TX align: %d\n", + d->tx_align); + seq_printf(seq, "TX burst: %d\n", + d->tx_burst); + seq_printf(seq, "TX TTL: %d\n", + d->tx_ttl); + seq_printf(seq, "Scan: %s\n", + atomic_read(&priv->scan.in_progress) ? "active" : "idle"); + + return 0; +} + +static int cw1200_status_open(struct inode *inode, struct file *file) +{ + return single_open(file, &cw1200_status_show, + inode->i_private); +} + +static const struct file_operations fops_status = { + .open = cw1200_status_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int cw1200_counters_show(struct seq_file *seq, void *v) +{ + int ret; + struct cw1200_common *priv = seq->private; + struct wsm_mib_counters_table counters; + + ret = wsm_get_counters_table(priv, &counters); + if (ret) + return ret; + +#define PUT_COUNTER(tab, name) \ + seq_printf(seq, "%s:" tab "%d\n", #name, \ + __le32_to_cpu(counters.name)) + + PUT_COUNTER("\t\t", plcp_errors); + PUT_COUNTER("\t\t", fcs_errors); + PUT_COUNTER("\t\t", tx_packets); + PUT_COUNTER("\t\t", rx_packets); + PUT_COUNTER("\t\t", rx_packet_errors); + PUT_COUNTER("\t", rx_decryption_failures); + PUT_COUNTER("\t\t", rx_mic_failures); + PUT_COUNTER("\t", rx_no_key_failures); + PUT_COUNTER("\t", tx_multicast_frames); + PUT_COUNTER("\t", tx_frames_success); + PUT_COUNTER("\t", tx_frame_failures); + PUT_COUNTER("\t", tx_frames_retried); + PUT_COUNTER("\t", tx_frames_multi_retried); + PUT_COUNTER("\t", rx_frame_duplicates); + PUT_COUNTER("\t\t", rts_success); + PUT_COUNTER("\t\t", rts_failures); + PUT_COUNTER("\t\t", ack_failures); + PUT_COUNTER("\t", rx_multicast_frames); + PUT_COUNTER("\t", rx_frames_success); + PUT_COUNTER("\t", rx_cmac_icv_errors); + PUT_COUNTER("\t\t", rx_cmac_replays); + PUT_COUNTER("\t", rx_mgmt_ccmp_replays); + +#undef PUT_COUNTER + + return 0; +} + +static int cw1200_counters_open(struct inode *inode, struct file *file) +{ + return single_open(file, &cw1200_counters_show, + inode->i_private); +} + +static const struct file_operations fops_counters = { + .open = cw1200_counters_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int cw1200_generic_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +#ifdef CONFIG_CW1200_ETF +static int cw1200_etf_out_show(struct seq_file *seq, void *v) +{ + struct cw1200_common *priv = seq->private; + struct sk_buff *skb; + u32 len = 0; + + skb = skb_dequeue(&priv->etf_q); + + if (skb) + len = skb->len; + + seq_write(seq, &len, sizeof(len)); + + if (skb) { + seq_write(seq, skb->data, len); + kfree_skb(skb); + } + + return 0; +} + +static int cw1200_etf_out_open(struct inode *inode, struct file *file) +{ + return single_open(file, &cw1200_etf_out_show, + inode->i_private); +} + +static const struct file_operations fops_etf_out = { + .open = cw1200_etf_out_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +struct etf_req_msg; +static int etf_request(struct cw1200_common *priv, + struct etf_req_msg *msg, u32 len); + +#define MAX_RX_SZE 2600 + +struct etf_in_state { + struct cw1200_common *priv; + u32 total_len; + u8 buf[MAX_RX_SZE]; + u32 written; +}; + +static int cw1200_etf_in_open(struct inode *inode, struct file *file) +{ + struct etf_in_state *etf = kmalloc(sizeof(struct etf_in_state), + GFP_KERNEL); + + if (!etf) + return -ENOMEM; + + etf->written = 0; + etf->total_len = 0; + etf->priv = inode->i_private; + + file->private_data = etf; + + return 0; +} + +static int cw1200_etf_in_release(struct inode *inode, struct file *file) +{ + kfree(file->private_data); + return 0; +} + +static ssize_t cw1200_etf_in_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct etf_in_state *etf = file->private_data; + + ssize_t written = 0; + + if (!etf->total_len) { + if (count < sizeof(etf->total_len)) { + pr_err("count < sizeof(total_len)\n"); + return -EINVAL; + } + + if (copy_from_user(&etf->total_len, user_buf, + sizeof(etf->total_len))) { + pr_err("copy_from_user (len) failed\n"); + return -EFAULT; + } + + written += sizeof(etf->total_len); + count -= sizeof(etf->total_len); + } + + if (!count) + goto done; + + if (copy_from_user(etf->buf + etf->written, user_buf + written, + count)) { + pr_err("copy_from_user (payload %zu) failed\n", count); + return -EFAULT; + } + + written += count; + etf->written += count; + + if (etf->written >= etf->total_len) { + if (etf_request(etf->priv, (struct etf_req_msg *)etf->buf, + etf->total_len)) { + pr_err("etf_request failed\n"); + return -EIO; + } + } + +done: + return written; +} + +static const struct file_operations fops_etf_in = { + .open = cw1200_etf_in_open, + .release = cw1200_etf_in_release, + .write = cw1200_etf_in_write, + .llseek = default_llseek, + .owner = THIS_MODULE, +}; +#endif /* CONFIG_CW1200_ETF */ + +static ssize_t cw1200_wsm_dumps(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct cw1200_common *priv = file->private_data; + char buf[1]; + + if (!count) + return -EINVAL; + if (copy_from_user(buf, user_buf, 1)) + return -EFAULT; + + if (buf[0] == '1') + priv->wsm_enable_wsm_dumps = 1; + else + priv->wsm_enable_wsm_dumps = 0; + + return count; +} + +static const struct file_operations fops_wsm_dumps = { + .open = cw1200_generic_open, + .write = cw1200_wsm_dumps, + .llseek = default_llseek, +}; + +int cw1200_debug_init(struct cw1200_common *priv) +{ + int ret = -ENOMEM; + struct cw1200_debug_priv *d = kzalloc(sizeof(struct cw1200_debug_priv), + GFP_KERNEL); + priv->debug = d; + if (!d) + return ret; + + d->debugfs_phy = debugfs_create_dir("cw1200", + priv->hw->wiphy->debugfsdir); + if (!d->debugfs_phy) + goto err; + + if (!debugfs_create_file("status", S_IRUSR, d->debugfs_phy, + priv, &fops_status)) + goto err; + + if (!debugfs_create_file("counters", S_IRUSR, d->debugfs_phy, + priv, &fops_counters)) + goto err; + +#ifdef CONFIG_CW1200_ETF + if (etf_mode) { + skb_queue_head_init(&priv->etf_q); + + if (!debugfs_create_file("etf_out", S_IRUSR, d->debugfs_phy, + priv, &fops_etf_out)) + goto err; + if (!debugfs_create_file("etf_in", S_IWUSR, d->debugfs_phy, + priv, &fops_etf_in)) + goto err; + } +#endif /* CONFIG_CW1200_ETF */ + + if (!debugfs_create_file("wsm_dumps", S_IWUSR, d->debugfs_phy, + priv, &fops_wsm_dumps)) + goto err; + + ret = cw1200_itp_init(priv); + if (ret) + goto err; + + return 0; + +err: + priv->debug = NULL; + debugfs_remove_recursive(d->debugfs_phy); + kfree(d); + return ret; +} + +void cw1200_debug_release(struct cw1200_common *priv) +{ + struct cw1200_debug_priv *d = priv->debug; + if (d) { + cw1200_itp_release(priv); + priv->debug = NULL; + kfree(d); + } +} + +#ifdef CONFIG_CW1200_ETF +struct cw1200_sdd { + u8 id; + u8 len; + u8 data[]; +}; + +struct etf_req_msg { + u32 id; + u32 len; + u8 data[]; +}; + +static int parse_sdd_file(struct cw1200_common *priv, u8 *data, u32 length) +{ + struct cw1200_sdd *ie; + + while (length > 0) { + ie = (struct cw1200_sdd *)data; + if (ie->id == SDD_REFERENCE_FREQUENCY_ELT_ID) { + priv->hw_refclk = cpu_to_le16(*((u16 *)ie->data)); + pr_info("Using Reference clock frequency %d KHz\n", + priv->hw_refclk); + break; + } + + length -= ie->len + sizeof(*ie); + data += ie->len + sizeof(*ie); + } + return 0; +} + +char *etf_firmware; + +#define ST90TDS_START_ADAPTER 0x09 /* Loads firmware too */ +#define ST90TDS_STOP_ADAPTER 0x0A +#define ST90TDS_CONFIG_ADAPTER 0x0E /* Send configuration params */ +#define ST90TDS_SBUS_READ 0x13 +#define ST90TDS_SBUS_WRITE 0x14 +#define ST90TDS_GET_DEVICE_OPTION 0x19 +#define ST90TDS_SET_DEVICE_OPTION 0x1A +#define ST90TDS_SEND_SDD 0x1D /* SDD File used to find DPLL */ + +#include "fwio.h" + +static int etf_request(struct cw1200_common *priv, + struct etf_req_msg *msg, + u32 len) +{ + int rval = -1; + switch (msg->id) { + case ST90TDS_START_ADAPTER: + etf_firmware = "cw1200_etf.bin"; + pr_info("ETF_START (len %d, '%s')\n", len, etf_firmware); + rval = cw1200_load_firmware(priv); + break; + case ST90TDS_STOP_ADAPTER: + pr_info("ETF_STOP (unhandled)\n"); + break; + case ST90TDS_SEND_SDD: + pr_info("ETF_SDD\n"); + rval = parse_sdd_file(priv, msg->data, msg->len); + break; + case ST90TDS_CONFIG_ADAPTER: + pr_info("ETF_CONFIG_ADAP (unhandled)\n"); + break; + case ST90TDS_SBUS_READ: + pr_info("ETF_SBUS_READ (unhandled)\n"); + break; + case ST90TDS_SBUS_WRITE: + pr_info("ETF_SBUS_WRITE (unhandled)\n"); + break; + case ST90TDS_SET_DEVICE_OPTION: + pr_info("ETF_SET_DEV_OPT (unhandled)\n"); + break; + default: + pr_info("ETF_PASSTHRU (0x%08x)\n", msg->id); + rval = wsm_raw_cmd(priv, (u8 *)msg, len); + break; + } + + return rval; +} +#endif /* CONFIG_CW1200_ETF */ diff --git a/drivers/net/wireless/cw1200/debug.h b/drivers/net/wireless/cw1200/debug.h new file mode 100644 index 000000000000..1fea5b29a819 --- /dev/null +++ b/drivers/net/wireless/cw1200/debug.h @@ -0,0 +1,98 @@ +/* + * DebugFS code for ST-Ericsson CW1200 mac80211 driver + * + * Copyright (c) 2011, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef CW1200_DEBUG_H_INCLUDED +#define CW1200_DEBUG_H_INCLUDED + +#include "itp.h" + +struct cw1200_debug_priv { + struct dentry *debugfs_phy; + int tx; + int tx_agg; + int rx; + int rx_agg; + int tx_multi; + int tx_multi_frames; + int tx_cache_miss; + int tx_align; + int tx_ttl; + int tx_burst; + int ba_cnt; + int ba_acc; + int ba_cnt_rx; + int ba_acc_rx; +#ifdef CONFIG_CW1200_ITP + struct cw1200_itp itp; +#endif /* CONFIG_CW1200_ITP */ +}; + +int cw1200_debug_init(struct cw1200_common *priv); +void cw1200_debug_release(struct cw1200_common *priv); + +static inline void cw1200_debug_txed(struct cw1200_common *priv) +{ + ++priv->debug->tx; +} + +static inline void cw1200_debug_txed_agg(struct cw1200_common *priv) +{ + ++priv->debug->tx_agg; +} + +static inline void cw1200_debug_txed_multi(struct cw1200_common *priv, + int count) +{ + ++priv->debug->tx_multi; + priv->debug->tx_multi_frames += count; +} + +static inline void cw1200_debug_rxed(struct cw1200_common *priv) +{ + ++priv->debug->rx; +} + +static inline void cw1200_debug_rxed_agg(struct cw1200_common *priv) +{ + ++priv->debug->rx_agg; +} + +static inline void cw1200_debug_tx_cache_miss(struct cw1200_common *priv) +{ + ++priv->debug->tx_cache_miss; +} + +static inline void cw1200_debug_tx_align(struct cw1200_common *priv) +{ + ++priv->debug->tx_align; +} + +static inline void cw1200_debug_tx_ttl(struct cw1200_common *priv) +{ + ++priv->debug->tx_ttl; +} + +static inline void cw1200_debug_tx_burst(struct cw1200_common *priv) +{ + ++priv->debug->tx_burst; +} + +static inline void cw1200_debug_ba(struct cw1200_common *priv, + int ba_cnt, int ba_acc, + int ba_cnt_rx, int ba_acc_rx) +{ + priv->debug->ba_cnt = ba_cnt; + priv->debug->ba_acc = ba_acc; + priv->debug->ba_cnt_rx = ba_cnt_rx; + priv->debug->ba_acc_rx = ba_acc_rx; +} + +#endif /* CW1200_DEBUG_H_INCLUDED */ diff --git a/drivers/net/wireless/cw1200/fwio.c b/drivers/net/wireless/cw1200/fwio.c new file mode 100644 index 000000000000..ad01cd2a59ec --- /dev/null +++ b/drivers/net/wireless/cw1200/fwio.c @@ -0,0 +1,525 @@ +/* + * Firmware I/O code for mac80211 ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * Based on: + * ST-Ericsson UMAC CW1200 driver which is + * Copyright (c) 2010, ST-Ericsson + * Author: Ajitpal Singh + * + * 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 +#include +#include +#include + +#include "cw1200.h" +#include "fwio.h" +#include "hwio.h" +#include "sbus.h" +#include "bh.h" + +static int cw1200_get_hw_type(u32 config_reg_val, int *major_revision) +{ + int hw_type = -1; + u32 silicon_type = (config_reg_val >> 24) & 0x7; + u32 silicon_vers = (config_reg_val >> 31) & 0x1; + + switch (silicon_type) { + case 0x00: + *major_revision = 1; + hw_type = HIF_9000_SILICON_VERSATILE; + break; + case 0x01: + case 0x02: /* CW1x00 */ + case 0x04: /* CW1x60 */ + *major_revision = silicon_type; + if (silicon_vers) + hw_type = HIF_8601_VERSATILE; + else + hw_type = HIF_8601_SILICON; + break; + default: + break; + } + + return hw_type; +} + +static int cw1200_load_firmware_cw1200(struct cw1200_common *priv) +{ + int ret, block, num_blocks; + unsigned i; + u32 val32; + u32 put = 0, get = 0; + u8 *buf = NULL; + const char *fw_path; + const struct firmware *firmware = NULL; + + /* Macroses are local. */ +#define APB_WRITE(reg, val) \ + do { \ + ret = cw1200_apb_write_32(priv, CW1200_APB(reg), (val)); \ + if (ret < 0) \ + goto error; \ + } while (0) +#define APB_READ(reg, val) \ + do { \ + ret = cw1200_apb_read_32(priv, CW1200_APB(reg), &(val)); \ + if (ret < 0) \ + goto error; \ + } while (0) +#define REG_WRITE(reg, val) \ + do { \ + ret = cw1200_reg_write_32(priv, (reg), (val)); \ + if (ret < 0) \ + goto error; \ + } while (0) +#define REG_READ(reg, val) \ + do { \ + ret = cw1200_reg_read_32(priv, (reg), &(val)); \ + if (ret < 0) \ + goto error; \ + } while (0) + + switch (priv->hw_revision) { + case CW1200_HW_REV_CUT10: + fw_path = FIRMWARE_CUT10; + if (!priv->sdd_path) + priv->sdd_path = SDD_FILE_10; + break; + case CW1200_HW_REV_CUT11: + fw_path = FIRMWARE_CUT11; + if (!priv->sdd_path) + priv->sdd_path = SDD_FILE_11; + break; + case CW1200_HW_REV_CUT20: + fw_path = FIRMWARE_CUT20; + if (!priv->sdd_path) + priv->sdd_path = SDD_FILE_20; + break; + case CW1200_HW_REV_CUT22: + fw_path = FIRMWARE_CUT22; + if (!priv->sdd_path) + priv->sdd_path = SDD_FILE_22; + break; + case CW1X60_HW_REV: + fw_path = FIRMWARE_CW1X60; + if (!priv->sdd_path) + priv->sdd_path = SDD_FILE_CW1X60; + break; + default: + pr_err("Invalid silicon revision %d.\n", priv->hw_revision); + return -EINVAL; + } + + /* Initialize common registers */ + APB_WRITE(DOWNLOAD_IMAGE_SIZE_REG, DOWNLOAD_ARE_YOU_HERE); + APB_WRITE(DOWNLOAD_PUT_REG, 0); + APB_WRITE(DOWNLOAD_GET_REG, 0); + APB_WRITE(DOWNLOAD_STATUS_REG, DOWNLOAD_PENDING); + APB_WRITE(DOWNLOAD_FLAGS_REG, 0); + + /* Write the NOP Instruction */ + REG_WRITE(ST90TDS_SRAM_BASE_ADDR_REG_ID, 0xFFF20000); + REG_WRITE(ST90TDS_AHB_DPORT_REG_ID, 0xEAFFFFFE); + + /* Release CPU from RESET */ + REG_READ(ST90TDS_CONFIG_REG_ID, val32); + val32 &= ~ST90TDS_CONFIG_CPU_RESET_BIT; + REG_WRITE(ST90TDS_CONFIG_REG_ID, val32); + + /* Enable Clock */ + val32 &= ~ST90TDS_CONFIG_CPU_CLK_DIS_BIT; + REG_WRITE(ST90TDS_CONFIG_REG_ID, val32); + +#ifdef CONFIG_CW1200_ETF + if (etf_mode) + fw_path = etf_firmware; +#endif + + /* Load a firmware file */ + ret = request_firmware(&firmware, fw_path, priv->pdev); + if (ret) { + pr_err("Can't load firmware file %s.\n", fw_path); + goto error; + } + + buf = kmalloc(DOWNLOAD_BLOCK_SIZE, GFP_KERNEL | GFP_DMA); + if (!buf) { + pr_err("Can't allocate firmware load buffer.\n"); + ret = -ENOMEM; + goto error; + } + + /* Check if the bootloader is ready */ + for (i = 0; i < 100; i += 1 + i / 2) { + APB_READ(DOWNLOAD_IMAGE_SIZE_REG, val32); + if (val32 == DOWNLOAD_I_AM_HERE) + break; + mdelay(i); + } /* End of for loop */ + + if (val32 != DOWNLOAD_I_AM_HERE) { + pr_err("Bootloader is not ready.\n"); + ret = -ETIMEDOUT; + goto error; + } + + /* Calculcate number of download blocks */ + num_blocks = (firmware->size - 1) / DOWNLOAD_BLOCK_SIZE + 1; + + /* Updating the length in Download Ctrl Area */ + val32 = firmware->size; /* Explicit cast from size_t to u32 */ + APB_WRITE(DOWNLOAD_IMAGE_SIZE_REG, val32); + + /* Firmware downloading loop */ + for (block = 0; block < num_blocks; block++) { + size_t tx_size; + size_t block_size; + + /* check the download status */ + APB_READ(DOWNLOAD_STATUS_REG, val32); + if (val32 != DOWNLOAD_PENDING) { + pr_err("Bootloader reported error %d.\n", val32); + ret = -EIO; + goto error; + } + + /* loop until put - get <= 24K */ + for (i = 0; i < 100; i++) { + APB_READ(DOWNLOAD_GET_REG, get); + if ((put - get) <= + (DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE)) + break; + mdelay(i); + } + + if ((put - get) > (DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE)) { + pr_err("Timeout waiting for FIFO.\n"); + ret = -ETIMEDOUT; + goto error; + } + + /* calculate the block size */ + tx_size = block_size = min((size_t)(firmware->size - put), + (size_t)DOWNLOAD_BLOCK_SIZE); + + memcpy(buf, &firmware->data[put], block_size); + if (block_size < DOWNLOAD_BLOCK_SIZE) { + memset(&buf[block_size], 0, + DOWNLOAD_BLOCK_SIZE - block_size); + tx_size = DOWNLOAD_BLOCK_SIZE; + } + + /* send the block to sram */ + ret = cw1200_apb_write(priv, + CW1200_APB(DOWNLOAD_FIFO_OFFSET + + (put & (DOWNLOAD_FIFO_SIZE - 1))), + buf, tx_size); + if (ret < 0) { + pr_err("Can't write firmware block @ %d!\n", + put & (DOWNLOAD_FIFO_SIZE - 1)); + goto error; + } + + /* update the put register */ + put += block_size; + APB_WRITE(DOWNLOAD_PUT_REG, put); + } /* End of firmware download loop */ + + /* Wait for the download completion */ + for (i = 0; i < 300; i += 1 + i / 2) { + APB_READ(DOWNLOAD_STATUS_REG, val32); + if (val32 != DOWNLOAD_PENDING) + break; + mdelay(i); + } + if (val32 != DOWNLOAD_SUCCESS) { + pr_err("Wait for download completion failed: 0x%.8X\n", val32); + ret = -ETIMEDOUT; + goto error; + } else { + pr_info("Firmware download completed.\n"); + ret = 0; + } + +error: + kfree(buf); + if (firmware) + release_firmware(firmware); + return ret; + +#undef APB_WRITE +#undef APB_READ +#undef REG_WRITE +#undef REG_READ +} + + +static int config_reg_read(struct cw1200_common *priv, u32 *val) +{ + switch (priv->hw_type) { + case HIF_9000_SILICON_VERSATILE: { + u16 val16; + int ret = cw1200_reg_read_16(priv, + ST90TDS_CONFIG_REG_ID, + &val16); + if (ret < 0) + return ret; + *val = val16; + return 0; + } + case HIF_8601_VERSATILE: + case HIF_8601_SILICON: + default: + cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, val); + break; + } + return 0; +} + +static int config_reg_write(struct cw1200_common *priv, u32 val) +{ + switch (priv->hw_type) { + case HIF_9000_SILICON_VERSATILE: + return cw1200_reg_write_16(priv, + ST90TDS_CONFIG_REG_ID, + (u16)val); + case HIF_8601_VERSATILE: + case HIF_8601_SILICON: + default: + return cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID, val); + break; + } + return 0; +} + +int cw1200_load_firmware(struct cw1200_common *priv) +{ + int ret; + int i; + u32 val32; + u16 val16; + int major_revision = -1; + + /* Read CONFIG Register */ + ret = cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32); + if (ret < 0) { + pr_err("Can't read config register.\n"); + goto out; + } + + if (val32 == 0 || val32 == 0xffffffff) { + pr_err("Bad config register value (0x%08x)\n", val32); + ret = -EIO; + goto out; + } + + priv->hw_type = cw1200_get_hw_type(val32, &major_revision); + if (priv->hw_type < 0) { + pr_err("Can't deduce hardware type.\n"); + ret = -ENOTSUPP; + goto out; + } + + /* Set DPLL Reg value, and read back to confirm writes work */ + ret = cw1200_reg_write_32(priv, ST90TDS_TSET_GEN_R_W_REG_ID, + cw1200_dpll_from_clk(priv->hw_refclk)); + if (ret < 0) { + pr_err("Can't write DPLL register.\n"); + goto out; + } + + msleep(20); + + ret = cw1200_reg_read_32(priv, + ST90TDS_TSET_GEN_R_W_REG_ID, &val32); + if (ret < 0) { + pr_err("Can't read DPLL register.\n"); + goto out; + } + + if (val32 != cw1200_dpll_from_clk(priv->hw_refclk)) { + pr_err("Unable to initialise DPLL register. Wrote 0x%.8X, Read 0x%.8X.\n", + cw1200_dpll_from_clk(priv->hw_refclk), val32); + ret = -EIO; + goto out; + } + + /* Set wakeup bit in device */ + ret = cw1200_reg_read_16(priv, ST90TDS_CONTROL_REG_ID, &val16); + if (ret < 0) { + pr_err("set_wakeup: can't read control register.\n"); + goto out; + } + + ret = cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID, + val16 | ST90TDS_CONT_WUP_BIT); + if (ret < 0) { + pr_err("set_wakeup: can't write control register.\n"); + goto out; + } + + /* Wait for wakeup */ + for (i = 0; i < 300; i += (1 + i / 2)) { + ret = cw1200_reg_read_16(priv, + ST90TDS_CONTROL_REG_ID, &val16); + if (ret < 0) { + pr_err("wait_for_wakeup: can't read control register.\n"); + goto out; + } + + if (val16 & ST90TDS_CONT_RDY_BIT) + break; + + msleep(i); + } + + if ((val16 & ST90TDS_CONT_RDY_BIT) == 0) { + pr_err("wait_for_wakeup: device is not responding.\n"); + ret = -ETIMEDOUT; + goto out; + } + + switch (major_revision) { + case 1: + /* CW1200 Hardware detection logic : Check for CUT1.1 */ + ret = cw1200_ahb_read_32(priv, CW1200_CUT_ID_ADDR, &val32); + if (ret) { + pr_err("HW detection: can't read CUT ID.\n"); + goto out; + } + + switch (val32) { + case CW1200_CUT_11_ID_STR: + pr_info("CW1x00 Cut 1.1 silicon detected.\n"); + priv->hw_revision = CW1200_HW_REV_CUT11; + break; + default: + pr_info("CW1x00 Cut 1.0 silicon detected.\n"); + priv->hw_revision = CW1200_HW_REV_CUT10; + break; + } + + /* According to ST-E, CUT<2.0 has busted BA TID0-3. + Just disable it entirely... + */ + priv->ba_rx_tid_mask = 0; + priv->ba_tx_tid_mask = 0; + break; + case 2: { + u32 ar1, ar2, ar3; + ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR, &ar1); + if (ret) { + pr_err("(1) HW detection: can't read CUT ID\n"); + goto out; + } + ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR + 4, &ar2); + if (ret) { + pr_err("(2) HW detection: can't read CUT ID.\n"); + goto out; + } + + ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR + 8, &ar3); + if (ret) { + pr_err("(3) HW detection: can't read CUT ID.\n"); + goto out; + } + + if (ar1 == CW1200_CUT_22_ID_STR1 && + ar2 == CW1200_CUT_22_ID_STR2 && + ar3 == CW1200_CUT_22_ID_STR3) { + pr_info("CW1x00 Cut 2.2 silicon detected.\n"); + priv->hw_revision = CW1200_HW_REV_CUT22; + } else { + pr_info("CW1x00 Cut 2.0 silicon detected.\n"); + priv->hw_revision = CW1200_HW_REV_CUT20; + } + break; + } + case 4: + pr_info("CW1x60 silicon detected.\n"); + priv->hw_revision = CW1X60_HW_REV; + break; + default: + pr_err("Unsupported silicon major revision %d.\n", + major_revision); + ret = -ENOTSUPP; + goto out; + } + + /* Checking for access mode */ + ret = config_reg_read(priv, &val32); + if (ret < 0) { + pr_err("Can't read config register.\n"); + goto out; + } + + if (!(val32 & ST90TDS_CONFIG_ACCESS_MODE_BIT)) { + pr_err("Device is already in QUEUE mode!\n"); + ret = -EINVAL; + goto out; + } + + switch (priv->hw_type) { + case HIF_8601_SILICON: + if (priv->hw_revision == CW1X60_HW_REV) { + pr_err("Can't handle CW1160/1260 firmware load yet.\n"); + ret = -ENOTSUPP; + goto out; + } + ret = cw1200_load_firmware_cw1200(priv); + break; + default: + pr_err("Can't perform firmware load for hw type %d.\n", + priv->hw_type); + ret = -ENOTSUPP; + goto out; + } + if (ret < 0) { + pr_err("Firmware load error.\n"); + goto out; + } + + /* Enable interrupt signalling */ + priv->sbus_ops->lock(priv->sbus_priv); + ret = __cw1200_irq_enable(priv, 1); + priv->sbus_ops->unlock(priv->sbus_priv); + if (ret < 0) + goto unsubscribe; + + /* Configure device for MESSSAGE MODE */ + ret = config_reg_read(priv, &val32); + if (ret < 0) { + pr_err("Can't read config register.\n"); + goto unsubscribe; + } + ret = config_reg_write(priv, val32 & ~ST90TDS_CONFIG_ACCESS_MODE_BIT); + if (ret < 0) { + pr_err("Can't write config register.\n"); + goto unsubscribe; + } + + /* Unless we read the CONFIG Register we are + * not able to get an interrupt + */ + mdelay(10); + config_reg_read(priv, &val32); + +out: + return ret; + +unsubscribe: + /* Disable interrupt signalling */ + priv->sbus_ops->lock(priv->sbus_priv); + ret = __cw1200_irq_enable(priv, 0); + priv->sbus_ops->unlock(priv->sbus_priv); + return ret; +} diff --git a/drivers/net/wireless/cw1200/fwio.h b/drivers/net/wireless/cw1200/fwio.h new file mode 100644 index 000000000000..ea3099362cdf --- /dev/null +++ b/drivers/net/wireless/cw1200/fwio.h @@ -0,0 +1,39 @@ +/* + * Firmware API for mac80211 ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * Based on: + * ST-Ericsson UMAC CW1200 driver which is + * Copyright (c) 2010, ST-Ericsson + * Author: Ajitpal Singh + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef FWIO_H_INCLUDED +#define FWIO_H_INCLUDED + +#define BOOTLOADER_CW1X60 "boot_cw1x60.bin" +#define FIRMWARE_CW1X60 "wsm_cw1x60.bin" +#define FIRMWARE_CUT22 "wsm_22.bin" +#define FIRMWARE_CUT20 "wsm_20.bin" +#define FIRMWARE_CUT11 "wsm_11.bin" +#define FIRMWARE_CUT10 "wsm_10.bin" +#define SDD_FILE_CW1X60 "sdd_cw1x60.bin" +#define SDD_FILE_22 "sdd_22.bin" +#define SDD_FILE_20 "sdd_20.bin" +#define SDD_FILE_11 "sdd_11.bin" +#define SDD_FILE_10 "sdd_10.bin" + +int cw1200_load_firmware(struct cw1200_common *priv); + +/* SDD definitions */ +#define SDD_PTA_CFG_ELT_ID 0xEB +#define SDD_REFERENCE_FREQUENCY_ELT_ID 0xc5 +u32 cw1200_dpll_from_clk(u16 clk); + +#endif diff --git a/drivers/net/wireless/cw1200/hwio.c b/drivers/net/wireless/cw1200/hwio.c new file mode 100644 index 000000000000..1af7b3d421b2 --- /dev/null +++ b/drivers/net/wireless/cw1200/hwio.c @@ -0,0 +1,311 @@ +/* + * Low-level device IO routines for ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * Based on: + * ST-Ericsson UMAC CW1200 driver, which is + * Copyright (c) 2010, ST-Ericsson + * Author: Ajitpal Singh + * + * 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 + +#include "cw1200.h" +#include "hwio.h" +#include "sbus.h" + + /* Sdio addr is 4*spi_addr */ +#define SPI_REG_ADDR_TO_SDIO(spi_reg_addr) ((spi_reg_addr) << 2) +#define SDIO_ADDR17BIT(buf_id, mpf, rfu, reg_id_ofs) \ + ((((buf_id) & 0x1F) << 7) \ + | (((mpf) & 1) << 6) \ + | (((rfu) & 1) << 5) \ + | (((reg_id_ofs) & 0x1F) << 0)) +#define MAX_RETRY 3 + + +static int __cw1200_reg_read(struct cw1200_common *priv, u16 addr, + void *buf, size_t buf_len, int buf_id) +{ + u16 addr_sdio; + u32 sdio_reg_addr_17bit; + + /* Check if buffer is aligned to 4 byte boundary */ + if (WARN_ON(((unsigned long)buf & 3) && (buf_len > 4))) { + pr_err("buffer is not aligned.\n"); + return -EINVAL; + } + + /* Convert to SDIO Register Address */ + addr_sdio = SPI_REG_ADDR_TO_SDIO(addr); + sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio); + + return priv->sbus_ops->sbus_memcpy_fromio(priv->sbus_priv, + sdio_reg_addr_17bit, + buf, buf_len); +} + +static int __cw1200_reg_write(struct cw1200_common *priv, u16 addr, + const void *buf, size_t buf_len, int buf_id) +{ + u16 addr_sdio; + u32 sdio_reg_addr_17bit; + + /* Convert to SDIO Register Address */ + addr_sdio = SPI_REG_ADDR_TO_SDIO(addr); + sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio); + + return priv->sbus_ops->sbus_memcpy_toio(priv->sbus_priv, + sdio_reg_addr_17bit, + buf, buf_len); +} + +static inline int __cw1200_reg_read_32(struct cw1200_common *priv, + u16 addr, u32 *val) +{ + int i = __cw1200_reg_read(priv, addr, val, sizeof(*val), 0); + *val = le32_to_cpu(*val); + return i; +} + +static inline int __cw1200_reg_write_32(struct cw1200_common *priv, + u16 addr, u32 val) +{ + val = cpu_to_le32(val); + return __cw1200_reg_write(priv, addr, &val, sizeof(val), 0); +} + +static inline int __cw1200_reg_read_16(struct cw1200_common *priv, + u16 addr, u16 *val) +{ + int i = __cw1200_reg_read(priv, addr, val, sizeof(*val), 0); + *val = le16_to_cpu(*val); + return i; +} + +static inline int __cw1200_reg_write_16(struct cw1200_common *priv, + u16 addr, u16 val) +{ + val = cpu_to_le16(val); + return __cw1200_reg_write(priv, addr, &val, sizeof(val), 0); +} + +int cw1200_reg_read(struct cw1200_common *priv, u16 addr, void *buf, + size_t buf_len) +{ + int ret; + priv->sbus_ops->lock(priv->sbus_priv); + ret = __cw1200_reg_read(priv, addr, buf, buf_len, 0); + priv->sbus_ops->unlock(priv->sbus_priv); + return ret; +} + +int cw1200_reg_write(struct cw1200_common *priv, u16 addr, const void *buf, + size_t buf_len) +{ + int ret; + priv->sbus_ops->lock(priv->sbus_priv); + ret = __cw1200_reg_write(priv, addr, buf, buf_len, 0); + priv->sbus_ops->unlock(priv->sbus_priv); + return ret; +} + +int cw1200_data_read(struct cw1200_common *priv, void *buf, size_t buf_len) +{ + int ret, retry = 1; + int buf_id_rx = priv->buf_id_rx; + + priv->sbus_ops->lock(priv->sbus_priv); + + while (retry <= MAX_RETRY) { + ret = __cw1200_reg_read(priv, + ST90TDS_IN_OUT_QUEUE_REG_ID, buf, + buf_len, buf_id_rx + 1); + if (!ret) { + buf_id_rx = (buf_id_rx + 1) & 3; + priv->buf_id_rx = buf_id_rx; + break; + } else { + retry++; + mdelay(1); + pr_err("error :[%d]\n", ret); + } + } + + priv->sbus_ops->unlock(priv->sbus_priv); + return ret; +} + +int cw1200_data_write(struct cw1200_common *priv, const void *buf, + size_t buf_len) +{ + int ret, retry = 1; + int buf_id_tx = priv->buf_id_tx; + + priv->sbus_ops->lock(priv->sbus_priv); + + while (retry <= MAX_RETRY) { + ret = __cw1200_reg_write(priv, + ST90TDS_IN_OUT_QUEUE_REG_ID, buf, + buf_len, buf_id_tx); + if (!ret) { + buf_id_tx = (buf_id_tx + 1) & 31; + priv->buf_id_tx = buf_id_tx; + break; + } else { + retry++; + mdelay(1); + pr_err("error :[%d]\n", ret); + } + } + + priv->sbus_ops->unlock(priv->sbus_priv); + return ret; +} + +int cw1200_indirect_read(struct cw1200_common *priv, u32 addr, void *buf, + size_t buf_len, u32 prefetch, u16 port_addr) +{ + u32 val32 = 0; + int i, ret; + + if ((buf_len / 2) >= 0x1000) { + pr_err("Can't read more than 0xfff words.\n"); + return -EINVAL; + goto out; + } + + priv->sbus_ops->lock(priv->sbus_priv); + /* Write address */ + ret = __cw1200_reg_write_32(priv, ST90TDS_SRAM_BASE_ADDR_REG_ID, addr); + if (ret < 0) { + pr_err("Can't write address register.\n"); + goto out; + } + + /* Read CONFIG Register Value - We will read 32 bits */ + ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32); + if (ret < 0) { + pr_err("Can't read config register.\n"); + goto out; + } + + /* Set PREFETCH bit */ + ret = __cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID, + val32 | prefetch); + if (ret < 0) { + pr_err("Can't write prefetch bit.\n"); + goto out; + } + + /* Check for PRE-FETCH bit to be cleared */ + for (i = 0; i < 20; i++) { + ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32); + if (ret < 0) { + pr_err("Can't check prefetch bit.\n"); + goto out; + } + if (!(val32 & prefetch)) + break; + + mdelay(i); + } + + if (val32 & prefetch) { + pr_err("Prefetch bit is not cleared.\n"); + goto out; + } + + /* Read data port */ + ret = __cw1200_reg_read(priv, port_addr, buf, buf_len, 0); + if (ret < 0) { + pr_err("Can't read data port.\n"); + goto out; + } + +out: + priv->sbus_ops->unlock(priv->sbus_priv); + return ret; +} + +int cw1200_apb_write(struct cw1200_common *priv, u32 addr, const void *buf, + size_t buf_len) +{ + int ret; + + if ((buf_len / 2) >= 0x1000) { + pr_err("Can't write more than 0xfff words.\n"); + return -EINVAL; + } + + priv->sbus_ops->lock(priv->sbus_priv); + + /* Write address */ + ret = __cw1200_reg_write_32(priv, ST90TDS_SRAM_BASE_ADDR_REG_ID, addr); + if (ret < 0) { + pr_err("Can't write address register.\n"); + goto out; + } + + /* Write data port */ + ret = __cw1200_reg_write(priv, ST90TDS_SRAM_DPORT_REG_ID, + buf, buf_len, 0); + if (ret < 0) { + pr_err("Can't write data port.\n"); + goto out; + } + +out: + priv->sbus_ops->unlock(priv->sbus_priv); + return ret; +} + +int __cw1200_irq_enable(struct cw1200_common *priv, int enable) +{ + u32 val32; + u16 val16; + int ret; + + if (HIF_8601_SILICON == priv->hw_type) { + ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32); + if (ret < 0) { + pr_err("Can't read config register.\n"); + return ret; + } + + if (enable) + val32 |= ST90TDS_CONF_IRQ_RDY_ENABLE; + else + val32 &= ~ST90TDS_CONF_IRQ_RDY_ENABLE; + + ret = __cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID, val32); + if (ret < 0) { + pr_err("Can't write config register.\n"); + return ret; + } + } else { + ret = __cw1200_reg_read_16(priv, ST90TDS_CONFIG_REG_ID, &val16); + if (ret < 0) { + pr_err("Can't read control register.\n"); + return ret; + } + + if (enable) + val16 |= ST90TDS_CONT_IRQ_RDY_ENABLE; + else + val16 &= ~ST90TDS_CONT_IRQ_RDY_ENABLE; + + ret = __cw1200_reg_write_16(priv, ST90TDS_CONFIG_REG_ID, val16); + if (ret < 0) { + pr_err("Can't write control register.\n"); + return ret; + } + } + return 0; +} diff --git a/drivers/net/wireless/cw1200/hwio.h b/drivers/net/wireless/cw1200/hwio.h new file mode 100644 index 000000000000..7ee73fe32ccf --- /dev/null +++ b/drivers/net/wireless/cw1200/hwio.h @@ -0,0 +1,247 @@ +/* + * Low-level API for mac80211 ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * Based on: + * ST-Ericsson UMAC CW1200 driver which is + * Copyright (c) 2010, ST-Ericsson + * Author: Ajitpal Singh + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef CW1200_HWIO_H_INCLUDED +#define CW1200_HWIO_H_INCLUDED + +/* extern */ struct cw1200_common; + +#define CW1200_CUT_11_ID_STR (0x302E3830) +#define CW1200_CUT_22_ID_STR1 (0x302e3132) +#define CW1200_CUT_22_ID_STR2 (0x32302e30) +#define CW1200_CUT_22_ID_STR3 (0x3335) +#define CW1200_CUT_ID_ADDR (0xFFF17F90) +#define CW1200_CUT2_ID_ADDR (0xFFF1FF90) + +/* Download control area */ +/* boot loader start address in SRAM */ +#define DOWNLOAD_BOOT_LOADER_OFFSET (0x00000000) +/* 32K, 0x4000 to 0xDFFF */ +#define DOWNLOAD_FIFO_OFFSET (0x00004000) +/* 32K */ +#define DOWNLOAD_FIFO_SIZE (0x00008000) +/* 128 bytes, 0xFF80 to 0xFFFF */ +#define DOWNLOAD_CTRL_OFFSET (0x0000FF80) +#define DOWNLOAD_CTRL_DATA_DWORDS (32-6) + +struct download_cntl_t { + /* size of whole firmware file (including Cheksum), host init */ + u32 image_size; + /* downloading flags */ + u32 flags; + /* No. of bytes put into the download, init & updated by host */ + u32 put; + /* last traced program counter, last ARM reg_pc */ + u32 trace_pc; + /* No. of bytes read from the download, host init, device updates */ + u32 get; + /* r0, boot losader status, host init to pending, device updates */ + u32 status; + /* Extra debug info, r1 to r14 if status=r0=DOWNLOAD_EXCEPTION */ + u32 debug_data[DOWNLOAD_CTRL_DATA_DWORDS]; +}; + +#define DOWNLOAD_IMAGE_SIZE_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, image_size)) +#define DOWNLOAD_FLAGS_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, flags)) +#define DOWNLOAD_PUT_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, put)) +#define DOWNLOAD_TRACE_PC_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, trace_pc)) +#define DOWNLOAD_GET_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, get)) +#define DOWNLOAD_STATUS_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, status)) +#define DOWNLOAD_DEBUG_DATA_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, debug_data)) +#define DOWNLOAD_DEBUG_DATA_LEN (108) + +#define DOWNLOAD_BLOCK_SIZE (1024) + +/* For boot loader detection */ +#define DOWNLOAD_ARE_YOU_HERE (0x87654321) +#define DOWNLOAD_I_AM_HERE (0x12345678) + +/* Download error code */ +#define DOWNLOAD_PENDING (0xFFFFFFFF) +#define DOWNLOAD_SUCCESS (0) +#define DOWNLOAD_EXCEPTION (1) +#define DOWNLOAD_ERR_MEM_1 (2) +#define DOWNLOAD_ERR_MEM_2 (3) +#define DOWNLOAD_ERR_SOFTWARE (4) +#define DOWNLOAD_ERR_FILE_SIZE (5) +#define DOWNLOAD_ERR_CHECKSUM (6) +#define DOWNLOAD_ERR_OVERFLOW (7) +#define DOWNLOAD_ERR_IMAGE (8) +#define DOWNLOAD_ERR_HOST (9) +#define DOWNLOAD_ERR_ABORT (10) + + +#define SYS_BASE_ADDR_SILICON (0) +#define PAC_BASE_ADDRESS_SILICON (SYS_BASE_ADDR_SILICON + 0x09000000) +#define PAC_SHARED_MEMORY_SILICON (PAC_BASE_ADDRESS_SILICON) + +#define CW1200_APB(addr) (PAC_SHARED_MEMORY_SILICON + (addr)) + +/* *************************************************************** +*Device register definitions +*************************************************************** */ +/* WBF - SPI Register Addresses */ +#define ST90TDS_ADDR_ID_BASE (0x0000) +/* 16/32 bits */ +#define ST90TDS_CONFIG_REG_ID (0x0000) +/* 16/32 bits */ +#define ST90TDS_CONTROL_REG_ID (0x0001) +/* 16 bits, Q mode W/R */ +#define ST90TDS_IN_OUT_QUEUE_REG_ID (0x0002) +/* 32 bits, AHB bus R/W */ +#define ST90TDS_AHB_DPORT_REG_ID (0x0003) +/* 16/32 bits */ +#define ST90TDS_SRAM_BASE_ADDR_REG_ID (0x0004) +/* 32 bits, APB bus R/W */ +#define ST90TDS_SRAM_DPORT_REG_ID (0x0005) +/* 32 bits, t_settle/general */ +#define ST90TDS_TSET_GEN_R_W_REG_ID (0x0006) +/* 16 bits, Q mode read, no length */ +#define ST90TDS_FRAME_OUT_REG_ID (0x0007) +#define ST90TDS_ADDR_ID_MAX (ST90TDS_FRAME_OUT_REG_ID) + +/* WBF - Control register bit set */ +/* next o/p length, bit 11 to 0 */ +#define ST90TDS_CONT_NEXT_LEN_MASK (0x0FFF) +#define ST90TDS_CONT_WUP_BIT (BIT(12)) +#define ST90TDS_CONT_RDY_BIT (BIT(13)) +#define ST90TDS_CONT_IRQ_ENABLE (BIT(14)) +#define ST90TDS_CONT_RDY_ENABLE (BIT(15)) +#define ST90TDS_CONT_IRQ_RDY_ENABLE (BIT(14)|BIT(15)) + +/* SPI Config register bit set */ +#define ST90TDS_CONFIG_FRAME_BIT (BIT(2)) +#define ST90TDS_CONFIG_WORD_MODE_BITS (BIT(3)|BIT(4)) +#define ST90TDS_CONFIG_WORD_MODE_1 (BIT(3)) +#define ST90TDS_CONFIG_WORD_MODE_2 (BIT(4)) +#define ST90TDS_CONFIG_ERROR_0_BIT (BIT(5)) +#define ST90TDS_CONFIG_ERROR_1_BIT (BIT(6)) +#define ST90TDS_CONFIG_ERROR_2_BIT (BIT(7)) +/* TBD: Sure??? */ +#define ST90TDS_CONFIG_CSN_FRAME_BIT (BIT(7)) +#define ST90TDS_CONFIG_ERROR_3_BIT (BIT(8)) +#define ST90TDS_CONFIG_ERROR_4_BIT (BIT(9)) +/* QueueM */ +#define ST90TDS_CONFIG_ACCESS_MODE_BIT (BIT(10)) +/* AHB bus */ +#define ST90TDS_CONFIG_AHB_PRFETCH_BIT (BIT(11)) +#define ST90TDS_CONFIG_CPU_CLK_DIS_BIT (BIT(12)) +/* APB bus */ +#define ST90TDS_CONFIG_PRFETCH_BIT (BIT(13)) +/* cpu reset */ +#define ST90TDS_CONFIG_CPU_RESET_BIT (BIT(14)) +#define ST90TDS_CONFIG_CLEAR_INT_BIT (BIT(15)) + +/* For CW1200 the IRQ Enable and Ready Bits are in CONFIG register */ +#define ST90TDS_CONF_IRQ_ENABLE (BIT(16)) +#define ST90TDS_CONF_RDY_ENABLE (BIT(17)) +#define ST90TDS_CONF_IRQ_RDY_ENABLE (BIT(16)|BIT(17)) + +int cw1200_data_read(struct cw1200_common *priv, + void *buf, size_t buf_len); +int cw1200_data_write(struct cw1200_common *priv, + const void *buf, size_t buf_len); + +int cw1200_reg_read(struct cw1200_common *priv, u16 addr, + void *buf, size_t buf_len); +int cw1200_reg_write(struct cw1200_common *priv, u16 addr, + const void *buf, size_t buf_len); + +static inline int cw1200_reg_read_16(struct cw1200_common *priv, + u16 addr, u16 *val) +{ + u32 tmp; + int i; + i = cw1200_reg_read(priv, addr, &tmp, sizeof(tmp)); + tmp = le32_to_cpu(tmp); + *val = tmp & 0xffff; + return i; +} + +static inline int cw1200_reg_write_16(struct cw1200_common *priv, + u16 addr, u16 val) +{ + u32 tmp = val; + tmp = cpu_to_le32(tmp); + return cw1200_reg_write(priv, addr, &tmp, sizeof(tmp)); +} + +static inline int cw1200_reg_read_32(struct cw1200_common *priv, + u16 addr, u32 *val) +{ + int i = cw1200_reg_read(priv, addr, val, sizeof(*val)); + *val = le32_to_cpu(*val); + return i; +} + +static inline int cw1200_reg_write_32(struct cw1200_common *priv, + u16 addr, u32 val) +{ + val = cpu_to_le32(val); + return cw1200_reg_write(priv, addr, &val, sizeof(val)); +} + +int cw1200_indirect_read(struct cw1200_common *priv, u32 addr, void *buf, + size_t buf_len, u32 prefetch, u16 port_addr); +int cw1200_apb_write(struct cw1200_common *priv, u32 addr, const void *buf, + size_t buf_len); + +static inline int cw1200_apb_read(struct cw1200_common *priv, u32 addr, + void *buf, size_t buf_len) +{ + return cw1200_indirect_read(priv, addr, buf, buf_len, + ST90TDS_CONFIG_PRFETCH_BIT, + ST90TDS_SRAM_DPORT_REG_ID); +} + +static inline int cw1200_ahb_read(struct cw1200_common *priv, u32 addr, + void *buf, size_t buf_len) +{ + return cw1200_indirect_read(priv, addr, buf, buf_len, + ST90TDS_CONFIG_AHB_PRFETCH_BIT, + ST90TDS_AHB_DPORT_REG_ID); +} + +static inline int cw1200_apb_read_32(struct cw1200_common *priv, + u32 addr, u32 *val) +{ + int i = cw1200_apb_read(priv, addr, val, sizeof(*val)); + *val = le32_to_cpu(*val); + return i; +} + +static inline int cw1200_apb_write_32(struct cw1200_common *priv, + u32 addr, u32 val) +{ + val = cpu_to_le32(val); + return cw1200_apb_write(priv, addr, &val, sizeof(val)); +} +static inline int cw1200_ahb_read_32(struct cw1200_common *priv, + u32 addr, u32 *val) +{ + int i = cw1200_ahb_read(priv, addr, val, sizeof(*val)); + *val = le32_to_cpu(*val); + return i; +} + +#endif /* CW1200_HWIO_H_INCLUDED */ diff --git a/drivers/net/wireless/cw1200/itp.c b/drivers/net/wireless/cw1200/itp.c new file mode 100644 index 000000000000..c0730bb49b75 --- /dev/null +++ b/drivers/net/wireless/cw1200/itp.c @@ -0,0 +1,730 @@ +/* + * mac80211 glue code for mac80211 ST-Ericsson CW1200 drivers + * ITP code + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * 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 +#include +#include +#include +#include +#include +#include +#include "cw1200.h" +#include "debug.h" +#include "itp.h" +#include "sta.h" + +static int __cw1200_itp_open(struct cw1200_common *priv); +static int __cw1200_itp_close(struct cw1200_common *priv); +static void cw1200_itp_rx_start(struct cw1200_common *priv); +static void cw1200_itp_rx_stop(struct cw1200_common *priv); +static void cw1200_itp_rx_stats(struct cw1200_common *priv); +static void cw1200_itp_rx_reset(struct cw1200_common *priv); +static void cw1200_itp_tx_stop(struct cw1200_common *priv); +static void cw1200_itp_handle(struct cw1200_common *priv, + struct sk_buff *skb); +static void cw1200_itp_err(struct cw1200_common *priv, + int err, + int arg); +static void __cw1200_itp_tx_stop(struct cw1200_common *priv); + +static ssize_t cw1200_itp_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + struct cw1200_common *priv = file->private_data; + struct cw1200_itp *itp = &priv->debug->itp; + struct sk_buff *skb; + int ret; + + if (skb_queue_empty(&itp->log_queue)) + return 0; + + skb = skb_dequeue(&itp->log_queue); + ret = copy_to_user(user_buf, skb->data, skb->len); + *ppos += skb->len; + skb->data[skb->len] = 0; + pr_debug("[ITP] >>> %s", skb->data); + consume_skb(skb); + + return skb->len - ret; +} + +static ssize_t cw1200_itp_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct cw1200_common *priv = file->private_data; + struct sk_buff *skb; + + if (!count || count > 1024) + return -EINVAL; + skb = dev_alloc_skb(count + 1); + if (!skb) + return -ENOMEM; + skb_trim(skb, 0); + skb_put(skb, count + 1); + if (copy_from_user(skb->data, user_buf, count)) { + kfree_skb(skb); + return -EFAULT; + } + skb->data[count] = 0; + + cw1200_itp_handle(priv, skb); + consume_skb(skb); + return count; +} + +static unsigned int cw1200_itp_poll(struct file *file, poll_table *wait) +{ + struct cw1200_common *priv = file->private_data; + struct cw1200_itp *itp = &priv->debug->itp; + unsigned int mask = 0; + + poll_wait(file, &itp->read_wait, wait); + + if (!skb_queue_empty(&itp->log_queue)) + mask |= POLLIN | POLLRDNORM; + + mask |= POLLOUT | POLLWRNORM; + + return mask; +} + +static int cw1200_itp_open(struct inode *inode, struct file *file) +{ + struct cw1200_common *priv = inode->i_private; + struct cw1200_itp *itp = &priv->debug->itp; + int ret = 0; + + file->private_data = priv; + if (atomic_inc_return(&itp->open_count) == 1) { + ret = __cw1200_itp_open(priv); + if (ret && !atomic_dec_return(&itp->open_count)) + __cw1200_itp_close(priv); + } else { + atomic_dec(&itp->open_count); + ret = -EBUSY; + } + + return ret; +} + +static int cw1200_itp_close(struct inode *inode, struct file *file) +{ + struct cw1200_common *priv = file->private_data; + struct cw1200_itp *itp = &priv->debug->itp; + if (!atomic_dec_return(&itp->open_count)) { + __cw1200_itp_close(priv); + wake_up(&itp->close_wait); + } + return 0; +} + +static const struct file_operations fops_itp = { + .open = cw1200_itp_open, + .read = cw1200_itp_read, + .write = cw1200_itp_write, + .poll = cw1200_itp_poll, + .release = cw1200_itp_close, + .llseek = default_llseek, + .owner = THIS_MODULE, +}; + +static void cw1200_itp_fill_pattern(u8 *data, int size, + enum cw1200_itp_data_modes mode) +{ + if (size <= 0) + return; + + switch (mode) { + default: + case ITP_DATA_ZEROS: + memset(data, 0x0, size); + break; + case ITP_DATA_ONES: + memset(data, 0xff, size); + break; + case ITP_DATA_ZERONES: + memset(data, 0x55, size); + break; + case ITP_DATA_RANDOM: + get_random_bytes(data, size); + break; + } + return; +} + +static void cw1200_itp_tx_work(struct work_struct *work) +{ + struct cw1200_itp *itp = container_of(work, struct cw1200_itp, + tx_work.work); + struct cw1200_common *priv = itp->priv; + atomic_set(&priv->bh_tx, 1); + wake_up(&priv->bh_wq); +} + +static void cw1200_itp_tx_finish(struct work_struct *work) +{ + struct cw1200_itp *itp = container_of(work, struct cw1200_itp, + tx_finish.work); + __cw1200_itp_tx_stop(itp->priv); +} + +int cw1200_itp_init(struct cw1200_common *priv) +{ + struct cw1200_itp *itp = &priv->debug->itp; + + itp->priv = priv; + atomic_set(&itp->open_count, 0); + atomic_set(&itp->stop_tx, 0); + atomic_set(&itp->awaiting_confirm, 0); + skb_queue_head_init(&itp->log_queue); + spin_lock_init(&itp->tx_lock); + init_waitqueue_head(&itp->read_wait); + init_waitqueue_head(&itp->write_wait); + init_waitqueue_head(&itp->close_wait); + INIT_DELAYED_WORK(&itp->tx_work, cw1200_itp_tx_work); + INIT_DELAYED_WORK(&itp->tx_finish, cw1200_itp_tx_finish); + itp->data = NULL; + itp->hdr_len = WSM_TX_EXTRA_HEADROOM + + sizeof(struct ieee80211_hdr_3addr); + + if (!debugfs_create_file("itp", S_IRUSR | S_IWUSR, + priv->debug->debugfs_phy, priv, &fops_itp)) + return -ENOMEM; + + return 0; +} + +void cw1200_itp_release(struct cw1200_common *priv) +{ + struct cw1200_itp *itp = &priv->debug->itp; + + wait_event_interruptible(itp->close_wait, + !atomic_read(&itp->open_count)); + + WARN_ON(atomic_read(&itp->open_count)); + + skb_queue_purge(&itp->log_queue); + cw1200_itp_tx_stop(priv); +} + +static int __cw1200_itp_open(struct cw1200_common *priv) +{ + struct cw1200_itp *itp = &priv->debug->itp; + + if (!priv->vif) + return -EINVAL; + if (priv->join_status) + return -EINVAL; + itp->saved_channel = priv->channel; + if (!priv->channel) + priv->channel = &priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ]->channels[0]; + wsm_set_bssid_filtering(priv, false); + cw1200_itp_rx_reset(priv); + return 0; +} + +static int __cw1200_itp_close(struct cw1200_common *priv) +{ + struct cw1200_itp *itp = &priv->debug->itp; + if (atomic_read(&itp->test_mode) == TEST_MODE_RX_TEST) + cw1200_itp_rx_stop(priv); + cw1200_itp_tx_stop(priv); + cw1200_disable_listening(priv); + cw1200_update_filtering(priv); + priv->channel = itp->saved_channel; + return 0; +} + +bool cw1200_is_itp(struct cw1200_common *priv) +{ + struct cw1200_itp *itp = &priv->debug->itp; + return atomic_read(&itp->open_count) != 0; +} + +static void cw1200_itp_rx_reset(struct cw1200_common *priv) +{ + struct cw1200_itp *itp = &priv->debug->itp; + itp->rx_cnt = 0; + itp->rx_rssi = 0; + itp->rx_rssi_max = -1000; + itp->rx_rssi_min = 1000; +} + +static void cw1200_itp_rx_start(struct cw1200_common *priv) +{ + struct cw1200_itp *itp = &priv->debug->itp; + + pr_debug("[ITP] RX start, band = %d, ch = %d\n", + itp->band, itp->ch); + atomic_set(&itp->test_mode, TEST_MODE_RX_TEST); + cw1200_update_listening(priv, false); + priv->channel = &priv->hw-> + wiphy->bands[itp->band]->channels[itp->ch]; + cw1200_update_listening(priv, true); + wsm_set_bssid_filtering(priv, false); +} + +static void cw1200_itp_rx_stop(struct cw1200_common *priv) +{ + struct cw1200_itp *itp = &priv->debug->itp; + pr_debug("[ITP] RX stop\n"); + atomic_set(&itp->test_mode, TEST_MODE_NO_TEST); + cw1200_itp_rx_reset(priv); +} + +static void cw1200_itp_rx_stats(struct cw1200_common *priv) +{ + struct cw1200_itp *itp = &priv->debug->itp; + struct sk_buff *skb; + char buf[128]; + int len, ret; + struct wsm_mib_counters_table counters; + + ret = wsm_get_counters_table(priv, &counters); + + if (ret) + cw1200_itp_err(priv, -EBUSY, 20); + + if (!itp->rx_cnt) + len = snprintf(buf, sizeof(buf), "1,0,0,0,0,%d\n", + counters.rx_packet_errors); + else + len = snprintf(buf, sizeof(buf), "1,%d,%ld,%d,%d,%d\n", + itp->rx_cnt, + itp->rx_cnt ? itp->rx_rssi / itp->rx_cnt : 0, + itp->rx_rssi_min, itp->rx_rssi_max, + counters.rx_packet_errors); + + if (len <= 0) { + cw1200_itp_err(priv, -EBUSY, 21); + return; + } + + skb = dev_alloc_skb(len); + if (!skb) { + cw1200_itp_err(priv, -ENOMEM, 22); + return; + } + + itp->rx_cnt = 0; + itp->rx_rssi = 0; + itp->rx_rssi_max = -1000; + itp->rx_rssi_min = 1000; + + skb_trim(skb, 0); + skb_put(skb, len); + + memcpy(skb->data, buf, len); + skb_queue_tail(&itp->log_queue, skb); + wake_up(&itp->read_wait); +} + +static void cw1200_itp_tx_start(struct cw1200_common *priv) +{ + struct wsm_tx *tx; + struct ieee80211_hdr_3addr *hdr; + struct cw1200_itp *itp = &priv->debug->itp; + struct wsm_mib_association_mode assoc_mode = { + .flags = WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE, + .preamble = itp->preamble, + }; + int len; + u8 da_addr[6] = ITP_DEFAULT_DA_ADDR; + + /* Rates index 4 and 5 are not supported */ + if (itp->rate > 3) + itp->rate += 2; + + pr_debug("[ITP] TX start: band = %d, ch = %d, rate = %d, preamble = %d, number = %d, data_mode = %d, interval = %d, power = %d, data_len = %d\n", + itp->band, itp->ch, itp->rate, itp->preamble, + itp->number, itp->data_mode, itp->interval_us, + itp->power, itp->data_len); + + len = itp->hdr_len + itp->data_len; + + itp->data = kmalloc(len, GFP_KERNEL); + tx = (struct wsm_tx *)itp->data; + tx->hdr.len = itp->data_len + itp->hdr_len; + tx->hdr.id = __cpu_to_le16(0x0004 | 1 << 6); + tx->max_tx_rate = itp->rate; + tx->queue_id = 3; + tx->more = 0; + tx->flags = 0xc; + tx->packet_id = 0x55ff55; + tx->reserved = 0; + tx->expire_time = 1; + + if (itp->preamble == ITP_PREAMBLE_GREENFIELD) + tx->ht_tx_parameters = WSM_HT_TX_GREENFIELD; + else if (itp->preamble == ITP_PREAMBLE_MIXED) + tx->ht_tx_parameters = WSM_HT_TX_MIXED; + + hdr = (struct ieee80211_hdr_3addr *)&itp->data[sizeof(struct wsm_tx)]; + memset(hdr, 0, sizeof(*hdr)); + hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_FCTL_TODS); + memcpy(hdr->addr1, da_addr, ETH_ALEN); + memcpy(hdr->addr2, priv->vif->addr, ETH_ALEN); + memcpy(hdr->addr3, da_addr, ETH_ALEN); + + cw1200_itp_fill_pattern(&itp->data[itp->hdr_len], + itp->data_len, itp->data_mode); + + cw1200_update_listening(priv, false); + priv->channel = &priv->hw->wiphy->bands[itp->band]->channels[itp->ch]; + WARN_ON(wsm_set_output_power(priv, itp->power)); + if (itp->preamble == ITP_PREAMBLE_SHORT || + itp->preamble == ITP_PREAMBLE_LONG) + WARN_ON(wsm_set_association_mode(priv, + &assoc_mode)); + wsm_set_bssid_filtering(priv, false); + cw1200_update_listening(priv, true); + + spin_lock_bh(&itp->tx_lock); + atomic_set(&itp->test_mode, TEST_MODE_TX_TEST); + atomic_set(&itp->awaiting_confirm, 0); + atomic_set(&itp->stop_tx, 0); + atomic_set(&priv->bh_tx, 1); + ktime_get_ts(&itp->last_sent); + wake_up(&priv->bh_wq); + spin_unlock_bh(&itp->tx_lock); +} + +void __cw1200_itp_tx_stop(struct cw1200_common *priv) +{ + struct cw1200_itp *itp = &priv->debug->itp; + spin_lock_bh(&itp->tx_lock); + kfree(itp->data); + itp->data = NULL; + atomic_set(&itp->test_mode, TEST_MODE_NO_TEST); + spin_unlock_bh(&itp->tx_lock); +} + +static void cw1200_itp_tx_stop(struct cw1200_common *priv) +{ + struct cw1200_itp *itp = &priv->debug->itp; + pr_debug("[ITP] TX stop\n"); + atomic_set(&itp->stop_tx, 1); + flush_workqueue(priv->workqueue); + + /* time for FW to confirm all tx requests */ + msleep(500); + + __cw1200_itp_tx_stop(priv); +} + +static int cw1200_print_fw_version(struct cw1200_common *priv, + u8 *buf, size_t len) +{ + return snprintf(buf, len, "%s %d.%d", + cw1200_fw_types[priv->wsm_caps.fw_type], + priv->wsm_caps.fw_ver, + priv->wsm_caps.fw_build); +} + +static void cw1200_itp_get_version(struct cw1200_common *priv, + enum cw1200_itp_version_type type) +{ + struct cw1200_itp *itp = &priv->debug->itp; + struct sk_buff *skb; + char buf[ITP_BUF_SIZE]; + size_t size = 0; + int len; + pr_debug("[ITP] print %s version\n", + type == ITP_CHIP_ID ? "chip" : "firmware"); + + len = snprintf(buf, ITP_BUF_SIZE, "2,"); + if (len <= 0) { + cw1200_itp_err(priv, -EINVAL, 40); + return; + } + size += len; + + switch (type) { + case ITP_CHIP_ID: + len = cw1200_print_fw_version(priv, buf+size, + ITP_BUF_SIZE - size); + + if (len <= 0) { + cw1200_itp_err(priv, -EINVAL, 41); + return; + } + size += len; + break; + case ITP_FW_VER: + len = snprintf(buf+size, ITP_BUF_SIZE - size, + "%d.%d", priv->wsm_caps.hw_id, + priv->wsm_caps.hw_subid); + if (len <= 0) { + cw1200_itp_err(priv, -EINVAL, 42); + return; + } + size += len; + break; + default: + cw1200_itp_err(priv, -EINVAL, 43); + break; + } + + len = snprintf(buf+size, ITP_BUF_SIZE-size, "\n"); + if (len <= 0) { + cw1200_itp_err(priv, -EINVAL, 44); + return; + } + size += len; + + skb = dev_alloc_skb(size); + if (!skb) { + cw1200_itp_err(priv, -ENOMEM, 45); + return; + } + + skb_trim(skb, 0); + skb_put(skb, size); + + memcpy(skb->data, buf, size); + skb_queue_tail(&itp->log_queue, skb); + wake_up(&itp->read_wait); +} + +int cw1200_itp_get_tx(struct cw1200_common *priv, u8 **data, + size_t *tx_len, int *burst) +{ + struct cw1200_itp *itp; + struct timespec now; + int time_left_us; + + if (!priv->debug) + return 0; + + itp = &priv->debug->itp; + + if (!itp) + return 0; + + spin_lock_bh(&itp->tx_lock); + if (atomic_read(&itp->test_mode) != TEST_MODE_TX_TEST) + goto out; + + if (atomic_read(&itp->stop_tx)) + goto out; + + if (itp->number == 0) { + atomic_set(&itp->stop_tx, 1); + queue_delayed_work(priv->workqueue, &itp->tx_finish, HZ/10); + goto out; + } + + if (!itp->data) + goto out; + + if (priv->hw_bufs_used >= 2) { + if (!atomic_read(&priv->bh_rx)) + atomic_set(&priv->bh_rx, 1); + atomic_set(&priv->bh_tx, 1); + goto out; + } + + ktime_get_ts(&now); + time_left_us = (itp->last_sent.tv_sec - now.tv_sec)*1000000 + + (itp->last_sent.tv_nsec - now.tv_nsec)/1000 + + itp->interval_us; + + if (time_left_us > ITP_TIME_THRES_US) { + queue_delayed_work(priv->workqueue, &itp->tx_work, + ITP_US_TO_MS(time_left_us)*HZ/1000); + goto out; + } + + if (time_left_us > 50) + udelay(time_left_us); + + if (itp->number > 0) + itp->number--; + + *data = itp->data; + *tx_len = itp->data_len + itp->hdr_len; + + if (itp->data_mode == ITP_DATA_RANDOM) + cw1200_itp_fill_pattern(&itp->data[itp->hdr_len], + itp->data_len, itp->data_mode); + *burst = 2; + atomic_set(&priv->bh_tx, 1); + ktime_get_ts(&itp->last_sent); + atomic_add(1, &itp->awaiting_confirm); + spin_unlock_bh(&itp->tx_lock); + return 1; + +out: + spin_unlock_bh(&itp->tx_lock); + return 0; +} + +bool cw1200_itp_rxed(struct cw1200_common *priv, struct sk_buff *skb) +{ + struct cw1200_itp *itp = &priv->debug->itp; + struct ieee80211_rx_status *rx = IEEE80211_SKB_RXCB(skb); + int signal; + + if (atomic_read(&itp->test_mode) != TEST_MODE_RX_TEST) + return cw1200_is_itp(priv); + if (rx->freq != priv->channel->center_freq) + return true; + + signal = rx->signal; + itp->rx_cnt++; + itp->rx_rssi += signal; + if (itp->rx_rssi_min > rx->signal) + itp->rx_rssi_min = rx->signal; + if (itp->rx_rssi_max < rx->signal) + itp->rx_rssi_max = rx->signal; + + return true; +} + +void cw1200_itp_wake_up_tx(struct cw1200_common *priv) +{ + wake_up(&priv->debug->itp.write_wait); +} + +bool cw1200_itp_tx_running(struct cw1200_common *priv) +{ + if (atomic_read(&priv->debug->itp.awaiting_confirm) || + atomic_read(&priv->debug->itp.test_mode) == + TEST_MODE_TX_TEST) { + atomic_sub(1, &priv->debug->itp.awaiting_confirm); + return true; + } + return false; +} + +static void cw1200_itp_handle(struct cw1200_common *priv, + struct sk_buff *skb) +{ + struct cw1200_itp *itp = &priv->debug->itp; + const struct wiphy *wiphy = priv->hw->wiphy; + int cmd; + int ret; + + pr_debug("[ITP] <<< %s", skb->data); + if (sscanf(skb->data, "%d", &cmd) != 1) { + cw1200_itp_err(priv, -EINVAL, 1); + return; + } + + switch (cmd) { + case 1: /* RX test */ + if (atomic_read(&itp->test_mode)) { + cw1200_itp_err(priv, -EBUSY, 0); + return; + } + ret = sscanf(skb->data, "%d,%d,%d", + &cmd, &itp->band, &itp->ch); + if (ret != 3) { + cw1200_itp_err(priv, -EINVAL, ret + 1); + return; + } + if (itp->band >= 2) { + cw1200_itp_err(priv, -EINVAL, 2); + } else if (!wiphy->bands[itp->band]) { + cw1200_itp_err(priv, -EINVAL, 2); + } else if (itp->ch >= wiphy->bands[itp->band]->n_channels) { + cw1200_itp_err(priv, -EINVAL, 3); + } else { + cw1200_itp_rx_stats(priv); + cw1200_itp_rx_start(priv); + } + break; + case 2: /* RX stat */ + cw1200_itp_rx_stats(priv); + break; + case 3: /* RX/TX stop */ + if (atomic_read(&itp->test_mode) == TEST_MODE_RX_TEST) { + cw1200_itp_rx_stats(priv); + cw1200_itp_rx_stop(priv); + } else if (atomic_read(&itp->test_mode) == TEST_MODE_TX_TEST) { + cw1200_itp_tx_stop(priv); + } else { + cw1200_itp_err(priv, -EBUSY, 0); + } + break; + case 4: /* TX start */ + if (atomic_read(&itp->test_mode) != TEST_MODE_NO_TEST) { + cw1200_itp_err(priv, -EBUSY, 0); + return; + } + ret = sscanf(skb->data, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", + &cmd, &itp->band, &itp->ch, &itp->rate, + &itp->preamble, &itp->number, &itp->data_mode, + &itp->interval_us, &itp->power, &itp->data_len); + if (ret != 10) { + cw1200_itp_err(priv, -EINVAL, ret + 1); + return; + } + if (itp->band >= 2) { + cw1200_itp_err(priv, -EINVAL, 2); + } else if (!wiphy->bands[itp->band]) { + cw1200_itp_err(priv, -EINVAL, 2); + } else if (itp->ch >= wiphy->bands[itp->band]->n_channels) { + cw1200_itp_err(priv, -EINVAL, 3); + } else if (itp->rate >= 20) { + cw1200_itp_err(priv, -EINVAL, 4); + } else if (itp->preamble >= ITP_PREAMBLE_MAX) { + cw1200_itp_err(priv, -EINVAL, 5); + } else if (itp->data_mode >= ITP_DATA_MAX_MODE) { + cw1200_itp_err(priv, -EINVAL, 7); + } else if (itp->data_len < ITP_MIN_DATA_SIZE || + itp->data_len > (priv->wsm_caps.input_buffer_size - itp->hdr_len)) { + cw1200_itp_err(priv, -EINVAL, 8); + } else { + cw1200_itp_tx_start(priv); + } + break; + case 5: + cw1200_itp_get_version(priv, ITP_CHIP_ID); + break; + case 6: + cw1200_itp_get_version(priv, ITP_FW_VER); + break; + } +} + +static void cw1200_itp_err(struct cw1200_common *priv, + int err, int arg) +{ + struct cw1200_itp *itp = &priv->debug->itp; + struct sk_buff *skb; + static char buf[255]; + int len; + + len = snprintf(buf, sizeof(buf), "%d,%d\n", + err, arg); + if (len <= 0) + return; + + skb = dev_alloc_skb(len); + if (!skb) + return; + + skb_trim(skb, 0); + skb_put(skb, len); + + memcpy(skb->data, buf, len); + skb_queue_tail(&itp->log_queue, skb); + wake_up(&itp->read_wait); + + len = sprint_symbol(buf, + (unsigned long)__builtin_return_address(0)); + if (len <= 0) + return; + pr_debug("[ITP] error %d,%d from %s\n", + err, arg, buf); +} diff --git a/drivers/net/wireless/cw1200/itp.h b/drivers/net/wireless/cw1200/itp.h new file mode 100644 index 000000000000..1e9dfb7fcadc --- /dev/null +++ b/drivers/net/wireless/cw1200/itp.h @@ -0,0 +1,144 @@ +/* + * ITP code for ST-Ericsson CW1200 mac80211 driver + * + * Copyright (c) 2011, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef CW1200_ITP_H_INCLUDED +#define CW1200_ITP_H_INCLUDED + +struct cw200_common; +struct wsm_tx_confirm; +struct dentry; + +#ifdef CONFIG_CW1200_ITP + +/*extern*/ struct ieee80211_channel; + +#define TEST_MODE_NO_TEST (0) +#define TEST_MODE_RX_TEST (1) +#define TEST_MODE_TX_TEST (2) +#define ITP_DEFAULT_DA_ADDR {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} +#define ITP_MIN_DATA_SIZE 6 +#define ITP_MAX_DATA_SIZE 1600 +#define ITP_TIME_THRES_US 10000 +#define ITP_US_TO_MS(x) ((x)/1000) +#define ITP_MS_TO_US(x) ((x)*1000) +#define ITP_BUF_SIZE 255 + + +enum cw1200_itp_data_modes { + ITP_DATA_ZEROS, + ITP_DATA_ONES, + ITP_DATA_ZERONES, + ITP_DATA_RANDOM, + ITP_DATA_MAX_MODE, +}; + +enum cw1200_itp_version_type { + ITP_CHIP_ID, + ITP_FW_VER, +}; + +enum cw1200_itp_preamble_type { + ITP_PREAMBLE_LONG, + ITP_PREAMBLE_SHORT, + ITP_PREAMBLE_OFDM, + ITP_PREAMBLE_MIXED, + ITP_PREAMBLE_GREENFIELD, + ITP_PREAMBLE_MAX, +}; + + +struct cw1200_itp { + struct cw1200_common *priv; + atomic_t open_count; + atomic_t awaiting_confirm; + struct sk_buff_head log_queue; + wait_queue_head_t read_wait; + wait_queue_head_t write_wait; + wait_queue_head_t close_wait; + struct ieee80211_channel *saved_channel; + atomic_t stop_tx; + struct delayed_work tx_work; + struct delayed_work tx_finish; + spinlock_t tx_lock; + struct timespec last_sent; + atomic_t test_mode; + int rx_cnt; + long rx_rssi; + int rx_rssi_max; + int rx_rssi_min; + unsigned band; + unsigned ch; + unsigned rate; + unsigned preamble; + unsigned int number; + unsigned data_mode; + int interval_us; + int power; + u8 *data; + int hdr_len; + int data_len; +}; + +int cw1200_itp_init(struct cw1200_common *priv); +void cw1200_itp_release(struct cw1200_common *priv); + +bool cw1200_is_itp(struct cw1200_common *priv); +bool cw1200_itp_rxed(struct cw1200_common *priv, struct sk_buff *skb); +void cw1200_itp_wake_up_tx(struct cw1200_common *priv); +int cw1200_itp_get_tx(struct cw1200_common *priv, u8 **data, + size_t *tx_len, int *burst); +bool cw1200_itp_tx_running(struct cw1200_common *priv); + +#else /* CONFIG_CW1200_ITP */ + +static inline int cw1200_itp_init(struct cw1200_common *priv) +{ + return 0; +} + +static inline void cw1200_itp_release(struct cw1200_common *priv) +{ +} + +static inline bool cw1200_is_itp(struct cw1200_common *priv) +{ + return false; +} + +static inline bool cw1200_itp_rxed(struct cw1200_common *priv, + struct sk_buff *skb) +{ + return false; +} + + +static inline void cw1200_itp_consume_txed(struct cw1200_common *priv) +{ +} + +static inline void cw1200_itp_wake_up_tx(struct cw1200_common *priv) +{ +} + +static inline int cw1200_itp_get_tx(struct cw1200_common *priv, u8 **data, + size_t *tx_len, int *burst) +{ + return 0; +} + +static inline bool cw1200_itp_tx_running(struct cw1200_common *priv) +{ + return false; +} + +#endif /* CONFIG_CW1200_ITP */ + +#endif /* CW1200_ITP_H_INCLUDED */ diff --git a/drivers/net/wireless/cw1200/main.c b/drivers/net/wireless/cw1200/main.c new file mode 100644 index 000000000000..8426d3d7607e --- /dev/null +++ b/drivers/net/wireless/cw1200/main.c @@ -0,0 +1,618 @@ +/* + * mac80211 glue code for mac80211 ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * Based on: + * Copyright (c) 2006, Michael Wu + * Copyright (c) 2007-2009, Christian Lamparter + * Copyright 2008, Johannes Berg + * + * Based on: + * - the islsm (softmac prism54) driver, which is: + * Copyright 2004-2006 Jean-Baptiste Note , et al. + * - stlc45xx driver + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "cw1200.h" +#include "txrx.h" +#include "sbus.h" +#include "fwio.h" +#include "hwio.h" +#include "bh.h" +#include "sta.h" +#include "scan.h" +#include "debug.h" +#include "pm.h" + +MODULE_AUTHOR("Dmitry Tarnyagin "); +MODULE_DESCRIPTION("Softmac ST-Ericsson CW1200 common code"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("cw1200_core"); + +/* Accept MAC address of the form macaddr=0x00,0x80,0xE1,0x30,0x40,0x50 */ +static u8 cw1200_mac_template[ETH_ALEN] = {0x02, 0x80, 0xe1, 0x00, 0x00, 0x00}; +module_param_array_named(macaddr, cw1200_mac_template, byte, NULL, S_IRUGO); +MODULE_PARM_DESC(macaddr, "Override platform_data MAC address"); + +static char *cw1200_sdd_path; +module_param(cw1200_sdd_path, charp, 0644); +MODULE_PARM_DESC(cw1200_sdd_path, "Override platform_data SDD file"); +static int cw1200_refclk; +module_param(cw1200_refclk, int, 0644); +MODULE_PARM_DESC(cw1200_refclk, "Override platform_data reference clock"); + +int cw1200_power_mode = wsm_power_mode_quiescent; +module_param(cw1200_power_mode, int, 0644); +MODULE_PARM_DESC(cw1200_power_mode, "WSM power mode. 0 == active, 1 == doze, 2 == quiescent (default)"); + +#ifdef CONFIG_CW1200_ETF +int etf_mode; +module_param(etf_mode, int, 0644); +MODULE_PARM_DESC(etf_mode, "Enable EngineeringTestingFramework operation"); +#endif + +#define RATETAB_ENT(_rate, _rateid, _flags) \ + { \ + .bitrate = (_rate), \ + .hw_value = (_rateid), \ + .flags = (_flags), \ + } + +static struct ieee80211_rate cw1200_rates[] = { + RATETAB_ENT(10, 0, 0), + RATETAB_ENT(20, 1, 0), + RATETAB_ENT(55, 2, 0), + RATETAB_ENT(110, 3, 0), + RATETAB_ENT(60, 6, 0), + RATETAB_ENT(90, 7, 0), + RATETAB_ENT(120, 8, 0), + RATETAB_ENT(180, 9, 0), + RATETAB_ENT(240, 10, 0), + RATETAB_ENT(360, 11, 0), + RATETAB_ENT(480, 12, 0), + RATETAB_ENT(540, 13, 0), +}; + +static struct ieee80211_rate cw1200_mcs_rates[] = { + RATETAB_ENT(65, 14, IEEE80211_TX_RC_MCS), + RATETAB_ENT(130, 15, IEEE80211_TX_RC_MCS), + RATETAB_ENT(195, 16, IEEE80211_TX_RC_MCS), + RATETAB_ENT(260, 17, IEEE80211_TX_RC_MCS), + RATETAB_ENT(390, 18, IEEE80211_TX_RC_MCS), + RATETAB_ENT(520, 19, IEEE80211_TX_RC_MCS), + RATETAB_ENT(585, 20, IEEE80211_TX_RC_MCS), + RATETAB_ENT(650, 21, IEEE80211_TX_RC_MCS), +}; + +#define cw1200_a_rates (cw1200_rates + 4) +#define cw1200_a_rates_size (ARRAY_SIZE(cw1200_rates) - 4) +#define cw1200_g_rates (cw1200_rates + 0) +#define cw1200_g_rates_size (ARRAY_SIZE(cw1200_rates)) +#define cw1200_n_rates (cw1200_mcs_rates) +#define cw1200_n_rates_size (ARRAY_SIZE(cw1200_mcs_rates)) + + +#define CHAN2G(_channel, _freq, _flags) { \ + .band = IEEE80211_BAND_2GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_channel), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +#define CHAN5G(_channel, _flags) { \ + .band = IEEE80211_BAND_5GHZ, \ + .center_freq = 5000 + (5 * (_channel)), \ + .hw_value = (_channel), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +static struct ieee80211_channel cw1200_2ghz_chantable[] = { + CHAN2G(1, 2412, 0), + CHAN2G(2, 2417, 0), + CHAN2G(3, 2422, 0), + CHAN2G(4, 2427, 0), + CHAN2G(5, 2432, 0), + CHAN2G(6, 2437, 0), + CHAN2G(7, 2442, 0), + CHAN2G(8, 2447, 0), + CHAN2G(9, 2452, 0), + CHAN2G(10, 2457, 0), + CHAN2G(11, 2462, 0), + CHAN2G(12, 2467, 0), + CHAN2G(13, 2472, 0), + CHAN2G(14, 2484, 0), +}; + +static struct ieee80211_channel cw1200_5ghz_chantable[] = { + CHAN5G(34, 0), CHAN5G(36, 0), + CHAN5G(38, 0), CHAN5G(40, 0), + CHAN5G(42, 0), CHAN5G(44, 0), + CHAN5G(46, 0), CHAN5G(48, 0), + CHAN5G(52, 0), CHAN5G(56, 0), + CHAN5G(60, 0), CHAN5G(64, 0), + CHAN5G(100, 0), CHAN5G(104, 0), + CHAN5G(108, 0), CHAN5G(112, 0), + CHAN5G(116, 0), CHAN5G(120, 0), + CHAN5G(124, 0), CHAN5G(128, 0), + CHAN5G(132, 0), CHAN5G(136, 0), + CHAN5G(140, 0), CHAN5G(149, 0), + CHAN5G(153, 0), CHAN5G(157, 0), + CHAN5G(161, 0), CHAN5G(165, 0), + CHAN5G(184, 0), CHAN5G(188, 0), + CHAN5G(192, 0), CHAN5G(196, 0), + CHAN5G(200, 0), CHAN5G(204, 0), + CHAN5G(208, 0), CHAN5G(212, 0), + CHAN5G(216, 0), +}; + +static struct ieee80211_supported_band cw1200_band_2ghz = { + .channels = cw1200_2ghz_chantable, + .n_channels = ARRAY_SIZE(cw1200_2ghz_chantable), + .bitrates = cw1200_g_rates, + .n_bitrates = cw1200_g_rates_size, + .ht_cap = { + .cap = IEEE80211_HT_CAP_GRN_FLD | + (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) | + IEEE80211_HT_CAP_MAX_AMSDU, + .ht_supported = 1, + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE, + .mcs = { + .rx_mask[0] = 0xFF, + .rx_highest = __cpu_to_le16(0x41), + .tx_params = IEEE80211_HT_MCS_TX_DEFINED, + }, + }, +}; + +static struct ieee80211_supported_band cw1200_band_5ghz = { + .channels = cw1200_5ghz_chantable, + .n_channels = ARRAY_SIZE(cw1200_5ghz_chantable), + .bitrates = cw1200_a_rates, + .n_bitrates = cw1200_a_rates_size, + .ht_cap = { + .cap = IEEE80211_HT_CAP_GRN_FLD | + (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) | + IEEE80211_HT_CAP_MAX_AMSDU, + .ht_supported = 1, + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE, + .mcs = { + .rx_mask[0] = 0xFF, + .rx_highest = __cpu_to_le16(0x41), + .tx_params = IEEE80211_HT_MCS_TX_DEFINED, + }, + }, +}; + +static const unsigned long cw1200_ttl[] = { + 1 * HZ, /* VO */ + 2 * HZ, /* VI */ + 5 * HZ, /* BE */ + 10 * HZ /* BK */ +}; + +static const struct ieee80211_ops cw1200_ops = { + .start = cw1200_start, + .stop = cw1200_stop, + .add_interface = cw1200_add_interface, + .remove_interface = cw1200_remove_interface, + .change_interface = cw1200_change_interface, + .tx = cw1200_tx, + .hw_scan = cw1200_hw_scan, + .set_tim = cw1200_set_tim, + .sta_notify = cw1200_sta_notify, + .sta_add = cw1200_sta_add, + .sta_remove = cw1200_sta_remove, + .set_key = cw1200_set_key, + .set_rts_threshold = cw1200_set_rts_threshold, + .config = cw1200_config, + .bss_info_changed = cw1200_bss_info_changed, + .prepare_multicast = cw1200_prepare_multicast, + .configure_filter = cw1200_configure_filter, + .conf_tx = cw1200_conf_tx, + .get_stats = cw1200_get_stats, + .ampdu_action = cw1200_ampdu_action, + .flush = cw1200_flush, + .suspend = cw1200_wow_suspend, + .resume = cw1200_wow_resume, + /* Intentionally not offloaded: */ + /*.channel_switch = cw1200_channel_switch, */ + /*.remain_on_channel = cw1200_remain_on_channel, */ + /*.cancel_remain_on_channel = cw1200_cancel_remain_on_channel, */ +}; + +int cw1200_ba_rx_tids = -1; +int cw1200_ba_tx_tids = -1; +module_param(cw1200_ba_rx_tids, int, 0644); +module_param(cw1200_ba_tx_tids, int, 0644); +MODULE_PARM_DESC(cw1200_ba_rx_tids, "Block ACK RX TIDs"); +MODULE_PARM_DESC(cw1200_ba_tx_tids, "Block ACK TX TIDs"); + +static struct ieee80211_hw *cw1200_init_common(const u8 *macaddr, + const bool have_5ghz) +{ + int i, band; + struct ieee80211_hw *hw; + struct cw1200_common *priv; + + hw = ieee80211_alloc_hw(sizeof(struct cw1200_common), &cw1200_ops); + if (!hw) + return NULL; + + priv = hw->priv; + priv->hw = hw; + priv->hw_type = -1; + priv->mode = NL80211_IFTYPE_UNSPECIFIED; + priv->rates = cw1200_rates; /* TODO: fetch from FW */ + priv->mcs_rates = cw1200_n_rates; + if (cw1200_ba_rx_tids != -1) + priv->ba_rx_tid_mask = cw1200_ba_rx_tids; + else + priv->ba_rx_tid_mask = 0xFF; /* Enable RX BLKACK for all TIDs */ + if (cw1200_ba_tx_tids != -1) + priv->ba_tx_tid_mask = cw1200_ba_tx_tids; + else + priv->ba_tx_tid_mask = 0xff; /* Enable TX BLKACK for all TIDs */ + + hw->flags = IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_SUPPORTS_DYNAMIC_PS | + IEEE80211_HW_REPORTS_TX_ACK_STATUS | + IEEE80211_HW_SUPPORTS_UAPSD | + IEEE80211_HW_CONNECTION_MONITOR | + IEEE80211_HW_AMPDU_AGGREGATION | + IEEE80211_HW_TX_AMPDU_SETUP_IN_HW | + IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC; + + hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_MESH_POINT) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO); + + /* Support only for limited wowlan functionalities */ + hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY | + WIPHY_WOWLAN_DISCONNECT; + hw->wiphy->wowlan.n_patterns = 0; + + hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; + + hw->channel_change_time = 1000; /* TODO: find actual value */ + hw->queues = 4; + + priv->rts_threshold = -1; + + hw->max_rates = 8; + hw->max_rate_tries = 15; + hw->extra_tx_headroom = WSM_TX_EXTRA_HEADROOM + + 8; /* TKIP IV */ + + hw->sta_data_size = sizeof(struct cw1200_sta_priv); + + hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &cw1200_band_2ghz; + if (have_5ghz) + hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &cw1200_band_5ghz; + + /* Channel params have to be cleared before registering wiphy again */ + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + struct ieee80211_supported_band *sband = hw->wiphy->bands[band]; + if (!sband) + continue; + for (i = 0; i < sband->n_channels; i++) { + sband->channels[i].flags = 0; + sband->channels[i].max_antenna_gain = 0; + sband->channels[i].max_power = 30; + } + } + + hw->wiphy->max_scan_ssids = 2; + hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; + + if (macaddr) + SET_IEEE80211_PERM_ADDR(hw, (u8 *)macaddr); + else + SET_IEEE80211_PERM_ADDR(hw, cw1200_mac_template); + + /* Fix up mac address if necessary */ + if (hw->wiphy->perm_addr[3] == 0 && + hw->wiphy->perm_addr[4] == 0 && + hw->wiphy->perm_addr[5] == 0) { + get_random_bytes(&hw->wiphy->perm_addr[3], 3); + } + + mutex_init(&priv->wsm_cmd_mux); + mutex_init(&priv->conf_mutex); + priv->workqueue = create_singlethread_workqueue("cw1200_wq"); + sema_init(&priv->scan.lock, 1); + INIT_WORK(&priv->scan.work, cw1200_scan_work); + INIT_DELAYED_WORK(&priv->scan.probe_work, cw1200_probe_work); + INIT_DELAYED_WORK(&priv->scan.timeout, cw1200_scan_timeout); + INIT_DELAYED_WORK(&priv->clear_recent_scan_work, + cw1200_clear_recent_scan_work); + INIT_DELAYED_WORK(&priv->join_timeout, cw1200_join_timeout); + INIT_WORK(&priv->unjoin_work, cw1200_unjoin_work); + INIT_WORK(&priv->join_complete_work, cw1200_join_complete_work); + INIT_WORK(&priv->wep_key_work, cw1200_wep_key_work); + INIT_WORK(&priv->tx_policy_upload_work, tx_policy_upload_work); + spin_lock_init(&priv->event_queue_lock); + INIT_LIST_HEAD(&priv->event_queue); + INIT_WORK(&priv->event_handler, cw1200_event_handler); + INIT_DELAYED_WORK(&priv->bss_loss_work, cw1200_bss_loss_work); + INIT_WORK(&priv->bss_params_work, cw1200_bss_params_work); + spin_lock_init(&priv->bss_loss_lock); + spin_lock_init(&priv->ps_state_lock); + INIT_WORK(&priv->set_cts_work, cw1200_set_cts_work); + INIT_WORK(&priv->set_tim_work, cw1200_set_tim_work); + INIT_WORK(&priv->multicast_start_work, cw1200_multicast_start_work); + INIT_WORK(&priv->multicast_stop_work, cw1200_multicast_stop_work); + INIT_WORK(&priv->link_id_work, cw1200_link_id_work); + INIT_DELAYED_WORK(&priv->link_id_gc_work, cw1200_link_id_gc_work); + INIT_WORK(&priv->linkid_reset_work, cw1200_link_id_reset); + INIT_WORK(&priv->update_filtering_work, cw1200_update_filtering_work); + INIT_WORK(&priv->set_beacon_wakeup_period_work, + cw1200_set_beacon_wakeup_period_work); + init_timer(&priv->mcast_timeout); + priv->mcast_timeout.data = (unsigned long)priv; + priv->mcast_timeout.function = cw1200_mcast_timeout; + + if (cw1200_queue_stats_init(&priv->tx_queue_stats, + CW1200_LINK_ID_MAX, + cw1200_skb_dtor, + priv)) { + ieee80211_free_hw(hw); + return NULL; + } + + for (i = 0; i < 4; ++i) { + if (cw1200_queue_init(&priv->tx_queue[i], + &priv->tx_queue_stats, i, 16, + cw1200_ttl[i])) { + for (; i > 0; i--) + cw1200_queue_deinit(&priv->tx_queue[i - 1]); + cw1200_queue_stats_deinit(&priv->tx_queue_stats); + ieee80211_free_hw(hw); + return NULL; + } + } + + init_waitqueue_head(&priv->channel_switch_done); + init_waitqueue_head(&priv->wsm_cmd_wq); + init_waitqueue_head(&priv->wsm_startup_done); + init_waitqueue_head(&priv->ps_mode_switch_done); + wsm_buf_init(&priv->wsm_cmd_buf); + spin_lock_init(&priv->wsm_cmd.lock); + priv->wsm_cmd.done = 1; + tx_policy_init(priv); + + return hw; +} + +static int cw1200_register_common(struct ieee80211_hw *dev) +{ + struct cw1200_common *priv = dev->priv; + int err; + +#ifdef CONFIG_CW1200_ETF + if (etf_mode) + goto done; +#endif + + err = cw1200_pm_init(&priv->pm_state, priv); + if (err) { + pr_err("Cannot init PM. (%d).\n", + err); + return err; + } + + err = ieee80211_register_hw(dev); + if (err) { + pr_err("Cannot register device (%d).\n", + err); + cw1200_pm_deinit(&priv->pm_state); + return err; + } + +#ifdef CONFIG_CW1200_ETF +done: +#endif + cw1200_debug_init(priv); + + pr_info("Registered as '%s'\n", wiphy_name(dev->wiphy)); + return 0; +} + +static void cw1200_free_common(struct ieee80211_hw *dev) +{ + ieee80211_free_hw(dev); +} + +static void cw1200_unregister_common(struct ieee80211_hw *dev) +{ + struct cw1200_common *priv = dev->priv; + int i; + +#ifdef CONFIG_CW1200_ETF + if (!etf_mode) { +#endif + ieee80211_unregister_hw(dev); +#ifdef CONFIG_CW1200_ETF + } +#endif + + del_timer_sync(&priv->mcast_timeout); + cw1200_unregister_bh(priv); + + cw1200_debug_release(priv); + + mutex_destroy(&priv->conf_mutex); + + wsm_buf_deinit(&priv->wsm_cmd_buf); + + destroy_workqueue(priv->workqueue); + priv->workqueue = NULL; + + if (priv->sdd) { + release_firmware(priv->sdd); + priv->sdd = NULL; + } + + for (i = 0; i < 4; ++i) + cw1200_queue_deinit(&priv->tx_queue[i]); + + cw1200_queue_stats_deinit(&priv->tx_queue_stats); + cw1200_pm_deinit(&priv->pm_state); +} + +/* Clock is in KHz */ +u32 cw1200_dpll_from_clk(u16 clk_khz) +{ + switch (clk_khz) { + case 0x32C8: /* 13000 KHz */ + return 0x1D89D241; + case 0x3E80: /* 16000 KHz */ + return 0x000001E1; + case 0x41A0: /* 16800 KHz */ + return 0x124931C1; + case 0x4B00: /* 19200 KHz */ + return 0x00000191; + case 0x5DC0: /* 24000 KHz */ + return 0x00000141; + case 0x6590: /* 26000 KHz */ + return 0x0EC4F121; + case 0x8340: /* 33600 KHz */ + return 0x092490E1; + case 0x9600: /* 38400 KHz */ + return 0x100010C1; + case 0x9C40: /* 40000 KHz */ + return 0x000000C1; + case 0xBB80: /* 48000 KHz */ + return 0x000000A1; + case 0xCB20: /* 52000 KHz */ + return 0x07627091; + default: + pr_err("Unknown Refclk freq (0x%04x), using 2600KHz\n", + clk_khz); + return 0x0EC4F121; + } +} + +int cw1200_core_probe(const struct sbus_ops *sbus_ops, + struct sbus_priv *sbus, + struct device *pdev, + struct cw1200_common **core, + int ref_clk, const u8 *macaddr, + const char *sdd_path, bool have_5ghz) +{ + int err = -EINVAL; + struct ieee80211_hw *dev; + struct cw1200_common *priv; + struct wsm_operational_mode mode = { + .power_mode = cw1200_power_mode, + .disable_more_flag_usage = true, + }; + + dev = cw1200_init_common(macaddr, have_5ghz); + if (!dev) + goto err; + + priv = dev->priv; + priv->hw_refclk = ref_clk; + if (cw1200_refclk) + priv->hw_refclk = cw1200_refclk; + + priv->sdd_path = (char *)sdd_path; + if (cw1200_sdd_path) + priv->sdd_path = cw1200_sdd_path; + + priv->sbus_ops = sbus_ops; + priv->sbus_priv = sbus; + priv->pdev = pdev; + SET_IEEE80211_DEV(priv->hw, pdev); + + /* Pass struct cw1200_common back up */ + *core = priv; + + err = cw1200_register_bh(priv); + if (err) + goto err1; + +#ifdef CONFIG_CW1200_ETF + if (etf_mode) + goto skip_fw; +#endif + + err = cw1200_load_firmware(priv); + if (err) + goto err2; + + if (wait_event_interruptible_timeout(priv->wsm_startup_done, + priv->firmware_ready, + 3*HZ) <= 0) { + /* TODO: Need to find how to reset device + in QUEUE mode properly. + */ + pr_err("Timeout waiting on device startup\n"); + err = -ETIMEDOUT; + goto err2; + } + + /* Set low-power mode. */ + wsm_set_operational_mode(priv, &mode); + + /* Enable multi-TX confirmation */ + wsm_use_multi_tx_conf(priv, true); + +#ifdef CONFIG_CW1200_ETF +skip_fw: +#endif + err = cw1200_register_common(dev); + if (err) + goto err2; + + return err; + +err2: + cw1200_unregister_bh(priv); +err1: + cw1200_free_common(dev); +err: + *core = NULL; + return err; +} +EXPORT_SYMBOL_GPL(cw1200_core_probe); + +void cw1200_core_release(struct cw1200_common *self) +{ + /* Disable device interrupts */ + self->sbus_ops->lock(self->sbus_priv); + __cw1200_irq_enable(self, 0); + self->sbus_ops->unlock(self->sbus_priv); + + /* And then clean up */ + cw1200_unregister_common(self->hw); + cw1200_free_common(self->hw); + return; +} +EXPORT_SYMBOL_GPL(cw1200_core_release); diff --git a/drivers/net/wireless/cw1200/pm.c b/drivers/net/wireless/cw1200/pm.c new file mode 100644 index 000000000000..79edfb93b292 --- /dev/null +++ b/drivers/net/wireless/cw1200/pm.c @@ -0,0 +1,367 @@ +/* + * Mac80211 power management API for ST-Ericsson CW1200 drivers + * + * Copyright (c) 2011, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * 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 +#include +#include "cw1200.h" +#include "pm.h" +#include "sta.h" +#include "bh.h" +#include "sbus.h" + +#define CW1200_BEACON_SKIPPING_MULTIPLIER 3 + +struct cw1200_udp_port_filter { + struct wsm_udp_port_filter_hdr hdr; + /* Up to 4 filters are allowed. */ + struct wsm_udp_port_filter filters[WSM_MAX_FILTER_ELEMENTS]; +} __packed; + +struct cw1200_ether_type_filter { + struct wsm_ether_type_filter_hdr hdr; + /* Up to 4 filters are allowed. */ + struct wsm_ether_type_filter filters[WSM_MAX_FILTER_ELEMENTS]; +} __packed; + +static struct cw1200_udp_port_filter cw1200_udp_port_filter_on = { + .hdr.num = 2, + .filters = { + [0] = { + .action = WSM_FILTER_ACTION_FILTER_OUT, + .type = WSM_FILTER_PORT_TYPE_DST, + .port = __cpu_to_le16(67), /* DHCP Bootps */ + }, + [1] = { + .action = WSM_FILTER_ACTION_FILTER_OUT, + .type = WSM_FILTER_PORT_TYPE_DST, + .port = __cpu_to_le16(68), /* DHCP Bootpc */ + }, + } +}; + +static struct wsm_udp_port_filter_hdr cw1200_udp_port_filter_off = { + .num = 0, +}; + +#ifndef ETH_P_WAPI +#define ETH_P_WAPI 0x88B4 +#endif + +static struct cw1200_ether_type_filter cw1200_ether_type_filter_on = { + .hdr.num = 4, + .filters = { + [0] = { + .action = WSM_FILTER_ACTION_FILTER_IN, + .type = __cpu_to_le16(ETH_P_IP), + }, + [1] = { + .action = WSM_FILTER_ACTION_FILTER_IN, + .type = __cpu_to_le16(ETH_P_PAE), + }, + [2] = { + .action = WSM_FILTER_ACTION_FILTER_IN, + .type = __cpu_to_le16(ETH_P_WAPI), + }, + [3] = { + .action = WSM_FILTER_ACTION_FILTER_IN, + .type = __cpu_to_le16(ETH_P_ARP), + }, + }, +}; + +static struct wsm_ether_type_filter_hdr cw1200_ether_type_filter_off = { + .num = 0, +}; + +/* private */ +struct cw1200_suspend_state { + unsigned long bss_loss_tmo; + unsigned long join_tmo; + unsigned long direct_probe; + unsigned long link_id_gc; + bool beacon_skipping; + u8 prev_ps_mode; +}; + +static void cw1200_pm_stay_awake_tmo(unsigned long arg) +{ + /* XXX what's the point of this ? */ +} + +int cw1200_pm_init(struct cw1200_pm_state *pm, + struct cw1200_common *priv) +{ + spin_lock_init(&pm->lock); + + init_timer(&pm->stay_awake); + pm->stay_awake.data = (unsigned long)pm; + pm->stay_awake.function = cw1200_pm_stay_awake_tmo; + + return 0; +} + +void cw1200_pm_deinit(struct cw1200_pm_state *pm) +{ + del_timer_sync(&pm->stay_awake); +} + +void cw1200_pm_stay_awake(struct cw1200_pm_state *pm, + unsigned long tmo) +{ + long cur_tmo; + spin_lock_bh(&pm->lock); + cur_tmo = pm->stay_awake.expires - jiffies; + if (!timer_pending(&pm->stay_awake) || cur_tmo < (long)tmo) + mod_timer(&pm->stay_awake, jiffies + tmo); + spin_unlock_bh(&pm->lock); +} + +static long cw1200_suspend_work(struct delayed_work *work) +{ + int ret = cancel_delayed_work(work); + long tmo; + if (ret > 0) { + /* Timer is pending */ + tmo = work->timer.expires - jiffies; + if (tmo < 0) + tmo = 0; + } else { + tmo = -1; + } + return tmo; +} + +static int cw1200_resume_work(struct cw1200_common *priv, + struct delayed_work *work, + unsigned long tmo) +{ + if ((long)tmo < 0) + return 1; + + return queue_delayed_work(priv->workqueue, work, tmo); +} + +int cw1200_can_suspend(struct cw1200_common *priv) +{ + if (atomic_read(&priv->bh_rx)) { + wiphy_dbg(priv->hw->wiphy, "Suspend interrupted.\n"); + return 0; + } + return 1; +} +EXPORT_SYMBOL_GPL(cw1200_can_suspend); + +int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) +{ + struct cw1200_common *priv = hw->priv; + struct cw1200_pm_state *pm_state = &priv->pm_state; + struct cw1200_suspend_state *state; + int ret; + + spin_lock_bh(&pm_state->lock); + ret = timer_pending(&pm_state->stay_awake); + spin_unlock_bh(&pm_state->lock); + if (ret) + return -EAGAIN; + + /* Do not suspend when datapath is not idle */ + if (priv->tx_queue_stats.num_queued) + return -EBUSY; + + /* Make sure there is no configuration requests in progress. */ + if (!mutex_trylock(&priv->conf_mutex)) + return -EBUSY; + + /* Ensure pending operations are done. + * Note also that wow_suspend must return in ~2.5sec, before + * watchdog is triggered. + */ + if (priv->channel_switch_in_progress) + goto revert1; + + /* Do not suspend when join is pending */ + if (priv->join_pending) + goto revert1; + + /* Do not suspend when scanning */ + if (down_trylock(&priv->scan.lock)) + goto revert1; + + /* Lock TX. */ + wsm_lock_tx_async(priv); + + /* Wait to avoid possible race with bh code. + * But do not wait too long... + */ + if (wait_event_timeout(priv->bh_evt_wq, + !priv->hw_bufs_used, HZ / 10) <= 0) + goto revert2; + + /* Set UDP filter */ + wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_on.hdr); + + /* Set ethernet frame type filter */ + wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_on.hdr); + + /* Allocate state */ + state = kzalloc(sizeof(struct cw1200_suspend_state), GFP_KERNEL); + if (!state) + goto revert3; + + /* Change to legacy PS while going to suspend */ + if (!priv->vif->p2p && + priv->join_status == CW1200_JOIN_STATUS_STA && + priv->powersave_mode.mode != WSM_PSM_PS) { + state->prev_ps_mode = priv->powersave_mode.mode; + priv->powersave_mode.mode = WSM_PSM_PS; + cw1200_set_pm(priv, &priv->powersave_mode); + if (wait_event_interruptible_timeout(priv->ps_mode_switch_done, + !priv->ps_mode_switch_in_progress, 1*HZ) <= 0) { + goto revert3; + } + } + + /* Store delayed work states. */ + state->bss_loss_tmo = + cw1200_suspend_work(&priv->bss_loss_work); + state->join_tmo = + cw1200_suspend_work(&priv->join_timeout); + state->direct_probe = + cw1200_suspend_work(&priv->scan.probe_work); + state->link_id_gc = + cw1200_suspend_work(&priv->link_id_gc_work); + + cancel_delayed_work_sync(&priv->clear_recent_scan_work); + atomic_set(&priv->recent_scan, 0); + + /* Enable beacon skipping */ + if (priv->join_status == CW1200_JOIN_STATUS_STA && + priv->join_dtim_period && + !priv->has_multicast_subscription) { + state->beacon_skipping = true; + wsm_set_beacon_wakeup_period(priv, + priv->join_dtim_period, + CW1200_BEACON_SKIPPING_MULTIPLIER * priv->join_dtim_period); + } + + /* Stop serving thread */ + if (cw1200_bh_suspend(priv)) + goto revert4; + + ret = timer_pending(&priv->mcast_timeout); + if (ret) + goto revert5; + + /* Store suspend state */ + pm_state->suspend_state = state; + + /* Enable IRQ wake */ + ret = priv->sbus_ops->power_mgmt(priv->sbus_priv, true); + if (ret) { + wiphy_err(priv->hw->wiphy, + "PM request failed: %d. WoW is disabled.\n", ret); + cw1200_wow_resume(hw); + return -EBUSY; + } + + /* Force resume if event is coming from the device. */ + if (atomic_read(&priv->bh_rx)) { + cw1200_wow_resume(hw); + return -EAGAIN; + } + + return 0; + +revert5: + WARN_ON(cw1200_bh_resume(priv)); +revert4: + cw1200_resume_work(priv, &priv->bss_loss_work, + state->bss_loss_tmo); + cw1200_resume_work(priv, &priv->join_timeout, + state->join_tmo); + cw1200_resume_work(priv, &priv->scan.probe_work, + state->direct_probe); + cw1200_resume_work(priv, &priv->link_id_gc_work, + state->link_id_gc); + kfree(state); +revert3: + wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_off); + wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_off); +revert2: + wsm_unlock_tx(priv); + up(&priv->scan.lock); +revert1: + mutex_unlock(&priv->conf_mutex); + return -EBUSY; +} + +int cw1200_wow_resume(struct ieee80211_hw *hw) +{ + struct cw1200_common *priv = hw->priv; + struct cw1200_pm_state *pm_state = &priv->pm_state; + struct cw1200_suspend_state *state; + + state = pm_state->suspend_state; + pm_state->suspend_state = NULL; + + /* Disable IRQ wake */ + priv->sbus_ops->power_mgmt(priv->sbus_priv, false); + + /* Scan.lock must be released before BH is resumed other way + * in case when BSS_LOST command arrived the processing of the + * command will be delayed. + */ + up(&priv->scan.lock); + + /* Resume BH thread */ + WARN_ON(cw1200_bh_resume(priv)); + + /* Restores previous PS mode */ + if (!priv->vif->p2p && priv->join_status == CW1200_JOIN_STATUS_STA) { + priv->powersave_mode.mode = state->prev_ps_mode; + cw1200_set_pm(priv, &priv->powersave_mode); + } + + if (state->beacon_skipping) { + wsm_set_beacon_wakeup_period(priv, priv->beacon_int * + priv->join_dtim_period > + MAX_BEACON_SKIP_TIME_MS ? 1 : + priv->join_dtim_period, 0); + state->beacon_skipping = false; + } + + /* Resume delayed work */ + cw1200_resume_work(priv, &priv->bss_loss_work, + state->bss_loss_tmo); + cw1200_resume_work(priv, &priv->join_timeout, + state->join_tmo); + cw1200_resume_work(priv, &priv->scan.probe_work, + state->direct_probe); + cw1200_resume_work(priv, &priv->link_id_gc_work, + state->link_id_gc); + + /* Remove UDP port filter */ + wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_off); + + /* Remove ethernet frame type filter */ + wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_off); + + /* Unlock datapath */ + wsm_unlock_tx(priv); + + /* Unlock configuration mutex */ + mutex_unlock(&priv->conf_mutex); + + /* Free memory */ + kfree(state); + + return 0; +} diff --git a/drivers/net/wireless/cw1200/pm.h b/drivers/net/wireless/cw1200/pm.h new file mode 100644 index 000000000000..516d9671dd1e --- /dev/null +++ b/drivers/net/wireless/cw1200/pm.h @@ -0,0 +1,38 @@ +/* + * Mac80211 power management interface for ST-Ericsson CW1200 mac80211 drivers + * + * Copyright (c) 2011, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef PM_H_INCLUDED +#define PM_H_INCLUDED + +/* ******************************************************************** */ +/* mac80211 API */ + +/* extern */ struct cw1200_common; +/* private */ struct cw1200_suspend_state; + +struct cw1200_pm_state { + struct cw1200_suspend_state *suspend_state; + struct timer_list stay_awake; + struct platform_device *pm_dev; + spinlock_t lock; /* Protect access */ +}; + +int cw1200_pm_init(struct cw1200_pm_state *pm, + struct cw1200_common *priv); +void cw1200_pm_deinit(struct cw1200_pm_state *pm); +void cw1200_pm_stay_awake(struct cw1200_pm_state *pm, + unsigned long tmo); +int cw1200_wow_suspend(struct ieee80211_hw *hw, + struct cfg80211_wowlan *wowlan); +int cw1200_wow_resume(struct ieee80211_hw *hw); +int cw1200_can_suspend(struct cw1200_common *priv); + +#endif diff --git a/drivers/net/wireless/cw1200/queue.c b/drivers/net/wireless/cw1200/queue.c new file mode 100644 index 000000000000..8510454d5db1 --- /dev/null +++ b/drivers/net/wireless/cw1200/queue.c @@ -0,0 +1,583 @@ +/* + * O(1) TX queue with built-in allocator for ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * 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 +#include +#include "queue.h" +#include "cw1200.h" +#include "debug.h" + +/* private */ struct cw1200_queue_item +{ + struct list_head head; + struct sk_buff *skb; + u32 packet_id; + unsigned long queue_timestamp; + unsigned long xmit_timestamp; + struct cw1200_txpriv txpriv; + u8 generation; +}; + +static inline void __cw1200_queue_lock(struct cw1200_queue *queue) +{ + struct cw1200_queue_stats *stats = queue->stats; + if (queue->tx_locked_cnt++ == 0) { + pr_debug("[TX] Queue %d is locked.\n", + queue->queue_id); + ieee80211_stop_queue(stats->priv->hw, queue->queue_id); + } +} + +static inline void __cw1200_queue_unlock(struct cw1200_queue *queue) +{ + struct cw1200_queue_stats *stats = queue->stats; + BUG_ON(!queue->tx_locked_cnt); + if (--queue->tx_locked_cnt == 0) { + pr_debug("[TX] Queue %d is unlocked.\n", + queue->queue_id); + ieee80211_wake_queue(stats->priv->hw, queue->queue_id); + } +} + +static inline void cw1200_queue_parse_id(u32 packet_id, u8 *queue_generation, + u8 *queue_id, u8 *item_generation, + u8 *item_id) +{ + *item_id = (packet_id >> 0) & 0xFF; + *item_generation = (packet_id >> 8) & 0xFF; + *queue_id = (packet_id >> 16) & 0xFF; + *queue_generation = (packet_id >> 24) & 0xFF; +} + +static inline u32 cw1200_queue_mk_packet_id(u8 queue_generation, u8 queue_id, + u8 item_generation, u8 item_id) +{ + return ((u32)item_id << 0) | + ((u32)item_generation << 8) | + ((u32)queue_id << 16) | + ((u32)queue_generation << 24); +} + +static void cw1200_queue_post_gc(struct cw1200_queue_stats *stats, + struct list_head *gc_list) +{ + struct cw1200_queue_item *item, *tmp; + + list_for_each_entry_safe(item, tmp, gc_list, head) { + list_del(&item->head); + stats->skb_dtor(stats->priv, item->skb, &item->txpriv); + kfree(item); + } +} + +static void cw1200_queue_register_post_gc(struct list_head *gc_list, + struct cw1200_queue_item *item) +{ + struct cw1200_queue_item *gc_item; + gc_item = kmalloc(sizeof(struct cw1200_queue_item), + GFP_ATOMIC); + BUG_ON(!gc_item); + memcpy(gc_item, item, sizeof(struct cw1200_queue_item)); + list_add_tail(&gc_item->head, gc_list); +} + +static void __cw1200_queue_gc(struct cw1200_queue *queue, + struct list_head *head, + bool unlock) +{ + struct cw1200_queue_stats *stats = queue->stats; + struct cw1200_queue_item *item = NULL, *tmp; + bool wakeup_stats = false; + + list_for_each_entry_safe(item, tmp, &queue->queue, head) { + if (jiffies - item->queue_timestamp < queue->ttl) + break; + --queue->num_queued; + --queue->link_map_cache[item->txpriv.link_id]; + spin_lock_bh(&stats->lock); + --stats->num_queued; + if (!--stats->link_map_cache[item->txpriv.link_id]) + wakeup_stats = true; + spin_unlock_bh(&stats->lock); + cw1200_debug_tx_ttl(stats->priv); + cw1200_queue_register_post_gc(head, item); + item->skb = NULL; + list_move_tail(&item->head, &queue->free_pool); + } + + if (wakeup_stats) + wake_up(&stats->wait_link_id_empty); + + if (queue->overfull) { + if (queue->num_queued <= (queue->capacity >> 1)) { + queue->overfull = false; + if (unlock) + __cw1200_queue_unlock(queue); + } else if (item) { + unsigned long tmo = item->queue_timestamp + queue->ttl; + mod_timer(&queue->gc, tmo); + cw1200_pm_stay_awake(&stats->priv->pm_state, + tmo - jiffies); + } + } +} + +static void cw1200_queue_gc(unsigned long arg) +{ + LIST_HEAD(list); + struct cw1200_queue *queue = + (struct cw1200_queue *)arg; + + spin_lock_bh(&queue->lock); + __cw1200_queue_gc(queue, &list, true); + spin_unlock_bh(&queue->lock); + cw1200_queue_post_gc(queue->stats, &list); +} + +int cw1200_queue_stats_init(struct cw1200_queue_stats *stats, + size_t map_capacity, + cw1200_queue_skb_dtor_t skb_dtor, + struct cw1200_common *priv) +{ + memset(stats, 0, sizeof(*stats)); + stats->map_capacity = map_capacity; + stats->skb_dtor = skb_dtor; + stats->priv = priv; + spin_lock_init(&stats->lock); + init_waitqueue_head(&stats->wait_link_id_empty); + + stats->link_map_cache = kzalloc(sizeof(int) * map_capacity, + GFP_KERNEL); + if (!stats->link_map_cache) + return -ENOMEM; + + return 0; +} + +int cw1200_queue_init(struct cw1200_queue *queue, + struct cw1200_queue_stats *stats, + u8 queue_id, + size_t capacity, + unsigned long ttl) +{ + size_t i; + + memset(queue, 0, sizeof(*queue)); + queue->stats = stats; + queue->capacity = capacity; + queue->queue_id = queue_id; + queue->ttl = ttl; + INIT_LIST_HEAD(&queue->queue); + INIT_LIST_HEAD(&queue->pending); + INIT_LIST_HEAD(&queue->free_pool); + spin_lock_init(&queue->lock); + init_timer(&queue->gc); + queue->gc.data = (unsigned long)queue; + queue->gc.function = cw1200_queue_gc; + + queue->pool = kzalloc(sizeof(struct cw1200_queue_item) * capacity, + GFP_KERNEL); + if (!queue->pool) + return -ENOMEM; + + queue->link_map_cache = kzalloc(sizeof(int) * stats->map_capacity, + GFP_KERNEL); + if (!queue->link_map_cache) { + kfree(queue->pool); + queue->pool = NULL; + return -ENOMEM; + } + + for (i = 0; i < capacity; ++i) + list_add_tail(&queue->pool[i].head, &queue->free_pool); + + return 0; +} + +int cw1200_queue_clear(struct cw1200_queue *queue) +{ + int i; + LIST_HEAD(gc_list); + struct cw1200_queue_stats *stats = queue->stats; + struct cw1200_queue_item *item, *tmp; + + spin_lock_bh(&queue->lock); + queue->generation++; + list_splice_tail_init(&queue->queue, &queue->pending); + list_for_each_entry_safe(item, tmp, &queue->pending, head) { + WARN_ON(!item->skb); + cw1200_queue_register_post_gc(&gc_list, item); + item->skb = NULL; + list_move_tail(&item->head, &queue->free_pool); + } + queue->num_queued = 0; + queue->num_pending = 0; + + spin_lock_bh(&stats->lock); + for (i = 0; i < stats->map_capacity; ++i) { + stats->num_queued -= queue->link_map_cache[i]; + stats->link_map_cache[i] -= queue->link_map_cache[i]; + queue->link_map_cache[i] = 0; + } + spin_unlock_bh(&stats->lock); + if (queue->overfull) { + queue->overfull = false; + __cw1200_queue_unlock(queue); + } + spin_unlock_bh(&queue->lock); + wake_up(&stats->wait_link_id_empty); + cw1200_queue_post_gc(stats, &gc_list); + return 0; +} + +void cw1200_queue_stats_deinit(struct cw1200_queue_stats *stats) +{ + kfree(stats->link_map_cache); + stats->link_map_cache = NULL; +} + +void cw1200_queue_deinit(struct cw1200_queue *queue) +{ + cw1200_queue_clear(queue); + del_timer_sync(&queue->gc); + INIT_LIST_HEAD(&queue->free_pool); + kfree(queue->pool); + kfree(queue->link_map_cache); + queue->pool = NULL; + queue->link_map_cache = NULL; + queue->capacity = 0; +} + +size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue, + u32 link_id_map) +{ + size_t ret; + int i, bit; + size_t map_capacity = queue->stats->map_capacity; + + if (!link_id_map) + return 0; + + spin_lock_bh(&queue->lock); + if (link_id_map == (u32)-1) { + ret = queue->num_queued - queue->num_pending; + } else { + ret = 0; + for (i = 0, bit = 1; i < map_capacity; ++i, bit <<= 1) { + if (link_id_map & bit) + ret += queue->link_map_cache[i]; + } + } + spin_unlock_bh(&queue->lock); + return ret; +} + +int cw1200_queue_put(struct cw1200_queue *queue, + struct sk_buff *skb, + struct cw1200_txpriv *txpriv) +{ + int ret = 0; + LIST_HEAD(gc_list); + struct cw1200_queue_stats *stats = queue->stats; + + if (txpriv->link_id >= queue->stats->map_capacity) + return -EINVAL; + + spin_lock_bh(&queue->lock); + if (!WARN_ON(list_empty(&queue->free_pool))) { + struct cw1200_queue_item *item = list_first_entry( + &queue->free_pool, struct cw1200_queue_item, head); + BUG_ON(item->skb); + + list_move_tail(&item->head, &queue->queue); + item->skb = skb; + item->txpriv = *txpriv; + item->generation = 0; + item->packet_id = cw1200_queue_mk_packet_id(queue->generation, + queue->queue_id, + item->generation, + item - queue->pool); + item->queue_timestamp = jiffies; + + ++queue->num_queued; + ++queue->link_map_cache[txpriv->link_id]; + + spin_lock_bh(&stats->lock); + ++stats->num_queued; + ++stats->link_map_cache[txpriv->link_id]; + spin_unlock_bh(&stats->lock); + + /* TX may happen in parallel sometimes. + * Leave extra queue slots so we don't overflow. + */ + if (queue->overfull == false && + queue->num_queued >= + (queue->capacity - (num_present_cpus() - 1))) { + queue->overfull = true; + __cw1200_queue_lock(queue); + mod_timer(&queue->gc, jiffies); + } + } else { + ret = -ENOENT; + } + spin_unlock_bh(&queue->lock); + return ret; +} + +int cw1200_queue_get(struct cw1200_queue *queue, + u32 link_id_map, + struct wsm_tx **tx, + struct ieee80211_tx_info **tx_info, + const struct cw1200_txpriv **txpriv) +{ + int ret = -ENOENT; + struct cw1200_queue_item *item; + struct cw1200_queue_stats *stats = queue->stats; + bool wakeup_stats = false; + + spin_lock_bh(&queue->lock); + list_for_each_entry(item, &queue->queue, head) { + if (link_id_map & BIT(item->txpriv.link_id)) { + ret = 0; + break; + } + } + + if (!WARN_ON(ret)) { + *tx = (struct wsm_tx *)item->skb->data; + *tx_info = IEEE80211_SKB_CB(item->skb); + *txpriv = &item->txpriv; + (*tx)->packet_id = __cpu_to_le32(item->packet_id); + list_move_tail(&item->head, &queue->pending); + ++queue->num_pending; + --queue->link_map_cache[item->txpriv.link_id]; + item->xmit_timestamp = jiffies; + + spin_lock_bh(&stats->lock); + --stats->num_queued; + if (!--stats->link_map_cache[item->txpriv.link_id]) + wakeup_stats = true; + spin_unlock_bh(&stats->lock); + } + spin_unlock_bh(&queue->lock); + if (wakeup_stats) + wake_up(&stats->wait_link_id_empty); + return ret; +} + +int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packet_id) +{ + int ret = 0; + u8 queue_generation, queue_id, item_generation, item_id; + struct cw1200_queue_item *item; + struct cw1200_queue_stats *stats = queue->stats; + + cw1200_queue_parse_id(packet_id, &queue_generation, &queue_id, + &item_generation, &item_id); + + item = &queue->pool[item_id]; + + spin_lock_bh(&queue->lock); + BUG_ON(queue_id != queue->queue_id); + if (queue_generation != queue->generation) { + ret = -ENOENT; + } else if (item_id >= (unsigned) queue->capacity) { + WARN_ON(1); + ret = -EINVAL; + } else if (item->generation != item_generation) { + WARN_ON(1); + ret = -ENOENT; + } else { + --queue->num_pending; + ++queue->link_map_cache[item->txpriv.link_id]; + + spin_lock_bh(&stats->lock); + ++stats->num_queued; + ++stats->link_map_cache[item->txpriv.link_id]; + spin_unlock_bh(&stats->lock); + + item->generation = ++item_generation; + item->packet_id = cw1200_queue_mk_packet_id(queue_generation, + queue_id, + item_generation, + item_id); + list_move(&item->head, &queue->queue); + } + spin_unlock_bh(&queue->lock); + return ret; +} + +int cw1200_queue_requeue_all(struct cw1200_queue *queue) +{ + struct cw1200_queue_item *item, *tmp; + struct cw1200_queue_stats *stats = queue->stats; + spin_lock_bh(&queue->lock); + + list_for_each_entry_safe_reverse(item, tmp, &queue->pending, head) { + --queue->num_pending; + ++queue->link_map_cache[item->txpriv.link_id]; + + spin_lock_bh(&stats->lock); + ++stats->num_queued; + ++stats->link_map_cache[item->txpriv.link_id]; + spin_unlock_bh(&stats->lock); + + ++item->generation; + item->packet_id = cw1200_queue_mk_packet_id(queue->generation, + queue->queue_id, + item->generation, + item - queue->pool); + list_move(&item->head, &queue->queue); + } + spin_unlock_bh(&queue->lock); + + return 0; +} + +int cw1200_queue_remove(struct cw1200_queue *queue, u32 packet_id) +{ + int ret = 0; + u8 queue_generation, queue_id, item_generation, item_id; + struct cw1200_queue_item *item; + struct cw1200_queue_stats *stats = queue->stats; + struct sk_buff *gc_skb = NULL; + struct cw1200_txpriv gc_txpriv; + + cw1200_queue_parse_id(packet_id, &queue_generation, &queue_id, + &item_generation, &item_id); + + item = &queue->pool[item_id]; + + spin_lock_bh(&queue->lock); + BUG_ON(queue_id != queue->queue_id); + if (queue_generation != queue->generation) { + ret = -ENOENT; + } else if (item_id >= (unsigned) queue->capacity) { + WARN_ON(1); + ret = -EINVAL; + } else if (item->generation != item_generation) { + WARN_ON(1); + ret = -ENOENT; + } else { + gc_txpriv = item->txpriv; + gc_skb = item->skb; + item->skb = NULL; + --queue->num_pending; + --queue->num_queued; + ++queue->num_sent; + ++item->generation; + /* Do not use list_move_tail here, but list_move: + * try to utilize cache row. + */ + list_move(&item->head, &queue->free_pool); + + if (queue->overfull && + (queue->num_queued <= (queue->capacity >> 1))) { + queue->overfull = false; + __cw1200_queue_unlock(queue); + } + } + spin_unlock_bh(&queue->lock); + + if (gc_skb) + stats->skb_dtor(stats->priv, gc_skb, &gc_txpriv); + + return ret; +} + +int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packet_id, + struct sk_buff **skb, + const struct cw1200_txpriv **txpriv) +{ + int ret = 0; + u8 queue_generation, queue_id, item_generation, item_id; + struct cw1200_queue_item *item; + cw1200_queue_parse_id(packet_id, &queue_generation, &queue_id, + &item_generation, &item_id); + + item = &queue->pool[item_id]; + + spin_lock_bh(&queue->lock); + BUG_ON(queue_id != queue->queue_id); + if (queue_generation != queue->generation) { + ret = -ENOENT; + } else if (item_id >= (unsigned) queue->capacity) { + WARN_ON(1); + ret = -EINVAL; + } else if (item->generation != item_generation) { + WARN_ON(1); + ret = -ENOENT; + } else { + *skb = item->skb; + *txpriv = &item->txpriv; + } + spin_unlock_bh(&queue->lock); + return ret; +} + +void cw1200_queue_lock(struct cw1200_queue *queue) +{ + spin_lock_bh(&queue->lock); + __cw1200_queue_lock(queue); + spin_unlock_bh(&queue->lock); +} + +void cw1200_queue_unlock(struct cw1200_queue *queue) +{ + spin_lock_bh(&queue->lock); + __cw1200_queue_unlock(queue); + spin_unlock_bh(&queue->lock); +} + +bool cw1200_queue_get_xmit_timestamp(struct cw1200_queue *queue, + unsigned long *timestamp, + u32 pending_frame_id) +{ + struct cw1200_queue_item *item; + bool ret; + + spin_lock_bh(&queue->lock); + ret = !list_empty(&queue->pending); + if (ret) { + list_for_each_entry(item, &queue->pending, head) { + if (item->packet_id != pending_frame_id) + if (time_before(item->xmit_timestamp, + *timestamp)) + *timestamp = item->xmit_timestamp; + } + } + spin_unlock_bh(&queue->lock); + return ret; +} + +bool cw1200_queue_stats_is_empty(struct cw1200_queue_stats *stats, + u32 link_id_map) +{ + bool empty = true; + + spin_lock_bh(&stats->lock); + if (link_id_map == (u32)-1) { + empty = stats->num_queued == 0; + } else { + int i; + for (i = 0; i < stats->map_capacity; ++i) { + if (link_id_map & BIT(i)) { + if (stats->link_map_cache[i]) { + empty = false; + break; + } + } + } + } + spin_unlock_bh(&stats->lock); + + return empty; +} diff --git a/drivers/net/wireless/cw1200/queue.h b/drivers/net/wireless/cw1200/queue.h new file mode 100644 index 000000000000..119f9c79c14e --- /dev/null +++ b/drivers/net/wireless/cw1200/queue.h @@ -0,0 +1,116 @@ +/* + * O(1) TX queue with built-in allocator for ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef CW1200_QUEUE_H_INCLUDED +#define CW1200_QUEUE_H_INCLUDED + +/* private */ struct cw1200_queue_item; + +/* extern */ struct sk_buff; +/* extern */ struct wsm_tx; +/* extern */ struct cw1200_common; +/* extern */ struct ieee80211_tx_queue_stats; +/* extern */ struct cw1200_txpriv; + +/* forward */ struct cw1200_queue_stats; + +typedef void (*cw1200_queue_skb_dtor_t)(struct cw1200_common *priv, + struct sk_buff *skb, + const struct cw1200_txpriv *txpriv); + +struct cw1200_queue { + struct cw1200_queue_stats *stats; + size_t capacity; + size_t num_queued; + size_t num_pending; + size_t num_sent; + struct cw1200_queue_item *pool; + struct list_head queue; + struct list_head free_pool; + struct list_head pending; + int tx_locked_cnt; + int *link_map_cache; + bool overfull; + spinlock_t lock; /* Protect queue entry */ + u8 queue_id; + u8 generation; + struct timer_list gc; + unsigned long ttl; +}; + +struct cw1200_queue_stats { + spinlock_t lock; /* Protect stats entry */ + int *link_map_cache; + int num_queued; + size_t map_capacity; + wait_queue_head_t wait_link_id_empty; + cw1200_queue_skb_dtor_t skb_dtor; + struct cw1200_common *priv; +}; + +struct cw1200_txpriv { + u8 link_id; + u8 raw_link_id; + u8 tid; + u8 rate_id; + u8 offset; +}; + +int cw1200_queue_stats_init(struct cw1200_queue_stats *stats, + size_t map_capacity, + cw1200_queue_skb_dtor_t skb_dtor, + struct cw1200_common *priv); +int cw1200_queue_init(struct cw1200_queue *queue, + struct cw1200_queue_stats *stats, + u8 queue_id, + size_t capacity, + unsigned long ttl); +int cw1200_queue_clear(struct cw1200_queue *queue); +void cw1200_queue_stats_deinit(struct cw1200_queue_stats *stats); +void cw1200_queue_deinit(struct cw1200_queue *queue); + +size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue, + u32 link_id_map); +int cw1200_queue_put(struct cw1200_queue *queue, + struct sk_buff *skb, + struct cw1200_txpriv *txpriv); +int cw1200_queue_get(struct cw1200_queue *queue, + u32 link_id_map, + struct wsm_tx **tx, + struct ieee80211_tx_info **tx_info, + const struct cw1200_txpriv **txpriv); +int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packet_id); +int cw1200_queue_requeue_all(struct cw1200_queue *queue); +int cw1200_queue_remove(struct cw1200_queue *queue, + u32 packet_id); +int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packet_id, + struct sk_buff **skb, + const struct cw1200_txpriv **txpriv); +void cw1200_queue_lock(struct cw1200_queue *queue); +void cw1200_queue_unlock(struct cw1200_queue *queue); +bool cw1200_queue_get_xmit_timestamp(struct cw1200_queue *queue, + unsigned long *timestamp, + u32 pending_frame_id); + +bool cw1200_queue_stats_is_empty(struct cw1200_queue_stats *stats, + u32 link_id_map); + +static inline u8 cw1200_queue_get_queue_id(u32 packet_id) +{ + return (packet_id >> 16) & 0xFF; +} + +static inline u8 cw1200_queue_get_generation(u32 packet_id) +{ + return (packet_id >> 8) & 0xFF; +} + +#endif /* CW1200_QUEUE_H_INCLUDED */ diff --git a/drivers/net/wireless/cw1200/sbus.h b/drivers/net/wireless/cw1200/sbus.h new file mode 100644 index 000000000000..603fd25eaa4a --- /dev/null +++ b/drivers/net/wireless/cw1200/sbus.h @@ -0,0 +1,37 @@ +/* + * Common sbus abstraction layer interface for cw1200 wireless driver + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef CW1200_SBUS_H +#define CW1200_SBUS_H + +/* + * sbus priv forward definition. + * Implemented and instantiated in particular modules. + */ +struct sbus_priv; + +void cw1200_irq_handler(struct cw1200_common *priv); + +/* This MUST be wrapped with sbus_ops->lock/unlock! */ +int __cw1200_irq_enable(struct cw1200_common *priv, int enable); + +struct sbus_ops { + int (*sbus_memcpy_fromio)(struct sbus_priv *self, unsigned int addr, + void *dst, int count); + int (*sbus_memcpy_toio)(struct sbus_priv *self, unsigned int addr, + const void *src, int count); + void (*lock)(struct sbus_priv *self); + void (*unlock)(struct sbus_priv *self); + size_t (*align_size)(struct sbus_priv *self, size_t size); + int (*power_mgmt)(struct sbus_priv *self, bool suspend); +}; + +#endif /* CW1200_SBUS_H */ diff --git a/drivers/net/wireless/cw1200/scan.c b/drivers/net/wireless/cw1200/scan.c new file mode 100644 index 000000000000..ee3c19037aac --- /dev/null +++ b/drivers/net/wireless/cw1200/scan.c @@ -0,0 +1,461 @@ +/* + * Scan implementation for ST-Ericsson CW1200 mac80211 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * 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 +#include "cw1200.h" +#include "scan.h" +#include "sta.h" +#include "pm.h" + +static void cw1200_scan_restart_delayed(struct cw1200_common *priv); + +static int cw1200_scan_start(struct cw1200_common *priv, struct wsm_scan *scan) +{ + int ret, i; + int tmo = 2000; + + switch (priv->join_status) { + case CW1200_JOIN_STATUS_PRE_STA: + case CW1200_JOIN_STATUS_JOINING: + return -EBUSY; + default: + break; + } + + wiphy_dbg(priv->hw->wiphy, "[SCAN] hw req, type %d, %d channels, flags: 0x%x.\n", + scan->type, scan->num_channels, scan->flags); + + for (i = 0; i < scan->num_channels; ++i) + tmo += scan->ch[i].max_chan_time + 10; + + cancel_delayed_work_sync(&priv->clear_recent_scan_work); + atomic_set(&priv->scan.in_progress, 1); + atomic_set(&priv->recent_scan, 1); + cw1200_pm_stay_awake(&priv->pm_state, tmo * HZ / 1000); + queue_delayed_work(priv->workqueue, &priv->scan.timeout, + tmo * HZ / 1000); + ret = wsm_scan(priv, scan); + if (ret) { + atomic_set(&priv->scan.in_progress, 0); + cancel_delayed_work_sync(&priv->scan.timeout); + cw1200_scan_restart_delayed(priv); + } + return ret; +} + +int cw1200_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_scan_request *req) +{ + struct cw1200_common *priv = hw->priv; + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST, + }; + int i, ret; + + if (!priv->vif) + return -EINVAL; + + /* Scan when P2P_GO corrupt firmware MiniAP mode */ + if (priv->join_status == CW1200_JOIN_STATUS_AP) + return -EOPNOTSUPP; + + if (req->n_ssids == 1 && !req->ssids[0].ssid_len) + req->n_ssids = 0; + + wiphy_dbg(hw->wiphy, "[SCAN] Scan request for %d SSIDs.\n", + req->n_ssids); + + if (req->n_ssids > WSM_SCAN_MAX_NUM_OF_SSIDS) + return -EINVAL; + + frame.skb = ieee80211_probereq_get(hw, priv->vif, NULL, 0, + req->ie_len); + if (!frame.skb) + return -ENOMEM; + + if (req->ie_len) + memcpy(skb_put(frame.skb, req->ie_len), req->ie, req->ie_len); + + /* will be unlocked in cw1200_scan_work() */ + down(&priv->scan.lock); + mutex_lock(&priv->conf_mutex); + + ret = wsm_set_template_frame(priv, &frame); + if (!ret) { + /* Host want to be the probe responder. */ + ret = wsm_set_probe_responder(priv, true); + } + if (ret) { + mutex_unlock(&priv->conf_mutex); + up(&priv->scan.lock); + dev_kfree_skb(frame.skb); + return ret; + } + + wsm_lock_tx(priv); + + BUG_ON(priv->scan.req); + priv->scan.req = req; + priv->scan.n_ssids = 0; + priv->scan.status = 0; + priv->scan.begin = &req->channels[0]; + priv->scan.curr = priv->scan.begin; + priv->scan.end = &req->channels[req->n_channels]; + priv->scan.output_power = priv->output_power; + + for (i = 0; i < req->n_ssids; ++i) { + struct wsm_ssid *dst = &priv->scan.ssids[priv->scan.n_ssids]; + memcpy(&dst->ssid[0], req->ssids[i].ssid, sizeof(dst->ssid)); + dst->length = req->ssids[i].ssid_len; + ++priv->scan.n_ssids; + } + + mutex_unlock(&priv->conf_mutex); + + if (frame.skb) + dev_kfree_skb(frame.skb); + queue_work(priv->workqueue, &priv->scan.work); + return 0; +} + +void cw1200_scan_work(struct work_struct *work) +{ + struct cw1200_common *priv = container_of(work, struct cw1200_common, + scan.work); + struct ieee80211_channel **it; + struct wsm_scan scan = { + .type = WSM_SCAN_TYPE_FOREGROUND, + .flags = WSM_SCAN_FLAG_SPLIT_METHOD, + }; + bool first_run = (priv->scan.begin == priv->scan.curr && + priv->scan.begin != priv->scan.end); + int i; + + if (first_run) { + /* Firmware gets crazy if scan request is sent + * when STA is joined but not yet associated. + * Force unjoin in this case. + */ + if (cancel_delayed_work_sync(&priv->join_timeout) > 0) + cw1200_join_timeout(&priv->join_timeout.work); + } + + mutex_lock(&priv->conf_mutex); + + if (first_run) { + if (priv->join_status == CW1200_JOIN_STATUS_STA && + !(priv->powersave_mode.mode & WSM_PSM_PS)) { + struct wsm_set_pm pm = priv->powersave_mode; + pm.mode = WSM_PSM_PS; + cw1200_set_pm(priv, &pm); + } else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) { + /* FW bug: driver has to restart p2p-dev mode + * after scan + */ + cw1200_disable_listening(priv); + } + } + + if (!priv->scan.req || (priv->scan.curr == priv->scan.end)) { + if (priv->scan.output_power != priv->output_power) + wsm_set_output_power(priv, priv->output_power * 10); + if (priv->join_status == CW1200_JOIN_STATUS_STA && + !(priv->powersave_mode.mode & WSM_PSM_PS)) + cw1200_set_pm(priv, &priv->powersave_mode); + + if (priv->scan.status < 0) + wiphy_dbg(priv->hw->wiphy, "[SCAN] Scan failed (%d).\n", + priv->scan.status); + else if (priv->scan.req) + wiphy_dbg(priv->hw->wiphy, + "[SCAN] Scan completed.\n"); + else + wiphy_dbg(priv->hw->wiphy, + "[SCAN] Scan canceled.\n"); + + priv->scan.req = NULL; + cw1200_scan_restart_delayed(priv); + wsm_unlock_tx(priv); + mutex_unlock(&priv->conf_mutex); + ieee80211_scan_completed(priv->hw, priv->scan.status ? 1 : 0); + up(&priv->scan.lock); + return; + } else { + struct ieee80211_channel *first = *priv->scan.curr; + for (it = priv->scan.curr + 1, i = 1; + it != priv->scan.end && i < WSM_SCAN_MAX_NUM_OF_CHANNELS; + ++it, ++i) { + if ((*it)->band != first->band) + break; + if (((*it)->flags ^ first->flags) & + IEEE80211_CHAN_PASSIVE_SCAN) + break; + if (!(first->flags & IEEE80211_CHAN_PASSIVE_SCAN) && + (*it)->max_power != first->max_power) + break; + } + scan.band = first->band; + + if (priv->scan.req->no_cck) + scan.max_tx_rate = WSM_TRANSMIT_RATE_6; + else + scan.max_tx_rate = WSM_TRANSMIT_RATE_1; + scan.num_probes = + (first->flags & IEEE80211_CHAN_PASSIVE_SCAN) ? 0 : 2; + scan.num_ssids = priv->scan.n_ssids; + scan.ssids = &priv->scan.ssids[0]; + scan.num_channels = it - priv->scan.curr; + /* TODO: Is it optimal? */ + scan.probe_delay = 100; + /* It is not stated in WSM specification, however + * FW team says that driver may not use FG scan + * when joined. + */ + if (priv->join_status == CW1200_JOIN_STATUS_STA) { + scan.type = WSM_SCAN_TYPE_BACKGROUND; + scan.flags = WSM_SCAN_FLAG_FORCE_BACKGROUND; + } + scan.ch = kzalloc( + sizeof(struct wsm_scan_ch) * (it - priv->scan.curr), + GFP_KERNEL); + if (!scan.ch) { + priv->scan.status = -ENOMEM; + goto fail; + } + for (i = 0; i < scan.num_channels; ++i) { + scan.ch[i].number = priv->scan.curr[i]->hw_value; + if (priv->scan.curr[i]->flags & IEEE80211_CHAN_PASSIVE_SCAN) { + scan.ch[i].min_chan_time = 50; + scan.ch[i].max_chan_time = 100; + } else { + scan.ch[i].min_chan_time = 10; + scan.ch[i].max_chan_time = 25; + } + } + if (!(first->flags & IEEE80211_CHAN_PASSIVE_SCAN) && + priv->scan.output_power != first->max_power) { + priv->scan.output_power = first->max_power; + wsm_set_output_power(priv, + priv->scan.output_power * 10); + } + priv->scan.status = cw1200_scan_start(priv, &scan); + kfree(scan.ch); + if (priv->scan.status) + goto fail; + priv->scan.curr = it; + } + mutex_unlock(&priv->conf_mutex); + return; + +fail: + priv->scan.curr = priv->scan.end; + mutex_unlock(&priv->conf_mutex); + queue_work(priv->workqueue, &priv->scan.work); + return; +} + +static void cw1200_scan_restart_delayed(struct cw1200_common *priv) +{ + /* FW bug: driver has to restart p2p-dev mode after scan. */ + if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) { + cw1200_enable_listening(priv); + cw1200_update_filtering(priv); + } + + if (priv->delayed_unjoin) { + priv->delayed_unjoin = false; + if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) + wsm_unlock_tx(priv); + } else if (priv->delayed_link_loss) { + wiphy_dbg(priv->hw->wiphy, "[CQM] Requeue BSS loss.\n"); + priv->delayed_link_loss = 0; + cw1200_cqm_bssloss_sm(priv, 1, 0, 0); + } +} + +static void cw1200_scan_complete(struct cw1200_common *priv) +{ + queue_delayed_work(priv->workqueue, &priv->clear_recent_scan_work, HZ); + if (priv->scan.direct_probe) { + wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe complete.\n"); + cw1200_scan_restart_delayed(priv); + priv->scan.direct_probe = 0; + up(&priv->scan.lock); + wsm_unlock_tx(priv); + } else { + cw1200_scan_work(&priv->scan.work); + } +} + +void cw1200_scan_failed_cb(struct cw1200_common *priv) +{ + if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) + /* STA is stopped. */ + return; + + if (cancel_delayed_work_sync(&priv->scan.timeout) > 0) { + priv->scan.status = -EIO; + queue_delayed_work(priv->workqueue, &priv->scan.timeout, 0); + } +} + + +void cw1200_scan_complete_cb(struct cw1200_common *priv, + struct wsm_scan_complete *arg) +{ + if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) + /* STA is stopped. */ + return; + + if (cancel_delayed_work_sync(&priv->scan.timeout) > 0) { + priv->scan.status = 1; + queue_delayed_work(priv->workqueue, &priv->scan.timeout, 0); + } +} + +void cw1200_clear_recent_scan_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, + clear_recent_scan_work.work); + atomic_xchg(&priv->recent_scan, 0); +} + +void cw1200_scan_timeout(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, scan.timeout.work); + if (atomic_xchg(&priv->scan.in_progress, 0)) { + if (priv->scan.status > 0) { + priv->scan.status = 0; + } else if (!priv->scan.status) { + wiphy_warn(priv->hw->wiphy, + "Timeout waiting for scan complete notification.\n"); + priv->scan.status = -ETIMEDOUT; + priv->scan.curr = priv->scan.end; + wsm_stop_scan(priv); + } + cw1200_scan_complete(priv); + } +} + +void cw1200_probe_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, scan.probe_work.work); + u8 queue_id = cw1200_queue_get_queue_id(priv->pending_frame_id); + struct cw1200_queue *queue = &priv->tx_queue[queue_id]; + const struct cw1200_txpriv *txpriv; + struct wsm_tx *wsm; + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST, + }; + struct wsm_ssid ssids[1] = {{ + .length = 0, + } }; + struct wsm_scan_ch ch[1] = {{ + .min_chan_time = 0, + .max_chan_time = 10, + } }; + struct wsm_scan scan = { + .type = WSM_SCAN_TYPE_FOREGROUND, + .num_probes = 1, + .probe_delay = 0, + .num_channels = 1, + .ssids = ssids, + .ch = ch, + }; + u8 *ies; + size_t ies_len; + int ret; + + wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe work.\n"); + + mutex_lock(&priv->conf_mutex); + if (down_trylock(&priv->scan.lock)) { + /* Scan is already in progress. Requeue self. */ + schedule(); + queue_delayed_work(priv->workqueue, + &priv->scan.probe_work, HZ / 10); + mutex_unlock(&priv->conf_mutex); + return; + } + + /* Make sure we still have a pending probe req */ + if (cw1200_queue_get_skb(queue, priv->pending_frame_id, + &frame.skb, &txpriv)) { + up(&priv->scan.lock); + mutex_unlock(&priv->conf_mutex); + wsm_unlock_tx(priv); + return; + } + wsm = (struct wsm_tx *)frame.skb->data; + scan.max_tx_rate = wsm->max_tx_rate; + scan.band = (priv->channel->band == IEEE80211_BAND_5GHZ) ? + WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G; + if (priv->join_status == CW1200_JOIN_STATUS_STA || + priv->join_status == CW1200_JOIN_STATUS_IBSS) { + scan.type = WSM_SCAN_TYPE_BACKGROUND; + scan.flags = WSM_SCAN_FLAG_FORCE_BACKGROUND; + } + ch[0].number = priv->channel->hw_value; + + skb_pull(frame.skb, txpriv->offset); + + ies = &frame.skb->data[sizeof(struct ieee80211_hdr_3addr)]; + ies_len = frame.skb->len - sizeof(struct ieee80211_hdr_3addr); + + if (ies_len) { + u8 *ssidie = + (u8 *)cfg80211_find_ie(WLAN_EID_SSID, ies, ies_len); + if (ssidie && ssidie[1] && ssidie[1] <= sizeof(ssids[0].ssid)) { + u8 *nextie = &ssidie[2 + ssidie[1]]; + /* Remove SSID from the IE list. It has to be provided + * as a separate argument in cw1200_scan_start call + */ + + /* Store SSID localy */ + ssids[0].length = ssidie[1]; + memcpy(ssids[0].ssid, &ssidie[2], ssids[0].length); + scan.num_ssids = 1; + + /* Remove SSID from IE list */ + ssidie[1] = 0; + memmove(&ssidie[2], nextie, &ies[ies_len] - nextie); + skb_trim(frame.skb, frame.skb->len - ssids[0].length); + } + } + + /* FW bug: driver has to restart p2p-dev mode after scan */ + if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) + cw1200_disable_listening(priv); + ret = wsm_set_template_frame(priv, &frame); + priv->scan.direct_probe = 1; + if (!ret) { + wsm_flush_tx(priv); + ret = cw1200_scan_start(priv, &scan); + } + mutex_unlock(&priv->conf_mutex); + + skb_push(frame.skb, txpriv->offset); + if (!ret) + IEEE80211_SKB_CB(frame.skb)->flags |= IEEE80211_TX_STAT_ACK; + BUG_ON(cw1200_queue_remove(queue, priv->pending_frame_id)); + + if (ret) { + priv->scan.direct_probe = 0; + up(&priv->scan.lock); + wsm_unlock_tx(priv); + } + + return; +} diff --git a/drivers/net/wireless/cw1200/scan.h b/drivers/net/wireless/cw1200/scan.h new file mode 100644 index 000000000000..5a8296ccfa82 --- /dev/null +++ b/drivers/net/wireless/cw1200/scan.h @@ -0,0 +1,56 @@ +/* + * Scan interface for ST-Ericsson CW1200 mac80211 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef SCAN_H_INCLUDED +#define SCAN_H_INCLUDED + +#include +#include "wsm.h" + +/* external */ struct sk_buff; +/* external */ struct cfg80211_scan_request; +/* external */ struct ieee80211_channel; +/* external */ struct ieee80211_hw; +/* external */ struct work_struct; + +struct cw1200_scan { + struct semaphore lock; + struct work_struct work; + struct delayed_work timeout; + struct cfg80211_scan_request *req; + struct ieee80211_channel **begin; + struct ieee80211_channel **curr; + struct ieee80211_channel **end; + struct wsm_ssid ssids[WSM_SCAN_MAX_NUM_OF_SSIDS]; + int output_power; + int n_ssids; + int status; + atomic_t in_progress; + /* Direct probe requests workaround */ + struct delayed_work probe_work; + int direct_probe; +}; + +int cw1200_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_scan_request *req); +void cw1200_scan_work(struct work_struct *work); +void cw1200_scan_timeout(struct work_struct *work); +void cw1200_clear_recent_scan_work(struct work_struct *work); +void cw1200_scan_complete_cb(struct cw1200_common *priv, + struct wsm_scan_complete *arg); +void cw1200_scan_failed_cb(struct cw1200_common *priv); + +/* ******************************************************************** */ +/* Raw probe requests TX workaround */ +void cw1200_probe_work(struct work_struct *work); + +#endif diff --git a/drivers/net/wireless/cw1200/sta.c b/drivers/net/wireless/cw1200/sta.c new file mode 100644 index 000000000000..679c55f15c67 --- /dev/null +++ b/drivers/net/wireless/cw1200/sta.c @@ -0,0 +1,2406 @@ +/* + * Mac80211 STA API for ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * 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 +#include +#include +#include + +#include "cw1200.h" +#include "sta.h" +#include "fwio.h" +#include "bh.h" +#include "debug.h" + +#ifndef ERP_INFO_BYTE_OFFSET +#define ERP_INFO_BYTE_OFFSET 2 +#endif + +static void cw1200_do_join(struct cw1200_common *priv); +static void cw1200_do_unjoin(struct cw1200_common *priv); + +static int cw1200_upload_beacon(struct cw1200_common *priv); +static int cw1200_upload_pspoll(struct cw1200_common *priv); +static int cw1200_upload_null(struct cw1200_common *priv); +static int cw1200_upload_qosnull(struct cw1200_common *priv); +static int cw1200_start_ap(struct cw1200_common *priv); +static int cw1200_update_beaconing(struct cw1200_common *priv); +static int cw1200_enable_beaconing(struct cw1200_common *priv, + bool enable); +static void __cw1200_sta_notify(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + enum sta_notify_cmd notify_cmd, + int link_id); +static int __cw1200_flush(struct cw1200_common *priv, bool drop); + +static inline void __cw1200_free_event_queue(struct list_head *list) +{ + struct cw1200_wsm_event *event, *tmp; + list_for_each_entry_safe(event, tmp, list, link) { + list_del(&event->link); + kfree(event); + } +} + +/* ******************************************************************** */ +/* STA API */ + +int cw1200_start(struct ieee80211_hw *dev) +{ + struct cw1200_common *priv = dev->priv; + int ret = 0; + + cw1200_pm_stay_awake(&priv->pm_state, HZ); + + mutex_lock(&priv->conf_mutex); + + /* default EDCA */ + WSM_EDCA_SET(&priv->edca, 0, 0x0002, 0x0003, 0x0007, 47, 0xc8, false); + WSM_EDCA_SET(&priv->edca, 1, 0x0002, 0x0007, 0x000f, 94, 0xc8, false); + WSM_EDCA_SET(&priv->edca, 2, 0x0003, 0x000f, 0x03ff, 0, 0xc8, false); + WSM_EDCA_SET(&priv->edca, 3, 0x0007, 0x000f, 0x03ff, 0, 0xc8, false); + ret = wsm_set_edca_params(priv, &priv->edca); + if (ret) + goto out; + + ret = cw1200_set_uapsd_param(priv, &priv->edca); + if (ret) + goto out; + + priv->setbssparams_done = false; + + memcpy(priv->mac_addr, dev->wiphy->perm_addr, ETH_ALEN); + priv->mode = NL80211_IFTYPE_MONITOR; + priv->wep_default_key_id = -1; + + priv->cqm_beacon_loss_count = 10; + + ret = cw1200_setup_mac(priv); + if (ret) + goto out; + +out: + mutex_unlock(&priv->conf_mutex); + return ret; +} + +void cw1200_stop(struct ieee80211_hw *dev) +{ + struct cw1200_common *priv = dev->priv; + LIST_HEAD(list); + int i; + + wsm_lock_tx(priv); + + while (down_trylock(&priv->scan.lock)) { + /* Scan is in progress. Force it to stop. */ + priv->scan.req = NULL; + schedule(); + } + up(&priv->scan.lock); + + cancel_delayed_work_sync(&priv->scan.probe_work); + cancel_delayed_work_sync(&priv->scan.timeout); + cancel_delayed_work_sync(&priv->clear_recent_scan_work); + cancel_delayed_work_sync(&priv->join_timeout); + cw1200_cqm_bssloss_sm(priv, 0, 0, 0); + cancel_work_sync(&priv->unjoin_work); + cancel_delayed_work_sync(&priv->link_id_gc_work); + flush_workqueue(priv->workqueue); + del_timer_sync(&priv->mcast_timeout); + mutex_lock(&priv->conf_mutex); + priv->mode = NL80211_IFTYPE_UNSPECIFIED; + priv->listening = false; + + spin_lock(&priv->event_queue_lock); + list_splice_init(&priv->event_queue, &list); + spin_unlock(&priv->event_queue_lock); + __cw1200_free_event_queue(&list); + + + priv->join_status = CW1200_JOIN_STATUS_PASSIVE; + priv->join_pending = false; + + for (i = 0; i < 4; i++) + cw1200_queue_clear(&priv->tx_queue[i]); + mutex_unlock(&priv->conf_mutex); + tx_policy_clean(priv); + + /* HACK! */ + if (atomic_xchg(&priv->tx_lock, 1) != 1) + pr_debug("[STA] TX is force-unlocked due to stop request.\n"); + + wsm_unlock_tx(priv); + atomic_xchg(&priv->tx_lock, 0); /* for recovery to work */ +} + +static int cw1200_bssloss_mitigation = 1; +module_param(cw1200_bssloss_mitigation, int, 0644); +MODULE_PARM_DESC(cw1200_bssloss_mitigation, "BSS Loss mitigation. 0 == disabled, 1 == enabled (default)"); + + +void __cw1200_cqm_bssloss_sm(struct cw1200_common *priv, + int init, int good, int bad) +{ + int tx = 0; + + priv->delayed_link_loss = 0; + cancel_work_sync(&priv->bss_params_work); + + pr_debug("[STA] CQM BSSLOSS_SM: state: %d init %d good %d bad: %d txlock: %d uj: %d\n", + priv->bss_loss_state, + init, good, bad, + atomic_read(&priv->tx_lock), + priv->delayed_unjoin); + + /* If we have a pending unjoin */ + if (priv->delayed_unjoin) + return; + + if (init) { + queue_delayed_work(priv->workqueue, + &priv->bss_loss_work, + HZ); + priv->bss_loss_state = 0; + + /* Skip the confimration procedure in P2P case */ + if (!priv->vif->p2p && !atomic_read(&priv->tx_lock)) + tx = 1; + } else if (good) { + cancel_delayed_work_sync(&priv->bss_loss_work); + priv->bss_loss_state = 0; + queue_work(priv->workqueue, &priv->bss_params_work); + } else if (bad) { + /* XXX Should we just keep going until we time out? */ + if (priv->bss_loss_state < 3) + tx = 1; + } else { + cancel_delayed_work_sync(&priv->bss_loss_work); + priv->bss_loss_state = 0; + } + + /* Bypass mitigation if it's disabled */ + if (!cw1200_bssloss_mitigation) + tx = 0; + + /* Spit out a NULL packet to our AP if necessary */ + if (tx) { + struct sk_buff *skb; + + priv->bss_loss_state++; + + skb = ieee80211_nullfunc_get(priv->hw, priv->vif); + WARN_ON(!skb); + if (skb) + cw1200_tx(priv->hw, NULL, skb); + } +} + +int cw1200_add_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif) +{ + int ret; + struct cw1200_common *priv = dev->priv; + /* __le32 auto_calibration_mode = __cpu_to_le32(1); */ + + vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER | + IEEE80211_VIF_SUPPORTS_CQM_RSSI; + + mutex_lock(&priv->conf_mutex); + + if (priv->mode != NL80211_IFTYPE_MONITOR) { + mutex_unlock(&priv->conf_mutex); + return -EOPNOTSUPP; + } + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_AP: + priv->mode = vif->type; + break; + default: + mutex_unlock(&priv->conf_mutex); + return -EOPNOTSUPP; + } + + priv->vif = vif; + memcpy(priv->mac_addr, vif->addr, ETH_ALEN); + ret = cw1200_setup_mac(priv); + /* Enable auto-calibration */ + /* Exception in subsequent channel switch; disabled. + * wsm_write_mib(priv, WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE, + * &auto_calibration_mode, sizeof(auto_calibration_mode)); + */ + + mutex_unlock(&priv->conf_mutex); + return ret; +} + +void cw1200_remove_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif) +{ + struct cw1200_common *priv = dev->priv; + struct wsm_reset reset = { + .reset_statistics = true, + }; + int i; + + mutex_lock(&priv->conf_mutex); + switch (priv->join_status) { + case CW1200_JOIN_STATUS_JOINING: + case CW1200_JOIN_STATUS_PRE_STA: + case CW1200_JOIN_STATUS_STA: + case CW1200_JOIN_STATUS_IBSS: + wsm_lock_tx(priv); + if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) + wsm_unlock_tx(priv); + break; + case CW1200_JOIN_STATUS_AP: + for (i = 0; priv->link_id_map; ++i) { + if (priv->link_id_map & BIT(i)) { + reset.link_id = i; + wsm_reset(priv, &reset); + priv->link_id_map &= ~BIT(i); + } + } + memset(priv->link_id_db, 0, sizeof(priv->link_id_db)); + priv->sta_asleep_mask = 0; + priv->enable_beacon = false; + priv->tx_multicast = false; + priv->aid0_bit_set = false; + priv->buffered_multicasts = false; + priv->pspoll_mask = 0; + reset.link_id = 0; + wsm_reset(priv, &reset); + break; + case CW1200_JOIN_STATUS_MONITOR: + cw1200_update_listening(priv, false); + break; + default: + break; + } + priv->vif = NULL; + priv->mode = NL80211_IFTYPE_MONITOR; + memset(priv->mac_addr, 0, ETH_ALEN); + memset(&priv->p2p_ps_modeinfo, 0, sizeof(priv->p2p_ps_modeinfo)); + cw1200_free_keys(priv); + cw1200_setup_mac(priv); + priv->listening = false; + priv->join_status = CW1200_JOIN_STATUS_PASSIVE; + if (!__cw1200_flush(priv, true)) + wsm_unlock_tx(priv); + + mutex_unlock(&priv->conf_mutex); +} + +int cw1200_change_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + enum nl80211_iftype new_type, + bool p2p) +{ + int ret = 0; + pr_debug("change_interface new: %d (%d), old: %d (%d)\n", new_type, + p2p, vif->type, vif->p2p); + + if (new_type != vif->type || vif->p2p != p2p) { + cw1200_remove_interface(dev, vif); + vif->type = new_type; + vif->p2p = p2p; + ret = cw1200_add_interface(dev, vif); + } + + return ret; +} + +int cw1200_config(struct ieee80211_hw *dev, u32 changed) +{ + int ret = 0; + struct cw1200_common *priv = dev->priv; + struct ieee80211_conf *conf = &dev->conf; + + pr_debug("CONFIG CHANGED: %08x\n", changed); + + down(&priv->scan.lock); + mutex_lock(&priv->conf_mutex); + /* TODO: IEEE80211_CONF_CHANGE_QOS */ + /* TODO: IEEE80211_CONF_CHANGE_LISTEN_INTERVAL */ + + if (changed & IEEE80211_CONF_CHANGE_POWER) { + priv->output_power = conf->power_level; + pr_debug("[STA] TX power: %d\n", priv->output_power); + wsm_set_output_power(priv, priv->output_power * 10); + } + + if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) && + (priv->channel != conf->chandef.chan)) { + struct ieee80211_channel *ch = conf->chandef.chan; + struct wsm_switch_channel channel = { + .channel_number = ch->hw_value, + }; + pr_debug("[STA] Freq %d (wsm ch: %d).\n", + ch->center_freq, ch->hw_value); + + /* __cw1200_flush() implicitly locks tx, if successful */ + if (!__cw1200_flush(priv, false)) { + if (!wsm_switch_channel(priv, &channel)) { + ret = wait_event_timeout(priv->channel_switch_done, + !priv->channel_switch_in_progress, + 3 * HZ); + if (ret) { + /* Already unlocks if successful */ + priv->channel = ch; + ret = 0; + } else { + ret = -ETIMEDOUT; + } + } else { + /* Unlock if switch channel fails */ + wsm_unlock_tx(priv); + } + } + } + + if (changed & IEEE80211_CONF_CHANGE_PS) { + if (!(conf->flags & IEEE80211_CONF_PS)) + priv->powersave_mode.mode = WSM_PSM_ACTIVE; + else if (conf->dynamic_ps_timeout <= 0) + priv->powersave_mode.mode = WSM_PSM_PS; + else + priv->powersave_mode.mode = WSM_PSM_FAST_PS; + + /* Firmware requires that value for this 1-byte field must + * be specified in units of 500us. Values above the 128ms + * threshold are not supported. + */ + if (conf->dynamic_ps_timeout >= 0x80) + priv->powersave_mode.fast_psm_idle_period = 0xFF; + else + priv->powersave_mode.fast_psm_idle_period = + conf->dynamic_ps_timeout << 1; + + if (priv->join_status == CW1200_JOIN_STATUS_STA && + priv->bss_params.aid) + cw1200_set_pm(priv, &priv->powersave_mode); + } + + if (changed & IEEE80211_CONF_CHANGE_MONITOR) { + /* TBD: It looks like it's transparent + * there's a monitor interface present -- use this + * to determine for example whether to calculate + * timestamps for packets or not, do not use instead + * of filter flags! + */ + } + + if (changed & IEEE80211_CONF_CHANGE_IDLE) { + struct wsm_operational_mode mode = { + .power_mode = cw1200_power_mode, + .disable_more_flag_usage = true, + }; + + wsm_lock_tx(priv); + /* Disable p2p-dev mode forced by TX request */ + if ((priv->join_status == CW1200_JOIN_STATUS_MONITOR) && + (conf->flags & IEEE80211_CONF_IDLE) && + !priv->listening) { + cw1200_disable_listening(priv); + priv->join_status = CW1200_JOIN_STATUS_PASSIVE; + } + wsm_set_operational_mode(priv, &mode); + wsm_unlock_tx(priv); + } + + if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) { + pr_debug("[STA] Retry limits: %d (long), %d (short).\n", + conf->long_frame_max_tx_count, + conf->short_frame_max_tx_count); + spin_lock_bh(&priv->tx_policy_cache.lock); + priv->long_frame_max_tx_count = conf->long_frame_max_tx_count; + priv->short_frame_max_tx_count = + (conf->short_frame_max_tx_count < 0x0F) ? + conf->short_frame_max_tx_count : 0x0F; + priv->hw->max_rate_tries = priv->short_frame_max_tx_count; + spin_unlock_bh(&priv->tx_policy_cache.lock); + } + mutex_unlock(&priv->conf_mutex); + up(&priv->scan.lock); + return ret; +} + +void cw1200_update_filtering(struct cw1200_common *priv) +{ + int ret; + bool bssid_filtering = !priv->rx_filter.bssid; + bool is_p2p = priv->vif && priv->vif->p2p; + bool is_sta = priv->vif && NL80211_IFTYPE_STATION == priv->vif->type; + + static struct wsm_beacon_filter_control bf_ctrl; + static struct wsm_mib_beacon_filter_table bf_tbl = { + .entry[0].ie_id = WLAN_EID_VENDOR_SPECIFIC, + .entry[0].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED | + WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | + WSM_BEACON_FILTER_IE_HAS_APPEARED, + .entry[0].oui[0] = 0x50, + .entry[0].oui[1] = 0x6F, + .entry[0].oui[2] = 0x9A, + .entry[1].ie_id = WLAN_EID_HT_OPERATION, + .entry[1].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED | + WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | + WSM_BEACON_FILTER_IE_HAS_APPEARED, + .entry[2].ie_id = WLAN_EID_ERP_INFO, + .entry[2].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED | + WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | + WSM_BEACON_FILTER_IE_HAS_APPEARED, + }; + + if (priv->join_status == CW1200_JOIN_STATUS_PASSIVE) + return; + else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) + bssid_filtering = false; + + if (priv->disable_beacon_filter) { + bf_ctrl.enabled = 0; + bf_ctrl.bcn_count = 1; + bf_tbl.num = __cpu_to_le32(0); + } else if (is_p2p || !is_sta) { + bf_ctrl.enabled = WSM_BEACON_FILTER_ENABLE | + WSM_BEACON_FILTER_AUTO_ERP; + bf_ctrl.bcn_count = 0; + bf_tbl.num = __cpu_to_le32(2); + } else { + bf_ctrl.enabled = WSM_BEACON_FILTER_ENABLE; + bf_ctrl.bcn_count = 0; + bf_tbl.num = __cpu_to_le32(3); + } + + /* + * When acting as p2p client being connected to p2p GO, in order to + * receive frames from a different p2p device, turn off bssid filter. + * + * WARNING: FW dependency! + * This can only be used with FW WSM371 and its successors. + * In that FW version even with bssid filter turned off, + * device will block most of the unwanted frames. + */ + if (is_p2p) + bssid_filtering = false; + + ret = wsm_set_rx_filter(priv, &priv->rx_filter); + if (!ret) + ret = wsm_set_beacon_filter_table(priv, &bf_tbl); + if (!ret) + ret = wsm_beacon_filter_control(priv, &bf_ctrl); + if (!ret) + ret = wsm_set_bssid_filtering(priv, bssid_filtering); + if (!ret) + ret = wsm_set_multicast_filter(priv, &priv->multicast_filter); + if (ret) + wiphy_err(priv->hw->wiphy, + "Update filtering failed: %d.\n", ret); + return; +} + +void cw1200_update_filtering_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, + update_filtering_work); + + cw1200_update_filtering(priv); +} + +void cw1200_set_beacon_wakeup_period_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, + set_beacon_wakeup_period_work); + + wsm_set_beacon_wakeup_period(priv, + priv->beacon_int * priv->join_dtim_period > + MAX_BEACON_SKIP_TIME_MS ? 1 : + priv->join_dtim_period, 0); +} + +u64 cw1200_prepare_multicast(struct ieee80211_hw *hw, + struct netdev_hw_addr_list *mc_list) +{ + static u8 broadcast_ipv6[ETH_ALEN] = { + 0x33, 0x33, 0x00, 0x00, 0x00, 0x01 + }; + static u8 broadcast_ipv4[ETH_ALEN] = { + 0x01, 0x00, 0x5e, 0x00, 0x00, 0x01 + }; + struct cw1200_common *priv = hw->priv; + struct netdev_hw_addr *ha; + int count = 0; + + /* Disable multicast filtering */ + priv->has_multicast_subscription = false; + memset(&priv->multicast_filter, 0x00, sizeof(priv->multicast_filter)); + + if (netdev_hw_addr_list_count(mc_list) > WSM_MAX_GRP_ADDRTABLE_ENTRIES) + return 0; + + /* Enable if requested */ + netdev_hw_addr_list_for_each(ha, mc_list) { + pr_debug("[STA] multicast: %pM\n", ha->addr); + memcpy(&priv->multicast_filter.macaddrs[count], + ha->addr, ETH_ALEN); + if (memcmp(ha->addr, broadcast_ipv4, ETH_ALEN) && + memcmp(ha->addr, broadcast_ipv6, ETH_ALEN)) + priv->has_multicast_subscription = true; + count++; + } + + if (count) { + priv->multicast_filter.enable = __cpu_to_le32(1); + priv->multicast_filter.num_addrs = __cpu_to_le32(count); + } + + return netdev_hw_addr_list_count(mc_list); +} + +void cw1200_configure_filter(struct ieee80211_hw *dev, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) +{ + struct cw1200_common *priv = dev->priv; + bool listening = !!(*total_flags & + (FIF_PROMISC_IN_BSS | + FIF_OTHER_BSS | + FIF_BCN_PRBRESP_PROMISC | + FIF_PROBE_REQ)); + + *total_flags &= FIF_PROMISC_IN_BSS | + FIF_OTHER_BSS | + FIF_FCSFAIL | + FIF_BCN_PRBRESP_PROMISC | + FIF_PROBE_REQ; + + down(&priv->scan.lock); + mutex_lock(&priv->conf_mutex); + + priv->rx_filter.promiscuous = (*total_flags & FIF_PROMISC_IN_BSS) + ? 1 : 0; + priv->rx_filter.bssid = (*total_flags & (FIF_OTHER_BSS | + FIF_PROBE_REQ)) ? 1 : 0; + priv->rx_filter.fcs = (*total_flags & FIF_FCSFAIL) ? 1 : 0; + priv->disable_beacon_filter = !(*total_flags & + (FIF_BCN_PRBRESP_PROMISC | + FIF_PROMISC_IN_BSS | + FIF_PROBE_REQ)); + if (priv->listening != listening) { + priv->listening = listening; + wsm_lock_tx(priv); + cw1200_update_listening(priv, listening); + wsm_unlock_tx(priv); + } + cw1200_update_filtering(priv); + mutex_unlock(&priv->conf_mutex); + up(&priv->scan.lock); +} + +int cw1200_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif, + u16 queue, const struct ieee80211_tx_queue_params *params) +{ + struct cw1200_common *priv = dev->priv; + int ret = 0; + /* To prevent re-applying PM request OID again and again*/ + bool old_uapsd_flags; + + mutex_lock(&priv->conf_mutex); + + if (queue < dev->queues) { + old_uapsd_flags = priv->uapsd_info.uapsd_flags; + + WSM_TX_QUEUE_SET(&priv->tx_queue_params, queue, 0, 0, 0); + ret = wsm_set_tx_queue_params(priv, + &priv->tx_queue_params.params[queue], queue); + if (ret) { + ret = -EINVAL; + goto out; + } + + WSM_EDCA_SET(&priv->edca, queue, params->aifs, + params->cw_min, params->cw_max, + params->txop, 0xc8, + params->uapsd); + ret = wsm_set_edca_params(priv, &priv->edca); + if (ret) { + ret = -EINVAL; + goto out; + } + + if (priv->mode == NL80211_IFTYPE_STATION) { + ret = cw1200_set_uapsd_param(priv, &priv->edca); + if (!ret && priv->setbssparams_done && + (priv->join_status == CW1200_JOIN_STATUS_STA) && + (old_uapsd_flags != priv->uapsd_info.uapsd_flags)) + ret = cw1200_set_pm(priv, &priv->powersave_mode); + } + } else { + ret = -EINVAL; + } + +out: + mutex_unlock(&priv->conf_mutex); + return ret; +} + +int cw1200_get_stats(struct ieee80211_hw *dev, + struct ieee80211_low_level_stats *stats) +{ + struct cw1200_common *priv = dev->priv; + + memcpy(stats, &priv->stats, sizeof(*stats)); + return 0; +} + +int cw1200_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg) +{ + struct wsm_set_pm pm = *arg; + + if (priv->uapsd_info.uapsd_flags != 0) + pm.mode &= ~WSM_PSM_FAST_PS_FLAG; + + if (memcmp(&pm, &priv->firmware_ps_mode, + sizeof(struct wsm_set_pm))) { + priv->firmware_ps_mode = pm; + return wsm_set_pm(priv, &pm); + } else { + return 0; + } +} + +int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + int ret = -EOPNOTSUPP; + struct cw1200_common *priv = dev->priv; + struct ieee80211_key_seq seq; + + mutex_lock(&priv->conf_mutex); + + if (cmd == SET_KEY) { + u8 *peer_addr = NULL; + int pairwise = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ? + 1 : 0; + int idx = cw1200_alloc_key(priv); + struct wsm_add_key *wsm_key = &priv->keys[idx]; + + if (idx < 0) { + ret = -EINVAL; + goto finally; + } + + if (sta) + peer_addr = sta->addr; + + key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE; + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + if (key->keylen > 16) { + cw1200_free_key(priv, idx); + ret = -EINVAL; + goto finally; + } + + if (pairwise) { + wsm_key->type = WSM_KEY_TYPE_WEP_PAIRWISE; + memcpy(wsm_key->wep_pairwise.peer, + peer_addr, ETH_ALEN); + memcpy(wsm_key->wep_pairwise.keydata, + &key->key[0], key->keylen); + wsm_key->wep_pairwise.keylen = key->keylen; + } else { + wsm_key->type = WSM_KEY_TYPE_WEP_DEFAULT; + memcpy(wsm_key->wep_group.keydata, + &key->key[0], key->keylen); + wsm_key->wep_group.keylen = key->keylen; + wsm_key->wep_group.keyid = key->keyidx; + } + break; + case WLAN_CIPHER_SUITE_TKIP: + ieee80211_get_key_rx_seq(key, 0, &seq); + if (pairwise) { + wsm_key->type = WSM_KEY_TYPE_TKIP_PAIRWISE; + memcpy(wsm_key->tkip_pairwise.peer, + peer_addr, ETH_ALEN); + memcpy(wsm_key->tkip_pairwise.keydata, + &key->key[0], 16); + memcpy(wsm_key->tkip_pairwise.tx_mic_key, + &key->key[16], 8); + memcpy(wsm_key->tkip_pairwise.rx_mic_key, + &key->key[24], 8); + } else { + size_t mic_offset = + (priv->mode == NL80211_IFTYPE_AP) ? + 16 : 24; + wsm_key->type = WSM_KEY_TYPE_TKIP_GROUP; + memcpy(wsm_key->tkip_group.keydata, + &key->key[0], 16); + memcpy(wsm_key->tkip_group.rx_mic_key, + &key->key[mic_offset], 8); + + wsm_key->tkip_group.rx_seqnum[0] = seq.tkip.iv16 & 0xff; + wsm_key->tkip_group.rx_seqnum[1] = (seq.tkip.iv16 >> 8) & 0xff; + wsm_key->tkip_group.rx_seqnum[2] = seq.tkip.iv32 & 0xff; + wsm_key->tkip_group.rx_seqnum[3] = (seq.tkip.iv32 >> 8) & 0xff; + wsm_key->tkip_group.rx_seqnum[4] = (seq.tkip.iv32 >> 16) & 0xff; + wsm_key->tkip_group.rx_seqnum[5] = (seq.tkip.iv32 >> 24) & 0xff; + wsm_key->tkip_group.rx_seqnum[6] = 0; + wsm_key->tkip_group.rx_seqnum[7] = 0; + + wsm_key->tkip_group.keyid = key->keyidx; + } + break; + case WLAN_CIPHER_SUITE_CCMP: + ieee80211_get_key_rx_seq(key, 0, &seq); + if (pairwise) { + wsm_key->type = WSM_KEY_TYPE_AES_PAIRWISE; + memcpy(wsm_key->aes_pairwise.peer, + peer_addr, ETH_ALEN); + memcpy(wsm_key->aes_pairwise.keydata, + &key->key[0], 16); + } else { + wsm_key->type = WSM_KEY_TYPE_AES_GROUP; + memcpy(wsm_key->aes_group.keydata, + &key->key[0], 16); + + wsm_key->aes_group.rx_seqnum[0] = seq.ccmp.pn[5]; + wsm_key->aes_group.rx_seqnum[1] = seq.ccmp.pn[4]; + wsm_key->aes_group.rx_seqnum[2] = seq.ccmp.pn[3]; + wsm_key->aes_group.rx_seqnum[3] = seq.ccmp.pn[2]; + wsm_key->aes_group.rx_seqnum[4] = seq.ccmp.pn[1]; + wsm_key->aes_group.rx_seqnum[5] = seq.ccmp.pn[0]; + wsm_key->aes_group.rx_seqnum[6] = 0; + wsm_key->aes_group.rx_seqnum[7] = 0; + wsm_key->aes_group.keyid = key->keyidx; + } + break; + case WLAN_CIPHER_SUITE_SMS4: + if (pairwise) { + wsm_key->type = WSM_KEY_TYPE_WAPI_PAIRWISE; + memcpy(wsm_key->wapi_pairwise.peer, + peer_addr, ETH_ALEN); + memcpy(wsm_key->wapi_pairwise.keydata, + &key->key[0], 16); + memcpy(wsm_key->wapi_pairwise.mic_key, + &key->key[16], 16); + wsm_key->wapi_pairwise.keyid = key->keyidx; + } else { + wsm_key->type = WSM_KEY_TYPE_WAPI_GROUP; + memcpy(wsm_key->wapi_group.keydata, + &key->key[0], 16); + memcpy(wsm_key->wapi_group.mic_key, + &key->key[16], 16); + wsm_key->wapi_group.keyid = key->keyidx; + } + break; + default: + pr_warn("Unhandled key type %d\n", key->cipher); + cw1200_free_key(priv, idx); + ret = -EOPNOTSUPP; + goto finally; + } + ret = wsm_add_key(priv, wsm_key); + if (!ret) + key->hw_key_idx = idx; + else + cw1200_free_key(priv, idx); + } else if (cmd == DISABLE_KEY) { + struct wsm_remove_key wsm_key = { + .index = key->hw_key_idx, + }; + + if (wsm_key.index > WSM_KEY_MAX_INDEX) { + ret = -EINVAL; + goto finally; + } + + cw1200_free_key(priv, wsm_key.index); + ret = wsm_remove_key(priv, &wsm_key); + } else { + pr_warn("Unhandled key command %d\n", cmd); + } + +finally: + mutex_unlock(&priv->conf_mutex); + return ret; +} + +void cw1200_wep_key_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, wep_key_work); + u8 queue_id = cw1200_queue_get_queue_id(priv->pending_frame_id); + struct cw1200_queue *queue = &priv->tx_queue[queue_id]; + __le32 wep_default_key_id = __cpu_to_le32( + priv->wep_default_key_id); + + pr_debug("[STA] Setting default WEP key: %d\n", + priv->wep_default_key_id); + wsm_flush_tx(priv); + wsm_write_mib(priv, WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID, + &wep_default_key_id, sizeof(wep_default_key_id)); + cw1200_queue_requeue(queue, priv->pending_frame_id); + wsm_unlock_tx(priv); +} + +int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value) +{ + int ret = 0; + __le32 val32; + struct cw1200_common *priv = hw->priv; + + if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) + return 0; + + if (value != (u32) -1) + val32 = __cpu_to_le32(value); + else + val32 = 0; /* disabled */ + + if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) { + /* device is down, can _not_ set threshold */ + ret = -ENODEV; + goto out; + } + + if (priv->rts_threshold == value) + goto out; + + pr_debug("[STA] Setting RTS threshold: %d\n", + priv->rts_threshold); + + /* mutex_lock(&priv->conf_mutex); */ + ret = wsm_write_mib(priv, WSM_MIB_ID_DOT11_RTS_THRESHOLD, + &val32, sizeof(val32)); + if (!ret) + priv->rts_threshold = value; + /* mutex_unlock(&priv->conf_mutex); */ + +out: + return ret; +} + +/* If successful, LOCKS the TX queue! */ +static int __cw1200_flush(struct cw1200_common *priv, bool drop) +{ + int i, ret; + + for (;;) { + /* TODO: correct flush handling is required when dev_stop. + * Temporary workaround: 2s + */ + if (drop) { + for (i = 0; i < 4; ++i) + cw1200_queue_clear(&priv->tx_queue[i]); + } else { + ret = wait_event_timeout( + priv->tx_queue_stats.wait_link_id_empty, + cw1200_queue_stats_is_empty( + &priv->tx_queue_stats, -1), + 2 * HZ); + } + + if (!drop && ret <= 0) { + ret = -ETIMEDOUT; + break; + } else { + ret = 0; + } + + wsm_lock_tx(priv); + if (!cw1200_queue_stats_is_empty(&priv->tx_queue_stats, -1)) { + /* Highly unlikely: WSM requeued frames. */ + wsm_unlock_tx(priv); + continue; + } + break; + } + return ret; +} + +void cw1200_flush(struct ieee80211_hw *hw, u32 queues, bool drop) +{ + struct cw1200_common *priv = hw->priv; + + switch (priv->mode) { + case NL80211_IFTYPE_MONITOR: + drop = true; + break; + case NL80211_IFTYPE_AP: + if (!priv->enable_beacon) + drop = true; + break; + } + + if (!__cw1200_flush(priv, drop)) + wsm_unlock_tx(priv); + + return; +} + +/* ******************************************************************** */ +/* WSM callbacks */ + +void cw1200_free_event_queue(struct cw1200_common *priv) +{ + LIST_HEAD(list); + + spin_lock(&priv->event_queue_lock); + list_splice_init(&priv->event_queue, &list); + spin_unlock(&priv->event_queue_lock); + + __cw1200_free_event_queue(&list); +} + +void cw1200_event_handler(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, event_handler); + struct cw1200_wsm_event *event; + LIST_HEAD(list); + + spin_lock(&priv->event_queue_lock); + list_splice_init(&priv->event_queue, &list); + spin_unlock(&priv->event_queue_lock); + + list_for_each_entry(event, &list, link) { + switch (event->evt.id) { + case WSM_EVENT_ERROR: + pr_err("Unhandled WSM Error from LMAC\n"); + break; + case WSM_EVENT_BSS_LOST: + pr_debug("[CQM] BSS lost.\n"); + cancel_work_sync(&priv->unjoin_work); + if (!down_trylock(&priv->scan.lock)) { + cw1200_cqm_bssloss_sm(priv, 1, 0, 0); + up(&priv->scan.lock); + } else { + /* Scan is in progress. Delay reporting. + * Scan complete will trigger bss_loss_work + */ + priv->delayed_link_loss = 1; + /* Also start a watchdog. */ + queue_delayed_work(priv->workqueue, + &priv->bss_loss_work, 5*HZ); + } + break; + case WSM_EVENT_BSS_REGAINED: + pr_debug("[CQM] BSS regained.\n"); + cw1200_cqm_bssloss_sm(priv, 0, 0, 0); + cancel_work_sync(&priv->unjoin_work); + break; + case WSM_EVENT_RADAR_DETECTED: + wiphy_info(priv->hw->wiphy, "radar pulse detected\n"); + break; + case WSM_EVENT_RCPI_RSSI: + { + /* RSSI: signed Q8.0, RCPI: unsigned Q7.1 + * RSSI = RCPI / 2 - 110 + */ + int rcpiRssi = (int)(event->evt.data & 0xFF); + int cqm_evt; + if (priv->cqm_use_rssi) + rcpiRssi = (s8)rcpiRssi; + else + rcpiRssi = rcpiRssi / 2 - 110; + + cqm_evt = (rcpiRssi <= priv->cqm_rssi_thold) ? + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW : + NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH; + pr_debug("[CQM] RSSI event: %d.\n", rcpiRssi); + ieee80211_cqm_rssi_notify(priv->vif, cqm_evt, + GFP_KERNEL); + break; + } + case WSM_EVENT_BT_INACTIVE: + pr_warn("Unhandled BT INACTIVE from LMAC\n"); + break; + case WSM_EVENT_BT_ACTIVE: + pr_warn("Unhandled BT ACTIVE from LMAC\n"); + break; + } + } + __cw1200_free_event_queue(&list); +} + +void cw1200_bss_loss_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, bss_loss_work.work); + + pr_debug("[CQM] Reporting connection loss.\n"); + wsm_lock_tx(priv); + if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) + wsm_unlock_tx(priv); +} + +void cw1200_bss_params_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, bss_params_work); + mutex_lock(&priv->conf_mutex); + + priv->bss_params.reset_beacon_loss = 1; + wsm_set_bss_params(priv, &priv->bss_params); + priv->bss_params.reset_beacon_loss = 0; + + mutex_unlock(&priv->conf_mutex); +} + +/* ******************************************************************** */ +/* Internal API */ + +/* + * This function is called to Parse the SDD file + * to extract listen_interval and PTA related information + * sdd is a TLV: u8 id, u8 len, u8 data[] + */ +static int cw1200_parse_sdd_file(struct cw1200_common *priv) +{ + const u8 *p = priv->sdd->data; + int ret = 0; + + while (p + 2 <= priv->sdd->data + priv->sdd->size) { + if (p + p[1] + 2 > priv->sdd->data + priv->sdd->size) { + pr_warn("Malformed sdd structure\n"); + return -1; + } + switch (p[0]) { + case SDD_PTA_CFG_ELT_ID: { + u16 v; + if (p[1] < 4) { + pr_warn("SDD_PTA_CFG_ELT_ID malformed\n"); + ret = -1; + break; + } + v = le16_to_cpu(*((u16 *)(p + 2))); + if (!v) /* non-zero means this is enabled */ + break; + + v = le16_to_cpu(*((u16 *)(p + 4))); + priv->conf_listen_interval = (v >> 7) & 0x1F; + pr_debug("PTA found; Listen Interval %d\n", + priv->conf_listen_interval); + break; + } + case SDD_REFERENCE_FREQUENCY_ELT_ID: { + u16 clk = le16_to_cpu(*((u16 *)(p + 2))); + if (clk != priv->hw_refclk) + pr_warn("SDD file doesn't match configured refclk (%d vs %d)\n", + clk, priv->hw_refclk); + break; + } + default: + break; + } + p += p[1] + 2; + } + + if (!priv->bt_present) { + pr_debug("PTA element NOT found.\n"); + priv->conf_listen_interval = 0; + } + return ret; +} + +int cw1200_setup_mac(struct cw1200_common *priv) +{ + int ret = 0; + + /* NOTE: There is a bug in FW: it reports signal + * as RSSI if RSSI subscription is enabled. + * It's not enough to set WSM_RCPI_RSSI_USE_RSSI. + * + * NOTE2: RSSI based reports have been switched to RCPI, since + * FW has a bug and RSSI reported values are not stable, + * what can leads to signal level oscilations in user-end applications + */ + struct wsm_rcpi_rssi_threshold threshold = { + .rssiRcpiMode = WSM_RCPI_RSSI_THRESHOLD_ENABLE | + WSM_RCPI_RSSI_DONT_USE_UPPER | + WSM_RCPI_RSSI_DONT_USE_LOWER, + .rollingAverageCount = 16, + }; + + struct wsm_configuration cfg = { + .dot11StationId = &priv->mac_addr[0], + }; + + /* Remember the decission here to make sure, we will handle + * the RCPI/RSSI value correctly on WSM_EVENT_RCPI_RSS + */ + if (threshold.rssiRcpiMode & WSM_RCPI_RSSI_USE_RSSI) + priv->cqm_use_rssi = true; + + if (!priv->sdd) { + ret = request_firmware(&priv->sdd, priv->sdd_path, priv->pdev); + if (ret) { + pr_err("Can't load sdd file %s.\n", priv->sdd_path); + return ret; + } + cw1200_parse_sdd_file(priv); + } + + cfg.dpdData = priv->sdd->data; + cfg.dpdData_size = priv->sdd->size; + ret = wsm_configuration(priv, &cfg); + if (ret) + return ret; + + /* Configure RSSI/SCPI reporting as RSSI. */ + wsm_set_rcpi_rssi_threshold(priv, &threshold); + + return 0; +} + +static void cw1200_join_complete(struct cw1200_common *priv) +{ + pr_debug("[STA] Join complete (%d)\n", priv->join_complete_status); + + priv->join_pending = false; + if (priv->join_complete_status) { + priv->join_status = CW1200_JOIN_STATUS_PASSIVE; + cw1200_update_listening(priv, priv->listening); + cw1200_do_unjoin(priv); + ieee80211_connection_loss(priv->vif); + } else { + if (priv->mode == NL80211_IFTYPE_ADHOC) + priv->join_status = CW1200_JOIN_STATUS_IBSS; + else + priv->join_status = CW1200_JOIN_STATUS_PRE_STA; + } + wsm_unlock_tx(priv); /* Clearing the lock held before do_join() */ +} + +void cw1200_join_complete_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, join_complete_work); + mutex_lock(&priv->conf_mutex); + cw1200_join_complete(priv); + mutex_unlock(&priv->conf_mutex); +} + +void cw1200_join_complete_cb(struct cw1200_common *priv, + struct wsm_join_complete *arg) +{ + pr_debug("[STA] cw1200_join_complete_cb called, status=%d.\n", + arg->status); + + if (cancel_delayed_work(&priv->join_timeout)) { + priv->join_complete_status = arg->status; + queue_work(priv->workqueue, &priv->join_complete_work); + } +} + +/* MUST be called with tx_lock held! It will be unlocked for us. */ +static void cw1200_do_join(struct cw1200_common *priv) +{ + const u8 *bssid; + struct ieee80211_bss_conf *conf = &priv->vif->bss_conf; + struct cfg80211_bss *bss = NULL; + struct wsm_protected_mgmt_policy mgmt_policy; + struct wsm_join join = { + .mode = conf->ibss_joined ? + WSM_JOIN_MODE_IBSS : WSM_JOIN_MODE_BSS, + .preamble_type = WSM_JOIN_PREAMBLE_LONG, + .probe_for_join = 1, + .atim_window = 0, + .basic_rate_set = cw1200_rate_mask_to_wsm(priv, + conf->basic_rates), + }; + if (delayed_work_pending(&priv->join_timeout)) { + pr_warn("[STA] - Join request already pending, skipping..\n"); + wsm_unlock_tx(priv); + return; + } + + if (priv->join_status) + cw1200_do_unjoin(priv); + + bssid = priv->vif->bss_conf.bssid; + + bss = cfg80211_get_bss(priv->hw->wiphy, priv->channel, + bssid, NULL, 0, 0, 0); + + if (!bss && !conf->ibss_joined) { + wsm_unlock_tx(priv); + return; + } + + mutex_lock(&priv->conf_mutex); + + /* Under the conf lock: check scan status and + * bail out if it is in progress. + */ + if (atomic_read(&priv->scan.in_progress)) { + wsm_unlock_tx(priv); + goto done_put; + } + + priv->join_pending = true; + + /* Sanity check basic rates */ + if (!join.basic_rate_set) + join.basic_rate_set = 7; + + /* Sanity check beacon interval */ + if (!priv->beacon_int) + priv->beacon_int = 1; + + join.beacon_interval = priv->beacon_int; + + /* BT Coex related changes */ + if (priv->bt_present) { + if (((priv->conf_listen_interval * 100) % + priv->beacon_int) == 0) + priv->listen_interval = + ((priv->conf_listen_interval * 100) / + priv->beacon_int); + else + priv->listen_interval = + ((priv->conf_listen_interval * 100) / + priv->beacon_int + 1); + } + + if (priv->hw->conf.ps_dtim_period) + priv->join_dtim_period = priv->hw->conf.ps_dtim_period; + join.dtim_period = priv->join_dtim_period; + + join.channel_number = priv->channel->hw_value; + join.band = (priv->channel->band == IEEE80211_BAND_5GHZ) ? + WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G; + + memcpy(join.bssid, bssid, sizeof(join.bssid)); + + pr_debug("[STA] Join BSSID: %pM DTIM: %d, interval: %d\n", + join.bssid, + join.dtim_period, priv->beacon_int); + + if (!conf->ibss_joined) { + const u8 *ssidie; + rcu_read_lock(); + ssidie = ieee80211_bss_get_ie(bss, WLAN_EID_SSID); + if (ssidie) { + join.ssid_len = ssidie[1]; + memcpy(join.ssid, &ssidie[2], join.ssid_len); + } + rcu_read_unlock(); + } + + if (priv->vif->p2p) { + join.flags |= WSM_JOIN_FLAGS_P2P_GO; + join.basic_rate_set = + cw1200_rate_mask_to_wsm(priv, 0xFF0); + } + + /* Enable asynchronous join calls */ + if (!conf->ibss_joined) { + join.flags |= WSM_JOIN_FLAGS_FORCE; + join.flags |= WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND; + } + + wsm_flush_tx(priv); + + /* Stay Awake for Join and Auth Timeouts and a bit more */ + cw1200_pm_stay_awake(&priv->pm_state, + CW1200_JOIN_TIMEOUT + CW1200_AUTH_TIMEOUT); + + cw1200_update_listening(priv, false); + + /* Turn on Block ACKs */ + wsm_set_block_ack_policy(priv, priv->ba_tx_tid_mask, + priv->ba_rx_tid_mask); + + /* Set up timeout */ + if (join.flags & WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND) { + priv->join_status = CW1200_JOIN_STATUS_JOINING; + queue_delayed_work(priv->workqueue, + &priv->join_timeout, + CW1200_JOIN_TIMEOUT); + } + + /* 802.11w protected mgmt frames */ + mgmt_policy.protectedMgmtEnable = 0; + mgmt_policy.unprotectedMgmtFramesAllowed = 1; + mgmt_policy.encryptionForAuthFrame = 1; + wsm_set_protected_mgmt_policy(priv, &mgmt_policy); + + /* Perform actual join */ + if (wsm_join(priv, &join)) { + pr_err("[STA] cw1200_join_work: wsm_join failed!\n"); + cancel_delayed_work_sync(&priv->join_timeout); + cw1200_update_listening(priv, priv->listening); + /* Tx lock still held, unjoin will clear it. */ + if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) + wsm_unlock_tx(priv); + } else { + if (!(join.flags & WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND)) + cw1200_join_complete(priv); /* Will clear tx_lock */ + + /* Upload keys */ + cw1200_upload_keys(priv); + + /* Due to beacon filtering it is possible that the + * AP's beacon is not known for the mac80211 stack. + * Disable filtering temporary to make sure the stack + * receives at least one + */ + priv->disable_beacon_filter = true; + } + cw1200_update_filtering(priv); + +done_put: + mutex_unlock(&priv->conf_mutex); + if (bss) + cfg80211_put_bss(priv->hw->wiphy, bss); +} + +void cw1200_join_timeout(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, join_timeout.work); + pr_debug("[WSM] Join timed out.\n"); + wsm_lock_tx(priv); + if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) + wsm_unlock_tx(priv); +} + +static void cw1200_do_unjoin(struct cw1200_common *priv) +{ + struct wsm_reset reset = { + .reset_statistics = true, + }; + + cancel_delayed_work_sync(&priv->join_timeout); + + mutex_lock(&priv->conf_mutex); + priv->join_pending = false; + + if (atomic_read(&priv->scan.in_progress)) { + if (priv->delayed_unjoin) + wiphy_dbg(priv->hw->wiphy, "Delayed unjoin is already scheduled.\n"); + else + priv->delayed_unjoin = true; + goto done; + } + + priv->delayed_link_loss = false; + + if (!priv->join_status) + goto done; + + if (priv->join_status > CW1200_JOIN_STATUS_IBSS) { + wiphy_err(priv->hw->wiphy, "Unexpected: join status: %d\n", + priv->join_status); + BUG_ON(1); + } + + cancel_work_sync(&priv->update_filtering_work); + cancel_work_sync(&priv->set_beacon_wakeup_period_work); + priv->join_status = CW1200_JOIN_STATUS_PASSIVE; + + /* Unjoin is a reset. */ + wsm_flush_tx(priv); + wsm_keep_alive_period(priv, 0); + wsm_reset(priv, &reset); + wsm_set_output_power(priv, priv->output_power * 10); + priv->join_dtim_period = 0; + cw1200_setup_mac(priv); + cw1200_free_event_queue(priv); + cancel_work_sync(&priv->event_handler); + cw1200_update_listening(priv, priv->listening); + cw1200_cqm_bssloss_sm(priv, 0, 0, 0); + + /* Disable Block ACKs */ + wsm_set_block_ack_policy(priv, 0, 0); + + priv->disable_beacon_filter = false; + cw1200_update_filtering(priv); + memset(&priv->association_mode, 0, + sizeof(priv->association_mode)); + memset(&priv->bss_params, 0, sizeof(priv->bss_params)); + priv->setbssparams_done = false; + memset(&priv->firmware_ps_mode, 0, + sizeof(priv->firmware_ps_mode)); + + pr_debug("[STA] Unjoin completed.\n"); + +done: + mutex_unlock(&priv->conf_mutex); +} + +void cw1200_unjoin_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, unjoin_work); + + cw1200_do_unjoin(priv); + + /* Tell the stack we're dead */ + ieee80211_connection_loss(priv->vif); + + wsm_unlock_tx(priv); +} + +int cw1200_enable_listening(struct cw1200_common *priv) +{ + struct wsm_start start = { + .mode = WSM_START_MODE_P2P_DEV, + .band = WSM_PHY_BAND_2_4G, + .beacon_interval = 100, + .dtim_period = 1, + .probe_delay = 0, + .basic_rate_set = 0x0F, + }; + + if (priv->channel) { + start.band = priv->channel->band == IEEE80211_BAND_5GHZ ? + WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G; + start.channel_number = priv->channel->hw_value; + } else { + start.band = WSM_PHY_BAND_2_4G; + start.channel_number = 1; + } + + return wsm_start(priv, &start); +} + +int cw1200_disable_listening(struct cw1200_common *priv) +{ + int ret; + struct wsm_reset reset = { + .reset_statistics = true, + }; + ret = wsm_reset(priv, &reset); + return ret; +} + +void cw1200_update_listening(struct cw1200_common *priv, bool enabled) +{ + if (enabled) { + if (priv->join_status == CW1200_JOIN_STATUS_PASSIVE) { + if (!cw1200_enable_listening(priv)) + priv->join_status = CW1200_JOIN_STATUS_MONITOR; + wsm_set_probe_responder(priv, true); + } + } else { + if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) { + if (!cw1200_disable_listening(priv)) + priv->join_status = CW1200_JOIN_STATUS_PASSIVE; + wsm_set_probe_responder(priv, false); + } + } +} + +int cw1200_set_uapsd_param(struct cw1200_common *priv, + const struct wsm_edca_params *arg) +{ + int ret; + u16 uapsd_flags = 0; + + /* Here's the mapping AC [queue, bit] + * VO [0,3], VI [1, 2], BE [2, 1], BK [3, 0] + */ + + if (arg->uapsd_enable[0]) + uapsd_flags |= 1 << 3; + + if (arg->uapsd_enable[1]) + uapsd_flags |= 1 << 2; + + if (arg->uapsd_enable[2]) + uapsd_flags |= 1 << 1; + + if (arg->uapsd_enable[3]) + uapsd_flags |= 1; + + /* Currently pseudo U-APSD operation is not supported, so setting + * MinAutoTriggerInterval, MaxAutoTriggerInterval and + * AutoTriggerStep to 0 + */ + + priv->uapsd_info.uapsd_flags = cpu_to_le16(uapsd_flags); + priv->uapsd_info.min_auto_trigger_interval = 0; + priv->uapsd_info.max_auto_trigger_interval = 0; + priv->uapsd_info.auto_trigger_step = 0; + + ret = wsm_set_uapsd_info(priv, &priv->uapsd_info); + return ret; +} + +/* ******************************************************************** */ +/* AP API */ + +int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct cw1200_common *priv = hw->priv; + struct cw1200_sta_priv *sta_priv = + (struct cw1200_sta_priv *)&sta->drv_priv; + struct cw1200_link_entry *entry; + struct sk_buff *skb; + + if (priv->mode != NL80211_IFTYPE_AP) + return 0; + + sta_priv->link_id = cw1200_find_link_id(priv, sta->addr); + if (WARN_ON(!sta_priv->link_id)) { + wiphy_info(priv->hw->wiphy, + "[AP] No more link IDs available.\n"); + return -ENOENT; + } + + entry = &priv->link_id_db[sta_priv->link_id - 1]; + spin_lock_bh(&priv->ps_state_lock); + if ((sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) == + IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) + priv->sta_asleep_mask |= BIT(sta_priv->link_id); + entry->status = CW1200_LINK_HARD; + while ((skb = skb_dequeue(&entry->rx_queue))) + ieee80211_rx_irqsafe(priv->hw, skb); + spin_unlock_bh(&priv->ps_state_lock); + return 0; +} + +int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct cw1200_common *priv = hw->priv; + struct cw1200_sta_priv *sta_priv = + (struct cw1200_sta_priv *)&sta->drv_priv; + struct cw1200_link_entry *entry; + + if (priv->mode != NL80211_IFTYPE_AP || !sta_priv->link_id) + return 0; + + entry = &priv->link_id_db[sta_priv->link_id - 1]; + spin_lock_bh(&priv->ps_state_lock); + entry->status = CW1200_LINK_RESERVE; + entry->timestamp = jiffies; + wsm_lock_tx_async(priv); + if (queue_work(priv->workqueue, &priv->link_id_work) <= 0) + wsm_unlock_tx(priv); + spin_unlock_bh(&priv->ps_state_lock); + flush_workqueue(priv->workqueue); + return 0; +} + +static void __cw1200_sta_notify(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + enum sta_notify_cmd notify_cmd, + int link_id) +{ + struct cw1200_common *priv = dev->priv; + u32 bit, prev; + + /* Zero link id means "for all link IDs" */ + if (link_id) + bit = BIT(link_id); + else if (WARN_ON_ONCE(notify_cmd != STA_NOTIFY_AWAKE)) + bit = 0; + else + bit = priv->link_id_map; + prev = priv->sta_asleep_mask & bit; + + switch (notify_cmd) { + case STA_NOTIFY_SLEEP: + if (!prev) { + if (priv->buffered_multicasts && + !priv->sta_asleep_mask) + queue_work(priv->workqueue, + &priv->multicast_start_work); + priv->sta_asleep_mask |= bit; + } + break; + case STA_NOTIFY_AWAKE: + if (prev) { + priv->sta_asleep_mask &= ~bit; + priv->pspoll_mask &= ~bit; + if (priv->tx_multicast && link_id && + !priv->sta_asleep_mask) + queue_work(priv->workqueue, + &priv->multicast_stop_work); + cw1200_bh_wakeup(priv); + } + break; + } +} + +void cw1200_sta_notify(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + enum sta_notify_cmd notify_cmd, + struct ieee80211_sta *sta) +{ + struct cw1200_common *priv = dev->priv; + struct cw1200_sta_priv *sta_priv = + (struct cw1200_sta_priv *)&sta->drv_priv; + + spin_lock_bh(&priv->ps_state_lock); + __cw1200_sta_notify(dev, vif, notify_cmd, sta_priv->link_id); + spin_unlock_bh(&priv->ps_state_lock); +} + +static void cw1200_ps_notify(struct cw1200_common *priv, + int link_id, bool ps) +{ + if (link_id > CW1200_MAX_STA_IN_AP_MODE) + return; + + pr_debug("%s for LinkId: %d. STAs asleep: %.8X\n", + ps ? "Stop" : "Start", + link_id, priv->sta_asleep_mask); + + __cw1200_sta_notify(priv->hw, priv->vif, + ps ? STA_NOTIFY_SLEEP : STA_NOTIFY_AWAKE, link_id); +} + +static int cw1200_set_tim_impl(struct cw1200_common *priv, bool aid0_bit_set) +{ + struct sk_buff *skb; + struct wsm_update_ie update_ie = { + .what = WSM_UPDATE_IE_BEACON, + .count = 1, + }; + u16 tim_offset, tim_length; + + pr_debug("[AP] mcast: %s.\n", aid0_bit_set ? "ena" : "dis"); + + skb = ieee80211_beacon_get_tim(priv->hw, priv->vif, + &tim_offset, &tim_length); + if (!skb) { + if (!__cw1200_flush(priv, true)) + wsm_unlock_tx(priv); + return -ENOENT; + } + + if (tim_offset && tim_length >= 6) { + /* Ignore DTIM count from mac80211: + * firmware handles DTIM internally. + */ + skb->data[tim_offset + 2] = 0; + + /* Set/reset aid0 bit */ + if (aid0_bit_set) + skb->data[tim_offset + 4] |= 1; + else + skb->data[tim_offset + 4] &= ~1; + } + + update_ie.ies = &skb->data[tim_offset]; + update_ie.length = tim_length; + wsm_update_ie(priv, &update_ie); + + dev_kfree_skb(skb); + + return 0; +} + +void cw1200_set_tim_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, set_tim_work); + (void)cw1200_set_tim_impl(priv, priv->aid0_bit_set); +} + +int cw1200_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta, + bool set) +{ + struct cw1200_common *priv = dev->priv; + queue_work(priv->workqueue, &priv->set_tim_work); + return 0; +} + +void cw1200_set_cts_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, set_cts_work); + + u8 erp_ie[3] = {WLAN_EID_ERP_INFO, 0x1, 0}; + struct wsm_update_ie update_ie = { + .what = WSM_UPDATE_IE_BEACON, + .count = 1, + .ies = erp_ie, + .length = 3, + }; + u32 erp_info; + __le32 use_cts_prot; + mutex_lock(&priv->conf_mutex); + erp_info = priv->erp_info; + mutex_unlock(&priv->conf_mutex); + use_cts_prot = + erp_info & WLAN_ERP_USE_PROTECTION ? + __cpu_to_le32(1) : 0; + + erp_ie[ERP_INFO_BYTE_OFFSET] = erp_info; + + pr_debug("[STA] ERP information 0x%x\n", erp_info); + + wsm_write_mib(priv, WSM_MIB_ID_NON_ERP_PROTECTION, + &use_cts_prot, sizeof(use_cts_prot)); + wsm_update_ie(priv, &update_ie); + + return; +} + +static int cw1200_set_btcoexinfo(struct cw1200_common *priv) +{ + struct wsm_override_internal_txrate arg; + int ret = 0; + + if (priv->mode == NL80211_IFTYPE_STATION) { + /* Plumb PSPOLL and NULL template */ + cw1200_upload_pspoll(priv); + cw1200_upload_null(priv); + cw1200_upload_qosnull(priv); + } else { + return 0; + } + + memset(&arg, 0, sizeof(struct wsm_override_internal_txrate)); + + if (!priv->vif->p2p) { + /* STATION mode */ + if (priv->bss_params.operational_rate_set & ~0xF) { + pr_debug("[STA] STA has ERP rates\n"); + /* G or BG mode */ + arg.internalTxRate = (__ffs( + priv->bss_params.operational_rate_set & ~0xF)); + } else { + pr_debug("[STA] STA has non ERP rates\n"); + /* B only mode */ + arg.internalTxRate = (__ffs(priv->association_mode.basic_rate_set)); + } + arg.nonErpInternalTxRate = (__ffs(priv->association_mode.basic_rate_set)); + } else { + /* P2P mode */ + arg.internalTxRate = (__ffs(priv->bss_params.operational_rate_set & ~0xF)); + arg.nonErpInternalTxRate = (__ffs(priv->bss_params.operational_rate_set & ~0xF)); + } + + pr_debug("[STA] BTCOEX_INFO MODE %d, internalTxRate : %x, nonErpInternalTxRate: %x\n", + priv->mode, + arg.internalTxRate, + arg.nonErpInternalTxRate); + + ret = wsm_write_mib(priv, WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE, + &arg, sizeof(arg)); + + return ret; +} + +void cw1200_bss_info_changed(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u32 changed) +{ + struct cw1200_common *priv = dev->priv; + bool do_join = false; + + mutex_lock(&priv->conf_mutex); + + pr_debug("BSS CHANGED: %08x\n", changed); + + /* TODO: BSS_CHANGED_QOS */ + /* TODO: BSS_CHANGED_TXPOWER */ + + if (changed & BSS_CHANGED_ARP_FILTER) { + struct wsm_mib_arp_ipv4_filter filter = {0}; + int i; + + pr_debug("[STA] BSS_CHANGED_ARP_FILTER cnt: %d\n", + info->arp_addr_cnt); + + /* Currently only one IP address is supported by firmware. + * In case of more IPs arp filtering will be disabled. + */ + if (info->arp_addr_cnt > 0 && + info->arp_addr_cnt <= WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES) { + for (i = 0; i < info->arp_addr_cnt; i++) { + filter.ipv4addrs[i] = info->arp_addr_list[i]; + pr_debug("[STA] addr[%d]: 0x%X\n", + i, filter.ipv4addrs[i]); + } + filter.enable = __cpu_to_le32(1); + } + + pr_debug("[STA] arp ip filter enable: %d\n", + __le32_to_cpu(filter.enable)); + + wsm_set_arp_ipv4_filter(priv, &filter); + } + + if (changed & + (BSS_CHANGED_BEACON | + BSS_CHANGED_AP_PROBE_RESP | + BSS_CHANGED_BSSID | + BSS_CHANGED_SSID | + BSS_CHANGED_IBSS)) { + pr_debug("BSS_CHANGED_BEACON\n"); + priv->beacon_int = info->beacon_int; + cw1200_update_beaconing(priv); + cw1200_upload_beacon(priv); + } + + if (changed & BSS_CHANGED_BEACON_ENABLED) { + pr_debug("BSS_CHANGED_BEACON_ENABLED (%d)\n", info->enable_beacon); + + if (priv->enable_beacon != info->enable_beacon) { + cw1200_enable_beaconing(priv, info->enable_beacon); + priv->enable_beacon = info->enable_beacon; + } + } + + if (changed & BSS_CHANGED_BEACON_INT) { + pr_debug("CHANGED_BEACON_INT\n"); + if (info->ibss_joined) + do_join = true; + else if (priv->join_status == CW1200_JOIN_STATUS_AP) + cw1200_update_beaconing(priv); + } + + /* assoc/disassoc, or maybe AID changed */ + if (changed & BSS_CHANGED_ASSOC) { + wsm_lock_tx(priv); + priv->wep_default_key_id = -1; + wsm_unlock_tx(priv); + } + + if (changed & BSS_CHANGED_BSSID) { + pr_debug("BSS_CHANGED_BSSID\n"); + do_join = true; + } + + if (changed & + (BSS_CHANGED_ASSOC | + BSS_CHANGED_BSSID | + BSS_CHANGED_IBSS | + BSS_CHANGED_BASIC_RATES | + BSS_CHANGED_HT)) { + pr_debug("BSS_CHANGED_ASSOC\n"); + if (info->assoc) { + if (priv->join_status < CW1200_JOIN_STATUS_PRE_STA) { + ieee80211_connection_loss(vif); + mutex_unlock(&priv->conf_mutex); + return; + } else if (priv->join_status == CW1200_JOIN_STATUS_PRE_STA) { + priv->join_status = CW1200_JOIN_STATUS_STA; + } + } else { + do_join = true; + } + + if (info->assoc || info->ibss_joined) { + struct ieee80211_sta *sta = NULL; + u32 val = 0; + + if (info->dtim_period) + priv->join_dtim_period = info->dtim_period; + priv->beacon_int = info->beacon_int; + + rcu_read_lock(); + + if (info->bssid && !info->ibss_joined) + sta = ieee80211_find_sta(vif, info->bssid); + if (sta) { + priv->ht_info.ht_cap = sta->ht_cap; + priv->bss_params.operational_rate_set = + cw1200_rate_mask_to_wsm(priv, + sta->supp_rates[priv->channel->band]); + priv->ht_info.channel_type = cfg80211_get_chandef_type(&dev->conf.chandef); + priv->ht_info.operation_mode = info->ht_operation_mode; + } else { + memset(&priv->ht_info, 0, + sizeof(priv->ht_info)); + priv->bss_params.operational_rate_set = -1; + } + rcu_read_unlock(); + + /* Non Greenfield stations present */ + if (priv->ht_info.operation_mode & + IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT) + val |= WSM_NON_GREENFIELD_STA_PRESENT; + + /* Set HT protection method */ + val |= (priv->ht_info.operation_mode & IEEE80211_HT_OP_MODE_PROTECTION) << 2; + + /* TODO: + * STBC_param.dual_cts + * STBC_param.LSIG_TXOP_FILL + */ + + val = cpu_to_le32(val); + wsm_write_mib(priv, WSM_MIB_ID_SET_HT_PROTECTION, + &val, sizeof(val)); + + priv->association_mode.greenfield = + cw1200_ht_greenfield(&priv->ht_info); + priv->association_mode.flags = + WSM_ASSOCIATION_MODE_SNOOP_ASSOC_FRAMES | + WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE | + WSM_ASSOCIATION_MODE_USE_HT_MODE | + WSM_ASSOCIATION_MODE_USE_BASIC_RATE_SET | + WSM_ASSOCIATION_MODE_USE_MPDU_START_SPACING; + priv->association_mode.preamble = + info->use_short_preamble ? + WSM_JOIN_PREAMBLE_SHORT : + WSM_JOIN_PREAMBLE_LONG; + priv->association_mode.basic_rate_set = __cpu_to_le32( + cw1200_rate_mask_to_wsm(priv, + info->basic_rates)); + priv->association_mode.mpdu_start_spacing = + cw1200_ht_ampdu_density(&priv->ht_info); + + cw1200_cqm_bssloss_sm(priv, 0, 0, 0); + cancel_work_sync(&priv->unjoin_work); + + priv->bss_params.beacon_lost_count = priv->cqm_beacon_loss_count; + priv->bss_params.aid = info->aid; + + if (priv->join_dtim_period < 1) + priv->join_dtim_period = 1; + + pr_debug("[STA] DTIM %d, interval: %d\n", + priv->join_dtim_period, priv->beacon_int); + pr_debug("[STA] Preamble: %d, Greenfield: %d, Aid: %d, Rates: 0x%.8X, Basic: 0x%.8X\n", + priv->association_mode.preamble, + priv->association_mode.greenfield, + priv->bss_params.aid, + priv->bss_params.operational_rate_set, + priv->association_mode.basic_rate_set); + wsm_set_association_mode(priv, &priv->association_mode); + + if (!info->ibss_joined) { + wsm_keep_alive_period(priv, 30 /* sec */); + wsm_set_bss_params(priv, &priv->bss_params); + priv->setbssparams_done = true; + cw1200_set_beacon_wakeup_period_work(&priv->set_beacon_wakeup_period_work); + cw1200_set_pm(priv, &priv->powersave_mode); + } + if (priv->vif->p2p) { + pr_debug("[STA] Setting p2p powersave configuration.\n"); + wsm_set_p2p_ps_modeinfo(priv, + &priv->p2p_ps_modeinfo); + } + if (priv->bt_present) + cw1200_set_btcoexinfo(priv); + } else { + memset(&priv->association_mode, 0, + sizeof(priv->association_mode)); + memset(&priv->bss_params, 0, sizeof(priv->bss_params)); + } + } + + /* ERP Protection */ + if (changed & (BSS_CHANGED_ASSOC | + BSS_CHANGED_ERP_CTS_PROT | + BSS_CHANGED_ERP_PREAMBLE)) { + u32 prev_erp_info = priv->erp_info; + if (info->use_cts_prot) + priv->erp_info |= WLAN_ERP_USE_PROTECTION; + else if (!(prev_erp_info & WLAN_ERP_NON_ERP_PRESENT)) + priv->erp_info &= ~WLAN_ERP_USE_PROTECTION; + + if (info->use_short_preamble) + priv->erp_info |= WLAN_ERP_BARKER_PREAMBLE; + else + priv->erp_info &= ~WLAN_ERP_BARKER_PREAMBLE; + + pr_debug("[STA] ERP Protection: %x\n", priv->erp_info); + + if (prev_erp_info != priv->erp_info) + queue_work(priv->workqueue, &priv->set_cts_work); + } + + /* ERP Slottime */ + if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_SLOT)) { + __le32 slot_time = info->use_short_slot ? + __cpu_to_le32(9) : __cpu_to_le32(20); + pr_debug("[STA] Slot time: %d us.\n", + __le32_to_cpu(slot_time)); + wsm_write_mib(priv, WSM_MIB_ID_DOT11_SLOT_TIME, + &slot_time, sizeof(slot_time)); + } + + if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_CQM)) { + struct wsm_rcpi_rssi_threshold threshold = { + .rollingAverageCount = 8, + }; + pr_debug("[CQM] RSSI threshold subscribe: %d +- %d\n", + info->cqm_rssi_thold, info->cqm_rssi_hyst); + priv->cqm_rssi_thold = info->cqm_rssi_thold; + priv->cqm_rssi_hyst = info->cqm_rssi_hyst; + + if (info->cqm_rssi_thold || info->cqm_rssi_hyst) { + /* RSSI subscription enabled */ + /* TODO: It's not a correct way of setting threshold. + * Upper and lower must be set equal here and adjusted + * in callback. However current implementation is much + * more relaible and stable. + */ + + /* RSSI: signed Q8.0, RCPI: unsigned Q7.1 + * RSSI = RCPI / 2 - 110 + */ + if (priv->cqm_use_rssi) { + threshold.upperThreshold = + info->cqm_rssi_thold + info->cqm_rssi_hyst; + threshold.lowerThreshold = + info->cqm_rssi_thold; + threshold.rssiRcpiMode |= WSM_RCPI_RSSI_USE_RSSI; + } else { + threshold.upperThreshold = (info->cqm_rssi_thold + info->cqm_rssi_hyst + 110) * 2; + threshold.lowerThreshold = (info->cqm_rssi_thold + 110) * 2; + } + threshold.rssiRcpiMode |= WSM_RCPI_RSSI_THRESHOLD_ENABLE; + } else { + /* There is a bug in FW, see sta.c. We have to enable + * dummy subscription to get correct RSSI values. + */ + threshold.rssiRcpiMode |= + WSM_RCPI_RSSI_THRESHOLD_ENABLE | + WSM_RCPI_RSSI_DONT_USE_UPPER | + WSM_RCPI_RSSI_DONT_USE_LOWER; + if (priv->cqm_use_rssi) + threshold.rssiRcpiMode |= WSM_RCPI_RSSI_USE_RSSI; + } + wsm_set_rcpi_rssi_threshold(priv, &threshold); + } + mutex_unlock(&priv->conf_mutex); + + if (do_join) { + wsm_lock_tx(priv); + cw1200_do_join(priv); /* Will unlock it for us */ + } +} + +void cw1200_multicast_start_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, multicast_start_work); + long tmo = priv->join_dtim_period * + (priv->beacon_int + 20) * HZ / 1024; + + cancel_work_sync(&priv->multicast_stop_work); + + if (!priv->aid0_bit_set) { + wsm_lock_tx(priv); + cw1200_set_tim_impl(priv, true); + priv->aid0_bit_set = true; + mod_timer(&priv->mcast_timeout, jiffies + tmo); + wsm_unlock_tx(priv); + } +} + +void cw1200_multicast_stop_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, multicast_stop_work); + + if (priv->aid0_bit_set) { + del_timer_sync(&priv->mcast_timeout); + wsm_lock_tx(priv); + priv->aid0_bit_set = false; + cw1200_set_tim_impl(priv, false); + wsm_unlock_tx(priv); + } +} + +void cw1200_mcast_timeout(unsigned long arg) +{ + struct cw1200_common *priv = + (struct cw1200_common *)arg; + + wiphy_warn(priv->hw->wiphy, + "Multicast delivery timeout.\n"); + spin_lock_bh(&priv->ps_state_lock); + priv->tx_multicast = priv->aid0_bit_set && + priv->buffered_multicasts; + if (priv->tx_multicast) + cw1200_bh_wakeup(priv); + spin_unlock_bh(&priv->ps_state_lock); +} + +int cw1200_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 *ssn, + u8 buf_size) +{ + /* Aggregation is implemented fully in firmware, + * including block ack negotiation. Do not allow + * mac80211 stack to do anything: it interferes with + * the firmware. + */ + + /* Note that we still need this function stubbed. */ + return -ENOTSUPP; +} + +/* ******************************************************************** */ +/* WSM callback */ +void cw1200_suspend_resume(struct cw1200_common *priv, + struct wsm_suspend_resume *arg) +{ + pr_debug("[AP] %s: %s\n", + arg->stop ? "stop" : "start", + arg->multicast ? "broadcast" : "unicast"); + + if (arg->multicast) { + bool cancel_tmo = false; + spin_lock_bh(&priv->ps_state_lock); + if (arg->stop) { + priv->tx_multicast = false; + } else { + /* Firmware sends this indication every DTIM if there + * is a STA in powersave connected. There is no reason + * to suspend, following wakeup will consume much more + * power than it could be saved. + */ + cw1200_pm_stay_awake(&priv->pm_state, + priv->join_dtim_period * + (priv->beacon_int + 20) * HZ / 1024); + priv->tx_multicast = (priv->aid0_bit_set && + priv->buffered_multicasts); + if (priv->tx_multicast) { + cancel_tmo = true; + cw1200_bh_wakeup(priv); + } + } + spin_unlock_bh(&priv->ps_state_lock); + if (cancel_tmo) + del_timer_sync(&priv->mcast_timeout); + } else { + spin_lock_bh(&priv->ps_state_lock); + cw1200_ps_notify(priv, arg->link_id, arg->stop); + spin_unlock_bh(&priv->ps_state_lock); + if (!arg->stop) + cw1200_bh_wakeup(priv); + } + return; +} + +/* ******************************************************************** */ +/* AP privates */ + +static int cw1200_upload_beacon(struct cw1200_common *priv) +{ + int ret = 0; + struct ieee80211_mgmt *mgmt; + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_BEACON, + }; + + u16 tim_offset; + u16 tim_len; + + if (priv->mode == NL80211_IFTYPE_STATION || + priv->mode == NL80211_IFTYPE_MONITOR || + priv->mode == NL80211_IFTYPE_UNSPECIFIED) + goto done; + + if (priv->vif->p2p) + frame.rate = WSM_TRANSMIT_RATE_6; + + frame.skb = ieee80211_beacon_get_tim(priv->hw, priv->vif, + &tim_offset, &tim_len); + if (!frame.skb) + return -ENOMEM; + + ret = wsm_set_template_frame(priv, &frame); + + if (ret) + goto done; + + /* TODO: Distill probe resp; remove TIM + * and any other beacon-specific IEs + */ + mgmt = (void *)frame.skb->data; + mgmt->frame_control = + __cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_PROBE_RESP); + + frame.frame_type = WSM_FRAME_TYPE_PROBE_RESPONSE; + if (priv->vif->p2p) { + ret = wsm_set_probe_responder(priv, true); + } else { + ret = wsm_set_template_frame(priv, &frame); + wsm_set_probe_responder(priv, false); + } + +done: + dev_kfree_skb(frame.skb); + + return ret; +} + +static int cw1200_upload_pspoll(struct cw1200_common *priv) +{ + int ret = 0; + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_PS_POLL, + .rate = 0xFF, + }; + + + frame.skb = ieee80211_pspoll_get(priv->hw, priv->vif); + if (!frame.skb) + return -ENOMEM; + + ret = wsm_set_template_frame(priv, &frame); + + dev_kfree_skb(frame.skb); + + return ret; +} + +static int cw1200_upload_null(struct cw1200_common *priv) +{ + int ret = 0; + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_NULL, + .rate = 0xFF, + }; + + frame.skb = ieee80211_nullfunc_get(priv->hw, priv->vif); + if (!frame.skb) + return -ENOMEM; + + ret = wsm_set_template_frame(priv, &frame); + + dev_kfree_skb(frame.skb); + + return ret; +} + +static int cw1200_upload_qosnull(struct cw1200_common *priv) +{ + int ret = 0; + /* TODO: This needs to be implemented + + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_QOS_NULL, + .rate = 0xFF, + }; + + frame.skb = ieee80211_qosnullfunc_get(priv->hw, priv->vif); + if (!frame.skb) + return -ENOMEM; + + ret = wsm_set_template_frame(priv, &frame); + + dev_kfree_skb(frame.skb); + + */ + return ret; +} + +static int cw1200_enable_beaconing(struct cw1200_common *priv, + bool enable) +{ + struct wsm_beacon_transmit transmit = { + .enable_beaconing = enable, + }; + + return wsm_beacon_transmit(priv, &transmit); +} + +static int cw1200_start_ap(struct cw1200_common *priv) +{ + int ret; + struct ieee80211_bss_conf *conf = &priv->vif->bss_conf; + struct wsm_start start = { + .mode = priv->vif->p2p ? + WSM_START_MODE_P2P_GO : WSM_START_MODE_AP, + .band = (priv->channel->band == IEEE80211_BAND_5GHZ) ? + WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G, + .channel_number = priv->channel->hw_value, + .beacon_interval = conf->beacon_int, + .dtim_period = conf->dtim_period, + .preamble = conf->use_short_preamble ? + WSM_JOIN_PREAMBLE_SHORT : + WSM_JOIN_PREAMBLE_LONG, + .probe_delay = 100, + .basic_rate_set = cw1200_rate_mask_to_wsm(priv, + conf->basic_rates), + }; + struct wsm_operational_mode mode = { + .power_mode = cw1200_power_mode, + .disable_more_flag_usage = true, + }; + + memset(start.ssid, 0, sizeof(start.ssid)); + if (!conf->hidden_ssid) { + start.ssid_len = conf->ssid_len; + memcpy(start.ssid, conf->ssid, start.ssid_len); + } + + priv->beacon_int = conf->beacon_int; + priv->join_dtim_period = conf->dtim_period; + + memset(&priv->link_id_db, 0, sizeof(priv->link_id_db)); + + pr_debug("[AP] ch: %d(%d), bcn: %d(%d), brt: 0x%.8X, ssid: %.*s.\n", + start.channel_number, start.band, + start.beacon_interval, start.dtim_period, + start.basic_rate_set, + start.ssid_len, start.ssid); + ret = wsm_start(priv, &start); + if (!ret) + ret = cw1200_upload_keys(priv); + if (!ret && priv->vif->p2p) { + pr_debug("[AP] Setting p2p powersave configuration.\n"); + wsm_set_p2p_ps_modeinfo(priv, &priv->p2p_ps_modeinfo); + } + if (!ret) { + wsm_set_block_ack_policy(priv, 0, 0); + priv->join_status = CW1200_JOIN_STATUS_AP; + cw1200_update_filtering(priv); + } + wsm_set_operational_mode(priv, &mode); + return ret; +} + +static int cw1200_update_beaconing(struct cw1200_common *priv) +{ + struct ieee80211_bss_conf *conf = &priv->vif->bss_conf; + struct wsm_reset reset = { + .link_id = 0, + .reset_statistics = true, + }; + + if (priv->mode == NL80211_IFTYPE_AP) { + /* TODO: check if changed channel, band */ + if (priv->join_status != CW1200_JOIN_STATUS_AP || + priv->beacon_int != conf->beacon_int) { + pr_debug("ap restarting\n"); + wsm_lock_tx(priv); + if (priv->join_status != CW1200_JOIN_STATUS_PASSIVE) + wsm_reset(priv, &reset); + priv->join_status = CW1200_JOIN_STATUS_PASSIVE; + cw1200_start_ap(priv); + wsm_unlock_tx(priv); + } else + pr_debug("ap started join_status: %d\n", + priv->join_status); + } + return 0; +} diff --git a/drivers/net/wireless/cw1200/sta.h b/drivers/net/wireless/cw1200/sta.h new file mode 100644 index 000000000000..35babb62cc6a --- /dev/null +++ b/drivers/net/wireless/cw1200/sta.h @@ -0,0 +1,123 @@ +/* + * Mac80211 STA interface for ST-Ericsson CW1200 mac80211 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef STA_H_INCLUDED +#define STA_H_INCLUDED + +/* ******************************************************************** */ +/* mac80211 API */ + +int cw1200_start(struct ieee80211_hw *dev); +void cw1200_stop(struct ieee80211_hw *dev); +int cw1200_add_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif); +void cw1200_remove_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif); +int cw1200_change_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + enum nl80211_iftype new_type, + bool p2p); +int cw1200_config(struct ieee80211_hw *dev, u32 changed); +void cw1200_configure_filter(struct ieee80211_hw *dev, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast); +int cw1200_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif, + u16 queue, const struct ieee80211_tx_queue_params *params); +int cw1200_get_stats(struct ieee80211_hw *dev, + struct ieee80211_low_level_stats *stats); +int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key); + +int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value); + +void cw1200_flush(struct ieee80211_hw *hw, u32 queues, bool drop); + +u64 cw1200_prepare_multicast(struct ieee80211_hw *hw, + struct netdev_hw_addr_list *mc_list); + +int cw1200_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg); + +/* ******************************************************************** */ +/* WSM callbacks */ + +void cw1200_join_complete_cb(struct cw1200_common *priv, + struct wsm_join_complete *arg); + +/* ******************************************************************** */ +/* WSM events */ + +void cw1200_free_event_queue(struct cw1200_common *priv); +void cw1200_event_handler(struct work_struct *work); +void cw1200_bss_loss_work(struct work_struct *work); +void cw1200_bss_params_work(struct work_struct *work); +void cw1200_keep_alive_work(struct work_struct *work); +void cw1200_tx_failure_work(struct work_struct *work); + +void __cw1200_cqm_bssloss_sm(struct cw1200_common *priv, int init, int good, + int bad); +static inline void cw1200_cqm_bssloss_sm(struct cw1200_common *priv, + int init, int good, int bad) +{ + spin_lock(&priv->bss_loss_lock); + __cw1200_cqm_bssloss_sm(priv, init, good, bad); + spin_unlock(&priv->bss_loss_lock); +} + +/* ******************************************************************** */ +/* Internal API */ + +int cw1200_setup_mac(struct cw1200_common *priv); +void cw1200_join_timeout(struct work_struct *work); +void cw1200_unjoin_work(struct work_struct *work); +void cw1200_join_complete_work(struct work_struct *work); +void cw1200_wep_key_work(struct work_struct *work); +void cw1200_update_listening(struct cw1200_common *priv, bool enabled); +void cw1200_update_filtering(struct cw1200_common *priv); +void cw1200_update_filtering_work(struct work_struct *work); +void cw1200_set_beacon_wakeup_period_work(struct work_struct *work); +int cw1200_enable_listening(struct cw1200_common *priv); +int cw1200_disable_listening(struct cw1200_common *priv); +int cw1200_set_uapsd_param(struct cw1200_common *priv, + const struct wsm_edca_params *arg); +void cw1200_ba_work(struct work_struct *work); +void cw1200_ba_timer(unsigned long arg); + +/* AP stuffs */ +int cw1200_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta, + bool set); +int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +void cw1200_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif, + enum sta_notify_cmd notify_cmd, + struct ieee80211_sta *sta); +void cw1200_bss_info_changed(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u32 changed); +int cw1200_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 *ssn, + u8 buf_size); + +void cw1200_suspend_resume(struct cw1200_common *priv, + struct wsm_suspend_resume *arg); +void cw1200_set_tim_work(struct work_struct *work); +void cw1200_set_cts_work(struct work_struct *work); +void cw1200_multicast_start_work(struct work_struct *work); +void cw1200_multicast_stop_work(struct work_struct *work); +void cw1200_mcast_timeout(unsigned long arg); + +#endif diff --git a/drivers/net/wireless/cw1200/txrx.c b/drivers/net/wireless/cw1200/txrx.c new file mode 100644 index 000000000000..0e40890e1993 --- /dev/null +++ b/drivers/net/wireless/cw1200/txrx.c @@ -0,0 +1,1474 @@ +/* + * Datapath implementation for ST-Ericsson CW1200 mac80211 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * 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 +#include +#include + +#include "cw1200.h" +#include "wsm.h" +#include "bh.h" +#include "sta.h" +#include "debug.h" + +#define CW1200_INVALID_RATE_ID (0xFF) + +static int cw1200_handle_action_rx(struct cw1200_common *priv, + struct sk_buff *skb); +static const struct ieee80211_rate * +cw1200_get_tx_rate(const struct cw1200_common *priv, + const struct ieee80211_tx_rate *rate); + +/* ******************************************************************** */ +/* TX queue lock / unlock */ + +static inline void cw1200_tx_queues_lock(struct cw1200_common *priv) +{ + int i; + for (i = 0; i < 4; ++i) + cw1200_queue_lock(&priv->tx_queue[i]); +} + +static inline void cw1200_tx_queues_unlock(struct cw1200_common *priv) +{ + int i; + for (i = 0; i < 4; ++i) + cw1200_queue_unlock(&priv->tx_queue[i]); +} + +/* ******************************************************************** */ +/* TX policy cache implementation */ + +static void tx_policy_dump(struct tx_policy *policy) +{ + pr_debug("[TX policy] %.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X %.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X %.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X: %d\n", + policy->raw[0] & 0x0F, policy->raw[0] >> 4, + policy->raw[1] & 0x0F, policy->raw[1] >> 4, + policy->raw[2] & 0x0F, policy->raw[2] >> 4, + policy->raw[3] & 0x0F, policy->raw[3] >> 4, + policy->raw[4] & 0x0F, policy->raw[4] >> 4, + policy->raw[5] & 0x0F, policy->raw[5] >> 4, + policy->raw[6] & 0x0F, policy->raw[6] >> 4, + policy->raw[7] & 0x0F, policy->raw[7] >> 4, + policy->raw[8] & 0x0F, policy->raw[8] >> 4, + policy->raw[9] & 0x0F, policy->raw[9] >> 4, + policy->raw[10] & 0x0F, policy->raw[10] >> 4, + policy->raw[11] & 0x0F, policy->raw[11] >> 4, + policy->defined); +} + +static void tx_policy_build(const struct cw1200_common *priv, + /* [out] */ struct tx_policy *policy, + struct ieee80211_tx_rate *rates, size_t count) +{ + int i, j; + unsigned limit = priv->short_frame_max_tx_count; + unsigned total = 0; + BUG_ON(rates[0].idx < 0); + memset(policy, 0, sizeof(*policy)); + + /* minstrel is buggy a little bit, so distille + * incoming rates first. */ + + /* Sort rates in descending order. */ + for (i = 1; i < count; ++i) { + if (rates[i].idx < 0) { + count = i; + break; + } + if (rates[i].idx > rates[i - 1].idx) { + struct ieee80211_tx_rate tmp = rates[i - 1]; + rates[i - 1] = rates[i]; + rates[i] = tmp; + } + } + + /* Eliminate duplicates. */ + total = rates[0].count; + for (i = 0, j = 1; j < count; ++j) { + if (rates[j].idx == rates[i].idx) { + rates[i].count += rates[j].count; + } else if (rates[j].idx > rates[i].idx) { + break; + } else { + ++i; + if (i != j) + rates[i] = rates[j]; + } + total += rates[j].count; + } + count = i + 1; + + /* Re-fill policy trying to keep every requested rate and with + * respect to the global max tx retransmission count. */ + if (limit < count) + limit = count; + if (total > limit) { + for (i = 0; i < count; ++i) { + int left = count - i - 1; + if (rates[i].count > limit - left) + rates[i].count = limit - left; + limit -= rates[i].count; + } + } + + /* HACK!!! Device has problems (at least) switching from + * 54Mbps CTS to 1Mbps. This switch takes enormous amount + * of time (100-200 ms), leading to valuable throughput drop. + * As a workaround, additional g-rates are injected to the + * policy. + */ + if (count == 2 && !(rates[0].flags & IEEE80211_TX_RC_MCS) && + rates[0].idx > 4 && rates[0].count > 2 && + rates[1].idx < 2) { + /* ">> 1" is an equivalent of "/ 2", but faster */ + int mid_rate = (rates[0].idx + 4) >> 1; + + /* Decrease number of retries for the initial rate */ + rates[0].count -= 2; + + if (mid_rate != 4) { + /* Keep fallback rate at 1Mbps. */ + rates[3] = rates[1]; + + /* Inject 1 transmission on lowest g-rate */ + rates[2].idx = 4; + rates[2].count = 1; + rates[2].flags = rates[1].flags; + + /* Inject 1 transmission on mid-rate */ + rates[1].idx = mid_rate; + rates[1].count = 1; + + /* Fallback to 1 Mbps is a really bad thing, + * so let's try to increase probability of + * successful transmission on the lowest g rate + * even more */ + if (rates[0].count >= 3) { + --rates[0].count; + ++rates[2].count; + } + + /* Adjust amount of rates defined */ + count += 2; + } else { + /* Keep fallback rate at 1Mbps. */ + rates[2] = rates[1]; + + /* Inject 2 transmissions on lowest g-rate */ + rates[1].idx = 4; + rates[1].count = 2; + + /* Adjust amount of rates defined */ + count += 1; + } + } + + policy->defined = cw1200_get_tx_rate(priv, &rates[0])->hw_value + 1; + + for (i = 0; i < count; ++i) { + register unsigned rateid, off, shift, retries; + + rateid = cw1200_get_tx_rate(priv, &rates[i])->hw_value; + off = rateid >> 3; /* eq. rateid / 8 */ + shift = (rateid & 0x07) << 2; /* eq. (rateid % 8) * 4 */ + + retries = rates[i].count; + if (retries > 0x0F) { + rates[i].count = 0x0f; + retries = 0x0F; + } + policy->tbl[off] |= __cpu_to_le32(retries << shift); + policy->retry_count += retries; + } + + pr_debug("[TX policy] Policy (%zu): %d:%d, %d:%d, %d:%d, %d:%d, %d:%d\n", + count, + rates[0].idx, rates[0].count, + rates[1].idx, rates[1].count, + rates[2].idx, rates[2].count, + rates[3].idx, rates[3].count, + rates[4].idx, rates[4].count); +} + +static inline bool tx_policy_is_equal(const struct tx_policy *wanted, + const struct tx_policy *cached) +{ + size_t count = wanted->defined >> 1; + if (wanted->defined > cached->defined) + return false; + if (count) { + if (memcmp(wanted->raw, cached->raw, count)) + return false; + } + if (wanted->defined & 1) { + if ((wanted->raw[count] & 0x0F) != (cached->raw[count] & 0x0F)) + return false; + } + return true; +} + +static int tx_policy_find(struct tx_policy_cache *cache, + const struct tx_policy *wanted) +{ + /* O(n) complexity. Not so good, but there's only 8 entries in + * the cache. + * Also lru helps to reduce search time. */ + struct tx_policy_cache_entry *it; + /* First search for policy in "used" list */ + list_for_each_entry(it, &cache->used, link) { + if (tx_policy_is_equal(wanted, &it->policy)) + return it - cache->cache; + } + /* Then - in "free list" */ + list_for_each_entry(it, &cache->free, link) { + if (tx_policy_is_equal(wanted, &it->policy)) + return it - cache->cache; + } + return -1; +} + +static inline void tx_policy_use(struct tx_policy_cache *cache, + struct tx_policy_cache_entry *entry) +{ + ++entry->policy.usage_count; + list_move(&entry->link, &cache->used); +} + +static inline int tx_policy_release(struct tx_policy_cache *cache, + struct tx_policy_cache_entry *entry) +{ + int ret = --entry->policy.usage_count; + if (!ret) + list_move(&entry->link, &cache->free); + return ret; +} + +void tx_policy_clean(struct cw1200_common *priv) +{ + int idx, locked; + struct tx_policy_cache *cache = &priv->tx_policy_cache; + struct tx_policy_cache_entry *entry; + + cw1200_tx_queues_lock(priv); + spin_lock_bh(&cache->lock); + locked = list_empty(&cache->free); + + for (idx = 0; idx < TX_POLICY_CACHE_SIZE; idx++) { + entry = &cache->cache[idx]; + /* Policy usage count should be 0 at this time as all queues + should be empty */ + if (WARN_ON(entry->policy.usage_count)) { + entry->policy.usage_count = 0; + list_move(&entry->link, &cache->free); + } + memset(&entry->policy, 0, sizeof(entry->policy)); + } + if (locked) + cw1200_tx_queues_unlock(priv); + + cw1200_tx_queues_unlock(priv); + spin_unlock_bh(&cache->lock); +} + +/* ******************************************************************** */ +/* External TX policy cache API */ + +void tx_policy_init(struct cw1200_common *priv) +{ + struct tx_policy_cache *cache = &priv->tx_policy_cache; + int i; + + memset(cache, 0, sizeof(*cache)); + + spin_lock_init(&cache->lock); + INIT_LIST_HEAD(&cache->used); + INIT_LIST_HEAD(&cache->free); + + for (i = 0; i < TX_POLICY_CACHE_SIZE; ++i) + list_add(&cache->cache[i].link, &cache->free); +} + +static int tx_policy_get(struct cw1200_common *priv, + struct ieee80211_tx_rate *rates, + size_t count, bool *renew) +{ + int idx; + struct tx_policy_cache *cache = &priv->tx_policy_cache; + struct tx_policy wanted; + + tx_policy_build(priv, &wanted, rates, count); + + spin_lock_bh(&cache->lock); + if (WARN_ON_ONCE(list_empty(&cache->free))) { + spin_unlock_bh(&cache->lock); + return CW1200_INVALID_RATE_ID; + } + idx = tx_policy_find(cache, &wanted); + if (idx >= 0) { + pr_debug("[TX policy] Used TX policy: %d\n", idx); + *renew = false; + } else { + struct tx_policy_cache_entry *entry; + *renew = true; + /* If policy is not found create a new one + * using the oldest entry in "free" list */ + entry = list_entry(cache->free.prev, + struct tx_policy_cache_entry, link); + entry->policy = wanted; + idx = entry - cache->cache; + pr_debug("[TX policy] New TX policy: %d\n", idx); + tx_policy_dump(&entry->policy); + } + tx_policy_use(cache, &cache->cache[idx]); + if (list_empty(&cache->free)) { + /* Lock TX queues. */ + cw1200_tx_queues_lock(priv); + } + spin_unlock_bh(&cache->lock); + return idx; +} + +static void tx_policy_put(struct cw1200_common *priv, int idx) +{ + int usage, locked; + struct tx_policy_cache *cache = &priv->tx_policy_cache; + + spin_lock_bh(&cache->lock); + locked = list_empty(&cache->free); + usage = tx_policy_release(cache, &cache->cache[idx]); + if (locked && !usage) { + /* Unlock TX queues. */ + cw1200_tx_queues_unlock(priv); + } + spin_unlock_bh(&cache->lock); +} + +static int tx_policy_upload(struct cw1200_common *priv) +{ + struct tx_policy_cache *cache = &priv->tx_policy_cache; + int i; + struct wsm_set_tx_rate_retry_policy arg = { + .num = 0, + }; + spin_lock_bh(&cache->lock); + + /* Upload only modified entries. */ + for (i = 0; i < TX_POLICY_CACHE_SIZE; ++i) { + struct tx_policy *src = &cache->cache[i].policy; + if (src->retry_count && !src->uploaded) { + struct wsm_tx_rate_retry_policy *dst = + &arg.tbl[arg.num]; + dst->index = i; + dst->short_retries = priv->short_frame_max_tx_count; + dst->long_retries = priv->long_frame_max_tx_count; + + dst->flags = WSM_TX_RATE_POLICY_FLAG_TERMINATE_WHEN_FINISHED | + WSM_TX_RATE_POLICY_FLAG_COUNT_INITIAL_TRANSMIT; + memcpy(dst->rate_count_indices, src->tbl, + sizeof(dst->rate_count_indices)); + src->uploaded = 1; + ++arg.num; + } + } + spin_unlock_bh(&cache->lock); + cw1200_debug_tx_cache_miss(priv); + pr_debug("[TX policy] Upload %d policies\n", arg.num); + return wsm_set_tx_rate_retry_policy(priv, &arg); +} + +void tx_policy_upload_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, tx_policy_upload_work); + + pr_debug("[TX] TX policy upload.\n"); + tx_policy_upload(priv); + + wsm_unlock_tx(priv); + cw1200_tx_queues_unlock(priv); +} + +/* ******************************************************************** */ +/* cw1200 TX implementation */ + +struct cw1200_txinfo { + struct sk_buff *skb; + unsigned queue; + struct ieee80211_tx_info *tx_info; + const struct ieee80211_rate *rate; + struct ieee80211_hdr *hdr; + size_t hdrlen; + const u8 *da; + struct cw1200_sta_priv *sta_priv; + struct ieee80211_sta *sta; + struct cw1200_txpriv txpriv; +}; + +u32 cw1200_rate_mask_to_wsm(struct cw1200_common *priv, u32 rates) +{ + u32 ret = 0; + int i; + for (i = 0; i < 32; ++i) { + if (rates & BIT(i)) + ret |= BIT(priv->rates[i].hw_value); + } + return ret; +} + +static const struct ieee80211_rate * +cw1200_get_tx_rate(const struct cw1200_common *priv, + const struct ieee80211_tx_rate *rate) +{ + if (rate->idx < 0) + return NULL; + if (rate->flags & IEEE80211_TX_RC_MCS) + return &priv->mcs_rates[rate->idx]; + return &priv->hw->wiphy->bands[priv->channel->band]-> + bitrates[rate->idx]; +} + +static int +cw1200_tx_h_calc_link_ids(struct cw1200_common *priv, + struct cw1200_txinfo *t) +{ + if (t->sta && t->sta_priv->link_id) + t->txpriv.raw_link_id = + t->txpriv.link_id = + t->sta_priv->link_id; + else if (priv->mode != NL80211_IFTYPE_AP) + t->txpriv.raw_link_id = + t->txpriv.link_id = 0; + else if (is_multicast_ether_addr(t->da)) { + if (priv->enable_beacon) { + t->txpriv.raw_link_id = 0; + t->txpriv.link_id = CW1200_LINK_ID_AFTER_DTIM; + } else { + t->txpriv.raw_link_id = 0; + t->txpriv.link_id = 0; + } + } else { + t->txpriv.link_id = cw1200_find_link_id(priv, t->da); + if (!t->txpriv.link_id) + t->txpriv.link_id = cw1200_alloc_link_id(priv, t->da); + if (!t->txpriv.link_id) { + wiphy_err(priv->hw->wiphy, + "No more link IDs available.\n"); + return -ENOENT; + } + t->txpriv.raw_link_id = t->txpriv.link_id; + } + if (t->txpriv.raw_link_id) + priv->link_id_db[t->txpriv.raw_link_id - 1].timestamp = + jiffies; + if (t->sta && (t->sta->uapsd_queues & BIT(t->queue))) + t->txpriv.link_id = CW1200_LINK_ID_UAPSD; + return 0; +} + +static void +cw1200_tx_h_pm(struct cw1200_common *priv, + struct cw1200_txinfo *t) +{ + if (ieee80211_is_auth(t->hdr->frame_control)) { + u32 mask = ~BIT(t->txpriv.raw_link_id); + spin_lock_bh(&priv->ps_state_lock); + priv->sta_asleep_mask &= mask; + priv->pspoll_mask &= mask; + spin_unlock_bh(&priv->ps_state_lock); + } +} + +static void +cw1200_tx_h_calc_tid(struct cw1200_common *priv, + struct cw1200_txinfo *t) +{ + if (ieee80211_is_data_qos(t->hdr->frame_control)) { + u8 *qos = ieee80211_get_qos_ctl(t->hdr); + t->txpriv.tid = qos[0] & IEEE80211_QOS_CTL_TID_MASK; + } else if (ieee80211_is_data(t->hdr->frame_control)) { + t->txpriv.tid = 0; + } +} + +static int +cw1200_tx_h_crypt(struct cw1200_common *priv, + struct cw1200_txinfo *t) +{ + if (!t->tx_info->control.hw_key || + !ieee80211_has_protected(t->hdr->frame_control)) + return 0; + + t->hdrlen += t->tx_info->control.hw_key->iv_len; + skb_put(t->skb, t->tx_info->control.hw_key->icv_len); + + if (t->tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) + skb_put(t->skb, 8); /* MIC space */ + + return 0; +} + +static int +cw1200_tx_h_align(struct cw1200_common *priv, + struct cw1200_txinfo *t, + u8 *flags) +{ + size_t offset = (size_t)t->skb->data & 3; + + if (!offset) + return 0; + + if (offset & 1) { + wiphy_err(priv->hw->wiphy, + "Bug: attempt to transmit a frame with wrong alignment: %zu\n", + offset); + return -EINVAL; + } + + if (skb_headroom(t->skb) < offset) { + wiphy_err(priv->hw->wiphy, + "Bug: no space allocated for DMA alignment. headroom: %d\n", + skb_headroom(t->skb)); + return -ENOMEM; + } + skb_push(t->skb, offset); + t->hdrlen += offset; + t->txpriv.offset += offset; + *flags |= WSM_TX_2BYTES_SHIFT; + cw1200_debug_tx_align(priv); + return 0; +} + +static int +cw1200_tx_h_action(struct cw1200_common *priv, + struct cw1200_txinfo *t) +{ + struct ieee80211_mgmt *mgmt = + (struct ieee80211_mgmt *)t->hdr; + if (ieee80211_is_action(t->hdr->frame_control) && + mgmt->u.action.category == WLAN_CATEGORY_BACK) + return 1; + else + return 0; +} + +/* Add WSM header */ +static struct wsm_tx * +cw1200_tx_h_wsm(struct cw1200_common *priv, + struct cw1200_txinfo *t) +{ + struct wsm_tx *wsm; + + if (skb_headroom(t->skb) < sizeof(struct wsm_tx)) { + wiphy_err(priv->hw->wiphy, + "Bug: no space allocated for WSM header. headroom: %d\n", + skb_headroom(t->skb)); + return NULL; + } + + wsm = (struct wsm_tx *)skb_push(t->skb, sizeof(struct wsm_tx)); + t->txpriv.offset += sizeof(struct wsm_tx); + memset(wsm, 0, sizeof(*wsm)); + wsm->hdr.len = __cpu_to_le16(t->skb->len); + wsm->hdr.id = __cpu_to_le16(0x0004); + wsm->queue_id = wsm_queue_id_to_wsm(t->queue); + return wsm; +} + +/* BT Coex specific handling */ +static void +cw1200_tx_h_bt(struct cw1200_common *priv, + struct cw1200_txinfo *t, + struct wsm_tx *wsm) +{ + u8 priority = 0; + + if (!priv->bt_present) + return; + + if (ieee80211_is_nullfunc(t->hdr->frame_control)) { + priority = WSM_EPTA_PRIORITY_MGT; + } else if (ieee80211_is_data(t->hdr->frame_control)) { + /* Skip LLC SNAP header (+6) */ + u8 *payload = &t->skb->data[t->hdrlen]; + u16 *ethertype = (u16 *)&payload[6]; + if (*ethertype == __be16_to_cpu(ETH_P_PAE)) + priority = WSM_EPTA_PRIORITY_EAPOL; + } else if (ieee80211_is_assoc_req(t->hdr->frame_control) || + ieee80211_is_reassoc_req(t->hdr->frame_control)) { + struct ieee80211_mgmt *mgt_frame = + (struct ieee80211_mgmt *)t->hdr; + + if (mgt_frame->u.assoc_req.listen_interval < + priv->listen_interval) { + pr_debug("Modified Listen Interval to %d from %d\n", + priv->listen_interval, + mgt_frame->u.assoc_req.listen_interval); + /* Replace listen interval derieved from + * the one read from SDD */ + mgt_frame->u.assoc_req.listen_interval = + priv->listen_interval; + } + } + + if (!priority) { + if (ieee80211_is_action(t->hdr->frame_control)) + priority = WSM_EPTA_PRIORITY_ACTION; + else if (ieee80211_is_mgmt(t->hdr->frame_control)) + priority = WSM_EPTA_PRIORITY_MGT; + else if ((wsm->queue_id == WSM_QUEUE_VOICE)) + priority = WSM_EPTA_PRIORITY_VOICE; + else if ((wsm->queue_id == WSM_QUEUE_VIDEO)) + priority = WSM_EPTA_PRIORITY_VIDEO; + else + priority = WSM_EPTA_PRIORITY_DATA; + } + + pr_debug("[TX] EPTA priority %d.\n", priority); + + wsm->flags |= priority << 1; +} + +static int +cw1200_tx_h_rate_policy(struct cw1200_common *priv, + struct cw1200_txinfo *t, + struct wsm_tx *wsm) +{ + bool tx_policy_renew = false; + + t->txpriv.rate_id = tx_policy_get(priv, + t->tx_info->control.rates, IEEE80211_TX_MAX_RATES, + &tx_policy_renew); + if (t->txpriv.rate_id == CW1200_INVALID_RATE_ID) + return -EFAULT; + + wsm->flags |= t->txpriv.rate_id << 4; + + t->rate = cw1200_get_tx_rate(priv, + &t->tx_info->control.rates[0]), + wsm->max_tx_rate = t->rate->hw_value; + if (t->rate->flags & IEEE80211_TX_RC_MCS) { + if (cw1200_ht_greenfield(&priv->ht_info)) + wsm->ht_tx_parameters |= + __cpu_to_le32(WSM_HT_TX_GREENFIELD); + else + wsm->ht_tx_parameters |= + __cpu_to_le32(WSM_HT_TX_MIXED); + } + + if (tx_policy_renew) { + pr_debug("[TX] TX policy renew.\n"); + /* It's not so optimal to stop TX queues every now and then. + * Better to reimplement task scheduling with + * a counter. TODO. */ + wsm_lock_tx_async(priv); + cw1200_tx_queues_lock(priv); + if (queue_work(priv->workqueue, + &priv->tx_policy_upload_work) <= 0) { + cw1200_tx_queues_unlock(priv); + wsm_unlock_tx(priv); + } + } + return 0; +} + +static bool +cw1200_tx_h_pm_state(struct cw1200_common *priv, + struct cw1200_txinfo *t) +{ + int was_buffered = 1; + + if (t->txpriv.link_id == CW1200_LINK_ID_AFTER_DTIM && + !priv->buffered_multicasts) { + priv->buffered_multicasts = true; + if (priv->sta_asleep_mask) + queue_work(priv->workqueue, + &priv->multicast_start_work); + } + + if (t->txpriv.raw_link_id && t->txpriv.tid < CW1200_MAX_TID) + was_buffered = priv->link_id_db[t->txpriv.raw_link_id - 1].buffered[t->txpriv.tid]++; + + return !was_buffered; +} + +/* ******************************************************************** */ + +void cw1200_tx(struct ieee80211_hw *dev, + struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct cw1200_common *priv = dev->priv; + struct cw1200_txinfo t = { + .skb = skb, + .queue = skb_get_queue_mapping(skb), + .tx_info = IEEE80211_SKB_CB(skb), + .hdr = (struct ieee80211_hdr *)skb->data, + .txpriv.tid = CW1200_MAX_TID, + .txpriv.rate_id = CW1200_INVALID_RATE_ID, + }; + struct ieee80211_sta *sta; + struct wsm_tx *wsm; + bool tid_update = 0; + u8 flags = 0; + int ret; + + if (priv->bh_error) + goto drop; + + t.hdrlen = ieee80211_hdrlen(t.hdr->frame_control); + t.da = ieee80211_get_DA(t.hdr); + if (control) { + t.sta = control->sta; + t.sta_priv = (struct cw1200_sta_priv *)&t.sta->drv_priv; + } + + if (WARN_ON(t.queue >= 4)) + goto drop; + + ret = cw1200_tx_h_calc_link_ids(priv, &t); + if (ret) + goto drop; + + pr_debug("[TX] TX %d bytes (queue: %d, link_id: %d (%d)).\n", + skb->len, t.queue, t.txpriv.link_id, + t.txpriv.raw_link_id); + + cw1200_tx_h_pm(priv, &t); + cw1200_tx_h_calc_tid(priv, &t); + ret = cw1200_tx_h_crypt(priv, &t); + if (ret) + goto drop; + ret = cw1200_tx_h_align(priv, &t, &flags); + if (ret) + goto drop; + ret = cw1200_tx_h_action(priv, &t); + if (ret) + goto drop; + wsm = cw1200_tx_h_wsm(priv, &t); + if (!wsm) { + ret = -ENOMEM; + goto drop; + } + wsm->flags |= flags; + cw1200_tx_h_bt(priv, &t, wsm); + ret = cw1200_tx_h_rate_policy(priv, &t, wsm); + if (ret) + goto drop; + + rcu_read_lock(); + sta = rcu_dereference(t.sta); + + spin_lock_bh(&priv->ps_state_lock); + { + tid_update = cw1200_tx_h_pm_state(priv, &t); + BUG_ON(cw1200_queue_put(&priv->tx_queue[t.queue], + t.skb, &t.txpriv)); + } + spin_unlock_bh(&priv->ps_state_lock); + + if (tid_update && sta) + ieee80211_sta_set_buffered(sta, t.txpriv.tid, true); + + rcu_read_unlock(); + + cw1200_bh_wakeup(priv); + + return; + +drop: + cw1200_skb_dtor(priv, skb, &t.txpriv); + return; +} + +/* ******************************************************************** */ + +static int cw1200_handle_action_rx(struct cw1200_common *priv, + struct sk_buff *skb) +{ + struct ieee80211_mgmt *mgmt = (void *)skb->data; + + /* Filter block ACK negotiation: fully controlled by firmware */ + if (mgmt->u.action.category == WLAN_CATEGORY_BACK) + return 1; + + return 0; +} + +static int cw1200_handle_pspoll(struct cw1200_common *priv, + struct sk_buff *skb) +{ + struct ieee80211_sta *sta; + struct ieee80211_pspoll *pspoll = (struct ieee80211_pspoll *)skb->data; + int link_id = 0; + u32 pspoll_mask = 0; + int drop = 1; + int i; + + if (priv->join_status != CW1200_JOIN_STATUS_AP) + goto done; + if (memcmp(priv->vif->addr, pspoll->bssid, ETH_ALEN)) + goto done; + + rcu_read_lock(); + sta = ieee80211_find_sta(priv->vif, pspoll->ta); + if (sta) { + struct cw1200_sta_priv *sta_priv; + sta_priv = (struct cw1200_sta_priv *)&sta->drv_priv; + link_id = sta_priv->link_id; + pspoll_mask = BIT(sta_priv->link_id); + } + rcu_read_unlock(); + if (!link_id) + goto done; + + priv->pspoll_mask |= pspoll_mask; + drop = 0; + + /* Do not report pspols if data for given link id is + * queued already. */ + for (i = 0; i < 4; ++i) { + if (cw1200_queue_get_num_queued(&priv->tx_queue[i], + pspoll_mask)) { + cw1200_bh_wakeup(priv); + drop = 1; + break; + } + } + pr_debug("[RX] PSPOLL: %s\n", drop ? "local" : "fwd"); +done: + return drop; +} + +/* ******************************************************************** */ + +void cw1200_tx_confirm_cb(struct cw1200_common *priv, + int link_id, + struct wsm_tx_confirm *arg) +{ + u8 queue_id = cw1200_queue_get_queue_id(arg->packet_id); + struct cw1200_queue *queue = &priv->tx_queue[queue_id]; + struct sk_buff *skb; + const struct cw1200_txpriv *txpriv; + + pr_debug("[TX] TX confirm: %d, %d.\n", + arg->status, arg->ack_failures); + + if (cw1200_itp_tx_running(priv)) + return; + + if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) { + /* STA is stopped. */ + return; + } + + if (WARN_ON(queue_id >= 4)) + return; + + if (arg->status) + pr_debug("TX failed: %d.\n", arg->status); + + if ((arg->status == WSM_REQUEUE) && + (arg->flags & WSM_TX_STATUS_REQUEUE)) { + /* "Requeue" means "implicit suspend" */ + struct wsm_suspend_resume suspend = { + .link_id = link_id, + .stop = 1, + .multicast = !link_id, + }; + cw1200_suspend_resume(priv, &suspend); + wiphy_warn(priv->hw->wiphy, "Requeue for link_id %d (try %d). STAs asleep: 0x%.8X\n", + link_id, + cw1200_queue_get_generation(arg->packet_id) + 1, + priv->sta_asleep_mask); + cw1200_queue_requeue(queue, arg->packet_id); + spin_lock_bh(&priv->ps_state_lock); + if (!link_id) { + priv->buffered_multicasts = true; + if (priv->sta_asleep_mask) { + queue_work(priv->workqueue, + &priv->multicast_start_work); + } + } + spin_unlock_bh(&priv->ps_state_lock); + } else if (!cw1200_queue_get_skb(queue, arg->packet_id, + &skb, &txpriv)) { + struct ieee80211_tx_info *tx = IEEE80211_SKB_CB(skb); + int tx_count = arg->ack_failures; + u8 ht_flags = 0; + int i; + + if (cw1200_ht_greenfield(&priv->ht_info)) + ht_flags |= IEEE80211_TX_RC_GREEN_FIELD; + + spin_lock(&priv->bss_loss_lock); + if (priv->bss_loss_state && + arg->packet_id == priv->bss_loss_confirm_id) { + if (arg->status) { + /* Recovery failed */ + __cw1200_cqm_bssloss_sm(priv, 0, 0, 1); + } else { + /* Recovery succeeded */ + __cw1200_cqm_bssloss_sm(priv, 0, 1, 0); + } + } + spin_unlock(&priv->bss_loss_lock); + + if (!arg->status) { + tx->flags |= IEEE80211_TX_STAT_ACK; + ++tx_count; + cw1200_debug_txed(priv); + if (arg->flags & WSM_TX_STATUS_AGGREGATION) { + /* Do not report aggregation to mac80211: + * it confuses minstrel a lot. */ + /* tx->flags |= IEEE80211_TX_STAT_AMPDU; */ + cw1200_debug_txed_agg(priv); + } + } else { + if (tx_count) + ++tx_count; + } + + for (i = 0; i < IEEE80211_TX_MAX_RATES; ++i) { + if (tx->status.rates[i].count >= tx_count) { + tx->status.rates[i].count = tx_count; + break; + } + tx_count -= tx->status.rates[i].count; + if (tx->status.rates[i].flags & IEEE80211_TX_RC_MCS) + tx->status.rates[i].flags |= ht_flags; + } + + for (++i; i < IEEE80211_TX_MAX_RATES; ++i) { + tx->status.rates[i].count = 0; + tx->status.rates[i].idx = -1; + } + + /* Pull off any crypto trailers that we added on */ + if (tx->control.hw_key) { + skb_trim(skb, skb->len - tx->control.hw_key->icv_len); + if (tx->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) + skb_trim(skb, skb->len - 8); /* MIC space */ + } + cw1200_queue_remove(queue, arg->packet_id); + } + /* XXX TODO: Only wake if there are pending transmits.. */ + cw1200_bh_wakeup(priv); +} + +static void cw1200_notify_buffered_tx(struct cw1200_common *priv, + struct sk_buff *skb, int link_id, int tid) +{ + struct ieee80211_sta *sta; + struct ieee80211_hdr *hdr; + u8 *buffered; + u8 still_buffered = 0; + + if (link_id && tid < CW1200_MAX_TID) { + buffered = priv->link_id_db + [link_id - 1].buffered; + + spin_lock_bh(&priv->ps_state_lock); + if (!WARN_ON(!buffered[tid])) + still_buffered = --buffered[tid]; + spin_unlock_bh(&priv->ps_state_lock); + + if (!still_buffered && tid < CW1200_MAX_TID) { + hdr = (struct ieee80211_hdr *)skb->data; + rcu_read_lock(); + sta = ieee80211_find_sta(priv->vif, hdr->addr1); + if (sta) + ieee80211_sta_set_buffered(sta, tid, false); + rcu_read_unlock(); + } + } +} + +void cw1200_skb_dtor(struct cw1200_common *priv, + struct sk_buff *skb, + const struct cw1200_txpriv *txpriv) +{ + skb_pull(skb, txpriv->offset); + if (txpriv->rate_id != CW1200_INVALID_RATE_ID) { + cw1200_notify_buffered_tx(priv, skb, + txpriv->raw_link_id, txpriv->tid); + tx_policy_put(priv, txpriv->rate_id); + } + if (!cw1200_is_itp(priv)) + ieee80211_tx_status(priv->hw, skb); +} + +void cw1200_rx_cb(struct cw1200_common *priv, + struct wsm_rx *arg, + int link_id, + struct sk_buff **skb_p) +{ + struct sk_buff *skb = *skb_p; + struct ieee80211_rx_status *hdr = IEEE80211_SKB_RXCB(skb); + struct ieee80211_hdr *frame = (struct ieee80211_hdr *)skb->data; + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; + struct cw1200_link_entry *entry = NULL; + unsigned long grace_period; + + bool early_data = false; + bool p2p = priv->vif && priv->vif->p2p; + size_t hdrlen; + hdr->flag = 0; + + if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) { + /* STA is stopped. */ + goto drop; + } + + if (link_id && link_id <= CW1200_MAX_STA_IN_AP_MODE) { + entry = &priv->link_id_db[link_id - 1]; + if (entry->status == CW1200_LINK_SOFT && + ieee80211_is_data(frame->frame_control)) + early_data = true; + entry->timestamp = jiffies; + } else if (p2p && + ieee80211_is_action(frame->frame_control) && + (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) { + pr_debug("[RX] Going to MAP&RESET link ID\n"); + WARN_ON(work_pending(&priv->linkid_reset_work)); + memcpy(&priv->action_frame_sa[0], + ieee80211_get_SA(frame), ETH_ALEN); + priv->action_linkid = 0; + schedule_work(&priv->linkid_reset_work); + } + + if (link_id && p2p && + ieee80211_is_action(frame->frame_control) && + (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) { + /* Link ID already exists for the ACTION frame. + * Reset and Remap */ + WARN_ON(work_pending(&priv->linkid_reset_work)); + memcpy(&priv->action_frame_sa[0], + ieee80211_get_SA(frame), ETH_ALEN); + priv->action_linkid = link_id; + schedule_work(&priv->linkid_reset_work); + } + if (arg->status) { + if (arg->status == WSM_STATUS_MICFAILURE) { + pr_debug("[RX] MIC failure.\n"); + hdr->flag |= RX_FLAG_MMIC_ERROR; + } else if (arg->status == WSM_STATUS_NO_KEY_FOUND) { + pr_debug("[RX] No key found.\n"); + goto drop; + } else { + pr_debug("[RX] Receive failure: %d.\n", + arg->status); + goto drop; + } + } + + if (skb->len < sizeof(struct ieee80211_pspoll)) { + wiphy_warn(priv->hw->wiphy, "Mailformed SDU rx'ed. Size is lesser than IEEE header.\n"); + goto drop; + } + + if (ieee80211_is_pspoll(frame->frame_control)) + if (cw1200_handle_pspoll(priv, skb)) + goto drop; + + hdr->mactime = 0; /* Not supported by WSM */ + hdr->band = ((arg->channel_number & 0xff00) || + (arg->channel_number > 14)) ? + IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ; + hdr->freq = ieee80211_channel_to_frequency( + arg->channel_number, + hdr->band); + + if (arg->rx_rate >= 14) { + hdr->flag |= RX_FLAG_HT; + hdr->rate_idx = arg->rx_rate - 14; + } else if (arg->rx_rate >= 4) { + hdr->rate_idx = arg->rx_rate - 2; + } else { + hdr->rate_idx = arg->rx_rate; + } + + hdr->signal = (s8)arg->rcpi_rssi; + hdr->antenna = 0; + + hdrlen = ieee80211_hdrlen(frame->frame_control); + + if (WSM_RX_STATUS_ENCRYPTION(arg->flags)) { + size_t iv_len = 0, icv_len = 0; + + hdr->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED; + + /* Oops... There is no fast way to ask mac80211 about + * IV/ICV lengths. Even defineas are not exposed.*/ + switch (WSM_RX_STATUS_ENCRYPTION(arg->flags)) { + case WSM_RX_STATUS_WEP: + iv_len = 4 /* WEP_IV_LEN */; + icv_len = 4 /* WEP_ICV_LEN */; + break; + case WSM_RX_STATUS_TKIP: + iv_len = 8 /* TKIP_IV_LEN */; + icv_len = 4 /* TKIP_ICV_LEN */ + + 8 /*MICHAEL_MIC_LEN*/; + hdr->flag |= RX_FLAG_MMIC_STRIPPED; + break; + case WSM_RX_STATUS_AES: + iv_len = 8 /* CCMP_HDR_LEN */; + icv_len = 8 /* CCMP_MIC_LEN */; + break; + case WSM_RX_STATUS_WAPI: + iv_len = 18 /* WAPI_HDR_LEN */; + icv_len = 16 /* WAPI_MIC_LEN */; + break; + default: + pr_warn("Unknown encryption type %d\n", + WSM_RX_STATUS_ENCRYPTION(arg->flags)); + goto drop; + } + + /* Firmware strips ICV in case of MIC failure. */ + if (arg->status == WSM_STATUS_MICFAILURE) + icv_len = 0; + + if (skb->len < hdrlen + iv_len + icv_len) { + wiphy_warn(priv->hw->wiphy, "Malformed SDU rx'ed. Size is lesser than crypto headers.\n"); + goto drop; + } + + /* Remove IV, ICV and MIC */ + skb_trim(skb, skb->len - icv_len); + memmove(skb->data + iv_len, skb->data, hdrlen); + skb_pull(skb, iv_len); + } + + /* Remove TSF from the end of frame */ + if (arg->flags & WSM_RX_STATUS_TSF_INCLUDED) { + memcpy(&hdr->mactime, skb->data + skb->len - 8, 8); + hdr->mactime = le64_to_cpu(hdr->mactime); + if (skb->len >= 8) + skb_trim(skb, skb->len - 8); + } + + cw1200_debug_rxed(priv); + if (arg->flags & WSM_RX_STATUS_AGGREGATE) + cw1200_debug_rxed_agg(priv); + + if (ieee80211_is_action(frame->frame_control) && + (arg->flags & WSM_RX_STATUS_ADDRESS1)) { + if (cw1200_handle_action_rx(priv, skb)) + return; + } else if (ieee80211_is_beacon(frame->frame_control) && + !arg->status && + !memcmp(ieee80211_get_SA(frame), priv->vif->bss_conf.bssid, + ETH_ALEN)) { + const u8 *tim_ie; + u8 *ies = ((struct ieee80211_mgmt *) + (skb->data))->u.beacon.variable; + size_t ies_len = skb->len - (ies - (u8 *)(skb->data)); + + tim_ie = cfg80211_find_ie(WLAN_EID_TIM, ies, ies_len); + if (tim_ie) { + struct ieee80211_tim_ie *tim = + (struct ieee80211_tim_ie *)&tim_ie[2]; + + if (priv->join_dtim_period != tim->dtim_period) { + priv->join_dtim_period = tim->dtim_period; + queue_work(priv->workqueue, + &priv->set_beacon_wakeup_period_work); + } + } + + /* Disable beacon filter once we're associated... */ + if (priv->disable_beacon_filter && + (priv->vif->bss_conf.assoc || + priv->vif->bss_conf.ibss_joined)) { + priv->disable_beacon_filter = false; + queue_work(priv->workqueue, + &priv->update_filtering_work); + } + } + + /* Stay awake after frame is received to give + * userspace chance to react and acquire appropriate + * wakelock. */ + if (ieee80211_is_auth(frame->frame_control)) + grace_period = 5 * HZ; + else if (ieee80211_is_deauth(frame->frame_control)) + grace_period = 5 * HZ; + else + grace_period = 1 * HZ; + cw1200_pm_stay_awake(&priv->pm_state, grace_period); + + if (cw1200_itp_rxed(priv, skb)) { + consume_skb(skb); + } else if (early_data) { + spin_lock_bh(&priv->ps_state_lock); + /* Double-check status with lock held */ + if (entry->status == CW1200_LINK_SOFT) + skb_queue_tail(&entry->rx_queue, skb); + else + ieee80211_rx_irqsafe(priv->hw, skb); + spin_unlock_bh(&priv->ps_state_lock); + } else { + ieee80211_rx_irqsafe(priv->hw, skb); + } + *skb_p = NULL; + + return; + +drop: + /* TODO: update failure counters */ + return; +} + +/* ******************************************************************** */ +/* Security */ + +int cw1200_alloc_key(struct cw1200_common *priv) +{ + int idx; + + idx = ffs(~priv->key_map) - 1; + if (idx < 0 || idx > WSM_KEY_MAX_INDEX) + return -1; + + priv->key_map |= BIT(idx); + priv->keys[idx].index = idx; + return idx; +} + +void cw1200_free_key(struct cw1200_common *priv, int idx) +{ + BUG_ON(!(priv->key_map & BIT(idx))); + memset(&priv->keys[idx], 0, sizeof(priv->keys[idx])); + priv->key_map &= ~BIT(idx); +} + +void cw1200_free_keys(struct cw1200_common *priv) +{ + memset(&priv->keys, 0, sizeof(priv->keys)); + priv->key_map = 0; +} + +int cw1200_upload_keys(struct cw1200_common *priv) +{ + int idx, ret = 0; + for (idx = 0; idx <= WSM_KEY_MAX_INDEX; ++idx) + if (priv->key_map & BIT(idx)) { + ret = wsm_add_key(priv, &priv->keys[idx]); + if (ret < 0) + break; + } + return ret; +} + +/* Workaround for WFD test case 6.1.10 */ +void cw1200_link_id_reset(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, linkid_reset_work); + int temp_linkid; + + if (!priv->action_linkid) { + /* In GO mode we can receive ACTION frames without a linkID */ + temp_linkid = cw1200_alloc_link_id(priv, + &priv->action_frame_sa[0]); + WARN_ON(!temp_linkid); + if (temp_linkid) { + /* Make sure we execute the WQ */ + flush_workqueue(priv->workqueue); + /* Release the link ID */ + spin_lock_bh(&priv->ps_state_lock); + priv->link_id_db[temp_linkid - 1].prev_status = + priv->link_id_db[temp_linkid - 1].status; + priv->link_id_db[temp_linkid - 1].status = + CW1200_LINK_RESET; + spin_unlock_bh(&priv->ps_state_lock); + wsm_lock_tx_async(priv); + if (queue_work(priv->workqueue, + &priv->link_id_work) <= 0) + wsm_unlock_tx(priv); + } + } else { + spin_lock_bh(&priv->ps_state_lock); + priv->link_id_db[priv->action_linkid - 1].prev_status = + priv->link_id_db[priv->action_linkid - 1].status; + priv->link_id_db[priv->action_linkid - 1].status = + CW1200_LINK_RESET_REMAP; + spin_unlock_bh(&priv->ps_state_lock); + wsm_lock_tx_async(priv); + if (queue_work(priv->workqueue, &priv->link_id_work) <= 0) + wsm_unlock_tx(priv); + flush_workqueue(priv->workqueue); + } +} + +int cw1200_find_link_id(struct cw1200_common *priv, const u8 *mac) +{ + int i, ret = 0; + spin_lock_bh(&priv->ps_state_lock); + for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) { + if (!memcmp(mac, priv->link_id_db[i].mac, ETH_ALEN) && + priv->link_id_db[i].status) { + priv->link_id_db[i].timestamp = jiffies; + ret = i + 1; + break; + } + } + spin_unlock_bh(&priv->ps_state_lock); + return ret; +} + +int cw1200_alloc_link_id(struct cw1200_common *priv, const u8 *mac) +{ + int i, ret = 0; + unsigned long max_inactivity = 0; + unsigned long now = jiffies; + + spin_lock_bh(&priv->ps_state_lock); + for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) { + if (!priv->link_id_db[i].status) { + ret = i + 1; + break; + } else if (priv->link_id_db[i].status != CW1200_LINK_HARD && + !priv->tx_queue_stats.link_map_cache[i + 1]) { + unsigned long inactivity = + now - priv->link_id_db[i].timestamp; + if (inactivity < max_inactivity) + continue; + max_inactivity = inactivity; + ret = i + 1; + } + } + if (ret) { + struct cw1200_link_entry *entry = &priv->link_id_db[ret - 1]; + pr_debug("[AP] STA added, link_id: %d\n", ret); + entry->status = CW1200_LINK_RESERVE; + memcpy(&entry->mac, mac, ETH_ALEN); + memset(&entry->buffered, 0, CW1200_MAX_TID); + skb_queue_head_init(&entry->rx_queue); + wsm_lock_tx_async(priv); + if (queue_work(priv->workqueue, &priv->link_id_work) <= 0) + wsm_unlock_tx(priv); + } else { + wiphy_info(priv->hw->wiphy, + "[AP] Early: no more link IDs available.\n"); + } + + spin_unlock_bh(&priv->ps_state_lock); + return ret; +} + +void cw1200_link_id_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, link_id_work); + wsm_flush_tx(priv); + cw1200_link_id_gc_work(&priv->link_id_gc_work.work); + wsm_unlock_tx(priv); +} + +void cw1200_link_id_gc_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, link_id_gc_work.work); + struct wsm_reset reset = { + .reset_statistics = false, + }; + struct wsm_map_link map_link = { + .link_id = 0, + }; + unsigned long now = jiffies; + unsigned long next_gc = -1; + long ttl; + bool need_reset; + u32 mask; + int i; + + if (priv->join_status != CW1200_JOIN_STATUS_AP) + return; + + wsm_lock_tx(priv); + spin_lock_bh(&priv->ps_state_lock); + for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) { + need_reset = false; + mask = BIT(i + 1); + if (priv->link_id_db[i].status == CW1200_LINK_RESERVE || + (priv->link_id_db[i].status == CW1200_LINK_HARD && + !(priv->link_id_map & mask))) { + if (priv->link_id_map & mask) { + priv->sta_asleep_mask &= ~mask; + priv->pspoll_mask &= ~mask; + need_reset = true; + } + priv->link_id_map |= mask; + if (priv->link_id_db[i].status != CW1200_LINK_HARD) + priv->link_id_db[i].status = CW1200_LINK_SOFT; + memcpy(map_link.mac_addr, priv->link_id_db[i].mac, + ETH_ALEN); + spin_unlock_bh(&priv->ps_state_lock); + if (need_reset) { + reset.link_id = i + 1; + wsm_reset(priv, &reset); + } + map_link.link_id = i + 1; + wsm_map_link(priv, &map_link); + next_gc = min(next_gc, CW1200_LINK_ID_GC_TIMEOUT); + spin_lock_bh(&priv->ps_state_lock); + } else if (priv->link_id_db[i].status == CW1200_LINK_SOFT) { + ttl = priv->link_id_db[i].timestamp - now + + CW1200_LINK_ID_GC_TIMEOUT; + if (ttl <= 0) { + need_reset = true; + priv->link_id_db[i].status = CW1200_LINK_OFF; + priv->link_id_map &= ~mask; + priv->sta_asleep_mask &= ~mask; + priv->pspoll_mask &= ~mask; + memset(map_link.mac_addr, 0, ETH_ALEN); + spin_unlock_bh(&priv->ps_state_lock); + reset.link_id = i + 1; + wsm_reset(priv, &reset); + spin_lock_bh(&priv->ps_state_lock); + } else { + next_gc = min_t(unsigned long, next_gc, ttl); + } + } else if (priv->link_id_db[i].status == CW1200_LINK_RESET || + priv->link_id_db[i].status == + CW1200_LINK_RESET_REMAP) { + int status = priv->link_id_db[i].status; + priv->link_id_db[i].status = + priv->link_id_db[i].prev_status; + priv->link_id_db[i].timestamp = now; + reset.link_id = i + 1; + spin_unlock_bh(&priv->ps_state_lock); + wsm_reset(priv, &reset); + if (status == CW1200_LINK_RESET_REMAP) { + memcpy(map_link.mac_addr, + priv->link_id_db[i].mac, + ETH_ALEN); + map_link.link_id = i + 1; + wsm_map_link(priv, &map_link); + next_gc = min(next_gc, + CW1200_LINK_ID_GC_TIMEOUT); + } + spin_lock_bh(&priv->ps_state_lock); + } + if (need_reset) { + skb_queue_purge(&priv->link_id_db[i].rx_queue); + pr_debug("[AP] STA removed, link_id: %d\n", + reset.link_id); + } + } + spin_unlock_bh(&priv->ps_state_lock); + if (next_gc != -1) + queue_delayed_work(priv->workqueue, + &priv->link_id_gc_work, next_gc); + wsm_unlock_tx(priv); +} diff --git a/drivers/net/wireless/cw1200/txrx.h b/drivers/net/wireless/cw1200/txrx.h new file mode 100644 index 000000000000..492a4e14213b --- /dev/null +++ b/drivers/net/wireless/cw1200/txrx.h @@ -0,0 +1,106 @@ +/* + * Datapath interface for ST-Ericsson CW1200 mac80211 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef CW1200_TXRX_H +#define CW1200_TXRX_H + +#include + +/* extern */ struct ieee80211_hw; +/* extern */ struct sk_buff; +/* extern */ struct wsm_tx; +/* extern */ struct wsm_rx; +/* extern */ struct wsm_tx_confirm; +/* extern */ struct cw1200_txpriv; + +struct tx_policy { + union { + __le32 tbl[3]; + u8 raw[12]; + }; + u8 defined; + u8 usage_count; + u8 retry_count; + u8 uploaded; +}; + +struct tx_policy_cache_entry { + struct tx_policy policy; + struct list_head link; +}; + +#define TX_POLICY_CACHE_SIZE (8) +struct tx_policy_cache { + struct tx_policy_cache_entry cache[TX_POLICY_CACHE_SIZE]; + struct list_head used; + struct list_head free; + spinlock_t lock; /* Protect policy cache */ +}; + +/* ******************************************************************** */ +/* TX policy cache */ +/* Intention of TX policy cache is an overcomplicated WSM API. + * Device does not accept per-PDU tx retry sequence. + * It uses "tx retry policy id" instead, so driver code has to sync + * linux tx retry sequences with a retry policy table in the device. + */ +void tx_policy_init(struct cw1200_common *priv); +void tx_policy_upload_work(struct work_struct *work); +void tx_policy_clean(struct cw1200_common *priv); + +/* ******************************************************************** */ +/* TX implementation */ + +u32 cw1200_rate_mask_to_wsm(struct cw1200_common *priv, + u32 rates); +void cw1200_tx(struct ieee80211_hw *dev, + struct ieee80211_tx_control *control, + struct sk_buff *skb); +void cw1200_skb_dtor(struct cw1200_common *priv, + struct sk_buff *skb, + const struct cw1200_txpriv *txpriv); + +/* ******************************************************************** */ +/* WSM callbacks */ + +void cw1200_tx_confirm_cb(struct cw1200_common *priv, + int link_id, + struct wsm_tx_confirm *arg); +void cw1200_rx_cb(struct cw1200_common *priv, + struct wsm_rx *arg, + int link_id, + struct sk_buff **skb_p); + +/* ******************************************************************** */ +/* Timeout */ + +void cw1200_tx_timeout(struct work_struct *work); + +/* ******************************************************************** */ +/* Security */ +int cw1200_alloc_key(struct cw1200_common *priv); +void cw1200_free_key(struct cw1200_common *priv, int idx); +void cw1200_free_keys(struct cw1200_common *priv); +int cw1200_upload_keys(struct cw1200_common *priv); + +/* ******************************************************************** */ +/* Workaround for WFD test case 6.1.10 */ +void cw1200_link_id_reset(struct work_struct *work); + +#define CW1200_LINK_ID_GC_TIMEOUT ((unsigned long)(10 * HZ)) + +int cw1200_find_link_id(struct cw1200_common *priv, const u8 *mac); +int cw1200_alloc_link_id(struct cw1200_common *priv, const u8 *mac); +void cw1200_link_id_work(struct work_struct *work); +void cw1200_link_id_gc_work(struct work_struct *work); + + +#endif /* CW1200_TXRX_H */ diff --git a/drivers/net/wireless/cw1200/wsm.c b/drivers/net/wireless/cw1200/wsm.c new file mode 100644 index 000000000000..4db6cc16879c --- /dev/null +++ b/drivers/net/wireless/cw1200/wsm.c @@ -0,0 +1,1884 @@ +/* + * WSM host interface (HI) implementation for + * ST-Ericsson CW1200 mac80211 drivers. + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * 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 +#include +#include +#include +#include +#include + +#include "cw1200.h" +#include "wsm.h" +#include "bh.h" +#include "sta.h" +#include "debug.h" +#include "itp.h" + +#define WSM_CMD_TIMEOUT (2 * HZ) /* With respect to interrupt loss */ +#define WSM_CMD_START_TIMEOUT (7 * HZ) +#define WSM_CMD_RESET_TIMEOUT (3 * HZ) /* 2 sec. timeout was observed. */ +#define WSM_CMD_MAX_TIMEOUT (3 * HZ) + +#define WSM_SKIP(buf, size) \ + do { \ + if ((buf)->data + size > (buf)->end) \ + goto underflow; \ + (buf)->data += size; \ + } while (0) + +#define WSM_GET(buf, ptr, size) \ + do { \ + if ((buf)->data + size > (buf)->end) \ + goto underflow; \ + memcpy(ptr, (buf)->data, size); \ + (buf)->data += size; \ + } while (0) + +#define __WSM_GET(buf, type, cvt) \ + ({ \ + type val; \ + if ((buf)->data + sizeof(type) > (buf)->end) \ + goto underflow; \ + val = cvt(*(type *)(buf)->data); \ + (buf)->data += sizeof(type); \ + val; \ + }) + +#define WSM_GET8(buf) __WSM_GET(buf, u8, (u8)) +#define WSM_GET16(buf) __WSM_GET(buf, u16, __le16_to_cpu) +#define WSM_GET32(buf) __WSM_GET(buf, u32, __le32_to_cpu) + +#define WSM_PUT(buf, ptr, size) \ + do { \ + if ((buf)->data + size > (buf)->end) \ + if (wsm_buf_reserve((buf), size)) \ + goto nomem; \ + memcpy((buf)->data, ptr, size); \ + (buf)->data += size; \ + } while (0) + +#define __WSM_PUT(buf, val, type, cvt) \ + do { \ + if ((buf)->data + sizeof(type) > (buf)->end) \ + if (wsm_buf_reserve((buf), sizeof(type))) \ + goto nomem; \ + *(type *)(buf)->data = cvt(val); \ + (buf)->data += sizeof(type); \ + } while (0) + +#define WSM_PUT8(buf, val) __WSM_PUT(buf, val, u8, (u8)) +#define WSM_PUT16(buf, val) __WSM_PUT(buf, val, u16, __cpu_to_le16) +#define WSM_PUT32(buf, val) __WSM_PUT(buf, val, u32, __cpu_to_le32) + +static void wsm_buf_reset(struct wsm_buf *buf); +static int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size); + +static int wsm_cmd_send(struct cw1200_common *priv, + struct wsm_buf *buf, + void *arg, u16 cmd, long tmo); + +#define wsm_cmd_lock(__priv) mutex_lock(&((__priv)->wsm_cmd_mux)) +#define wsm_cmd_unlock(__priv) mutex_unlock(&((__priv)->wsm_cmd_mux)) + +/* ******************************************************************** */ +/* WSM API implementation */ + +static int wsm_generic_confirm(struct cw1200_common *priv, + void *arg, + struct wsm_buf *buf) +{ + u32 status = WSM_GET32(buf); + if (status != WSM_STATUS_SUCCESS) + return -EINVAL; + return 0; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +int wsm_configuration(struct cw1200_common *priv, struct wsm_configuration *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT32(buf, arg->dot11MaxTransmitMsduLifeTime); + WSM_PUT32(buf, arg->dot11MaxReceiveLifeTime); + WSM_PUT32(buf, arg->dot11RtsThreshold); + + /* DPD block. */ + WSM_PUT16(buf, arg->dpdData_size + 12); + WSM_PUT16(buf, 1); /* DPD version */ + WSM_PUT(buf, arg->dot11StationId, ETH_ALEN); + WSM_PUT16(buf, 5); /* DPD flags */ + WSM_PUT(buf, arg->dpdData, arg->dpdData_size); + + ret = wsm_cmd_send(priv, buf, arg, + WSM_CONFIGURATION_REQ_ID, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +static int wsm_configuration_confirm(struct cw1200_common *priv, + struct wsm_configuration *arg, + struct wsm_buf *buf) +{ + int i; + int status; + + status = WSM_GET32(buf); + if (WARN_ON(status != WSM_STATUS_SUCCESS)) + return -EINVAL; + + WSM_GET(buf, arg->dot11StationId, ETH_ALEN); + arg->dot11FrequencyBandsSupported = WSM_GET8(buf); + WSM_SKIP(buf, 1); + arg->supportedRateMask = WSM_GET32(buf); + for (i = 0; i < 2; ++i) { + arg->txPowerRange[i].min_power_level = WSM_GET32(buf); + arg->txPowerRange[i].max_power_level = WSM_GET32(buf); + arg->txPowerRange[i].stepping = WSM_GET32(buf); + } + return 0; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +/* ******************************************************************** */ + +int wsm_reset(struct cw1200_common *priv, const struct wsm_reset *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + u16 cmd = WSM_RESET_REQ_ID | WSM_TX_LINK_ID(arg->link_id); + + wsm_cmd_lock(priv); + + WSM_PUT32(buf, arg->reset_statistics ? 0 : 1); + ret = wsm_cmd_send(priv, buf, NULL, cmd, WSM_CMD_RESET_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +struct wsm_mib { + u16 mib_id; + void *buf; + size_t buf_size; +}; + +int wsm_read_mib(struct cw1200_common *priv, u16 mib_id, void *_buf, + size_t buf_size) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + struct wsm_mib mib_buf = { + .mib_id = mib_id, + .buf = _buf, + .buf_size = buf_size, + }; + wsm_cmd_lock(priv); + + WSM_PUT16(buf, mib_id); + WSM_PUT16(buf, 0); + + ret = wsm_cmd_send(priv, buf, &mib_buf, + WSM_READ_MIB_REQ_ID, WSM_CMD_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +static int wsm_read_mib_confirm(struct cw1200_common *priv, + struct wsm_mib *arg, + struct wsm_buf *buf) +{ + u16 size; + if (WARN_ON(WSM_GET32(buf) != WSM_STATUS_SUCCESS)) + return -EINVAL; + + if (WARN_ON(WSM_GET16(buf) != arg->mib_id)) + return -EINVAL; + + size = WSM_GET16(buf); + if (size > arg->buf_size) + size = arg->buf_size; + + WSM_GET(buf, arg->buf, size); + arg->buf_size = size; + return 0; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +/* ******************************************************************** */ + +int wsm_write_mib(struct cw1200_common *priv, u16 mib_id, void *_buf, + size_t buf_size) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + struct wsm_mib mib_buf = { + .mib_id = mib_id, + .buf = _buf, + .buf_size = buf_size, + }; + + wsm_cmd_lock(priv); + + WSM_PUT16(buf, mib_id); + WSM_PUT16(buf, buf_size); + WSM_PUT(buf, _buf, buf_size); + + ret = wsm_cmd_send(priv, buf, &mib_buf, + WSM_WRITE_MIB_REQ_ID, WSM_CMD_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +static int wsm_write_mib_confirm(struct cw1200_common *priv, + struct wsm_mib *arg, + struct wsm_buf *buf) +{ + int ret; + + ret = wsm_generic_confirm(priv, arg, buf); + if (ret) + return ret; + + if (arg->mib_id == WSM_MIB_ID_OPERATIONAL_POWER_MODE) { + /* OperationalMode: update PM status. */ + const char *p = arg->buf; + cw1200_enable_powersave(priv, (p[0] & 0x0F) ? true : false); + } + return 0; +} + +/* ******************************************************************** */ + +int wsm_scan(struct cw1200_common *priv, const struct wsm_scan *arg) +{ + int i; + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + if (arg->num_channels > 48) + return -EINVAL; + + if (arg->num_ssids > 2) + return -EINVAL; + + if (arg->band > 1) + return -EINVAL; + + wsm_cmd_lock(priv); + + WSM_PUT8(buf, arg->band); + WSM_PUT8(buf, arg->type); + WSM_PUT8(buf, arg->flags); + WSM_PUT8(buf, arg->max_tx_rate); + WSM_PUT32(buf, arg->auto_scan_interval); + WSM_PUT8(buf, arg->num_probes); + WSM_PUT8(buf, arg->num_channels); + WSM_PUT8(buf, arg->num_ssids); + WSM_PUT8(buf, arg->probe_delay); + + for (i = 0; i < arg->num_channels; ++i) { + WSM_PUT16(buf, arg->ch[i].number); + WSM_PUT16(buf, 0); + WSM_PUT32(buf, arg->ch[i].min_chan_time); + WSM_PUT32(buf, arg->ch[i].max_chan_time); + WSM_PUT32(buf, 0); + } + + for (i = 0; i < arg->num_ssids; ++i) { + WSM_PUT32(buf, arg->ssids[i].length); + WSM_PUT(buf, &arg->ssids[i].ssid[0], + sizeof(arg->ssids[i].ssid)); + } + + ret = wsm_cmd_send(priv, buf, NULL, + WSM_START_SCAN_REQ_ID, WSM_CMD_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_stop_scan(struct cw1200_common *priv) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + wsm_cmd_lock(priv); + ret = wsm_cmd_send(priv, buf, NULL, + WSM_STOP_SCAN_REQ_ID, WSM_CMD_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; +} + + +static int wsm_tx_confirm(struct cw1200_common *priv, + struct wsm_buf *buf, + int link_id) +{ + struct wsm_tx_confirm tx_confirm; + + tx_confirm.packet_id = WSM_GET32(buf); + tx_confirm.status = WSM_GET32(buf); + tx_confirm.tx_rate = WSM_GET8(buf); + tx_confirm.ack_failures = WSM_GET8(buf); + tx_confirm.flags = WSM_GET16(buf); + tx_confirm.media_delay = WSM_GET32(buf); + tx_confirm.tx_queue_delay = WSM_GET32(buf); + + cw1200_tx_confirm_cb(priv, link_id, &tx_confirm); + return 0; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +static int wsm_multi_tx_confirm(struct cw1200_common *priv, + struct wsm_buf *buf, int link_id) +{ + int ret; + int count; + int i; + + count = WSM_GET32(buf); + if (WARN_ON(count <= 0)) + return -EINVAL; + + if (count > 1) { + /* We already released one buffer, now for the rest */ + ret = wsm_release_tx_buffer(priv, count - 1); + if (ret < 0) + return ret; + else if (ret > 0) + cw1200_bh_wakeup(priv); + } + + cw1200_debug_txed_multi(priv, count); + for (i = 0; i < count; ++i) { + ret = wsm_tx_confirm(priv, buf, link_id); + if (ret) + return ret; + } + return ret; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +/* ******************************************************************** */ + +static int wsm_join_confirm(struct cw1200_common *priv, + struct wsm_join_cnf *arg, + struct wsm_buf *buf) +{ + arg->status = WSM_GET32(buf); + if (WARN_ON(arg->status) != WSM_STATUS_SUCCESS) + return -EINVAL; + + arg->min_power_level = WSM_GET32(buf); + arg->max_power_level = WSM_GET32(buf); + + return 0; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +int wsm_join(struct cw1200_common *priv, struct wsm_join *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + struct wsm_join_cnf resp; + wsm_cmd_lock(priv); + + WSM_PUT8(buf, arg->mode); + WSM_PUT8(buf, arg->band); + WSM_PUT16(buf, arg->channel_number); + WSM_PUT(buf, &arg->bssid[0], sizeof(arg->bssid)); + WSM_PUT16(buf, arg->atim_window); + WSM_PUT8(buf, arg->preamble_type); + WSM_PUT8(buf, arg->probe_for_join); + WSM_PUT8(buf, arg->dtim_period); + WSM_PUT8(buf, arg->flags); + WSM_PUT32(buf, arg->ssid_len); + WSM_PUT(buf, &arg->ssid[0], sizeof(arg->ssid)); + WSM_PUT32(buf, arg->beacon_interval); + WSM_PUT32(buf, arg->basic_rate_set); + + priv->tx_burst_idx = -1; + ret = wsm_cmd_send(priv, buf, &resp, + WSM_JOIN_REQ_ID, WSM_CMD_TIMEOUT); + /* TODO: Update state based on resp.min|max_power_level */ + + priv->join_complete_status = resp.status; + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_set_bss_params(struct cw1200_common *priv, + const struct wsm_set_bss_params *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT8(buf, (arg->reset_beacon_loss ? 0x1 : 0)); + WSM_PUT8(buf, arg->beacon_lost_count); + WSM_PUT16(buf, arg->aid); + WSM_PUT32(buf, arg->operational_rate_set); + + ret = wsm_cmd_send(priv, buf, NULL, + WSM_SET_BSS_PARAMS_REQ_ID, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_add_key(struct cw1200_common *priv, const struct wsm_add_key *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT(buf, arg, sizeof(*arg)); + + ret = wsm_cmd_send(priv, buf, NULL, + WSM_ADD_KEY_REQ_ID, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_remove_key(struct cw1200_common *priv, const struct wsm_remove_key *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT8(buf, arg->index); + WSM_PUT8(buf, 0); + WSM_PUT16(buf, 0); + + ret = wsm_cmd_send(priv, buf, NULL, + WSM_REMOVE_KEY_REQ_ID, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_set_tx_queue_params(struct cw1200_common *priv, + const struct wsm_set_tx_queue_params *arg, u8 id) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + u8 queue_id_to_wmm_aci[] = {3, 2, 0, 1}; + + wsm_cmd_lock(priv); + + WSM_PUT8(buf, queue_id_to_wmm_aci[id]); + WSM_PUT8(buf, 0); + WSM_PUT8(buf, arg->ackPolicy); + WSM_PUT8(buf, 0); + WSM_PUT32(buf, arg->maxTransmitLifetime); + WSM_PUT16(buf, arg->allowedMediumTime); + WSM_PUT16(buf, 0); + + ret = wsm_cmd_send(priv, buf, NULL, 0x0012, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_set_edca_params(struct cw1200_common *priv, + const struct wsm_edca_params *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + /* Implemented according to specification. */ + + WSM_PUT16(buf, arg->params[3].cwmin); + WSM_PUT16(buf, arg->params[2].cwmin); + WSM_PUT16(buf, arg->params[1].cwmin); + WSM_PUT16(buf, arg->params[0].cwmin); + + WSM_PUT16(buf, arg->params[3].cwmax); + WSM_PUT16(buf, arg->params[2].cwmax); + WSM_PUT16(buf, arg->params[1].cwmax); + WSM_PUT16(buf, arg->params[0].cwmax); + + WSM_PUT8(buf, arg->params[3].aifns); + WSM_PUT8(buf, arg->params[2].aifns); + WSM_PUT8(buf, arg->params[1].aifns); + WSM_PUT8(buf, arg->params[0].aifns); + + WSM_PUT16(buf, arg->params[3].txop_limit); + WSM_PUT16(buf, arg->params[2].txop_limit); + WSM_PUT16(buf, arg->params[1].txop_limit); + WSM_PUT16(buf, arg->params[0].txop_limit); + + WSM_PUT32(buf, arg->params[3].max_rx_lifetime); + WSM_PUT32(buf, arg->params[2].max_rx_lifetime); + WSM_PUT32(buf, arg->params[1].max_rx_lifetime); + WSM_PUT32(buf, arg->params[0].max_rx_lifetime); + + ret = wsm_cmd_send(priv, buf, NULL, + WSM_EDCA_PARAMS_REQ_ID, WSM_CMD_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_switch_channel(struct cw1200_common *priv, + const struct wsm_switch_channel *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT8(buf, arg->mode); + WSM_PUT8(buf, arg->switch_count); + WSM_PUT16(buf, arg->channel_number); + + priv->channel_switch_in_progress = 1; + + ret = wsm_cmd_send(priv, buf, NULL, + WSM_SWITCH_CHANNEL_REQ_ID, WSM_CMD_TIMEOUT); + if (ret) + priv->channel_switch_in_progress = 0; + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + priv->ps_mode_switch_in_progress = 1; + + wsm_cmd_lock(priv); + + WSM_PUT8(buf, arg->mode); + WSM_PUT8(buf, arg->fast_psm_idle_period); + WSM_PUT8(buf, arg->ap_psm_change_period); + WSM_PUT8(buf, arg->min_auto_pspoll_period); + + ret = wsm_cmd_send(priv, buf, NULL, + WSM_SET_PM_REQ_ID, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_start(struct cw1200_common *priv, const struct wsm_start *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT8(buf, arg->mode); + WSM_PUT8(buf, arg->band); + WSM_PUT16(buf, arg->channel_number); + WSM_PUT32(buf, arg->ct_window); + WSM_PUT32(buf, arg->beacon_interval); + WSM_PUT8(buf, arg->dtim_period); + WSM_PUT8(buf, arg->preamble); + WSM_PUT8(buf, arg->probe_delay); + WSM_PUT8(buf, arg->ssid_len); + WSM_PUT(buf, arg->ssid, sizeof(arg->ssid)); + WSM_PUT32(buf, arg->basic_rate_set); + + priv->tx_burst_idx = -1; + ret = wsm_cmd_send(priv, buf, NULL, + WSM_START_REQ_ID, WSM_CMD_START_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_beacon_transmit(struct cw1200_common *priv, + const struct wsm_beacon_transmit *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT32(buf, arg->enable_beaconing ? 1 : 0); + + ret = wsm_cmd_send(priv, buf, NULL, + WSM_BEACON_TRANSMIT_REQ_ID, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_start_find(struct cw1200_common *priv) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + ret = wsm_cmd_send(priv, buf, NULL, 0x0019, WSM_CMD_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; +} + +/* ******************************************************************** */ + +int wsm_stop_find(struct cw1200_common *priv) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + ret = wsm_cmd_send(priv, buf, NULL, 0x001A, WSM_CMD_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; +} + +/* ******************************************************************** */ + +int wsm_map_link(struct cw1200_common *priv, const struct wsm_map_link *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + u16 cmd = 0x001C | WSM_TX_LINK_ID(arg->link_id); + + wsm_cmd_lock(priv); + + WSM_PUT(buf, &arg->mac_addr[0], sizeof(arg->mac_addr)); + WSM_PUT16(buf, 0); + + ret = wsm_cmd_send(priv, buf, NULL, cmd, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_update_ie(struct cw1200_common *priv, + const struct wsm_update_ie *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT16(buf, arg->what); + WSM_PUT16(buf, arg->count); + WSM_PUT(buf, arg->ies, arg->length); + + ret = wsm_cmd_send(priv, buf, NULL, 0x001B, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ +int wsm_set_probe_responder(struct cw1200_common *priv, bool enable) +{ + priv->rx_filter.probeResponder = enable; + return wsm_set_rx_filter(priv, &priv->rx_filter); +} + +/* ******************************************************************** */ +/* WSM indication events implementation */ +const char * const cw1200_fw_types[] = { + "ETF", + "WFM", + "WSM", + "HI test", + "Platform test" +}; + +static int wsm_startup_indication(struct cw1200_common *priv, + struct wsm_buf *buf) +{ + priv->wsm_caps.input_buffers = WSM_GET16(buf); + priv->wsm_caps.input_buffer_size = WSM_GET16(buf); + priv->wsm_caps.hw_id = WSM_GET16(buf); + priv->wsm_caps.hw_subid = WSM_GET16(buf); + priv->wsm_caps.status = WSM_GET16(buf); + priv->wsm_caps.fw_cap = WSM_GET16(buf); + priv->wsm_caps.fw_type = WSM_GET16(buf); + priv->wsm_caps.fw_api = WSM_GET16(buf); + priv->wsm_caps.fw_build = WSM_GET16(buf); + priv->wsm_caps.fw_ver = WSM_GET16(buf); + WSM_GET(buf, priv->wsm_caps.fw_label, sizeof(priv->wsm_caps.fw_label)); + priv->wsm_caps.fw_label[sizeof(priv->wsm_caps.fw_label) - 1] = 0; /* Do not trust FW too much... */ + + if (WARN_ON(priv->wsm_caps.status)) + return -EINVAL; + + if (WARN_ON(priv->wsm_caps.fw_type > 4)) + return -EINVAL; + + pr_info("CW1200 WSM init done.\n" + " Input buffers: %d x %d bytes\n" + " Hardware: %d.%d\n" + " %s firmware [%s], ver: %d, build: %d," + " api: %d, cap: 0x%.4X\n", + priv->wsm_caps.input_buffers, + priv->wsm_caps.input_buffer_size, + priv->wsm_caps.hw_id, priv->wsm_caps.hw_subid, + cw1200_fw_types[priv->wsm_caps.fw_type], + priv->wsm_caps.fw_label, priv->wsm_caps.fw_ver, + priv->wsm_caps.fw_build, + priv->wsm_caps.fw_api, priv->wsm_caps.fw_cap); + + /* Disable unsupported frequency bands */ + if (!(priv->wsm_caps.fw_cap & 0x1)) + priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = NULL; + if (!(priv->wsm_caps.fw_cap & 0x2)) + priv->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = NULL; + + priv->firmware_ready = 1; + wake_up(&priv->wsm_startup_done); + return 0; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +static int wsm_receive_indication(struct cw1200_common *priv, + int link_id, + struct wsm_buf *buf, + struct sk_buff **skb_p) +{ + struct wsm_rx rx; + struct ieee80211_hdr *hdr; + size_t hdr_len; + __le16 fctl; + + rx.status = WSM_GET32(buf); + rx.channel_number = WSM_GET16(buf); + rx.rx_rate = WSM_GET8(buf); + rx.rcpi_rssi = WSM_GET8(buf); + rx.flags = WSM_GET32(buf); + + /* FW Workaround: Drop probe resp or + beacon when RSSI is 0 + */ + hdr = (struct ieee80211_hdr *)(*skb_p)->data; + + if (!rx.rcpi_rssi && + (ieee80211_is_probe_resp(hdr->frame_control) || + ieee80211_is_beacon(hdr->frame_control))) + return 0; + + /* If no RSSI subscription has been made, + * convert RCPI to RSSI here + */ + if (!priv->cqm_use_rssi) + rx.rcpi_rssi = rx.rcpi_rssi / 2 - 110; + + fctl = *(__le16 *)buf->data; + hdr_len = buf->data - buf->begin; + skb_pull(*skb_p, hdr_len); + if (!rx.status && ieee80211_is_deauth(fctl)) { + if (priv->join_status == CW1200_JOIN_STATUS_STA) { + /* Shedule unjoin work */ + pr_debug("[WSM] Issue unjoin command (RX).\n"); + wsm_lock_tx_async(priv); + if (queue_work(priv->workqueue, + &priv->unjoin_work) <= 0) + wsm_unlock_tx(priv); + } + } + cw1200_rx_cb(priv, &rx, link_id, skb_p); + if (*skb_p) + skb_push(*skb_p, hdr_len); + + return 0; + +underflow: + return -EINVAL; +} + +static int wsm_event_indication(struct cw1200_common *priv, struct wsm_buf *buf) +{ + int first; + struct cw1200_wsm_event *event; + + if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) { + /* STA is stopped. */ + return 0; + } + + event = kzalloc(sizeof(struct cw1200_wsm_event), GFP_KERNEL); + + event->evt.id = __le32_to_cpu(WSM_GET32(buf)); + event->evt.data = __le32_to_cpu(WSM_GET32(buf)); + + pr_debug("[WSM] Event: %d(%d)\n", + event->evt.id, event->evt.data); + + spin_lock(&priv->event_queue_lock); + first = list_empty(&priv->event_queue); + list_add_tail(&event->link, &priv->event_queue); + spin_unlock(&priv->event_queue_lock); + + if (first) + queue_work(priv->workqueue, &priv->event_handler); + + return 0; + +underflow: + kfree(event); + return -EINVAL; +} + +static int wsm_channel_switch_indication(struct cw1200_common *priv, + struct wsm_buf *buf) +{ + WARN_ON(WSM_GET32(buf)); + + priv->channel_switch_in_progress = 0; + wake_up(&priv->channel_switch_done); + + wsm_unlock_tx(priv); + + return 0; + +underflow: + return -EINVAL; +} + +static int wsm_set_pm_indication(struct cw1200_common *priv, + struct wsm_buf *buf) +{ + /* TODO: Check buf (struct wsm_set_pm_complete) for validity */ + if (priv->ps_mode_switch_in_progress) { + priv->ps_mode_switch_in_progress = 0; + wake_up(&priv->ps_mode_switch_done); + } + return 0; +} + +static int wsm_scan_started(struct cw1200_common *priv, void *arg, + struct wsm_buf *buf) +{ + u32 status = WSM_GET32(buf); + if (status != WSM_STATUS_SUCCESS) { + cw1200_scan_failed_cb(priv); + return -EINVAL; + } + return 0; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +static int wsm_scan_complete_indication(struct cw1200_common *priv, + struct wsm_buf *buf) +{ + struct wsm_scan_complete arg; + arg.status = WSM_GET32(buf); + arg.psm = WSM_GET8(buf); + arg.num_channels = WSM_GET8(buf); + cw1200_scan_complete_cb(priv, &arg); + + return 0; + +underflow: + return -EINVAL; +} + +static int wsm_join_complete_indication(struct cw1200_common *priv, + struct wsm_buf *buf) +{ + struct wsm_join_complete arg; + arg.status = WSM_GET32(buf); + pr_debug("[WSM] Join complete indication, status: %d\n", arg.status); + cw1200_join_complete_cb(priv, &arg); + + return 0; + +underflow: + return -EINVAL; +} + +static int wsm_find_complete_indication(struct cw1200_common *priv, + struct wsm_buf *buf) +{ + pr_warn("Implement find_complete_indication\n"); + return 0; +} + +static int wsm_ba_timeout_indication(struct cw1200_common *priv, + struct wsm_buf *buf) +{ + u32 dummy; + u8 tid; + u8 dummy2; + u8 addr[ETH_ALEN]; + + dummy = WSM_GET32(buf); + tid = WSM_GET8(buf); + dummy2 = WSM_GET8(buf); + WSM_GET(buf, addr, ETH_ALEN); + + pr_info("BlockACK timeout, tid %d, addr %pM\n", + tid, addr); + + return 0; + +underflow: + return -EINVAL; +} + +static int wsm_suspend_resume_indication(struct cw1200_common *priv, + int link_id, struct wsm_buf *buf) +{ + u32 flags; + struct wsm_suspend_resume arg; + + flags = WSM_GET32(buf); + arg.link_id = link_id; + arg.stop = !(flags & 1); + arg.multicast = !!(flags & 8); + arg.queue = (flags >> 1) & 3; + + cw1200_suspend_resume(priv, &arg); + + return 0; + +underflow: + return -EINVAL; +} + + +/* ******************************************************************** */ +/* WSM TX */ + +static int wsm_cmd_send(struct cw1200_common *priv, + struct wsm_buf *buf, + void *arg, u16 cmd, long tmo) +{ + size_t buf_len = buf->data - buf->begin; + int ret; + + /* Don't bother if we're dead. */ + if (priv->bh_error) { + ret = 0; + goto done; + } + + /* Block until the cmd buffer is completed. Tortuous. */ + spin_lock(&priv->wsm_cmd.lock); + while (!priv->wsm_cmd.done) { + spin_unlock(&priv->wsm_cmd.lock); + spin_lock(&priv->wsm_cmd.lock); + } + priv->wsm_cmd.done = 0; + spin_unlock(&priv->wsm_cmd.lock); + + if (cmd == WSM_WRITE_MIB_REQ_ID || + cmd == WSM_READ_MIB_REQ_ID) + pr_debug("[WSM] >>> 0x%.4X [MIB: 0x%.4X] (%zu)\n", + cmd, __le16_to_cpu(((__le16 *)buf->begin)[2]), + buf_len); + else + pr_debug("[WSM] >>> 0x%.4X (%zu)\n", cmd, buf_len); + + /* + * Due to buggy SPI on CW1200, we need to + * pad the message by a few bytes to ensure + * that it's completely received. + */ +#ifdef CONFIG_CW1200_ETF + if (!etf_mode) +#endif + buf_len += 4; + + /* Fill HI message header */ + /* BH will add sequence number */ + ((__le16 *)buf->begin)[0] = __cpu_to_le16(buf_len); + ((__le16 *)buf->begin)[1] = __cpu_to_le16(cmd); + + spin_lock(&priv->wsm_cmd.lock); + BUG_ON(priv->wsm_cmd.ptr); + priv->wsm_cmd.ptr = buf->begin; + priv->wsm_cmd.len = buf_len; + priv->wsm_cmd.arg = arg; + priv->wsm_cmd.cmd = cmd; + spin_unlock(&priv->wsm_cmd.lock); + + cw1200_bh_wakeup(priv); + + /* Wait for command completion */ + ret = wait_event_timeout(priv->wsm_cmd_wq, + priv->wsm_cmd.done, tmo); + + if (!ret && !priv->wsm_cmd.done) { + spin_lock(&priv->wsm_cmd.lock); + priv->wsm_cmd.done = 1; + priv->wsm_cmd.ptr = NULL; + spin_unlock(&priv->wsm_cmd.lock); + if (priv->bh_error) { + /* Return ok to help system cleanup */ + ret = 0; + } else { + pr_err("CMD req (0x%04x) stuck in firmware, killing BH\n", priv->wsm_cmd.cmd); + print_hex_dump_bytes("REQDUMP: ", DUMP_PREFIX_NONE, + buf->begin, buf_len); + pr_err("Outstanding outgoing frames: %d\n", priv->hw_bufs_used); + + /* Kill BH thread to report the error to the top layer. */ + atomic_add(1, &priv->bh_term); + wake_up(&priv->bh_wq); + ret = -ETIMEDOUT; + } + } else { + spin_lock(&priv->wsm_cmd.lock); + BUG_ON(!priv->wsm_cmd.done); + ret = priv->wsm_cmd.ret; + spin_unlock(&priv->wsm_cmd.lock); + } +done: + wsm_buf_reset(buf); + return ret; +} + +#ifdef CONFIG_CW1200_ETF +int wsm_raw_cmd(struct cw1200_common *priv, u8 *data, size_t len) +{ + struct wsm_buf *buf = &priv->wsm_cmd_buf; + int ret; + + u16 *cmd = (u16 *)(data + 2); + + wsm_cmd_lock(priv); + + WSM_PUT(buf, data + 4, len - 4); /* Skip over header (u16+u16) */ + + ret = wsm_cmd_send(priv, buf, NULL, __le16_to_cpu(*cmd), WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} +#endif /* CONFIG_CW1200_ETF */ + +/* ******************************************************************** */ +/* WSM TX port control */ + +void wsm_lock_tx(struct cw1200_common *priv) +{ + wsm_cmd_lock(priv); + if (atomic_add_return(1, &priv->tx_lock) == 1) { + if (wsm_flush_tx(priv)) + pr_debug("[WSM] TX is locked.\n"); + } + wsm_cmd_unlock(priv); +} + +void wsm_lock_tx_async(struct cw1200_common *priv) +{ + if (atomic_add_return(1, &priv->tx_lock) == 1) + pr_debug("[WSM] TX is locked (async).\n"); +} + +bool wsm_flush_tx(struct cw1200_common *priv) +{ + unsigned long timestamp = jiffies; + bool pending = false; + long timeout; + int i; + + /* Flush must be called with TX lock held. */ + BUG_ON(!atomic_read(&priv->tx_lock)); + + /* First check if we really need to do something. + * It is safe to use unprotected access, as hw_bufs_used + * can only decrements. + */ + if (!priv->hw_bufs_used) + return true; + + if (priv->bh_error) { + /* In case of failure do not wait for magic. */ + pr_err("[WSM] Fatal error occured, will not flush TX.\n"); + return false; + } else { + /* Get a timestamp of "oldest" frame */ + for (i = 0; i < 4; ++i) + pending |= cw1200_queue_get_xmit_timestamp( + &priv->tx_queue[i], + ×tamp, 0xffffffff); + /* If there's nothing pending, we're good */ + if (!pending) + return true; + + timeout = timestamp + WSM_CMD_LAST_CHANCE_TIMEOUT - jiffies; + if (timeout < 0 || wait_event_timeout(priv->bh_evt_wq, + !priv->hw_bufs_used, + timeout) <= 0) { + /* Hmmm... Not good. Frame had stuck in firmware. */ + priv->bh_error = 1; + wiphy_err(priv->hw->wiphy, "[WSM] TX Frames (%d) stuck in firmware, killing BH\n", priv->hw_bufs_used); + wake_up(&priv->bh_wq); + return false; + } + + /* Ok, everything is flushed. */ + return true; + } +} + +void wsm_unlock_tx(struct cw1200_common *priv) +{ + int tx_lock; + tx_lock = atomic_sub_return(1, &priv->tx_lock); + BUG_ON(tx_lock < 0); + + if (tx_lock == 0) { + if (!priv->bh_error) + cw1200_bh_wakeup(priv); + pr_debug("[WSM] TX is unlocked.\n"); + } +} + +/* ******************************************************************** */ +/* WSM RX */ + +int wsm_handle_exception(struct cw1200_common *priv, u8 *data, size_t len) +{ + struct wsm_buf buf; + u32 reason; + u32 reg[18]; + char fname[48]; + unsigned int i; + + static const char * const reason_str[] = { + "undefined instruction", + "prefetch abort", + "data abort", + "unknown error", + }; + + buf.begin = buf.data = data; + buf.end = &buf.begin[len]; + + reason = WSM_GET32(&buf); + for (i = 0; i < ARRAY_SIZE(reg); ++i) + reg[i] = WSM_GET32(&buf); + WSM_GET(&buf, fname, sizeof(fname)); + + if (reason < 4) + wiphy_err(priv->hw->wiphy, + "Firmware exception: %s.\n", + reason_str[reason]); + else + wiphy_err(priv->hw->wiphy, + "Firmware assert at %.*s, line %d\n", + (int) sizeof(fname), fname, reg[1]); + + for (i = 0; i < 12; i += 4) + wiphy_err(priv->hw->wiphy, + "R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X,\n", + i + 0, reg[i + 0], i + 1, reg[i + 1], + i + 2, reg[i + 2], i + 3, reg[i + 3]); + wiphy_err(priv->hw->wiphy, + "R12: 0x%.8X, SP: 0x%.8X, LR: 0x%.8X, PC: 0x%.8X,\n", + reg[i + 0], reg[i + 1], reg[i + 2], reg[i + 3]); + i += 4; + wiphy_err(priv->hw->wiphy, + "CPSR: 0x%.8X, SPSR: 0x%.8X\n", + reg[i + 0], reg[i + 1]); + + print_hex_dump_bytes("R1: ", DUMP_PREFIX_NONE, + fname, sizeof(fname)); + return 0; + +underflow: + wiphy_err(priv->hw->wiphy, "Firmware exception.\n"); + print_hex_dump_bytes("Exception: ", DUMP_PREFIX_NONE, + data, len); + return -EINVAL; +} + +int wsm_handle_rx(struct cw1200_common *priv, u16 id, + struct wsm_hdr *wsm, struct sk_buff **skb_p) +{ + int ret = 0; + struct wsm_buf wsm_buf; + int link_id = (id >> 6) & 0x0F; + + /* Strip link id. */ + id &= ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX); + + wsm_buf.begin = (u8 *)&wsm[0]; + wsm_buf.data = (u8 *)&wsm[1]; + wsm_buf.end = &wsm_buf.begin[__le32_to_cpu(wsm->len)]; + + pr_debug("[WSM] <<< 0x%.4X (%td)\n", id, + wsm_buf.end - wsm_buf.begin); + +#ifdef CONFIG_CW1200_ETF + if (etf_mode) { + struct sk_buff *skb = alloc_skb(wsm_buf.end - wsm_buf.begin, GFP_KERNEL); + + /* Strip out Sequence num before passing up */ + wsm->id = __le16_to_cpu(wsm->id); + wsm->id &= 0x0FFF; + wsm->id = __cpu_to_le16(wsm->id); + + memcpy(skb_put(skb, wsm_buf.end - wsm_buf.begin), + wsm_buf.begin, + wsm_buf.end - wsm_buf.begin); + skb_queue_tail(&priv->etf_q, skb); + + /* Special case for startup */ + if (id == WSM_STARTUP_IND_ID) { + wsm_startup_indication(priv, &wsm_buf); + } else if (id & 0x0400) { + spin_lock(&priv->wsm_cmd.lock); + priv->wsm_cmd.done = 1; + spin_unlock(&priv->wsm_cmd.lock); + wake_up(&priv->wsm_cmd_wq); + } + + goto out; + } +#endif + + if (id == WSM_TX_CONFIRM_IND_ID) { + ret = wsm_tx_confirm(priv, &wsm_buf, link_id); + } else if (id == WSM_MULTI_TX_CONFIRM_ID) { + ret = wsm_multi_tx_confirm(priv, &wsm_buf, link_id); + } else if (id & 0x0400) { + void *wsm_arg; + u16 wsm_cmd; + + /* Do not trust FW too much. Protection against repeated + * response and race condition removal (see above). + */ + spin_lock(&priv->wsm_cmd.lock); + wsm_arg = priv->wsm_cmd.arg; + wsm_cmd = priv->wsm_cmd.cmd & + ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX); + priv->wsm_cmd.cmd = 0xFFFF; + spin_unlock(&priv->wsm_cmd.lock); + + if (WARN_ON((id & ~0x0400) != wsm_cmd)) { + /* Note that any non-zero is a fatal retcode. */ + ret = -EINVAL; + goto out; + } + + /* Note that wsm_arg can be NULL in case of timeout in + * wsm_cmd_send(). + */ + + switch (id) { + case WSM_READ_MIB_RESP_ID: + if (wsm_arg) + ret = wsm_read_mib_confirm(priv, wsm_arg, + &wsm_buf); + break; + case WSM_WRITE_MIB_RESP_ID: + if (wsm_arg) + ret = wsm_write_mib_confirm(priv, wsm_arg, + &wsm_buf); + break; + case WSM_START_SCAN_RESP_ID: + if (wsm_arg) + ret = wsm_scan_started(priv, wsm_arg, &wsm_buf); + break; + case WSM_CONFIGURATION_RESP_ID: + if (wsm_arg) + ret = wsm_configuration_confirm(priv, wsm_arg, + &wsm_buf); + break; + case WSM_JOIN_RESP_ID: + if (wsm_arg) + ret = wsm_join_confirm(priv, wsm_arg, &wsm_buf); + break; + case WSM_STOP_SCAN_RESP_ID: + case WSM_RESET_RESP_ID: + case WSM_ADD_KEY_RESP_ID: + case WSM_REMOVE_KEY_RESP_ID: + case WSM_SET_PM_RESP_ID: + case WSM_SET_BSS_PARAMS_RESP_ID: + case 0x0412: /* set_tx_queue_params */ + case WSM_EDCA_PARAMS_RESP_ID: + case WSM_SWITCH_CHANNEL_RESP_ID: + case WSM_START_RESP_ID: + case WSM_BEACON_TRANSMIT_RESP_ID: + case 0x0419: /* start_find */ + case 0x041A: /* stop_find */ + case 0x041B: /* update_ie */ + case 0x041C: /* map_link */ + WARN_ON(wsm_arg != NULL); + ret = wsm_generic_confirm(priv, wsm_arg, &wsm_buf); + if (ret) { + wiphy_warn(priv->hw->wiphy, + "wsm_generic_confirm failed for request 0x%04x.\n", + id & ~0x0400); + + /* often 0x407 and 0x410 occur, this means we're dead.. */ + if (priv->join_status >= CW1200_JOIN_STATUS_JOINING) { + wsm_lock_tx(priv); + if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) + wsm_unlock_tx(priv); + } + } + break; + default: + wiphy_warn(priv->hw->wiphy, + "Unrecognized confirmation 0x%04x\n", + id & ~0x0400); + } + + spin_lock(&priv->wsm_cmd.lock); + priv->wsm_cmd.ret = ret; + priv->wsm_cmd.done = 1; + spin_unlock(&priv->wsm_cmd.lock); + + ret = 0; /* Error response from device should ne stop BH. */ + + wake_up(&priv->wsm_cmd_wq); + } else if (id & 0x0800) { + switch (id) { + case WSM_STARTUP_IND_ID: + ret = wsm_startup_indication(priv, &wsm_buf); + break; + case WSM_RECEIVE_IND_ID: + ret = wsm_receive_indication(priv, link_id, + &wsm_buf, skb_p); + break; + case 0x0805: + ret = wsm_event_indication(priv, &wsm_buf); + break; + case WSM_SCAN_COMPLETE_IND_ID: + ret = wsm_scan_complete_indication(priv, &wsm_buf); + break; + case 0x0808: + ret = wsm_ba_timeout_indication(priv, &wsm_buf); + break; + case 0x0809: + ret = wsm_set_pm_indication(priv, &wsm_buf); + break; + case 0x080A: + ret = wsm_channel_switch_indication(priv, &wsm_buf); + break; + case 0x080B: + ret = wsm_find_complete_indication(priv, &wsm_buf); + break; + case 0x080C: + ret = wsm_suspend_resume_indication(priv, + link_id, &wsm_buf); + break; + case 0x080F: + ret = wsm_join_complete_indication(priv, &wsm_buf); + break; + default: + pr_warn("Unrecognised WSM ID %04x\n", id); + } + } else { + WARN_ON(1); + ret = -EINVAL; + } +out: + return ret; +} + +static bool wsm_handle_tx_data(struct cw1200_common *priv, + struct wsm_tx *wsm, + const struct ieee80211_tx_info *tx_info, + const struct cw1200_txpriv *txpriv, + struct cw1200_queue *queue) +{ + bool handled = false; + const struct ieee80211_hdr *frame = + (struct ieee80211_hdr *)&((u8 *)wsm)[txpriv->offset]; + __le16 fctl = frame->frame_control; + enum { + do_probe, + do_drop, + do_wep, + do_tx, + } action = do_tx; + + switch (priv->mode) { + case NL80211_IFTYPE_STATION: + if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) + action = do_tx; + else if (priv->join_status < CW1200_JOIN_STATUS_PRE_STA) + action = do_drop; + break; + case NL80211_IFTYPE_AP: + if (!priv->join_status) { + action = do_drop; + } else if (!(BIT(txpriv->raw_link_id) & + (BIT(0) | priv->link_id_map))) { + wiphy_warn(priv->hw->wiphy, + "A frame with expired link id is dropped.\n"); + action = do_drop; + } + if (cw1200_queue_get_generation(wsm->packet_id) > + CW1200_MAX_REQUEUE_ATTEMPTS) { + /* HACK!!! WSM324 firmware has tendency to requeue + * multicast frames in a loop, causing performance + * drop and high power consumption of the driver. + * In this situation it is better just to drop + * the problematic frame. + */ + wiphy_warn(priv->hw->wiphy, + "Too many attempts to requeue a frame; dropped.\n"); + action = do_drop; + } + break; + case NL80211_IFTYPE_ADHOC: + if (priv->join_status != CW1200_JOIN_STATUS_IBSS) + action = do_drop; + break; + case NL80211_IFTYPE_MESH_POINT: + action = do_tx; /* TODO: Test me! */ + break; + case NL80211_IFTYPE_MONITOR: + default: + action = do_drop; + break; + } + + if (action == do_tx) { + if (ieee80211_is_nullfunc(fctl)) { + spin_lock(&priv->bss_loss_lock); + if (priv->bss_loss_state) { + priv->bss_loss_confirm_id = wsm->packet_id; + wsm->queue_id = WSM_QUEUE_VOICE; + } + spin_unlock(&priv->bss_loss_lock); + } else if (ieee80211_is_probe_req(fctl)) { + action = do_probe; + } else if (ieee80211_is_deauth(fctl) && + priv->mode != NL80211_IFTYPE_AP) { + pr_debug("[WSM] Issue unjoin command due to tx deauth.\n"); + wsm_lock_tx_async(priv); + if (queue_work(priv->workqueue, + &priv->unjoin_work) <= 0) + wsm_unlock_tx(priv); + } else if (ieee80211_has_protected(fctl) && + tx_info->control.hw_key && + tx_info->control.hw_key->keyidx != priv->wep_default_key_id && + (tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_WEP40 || + tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_WEP104)) { + action = do_wep; + } + } + + switch (action) { + case do_probe: + /* An interesting FW "feature". Device filters probe responses. + * The easiest way to get it back is to convert + * probe request into WSM start_scan command. + */ + pr_debug("[WSM] Convert probe request to scan.\n"); + wsm_lock_tx_async(priv); + priv->pending_frame_id = __le32_to_cpu(wsm->packet_id); + if (queue_delayed_work(priv->workqueue, + &priv->scan.probe_work, 0) <= 0) + wsm_unlock_tx(priv); + handled = true; + break; + case do_drop: + pr_debug("[WSM] Drop frame (0x%.4X).\n", fctl); + BUG_ON(cw1200_queue_remove(queue, + __le32_to_cpu(wsm->packet_id))); + handled = true; + break; + case do_wep: + pr_debug("[WSM] Issue set_default_wep_key.\n"); + wsm_lock_tx_async(priv); + priv->wep_default_key_id = tx_info->control.hw_key->keyidx; + priv->pending_frame_id = __le32_to_cpu(wsm->packet_id); + if (queue_work(priv->workqueue, &priv->wep_key_work) <= 0) + wsm_unlock_tx(priv); + handled = true; + break; + case do_tx: + pr_debug("[WSM] Transmit frame.\n"); + break; + default: + /* Do nothing */ + break; + } + return handled; +} + +static int cw1200_get_prio_queue(struct cw1200_common *priv, + u32 link_id_map, int *total) +{ + static const int urgent = BIT(CW1200_LINK_ID_AFTER_DTIM) | + BIT(CW1200_LINK_ID_UAPSD); + struct wsm_edca_queue_params *edca; + unsigned score, best = -1; + int winner = -1; + int queued; + int i; + + /* search for a winner using edca params */ + for (i = 0; i < 4; ++i) { + queued = cw1200_queue_get_num_queued(&priv->tx_queue[i], + link_id_map); + if (!queued) + continue; + *total += queued; + edca = &priv->edca.params[i]; + score = ((edca->aifns + edca->cwmin) << 16) + + ((edca->cwmax - edca->cwmin) * + (get_random_int() & 0xFFFF)); + if (score < best && (winner < 0 || i != 3)) { + best = score; + winner = i; + } + } + + /* override winner if bursting */ + if (winner >= 0 && priv->tx_burst_idx >= 0 && + winner != priv->tx_burst_idx && + !cw1200_queue_get_num_queued( + &priv->tx_queue[winner], + link_id_map & urgent) && + cw1200_queue_get_num_queued( + &priv->tx_queue[priv->tx_burst_idx], + link_id_map)) + winner = priv->tx_burst_idx; + + return winner; +} + +static int wsm_get_tx_queue_and_mask(struct cw1200_common *priv, + struct cw1200_queue **queue_p, + u32 *tx_allowed_mask_p, + bool *more) +{ + int idx; + u32 tx_allowed_mask; + int total = 0; + + /* Search for a queue with multicast frames buffered */ + if (priv->tx_multicast) { + tx_allowed_mask = BIT(CW1200_LINK_ID_AFTER_DTIM); + idx = cw1200_get_prio_queue(priv, + tx_allowed_mask, &total); + if (idx >= 0) { + *more = total > 1; + goto found; + } + } + + /* Search for unicast traffic */ + tx_allowed_mask = ~priv->sta_asleep_mask; + tx_allowed_mask |= BIT(CW1200_LINK_ID_UAPSD); + if (priv->sta_asleep_mask) { + tx_allowed_mask |= priv->pspoll_mask; + tx_allowed_mask &= ~BIT(CW1200_LINK_ID_AFTER_DTIM); + } else { + tx_allowed_mask |= BIT(CW1200_LINK_ID_AFTER_DTIM); + } + idx = cw1200_get_prio_queue(priv, + tx_allowed_mask, &total); + if (idx < 0) + return -ENOENT; + +found: + *queue_p = &priv->tx_queue[idx]; + *tx_allowed_mask_p = tx_allowed_mask; + return 0; +} + +int wsm_get_tx(struct cw1200_common *priv, u8 **data, + size_t *tx_len, int *burst) +{ + struct wsm_tx *wsm = NULL; + struct ieee80211_tx_info *tx_info; + struct cw1200_queue *queue = NULL; + int queue_num; + u32 tx_allowed_mask = 0; + const struct cw1200_txpriv *txpriv = NULL; + int count = 0; + + /* More is used only for broadcasts. */ + bool more = false; + +#ifdef CONFIG_CW1200_ITP + count = cw1200_itp_get_tx(priv, data, tx_len, burst); + if (count) + return count; +#endif + + if (priv->wsm_cmd.ptr) { /* CMD request */ + ++count; + spin_lock(&priv->wsm_cmd.lock); + BUG_ON(!priv->wsm_cmd.ptr); + *data = priv->wsm_cmd.ptr; + *tx_len = priv->wsm_cmd.len; + *burst = 1; + spin_unlock(&priv->wsm_cmd.lock); + } else { + for (;;) { + int ret; + + if (atomic_add_return(0, &priv->tx_lock)) + break; + + spin_lock_bh(&priv->ps_state_lock); + + ret = wsm_get_tx_queue_and_mask(priv, &queue, + &tx_allowed_mask, &more); + queue_num = queue - priv->tx_queue; + + if (priv->buffered_multicasts && + (ret || !more) && + (priv->tx_multicast || !priv->sta_asleep_mask)) { + priv->buffered_multicasts = false; + if (priv->tx_multicast) { + priv->tx_multicast = false; + queue_work(priv->workqueue, + &priv->multicast_stop_work); + } + } + + spin_unlock_bh(&priv->ps_state_lock); + + if (ret) + break; + + if (cw1200_queue_get(queue, + tx_allowed_mask, + &wsm, &tx_info, &txpriv)) + continue; + + if (wsm_handle_tx_data(priv, wsm, + tx_info, txpriv, queue)) + continue; /* Handled by WSM */ + + wsm->hdr.id &= __cpu_to_le16( + ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX)); + wsm->hdr.id |= cpu_to_le16( + WSM_TX_LINK_ID(txpriv->raw_link_id)); + priv->pspoll_mask &= ~BIT(txpriv->raw_link_id); + + *data = (u8 *)wsm; + *tx_len = __le16_to_cpu(wsm->hdr.len); + + /* allow bursting if txop is set */ + if (priv->edca.params[queue_num].txop_limit) + *burst = min(*burst, + (int)cw1200_queue_get_num_queued(queue, tx_allowed_mask) + 1); + else + *burst = 1; + + /* store index of bursting queue */ + if (*burst > 1) + priv->tx_burst_idx = queue_num; + else + priv->tx_burst_idx = -1; + + if (more) { + struct ieee80211_hdr *hdr = + (struct ieee80211_hdr *) + &((u8 *)wsm)[txpriv->offset]; + /* more buffered multicast/broadcast frames + * ==> set MoreData flag in IEEE 802.11 header + * to inform PS STAs + */ + hdr->frame_control |= + cpu_to_le16(IEEE80211_FCTL_MOREDATA); + } + + pr_debug("[WSM] >>> 0x%.4X (%zu) %p %c\n", + 0x0004, *tx_len, *data, + wsm->more ? 'M' : ' '); + ++count; + break; + } + } + + return count; +} + +void wsm_txed(struct cw1200_common *priv, u8 *data) +{ + if (data == priv->wsm_cmd.ptr) { + spin_lock(&priv->wsm_cmd.lock); + priv->wsm_cmd.ptr = NULL; + spin_unlock(&priv->wsm_cmd.lock); + } +} + +/* ******************************************************************** */ +/* WSM buffer */ + +void wsm_buf_init(struct wsm_buf *buf) +{ + BUG_ON(buf->begin); + buf->begin = kmalloc(FWLOAD_BLOCK_SIZE, GFP_KERNEL | GFP_DMA); + buf->end = buf->begin ? &buf->begin[FWLOAD_BLOCK_SIZE] : buf->begin; + wsm_buf_reset(buf); +} + +void wsm_buf_deinit(struct wsm_buf *buf) +{ + kfree(buf->begin); + buf->begin = buf->data = buf->end = NULL; +} + +static void wsm_buf_reset(struct wsm_buf *buf) +{ + if (buf->begin) { + buf->data = &buf->begin[4]; + *(u32 *)buf->begin = 0; + } else { + buf->data = buf->begin; + } +} + +static int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size) +{ + size_t pos = buf->data - buf->begin; + size_t size = pos + extra_size; + + size = round_up(size, FWLOAD_BLOCK_SIZE); + + buf->begin = krealloc(buf->begin, size, GFP_KERNEL | GFP_DMA); + if (buf->begin) { + buf->data = &buf->begin[pos]; + buf->end = &buf->begin[size]; + return 0; + } else { + buf->end = buf->data = buf->begin; + return -ENOMEM; + } +} diff --git a/drivers/net/wireless/cw1200/wsm.h b/drivers/net/wireless/cw1200/wsm.h new file mode 100644 index 000000000000..8d902d6a7dc4 --- /dev/null +++ b/drivers/net/wireless/cw1200/wsm.h @@ -0,0 +1,1879 @@ +/* + * WSM host interface (HI) interface for ST-Ericsson CW1200 mac80211 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * Based on CW1200 UMAC WSM API, which is + * Copyright (C) ST-Ericsson SA 2010 + * Author: Stewart Mathers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef CW1200_WSM_H_INCLUDED +#define CW1200_WSM_H_INCLUDED + +#include + +struct cw1200_common; + +/* Bands */ +/* Radio band 2.412 -2.484 GHz. */ +#define WSM_PHY_BAND_2_4G (0) + +/* Radio band 4.9375-5.8250 GHz. */ +#define WSM_PHY_BAND_5G (1) + +/* Transmit rates */ +/* 1 Mbps ERP-DSSS */ +#define WSM_TRANSMIT_RATE_1 (0) + +/* 2 Mbps ERP-DSSS */ +#define WSM_TRANSMIT_RATE_2 (1) + +/* 5.5 Mbps ERP-CCK */ +#define WSM_TRANSMIT_RATE_5 (2) + +/* 11 Mbps ERP-CCK */ +#define WSM_TRANSMIT_RATE_11 (3) + +/* 22 Mbps ERP-PBCC (Not supported) */ +/* #define WSM_TRANSMIT_RATE_22 (4) */ + +/* 33 Mbps ERP-PBCC (Not supported) */ +/* #define WSM_TRANSMIT_RATE_33 (5) */ + +/* 6 Mbps (3 Mbps) ERP-OFDM, BPSK coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_6 (6) + +/* 9 Mbps (4.5 Mbps) ERP-OFDM, BPSK coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_9 (7) + +/* 12 Mbps (6 Mbps) ERP-OFDM, QPSK coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_12 (8) + +/* 18 Mbps (9 Mbps) ERP-OFDM, QPSK coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_18 (9) + +/* 24 Mbps (12 Mbps) ERP-OFDM, 16QAM coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_24 (10) + +/* 36 Mbps (18 Mbps) ERP-OFDM, 16QAM coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_36 (11) + +/* 48 Mbps (24 Mbps) ERP-OFDM, 64QAM coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_48 (12) + +/* 54 Mbps (27 Mbps) ERP-OFDM, 64QAM coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_54 (13) + +/* 6.5 Mbps HT-OFDM, BPSK coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_HT_6 (14) + +/* 13 Mbps HT-OFDM, QPSK coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_HT_13 (15) + +/* 19.5 Mbps HT-OFDM, QPSK coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_HT_19 (16) + +/* 26 Mbps HT-OFDM, 16QAM coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_HT_26 (17) + +/* 39 Mbps HT-OFDM, 16QAM coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_HT_39 (18) + +/* 52 Mbps HT-OFDM, 64QAM coding rate 2/3 */ +#define WSM_TRANSMIT_RATE_HT_52 (19) + +/* 58.5 Mbps HT-OFDM, 64QAM coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_HT_58 (20) + +/* 65 Mbps HT-OFDM, 64QAM coding rate 5/6 */ +#define WSM_TRANSMIT_RATE_HT_65 (21) + +/* Scan types */ +/* Foreground scan */ +#define WSM_SCAN_TYPE_FOREGROUND (0) + +/* Background scan */ +#define WSM_SCAN_TYPE_BACKGROUND (1) + +/* Auto scan */ +#define WSM_SCAN_TYPE_AUTO (2) + +/* Scan flags */ +/* Forced background scan means if the station cannot */ +/* enter the power-save mode, it shall force to perform a */ +/* background scan. Only valid when ScanType is */ +/* background scan. */ +#define WSM_SCAN_FLAG_FORCE_BACKGROUND (BIT(0)) + +/* The WLAN device scans one channel at a time so */ +/* that disturbance to the data traffic is minimized. */ +#define WSM_SCAN_FLAG_SPLIT_METHOD (BIT(1)) + +/* Preamble Type. Long if not set. */ +#define WSM_SCAN_FLAG_SHORT_PREAMBLE (BIT(2)) + +/* 11n Tx Mode. Mixed if not set. */ +#define WSM_SCAN_FLAG_11N_GREENFIELD (BIT(3)) + +/* Scan constraints */ +/* Maximum number of channels to be scanned. */ +#define WSM_SCAN_MAX_NUM_OF_CHANNELS (48) + +/* The maximum number of SSIDs that the device can scan for. */ +#define WSM_SCAN_MAX_NUM_OF_SSIDS (2) + +/* Power management modes */ +/* 802.11 Active mode */ +#define WSM_PSM_ACTIVE (0) + +/* 802.11 PS mode */ +#define WSM_PSM_PS BIT(0) + +/* Fast Power Save bit */ +#define WSM_PSM_FAST_PS_FLAG BIT(7) + +/* Dynamic aka Fast power save */ +#define WSM_PSM_FAST_PS (BIT(0) | BIT(7)) + +/* Undetermined */ +/* Note : Undetermined status is reported when the */ +/* NULL data frame used to advertise the PM mode to */ +/* the AP at Pre or Post Background Scan is not Acknowledged */ +#define WSM_PSM_UNKNOWN BIT(1) + +/* Queue IDs */ +/* best effort/legacy */ +#define WSM_QUEUE_BEST_EFFORT (0) + +/* background */ +#define WSM_QUEUE_BACKGROUND (1) + +/* video */ +#define WSM_QUEUE_VIDEO (2) + +/* voice */ +#define WSM_QUEUE_VOICE (3) + +/* HT TX parameters */ +/* Non-HT */ +#define WSM_HT_TX_NON_HT (0) + +/* Mixed format */ +#define WSM_HT_TX_MIXED (1) + +/* Greenfield format */ +#define WSM_HT_TX_GREENFIELD (2) + +/* STBC allowed */ +#define WSM_HT_TX_STBC (BIT(7)) + +/* EPTA prioirty flags for BT Coex */ +/* default epta priority */ +#define WSM_EPTA_PRIORITY_DEFAULT 4 +/* use for normal data */ +#define WSM_EPTA_PRIORITY_DATA 4 +/* use for connect/disconnect/roaming*/ +#define WSM_EPTA_PRIORITY_MGT 5 +/* use for action frames */ +#define WSM_EPTA_PRIORITY_ACTION 5 +/* use for AC_VI data */ +#define WSM_EPTA_PRIORITY_VIDEO 5 +/* use for AC_VO data */ +#define WSM_EPTA_PRIORITY_VOICE 6 +/* use for EAPOL exchange */ +#define WSM_EPTA_PRIORITY_EAPOL 7 + +/* TX status */ +/* Frame was sent aggregated */ +/* Only valid for WSM_SUCCESS status. */ +#define WSM_TX_STATUS_AGGREGATION (BIT(0)) + +/* Host should requeue this frame later. */ +/* Valid only when status is WSM_REQUEUE. */ +#define WSM_TX_STATUS_REQUEUE (BIT(1)) + +/* Normal Ack */ +#define WSM_TX_STATUS_NORMAL_ACK (0<<2) + +/* No Ack */ +#define WSM_TX_STATUS_NO_ACK (1<<2) + +/* No explicit acknowledgement */ +#define WSM_TX_STATUS_NO_EXPLICIT_ACK (2<<2) + +/* Block Ack */ +/* Only valid for WSM_SUCCESS status. */ +#define WSM_TX_STATUS_BLOCK_ACK (3<<2) + +/* RX status */ +/* Unencrypted */ +#define WSM_RX_STATUS_UNENCRYPTED (0<<0) + +/* WEP */ +#define WSM_RX_STATUS_WEP (1<<0) + +/* TKIP */ +#define WSM_RX_STATUS_TKIP (2<<0) + +/* AES */ +#define WSM_RX_STATUS_AES (3<<0) + +/* WAPI */ +#define WSM_RX_STATUS_WAPI (4<<0) + +/* Macro to fetch encryption subfield. */ +#define WSM_RX_STATUS_ENCRYPTION(status) ((status) & 0x07) + +/* Frame was part of an aggregation */ +#define WSM_RX_STATUS_AGGREGATE (BIT(3)) + +/* Frame was first in the aggregation */ +#define WSM_RX_STATUS_AGGREGATE_FIRST (BIT(4)) + +/* Frame was last in the aggregation */ +#define WSM_RX_STATUS_AGGREGATE_LAST (BIT(5)) + +/* Indicates a defragmented frame */ +#define WSM_RX_STATUS_DEFRAGMENTED (BIT(6)) + +/* Indicates a Beacon frame */ +#define WSM_RX_STATUS_BEACON (BIT(7)) + +/* Indicates STA bit beacon TIM field */ +#define WSM_RX_STATUS_TIM (BIT(8)) + +/* Indicates Beacon frame's virtual bitmap contains multicast bit */ +#define WSM_RX_STATUS_MULTICAST (BIT(9)) + +/* Indicates frame contains a matching SSID */ +#define WSM_RX_STATUS_MATCHING_SSID (BIT(10)) + +/* Indicates frame contains a matching BSSI */ +#define WSM_RX_STATUS_MATCHING_BSSI (BIT(11)) + +/* Indicates More bit set in Framectl field */ +#define WSM_RX_STATUS_MORE_DATA (BIT(12)) + +/* Indicates frame received during a measurement process */ +#define WSM_RX_STATUS_MEASUREMENT (BIT(13)) + +/* Indicates frame received as an HT packet */ +#define WSM_RX_STATUS_HT (BIT(14)) + +/* Indicates frame received with STBC */ +#define WSM_RX_STATUS_STBC (BIT(15)) + +/* Indicates Address 1 field matches dot11StationId */ +#define WSM_RX_STATUS_ADDRESS1 (BIT(16)) + +/* Indicates Group address present in the Address 1 field */ +#define WSM_RX_STATUS_GROUP (BIT(17)) + +/* Indicates Broadcast address present in the Address 1 field */ +#define WSM_RX_STATUS_BROADCAST (BIT(18)) + +/* Indicates group key used with encrypted frames */ +#define WSM_RX_STATUS_GROUP_KEY (BIT(19)) + +/* Macro to fetch encryption key index. */ +#define WSM_RX_STATUS_KEY_IDX(status) (((status >> 20)) & 0x0F) + +/* Indicates TSF inclusion after 802.11 frame body */ +#define WSM_RX_STATUS_TSF_INCLUDED (BIT(24)) + +/* Frame Control field starts at Frame offset + 2 */ +#define WSM_TX_2BYTES_SHIFT (BIT(7)) + +/* Join mode */ +/* IBSS */ +#define WSM_JOIN_MODE_IBSS (0) + +/* BSS */ +#define WSM_JOIN_MODE_BSS (1) + +/* PLCP preamble type */ +/* For long preamble */ +#define WSM_JOIN_PREAMBLE_LONG (0) + +/* For short preamble (Long for 1Mbps) */ +#define WSM_JOIN_PREAMBLE_SHORT (1) + +/* For short preamble (Long for 1 and 2Mbps) */ +#define WSM_JOIN_PREAMBLE_SHORT_2 (2) + +/* Join flags */ +/* Unsynchronized */ +#define WSM_JOIN_FLAGS_UNSYNCRONIZED BIT(0) +/* The BSS owner is a P2P GO */ +#define WSM_JOIN_FLAGS_P2P_GO BIT(1) +/* Force to join BSS with the BSSID and the + * SSID specified without waiting for beacons. The + * ProbeForJoin parameter is ignored. */ +#define WSM_JOIN_FLAGS_FORCE BIT(2) +/* Give probe request/response higher + * priority over the BT traffic */ +#define WSM_JOIN_FLAGS_PRIO BIT(3) +/* Issue immediate join confirmation and use + * join complete to notify about completion */ +#define WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND BIT(5) + +/* Key types */ +#define WSM_KEY_TYPE_WEP_DEFAULT (0) +#define WSM_KEY_TYPE_WEP_PAIRWISE (1) +#define WSM_KEY_TYPE_TKIP_GROUP (2) +#define WSM_KEY_TYPE_TKIP_PAIRWISE (3) +#define WSM_KEY_TYPE_AES_GROUP (4) +#define WSM_KEY_TYPE_AES_PAIRWISE (5) +#define WSM_KEY_TYPE_WAPI_GROUP (6) +#define WSM_KEY_TYPE_WAPI_PAIRWISE (7) + +/* Key indexes */ +#define WSM_KEY_MAX_INDEX (10) + +/* ACK policy */ +#define WSM_ACK_POLICY_NORMAL (0) +#define WSM_ACK_POLICY_NO_ACK (1) + +/* Start modes */ +#define WSM_START_MODE_AP (0) /* Mini AP */ +#define WSM_START_MODE_P2P_GO (1) /* P2P GO */ +#define WSM_START_MODE_P2P_DEV (2) /* P2P device */ + +/* SetAssociationMode MIB flags */ +#define WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE (BIT(0)) +#define WSM_ASSOCIATION_MODE_USE_HT_MODE (BIT(1)) +#define WSM_ASSOCIATION_MODE_USE_BASIC_RATE_SET (BIT(2)) +#define WSM_ASSOCIATION_MODE_USE_MPDU_START_SPACING (BIT(3)) +#define WSM_ASSOCIATION_MODE_SNOOP_ASSOC_FRAMES (BIT(4)) + +/* RcpiRssiThreshold MIB flags */ +#define WSM_RCPI_RSSI_THRESHOLD_ENABLE (BIT(0)) +#define WSM_RCPI_RSSI_USE_RSSI (BIT(1)) +#define WSM_RCPI_RSSI_DONT_USE_UPPER (BIT(2)) +#define WSM_RCPI_RSSI_DONT_USE_LOWER (BIT(3)) + +/* Update-ie constants */ +#define WSM_UPDATE_IE_BEACON (BIT(0)) +#define WSM_UPDATE_IE_PROBE_RESP (BIT(1)) +#define WSM_UPDATE_IE_PROBE_REQ (BIT(2)) + +/* WSM events */ +/* Error */ +#define WSM_EVENT_ERROR (0) + +/* BSS lost */ +#define WSM_EVENT_BSS_LOST (1) + +/* BSS regained */ +#define WSM_EVENT_BSS_REGAINED (2) + +/* Radar detected */ +#define WSM_EVENT_RADAR_DETECTED (3) + +/* RCPI or RSSI threshold triggered */ +#define WSM_EVENT_RCPI_RSSI (4) + +/* BT inactive */ +#define WSM_EVENT_BT_INACTIVE (5) + +/* BT active */ +#define WSM_EVENT_BT_ACTIVE (6) + +/* MIB IDs */ +/* 4.1 dot11StationId */ +#define WSM_MIB_ID_DOT11_STATION_ID 0x0000 + +/* 4.2 dot11MaxtransmitMsduLifeTime */ +#define WSM_MIB_ID_DOT11_MAX_TRANSMIT_LIFTIME 0x0001 + +/* 4.3 dot11MaxReceiveLifeTime */ +#define WSM_MIB_ID_DOT11_MAX_RECEIVE_LIFETIME 0x0002 + +/* 4.4 dot11SlotTime */ +#define WSM_MIB_ID_DOT11_SLOT_TIME 0x0003 + +/* 4.5 dot11GroupAddressesTable */ +#define WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE 0x0004 +#define WSM_MAX_GRP_ADDRTABLE_ENTRIES 8 + +/* 4.6 dot11WepDefaultKeyId */ +#define WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID 0x0005 + +/* 4.7 dot11CurrentTxPowerLevel */ +#define WSM_MIB_ID_DOT11_CURRENT_TX_POWER_LEVEL 0x0006 + +/* 4.8 dot11RTSThreshold */ +#define WSM_MIB_ID_DOT11_RTS_THRESHOLD 0x0007 + +/* 4.9 NonErpProtection */ +#define WSM_MIB_ID_NON_ERP_PROTECTION 0x1000 + +/* 4.10 ArpIpAddressesTable */ +#define WSM_MIB_ID_ARP_IP_ADDRESSES_TABLE 0x1001 +#define WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES 1 + +/* 4.11 TemplateFrame */ +#define WSM_MIB_ID_TEMPLATE_FRAME 0x1002 + +/* 4.12 RxFilter */ +#define WSM_MIB_ID_RX_FILTER 0x1003 + +/* 4.13 BeaconFilterTable */ +#define WSM_MIB_ID_BEACON_FILTER_TABLE 0x1004 + +/* 4.14 BeaconFilterEnable */ +#define WSM_MIB_ID_BEACON_FILTER_ENABLE 0x1005 + +/* 4.15 OperationalPowerMode */ +#define WSM_MIB_ID_OPERATIONAL_POWER_MODE 0x1006 + +/* 4.16 BeaconWakeUpPeriod */ +#define WSM_MIB_ID_BEACON_WAKEUP_PERIOD 0x1007 + +/* 4.17 RcpiRssiThreshold */ +#define WSM_MIB_ID_RCPI_RSSI_THRESHOLD 0x1009 + +/* 4.18 StatisticsTable */ +#define WSM_MIB_ID_STATISTICS_TABLE 0x100A + +/* 4.19 IbssPsConfig */ +#define WSM_MIB_ID_IBSS_PS_CONFIG 0x100B + +/* 4.20 CountersTable */ +#define WSM_MIB_ID_COUNTERS_TABLE 0x100C + +/* 4.21 BlockAckPolicy */ +#define WSM_MIB_ID_BLOCK_ACK_POLICY 0x100E + +/* 4.22 OverrideInternalTxRate */ +#define WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE 0x100F + +/* 4.23 SetAssociationMode */ +#define WSM_MIB_ID_SET_ASSOCIATION_MODE 0x1010 + +/* 4.24 UpdateEptaConfigData */ +#define WSM_MIB_ID_UPDATE_EPTA_CONFIG_DATA 0x1011 + +/* 4.25 SelectCcaMethod */ +#define WSM_MIB_ID_SELECT_CCA_METHOD 0x1012 + +/* 4.26 SetUpasdInformation */ +#define WSM_MIB_ID_SET_UAPSD_INFORMATION 0x1013 + +/* 4.27 SetAutoCalibrationMode WBF00004073 */ +#define WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE 0x1015 + +/* 4.28 SetTxRateRetryPolicy */ +#define WSM_MIB_ID_SET_TX_RATE_RETRY_POLICY 0x1016 + +/* 4.29 SetHostMessageTypeFilter */ +#define WSM_MIB_ID_SET_HOST_MSG_TYPE_FILTER 0x1017 + +/* 4.30 P2PFindInfo */ +#define WSM_MIB_ID_P2P_FIND_INFO 0x1018 + +/* 4.31 P2PPsModeInfo */ +#define WSM_MIB_ID_P2P_PS_MODE_INFO 0x1019 + +/* 4.32 SetEtherTypeDataFrameFilter */ +#define WSM_MIB_ID_SET_ETHERTYPE_DATAFRAME_FILTER 0x101A + +/* 4.33 SetUDPPortDataFrameFilter */ +#define WSM_MIB_ID_SET_UDPPORT_DATAFRAME_FILTER 0x101B + +/* 4.34 SetMagicDataFrameFilter */ +#define WSM_MIB_ID_SET_MAGIC_DATAFRAME_FILTER 0x101C + +/* 4.35 P2PDeviceInfo */ +#define WSM_MIB_ID_P2P_DEVICE_INFO 0x101D + +/* 4.36 SetWCDMABand */ +#define WSM_MIB_ID_SET_WCDMA_BAND 0x101E + +/* 4.37 GroupTxSequenceCounter */ +#define WSM_MIB_ID_GRP_SEQ_COUNTER 0x101F + +/* 4.38 ProtectedMgmtPolicy */ +#define WSM_MIB_ID_PROTECTED_MGMT_POLICY 0x1020 + +/* 4.39 SetHtProtection */ +#define WSM_MIB_ID_SET_HT_PROTECTION 0x1021 + +/* 4.40 GPIO Command */ +#define WSM_MIB_ID_GPIO_COMMAND 0x1022 + +/* 4.41 TSF Counter Value */ +#define WSM_MIB_ID_TSF_COUNTER 0x1023 + +/* Test Purposes Only */ +#define WSM_MIB_ID_BLOCK_ACK_INFO 0x100D + +/* 4.42 UseMultiTxConfMessage */ +#define WSM_MIB_USE_MULTI_TX_CONF 0x1024 + +/* 4.43 Keep-alive period */ +#define WSM_MIB_ID_KEEP_ALIVE_PERIOD 0x1025 + +/* 4.44 Disable BSSID filter */ +#define WSM_MIB_ID_DISABLE_BSSID_FILTER 0x1026 + +/* Frame template types */ +#define WSM_FRAME_TYPE_PROBE_REQUEST (0) +#define WSM_FRAME_TYPE_BEACON (1) +#define WSM_FRAME_TYPE_NULL (2) +#define WSM_FRAME_TYPE_QOS_NULL (3) +#define WSM_FRAME_TYPE_PS_POLL (4) +#define WSM_FRAME_TYPE_PROBE_RESPONSE (5) + +#define WSM_FRAME_GREENFIELD (0x80) /* See 4.11 */ + +/* Status */ +/* The WSM firmware has completed a request */ +/* successfully. */ +#define WSM_STATUS_SUCCESS (0) + +/* This is a generic failure code if other error codes do */ +/* not apply. */ +#define WSM_STATUS_FAILURE (1) + +/* A request contains one or more invalid parameters. */ +#define WSM_INVALID_PARAMETER (2) + +/* The request cannot perform because the device is in */ +/* an inappropriate mode. */ +#define WSM_ACCESS_DENIED (3) + +/* The frame received includes a decryption error. */ +#define WSM_STATUS_DECRYPTFAILURE (4) + +/* A MIC failure is detected in the received packets. */ +#define WSM_STATUS_MICFAILURE (5) + +/* The transmit request failed due to retry limit being */ +/* exceeded. */ +#define WSM_STATUS_RETRY_EXCEEDED (6) + +/* The transmit request failed due to MSDU life time */ +/* being exceeded. */ +#define WSM_STATUS_TX_LIFETIME_EXCEEDED (7) + +/* The link to the AP is lost. */ +#define WSM_STATUS_LINK_LOST (8) + +/* No key was found for the encrypted frame */ +#define WSM_STATUS_NO_KEY_FOUND (9) + +/* Jammer was detected when transmitting this frame */ +#define WSM_STATUS_JAMMER_DETECTED (10) + +/* The message should be requeued later. */ +/* This is applicable only to Transmit */ +#define WSM_REQUEUE (11) + +/* Advanced filtering options */ +#define WSM_MAX_FILTER_ELEMENTS (4) + +#define WSM_FILTER_ACTION_IGNORE (0) +#define WSM_FILTER_ACTION_FILTER_IN (1) +#define WSM_FILTER_ACTION_FILTER_OUT (2) + +#define WSM_FILTER_PORT_TYPE_DST (0) +#define WSM_FILTER_PORT_TYPE_SRC (1) + +/* Actual header of WSM messages */ +struct wsm_hdr { + __le16 len; + __le16 id; +}; + +#define WSM_TX_SEQ_MAX (7) +#define WSM_TX_SEQ(seq) \ + ((seq & WSM_TX_SEQ_MAX) << 13) +#define WSM_TX_LINK_ID_MAX (0x0F) +#define WSM_TX_LINK_ID(link_id) \ + ((link_id & WSM_TX_LINK_ID_MAX) << 6) + +#define MAX_BEACON_SKIP_TIME_MS 1000 + +#define WSM_CMD_LAST_CHANCE_TIMEOUT (HZ * 3 / 2) + +/* ******************************************************************** */ +/* WSM capability */ + +#define WSM_STARTUP_IND_ID 0x0801 + +struct wsm_startup_ind { + u16 input_buffers; + u16 input_buffer_size; + u16 status; + u16 hw_id; + u16 hw_subid; + u16 fw_cap; + u16 fw_type; + u16 fw_api; + u16 fw_build; + u16 fw_ver; + char fw_label[128]; + u32 config[4]; +}; + +/* ******************************************************************** */ +/* WSM commands */ + +/* 3.1 */ +#define WSM_CONFIGURATION_REQ_ID 0x0009 +#define WSM_CONFIGURATION_RESP_ID 0x0409 + +struct wsm_tx_power_range { + int min_power_level; + int max_power_level; + u32 stepping; +}; + +struct wsm_configuration { + /* [in] */ u32 dot11MaxTransmitMsduLifeTime; + /* [in] */ u32 dot11MaxReceiveLifeTime; + /* [in] */ u32 dot11RtsThreshold; + /* [in, out] */ u8 *dot11StationId; + /* [in] */ const void *dpdData; + /* [in] */ size_t dpdData_size; + /* [out] */ u8 dot11FrequencyBandsSupported; + /* [out] */ u32 supportedRateMask; + /* [out] */ struct wsm_tx_power_range txPowerRange[2]; +}; + +int wsm_configuration(struct cw1200_common *priv, + struct wsm_configuration *arg); + +/* 3.3 */ +#define WSM_RESET_REQ_ID 0x000A +#define WSM_RESET_RESP_ID 0x040A +struct wsm_reset { + /* [in] */ int link_id; + /* [in] */ bool reset_statistics; +}; + +int wsm_reset(struct cw1200_common *priv, const struct wsm_reset *arg); + +/* 3.5 */ +#define WSM_READ_MIB_REQ_ID 0x0005 +#define WSM_READ_MIB_RESP_ID 0x0405 +int wsm_read_mib(struct cw1200_common *priv, u16 mib_id, void *buf, + size_t buf_size); + +/* 3.7 */ +#define WSM_WRITE_MIB_REQ_ID 0x0006 +#define WSM_WRITE_MIB_RESP_ID 0x0406 +int wsm_write_mib(struct cw1200_common *priv, u16 mib_id, void *buf, + size_t buf_size); + +/* 3.9 */ +#define WSM_START_SCAN_REQ_ID 0x0007 +#define WSM_START_SCAN_RESP_ID 0x0407 + +struct wsm_ssid { + u8 ssid[32]; + u32 length; +}; + +struct wsm_scan_ch { + u16 number; + u32 min_chan_time; + u32 max_chan_time; + u32 tx_power_level; +}; + +struct wsm_scan { + /* WSM_PHY_BAND_... */ + u8 band; + + /* WSM_SCAN_TYPE_... */ + u8 type; + + /* WSM_SCAN_FLAG_... */ + u8 flags; + + /* WSM_TRANSMIT_RATE_... */ + u8 max_tx_rate; + + /* Interval period in TUs that the device shall the re- */ + /* execute the requested scan. Max value supported by the device */ + /* is 256s. */ + u32 auto_scan_interval; + + /* Number of probe requests (per SSID) sent to one (1) */ + /* channel. Zero (0) means that none is send, which */ + /* means that a passive scan is to be done. Value */ + /* greater than zero (0) means that an active scan is to */ + /* be done. */ + u32 num_probes; + + /* Number of channels to be scanned. */ + /* Maximum value is WSM_SCAN_MAX_NUM_OF_CHANNELS. */ + u8 num_channels; + + /* Number of SSID provided in the scan command (this */ + /* is zero (0) in broadcast scan) */ + /* The maximum number of SSIDs is WSM_SCAN_MAX_NUM_OF_SSIDS. */ + u8 num_ssids; + + /* The delay time (in microseconds) period */ + /* before sending a probe-request. */ + u8 probe_delay; + + /* SSIDs to be scanned [numOfSSIDs]; */ + struct wsm_ssid *ssids; + + /* Channels to be scanned [numOfChannels]; */ + struct wsm_scan_ch *ch; +}; + +int wsm_scan(struct cw1200_common *priv, const struct wsm_scan *arg); + +/* 3.11 */ +#define WSM_STOP_SCAN_REQ_ID 0x0008 +#define WSM_STOP_SCAN_RESP_ID 0x0408 +int wsm_stop_scan(struct cw1200_common *priv); + +/* 3.13 */ +#define WSM_SCAN_COMPLETE_IND_ID 0x0806 +struct wsm_scan_complete { + /* WSM_STATUS_... */ + u32 status; + + /* WSM_PSM_... */ + u8 psm; + + /* Number of channels that the scan operation completed. */ + u8 num_channels; +}; + +/* 3.14 */ +#define WSM_TX_CONFIRM_IND_ID 0x0404 +#define WSM_MULTI_TX_CONFIRM_ID 0x041E + +struct wsm_tx_confirm { + /* Packet identifier used in wsm_tx. */ + u32 packet_id; + + /* WSM_STATUS_... */ + u32 status; + + /* WSM_TRANSMIT_RATE_... */ + u8 tx_rate; + + /* The number of times the frame was transmitted */ + /* without receiving an acknowledgement. */ + u8 ack_failures; + + /* WSM_TX_STATUS_... */ + u16 flags; + + /* The total time in microseconds that the frame spent in */ + /* the WLAN device before transmission as completed. */ + u32 media_delay; + + /* The total time in microseconds that the frame spent in */ + /* the WLAN device before transmission was started. */ + u32 tx_queue_delay; +}; + +/* 3.15 */ +typedef void (*wsm_tx_confirm_cb) (struct cw1200_common *priv, + struct wsm_tx_confirm *arg); + +/* Note that ideology of wsm_tx struct is different against the rest of + * WSM API. wsm_hdr is /not/ a caller-adapted struct to be used as an input + * argument for WSM call, but a prepared bytestream to be sent to firmware. + * It is filled partly in cw1200_tx, partly in low-level WSM code. + * Please pay attention once again: ideology is different. + * + * Legend: + * - [in]: cw1200_tx must fill this field. + * - [wsm]: the field is filled by low-level WSM. + */ +struct wsm_tx { + /* common WSM header */ + struct wsm_hdr hdr; + + /* Packet identifier that meant to be used in completion. */ + __le32 packet_id; + + /* WSM_TRANSMIT_RATE_... */ + u8 max_tx_rate; + + /* WSM_QUEUE_... */ + u8 queue_id; + + /* True: another packet is pending on the host for transmission. */ + u8 more; + + /* Bit 0 = 0 - Start expiry time from first Tx attempt (default) */ + /* Bit 0 = 1 - Start expiry time from receipt of Tx Request */ + /* Bits 3:1 - PTA Priority */ + /* Bits 6:4 - Tx Rate Retry Policy */ + /* Bit 7 - Reserved */ + u8 flags; + + /* Should be 0. */ + __le32 reserved; + + /* The elapsed time in TUs, after the initial transmission */ + /* of an MSDU, after which further attempts to transmit */ + /* the MSDU shall be terminated. Overrides the global */ + /* dot11MaxTransmitMsduLifeTime setting [optional] */ + /* Device will set the default value if this is 0. */ + __le32 expire_time; + + /* WSM_HT_TX_... */ + __le32 ht_tx_parameters; +}; + +/* = sizeof(generic hi hdr) + sizeof(wsm hdr) + sizeof(alignment) */ +#define WSM_TX_EXTRA_HEADROOM (28) + +/* 3.16 */ +#define WSM_RECEIVE_IND_ID 0x0804 + +struct wsm_rx { + /* WSM_STATUS_... */ + __le32 status; + + /* Specifies the channel of the received packet. */ + __le16 channel_number; + + /* WSM_TRANSMIT_RATE_... */ + u8 rx_rate; + + /* This value is expressed in signed Q8.0 format for */ + /* RSSI and unsigned Q7.1 format for RCPI. */ + u8 rcpi_rssi; + + /* WSM_RX_STATUS_... */ + __le32 flags; + + /* Payload */ + u8 data[0]; +} __packed; + +/* = sizeof(generic hi hdr) + sizeof(wsm hdr) */ +#define WSM_RX_EXTRA_HEADROOM (16) + +typedef void (*wsm_rx_cb) (struct cw1200_common *priv, struct wsm_rx *arg, + struct sk_buff **skb_p); + +/* 3.17 */ +struct wsm_event { + /* WSM_STATUS_... */ + /* [out] */ u32 id; + + /* Indication parameters. */ + /* For error indication, this shall be a 32-bit WSM status. */ + /* For RCPI or RSSI indication, this should be an 8-bit */ + /* RCPI or RSSI value. */ + /* [out] */ u32 data; +}; + +struct cw1200_wsm_event { + struct list_head link; + struct wsm_event evt; +}; + +/* 3.18 - 3.22 */ +/* Measurement. Skipped for now. Irrelevent. */ + +typedef void (*wsm_event_cb) (struct cw1200_common *priv, + struct wsm_event *arg); + +/* 3.23 */ +#define WSM_JOIN_REQ_ID 0x000B +#define WSM_JOIN_RESP_ID 0x040B + +struct wsm_join { + /* WSM_JOIN_MODE_... */ + u8 mode; + + /* WSM_PHY_BAND_... */ + u8 band; + + /* Specifies the channel number to join. The channel */ + /* number will be mapped to an actual frequency */ + /* according to the band */ + u16 channel_number; + + /* Specifies the BSSID of the BSS or IBSS to be joined */ + /* or the IBSS to be started. */ + u8 bssid[6]; + + /* ATIM window of IBSS */ + /* When ATIM window is zero the initiated IBSS does */ + /* not support power saving. */ + u16 atim_window; + + /* WSM_JOIN_PREAMBLE_... */ + u8 preamble_type; + + /* Specifies if a probe request should be send with the */ + /* specified SSID when joining to the network. */ + u8 probe_for_join; + + /* DTIM Period (In multiples of beacon interval) */ + u8 dtim_period; + + /* WSM_JOIN_FLAGS_... */ + u8 flags; + + /* Length of the SSID */ + u32 ssid_len; + + /* Specifies the SSID of the IBSS to join or start */ + u8 ssid[32]; + + /* Specifies the time between TBTTs in TUs */ + u32 beacon_interval; + + /* A bit mask that defines the BSS basic rate set. */ + u32 basic_rate_set; +}; + +struct wsm_join_cnf { + u32 status; + + /* Minimum transmission power level in units of 0.1dBm */ + u32 min_power_level; + + /* Maximum transmission power level in units of 0.1dBm */ + u32 max_power_level; +}; + +int wsm_join(struct cw1200_common *priv, struct wsm_join *arg); + +/* 3.24 */ +struct wsm_join_complete { + /* WSM_STATUS_... */ + u32 status; +}; + +/* 3.25 */ +#define WSM_SET_PM_REQ_ID 0x0010 +#define WSM_SET_PM_RESP_ID 0x0410 +struct wsm_set_pm { + /* WSM_PSM_... */ + u8 mode; + + /* in unit of 500us; 0 to use default */ + u8 fast_psm_idle_period; + + /* in unit of 500us; 0 to use default */ + u8 ap_psm_change_period; + + /* in unit of 500us; 0 to disable auto-pspoll */ + u8 min_auto_pspoll_period; +}; + +int wsm_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg); + +/* 3.27 */ +struct wsm_set_pm_complete { + u8 psm; /* WSM_PSM_... */ +}; + +/* 3.28 */ +#define WSM_SET_BSS_PARAMS_REQ_ID 0x0011 +#define WSM_SET_BSS_PARAMS_RESP_ID 0x0411 +struct wsm_set_bss_params { + /* This resets the beacon loss counters only */ + u8 reset_beacon_loss; + + /* The number of lost consecutive beacons after which */ + /* the WLAN device should indicate the BSS-Lost event */ + /* to the WLAN host driver. */ + u8 beacon_lost_count; + + /* The AID received during the association process. */ + u16 aid; + + /* The operational rate set mask */ + u32 operational_rate_set; +}; + +int wsm_set_bss_params(struct cw1200_common *priv, + const struct wsm_set_bss_params *arg); + +/* 3.30 */ +#define WSM_ADD_KEY_REQ_ID 0x000C +#define WSM_ADD_KEY_RESP_ID 0x040C +struct wsm_add_key { + u8 type; /* WSM_KEY_TYPE_... */ + u8 index; /* Key entry index: 0 -- WSM_KEY_MAX_INDEX */ + u16 reserved; + union { + struct { + u8 peer[6]; /* MAC address of the + * peer station */ + u8 reserved; + u8 keylen; /* Key length in bytes */ + u8 keydata[16]; /* Key data */ + } __packed wep_pairwise; + struct { + u8 keyid; /* Unique per key identifier + * (0..3) */ + u8 keylen; /* Key length in bytes */ + u16 reserved; + u8 keydata[16]; /* Key data */ + } __packed wep_group; + struct { + u8 peer[6]; /* MAC address of the + * peer station */ + u16 reserved; + u8 keydata[16]; /* TKIP key data */ + u8 rx_mic_key[8]; /* Rx MIC key */ + u8 tx_mic_key[8]; /* Tx MIC key */ + } __packed tkip_pairwise; + struct { + u8 keydata[16]; /* TKIP key data */ + u8 rx_mic_key[8]; /* Rx MIC key */ + u8 keyid; /* Key ID */ + u8 reserved[3]; + u8 rx_seqnum[8]; /* Receive Sequence Counter */ + } __packed tkip_group; + struct { + u8 peer[6]; /* MAC address of the + * peer station */ + u16 reserved; + u8 keydata[16]; /* AES key data */ + } __packed aes_pairwise; + struct { + u8 keydata[16]; /* AES key data */ + u8 keyid; /* Key ID */ + u8 reserved[3]; + u8 rx_seqnum[8]; /* Receive Sequence Counter */ + } __packed aes_group; + struct { + u8 peer[6]; /* MAC address of the + * peer station */ + u8 keyid; /* Key ID */ + u8 reserved; + u8 keydata[16]; /* WAPI key data */ + u8 mic_key[16]; /* MIC key data */ + } __packed wapi_pairwise; + struct { + u8 keydata[16]; /* WAPI key data */ + u8 mic_key[16]; /* MIC key data */ + u8 keyid; /* Key ID */ + u8 reserved[3]; + } __packed wapi_group; + } __packed; +} __packed; + +int wsm_add_key(struct cw1200_common *priv, const struct wsm_add_key *arg); + +/* 3.32 */ +#define WSM_REMOVE_KEY_REQ_ID 0x000D +#define WSM_REMOVE_KEY_RESP_ID 0x040D +struct wsm_remove_key { + u8 index; /* Key entry index : 0-10 */ +}; + +int wsm_remove_key(struct cw1200_common *priv, + const struct wsm_remove_key *arg); + +/* 3.34 */ +struct wsm_set_tx_queue_params { + /* WSM_ACK_POLICY_... */ + u8 ackPolicy; + + /* Medium Time of TSPEC (in 32us units) allowed per */ + /* One Second Averaging Period for this queue. */ + u16 allowedMediumTime; + + /* dot11MaxTransmitMsduLifetime to be used for the */ + /* specified queue. */ + u32 maxTransmitLifetime; +}; + +struct wsm_tx_queue_params { + /* NOTE: index is a linux queue id. */ + struct wsm_set_tx_queue_params params[4]; +}; + + +#define WSM_TX_QUEUE_SET(queue_params, queue, ack_policy, allowed_time,\ + max_life_time) \ +do { \ + struct wsm_set_tx_queue_params *p = &(queue_params)->params[queue]; \ + p->ackPolicy = (ack_policy); \ + p->allowedMediumTime = (allowed_time); \ + p->maxTransmitLifetime = (max_life_time); \ +} while (0) + +int wsm_set_tx_queue_params(struct cw1200_common *priv, + const struct wsm_set_tx_queue_params *arg, u8 id); + +/* 3.36 */ +#define WSM_EDCA_PARAMS_REQ_ID 0x0013 +#define WSM_EDCA_PARAMS_RESP_ID 0x0413 +struct wsm_edca_queue_params { + /* CWmin (in slots) for the access class. */ + __le16 cwmin; + + /* CWmax (in slots) for the access class. */ + __le16 cwmax; + + /* AIFS (in slots) for the access class. */ + __le16 aifns; + + /* TX OP Limit (in microseconds) for the access class. */ + __le16 txop_limit; + + /* dot11MaxReceiveLifetime to be used for the specified */ + /* the access class. Overrides the global */ + /* dot11MaxReceiveLifetime value */ + __le32 max_rx_lifetime; +} __packed; + +struct wsm_edca_params { + /* NOTE: index is a linux queue id. */ + struct wsm_edca_queue_params params[4]; + bool uapsd_enable[4]; +}; + +#define TXOP_UNIT 32 +#define WSM_EDCA_SET(__edca, __queue, __aifs, __cw_min, __cw_max, __txop, __lifetime,\ + __uapsd) \ + do { \ + struct wsm_edca_queue_params *p = &(__edca)->params[__queue]; \ + p->cwmin = (__cw_min); \ + p->cwmax = (__cw_max); \ + p->aifns = (__aifs); \ + p->txop_limit = ((__txop) * TXOP_UNIT); \ + p->max_rx_lifetime = (__lifetime); \ + (__edca)->uapsd_enable[__queue] = (__uapsd); \ + } while (0) + +int wsm_set_edca_params(struct cw1200_common *priv, + const struct wsm_edca_params *arg); + +int wsm_set_uapsd_param(struct cw1200_common *priv, + const struct wsm_edca_params *arg); + +/* 3.38 */ +/* Set-System info. Skipped for now. Irrelevent. */ + +/* 3.40 */ +#define WSM_SWITCH_CHANNEL_REQ_ID 0x0016 +#define WSM_SWITCH_CHANNEL_RESP_ID 0x0416 + +struct wsm_switch_channel { + /* 1 - means the STA shall not transmit any further */ + /* frames until the channel switch has completed */ + u8 mode; + + /* Number of TBTTs until channel switch occurs. */ + /* 0 - indicates switch shall occur at any time */ + /* 1 - occurs immediately before the next TBTT */ + u8 switch_count; + + /* The new channel number to switch to. */ + /* Note this is defined as per section 2.7. */ + u16 channel_number; +}; + +int wsm_switch_channel(struct cw1200_common *priv, + const struct wsm_switch_channel *arg); + +typedef void (*wsm_channel_switch_cb) (struct cw1200_common *priv); + +#define WSM_START_REQ_ID 0x0017 +#define WSM_START_RESP_ID 0x0417 + +struct wsm_start { + /* WSM_START_MODE_... */ + /* [in] */ u8 mode; + + /* WSM_PHY_BAND_... */ + /* [in] */ u8 band; + + /* Channel number */ + /* [in] */ u16 channel_number; + + /* Client Traffic window in units of TU */ + /* Valid only when mode == ..._P2P */ + /* [in] */ u32 ct_window; + + /* Interval between two consecutive */ + /* beacon transmissions in TU. */ + /* [in] */ u32 beacon_interval; + + /* DTIM period in terms of beacon intervals */ + /* [in] */ u8 dtim_period; + + /* WSM_JOIN_PREAMBLE_... */ + /* [in] */ u8 preamble; + + /* The delay time (in microseconds) period */ + /* before sending a probe-request. */ + /* [in] */ u8 probe_delay; + + /* Length of the SSID */ + /* [in] */ u8 ssid_len; + + /* SSID of the BSS or P2P_GO to be started now. */ + /* [in] */ u8 ssid[32]; + + /* The basic supported rates for the MiniAP. */ + /* [in] */ u32 basic_rate_set; +}; + +int wsm_start(struct cw1200_common *priv, const struct wsm_start *arg); + +#define WSM_BEACON_TRANSMIT_REQ_ID 0x0018 +#define WSM_BEACON_TRANSMIT_RESP_ID 0x0418 + +struct wsm_beacon_transmit { + /* 1: enable; 0: disable */ + /* [in] */ u8 enable_beaconing; +}; + +int wsm_beacon_transmit(struct cw1200_common *priv, + const struct wsm_beacon_transmit *arg); + +int wsm_start_find(struct cw1200_common *priv); + +int wsm_stop_find(struct cw1200_common *priv); + +typedef void (*wsm_find_complete_cb) (struct cw1200_common *priv, u32 status); + +struct wsm_suspend_resume { + /* See 3.52 */ + /* Link ID */ + /* [out] */ int link_id; + /* Stop sending further Tx requests down to device for this link */ + /* [out] */ bool stop; + /* Transmit multicast Frames */ + /* [out] */ bool multicast; + /* The AC on which Tx to be suspended /resumed. */ + /* This is applicable only for U-APSD */ + /* WSM_QUEUE_... */ + /* [out] */ int queue; +}; + +typedef void (*wsm_suspend_resume_cb) (struct cw1200_common *priv, + struct wsm_suspend_resume *arg); + +/* 3.54 Update-IE request. */ +struct wsm_update_ie { + /* WSM_UPDATE_IE_... */ + /* [in] */ u16 what; + /* [in] */ u16 count; + /* [in] */ u8 *ies; + /* [in] */ size_t length; +}; + +int wsm_update_ie(struct cw1200_common *priv, + const struct wsm_update_ie *arg); + +/* 3.56 */ +struct wsm_map_link { + /* MAC address of the remote device */ + /* [in] */ u8 mac_addr[6]; + /* [in] */ u8 link_id; +}; + +int wsm_map_link(struct cw1200_common *priv, const struct wsm_map_link *arg); + +/* ******************************************************************** */ +/* MIB shortcats */ + +static inline int wsm_set_output_power(struct cw1200_common *priv, + int power_level) +{ + __le32 val = __cpu_to_le32(power_level); + return wsm_write_mib(priv, WSM_MIB_ID_DOT11_CURRENT_TX_POWER_LEVEL, + &val, sizeof(val)); +} + +static inline int wsm_set_beacon_wakeup_period(struct cw1200_common *priv, + unsigned dtim_interval, + unsigned listen_interval) +{ + struct { + u8 numBeaconPeriods; + u8 reserved; + __le16 listenInterval; + } val = { + dtim_interval, 0, __cpu_to_le16(listen_interval) + }; + + if (dtim_interval > 0xFF || listen_interval > 0xFFFF) + return -EINVAL; + else + return wsm_write_mib(priv, WSM_MIB_ID_BEACON_WAKEUP_PERIOD, + &val, sizeof(val)); +} + +struct wsm_rcpi_rssi_threshold { + u8 rssiRcpiMode; /* WSM_RCPI_RSSI_... */ + u8 lowerThreshold; + u8 upperThreshold; + u8 rollingAverageCount; +}; + +static inline int wsm_set_rcpi_rssi_threshold(struct cw1200_common *priv, + struct wsm_rcpi_rssi_threshold *arg) +{ + return wsm_write_mib(priv, WSM_MIB_ID_RCPI_RSSI_THRESHOLD, arg, + sizeof(*arg)); +} + +struct wsm_mib_counters_table { + __le32 plcp_errors; + __le32 fcs_errors; + __le32 tx_packets; + __le32 rx_packets; + __le32 rx_packet_errors; + __le32 rx_decryption_failures; + __le32 rx_mic_failures; + __le32 rx_no_key_failures; + __le32 tx_multicast_frames; + __le32 tx_frames_success; + __le32 tx_frame_failures; + __le32 tx_frames_retried; + __le32 tx_frames_multi_retried; + __le32 rx_frame_duplicates; + __le32 rts_success; + __le32 rts_failures; + __le32 ack_failures; + __le32 rx_multicast_frames; + __le32 rx_frames_success; + __le32 rx_cmac_icv_errors; + __le32 rx_cmac_replays; + __le32 rx_mgmt_ccmp_replays; +} __packed; + +static inline int wsm_get_counters_table(struct cw1200_common *priv, + struct wsm_mib_counters_table *arg) +{ + return wsm_read_mib(priv, WSM_MIB_ID_COUNTERS_TABLE, + arg, sizeof(*arg)); +} + +static inline int wsm_get_station_id(struct cw1200_common *priv, u8 *mac) +{ + return wsm_read_mib(priv, WSM_MIB_ID_DOT11_STATION_ID, mac, ETH_ALEN); +} + +struct wsm_rx_filter { + bool promiscuous; + bool bssid; + bool fcs; + bool probeResponder; +}; + +static inline int wsm_set_rx_filter(struct cw1200_common *priv, + const struct wsm_rx_filter *arg) +{ + __le32 val = 0; + if (arg->promiscuous) + val |= __cpu_to_le32(BIT(0)); + if (arg->bssid) + val |= __cpu_to_le32(BIT(1)); + if (arg->fcs) + val |= __cpu_to_le32(BIT(2)); + if (arg->probeResponder) + val |= __cpu_to_le32(BIT(3)); + return wsm_write_mib(priv, WSM_MIB_ID_RX_FILTER, &val, sizeof(val)); +} + +int wsm_set_probe_responder(struct cw1200_common *priv, bool enable); + +#define WSM_BEACON_FILTER_IE_HAS_CHANGED BIT(0) +#define WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT BIT(1) +#define WSM_BEACON_FILTER_IE_HAS_APPEARED BIT(2) + +struct wsm_beacon_filter_table_entry { + u8 ie_id; + u8 flags; + u8 oui[3]; + u8 match_data[3]; +} __packed; + +struct wsm_mib_beacon_filter_table { + __le32 num; + struct wsm_beacon_filter_table_entry entry[10]; +} __packed; + +static inline int wsm_set_beacon_filter_table(struct cw1200_common *priv, + struct wsm_mib_beacon_filter_table *ft) +{ + size_t size = __le32_to_cpu(ft->num) * + sizeof(struct wsm_beacon_filter_table_entry) + + sizeof(__le32); + + return wsm_write_mib(priv, WSM_MIB_ID_BEACON_FILTER_TABLE, ft, size); +} + +#define WSM_BEACON_FILTER_ENABLE BIT(0) /* Enable/disable beacon filtering */ +#define WSM_BEACON_FILTER_AUTO_ERP BIT(1) /* If 1 FW will handle ERP IE changes internally */ + +struct wsm_beacon_filter_control { + int enabled; + int bcn_count; +}; + +static inline int wsm_beacon_filter_control(struct cw1200_common *priv, + struct wsm_beacon_filter_control *arg) +{ + struct { + __le32 enabled; + __le32 bcn_count; + } val; + val.enabled = __cpu_to_le32(arg->enabled); + val.bcn_count = __cpu_to_le32(arg->bcn_count); + return wsm_write_mib(priv, WSM_MIB_ID_BEACON_FILTER_ENABLE, &val, + sizeof(val)); +} + +enum wsm_power_mode { + wsm_power_mode_active = 0, + wsm_power_mode_doze = 1, + wsm_power_mode_quiescent = 2, +}; + +struct wsm_operational_mode { + enum wsm_power_mode power_mode; + int disable_more_flag_usage; + int perform_ant_diversity; +}; + +static inline int wsm_set_operational_mode(struct cw1200_common *priv, + const struct wsm_operational_mode *arg) +{ + u8 val = arg->power_mode; + if (arg->disable_more_flag_usage) + val |= BIT(4); + if (arg->perform_ant_diversity) + val |= BIT(5); + return wsm_write_mib(priv, WSM_MIB_ID_OPERATIONAL_POWER_MODE, &val, + sizeof(val)); +} + +struct wsm_template_frame { + u8 frame_type; + u8 rate; + struct sk_buff *skb; +}; + +static inline int wsm_set_template_frame(struct cw1200_common *priv, + struct wsm_template_frame *arg) +{ + int ret; + u8 *p = skb_push(arg->skb, 4); + p[0] = arg->frame_type; + p[1] = arg->rate; + ((u16 *)p)[1] = __cpu_to_le16(arg->skb->len - 4); + ret = wsm_write_mib(priv, WSM_MIB_ID_TEMPLATE_FRAME, p, arg->skb->len); + skb_pull(arg->skb, 4); + return ret; +} + + +struct wsm_protected_mgmt_policy { + bool protectedMgmtEnable; + bool unprotectedMgmtFramesAllowed; + bool encryptionForAuthFrame; +}; + +static inline int wsm_set_protected_mgmt_policy(struct cw1200_common *priv, + struct wsm_protected_mgmt_policy *arg) +{ + __le32 val = 0; + int ret; + if (arg->protectedMgmtEnable) + val |= __cpu_to_le32(BIT(0)); + if (arg->unprotectedMgmtFramesAllowed) + val |= __cpu_to_le32(BIT(1)); + if (arg->encryptionForAuthFrame) + val |= __cpu_to_le32(BIT(2)); + ret = wsm_write_mib(priv, WSM_MIB_ID_PROTECTED_MGMT_POLICY, + &val, sizeof(val)); + return ret; +} + +struct wsm_mib_block_ack_policy { + u8 tx_tid; + u8 reserved1; + u8 rx_tid; + u8 reserved2; +} __packed; + +static inline int wsm_set_block_ack_policy(struct cw1200_common *priv, + u8 tx_tid_policy, + u8 rx_tid_policy) +{ + struct wsm_mib_block_ack_policy val = { + .tx_tid = tx_tid_policy, + .rx_tid = rx_tid_policy, + }; + return wsm_write_mib(priv, WSM_MIB_ID_BLOCK_ACK_POLICY, &val, + sizeof(val)); +} + +struct wsm_mib_association_mode { + u8 flags; /* WSM_ASSOCIATION_MODE_... */ + u8 preamble; /* WSM_JOIN_PREAMBLE_... */ + u8 greenfield; /* 1 for greenfield */ + u8 mpdu_start_spacing; + __le32 basic_rate_set; +} __packed; + +static inline int wsm_set_association_mode(struct cw1200_common *priv, + struct wsm_mib_association_mode *arg) +{ + return wsm_write_mib(priv, WSM_MIB_ID_SET_ASSOCIATION_MODE, arg, + sizeof(*arg)); +} + +#define WSM_TX_RATE_POLICY_FLAG_TERMINATE_WHEN_FINISHED BIT(2) +#define WSM_TX_RATE_POLICY_FLAG_COUNT_INITIAL_TRANSMIT BIT(3) +struct wsm_tx_rate_retry_policy { + u8 index; + u8 short_retries; + u8 long_retries; + /* BIT(2) - Terminate retries when Tx rate retry policy + * finishes. + * BIT(3) - Count initial frame transmission as part of + * rate retry counting but not as a retry + * attempt */ + u8 flags; + u8 rate_recoveries; + u8 reserved[3]; + __le32 rate_count_indices[3]; +} __packed; + +struct wsm_set_tx_rate_retry_policy { + u8 num; + u8 reserved[3]; + struct wsm_tx_rate_retry_policy tbl[8]; +} __packed; + +static inline int wsm_set_tx_rate_retry_policy(struct cw1200_common *priv, + struct wsm_set_tx_rate_retry_policy *arg) +{ + size_t size = 4 + arg->num * sizeof(struct wsm_tx_rate_retry_policy); + return wsm_write_mib(priv, WSM_MIB_ID_SET_TX_RATE_RETRY_POLICY, arg, + size); +} + +/* 4.32 SetEtherTypeDataFrameFilter */ +struct wsm_ether_type_filter_hdr { + u8 num; /* Up to WSM_MAX_FILTER_ELEMENTS */ + u8 reserved[3]; +} __packed; + +struct wsm_ether_type_filter { + u8 action; /* WSM_FILTER_ACTION_XXX */ + u8 reserved; + __le16 type; /* Type of ethernet frame */ +} __packed; + +static inline int wsm_set_ether_type_filter(struct cw1200_common *priv, + struct wsm_ether_type_filter_hdr *arg) +{ + size_t size = sizeof(struct wsm_ether_type_filter_hdr) + + arg->num * sizeof(struct wsm_ether_type_filter); + return wsm_write_mib(priv, WSM_MIB_ID_SET_ETHERTYPE_DATAFRAME_FILTER, + arg, size); +} + +/* 4.33 SetUDPPortDataFrameFilter */ +struct wsm_udp_port_filter_hdr { + u8 num; /* Up to WSM_MAX_FILTER_ELEMENTS */ + u8 reserved[3]; +} __packed; + +struct wsm_udp_port_filter { + u8 action; /* WSM_FILTER_ACTION_XXX */ + u8 type; /* WSM_FILTER_PORT_TYPE_XXX */ + __le16 port; /* Port number */ +} __packed; + +static inline int wsm_set_udp_port_filter(struct cw1200_common *priv, + struct wsm_udp_port_filter_hdr *arg) +{ + size_t size = sizeof(struct wsm_udp_port_filter_hdr) + + arg->num * sizeof(struct wsm_udp_port_filter); + return wsm_write_mib(priv, WSM_MIB_ID_SET_UDPPORT_DATAFRAME_FILTER, + arg, size); +} + +/* Undocumented MIBs: */ +/* 4.35 P2PDeviceInfo */ +#define D11_MAX_SSID_LEN (32) + +struct wsm_p2p_device_type { + __le16 categoryId; + u8 oui[4]; + __le16 subCategoryId; +} __packed; + +struct wsm_p2p_device_info { + struct wsm_p2p_device_type primaryDevice; + u8 reserved1[3]; + u8 devNameSize; + u8 localDevName[D11_MAX_SSID_LEN]; + u8 reserved2[3]; + u8 numSecDevSupported; + struct wsm_p2p_device_type secondaryDevices[0]; +} __packed; + +/* 4.36 SetWCDMABand - WO */ +struct wsm_cdma_band { + u8 WCDMA_Band; + u8 reserved[3]; +} __packed; + +/* 4.37 GroupTxSequenceCounter - RO */ +struct wsm_group_tx_seq { + __le32 bits_47_16; + __le16 bits_15_00; + __le16 reserved; +} __packed; + +/* 4.39 SetHtProtection - WO */ +#define WSM_DUAL_CTS_PROT_ENB (1 << 0) +#define WSM_NON_GREENFIELD_STA_PRESENT (1 << 1) +#define WSM_HT_PROT_MODE__NO_PROT (0 << 2) +#define WSM_HT_PROT_MODE__NON_MEMBER (1 << 2) +#define WSM_HT_PROT_MODE__20_MHZ (2 << 2) +#define WSM_HT_PROT_MODE__NON_HT_MIXED (3 << 2) +#define WSM_LSIG_TXOP_PROT_FULL (1 << 4) +#define WSM_LARGE_L_LENGTH_PROT (1 << 5) + +struct wsm_ht_protection { + __le32 flags; +} __packed; + +/* 4.40 GPIO Command - R/W */ +#define WSM_GPIO_COMMAND_SETUP 0 +#define WSM_GPIO_COMMAND_READ 1 +#define WSM_GPIO_COMMAND_WRITE 2 +#define WSM_GPIO_COMMAND_RESET 3 +#define WSM_GPIO_ALL_PINS 0xFF + +struct wsm_gpio_command { + u8 GPIO_Command; + u8 pin; + __le16 config; +} __packed; + +/* 4.41 TSFCounter - RO */ +struct wsm_tsf_counter { + __le64 TSF_Counter; +} __packed; + +/* 4.43 Keep alive period */ +struct wsm_keep_alive_period { + __le16 keepAlivePeriod; + u8 reserved[2]; +} __packed; + +static inline int wsm_keep_alive_period(struct cw1200_common *priv, + int period) +{ + struct wsm_keep_alive_period arg = { + .keepAlivePeriod = __cpu_to_le16(period), + }; + return wsm_write_mib(priv, WSM_MIB_ID_KEEP_ALIVE_PERIOD, + &arg, sizeof(arg)); +}; + +/* BSSID filtering */ +struct wsm_set_bssid_filtering { + u8 filter; + u8 reserved[3]; +} __packed; + +static inline int wsm_set_bssid_filtering(struct cw1200_common *priv, + bool enabled) +{ + struct wsm_set_bssid_filtering arg = { + .filter = !enabled, + }; + return wsm_write_mib(priv, WSM_MIB_ID_DISABLE_BSSID_FILTER, + &arg, sizeof(arg)); +} + +/* Multicast filtering - 4.5 */ +struct wsm_mib_multicast_filter { + __le32 enable; + __le32 num_addrs; + u8 macaddrs[WSM_MAX_GRP_ADDRTABLE_ENTRIES][ETH_ALEN]; +} __packed; + +static inline int wsm_set_multicast_filter(struct cw1200_common *priv, + struct wsm_mib_multicast_filter *fp) +{ + return wsm_write_mib(priv, WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE, + fp, sizeof(*fp)); +} + +/* ARP IPv4 filtering - 4.10 */ +struct wsm_mib_arp_ipv4_filter { + __le32 enable; + __be32 ipv4addrs[WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES]; +} __packed; + +static inline int wsm_set_arp_ipv4_filter(struct cw1200_common *priv, + struct wsm_mib_arp_ipv4_filter *fp) +{ + return wsm_write_mib(priv, WSM_MIB_ID_ARP_IP_ADDRESSES_TABLE, + fp, sizeof(*fp)); +} + +/* P2P Power Save Mode Info - 4.31 */ +struct wsm_p2p_ps_modeinfo { + u8 oppPsCTWindow; + u8 count; + u8 reserved; + u8 dtimCount; + __le32 duration; + __le32 interval; + __le32 startTime; +} __packed; + +static inline int wsm_set_p2p_ps_modeinfo(struct cw1200_common *priv, + struct wsm_p2p_ps_modeinfo *mi) +{ + return wsm_write_mib(priv, WSM_MIB_ID_P2P_PS_MODE_INFO, + mi, sizeof(*mi)); +} + +static inline int wsm_get_p2p_ps_modeinfo(struct cw1200_common *priv, + struct wsm_p2p_ps_modeinfo *mi) +{ + return wsm_read_mib(priv, WSM_MIB_ID_P2P_PS_MODE_INFO, + mi, sizeof(*mi)); +} + +/* UseMultiTxConfMessage */ + +static inline int wsm_use_multi_tx_conf(struct cw1200_common *priv, + bool enabled) +{ + __le32 arg = enabled ? __cpu_to_le32(1) : 0; + + return wsm_write_mib(priv, WSM_MIB_USE_MULTI_TX_CONF, + &arg, sizeof(arg)); +} + + +/* 4.26 SetUpasdInformation */ +struct wsm_uapsd_info { + __le16 uapsd_flags; + __le16 min_auto_trigger_interval; + __le16 max_auto_trigger_interval; + __le16 auto_trigger_step; +}; + +static inline int wsm_set_uapsd_info(struct cw1200_common *priv, + struct wsm_uapsd_info *arg) +{ + return wsm_write_mib(priv, WSM_MIB_ID_SET_UAPSD_INFORMATION, + arg, sizeof(*arg)); +} + +/* 4.22 OverrideInternalTxRate */ +struct wsm_override_internal_txrate { + u8 internalTxRate; + u8 nonErpInternalTxRate; + u8 reserved[2]; +} __packed; + +static inline int wsm_set_override_internal_txrate(struct cw1200_common *priv, + struct wsm_override_internal_txrate *arg) +{ + return wsm_write_mib(priv, WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE, + arg, sizeof(*arg)); +} + +/* ******************************************************************** */ +/* WSM TX port control */ + +void wsm_lock_tx(struct cw1200_common *priv); +void wsm_lock_tx_async(struct cw1200_common *priv); +bool wsm_flush_tx(struct cw1200_common *priv); +void wsm_unlock_tx(struct cw1200_common *priv); + +/* ******************************************************************** */ +/* WSM / BH API */ + +int wsm_handle_exception(struct cw1200_common *priv, u8 *data, size_t len); +int wsm_handle_rx(struct cw1200_common *priv, u16 id, struct wsm_hdr *wsm, + struct sk_buff **skb_p); + +/* ******************************************************************** */ +/* wsm_buf API */ + +struct wsm_buf { + u8 *begin; + u8 *data; + u8 *end; +}; + +void wsm_buf_init(struct wsm_buf *buf); +void wsm_buf_deinit(struct wsm_buf *buf); + +/* ******************************************************************** */ +/* wsm_cmd API */ + +struct wsm_cmd { + spinlock_t lock; /* Protect structure from multiple access */ + int done; + u8 *ptr; + size_t len; + void *arg; + int ret; + u16 cmd; +}; + +/* ******************************************************************** */ +/* WSM TX buffer access */ + +int wsm_get_tx(struct cw1200_common *priv, u8 **data, + size_t *tx_len, int *burst); +void wsm_txed(struct cw1200_common *priv, u8 *data); + +/* ******************************************************************** */ +/* Queue mapping: WSM <---> linux */ +/* Linux: VO VI BE BK */ +/* WSM: BE BK VI VO */ + +static inline u8 wsm_queue_id_to_linux(u8 queue_id) +{ + static const u8 queue_mapping[] = { + 2, 3, 1, 0 + }; + return queue_mapping[queue_id]; +} + +static inline u8 wsm_queue_id_to_wsm(u8 queue_id) +{ + static const u8 queue_mapping[] = { + 3, 2, 0, 1 + }; + return queue_mapping[queue_id]; +} + + +#ifdef CONFIG_CW1200_ETF +int wsm_raw_cmd(struct cw1200_common *priv, u8 *data, size_t len); +#endif + +#endif /* CW1200_HWIO_H_INCLUDED */ diff --git a/include/linux/cw1200_platform.h b/include/linux/cw1200_platform.h new file mode 100644 index 000000000000..c168fa512347 --- /dev/null +++ b/include/linux/cw1200_platform.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Author: Dmitry Tarnyagin + * License terms: GNU General Public License (GPL) version 2 + */ + +#ifndef CW1200_PLAT_H_INCLUDED +#define CW1200_PLAT_H_INCLUDED + +struct cw1200_platform_data_spi { + u8 spi_bits_per_word; /* REQUIRED */ + u16 ref_clk; /* REQUIRED (in KHz) */ + + /* All others are optional */ + bool have_5ghz; + const struct resource *reset; /* GPIO to RSTn signal */ + const struct resource *powerup; /* GPIO to POWERUP signal */ + int (*power_ctrl)(const struct cw1200_platform_data_spi *pdata, + bool enable); /* Control 3v3 / 1v8 supply */ + int (*clk_ctrl)(const struct cw1200_platform_data_spi *pdata, + bool enable); /* Control CLK32K */ + const u8 *macaddr; /* if NULL, use cw1200_mac_template module parameter */ + const char *sdd_file; /* if NULL, will use default for detected hw type */ +}; + +struct cw1200_platform_data_sdio { + u16 ref_clk; /* REQUIRED (in KHz) */ + + /* All others are optional */ + const struct resource *irq; /* if using GPIO for IRQ */ + bool have_5ghz; + bool no_nptb; /* SDIO hardware does not support non-power-of-2-blocksizes */ + const struct resource *reset; /* GPIO to RSTn signal */ + const struct resource *powerup; /* GPIO to POWERUP signal */ + int (*power_ctrl)(const struct cw1200_platform_data_sdio *pdata, + bool enable); /* Control 3v3 / 1v8 supply */ + int (*clk_ctrl)(const struct cw1200_platform_data_sdio *pdata, + bool enable); /* Control CLK32K */ + const u8 *macaddr; /* if NULL, use cw1200_mac_template module parameter */ + const char *sdd_file; /* if NULL, will use default for detected hw type */ +}; + +const void *cw1200_get_platform_data(void); + +#endif /* CW1200_PLAT_H_INCLUDED */ -- cgit v1.2.3 From 70bd3b298bbbd5a36c55af957bb3b5f727218918 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 29 May 2013 20:28:16 +0100 Subject: ASoC: wm8994: Defer declaration of open circuit microphones Provide a bit of debounce to handle pathological cases with slow input better by allowing the microphone detection to run for a bit longer. Signed-off-by: Mark Brown --- sound/soc/codecs/wm8994.c | 40 +++++++++++++++++++++++++++++++--------- sound/soc/codecs/wm8994.h | 1 + 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 0805d6ff9ff7..2c2a183da2b6 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -3513,6 +3513,31 @@ static void wm8958_button_det(struct snd_soc_codec *codec, u16 status) wm8994->btn_mask); } +static void wm8958_open_circuit_work(struct work_struct *work) +{ + struct wm8994_priv *wm8994 = container_of(work, + struct wm8994_priv, + open_circuit_work.work); + struct device *dev = wm8994->wm8994->dev; + + wm1811_micd_stop(wm8994->hubs.codec); + + mutex_lock(&wm8994->accdet_lock); + + dev_dbg(dev, "Reporting open circuit\n"); + + wm8994->jack_mic = false; + wm8994->mic_detecting = true; + + wm8958_micd_set_rate(wm8994->hubs.codec); + + snd_soc_jack_report(wm8994->micdet[0].jack, 0, + wm8994->btn_mask | + SND_JACK_HEADSET); + + mutex_unlock(&wm8994->accdet_lock); +} + static void wm8958_mic_id(void *data, u16 status) { struct snd_soc_codec *codec = data; @@ -3522,16 +3547,9 @@ static void wm8958_mic_id(void *data, u16 status) if (!(status & WM8958_MICD_STS)) { /* If nothing present then clear our statuses */ dev_dbg(codec->dev, "Detected open circuit\n"); - wm8994->jack_mic = false; - wm8994->mic_detecting = true; - - wm1811_micd_stop(codec); - wm8958_micd_set_rate(codec); - - snd_soc_jack_report(wm8994->micdet[0].jack, 0, - wm8994->btn_mask | - SND_JACK_HEADSET); + schedule_delayed_work(&wm8994->open_circuit_work, + msecs_to_jiffies(2500)); return; } @@ -3812,6 +3830,8 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data) if (!(snd_soc_read(codec, WM8958_MIC_DETECT_1) & WM8958_MICD_ENA)) return IRQ_HANDLED; + cancel_delayed_work_sync(&wm8994->open_circuit_work); + pm_runtime_get_sync(codec->dev); /* We may occasionally read a detection without an impedence @@ -3911,6 +3931,8 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) mutex_init(&wm8994->accdet_lock); INIT_DELAYED_WORK(&wm8994->jackdet_bootstrap, wm1811_jackdet_bootstrap); + INIT_DELAYED_WORK(&wm8994->open_circuit_work, + wm8958_open_circuit_work); switch (control->type) { case WM8994: diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h index 55ddf4d57d9b..9d19a9185d35 100644 --- a/sound/soc/codecs/wm8994.h +++ b/sound/soc/codecs/wm8994.h @@ -134,6 +134,7 @@ struct wm8994_priv { struct mutex accdet_lock; struct wm8994_micdet micdet[2]; struct delayed_work mic_work; + struct delayed_work open_circuit_work; bool mic_detecting; bool jack_mic; int btn_mask; -- cgit v1.2.3 From 24778be20f87d5aadb19624fc768b3159fa43efc Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Tue, 21 May 2013 20:36:35 -0600 Subject: spi: convert drivers to use bits_per_word_mask Fill in the recently added spi_master.bits_per_word_mask field in as many drivers as possible. Make related cleanups, such as removing any redundant error-checking, or empty setup callbacks. Signed-off-by: Stephen Warren Acked-by: H Hartley Sweeten Signed-off-by: Mark Brown --- drivers/spi/spi-altera.c | 12 ------------ drivers/spi/spi-ath79.c | 4 +--- drivers/spi/spi-atmel.c | 9 +-------- drivers/spi/spi-au1550.c | 12 +----------- drivers/spi/spi-bcm63xx.c | 20 +------------------- drivers/spi/spi-bfin-sport.c | 10 ++-------- drivers/spi/spi-bfin5xx.c | 21 +++++---------------- drivers/spi/spi-clps711x.c | 7 +------ drivers/spi/spi-coldfire-qspi.c | 11 ++--------- drivers/spi/spi-davinci.c | 8 ++++---- drivers/spi/spi-dw.c | 26 ++++---------------------- drivers/spi/spi-ep93xx.c | 11 +---------- drivers/spi/spi-fsl-espi.c | 9 ++------- drivers/spi/spi-gpio.c | 4 +--- drivers/spi/spi-imx.c | 6 +++--- drivers/spi/spi-mxs.c | 7 +------ drivers/spi/spi-nuc900.c | 13 ------------- drivers/spi/spi-omap-100k.c | 12 ++---------- drivers/spi/spi-omap2-mcspi.c | 13 ++----------- drivers/spi/spi-ppc4xx.c | 13 +------------ drivers/spi/spi-pxa2xx.c | 20 ++------------------ drivers/spi/spi-s3c64xx.c | 3 ++- drivers/spi/spi-sirf.c | 6 ++---- drivers/spi/spi-ti-ssp.c | 16 +--------------- drivers/spi/spi-topcliff-pch.c | 17 +---------------- drivers/spi/spi-txx9.c | 8 +------- drivers/spi/spi-xcomm.c | 12 ++---------- drivers/spi/spi-xilinx.c | 16 ---------------- 28 files changed, 46 insertions(+), 280 deletions(-) diff --git a/drivers/spi/spi-altera.c b/drivers/spi/spi-altera.c index a537f8dffc09..6b7096e0ad68 100644 --- a/drivers/spi/spi-altera.c +++ b/drivers/spi/spi-altera.c @@ -103,16 +103,6 @@ static void altera_spi_chipsel(struct spi_device *spi, int value) } } -static int altera_spi_setupxfer(struct spi_device *spi, struct spi_transfer *t) -{ - return 0; -} - -static int altera_spi_setup(struct spi_device *spi) -{ - return 0; -} - static inline unsigned int hw_txbyte(struct altera_spi *hw, int count) { if (hw->tx) { @@ -231,7 +221,6 @@ static int altera_spi_probe(struct platform_device *pdev) master->bus_num = pdev->id; master->num_chipselect = 16; master->mode_bits = SPI_CS_HIGH; - master->setup = altera_spi_setup; hw = spi_master_get_devdata(master); platform_set_drvdata(pdev, hw); @@ -240,7 +229,6 @@ static int altera_spi_probe(struct platform_device *pdev) hw->bitbang.master = spi_master_get(master); if (!hw->bitbang.master) return err; - hw->bitbang.setup_transfer = altera_spi_setupxfer; hw->bitbang.chipselect = altera_spi_chipsel; hw->bitbang.txrx_bufs = altera_spi_txrx; diff --git a/drivers/spi/spi-ath79.c b/drivers/spi/spi-ath79.c index e504b7636058..5e2d52ce387f 100644 --- a/drivers/spi/spi-ath79.c +++ b/drivers/spi/spi-ath79.c @@ -155,9 +155,6 @@ static int ath79_spi_setup(struct spi_device *spi) { int status = 0; - if (spi->bits_per_word > 32) - return -EINVAL; - if (!spi->controller_state) { status = ath79_spi_setup_cs(spi); if (status) @@ -226,6 +223,7 @@ static int ath79_spi_probe(struct platform_device *pdev) pdata = pdev->dev.platform_data; + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32); master->setup = ath79_spi_setup; master->cleanup = ath79_spi_cleanup; if (pdata) { diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 380387a47b1d..31cfc8711547 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -1268,13 +1268,6 @@ static int atmel_spi_setup(struct spi_device *spi) return -EINVAL; } - if (bits < 8 || bits > 16) { - dev_dbg(&spi->dev, - "setup: invalid bits_per_word %u (8 to 16)\n", - bits); - return -EINVAL; - } - /* see notes above re chipselect */ if (!atmel_spi_is_v2(as) && spi->chip_select == 0 @@ -1515,7 +1508,7 @@ static int atmel_spi_probe(struct platform_device *pdev) /* the spi->mode bits understood by this driver: */ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; - + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(8, 16); master->dev.of_node = pdev->dev.of_node; master->bus_num = pdev->id; master->num_chipselect = master->dev.of_node ? 0 : 4; diff --git a/drivers/spi/spi-au1550.c b/drivers/spi/spi-au1550.c index 44dd34b6ad09..d736fa942020 100644 --- a/drivers/spi/spi-au1550.c +++ b/drivers/spi/spi-au1550.c @@ -248,11 +248,6 @@ static int au1550_spi_setupxfer(struct spi_device *spi, struct spi_transfer *t) hz = t->speed_hz; } - if (bpw < 4 || bpw > 24) { - dev_err(&spi->dev, "setupxfer: invalid bits_per_word=%d\n", - bpw); - return -EINVAL; - } if (hz > spi->max_speed_hz || hz > hw->freq_max || hz < hw->freq_min) { dev_err(&spi->dev, "setupxfer: clock rate=%d out of range\n", hz); @@ -296,12 +291,6 @@ static int au1550_spi_setup(struct spi_device *spi) { struct au1550_spi *hw = spi_master_get_devdata(spi->master); - if (spi->bits_per_word < 4 || spi->bits_per_word > 24) { - dev_err(&spi->dev, "setup: invalid bits_per_word=%d\n", - spi->bits_per_word); - return -EINVAL; - } - if (spi->max_speed_hz == 0) spi->max_speed_hz = hw->freq_max; if (spi->max_speed_hz > hw->freq_max @@ -782,6 +771,7 @@ static int au1550_spi_probe(struct platform_device *pdev) /* the spi->mode bits understood by this driver: */ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST; + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 24); hw = spi_master_get_devdata(master); diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index a4ec5f4ec817..270f09c071fb 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c @@ -124,17 +124,6 @@ static void bcm63xx_spi_setup_transfer(struct spi_device *spi, /* the spi->mode bits understood by this driver: */ #define MODEBITS (SPI_CPOL | SPI_CPHA) -static int bcm63xx_spi_setup(struct spi_device *spi) -{ - if (spi->bits_per_word != 8) { - dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n", - __func__, spi->bits_per_word); - return -EINVAL; - } - - return 0; -} - static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *first, unsigned int num_transfers) { @@ -277,13 +266,6 @@ static int bcm63xx_spi_transfer_one(struct spi_master *master, * full-duplex transfers. */ list_for_each_entry(t, &m->transfers, transfer_list) { - if (t->bits_per_word != 8) { - dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n", - __func__, t->bits_per_word); - status = -EINVAL; - goto exit; - } - if (!first) first = t; @@ -430,11 +412,11 @@ static int bcm63xx_spi_probe(struct platform_device *pdev) master->bus_num = pdata->bus_num; master->num_chipselect = pdata->num_chipselect; - master->setup = bcm63xx_spi_setup; master->prepare_transfer_hardware = bcm63xx_spi_prepare_transfer; master->unprepare_transfer_hardware = bcm63xx_spi_unprepare_transfer; master->transfer_one_message = bcm63xx_spi_transfer_one; master->mode_bits = MODEBITS; + master->bits_per_word_mask = SPI_BPW_MASK(8); bs->msg_type_shift = pdata->msg_type_shift; bs->msg_ctl_width = pdata->msg_ctl_width; bs->tx_io = (u8 *)(bs->regs + bcm63xx_spireg(SPI_MSG_DATA)); diff --git a/drivers/spi/spi-bfin-sport.c b/drivers/spi/spi-bfin-sport.c index 39b0d1711b4e..e4a858ad6ef6 100644 --- a/drivers/spi/spi-bfin-sport.c +++ b/drivers/spi/spi-bfin-sport.c @@ -417,7 +417,7 @@ bfin_sport_spi_pump_transfers(unsigned long data) /* Bits per word setup */ bits_per_word = transfer->bits_per_word; - if (bits_per_word % 16 == 0) + if (bits_per_word == 16) drv_data->ops = &bfin_sport_transfer_ops_u16; else drv_data->ops = &bfin_sport_transfer_ops_u8; @@ -600,13 +600,6 @@ bfin_sport_spi_setup(struct spi_device *spi) } } - if (spi->bits_per_word % 8) { - dev_err(&spi->dev, "%d bits_per_word is not supported\n", - spi->bits_per_word); - ret = -EINVAL; - goto error; - } - /* translate common spi framework into our register * following configure contents are same for tx and rx. */ @@ -778,6 +771,7 @@ static int bfin_sport_spi_probe(struct platform_device *pdev) drv_data->pin_req = platform_info->pin_req; master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST; + master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16); master->bus_num = pdev->id; master->num_chipselect = platform_info->num_chipselect; master->cleanup = bfin_sport_spi_cleanup; diff --git a/drivers/spi/spi-bfin5xx.c b/drivers/spi/spi-bfin5xx.c index 317f564c899c..e35c19cff9ca 100644 --- a/drivers/spi/spi-bfin5xx.c +++ b/drivers/spi/spi-bfin5xx.c @@ -643,21 +643,16 @@ static void bfin_spi_pump_transfers(unsigned long data) /* Bits per word setup */ bits_per_word = transfer->bits_per_word; - if (bits_per_word % 16 == 0) { + if (bits_per_word == 16) { drv_data->n_bytes = bits_per_word/8; drv_data->len = (transfer->len) >> 1; cr_width = BIT_CTL_WORDSIZE; drv_data->ops = &bfin_bfin_spi_transfer_ops_u16; - } else if (bits_per_word % 8 == 0) { + } else if (bits_per_word == 8) { drv_data->n_bytes = bits_per_word/8; drv_data->len = transfer->len; cr_width = 0; drv_data->ops = &bfin_bfin_spi_transfer_ops_u8; - } else { - dev_err(&drv_data->pdev->dev, "transfer: unsupported bits_per_word\n"); - message->status = -EINVAL; - bfin_spi_giveback(drv_data); - return; } cr = bfin_read(&drv_data->regs->ctl) & ~(BIT_CTL_TIMOD | BIT_CTL_WORDSIZE); cr |= cr_width; @@ -808,13 +803,13 @@ static void bfin_spi_pump_transfers(unsigned long data) bfin_write(&drv_data->regs->tdbr, chip->idle_tx_val); else { int loop; - if (bits_per_word % 16 == 0) { + if (bits_per_word == 16) { u16 *buf = (u16 *)drv_data->tx; for (loop = 0; loop < bits_per_word / 16; loop++) { bfin_write(&drv_data->regs->tdbr, *buf++); } - } else if (bits_per_word % 8 == 0) { + } else if (bits_per_word == 8) { u8 *buf = (u8 *)drv_data->tx; for (loop = 0; loop < bits_per_word / 8; loop++) bfin_write(&drv_data->regs->tdbr, *buf++); @@ -1033,12 +1028,6 @@ static int bfin_spi_setup(struct spi_device *spi) chip->ctl_reg &= bfin_ctl_reg; } - if (spi->bits_per_word % 8) { - dev_err(&spi->dev, "%d bits_per_word is not supported\n", - spi->bits_per_word); - goto error; - } - /* translate common spi framework into our register */ if (spi->mode & ~(SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST)) { dev_err(&spi->dev, "unsupported spi modes detected\n"); @@ -1299,7 +1288,7 @@ static int bfin_spi_probe(struct platform_device *pdev) /* the spi->mode bits supported by this driver: */ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST; - + master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16); master->bus_num = pdev->id; master->num_chipselect = platform_info->num_chipselect; master->cleanup = bfin_spi_cleanup; diff --git a/drivers/spi/spi-clps711x.c b/drivers/spi/spi-clps711x.c index a11cbf02691a..e322b5bf9910 100644 --- a/drivers/spi/spi-clps711x.c +++ b/drivers/spi/spi-clps711x.c @@ -42,12 +42,6 @@ static int spi_clps711x_setup(struct spi_device *spi) { struct spi_clps711x_data *hw = spi_master_get_devdata(spi->master); - if (spi->bits_per_word != 8) { - dev_err(&spi->dev, "Unsupported master bus width %i\n", - spi->bits_per_word); - return -EINVAL; - } - /* We are expect that SPI-device is not selected */ gpio_direction_output(hw->chipselect[spi->chip_select], !(spi->mode & SPI_CS_HIGH)); @@ -190,6 +184,7 @@ static int spi_clps711x_probe(struct platform_device *pdev) master->bus_num = pdev->id; master->mode_bits = SPI_CPHA | SPI_CS_HIGH; + master->bits_per_word_mask = SPI_BPW_MASK(8); master->num_chipselect = pdata->num_chipselect; master->setup = spi_clps711x_setup; master->transfer_one_message = spi_clps711x_transfer_one_message; diff --git a/drivers/spi/spi-coldfire-qspi.c b/drivers/spi/spi-coldfire-qspi.c index 7b5cc9e4e94d..9a7db9d1b4ca 100644 --- a/drivers/spi/spi-coldfire-qspi.c +++ b/drivers/spi/spi-coldfire-qspi.c @@ -312,10 +312,7 @@ static int mcfqspi_transfer_one_message(struct spi_master *master, bool cs_high = spi->mode & SPI_CS_HIGH; u16 qmr = MCFQSPI_QMR_MSTR; - if (t->bits_per_word) - qmr |= t->bits_per_word << 10; - else - qmr |= spi->bits_per_word << 10; + qmr |= t->bits_per_word << 10; if (spi->mode & SPI_CPHA) qmr |= MCFQSPI_QMR_CPHA; if (spi->mode & SPI_CPOL) @@ -377,11 +374,6 @@ static int mcfqspi_unprepare_transfer_hw(struct spi_master *master) static int mcfqspi_setup(struct spi_device *spi) { - if ((spi->bits_per_word < 8) || (spi->bits_per_word > 16)) { - dev_dbg(&spi->dev, "%d bits per word is not supported\n", - spi->bits_per_word); - return -EINVAL; - } if (spi->chip_select >= spi->master->num_chipselect) { dev_dbg(&spi->dev, "%d chip select is out of range\n", spi->chip_select); @@ -477,6 +469,7 @@ static int mcfqspi_probe(struct platform_device *pdev) mcfqspi->dev = &pdev->dev; master->mode_bits = SPI_CS_HIGH | SPI_CPOL | SPI_CPHA; + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(8, 16); master->setup = mcfqspi_setup; master->transfer_one_message = mcfqspi_transfer_one_message; master->prepare_transfer_hardware = mcfqspi_prepare_transfer_hw; diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c index 50b13c9b1ab6..b141ef221b14 100644 --- a/drivers/spi/spi-davinci.c +++ b/drivers/spi/spi-davinci.c @@ -299,16 +299,15 @@ static int davinci_spi_setup_transfer(struct spi_device *spi, * Assign function pointer to appropriate transfer method * 8bit, 16bit or 32bit transfer */ - if (bits_per_word <= 8 && bits_per_word >= 2) { + if (bits_per_word <= 8) { dspi->get_rx = davinci_spi_rx_buf_u8; dspi->get_tx = davinci_spi_tx_buf_u8; dspi->bytes_per_word[spi->chip_select] = 1; - } else if (bits_per_word <= 16 && bits_per_word >= 2) { + } else { dspi->get_rx = davinci_spi_rx_buf_u16; dspi->get_tx = davinci_spi_tx_buf_u16; dspi->bytes_per_word[spi->chip_select] = 2; - } else - return -EINVAL; + } if (!hz) hz = spi->max_speed_hz; @@ -933,6 +932,7 @@ static int davinci_spi_probe(struct platform_device *pdev) master->dev.of_node = pdev->dev.of_node; master->bus_num = pdev->id; master->num_chipselect = pdata->num_chipselect; + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(2, 16); master->setup = davinci_spi_setup; dspi->bitbang.chipselect = davinci_spi_chipselect; diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index c1abc06899e7..79c958e49f61 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -457,19 +457,7 @@ static void pump_transfers(unsigned long data) } if (transfer->bits_per_word) { bits = transfer->bits_per_word; - - switch (bits) { - case 8: - case 16: - dws->n_bytes = dws->dma_width = bits >> 3; - break; - default: - printk(KERN_ERR "MRST SPI0: unsupported bits:" - "%db\n", bits); - message->status = -EIO; - goto early_exit; - } - + dws->n_bytes = dws->dma_width = bits >> 3; cr0 = (bits - 1) | (chip->type << SPI_FRF_OFFSET) | (spi->mode << SPI_MODE_OFFSET) @@ -629,9 +617,6 @@ static int dw_spi_setup(struct spi_device *spi) struct dw_spi_chip *chip_info = NULL; struct chip_data *chip; - if (spi->bits_per_word != 8 && spi->bits_per_word != 16) - return -EINVAL; - /* Only alloc on first setup */ chip = spi_get_ctldata(spi); if (!chip) { @@ -660,16 +645,12 @@ static int dw_spi_setup(struct spi_device *spi) chip->enable_dma = chip_info->enable_dma; } - if (spi->bits_per_word <= 8) { + if (spi->bits_per_word == 8) { chip->n_bytes = 1; chip->dma_width = 1; - } else if (spi->bits_per_word <= 16) { + } else if (spi->bits_per_word == 16) { chip->n_bytes = 2; chip->dma_width = 2; - } else { - /* Never take >16b case for MRST SPIC */ - dev_err(&spi->dev, "invalid wordsize\n"); - return -EINVAL; } chip->bits_per_word = spi->bits_per_word; @@ -824,6 +805,7 @@ int dw_spi_add_host(struct dw_spi *dws) } master->mode_bits = SPI_CPOL | SPI_CPHA; + master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16); master->bus_num = dws->bus_num; master->num_chipselect = dws->num_cs; master->cleanup = dw_spi_cleanup; diff --git a/drivers/spi/spi-ep93xx.c b/drivers/spi/spi-ep93xx.c index d7bac60253c9..f1b14977abdf 100644 --- a/drivers/spi/spi-ep93xx.c +++ b/drivers/spi/spi-ep93xx.c @@ -296,12 +296,6 @@ static int ep93xx_spi_setup(struct spi_device *spi) struct ep93xx_spi *espi = spi_master_get_devdata(spi->master); struct ep93xx_spi_chip *chip; - if (spi->bits_per_word < 4 || spi->bits_per_word > 16) { - dev_err(&espi->pdev->dev, "invalid bits per word %d\n", - spi->bits_per_word); - return -EINVAL; - } - chip = spi_get_ctldata(spi); if (!chip) { dev_dbg(&espi->pdev->dev, "initial setup for %s\n", @@ -365,10 +359,6 @@ static int ep93xx_spi_transfer(struct spi_device *spi, struct spi_message *msg) /* first validate each transfer */ list_for_each_entry(t, &msg->transfers, transfer_list) { - if (t->bits_per_word) { - if (t->bits_per_word < 4 || t->bits_per_word > 16) - return -EINVAL; - } if (t->speed_hz && t->speed_hz < espi->min_rate) return -EINVAL; } @@ -1046,6 +1036,7 @@ static int ep93xx_spi_probe(struct platform_device *pdev) master->bus_num = pdev->id; master->num_chipselect = info->num_chipselect; master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16); platform_set_drvdata(pdev, master); diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 24610ca8955d..0ef05f198f22 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -144,10 +144,6 @@ static int fsl_espi_setup_transfer(struct spi_device *spi, if (!bits_per_word) bits_per_word = spi->bits_per_word; - /* Make sure its a bit width we support [4..16] */ - if ((bits_per_word < 4) || (bits_per_word > 16)) - return -EINVAL; - if (!hz) hz = spi->max_speed_hz; @@ -157,12 +153,10 @@ static int fsl_espi_setup_transfer(struct spi_device *spi, cs->get_tx = mpc8xxx_spi_tx_buf_u32; if (bits_per_word <= 8) { cs->rx_shift = 8 - bits_per_word; - } else if (bits_per_word <= 16) { + } else cs->rx_shift = 16 - bits_per_word; if (spi->mode & SPI_LSB_FIRST) cs->get_tx = fsl_espi_tx_buf_lsb; - } else { - return -EINVAL; } mpc8xxx_spi->rx_shift = cs->rx_shift; @@ -609,6 +603,7 @@ static struct spi_master * fsl_espi_probe(struct device *dev, if (ret) goto err_probe; + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16); master->setup = fsl_espi_setup; mpc8xxx_spi = spi_master_get_devdata(master); diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index 0021fc4c45bc..a7801a889ff9 100644 --- a/drivers/spi/spi-gpio.c +++ b/drivers/spi/spi-gpio.c @@ -239,9 +239,6 @@ static int spi_gpio_setup(struct spi_device *spi) struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi); struct device_node *np = spi->master->dev.of_node; - if (spi->bits_per_word > 32) - return -EINVAL; - if (np) { /* * In DT environments, the CS GPIOs have already been @@ -446,6 +443,7 @@ static int spi_gpio_probe(struct platform_device *pdev) if (pdata) spi_gpio->pdata = *pdata; + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32); master->flags = master_flags; master->bus_num = pdev->id; master->num_chipselect = SPI_N_CHIPSEL; diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 0befeeb522f4..cb88eeb74cc4 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -698,11 +698,10 @@ static int spi_imx_setupxfer(struct spi_device *spi, } else if (config.bpw <= 16) { spi_imx->rx = spi_imx_buf_rx_u16; spi_imx->tx = spi_imx_buf_tx_u16; - } else if (config.bpw <= 32) { + } spi_imx->rx = spi_imx_buf_rx_u32; spi_imx->tx = spi_imx_buf_tx_u32; - } else - BUG(); + } spi_imx->devtype_data->config(spi_imx, &config); @@ -783,6 +782,7 @@ static int spi_imx_probe(struct platform_device *pdev) platform_set_drvdata(pdev, master); + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32); master->bus_num = pdev->id; master->num_chipselect = num_cs; diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index 84982768cd10..62b7e8ee1163 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -75,12 +75,6 @@ static int mxs_spi_setup_transfer(struct spi_device *dev, if (t && t->bits_per_word) bits_per_word = t->bits_per_word; - if (bits_per_word != 8) { - dev_err(&dev->dev, "%s, unsupported bits_per_word=%d\n", - __func__, bits_per_word); - return -EINVAL; - } - hz = dev->max_speed_hz; if (t && t->speed_hz) hz = min(hz, t->speed_hz); @@ -548,6 +542,7 @@ static int mxs_spi_probe(struct platform_device *pdev) master->transfer_one_message = mxs_spi_transfer_one; master->setup = mxs_spi_setup; + master->bits_per_word_mask = SPI_BPW_MASK(8); master->mode_bits = SPI_CPOL | SPI_CPHA; master->num_chipselect = 3; master->dev.of_node = np; diff --git a/drivers/spi/spi-nuc900.c b/drivers/spi/spi-nuc900.c index b3f9ec83ef73..94efa2c6298e 100644 --- a/drivers/spi/spi-nuc900.c +++ b/drivers/spi/spi-nuc900.c @@ -174,17 +174,6 @@ static void nuc900_spi_gobusy(struct nuc900_spi *hw) spin_unlock_irqrestore(&hw->lock, flags); } -static int nuc900_spi_setupxfer(struct spi_device *spi, - struct spi_transfer *t) -{ - return 0; -} - -static int nuc900_spi_setup(struct spi_device *spi) -{ - return 0; -} - static inline unsigned int hw_txbyte(struct nuc900_spi *hw, int count) { return hw->tx ? hw->tx[count] : 0; @@ -377,10 +366,8 @@ static int nuc900_spi_probe(struct platform_device *pdev) master->num_chipselect = hw->pdata->num_cs; master->bus_num = hw->pdata->bus_num; hw->bitbang.master = hw->master; - hw->bitbang.setup_transfer = nuc900_spi_setupxfer; hw->bitbang.chipselect = nuc900_spi_chipsel; hw->bitbang.txrx_bufs = nuc900_spi_txrx; - hw->bitbang.master->setup = nuc900_spi_setup; hw->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (hw->res == NULL) { diff --git a/drivers/spi/spi-omap-100k.c b/drivers/spi/spi-omap-100k.c index 78d29a18dcc4..9137fcc4f99b 100644 --- a/drivers/spi/spi-omap-100k.c +++ b/drivers/spi/spi-omap-100k.c @@ -298,12 +298,6 @@ static int omap1_spi100k_setup(struct spi_device *spi) struct omap1_spi100k *spi100k; struct omap1_spi100k_cs *cs = spi->controller_state; - if (spi->bits_per_word < 4 || spi->bits_per_word > 32) { - dev_dbg(&spi->dev, "setup: unsupported %d bit words\n", - spi->bits_per_word); - return -EINVAL; - } - spi100k = spi_master_get_devdata(spi->master); if (!cs) { @@ -451,10 +445,7 @@ static int omap1_spi100k_transfer(struct spi_device *spi, struct spi_message *m) unsigned len = t->len; if (t->speed_hz > OMAP1_SPI100K_MAX_FREQ - || (len && !(rx_buf || tx_buf)) - || (t->bits_per_word && - ( t->bits_per_word < 4 - || t->bits_per_word > 32))) { + || (len && !(rx_buf || tx_buf))) { dev_dbg(&spi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n", t->speed_hz, len, @@ -509,6 +500,7 @@ static int omap1_spi100k_probe(struct platform_device *pdev) master->cleanup = NULL; master->num_chipselect = 2; master->mode_bits = MODEBITS; + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); dev_set_drvdata(&pdev->dev, master); diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 86d2158946bb..6246fb455774 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -857,12 +857,6 @@ static int omap2_mcspi_setup(struct spi_device *spi) struct omap2_mcspi_dma *mcspi_dma; struct omap2_mcspi_cs *cs = spi->controller_state; - if (spi->bits_per_word < 4 || spi->bits_per_word > 32) { - dev_dbg(&spi->dev, "setup: unsupported %d bit words\n", - spi->bits_per_word); - return -EINVAL; - } - mcspi_dma = &mcspi->dma_channels[spi->chip_select]; if (!cs) { @@ -1072,10 +1066,7 @@ static int omap2_mcspi_transfer_one_message(struct spi_master *master, unsigned len = t->len; if (t->speed_hz > OMAP2_MCSPI_MAX_FREQ - || (len && !(rx_buf || tx_buf)) - || (t->bits_per_word && - ( t->bits_per_word < 4 - || t->bits_per_word > 32))) { + || (len && !(rx_buf || tx_buf))) { dev_dbg(mcspi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n", t->speed_hz, len, @@ -1196,7 +1187,7 @@ static int omap2_mcspi_probe(struct platform_device *pdev) /* the spi->mode bits understood by this driver: */ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; - + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); master->setup = omap2_mcspi_setup; master->prepare_transfer_hardware = omap2_prepare_transfer; master->unprepare_transfer_hardware = omap2_unprepare_transfer; diff --git a/drivers/spi/spi-ppc4xx.c b/drivers/spi/spi-ppc4xx.c index 357f183a4fb7..01df34bed2d0 100644 --- a/drivers/spi/spi-ppc4xx.c +++ b/drivers/spi/spi-ppc4xx.c @@ -190,12 +190,6 @@ static int spi_ppc4xx_setupxfer(struct spi_device *spi, struct spi_transfer *t) speed = min(t->speed_hz, spi->max_speed_hz); } - if (bits_per_word != 8) { - dev_err(&spi->dev, "invalid bits-per-word (%d)\n", - bits_per_word); - return -EINVAL; - } - if (!speed || (speed > spi->max_speed_hz)) { dev_err(&spi->dev, "invalid speed_hz (%d)\n", speed); return -EINVAL; @@ -229,12 +223,6 @@ static int spi_ppc4xx_setup(struct spi_device *spi) { struct spi_ppc4xx_cs *cs = spi->controller_state; - if (spi->bits_per_word != 8) { - dev_err(&spi->dev, "invalid bits-per-word (%d)\n", - spi->bits_per_word); - return -EINVAL; - } - if (!spi->max_speed_hz) { dev_err(&spi->dev, "invalid max_speed_hz (must be non-zero)\n"); return -EINVAL; @@ -465,6 +453,7 @@ static int spi_ppc4xx_of_probe(struct platform_device *op) bbp->use_dma = 0; bbp->master->setup = spi_ppc4xx_setup; bbp->master->cleanup = spi_ppc4xx_cleanup; + bbp->master->bits_per_word_mask = SPI_BPW_MASK(8); /* the spi->mode bits understood by this driver: */ bbp->master->mode_bits = diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index f5d84d6f8222..7de2ca1c4ee3 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -881,21 +881,6 @@ static int setup(struct spi_device *spi) rx_thres = RX_THRESH_DFLT; } - if (!pxa25x_ssp_comp(drv_data) - && (spi->bits_per_word < 4 || spi->bits_per_word > 32)) { - dev_err(&spi->dev, "failed setup: ssp_type=%d, bits/wrd=%d " - "b/w not 4-32 for type non-PXA25x_SSP\n", - drv_data->ssp_type, spi->bits_per_word); - return -EINVAL; - } else if (pxa25x_ssp_comp(drv_data) - && (spi->bits_per_word < 4 - || spi->bits_per_word > 16)) { - dev_err(&spi->dev, "failed setup: ssp_type=%d, bits/wrd=%d " - "b/w not 4-16 for type PXA25x_SSP\n", - drv_data->ssp_type, spi->bits_per_word); - return -EINVAL; - } - /* Only alloc on first setup */ chip = spi_get_ctldata(spi); if (!chip) { @@ -1011,9 +996,6 @@ static int setup(struct spi_device *spi) chip->n_bytes = 4; chip->read = u32_reader; chip->write = u32_writer; - } else { - dev_err(&spi->dev, "invalid wordsize\n"); - return -ENODEV; } chip->bits_per_word = spi->bits_per_word; @@ -1190,11 +1172,13 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) drv_data->ioaddr = ssp->mmio_base; drv_data->ssdr_physical = ssp->phys_base + SSDR; if (pxa25x_ssp_comp(drv_data)) { + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16); drv_data->int_cr1 = SSCR1_TIE | SSCR1_RIE; drv_data->dma_cr1 = 0; drv_data->clear_sr = SSSR_ROR; drv_data->mask_sr = SSSR_RFS | SSSR_TFS | SSSR_ROR; } else { + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); drv_data->int_cr1 = SSCR1_TIE | SSCR1_RIE | SSCR1_TINTE; drv_data->dma_cr1 = DEFAULT_DMA_CR1; drv_data->clear_sr = SSSR_ROR | SSSR_TINT; diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 5000586cb98d..39a3824ec3d7 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -1314,7 +1314,8 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) master->unprepare_transfer_hardware = s3c64xx_spi_unprepare_transfer; master->num_chipselect = sci->num_cs; master->dma_alignment = 8; - master->bits_per_word_mask = BIT(32 - 1) | BIT(16 - 1) | BIT(8 - 1); + master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) | + SPI_BPW_MASK(8); /* the spi->mode bits understood by this driver: */ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; diff --git a/drivers/spi/spi-sirf.c b/drivers/spi/spi-sirf.c index 0808cd56bf8d..77497be042a8 100644 --- a/drivers/spi/spi-sirf.c +++ b/drivers/spi/spi-sirf.c @@ -425,10 +425,6 @@ spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t) rxfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) | SIRFSOC_SPI_FIFO_WIDTH_DWORD; break; - default: - dev_err(&spi->dev, "Bits per word %d not supported\n", - bits_per_word); - return -EINVAL; } if (!(spi->mode & SPI_CS_HIGH)) @@ -556,6 +552,8 @@ static int spi_sirfsoc_probe(struct platform_device *pdev) sspi->bitbang.txrx_bufs = spi_sirfsoc_transfer; sspi->bitbang.master->setup = spi_sirfsoc_setup; master->bus_num = pdev->id; + master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(12) | + SPI_BPW_MASK(16) | SPI_BPW_MASK(32); sspi->bitbang.master->dev.of_node = pdev->dev.of_node; sspi->p = pinctrl_get_select_default(&pdev->dev); diff --git a/drivers/spi/spi-ti-ssp.c b/drivers/spi/spi-ti-ssp.c index 46992cab65f1..10606fcc6efc 100644 --- a/drivers/spi/spi-ti-ssp.c +++ b/drivers/spi/spi-ti-ssp.c @@ -237,14 +237,6 @@ static void ti_ssp_spi_work(struct work_struct *work) spin_unlock(&hw->lock); } -static int ti_ssp_spi_setup(struct spi_device *spi) -{ - if (spi->bits_per_word > 32) - return -EINVAL; - - return 0; -} - static int ti_ssp_spi_transfer(struct spi_device *spi, struct spi_message *m) { struct ti_ssp_spi *hw; @@ -269,12 +261,6 @@ static int ti_ssp_spi_transfer(struct spi_device *spi, struct spi_message *m) dev_err(&spi->dev, "invalid xfer, full duplex\n"); return -EINVAL; } - - if (t->bits_per_word > 32) { - dev_err(&spi->dev, "invalid xfer width %d\n", - t->bits_per_word); - return -EINVAL; - } } spin_lock(&hw->lock); @@ -337,8 +323,8 @@ static int ti_ssp_spi_probe(struct platform_device *pdev) master->bus_num = pdev->id; master->num_chipselect = pdata->num_cs; master->mode_bits = MODE_BITS; + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32); master->flags = SPI_MASTER_HALF_DUPLEX; - master->setup = ti_ssp_spi_setup; master->transfer = ti_ssp_spi_transfer; error = spi_register_master(master); diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c index 35f60bd252dd..15cb39b0549e 100644 --- a/drivers/spi/spi-topcliff-pch.c +++ b/drivers/spi/spi-topcliff-pch.c @@ -472,11 +472,6 @@ static int pch_spi_setup(struct spi_device *pspi) dev_dbg(&pspi->dev, "%s 8 bits per word\n", __func__); } - if ((pspi->bits_per_word != 8) && (pspi->bits_per_word != 16)) { - dev_err(&pspi->dev, "%s Invalid bits per word\n", __func__); - return -EINVAL; - } - /* Check baud rate setting */ /* if baud rate of chip is greater than max we can support,return error */ @@ -537,17 +532,6 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg) /* if baud rate has been specified validate the same */ if (transfer->speed_hz > PCH_MAX_BAUDRATE) transfer->speed_hz = PCH_MAX_BAUDRATE; - - /* if bits per word has been specified validate the same */ - if (transfer->bits_per_word) { - if ((transfer->bits_per_word != 8) - && (transfer->bits_per_word != 16)) { - retval = -EINVAL; - dev_err(&pspi->dev, - "%s Invalid bits per word\n", __func__); - goto err_return_spinlock; - } - } } spin_unlock_irqrestore(&data->lock, flags); @@ -1442,6 +1426,7 @@ static int pch_spi_pd_probe(struct platform_device *plat_dev) master->setup = pch_spi_setup; master->transfer = pch_spi_transfer; master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST; + master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16); data->board_dat = board_dat; data->plat_dev = plat_dev; diff --git a/drivers/spi/spi-txx9.c b/drivers/spi/spi-txx9.c index adb853047926..cefd9568f098 100644 --- a/drivers/spi/spi-txx9.c +++ b/drivers/spi/spi-txx9.c @@ -116,17 +116,12 @@ static void txx9spi_cs_func(struct spi_device *spi, struct txx9spi *c, static int txx9spi_setup(struct spi_device *spi) { struct txx9spi *c = spi_master_get_devdata(spi->master); - u8 bits_per_word; if (!spi->max_speed_hz || spi->max_speed_hz > c->max_speed_hz || spi->max_speed_hz < c->min_speed_hz) return -EINVAL; - bits_per_word = spi->bits_per_word; - if (bits_per_word != 8 && bits_per_word != 16) - return -EINVAL; - if (gpio_direction_output(spi->chip_select, !(spi->mode & SPI_CS_HIGH))) { dev_err(&spi->dev, "Cannot setup GPIO for chipselect.\n"); @@ -319,8 +314,6 @@ static int txx9spi_transfer(struct spi_device *spi, struct spi_message *m) if (!t->tx_buf && !t->rx_buf && t->len) return -EINVAL; - if (bits_per_word != 8 && bits_per_word != 16) - return -EINVAL; if (t->len & ((bits_per_word >> 3) - 1)) return -EINVAL; if (speed_hz < c->min_speed_hz || speed_hz > c->max_speed_hz) @@ -411,6 +404,7 @@ static int txx9spi_probe(struct platform_device *dev) master->setup = txx9spi_setup; master->transfer = txx9spi_transfer; master->num_chipselect = (u16)UINT_MAX; /* any GPIO numbers */ + master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16); ret = spi_register_master(master); if (ret) diff --git a/drivers/spi/spi-xcomm.c b/drivers/spi/spi-xcomm.c index 4d3ec8b9f479..4258c712ad3c 100644 --- a/drivers/spi/spi-xcomm.c +++ b/drivers/spi/spi-xcomm.c @@ -76,7 +76,7 @@ static int spi_xcomm_setup_transfer(struct spi_xcomm *spi_xcomm, { unsigned int speed; - if ((t->bits_per_word && t->bits_per_word != 8) || t->len > 62) + if (t->len > 62) return -EINVAL; speed = t->speed_hz ? t->speed_hz : spi->max_speed_hz; @@ -209,14 +209,6 @@ static int spi_xcomm_transfer_one(struct spi_master *master, return status; } -static int spi_xcomm_setup(struct spi_device *spi) -{ - if (spi->bits_per_word != 8) - return -EINVAL; - - return 0; -} - static int spi_xcomm_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -233,8 +225,8 @@ static int spi_xcomm_probe(struct i2c_client *i2c, master->num_chipselect = 16; master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_3WIRE; + master->bits_per_word_mask = SPI_BPW_MASK(8); master->flags = SPI_MASTER_HALF_DUPLEX; - master->setup = spi_xcomm_setup; master->transfer_one_message = spi_xcomm_transfer_one; master->dev.of_node = i2c->dev.of_node; i2c_set_clientdata(i2c, master); diff --git a/drivers/spi/spi-xilinx.c b/drivers/spi/spi-xilinx.c index e1d769607425..f321bf95d7c3 100644 --- a/drivers/spi/spi-xilinx.c +++ b/drivers/spi/spi-xilinx.c @@ -232,21 +232,6 @@ static int xilinx_spi_setup_transfer(struct spi_device *spi, return 0; } -static int xilinx_spi_setup(struct spi_device *spi) -{ - /* always return 0, we can not check the number of bits. - * There are cases when SPI setup is called before any driver is - * there, in that case the SPI core defaults to 8 bits, which we - * do not support in some cases. But if we return an error, the - * SPI device would not be registered and no driver can get hold of it - * When the driver is there, it will call SPI setup again with the - * correct number of bits per transfer. - * If a driver setups with the wrong bit number, it will fail when - * it tries to do a transfer - */ - return 0; -} - static void xilinx_spi_fill_tx_fifo(struct xilinx_spi *xspi) { u8 sr; @@ -377,7 +362,6 @@ struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem, xspi->bitbang.chipselect = xilinx_spi_chipselect; xspi->bitbang.setup_transfer = xilinx_spi_setup_transfer; xspi->bitbang.txrx_bufs = xilinx_spi_txrx_bufs; - xspi->bitbang.master->setup = xilinx_spi_setup; init_completion(&xspi->done); if (!request_mem_region(mem->start, resource_size(mem), -- cgit v1.2.3 From debff6184c32149bd08cfecfafbebb96201be37d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 14 May 2013 13:53:45 +0200 Subject: iwlwifi: mvm: implement D3 testing For testing the D3 (WoWLAN) firmware, it is useful to be able to run the firmware with instrumentation while the host isn't sleeping and can poke at the firmware debug logging etc. Implement this by a debugfs file. When the file is opened the D3 firmware is loaded and all regular commands are blocked. While the file is being read, poll the firmware's PME status flag and report EOF once it changes to non-zero. When it is closed, do (most of) the resume processing. This lets a user just "cat" the file. Pressing Ctrl-C to kill the cat process will resume the firwmare as though the platform resumed for non-wireless reason and when the firmware wants to wake up reading from the file automatically completes. Unlike in real suspend, only disable interrupts and don't reset the TX/RX hardware while in the test mode. This is a workaround for some interrupt problems that happen only when the PCIe link isn't fully reset (presumably by changing the PCI config space registers which the core PCI code does.) Note that while regular operations are blocked from sending commands to the firmware, they could still be made and cause strange mac80211 issues. Therefore, while using this testing feature you need to be careful to not try to disconnect, roam or similar, and will see warnings for such attempts. Als note that this requires an upcoming firmware change to tell the driver the location of the PME status flag in SRAM. D3 test will fail if the firmware doesn't report the pointer. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/mac80211.c | 4 +- drivers/net/wireless/iwlwifi/iwl-trans.h | 14 +-- drivers/net/wireless/iwlwifi/mvm/d3.c | 149 ++++++++++++++++++++++++++-- drivers/net/wireless/iwlwifi/mvm/debugfs.c | 1 + drivers/net/wireless/iwlwifi/mvm/mvm.h | 3 + drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c | 3 +- drivers/net/wireless/iwlwifi/mvm/utils.c | 10 ++ drivers/net/wireless/iwlwifi/pcie/trans.c | 19 +++- 8 files changed, 182 insertions(+), 21 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index ac7ed3f809a0..c0039a992909 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -430,7 +430,7 @@ static int iwlagn_mac_suspend(struct ieee80211_hw *hw, iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_SET, CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE); - iwl_trans_d3_suspend(priv->trans); + iwl_trans_d3_suspend(priv->trans, false); goto out; @@ -504,7 +504,7 @@ static int iwlagn_mac_resume(struct ieee80211_hw *hw) /* we'll clear ctx->vif during iwlagn_prepare_restart() */ vif = ctx->vif; - ret = iwl_trans_d3_resume(priv->trans, &d3_status); + ret = iwl_trans_d3_resume(priv->trans, &d3_status, false); if (ret) goto out_unlock; diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 84f1c8dc9741..be4b2ac3dbbf 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -428,8 +428,9 @@ struct iwl_trans_ops { void (*fw_alive)(struct iwl_trans *trans, u32 scd_addr); void (*stop_device)(struct iwl_trans *trans); - void (*d3_suspend)(struct iwl_trans *trans); - int (*d3_resume)(struct iwl_trans *trans, enum iwl_d3_status *status); + void (*d3_suspend)(struct iwl_trans *trans, bool test); + int (*d3_resume)(struct iwl_trans *trans, enum iwl_d3_status *status, + bool test); int (*send_cmd)(struct iwl_trans *trans, struct iwl_host_cmd *cmd); @@ -588,17 +589,18 @@ static inline void iwl_trans_stop_device(struct iwl_trans *trans) trans->state = IWL_TRANS_NO_FW; } -static inline void iwl_trans_d3_suspend(struct iwl_trans *trans) +static inline void iwl_trans_d3_suspend(struct iwl_trans *trans, bool test) { might_sleep(); - trans->ops->d3_suspend(trans); + trans->ops->d3_suspend(trans, test); } static inline int iwl_trans_d3_resume(struct iwl_trans *trans, - enum iwl_d3_status *status) + enum iwl_d3_status *status, + bool test) { might_sleep(); - return trans->ops->d3_resume(trans, status); + return trans->ops->d3_resume(trans, status, test); } static inline int iwl_trans_send_cmd(struct iwl_trans *trans, diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c index 4d3c978b5c76..7a2ef3f013fd 100644 --- a/drivers/net/wireless/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/iwlwifi/mvm/d3.c @@ -63,6 +63,7 @@ #include #include +#include #include #include #include @@ -756,7 +757,9 @@ static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif, return 0; } -int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) +static int __iwl_mvm_suspend(struct ieee80211_hw *hw, + struct cfg80211_wowlan *wowlan, + bool test) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); struct iwl_d3_iter_data suspend_iter_data = { @@ -769,7 +772,7 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) struct iwl_wowlan_config_cmd wowlan_config_cmd = {}; struct iwl_wowlan_kek_kck_material_cmd kek_kck_cmd = {}; struct iwl_wowlan_tkip_params_cmd tkip_cmd = {}; - struct iwl_d3_manager_config d3_cfg_cmd = { + struct iwl_d3_manager_config d3_cfg_cmd_data = { /* * Program the minimum sleep time to 10 seconds, as many * platforms have issues processing a wakeup signal while @@ -777,17 +780,30 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) */ .min_sleep_time = cpu_to_le32(10 * 1000 * 1000), }; + struct iwl_host_cmd d3_cfg_cmd = { + .id = D3_CONFIG_CMD, + .flags = CMD_SYNC | CMD_WANT_SKB, + .data[0] = &d3_cfg_cmd_data, + .len[0] = sizeof(d3_cfg_cmd_data), + }; struct wowlan_key_data key_data = { .use_rsc_tsc = false, .tkip = &tkip_cmd, .use_tkip = false, }; int ret, i; + int len __maybe_unused; u16 seq; u8 old_aux_sta_id, old_ap_sta_id = IWL_MVM_STATION_COUNT; - if (WARN_ON(!wowlan)) + if (!wowlan) { + /* + * mac80211 shouldn't get here, but for D3 test + * it doesn't warrant a warning + */ + WARN_ON(!test); return -EINVAL; + } key_data.rsc_tsc = kzalloc(sizeof(*key_data.rsc_tsc), GFP_KERNEL); if (!key_data.rsc_tsc) @@ -1012,14 +1028,26 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) goto out; /* must be last -- this switches firmware state */ - ret = iwl_mvm_send_cmd_pdu(mvm, D3_CONFIG_CMD, CMD_SYNC, - sizeof(d3_cfg_cmd), &d3_cfg_cmd); + ret = iwl_mvm_send_cmd(mvm, &d3_cfg_cmd); if (ret) goto out; +#ifdef CONFIG_IWLWIFI_DEBUGFS + len = le32_to_cpu(d3_cfg_cmd.resp_pkt->len_n_flags) & + FH_RSCSR_FRAME_SIZE_MSK; + if (len >= sizeof(u32) * 2) { + mvm->d3_test_pme_ptr = + le32_to_cpup((__le32 *)d3_cfg_cmd.resp_pkt->data); + } else if (test) { + /* in test mode we require the pointer */ + ret = -EIO; + goto out; + } +#endif + iwl_free_resp(&d3_cfg_cmd); clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); - iwl_trans_d3_suspend(mvm->trans); + iwl_trans_d3_suspend(mvm->trans, test); out: mvm->aux_sta.sta_id = old_aux_sta_id; mvm_ap_sta->sta_id = old_ap_sta_id; @@ -1034,6 +1062,11 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) return ret; } +int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) +{ + return __iwl_mvm_suspend(hw, wowlan, false); +} + static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { @@ -1238,9 +1271,8 @@ static void iwl_mvm_read_d3_sram(struct iwl_mvm *mvm) #endif } -int iwl_mvm_resume(struct ieee80211_hw *hw) +static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) { - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); struct iwl_d3_iter_data resume_iter_data = { .mvm = mvm, }; @@ -1260,7 +1292,7 @@ int iwl_mvm_resume(struct ieee80211_hw *hw) vif = resume_iter_data.vif; - ret = iwl_trans_d3_resume(mvm->trans, &d3_status); + ret = iwl_trans_d3_resume(mvm->trans, &d3_status, test); if (ret) goto out_unlock; @@ -1277,7 +1309,7 @@ int iwl_mvm_resume(struct ieee80211_hw *hw) out_unlock: mutex_unlock(&mvm->mutex); - if (vif) + if (!test && vif) ieee80211_resume_disconnect(vif); /* return 1 to reconfigure the device */ @@ -1285,9 +1317,106 @@ int iwl_mvm_resume(struct ieee80211_hw *hw) return 1; } +int iwl_mvm_resume(struct ieee80211_hw *hw) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + + return __iwl_mvm_resume(mvm, false); +} + void iwl_mvm_set_wakeup(struct ieee80211_hw *hw, bool enabled) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); device_set_wakeup_enable(mvm->trans->dev, enabled); } + +#ifdef CONFIG_IWLWIFI_DEBUGFS +static int iwl_mvm_d3_test_open(struct inode *inode, struct file *file) +{ + struct iwl_mvm *mvm = inode->i_private; + int err; + + if (mvm->d3_test_active) + return -EBUSY; + + file->private_data = inode->i_private; + + ieee80211_stop_queues(mvm->hw); + synchronize_net(); + + /* start pseudo D3 */ + rtnl_lock(); + err = __iwl_mvm_suspend(mvm->hw, mvm->hw->wiphy->wowlan_config, true); + rtnl_unlock(); + if (err > 0) + err = -EINVAL; + if (err) { + ieee80211_wake_queues(mvm->hw); + return err; + } + mvm->d3_test_active = true; + return 0; +} + +static ssize_t iwl_mvm_d3_test_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + u32 pme_asserted; + + while (true) { + pme_asserted = iwl_trans_read_mem32(mvm->trans, + mvm->d3_test_pme_ptr); + if (pme_asserted) + break; + if (msleep_interruptible(100)) + break; + } + + return 0; +} + +static void iwl_mvm_d3_test_disconn_work_iter(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + if (vif->type == NL80211_IFTYPE_STATION) + ieee80211_connection_loss(vif); +} + +static int iwl_mvm_d3_test_release(struct inode *inode, struct file *file) +{ + struct iwl_mvm *mvm = inode->i_private; + int remaining_time = 10; + + mvm->d3_test_active = false; + __iwl_mvm_resume(mvm, true); + iwl_abort_notification_waits(&mvm->notif_wait); + ieee80211_restart_hw(mvm->hw); + + /* wait for restart and disconnect all interfaces */ + while (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) && + remaining_time > 0) { + remaining_time--; + msleep(1000); + } + + if (remaining_time == 0) + IWL_ERR(mvm, "Timed out waiting for HW restart to finish!\n"); + + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_d3_test_disconn_work_iter, NULL); + + ieee80211_wake_queues(mvm->hw); + + return 0; +} + +const struct file_operations iwl_dbgfs_d3_test_ops = { + .llseek = no_llseek, + .open = iwl_mvm_d3_test_open, + .read = iwl_mvm_d3_test_read, + .release = iwl_mvm_d3_test_release, +}; +#endif diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c index 69e0806075a2..b7643c16201f 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c @@ -938,6 +938,7 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) MVM_DEBUGFS_ADD_FILE(fw_restart, mvm->debugfs_dir, S_IWUSR); #ifdef CONFIG_PM_SLEEP MVM_DEBUGFS_ADD_FILE(d3_sram, mvm->debugfs_dir, S_IRUSR | S_IWUSR); + MVM_DEBUGFS_ADD_FILE(d3_test, mvm->debugfs_dir, S_IRUSR); #endif /* diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index 6a3220b6273e..384259baf525 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -459,8 +459,10 @@ struct iwl_mvm { #ifdef CONFIG_PM_SLEEP int gtk_ivlen, gtk_icvlen, ptk_ivlen, ptk_icvlen; #ifdef CONFIG_IWLWIFI_DEBUGFS + bool d3_test_active; bool store_d3_resume_sram; void *d3_resume_sram; + u32 d3_test_pme_ptr; #endif #endif @@ -669,6 +671,7 @@ void iwl_mvm_ipv6_addr_change(struct ieee80211_hw *hw, struct inet6_dev *idev); void iwl_mvm_set_default_unicast_key(struct ieee80211_hw *hw, struct ieee80211_vif *vif, int idx); +extern const struct file_operations iwl_dbgfs_d3_test_ops; /* BT Coex */ int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm); diff --git a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c index 1b4db25d53fb..a8652ddd6bed 100644 --- a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c @@ -204,7 +204,8 @@ int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, { int ret; - WARN_ON(ctxt->ref); + WARN_ON(!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) && + ctxt->ref); lockdep_assert_held(&mvm->mutex); ctxt->channel = chandef->chan; diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c index c9b44ab4af07..1e1332839e4a 100644 --- a/drivers/net/wireless/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/iwlwifi/mvm/utils.c @@ -76,6 +76,11 @@ int iwl_mvm_send_cmd(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd) { int ret; +#if defined(CONFIG_IWLWIFI_DEBUGFS) && defined(CONFIG_PM_SLEEP) + if (WARN_ON(mvm->d3_test_active)) + return -EIO; +#endif + /* * Synchronous commands from this op-mode must hold * the mutex, this ensures we don't try to send two @@ -125,6 +130,11 @@ int iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd, lockdep_assert_held(&mvm->mutex); +#if defined(CONFIG_IWLWIFI_DEBUGFS) && defined(CONFIG_PM_SLEEP) + if (WARN_ON(mvm->d3_test_active)) + return -EIO; +#endif + /* * Only synchronous commands can wait for status, * we use WANT_SKB so the caller can't. diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 0b021305eedf..197dbe0a868c 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -578,9 +578,17 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) clear_bit(STATUS_RFKILL, &trans_pcie->status); } -static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans) +static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test) { iwl_disable_interrupts(trans); + + /* + * in testing mode, the host stays awake and the + * hardware won't be reset (not even partially) + */ + if (test) + return; + iwl_pcie_disable_ict(trans); iwl_clear_bit(trans, CSR_GP_CNTRL, @@ -599,11 +607,18 @@ static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans) } static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans, - enum iwl_d3_status *status) + enum iwl_d3_status *status, + bool test) { u32 val; int ret; + if (test) { + iwl_enable_interrupts(trans); + *status = IWL_D3_STATUS_ALIVE; + return 0; + } + iwl_pcie_set_pwr(trans, false); val = iwl_read32(trans, CSR_RESET); -- cgit v1.2.3 From 6cffe00f7d4e24679eae6b7aae4caaf915288256 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 15 May 2013 14:38:11 -0700 Subject: alarmtimer: Add functions for timerfd support Add functions needed for hooking up alarmtimer to timerfd: * alarm_restart: Similar to hrtimer_restart, restart an alarmtimer after the expires time has already been updated (as with alarm_forward). * alarm_forward_now: Similar to hrtimer_forward_now, move the expires time forward to an interval from the current time of the associated clock. * alarm_start_relative: Start an alarmtimer with an expires time relative to the current time of the associated clock. * alarm_expires_remaining: Similar to hrtimer_expires_remaining, return the amount of time remaining until alarm expiry. Signed-off-by: Todd Poynor Signed-off-by: John Stultz --- include/linux/alarmtimer.h | 4 ++++ kernel/time/alarmtimer.c | 39 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/include/linux/alarmtimer.h b/include/linux/alarmtimer.h index 9069694e70eb..a899402a5a0e 100644 --- a/include/linux/alarmtimer.h +++ b/include/linux/alarmtimer.h @@ -44,10 +44,14 @@ struct alarm { void alarm_init(struct alarm *alarm, enum alarmtimer_type type, enum alarmtimer_restart (*function)(struct alarm *, ktime_t)); int alarm_start(struct alarm *alarm, ktime_t start); +int alarm_start_relative(struct alarm *alarm, ktime_t start); +void alarm_restart(struct alarm *alarm); int alarm_try_to_cancel(struct alarm *alarm); int alarm_cancel(struct alarm *alarm); u64 alarm_forward(struct alarm *alarm, ktime_t now, ktime_t interval); +u64 alarm_forward_now(struct alarm *alarm, ktime_t interval); +ktime_t alarm_expires_remaining(const struct alarm *alarm); /* Provide way to access the rtc device being used by alarmtimers */ struct rtc_device *alarmtimer_get_rtcdev(void); diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c index f11d83b12949..3e5cba274475 100644 --- a/kernel/time/alarmtimer.c +++ b/kernel/time/alarmtimer.c @@ -199,6 +199,12 @@ static enum hrtimer_restart alarmtimer_fired(struct hrtimer *timer) } +ktime_t alarm_expires_remaining(const struct alarm *alarm) +{ + struct alarm_base *base = &alarm_bases[alarm->type]; + return ktime_sub(alarm->node.expires, base->gettime()); +} + #ifdef CONFIG_RTC_CLASS /** * alarmtimer_suspend - Suspend time callback @@ -305,7 +311,7 @@ void alarm_init(struct alarm *alarm, enum alarmtimer_type type, } /** - * alarm_start - Sets an alarm to fire + * alarm_start - Sets an absolute alarm to fire * @alarm: ptr to alarm to set * @start: time to run the alarm */ @@ -324,6 +330,31 @@ int alarm_start(struct alarm *alarm, ktime_t start) return ret; } +/** + * alarm_start_relative - Sets a relative alarm to fire + * @alarm: ptr to alarm to set + * @start: time relative to now to run the alarm + */ +int alarm_start_relative(struct alarm *alarm, ktime_t start) +{ + struct alarm_base *base = &alarm_bases[alarm->type]; + + start = ktime_add(start, base->gettime()); + return alarm_start(alarm, start); +} + +void alarm_restart(struct alarm *alarm) +{ + struct alarm_base *base = &alarm_bases[alarm->type]; + unsigned long flags; + + spin_lock_irqsave(&base->lock, flags); + hrtimer_set_expires(&alarm->timer, alarm->node.expires); + hrtimer_restart(&alarm->timer); + alarmtimer_enqueue(base, alarm); + spin_unlock_irqrestore(&base->lock, flags); +} + /** * alarm_try_to_cancel - Tries to cancel an alarm timer * @alarm: ptr to alarm to be canceled @@ -394,6 +425,12 @@ u64 alarm_forward(struct alarm *alarm, ktime_t now, ktime_t interval) return overrun; } +u64 alarm_forward_now(struct alarm *alarm, ktime_t interval) +{ + struct alarm_base *base = &alarm_bases[alarm->type]; + + return alarm_forward(alarm, base->gettime(), interval); +} -- cgit v1.2.3 From 11ffa9d6065f344a9bd769a2452f26f2f671e5f8 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 15 May 2013 14:38:12 -0700 Subject: timerfd: Add alarm timers Add support for clocks CLOCK_REALTIME_ALARM and CLOCK_BOOTTIME_ALARM, thereby enabling wakeup alarm timers via file descriptors. Signed-off-by: Todd Poynor Signed-off-by: John Stultz --- fs/timerfd.c | 131 ++++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 108 insertions(+), 23 deletions(-) diff --git a/fs/timerfd.c b/fs/timerfd.c index 32b644f03690..929312180dd0 100644 --- a/fs/timerfd.c +++ b/fs/timerfd.c @@ -8,6 +8,7 @@ * */ +#include #include #include #include @@ -26,7 +27,10 @@ #include struct timerfd_ctx { - struct hrtimer tmr; + union { + struct hrtimer tmr; + struct alarm alarm; + } t; ktime_t tintv; ktime_t moffs; wait_queue_head_t wqh; @@ -41,14 +45,19 @@ struct timerfd_ctx { static LIST_HEAD(cancel_list); static DEFINE_SPINLOCK(cancel_lock); +static inline bool isalarm(struct timerfd_ctx *ctx) +{ + return ctx->clockid == CLOCK_REALTIME_ALARM || + ctx->clockid == CLOCK_BOOTTIME_ALARM; +} + /* * This gets called when the timer event triggers. We set the "expired" * flag, but we do not re-arm the timer (in case it's necessary, * tintv.tv64 != 0) until the timer is accessed. */ -static enum hrtimer_restart timerfd_tmrproc(struct hrtimer *htmr) +static void timerfd_triggered(struct timerfd_ctx *ctx) { - struct timerfd_ctx *ctx = container_of(htmr, struct timerfd_ctx, tmr); unsigned long flags; spin_lock_irqsave(&ctx->wqh.lock, flags); @@ -56,10 +65,25 @@ static enum hrtimer_restart timerfd_tmrproc(struct hrtimer *htmr) ctx->ticks++; wake_up_locked(&ctx->wqh); spin_unlock_irqrestore(&ctx->wqh.lock, flags); +} +static enum hrtimer_restart timerfd_tmrproc(struct hrtimer *htmr) +{ + struct timerfd_ctx *ctx = container_of(htmr, struct timerfd_ctx, + t.tmr); + timerfd_triggered(ctx); return HRTIMER_NORESTART; } +static enum alarmtimer_restart timerfd_alarmproc(struct alarm *alarm, + ktime_t now) +{ + struct timerfd_ctx *ctx = container_of(alarm, struct timerfd_ctx, + t.alarm); + timerfd_triggered(ctx); + return ALARMTIMER_NORESTART; +} + /* * Called when the clock was set to cancel the timers in the cancel * list. This will wake up processes waiting on these timers. The @@ -107,8 +131,9 @@ static bool timerfd_canceled(struct timerfd_ctx *ctx) static void timerfd_setup_cancel(struct timerfd_ctx *ctx, int flags) { - if (ctx->clockid == CLOCK_REALTIME && (flags & TFD_TIMER_ABSTIME) && - (flags & TFD_TIMER_CANCEL_ON_SET)) { + if ((ctx->clockid == CLOCK_REALTIME || + ctx->clockid == CLOCK_REALTIME_ALARM) && + (flags & TFD_TIMER_ABSTIME) && (flags & TFD_TIMER_CANCEL_ON_SET)) { if (!ctx->might_cancel) { ctx->might_cancel = true; spin_lock(&cancel_lock); @@ -124,7 +149,11 @@ static ktime_t timerfd_get_remaining(struct timerfd_ctx *ctx) { ktime_t remaining; - remaining = hrtimer_expires_remaining(&ctx->tmr); + if (isalarm(ctx)) + remaining = alarm_expires_remaining(&ctx->t.alarm); + else + remaining = hrtimer_expires_remaining(&ctx->t.tmr); + return remaining.tv64 < 0 ? ktime_set(0, 0): remaining; } @@ -142,11 +171,28 @@ static int timerfd_setup(struct timerfd_ctx *ctx, int flags, ctx->expired = 0; ctx->ticks = 0; ctx->tintv = timespec_to_ktime(ktmr->it_interval); - hrtimer_init(&ctx->tmr, clockid, htmode); - hrtimer_set_expires(&ctx->tmr, texp); - ctx->tmr.function = timerfd_tmrproc; + + if (isalarm(ctx)) { + alarm_init(&ctx->t.alarm, + ctx->clockid == CLOCK_REALTIME_ALARM ? + ALARM_REALTIME : ALARM_BOOTTIME, + timerfd_alarmproc); + } else { + hrtimer_init(&ctx->t.tmr, clockid, htmode); + hrtimer_set_expires(&ctx->t.tmr, texp); + ctx->t.tmr.function = timerfd_tmrproc; + } + if (texp.tv64 != 0) { - hrtimer_start(&ctx->tmr, texp, htmode); + if (isalarm(ctx)) { + if (flags & TFD_TIMER_ABSTIME) + alarm_start(&ctx->t.alarm, texp); + else + alarm_start_relative(&ctx->t.alarm, texp); + } else { + hrtimer_start(&ctx->t.tmr, texp, htmode); + } + if (timerfd_canceled(ctx)) return -ECANCELED; } @@ -158,7 +204,11 @@ static int timerfd_release(struct inode *inode, struct file *file) struct timerfd_ctx *ctx = file->private_data; timerfd_remove_cancel(ctx); - hrtimer_cancel(&ctx->tmr); + + if (isalarm(ctx)) + alarm_cancel(&ctx->t.alarm); + else + hrtimer_cancel(&ctx->t.tmr); kfree_rcu(ctx, rcu); return 0; } @@ -215,9 +265,15 @@ static ssize_t timerfd_read(struct file *file, char __user *buf, size_t count, * callback to avoid DoS attacks specifying a very * short timer period. */ - ticks += hrtimer_forward_now(&ctx->tmr, - ctx->tintv) - 1; - hrtimer_restart(&ctx->tmr); + if (isalarm(ctx)) { + ticks += alarm_forward_now( + &ctx->t.alarm, ctx->tintv) - 1; + alarm_restart(&ctx->t.alarm); + } else { + ticks += hrtimer_forward_now(&ctx->t.tmr, + ctx->tintv) - 1; + hrtimer_restart(&ctx->t.tmr); + } } ctx->expired = 0; ctx->ticks = 0; @@ -259,7 +315,9 @@ SYSCALL_DEFINE2(timerfd_create, int, clockid, int, flags) if ((flags & ~TFD_CREATE_FLAGS) || (clockid != CLOCK_MONOTONIC && - clockid != CLOCK_REALTIME)) + clockid != CLOCK_REALTIME && + clockid != CLOCK_REALTIME_ALARM && + clockid != CLOCK_BOOTTIME_ALARM)) return -EINVAL; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); @@ -268,7 +326,15 @@ SYSCALL_DEFINE2(timerfd_create, int, clockid, int, flags) init_waitqueue_head(&ctx->wqh); ctx->clockid = clockid; - hrtimer_init(&ctx->tmr, clockid, HRTIMER_MODE_ABS); + + if (isalarm(ctx)) + alarm_init(&ctx->t.alarm, + ctx->clockid == CLOCK_REALTIME_ALARM ? + ALARM_REALTIME : ALARM_BOOTTIME, + timerfd_alarmproc); + else + hrtimer_init(&ctx->t.tmr, clockid, HRTIMER_MODE_ABS); + ctx->moffs = ktime_get_monotonic_offset(); ufd = anon_inode_getfd("[timerfd]", &timerfd_fops, ctx, @@ -305,8 +371,14 @@ static int do_timerfd_settime(int ufd, int flags, */ for (;;) { spin_lock_irq(&ctx->wqh.lock); - if (hrtimer_try_to_cancel(&ctx->tmr) >= 0) - break; + + if (isalarm(ctx)) { + if (alarm_try_to_cancel(&ctx->t.alarm) >= 0) + break; + } else { + if (hrtimer_try_to_cancel(&ctx->t.tmr) >= 0) + break; + } spin_unlock_irq(&ctx->wqh.lock); cpu_relax(); } @@ -317,8 +389,12 @@ static int do_timerfd_settime(int ufd, int flags, * We do not update "ticks" and "expired" since the timer will be * re-programmed again in the following timerfd_setup() call. */ - if (ctx->expired && ctx->tintv.tv64) - hrtimer_forward_now(&ctx->tmr, ctx->tintv); + if (ctx->expired && ctx->tintv.tv64) { + if (isalarm(ctx)) + alarm_forward_now(&ctx->t.alarm, ctx->tintv); + else + hrtimer_forward_now(&ctx->t.tmr, ctx->tintv); + } old->it_value = ktime_to_timespec(timerfd_get_remaining(ctx)); old->it_interval = ktime_to_timespec(ctx->tintv); @@ -345,9 +421,18 @@ static int do_timerfd_gettime(int ufd, struct itimerspec *t) spin_lock_irq(&ctx->wqh.lock); if (ctx->expired && ctx->tintv.tv64) { ctx->expired = 0; - ctx->ticks += - hrtimer_forward_now(&ctx->tmr, ctx->tintv) - 1; - hrtimer_restart(&ctx->tmr); + + if (isalarm(ctx)) { + ctx->ticks += + alarm_forward_now( + &ctx->t.alarm, ctx->tintv) - 1; + alarm_restart(&ctx->t.alarm); + } else { + ctx->ticks += + hrtimer_forward_now(&ctx->t.tmr, ctx->tintv) + - 1; + hrtimer_restart(&ctx->t.tmr); + } } t->it_value = ktime_to_timespec(timerfd_get_remaining(ctx)); t->it_interval = ktime_to_timespec(ctx->tintv); -- cgit v1.2.3 From 5c83545f24ab3dd67e0ae0e2b795fea750f08c34 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Tue, 21 May 2013 22:32:14 -0700 Subject: power: Add option to log time spent in suspend Below is a patch from android kernel that maintains a histogram of suspend times. Please review and provide feedback. Statistices on the time spent in suspend are kept in /sys/kernel/debug/sleep_time. Cc: Android Kernel Team Cc: Colin Cross Cc: Todd Poynor Cc: San Mehat Cc: Benoit Goby Cc: John Stultz Cc: Thomas Gleixner Signed-off-by: Colin Cross Signed-off-by: Todd Poynor [zoran.markovic@linaro.org: Re-formatted suspend time table to better fit expected values. Moved accounting of suspend time into timekeeping core. Removed CONFIG_SUSPEND_TIME flag and made the feature conditional on CONFIG_DEBUG_FS. Changed the file name to sleep_time to better fit terminology in timekeeping core. Changed seq_printf to seq_puts. Tweaked commit message] Signed-off-by: Zoran Markovic Signed-off-by: John Stultz --- kernel/time/Makefile | 1 + kernel/time/timekeeping.c | 2 ++ kernel/time/timekeeping_debug.c | 72 ++++++++++++++++++++++++++++++++++++++ kernel/time/timekeeping_internal.h | 14 ++++++++ 4 files changed, 89 insertions(+) create mode 100644 kernel/time/timekeeping_debug.c create mode 100644 kernel/time/timekeeping_internal.h diff --git a/kernel/time/Makefile b/kernel/time/Makefile index ff7d9d2ab504..d52ac8bf0006 100644 --- a/kernel/time/Makefile +++ b/kernel/time/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST) += tick-broadcast.o obj-$(CONFIG_TICK_ONESHOT) += tick-oneshot.o obj-$(CONFIG_TICK_ONESHOT) += tick-sched.o obj-$(CONFIG_TIMER_STATS) += timer_stats.o +obj-$(CONFIG_DEBUG_FS) += timekeeping_debug.o diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 933efa4071c3..838fc0777b68 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -25,6 +25,7 @@ #include "tick-internal.h" #include "ntp_internal.h" +#include "timekeeping_internal.h" static struct timekeeper timekeeper; static DEFINE_RAW_SPINLOCK(timekeeper_lock); @@ -851,6 +852,7 @@ static void __timekeeping_inject_sleeptime(struct timekeeper *tk, tk_xtime_add(tk, delta); tk_set_wall_to_mono(tk, timespec_sub(tk->wall_to_monotonic, *delta)); tk_set_sleep_time(tk, timespec_add(tk->total_sleep_time, *delta)); + tk_debug_account_sleep_time(delta); } /** diff --git a/kernel/time/timekeeping_debug.c b/kernel/time/timekeeping_debug.c new file mode 100644 index 000000000000..802433a4f5eb --- /dev/null +++ b/kernel/time/timekeeping_debug.c @@ -0,0 +1,72 @@ +/* + * debugfs file to track time spent in suspend + * + * Copyright (c) 2011, Google, 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. + * + * 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 +#include +#include +#include +#include +#include + +static unsigned int sleep_time_bin[32] = {0}; + +static int tk_debug_show_sleep_time(struct seq_file *s, void *data) +{ + unsigned int bin; + seq_puts(s, " time (secs) count\n"); + seq_puts(s, "------------------------------\n"); + for (bin = 0; bin < 32; bin++) { + if (sleep_time_bin[bin] == 0) + continue; + seq_printf(s, "%10u - %-10u %4u\n", + bin ? 1 << (bin - 1) : 0, 1 << bin, + sleep_time_bin[bin]); + } + return 0; +} + +static int tk_debug_sleep_time_open(struct inode *inode, struct file *file) +{ + return single_open(file, tk_debug_show_sleep_time, NULL); +} + +static const struct file_operations tk_debug_sleep_time_fops = { + .open = tk_debug_sleep_time_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int __init tk_debug_sleep_time_init(void) +{ + struct dentry *d; + + d = debugfs_create_file("sleep_time", 0444, NULL, NULL, + &tk_debug_sleep_time_fops); + if (!d) { + pr_err("Failed to create sleep_time debug file\n"); + return -ENOMEM; + } + + return 0; +} +late_initcall(tk_debug_sleep_time_init); + +void tk_debug_account_sleep_time(struct timespec *t) +{ + sleep_time_bin[fls(t->tv_sec)]++; +} + diff --git a/kernel/time/timekeeping_internal.h b/kernel/time/timekeeping_internal.h new file mode 100644 index 000000000000..13323ea08ffa --- /dev/null +++ b/kernel/time/timekeeping_internal.h @@ -0,0 +1,14 @@ +#ifndef _TIMEKEEPING_INTERNAL_H +#define _TIMEKEEPING_INTERNAL_H +/* + * timekeeping debug functions + */ +#include + +#ifdef CONFIG_DEBUG_FS +extern void tk_debug_account_sleep_time(struct timespec *t); +#else +#define tk_debug_account_sleep_time(x) +#endif + +#endif /* _TIMEKEEPING_INTERNAL_H */ -- cgit v1.2.3 From ce0b098981544d9f4e910ea48f3af3e726ca6d6b Mon Sep 17 00:00:00 2001 From: John Stultz Date: Tue, 28 May 2013 17:03:09 -0700 Subject: x86: Fix vrtc_get_time/set_mmss to use new timespec interface The patch "x86: Increase precision of x86_platform.get/set_wallclock" changed the x86 platform set_wallclock/get_wallclock interfaces to use nsec granular timespecs instead of a second granular interface. However, that patch missed converting the vrtc code, so this patch converts those functions to use timespecs. Many thanks to the kbuild test robot for finding this! Reported-by: kbuild test robot Signed-off-by: John Stultz --- arch/x86/include/asm/mrst-vrtc.h | 4 ++-- arch/x86/platform/mrst/vrtc.c | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/arch/x86/include/asm/mrst-vrtc.h b/arch/x86/include/asm/mrst-vrtc.h index 73668abdbedf..1e69a75412a4 100644 --- a/arch/x86/include/asm/mrst-vrtc.h +++ b/arch/x86/include/asm/mrst-vrtc.h @@ -3,7 +3,7 @@ extern unsigned char vrtc_cmos_read(unsigned char reg); extern void vrtc_cmos_write(unsigned char val, unsigned char reg); -extern unsigned long vrtc_get_time(void); -extern int vrtc_set_mmss(unsigned long nowtime); +extern void vrtc_get_time(struct timespec *now); +extern int vrtc_set_mmss(const struct timespec *now); #endif diff --git a/arch/x86/platform/mrst/vrtc.c b/arch/x86/platform/mrst/vrtc.c index d62b0a3b5c14..5e355b134ba4 100644 --- a/arch/x86/platform/mrst/vrtc.c +++ b/arch/x86/platform/mrst/vrtc.c @@ -56,7 +56,7 @@ void vrtc_cmos_write(unsigned char val, unsigned char reg) } EXPORT_SYMBOL_GPL(vrtc_cmos_write); -unsigned long vrtc_get_time(void) +void vrtc_get_time(struct timespec *now) { u8 sec, min, hour, mday, mon; unsigned long flags; @@ -82,17 +82,18 @@ unsigned long vrtc_get_time(void) printk(KERN_INFO "vRTC: sec: %d min: %d hour: %d day: %d " "mon: %d year: %d\n", sec, min, hour, mday, mon, year); - return mktime(year, mon, mday, hour, min, sec); + now->tv_sec = mktime(year, mon, mday, hour, min, sec); + now->tv_nsec = 0; } -int vrtc_set_mmss(unsigned long nowtime) +int vrtc_set_mmss(const struct timespec *now) { unsigned long flags; struct rtc_time tm; int year; int retval = 0; - rtc_time_to_tm(nowtime, &tm); + rtc_time_to_tm(now->tv_sec, &tm); if (!rtc_valid_tm(&tm) && tm.tm_year >= 72) { /* * tm.year is the number of years since 1900, and the @@ -110,7 +111,7 @@ int vrtc_set_mmss(unsigned long nowtime) } else { printk(KERN_ERR "%s: Invalid vRTC value: write of %lx to vRTC failed\n", - __FUNCTION__, nowtime); + __FUNCTION__, now->tv_sec); retval = -EINVAL; } return retval; -- cgit v1.2.3 From 516ca22307df6ba972e9719ec756a356187cdde7 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Tue, 28 May 2013 18:04:46 -0600 Subject: PCI/ACPI: Combine duplicate adjacent "if" tests [bhelgaas: split out from acpi_pci_roots removal] Signed-off-by: Jiang Liu Signed-off-by: Bjorn Helgaas --- drivers/acpi/pci_root.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 1dd6f6c85874..eb09053c0fce 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -536,11 +536,10 @@ static int acpi_pci_root_add(struct acpi_device *device, if (system_state != SYSTEM_BOOTING) { pcibios_resource_survey_bus(root->bus); pci_assign_unassigned_bus_resources(root->bus); - } - /* need to after hot-added ioapic is registered */ - if (system_state != SYSTEM_BOOTING) + /* need to after hot-added ioapic is registered */ pci_enable_bridges(root->bus); + } pci_bus_add_devices(root->bus); return 1; -- cgit v1.2.3 From bfe2414aecca03d884f680d5cf1d612c7813ee33 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Tue, 28 May 2013 18:31:27 -0600 Subject: PCI/ACPI: Introduce "handle" local for economy of expression [bhelgaas: split out from acpi_handle_printk() changes] Signed-off-by: Jiang Liu Signed-off-by: Bjorn Helgaas --- drivers/acpi/pci_root.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index eb09053c0fce..852fb96f94e5 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -382,13 +382,14 @@ static int acpi_pci_root_add(struct acpi_device *device, int result; struct acpi_pci_root *root; u32 flags, base_flags; + acpi_handle handle = device->handle; root = kzalloc(sizeof(struct acpi_pci_root), GFP_KERNEL); if (!root) return -ENOMEM; segment = 0; - status = acpi_evaluate_integer(device->handle, METHOD_NAME__SEG, NULL, + status = acpi_evaluate_integer(handle, METHOD_NAME__SEG, NULL, &segment); if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { printk(KERN_ERR PREFIX "can't evaluate _SEG\n"); @@ -398,7 +399,7 @@ static int acpi_pci_root_add(struct acpi_device *device, /* Check _CRS first, then _BBN. If no _BBN, default to zero. */ root->secondary.flags = IORESOURCE_BUS; - status = try_get_root_bridge_busnr(device->handle, &root->secondary); + status = try_get_root_bridge_busnr(handle, &root->secondary); if (ACPI_FAILURE(status)) { /* * We need both the start and end of the downstream bus range @@ -409,7 +410,7 @@ static int acpi_pci_root_add(struct acpi_device *device, root->secondary.end = 0xFF; printk(KERN_WARNING FW_BUG PREFIX "no secondary bus range in _CRS\n"); - status = acpi_evaluate_integer(device->handle, METHOD_NAME__BBN, + status = acpi_evaluate_integer(handle, METHOD_NAME__BBN, NULL, &bus); if (ACPI_SUCCESS(status)) root->secondary.start = bus; @@ -433,7 +434,7 @@ static int acpi_pci_root_add(struct acpi_device *device, acpi_device_name(device), acpi_device_bid(device), root->segment, &root->secondary); - root->mcfg_addr = acpi_pci_root_get_mcfg_addr(device->handle); + root->mcfg_addr = acpi_pci_root_get_mcfg_addr(handle); /* * All supported architectures that use ACPI have support for @@ -502,7 +503,7 @@ static int acpi_pci_root_add(struct acpi_device *device, dev_info(&device->dev, "Requesting ACPI _OSC control (0x%02x)\n", flags); - status = acpi_pci_osc_control_set(device->handle, &flags, + status = acpi_pci_osc_control_set(handle, &flags, OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL); if (ACPI_SUCCESS(status)) { dev_info(&device->dev, -- cgit v1.2.3 From bbebed6423f5b281f9ca314518531f90424f6f57 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Tue, 28 May 2013 17:59:25 -0600 Subject: PCI/ACPI: Remove unused global list acpi_pci_roots Now the global list acpi_pci_roots pci_root.c is useless, remove it. Signed-off-by: Jiang Liu Signed-off-by: Bjorn Helgaas Acked-by: Rafael J. Wysocki Cc: Len Brown --- drivers/acpi/pci_root.c | 19 +------------------ include/acpi/acpi_bus.h | 1 - 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 852fb96f94e5..18b7ab2dee4f 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -65,10 +65,6 @@ static struct acpi_scan_handler pci_root_handler = { .detach = acpi_pci_root_remove, }; -/* Lock to protect both acpi_pci_roots lists */ -static DEFINE_MUTEX(acpi_pci_root_lock); -static LIST_HEAD(acpi_pci_roots); - static DEFINE_MUTEX(osc_lock); /** @@ -423,7 +419,6 @@ static int acpi_pci_root_add(struct acpi_device *device, } } - INIT_LIST_HEAD(&root->node); root->device = device; root->segment = segment & 0xFFFF; strcpy(acpi_device_name(device), ACPI_PCI_ROOT_DEVICE_NAME); @@ -447,10 +442,6 @@ static int acpi_pci_root_add(struct acpi_device *device, * TBD: Need PCI interface for enumeration/configuration of roots. */ - mutex_lock(&acpi_pci_root_lock); - list_add_tail(&root->node, &acpi_pci_roots); - mutex_unlock(&acpi_pci_root_lock); - /* * Scan the Root Bridge * -------------------- @@ -464,7 +455,7 @@ static int acpi_pci_root_add(struct acpi_device *device, "Bus %04x:%02x not present in PCI namespace\n", root->segment, (unsigned int)root->secondary.start); result = -ENODEV; - goto out_del_root; + goto end; } /* Indicate support for various _OSC capabilities. */ @@ -545,11 +536,6 @@ static int acpi_pci_root_add(struct acpi_device *device, pci_bus_add_devices(root->bus); return 1; -out_del_root: - mutex_lock(&acpi_pci_root_lock); - list_del(&root->node); - mutex_unlock(&acpi_pci_root_lock); - end: kfree(root); return result; @@ -566,9 +552,6 @@ static void acpi_pci_root_remove(struct acpi_device *device) pci_remove_root_bus(root->bus); - mutex_lock(&acpi_pci_root_lock); - list_del(&root->node); - mutex_unlock(&acpi_pci_root_lock); kfree(root); } diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 98db31d9f9b4..af0f840f77c8 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -472,7 +472,6 @@ int register_acpi_bus_type(struct acpi_bus_type *); int unregister_acpi_bus_type(struct acpi_bus_type *); struct acpi_pci_root { - struct list_head node; struct acpi_device * device; struct pci_bus *bus; u16 segment; -- cgit v1.2.3 From 6dc7d22c67384d313fca88c1a63af3220a2beff7 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Tue, 28 May 2013 18:10:05 -0600 Subject: PCI/ACPI: Use dev_printk(), acpi_handle_print(), pr_xxx() when possible Use dev_printk(), acpi_handle_print(), and pr_xxx() to print messages in pci_root.c. [bhelgaas: fold in dev_printk() changes, use dev_printk() in handle_root_bridge_insertion()] Signed-off-by: Jiang Liu Signed-off-by: Bjorn Helgaas Acked-by: Rafael J. Wysocki Cc: Len Brown --- drivers/acpi/pci_root.c | 59 ++++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 32 deletions(-) diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 18b7ab2dee4f..27020882eb97 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -388,7 +388,7 @@ static int acpi_pci_root_add(struct acpi_device *device, status = acpi_evaluate_integer(handle, METHOD_NAME__SEG, NULL, &segment); if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { - printk(KERN_ERR PREFIX "can't evaluate _SEG\n"); + dev_err(&device->dev, "can't evaluate _SEG\n"); result = -ENODEV; goto end; } @@ -404,8 +404,8 @@ static int acpi_pci_root_add(struct acpi_device *device, * can do is assume [_BBN-0xFF] or [0-0xFF]. */ root->secondary.end = 0xFF; - printk(KERN_WARNING FW_BUG PREFIX - "no secondary bus range in _CRS\n"); + dev_warn(&device->dev, + FW_BUG "no secondary bus range in _CRS\n"); status = acpi_evaluate_integer(handle, METHOD_NAME__BBN, NULL, &bus); if (ACPI_SUCCESS(status)) @@ -413,7 +413,7 @@ static int acpi_pci_root_add(struct acpi_device *device, else if (status == AE_NOT_FOUND) root->secondary.start = 0; else { - printk(KERN_ERR PREFIX "can't evaluate _BBN\n"); + dev_err(&device->dev, "can't evaluate _BBN\n"); result = -ENODEV; goto end; } @@ -425,7 +425,7 @@ static int acpi_pci_root_add(struct acpi_device *device, strcpy(acpi_device_class(device), ACPI_PCI_ROOT_CLASS); device->driver_data = root; - printk(KERN_INFO PREFIX "%s [%s] (domain %04x %pR)\n", + pr_info(PREFIX "%s [%s] (domain %04x %pR)\n", acpi_device_name(device), acpi_device_bid(device), root->segment, &root->secondary); @@ -451,9 +451,9 @@ static int acpi_pci_root_add(struct acpi_device *device, */ root->bus = pci_acpi_scan_root(root); if (!root->bus) { - printk(KERN_ERR PREFIX - "Bus %04x:%02x not present in PCI namespace\n", - root->segment, (unsigned int)root->secondary.start); + dev_err(&device->dev, + "Bus %04x:%02x not present in PCI namespace\n", + root->segment, (unsigned int)root->secondary.start); result = -ENODEV; goto end; } @@ -511,8 +511,8 @@ static int acpi_pci_root_add(struct acpi_device *device, "ACPI _OSC request failed (%s), " "returned control mask: 0x%02x\n", acpi_format_exception(status), flags); - pr_info("ACPI _OSC control for PCIe not granted, " - "disabling ASPM\n"); + dev_info(&device->dev, + "ACPI _OSC control for PCIe not granted, disabling ASPM\n"); pcie_no_aspm(); } } else { @@ -571,12 +571,13 @@ static void handle_root_bridge_insertion(acpi_handle handle) struct acpi_device *device; if (!acpi_bus_get_device(handle, &device)) { - printk(KERN_DEBUG "acpi device exists...\n"); + dev_printk(KERN_DEBUG, &device->dev, + "acpi device already exists; ignoring notify\n"); return; } if (acpi_bus_scan(handle)) - printk(KERN_ERR "cannot add bridge to acpi list\n"); + acpi_handle_err(handle, "cannot add bridge to acpi list\n"); } static void handle_root_bridge_removal(struct acpi_device *device) @@ -605,7 +606,6 @@ static void handle_root_bridge_removal(struct acpi_device *device) static void _handle_hotplug_event_root(struct work_struct *work) { struct acpi_pci_root *root; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER }; struct acpi_hp_work *hp_work; acpi_handle handle; u32 type; @@ -617,13 +617,12 @@ static void _handle_hotplug_event_root(struct work_struct *work) acpi_scan_lock_acquire(); root = acpi_pci_find_root(handle); - acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); switch (type) { case ACPI_NOTIFY_BUS_CHECK: /* bus enumerate */ - printk(KERN_DEBUG "%s: Bus check notify on %s\n", __func__, - (char *)buffer.pointer); + acpi_handle_printk(KERN_DEBUG, handle, + "Bus check notify on %s\n", __func__); if (!root) handle_root_bridge_insertion(handle); @@ -631,28 +630,28 @@ static void _handle_hotplug_event_root(struct work_struct *work) case ACPI_NOTIFY_DEVICE_CHECK: /* device check */ - printk(KERN_DEBUG "%s: Device check notify on %s\n", __func__, - (char *)buffer.pointer); + acpi_handle_printk(KERN_DEBUG, handle, + "Device check notify on %s\n", __func__); if (!root) handle_root_bridge_insertion(handle); break; case ACPI_NOTIFY_EJECT_REQUEST: /* request device eject */ - printk(KERN_DEBUG "%s: Device eject notify on %s\n", __func__, - (char *)buffer.pointer); + acpi_handle_printk(KERN_DEBUG, handle, + "Device eject notify on %s\n", __func__); if (root) handle_root_bridge_removal(root->device); break; default: - printk(KERN_WARNING "notify_handler: unknown event type 0x%x for %s\n", - type, (char *)buffer.pointer); + acpi_handle_warn(handle, + "notify_handler: unknown event type 0x%x\n", + type); break; } acpi_scan_lock_release(); kfree(hp_work); /* allocated in handle_hotplug_event_bridge */ - kfree(buffer.pointer); } static void handle_hotplug_event_root(acpi_handle handle, u32 type, @@ -666,9 +665,6 @@ static acpi_status __init find_root_bridges(acpi_handle handle, u32 lvl, void *context, void **rv) { acpi_status status; - char objname[64]; - struct acpi_buffer buffer = { .length = sizeof(objname), - .pointer = objname }; int *count = (int *)context; if (!acpi_is_root_bridge(handle)) @@ -676,16 +672,15 @@ find_root_bridges(acpi_handle handle, u32 lvl, void *context, void **rv) (*count)++; - acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); - status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, handle_hotplug_event_root, NULL); if (ACPI_FAILURE(status)) - printk(KERN_DEBUG "acpi root: %s notify handler is not installed, exit status: %u\n", - objname, (unsigned int)status); + acpi_handle_printk(KERN_DEBUG, handle, + "notify handler is not installed, exit status: %u\n", + (unsigned int)status); else - printk(KERN_DEBUG "acpi root: %s notify handler is installed\n", - objname); + acpi_handle_printk(KERN_DEBUG, handle, + "notify handler is installed\n"); return AE_OK; } -- cgit v1.2.3 From cb93b1864088eb833ea9cef2c20f07d1961241b0 Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Wed, 29 May 2013 17:01:52 +0800 Subject: PCI: Fix comment typo for PCI_EXP_LNKCAP_CLKPM Fix trivial typo for PCI_EXP_LNKCAP_CLKPM comment. Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas --- include/uapi/linux/pci_regs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index 864e324da80d..c3cc01d474b0 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -468,7 +468,7 @@ #define PCI_EXP_LNKCAP_ASPMS 0x00000c00 /* ASPM Support */ #define PCI_EXP_LNKCAP_L0SEL 0x00007000 /* L0s Exit Latency */ #define PCI_EXP_LNKCAP_L1EL 0x00038000 /* L1 Exit Latency */ -#define PCI_EXP_LNKCAP_CLKPM 0x00040000 /* L1 Clock Power Management */ +#define PCI_EXP_LNKCAP_CLKPM 0x00040000 /* Clock Power Management */ #define PCI_EXP_LNKCAP_SDERC 0x00080000 /* Surprise Down Error Reporting Capable */ #define PCI_EXP_LNKCAP_DLLLARC 0x00100000 /* Data Link Layer Link Active Reporting Capable */ #define PCI_EXP_LNKCAP_LBNC 0x00200000 /* Link Bandwidth Notification Capability */ -- cgit v1.2.3 From 518d4709f1961539a64f5d5f9b5b842824c0d971 Mon Sep 17 00:00:00 2001 From: Tony Prisk Date: Mon, 13 May 2013 20:20:59 +1200 Subject: clk: vt8500: Add support for clocks on the WM8850 SoCs The WM8850 has a different PLL clock to the previous versions. This patch adds support for the WM8850-style PLL clocks. Signed-off-by: Tony Prisk Signed-off-by: Mike Turquette --- Documentation/devicetree/bindings/clock/vt8500.txt | 2 + drivers/clk/clk-vt8500.c | 71 ++++++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/Documentation/devicetree/bindings/clock/vt8500.txt b/Documentation/devicetree/bindings/clock/vt8500.txt index a880c70d0047..91d71cc0314a 100644 --- a/Documentation/devicetree/bindings/clock/vt8500.txt +++ b/Documentation/devicetree/bindings/clock/vt8500.txt @@ -8,6 +8,8 @@ Required properties: - compatible : shall be one of the following: "via,vt8500-pll-clock" - for a VT8500/WM8505 PLL clock "wm,wm8650-pll-clock" - for a WM8650 PLL clock + "wm,wm8750-pll-clock" - for a WM8750 PLL clock + "wm,wm8850-pll-clock" - for a WM8850 PLL clock "via,vt8500-device-clock" - for a VT/WM device clock Required properties for PLL clocks: diff --git a/drivers/clk/clk-vt8500.c b/drivers/clk/clk-vt8500.c index debf688afa8e..6d5b6e901b96 100644 --- a/drivers/clk/clk-vt8500.c +++ b/drivers/clk/clk-vt8500.c @@ -42,6 +42,7 @@ struct clk_device { #define PLL_TYPE_VT8500 0 #define PLL_TYPE_WM8650 1 #define PLL_TYPE_WM8750 2 +#define PLL_TYPE_WM8850 3 struct clk_pll { struct clk_hw hw; @@ -327,6 +328,15 @@ CLK_OF_DECLARE(vt8500_device, "via,vt8500-device-clock", vtwm_device_clk_init); #define WM8750_BITS_TO_VAL(f, m, d1, d2) \ ((f << 24) | ((m - 1) << 16) | ((d1 - 1) << 8) | d2) +/* Helper macros for PLL_WM8850 */ +#define WM8850_PLL_MUL(x) ((((x >> 16) & 0x7F) + 1) * 2) +#define WM8850_PLL_DIV(x) ((((x >> 8) & 1) + 1) * (1 << (x & 3))) + +#define WM8850_BITS_TO_FREQ(r, m, d1, d2) \ + (r * ((m + 1) * 2) / ((d1+1) * (1 << d2))) + +#define WM8850_BITS_TO_VAL(m, d1, d2) \ + ((((m / 2) - 1) << 16) | ((d1 - 1) << 8) | d2) static void vt8500_find_pll_bits(unsigned long rate, unsigned long parent_rate, u32 *multiplier, u32 *prediv) @@ -466,6 +476,49 @@ static void wm8750_find_pll_bits(unsigned long rate, unsigned long parent_rate, *divisor2 = best_div2; } +static void wm8850_find_pll_bits(unsigned long rate, unsigned long parent_rate, + u32 *multiplier, u32 *divisor1, u32 *divisor2) +{ + u32 mul, div1, div2; + u32 best_mul, best_div1, best_div2; + unsigned long tclk, rate_err, best_err; + + best_err = (unsigned long)-1; + + /* Find the closest match (lower or equal to requested) */ + for (div1 = 1; div1 >= 0; div1--) + for (div2 = 3; div2 >= 0; div2--) + for (mul = 0; mul <= 127; mul++) { + tclk = parent_rate * ((mul + 1) * 2) / + ((div1 + 1) * (1 << div2)); + if (tclk > rate) + continue; + /* error will always be +ve */ + rate_err = rate - tclk; + if (rate_err == 0) { + *multiplier = mul; + *divisor1 = div1; + *divisor2 = div2; + return; + } + + if (rate_err < best_err) { + best_err = rate_err; + best_mul = mul; + best_div1 = div1; + best_div2 = div2; + } + } + + /* if we got here, it wasn't an exact match */ + pr_warn("%s: requested rate %lu, found rate %lu\n", __func__, rate, + rate - best_err); + + *multiplier = best_mul; + *divisor1 = best_div1; + *divisor2 = best_div2; +} + static int vtwm_pll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { @@ -489,6 +542,10 @@ static int vtwm_pll_set_rate(struct clk_hw *hw, unsigned long rate, wm8750_find_pll_bits(rate, parent_rate, &filter, &mul, &div1, &div2); pll_val = WM8750_BITS_TO_VAL(filter, mul, div1, div2); break; + case PLL_TYPE_WM8850: + wm8850_find_pll_bits(rate, parent_rate, &mul, &div1, &div2); + pll_val = WM8850_BITS_TO_VAL(mul, div1, div2); + break; default: pr_err("%s: invalid pll type\n", __func__); return 0; @@ -525,6 +582,10 @@ static long vtwm_pll_round_rate(struct clk_hw *hw, unsigned long rate, wm8750_find_pll_bits(rate, *prate, &filter, &mul, &div1, &div2); round_rate = WM8750_BITS_TO_FREQ(*prate, mul, div1, div2); break; + case PLL_TYPE_WM8850: + wm8850_find_pll_bits(rate, *prate, &mul, &div1, &div2); + round_rate = WM8850_BITS_TO_FREQ(*prate, mul, div1, div2); + break; default: round_rate = 0; } @@ -552,6 +613,10 @@ static unsigned long vtwm_pll_recalc_rate(struct clk_hw *hw, pll_freq = parent_rate * WM8750_PLL_MUL(pll_val); pll_freq /= WM8750_PLL_DIV(pll_val); break; + case PLL_TYPE_WM8850: + pll_freq = parent_rate * WM8850_PLL_MUL(pll_val); + pll_freq /= WM8850_PLL_DIV(pll_val); + break; default: pll_freq = 0; } @@ -628,6 +693,12 @@ static void __init wm8750_pll_init(struct device_node *node) } CLK_OF_DECLARE(wm8750_pll, "wm,wm8750-pll-clock", wm8750_pll_init); +static void __init wm8850_pll_init(struct device_node *node) +{ + vtwm_pll_clk_init(node, PLL_TYPE_WM8850); +} +CLK_OF_DECLARE(wm8850_pll, "wm,wm8850-pll-clock", wm8850_pll_init); + void __init vtwm_clk_init(void __iomem *base) { if (!base) -- cgit v1.2.3 From 65f2c58f0f44403aa64eccc14f3a0a74d721fe7e Mon Sep 17 00:00:00 2001 From: Tony Prisk Date: Mon, 13 May 2013 20:21:00 +1200 Subject: clk: vt8500: Remove unnecessary divisor adjustment in vtwm_dclk_set_rate() The divisor adjustment code to ensure that a divisor is not rounded down, thereby giving a rate higher than requested, is unnecessary and in some instances results in the actual rate being much lower than requested due to rounding errors. The test is already performed in vtwm_dclk_round_rate(), which is always called when clk_set_rate is called. Due to rounding errors in the line: divisor = parent_rate / rate (clk-vt8500.c:160) we will sometimes end up adjusting the divisor twice - first in round_rate and then again in set_rate. This patch removes the test/adjustment in vtwm_dclk_set_rate. Signed-off-by: Tony Prisk Signed-off-by: Mike Turquette --- drivers/clk/clk-vt8500.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/clk/clk-vt8500.c b/drivers/clk/clk-vt8500.c index 6d5b6e901b96..d8fd085719bf 100644 --- a/drivers/clk/clk-vt8500.c +++ b/drivers/clk/clk-vt8500.c @@ -157,10 +157,6 @@ static int vt8500_dclk_set_rate(struct clk_hw *hw, unsigned long rate, divisor = parent_rate / rate; - /* If prate / rate would be decimal, incr the divisor */ - if (rate * divisor < parent_rate) - divisor++; - if (divisor == cdev->div_mask + 1) divisor = 0; -- cgit v1.2.3 From 1001b4d4a8ee6b2e7a6078a02ccdf68f91b192bd Mon Sep 17 00:00:00 2001 From: Toshi Kani Date: Thu, 30 May 2013 00:30:05 +0200 Subject: CPU: Fix sysfs cpu/online of offlined CPUs As reported by Dave Hansen, sysfs cpu/online shows 1 for offlined CPUs at boot. Fix this problem by initializing dev.offline with cpu_online() when registering a CPU. References: https://lkml.org/lkml/2013/5/29/403 Reported-and-tested-by: Dave Hansen Signed-off-by: Toshi Kani Signed-off-by: Rafael J. Wysocki --- drivers/base/cpu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 7431ba6fc2d4..1d110dc6f0c1 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -265,6 +265,7 @@ int __cpuinit register_cpu(struct cpu *cpu, int num) cpu->dev.bus = &cpu_subsys; cpu->dev.release = cpu_device_release; cpu->dev.offline_disabled = !cpu->hotpluggable; + cpu->dev.offline = !cpu_online(num); #ifdef CONFIG_ARCH_HAS_CPU_AUTOPROBE cpu->dev.bus->uevent = arch_cpu_uevent; #endif -- cgit v1.2.3 From 37351fd56255cacf731dc48914aaac3acbfa4bfe Mon Sep 17 00:00:00 2001 From: Tushar Behera Date: Fri, 17 May 2013 11:25:52 +0530 Subject: clk: exynos5250: Update cpufreq related clocks for EXYNOS5250 cpufreq driver for EXYNOS5250 is not a platform driver, hence we cannot currently pass the clock names through a device tree node. Instead, we need to make them available through a global alias. cpufreq driver for EXYNOS5250 requires four clocks - 'armclk', 'mout_cpu', 'mout_mpll' and 'mout_apll'. 'armclk' has already been defined with an alias, 'mout_cpu', 'mout_mpll' and 'mout_apll' are now defined with an alias. Signed-off-by: Tushar Behera Signed-off-by: Mike Turquette --- drivers/clk/samsung/clk-exynos5250.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/clk/samsung/clk-exynos5250.c b/drivers/clk/samsung/clk-exynos5250.c index 5c97e75924a8..05d099d0d8ba 100644 --- a/drivers/clk/samsung/clk-exynos5250.c +++ b/drivers/clk/samsung/clk-exynos5250.c @@ -208,10 +208,10 @@ struct samsung_fixed_factor_clock exynos5250_fixed_factor_clks[] __initdata = { }; struct samsung_mux_clock exynos5250_mux_clks[] __initdata = { - MUX(none, "mout_apll", mout_apll_p, SRC_CPU, 0, 1), - MUX(none, "mout_cpu", mout_cpu_p, SRC_CPU, 16, 1), + MUX_A(none, "mout_apll", mout_apll_p, SRC_CPU, 0, 1, "mout_apll"), + MUX_A(none, "mout_cpu", mout_cpu_p, SRC_CPU, 16, 1, "mout_cpu"), MUX(none, "mout_mpll_fout", mout_mpll_fout_p, PLL_DIV2_SEL, 4, 1), - MUX(none, "sclk_mpll", mout_mpll_p, SRC_CORE1, 8, 1), + MUX_A(none, "sclk_mpll", mout_mpll_p, SRC_CORE1, 8, 1, "mout_mpll"), MUX(none, "mout_bpll_fout", mout_bpll_fout_p, PLL_DIV2_SEL, 0, 1), MUX(none, "sclk_bpll", mout_bpll_p, SRC_CDREX, 0, 1), MUX(none, "mout_vpllsrc", mout_vpllsrc_p, SRC_TOP2, 0, 1), -- cgit v1.2.3 From 0e56523fd743fa67c21d31abf82e88a4a38decc3 Mon Sep 17 00:00:00 2001 From: Tushar Behera Date: Fri, 17 May 2013 11:25:53 +0530 Subject: clk: exynos5250: Add sclk_mpll to the parent list of mout_cpu clock 'mout_mpll' is added the list of parent clocks for 'mout_cpu'. 'mout_mpll' is an alias to the clock 'sclk_mpll'. Hence 'sclk_mpll' should be added to the list of parent clocks. This results in an error when cpufreq driver for EXYNOS5250 tries to set 'mout_mpll' as a parent for 'mout_cpu'. clk_set_parent: clk sclk_mpll can not be parent of clk mout_cpu Signed-off-by: Tushar Behera Signed-off-by: Mike Turquette --- drivers/clk/samsung/clk-exynos5250.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/samsung/clk-exynos5250.c b/drivers/clk/samsung/clk-exynos5250.c index 05d099d0d8ba..b6d79c0cacff 100644 --- a/drivers/clk/samsung/clk-exynos5250.c +++ b/drivers/clk/samsung/clk-exynos5250.c @@ -155,7 +155,7 @@ static __initdata unsigned long exynos5250_clk_regs[] = { /* list of all parent clock list */ PNAME(mout_apll_p) = { "fin_pll", "fout_apll", }; -PNAME(mout_cpu_p) = { "mout_apll", "mout_mpll", }; +PNAME(mout_cpu_p) = { "mout_apll", "sclk_mpll", }; PNAME(mout_mpll_fout_p) = { "fout_mplldiv2", "fout_mpll" }; PNAME(mout_mpll_p) = { "fin_pll", "mout_mpll_fout" }; PNAME(mout_bpll_fout_p) = { "fout_bplldiv2", "fout_bpll" }; -- cgit v1.2.3 From 61fd58dc578d40c1cfe2691380c42860c6fff809 Mon Sep 17 00:00:00 2001 From: "Giacomo A. Catenazzi" Date: Fri, 17 May 2013 10:43:20 -0300 Subject: clk: sunxi: "cpu_data" is defined in header files of some architectures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In some architectures, the #define cpu_data is not a "macro-function", so the compiler will substitute the identifier with probably something wrong. Signed-off-by: Giacomo A. Catenazzi Signed-off-by: Emilio López [emilio@elopez.com.ar: use cpu_mux_data instead of this_cpu_data] Signed-off-by: Mike Turquette --- drivers/clk/sunxi/clk-sunxi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 930d36fdb1c9..412912bbba53 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -239,7 +239,7 @@ struct mux_data { u8 shift; }; -static const __initconst struct mux_data cpu_data = { +static const __initconst struct mux_data cpu_mux_data = { .shift = 16, }; @@ -433,7 +433,7 @@ static const __initconst struct of_device_id clk_div_match[] = { /* Matches for mux clocks */ static const __initconst struct of_device_id clk_mux_match[] = { - {.compatible = "allwinner,sun4i-cpu-clk", .data = &cpu_data,}, + {.compatible = "allwinner,sun4i-cpu-clk", .data = &cpu_mux_data,}, {.compatible = "allwinner,sun4i-apb1-mux-clk", .data = &apb1_mux_data,}, {} }; -- cgit v1.2.3 From e684533b1044498606a37e2b5ba8bb0bef56067f Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 30 May 2013 10:06:01 +0100 Subject: ASoC: wm0010: Set IRQ as a wake source The DSPs IRQ should be a wake source as several of the possible algorithms may run whilst the AP is asleepi and require to wake the AP to push or pull more data, such as compressed playback. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm0010.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c index 370af0cbcc9a..b6df319869ac 100644 --- a/sound/soc/codecs/wm0010.c +++ b/sound/soc/codecs/wm0010.c @@ -972,6 +972,13 @@ static int wm0010_spi_probe(struct spi_device *spi) } wm0010->irq = irq; + ret = irq_set_irq_wake(irq, 1); + if (ret) { + dev_err(wm0010->dev, "Failed to set IRQ %d as wake source: %d\n", + irq, ret); + return ret; + } + if (spi->max_speed_hz) wm0010->board_max_spi_speed = spi->max_speed_hz; else -- cgit v1.2.3 From 6051426fd0538eac4e28c55a08e4f4fb1c709b65 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 30 May 2013 13:38:09 +0530 Subject: spi: spi-imx: Fix compilation error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix compilation error due to a typo introduced by commit 24778be20f ("spi: convert drivers to use bits_per_word_mask"). Without this patch we get the following build errors: drivers/spi/spi-imx.c: In function ‘spi_imx_setupxfer’: drivers/spi/spi-imx.c:703:2: warning: no return statement in function returning non-void [-Wreturn-type] drivers/spi/spi-imx.c: At top level: drivers/spi/spi-imx.c:705:9: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘->’ token drivers/spi/spi-imx.c:707:2: error: expected identifier or ‘(’ before ‘return’ drivers/spi/spi-imx.c:708:1: error: expected identifier or ‘(’ before ‘}’ token Signed-off-by: Sachin Kamat Cc: Stephen Warren Signed-off-by: Mark Brown --- drivers/spi/spi-imx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index cb88eeb74cc4..982667383dd5 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -698,7 +698,7 @@ static int spi_imx_setupxfer(struct spi_device *spi, } else if (config.bpw <= 16) { spi_imx->rx = spi_imx_buf_rx_u16; spi_imx->tx = spi_imx_buf_tx_u16; - } + } else { spi_imx->rx = spi_imx_buf_rx_u32; spi_imx->tx = spi_imx_buf_tx_u32; } -- cgit v1.2.3 From 18b494527bc3e6847e34d18e540c78a12c3aef2f Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Thu, 30 May 2013 09:37:40 +0200 Subject: ASoC: OMAP: Remove obsolete Makefile line Support for omap2evm was removed in v3.0. But only one of its two lines in this Makefile was removed. Remove the second line too. Signed-off-by: Paul Bolle Acked-by: Jarkko Nikula Signed-off-by: Mark Brown --- sound/soc/omap/Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile index 2b225945359b..a725905b2c68 100644 --- a/sound/soc/omap/Makefile +++ b/sound/soc/omap/Makefile @@ -26,7 +26,6 @@ obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o obj-$(CONFIG_SND_OMAP_SOC_RX51) += snd-soc-rx51.o obj-$(CONFIG_SND_OMAP_SOC_AMS_DELTA) += snd-soc-ams-delta.o obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o -obj-$(CONFIG_SND_OMAP_SOC_OMAP2EVM) += snd-soc-omap2evm.o obj-$(CONFIG_SND_OMAP_SOC_AM3517EVM) += snd-soc-am3517evm.o obj-$(CONFIG_SND_OMAP_SOC_OMAP_ABE_TWL6040) += snd-soc-omap-abe-twl6040.o obj-$(CONFIG_SND_OMAP_SOC_OMAP_TWL4030) += snd-soc-omap-twl4030.o -- cgit v1.2.3 From d65f63da22092176f3ffedcc013b801c7e7ba390 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 28 May 2013 19:22:09 +0200 Subject: ASoC: blackfin: Remove unused bf5xx-{i2s, tdm, ac97}-pcm.h The structs defined in these files are completely unused, so remove both the structs and the files. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/blackfin/bf5xx-ac97-pcm.c | 1 - sound/soc/blackfin/bf5xx-ac97-pcm.h | 26 -------------------------- sound/soc/blackfin/bf5xx-ad1836.c | 1 - sound/soc/blackfin/bf5xx-ad193x.c | 1 - sound/soc/blackfin/bf5xx-ad1980.c | 1 - sound/soc/blackfin/bf5xx-ad73311.c | 1 - sound/soc/blackfin/bf5xx-i2s-pcm.c | 1 - sound/soc/blackfin/bf5xx-i2s-pcm.h | 26 -------------------------- sound/soc/blackfin/bf5xx-ssm2602.c | 1 - sound/soc/blackfin/bf5xx-tdm-pcm.c | 1 - sound/soc/blackfin/bf5xx-tdm-pcm.h | 18 ------------------ 11 files changed, 78 deletions(-) delete mode 100644 sound/soc/blackfin/bf5xx-ac97-pcm.h delete mode 100644 sound/soc/blackfin/bf5xx-i2s-pcm.h delete mode 100644 sound/soc/blackfin/bf5xx-tdm-pcm.h diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.c b/sound/soc/blackfin/bf5xx-ac97-pcm.c index 7e2f36004a5a..53f84085bf1f 100644 --- a/sound/soc/blackfin/bf5xx-ac97-pcm.c +++ b/sound/soc/blackfin/bf5xx-ac97-pcm.c @@ -39,7 +39,6 @@ #include -#include "bf5xx-ac97-pcm.h" #include "bf5xx-ac97.h" #include "bf5xx-sport.h" diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.h b/sound/soc/blackfin/bf5xx-ac97-pcm.h deleted file mode 100644 index d324d5826a9b..000000000000 --- a/sound/soc/blackfin/bf5xx-ac97-pcm.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * linux/sound/arm/bf5xx-ac97-pcm.h -- ALSA PCM interface for the Blackfin - * - * Copyright 2007 Analog Device Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef _BF5XX_AC97_PCM_H -#define _BF5XX_AC97_PCM_H - -struct bf5xx_pcm_dma_params { - char *name; /* stream identifier */ -}; - -struct bf5xx_gpio { - u32 sys; - u32 rx; - u32 tx; - u32 clk; - u32 frm; -}; - -#endif diff --git a/sound/soc/blackfin/bf5xx-ad1836.c b/sound/soc/blackfin/bf5xx-ad1836.c index d23f4b0ea54f..152817633256 100644 --- a/sound/soc/blackfin/bf5xx-ad1836.c +++ b/sound/soc/blackfin/bf5xx-ad1836.c @@ -30,7 +30,6 @@ #include "../codecs/ad1836.h" -#include "bf5xx-tdm-pcm.h" #include "bf5xx-tdm.h" static struct snd_soc_card bf5xx_ad1836; diff --git a/sound/soc/blackfin/bf5xx-ad193x.c b/sound/soc/blackfin/bf5xx-ad193x.c index 0e55e9f2a514..ce773b31916f 100644 --- a/sound/soc/blackfin/bf5xx-ad193x.c +++ b/sound/soc/blackfin/bf5xx-ad193x.c @@ -39,7 +39,6 @@ #include "../codecs/ad193x.h" -#include "bf5xx-tdm-pcm.h" #include "bf5xx-tdm.h" static struct snd_soc_card bf5xx_ad193x; diff --git a/sound/soc/blackfin/bf5xx-ad1980.c b/sound/soc/blackfin/bf5xx-ad1980.c index b30f88bbd703..3450e8f9080d 100644 --- a/sound/soc/blackfin/bf5xx-ad1980.c +++ b/sound/soc/blackfin/bf5xx-ad1980.c @@ -48,7 +48,6 @@ #include "../codecs/ad1980.h" -#include "bf5xx-ac97-pcm.h" #include "bf5xx-ac97.h" static struct snd_soc_card bf5xx_board; diff --git a/sound/soc/blackfin/bf5xx-ad73311.c b/sound/soc/blackfin/bf5xx-ad73311.c index 61cc91d4a028..786bbdd96e7c 100644 --- a/sound/soc/blackfin/bf5xx-ad73311.c +++ b/sound/soc/blackfin/bf5xx-ad73311.c @@ -45,7 +45,6 @@ #include "../codecs/ad73311.h" #include "bf5xx-sport.h" -#include "bf5xx-i2s-pcm.h" #if CONFIG_SND_BF5XX_SPORT_NUM == 0 #define bfin_write_SPORT_TCR1 bfin_write_SPORT0_TCR1 diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.c b/sound/soc/blackfin/bf5xx-i2s-pcm.c index 262c1de364d8..8726c3ad4947 100644 --- a/sound/soc/blackfin/bf5xx-i2s-pcm.c +++ b/sound/soc/blackfin/bf5xx-i2s-pcm.c @@ -39,7 +39,6 @@ #include -#include "bf5xx-i2s-pcm.h" #include "bf5xx-sport.h" static void bf5xx_dma_irq(void *data) diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.h b/sound/soc/blackfin/bf5xx-i2s-pcm.h deleted file mode 100644 index 0c2c5a68d4ff..000000000000 --- a/sound/soc/blackfin/bf5xx-i2s-pcm.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * linux/sound/arm/bf5xx-i2s-pcm.h -- ALSA PCM interface for the Blackfin - * - * Copyright 2007 Analog Device Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef _BF5XX_I2S_PCM_H -#define _BF5XX_I2S_PCM_H - -struct bf5xx_pcm_dma_params { - char *name; /* stream identifier */ -}; - -struct bf5xx_gpio { - u32 sys; - u32 rx; - u32 tx; - u32 clk; - u32 frm; -}; - -#endif diff --git a/sound/soc/blackfin/bf5xx-ssm2602.c b/sound/soc/blackfin/bf5xx-ssm2602.c index 7dbeef1099b4..9c19ccc936e2 100644 --- a/sound/soc/blackfin/bf5xx-ssm2602.c +++ b/sound/soc/blackfin/bf5xx-ssm2602.c @@ -40,7 +40,6 @@ #include #include "../codecs/ssm2602.h" #include "bf5xx-sport.h" -#include "bf5xx-i2s-pcm.h" static struct snd_soc_card bf5xx_ssm2602; diff --git a/sound/soc/blackfin/bf5xx-tdm-pcm.c b/sound/soc/blackfin/bf5xx-tdm-pcm.c index 19e855d5677e..a6b5457036ef 100644 --- a/sound/soc/blackfin/bf5xx-tdm-pcm.c +++ b/sound/soc/blackfin/bf5xx-tdm-pcm.c @@ -39,7 +39,6 @@ #include -#include "bf5xx-tdm-pcm.h" #include "bf5xx-tdm.h" #include "bf5xx-sport.h" diff --git a/sound/soc/blackfin/bf5xx-tdm-pcm.h b/sound/soc/blackfin/bf5xx-tdm-pcm.h deleted file mode 100644 index 7f8cc01c4477..000000000000 --- a/sound/soc/blackfin/bf5xx-tdm-pcm.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * sound/soc/blackfin/bf5xx-tdm-pcm.h -- ALSA PCM interface for the Blackfin - * - * Copyright 2009 Analog Device Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef _BF5XX_TDM_PCM_H -#define _BF5XX_TDM_PCM_H - -struct bf5xx_pcm_dma_params { - char *name; /* stream identifier */ -}; - -#endif -- cgit v1.2.3 From b7ede5dea0746611a75cf49cd3b2f64097c53ef5 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 28 May 2013 19:22:10 +0200 Subject: ASoC: blackfin: bf5xx-i2s: Use dev_{err, dbg} instead of pr_{error, debug} Using dev_{err,dbg} instead of pr_{error,debug} makes it easier to recognize which device created the message. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/blackfin/bf5xx-i2s.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/sound/soc/blackfin/bf5xx-i2s.c b/sound/soc/blackfin/bf5xx-i2s.c index dd0c2a4f83a3..78411f266f60 100644 --- a/sound/soc/blackfin/bf5xx-i2s.c +++ b/sound/soc/blackfin/bf5xx-i2s.c @@ -74,7 +74,8 @@ static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, ret = -EINVAL; break; default: - printk(KERN_ERR "%s: Unknown DAI format type\n", __func__); + dev_err(cpu_dai->dev, "%s: Unknown DAI format type\n", + __func__); ret = -EINVAL; break; } @@ -88,7 +89,8 @@ static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, ret = -EINVAL; break; default: - printk(KERN_ERR "%s: Unknown DAI master type\n", __func__); + dev_err(cpu_dai->dev, "%s: Unknown DAI master type\n", + __func__); ret = -EINVAL; break; } @@ -141,14 +143,14 @@ static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream, ret = sport_config_rx(sport_handle, bf5xx_i2s->rcr1, bf5xx_i2s->rcr2, 0, 0); if (ret) { - pr_err("SPORT is busy!\n"); + dev_err(dai->dev, "SPORT is busy!\n"); return -EBUSY; } ret = sport_config_tx(sport_handle, bf5xx_i2s->tcr1, bf5xx_i2s->tcr2, 0, 0); if (ret) { - pr_err("SPORT is busy!\n"); + dev_err(dai->dev, "SPORT is busy!\n"); return -EBUSY; } } @@ -162,7 +164,7 @@ static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream, struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai); struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data; - pr_debug("%s enter\n", __func__); + dev_dbg(dai->dev, "%s enter\n", __func__); /* No active stream, SPORT is allowed to be configured again. */ if (!dai->active) bf5xx_i2s->configured = 0; @@ -173,7 +175,7 @@ static int bf5xx_i2s_suspend(struct snd_soc_dai *dai) { struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai); - pr_debug("%s : sport %d\n", __func__, dai->id); + dev_dbg(dai->dev, "%s : sport %d\n", __func__, dai->id); if (dai->capture_active) sport_rx_stop(sport_handle); @@ -188,19 +190,19 @@ static int bf5xx_i2s_resume(struct snd_soc_dai *dai) struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data; int ret; - pr_debug("%s : sport %d\n", __func__, dai->id); + dev_dbg(dai->dev, "%s : sport %d\n", __func__, dai->id); ret = sport_config_rx(sport_handle, bf5xx_i2s->rcr1, bf5xx_i2s->rcr2, 0, 0); if (ret) { - pr_err("SPORT is busy!\n"); + dev_err(dai->dev, "SPORT is busy!\n"); return -EBUSY; } ret = sport_config_tx(sport_handle, bf5xx_i2s->tcr1, bf5xx_i2s->tcr2, 0, 0); if (ret) { - pr_err("SPORT is busy!\n"); + dev_err(dai->dev, "SPORT is busy!\n"); return -EBUSY; } @@ -264,7 +266,7 @@ static int bf5xx_i2s_probe(struct platform_device *pdev) ret = snd_soc_register_component(&pdev->dev, &bf5xx_i2s_component, &bf5xx_i2s_dai, 1); if (ret) { - pr_err("Failed to register DAI: %d\n", ret); + dev_err(&pdev->dev, "Failed to register DAI: %d\n", ret); sport_done(sport_handle); return ret; } @@ -276,7 +278,7 @@ static int bf5xx_i2s_remove(struct platform_device *pdev) { struct sport_device *sport_handle = platform_get_drvdata(pdev); - pr_debug("%s enter\n", __func__); + dev_dbg(&pdev->dev, "%s enter\n", __func__); snd_soc_unregister_component(&pdev->dev); sport_done(sport_handle); -- cgit v1.2.3 From 634426048462373aba69c201390c3e75bc9d00d1 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 28 May 2013 19:22:11 +0200 Subject: ASoC: blackfin: bf5xx-sport: Allow setting rx and tx mask independently Since the hardware supports it there is no need to artificially limit this to just being able to set the same mask for both tx and rx. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/blackfin/bf5xx-ac97.c | 8 ++++---- sound/soc/blackfin/bf5xx-sport.c | 10 +++++----- sound/soc/blackfin/bf5xx-sport.h | 2 +- sound/soc/blackfin/bf5xx-tdm.c | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/sound/soc/blackfin/bf5xx-ac97.c b/sound/soc/blackfin/bf5xx-ac97.c index 490217325975..c66bef826ac5 100644 --- a/sound/soc/blackfin/bf5xx-ac97.c +++ b/sound/soc/blackfin/bf5xx-ac97.c @@ -231,9 +231,9 @@ static int bf5xx_ac97_resume(struct snd_soc_dai *dai) return 0; #if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT) - ret = sport_set_multichannel(sport, 16, 0x3FF, 1); + ret = sport_set_multichannel(sport, 16, 0x3FF, 0x3FF, 1); #else - ret = sport_set_multichannel(sport, 16, 0x1F, 1); + ret = sport_set_multichannel(sport, 16, 0x1F, 0x1F, 1); #endif if (ret) { pr_err("SPORT is busy!\n"); @@ -311,9 +311,9 @@ static int asoc_bfin_ac97_probe(struct platform_device *pdev) /*SPORT works in TDM mode to simulate AC97 transfers*/ #if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT) - ret = sport_set_multichannel(sport_handle, 16, 0x3FF, 1); + ret = sport_set_multichannel(sport_handle, 16, 0x3FF, 0x3FF, 1); #else - ret = sport_set_multichannel(sport_handle, 16, 0x1F, 1); + ret = sport_set_multichannel(sport_handle, 16, 0x1F, 0x1F, 1); #endif if (ret) { pr_err("SPORT is busy!\n"); diff --git a/sound/soc/blackfin/bf5xx-sport.c b/sound/soc/blackfin/bf5xx-sport.c index 2fd9f2a06968..695351241db8 100644 --- a/sound/soc/blackfin/bf5xx-sport.c +++ b/sound/soc/blackfin/bf5xx-sport.c @@ -46,10 +46,10 @@ /* note: multichannel is in units of 8 channels, * tdm_count is # channels NOT / 8 ! */ int sport_set_multichannel(struct sport_device *sport, - int tdm_count, u32 mask, int packed) + int tdm_count, u32 tx_mask, u32 rx_mask, int packed) { - pr_debug("%s tdm_count=%d mask:0x%08x packed=%d\n", __func__, - tdm_count, mask, packed); + pr_debug("%s tdm_count=%d tx_mask:0x%08x rx_mask:0x%08x packed=%d\n", + __func__, tdm_count, tx_mask, rx_mask, packed); if ((sport->regs->tcr1 & TSPEN) || (sport->regs->rcr1 & RSPEN)) return -EBUSY; @@ -65,8 +65,8 @@ int sport_set_multichannel(struct sport_device *sport, sport->regs->mcmc2 = FRAME_DELAY | MCMEN | \ (packed ? (MCDTXPE|MCDRXPE) : 0); - sport->regs->mtcs0 = mask; - sport->regs->mrcs0 = mask; + sport->regs->mtcs0 = tx_mask; + sport->regs->mrcs0 = rx_mask; sport->regs->mtcs1 = 0; sport->regs->mrcs1 = 0; sport->regs->mtcs2 = 0; diff --git a/sound/soc/blackfin/bf5xx-sport.h b/sound/soc/blackfin/bf5xx-sport.h index 5ab60bd613ea..9fc2192feb3b 100644 --- a/sound/soc/blackfin/bf5xx-sport.h +++ b/sound/soc/blackfin/bf5xx-sport.h @@ -128,7 +128,7 @@ void sport_done(struct sport_device *sport); /* note: multichannel is in units of 8 channels, tdm_count is number of channels * NOT / 8 ! all channels are enabled by default */ int sport_set_multichannel(struct sport_device *sport, int tdm_count, - u32 mask, int packed); + u32 tx_mask, u32 rx_mask, int packed); int sport_config_rx(struct sport_device *sport, unsigned int rcr1, unsigned int rcr2, diff --git a/sound/soc/blackfin/bf5xx-tdm.c b/sound/soc/blackfin/bf5xx-tdm.c index 69e9a3e935bd..aa0851650b56 100644 --- a/sound/soc/blackfin/bf5xx-tdm.c +++ b/sound/soc/blackfin/bf5xx-tdm.c @@ -198,7 +198,7 @@ static int bf5xx_tdm_resume(struct snd_soc_dai *dai) int ret; struct sport_device *sport = snd_soc_dai_get_drvdata(dai); - ret = sport_set_multichannel(sport, 8, 0xFF, 1); + ret = sport_set_multichannel(sport, 8, 0xFF, 0xFF, 1); if (ret) { pr_err("SPORT is busy!\n"); ret = -EBUSY; @@ -265,7 +265,7 @@ static int bfin_tdm_probe(struct platform_device *pdev) return -ENODEV; /* SPORT works in TDM mode */ - ret = sport_set_multichannel(sport_handle, 8, 0xFF, 1); + ret = sport_set_multichannel(sport_handle, 8, 0xFF, 0xFF, 1); if (ret) { pr_err("SPORT is busy!\n"); ret = -EBUSY; -- cgit v1.2.3 From 569ef65a973e19ec3327c8efbcf26bfc844af7e3 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 28 May 2013 19:22:12 +0200 Subject: ASoC: blackfin: bf5xx-i2s: Allocate buffer only as large as requested There is no need to always allocate the maximum buffer size. While we are at it also pass errors returned by snd_pcm_lib_malloc_pages() on to the upper layers. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/blackfin/bf5xx-i2s-pcm.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.c b/sound/soc/blackfin/bf5xx-i2s-pcm.c index 8726c3ad4947..107c1c9b1cb6 100644 --- a/sound/soc/blackfin/bf5xx-i2s-pcm.c +++ b/sound/soc/blackfin/bf5xx-i2s-pcm.c @@ -66,10 +66,7 @@ static const struct snd_pcm_hardware bf5xx_pcm_hardware = { static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - size_t size = bf5xx_pcm_hardware.buffer_bytes_max; - snd_pcm_lib_malloc_pages(substream, size); - - return 0; + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); } static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream) -- cgit v1.2.3 From a3935a29f68c261d31b41c896f95c9333b615abf Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 28 May 2013 19:22:13 +0200 Subject: ASoC: blackfin: bf5xx-i2s-pcm: Use snd_pcm_lib_preallocate_pages_for_all() Use snd_pcm_lib_preallocate_pages_for_all() for pre-allocating the DMA buffers instead of re-implementing the same functionality. Note that there is no need to call snd_pcm_lib_free_pages_for_all() since the ALSA core takes care of this for us. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/blackfin/bf5xx-i2s-pcm.c | 63 ++------------------------------------ 1 file changed, 3 insertions(+), 60 deletions(-) diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.c b/sound/soc/blackfin/bf5xx-i2s-pcm.c index 107c1c9b1cb6..9931a18c962e 100644 --- a/sound/soc/blackfin/bf5xx-i2s-pcm.c +++ b/sound/soc/blackfin/bf5xx-i2s-pcm.c @@ -209,55 +209,12 @@ static struct snd_pcm_ops bf5xx_pcm_i2s_ops = { .mmap = bf5xx_pcm_mmap, }; -static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) -{ - struct snd_pcm_substream *substream = pcm->streams[stream].substream; - struct snd_dma_buffer *buf = &substream->dma_buffer; - size_t size = bf5xx_pcm_hardware.buffer_bytes_max; - - buf->dev.type = SNDRV_DMA_TYPE_DEV; - buf->dev.dev = pcm->card->dev; - buf->private_data = NULL; - buf->area = dma_alloc_coherent(pcm->card->dev, size, - &buf->addr, GFP_KERNEL); - if (!buf->area) { - pr_err("Failed to allocate dma memory - Please increase uncached DMA memory region\n"); - return -ENOMEM; - } - buf->bytes = size; - - pr_debug("%s, area:%p, size:0x%08lx\n", __func__, - buf->area, buf->bytes); - - return 0; -} - -static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm) -{ - struct snd_pcm_substream *substream; - struct snd_dma_buffer *buf; - int stream; - - for (stream = 0; stream < 2; stream++) { - substream = pcm->streams[stream].substream; - if (!substream) - continue; - - buf = &substream->dma_buffer; - if (!buf->area) - continue; - dma_free_coherent(NULL, buf->bytes, buf->area, 0); - buf->area = NULL; - } -} - static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32); static int bf5xx_pcm_i2s_new(struct snd_soc_pcm_runtime *rtd) { struct snd_card *card = rtd->card->snd_card; - struct snd_pcm *pcm = rtd->pcm; - int ret = 0; + size_t size = bf5xx_pcm_hardware.buffer_bytes_max; pr_debug("%s enter\n", __func__); if (!card->dev->dma_mask) @@ -265,27 +222,13 @@ static int bf5xx_pcm_i2s_new(struct snd_soc_pcm_runtime *rtd) if (!card->dev->coherent_dma_mask) card->dev->coherent_dma_mask = DMA_BIT_MASK(32); - if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { - ret = bf5xx_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_PLAYBACK); - if (ret) - goto out; - } - - if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { - ret = bf5xx_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_CAPTURE); - if (ret) - goto out; - } - out: - return ret; + return snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, + SNDRV_DMA_TYPE_DEV, card->dev, size, size); } static struct snd_soc_platform_driver bf5xx_i2s_soc_platform = { .ops = &bf5xx_pcm_i2s_ops, .pcm_new = bf5xx_pcm_i2s_new, - .pcm_free = bf5xx_pcm_free_dma_buffers, }; static int bfin_i2s_soc_platform_probe(struct platform_device *pdev) -- cgit v1.2.3 From 8b5e2e396b589119bcc9c6a382a999e0202bae18 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 28 May 2013 19:22:14 +0200 Subject: ASoC: blackfin: bf5xx-i2s: Add support for TDM mode The bf5xx-i2s{,-pcm} and bf5xx-tdm{-pcm} drivers are nearly identical. Both are for the same hardware each supporting a slight different subset of the hardware. The bf5xx-i2s driver supports 2 channel I2S mode while the bf5xx-tdm driver supports TDM mode and channel remapping. This patch adds support for TDM mode and channel remapping to the bf5xx-i2s driver so that we'll eventually be able to retire the bf5xx-tdm driver. Unfortunately the hardware is fixed to using 8 channels in TDM mode. The bf5xx-tdm driver jumps through a few hoops to make it work well with other channel counts as well: * Don't support mmap * Translate between internal frame size (which is always 8 * sample_size) and ALSA frame size (which depends on the channel count) * Have special copy and silence callbacks which are aware of the mismatch between internal and ALSA frame size * Reduce the maximum buffer size to ensure that there is enough headroom for dummy data. The bf5xx-i2s driver is going to use the same mechanisms when being used int TDM mode. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/blackfin/bf5xx-i2s-pcm.c | 118 ++++++++++++++++++++++++++++++++++++- sound/soc/blackfin/bf5xx-i2s-pcm.h | 17 ++++++ sound/soc/blackfin/bf5xx-i2s.c | 105 ++++++++++++++++++++++++++++++--- 3 files changed, 229 insertions(+), 11 deletions(-) create mode 100644 sound/soc/blackfin/bf5xx-i2s-pcm.h diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.c b/sound/soc/blackfin/bf5xx-i2s-pcm.c index 9931a18c962e..9cb4a80df98e 100644 --- a/sound/soc/blackfin/bf5xx-i2s-pcm.c +++ b/sound/soc/blackfin/bf5xx-i2s-pcm.c @@ -40,6 +40,7 @@ #include #include "bf5xx-sport.h" +#include "bf5xx-i2s-pcm.h" static void bf5xx_dma_irq(void *data) { @@ -49,7 +50,6 @@ static void bf5xx_dma_irq(void *data) static const struct snd_pcm_hardware bf5xx_pcm_hardware = { .info = SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BLOCK_TRANSFER, .formats = SNDRV_PCM_FMTBIT_S16_LE | @@ -66,7 +66,16 @@ static const struct snd_pcm_hardware bf5xx_pcm_hardware = { static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); + struct snd_soc_pcm_runtime *rtd = substream->private_data; + unsigned int buffer_size = params_buffer_bytes(params); + struct bf5xx_i2s_pcm_data *dma_data; + + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + + if (dma_data->tdm_mode) + buffer_size = buffer_size / params_channels(params) * 8; + + return snd_pcm_lib_malloc_pages(substream, buffer_size); } static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream) @@ -78,9 +87,16 @@ static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream) static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream) { + struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; struct sport_device *sport = runtime->private_data; int period_bytes = frames_to_bytes(runtime, runtime->period_size); + struct bf5xx_i2s_pcm_data *dma_data; + + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + + if (dma_data->tdm_mode) + period_bytes = period_bytes / runtime->channels * 8; pr_debug("%s enter\n", __func__); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { @@ -127,10 +143,15 @@ static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream) { + struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; struct sport_device *sport = runtime->private_data; unsigned int diff; snd_pcm_uframes_t frames; + struct bf5xx_i2s_pcm_data *dma_data; + + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + pr_debug("%s enter\n", __func__); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { diff = sport_curr_offset_tx(sport); @@ -147,6 +168,8 @@ static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream) diff = 0; frames = bytes_to_frames(substream->runtime, diff); + if (dma_data->tdm_mode) + frames = frames * runtime->channels / 8; return frames; } @@ -158,11 +181,18 @@ static int bf5xx_pcm_open(struct snd_pcm_substream *substream) struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai); struct snd_pcm_runtime *runtime = substream->runtime; struct snd_dma_buffer *buf = &substream->dma_buffer; + struct bf5xx_i2s_pcm_data *dma_data; int ret; + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + pr_debug("%s enter\n", __func__); snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware); + if (dma_data->tdm_mode) + runtime->hw.buffer_bytes_max /= 4; + else + runtime->hw.info |= SNDRV_PCM_INFO_MMAP; ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); @@ -198,6 +228,88 @@ static int bf5xx_pcm_mmap(struct snd_pcm_substream *substream, return 0 ; } +static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel, + snd_pcm_uframes_t pos, void *buf, snd_pcm_uframes_t count) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned int sample_size = runtime->sample_bits / 8; + struct bf5xx_i2s_pcm_data *dma_data; + unsigned int i; + void *src, *dst; + + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + + if (dma_data->tdm_mode) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + src = buf; + dst = runtime->dma_area; + dst += pos * sample_size * 8; + + while (count--) { + for (i = 0; i < runtime->channels; i++) { + memcpy(dst + dma_data->map[i] * + sample_size, src, sample_size); + src += sample_size; + } + dst += 8 * sample_size; + } + } else { + src = runtime->dma_area; + src += pos * sample_size * 8; + dst = buf; + + while (count--) { + for (i = 0; i < runtime->channels; i++) { + memcpy(dst, src + dma_data->map[i] * + sample_size, sample_size); + dst += sample_size; + } + src += 8 * sample_size; + } + } + } else { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + src = buf; + dst = runtime->dma_area; + dst += frames_to_bytes(runtime, pos); + } else { + src = runtime->dma_area; + src += frames_to_bytes(runtime, pos); + dst = buf; + } + + memcpy(dst, src, frames_to_bytes(runtime, count)); + } + + return 0; +} + +static int bf5xx_pcm_silence(struct snd_pcm_substream *substream, + int channel, snd_pcm_uframes_t pos, snd_pcm_uframes_t count) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned int sample_size = runtime->sample_bits / 8; + void *buf = runtime->dma_area; + struct bf5xx_i2s_pcm_data *dma_data; + unsigned int offset, size; + + dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + + if (dma_data->tdm_mode) { + offset = pos * 8 * sample_size; + size = count * 8 * sample_size; + } else { + offset = frames_to_bytes(runtime, pos); + size = frames_to_bytes(runtime, count); + } + + snd_pcm_format_set_silence(runtime->format, buf + offset, size); + + return 0; +} + static struct snd_pcm_ops bf5xx_pcm_i2s_ops = { .open = bf5xx_pcm_open, .ioctl = snd_pcm_lib_ioctl, @@ -207,6 +319,8 @@ static struct snd_pcm_ops bf5xx_pcm_i2s_ops = { .trigger = bf5xx_pcm_trigger, .pointer = bf5xx_pcm_pointer, .mmap = bf5xx_pcm_mmap, + .copy = bf5xx_pcm_copy, + .silence = bf5xx_pcm_silence, }; static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32); diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.h b/sound/soc/blackfin/bf5xx-i2s-pcm.h new file mode 100644 index 000000000000..1f0435249f88 --- /dev/null +++ b/sound/soc/blackfin/bf5xx-i2s-pcm.h @@ -0,0 +1,17 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _BF5XX_TDM_PCM_H +#define _BF5XX_TDM_PCM_H + +#define BFIN_TDM_DAI_MAX_SLOTS 8 + +struct bf5xx_i2s_pcm_data { + unsigned int map[BFIN_TDM_DAI_MAX_SLOTS]; + bool tdm_mode; +}; + +#endif diff --git a/sound/soc/blackfin/bf5xx-i2s.c b/sound/soc/blackfin/bf5xx-i2s.c index 78411f266f60..9a174fc47d39 100644 --- a/sound/soc/blackfin/bf5xx-i2s.c +++ b/sound/soc/blackfin/bf5xx-i2s.c @@ -42,6 +42,7 @@ #include #include "bf5xx-sport.h" +#include "bf5xx-i2s-pcm.h" struct bf5xx_i2s_port { u16 tcr1; @@ -49,6 +50,13 @@ struct bf5xx_i2s_port { u16 tcr2; u16 rcr2; int configured; + + unsigned int slots; + unsigned int tx_mask; + unsigned int rx_mask; + + struct bf5xx_i2s_pcm_data tx_dma_data; + struct bf5xx_i2s_pcm_data rx_dma_data; }; static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, @@ -170,6 +178,64 @@ static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream, bf5xx_i2s->configured = 0; } +static int bf5xx_i2s_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot) +{ + struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai); + struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data; + unsigned int tx_mapped = 0, rx_mapped = 0; + unsigned int slot; + int i; + + if ((tx_num > BFIN_TDM_DAI_MAX_SLOTS) || + (rx_num > BFIN_TDM_DAI_MAX_SLOTS)) + return -EINVAL; + + for (i = 0; i < tx_num; i++) { + slot = tx_slot[i]; + if ((slot < BFIN_TDM_DAI_MAX_SLOTS) && + (!(tx_mapped & (1 << slot)))) { + bf5xx_i2s->tx_dma_data.map[i] = slot; + tx_mapped |= 1 << slot; + } else + return -EINVAL; + } + for (i = 0; i < rx_num; i++) { + slot = rx_slot[i]; + if ((slot < BFIN_TDM_DAI_MAX_SLOTS) && + (!(rx_mapped & (1 << slot)))) { + bf5xx_i2s->rx_dma_data.map[i] = slot; + rx_mapped |= 1 << slot; + } else + return -EINVAL; + } + + return 0; +} + +static int bf5xx_i2s_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int width) +{ + struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai); + struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data; + + if (slots % 8 != 0 || slots > 8) + return -EINVAL; + + if (width != 32) + return -EINVAL; + + bf5xx_i2s->slots = slots; + bf5xx_i2s->tx_mask = tx_mask; + bf5xx_i2s->rx_mask = rx_mask; + + bf5xx_i2s->tx_dma_data.tdm_mode = slots != 0; + bf5xx_i2s->rx_dma_data.tdm_mode = slots != 0; + + return sport_set_multichannel(sport_handle, slots, tx_mask, rx_mask, 0); +} + #ifdef CONFIG_PM static int bf5xx_i2s_suspend(struct snd_soc_dai *dai) { @@ -206,7 +272,8 @@ static int bf5xx_i2s_resume(struct snd_soc_dai *dai) return -EBUSY; } - return 0; + return sport_set_multichannel(sport_handle, bf5xx_i2s->slots, + bf5xx_i2s->tx_mask, bf5xx_i2s->rx_mask, 0); } #else @@ -214,6 +281,23 @@ static int bf5xx_i2s_resume(struct snd_soc_dai *dai) #define bf5xx_i2s_resume NULL #endif +static int bf5xx_i2s_dai_probe(struct snd_soc_dai *dai) +{ + struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai); + struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data; + unsigned int i; + + for (i = 0; i < BFIN_TDM_DAI_MAX_SLOTS; i++) { + bf5xx_i2s->tx_dma_data.map[i] = i; + bf5xx_i2s->rx_dma_data.map[i] = i; + } + + dai->playback_dma_data = &bf5xx_i2s->tx_dma_data; + dai->capture_dma_data = &bf5xx_i2s->rx_dma_data; + + return 0; +} + #define BF5XX_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \ @@ -226,22 +310,25 @@ static int bf5xx_i2s_resume(struct snd_soc_dai *dai) SNDRV_PCM_FMTBIT_S32_LE) static const struct snd_soc_dai_ops bf5xx_i2s_dai_ops = { - .shutdown = bf5xx_i2s_shutdown, - .hw_params = bf5xx_i2s_hw_params, - .set_fmt = bf5xx_i2s_set_dai_fmt, + .shutdown = bf5xx_i2s_shutdown, + .hw_params = bf5xx_i2s_hw_params, + .set_fmt = bf5xx_i2s_set_dai_fmt, + .set_tdm_slot = bf5xx_i2s_set_tdm_slot, + .set_channel_map = bf5xx_i2s_set_channel_map, }; static struct snd_soc_dai_driver bf5xx_i2s_dai = { + .probe = bf5xx_i2s_dai_probe, .suspend = bf5xx_i2s_suspend, .resume = bf5xx_i2s_resume, .playback = { - .channels_min = 1, - .channels_max = 2, + .channels_min = 2, + .channels_max = 8, .rates = BF5XX_I2S_RATES, .formats = BF5XX_I2S_FORMATS,}, .capture = { - .channels_min = 1, - .channels_max = 2, + .channels_min = 2, + .channels_max = 8, .rates = BF5XX_I2S_RATES, .formats = BF5XX_I2S_FORMATS,}, .ops = &bf5xx_i2s_dai_ops, @@ -257,7 +344,7 @@ static int bf5xx_i2s_probe(struct platform_device *pdev) int ret; /* configure SPORT for I2S */ - sport_handle = sport_init(pdev, 4, 2 * sizeof(u32), + sport_handle = sport_init(pdev, 4, 8 * sizeof(u32), sizeof(struct bf5xx_i2s_port)); if (!sport_handle) return -ENODEV; -- cgit v1.2.3 From b88546324ef1b61fc6e844e56ad4e90169514fb7 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 28 May 2013 19:22:15 +0200 Subject: ASoC: blackfin: Switch bf5xx-ad193x from bf5xx-tdm to bf5xx-i2s The bf5xx-i2s driver now has support for TDM mode and the bf5xx-tdm driver is going to be removed soon, so switch the driver over to bf5xx-i2s. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/blackfin/Kconfig | 18 +++++++++--------- sound/soc/blackfin/bf5xx-ad193x.c | 39 ++++++++++----------------------------- 2 files changed, 19 insertions(+), 38 deletions(-) diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig index 16b88f5c26e2..906c349a00ea 100644 --- a/sound/soc/blackfin/Kconfig +++ b/sound/soc/blackfin/Kconfig @@ -56,6 +56,15 @@ config SND_SOC_BFIN_EVAL_ADAV80X Note: This driver assumes that the ADAV80X digital record and playback interfaces are connected to the first SPORT port on the BF5XX board. +config SND_BF5XX_SOC_AD193X + tristate "SoC AD193X Audio support for Blackfin" + depends on SND_BF5XX_I2S + select SND_BF5XX_SOC_I2S + select SND_SOC_AD193X + help + Say Y if you want to add support for AD193X codec on Blackfin. + This driver supports AD1936, AD1937, AD1938 and AD1939. + config SND_BF5XX_SOC_AD73311 tristate "SoC AD73311 Audio support for Blackfin" depends on SND_BF5XX_I2S @@ -90,15 +99,6 @@ config SND_BF5XX_SOC_AD1836 help Say Y if you want to add support for SoC audio on BF5xx STAMP/EZKIT. -config SND_BF5XX_SOC_AD193X - tristate "SoC AD193X Audio support for Blackfin" - depends on SND_BF5XX_TDM - select SND_BF5XX_SOC_TDM - select SND_SOC_AD193X - help - Say Y if you want to add support for AD193X codec on Blackfin. - This driver supports AD1936, AD1937, AD1938 and AD1939. - config SND_BF5XX_AC97 tristate "SoC AC97 Audio for the ADI BF5xx chip" depends on BLACKFIN diff --git a/sound/soc/blackfin/bf5xx-ad193x.c b/sound/soc/blackfin/bf5xx-ad193x.c index ce773b31916f..603ad1f2b9b9 100644 --- a/sound/soc/blackfin/bf5xx-ad193x.c +++ b/sound/soc/blackfin/bf5xx-ad193x.c @@ -39,29 +39,16 @@ #include "../codecs/ad193x.h" -#include "bf5xx-tdm.h" - static struct snd_soc_card bf5xx_ad193x; -static int bf5xx_ad193x_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) +static int bf5xx_ad193x_link_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai = rtd->codec_dai; - unsigned int clk = 0; - unsigned int channel_map[] = {0, 1, 2, 3, 4, 5, 6, 7}; - int ret = 0; - - switch (params_rate(params)) { - case 48000: - clk = 24576000; - break; - } + int ret; /* set the codec system clock for DAC and ADC */ - ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk, - SND_SOC_CLOCK_IN); + ret = snd_soc_dai_set_sysclk(codec_dai, 0, 24576000, SND_SOC_CLOCK_IN); if (ret < 0) return ret; @@ -70,9 +57,7 @@ static int bf5xx_ad193x_hw_params(struct snd_pcm_substream *substream, if (ret < 0) return ret; - /* set cpu DAI channel mapping */ - ret = snd_soc_dai_set_channel_map(cpu_dai, ARRAY_SIZE(channel_map), - channel_map, ARRAY_SIZE(channel_map), channel_map); + ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0xFF, 0xFF, 8, 32); if (ret < 0) return ret; @@ -82,30 +67,26 @@ static int bf5xx_ad193x_hw_params(struct snd_pcm_substream *substream, #define BF5XX_AD193X_DAIFMT (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_IF | \ SND_SOC_DAIFMT_CBM_CFM) -static struct snd_soc_ops bf5xx_ad193x_ops = { - .hw_params = bf5xx_ad193x_hw_params, -}; - static struct snd_soc_dai_link bf5xx_ad193x_dai[] = { { .name = "ad193x", .stream_name = "AD193X", - .cpu_dai_name = "bfin-tdm.0", + .cpu_dai_name = "bfin-i2s.0", .codec_dai_name ="ad193x-hifi", - .platform_name = "bfin-tdm-pcm-audio", + .platform_name = "bfin-i2s-pcm-audio", .codec_name = "spi0.5", - .ops = &bf5xx_ad193x_ops, .dai_fmt = BF5XX_AD193X_DAIFMT, + .init = bf5xx_ad193x_link_init, }, { .name = "ad193x", .stream_name = "AD193X", - .cpu_dai_name = "bfin-tdm.1", + .cpu_dai_name = "bfin-i2s.1", .codec_dai_name ="ad193x-hifi", - .platform_name = "bfin-tdm-pcm-audio", + .platform_name = "bfin-i2s-pcm-audio", .codec_name = "spi0.5", - .ops = &bf5xx_ad193x_ops, .dai_fmt = BF5XX_AD193X_DAIFMT, + .init = bf5xx_ad193x_link_init, }, }; -- cgit v1.2.3 From 34f4095564ff334adae5ab4a9904f8d66d03e994 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 28 May 2013 19:22:16 +0200 Subject: ASoC: blackfin: Switch bf5xx-ad1836 from bf5xx-tdm to bf5xx-i2s The bf5xx-i2s driver now has support for TDM mode and the bf5xx-tdm driver is going to be removed soon, so switch the driver over to bf5xx-i2s. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- arch/blackfin/mach-bf527/boards/ezkit.c | 2 +- arch/blackfin/mach-bf533/boards/stamp.c | 2 +- arch/blackfin/mach-bf537/boards/stamp.c | 2 +- arch/blackfin/mach-bf561/boards/ezkit.c | 2 +- arch/blackfin/mach-bf609/boards/ezkit.c | 2 +- sound/soc/blackfin/Kconfig | 16 ++++++++-------- sound/soc/blackfin/bf5xx-ad1836.c | 18 +++++++----------- 7 files changed, 20 insertions(+), 24 deletions(-) diff --git a/arch/blackfin/mach-bf527/boards/ezkit.c b/arch/blackfin/mach-bf527/boards/ezkit.c index 29f16e5c37b9..82248955b624 100644 --- a/arch/blackfin/mach-bf527/boards/ezkit.c +++ b/arch/blackfin/mach-bf527/boards/ezkit.c @@ -590,7 +590,7 @@ static struct platform_device bfin_tdm = { #if defined(CONFIG_SND_BF5XX_SOC_AD1836) \ || defined(CONFIG_SND_BF5XX_SOC_AD1836_MODULE) static const char * const ad1836_link[] = { - "bfin-tdm.0", + "bfin-i2s.0", "spi0.4", }; static struct platform_device bfin_ad1836_machine = { diff --git a/arch/blackfin/mach-bf533/boards/stamp.c b/arch/blackfin/mach-bf533/boards/stamp.c index 6fca8698bf3b..a644a8bf92b5 100644 --- a/arch/blackfin/mach-bf533/boards/stamp.c +++ b/arch/blackfin/mach-bf533/boards/stamp.c @@ -620,7 +620,7 @@ static struct platform_device bfin_ac97_pcm = { #if defined(CONFIG_SND_BF5XX_SOC_AD1836) \ || defined(CONFIG_SND_BF5XX_SOC_AD1836_MODULE) static const char * const ad1836_link[] = { - "bfin-tdm.0", + "bfin-i2s.0", "spi0.4", }; static struct platform_device bfin_ad1836_machine = { diff --git a/arch/blackfin/mach-bf537/boards/stamp.c b/arch/blackfin/mach-bf537/boards/stamp.c index 6a3a14bcd3a1..d978ca93fcf7 100644 --- a/arch/blackfin/mach-bf537/boards/stamp.c +++ b/arch/blackfin/mach-bf537/boards/stamp.c @@ -2645,7 +2645,7 @@ static struct platform_device bfin_ac97_pcm = { #if defined(CONFIG_SND_BF5XX_SOC_AD1836) \ || defined(CONFIG_SND_BF5XX_SOC_AD1836_MODULE) static const char * const ad1836_link[] = { - "bfin-tdm.0", + "bfin-i2s.0", "spi0.4", }; static struct platform_device bfin_ad1836_machine = { diff --git a/arch/blackfin/mach-bf561/boards/ezkit.c b/arch/blackfin/mach-bf561/boards/ezkit.c index 551f866172cf..9512c369bc0e 100644 --- a/arch/blackfin/mach-bf561/boards/ezkit.c +++ b/arch/blackfin/mach-bf561/boards/ezkit.c @@ -542,7 +542,7 @@ static struct platform_device bfin_ac97 = { #if defined(CONFIG_SND_BF5XX_SOC_AD1836) \ || defined(CONFIG_SND_BF5XX_SOC_AD1836_MODULE) static const char * const ad1836_link[] = { - "bfin-tdm.0", + "bfin-i2s.0", "spi0.4", }; static struct platform_device bfin_ad1836_machine = { diff --git a/arch/blackfin/mach-bf609/boards/ezkit.c b/arch/blackfin/mach-bf609/boards/ezkit.c index 97d701639585..bba40aed4273 100644 --- a/arch/blackfin/mach-bf609/boards/ezkit.c +++ b/arch/blackfin/mach-bf609/boards/ezkit.c @@ -821,7 +821,7 @@ static struct platform_device bfin_i2s = { #if defined(CONFIG_SND_BF5XX_SOC_AD1836) \ || defined(CONFIG_SND_BF5XX_SOC_AD1836_MODULE) static const char * const ad1836_link[] = { - "bfin-tdm.0", + "bfin-i2s.0", "spi0.76", }; static struct platform_device bfin_ad1836_machine = { diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig index 906c349a00ea..4a67865bc4fc 100644 --- a/sound/soc/blackfin/Kconfig +++ b/sound/soc/blackfin/Kconfig @@ -56,6 +56,14 @@ config SND_SOC_BFIN_EVAL_ADAV80X Note: This driver assumes that the ADAV80X digital record and playback interfaces are connected to the first SPORT port on the BF5XX board. +config SND_BF5XX_SOC_AD1836 + tristate "SoC AD1836 Audio support for BF5xx" + depends on SND_BF5XX_I2S + select SND_BF5XX_SOC_I2S + select SND_SOC_AD1836 + help + Say Y if you want to add support for SoC audio on BF5xx STAMP/EZKIT. + config SND_BF5XX_SOC_AD193X tristate "SoC AD193X Audio support for Blackfin" depends on SND_BF5XX_I2S @@ -91,14 +99,6 @@ config SND_BF5XX_TDM mode. You will also need to select the audio interfaces to support below. -config SND_BF5XX_SOC_AD1836 - tristate "SoC AD1836 Audio support for BF5xx" - depends on SND_BF5XX_TDM - select SND_BF5XX_SOC_TDM - select SND_SOC_AD1836 - help - Say Y if you want to add support for SoC audio on BF5xx STAMP/EZKIT. - config SND_BF5XX_AC97 tristate "SoC AC97 Audio for the ADI BF5xx chip" depends on BLACKFIN diff --git a/sound/soc/blackfin/bf5xx-ad1836.c b/sound/soc/blackfin/bf5xx-ad1836.c index 152817633256..8fcfc4ec3a51 100644 --- a/sound/soc/blackfin/bf5xx-ad1836.c +++ b/sound/soc/blackfin/bf5xx-ad1836.c @@ -30,14 +30,10 @@ #include "../codecs/ad1836.h" -#include "bf5xx-tdm.h" - static struct snd_soc_card bf5xx_ad1836; -static int bf5xx_ad1836_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) +static int bf5xx_ad1836_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; unsigned int channel_map[] = {0, 4, 1, 5, 2, 6, 3, 7}; int ret = 0; @@ -48,13 +44,13 @@ static int bf5xx_ad1836_hw_params(struct snd_pcm_substream *substream, if (ret < 0) return ret; + ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0xFF, 0xFF, 8, 32); + if (ret < 0) + return ret; + return 0; } -static struct snd_soc_ops bf5xx_ad1836_ops = { - .hw_params = bf5xx_ad1836_hw_params, -}; - #define BF5XX_AD1836_DAIFMT (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_IF | \ SND_SOC_DAIFMT_CBM_CFM) @@ -62,9 +58,9 @@ static struct snd_soc_dai_link bf5xx_ad1836_dai = { .name = "ad1836", .stream_name = "AD1836", .codec_dai_name = "ad1836-hifi", - .platform_name = "bfin-tdm-pcm-audio", - .ops = &bf5xx_ad1836_ops, + .platform_name = "bfin-i2s-pcm-audio", .dai_fmt = BF5XX_AD1836_DAIFMT, + .init = bf5xx_ad1836_init, }; static struct snd_soc_card bf5xx_ad1836 = { -- cgit v1.2.3 From cc37961b21eb3d57d421ca34ffec9bbe0a6096c0 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 28 May 2013 19:22:17 +0200 Subject: ASoC: blackfin: Remove bf5xx-tdm driver Now that the bf5xx-i2s driver supports TDM mode and all users of the bf5xx-tdm driver have been switch over to using the bf5xx-i2s driver there is no need to keep the b5fxx-tdm driver around. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/blackfin/Kconfig | 13 -- sound/soc/blackfin/Makefile | 4 - sound/soc/blackfin/bf5xx-tdm-pcm.c | 343 ------------------------------------- sound/soc/blackfin/bf5xx-tdm.c | 328 ----------------------------------- sound/soc/blackfin/bf5xx-tdm.h | 23 --- 5 files changed, 711 deletions(-) delete mode 100644 sound/soc/blackfin/bf5xx-tdm-pcm.c delete mode 100644 sound/soc/blackfin/bf5xx-tdm.c delete mode 100644 sound/soc/blackfin/bf5xx-tdm.h diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig index 4a67865bc4fc..54f74f8cbb75 100644 --- a/sound/soc/blackfin/Kconfig +++ b/sound/soc/blackfin/Kconfig @@ -89,16 +89,6 @@ config SND_BFIN_AD73311_SE Enter the GPIO used to control AD73311's SE pin. Acceptable values are 0 to 7 -config SND_BF5XX_TDM - tristate "SoC I2S(TDM mode) Audio for the ADI BF5xx chip" - depends on (BLACKFIN && SND_SOC) - select SND_BF5XX_SOC_SPORT - help - Say Y or M if you want to add support for codecs attached to - the Blackfin SPORT (synchronous serial ports) interface in TDM - mode. - You will also need to select the audio interfaces to support below. - config SND_BF5XX_AC97 tristate "SoC AC97 Audio for the ADI BF5xx chip" depends on BLACKFIN @@ -174,9 +164,6 @@ config SND_BF5XX_SOC_I2S config SND_BF6XX_SOC_I2S tristate -config SND_BF5XX_SOC_TDM - tristate - config SND_BF5XX_SOC_AC97 tristate diff --git a/sound/soc/blackfin/Makefile b/sound/soc/blackfin/Makefile index 6fea1f4cbee2..ad0a6e99bc5d 100644 --- a/sound/soc/blackfin/Makefile +++ b/sound/soc/blackfin/Makefile @@ -1,23 +1,19 @@ # Blackfin Platform Support snd-bf5xx-ac97-objs := bf5xx-ac97-pcm.o snd-bf5xx-i2s-objs := bf5xx-i2s-pcm.o -snd-bf5xx-tdm-objs := bf5xx-tdm-pcm.o snd-soc-bf5xx-sport-objs := bf5xx-sport.o snd-soc-bf6xx-sport-objs := bf6xx-sport.o snd-soc-bf5xx-ac97-objs := bf5xx-ac97.o snd-soc-bf5xx-i2s-objs := bf5xx-i2s.o snd-soc-bf6xx-i2s-objs := bf6xx-i2s.o -snd-soc-bf5xx-tdm-objs := bf5xx-tdm.o obj-$(CONFIG_SND_BF5XX_AC97) += snd-bf5xx-ac97.o obj-$(CONFIG_SND_BF5XX_I2S) += snd-bf5xx-i2s.o -obj-$(CONFIG_SND_BF5XX_TDM) += snd-bf5xx-tdm.o obj-$(CONFIG_SND_BF5XX_SOC_SPORT) += snd-soc-bf5xx-sport.o obj-$(CONFIG_SND_BF6XX_SOC_SPORT) += snd-soc-bf6xx-sport.o obj-$(CONFIG_SND_BF5XX_SOC_AC97) += snd-soc-bf5xx-ac97.o obj-$(CONFIG_SND_BF5XX_SOC_I2S) += snd-soc-bf5xx-i2s.o obj-$(CONFIG_SND_BF6XX_SOC_I2S) += snd-soc-bf6xx-i2s.o -obj-$(CONFIG_SND_BF5XX_SOC_TDM) += snd-soc-bf5xx-tdm.o # Blackfin Machine Support snd-ad1836-objs := bf5xx-ad1836.o diff --git a/sound/soc/blackfin/bf5xx-tdm-pcm.c b/sound/soc/blackfin/bf5xx-tdm-pcm.c deleted file mode 100644 index a6b5457036ef..000000000000 --- a/sound/soc/blackfin/bf5xx-tdm-pcm.c +++ /dev/null @@ -1,343 +0,0 @@ -/* - * File: sound/soc/blackfin/bf5xx-tdm-pcm.c - * Author: Barry Song - * - * Created: Tue June 06 2009 - * Description: DMA driver for tdm codec - * - * Modified: - * Copyright 2009 Analog Devices Inc. - * - * Bugs: Enter bugs at http://blackfin.uclinux.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, see the file COPYING, or write - * to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include "bf5xx-tdm.h" -#include "bf5xx-sport.h" - -#define PCM_BUFFER_MAX 0x8000 -#define FRAGMENT_SIZE_MIN (4*1024) -#define FRAGMENTS_MIN 2 -#define FRAGMENTS_MAX 32 - -static void bf5xx_dma_irq(void *data) -{ - struct snd_pcm_substream *pcm = data; - snd_pcm_period_elapsed(pcm); -} - -static const struct snd_pcm_hardware bf5xx_pcm_hardware = { - .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_RESUME), - .formats = SNDRV_PCM_FMTBIT_S32_LE, - .rates = SNDRV_PCM_RATE_48000, - .channels_min = 2, - .channels_max = 8, - .buffer_bytes_max = PCM_BUFFER_MAX, - .period_bytes_min = FRAGMENT_SIZE_MIN, - .period_bytes_max = PCM_BUFFER_MAX/2, - .periods_min = FRAGMENTS_MIN, - .periods_max = FRAGMENTS_MAX, -}; - -static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - size_t size = bf5xx_pcm_hardware.buffer_bytes_max; - snd_pcm_lib_malloc_pages(substream, size * 4); - - return 0; -} - -static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream) -{ - snd_pcm_lib_free_pages(substream); - - return 0; -} - -static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct sport_device *sport = runtime->private_data; - int fragsize_bytes = frames_to_bytes(runtime, runtime->period_size); - - fragsize_bytes /= runtime->channels; - /* inflate the fragsize to match the dma width of SPORT */ - fragsize_bytes *= 8; - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - sport_set_tx_callback(sport, bf5xx_dma_irq, substream); - sport_config_tx_dma(sport, runtime->dma_area, - runtime->periods, fragsize_bytes); - } else { - sport_set_rx_callback(sport, bf5xx_dma_irq, substream); - sport_config_rx_dma(sport, runtime->dma_area, - runtime->periods, fragsize_bytes); - } - - return 0; -} - -static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct sport_device *sport = runtime->private_data; - int ret = 0; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - sport_tx_start(sport); - else - sport_rx_start(sport); - break; - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - sport_tx_stop(sport); - else - sport_rx_stop(sport); - break; - default: - ret = -EINVAL; - } - - return ret; -} - -static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct sport_device *sport = runtime->private_data; - unsigned int diff; - snd_pcm_uframes_t frames; - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - diff = sport_curr_offset_tx(sport); - frames = diff / (8*4); /* 32 bytes per frame */ - } else { - diff = sport_curr_offset_rx(sport); - frames = diff / (8*4); - } - return frames; -} - -static int bf5xx_pcm_open(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai); - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_dma_buffer *buf = &substream->dma_buffer; - - int ret = 0; - - snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware); - - ret = snd_pcm_hw_constraint_integer(runtime, - SNDRV_PCM_HW_PARAM_PERIODS); - if (ret < 0) - goto out; - - if (sport_handle != NULL) { - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - sport_handle->tx_buf = buf->area; - else - sport_handle->rx_buf = buf->area; - - runtime->private_data = sport_handle; - } else { - pr_err("sport_handle is NULL\n"); - ret = -ENODEV; - } -out: - return ret; -} - -static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel, - snd_pcm_uframes_t pos, void *buf, snd_pcm_uframes_t count) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct sport_device *sport = runtime->private_data; - struct bf5xx_tdm_port *tdm_port = sport->private_data; - unsigned int *src; - unsigned int *dst; - int i; - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - src = buf; - dst = (unsigned int *)substream->runtime->dma_area; - - dst += pos * 8; - while (count--) { - for (i = 0; i < substream->runtime->channels; i++) - *(dst + tdm_port->tx_map[i]) = *src++; - dst += 8; - } - } else { - src = (unsigned int *)substream->runtime->dma_area; - dst = buf; - - src += pos * 8; - while (count--) { - for (i = 0; i < substream->runtime->channels; i++) - *dst++ = *(src + tdm_port->rx_map[i]); - src += 8; - } - } - - return 0; -} - -static int bf5xx_pcm_silence(struct snd_pcm_substream *substream, - int channel, snd_pcm_uframes_t pos, snd_pcm_uframes_t count) -{ - unsigned char *buf = substream->runtime->dma_area; - buf += pos * 8 * 4; - memset(buf, '\0', count * 8 * 4); - - return 0; -} - -static struct snd_pcm_ops bf5xx_pcm_tdm_ops = { - .open = bf5xx_pcm_open, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = bf5xx_pcm_hw_params, - .hw_free = bf5xx_pcm_hw_free, - .prepare = bf5xx_pcm_prepare, - .trigger = bf5xx_pcm_trigger, - .pointer = bf5xx_pcm_pointer, - .copy = bf5xx_pcm_copy, - .silence = bf5xx_pcm_silence, -}; - -static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) -{ - struct snd_pcm_substream *substream = pcm->streams[stream].substream; - struct snd_dma_buffer *buf = &substream->dma_buffer; - size_t size = bf5xx_pcm_hardware.buffer_bytes_max; - - buf->dev.type = SNDRV_DMA_TYPE_DEV; - buf->dev.dev = pcm->card->dev; - buf->private_data = NULL; - buf->area = dma_alloc_coherent(pcm->card->dev, size * 4, - &buf->addr, GFP_KERNEL); - if (!buf->area) { - pr_err("Failed to allocate dma memory - Please increase uncached DMA memory region\n"); - return -ENOMEM; - } - buf->bytes = size; - - return 0; -} - -static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm) -{ - struct snd_pcm_substream *substream; - struct snd_dma_buffer *buf; - int stream; - - for (stream = 0; stream < 2; stream++) { - substream = pcm->streams[stream].substream; - if (!substream) - continue; - - buf = &substream->dma_buffer; - if (!buf->area) - continue; - dma_free_coherent(NULL, buf->bytes, buf->area, 0); - buf->area = NULL; - } -} - -static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32); - -static int bf5xx_pcm_tdm_new(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_card *card = rtd->card->snd_card; - struct snd_pcm *pcm = rtd->pcm; - int ret = 0; - - if (!card->dev->dma_mask) - card->dev->dma_mask = &bf5xx_pcm_dmamask; - if (!card->dev->coherent_dma_mask) - card->dev->coherent_dma_mask = DMA_BIT_MASK(32); - - if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { - ret = bf5xx_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_PLAYBACK); - if (ret) - goto out; - } - - if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { - ret = bf5xx_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_CAPTURE); - if (ret) - goto out; - } -out: - return ret; -} - -static struct snd_soc_platform_driver bf5xx_tdm_soc_platform = { - .ops = &bf5xx_pcm_tdm_ops, - .pcm_new = bf5xx_pcm_tdm_new, - .pcm_free = bf5xx_pcm_free_dma_buffers, -}; - -static int bf5xx_soc_platform_probe(struct platform_device *pdev) -{ - return snd_soc_register_platform(&pdev->dev, &bf5xx_tdm_soc_platform); -} - -static int bf5xx_soc_platform_remove(struct platform_device *pdev) -{ - snd_soc_unregister_platform(&pdev->dev); - return 0; -} - -static struct platform_driver bfin_tdm_driver = { - .driver = { - .name = "bfin-tdm-pcm-audio", - .owner = THIS_MODULE, - }, - - .probe = bf5xx_soc_platform_probe, - .remove = bf5xx_soc_platform_remove, -}; - -module_platform_driver(bfin_tdm_driver); - -MODULE_AUTHOR("Barry Song"); -MODULE_DESCRIPTION("ADI Blackfin TDM PCM DMA module"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/blackfin/bf5xx-tdm.c b/sound/soc/blackfin/bf5xx-tdm.c deleted file mode 100644 index aa0851650b56..000000000000 --- a/sound/soc/blackfin/bf5xx-tdm.c +++ /dev/null @@ -1,328 +0,0 @@ -/* - * File: sound/soc/blackfin/bf5xx-tdm.c - * Author: Barry Song - * - * Created: Thurs June 04 2009 - * Description: Blackfin I2S(TDM) CPU DAI driver - * Even though TDM mode can be as part of I2S DAI, but there - * are so much difference in configuration and data flow, - * it's very ugly to integrate I2S and TDM into a module - * - * Modified: - * Copyright 2009 Analog Devices Inc. - * - * Bugs: Enter bugs at http://blackfin.uclinux.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, see the file COPYING, or write - * to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "bf5xx-sport.h" -#include "bf5xx-tdm.h" - -static int bf5xx_tdm_set_dai_fmt(struct snd_soc_dai *cpu_dai, - unsigned int fmt) -{ - int ret = 0; - - /* interface format:support TDM,slave mode */ - switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { - case SND_SOC_DAIFMT_DSP_A: - break; - default: - printk(KERN_ERR "%s: Unknown DAI format type\n", __func__); - ret = -EINVAL; - break; - } - - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: - break; - case SND_SOC_DAIFMT_CBS_CFS: - case SND_SOC_DAIFMT_CBM_CFS: - case SND_SOC_DAIFMT_CBS_CFM: - ret = -EINVAL; - break; - default: - printk(KERN_ERR "%s: Unknown DAI master type\n", __func__); - ret = -EINVAL; - break; - } - - return ret; -} - -static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) -{ - struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai); - struct bf5xx_tdm_port *bf5xx_tdm = sport_handle->private_data; - int ret = 0; - - bf5xx_tdm->tcr2 &= ~0x1f; - bf5xx_tdm->rcr2 &= ~0x1f; - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S32_LE: - bf5xx_tdm->tcr2 |= 31; - bf5xx_tdm->rcr2 |= 31; - sport_handle->wdsize = 4; - break; - /* at present, we only support 32bit transfer */ - default: - pr_err("not supported PCM format yet\n"); - return -EINVAL; - break; - } - - if (!bf5xx_tdm->configured) { - /* - * TX and RX are not independent,they are enabled at the - * same time, even if only one side is running. So, we - * need to configure both of them at the time when the first - * stream is opened. - * - * CPU DAI:slave mode. - */ - ret = sport_config_rx(sport_handle, bf5xx_tdm->rcr1, - bf5xx_tdm->rcr2, 0, 0); - if (ret) { - pr_err("SPORT is busy!\n"); - return -EBUSY; - } - - ret = sport_config_tx(sport_handle, bf5xx_tdm->tcr1, - bf5xx_tdm->tcr2, 0, 0); - if (ret) { - pr_err("SPORT is busy!\n"); - return -EBUSY; - } - - bf5xx_tdm->configured = 1; - } - - return 0; -} - -static void bf5xx_tdm_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai); - struct bf5xx_tdm_port *bf5xx_tdm = sport_handle->private_data; - - /* No active stream, SPORT is allowed to be configured again. */ - if (!dai->active) - bf5xx_tdm->configured = 0; -} - -static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai, - unsigned int tx_num, unsigned int *tx_slot, - unsigned int rx_num, unsigned int *rx_slot) -{ - struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai); - struct bf5xx_tdm_port *bf5xx_tdm = sport_handle->private_data; - int i; - unsigned int slot; - unsigned int tx_mapped = 0, rx_mapped = 0; - - if ((tx_num > BFIN_TDM_DAI_MAX_SLOTS) || - (rx_num > BFIN_TDM_DAI_MAX_SLOTS)) - return -EINVAL; - - for (i = 0; i < tx_num; i++) { - slot = tx_slot[i]; - if ((slot < BFIN_TDM_DAI_MAX_SLOTS) && - (!(tx_mapped & (1 << slot)))) { - bf5xx_tdm->tx_map[i] = slot; - tx_mapped |= 1 << slot; - } else - return -EINVAL; - } - for (i = 0; i < rx_num; i++) { - slot = rx_slot[i]; - if ((slot < BFIN_TDM_DAI_MAX_SLOTS) && - (!(rx_mapped & (1 << slot)))) { - bf5xx_tdm->rx_map[i] = slot; - rx_mapped |= 1 << slot; - } else - return -EINVAL; - } - - return 0; -} - -#ifdef CONFIG_PM -static int bf5xx_tdm_suspend(struct snd_soc_dai *dai) -{ - struct sport_device *sport = snd_soc_dai_get_drvdata(dai); - - if (dai->playback_active) - sport_tx_stop(sport); - if (dai->capture_active) - sport_rx_stop(sport); - - /* isolate sync/clock pins from codec while sports resume */ - peripheral_free_list(sport->pin_req); - - return 0; -} - -static int bf5xx_tdm_resume(struct snd_soc_dai *dai) -{ - int ret; - struct sport_device *sport = snd_soc_dai_get_drvdata(dai); - - ret = sport_set_multichannel(sport, 8, 0xFF, 0xFF, 1); - if (ret) { - pr_err("SPORT is busy!\n"); - ret = -EBUSY; - } - - ret = sport_config_rx(sport, 0, 0x1F, 0, 0); - if (ret) { - pr_err("SPORT is busy!\n"); - ret = -EBUSY; - } - - ret = sport_config_tx(sport, 0, 0x1F, 0, 0); - if (ret) { - pr_err("SPORT is busy!\n"); - ret = -EBUSY; - } - - peripheral_request_list(sport->pin_req, "soc-audio"); - - return 0; -} - -#else -#define bf5xx_tdm_suspend NULL -#define bf5xx_tdm_resume NULL -#endif - -static const struct snd_soc_dai_ops bf5xx_tdm_dai_ops = { - .hw_params = bf5xx_tdm_hw_params, - .set_fmt = bf5xx_tdm_set_dai_fmt, - .shutdown = bf5xx_tdm_shutdown, - .set_channel_map = bf5xx_tdm_set_channel_map, -}; - -static struct snd_soc_dai_driver bf5xx_tdm_dai = { - .suspend = bf5xx_tdm_suspend, - .resume = bf5xx_tdm_resume, - .playback = { - .channels_min = 2, - .channels_max = 8, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S32_LE,}, - .capture = { - .channels_min = 2, - .channels_max = 8, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S32_LE,}, - .ops = &bf5xx_tdm_dai_ops, -}; - -static const struct snd_soc_component_driver bf5xx_tdm_component = { - .name = "bf5xx-tdm", -}; - -static int bfin_tdm_probe(struct platform_device *pdev) -{ - struct sport_device *sport_handle; - int ret; - - /* configure SPORT for TDM */ - sport_handle = sport_init(pdev, 4, 8 * sizeof(u32), - sizeof(struct bf5xx_tdm_port)); - if (!sport_handle) - return -ENODEV; - - /* SPORT works in TDM mode */ - ret = sport_set_multichannel(sport_handle, 8, 0xFF, 0xFF, 1); - if (ret) { - pr_err("SPORT is busy!\n"); - ret = -EBUSY; - goto sport_config_err; - } - - ret = sport_config_rx(sport_handle, 0, 0x1F, 0, 0); - if (ret) { - pr_err("SPORT is busy!\n"); - ret = -EBUSY; - goto sport_config_err; - } - - ret = sport_config_tx(sport_handle, 0, 0x1F, 0, 0); - if (ret) { - pr_err("SPORT is busy!\n"); - ret = -EBUSY; - goto sport_config_err; - } - - ret = snd_soc_register_component(&pdev->dev, &bf5xx_tdm_component, - &bf5xx_tdm_dai, 1); - if (ret) { - pr_err("Failed to register DAI: %d\n", ret); - goto sport_config_err; - } - - return 0; - -sport_config_err: - sport_done(sport_handle); - return ret; -} - -static int bfin_tdm_remove(struct platform_device *pdev) -{ - struct sport_device *sport_handle = platform_get_drvdata(pdev); - - snd_soc_unregister_component(&pdev->dev); - sport_done(sport_handle); - - return 0; -} - -static struct platform_driver bfin_tdm_driver = { - .probe = bfin_tdm_probe, - .remove = bfin_tdm_remove, - .driver = { - .name = "bfin-tdm", - .owner = THIS_MODULE, - }, -}; - -module_platform_driver(bfin_tdm_driver); - -/* Module information */ -MODULE_AUTHOR("Barry Song"); -MODULE_DESCRIPTION("TDM driver for ADI Blackfin"); -MODULE_LICENSE("GPL"); - diff --git a/sound/soc/blackfin/bf5xx-tdm.h b/sound/soc/blackfin/bf5xx-tdm.h deleted file mode 100644 index e986a3ea3315..000000000000 --- a/sound/soc/blackfin/bf5xx-tdm.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * sound/soc/blackfin/bf5xx-tdm.h - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef _BF5XX_TDM_H -#define _BF5XX_TDM_H - -#define BFIN_TDM_DAI_MAX_SLOTS 8 -struct bf5xx_tdm_port { - u16 tcr1; - u16 rcr1; - u16 tcr2; - u16 rcr2; - unsigned int tx_map[BFIN_TDM_DAI_MAX_SLOTS]; - unsigned int rx_map[BFIN_TDM_DAI_MAX_SLOTS]; - int configured; -}; - -#endif -- cgit v1.2.3 From 15502e0ca0da651b48c7def2983e7bb464349b2a Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 28 May 2013 19:22:18 +0200 Subject: blackfin: Remove references to the bf5x_tdm driver The bf5x_tdm driver has been removed. Remove all references to it from board code. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- arch/blackfin/mach-bf527/boards/ad7160eval.c | 12 ----------- arch/blackfin/mach-bf527/boards/ezkit.c | 30 +-------------------------- arch/blackfin/mach-bf533/boards/ezkit.c | 12 ----------- arch/blackfin/mach-bf533/boards/stamp.c | 31 ---------------------------- arch/blackfin/mach-bf537/boards/stamp.c | 28 ------------------------- arch/blackfin/mach-bf548/boards/ezkit.c | 28 +------------------------ arch/blackfin/mach-bf561/boards/ezkit.c | 12 ----------- 7 files changed, 2 insertions(+), 151 deletions(-) diff --git a/arch/blackfin/mach-bf527/boards/ad7160eval.c b/arch/blackfin/mach-bf527/boards/ad7160eval.c index d58f50e5aa4b..1e7be62fccb6 100644 --- a/arch/blackfin/mach-bf527/boards/ad7160eval.c +++ b/arch/blackfin/mach-bf527/boards/ad7160eval.c @@ -283,14 +283,6 @@ static struct platform_device bfin_i2s = { }; #endif -#if defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE) -static struct platform_device bfin_tdm = { - .name = "bfin-tdm", - .id = CONFIG_SND_BF5XX_SPORT_NUM, - /* TODO: add platform data here */ -}; -#endif - static struct spi_board_info bfin_spi_board_info[] __initdata = { #if defined(CONFIG_MTD_M25P80) \ || defined(CONFIG_MTD_M25P80_MODULE) @@ -800,10 +792,6 @@ static struct platform_device *stamp_devices[] __initdata = { #if defined(CONFIG_SND_BF5XX_I2S) || defined(CONFIG_SND_BF5XX_I2S_MODULE) &bfin_i2s, #endif - -#if defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE) - &bfin_tdm, -#endif }; static int __init ad7160eval_init(void) diff --git a/arch/blackfin/mach-bf527/boards/ezkit.c b/arch/blackfin/mach-bf527/boards/ezkit.c index 82248955b624..d0a0c5e527cd 100644 --- a/arch/blackfin/mach-bf527/boards/ezkit.c +++ b/arch/blackfin/mach-bf527/boards/ezkit.c @@ -493,8 +493,7 @@ static const struct ad7879_platform_data bfin_ad7879_ts_info = { }; #endif -#if defined(CONFIG_SND_BF5XX_I2S) || defined(CONFIG_SND_BF5XX_I2S_MODULE) || \ - defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE) +#if defined(CONFIG_SND_BF5XX_I2S) || defined(CONFIG_SND_BF5XX_I2S_MODULE) static const u16 bfin_snd_pin[][7] = { {P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS, @@ -549,13 +548,6 @@ static struct platform_device bfin_i2s_pcm = { }; #endif -#if defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE) -static struct platform_device bfin_tdm_pcm = { - .name = "bfin-tdm-pcm-audio", - .id = -1, -}; -#endif - #if defined(CONFIG_SND_BF5XX_AC97) || defined(CONFIG_SND_BF5XX_AC97_MODULE) static struct platform_device bfin_ac97_pcm = { .name = "bfin-ac97-pcm-audio", @@ -575,18 +567,6 @@ static struct platform_device bfin_i2s = { }; #endif -#if defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE) -static struct platform_device bfin_tdm = { - .name = "bfin-tdm", - .id = CONFIG_SND_BF5XX_SPORT_NUM, - .num_resources = ARRAY_SIZE(bfin_snd_resources[CONFIG_SND_BF5XX_SPORT_NUM]), - .resource = bfin_snd_resources[CONFIG_SND_BF5XX_SPORT_NUM], - .dev = { - .platform_data = &bfin_snd_data[CONFIG_SND_BF5XX_SPORT_NUM], - }, -}; -#endif - #if defined(CONFIG_SND_BF5XX_SOC_AD1836) \ || defined(CONFIG_SND_BF5XX_SOC_AD1836_MODULE) static const char * const ad1836_link[] = { @@ -1269,10 +1249,6 @@ static struct platform_device *stamp_devices[] __initdata = { &bfin_i2s_pcm, #endif -#if defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE) - &bfin_tdm_pcm, -#endif - #if defined(CONFIG_SND_BF5XX_AC97) || defined(CONFIG_SND_BF5XX_AC97_MODULE) &bfin_ac97_pcm, #endif @@ -1281,10 +1257,6 @@ static struct platform_device *stamp_devices[] __initdata = { &bfin_i2s, #endif -#if defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE) - &bfin_tdm, -#endif - #if defined(CONFIG_SND_BF5XX_SOC_AD1836) || \ defined(CONFIG_SND_BF5XX_SOC_AD1836_MODULE) &bfin_ad1836_machine, diff --git a/arch/blackfin/mach-bf533/boards/ezkit.c b/arch/blackfin/mach-bf533/boards/ezkit.c index 07811c209b9d..90fb0d14b147 100644 --- a/arch/blackfin/mach-bf533/boards/ezkit.c +++ b/arch/blackfin/mach-bf533/boards/ezkit.c @@ -450,14 +450,6 @@ static struct platform_device bfin_i2s = { }; #endif -#if defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE) -static struct platform_device bfin_tdm = { - .name = "bfin-tdm", - .id = CONFIG_SND_BF5XX_SPORT_NUM, - /* TODO: add platform data here */ -}; -#endif - #if defined(CONFIG_SND_BF5XX_AC97) || defined(CONFIG_SND_BF5XX_AC97_MODULE) static struct platform_device bfin_ac97 = { .name = "bfin-ac97", @@ -516,10 +508,6 @@ static struct platform_device *ezkit_devices[] __initdata = { &bfin_i2s, #endif -#if defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE) - &bfin_tdm, -#endif - #if defined(CONFIG_SND_BF5XX_AC97) || defined(CONFIG_SND_BF5XX_AC97_MODULE) &bfin_ac97, #endif diff --git a/arch/blackfin/mach-bf533/boards/stamp.c b/arch/blackfin/mach-bf533/boards/stamp.c index a644a8bf92b5..1ea1dda98ad7 100644 --- a/arch/blackfin/mach-bf533/boards/stamp.c +++ b/arch/blackfin/mach-bf533/boards/stamp.c @@ -542,7 +542,6 @@ static struct platform_device bfin_dpmc = { }; #if defined(CONFIG_SND_BF5XX_I2S) || defined(CONFIG_SND_BF5XX_I2S_MODULE) || \ - defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE) \ || defined(CONFIG_SND_BF5XX_AC97) || \ defined(CONFIG_SND_BF5XX_AC97_MODULE) @@ -603,13 +602,6 @@ static struct platform_device bfin_i2s_pcm = { }; #endif -#if defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE) -static struct platform_device bfin_tdm_pcm = { - .name = "bfin-tdm-pcm-audio", - .id = -1, -}; -#endif - #if defined(CONFIG_SND_BF5XX_AC97) || defined(CONFIG_SND_BF5XX_AC97_MODULE) static struct platform_device bfin_ac97_pcm = { .name = "bfin-ac97-pcm-audio", @@ -675,20 +667,6 @@ static struct platform_device bfin_i2s = { }; #endif -#if defined(CONFIG_SND_BF5XX_SOC_TDM) || \ - defined(CONFIG_SND_BF5XX_SOC_TDM_MODULE) -static struct platform_device bfin_tdm = { - .name = "bfin-tdm", - .id = CONFIG_SND_BF5XX_SPORT_NUM, - .num_resources = - ARRAY_SIZE(bfin_snd_resources[CONFIG_SND_BF5XX_SPORT_NUM]), - .resource = bfin_snd_resources[CONFIG_SND_BF5XX_SPORT_NUM], - .dev = { - .platform_data = &bfin_snd_data[CONFIG_SND_BF5XX_SPORT_NUM], - }, -}; -#endif - #if defined(CONFIG_SND_BF5XX_SOC_AC97) || \ defined(CONFIG_SND_BF5XX_SOC_AC97_MODULE) static struct platform_device bfin_ac97 = { @@ -761,10 +739,6 @@ static struct platform_device *stamp_devices[] __initdata = { &bfin_i2s_pcm, #endif -#if defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE) - &bfin_tdm_pcm, -#endif - #if defined(CONFIG_SND_BF5XX_AC97) || defined(CONFIG_SND_BF5XX_AC97_MODULE) &bfin_ac97_pcm, #endif @@ -792,11 +766,6 @@ static struct platform_device *stamp_devices[] __initdata = { &bfin_i2s, #endif -#if defined(CONFIG_SND_BF5XX_SOC_TDM) || \ - defined(CONFIG_SND_BF5XX_SOC_TDM_MODULE) - &bfin_tdm, -#endif - #if defined(CONFIG_SND_BF5XX_SOC_AC97) || \ defined(CONFIG_SND_BF5XX_SOC_AC97_MODULE) &bfin_ac97, diff --git a/arch/blackfin/mach-bf537/boards/stamp.c b/arch/blackfin/mach-bf537/boards/stamp.c index d978ca93fcf7..44fd1d4682ac 100644 --- a/arch/blackfin/mach-bf537/boards/stamp.c +++ b/arch/blackfin/mach-bf537/boards/stamp.c @@ -2570,7 +2570,6 @@ static struct platform_device bfin_dpmc = { }; #if defined(CONFIG_SND_BF5XX_I2S) || defined(CONFIG_SND_BF5XX_I2S_MODULE) || \ - defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE) || \ defined(CONFIG_SND_BF5XX_AC97) || defined(CONFIG_SND_BF5XX_AC97_MODULE) #define SPORT_REQ(x) \ @@ -2628,13 +2627,6 @@ static struct platform_device bfin_i2s_pcm = { }; #endif -#if defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE) -static struct platform_device bfin_tdm_pcm = { - .name = "bfin-tdm-pcm-audio", - .id = -1, -}; -#endif - #if defined(CONFIG_SND_BF5XX_AC97) || defined(CONFIG_SND_BF5XX_AC97_MODULE) static struct platform_device bfin_ac97_pcm = { .name = "bfin-ac97-pcm-audio", @@ -2699,18 +2691,6 @@ static struct platform_device bfin_i2s = { }; #endif -#if defined(CONFIG_SND_BF5XX_SOC_TDM) || defined(CONFIG_SND_BF5XX_SOC_TDM_MODULE) -static struct platform_device bfin_tdm = { - .name = "bfin-tdm", - .id = CONFIG_SND_BF5XX_SPORT_NUM, - .num_resources = ARRAY_SIZE(bfin_snd_resources[CONFIG_SND_BF5XX_SPORT_NUM]), - .resource = bfin_snd_resources[CONFIG_SND_BF5XX_SPORT_NUM], - .dev = { - .platform_data = &bfin_snd_data[CONFIG_SND_BF5XX_SPORT_NUM], - }, -}; -#endif - #if defined(CONFIG_SND_BF5XX_SOC_AC97) || defined(CONFIG_SND_BF5XX_SOC_AC97_MODULE) static struct platform_device bfin_ac97 = { .name = "bfin-ac97", @@ -2935,10 +2915,6 @@ static struct platform_device *stamp_devices[] __initdata = { &bfin_i2s_pcm, #endif -#if defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE) - &bfin_tdm_pcm, -#endif - #if defined(CONFIG_SND_BF5XX_AC97) || defined(CONFIG_SND_BF5XX_AC97_MODULE) &bfin_ac97_pcm, #endif @@ -2961,10 +2937,6 @@ static struct platform_device *stamp_devices[] __initdata = { &bfin_i2s, #endif -#if defined(CONFIG_SND_BF5XX_SOC_TDM) || defined(CONFIG_SND_BF5XX_SOC_TDM_MODULE) - &bfin_tdm, -#endif - #if defined(CONFIG_SND_BF5XX_SOC_AC97) || defined(CONFIG_SND_BF5XX_SOC_AC97_MODULE) &bfin_ac97, #endif diff --git a/arch/blackfin/mach-bf548/boards/ezkit.c b/arch/blackfin/mach-bf548/boards/ezkit.c index c4d07f040947..372eb54944ef 100644 --- a/arch/blackfin/mach-bf548/boards/ezkit.c +++ b/arch/blackfin/mach-bf548/boards/ezkit.c @@ -1393,7 +1393,6 @@ static struct platform_device bfin_dpmc = { }; #if defined(CONFIG_SND_BF5XX_I2S) || defined(CONFIG_SND_BF5XX_I2S_MODULE) || \ - defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE) || \ defined(CONFIG_SND_BF5XX_AC97) || defined(CONFIG_SND_BF5XX_AC97_MODULE) #define SPORT_REQ(x) \ @@ -1461,13 +1460,6 @@ static struct platform_device bfin_i2s_pcm = { }; #endif -#if defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE) -static struct platform_device bfin_tdm_pcm = { - .name = "bfin-tdm-pcm-audio", - .id = -1, -}; -#endif - #if defined(CONFIG_SND_BF5XX_AC97) || defined(CONFIG_SND_BF5XX_AC97_MODULE) static struct platform_device bfin_ac97_pcm = { .name = "bfin-ac97-pcm-audio", @@ -1501,18 +1493,6 @@ static struct platform_device bfin_i2s = { }; #endif -#if defined(CONFIG_SND_BF5XX_SOC_TDM) || defined(CONFIG_SND_BF5XX_SOC_TDM_MODULE) -static struct platform_device bfin_tdm = { - .name = "bfin-tdm", - .id = CONFIG_SND_BF5XX_SPORT_NUM, - .num_resources = ARRAY_SIZE(bfin_snd_resources[CONFIG_SND_BF5XX_SPORT_NUM]), - .resource = bfin_snd_resources[CONFIG_SND_BF5XX_SPORT_NUM], - .dev = { - .platform_data = &bfin_snd_data[CONFIG_SND_BF5XX_SPORT_NUM], - }, -}; -#endif - #if defined(CONFIG_SND_BF5XX_SOC_AC97) || defined(CONFIG_SND_BF5XX_SOC_AC97_MODULE) static struct platform_device bfin_ac97 = { .name = "bfin-ac97", @@ -1646,9 +1626,7 @@ static struct platform_device *ezkit_devices[] __initdata = { #if defined(CONFIG_SND_BF5XX_I2S) || defined(CONFIG_SND_BF5XX_I2S_MODULE) &bfin_i2s_pcm, #endif -#if defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE) - &bfin_tdm_pcm, -#endif + #if defined(CONFIG_SND_BF5XX_AC97) || defined(CONFIG_SND_BF5XX_AC97_MODULE) &bfin_ac97_pcm, #endif @@ -1661,10 +1639,6 @@ static struct platform_device *ezkit_devices[] __initdata = { &bfin_i2s, #endif -#if defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE) - &bfin_tdm, -#endif - #if defined(CONFIG_SND_BF5XX_AC97) || defined(CONFIG_SND_BF5XX_AC97_MODULE) &bfin_ac97, #endif diff --git a/arch/blackfin/mach-bf561/boards/ezkit.c b/arch/blackfin/mach-bf561/boards/ezkit.c index 9512c369bc0e..92938e79b9e3 100644 --- a/arch/blackfin/mach-bf561/boards/ezkit.c +++ b/arch/blackfin/mach-bf561/boards/ezkit.c @@ -523,14 +523,6 @@ static struct platform_device bfin_i2s = { }; #endif -#if defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE) -static struct platform_device bfin_tdm = { - .name = "bfin-tdm", - .id = CONFIG_SND_BF5XX_SPORT_NUM, - /* TODO: add platform data here */ -}; -#endif - #if defined(CONFIG_SND_BF5XX_AC97) || defined(CONFIG_SND_BF5XX_AC97_MODULE) static struct platform_device bfin_ac97 = { .name = "bfin-ac97", @@ -611,10 +603,6 @@ static struct platform_device *ezkit_devices[] __initdata = { &bfin_i2s, #endif -#if defined(CONFIG_SND_BF5XX_TDM) || defined(CONFIG_SND_BF5XX_TDM_MODULE) - &bfin_tdm, -#endif - #if defined(CONFIG_SND_BF5XX_AC97) || defined(CONFIG_SND_BF5XX_AC97_MODULE) &bfin_ac97, #endif -- cgit v1.2.3 From 50941968fc9e359a89da2136b11328fe700dbd7d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 30 May 2013 13:42:26 +0100 Subject: ASoC: wm8994: Add digital loopback paths There is loopback control within the audio interfaces, provide control of this as there are some obscure scenarios where this could be used in production. Signed-off-by: Mark Brown --- sound/soc/codecs/wm8994.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 2c2a183da2b6..55a5cc639b90 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -1494,6 +1494,24 @@ static const char *aif1dac_text[] = { "AIF1DACDAT", "AIF3DACDAT", }; +static const char *loopback_text[] = { + "None", "ADCDAT", +}; + +static const struct soc_enum aif1_loopback_enum = + SOC_ENUM_SINGLE(WM8994_AIF1_CONTROL_2, WM8994_AIF1_LOOPBACK_SHIFT, 2, + loopback_text); + +static const struct snd_kcontrol_new aif1_loopback = + SOC_DAPM_ENUM("AIF1 Loopback", aif1_loopback_enum); + +static const struct soc_enum aif2_loopback_enum = + SOC_ENUM_SINGLE(WM8994_AIF2_CONTROL_2, WM8994_AIF2_LOOPBACK_SHIFT, 2, + loopback_text); + +static const struct snd_kcontrol_new aif2_loopback = + SOC_DAPM_ENUM("AIF2 Loopback", aif2_loopback_enum); + static const struct soc_enum aif1dac_enum = SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 0, 2, aif1dac_text); @@ -1740,6 +1758,9 @@ SND_SOC_DAPM_ADC("DMIC1R", NULL, WM8994_POWER_MANAGEMENT_4, 2, 0), SND_SOC_DAPM_ADC("ADCL", NULL, SND_SOC_NOPM, 1, 0), SND_SOC_DAPM_ADC("ADCR", NULL, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_MUX("AIF1 Loopback", SND_SOC_NOPM, 0, 0, &aif1_loopback), +SND_SOC_DAPM_MUX("AIF2 Loopback", SND_SOC_NOPM, 0, 0, &aif2_loopback), + SND_SOC_DAPM_POST("Debug log", post_ev), }; @@ -1871,9 +1892,9 @@ static const struct snd_soc_dapm_route intercon[] = { { "AIF1DAC2L", NULL, "AIF1DAC Mux" }, { "AIF1DAC2R", NULL, "AIF1DAC Mux" }, - { "AIF1DAC Mux", "AIF1DACDAT", "AIF1DACDAT" }, + { "AIF1DAC Mux", "AIF1DACDAT", "AIF1 Loopback" }, { "AIF1DAC Mux", "AIF3DACDAT", "AIF3DACDAT" }, - { "AIF2DAC Mux", "AIF2DACDAT", "AIF2DACDAT" }, + { "AIF2DAC Mux", "AIF2DACDAT", "AIF2 Loopback" }, { "AIF2DAC Mux", "AIF3DACDAT", "AIF3DACDAT" }, { "AIF2ADC Mux", "AIF2ADCDAT", "AIF2ADCL" }, { "AIF2ADC Mux", "AIF2ADCDAT", "AIF2ADCR" }, @@ -1924,6 +1945,12 @@ static const struct snd_soc_dapm_route intercon[] = { { "AIF3ADCDAT", "AIF2DACDAT", "AIF2DACL" }, { "AIF3ADCDAT", "AIF2DACDAT", "AIF2DACR" }, + /* Loopback */ + { "AIF1 Loopback", "ADCDAT", "AIF1ADCDAT" }, + { "AIF1 Loopback", "None", "AIF1DACDAT" }, + { "AIF2 Loopback", "ADCDAT", "AIF2ADCDAT" }, + { "AIF2 Loopback", "None", "AIF2DACDAT" }, + /* Sidetone */ { "Left Sidetone", "ADC/DMIC1", "ADCL Mux" }, { "Left Sidetone", "DMIC2", "DMIC2L" }, -- cgit v1.2.3 From 926edcc747e2efb3c9add7ed4dbc4e7a3a959d02 Mon Sep 17 00:00:00 2001 From: Cyril Chemparathy Date: Sun, 22 Jul 2012 13:40:38 -0400 Subject: ARM: LPAE: use signed arithmetic for mask definitions This patch applies to PAGE_MASK, PMD_MASK, and PGDIR_MASK, where forcing unsigned long math truncates the mask at the 32-bits. This clearly does bad things on PAE systems. This patch fixes this problem by defining these masks as signed quantities. We then rely on sign extension to do the right thing. Signed-off-by: Cyril Chemparathy Signed-off-by: Vitaly Andrianov Reviewed-by: Nicolas Pitre Reviewed-by: Catalin Marinas Tested-by: Santosh Shilimkar Tested-by: Subash Patel Signed-off-by: Will Deacon --- arch/arm/include/asm/page.h | 2 +- arch/arm/include/asm/pgtable-3level.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm/include/asm/page.h b/arch/arm/include/asm/page.h index 812a4944e783..6363f3d1d505 100644 --- a/arch/arm/include/asm/page.h +++ b/arch/arm/include/asm/page.h @@ -13,7 +13,7 @@ /* PAGE_SHIFT determines the page size */ #define PAGE_SHIFT 12 #define PAGE_SIZE (_AC(1,UL) << PAGE_SHIFT) -#define PAGE_MASK (~(PAGE_SIZE-1)) +#define PAGE_MASK (~((1 << PAGE_SHIFT) - 1)) #ifndef __ASSEMBLY__ diff --git a/arch/arm/include/asm/pgtable-3level.h b/arch/arm/include/asm/pgtable-3level.h index 86b8fe398b95..5b85b218b0f0 100644 --- a/arch/arm/include/asm/pgtable-3level.h +++ b/arch/arm/include/asm/pgtable-3level.h @@ -48,16 +48,16 @@ #define PMD_SHIFT 21 #define PMD_SIZE (1UL << PMD_SHIFT) -#define PMD_MASK (~(PMD_SIZE-1)) +#define PMD_MASK (~((1 << PMD_SHIFT) - 1)) #define PGDIR_SIZE (1UL << PGDIR_SHIFT) -#define PGDIR_MASK (~(PGDIR_SIZE-1)) +#define PGDIR_MASK (~((1 << PGDIR_SHIFT) - 1)) /* * section address mask and size definitions. */ #define SECTION_SHIFT 21 #define SECTION_SIZE (1UL << SECTION_SHIFT) -#define SECTION_MASK (~(SECTION_SIZE-1)) +#define SECTION_MASK (~((1 << SECTION_SHIFT) - 1)) #define USER_PTRS_PER_PGD (PAGE_OFFSET / PGDIR_SIZE) -- cgit v1.2.3 From 20d6956d8cd2452cec0889ff040f18afc03c2e6b Mon Sep 17 00:00:00 2001 From: Vitaly Andrianov Date: Tue, 10 Jul 2012 14:41:17 -0400 Subject: ARM: LPAE: use phys_addr_t in alloc_init_pud() This patch fixes the alloc_init_pud() function to use phys_addr_t instead of unsigned long when passing in the phys argument. This is an extension to commit 97092e0c56830457af0639f6bd904537a150ea4a (ARM: pgtable: use phys_addr_t for physical addresses), which applied similar changes elsewhere in the ARM memory management code. Signed-off-by: Vitaly Andrianov Signed-off-by: Cyril Chemparathy Acked-by: Nicolas Pitre Acked-by: Catalin Marinas Tested-by: Santosh Shilimkar Tested-by: Subash Patel Signed-off-by: Will Deacon --- arch/arm/mm/mmu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c index e0d8565671a6..b7ce65a82371 100644 --- a/arch/arm/mm/mmu.c +++ b/arch/arm/mm/mmu.c @@ -673,7 +673,8 @@ static void __init alloc_init_pmd(pud_t *pud, unsigned long addr, } static void __init alloc_init_pud(pgd_t *pgd, unsigned long addr, - unsigned long end, unsigned long phys, const struct mem_type *type) + unsigned long end, phys_addr_t phys, + const struct mem_type *type) { pud_t *pud = pud_offset(pgd, addr); unsigned long next; -- cgit v1.2.3 From 56bc628666b39dc8cb395c7686d8c032efd731f4 Mon Sep 17 00:00:00 2001 From: Vitaly Andrianov Date: Thu, 21 Jun 2012 08:09:05 -0400 Subject: ARM: LPAE: use phys_addr_t in free_memmap() The free_memmap() was mistakenly using unsigned long type to represent physical addresses. This breaks on PAE systems where memory could be placed above the 32-bit addressible limit. This patch fixes this function to properly use phys_addr_t instead. Signed-off-by: Vitaly Andrianov Signed-off-by: Cyril Chemparathy Acked-by: Nicolas Pitre Tested-by: Santosh Shilimkar Tested-by: Subash Patel Signed-off-by: Will Deacon --- arch/arm/mm/init.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 9a5cdc01fcdf..68c914e8544e 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -442,7 +442,7 @@ static inline void free_memmap(unsigned long start_pfn, unsigned long end_pfn) { struct page *start_pg, *end_pg; - unsigned long pg, pgend; + phys_addr_t pg, pgend; /* * Convert start_pfn/end_pfn to a struct page pointer. @@ -454,8 +454,8 @@ free_memmap(unsigned long start_pfn, unsigned long end_pfn) * Convert to physical addresses, and * round start upwards and end downwards. */ - pg = (unsigned long)PAGE_ALIGN(__pa(start_pg)); - pgend = (unsigned long)__pa(end_pg) & PAGE_MASK; + pg = PAGE_ALIGN(__pa(start_pg)); + pgend = __pa(end_pg) & PAGE_MASK; /* * If there are free pages between these, -- cgit v1.2.3 From de22cc6e33449d8d6fb339619e32138ea4fcc2a4 Mon Sep 17 00:00:00 2001 From: Vitaly Andrianov Date: Fri, 22 Jun 2012 14:26:04 -0400 Subject: ARM: LPAE: use phys_addr_t for initrd location This patch fixes the initrd setup code to use phys_addr_t instead of assuming 32-bit addressing. Without this we cannot boot on systems where initrd is located above the 4G physical address limit. Signed-off-by: Vitaly Andrianov Signed-off-by: Cyril Chemparathy Acked-by: Nicolas Pitre Acked-by: Catalin Marinas Tested-by: Santosh Shilimkar Tested-by: Subash Patel Signed-off-by: Will Deacon --- arch/arm/mm/init.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 68c914e8544e..2ffee02d1d5c 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -36,12 +36,13 @@ #include "mm.h" -static unsigned long phys_initrd_start __initdata = 0; +static phys_addr_t phys_initrd_start __initdata = 0; static unsigned long phys_initrd_size __initdata = 0; static int __init early_initrd(char *p) { - unsigned long start, size; + phys_addr_t start; + unsigned long size; char *endp; start = memparse(p, &endp); @@ -350,14 +351,14 @@ void __init arm_memblock_init(struct meminfo *mi, struct machine_desc *mdesc) #ifdef CONFIG_BLK_DEV_INITRD if (phys_initrd_size && !memblock_is_region_memory(phys_initrd_start, phys_initrd_size)) { - pr_err("INITRD: 0x%08lx+0x%08lx is not a memory region - disabling initrd\n", - phys_initrd_start, phys_initrd_size); + pr_err("INITRD: 0x%08llx+0x%08lx is not a memory region - disabling initrd\n", + (u64)phys_initrd_start, phys_initrd_size); phys_initrd_start = phys_initrd_size = 0; } if (phys_initrd_size && memblock_is_region_reserved(phys_initrd_start, phys_initrd_size)) { - pr_err("INITRD: 0x%08lx+0x%08lx overlaps in-use memory region - disabling initrd\n", - phys_initrd_start, phys_initrd_size); + pr_err("INITRD: 0x%08llx+0x%08lx overlaps in-use memory region - disabling initrd\n", + (u64)phys_initrd_start, phys_initrd_size); phys_initrd_start = phys_initrd_size = 0; } if (phys_initrd_size) { -- cgit v1.2.3 From 13f659b0f363114282679d06094337c5efa12fa8 Mon Sep 17 00:00:00 2001 From: Cyril Chemparathy Date: Mon, 16 Jul 2012 15:37:06 -0400 Subject: ARM: LPAE: use phys_addr_t in switch_mm() This patch modifies the switch_mm() processor functions to use phys_addr_t. On LPAE systems, we now honor the upper 32-bits of the physical address that is being passed in, and program these into TTBR as expected. Signed-off-by: Cyril Chemparathy Signed-off-by: Vitaly Andrianov Reviewed-by: Nicolas Pitre Tested-by: Santosh Shilimkar Tested-by: Subash Patel [will: fixed up conflict in 3-level switch_mm with big-endian changes] Signed-off-by: Will Deacon --- arch/arm/include/asm/proc-fns.h | 4 ++-- arch/arm/mm/proc-v7-3level.S | 16 ++++++++++++---- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/arch/arm/include/asm/proc-fns.h b/arch/arm/include/asm/proc-fns.h index f3628fb3d2b3..75b5f14617c3 100644 --- a/arch/arm/include/asm/proc-fns.h +++ b/arch/arm/include/asm/proc-fns.h @@ -60,7 +60,7 @@ extern struct processor { /* * Set the page table */ - void (*switch_mm)(unsigned long pgd_phys, struct mm_struct *mm); + void (*switch_mm)(phys_addr_t pgd_phys, struct mm_struct *mm); /* * Set a possibly extended PTE. Non-extended PTEs should * ignore 'ext'. @@ -82,7 +82,7 @@ extern void cpu_proc_init(void); extern void cpu_proc_fin(void); extern int cpu_do_idle(void); extern void cpu_dcache_clean_area(void *, int); -extern void cpu_do_switch_mm(unsigned long pgd_phys, struct mm_struct *mm); +extern void cpu_do_switch_mm(phys_addr_t pgd_phys, struct mm_struct *mm); #ifdef CONFIG_ARM_LPAE extern void cpu_set_pte_ext(pte_t *ptep, pte_t pte); #else diff --git a/arch/arm/mm/proc-v7-3level.S b/arch/arm/mm/proc-v7-3level.S index 363027e811d6..995857d3b530 100644 --- a/arch/arm/mm/proc-v7-3level.S +++ b/arch/arm/mm/proc-v7-3level.S @@ -39,6 +39,14 @@ #define TTB_FLAGS_SMP (TTB_IRGN_WBWA|TTB_S|TTB_RGN_OC_WBWA) #define PMD_FLAGS_SMP (PMD_SECT_WBWA|PMD_SECT_S) +#ifndef __ARMEB__ +# define rpgdl r0 +# define rpgdh r1 +#else +# define rpgdl r1 +# define rpgdh r0 +#endif + /* * cpu_v7_switch_mm(pgd_phys, tsk) * @@ -47,10 +55,10 @@ */ ENTRY(cpu_v7_switch_mm) #ifdef CONFIG_MMU - mmid r1, r1 @ get mm->context.id - asid r3, r1 - mov r3, r3, lsl #(48 - 32) @ ASID - mcrr p15, 0, r0, r3, c2 @ set TTB 0 + mmid r2, r2 + asid r2, r2 + orr rpgdh, rpgdh, r2, lsl #(48 - 32) @ upper 32-bits of pgd + mcrr p15, 0, rpgdl, rpgdh, c2 @ set TTB 0 isb #endif mov pc, lr -- cgit v1.2.3 From 1fc84ae84b5153e32a4b6ace507f9663e10b0cb2 Mon Sep 17 00:00:00 2001 From: Cyril Chemparathy Date: Mon, 16 Jul 2012 17:20:17 -0400 Subject: ARM: LPAE: use 64-bit accessors for TTBR registers This patch adds TTBR accessor macros, and modifies cpu_get_pgd() and the LPAE version of cpu_set_reserved_ttbr0() to use these instead. In the process, we also fix these functions to correctly handle cases where the physical address lies beyond the 4G limit of 32-bit addressing. Signed-off-by: Cyril Chemparathy Signed-off-by: Vitaly Andrianov Acked-by: Nicolas Pitre Reviewed-by: Catalin Marinas Tested-by: Santosh Shilimkar Tested-by: Subash Patel Signed-off-by: Will Deacon --- arch/arm/include/asm/proc-fns.h | 22 +++++++++++++++++----- arch/arm/mm/context.c | 9 ++------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/arch/arm/include/asm/proc-fns.h b/arch/arm/include/asm/proc-fns.h index 75b5f14617c3..1c3cf9407a31 100644 --- a/arch/arm/include/asm/proc-fns.h +++ b/arch/arm/include/asm/proc-fns.h @@ -116,13 +116,25 @@ extern void cpu_resume(void); #define cpu_switch_mm(pgd,mm) cpu_do_switch_mm(virt_to_phys(pgd),mm) #ifdef CONFIG_ARM_LPAE + +#define cpu_get_ttbr(nr) \ + ({ \ + u64 ttbr; \ + __asm__("mrrc p15, " #nr ", %Q0, %R0, c2" \ + : "=r" (ttbr)); \ + ttbr; \ + }) + +#define cpu_set_ttbr(nr, val) \ + do { \ + u64 ttbr = val; \ + __asm__("mcrr p15, " #nr ", %Q0, %R0, c2" \ + : : "r" (ttbr)); \ + } while (0) + #define cpu_get_pgd() \ ({ \ - unsigned long pg, pg2; \ - __asm__("mrrc p15, 0, %0, %1, c2" \ - : "=r" (pg), "=r" (pg2) \ - : \ - : "cc"); \ + u64 pg = cpu_get_ttbr(0); \ pg &= ~(PTRS_PER_PGD*sizeof(pgd_t)-1); \ (pgd_t *)phys_to_virt(pg); \ }) diff --git a/arch/arm/mm/context.c b/arch/arm/mm/context.c index 2ac37372ef52..3675e31473e3 100644 --- a/arch/arm/mm/context.c +++ b/arch/arm/mm/context.c @@ -20,6 +20,7 @@ #include #include #include +#include /* * On ARMv6, we have the following structure in the Context ID: @@ -55,17 +56,11 @@ static cpumask_t tlb_flush_pending; #ifdef CONFIG_ARM_LPAE static void cpu_set_reserved_ttbr0(void) { - unsigned long ttbl = __pa(swapper_pg_dir); - unsigned long ttbh = 0; - /* * Set TTBR0 to swapper_pg_dir which contains only global entries. The * ASID is set to 0. */ - asm volatile( - " mcrr p15, 0, %0, %1, c2 @ set TTBR0\n" - : - : "r" (ttbl), "r" (ttbh)); + cpu_set_ttbr(0, __pa(swapper_pg_dir)); isb(); } #else -- cgit v1.2.3 From a7fbc0d62a4d46e642af889e7288fede5078bc46 Mon Sep 17 00:00:00 2001 From: Cyril Chemparathy Date: Sat, 21 Jul 2012 19:47:52 -0400 Subject: ARM: LPAE: factor out T1SZ and TTBR1 computations This patch moves the TTBR1 offset calculation and the T1SZ calculation out of the TTB setup assembly code. This should not affect functionality in any way, but improves code readability as well as readability of subsequent patches in this series. Signed-off-by: Cyril Chemparathy Signed-off-by: Vitaly Andrianov Acked-by: Nicolas Pitre Tested-by: Santosh Shilimkar Tested-by: Subash Patel Signed-off-by: Will Deacon --- arch/arm/include/asm/pgtable-3level-hwdef.h | 20 ++++++++++++++++++++ arch/arm/mm/proc-v7-3level.S | 29 ++++++++--------------------- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/arch/arm/include/asm/pgtable-3level-hwdef.h b/arch/arm/include/asm/pgtable-3level-hwdef.h index 18f5cef82ad5..c6c6e6df22f3 100644 --- a/arch/arm/include/asm/pgtable-3level-hwdef.h +++ b/arch/arm/include/asm/pgtable-3level-hwdef.h @@ -79,4 +79,24 @@ #define PHYS_MASK_SHIFT (40) #define PHYS_MASK ((1ULL << PHYS_MASK_SHIFT) - 1) +/* + * TTBR0/TTBR1 split (PAGE_OFFSET): + * 0x40000000: T0SZ = 2, T1SZ = 0 (not used) + * 0x80000000: T0SZ = 0, T1SZ = 1 + * 0xc0000000: T0SZ = 0, T1SZ = 2 + * + * Only use this feature if PHYS_OFFSET <= PAGE_OFFSET, otherwise + * booting secondary CPUs would end up using TTBR1 for the identity + * mapping set up in TTBR0. + */ +#if defined CONFIG_VMSPLIT_2G +#define TTBR1_OFFSET 16 /* skip two L1 entries */ +#elif defined CONFIG_VMSPLIT_3G +#define TTBR1_OFFSET (4096 * (1 + 3)) /* only L2, skip pgd + 3*pmd */ +#else +#define TTBR1_OFFSET 0 +#endif + +#define TTBR1_SIZE (((PAGE_OFFSET >> 30) - 1) << 16) + #endif diff --git a/arch/arm/mm/proc-v7-3level.S b/arch/arm/mm/proc-v7-3level.S index 995857d3b530..58ab7477bb61 100644 --- a/arch/arm/mm/proc-v7-3level.S +++ b/arch/arm/mm/proc-v7-3level.S @@ -114,7 +114,7 @@ ENDPROC(cpu_v7_set_pte_ext) */ .macro v7_ttb_setup, zero, ttbr0, ttbr1, tmp ldr \tmp, =swapper_pg_dir @ swapper_pg_dir virtual address - cmp \ttbr1, \tmp @ PHYS_OFFSET > PAGE_OFFSET? (branch below) + cmp \ttbr1, \tmp @ PHYS_OFFSET > PAGE_OFFSET? mrc p15, 0, \tmp, c2, c0, 2 @ TTB control register orr \tmp, \tmp, #TTB_EAE ALT_SMP(orr \tmp, \tmp, #TTB_FLAGS_SMP) @@ -122,27 +122,14 @@ ENDPROC(cpu_v7_set_pte_ext) ALT_SMP(orr \tmp, \tmp, #TTB_FLAGS_SMP << 16) ALT_UP(orr \tmp, \tmp, #TTB_FLAGS_UP << 16) /* - * TTBR0/TTBR1 split (PAGE_OFFSET): - * 0x40000000: T0SZ = 2, T1SZ = 0 (not used) - * 0x80000000: T0SZ = 0, T1SZ = 1 - * 0xc0000000: T0SZ = 0, T1SZ = 2 - * - * Only use this feature if PHYS_OFFSET <= PAGE_OFFSET, otherwise - * booting secondary CPUs would end up using TTBR1 for the identity - * mapping set up in TTBR0. + * Only use split TTBRs if PHYS_OFFSET <= PAGE_OFFSET (cmp above), + * otherwise booting secondary CPUs would end up using TTBR1 for the + * identity mapping set up in TTBR0. */ - bhi 9001f @ PHYS_OFFSET > PAGE_OFFSET? - orr \tmp, \tmp, #(((PAGE_OFFSET >> 30) - 1) << 16) @ TTBCR.T1SZ -#if defined CONFIG_VMSPLIT_2G - /* PAGE_OFFSET == 0x80000000, T1SZ == 1 */ - add \ttbr1, \ttbr1, #1 << 4 @ skip two L1 entries -#elif defined CONFIG_VMSPLIT_3G - /* PAGE_OFFSET == 0xc0000000, T1SZ == 2 */ - add \ttbr1, \ttbr1, #4096 * (1 + 3) @ only L2 used, skip pgd+3*pmd -#endif - /* CONFIG_VMSPLIT_1G does not need TTBR1 adjustment */ -9001: mcr p15, 0, \tmp, c2, c0, 2 @ TTB control register - mcrr p15, 1, \ttbr1, \zero, c2 @ load TTBR1 + orrls \tmp, \tmp, #TTBR1_SIZE @ TTBCR.T1SZ + mcr p15, 0, \tmp, c2, c0, 2 @ TTBCR + addls \ttbr1, \ttbr1, #TTBR1_OFFSET + mcrr p15, 1, \ttbr1, \zero, c2 @ load TTBR1 .endm __CPUINIT -- cgit v1.2.3 From 4756dcbfd37819a8359d3c69a22be2ee41666d0f Mon Sep 17 00:00:00 2001 From: Cyril Chemparathy Date: Sat, 21 Jul 2012 15:55:04 -0400 Subject: ARM: LPAE: accomodate >32-bit addresses for page table base This patch redefines the early boot time use of the R4 register to steal a few low order bits (ARCH_PGD_SHIFT bits) on LPAE systems. This allows for up to 38-bit physical addresses. Signed-off-by: Cyril Chemparathy Signed-off-by: Vitaly Andrianov Acked-by: Nicolas Pitre Tested-by: Santosh Shilimkar Tested-by: Subash Patel Signed-off-by: Will Deacon --- arch/arm/include/asm/memory.h | 16 ++++++++++++++++ arch/arm/kernel/head.S | 10 ++++------ arch/arm/kernel/smp.c | 11 +++++++++-- arch/arm/mm/proc-v7-3level.S | 8 ++++++++ 4 files changed, 37 insertions(+), 8 deletions(-) diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h index 57870ab313c5..e506088a7678 100644 --- a/arch/arm/include/asm/memory.h +++ b/arch/arm/include/asm/memory.h @@ -18,6 +18,8 @@ #include #include +#include + #ifdef CONFIG_NEED_MACH_MEMORY_H #include #endif @@ -141,6 +143,20 @@ #define page_to_phys(page) (__pfn_to_phys(page_to_pfn(page))) #define phys_to_page(phys) (pfn_to_page(__phys_to_pfn(phys))) +/* + * Minimum guaranted alignment in pgd_alloc(). The page table pointers passed + * around in head.S and proc-*.S are shifted by this amount, in order to + * leave spare high bits for systems with physical address extension. This + * does not fully accomodate the 40-bit addressing capability of ARM LPAE, but + * gives us about 38-bits or so. + */ +#ifdef CONFIG_ARM_LPAE +#define ARCH_PGD_SHIFT L1_CACHE_SHIFT +#else +#define ARCH_PGD_SHIFT 0 +#endif +#define ARCH_PGD_MASK ((1 << ARCH_PGD_SHIFT) - 1) + #ifndef __ASSEMBLY__ /* diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S index 8bac553fe213..45e8935cae4e 100644 --- a/arch/arm/kernel/head.S +++ b/arch/arm/kernel/head.S @@ -156,7 +156,7 @@ ENDPROC(stext) * * Returns: * r0, r3, r5-r7 corrupted - * r4 = physical page table address + * r4 = page table (see ARCH_PGD_SHIFT in asm/memory.h) */ __create_page_tables: pgtbl r4, r8 @ page table address @@ -331,6 +331,7 @@ __create_page_tables: #endif #ifdef CONFIG_ARM_LPAE sub r4, r4, #0x1000 @ point to the PGD table + mov r4, r4, lsr #ARCH_PGD_SHIFT #endif mov pc, lr ENDPROC(__create_page_tables) @@ -408,7 +409,7 @@ __secondary_data: * r0 = cp#15 control register * r1 = machine ID * r2 = atags or dtb pointer - * r4 = page table pointer + * r4 = page table (see ARCH_PGD_SHIFT in asm/memory.h) * r9 = processor ID * r13 = *virtual* address to jump to upon completion */ @@ -427,10 +428,7 @@ __enable_mmu: #ifdef CONFIG_CPU_ICACHE_DISABLE bic r0, r0, #CR_I #endif -#ifdef CONFIG_ARM_LPAE - mov r5, #0 - mcrr p15, 0, r4, r5, c2 @ load TTBR0 -#else +#ifndef CONFIG_ARM_LPAE mov r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \ domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \ domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | \ diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index 550d63cef68e..217b755aadd4 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -78,6 +78,13 @@ void __init smp_set_ops(struct smp_operations *ops) smp_ops = *ops; }; +static unsigned long get_arch_pgd(pgd_t *pgd) +{ + phys_addr_t pgdir = virt_to_phys(pgd); + BUG_ON(pgdir & ARCH_PGD_MASK); + return pgdir >> ARCH_PGD_SHIFT; +} + int __cpuinit __cpu_up(unsigned int cpu, struct task_struct *idle) { int ret; @@ -87,8 +94,8 @@ int __cpuinit __cpu_up(unsigned int cpu, struct task_struct *idle) * its stack and the page tables. */ secondary_data.stack = task_stack_page(idle) + THREAD_START_SP; - secondary_data.pgdir = virt_to_phys(idmap_pgd); - secondary_data.swapper_pg_dir = virt_to_phys(swapper_pg_dir); + secondary_data.pgdir = get_arch_pgd(idmap_pgd); + secondary_data.swapper_pg_dir = get_arch_pgd(swapper_pg_dir); __cpuc_flush_dcache_area(&secondary_data, sizeof(secondary_data)); outer_clean_range(__pa(&secondary_data), __pa(&secondary_data + 1)); diff --git a/arch/arm/mm/proc-v7-3level.S b/arch/arm/mm/proc-v7-3level.S index 58ab7477bb61..5ffe1956c6d9 100644 --- a/arch/arm/mm/proc-v7-3level.S +++ b/arch/arm/mm/proc-v7-3level.S @@ -114,6 +114,7 @@ ENDPROC(cpu_v7_set_pte_ext) */ .macro v7_ttb_setup, zero, ttbr0, ttbr1, tmp ldr \tmp, =swapper_pg_dir @ swapper_pg_dir virtual address + mov \tmp, \tmp, lsr #ARCH_PGD_SHIFT cmp \ttbr1, \tmp @ PHYS_OFFSET > PAGE_OFFSET? mrc p15, 0, \tmp, c2, c0, 2 @ TTB control register orr \tmp, \tmp, #TTB_EAE @@ -128,8 +129,15 @@ ENDPROC(cpu_v7_set_pte_ext) */ orrls \tmp, \tmp, #TTBR1_SIZE @ TTBCR.T1SZ mcr p15, 0, \tmp, c2, c0, 2 @ TTBCR + mov \tmp, \ttbr1, lsr #(32 - ARCH_PGD_SHIFT) @ upper bits + mov \ttbr1, \ttbr1, lsl #ARCH_PGD_SHIFT @ lower bits addls \ttbr1, \ttbr1, #TTBR1_OFFSET mcrr p15, 1, \ttbr1, \zero, c2 @ load TTBR1 + mov \tmp, \ttbr0, lsr #(32 - ARCH_PGD_SHIFT) @ upper bits + mov \ttbr0, \ttbr0, lsl #ARCH_PGD_SHIFT @ lower bits + mcrr p15, 0, \ttbr0, \zero, c2 @ load TTBR0 + mcrr p15, 1, \ttbr1, \zero, c2 @ load TTBR1 + mcrr p15, 0, \ttbr0, \zero, c2 @ load TTBR0 .endm __CPUINIT -- cgit v1.2.3 From 82f667046ec895552caf2f7a4c6841c530bfc215 Mon Sep 17 00:00:00 2001 From: Cyril Chemparathy Date: Fri, 20 Jul 2012 12:01:23 -0400 Subject: ARM: mm: use physical addresses in highmem sanity checks This patch modifies the highmem sanity checking code to use physical addresses instead. This change eliminates the wrap-around problems associated with the original virtual address based checks, and this simplifies the code a bit. The one constraint imposed here is that low physical memory must be mapped in a monotonically increasing fashion if there are multiple banks of memory, i.e., x < y must => pa(x) < pa(y). Signed-off-by: Cyril Chemparathy Signed-off-by: Vitaly Andrianov Acked-by: Nicolas Pitre Tested-by: Santosh Shilimkar Tested-by: Subash Patel Signed-off-by: Will Deacon --- arch/arm/mm/mmu.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c index b7ce65a82371..fc6ff1a9db50 100644 --- a/arch/arm/mm/mmu.c +++ b/arch/arm/mm/mmu.c @@ -988,6 +988,7 @@ phys_addr_t arm_lowmem_limit __initdata = 0; void __init sanity_check_meminfo(void) { int i, j, highmem = 0; + phys_addr_t vmalloc_limit = __pa(vmalloc_min - 1) + 1; for (i = 0, j = 0; i < meminfo.nr_banks; i++) { struct membank *bank = &meminfo.bank[j]; @@ -997,8 +998,7 @@ void __init sanity_check_meminfo(void) highmem = 1; #ifdef CONFIG_HIGHMEM - if (__va(bank->start) >= vmalloc_min || - __va(bank->start) < (void *)PAGE_OFFSET) + if (bank->start >= vmalloc_limit) highmem = 1; bank->highmem = highmem; @@ -1007,8 +1007,8 @@ void __init sanity_check_meminfo(void) * Split those memory banks which are partially overlapping * the vmalloc area greatly simplifying things later. */ - if (!highmem && __va(bank->start) < vmalloc_min && - bank->size > vmalloc_min - __va(bank->start)) { + if (!highmem && bank->start < vmalloc_limit && + bank->size > vmalloc_limit - bank->start) { if (meminfo.nr_banks >= NR_BANKS) { printk(KERN_CRIT "NR_BANKS too low, " "ignoring high memory\n"); @@ -1017,12 +1017,12 @@ void __init sanity_check_meminfo(void) (meminfo.nr_banks - i) * sizeof(*bank)); meminfo.nr_banks++; i++; - bank[1].size -= vmalloc_min - __va(bank->start); - bank[1].start = __pa(vmalloc_min - 1) + 1; + bank[1].size -= vmalloc_limit - bank->start; + bank[1].start = vmalloc_limit; bank[1].highmem = highmem = 1; j++; } - bank->size = vmalloc_min - __va(bank->start); + bank->size = vmalloc_limit - bank->start; } #else bank->highmem = highmem; @@ -1042,8 +1042,7 @@ void __init sanity_check_meminfo(void) * Check whether this memory bank would entirely overlap * the vmalloc area. */ - if (__va(bank->start) >= vmalloc_min || - __va(bank->start) < (void *)PAGE_OFFSET) { + if (bank->start >= vmalloc_limit) { printk(KERN_NOTICE "Ignoring RAM at %.8llx-%.8llx " "(vmalloc region overlap).\n", (unsigned long long)bank->start, @@ -1055,9 +1054,8 @@ void __init sanity_check_meminfo(void) * Check whether this memory bank would partially overlap * the vmalloc area. */ - if (__va(bank->start + bank->size - 1) >= vmalloc_min || - __va(bank->start + bank->size - 1) <= __va(bank->start)) { - unsigned long newsize = vmalloc_min - __va(bank->start); + if (bank->start + bank->size > vmalloc_limit) + unsigned long newsize = vmalloc_limit - bank->start; printk(KERN_NOTICE "Truncating RAM at %.8llx-%.8llx " "to -%.8llx (vmalloc region overlap).\n", (unsigned long long)bank->start, -- cgit v1.2.3 From 5b20c5b2f014ecc0a6310988af69cd7ede9e7c67 Mon Sep 17 00:00:00 2001 From: Cyril Chemparathy Date: Wed, 12 Sep 2012 10:19:05 -0400 Subject: ARM: fix type of PHYS_PFN_OFFSET to unsigned long On LPAE machines, PHYS_OFFSET evaluates to a phys_addr_t and this type is inherited by the PHYS_PFN_OFFSET definition as well. Consequently, the kernel build emits warnings of the form: init/main.c: In function 'start_kernel': init/main.c:588:7: warning: format '%lx' expects argument of type 'long unsigned int', but argument 2 has type 'phys_addr_t' [-Wformat] This patch fixes this warning by pinning down the PFN type to unsigned long. Signed-off-by: Cyril Chemparathy Acked-by: Nicolas Pitre Tested-by: Santosh Shilimkar Tested-by: Subash Patel Signed-off-by: Will Deacon --- arch/arm/include/asm/memory.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h index e506088a7678..584786f740f9 100644 --- a/arch/arm/include/asm/memory.h +++ b/arch/arm/include/asm/memory.h @@ -223,7 +223,7 @@ static inline unsigned long __phys_to_virt(unsigned long x) * direct-mapped view. We assume this is the first page * of RAM in the mem_map as well. */ -#define PHYS_PFN_OFFSET (PHYS_OFFSET >> PAGE_SHIFT) +#define PHYS_PFN_OFFSET ((unsigned long)(PHYS_OFFSET >> PAGE_SHIFT)) /* * These are *only* valid on the kernel direct mapped RAM memory. -- cgit v1.2.3 From adf2e9fda34c1cfff2ee4e47078b1e142adb2c30 Mon Sep 17 00:00:00 2001 From: Cyril Chemparathy Date: Fri, 20 Jul 2012 12:24:45 -0400 Subject: ARM: mm: cleanup checks for membank overlap with vmalloc area On Keystone platforms, physical memory is entirely outside the 32-bit addressible range. Therefore, the (bank->start > ULONG_MAX) check below marks the entire system memory as highmem, and this causes unpleasentness all over. This patch eliminates the extra bank start check (against ULONG_MAX) by checking bank->start against the physical address corresponding to vmalloc_min instead. In the process, this patch also cleans up parts of the highmem sanity check code by removing what has now become a redundant check for banks that entirely overlap with the vmalloc range. Signed-off-by: Cyril Chemparathy Signed-off-by: Vitaly Andrianov Acked-by: Nicolas Pitre Tested-by: Santosh Shilimkar Tested-by: Subash Patel Signed-off-by: Will Deacon --- arch/arm/mm/mmu.c | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c index fc6ff1a9db50..ae249d1ab1d3 100644 --- a/arch/arm/mm/mmu.c +++ b/arch/arm/mm/mmu.c @@ -994,15 +994,12 @@ void __init sanity_check_meminfo(void) struct membank *bank = &meminfo.bank[j]; *bank = meminfo.bank[i]; - if (bank->start > ULONG_MAX) - highmem = 1; - -#ifdef CONFIG_HIGHMEM if (bank->start >= vmalloc_limit) highmem = 1; bank->highmem = highmem; +#ifdef CONFIG_HIGHMEM /* * Split those memory banks which are partially overlapping * the vmalloc area greatly simplifying things later. @@ -1025,8 +1022,6 @@ void __init sanity_check_meminfo(void) bank->size = vmalloc_limit - bank->start; } #else - bank->highmem = highmem; - /* * Highmem banks not allowed with !CONFIG_HIGHMEM. */ @@ -1038,18 +1033,6 @@ void __init sanity_check_meminfo(void) continue; } - /* - * Check whether this memory bank would entirely overlap - * the vmalloc area. - */ - if (bank->start >= vmalloc_limit) { - printk(KERN_NOTICE "Ignoring RAM at %.8llx-%.8llx " - "(vmalloc region overlap).\n", - (unsigned long long)bank->start, - (unsigned long long)bank->start + bank->size - 1); - continue; - } - /* * Check whether this memory bank would partially overlap * the vmalloc area. -- cgit v1.2.3 From 28d4bf7a2929c5e525171d249e12662e21130ec3 Mon Sep 17 00:00:00 2001 From: Cyril Chemparathy Date: Fri, 20 Jul 2012 13:16:41 -0400 Subject: ARM: mm: clean up membank size limit checks This patch cleans up the highmem sanity check code by simplifying the range checks with a pre-calculated size_limit. This patch should otherwise have no functional impact on behavior. This patch also removes a redundant (bank->start < vmalloc_limit) check, since this is already covered by the !highmem condition. Signed-off-by: Cyril Chemparathy Signed-off-by: Vitaly Andrianov Acked-by: Nicolas Pitre Tested-by: Santosh Shilimkar Tested-by: Subash Patel Signed-off-by: Will Deacon --- arch/arm/mm/mmu.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c index ae249d1ab1d3..280f91d02de2 100644 --- a/arch/arm/mm/mmu.c +++ b/arch/arm/mm/mmu.c @@ -992,10 +992,15 @@ void __init sanity_check_meminfo(void) for (i = 0, j = 0; i < meminfo.nr_banks; i++) { struct membank *bank = &meminfo.bank[j]; + phys_addr_t size_limit; + *bank = meminfo.bank[i]; + size_limit = bank->size; if (bank->start >= vmalloc_limit) highmem = 1; + else + size_limit = vmalloc_limit - bank->start; bank->highmem = highmem; @@ -1004,8 +1009,7 @@ void __init sanity_check_meminfo(void) * Split those memory banks which are partially overlapping * the vmalloc area greatly simplifying things later. */ - if (!highmem && bank->start < vmalloc_limit && - bank->size > vmalloc_limit - bank->start) { + if (!highmem && bank->size > size_limit) { if (meminfo.nr_banks >= NR_BANKS) { printk(KERN_CRIT "NR_BANKS too low, " "ignoring high memory\n"); @@ -1014,12 +1018,12 @@ void __init sanity_check_meminfo(void) (meminfo.nr_banks - i) * sizeof(*bank)); meminfo.nr_banks++; i++; - bank[1].size -= vmalloc_limit - bank->start; + bank[1].size -= size_limit; bank[1].start = vmalloc_limit; bank[1].highmem = highmem = 1; j++; } - bank->size = vmalloc_limit - bank->start; + bank->size = size_limit; } #else /* @@ -1037,14 +1041,13 @@ void __init sanity_check_meminfo(void) * Check whether this memory bank would partially overlap * the vmalloc area. */ - if (bank->start + bank->size > vmalloc_limit) - unsigned long newsize = vmalloc_limit - bank->start; + if (bank->size > size_limit) { printk(KERN_NOTICE "Truncating RAM at %.8llx-%.8llx " "to -%.8llx (vmalloc region overlap).\n", (unsigned long long)bank->start, (unsigned long long)bank->start + bank->size - 1, - (unsigned long long)bank->start + newsize - 1); - bank->size = newsize; + (unsigned long long)bank->start + size_limit - 1); + bank->size = size_limit; } #endif if (!bank->highmem && bank->start + bank->size > arm_lowmem_limit) -- cgit v1.2.3 From e38a517578d6c0f764b0d0f6e26dcdf9f70c69d7 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Thu, 2 May 2013 13:52:01 +0100 Subject: ARM: lpae: fix definition of PTE_HWTABLE_PTRS For 2-level page tables, PTE_HWTABLE_PTRS describes the offset between Linux PTEs and hardware PTEs. On LPAE, there is no distinction (since we have 64-bit descriptors with plenty of space) so PTE_HWTABLE_PTRS should be 0. Unfortunately, it is wrongly defined as PTRS_PER_PTE, meaning that current pte table flushing is off by a page. Luckily, all current LPAE implementations are SMP, so the hardware walker can snoop L1. This patch fixes the broken definition. Acked-by: Catalin Marinas Signed-off-by: Will Deacon --- arch/arm/include/asm/pgtable-3level.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/include/asm/pgtable-3level.h b/arch/arm/include/asm/pgtable-3level.h index 5b85b218b0f0..d03c589cf6b5 100644 --- a/arch/arm/include/asm/pgtable-3level.h +++ b/arch/arm/include/asm/pgtable-3level.h @@ -33,7 +33,7 @@ #define PTRS_PER_PMD 512 #define PTRS_PER_PGD 4 -#define PTE_HWTABLE_PTRS (PTRS_PER_PTE) +#define PTE_HWTABLE_PTRS (0) #define PTE_HWTABLE_OFF (0) #define PTE_HWTABLE_SIZE (PTRS_PER_PTE * sizeof(u64)) -- cgit v1.2.3 From a469abd0f868c902b75532579bf87553dcf1b360 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Mon, 8 Apr 2013 17:13:12 +0100 Subject: ARM: elf: add new hwcap for identifying atomic ldrd/strd instructions CPUs implementing LPAE have atomic ldrd/strd instructions, meaning that userspace software can avoid having to use the exclusive variants of these instructions if they wish. This patch advertises the atomicity of these instructions via the hwcaps, so userspace can detect this CPU feature. Reported-by: Vladimir Danushevsky Signed-off-by: Will Deacon --- arch/arm/include/uapi/asm/hwcap.h | 2 +- arch/arm/kernel/setup.c | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/arch/arm/include/uapi/asm/hwcap.h b/arch/arm/include/uapi/asm/hwcap.h index 3688fd15a32d..6d34d080372a 100644 --- a/arch/arm/include/uapi/asm/hwcap.h +++ b/arch/arm/include/uapi/asm/hwcap.h @@ -25,6 +25,6 @@ #define HWCAP_IDIVT (1 << 18) #define HWCAP_VFPD32 (1 << 19) /* set if VFP has 32 regs (not 16) */ #define HWCAP_IDIV (HWCAP_IDIVA | HWCAP_IDIVT) - +#define HWCAP_LPAE (1 << 20) #endif /* _UAPI__ASMARM_HWCAP_H */ diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index 1522c7ae31b0..bdcd4dd13230 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -355,7 +355,7 @@ void __init early_print(const char *str, ...) static void __init cpuid_init_hwcaps(void) { - unsigned int divide_instrs; + unsigned int divide_instrs, vmsa; if (cpu_architecture() < CPU_ARCH_ARMv7) return; @@ -368,6 +368,11 @@ static void __init cpuid_init_hwcaps(void) case 1: elf_hwcap |= HWCAP_IDIVT; } + + /* LPAE implies atomic ldrd/strd instructions */ + vmsa = (read_cpuid_ext(CPUID_EXT_MMFR0) & 0xf) >> 0; + if (vmsa >= 5) + elf_hwcap |= HWCAP_LPAE; } static void __init feat_v6_fixup(void) @@ -872,6 +877,7 @@ static const char *hwcap_str[] = { "vfpv4", "idiva", "idivt", + "lpae", NULL }; -- cgit v1.2.3 From aa3e22473cb89eeb25c1bca8141ef00a13299e61 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 6 May 2013 12:27:24 +0900 Subject: gpio: langwell: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Linus Walleij --- drivers/gpio/gpio-langwell.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpio/gpio-langwell.c b/drivers/gpio/gpio-langwell.c index 62ef10a641c4..b8a5729ca4f6 100644 --- a/drivers/gpio/gpio-langwell.c +++ b/drivers/gpio/gpio-langwell.c @@ -481,7 +481,6 @@ static int wp_gpio_remove(struct platform_device *pdev) dev_err(&pdev->dev, "failed to remove gpio_chip.\n"); iounmap(lnw->reg_base); kfree(lnw); - platform_set_drvdata(pdev, NULL); return 0; } -- cgit v1.2.3 From 07606bfc73248fd9ef0bf5ac3c0b12098fb19b25 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 6 May 2013 12:28:37 +0900 Subject: gpio: lynxpoint: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Acked-by: Mathias Nyman Signed-off-by: Linus Walleij --- drivers/gpio/gpio-lynxpoint.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpio/gpio-lynxpoint.c b/drivers/gpio/gpio-lynxpoint.c index 86c17de87692..761c4705dfbb 100644 --- a/drivers/gpio/gpio-lynxpoint.c +++ b/drivers/gpio/gpio-lynxpoint.c @@ -447,7 +447,6 @@ static int lp_gpio_remove(struct platform_device *pdev) err = gpiochip_remove(&lg->chip); if (err) dev_warn(&pdev->dev, "failed to remove gpio_chip.\n"); - platform_set_drvdata(pdev, NULL); return 0; } -- cgit v1.2.3 From 8be2010ec96ae36e71df119eff0aba3d8ecd5622 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 6 May 2013 12:29:18 +0900 Subject: gpio: rdc321x: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Linus Walleij --- drivers/gpio/gpio-rdc321x.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-rdc321x.c b/drivers/gpio/gpio-rdc321x.c index 1bf55f67f7a5..368c3c00fca5 100644 --- a/drivers/gpio/gpio-rdc321x.c +++ b/drivers/gpio/gpio-rdc321x.c @@ -187,20 +187,18 @@ static int rdc321x_gpio_probe(struct platform_device *pdev) rdc321x_gpio_dev->reg1_data_base, &rdc321x_gpio_dev->data_reg[0]); if (err) - goto out_drvdata; + goto out_free; err = pci_read_config_dword(rdc321x_gpio_dev->sb_pdev, rdc321x_gpio_dev->reg2_data_base, &rdc321x_gpio_dev->data_reg[1]); if (err) - goto out_drvdata; + goto out_free; dev_info(&pdev->dev, "registering %d GPIOs\n", rdc321x_gpio_dev->chip.ngpio); return gpiochip_add(&rdc321x_gpio_dev->chip); -out_drvdata: - platform_set_drvdata(pdev, NULL); out_free: kfree(rdc321x_gpio_dev); return err; @@ -216,7 +214,6 @@ static int rdc321x_gpio_remove(struct platform_device *pdev) dev_err(&pdev->dev, "failed to unregister chip\n"); kfree(rdc321x_gpio_dev); - platform_set_drvdata(pdev, NULL); return ret; } -- cgit v1.2.3 From e1142aaeba1859c6c2416aa237f8ce93f777b332 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 6 May 2013 12:30:49 +0900 Subject: gpio: stmpe: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Linus Walleij --- drivers/gpio/gpio-stmpe.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index 3ce5bc38ac31..671913e63458 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -431,7 +431,6 @@ static int stmpe_gpio_remove(struct platform_device *pdev) if (irq >= 0) free_irq(irq, stmpe_gpio); - platform_set_drvdata(pdev, NULL); kfree(stmpe_gpio); return 0; -- cgit v1.2.3 From 04df77298ff0bc108395b17a7c8fa8c5c8c3deab Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 6 May 2013 12:32:19 +0900 Subject: gpio: tc3589x: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Acked-by: Lee Jones Signed-off-by: Linus Walleij --- drivers/gpio/gpio-tc3589x.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpio/gpio-tc3589x.c b/drivers/gpio/gpio-tc3589x.c index d34d80dfb083..4a5de273c230 100644 --- a/drivers/gpio/gpio-tc3589x.c +++ b/drivers/gpio/gpio-tc3589x.c @@ -407,7 +407,6 @@ static int tc3589x_gpio_remove(struct platform_device *pdev) free_irq(irq, tc3589x_gpio); - platform_set_drvdata(pdev, NULL); kfree(tc3589x_gpio); return 0; -- cgit v1.2.3 From 752057e5ba87876b9d1126272e886c6a1a6234d0 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 6 May 2013 12:35:19 +0900 Subject: gpio: timberdale: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Linus Walleij --- drivers/gpio/gpio-timberdale.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpio/gpio-timberdale.c b/drivers/gpio/gpio-timberdale.c index 43774058b693..4c65f8883204 100644 --- a/drivers/gpio/gpio-timberdale.c +++ b/drivers/gpio/gpio-timberdale.c @@ -342,8 +342,6 @@ static int timbgpio_remove(struct platform_device *pdev) release_mem_region(iomem->start, resource_size(iomem)); kfree(tgpio); - platform_set_drvdata(pdev, NULL); - return 0; } -- cgit v1.2.3 From 1cdd8d52ecbedbce1cbac063aa5715810a228ab3 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 6 May 2013 12:36:51 +0900 Subject: gpio: vx855: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Linus Walleij --- drivers/gpio/gpio-vx855.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpio/gpio-vx855.c b/drivers/gpio/gpio-vx855.c index 2b7252cb2427..cddfa22edb41 100644 --- a/drivers/gpio/gpio-vx855.c +++ b/drivers/gpio/gpio-vx855.c @@ -279,7 +279,6 @@ out_release: release_region(res_gpi->start, resource_size(res_gpi)); if (vg->gpo_reserved) release_region(res_gpi->start, resource_size(res_gpo)); - platform_set_drvdata(pdev, NULL); kfree(vg); return ret; } @@ -301,7 +300,6 @@ static int vx855gpio_remove(struct platform_device *pdev) release_region(res->start, resource_size(res)); } - platform_set_drvdata(pdev, NULL); kfree(vg); return 0; } -- cgit v1.2.3 From 8700d0afc8ec3bc93f1105a94babfec6f9573b82 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 10 May 2013 17:32:28 +0800 Subject: gpio: stmpe: Staticize non-exported symbols Both stmpe_gpio_irq_map() and stmpe_gpio_irq_unmap() are not referenced outside of this file, make them static. Signed-off-by: Axel Lin Acked-by: Lee Jones Signed-off-by: Linus Walleij --- drivers/gpio/gpio-stmpe.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index 671913e63458..b33bad1bb4df 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -271,8 +271,8 @@ static irqreturn_t stmpe_gpio_irq(int irq, void *dev) return IRQ_HANDLED; } -int stmpe_gpio_irq_map(struct irq_domain *d, unsigned int virq, - irq_hw_number_t hwirq) +static int stmpe_gpio_irq_map(struct irq_domain *d, unsigned int virq, + irq_hw_number_t hwirq) { struct stmpe_gpio *stmpe_gpio = d->host_data; @@ -292,7 +292,7 @@ int stmpe_gpio_irq_map(struct irq_domain *d, unsigned int virq, return 0; } -void stmpe_gpio_irq_unmap(struct irq_domain *d, unsigned int virq) +static void stmpe_gpio_irq_unmap(struct irq_domain *d, unsigned int virq) { #ifdef CONFIG_ARM set_irq_flags(virq, 0); -- cgit v1.2.3 From 2b861f4b2b4787772b4f508c8cdfa492a01a60db Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 19 May 2013 14:25:48 +0800 Subject: gpio: max7300: Fix trivial typo in Kconfig help text max7301 is controlled through SPI interface which means another driver. Signed-off-by: Axel Lin Acked-by: Wolfram Sang Signed-off-by: Linus Walleij --- drivers/gpio/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 573c449c49b9..d133dd85443e 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -330,7 +330,7 @@ config GPIO_MAX7300 depends on I2C select GPIO_MAX730X help - GPIO driver for Maxim MAX7301 I2C-based GPIO expander. + GPIO driver for Maxim MAX7300 I2C-based GPIO expander. config GPIO_MAX732X tristate "MAX7319, MAX7320-7327 I2C Port Expanders" -- cgit v1.2.3 From 34510185abeaa5be9b178a41c0a03d30aec3db7e Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 27 May 2013 16:38:19 +1000 Subject: xfs: don't emit v5 superblock warnings on write We write the superblock every 30s or so which results in the verifier being called. Right now that results in this output every 30s: XFS (vda): Version 5 superblock detected. This kernel has EXPERIMENTAL support enabled! Use of these features in this kernel is at your own risk! And spamming the logs. We don't need to check for whether we support v5 superblocks or whether there are feature bits we don't support set as these are only relevant when we first mount the filesytem. i.e. on superblock read. Hence for the write verification we can just skip all the checks (and hence verbose output) altogether. Signed-off-by: Dave Chinner Reviewed-by: Brian Foster Signed-off-by: Ben Myers --- fs/xfs/xfs_mount.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index f6bfbd734669..e8e310c05097 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -314,7 +314,8 @@ STATIC int xfs_mount_validate_sb( xfs_mount_t *mp, xfs_sb_t *sbp, - bool check_inprogress) + bool check_inprogress, + bool check_version) { /* @@ -337,9 +338,10 @@ xfs_mount_validate_sb( /* * Version 5 superblock feature mask validation. Reject combinations the - * kernel cannot support up front before checking anything else. + * kernel cannot support up front before checking anything else. For + * write validation, we don't need to check feature masks. */ - if (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_5) { + if (check_version && XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_5) { xfs_alert(mp, "Version 5 superblock detected. This kernel has EXPERIMENTAL support enabled!\n" "Use of these features in this kernel is at your own risk!"); @@ -675,7 +677,8 @@ xfs_sb_to_disk( static int xfs_sb_verify( - struct xfs_buf *bp) + struct xfs_buf *bp, + bool check_version) { struct xfs_mount *mp = bp->b_target->bt_mount; struct xfs_sb sb; @@ -686,7 +689,8 @@ xfs_sb_verify( * Only check the in progress field for the primary superblock as * mkfs.xfs doesn't clear it from secondary superblocks. */ - return xfs_mount_validate_sb(mp, &sb, bp->b_bn == XFS_SB_DADDR); + return xfs_mount_validate_sb(mp, &sb, bp->b_bn == XFS_SB_DADDR, + check_version); } /* @@ -719,7 +723,7 @@ xfs_sb_read_verify( goto out_error; } } - error = xfs_sb_verify(bp); + error = xfs_sb_verify(bp, true); out_error: if (error) { @@ -758,7 +762,7 @@ xfs_sb_write_verify( struct xfs_buf_log_item *bip = bp->b_fspriv; int error; - error = xfs_sb_verify(bp); + error = xfs_sb_verify(bp, false); if (error) { XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr); xfs_buf_ioerror(bp, error); -- cgit v1.2.3 From aeb168f77c745fa4964e217400c9d43685e46705 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 22 May 2013 13:20:10 +0300 Subject: gpio-langwell: initialize lock before usage Otherwise we will end up with traceback from LOCKDEP: INFO: trying to register non-static key. the code is fine but needs lockdep annotation. turning off the locking correctness validator. CPU: 0 PID: 1 Comm: swapper/0 Not tainted 3.10.0-rc2-next-20130521-00028-g09aa9fc #487 00000000 00000000 f6c55c54 c1541fe4 f6040bf8 f6c55c8c c1069ef1 c1726bc1 c1726cc8 c1726c9e 00000000 f6c584e0 f6c58000 f6c55ce8 00000000 f6040bf8 f6040bf8 00000046 f6c58000 f6c55d00 c106a18d 00000a2b 00000003 00004f02 Call Trace: [] dump_stack+0x49/0x77 [] register_lock_class+0x58/0x260 [] __lock_acquire+0x94/0xcff [] ? __lock_acquire+0xccf/0xcff [] lock_acquire+0xcc/0x10d [] ? lnw_irq_type+0x63/0xe9 [] _raw_spin_lock_irqsave+0x32/0x42 [] ? lnw_irq_type+0x63/0xe9 [] lnw_irq_type+0x63/0xe9 [] __irq_set_trigger+0x98/0x123 [] irq_set_irq_type+0x2f/0x51 [] ? irq_set_irq_type+0x2f/0x51 [] ? lnw_irq_type+0xe9/0xe9 [] lnw_gpio_irq_map+0x32/0x3b [] irq_domain_add_legacy+0xe2/0x107 [] irq_domain_add_simple+0x47/0x60 [] lnw_gpio_probe+0x119/0x217 [] pci_device_probe+0x5a/0x92 ... Signed-off-by: Andy Shevchenko Acked-by: Mika Westerberg Acked-by: David Cohen Signed-off-by: Linus Walleij --- drivers/gpio/gpio-langwell.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-langwell.c b/drivers/gpio/gpio-langwell.c index b8a5729ca4f6..a0190de14371 100644 --- a/drivers/gpio/gpio-langwell.c +++ b/drivers/gpio/gpio-langwell.c @@ -380,6 +380,8 @@ static int lnw_gpio_probe(struct pci_dev *pdev, lnw->chip.can_sleep = 0; lnw->pdev = pdev; + spin_lock_init(&lnw->lock); + lnw->domain = irq_domain_add_simple(pdev->dev.of_node, ngpio, irq_base, &lnw_gpio_irq_ops, lnw); if (!lnw->domain) { @@ -399,8 +401,6 @@ static int lnw_gpio_probe(struct pci_dev *pdev, irq_set_handler_data(pdev->irq, lnw); irq_set_chained_handler(pdev->irq, lnw_irq_handler); - spin_lock_init(&lnw->lock); - pm_runtime_put_noidle(&pdev->dev); pm_runtime_allow(&pdev->dev); -- cgit v1.2.3 From 64c8cbc17c8d4af3b600c66247e47eac99e46aaf Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 22 May 2013 13:20:11 +0300 Subject: gpio-langwell: do not use direct access to iomapped memory We better to use readl() function instead of bad looking direct access. Signed-off-by: Andy Shevchenko Acked-by: Mika Westerberg Acked-by: David Cohen Signed-off-by: Linus Walleij --- drivers/gpio/gpio-langwell.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-langwell.c b/drivers/gpio/gpio-langwell.c index a0190de14371..fff661126dc7 100644 --- a/drivers/gpio/gpio-langwell.c +++ b/drivers/gpio/gpio-langwell.c @@ -65,7 +65,7 @@ enum GPIO_REG { struct lnw_gpio { struct gpio_chip chip; - void *reg_base; + void __iomem *reg_base; spinlock_t lock; struct pci_dev *pdev; struct irq_domain *domain; @@ -318,9 +318,9 @@ static const struct dev_pm_ops lnw_gpio_pm_ops = { }; static int lnw_gpio_probe(struct pci_dev *pdev, - const struct pci_device_id *id) + const struct pci_device_id *id) { - void *base; + void __iomem *base; resource_size_t start, len; struct lnw_gpio *lnw; u32 gpio_base; @@ -346,8 +346,10 @@ static int lnw_gpio_probe(struct pci_dev *pdev, retval = -EFAULT; goto err_ioremap; } - irq_base = *(u32 *)base; - gpio_base = *((u32 *)base + 1); + + irq_base = readl(base); + gpio_base = readl(sizeof(u32) + base); + /* release the IO mapping, since we already get the info from bar1 */ iounmap(base); /* get the register base from bar0 */ -- cgit v1.2.3 From 786e07ecb5b2ef896c9d42fa982d02da168e4290 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 22 May 2013 13:20:12 +0300 Subject: gpio-langwell: use managed functions pcim_* and devm_* This makes the error handling much more simpler than open-coding everything and in addition makes the probe function smaller an tidier. Signed-off-by: Andy Shevchenko Acked-by: Mika Westerberg Acked-by: David Cohen [Rebased on the platform-data set to NULL removal patch] Signed-off-by: Linus Walleij --- drivers/gpio/gpio-langwell.c | 78 ++++++++++++-------------------------------- 1 file changed, 21 insertions(+), 57 deletions(-) diff --git a/drivers/gpio/gpio-langwell.c b/drivers/gpio/gpio-langwell.c index fff661126dc7..54b6caf1742e 100644 --- a/drivers/gpio/gpio-langwell.c +++ b/drivers/gpio/gpio-langwell.c @@ -321,55 +321,37 @@ static int lnw_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id) { void __iomem *base; - resource_size_t start, len; struct lnw_gpio *lnw; u32 gpio_base; u32 irq_base; int retval; int ngpio = id->driver_data; - retval = pci_enable_device(pdev); + retval = pcim_enable_device(pdev); if (retval) return retval; - retval = pci_request_regions(pdev, "langwell_gpio"); + retval = pcim_iomap_regions(pdev, 1 << 0 | 1 << 1, pci_name(pdev)); if (retval) { - dev_err(&pdev->dev, "error requesting resources\n"); - goto err_pci_req_region; - } - /* get the gpio_base from bar1 */ - start = pci_resource_start(pdev, 1); - len = pci_resource_len(pdev, 1); - base = ioremap_nocache(start, len); - if (!base) { - dev_err(&pdev->dev, "error mapping bar1\n"); - retval = -EFAULT; - goto err_ioremap; + dev_err(&pdev->dev, "I/O memory mapping error\n"); + return retval; } + base = pcim_iomap_table(pdev)[1]; + irq_base = readl(base); gpio_base = readl(sizeof(u32) + base); /* release the IO mapping, since we already get the info from bar1 */ - iounmap(base); - /* get the register base from bar0 */ - start = pci_resource_start(pdev, 0); - len = pci_resource_len(pdev, 0); - base = devm_ioremap_nocache(&pdev->dev, start, len); - if (!base) { - dev_err(&pdev->dev, "error mapping bar0\n"); - retval = -EFAULT; - goto err_ioremap; - } + pcim_iounmap_regions(pdev, 1 << 1); lnw = devm_kzalloc(&pdev->dev, sizeof(*lnw), GFP_KERNEL); if (!lnw) { dev_err(&pdev->dev, "can't allocate langwell_gpio chip data\n"); - retval = -ENOMEM; - goto err_ioremap; + return -ENOMEM; } - lnw->reg_base = base; + lnw->reg_base = pcim_iomap_table(pdev)[0]; lnw->chip.label = dev_name(&pdev->dev); lnw->chip.request = lnw_gpio_request; lnw->chip.direction_input = lnw_gpio_direction_input; @@ -386,16 +368,14 @@ static int lnw_gpio_probe(struct pci_dev *pdev, lnw->domain = irq_domain_add_simple(pdev->dev.of_node, ngpio, irq_base, &lnw_gpio_irq_ops, lnw); - if (!lnw->domain) { - retval = -ENOMEM; - goto err_ioremap; - } + if (!lnw->domain) + return -ENOMEM; pci_set_drvdata(pdev, lnw); retval = gpiochip_add(&lnw->chip); if (retval) { dev_err(&pdev->dev, "langwell gpiochip_add error %d\n", retval); - goto err_ioremap; + return retval; } lnw_irq_init_hw(lnw); @@ -407,12 +387,6 @@ static int lnw_gpio_probe(struct pci_dev *pdev, pm_runtime_allow(&pdev->dev); return 0; - -err_ioremap: - pci_release_regions(pdev); -err_pci_req_region: - pci_disable_device(pdev); - return retval; } static struct pci_driver lnw_gpio_driver = { @@ -430,23 +404,20 @@ static int wp_gpio_probe(struct platform_device *pdev) struct lnw_gpio *lnw; struct gpio_chip *gc; struct resource *rc; - int retval = 0; - - rc = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!rc) - return -EINVAL; + int retval; - lnw = kzalloc(sizeof(struct lnw_gpio), GFP_KERNEL); + lnw = devm_kzalloc(&pdev->dev, sizeof(struct lnw_gpio), GFP_KERNEL); if (!lnw) { dev_err(&pdev->dev, "can't allocate whitneypoint_gpio chip data\n"); return -ENOMEM; } - lnw->reg_base = ioremap_nocache(rc->start, resource_size(rc)); - if (lnw->reg_base == NULL) { - retval = -EINVAL; - goto err_kmalloc; - } + + rc = platform_get_resource(pdev, IORESOURCE_MEM, 0); + lnw->reg_base = devm_ioremap_resource(&pdev->dev, rc); + if (IS_ERR(lnw->reg_base)) + return PTR_ERR(lnw->reg_base); + spin_lock_init(&lnw->lock); gc = &lnw->chip; gc->label = dev_name(&pdev->dev); @@ -463,15 +434,10 @@ static int wp_gpio_probe(struct platform_device *pdev) if (retval) { dev_err(&pdev->dev, "whitneypoint gpiochip_add error %d\n", retval); - goto err_ioremap; + return retval; } platform_set_drvdata(pdev, lnw); return 0; -err_ioremap: - iounmap(lnw->reg_base); -err_kmalloc: - kfree(lnw); - return retval; } static int wp_gpio_remove(struct platform_device *pdev) @@ -481,8 +447,6 @@ static int wp_gpio_remove(struct platform_device *pdev) err = gpiochip_remove(&lnw->chip); if (err) dev_err(&pdev->dev, "failed to remove gpio_chip.\n"); - iounmap(lnw->reg_base); - kfree(lnw); return 0; } -- cgit v1.2.3 From 8aca119f561ddb51d407b26f5dacb4f4ce3b3364 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 22 May 2013 13:20:13 +0300 Subject: gpio-langwell: amend error messages There is no need to use hardcoded device name in the error messages, because dev_err() prefixes the message with the device name anyway. Signed-off-by: Andy Shevchenko Acked-by: Mika Westerberg Acked-by: David Cohen Signed-off-by: Linus Walleij --- drivers/gpio/gpio-langwell.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpio-langwell.c b/drivers/gpio/gpio-langwell.c index 54b6caf1742e..313d190c528d 100644 --- a/drivers/gpio/gpio-langwell.c +++ b/drivers/gpio/gpio-langwell.c @@ -347,7 +347,7 @@ static int lnw_gpio_probe(struct pci_dev *pdev, lnw = devm_kzalloc(&pdev->dev, sizeof(*lnw), GFP_KERNEL); if (!lnw) { - dev_err(&pdev->dev, "can't allocate langwell_gpio chip data\n"); + dev_err(&pdev->dev, "can't allocate chip data\n"); return -ENOMEM; } @@ -374,7 +374,7 @@ static int lnw_gpio_probe(struct pci_dev *pdev, pci_set_drvdata(pdev, lnw); retval = gpiochip_add(&lnw->chip); if (retval) { - dev_err(&pdev->dev, "langwell gpiochip_add error %d\n", retval); + dev_err(&pdev->dev, "gpiochip_add error %d\n", retval); return retval; } @@ -408,8 +408,7 @@ static int wp_gpio_probe(struct platform_device *pdev) lnw = devm_kzalloc(&pdev->dev, sizeof(struct lnw_gpio), GFP_KERNEL); if (!lnw) { - dev_err(&pdev->dev, - "can't allocate whitneypoint_gpio chip data\n"); + dev_err(&pdev->dev, "can't allocate chip data\n"); return -ENOMEM; } @@ -432,8 +431,7 @@ static int wp_gpio_probe(struct platform_device *pdev) gc->can_sleep = 0; retval = gpiochip_add(gc); if (retval) { - dev_err(&pdev->dev, "whitneypoint gpiochip_add error %d\n", - retval); + dev_err(&pdev->dev, "gpiochip_add error %d\n", retval); return retval; } platform_set_drvdata(pdev, lnw); -- cgit v1.2.3 From 611a485b5fac3a6dce32a1ae6ddd3e0df215aecd Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 22 May 2013 13:20:14 +0300 Subject: gpio-langwell: drop away explicit casting Since the type of the reg_base member is void __iomem * we don't need to have explicit casting in gpio_reg() and gpio_reg_2bit(). Update year in the copyright notice as well. Signed-off-by: Andy Shevchenko Acked-by: Mika Westerberg Acked-by: David Cohen Signed-off-by: Linus Walleij --- drivers/gpio/gpio-langwell.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/gpio/gpio-langwell.c b/drivers/gpio/gpio-langwell.c index 313d190c528d..fec85ca89c58 100644 --- a/drivers/gpio/gpio-langwell.c +++ b/drivers/gpio/gpio-langwell.c @@ -1,7 +1,7 @@ /* * Moorestown platform Langwell chip GPIO driver * - * Copyright (c) 2008 - 2009, Intel Corporation. + * Copyright (c) 2008, 2009, 2013, Intel Corporation. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -74,15 +74,13 @@ struct lnw_gpio { #define to_lnw_priv(chip) container_of(chip, struct lnw_gpio, chip) static void __iomem *gpio_reg(struct gpio_chip *chip, unsigned offset, - enum GPIO_REG reg_type) + enum GPIO_REG reg_type) { struct lnw_gpio *lnw = to_lnw_priv(chip); unsigned nreg = chip->ngpio / 32; u8 reg = offset / 32; - void __iomem *ptr; - ptr = (void __iomem *)(lnw->reg_base + reg_type * nreg * 4 + reg * 4); - return ptr; + return lnw->reg_base + reg_type * nreg * 4 + reg * 4; } static void __iomem *gpio_reg_2bit(struct gpio_chip *chip, unsigned offset, @@ -91,10 +89,8 @@ static void __iomem *gpio_reg_2bit(struct gpio_chip *chip, unsigned offset, struct lnw_gpio *lnw = to_lnw_priv(chip); unsigned nreg = chip->ngpio / 32; u8 reg = offset / 16; - void __iomem *ptr; - ptr = (void __iomem *)(lnw->reg_base + reg_type * nreg * 4 + reg * 4); - return ptr; + return lnw->reg_base + reg_type * nreg * 4 + reg * 4; } static int lnw_gpio_request(struct gpio_chip *chip, unsigned offset) -- cgit v1.2.3 From 321a95839e65db3759a07a3655184b0283af90fe Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 27 May 2013 16:38:20 +1000 Subject: xfs: fix incorrect remote symlink block count When CRCs are enabled, the number of blocks needed to hold a remote symlink on a 1k block size filesystem may be 2 instead of 1. The transaction reservation for the allocated blocks was not taking this into account and only allocating one block. Hence when trying to read or invalidate such symlinks, we are mapping a hole where there should be a block and things go bad at that point. Fix the reservation to use the correct block count, clean up the block count calculation similar to the remote attribute calculation, and add a debug guard to detect when we don't write the entire symlink to disk. Signed-off-by: Dave Chinner Reviewed-by: Ben Myers Reviewed-by: Brian Foster Signed-off-by: Ben Myers --- fs/xfs/xfs_symlink.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/fs/xfs/xfs_symlink.c b/fs/xfs/xfs_symlink.c index 5f234389327c..195a403e1522 100644 --- a/fs/xfs/xfs_symlink.c +++ b/fs/xfs/xfs_symlink.c @@ -56,16 +56,9 @@ xfs_symlink_blocks( struct xfs_mount *mp, int pathlen) { - int fsblocks = 0; - int len = pathlen; + int buflen = XFS_SYMLINK_BUF_SPACE(mp, mp->m_sb.sb_blocksize); - do { - fsblocks++; - len -= XFS_SYMLINK_BUF_SPACE(mp, mp->m_sb.sb_blocksize); - } while (len > 0); - - ASSERT(fsblocks <= XFS_SYMLINK_MAPS); - return fsblocks; + return (pathlen + buflen - 1) / buflen; } static int @@ -405,7 +398,7 @@ xfs_symlink( if (pathlen <= XFS_LITINO(mp, dp->i_d.di_version)) fs_blocks = 0; else - fs_blocks = XFS_B_TO_FSB(mp, pathlen); + fs_blocks = xfs_symlink_blocks(mp, pathlen); resblks = XFS_SYMLINK_SPACE_RES(mp, link_name->len, fs_blocks); error = xfs_trans_reserve(tp, resblks, XFS_SYMLINK_LOG_RES(mp), 0, XFS_TRANS_PERM_LOG_RES, XFS_SYMLINK_LOG_COUNT); @@ -512,7 +505,7 @@ xfs_symlink( cur_chunk = target_path; offset = 0; for (n = 0; n < nmaps; n++) { - char *buf; + char *buf; d = XFS_FSB_TO_DADDR(mp, mval[n].br_startblock); byte_cnt = XFS_FSB_TO_B(mp, mval[n].br_blockcount); @@ -525,9 +518,7 @@ xfs_symlink( bp->b_ops = &xfs_symlink_buf_ops; byte_cnt = XFS_SYMLINK_BUF_SPACE(mp, byte_cnt); - if (pathlen < byte_cnt) { - byte_cnt = pathlen; - } + byte_cnt = min(byte_cnt, pathlen); buf = bp->b_addr; buf += xfs_symlink_hdr_set(mp, ip->i_ino, offset, @@ -542,6 +533,7 @@ xfs_symlink( xfs_trans_log_buf(tp, bp, 0, (buf + byte_cnt - 1) - (char *)bp->b_addr); } + ASSERT(pathlen == 0); } /* -- cgit v1.2.3 From df46dce09113ea0a52ce7403d35087e167f3a460 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 21 May 2013 23:11:10 +0800 Subject: gpio-ml-ioh: fix error return code in ioh_gpio_probe() Fix to return a negative error code in the irq descs alloc error handling case instead of 0, as done elsewhere in this function. Signed-off-by: Wei Yongjun Signed-off-by: Linus Walleij --- drivers/gpio/gpio-ml-ioh.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c index 0966f2637ad2..6da6d7667c6d 100644 --- a/drivers/gpio/gpio-ml-ioh.c +++ b/drivers/gpio/gpio-ml-ioh.c @@ -465,6 +465,7 @@ static int ioh_gpio_probe(struct pci_dev *pdev, dev_warn(&pdev->dev, "ml_ioh_gpio: Failed to get IRQ base num\n"); chip->irq_base = -1; + ret = irq_base; goto err_irq_alloc_descs; } chip->irq_base = irq_base; -- cgit v1.2.3 From 709da6a61aaf12181a8eea8443919ae5fc1b731d Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 27 May 2013 16:38:23 +1000 Subject: xfs: fix split buffer vector log recovery support A long time ago in a galaxy far away.... .. the was a commit made to fix some ilinux specific "fragmented buffer" log recovery problem: http://oss.sgi.com/cgi-bin/gitweb.cgi?p=archive/xfs-import.git;a=commitdiff;h=b29c0bece51da72fb3ff3b61391a391ea54e1603 That problem occurred when a contiguous dirty region of a buffer was split across across two pages of an unmapped buffer. It's been a long time since that has been done in XFS, and the changes to log the entire inode buffers for CRC enabled filesystems has re-introduced that corner case. And, of course, it turns out that the above commit didn't actually fix anything - it just ensured that log recovery is guaranteed to fail when this situation occurs. And now for the gory details. xfstest xfs/085 is failing with this assert: XFS (vdb): bad number of regions (0) in inode log format XFS: Assertion failed: 0, file: fs/xfs/xfs_log_recover.c, line: 1583 Largely undocumented factoid #1: Log recovery depends on all log buffer format items starting with this format: struct foo_log_format { __uint16_t type; __uint16_t size; .... As recoery uses the size field and assumptions about 32 bit alignment in decoding format items. So don't pay much attention to the fact log recovery thinks that it decoding an inode log format item - it just uses them to determine what the size of the item is. But why would it see a log format item with a zero size? Well, luckily enough xfs_logprint uses the same code and gives the same error, so with a bit of gdb magic, it turns out that it isn't a log format that is being decoded. What logprint tells us is this: Oper (130): tid: a0375e1a len: 28 clientid: TRANS flags: none BUF: #regs: 2 start blkno: 144 (0x90) len: 16 bmap size: 2 flags: 0x4000 Oper (131): tid: a0375e1a len: 4096 clientid: TRANS flags: none BUF DATA ---------------------------------------------------------------------------- Oper (132): tid: a0375e1a len: 4096 clientid: TRANS flags: none xfs_logprint: unknown log operation type (4e49) ********************************************************************** * ERROR: data block=2 * ********************************************************************** That we've got a buffer format item (oper 130) that has two regions; the format item itself and one dirty region. The subsequent region after the buffer format item and it's data is them what we are tripping over, and the first bytes of it at an inode magic number. Not a log opheader like there is supposed to be. That means there's a problem with the buffer format item. It's dirty data region is 4096 bytes, and it contains - you guessed it - initialised inodes. But inode buffers are 8k, not 4k, and we log them in their entirety. So something is wrong here. The buffer format item contains: (gdb) p /x *(struct xfs_buf_log_format *)in_f $22 = {blf_type = 0x123c, blf_size = 0x2, blf_flags = 0x4000, blf_len = 0x10, blf_blkno = 0x90, blf_map_size = 0x2, blf_data_map = {0xffffffff, 0xffffffff, .... }} Two regions, and a signle dirty contiguous region of 64 bits. 64 * 128 = 8k, so this should be followed by a single 8k region of data. And the blf_flags tell us that the type of buffer is a XFS_BLFT_DINO_BUF. It contains inodes. And because it doesn't have the XFS_BLF_INODE_BUF flag set, that means it's an inode allocation buffer. So, it should be followed by 8k of inode data. But we know that the next region has a header of: (gdb) p /x *ohead $25 = {oh_tid = 0x1a5e37a0, oh_len = 0x100000, oh_clientid = 0x69, oh_flags = 0x0, oh_res2 = 0x0} and so be32_to_cpu(oh_len) = 0x1000 = 4096 bytes. It's simply not long enough to hold all the logged data. There must be another region. There is - there's a following opheader for another 4k of data that contains the other half of the inode cluster data - the one we assert fail on because it's not a log format header. So why is the second part of the data not being accounted to the correct buffer log format structure? It took a little more work with gdb to work out that the buffer log format structure was both expecting it to be there but hadn't accounted for it. It was at that point I went to the kernel code, as clearly this wasn't a bug in xfs_logprint and the kernel was writing bad stuff to the log. First port of call was the buffer item formatting code, and the discontiguous memory/contiguous dirty region handling code immediately stood out. I've wondered for a long time why the code had this comment in it: vecp->i_addr = xfs_buf_offset(bp, buffer_offset); vecp->i_len = nbits * XFS_BLF_CHUNK; vecp->i_type = XLOG_REG_TYPE_BCHUNK; /* * You would think we need to bump the nvecs here too, but we do not * this number is used by recovery, and it gets confused by the boundary * split here * nvecs++; */ vecp++; And it didn't account for the extra vector pointer. The case being handled here is that a contiguous dirty region lies across a boundary that cannot be memcpy()d across, and so has to be split into two separate operations for xlog_write() to perform. What this code assumes is that what is written to the log is two consecutive blocks of data that are accounted in the buf log format item as the same contiguous dirty region and so will get decoded as such by the log recovery code. The thing is, xlog_write() knows nothing about this, and so just does it's normal thing of adding an opheader for each vector. That means the 8k region gets written to the log as two separate regions of 4k each, but because nvecs has not been incremented, the buf log format item accounts for only one of them. Hence when we come to log recovery, we process the first 4k region and then expect to come across a new item that starts with a log format structure of some kind that tells us whenteh next data is going to be. Instead, we hit raw buffer data and things go bad real quick. So, the commit from 2002 that commented out nvecs++ is just plain wrong. It breaks log recovery completely, and it would seem the only reason this hasn't been since then is that we don't log large contigous regions of multi-page unmapped buffers very often. Never would be a closer estimate, at least until the CRC code came along.... So, lets fix that by restoring the nvecs accounting for the extra region when we hit this case..... .... and there's the problemin log recovery it is apparently working around: XFS: Assertion failed: i == item->ri_total, file: fs/xfs/xfs_log_recover.c, line: 2135 Yup, xlog_recover_do_reg_buffer() doesn't handle contigous dirty regions being broken up into multiple regions by the log formatting code. That's an easy fix, though - if the number of contiguous dirty bits exceeds the length of the region being copied out of the log, only account for the number of dirty bits that region covers, and then loop again and copy more from the next region. It's a 2 line fix. Now xfstests xfs/085 passes, we have one less piece of mystery code, and one more important piece of knowledge about how to structure new log format items.. Signed-off-by: Dave Chinner Reviewed-by: Mark Tinguely Signed-off-by: Ben Myers --- fs/xfs/xfs_buf_item.c | 7 +------ fs/xfs/xfs_log_recover.c | 11 +++++++++++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c index cf263476d6b4..4ec431777048 100644 --- a/fs/xfs/xfs_buf_item.c +++ b/fs/xfs/xfs_buf_item.c @@ -262,12 +262,7 @@ xfs_buf_item_format_segment( vecp->i_addr = xfs_buf_offset(bp, buffer_offset); vecp->i_len = nbits * XFS_BLF_CHUNK; vecp->i_type = XLOG_REG_TYPE_BCHUNK; -/* - * You would think we need to bump the nvecs here too, but we do not - * this number is used by recovery, and it gets confused by the boundary - * split here - * nvecs++; - */ + nvecs++; vecp++; first_bit = next_bit; last_bit = next_bit; diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index 93f03ec17eec..d9e4d3c3991a 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -2096,6 +2096,17 @@ xlog_recover_do_reg_buffer( ASSERT(BBTOB(bp->b_io_length) >= ((uint)bit << XFS_BLF_SHIFT) + (nbits << XFS_BLF_SHIFT)); + /* + * The dirty regions logged in the buffer, even though + * contiguous, may span multiple chunks. This is because the + * dirty region may span a physical page boundary in a buffer + * and hence be split into two separate vectors for writing into + * the log. Hence we need to trim nbits back to the length of + * the current region being copied out of the log. + */ + if (item->ri_buf[i].i_len < (nbits << XFS_BLF_SHIFT)) + nbits = item->ri_buf[i].i_len >> XFS_BLF_SHIFT; + /* * Do a sanity check if this is a dquot buffer. Just checking * the first dquot in the buffer should do. XXXThis is -- cgit v1.2.3 From 02f75405a75eadfb072609f6bf839e027de6a29a Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 27 May 2013 16:38:24 +1000 Subject: xfs: disable swap extents ioctl on CRC enabled filesystems Currently, swapping extents from one inode to another is a simple act of switching data and attribute forks from one inode to another. This, unfortunately in no longer so simple with CRC enabled filesystems as there is owner information embedded into the BMBT blocks that are swapped between inodes. Hence swapping the forks between inodes results in the inodes having mapping blocks that point to the wrong owner and hence are considered corrupt. To fix this we need an extent tree block or record based swap algorithm so that the BMBT block owner information can be updated atomically in the swap transaction. This is a significant piece of new work, so for the moment simply don't allow swap extent operations to succeed on CRC enabled filesystems. Signed-off-by: Dave Chinner Reviewed-by: Ben Myers Reviewed-by: Brian Foster Signed-off-by: Ben Myers --- fs/xfs/xfs_dfrag.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fs/xfs/xfs_dfrag.c b/fs/xfs/xfs_dfrag.c index f852b082a084..c407e1ccff43 100644 --- a/fs/xfs/xfs_dfrag.c +++ b/fs/xfs/xfs_dfrag.c @@ -219,6 +219,14 @@ xfs_swap_extents( int taforkblks = 0; __uint64_t tmp; + /* + * We have no way of updating owner information in the BMBT blocks for + * each inode on CRC enabled filesystems, so to avoid corrupting the + * this metadata we simply don't allow extent swaps to occur. + */ + if (xfs_sb_version_hascrc(&mp->m_sb)) + return XFS_ERROR(EINVAL); + tempifp = kmem_alloc(sizeof(xfs_ifork_t), KM_MAYFAIL); if (!tempifp) { error = XFS_ERROR(ENOMEM); -- cgit v1.2.3 From 74137fff067961c9aca1e14d073805c3de8549bd Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 27 May 2013 16:38:26 +1000 Subject: xfs: add fsgeom flag for v5 superblock support. Currently userspace has no way of determining that a filesystem is CRC enabled. Add a flag to the XFS_IOC_FSGEOMETRY ioctl output to indicate that the filesystem has v5 superblock support enabled. This will allow xfs_info to correctly report the state of the filesystem. Signed-off-by: Dave Chinner Reviewed-by: Eric Sandeen Reviewed-by: Brian Foster Signed-off-by: Ben Myers --- fs/xfs/xfs_fs.h | 1 + fs/xfs/xfs_fsops.c | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/xfs/xfs_fs.h b/fs/xfs/xfs_fs.h index 6dda3f949b04..d04695545397 100644 --- a/fs/xfs/xfs_fs.h +++ b/fs/xfs/xfs_fs.h @@ -236,6 +236,7 @@ typedef struct xfs_fsop_resblks { #define XFS_FSOP_GEOM_FLAGS_PROJID32 0x0800 /* 32-bit project IDs */ #define XFS_FSOP_GEOM_FLAGS_DIRV2CI 0x1000 /* ASCII only CI names */ #define XFS_FSOP_GEOM_FLAGS_LAZYSB 0x4000 /* lazy superblock counters */ +#define XFS_FSOP_GEOM_FLAGS_V5SB 0x8000 /* version 5 superblock */ /* diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c index 87595b211da1..3c3644ea825b 100644 --- a/fs/xfs/xfs_fsops.c +++ b/fs/xfs/xfs_fsops.c @@ -99,7 +99,9 @@ xfs_fs_geometry( (xfs_sb_version_hasattr2(&mp->m_sb) ? XFS_FSOP_GEOM_FLAGS_ATTR2 : 0) | (xfs_sb_version_hasprojid32bit(&mp->m_sb) ? - XFS_FSOP_GEOM_FLAGS_PROJID32 : 0); + XFS_FSOP_GEOM_FLAGS_PROJID32 : 0) | + (xfs_sb_version_hascrc(&mp->m_sb) ? + XFS_FSOP_GEOM_FLAGS_V5SB : 0); geo->logsectsize = xfs_sb_version_hassector(&mp->m_sb) ? mp->m_sb.sb_logsectsize : BBSIZE; geo->rtsectsize = mp->m_sb.sb_blocksize; -- cgit v1.2.3 From ab276103357637fb26cc851369b5abbdc42afbf4 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Fri, 24 May 2013 12:18:30 +0200 Subject: ath9k: remove useless flag conversation. some flags used only outside of ath9k - In this case we can use "enum mac80211_rx_flags" and pass it upstream without extra conversation. Signed-off-by: Oleksij Rempel Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_mac.c | 5 +++-- drivers/net/wireless/ath/ath9k/mac.c | 11 +++++++---- drivers/net/wireless/ath/ath9k/mac.h | 1 + drivers/net/wireless/ath/ath9k/recv.c | 5 +---- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mac.c b/drivers/net/wireless/ath/ath9k/ar9003_mac.c index 301bf72c53bf..5163abd3937c 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c @@ -469,6 +469,7 @@ int ath9k_hw_process_rxdesc_edma(struct ath_hw *ah, struct ath_rx_status *rxs, rxs->rs_status = 0; rxs->rs_flags = 0; + rxs->flag = 0; rxs->rs_datalen = rxsp->status2 & AR_DataLen; rxs->rs_tstamp = rxsp->status3; @@ -493,8 +494,8 @@ int ath9k_hw_process_rxdesc_edma(struct ath_hw *ah, struct ath_rx_status *rxs, rxs->rs_isaggr = (rxsp->status11 & AR_RxAggr) ? 1 : 0; rxs->rs_moreaggr = (rxsp->status11 & AR_RxMoreAggr) ? 1 : 0; rxs->rs_antenna = (MS(rxsp->status4, AR_RxAntenna) & 0x7); - rxs->rs_flags = (rxsp->status4 & AR_GI) ? ATH9K_RX_GI : 0; - rxs->rs_flags |= (rxsp->status4 & AR_2040) ? ATH9K_RX_2040 : 0; + rxs->flag |= (rxsp->status4 & AR_GI) ? RX_FLAG_SHORT_GI : 0; + rxs->flag |= (rxsp->status4 & AR_2040) ? RX_FLAG_40MHZ : 0; rxs->evm0 = rxsp->status6; rxs->evm1 = rxsp->status7; diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c index 498fee04afa0..a52081d73bef 100644 --- a/drivers/net/wireless/ath/ath9k/mac.c +++ b/drivers/net/wireless/ath/ath9k/mac.c @@ -547,6 +547,7 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds, rs->rs_status = 0; rs->rs_flags = 0; + rs->flag = 0; rs->rs_datalen = ads.ds_rxstatus1 & AR_DataLen; rs->rs_tstamp = ads.AR_RcvTimestamp; @@ -586,10 +587,12 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds, rs->rs_moreaggr = (ads.ds_rxstatus8 & AR_RxMoreAggr) ? 1 : 0; rs->rs_antenna = MS(ads.ds_rxstatus3, AR_RxAntenna); - rs->rs_flags = - (ads.ds_rxstatus3 & AR_GI) ? ATH9K_RX_GI : 0; - rs->rs_flags |= - (ads.ds_rxstatus3 & AR_2040) ? ATH9K_RX_2040 : 0; + + /* directly mapped flags for ieee80211_rx_status */ + rs->flag |= + (ads.ds_rxstatus3 & AR_GI) ? RX_FLAG_SHORT_GI : 0; + rs->flag |= + (ads.ds_rxstatus3 & AR_2040) ? RX_FLAG_40MHZ : 0; if (ads.ds_rxstatus8 & AR_PreDelimCRCErr) rs->rs_flags |= ATH9K_RX_DELIM_CRC_PRE; diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h index 5865f92998e1..3f1e7754a1a7 100644 --- a/drivers/net/wireless/ath/ath9k/mac.h +++ b/drivers/net/wireless/ath/ath9k/mac.h @@ -149,6 +149,7 @@ struct ath_rx_status { u32 evm2; u32 evm3; u32 evm4; + u32 flag; /* see enum mac80211_rx_flags */ }; struct ath_htc_rx_status { diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 8be2b5d8c155..b4b758deb30e 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -868,10 +868,7 @@ static int ath9k_process_rate(struct ath_common *common, if (rx_stats->rs_rate & 0x80) { /* HT rate */ rxs->flag |= RX_FLAG_HT; - if (rx_stats->rs_flags & ATH9K_RX_2040) - rxs->flag |= RX_FLAG_40MHZ; - if (rx_stats->rs_flags & ATH9K_RX_GI) - rxs->flag |= RX_FLAG_SHORT_GI; + rxs->flag |= rx_stats->flag; rxs->rate_idx = rx_stats->rs_rate & 0x7f; return 0; } -- cgit v1.2.3 From b0a1ae976d6cd40ff90ba87883e17eb2610dae3d Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Fri, 24 May 2013 20:30:59 +0200 Subject: ath9k: check for Rx-STBC flag and pass it to ieee80211 This patch make use of STBC flag in DMA RX descriptor. Only devices after ar9280 can provide this information. If card support it we will set HAVE_STBC flag, to show clint programm thet STBC is supported but not received. Signed-off-by: Oleksij Rempel Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/init.c | 10 ++++++++-- drivers/net/wireless/ath/ath9k/mac.c | 5 +++++ drivers/net/wireless/ath/ath9k/mac.h | 3 ++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index c7b888f22703..0536d442b068 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "ath9k.h" @@ -766,8 +767,13 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) IEEE80211_HW_REPORTS_TX_ACK_STATUS | IEEE80211_HW_SUPPORTS_RC_TABLE; - if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) - hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION; + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) { + hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION; + + if (AR_SREV_9280_20_OR_LATER(ah)) + hw->radiotap_mcs_details |= + IEEE80211_RADIOTAP_MCS_HAVE_STBC; + } if (AR_SREV_9160_10_OR_LATER(sc->sc_ah) || ath9k_modparam_nohwcrypt) hw->flags |= IEEE80211_HW_MFP_CAPABLE; diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c index a52081d73bef..d055e389abfc 100644 --- a/drivers/net/wireless/ath/ath9k/mac.c +++ b/drivers/net/wireless/ath/ath9k/mac.c @@ -593,6 +593,11 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds, (ads.ds_rxstatus3 & AR_GI) ? RX_FLAG_SHORT_GI : 0; rs->flag |= (ads.ds_rxstatus3 & AR_2040) ? RX_FLAG_40MHZ : 0; + if (AR_SREV_9280_20_OR_LATER(ah)) + rs->flag |= + (ads.ds_rxstatus3 & AR_STBC) ? + /* we can only Nss=1 STBC */ + (1 << RX_FLAG_STBC_SHIFT) : 0; if (ads.ds_rxstatus8 & AR_PreDelimCRCErr) rs->rs_flags |= ATH9K_RX_DELIM_CRC_PRE; diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h index 3f1e7754a1a7..b02dfce964b4 100644 --- a/drivers/net/wireless/ath/ath9k/mac.h +++ b/drivers/net/wireless/ath/ath9k/mac.h @@ -534,7 +534,8 @@ struct ar5416_desc { #define AR_2040 0x00000002 #define AR_Parallel40 0x00000004 #define AR_Parallel40_S 2 -#define AR_RxStatusRsvd30 0x000000f8 +#define AR_STBC 0x00000008 /* on ar9280 and later */ +#define AR_RxStatusRsvd30 0x000000f0 #define AR_RxAntenna 0xffffff00 #define AR_RxAntenna_S 8 -- cgit v1.2.3 From 4e17b87e792ed19e75a96eea618b90510265120c Mon Sep 17 00:00:00 2001 From: Solomon Peachy Date: Wed, 29 May 2013 22:22:05 -0400 Subject: cw1200: Fix compile with CONFIG_PM=n Intel's 0-day kernel build tester caught this build failure. This patch properly wraps everything that depends on CONFIG_PM. Signed-off-by: Solomon Peachy Signed-off-by: John W. Linville --- drivers/net/wireless/cw1200/Makefile | 2 +- drivers/net/wireless/cw1200/cw1200_sdio.c | 4 ++++ drivers/net/wireless/cw1200/cw1200_spi.c | 4 ++++ drivers/net/wireless/cw1200/main.c | 10 ++++++++++ drivers/net/wireless/cw1200/pm.h | 11 ++++++++--- 5 files changed, 27 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/cw1200/Makefile b/drivers/net/wireless/cw1200/Makefile index c19737276be2..1aa3682066e0 100644 --- a/drivers/net/wireless/cw1200/Makefile +++ b/drivers/net/wireless/cw1200/Makefile @@ -8,9 +8,9 @@ cw1200_core-y := \ wsm.o \ sta.o \ scan.o \ - pm.o \ debug.o cw1200_core-$(CONFIG_CW1200_ITP) += itp.o +cw1200_core-$(CONFIG_PM) += pm.o # CFLAGS_sta.o += -DDEBUG diff --git a/drivers/net/wireless/cw1200/cw1200_sdio.c b/drivers/net/wireless/cw1200/cw1200_sdio.c index f6e22192d80a..2059a31815f3 100644 --- a/drivers/net/wireless/cw1200/cw1200_sdio.c +++ b/drivers/net/wireless/cw1200/cw1200_sdio.c @@ -334,6 +334,7 @@ static void cw1200_sdio_disconnect(struct sdio_func *func) } } +#ifdef CONFIG_PM static int cw1200_sdio_suspend(struct device *dev) { int ret; @@ -360,15 +361,18 @@ static const struct dev_pm_ops cw1200_pm_ops = { .suspend = cw1200_sdio_suspend, .resume = cw1200_sdio_resume, }; +#endif static struct sdio_driver sdio_driver = { .name = "cw1200_wlan_sdio", .id_table = cw1200_sdio_ids, .probe = cw1200_sdio_probe, .remove = cw1200_sdio_disconnect, +#ifdef CONFIG_PM .drv = { .pm = &cw1200_pm_ops, } +#endif }; /* Init Module function -> Called by insmod */ diff --git a/drivers/net/wireless/cw1200/cw1200_spi.c b/drivers/net/wireless/cw1200/cw1200_spi.c index 04af68534854..b957d4a01c52 100644 --- a/drivers/net/wireless/cw1200/cw1200_spi.c +++ b/drivers/net/wireless/cw1200/cw1200_spi.c @@ -436,6 +436,7 @@ static int cw1200_spi_disconnect(struct spi_device *func) return 0; } +#ifdef CONFIG_PM static int cw1200_spi_suspend(struct device *dev, pm_message_t state) { struct sbus_priv *self = spi_get_drvdata(to_spi_device(dev)); @@ -451,6 +452,7 @@ static int cw1200_spi_resume(struct device *dev) { return 0; } +#endif static struct spi_driver spi_driver = { .probe = cw1200_spi_probe, @@ -459,8 +461,10 @@ static struct spi_driver spi_driver = { .name = "cw1200_wlan_spi", .bus = &spi_bus_type, .owner = THIS_MODULE, +#ifdef CONFIG_PM .suspend = cw1200_spi_suspend, .resume = cw1200_spi_resume, +#endif }, }; diff --git a/drivers/net/wireless/cw1200/main.c b/drivers/net/wireless/cw1200/main.c index 8426d3d7607e..ef4b0b915f87 100644 --- a/drivers/net/wireless/cw1200/main.c +++ b/drivers/net/wireless/cw1200/main.c @@ -234,8 +234,10 @@ static const struct ieee80211_ops cw1200_ops = { .get_stats = cw1200_get_stats, .ampdu_action = cw1200_ampdu_action, .flush = cw1200_flush, +#ifdef CONFIG_PM .suspend = cw1200_wow_suspend, .resume = cw1200_wow_resume, +#endif /* Intentionally not offloaded: */ /*.channel_switch = cw1200_channel_switch, */ /*.remain_on_channel = cw1200_remain_on_channel, */ @@ -292,10 +294,12 @@ static struct ieee80211_hw *cw1200_init_common(const u8 *macaddr, BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO); +#ifdef CONFIG_PM /* Support only for limited wowlan functionalities */ hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_DISCONNECT; hw->wiphy->wowlan.n_patterns = 0; +#endif hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; @@ -419,18 +423,22 @@ static int cw1200_register_common(struct ieee80211_hw *dev) goto done; #endif +#ifdef CONFIG_PM err = cw1200_pm_init(&priv->pm_state, priv); if (err) { pr_err("Cannot init PM. (%d).\n", err); return err; } +#endif err = ieee80211_register_hw(dev); if (err) { pr_err("Cannot register device (%d).\n", err); +#ifdef CONFIG_PM cw1200_pm_deinit(&priv->pm_state); +#endif return err; } @@ -482,7 +490,9 @@ static void cw1200_unregister_common(struct ieee80211_hw *dev) cw1200_queue_deinit(&priv->tx_queue[i]); cw1200_queue_stats_deinit(&priv->tx_queue_stats); +#ifdef CONFIG_PM cw1200_pm_deinit(&priv->pm_state); +#endif } /* Clock is in KHz */ diff --git a/drivers/net/wireless/cw1200/pm.h b/drivers/net/wireless/cw1200/pm.h index 516d9671dd1e..3ed90ff22bb8 100644 --- a/drivers/net/wireless/cw1200/pm.h +++ b/drivers/net/wireless/cw1200/pm.h @@ -25,14 +25,19 @@ struct cw1200_pm_state { spinlock_t lock; /* Protect access */ }; +#ifdef CONFIG_PM int cw1200_pm_init(struct cw1200_pm_state *pm, struct cw1200_common *priv); void cw1200_pm_deinit(struct cw1200_pm_state *pm); -void cw1200_pm_stay_awake(struct cw1200_pm_state *pm, - unsigned long tmo); int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan); int cw1200_wow_resume(struct ieee80211_hw *hw); int cw1200_can_suspend(struct cw1200_common *priv); - +void cw1200_pm_stay_awake(struct cw1200_pm_state *pm, + unsigned long tmo); +#else +static inline void cw1200_pm_stay_awake(struct cw1200_pm_state *pm, + unsigned long tmo) { +} +#endif #endif -- cgit v1.2.3 From 56c19e89b38618390addfc743d822f99519055c6 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 27 May 2013 16:38:25 +1000 Subject: xfs: kill suid/sgid through the truncate path. XFS has failed to kill suid/sgid bits correctly when truncating files of non-zero size since commit c4ed4243 ("xfs: split xfs_setattr") introduced in the 3.1 kernel. Fix it. Fix it. cc: stable kernel Signed-off-by: Dave Chinner Reviewed-by: Brian Foster Signed-off-by: Ben Myers --- fs/xfs/xfs_iops.c | 47 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c index d82efaa2ac73..ca9ecaa81112 100644 --- a/fs/xfs/xfs_iops.c +++ b/fs/xfs/xfs_iops.c @@ -455,6 +455,28 @@ xfs_vn_getattr( return 0; } +static void +xfs_setattr_mode( + struct xfs_trans *tp, + struct xfs_inode *ip, + struct iattr *iattr) +{ + struct inode *inode = VFS_I(ip); + umode_t mode = iattr->ia_mode; + + ASSERT(tp); + ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); + + if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID)) + mode &= ~S_ISGID; + + ip->i_d.di_mode &= S_IFMT; + ip->i_d.di_mode |= mode & ~S_IFMT; + + inode->i_mode &= S_IFMT; + inode->i_mode |= mode & ~S_IFMT; +} + int xfs_setattr_nonsize( struct xfs_inode *ip, @@ -606,18 +628,8 @@ xfs_setattr_nonsize( /* * Change file access modes. */ - if (mask & ATTR_MODE) { - umode_t mode = iattr->ia_mode; - - if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID)) - mode &= ~S_ISGID; - - ip->i_d.di_mode &= S_IFMT; - ip->i_d.di_mode |= mode & ~S_IFMT; - - inode->i_mode &= S_IFMT; - inode->i_mode |= mode & ~S_IFMT; - } + if (mask & ATTR_MODE) + xfs_setattr_mode(tp, ip, iattr); /* * Change file access or modified times. @@ -714,9 +726,8 @@ xfs_setattr_size( return XFS_ERROR(error); ASSERT(S_ISREG(ip->i_d.di_mode)); - ASSERT((mask & (ATTR_MODE|ATTR_UID|ATTR_GID|ATTR_ATIME|ATTR_ATIME_SET| - ATTR_MTIME_SET|ATTR_KILL_SUID|ATTR_KILL_SGID| - ATTR_KILL_PRIV|ATTR_TIMES_SET)) == 0); + ASSERT((mask & (ATTR_UID|ATTR_GID|ATTR_ATIME|ATTR_ATIME_SET| + ATTR_MTIME_SET|ATTR_KILL_PRIV|ATTR_TIMES_SET)) == 0); if (!(flags & XFS_ATTR_NOLOCK)) { lock_flags |= XFS_IOLOCK_EXCL; @@ -860,6 +871,12 @@ xfs_setattr_size( xfs_inode_clear_eofblocks_tag(ip); } + /* + * Change file access modes. + */ + if (mask & ATTR_MODE) + xfs_setattr_mode(tp, ip, iattr); + if (mask & ATTR_CTIME) { inode->i_ctime = iattr->ia_ctime; ip->i_d.di_ctime.t_sec = iattr->ia_ctime.tv_sec; -- cgit v1.2.3 From 4696d477d8dd23ad990d716a90d08cb149c2e861 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 30 May 2013 19:41:11 +0800 Subject: cw1200: remove duplicated include from wsm.c Remove duplicated include. Signed-off-by: Wei Yongjun Signed-off-by: John W. Linville --- drivers/net/wireless/cw1200/wsm.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/cw1200/wsm.c b/drivers/net/wireless/cw1200/wsm.c index 4db6cc16879c..f3fd9b218724 100644 --- a/drivers/net/wireless/cw1200/wsm.c +++ b/drivers/net/wireless/cw1200/wsm.c @@ -12,7 +12,6 @@ #include #include -#include #include #include #include -- cgit v1.2.3 From c0dc79d607c39f5f6c6fd6cc8119b81639a6d6f3 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 30 May 2013 19:42:35 +0800 Subject: cw1200: convert to use simple_open() This removes an open coded simple_open() function and replaces file operations references to the function with simple_open() instead. Signed-off-by: Wei Yongjun Signed-off-by: John W. Linville --- drivers/net/wireless/cw1200/debug.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/net/wireless/cw1200/debug.c b/drivers/net/wireless/cw1200/debug.c index b815181802e0..eb40c9c61a51 100644 --- a/drivers/net/wireless/cw1200/debug.c +++ b/drivers/net/wireless/cw1200/debug.c @@ -357,12 +357,6 @@ static const struct file_operations fops_counters = { .owner = THIS_MODULE, }; -static int cw1200_generic_open(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - #ifdef CONFIG_CW1200_ETF static int cw1200_etf_out_show(struct seq_file *seq, void *v) { @@ -511,7 +505,7 @@ static ssize_t cw1200_wsm_dumps(struct file *file, } static const struct file_operations fops_wsm_dumps = { - .open = cw1200_generic_open, + .open = simple_open, .write = cw1200_wsm_dumps, .llseek = default_llseek, }; -- cgit v1.2.3 From d071c0430c6ae42fedec78f1ec2e37603adb8c78 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 30 May 2013 19:42:54 +0800 Subject: cw1200: use module_spi_driver to simplify the code module_spi_driver() makes the code simpler by eliminating boilerplate code. Signed-off-by: Wei Yongjun Signed-off-by: John W. Linville --- drivers/net/wireless/cw1200/cw1200_spi.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/drivers/net/wireless/cw1200/cw1200_spi.c b/drivers/net/wireless/cw1200/cw1200_spi.c index b957d4a01c52..feb541bb507c 100644 --- a/drivers/net/wireless/cw1200/cw1200_spi.c +++ b/drivers/net/wireless/cw1200/cw1200_spi.c @@ -468,17 +468,4 @@ static struct spi_driver spi_driver = { }, }; -/* Init Module function -> Called by insmod */ -static int __init cw1200_spi_init(void) -{ - return spi_register_driver(&spi_driver); -} - -/* Called at Driver Unloading */ -static void __exit cw1200_spi_exit(void) -{ - spi_unregister_driver(&spi_driver); -} - -module_init(cw1200_spi_init); -module_exit(cw1200_spi_exit); +module_spi_driver(spi_driver); -- cgit v1.2.3 From 3e817f086f06069a23b797ee2279bbae638d5edc Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 30 May 2013 19:43:13 +0800 Subject: cw1200: remove unused including Remove including that don't need it. Signed-off-by: Wei Yongjun Signed-off-by: John W. Linville --- drivers/net/wireless/cw1200/cw1200.h | 1 - drivers/net/wireless/cw1200/cw1200_sdio.c | 1 - drivers/net/wireless/cw1200/cw1200_spi.c | 1 - 3 files changed, 3 deletions(-) diff --git a/drivers/net/wireless/cw1200/cw1200.h b/drivers/net/wireless/cw1200/cw1200.h index 2aa17ca60ba8..e9424e635883 100644 --- a/drivers/net/wireless/cw1200/cw1200.h +++ b/drivers/net/wireless/cw1200/cw1200.h @@ -19,7 +19,6 @@ #define CW1200_H #include -#include #include #include #include diff --git a/drivers/net/wireless/cw1200/cw1200_sdio.c b/drivers/net/wireless/cw1200/cw1200_sdio.c index 2059a31815f3..863510d062fb 100644 --- a/drivers/net/wireless/cw1200/cw1200_sdio.c +++ b/drivers/net/wireless/cw1200/cw1200_sdio.c @@ -9,7 +9,6 @@ * published by the Free Software Foundation. */ -#include #include #include #include diff --git a/drivers/net/wireless/cw1200/cw1200_spi.c b/drivers/net/wireless/cw1200/cw1200_spi.c index feb541bb507c..75adef0c0cfd 100644 --- a/drivers/net/wireless/cw1200/cw1200_spi.c +++ b/drivers/net/wireless/cw1200/cw1200_spi.c @@ -13,7 +13,6 @@ * published by the Free Software Foundation. */ -#include #include #include #include -- cgit v1.2.3 From d15b71796316100b31729c8305ac11f03a189670 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Sun, 26 May 2013 11:50:54 +0900 Subject: MAINTAINERS: add linux-gpio mailing list Signed-off-by: Alexandre Courbot Signed-off-by: Linus Walleij --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index fd3a495a0005..e9049a8f1218 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3566,6 +3566,7 @@ GPIO SUBSYSTEM M: Grant Likely M: Linus Walleij S: Maintained +L: linux-gpio@vger.kernel.org T: git git://git.secretlab.ca/git/linux-2.6.git F: Documentation/gpio.txt F: drivers/gpio/ -- cgit v1.2.3 From 4bcccf193dae6afd0ac78bb89c6e4b0b8324e9d4 Mon Sep 17 00:00:00 2001 From: Tang Yuantian Date: Wed, 22 May 2013 16:22:19 +0800 Subject: clk: mpc85xx: Update the compatible string The compatible string of clock is changed from *-2 to *-2.0 on chassis 2. So updated it accordingly. Signed-off-by: Tang Yuantian Signed-off-by: Mike Turquette [mturquette@linaro.org: improved $SUBJECT line] --- drivers/clk/clk-ppc-corenet.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/clk-ppc-corenet.c b/drivers/clk/clk-ppc-corenet.c index a2d483fd9134..e9587073bd32 100644 --- a/drivers/clk/clk-ppc-corenet.c +++ b/drivers/clk/clk-ppc-corenet.c @@ -260,7 +260,7 @@ static int __init ppc_corenet_clk_probe(struct platform_device *pdev) static const struct of_device_id ppc_clk_ids[] __initconst = { { .compatible = "fsl,qoriq-clockgen-1.0", }, - { .compatible = "fsl,qoriq-clockgen-2", }, + { .compatible = "fsl,qoriq-clockgen-2.0", }, {} }; -- cgit v1.2.3 From 5ae6e6a401957698f2bd8c9f4a86d86d02199fea Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Tue, 28 May 2013 18:37:17 +1000 Subject: xfs: fix dir3 freespace block corruption When the directory freespace index grows to a second block (2017 4k data blocks in the directory), the initialisation of the second new block header goes wrong. The write verifier fires a corruption error indicating that the block number in the header is zero. This was being tripped by xfs/110. The problem is that the initialisation of the new block is done just fine in xfs_dir3_free_get_buf(), but the caller then users a dirv2 structure to zero on-disk header fields that xfs_dir3_free_get_buf() has already zeroed. These lined up with the block number in the dir v3 header format. While looking at this, I noticed that the struct xfs_dir3_free_hdr() had 4 bytes of padding in it that wasn't defined as padding or being zeroed by the initialisation. Add a pad field declaration and fully zero the on disk and in-core headers in xfs_dir3_free_get_buf() so that this is never an issue in the future. Note that this doesn't change the on-disk layout, just makes the 32 bits of padding in the layout explicit. Signed-off-by: Dave Chinner Reviewed-by: Ben Myers Signed-off-by: Ben Myers --- fs/xfs/xfs_dir2_format.h | 1 + fs/xfs/xfs_dir2_node.c | 13 ++++++------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/fs/xfs/xfs_dir2_format.h b/fs/xfs/xfs_dir2_format.h index a3b1bd841a80..995f1f505a52 100644 --- a/fs/xfs/xfs_dir2_format.h +++ b/fs/xfs/xfs_dir2_format.h @@ -715,6 +715,7 @@ struct xfs_dir3_free_hdr { __be32 firstdb; /* db of first entry */ __be32 nvalid; /* count of valid entries */ __be32 nused; /* count of used entries */ + __be32 pad; /* 64 bit alignment. */ }; struct xfs_dir3_free { diff --git a/fs/xfs/xfs_dir2_node.c b/fs/xfs/xfs_dir2_node.c index 5246de4912d4..2226a00acd15 100644 --- a/fs/xfs/xfs_dir2_node.c +++ b/fs/xfs/xfs_dir2_node.c @@ -263,18 +263,19 @@ xfs_dir3_free_get_buf( * Initialize the new block to be empty, and remember * its first slot as our empty slot. */ - hdr.magic = XFS_DIR2_FREE_MAGIC; - hdr.firstdb = 0; - hdr.nused = 0; - hdr.nvalid = 0; + memset(bp->b_addr, 0, sizeof(struct xfs_dir3_free_hdr)); + memset(&hdr, 0, sizeof(hdr)); + if (xfs_sb_version_hascrc(&mp->m_sb)) { struct xfs_dir3_free_hdr *hdr3 = bp->b_addr; hdr.magic = XFS_DIR3_FREE_MAGIC; + hdr3->hdr.blkno = cpu_to_be64(bp->b_bn); hdr3->hdr.owner = cpu_to_be64(dp->i_ino); uuid_copy(&hdr3->hdr.uuid, &mp->m_sb.sb_uuid); - } + } else + hdr.magic = XFS_DIR2_FREE_MAGIC; xfs_dir3_free_hdr_to_disk(bp->b_addr, &hdr); *bpp = bp; return 0; @@ -1921,8 +1922,6 @@ xfs_dir2_node_addname_int( */ freehdr.firstdb = (fbno - XFS_DIR2_FREE_FIRSTDB(mp)) * xfs_dir3_free_max_bests(mp); - free->hdr.nvalid = 0; - free->hdr.nused = 0; } else { free = fbp->b_addr; bests = xfs_dir3_free_bests_p(mp, free); -- cgit v1.2.3 From 2e4f1db49d97222110b6add9a2c6cf5251a41e35 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 30 May 2013 21:55:46 +0200 Subject: ACPI / processor: Initialize per_cpu(processors, pr->id) properly Commit ac212b6 (ACPI / processor: Use common hotplug infrastructure) forgot about initializing the per-CPU 'processors' variables which lead to ACPI cpuidle failure to use C-states and caused boot slowdown on multi-CPU machines. Fix the problem by adding per_cpu(processors, pr->id) initialization to acpi_processor_add() and add make acpi_processor_remove() clean it up as appropriate. Also modify acpi_processor_stop() so that it doesn't clear per_cpu(processors, pr->id) on processor driver removal which would then cause problems to happen when the driver is loaded again. This version of the patch contains fixes from Yinghai Lu. Reported-and-tested-by: Yinghai Lu Reported-and-tested-by: Daniel Lezcano Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_processor.c | 6 ++++++ drivers/acpi/processor_driver.c | 5 ----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c index 587d2af4b323..cae2641e8d84 100644 --- a/drivers/acpi/acpi_processor.c +++ b/drivers/acpi/acpi_processor.c @@ -29,6 +29,9 @@ ACPI_MODULE_NAME("processor"); +DEFINE_PER_CPU(struct acpi_processor *, processors); +EXPORT_PER_CPU_SYMBOL(processors); + /* -------------------------------------------------------------------------- Errata Handling -------------------------------------------------------------------------- */ @@ -387,6 +390,7 @@ static int __cpuinit acpi_processor_add(struct acpi_device *device, * checks. */ per_cpu(processor_device_array, pr->id) = device; + per_cpu(processors, pr->id) = pr; dev = get_cpu_device(pr->id); ACPI_HANDLE_SET(dev, pr->handle); @@ -407,6 +411,7 @@ static int __cpuinit acpi_processor_add(struct acpi_device *device, err: free_cpumask_var(pr->throttling.shared_cpu_map); device->driver_data = NULL; + per_cpu(processors, pr->id) = NULL; err_free_pr: kfree(pr); return result; @@ -441,6 +446,7 @@ static void acpi_processor_remove(struct acpi_device *device) /* Clean up. */ per_cpu(processor_device_array, pr->id) = NULL; + per_cpu(processors, pr->id) = NULL; try_offline_node(cpu_to_node(pr->id)); /* Remove the CPU. */ diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index ac28f18823b3..d93963f1e8f4 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -78,9 +78,6 @@ static struct device_driver acpi_processor_driver = { .remove = acpi_processor_stop, }; -DEFINE_PER_CPU(struct acpi_processor *, processors); -EXPORT_PER_CPU_SYMBOL(processors); - static void acpi_processor_notify(acpi_handle handle, u32 event, void *data) { struct acpi_device *device = data; @@ -268,8 +265,6 @@ static int acpi_processor_stop(struct device *dev) thermal_cooling_device_unregister(pr->cdev); pr->cdev = NULL; } - - per_cpu(processors, pr->id) = NULL; return 0; } -- cgit v1.2.3 From 644c8df2fb276e6cdfab18d60e9e0bf7a5b65b9a Mon Sep 17 00:00:00 2001 From: Nikolay Balandin Date: Tue, 28 May 2013 15:02:50 -0700 Subject: gpio: sx150x: convert to use devm_* functions Use devm_* functions to make cleanup paths simpler. Signed-off-by: Nikolay Balandin Signed-off-by: Linus Walleij --- drivers/gpio/gpio-sx150x.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/drivers/gpio/gpio-sx150x.c b/drivers/gpio/gpio-sx150x.c index 796b6c42fa70..f371732591d2 100644 --- a/drivers/gpio/gpio-sx150x.c +++ b/drivers/gpio/gpio-sx150x.c @@ -548,7 +548,8 @@ static int sx150x_install_irq_chip(struct sx150x_chip *chip, #endif } - err = request_threaded_irq(irq_summary, + err = devm_request_threaded_irq(&chip->client->dev, + irq_summary, NULL, sx150x_irq_thread_fn, IRQF_SHARED | IRQF_TRIGGER_FALLING, @@ -567,8 +568,6 @@ static void sx150x_remove_irq_chip(struct sx150x_chip *chip) unsigned n; unsigned irq; - free_irq(chip->irq_summary, chip); - for (n = 0; n < chip->dev_cfg->ngpios; ++n) { irq = chip->irq_base + n; irq_set_chip_and_handler(irq, NULL, NULL); @@ -591,18 +590,19 @@ static int sx150x_probe(struct i2c_client *client, if (!i2c_check_functionality(client->adapter, i2c_funcs)) return -ENOSYS; - chip = kzalloc(sizeof(struct sx150x_chip), GFP_KERNEL); + chip = devm_kzalloc(&client->dev, + sizeof(struct sx150x_chip), GFP_KERNEL); if (!chip) return -ENOMEM; sx150x_init_chip(chip, client, id->driver_data, pdata); rc = sx150x_init_hw(chip, pdata); if (rc < 0) - goto probe_fail_pre_gpiochip_add; + return rc; rc = gpiochip_add(&chip->gpio_chip); - if (rc < 0) - goto probe_fail_pre_gpiochip_add; + if (rc) + return rc; if (pdata->irq_summary >= 0) { rc = sx150x_install_irq_chip(chip, @@ -617,8 +617,6 @@ static int sx150x_probe(struct i2c_client *client, return 0; probe_fail_post_gpiochip_add: WARN_ON(gpiochip_remove(&chip->gpio_chip) < 0); -probe_fail_pre_gpiochip_add: - kfree(chip); return rc; } @@ -635,8 +633,6 @@ static int sx150x_remove(struct i2c_client *client) if (chip->irq_summary >= 0) sx150x_remove_irq_chip(chip); - kfree(chip); - return 0; } -- cgit v1.2.3 From dc7551cbf7744ef7374c3f3c46817413ad4bcc40 Mon Sep 17 00:00:00 2001 From: Manish Chopra Date: Thu, 30 May 2013 09:51:34 +0000 Subject: netxen_nic: Log driver version with firmware version Signed-off-by: Manish Chopra Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c index 51e13d92761e..2294cd2293b1 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c @@ -860,9 +860,9 @@ netxen_check_options(struct netxen_adapter *adapter) adapter->ahw.cut_through = (i & 0x8000) ? 1 : 0; } - dev_info(&pdev->dev, "firmware v%d.%d.%d [%s]\n", - fw_major, fw_minor, fw_build, - adapter->ahw.cut_through ? "cut-through" : "legacy"); + dev_info(&pdev->dev, "Driver v%s, firmware v%d.%d.%d [%s]\n", + NETXEN_NIC_LINUX_VERSIONID, fw_major, fw_minor, fw_build, + adapter->ahw.cut_through ? "cut-through" : "legacy"); if (adapter->fw_version >= NETXEN_VERSION_CODE(4, 0, 222)) adapter->capabilities = NXRD32(adapter, CRB_FW_CAPABILITIES_1); -- cgit v1.2.3 From b08a92bb7381ff03d4101bcc61e65120bc690a4f Mon Sep 17 00:00:00 2001 From: Manish Chopra Date: Thu, 30 May 2013 09:51:35 +0000 Subject: netxen_nic: Log proper error message in case of mismatched adapter type o log "Unknown" board name and "Unknown" serial number in case of mismatched adapter type found. This will avoid weird characters logs when an adapter is in bad state or corrupted. Signed-off-by: Manish Chopra Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/netxen/netxen_nic.h | 10 +++++++--- drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c | 4 +++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic.h b/drivers/net/ethernet/qlogic/netxen/netxen_nic.h index 322a36b76727..e5ab1ecb4bb2 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic.h +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic.h @@ -1855,7 +1855,7 @@ static const struct netxen_brdinfo netxen_boards[] = { #define NUM_SUPPORTED_BOARDS ARRAY_SIZE(netxen_boards) -static inline void get_brd_name_by_type(u32 type, char *name) +static inline int netxen_nic_get_brd_name_by_type(u32 type, char *name) { int i, found = 0; for (i = 0; i < NUM_SUPPORTED_BOARDS; ++i) { @@ -1864,10 +1864,14 @@ static inline void get_brd_name_by_type(u32 type, char *name) found = 1; break; } + } + if (!found) { + strcpy(name, "Unknown"); + return -EINVAL; } - if (!found) - name = "Unknown"; + + return 0; } static inline u32 netxen_tx_avail(struct nx_host_tx_ring *tx_ring) diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c index 2294cd2293b1..558df468f2e4 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c @@ -841,7 +841,9 @@ netxen_check_options(struct netxen_adapter *adapter) } if (adapter->portnum == 0) { - get_brd_name_by_type(adapter->ahw.board_type, brd_name); + if (netxen_nic_get_brd_name_by_type(adapter->ahw.board_type, + brd_name)) + strcpy(serial_num, "Unknown"); pr_info("%s: %s Board S/N %s Chip rev 0x%x\n", module_name(THIS_MODULE), -- cgit v1.2.3 From a120e864676548454f8d061e7d8a2e68c908a794 Mon Sep 17 00:00:00 2001 From: Manish Chopra Date: Thu, 30 May 2013 09:51:36 +0000 Subject: netxen_nic: netxen_setup_intr() function code cleanup Signed-off-by: Manish Chopra Signed-off-by: David S. Miller --- .../net/ethernet/qlogic/netxen/netxen_nic_main.c | 77 +++++++++++++--------- 1 file changed, 46 insertions(+), 31 deletions(-) diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c index 558df468f2e4..927ae9f801fb 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c @@ -593,47 +593,43 @@ static const struct net_device_ops netxen_netdev_ops = { }; static void -netxen_setup_intr(struct netxen_adapter *adapter) +netxen_initialize_interrupt_registers(struct netxen_adapter *adapter) { struct netxen_legacy_intr_set *legacy_intrp; - struct pci_dev *pdev = adapter->pdev; - int err, num_msix; - - if (adapter->rss_supported) { - num_msix = (num_online_cpus() >= MSIX_ENTRIES_PER_ADAPTER) ? - MSIX_ENTRIES_PER_ADAPTER : 2; - } else - num_msix = 1; - - adapter->max_sds_rings = 1; - - adapter->flags &= ~(NETXEN_NIC_MSI_ENABLED | NETXEN_NIC_MSIX_ENABLED); + u32 tgt_status_reg, int_state_reg; if (adapter->ahw.revision_id >= NX_P3_B0) legacy_intrp = &legacy_intr[adapter->ahw.pci_func]; else legacy_intrp = &legacy_intr[0]; + tgt_status_reg = legacy_intrp->tgt_status_reg; + int_state_reg = ISR_INT_STATE_REG; + adapter->int_vec_bit = legacy_intrp->int_vec_bit; - adapter->tgt_status_reg = netxen_get_ioaddr(adapter, - legacy_intrp->tgt_status_reg); + adapter->tgt_status_reg = netxen_get_ioaddr(adapter, tgt_status_reg); adapter->tgt_mask_reg = netxen_get_ioaddr(adapter, - legacy_intrp->tgt_mask_reg); + legacy_intrp->tgt_mask_reg); adapter->pci_int_reg = netxen_get_ioaddr(adapter, - legacy_intrp->pci_int_reg); + legacy_intrp->pci_int_reg); adapter->isr_int_vec = netxen_get_ioaddr(adapter, ISR_INT_VECTOR); if (adapter->ahw.revision_id >= NX_P3_B1) adapter->crb_int_state_reg = netxen_get_ioaddr(adapter, - ISR_INT_STATE_REG); + int_state_reg); else adapter->crb_int_state_reg = netxen_get_ioaddr(adapter, - CRB_INT_VECTOR); + CRB_INT_VECTOR); +} - netxen_set_msix_bit(pdev, 0); +static int netxen_setup_msi_interrupts(struct netxen_adapter *adapter, + int num_msix) +{ + struct pci_dev *pdev = adapter->pdev; + u32 value; + int err; if (adapter->msix_supported) { - netxen_init_msix_entries(adapter, num_msix); err = pci_enable_msix(pdev, adapter->msix_entries, num_msix); if (err == 0) { @@ -644,26 +640,45 @@ netxen_setup_intr(struct netxen_adapter *adapter) adapter->max_sds_rings = num_msix; dev_info(&pdev->dev, "using msi-x interrupts\n"); - return; + return 0; } - - if (err > 0) - pci_disable_msix(pdev); - /* fall through for msi */ } if (use_msi && !pci_enable_msi(pdev)) { + value = msi_tgt_status[adapter->ahw.pci_func]; adapter->flags |= NETXEN_NIC_MSI_ENABLED; - adapter->tgt_status_reg = netxen_get_ioaddr(adapter, - msi_tgt_status[adapter->ahw.pci_func]); - dev_info(&pdev->dev, "using msi interrupts\n"); + adapter->tgt_status_reg = netxen_get_ioaddr(adapter, value); adapter->msix_entries[0].vector = pdev->irq; - return; + dev_info(&pdev->dev, "using msi interrupts\n"); + return 0; } - dev_info(&pdev->dev, "using legacy interrupts\n"); + return -EIO; +} + +static void netxen_setup_intr(struct netxen_adapter *adapter) +{ + struct pci_dev *pdev = adapter->pdev; + int num_msix; + + if (adapter->rss_supported) + num_msix = (num_online_cpus() >= MSIX_ENTRIES_PER_ADAPTER) ? + MSIX_ENTRIES_PER_ADAPTER : 2; + else + num_msix = 1; + + adapter->max_sds_rings = 1; + adapter->flags &= ~(NETXEN_NIC_MSI_ENABLED | NETXEN_NIC_MSIX_ENABLED); + + netxen_initialize_interrupt_registers(adapter); + netxen_set_msix_bit(pdev, 0); + + if (!netxen_setup_msi_interrupts(adapter, num_msix)) + return; + adapter->msix_entries[0].vector = pdev->irq; + dev_info(&pdev->dev, "using legacy interrupts\n"); } static void -- cgit v1.2.3 From b37eb210c076b0465fcb8d14694fcce3f267ac63 Mon Sep 17 00:00:00 2001 From: Manish Chopra Date: Thu, 30 May 2013 09:51:37 +0000 Subject: netxen_nic: Avoid mixed mode interrupts o Adapters do not support co-existence of INTx Interrupt with MSI-X or MSI among multiple functions. Prevent attaching of a function during normal load, if adapter gets into mixed mode of interrupts Signed-off-by: Manish Chopra Signed-off-by: David S. Miller --- .../net/ethernet/qlogic/netxen/netxen_nic_hdr.h | 3 ++ .../net/ethernet/qlogic/netxen/netxen_nic_main.c | 50 +++++++++++++++++++--- 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_hdr.h b/drivers/net/ethernet/qlogic/netxen/netxen_nic_hdr.h index 28e076960bcb..32c790659f9c 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_hdr.h +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_hdr.h @@ -734,6 +734,9 @@ enum { #define NIC_CRB_BASE_2 (NETXEN_CAM_RAM(0x700)) #define NETXEN_NIC_REG(X) (NIC_CRB_BASE+(X)) #define NETXEN_NIC_REG_2(X) (NIC_CRB_BASE_2+(X)) +#define NETXEN_INTR_MODE_REG NETXEN_NIC_REG(0x44) +#define NETXEN_MSI_MODE 0x1 +#define NETXEN_INTX_MODE 0x2 #define NX_CDRP_CRB_OFFSET (NETXEN_NIC_REG(0x18)) #define NX_ARG1_CRB_OFFSET (NETXEN_NIC_REG(0x1c)) diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c index 927ae9f801fb..20c7b3c12b56 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c @@ -592,6 +592,22 @@ static const struct net_device_ops netxen_netdev_ops = { #endif }; +static inline bool netxen_function_zero(struct pci_dev *pdev) +{ + return (PCI_FUNC(pdev->devfn) == 0) ? true : false; +} + +static inline void netxen_set_interrupt_mode(struct netxen_adapter *adapter, + u32 mode) +{ + NXWR32(adapter, NETXEN_INTR_MODE_REG, mode); +} + +static inline u32 netxen_get_interrupt_mode(struct netxen_adapter *adapter) +{ + return NXRD32(adapter, NETXEN_INTR_MODE_REG); +} + static void netxen_initialize_interrupt_registers(struct netxen_adapter *adapter) { @@ -654,10 +670,11 @@ static int netxen_setup_msi_interrupts(struct netxen_adapter *adapter, return 0; } + dev_err(&pdev->dev, "Failed to acquire MSI-X/MSI interrupt vector\n"); return -EIO; } -static void netxen_setup_intr(struct netxen_adapter *adapter) +static int netxen_setup_intr(struct netxen_adapter *adapter) { struct pci_dev *pdev = adapter->pdev; int num_msix; @@ -674,11 +691,24 @@ static void netxen_setup_intr(struct netxen_adapter *adapter) netxen_initialize_interrupt_registers(adapter); netxen_set_msix_bit(pdev, 0); - if (!netxen_setup_msi_interrupts(adapter, num_msix)) - return; + if (netxen_function_zero(pdev)) { + if (!netxen_setup_msi_interrupts(adapter, num_msix)) + netxen_set_interrupt_mode(adapter, NETXEN_MSI_MODE); + else + netxen_set_interrupt_mode(adapter, NETXEN_INTX_MODE); + } else { + if (netxen_get_interrupt_mode(adapter) == NETXEN_MSI_MODE && + netxen_setup_msi_interrupts(adapter, num_msix)) { + dev_err(&pdev->dev, "Co-existence of MSI-X/MSI and INTx interrupts is not supported\n"); + return -EIO; + } + } - adapter->msix_entries[0].vector = pdev->irq; - dev_info(&pdev->dev, "using legacy interrupts\n"); + if (!NETXEN_IS_MSI_FAMILY(adapter)) { + adapter->msix_entries[0].vector = pdev->irq; + dev_info(&pdev->dev, "using legacy interrupts\n"); + } + return 0; } static void @@ -1525,7 +1555,13 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) netxen_nic_clear_stats(adapter); - netxen_setup_intr(adapter); + err = netxen_setup_intr(adapter); + + if (err) { + dev_err(&adapter->pdev->dev, + "Failed to setup interrupts, error = %d\n", err); + goto err_out_disable_msi; + } err = netxen_setup_netdev(adapter, netdev); if (err) @@ -1613,7 +1649,7 @@ static void netxen_nic_remove(struct pci_dev *pdev) clear_bit(__NX_RESETTING, &adapter->state); netxen_teardown_intr(adapter); - + netxen_set_interrupt_mode(adapter, 0); netxen_remove_diag_entries(adapter); netxen_cleanup_pci_map(adapter); -- cgit v1.2.3 From 90648153754cc1ca2eb08cd19c7b13354a795183 Mon Sep 17 00:00:00 2001 From: Manish Chopra Date: Thu, 30 May 2013 09:51:38 +0000 Subject: netxen_nic: Update version to 4.0.81 Signed-off-by: Manish Chopra Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/netxen/netxen_nic.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic.h b/drivers/net/ethernet/qlogic/netxen/netxen_nic.h index e5ab1ecb4bb2..3fe09ab2d7c9 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic.h +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic.h @@ -53,8 +53,8 @@ #define _NETXEN_NIC_LINUX_MAJOR 4 #define _NETXEN_NIC_LINUX_MINOR 0 -#define _NETXEN_NIC_LINUX_SUBVERSION 80 -#define NETXEN_NIC_LINUX_VERSIONID "4.0.80" +#define _NETXEN_NIC_LINUX_SUBVERSION 81 +#define NETXEN_NIC_LINUX_VERSIONID "4.0.81" #define NETXEN_VERSION_CODE(a, b, c) (((a) << 24) + ((b) << 16) + (c)) #define _major(v) (((v) >> 24) & 0xff) -- cgit v1.2.3 From ccfecdfe16a872ed3e8322ea48e34502568eb849 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 30 May 2013 00:28:03 +0000 Subject: net: emaclite: Report failures in mdio setup Be more verbose when any problem happens. Signed-off-by: Michal Simek Signed-off-by: David S. Miller --- drivers/net/ethernet/xilinx/xilinx_emaclite.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/xilinx/xilinx_emaclite.c b/drivers/net/ethernet/xilinx/xilinx_emaclite.c index 919b983114e9..a16dc3508998 100644 --- a/drivers/net/ethernet/xilinx/xilinx_emaclite.c +++ b/drivers/net/ethernet/xilinx/xilinx_emaclite.c @@ -852,8 +852,10 @@ static int xemaclite_mdio_setup(struct net_local *lp, struct device *dev) /* Don't register the MDIO bus if the phy_node or its parent node * can't be found. */ - if (!np) + if (!np) { + dev_err(dev, "Failed to register mdio bus.\n"); return -ENODEV; + } /* Enable the MDIO bus by asserting the enable bit in MDIO Control * register. @@ -862,8 +864,10 @@ static int xemaclite_mdio_setup(struct net_local *lp, struct device *dev) XEL_MDIOCTRL_MDIOEN_MASK); bus = mdiobus_alloc(); - if (!bus) + if (!bus) { + dev_err(dev, "Failed to allocal mdiobus\n"); return -ENOMEM; + } of_address_to_resource(np, 0, &res); snprintf(bus->id, MII_BUS_ID_SIZE, "%.8llx", @@ -879,8 +883,10 @@ static int xemaclite_mdio_setup(struct net_local *lp, struct device *dev) lp->mii_bus = bus; rc = of_mdiobus_register(bus, np); - if (rc) + if (rc) { + dev_err(dev, "Failed to register mdio bus.\n"); goto err_register; + } return 0; -- cgit v1.2.3 From e0a3bc65448c01289a74329ddf6c84d8c0594e59 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 30 May 2013 00:28:04 +0000 Subject: net: emaclite: Support multiple phys connected to one MDIO bus For system which contains at least two ethernet IP where one IP manage MDIO bus with several PHYs. Example dts node: ethernet_mac0: ethernet@81000000 { compatible = "xlnx,xps-ethernetlite-1.00.a"; device_type = "network"; interrupt-parent = <&xps_intc_0>; interrupts = < 1 0 >; local-mac-address = [ 00 0a 35 00 db bb ]; phy-handle = <ðernet_mac0_phy0>; reg = < 0x81000000 0x10000 >; xlnx,duplex = <0x1>; xlnx,family = "spartan3e"; xlnx,include-internal-loopback = <0x0>; xlnx,include-mdio = <0x1>; xlnx,rx-ping-pong = <0x0>; xlnx,tx-ping-pong = <0x0>; ethernet_mac0_mdio { #address-cells = <1>; #size-cells = <0>; ethernet_mac0_phy0: phy@1 { reg = <0x1>; } ; ethernet_mac0_phy1: phy@3 { reg = <0x3>; } ; } ; } ; ethernet_mac2: ethernet@81040000 { compatible = "xlnx,xps-ethernetlite-1.00.a"; device_type = "network"; interrupt-parent = <&xps_intc_0>; interrupts = < 11 0 >; local-mac-address = [ 00 0a 35 00 db bb ]; phy-handle = <ðernet_mac0_phy1>; reg = < 0x81040000 0x10000 >; xlnx,duplex = <0x1>; xlnx,family = "spartan3e"; xlnx,include-internal-loopback = <0x0>; xlnx,include-mdio = <0x0>; xlnx,rx-ping-pong = <0x0>; xlnx,tx-ping-pong = <0x0>; } ; Signed-off-by: Michal Simek Signed-off-by: David S. Miller --- drivers/net/ethernet/xilinx/xilinx_emaclite.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/xilinx/xilinx_emaclite.c b/drivers/net/ethernet/xilinx/xilinx_emaclite.c index a16dc3508998..fcd1e0b46103 100644 --- a/drivers/net/ethernet/xilinx/xilinx_emaclite.c +++ b/drivers/net/ethernet/xilinx/xilinx_emaclite.c @@ -848,6 +848,7 @@ static int xemaclite_mdio_setup(struct net_local *lp, struct device *dev) int rc; struct resource res; struct device_node *np = of_get_parent(lp->phy_node); + struct device_node *npp; /* Don't register the MDIO bus if the phy_node or its parent node * can't be found. @@ -856,6 +857,17 @@ static int xemaclite_mdio_setup(struct net_local *lp, struct device *dev) dev_err(dev, "Failed to register mdio bus.\n"); return -ENODEV; } + npp = of_get_parent(np); + + of_address_to_resource(npp, 0, &res); + if (lp->ndev->mem_start != res.start) { + struct phy_device *phydev; + phydev = of_phy_find_device(lp->phy_node); + if (!phydev) + dev_info(dev, + "MDIO of the phy is not registered yet\n"); + return 0; + } /* Enable the MDIO bus by asserting the enable bit in MDIO Control * register. @@ -869,7 +881,6 @@ static int xemaclite_mdio_setup(struct net_local *lp, struct device *dev) return -ENOMEM; } - of_address_to_resource(np, 0, &res); snprintf(bus->id, MII_BUS_ID_SIZE, "%.8llx", (unsigned long long)res.start); bus->priv = lp; -- cgit v1.2.3 From 3fb99fa7c7d2310791bf39929285da44b201fd40 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 30 May 2013 00:28:05 +0000 Subject: net: emaclite: Let's make xemaclite_adjust_link static xemaclite_adjust_link is used locally. It removes sparse warning: drivers/net/ethernet/xilinx/xilinx_emaclite.c:916:6: warning: symbol 'xemaclite_adjust_link' was not declared. Should it be static? Signed-off-by: Michal Simek Signed-off-by: David S. Miller --- drivers/net/ethernet/xilinx/xilinx_emaclite.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/xilinx/xilinx_emaclite.c b/drivers/net/ethernet/xilinx/xilinx_emaclite.c index fcd1e0b46103..93bb14e04d84 100644 --- a/drivers/net/ethernet/xilinx/xilinx_emaclite.c +++ b/drivers/net/ethernet/xilinx/xilinx_emaclite.c @@ -913,7 +913,7 @@ err_register: * There's nothing in the Emaclite device to be configured when the link * state changes. We just print the status. */ -void xemaclite_adjust_link(struct net_device *ndev) +static void xemaclite_adjust_link(struct net_device *ndev) { struct net_local *lp = netdev_priv(ndev); struct phy_device *phy = lp->phy_dev; -- cgit v1.2.3 From 123c1407af877b9b036b852b77013a6f9f6049b0 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 30 May 2013 00:28:06 +0000 Subject: net: emaclite: Do not use microblaze and ppc IO functions Emaclite can be used on ARM zynq where in_be32/out_be32 IO functions are not present. Use standard __raw_readl/__raw_writel IO functions instead. Signed-off-by: Michal Simek Signed-off-by: David S. Miller --- drivers/net/ethernet/xilinx/xilinx_emaclite.c | 138 +++++++++++++------------- 1 file changed, 69 insertions(+), 69 deletions(-) diff --git a/drivers/net/ethernet/xilinx/xilinx_emaclite.c b/drivers/net/ethernet/xilinx/xilinx_emaclite.c index 93bb14e04d84..9227ed7bce10 100644 --- a/drivers/net/ethernet/xilinx/xilinx_emaclite.c +++ b/drivers/net/ethernet/xilinx/xilinx_emaclite.c @@ -159,34 +159,32 @@ static void xemaclite_enable_interrupts(struct net_local *drvdata) u32 reg_data; /* Enable the Tx interrupts for the first Buffer */ - reg_data = in_be32(drvdata->base_addr + XEL_TSR_OFFSET); - out_be32(drvdata->base_addr + XEL_TSR_OFFSET, - reg_data | XEL_TSR_XMIT_IE_MASK); + reg_data = __raw_readl(drvdata->base_addr + XEL_TSR_OFFSET); + __raw_writel(reg_data | XEL_TSR_XMIT_IE_MASK, + drvdata->base_addr + XEL_TSR_OFFSET); /* Enable the Tx interrupts for the second Buffer if * configured in HW */ if (drvdata->tx_ping_pong != 0) { - reg_data = in_be32(drvdata->base_addr + + reg_data = __raw_readl(drvdata->base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET); - out_be32(drvdata->base_addr + XEL_BUFFER_OFFSET + - XEL_TSR_OFFSET, - reg_data | XEL_TSR_XMIT_IE_MASK); + __raw_writel(reg_data | XEL_TSR_XMIT_IE_MASK, + drvdata->base_addr + XEL_BUFFER_OFFSET + + XEL_TSR_OFFSET); } /* Enable the Rx interrupts for the first buffer */ - out_be32(drvdata->base_addr + XEL_RSR_OFFSET, - XEL_RSR_RECV_IE_MASK); + __raw_writel(XEL_RSR_RECV_IE_MASK, drvdata->base_addr + XEL_RSR_OFFSET); /* Enable the Rx interrupts for the second Buffer if * configured in HW */ if (drvdata->rx_ping_pong != 0) { - out_be32(drvdata->base_addr + XEL_BUFFER_OFFSET + - XEL_RSR_OFFSET, - XEL_RSR_RECV_IE_MASK); + __raw_writel(XEL_RSR_RECV_IE_MASK, drvdata->base_addr + + XEL_BUFFER_OFFSET + XEL_RSR_OFFSET); } /* Enable the Global Interrupt Enable */ - out_be32(drvdata->base_addr + XEL_GIER_OFFSET, XEL_GIER_GIE_MASK); + __raw_writel(XEL_GIER_GIE_MASK, drvdata->base_addr + XEL_GIER_OFFSET); } /** @@ -201,37 +199,37 @@ static void xemaclite_disable_interrupts(struct net_local *drvdata) u32 reg_data; /* Disable the Global Interrupt Enable */ - out_be32(drvdata->base_addr + XEL_GIER_OFFSET, XEL_GIER_GIE_MASK); + __raw_writel(XEL_GIER_GIE_MASK, drvdata->base_addr + XEL_GIER_OFFSET); /* Disable the Tx interrupts for the first buffer */ - reg_data = in_be32(drvdata->base_addr + XEL_TSR_OFFSET); - out_be32(drvdata->base_addr + XEL_TSR_OFFSET, - reg_data & (~XEL_TSR_XMIT_IE_MASK)); + reg_data = __raw_readl(drvdata->base_addr + XEL_TSR_OFFSET); + __raw_writel(reg_data & (~XEL_TSR_XMIT_IE_MASK), + drvdata->base_addr + XEL_TSR_OFFSET); /* Disable the Tx interrupts for the second Buffer * if configured in HW */ if (drvdata->tx_ping_pong != 0) { - reg_data = in_be32(drvdata->base_addr + XEL_BUFFER_OFFSET + + reg_data = __raw_readl(drvdata->base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET); - out_be32(drvdata->base_addr + XEL_BUFFER_OFFSET + - XEL_TSR_OFFSET, - reg_data & (~XEL_TSR_XMIT_IE_MASK)); + __raw_writel(reg_data & (~XEL_TSR_XMIT_IE_MASK), + drvdata->base_addr + XEL_BUFFER_OFFSET + + XEL_TSR_OFFSET); } /* Disable the Rx interrupts for the first buffer */ - reg_data = in_be32(drvdata->base_addr + XEL_RSR_OFFSET); - out_be32(drvdata->base_addr + XEL_RSR_OFFSET, - reg_data & (~XEL_RSR_RECV_IE_MASK)); + reg_data = __raw_readl(drvdata->base_addr + XEL_RSR_OFFSET); + __raw_writel(reg_data & (~XEL_RSR_RECV_IE_MASK), + drvdata->base_addr + XEL_RSR_OFFSET); /* Disable the Rx interrupts for the second buffer * if configured in HW */ if (drvdata->rx_ping_pong != 0) { - reg_data = in_be32(drvdata->base_addr + XEL_BUFFER_OFFSET + + reg_data = __raw_readl(drvdata->base_addr + XEL_BUFFER_OFFSET + XEL_RSR_OFFSET); - out_be32(drvdata->base_addr + XEL_BUFFER_OFFSET + - XEL_RSR_OFFSET, - reg_data & (~XEL_RSR_RECV_IE_MASK)); + __raw_writel(reg_data & (~XEL_RSR_RECV_IE_MASK), + drvdata->base_addr + XEL_BUFFER_OFFSET + + XEL_RSR_OFFSET); } } @@ -351,7 +349,7 @@ static int xemaclite_send_data(struct net_local *drvdata, u8 *data, byte_count = ETH_FRAME_LEN; /* Check if the expected buffer is available */ - reg_data = in_be32(addr + XEL_TSR_OFFSET); + reg_data = __raw_readl(addr + XEL_TSR_OFFSET); if ((reg_data & (XEL_TSR_XMIT_BUSY_MASK | XEL_TSR_XMIT_ACTIVE_MASK)) == 0) { @@ -364,7 +362,7 @@ static int xemaclite_send_data(struct net_local *drvdata, u8 *data, addr = (void __iomem __force *)((u32 __force)addr ^ XEL_BUFFER_OFFSET); - reg_data = in_be32(addr + XEL_TSR_OFFSET); + reg_data = __raw_readl(addr + XEL_TSR_OFFSET); if ((reg_data & (XEL_TSR_XMIT_BUSY_MASK | XEL_TSR_XMIT_ACTIVE_MASK)) != 0) @@ -375,15 +373,16 @@ static int xemaclite_send_data(struct net_local *drvdata, u8 *data, /* Write the frame to the buffer */ xemaclite_aligned_write(data, (u32 __force *) addr, byte_count); - out_be32(addr + XEL_TPLR_OFFSET, (byte_count & XEL_TPLR_LENGTH_MASK)); + __raw_writel((byte_count & XEL_TPLR_LENGTH_MASK), + addr + XEL_TPLR_OFFSET); /* Update the Tx Status Register to indicate that there is a * frame to send. Set the XEL_TSR_XMIT_ACTIVE_MASK flag which * is used by the interrupt handler to check whether a frame * has been transmitted */ - reg_data = in_be32(addr + XEL_TSR_OFFSET); + reg_data = __raw_readl(addr + XEL_TSR_OFFSET); reg_data |= (XEL_TSR_XMIT_BUSY_MASK | XEL_TSR_XMIT_ACTIVE_MASK); - out_be32(addr + XEL_TSR_OFFSET, reg_data); + __raw_writel(reg_data, addr + XEL_TSR_OFFSET); return 0; } @@ -408,7 +407,7 @@ static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data) addr = (drvdata->base_addr + drvdata->next_rx_buf_to_use); /* Verify which buffer has valid data */ - reg_data = in_be32(addr + XEL_RSR_OFFSET); + reg_data = __raw_readl(addr + XEL_RSR_OFFSET); if ((reg_data & XEL_RSR_RECV_DONE_MASK) == XEL_RSR_RECV_DONE_MASK) { if (drvdata->rx_ping_pong != 0) @@ -425,14 +424,14 @@ static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data) return 0; /* No data was available */ /* Verify that buffer has valid data */ - reg_data = in_be32(addr + XEL_RSR_OFFSET); + reg_data = __raw_readl(addr + XEL_RSR_OFFSET); if ((reg_data & XEL_RSR_RECV_DONE_MASK) != XEL_RSR_RECV_DONE_MASK) return 0; /* No data was available */ } /* Get the protocol type of the ethernet frame that arrived */ - proto_type = ((ntohl(in_be32(addr + XEL_HEADER_OFFSET + + proto_type = ((ntohl(__raw_readl(addr + XEL_HEADER_OFFSET + XEL_RXBUFF_OFFSET)) >> XEL_HEADER_SHIFT) & XEL_RPLR_LENGTH_MASK); @@ -441,7 +440,7 @@ 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 = ((ntohl(in_be32(addr + + length = ((ntohl(__raw_readl(addr + XEL_HEADER_IP_LENGTH_OFFSET + XEL_RXBUFF_OFFSET)) >> XEL_HEADER_SHIFT) & @@ -463,9 +462,9 @@ static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data) data, length); /* Acknowledge the frame */ - reg_data = in_be32(addr + XEL_RSR_OFFSET); + reg_data = __raw_readl(addr + XEL_RSR_OFFSET); reg_data &= ~XEL_RSR_RECV_DONE_MASK; - out_be32(addr + XEL_RSR_OFFSET, reg_data); + __raw_writel(reg_data, addr + XEL_RSR_OFFSET); return length; } @@ -492,14 +491,14 @@ static void xemaclite_update_address(struct net_local *drvdata, xemaclite_aligned_write(address_ptr, (u32 __force *) addr, ETH_ALEN); - out_be32(addr + XEL_TPLR_OFFSET, ETH_ALEN); + __raw_writel(ETH_ALEN, addr + XEL_TPLR_OFFSET); /* Update the MAC address in the EmacLite */ - reg_data = in_be32(addr + XEL_TSR_OFFSET); - out_be32(addr + XEL_TSR_OFFSET, reg_data | XEL_TSR_PROG_MAC_ADDR); + reg_data = __raw_readl(addr + XEL_TSR_OFFSET); + __raw_writel(reg_data | XEL_TSR_PROG_MAC_ADDR, addr + XEL_TSR_OFFSET); /* Wait for EmacLite to finish with the MAC address update */ - while ((in_be32(addr + XEL_TSR_OFFSET) & + while ((__raw_readl(addr + XEL_TSR_OFFSET) & XEL_TSR_PROG_MAC_ADDR) != 0) ; } @@ -669,31 +668,32 @@ static irqreturn_t xemaclite_interrupt(int irq, void *dev_id) u32 tx_status; /* Check if there is Rx Data available */ - if ((in_be32(base_addr + XEL_RSR_OFFSET) & XEL_RSR_RECV_DONE_MASK) || - (in_be32(base_addr + XEL_BUFFER_OFFSET + XEL_RSR_OFFSET) + if ((__raw_readl(base_addr + XEL_RSR_OFFSET) & + XEL_RSR_RECV_DONE_MASK) || + (__raw_readl(base_addr + XEL_BUFFER_OFFSET + XEL_RSR_OFFSET) & XEL_RSR_RECV_DONE_MASK)) xemaclite_rx_handler(dev); /* Check if the Transmission for the first buffer is completed */ - tx_status = in_be32(base_addr + XEL_TSR_OFFSET); + tx_status = __raw_readl(base_addr + XEL_TSR_OFFSET); if (((tx_status & XEL_TSR_XMIT_BUSY_MASK) == 0) && (tx_status & XEL_TSR_XMIT_ACTIVE_MASK) != 0) { tx_status &= ~XEL_TSR_XMIT_ACTIVE_MASK; - out_be32(base_addr + XEL_TSR_OFFSET, tx_status); + __raw_writel(tx_status, base_addr + XEL_TSR_OFFSET); tx_complete = true; } /* Check if the Transmission for the second buffer is completed */ - tx_status = in_be32(base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET); + tx_status = __raw_readl(base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET); if (((tx_status & XEL_TSR_XMIT_BUSY_MASK) == 0) && (tx_status & XEL_TSR_XMIT_ACTIVE_MASK) != 0) { tx_status &= ~XEL_TSR_XMIT_ACTIVE_MASK; - out_be32(base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET, - tx_status); + __raw_writel(tx_status, base_addr + XEL_BUFFER_OFFSET + + XEL_TSR_OFFSET); tx_complete = true; } @@ -726,7 +726,7 @@ static int xemaclite_mdio_wait(struct net_local *lp) /* wait for the MDIO interface to not be busy or timeout after some time. */ - while (in_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET) & + while (__raw_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET) & XEL_MDIOCTRL_MDIOSTS_MASK) { if (end - jiffies <= 0) { WARN_ON(1); @@ -762,17 +762,17 @@ static int xemaclite_mdio_read(struct mii_bus *bus, int phy_id, int reg) * MDIO Address register. Set the Status bit in the MDIO Control * register to start a MDIO read transaction. */ - ctrl_reg = in_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET); - out_be32(lp->base_addr + XEL_MDIOADDR_OFFSET, - XEL_MDIOADDR_OP_MASK | - ((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg)); - out_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET, - ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK); + ctrl_reg = __raw_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET); + __raw_writel(XEL_MDIOADDR_OP_MASK | + ((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg), + lp->base_addr + XEL_MDIOADDR_OFFSET); + __raw_writel(ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK, + lp->base_addr + XEL_MDIOCTRL_OFFSET); if (xemaclite_mdio_wait(lp)) return -ETIMEDOUT; - rc = in_be32(lp->base_addr + XEL_MDIORD_OFFSET); + rc = __raw_readl(lp->base_addr + XEL_MDIORD_OFFSET); dev_dbg(&lp->ndev->dev, "xemaclite_mdio_read(phy_id=%i, reg=%x) == %x\n", @@ -809,13 +809,13 @@ static int xemaclite_mdio_write(struct mii_bus *bus, int phy_id, int reg, * Data register. Finally, set the Status bit in the MDIO Control * register to start a MDIO write transaction. */ - ctrl_reg = in_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET); - out_be32(lp->base_addr + XEL_MDIOADDR_OFFSET, - ~XEL_MDIOADDR_OP_MASK & - ((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg)); - out_be32(lp->base_addr + XEL_MDIOWR_OFFSET, val); - out_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET, - ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK); + ctrl_reg = __raw_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET); + __raw_writel(~XEL_MDIOADDR_OP_MASK & + ((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg), + lp->base_addr + XEL_MDIOADDR_OFFSET); + __raw_writel(val, lp->base_addr + XEL_MDIOWR_OFFSET); + __raw_writel(ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK, + lp->base_addr + XEL_MDIOCTRL_OFFSET); return 0; } @@ -872,8 +872,8 @@ static int xemaclite_mdio_setup(struct net_local *lp, struct device *dev) /* Enable the MDIO bus by asserting the enable bit in MDIO Control * register. */ - out_be32(lp->base_addr + XEL_MDIOCTRL_OFFSET, - XEL_MDIOCTRL_MDIOEN_MASK); + __raw_writel(XEL_MDIOCTRL_MDIOEN_MASK, + lp->base_addr + XEL_MDIOCTRL_OFFSET); bus = mdiobus_alloc(); if (!bus) { @@ -1197,8 +1197,8 @@ static int xemaclite_of_probe(struct platform_device *ofdev) dev_warn(dev, "No MAC address found\n"); /* Clear the Tx CSR's in case this is a restart */ - out_be32(lp->base_addr + XEL_TSR_OFFSET, 0); - out_be32(lp->base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET, 0); + __raw_writel(0, lp->base_addr + XEL_TSR_OFFSET); + __raw_writel(0, lp->base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET); /* Set the MAC address in the EmacLite device */ xemaclite_update_address(lp, ndev->dev_addr); -- cgit v1.2.3 From 1156ee88dd3992bbacd5e4c659530f9d18c07378 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 30 May 2013 00:28:07 +0000 Subject: net: emaclite: Enable emaclite for Xilinx Arm Zynq platform Enable emaclite for Xilinx ARM Zynq platform. Signed-off-by: Michal Simek Signed-off-by: David S. Miller --- drivers/net/ethernet/xilinx/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/xilinx/Kconfig b/drivers/net/ethernet/xilinx/Kconfig index 122d60c0481b..7b90a5eba099 100644 --- a/drivers/net/ethernet/xilinx/Kconfig +++ b/drivers/net/ethernet/xilinx/Kconfig @@ -5,7 +5,7 @@ config NET_VENDOR_XILINX bool "Xilinx devices" default y - depends on PPC || PPC32 || MICROBLAZE + depends on PPC || PPC32 || MICROBLAZE || ARCH_ZYNQ ---help--- If you have a network (Ethernet) card belonging to this class, say Y and read the Ethernet-HOWTO, available from @@ -20,7 +20,7 @@ if NET_VENDOR_XILINX config XILINX_EMACLITE tristate "Xilinx 10/100 Ethernet Lite support" - depends on (PPC32 || MICROBLAZE) + depends on (PPC32 || MICROBLAZE || ARCH_ZYNQ) select PHYLIB ---help--- This driver supports the 10/100 Ethernet Lite from Xilinx. -- cgit v1.2.3 From 9e7c414350a6fa4e5f9b7e5f7e074fa75ba850c3 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 30 May 2013 00:28:08 +0000 Subject: net: emaclite: Update driver header Correct email address and years. Signed-off-by: Michal Simek Signed-off-by: David S. Miller --- drivers/net/ethernet/xilinx/xilinx_emaclite.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/xilinx/xilinx_emaclite.c b/drivers/net/ethernet/xilinx/xilinx_emaclite.c index 9227ed7bce10..aa14d8adcecb 100644 --- a/drivers/net/ethernet/xilinx/xilinx_emaclite.c +++ b/drivers/net/ethernet/xilinx/xilinx_emaclite.c @@ -2,9 +2,9 @@ * Xilinx EmacLite Linux driver for the Xilinx Ethernet MAC Lite device. * * This is a new flat driver which is based on the original emac_lite - * driver from John Williams . + * driver from John Williams . * - * 2007-2009 (c) Xilinx, Inc. + * 2007 - 2013 (c) Xilinx, 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 -- cgit v1.2.3 From cec753f5dd103fcfef28c1d1c942395000abe15e Mon Sep 17 00:00:00 2001 From: Sebastian Hesselbarth Date: Wed, 29 May 2013 09:32:43 +0000 Subject: net: mv643xx_eth: use phy_disconnect instead of phy_detach Using a separated mdio bus driver with mvmdio, phy_detach on network device removal will not stop the phy and finally lead to NULL pointer dereference in mvmdio due to non-existent network device. Use phy_disconnect instead to properly stop phy device from accessing network device prior removal of the network device. Signed-off-by: Sebastian Hesselbarth Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mv643xx_eth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index afb8bcbe29f8..8124464af83c 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -2805,7 +2805,7 @@ static int mv643xx_eth_remove(struct platform_device *pdev) unregister_netdev(mp->dev); if (mp->phy != NULL) - phy_detach(mp->phy); + phy_disconnect(mp->phy); cancel_work_sync(&mp->tx_timeout_task); if (!IS_ERR(mp->clk)) -- cgit v1.2.3 From 65a6f969fd1af549111b1667d9ca857e3cc649eb Mon Sep 17 00:00:00 2001 From: Sebastian Hesselbarth Date: Wed, 29 May 2013 09:32:44 +0000 Subject: net: mv643xx_eth: use managed devm_ioremap for port registers Make use of managed devm_ioremap and remove corresponding iounmap. Signed-off-by: Sebastian Hesselbarth Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mv643xx_eth.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index 8124464af83c..3417847b8485 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -2470,7 +2470,7 @@ static int mv643xx_eth_shared_probe(struct platform_device *pdev) if (msp == NULL) return -ENOMEM; - msp->base = ioremap(res->start, resource_size(res)); + msp->base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); if (msp->base == NULL) return -ENOMEM; @@ -2498,7 +2498,6 @@ static int mv643xx_eth_shared_remove(struct platform_device *pdev) { struct mv643xx_eth_shared_private *msp = platform_get_drvdata(pdev); - iounmap(msp->base); if (!IS_ERR(msp->clk)) clk_disable_unprepare(msp->clk); -- cgit v1.2.3 From 5f292354e7ff69afbe9a272ef997b8f3edaf860a Mon Sep 17 00:00:00 2001 From: Sebastian Hesselbarth Date: Wed, 29 May 2013 09:32:45 +0000 Subject: net: mv643xx_eth: add phy_node to platform_data struct This adds a struct device_node pointer for a phy passed by phandle to mv643xx_eth node. Signed-off-by: Sebastian Hesselbarth Signed-off-by: David S. Miller --- include/linux/mv643xx_eth.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/mv643xx_eth.h b/include/linux/mv643xx_eth.h index 141d395bbb5f..6e8215b15998 100644 --- a/include/linux/mv643xx_eth.h +++ b/include/linux/mv643xx_eth.h @@ -30,6 +30,7 @@ struct mv643xx_eth_shared_platform_data { #define MV643XX_ETH_PHY_ADDR(x) (0x80 | (x)) #define MV643XX_ETH_PHY_NONE 0xff +struct device_node; struct mv643xx_eth_platform_data { /* * Pointer back to our parent instance, and our port number. @@ -41,6 +42,7 @@ struct mv643xx_eth_platform_data { * Whether a PHY is present, and if yes, at which address. */ int phy_addr; + struct device_node *phy_node; /* * Use this MAC address if it is valid, overriding the -- cgit v1.2.3 From cc9d459894b0927d3f9280b07b308cec2576ad80 Mon Sep 17 00:00:00 2001 From: Sebastian Hesselbarth Date: Wed, 29 May 2013 09:32:46 +0000 Subject: net: mv643xx_eth: use of_phy_connect if phy_node present This connects to a phy node passed to the port device instead of probing the phy by phy_addr. Signed-off-by: Sebastian Hesselbarth Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mv643xx_eth.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index 3417847b8485..4f17d398a8ae 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -60,6 +60,7 @@ #include #include #include +#include static char mv643xx_eth_driver_name[] = "mv643xx_eth"; static char mv643xx_eth_driver_version[] = "1.4"; @@ -2715,17 +2716,27 @@ static int mv643xx_eth_probe(struct platform_device *pdev) netif_set_real_num_tx_queues(dev, mp->txq_count); netif_set_real_num_rx_queues(dev, mp->rxq_count); - if (pd->phy_addr != MV643XX_ETH_PHY_NONE) { + err = 0; + if (pd->phy_node) { + mp->phy = of_phy_connect(mp->dev, pd->phy_node, + mv643xx_eth_adjust_link, 0, + PHY_INTERFACE_MODE_GMII); + if (!mp->phy) + err = -ENODEV; + } else if (pd->phy_addr != MV643XX_ETH_PHY_NONE) { mp->phy = phy_scan(mp, pd->phy_addr); - if (IS_ERR(mp->phy)) { + if (IS_ERR(mp->phy)) err = PTR_ERR(mp->phy); - if (err == -ENODEV) - err = -EPROBE_DEFER; - goto out; - } - phy_init(mp, pd->speed, pd->duplex); + else + phy_init(mp, pd->speed, pd->duplex); } + if (err == -ENODEV) { + err = -EPROBE_DEFER; + goto out; + } + if (err) + goto out; SET_ETHTOOL_OPS(dev, &mv643xx_eth_ethtool_ops); -- cgit v1.2.3 From cb85215ffc34479188d0ec25a56352f05ad672ce Mon Sep 17 00:00:00 2001 From: Sebastian Hesselbarth Date: Wed, 29 May 2013 09:32:47 +0000 Subject: net: mv643xx_eth: proper initialization for Kirkwood SoCs Ethernet controllers found on Kirkwood SoCs not only suffer from loosing MAC address register contents on clock gating but also some important registers are reset to values that would break ethernet. This patch clears the CLK125_BYPASS_EN bit for DT enabled Kirkwood only by using of_device_is_compatible() instead of #ifdefs. Non-DT Kirkwood is not affected as it installs a clock gating workaround because of the MAC address issue above. Other Orion SoCs do not suffer from register reset, do not have the bit in question, or do not have the register at all. Moreover, system controllers on PPC using this driver should also be protected from clearing that bit. Signed-off-by: Sebastian Hesselbarth Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mv643xx_eth.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index 4f17d398a8ae..1b31d752ff90 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -116,6 +116,8 @@ static char mv643xx_eth_driver_version[] = "1.4"; #define LINK_UP 0x00000002 #define TXQ_COMMAND 0x0048 #define TXQ_FIX_PRIO_CONF 0x004c +#define PORT_SERIAL_CONTROL1 0x004c +#define CLK125_BYPASS_EN 0x00000010 #define TX_BW_RATE 0x0050 #define TX_BW_MTU 0x0058 #define TX_BW_BURST 0x005c @@ -2701,6 +2703,15 @@ static int mv643xx_eth_probe(struct platform_device *pdev) mp->dev = dev; + /* Kirkwood resets some registers on gated clocks. Especially + * CLK125_BYPASS_EN must be cleared but is not available on + * all other SoCs/System Controllers using this driver. + */ + if (of_device_is_compatible(pdev->dev.of_node, + "marvell,kirkwood-eth-port")) + wrlp(mp, PORT_SERIAL_CONTROL1, + rdlp(mp, PORT_SERIAL_CONTROL1) & ~CLK125_BYPASS_EN); + /* * Start with a default rate, and if there is a clock, allow * it to override the default. -- cgit v1.2.3 From 76723bca2802eb80990a5fefa179662e2e561d66 Mon Sep 17 00:00:00 2001 From: Sebastian Hesselbarth Date: Wed, 29 May 2013 09:32:48 +0000 Subject: net: mv643xx_eth: add DT parsing support This adds device tree parsing support for the shared driver of mv643xx_eth. As the bindings are slightly different from current PPC bindings new binding documentation is also added. Following PPC-style device setup, the shared driver now also adds port platform_devices and sets up port platform_data. Signed-off-by: Sebastian Hesselbarth Signed-off-by: David S. Miller --- .../devicetree/bindings/net/marvell-orion-net.txt | 85 ++++++++++++ drivers/net/ethernet/marvell/mv643xx_eth.c | 153 ++++++++++++++++++++- 2 files changed, 234 insertions(+), 4 deletions(-) create mode 100644 Documentation/devicetree/bindings/net/marvell-orion-net.txt diff --git a/Documentation/devicetree/bindings/net/marvell-orion-net.txt b/Documentation/devicetree/bindings/net/marvell-orion-net.txt new file mode 100644 index 000000000000..a73b79f227e1 --- /dev/null +++ b/Documentation/devicetree/bindings/net/marvell-orion-net.txt @@ -0,0 +1,85 @@ +Marvell Orion/Discovery ethernet controller +============================================= + +The Marvell Discovery ethernet controller can be found on Marvell Orion SoCs +(Kirkwood, Dove, Orion5x, and Discovery Innovation) and as part of Marvell +Discovery system controller chips (mv64[345]60). + +The Discovery ethernet controller is described with two levels of nodes. The +first level describes the ethernet controller itself and the second level +describes up to 3 ethernet port nodes within that controller. The reason for +the multiple levels is that the port registers are interleaved within a single +set of controller registers. Each port node describes port-specific properties. + +Note: The above separation is only true for Discovery system controllers. +For Orion SoCs we stick to the separation, although there each controller has +only one port associated. Multiple ports are implemented as multiple single-port +controllers. As Kirkwood has some issues with proper initialization after reset, +an extra compatible string is added for it. + +* Ethernet controller node + +Required controller properties: + - #address-cells: shall be 1. + - #size-cells: shall be 0. + - compatible: shall be one of "marvell,orion-eth", "marvell,kirkwood-eth". + - reg: address and length of the controller registers. + +Optional controller properties: + - clocks: phandle reference to the controller clock. + - marvell,tx-checksum-limit: max tx packet size for hardware checksum. + +* Ethernet port node + +Required port properties: + - device_type: shall be "network". + - compatible: shall be one of "marvell,orion-eth-port", + "marvell,kirkwood-eth-port". + - reg: port number relative to ethernet controller, shall be 0, 1, or 2. + - interrupts: port interrupt. + - local-mac-address: 6 bytes MAC address. + +Optional port properties: + - marvell,tx-queue-size: size of the transmit ring buffer. + - marvell,tx-sram-addr: address of transmit descriptor buffer located in SRAM. + - marvell,tx-sram-size: size of transmit descriptor buffer located in SRAM. + - marvell,rx-queue-size: size of the receive ring buffer. + - marvell,rx-sram-addr: address of receive descriptor buffer located in SRAM. + - marvell,rx-sram-size: size of receive descriptor buffer located in SRAM. + +and + + - phy-handle: phandle reference to ethernet PHY. + +or + + - speed: port speed if no PHY connected. + - duplex: port mode if no PHY connected. + +* Node example: + +mdio-bus { + ... + ethphy: ethernet-phy@8 { + device_type = "ethernet-phy"; + ... + }; +}; + +eth: ethernet-controller@72000 { + compatible = "marvell,orion-eth"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x72000 0x2000>; + clocks = <&gate_clk 2>; + marvell,tx-checksum-limit = <1600>; + + ethernet@0 { + device_type = "network"; + compatible = "marvell,orion-eth-port"; + reg = <0>; + interrupts = <29>; + phy-handle = <ðphy>; + local-mac-address = [00 00 00 00 00 00]; + }; +}; diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index 1b31d752ff90..23ea7b6e23f1 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -60,6 +60,9 @@ #include #include #include +#include +#include +#include #include static char mv643xx_eth_driver_name[] = "mv643xx_eth"; @@ -2453,13 +2456,148 @@ static void infer_hw_params(struct mv643xx_eth_shared_private *msp) } } +#if defined(CONFIG_OF) +static const struct of_device_id mv643xx_eth_shared_ids[] = { + { .compatible = "marvell,orion-eth", }, + { .compatible = "marvell,kirkwood-eth", }, + { } +}; +MODULE_DEVICE_TABLE(of, mv643xx_eth_shared_ids); +#endif + +#if defined(CONFIG_OF) && !defined(CONFIG_MV64X60) +#define mv643xx_eth_property(_np, _name, _v) \ + do { \ + u32 tmp; \ + if (!of_property_read_u32(_np, "marvell," _name, &tmp)) \ + _v = tmp; \ + } while (0) + +static struct platform_device *port_platdev[3]; + +static int mv643xx_eth_shared_of_add_port(struct platform_device *pdev, + struct device_node *pnp) +{ + struct platform_device *ppdev; + struct mv643xx_eth_platform_data ppd; + struct resource res; + const char *mac_addr; + int ret; + + memset(&ppd, 0, sizeof(ppd)); + ppd.shared = pdev; + + memset(&res, 0, sizeof(res)); + if (!of_irq_to_resource(pnp, 0, &res)) { + dev_err(&pdev->dev, "missing interrupt on %s\n", pnp->name); + return -EINVAL; + } + + if (of_property_read_u32(pnp, "reg", &ppd.port_number)) { + dev_err(&pdev->dev, "missing reg property on %s\n", pnp->name); + return -EINVAL; + } + + if (ppd.port_number >= 3) { + dev_err(&pdev->dev, "invalid reg property on %s\n", pnp->name); + return -EINVAL; + } + + mac_addr = of_get_mac_address(pnp); + if (mac_addr) + memcpy(ppd.mac_addr, mac_addr, 6); + + mv643xx_eth_property(pnp, "tx-queue-size", ppd.tx_queue_size); + mv643xx_eth_property(pnp, "tx-sram-addr", ppd.tx_sram_addr); + mv643xx_eth_property(pnp, "tx-sram-size", ppd.tx_sram_size); + mv643xx_eth_property(pnp, "rx-queue-size", ppd.rx_queue_size); + mv643xx_eth_property(pnp, "rx-sram-addr", ppd.rx_sram_addr); + mv643xx_eth_property(pnp, "rx-sram-size", ppd.rx_sram_size); + + ppd.phy_node = of_parse_phandle(pnp, "phy-handle", 0); + if (!ppd.phy_node) { + ppd.phy_addr = MV643XX_ETH_PHY_NONE; + of_property_read_u32(pnp, "speed", &ppd.speed); + of_property_read_u32(pnp, "duplex", &ppd.duplex); + } + + ppdev = platform_device_alloc(MV643XX_ETH_NAME, ppd.port_number); + if (!ppdev) + return -ENOMEM; + ppdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); + + ret = platform_device_add_resources(ppdev, &res, 1); + if (ret) + goto port_err; + + ret = platform_device_add_data(ppdev, &ppd, sizeof(ppd)); + if (ret) + goto port_err; + + ret = platform_device_add(ppdev); + if (ret) + goto port_err; + + port_platdev[ppd.port_number] = ppdev; + + return 0; + +port_err: + platform_device_put(ppdev); + return ret; +} + +static int mv643xx_eth_shared_of_probe(struct platform_device *pdev) +{ + struct mv643xx_eth_shared_platform_data *pd; + struct device_node *pnp, *np = pdev->dev.of_node; + int ret; + + /* bail out if not registered from DT */ + if (!np) + return 0; + + pd = devm_kzalloc(&pdev->dev, sizeof(*pd), GFP_KERNEL); + if (!pd) + return -ENOMEM; + pdev->dev.platform_data = pd; + + mv643xx_eth_property(np, "tx-checksum-limit", pd->tx_csum_limit); + + for_each_available_child_of_node(np, pnp) { + ret = mv643xx_eth_shared_of_add_port(pdev, pnp); + if (ret) + return ret; + } + return 0; +} + +static void mv643xx_eth_shared_of_remove(void) +{ + int n; + + for (n = 0; n < 3; n++) { + platform_device_del(port_platdev[n]); + port_platdev[n] = NULL; + } +} +#else +static int mv643xx_eth_shared_of_probe(struct platform_device *pdev) +{ + return 0 +} + +#define mv643xx_eth_shared_of_remove() +#endif + static int mv643xx_eth_shared_probe(struct platform_device *pdev) { static int mv643xx_eth_version_printed; - struct mv643xx_eth_shared_platform_data *pd = pdev->dev.platform_data; + struct mv643xx_eth_shared_platform_data *pd; struct mv643xx_eth_shared_private *msp; const struct mbus_dram_target_info *dram; struct resource *res; + int ret; if (!mv643xx_eth_version_printed++) pr_notice("MV-643xx 10/100/1000 ethernet driver version %s\n", @@ -2472,6 +2610,7 @@ static int mv643xx_eth_shared_probe(struct platform_device *pdev) msp = devm_kzalloc(&pdev->dev, sizeof(*msp), GFP_KERNEL); if (msp == NULL) return -ENOMEM; + platform_set_drvdata(pdev, msp); msp->base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); if (msp->base == NULL) @@ -2488,12 +2627,15 @@ static int mv643xx_eth_shared_probe(struct platform_device *pdev) if (dram) mv643xx_eth_conf_mbus_windows(msp, dram); + ret = mv643xx_eth_shared_of_probe(pdev); + if (ret) + return ret; + pd = pdev->dev.platform_data; + msp->tx_csum_limit = (pd != NULL && pd->tx_csum_limit) ? pd->tx_csum_limit : 9 * 1024; infer_hw_params(msp); - platform_set_drvdata(pdev, msp); - return 0; } @@ -2501,9 +2643,9 @@ static int mv643xx_eth_shared_remove(struct platform_device *pdev) { struct mv643xx_eth_shared_private *msp = platform_get_drvdata(pdev); + mv643xx_eth_shared_of_remove(); if (!IS_ERR(msp->clk)) clk_disable_unprepare(msp->clk); - return 0; } @@ -2513,6 +2655,7 @@ static struct platform_driver mv643xx_eth_shared_driver = { .driver = { .name = MV643XX_ETH_SHARED_NAME, .owner = THIS_MODULE, + .of_match_table = of_match_ptr(mv643xx_eth_shared_ids), }, }; @@ -2721,6 +2864,8 @@ static int mv643xx_eth_probe(struct platform_device *pdev) if (!IS_ERR(mp->clk)) { clk_prepare_enable(mp->clk); mp->t_clk = clk_get_rate(mp->clk); + } else if (!IS_ERR(mp->shared->clk)) { + mp->t_clk = clk_get_rate(mp->shared->clk); } set_params(mp, pd); -- cgit v1.2.3 From 6804973ffb4288bba14d53223e2fbb2bbd1d2e1b Mon Sep 17 00:00:00 2001 From: Yuchung Cheng Date: Wed, 29 May 2013 14:20:11 +0000 Subject: tcp: consolidate PRR packet accounting This patch series fixes an undo bug in fast recovery: the sender mistakenly undos the cwnd too early but continues fast retransmits until all pending data are acked. This also multiplies the SNMP stat PARTIALUNDO events by the degree of the network reordering. The first patch prepares the fix by consolidating the accounting of newly_acked_sacked in tcp_cwnd_reduction(), instead of updating newly_acked_sacked everytime sacked_out is adjusted. Also pass acked and prior_unsacked as const type because they are readonly in the rest of recovery processing. Signed-off-by: Yuchung Cheng Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- net/ipv4/tcp_input.c | 45 ++++++++++++++++++++------------------------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 9579e1a5a144..86b5fa72ff9e 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -2430,12 +2430,14 @@ static void tcp_init_cwnd_reduction(struct sock *sk, const bool set_ssthresh) TCP_ECN_queue_cwr(tp); } -static void tcp_cwnd_reduction(struct sock *sk, int newly_acked_sacked, +static void tcp_cwnd_reduction(struct sock *sk, const int prior_unsacked, int fast_rexmit) { struct tcp_sock *tp = tcp_sk(sk); int sndcnt = 0; int delta = tp->snd_ssthresh - tcp_packets_in_flight(tp); + int newly_acked_sacked = prior_unsacked - + (tp->packets_out - tp->sacked_out); tp->prr_delivered += newly_acked_sacked; if (tcp_packets_in_flight(tp) > tp->snd_ssthresh) { @@ -2492,7 +2494,7 @@ static void tcp_try_keep_open(struct sock *sk) } } -static void tcp_try_to_open(struct sock *sk, int flag, int newly_acked_sacked) +static void tcp_try_to_open(struct sock *sk, int flag, const int prior_unsacked) { struct tcp_sock *tp = tcp_sk(sk); @@ -2509,7 +2511,7 @@ static void tcp_try_to_open(struct sock *sk, int flag, int newly_acked_sacked) if (inet_csk(sk)->icsk_ca_state != TCP_CA_Open) tcp_moderate_cwnd(tp); } else { - tcp_cwnd_reduction(sk, newly_acked_sacked, 0); + tcp_cwnd_reduction(sk, prior_unsacked, 0); } } @@ -2678,15 +2680,14 @@ static void tcp_process_loss(struct sock *sk, int flag, bool is_dupack) * It does _not_ decide what to send, it is made in function * tcp_xmit_retransmit_queue(). */ -static void tcp_fastretrans_alert(struct sock *sk, int pkts_acked, - int prior_sacked, int prior_packets, +static void tcp_fastretrans_alert(struct sock *sk, const int acked, + const int prior_unsacked, bool is_dupack, int flag) { struct inet_connection_sock *icsk = inet_csk(sk); struct tcp_sock *tp = tcp_sk(sk); int do_lost = is_dupack || ((flag & FLAG_DATA_SACKED) && (tcp_fackets_out(tp) > tp->reordering)); - int newly_acked_sacked = 0; int fast_rexmit = 0; if (WARN_ON(!tp->packets_out && tp->sacked_out)) @@ -2739,9 +2740,7 @@ static void tcp_fastretrans_alert(struct sock *sk, int pkts_acked, if (tcp_is_reno(tp) && is_dupack) tcp_add_reno_sack(sk); } else - do_lost = tcp_try_undo_partial(sk, pkts_acked); - newly_acked_sacked = prior_packets - tp->packets_out + - tp->sacked_out - prior_sacked; + do_lost = tcp_try_undo_partial(sk, acked); break; case TCP_CA_Loss: tcp_process_loss(sk, flag, is_dupack); @@ -2755,14 +2754,12 @@ static void tcp_fastretrans_alert(struct sock *sk, int pkts_acked, if (is_dupack) tcp_add_reno_sack(sk); } - newly_acked_sacked = prior_packets - tp->packets_out + - tp->sacked_out - prior_sacked; if (icsk->icsk_ca_state <= TCP_CA_Disorder) tcp_try_undo_dsack(sk); if (!tcp_time_to_recover(sk, flag)) { - tcp_try_to_open(sk, flag, newly_acked_sacked); + tcp_try_to_open(sk, flag, prior_unsacked); return; } @@ -2784,7 +2781,7 @@ static void tcp_fastretrans_alert(struct sock *sk, int pkts_acked, if (do_lost) tcp_update_scoreboard(sk, fast_rexmit); - tcp_cwnd_reduction(sk, newly_acked_sacked, fast_rexmit); + tcp_cwnd_reduction(sk, prior_unsacked, fast_rexmit); tcp_xmit_retransmit_queue(sk); } @@ -3268,9 +3265,8 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) u32 prior_in_flight; u32 prior_fackets; int prior_packets = tp->packets_out; - int prior_sacked = tp->sacked_out; - int pkts_acked = 0; - int previous_packets_out = 0; + const int prior_unsacked = tp->packets_out - tp->sacked_out; + int acked = 0; /* Number of packets newly acked */ /* If the ack is older than previous acks * then we can probably ignore it. @@ -3345,18 +3341,17 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) goto no_queue; /* See if we can take anything off of the retransmit queue. */ - previous_packets_out = tp->packets_out; + acked = tp->packets_out; flag |= tcp_clean_rtx_queue(sk, prior_fackets, prior_snd_una); - - pkts_acked = previous_packets_out - tp->packets_out; + acked -= tp->packets_out; if (tcp_ack_is_dubious(sk, flag)) { /* Advance CWND, if state allows this. */ if ((flag & FLAG_DATA_ACKED) && tcp_may_raise_cwnd(sk, flag)) tcp_cong_avoid(sk, ack, prior_in_flight); is_dupack = !(flag & (FLAG_SND_UNA_ADVANCED | FLAG_NOT_DUP)); - tcp_fastretrans_alert(sk, pkts_acked, prior_sacked, - prior_packets, is_dupack, flag); + tcp_fastretrans_alert(sk, acked, prior_unsacked, + is_dupack, flag); } else { if (flag & FLAG_DATA_ACKED) tcp_cong_avoid(sk, ack, prior_in_flight); @@ -3378,8 +3373,8 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) no_queue: /* If data was DSACKed, see if we can undo a cwnd reduction. */ if (flag & FLAG_DSACKING_ACK) - tcp_fastretrans_alert(sk, pkts_acked, prior_sacked, - prior_packets, is_dupack, flag); + tcp_fastretrans_alert(sk, acked, prior_unsacked, + is_dupack, flag); /* If this ack opens up a zero window, clear backoff. It was * being used to time the probes, and is probably far higher than * it needs to be for normal retransmission. @@ -3401,8 +3396,8 @@ old_ack: */ if (TCP_SKB_CB(skb)->sacked) { flag |= tcp_sacktag_write_queue(sk, skb, prior_snd_una); - tcp_fastretrans_alert(sk, pkts_acked, prior_sacked, - prior_packets, is_dupack, flag); + tcp_fastretrans_alert(sk, acked, prior_unsacked, + is_dupack, flag); } SOCK_DEBUG(sk, "Ack %u before %u:%u\n", ack, tp->snd_una, tp->snd_nxt); -- cgit v1.2.3 From 6a63df46a7363833a0dc0c431027f522b3487972 Mon Sep 17 00:00:00 2001 From: Yuchung Cheng Date: Wed, 29 May 2013 14:20:12 +0000 Subject: tcp: refactor undo functions Refactor and relocate various functions or variables to prepare the undo fix. Remove some unused function arguments. Rename tcp_undo_cwr to tcp_undo_cwnd_reduction to be consistent with the rest of CWR related function names. Signed-off-by: Yuchung Cheng Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- net/ipv4/tcp_input.c | 97 +++++++++++++++++++++++++++------------------------- 1 file changed, 50 insertions(+), 47 deletions(-) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 86b5fa72ff9e..fcb668d1860d 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -2243,10 +2243,23 @@ static void DBGUNDO(struct sock *sk, const char *msg) #define DBGUNDO(x...) do { } while (0) #endif -static void tcp_undo_cwr(struct sock *sk, const bool undo_ssthresh) +static void tcp_undo_cwnd_reduction(struct sock *sk, const bool undo_ssthresh, + bool unmark_loss) { struct tcp_sock *tp = tcp_sk(sk); + if (unmark_loss) { + struct sk_buff *skb; + + tcp_for_write_queue(skb, sk) { + if (skb == tcp_send_head(sk)) + break; + TCP_SKB_CB(skb)->sacked &= ~TCPCB_LOST; + } + tp->lost_out = 0; + tcp_clear_all_retrans_hints(tp); + } + if (tp->prior_ssthresh) { const struct inet_connection_sock *icsk = inet_csk(sk); @@ -2263,6 +2276,9 @@ static void tcp_undo_cwr(struct sock *sk, const bool undo_ssthresh) tp->snd_cwnd = max(tp->snd_cwnd, tp->snd_ssthresh); } tp->snd_cwnd_stamp = tcp_time_stamp; + + if (undo_ssthresh) + tp->undo_marker = 0; } static inline bool tcp_may_undo(const struct tcp_sock *tp) @@ -2282,14 +2298,13 @@ static bool tcp_try_undo_recovery(struct sock *sk) * or our original transmission succeeded. */ DBGUNDO(sk, inet_csk(sk)->icsk_ca_state == TCP_CA_Loss ? "loss" : "retrans"); - tcp_undo_cwr(sk, true); + tcp_undo_cwnd_reduction(sk, true, false); if (inet_csk(sk)->icsk_ca_state == TCP_CA_Loss) mib_idx = LINUX_MIB_TCPLOSSUNDO; else mib_idx = LINUX_MIB_TCPFULLUNDO; NET_INC_STATS_BH(sock_net(sk), mib_idx); - tp->undo_marker = 0; } if (tp->snd_una == tp->high_seq && tcp_is_reno(tp)) { /* Hold old state until something *above* high_seq @@ -2309,8 +2324,7 @@ static void tcp_try_undo_dsack(struct sock *sk) if (tp->undo_marker && !tp->undo_retrans) { DBGUNDO(sk, "D-SACK"); - tcp_undo_cwr(sk, true); - tp->undo_marker = 0; + tcp_undo_cwnd_reduction(sk, true, false); NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPDSACKUNDO); } } @@ -2344,60 +2358,20 @@ static bool tcp_any_retrans_done(const struct sock *sk) return false; } -/* Undo during fast recovery after partial ACK. */ - -static int tcp_try_undo_partial(struct sock *sk, int acked) -{ - struct tcp_sock *tp = tcp_sk(sk); - /* Partial ACK arrived. Force Hoe's retransmit. */ - int failed = tcp_is_reno(tp) || (tcp_fackets_out(tp) > tp->reordering); - - if (tcp_may_undo(tp)) { - /* Plain luck! Hole if filled with delayed - * packet, rather than with a retransmit. - */ - if (!tcp_any_retrans_done(sk)) - tp->retrans_stamp = 0; - - tcp_update_reordering(sk, tcp_fackets_out(tp) + acked, 1); - - DBGUNDO(sk, "Hoe"); - tcp_undo_cwr(sk, false); - NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPPARTIALUNDO); - - /* So... Do not make Hoe's retransmit yet. - * If the first packet was delayed, the rest - * ones are most probably delayed as well. - */ - failed = 0; - } - return failed; -} - /* Undo during loss recovery after partial ACK or using F-RTO. */ static bool tcp_try_undo_loss(struct sock *sk, bool frto_undo) { struct tcp_sock *tp = tcp_sk(sk); if (frto_undo || tcp_may_undo(tp)) { - struct sk_buff *skb; - tcp_for_write_queue(skb, sk) { - if (skb == tcp_send_head(sk)) - break; - TCP_SKB_CB(skb)->sacked &= ~TCPCB_LOST; - } - - tcp_clear_all_retrans_hints(tp); + tcp_undo_cwnd_reduction(sk, true, true); DBGUNDO(sk, "partial loss"); - tp->lost_out = 0; - tcp_undo_cwr(sk, true); NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPLOSSUNDO); if (frto_undo) NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPSPURIOUSRTOS); inet_csk(sk)->icsk_retransmits = 0; - tp->undo_marker = 0; if (frto_undo || tcp_is_sack(tp)) tcp_set_ca_state(sk, TCP_CA_Open); return true; @@ -2669,6 +2643,35 @@ static void tcp_process_loss(struct sock *sk, int flag, bool is_dupack) tcp_xmit_retransmit_queue(sk); } +/* Undo during fast recovery after partial ACK. */ +static bool tcp_try_undo_partial(struct sock *sk, int acked) +{ + struct tcp_sock *tp = tcp_sk(sk); + /* Partial ACK arrived. Force Hoe's retransmit. */ + bool failed = tcp_is_reno(tp) || (tcp_fackets_out(tp) > tp->reordering); + + if (tcp_may_undo(tp)) { + /* Plain luck! Hole if filled with delayed + * packet, rather than with a retransmit. + */ + if (!tcp_any_retrans_done(sk)) + tp->retrans_stamp = 0; + + tcp_update_reordering(sk, tcp_fackets_out(tp) + acked, 1); + + DBGUNDO(sk, "Hoe"); + tcp_undo_cwnd_reduction(sk, false, false); + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPPARTIALUNDO); + + /* So... Do not make Hoe's retransmit yet. + * If the first packet was delayed, the rest + * ones are most probably delayed as well. + */ + failed = false; + } + return failed; +} + /* Process an event, which can update packets-in-flight not trivially. * Main goal of this function is to calculate new estimate for left_out, * taking into account both packets sitting in receiver's buffer and @@ -2686,7 +2689,7 @@ static void tcp_fastretrans_alert(struct sock *sk, const int acked, { struct inet_connection_sock *icsk = inet_csk(sk); struct tcp_sock *tp = tcp_sk(sk); - int do_lost = is_dupack || ((flag & FLAG_DATA_SACKED) && + bool do_lost = is_dupack || ((flag & FLAG_DATA_SACKED) && (tcp_fackets_out(tp) > tp->reordering)); int fast_rexmit = 0; -- cgit v1.2.3 From 7026b912f97d912476dff5465ed9a127be094208 Mon Sep 17 00:00:00 2001 From: Yuchung Cheng Date: Wed, 29 May 2013 14:20:13 +0000 Subject: tcp: fix undo on partial ack in recovery Upon detecting spurious fast retransmit via timestamps during recovery, use PRR to clock out new data packet instead of retransmission. Once all retransmission are proven spurious, the sender then reverts the cwnd reduction and congestion state to open or disorder. The current code does the opposite: it undoes cwnd as soon as any retransmission is spurious and continues to retransmit until all data are acked. This nullifies the point to undo the cwnd because the sender is still retransmistting spuriously. This patch fixes it. The undo_ssthresh argument of tcp_undo_cwnd_reductiuon() is no longer needed and is removed. Signed-off-by: Yuchung Cheng Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- net/ipv4/tcp_input.c | 59 +++++++++++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 26 deletions(-) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index fcb668d1860d..c35b22751982 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -2243,8 +2243,7 @@ static void DBGUNDO(struct sock *sk, const char *msg) #define DBGUNDO(x...) do { } while (0) #endif -static void tcp_undo_cwnd_reduction(struct sock *sk, const bool undo_ssthresh, - bool unmark_loss) +static void tcp_undo_cwnd_reduction(struct sock *sk, bool unmark_loss) { struct tcp_sock *tp = tcp_sk(sk); @@ -2268,7 +2267,7 @@ static void tcp_undo_cwnd_reduction(struct sock *sk, const bool undo_ssthresh, else tp->snd_cwnd = max(tp->snd_cwnd, tp->snd_ssthresh << 1); - if (undo_ssthresh && tp->prior_ssthresh > tp->snd_ssthresh) { + if (tp->prior_ssthresh > tp->snd_ssthresh) { tp->snd_ssthresh = tp->prior_ssthresh; TCP_ECN_withdraw_cwr(tp); } @@ -2276,9 +2275,7 @@ static void tcp_undo_cwnd_reduction(struct sock *sk, const bool undo_ssthresh, tp->snd_cwnd = max(tp->snd_cwnd, tp->snd_ssthresh); } tp->snd_cwnd_stamp = tcp_time_stamp; - - if (undo_ssthresh) - tp->undo_marker = 0; + tp->undo_marker = 0; } static inline bool tcp_may_undo(const struct tcp_sock *tp) @@ -2298,7 +2295,7 @@ static bool tcp_try_undo_recovery(struct sock *sk) * or our original transmission succeeded. */ DBGUNDO(sk, inet_csk(sk)->icsk_ca_state == TCP_CA_Loss ? "loss" : "retrans"); - tcp_undo_cwnd_reduction(sk, true, false); + tcp_undo_cwnd_reduction(sk, false); if (inet_csk(sk)->icsk_ca_state == TCP_CA_Loss) mib_idx = LINUX_MIB_TCPLOSSUNDO; else @@ -2324,7 +2321,7 @@ static void tcp_try_undo_dsack(struct sock *sk) if (tp->undo_marker && !tp->undo_retrans) { DBGUNDO(sk, "D-SACK"); - tcp_undo_cwnd_reduction(sk, true, false); + tcp_undo_cwnd_reduction(sk, false); NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPDSACKUNDO); } } @@ -2364,7 +2361,7 @@ static bool tcp_try_undo_loss(struct sock *sk, bool frto_undo) struct tcp_sock *tp = tcp_sk(sk); if (frto_undo || tcp_may_undo(tp)) { - tcp_undo_cwnd_reduction(sk, true, true); + tcp_undo_cwnd_reduction(sk, true); DBGUNDO(sk, "partial loss"); NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPLOSSUNDO); @@ -2644,32 +2641,37 @@ static void tcp_process_loss(struct sock *sk, int flag, bool is_dupack) } /* Undo during fast recovery after partial ACK. */ -static bool tcp_try_undo_partial(struct sock *sk, int acked) +static bool tcp_try_undo_partial(struct sock *sk, const int acked, + const int prior_unsacked) { struct tcp_sock *tp = tcp_sk(sk); - /* Partial ACK arrived. Force Hoe's retransmit. */ - bool failed = tcp_is_reno(tp) || (tcp_fackets_out(tp) > tp->reordering); - if (tcp_may_undo(tp)) { + if (tp->undo_marker && tcp_packet_delayed(tp)) { /* Plain luck! Hole if filled with delayed * packet, rather than with a retransmit. */ + tcp_update_reordering(sk, tcp_fackets_out(tp) + acked, 1); + + /* We are getting evidence that the reordering degree is higher + * than we realized. If there are no retransmits out then we + * can undo. Otherwise we clock out new packets but do not + * mark more packets lost or retransmit more. + */ + if (tp->retrans_out) { + tcp_cwnd_reduction(sk, prior_unsacked, 0); + return true; + } + if (!tcp_any_retrans_done(sk)) tp->retrans_stamp = 0; - tcp_update_reordering(sk, tcp_fackets_out(tp) + acked, 1); - - DBGUNDO(sk, "Hoe"); - tcp_undo_cwnd_reduction(sk, false, false); + DBGUNDO(sk, "partial recovery"); + tcp_undo_cwnd_reduction(sk, true); NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPPARTIALUNDO); - - /* So... Do not make Hoe's retransmit yet. - * If the first packet was delayed, the rest - * ones are most probably delayed as well. - */ - failed = false; + tcp_try_keep_open(sk); + return true; } - return failed; + return false; } /* Process an event, which can update packets-in-flight not trivially. @@ -2742,8 +2744,13 @@ static void tcp_fastretrans_alert(struct sock *sk, const int acked, if (!(flag & FLAG_SND_UNA_ADVANCED)) { if (tcp_is_reno(tp) && is_dupack) tcp_add_reno_sack(sk); - } else - do_lost = tcp_try_undo_partial(sk, acked); + } else { + if (tcp_try_undo_partial(sk, acked, prior_unsacked)) + return; + /* Partial ACK arrived. Force fast retransmit. */ + do_lost = tcp_is_reno(tp) || + tcp_fackets_out(tp) > tp->reordering; + } break; case TCP_CA_Loss: tcp_process_loss(sk, flag, is_dupack); -- cgit v1.2.3 From c7d9d6a185a7ea383b719b79c428d34ec1470275 Mon Sep 17 00:00:00 2001 From: Yuchung Cheng Date: Wed, 29 May 2013 14:20:14 +0000 Subject: tcp: undo on DSACK during recovery If the receiver supports DSACK, sender can detect false recoveries and revert cwnd reductions triggered by either severe network reordering or concurrent reordering and loss event. Signed-off-by: Yuchung Cheng Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- net/ipv4/tcp_input.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index c35b22751982..907311c9a012 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -2315,7 +2315,7 @@ static bool tcp_try_undo_recovery(struct sock *sk) } /* Try to undo cwnd reduction, because D-SACKs acked all retransmitted data */ -static void tcp_try_undo_dsack(struct sock *sk) +static bool tcp_try_undo_dsack(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); @@ -2323,7 +2323,9 @@ static void tcp_try_undo_dsack(struct sock *sk) DBGUNDO(sk, "D-SACK"); tcp_undo_cwnd_reduction(sk, false); NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPDSACKUNDO); + return true; } + return false; } /* We can clear retrans_stamp when there are no retransmissions in the @@ -2751,6 +2753,10 @@ static void tcp_fastretrans_alert(struct sock *sk, const int acked, do_lost = tcp_is_reno(tp) || tcp_fackets_out(tp) > tp->reordering; } + if (tcp_try_undo_dsack(sk)) { + tcp_try_keep_open(sk); + return; + } break; case TCP_CA_Loss: tcp_process_loss(sk, flag, is_dupack); -- cgit v1.2.3 From c0431037b4492e8c82b50f72ce127b74776433c2 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Fri, 24 May 2013 10:11:46 +0900 Subject: clk: use platform_{get,set}_drvdata() Use the wrapper functions for getting and setting the driver data using platform_device instead of using dev_{get,set}_drvdata() with &pdev->dev, so we can directly pass a struct platform_device. Signed-off-by: Jingoo Han Acked-by: Mark Brown Signed-off-by: Mike Turquette --- drivers/clk/clk-twl6040.c | 4 ++-- drivers/clk/clk-wm831x.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/clk/clk-twl6040.c b/drivers/clk/clk-twl6040.c index 3af729b1b89d..1ada79a28052 100644 --- a/drivers/clk/clk-twl6040.c +++ b/drivers/clk/clk-twl6040.c @@ -95,14 +95,14 @@ static int twl6040_clk_probe(struct platform_device *pdev) if (IS_ERR(clkdata->clk)) return PTR_ERR(clkdata->clk); - dev_set_drvdata(&pdev->dev, clkdata); + platform_set_drvdata(pdev, clkdata); return 0; } static int twl6040_clk_remove(struct platform_device *pdev) { - struct twl6040_clk *clkdata = dev_get_drvdata(&pdev->dev); + struct twl6040_clk *clkdata = platform_get_drvdata(pdev); clk_unregister(clkdata->clk); diff --git a/drivers/clk/clk-wm831x.c b/drivers/clk/clk-wm831x.c index 58d08a640275..1b3f8c9b98cc 100644 --- a/drivers/clk/clk-wm831x.c +++ b/drivers/clk/clk-wm831x.c @@ -384,7 +384,7 @@ static int wm831x_clk_probe(struct platform_device *pdev) if (IS_ERR(clkdata->clkout)) return PTR_ERR(clkdata->clkout); - dev_set_drvdata(&pdev->dev, clkdata); + platform_set_drvdata(pdev, clkdata); return 0; } -- cgit v1.2.3 From d0d98eedee2178c803dd824bb09f52b0e2ac1811 Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Mon, 13 May 2013 23:58:40 +0000 Subject: Add arch_phys_wc_{add, del} to manipulate WC MTRRs if needed Several drivers currently use mtrr_add through various #ifdef guards and/or drm wrappers. The vast majority of them want to add WC MTRRs on x86 systems and don't actually need the MTRR if PAT (i.e. ioremap_wc, etc) are working. arch_phys_wc_add and arch_phys_wc_del are new functions, available on all architectures and configurations, that add WC MTRRs on x86 if needed (and handle errors) and do nothing at all otherwise. They're also easier to use than mtrr_add and mtrr_del, so the call sites can be simplified. As an added benefit, this will avoid wasting MTRRs and possibly warning pointlessly on PAT-supporting systems. Reviewed-by: Daniel Vetter Signed-off-by: Andy Lutomirski Signed-off-by: Dave Airlie --- arch/x86/include/asm/io.h | 7 ++++ arch/x86/include/asm/mtrr.h | 10 +++++- arch/x86/kernel/cpu/mtrr/main.c | 71 +++++++++++++++++++++++++++++++++++++++++ include/linux/io.h | 25 +++++++++++++++ 4 files changed, 112 insertions(+), 1 deletion(-) diff --git a/arch/x86/include/asm/io.h b/arch/x86/include/asm/io.h index d8e8eefbe24c..34f69cb9350a 100644 --- a/arch/x86/include/asm/io.h +++ b/arch/x86/include/asm/io.h @@ -345,4 +345,11 @@ extern bool xen_biovec_phys_mergeable(const struct bio_vec *vec1, #define IO_SPACE_LIMIT 0xffff +#ifdef CONFIG_MTRR +extern int __must_check arch_phys_wc_add(unsigned long base, + unsigned long size); +extern void arch_phys_wc_del(int handle); +#define arch_phys_wc_add arch_phys_wc_add +#endif + #endif /* _ASM_X86_IO_H */ diff --git a/arch/x86/include/asm/mtrr.h b/arch/x86/include/asm/mtrr.h index e235582f9930..f768f6298419 100644 --- a/arch/x86/include/asm/mtrr.h +++ b/arch/x86/include/asm/mtrr.h @@ -26,7 +26,10 @@ #include -/* The following functions are for use by other drivers */ +/* + * The following functions are for use by other drivers that cannot use + * arch_phys_wc_add and arch_phys_wc_del. + */ # ifdef CONFIG_MTRR extern u8 mtrr_type_lookup(u64 addr, u64 end); extern void mtrr_save_fixed_ranges(void *); @@ -45,6 +48,7 @@ extern void mtrr_aps_init(void); extern void mtrr_bp_restore(void); extern int mtrr_trim_uncached_memory(unsigned long end_pfn); extern int amd_special_default_mtrr(void); +extern int phys_wc_to_mtrr_index(int handle); # else static inline u8 mtrr_type_lookup(u64 addr, u64 end) { @@ -80,6 +84,10 @@ static inline int mtrr_trim_uncached_memory(unsigned long end_pfn) static inline void mtrr_centaur_report_mcr(int mcr, u32 lo, u32 hi) { } +static inline int phys_wc_to_mtrr_index(int handle) +{ + return -1; +} #define mtrr_ap_init() do {} while (0) #define mtrr_bp_init() do {} while (0) diff --git a/arch/x86/kernel/cpu/mtrr/main.c b/arch/x86/kernel/cpu/mtrr/main.c index 726bf963c227..3533d4d16f8c 100644 --- a/arch/x86/kernel/cpu/mtrr/main.c +++ b/arch/x86/kernel/cpu/mtrr/main.c @@ -51,9 +51,13 @@ #include #include #include +#include #include "mtrr.h" +/* arch_phys_wc_add returns an MTRR register index plus this offset. */ +#define MTRR_TO_PHYS_WC_OFFSET 1000 + u32 num_var_ranges; unsigned int mtrr_usage_table[MTRR_MAX_VAR_RANGES]; @@ -524,6 +528,73 @@ int mtrr_del(int reg, unsigned long base, unsigned long size) } EXPORT_SYMBOL(mtrr_del); +/** + * arch_phys_wc_add - add a WC MTRR and handle errors if PAT is unavailable + * @base: Physical base address + * @size: Size of region + * + * If PAT is available, this does nothing. If PAT is unavailable, it + * attempts to add a WC MTRR covering size bytes starting at base and + * logs an error if this fails. + * + * Drivers must store the return value to pass to mtrr_del_wc_if_needed, + * but drivers should not try to interpret that return value. + */ +int arch_phys_wc_add(unsigned long base, unsigned long size) +{ + int ret; + + if (pat_enabled) + return 0; /* Success! (We don't need to do anything.) */ + + ret = mtrr_add(base, size, MTRR_TYPE_WRCOMB, true); + if (ret < 0) { + pr_warn("Failed to add WC MTRR for [%p-%p]; performance may suffer.", + (void *)base, (void *)(base + size - 1)); + return ret; + } + return ret + MTRR_TO_PHYS_WC_OFFSET; +} +EXPORT_SYMBOL(arch_phys_wc_add); + +/* + * arch_phys_wc_del - undoes arch_phys_wc_add + * @handle: Return value from arch_phys_wc_add + * + * This cleans up after mtrr_add_wc_if_needed. + * + * The API guarantees that mtrr_del_wc_if_needed(error code) and + * mtrr_del_wc_if_needed(0) do nothing. + */ +void arch_phys_wc_del(int handle) +{ + if (handle >= 1) { + WARN_ON(handle < MTRR_TO_PHYS_WC_OFFSET); + mtrr_del(handle - MTRR_TO_PHYS_WC_OFFSET, 0, 0); + } +} +EXPORT_SYMBOL(arch_phys_wc_del); + +/* + * phys_wc_to_mtrr_index - translates arch_phys_wc_add's return value + * @handle: Return value from arch_phys_wc_add + * + * This will turn the return value from arch_phys_wc_add into an mtrr + * index suitable for debugging. + * + * Note: There is no legitimate use for this function, except possibly + * in printk line. Alas there is an illegitimate use in some ancient + * drm ioctls. + */ +int phys_wc_to_mtrr_index(int handle) +{ + if (handle < MTRR_TO_PHYS_WC_OFFSET) + return -1; + else + return handle - MTRR_TO_PHYS_WC_OFFSET; +} +EXPORT_SYMBOL_GPL(phys_wc_to_mtrr_index); + /* * HACK ALERT! * These should be called implicitly, but we can't yet until all the initcall diff --git a/include/linux/io.h b/include/linux/io.h index 069e4075f872..f4f42faec686 100644 --- a/include/linux/io.h +++ b/include/linux/io.h @@ -76,4 +76,29 @@ void devm_ioremap_release(struct device *dev, void *res); #define arch_has_dev_port() (1) #endif +/* + * Some systems (x86 without PAT) have a somewhat reliable way to mark a + * physical address range such that uncached mappings will actually + * end up write-combining. This facility should be used in conjunction + * with pgprot_writecombine, ioremap-wc, or set_memory_wc, since it has + * no effect if the per-page mechanisms are functional. + * (On x86 without PAT, these functions manipulate MTRRs.) + * + * arch_phys_del_wc(0) or arch_phys_del_wc(any error code) is guaranteed + * to have no effect. + */ +#ifndef arch_phys_wc_add +static inline int __must_check arch_phys_wc_add(unsigned long base, + unsigned long size) +{ + return 0; /* It worked (i.e. did nothing). */ +} + +static inline void arch_phys_wc_del(int handle) +{ +} + +#define arch_phys_wc_add arch_phys_wc_add +#endif + #endif /* _LINUX_IO_H */ -- cgit v1.2.3 From 247d36d75128ba1f63702e0e6185d9a7a23ee5cb Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Mon, 13 May 2013 23:58:41 +0000 Subject: drm (ast, cirrus, mgag200, nouveau, savage, vmwgfx): Remove drm_mtrr_{add, del} This replaces drm_mtrr_{add,del} with arch_phys_wc_{add,del}. The interface is simplified (because the base and size parameters to drm_mtrr_del never did anything), and it no longer adds MTRRs on systems that don't need them. Reviewed-by: Daniel Vetter Signed-off-by: Andy Lutomirski Signed-off-by: Dave Airlie --- drivers/gpu/drm/ast/ast_ttm.c | 13 +++-------- drivers/gpu/drm/cirrus/cirrus_ttm.c | 15 ++++-------- drivers/gpu/drm/mgag200/mgag200_ttm.c | 14 ++++-------- drivers/gpu/drm/nouveau/nouveau_ttm.c | 13 ++++------- drivers/gpu/drm/savage/savage_bci.c | 43 ++++++++++++----------------------- drivers/gpu/drm/savage/savage_drv.h | 5 +--- drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 10 ++++---- include/drm/drmP.h | 29 ----------------------- 8 files changed, 35 insertions(+), 107 deletions(-) diff --git a/drivers/gpu/drm/ast/ast_ttm.c b/drivers/gpu/drm/ast/ast_ttm.c index 09da3393c527..a5a1a034033d 100644 --- a/drivers/gpu/drm/ast/ast_ttm.c +++ b/drivers/gpu/drm/ast/ast_ttm.c @@ -271,26 +271,19 @@ int ast_mm_init(struct ast_private *ast) return ret; } - ast->fb_mtrr = drm_mtrr_add(pci_resource_start(dev->pdev, 0), - pci_resource_len(dev->pdev, 0), - DRM_MTRR_WC); + ast->fb_mtrr = arch_phys_wc_add(pci_resource_start(dev->pdev, 0), + pci_resource_len(dev->pdev, 0)); return 0; } void ast_mm_fini(struct ast_private *ast) { - struct drm_device *dev = ast->dev; ttm_bo_device_release(&ast->ttm.bdev); ast_ttm_global_release(ast); - if (ast->fb_mtrr >= 0) { - drm_mtrr_del(ast->fb_mtrr, - pci_resource_start(dev->pdev, 0), - pci_resource_len(dev->pdev, 0), DRM_MTRR_WC); - ast->fb_mtrr = -1; - } + arch_phys_wc_del(ast->fb_mtrr); } void ast_ttm_placement(struct ast_bo *bo, int domain) diff --git a/drivers/gpu/drm/cirrus/cirrus_ttm.c b/drivers/gpu/drm/cirrus/cirrus_ttm.c index 2ed8cfc740c9..36b9b0bab1da 100644 --- a/drivers/gpu/drm/cirrus/cirrus_ttm.c +++ b/drivers/gpu/drm/cirrus/cirrus_ttm.c @@ -271,9 +271,8 @@ int cirrus_mm_init(struct cirrus_device *cirrus) return ret; } - cirrus->fb_mtrr = drm_mtrr_add(pci_resource_start(dev->pdev, 0), - pci_resource_len(dev->pdev, 0), - DRM_MTRR_WC); + cirrus->fb_mtrr = arch_phys_wc_add(pci_resource_start(dev->pdev, 0), + pci_resource_len(dev->pdev, 0)); cirrus->mm_inited = true; return 0; @@ -281,8 +280,6 @@ int cirrus_mm_init(struct cirrus_device *cirrus) void cirrus_mm_fini(struct cirrus_device *cirrus) { - struct drm_device *dev = cirrus->dev; - if (!cirrus->mm_inited) return; @@ -290,12 +287,8 @@ void cirrus_mm_fini(struct cirrus_device *cirrus) cirrus_ttm_global_release(cirrus); - if (cirrus->fb_mtrr >= 0) { - drm_mtrr_del(cirrus->fb_mtrr, - pci_resource_start(dev->pdev, 0), - pci_resource_len(dev->pdev, 0), DRM_MTRR_WC); - cirrus->fb_mtrr = -1; - } + arch_phys_wc_del(cirrus->fb_mtrr); + cirrus->fb_mtrr = 0; } void cirrus_ttm_placement(struct cirrus_bo *bo, int domain) diff --git a/drivers/gpu/drm/mgag200/mgag200_ttm.c b/drivers/gpu/drm/mgag200/mgag200_ttm.c index 401c9891d3a8..0004f7740b8e 100644 --- a/drivers/gpu/drm/mgag200/mgag200_ttm.c +++ b/drivers/gpu/drm/mgag200/mgag200_ttm.c @@ -270,26 +270,20 @@ int mgag200_mm_init(struct mga_device *mdev) return ret; } - mdev->fb_mtrr = drm_mtrr_add(pci_resource_start(dev->pdev, 0), - pci_resource_len(dev->pdev, 0), - DRM_MTRR_WC); + mdev->fb_mtrr = arch_phys_wc_add(pci_resource_start(dev->pdev, 0), + pci_resource_len(dev->pdev, 0)); return 0; } void mgag200_mm_fini(struct mga_device *mdev) { - struct drm_device *dev = mdev->dev; ttm_bo_device_release(&mdev->ttm.bdev); mgag200_ttm_global_release(mdev); - if (mdev->fb_mtrr >= 0) { - drm_mtrr_del(mdev->fb_mtrr, - pci_resource_start(dev->pdev, 0), - pci_resource_len(dev->pdev, 0), DRM_MTRR_WC); - mdev->fb_mtrr = -1; - } + arch_phys_wc_del(mdev->fb_mtrr); + mdev->fb_mtrr = 0; } void mgag200_ttm_placement(struct mgag200_bo *bo, int domain) diff --git a/drivers/gpu/drm/nouveau/nouveau_ttm.c b/drivers/gpu/drm/nouveau/nouveau_ttm.c index f19a15a3bc03..3da985eb38cc 100644 --- a/drivers/gpu/drm/nouveau/nouveau_ttm.c +++ b/drivers/gpu/drm/nouveau/nouveau_ttm.c @@ -396,9 +396,8 @@ nouveau_ttm_init(struct nouveau_drm *drm) return ret; } - drm->ttm.mtrr = drm_mtrr_add(pci_resource_start(dev->pdev, 1), - pci_resource_len(dev->pdev, 1), - DRM_MTRR_WC); + drm->ttm.mtrr = arch_phys_wc_add(pci_resource_start(dev->pdev, 1), + pci_resource_len(dev->pdev, 1)); /* GART init */ if (drm->agp.stat != ENABLED) { @@ -433,10 +432,6 @@ nouveau_ttm_fini(struct nouveau_drm *drm) nouveau_ttm_global_release(drm); - if (drm->ttm.mtrr >= 0) { - drm_mtrr_del(drm->ttm.mtrr, - pci_resource_start(drm->dev->pdev, 1), - pci_resource_len(drm->dev->pdev, 1), DRM_MTRR_WC); - drm->ttm.mtrr = -1; - } + arch_phys_wc_del(drm->ttm.mtrr); + drm->ttm.mtrr = 0; } diff --git a/drivers/gpu/drm/savage/savage_bci.c b/drivers/gpu/drm/savage/savage_bci.c index b55c1d661147..bd6b2cf508d5 100644 --- a/drivers/gpu/drm/savage/savage_bci.c +++ b/drivers/gpu/drm/savage/savage_bci.c @@ -570,9 +570,6 @@ int savage_driver_firstopen(struct drm_device *dev) unsigned int fb_rsrc, aper_rsrc; int ret = 0; - dev_priv->mtrr[0].handle = -1; - dev_priv->mtrr[1].handle = -1; - dev_priv->mtrr[2].handle = -1; if (S3_SAVAGE3D_SERIES(dev_priv->chipset)) { fb_rsrc = 0; fb_base = pci_resource_start(dev->pdev, 0); @@ -584,21 +581,14 @@ int savage_driver_firstopen(struct drm_device *dev) if (pci_resource_len(dev->pdev, 0) == 0x08000000) { /* Don't make MMIO write-cobining! We need 3 * MTRRs. */ - dev_priv->mtrr[0].base = fb_base; - dev_priv->mtrr[0].size = 0x01000000; - dev_priv->mtrr[0].handle = - drm_mtrr_add(dev_priv->mtrr[0].base, - dev_priv->mtrr[0].size, DRM_MTRR_WC); - dev_priv->mtrr[1].base = fb_base + 0x02000000; - dev_priv->mtrr[1].size = 0x02000000; - dev_priv->mtrr[1].handle = - drm_mtrr_add(dev_priv->mtrr[1].base, - dev_priv->mtrr[1].size, DRM_MTRR_WC); - dev_priv->mtrr[2].base = fb_base + 0x04000000; - dev_priv->mtrr[2].size = 0x04000000; - dev_priv->mtrr[2].handle = - drm_mtrr_add(dev_priv->mtrr[2].base, - dev_priv->mtrr[2].size, DRM_MTRR_WC); + dev_priv->mtrr_handles[0] = + arch_phys_wc_add(fb_base, 0x01000000); + dev_priv->mtrr_handles[1] = + arch_phys_wc_add(fb_base + 0x02000000, + 0x02000000); + dev_priv->mtrr_handles[2] = + arch_phys_wc_add(fb_base + 0x04000000, + 0x04000000); } else { DRM_ERROR("strange pci_resource_len %08llx\n", (unsigned long long) @@ -616,11 +606,9 @@ int savage_driver_firstopen(struct drm_device *dev) if (pci_resource_len(dev->pdev, 1) == 0x08000000) { /* Can use one MTRR to cover both fb and * aperture. */ - dev_priv->mtrr[0].base = fb_base; - dev_priv->mtrr[0].size = 0x08000000; - dev_priv->mtrr[0].handle = - drm_mtrr_add(dev_priv->mtrr[0].base, - dev_priv->mtrr[0].size, DRM_MTRR_WC); + dev_priv->mtrr_handles[0] = + arch_phys_wc_add(fb_base, + 0x08000000); } else { DRM_ERROR("strange pci_resource_len %08llx\n", (unsigned long long) @@ -660,11 +648,10 @@ void savage_driver_lastclose(struct drm_device *dev) drm_savage_private_t *dev_priv = dev->dev_private; int i; - for (i = 0; i < 3; ++i) - if (dev_priv->mtrr[i].handle >= 0) - drm_mtrr_del(dev_priv->mtrr[i].handle, - dev_priv->mtrr[i].base, - dev_priv->mtrr[i].size, DRM_MTRR_WC); + for (i = 0; i < 3; ++i) { + arch_phys_wc_del(dev_priv->mtrr_handles[i]); + dev_priv->mtrr_handles[i] = 0; + } } int savage_driver_unload(struct drm_device *dev) diff --git a/drivers/gpu/drm/savage/savage_drv.h b/drivers/gpu/drm/savage/savage_drv.h index df2aac6636f7..c05082a59f6f 100644 --- a/drivers/gpu/drm/savage/savage_drv.h +++ b/drivers/gpu/drm/savage/savage_drv.h @@ -160,10 +160,7 @@ typedef struct drm_savage_private { drm_local_map_t *cmd_dma; drm_local_map_t fake_dma; - struct { - int handle; - unsigned long base, size; - } mtrr[3]; + int mtrr_handles[3]; /* BCI and status-related stuff */ volatile uint32_t *status_ptr, *bci_ptr; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 07dfd823cc30..78e21649d48a 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -565,8 +565,8 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) dev_priv->has_gmr = false; } - dev_priv->mmio_mtrr = drm_mtrr_add(dev_priv->mmio_start, - dev_priv->mmio_size, DRM_MTRR_WC); + dev_priv->mmio_mtrr = arch_phys_wc_add(dev_priv->mmio_start, + dev_priv->mmio_size); dev_priv->mmio_virt = ioremap_wc(dev_priv->mmio_start, dev_priv->mmio_size); @@ -664,8 +664,7 @@ out_no_device: out_err4: iounmap(dev_priv->mmio_virt); out_err3: - drm_mtrr_del(dev_priv->mmio_mtrr, dev_priv->mmio_start, - dev_priv->mmio_size, DRM_MTRR_WC); + arch_phys_wc_del(dev_priv->mmio_mtrr); 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); @@ -709,8 +708,7 @@ static int vmw_driver_unload(struct drm_device *dev) ttm_object_device_release(&dev_priv->tdev); iounmap(dev_priv->mmio_virt); - drm_mtrr_del(dev_priv->mmio_mtrr, dev_priv->mmio_start, - dev_priv->mmio_size, DRM_MTRR_WC); + arch_phys_wc_del(dev_priv->mmio_mtrr); 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); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 63d17ee9eb48..5a87655508fc 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1250,37 +1250,8 @@ static inline int drm_core_has_MTRR(struct drm_device *dev) { return drm_core_check_feature(dev, DRIVER_USE_MTRR); } - -#define DRM_MTRR_WC MTRR_TYPE_WRCOMB - -static inline int drm_mtrr_add(unsigned long offset, unsigned long size, - unsigned int flags) -{ - return mtrr_add(offset, size, flags, 1); -} - -static inline int drm_mtrr_del(int handle, unsigned long offset, - unsigned long size, unsigned int flags) -{ - return mtrr_del(handle, offset, size); -} - #else #define drm_core_has_MTRR(dev) (0) - -#define DRM_MTRR_WC 0 - -static inline int drm_mtrr_add(unsigned long offset, unsigned long size, - unsigned int flags) -{ - return 0; -} - -static inline int drm_mtrr_del(int handle, unsigned long offset, - unsigned long size, unsigned int flags) -{ - return 0; -} #endif static inline void drm_device_set_unplugged(struct drm_device *dev) -- cgit v1.2.3 From ff47eaf24d01b5753e4964b10c606e0d711b143e Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Mon, 13 May 2013 23:58:42 +0000 Subject: drm: Update drm_addmap and drm_mmap to use PAT WC instead of MTRRs Previously, DRM_FRAME_BUFFER mappings, as well as DRM_REGISTERS mappings with DRM_WRITE_COMBINING set, resulted in an unconditional MTRR being added but the actual mappings being created as UC-. Now these mappings have the MTRR added only if needed, but they will be mapped with pgprot_writecombine. The non-WC DRM_REGISTERS case now uses pgprot_noncached instead of hardcoding the bit twiddling. The DRM_AGP case is unchanged for now. [airlied: fix ppc build] Reviewed-by: Daniel Vetter Signed-off-by: Andy Lutomirski Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_bufs.c | 17 +++++++++-------- drivers/gpu/drm/drm_vm.c | 25 ++++++++++++------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c index 0128147265f3..0190fce20078 100644 --- a/drivers/gpu/drm/drm_bufs.c +++ b/drivers/gpu/drm/drm_bufs.c @@ -210,12 +210,16 @@ static int drm_addmap_core(struct drm_device * dev, resource_size_t offset, if (drm_core_has_MTRR(dev)) { if (map->type == _DRM_FRAME_BUFFER || (map->flags & _DRM_WRITE_COMBINING)) { - map->mtrr = mtrr_add(map->offset, map->size, - MTRR_TYPE_WRCOMB, 1); + map->mtrr = + arch_phys_wc_add(map->offset, map->size); } } if (map->type == _DRM_REGISTERS) { - map->handle = ioremap(map->offset, map->size); + if (map->flags & _DRM_WRITE_COMBINING) + map->handle = ioremap_wc(map->offset, + map->size); + else + map->handle = ioremap(map->offset, map->size); if (!map->handle) { kfree(map); return -ENOMEM; @@ -451,11 +455,8 @@ int drm_rmmap_locked(struct drm_device *dev, struct drm_local_map *map) iounmap(map->handle); /* FALLTHROUGH */ case _DRM_FRAME_BUFFER: - if (drm_core_has_MTRR(dev) && map->mtrr >= 0) { - int retcode; - retcode = mtrr_del(map->mtrr, map->offset, map->size); - DRM_DEBUG("mtrr_del=%d\n", retcode); - } + if (drm_core_has_MTRR(dev)) + arch_phys_wc_del(map->mtrr); break; case _DRM_SHM: vfree(map->handle); diff --git a/drivers/gpu/drm/drm_vm.c b/drivers/gpu/drm/drm_vm.c index 1d4f7c9fe661..43e7825d3717 100644 --- a/drivers/gpu/drm/drm_vm.c +++ b/drivers/gpu/drm/drm_vm.c @@ -43,18 +43,22 @@ static void drm_vm_open(struct vm_area_struct *vma); static void drm_vm_close(struct vm_area_struct *vma); -static pgprot_t drm_io_prot(uint32_t map_type, struct vm_area_struct *vma) +static pgprot_t drm_io_prot(struct drm_local_map *map, + struct vm_area_struct *vma) { pgprot_t tmp = vm_get_page_prot(vma->vm_flags); #if defined(__i386__) || defined(__x86_64__) - if (boot_cpu_data.x86 > 3 && map_type != _DRM_AGP) { - pgprot_val(tmp) |= _PAGE_PCD; - pgprot_val(tmp) &= ~_PAGE_PWT; + if (map->type != _DRM_AGP) { + if (map->type == _DRM_FRAME_BUFFER || + map->flags & _DRM_WRITE_COMBINING) + tmp = pgprot_writecombine(tmp); + else + tmp = pgprot_noncached(tmp); } #elif defined(__powerpc__) pgprot_val(tmp) |= _PAGE_NO_CACHE; - if (map_type == _DRM_REGISTERS) + if (map->type == _DRM_REGISTERS) pgprot_val(tmp) |= _PAGE_GUARDED; #elif defined(__ia64__) if (efi_range_is_wc(vma->vm_start, vma->vm_end - @@ -250,13 +254,8 @@ static void drm_vm_shm_close(struct vm_area_struct *vma) switch (map->type) { case _DRM_REGISTERS: case _DRM_FRAME_BUFFER: - if (drm_core_has_MTRR(dev) && map->mtrr >= 0) { - int retcode; - retcode = mtrr_del(map->mtrr, - map->offset, - map->size); - DRM_DEBUG("mtrr_del = %d\n", retcode); - } + if (drm_core_has_MTRR(dev)) + arch_phys_wc_del(map->mtrr); iounmap(map->handle); break; case _DRM_SHM: @@ -618,7 +617,7 @@ int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma) case _DRM_REGISTERS: 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); + vma->vm_page_prot = drm_io_prot(map, vma); if (io_remap_pfn_range(vma, vma->vm_start, (map->offset + offset) >> PAGE_SHIFT, vma->vm_end - vma->vm_start, -- cgit v1.2.3 From f435046d38af631920b299455db9e95dfc06d055 Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Mon, 13 May 2013 23:58:43 +0000 Subject: drm, agpgart: Use pgprot_writecombine for AGP maps and make the MTRR optional I'm not sure I understand the intent of the previous behavior. mmap on /dev/agpgart and DRM_AGP maps had no cache flags set, so they would be fully cacheable. But the DRM code (most of the time) would add a write-combining MTRR that would change the effective memory type to WC. The new behavior just requests WC explicitly for all AGP maps. If there is any code out there that expects cacheable access to the AGP aperture (because the drm driver doesn't request an MTRR or because it's using /dev/agpgart directly), then it will now end up with a UC or WC mapping, depending on the architecture and PAT availability. But cacheable access to the aperture seems like it's asking for trouble, because, AIUI, the aperture is an alias of RAM. Reviewed-by: Daniel Vetter Signed-off-by: Andy Lutomirski Signed-off-by: Dave Airlie --- drivers/char/agp/frontend.c | 8 +++++--- drivers/gpu/drm/drm_pci.c | 8 ++++---- drivers/gpu/drm/drm_stub.c | 10 ++-------- drivers/gpu/drm/drm_vm.c | 11 ++++------- 4 files changed, 15 insertions(+), 22 deletions(-) diff --git a/drivers/char/agp/frontend.c b/drivers/char/agp/frontend.c index 2e044338753c..1b192395a90c 100644 --- a/drivers/char/agp/frontend.c +++ b/drivers/char/agp/frontend.c @@ -603,7 +603,8 @@ static int agp_mmap(struct file *file, struct vm_area_struct *vma) vma->vm_ops = kerninfo.vm_ops; } else if (io_remap_pfn_range(vma, vma->vm_start, (kerninfo.aper_base + offset) >> PAGE_SHIFT, - size, vma->vm_page_prot)) { + size, + pgprot_writecombine(vma->vm_page_prot))) { goto out_again; } mutex_unlock(&(agp_fe.agp_mutex)); @@ -618,8 +619,9 @@ static int agp_mmap(struct file *file, struct vm_area_struct *vma) if (kerninfo.vm_ops) { vma->vm_ops = kerninfo.vm_ops; } else if (io_remap_pfn_range(vma, vma->vm_start, - kerninfo.aper_base >> PAGE_SHIFT, - size, vma->vm_page_prot)) { + kerninfo.aper_base >> PAGE_SHIFT, + size, + pgprot_writecombine(vma->vm_page_prot))) { goto out_again; } mutex_unlock(&(agp_fe.agp_mutex)); diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index 14194b6ef644..80c0b2b29801 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -278,10 +278,10 @@ static int drm_pci_agp_init(struct drm_device *dev) } if (drm_core_has_MTRR(dev)) { if (dev->agp) - dev->agp->agp_mtrr = - mtrr_add(dev->agp->agp_info.aper_base, - dev->agp->agp_info.aper_size * - 1024 * 1024, MTRR_TYPE_WRCOMB, 1); + dev->agp->agp_mtrr = arch_phys_wc_add( + dev->agp->agp_info.aper_base, + dev->agp->agp_info.aper_size * + 1024 * 1024); } } return 0; diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index 16f3ec579b3b..577786ce9fbc 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -451,14 +451,8 @@ void drm_put_dev(struct drm_device *dev) drm_lastclose(dev); - if (drm_core_has_MTRR(dev) && drm_core_has_AGP(dev) && - dev->agp && dev->agp->agp_mtrr >= 0) { - int retval; - retval = mtrr_del(dev->agp->agp_mtrr, - dev->agp->agp_info.aper_base, - dev->agp->agp_info.aper_size * 1024 * 1024); - DRM_DEBUG("mtrr_del=%d\n", retval); - } + if (drm_core_has_MTRR(dev) && drm_core_has_AGP(dev) && dev->agp) + arch_phys_wc_del(dev->agp->agp_mtrr); if (dev->driver->unload) dev->driver->unload(dev); diff --git a/drivers/gpu/drm/drm_vm.c b/drivers/gpu/drm/drm_vm.c index 43e7825d3717..1575694ccaca 100644 --- a/drivers/gpu/drm/drm_vm.c +++ b/drivers/gpu/drm/drm_vm.c @@ -49,13 +49,10 @@ static pgprot_t drm_io_prot(struct drm_local_map *map, pgprot_t tmp = vm_get_page_prot(vma->vm_flags); #if defined(__i386__) || defined(__x86_64__) - if (map->type != _DRM_AGP) { - if (map->type == _DRM_FRAME_BUFFER || - map->flags & _DRM_WRITE_COMBINING) - tmp = pgprot_writecombine(tmp); - else - tmp = pgprot_noncached(tmp); - } + if (map->type == _DRM_REGISTERS && !(map->flags & _DRM_WRITE_COMBINING)) + tmp = pgprot_noncached(tmp); + else + tmp = pgprot_writecombine(tmp); #elif defined(__powerpc__) pgprot_val(tmp) |= _PAGE_NO_CACHE; if (map->type == _DRM_REGISTERS) -- cgit v1.2.3 From 1c0f6749e86a990eca10499c8da2b04888ce4560 Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Mon, 13 May 2013 23:58:44 +0000 Subject: i915: Use arch_phys_wc_{add,del} i915 open-coded logic that was essentially equivalent to the new API. Reviewed-by: Daniel Vetter Signed-off-by: Andy Lutomirski Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/i915_dma.c | 42 ++++------------------------------------- 1 file changed, 4 insertions(+), 38 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index f5addac7c154..b76da1470e71 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -42,7 +42,6 @@ #include #include #include -#include #define LP_RING(d) (&((struct drm_i915_private *)(d))->ring[RCS]) @@ -1397,29 +1396,6 @@ void i915_master_destroy(struct drm_device *dev, struct drm_master *master) master->driver_priv = NULL; } -static void -i915_mtrr_setup(struct drm_i915_private *dev_priv, unsigned long base, - unsigned long size) -{ - dev_priv->mm.gtt_mtrr = -1; - -#if defined(CONFIG_X86_PAT) - if (cpu_has_pat) - return; -#endif - - /* Set up a WC MTRR for non-PAT systems. This is more common than - * one would think, because the kernel disables PAT on first - * generation Core chips because WC PAT gets overridden by a UC - * MTRR if present. Even if a UC MTRR isn't present. - */ - dev_priv->mm.gtt_mtrr = mtrr_add(base, size, MTRR_TYPE_WRCOMB, 1); - if (dev_priv->mm.gtt_mtrr < 0) { - DRM_INFO("MTRR allocation failed. Graphics " - "performance may suffer.\n"); - } -} - static void i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv) { struct apertures_struct *ap; @@ -1578,8 +1554,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) goto out_rmmap; } - i915_mtrr_setup(dev_priv, dev_priv->gtt.mappable_base, - aperture_size); + dev_priv->mm.gtt_mtrr = arch_phys_wc_add(dev_priv->gtt.mappable_base, + aperture_size); /* The i915 workqueue is primarily used for batched retirement of * requests (and thus managing bo) once the task has been completed @@ -1684,12 +1660,7 @@ out_gem_unload: intel_teardown_mchbar(dev); destroy_workqueue(dev_priv->wq); out_mtrrfree: - if (dev_priv->mm.gtt_mtrr >= 0) { - mtrr_del(dev_priv->mm.gtt_mtrr, - dev_priv->gtt.mappable_base, - aperture_size); - dev_priv->mm.gtt_mtrr = -1; - } + arch_phys_wc_del(dev_priv->mm.gtt_mtrr); io_mapping_free(dev_priv->gtt.mappable); dev_priv->gtt.gtt_remove(dev); out_rmmap: @@ -1724,12 +1695,7 @@ int i915_driver_unload(struct drm_device *dev) cancel_delayed_work_sync(&dev_priv->mm.retire_work); io_mapping_free(dev_priv->gtt.mappable); - if (dev_priv->mm.gtt_mtrr >= 0) { - mtrr_del(dev_priv->mm.gtt_mtrr, - dev_priv->gtt.mappable_base, - dev_priv->gtt.mappable_end); - dev_priv->mm.gtt_mtrr = -1; - } + arch_phys_wc_del(dev_priv->mm.gtt_mtrr); acpi_video_unregister(); -- cgit v1.2.3 From 07ebea251d08ae851d501f7402359f053d038862 Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Mon, 13 May 2013 23:58:45 +0000 Subject: radeon: Switch to arch_phys_wc_add and add a missing ..._del Reviewed-by: Daniel Vetter Signed-off-by: Andy Lutomirski Reviewed-by: Alex Deucher Signed-off-by: Dave Airlie --- drivers/gpu/drm/radeon/radeon_object.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c index 1424ccde2377..07af5a95bb62 100644 --- a/drivers/gpu/drm/radeon/radeon_object.c +++ b/drivers/gpu/drm/radeon/radeon_object.c @@ -322,8 +322,8 @@ int radeon_bo_init(struct radeon_device *rdev) { /* Add an MTRR for the VRAM */ if (!rdev->fastfb_working) { - rdev->mc.vram_mtrr = mtrr_add(rdev->mc.aper_base, rdev->mc.aper_size, - MTRR_TYPE_WRCOMB, 1); + rdev->mc.vram_mtrr = arch_phys_wc_add(rdev->mc.aper_base, + rdev->mc.aper_size); } DRM_INFO("Detected VRAM RAM=%lluM, BAR=%lluM\n", rdev->mc.mc_vram_size >> 20, @@ -336,6 +336,7 @@ int radeon_bo_init(struct radeon_device *rdev) void radeon_bo_fini(struct radeon_device *rdev) { radeon_ttm_fini(rdev); + arch_phys_wc_del(rdev->mc.vram_mtrr); } void radeon_bo_list_add_object(struct radeon_bo_list *lobj, -- cgit v1.2.3 From 63e28a7a5ffce59b645ca9cbcc01e1e8be56bd75 Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Mon, 13 May 2013 23:58:46 +0000 Subject: uvesafb: Clean up MTRR code The old code allowed very strange memory types. Now it works like all the other video drivers: ioremap_wc is used unconditionally, and MTRRs are set if PAT is unavailable (unless MTRR is disabled by a module parameter). UC, WB, and WT support is gone. If there are MTRR conflicts that prevent addition of a WC MTRR, adding a non-conflicting MTRR is pointless; it's better to just turn off MTRR support entirely. As an added bonus, any MTRR added is freed on unload. Reviewed-by: Daniel Vetter Signed-off-by: Andy Lutomirski Signed-off-by: Dave Airlie --- Documentation/fb/uvesafb.txt | 16 ++++------ drivers/video/uvesafb.c | 70 +++++++++++--------------------------------- include/video/uvesafb.h | 1 + 3 files changed, 23 insertions(+), 64 deletions(-) diff --git a/Documentation/fb/uvesafb.txt b/Documentation/fb/uvesafb.txt index eefdd91d298a..f6362d88763b 100644 --- a/Documentation/fb/uvesafb.txt +++ b/Documentation/fb/uvesafb.txt @@ -81,17 +81,11 @@ pmipal Use the protected mode interface for palette changes. mtrr:n Setup memory type range registers for the framebuffer where n: - 0 - disabled (equivalent to nomtrr) (default) - 1 - uncachable - 2 - write-back - 3 - write-combining - 4 - write-through - - If you see the following in dmesg, choose the type that matches - the old one. In this example, use "mtrr:2". -... -mtrr: type mismatch for e0000000,8000000 old: write-back new: write-combining -... + 0 - disabled (equivalent to nomtrr) + 3 - write-combining (default) + + Values other than 0 and 3 will result in a warning and will be + treated just like 3. nomtrr Do not use memory type range registers. diff --git a/drivers/video/uvesafb.c b/drivers/video/uvesafb.c index e328a61b64ba..296279bc71d2 100644 --- a/drivers/video/uvesafb.c +++ b/drivers/video/uvesafb.c @@ -24,9 +24,6 @@ #ifdef CONFIG_X86 #include Width of the buffer has to be aligned to the multiple of 128, and -height alignment is 32. Every four adjactent buffers - two horizontally and two +height alignment is 32. Every four adjacent buffers - two horizontally and two vertically are grouped together and are located in memory in Z or flipped Z order. Layout of macroblocks in memory is presented in the following -- cgit v1.2.3 From 3d9738aa41bb746bbf963c3260b042efc24e5e72 Mon Sep 17 00:00:00 2001 From: Stefan Huber Date: Wed, 5 Jun 2013 12:24:34 +0200 Subject: Documentation/networking/ieee802154 fix a typo Corrected the words Accsess to Access. Signed-off-by: Stefan Huber Acked-by: Randy Dunlap Signed-off-by: Jiri Kosina --- Documentation/networking/ieee802154.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/networking/ieee802154.txt b/Documentation/networking/ieee802154.txt index 67a9cb259d40..09eb57329f11 100644 --- a/Documentation/networking/ieee802154.txt +++ b/Documentation/networking/ieee802154.txt @@ -5,7 +5,7 @@ Introduction ============ The IEEE 802.15.4 working group focuses on standartization of bottom -two layers: Medium Accsess Control (MAC) and Physical (PHY). And there +two layers: Medium Access Control (MAC) and Physical (PHY). And there are mainly two options available for upper layers: - ZigBee - proprietary protocol from ZigBee Alliance - 6LowPAN - IPv6 networking over low rate personal area networks -- cgit v1.2.3 From 6d1ee2cef617f27d8b737871784d8cd23e3fd9e9 Mon Sep 17 00:00:00 2001 From: Stefan Huber Date: Wed, 5 Jun 2013 12:24:39 +0200 Subject: Documentation/arm/IXP4xx fix a typo Corrected the word softare to software. Signed-off-by: Stefan Huber Acked-by: Randy Dunlap Signed-off-by: Jiri Kosina --- Documentation/arm/IXP4xx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/arm/IXP4xx b/Documentation/arm/IXP4xx index 7b9351f2f555..e48b74de6ac0 100644 --- a/Documentation/arm/IXP4xx +++ b/Documentation/arm/IXP4xx @@ -36,7 +36,7 @@ Linux currently supports the following features on the IXP4xx chips: - Timers (watchdog, OS) The following components of the chips are not supported by Linux and -require the use of Intel's proprietary CSR softare: +require the use of Intel's proprietary CSR software: - USB device interface - Network interfaces (HSS, Utopia, NPEs, etc) -- cgit v1.2.3 From 6dc6a3f81ee66b50acee5f1aa1de2f7d2d4e55fa Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 5 Jun 2013 14:51:31 +0100 Subject: ASoC: arizona: Hookup SYSCLK to inputs and noise generators All sources and sinks should enable SYSCLK. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm5102.c | 11 +++++++++++ sound/soc/codecs/wm5110.c | 13 +++++++++++++ 2 files changed, 24 insertions(+) diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index 9a186ff22b55..842adfdcb2a9 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -1388,10 +1388,21 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = { { "OUT5L", NULL, "SYSCLK" }, { "OUT5R", NULL, "SYSCLK" }, + { "IN1L", NULL, "SYSCLK" }, + { "IN1R", NULL, "SYSCLK" }, + { "IN2L", NULL, "SYSCLK" }, + { "IN2R", NULL, "SYSCLK" }, + { "IN3L", NULL, "SYSCLK" }, + { "IN3R", NULL, "SYSCLK" }, + { "MICBIAS1", NULL, "MICVDD" }, { "MICBIAS2", NULL, "MICVDD" }, { "MICBIAS3", NULL, "MICVDD" }, + { "Noise Generator", NULL, "SYSCLK" }, + { "Tone Generator 1", NULL, "SYSCLK" }, + { "Tone Generator 2", NULL, "SYSCLK" }, + { "Noise Generator", NULL, "NOISE" }, { "Tone Generator 1", NULL, "TONE" }, { "Tone Generator 2", NULL, "TONE" }, diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index f53062f8c42e..17fdd9dbb17a 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -820,10 +820,23 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = { { "OUT6L", NULL, "SYSCLK" }, { "OUT6R", NULL, "SYSCLK" }, + { "IN1L", NULL, "SYSCLK" }, + { "IN1R", NULL, "SYSCLK" }, + { "IN2L", NULL, "SYSCLK" }, + { "IN2R", NULL, "SYSCLK" }, + { "IN3L", NULL, "SYSCLK" }, + { "IN3R", NULL, "SYSCLK" }, + { "IN4L", NULL, "SYSCLK" }, + { "IN4R", NULL, "SYSCLK" }, + { "MICBIAS1", NULL, "MICVDD" }, { "MICBIAS2", NULL, "MICVDD" }, { "MICBIAS3", NULL, "MICVDD" }, + { "Noise Generator", NULL, "SYSCLK" }, + { "Tone Generator 1", NULL, "SYSCLK" }, + { "Tone Generator 2", NULL, "SYSCLK" }, + { "Noise Generator", NULL, "NOISE" }, { "Tone Generator 1", NULL, "TONE" }, { "Tone Generator 2", NULL, "TONE" }, -- cgit v1.2.3 From bf7c6e6ccbde22c96c5c1e5cec08740c31229df1 Mon Sep 17 00:00:00 2001 From: Barry Song <21cnbao@gmail.com> Date: Thu, 16 May 2013 14:08:07 +0800 Subject: ASoC: omap-hdmi-codec: make the driver common for other SoCs to support HDMI on CSR SiRFprimaII and atlasVI, we need one more HDMI pseudo codec, rather than add a new driver, we can make omap HDMI codec common for other SoCs as well. then the omap-hdmi codec becomes a generic HDMI pseudo- codec as HDMI audio features depend on HDMI specification not on SoCs. Signed-off-by: Barry Song Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 4 +-- sound/soc/codecs/Makefile | 4 +-- sound/soc/codecs/hdmi.c | 69 +++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/omap-hdmi.c | 69 ----------------------------------------- sound/soc/omap/omap-hdmi-card.c | 2 +- 5 files changed, 74 insertions(+), 74 deletions(-) create mode 100644 sound/soc/codecs/hdmi.c delete mode 100644 sound/soc/codecs/omap-hdmi.c diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 2f45f00e31b0..d8c4f3dcf4a1 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -53,7 +53,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_MAX9877 if I2C select SND_SOC_MC13783 if MFD_MC13XXX select SND_SOC_ML26124 if I2C - select SND_SOC_OMAP_HDMI_CODEC if OMAP4_DSS_HDMI + select SND_SOC_HDMI_CODEC select SND_SOC_PCM3008 select SND_SOC_RT5631 if I2C select SND_SOC_SGTL5000 if I2C @@ -287,7 +287,7 @@ config SND_SOC_MAX98095 config SND_SOC_MAX9850 tristate -config SND_SOC_OMAP_HDMI_CODEC +config SND_SOC_HDMI_CODEC tristate config SND_SOC_PCM3008 diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index b9e41c9a1f4c..49ff12718bed 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -41,7 +41,7 @@ snd-soc-max98095-objs := max98095.o snd-soc-max9850-objs := max9850.o snd-soc-mc13783-objs := mc13783.o snd-soc-ml26124-objs := ml26124.o -snd-soc-omap-hdmi-codec-objs := omap-hdmi.o +snd-soc-hdmi-codec-objs := hdmi.o snd-soc-pcm3008-objs := pcm3008.o snd-soc-rt5631-objs := rt5631.o snd-soc-sgtl5000-objs := sgtl5000.o @@ -168,7 +168,7 @@ obj-$(CONFIG_SND_SOC_MAX98095) += snd-soc-max98095.o obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o -obj-$(CONFIG_SND_SOC_OMAP_HDMI_CODEC) += snd-soc-omap-hdmi-codec.o +obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o diff --git a/sound/soc/codecs/hdmi.c b/sound/soc/codecs/hdmi.c new file mode 100644 index 000000000000..2bcae2b40c92 --- /dev/null +++ b/sound/soc/codecs/hdmi.c @@ -0,0 +1,69 @@ +/* + * ALSA SoC codec driver for HDMI audio codecs. + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Ricardo Neri + * + * 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 +#include + +#define DRV_NAME "hdmi-audio-codec" + +static struct snd_soc_codec_driver hdmi_codec; + +static struct snd_soc_dai_driver hdmi_codec_dai = { + .name = "hdmi-hifi", + .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 hdmi_codec_probe(struct platform_device *pdev) +{ + return snd_soc_register_codec(&pdev->dev, &hdmi_codec, + &hdmi_codec_dai, 1); +} + +static int hdmi_codec_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + return 0; +} + +static struct platform_driver hdmi_codec_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, + + .probe = hdmi_codec_probe, + .remove = hdmi_codec_remove, +}; + +module_platform_driver(hdmi_codec_driver); + +MODULE_AUTHOR("Ricardo Neri "); +MODULE_DESCRIPTION("ASoC generic HDMI codec driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/codecs/omap-hdmi.c b/sound/soc/codecs/omap-hdmi.c deleted file mode 100644 index 529d06444c54..000000000000 --- a/sound/soc/codecs/omap-hdmi.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - * ALSA SoC codec driver for HDMI audio on OMAP processors. - * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ - * Author: Ricardo Neri - * - * 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 -#include - -#define DRV_NAME "hdmi-audio-codec" - -static struct snd_soc_codec_driver omap_hdmi_codec; - -static struct snd_soc_dai_driver omap_hdmi_codec_dai = { - .name = "omap-hdmi-hifi", - .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 omap_hdmi_codec_probe(struct platform_device *pdev) -{ - return snd_soc_register_codec(&pdev->dev, &omap_hdmi_codec, - &omap_hdmi_codec_dai, 1); -} - -static int omap_hdmi_codec_remove(struct platform_device *pdev) -{ - snd_soc_unregister_codec(&pdev->dev); - return 0; -} - -static struct platform_driver omap_hdmi_codec_driver = { - .driver = { - .name = DRV_NAME, - .owner = THIS_MODULE, - }, - - .probe = omap_hdmi_codec_probe, - .remove = omap_hdmi_codec_remove, -}; - -module_platform_driver(omap_hdmi_codec_driver); - -MODULE_AUTHOR("Ricardo Neri "); -MODULE_DESCRIPTION("ASoC OMAP HDMI codec driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/omap/omap-hdmi-card.c b/sound/soc/omap/omap-hdmi-card.c index d4eaa92e518e..7e66e9cba5a8 100644 --- a/sound/soc/omap/omap-hdmi-card.c +++ b/sound/soc/omap/omap-hdmi-card.c @@ -35,7 +35,7 @@ static struct snd_soc_dai_link omap_hdmi_dai = { .cpu_dai_name = "omap-hdmi-audio-dai", .platform_name = "omap-pcm-audio", .codec_name = "hdmi-audio-codec", - .codec_dai_name = "omap-hdmi-hifi", + .codec_dai_name = "hdmi-hifi", }; static struct snd_soc_card snd_soc_omap_hdmi = { -- cgit v1.2.3 From a775ad778073d55744ed6709ccede36310638911 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Wed, 5 Jun 2013 12:09:07 +1000 Subject: xfs: fix log recovery transaction item reordering There are several constraints that inode allocation and unlink logging impose on log recovery. These all stem from the fact that inode alloc/unlink are logged in buffers, but all other inode changes are logged in inode items. Hence there are ordering constraints that recovery must follow to ensure the correct result occurs. As it turns out, this ordering has been working mostly by chance than good management. The existing code moves all buffers except cancelled buffers to the head of the list, and everything else to the tail of the list. The problem with this is that is interleaves inode items with the buffer cancellation items, and hence whether the inode item in an cancelled buffer gets replayed is essentially left to chance. Further, this ordering causes problems for log recovery when inode CRCs are enabled. It typically replays the inode unlink buffer long before it replays the inode core changes, and so the CRC recorded in an unlink buffer is going to be invalid and hence any attempt to validate the inode in the buffer is going to fail. Hence we really need to enforce the ordering that the inode alloc/unlink code has expected log recovery to have since inode chunk de-allocation was introduced back in 2003... Signed-off-by: Dave Chinner Reviewed-by: Mark Tinguely Signed-off-by: Ben Myers --- fs/xfs/xfs_log_recover.c | 65 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 58 insertions(+), 7 deletions(-) diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index d6204d1ac47f..83088d96e6c4 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -1599,10 +1599,43 @@ xlog_recover_add_to_trans( } /* - * Sort the log items in the transaction. Cancelled buffers need - * to be put first so they are processed before any items that might - * modify the buffers. If they are cancelled, then the modifications - * don't need to be replayed. + * Sort the log items in the transaction. + * + * The ordering constraints are defined by the inode allocation and unlink + * behaviour. The rules are: + * + * 1. Every item is only logged once in a given transaction. Hence it + * represents the last logged state of the item. Hence ordering is + * dependent on the order in which operations need to be performed so + * required initial conditions are always met. + * + * 2. Cancelled buffers are recorded in pass 1 in a separate table and + * there's nothing to replay from them so we can simply cull them + * from the transaction. However, we can't do that until after we've + * replayed all the other items because they may be dependent on the + * cancelled buffer and replaying the cancelled buffer can remove it + * form the cancelled buffer table. Hence they have tobe done last. + * + * 3. Inode allocation buffers must be replayed before inode items that + * read the buffer and replay changes into it. + * + * 4. Inode unlink buffers must be replayed after inode items are replayed. + * This ensures that inodes are completely flushed to the inode buffer + * in a "free" state before we remove the unlinked inode list pointer. + * + * Hence the ordering needs to be inode allocation buffers first, inode items + * second, inode unlink buffers third and cancelled buffers last. + * + * But there's a problem with that - we can't tell an inode allocation buffer + * apart from a regular buffer, so we can't separate them. We can, however, + * tell an inode unlink buffer from the others, and so we can separate them out + * from all the other buffers and move them to last. + * + * Hence, 4 lists, in order from head to tail: + * - buffer_list for all buffers except cancelled/inode unlink buffers + * - item_list for all non-buffer items + * - inode_buffer_list for inode unlink buffers + * - cancel_list for the cancelled buffers */ STATIC int xlog_recover_reorder_trans( @@ -1612,6 +1645,10 @@ xlog_recover_reorder_trans( { xlog_recover_item_t *item, *n; LIST_HEAD(sort_list); + LIST_HEAD(cancel_list); + LIST_HEAD(buffer_list); + LIST_HEAD(inode_buffer_list); + LIST_HEAD(inode_list); list_splice_init(&trans->r_itemq, &sort_list); list_for_each_entry_safe(item, n, &sort_list, ri_list) { @@ -1619,12 +1656,18 @@ xlog_recover_reorder_trans( switch (ITEM_TYPE(item)) { case XFS_LI_BUF: - if (!(buf_f->blf_flags & XFS_BLF_CANCEL)) { + if (buf_f->blf_flags & XFS_BLF_CANCEL) { trace_xfs_log_recover_item_reorder_head(log, trans, item, pass); - list_move(&item->ri_list, &trans->r_itemq); + list_move(&item->ri_list, &cancel_list); break; } + if (buf_f->blf_flags & XFS_BLF_INODE_BUF) { + list_move(&item->ri_list, &inode_buffer_list); + break; + } + list_move_tail(&item->ri_list, &buffer_list); + break; case XFS_LI_INODE: case XFS_LI_DQUOT: case XFS_LI_QUOTAOFF: @@ -1632,7 +1675,7 @@ xlog_recover_reorder_trans( case XFS_LI_EFI: trace_xfs_log_recover_item_reorder_tail(log, trans, item, pass); - list_move_tail(&item->ri_list, &trans->r_itemq); + list_move_tail(&item->ri_list, &inode_list); break; default: xfs_warn(log->l_mp, @@ -1643,6 +1686,14 @@ xlog_recover_reorder_trans( } } ASSERT(list_empty(&sort_list)); + if (!list_empty(&buffer_list)) + list_splice(&buffer_list, &trans->r_itemq); + if (!list_empty(&inode_list)) + list_splice_tail(&inode_list, &trans->r_itemq); + if (!list_empty(&inode_buffer_list)) + list_splice_tail(&inode_buffer_list, &trans->r_itemq); + if (!list_empty(&cancel_list)) + list_splice_tail(&cancel_list, &trans->r_itemq); return 0; } -- cgit v1.2.3 From 0a32c26e720a8b38971d0685976f4a7d63f9e2ef Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Wed, 5 Jun 2013 12:09:08 +1000 Subject: xfs: inode unlinked list needs to recalculate the inode CRC The inode unlinked list manipulations operate directly on the inode buffer, and so bypass the inode CRC calculation mechanisms. Hence an inode on the unlinked list has an invalid CRC. Fix this by recalculating the CRC whenever we modify an unlinked list pointer in an inode, ncluding during log recovery. This is trivial to do and results in unlinked list operations always leaving a consistent inode in the buffer. Signed-off-by: Dave Chinner Reviewed-by: Mark Tinguely Signed-off-by: Ben Myers --- fs/xfs/xfs_inode.c | 16 ++++++++++++++++ fs/xfs/xfs_log_recover.c | 9 +++++++++ 2 files changed, 25 insertions(+) diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index efbe1accb6ca..7f7be5f98f52 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -1638,6 +1638,10 @@ xfs_iunlink( dip->di_next_unlinked = agi->agi_unlinked[bucket_index]; offset = ip->i_imap.im_boffset + offsetof(xfs_dinode_t, di_next_unlinked); + + /* need to recalc the inode CRC if appropriate */ + xfs_dinode_calc_crc(mp, dip); + xfs_trans_inode_buf(tp, ibp); xfs_trans_log_buf(tp, ibp, offset, (offset + sizeof(xfs_agino_t) - 1)); @@ -1723,6 +1727,10 @@ xfs_iunlink_remove( dip->di_next_unlinked = cpu_to_be32(NULLAGINO); offset = ip->i_imap.im_boffset + offsetof(xfs_dinode_t, di_next_unlinked); + + /* need to recalc the inode CRC if appropriate */ + xfs_dinode_calc_crc(mp, dip); + xfs_trans_inode_buf(tp, ibp); xfs_trans_log_buf(tp, ibp, offset, (offset + sizeof(xfs_agino_t) - 1)); @@ -1796,6 +1804,10 @@ xfs_iunlink_remove( dip->di_next_unlinked = cpu_to_be32(NULLAGINO); offset = ip->i_imap.im_boffset + offsetof(xfs_dinode_t, di_next_unlinked); + + /* need to recalc the inode CRC if appropriate */ + xfs_dinode_calc_crc(mp, dip); + xfs_trans_inode_buf(tp, ibp); xfs_trans_log_buf(tp, ibp, offset, (offset + sizeof(xfs_agino_t) - 1)); @@ -1809,6 +1821,10 @@ xfs_iunlink_remove( last_dip->di_next_unlinked = cpu_to_be32(next_agino); ASSERT(next_agino != 0); offset = last_offset + offsetof(xfs_dinode_t, di_next_unlinked); + + /* need to recalc the inode CRC if appropriate */ + xfs_dinode_calc_crc(mp, last_dip); + xfs_trans_inode_buf(tp, last_ibp); xfs_trans_log_buf(tp, last_ibp, offset, (offset + sizeof(xfs_agino_t) - 1)); diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index 83088d96e6c4..45a85ff84da1 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -1912,6 +1912,15 @@ xlog_recover_do_inode_buffer( buffer_nextp = (xfs_agino_t *)xfs_buf_offset(bp, next_unlinked_offset); *buffer_nextp = *logged_nextp; + + /* + * If necessary, recalculate the CRC in the on-disk inode. We + * have to leave the inode in a consistent state for whoever + * reads it next.... + */ + xfs_dinode_calc_crc(mp, (struct xfs_dinode *) + xfs_buf_offset(bp, i * mp->m_sb.sb_inodesize)); + } return 0; -- cgit v1.2.3 From d3eaace84e40bf946129e516dcbd617173c1cf14 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Wed, 5 Jun 2013 12:09:09 +1000 Subject: xfs: disable noattr2/attr2 mount options for CRC enabled filesystems attr2 format is always enabled for v5 superblock filesystems, so the mount options to enable or disable it need to be cause mount errors. Signed-off-by: Dave Chinner Reviewed-by: Brian Foster Signed-off-by: Ben Myers --- Documentation/filesystems/xfs.txt | 3 +++ fs/xfs/xfs_super.c | 11 +++++++++++ 2 files changed, 14 insertions(+) diff --git a/Documentation/filesystems/xfs.txt b/Documentation/filesystems/xfs.txt index 3e4b3dd1e046..83577f0232a0 100644 --- a/Documentation/filesystems/xfs.txt +++ b/Documentation/filesystems/xfs.txt @@ -33,6 +33,9 @@ When mounting an XFS filesystem, the following options are accepted. removing extended attributes) the on-disk superblock feature bit field will be updated to reflect this format being in use. + CRC enabled filesystems always use the attr2 format, and so + will reject the noattr2 mount option if it is set. + barrier Enables the use of block layer write barriers for writes into the journal and unwritten extent conversion. This allows for diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c index ea341cea68cb..3033ba5e9762 100644 --- a/fs/xfs/xfs_super.c +++ b/fs/xfs/xfs_super.c @@ -1372,6 +1372,17 @@ xfs_finish_flags( } } + /* + * V5 filesystems always use attr2 format for attributes. + */ + if (xfs_sb_version_hascrc(&mp->m_sb) && + (mp->m_flags & XFS_MOUNT_NOATTR2)) { + xfs_warn(mp, +"Cannot mount a V5 filesystem as %s. %s is always enabled for V5 filesystems.", + MNTOPT_NOATTR2, MNTOPT_ATTR2); + return XFS_ERROR(EINVAL); + } + /* * mkfs'ed attr2 will turn on attr2 mount unless explicitly * told by noattr2 to turn it off -- cgit v1.2.3 From 5c87d4bc1a86bd6e6754ac3d6e111d776ddcfe57 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Wed, 5 Jun 2013 12:09:10 +1000 Subject: xfs: increase number of ACL entries for V5 superblocks The limit of 25 ACL entries is arbitrary, but baked into the on-disk format. For version 5 superblocks, increase it to the maximum nuber of ACLs that can fit into a single xattr. Signed-off-by: Dave Chinner Reviewed-by: Brian Foster Reviewed-by: Mark Tinguely Signed-off-by: Ben Myers --- fs/xfs/xfs_acl.c | 31 ++++++++++++++++++------------- fs/xfs/xfs_acl.h | 31 ++++++++++++++++++++++++------- 2 files changed, 42 insertions(+), 20 deletions(-) diff --git a/fs/xfs/xfs_acl.c b/fs/xfs/xfs_acl.c index 1d32f1d52763..306d883d89bc 100644 --- a/fs/xfs/xfs_acl.c +++ b/fs/xfs/xfs_acl.c @@ -21,6 +21,8 @@ #include "xfs_bmap_btree.h" #include "xfs_inode.h" #include "xfs_vnodeops.h" +#include "xfs_sb.h" +#include "xfs_mount.h" #include "xfs_trace.h" #include #include @@ -34,7 +36,9 @@ */ STATIC struct posix_acl * -xfs_acl_from_disk(struct xfs_acl *aclp) +xfs_acl_from_disk( + struct xfs_acl *aclp, + int max_entries) { struct posix_acl_entry *acl_e; struct posix_acl *acl; @@ -42,7 +46,7 @@ xfs_acl_from_disk(struct xfs_acl *aclp) unsigned int count, i; count = be32_to_cpu(aclp->acl_cnt); - if (count > XFS_ACL_MAX_ENTRIES) + if (count > max_entries) return ERR_PTR(-EFSCORRUPTED); acl = posix_acl_alloc(count, GFP_KERNEL); @@ -108,9 +112,9 @@ xfs_get_acl(struct inode *inode, int type) struct xfs_inode *ip = XFS_I(inode); struct posix_acl *acl; struct xfs_acl *xfs_acl; - int len = sizeof(struct xfs_acl); unsigned char *ea_name; int error; + int len; acl = get_cached_acl(inode, type); if (acl != ACL_NOT_CACHED) @@ -133,8 +137,8 @@ xfs_get_acl(struct inode *inode, int type) * If we have a cached ACLs value just return it, not need to * go out to the disk. */ - - xfs_acl = kzalloc(sizeof(struct xfs_acl), GFP_KERNEL); + len = XFS_ACL_MAX_SIZE(ip->i_mount); + xfs_acl = kzalloc(len, GFP_KERNEL); if (!xfs_acl) return ERR_PTR(-ENOMEM); @@ -153,7 +157,7 @@ xfs_get_acl(struct inode *inode, int type) goto out; } - acl = xfs_acl_from_disk(xfs_acl); + acl = xfs_acl_from_disk(xfs_acl, XFS_ACL_MAX_ENTRIES(ip->i_mount)); if (IS_ERR(acl)) goto out; @@ -189,16 +193,17 @@ xfs_set_acl(struct inode *inode, int type, struct posix_acl *acl) if (acl) { struct xfs_acl *xfs_acl; - int len; + int len = XFS_ACL_MAX_SIZE(ip->i_mount); - xfs_acl = kzalloc(sizeof(struct xfs_acl), GFP_KERNEL); + xfs_acl = kzalloc(len, GFP_KERNEL); if (!xfs_acl) return -ENOMEM; xfs_acl_to_disk(xfs_acl, acl); - len = sizeof(struct xfs_acl) - - (sizeof(struct xfs_acl_entry) * - (XFS_ACL_MAX_ENTRIES - acl->a_count)); + + /* subtract away the unused acl entries */ + len -= sizeof(struct xfs_acl_entry) * + (XFS_ACL_MAX_ENTRIES(ip->i_mount) - acl->a_count); error = -xfs_attr_set(ip, ea_name, (unsigned char *)xfs_acl, len, ATTR_ROOT); @@ -243,7 +248,7 @@ xfs_set_mode(struct inode *inode, umode_t mode) static int xfs_acl_exists(struct inode *inode, unsigned char *name) { - int len = sizeof(struct xfs_acl); + int len = XFS_ACL_MAX_SIZE(XFS_M(inode->i_sb)); return (xfs_attr_get(XFS_I(inode), name, NULL, &len, ATTR_ROOT|ATTR_KERNOVAL) == 0); @@ -379,7 +384,7 @@ xfs_xattr_acl_set(struct dentry *dentry, const char *name, goto out_release; error = -EINVAL; - if (acl->a_count > XFS_ACL_MAX_ENTRIES) + if (acl->a_count > XFS_ACL_MAX_ENTRIES(XFS_M(inode->i_sb))) goto out_release; if (type == ACL_TYPE_ACCESS) { diff --git a/fs/xfs/xfs_acl.h b/fs/xfs/xfs_acl.h index 39632d941354..4016a567b83c 100644 --- a/fs/xfs/xfs_acl.h +++ b/fs/xfs/xfs_acl.h @@ -22,19 +22,36 @@ struct inode; struct posix_acl; struct xfs_inode; -#define XFS_ACL_MAX_ENTRIES 25 #define XFS_ACL_NOT_PRESENT (-1) /* On-disk XFS access control list structure */ +struct xfs_acl_entry { + __be32 ae_tag; + __be32 ae_id; + __be16 ae_perm; + __be16 ae_pad; /* fill the implicit hole in the structure */ +}; + struct xfs_acl { - __be32 acl_cnt; - struct xfs_acl_entry { - __be32 ae_tag; - __be32 ae_id; - __be16 ae_perm; - } acl_entry[XFS_ACL_MAX_ENTRIES]; + __be32 acl_cnt; + struct xfs_acl_entry acl_entry[0]; }; +/* + * The number of ACL entries allowed is defined by the on-disk format. + * For v4 superblocks, that is limited to 25 entries. For v5 superblocks, it is + * limited only by the maximum size of the xattr that stores the information. + */ +#define XFS_ACL_MAX_ENTRIES(mp) \ + (xfs_sb_version_hascrc(&mp->m_sb) \ + ? (XATTR_SIZE_MAX - sizeof(struct xfs_acl)) / \ + sizeof(struct xfs_acl_entry) \ + : 25) + +#define XFS_ACL_MAX_SIZE(mp) \ + (sizeof(struct xfs_acl) + \ + sizeof(struct xfs_acl_entry) * XFS_ACL_MAX_ENTRIES((mp))) + /* On-disk XFS extended attribute names */ #define SGI_ACL_FILE (unsigned char *)"SGI_ACL_FILE" #define SGI_ACL_DEFAULT (unsigned char *)"SGI_ACL_DEFAULT" -- cgit v1.2.3 From 87962c4db7f594c377d8b0b5a5f563e5f0b5d5d0 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Wed, 15 May 2013 19:02:06 +0400 Subject: xtensa: flush TLB entries for pages of non-current mm correctly Sometimes under high memory pressure one process gets a page of another process, which manifests itself with an invalid instruction exception. This happens because flush_tlb_page fails to clear TLB entries when called with vma that does not belong to current mm, because it does not set RASID appropriately. When page reclaiming mechanism swaps physical pages out replacing their PTEs with none or swap PTEs, it calls flush_tlb_page. Later physical page may be reused elsewhere, but the stale TLB mapping still refers to it, allowing process that owned the mapping to see the new state of that physical page. Put ASID of the mm that owns vma to the RASID to fix that issue. Also replace otherwise meaningless local_save_flags with local_irq_save. Signed-off-by: Max Filippov Signed-off-by: Chris Zankel --- arch/xtensa/mm/tlb.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/xtensa/mm/tlb.c b/arch/xtensa/mm/tlb.c index 5411aa67c68e..743346150eea 100644 --- a/arch/xtensa/mm/tlb.c +++ b/arch/xtensa/mm/tlb.c @@ -64,7 +64,7 @@ void flush_tlb_mm(struct mm_struct *mm) { if (mm == current->active_mm) { unsigned long flags; - local_save_flags(flags); + local_irq_save(flags); __get_new_mmu_context(mm); __load_mmu_context(mm); local_irq_restore(flags); @@ -94,7 +94,7 @@ void flush_tlb_range (struct vm_area_struct *vma, printk("[tlbrange<%02lx,%08lx,%08lx>]\n", (unsigned long)mm->context, start, end); #endif - local_save_flags(flags); + local_irq_save(flags); if (end-start + (PAGE_SIZE-1) <= _TLB_ENTRIES << PAGE_SHIFT) { int oldpid = get_rasid_register(); @@ -128,9 +128,10 @@ void flush_tlb_page (struct vm_area_struct *vma, unsigned long page) if(mm->context == NO_CONTEXT) return; - local_save_flags(flags); + local_irq_save(flags); oldpid = get_rasid_register(); + set_rasid_register(ASID_INSERT(mm->context)); if (vma->vm_flags & VM_EXEC) invalidate_itlb_mapping(page); -- cgit v1.2.3 From 28622c5353cb30d6ad8f6ef008e46103f1083f5c Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Thu, 23 May 2013 10:58:18 +0300 Subject: xtensa: tell git to ignore generated files Signed-off-by: Baruch Siach Signed-off-by: Max Filippov Signed-off-by: Chris Zankel --- arch/xtensa/boot/.gitignore | 2 ++ arch/xtensa/boot/boot-elf/.gitignore | 1 + arch/xtensa/kernel/.gitignore | 1 + 3 files changed, 4 insertions(+) create mode 100644 arch/xtensa/boot/.gitignore create mode 100644 arch/xtensa/boot/boot-elf/.gitignore create mode 100644 arch/xtensa/kernel/.gitignore diff --git a/arch/xtensa/boot/.gitignore b/arch/xtensa/boot/.gitignore new file mode 100644 index 000000000000..38177c7ebcab --- /dev/null +++ b/arch/xtensa/boot/.gitignore @@ -0,0 +1,2 @@ +uImage +zImage.redboot diff --git a/arch/xtensa/boot/boot-elf/.gitignore b/arch/xtensa/boot/boot-elf/.gitignore new file mode 100644 index 000000000000..5ff8fbb8561b --- /dev/null +++ b/arch/xtensa/boot/boot-elf/.gitignore @@ -0,0 +1 @@ +boot.lds diff --git a/arch/xtensa/kernel/.gitignore b/arch/xtensa/kernel/.gitignore new file mode 100644 index 000000000000..c5f676c3c224 --- /dev/null +++ b/arch/xtensa/kernel/.gitignore @@ -0,0 +1 @@ +vmlinux.lds -- cgit v1.2.3 From 214fe80fcba6df6be28d6ea66762d203d84842c4 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Mon, 27 May 2013 12:23:52 +0300 Subject: xtensa: remove unused platform_init_irq() Signed-off-by: Baruch Siach Signed-off-by: Max Filippov Signed-off-by: Chris Zankel --- arch/xtensa/include/asm/platform.h | 5 ----- arch/xtensa/kernel/platform.c | 1 - 2 files changed, 6 deletions(-) diff --git a/arch/xtensa/include/asm/platform.h b/arch/xtensa/include/asm/platform.h index ec098b68fb9a..32e98f27ce97 100644 --- a/arch/xtensa/include/asm/platform.h +++ b/arch/xtensa/include/asm/platform.h @@ -29,11 +29,6 @@ extern void platform_init(bp_tag_t*); */ extern void platform_setup (char **); -/* - * platform_init_irq is called from init_IRQ. - */ -extern void platform_init_irq (void); - /* * platform_restart is called to restart the system. */ diff --git a/arch/xtensa/kernel/platform.c b/arch/xtensa/kernel/platform.c index 2bd6c351f37c..da827cbb2f59 100644 --- a/arch/xtensa/kernel/platform.c +++ b/arch/xtensa/kernel/platform.c @@ -29,7 +29,6 @@ */ _F(void, setup, (char** cmd), { }); -_F(void, init_irq, (void), { }); _F(void, restart, (void), { while(1); }); _F(void, halt, (void), { while(1); }); _F(void, power_off, (void), { while(1); }); -- cgit v1.2.3 From 54c0af9f1a1bfe9639666aea789dae6a37a741cf Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Mon, 27 May 2013 09:11:59 +0300 Subject: xtensa: xtfpga: fix section mismatch platform_calibrate_ccount() calls update_clock_frequency() which is in .init section. However, platform_calibrate_ccount() itself is only called from .init (i.e., time_init()). Signed-off-by: Baruch Siach Signed-off-by: Max Filippov Signed-off-by: Chris Zankel --- arch/xtensa/platforms/xtfpga/setup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/xtensa/platforms/xtfpga/setup.c b/arch/xtensa/platforms/xtfpga/setup.c index 96ef8eeb064e..c7ce62ff73f7 100644 --- a/arch/xtensa/platforms/xtfpga/setup.c +++ b/arch/xtensa/platforms/xtfpga/setup.c @@ -163,7 +163,7 @@ void platform_heartbeat(void) #ifdef CONFIG_XTENSA_CALIBRATE_CCOUNT -void platform_calibrate_ccount(void) +void __init platform_calibrate_ccount(void) { long clk_freq = 0; #ifdef CONFIG_OF -- cgit v1.2.3 From b5514786ec3257c8703fc7f29bdf98b42253ee39 Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Mon, 27 May 2013 19:57:01 +0800 Subject: arch: xtensa: include: asm: compiling issue, need cmpxchg64() defined. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When compiling with 'allmodconfig', some of drivers need cmpxchg64(), xtensa does not supply 64-bit implementation for 'xchg', so use the 'generic' implementation. e.g. (for next-20130527 tree): drivers/block/blockconsole.c:164:2: error: implicit declaration of function ‘cmpxchg64’ [-Werror=implicit-function-declaration] Signed-off-by: Chen Gang Signed-off-by: Max Filippov Signed-off-by: Chris Zankel --- arch/xtensa/include/asm/cmpxchg.h | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/xtensa/include/asm/cmpxchg.h b/arch/xtensa/include/asm/cmpxchg.h index d9ab131bc1aa..370b26f38414 100644 --- a/arch/xtensa/include/asm/cmpxchg.h +++ b/arch/xtensa/include/asm/cmpxchg.h @@ -93,6 +93,7 @@ static inline unsigned long __cmpxchg_local(volatile void *ptr, ((__typeof__(*(ptr)))__cmpxchg_local_generic((ptr), (unsigned long)(o),\ (unsigned long)(n), sizeof(*(ptr)))) #define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n)) +#define cmpxchg64(ptr, o, n) cmpxchg64_local((ptr), (o), (n)) /* * xchg_u32 -- cgit v1.2.3 From dc2bffa77a2866a451126cf3146abb9787edce78 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Mon, 27 May 2013 18:58:24 +0400 Subject: xtensa: ISS: fix section mismatch in iss_net_setup iss_net_setup is only called from __setup, so it should be marked __init. Signed-off-by: Max Filippov Signed-off-by: Chris Zankel --- arch/xtensa/platforms/iss/network.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/xtensa/platforms/iss/network.c b/arch/xtensa/platforms/iss/network.c index 7d0fea6d7f20..56f88b7afe2f 100644 --- a/arch/xtensa/platforms/iss/network.c +++ b/arch/xtensa/platforms/iss/network.c @@ -700,7 +700,7 @@ struct iss_net_init { #define ERR KERN_ERR "iss_net_setup: " -static int iss_net_setup(char *str) +static int __init iss_net_setup(char *str) { struct iss_net_private *device = NULL; struct iss_net_init *new; -- cgit v1.2.3 From fd95ee7380ae973c6f11b897af6ef9d253a1df9e Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Mon, 27 May 2013 19:45:58 +0400 Subject: xtensa: fix section mismatch in pcibios_fixup_bus Remove __init annotation from pcibios_fixup_bus as is called from pci_scan_child_bus which is not __init. Also fix a couple of minor build warnings. Signed-off-by: Max Filippov Signed-off-by: Chris Zankel --- arch/xtensa/kernel/pci.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/xtensa/kernel/pci.c b/arch/xtensa/kernel/pci.c index 126c18839409..5b3403388d7f 100644 --- a/arch/xtensa/kernel/pci.c +++ b/arch/xtensa/kernel/pci.c @@ -77,9 +77,9 @@ pcibios_align_resource(void *data, const struct resource *res, if (res->flags & IORESOURCE_IO) { if (size > 0x100) { - printk(KERN_ERR "PCI: I/O Region %s/%d too large" - " (%ld bytes)\n", pci_name(dev), - dev->resource - res, size); + pr_err("PCI: I/O Region %s/%d too large (%u bytes)\n", + pci_name(dev), dev->resource - res, + size); } if (start & 0x300) @@ -174,7 +174,7 @@ static int __init pcibios_init(void) struct pci_controller *pci_ctrl; struct list_head resources; struct pci_bus *bus; - int next_busno = 0, i; + int next_busno = 0; printk("PCI: Probing PCI hardware\n"); @@ -197,7 +197,7 @@ static int __init pcibios_init(void) subsys_initcall(pcibios_init); -void __init pcibios_fixup_bus(struct pci_bus *bus) +void pcibios_fixup_bus(struct pci_bus *bus) { if (bus->parent) { /* This is a subordinate bridge */ -- cgit v1.2.3 From dfab88beda88d6c24111e5966b08ecf813c3a18a Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Fri, 31 May 2013 12:21:31 +0800 Subject: PCI: Hide remove and rescan sysfs interfaces for SR-IOV virtual functions PCI devices for SR-IOV virtual functions should only be created/ destroyed by pci_enable_sriov()/pci_disable_sriov() because special data structures are associated with SR-IOV virtual functions. So hide hotplug related sysfs interfaces "remove" and "rescan" for SR-IOV virtual functions, otherwise it may cause memory leakage and other issues. Signed-off-by: Jiang Liu Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas Cc: Donald Dutile Cc: Yinghai Lu Cc: Ram Pai --- drivers/pci/pci-sysfs.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 5b4a9d9cd200..403da60cd983 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -325,6 +325,8 @@ dev_rescan_store(struct device *dev, struct device_attribute *attr, } return count; } +struct device_attribute dev_rescan_attr = __ATTR(rescan, (S_IWUSR|S_IWGRP), + NULL, dev_rescan_store); static void remove_callback(struct device *dev) { @@ -354,6 +356,8 @@ remove_store(struct device *dev, struct device_attribute *dummy, count = ret; return count; } +struct device_attribute dev_remove_attr = __ATTR(remove, (S_IWUSR|S_IWGRP), + NULL, remove_store); static ssize_t dev_bus_rescan_store(struct device *dev, struct device_attribute *attr, @@ -504,8 +508,6 @@ struct device_attribute pci_dev_attrs[] = { __ATTR(broken_parity_status,(S_IRUGO|S_IWUSR), broken_parity_status_show,broken_parity_status_store), __ATTR(msi_bus, 0644, msi_bus_show, msi_bus_store), - __ATTR(remove, (S_IWUSR|S_IWGRP), NULL, remove_store), - __ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, dev_rescan_store), #if defined(CONFIG_PM_RUNTIME) && defined(CONFIG_ACPI) __ATTR(d3cold_allowed, 0644, d3cold_allowed_show, d3cold_allowed_store), #endif @@ -1463,6 +1465,29 @@ static umode_t pci_dev_attrs_are_visible(struct kobject *kobj, return a->mode; } +static struct attribute *pci_dev_hp_attrs[] = { + &dev_remove_attr.attr, + &dev_rescan_attr.attr, + NULL, +}; + +static umode_t pci_dev_hp_attrs_are_visible(struct kobject *kobj, + struct attribute *a, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct pci_dev *pdev = to_pci_dev(dev); + + if (pdev->is_virtfn) + return 0; + + return a->mode; +} + +static struct attribute_group pci_dev_hp_attr_group = { + .attrs = pci_dev_hp_attrs, + .is_visible = pci_dev_hp_attrs_are_visible, +}; + #ifdef CONFIG_PCI_IOV static struct attribute *sriov_dev_attrs[] = { &sriov_totalvfs_attr.attr, @@ -1494,6 +1519,7 @@ static struct attribute_group pci_dev_attr_group = { static const struct attribute_group *pci_dev_attr_groups[] = { &pci_dev_attr_group, + &pci_dev_hp_attr_group, #ifdef CONFIG_PCI_IOV &sriov_dev_attr_group, #endif -- cgit v1.2.3 From 2bdc1bb2b4e1f517d8aa5bbbad9cb6ccac8a94fb Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 3 Jun 2013 10:20:39 +0100 Subject: ASoC: sgtl5000: Make device cache only when powered off When the regulators have been disabled mark the device as cache only so that we don't try to interact with the hardware. Signed-off-by: Mark Brown --- sound/soc/codecs/sgtl5000.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index b4297416401e..c8f2afb74706 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -907,10 +907,25 @@ static int sgtl5000_set_bias_level(struct snd_soc_codec *codec, if (ret) return ret; udelay(10); + + regcache_cache_only(sgtl5000->regmap, false); + + ret = regcache_sync(sgtl5000->regmap); + if (ret != 0) { + dev_err(codec->dev, + "Failed to restore cache: %d\n", ret); + + regcache_cache_only(sgtl5000->regmap, true); + regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + + return ret; + } } break; case SND_SOC_BIAS_OFF: + regcache_cache_only(sgtl5000->regmap, true); regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies), sgtl5000->supplies); break; -- cgit v1.2.3 From f12dc020149fad7087e119e54cffea668272bf7d Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 3 Jun 2013 19:13:02 -0700 Subject: cgroup: mark "tasks" cgroup file as insane Some resources controlled by cgroup aren't per-task and cgroup core allowing threads of a single thread_group to be in different cgroups forced memcg do explicitly find the group leader and use it. This is gonna be nasty when transitioning to unified hierarchy and in general we don't want and won't support granularity finer than processes. Mark "tasks" with CFTYPE_INSANE. Signed-off-by: Tejun Heo Acked-by: Li Zefan Cc: Johannes Weiner Cc: Michal Hocko Cc: Balbir Singh Cc: KAMEZAWA Hiroyuki Cc: cgroups@vger.kernel.org Cc: Vivek Goyal --- kernel/cgroup.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/cgroup.c b/kernel/cgroup.c index fefc41c1a147..1e0f445b5b88 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -4037,6 +4037,7 @@ static int cgroup_clone_children_write(struct cgroup *cgrp, static struct cftype files[] = { { .name = "tasks", + .flags = CFTYPE_INSANE, /* use "procs" instead */ .open = cgroup_tasks_open, .write_u64 = cgroup_tasks_write, .release = cgroup_pidlist_release, -- cgit v1.2.3 From cc5943a7816ba6c00639837a62131386619548dc Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 3 Jun 2013 19:13:55 -0700 Subject: cgroup: mark "notify_on_release" and "release_agent" cgroup files insane The empty cgroup notification mechanism currently implemented in cgroup is tragically outdated. Forking and execing userland process stopped being a viable notification mechanism more than a decade ago. We're gonna have a saner mechanism. Let's make it clear that this abomination is going away. Mark "notify_on_release" and "release_agent" with CFTYPE_INSANE. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- kernel/cgroup.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 1e0f445b5b88..b3bb8a393642 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -4052,6 +4052,7 @@ static struct cftype files[] = { }, { .name = "notify_on_release", + .flags = CFTYPE_INSANE, .read_u64 = cgroup_read_notify_on_release, .write_u64 = cgroup_write_notify_on_release, }, @@ -4073,7 +4074,7 @@ static struct cftype files[] = { }, { .name = "release_agent", - .flags = CFTYPE_ONLY_ON_ROOT, + .flags = CFTYPE_INSANE | CFTYPE_ONLY_ON_ROOT, .read_seq_string = cgroup_release_agent_show, .write_string = cgroup_release_agent_write, .max_write_len = PATH_MAX, -- cgit v1.2.3 From d5c56ced775f6bdc32b689b01c9c4f9b66e18610 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 3 Jun 2013 19:14:34 -0700 Subject: cgroup: clean up the cftype array for the base cgroup files * Rename it from files[] (really?) to cgroup_base_files[]. * Drop CGROUP_FILE_GENERIC_PREFIX which was defined as "cgroup." and used inconsistently. Just use "cgroup." directly. * Collect insane files at the end. Note that only the insane ones are missing "cgroup." prefix. This patch doesn't introduce any functional changes. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- kernel/cgroup.c | 47 ++++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/kernel/cgroup.c b/kernel/cgroup.c index b3bb8a393642..bc53d5014b28 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -4029,35 +4029,16 @@ static int cgroup_clone_children_write(struct cgroup *cgrp, return 0; } -/* - * for the common functions, 'private' gives the type of file - */ -/* for hysterical raisins, we can't put this on the older files */ -#define CGROUP_FILE_GENERIC_PREFIX "cgroup." -static struct cftype files[] = { +static struct cftype cgroup_base_files[] = { { - .name = "tasks", - .flags = CFTYPE_INSANE, /* use "procs" instead */ - .open = cgroup_tasks_open, - .write_u64 = cgroup_tasks_write, - .release = cgroup_pidlist_release, - .mode = S_IRUGO | S_IWUSR, - }, - { - .name = CGROUP_FILE_GENERIC_PREFIX "procs", + .name = "cgroup.procs", .open = cgroup_procs_open, .write_u64 = cgroup_procs_write, .release = cgroup_pidlist_release, .mode = S_IRUGO | S_IWUSR, }, { - .name = "notify_on_release", - .flags = CFTYPE_INSANE, - .read_u64 = cgroup_read_notify_on_release, - .write_u64 = cgroup_write_notify_on_release, - }, - { - .name = CGROUP_FILE_GENERIC_PREFIX "event_control", + .name = "cgroup.event_control", .write_string = cgroup_write_event_control, .mode = S_IWUGO, }, @@ -4072,6 +4053,26 @@ static struct cftype files[] = { .flags = CFTYPE_ONLY_ON_ROOT, .read_seq_string = cgroup_sane_behavior_show, }, + + /* + * Historical crazy stuff. These don't have "cgroup." prefix and + * don't exist if sane_behavior. If you're depending on these, be + * prepared to be burned. + */ + { + .name = "tasks", + .flags = CFTYPE_INSANE, /* use "procs" instead */ + .open = cgroup_tasks_open, + .write_u64 = cgroup_tasks_write, + .release = cgroup_pidlist_release, + .mode = S_IRUGO | S_IWUSR, + }, + { + .name = "notify_on_release", + .flags = CFTYPE_INSANE, + .read_u64 = cgroup_read_notify_on_release, + .write_u64 = cgroup_write_notify_on_release, + }, { .name = "release_agent", .flags = CFTYPE_INSANE | CFTYPE_ONLY_ON_ROOT, @@ -4095,7 +4096,7 @@ static int cgroup_populate_dir(struct cgroup *cgrp, bool base_files, struct cgroup_subsys *ss; if (base_files) { - err = cgroup_addrm_files(cgrp, NULL, files, true); + err = cgroup_addrm_files(cgrp, NULL, cgroup_base_files, true); if (err < 0) return err; } -- cgit v1.2.3 From c2a2a1a722b7e4edce7dad285b461e0b2592eecc Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 5 Jun 2013 21:55:29 +0300 Subject: drm/i915: distinguish between error messages in DIDL initialization Two exactly same error messages on different error paths makes debugging difficult. Clarify the messages and distinguish them from each other. Signed-off-by: Jani Nikula Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_opregion.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c index 5c2d6939600e..79be7cfd3152 100644 --- a/drivers/gpu/drm/i915/intel_opregion.c +++ b/drivers/gpu/drm/i915/intel_opregion.c @@ -312,7 +312,7 @@ static void intel_didl_outputs(struct drm_device *dev) list_for_each_entry(acpi_cdev, &acpi_video_bus->children, node) { if (i >= 8) { dev_printk(KERN_ERR, &dev->pdev->dev, - "More than 8 outputs detected\n"); + "More than 8 outputs detected via ACPI\n"); return; } status = @@ -339,7 +339,7 @@ blind_set: int output_type = ACPI_OTHER_OUTPUT; if (i >= 8) { dev_printk(KERN_ERR, &dev->pdev->dev, - "More than 8 outputs detected\n"); + "More than 8 outputs in connector list\n"); return; } switch (connector->connector_type) { -- cgit v1.2.3 From 21d1101f013c12d1dd78cfdf263d619c80975b47 Mon Sep 17 00:00:00 2001 From: Dave Kleikamp Date: Wed, 5 Jun 2013 14:32:08 -0500 Subject: jfs: fix sparse warning in fs/jfs/xattr.c CHECK fs/jfs/xattr.c fs/jfs/xattr.c:1092:5: warning: symbol 'jfs_initxattrs' was not declared. Should it be static? Signed-off-by: Dave Kleikamp --- fs/jfs/xattr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/jfs/xattr.c b/fs/jfs/xattr.c index 42d67f9757bf..86ce028f2566 100644 --- a/fs/jfs/xattr.c +++ b/fs/jfs/xattr.c @@ -1089,8 +1089,8 @@ int jfs_removexattr(struct dentry *dentry, const char *name) } #ifdef CONFIG_JFS_SECURITY -int jfs_initxattrs(struct inode *inode, const struct xattr *xattr_array, - void *fs_info) +static int jfs_initxattrs(struct inode *inode, const struct xattr *xattr_array, + void *fs_info) { const struct xattr *xattr; tid_t *tid = fs_info; -- cgit v1.2.3 From eb8630d7d2fd13589e6a7a3ae2fe1f75f867fbed Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 4 Jun 2013 16:39:15 -0700 Subject: jfs: Update jfs_error Use a more current logging style. Add __printf format and argument verification. Remove embedded function names from formats. Add %pf, __builtin_return_address(0) to jfs_error. Add newlines to formats for kernel style consistency. (One format already had an erroneous newline) Coalesce formats and align arguments. Object size reduced ~1KiB. $ size fs/jfs/built-in.o* text data bss dec hex filename 201891 35488 63936 301315 49903 fs/jfs/built-in.o.new 202821 35488 64192 302501 49da5 fs/jfs/built-in.o.old Signed-off-by: Joe Perches Signed-off-by: Dave Kleikamp --- fs/jfs/jfs_dmap.c | 70 +++++++++++++++++++------------------------------ fs/jfs/jfs_dtree.c | 37 +++++++++++++------------- fs/jfs/jfs_extent.c | 2 +- fs/jfs/jfs_imap.c | 69 ++++++++++++++++++++---------------------------- fs/jfs/jfs_metapage.c | 5 ++-- fs/jfs/jfs_superblock.h | 1 + fs/jfs/jfs_txnmgr.c | 2 +- fs/jfs/jfs_xtree.c | 62 +++++++++++++++++++++---------------------- fs/jfs/namei.c | 2 +- fs/jfs/resize.c | 2 +- fs/jfs/super.c | 22 +++++++++------- fs/jfs/xattr.c | 4 +-- 12 files changed, 126 insertions(+), 152 deletions(-) diff --git a/fs/jfs/jfs_dmap.c b/fs/jfs/jfs_dmap.c index 9a55f53be5ff..370d7b6c5942 100644 --- a/fs/jfs/jfs_dmap.c +++ b/fs/jfs/jfs_dmap.c @@ -346,8 +346,7 @@ int dbFree(struct inode *ip, s64 blkno, s64 nblocks) printk(KERN_ERR "blkno = %Lx, nblocks = %Lx\n", (unsigned long long) blkno, (unsigned long long) nblocks); - jfs_error(ip->i_sb, - "dbFree: block to be freed is outside the map"); + jfs_error(ip->i_sb, "block to be freed is outside the map\n"); return -EIO; } @@ -384,7 +383,7 @@ int dbFree(struct inode *ip, s64 blkno, s64 nblocks) /* free the blocks. */ if ((rc = dbFreeDmap(bmp, dp, blkno, nb))) { - jfs_error(ip->i_sb, "dbFree: error in block map\n"); + jfs_error(ip->i_sb, "error in block map\n"); release_metapage(mp); IREAD_UNLOCK(ipbmap); return (rc); @@ -441,8 +440,7 @@ dbUpdatePMap(struct inode *ipbmap, printk(KERN_ERR "blkno = %Lx, nblocks = %Lx\n", (unsigned long long) blkno, (unsigned long long) nblocks); - jfs_error(ipbmap->i_sb, - "dbUpdatePMap: blocks are outside the map"); + jfs_error(ipbmap->i_sb, "blocks are outside the map\n"); return -EIO; } @@ -726,7 +724,7 @@ int dbAlloc(struct inode *ip, s64 hint, s64 nblocks, s64 * results) /* the hint should be within the map */ if (hint >= mapSize) { - jfs_error(ip->i_sb, "dbAlloc: the hint is outside the map"); + jfs_error(ip->i_sb, "the hint is outside the map\n"); return -EIO; } @@ -1057,8 +1055,7 @@ static int dbExtend(struct inode *ip, s64 blkno, s64 nblocks, s64 addnblocks) bmp = sbi->bmap; if (lastblkno < 0 || lastblkno >= bmp->db_mapsize) { IREAD_UNLOCK(ipbmap); - jfs_error(ip->i_sb, - "dbExtend: the block is outside the filesystem"); + jfs_error(ip->i_sb, "the block is outside the filesystem\n"); return -EIO; } @@ -1134,8 +1131,7 @@ static int dbAllocNext(struct bmap * bmp, struct dmap * dp, s64 blkno, u32 mask; if (dp->tree.leafidx != cpu_to_le32(LEAFIND)) { - jfs_error(bmp->db_ipbmap->i_sb, - "dbAllocNext: Corrupt dmap page"); + jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n"); return -EIO; } @@ -1265,8 +1261,7 @@ dbAllocNear(struct bmap * bmp, s8 *leaf; if (dp->tree.leafidx != cpu_to_le32(LEAFIND)) { - jfs_error(bmp->db_ipbmap->i_sb, - "dbAllocNear: Corrupt dmap page"); + jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmap page\n"); return -EIO; } @@ -1381,8 +1376,7 @@ dbAllocAG(struct bmap * bmp, int agno, s64 nblocks, int l2nb, s64 * results) */ if (l2nb > bmp->db_agl2size) { jfs_error(bmp->db_ipbmap->i_sb, - "dbAllocAG: allocation request is larger than the " - "allocation group size"); + "allocation request is larger than the allocation group size\n"); return -EIO; } @@ -1417,7 +1411,7 @@ dbAllocAG(struct bmap * bmp, int agno, s64 nblocks, int l2nb, s64 * results) (unsigned long long) blkno, (unsigned long long) nblocks); jfs_error(bmp->db_ipbmap->i_sb, - "dbAllocAG: dbAllocCtl failed in free AG"); + "dbAllocCtl failed in free AG\n"); } return (rc); } @@ -1433,8 +1427,7 @@ dbAllocAG(struct bmap * bmp, int agno, s64 nblocks, int l2nb, s64 * results) budmin = dcp->budmin; if (dcp->leafidx != cpu_to_le32(CTLLEAFIND)) { - jfs_error(bmp->db_ipbmap->i_sb, - "dbAllocAG: Corrupt dmapctl page"); + jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmapctl page\n"); release_metapage(mp); return -EIO; } @@ -1475,7 +1468,7 @@ dbAllocAG(struct bmap * bmp, int agno, s64 nblocks, int l2nb, s64 * results) } if (n == 4) { jfs_error(bmp->db_ipbmap->i_sb, - "dbAllocAG: failed descending stree"); + "failed descending stree\n"); release_metapage(mp); return -EIO; } @@ -1515,8 +1508,7 @@ dbAllocAG(struct bmap * bmp, int agno, s64 nblocks, int l2nb, s64 * results) &blkno))) { if (rc == -ENOSPC) { jfs_error(bmp->db_ipbmap->i_sb, - "dbAllocAG: control page " - "inconsistent"); + "control page inconsistent\n"); return -EIO; } return (rc); @@ -1528,7 +1520,7 @@ dbAllocAG(struct bmap * bmp, int agno, s64 nblocks, int l2nb, s64 * results) rc = dbAllocCtl(bmp, nblocks, l2nb, blkno, results); if (rc == -ENOSPC) { jfs_error(bmp->db_ipbmap->i_sb, - "dbAllocAG: unable to allocate blocks"); + "unable to allocate blocks\n"); rc = -EIO; } return (rc); @@ -1587,8 +1579,7 @@ static int dbAllocAny(struct bmap * bmp, s64 nblocks, int l2nb, s64 * results) */ rc = dbAllocCtl(bmp, nblocks, l2nb, blkno, results); if (rc == -ENOSPC) { - jfs_error(bmp->db_ipbmap->i_sb, - "dbAllocAny: unable to allocate blocks"); + jfs_error(bmp->db_ipbmap->i_sb, "unable to allocate blocks\n"); return -EIO; } return (rc); @@ -1652,8 +1643,7 @@ s64 dbDiscardAG(struct inode *ip, int agno, s64 minlen) range_cnt = min_t(u64, max_ranges + 1, 32 * 1024); totrim = kmalloc(sizeof(struct range2trim) * range_cnt, GFP_NOFS); if (totrim == NULL) { - jfs_error(bmp->db_ipbmap->i_sb, - "dbDiscardAG: no memory for trim array"); + jfs_error(bmp->db_ipbmap->i_sb, "no memory for trim array\n"); IWRITE_UNLOCK(ipbmap); return 0; } @@ -1682,8 +1672,7 @@ s64 dbDiscardAG(struct inode *ip, int agno, s64 minlen) nblocks = 1 << l2nb; } else { /* Trim any already allocated blocks */ - jfs_error(bmp->db_ipbmap->i_sb, - "dbDiscardAG: -EIO"); + jfs_error(bmp->db_ipbmap->i_sb, "-EIO\n"); break; } @@ -1761,7 +1750,7 @@ static int dbFindCtl(struct bmap * bmp, int l2nb, int level, s64 * blkno) if (dcp->leafidx != cpu_to_le32(CTLLEAFIND)) { jfs_error(bmp->db_ipbmap->i_sb, - "dbFindCtl: Corrupt dmapctl page"); + "Corrupt dmapctl page\n"); release_metapage(mp); return -EIO; } @@ -1782,7 +1771,7 @@ static int dbFindCtl(struct bmap * bmp, int l2nb, int level, s64 * blkno) if (rc) { if (lev != level) { jfs_error(bmp->db_ipbmap->i_sb, - "dbFindCtl: dmap inconsistent"); + "dmap inconsistent\n"); return -EIO; } return -ENOSPC; @@ -1906,7 +1895,7 @@ dbAllocCtl(struct bmap * bmp, s64 nblocks, int l2nb, s64 blkno, s64 * results) if (dp->tree.stree[ROOT] != L2BPERDMAP) { release_metapage(mp); jfs_error(bmp->db_ipbmap->i_sb, - "dbAllocCtl: the dmap is not all free"); + "the dmap is not all free\n"); rc = -EIO; goto backout; } @@ -1953,7 +1942,7 @@ dbAllocCtl(struct bmap * bmp, s64 nblocks, int l2nb, s64 blkno, s64 * results) * to indicate that we have leaked blocks. */ jfs_error(bmp->db_ipbmap->i_sb, - "dbAllocCtl: I/O Error: Block Leakage."); + "I/O Error: Block Leakage\n"); continue; } dp = (struct dmap *) mp->data; @@ -1965,8 +1954,7 @@ dbAllocCtl(struct bmap * bmp, s64 nblocks, int l2nb, s64 blkno, s64 * results) * to indicate that we have leaked blocks. */ release_metapage(mp); - jfs_error(bmp->db_ipbmap->i_sb, - "dbAllocCtl: Block Leakage."); + jfs_error(bmp->db_ipbmap->i_sb, "Block Leakage\n"); continue; } @@ -2263,8 +2251,7 @@ static void dbAllocBits(struct bmap * bmp, struct dmap * dp, s64 blkno, for (; nwords > 0; nwords -= nw) { if (leaf[word] < BUDMIN) { jfs_error(bmp->db_ipbmap->i_sb, - "dbAllocBits: leaf page " - "corrupt"); + "leaf page corrupt\n"); break; } @@ -2536,8 +2523,7 @@ dbAdjCtl(struct bmap * bmp, s64 blkno, int newval, int alloc, int level) dcp = (struct dmapctl *) mp->data; if (dcp->leafidx != cpu_to_le32(CTLLEAFIND)) { - jfs_error(bmp->db_ipbmap->i_sb, - "dbAdjCtl: Corrupt dmapctl page"); + jfs_error(bmp->db_ipbmap->i_sb, "Corrupt dmapctl page\n"); release_metapage(mp); return -EIO; } @@ -2638,8 +2624,7 @@ dbAdjCtl(struct bmap * bmp, s64 blkno, int newval, int alloc, int level) assert(level == bmp->db_maxlevel); if (bmp->db_maxfreebud != oldroot) { jfs_error(bmp->db_ipbmap->i_sb, - "dbAdjCtl: the maximum free buddy is " - "not the old root"); + "the maximum free buddy is not the old root\n"); } bmp->db_maxfreebud = dcp->stree[ROOT]; } @@ -3481,7 +3466,7 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks) p = BMAPBLKNO + nbperpage; /* L2 page */ l2mp = read_metapage(ipbmap, p, PSIZE, 0); if (!l2mp) { - jfs_error(ipbmap->i_sb, "dbExtendFS: L2 page could not be read"); + jfs_error(ipbmap->i_sb, "L2 page could not be read\n"); return -EIO; } l2dcp = (struct dmapctl *) l2mp->data; @@ -3646,8 +3631,7 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks) } } /* for each L1 in a L2 */ - jfs_error(ipbmap->i_sb, - "dbExtendFS: function has not returned as expected"); + jfs_error(ipbmap->i_sb, "function has not returned as expected\n"); errout: if (l0mp) release_metapage(l0mp); @@ -3717,7 +3701,7 @@ void dbFinalizeBmap(struct inode *ipbmap) } if (bmp->db_agpref >= bmp->db_numag) { jfs_error(ipbmap->i_sb, - "cannot find ag with average freespace"); + "cannot find ag with average freespace\n"); } } diff --git a/fs/jfs/jfs_dtree.c b/fs/jfs/jfs_dtree.c index 0ddbeceafc62..c89424e71a0a 100644 --- a/fs/jfs/jfs_dtree.c +++ b/fs/jfs/jfs_dtree.c @@ -124,21 +124,21 @@ struct dtsplit { #define DT_PAGE(IP, MP) BT_PAGE(IP, MP, dtpage_t, i_dtroot) /* get page buffer for specified block address */ -#define DT_GETPAGE(IP, BN, MP, SIZE, P, RC)\ -{\ - BT_GETPAGE(IP, BN, MP, dtpage_t, SIZE, P, RC, i_dtroot)\ - if (!(RC))\ - {\ - if (((P)->header.nextindex > (((BN)==0)?DTROOTMAXSLOT:(P)->header.maxslot)) ||\ - ((BN) && ((P)->header.maxslot > DTPAGEMAXSLOT)))\ - {\ - BT_PUTPAGE(MP);\ - jfs_error((IP)->i_sb, "DT_GETPAGE: dtree page corrupt");\ - MP = NULL;\ - RC = -EIO;\ - }\ - }\ -} +#define DT_GETPAGE(IP, BN, MP, SIZE, P, RC) \ +do { \ + BT_GETPAGE(IP, BN, MP, dtpage_t, SIZE, P, RC, i_dtroot); \ + if (!(RC)) { \ + if (((P)->header.nextindex > \ + (((BN) == 0) ? DTROOTMAXSLOT : (P)->header.maxslot)) || \ + ((BN) && ((P)->header.maxslot > DTPAGEMAXSLOT))) { \ + BT_PUTPAGE(MP); \ + jfs_error((IP)->i_sb, \ + "DT_GETPAGE: dtree page corrupt\n"); \ + MP = NULL; \ + RC = -EIO; \ + } \ + } \ +} while (0) /* for consistency */ #define DT_PUTPAGE(MP) BT_PUTPAGE(MP) @@ -776,7 +776,7 @@ int dtSearch(struct inode *ip, struct component_name * key, ino_t * data, /* Something's corrupted, mark filesystem dirty so * chkdsk will fix it. */ - jfs_error(sb, "stack overrun in dtSearch!"); + jfs_error(sb, "stack overrun!\n"); BT_STACK_DUMP(btstack); rc = -EIO; goto out; @@ -3252,8 +3252,7 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir) /* Sanity Check */ if (d_namleft == 0) { jfs_error(ip->i_sb, - "JFS:Dtree error: ino = " - "%ld, bn=%Ld, index = %d", + "JFS:Dtree error: ino = %ld, bn=%lld, index = %d\n", (long)ip->i_ino, (long long)bn, i); @@ -3373,7 +3372,7 @@ static int dtReadFirst(struct inode *ip, struct btstack * btstack) */ if (BT_STACK_FULL(btstack)) { DT_PUTPAGE(mp); - jfs_error(ip->i_sb, "dtReadFirst: btstack overrun"); + jfs_error(ip->i_sb, "btstack overrun\n"); BT_STACK_DUMP(btstack); return -EIO; } diff --git a/fs/jfs/jfs_extent.c b/fs/jfs/jfs_extent.c index e5fe8506ed16..2ae7d59ab10a 100644 --- a/fs/jfs/jfs_extent.c +++ b/fs/jfs/jfs_extent.c @@ -388,7 +388,7 @@ int extHint(struct inode *ip, s64 offset, xad_t * xp) if ((rc == 0) && xlen) { if (xlen != nbperpage) { - jfs_error(ip->i_sb, "extHint: corrupt xtree"); + jfs_error(ip->i_sb, "corrupt xtree\n"); rc = -EIO; } XADaddress(xp, xaddr); diff --git a/fs/jfs/jfs_imap.c b/fs/jfs/jfs_imap.c index f7e042b63ddb..f321986e73d2 100644 --- a/fs/jfs/jfs_imap.c +++ b/fs/jfs/jfs_imap.c @@ -386,7 +386,7 @@ int diRead(struct inode *ip) dp += rel_inode; if (ip->i_ino != le32_to_cpu(dp->di_number)) { - jfs_error(ip->i_sb, "diRead: i_ino != di_number"); + jfs_error(ip->i_sb, "i_ino != di_number\n"); rc = -EIO; } else if (le32_to_cpu(dp->di_nlink) == 0) rc = -ESTALE; @@ -625,7 +625,7 @@ int diWrite(tid_t tid, struct inode *ip) if (!addressPXD(&(jfs_ip->ixpxd)) || (lengthPXD(&(jfs_ip->ixpxd)) != JFS_IP(ipimap)->i_imap->im_nbperiext)) { - jfs_error(ip->i_sb, "diWrite: ixpxd invalid"); + jfs_error(ip->i_sb, "ixpxd invalid\n"); return -EIO; } @@ -893,8 +893,7 @@ int diFree(struct inode *ip) if (iagno >= imap->im_nextiag) { print_hex_dump(KERN_ERR, "imap: ", DUMP_PREFIX_ADDRESS, 16, 4, imap, 32, 0); - jfs_error(ip->i_sb, - "diFree: inum = %d, iagno = %d, nextiag = %d", + jfs_error(ip->i_sb, "inum = %d, iagno = %d, nextiag = %d\n", (uint) inum, iagno, imap->im_nextiag); return -EIO; } @@ -930,15 +929,14 @@ int diFree(struct inode *ip) mask = HIGHORDER >> bitno; if (!(le32_to_cpu(iagp->wmap[extno]) & mask)) { - jfs_error(ip->i_sb, - "diFree: wmap shows inode already free"); + jfs_error(ip->i_sb, "wmap shows inode already free\n"); } if (!addressPXD(&iagp->inoext[extno])) { release_metapage(mp); IREAD_UNLOCK(ipimap); AG_UNLOCK(imap, agno); - jfs_error(ip->i_sb, "diFree: invalid inoext"); + jfs_error(ip->i_sb, "invalid inoext\n"); return -EIO; } @@ -950,7 +948,7 @@ int diFree(struct inode *ip) release_metapage(mp); IREAD_UNLOCK(ipimap); AG_UNLOCK(imap, agno); - jfs_error(ip->i_sb, "diFree: numfree > numinos"); + jfs_error(ip->i_sb, "numfree > numinos\n"); return -EIO; } /* @@ -1199,7 +1197,7 @@ int diFree(struct inode *ip) * for the inode being freed. */ if (iagp->pmap[extno] != 0) { - jfs_error(ip->i_sb, "diFree: the pmap does not show inode free"); + jfs_error(ip->i_sb, "the pmap does not show inode free\n"); } iagp->wmap[extno] = 0; PXDlength(&iagp->inoext[extno], 0); @@ -1518,8 +1516,7 @@ int diAlloc(struct inode *pip, bool dir, struct inode *ip) release_metapage(mp); AG_UNLOCK(imap, agno); jfs_error(ip->i_sb, - "diAlloc: can't find free bit " - "in wmap"); + "can't find free bit in wmap\n"); return -EIO; } @@ -1660,7 +1657,7 @@ diAllocAG(struct inomap * imap, int agno, bool dir, struct inode *ip) numinos = imap->im_agctl[agno].numinos; if (numfree > numinos) { - jfs_error(ip->i_sb, "diAllocAG: numfree > numinos"); + jfs_error(ip->i_sb, "numfree > numinos\n"); return -EIO; } @@ -1811,8 +1808,7 @@ static int diAllocIno(struct inomap * imap, int agno, struct inode *ip) if (!iagp->nfreeinos) { IREAD_UNLOCK(imap->im_ipimap); release_metapage(mp); - jfs_error(ip->i_sb, - "diAllocIno: nfreeinos = 0, but iag on freelist"); + jfs_error(ip->i_sb, "nfreeinos = 0, but iag on freelist\n"); return -EIO; } @@ -1824,7 +1820,7 @@ static int diAllocIno(struct inomap * imap, int agno, struct inode *ip) IREAD_UNLOCK(imap->im_ipimap); release_metapage(mp); jfs_error(ip->i_sb, - "diAllocIno: free inode not found in summary map"); + "free inode not found in summary map\n"); return -EIO; } @@ -1839,7 +1835,7 @@ static int diAllocIno(struct inomap * imap, int agno, struct inode *ip) if (rem >= EXTSPERSUM) { IREAD_UNLOCK(imap->im_ipimap); release_metapage(mp); - jfs_error(ip->i_sb, "diAllocIno: no free extent found"); + jfs_error(ip->i_sb, "no free extent found\n"); return -EIO; } extno = (sword << L2EXTSPERSUM) + rem; @@ -1850,7 +1846,7 @@ static int diAllocIno(struct inomap * imap, int agno, struct inode *ip) if (rem >= INOSPEREXT) { IREAD_UNLOCK(imap->im_ipimap); release_metapage(mp); - jfs_error(ip->i_sb, "diAllocIno: free inode not found"); + jfs_error(ip->i_sb, "free inode not found\n"); return -EIO; } @@ -1936,7 +1932,7 @@ static int diAllocExt(struct inomap * imap, int agno, struct inode *ip) IREAD_LOCK(imap->im_ipimap, RDWRLOCK_IMAP); if ((rc = diIAGRead(imap, iagno, &mp))) { IREAD_UNLOCK(imap->im_ipimap); - jfs_error(ip->i_sb, "diAllocExt: error reading iag"); + jfs_error(ip->i_sb, "error reading iag\n"); return rc; } iagp = (struct iag *) mp->data; @@ -1948,8 +1944,7 @@ static int diAllocExt(struct inomap * imap, int agno, struct inode *ip) if (sword >= SMAPSZ) { release_metapage(mp); IREAD_UNLOCK(imap->im_ipimap); - jfs_error(ip->i_sb, - "diAllocExt: free ext summary map not found"); + jfs_error(ip->i_sb, "free ext summary map not found\n"); return -EIO; } if (~iagp->extsmap[sword]) @@ -1962,7 +1957,7 @@ static int diAllocExt(struct inomap * imap, int agno, struct inode *ip) if (rem >= EXTSPERSUM) { release_metapage(mp); IREAD_UNLOCK(imap->im_ipimap); - jfs_error(ip->i_sb, "diAllocExt: free extent not found"); + jfs_error(ip->i_sb, "free extent not found\n"); return -EIO; } extno = (sword << L2EXTSPERSUM) + rem; @@ -2081,8 +2076,7 @@ static int diAllocBit(struct inomap * imap, struct iag * iagp, int ino) if (bmp) release_metapage(bmp); - jfs_error(imap->im_ipimap->i_sb, - "diAllocBit: iag inconsistent"); + jfs_error(imap->im_ipimap->i_sb, "iag inconsistent\n"); return -EIO; } @@ -2189,7 +2183,7 @@ static int diNewExt(struct inomap * imap, struct iag * iagp, int extno) /* better have free extents. */ if (!iagp->nfreeexts) { - jfs_error(imap->im_ipimap->i_sb, "diNewExt: no free extents"); + jfs_error(imap->im_ipimap->i_sb, "no free extents\n"); return -EIO; } @@ -2261,7 +2255,7 @@ static int diNewExt(struct inomap * imap, struct iag * iagp, int extno) } if (ciagp == NULL) { jfs_error(imap->im_ipimap->i_sb, - "diNewExt: ciagp == NULL"); + "ciagp == NULL\n"); rc = -EIO; goto error_out; } @@ -2498,7 +2492,7 @@ diNewIAG(struct inomap * imap, int *iagnop, int agno, struct metapage ** mpp) IWRITE_UNLOCK(ipimap); IAGFREE_UNLOCK(imap); jfs_error(imap->im_ipimap->i_sb, - "diNewIAG: ipimap->i_size is wrong"); + "ipimap->i_size is wrong\n"); return -EIO; } @@ -2758,8 +2752,7 @@ diUpdatePMap(struct inode *ipimap, iagno = INOTOIAG(inum); /* make sure that the iag is contained within the map */ if (iagno >= imap->im_nextiag) { - jfs_error(ipimap->i_sb, - "diUpdatePMap: the iag is outside the map"); + jfs_error(ipimap->i_sb, "the iag is outside the map\n"); return -EIO; } /* read the iag */ @@ -2788,13 +2781,13 @@ diUpdatePMap(struct inode *ipimap, */ if (!(le32_to_cpu(iagp->wmap[extno]) & mask)) { jfs_error(ipimap->i_sb, - "diUpdatePMap: inode %ld not marked as " - "allocated in wmap!", inum); + "inode %ld not marked as allocated in wmap!\n", + inum); } if (!(le32_to_cpu(iagp->pmap[extno]) & mask)) { jfs_error(ipimap->i_sb, - "diUpdatePMap: inode %ld not marked as " - "allocated in pmap!", inum); + "inode %ld not marked as allocated in pmap!\n", + inum); } /* update the bitmap for the extent of the freed inode */ iagp->pmap[extno] &= cpu_to_le32(~mask); @@ -2809,15 +2802,13 @@ diUpdatePMap(struct inode *ipimap, if (!(le32_to_cpu(iagp->wmap[extno]) & mask)) { release_metapage(mp); jfs_error(ipimap->i_sb, - "diUpdatePMap: the inode is not allocated in " - "the working map"); + "the inode is not allocated in the working map\n"); return -EIO; } if ((le32_to_cpu(iagp->pmap[extno]) & mask) != 0) { release_metapage(mp); jfs_error(ipimap->i_sb, - "diUpdatePMap: the inode is not free in the " - "persistent map"); + "the inode is not free in the persistent map\n"); return -EIO; } /* update the bitmap for the extent of the allocated inode */ @@ -2909,8 +2900,7 @@ int diExtendFS(struct inode *ipimap, struct inode *ipbmap) iagp = (struct iag *) bp->data; if (le32_to_cpu(iagp->iagnum) != i) { release_metapage(bp); - jfs_error(ipimap->i_sb, - "diExtendFs: unexpected value of iagnum"); + jfs_error(ipimap->i_sb, "unexpected value of iagnum\n"); return -EIO; } @@ -2986,8 +2976,7 @@ int diExtendFS(struct inode *ipimap, struct inode *ipbmap) if (xnuminos != atomic_read(&imap->im_numinos) || xnumfree != atomic_read(&imap->im_numfree)) { - jfs_error(ipimap->i_sb, - "diExtendFs: numinos or numfree incorrect"); + jfs_error(ipimap->i_sb, "numinos or numfree incorrect\n"); return -EIO; } diff --git a/fs/jfs/jfs_metapage.c b/fs/jfs/jfs_metapage.c index 6740d34cd82b..6052c0d39382 100644 --- a/fs/jfs/jfs_metapage.c +++ b/fs/jfs/jfs_metapage.c @@ -646,7 +646,7 @@ struct metapage *__get_metapage(struct inode *inode, unsigned long lblock, if (mp) { if (mp->logical_size != size) { jfs_error(inode->i_sb, - "__get_metapage: mp->logical_size != size"); + "get_mp->logical_size != size\n"); jfs_err("logical_size = %d, size = %d", mp->logical_size, size); dump_stack(); @@ -657,8 +657,7 @@ struct metapage *__get_metapage(struct inode *inode, unsigned long lblock, if (test_bit(META_discard, &mp->flag)) { if (!new) { jfs_error(inode->i_sb, - "__get_metapage: using a " - "discarded metapage"); + "using a discarded metapage\n"); discard_metapage(mp); goto unlock; } diff --git a/fs/jfs/jfs_superblock.h b/fs/jfs/jfs_superblock.h index 884fc21ab8ee..04847b8d3070 100644 --- a/fs/jfs/jfs_superblock.h +++ b/fs/jfs/jfs_superblock.h @@ -108,6 +108,7 @@ struct jfs_superblock { extern int readSuper(struct super_block *, struct buffer_head **); extern int updateSuper(struct super_block *, uint); +__printf(2, 3) extern void jfs_error(struct super_block *, const char *, ...); extern int jfs_mount(struct super_block *); extern int jfs_mount_rw(struct super_block *, int); diff --git a/fs/jfs/jfs_txnmgr.c b/fs/jfs/jfs_txnmgr.c index 5fcc02eaa64c..564c4f279ac6 100644 --- a/fs/jfs/jfs_txnmgr.c +++ b/fs/jfs/jfs_txnmgr.c @@ -2684,7 +2684,7 @@ void txAbort(tid_t tid, int dirty) * mark filesystem dirty */ if (dirty) - jfs_error(tblk->sb, "txAbort"); + jfs_error(tblk->sb, "\n"); return; } diff --git a/fs/jfs/jfs_xtree.c b/fs/jfs/jfs_xtree.c index 6c50871e6220..5ad7748860ce 100644 --- a/fs/jfs/jfs_xtree.c +++ b/fs/jfs/jfs_xtree.c @@ -64,22 +64,23 @@ /* get page buffer for specified block address */ /* ToDo: Replace this ugly macro with a function */ -#define XT_GETPAGE(IP, BN, MP, SIZE, P, RC)\ -{\ - BT_GETPAGE(IP, BN, MP, xtpage_t, SIZE, P, RC, i_xtroot)\ - if (!(RC))\ - {\ - if ((le16_to_cpu((P)->header.nextindex) < XTENTRYSTART) ||\ - (le16_to_cpu((P)->header.nextindex) > le16_to_cpu((P)->header.maxentry)) ||\ - (le16_to_cpu((P)->header.maxentry) > (((BN)==0)?XTROOTMAXSLOT:PSIZE>>L2XTSLOTSIZE)))\ - {\ - jfs_error((IP)->i_sb, "XT_GETPAGE: xtree page corrupt");\ - BT_PUTPAGE(MP);\ - MP = NULL;\ - RC = -EIO;\ - }\ - }\ -} +#define XT_GETPAGE(IP, BN, MP, SIZE, P, RC) \ +do { \ + BT_GETPAGE(IP, BN, MP, xtpage_t, SIZE, P, RC, i_xtroot); \ + if (!(RC)) { \ + if ((le16_to_cpu((P)->header.nextindex) < XTENTRYSTART) || \ + (le16_to_cpu((P)->header.nextindex) > \ + le16_to_cpu((P)->header.maxentry)) || \ + (le16_to_cpu((P)->header.maxentry) > \ + (((BN) == 0) ? XTROOTMAXSLOT : PSIZE >> L2XTSLOTSIZE))) { \ + jfs_error((IP)->i_sb, \ + "XT_GETPAGE: xtree page corrupt\n"); \ + BT_PUTPAGE(MP); \ + MP = NULL; \ + RC = -EIO; \ + } \ + } \ +} while (0) /* for consistency */ #define XT_PUTPAGE(MP) BT_PUTPAGE(MP) @@ -499,7 +500,7 @@ static int xtSearch(struct inode *ip, s64 xoff, s64 *nextp, /* push (bn, index) of the parent page/entry */ if (BT_STACK_FULL(btstack)) { - jfs_error(ip->i_sb, "stack overrun in xtSearch!"); + jfs_error(ip->i_sb, "stack overrun!\n"); XT_PUTPAGE(mp); return -EIO; } @@ -1385,7 +1386,7 @@ int xtExtend(tid_t tid, /* transaction id */ if (cmp != 0) { XT_PUTPAGE(mp); - jfs_error(ip->i_sb, "xtExtend: xtSearch did not find extent"); + jfs_error(ip->i_sb, "xtSearch did not find extent\n"); return -EIO; } @@ -1393,7 +1394,7 @@ int xtExtend(tid_t tid, /* transaction id */ xad = &p->xad[index]; if ((offsetXAD(xad) + lengthXAD(xad)) != xoff) { XT_PUTPAGE(mp); - jfs_error(ip->i_sb, "xtExtend: extension is not contiguous"); + jfs_error(ip->i_sb, "extension is not contiguous\n"); return -EIO; } @@ -1552,7 +1553,7 @@ printf("xtTailgate: nxoff:0x%lx nxlen:0x%x nxaddr:0x%lx\n", if (cmp != 0) { XT_PUTPAGE(mp); - jfs_error(ip->i_sb, "xtTailgate: couldn't find extent"); + jfs_error(ip->i_sb, "couldn't find extent\n"); return -EIO; } @@ -1560,8 +1561,7 @@ printf("xtTailgate: nxoff:0x%lx nxlen:0x%x nxaddr:0x%lx\n", nextindex = le16_to_cpu(p->header.nextindex); if (index != nextindex - 1) { XT_PUTPAGE(mp); - jfs_error(ip->i_sb, - "xtTailgate: the entry found is not the last entry"); + jfs_error(ip->i_sb, "the entry found is not the last entry\n"); return -EIO; } @@ -1734,7 +1734,7 @@ int xtUpdate(tid_t tid, struct inode *ip, xad_t * nxad) if (cmp != 0) { XT_PUTPAGE(mp); - jfs_error(ip->i_sb, "xtUpdate: Could not find extent"); + jfs_error(ip->i_sb, "Could not find extent\n"); return -EIO; } @@ -1758,7 +1758,7 @@ int xtUpdate(tid_t tid, struct inode *ip, xad_t * nxad) (nxoff + nxlen > xoff + xlen)) { XT_PUTPAGE(mp); jfs_error(ip->i_sb, - "xtUpdate: nXAD in not completely contained within XAD"); + "nXAD in not completely contained within XAD\n"); return -EIO; } @@ -1907,7 +1907,7 @@ int xtUpdate(tid_t tid, struct inode *ip, xad_t * nxad) if (xoff >= nxoff) { XT_PUTPAGE(mp); - jfs_error(ip->i_sb, "xtUpdate: xoff >= nxoff"); + jfs_error(ip->i_sb, "xoff >= nxoff\n"); return -EIO; } /* #endif _JFS_WIP_COALESCE */ @@ -2048,14 +2048,13 @@ int xtUpdate(tid_t tid, struct inode *ip, xad_t * nxad) if (cmp != 0) { XT_PUTPAGE(mp); - jfs_error(ip->i_sb, "xtUpdate: xtSearch failed"); + jfs_error(ip->i_sb, "xtSearch failed\n"); return -EIO; } if (index0 != index) { XT_PUTPAGE(mp); - jfs_error(ip->i_sb, - "xtUpdate: unexpected value of index"); + jfs_error(ip->i_sb, "unexpected value of index\n"); return -EIO; } } @@ -3650,7 +3649,7 @@ s64 xtTruncate(tid_t tid, struct inode *ip, s64 newsize, int flag) getChild: /* save current parent entry for the child page */ if (BT_STACK_FULL(&btstack)) { - jfs_error(ip->i_sb, "stack overrun in xtTruncate!"); + jfs_error(ip->i_sb, "stack overrun!\n"); XT_PUTPAGE(mp); return -EIO; } @@ -3751,8 +3750,7 @@ s64 xtTruncate_pmap(tid_t tid, struct inode *ip, s64 committed_size) if (cmp != 0) { XT_PUTPAGE(mp); - jfs_error(ip->i_sb, - "xtTruncate_pmap: did not find extent"); + jfs_error(ip->i_sb, "did not find extent\n"); return -EIO; } } else { @@ -3851,7 +3849,7 @@ s64 xtTruncate_pmap(tid_t tid, struct inode *ip, s64 committed_size) getChild: /* save current parent entry for the child page */ if (BT_STACK_FULL(&btstack)) { - jfs_error(ip->i_sb, "stack overrun in xtTruncate_pmap!"); + jfs_error(ip->i_sb, "stack overrun!\n"); XT_PUTPAGE(mp); return -EIO; } diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c index 3b91a7ad6086..eb9799bb367c 100644 --- a/fs/jfs/namei.c +++ b/fs/jfs/namei.c @@ -1176,7 +1176,7 @@ static int jfs_rename(struct inode *old_dir, struct dentry *old_dentry, if (!S_ISDIR(old_ip->i_mode) && new_ip) IWRITE_UNLOCK(new_ip); jfs_error(new_ip->i_sb, - "jfs_rename: new_ip->i_nlink != 0"); + "new_ip->i_nlink != 0\n"); return -EIO; } tblk = tid_to_tblock(tid); diff --git a/fs/jfs/resize.c b/fs/jfs/resize.c index 8d0c1c7c0820..90b3bc21e9b0 100644 --- a/fs/jfs/resize.c +++ b/fs/jfs/resize.c @@ -530,7 +530,7 @@ int jfs_extendfs(struct super_block *sb, s64 newLVSize, int newLogSize) goto resume; error_out: - jfs_error(sb, "jfs_extendfs"); + jfs_error(sb, "\n"); resume: /* diff --git a/fs/jfs/super.c b/fs/jfs/super.c index 788e0a9c1fb0..6669aa2042c3 100644 --- a/fs/jfs/super.c +++ b/fs/jfs/super.c @@ -92,16 +92,20 @@ static void jfs_handle_error(struct super_block *sb) /* nothing is done for continue beyond marking the superblock dirty */ } -void jfs_error(struct super_block *sb, const char * function, ...) +void jfs_error(struct super_block *sb, const char *fmt, ...) { - static char error_buf[256]; + struct va_format vaf; va_list args; - va_start(args, function); - vsnprintf(error_buf, sizeof(error_buf), function, args); - va_end(args); + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; - pr_err("ERROR: (device %s): %s\n", sb->s_id, error_buf); + pr_err("ERROR: (device %s): %pf: %pV\n", + sb->s_id, __builtin_return_address(0), &vaf); + + va_end(args); jfs_handle_error(sb); } @@ -617,7 +621,7 @@ static int jfs_freeze(struct super_block *sb) txQuiesce(sb); rc = lmLogShutdown(log); if (rc) { - jfs_error(sb, "jfs_freeze: lmLogShutdown failed"); + jfs_error(sb, "lmLogShutdown failed\n"); /* let operations fail rather than hang */ txResume(sb); @@ -646,12 +650,12 @@ static int jfs_unfreeze(struct super_block *sb) if (!(sb->s_flags & MS_RDONLY)) { rc = updateSuper(sb, FM_MOUNT); if (rc) { - jfs_error(sb, "jfs_unfreeze: updateSuper failed"); + jfs_error(sb, "updateSuper failed\n"); goto out; } rc = lmLogInit(log); if (rc) - jfs_error(sb, "jfs_unfreeze: lmLogInit failed"); + jfs_error(sb, "lmLogInit failed\n"); out: txResume(sb); } diff --git a/fs/jfs/xattr.c b/fs/jfs/xattr.c index 86ce028f2566..d3472f4cd530 100644 --- a/fs/jfs/xattr.c +++ b/fs/jfs/xattr.c @@ -382,7 +382,7 @@ static int ea_read(struct inode *ip, struct jfs_ea_list *ealist) nbytes = sizeDXD(&ji->ea); if (!nbytes) { - jfs_error(sb, "ea_read: nbytes is 0"); + jfs_error(sb, "nbytes is 0\n"); return -EIO; } @@ -482,7 +482,7 @@ static int ea_get(struct inode *inode, struct ea_buffer *ea_buf, int min_size) current_blocks = 0; } else { if (!(ji->ea.flag & DXD_EXTENT)) { - jfs_error(sb, "ea_get: invalid ea.flag)"); + jfs_error(sb, "invalid ea.flag\n"); return -EIO; } current_blocks = (ea_size + sb->s_blocksize - 1) >> -- cgit v1.2.3 From 8b1fce04dc2a2210f050484afa85acc3a81cfbba Mon Sep 17 00:00:00 2001 From: Gu Zheng Date: Sat, 25 May 2013 21:48:31 +0800 Subject: PCI: Convert alloc_pci_dev(void) to pci_alloc_dev(bus) Use the new pci_alloc_dev(bus) to replace the existing using of alloc_pci_dev(void). [bhelgaas: drop pci_bus ref later in pci_release_dev()] Signed-off-by: Gu Zheng Signed-off-by: Jiang Liu Signed-off-by: Bjorn Helgaas Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: "David S. Miller" Cc: David Airlie Cc: Neela Syam Kolli Cc: "James E.J. Bottomley" Cc: Yinghai Lu Cc: Greg Kroah-Hartman Cc: Andrew Morton --- arch/powerpc/kernel/pci_of_scan.c | 3 +-- arch/sparc/kernel/pci.c | 3 +-- drivers/char/agp/alpha-agp.c | 2 +- drivers/char/agp/parisc-agp.c | 2 +- drivers/pci/iov.c | 8 +++++--- drivers/pci/probe.c | 5 +++-- drivers/scsi/megaraid.c | 2 +- 7 files changed, 13 insertions(+), 12 deletions(-) diff --git a/arch/powerpc/kernel/pci_of_scan.c b/arch/powerpc/kernel/pci_of_scan.c index 2a67e9baa59f..24d01c4eac5c 100644 --- a/arch/powerpc/kernel/pci_of_scan.c +++ b/arch/powerpc/kernel/pci_of_scan.c @@ -128,7 +128,7 @@ struct pci_dev *of_create_pci_dev(struct device_node *node, const char *type; struct pci_slot *slot; - dev = alloc_pci_dev(); + dev = pci_alloc_dev(bus); if (!dev) return NULL; type = of_get_property(node, "device_type", NULL); @@ -137,7 +137,6 @@ struct pci_dev *of_create_pci_dev(struct device_node *node, pr_debug(" create device, devfn: %x, type: %s\n", devfn, type); - dev->bus = bus; dev->dev.of_node = of_node_get(node); dev->dev.parent = bus->bridge; dev->dev.bus = &pci_bus_type; diff --git a/arch/sparc/kernel/pci.c b/arch/sparc/kernel/pci.c index baf4366e2d6a..e5871fb455b3 100644 --- a/arch/sparc/kernel/pci.c +++ b/arch/sparc/kernel/pci.c @@ -254,7 +254,7 @@ static struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm, const char *type; u32 class; - dev = alloc_pci_dev(); + dev = pci_alloc_dev(bus); if (!dev) return NULL; @@ -281,7 +281,6 @@ static struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm, printk(" create device, devfn: %x, type: %s\n", devfn, type); - dev->bus = bus; dev->sysdata = node; dev->dev.parent = bus->bridge; dev->dev.bus = &pci_bus_type; diff --git a/drivers/char/agp/alpha-agp.c b/drivers/char/agp/alpha-agp.c index dd84af4d4f7e..199b8e99f7d7 100644 --- a/drivers/char/agp/alpha-agp.c +++ b/drivers/char/agp/alpha-agp.c @@ -174,7 +174,7 @@ alpha_core_agp_setup(void) /* * Build a fake pci_dev struct */ - pdev = alloc_pci_dev(); + pdev = pci_alloc_dev(NULL); if (!pdev) return -ENOMEM; pdev->vendor = 0xffff; diff --git a/drivers/char/agp/parisc-agp.c b/drivers/char/agp/parisc-agp.c index 94821ab01c6d..bf5d2477cb77 100644 --- a/drivers/char/agp/parisc-agp.c +++ b/drivers/char/agp/parisc-agp.c @@ -333,7 +333,7 @@ parisc_agp_setup(void __iomem *ioc_hpa, void __iomem *lba_hpa) struct agp_bridge_data *bridge; int error = 0; - fake_bridge_dev = alloc_pci_dev(); + fake_bridge_dev = pci_alloc_dev(NULL); if (!fake_bridge_dev) { error = -ENOMEM; goto fail; diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index c93071d428f5..2652ca00fae7 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -75,18 +75,20 @@ static int virtfn_add(struct pci_dev *dev, int id, int reset) struct pci_dev *virtfn; struct resource *res; struct pci_sriov *iov = dev->sriov; + struct pci_bus *bus; - virtfn = alloc_pci_dev(); + virtfn = pci_alloc_dev(NULL); if (!virtfn) return -ENOMEM; mutex_lock(&iov->dev->sriov->lock); - virtfn->bus = virtfn_add_bus(dev->bus, virtfn_bus(dev, id)); - if (!virtfn->bus) { + bus = virtfn_add_bus(dev->bus, virtfn_bus(dev, id)); + if (!bus) { kfree(virtfn); mutex_unlock(&iov->dev->sriov->lock); return -ENOMEM; } + virtfn->bus = pci_bus_get(bus); virtfn->devfn = virtfn_devfn(dev, id); virtfn->vendor = dev->vendor; pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_DID, &virtfn->device); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index d47ce1400c26..ed5ce185eed9 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1132,6 +1132,7 @@ static void pci_release_dev(struct device *dev) pci_dev = to_pci_dev(dev); pci_release_capabilities(pci_dev); pci_release_of_node(pci_dev); + pci_bus_put(pci_dev->bus); kfree(pci_dev); } @@ -1270,11 +1271,10 @@ static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn) if (!pci_bus_read_dev_vendor_id(bus, devfn, &l, 60*1000)) return NULL; - dev = alloc_pci_dev(); + dev = pci_alloc_dev(bus); if (!dev) return NULL; - dev->bus = bus; dev->devfn = devfn; dev->vendor = l & 0xffff; dev->device = (l >> 16) & 0xffff; @@ -1282,6 +1282,7 @@ static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn) pci_set_of_node(dev); if (pci_setup_device(dev)) { + pci_bus_put(dev->bus); kfree(dev); return NULL; } diff --git a/drivers/scsi/megaraid.c b/drivers/scsi/megaraid.c index 846f475f62c1..90c95a3385d1 100644 --- a/drivers/scsi/megaraid.c +++ b/drivers/scsi/megaraid.c @@ -2026,7 +2026,7 @@ megaraid_abort_and_reset(adapter_t *adapter, Scsi_Cmnd *cmd, int aor) static inline int make_local_pdev(adapter_t *adapter, struct pci_dev **pdev) { - *pdev = alloc_pci_dev(); + *pdev = pci_alloc_dev(NULL); if( *pdev == NULL ) return -1; -- cgit v1.2.3 From ef1b460d1bab7e5b04c34f88c3dfa042528e7c27 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 1 Jun 2013 17:17:04 +0200 Subject: drm/i915: set default value for config->pixel_multiplier This way we can simplify the code quite a bit. Also add a WARN in the sdvo code to complain about a bogus value and kill the readout code in intel_ddi.c that Jesse sneaked in. HW state readout for the pixel multiplier will work a bit differently in the end. v2: Rebase on top of the fdi pixel mutliplier handling fix. Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ddi.c | 1 - drivers/gpu/drm/i915/intel_display.c | 29 ++++++++++------------------- drivers/gpu/drm/i915/intel_sdvo.c | 1 + 3 files changed, 11 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 486c46bf5ccf..224ce25129ce 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -1279,7 +1279,6 @@ static void intel_ddi_get_config(struct intel_encoder *encoder, flags |= DRM_MODE_FLAG_NVSYNC; pipe_config->adjusted_mode.flags |= flags; - pipe_config->pixel_multiplier = 1; } static void intel_ddi_destroy(struct drm_encoder *encoder) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 827d7ca2d9d9..ca90d36fd650 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4003,8 +4003,7 @@ retry: link_bw = intel_fdi_link_freq(dev) * MHz(100)/KHz(1)/10; fdi_dotclock = adjusted_mode->clock; - if (pipe_config->pixel_multiplier > 1) - fdi_dotclock /= pipe_config->pixel_multiplier; + fdi_dotclock /= pipe_config->pixel_multiplier; lane = ironlake_get_lanes_required(fdi_dotclock, link_bw, pipe_config->pipe_bpp); @@ -4458,11 +4457,8 @@ static void vlv_update_pll(struct intel_crtc *crtc) if (wait_for(((I915_READ(DPLL(pipe)) & DPLL_LOCK_VLV) == DPLL_LOCK_VLV), 1)) DRM_ERROR("DPLL %d failed to lock\n", pipe); - dpll_md = 0; - if (crtc->config.pixel_multiplier > 1) { - dpll_md = (crtc->config.pixel_multiplier - 1) - << DPLL_MD_UDI_MULTIPLIER_SHIFT; - } + dpll_md = (crtc->config.pixel_multiplier - 1) + << DPLL_MD_UDI_MULTIPLIER_SHIFT; I915_WRITE(DPLL_MD(pipe), dpll_md); POSTING_READ(DPLL_MD(pipe)); @@ -4496,8 +4492,7 @@ static void i9xx_update_pll(struct intel_crtc *crtc, else dpll |= DPLLB_MODE_DAC_SERIAL; - if ((crtc->config.pixel_multiplier > 1) && - (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev))) { + if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev)) { dpll |= (crtc->config.pixel_multiplier - 1) << SDVO_MULTIPLIER_SHIFT_HIRES; } @@ -4560,11 +4555,8 @@ static void i9xx_update_pll(struct intel_crtc *crtc, udelay(150); if (INTEL_INFO(dev)->gen >= 4) { - u32 dpll_md = 0; - if (crtc->config.pixel_multiplier > 1) { - dpll_md = (crtc->config.pixel_multiplier - 1) - << DPLL_MD_UDI_MULTIPLIER_SHIFT; - } + u32 dpll_md = (crtc->config.pixel_multiplier - 1) + << DPLL_MD_UDI_MULTIPLIER_SHIFT; I915_WRITE(DPLL_MD(pipe), dpll_md); } else { /* The pixel multiplier can only be updated once the @@ -5613,10 +5605,8 @@ static uint32_t ironlake_compute_dpll(struct intel_crtc *intel_crtc, else dpll |= DPLLB_MODE_DAC_SERIAL; - if (intel_crtc->config.pixel_multiplier > 1) { - dpll |= (intel_crtc->config.pixel_multiplier - 1) - << PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT; - } + dpll |= (intel_crtc->config.pixel_multiplier - 1) + << PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT; if (is_sdvo) dpll |= DPLL_DVO_HIGH_SPEED; @@ -7783,8 +7773,9 @@ intel_modeset_pipe_config(struct drm_crtc *crtc, goto fail; encoder_retry: - /* Ensure the port clock default is reset when retrying. */ + /* Ensure the port clock defaults are reset when retrying. */ pipe_config->port_clock = 0; + pipe_config->pixel_multiplier = 1; /* Pass our mode to the connectors and the CRTC to give them a chance to * adjust it according to limitations or connector properties, and also diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 7068195376ef..f4588a2c65ac 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1219,6 +1219,7 @@ static void intel_sdvo_mode_set(struct intel_encoder *intel_encoder) switch (intel_crtc->config.pixel_multiplier) { default: + WARN(1, "unknown pixel mutlipler specified\n"); 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; -- cgit v1.2.3 From 06d6b3cbdf94bc37732df83e7c25774370411a56 Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Wed, 5 Jun 2013 17:15:11 +0800 Subject: cpuset: remove redundant check in cpuset_cpus_allowed_fallback() task_cs() will never return NULL. Signed-off-by: Li Zefan Signed-off-by: Tejun Heo --- kernel/cpuset.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/kernel/cpuset.c b/kernel/cpuset.c index 64b3f791bbe5..f0c884a0e574 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -2253,8 +2253,7 @@ void cpuset_cpus_allowed_fallback(struct task_struct *tsk) rcu_read_lock(); cs = task_cs(tsk); - if (cs) - do_set_cpus_allowed(tsk, cs->cpus_allowed); + do_set_cpus_allowed(tsk, cs->cpus_allowed); rcu_read_unlock(); /* -- cgit v1.2.3 From 40df2deb50570b288b7067b111af0aa9ca640e6f Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Wed, 5 Jun 2013 17:15:23 +0800 Subject: cpuset: cleanup guarantee_online_{cpus|mems}() - We never pass a NULL @cs to these functions. - The top cpuset always has some online cpus/mems. Signed-off-by: Li Zefan Signed-off-by: Tejun Heo --- kernel/cpuset.c | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/kernel/cpuset.c b/kernel/cpuset.c index f0c884a0e574..d753837cca33 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -304,53 +304,38 @@ static struct file_system_type cpuset_fs_type = { /* * Return in pmask the portion of a cpusets's cpus_allowed that * are online. If none are online, walk up the cpuset hierarchy - * until we find one that does have some online cpus. If we get - * all the way to the top and still haven't found any online cpus, - * return cpu_online_mask. Or if passed a NULL cs from an exit'ing - * task, return cpu_online_mask. + * until we find one that does have some online cpus. The top + * cpuset always has some cpus online. * * One way or another, we guarantee to return some non-empty subset * of cpu_online_mask. * * Call with callback_mutex held. */ - static void guarantee_online_cpus(const struct cpuset *cs, struct cpumask *pmask) { - while (cs && !cpumask_intersects(cs->cpus_allowed, cpu_online_mask)) + while (!cpumask_intersects(cs->cpus_allowed, cpu_online_mask)) cs = parent_cs(cs); - if (cs) - cpumask_and(pmask, cs->cpus_allowed, cpu_online_mask); - else - cpumask_copy(pmask, cpu_online_mask); - BUG_ON(!cpumask_intersects(pmask, cpu_online_mask)); + cpumask_and(pmask, cs->cpus_allowed, cpu_online_mask); } /* * Return in *pmask the portion of a cpusets's mems_allowed that * are online, with memory. If none are online with memory, walk * up the cpuset hierarchy until we find one that does have some - * online mems. If we get all the way to the top and still haven't - * found any online mems, return node_states[N_MEMORY]. + * online mems. The top cpuset always has some mems online. * * One way or another, we guarantee to return some non-empty subset * of node_states[N_MEMORY]. * * Call with callback_mutex held. */ - static void guarantee_online_mems(const struct cpuset *cs, nodemask_t *pmask) { - while (cs && !nodes_intersects(cs->mems_allowed, - node_states[N_MEMORY])) + while (!nodes_intersects(cs->mems_allowed, node_states[N_MEMORY])) cs = parent_cs(cs); - if (cs) - nodes_and(*pmask, cs->mems_allowed, - node_states[N_MEMORY]); - else - *pmask = node_states[N_MEMORY]; - BUG_ON(!nodes_intersects(*pmask, node_states[N_MEMORY])); + nodes_and(*pmask, cs->mems_allowed, node_states[N_MEMORY]); } /* -- cgit v1.2.3 From 67bd2c59850de20d0ecdc8084cbbfe34e53b6804 Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Wed, 5 Jun 2013 17:15:35 +0800 Subject: cpuset: remove unnecessary variable in cpuset_attach() We can just use oldcs->mems_allowed. Signed-off-by: Li Zefan Signed-off-by: Tejun Heo --- kernel/cpuset.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/kernel/cpuset.c b/kernel/cpuset.c index d753837cca33..dbef832e5e2d 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -1407,8 +1407,7 @@ static cpumask_var_t cpus_attach; static void cpuset_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) { - /* static bufs protected by cpuset_mutex */ - static nodemask_t cpuset_attach_nodemask_from; + /* static buf protected by cpuset_mutex */ static nodemask_t cpuset_attach_nodemask_to; struct mm_struct *mm; struct task_struct *task; @@ -1442,13 +1441,12 @@ static void cpuset_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) * Change mm, possibly for multiple threads in a threadgroup. This is * expensive and may sleep. */ - cpuset_attach_nodemask_from = oldcs->mems_allowed; cpuset_attach_nodemask_to = cs->mems_allowed; mm = get_task_mm(leader); if (mm) { mpol_rebind_mm(mm, &cpuset_attach_nodemask_to); if (is_memory_migrate(cs)) - cpuset_migrate_mm(mm, &cpuset_attach_nodemask_from, + cpuset_migrate_mm(mm, &oldcs->mems_allowed, &cpuset_attach_nodemask_to); mmput(mm); } -- cgit v1.2.3 From 249cc86db7492dc8de1d2eddebc6bcc4ab2a8e9e Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Wed, 5 Jun 2013 17:15:48 +0800 Subject: cpuset: remove cpuset_test_cpumask() The test is done in set_cpus_allowed_ptr(), so it's redundant. Signed-off-by: Li Zefan Signed-off-by: Tejun Heo --- kernel/cpuset.c | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/kernel/cpuset.c b/kernel/cpuset.c index dbef832e5e2d..51f8e1d5a2a9 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -783,23 +783,6 @@ void rebuild_sched_domains(void) mutex_unlock(&cpuset_mutex); } -/** - * cpuset_test_cpumask - test a task's cpus_allowed versus its cpuset's - * @tsk: task to test - * @scan: struct cgroup_scanner contained in its struct cpuset_hotplug_scanner - * - * Call with cpuset_mutex held. May take callback_mutex during call. - * Called for each task in a cgroup by cgroup_scan_tasks(). - * Return nonzero if this tasks's cpus_allowed mask should be changed (in other - * words, if its mask is not equal to its cpuset's mask). - */ -static int cpuset_test_cpumask(struct task_struct *tsk, - struct cgroup_scanner *scan) -{ - return !cpumask_equal(&tsk->cpus_allowed, - (cgroup_cs(scan->cg))->cpus_allowed); -} - /** * cpuset_change_cpumask - make a task's cpus_allowed the same as its cpuset's * @tsk: task to test @@ -835,7 +818,7 @@ static void update_tasks_cpumask(struct cpuset *cs, struct ptr_heap *heap) struct cgroup_scanner scan; scan.cg = cs->css.cgroup; - scan.test_task = cpuset_test_cpumask; + scan.test_task = NULL; scan.process_task = cpuset_change_cpumask; scan.heap = heap; cgroup_scan_tasks(&scan); -- cgit v1.2.3 From a73456f37b9dbc917398387d0cba926b4455b70f Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Wed, 5 Jun 2013 17:15:59 +0800 Subject: cpuset: re-structure update_cpumask() a bit Check if cpus_allowed is to be changed before calling validate_change(). This won't change any behavior, but later it will allow us to do this: # mkdir /cpuset/child # echo $$ > /cpuset/child/tasks /* empty cpuset */ # echo > /cpuset/child/cpuset.cpus /* do nothing, won't fail */ Without this patch, the last operation will fail. Signed-off-by: Li Zefan Signed-off-by: Tejun Heo --- kernel/cpuset.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/kernel/cpuset.c b/kernel/cpuset.c index 51f8e1d5a2a9..535dce685eec 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -856,14 +856,15 @@ static int update_cpumask(struct cpuset *cs, struct cpuset *trialcs, if (!cpumask_subset(trialcs->cpus_allowed, cpu_active_mask)) return -EINVAL; } - retval = validate_change(cs, trialcs); - if (retval < 0) - return retval; /* Nothing to do if the cpus didn't change */ if (cpumask_equal(cs->cpus_allowed, trialcs->cpus_allowed)) return 0; + retval = validate_change(cs, trialcs); + if (retval < 0) + return retval; + retval = heap_init(&heap, PAGE_SIZE, GFP_KERNEL, NULL); if (retval) return retval; -- cgit v1.2.3 From 1c2d721a7063bc3b8668de07f8d14dfbac4bf515 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sun, 2 Jun 2013 23:05:03 +0000 Subject: i2c: intel-mid: remove obsolete driver Moorestown support is removed from kernel and Medfield is supported by i2c-designware-pci. But i2c-intel-mid is still in upstream and community resources are wasted to maintain it. Signed-off-by: Andy Shevchenko Signed-off-by: Wolfram Sang --- drivers/i2c/busses/Kconfig | 10 - drivers/i2c/busses/Makefile | 1 - drivers/i2c/busses/i2c-intel-mid.c | 1121 ------------------------------------ 3 files changed, 1132 deletions(-) delete mode 100644 drivers/i2c/busses/i2c-intel-mid.c diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 631736e2e7ed..94364d41686f 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -474,16 +474,6 @@ config I2C_IMX This driver can also be built as a module. If so, the module will be called i2c-imx. -config I2C_INTEL_MID - tristate "Intel Moorestown/Medfield Platform I2C controller" - depends on PCI - help - Say Y here if you have an Intel Moorestown/Medfield platform I2C - controller. - - This support is also available as a module. If so, the module - will be called i2c-intel-mid. - config I2C_IOP3XX tristate "Intel IOPx3xx and IXP4xx on-chip I2C interface" depends on ARCH_IOP32X || ARCH_IOP33X || ARCH_IXP4XX || ARCH_IOP13XX diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 8f4fc23b85b1..3e07dc53eeae 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -46,7 +46,6 @@ obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o obj-$(CONFIG_I2C_HIGHLANDER) += i2c-highlander.o obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.o obj-$(CONFIG_I2C_IMX) += i2c-imx.o -obj-$(CONFIG_I2C_INTEL_MID) += i2c-intel-mid.o obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o obj-$(CONFIG_I2C_MPC) += i2c-mpc.o obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o diff --git a/drivers/i2c/busses/i2c-intel-mid.c b/drivers/i2c/busses/i2c-intel-mid.c deleted file mode 100644 index 0fb659726ffc..000000000000 --- a/drivers/i2c/busses/i2c-intel-mid.c +++ /dev/null @@ -1,1121 +0,0 @@ -/* - * Support for Moorestown/Medfield I2C chip - * - * Copyright (c) 2009 Intel Corporation. - * Copyright (c) 2009 Synopsys. Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, version - * 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., 51 - * Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRIVER_NAME "i2c-intel-mid" -#define VERSION "Version 0.5ac2" -#define PLATFORM "Moorestown/Medfield" - -/* Tables use: 0 Moorestown, 1 Medfield */ -#define NUM_PLATFORMS 2 -enum platform_enum { - MOORESTOWN = 0, - MEDFIELD = 1, -}; - -enum mid_i2c_status { - STATUS_IDLE = 0, - STATUS_READ_START, - STATUS_READ_IN_PROGRESS, - STATUS_READ_SUCCESS, - STATUS_WRITE_START, - STATUS_WRITE_SUCCESS, - STATUS_XFER_ABORT, - STATUS_STANDBY -}; - -/** - * struct intel_mid_i2c_private - per device I²C context - * @adap: core i2c layer adapter information - * @dev: device reference for power management - * @base: register base - * @speed: speed mode for this port - * @complete: completion object for transaction wait - * @abort: reason for last abort - * @rx_buf: pointer into working receive buffer - * @rx_buf_len: receive buffer length - * @status: adapter state machine - * @msg: the message we are currently processing - * @platform: the MID device type we are part of - * @lock: transaction serialization - * - * We allocate one of these per device we discover, it holds the core - * i2c layer objects and the data we need to track privately. - */ -struct intel_mid_i2c_private { - struct i2c_adapter adap; - struct device *dev; - void __iomem *base; - int speed; - struct completion complete; - int abort; - u8 *rx_buf; - int rx_buf_len; - enum mid_i2c_status status; - struct i2c_msg *msg; - enum platform_enum platform; - struct mutex lock; -}; - -#define NUM_SPEEDS 3 - -#define ACTIVE 0 -#define STANDBY 1 - - -/* Control register */ -#define IC_CON 0x00 -#define SLV_DIS (1 << 6) /* Disable slave mode */ -#define RESTART (1 << 5) /* Send a Restart condition */ -#define ADDR_10BIT (1 << 4) /* 10-bit addressing */ -#define STANDARD_MODE (1 << 1) /* standard mode */ -#define FAST_MODE (2 << 1) /* fast mode */ -#define HIGH_MODE (3 << 1) /* high speed mode */ -#define MASTER_EN (1 << 0) /* Master mode */ - -/* Target address register */ -#define IC_TAR 0x04 -#define IC_TAR_10BIT_ADDR (1 << 12) /* 10-bit addressing */ -#define IC_TAR_SPECIAL (1 << 11) /* Perform special I2C cmd */ -#define IC_TAR_GC_OR_START (1 << 10) /* 0: Gerneral Call Address */ - /* 1: START BYTE */ -/* Slave Address Register */ -#define IC_SAR 0x08 /* Not used in Master mode */ - -/* High Speed Master Mode Code Address Register */ -#define IC_HS_MADDR 0x0c - -/* Rx/Tx Data Buffer and Command Register */ -#define IC_DATA_CMD 0x10 -#define IC_RD (1 << 8) /* 1: Read 0: Write */ - -/* Standard Speed Clock SCL High Count Register */ -#define IC_SS_SCL_HCNT 0x14 - -/* Standard Speed Clock SCL Low Count Register */ -#define IC_SS_SCL_LCNT 0x18 - -/* Fast Speed Clock SCL High Count Register */ -#define IC_FS_SCL_HCNT 0x1c - -/* Fast Spedd Clock SCL Low Count Register */ -#define IC_FS_SCL_LCNT 0x20 - -/* High Speed Clock SCL High Count Register */ -#define IC_HS_SCL_HCNT 0x24 - -/* High Speed Clock SCL Low Count Register */ -#define IC_HS_SCL_LCNT 0x28 - -/* Interrupt Status Register */ -#define IC_INTR_STAT 0x2c /* Read only */ -#define R_GEN_CALL (1 << 11) -#define R_START_DET (1 << 10) -#define R_STOP_DET (1 << 9) -#define R_ACTIVITY (1 << 8) -#define R_RX_DONE (1 << 7) -#define R_TX_ABRT (1 << 6) -#define R_RD_REQ (1 << 5) -#define R_TX_EMPTY (1 << 4) -#define R_TX_OVER (1 << 3) -#define R_RX_FULL (1 << 2) -#define R_RX_OVER (1 << 1) -#define R_RX_UNDER (1 << 0) - -/* Interrupt Mask Register */ -#define IC_INTR_MASK 0x30 /* Read and Write */ -#define M_GEN_CALL (1 << 11) -#define M_START_DET (1 << 10) -#define M_STOP_DET (1 << 9) -#define M_ACTIVITY (1 << 8) -#define M_RX_DONE (1 << 7) -#define M_TX_ABRT (1 << 6) -#define M_RD_REQ (1 << 5) -#define M_TX_EMPTY (1 << 4) -#define M_TX_OVER (1 << 3) -#define M_RX_FULL (1 << 2) -#define M_RX_OVER (1 << 1) -#define M_RX_UNDER (1 << 0) - -/* Raw Interrupt Status Register */ -#define IC_RAW_INTR_STAT 0x34 /* Read Only */ -#define GEN_CALL (1 << 11) /* General call */ -#define START_DET (1 << 10) /* (RE)START occurred */ -#define STOP_DET (1 << 9) /* STOP occurred */ -#define ACTIVITY (1 << 8) /* Bus busy */ -#define RX_DONE (1 << 7) /* Not used in Master mode */ -#define TX_ABRT (1 << 6) /* Transmit Abort */ -#define RD_REQ (1 << 5) /* Not used in Master mode */ -#define TX_EMPTY (1 << 4) /* TX FIFO <= threshold */ -#define TX_OVER (1 << 3) /* TX FIFO overflow */ -#define RX_FULL (1 << 2) /* RX FIFO >= threshold */ -#define RX_OVER (1 << 1) /* RX FIFO overflow */ -#define RX_UNDER (1 << 0) /* RX FIFO empty */ - -/* Receive FIFO Threshold Register */ -#define IC_RX_TL 0x38 - -/* Transmit FIFO Treshold Register */ -#define IC_TX_TL 0x3c - -/* Clear Combined and Individual Interrupt Register */ -#define IC_CLR_INTR 0x40 -#define CLR_INTR (1 << 0) - -/* Clear RX_UNDER Interrupt Register */ -#define IC_CLR_RX_UNDER 0x44 -#define CLR_RX_UNDER (1 << 0) - -/* Clear RX_OVER Interrupt Register */ -#define IC_CLR_RX_OVER 0x48 -#define CLR_RX_OVER (1 << 0) - -/* Clear TX_OVER Interrupt Register */ -#define IC_CLR_TX_OVER 0x4c -#define CLR_TX_OVER (1 << 0) - -#define IC_CLR_RD_REQ 0x50 - -/* Clear TX_ABRT Interrupt Register */ -#define IC_CLR_TX_ABRT 0x54 -#define CLR_TX_ABRT (1 << 0) -#define IC_CLR_RX_DONE 0x58 - -/* Clear ACTIVITY Interrupt Register */ -#define IC_CLR_ACTIVITY 0x5c -#define CLR_ACTIVITY (1 << 0) - -/* Clear STOP_DET Interrupt Register */ -#define IC_CLR_STOP_DET 0x60 -#define CLR_STOP_DET (1 << 0) - -/* Clear START_DET Interrupt Register */ -#define IC_CLR_START_DET 0x64 -#define CLR_START_DET (1 << 0) - -/* Clear GEN_CALL Interrupt Register */ -#define IC_CLR_GEN_CALL 0x68 -#define CLR_GEN_CALL (1 << 0) - -/* Enable Register */ -#define IC_ENABLE 0x6c -#define ENABLE (1 << 0) - -/* Status Register */ -#define IC_STATUS 0x70 /* Read Only */ -#define STAT_SLV_ACTIVITY (1 << 6) /* Slave not in idle */ -#define STAT_MST_ACTIVITY (1 << 5) /* Master not in idle */ -#define STAT_RFF (1 << 4) /* RX FIFO Full */ -#define STAT_RFNE (1 << 3) /* RX FIFO Not Empty */ -#define STAT_TFE (1 << 2) /* TX FIFO Empty */ -#define STAT_TFNF (1 << 1) /* TX FIFO Not Full */ -#define STAT_ACTIVITY (1 << 0) /* Activity Status */ - -/* Transmit FIFO Level Register */ -#define IC_TXFLR 0x74 /* Read Only */ -#define TXFLR (1 << 0) /* TX FIFO level */ - -/* Receive FIFO Level Register */ -#define IC_RXFLR 0x78 /* Read Only */ -#define RXFLR (1 << 0) /* RX FIFO level */ - -/* Transmit Abort Source Register */ -#define IC_TX_ABRT_SOURCE 0x80 -#define ABRT_SLVRD_INTX (1 << 15) -#define ABRT_SLV_ARBLOST (1 << 14) -#define ABRT_SLVFLUSH_TXFIFO (1 << 13) -#define ARB_LOST (1 << 12) -#define ABRT_MASTER_DIS (1 << 11) -#define ABRT_10B_RD_NORSTRT (1 << 10) -#define ABRT_SBYTE_NORSTRT (1 << 9) -#define ABRT_HS_NORSTRT (1 << 8) -#define ABRT_SBYTE_ACKDET (1 << 7) -#define ABRT_HS_ACKDET (1 << 6) -#define ABRT_GCALL_READ (1 << 5) -#define ABRT_GCALL_NOACK (1 << 4) -#define ABRT_TXDATA_NOACK (1 << 3) -#define ABRT_10ADDR2_NOACK (1 << 2) -#define ABRT_10ADDR1_NOACK (1 << 1) -#define ABRT_7B_ADDR_NOACK (1 << 0) - -/* Enable Status Register */ -#define IC_ENABLE_STATUS 0x9c -#define IC_EN (1 << 0) /* I2C in an enabled state */ - -/* Component Parameter Register 1*/ -#define IC_COMP_PARAM_1 0xf4 -#define APB_DATA_WIDTH (0x3 << 0) - -/* added by xiaolin --begin */ -#define SS_MIN_SCL_HIGH 4000 -#define SS_MIN_SCL_LOW 4700 -#define FS_MIN_SCL_HIGH 600 -#define FS_MIN_SCL_LOW 1300 -#define HS_MIN_SCL_HIGH_100PF 60 -#define HS_MIN_SCL_LOW_100PF 120 - -#define STANDARD 0 -#define FAST 1 -#define HIGH 2 - -#define NUM_SPEEDS 3 - -static int speed_mode[6] = { - FAST, - FAST, - FAST, - STANDARD, - FAST, - FAST -}; - -static int ctl_num = 6; -module_param_array(speed_mode, int, &ctl_num, S_IRUGO); -MODULE_PARM_DESC(speed_mode, "Set the speed of the i2c interface (0-2)"); - -/** - * intel_mid_i2c_disable - Disable I2C controller - * @adap: struct pointer to i2c_adapter - * - * Return Value: - * 0 success - * -EBUSY if device is busy - * -ETIMEDOUT if i2c cannot be disabled within the given time - * - * I2C bus state should be checked prior to disabling the hardware. If bus is - * not in idle state, an errno is returned. Write "0" to IC_ENABLE to disable - * I2C controller. - */ -static int intel_mid_i2c_disable(struct i2c_adapter *adap) -{ - struct intel_mid_i2c_private *i2c = i2c_get_adapdata(adap); - int err = 0; - int count = 0; - int ret1, ret2; - static const u16 delay[NUM_SPEEDS] = {100, 25, 3}; - - /* Set IC_ENABLE to 0 */ - writel(0, i2c->base + IC_ENABLE); - - /* Check if device is busy */ - dev_dbg(&adap->dev, "mrst i2c disable\n"); - while ((ret1 = readl(i2c->base + IC_ENABLE_STATUS) & 0x1) - || (ret2 = readl(i2c->base + IC_STATUS) & 0x1)) { - udelay(delay[i2c->speed]); - writel(0, i2c->base + IC_ENABLE); - dev_dbg(&adap->dev, "i2c is busy, count is %d speed %d\n", - count, i2c->speed); - if (count++ > 10) { - err = -ETIMEDOUT; - break; - } - } - - /* Clear all interrupts */ - readl(i2c->base + IC_CLR_INTR); - readl(i2c->base + IC_CLR_STOP_DET); - readl(i2c->base + IC_CLR_START_DET); - readl(i2c->base + IC_CLR_ACTIVITY); - readl(i2c->base + IC_CLR_TX_ABRT); - readl(i2c->base + IC_CLR_RX_OVER); - readl(i2c->base + IC_CLR_RX_UNDER); - readl(i2c->base + IC_CLR_TX_OVER); - readl(i2c->base + IC_CLR_RX_DONE); - readl(i2c->base + IC_CLR_GEN_CALL); - - /* Disable all interupts */ - writel(0x0000, i2c->base + IC_INTR_MASK); - - return err; -} - -/** - * intel_mid_i2c_hwinit - Initialize the I2C hardware registers - * @dev: pci device struct pointer - * - * This function will be called in intel_mid_i2c_probe() before device - * registration. - * - * Return Values: - * 0 success - * -EBUSY i2c cannot be disabled - * -ETIMEDOUT i2c cannot be disabled - * -EFAULT If APB data width is not 32-bit wide - * - * I2C should be disabled prior to other register operation. If failed, an - * errno is returned. Mask and Clear all interrpts, this should be done at - * first. Set common registers which will not be modified during normal - * transfers, including: control register, FIFO threshold and clock freq. - * Check APB data width at last. - */ -static int intel_mid_i2c_hwinit(struct intel_mid_i2c_private *i2c) -{ - int err; - - static const u16 hcnt[NUM_PLATFORMS][NUM_SPEEDS] = { - { 0x75, 0x15, 0x07 }, - { 0x04c, 0x10, 0x06 } - }; - static const u16 lcnt[NUM_PLATFORMS][NUM_SPEEDS] = { - { 0x7C, 0x21, 0x0E }, - { 0x053, 0x19, 0x0F } - }; - - /* Disable i2c first */ - err = intel_mid_i2c_disable(&i2c->adap); - if (err) - return err; - - /* - * Setup clock frequency and speed mode - * Enable restart condition, - * enable master FSM, disable slave FSM, - * use target address when initiating transfer - */ - - writel((i2c->speed + 1) << 1 | SLV_DIS | RESTART | MASTER_EN, - i2c->base + IC_CON); - writel(hcnt[i2c->platform][i2c->speed], - i2c->base + (IC_SS_SCL_HCNT + (i2c->speed << 3))); - writel(lcnt[i2c->platform][i2c->speed], - i2c->base + (IC_SS_SCL_LCNT + (i2c->speed << 3))); - - /* Set tranmit & receive FIFO threshold to zero */ - writel(0x0, i2c->base + IC_RX_TL); - writel(0x0, i2c->base + IC_TX_TL); - - return 0; -} - -/** - * intel_mid_i2c_func - Return the supported three I2C operations. - * @adapter: i2c_adapter struct pointer - */ -static u32 intel_mid_i2c_func(struct i2c_adapter *adapter) -{ - return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SMBUS_EMUL; -} - -/** - * intel_mid_i2c_address_neq - To check if the addresses for different i2c messages - * are equal. - * @p1: first i2c_msg - * @p2: second i2c_msg - * - * Return Values: - * 0 if addresses are equal - * 1 if not equal - * - * Within a single transfer, the I2C client may need to send its address more - * than once. So a check if the addresses match is needed. - */ -static inline bool intel_mid_i2c_address_neq(const struct i2c_msg *p1, - const struct i2c_msg *p2) -{ - if (p1->addr != p2->addr) - return 1; - if ((p1->flags ^ p2->flags) & I2C_M_TEN) - return 1; - return 0; -} - -/** - * intel_mid_i2c_abort - To handle transfer abortions and print error messages. - * @adap: i2c_adapter struct pointer - * - * By reading register IC_TX_ABRT_SOURCE, various transfer errors can be - * distingushed. At present, no circumstances have been found out that - * multiple errors would be occurred simutaneously, so we simply use the - * register value directly. - * - * At last the error bits are cleared. (Note clear ABRT_SBYTE_NORSTRT bit need - * a few extra steps) - */ -static void intel_mid_i2c_abort(struct intel_mid_i2c_private *i2c) -{ - /* Read about source register */ - int abort = i2c->abort; - struct i2c_adapter *adap = &i2c->adap; - - /* Single transfer error check: - * According to databook, TX/RX FIFOs would be flushed when - * the abort interrupt occurred. - */ - if (abort & ABRT_MASTER_DIS) - dev_err(&adap->dev, - "initiate master operation with master mode disabled.\n"); - if (abort & ABRT_10B_RD_NORSTRT) - dev_err(&adap->dev, - "RESTART disabled and master sent READ cmd in 10-bit addressing.\n"); - - if (abort & ABRT_SBYTE_NORSTRT) { - dev_err(&adap->dev, - "RESTART disabled and user is trying to send START byte.\n"); - writel(~ABRT_SBYTE_NORSTRT, i2c->base + IC_TX_ABRT_SOURCE); - writel(RESTART, i2c->base + IC_CON); - writel(~IC_TAR_SPECIAL, i2c->base + IC_TAR); - } - - if (abort & ABRT_SBYTE_ACKDET) - dev_err(&adap->dev, - "START byte was not acknowledged.\n"); - if (abort & ABRT_TXDATA_NOACK) - dev_dbg(&adap->dev, - "No acknowledgement received from slave.\n"); - if (abort & ABRT_10ADDR2_NOACK) - dev_dbg(&adap->dev, - "The 2nd address byte of the 10-bit address was not acknowledged.\n"); - if (abort & ABRT_10ADDR1_NOACK) - dev_dbg(&adap->dev, - "The 1st address byte of 10-bit address was not acknowledged.\n"); - if (abort & ABRT_7B_ADDR_NOACK) - dev_dbg(&adap->dev, - "I2C slave device not acknowledged.\n"); - - /* Clear TX_ABRT bit */ - readl(i2c->base + IC_CLR_TX_ABRT); - i2c->status = STATUS_XFER_ABORT; -} - -/** - * xfer_read - Internal function to implement master read transfer. - * @adap: i2c_adapter struct pointer - * @buf: buffer in i2c_msg - * @length: number of bytes to be read - * - * Return Values: - * 0 if the read transfer succeeds - * -ETIMEDOUT if cannot read the "raw" interrupt register - * -EINVAL if a transfer abort occurred - * - * For every byte, a "READ" command will be loaded into IC_DATA_CMD prior to - * data transfer. The actual "read" operation will be performed if an RX_FULL - * interrupt occurred. - * - * Note there may be two interrupt signals captured, one should read - * IC_RAW_INTR_STAT to separate between errors and actual data. - */ -static int xfer_read(struct i2c_adapter *adap, unsigned char *buf, int length) -{ - struct intel_mid_i2c_private *i2c = i2c_get_adapdata(adap); - int i = length; - int err; - - if (length >= 256) { - dev_err(&adap->dev, - "I2C FIFO cannot support larger than 256 bytes\n"); - return -EMSGSIZE; - } - - INIT_COMPLETION(i2c->complete); - - readl(i2c->base + IC_CLR_INTR); - writel(0x0044, i2c->base + IC_INTR_MASK); - - i2c->status = STATUS_READ_START; - - while (i--) - writel(IC_RD, i2c->base + IC_DATA_CMD); - - i2c->status = STATUS_READ_START; - err = wait_for_completion_interruptible_timeout(&i2c->complete, HZ); - if (!err) { - dev_err(&adap->dev, "Timeout for ACK from I2C slave device\n"); - intel_mid_i2c_hwinit(i2c); - return -ETIMEDOUT; - } - if (i2c->status == STATUS_READ_SUCCESS) - return 0; - else - return -EIO; -} - -/** - * xfer_write - Internal function to implement master write transfer. - * @adap: i2c_adapter struct pointer - * @buf: buffer in i2c_msg - * @length: number of bytes to be read - * - * Return Values: - * 0 if the read transfer succeeds - * -ETIMEDOUT if we cannot read the "raw" interrupt register - * -EINVAL if a transfer abort occurred - * - * For every byte, a "WRITE" command will be loaded into IC_DATA_CMD prior to - * data transfer. The actual "write" operation will be performed when the - * RX_FULL interrupt signal occurs. - * - * Note there may be two interrupt signals captured, one should read - * IC_RAW_INTR_STAT to separate between errors and actual data. - */ -static int xfer_write(struct i2c_adapter *adap, - unsigned char *buf, int length) -{ - struct intel_mid_i2c_private *i2c = i2c_get_adapdata(adap); - int i, err; - - if (length >= 256) { - dev_err(&adap->dev, - "I2C FIFO cannot support larger than 256 bytes\n"); - return -EMSGSIZE; - } - - INIT_COMPLETION(i2c->complete); - - readl(i2c->base + IC_CLR_INTR); - writel(0x0050, i2c->base + IC_INTR_MASK); - - i2c->status = STATUS_WRITE_START; - for (i = 0; i < length; i++) - writel((u16)(*(buf + i)), i2c->base + IC_DATA_CMD); - - i2c->status = STATUS_WRITE_START; - err = wait_for_completion_interruptible_timeout(&i2c->complete, HZ); - if (!err) { - dev_err(&adap->dev, "Timeout for ACK from I2C slave device\n"); - intel_mid_i2c_hwinit(i2c); - return -ETIMEDOUT; - } else { - if (i2c->status == STATUS_WRITE_SUCCESS) - return 0; - else - return -EIO; - } -} - -static int intel_mid_i2c_setup(struct i2c_adapter *adap, struct i2c_msg *pmsg) -{ - struct intel_mid_i2c_private *i2c = i2c_get_adapdata(adap); - int err; - u32 reg; - u32 bit_mask; - u32 mode; - - /* Disable device first */ - err = intel_mid_i2c_disable(adap); - if (err) { - dev_err(&adap->dev, - "Cannot disable i2c controller, timeout\n"); - return err; - } - - mode = (1 + i2c->speed) << 1; - /* set the speed mode */ - reg = readl(i2c->base + IC_CON); - if ((reg & 0x06) != mode) { - dev_dbg(&adap->dev, "set mode %d\n", i2c->speed); - writel((reg & ~0x6) | mode, i2c->base + IC_CON); - } - - reg = readl(i2c->base + IC_CON); - /* use 7-bit addressing */ - if (pmsg->flags & I2C_M_TEN) { - if ((reg & ADDR_10BIT) != ADDR_10BIT) { - dev_dbg(&adap->dev, "set i2c 10 bit address mode\n"); - writel(reg | ADDR_10BIT, i2c->base + IC_CON); - } - } else { - if ((reg & ADDR_10BIT) != 0x0) { - dev_dbg(&adap->dev, "set i2c 7 bit address mode\n"); - writel(reg & ~ADDR_10BIT, i2c->base + IC_CON); - } - } - /* enable restart conditions */ - reg = readl(i2c->base + IC_CON); - if ((reg & RESTART) != RESTART) { - dev_dbg(&adap->dev, "enable restart conditions\n"); - writel(reg | RESTART, i2c->base + IC_CON); - } - - /* enable master FSM */ - reg = readl(i2c->base + IC_CON); - dev_dbg(&adap->dev, "ic_con reg is 0x%x\n", reg); - writel(reg | MASTER_EN, i2c->base + IC_CON); - if ((reg & SLV_DIS) != SLV_DIS) { - dev_dbg(&adap->dev, "enable master FSM\n"); - writel(reg | SLV_DIS, i2c->base + IC_CON); - dev_dbg(&adap->dev, "ic_con reg is 0x%x\n", reg); - } - - /* use target address when initiating transfer */ - reg = readl(i2c->base + IC_TAR); - bit_mask = IC_TAR_SPECIAL | IC_TAR_GC_OR_START; - - if ((reg & bit_mask) != 0x0) { - dev_dbg(&adap->dev, - "WR: use target address when intiating transfer, i2c_tx_target\n"); - writel(reg & ~bit_mask, i2c->base + IC_TAR); - } - - /* set target address to the I2C slave address */ - dev_dbg(&adap->dev, - "set target address to the I2C slave address, addr is %x\n", - pmsg->addr); - writel(pmsg->addr | (pmsg->flags & I2C_M_TEN ? IC_TAR_10BIT_ADDR : 0), - i2c->base + IC_TAR); - - /* Enable I2C controller */ - writel(ENABLE, i2c->base + IC_ENABLE); - - return 0; -} - -/** - * intel_mid_i2c_xfer - Main master transfer routine. - * @adap: i2c_adapter struct pointer - * @pmsg: i2c_msg struct pointer - * @num: number of i2c_msg - * - * Return Values: - * + number of messages transferred - * -ETIMEDOUT If cannot disable I2C controller or read IC_STATUS - * -EINVAL If the address in i2c_msg is invalid - * - * This function will be registered in i2c-core and exposed to external - * I2C clients. - * 1. Disable I2C controller - * 2. Unmask three interrupts: RX_FULL, TX_EMPTY, TX_ABRT - * 3. Check if address in i2c_msg is valid - * 4. Enable I2C controller - * 5. Perform real transfer (call xfer_read or xfer_write) - * 6. Wait until the current transfer is finished (check bus state) - * 7. Mask and clear all interrupts - */ -static int intel_mid_i2c_xfer(struct i2c_adapter *adap, - struct i2c_msg *pmsg, - int num) -{ - struct intel_mid_i2c_private *i2c = i2c_get_adapdata(adap); - int i, err = 0; - - /* if number of messages equal 0*/ - if (num == 0) - return 0; - - pm_runtime_get(i2c->dev); - - mutex_lock(&i2c->lock); - dev_dbg(&adap->dev, "intel_mid_i2c_xfer, process %d msg(s)\n", num); - dev_dbg(&adap->dev, "slave address is %x\n", pmsg->addr); - - - if (i2c->status != STATUS_IDLE) { - dev_err(&adap->dev, "Adapter %d in transfer/standby\n", - adap->nr); - mutex_unlock(&i2c->lock); - pm_runtime_put(i2c->dev); - return -1; - } - - - for (i = 1; i < num; i++) { - /* Message address equal? */ - if (unlikely(intel_mid_i2c_address_neq(&pmsg[0], &pmsg[i]))) { - dev_err(&adap->dev, "Invalid address in msg[%d]\n", i); - mutex_unlock(&i2c->lock); - pm_runtime_put(i2c->dev); - return -EINVAL; - } - } - - if (intel_mid_i2c_setup(adap, pmsg)) { - mutex_unlock(&i2c->lock); - pm_runtime_put(i2c->dev); - return -EINVAL; - } - - for (i = 0; i < num; i++) { - i2c->msg = pmsg; - i2c->status = STATUS_IDLE; - /* Read or Write */ - if (pmsg->flags & I2C_M_RD) { - dev_dbg(&adap->dev, "I2C_M_RD\n"); - err = xfer_read(adap, pmsg->buf, pmsg->len); - } else { - dev_dbg(&adap->dev, "I2C_M_WR\n"); - err = xfer_write(adap, pmsg->buf, pmsg->len); - } - if (err < 0) - break; - dev_dbg(&adap->dev, "msg[%d] transfer complete\n", i); - pmsg++; /* next message */ - } - - /* Mask interrupts */ - writel(0x0000, i2c->base + IC_INTR_MASK); - /* Clear all interrupts */ - readl(i2c->base + IC_CLR_INTR); - - i2c->status = STATUS_IDLE; - mutex_unlock(&i2c->lock); - pm_runtime_put(i2c->dev); - - return err; -} - -static int intel_mid_i2c_runtime_suspend(struct device *dev) -{ - struct pci_dev *pdev = to_pci_dev(dev); - struct intel_mid_i2c_private *i2c = pci_get_drvdata(pdev); - struct i2c_adapter *adap = to_i2c_adapter(dev); - int err; - - if (i2c->status != STATUS_IDLE) - return -1; - - intel_mid_i2c_disable(adap); - - err = pci_save_state(pdev); - if (err) { - dev_err(dev, "pci_save_state failed\n"); - return err; - } - - err = pci_set_power_state(pdev, PCI_D3hot); - if (err) { - dev_err(dev, "pci_set_power_state failed\n"); - return err; - } - i2c->status = STATUS_STANDBY; - - return 0; -} - -static int intel_mid_i2c_runtime_resume(struct device *dev) -{ - struct pci_dev *pdev = to_pci_dev(dev); - struct intel_mid_i2c_private *i2c = pci_get_drvdata(pdev); - int err; - - if (i2c->status != STATUS_STANDBY) - return 0; - - pci_set_power_state(pdev, PCI_D0); - pci_restore_state(pdev); - err = pci_enable_device(pdev); - if (err) { - dev_err(dev, "pci_enable_device failed\n"); - return err; - } - - i2c->status = STATUS_IDLE; - - intel_mid_i2c_hwinit(i2c); - return err; -} - -static void i2c_isr_read(struct intel_mid_i2c_private *i2c) -{ - struct i2c_msg *msg = i2c->msg; - int rx_num; - u32 len; - u8 *buf; - - if (!(msg->flags & I2C_M_RD)) - return; - - if (i2c->status != STATUS_READ_IN_PROGRESS) { - len = msg->len; - buf = msg->buf; - } else { - len = i2c->rx_buf_len; - buf = i2c->rx_buf; - } - - rx_num = readl(i2c->base + IC_RXFLR); - - for (; len > 0 && rx_num > 0; len--, rx_num--) - *buf++ = readl(i2c->base + IC_DATA_CMD); - - if (len > 0) { - i2c->status = STATUS_READ_IN_PROGRESS; - i2c->rx_buf_len = len; - i2c->rx_buf = buf; - } else - i2c->status = STATUS_READ_SUCCESS; - - return; -} - -static irqreturn_t intel_mid_i2c_isr(int this_irq, void *dev) -{ - struct intel_mid_i2c_private *i2c = dev; - u32 stat = readl(i2c->base + IC_INTR_STAT); - - if (!stat) - return IRQ_NONE; - - dev_dbg(&i2c->adap.dev, "%s, stat = 0x%x\n", __func__, stat); - stat &= 0x54; - - if (i2c->status != STATUS_WRITE_START && - i2c->status != STATUS_READ_START && - i2c->status != STATUS_READ_IN_PROGRESS) - goto err; - - if (stat & TX_ABRT) - i2c->abort = readl(i2c->base + IC_TX_ABRT_SOURCE); - - readl(i2c->base + IC_CLR_INTR); - - if (stat & TX_ABRT) { - intel_mid_i2c_abort(i2c); - goto exit; - } - - if (stat & RX_FULL) { - i2c_isr_read(i2c); - goto exit; - } - - if (stat & TX_EMPTY) { - if (readl(i2c->base + IC_STATUS) & 0x4) - i2c->status = STATUS_WRITE_SUCCESS; - } - -exit: - if (i2c->status == STATUS_READ_SUCCESS || - i2c->status == STATUS_WRITE_SUCCESS || - i2c->status == STATUS_XFER_ABORT) { - /* Clear all interrupts */ - readl(i2c->base + IC_CLR_INTR); - /* Mask interrupts */ - writel(0, i2c->base + IC_INTR_MASK); - complete(&i2c->complete); - } -err: - return IRQ_HANDLED; -} - -static struct i2c_algorithm intel_mid_i2c_algorithm = { - .master_xfer = intel_mid_i2c_xfer, - .functionality = intel_mid_i2c_func, -}; - - -static const struct dev_pm_ops intel_mid_i2c_pm_ops = { - .runtime_suspend = intel_mid_i2c_runtime_suspend, - .runtime_resume = intel_mid_i2c_runtime_resume, -}; - -/** - * intel_mid_i2c_probe - I2C controller initialization routine - * @dev: pci device - * @id: device id - * - * Return Values: - * 0 success - * -ENODEV If cannot allocate pci resource - * -ENOMEM If the register base remapping failed, or - * if kzalloc failed - * - * Initialization steps: - * 1. Request for PCI resource - * 2. Remap the start address of PCI resource to register base - * 3. Request for device memory region - * 4. Fill in the struct members of intel_mid_i2c_private - * 5. Call intel_mid_i2c_hwinit() for hardware initialization - * 6. Register I2C adapter in i2c-core - */ -static int intel_mid_i2c_probe(struct pci_dev *dev, - const struct pci_device_id *id) -{ - struct intel_mid_i2c_private *mrst; - unsigned long start, len; - int err, busnum; - void __iomem *base = NULL; - - dev_dbg(&dev->dev, "Get into probe function for I2C\n"); - err = pci_enable_device(dev); - if (err) { - dev_err(&dev->dev, "Failed to enable I2C PCI device (%d)\n", - err); - goto exit; - } - - /* Determine the address of the I2C area */ - start = pci_resource_start(dev, 0); - len = pci_resource_len(dev, 0); - if (!start || len == 0) { - dev_err(&dev->dev, "base address not set\n"); - err = -ENODEV; - goto exit; - } - dev_dbg(&dev->dev, "%s i2c resource start 0x%lx, len=%ld\n", - PLATFORM, start, len); - - err = pci_request_region(dev, 0, DRIVER_NAME); - if (err) { - dev_err(&dev->dev, "failed to request I2C region " - "0x%lx-0x%lx\n", start, - (unsigned long)pci_resource_end(dev, 0)); - goto exit; - } - - base = ioremap_nocache(start, len); - if (!base) { - dev_err(&dev->dev, "I/O memory remapping failed\n"); - err = -ENOMEM; - goto fail0; - } - - /* Allocate the per-device data structure, intel_mid_i2c_private */ - mrst = kzalloc(sizeof(struct intel_mid_i2c_private), GFP_KERNEL); - if (mrst == NULL) { - dev_err(&dev->dev, "can't allocate interface\n"); - err = -ENOMEM; - goto fail1; - } - - /* Initialize struct members */ - snprintf(mrst->adap.name, sizeof(mrst->adap.name), - "Intel MID I2C at %lx", start); - mrst->adap.owner = THIS_MODULE; - mrst->adap.algo = &intel_mid_i2c_algorithm; - mrst->adap.dev.parent = &dev->dev; - mrst->dev = &dev->dev; - mrst->base = base; - mrst->speed = STANDARD; - mrst->abort = 0; - mrst->rx_buf_len = 0; - mrst->status = STATUS_IDLE; - - pci_set_drvdata(dev, mrst); - i2c_set_adapdata(&mrst->adap, mrst); - - mrst->adap.nr = busnum = id->driver_data; - if (dev->device <= 0x0804) - mrst->platform = MOORESTOWN; - else - mrst->platform = MEDFIELD; - - dev_dbg(&dev->dev, "I2C%d\n", busnum); - - if (ctl_num > busnum) { - if (speed_mode[busnum] < 0 || speed_mode[busnum] >= NUM_SPEEDS) - dev_warn(&dev->dev, "invalid speed %d ignored.\n", - speed_mode[busnum]); - else - mrst->speed = speed_mode[busnum]; - } - - /* Initialize i2c controller */ - err = intel_mid_i2c_hwinit(mrst); - if (err < 0) { - dev_err(&dev->dev, "I2C interface initialization failed\n"); - goto fail2; - } - - mutex_init(&mrst->lock); - init_completion(&mrst->complete); - - /* Clear all interrupts */ - readl(mrst->base + IC_CLR_INTR); - writel(0x0000, mrst->base + IC_INTR_MASK); - - err = request_irq(dev->irq, intel_mid_i2c_isr, IRQF_SHARED, - mrst->adap.name, mrst); - if (err) { - dev_err(&dev->dev, "Failed to request IRQ for I2C controller: " - "%s", mrst->adap.name); - goto fail2; - } - - /* Adapter registration */ - err = i2c_add_numbered_adapter(&mrst->adap); - if (err) { - dev_err(&dev->dev, "Adapter %s registration failed\n", - mrst->adap.name); - goto fail3; - } - - dev_dbg(&dev->dev, "%s I2C bus %d driver bind success.\n", - (mrst->platform == MOORESTOWN) ? "Moorestown" : "Medfield", - busnum); - - pm_runtime_enable(&dev->dev); - return 0; - -fail3: - free_irq(dev->irq, mrst); -fail2: - kfree(mrst); -fail1: - iounmap(base); -fail0: - pci_release_region(dev, 0); -exit: - return err; -} - -static void intel_mid_i2c_remove(struct pci_dev *dev) -{ - struct intel_mid_i2c_private *mrst = pci_get_drvdata(dev); - intel_mid_i2c_disable(&mrst->adap); - i2c_del_adapter(&mrst->adap); - - free_irq(dev->irq, mrst); - iounmap(mrst->base); - kfree(mrst); - pci_release_region(dev, 0); -} - -static DEFINE_PCI_DEVICE_TABLE(intel_mid_i2c_ids) = { - /* Moorestown */ - { PCI_VDEVICE(INTEL, 0x0802), 0 }, - { PCI_VDEVICE(INTEL, 0x0803), 1 }, - { PCI_VDEVICE(INTEL, 0x0804), 2 }, - /* Medfield */ - { PCI_VDEVICE(INTEL, 0x0817), 3,}, - { PCI_VDEVICE(INTEL, 0x0818), 4 }, - { PCI_VDEVICE(INTEL, 0x0819), 5 }, - { PCI_VDEVICE(INTEL, 0x082C), 0 }, - { PCI_VDEVICE(INTEL, 0x082D), 1 }, - { PCI_VDEVICE(INTEL, 0x082E), 2 }, - { 0,} -}; -MODULE_DEVICE_TABLE(pci, intel_mid_i2c_ids); - -static struct pci_driver intel_mid_i2c_driver = { - .name = DRIVER_NAME, - .id_table = intel_mid_i2c_ids, - .probe = intel_mid_i2c_probe, - .remove = intel_mid_i2c_remove, -}; - -module_pci_driver(intel_mid_i2c_driver); - -MODULE_AUTHOR("Ba Zheng "); -MODULE_DESCRIPTION("I2C driver for Moorestown Platform"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(VERSION); -- cgit v1.2.3 From d5ac456144413950d2d32ec4f22542e45be13cd7 Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 16 May 2013 10:32:08 +0000 Subject: I2C: mv64xxx: use return value from mv64xxx_i2c_map_regs() mv64xxx_i2c_map_regs() already returns an error code, so lets propagate that to mv64xxx_i2c_probe()'s caller. Signed-off-by: Russell King Acked-by: Mark A. Greer Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mv64xxx.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c index 1a3abd6a0bfc..940a190b1a53 100644 --- a/drivers/i2c/busses/i2c-mv64xxx.c +++ b/drivers/i2c/busses/i2c-mv64xxx.c @@ -619,10 +619,9 @@ mv64xxx_i2c_probe(struct platform_device *pd) if (!drv_data) return -ENOMEM; - if (mv64xxx_i2c_map_regs(pd, drv_data)) { - rc = -ENODEV; + rc = mv64xxx_i2c_map_regs(pd, drv_data); + if (rc) goto exit_kfree; - } strlcpy(drv_data->adapter.name, MV64XXX_I2C_CTLR_NAME " adapter", sizeof(drv_data->adapter.name)); -- cgit v1.2.3 From 16874b0709c7c78489de1f502edd33acad2918e8 Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 16 May 2013 21:33:09 +0100 Subject: I2C: mv64xxx: use devm_ioremap_resource() Eliminate reg_base_p and reg_size, mv64xxx_i2c_unmap_regs() and an unchecked ioremap() return from this driver by using the devm_* API for requesting and ioremapping resources. Signed-off-by: Russell King Acked-by: Mark A. Greer Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mv64xxx.c | 46 ++++++---------------------------------- 1 file changed, 6 insertions(+), 40 deletions(-) diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c index 940a190b1a53..54b8cf6b6dd0 100644 --- a/drivers/i2c/busses/i2c-mv64xxx.c +++ b/drivers/i2c/busses/i2c-mv64xxx.c @@ -92,8 +92,6 @@ struct mv64xxx_i2c_data { u32 aborting; u32 cntl_bits; void __iomem *reg_base; - u32 reg_base_p; - u32 reg_size; u32 addr1; u32 addr2; u32 bytes_left; @@ -495,40 +493,6 @@ static const struct i2c_algorithm mv64xxx_i2c_algo = { * ***************************************************************************** */ -static int -mv64xxx_i2c_map_regs(struct platform_device *pd, - struct mv64xxx_i2c_data *drv_data) -{ - int size; - struct resource *r = platform_get_resource(pd, IORESOURCE_MEM, 0); - - if (!r) - return -ENODEV; - - size = resource_size(r); - - if (!request_mem_region(r->start, size, drv_data->adapter.name)) - return -EBUSY; - - drv_data->reg_base = ioremap(r->start, size); - drv_data->reg_base_p = r->start; - drv_data->reg_size = size; - - return 0; -} - -static void -mv64xxx_i2c_unmap_regs(struct mv64xxx_i2c_data *drv_data) -{ - if (drv_data->reg_base) { - iounmap(drv_data->reg_base); - release_mem_region(drv_data->reg_base_p, drv_data->reg_size); - } - - drv_data->reg_base = NULL; - drv_data->reg_base_p = 0; -} - #ifdef CONFIG_OF static int mv64xxx_calc_freq(const int tclk, const int n, const int m) @@ -610,6 +574,7 @@ mv64xxx_i2c_probe(struct platform_device *pd) { struct mv64xxx_i2c_data *drv_data; struct mv64xxx_i2c_pdata *pdata = pd->dev.platform_data; + struct resource *r; int rc; if ((!pdata && !pd->dev.of_node)) @@ -619,9 +584,12 @@ mv64xxx_i2c_probe(struct platform_device *pd) if (!drv_data) return -ENOMEM; - rc = mv64xxx_i2c_map_regs(pd, drv_data); - if (rc) + r = platform_get_resource(pd, IORESOURCE_MEM, 0); + drv_data->reg_base = devm_ioremap_resource(&pd->dev, r); + if (IS_ERR(drv_data->reg_base)) { + rc = PTR_ERR(drv_data->reg_base); goto exit_kfree; + } strlcpy(drv_data->adapter.name, MV64XXX_I2C_CTLR_NAME " adapter", sizeof(drv_data->adapter.name)); @@ -690,7 +658,6 @@ mv64xxx_i2c_probe(struct platform_device *pd) clk_unprepare(drv_data->clk); } #endif - mv64xxx_i2c_unmap_regs(drv_data); exit_kfree: kfree(drv_data); return rc; @@ -703,7 +670,6 @@ mv64xxx_i2c_remove(struct platform_device *dev) i2c_del_adapter(&drv_data->adapter); free_irq(drv_data->irq, drv_data); - mv64xxx_i2c_unmap_regs(drv_data); #if defined(CONFIG_HAVE_CLK) /* Not all platforms have a clk */ if (!IS_ERR(drv_data->clk)) { -- cgit v1.2.3 From 4c5c95f53b5cb5666906242a63d4d2c4fd0a0be8 Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 16 May 2013 21:34:10 +0100 Subject: I2C: mv64xxx: use devm_clk_get() to avoid missing clk_put() This driver forgets to use clk_put(). Rather than adding clk_put(), lets instead use devm_clk_get() to obtain this clock so that it's automatically handled on cleanup. Signed-off-by: Russell King Acked-by: Mark A. Greer Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mv64xxx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c index 54b8cf6b6dd0..25e64c445e61 100644 --- a/drivers/i2c/busses/i2c-mv64xxx.c +++ b/drivers/i2c/busses/i2c-mv64xxx.c @@ -599,7 +599,7 @@ mv64xxx_i2c_probe(struct platform_device *pd) #if defined(CONFIG_HAVE_CLK) /* Not all platforms have a clk */ - drv_data->clk = clk_get(&pd->dev, NULL); + drv_data->clk = devm_clk_get(&pd->dev, NULL); if (!IS_ERR(drv_data->clk)) { clk_prepare(drv_data->clk); clk_enable(drv_data->clk); -- cgit v1.2.3 From 2c911103040faad9d092a72b9f4503f92154c1dc Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 16 May 2013 21:35:10 +0100 Subject: I2C: mv64xxx: use devm_kzalloc() As we're changing to using devm_* APIs to fix various problems in this driver, lets also do devm_kzalloc() while we're here too. Signed-off-by: Russell King Acked-by: Mark A. Greer Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mv64xxx.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c index 25e64c445e61..640b49df2ddd 100644 --- a/drivers/i2c/busses/i2c-mv64xxx.c +++ b/drivers/i2c/busses/i2c-mv64xxx.c @@ -580,16 +580,15 @@ mv64xxx_i2c_probe(struct platform_device *pd) if ((!pdata && !pd->dev.of_node)) return -ENODEV; - drv_data = kzalloc(sizeof(struct mv64xxx_i2c_data), GFP_KERNEL); + drv_data = devm_kzalloc(&pd->dev, sizeof(struct mv64xxx_i2c_data), + GFP_KERNEL); if (!drv_data) return -ENOMEM; r = platform_get_resource(pd, IORESOURCE_MEM, 0); drv_data->reg_base = devm_ioremap_resource(&pd->dev, r); - if (IS_ERR(drv_data->reg_base)) { - rc = PTR_ERR(drv_data->reg_base); - goto exit_kfree; - } + if (IS_ERR(drv_data->reg_base)) + return PTR_ERR(drv_data->reg_base); strlcpy(drv_data->adapter.name, MV64XXX_I2C_CTLR_NAME " adapter", sizeof(drv_data->adapter.name)); @@ -613,11 +612,11 @@ mv64xxx_i2c_probe(struct platform_device *pd) } else if (pd->dev.of_node) { rc = mv64xxx_of_config(drv_data, pd->dev.of_node); if (rc) - goto exit_unmap_regs; + goto exit_clk; } if (drv_data->irq < 0) { rc = -ENXIO; - goto exit_unmap_regs; + goto exit_clk; } drv_data->adapter.dev.parent = &pd->dev; @@ -637,7 +636,7 @@ mv64xxx_i2c_probe(struct platform_device *pd) "mv64xxx: Can't register intr handler irq: %d\n", drv_data->irq); rc = -EINVAL; - goto exit_unmap_regs; + goto exit_clk; } else if ((rc = i2c_add_numbered_adapter(&drv_data->adapter)) != 0) { dev_err(&drv_data->adapter.dev, "mv64xxx: Can't add i2c adapter, rc: %d\n", -rc); @@ -648,9 +647,9 @@ mv64xxx_i2c_probe(struct platform_device *pd) return 0; - exit_free_irq: - free_irq(drv_data->irq, drv_data); - exit_unmap_regs: +exit_free_irq: + free_irq(drv_data->irq, drv_data); +exit_clk: #if defined(CONFIG_HAVE_CLK) /* Not all platforms have a clk */ if (!IS_ERR(drv_data->clk)) { @@ -658,8 +657,6 @@ mv64xxx_i2c_probe(struct platform_device *pd) clk_unprepare(drv_data->clk); } #endif - exit_kfree: - kfree(drv_data); return rc; } @@ -677,7 +674,6 @@ mv64xxx_i2c_remove(struct platform_device *dev) clk_unprepare(drv_data->clk); } #endif - kfree(drv_data); return 0; } -- cgit v1.2.3 From 0c195afb8741c32974266ba7c964481ba418b4b5 Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 16 May 2013 21:36:11 +0100 Subject: I2C: mv64xxx: fix error handling for request_irq() Propagate the error code from request_irq() rather than ignoring it entirely. Signed-off-by: Russell King Acked-by: Mark A. Greer Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mv64xxx.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c index 640b49df2ddd..4b45c7363501 100644 --- a/drivers/i2c/busses/i2c-mv64xxx.c +++ b/drivers/i2c/busses/i2c-mv64xxx.c @@ -630,12 +630,12 @@ mv64xxx_i2c_probe(struct platform_device *pd) mv64xxx_i2c_hw_init(drv_data); - if (request_irq(drv_data->irq, mv64xxx_i2c_intr, 0, - MV64XXX_I2C_CTLR_NAME, drv_data)) { + rc = request_irq(drv_data->irq, mv64xxx_i2c_intr, 0, + MV64XXX_I2C_CTLR_NAME, drv_data); + if (rc) { dev_err(&drv_data->adapter.dev, - "mv64xxx: Can't register intr handler irq: %d\n", - drv_data->irq); - rc = -EINVAL; + "mv64xxx: Can't register intr handler irq%d: %d\n", + drv_data->irq, rc); goto exit_clk; } else if ((rc = i2c_add_numbered_adapter(&drv_data->adapter)) != 0) { dev_err(&drv_data->adapter.dev, -- cgit v1.2.3 From aa6bce5319a54c050af26e095287472854abccfd Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 16 May 2013 21:37:11 +0100 Subject: I2C: mv64xxx: remove I2C_M_NOSTART code As this driver does not advertise protocol mangling support (I2C_FUNC_PROTOCOL_MANGLING is not set), having code to act on I2C_M_NOSTART is illogical, and in any case isn't supportable on anything but the first message - which makes no sense. Remove the I2C_M_NOSTART code. Signed-off-by: Russell King Acked-by: Mark A. Greer Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mv64xxx.c | 26 +++++--------------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c index 4b45c7363501..f11cb25c3295 100644 --- a/drivers/i2c/busses/i2c-mv64xxx.c +++ b/drivers/i2c/busses/i2c-mv64xxx.c @@ -419,28 +419,12 @@ mv64xxx_i2c_execute_msg(struct mv64xxx_i2c_data *drv_data, struct i2c_msg *msg, spin_lock_irqsave(&drv_data->lock, flags); mv64xxx_i2c_prepare_for_io(drv_data, msg); - if (unlikely(msg->flags & I2C_M_NOSTART)) { /* Skip start/addr phases */ - if (drv_data->msg->flags & I2C_M_RD) { - /* No action to do, wait for slave to send a byte */ - drv_data->action = MV64XXX_I2C_ACTION_CONTINUE; - drv_data->state = - MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_DATA; - } else { - drv_data->action = MV64XXX_I2C_ACTION_SEND_DATA; - drv_data->state = - MV64XXX_I2C_STATE_WAITING_FOR_SLAVE_ACK; - drv_data->bytes_left--; - } + if (is_first) { + drv_data->action = MV64XXX_I2C_ACTION_SEND_START; + drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_START_COND; } else { - if (is_first) { - drv_data->action = MV64XXX_I2C_ACTION_SEND_START; - drv_data->state = - MV64XXX_I2C_STATE_WAITING_FOR_START_COND; - } else { - drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_1; - drv_data->state = - MV64XXX_I2C_STATE_WAITING_FOR_ADDR_1_ACK; - } + drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_1; + drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_ADDR_1_ACK; } drv_data->send_stop = is_last; -- cgit v1.2.3 From 3420afbc06058c9c13f7d69cf48b9d5429db6bd9 Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 16 May 2013 21:38:11 +0100 Subject: I2C: mv64xxx: move mv64xxx_i2c_prepare_for_io() Move mv64xxx_i2c_prepare_for_io() higher up in the driver to avoid a future forward declaration for this function. Signed-off-by: Russell King Acked-by: Mark A. Greer Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mv64xxx.c | 52 ++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c index f11cb25c3295..bb37e14f3fbd 100644 --- a/drivers/i2c/busses/i2c-mv64xxx.c +++ b/drivers/i2c/busses/i2c-mv64xxx.c @@ -110,6 +110,32 @@ struct mv64xxx_i2c_data { struct i2c_adapter adapter; }; +static void +mv64xxx_i2c_prepare_for_io(struct mv64xxx_i2c_data *drv_data, + struct i2c_msg *msg) +{ + u32 dir = 0; + + drv_data->msg = msg; + drv_data->byte_posn = 0; + drv_data->bytes_left = msg->len; + drv_data->aborting = 0; + drv_data->rc = 0; + drv_data->cntl_bits = MV64XXX_I2C_REG_CONTROL_ACK | + MV64XXX_I2C_REG_CONTROL_INTEN | MV64XXX_I2C_REG_CONTROL_TWSIEN; + + if (msg->flags & I2C_M_RD) + dir = 1; + + if (msg->flags & I2C_M_TEN) { + drv_data->addr1 = 0xf0 | (((u32)msg->addr & 0x300) >> 7) | dir; + drv_data->addr2 = (u32)msg->addr & 0xff; + } else { + drv_data->addr1 = ((u32)msg->addr & 0x7f) << 1 | dir; + drv_data->addr2 = 0; + } +} + /* ***************************************************************************** * @@ -346,32 +372,6 @@ mv64xxx_i2c_intr(int irq, void *dev_id) * ***************************************************************************** */ -static void -mv64xxx_i2c_prepare_for_io(struct mv64xxx_i2c_data *drv_data, - struct i2c_msg *msg) -{ - u32 dir = 0; - - drv_data->msg = msg; - drv_data->byte_posn = 0; - drv_data->bytes_left = msg->len; - drv_data->aborting = 0; - drv_data->rc = 0; - drv_data->cntl_bits = MV64XXX_I2C_REG_CONTROL_ACK | - MV64XXX_I2C_REG_CONTROL_INTEN | MV64XXX_I2C_REG_CONTROL_TWSIEN; - - if (msg->flags & I2C_M_RD) - dir = 1; - - if (msg->flags & I2C_M_TEN) { - drv_data->addr1 = 0xf0 | (((u32)msg->addr & 0x300) >> 7) | dir; - drv_data->addr2 = (u32)msg->addr & 0xff; - } else { - drv_data->addr1 = ((u32)msg->addr & 0x7f) << 1 | dir; - drv_data->addr2 = 0; - } -} - static void mv64xxx_i2c_wait_for_completion(struct mv64xxx_i2c_data *drv_data) { -- cgit v1.2.3 From 4243fa0bad551b8c8d4ff7104e8fd557ae848845 Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 16 May 2013 21:39:12 +0100 Subject: I2C: mv64xxx: fix race between FSM/interrupt and process context Asking for a multi-part message to be handled by this driver is racy; it has been observed that the following sequence is possible with this driver: - send start - send address + write - send data - send (repeated) start - send address + write - send (repeated) start - send address + read - unrecoverable bus hang (except by system reset) The problem is that the interrupt handling sees the next event after the first repeated start is sent - the IFLG bit is set in the register even though INTEN is disabled. Let's fix this by moving all of the message processing into interrupt context, rather than having it partly in IRQ and partly in process context. This allows us to move immediately to the next message in the interrupt handler and get on with the transfer, rather than incuring a couple of scheduling switches to get the next message. Signed-off-by: Russell King Acked-by: Mark A. Greer Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mv64xxx.c | 54 +++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c index bb37e14f3fbd..6356439454ee 100644 --- a/drivers/i2c/busses/i2c-mv64xxx.c +++ b/drivers/i2c/busses/i2c-mv64xxx.c @@ -86,6 +86,8 @@ enum { }; struct mv64xxx_i2c_data { + struct i2c_msg *msgs; + int num_msgs; int irq; u32 state; u32 action; @@ -194,7 +196,7 @@ mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status) if ((drv_data->bytes_left == 0) || (drv_data->aborting && (drv_data->byte_posn != 0))) { - if (drv_data->send_stop) { + if (drv_data->send_stop || drv_data->aborting) { drv_data->action = MV64XXX_I2C_ACTION_SEND_STOP; drv_data->state = MV64XXX_I2C_STATE_IDLE; } else { @@ -271,12 +273,25 @@ mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data) { switch(drv_data->action) { case MV64XXX_I2C_ACTION_SEND_RESTART: + /* We should only get here if we have further messages */ + BUG_ON(drv_data->num_msgs == 0); + drv_data->cntl_bits |= MV64XXX_I2C_REG_CONTROL_START; - drv_data->cntl_bits &= ~MV64XXX_I2C_REG_CONTROL_INTEN; writel(drv_data->cntl_bits, drv_data->reg_base + MV64XXX_I2C_REG_CONTROL); - drv_data->block = 0; - wake_up(&drv_data->waitq); + + drv_data->msgs++; + drv_data->num_msgs--; + + /* Setup for the next message */ + mv64xxx_i2c_prepare_for_io(drv_data, drv_data->msgs); + + /* + * We're never at the start of the message here, and by this + * time it's already too late to do any protocol mangling. + * Thankfully, do not advertise support for that feature. + */ + drv_data->send_stop = drv_data->num_msgs == 1; break; case MV64XXX_I2C_ACTION_CONTINUE: @@ -412,20 +427,15 @@ mv64xxx_i2c_wait_for_completion(struct mv64xxx_i2c_data *drv_data) static int mv64xxx_i2c_execute_msg(struct mv64xxx_i2c_data *drv_data, struct i2c_msg *msg, - int is_first, int is_last) + int is_last) { unsigned long flags; spin_lock_irqsave(&drv_data->lock, flags); mv64xxx_i2c_prepare_for_io(drv_data, msg); - if (is_first) { - drv_data->action = MV64XXX_I2C_ACTION_SEND_START; - drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_START_COND; - } else { - drv_data->action = MV64XXX_I2C_ACTION_SEND_ADDR_1; - drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_ADDR_1_ACK; - } + drv_data->action = MV64XXX_I2C_ACTION_SEND_START; + drv_data->state = MV64XXX_I2C_STATE_WAITING_FOR_START_COND; drv_data->send_stop = is_last; drv_data->block = 1; @@ -453,16 +463,20 @@ static int mv64xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) { struct mv64xxx_i2c_data *drv_data = i2c_get_adapdata(adap); - int i, rc; + int rc, ret = num; - for (i = 0; i < num; i++) { - rc = mv64xxx_i2c_execute_msg(drv_data, &msgs[i], - i == 0, i + 1 == num); - if (rc < 0) - return rc; - } + BUG_ON(drv_data->msgs != NULL); + drv_data->msgs = msgs; + drv_data->num_msgs = num; + + rc = mv64xxx_i2c_execute_msg(drv_data, &msgs[0], num == 1); + if (rc < 0) + ret = rc; + + drv_data->num_msgs = 0; + drv_data->msgs = NULL; - return num; + return ret; } static const struct i2c_algorithm mv64xxx_i2c_algo = { -- cgit v1.2.3 From fdeb94b5dc5bf9db7b3e36f3f38089a554f6a108 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 3 Jun 2013 23:09:14 +0100 Subject: ARM: 7745/1: psci: fix building without HOTPLUG_CPU The cpu_die field in smp_operations is not valid with CONFIG_HOTPLUG_CPU, so we must enclose it in #ifdef, but at least that lets us remove two other lines. Signed-off-by: Arnd Bergmann Cc: Stefano Stabellini Cc: Will Deacon Signed-off-by: Russell King --- arch/arm/kernel/psci_smp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/kernel/psci_smp.c b/arch/arm/kernel/psci_smp.c index 23a11424c568..219f1d73572a 100644 --- a/arch/arm/kernel/psci_smp.c +++ b/arch/arm/kernel/psci_smp.c @@ -68,8 +68,6 @@ void __ref psci_cpu_die(unsigned int cpu) /* We should never return */ panic("psci: cpu %d failed to shutdown\n", cpu); } -#else -#define psci_cpu_die NULL #endif bool __init psci_smp_available(void) @@ -80,5 +78,7 @@ bool __init psci_smp_available(void) struct smp_operations __initdata psci_smp_ops = { .smp_boot_secondary = psci_boot_secondary, +#ifdef CONFIG_HOTPLUG_CPU .cpu_die = psci_cpu_die, +#endif }; -- cgit v1.2.3 From 81f28946a8b066d484ca8935ff545d94ce730aa3 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Wed, 5 Jun 2013 02:44:00 +0100 Subject: ARM: 7746/1: mm: lazy cache flushing on non-mapped pages Currently flush_dcache_page() thinks pages as non-mapped if mapping_mapped(mapping) return false. This approach is very coase: - mmap on part of file may cause all pages backed on the file being thought as mmaped - file-backed pages aren't mapped into user space actually if the memory mmaped on the file isn't accessed This patch uses page_mapped() to decide if the page has been mapped. From the attached test code, I find there is much performance improvement(>25%) when accessing page caches via read under this situations, so memcpy benefits a lot from not flushing cache under this situation. No. read time without the patch No. read time with the patch ================================================================ No. 0, time 22615636 us No. 0, time 22014717 us No. 1, time 4387851 us No. 1, time 3113184 us No. 2, time 4276535 us No. 2, time 3005244 us No. 3, time 4259821 us No. 3, time 3001565 us No. 4, time 4263811 us No. 4, time 3002748 us No. 5, time 4258486 us No. 5, time 3004104 us No. 6, time 4253009 us No. 6, time 3002188 us No. 7, time 4262809 us No. 7, time 2998196 us No. 8, time 4264525 us No. 8, time 3007255 us No. 9, time 4267795 us No. 9, time 3005094 us 1), No.0. is to read the file from storage device, and others are to read the file from page caches basically. 2), file size is 512M, and is on ext4 over usb mass storage. 3), the test is done on Pandaboard. unsigned int sum = 0; unsigned long sum_val = 0; static unsigned long tv_diff(struct timeval *tv1, struct timeval *tv2) { return (tv2->tv_sec - tv1->tv_sec) * 1000000 + (tv2->tv_usec - tv1->tv_usec); } int main(int argc, char *argv[]) { char *mbuf, fbuf; int fd; int i; unsigned long page_size, size; struct stat stat; struct timeval t1, t2; unsigned char *rbuf = malloc(32 * page_size); if (!rbuf) { printf(" %sn", "malloc failed"); exit(-1); } page_size = getpagesize(); fd = open(argv[1], O_RDWR); assert(fd >= 0); fstat(fd, &stat); size = stat.st_size; printf("%s: file %s, size %lu, page size %lun", argv[0], argv[1], size, page_size); gettimeofday(&t1, NULL); mbuf = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (!mbuf) { printf(" %sn", "mmap failed"); exit(-1); } for (i = 0 ; i < size ; i += (page_size * 32)) { int rcnt; lseek(fd, i, SEEK_SET); rcnt = read(fd, rbuf, page_size * 32); if (rcnt != page_size * 32) { printf("%s: read faildn", __func__); exit(-1); } } free(rbuf); munmap(mbuf, size); gettimeofday(&t2, NULL); printf("tread mmaped time: %luusn", tv_diff(&t1, &t2)); close(fd); } Cc: Michel Lespinasse Cc: Andrew Morton Cc: Nicolas Pitre Reviewed-by: Will Deacon Acked-by: Catalin Marinas Signed-off-by: Ming Lei Signed-off-by: Russell King --- arch/arm/mm/flush.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c index 0d473cce501c..2ff66eb98ff0 100644 --- a/arch/arm/mm/flush.c +++ b/arch/arm/mm/flush.c @@ -287,7 +287,7 @@ void flush_dcache_page(struct page *page) mapping = page_mapping(page); if (!cache_ops_need_broadcast() && - mapping && !mapping_mapped(mapping)) + mapping && !page_mapped(page)) clear_bit(PG_dcache_clean, &page->flags); else { __flush_dcache_page(mapping, page); -- cgit v1.2.3 From 0305ffd7536270befc3e772bb978a6e3c6d37150 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Fri, 31 May 2013 05:17:29 +0100 Subject: ARM: 7740/1: MCPM: prettify debug output The CPU and cluster numbers (MPIDR affinity levels 0 and 1) may have at most 8 bits each. Signed-off-by: Nicolas Pitre Signed-off-by: Russell King --- arch/arm/common/mcpm_head.S | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/common/mcpm_head.S b/arch/arm/common/mcpm_head.S index 8178705c4b24..80f033614a1f 100644 --- a/arch/arm/common/mcpm_head.S +++ b/arch/arm/common/mcpm_head.S @@ -32,11 +32,11 @@ 1901: adr r0, 1902b bl printascii mov r0, r9 - bl printhex8 + bl printhex2 adr r0, 1903b bl printascii mov r0, r10 - bl printhex8 + bl printhex2 adr r0, 1904b bl printascii #endif -- cgit v1.2.3 From 6c93dd438aad39c60630dbe47fde8193c845f8b8 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Fri, 31 May 2013 05:24:03 +0100 Subject: ARM: 7741/1: mcpm_platsmp.c: remove empty smp_init_cpus method The smp_init_cpus method in the smp_operations structure is optional and can be omitted entirely. Signed-off-by: Nicolas Pitre Signed-off-by: Russell King --- arch/arm/common/mcpm_platsmp.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/arch/arm/common/mcpm_platsmp.c b/arch/arm/common/mcpm_platsmp.c index 3caed0db6986..510e5b13aa2e 100644 --- a/arch/arm/common/mcpm_platsmp.c +++ b/arch/arm/common/mcpm_platsmp.c @@ -19,10 +19,6 @@ #include #include -static void __init simple_smp_init_cpus(void) -{ -} - static int __cpuinit mcpm_boot_secondary(unsigned int cpu, struct task_struct *idle) { unsigned int mpidr, pcpu, pcluster, ret; @@ -74,7 +70,6 @@ static void mcpm_cpu_die(unsigned int cpu) #endif static struct smp_operations __initdata mcpm_smp_ops = { - .smp_init_cpus = simple_smp_init_cpus, .smp_boot_secondary = mcpm_boot_secondary, .smp_secondary_init = mcpm_secondary_init, #ifdef CONFIG_HOTPLUG_CPU -- cgit v1.2.3 From 4c4d41f200db375b2d2cc6d0a1de0606c8266398 Mon Sep 17 00:00:00 2001 From: Fan Du Date: Thu, 6 Jun 2013 10:15:54 +0800 Subject: xfrm: add LINUX_MIB_XFRMACQUIREERROR statistic counter When host ping its peer, ICMP echo request packet triggers IPsec policy, then host negotiates SA secret with its peer. After IKE installed SA for OUT direction, but before SA for IN direction installed, host get ICMP echo reply from its peer. At the time being, the SA state for IN direction could be XFRM_STATE_ACQ, then the received packet will be dropped after adding LINUX_MIB_XFRMINSTATEINVALID statistic. Adding a LINUX_MIB_XFRMACQUIREERROR statistic counter for such scenario when SA in larval state is much clearer for user than LINUX_MIB_XFRMINSTATEINVALID which indicates the SA is totally bad. Signed-off-by: Fan Du Signed-off-by: Steffen Klassert --- include/uapi/linux/snmp.h | 1 + net/xfrm/xfrm_input.c | 5 +++++ net/xfrm/xfrm_proc.c | 1 + 3 files changed, 7 insertions(+) diff --git a/include/uapi/linux/snmp.h b/include/uapi/linux/snmp.h index df2e8b4f9c03..ea17542ff321 100644 --- a/include/uapi/linux/snmp.h +++ b/include/uapi/linux/snmp.h @@ -287,6 +287,7 @@ enum LINUX_MIB_XFRMOUTPOLERROR, /* XfrmOutPolError */ LINUX_MIB_XFRMFWDHDRERROR, /* XfrmFwdHdrError*/ LINUX_MIB_XFRMOUTSTATEINVALID, /* XfrmOutStateInvalid */ + LINUX_MIB_XFRMACQUIREERROR, /* XfrmAcquireError */ __LINUX_MIB_XFRMMAX }; diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c index ab2bb42fe094..88843996f935 100644 --- a/net/xfrm/xfrm_input.c +++ b/net/xfrm/xfrm_input.c @@ -163,6 +163,11 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) skb->sp->xvec[skb->sp->len++] = x; spin_lock(&x->lock); + if (unlikely(x->km.state == XFRM_STATE_ACQ)) { + XFRM_INC_STATS(net, LINUX_MIB_XFRMACQUIREERROR); + goto drop_unlock; + } + if (unlikely(x->km.state != XFRM_STATE_VALID)) { XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEINVALID); goto drop_unlock; diff --git a/net/xfrm/xfrm_proc.c b/net/xfrm/xfrm_proc.c index c721b0d9ab8b..80cd1e55b834 100644 --- a/net/xfrm/xfrm_proc.c +++ b/net/xfrm/xfrm_proc.c @@ -44,6 +44,7 @@ static const struct snmp_mib xfrm_mib_list[] = { SNMP_MIB_ITEM("XfrmOutPolError", LINUX_MIB_XFRMOUTPOLERROR), SNMP_MIB_ITEM("XfrmFwdHdrError", LINUX_MIB_XFRMFWDHDRERROR), SNMP_MIB_ITEM("XfrmOutStateInvalid", LINUX_MIB_XFRMOUTSTATEINVALID), + SNMP_MIB_ITEM("XfrmAcquireError", LINUX_MIB_XFRMACQUIREERROR), SNMP_MIB_SENTINEL }; -- cgit v1.2.3 From 64b8dd299a1344626bbcfcbd62f19f1542349ad8 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 28 May 2013 00:58:19 -0700 Subject: Input: atmel_tsadcc - fix error handing with missing platform data If pdata is NULL, atmel_tsadcc_probe() will release all the resources and return 0, but we need a error code is returned in this case. Fix to return -EINVAL and move the check for pdata to the begin of this function. Signed-off-by: Wei Yongjun Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/atmel_tsadcc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/input/touchscreen/atmel_tsadcc.c b/drivers/input/touchscreen/atmel_tsadcc.c index 75a0693a3f91..bddabc595077 100644 --- a/drivers/input/touchscreen/atmel_tsadcc.c +++ b/drivers/input/touchscreen/atmel_tsadcc.c @@ -183,10 +183,13 @@ static int atmel_tsadcc_probe(struct platform_device *pdev) struct input_dev *input_dev; struct resource *res; struct at91_tsadcc_data *pdata = pdev->dev.platform_data; - int err = 0; + int err; unsigned int prsc; unsigned int reg; + if (!pdata) + return -EINVAL; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&pdev->dev, "no mmio resource defined.\n"); @@ -265,9 +268,6 @@ static int atmel_tsadcc_probe(struct platform_device *pdev) prsc = clk_get_rate(ts_dev->clk); dev_info(&pdev->dev, "Master clock is set at: %d Hz\n", prsc); - if (!pdata) - goto err_fail; - if (!pdata->adc_clock) pdata->adc_clock = ADC_DEFAULT_CLOCK; -- cgit v1.2.3 From 2db8e9d6b255a4fd070df70fa58306bf64b41984 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 4 Jun 2013 23:49:08 +0100 Subject: drm/i915: Track clients and print their object usage in debugfs By stashing a pointer of who opened the device and keeping a list of open fd, we can then walk each client and inspect how many objects they have open. For example, i915_gem_objects: 1102 objects, 613646336 bytes 663 [662] objects, 468783104 [468750336] bytes in gtt 37 [37] active objects, 46874624 [46874624] bytes 626 [625] inactive objects, 421908480 [421875712] bytes 282 unbound objects, 6512640 bytes 85 purgeable objects, 6787072 bytes 28 pinned mappable objects, 3686400 bytes 40 fault mappable objects, 27783168 bytes 2145386496 [536870912] gtt total Xorg: 43 objects, 32243712 bytes (10223616 active, 16683008 inactive, 4096 unbound) gnome-shell: 30 objects, 28381184 bytes (0 active, 28336128 inactive, 0 unbound) xonotic-linux64: 1032 objects, 569933824 bytes (46874624 active, 383545344 inactive, 6508544 unbound) v2: Use existing drm->filelist as pointed out by Ben. v3: Not even stashing the task_struct is required as Ben pointed out drm_file->pid. Signed-off-by: Chris Wilson Reviewed-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 42 +++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 0e7e3c04d939..d4e78b64ca87 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -196,6 +196,32 @@ static int i915_gem_object_list_info(struct seq_file *m, void *data) } \ } while (0) +struct file_stats { + int count; + size_t total, active, inactive, unbound; +}; + +static int per_file_stats(int id, void *ptr, void *data) +{ + struct drm_i915_gem_object *obj = ptr; + struct file_stats *stats = data; + + stats->count++; + stats->total += obj->base.size; + + if (obj->gtt_space) { + if (!list_empty(&obj->ring_list)) + stats->active += obj->base.size; + else + stats->inactive += obj->base.size; + } else { + if (!list_empty(&obj->global_list)) + stats->unbound += obj->base.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; @@ -204,6 +230,7 @@ static int i915_gem_object_info(struct seq_file *m, void* data) u32 count, mappable_count, purgeable_count; size_t size, mappable_size, purgeable_size; struct drm_i915_gem_object *obj; + struct drm_file *file; int ret; ret = mutex_lock_interruptible(&dev->struct_mutex); @@ -263,6 +290,21 @@ static int i915_gem_object_info(struct seq_file *m, void* data) dev_priv->gtt.total, dev_priv->gtt.mappable_end - dev_priv->gtt.start); + seq_printf(m, "\n"); + list_for_each_entry_reverse(file, &dev->filelist, lhead) { + struct file_stats stats; + + memset(&stats, 0, sizeof(stats)); + idr_for_each(&file->object_idr, per_file_stats, &stats); + seq_printf(m, "%s: %u objects, %zu bytes (%zu active, %zu inactive, %zu unbound)\n", + get_pid_task(file->pid, PIDTYPE_PID)->comm, + stats.count, + stats.total, + stats.active, + stats.inactive, + stats.unbound); + } + mutex_unlock(&dev->struct_mutex); return 0; -- cgit v1.2.3 From 3722dc8ebf041aedb1075078bd6728a38539a2aa Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 5 Jun 2013 19:33:03 +0100 Subject: ASoC: max98090: Guard runtime PM callbacks Otherwise the functions will be defined but unreferenced when runtime PM is disabled, generating warnings. Signed-off-by: Mark Brown --- sound/soc/codecs/max98090.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index cbb272b1f73d..3854d2209676 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -2350,6 +2350,7 @@ static int max98090_i2c_remove(struct i2c_client *client) return 0; } +#ifdef CONFIG_PM_RUNTIME static int max98090_runtime_resume(struct device *dev) { struct max98090_priv *max98090 = dev_get_drvdata(dev); @@ -2369,6 +2370,7 @@ static int max98090_runtime_suspend(struct device *dev) return 0; } +#endif static const struct dev_pm_ops max98090_pm = { SET_RUNTIME_PM_OPS(max98090_runtime_suspend, -- cgit v1.2.3 From 36bb00d4b2463b0b8b37fb0ce753813bf729b997 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Jun 2013 12:10:24 +0200 Subject: ALSA: hda - Drop hard dependency on CONFIG_SND_DYNAMIC_MINORS Currently HDMI codec driver sets the hard dependency (reverse selection) on CONFIG_SND_DYNAMIC_MINORS because the recent codecs may support more than two PCMs. But, this doesn't mean that we need always this option, since there can be a single PCM stream even with the modern codecs. This patch drops the hard dependency again but give more sensible error message when no enough PCMs are available due to the lack of this option. Signed-off-by: Takashi Iwai --- sound/pci/hda/Kconfig | 1 - sound/pci/hda/hda_codec.c | 19 +++++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 80a7d44bcf81..0c5371abecd2 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -140,7 +140,6 @@ config SND_HDA_CODEC_VIA config SND_HDA_CODEC_HDMI bool "Build HDMI/DisplayPort HD-audio codec support" - select SND_DYNAMIC_MINORS default y help Say Y here to include HDMI and DisplayPort HD-audio codec diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 55108b5fb291..679fba44bdb5 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -4461,12 +4461,13 @@ const char *snd_hda_pcm_type_name[HDA_PCM_NTYPES] = { /* * get the empty PCM device number to assign - * - * note the max device number is limited by HDA_MAX_PCMS, currently 10 */ -static int get_empty_pcm_device(struct hda_bus *bus, int type) +static int get_empty_pcm_device(struct hda_bus *bus, unsigned int type) { /* audio device indices; not linear to keep compatibility */ + /* assigned to static slots up to dev#10; if more needed, assign + * the later slot dynamically (when CONFIG_SND_DYNAMIC_MINORS=y) + */ static int audio_idx[HDA_PCM_NTYPES][5] = { [HDA_PCM_TYPE_AUDIO] = { 0, 2, 4, 5, -1 }, [HDA_PCM_TYPE_SPDIF] = { 1, -1 }, @@ -4480,18 +4481,28 @@ static int get_empty_pcm_device(struct hda_bus *bus, int type) return -EINVAL; } - for (i = 0; audio_idx[type][i] >= 0 ; i++) + for (i = 0; audio_idx[type][i] >= 0; i++) { +#ifndef CONFIG_SND_DYNAMIC_MINORS + if (audio_idx[type][i] >= 8) + break; +#endif if (!test_and_set_bit(audio_idx[type][i], bus->pcm_dev_bits)) return audio_idx[type][i]; + } +#ifdef CONFIG_SND_DYNAMIC_MINORS /* non-fixed slots starting from 10 */ for (i = 10; i < 32; i++) { if (!test_and_set_bit(i, bus->pcm_dev_bits)) return i; } +#endif snd_printk(KERN_WARNING "Too many %s devices\n", snd_hda_pcm_type_name[type]); +#ifndef CONFIG_SND_DYNAMIC_MINORS + snd_printk(KERN_WARNING "Consider building the kernel with CONFIG_SND_DYNAMIC_MINORS=y\n"); +#endif return -EAGAIN; } -- cgit v1.2.3 From 26cb3bb14950ee017f311f3bf1ae82f95ee0be64 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 31 May 2013 13:29:06 +0530 Subject: spi: spi-nuc900: Remove redundant platform_set_drvdata() Setting platform data to NULL is not necessary. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown --- drivers/spi/spi-nuc900.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/spi/spi-nuc900.c b/drivers/spi/spi-nuc900.c index b3f9ec83ef73..150d85453c27 100644 --- a/drivers/spi/spi-nuc900.c +++ b/drivers/spi/spi-nuc900.c @@ -459,8 +459,6 @@ static int nuc900_spi_remove(struct platform_device *dev) free_irq(hw->irq, hw); - platform_set_drvdata(dev, NULL); - spi_bitbang_stop(&hw->bitbang); clk_disable(hw->clk); -- cgit v1.2.3 From 91738a95bf40a3405bb7b8a3e76d30e060a80705 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Wed, 5 Jun 2013 14:21:51 -0300 Subject: drm/i915: add ibx_irq_preinstall So we can remove some duplicate code. All the PCHs are very similar and right now the code is the same. I plan to add more code, so we would have more duplicated code. Signed-off-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 44 ++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 63996aa3fb17..c482e8ae58dc 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -2472,6 +2472,25 @@ void i915_hangcheck_elapsed(unsigned long data) DRM_I915_HANGCHECK_JIFFIES)); } +static void ibx_irq_preinstall(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + if (HAS_PCH_NOP(dev)) + return; + + /* south display irq */ + I915_WRITE(SDEIMR, 0xffffffff); + /* + * SDEIER is also touched by the interrupt handler to work around missed + * PCH interrupts. Hence we can't update it after the interrupt handler + * is enabled - instead we unconditionally enable all PCH interrupt + * sources here, but then only unmask them as needed with SDEIMR. + */ + I915_WRITE(SDEIER, 0xffffffff); + POSTING_READ(SDEIER); +} + /* drm_dma.h hooks */ static void ironlake_irq_preinstall(struct drm_device *dev) @@ -2493,16 +2512,7 @@ static void ironlake_irq_preinstall(struct drm_device *dev) I915_WRITE(GTIER, 0x0); POSTING_READ(GTIER); - /* south display irq */ - I915_WRITE(SDEIMR, 0xffffffff); - /* - * SDEIER is also touched by the interrupt handler to work around missed - * PCH interrupts. Hence we can't update it after the interrupt handler - * is enabled - instead we unconditionally enable all PCH interrupt - * sources here, but then only unmask them as needed with SDEIMR. - */ - I915_WRITE(SDEIER, 0xffffffff); - POSTING_READ(SDEIER); + ibx_irq_preinstall(dev); } static void ivybridge_irq_preinstall(struct drm_device *dev) @@ -2529,19 +2539,7 @@ static void ivybridge_irq_preinstall(struct drm_device *dev) I915_WRITE(GEN6_PMIER, 0x0); POSTING_READ(GEN6_PMIER); - if (HAS_PCH_NOP(dev)) - return; - - /* south display irq */ - I915_WRITE(SDEIMR, 0xffffffff); - /* - * SDEIER is also touched by the interrupt handler to work around missed - * PCH interrupts. Hence we can't update it after the interrupt handler - * is enabled - instead we unconditionally enable all PCH interrupt - * sources here, but then only unmask them as needed with SDEIMR. - */ - I915_WRITE(SDEIER, 0xffffffff); - POSTING_READ(SDEIER); + ibx_irq_preinstall(dev); } static void valleyview_irq_preinstall(struct drm_device *dev) -- cgit v1.2.3 From b22ffdcd25d67a07f2b5a75a7805826bfe8597f1 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 22 Apr 2013 10:29:26 +0200 Subject: mmc: tmio: postpone controller reset during resume When resuming, the tmio_mmc_host_resume() function is run when the controller might still be powered down. Issuing a reset command to it at that time has no effect. This patch postpones resetting the controller until the first powering-up .set_ios() call. Reported-by: Nguyen Viet Dung Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/tmio_mmc.h | 1 + drivers/mmc/host/tmio_mmc_pio.c | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index d857f5c6e7d9..759d8f4f130c 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -85,6 +85,7 @@ struct tmio_mmc_host { unsigned long last_req_ts; struct mutex ios_lock; /* protect set_ios() context */ bool native_hotplug; + bool resuming; }; int tmio_mmc_host_probe(struct tmio_mmc_host **host, diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index f508ecb5b8a7..435cc4d2520f 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -862,6 +862,10 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (!host->power) { tmio_mmc_clk_update(mmc); pm_runtime_get_sync(dev); + if (host->resuming) { + tmio_mmc_reset(host); + host->resuming = false; + } } tmio_mmc_set_clock(host, ios->clock); if (!host->power) { @@ -1154,10 +1158,10 @@ int tmio_mmc_host_resume(struct device *dev) struct mmc_host *mmc = dev_get_drvdata(dev); struct tmio_mmc_host *host = mmc_priv(mmc); - tmio_mmc_reset(host); tmio_mmc_enable_dma(host, true); /* The MMC core will perform the complete set up */ + host->resuming = true; return mmc_resume_host(mmc); } EXPORT_SYMBOL(tmio_mmc_host_resume); -- cgit v1.2.3 From 19f1ba51c79f133aec3ce558b8292e3b081363f3 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 15 May 2013 07:50:51 +0200 Subject: mmc: sh_mmcif: don't clear masked interrupts Masking events on MMCIF means that an occurrence of the masked event won't raise an interrupt, but the event bit will still be set in the interrupt status register. If simultaneously a different event occurs which was enabled, both flags will be set. However, only the unmasked event bit should be cleared in the status register in such a case. Clearing also the masked bit can lead to lost interrupts, which indeed can be observed on the armadillo800eva r8a7740 board with an eMMC chip. The problem has been introduced by the recent "mmc: sh_mmcif: simplify IRQ processing" patch. Fix the problem by only clearing enabled interrupts. Signed-off-by: Guennadi Liakhovetski Tested-by: Nguyen Viet Dung Tested-by: Kuninori Morimoto Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 117a1f774720..8ef5efad45d0 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -1244,7 +1244,8 @@ static irqreturn_t sh_mmcif_intr(int irq, void *dev_id) u32 state; state = sh_mmcif_readl(host->addr, MMCIF_CE_INT); - sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~state); + sh_mmcif_writel(host->addr, MMCIF_CE_INT, + ~(state & sh_mmcif_readl(host->addr, MMCIF_CE_INT_MASK))); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state & MASK_CLEAN); if (state & ~MASK_CLEAN) -- cgit v1.2.3 From 5434fd926d1e4de5d82fcbd4e7e4698cc6575bdb Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Thu, 6 Jun 2013 13:09:32 +0300 Subject: Revert "drm/i915: Include display_mmio_offset in sequencer index/data registers" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We use port I/O for VGA register access, so adding display_mmio_offset is just wrong. This reverts commit 56a12a509296c87d6f149be86c6694d312b21d35. Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 47a9de0d51cc..ff9f71af9347 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -147,15 +147,9 @@ #define VGA_MSR_MEM_EN (1<<1) #define VGA_MSR_CGA_MODE (1<<0) -/* - * SR01 is the only VGA register touched on non-UMS setups. - * VLV doesn't do UMS, so the sequencer index/data registers - * are the only VGA registers which need to include - * display_mmio_offset. - */ -#define VGA_SR_INDEX (dev_priv->info->display_mmio_offset + 0x3c4) +#define VGA_SR_INDEX 0x3c4 #define SR01 1 -#define VGA_SR_DATA (dev_priv->info->display_mmio_offset + 0x3c5) +#define VGA_SR_DATA 0x3c5 #define VGA_AR_INDEX 0x3c0 #define VGA_AR_VID_EN (1<<5) -- cgit v1.2.3 From 63cbb0747622d923665294519e9a24bc9c654c19 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Tue, 4 Jun 2013 13:48:59 +0300 Subject: drm/i915: Always load the display palette before enabling the pipe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Loading the palette after the planes are enabled can risk showing incorrect colors. ILK+ already load the palette before even the pipe is enabled. Just follow the same order for gen2-4 and VLV. According to BSpec the requirements for palette access are display core clock and display PLL running. In certain platforms just the core clock may be enough. But we definitely should have both running when this gets called during the modeset. v2: Amend the commit message with some display PLL/core clock info Signed-off-by: Ville Syrjälä Reviewed-by: Rodrigo Vivi Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index ca90d36fd650..240dfc7af8b6 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3618,10 +3618,11 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc) /* Enable panel fitting for eDP */ i9xx_pfit_enable(intel_crtc); + intel_crtc_load_lut(crtc); + intel_enable_pipe(dev_priv, pipe, false); intel_enable_plane(dev_priv, plane, pipe); - intel_crtc_load_lut(crtc); intel_update_fbc(dev); /* Give the overlay scaler a chance to enable if it's on this pipe */ @@ -3657,12 +3658,13 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc) /* Enable panel fitting for LVDS */ i9xx_pfit_enable(intel_crtc); + intel_crtc_load_lut(crtc); + intel_enable_pipe(dev_priv, pipe, false); intel_enable_plane(dev_priv, plane, pipe); if (IS_G4X(dev)) g4x_fixup_plane(dev_priv, pipe); - intel_crtc_load_lut(crtc); intel_update_fbc(dev); /* Give the overlay scaler a chance to enable if it's on this pipe */ -- cgit v1.2.3 From 5c38d48cd8dfb3332c072a257bc2a6dab1e5dc3b Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Tue, 4 Jun 2013 13:49:00 +0300 Subject: drm/i915: Always enable the cursor right after the primary plane MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow the same sequence when enabling the cursor plane during modeset. No point in doing this stuff in different order on different generations. This should also avoid a needless wait for vblank for the g4x cursor workaround when the cursor gets enabled anyway. Acked-by: Egbert Eich Signed-off-by: Ville Syrjälä Reviewed-by: Rodrigo Vivi Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 240dfc7af8b6..532a4fa67bb2 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3219,6 +3219,7 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) intel_enable_pipe(dev_priv, pipe, intel_crtc->config.has_pch_encoder); intel_enable_plane(dev_priv, plane, pipe); + intel_crtc_update_cursor(crtc, true); if (intel_crtc->config.has_pch_encoder) ironlake_pch_enable(crtc); @@ -3227,8 +3228,6 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) intel_update_fbc(dev); mutex_unlock(&dev->struct_mutex); - intel_crtc_update_cursor(crtc, true); - for_each_encoder_on_crtc(dev, crtc, encoder) encoder->enable(encoder); @@ -3328,6 +3327,7 @@ static void haswell_crtc_enable(struct drm_crtc *crtc) intel_enable_pipe(dev_priv, pipe, intel_crtc->config.has_pch_encoder); intel_enable_plane(dev_priv, plane, pipe); + intel_crtc_update_cursor(crtc, true); hsw_enable_ips(intel_crtc); @@ -3338,8 +3338,6 @@ static void haswell_crtc_enable(struct drm_crtc *crtc) intel_update_fbc(dev); mutex_unlock(&dev->struct_mutex); - intel_crtc_update_cursor(crtc, true); - for_each_encoder_on_crtc(dev, crtc, encoder) encoder->enable(encoder); @@ -3622,12 +3620,12 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc) intel_enable_pipe(dev_priv, pipe, false); intel_enable_plane(dev_priv, plane, pipe); + intel_crtc_update_cursor(crtc, true); 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); mutex_unlock(&dev_priv->dpio_lock); } @@ -3662,6 +3660,7 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc) intel_enable_pipe(dev_priv, pipe, false); intel_enable_plane(dev_priv, plane, pipe); + intel_crtc_update_cursor(crtc, true); if (IS_G4X(dev)) g4x_fixup_plane(dev_priv, pipe); @@ -3669,7 +3668,6 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc) /* 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); for_each_encoder_on_crtc(dev, crtc, encoder) encoder->enable(encoder); -- cgit v1.2.3 From f440eb1354bc92855bbbff08d3ac4a93029c3097 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Tue, 4 Jun 2013 13:49:01 +0300 Subject: drm/i915: Enable the overlay right after primary and cursor planes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Again follow the same sequence for all generations, because doing otherwise just doesn't make sense. Signed-off-by: Ville Syrjälä Reviewed-by: Rodrigo Vivi Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 532a4fa67bb2..bf8fe819cfbd 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3622,11 +3622,11 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc) intel_enable_plane(dev_priv, plane, pipe); intel_crtc_update_cursor(crtc, true); - 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_update_fbc(dev); + mutex_unlock(&dev_priv->dpio_lock); } @@ -3664,11 +3664,11 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc) if (IS_G4X(dev)) g4x_fixup_plane(dev_priv, pipe); - 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_update_fbc(dev); + for_each_encoder_on_crtc(dev, crtc, encoder) encoder->enable(encoder); } -- cgit v1.2.3 From 0d5b8c61d877072431d29f3338ec081a3f5d7981 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Tue, 4 Jun 2013 13:49:02 +0300 Subject: drm/i915: Follow the same sequence when disabling planes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First disable FBC, then IPS, then disable all planes, and finally disable the pipe. v2: Mention IPS in the commit message Signed-off-by: Ville Syrjälä Reviewed-by: Rodrigo Vivi Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index bf8fe819cfbd..1e3f31721615 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3386,13 +3386,13 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) intel_crtc_wait_for_pending_flips(crtc); drm_vblank_off(dev, pipe); - intel_crtc_update_cursor(crtc, false); - - intel_disable_plane(dev_priv, plane, pipe); if (dev_priv->cfb_plane == plane) intel_disable_fbc(dev); + intel_crtc_update_cursor(crtc, false); + intel_disable_plane(dev_priv, plane, pipe); + intel_set_pch_fifo_underrun_reporting(dev, pipe, false); intel_disable_pipe(dev_priv, pipe); @@ -3465,7 +3465,6 @@ static void haswell_crtc_disable(struct drm_crtc *crtc) intel_crtc_wait_for_pending_flips(crtc); drm_vblank_off(dev, pipe); - intel_crtc_update_cursor(crtc, false); /* FBC must be disabled before disabling the plane on HSW. */ if (dev_priv->cfb_plane == plane) @@ -3473,6 +3472,7 @@ static void haswell_crtc_disable(struct drm_crtc *crtc) hsw_disable_ips(intel_crtc); + intel_crtc_update_cursor(crtc, false); intel_disable_plane(dev_priv, plane, pipe); if (intel_crtc->config.has_pch_encoder) @@ -3706,13 +3706,14 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc) /* 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) intel_disable_fbc(dev); + intel_crtc_dpms_overlay(intel_crtc, false); + intel_crtc_update_cursor(crtc, false); intel_disable_plane(dev_priv, plane, pipe); + intel_disable_pipe(dev_priv, pipe); i9xx_pfit_disable(intel_crtc); -- cgit v1.2.3 From b85dfcf9240043e81ba13b1bc99afc8645bf4c6a Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Tue, 4 Jun 2013 13:49:03 +0300 Subject: drm/i915: Drop overlay DPMS call from valleyview_crtc_enable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VLV doesn't have the old video overlay. Signed-off-by: Ville Syrjälä Reviewed-by: Rodrigo Vivi Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 1e3f31721615..62557ecc0eff 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3622,9 +3622,6 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc) intel_enable_plane(dev_priv, plane, pipe); intel_crtc_update_cursor(crtc, true); - /* Give the overlay scaler a chance to enable if it's on this pipe */ - intel_crtc_dpms_overlay(intel_crtc, true); - intel_update_fbc(dev); mutex_unlock(&dev_priv->dpio_lock); -- cgit v1.2.3 From bb53d4aeac59079240605ef9269166f204612b78 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Tue, 4 Jun 2013 13:49:04 +0300 Subject: drm/i915: Disable/restore all sprite planes around modeset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Disable/restore sprite planes around mode-set just like we do for the primary and cursor planes. Now that we have working sprite clipping, this actually works quite decently. Previosuly we didn't even bother to disable sprites when changing mode, which could lead to a corrupted sprite appearing on the screen after a modeset (at least on my IVB). Not sure if all hardware generations would be so forgiving when enabled sprites end up outside the pipe dimensons. v2: Disable rather than enable sprites in ironlake_crtc_disable() Signed-off-by: Ville Syrjälä Reviewed-by: Rodrigo Vivi Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 29 +++++++++++++++++++++++++++++ drivers/gpu/drm/i915/intel_drv.h | 1 + drivers/gpu/drm/i915/intel_sprite.c | 8 ++++++++ 3 files changed, 38 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 62557ecc0eff..f084a1dd8f4d 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3164,6 +3164,28 @@ static void ironlake_pfit_enable(struct intel_crtc *crtc) } } +static void intel_enable_planes(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + enum pipe pipe = to_intel_crtc(crtc)->pipe; + struct intel_plane *intel_plane; + + list_for_each_entry(intel_plane, &dev->mode_config.plane_list, base.head) + if (intel_plane->pipe == pipe) + intel_plane_restore(&intel_plane->base); +} + +static void intel_disable_planes(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + enum pipe pipe = to_intel_crtc(crtc)->pipe; + struct intel_plane *intel_plane; + + list_for_each_entry(intel_plane, &dev->mode_config.plane_list, base.head) + if (intel_plane->pipe == pipe) + intel_plane_disable(&intel_plane->base); +} + static void ironlake_crtc_enable(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; @@ -3219,6 +3241,7 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) intel_enable_pipe(dev_priv, pipe, intel_crtc->config.has_pch_encoder); intel_enable_plane(dev_priv, plane, pipe); + intel_enable_planes(crtc); intel_crtc_update_cursor(crtc, true); if (intel_crtc->config.has_pch_encoder) @@ -3327,6 +3350,7 @@ static void haswell_crtc_enable(struct drm_crtc *crtc) intel_enable_pipe(dev_priv, pipe, intel_crtc->config.has_pch_encoder); intel_enable_plane(dev_priv, plane, pipe); + intel_enable_planes(crtc); intel_crtc_update_cursor(crtc, true); hsw_enable_ips(intel_crtc); @@ -3391,6 +3415,7 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) intel_disable_fbc(dev); intel_crtc_update_cursor(crtc, false); + intel_disable_planes(crtc); intel_disable_plane(dev_priv, plane, pipe); intel_set_pch_fifo_underrun_reporting(dev, pipe, false); @@ -3473,6 +3498,7 @@ static void haswell_crtc_disable(struct drm_crtc *crtc) hsw_disable_ips(intel_crtc); intel_crtc_update_cursor(crtc, false); + intel_disable_planes(crtc); intel_disable_plane(dev_priv, plane, pipe); if (intel_crtc->config.has_pch_encoder) @@ -3620,6 +3646,7 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc) intel_enable_pipe(dev_priv, pipe, false); intel_enable_plane(dev_priv, plane, pipe); + intel_enable_planes(crtc); intel_crtc_update_cursor(crtc, true); intel_update_fbc(dev); @@ -3657,6 +3684,7 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc) intel_enable_pipe(dev_priv, pipe, false); intel_enable_plane(dev_priv, plane, pipe); + intel_enable_planes(crtc); intel_crtc_update_cursor(crtc, true); if (IS_G4X(dev)) g4x_fixup_plane(dev_priv, pipe); @@ -3709,6 +3737,7 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc) intel_crtc_dpms_overlay(intel_crtc, false); intel_crtc_update_cursor(crtc, false); + intel_disable_planes(crtc); intel_disable_plane(dev_priv, plane, pipe); intel_disable_pipe(dev_priv, pipe); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index eae3dbc4e3ee..6bbebf824078 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -633,6 +633,7 @@ extern void intel_connector_dpms(struct drm_connector *, int mode); extern bool intel_connector_get_hw_state(struct intel_connector *connector); extern void intel_modeset_check_state(struct drm_device *dev); extern void intel_plane_restore(struct drm_plane *plane); +extern void intel_plane_disable(struct drm_plane *plane); static inline struct intel_encoder *intel_attached_encoder(struct drm_connector *connector) diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 04d38d4d811a..1fa5612a4572 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -957,6 +957,14 @@ void intel_plane_restore(struct drm_plane *plane) intel_plane->src_w, intel_plane->src_h); } +void intel_plane_disable(struct drm_plane *plane) +{ + if (!plane->crtc || !plane->fb) + return; + + intel_disable_plane(plane); +} + static const struct drm_plane_funcs intel_plane_funcs = { .update_plane = intel_update_plane, .disable_plane = intel_disable_plane, -- cgit v1.2.3 From 653e10266df8319d6003fbf46ec34865a5a363f6 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Tue, 4 Jun 2013 13:49:05 +0300 Subject: drm/i915: Improve assert_planes_disabled() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ever since gen4 primary planes were fixed to pipes. And for gen2-3, don't check plane B if it doesn't exist. Signed-off-by: Ville Syrjälä Reviewed-by: Rodrigo Vivi Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index f084a1dd8f4d..0f0ac933bd6c 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1105,12 +1105,13 @@ static void assert_plane(struct drm_i915_private *dev_priv, static void assert_planes_disabled(struct drm_i915_private *dev_priv, enum pipe pipe) { + struct drm_device *dev = dev_priv->dev; int reg, i; u32 val; int cur_pipe; - /* Planes are fixed to pipes on ILK+ */ - if (HAS_PCH_SPLIT(dev_priv->dev) || IS_VALLEYVIEW(dev_priv->dev)) { + /* Primary planes are fixed to pipes on gen4+ */ + if (INTEL_INFO(dev)->gen >= 4) { reg = DSPCNTR(pipe); val = I915_READ(reg); WARN((val & DISPLAY_PLANE_ENABLE), @@ -1120,7 +1121,7 @@ static void assert_planes_disabled(struct drm_i915_private *dev_priv, } /* Need to check both planes against the pipe */ - for (i = 0; i < 2; i++) { + for (i = 0; i < INTEL_INFO(dev)->num_pipes; i++) { reg = DSPCNTR(i); val = I915_READ(reg); cur_pipe = (val & DISPPLANE_SEL_PIPE_MASK) >> -- cgit v1.2.3 From 20674eef808dada6c30988a8cfcb908406cdea02 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Tue, 4 Jun 2013 13:49:06 +0300 Subject: drm/i915: Spruce up assert_sprites_disabled() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make assert_sprites_disabled() operational on all platforms where we currently have sprite support enabled. Signed-off-by: Ville Syrjälä Reviewed-by: Rodrigo Vivi Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 0f0ac933bd6c..c593ed0ca1b9 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1135,19 +1135,30 @@ static void assert_planes_disabled(struct drm_i915_private *dev_priv, static void assert_sprites_disabled(struct drm_i915_private *dev_priv, enum pipe pipe) { + struct drm_device *dev = dev_priv->dev; int reg, i; u32 val; - if (!IS_VALLEYVIEW(dev_priv->dev)) - return; - - /* Need to check both planes against the pipe */ - for (i = 0; i < dev_priv->num_plane; i++) { - reg = SPCNTR(pipe, i); + if (IS_VALLEYVIEW(dev)) { + for (i = 0; i < dev_priv->num_plane; i++) { + reg = SPCNTR(pipe, i); + val = I915_READ(reg); + WARN((val & SP_ENABLE), + "sprite %c assertion failure, should be off on pipe %c but is still active\n", + sprite_name(pipe, i), pipe_name(pipe)); + } + } else if (INTEL_INFO(dev)->gen >= 7) { + reg = SPRCTL(pipe); + val = I915_READ(reg); + WARN((val & SPRITE_ENABLE), + "sprite %c assertion failure, should be off on pipe %c but is still active\n", + plane_name(pipe), pipe_name(pipe)); + } else if (INTEL_INFO(dev)->gen >= 5) { + reg = DVSCNTR(pipe); val = I915_READ(reg); - WARN((val & SP_ENABLE), + WARN((val & DVS_ENABLE), "sprite %c assertion failure, should be off on pipe %c but is still active\n", - sprite_name(pipe, i), pipe_name(pipe)); + plane_name(pipe), pipe_name(pipe)); } } -- cgit v1.2.3 From 14420bd0065c1757a353e36ebc9cc4bdc6932dcd Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Tue, 4 Jun 2013 13:49:07 +0300 Subject: drm/i915: Assert dpll running in intel_crtc_load_lut() on pre-PCH platforms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adding more context from Ville's reply to Rodrigo's question why we need this: "The spec says that on some hardware you need to PLL running before you can poke at the palette registers. I didn't actually try to anger the hardware so I'm not really sure what would happen otherwise, but IIRC Jesse said something about a hard system hang..." And generally documenting such ordering constraints with asserts is Just Good. Signed-off-by: Ville Syrjälä Reviewed-by: Rodrigo Vivi [danvet: Spruce up the commit message a lot.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index c593ed0ca1b9..0e1f82810f58 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6311,6 +6311,9 @@ void intel_crtc_load_lut(struct drm_crtc *crtc) if (!crtc->enabled || !intel_crtc->active) return; + if (!HAS_PCH_SPLIT(dev_priv->dev)) + assert_pll_enabled(dev_priv, pipe); + /* use legacy palette for Ironlake */ if (HAS_PCH_SPLIT(dev)) palreg = LGC_PALETTE(pipe); -- cgit v1.2.3 From e7ecc27e520a9c9891362c6dabd18c4da9885946 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Jun 2013 14:00:23 +0200 Subject: ALSA: hda - Introduce bit flags to snd_hda_codec_read/write() snd_hda_codec_read(), snd_hda_codec_write() & co take the argument "direct" that indicates whether the given NID is a direct reference or an indirect reference. However, the indirect reference is practically unimplemented and never exists. And moreover, we don't need the indication of indirect reference at this high level, as NID can be represented in 16bit values at this point. Meanwhile, there are some cases where it'd be nice to give some operational options to these functions. So, we can reuse this argument as a new bit flag! Pretty frugal, eh? All callers so far pass zero to this argument, thus there is no behavior change by this replacement. The real usage of this new bit option will be added in the following patches. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 41 ++++++++++++++++++++--------------------- sound/pci/hda/hda_codec.h | 8 ++++---- 2 files changed, 24 insertions(+), 25 deletions(-) diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 679fba44bdb5..503869aad7f9 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -185,20 +185,19 @@ EXPORT_SYMBOL_HDA(snd_hda_get_jack_type); * Compose a 32bit command word to be sent to the HD-audio controller */ static inline unsigned int -make_codec_cmd(struct hda_codec *codec, hda_nid_t nid, int direct, +make_codec_cmd(struct hda_codec *codec, hda_nid_t nid, int flags, unsigned int verb, unsigned int parm) { u32 val; - if ((codec->addr & ~0xf) || (direct & ~1) || (nid & ~0x7f) || + if ((codec->addr & ~0xf) || (nid & ~0x7f) || (verb & ~0xfff) || (parm & ~0xffff)) { - printk(KERN_ERR "hda-codec: out of range cmd %x:%x:%x:%x:%x\n", - codec->addr, direct, nid, verb, parm); + printk(KERN_ERR "hda-codec: out of range cmd %x:%x:%x:%x\n", + codec->addr, nid, verb, parm); return ~0; } val = (u32)codec->addr << 28; - val |= (u32)direct << 27; val |= (u32)nid << 20; val |= verb << 8; val |= parm; @@ -209,7 +208,7 @@ make_codec_cmd(struct hda_codec *codec, hda_nid_t nid, int direct, * Send and receive a verb */ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd, - unsigned int *res) + int flags, unsigned int *res) { struct hda_bus *bus = codec->bus; int err; @@ -255,7 +254,7 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd, * snd_hda_codec_read - send a command and get the response * @codec: the HDA codec * @nid: NID to send the command - * @direct: direct flag + * @flags: optional bit flags * @verb: the verb to send * @parm: the parameter for the verb * @@ -264,12 +263,12 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd, * Returns the obtained response value, or -1 for an error. */ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, - int direct, + int flags, unsigned int verb, unsigned int parm) { - unsigned cmd = make_codec_cmd(codec, nid, direct, verb, parm); + unsigned cmd = make_codec_cmd(codec, nid, flags, verb, parm); unsigned int res; - if (codec_exec_verb(codec, cmd, &res)) + if (codec_exec_verb(codec, cmd, flags, &res)) return -1; return res; } @@ -279,7 +278,7 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_read); * snd_hda_codec_write - send a single command without waiting for response * @codec: the HDA codec * @nid: NID to send the command - * @direct: direct flag + * @flags: optional bit flags * @verb: the verb to send * @parm: the parameter for the verb * @@ -287,12 +286,12 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_read); * * Returns 0 if successful, or a negative error code. */ -int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct, - unsigned int verb, unsigned int parm) +int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags, + unsigned int verb, unsigned int parm) { - unsigned int cmd = make_codec_cmd(codec, nid, direct, verb, parm); + unsigned int cmd = make_codec_cmd(codec, nid, flags, verb, parm); unsigned int res; - return codec_exec_verb(codec, cmd, + return codec_exec_verb(codec, cmd, flags, codec->bus->sync_write ? &res : NULL); } EXPORT_SYMBOL_HDA(snd_hda_codec_write); @@ -3582,7 +3581,7 @@ EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls); * snd_hda_codec_write_cache - send a single command with caching * @codec: the HDA codec * @nid: NID to send the command - * @direct: direct flag + * @flags: optional bit flags * @verb: the verb to send * @parm: the parameter for the verb * @@ -3591,7 +3590,7 @@ EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls); * Returns 0 if successful, or a negative error code. */ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, - int direct, unsigned int verb, unsigned int parm) + int flags, unsigned int verb, unsigned int parm) { int err; struct hda_cache_head *c; @@ -3600,7 +3599,7 @@ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, cache_only = codec->cached_write; if (!cache_only) { - err = snd_hda_codec_write(codec, nid, direct, verb, parm); + err = snd_hda_codec_write(codec, nid, flags, verb, parm); if (err < 0) return err; } @@ -3624,7 +3623,7 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_write_cache); * snd_hda_codec_update_cache - check cache and write the cmd only when needed * @codec: the HDA codec * @nid: NID to send the command - * @direct: direct flag + * @flags: optional bit flags * @verb: the verb to send * @parm: the parameter for the verb * @@ -3635,7 +3634,7 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_write_cache); * Returns 0 if successful, or a negative error code. */ int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid, - int direct, unsigned int verb, unsigned int parm) + int flags, unsigned int verb, unsigned int parm) { struct hda_cache_head *c; u32 key; @@ -3651,7 +3650,7 @@ int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid, return 0; } mutex_unlock(&codec->bus->cmd_mutex); - return snd_hda_codec_write_cache(codec, nid, direct, verb, parm); + return snd_hda_codec_write_cache(codec, nid, flags, verb, parm); } EXPORT_SYMBOL_HDA(snd_hda_codec_update_cache); diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index c93f9021f452..39a658e02988 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -945,9 +945,9 @@ int snd_hda_codec_update_widgets(struct hda_codec *codec); * low level functions */ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, - int direct, + int flags, unsigned int verb, unsigned int parm); -int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct, +int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags, unsigned int verb, unsigned int parm); #define snd_hda_param_read(codec, nid, param) \ snd_hda_codec_read(codec, nid, 0, AC_VERB_PARAMETERS, param) @@ -986,11 +986,11 @@ int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex); /* cached write */ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, - int direct, unsigned int verb, unsigned int parm); + int flags, unsigned int verb, unsigned int parm); void snd_hda_sequence_write_cache(struct hda_codec *codec, const struct hda_verb *seq); int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid, - int direct, unsigned int verb, unsigned int parm); + int flags, unsigned int verb, unsigned int parm); void snd_hda_codec_resume_cache(struct hda_codec *codec); /* both for cmd & amp caches */ void snd_hda_codec_flush_cache(struct hda_codec *codec); -- cgit v1.2.3 From 63e51fd708f511a5989da04c669647993bc1a512 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Jun 2013 14:20:19 +0200 Subject: ALSA: hda - Don't take unresponsive D3 transition too serious When a codec is powered off, some systems don't respond properly after D3 FG transition, while the driver still expects the response and tries to fall back to different modes (polling and single-cmd). When the fallback happens, the driver stays in that mode, and falling back to the single-cmd mode means it'll loose the unsol event handling, too. The unresponsiveness at D3 isn't too serious, thus this fallback is mostly superfluous. We can gracefully ignore the error there so that the driver keeps the normal operation mode. This patch adds a new bit flag for codec read/write, set in the power transition stage, which is notified to the controller driver via a new bus->no_response_fallback flag. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 7 ++++++- sound/pci/hda/hda_codec.h | 3 +++ sound/pci/hda/hda_intel.c | 3 +++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 503869aad7f9..35090b3acbac 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -221,6 +221,8 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd, again: snd_hda_power_up(codec); mutex_lock(&bus->cmd_mutex); + if (flags & HDA_RW_NO_RESPONSE_FALLBACK) + bus->no_response_fallback = 1; for (;;) { trace_hda_send_cmd(codec, cmd); err = bus->ops.command(bus, cmd); @@ -233,6 +235,7 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd, *res = bus->ops.get_response(bus, codec->addr); trace_hda_get_response(codec, *res); } + bus->no_response_fallback = 0; mutex_unlock(&bus->cmd_mutex); snd_hda_power_down(codec); if (!codec_in_pm(codec) && res && *res == -1 && bus->rirb_error) { @@ -3805,11 +3808,13 @@ static unsigned int hda_set_power_state(struct hda_codec *codec, hda_nid_t fg = codec->afg ? codec->afg : codec->mfg; int count; unsigned int state; + int flags = 0; /* this delay seems necessary to avoid click noise at power-down */ if (power_state == AC_PWRST_D3) { /* transition time less than 10ms for power down */ msleep(codec->epss ? 10 : 100); + flags = HDA_RW_NO_RESPONSE_FALLBACK; } /* repeat power states setting at most 10 times*/ @@ -3818,7 +3823,7 @@ static unsigned int hda_set_power_state(struct hda_codec *codec, codec->patch_ops.set_power_state(codec, fg, power_state); else { - snd_hda_codec_read(codec, fg, 0, + snd_hda_codec_read(codec, fg, flags, AC_VERB_SET_POWER_STATE, power_state); snd_hda_codec_set_power_to_all(codec, fg, power_state); diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 39a658e02988..701c2e069b10 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -679,6 +679,7 @@ struct hda_bus { unsigned int response_reset:1; /* controller was reset */ unsigned int in_reset:1; /* during reset operation */ unsigned int power_keep_link_on:1; /* don't power off HDA link */ + unsigned int no_response_fallback:1; /* don't fallback at RIRB error */ int primary_dig_out_type; /* primary digital out PCM type */ }; @@ -930,6 +931,8 @@ enum { HDA_INPUT, HDA_OUTPUT }; +/* snd_hda_codec_read/write optional flags */ +#define HDA_RW_NO_RESPONSE_FALLBACK (1 << 0) /* * constructors diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 49dfad4a099e..f089fa0aa03d 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -942,6 +942,9 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus, } } + if (!bus->no_response_fallback) + return -1; + if (!chip->polling_mode && chip->poll_count < 2) { snd_printdd(SFX "%s: azx_get_response timeout, " "polling the codec once: last cmd=0x%08x\n", -- cgit v1.2.3 From 6c49f24180c308a07be3f1d59ee7af33184ba17e Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 6 Jun 2013 12:45:25 +0200 Subject: drm/i915: hw state readout support for pixel_multiplier Incomplete since ilk+ support needs proper pch dpll tracking first. SDVO get_config parts based on a patch from Jesse Barnes, but fixed up to actually work. v2: Make sure that we call encoder->get_config _after_ we get_pipe_config to be consistent in both setup_hw_state and the modeset state checker. Otherwise the clever trick with handling the pixel mutliplier on i915G/GM where the encoder overrides the default value of 1 from the crtc get_pipe_config function doesn't work. Spotted by Imre Deak. v3: Actually cross-check the pixel mutliplier (but not on pch split platforms for now). Now actually also tested on a i915G with a sdvo encoder plugged in. Cc: Imre Deak Cc: Jesse Barnes Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 39 ++++++++++++++++++++++++++++++++++-- drivers/gpu/drm/i915/intel_sdvo.c | 30 ++++++++++++++++++++++++++- 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 0e1f82810f58..421b7e2ece3b 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5001,6 +5001,23 @@ static bool i9xx_get_pipe_config(struct intel_crtc *crtc, i9xx_get_pfit_config(crtc, pipe_config); + if (INTEL_INFO(dev)->gen >= 4) { + tmp = I915_READ(DPLL_MD(crtc->pipe)); + pipe_config->pixel_multiplier = + ((tmp & DPLL_MD_UDI_MULTIPLIER_MASK) + >> DPLL_MD_UDI_MULTIPLIER_SHIFT) + 1; + } else if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev)) { + tmp = I915_READ(DPLL(crtc->pipe)); + pipe_config->pixel_multiplier = + ((tmp & SDVO_MULTIPLIER_MASK) + >> SDVO_MULTIPLIER_SHIFT_HIRES) + 1; + } else { + /* Note that on i915G/GM the pixel multiplier is in the sdvo + * port and will be fixed up in the encoder->get_config + * function. */ + pipe_config->pixel_multiplier = 1; + } + return true; } @@ -5864,6 +5881,12 @@ static bool ironlake_get_pipe_config(struct intel_crtc *crtc, FDI_DP_PORT_WIDTH_SHIFT) + 1; ironlake_get_fdi_m_n_config(crtc, pipe_config); + + /* XXX: Can't properly read out the pch dpll pixel multiplier + * since we don't have state tracking for pch clocks yet. */ + pipe_config->pixel_multiplier = 1; + } else { + pipe_config->pixel_multiplier = 1; } intel_get_pipe_timings(crtc, pipe_config); @@ -5998,6 +6021,8 @@ static bool haswell_get_pipe_config(struct intel_crtc *crtc, pipe_config->ips_enabled = hsw_crtc_supports_ips(crtc) && (I915_READ(IPS_CTL) & IPS_ENABLE); + pipe_config->pixel_multiplier = 1; + return true; } @@ -8090,6 +8115,9 @@ intel_pipe_config_compare(struct drm_device *dev, PIPE_CONF_CHECK_I(adjusted_mode.crtc_vsync_start); PIPE_CONF_CHECK_I(adjusted_mode.crtc_vsync_end); + if (!HAS_PCH_SPLIT(dev)) + PIPE_CONF_CHECK_I(pixel_multiplier); + PIPE_CONF_CHECK_FLAGS(adjusted_mode.flags, DRM_MODE_FLAG_INTERLACE); @@ -8211,9 +8239,8 @@ intel_modeset_check_state(struct drm_device *dev) enabled = true; if (encoder->connectors_active) active = true; - if (encoder->get_config) - encoder->get_config(encoder, &pipe_config); } + WARN(active != crtc->active, "crtc's computed active state doesn't match tracked active state " "(expected %i, found %i)\n", active, crtc->active); @@ -8223,6 +8250,14 @@ intel_modeset_check_state(struct drm_device *dev) active = dev_priv->display.get_pipe_config(crtc, &pipe_config); + list_for_each_entry(encoder, &dev->mode_config.encoder_list, + base.head) { + if (encoder->base.crtc != &crtc->base) + continue; + if (encoder->get_config) + encoder->get_config(encoder, &pipe_config); + } + WARN(crtc->active != active, "crtc active state doesn't match with hw state " "(expected %i, found %i)\n", crtc->active, active); diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index f4588a2c65ac..5c816dd75bec 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1313,9 +1313,13 @@ static bool intel_sdvo_get_hw_state(struct intel_encoder *encoder, static void intel_sdvo_get_config(struct intel_encoder *encoder, struct intel_crtc_config *pipe_config) { + struct drm_device *dev = encoder->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; struct intel_sdvo *intel_sdvo = to_intel_sdvo(&encoder->base); struct intel_sdvo_dtd dtd; - u32 flags = 0; + int encoder_pixel_multiplier = 0; + u32 flags = 0, sdvox; + u8 val; bool ret; ret = intel_sdvo_get_input_timing(intel_sdvo, &dtd); @@ -1335,6 +1339,30 @@ static void intel_sdvo_get_config(struct intel_encoder *encoder, flags |= DRM_MODE_FLAG_NVSYNC; pipe_config->adjusted_mode.flags |= flags; + + if (IS_I915G(dev) || IS_I915GM(dev)) { + sdvox = I915_READ(intel_sdvo->sdvo_reg); + pipe_config->pixel_multiplier = + ((sdvox & SDVO_PORT_MULTIPLY_MASK) + >> SDVO_PORT_MULTIPLY_SHIFT) + 1; + } + + /* Cross check the port pixel multiplier with the sdvo encoder state. */ + intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_CLOCK_RATE_MULT, &val, 1); + switch (val) { + case SDVO_CLOCK_RATE_MULT_1X: + encoder_pixel_multiplier = 1; + break; + case SDVO_CLOCK_RATE_MULT_2X: + encoder_pixel_multiplier = 2; + break; + case SDVO_CLOCK_RATE_MULT_4X: + encoder_pixel_multiplier = 4; + break; + } + WARN(encoder_pixel_multiplier != pipe_config->pixel_multiplier, + "SDVO pixel multiplier mismatch, port: %i, encoder: %i\n", + pipe_config->pixel_multiplier, encoder_pixel_multiplier); } static void intel_disable_sdvo(struct intel_encoder *encoder) -- cgit v1.2.3 From f85da868e3b998394634209cc1e48e0f4126901b Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Tue, 4 Jun 2013 16:53:39 -0300 Subject: drm/i915: update FBC maximum fb sizes CTG/ILK/SNB/IVB support 4kx2k surfaces. HSW supports 4kx4k, but without proper front buffer invalidation on the last 2k lines, so don't enable FBC on these cases for now. v2: Use gen >= 5, not gen > 4 (Daniel). Signed-off-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_pm.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 50fe3d7303cb..1e1b48ec6c46 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -431,7 +431,7 @@ void intel_disable_fbc(struct drm_device *dev) * - no pixel mulitply/line duplication * - no alpha buffer discard * - no dual wide - * - framebuffer <= 2048 in width, 1536 in height + * - framebuffer <= max_hdisplay in width, max_vdisplay in height * * We can't assume that any compression will take place (worst case), * so the compressed buffer has to be the same size as the uncompressed @@ -449,6 +449,7 @@ void intel_update_fbc(struct drm_device *dev) struct intel_framebuffer *intel_fb; struct drm_i915_gem_object *obj; int enable_fbc; + unsigned int max_hdisplay, max_vdisplay; if (!i915_powersave) return; @@ -507,8 +508,16 @@ void intel_update_fbc(struct drm_device *dev) dev_priv->no_fbc_reason = FBC_UNSUPPORTED_MODE; goto out_disable; } - if ((crtc->mode.hdisplay > 2048) || - (crtc->mode.vdisplay > 1536)) { + + if (IS_G4X(dev) || INTEL_INFO(dev)->gen >= 5) { + max_hdisplay = 4096; + max_vdisplay = 2048; + } else { + max_hdisplay = 2048; + max_vdisplay = 1536; + } + if ((crtc->mode.hdisplay > max_hdisplay) || + (crtc->mode.vdisplay > max_vdisplay)) { DRM_DEBUG_KMS("mode too large for compression, disabling\n"); dev_priv->no_fbc_reason = FBC_MODE_TOO_LARGE; goto out_disable; -- cgit v1.2.3 From e75a52c6723a61a0d768ee53794e86b7edbe54f0 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Thu, 6 Jun 2013 19:38:45 +0800 Subject: ASoC: WM8962: Create default platform data structure Embed a copy of struct wm8962_pdata in stuct wm8962_priv so that there's no need to check validity of pdata any more. Signed-off-by: Nicolin Chen Signed-off-by: Mark Brown --- sound/soc/codecs/wm8962.c | 68 +++++++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index e9710280e5e1..d56dd867057d 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -51,6 +51,7 @@ static const char *wm8962_supply_names[WM8962_NUM_SUPPLIES] = { /* codec private data */ struct wm8962_priv { + struct wm8962_pdata pdata; struct regmap *regmap; struct snd_soc_codec *codec; @@ -2345,12 +2346,13 @@ static const struct snd_soc_dapm_route wm8962_spk_stereo_intercon[] = { static int wm8962_add_widgets(struct snd_soc_codec *codec) { - struct wm8962_pdata *pdata = dev_get_platdata(codec->dev); + struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); + struct wm8962_pdata *pdata = &wm8962->pdata; struct snd_soc_dapm_context *dapm = &codec->dapm; snd_soc_add_codec_controls(codec, wm8962_snd_controls, ARRAY_SIZE(wm8962_snd_controls)); - if (pdata && pdata->spk_mono) + if (pdata->spk_mono) snd_soc_add_codec_controls(codec, wm8962_spk_mono_controls, ARRAY_SIZE(wm8962_spk_mono_controls)); else @@ -2360,7 +2362,7 @@ static int wm8962_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_new_controls(dapm, wm8962_dapm_widgets, ARRAY_SIZE(wm8962_dapm_widgets)); - if (pdata && pdata->spk_mono) + if (pdata->spk_mono) snd_soc_dapm_new_controls(dapm, wm8962_dapm_spk_mono_widgets, ARRAY_SIZE(wm8962_dapm_spk_mono_widgets)); else @@ -2369,7 +2371,7 @@ static int wm8962_add_widgets(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(dapm, wm8962_intercon, ARRAY_SIZE(wm8962_intercon)); - if (pdata && pdata->spk_mono) + if (pdata->spk_mono) snd_soc_dapm_add_routes(dapm, wm8962_spk_mono_intercon, ARRAY_SIZE(wm8962_spk_mono_intercon)); else @@ -3333,14 +3335,14 @@ static struct gpio_chip wm8962_template_chip = { static void wm8962_init_gpio(struct snd_soc_codec *codec) { struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); - struct wm8962_pdata *pdata = dev_get_platdata(codec->dev); + struct wm8962_pdata *pdata = &wm8962->pdata; int ret; wm8962->gpio_chip = wm8962_template_chip; wm8962->gpio_chip.ngpio = WM8962_MAX_GPIO; wm8962->gpio_chip.dev = codec->dev; - if (pdata && pdata->gpio_base) + if (pdata->gpio_base) wm8962->gpio_chip.base = pdata->gpio_base; else wm8962->gpio_chip.base = -1; @@ -3373,7 +3375,7 @@ static int wm8962_probe(struct snd_soc_codec *codec) { int ret; struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); - struct wm8962_pdata *pdata = dev_get_platdata(codec->dev); + struct wm8962_pdata *pdata = &wm8962->pdata; u16 *reg_cache = codec->reg_cache; int i, trigger, irq_pol; bool dmicclk, dmicdat; @@ -3421,30 +3423,28 @@ static int wm8962_probe(struct snd_soc_codec *codec) WM8962_OSC_ENA | WM8962_PLL2_ENA | WM8962_PLL3_ENA, 0); - if (pdata) { - /* Apply static configuration for GPIOs */ - for (i = 0; i < ARRAY_SIZE(pdata->gpio_init); i++) - if (pdata->gpio_init[i]) { - wm8962_set_gpio_mode(codec, i + 1); - snd_soc_write(codec, 0x200 + i, - pdata->gpio_init[i] & 0xffff); - } + /* Apply static configuration for GPIOs */ + for (i = 0; i < ARRAY_SIZE(pdata->gpio_init); i++) + if (pdata->gpio_init[i]) { + wm8962_set_gpio_mode(codec, i + 1); + snd_soc_write(codec, 0x200 + i, + pdata->gpio_init[i] & 0xffff); + } - /* Put the speakers into mono mode? */ - if (pdata->spk_mono) - reg_cache[WM8962_CLASS_D_CONTROL_2] - |= WM8962_SPK_MONO; - - /* Micbias setup, detection enable and detection - * threasholds. */ - if (pdata->mic_cfg) - snd_soc_update_bits(codec, WM8962_ADDITIONAL_CONTROL_4, - WM8962_MICDET_ENA | - WM8962_MICDET_THR_MASK | - WM8962_MICSHORT_THR_MASK | - WM8962_MICBIAS_LVL, - pdata->mic_cfg); - } + /* Put the speakers into mono mode? */ + if (pdata->spk_mono) + reg_cache[WM8962_CLASS_D_CONTROL_2] + |= WM8962_SPK_MONO; + + /* Micbias setup, detection enable and detection + * threasholds. */ + if (pdata->mic_cfg) + snd_soc_update_bits(codec, WM8962_ADDITIONAL_CONTROL_4, + WM8962_MICDET_ENA | + WM8962_MICDET_THR_MASK | + WM8962_MICSHORT_THR_MASK | + WM8962_MICBIAS_LVL, + pdata->mic_cfg); /* Latch volume update bits */ snd_soc_update_bits(codec, WM8962_LEFT_INPUT_VOLUME, @@ -3506,7 +3506,7 @@ static int wm8962_probe(struct snd_soc_codec *codec) wm8962_init_gpio(codec); if (wm8962->irq) { - if (pdata && pdata->irq_active_low) { + if (pdata->irq_active_low) { trigger = IRQF_TRIGGER_LOW; irq_pol = WM8962_IRQ_POL; } else { @@ -3603,6 +3603,10 @@ static int wm8962_i2c_probe(struct i2c_client *i2c, init_completion(&wm8962->fll_lock); wm8962->irq = i2c->irq; + /* If platform data was supplied, update the default data in priv */ + if (pdata) + memcpy(&wm8962->pdata, pdata, sizeof(struct wm8962_pdata)); + for (i = 0; i < ARRAY_SIZE(wm8962->supplies); i++) wm8962->supplies[i].supply = wm8962_supply_names[i]; @@ -3666,7 +3670,7 @@ static int wm8962_i2c_probe(struct i2c_client *i2c, goto err_enable; } - if (pdata && pdata->in4_dc_measure) { + if (wm8962->pdata.in4_dc_measure) { ret = regmap_register_patch(wm8962->regmap, wm8962_dc_measure, ARRAY_SIZE(wm8962_dc_measure)); -- cgit v1.2.3 From 1a33bd2be705cbb3f57d7223b60baea441039307 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Wed, 29 May 2013 10:11:17 +0200 Subject: clocksource: dw_apb: Fix error check irq_of_parse_and_map() returns 0 on error, while the code checks for NO_IRQ. This breaks on platforms that have NO_IRQ != 0. Cc: Signed-off-by: Baruch Siach Signed-off-by: Daniel Lezcano --- drivers/clocksource/dw_apb_timer_of.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clocksource/dw_apb_timer_of.c b/drivers/clocksource/dw_apb_timer_of.c index ab09ed3742ee..6b02eddc5f50 100644 --- a/drivers/clocksource/dw_apb_timer_of.c +++ b/drivers/clocksource/dw_apb_timer_of.c @@ -44,7 +44,7 @@ static void add_clockevent(struct device_node *event_timer) u32 irq, rate; irq = irq_of_parse_and_map(event_timer, 0); - if (irq == NO_IRQ) + if (irq == 0) panic("No IRQ for clock event timer"); timer_get_base_and_rate(event_timer, &iobase, &rate); -- cgit v1.2.3 From c19672492d233e0012b60fbfa460ffac1381ee26 Mon Sep 17 00:00:00 2001 From: Jingchang Lu Date: Wed, 29 May 2013 10:12:17 +0200 Subject: clocksource: Add Freescale Vybrid pit timer support Add Freescale Vybrid Family period interrupt timer support. Signed-off-by: Jingchang Lu Reviewed-by: Daniel Lezcano Acked-by: Shawn Guo Signed-off-by: Daniel Lezcano --- drivers/clocksource/Kconfig | 5 + drivers/clocksource/Makefile | 1 + drivers/clocksource/vf_pit_timer.c | 194 +++++++++++++++++++++++++++++++++++++ 3 files changed, 200 insertions(+) create mode 100644 drivers/clocksource/vf_pit_timer.c diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index f151c6cf27c3..0a04257edf65 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -85,3 +85,8 @@ config CLKSRC_SAMSUNG_PWM Samsung S3C, S5P and Exynos SoCs, replacing an earlier driver for all devicetree enabled platforms. This driver will be needed only on systems that do not have the Exynos MCT available. + +config VF_PIT_TIMER + bool + help + Support for Period Interrupt Timer on Freescale Vybrid Family SoCs. diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 8d979c72aa94..36a9ac105d55 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_ARCH_BCM) += bcm_kona_timer.o obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence_ttc_timer.o obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o obj-$(CONFIG_CLKSRC_SAMSUNG_PWM) += samsung_pwm_timer.o +obj-$(CONFIG_VF_PIT_TIMER) += vf_pit_timer.o obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o diff --git a/drivers/clocksource/vf_pit_timer.c b/drivers/clocksource/vf_pit_timer.c new file mode 100644 index 000000000000..598399d57fc5 --- /dev/null +++ b/drivers/clocksource/vf_pit_timer.c @@ -0,0 +1,194 @@ +/* + * Copyright 2012-2013 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +/* + * Each pit takes 0x10 Bytes register space + */ +#define PITMCR 0x00 +#define PIT0_OFFSET 0x100 +#define PITn_OFFSET(n) (PIT0_OFFSET + 0x10 * (n)) +#define PITLDVAL 0x00 +#define PITCVAL 0x04 +#define PITTCTRL 0x08 +#define PITTFLG 0x0c + +#define PITMCR_MDIS (0x1 << 1) + +#define PITTCTRL_TEN (0x1 << 0) +#define PITTCTRL_TIE (0x1 << 1) +#define PITCTRL_CHN (0x1 << 2) + +#define PITTFLG_TIF 0x1 + +static void __iomem *clksrc_base; +static void __iomem *clkevt_base; +static unsigned long cycle_per_jiffy; + +static inline void pit_timer_enable(void) +{ + __raw_writel(PITTCTRL_TEN | PITTCTRL_TIE, clkevt_base + PITTCTRL); +} + +static inline void pit_timer_disable(void) +{ + __raw_writel(0, clkevt_base + PITTCTRL); +} + +static inline void pit_irq_acknowledge(void) +{ + __raw_writel(PITTFLG_TIF, clkevt_base + PITTFLG); +} + +static unsigned int pit_read_sched_clock(void) +{ + return __raw_readl(clksrc_base + PITCVAL); +} + +static int __init pit_clocksource_init(unsigned long rate) +{ + /* set the max load value and start the clock source counter */ + __raw_writel(0, clksrc_base + PITTCTRL); + __raw_writel(~0UL, clksrc_base + PITLDVAL); + __raw_writel(PITTCTRL_TEN, clksrc_base + PITTCTRL); + + setup_sched_clock(pit_read_sched_clock, 32, rate); + return clocksource_mmio_init(clksrc_base + PITCVAL, "vf-pit", rate, + 300, 32, clocksource_mmio_readl_down); +} + +static int pit_set_next_event(unsigned long delta, + struct clock_event_device *unused) +{ + /* + * set a new value to PITLDVAL register will not restart the timer, + * to abort the current cycle and start a timer period with the new + * value, the timer must be disabled and enabled again. + * and the PITLAVAL should be set to delta minus one according to pit + * hardware requirement. + */ + pit_timer_disable(); + __raw_writel(delta - 1, clkevt_base + PITLDVAL); + pit_timer_enable(); + + return 0; +} + +static void pit_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + pit_set_next_event(cycle_per_jiffy, evt); + break; + default: + break; + } +} + +static irqreturn_t pit_timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = dev_id; + + pit_irq_acknowledge(); + + /* + * pit hardware doesn't support oneshot, it will generate an interrupt + * and reload the counter value from PITLDVAL when PITCVAL reach zero, + * and start the counter again. So software need to disable the timer + * to stop the counter loop in ONESHOT mode. + */ + if (likely(evt->mode == CLOCK_EVT_MODE_ONESHOT)) + pit_timer_disable(); + + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static struct clock_event_device clockevent_pit = { + .name = "VF pit timer", + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .set_mode = pit_set_mode, + .set_next_event = pit_set_next_event, + .rating = 300, +}; + +static struct irqaction pit_timer_irq = { + .name = "VF pit timer", + .flags = IRQF_TIMER | IRQF_IRQPOLL, + .handler = pit_timer_interrupt, + .dev_id = &clockevent_pit, +}; + +static int __init pit_clockevent_init(unsigned long rate, int irq) +{ + __raw_writel(0, clkevt_base + PITTCTRL); + __raw_writel(PITTFLG_TIF, clkevt_base + PITTFLG); + + BUG_ON(setup_irq(irq, &pit_timer_irq)); + + clockevent_pit.cpumask = cpumask_of(0); + clockevent_pit.irq = irq; + /* + * The value for the LDVAL register trigger is calculated as: + * LDVAL trigger = (period / clock period) - 1 + * The pit is a 32-bit down count timer, when the conter value + * reaches 0, it will generate an interrupt, thus the minimal + * LDVAL trigger value is 1. And then the min_delta is + * minimal LDVAL trigger value + 1, and the max_delta is full 32-bit. + */ + clockevents_config_and_register(&clockevent_pit, rate, 2, 0xffffffff); + + return 0; +} + +static void __init pit_timer_init(struct device_node *np) +{ + struct clk *pit_clk; + void __iomem *timer_base; + unsigned long clk_rate; + int irq; + + timer_base = of_iomap(np, 0); + BUG_ON(!timer_base); + + /* + * PIT0 and PIT1 can be chained to build a 64-bit timer, + * so choose PIT2 as clocksource, PIT3 as clockevent device, + * and leave PIT0 and PIT1 unused for anyone else who needs them. + */ + clksrc_base = timer_base + PITn_OFFSET(2); + clkevt_base = timer_base + PITn_OFFSET(3); + + irq = irq_of_parse_and_map(np, 0); + BUG_ON(irq <= 0); + + pit_clk = of_clk_get(np, 0); + BUG_ON(IS_ERR(pit_clk)); + + BUG_ON(clk_prepare_enable(pit_clk)); + + clk_rate = clk_get_rate(pit_clk); + cycle_per_jiffy = clk_rate / (HZ); + + /* enable the pit module */ + __raw_writel(~PITMCR_MDIS, timer_base + PITMCR); + + BUG_ON(pit_clocksource_init(clk_rate)); + + pit_clockevent_init(clk_rate, irq); +} +CLOCKSOURCE_OF_DECLARE(vf610, "fsl,vf610-pit", pit_timer_init); -- cgit v1.2.3 From 77ba83bb1bb1cdabd522d32536f8eee65a870145 Mon Sep 17 00:00:00 2001 From: Daniel Tang Date: Sat, 1 Jun 2013 16:02:37 +1000 Subject: clocksource: Add TI-Nspire timer support This patch adds a clocksource/clockevent driver for the timer found on some models in the TI-Nspire calculator series. The timer has two 16bit subtimers within its memory mapped I/O interface but only the first can generate interrupts. The first subtimer is used to generate clockevents but only if an interrupt number and register is given. The interrupt acknowledgement mechanism is a little strange because the interrupt mask and acknowledge registers are located in another memory mapped I/O peripheral. The address of this register is passed to the driver through device tree bindings. The second subtimer is used as a clocksource because it isn't capable of generating an interrupt. This subtimer is always added. Reviewed-by: Linus Walleij Signed-off-by: Daniel Tang Signed-off-by: Daniel Lezcano --- .../devicetree/bindings/timer/lsi,zevio-timer.txt | 33 ++++ drivers/clocksource/Makefile | 1 + drivers/clocksource/zevio-timer.c | 215 +++++++++++++++++++++ 3 files changed, 249 insertions(+) create mode 100644 Documentation/devicetree/bindings/timer/lsi,zevio-timer.txt create mode 100644 drivers/clocksource/zevio-timer.c diff --git a/Documentation/devicetree/bindings/timer/lsi,zevio-timer.txt b/Documentation/devicetree/bindings/timer/lsi,zevio-timer.txt new file mode 100644 index 000000000000..b2d07ad90e9a --- /dev/null +++ b/Documentation/devicetree/bindings/timer/lsi,zevio-timer.txt @@ -0,0 +1,33 @@ +TI-NSPIRE timer + +Required properties: + +- compatible : should be "lsi,zevio-timer". +- reg : The physical base address and size of the timer (always first). +- clocks: phandle to the source clock. + +Optional properties: + +- interrupts : The interrupt number of the first timer. +- reg : The interrupt acknowledgement registers + (always after timer base address) + +If any of the optional properties are not given, the timer is added as a +clock-source only. + +Example: + +timer { + compatible = "lsi,zevio-timer"; + reg = <0x900D0000 0x1000>, <0x900A0020 0x8>; + interrupts = <19>; + clocks = <&timer_clk>; +}; + +Example (no clock-events): + +timer { + compatible = "lsi,zevio-timer"; + reg = <0x900D0000 0x1000>; + clocks = <&timer_clk>; +}; diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 36a9ac105d55..4853ea0f8fd5 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_ARCH_PRIMA2) += timer-prima2.o obj-$(CONFIG_SUN4I_TIMER) += sun4i_timer.o obj-$(CONFIG_ARCH_TEGRA) += tegra20_timer.o obj-$(CONFIG_VT8500_TIMER) += vt8500_timer.o +obj-$(CONFIG_ARCH_NSPIRE) += zevio-timer.o obj-$(CONFIG_ARCH_BCM) += bcm_kona_timer.o obj-$(CONFIG_CADENCE_TTC_TIMER) += cadence_ttc_timer.o obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o diff --git a/drivers/clocksource/zevio-timer.c b/drivers/clocksource/zevio-timer.c new file mode 100644 index 000000000000..ca81809d159d --- /dev/null +++ b/drivers/clocksource/zevio-timer.c @@ -0,0 +1,215 @@ +/* + * linux/drivers/clocksource/zevio-timer.c + * + * Copyright (C) 2013 Daniel Tang + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IO_CURRENT_VAL 0x00 +#define IO_DIVIDER 0x04 +#define IO_CONTROL 0x08 + +#define IO_TIMER1 0x00 +#define IO_TIMER2 0x0C + +#define IO_MATCH_BEGIN 0x18 +#define IO_MATCH(x) (IO_MATCH_BEGIN + ((x) << 2)) + +#define IO_INTR_STS 0x00 +#define IO_INTR_ACK 0x00 +#define IO_INTR_MSK 0x04 + +#define CNTL_STOP_TIMER (1 << 4) +#define CNTL_RUN_TIMER (0 << 4) + +#define CNTL_INC (1 << 3) +#define CNTL_DEC (0 << 3) + +#define CNTL_TOZERO 0 +#define CNTL_MATCH(x) ((x) + 1) +#define CNTL_FOREVER 7 + +/* There are 6 match registers but we only use one. */ +#define TIMER_MATCH 0 + +#define TIMER_INTR_MSK (1 << (TIMER_MATCH)) +#define TIMER_INTR_ALL 0x3F + +struct zevio_timer { + void __iomem *base; + void __iomem *timer1, *timer2; + void __iomem *interrupt_regs; + + struct clk *clk; + struct clock_event_device clkevt; + struct irqaction clkevt_irq; + + char clocksource_name[64]; + char clockevent_name[64]; +}; + +static int zevio_timer_set_event(unsigned long delta, + struct clock_event_device *dev) +{ + struct zevio_timer *timer = container_of(dev, struct zevio_timer, + clkevt); + + writel(delta, timer->timer1 + IO_CURRENT_VAL); + writel(CNTL_RUN_TIMER | CNTL_DEC | CNTL_MATCH(TIMER_MATCH), + timer->timer1 + IO_CONTROL); + + return 0; +} + +static void zevio_timer_set_mode(enum clock_event_mode mode, + struct clock_event_device *dev) +{ + struct zevio_timer *timer = container_of(dev, struct zevio_timer, + clkevt); + + switch (mode) { + case CLOCK_EVT_MODE_RESUME: + case CLOCK_EVT_MODE_ONESHOT: + /* Enable timer interrupts */ + writel(TIMER_INTR_MSK, timer->interrupt_regs + IO_INTR_MSK); + writel(TIMER_INTR_ALL, timer->interrupt_regs + IO_INTR_ACK); + break; + case CLOCK_EVT_MODE_SHUTDOWN: + case CLOCK_EVT_MODE_UNUSED: + /* Disable timer interrupts */ + writel(0, timer->interrupt_regs + IO_INTR_MSK); + writel(TIMER_INTR_ALL, timer->interrupt_regs + IO_INTR_ACK); + /* Stop timer */ + writel(CNTL_STOP_TIMER, timer->timer1 + IO_CONTROL); + break; + case CLOCK_EVT_MODE_PERIODIC: + default: + /* Unsupported */ + break; + } +} + +static irqreturn_t zevio_timer_interrupt(int irq, void *dev_id) +{ + struct zevio_timer *timer = dev_id; + u32 intr; + + intr = readl(timer->interrupt_regs + IO_INTR_ACK); + if (!(intr & TIMER_INTR_MSK)) + return IRQ_NONE; + + writel(TIMER_INTR_MSK, timer->interrupt_regs + IO_INTR_ACK); + writel(CNTL_STOP_TIMER, timer->timer1 + IO_CONTROL); + + if (timer->clkevt.event_handler) + timer->clkevt.event_handler(&timer->clkevt); + + return IRQ_HANDLED; +} + +static int __init zevio_timer_add(struct device_node *node) +{ + struct zevio_timer *timer; + struct resource res; + int irqnr, ret; + + timer = kzalloc(sizeof(*timer), GFP_KERNEL); + if (!timer) + return -ENOMEM; + + timer->base = of_iomap(node, 0); + if (!timer->base) { + ret = -EINVAL; + goto error_free; + } + timer->timer1 = timer->base + IO_TIMER1; + timer->timer2 = timer->base + IO_TIMER2; + + timer->clk = of_clk_get(node, 0); + if (IS_ERR(timer->clk)) { + ret = PTR_ERR(timer->clk); + pr_err("Timer clock not found! (error %d)\n", ret); + goto error_unmap; + } + + timer->interrupt_regs = of_iomap(node, 1); + irqnr = irq_of_parse_and_map(node, 0); + + of_address_to_resource(node, 0, &res); + scnprintf(timer->clocksource_name, sizeof(timer->clocksource_name), + "%llx.%s_clocksource", + (unsigned long long)res.start, node->name); + + scnprintf(timer->clockevent_name, sizeof(timer->clockevent_name), + "%llx.%s_clockevent", + (unsigned long long)res.start, node->name); + + if (timer->interrupt_regs && irqnr) { + timer->clkevt.name = timer->clockevent_name; + timer->clkevt.set_next_event = zevio_timer_set_event; + timer->clkevt.set_mode = zevio_timer_set_mode; + timer->clkevt.rating = 200; + timer->clkevt.cpumask = cpu_all_mask; + timer->clkevt.features = CLOCK_EVT_FEAT_ONESHOT; + timer->clkevt.irq = irqnr; + + writel(CNTL_STOP_TIMER, timer->timer1 + IO_CONTROL); + writel(0, timer->timer1 + IO_DIVIDER); + + /* Start with timer interrupts disabled */ + writel(0, timer->interrupt_regs + IO_INTR_MSK); + writel(TIMER_INTR_ALL, timer->interrupt_regs + IO_INTR_ACK); + + /* Interrupt to occur when timer value matches 0 */ + writel(0, timer->base + IO_MATCH(TIMER_MATCH)); + + timer->clkevt_irq.name = timer->clockevent_name; + timer->clkevt_irq.handler = zevio_timer_interrupt; + timer->clkevt_irq.dev_id = timer; + timer->clkevt_irq.flags = IRQF_TIMER | IRQF_IRQPOLL; + + setup_irq(irqnr, &timer->clkevt_irq); + + clockevents_config_and_register(&timer->clkevt, + clk_get_rate(timer->clk), 0x0001, 0xffff); + pr_info("Added %s as clockevent\n", timer->clockevent_name); + } + + writel(CNTL_STOP_TIMER, timer->timer2 + IO_CONTROL); + writel(0, timer->timer2 + IO_CURRENT_VAL); + writel(0, timer->timer2 + IO_DIVIDER); + writel(CNTL_RUN_TIMER | CNTL_FOREVER | CNTL_INC, + timer->timer2 + IO_CONTROL); + + clocksource_mmio_init(timer->timer2 + IO_CURRENT_VAL, + timer->clocksource_name, + clk_get_rate(timer->clk), + 200, 16, + clocksource_mmio_readw_up); + + pr_info("Added %s as clocksource\n", timer->clocksource_name); + + return 0; +error_unmap: + iounmap(timer->base); +error_free: + kfree(timer); + return ret; +} + +CLOCKSOURCE_OF_DECLARE(zevio_timer, "lsi,zevio-timer", zevio_timer_add); -- cgit v1.2.3 From c67e2228b799ea586f4f6ba7006ce61431f8dde6 Mon Sep 17 00:00:00 2001 From: Wang Xingchao Date: Thu, 30 May 2013 22:07:08 +0800 Subject: ALSA: hda - Fix runtime PM check The device can support runtime PM no matter whether it support signal wakeup or not. For some chips like Haswell which doesnot support PME by default, this patch let haswell Display HD-A controller enter runtime suspend, and bring more power saving whith power-well feature enabled. Signed-off-by: Wang Xingchao Reviewed-by: Takashi Iwai Reviewed-by: Rafael J. Wysocki Signed-off-by: Daniel Vetter --- sound/pci/hda/hda_intel.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index de18722c4873..aa4d4d716704 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -3120,8 +3120,13 @@ static int register_vga_switcheroo(struct azx *chip) */ static int azx_free(struct azx *chip) { + struct pci_dev *pci = chip->pci; int i; + if ((chip->driver_caps & AZX_DCAPS_PM_RUNTIME) + && chip->running) + pm_runtime_get_noresume(&pci->dev); + azx_del_card_list(chip); azx_notifier_unregister(chip); @@ -3755,9 +3760,6 @@ static int azx_probe(struct pci_dev *pci, goto out_free; } - if (pci_dev_run_wake(pci)) - pm_runtime_put_noidle(&pci->dev); - dev++; complete_all(&chip->probe_wait); return 0; @@ -3770,6 +3772,7 @@ out_free: static int azx_probe_continue(struct azx *chip) { + struct pci_dev *pci = chip->pci; int dev = chip->dev_index; int err; @@ -3817,6 +3820,8 @@ static int azx_probe_continue(struct azx *chip) power_down_all_codecs(chip); azx_notifier_register(chip); azx_add_card_list(chip); + if (chip->driver_caps & AZX_DCAPS_PM_RUNTIME) + pm_runtime_put_noidle(&pci->dev); return 0; @@ -3829,9 +3834,6 @@ static void azx_remove(struct pci_dev *pci) { struct snd_card *card = pci_get_drvdata(pci); - if (pci_dev_run_wake(pci)) - pm_runtime_get_noresume(&pci->dev); - if (card) snd_card_free(card); pci_set_drvdata(pci, NULL); -- cgit v1.2.3 From 5c90680e42b08a1e4a6800ca02e75ad201f8037f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 30 May 2013 22:07:09 +0800 Subject: ALSA: hda - Move azx_first_init() into azx_probe_continue() This is a preliminary work for the upcoming Haswell HDMI audio fixes. azx_first_init() function can be safely called after the f/w loader, since the f/w loader doesn't require the sound hardware initialization beforehand. Moving it into azx_probe_continue() cleans up the code flow a bit. Signed-off-by: Takashi Iwai Signed-off-by: Daniel Vetter --- sound/pci/hda/hda_intel.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index aa4d4d716704..3e3126ba8276 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -3006,7 +3006,6 @@ static void azx_notifier_unregister(struct azx *chip) unregister_reboot_notifier(&chip->reboot_notifier); } -static int azx_first_init(struct azx *chip); static int azx_probe_continue(struct azx *chip); #ifdef SUPPORT_VGA_SWITCHEROO @@ -3033,8 +3032,7 @@ static void azx_vs_set_state(struct pci_dev *pci, snd_printk(KERN_INFO SFX "%s: Start delayed initialization\n", pci_name(chip->pci)); - if (azx_first_init(chip) < 0 || - azx_probe_continue(chip) < 0) { + if (azx_probe_continue(chip) < 0) { snd_printk(KERN_ERR SFX "%s: initialization error\n", pci_name(chip->pci)); @@ -3735,11 +3733,6 @@ static int azx_probe(struct pci_dev *pci, } probe_now = !chip->disabled; - if (probe_now) { - err = azx_first_init(chip); - if (err < 0) - goto out_free; - } #ifdef CONFIG_SND_HDA_PATCH_LOADER if (patch[dev] && *patch[dev]) { @@ -3776,6 +3769,10 @@ static int azx_probe_continue(struct azx *chip) int dev = chip->dev_index; int err; + err = azx_first_init(chip); + if (err < 0) + goto out_free; + #ifdef CONFIG_SND_HDA_INPUT_BEEP chip->beep_mode = beep_mode[dev]; #endif -- cgit v1.2.3 From 99a2008d0b32d72dfc2a54e7be1eb698dd2e3bd6 Mon Sep 17 00:00:00 2001 From: Wang Xingchao Date: Thu, 30 May 2013 22:07:10 +0800 Subject: ALSA: hda - Add power-welll support for haswell HDA For Intel Haswell chip, HDA controller and codec have power well dependency from GPU side. This patch added support to request/release power well in audio driver. Power save feature should be enabled to get runtime power saving. There's deadlock when request_module(i915) in azx_probe. It looks like: device_lock(audio pci device) -> azx_probe -> module_request (or symbol_request) -> modprobe (userspace) -> i915 init -> drm_pci_init -> pci_register_driver -> bus_add_driver -> driver_attach -> which in turn tries all locks on pci bus, and when it tries the one on the audio device, it will deadlock. This patch introduce a work to store remaining probe stuff, and let request_module run in safe work context. Signed-off-by: Wang Xingchao Reviewed-by: Takashi Iwai Reviewed-by: Liam Girdwood Reviewed-by: David Henningsson Signed-off-by: Daniel Vetter --- sound/pci/hda/Kconfig | 10 +++++++ sound/pci/hda/Makefile | 2 ++ sound/pci/hda/hda_i915.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++ sound/pci/hda/hda_i915.h | 35 ++++++++++++++++++++++ sound/pci/hda/hda_intel.c | 60 +++++++++++++++++++++++++++++++++++-- 5 files changed, 179 insertions(+), 3 deletions(-) create mode 100644 sound/pci/hda/hda_i915.c create mode 100644 sound/pci/hda/hda_i915.h diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 80a7d44bcf81..c5a872ca7703 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -152,6 +152,16 @@ config SND_HDA_CODEC_HDMI snd-hda-codec-hdmi. This module is automatically loaded at probing. +config SND_HDA_I915 + bool "Build Display HD-audio controller/codec power well support for i915 cards" + depends on DRM_I915 + help + Say Y here to include full HDMI and DisplayPort HD-audio controller/codec + power-well support for Intel Haswell graphics cards based on the i915 driver. + + Note that this option must be enabled for Intel Haswell C+ stepping machines, otherwise + the GPU audio controller/codecs will not be initialized or damaged when exit from S3 mode. + config SND_HDA_CODEC_CIRRUS bool "Build Cirrus Logic codec support" default y diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index 24a251497a1f..c091438286a3 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -1,4 +1,6 @@ snd-hda-intel-objs := hda_intel.o +# for haswell power well +snd-hda-intel-$(CONFIG_SND_HDA_I915) += hda_i915.o snd-hda-codec-y := hda_codec.o hda_jack.o hda_auto_parser.o snd-hda-codec-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o diff --git a/sound/pci/hda/hda_i915.c b/sound/pci/hda/hda_i915.c new file mode 100644 index 000000000000..76c13d5b3ca0 --- /dev/null +++ b/sound/pci/hda/hda_i915.c @@ -0,0 +1,75 @@ +/* + * hda_i915.c - routines for Haswell HDA controller power well support + * + * 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 +#include +#include +#include +#include "hda_i915.h" + +static void (*get_power)(void); +static void (*put_power)(void); + +void hda_display_power(bool enable) +{ + if (!get_power || !put_power) + return; + + snd_printdd("HDA display power %s \n", + enable ? "Enable" : "Disable"); + if (enable) + get_power(); + else + put_power(); +} + +int hda_i915_init(void) +{ + int err = 0; + + get_power = symbol_request(i915_request_power_well); + if (!get_power) { + snd_printk(KERN_WARNING "hda-i915: get_power symbol get fail\n"); + return -ENODEV; + } + + put_power = symbol_request(i915_release_power_well); + if (!put_power) { + symbol_put(i915_request_power_well); + get_power = NULL; + return -ENODEV; + } + + snd_printd("HDA driver get symbol successfully from i915 module\n"); + + return err; +} + +int hda_i915_exit(void) +{ + if (get_power) { + symbol_put(i915_request_power_well); + get_power = NULL; + } + if (put_power) { + symbol_put(i915_release_power_well); + put_power = NULL; + } + + return 0; +} diff --git a/sound/pci/hda/hda_i915.h b/sound/pci/hda/hda_i915.h new file mode 100644 index 000000000000..5a63da2c53e5 --- /dev/null +++ b/sound/pci/hda/hda_i915.h @@ -0,0 +1,35 @@ +/* + * 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 __SOUND_HDA_I915_H +#define __SOUND_HDA_I915_H + +#ifdef CONFIG_SND_HDA_I915 +void hda_display_power(bool enable); +int hda_i915_init(void); +int hda_i915_exit(void); +#else +static inline void hda_display_power(bool enable) {} +static inline int hda_i915_init(void) +{ + return -ENODEV; +} +static inline int hda_i915_exit(void) +{ + return 0; +} +#endif + +#endif diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 3e3126ba8276..35e9f8b010a7 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -62,6 +62,7 @@ #include #include #include "hda_codec.h" +#include "hda_i915.h" static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; @@ -541,6 +542,10 @@ struct azx { /* for pending irqs */ struct work_struct irq_pending_work; +#ifdef CONFIG_SND_HDA_I915 + struct work_struct probe_work; +#endif + /* reboot notifier (for mysterious hangup problem at power-down) */ struct notifier_block reboot_notifier; @@ -594,6 +599,7 @@ enum { #define AZX_DCAPS_4K_BDLE_BOUNDARY (1 << 23) /* BDLE in 4k boundary */ #define AZX_DCAPS_COUNT_LPIB_DELAY (1 << 25) /* Take LPIB as delay */ #define AZX_DCAPS_PM_RUNTIME (1 << 26) /* runtime PM support */ +#define AZX_DCAPS_I915_POWERWELL (1 << 27) /* HSW i915 power well support */ /* quirks for Intel PCH */ #define AZX_DCAPS_INTEL_PCH_NOPM \ @@ -2900,6 +2906,8 @@ static int azx_suspend(struct device *dev) pci_disable_device(pci); pci_save_state(pci); pci_set_power_state(pci, PCI_D3hot); + if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) + hda_display_power(false); return 0; } @@ -2912,6 +2920,8 @@ static int azx_resume(struct device *dev) if (chip->disabled) return 0; + if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) + hda_display_power(true); pci_set_power_state(pci, PCI_D0); pci_restore_state(pci); if (pci_enable_device(pci) < 0) { @@ -2944,6 +2954,8 @@ static int azx_runtime_suspend(struct device *dev) azx_stop_chip(chip); azx_clear_irq_pending(chip); + if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) + hda_display_power(false); return 0; } @@ -2952,6 +2964,8 @@ static int azx_runtime_resume(struct device *dev) struct snd_card *card = dev_get_drvdata(dev); struct azx *chip = card->private_data; + if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) + hda_display_power(true); azx_init_pci(chip); azx_init_chip(chip, 1); return 0; @@ -3176,6 +3190,10 @@ static int azx_free(struct azx *chip) if (chip->fw) release_firmware(chip->fw); #endif + if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) { + hda_display_power(false); + hda_i915_exit(); + } kfree(chip); return 0; @@ -3401,6 +3419,13 @@ static void azx_check_snoop_available(struct azx *chip) } } +#ifdef CONFIG_SND_HDA_I915 +static void azx_probe_work(struct work_struct *work) +{ + azx_probe_continue(container_of(work, struct azx, probe_work)); +} +#endif + /* * constructor */ @@ -3476,7 +3501,13 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci, return err; } +#ifdef CONFIG_SND_HDA_I915 + /* continue probing in work context as may trigger request module */ + INIT_WORK(&chip->probe_work, azx_probe_work); +#endif + *rchip = chip; + return 0; } @@ -3747,6 +3778,16 @@ static int azx_probe(struct pci_dev *pci, } #endif /* CONFIG_SND_HDA_PATCH_LOADER */ + /* continue probing in work context, avoid request_module deadlock */ + if (probe_now && (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)) { +#ifdef CONFIG_SND_HDA_I915 + probe_now = false; + schedule_work(&chip->probe_work); +#else + snd_printk(KERN_ERR SFX "Haswell must build in CONFIG_SND_HDA_I915\n"); +#endif + } + if (probe_now) { err = azx_probe_continue(chip); if (err < 0) @@ -3769,6 +3810,16 @@ static int azx_probe_continue(struct azx *chip) int dev = chip->dev_index; int err; + /* Request power well for Haswell HDA controller and codec */ + if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) { + err = hda_i915_init(); + if (err < 0) { + snd_printk(KERN_ERR SFX "Error request power-well from i915\n"); + goto out_free; + } + hda_display_power(true); + } + err = azx_first_init(chip); if (err < 0) goto out_free; @@ -3863,11 +3914,14 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = { .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, /* Haswell */ { PCI_DEVICE(0x8086, 0x0a0c), - .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH }, + .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH | + AZX_DCAPS_I915_POWERWELL }, { PCI_DEVICE(0x8086, 0x0c0c), - .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH }, + .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH | + AZX_DCAPS_I915_POWERWELL }, { PCI_DEVICE(0x8086, 0x0d0c), - .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH }, + .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH | + AZX_DCAPS_I915_POWERWELL }, /* 5 Series/3400 */ { PCI_DEVICE(0x8086, 0x3b56), .driver_data = AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM }, -- cgit v1.2.3 From a38911a3fede294e2adfd2deea8104dfbbd760c5 Mon Sep 17 00:00:00 2001 From: Wang Xingchao Date: Thu, 30 May 2013 22:07:11 +0800 Subject: i915/drm: Add private api for power well usage Haswell Display audio depends on power well in graphic side, it should request power well before use it and release power well after use. I915 will not shutdown power well if it detects audio is using. This patch protects display audio crash for Intel Haswell C3 stepping board. Signed-off-by: Wang Xingchao Reviewed-by: Takashi Iwai Reviewed-by: Damien Lespiau Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 6 +++ drivers/gpu/drm/i915/i915_drv.h | 12 ++++++ drivers/gpu/drm/i915/intel_drv.h | 4 ++ drivers/gpu/drm/i915/intel_pm.c | 81 ++++++++++++++++++++++++++++++++++++---- include/drm/i915_powerwell.h | 36 ++++++++++++++++++ 5 files changed, 132 insertions(+), 7 deletions(-) create mode 100644 include/drm/i915_powerwell.h diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 22534c3b4fe5..fd8898c8f8e3 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1656,6 +1656,9 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) /* Start out suspended */ dev_priv->mm.suspended = 1; + if (HAS_POWER_WELL(dev)) + i915_init_power_well(dev); + if (drm_core_check_feature(dev, DRIVER_MODESET)) { ret = i915_load_modeset_init(dev); if (ret < 0) { @@ -1712,6 +1715,9 @@ int i915_driver_unload(struct drm_device *dev) intel_gpu_ips_teardown(); + if (HAS_POWER_WELL(dev)) + i915_remove_power_well(dev); + i915_teardown_sysfs(dev); if (dev_priv->mm.inactive_shrinker.shrink) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 215aa63e3f47..87f7f88b1030 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -740,6 +740,15 @@ struct intel_ilk_power_mgmt { struct drm_i915_gem_object *renderctx; }; +/* Power well structure for haswell */ +struct i915_power_well { + struct drm_device *device; + spinlock_t lock; + /* power well enable/disable usage count */ + int count; + int i915_request; +}; + struct i915_dri1_state { unsigned allow_batchbuffer : 1; u32 __iomem *gfx_hws_cpu_addr; @@ -1099,6 +1108,9 @@ typedef struct drm_i915_private { * mchdev_lock in intel_pm.c */ struct intel_ilk_power_mgmt ips; + /* Haswell power well */ + struct i915_power_well power_well; + enum no_fbc_reason no_fbc_reason; struct drm_mm_node *compressed_fb; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 6bbebf824078..1735cdc86770 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -768,6 +768,10 @@ extern void intel_update_fbc(struct drm_device *dev); extern void intel_gpu_ips_init(struct drm_i915_private *dev_priv); extern void intel_gpu_ips_teardown(void); +/* Power well */ +extern int i915_init_power_well(struct drm_device *dev); +extern void i915_remove_power_well(struct drm_device *dev); + extern bool intel_display_power_enabled(struct drm_device *dev, enum intel_display_power_domain domain); extern void intel_init_power_well(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 1e1b48ec6c46..a417d7b196c2 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -5024,18 +5024,12 @@ bool intel_display_power_enabled(struct drm_device *dev, } } -void intel_set_power_well(struct drm_device *dev, bool enable) +static void __intel_set_power_well(struct drm_device *dev, bool enable) { struct drm_i915_private *dev_priv = dev->dev_private; bool is_enabled, enable_requested; uint32_t tmp; - if (!HAS_POWER_WELL(dev)) - return; - - if (!i915_disable_power_well && !enable) - return; - tmp = I915_READ(HSW_PWR_WELL_DRIVER); is_enabled = tmp & HSW_PWR_WELL_STATE; enable_requested = tmp & HSW_PWR_WELL_ENABLE; @@ -5058,6 +5052,79 @@ void intel_set_power_well(struct drm_device *dev, bool enable) } } +static struct i915_power_well *hsw_pwr; + +/* Display audio driver power well request */ +void i915_request_power_well(void) +{ + if (WARN_ON(!hsw_pwr)) + return; + + spin_lock_irq(&hsw_pwr->lock); + if (!hsw_pwr->count++ && + !hsw_pwr->i915_request) + __intel_set_power_well(hsw_pwr->device, true); + spin_unlock_irq(&hsw_pwr->lock); +} +EXPORT_SYMBOL_GPL(i915_request_power_well); + +/* Display audio driver power well release */ +void i915_release_power_well(void) +{ + if (WARN_ON(!hsw_pwr)) + return; + + spin_lock_irq(&hsw_pwr->lock); + WARN_ON(!hsw_pwr->count); + if (!--hsw_pwr->count && + !hsw_pwr->i915_request) + __intel_set_power_well(hsw_pwr->device, false); + spin_unlock_irq(&hsw_pwr->lock); +} +EXPORT_SYMBOL_GPL(i915_release_power_well); + +int i915_init_power_well(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + hsw_pwr = &dev_priv->power_well; + + hsw_pwr->device = dev; + spin_lock_init(&hsw_pwr->lock); + hsw_pwr->count = 0; + + return 0; +} + +void i915_remove_power_well(struct drm_device *dev) +{ + hsw_pwr = NULL; +} + +void intel_set_power_well(struct drm_device *dev, bool enable) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct i915_power_well *power_well = &dev_priv->power_well; + + if (!HAS_POWER_WELL(dev)) + return; + + if (!i915_disable_power_well && !enable) + return; + + spin_lock_irq(&power_well->lock); + power_well->i915_request = enable; + + /* only reject "disable" power well request */ + if (power_well->count && !enable) { + spin_unlock_irq(&power_well->lock); + return; + } + + __intel_set_power_well(dev, enable); + spin_unlock_irq(&power_well->lock); +} + /* * Starting with Haswell, we have a "Power Down Well" that can be turned off * when not needed anymore. We have 4 registers that can request the power well diff --git a/include/drm/i915_powerwell.h b/include/drm/i915_powerwell.h new file mode 100644 index 000000000000..cfdc884405b7 --- /dev/null +++ b/include/drm/i915_powerwell.h @@ -0,0 +1,36 @@ +/************************************************************************** + * + * Copyright 2013 Intel Inc. + * 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. + * + * + **************************************************************************/ + +#ifndef _I915_POWERWELL_H_ +#define _I915_POWERWELL_H_ + +/* For use by hda_i915 driver */ +extern void i915_request_power_well(void); +extern void i915_release_power_well(void); + +#endif /* _I915_POWERWELL_H_ */ -- cgit v1.2.3 From e83b7a8acc420923cbe8a30901d9eb60677f54fb Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 6 Jun 2013 16:35:44 +0200 Subject: mmc: tmio: fix unbalanced power-on calls with clock-gating enabled With MMC clock gating enabled the MMC core currently calls MMC host driver's .set_ios() method with .power_mode == MMC_POWER_ON and the clock value set either to 0 or to the target rate. The tmio MMC driver then wrongly translates the latter calls to card slot power-on requests, even when the slot already was on. This patch fixes the driver to avoid needlessly incrementing power-supplying regulator's use count. Signed-off-by: Guennadi Liakhovetski Tested-by: Laurent Pinchart Signed-off-by: Chris Ball --- drivers/mmc/host/tmio_mmc.h | 20 ++++++++++++++++++-- drivers/mmc/host/tmio_mmc_pio.c | 27 +++++++++++++++++---------- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index 759d8f4f130c..86fd21e00099 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -40,6 +40,22 @@ struct tmio_mmc_data; +/* + * We differentiate between the following 3 power states: + * 1. card slot powered off, controller stopped. This is used, when either there + * is no card in the slot, or the card really has to be powered down. + * 2. card slot powered on, controller stopped. This is used, when a card is in + * the slot, but no activity is currently taking place. This is a power- + * saving mode with card-state preserved. This state can be entered, e.g. + * when MMC clock-gating is used. + * 3. card slot powered on, controller running. This is the actual active state. + */ +enum tmio_mmc_power { + TMIO_MMC_OFF_STOP, /* card power off, controller stopped */ + TMIO_MMC_ON_STOP, /* card power on, controller stopped */ + TMIO_MMC_ON_RUN, /* card power on, controller running */ +}; + struct tmio_mmc_host { void __iomem *ctl; unsigned long bus_shift; @@ -48,8 +64,8 @@ struct tmio_mmc_host { struct mmc_data *data; struct mmc_host *mmc; - /* Controller power state */ - bool power; + /* Controller and card power state */ + enum tmio_mmc_power power; /* Callbacks for clock / power control */ void (*set_pwr)(struct platform_device *host, int state); diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 435cc4d2520f..67d96428e62a 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -859,7 +859,7 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) * is kept positive, so no suspending actually takes place. */ if (ios->power_mode == MMC_POWER_ON && ios->clock) { - if (!host->power) { + if (host->power != TMIO_MMC_ON_RUN) { tmio_mmc_clk_update(mmc); pm_runtime_get_sync(dev); if (host->resuming) { @@ -868,27 +868,34 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) } } tmio_mmc_set_clock(host, ios->clock); - if (!host->power) { + if (host->power == TMIO_MMC_OFF_STOP) /* power up SD card and the bus */ tmio_mmc_power_on(host, ios->vdd); - host->power = true; - } + host->power = TMIO_MMC_ON_RUN; /* start bus clock */ tmio_mmc_clk_start(host); } else if (ios->power_mode != MMC_POWER_UP) { - if (host->power) { - struct tmio_mmc_data *pdata = host->pdata; - if (ios->power_mode == MMC_POWER_OFF) + struct tmio_mmc_data *pdata = host->pdata; + unsigned int old_power = host->power; + + if (old_power != TMIO_MMC_OFF_STOP) { + if (ios->power_mode == MMC_POWER_OFF) { tmio_mmc_power_off(host); + host->power = TMIO_MMC_OFF_STOP; + } else { + host->power = TMIO_MMC_ON_STOP; + } + } + + if (old_power == TMIO_MMC_ON_RUN) { tmio_mmc_clk_stop(host); - host->power = false; pm_runtime_put(dev); if (pdata->clk_disable) pdata->clk_disable(host->pdev); } } - if (host->power) { + if (host->power != TMIO_MMC_OFF_STOP) { switch (ios->bus_width) { case MMC_BUS_WIDTH_1: sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x80e0); @@ -1029,7 +1036,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host, mmc->caps & MMC_CAP_NONREMOVABLE || mmc->slot.cd_irq >= 0); - _host->power = false; + _host->power = TMIO_MMC_OFF_STOP; pm_runtime_enable(&pdev->dev); ret = pm_runtime_resume(&pdev->dev); if (ret < 0) -- cgit v1.2.3 From b9ec2744128d0940342b236e9018614ba8848118 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 6 Jun 2013 16:35:48 +0200 Subject: mmc: tmio: reset the controller after power-up This fixes two reported problems: 1. after a system resume the controller isn't functioning until a command runs on a timeout and a controller reset is performed. 2. if a card is ejected during a running write operation, its re-insertion isn't detected. Reported-by: Nguyen Viet Dung Reported-by: Nguyen Hong Ky Signed-off-by: Guennadi Liakhovetski Tested-by: Nguyen Viet Dung Tested-by: Nguyen Hong Ky Signed-off-by: Chris Ball --- drivers/mmc/host/tmio_mmc_pio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 67d96428e62a..f29470835a4f 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -867,6 +867,8 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) host->resuming = false; } } + if (host->power == TMIO_MMC_OFF_STOP) + tmio_mmc_reset(host); tmio_mmc_set_clock(host, ios->clock); if (host->power == TMIO_MMC_OFF_STOP) /* power up SD card and the bus */ @@ -1186,7 +1188,6 @@ int tmio_mmc_host_runtime_resume(struct device *dev) struct mmc_host *mmc = dev_get_drvdata(dev); struct tmio_mmc_host *host = mmc_priv(mmc); - tmio_mmc_reset(host); tmio_mmc_enable_dma(host, true); return 0; -- cgit v1.2.3 From 3d289517dfd48f6487efda81543c3dda8b0e66f2 Mon Sep 17 00:00:00 2001 From: "Mathieu J. Poirier" Date: Wed, 5 Jun 2013 22:51:46 -0700 Subject: Input: sysrq - request graceful shutdown for key reset Attempt to reboot the system gracefully when a key combo is detected. If the reste combination is pressed the 2nd time we assume that graceful reboot failed and perform emergency reboot. This fucntionality is useful when UI is stuck but the system is otherwise working fine. Signed-off-by: Mathieu Poirier Signed-off-by: Dmitry Torokhov --- drivers/tty/sysrq.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index b6b267d939a0..80310f7f58d3 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include @@ -586,6 +587,7 @@ struct sysrq_state { /* reset sequence handling */ bool reset_canceled; + bool reset_requested; unsigned long reset_keybit[BITS_TO_LONGS(KEY_CNT)]; int reset_seq_len; int reset_seq_cnt; @@ -624,18 +626,26 @@ static void sysrq_parse_reset_sequence(struct sysrq_state *state) state->reset_seq_version = sysrq_reset_seq_version; } -static void sysrq_do_reset(unsigned long dummy) +static void sysrq_do_reset(unsigned long _state) { - __handle_sysrq(sysrq_xlate[KEY_B], false); + struct sysrq_state *state = (struct sysrq_state *) _state; + + state->reset_requested = true; + + sys_sync(); + kernel_restart(NULL); } static void sysrq_handle_reset_request(struct sysrq_state *state) { + if (state->reset_requested) + __handle_sysrq(sysrq_xlate[KEY_B], false); + if (sysrq_reset_downtime_ms) mod_timer(&state->keyreset_timer, jiffies + msecs_to_jiffies(sysrq_reset_downtime_ms)); else - sysrq_do_reset(0); + sysrq_do_reset((unsigned long)state); } static void sysrq_detect_reset_sequence(struct sysrq_state *state, @@ -837,7 +847,8 @@ static int sysrq_connect(struct input_handler *handler, sysrq->handle.handler = handler; sysrq->handle.name = "sysrq"; sysrq->handle.private = sysrq; - setup_timer(&sysrq->keyreset_timer, sysrq_do_reset, 0); + setup_timer(&sysrq->keyreset_timer, + sysrq_do_reset, (unsigned long)sysrq); error = input_register_handle(&sysrq->handle); if (error) { -- cgit v1.2.3 From 6ab59344d9796eaf1312c12cfa8ad08328d50fde Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 20 May 2013 10:49:34 -0400 Subject: NFSv4.1: Ensure that layoutget is called using the layout credential Ensure that we use the same credential for layoutget, layoutcommit and layoutreturn. Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 1 + fs/nfs/pnfs.c | 1 + include/linux/nfs_xdr.h | 1 + 3 files changed, 3 insertions(+) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index d7ba5616989c..a6b8db43ce3f 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -6348,6 +6348,7 @@ nfs4_proc_layoutget(struct nfs4_layoutget *lgp, gfp_t gfp_flags) .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTGET], .rpc_argp = &lgp->args, .rpc_resp = &lgp->res, + .rpc_cred = lgp->cred, }; struct rpc_task_setup task_setup_data = { .rpc_client = server->client, diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index c5bd758e5637..2f86115e6ad0 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -766,6 +766,7 @@ send_layoutget(struct pnfs_layout_hdr *lo, lgp->args.inode = ino; lgp->args.ctx = get_nfs_open_context(ctx); lgp->gfp_flags = gfp_flags; + lgp->cred = lo->plh_lc_cred; /* Synchronously retrieve layout information from server and * store in lseg. diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 104b62f23ee0..32c95d64e3aa 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -246,6 +246,7 @@ struct nfs4_layoutget_res { struct nfs4_layoutget { struct nfs4_layoutget_args args; struct nfs4_layoutget_res res; + struct rpc_cred *cred; gfp_t gfp_flags; }; -- cgit v1.2.3 From 9556000d8c5af9fb3a5f0abd97c632108f3acfb1 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 20 May 2013 10:43:47 -0400 Subject: NFSv4.1: Ensure that layoutreturn uses the correct credential We need to use the same credential as was used for the layoutget and/or layoutcommit operations. Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 1 + fs/nfs/pnfs.c | 1 + 2 files changed, 2 insertions(+) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index a6b8db43ce3f..a8d035e2c53b 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -6452,6 +6452,7 @@ int nfs4_proc_layoutreturn(struct nfs4_layoutreturn *lrp) .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTRETURN], .rpc_argp = &lrp->args, .rpc_resp = &lrp->res, + .rpc_cred = lrp->cred, }; struct rpc_task_setup task_setup_data = { .rpc_client = lrp->clp->cl_rpcclient, diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index 2f86115e6ad0..89ca75f25721 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -861,6 +861,7 @@ _pnfs_return_layout(struct inode *ino) lrp->args.inode = ino; lrp->args.layout = lo; lrp->clp = NFS_SERVER(ino)->nfs_client; + lrp->cred = lo->plh_lc_cred; status = nfs4_proc_layoutreturn(lrp); out: -- cgit v1.2.3 From 965e9c23de1c69a9fae2e68679027d01685530c4 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 20 May 2013 11:05:17 -0400 Subject: NFSv4.1: Ensure that reclaim_complete uses the right credential We want to use the same credential for reclaim_complete as we used for the exchange_id call. Signed-off-by: Trond Myklebust --- fs/nfs/nfs4_fs.h | 2 +- fs/nfs/nfs4proc.c | 4 +++- fs/nfs/nfs4state.c | 13 ++++++++++--- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index a1dd768d0a35..e64a00c03371 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -194,7 +194,7 @@ struct nfs4_state_recovery_ops { int (*recover_lock)(struct nfs4_state *, struct file_lock *); int (*establish_clid)(struct nfs_client *, struct rpc_cred *); struct rpc_cred * (*get_clid_cred)(struct nfs_client *); - int (*reclaim_complete)(struct nfs_client *); + int (*reclaim_complete)(struct nfs_client *, struct rpc_cred *); int (*detect_trunking)(struct nfs_client *, struct nfs_client **, struct rpc_cred *); }; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index a8d035e2c53b..d94af83171af 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -6159,12 +6159,14 @@ static const struct rpc_call_ops nfs4_reclaim_complete_call_ops = { /* * Issue a global reclaim complete. */ -static int nfs41_proc_reclaim_complete(struct nfs_client *clp) +static int nfs41_proc_reclaim_complete(struct nfs_client *clp, + struct rpc_cred *cred) { struct nfs4_reclaim_complete_data *calldata; struct rpc_task *task; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RECLAIM_COMPLETE], + .rpc_cred = cred, }; struct rpc_task_setup task_setup_data = { .rpc_client = clp->cl_rpcclient, diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 1fab140764c4..5244ffd304e8 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -1563,11 +1563,12 @@ static void nfs4_state_start_reclaim_reboot(struct nfs_client *clp) } static void nfs4_reclaim_complete(struct nfs_client *clp, - const struct nfs4_state_recovery_ops *ops) + const struct nfs4_state_recovery_ops *ops, + struct rpc_cred *cred) { /* Notify the server we're done reclaiming our state */ if (ops->reclaim_complete) - (void)ops->reclaim_complete(clp); + (void)ops->reclaim_complete(clp, cred); } static void nfs4_clear_reclaim_server(struct nfs_server *server) @@ -1612,9 +1613,15 @@ static int nfs4_state_clear_reclaim_reboot(struct nfs_client *clp) static void nfs4_state_end_reclaim_reboot(struct nfs_client *clp) { + const struct nfs4_state_recovery_ops *ops; + struct rpc_cred *cred; + if (!nfs4_state_clear_reclaim_reboot(clp)) return; - nfs4_reclaim_complete(clp, clp->cl_mvops->reboot_recovery_ops); + ops = clp->cl_mvops->reboot_recovery_ops; + cred = ops->get_clid_cred(clp); + nfs4_reclaim_complete(clp, ops, cred); + put_rpccred(cred); } static void nfs_delegation_clear_all(struct nfs_client *clp) -- cgit v1.2.3 From ab7cb0dfab0baa63f9a1faa7441b90b07881a9c7 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 20 May 2013 11:20:27 -0400 Subject: NFSv4.1: Ensure that test_stateid and free_stateid use correct credentials Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 65 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 15 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index d94af83171af..e4398e8f512a 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -83,8 +83,10 @@ static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred, struct nfs_fattr *fattr, struct iattr *sattr, struct nfs4_state *state); #ifdef CONFIG_NFS_V4_1 -static int nfs41_test_stateid(struct nfs_server *, nfs4_stateid *); -static int nfs41_free_stateid(struct nfs_server *, nfs4_stateid *); +static int nfs41_test_stateid(struct nfs_server *, nfs4_stateid *, + struct rpc_cred *); +static int nfs41_free_stateid(struct nfs_server *, nfs4_stateid *, + struct rpc_cred *); #endif /* Prevent leaks of NFSv4 errors into userland */ static int nfs4_map_errors(int err) @@ -1855,18 +1857,30 @@ static void nfs41_clear_delegation_stateid(struct nfs4_state *state) { struct nfs_server *server = NFS_SERVER(state->inode); nfs4_stateid *stateid = &state->stateid; - int status; + struct nfs_delegation *delegation; + struct rpc_cred *cred = NULL; + int status = -NFS4ERR_BAD_STATEID; /* If a state reset has been done, test_stateid is unneeded */ if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0) return; - status = nfs41_test_stateid(server, stateid); + /* Get the delegation credential for use by test/free_stateid */ + rcu_read_lock(); + delegation = rcu_dereference(NFS_I(state->inode)->delegation); + if (delegation != NULL && + nfs4_stateid_match(&delegation->stateid, stateid)) { + cred = get_rpccred(delegation->cred); + rcu_read_unlock(); + status = nfs41_test_stateid(server, stateid, cred); + } else + rcu_read_unlock(); + if (status != NFS_OK) { /* Free the stateid unless the server explicitly * informs us the stateid is unrecognized. */ if (status != -NFS4ERR_BAD_STATEID) - nfs41_free_stateid(server, stateid); + nfs41_free_stateid(server, stateid, cred); nfs_remove_bad_delegation(state->inode); write_seqlock(&state->seqlock); @@ -1874,6 +1888,9 @@ static void nfs41_clear_delegation_stateid(struct nfs4_state *state) write_sequnlock(&state->seqlock); clear_bit(NFS_DELEGATED_STATE, &state->flags); } + + if (cred != NULL) + put_rpccred(cred); } /** @@ -1888,6 +1905,7 @@ static int nfs41_check_open_stateid(struct nfs4_state *state) { struct nfs_server *server = NFS_SERVER(state->inode); nfs4_stateid *stateid = &state->open_stateid; + struct rpc_cred *cred = state->owner->so_cred; int status; /* If a state reset has been done, test_stateid is unneeded */ @@ -1896,12 +1914,12 @@ static int nfs41_check_open_stateid(struct nfs4_state *state) (test_bit(NFS_O_RDWR_STATE, &state->flags) == 0)) return -NFS4ERR_BAD_STATEID; - status = nfs41_test_stateid(server, stateid); + status = nfs41_test_stateid(server, stateid, cred); if (status != NFS_OK) { /* Free the stateid unless the server explicitly * informs us the stateid is unrecognized. */ if (status != -NFS4ERR_BAD_STATEID) - nfs41_free_stateid(server, stateid); + nfs41_free_stateid(server, stateid, cred); clear_bit(NFS_O_RDONLY_STATE, &state->flags); clear_bit(NFS_O_WRONLY_STATE, &state->flags); @@ -5056,13 +5074,18 @@ static int nfs41_check_expired_locks(struct nfs4_state *state) list_for_each_entry(lsp, &state->lock_states, ls_locks) { if (test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags)) { - status = nfs41_test_stateid(server, &lsp->ls_stateid); + struct rpc_cred *cred = lsp->ls_state->owner->so_cred; + + status = nfs41_test_stateid(server, + &lsp->ls_stateid, + cred); if (status != NFS_OK) { /* Free the stateid unless the server * informs us the stateid is unrecognized. */ if (status != -NFS4ERR_BAD_STATEID) nfs41_free_stateid(server, - &lsp->ls_stateid); + &lsp->ls_stateid, + cred); clear_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags); ret = status; } @@ -6737,7 +6760,9 @@ out: return err; } -static int _nfs41_test_stateid(struct nfs_server *server, nfs4_stateid *stateid) +static int _nfs41_test_stateid(struct nfs_server *server, + nfs4_stateid *stateid, + struct rpc_cred *cred) { int status; struct nfs41_test_stateid_args args = { @@ -6748,6 +6773,7 @@ static int _nfs41_test_stateid(struct nfs_server *server, nfs4_stateid *stateid) .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_TEST_STATEID], .rpc_argp = &args, .rpc_resp = &res, + .rpc_cred = cred, }; dprintk("NFS call test_stateid %p\n", stateid); @@ -6768,17 +6794,20 @@ static int _nfs41_test_stateid(struct nfs_server *server, nfs4_stateid *stateid) * * @server: server / transport on which to perform the operation * @stateid: state ID to test + * @cred: credential * * Returns NFS_OK if the server recognizes that "stateid" is valid. * Otherwise a negative NFS4ERR value is returned if the operation * failed or the state ID is not currently valid. */ -static int nfs41_test_stateid(struct nfs_server *server, nfs4_stateid *stateid) +static int nfs41_test_stateid(struct nfs_server *server, + nfs4_stateid *stateid, + struct rpc_cred *cred) { struct nfs4_exception exception = { }; int err; do { - err = _nfs41_test_stateid(server, stateid); + err = _nfs41_test_stateid(server, stateid, cred); if (err != -NFS4ERR_DELAY) break; nfs4_handle_exception(server, err, &exception); @@ -6827,10 +6856,12 @@ const struct rpc_call_ops nfs41_free_stateid_ops = { static struct rpc_task *_nfs41_free_stateid(struct nfs_server *server, nfs4_stateid *stateid, + struct rpc_cred *cred, bool privileged) { struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FREE_STATEID], + .rpc_cred = cred, }; struct rpc_task_setup task_setup = { .rpc_client = server->client, @@ -6863,16 +6894,19 @@ static struct rpc_task *_nfs41_free_stateid(struct nfs_server *server, * * @server: server / transport on which to perform the operation * @stateid: state ID to release + * @cred: credential * * Returns NFS_OK if the server freed "stateid". Otherwise a * negative NFS4ERR value is returned. */ -static int nfs41_free_stateid(struct nfs_server *server, nfs4_stateid *stateid) +static int nfs41_free_stateid(struct nfs_server *server, + nfs4_stateid *stateid, + struct rpc_cred *cred) { struct rpc_task *task; int ret; - task = _nfs41_free_stateid(server, stateid, true); + task = _nfs41_free_stateid(server, stateid, cred, true); if (IS_ERR(task)) return PTR_ERR(task); ret = rpc_wait_for_completion_task(task); @@ -6885,8 +6919,9 @@ static int nfs41_free_stateid(struct nfs_server *server, nfs4_stateid *stateid) static int nfs41_free_lock_state(struct nfs_server *server, struct nfs4_lock_state *lsp) { struct rpc_task *task; + struct rpc_cred *cred = lsp->ls_state->owner->so_cred; - task = _nfs41_free_stateid(server, &lsp->ls_stateid, false); + task = _nfs41_free_stateid(server, &lsp->ls_stateid, cred, false); nfs4_free_lock_state(server, lsp); if (IS_ERR(task)) return PTR_ERR(task); -- cgit v1.2.3 From cd5875fefe09b1921ea5845890009c9b2386d566 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 20 May 2013 11:42:54 -0400 Subject: NFSv4.1: Use layout credentials for get_deviceinfo calls This is not strictly needed, since get_deviceinfo is not allowed to return NFS4ERR_ACCESS or NFS4ERR_WRONG_CRED, but lets do it anyway for consistency with other pNFS operations. Signed-off-by: Trond Myklebust --- fs/nfs/blocklayout/blocklayout.c | 2 +- fs/nfs/nfs4filelayout.c | 3 ++- fs/nfs/nfs4filelayout.h | 3 ++- fs/nfs/nfs4filelayoutdev.c | 7 +++++-- fs/nfs/nfs4proc.c | 11 ++++++++--- fs/nfs/objlayout/objlayout.c | 3 ++- fs/nfs/pnfs.h | 3 ++- 7 files changed, 22 insertions(+), 10 deletions(-) diff --git a/fs/nfs/blocklayout/blocklayout.c b/fs/nfs/blocklayout/blocklayout.c index 434b93ec0970..1e5fdd3506e2 100644 --- a/fs/nfs/blocklayout/blocklayout.c +++ b/fs/nfs/blocklayout/blocklayout.c @@ -1091,7 +1091,7 @@ nfs4_blk_get_deviceinfo(struct nfs_server *server, const struct nfs_fh *fh, dev->mincount = 0; dprintk("%s: dev_id: %s\n", __func__, dev->dev_id.data); - rc = nfs4_proc_getdeviceinfo(server, dev); + rc = nfs4_proc_getdeviceinfo(server, dev, NULL); dprintk("%s getdevice info returns %d\n", __func__, rc); if (rc) { rv = ERR_PTR(rc); diff --git a/fs/nfs/nfs4filelayout.c b/fs/nfs/nfs4filelayout.c index 22d10623f5ee..17ed87ef9de8 100644 --- a/fs/nfs/nfs4filelayout.c +++ b/fs/nfs/nfs4filelayout.c @@ -643,7 +643,8 @@ filelayout_check_layout(struct pnfs_layout_hdr *lo, d = nfs4_find_get_deviceid(NFS_SERVER(lo->plh_inode)->pnfs_curr_ld, NFS_SERVER(lo->plh_inode)->nfs_client, id); if (d == NULL) { - dsaddr = filelayout_get_device_info(lo->plh_inode, id, gfp_flags); + dsaddr = filelayout_get_device_info(lo->plh_inode, id, + lo->plh_lc_cred, gfp_flags); if (dsaddr == NULL) goto out; } else diff --git a/fs/nfs/nfs4filelayout.h b/fs/nfs/nfs4filelayout.h index 235ff952d3c8..cebd20e7e923 100644 --- a/fs/nfs/nfs4filelayout.h +++ b/fs/nfs/nfs4filelayout.h @@ -150,6 +150,7 @@ struct nfs4_pnfs_ds *nfs4_fl_prepare_ds(struct pnfs_layout_segment *lseg, extern void nfs4_fl_put_deviceid(struct nfs4_file_layout_dsaddr *dsaddr); extern void nfs4_fl_free_deviceid(struct nfs4_file_layout_dsaddr *dsaddr); struct nfs4_file_layout_dsaddr * -filelayout_get_device_info(struct inode *inode, struct nfs4_deviceid *dev_id, gfp_t gfp_flags); +filelayout_get_device_info(struct inode *inode, struct nfs4_deviceid *dev_id, + struct rpc_cred *cred, gfp_t gfp_flags); #endif /* FS_NFS_NFS4FILELAYOUT_H */ diff --git a/fs/nfs/nfs4filelayoutdev.c b/fs/nfs/nfs4filelayoutdev.c index 661a0f611215..0493dbd129c5 100644 --- a/fs/nfs/nfs4filelayoutdev.c +++ b/fs/nfs/nfs4filelayoutdev.c @@ -668,7 +668,10 @@ decode_and_add_device(struct inode *inode, struct pnfs_device *dev, gfp_t gfp_fl * of available devices, and return it. */ struct nfs4_file_layout_dsaddr * -filelayout_get_device_info(struct inode *inode, struct nfs4_deviceid *dev_id, gfp_t gfp_flags) +filelayout_get_device_info(struct inode *inode, + struct nfs4_deviceid *dev_id, + struct rpc_cred *cred, + gfp_t gfp_flags) { struct pnfs_device *pdev = NULL; u32 max_resp_sz; @@ -709,7 +712,7 @@ filelayout_get_device_info(struct inode *inode, struct nfs4_deviceid *dev_id, gf pdev->pglen = max_resp_sz; pdev->mincount = 0; - rc = nfs4_proc_getdeviceinfo(server, pdev); + rc = nfs4_proc_getdeviceinfo(server, pdev, cred); dprintk("%s getdevice info returns %d\n", __func__, rc); if (rc) goto out_free; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index e4398e8f512a..98f0df3ee50d 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -6547,7 +6547,9 @@ int nfs4_proc_getdevicelist(struct nfs_server *server, EXPORT_SYMBOL_GPL(nfs4_proc_getdevicelist); static int -_nfs4_proc_getdeviceinfo(struct nfs_server *server, struct pnfs_device *pdev) +_nfs4_proc_getdeviceinfo(struct nfs_server *server, + struct pnfs_device *pdev, + struct rpc_cred *cred) { struct nfs4_getdeviceinfo_args args = { .pdev = pdev, @@ -6559,6 +6561,7 @@ _nfs4_proc_getdeviceinfo(struct nfs_server *server, struct pnfs_device *pdev) .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETDEVICEINFO], .rpc_argp = &args, .rpc_resp = &res, + .rpc_cred = cred, }; int status; @@ -6569,14 +6572,16 @@ _nfs4_proc_getdeviceinfo(struct nfs_server *server, struct pnfs_device *pdev) return status; } -int nfs4_proc_getdeviceinfo(struct nfs_server *server, struct pnfs_device *pdev) +int nfs4_proc_getdeviceinfo(struct nfs_server *server, + struct pnfs_device *pdev, + struct rpc_cred *cred) { struct nfs4_exception exception = { }; int err; do { err = nfs4_handle_exception(server, - _nfs4_proc_getdeviceinfo(server, pdev), + _nfs4_proc_getdeviceinfo(server, pdev, cred), &exception); } while (exception.retry); return err; diff --git a/fs/nfs/objlayout/objlayout.c b/fs/nfs/objlayout/objlayout.c index a9ebd817278b..198990852d50 100644 --- a/fs/nfs/objlayout/objlayout.c +++ b/fs/nfs/objlayout/objlayout.c @@ -614,7 +614,8 @@ int objlayout_get_deviceinfo(struct pnfs_layout_hdr *pnfslay, pd.pglen = PAGE_SIZE; pd.mincount = 0; - err = nfs4_proc_getdeviceinfo(NFS_SERVER(pnfslay->plh_inode), &pd); + err = nfs4_proc_getdeviceinfo(NFS_SERVER(pnfslay->plh_inode), &pd, + pnfslay->plh_lc_cred); dprintk("%s nfs_getdeviceinfo returned %d\n", __func__, err); if (err) goto err_out; diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h index f5f8a470a647..1441dff9b0e7 100644 --- a/fs/nfs/pnfs.h +++ b/fs/nfs/pnfs.h @@ -170,7 +170,8 @@ extern int nfs4_proc_getdevicelist(struct nfs_server *server, const struct nfs_fh *fh, struct pnfs_devicelist *devlist); extern int nfs4_proc_getdeviceinfo(struct nfs_server *server, - struct pnfs_device *dev); + struct pnfs_device *dev, + struct rpc_cred *cred); extern struct pnfs_layout_segment* nfs4_proc_layoutget(struct nfs4_layoutget *lgp, gfp_t gfp_flags); extern int nfs4_proc_layoutreturn(struct nfs4_layoutreturn *lrp); -- cgit v1.2.3 From 4f0b429df104c8ab41fa1b3013e13e0f95f3da52 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 20 May 2013 12:24:03 -0400 Subject: NFSv4.1: Enable state protection Use the EXCHGID4_FLAG_BIND_PRINC_STATEID exchange_id flag to enable stateid protection. This means that if we create a stateid using a particular principal, then we must use the same principal if we want to change that state. IOW: if we OPEN a file using a particular credential, then we have to use the same credential in subsequent OPEN_DOWNGRADE, CLOSE, or DELEGRETURN operations that use that stateid. Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 98f0df3ee50d..7490359f6c60 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -5528,7 +5528,8 @@ int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred) struct nfs41_exchange_id_args args = { .verifier = &verifier, .client = clp, - .flags = EXCHGID4_FLAG_SUPP_MOVED_REFER, + .flags = EXCHGID4_FLAG_SUPP_MOVED_REFER | + EXCHGID4_FLAG_BIND_PRINC_STATEID, }; struct nfs41_exchange_id_res res = { 0 -- cgit v1.2.3 From 5cc2216db844beac6ce78c3e48137cd58911b297 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 21 May 2013 09:26:49 -0400 Subject: NFSv4.1: Simplify setting the layout header credential ctx->cred == ctx->state->owner->so_cred, so let's just use the former. Signed-off-by: Trond Myklebust --- fs/nfs/pnfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index 89ca75f25721..7bb03c1da234 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -1052,7 +1052,7 @@ alloc_init_layout_hdr(struct inode *ino, INIT_LIST_HEAD(&lo->plh_segs); INIT_LIST_HEAD(&lo->plh_bulk_destroy); lo->plh_inode = ino; - lo->plh_lc_cred = get_rpccred(ctx->state->owner->so_cred); + lo->plh_lc_cred = get_rpccred(ctx->cred); return lo; } -- cgit v1.2.3 From a76580fbf09e6e19c2040c08969af5137e064eda Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Mon, 20 May 2013 23:00:18 -0400 Subject: SUNRPC: Fix a potential race in rpc_execute If the rpc_task is asynchronous, it could theoretically finish executing on the workqueue it was assigned by rpc_make_runnable() before we get round to testing RPC_IS_ASYNC() in rpc_execute. In practice, however, all the existing callers hold a reference to the rpc_task, so this can't happen today... Signed-off-by: Trond Myklebust --- net/sunrpc/sched.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index 5356b120dbf8..849ca413522c 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -825,9 +825,11 @@ static void __rpc_execute(struct rpc_task *task) */ void rpc_execute(struct rpc_task *task) { + bool is_async = RPC_IS_ASYNC(task); + rpc_set_active(task); rpc_make_runnable(task); - if (!RPC_IS_ASYNC(task)) + if (!is_async) __rpc_execute(task); } -- cgit v1.2.3 From 0053a8e65c0b949fd230488e5be871755f3f860f Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 21 May 2013 12:51:32 -0400 Subject: SUNRPC: Remove unused function rpc_queue_empty Signed-off-by: Trond Myklebust --- include/linux/sunrpc/sched.h | 1 - net/sunrpc/sched.c | 14 -------------- 2 files changed, 15 deletions(-) diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h index 84ca436b76c2..5e255ab893da 100644 --- a/include/linux/sunrpc/sched.h +++ b/include/linux/sunrpc/sched.h @@ -238,7 +238,6 @@ struct rpc_task *rpc_wake_up_first(struct rpc_wait_queue *, bool (*)(struct rpc_task *, void *), void *); void rpc_wake_up_status(struct rpc_wait_queue *, int); -int rpc_queue_empty(struct rpc_wait_queue *); void rpc_delay(struct rpc_task *, unsigned long); void * rpc_malloc(struct rpc_task *, size_t); void rpc_free(void *); diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index 849ca413522c..dcbd69cb1cbd 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -445,20 +445,6 @@ static void rpc_wake_up_task_queue_locked(struct rpc_wait_queue *queue, struct r } } -/* - * Tests whether rpc queue is empty - */ -int rpc_queue_empty(struct rpc_wait_queue *queue) -{ - int res; - - spin_lock_bh(&queue->lock); - res = queue->qlen; - spin_unlock_bh(&queue->lock); - return res == 0; -} -EXPORT_SYMBOL_GPL(rpc_queue_empty); - /* * Wake up a task on a specific queue */ -- cgit v1.2.3 From 64bbe3d670ed595df2589d16297305ea9518a84f Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 21 May 2013 12:58:57 -0400 Subject: SUNRPC: Remove the unused helpers task_for_each() and task_for_first() Signed-off-by: Trond Myklebust --- include/linux/sunrpc/sched.h | 9 --------- 1 file changed, 9 deletions(-) diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h index 5e255ab893da..7ec7e6e7e42c 100644 --- a/include/linux/sunrpc/sched.h +++ b/include/linux/sunrpc/sched.h @@ -88,15 +88,6 @@ struct rpc_task { tk_rebind_retry : 2; }; -/* support walking a list of tasks on a wait queue */ -#define task_for_each(task, pos, head) \ - list_for_each(pos, head) \ - if ((task=list_entry(pos, struct rpc_task, u.tk_wait.list)),1) - -#define task_for_first(task, head) \ - if (!list_empty(head) && \ - ((task=list_entry((head)->next, struct rpc_task, u.tk_wait.list)),1)) - typedef void (*rpc_action)(struct rpc_task *); struct rpc_call_ops { -- cgit v1.2.3 From 74fe5f7c2a74d58a39a386bd511e50d1dfc0134c Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 21 May 2013 18:36:27 -0400 Subject: SUNRPC: Remove unused functions rpc_task_set/has_priority Signed-off-by: Trond Myklebust --- include/linux/sunrpc/sched.h | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h index 7ec7e6e7e42c..6d870353674a 100644 --- a/include/linux/sunrpc/sched.h +++ b/include/linux/sunrpc/sched.h @@ -249,16 +249,6 @@ static inline int rpc_wait_for_completion_task(struct rpc_task *task) return __rpc_wait_for_completion_task(task, NULL); } -static inline void rpc_task_set_priority(struct rpc_task *task, unsigned char prio) -{ - task->tk_priority = prio - RPC_PRIORITY_LOW; -} - -static inline int rpc_task_has_priority(struct rpc_task *task, unsigned char prio) -{ - return (task->tk_priority + RPC_PRIORITY_LOW == prio); -} - #if defined(RPC_DEBUG) || defined (RPC_TRACEPOINTS) static inline const char * rpc_qname(const struct rpc_wait_queue *q) { -- cgit v1.2.3 From 9ec2ef53b92fdbb1b5f24af000fc2ba0b18221ea Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 22 May 2013 18:52:18 -0400 Subject: SUNRPC: Remove redundant call to rpc_set_running() in __rpc_execute() The RPC_TASK_RUNNING flag will always have been set in rpc_make_runnable() once we get past the test for out_of_line_wait_on_bit() returning ERESTARTSYS. Signed-off-by: Trond Myklebust --- net/sunrpc/sched.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index dcbd69cb1cbd..b7b32c34c18d 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -790,7 +790,6 @@ static void __rpc_execute(struct rpc_task *task) task->tk_flags |= RPC_TASK_KILLED; rpc_exit(task, -ERESTARTSYS); } - rpc_set_running(task); dprintk("RPC: %5u sync task resuming\n", task->tk_pid); } -- cgit v1.2.3 From 1a1a29fa8420e42a760410d9f5b0b51a9eb521eb Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 29 May 2013 11:53:29 -0400 Subject: NFSv4: Remove redundant check for FMODE_EXEC in nfs_finish_open We already check the EXEC access mode in the lower layers. Signed-off-by: Trond Myklebust --- fs/nfs/dir.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index e093e73178b7..bf4e5f47d686 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -1362,13 +1362,6 @@ static int nfs_finish_open(struct nfs_open_context *ctx, ctx->dentry = dget(dentry); } - /* If the open_intent is for execute, we have an extra check to make */ - if (ctx->mode & FMODE_EXEC) { - err = nfs_may_open(dentry->d_inode, ctx->cred, open_flags); - if (err < 0) - goto out; - } - err = finish_open(file, dentry, do_open, opened); if (err) goto out; -- cgit v1.2.3 From 4197a055eb06f5fa7e73290b8b710cd32176cd41 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 29 May 2013 12:37:49 -0400 Subject: NFSv4: Cleanup: pass the nfs_open_context to nfs4_do_open All the callers have an open_context at this point, and since we always need one in order to do state recovery, it makes sense to use it as the basis for the nfs4_do_open() call. Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 7490359f6c60..32d942d7f343 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -1996,18 +1996,19 @@ out: * Returns a referenced nfs4_state */ static int _nfs4_do_open(struct inode *dir, - struct dentry *dentry, - fmode_t fmode, + struct nfs_open_context *ctx, int flags, struct iattr *sattr, - struct rpc_cred *cred, - struct nfs4_state **res, - struct nfs4_threshold **ctx_th) + struct nfs4_state **res) { struct nfs4_state_owner *sp; struct nfs4_state *state = NULL; struct nfs_server *server = NFS_SERVER(dir); struct nfs4_opendata *opendata; + struct dentry *dentry = ctx->dentry; + struct rpc_cred *cred = ctx->cred; + struct nfs4_threshold **ctx_th = &ctx->mdsthreshold; + fmode_t fmode = ctx->mode & (FMODE_READ|FMODE_WRITE|FMODE_EXEC); enum open_claim_type4 claim = NFS4_OPEN_CLAIM_NULL; int status; @@ -2079,22 +2080,17 @@ out_err: static struct nfs4_state *nfs4_do_open(struct inode *dir, - struct dentry *dentry, - fmode_t fmode, + struct nfs_open_context *ctx, int flags, - struct iattr *sattr, - struct rpc_cred *cred, - struct nfs4_threshold **ctx_th) + struct iattr *sattr) { struct nfs_server *server = NFS_SERVER(dir); struct nfs4_exception exception = { }; struct nfs4_state *res; int status; - fmode &= FMODE_READ|FMODE_WRITE|FMODE_EXEC; do { - status = _nfs4_do_open(dir, dentry, fmode, flags, sattr, cred, - &res, ctx_th); + status = _nfs4_do_open(dir, ctx, flags, sattr, &res); if (status == 0) break; /* NOTE: BAD_SEQID means the server and client disagree about the @@ -2446,8 +2442,7 @@ nfs4_atomic_open(struct inode *dir, struct nfs_open_context *ctx, int open_flags struct nfs4_state *state; /* Protect against concurrent sillydeletes */ - state = nfs4_do_open(dir, ctx->dentry, ctx->mode, open_flags, attr, - ctx->cred, &ctx->mdsthreshold); + state = nfs4_do_open(dir, ctx, open_flags, attr); if (IS_ERR(state)) return ERR_CAST(state); ctx->state = state; @@ -3056,9 +3051,7 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, return PTR_ERR(ctx); sattr->ia_mode &= ~current_umask(); - state = nfs4_do_open(dir, dentry, ctx->mode, - flags, sattr, ctx->cred, - &ctx->mdsthreshold); + state = nfs4_do_open(dir, ctx, flags, sattr); d_drop(dentry); if (IS_ERR(state)) { status = PTR_ERR(state); -- cgit v1.2.3 From 3efb9722475e56c8878ce09395dd8ccb7a1a3611 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 29 May 2013 13:17:04 -0400 Subject: NFSv4: Refactor _nfs4_open_and_get_state to set ctx->state Instead of having the callers set ctx->state, do it inside _nfs4_open_and_get_state. Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 32d942d7f343..70f378f071d8 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -1960,7 +1960,7 @@ static inline void nfs4_exclusive_attrset(struct nfs4_opendata *opendata, struct static int _nfs4_open_and_get_state(struct nfs4_opendata *opendata, fmode_t fmode, int flags, - struct nfs4_state **res) + struct nfs_open_context *ctx) { struct nfs4_state_owner *sp = opendata->owner; struct nfs_server *server = sp->so_server; @@ -1985,9 +1985,9 @@ static int _nfs4_open_and_get_state(struct nfs4_opendata *opendata, if (ret != 0) goto out; + ctx->state = state; if (read_seqcount_retry(&sp->so_reclaim_seqcount, seq)) nfs4_schedule_stateid_recovery(server, state); - *res = state; out: return ret; } @@ -1998,8 +1998,7 @@ out: static int _nfs4_do_open(struct inode *dir, struct nfs_open_context *ctx, int flags, - struct iattr *sattr, - struct nfs4_state **res) + struct iattr *sattr) { struct nfs4_state_owner *sp; struct nfs4_state *state = NULL; @@ -2041,9 +2040,10 @@ static int _nfs4_do_open(struct inode *dir, if (dentry->d_inode != NULL) opendata->state = nfs4_get_open_state(dentry->d_inode, sp); - status = _nfs4_open_and_get_state(opendata, fmode, flags, &state); + status = _nfs4_open_and_get_state(opendata, fmode, flags, ctx); if (status != 0) goto err_opendata_put; + state = ctx->state; if ((opendata->o_arg.open_flags & O_EXCL) && (opendata->o_arg.createmode != NFS4_CREATE_GUARDED)) { @@ -2066,7 +2066,6 @@ static int _nfs4_do_open(struct inode *dir, nfs4_opendata_put(opendata); nfs4_put_state_owner(sp); - *res = state; return 0; err_opendata_put: kfree(opendata->f_attr.mdsthreshold); @@ -2074,7 +2073,6 @@ err_opendata_put: err_put_state_owner: nfs4_put_state_owner(sp); out_err: - *res = NULL; return status; } @@ -2090,7 +2088,8 @@ static struct nfs4_state *nfs4_do_open(struct inode *dir, int status; do { - status = _nfs4_do_open(dir, ctx, flags, sattr, &res); + status = _nfs4_do_open(dir, ctx, flags, sattr); + res = ctx->state; if (status == 0) break; /* NOTE: BAD_SEQID means the server and client disagree about the @@ -2445,7 +2444,6 @@ nfs4_atomic_open(struct inode *dir, struct nfs_open_context *ctx, int open_flags state = nfs4_do_open(dir, ctx, open_flags, attr); if (IS_ERR(state)) return ERR_CAST(state); - ctx->state = state; return igrab(state->inode); } @@ -3059,7 +3057,6 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, } d_add(dentry, igrab(state->inode)); nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); - ctx->state = state; out: put_nfs_open_context(ctx); return status; -- cgit v1.2.3 From 275bb307865a316cef390e01e6ab5e21e97023a2 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 29 May 2013 13:11:28 -0400 Subject: NFSv4: Move dentry instantiation into the NFSv4-specific atomic open code Signed-off-by: Trond Myklebust --- fs/nfs/dir.c | 19 +++---------------- fs/nfs/nfs4file.c | 1 - fs/nfs/nfs4proc.c | 21 +++++++++++++++++---- 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index bf4e5f47d686..c662ff6befd6 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -1357,11 +1357,6 @@ static int nfs_finish_open(struct nfs_open_context *ctx, { int err; - if (ctx->dentry != dentry) { - dput(ctx->dentry); - ctx->dentry = dget(dentry); - } - err = finish_open(file, dentry, do_open, opened); if (err) goto out; @@ -1420,13 +1415,13 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry, nfs_block_sillyrename(dentry->d_parent); inode = NFS_PROTO(dir)->open_context(dir, ctx, open_flags, &attr); - d_drop(dentry); + nfs_unblock_sillyrename(dentry->d_parent); if (IS_ERR(inode)) { - nfs_unblock_sillyrename(dentry->d_parent); put_nfs_open_context(ctx); err = PTR_ERR(inode); switch (err) { case -ENOENT: + d_drop(dentry); d_add(dentry, NULL); break; case -EISDIR: @@ -1442,16 +1437,8 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry, } goto out; } - res = d_add_unique(dentry, inode); - if (res != NULL) - dentry = res; - - nfs_unblock_sillyrename(dentry->d_parent); - nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); - - err = nfs_finish_open(ctx, dentry, file, open_flags, opened); - dput(res); + err = nfs_finish_open(ctx, ctx->dentry, file, open_flags, opened); out: return err; diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c index 13e6bb3e3fe5..e5b804dd944c 100644 --- a/fs/nfs/nfs4file.c +++ b/fs/nfs/nfs4file.c @@ -69,7 +69,6 @@ nfs4_file_open(struct inode *inode, struct file *filp) goto out_drop; } } - iput(inode); if (inode != dentry->d_inode) goto out_drop; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 70f378f071d8..aaf2c1324be7 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -1964,6 +1964,7 @@ static int _nfs4_open_and_get_state(struct nfs4_opendata *opendata, { struct nfs4_state_owner *sp = opendata->owner; struct nfs_server *server = sp->so_server; + struct dentry *dentry; struct nfs4_state *state; unsigned int seq; int ret; @@ -1981,6 +1982,21 @@ static int _nfs4_open_and_get_state(struct nfs4_opendata *opendata, if (server->caps & NFS_CAP_POSIX_LOCK) set_bit(NFS_STATE_POSIX_LOCKS, &state->flags); + dentry = opendata->dentry; + if (dentry->d_inode == NULL) { + /* FIXME: Is this d_drop() ever needed? */ + d_drop(dentry); + dentry = d_add_unique(dentry, igrab(state->inode)); + if (dentry == NULL) { + dentry = opendata->dentry; + } else if (dentry != ctx->dentry) { + dput(ctx->dentry); + ctx->dentry = dget(dentry); + } + nfs_set_verifier(dentry, + nfs_save_change_attribute(opendata->dir->d_inode)); + } + ret = nfs4_opendata_access(sp->so_cred, opendata, state, fmode, flags); if (ret != 0) goto out; @@ -2444,7 +2460,7 @@ nfs4_atomic_open(struct inode *dir, struct nfs_open_context *ctx, int open_flags state = nfs4_do_open(dir, ctx, open_flags, attr); if (IS_ERR(state)) return ERR_CAST(state); - return igrab(state->inode); + return state->inode; } static void nfs4_close_context(struct nfs_open_context *ctx, int is_sync) @@ -3050,13 +3066,10 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, sattr->ia_mode &= ~current_umask(); state = nfs4_do_open(dir, ctx, flags, sattr); - d_drop(dentry); if (IS_ERR(state)) { status = PTR_ERR(state); goto out; } - d_add(dentry, igrab(state->inode)); - nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); out: put_nfs_open_context(ctx); return status; -- cgit v1.2.3 From c45ffdd26961302ec5eeac7311553d6f1e348e9c Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 29 May 2013 13:34:46 -0400 Subject: NFSv4: Close another NFSv4 recovery race State recovery currently relies on being able to find a valid nfs_open_context in the inode->open_files list. We therefore need to put the nfs_open_context on the list while we're still protected by the sp->so_reclaim_seqcount in order to avoid reboot races. Signed-off-by: Trond Myklebust --- fs/nfs/inode.c | 16 ++++++++++++---- fs/nfs/nfs4proc.c | 7 +++++-- include/linux/nfs_fs.h | 1 + 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index c1c7a9d78722..c121982659a2 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -713,16 +713,23 @@ EXPORT_SYMBOL_GPL(put_nfs_open_context); * Ensure that mmap has a recent RPC credential for use when writing out * shared pages */ -void nfs_file_set_open_context(struct file *filp, struct nfs_open_context *ctx) +void nfs_inode_attach_open_context(struct nfs_open_context *ctx) { - struct inode *inode = file_inode(filp); + struct inode *inode = ctx->dentry->d_inode; struct nfs_inode *nfsi = NFS_I(inode); - filp->private_data = get_nfs_open_context(ctx); spin_lock(&inode->i_lock); list_add(&ctx->list, &nfsi->open_files); spin_unlock(&inode->i_lock); } +EXPORT_SYMBOL_GPL(nfs_inode_attach_open_context); + +void nfs_file_set_open_context(struct file *filp, struct nfs_open_context *ctx) +{ + filp->private_data = get_nfs_open_context(ctx); + if (list_empty(&ctx->list)) + nfs_inode_attach_open_context(ctx); +} EXPORT_SYMBOL_GPL(nfs_file_set_open_context); /* @@ -748,10 +755,11 @@ struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_c static void nfs_file_clear_open_context(struct file *filp) { - struct inode *inode = file_inode(filp); struct nfs_open_context *ctx = nfs_file_open_context(filp); if (ctx) { + struct inode *inode = ctx->dentry->d_inode; + filp->private_data = NULL; spin_lock(&inode->i_lock); list_move_tail(&ctx->list, &NFS_I(inode)->open_files); diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index aaf2c1324be7..65467abbd5a6 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -2002,8 +2002,11 @@ static int _nfs4_open_and_get_state(struct nfs4_opendata *opendata, goto out; ctx->state = state; - if (read_seqcount_retry(&sp->so_reclaim_seqcount, seq)) - nfs4_schedule_stateid_recovery(server, state); + if (dentry->d_inode == state->inode) { + nfs_inode_attach_open_context(ctx); + if (read_seqcount_retry(&sp->so_reclaim_seqcount, seq)) + nfs4_schedule_stateid_recovery(server, state); + } out: return ret; } diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index fc01d5cb4cf1..1384ed92cad6 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -356,6 +356,7 @@ extern struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ct extern void put_nfs_open_context(struct nfs_open_context *ctx); extern struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_cred *cred, fmode_t mode); extern struct nfs_open_context *alloc_nfs_open_context(struct dentry *dentry, fmode_t f_mode); +extern void nfs_inode_attach_open_context(struct nfs_open_context *ctx); extern void nfs_file_set_open_context(struct file *filp, struct nfs_open_context *ctx); extern struct nfs_lock_context *nfs_get_lock_context(struct nfs_open_context *ctx); extern void nfs_put_lock_context(struct nfs_lock_context *l_ctx); -- cgit v1.2.3 From 8b8d2b658f2cb7092672a56c72b41121ce786d93 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 6 Jun 2013 12:10:46 -0600 Subject: PCI/AER: Don't parse HEST table for non-PCIe devices AER is a PCIe-only capability, so there's no point in trying to match a HEST PCIe structure with a non-PCIe device. Previously, a HEST global AER bridge entry (type 8) could incorrectly match *any* bridge, even a legacy PCI-PCI bridge, and a non-global HEST entry could match a legacy PCI device. Tested-by: Betty Dall Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aer/aerdrv_acpi.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/pci/pcie/aer/aerdrv_acpi.c b/drivers/pci/pcie/aer/aerdrv_acpi.c index 5194a7d41730..4f798ab629c8 100644 --- a/drivers/pci/pcie/aer/aerdrv_acpi.c +++ b/drivers/pci/pcie/aer/aerdrv_acpi.c @@ -59,8 +59,7 @@ static int aer_hest_parse(struct acpi_hest_header *hest_hdr, void *data) p = (struct acpi_hest_aer_common *)(hest_hdr + 1); if (p->flags & ACPI_HEST_GLOBAL) { - if ((pci_is_pcie(info->pci_dev) && - pci_pcie_type(info->pci_dev) == pcie_type) || bridge) + if ((pci_pcie_type(info->pci_dev) == pcie_type) || bridge) ff = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST); } else if (hest_match_pci(p, info->pci_dev)) @@ -89,6 +88,9 @@ static void aer_set_firmware_first(struct pci_dev *pci_dev) int pcie_aer_get_firmware_first(struct pci_dev *dev) { + if (!pci_is_pcie(dev)) + return 0; + if (!dev->__aer_firmware_first_valid) aer_set_firmware_first(dev); return dev->__aer_firmware_first; -- cgit v1.2.3 From a6bd73cdc93ec03781ff99beb5b465fd6ea3a784 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 6 Jun 2013 12:10:47 -0600 Subject: PCI/AER: Factor out HEST device type matching This factors out the matching of HEST structure type and PCIe device type to improve readability. No functional change. Tested-by: Betty Dall Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aer/aerdrv_acpi.c | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/drivers/pci/pcie/aer/aerdrv_acpi.c b/drivers/pci/pcie/aer/aerdrv_acpi.c index 4f798ab629c8..e7ea5735a61f 100644 --- a/drivers/pci/pcie/aer/aerdrv_acpi.c +++ b/drivers/pci/pcie/aer/aerdrv_acpi.c @@ -29,6 +29,22 @@ static inline int hest_match_pci(struct acpi_hest_aer_common *p, p->function == PCI_FUNC(pci->devfn)); } +static inline bool hest_match_type(struct acpi_hest_header *hest_hdr, + struct pci_dev *dev) +{ + u16 hest_type = hest_hdr->type; + u8 pcie_type = pci_pcie_type(dev); + + if ((hest_type == ACPI_HEST_TYPE_AER_ROOT_PORT && + pcie_type == PCI_EXP_TYPE_ROOT_PORT) || + (hest_type == ACPI_HEST_TYPE_AER_ENDPOINT && + pcie_type == PCI_EXP_TYPE_ENDPOINT) || + (hest_type == ACPI_HEST_TYPE_AER_BRIDGE && + (dev->class >> 16) == PCI_BASE_CLASS_BRIDGE)) + return true; + return false; +} + struct aer_hest_parse_info { struct pci_dev *pci_dev; int firmware_first; @@ -38,28 +54,11 @@ static int aer_hest_parse(struct acpi_hest_header *hest_hdr, void *data) { struct aer_hest_parse_info *info = data; struct acpi_hest_aer_common *p; - u8 pcie_type = 0; - u8 bridge = 0; int ff = 0; - switch (hest_hdr->type) { - case ACPI_HEST_TYPE_AER_ROOT_PORT: - pcie_type = PCI_EXP_TYPE_ROOT_PORT; - break; - case ACPI_HEST_TYPE_AER_ENDPOINT: - pcie_type = PCI_EXP_TYPE_ENDPOINT; - break; - case ACPI_HEST_TYPE_AER_BRIDGE: - if ((info->pci_dev->class >> 16) == PCI_BASE_CLASS_BRIDGE) - bridge = 1; - break; - default: - return 0; - } - p = (struct acpi_hest_aer_common *)(hest_hdr + 1); if (p->flags & ACPI_HEST_GLOBAL) { - if ((pci_pcie_type(info->pci_dev) == pcie_type) || bridge) + if (hest_match_type(hest_hdr, info->pci_dev)) ff = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST); } else if (hest_match_pci(p, info->pci_dev)) -- cgit v1.2.3 From 8d2a171f18f7cc9271ee3b9f1355fa4a13380320 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 6 Jun 2013 12:10:48 -0600 Subject: PCI/AER: Set dev->__aer_firmware_first only for matching devices Previously, we always updated info->firmware_first, even for HEST entries that didn't match the device. Therefore, if the last HEST descriptor was a PCIe structure that didn't match the device, we always cleared dev->__aer_firmware_first. Tested-by: Betty Dall Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aer/aerdrv_acpi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pci/pcie/aer/aerdrv_acpi.c b/drivers/pci/pcie/aer/aerdrv_acpi.c index e7ea5735a61f..cf611ab2193a 100644 --- a/drivers/pci/pcie/aer/aerdrv_acpi.c +++ b/drivers/pci/pcie/aer/aerdrv_acpi.c @@ -54,16 +54,16 @@ static int aer_hest_parse(struct acpi_hest_header *hest_hdr, void *data) { struct aer_hest_parse_info *info = data; struct acpi_hest_aer_common *p; - int ff = 0; + int ff; p = (struct acpi_hest_aer_common *)(hest_hdr + 1); + ff = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST); if (p->flags & ACPI_HEST_GLOBAL) { if (hest_match_type(hest_hdr, info->pci_dev)) - ff = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST); + info->firmware_first = ff; } else if (hest_match_pci(p, info->pci_dev)) - ff = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST); - info->firmware_first = ff; + info->firmware_first = ff; return 0; } -- cgit v1.2.3 From 9e50a9122f048c67a4e83916434e2e212a6f0fe2 Mon Sep 17 00:00:00 2001 From: Betty Dall Date: Thu, 6 Jun 2013 12:10:49 -0600 Subject: PCI/AER: Move AER severity defines to aer.h The function aer_recover_queue() is a public interface and the severity argument uses #defines that are in the private header pci/pcie/aer/aerdrv.h. This patch moves the #defines from pci/pcie/aer/aerdrv.h to include/linux/aer.h. [bhelgaas: split "remove 'extern' from declarations" to another patch] Signed-off-by: Betty Dall Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aer/aerdrv.h | 4 ---- include/linux/aer.h | 4 ++++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h index d12c77cd6991..90ea3e88041f 100644 --- a/drivers/pci/pcie/aer/aerdrv.h +++ b/drivers/pci/pcie/aer/aerdrv.h @@ -13,10 +13,6 @@ #include #include -#define AER_NONFATAL 0 -#define AER_FATAL 1 -#define AER_CORRECTABLE 2 - #define SYSTEM_ERROR_INTR_ON_MESG_MASK (PCI_EXP_RTCTL_SECEE| \ PCI_EXP_RTCTL_SENFEE| \ PCI_EXP_RTCTL_SEFEE) diff --git a/include/linux/aer.h b/include/linux/aer.h index ec10e1b24c1c..d2cf2e4f9c2d 100644 --- a/include/linux/aer.h +++ b/include/linux/aer.h @@ -7,6 +7,10 @@ #ifndef _AER_H_ #define _AER_H_ +#define AER_NONFATAL 0 +#define AER_FATAL 1 +#define AER_CORRECTABLE 2 + struct aer_header_log_regs { unsigned int dw0; unsigned int dw1; -- cgit v1.2.3 From bb760063790fbbc3c956f23aff4dbdfdd3c03818 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 6 Jun 2013 14:55:52 +0200 Subject: drm/i915: pipe config quirk infrastructure plus sdvo mode.flags fix For various reasons the hw state readout might not be able to faithfully match the hw state: - broken hw (like the case which motivated this patch here where the sdvo encoder does not implemented mandatory functionality correctly). - platforms which are not supported fully with the pipe config infrastructure - if our code doesn't support a given hw configuration natively, e.g. special restrictions on the per-pipe panel fitters when they're used in high-quality scaling modes. In all these cases both fastboot and the hw state cross checker need to be aware of these cases and act accordingly. To be able to do this add a new quirk flag to the pipe config structure. The specific case at hand is an sdvo encoder which doesn't implement the get_timings function, so adjusted_mode flags will be wrong. The strange thing though is that the encoder _does_ work, even though it doesn't implement any of the timings functions (so neither get nor set, neither for input nor output timings). Not that non-compliant sdvo encoder are any surprise at all ... v2: - Don't read random garbage from the dtd if the get_timings call failed (suggested by Chris). - Still check the interlaced flag, that's read out from someplace else. We want maximal paranoia, after all. Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 22 ++++++++++++++-------- drivers/gpu/drm/i915/intel_drv.h | 11 +++++++++++ drivers/gpu/drm/i915/intel_sdvo.c | 24 +++++++++++++----------- 3 files changed, 38 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 421b7e2ece3b..8d9e7c0e9e4b 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8091,6 +8091,9 @@ intel_pipe_config_compare(struct drm_device *dev, return false; \ } +#define PIPE_CONF_QUIRK(quirk) \ + ((current_config->quirks | pipe_config->quirks) & (quirk)) + PIPE_CONF_CHECK_I(cpu_transcoder); PIPE_CONF_CHECK_I(has_pch_encoder); @@ -8121,14 +8124,16 @@ intel_pipe_config_compare(struct drm_device *dev, PIPE_CONF_CHECK_FLAGS(adjusted_mode.flags, DRM_MODE_FLAG_INTERLACE); - PIPE_CONF_CHECK_FLAGS(adjusted_mode.flags, - DRM_MODE_FLAG_PHSYNC); - PIPE_CONF_CHECK_FLAGS(adjusted_mode.flags, - DRM_MODE_FLAG_NHSYNC); - PIPE_CONF_CHECK_FLAGS(adjusted_mode.flags, - DRM_MODE_FLAG_PVSYNC); - PIPE_CONF_CHECK_FLAGS(adjusted_mode.flags, - DRM_MODE_FLAG_NVSYNC); + if (!PIPE_CONF_QUIRK(PIPE_CONFIG_QUIRK_MODE_SYNC_FLAGS)) { + PIPE_CONF_CHECK_FLAGS(adjusted_mode.flags, + DRM_MODE_FLAG_PHSYNC); + PIPE_CONF_CHECK_FLAGS(adjusted_mode.flags, + DRM_MODE_FLAG_NHSYNC); + PIPE_CONF_CHECK_FLAGS(adjusted_mode.flags, + DRM_MODE_FLAG_PVSYNC); + PIPE_CONF_CHECK_FLAGS(adjusted_mode.flags, + DRM_MODE_FLAG_NVSYNC); + } PIPE_CONF_CHECK_I(requested_mode.hdisplay); PIPE_CONF_CHECK_I(requested_mode.vdisplay); @@ -8145,6 +8150,7 @@ intel_pipe_config_compare(struct drm_device *dev, #undef PIPE_CONF_CHECK_I #undef PIPE_CONF_CHECK_FLAGS +#undef PIPE_CONF_QUIRK return true; } diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 1735cdc86770..b76a956cfb9a 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -193,6 +193,17 @@ typedef struct dpll { } intel_clock_t; struct intel_crtc_config { + /** + * quirks - bitfield with hw state readout quirks + * + * For various reasons the hw state readout code might not be able to + * completely faithfully read out the current state. These cases are + * tracked with quirk flags so that fastboot and state checker can act + * accordingly. + */ +#define PIPE_CONFIG_QUIRK_MODE_SYNC_FLAGS (1<<0) /* unreliable sync mode.flags */ + unsigned long quirks; + struct drm_display_mode requested_mode; struct drm_display_mode adjusted_mode; /* This flag must be set by the encoder's compute_config callback if it diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 5c816dd75bec..960358d8e336 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1324,19 +1324,21 @@ static void intel_sdvo_get_config(struct intel_encoder *encoder, ret = intel_sdvo_get_input_timing(intel_sdvo, &dtd); if (!ret) { + /* Some sdvo encoders are not spec compliant and don't + * implement the mandatory get_timings function. */ DRM_DEBUG_DRIVER("failed to retrieve SDVO DTD\n"); - return; - } - - if (dtd.part2.dtd_flags & DTD_FLAG_HSYNC_POSITIVE) - flags |= DRM_MODE_FLAG_PHSYNC; - else - flags |= DRM_MODE_FLAG_NHSYNC; + pipe_config->quirks |= PIPE_CONFIG_QUIRK_MODE_SYNC_FLAGS; + } else { + if (dtd.part2.dtd_flags & DTD_FLAG_HSYNC_POSITIVE) + flags |= DRM_MODE_FLAG_PHSYNC; + else + flags |= DRM_MODE_FLAG_NHSYNC; - if (dtd.part2.dtd_flags & DTD_FLAG_VSYNC_POSITIVE) - flags |= DRM_MODE_FLAG_PVSYNC; - else - flags |= DRM_MODE_FLAG_NVSYNC; + if (dtd.part2.dtd_flags & DTD_FLAG_VSYNC_POSITIVE) + flags |= DRM_MODE_FLAG_PVSYNC; + else + flags |= DRM_MODE_FLAG_NVSYNC; + } pipe_config->adjusted_mode.flags |= flags; -- cgit v1.2.3 From fde41b9fa2d0d6abd5b1b5674f1da3bb40ebc98d Mon Sep 17 00:00:00 2001 From: Betty Dall Date: Thu, 6 Jun 2013 14:35:35 -0600 Subject: PCI/AER: Remove "extern" from function declarations We had an inconsistent mix of using and omitting the "extern" keyword on function declarations in header files. This removes them all. [bhelgaas: split out from "move AER severity defines" patch] Signed-off-by: Betty Dall Signed-off-by: Bjorn Helgaas --- include/linux/aer.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/include/linux/aer.h b/include/linux/aer.h index d2cf2e4f9c2d..55bb3dc4b2db 100644 --- a/include/linux/aer.h +++ b/include/linux/aer.h @@ -35,9 +35,9 @@ struct aer_capability_regs { #if defined(CONFIG_PCIEAER) /* pci-e port driver needs this function to enable aer */ -extern int pci_enable_pcie_error_reporting(struct pci_dev *dev); -extern int pci_disable_pcie_error_reporting(struct pci_dev *dev); -extern int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev); +int pci_enable_pcie_error_reporting(struct pci_dev *dev); +int pci_disable_pcie_error_reporting(struct pci_dev *dev); +int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev); #else static inline int pci_enable_pcie_error_reporting(struct pci_dev *dev) { @@ -53,10 +53,10 @@ static inline int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev) } #endif -extern void cper_print_aer(const char *prefix, struct pci_dev *dev, - int cper_severity, struct aer_capability_regs *aer); -extern int cper_severity_to_aer(int cper_severity); -extern void aer_recover_queue(int domain, unsigned int bus, unsigned int devfn, - int severity); +void cper_print_aer(const char *prefix, struct pci_dev *dev, int cper_severity, + struct aer_capability_regs *aer); +int cper_severity_to_aer(int cper_severity); +void aer_recover_queue(int domain, unsigned int bus, unsigned int devfn, + int severity); #endif //_AER_H_ -- cgit v1.2.3 From 0ba98ec9196746dd6abfa7bb9856ef4f29ffb9be Mon Sep 17 00:00:00 2001 From: Betty Dall Date: Thu, 6 Jun 2013 12:10:50 -0600 Subject: ACPI / APEI: Force fatal AER severity when component has been reset The CPER error record has a reset bit that indicates that the platform has reset the component. The reset bit can be set for any severity error including recoverable. From the AER code path's perspective, any error is fatal if the component has been reset. This patch upgrades the severity of the AER recovery to AER_FATAL whenever the CPER error record indicates that the component has been reset. [bhelgaas: s/bus has been reset/component has been reset/] Signed-off-by: Betty Dall Signed-off-by: Bjorn Helgaas --- drivers/acpi/apei/ghes.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index d668a8ae602b..ab315515908e 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -449,9 +449,19 @@ static void ghes_do_proc(struct ghes *ghes, pcie_err->validation_bits & CPER_PCIE_VALID_AER_INFO) { unsigned int devfn; int aer_severity; + devfn = PCI_DEVFN(pcie_err->device_id.device, pcie_err->device_id.function); aer_severity = cper_severity_to_aer(sev); + + /* + * If firmware reset the component to contain + * the error, we must reinitialize it before + * use, so treat it as a fatal AER error. + */ + if (gdata->flags & CPER_SEC_RESET) + aer_severity = AER_FATAL; + aer_recover_queue(pcie_err->device_id.segment, pcie_err->device_id.bus, devfn, aer_severity); -- cgit v1.2.3 From 081d0fe0ef60c258b67428a8fdf2d14f8ce392ef Mon Sep 17 00:00:00 2001 From: Betty Dall Date: Thu, 6 Jun 2013 12:10:51 -0600 Subject: PCI/AER: Reset link for devices below Root Port or Downstream Port When a PCIe device reports a fatal error, we reset the link leading to it. Previously we only did this for devices below Downstream Ports, not for devices directly below Root Ports. This patch changes that so we reset the link leading to devices below Root Ports just like we do for those below Downstream Ports. [bhelgaas: changelog, keep dev_printk(KERN_DEBUG)] Signed-off-by: Betty Dall Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aer/aerdrv_core.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index 8ec8b4f48560..d9e776e69fe8 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -400,16 +400,16 @@ void aer_do_secondary_bus_reset(struct pci_dev *dev) } /** - * default_downstream_reset_link - default reset function for Downstream Port - * @dev: pointer to downstream port's pci_dev data structure + * default_reset_link - default reset function + * @dev: pointer to pci_dev data structure * - * Invoked when performing link reset at Downstream Port w/ no aer driver. + * Invoked when performing link reset on a Downstream Port or a + * Root Port with no aer driver. */ -static pci_ers_result_t default_downstream_reset_link(struct pci_dev *dev) +static pci_ers_result_t default_reset_link(struct pci_dev *dev) { aer_do_secondary_bus_reset(dev); - dev_printk(KERN_DEBUG, &dev->dev, - "Downstream Port link has been reset\n"); + dev_printk(KERN_DEBUG, &dev->dev, "downstream link has been reset\n"); return PCI_ERS_RESULT_RECOVERED; } @@ -458,8 +458,9 @@ static pci_ers_result_t reset_link(struct pci_dev *dev) if (driver && driver->reset_link) { status = driver->reset_link(udev); - } else if (pci_pcie_type(udev) == PCI_EXP_TYPE_DOWNSTREAM) { - status = default_downstream_reset_link(udev); + } else if (pci_pcie_type(udev) == PCI_EXP_TYPE_DOWNSTREAM || + pci_pcie_type(udev) == PCI_EXP_TYPE_ROOT_PORT) { + status = default_reset_link(udev); } else { dev_printk(KERN_DEBUG, &dev->dev, "no link-reset support at upstream device %s\n", -- cgit v1.2.3 From 439d7a358f93a52458527329939be9f97db1242a Mon Sep 17 00:00:00 2001 From: Mark Langsdorf Date: Thu, 30 May 2013 15:17:30 -0500 Subject: ahci: make ahci_transmit_led_message into a function pointer Create a new ata_port_operations function pointer called transmit_led_message and give it the default value of ahci_transmit_led_message. This allows AHCI controllers with non-standard LED interfaces to use the existing em_ interface. Signed-off-by: Mark Langsdorf Signed-off-by: Tejun Heo --- drivers/ata/libahci.c | 11 ++++++----- include/linux/libata.h | 3 +++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index 3797a7bba15d..64c3f1f41af1 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -173,6 +173,7 @@ struct ata_port_operations ahci_ops = { .em_store = ahci_led_store, .sw_activity_show = ahci_activity_show, .sw_activity_store = ahci_activity_store, + .transmit_led_message = ahci_transmit_led_message, #ifdef CONFIG_PM .port_suspend = ahci_port_suspend, .port_resume = ahci_port_resume, @@ -774,7 +775,7 @@ static void ahci_start_port(struct ata_port *ap) /* EM Transmit bit maybe busy during init */ for (i = 0; i < EM_MAX_RETRY; i++) { - rc = ahci_transmit_led_message(ap, + rc = ap->ops->transmit_led_message(ap, emp->led_state, 4); if (rc == -EBUSY) @@ -915,7 +916,7 @@ static void ahci_sw_activity_blink(unsigned long arg) led_message |= (1 << 16); } spin_unlock_irqrestore(ap->lock, flags); - ahci_transmit_led_message(ap, led_message, 4); + ap->ops->transmit_led_message(ap, led_message, 4); } static void ahci_init_sw_activity(struct ata_link *link) @@ -1044,7 +1045,7 @@ static ssize_t ahci_led_store(struct ata_port *ap, const char *buf, if (emp->blink_policy) state &= ~EM_MSG_LED_VALUE_ACTIVITY; - return ahci_transmit_led_message(ap, state, size); + return ap->ops->transmit_led_message(ap, state, size); } static ssize_t ahci_activity_store(struct ata_device *dev, enum sw_activity val) @@ -1063,7 +1064,7 @@ static ssize_t ahci_activity_store(struct ata_device *dev, enum sw_activity val) /* set the LED to OFF */ port_led_state &= EM_MSG_LED_VALUE_OFF; port_led_state |= (ap->port_no | (link->pmp << 8)); - ahci_transmit_led_message(ap, port_led_state, 4); + ap->ops->transmit_led_message(ap, port_led_state, 4); } else { link->flags |= ATA_LFLAG_SW_ACTIVITY; if (val == BLINK_OFF) { @@ -1071,7 +1072,7 @@ static ssize_t ahci_activity_store(struct ata_device *dev, enum sw_activity val) port_led_state &= EM_MSG_LED_VALUE_OFF; port_led_state |= (ap->port_no | (link->pmp << 8)); port_led_state |= EM_MSG_LED_VALUE_ON; /* check this */ - ahci_transmit_led_message(ap, port_led_state, 4); + ap->ops->transmit_led_message(ap, port_led_state, 4); } } emp->blink_policy = val; diff --git a/include/linux/libata.h b/include/linux/libata.h index c886dc87aa81..4ea55bb45deb 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -910,6 +910,9 @@ struct ata_port_operations { ssize_t (*sw_activity_show)(struct ata_device *dev, char *buf); ssize_t (*sw_activity_store)(struct ata_device *dev, enum sw_activity val); + ssize_t (*transmit_led_message)(struct ata_port *ap, u32 state, + ssize_t size); + /* * Obsolete */ -- cgit v1.2.3 From d50b110f14ad07066f9ad6e7f32e2b1a595b92f9 Mon Sep 17 00:00:00 2001 From: Mark Langsdorf Date: Thu, 6 Jun 2013 07:52:41 -0500 Subject: sata highbank: add bit-banged SGPIO driver support Highbank supports SGPIO by bit-banging out the SGPIO signals over three GPIO pins defined in the DTB. Add support for this SGPIO functionality. Signed-off-by: Mark Langsdorf Signed-off-by: Tejun Heo --- .../devicetree/bindings/ata/ahci-platform.txt | 5 + arch/arm/boot/dts/ecx-common.dtsi | 2 + drivers/ata/ahci.h | 1 + drivers/ata/sata_highbank.c | 161 ++++++++++++++++++++- 4 files changed, 163 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/ata/ahci-platform.txt b/Documentation/devicetree/bindings/ata/ahci-platform.txt index b519f9b699c3..3ec0c5c4f0e9 100644 --- a/Documentation/devicetree/bindings/ata/ahci-platform.txt +++ b/Documentation/devicetree/bindings/ata/ahci-platform.txt @@ -12,6 +12,11 @@ Optional properties: - calxeda,port-phys: phandle-combophy and lane assignment, which maps each SATA port to a combophy and a lane within that combophy +- calxeda,sgpio-gpio: phandle-gpio bank, bit offset, and default on or off, + which indicates that the driver supports SGPIO + indicator lights using the indicated GPIOs +- calxeda,led-order : a u32 array that map port numbers to offsets within the + SGPIO bitstream. - dma-coherent : Present if dma operations are coherent Example: diff --git a/arch/arm/boot/dts/ecx-common.dtsi b/arch/arm/boot/dts/ecx-common.dtsi index d61b535f682a..e8559b753c9d 100644 --- a/arch/arm/boot/dts/ecx-common.dtsi +++ b/arch/arm/boot/dts/ecx-common.dtsi @@ -33,6 +33,8 @@ calxeda,port-phys = <&combophy5 0 &combophy0 0 &combophy0 1 &combophy0 2 &combophy0 3>; + calxeda,sgpio-gpio =<&gpioh 5 1 &gpioh 6 1 &gpioh 7 1>; + calxeda,led-order = <4 0 1 2 3>; }; sdhci@ffe0e000 { diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h index 05adf29a2814..adfb2df20b7a 100644 --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h @@ -322,6 +322,7 @@ struct ahci_host_priv { u32 em_buf_sz; /* EM buffer size in byte */ u32 em_msg_type; /* EM message type */ struct clk *clk; /* Only for platforms supporting clk */ + void *plat_data; /* Other platform data */ }; extern int ahci_ignore_sss; diff --git a/drivers/ata/sata_highbank.c b/drivers/ata/sata_highbank.c index b20aa96b958d..8de8ac80335b 100644 --- a/drivers/ata/sata_highbank.c +++ b/drivers/ata/sata_highbank.c @@ -33,6 +33,9 @@ #include #include #include +#include +#include + #include "ahci.h" #define CPHY_MAP(dev, addr) ((((dev) & 0x1f) << 7) | (((addr) >> 9) & 0x7f)) @@ -66,6 +69,146 @@ struct phy_lane_info { }; static struct phy_lane_info port_data[CPHY_PORT_COUNT]; +static DEFINE_SPINLOCK(sgpio_lock); +#define SCLOCK 0 +#define SLOAD 1 +#define SDATA 2 +#define SGPIO_PINS 3 +#define SGPIO_PORTS 8 + +/* can be cast as an ahci_host_priv for compatibility with most functions */ +struct ecx_plat_data { + u32 n_ports; + unsigned sgpio_gpio[SGPIO_PINS]; + u32 sgpio_pattern; + u32 port_to_sgpio[SGPIO_PORTS]; +}; + +#define SGPIO_SIGNALS 3 +#define ECX_ACTIVITY_BITS 0x300000 +#define ECX_ACTIVITY_SHIFT 2 +#define ECX_LOCATE_BITS 0x80000 +#define ECX_LOCATE_SHIFT 1 +#define ECX_FAULT_BITS 0x400000 +#define ECX_FAULT_SHIFT 0 +static inline int sgpio_bit_shift(struct ecx_plat_data *pdata, u32 port, + u32 shift) +{ + return 1 << (3 * pdata->port_to_sgpio[port] + shift); +} + +static void ecx_parse_sgpio(struct ecx_plat_data *pdata, u32 port, u32 state) +{ + if (state & ECX_ACTIVITY_BITS) + pdata->sgpio_pattern |= sgpio_bit_shift(pdata, port, + ECX_ACTIVITY_SHIFT); + else + pdata->sgpio_pattern &= ~sgpio_bit_shift(pdata, port, + ECX_ACTIVITY_SHIFT); + if (state & ECX_LOCATE_BITS) + pdata->sgpio_pattern |= sgpio_bit_shift(pdata, port, + ECX_LOCATE_SHIFT); + else + pdata->sgpio_pattern &= ~sgpio_bit_shift(pdata, port, + ECX_LOCATE_SHIFT); + if (state & ECX_FAULT_BITS) + pdata->sgpio_pattern |= sgpio_bit_shift(pdata, port, + ECX_FAULT_SHIFT); + else + pdata->sgpio_pattern &= ~sgpio_bit_shift(pdata, port, + ECX_FAULT_SHIFT); +} + +/* + * Tell the LED controller that the signal has changed by raising the clock + * line for 50 uS and then lowering it for 50 uS. + */ +static void ecx_led_cycle_clock(struct ecx_plat_data *pdata) +{ + gpio_set_value(pdata->sgpio_gpio[SCLOCK], 1); + udelay(50); + gpio_set_value(pdata->sgpio_gpio[SCLOCK], 0); + udelay(50); +} + +static ssize_t ecx_transmit_led_message(struct ata_port *ap, u32 state, + ssize_t size) +{ + struct ahci_host_priv *hpriv = ap->host->private_data; + struct ecx_plat_data *pdata = (struct ecx_plat_data *) hpriv->plat_data; + struct ahci_port_priv *pp = ap->private_data; + unsigned long flags; + int pmp, i; + struct ahci_em_priv *emp; + u32 sgpio_out; + + /* get the slot number from the message */ + pmp = (state & EM_MSG_LED_PMP_SLOT) >> 8; + if (pmp < EM_MAX_SLOTS) + emp = &pp->em_priv[pmp]; + else + return -EINVAL; + + if (!(hpriv->em_msg_type & EM_MSG_TYPE_LED)) + return size; + + spin_lock_irqsave(&sgpio_lock, flags); + ecx_parse_sgpio(pdata, ap->port_no, state); + sgpio_out = pdata->sgpio_pattern; + gpio_set_value(pdata->sgpio_gpio[SLOAD], 1); + ecx_led_cycle_clock(pdata); + gpio_set_value(pdata->sgpio_gpio[SLOAD], 0); + /* + * bit-bang out the SGPIO pattern, by consuming a bit and then + * clocking it out. + */ + for (i = 0; i < (SGPIO_SIGNALS * pdata->n_ports); i++) { + gpio_set_value(pdata->sgpio_gpio[SDATA], sgpio_out & 1); + sgpio_out >>= 1; + ecx_led_cycle_clock(pdata); + } + + /* save off new led state for port/slot */ + emp->led_state = state; + + spin_unlock_irqrestore(&sgpio_lock, flags); + return size; +} + +static void highbank_set_em_messages(struct device *dev, + struct ahci_host_priv *hpriv, + struct ata_port_info *pi) +{ + struct device_node *np = dev->of_node; + struct ecx_plat_data *pdata = hpriv->plat_data; + int i; + int err; + + for (i = 0; i < SGPIO_PINS; i++) { + err = of_get_named_gpio(np, "calxeda,sgpio-gpio", i); + if (IS_ERR_VALUE(err)) + return; + + pdata->sgpio_gpio[i] = err; + err = gpio_request(pdata->sgpio_gpio[i], "CX SGPIO"); + if (err) { + pr_err("sata_highbank gpio_request %d failed: %d\n", + i, err); + return; + } + gpio_direction_output(pdata->sgpio_gpio[i], 1); + } + of_property_read_u32_array(np, "calxeda,led-order", + pdata->port_to_sgpio, + pdata->n_ports); + + /* store em_loc */ + hpriv->em_loc = 0; + hpriv->em_buf_sz = 4; + hpriv->em_msg_type = EM_MSG_TYPE_LED; + pi->flags |= ATA_FLAG_EM | ATA_FLAG_SW_ACTIVITY; +} + static u32 __combo_phy_reg_read(u8 sata_port, u32 addr) { u32 data; @@ -241,6 +384,7 @@ static int ahci_highbank_hardreset(struct ata_link *link, unsigned int *class, static struct ata_port_operations ahci_highbank_ops = { .inherits = &ahci_ops, .hardreset = ahci_highbank_hardreset, + .transmit_led_message = ecx_transmit_led_message, }; static const struct ata_port_info ahci_highbank_port_info = { @@ -264,12 +408,13 @@ static int ahci_highbank_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct ahci_host_priv *hpriv; + struct ecx_plat_data *pdata; struct ata_host *host; struct resource *mem; int irq; - int n_ports; int i; int rc; + u32 n_ports; struct ata_port_info pi = ahci_highbank_port_info; const struct ata_port_info *ppi[] = { &pi, NULL }; @@ -290,6 +435,11 @@ static int ahci_highbank_probe(struct platform_device *pdev) dev_err(dev, "can't alloc ahci_host_priv\n"); return -ENOMEM; } + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(dev, "can't alloc ecx_plat_data\n"); + return -ENOMEM; + } hpriv->flags |= (unsigned long)pi.private_data; @@ -313,8 +463,6 @@ static int ahci_highbank_probe(struct platform_device *pdev) if (hpriv->cap & HOST_CAP_PMP) pi.flags |= ATA_FLAG_PMP; - ahci_set_em_messages(hpriv, &pi); - /* CAP.NP sometimes indicate the index of the last enabled * port, at other times, that of the last possible port, so * determining the maximum port number requires looking at @@ -322,6 +470,10 @@ static int ahci_highbank_probe(struct platform_device *pdev) */ n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map)); + pdata->n_ports = n_ports; + hpriv->plat_data = pdata; + highbank_set_em_messages(dev, hpriv, &pi); + host = ata_host_alloc_pinfo(dev, ppi, n_ports); if (!host) { rc = -ENOMEM; @@ -333,9 +485,6 @@ static int ahci_highbank_probe(struct platform_device *pdev) if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss) host->flags |= ATA_HOST_PARALLEL_SCAN; - if (pi.flags & ATA_FLAG_EM) - ahci_reset_em(host); - for (i = 0; i < host->n_ports; i++) { struct ata_port *ap = host->ports[i]; -- cgit v1.2.3 From 41d5ffeb9d12f760722f00ba0756389d32435124 Mon Sep 17 00:00:00 2001 From: Nobuhiro Iwamatsu Date: Thu, 6 Jun 2013 09:43:16 +0000 Subject: sh_eth: remove #ifdef around EDSR and GECMR bit definitions Remove #ifdef around 'enum EDSR_BIT' and 'enum GECMR_BIT', replacing it with the comments on which SoCs these registers exist. SH7757 also has EDSR, so add a comment about it to 'enum EDSR_BIT'. Signed-off-by: Nobuhiro Iwamatsu [Sergei: folded in the former patch #2, updated the changelog, reworded the subject, changing the prefix.] Signed-off-by: Sergei Shtylyov Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/sh_eth.h | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/renesas/sh_eth.h b/drivers/net/ethernet/renesas/sh_eth.h index 1ddc9f235bcb..8daba1d70539 100644 --- a/drivers/net/ethernet/renesas/sh_eth.h +++ b/drivers/net/ethernet/renesas/sh_eth.h @@ -166,19 +166,16 @@ enum { /* * Register's bits */ -#if defined(CONFIG_CPU_SUBTYPE_SH7734) || defined(CONFIG_CPU_SUBTYPE_SH7763) ||\ - defined(CONFIG_ARCH_R8A7740) -/* EDSR */ +/* EDSR : sh7734, sh7757, sh7763, and r8a7740 only */ enum EDSR_BIT { EDSR_ENT = 0x01, EDSR_ENR = 0x02, }; #define EDSR_ENALL (EDSR_ENT|EDSR_ENR) -/* GECMR */ +/* GECMR : sh7734, sh7763 and r8a7740 only */ enum GECMR_BIT { GECMR_10 = 0x0, GECMR_100 = 0x04, GECMR_1000 = 0x01, }; -#endif /* EDMR */ enum DMAC_M_BIT { -- cgit v1.2.3 From ddcd91c6b8ec60a6bd5d3691d2cfaf4447215da1 Mon Sep 17 00:00:00 2001 From: Nobuhiro Iwamatsu Date: Thu, 6 Jun 2013 09:44:27 +0000 Subject: sh_eth: use EDSR_ENALL to set EDSR Use now always available EDSR_ENALL instead of the bare number to set EDSR. Signed-off-by: Nobuhiro Iwamatsu [Sergei: added the changelog, reworded the subject, changing the prefix.] Signed-off-by: Sergei Shtylyov Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/sh_eth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 399a7c968e0d..8b7ff6a03699 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -523,7 +523,7 @@ static int sh_eth_reset(struct net_device *ndev) int ret = 0; if (sh_eth_is_gether(mdp)) { - sh_eth_write(ndev, 0x03, EDSR); + sh_eth_write(ndev, EDSR_ENALL, EDSR); sh_eth_write(ndev, sh_eth_read(ndev, EDMR) | EDMR_SRST_GETHER, EDMR); -- cgit v1.2.3 From 04b0ed2ad49834fe47d3c4171ad189c688a222b9 Mon Sep 17 00:00:00 2001 From: Nobuhiro Iwamatsu Date: Thu, 6 Jun 2013 09:45:25 +0000 Subject: sh_eth: remove duplicate sh_eth_set_duplex() definitions Remove all the duplicate definitions of sh_eth_set_duplex() under different #ifdef's, leaving only one outside the #ifdef's. We have to annotate it with '__maybe_unused' since it's called not from all SoC #ifdef blocks. Signed-off-by: Nobuhiro Iwamatsu [Sergei: annotated sh_eth_set_duplex() as '__maybe_unused', added the changelog, reworded the subject, changing the prefix.] Signed-off-by: Sergei Shtylyov Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/sh_eth.c | 59 +++-------------------------------- 1 file changed, 5 insertions(+), 54 deletions(-) diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 8b7ff6a03699..856ea881b45c 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -341,10 +341,7 @@ static void sh_eth_select_mii(struct net_device *ndev) } #endif -/* There is CPU dependent code */ -#if defined(CONFIG_ARCH_R8A7778) || defined(CONFIG_ARCH_R8A7779) -#define SH_ETH_RESET_DEFAULT 1 -static void sh_eth_set_duplex(struct net_device *ndev) +static void __maybe_unused sh_eth_set_duplex(struct net_device *ndev) { struct sh_eth_private *mdp = netdev_priv(ndev); @@ -354,6 +351,9 @@ static void sh_eth_set_duplex(struct net_device *ndev) sh_eth_write(ndev, sh_eth_read(ndev, ECMR) & ~ECMR_DM, ECMR); } +/* There is CPU dependent code */ +#if defined(CONFIG_ARCH_R8A7778) || defined(CONFIG_ARCH_R8A7779) +#define SH_ETH_RESET_DEFAULT 1 static void sh_eth_set_rate(struct net_device *ndev) { struct sh_eth_private *mdp = netdev_priv(ndev); @@ -391,15 +391,6 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = { }; #elif defined(CONFIG_CPU_SUBTYPE_SH7724) #define SH_ETH_RESET_DEFAULT 1 -static void sh_eth_set_duplex(struct net_device *ndev) -{ - struct sh_eth_private *mdp = netdev_priv(ndev); - - if (mdp->duplex) /* Full */ - sh_eth_write(ndev, sh_eth_read(ndev, ECMR) | ECMR_DM, ECMR); - else /* Half */ - sh_eth_write(ndev, sh_eth_read(ndev, ECMR) & ~ECMR_DM, ECMR); -} static void sh_eth_set_rate(struct net_device *ndev) { @@ -443,16 +434,6 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = { #define SH_ETH_HAS_TSU 1 static int sh_eth_check_reset(struct net_device *ndev); -static void sh_eth_set_duplex(struct net_device *ndev) -{ - struct sh_eth_private *mdp = netdev_priv(ndev); - - if (mdp->duplex) /* Full */ - sh_eth_write(ndev, sh_eth_read(ndev, ECMR) | ECMR_DM, ECMR); - else /* Half */ - sh_eth_write(ndev, sh_eth_read(ndev, ECMR) & ~ECMR_DM, ECMR); -} - static void sh_eth_set_rate(struct net_device *ndev) { struct sh_eth_private *mdp = netdev_priv(ndev); @@ -552,16 +533,6 @@ out: return ret; } -static void sh_eth_set_duplex_giga(struct net_device *ndev) -{ - struct sh_eth_private *mdp = netdev_priv(ndev); - - if (mdp->duplex) /* Full */ - sh_eth_write(ndev, sh_eth_read(ndev, ECMR) | ECMR_DM, ECMR); - else /* Half */ - sh_eth_write(ndev, sh_eth_read(ndev, ECMR) & ~ECMR_DM, ECMR); -} - static void sh_eth_set_rate_giga(struct net_device *ndev) { struct sh_eth_private *mdp = netdev_priv(ndev); @@ -584,7 +555,7 @@ static void sh_eth_set_rate_giga(struct net_device *ndev) /* SH7757(GETHERC) */ static struct sh_eth_cpu_data sh_eth_my_cpu_data_giga = { .chip_reset = sh_eth_chip_reset_giga, - .set_duplex = sh_eth_set_duplex_giga, + .set_duplex = sh_eth_set_duplex, .set_rate = sh_eth_set_rate_giga, .ecsr_value = ECSR_ICD | ECSR_MPD, @@ -634,16 +605,6 @@ static void sh_eth_chip_reset(struct net_device *ndev) mdelay(1); } -static void sh_eth_set_duplex(struct net_device *ndev) -{ - struct sh_eth_private *mdp = netdev_priv(ndev); - - if (mdp->duplex) /* Full */ - sh_eth_write(ndev, sh_eth_read(ndev, ECMR) | ECMR_DM, ECMR); - else /* Half */ - sh_eth_write(ndev, sh_eth_read(ndev, ECMR) & ~ECMR_DM, ECMR); -} - static void sh_eth_set_rate(struct net_device *ndev) { struct sh_eth_private *mdp = netdev_priv(ndev); @@ -771,16 +732,6 @@ out: return ret; } -static void sh_eth_set_duplex(struct net_device *ndev) -{ - struct sh_eth_private *mdp = netdev_priv(ndev); - - if (mdp->duplex) /* Full */ - sh_eth_write(ndev, sh_eth_read(ndev, ECMR) | ECMR_DM, ECMR); - else /* Half */ - sh_eth_write(ndev, sh_eth_read(ndev, ECMR) & ~ECMR_DM, ECMR); -} - static void sh_eth_set_rate(struct net_device *ndev) { struct sh_eth_private *mdp = netdev_priv(ndev); -- cgit v1.2.3 From 9f86134155047720a3685cda21467f68695152d2 Mon Sep 17 00:00:00 2001 From: Nobuhiro Iwamatsu Date: Thu, 6 Jun 2013 09:48:09 +0000 Subject: sh_eth: remove SH_ETH_HAS_TSU Remove SH_ETH_HAS_TSU #define's and #ifdef's. Set three 'struct net_device_ops' methods that depend on the presence of TSU basing on the 'tsu' field of 'struct sh_eth_cpu_data'. Signed-off-by: Nobuhiro Iwamatsu [Sergei: made two method assignments one-liners, added the changelog, reworded the subject, changing the prefix.] Signed-off-by: Sergei Shtylyov Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/sh_eth.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 856ea881b45c..f8d60947fa72 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -431,7 +431,6 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = { }; #elif defined(CONFIG_CPU_SUBTYPE_SH7757) #define SH_ETH_HAS_BOTH_MODULES 1 -#define SH_ETH_HAS_TSU 1 static int sh_eth_check_reset(struct net_device *ndev); static void sh_eth_set_rate(struct net_device *ndev) @@ -592,7 +591,6 @@ static struct sh_eth_cpu_data *sh_eth_get_cpu_data(struct sh_eth_private *mdp) } #elif defined(CONFIG_CPU_SUBTYPE_SH7734) || defined(CONFIG_CPU_SUBTYPE_SH7763) -#define SH_ETH_HAS_TSU 1 static int sh_eth_check_reset(struct net_device *ndev); static void sh_eth_reset_hw_crc(struct net_device *ndev); @@ -693,7 +691,6 @@ static void sh_eth_reset_hw_crc(struct net_device *ndev) } #elif defined(CONFIG_ARCH_R8A7740) -#define SH_ETH_HAS_TSU 1 static int sh_eth_check_reset(struct net_device *ndev); static void sh_eth_chip_reset(struct net_device *ndev) @@ -791,7 +788,6 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = { }; #elif defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712) #define SH_ETH_RESET_DEFAULT 1 -#define SH_ETH_HAS_TSU 1 static struct sh_eth_cpu_data sh_eth_my_cpu_data = { .eesipr_value = DMAC_M_RFRMER | DMAC_M_ECI | 0x003fffff, .tsu = 1, @@ -2105,7 +2101,6 @@ static int sh_eth_do_ioctl(struct net_device *ndev, struct ifreq *rq, return phy_mii_ioctl(phydev, rq, cmd); } -#if defined(SH_ETH_HAS_TSU) /* For TSU_POSTn. Please refer to the manual about this (strange) bitfields */ static void *sh_eth_tsu_get_post_reg_offset(struct sh_eth_private *mdp, int entry) @@ -2448,7 +2443,6 @@ static int sh_eth_vlan_rx_kill_vid(struct net_device *ndev, return 0; } -#endif /* SH_ETH_HAS_TSU */ /* SuperH's TSU register init function */ static void sh_eth_tsu_init(struct sh_eth_private *mdp) @@ -2587,16 +2581,11 @@ static const u16 *sh_eth_get_register_offset(int register_type) return reg_offset; } -static const struct net_device_ops sh_eth_netdev_ops = { +static struct net_device_ops sh_eth_netdev_ops = { .ndo_open = sh_eth_open, .ndo_stop = sh_eth_close, .ndo_start_xmit = sh_eth_start_xmit, .ndo_get_stats = sh_eth_get_stats, -#if defined(SH_ETH_HAS_TSU) - .ndo_set_rx_mode = sh_eth_set_multicast_list, - .ndo_vlan_rx_add_vid = sh_eth_vlan_rx_add_vid, - .ndo_vlan_rx_kill_vid = sh_eth_vlan_rx_kill_vid, -#endif .ndo_tx_timeout = sh_eth_tx_timeout, .ndo_do_ioctl = sh_eth_do_ioctl, .ndo_validate_addr = eth_validate_addr, @@ -2677,6 +2666,13 @@ static int sh_eth_drv_probe(struct platform_device *pdev) sh_eth_set_default_cpu_data(mdp->cd); /* set function */ + if (mdp->cd->tsu) { + sh_eth_netdev_ops.ndo_set_rx_mode = sh_eth_set_multicast_list; + sh_eth_netdev_ops.ndo_vlan_rx_add_vid = sh_eth_vlan_rx_add_vid; + sh_eth_netdev_ops.ndo_vlan_rx_kill_vid = + sh_eth_vlan_rx_kill_vid; + } + ndev->netdev_ops = &sh_eth_netdev_ops; SET_ETHTOOL_OPS(ndev, &sh_eth_ethtool_ops); ndev->watchdog_timeo = TX_TIMEOUT; -- cgit v1.2.3 From 5b3dfd13ae8bdebea67c02612fe282baff850bb0 Mon Sep 17 00:00:00 2001 From: Nobuhiro Iwamatsu Date: Thu, 6 Jun 2013 09:49:30 +0000 Subject: sh_eth: add IRQ flags to 'struct sh_eth_cpu_data' The driver supports some SH and SH-Mobile SOCs. There are SOCs with two or more Ethernet devices, for these we need to pass IRQF_SHARED to request_irq(). Add the 'irq_flags' field to the 'struct sh_eth_cpu_data' instead of #ifdef'fery. Signed-off-by: Nobuhiro Iwamatsu [Sergei: properly aligned request_irq() call continuation line, reworded the changelog, reworded the subject, changing the prefix.] Signed-off-by: Sergei Shtylyov Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/sh_eth.c | 13 +++++-------- drivers/net/ethernet/renesas/sh_eth.h | 1 + 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index f8d60947fa72..d0151d02416f 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -462,6 +462,7 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = { EESR_RFRMER | EESR_TFE | EESR_TDE | EESR_ECI, .tx_error_check = EESR_TWB | EESR_TABT | EESR_TDE | EESR_TFE, + .irq_flags = IRQF_SHARED, .apr = 1, .mpr = 1, .tpauser = 1, @@ -570,6 +571,7 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data_giga = { .fdr_value = 0x0000072f, .rmcr_value = 0x00000001, + .irq_flags = IRQF_SHARED, .apr = 1, .mpr = 1, .tpauser = 1, @@ -650,6 +652,8 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = { #if defined(CONFIG_CPU_SUBTYPE_SH7734) .hw_crc = 1, .select_mii = 1, +#else + .irq_flags = IRQF_SHARED, #endif }; @@ -1908,14 +1912,7 @@ static int sh_eth_open(struct net_device *ndev) pm_runtime_get_sync(&mdp->pdev->dev); ret = request_irq(ndev->irq, sh_eth_interrupt, -#if defined(CONFIG_CPU_SUBTYPE_SH7763) || \ - defined(CONFIG_CPU_SUBTYPE_SH7764) || \ - defined(CONFIG_CPU_SUBTYPE_SH7757) - IRQF_SHARED, -#else - 0, -#endif - ndev->name, ndev); + mdp->cd->irq_flags, ndev->name, ndev); if (ret) { dev_err(&ndev->dev, "Can not assign IRQ number\n"); return ret; diff --git a/drivers/net/ethernet/renesas/sh_eth.h b/drivers/net/ethernet/renesas/sh_eth.h index 8daba1d70539..729e77e66f19 100644 --- a/drivers/net/ethernet/renesas/sh_eth.h +++ b/drivers/net/ethernet/renesas/sh_eth.h @@ -463,6 +463,7 @@ struct sh_eth_cpu_data { unsigned long tx_error_check; /* hardware features */ + unsigned long irq_flags; /* IRQ configuration flags */ unsigned no_psr:1; /* EtherC DO NOT have PSR */ unsigned apr:1; /* EtherC have APR */ unsigned mpr:1; /* EtherC have MPR */ -- cgit v1.2.3 From b7feacf1ee6944fc571802d58dcffaf13b4fb4cc Mon Sep 17 00:00:00 2001 From: Nobuhiro Iwamatsu Date: Thu, 6 Jun 2013 09:50:30 +0000 Subject: sh_eth: remove #ifdef around sh_eth_select_mii() We can simply remove #ifdef'fery around sh_eth_select_mii(). We have to annotate it with '__maybe_unused' then. Signed-off-by: Nobuhiro Iwamatsu [Sergei: added the changelog, reworded the subject, changing the prefix.] Signed-off-by: Sergei Shtylyov Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/sh_eth.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index d0151d02416f..2234a945ae5b 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -313,10 +313,7 @@ static const u16 sh_eth_offset_fast_sh3_sh2[SH_ETH_MAX_REGISTER_OFFSET] = { [TSU_ADRL31] = 0x01fc, }; -#if defined(CONFIG_CPU_SUBTYPE_SH7734) || \ - defined(CONFIG_CPU_SUBTYPE_SH7763) || \ - defined(CONFIG_ARCH_R8A7740) -static void sh_eth_select_mii(struct net_device *ndev) +static void __maybe_unused sh_eth_select_mii(struct net_device *ndev) { u32 value = 0x0; struct sh_eth_private *mdp = netdev_priv(ndev); @@ -339,7 +336,6 @@ static void sh_eth_select_mii(struct net_device *ndev) sh_eth_write(ndev, value, RMII_MII); } -#endif static void __maybe_unused sh_eth_set_duplex(struct net_device *ndev) { -- cgit v1.2.3 From dabdde9ea77d4a77094726c82468a3bd767fb29b Mon Sep 17 00:00:00 2001 From: Nobuhiro Iwamatsu Date: Thu, 6 Jun 2013 09:51:39 +0000 Subject: sh_eth: consolidate sh_eth_reset() This driver has sh_eth_reset() function for each SoC and this function is almost always the same, except for the several a bit different variations for Gigabit Ethernet. Consolidate every variation into a single function -- which allows us to get rid of some more #ifdef'fery. Signed-off-by: Nobuhiro Iwamatsu [Sergei: moved the new sh_eth_reset() and sh_eth_is_gether() up to decrease the patch size, fixed function call continuation lines' indentation, reworded the changelog, reworded the subject, changing the prefix.] Signed-off-by: Sergei Shtylyov Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/sh_eth.c | 176 ++++++++++------------------------ 1 file changed, 51 insertions(+), 125 deletions(-) diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 2234a945ae5b..d190c1e0b0c0 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -313,6 +313,14 @@ static const u16 sh_eth_offset_fast_sh3_sh2[SH_ETH_MAX_REGISTER_OFFSET] = { [TSU_ADRL31] = 0x01fc, }; +static int sh_eth_is_gether(struct sh_eth_private *mdp) +{ + if (mdp->reg_offset == sh_eth_offset_gigabit) + return 1; + else + return 0; +} + static void __maybe_unused sh_eth_select_mii(struct net_device *ndev) { u32 value = 0x0; @@ -349,7 +357,6 @@ static void __maybe_unused sh_eth_set_duplex(struct net_device *ndev) /* There is CPU dependent code */ #if defined(CONFIG_ARCH_R8A7778) || defined(CONFIG_ARCH_R8A7779) -#define SH_ETH_RESET_DEFAULT 1 static void sh_eth_set_rate(struct net_device *ndev) { struct sh_eth_private *mdp = netdev_priv(ndev); @@ -386,7 +393,6 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = { .hw_swap = 1, }; #elif defined(CONFIG_CPU_SUBTYPE_SH7724) -#define SH_ETH_RESET_DEFAULT 1 static void sh_eth_set_rate(struct net_device *ndev) { @@ -427,7 +433,6 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = { }; #elif defined(CONFIG_CPU_SUBTYPE_SH7757) #define SH_ETH_HAS_BOTH_MODULES 1 -static int sh_eth_check_reset(struct net_device *ndev); static void sh_eth_set_rate(struct net_device *ndev) { @@ -493,42 +498,6 @@ static void sh_eth_chip_reset_giga(struct net_device *ndev) } } -static int sh_eth_is_gether(struct sh_eth_private *mdp); -static int sh_eth_reset(struct net_device *ndev) -{ - struct sh_eth_private *mdp = netdev_priv(ndev); - int ret = 0; - - if (sh_eth_is_gether(mdp)) { - sh_eth_write(ndev, EDSR_ENALL, EDSR); - sh_eth_write(ndev, sh_eth_read(ndev, EDMR) | EDMR_SRST_GETHER, - EDMR); - - ret = sh_eth_check_reset(ndev); - if (ret) - goto out; - - /* Table Init */ - sh_eth_write(ndev, 0x0, TDLAR); - sh_eth_write(ndev, 0x0, TDFAR); - sh_eth_write(ndev, 0x0, TDFXR); - sh_eth_write(ndev, 0x0, TDFFR); - sh_eth_write(ndev, 0x0, RDLAR); - sh_eth_write(ndev, 0x0, RDFAR); - sh_eth_write(ndev, 0x0, RDFXR); - sh_eth_write(ndev, 0x0, RDFFR); - } else { - sh_eth_write(ndev, sh_eth_read(ndev, EDMR) | EDMR_SRST_ETHER, - EDMR); - mdelay(3); - sh_eth_write(ndev, sh_eth_read(ndev, EDMR) & ~EDMR_SRST_ETHER, - EDMR); - } - -out: - return ret; -} - static void sh_eth_set_rate_giga(struct net_device *ndev) { struct sh_eth_private *mdp = netdev_priv(ndev); @@ -589,8 +558,6 @@ static struct sh_eth_cpu_data *sh_eth_get_cpu_data(struct sh_eth_private *mdp) } #elif defined(CONFIG_CPU_SUBTYPE_SH7734) || defined(CONFIG_CPU_SUBTYPE_SH7763) -static int sh_eth_check_reset(struct net_device *ndev); -static void sh_eth_reset_hw_crc(struct net_device *ndev); static void sh_eth_chip_reset(struct net_device *ndev) { @@ -653,45 +620,8 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = { #endif }; -static int sh_eth_reset(struct net_device *ndev) -{ - int ret = 0; - - sh_eth_write(ndev, EDSR_ENALL, EDSR); - sh_eth_write(ndev, sh_eth_read(ndev, EDMR) | EDMR_SRST_GETHER, EDMR); - - ret = sh_eth_check_reset(ndev); - if (ret) - goto out; - - /* Table Init */ - sh_eth_write(ndev, 0x0, TDLAR); - sh_eth_write(ndev, 0x0, TDFAR); - sh_eth_write(ndev, 0x0, TDFXR); - sh_eth_write(ndev, 0x0, TDFFR); - sh_eth_write(ndev, 0x0, RDLAR); - sh_eth_write(ndev, 0x0, RDFAR); - sh_eth_write(ndev, 0x0, RDFXR); - sh_eth_write(ndev, 0x0, RDFFR); - - /* Reset HW CRC register */ - sh_eth_reset_hw_crc(ndev); - - /* Select MII mode */ - if (sh_eth_my_cpu_data.select_mii) - sh_eth_select_mii(ndev); -out: - return ret; -} - -static void sh_eth_reset_hw_crc(struct net_device *ndev) -{ - if (sh_eth_my_cpu_data.hw_crc) - sh_eth_write(ndev, 0x0, CSMR); -} #elif defined(CONFIG_ARCH_R8A7740) -static int sh_eth_check_reset(struct net_device *ndev); static void sh_eth_chip_reset(struct net_device *ndev) { @@ -704,31 +634,6 @@ static void sh_eth_chip_reset(struct net_device *ndev) sh_eth_select_mii(ndev); } -static int sh_eth_reset(struct net_device *ndev) -{ - int ret = 0; - - sh_eth_write(ndev, EDSR_ENALL, EDSR); - sh_eth_write(ndev, sh_eth_read(ndev, EDMR) | EDMR_SRST_GETHER, EDMR); - - ret = sh_eth_check_reset(ndev); - if (ret) - goto out; - - /* Table Init */ - sh_eth_write(ndev, 0x0, TDLAR); - sh_eth_write(ndev, 0x0, TDFAR); - sh_eth_write(ndev, 0x0, TDFXR); - sh_eth_write(ndev, 0x0, TDFFR); - sh_eth_write(ndev, 0x0, RDLAR); - sh_eth_write(ndev, 0x0, RDFAR); - sh_eth_write(ndev, 0x0, RDFXR); - sh_eth_write(ndev, 0x0, RDFFR); - -out: - return ret; -} - static void sh_eth_set_rate(struct net_device *ndev) { struct sh_eth_private *mdp = netdev_priv(ndev); @@ -777,7 +682,6 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = { }; #elif defined(CONFIG_CPU_SUBTYPE_SH7619) -#define SH_ETH_RESET_DEFAULT 1 static struct sh_eth_cpu_data sh_eth_my_cpu_data = { .eesipr_value = DMAC_M_RFRMER | DMAC_M_ECI | 0x003fffff, @@ -787,7 +691,6 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = { .hw_swap = 1, }; #elif defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712) -#define SH_ETH_RESET_DEFAULT 1 static struct sh_eth_cpu_data sh_eth_my_cpu_data = { .eesipr_value = DMAC_M_RFRMER | DMAC_M_ECI | 0x003fffff, .tsu = 1, @@ -822,17 +725,6 @@ static void sh_eth_set_default_cpu_data(struct sh_eth_cpu_data *cd) cd->tx_error_check = DEFAULT_TX_ERROR_CHECK; } -#if defined(SH_ETH_RESET_DEFAULT) -/* Chip Reset */ -static int sh_eth_reset(struct net_device *ndev) -{ - sh_eth_write(ndev, sh_eth_read(ndev, EDMR) | EDMR_SRST_ETHER, EDMR); - mdelay(3); - sh_eth_write(ndev, sh_eth_read(ndev, EDMR) & ~EDMR_SRST_ETHER, EDMR); - - return 0; -} -#else static int sh_eth_check_reset(struct net_device *ndev) { int ret = 0; @@ -850,7 +742,49 @@ static int sh_eth_check_reset(struct net_device *ndev) } return ret; } -#endif + +static int sh_eth_reset(struct net_device *ndev) +{ + struct sh_eth_private *mdp = netdev_priv(ndev); + int ret = 0; + + if (sh_eth_is_gether(mdp)) { + sh_eth_write(ndev, EDSR_ENALL, EDSR); + sh_eth_write(ndev, sh_eth_read(ndev, EDMR) | EDMR_SRST_GETHER, + EDMR); + + ret = sh_eth_check_reset(ndev); + if (ret) + goto out; + + /* Table Init */ + sh_eth_write(ndev, 0x0, TDLAR); + sh_eth_write(ndev, 0x0, TDFAR); + sh_eth_write(ndev, 0x0, TDFXR); + sh_eth_write(ndev, 0x0, TDFFR); + sh_eth_write(ndev, 0x0, RDLAR); + sh_eth_write(ndev, 0x0, RDFAR); + sh_eth_write(ndev, 0x0, RDFXR); + sh_eth_write(ndev, 0x0, RDFFR); + + /* Reset HW CRC register */ + if (mdp->cd->hw_crc) + sh_eth_write(ndev, 0x0, CSMR); + + /* Select MII mode */ + if (mdp->cd->select_mii) + sh_eth_select_mii(ndev); + } else { + sh_eth_write(ndev, sh_eth_read(ndev, EDMR) | EDMR_SRST_ETHER, + EDMR); + mdelay(3); + sh_eth_write(ndev, sh_eth_read(ndev, EDMR) & ~EDMR_SRST_ETHER, + EDMR); + } + +out: + return ret; +} #if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE) static void sh_eth_set_receive_align(struct sk_buff *skb) @@ -926,14 +860,6 @@ static void read_mac_address(struct net_device *ndev, unsigned char *mac) } } -static int sh_eth_is_gether(struct sh_eth_private *mdp) -{ - if (mdp->reg_offset == sh_eth_offset_gigabit) - return 1; - else - return 0; -} - static unsigned long sh_eth_get_edtrr_trns(struct sh_eth_private *mdp) { if (sh_eth_is_gether(mdp)) -- cgit v1.2.3 From 540ad1b888ad9564520c1c8c48ad675f76ffce62 Mon Sep 17 00:00:00 2001 From: Nobuhiro Iwamatsu Date: Thu, 6 Jun 2013 09:52:37 +0000 Subject: sh_eth: enclose PM code into #ifdef CONFIG_PM Put '#ifdef CONFIG_PM' around sh_eth_runtime_nop() and 'sh_eth_dev_pm_ops'. Add '#define SH_ETH_PM_OPS' to facilitate initialization of driver's 'pm' field depending on whether CONFIG_PM is enabled. Signed-off-by: Nobuhiro Iwamatsu [Sergei: added the changelog, reworded the subject, changing the prefix.] Signed-off-by: Sergei Shtylyov Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/sh_eth.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index d190c1e0b0c0..0699b1ec7e32 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -2673,6 +2673,7 @@ static int sh_eth_drv_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM static int sh_eth_runtime_nop(struct device *dev) { /* @@ -2686,17 +2687,21 @@ static int sh_eth_runtime_nop(struct device *dev) return 0; } -static struct dev_pm_ops sh_eth_dev_pm_ops = { +static const struct dev_pm_ops sh_eth_dev_pm_ops = { .runtime_suspend = sh_eth_runtime_nop, .runtime_resume = sh_eth_runtime_nop, }; +#define SH_ETH_PM_OPS (&sh_eth_dev_pm_ops) +#else +#define SH_ETH_PM_OPS NULL +#endif static struct platform_driver sh_eth_driver = { .probe = sh_eth_drv_probe, .remove = sh_eth_drv_remove, .driver = { .name = CARDNAME, - .pm = &sh_eth_dev_pm_ops, + .pm = SH_ETH_PM_OPS, }, }; -- cgit v1.2.3 From 1b44791ab4ed27e6fa69f5dfa81b0fd48b1d050d Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Tue, 4 Jun 2013 21:57:11 +0000 Subject: net/macb: increase RX buffer size for GEM Macb Ethernet controller requires a RX buffer of 128 bytes. It is highly sub-optimal for Gigabit-capable GEM that is able to use a bigger DMA buffer. Change this constant and associated macros with data stored in the private structure. RX DMA buffer size has to be multiple of 64 bytes as indicated in DMA Configuration Register specification. Signed-off-by: Nicolas Ferre Signed-off-by: David S. Miller --- drivers/net/ethernet/cadence/macb.c | 45 ++++++++++++++++++++++++++++++------- drivers/net/ethernet/cadence/macb.h | 1 + 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c index 4465e27ba9d1..aab7bb22b1d0 100644 --- a/drivers/net/ethernet/cadence/macb.c +++ b/drivers/net/ethernet/cadence/macb.c @@ -32,7 +32,9 @@ #include "macb.h" -#define RX_BUFFER_SIZE 128 +#define MACB_RX_BUFFER_SIZE 128 +#define GEM_RX_BUFFER_SIZE 2048 +#define RX_BUFFER_MULTIPLE 64 /* bytes */ #define RX_RING_SIZE 512 /* must be power of 2 */ #define RX_RING_BYTES (sizeof(struct macb_dma_desc) * RX_RING_SIZE) @@ -92,7 +94,7 @@ static struct macb_dma_desc *macb_rx_desc(struct macb *bp, unsigned int index) static void *macb_rx_buffer(struct macb *bp, unsigned int index) { - return bp->rx_buffers + RX_BUFFER_SIZE * macb_rx_ring_wrap(index); + return bp->rx_buffers + bp->rx_buffer_size * macb_rx_ring_wrap(index); } void macb_set_hwaddr(struct macb *bp) @@ -575,7 +577,7 @@ static int macb_rx_frame(struct macb *bp, unsigned int first_frag, skb_put(skb, len); for (frag = first_frag; ; frag++) { - unsigned int frag_len = RX_BUFFER_SIZE; + unsigned int frag_len = bp->rx_buffer_size; if (offset + frag_len > len) { BUG_ON(frag != last_frag); @@ -583,7 +585,7 @@ static int macb_rx_frame(struct macb *bp, unsigned int first_frag, } skb_copy_to_linear_data_offset(skb, offset, macb_rx_buffer(bp, frag), frag_len); - offset += RX_BUFFER_SIZE; + offset += bp->rx_buffer_size; desc = macb_rx_desc(bp, frag); desc->addr &= ~MACB_BIT(RX_USED); @@ -870,6 +872,30 @@ static int macb_start_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_OK; } +static void macb_init_rx_buffer_size(struct macb *bp) +{ + if (!macb_is_gem(bp)) { + bp->rx_buffer_size = MACB_RX_BUFFER_SIZE; + } else { + bp->rx_buffer_size = GEM_RX_BUFFER_SIZE; + + if (bp->rx_buffer_size > PAGE_SIZE) { + netdev_warn(bp->dev, + "RX buffer cannot be bigger than PAGE_SIZE, shrinking\n"); + bp->rx_buffer_size = PAGE_SIZE; + } + if (bp->rx_buffer_size % RX_BUFFER_MULTIPLE) { + netdev_warn(bp->dev, + "RX buffer must be multiple of %d bytes, shrinking\n", + RX_BUFFER_MULTIPLE); + bp->rx_buffer_size = + rounddown(bp->rx_buffer_size, RX_BUFFER_MULTIPLE); + } + bp->rx_buffer_size = max(RX_BUFFER_MULTIPLE, GEM_RX_BUFFER_SIZE); + } +} + + static void macb_free_consistent(struct macb *bp) { if (bp->tx_skb) { @@ -888,7 +914,7 @@ static void macb_free_consistent(struct macb *bp) } if (bp->rx_buffers) { dma_free_coherent(&bp->pdev->dev, - RX_RING_SIZE * RX_BUFFER_SIZE, + RX_RING_SIZE * bp->rx_buffer_size, bp->rx_buffers, bp->rx_buffers_dma); bp->rx_buffers = NULL; } @@ -921,7 +947,7 @@ static int macb_alloc_consistent(struct macb *bp) "Allocated TX ring of %d bytes at %08lx (mapped %p)\n", size, (unsigned long)bp->tx_ring_dma, bp->tx_ring); - size = RX_RING_SIZE * RX_BUFFER_SIZE; + size = RX_RING_SIZE * bp->rx_buffer_size; bp->rx_buffers = dma_alloc_coherent(&bp->pdev->dev, size, &bp->rx_buffers_dma, GFP_KERNEL); if (!bp->rx_buffers) @@ -946,7 +972,7 @@ static void macb_init_rings(struct macb *bp) for (i = 0; i < RX_RING_SIZE; i++) { bp->rx_ring[i].addr = addr; bp->rx_ring[i].ctrl = 0; - addr += RX_BUFFER_SIZE; + addr += bp->rx_buffer_size; } bp->rx_ring[RX_RING_SIZE - 1].addr |= MACB_BIT(RX_WRAP); @@ -1056,7 +1082,7 @@ static void macb_configure_dma(struct macb *bp) if (macb_is_gem(bp)) { dmacfg = gem_readl(bp, DMACFG) & ~GEM_BF(RXBS, -1L); - dmacfg |= GEM_BF(RXBS, RX_BUFFER_SIZE / 64); + dmacfg |= GEM_BF(RXBS, bp->rx_buffer_size / RX_BUFFER_MULTIPLE); dmacfg |= GEM_BF(FBLDO, 16); dmacfg |= GEM_BIT(TXPBMS) | GEM_BF(RXBMS, -1L); dmacfg &= ~GEM_BIT(ENDIA); @@ -1244,6 +1270,9 @@ static int macb_open(struct net_device *dev) if (!bp->phy_dev) return -EAGAIN; + /* RX buffers initialization */ + macb_init_rx_buffer_size(bp); + err = macb_alloc_consistent(bp); if (err) { netdev_err(dev, "Unable to allocate DMA memory (error %d)\n", diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index 548c0ecae869..9b5e19f8b61d 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -551,6 +551,7 @@ struct macb { unsigned int rx_tail; struct macb_dma_desc *rx_ring; void *rx_buffers; + size_t rx_buffer_size; unsigned int tx_head, tx_tail; struct macb_dma_desc *tx_ring; -- cgit v1.2.3 From 4df95131ea803bcb94f472d465c73ed57015c470 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Tue, 4 Jun 2013 21:57:12 +0000 Subject: net/macb: change RX path for GEM GEM is able to adapt its DMA buffer size, so change the RX path to take advantage of this possibility and remove all kind of memcpy in this path. This modification introduces function pointers for managing differences between MACB and GEM adapter type. Signed-off-by: Nicolas Ferre Signed-off-by: David S. Miller --- drivers/net/ethernet/cadence/macb.c | 308 ++++++++++++++++++++++++++++++------ drivers/net/ethernet/cadence/macb.h | 13 ++ 2 files changed, 272 insertions(+), 49 deletions(-) diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c index aab7bb22b1d0..f7e21f278e05 100644 --- a/drivers/net/ethernet/cadence/macb.c +++ b/drivers/net/ethernet/cadence/macb.c @@ -33,7 +33,6 @@ #include "macb.h" #define MACB_RX_BUFFER_SIZE 128 -#define GEM_RX_BUFFER_SIZE 2048 #define RX_BUFFER_MULTIPLE 64 /* bytes */ #define RX_RING_SIZE 512 /* must be power of 2 */ #define RX_RING_BYTES (sizeof(struct macb_dma_desc) * RX_RING_SIZE) @@ -530,6 +529,155 @@ static void macb_tx_interrupt(struct macb *bp) netif_wake_queue(bp->dev); } +static void gem_rx_refill(struct macb *bp) +{ + unsigned int entry; + struct sk_buff *skb; + struct macb_dma_desc *desc; + dma_addr_t paddr; + + while (CIRC_SPACE(bp->rx_prepared_head, bp->rx_tail, RX_RING_SIZE) > 0) { + u32 addr, ctrl; + + entry = macb_rx_ring_wrap(bp->rx_prepared_head); + desc = &bp->rx_ring[entry]; + + /* Make hw descriptor updates visible to CPU */ + rmb(); + + addr = desc->addr; + ctrl = desc->ctrl; + bp->rx_prepared_head++; + + if ((addr & MACB_BIT(RX_USED))) + continue; + + if (bp->rx_skbuff[entry] == NULL) { + /* allocate sk_buff for this free entry in ring */ + skb = netdev_alloc_skb(bp->dev, bp->rx_buffer_size); + if (unlikely(skb == NULL)) { + netdev_err(bp->dev, + "Unable to allocate sk_buff\n"); + break; + } + bp->rx_skbuff[entry] = skb; + + /* now fill corresponding descriptor entry */ + paddr = dma_map_single(&bp->pdev->dev, skb->data, + bp->rx_buffer_size, DMA_FROM_DEVICE); + + if (entry == RX_RING_SIZE - 1) + paddr |= MACB_BIT(RX_WRAP); + bp->rx_ring[entry].addr = paddr; + bp->rx_ring[entry].ctrl = 0; + + /* properly align Ethernet header */ + skb_reserve(skb, NET_IP_ALIGN); + } + } + + /* Make descriptor updates visible to hardware */ + wmb(); + + netdev_vdbg(bp->dev, "rx ring: prepared head %d, tail %d\n", + bp->rx_prepared_head, bp->rx_tail); +} + +/* Mark DMA descriptors from begin up to and not including end as unused */ +static void discard_partial_frame(struct macb *bp, unsigned int begin, + unsigned int end) +{ + unsigned int frag; + + for (frag = begin; frag != end; frag++) { + struct macb_dma_desc *desc = macb_rx_desc(bp, frag); + desc->addr &= ~MACB_BIT(RX_USED); + } + + /* Make descriptor updates visible to hardware */ + wmb(); + + /* + * When this happens, the hardware stats registers for + * whatever caused this is updated, so we don't have to record + * anything. + */ +} + +static int gem_rx(struct macb *bp, int budget) +{ + unsigned int len; + unsigned int entry; + struct sk_buff *skb; + struct macb_dma_desc *desc; + int count = 0; + + while (count < budget) { + u32 addr, ctrl; + + entry = macb_rx_ring_wrap(bp->rx_tail); + desc = &bp->rx_ring[entry]; + + /* Make hw descriptor updates visible to CPU */ + rmb(); + + addr = desc->addr; + ctrl = desc->ctrl; + + if (!(addr & MACB_BIT(RX_USED))) + break; + + desc->addr &= ~MACB_BIT(RX_USED); + bp->rx_tail++; + count++; + + if (!(ctrl & MACB_BIT(RX_SOF) && ctrl & MACB_BIT(RX_EOF))) { + netdev_err(bp->dev, + "not whole frame pointed by descriptor\n"); + bp->stats.rx_dropped++; + break; + } + skb = bp->rx_skbuff[entry]; + if (unlikely(!skb)) { + netdev_err(bp->dev, + "inconsistent Rx descriptor chain\n"); + bp->stats.rx_dropped++; + break; + } + /* now everything is ready for receiving packet */ + bp->rx_skbuff[entry] = NULL; + len = MACB_BFEXT(RX_FRMLEN, ctrl); + + netdev_vdbg(bp->dev, "gem_rx %u (len %u)\n", entry, len); + + skb_put(skb, len); + addr = MACB_BF(RX_WADDR, MACB_BFEXT(RX_WADDR, addr)); + dma_unmap_single(&bp->pdev->dev, addr, + len, DMA_FROM_DEVICE); + + skb->protocol = eth_type_trans(skb, bp->dev); + skb_checksum_none_assert(skb); + + bp->stats.rx_packets++; + bp->stats.rx_bytes += skb->len; + +#if defined(DEBUG) && defined(VERBOSE_DEBUG) + netdev_vdbg(bp->dev, "received skb of length %u, csum: %08x\n", + skb->len, skb->csum); + print_hex_dump(KERN_DEBUG, " mac: ", DUMP_PREFIX_ADDRESS, 16, 1, + skb->mac_header, 16, true); + print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_ADDRESS, 16, 1, + skb->data, 32, true); +#endif + + netif_receive_skb(skb); + } + + gem_rx_refill(bp); + + return count; +} + static int macb_rx_frame(struct macb *bp, unsigned int first_frag, unsigned int last_frag) { @@ -608,27 +756,6 @@ static int macb_rx_frame(struct macb *bp, unsigned int first_frag, return 0; } -/* Mark DMA descriptors from begin up to and not including end as unused */ -static void discard_partial_frame(struct macb *bp, unsigned int begin, - unsigned int end) -{ - unsigned int frag; - - for (frag = begin; frag != end; frag++) { - struct macb_dma_desc *desc = macb_rx_desc(bp, frag); - desc->addr &= ~MACB_BIT(RX_USED); - } - - /* Make descriptor updates visible to hardware */ - wmb(); - - /* - * When this happens, the hardware stats registers for - * whatever caused this is updated, so we don't have to record - * anything. - */ -} - static int macb_rx(struct macb *bp, int budget) { int received = 0; @@ -689,7 +816,7 @@ static int macb_poll(struct napi_struct *napi, int budget) netdev_vdbg(bp->dev, "poll: status = %08lx, budget = %d\n", (unsigned long)status, budget); - work_done = macb_rx(bp, budget); + work_done = bp->macbgem_ops.mog_rx(bp, budget); if (work_done < budget) { napi_complete(napi); @@ -872,29 +999,63 @@ static int macb_start_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_OK; } -static void macb_init_rx_buffer_size(struct macb *bp) +static void macb_init_rx_buffer_size(struct macb *bp, size_t size) { if (!macb_is_gem(bp)) { bp->rx_buffer_size = MACB_RX_BUFFER_SIZE; } else { - bp->rx_buffer_size = GEM_RX_BUFFER_SIZE; + bp->rx_buffer_size = size; - if (bp->rx_buffer_size > PAGE_SIZE) { - netdev_warn(bp->dev, - "RX buffer cannot be bigger than PAGE_SIZE, shrinking\n"); - bp->rx_buffer_size = PAGE_SIZE; - } if (bp->rx_buffer_size % RX_BUFFER_MULTIPLE) { - netdev_warn(bp->dev, - "RX buffer must be multiple of %d bytes, shrinking\n", + netdev_dbg(bp->dev, + "RX buffer must be multiple of %d bytes, expanding\n", RX_BUFFER_MULTIPLE); bp->rx_buffer_size = - rounddown(bp->rx_buffer_size, RX_BUFFER_MULTIPLE); + roundup(bp->rx_buffer_size, RX_BUFFER_MULTIPLE); } - bp->rx_buffer_size = max(RX_BUFFER_MULTIPLE, GEM_RX_BUFFER_SIZE); } + + netdev_dbg(bp->dev, "mtu [%u] rx_buffer_size [%Zu]\n", + bp->dev->mtu, bp->rx_buffer_size); } +static void gem_free_rx_buffers(struct macb *bp) +{ + struct sk_buff *skb; + struct macb_dma_desc *desc; + dma_addr_t addr; + int i; + + if (!bp->rx_skbuff) + return; + + for (i = 0; i < RX_RING_SIZE; i++) { + skb = bp->rx_skbuff[i]; + + if (skb == NULL) + continue; + + desc = &bp->rx_ring[i]; + addr = MACB_BF(RX_WADDR, MACB_BFEXT(RX_WADDR, desc->addr)); + dma_unmap_single(&bp->pdev->dev, addr, skb->len, + DMA_FROM_DEVICE); + dev_kfree_skb_any(skb); + skb = NULL; + } + + kfree(bp->rx_skbuff); + bp->rx_skbuff = NULL; +} + +static void macb_free_rx_buffers(struct macb *bp) +{ + if (bp->rx_buffers) { + dma_free_coherent(&bp->pdev->dev, + RX_RING_SIZE * bp->rx_buffer_size, + bp->rx_buffers, bp->rx_buffers_dma); + bp->rx_buffers = NULL; + } +} static void macb_free_consistent(struct macb *bp) { @@ -902,6 +1063,7 @@ static void macb_free_consistent(struct macb *bp) kfree(bp->tx_skb); bp->tx_skb = NULL; } + bp->macbgem_ops.mog_free_rx_buffers(bp); if (bp->rx_ring) { dma_free_coherent(&bp->pdev->dev, RX_RING_BYTES, bp->rx_ring, bp->rx_ring_dma); @@ -912,12 +1074,37 @@ static void macb_free_consistent(struct macb *bp) bp->tx_ring, bp->tx_ring_dma); bp->tx_ring = NULL; } - if (bp->rx_buffers) { - dma_free_coherent(&bp->pdev->dev, - RX_RING_SIZE * bp->rx_buffer_size, - bp->rx_buffers, bp->rx_buffers_dma); - bp->rx_buffers = NULL; - } +} + +static int gem_alloc_rx_buffers(struct macb *bp) +{ + int size; + + size = RX_RING_SIZE * sizeof(struct sk_buff *); + bp->rx_skbuff = kzalloc(size, GFP_KERNEL); + if (!bp->rx_skbuff) + return -ENOMEM; + else + netdev_dbg(bp->dev, + "Allocated %d RX struct sk_buff entries at %p\n", + RX_RING_SIZE, bp->rx_skbuff); + return 0; +} + +static int macb_alloc_rx_buffers(struct macb *bp) +{ + int size; + + size = RX_RING_SIZE * bp->rx_buffer_size; + bp->rx_buffers = dma_alloc_coherent(&bp->pdev->dev, size, + &bp->rx_buffers_dma, GFP_KERNEL); + if (!bp->rx_buffers) + return -ENOMEM; + else + netdev_dbg(bp->dev, + "Allocated RX buffers of %d bytes at %08lx (mapped %p)\n", + size, (unsigned long)bp->rx_buffers_dma, bp->rx_buffers); + return 0; } static int macb_alloc_consistent(struct macb *bp) @@ -947,14 +1134,8 @@ static int macb_alloc_consistent(struct macb *bp) "Allocated TX ring of %d bytes at %08lx (mapped %p)\n", size, (unsigned long)bp->tx_ring_dma, bp->tx_ring); - size = RX_RING_SIZE * bp->rx_buffer_size; - bp->rx_buffers = dma_alloc_coherent(&bp->pdev->dev, size, - &bp->rx_buffers_dma, GFP_KERNEL); - if (!bp->rx_buffers) + if (bp->macbgem_ops.mog_alloc_rx_buffers(bp)) goto out_err; - netdev_dbg(bp->dev, - "Allocated RX buffers of %d bytes at %08lx (mapped %p)\n", - size, (unsigned long)bp->rx_buffers_dma, bp->rx_buffers); return 0; @@ -963,6 +1144,21 @@ out_err: return -ENOMEM; } +static void gem_init_rings(struct macb *bp) +{ + int i; + + for (i = 0; i < TX_RING_SIZE; i++) { + bp->tx_ring[i].addr = 0; + bp->tx_ring[i].ctrl = MACB_BIT(TX_USED); + } + bp->tx_ring[TX_RING_SIZE - 1].ctrl |= MACB_BIT(TX_WRAP); + + bp->rx_tail = bp->rx_prepared_head = bp->tx_head = bp->tx_tail = 0; + + gem_rx_refill(bp); +} + static void macb_init_rings(struct macb *bp) { int i; @@ -1259,6 +1455,7 @@ EXPORT_SYMBOL_GPL(macb_set_rx_mode); static int macb_open(struct net_device *dev) { struct macb *bp = netdev_priv(dev); + size_t bufsz = dev->mtu + ETH_HLEN + ETH_FCS_LEN + NET_IP_ALIGN; int err; netdev_dbg(bp->dev, "open\n"); @@ -1271,7 +1468,7 @@ static int macb_open(struct net_device *dev) return -EAGAIN; /* RX buffers initialization */ - macb_init_rx_buffer_size(bp); + macb_init_rx_buffer_size(bp, bufsz); err = macb_alloc_consistent(bp); if (err) { @@ -1282,7 +1479,7 @@ static int macb_open(struct net_device *dev) napi_enable(&bp->napi); - macb_init_rings(bp); + bp->macbgem_ops.mog_init_rings(bp); macb_init_hw(bp); /* schedule a link state check */ @@ -1601,6 +1798,19 @@ static int __init macb_probe(struct platform_device *pdev) dev->base_addr = regs->start; + /* setup appropriated routines according to adapter type */ + if (macb_is_gem(bp)) { + bp->macbgem_ops.mog_alloc_rx_buffers = gem_alloc_rx_buffers; + bp->macbgem_ops.mog_free_rx_buffers = gem_free_rx_buffers; + bp->macbgem_ops.mog_init_rings = gem_init_rings; + bp->macbgem_ops.mog_rx = gem_rx; + } else { + bp->macbgem_ops.mog_alloc_rx_buffers = macb_alloc_rx_buffers; + bp->macbgem_ops.mog_free_rx_buffers = macb_free_rx_buffers; + bp->macbgem_ops.mog_init_rings = macb_init_rings; + bp->macbgem_ops.mog_rx = macb_rx; + } + /* Set MII management clock divider */ config = macb_mdc_clk_div(bp); config |= macb_dbw(bp); diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index 9b5e19f8b61d..f4076155bed7 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -545,11 +545,22 @@ struct gem_stats { u32 rx_udp_checksum_errors; }; +struct macb; + +struct macb_or_gem_ops { + int (*mog_alloc_rx_buffers)(struct macb *bp); + void (*mog_free_rx_buffers)(struct macb *bp); + void (*mog_init_rings)(struct macb *bp); + int (*mog_rx)(struct macb *bp, int budget); +}; + struct macb { void __iomem *regs; unsigned int rx_tail; + unsigned int rx_prepared_head; struct macb_dma_desc *rx_ring; + struct sk_buff **rx_skbuff; void *rx_buffers; size_t rx_buffer_size; @@ -574,6 +585,8 @@ struct macb { dma_addr_t tx_ring_dma; dma_addr_t rx_buffers_dma; + struct macb_or_gem_ops macbgem_ops; + struct mii_bus *mii_bus; struct phy_device *phy_dev; unsigned int link; -- cgit v1.2.3 From 6c1d7a1c77e98ff53cd58687251c13c5985024ab Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 6 May 2013 13:16:30 +0900 Subject: 88pm860x_battery: Remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Anton Vorontsov --- drivers/power/88pm860x_battery.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/power/88pm860x_battery.c b/drivers/power/88pm860x_battery.c index d338c1c4e8c8..dfcda3a49403 100644 --- a/drivers/power/88pm860x_battery.c +++ b/drivers/power/88pm860x_battery.c @@ -992,7 +992,6 @@ static int pm860x_battery_remove(struct platform_device *pdev) free_irq(info->irq_batt, info); free_irq(info->irq_cc, info); power_supply_unregister(&info->battery); - platform_set_drvdata(pdev, NULL); return 0; } -- cgit v1.2.3 From 71c29b3a89ea49c1f07c85518a9e75bf530673cd Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 6 May 2013 13:17:24 +0900 Subject: 88pm860x_charger: Remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Anton Vorontsov --- drivers/power/88pm860x_charger.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/power/88pm860x_charger.c b/drivers/power/88pm860x_charger.c index 36fb4b5a4b0d..ffff66b1c1aa 100644 --- a/drivers/power/88pm860x_charger.c +++ b/drivers/power/88pm860x_charger.c @@ -722,7 +722,6 @@ static int pm860x_charger_remove(struct platform_device *pdev) struct pm860x_charger_info *info = platform_get_drvdata(pdev); int i; - platform_set_drvdata(pdev, NULL); power_supply_unregister(&info->usb); free_irq(info->irq[0], info); for (i = 0; i < info->irq_nums; i++) -- cgit v1.2.3 From ddeeb8f973bbe3bebac78e78c234ff18bfb00bf0 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 6 May 2013 13:20:15 +0900 Subject: ab8500_bm: Remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Acked-by: Lee Jones Signed-off-by: Anton Vorontsov --- drivers/power/ab8500_btemp.c | 1 - drivers/power/ab8500_charger.c | 2 -- drivers/power/ab8500_fg.c | 1 - drivers/power/abx500_chargalg.c | 1 - 4 files changed, 5 deletions(-) diff --git a/drivers/power/ab8500_btemp.c b/drivers/power/ab8500_btemp.c index d412d34bf3df..7f9a4547dccd 100644 --- a/drivers/power/ab8500_btemp.c +++ b/drivers/power/ab8500_btemp.c @@ -1045,7 +1045,6 @@ static int ab8500_btemp_remove(struct platform_device *pdev) flush_scheduled_work(); power_supply_unregister(&di->btemp_psy); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c index a558318b169c..f098fdafee9f 100644 --- a/drivers/power/ab8500_charger.c +++ b/drivers/power/ab8500_charger.c @@ -3425,8 +3425,6 @@ static int ab8500_charger_remove(struct platform_device *pdev) if (di->ac_chg.enabled && !di->ac_chg.external) power_supply_unregister(&di->ac_chg.psy); - platform_set_drvdata(pdev, NULL); - return 0; } diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c index c5391f5c372d..1263638b102d 100644 --- a/drivers/power/ab8500_fg.c +++ b/drivers/power/ab8500_fg.c @@ -3070,7 +3070,6 @@ static int ab8500_fg_remove(struct platform_device *pdev) flush_scheduled_work(); ab8500_fg_sysfs_psy_remove_attrs(di->fg_psy.dev); power_supply_unregister(&di->fg_psy); - platform_set_drvdata(pdev, NULL); return ret; } diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c index 9863e423602c..6d2723664a01 100644 --- a/drivers/power/abx500_chargalg.c +++ b/drivers/power/abx500_chargalg.c @@ -2035,7 +2035,6 @@ static int abx500_chargalg_remove(struct platform_device *pdev) destroy_workqueue(di->chargalg_wq); power_supply_unregister(&di->chargalg_psy); - platform_set_drvdata(pdev, NULL); return 0; } -- cgit v1.2.3 From 30cf74b94166b2cdeaa8e0439161e339f17e1012 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 6 May 2013 13:21:16 +0900 Subject: bq27x00_battery: Remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Anton Vorontsov --- drivers/power/bq27x00_battery.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index 26037ca7efb4..b309713b63bc 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -966,7 +966,6 @@ static int bq27000_battery_probe(struct platform_device *pdev) return 0; err_free: - platform_set_drvdata(pdev, NULL); kfree(di); return ret; @@ -978,7 +977,6 @@ static int bq27000_battery_remove(struct platform_device *pdev) bq27x00_powersupply_unregister(di); - platform_set_drvdata(pdev, NULL); kfree(di); return 0; -- cgit v1.2.3 From 6fe6a40a83716166ac55e7f60053f59563d53878 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 6 May 2013 13:22:00 +0900 Subject: gpio-charger: Remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Anton Vorontsov --- drivers/power/gpio-charger.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/power/gpio-charger.c b/drivers/power/gpio-charger.c index e9883eeeee76..4e858a23568f 100644 --- a/drivers/power/gpio-charger.c +++ b/drivers/power/gpio-charger.c @@ -155,8 +155,6 @@ static int gpio_charger_remove(struct platform_device *pdev) gpio_free(gpio_charger->pdata->gpio); - platform_set_drvdata(pdev, NULL); - return 0; } -- cgit v1.2.3 From 8ff3fb2902dd5d3c953f907906a413b18d9d150a Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 6 May 2013 13:22:41 +0900 Subject: jz4740-battery: Remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Anton Vorontsov --- drivers/power/jz4740-battery.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/power/jz4740-battery.c b/drivers/power/jz4740-battery.c index c675553d4647..d9686aa9270a 100644 --- a/drivers/power/jz4740-battery.c +++ b/drivers/power/jz4740-battery.c @@ -292,7 +292,7 @@ static int jz_battery_probe(struct platform_device *pdev) jz_battery); if (ret) { dev_err(&pdev->dev, "Failed to request irq %d\n", ret); - goto err; + return ret; } disable_irq(jz_battery->irq); @@ -349,8 +349,6 @@ err_free_gpio: gpio_free(jz_battery->pdata->gpio_charge); err_free_irq: free_irq(jz_battery->irq, jz_battery); -err: - platform_set_drvdata(pdev, NULL); return ret; } -- cgit v1.2.3 From f70d739ada3298fd613d738c0a1c8b10cef63bdf Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 6 May 2013 13:23:45 +0900 Subject: rx51_battery: Remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Anton Vorontsov --- drivers/power/rx51_battery.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/power/rx51_battery.c b/drivers/power/rx51_battery.c index cbde1d6d3228..8a6288d87056 100644 --- a/drivers/power/rx51_battery.c +++ b/drivers/power/rx51_battery.c @@ -216,10 +216,8 @@ static int rx51_battery_probe(struct platform_device *pdev) di->bat.get_property = rx51_battery_get_property; ret = power_supply_register(di->dev, &di->bat); - if (ret) { - platform_set_drvdata(pdev, NULL); + if (ret) return ret; - } return 0; } @@ -229,7 +227,6 @@ static int rx51_battery_remove(struct platform_device *pdev) struct rx51_device_info *di = platform_get_drvdata(pdev); power_supply_unregister(&di->bat); - platform_set_drvdata(pdev, NULL); return 0; } -- cgit v1.2.3 From 0f1e0169e08e1645a8406ab84286ebaf19c5cce5 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 6 May 2013 13:24:33 +0900 Subject: twl4030_charger: Remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Anton Vorontsov --- drivers/power/twl4030_charger.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index bed458172dd2..be98e70380f9 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -594,7 +594,6 @@ fail_chg_irq: fail_register_usb: power_supply_unregister(&bci->ac); fail_register_ac: - platform_set_drvdata(pdev, NULL); kfree(bci); return ret; @@ -622,7 +621,6 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev) 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; -- cgit v1.2.3 From 17b4565b308ed31fa20b59842c75e685a101dc8a Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Fri, 10 May 2013 07:17:51 +0000 Subject: lp8727_charger: Support the device tree feature The interrupt and charging parameters are configurable in the device tree structure. In the board test, a GPIO is used for handling LP8727 interrupts. The device tree binding documentation is added also. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Anton Vorontsov --- .../bindings/power_supply/lp8727_charger.txt | 44 ++++++++++++++ drivers/power/lp8727_charger.c | 68 ++++++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 Documentation/devicetree/bindings/power_supply/lp8727_charger.txt diff --git a/Documentation/devicetree/bindings/power_supply/lp8727_charger.txt b/Documentation/devicetree/bindings/power_supply/lp8727_charger.txt new file mode 100644 index 000000000000..2246bc5c874b --- /dev/null +++ b/Documentation/devicetree/bindings/power_supply/lp8727_charger.txt @@ -0,0 +1,44 @@ +Binding for TI/National Semiconductor LP8727 Charger + +Required properties: +- compatible: "ti,lp8727" +- reg: I2C slave address 27h + +Optional properties: +- interrupt-parent: interrupt controller node (see interrupt binding[0]) +- interrupts: interrupt specifier (see interrupt binding[0]) +- debounce-ms: interrupt debounce time. (u32) + +AC and USB charging parameters +- charger-type: "ac" or "usb" (string) +- eoc-level: value of 'enum lp8727_eoc_level' (u8) +- charging-current: value of 'enum lp8727_ichg' (u8) + +[0]: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt + +Example) + +lp8727@27 { + compatible = "ti,lp8727"; + reg = <0x27>; + + /* GPIO 134 is used for LP8728 interrupt pin */ + interrupt-parent = <&gpio5>; /* base = 128 */ + interrupts = <6 0x2>; /* offset = 6, falling edge type */ + + debounce-ms = <300>; + + /* AC charger: 5% EOC and 500mA charging current */ + ac { + charger-type = "ac"; + eoc-level = /bits/ 8 <0>; + charging-current = /bits/ 8 <4>; + }; + + /* USB charger: 10% EOC and 400mA charging current */ + usb { + charger-type = "usb"; + eoc-level = /bits/ 8 <1>; + charging-current = /bits/ 8 <2>; + }; +}; diff --git a/drivers/power/lp8727_charger.c b/drivers/power/lp8727_charger.c index 5ef41b819172..32de636dcd73 100644 --- a/drivers/power/lp8727_charger.c +++ b/drivers/power/lp8727_charger.c @@ -16,6 +16,7 @@ #include #include #include +#include #define LP8788_NUM_INTREGS 2 #define DEFAULT_DEBOUNCE_MSEC 270 @@ -481,6 +482,60 @@ static void lp8727_unregister_psy(struct lp8727_chg *pchg) power_supply_unregister(&psy->batt); } +#ifdef CONFIG_OF +static struct lp8727_chg_param +*lp8727_parse_charge_pdata(struct device *dev, struct device_node *np) +{ + struct lp8727_chg_param *param; + + param = devm_kzalloc(dev, sizeof(*param), GFP_KERNEL); + if (!param) + goto out; + + of_property_read_u8(np, "eoc-level", (u8 *)¶m->eoc_level); + of_property_read_u8(np, "charging-current", (u8 *)¶m->ichg); +out: + return param; +} + +static int lp8727_parse_dt(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct device_node *child; + struct lp8727_platform_data *pdata; + const char *type; + + /* If charging parameter is not defined, just skip parsing the dt */ + if (of_get_child_count(np) == 0) + goto out; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + of_property_read_u32(np, "debounce-ms", &pdata->debounce_msec); + + for_each_child_of_node(np, child) { + of_property_read_string(child, "charger-type", &type); + + if (!strcmp(type, "ac")) + pdata->ac = lp8727_parse_charge_pdata(dev, child); + + if (!strcmp(type, "usb")) + pdata->usb = lp8727_parse_charge_pdata(dev, child); + } + + dev->platform_data = pdata; +out: + return 0; +} +#else +static int lp8727_parse_dt(struct device *dev) +{ + return 0; +} +#endif + static int lp8727_probe(struct i2c_client *cl, const struct i2c_device_id *id) { struct lp8727_chg *pchg; @@ -489,6 +544,12 @@ static int lp8727_probe(struct i2c_client *cl, const struct i2c_device_id *id) if (!i2c_check_functionality(cl->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) return -EIO; + if (cl->dev.of_node) { + ret = lp8727_parse_dt(&cl->dev); + if (ret) + return ret; + } + pchg = devm_kzalloc(&cl->dev, sizeof(*pchg), GFP_KERNEL); if (!pchg) return -ENOMEM; @@ -531,6 +592,12 @@ static int lp8727_remove(struct i2c_client *cl) return 0; } +static const struct of_device_id lp8727_dt_ids[] = { + { .compatible = "ti,lp8727", }, + { } +}; +MODULE_DEVICE_TABLE(of, lp8727_dt_ids); + static const struct i2c_device_id lp8727_ids[] = { {"lp8727", 0}, { } @@ -540,6 +607,7 @@ MODULE_DEVICE_TABLE(i2c, lp8727_ids); static struct i2c_driver lp8727_driver = { .driver = { .name = "lp8727", + .of_match_table = of_match_ptr(lp8727_dt_ids), }, .probe = lp8727_probe, .remove = lp8727_remove, -- cgit v1.2.3 From df311333a8c13f3ffe2537a4ff507887208cb863 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 16 May 2013 17:31:00 +0800 Subject: pm2301_charger: Fix NULL pointer dereference Add checking pl_data in probe, this prevent possible NULL pointer dereference. Also fix NULL pointer deference in dev_err when allocate memory for pm2 fails. Signed-off-by: Axel Lin Acked-by: Linus Walleij Signed-off-by: Anton Vorontsov --- drivers/power/pm2301_charger.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/power/pm2301_charger.c b/drivers/power/pm2301_charger.c index fef56e2041b3..bb346becd7b4 100644 --- a/drivers/power/pm2301_charger.c +++ b/drivers/power/pm2301_charger.c @@ -1007,9 +1007,14 @@ static int pm2xxx_wall_charger_probe(struct i2c_client *i2c_client, u8 val; int i; + if (!pl_data) { + dev_err(&i2c_client->dev, "No platform data supplied\n"); + return -EINVAL; + } + pm2 = kzalloc(sizeof(struct pm2xxx_charger), GFP_KERNEL); if (!pm2) { - dev_err(pm2->dev, "pm2xxx_charger allocation failed\n"); + dev_err(&i2c_client->dev, "pm2xxx_charger allocation failed\n"); return -ENOMEM; } -- cgit v1.2.3 From c509a62c9469e2787bcacc7aba454d555732b0b6 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 16 May 2013 21:17:04 +0800 Subject: pm2301_charger: Return error if create_singlethread_workqueue fails Return error instead of 0 if create_singlethread_workqueue call fails. Signed-off-by: Axel Lin Acked-by: Linus Walleij Signed-off-by: Anton Vorontsov --- drivers/power/pm2301_charger.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/power/pm2301_charger.c b/drivers/power/pm2301_charger.c index bb346becd7b4..1c0bfcbae062 100644 --- a/drivers/power/pm2301_charger.c +++ b/drivers/power/pm2301_charger.c @@ -1075,9 +1075,9 @@ static int pm2xxx_wall_charger_probe(struct i2c_client *i2c_client, pm2->ac_chg.external = true; /* Create a work queue for the charger */ - pm2->charger_wq = - create_singlethread_workqueue("pm2xxx_charger_wq"); + pm2->charger_wq = create_singlethread_workqueue("pm2xxx_charger_wq"); if (pm2->charger_wq == NULL) { + ret = -ENOMEM; dev_err(pm2->dev, "failed to create work queue\n"); goto free_device_info; } -- cgit v1.2.3 From e08358792422ef0b78d7c81f333fc0803fb41cd4 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 23 May 2013 19:34:39 +0900 Subject: power: Use platform_{get,set}_drvdata() Use the wrapper functions for getting and setting the driver data using platform_device instead of using dev_{get,set}_drvdata() with &pdev->dev, so we can directly pass a struct platform_device. Signed-off-by: Jingoo Han Signed-off-by: Anton Vorontsov --- drivers/power/intel_mid_battery.c | 2 +- drivers/power/tps65090-charger.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/power/intel_mid_battery.c b/drivers/power/intel_mid_battery.c index 18d136b443ee..4520811168ad 100644 --- a/drivers/power/intel_mid_battery.c +++ b/drivers/power/intel_mid_battery.c @@ -756,7 +756,7 @@ static int platform_pmic_battery_probe(struct platform_device *pdev) static int platform_pmic_battery_remove(struct platform_device *pdev) { - struct pmic_power_module_info *pbi = dev_get_drvdata(&pdev->dev); + struct pmic_power_module_info *pbi = platform_get_drvdata(pdev); free_irq(pbi->irq, pbi); cancel_delayed_work_sync(&pbi->monitor_battery); diff --git a/drivers/power/tps65090-charger.c b/drivers/power/tps65090-charger.c index 9fbca310a2ad..77ab8561fa34 100644 --- a/drivers/power/tps65090-charger.c +++ b/drivers/power/tps65090-charger.c @@ -218,7 +218,7 @@ static int tps65090_charger_probe(struct platform_device *pdev) return -ENOMEM; } - dev_set_drvdata(&pdev->dev, cdata); + platform_set_drvdata(pdev, cdata); cdata->dev = &pdev->dev; cdata->pdata = pdata; @@ -291,7 +291,7 @@ fail_unregister_supply: static int tps65090_charger_remove(struct platform_device *pdev) { - struct tps65090_charger *cdata = dev_get_drvdata(&pdev->dev); + struct tps65090_charger *cdata = platform_get_drvdata(pdev); devm_free_irq(cdata->dev, cdata->irq, cdata); power_supply_unregister(&cdata->ac); -- cgit v1.2.3 From d211c6e82435dfa4ae9f4d80bb2ee75930bde5d4 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sat, 25 May 2013 01:41:44 +0800 Subject: generic-adc-battery: Fix checking if none of the channels are supported If none of the channels are supported, index is 0. Also ensure to return error code instead of 0 in goto second_mem_fail path. Signed-off-by: Axel Lin Acked-by: anish kumar Signed-off-by: Anton Vorontsov --- drivers/power/generic-adc-battery.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/power/generic-adc-battery.c b/drivers/power/generic-adc-battery.c index 8cb5d7f67ace..59a1421f9288 100644 --- a/drivers/power/generic-adc-battery.c +++ b/drivers/power/generic-adc-battery.c @@ -299,8 +299,10 @@ static int gab_probe(struct platform_device *pdev) } /* none of the channels are supported so let's bail out */ - if (index == ARRAY_SIZE(gab_chan_name)) + if (index == 0) { + ret = -ENODEV; goto second_mem_fail; + } /* * Total number of properties is equal to static properties -- cgit v1.2.3 From 4b43eb67ae246b9a846c46e5ff96ea0212ae7a51 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 3 Jun 2013 18:05:05 +0900 Subject: power_supply: Replace strict_strtoul() with kstrtoul() The usage of strict_strtoul() is not preferred, because strict_strtoul() is obsolete. Thus, kstrtoul() should be used. Signed-off-by: Jingoo Han Reviewed-by: Andy Shevchenko Signed-off-by: Anton Vorontsov --- drivers/power/ab8500_fg.c | 6 +++--- drivers/power/pcf50633-charger.c | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c index 1263638b102d..754970717c31 100644 --- a/drivers/power/ab8500_fg.c +++ b/drivers/power/ab8500_fg.c @@ -2465,9 +2465,9 @@ static ssize_t charge_full_store(struct ab8500_fg *di, const char *buf, size_t count) { unsigned long charge_full; - ssize_t ret = -EINVAL; + ssize_t ret; - ret = strict_strtoul(buf, 10, &charge_full); + ret = kstrtoul(buf, 10, &charge_full); dev_dbg(di->dev, "Ret %zd charge_full %lu", ret, charge_full); @@ -2489,7 +2489,7 @@ static ssize_t charge_now_store(struct ab8500_fg *di, const char *buf, unsigned long charge_now; ssize_t ret; - ret = strict_strtoul(buf, 10, &charge_now); + ret = kstrtoul(buf, 10, &charge_now); dev_dbg(di->dev, "Ret %zd charge_now %lu was %d", ret, charge_now, di->bat_cap.prev_mah); diff --git a/drivers/power/pcf50633-charger.c b/drivers/power/pcf50633-charger.c index 17fd77f24b2a..771c4f0fb8ac 100644 --- a/drivers/power/pcf50633-charger.c +++ b/drivers/power/pcf50633-charger.c @@ -191,9 +191,9 @@ static ssize_t set_usblim(struct device *dev, unsigned long ma; int ret; - ret = strict_strtoul(buf, 10, &ma); + ret = kstrtoul(buf, 10, &ma); if (ret) - return -EINVAL; + return ret; pcf50633_mbc_usb_curlim_set(mbc->pcf, ma); @@ -228,9 +228,9 @@ static ssize_t set_chglim(struct device *dev, if (!mbc->pcf->pdata->charger_reference_current_ma) return -ENODEV; - ret = strict_strtoul(buf, 10, &ma); + ret = kstrtoul(buf, 10, &ma); if (ret) - return -EINVAL; + return ret; mbcc5 = (ma << 8) / mbc->pcf->pdata->charger_reference_current_ma; if (mbcc5 > 255) -- cgit v1.2.3 From 3594f4c0d7bc51e3a7e6d73c44e368ae079e42f3 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 6 Jun 2013 13:52:21 -0700 Subject: charger-manager: Ensure event is not used as format string The exposed interface for cm_notify_event() could result in the event msg string being parsed as a format string. Make sure it is only used as a literal string. Signed-off-by: Kees Cook Cc: stable@vger.kernel.org Cc: Anton Vorontsov Cc: David Woodhouse Signed-off-by: Anton Vorontsov --- drivers/power/charger-manager.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c index fefc39fe42be..98de1ddce458 100644 --- a/drivers/power/charger-manager.c +++ b/drivers/power/charger-manager.c @@ -450,7 +450,7 @@ static void uevent_notify(struct charger_manager *cm, const char *event) strncpy(env_str, event, UEVENT_BUF_SIZE); kobject_uevent(&cm->dev->kobj, KOBJ_CHANGE); - dev_info(cm->dev, event); + dev_info(cm->dev, "%s", event); } /** -- cgit v1.2.3 From 1237e598a94b5a44a0162a4f4534d18ef8a81a7d Mon Sep 17 00:00:00 2001 From: Philippe Begnic Date: Mon, 27 May 2013 14:41:29 +0200 Subject: clk: ux500: Pass clock base adresses in initcall for u8540 and u9540 Align on u8500 version, pass clock base address in clk_init functions for u8540 and u9540. Signed-off-by: Linus Walleij Signed-off-by: Philippe Begnic Reviewed-by: Ulf Hansson Signed-off-by: Mike Turquette --- arch/arm/mach-ux500/cpu.c | 6 ++++-- drivers/clk/ux500/u8540_clk.c | 4 ++-- drivers/clk/ux500/u9540_clk.c | 4 ++-- include/linux/platform_data/clk-ux500.h | 6 ++++-- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/arch/arm/mach-ux500/cpu.c b/arch/arm/mach-ux500/cpu.c index b6145ea51641..e6fb0239151b 100644 --- a/arch/arm/mach-ux500/cpu.c +++ b/arch/arm/mach-ux500/cpu.c @@ -76,13 +76,15 @@ void __init ux500_init_irq(void) } else if (cpu_is_u9540()) { prcmu_early_init(U8500_PRCMU_BASE, SZ_8K - 1); ux500_pm_init(U8500_PRCMU_BASE, SZ_8K - 1); - u8500_clk_init(U8500_CLKRST1_BASE, U8500_CLKRST2_BASE, + u9540_clk_init(U8500_CLKRST1_BASE, U8500_CLKRST2_BASE, U8500_CLKRST3_BASE, U8500_CLKRST5_BASE, U8500_CLKRST6_BASE); } else if (cpu_is_u8540()) { prcmu_early_init(U8500_PRCMU_BASE, SZ_8K + SZ_4K - 1); ux500_pm_init(U8500_PRCMU_BASE, SZ_8K + SZ_4K - 1); - u8540_clk_init(); + u8540_clk_init(U8500_CLKRST1_BASE, U8500_CLKRST2_BASE, + U8500_CLKRST3_BASE, U8500_CLKRST5_BASE, + U8500_CLKRST6_BASE); } } diff --git a/drivers/clk/ux500/u8540_clk.c b/drivers/clk/ux500/u8540_clk.c index 10adfd2ead21..90f3c88694b9 100644 --- a/drivers/clk/ux500/u8540_clk.c +++ b/drivers/clk/ux500/u8540_clk.c @@ -12,10 +12,10 @@ #include #include #include - #include "clk.h" -void u8540_clk_init(void) +void u8540_clk_init(u32 clkrst1_base, u32 clkrst2_base, u32 clkrst3_base, + u32 clkrst5_base, u32 clkrst6_base) { /* register clocks here */ } diff --git a/drivers/clk/ux500/u9540_clk.c b/drivers/clk/ux500/u9540_clk.c index dbc0191e16c8..44794782e7e0 100644 --- a/drivers/clk/ux500/u9540_clk.c +++ b/drivers/clk/ux500/u9540_clk.c @@ -12,10 +12,10 @@ #include #include #include - #include "clk.h" -void u9540_clk_init(void) +void u9540_clk_init(u32 clkrst1_base, u32 clkrst2_base, u32 clkrst3_base, + u32 clkrst5_base, u32 clkrst6_base) { /* register clocks here */ } diff --git a/include/linux/platform_data/clk-ux500.h b/include/linux/platform_data/clk-ux500.h index 320d9c39ea0a..9d98f3aaa16c 100644 --- a/include/linux/platform_data/clk-ux500.h +++ b/include/linux/platform_data/clk-ux500.h @@ -12,7 +12,9 @@ void u8500_clk_init(u32 clkrst1_base, u32 clkrst2_base, u32 clkrst3_base, u32 clkrst5_base, u32 clkrst6_base); -void u9540_clk_init(void); -void u8540_clk_init(void); +void u9540_clk_init(u32 clkrst1_base, u32 clkrst2_base, u32 clkrst3_base, + u32 clkrst5_base, u32 clkrst6_base); +void u8540_clk_init(u32 clkrst1_base, u32 clkrst2_base, u32 clkrst3_base, + u32 clkrst5_base, u32 clkrst6_base); #endif /* __CLK_UX500_H */ -- cgit v1.2.3 From 852bbba96773777efc2b932f2c5939c6fbf252da Mon Sep 17 00:00:00 2001 From: Philippe Begnic Date: Mon, 27 May 2013 14:41:30 +0200 Subject: mfd: db8500: Update register definition for u8540 clock PRCMU and ab8500 registers updated for u8540 Signed-off-by: Philippe Begnic Acked-by: Lee Jones Signed-off-by: Mike Turquette --- include/linux/mfd/abx500/ab8500-sysctrl.h | 4 ++-- include/linux/mfd/dbx500-prcmu.h | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/include/linux/mfd/abx500/ab8500-sysctrl.h b/include/linux/mfd/abx500/ab8500-sysctrl.h index 990bc93f46e1..adba89d9c660 100644 --- a/include/linux/mfd/abx500/ab8500-sysctrl.h +++ b/include/linux/mfd/abx500/ab8500-sysctrl.h @@ -278,8 +278,8 @@ struct ab8500_sysctrl_platform_data { #define AB9540_SYSCLK12CONFCTRL_PLL26TO38ENA BIT(0) #define AB9540_SYSCLK12CONFCTRL_SYSCLK12USBMUXSEL BIT(1) -#define AB9540_SYSCLK12CONFCTRL_INT384MHZMUXSEL_MASK 0x0C -#define AB9540_SYSCLK12CONFCTRL_INT384MHZMUXSEL_SHIFT 2 +#define AB9540_SYSCLK12CONFCTRL_INT384MHZMUXSEL0 BIT(2) +#define AB9540_SYSCLK12CONFCTRL_INT384MHZMUXSEL1 BIT(3) #define AB9540_SYSCLK12CONFCTRL_SYSCLK12BUFMUX BIT(4) #define AB9540_SYSCLK12CONFCTRL_SYSCLK12PLLMUX BIT(5) #define AB9540_SYSCLK12CONFCTRL_SYSCLK2MUXVALID BIT(6) diff --git a/include/linux/mfd/dbx500-prcmu.h b/include/linux/mfd/dbx500-prcmu.h index 689e6a0d9c99..d0ba355cc55f 100644 --- a/include/linux/mfd/dbx500-prcmu.h +++ b/include/linux/mfd/dbx500-prcmu.h @@ -134,6 +134,10 @@ enum prcmu_clock { PRCMU_SIACLK, PRCMU_SVACLK, PRCMU_ACLK, + PRCMU_HVACLK, /* Ux540 only */ + PRCMU_G1CLK, /* Ux540 only */ + PRCMU_SDMMCHCLK, + PRCMU_CAMCLK, PRCMU_NUM_REG_CLOCKS, PRCMU_SYSCLK = PRCMU_NUM_REG_CLOCKS, PRCMU_CDCLK, @@ -148,6 +152,13 @@ enum prcmu_clock { PRCMU_DSI0ESCCLK, PRCMU_DSI1ESCCLK, PRCMU_DSI2ESCCLK, + /* LCD DSI PLL - Ux540 only */ + PRCMU_PLLDSI_LCD, + PRCMU_DSI0CLK_LCD, + PRCMU_DSI1CLK_LCD, + PRCMU_DSI0ESCCLK_LCD, + PRCMU_DSI1ESCCLK_LCD, + PRCMU_DSI2ESCCLK_LCD, }; /** -- cgit v1.2.3 From 54e300339cce6d4be665e6bbd736123dd0f15888 Mon Sep 17 00:00:00 2001 From: Philippe Begnic Date: Mon, 27 May 2013 14:41:31 +0200 Subject: mfd: db8500: Update BML clock register for db8580 BML clock register address in DB8580 has changed.Defined a new address under different name for DB8580. Signed-off-by: Philippe Begnic Acked-by: Lee Jones Signed-off-by: Mike Turquette --- drivers/mfd/db8500-prcmu.c | 1 + drivers/mfd/dbx500-prcmu-regs.h | 1 + include/linux/mfd/dbx500-prcmu.h | 1 + 3 files changed, 3 insertions(+) diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 66f80973596b..a292a1deff9a 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -480,6 +480,7 @@ struct clk_mgt clk_mgt[PRCMU_NUM_REG_CLOCKS] = { CLK_MGT_ENTRY(PER6CLK, PLL_DIV, true), CLK_MGT_ENTRY(PER7CLK, PLL_DIV, true), CLK_MGT_ENTRY(LCDCLK, PLL_FIX, true), + CLK_MGT_ENTRY(BML8580CLK, PLL_DIV, true), CLK_MGT_ENTRY(BMLCLK, PLL_DIV, true), CLK_MGT_ENTRY(HSITXCLK, PLL_DIV, true), CLK_MGT_ENTRY(HSIRXCLK, PLL_DIV, true), diff --git a/drivers/mfd/dbx500-prcmu-regs.h b/drivers/mfd/dbx500-prcmu-regs.h index d14836ed2114..ca355dd423a6 100644 --- a/drivers/mfd/dbx500-prcmu-regs.h +++ b/drivers/mfd/dbx500-prcmu-regs.h @@ -32,6 +32,7 @@ #define PRCM_PER7CLK_MGT (0x040) #define PRCM_LCDCLK_MGT (0x044) #define PRCM_BMLCLK_MGT (0x04C) +#define PRCM_BML8580CLK_MGT (0x108) #define PRCM_HSITXCLK_MGT (0x050) #define PRCM_HSIRXCLK_MGT (0x054) #define PRCM_HDMICLK_MGT (0x058) diff --git a/include/linux/mfd/dbx500-prcmu.h b/include/linux/mfd/dbx500-prcmu.h index d0ba355cc55f..ca0790fba2f5 100644 --- a/include/linux/mfd/dbx500-prcmu.h +++ b/include/linux/mfd/dbx500-prcmu.h @@ -138,6 +138,7 @@ enum prcmu_clock { PRCMU_G1CLK, /* Ux540 only */ PRCMU_SDMMCHCLK, PRCMU_CAMCLK, + PRCMU_BML8580CLK, PRCMU_NUM_REG_CLOCKS, PRCMU_SYSCLK = PRCMU_NUM_REG_CLOCKS, PRCMU_CDCLK, -- cgit v1.2.3 From a6a3ec7b512b975a9bb68ed9c4d55519d175acd9 Mon Sep 17 00:00:00 2001 From: Philippe Begnic Date: Mon, 27 May 2013 14:41:32 +0200 Subject: clk: ux500: Clocks definition for u8540 First clocks definition version of PRCMU and PRCC clocks for u8540 platform Signed-off-by: Philippe Begnic Signed-off-by: Mike Turquette --- drivers/clk/ux500/u8540_clk.c | 560 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 559 insertions(+), 1 deletion(-) diff --git a/drivers/clk/ux500/u8540_clk.c b/drivers/clk/ux500/u8540_clk.c index 90f3c88694b9..f26258869deb 100644 --- a/drivers/clk/ux500/u8540_clk.c +++ b/drivers/clk/ux500/u8540_clk.c @@ -17,5 +17,563 @@ void u8540_clk_init(u32 clkrst1_base, u32 clkrst2_base, u32 clkrst3_base, u32 clkrst5_base, u32 clkrst6_base) { - /* register clocks here */ + struct clk *clk; + + /* Clock sources. */ + /* Fixed ClockGen */ + clk = clk_reg_prcmu_gate("soc0_pll", NULL, PRCMU_PLLSOC0, + CLK_IS_ROOT|CLK_IGNORE_UNUSED); + clk_register_clkdev(clk, "soc0_pll", NULL); + + clk = clk_reg_prcmu_gate("soc1_pll", NULL, PRCMU_PLLSOC1, + CLK_IS_ROOT|CLK_IGNORE_UNUSED); + clk_register_clkdev(clk, "soc1_pll", NULL); + + clk = clk_reg_prcmu_gate("ddr_pll", NULL, PRCMU_PLLDDR, + CLK_IS_ROOT|CLK_IGNORE_UNUSED); + clk_register_clkdev(clk, "ddr_pll", NULL); + + clk = clk_register_fixed_rate(NULL, "rtc32k", NULL, + CLK_IS_ROOT|CLK_IGNORE_UNUSED, + 32768); + clk_register_clkdev(clk, "clk32k", NULL); + clk_register_clkdev(clk, "apb_pclk", "rtc-pl031"); + + clk = clk_register_fixed_rate(NULL, "ulp38m4", NULL, + CLK_IS_ROOT|CLK_IGNORE_UNUSED, + 38400000); + + clk = clk_reg_prcmu_gate("uartclk", NULL, PRCMU_UARTCLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "UART"); + + /* msp02clk needs a abx500 clk as parent. Handle by abx500 clk driver */ + clk = clk_reg_prcmu_gate("msp02clk", "ab9540_sysclk12_b1", + PRCMU_MSP02CLK, 0); + clk_register_clkdev(clk, NULL, "MSP02"); + + clk = clk_reg_prcmu_gate("msp1clk", NULL, PRCMU_MSP1CLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "MSP1"); + + clk = clk_reg_prcmu_gate("i2cclk", NULL, PRCMU_I2CCLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "I2C"); + + clk = clk_reg_prcmu_gate("slimclk", NULL, PRCMU_SLIMCLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "slim"); + + clk = clk_reg_prcmu_gate("per1clk", NULL, PRCMU_PER1CLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "PERIPH1"); + + clk = clk_reg_prcmu_gate("per2clk", NULL, PRCMU_PER2CLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "PERIPH2"); + + clk = clk_reg_prcmu_gate("per3clk", NULL, PRCMU_PER3CLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "PERIPH3"); + + clk = clk_reg_prcmu_gate("per5clk", NULL, PRCMU_PER5CLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "PERIPH5"); + + clk = clk_reg_prcmu_gate("per6clk", NULL, PRCMU_PER6CLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "PERIPH6"); + + clk = clk_reg_prcmu_gate("per7clk", NULL, PRCMU_PER7CLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "PERIPH7"); + + clk = clk_reg_prcmu_scalable("lcdclk", NULL, PRCMU_LCDCLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "lcd"); + clk_register_clkdev(clk, "lcd", "mcde"); + + clk = clk_reg_prcmu_opp_gate("bmlclk", NULL, PRCMU_BML8580CLK, + CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "bml"); + + clk = clk_reg_prcmu_scalable("hsitxclk", NULL, PRCMU_HSITXCLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + + clk = clk_reg_prcmu_scalable("hsirxclk", NULL, PRCMU_HSIRXCLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + + clk = clk_reg_prcmu_scalable("hdmiclk", NULL, PRCMU_HDMICLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "hdmi"); + clk_register_clkdev(clk, "hdmi", "mcde"); + + clk = clk_reg_prcmu_gate("apeatclk", NULL, PRCMU_APEATCLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "apeat"); + + clk = clk_reg_prcmu_gate("apetraceclk", NULL, PRCMU_APETRACECLK, + CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "apetrace"); + + clk = clk_reg_prcmu_gate("mcdeclk", NULL, PRCMU_MCDECLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "mcde"); + clk_register_clkdev(clk, "mcde", "mcde"); + clk_register_clkdev(clk, NULL, "dsilink.0"); + clk_register_clkdev(clk, NULL, "dsilink.1"); + clk_register_clkdev(clk, NULL, "dsilink.2"); + + clk = clk_reg_prcmu_opp_gate("ipi2cclk", NULL, PRCMU_IPI2CCLK, + CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "ipi2"); + + clk = clk_reg_prcmu_gate("dsialtclk", NULL, PRCMU_DSIALTCLK, + CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "dsialt"); + + clk = clk_reg_prcmu_gate("dmaclk", NULL, PRCMU_DMACLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "dma40.0"); + + clk = clk_reg_prcmu_gate("b2r2clk", NULL, PRCMU_B2R2CLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "b2r2"); + clk_register_clkdev(clk, NULL, "b2r2_core"); + clk_register_clkdev(clk, NULL, "U8500-B2R2.0"); + clk_register_clkdev(clk, NULL, "b2r2_1_core"); + + clk = clk_reg_prcmu_scalable("tvclk", NULL, PRCMU_TVCLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "tv"); + clk_register_clkdev(clk, "tv", "mcde"); + + clk = clk_reg_prcmu_gate("sspclk", NULL, PRCMU_SSPCLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "SSP"); + + clk = clk_reg_prcmu_gate("rngclk", NULL, PRCMU_RNGCLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "rngclk"); + + clk = clk_reg_prcmu_gate("uiccclk", NULL, PRCMU_UICCCLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "uicc"); + + clk = clk_reg_prcmu_gate("timclk", NULL, PRCMU_TIMCLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "mtu0"); + clk_register_clkdev(clk, NULL, "mtu1"); + + clk = clk_reg_prcmu_opp_volt_scalable("sdmmcclk", NULL, + PRCMU_SDMMCCLK, 100000000, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "sdmmc"); + + clk = clk_reg_prcmu_opp_volt_scalable("sdmmchclk", NULL, + PRCMU_SDMMCHCLK, 400000000, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "sdmmchclk"); + + clk = clk_reg_prcmu_gate("hvaclk", NULL, PRCMU_HVACLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "hva"); + + clk = clk_reg_prcmu_gate("g1clk", NULL, PRCMU_G1CLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "g1"); + + clk = clk_reg_prcmu_scalable("spare1clk", NULL, PRCMU_SPARE1CLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + clk_register_clkdev(clk, "dsilcd", "mcde"); + + clk = clk_reg_prcmu_scalable("dsi_pll", "hdmiclk", + PRCMU_PLLDSI, 0, CLK_SET_RATE_GATE); + clk_register_clkdev(clk, "dsihs2", "mcde"); + clk_register_clkdev(clk, "hs_clk", "dsilink.2"); + + clk = clk_reg_prcmu_scalable("dsilcd_pll", "spare1clk", + PRCMU_PLLDSI_LCD, 0, CLK_SET_RATE_GATE); + clk_register_clkdev(clk, "dsilcd_pll", "mcde"); + + clk = clk_reg_prcmu_scalable("dsi0clk", "dsi_pll", + PRCMU_DSI0CLK, 0, CLK_SET_RATE_GATE); + clk_register_clkdev(clk, "dsihs0", "mcde"); + + clk = clk_reg_prcmu_scalable("dsi0lcdclk", "dsilcd_pll", + PRCMU_DSI0CLK_LCD, 0, CLK_SET_RATE_GATE); + clk_register_clkdev(clk, "dsihs0", "mcde"); + clk_register_clkdev(clk, "hs_clk", "dsilink.0"); + + clk = clk_reg_prcmu_scalable("dsi1clk", "dsi_pll", + PRCMU_DSI1CLK, 0, CLK_SET_RATE_GATE); + clk_register_clkdev(clk, "dsihs1", "mcde"); + + clk = clk_reg_prcmu_scalable("dsi1lcdclk", "dsilcd_pll", + PRCMU_DSI1CLK_LCD, 0, CLK_SET_RATE_GATE); + clk_register_clkdev(clk, "dsihs1", "mcde"); + clk_register_clkdev(clk, "hs_clk", "dsilink.1"); + + clk = clk_reg_prcmu_scalable("dsi0escclk", "tvclk", + PRCMU_DSI0ESCCLK, 0, CLK_SET_RATE_GATE); + clk_register_clkdev(clk, "lp_clk", "dsilink.0"); + clk_register_clkdev(clk, "dsilp0", "mcde"); + + clk = clk_reg_prcmu_scalable("dsi1escclk", "tvclk", + PRCMU_DSI1ESCCLK, 0, CLK_SET_RATE_GATE); + clk_register_clkdev(clk, "lp_clk", "dsilink.1"); + clk_register_clkdev(clk, "dsilp1", "mcde"); + + clk = clk_reg_prcmu_scalable("dsi2escclk", "tvclk", + PRCMU_DSI2ESCCLK, 0, CLK_SET_RATE_GATE); + clk_register_clkdev(clk, "lp_clk", "dsilink.2"); + clk_register_clkdev(clk, "dsilp2", "mcde"); + + clk = clk_reg_prcmu_scalable_rate("armss", NULL, + PRCMU_ARMSS, 0, CLK_IS_ROOT|CLK_IGNORE_UNUSED); + clk_register_clkdev(clk, "armss", NULL); + + clk = clk_register_fixed_factor(NULL, "smp_twd", "armss", + CLK_IGNORE_UNUSED, 1, 2); + clk_register_clkdev(clk, NULL, "smp_twd"); + + /* PRCC P-clocks */ + /* Peripheral 1 : PRCC P-clocks */ + clk = clk_reg_prcc_pclk("p1_pclk0", "per1clk", clkrst1_base, + BIT(0), 0); + clk_register_clkdev(clk, "apb_pclk", "uart0"); + + clk = clk_reg_prcc_pclk("p1_pclk1", "per1clk", clkrst1_base, + BIT(1), 0); + clk_register_clkdev(clk, "apb_pclk", "uart1"); + + clk = clk_reg_prcc_pclk("p1_pclk2", "per1clk", clkrst1_base, + BIT(2), 0); + clk_register_clkdev(clk, "apb_pclk", "nmk-i2c.1"); + + clk = clk_reg_prcc_pclk("p1_pclk3", "per1clk", clkrst1_base, + BIT(3), 0); + clk_register_clkdev(clk, "apb_pclk", "msp0"); + clk_register_clkdev(clk, "apb_pclk", "dbx5x0-msp-i2s.0"); + + clk = clk_reg_prcc_pclk("p1_pclk4", "per1clk", clkrst1_base, + BIT(4), 0); + clk_register_clkdev(clk, "apb_pclk", "msp1"); + clk_register_clkdev(clk, "apb_pclk", "dbx5x0-msp-i2s.1"); + + clk = clk_reg_prcc_pclk("p1_pclk5", "per1clk", clkrst1_base, + BIT(5), 0); + clk_register_clkdev(clk, "apb_pclk", "sdi0"); + + clk = clk_reg_prcc_pclk("p1_pclk6", "per1clk", clkrst1_base, + BIT(6), 0); + clk_register_clkdev(clk, "apb_pclk", "nmk-i2c.2"); + + clk = clk_reg_prcc_pclk("p1_pclk7", "per1clk", clkrst1_base, + BIT(7), 0); + clk_register_clkdev(clk, NULL, "spi3"); + + clk = clk_reg_prcc_pclk("p1_pclk8", "per1clk", clkrst1_base, + BIT(8), 0); + clk_register_clkdev(clk, "apb_pclk", "slimbus0"); + + clk = clk_reg_prcc_pclk("p1_pclk9", "per1clk", clkrst1_base, + BIT(9), 0); + clk_register_clkdev(clk, NULL, "gpio.0"); + clk_register_clkdev(clk, NULL, "gpio.1"); + clk_register_clkdev(clk, NULL, "gpioblock0"); + clk_register_clkdev(clk, "apb_pclk", "ab85xx-codec.0"); + + clk = clk_reg_prcc_pclk("p1_pclk10", "per1clk", clkrst1_base, + BIT(10), 0); + clk_register_clkdev(clk, "apb_pclk", "nmk-i2c.4"); + + clk = clk_reg_prcc_pclk("p1_pclk11", "per1clk", clkrst1_base, + BIT(11), 0); + clk_register_clkdev(clk, "apb_pclk", "msp3"); + clk_register_clkdev(clk, "apb_pclk", "dbx5x0-msp-i2s.3"); + + /* Peripheral 2 : PRCC P-clocks */ + clk = clk_reg_prcc_pclk("p2_pclk0", "per2clk", clkrst2_base, + BIT(0), 0); + clk_register_clkdev(clk, "apb_pclk", "nmk-i2c.3"); + + clk = clk_reg_prcc_pclk("p2_pclk1", "per2clk", clkrst2_base, + BIT(1), 0); + clk_register_clkdev(clk, NULL, "spi2"); + + clk = clk_reg_prcc_pclk("p2_pclk2", "per2clk", clkrst2_base, + BIT(2), 0); + clk_register_clkdev(clk, NULL, "spi1"); + + clk = clk_reg_prcc_pclk("p2_pclk3", "per2clk", clkrst2_base, + BIT(3), 0); + clk_register_clkdev(clk, NULL, "pwl"); + + clk = clk_reg_prcc_pclk("p2_pclk4", "per2clk", clkrst2_base, + BIT(4), 0); + clk_register_clkdev(clk, "apb_pclk", "sdi4"); + + clk = clk_reg_prcc_pclk("p2_pclk5", "per2clk", clkrst2_base, + BIT(5), 0); + clk_register_clkdev(clk, "apb_pclk", "msp2"); + clk_register_clkdev(clk, "apb_pclk", "dbx5x0-msp-i2s.2"); + + clk = clk_reg_prcc_pclk("p2_pclk6", "per2clk", clkrst2_base, + BIT(6), 0); + clk_register_clkdev(clk, "apb_pclk", "sdi1"); + + clk = clk_reg_prcc_pclk("p2_pclk7", "per2clk", clkrst2_base, + BIT(7), 0); + clk_register_clkdev(clk, "apb_pclk", "sdi3"); + + clk = clk_reg_prcc_pclk("p2_pclk8", "per2clk", clkrst2_base, + BIT(8), 0); + clk_register_clkdev(clk, NULL, "spi0"); + + clk = clk_reg_prcc_pclk("p2_pclk9", "per2clk", clkrst2_base, + BIT(9), 0); + clk_register_clkdev(clk, "hsir_hclk", "ste_hsi.0"); + + clk = clk_reg_prcc_pclk("p2_pclk10", "per2clk", clkrst2_base, + BIT(10), 0); + clk_register_clkdev(clk, "hsit_hclk", "ste_hsi.0"); + + clk = clk_reg_prcc_pclk("p2_pclk11", "per2clk", clkrst2_base, + BIT(11), 0); + clk_register_clkdev(clk, NULL, "gpio.6"); + clk_register_clkdev(clk, NULL, "gpio.7"); + clk_register_clkdev(clk, NULL, "gpioblock1"); + + clk = clk_reg_prcc_pclk("p2_pclk12", "per2clk", clkrst2_base, + BIT(12), 0); + clk_register_clkdev(clk, "msp4-pclk", "ab85xx-codec.0"); + + /* Peripheral 3 : PRCC P-clocks */ + clk = clk_reg_prcc_pclk("p3_pclk0", "per3clk", clkrst3_base, + BIT(0), 0); + clk_register_clkdev(clk, NULL, "fsmc"); + + clk = clk_reg_prcc_pclk("p3_pclk1", "per3clk", clkrst3_base, + BIT(1), 0); + clk_register_clkdev(clk, "apb_pclk", "ssp0"); + + clk = clk_reg_prcc_pclk("p3_pclk2", "per3clk", clkrst3_base, + BIT(2), 0); + clk_register_clkdev(clk, "apb_pclk", "ssp1"); + + clk = clk_reg_prcc_pclk("p3_pclk3", "per3clk", clkrst3_base, + BIT(3), 0); + clk_register_clkdev(clk, "apb_pclk", "nmk-i2c.0"); + + clk = clk_reg_prcc_pclk("p3_pclk4", "per3clk", clkrst3_base, + BIT(4), 0); + clk_register_clkdev(clk, "apb_pclk", "sdi2"); + + clk = clk_reg_prcc_pclk("p3_pclk5", "per3clk", clkrst3_base, + BIT(5), 0); + clk_register_clkdev(clk, "apb_pclk", "ske"); + clk_register_clkdev(clk, "apb_pclk", "nmk-ske-keypad"); + + clk = clk_reg_prcc_pclk("p3_pclk6", "per3clk", clkrst3_base, + BIT(6), 0); + clk_register_clkdev(clk, "apb_pclk", "uart2"); + + clk = clk_reg_prcc_pclk("p3_pclk7", "per3clk", clkrst3_base, + BIT(7), 0); + clk_register_clkdev(clk, "apb_pclk", "sdi5"); + + clk = clk_reg_prcc_pclk("p3_pclk8", "per3clk", clkrst3_base, + BIT(8), 0); + clk_register_clkdev(clk, NULL, "gpio.2"); + clk_register_clkdev(clk, NULL, "gpio.3"); + clk_register_clkdev(clk, NULL, "gpio.4"); + clk_register_clkdev(clk, NULL, "gpio.5"); + clk_register_clkdev(clk, NULL, "gpioblock2"); + + clk = clk_reg_prcc_pclk("p3_pclk9", "per3clk", clkrst3_base, + BIT(9), 0); + clk_register_clkdev(clk, "apb_pclk", "nmk-i2c.5"); + + clk = clk_reg_prcc_pclk("p3_pclk10", "per3clk", clkrst3_base, + BIT(10), 0); + clk_register_clkdev(clk, "apb_pclk", "nmk-i2c.6"); + + clk = clk_reg_prcc_pclk("p3_pclk11", "per3clk", clkrst3_base, + BIT(11), 0); + clk_register_clkdev(clk, "apb_pclk", "uart3"); + + clk = clk_reg_prcc_pclk("p3_pclk12", "per3clk", clkrst3_base, + BIT(12), 0); + clk_register_clkdev(clk, "apb_pclk", "uart4"); + + /* Peripheral 5 : PRCC P-clocks */ + clk = clk_reg_prcc_pclk("p5_pclk0", "per5clk", clkrst5_base, + BIT(0), 0); + clk_register_clkdev(clk, "usb", "musb-ux500.0"); + clk_register_clkdev(clk, "usbclk", "ab-iddet.0"); + + clk = clk_reg_prcc_pclk("p5_pclk1", "per5clk", clkrst5_base, + BIT(1), 0); + clk_register_clkdev(clk, NULL, "gpio.8"); + clk_register_clkdev(clk, NULL, "gpioblock3"); + + /* Peripheral 6 : PRCC P-clocks */ + clk = clk_reg_prcc_pclk("p6_pclk0", "per6clk", clkrst6_base, + BIT(0), 0); + clk_register_clkdev(clk, "apb_pclk", "rng"); + + clk = clk_reg_prcc_pclk("p6_pclk1", "per6clk", clkrst6_base, + BIT(1), 0); + clk_register_clkdev(clk, NULL, "cryp0"); + clk_register_clkdev(clk, NULL, "cryp1"); + + clk = clk_reg_prcc_pclk("p6_pclk2", "per6clk", clkrst6_base, + BIT(2), 0); + clk_register_clkdev(clk, NULL, "hash0"); + + clk = clk_reg_prcc_pclk("p6_pclk3", "per6clk", clkrst6_base, + BIT(3), 0); + clk_register_clkdev(clk, NULL, "pka"); + + clk = clk_reg_prcc_pclk("p6_pclk4", "per6clk", clkrst6_base, + BIT(4), 0); + clk_register_clkdev(clk, NULL, "db8540-hash1"); + + clk = clk_reg_prcc_pclk("p6_pclk5", "per6clk", clkrst6_base, + BIT(5), 0); + clk_register_clkdev(clk, NULL, "cfgreg"); + + clk = clk_reg_prcc_pclk("p6_pclk6", "per6clk", clkrst6_base, + BIT(6), 0); + clk_register_clkdev(clk, "apb_pclk", "mtu0"); + + clk = clk_reg_prcc_pclk("p6_pclk7", "per6clk", clkrst6_base, + BIT(7), 0); + clk_register_clkdev(clk, "apb_pclk", "mtu1"); + + /* + * PRCC K-clocks ==> see table PRCC_PCKEN/PRCC_KCKEN + * This differs from the internal implementation: + * We don't use the PERPIH[n| clock as parent, since those _should_ + * only be used as parents for the P-clocks. + * TODO: "parentjoin" with corresponding P-clocks for all K-clocks. + */ + + /* Peripheral 1 : PRCC K-clocks */ + clk = clk_reg_prcc_kclk("p1_uart0_kclk", "uartclk", + clkrst1_base, BIT(0), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "uart0"); + + clk = clk_reg_prcc_kclk("p1_uart1_kclk", "uartclk", + clkrst1_base, BIT(1), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "uart1"); + + clk = clk_reg_prcc_kclk("p1_i2c1_kclk", "i2cclk", + clkrst1_base, BIT(2), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "nmk-i2c.1"); + + clk = clk_reg_prcc_kclk("p1_msp0_kclk", "msp02clk", + clkrst1_base, BIT(3), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "msp0"); + clk_register_clkdev(clk, NULL, "dbx5x0-msp-i2s.0"); + + clk = clk_reg_prcc_kclk("p1_msp1_kclk", "msp1clk", + clkrst1_base, BIT(4), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "msp1"); + clk_register_clkdev(clk, NULL, "dbx5x0-msp-i2s.1"); + + clk = clk_reg_prcc_kclk("p1_sdi0_kclk", "sdmmchclk", + clkrst1_base, BIT(5), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "sdi0"); + + clk = clk_reg_prcc_kclk("p1_i2c2_kclk", "i2cclk", + clkrst1_base, BIT(6), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "nmk-i2c.2"); + + clk = clk_reg_prcc_kclk("p1_slimbus0_kclk", "slimclk", + clkrst1_base, BIT(8), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "slimbus0"); + + clk = clk_reg_prcc_kclk("p1_i2c4_kclk", "i2cclk", + clkrst1_base, BIT(9), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "nmk-i2c.4"); + + clk = clk_reg_prcc_kclk("p1_msp3_kclk", "msp1clk", + clkrst1_base, BIT(10), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "msp3"); + clk_register_clkdev(clk, NULL, "dbx5x0-msp-i2s.3"); + + /* Peripheral 2 : PRCC K-clocks */ + clk = clk_reg_prcc_kclk("p2_i2c3_kclk", "i2cclk", + clkrst2_base, BIT(0), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "nmk-i2c.3"); + + clk = clk_reg_prcc_kclk("p2_pwl_kclk", "rtc32k", + clkrst2_base, BIT(1), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "pwl"); + + clk = clk_reg_prcc_kclk("p2_sdi4_kclk", "sdmmchclk", + clkrst2_base, BIT(2), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "sdi4"); + + clk = clk_reg_prcc_kclk("p2_msp2_kclk", "msp02clk", + clkrst2_base, BIT(3), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "msp2"); + clk_register_clkdev(clk, NULL, "dbx5x0-msp-i2s.2"); + + clk = clk_reg_prcc_kclk("p2_sdi1_kclk", "sdmmchclk", + clkrst2_base, BIT(4), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "sdi1"); + + clk = clk_reg_prcc_kclk("p2_sdi3_kclk", "sdmmcclk", + clkrst2_base, BIT(5), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "sdi3"); + + clk = clk_reg_prcc_kclk("p2_ssirx_kclk", "hsirxclk", + clkrst2_base, BIT(6), + CLK_SET_RATE_GATE|CLK_SET_RATE_PARENT); + clk_register_clkdev(clk, "hsir_hsirxclk", "ste_hsi.0"); + + clk = clk_reg_prcc_kclk("p2_ssitx_kclk", "hsitxclk", + clkrst2_base, BIT(7), + CLK_SET_RATE_GATE|CLK_SET_RATE_PARENT); + clk_register_clkdev(clk, "hsit_hsitxclk", "ste_hsi.0"); + + /* Should only be 9540, but might be added for 85xx as well */ + clk = clk_reg_prcc_kclk("p2_msp4_kclk", "msp02clk", + clkrst2_base, BIT(9), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "msp4"); + clk_register_clkdev(clk, "msp4", "ab85xx-codec.0"); + + /* Peripheral 3 : PRCC K-clocks */ + clk = clk_reg_prcc_kclk("p3_ssp0_kclk", "sspclk", + clkrst3_base, BIT(1), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "ssp0"); + + clk = clk_reg_prcc_kclk("p3_ssp1_kclk", "sspclk", + clkrst3_base, BIT(2), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "ssp1"); + + clk = clk_reg_prcc_kclk("p3_i2c0_kclk", "i2cclk", + clkrst3_base, BIT(3), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "nmk-i2c.0"); + + clk = clk_reg_prcc_kclk("p3_sdi2_kclk", "sdmmchclk", + clkrst3_base, BIT(4), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "sdi2"); + + clk = clk_reg_prcc_kclk("p3_ske_kclk", "rtc32k", + clkrst3_base, BIT(5), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "ske"); + clk_register_clkdev(clk, NULL, "nmk-ske-keypad"); + + clk = clk_reg_prcc_kclk("p3_uart2_kclk", "uartclk", + clkrst3_base, BIT(6), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "uart2"); + + clk = clk_reg_prcc_kclk("p3_sdi5_kclk", "sdmmcclk", + clkrst3_base, BIT(7), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "sdi5"); + + clk = clk_reg_prcc_kclk("p3_i2c5_kclk", "i2cclk", + clkrst3_base, BIT(8), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "nmk-i2c.5"); + + clk = clk_reg_prcc_kclk("p3_i2c6_kclk", "i2cclk", + clkrst3_base, BIT(9), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "nmk-i2c.6"); + + clk = clk_reg_prcc_kclk("p3_uart3_kclk", "uartclk", + clkrst3_base, BIT(10), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "uart3"); + + clk = clk_reg_prcc_kclk("p3_uart4_kclk", "uartclk", + clkrst3_base, BIT(11), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "uart4"); + + /* Peripheral 6 : PRCC K-clocks */ + clk = clk_reg_prcc_kclk("p6_rng_kclk", "rngclk", + clkrst6_base, BIT(0), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "rng"); } -- cgit v1.2.3 From 3e7ca9858d51a8df2bb18b82a529df5e5f9abc51 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 1 Jun 2013 19:45:56 +0200 Subject: drm/i915: enable 30bpp for DP outputs We always limited the link bw calculations to 24bpp. Tested with my shiny new high-bpc screen, seems to work as advertised. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=65280 Tested-by: shui yangwei Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 759a1c5d170d..6d560755cf53 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -704,7 +704,7 @@ intel_dp_compute_config(struct intel_encoder *encoder, /* Walk through all bpp values. Luckily they're all nicely spaced with 2 * bpc in between. */ - bpp = min_t(int, 8*3, pipe_config->pipe_bpp); + bpp = pipe_config->pipe_bpp; if (is_edp(intel_dp) && dev_priv->vbt.edp_bpp) bpp = min_t(int, bpp, dev_priv->vbt.edp_bpp); -- cgit v1.2.3 From de1aa629aac8377bdfc55674bb8e30b5f15f418d Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Fri, 7 Jun 2013 10:47:01 +0300 Subject: drm/i915: Disable primary plane trickle feed for g4x MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The docs say that the trickle feed disable bit is present (for primary planes only, not video sprites) on CTG, and that it must be set for ELK. Just set it for all g4x chipsets. v2: Do it in init_clock_gating too Reviewed-by: Chris Wilson Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 3 +++ drivers/gpu/drm/i915/intel_pm.c | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 8d9e7c0e9e4b..3f025ee299dd 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1958,6 +1958,9 @@ static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb, dspcntr &= ~DISPPLANE_TILED; } + if (IS_G4X(dev)) + dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE; + I915_WRITE(reg, dspcntr); linear_offset = y * fb->pitches[0] + x * (fb->bits_per_pixel / 8); diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index a417d7b196c2..47f3c48cd3c2 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -4908,6 +4908,7 @@ static void g4x_init_clock_gating(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; uint32_t dspclk_gate; + int pipe; I915_WRITE(RENCLK_GATE_D1, 0); I915_WRITE(RENCLK_GATE_D2, VF_UNIT_CLOCK_GATE_DISABLE | @@ -4924,6 +4925,14 @@ static void g4x_init_clock_gating(struct drm_device *dev) /* WaDisableRenderCachePipelinedFlush */ I915_WRITE(CACHE_MODE_0, _MASKED_BIT_ENABLE(CM0_PIPELINED_RENDER_FLUSH_DISABLE)); + + for_each_pipe(pipe) { + I915_WRITE(DSPCNTR(pipe), + I915_READ(DSPCNTR(pipe)) | + DISPPLANE_TRICKLE_FEED_DISABLE); + intel_flush_display_plane(dev_priv, pipe); + } + } static void crestline_init_clock_gating(struct drm_device *dev) -- cgit v1.2.3 From 20f949670f51341f255b17ec4650fa69ba22cb87 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Fri, 7 Jun 2013 10:47:02 +0300 Subject: drm/i915: Disable trickle feed via MI_ARB_STATE for the gen4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to BSpec, trickle feed should be disabled for BW and mobile CL. Those constraints seem to match all of our gen4 chipsets. Trickle feed is disabled via the MI_ARB_STATE register instead of per plane controls on gen4. Reviewed-by: Chris Wilson Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_pm.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 47f3c48cd3c2..2948764d7e38 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -4944,6 +4944,8 @@ static void crestline_init_clock_gating(struct drm_device *dev) I915_WRITE(DSPCLK_GATE_D, 0); I915_WRITE(RAMCLK_GATE_D, 0); I915_WRITE16(DEUC, 0); + I915_WRITE(MI_ARB_STATE, + _MASKED_BIT_ENABLE(MI_ARB_DISPLAY_TRICKLE_FEED_DISABLE)); } static void broadwater_init_clock_gating(struct drm_device *dev) @@ -4956,6 +4958,8 @@ static void broadwater_init_clock_gating(struct drm_device *dev) I965_ISC_CLOCK_GATE_DISABLE | I965_FBC_CLOCK_GATE_DISABLE); I915_WRITE(RENCLK_GATE_D2, 0); + I915_WRITE(MI_ARB_STATE, + _MASKED_BIT_ENABLE(MI_ARB_DISPLAY_TRICKLE_FEED_DISABLE)); } static void gen3_init_clock_gating(struct drm_device *dev) -- cgit v1.2.3 From bdad2b2f31852af4eb450d6a96e38940a0a1fdef Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Fri, 7 Jun 2013 10:47:03 +0300 Subject: drm/i915: Disable trickle feed in ironlake_init_clock_gating() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We disable trickle feed in all the (relevant) clock gating functions, except ironlake_init_clock_gating(). Copy paste the same code there as well. Signed-off-by: Ville Syrjälä Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_pm.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 2948764d7e38..78addac68521 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -4391,6 +4391,7 @@ static void ironlake_init_clock_gating(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; uint32_t dspclk_gate = ILK_VRHUNIT_CLOCK_GATE_DISABLE; + int pipe; /* Required for FBC */ dspclk_gate |= ILK_DPFCRUNIT_CLOCK_GATE_DISABLE | @@ -4450,6 +4451,13 @@ static void ironlake_init_clock_gating(struct drm_device *dev) I915_WRITE(CACHE_MODE_0, _MASKED_BIT_ENABLE(CM0_PIPELINED_RENDER_FLUSH_DISABLE)); + for_each_pipe(pipe) { + I915_WRITE(DSPCNTR(pipe), + I915_READ(DSPCNTR(pipe)) | + DISPPLANE_TRICKLE_FEED_DISABLE); + intel_flush_display_plane(dev_priv, pipe); + } + ibx_init_clock_gating(dev); } -- cgit v1.2.3 From 0e088b8f33dab39bd8beb0c7a030d44beb936dd0 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Fri, 7 Jun 2013 10:47:04 +0300 Subject: drm/i915: Refactor ctg+ trickle feed disable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pull the code to disable trickle feed for all primary planes into a separate function. Signed-off-by: Ville Syrjälä Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_pm.c | 62 +++++++++++++---------------------------- 1 file changed, 19 insertions(+), 43 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 78addac68521..8faa43fc9bc9 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -4387,11 +4387,23 @@ static void ibx_init_clock_gating(struct drm_device *dev) I915_WRITE(SOUTH_DSPCLK_GATE_D, PCH_DPLSUNIT_CLOCK_GATE_DISABLE); } +static void g4x_disable_trickle_feed(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + int pipe; + + for_each_pipe(pipe) { + I915_WRITE(DSPCNTR(pipe), + I915_READ(DSPCNTR(pipe)) | + DISPPLANE_TRICKLE_FEED_DISABLE); + intel_flush_display_plane(dev_priv, pipe); + } +} + static void ironlake_init_clock_gating(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; uint32_t dspclk_gate = ILK_VRHUNIT_CLOCK_GATE_DISABLE; - int pipe; /* Required for FBC */ dspclk_gate |= ILK_DPFCRUNIT_CLOCK_GATE_DISABLE | @@ -4451,12 +4463,7 @@ static void ironlake_init_clock_gating(struct drm_device *dev) I915_WRITE(CACHE_MODE_0, _MASKED_BIT_ENABLE(CM0_PIPELINED_RENDER_FLUSH_DISABLE)); - for_each_pipe(pipe) { - I915_WRITE(DSPCNTR(pipe), - I915_READ(DSPCNTR(pipe)) | - DISPPLANE_TRICKLE_FEED_DISABLE); - intel_flush_display_plane(dev_priv, pipe); - } + g4x_disable_trickle_feed(dev); ibx_init_clock_gating(dev); } @@ -4512,7 +4519,6 @@ static void gen6_check_mch_setup(struct drm_device *dev) static void gen6_init_clock_gating(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - int pipe; uint32_t dspclk_gate = ILK_VRHUNIT_CLOCK_GATE_DISABLE; I915_WRITE(ILK_DSPCLK_GATE_D, dspclk_gate); @@ -4588,12 +4594,7 @@ static void gen6_init_clock_gating(struct drm_device *dev) I915_WRITE(GEN6_MBCTL, I915_READ(GEN6_MBCTL) | GEN6_MBCTL_ENABLE_BOOT_FETCH); - for_each_pipe(pipe) { - I915_WRITE(DSPCNTR(pipe), - I915_READ(DSPCNTR(pipe)) | - DISPPLANE_TRICKLE_FEED_DISABLE); - intel_flush_display_plane(dev_priv, pipe); - } + g4x_disable_trickle_feed(dev); /* The default value should be 0x200 according to docs, but the two * platforms I checked have a 0 for this. (Maybe BIOS overrides?) */ @@ -4654,7 +4655,6 @@ static void lpt_suspend_hw(struct drm_device *dev) static void haswell_init_clock_gating(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - int pipe; I915_WRITE(WM3_LP_ILK, 0); I915_WRITE(WM2_LP_ILK, 0); @@ -4680,12 +4680,7 @@ static void haswell_init_clock_gating(struct drm_device *dev) I915_READ(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG) | GEN7_SQ_CHICKEN_MBCUNIT_SQINTMOB); - for_each_pipe(pipe) { - I915_WRITE(DSPCNTR(pipe), - I915_READ(DSPCNTR(pipe)) | - DISPPLANE_TRICKLE_FEED_DISABLE); - intel_flush_display_plane(dev_priv, pipe); - } + g4x_disable_trickle_feed(dev); /* WaVSRefCountFullforceMissDisable:hsw */ gen7_setup_fixed_func_scheduler(dev_priv); @@ -4711,7 +4706,6 @@ static void haswell_init_clock_gating(struct drm_device *dev) static void ivybridge_init_clock_gating(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - int pipe; uint32_t snpcr; I915_WRITE(WM3_LP_ILK, 0); @@ -4780,12 +4774,7 @@ static void ivybridge_init_clock_gating(struct drm_device *dev) I915_READ(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG) | GEN7_SQ_CHICKEN_MBCUNIT_SQINTMOB); - for_each_pipe(pipe) { - I915_WRITE(DSPCNTR(pipe), - I915_READ(DSPCNTR(pipe)) | - DISPPLANE_TRICKLE_FEED_DISABLE); - intel_flush_display_plane(dev_priv, pipe); - } + g4x_disable_trickle_feed(dev); /* WaMbcDriverBootEnable:ivb */ I915_WRITE(GEN6_MBCTL, I915_READ(GEN6_MBCTL) | @@ -4812,7 +4801,6 @@ static void ivybridge_init_clock_gating(struct drm_device *dev) static void valleyview_init_clock_gating(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - int pipe; I915_WRITE(DSPCLK_GATE_D, VRHUNIT_CLOCK_GATE_DISABLE); @@ -4885,12 +4873,7 @@ static void valleyview_init_clock_gating(struct drm_device *dev) I915_WRITE(GEN7_UCGCTL4, GEN7_L3BANK2X_CLOCK_GATE_DISABLE); - for_each_pipe(pipe) { - I915_WRITE(DSPCNTR(pipe), - I915_READ(DSPCNTR(pipe)) | - DISPPLANE_TRICKLE_FEED_DISABLE); - intel_flush_display_plane(dev_priv, pipe); - } + g4x_disable_trickle_feed(dev); I915_WRITE(CACHE_MODE_1, _MASKED_BIT_ENABLE(PIXEL_SUBSPAN_COLLECT_OPT_DISABLE)); @@ -4916,7 +4899,6 @@ static void g4x_init_clock_gating(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; uint32_t dspclk_gate; - int pipe; I915_WRITE(RENCLK_GATE_D1, 0); I915_WRITE(RENCLK_GATE_D2, VF_UNIT_CLOCK_GATE_DISABLE | @@ -4934,13 +4916,7 @@ static void g4x_init_clock_gating(struct drm_device *dev) I915_WRITE(CACHE_MODE_0, _MASKED_BIT_ENABLE(CM0_PIPELINED_RENDER_FLUSH_DISABLE)); - for_each_pipe(pipe) { - I915_WRITE(DSPCNTR(pipe), - I915_READ(DSPCNTR(pipe)) | - DISPPLANE_TRICKLE_FEED_DISABLE); - intel_flush_display_plane(dev_priv, pipe); - } - + g4x_disable_trickle_feed(dev); } static void crestline_init_clock_gating(struct drm_device *dev) -- cgit v1.2.3 From 0af0b189abf73d232af782df2f999235cd2fed7f Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 30 Jan 2013 18:17:49 +0000 Subject: ARM: hyp: initialize CNTVOFF to zero In order to be able to use the virtual counter in a safe way, make sure it is initialized to zero before dropping to SVC. Signed-off-by: Marc Zyngier Signed-off-by: Mark Rutland Acked-by: Santosh Shilimkar Cc: Dave Martin --- arch/arm/kernel/hyp-stub.S | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/kernel/hyp-stub.S b/arch/arm/kernel/hyp-stub.S index 1315c4ccfa56..dbe21107945a 100644 --- a/arch/arm/kernel/hyp-stub.S +++ b/arch/arm/kernel/hyp-stub.S @@ -153,6 +153,8 @@ THUMB( orr r7, #(1 << 30) ) @ HSCTLR.TE mrc p15, 4, r7, c14, c1, 0 @ CNTHCTL orr r7, r7, #3 @ PL1PCEN | PL1PCTEN mcr p15, 4, r7, c14, c1, 0 @ CNTHCTL + mov r7, #0 + mcrr p15, 4, r7, r7, c14 @ CNTVOFF 1: #endif -- cgit v1.2.3 From f793c23ebbe5afd1cabf4a42a3a297022213756f Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Tue, 26 Mar 2013 13:41:35 +0000 Subject: ARM: KVM: arch_timers: zero CNTVOFF upon return to host To use the virtual counters from the host, we need to ensure that CNTVOFF doesn't change unexpectedly. When we change to a guest, we replace the host's CNTVOFF, but we don't restore it when returning to the host. As the host sets CNTVOFF to zero, and never changes it, we can simply zero CNTVOFF when returning to the host. This patch adds said zeroing to the return to host path. Signed-off-by: Mark Rutland Acked-by: Marc Zyngier Acked-by: Santosh Shilimkar Acked-by: Christoffer Dall --- arch/arm/kvm/interrupts_head.S | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/kvm/interrupts_head.S b/arch/arm/kvm/interrupts_head.S index 3c8f2f0b4c5e..d43cfb5b37c4 100644 --- a/arch/arm/kvm/interrupts_head.S +++ b/arch/arm/kvm/interrupts_head.S @@ -497,6 +497,10 @@ vcpu .req r0 @ vcpu pointer always in r0 add r5, vcpu, r4 strd r2, r3, [r5] + @ Ensure host CNTVCT == CNTPCT + mov r2, #0 + mcrr p15, 4, r2, r2, c14 @ CNTVOFF + 1: #endif @ Allow physical timer/counter access for the host -- cgit v1.2.3 From 0d651e4e65e96989f72236bf83bd4c6e55eb6ce4 Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Wed, 30 Jan 2013 17:51:26 +0000 Subject: clocksource: arch_timer: use virtual counters Switching between reading the virtual or physical counters is problematic, as some core code wants a view of time before we're fully set up. Using a function pointer and switching the source after the first read can make time appear to go backwards, and having a check in the read function is an unfortunate block on what we want to be a fast path. Instead, this patch makes us always use the virtual counters. If we're a guest, or don't have hyp mode, we'll use the virtual timers, and as such don't care about CNTVOFF as long as it doesn't change in such a way as to make time appear to travel backwards. As the guest will use the virtual timers, a (potential) KVM host must use the physical timers (which can wake up the host even if they fire while a guest is executing), and hence a host must have CNTVOFF set to zero so as to have a consistent view of time between the physical timers and virtual counters. Signed-off-by: Mark Rutland Acked-by: Catalin Marinas Acked-by: Marc Zyngier Acked-by: Santosh Shilimkar Cc: Rob Herring --- arch/arm/include/asm/arch_timer.h | 9 --------- arch/arm64/include/asm/arch_timer.h | 10 ---------- drivers/clocksource/arm_arch_timer.c | 23 +++++------------------ include/clocksource/arm_arch_timer.h | 2 +- 4 files changed, 6 insertions(+), 38 deletions(-) diff --git a/arch/arm/include/asm/arch_timer.h b/arch/arm/include/asm/arch_timer.h index 7c1bfc0aea0c..accefe099182 100644 --- a/arch/arm/include/asm/arch_timer.h +++ b/arch/arm/include/asm/arch_timer.h @@ -80,15 +80,6 @@ static inline u32 arch_timer_get_cntfrq(void) return val; } -static inline u64 arch_counter_get_cntpct(void) -{ - u64 cval; - - isb(); - asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (cval)); - return cval; -} - static inline u64 arch_counter_get_cntvct(void) { u64 cval; diff --git a/arch/arm64/include/asm/arch_timer.h b/arch/arm64/include/asm/arch_timer.h index bf6ab242f047..d56ed11ba9a3 100644 --- a/arch/arm64/include/asm/arch_timer.h +++ b/arch/arm64/include/asm/arch_timer.h @@ -110,16 +110,6 @@ static inline void __cpuinit arch_counter_set_user_access(void) asm volatile("msr cntkctl_el1, %0" : : "r" (cntkctl)); } -static inline u64 arch_counter_get_cntpct(void) -{ - u64 cval; - - isb(); - asm volatile("mrs %0, cntpct_el0" : "=r" (cval)); - - return cval; -} - static inline u64 arch_counter_get_cntvct(void) { u64 cval; diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index a2b254189782..053d846ab5b1 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -186,27 +186,19 @@ u32 arch_timer_get_rate(void) return arch_timer_rate; } -/* - * Some external users of arch_timer_read_counter (e.g. sched_clock) may try to - * call it before it has been initialised. Rather than incur a performance - * penalty checking for initialisation, provide a default implementation that - * won't lead to time appearing to jump backwards. - */ -static u64 arch_timer_read_zero(void) +u64 arch_timer_read_counter(void) { - return 0; + return arch_counter_get_cntvct(); } -u64 (*arch_timer_read_counter)(void) = arch_timer_read_zero; - static cycle_t arch_counter_read(struct clocksource *cs) { - return arch_timer_read_counter(); + return arch_counter_get_cntvct(); } static cycle_t arch_counter_read_cc(const struct cyclecounter *cc) { - return arch_timer_read_counter(); + return arch_counter_get_cntvct(); } static struct clocksource clocksource_counter = { @@ -287,7 +279,7 @@ static int __init arch_timer_register(void) cyclecounter.mult = clocksource_counter.mult; cyclecounter.shift = clocksource_counter.shift; timecounter_init(&timecounter, &cyclecounter, - arch_counter_get_cntpct()); + arch_counter_get_cntvct()); if (arch_timer_use_virtual) { ppi = arch_timer_ppi[VIRT_PPI]; @@ -376,11 +368,6 @@ static void __init arch_timer_init(struct device_node *np) } } - if (arch_timer_use_virtual) - arch_timer_read_counter = arch_counter_get_cntvct; - else - arch_timer_read_counter = arch_counter_get_cntpct; - arch_timer_register(); arch_timer_arch_init(); } diff --git a/include/clocksource/arm_arch_timer.h b/include/clocksource/arm_arch_timer.h index e6c9c4cc9b23..c463ce990c48 100644 --- a/include/clocksource/arm_arch_timer.h +++ b/include/clocksource/arm_arch_timer.h @@ -32,7 +32,7 @@ #ifdef CONFIG_ARM_ARCH_TIMER extern u32 arch_timer_get_rate(void); -extern u64 (*arch_timer_read_counter)(void); +extern u64 arch_timer_read_counter(void); extern struct timecounter *arch_timer_get_timecounter(void); #else -- cgit v1.2.3 From fb521a0da1551468a45f2e2a1c1941d0033357ea Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Wed, 20 Mar 2013 13:57:38 +0000 Subject: arm: fix up ARM_ARCH_TIMER selects In 8a4da6e: "arm: arch_timer: move core to drivers/clocksource", the selection of ARM_ARCH_TIMER was indirected via HAVE_ARM_ARCH_TIMER, though mach-exynos's selection of ARM_ARCH_TIMER was missed, and since then mach-shmobile, mach-tegra, and mach-virt have begun selecting ARM_ARCH_TIMER. This can lead to architected timer support erroneously appearing to not be selected in menuconfig. This patch fixes up the Kconfigs for those platforms to select HAVE_ARM_ARCH_TIMER. Signed-off-by: Mark Rutland Acked-by: Stephen Warren Acked-by: Santosh Shilimkar Acked-by: Simon Horman Cc: Kukjin Kim Cc: Marc Zyngier --- arch/arm/mach-exynos/Kconfig | 2 +- arch/arm/mach-shmobile/Kconfig | 4 ++-- arch/arm/mach-tegra/Kconfig | 2 +- arch/arm/mach-virt/Kconfig | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig index d19edff0ea6e..3c3f36a8fbd4 100644 --- a/arch/arm/mach-exynos/Kconfig +++ b/arch/arm/mach-exynos/Kconfig @@ -76,7 +76,7 @@ config SOC_EXYNOS5440 default y depends on ARCH_EXYNOS5 select ARCH_HAS_OPP - select ARM_ARCH_TIMER + select HAVE_ARM_ARCH_TIMER select AUTO_ZRELADDR select PINCTRL select PINCTRL_EXYNOS5440 diff --git a/arch/arm/mach-shmobile/Kconfig b/arch/arm/mach-shmobile/Kconfig index 1a517e2fe449..767a6c3c27a4 100644 --- a/arch/arm/mach-shmobile/Kconfig +++ b/arch/arm/mach-shmobile/Kconfig @@ -23,7 +23,7 @@ config ARCH_R8A73A4 select ARCH_WANT_OPTIONAL_GPIOLIB select ARM_GIC select CPU_V7 - select ARM_ARCH_TIMER + select HAVE_ARM_ARCH_TIMER select SH_CLK_CPG select RENESAS_IRQC @@ -56,7 +56,7 @@ config ARCH_R8A7790 select ARCH_WANT_OPTIONAL_GPIOLIB select ARM_GIC select CPU_V7 - select ARM_ARCH_TIMER + select HAVE_ARM_ARCH_TIMER select SH_CLK_CPG select RENESAS_IRQC diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig index 84d72fc36dfe..65c5ae6fa386 100644 --- a/arch/arm/mach-tegra/Kconfig +++ b/arch/arm/mach-tegra/Kconfig @@ -60,7 +60,7 @@ config ARCH_TEGRA_3x_SOC config ARCH_TEGRA_114_SOC bool "Enable support for Tegra114 family" - select ARM_ARCH_TIMER + select HAVE_ARM_ARCH_TIMER select ARM_GIC select ARM_L1_CACHE_SHIFT_6 select CPU_FREQ_TABLE if CPU_FREQ diff --git a/arch/arm/mach-virt/Kconfig b/arch/arm/mach-virt/Kconfig index 8958f0d896bc..081d46929436 100644 --- a/arch/arm/mach-virt/Kconfig +++ b/arch/arm/mach-virt/Kconfig @@ -2,7 +2,7 @@ config ARCH_VIRT bool "Dummy Virtual Machine" if ARCH_MULTI_V7 select ARCH_WANT_OPTIONAL_GPIOLIB select ARM_GIC - select ARM_ARCH_TIMER + select HAVE_ARM_ARCH_TIMER select ARM_PSCI select HAVE_SMP select CPU_V7 -- cgit v1.2.3 From 3f71be237ce37e0131973ebfa33b326bc51d743e Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 12 Mar 2013 14:56:12 +0000 Subject: ARM: arch_timer: stop virtual timer when booted in HYP mode When booting the kernel, a bootloader could have left the virtual timer ticking away, potentially generating interrupts. This could be troublesome if the user of the virtual timer is not careful when enabling the interrupt. In order to avoid any surprise, stop the virtual timer from interrupting us when booted in HYP mode, as we'll use the physical timer in this case. Reported-by: Giridhar Maruthy Signed-off-by: Marc Zyngier Signed-off-by: Mark Rutland Acked-by: Santosh Shilimkar Cc: Dave Martin --- arch/arm/kernel/hyp-stub.S | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm/kernel/hyp-stub.S b/arch/arm/kernel/hyp-stub.S index dbe21107945a..4910232c4833 100644 --- a/arch/arm/kernel/hyp-stub.S +++ b/arch/arm/kernel/hyp-stub.S @@ -155,6 +155,11 @@ THUMB( orr r7, #(1 << 30) ) @ HSCTLR.TE mcr p15, 4, r7, c14, c1, 0 @ CNTHCTL mov r7, #0 mcrr p15, 4, r7, r7, c14 @ CNTVOFF + + @ Disable virtual timer in case it was counting + mrc p15, 0, r7, c14, c3, 1 @ CNTV_CTL + bic r7, #1 @ Clear ENABLE + mcr p15, 0, r7, c14, c3, 1 @ CNTV_CTL 1: #endif -- cgit v1.2.3 From d74e9e7090aeb9b61e683e5abf7ca70fa18f846b Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Fri, 7 Jun 2013 11:23:27 +0800 Subject: ASoC: wm8962: Add device tree binding Document the device tree binding for the WM8962 codec, and modify the driver to extract platform data from the device tree, if present. Based on work of WM8903 by Stephen Warren Signed-off-by: Nicolin Chen Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/wm8962.txt | 23 ++++++++++++++ sound/soc/codecs/wm8962.c | 35 +++++++++++++++++++++- 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/sound/wm8962.txt b/Documentation/devicetree/bindings/sound/wm8962.txt index dceb3b1c2bb7..7f82b59ec8f9 100644 --- a/Documentation/devicetree/bindings/sound/wm8962.txt +++ b/Documentation/devicetree/bindings/sound/wm8962.txt @@ -8,9 +8,32 @@ Required properties: - reg : the I2C address of the device. +Optional properties: + - spk-mono: This is a boolean property. If present, the SPK_MONO bit + of R51 (Class D Control 2) gets set, indicating that the speaker is + in mono mode. + + - mic-cfg : Default register value for R48 (Additional Control 4). + If absent, the default should be the register default. + + - gpio-cfg : A list of GPIO configuration register values. The list must + be 6 entries long. If absent, no configuration of these registers is + performed. And note that only the value within [0x0, 0xffff] is valid. + Any other value is regarded as setting the GPIO register by its reset + value 0x0. + Example: codec: wm8962@1a { compatible = "wlf,wm8962"; reg = <0x1a>; + + gpio-cfg = < + 0x0000 /* 0:Default */ + 0x0000 /* 1:Default */ + 0x0013 /* 2:FN_DMICCLK */ + 0x0000 /* 3:Default */ + 0x8014 /* 4:FN_DMICCDAT */ + 0x0000 /* 5:Default */ + >; }; diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index d56dd867057d..26219ea2bbb5 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -3584,6 +3584,34 @@ static const struct regmap_config wm8962_regmap = { .cache_type = REGCACHE_RBTREE, }; +static int wm8962_set_pdata_from_of(struct i2c_client *i2c, + struct wm8962_pdata *pdata) +{ + const struct device_node *np = i2c->dev.of_node; + u32 val32; + int i; + + if (of_property_read_bool(np, "spk-mono")) + pdata->spk_mono = true; + + if (of_property_read_u32(np, "mic-cfg", &val32) >= 0) + pdata->mic_cfg = val32; + + if (of_property_read_u32_array(np, "gpio-cfg", pdata->gpio_init, + ARRAY_SIZE(pdata->gpio_init)) >= 0) + for (i = 0; i < ARRAY_SIZE(pdata->gpio_init); i++) { + /* + * The range of GPIO register value is [0x0, 0xffff] + * While the default value of each register is 0x0 + * Any other value will be regarded as default value + */ + if (pdata->gpio_init[i] > 0xffff) + pdata->gpio_init[i] = 0x0; + } + + return 0; +} + static int wm8962_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -3604,8 +3632,13 @@ static int wm8962_i2c_probe(struct i2c_client *i2c, wm8962->irq = i2c->irq; /* If platform data was supplied, update the default data in priv */ - if (pdata) + if (pdata) { memcpy(&wm8962->pdata, pdata, sizeof(struct wm8962_pdata)); + } else if (i2c->dev.of_node) { + ret = wm8962_set_pdata_from_of(i2c, &wm8962->pdata); + if (ret != 0) + return ret; + } for (i = 0; i < ARRAY_SIZE(wm8962->supplies); i++) wm8962->supplies[i].supply = wm8962_supply_names[i]; -- cgit v1.2.3 From 2a668a8bc2cbe7a464ab1212475a3efb23a94b1e Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Fri, 7 Jun 2013 08:06:56 +0000 Subject: regulator: core: add regulator_get_linear_step() Add regulator_get_linear_step(), which returns the voltage step size between VSEL values for linear regulators. This is intended for use by regulator consumers which build their own voltage-to-VSEL tables. Signed-off-by: Paul Walmsley Reviewed-by: Andrew Chew Cc: Matthew Longnecker Signed-off-by: Mark Brown --- drivers/regulator/core.c | 15 +++++++++++++++ include/linux/regulator/consumer.h | 1 + 2 files changed, 16 insertions(+) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 6e5017841582..f07d7adefdda 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2134,6 +2134,21 @@ int regulator_list_voltage(struct regulator *regulator, unsigned selector) } EXPORT_SYMBOL_GPL(regulator_list_voltage); +/** + * regulator_get_linear_step - return the voltage step size between VSEL values + * @regulator: regulator source + * + * Returns the voltage step size between VSEL values for linear + * regulators, or return 0 if the regulator isn't a linear regulator. + */ +unsigned int regulator_get_linear_step(struct regulator *regulator) +{ + struct regulator_dev *rdev = regulator->rdev; + + return rdev->desc->uV_step; +} +EXPORT_SYMBOL_GPL(regulator_get_linear_step); + /** * regulator_is_supported_voltage - check if a voltage range can be supported * diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h index 145022a83085..3a76389c6aaa 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -165,6 +165,7 @@ int regulator_count_voltages(struct regulator *regulator); int regulator_list_voltage(struct regulator *regulator, unsigned selector); int regulator_is_supported_voltage(struct regulator *regulator, int min_uV, int max_uV); +unsigned int regulator_get_linear_step(struct regulator *regulator); int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV); int regulator_set_voltage_time(struct regulator *regulator, int old_uV, int new_uV); -- cgit v1.2.3 From 9f54ad6652d454b6a8e932b5307e60c9023bd974 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Tue, 4 Jun 2013 11:07:03 +0000 Subject: arm64/xen: introduce asm/xen header files on arm64 asm/xen/hypercall.h, asm/xen/hypervisor.h, asm/xen/interface.h and asm/xen/page.h are identical so to avoid code duplication we are just including the original arm header files here. asm/xen/events.h is slightly different, so introduce a different file here (use xchg to implement xchg_xen_ulong and pass regs->pstate to raw_irqs_disabled_flags). Also introduce asm/hypervisor.h and asm/sync_bitops.h. Signed-off-by: Stefano Stabellini Acked-by: Ian Campbell --- arch/arm64/include/asm/hypervisor.h | 6 ++++++ arch/arm64/include/asm/sync_bitops.h | 26 ++++++++++++++++++++++++++ arch/arm64/include/asm/xen/events.h | 21 +++++++++++++++++++++ arch/arm64/include/asm/xen/hypercall.h | 1 + arch/arm64/include/asm/xen/hypervisor.h | 1 + arch/arm64/include/asm/xen/interface.h | 1 + arch/arm64/include/asm/xen/page.h | 1 + 7 files changed, 57 insertions(+) create mode 100644 arch/arm64/include/asm/hypervisor.h create mode 100644 arch/arm64/include/asm/sync_bitops.h create mode 100644 arch/arm64/include/asm/xen/events.h create mode 100644 arch/arm64/include/asm/xen/hypercall.h create mode 100644 arch/arm64/include/asm/xen/hypervisor.h create mode 100644 arch/arm64/include/asm/xen/interface.h create mode 100644 arch/arm64/include/asm/xen/page.h diff --git a/arch/arm64/include/asm/hypervisor.h b/arch/arm64/include/asm/hypervisor.h new file mode 100644 index 000000000000..d2c79049ff11 --- /dev/null +++ b/arch/arm64/include/asm/hypervisor.h @@ -0,0 +1,6 @@ +#ifndef _ASM_ARM64_HYPERVISOR_H +#define _ASM_ARM64_HYPERVISOR_H + +#include + +#endif diff --git a/arch/arm64/include/asm/sync_bitops.h b/arch/arm64/include/asm/sync_bitops.h new file mode 100644 index 000000000000..8da0bf4f7659 --- /dev/null +++ b/arch/arm64/include/asm/sync_bitops.h @@ -0,0 +1,26 @@ +#ifndef __ASM_SYNC_BITOPS_H__ +#define __ASM_SYNC_BITOPS_H__ + +#include +#include + +/* sync_bitops functions are equivalent to the SMP implementation of the + * original functions, independently from CONFIG_SMP being defined. + * + * We need them because _set_bit etc are not SMP safe if !CONFIG_SMP. But + * under Xen you might be communicating with a completely external entity + * who might be on another CPU (e.g. two uniprocessor guests communicating + * via event channels and grant tables). So we need a variant of the bit + * ops which are SMP safe even on a UP kernel. + */ + +#define sync_set_bit(nr, p) set_bit(nr, p) +#define sync_clear_bit(nr, p) clear_bit(nr, p) +#define sync_change_bit(nr, p) change_bit(nr, p) +#define sync_test_and_set_bit(nr, p) test_and_set_bit(nr, p) +#define sync_test_and_clear_bit(nr, p) test_and_clear_bit(nr, p) +#define sync_test_and_change_bit(nr, p) test_and_change_bit(nr, p) +#define sync_test_bit(nr, addr) test_bit(nr, addr) +#define sync_cmpxchg cmpxchg + +#endif diff --git a/arch/arm64/include/asm/xen/events.h b/arch/arm64/include/asm/xen/events.h new file mode 100644 index 000000000000..86553213c132 --- /dev/null +++ b/arch/arm64/include/asm/xen/events.h @@ -0,0 +1,21 @@ +#ifndef _ASM_ARM64_XEN_EVENTS_H +#define _ASM_ARM64_XEN_EVENTS_H + +#include +#include + +enum ipi_vector { + XEN_PLACEHOLDER_VECTOR, + + /* Xen IPIs go here */ + XEN_NR_IPIS, +}; + +static inline int xen_irqs_disabled(struct pt_regs *regs) +{ + return raw_irqs_disabled_flags((unsigned long) regs->pstate); +} + +#define xchg_xen_ulong(ptr, val) xchg((ptr), (val)) + +#endif /* _ASM_ARM64_XEN_EVENTS_H */ diff --git a/arch/arm64/include/asm/xen/hypercall.h b/arch/arm64/include/asm/xen/hypercall.h new file mode 100644 index 000000000000..74b0c423ff5b --- /dev/null +++ b/arch/arm64/include/asm/xen/hypercall.h @@ -0,0 +1 @@ +#include <../../arm/include/asm/xen/hypercall.h> diff --git a/arch/arm64/include/asm/xen/hypervisor.h b/arch/arm64/include/asm/xen/hypervisor.h new file mode 100644 index 000000000000..f263da8e8769 --- /dev/null +++ b/arch/arm64/include/asm/xen/hypervisor.h @@ -0,0 +1 @@ +#include <../../arm/include/asm/xen/hypervisor.h> diff --git a/arch/arm64/include/asm/xen/interface.h b/arch/arm64/include/asm/xen/interface.h new file mode 100644 index 000000000000..44457aebeed4 --- /dev/null +++ b/arch/arm64/include/asm/xen/interface.h @@ -0,0 +1 @@ +#include <../../arm/include/asm/xen/interface.h> diff --git a/arch/arm64/include/asm/xen/page.h b/arch/arm64/include/asm/xen/page.h new file mode 100644 index 000000000000..bed87ec36780 --- /dev/null +++ b/arch/arm64/include/asm/xen/page.h @@ -0,0 +1 @@ +#include <../../arm/include/asm/xen/page.h> -- cgit v1.2.3 From 22d4102f778df9cab47e871b8de3400f6e685378 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Fri, 31 May 2013 15:22:25 +0000 Subject: arm64/xen: implement ioremap_cached on arm64 Signed-off-by: Stefano Stabellini --- arch/arm64/include/asm/io.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/include/asm/io.h b/arch/arm64/include/asm/io.h index 2e12258aa7e4..1d12f89140ba 100644 --- a/arch/arm64/include/asm/io.h +++ b/arch/arm64/include/asm/io.h @@ -228,10 +228,12 @@ extern void __iounmap(volatile void __iomem *addr); #define PROT_DEFAULT (PTE_TYPE_PAGE | PTE_AF | PTE_DIRTY) #define PROT_DEVICE_nGnRE (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_ATTRINDX(MT_DEVICE_nGnRE)) #define PROT_NORMAL_NC (PROT_DEFAULT | PTE_ATTRINDX(MT_NORMAL_NC)) +#define PROT_NORMAL (PROT_DEFAULT | PTE_ATTRINDX(MT_NORMAL)) #define ioremap(addr, size) __ioremap((addr), (size), __pgprot(PROT_DEVICE_nGnRE)) #define ioremap_nocache(addr, size) __ioremap((addr), (size), __pgprot(PROT_DEVICE_nGnRE)) #define ioremap_wc(addr, size) __ioremap((addr), (size), __pgprot(PROT_NORMAL_NC)) +#define ioremap_cached(addr, size) __ioremap((addr), (size), __pgprot(PROT_NORMAL)) #define iounmap __iounmap #define PROT_SECT_DEFAULT (PMD_TYPE_SECT | PMD_SECT_AF) -- cgit v1.2.3 From ea9c3652f5aec45a36b3e1c9a21ecfcc0ffd77b0 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Tue, 4 Jun 2013 11:06:30 +0000 Subject: arm64/xen: use XEN_IO_PROTO_ABI_ARM on ARM64 Signed-off-by: Stefano Stabellini Acked-by: Ian Campbell --- include/xen/interface/io/protocols.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/xen/interface/io/protocols.h b/include/xen/interface/io/protocols.h index 0eafaf254fff..056744b4b05e 100644 --- a/include/xen/interface/io/protocols.h +++ b/include/xen/interface/io/protocols.h @@ -15,7 +15,7 @@ # define XEN_IO_PROTO_ABI_NATIVE XEN_IO_PROTO_ABI_IA64 #elif defined(__powerpc64__) # define XEN_IO_PROTO_ABI_NATIVE XEN_IO_PROTO_ABI_POWERPC64 -#elif defined(__arm__) +#elif defined(__arm__) || defined(__aarch64__) # define XEN_IO_PROTO_ABI_NATIVE XEN_IO_PROTO_ABI_ARM #else # error arch fixup needed here -- cgit v1.2.3 From aa42aa1389a54d1afb1c7606c5a37c3429cdf517 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Mon, 3 Jun 2013 17:05:43 +0000 Subject: arm64/xen: introduce CONFIG_XEN and hypercall.S on ARM64 Introduce CONFIG_XEN and the implementation of hypercall.S (that is the only ARMv8 specific code in Xen support for ARM). Compile enlighten.c and grant_table.c from arch/arm. Signed-off-by: Stefano Stabellini Acked-by: Ian Campbell --- arch/arm64/Kconfig | 10 +++++ arch/arm64/Makefile | 1 + arch/arm64/xen/Makefile | 2 + arch/arm64/xen/hypercall.S | 92 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 105 insertions(+) create mode 100644 arch/arm64/xen/Makefile create mode 100644 arch/arm64/xen/hypercall.S diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 56b3f6d447ae..b5d6ada9f98c 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -182,6 +182,16 @@ config HW_PERF_EVENTS source "mm/Kconfig" +config XEN_DOM0 + def_bool y + depends on XEN + +config XEN + bool "Xen guest support on ARM64 (EXPERIMENTAL)" + depends on ARM64 && OF + help + Say Y if you want to run Linux in a Virtual Machine on Xen on ARM64. + endmenu menu "Boot options" diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile index c95c5cb212fd..79dd13dc8837 100644 --- a/arch/arm64/Makefile +++ b/arch/arm64/Makefile @@ -37,6 +37,7 @@ TEXT_OFFSET := 0x00080000 export TEXT_OFFSET GZFLAGS core-y += arch/arm64/kernel/ arch/arm64/mm/ +core-$(CONFIG_XEN) += arch/arm64/xen/ libs-y := arch/arm64/lib/ $(libs-y) libs-y += $(LIBGCC) diff --git a/arch/arm64/xen/Makefile b/arch/arm64/xen/Makefile new file mode 100644 index 000000000000..be240404ba96 --- /dev/null +++ b/arch/arm64/xen/Makefile @@ -0,0 +1,2 @@ +xen-arm-y += $(addprefix ../../arm/xen/, enlighten.o grant-table.o) +obj-y := xen-arm.o hypercall.o diff --git a/arch/arm64/xen/hypercall.S b/arch/arm64/xen/hypercall.S new file mode 100644 index 000000000000..2816c479cd49 --- /dev/null +++ b/arch/arm64/xen/hypercall.S @@ -0,0 +1,92 @@ +/****************************************************************************** + * hypercall.S + * + * Xen hypercall wrappers + * + * Stefano Stabellini , Citrix, 2012 + * + * 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; or, when distributed + * separately from the Linux kernel or incorporated into other + * software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (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 + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/* + * The Xen hypercall calling convention is very similar to the procedure + * call standard for the ARM 64-bit architecture: the first parameter is + * passed in x0, the second in x1, the third in x2, the fourth in x3 and + * the fifth in x4. + * + * The hypercall number is passed in x16. + * + * The return value is in x0. + * + * The hvc ISS is required to be 0xEA1, that is the Xen specific ARM + * hypercall tag. + * + * Parameter structs passed to hypercalls are laid out according to + * the ARM 64-bit EABI standard. + */ + +#include +#include +#include + + +#define XEN_IMM 0xEA1 + +#define HYPERCALL_SIMPLE(hypercall) \ +ENTRY(HYPERVISOR_##hypercall) \ + mov x16, #__HYPERVISOR_##hypercall; \ + hvc XEN_IMM; \ + ret; \ +ENDPROC(HYPERVISOR_##hypercall) + +#define HYPERCALL0 HYPERCALL_SIMPLE +#define HYPERCALL1 HYPERCALL_SIMPLE +#define HYPERCALL2 HYPERCALL_SIMPLE +#define HYPERCALL3 HYPERCALL_SIMPLE +#define HYPERCALL4 HYPERCALL_SIMPLE +#define HYPERCALL5 HYPERCALL_SIMPLE + + .text + +HYPERCALL2(xen_version); +HYPERCALL3(console_io); +HYPERCALL3(grant_table_op); +HYPERCALL2(sched_op); +HYPERCALL2(event_channel_op); +HYPERCALL2(hvm_op); +HYPERCALL2(memory_op); +HYPERCALL2(physdev_op); +HYPERCALL3(vcpu_op); + +ENTRY(privcmd_call) + mov x16, x0 + mov x0, x1 + mov x1, x2 + mov x2, x3 + mov x3, x4 + mov x4, x5 + hvc XEN_IMM + ret +ENDPROC(privcmd_call); -- cgit v1.2.3 From b475e83f4653e43880a07c7daaa8c58d9237135d Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Tue, 4 Jun 2013 16:13:23 +0000 Subject: MAINTAINERS: add myself as arm64/xen maintainer Signed-off-by: Stefano Stabellini Acked-by: Ian Campbell Acked-by: Konrad Rzeszutek Wilk --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index fd3a495a0005..95a6a5141e68 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9096,6 +9096,13 @@ S: Supported F: arch/arm/xen/ F: arch/arm/include/asm/xen/ +XEN HYPERVISOR ARM64 +M: Stefano Stabellini +L: xen-devel@lists.xensource.com (moderated for non-subscribers) +S: Supported +F: arch/arm64/xen/ +F: arch/arm64/include/asm/xen/ + XEN NETWORK BACKEND DRIVER M: Ian Campbell L: xen-devel@lists.xensource.com (moderated for non-subscribers) -- cgit v1.2.3 From 4b416745b9aa20a2b51509348d886ea8a5c99951 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Sat, 1 Jun 2013 00:22:44 +0200 Subject: cpufreq: SPEAr needs cpufreq table Like a lot of the other cpufreq drivers, this one needs to select CONFIG_CPU_FREQ_TABLE to avoid a build error like drivers/built-in.o: In function `spear_cpufreq_exit': spear-cpufreq.c:198: undefined reference to `cpufreq_frequency_table_put_attr' drivers/built-in.o: In function `spear_cpufreq_verify': spear-cpufreq.c:35: undefined reference to `cpufreq_frequency_table_verify' drivers/built-in.o: In function `spear_cpufreq_init': spear-cpufreq.c:181: undefined reference to `cpufreq_frequency_table_cpuinfo' spear-cpufreq.c:187: undefined reference to `cpufreq_frequency_table_get_attr' drivers/built-in.o: In function `spear_cpufreq_target': spear-cpufreq.c:120: undefined reference to `cpufreq_frequency_table_target' drivers/built-in.o:(.data+0x5e63c): undefined reference to `cpufreq_freq_attr_scaling_available_freqs' Signed-off-by: Arnd Bergmann Cc: Rafael J. Wysocki Cc: Viresh Kumar Cc: cpufreq@vger.kernel.org Cc: linux-pm@vger.kernel.org Signed-off-by: Viresh Kumar --- drivers/cpufreq/Kconfig.arm | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 6e57543fe0b9..d06fe1a54080 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -147,6 +147,7 @@ config ARM_SA1110_CPUFREQ config ARM_SPEAR_CPUFREQ bool "SPEAr CPUFreq support" depends on PLAT_SPEAR + select CPU_FREQ_TABLE default y help This adds the CPUFreq driver support for SPEAr SOCs. -- cgit v1.2.3 From fe948f541abc1e176da4daf4dd181751f764ec75 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 3 Jun 2013 23:41:14 +0200 Subject: cpufreq: big.LITTLE needs cpufreq table Like a lot of the other cpufreq drivers, this one needs to select CONFIG_CPU_FREQ_TABLE to avoid a build error like built-in.o: In function `bL_cpufreq_set_target': cpufreq/arm_big_little.c:71: undefined reference to `cpufreq_frequency_table_target' built-in.o: In function `bL_cpufreq_verify_policy': cpufreq/arm_big_little.c:55: undefined reference to `cpufreq_frequency_table_verify' built-in.o: In function `bL_cpufreq_init': cpufreq/arm_big_little.c:170: undefined reference to `cpufreq_frequency_table_cpuinfo' cpufreq/arm_big_little.c:178: undefined reference to `cpufreq_frequency_table_get_attr' built-in.o:(.data+0x5a80c): undefined reference to `cpufreq_freq_attr_scaling_available_freqs' Signed-off-by: Arnd Bergmann Cc: Rafael J. Wysocki Cc: Viresh Kumar Signed-off-by: Viresh Kumar --- drivers/cpufreq/Kconfig.arm | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index d06fe1a54080..1ed171db8fe6 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -5,6 +5,7 @@ config ARM_BIG_LITTLE_CPUFREQ tristate "Generic ARM big LITTLE CPUfreq driver" depends on ARM_CPU_TOPOLOGY && PM_OPP && HAVE_CLK + select CPU_FREQ_TABLE help This enables the Generic CPUfreq driver for ARM big.LITTLE platforms. -- cgit v1.2.3 From ea61623fe9badd5a195b3a0878e6d89a2f97ac0e Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 4 Jun 2013 14:03:18 -0300 Subject: cpufreq: kirkwood: Select CPU_FREQ_TABLE option We need to select CPU_FREQ_TABLE in order to build without this kind of errors: drivers/built-in.o: In function `kirkwood_cpufreq_cpu_exit': /home/zeta/linux-devel/marvell-legacy/drivers/cpufreq/kirkwood-cpufreq.c:145: undefined reference to `cpufreq_frequency_table_put_attr' Signed-off-by: Ezequiel Garcia Acked-by: Jason Cooper Signed-off-by: Viresh Kumar --- drivers/cpufreq/Kconfig.arm | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 1ed171db8fe6..5c7c2e1645b4 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -87,6 +87,7 @@ config ARM_INTEGRATOR config ARM_KIRKWOOD_CPUFREQ def_bool ARCH_KIRKWOOD && OF + select CPU_FREQ_TABLE help This adds the CPUFreq driver for Marvell Kirkwood SoCs. -- cgit v1.2.3 From c655affbd524d0105978ecd696c3bb8a281b418b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 7 Jun 2013 13:13:31 +0200 Subject: ACPI / cpufreq: Add ACPI processor device IDs to acpi-cpufreq After commit ac212b6 (ACPI / processor: Use common hotplug infrastructure) the acpi-cpufreq module is not loaded automatically by udev which fails to match it against the x86cpu modalias. Still, it can be matched against ACPI processor device IDs, which even makes more sense, because it depends on the ACPI processor driver that uses those device IDs to bind to processor devices. For this reason, add ACPI processor device IDs to acpi-cpufreq. Signed-off-by: Rafael J. Wysocki Acked-by: Viresh Kumar --- drivers/cpufreq/acpi-cpufreq.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c index 11b8b4b54ceb..4a9ca0149719 100644 --- a/drivers/cpufreq/acpi-cpufreq.c +++ b/drivers/cpufreq/acpi-cpufreq.c @@ -1034,4 +1034,11 @@ static const struct x86_cpu_id acpi_cpufreq_ids[] = { }; MODULE_DEVICE_TABLE(x86cpu, acpi_cpufreq_ids); +static const struct acpi_device_id processor_device_ids[] = { + {ACPI_PROCESSOR_OBJECT_HID, }, + {ACPI_PROCESSOR_DEVICE_HID, }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, processor_device_ids); + MODULE_ALIAS("acpi"); -- cgit v1.2.3 From 7fb6a53db58c729ff470095371f431b6d66c527b Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Apr 2013 18:24:25 +0530 Subject: cpufreq: powerpc: move cpufreq driver to drivers/cpufreq Move cpufreq driver of powerpc platform to drivers/cpufreq. Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- arch/powerpc/platforms/Kconfig | 31 -- arch/powerpc/platforms/pasemi/Makefile | 1 - arch/powerpc/platforms/pasemi/cpufreq.c | 331 ------------ arch/powerpc/platforms/powermac/Makefile | 2 - arch/powerpc/platforms/powermac/cpufreq_32.c | 721 -------------------------- arch/powerpc/platforms/powermac/cpufreq_64.c | 746 --------------------------- drivers/cpufreq/Kconfig.powerpc | 26 + drivers/cpufreq/Makefile | 3 + drivers/cpufreq/pasemi-cpufreq.c | 331 ++++++++++++ drivers/cpufreq/pmac32-cpufreq.c | 721 ++++++++++++++++++++++++++ drivers/cpufreq/pmac64-cpufreq.c | 746 +++++++++++++++++++++++++++ 11 files changed, 1827 insertions(+), 1832 deletions(-) delete mode 100644 arch/powerpc/platforms/pasemi/cpufreq.c delete mode 100644 arch/powerpc/platforms/powermac/cpufreq_32.c delete mode 100644 arch/powerpc/platforms/powermac/cpufreq_64.c create mode 100644 drivers/cpufreq/pasemi-cpufreq.c create mode 100644 drivers/cpufreq/pmac32-cpufreq.c create mode 100644 drivers/cpufreq/pmac64-cpufreq.c diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig index b62aab3e22ec..e17cdfc5ba40 100644 --- a/arch/powerpc/platforms/Kconfig +++ b/arch/powerpc/platforms/Kconfig @@ -193,37 +193,6 @@ config PPC_IO_WORKAROUNDS source "drivers/cpufreq/Kconfig" -menu "CPU Frequency drivers" - depends on CPU_FREQ - -config CPU_FREQ_PMAC - bool "Support for Apple PowerBooks" - depends on ADB_PMU && PPC32 - select CPU_FREQ_TABLE - help - This adds support for frequency switching on Apple PowerBooks, - this currently includes some models of iBook & Titanium - PowerBook. - -config CPU_FREQ_PMAC64 - bool "Support for some Apple G5s" - depends on PPC_PMAC && PPC64 - select CPU_FREQ_TABLE - help - This adds support for frequency switching on Apple iMac G5, - and some of the more recent desktop G5 machines as well. - -config PPC_PASEMI_CPUFREQ - bool "Support for PA Semi PWRficient" - depends on PPC_PASEMI - default y - select CPU_FREQ_TABLE - help - This adds the support for frequency switching on PA Semi - PWRficient processors. - -endmenu - menu "CPUIdle driver" source "drivers/cpuidle/Kconfig" diff --git a/arch/powerpc/platforms/pasemi/Makefile b/arch/powerpc/platforms/pasemi/Makefile index ce6d789e0741..8e8d4cae5ebe 100644 --- a/arch/powerpc/platforms/pasemi/Makefile +++ b/arch/powerpc/platforms/pasemi/Makefile @@ -1,3 +1,2 @@ obj-y += setup.o pci.o time.o idle.o powersave.o iommu.o dma_lib.o misc.o obj-$(CONFIG_PPC_PASEMI_MDIO) += gpio_mdio.o -obj-$(CONFIG_PPC_PASEMI_CPUFREQ) += cpufreq.o diff --git a/arch/powerpc/platforms/pasemi/cpufreq.c b/arch/powerpc/platforms/pasemi/cpufreq.c deleted file mode 100644 index b704da404067..000000000000 --- a/arch/powerpc/platforms/pasemi/cpufreq.c +++ /dev/null @@ -1,331 +0,0 @@ -/* - * Copyright (C) 2007 PA Semi, Inc - * - * Authors: Egor Martovetsky - * Olof Johansson - * - * Maintained by: Olof Johansson - * - * Based on arch/powerpc/platforms/cell/cbe_cpufreq.c: - * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ - -#include -#include -#include - -#include -#include -#include -#include -#include - -#define SDCASR_REG 0x0100 -#define SDCASR_REG_STRIDE 0x1000 -#define SDCPWR_CFGA0_REG 0x0100 -#define SDCPWR_PWST0_REG 0x0000 -#define SDCPWR_GIZTIME_REG 0x0440 - -/* SDCPWR_GIZTIME_REG fields */ -#define SDCPWR_GIZTIME_GR 0x80000000 -#define SDCPWR_GIZTIME_LONGLOCK 0x000000ff - -/* Offset of ASR registers from SDC base */ -#define SDCASR_OFFSET 0x120000 - -static void __iomem *sdcpwr_mapbase; -static void __iomem *sdcasr_mapbase; - -static DEFINE_MUTEX(pas_switch_mutex); - -/* Current astate, is used when waking up from power savings on - * one core, in case the other core has switched states during - * the idle time. - */ -static int current_astate; - -/* We support 5(A0-A4) power states excluding turbo(A5-A6) modes */ -static struct cpufreq_frequency_table pas_freqs[] = { - {0, 0}, - {1, 0}, - {2, 0}, - {3, 0}, - {4, 0}, - {0, CPUFREQ_TABLE_END}, -}; - -static struct freq_attr *pas_cpu_freqs_attr[] = { - &cpufreq_freq_attr_scaling_available_freqs, - NULL, -}; - -/* - * hardware specific functions - */ - -static int get_astate_freq(int astate) -{ - u32 ret; - ret = in_le32(sdcpwr_mapbase + SDCPWR_CFGA0_REG + (astate * 0x10)); - - return ret & 0x3f; -} - -static int get_cur_astate(int cpu) -{ - u32 ret; - - ret = in_le32(sdcpwr_mapbase + SDCPWR_PWST0_REG); - ret = (ret >> (cpu * 4)) & 0x7; - - return ret; -} - -static int get_gizmo_latency(void) -{ - u32 giztime, ret; - - giztime = in_le32(sdcpwr_mapbase + SDCPWR_GIZTIME_REG); - - /* just provide the upper bound */ - if (giztime & SDCPWR_GIZTIME_GR) - ret = (giztime & SDCPWR_GIZTIME_LONGLOCK) * 128000; - else - ret = (giztime & SDCPWR_GIZTIME_LONGLOCK) * 1000; - - return ret; -} - -static void set_astate(int cpu, unsigned int astate) -{ - unsigned long flags; - - /* Return if called before init has run */ - if (unlikely(!sdcasr_mapbase)) - return; - - local_irq_save(flags); - - out_le32(sdcasr_mapbase + SDCASR_REG + SDCASR_REG_STRIDE*cpu, astate); - - local_irq_restore(flags); -} - -int check_astate(void) -{ - return get_cur_astate(hard_smp_processor_id()); -} - -void restore_astate(int cpu) -{ - set_astate(cpu, current_astate); -} - -/* - * cpufreq functions - */ - -static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy) -{ - const u32 *max_freqp; - u32 max_freq; - int i, cur_astate; - struct resource res; - struct device_node *cpu, *dn; - int err = -ENODEV; - - cpu = of_get_cpu_node(policy->cpu, NULL); - - if (!cpu) - goto out; - - dn = of_find_compatible_node(NULL, NULL, "1682m-sdc"); - if (!dn) - dn = of_find_compatible_node(NULL, NULL, - "pasemi,pwrficient-sdc"); - if (!dn) - goto out; - err = of_address_to_resource(dn, 0, &res); - of_node_put(dn); - if (err) - goto out; - sdcasr_mapbase = ioremap(res.start + SDCASR_OFFSET, 0x2000); - if (!sdcasr_mapbase) { - err = -EINVAL; - goto out; - } - - dn = of_find_compatible_node(NULL, NULL, "1682m-gizmo"); - if (!dn) - dn = of_find_compatible_node(NULL, NULL, - "pasemi,pwrficient-gizmo"); - if (!dn) { - err = -ENODEV; - goto out_unmap_sdcasr; - } - err = of_address_to_resource(dn, 0, &res); - of_node_put(dn); - if (err) - goto out_unmap_sdcasr; - sdcpwr_mapbase = ioremap(res.start, 0x1000); - if (!sdcpwr_mapbase) { - err = -EINVAL; - goto out_unmap_sdcasr; - } - - pr_debug("init cpufreq on CPU %d\n", policy->cpu); - - max_freqp = of_get_property(cpu, "clock-frequency", NULL); - if (!max_freqp) { - err = -EINVAL; - goto out_unmap_sdcpwr; - } - - /* we need the freq in kHz */ - max_freq = *max_freqp / 1000; - - pr_debug("max clock-frequency is at %u kHz\n", max_freq); - pr_debug("initializing frequency table\n"); - - /* initialize frequency table */ - for (i=0; pas_freqs[i].frequency!=CPUFREQ_TABLE_END; i++) { - pas_freqs[i].frequency = - get_astate_freq(pas_freqs[i].driver_data) * 100000; - pr_debug("%d: %d\n", i, pas_freqs[i].frequency); - } - - policy->cpuinfo.transition_latency = get_gizmo_latency(); - - cur_astate = get_cur_astate(policy->cpu); - pr_debug("current astate is at %d\n",cur_astate); - - policy->cur = pas_freqs[cur_astate].frequency; - cpumask_copy(policy->cpus, cpu_online_mask); - - ppc_proc_freq = policy->cur * 1000ul; - - cpufreq_frequency_table_get_attr(pas_freqs, policy->cpu); - - /* this ensures that policy->cpuinfo_min and policy->cpuinfo_max - * are set correctly - */ - return cpufreq_frequency_table_cpuinfo(policy, pas_freqs); - -out_unmap_sdcpwr: - iounmap(sdcpwr_mapbase); - -out_unmap_sdcasr: - iounmap(sdcasr_mapbase); -out: - return err; -} - -static int pas_cpufreq_cpu_exit(struct cpufreq_policy *policy) -{ - /* - * We don't support CPU hotplug. Don't unmap after the system - * has already made it to a running state. - */ - if (system_state != SYSTEM_BOOTING) - return 0; - - if (sdcasr_mapbase) - iounmap(sdcasr_mapbase); - if (sdcpwr_mapbase) - iounmap(sdcpwr_mapbase); - - cpufreq_frequency_table_put_attr(policy->cpu); - return 0; -} - -static int pas_cpufreq_verify(struct cpufreq_policy *policy) -{ - return cpufreq_frequency_table_verify(policy, pas_freqs); -} - -static int pas_cpufreq_target(struct cpufreq_policy *policy, - unsigned int target_freq, - unsigned int relation) -{ - struct cpufreq_freqs freqs; - int pas_astate_new; - int i; - - cpufreq_frequency_table_target(policy, - pas_freqs, - target_freq, - relation, - &pas_astate_new); - - freqs.old = policy->cur; - freqs.new = pas_freqs[pas_astate_new].frequency; - - mutex_lock(&pas_switch_mutex); - cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); - - pr_debug("setting frequency for cpu %d to %d kHz, 1/%d of max frequency\n", - policy->cpu, - pas_freqs[pas_astate_new].frequency, - pas_freqs[pas_astate_new].driver_data); - - current_astate = pas_astate_new; - - for_each_online_cpu(i) - set_astate(i, pas_astate_new); - - cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); - mutex_unlock(&pas_switch_mutex); - - ppc_proc_freq = freqs.new * 1000ul; - return 0; -} - -static struct cpufreq_driver pas_cpufreq_driver = { - .name = "pas-cpufreq", - .owner = THIS_MODULE, - .flags = CPUFREQ_CONST_LOOPS, - .init = pas_cpufreq_cpu_init, - .exit = pas_cpufreq_cpu_exit, - .verify = pas_cpufreq_verify, - .target = pas_cpufreq_target, - .attr = pas_cpu_freqs_attr, -}; - -/* - * module init and destoy - */ - -static int __init pas_cpufreq_init(void) -{ - if (!of_machine_is_compatible("PA6T-1682M") && - !of_machine_is_compatible("pasemi,pwrficient")) - return -ENODEV; - - return cpufreq_register_driver(&pas_cpufreq_driver); -} - -static void __exit pas_cpufreq_exit(void) -{ - cpufreq_unregister_driver(&pas_cpufreq_driver); -} - -module_init(pas_cpufreq_init); -module_exit(pas_cpufreq_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Egor Martovetsky , Olof Johansson "); diff --git a/arch/powerpc/platforms/powermac/Makefile b/arch/powerpc/platforms/powermac/Makefile index ea47df66fee5..52c6ce1cc985 100644 --- a/arch/powerpc/platforms/powermac/Makefile +++ b/arch/powerpc/platforms/powermac/Makefile @@ -9,8 +9,6 @@ obj-y += pic.o setup.o time.o feature.o pci.o \ sleep.o low_i2c.o cache.o pfunc_core.o \ pfunc_base.o udbg_scc.o udbg_adb.o obj-$(CONFIG_PMAC_BACKLIGHT) += backlight.o -obj-$(CONFIG_CPU_FREQ_PMAC) += cpufreq_32.o -obj-$(CONFIG_CPU_FREQ_PMAC64) += cpufreq_64.o # CONFIG_NVRAM is an arch. independent tristate symbol, for pmac32 we really # need this to be a bool. Cheat here and pretend CONFIG_NVRAM=m is really # CONFIG_NVRAM=y diff --git a/arch/powerpc/platforms/powermac/cpufreq_32.c b/arch/powerpc/platforms/powermac/cpufreq_32.c deleted file mode 100644 index 3104fad82480..000000000000 --- a/arch/powerpc/platforms/powermac/cpufreq_32.c +++ /dev/null @@ -1,721 +0,0 @@ -/* - * Copyright (C) 2002 - 2005 Benjamin Herrenschmidt - * Copyright (C) 2004 John Steele Scott - * - * 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. - * - * TODO: Need a big cleanup here. Basically, we need to have different - * cpufreq_driver structures for the different type of HW instead of the - * current mess. We also need to better deal with the detection of the - * type of machine. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* WARNING !!! This will cause calibrate_delay() to be called, - * but this is an __init function ! So you MUST go edit - * init/main.c to make it non-init before enabling DEBUG_FREQ - */ -#undef DEBUG_FREQ - -extern void low_choose_7447a_dfs(int dfs); -extern void low_choose_750fx_pll(int pll); -extern void low_sleep_handler(void); - -/* - * Currently, PowerMac cpufreq supports only high & low frequencies - * that are set by the firmware - */ -static unsigned int low_freq; -static unsigned int hi_freq; -static unsigned int cur_freq; -static unsigned int sleep_freq; -static unsigned long transition_latency; - -/* - * Different models uses different mechanisms to switch the frequency - */ -static int (*set_speed_proc)(int low_speed); -static unsigned int (*get_speed_proc)(void); - -/* - * Some definitions used by the various speedprocs - */ -static u32 voltage_gpio; -static u32 frequency_gpio; -static u32 slew_done_gpio; -static int no_schedule; -static int has_cpu_l2lve; -static int is_pmu_based; - -/* There are only two frequency states for each processor. Values - * are in kHz for the time being. - */ -#define CPUFREQ_HIGH 0 -#define CPUFREQ_LOW 1 - -static struct cpufreq_frequency_table pmac_cpu_freqs[] = { - {CPUFREQ_HIGH, 0}, - {CPUFREQ_LOW, 0}, - {0, CPUFREQ_TABLE_END}, -}; - -static struct freq_attr* pmac_cpu_freqs_attr[] = { - &cpufreq_freq_attr_scaling_available_freqs, - NULL, -}; - -static inline void local_delay(unsigned long ms) -{ - if (no_schedule) - mdelay(ms); - else - msleep(ms); -} - -#ifdef DEBUG_FREQ -static inline void debug_calc_bogomips(void) -{ - /* This will cause a recalc of bogomips and display the - * result. We backup/restore the value to avoid affecting the - * core cpufreq framework's own calculation. - */ - unsigned long save_lpj = loops_per_jiffy; - calibrate_delay(); - loops_per_jiffy = save_lpj; -} -#endif /* DEBUG_FREQ */ - -/* Switch CPU speed under 750FX CPU control - */ -static int cpu_750fx_cpu_speed(int low_speed) -{ - u32 hid2; - - if (low_speed == 0) { - /* ramping up, set voltage first */ - pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x05); - /* Make sure we sleep for at least 1ms */ - local_delay(10); - - /* tweak L2 for high voltage */ - if (has_cpu_l2lve) { - hid2 = mfspr(SPRN_HID2); - hid2 &= ~0x2000; - mtspr(SPRN_HID2, hid2); - } - } -#ifdef CONFIG_6xx - low_choose_750fx_pll(low_speed); -#endif - if (low_speed == 1) { - /* tweak L2 for low voltage */ - if (has_cpu_l2lve) { - hid2 = mfspr(SPRN_HID2); - hid2 |= 0x2000; - mtspr(SPRN_HID2, hid2); - } - - /* ramping down, set voltage last */ - pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x04); - local_delay(10); - } - - return 0; -} - -static unsigned int cpu_750fx_get_cpu_speed(void) -{ - if (mfspr(SPRN_HID1) & HID1_PS) - return low_freq; - else - return hi_freq; -} - -/* Switch CPU speed using DFS */ -static int dfs_set_cpu_speed(int low_speed) -{ - if (low_speed == 0) { - /* ramping up, set voltage first */ - pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x05); - /* Make sure we sleep for at least 1ms */ - local_delay(1); - } - - /* set frequency */ -#ifdef CONFIG_6xx - low_choose_7447a_dfs(low_speed); -#endif - udelay(100); - - if (low_speed == 1) { - /* ramping down, set voltage last */ - pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x04); - local_delay(1); - } - - return 0; -} - -static unsigned int dfs_get_cpu_speed(void) -{ - if (mfspr(SPRN_HID1) & HID1_DFS) - return low_freq; - else - return hi_freq; -} - - -/* Switch CPU speed using slewing GPIOs - */ -static int gpios_set_cpu_speed(int low_speed) -{ - int gpio, timeout = 0; - - /* If ramping up, set voltage first */ - if (low_speed == 0) { - pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x05); - /* Delay is way too big but it's ok, we schedule */ - local_delay(10); - } - - /* Set frequency */ - gpio = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, frequency_gpio, 0); - if (low_speed == ((gpio & 0x01) == 0)) - goto skip; - - pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, frequency_gpio, - low_speed ? 0x04 : 0x05); - udelay(200); - do { - if (++timeout > 100) - break; - local_delay(1); - gpio = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, slew_done_gpio, 0); - } while((gpio & 0x02) == 0); - skip: - /* If ramping down, set voltage last */ - if (low_speed == 1) { - pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x04); - /* Delay is way too big but it's ok, we schedule */ - local_delay(10); - } - -#ifdef DEBUG_FREQ - debug_calc_bogomips(); -#endif - - return 0; -} - -/* Switch CPU speed under PMU control - */ -static int pmu_set_cpu_speed(int low_speed) -{ - struct adb_request req; - unsigned long save_l2cr; - unsigned long save_l3cr; - unsigned int pic_prio; - unsigned long flags; - - preempt_disable(); - -#ifdef DEBUG_FREQ - printk(KERN_DEBUG "HID1, before: %x\n", mfspr(SPRN_HID1)); -#endif - pmu_suspend(); - - /* Disable all interrupt sources on openpic */ - pic_prio = mpic_cpu_get_priority(); - mpic_cpu_set_priority(0xf); - - /* Make sure the decrementer won't interrupt us */ - asm volatile("mtdec %0" : : "r" (0x7fffffff)); - /* Make sure any pending DEC interrupt occurring while we did - * the above didn't re-enable the DEC */ - mb(); - asm volatile("mtdec %0" : : "r" (0x7fffffff)); - - /* We can now disable MSR_EE */ - local_irq_save(flags); - - /* Giveup the FPU & vec */ - enable_kernel_fp(); - -#ifdef CONFIG_ALTIVEC - if (cpu_has_feature(CPU_FTR_ALTIVEC)) - enable_kernel_altivec(); -#endif /* CONFIG_ALTIVEC */ - - /* Save & disable L2 and L3 caches */ - save_l3cr = _get_L3CR(); /* (returns -1 if not available) */ - save_l2cr = _get_L2CR(); /* (returns -1 if not available) */ - - /* Send the new speed command. My assumption is that this command - * will cause PLL_CFG[0..3] to be changed next time CPU goes to sleep - */ - pmu_request(&req, NULL, 6, PMU_CPU_SPEED, 'W', 'O', 'O', 'F', low_speed); - while (!req.complete) - pmu_poll(); - - /* Prepare the northbridge for the speed transition */ - pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,1,1); - - /* Call low level code to backup CPU state and recover from - * hardware reset - */ - low_sleep_handler(); - - /* Restore the northbridge */ - pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,1,0); - - /* Restore L2 cache */ - if (save_l2cr != 0xffffffff && (save_l2cr & L2CR_L2E) != 0) - _set_L2CR(save_l2cr); - /* Restore L3 cache */ - if (save_l3cr != 0xffffffff && (save_l3cr & L3CR_L3E) != 0) - _set_L3CR(save_l3cr); - - /* Restore userland MMU context */ - switch_mmu_context(NULL, current->active_mm); - -#ifdef DEBUG_FREQ - printk(KERN_DEBUG "HID1, after: %x\n", mfspr(SPRN_HID1)); -#endif - - /* Restore low level PMU operations */ - pmu_unlock(); - - /* - * Restore decrementer; we'll take a decrementer interrupt - * as soon as interrupts are re-enabled and the generic - * clockevents code will reprogram it with the right value. - */ - set_dec(1); - - /* Restore interrupts */ - mpic_cpu_set_priority(pic_prio); - - /* Let interrupts flow again ... */ - local_irq_restore(flags); - -#ifdef DEBUG_FREQ - debug_calc_bogomips(); -#endif - - pmu_resume(); - - preempt_enable(); - - return 0; -} - -static int do_set_cpu_speed(struct cpufreq_policy *policy, int speed_mode, - int notify) -{ - struct cpufreq_freqs freqs; - unsigned long l3cr; - static unsigned long prev_l3cr; - - freqs.old = cur_freq; - freqs.new = (speed_mode == CPUFREQ_HIGH) ? hi_freq : low_freq; - - if (freqs.old == freqs.new) - return 0; - - if (notify) - cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); - if (speed_mode == CPUFREQ_LOW && - cpu_has_feature(CPU_FTR_L3CR)) { - l3cr = _get_L3CR(); - if (l3cr & L3CR_L3E) { - prev_l3cr = l3cr; - _set_L3CR(0); - } - } - set_speed_proc(speed_mode == CPUFREQ_LOW); - if (speed_mode == CPUFREQ_HIGH && - cpu_has_feature(CPU_FTR_L3CR)) { - l3cr = _get_L3CR(); - if ((prev_l3cr & L3CR_L3E) && l3cr != prev_l3cr) - _set_L3CR(prev_l3cr); - } - if (notify) - cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); - cur_freq = (speed_mode == CPUFREQ_HIGH) ? hi_freq : low_freq; - - return 0; -} - -static unsigned int pmac_cpufreq_get_speed(unsigned int cpu) -{ - return cur_freq; -} - -static int pmac_cpufreq_verify(struct cpufreq_policy *policy) -{ - return cpufreq_frequency_table_verify(policy, pmac_cpu_freqs); -} - -static int pmac_cpufreq_target( struct cpufreq_policy *policy, - unsigned int target_freq, - unsigned int relation) -{ - unsigned int newstate = 0; - int rc; - - if (cpufreq_frequency_table_target(policy, pmac_cpu_freqs, - target_freq, relation, &newstate)) - return -EINVAL; - - rc = do_set_cpu_speed(policy, newstate, 1); - - ppc_proc_freq = cur_freq * 1000ul; - return rc; -} - -static int pmac_cpufreq_cpu_init(struct cpufreq_policy *policy) -{ - if (policy->cpu != 0) - return -ENODEV; - - policy->cpuinfo.transition_latency = transition_latency; - policy->cur = cur_freq; - - cpufreq_frequency_table_get_attr(pmac_cpu_freqs, policy->cpu); - return cpufreq_frequency_table_cpuinfo(policy, pmac_cpu_freqs); -} - -static u32 read_gpio(struct device_node *np) -{ - const u32 *reg = of_get_property(np, "reg", NULL); - u32 offset; - - if (reg == NULL) - return 0; - /* That works for all keylargos but shall be fixed properly - * some day... The problem is that it seems we can't rely - * on the "reg" property of the GPIO nodes, they are either - * relative to the base of KeyLargo or to the base of the - * GPIO space, and the device-tree doesn't help. - */ - offset = *reg; - if (offset < KEYLARGO_GPIO_LEVELS0) - offset += KEYLARGO_GPIO_LEVELS0; - return offset; -} - -static int pmac_cpufreq_suspend(struct cpufreq_policy *policy) -{ - /* Ok, this could be made a bit smarter, but let's be robust for now. We - * always force a speed change to high speed before sleep, to make sure - * we have appropriate voltage and/or bus speed for the wakeup process, - * and to make sure our loops_per_jiffies are "good enough", that is will - * not cause too short delays if we sleep in low speed and wake in high - * speed.. - */ - no_schedule = 1; - sleep_freq = cur_freq; - if (cur_freq == low_freq && !is_pmu_based) - do_set_cpu_speed(policy, CPUFREQ_HIGH, 0); - return 0; -} - -static int pmac_cpufreq_resume(struct cpufreq_policy *policy) -{ - /* If we resume, first check if we have a get() function */ - if (get_speed_proc) - cur_freq = get_speed_proc(); - else - cur_freq = 0; - - /* We don't, hrm... we don't really know our speed here, best - * is that we force a switch to whatever it was, which is - * probably high speed due to our suspend() routine - */ - do_set_cpu_speed(policy, sleep_freq == low_freq ? - CPUFREQ_LOW : CPUFREQ_HIGH, 0); - - ppc_proc_freq = cur_freq * 1000ul; - - no_schedule = 0; - return 0; -} - -static struct cpufreq_driver pmac_cpufreq_driver = { - .verify = pmac_cpufreq_verify, - .target = pmac_cpufreq_target, - .get = pmac_cpufreq_get_speed, - .init = pmac_cpufreq_cpu_init, - .suspend = pmac_cpufreq_suspend, - .resume = pmac_cpufreq_resume, - .flags = CPUFREQ_PM_NO_WARN, - .attr = pmac_cpu_freqs_attr, - .name = "powermac", - .owner = THIS_MODULE, -}; - - -static int pmac_cpufreq_init_MacRISC3(struct device_node *cpunode) -{ - struct device_node *volt_gpio_np = of_find_node_by_name(NULL, - "voltage-gpio"); - struct device_node *freq_gpio_np = of_find_node_by_name(NULL, - "frequency-gpio"); - struct device_node *slew_done_gpio_np = of_find_node_by_name(NULL, - "slewing-done"); - const u32 *value; - - /* - * Check to see if it's GPIO driven or PMU only - * - * The way we extract the GPIO address is slightly hackish, but it - * works well enough for now. We need to abstract the whole GPIO - * stuff sooner or later anyway - */ - - if (volt_gpio_np) - voltage_gpio = read_gpio(volt_gpio_np); - if (freq_gpio_np) - frequency_gpio = read_gpio(freq_gpio_np); - if (slew_done_gpio_np) - slew_done_gpio = read_gpio(slew_done_gpio_np); - - /* If we use the frequency GPIOs, calculate the min/max speeds based - * on the bus frequencies - */ - if (frequency_gpio && slew_done_gpio) { - int lenp, rc; - const u32 *freqs, *ratio; - - freqs = of_get_property(cpunode, "bus-frequencies", &lenp); - lenp /= sizeof(u32); - if (freqs == NULL || lenp != 2) { - printk(KERN_ERR "cpufreq: bus-frequencies incorrect or missing\n"); - return 1; - } - ratio = of_get_property(cpunode, "processor-to-bus-ratio*2", - NULL); - if (ratio == NULL) { - printk(KERN_ERR "cpufreq: processor-to-bus-ratio*2 missing\n"); - return 1; - } - - /* Get the min/max bus frequencies */ - low_freq = min(freqs[0], freqs[1]); - hi_freq = max(freqs[0], freqs[1]); - - /* Grrrr.. It _seems_ that the device-tree is lying on the low bus - * frequency, it claims it to be around 84Mhz on some models while - * it appears to be approx. 101Mhz on all. Let's hack around here... - * fortunately, we don't need to be too precise - */ - if (low_freq < 98000000) - low_freq = 101000000; - - /* Convert those to CPU core clocks */ - low_freq = (low_freq * (*ratio)) / 2000; - hi_freq = (hi_freq * (*ratio)) / 2000; - - /* Now we get the frequencies, we read the GPIO to see what is out current - * speed - */ - rc = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, frequency_gpio, 0); - cur_freq = (rc & 0x01) ? hi_freq : low_freq; - - set_speed_proc = gpios_set_cpu_speed; - return 1; - } - - /* If we use the PMU, look for the min & max frequencies in the - * device-tree - */ - value = of_get_property(cpunode, "min-clock-frequency", NULL); - if (!value) - return 1; - low_freq = (*value) / 1000; - /* The PowerBook G4 12" (PowerBook6,1) has an error in the device-tree - * here */ - if (low_freq < 100000) - low_freq *= 10; - - value = of_get_property(cpunode, "max-clock-frequency", NULL); - if (!value) - return 1; - hi_freq = (*value) / 1000; - set_speed_proc = pmu_set_cpu_speed; - is_pmu_based = 1; - - return 0; -} - -static int pmac_cpufreq_init_7447A(struct device_node *cpunode) -{ - struct device_node *volt_gpio_np; - - if (of_get_property(cpunode, "dynamic-power-step", NULL) == NULL) - return 1; - - volt_gpio_np = of_find_node_by_name(NULL, "cpu-vcore-select"); - if (volt_gpio_np) - voltage_gpio = read_gpio(volt_gpio_np); - if (!voltage_gpio){ - printk(KERN_ERR "cpufreq: missing cpu-vcore-select gpio\n"); - return 1; - } - - /* OF only reports the high frequency */ - hi_freq = cur_freq; - low_freq = cur_freq/2; - - /* Read actual frequency from CPU */ - cur_freq = dfs_get_cpu_speed(); - set_speed_proc = dfs_set_cpu_speed; - get_speed_proc = dfs_get_cpu_speed; - - return 0; -} - -static int pmac_cpufreq_init_750FX(struct device_node *cpunode) -{ - struct device_node *volt_gpio_np; - u32 pvr; - const u32 *value; - - if (of_get_property(cpunode, "dynamic-power-step", NULL) == NULL) - return 1; - - hi_freq = cur_freq; - value = of_get_property(cpunode, "reduced-clock-frequency", NULL); - if (!value) - return 1; - low_freq = (*value) / 1000; - - volt_gpio_np = of_find_node_by_name(NULL, "cpu-vcore-select"); - if (volt_gpio_np) - voltage_gpio = read_gpio(volt_gpio_np); - - pvr = mfspr(SPRN_PVR); - has_cpu_l2lve = !((pvr & 0xf00) == 0x100); - - set_speed_proc = cpu_750fx_cpu_speed; - get_speed_proc = cpu_750fx_get_cpu_speed; - cur_freq = cpu_750fx_get_cpu_speed(); - - return 0; -} - -/* Currently, we support the following machines: - * - * - Titanium PowerBook 1Ghz (PMU based, 667Mhz & 1Ghz) - * - Titanium PowerBook 800 (PMU based, 667Mhz & 800Mhz) - * - Titanium PowerBook 400 (PMU based, 300Mhz & 400Mhz) - * - Titanium PowerBook 500 (PMU based, 300Mhz & 500Mhz) - * - iBook2 500/600 (PMU based, 400Mhz & 500/600Mhz) - * - iBook2 700 (CPU based, 400Mhz & 700Mhz, support low voltage) - * - Recent MacRISC3 laptops - * - All new machines with 7447A CPUs - */ -static int __init pmac_cpufreq_setup(void) -{ - struct device_node *cpunode; - const u32 *value; - - if (strstr(cmd_line, "nocpufreq")) - return 0; - - /* Assume only one CPU */ - cpunode = of_find_node_by_type(NULL, "cpu"); - if (!cpunode) - goto out; - - /* Get current cpu clock freq */ - value = of_get_property(cpunode, "clock-frequency", NULL); - if (!value) - goto out; - cur_freq = (*value) / 1000; - transition_latency = CPUFREQ_ETERNAL; - - /* Check for 7447A based MacRISC3 */ - if (of_machine_is_compatible("MacRISC3") && - of_get_property(cpunode, "dynamic-power-step", NULL) && - PVR_VER(mfspr(SPRN_PVR)) == 0x8003) { - pmac_cpufreq_init_7447A(cpunode); - transition_latency = 8000000; - /* Check for other MacRISC3 machines */ - } else if (of_machine_is_compatible("PowerBook3,4") || - of_machine_is_compatible("PowerBook3,5") || - of_machine_is_compatible("MacRISC3")) { - pmac_cpufreq_init_MacRISC3(cpunode); - /* Else check for iBook2 500/600 */ - } else if (of_machine_is_compatible("PowerBook4,1")) { - hi_freq = cur_freq; - low_freq = 400000; - set_speed_proc = pmu_set_cpu_speed; - is_pmu_based = 1; - } - /* Else check for TiPb 550 */ - else if (of_machine_is_compatible("PowerBook3,3") && cur_freq == 550000) { - hi_freq = cur_freq; - low_freq = 500000; - set_speed_proc = pmu_set_cpu_speed; - is_pmu_based = 1; - } - /* Else check for TiPb 400 & 500 */ - else if (of_machine_is_compatible("PowerBook3,2")) { - /* We only know about the 400 MHz and the 500Mhz model - * they both have 300 MHz as low frequency - */ - if (cur_freq < 350000 || cur_freq > 550000) - goto out; - hi_freq = cur_freq; - low_freq = 300000; - set_speed_proc = pmu_set_cpu_speed; - is_pmu_based = 1; - } - /* Else check for 750FX */ - else if (PVR_VER(mfspr(SPRN_PVR)) == 0x7000) - pmac_cpufreq_init_750FX(cpunode); -out: - of_node_put(cpunode); - if (set_speed_proc == NULL) - return -ENODEV; - - pmac_cpu_freqs[CPUFREQ_LOW].frequency = low_freq; - pmac_cpu_freqs[CPUFREQ_HIGH].frequency = hi_freq; - ppc_proc_freq = cur_freq * 1000ul; - - printk(KERN_INFO "Registering PowerMac CPU frequency driver\n"); - printk(KERN_INFO "Low: %d Mhz, High: %d Mhz, Boot: %d Mhz\n", - low_freq/1000, hi_freq/1000, cur_freq/1000); - - return cpufreq_register_driver(&pmac_cpufreq_driver); -} - -module_init(pmac_cpufreq_setup); - diff --git a/arch/powerpc/platforms/powermac/cpufreq_64.c b/arch/powerpc/platforms/powermac/cpufreq_64.c deleted file mode 100644 index 7ba423431cfe..000000000000 --- a/arch/powerpc/platforms/powermac/cpufreq_64.c +++ /dev/null @@ -1,746 +0,0 @@ -/* - * Copyright (C) 2002 - 2005 Benjamin Herrenschmidt - * and Markus Demleitner - * - * 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 driver adds basic cpufreq support for SMU & 970FX based G5 Macs, - * that is iMac G5 and latest single CPU desktop. - */ - -#undef DEBUG - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DBG(fmt...) pr_debug(fmt) - -/* see 970FX user manual */ - -#define SCOM_PCR 0x0aa001 /* PCR scom addr */ - -#define PCR_HILO_SELECT 0x80000000U /* 1 = PCR, 0 = PCRH */ -#define PCR_SPEED_FULL 0x00000000U /* 1:1 speed value */ -#define PCR_SPEED_HALF 0x00020000U /* 1:2 speed value */ -#define PCR_SPEED_QUARTER 0x00040000U /* 1:4 speed value */ -#define PCR_SPEED_MASK 0x000e0000U /* speed mask */ -#define PCR_SPEED_SHIFT 17 -#define PCR_FREQ_REQ_VALID 0x00010000U /* freq request valid */ -#define PCR_VOLT_REQ_VALID 0x00008000U /* volt request valid */ -#define PCR_TARGET_TIME_MASK 0x00006000U /* target time */ -#define PCR_STATLAT_MASK 0x00001f00U /* STATLAT value */ -#define PCR_SNOOPLAT_MASK 0x000000f0U /* SNOOPLAT value */ -#define PCR_SNOOPACC_MASK 0x0000000fU /* SNOOPACC value */ - -#define SCOM_PSR 0x408001 /* PSR scom addr */ -/* warning: PSR is a 64 bits register */ -#define PSR_CMD_RECEIVED 0x2000000000000000U /* command received */ -#define PSR_CMD_COMPLETED 0x1000000000000000U /* command completed */ -#define PSR_CUR_SPEED_MASK 0x0300000000000000U /* current speed */ -#define PSR_CUR_SPEED_SHIFT (56) - -/* - * The G5 only supports two frequencies (Quarter speed is not supported) - */ -#define CPUFREQ_HIGH 0 -#define CPUFREQ_LOW 1 - -static struct cpufreq_frequency_table g5_cpu_freqs[] = { - {CPUFREQ_HIGH, 0}, - {CPUFREQ_LOW, 0}, - {0, CPUFREQ_TABLE_END}, -}; - -static struct freq_attr* g5_cpu_freqs_attr[] = { - &cpufreq_freq_attr_scaling_available_freqs, - NULL, -}; - -/* Power mode data is an array of the 32 bits PCR values to use for - * the various frequencies, retrieved from the device-tree - */ -static int g5_pmode_cur; - -static void (*g5_switch_volt)(int speed_mode); -static int (*g5_switch_freq)(int speed_mode); -static int (*g5_query_freq)(void); - -static DEFINE_MUTEX(g5_switch_mutex); - -static unsigned long transition_latency; - -#ifdef CONFIG_PMAC_SMU - -static const u32 *g5_pmode_data; -static int g5_pmode_max; - -static struct smu_sdbp_fvt *g5_fvt_table; /* table of op. points */ -static int g5_fvt_count; /* number of op. points */ -static int g5_fvt_cur; /* current op. point */ - -/* - * SMU based voltage switching for Neo2 platforms - */ - -static void g5_smu_switch_volt(int speed_mode) -{ - struct smu_simple_cmd cmd; - - DECLARE_COMPLETION_ONSTACK(comp); - smu_queue_simple(&cmd, SMU_CMD_POWER_COMMAND, 8, smu_done_complete, - &comp, 'V', 'S', 'L', 'E', 'W', - 0xff, g5_fvt_cur+1, speed_mode); - wait_for_completion(&comp); -} - -/* - * Platform function based voltage/vdnap switching for Neo2 - */ - -static struct pmf_function *pfunc_set_vdnap0; -static struct pmf_function *pfunc_vdnap0_complete; - -static void g5_vdnap_switch_volt(int speed_mode) -{ - struct pmf_args args; - u32 slew, done = 0; - unsigned long timeout; - - slew = (speed_mode == CPUFREQ_LOW) ? 1 : 0; - args.count = 1; - args.u[0].p = &slew; - - pmf_call_one(pfunc_set_vdnap0, &args); - - /* It's an irq GPIO so we should be able to just block here, - * I'll do that later after I've properly tested the IRQ code for - * platform functions - */ - timeout = jiffies + HZ/10; - while(!time_after(jiffies, timeout)) { - args.count = 1; - args.u[0].p = &done; - pmf_call_one(pfunc_vdnap0_complete, &args); - if (done) - break; - msleep(1); - } - if (done == 0) - printk(KERN_WARNING "cpufreq: Timeout in clock slewing !\n"); -} - - -/* - * SCOM based frequency switching for 970FX rev3 - */ -static int g5_scom_switch_freq(int speed_mode) -{ - unsigned long flags; - int to; - - /* If frequency is going up, first ramp up the voltage */ - if (speed_mode < g5_pmode_cur) - g5_switch_volt(speed_mode); - - local_irq_save(flags); - - /* Clear PCR high */ - scom970_write(SCOM_PCR, 0); - /* Clear PCR low */ - scom970_write(SCOM_PCR, PCR_HILO_SELECT | 0); - /* Set PCR low */ - scom970_write(SCOM_PCR, PCR_HILO_SELECT | - g5_pmode_data[speed_mode]); - - /* Wait for completion */ - for (to = 0; to < 10; to++) { - unsigned long psr = scom970_read(SCOM_PSR); - - if ((psr & PSR_CMD_RECEIVED) == 0 && - (((psr >> PSR_CUR_SPEED_SHIFT) ^ - (g5_pmode_data[speed_mode] >> PCR_SPEED_SHIFT)) & 0x3) - == 0) - break; - if (psr & PSR_CMD_COMPLETED) - break; - udelay(100); - } - - local_irq_restore(flags); - - /* If frequency is going down, last ramp the voltage */ - if (speed_mode > g5_pmode_cur) - g5_switch_volt(speed_mode); - - g5_pmode_cur = speed_mode; - ppc_proc_freq = g5_cpu_freqs[speed_mode].frequency * 1000ul; - - return 0; -} - -static int g5_scom_query_freq(void) -{ - unsigned long psr = scom970_read(SCOM_PSR); - int i; - - for (i = 0; i <= g5_pmode_max; i++) - if ((((psr >> PSR_CUR_SPEED_SHIFT) ^ - (g5_pmode_data[i] >> PCR_SPEED_SHIFT)) & 0x3) == 0) - break; - return i; -} - -/* - * Fake voltage switching for platforms with missing support - */ - -static void g5_dummy_switch_volt(int speed_mode) -{ -} - -#endif /* CONFIG_PMAC_SMU */ - -/* - * Platform function based voltage switching for PowerMac7,2 & 7,3 - */ - -static struct pmf_function *pfunc_cpu0_volt_high; -static struct pmf_function *pfunc_cpu0_volt_low; -static struct pmf_function *pfunc_cpu1_volt_high; -static struct pmf_function *pfunc_cpu1_volt_low; - -static void g5_pfunc_switch_volt(int speed_mode) -{ - if (speed_mode == CPUFREQ_HIGH) { - if (pfunc_cpu0_volt_high) - pmf_call_one(pfunc_cpu0_volt_high, NULL); - if (pfunc_cpu1_volt_high) - pmf_call_one(pfunc_cpu1_volt_high, NULL); - } else { - if (pfunc_cpu0_volt_low) - pmf_call_one(pfunc_cpu0_volt_low, NULL); - if (pfunc_cpu1_volt_low) - pmf_call_one(pfunc_cpu1_volt_low, NULL); - } - msleep(10); /* should be faster , to fix */ -} - -/* - * Platform function based frequency switching for PowerMac7,2 & 7,3 - */ - -static struct pmf_function *pfunc_cpu_setfreq_high; -static struct pmf_function *pfunc_cpu_setfreq_low; -static struct pmf_function *pfunc_cpu_getfreq; -static struct pmf_function *pfunc_slewing_done; - -static int g5_pfunc_switch_freq(int speed_mode) -{ - struct pmf_args args; - u32 done = 0; - unsigned long timeout; - int rc; - - DBG("g5_pfunc_switch_freq(%d)\n", speed_mode); - - /* If frequency is going up, first ramp up the voltage */ - if (speed_mode < g5_pmode_cur) - g5_switch_volt(speed_mode); - - /* Do it */ - if (speed_mode == CPUFREQ_HIGH) - rc = pmf_call_one(pfunc_cpu_setfreq_high, NULL); - else - rc = pmf_call_one(pfunc_cpu_setfreq_low, NULL); - - if (rc) - printk(KERN_WARNING "cpufreq: pfunc switch error %d\n", rc); - - /* It's an irq GPIO so we should be able to just block here, - * I'll do that later after I've properly tested the IRQ code for - * platform functions - */ - timeout = jiffies + HZ/10; - while(!time_after(jiffies, timeout)) { - args.count = 1; - args.u[0].p = &done; - pmf_call_one(pfunc_slewing_done, &args); - if (done) - break; - msleep(1); - } - if (done == 0) - printk(KERN_WARNING "cpufreq: Timeout in clock slewing !\n"); - - /* If frequency is going down, last ramp the voltage */ - if (speed_mode > g5_pmode_cur) - g5_switch_volt(speed_mode); - - g5_pmode_cur = speed_mode; - ppc_proc_freq = g5_cpu_freqs[speed_mode].frequency * 1000ul; - - return 0; -} - -static int g5_pfunc_query_freq(void) -{ - struct pmf_args args; - u32 val = 0; - - args.count = 1; - args.u[0].p = &val; - pmf_call_one(pfunc_cpu_getfreq, &args); - return val ? CPUFREQ_HIGH : CPUFREQ_LOW; -} - - -/* - * Common interface to the cpufreq core - */ - -static int g5_cpufreq_verify(struct cpufreq_policy *policy) -{ - return cpufreq_frequency_table_verify(policy, g5_cpu_freqs); -} - -static int g5_cpufreq_target(struct cpufreq_policy *policy, - unsigned int target_freq, unsigned int relation) -{ - unsigned int newstate = 0; - struct cpufreq_freqs freqs; - int rc; - - if (cpufreq_frequency_table_target(policy, g5_cpu_freqs, - target_freq, relation, &newstate)) - return -EINVAL; - - if (g5_pmode_cur == newstate) - return 0; - - mutex_lock(&g5_switch_mutex); - - freqs.old = g5_cpu_freqs[g5_pmode_cur].frequency; - freqs.new = g5_cpu_freqs[newstate].frequency; - - cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); - rc = g5_switch_freq(newstate); - cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); - - mutex_unlock(&g5_switch_mutex); - - return rc; -} - -static unsigned int g5_cpufreq_get_speed(unsigned int cpu) -{ - return g5_cpu_freqs[g5_pmode_cur].frequency; -} - -static int g5_cpufreq_cpu_init(struct cpufreq_policy *policy) -{ - policy->cpuinfo.transition_latency = transition_latency; - policy->cur = g5_cpu_freqs[g5_query_freq()].frequency; - /* secondary CPUs are tied to the primary one by the - * cpufreq core if in the secondary policy we tell it that - * it actually must be one policy together with all others. */ - cpumask_copy(policy->cpus, cpu_online_mask); - cpufreq_frequency_table_get_attr(g5_cpu_freqs, policy->cpu); - - return cpufreq_frequency_table_cpuinfo(policy, - g5_cpu_freqs); -} - - -static struct cpufreq_driver g5_cpufreq_driver = { - .name = "powermac", - .owner = THIS_MODULE, - .flags = CPUFREQ_CONST_LOOPS, - .init = g5_cpufreq_cpu_init, - .verify = g5_cpufreq_verify, - .target = g5_cpufreq_target, - .get = g5_cpufreq_get_speed, - .attr = g5_cpu_freqs_attr, -}; - - -#ifdef CONFIG_PMAC_SMU - -static int __init g5_neo2_cpufreq_init(struct device_node *cpus) -{ - struct device_node *cpunode; - unsigned int psize, ssize; - unsigned long max_freq; - char *freq_method, *volt_method; - const u32 *valp; - u32 pvr_hi; - int use_volts_vdnap = 0; - int use_volts_smu = 0; - int rc = -ENODEV; - - /* Check supported platforms */ - if (of_machine_is_compatible("PowerMac8,1") || - of_machine_is_compatible("PowerMac8,2") || - of_machine_is_compatible("PowerMac9,1")) - use_volts_smu = 1; - else if (of_machine_is_compatible("PowerMac11,2")) - use_volts_vdnap = 1; - else - return -ENODEV; - - /* Get first CPU node */ - for (cpunode = NULL; - (cpunode = of_get_next_child(cpus, cpunode)) != NULL;) { - const u32 *reg = of_get_property(cpunode, "reg", NULL); - if (reg == NULL || (*reg) != 0) - continue; - if (!strcmp(cpunode->type, "cpu")) - break; - } - if (cpunode == NULL) { - printk(KERN_ERR "cpufreq: Can't find any CPU 0 node\n"); - return -ENODEV; - } - - /* Check 970FX for now */ - valp = of_get_property(cpunode, "cpu-version", NULL); - if (!valp) { - DBG("No cpu-version property !\n"); - goto bail_noprops; - } - pvr_hi = (*valp) >> 16; - if (pvr_hi != 0x3c && pvr_hi != 0x44) { - printk(KERN_ERR "cpufreq: Unsupported CPU version\n"); - goto bail_noprops; - } - - /* Look for the powertune data in the device-tree */ - g5_pmode_data = of_get_property(cpunode, "power-mode-data",&psize); - if (!g5_pmode_data) { - DBG("No power-mode-data !\n"); - goto bail_noprops; - } - g5_pmode_max = psize / sizeof(u32) - 1; - - if (use_volts_smu) { - const struct smu_sdbp_header *shdr; - - /* Look for the FVT table */ - shdr = smu_get_sdb_partition(SMU_SDB_FVT_ID, NULL); - if (!shdr) - goto bail_noprops; - g5_fvt_table = (struct smu_sdbp_fvt *)&shdr[1]; - ssize = (shdr->len * sizeof(u32)) - - sizeof(struct smu_sdbp_header); - g5_fvt_count = ssize / sizeof(struct smu_sdbp_fvt); - g5_fvt_cur = 0; - - /* Sanity checking */ - if (g5_fvt_count < 1 || g5_pmode_max < 1) - goto bail_noprops; - - g5_switch_volt = g5_smu_switch_volt; - volt_method = "SMU"; - } else if (use_volts_vdnap) { - struct device_node *root; - - root = of_find_node_by_path("/"); - if (root == NULL) { - printk(KERN_ERR "cpufreq: Can't find root of " - "device tree\n"); - goto bail_noprops; - } - pfunc_set_vdnap0 = pmf_find_function(root, "set-vdnap0"); - pfunc_vdnap0_complete = - pmf_find_function(root, "slewing-done"); - if (pfunc_set_vdnap0 == NULL || - pfunc_vdnap0_complete == NULL) { - printk(KERN_ERR "cpufreq: Can't find required " - "platform function\n"); - goto bail_noprops; - } - - g5_switch_volt = g5_vdnap_switch_volt; - volt_method = "GPIO"; - } else { - g5_switch_volt = g5_dummy_switch_volt; - volt_method = "none"; - } - - /* - * From what I see, clock-frequency is always the maximal frequency. - * The current driver can not slew sysclk yet, so we really only deal - * with powertune steps for now. We also only implement full freq and - * half freq in this version. So far, I haven't yet seen a machine - * supporting anything else. - */ - valp = of_get_property(cpunode, "clock-frequency", NULL); - if (!valp) - return -ENODEV; - max_freq = (*valp)/1000; - g5_cpu_freqs[0].frequency = max_freq; - g5_cpu_freqs[1].frequency = max_freq/2; - - /* Set callbacks */ - transition_latency = 12000; - g5_switch_freq = g5_scom_switch_freq; - g5_query_freq = g5_scom_query_freq; - freq_method = "SCOM"; - - /* Force apply current frequency to make sure everything is in - * sync (voltage is right for example). Firmware may leave us with - * a strange setting ... - */ - g5_switch_volt(CPUFREQ_HIGH); - msleep(10); - g5_pmode_cur = -1; - g5_switch_freq(g5_query_freq()); - - printk(KERN_INFO "Registering G5 CPU frequency driver\n"); - printk(KERN_INFO "Frequency method: %s, Voltage method: %s\n", - freq_method, volt_method); - printk(KERN_INFO "Low: %d Mhz, High: %d Mhz, Cur: %d MHz\n", - g5_cpu_freqs[1].frequency/1000, - g5_cpu_freqs[0].frequency/1000, - g5_cpu_freqs[g5_pmode_cur].frequency/1000); - - rc = cpufreq_register_driver(&g5_cpufreq_driver); - - /* We keep the CPU node on hold... hopefully, Apple G5 don't have - * hotplug CPU with a dynamic device-tree ... - */ - return rc; - - bail_noprops: - of_node_put(cpunode); - - return rc; -} - -#endif /* CONFIG_PMAC_SMU */ - - -static int __init g5_pm72_cpufreq_init(struct device_node *cpus) -{ - struct device_node *cpuid = NULL, *hwclock = NULL, *cpunode = NULL; - const u8 *eeprom = NULL; - const u32 *valp; - u64 max_freq, min_freq, ih, il; - int has_volt = 1, rc = 0; - - DBG("cpufreq: Initializing for PowerMac7,2, PowerMac7,3 and" - " RackMac3,1...\n"); - - /* Get first CPU node */ - for (cpunode = NULL; - (cpunode = of_get_next_child(cpus, cpunode)) != NULL;) { - if (!strcmp(cpunode->type, "cpu")) - break; - } - if (cpunode == NULL) { - printk(KERN_ERR "cpufreq: Can't find any CPU node\n"); - return -ENODEV; - } - - /* Lookup the cpuid eeprom node */ - cpuid = of_find_node_by_path("/u3@0,f8000000/i2c@f8001000/cpuid@a0"); - if (cpuid != NULL) - eeprom = of_get_property(cpuid, "cpuid", NULL); - if (eeprom == NULL) { - printk(KERN_ERR "cpufreq: Can't find cpuid EEPROM !\n"); - rc = -ENODEV; - goto bail; - } - - /* Lookup the i2c hwclock */ - for (hwclock = NULL; - (hwclock = of_find_node_by_name(hwclock, "i2c-hwclock")) != NULL;){ - const char *loc = of_get_property(hwclock, - "hwctrl-location", NULL); - if (loc == NULL) - continue; - if (strcmp(loc, "CPU CLOCK")) - continue; - if (!of_get_property(hwclock, "platform-get-frequency", NULL)) - continue; - break; - } - if (hwclock == NULL) { - printk(KERN_ERR "cpufreq: Can't find i2c clock chip !\n"); - rc = -ENODEV; - goto bail; - } - - DBG("cpufreq: i2c clock chip found: %s\n", hwclock->full_name); - - /* Now get all the platform functions */ - pfunc_cpu_getfreq = - pmf_find_function(hwclock, "get-frequency"); - pfunc_cpu_setfreq_high = - pmf_find_function(hwclock, "set-frequency-high"); - pfunc_cpu_setfreq_low = - pmf_find_function(hwclock, "set-frequency-low"); - pfunc_slewing_done = - pmf_find_function(hwclock, "slewing-done"); - pfunc_cpu0_volt_high = - pmf_find_function(hwclock, "set-voltage-high-0"); - pfunc_cpu0_volt_low = - pmf_find_function(hwclock, "set-voltage-low-0"); - pfunc_cpu1_volt_high = - pmf_find_function(hwclock, "set-voltage-high-1"); - pfunc_cpu1_volt_low = - pmf_find_function(hwclock, "set-voltage-low-1"); - - /* Check we have minimum requirements */ - if (pfunc_cpu_getfreq == NULL || pfunc_cpu_setfreq_high == NULL || - pfunc_cpu_setfreq_low == NULL || pfunc_slewing_done == NULL) { - printk(KERN_ERR "cpufreq: Can't find platform functions !\n"); - rc = -ENODEV; - goto bail; - } - - /* Check that we have complete sets */ - if (pfunc_cpu0_volt_high == NULL || pfunc_cpu0_volt_low == NULL) { - pmf_put_function(pfunc_cpu0_volt_high); - pmf_put_function(pfunc_cpu0_volt_low); - pfunc_cpu0_volt_high = pfunc_cpu0_volt_low = NULL; - has_volt = 0; - } - if (!has_volt || - pfunc_cpu1_volt_high == NULL || pfunc_cpu1_volt_low == NULL) { - pmf_put_function(pfunc_cpu1_volt_high); - pmf_put_function(pfunc_cpu1_volt_low); - pfunc_cpu1_volt_high = pfunc_cpu1_volt_low = NULL; - } - - /* Note: The device tree also contains a "platform-set-values" - * function for which I haven't quite figured out the usage. It - * might have to be called on init and/or wakeup, I'm not too sure - * but things seem to work fine without it so far ... - */ - - /* Get max frequency from device-tree */ - valp = of_get_property(cpunode, "clock-frequency", NULL); - if (!valp) { - printk(KERN_ERR "cpufreq: Can't find CPU frequency !\n"); - rc = -ENODEV; - goto bail; - } - - max_freq = (*valp)/1000; - - /* Now calculate reduced frequency by using the cpuid input freq - * ratio. This requires 64 bits math unless we are willing to lose - * some precision - */ - ih = *((u32 *)(eeprom + 0x10)); - il = *((u32 *)(eeprom + 0x20)); - - /* Check for machines with no useful settings */ - if (il == ih) { - printk(KERN_WARNING "cpufreq: No low frequency mode available" - " on this model !\n"); - rc = -ENODEV; - goto bail; - } - - min_freq = 0; - if (ih != 0 && il != 0) - min_freq = (max_freq * il) / ih; - - /* Sanity check */ - if (min_freq >= max_freq || min_freq < 1000) { - printk(KERN_ERR "cpufreq: Can't calculate low frequency !\n"); - rc = -ENXIO; - goto bail; - } - g5_cpu_freqs[0].frequency = max_freq; - g5_cpu_freqs[1].frequency = min_freq; - - /* Set callbacks */ - transition_latency = CPUFREQ_ETERNAL; - g5_switch_volt = g5_pfunc_switch_volt; - g5_switch_freq = g5_pfunc_switch_freq; - g5_query_freq = g5_pfunc_query_freq; - - /* Force apply current frequency to make sure everything is in - * sync (voltage is right for example). Firmware may leave us with - * a strange setting ... - */ - g5_switch_volt(CPUFREQ_HIGH); - msleep(10); - g5_pmode_cur = -1; - g5_switch_freq(g5_query_freq()); - - printk(KERN_INFO "Registering G5 CPU frequency driver\n"); - printk(KERN_INFO "Frequency method: i2c/pfunc, " - "Voltage method: %s\n", has_volt ? "i2c/pfunc" : "none"); - printk(KERN_INFO "Low: %d Mhz, High: %d Mhz, Cur: %d MHz\n", - g5_cpu_freqs[1].frequency/1000, - g5_cpu_freqs[0].frequency/1000, - g5_cpu_freqs[g5_pmode_cur].frequency/1000); - - rc = cpufreq_register_driver(&g5_cpufreq_driver); - bail: - if (rc != 0) { - pmf_put_function(pfunc_cpu_getfreq); - pmf_put_function(pfunc_cpu_setfreq_high); - pmf_put_function(pfunc_cpu_setfreq_low); - pmf_put_function(pfunc_slewing_done); - pmf_put_function(pfunc_cpu0_volt_high); - pmf_put_function(pfunc_cpu0_volt_low); - pmf_put_function(pfunc_cpu1_volt_high); - pmf_put_function(pfunc_cpu1_volt_low); - } - of_node_put(hwclock); - of_node_put(cpuid); - of_node_put(cpunode); - - return rc; -} - -static int __init g5_cpufreq_init(void) -{ - struct device_node *cpus; - int rc = 0; - - cpus = of_find_node_by_path("/cpus"); - if (cpus == NULL) { - DBG("No /cpus node !\n"); - return -ENODEV; - } - - if (of_machine_is_compatible("PowerMac7,2") || - of_machine_is_compatible("PowerMac7,3") || - of_machine_is_compatible("RackMac3,1")) - rc = g5_pm72_cpufreq_init(cpus); -#ifdef CONFIG_PMAC_SMU - else - rc = g5_neo2_cpufreq_init(cpus); -#endif /* CONFIG_PMAC_SMU */ - - of_node_put(cpus); - return rc; -} - -module_init(g5_cpufreq_init); - - -MODULE_LICENSE("GPL"); diff --git a/drivers/cpufreq/Kconfig.powerpc b/drivers/cpufreq/Kconfig.powerpc index 88f629e5a393..c48f83c93a96 100644 --- a/drivers/cpufreq/Kconfig.powerpc +++ b/drivers/cpufreq/Kconfig.powerpc @@ -33,3 +33,29 @@ config PPC_CORENET_CPUFREQ This adds the CPUFreq driver support for Freescale e500mc, e5500 and e6500 series SoCs which are capable of changing the CPU's frequency dynamically. + +config CPU_FREQ_PMAC + bool "Support for Apple PowerBooks" + depends on ADB_PMU && PPC32 + select CPU_FREQ_TABLE + help + This adds support for frequency switching on Apple PowerBooks, + this currently includes some models of iBook & Titanium + PowerBook. + +config CPU_FREQ_PMAC64 + bool "Support for some Apple G5s" + depends on PPC_PMAC && PPC64 + select CPU_FREQ_TABLE + help + This adds support for frequency switching on Apple iMac G5, + and some of the more recent desktop G5 machines as well. + +config PPC_PASEMI_CPUFREQ + bool "Support for PA Semi PWRficient" + depends on PPC_PASEMI + select CPU_FREQ_TABLE + default y + help + This adds the support for frequency switching on PA Semi + PWRficient processors. diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index c956094ccc39..2de2af2ca3b1 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -80,6 +80,9 @@ ppc-cbe-cpufreq-y += ppc_cbe_cpufreq_pervasive.o ppc_cbe_cpufreq.o obj-$(CONFIG_CPU_FREQ_CBE_PMI) += ppc_cbe_cpufreq_pmi.o obj-$(CONFIG_CPU_FREQ_MAPLE) += maple-cpufreq.o obj-$(CONFIG_PPC_CORENET_CPUFREQ) += ppc-corenet-cpufreq.o +obj-$(CONFIG_CPU_FREQ_PMAC) += pmac32-cpufreq.o +obj-$(CONFIG_CPU_FREQ_PMAC64) += pmac64-cpufreq.o +obj-$(CONFIG_PPC_PASEMI_CPUFREQ) += pasemi-cpufreq.o ################################################################################## # Other platform drivers diff --git a/drivers/cpufreq/pasemi-cpufreq.c b/drivers/cpufreq/pasemi-cpufreq.c new file mode 100644 index 000000000000..b704da404067 --- /dev/null +++ b/drivers/cpufreq/pasemi-cpufreq.c @@ -0,0 +1,331 @@ +/* + * Copyright (C) 2007 PA Semi, Inc + * + * Authors: Egor Martovetsky + * Olof Johansson + * + * Maintained by: Olof Johansson + * + * Based on arch/powerpc/platforms/cell/cbe_cpufreq.c: + * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#define SDCASR_REG 0x0100 +#define SDCASR_REG_STRIDE 0x1000 +#define SDCPWR_CFGA0_REG 0x0100 +#define SDCPWR_PWST0_REG 0x0000 +#define SDCPWR_GIZTIME_REG 0x0440 + +/* SDCPWR_GIZTIME_REG fields */ +#define SDCPWR_GIZTIME_GR 0x80000000 +#define SDCPWR_GIZTIME_LONGLOCK 0x000000ff + +/* Offset of ASR registers from SDC base */ +#define SDCASR_OFFSET 0x120000 + +static void __iomem *sdcpwr_mapbase; +static void __iomem *sdcasr_mapbase; + +static DEFINE_MUTEX(pas_switch_mutex); + +/* Current astate, is used when waking up from power savings on + * one core, in case the other core has switched states during + * the idle time. + */ +static int current_astate; + +/* We support 5(A0-A4) power states excluding turbo(A5-A6) modes */ +static struct cpufreq_frequency_table pas_freqs[] = { + {0, 0}, + {1, 0}, + {2, 0}, + {3, 0}, + {4, 0}, + {0, CPUFREQ_TABLE_END}, +}; + +static struct freq_attr *pas_cpu_freqs_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + +/* + * hardware specific functions + */ + +static int get_astate_freq(int astate) +{ + u32 ret; + ret = in_le32(sdcpwr_mapbase + SDCPWR_CFGA0_REG + (astate * 0x10)); + + return ret & 0x3f; +} + +static int get_cur_astate(int cpu) +{ + u32 ret; + + ret = in_le32(sdcpwr_mapbase + SDCPWR_PWST0_REG); + ret = (ret >> (cpu * 4)) & 0x7; + + return ret; +} + +static int get_gizmo_latency(void) +{ + u32 giztime, ret; + + giztime = in_le32(sdcpwr_mapbase + SDCPWR_GIZTIME_REG); + + /* just provide the upper bound */ + if (giztime & SDCPWR_GIZTIME_GR) + ret = (giztime & SDCPWR_GIZTIME_LONGLOCK) * 128000; + else + ret = (giztime & SDCPWR_GIZTIME_LONGLOCK) * 1000; + + return ret; +} + +static void set_astate(int cpu, unsigned int astate) +{ + unsigned long flags; + + /* Return if called before init has run */ + if (unlikely(!sdcasr_mapbase)) + return; + + local_irq_save(flags); + + out_le32(sdcasr_mapbase + SDCASR_REG + SDCASR_REG_STRIDE*cpu, astate); + + local_irq_restore(flags); +} + +int check_astate(void) +{ + return get_cur_astate(hard_smp_processor_id()); +} + +void restore_astate(int cpu) +{ + set_astate(cpu, current_astate); +} + +/* + * cpufreq functions + */ + +static int pas_cpufreq_cpu_init(struct cpufreq_policy *policy) +{ + const u32 *max_freqp; + u32 max_freq; + int i, cur_astate; + struct resource res; + struct device_node *cpu, *dn; + int err = -ENODEV; + + cpu = of_get_cpu_node(policy->cpu, NULL); + + if (!cpu) + goto out; + + dn = of_find_compatible_node(NULL, NULL, "1682m-sdc"); + if (!dn) + dn = of_find_compatible_node(NULL, NULL, + "pasemi,pwrficient-sdc"); + if (!dn) + goto out; + err = of_address_to_resource(dn, 0, &res); + of_node_put(dn); + if (err) + goto out; + sdcasr_mapbase = ioremap(res.start + SDCASR_OFFSET, 0x2000); + if (!sdcasr_mapbase) { + err = -EINVAL; + goto out; + } + + dn = of_find_compatible_node(NULL, NULL, "1682m-gizmo"); + if (!dn) + dn = of_find_compatible_node(NULL, NULL, + "pasemi,pwrficient-gizmo"); + if (!dn) { + err = -ENODEV; + goto out_unmap_sdcasr; + } + err = of_address_to_resource(dn, 0, &res); + of_node_put(dn); + if (err) + goto out_unmap_sdcasr; + sdcpwr_mapbase = ioremap(res.start, 0x1000); + if (!sdcpwr_mapbase) { + err = -EINVAL; + goto out_unmap_sdcasr; + } + + pr_debug("init cpufreq on CPU %d\n", policy->cpu); + + max_freqp = of_get_property(cpu, "clock-frequency", NULL); + if (!max_freqp) { + err = -EINVAL; + goto out_unmap_sdcpwr; + } + + /* we need the freq in kHz */ + max_freq = *max_freqp / 1000; + + pr_debug("max clock-frequency is at %u kHz\n", max_freq); + pr_debug("initializing frequency table\n"); + + /* initialize frequency table */ + for (i=0; pas_freqs[i].frequency!=CPUFREQ_TABLE_END; i++) { + pas_freqs[i].frequency = + get_astate_freq(pas_freqs[i].driver_data) * 100000; + pr_debug("%d: %d\n", i, pas_freqs[i].frequency); + } + + policy->cpuinfo.transition_latency = get_gizmo_latency(); + + cur_astate = get_cur_astate(policy->cpu); + pr_debug("current astate is at %d\n",cur_astate); + + policy->cur = pas_freqs[cur_astate].frequency; + cpumask_copy(policy->cpus, cpu_online_mask); + + ppc_proc_freq = policy->cur * 1000ul; + + cpufreq_frequency_table_get_attr(pas_freqs, policy->cpu); + + /* this ensures that policy->cpuinfo_min and policy->cpuinfo_max + * are set correctly + */ + return cpufreq_frequency_table_cpuinfo(policy, pas_freqs); + +out_unmap_sdcpwr: + iounmap(sdcpwr_mapbase); + +out_unmap_sdcasr: + iounmap(sdcasr_mapbase); +out: + return err; +} + +static int pas_cpufreq_cpu_exit(struct cpufreq_policy *policy) +{ + /* + * We don't support CPU hotplug. Don't unmap after the system + * has already made it to a running state. + */ + if (system_state != SYSTEM_BOOTING) + return 0; + + if (sdcasr_mapbase) + iounmap(sdcasr_mapbase); + if (sdcpwr_mapbase) + iounmap(sdcpwr_mapbase); + + cpufreq_frequency_table_put_attr(policy->cpu); + return 0; +} + +static int pas_cpufreq_verify(struct cpufreq_policy *policy) +{ + return cpufreq_frequency_table_verify(policy, pas_freqs); +} + +static int pas_cpufreq_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + struct cpufreq_freqs freqs; + int pas_astate_new; + int i; + + cpufreq_frequency_table_target(policy, + pas_freqs, + target_freq, + relation, + &pas_astate_new); + + freqs.old = policy->cur; + freqs.new = pas_freqs[pas_astate_new].frequency; + + mutex_lock(&pas_switch_mutex); + cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); + + pr_debug("setting frequency for cpu %d to %d kHz, 1/%d of max frequency\n", + policy->cpu, + pas_freqs[pas_astate_new].frequency, + pas_freqs[pas_astate_new].driver_data); + + current_astate = pas_astate_new; + + for_each_online_cpu(i) + set_astate(i, pas_astate_new); + + cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); + mutex_unlock(&pas_switch_mutex); + + ppc_proc_freq = freqs.new * 1000ul; + return 0; +} + +static struct cpufreq_driver pas_cpufreq_driver = { + .name = "pas-cpufreq", + .owner = THIS_MODULE, + .flags = CPUFREQ_CONST_LOOPS, + .init = pas_cpufreq_cpu_init, + .exit = pas_cpufreq_cpu_exit, + .verify = pas_cpufreq_verify, + .target = pas_cpufreq_target, + .attr = pas_cpu_freqs_attr, +}; + +/* + * module init and destoy + */ + +static int __init pas_cpufreq_init(void) +{ + if (!of_machine_is_compatible("PA6T-1682M") && + !of_machine_is_compatible("pasemi,pwrficient")) + return -ENODEV; + + return cpufreq_register_driver(&pas_cpufreq_driver); +} + +static void __exit pas_cpufreq_exit(void) +{ + cpufreq_unregister_driver(&pas_cpufreq_driver); +} + +module_init(pas_cpufreq_init); +module_exit(pas_cpufreq_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Egor Martovetsky , Olof Johansson "); diff --git a/drivers/cpufreq/pmac32-cpufreq.c b/drivers/cpufreq/pmac32-cpufreq.c new file mode 100644 index 000000000000..3104fad82480 --- /dev/null +++ b/drivers/cpufreq/pmac32-cpufreq.c @@ -0,0 +1,721 @@ +/* + * Copyright (C) 2002 - 2005 Benjamin Herrenschmidt + * Copyright (C) 2004 John Steele Scott + * + * 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. + * + * TODO: Need a big cleanup here. Basically, we need to have different + * cpufreq_driver structures for the different type of HW instead of the + * current mess. We also need to better deal with the detection of the + * type of machine. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* WARNING !!! This will cause calibrate_delay() to be called, + * but this is an __init function ! So you MUST go edit + * init/main.c to make it non-init before enabling DEBUG_FREQ + */ +#undef DEBUG_FREQ + +extern void low_choose_7447a_dfs(int dfs); +extern void low_choose_750fx_pll(int pll); +extern void low_sleep_handler(void); + +/* + * Currently, PowerMac cpufreq supports only high & low frequencies + * that are set by the firmware + */ +static unsigned int low_freq; +static unsigned int hi_freq; +static unsigned int cur_freq; +static unsigned int sleep_freq; +static unsigned long transition_latency; + +/* + * Different models uses different mechanisms to switch the frequency + */ +static int (*set_speed_proc)(int low_speed); +static unsigned int (*get_speed_proc)(void); + +/* + * Some definitions used by the various speedprocs + */ +static u32 voltage_gpio; +static u32 frequency_gpio; +static u32 slew_done_gpio; +static int no_schedule; +static int has_cpu_l2lve; +static int is_pmu_based; + +/* There are only two frequency states for each processor. Values + * are in kHz for the time being. + */ +#define CPUFREQ_HIGH 0 +#define CPUFREQ_LOW 1 + +static struct cpufreq_frequency_table pmac_cpu_freqs[] = { + {CPUFREQ_HIGH, 0}, + {CPUFREQ_LOW, 0}, + {0, CPUFREQ_TABLE_END}, +}; + +static struct freq_attr* pmac_cpu_freqs_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + +static inline void local_delay(unsigned long ms) +{ + if (no_schedule) + mdelay(ms); + else + msleep(ms); +} + +#ifdef DEBUG_FREQ +static inline void debug_calc_bogomips(void) +{ + /* This will cause a recalc of bogomips and display the + * result. We backup/restore the value to avoid affecting the + * core cpufreq framework's own calculation. + */ + unsigned long save_lpj = loops_per_jiffy; + calibrate_delay(); + loops_per_jiffy = save_lpj; +} +#endif /* DEBUG_FREQ */ + +/* Switch CPU speed under 750FX CPU control + */ +static int cpu_750fx_cpu_speed(int low_speed) +{ + u32 hid2; + + if (low_speed == 0) { + /* ramping up, set voltage first */ + pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x05); + /* Make sure we sleep for at least 1ms */ + local_delay(10); + + /* tweak L2 for high voltage */ + if (has_cpu_l2lve) { + hid2 = mfspr(SPRN_HID2); + hid2 &= ~0x2000; + mtspr(SPRN_HID2, hid2); + } + } +#ifdef CONFIG_6xx + low_choose_750fx_pll(low_speed); +#endif + if (low_speed == 1) { + /* tweak L2 for low voltage */ + if (has_cpu_l2lve) { + hid2 = mfspr(SPRN_HID2); + hid2 |= 0x2000; + mtspr(SPRN_HID2, hid2); + } + + /* ramping down, set voltage last */ + pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x04); + local_delay(10); + } + + return 0; +} + +static unsigned int cpu_750fx_get_cpu_speed(void) +{ + if (mfspr(SPRN_HID1) & HID1_PS) + return low_freq; + else + return hi_freq; +} + +/* Switch CPU speed using DFS */ +static int dfs_set_cpu_speed(int low_speed) +{ + if (low_speed == 0) { + /* ramping up, set voltage first */ + pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x05); + /* Make sure we sleep for at least 1ms */ + local_delay(1); + } + + /* set frequency */ +#ifdef CONFIG_6xx + low_choose_7447a_dfs(low_speed); +#endif + udelay(100); + + if (low_speed == 1) { + /* ramping down, set voltage last */ + pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x04); + local_delay(1); + } + + return 0; +} + +static unsigned int dfs_get_cpu_speed(void) +{ + if (mfspr(SPRN_HID1) & HID1_DFS) + return low_freq; + else + return hi_freq; +} + + +/* Switch CPU speed using slewing GPIOs + */ +static int gpios_set_cpu_speed(int low_speed) +{ + int gpio, timeout = 0; + + /* If ramping up, set voltage first */ + if (low_speed == 0) { + pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x05); + /* Delay is way too big but it's ok, we schedule */ + local_delay(10); + } + + /* Set frequency */ + gpio = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, frequency_gpio, 0); + if (low_speed == ((gpio & 0x01) == 0)) + goto skip; + + pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, frequency_gpio, + low_speed ? 0x04 : 0x05); + udelay(200); + do { + if (++timeout > 100) + break; + local_delay(1); + gpio = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, slew_done_gpio, 0); + } while((gpio & 0x02) == 0); + skip: + /* If ramping down, set voltage last */ + if (low_speed == 1) { + pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, voltage_gpio, 0x04); + /* Delay is way too big but it's ok, we schedule */ + local_delay(10); + } + +#ifdef DEBUG_FREQ + debug_calc_bogomips(); +#endif + + return 0; +} + +/* Switch CPU speed under PMU control + */ +static int pmu_set_cpu_speed(int low_speed) +{ + struct adb_request req; + unsigned long save_l2cr; + unsigned long save_l3cr; + unsigned int pic_prio; + unsigned long flags; + + preempt_disable(); + +#ifdef DEBUG_FREQ + printk(KERN_DEBUG "HID1, before: %x\n", mfspr(SPRN_HID1)); +#endif + pmu_suspend(); + + /* Disable all interrupt sources on openpic */ + pic_prio = mpic_cpu_get_priority(); + mpic_cpu_set_priority(0xf); + + /* Make sure the decrementer won't interrupt us */ + asm volatile("mtdec %0" : : "r" (0x7fffffff)); + /* Make sure any pending DEC interrupt occurring while we did + * the above didn't re-enable the DEC */ + mb(); + asm volatile("mtdec %0" : : "r" (0x7fffffff)); + + /* We can now disable MSR_EE */ + local_irq_save(flags); + + /* Giveup the FPU & vec */ + enable_kernel_fp(); + +#ifdef CONFIG_ALTIVEC + if (cpu_has_feature(CPU_FTR_ALTIVEC)) + enable_kernel_altivec(); +#endif /* CONFIG_ALTIVEC */ + + /* Save & disable L2 and L3 caches */ + save_l3cr = _get_L3CR(); /* (returns -1 if not available) */ + save_l2cr = _get_L2CR(); /* (returns -1 if not available) */ + + /* Send the new speed command. My assumption is that this command + * will cause PLL_CFG[0..3] to be changed next time CPU goes to sleep + */ + pmu_request(&req, NULL, 6, PMU_CPU_SPEED, 'W', 'O', 'O', 'F', low_speed); + while (!req.complete) + pmu_poll(); + + /* Prepare the northbridge for the speed transition */ + pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,1,1); + + /* Call low level code to backup CPU state and recover from + * hardware reset + */ + low_sleep_handler(); + + /* Restore the northbridge */ + pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,1,0); + + /* Restore L2 cache */ + if (save_l2cr != 0xffffffff && (save_l2cr & L2CR_L2E) != 0) + _set_L2CR(save_l2cr); + /* Restore L3 cache */ + if (save_l3cr != 0xffffffff && (save_l3cr & L3CR_L3E) != 0) + _set_L3CR(save_l3cr); + + /* Restore userland MMU context */ + switch_mmu_context(NULL, current->active_mm); + +#ifdef DEBUG_FREQ + printk(KERN_DEBUG "HID1, after: %x\n", mfspr(SPRN_HID1)); +#endif + + /* Restore low level PMU operations */ + pmu_unlock(); + + /* + * Restore decrementer; we'll take a decrementer interrupt + * as soon as interrupts are re-enabled and the generic + * clockevents code will reprogram it with the right value. + */ + set_dec(1); + + /* Restore interrupts */ + mpic_cpu_set_priority(pic_prio); + + /* Let interrupts flow again ... */ + local_irq_restore(flags); + +#ifdef DEBUG_FREQ + debug_calc_bogomips(); +#endif + + pmu_resume(); + + preempt_enable(); + + return 0; +} + +static int do_set_cpu_speed(struct cpufreq_policy *policy, int speed_mode, + int notify) +{ + struct cpufreq_freqs freqs; + unsigned long l3cr; + static unsigned long prev_l3cr; + + freqs.old = cur_freq; + freqs.new = (speed_mode == CPUFREQ_HIGH) ? hi_freq : low_freq; + + if (freqs.old == freqs.new) + return 0; + + if (notify) + cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); + if (speed_mode == CPUFREQ_LOW && + cpu_has_feature(CPU_FTR_L3CR)) { + l3cr = _get_L3CR(); + if (l3cr & L3CR_L3E) { + prev_l3cr = l3cr; + _set_L3CR(0); + } + } + set_speed_proc(speed_mode == CPUFREQ_LOW); + if (speed_mode == CPUFREQ_HIGH && + cpu_has_feature(CPU_FTR_L3CR)) { + l3cr = _get_L3CR(); + if ((prev_l3cr & L3CR_L3E) && l3cr != prev_l3cr) + _set_L3CR(prev_l3cr); + } + if (notify) + cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); + cur_freq = (speed_mode == CPUFREQ_HIGH) ? hi_freq : low_freq; + + return 0; +} + +static unsigned int pmac_cpufreq_get_speed(unsigned int cpu) +{ + return cur_freq; +} + +static int pmac_cpufreq_verify(struct cpufreq_policy *policy) +{ + return cpufreq_frequency_table_verify(policy, pmac_cpu_freqs); +} + +static int pmac_cpufreq_target( struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + unsigned int newstate = 0; + int rc; + + if (cpufreq_frequency_table_target(policy, pmac_cpu_freqs, + target_freq, relation, &newstate)) + return -EINVAL; + + rc = do_set_cpu_speed(policy, newstate, 1); + + ppc_proc_freq = cur_freq * 1000ul; + return rc; +} + +static int pmac_cpufreq_cpu_init(struct cpufreq_policy *policy) +{ + if (policy->cpu != 0) + return -ENODEV; + + policy->cpuinfo.transition_latency = transition_latency; + policy->cur = cur_freq; + + cpufreq_frequency_table_get_attr(pmac_cpu_freqs, policy->cpu); + return cpufreq_frequency_table_cpuinfo(policy, pmac_cpu_freqs); +} + +static u32 read_gpio(struct device_node *np) +{ + const u32 *reg = of_get_property(np, "reg", NULL); + u32 offset; + + if (reg == NULL) + return 0; + /* That works for all keylargos but shall be fixed properly + * some day... The problem is that it seems we can't rely + * on the "reg" property of the GPIO nodes, they are either + * relative to the base of KeyLargo or to the base of the + * GPIO space, and the device-tree doesn't help. + */ + offset = *reg; + if (offset < KEYLARGO_GPIO_LEVELS0) + offset += KEYLARGO_GPIO_LEVELS0; + return offset; +} + +static int pmac_cpufreq_suspend(struct cpufreq_policy *policy) +{ + /* Ok, this could be made a bit smarter, but let's be robust for now. We + * always force a speed change to high speed before sleep, to make sure + * we have appropriate voltage and/or bus speed for the wakeup process, + * and to make sure our loops_per_jiffies are "good enough", that is will + * not cause too short delays if we sleep in low speed and wake in high + * speed.. + */ + no_schedule = 1; + sleep_freq = cur_freq; + if (cur_freq == low_freq && !is_pmu_based) + do_set_cpu_speed(policy, CPUFREQ_HIGH, 0); + return 0; +} + +static int pmac_cpufreq_resume(struct cpufreq_policy *policy) +{ + /* If we resume, first check if we have a get() function */ + if (get_speed_proc) + cur_freq = get_speed_proc(); + else + cur_freq = 0; + + /* We don't, hrm... we don't really know our speed here, best + * is that we force a switch to whatever it was, which is + * probably high speed due to our suspend() routine + */ + do_set_cpu_speed(policy, sleep_freq == low_freq ? + CPUFREQ_LOW : CPUFREQ_HIGH, 0); + + ppc_proc_freq = cur_freq * 1000ul; + + no_schedule = 0; + return 0; +} + +static struct cpufreq_driver pmac_cpufreq_driver = { + .verify = pmac_cpufreq_verify, + .target = pmac_cpufreq_target, + .get = pmac_cpufreq_get_speed, + .init = pmac_cpufreq_cpu_init, + .suspend = pmac_cpufreq_suspend, + .resume = pmac_cpufreq_resume, + .flags = CPUFREQ_PM_NO_WARN, + .attr = pmac_cpu_freqs_attr, + .name = "powermac", + .owner = THIS_MODULE, +}; + + +static int pmac_cpufreq_init_MacRISC3(struct device_node *cpunode) +{ + struct device_node *volt_gpio_np = of_find_node_by_name(NULL, + "voltage-gpio"); + struct device_node *freq_gpio_np = of_find_node_by_name(NULL, + "frequency-gpio"); + struct device_node *slew_done_gpio_np = of_find_node_by_name(NULL, + "slewing-done"); + const u32 *value; + + /* + * Check to see if it's GPIO driven or PMU only + * + * The way we extract the GPIO address is slightly hackish, but it + * works well enough for now. We need to abstract the whole GPIO + * stuff sooner or later anyway + */ + + if (volt_gpio_np) + voltage_gpio = read_gpio(volt_gpio_np); + if (freq_gpio_np) + frequency_gpio = read_gpio(freq_gpio_np); + if (slew_done_gpio_np) + slew_done_gpio = read_gpio(slew_done_gpio_np); + + /* If we use the frequency GPIOs, calculate the min/max speeds based + * on the bus frequencies + */ + if (frequency_gpio && slew_done_gpio) { + int lenp, rc; + const u32 *freqs, *ratio; + + freqs = of_get_property(cpunode, "bus-frequencies", &lenp); + lenp /= sizeof(u32); + if (freqs == NULL || lenp != 2) { + printk(KERN_ERR "cpufreq: bus-frequencies incorrect or missing\n"); + return 1; + } + ratio = of_get_property(cpunode, "processor-to-bus-ratio*2", + NULL); + if (ratio == NULL) { + printk(KERN_ERR "cpufreq: processor-to-bus-ratio*2 missing\n"); + return 1; + } + + /* Get the min/max bus frequencies */ + low_freq = min(freqs[0], freqs[1]); + hi_freq = max(freqs[0], freqs[1]); + + /* Grrrr.. It _seems_ that the device-tree is lying on the low bus + * frequency, it claims it to be around 84Mhz on some models while + * it appears to be approx. 101Mhz on all. Let's hack around here... + * fortunately, we don't need to be too precise + */ + if (low_freq < 98000000) + low_freq = 101000000; + + /* Convert those to CPU core clocks */ + low_freq = (low_freq * (*ratio)) / 2000; + hi_freq = (hi_freq * (*ratio)) / 2000; + + /* Now we get the frequencies, we read the GPIO to see what is out current + * speed + */ + rc = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, frequency_gpio, 0); + cur_freq = (rc & 0x01) ? hi_freq : low_freq; + + set_speed_proc = gpios_set_cpu_speed; + return 1; + } + + /* If we use the PMU, look for the min & max frequencies in the + * device-tree + */ + value = of_get_property(cpunode, "min-clock-frequency", NULL); + if (!value) + return 1; + low_freq = (*value) / 1000; + /* The PowerBook G4 12" (PowerBook6,1) has an error in the device-tree + * here */ + if (low_freq < 100000) + low_freq *= 10; + + value = of_get_property(cpunode, "max-clock-frequency", NULL); + if (!value) + return 1; + hi_freq = (*value) / 1000; + set_speed_proc = pmu_set_cpu_speed; + is_pmu_based = 1; + + return 0; +} + +static int pmac_cpufreq_init_7447A(struct device_node *cpunode) +{ + struct device_node *volt_gpio_np; + + if (of_get_property(cpunode, "dynamic-power-step", NULL) == NULL) + return 1; + + volt_gpio_np = of_find_node_by_name(NULL, "cpu-vcore-select"); + if (volt_gpio_np) + voltage_gpio = read_gpio(volt_gpio_np); + if (!voltage_gpio){ + printk(KERN_ERR "cpufreq: missing cpu-vcore-select gpio\n"); + return 1; + } + + /* OF only reports the high frequency */ + hi_freq = cur_freq; + low_freq = cur_freq/2; + + /* Read actual frequency from CPU */ + cur_freq = dfs_get_cpu_speed(); + set_speed_proc = dfs_set_cpu_speed; + get_speed_proc = dfs_get_cpu_speed; + + return 0; +} + +static int pmac_cpufreq_init_750FX(struct device_node *cpunode) +{ + struct device_node *volt_gpio_np; + u32 pvr; + const u32 *value; + + if (of_get_property(cpunode, "dynamic-power-step", NULL) == NULL) + return 1; + + hi_freq = cur_freq; + value = of_get_property(cpunode, "reduced-clock-frequency", NULL); + if (!value) + return 1; + low_freq = (*value) / 1000; + + volt_gpio_np = of_find_node_by_name(NULL, "cpu-vcore-select"); + if (volt_gpio_np) + voltage_gpio = read_gpio(volt_gpio_np); + + pvr = mfspr(SPRN_PVR); + has_cpu_l2lve = !((pvr & 0xf00) == 0x100); + + set_speed_proc = cpu_750fx_cpu_speed; + get_speed_proc = cpu_750fx_get_cpu_speed; + cur_freq = cpu_750fx_get_cpu_speed(); + + return 0; +} + +/* Currently, we support the following machines: + * + * - Titanium PowerBook 1Ghz (PMU based, 667Mhz & 1Ghz) + * - Titanium PowerBook 800 (PMU based, 667Mhz & 800Mhz) + * - Titanium PowerBook 400 (PMU based, 300Mhz & 400Mhz) + * - Titanium PowerBook 500 (PMU based, 300Mhz & 500Mhz) + * - iBook2 500/600 (PMU based, 400Mhz & 500/600Mhz) + * - iBook2 700 (CPU based, 400Mhz & 700Mhz, support low voltage) + * - Recent MacRISC3 laptops + * - All new machines with 7447A CPUs + */ +static int __init pmac_cpufreq_setup(void) +{ + struct device_node *cpunode; + const u32 *value; + + if (strstr(cmd_line, "nocpufreq")) + return 0; + + /* Assume only one CPU */ + cpunode = of_find_node_by_type(NULL, "cpu"); + if (!cpunode) + goto out; + + /* Get current cpu clock freq */ + value = of_get_property(cpunode, "clock-frequency", NULL); + if (!value) + goto out; + cur_freq = (*value) / 1000; + transition_latency = CPUFREQ_ETERNAL; + + /* Check for 7447A based MacRISC3 */ + if (of_machine_is_compatible("MacRISC3") && + of_get_property(cpunode, "dynamic-power-step", NULL) && + PVR_VER(mfspr(SPRN_PVR)) == 0x8003) { + pmac_cpufreq_init_7447A(cpunode); + transition_latency = 8000000; + /* Check for other MacRISC3 machines */ + } else if (of_machine_is_compatible("PowerBook3,4") || + of_machine_is_compatible("PowerBook3,5") || + of_machine_is_compatible("MacRISC3")) { + pmac_cpufreq_init_MacRISC3(cpunode); + /* Else check for iBook2 500/600 */ + } else if (of_machine_is_compatible("PowerBook4,1")) { + hi_freq = cur_freq; + low_freq = 400000; + set_speed_proc = pmu_set_cpu_speed; + is_pmu_based = 1; + } + /* Else check for TiPb 550 */ + else if (of_machine_is_compatible("PowerBook3,3") && cur_freq == 550000) { + hi_freq = cur_freq; + low_freq = 500000; + set_speed_proc = pmu_set_cpu_speed; + is_pmu_based = 1; + } + /* Else check for TiPb 400 & 500 */ + else if (of_machine_is_compatible("PowerBook3,2")) { + /* We only know about the 400 MHz and the 500Mhz model + * they both have 300 MHz as low frequency + */ + if (cur_freq < 350000 || cur_freq > 550000) + goto out; + hi_freq = cur_freq; + low_freq = 300000; + set_speed_proc = pmu_set_cpu_speed; + is_pmu_based = 1; + } + /* Else check for 750FX */ + else if (PVR_VER(mfspr(SPRN_PVR)) == 0x7000) + pmac_cpufreq_init_750FX(cpunode); +out: + of_node_put(cpunode); + if (set_speed_proc == NULL) + return -ENODEV; + + pmac_cpu_freqs[CPUFREQ_LOW].frequency = low_freq; + pmac_cpu_freqs[CPUFREQ_HIGH].frequency = hi_freq; + ppc_proc_freq = cur_freq * 1000ul; + + printk(KERN_INFO "Registering PowerMac CPU frequency driver\n"); + printk(KERN_INFO "Low: %d Mhz, High: %d Mhz, Boot: %d Mhz\n", + low_freq/1000, hi_freq/1000, cur_freq/1000); + + return cpufreq_register_driver(&pmac_cpufreq_driver); +} + +module_init(pmac_cpufreq_setup); + diff --git a/drivers/cpufreq/pmac64-cpufreq.c b/drivers/cpufreq/pmac64-cpufreq.c new file mode 100644 index 000000000000..7ba423431cfe --- /dev/null +++ b/drivers/cpufreq/pmac64-cpufreq.c @@ -0,0 +1,746 @@ +/* + * Copyright (C) 2002 - 2005 Benjamin Herrenschmidt + * and Markus Demleitner + * + * 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 driver adds basic cpufreq support for SMU & 970FX based G5 Macs, + * that is iMac G5 and latest single CPU desktop. + */ + +#undef DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DBG(fmt...) pr_debug(fmt) + +/* see 970FX user manual */ + +#define SCOM_PCR 0x0aa001 /* PCR scom addr */ + +#define PCR_HILO_SELECT 0x80000000U /* 1 = PCR, 0 = PCRH */ +#define PCR_SPEED_FULL 0x00000000U /* 1:1 speed value */ +#define PCR_SPEED_HALF 0x00020000U /* 1:2 speed value */ +#define PCR_SPEED_QUARTER 0x00040000U /* 1:4 speed value */ +#define PCR_SPEED_MASK 0x000e0000U /* speed mask */ +#define PCR_SPEED_SHIFT 17 +#define PCR_FREQ_REQ_VALID 0x00010000U /* freq request valid */ +#define PCR_VOLT_REQ_VALID 0x00008000U /* volt request valid */ +#define PCR_TARGET_TIME_MASK 0x00006000U /* target time */ +#define PCR_STATLAT_MASK 0x00001f00U /* STATLAT value */ +#define PCR_SNOOPLAT_MASK 0x000000f0U /* SNOOPLAT value */ +#define PCR_SNOOPACC_MASK 0x0000000fU /* SNOOPACC value */ + +#define SCOM_PSR 0x408001 /* PSR scom addr */ +/* warning: PSR is a 64 bits register */ +#define PSR_CMD_RECEIVED 0x2000000000000000U /* command received */ +#define PSR_CMD_COMPLETED 0x1000000000000000U /* command completed */ +#define PSR_CUR_SPEED_MASK 0x0300000000000000U /* current speed */ +#define PSR_CUR_SPEED_SHIFT (56) + +/* + * The G5 only supports two frequencies (Quarter speed is not supported) + */ +#define CPUFREQ_HIGH 0 +#define CPUFREQ_LOW 1 + +static struct cpufreq_frequency_table g5_cpu_freqs[] = { + {CPUFREQ_HIGH, 0}, + {CPUFREQ_LOW, 0}, + {0, CPUFREQ_TABLE_END}, +}; + +static struct freq_attr* g5_cpu_freqs_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + +/* Power mode data is an array of the 32 bits PCR values to use for + * the various frequencies, retrieved from the device-tree + */ +static int g5_pmode_cur; + +static void (*g5_switch_volt)(int speed_mode); +static int (*g5_switch_freq)(int speed_mode); +static int (*g5_query_freq)(void); + +static DEFINE_MUTEX(g5_switch_mutex); + +static unsigned long transition_latency; + +#ifdef CONFIG_PMAC_SMU + +static const u32 *g5_pmode_data; +static int g5_pmode_max; + +static struct smu_sdbp_fvt *g5_fvt_table; /* table of op. points */ +static int g5_fvt_count; /* number of op. points */ +static int g5_fvt_cur; /* current op. point */ + +/* + * SMU based voltage switching for Neo2 platforms + */ + +static void g5_smu_switch_volt(int speed_mode) +{ + struct smu_simple_cmd cmd; + + DECLARE_COMPLETION_ONSTACK(comp); + smu_queue_simple(&cmd, SMU_CMD_POWER_COMMAND, 8, smu_done_complete, + &comp, 'V', 'S', 'L', 'E', 'W', + 0xff, g5_fvt_cur+1, speed_mode); + wait_for_completion(&comp); +} + +/* + * Platform function based voltage/vdnap switching for Neo2 + */ + +static struct pmf_function *pfunc_set_vdnap0; +static struct pmf_function *pfunc_vdnap0_complete; + +static void g5_vdnap_switch_volt(int speed_mode) +{ + struct pmf_args args; + u32 slew, done = 0; + unsigned long timeout; + + slew = (speed_mode == CPUFREQ_LOW) ? 1 : 0; + args.count = 1; + args.u[0].p = &slew; + + pmf_call_one(pfunc_set_vdnap0, &args); + + /* It's an irq GPIO so we should be able to just block here, + * I'll do that later after I've properly tested the IRQ code for + * platform functions + */ + timeout = jiffies + HZ/10; + while(!time_after(jiffies, timeout)) { + args.count = 1; + args.u[0].p = &done; + pmf_call_one(pfunc_vdnap0_complete, &args); + if (done) + break; + msleep(1); + } + if (done == 0) + printk(KERN_WARNING "cpufreq: Timeout in clock slewing !\n"); +} + + +/* + * SCOM based frequency switching for 970FX rev3 + */ +static int g5_scom_switch_freq(int speed_mode) +{ + unsigned long flags; + int to; + + /* If frequency is going up, first ramp up the voltage */ + if (speed_mode < g5_pmode_cur) + g5_switch_volt(speed_mode); + + local_irq_save(flags); + + /* Clear PCR high */ + scom970_write(SCOM_PCR, 0); + /* Clear PCR low */ + scom970_write(SCOM_PCR, PCR_HILO_SELECT | 0); + /* Set PCR low */ + scom970_write(SCOM_PCR, PCR_HILO_SELECT | + g5_pmode_data[speed_mode]); + + /* Wait for completion */ + for (to = 0; to < 10; to++) { + unsigned long psr = scom970_read(SCOM_PSR); + + if ((psr & PSR_CMD_RECEIVED) == 0 && + (((psr >> PSR_CUR_SPEED_SHIFT) ^ + (g5_pmode_data[speed_mode] >> PCR_SPEED_SHIFT)) & 0x3) + == 0) + break; + if (psr & PSR_CMD_COMPLETED) + break; + udelay(100); + } + + local_irq_restore(flags); + + /* If frequency is going down, last ramp the voltage */ + if (speed_mode > g5_pmode_cur) + g5_switch_volt(speed_mode); + + g5_pmode_cur = speed_mode; + ppc_proc_freq = g5_cpu_freqs[speed_mode].frequency * 1000ul; + + return 0; +} + +static int g5_scom_query_freq(void) +{ + unsigned long psr = scom970_read(SCOM_PSR); + int i; + + for (i = 0; i <= g5_pmode_max; i++) + if ((((psr >> PSR_CUR_SPEED_SHIFT) ^ + (g5_pmode_data[i] >> PCR_SPEED_SHIFT)) & 0x3) == 0) + break; + return i; +} + +/* + * Fake voltage switching for platforms with missing support + */ + +static void g5_dummy_switch_volt(int speed_mode) +{ +} + +#endif /* CONFIG_PMAC_SMU */ + +/* + * Platform function based voltage switching for PowerMac7,2 & 7,3 + */ + +static struct pmf_function *pfunc_cpu0_volt_high; +static struct pmf_function *pfunc_cpu0_volt_low; +static struct pmf_function *pfunc_cpu1_volt_high; +static struct pmf_function *pfunc_cpu1_volt_low; + +static void g5_pfunc_switch_volt(int speed_mode) +{ + if (speed_mode == CPUFREQ_HIGH) { + if (pfunc_cpu0_volt_high) + pmf_call_one(pfunc_cpu0_volt_high, NULL); + if (pfunc_cpu1_volt_high) + pmf_call_one(pfunc_cpu1_volt_high, NULL); + } else { + if (pfunc_cpu0_volt_low) + pmf_call_one(pfunc_cpu0_volt_low, NULL); + if (pfunc_cpu1_volt_low) + pmf_call_one(pfunc_cpu1_volt_low, NULL); + } + msleep(10); /* should be faster , to fix */ +} + +/* + * Platform function based frequency switching for PowerMac7,2 & 7,3 + */ + +static struct pmf_function *pfunc_cpu_setfreq_high; +static struct pmf_function *pfunc_cpu_setfreq_low; +static struct pmf_function *pfunc_cpu_getfreq; +static struct pmf_function *pfunc_slewing_done; + +static int g5_pfunc_switch_freq(int speed_mode) +{ + struct pmf_args args; + u32 done = 0; + unsigned long timeout; + int rc; + + DBG("g5_pfunc_switch_freq(%d)\n", speed_mode); + + /* If frequency is going up, first ramp up the voltage */ + if (speed_mode < g5_pmode_cur) + g5_switch_volt(speed_mode); + + /* Do it */ + if (speed_mode == CPUFREQ_HIGH) + rc = pmf_call_one(pfunc_cpu_setfreq_high, NULL); + else + rc = pmf_call_one(pfunc_cpu_setfreq_low, NULL); + + if (rc) + printk(KERN_WARNING "cpufreq: pfunc switch error %d\n", rc); + + /* It's an irq GPIO so we should be able to just block here, + * I'll do that later after I've properly tested the IRQ code for + * platform functions + */ + timeout = jiffies + HZ/10; + while(!time_after(jiffies, timeout)) { + args.count = 1; + args.u[0].p = &done; + pmf_call_one(pfunc_slewing_done, &args); + if (done) + break; + msleep(1); + } + if (done == 0) + printk(KERN_WARNING "cpufreq: Timeout in clock slewing !\n"); + + /* If frequency is going down, last ramp the voltage */ + if (speed_mode > g5_pmode_cur) + g5_switch_volt(speed_mode); + + g5_pmode_cur = speed_mode; + ppc_proc_freq = g5_cpu_freqs[speed_mode].frequency * 1000ul; + + return 0; +} + +static int g5_pfunc_query_freq(void) +{ + struct pmf_args args; + u32 val = 0; + + args.count = 1; + args.u[0].p = &val; + pmf_call_one(pfunc_cpu_getfreq, &args); + return val ? CPUFREQ_HIGH : CPUFREQ_LOW; +} + + +/* + * Common interface to the cpufreq core + */ + +static int g5_cpufreq_verify(struct cpufreq_policy *policy) +{ + return cpufreq_frequency_table_verify(policy, g5_cpu_freqs); +} + +static int g5_cpufreq_target(struct cpufreq_policy *policy, + unsigned int target_freq, unsigned int relation) +{ + unsigned int newstate = 0; + struct cpufreq_freqs freqs; + int rc; + + if (cpufreq_frequency_table_target(policy, g5_cpu_freqs, + target_freq, relation, &newstate)) + return -EINVAL; + + if (g5_pmode_cur == newstate) + return 0; + + mutex_lock(&g5_switch_mutex); + + freqs.old = g5_cpu_freqs[g5_pmode_cur].frequency; + freqs.new = g5_cpu_freqs[newstate].frequency; + + cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); + rc = g5_switch_freq(newstate); + cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); + + mutex_unlock(&g5_switch_mutex); + + return rc; +} + +static unsigned int g5_cpufreq_get_speed(unsigned int cpu) +{ + return g5_cpu_freqs[g5_pmode_cur].frequency; +} + +static int g5_cpufreq_cpu_init(struct cpufreq_policy *policy) +{ + policy->cpuinfo.transition_latency = transition_latency; + policy->cur = g5_cpu_freqs[g5_query_freq()].frequency; + /* secondary CPUs are tied to the primary one by the + * cpufreq core if in the secondary policy we tell it that + * it actually must be one policy together with all others. */ + cpumask_copy(policy->cpus, cpu_online_mask); + cpufreq_frequency_table_get_attr(g5_cpu_freqs, policy->cpu); + + return cpufreq_frequency_table_cpuinfo(policy, + g5_cpu_freqs); +} + + +static struct cpufreq_driver g5_cpufreq_driver = { + .name = "powermac", + .owner = THIS_MODULE, + .flags = CPUFREQ_CONST_LOOPS, + .init = g5_cpufreq_cpu_init, + .verify = g5_cpufreq_verify, + .target = g5_cpufreq_target, + .get = g5_cpufreq_get_speed, + .attr = g5_cpu_freqs_attr, +}; + + +#ifdef CONFIG_PMAC_SMU + +static int __init g5_neo2_cpufreq_init(struct device_node *cpus) +{ + struct device_node *cpunode; + unsigned int psize, ssize; + unsigned long max_freq; + char *freq_method, *volt_method; + const u32 *valp; + u32 pvr_hi; + int use_volts_vdnap = 0; + int use_volts_smu = 0; + int rc = -ENODEV; + + /* Check supported platforms */ + if (of_machine_is_compatible("PowerMac8,1") || + of_machine_is_compatible("PowerMac8,2") || + of_machine_is_compatible("PowerMac9,1")) + use_volts_smu = 1; + else if (of_machine_is_compatible("PowerMac11,2")) + use_volts_vdnap = 1; + else + return -ENODEV; + + /* Get first CPU node */ + for (cpunode = NULL; + (cpunode = of_get_next_child(cpus, cpunode)) != NULL;) { + const u32 *reg = of_get_property(cpunode, "reg", NULL); + if (reg == NULL || (*reg) != 0) + continue; + if (!strcmp(cpunode->type, "cpu")) + break; + } + if (cpunode == NULL) { + printk(KERN_ERR "cpufreq: Can't find any CPU 0 node\n"); + return -ENODEV; + } + + /* Check 970FX for now */ + valp = of_get_property(cpunode, "cpu-version", NULL); + if (!valp) { + DBG("No cpu-version property !\n"); + goto bail_noprops; + } + pvr_hi = (*valp) >> 16; + if (pvr_hi != 0x3c && pvr_hi != 0x44) { + printk(KERN_ERR "cpufreq: Unsupported CPU version\n"); + goto bail_noprops; + } + + /* Look for the powertune data in the device-tree */ + g5_pmode_data = of_get_property(cpunode, "power-mode-data",&psize); + if (!g5_pmode_data) { + DBG("No power-mode-data !\n"); + goto bail_noprops; + } + g5_pmode_max = psize / sizeof(u32) - 1; + + if (use_volts_smu) { + const struct smu_sdbp_header *shdr; + + /* Look for the FVT table */ + shdr = smu_get_sdb_partition(SMU_SDB_FVT_ID, NULL); + if (!shdr) + goto bail_noprops; + g5_fvt_table = (struct smu_sdbp_fvt *)&shdr[1]; + ssize = (shdr->len * sizeof(u32)) - + sizeof(struct smu_sdbp_header); + g5_fvt_count = ssize / sizeof(struct smu_sdbp_fvt); + g5_fvt_cur = 0; + + /* Sanity checking */ + if (g5_fvt_count < 1 || g5_pmode_max < 1) + goto bail_noprops; + + g5_switch_volt = g5_smu_switch_volt; + volt_method = "SMU"; + } else if (use_volts_vdnap) { + struct device_node *root; + + root = of_find_node_by_path("/"); + if (root == NULL) { + printk(KERN_ERR "cpufreq: Can't find root of " + "device tree\n"); + goto bail_noprops; + } + pfunc_set_vdnap0 = pmf_find_function(root, "set-vdnap0"); + pfunc_vdnap0_complete = + pmf_find_function(root, "slewing-done"); + if (pfunc_set_vdnap0 == NULL || + pfunc_vdnap0_complete == NULL) { + printk(KERN_ERR "cpufreq: Can't find required " + "platform function\n"); + goto bail_noprops; + } + + g5_switch_volt = g5_vdnap_switch_volt; + volt_method = "GPIO"; + } else { + g5_switch_volt = g5_dummy_switch_volt; + volt_method = "none"; + } + + /* + * From what I see, clock-frequency is always the maximal frequency. + * The current driver can not slew sysclk yet, so we really only deal + * with powertune steps for now. We also only implement full freq and + * half freq in this version. So far, I haven't yet seen a machine + * supporting anything else. + */ + valp = of_get_property(cpunode, "clock-frequency", NULL); + if (!valp) + return -ENODEV; + max_freq = (*valp)/1000; + g5_cpu_freqs[0].frequency = max_freq; + g5_cpu_freqs[1].frequency = max_freq/2; + + /* Set callbacks */ + transition_latency = 12000; + g5_switch_freq = g5_scom_switch_freq; + g5_query_freq = g5_scom_query_freq; + freq_method = "SCOM"; + + /* Force apply current frequency to make sure everything is in + * sync (voltage is right for example). Firmware may leave us with + * a strange setting ... + */ + g5_switch_volt(CPUFREQ_HIGH); + msleep(10); + g5_pmode_cur = -1; + g5_switch_freq(g5_query_freq()); + + printk(KERN_INFO "Registering G5 CPU frequency driver\n"); + printk(KERN_INFO "Frequency method: %s, Voltage method: %s\n", + freq_method, volt_method); + printk(KERN_INFO "Low: %d Mhz, High: %d Mhz, Cur: %d MHz\n", + g5_cpu_freqs[1].frequency/1000, + g5_cpu_freqs[0].frequency/1000, + g5_cpu_freqs[g5_pmode_cur].frequency/1000); + + rc = cpufreq_register_driver(&g5_cpufreq_driver); + + /* We keep the CPU node on hold... hopefully, Apple G5 don't have + * hotplug CPU with a dynamic device-tree ... + */ + return rc; + + bail_noprops: + of_node_put(cpunode); + + return rc; +} + +#endif /* CONFIG_PMAC_SMU */ + + +static int __init g5_pm72_cpufreq_init(struct device_node *cpus) +{ + struct device_node *cpuid = NULL, *hwclock = NULL, *cpunode = NULL; + const u8 *eeprom = NULL; + const u32 *valp; + u64 max_freq, min_freq, ih, il; + int has_volt = 1, rc = 0; + + DBG("cpufreq: Initializing for PowerMac7,2, PowerMac7,3 and" + " RackMac3,1...\n"); + + /* Get first CPU node */ + for (cpunode = NULL; + (cpunode = of_get_next_child(cpus, cpunode)) != NULL;) { + if (!strcmp(cpunode->type, "cpu")) + break; + } + if (cpunode == NULL) { + printk(KERN_ERR "cpufreq: Can't find any CPU node\n"); + return -ENODEV; + } + + /* Lookup the cpuid eeprom node */ + cpuid = of_find_node_by_path("/u3@0,f8000000/i2c@f8001000/cpuid@a0"); + if (cpuid != NULL) + eeprom = of_get_property(cpuid, "cpuid", NULL); + if (eeprom == NULL) { + printk(KERN_ERR "cpufreq: Can't find cpuid EEPROM !\n"); + rc = -ENODEV; + goto bail; + } + + /* Lookup the i2c hwclock */ + for (hwclock = NULL; + (hwclock = of_find_node_by_name(hwclock, "i2c-hwclock")) != NULL;){ + const char *loc = of_get_property(hwclock, + "hwctrl-location", NULL); + if (loc == NULL) + continue; + if (strcmp(loc, "CPU CLOCK")) + continue; + if (!of_get_property(hwclock, "platform-get-frequency", NULL)) + continue; + break; + } + if (hwclock == NULL) { + printk(KERN_ERR "cpufreq: Can't find i2c clock chip !\n"); + rc = -ENODEV; + goto bail; + } + + DBG("cpufreq: i2c clock chip found: %s\n", hwclock->full_name); + + /* Now get all the platform functions */ + pfunc_cpu_getfreq = + pmf_find_function(hwclock, "get-frequency"); + pfunc_cpu_setfreq_high = + pmf_find_function(hwclock, "set-frequency-high"); + pfunc_cpu_setfreq_low = + pmf_find_function(hwclock, "set-frequency-low"); + pfunc_slewing_done = + pmf_find_function(hwclock, "slewing-done"); + pfunc_cpu0_volt_high = + pmf_find_function(hwclock, "set-voltage-high-0"); + pfunc_cpu0_volt_low = + pmf_find_function(hwclock, "set-voltage-low-0"); + pfunc_cpu1_volt_high = + pmf_find_function(hwclock, "set-voltage-high-1"); + pfunc_cpu1_volt_low = + pmf_find_function(hwclock, "set-voltage-low-1"); + + /* Check we have minimum requirements */ + if (pfunc_cpu_getfreq == NULL || pfunc_cpu_setfreq_high == NULL || + pfunc_cpu_setfreq_low == NULL || pfunc_slewing_done == NULL) { + printk(KERN_ERR "cpufreq: Can't find platform functions !\n"); + rc = -ENODEV; + goto bail; + } + + /* Check that we have complete sets */ + if (pfunc_cpu0_volt_high == NULL || pfunc_cpu0_volt_low == NULL) { + pmf_put_function(pfunc_cpu0_volt_high); + pmf_put_function(pfunc_cpu0_volt_low); + pfunc_cpu0_volt_high = pfunc_cpu0_volt_low = NULL; + has_volt = 0; + } + if (!has_volt || + pfunc_cpu1_volt_high == NULL || pfunc_cpu1_volt_low == NULL) { + pmf_put_function(pfunc_cpu1_volt_high); + pmf_put_function(pfunc_cpu1_volt_low); + pfunc_cpu1_volt_high = pfunc_cpu1_volt_low = NULL; + } + + /* Note: The device tree also contains a "platform-set-values" + * function for which I haven't quite figured out the usage. It + * might have to be called on init and/or wakeup, I'm not too sure + * but things seem to work fine without it so far ... + */ + + /* Get max frequency from device-tree */ + valp = of_get_property(cpunode, "clock-frequency", NULL); + if (!valp) { + printk(KERN_ERR "cpufreq: Can't find CPU frequency !\n"); + rc = -ENODEV; + goto bail; + } + + max_freq = (*valp)/1000; + + /* Now calculate reduced frequency by using the cpuid input freq + * ratio. This requires 64 bits math unless we are willing to lose + * some precision + */ + ih = *((u32 *)(eeprom + 0x10)); + il = *((u32 *)(eeprom + 0x20)); + + /* Check for machines with no useful settings */ + if (il == ih) { + printk(KERN_WARNING "cpufreq: No low frequency mode available" + " on this model !\n"); + rc = -ENODEV; + goto bail; + } + + min_freq = 0; + if (ih != 0 && il != 0) + min_freq = (max_freq * il) / ih; + + /* Sanity check */ + if (min_freq >= max_freq || min_freq < 1000) { + printk(KERN_ERR "cpufreq: Can't calculate low frequency !\n"); + rc = -ENXIO; + goto bail; + } + g5_cpu_freqs[0].frequency = max_freq; + g5_cpu_freqs[1].frequency = min_freq; + + /* Set callbacks */ + transition_latency = CPUFREQ_ETERNAL; + g5_switch_volt = g5_pfunc_switch_volt; + g5_switch_freq = g5_pfunc_switch_freq; + g5_query_freq = g5_pfunc_query_freq; + + /* Force apply current frequency to make sure everything is in + * sync (voltage is right for example). Firmware may leave us with + * a strange setting ... + */ + g5_switch_volt(CPUFREQ_HIGH); + msleep(10); + g5_pmode_cur = -1; + g5_switch_freq(g5_query_freq()); + + printk(KERN_INFO "Registering G5 CPU frequency driver\n"); + printk(KERN_INFO "Frequency method: i2c/pfunc, " + "Voltage method: %s\n", has_volt ? "i2c/pfunc" : "none"); + printk(KERN_INFO "Low: %d Mhz, High: %d Mhz, Cur: %d MHz\n", + g5_cpu_freqs[1].frequency/1000, + g5_cpu_freqs[0].frequency/1000, + g5_cpu_freqs[g5_pmode_cur].frequency/1000); + + rc = cpufreq_register_driver(&g5_cpufreq_driver); + bail: + if (rc != 0) { + pmf_put_function(pfunc_cpu_getfreq); + pmf_put_function(pfunc_cpu_setfreq_high); + pmf_put_function(pfunc_cpu_setfreq_low); + pmf_put_function(pfunc_slewing_done); + pmf_put_function(pfunc_cpu0_volt_high); + pmf_put_function(pfunc_cpu0_volt_low); + pmf_put_function(pfunc_cpu1_volt_high); + pmf_put_function(pfunc_cpu1_volt_low); + } + of_node_put(hwclock); + of_node_put(cpuid); + of_node_put(cpunode); + + return rc; +} + +static int __init g5_cpufreq_init(void) +{ + struct device_node *cpus; + int rc = 0; + + cpus = of_find_node_by_path("/cpus"); + if (cpus == NULL) { + DBG("No /cpus node !\n"); + return -ENODEV; + } + + if (of_machine_is_compatible("PowerMac7,2") || + of_machine_is_compatible("PowerMac7,3") || + of_machine_is_compatible("RackMac3,1")) + rc = g5_pm72_cpufreq_init(cpus); +#ifdef CONFIG_PMAC_SMU + else + rc = g5_neo2_cpufreq_init(cpus); +#endif /* CONFIG_PMAC_SMU */ + + of_node_put(cpus); + return rc; +} + +module_init(g5_cpufreq_init); + + +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 363116073a26dbc2903d8417047597eebcc05273 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 7 Dec 2012 18:35:41 +0000 Subject: arm64: KVM: define HYP and Stage-2 translation page flags Add HYP and S2 page flags, for both normal and device memory. Reviewed-by: Christopher Covington Reviewed-by: Catalin Marinas Signed-off-by: Marc Zyngier --- arch/arm64/include/asm/memory.h | 6 ++++++ arch/arm64/include/asm/pgtable-hwdef.h | 19 +++++++++++++++++++ arch/arm64/include/asm/pgtable.h | 12 ++++++++++++ 3 files changed, 37 insertions(+) diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h index 381f556b664e..20925bcf4e2a 100644 --- a/arch/arm64/include/asm/memory.h +++ b/arch/arm64/include/asm/memory.h @@ -90,6 +90,12 @@ #define MT_NORMAL_NC 3 #define MT_NORMAL 4 +/* + * Memory types for Stage-2 translation + */ +#define MT_S2_NORMAL 0xf +#define MT_S2_DEVICE_nGnRE 0x1 + #ifndef __ASSEMBLY__ extern phys_addr_t memstart_addr; diff --git a/arch/arm64/include/asm/pgtable-hwdef.h b/arch/arm64/include/asm/pgtable-hwdef.h index 75fd13d289b9..66367c6c6527 100644 --- a/arch/arm64/include/asm/pgtable-hwdef.h +++ b/arch/arm64/include/asm/pgtable-hwdef.h @@ -35,6 +35,7 @@ /* * Section */ +#define PMD_SECT_USER (_AT(pteval_t, 1) << 6) /* AP[1] */ #define PMD_SECT_S (_AT(pmdval_t, 3) << 8) #define PMD_SECT_AF (_AT(pmdval_t, 1) << 10) #define PMD_SECT_NG (_AT(pmdval_t, 1) << 11) @@ -67,6 +68,24 @@ #define PTE_ATTRINDX(t) (_AT(pteval_t, (t)) << 2) #define PTE_ATTRINDX_MASK (_AT(pteval_t, 7) << 2) +/* + * 2nd stage PTE definitions + */ +#define PTE_S2_RDONLY (_AT(pteval_t, 1) << 6) /* HAP[2:1] */ +#define PTE_S2_RDWR (_AT(pteval_t, 3) << 6) /* HAP[2:1] */ + +/* + * Memory Attribute override for Stage-2 (MemAttr[3:0]) + */ +#define PTE_S2_MEMATTR(t) (_AT(pteval_t, (t)) << 2) +#define PTE_S2_MEMATTR_MASK (_AT(pteval_t, 0xf) << 2) + +/* + * EL2/HYP PTE/PMD definitions + */ +#define PMD_HYP PMD_SECT_USER +#define PTE_HYP PTE_USER + /* * 40-bit physical address supported. */ diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h index e333a243bfcc..fc2915a35c52 100644 --- a/arch/arm64/include/asm/pgtable.h +++ b/arch/arm64/include/asm/pgtable.h @@ -76,6 +76,12 @@ extern pgprot_t pgprot_default; #define PAGE_KERNEL _MOD_PROT(pgprot_default, PTE_PXN | PTE_UXN | PTE_DIRTY) #define PAGE_KERNEL_EXEC _MOD_PROT(pgprot_default, PTE_UXN | PTE_DIRTY) +#define PAGE_HYP _MOD_PROT(pgprot_default, PTE_HYP) +#define PAGE_HYP_DEVICE __pgprot(PROT_DEVICE_nGnRE | PTE_HYP) + +#define PAGE_S2 __pgprot_modify(pgprot_default, PTE_S2_MEMATTR_MASK, PTE_S2_MEMATTR(MT_S2_NORMAL) | PTE_S2_RDONLY) +#define PAGE_S2_DEVICE __pgprot(PROT_DEFAULT | PTE_S2_MEMATTR(MT_S2_DEVICE_nGnRE) | PTE_S2_RDWR | PTE_UXN) + #define __PAGE_NONE __pgprot(((_PAGE_DEFAULT) & ~PTE_TYPE_MASK) | PTE_PROT_NONE) #define __PAGE_SHARED __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_UXN) #define __PAGE_SHARED_EXEC __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN) @@ -197,6 +203,12 @@ extern pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn, #define pmd_bad(pmd) (!(pmd_val(pmd) & 2)) +#define pmd_table(pmd) ((pmd_val(pmd) & PMD_TYPE_MASK) == \ + PMD_TYPE_TABLE) +#define pmd_sect(pmd) ((pmd_val(pmd) & PMD_TYPE_MASK) == \ + PMD_TYPE_SECT) + + static inline void set_pmd(pmd_t *pmdp, pmd_t pmd) { *pmdp = pmd; -- cgit v1.2.3 From 2240bbb697354f5617d95e3ee104ca61bb812507 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 7 Dec 2012 18:40:43 +0000 Subject: arm64: KVM: HYP mode idmap support Add the necessary infrastructure for identity-mapped HYP page tables. Idmap-ed code must be in the ".hyp.idmap.text" linker section. The rest of the HYP ends up in ".hyp.text". Reviewed-by: Catalin Marinas Signed-off-by: Marc Zyngier --- arch/arm64/kernel/vmlinux.lds.S | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S index 3fae2be8b016..b0c51b7d6293 100644 --- a/arch/arm64/kernel/vmlinux.lds.S +++ b/arch/arm64/kernel/vmlinux.lds.S @@ -17,6 +17,19 @@ ENTRY(stext) jiffies = jiffies_64; +#define HYPERVISOR_TEXT \ + /* \ + * Force the alignment to be compatible with \ + * the vectors requirements \ + */ \ + . = ALIGN(2048); \ + VMLINUX_SYMBOL(__hyp_idmap_text_start) = .; \ + *(.hyp.idmap.text) \ + VMLINUX_SYMBOL(__hyp_idmap_text_end) = .; \ + VMLINUX_SYMBOL(__hyp_text_start) = .; \ + *(.hyp.text) \ + VMLINUX_SYMBOL(__hyp_text_end) = .; + SECTIONS { /* @@ -49,6 +62,7 @@ SECTIONS TEXT_TEXT SCHED_TEXT LOCK_TEXT + HYPERVISOR_TEXT *(.fixup) *(.gnu.warning) . = ALIGN(16); @@ -124,3 +138,9 @@ SECTIONS STABS_DEBUG .comment 0 : { *(.comment) } } + +/* + * The HYP init code can't be more than a page long. + */ +ASSERT(((__hyp_idmap_text_start + PAGE_SIZE) > __hyp_idmap_text_end), + "HYP init code too big") -- cgit v1.2.3 From 0369f6a34b9facd16eea4236518ca6f9cbc9e5ef Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 10 Dec 2012 10:46:47 +0000 Subject: arm64: KVM: EL2 register definitions Define all the useful bitfields for EL2 registers. Reviewed-by: Christopher Covington Reviewed-by: Catalin Marinas Signed-off-by: Marc Zyngier --- arch/arm64/include/asm/kvm_arm.h | 245 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 245 insertions(+) create mode 100644 arch/arm64/include/asm/kvm_arm.h diff --git a/arch/arm64/include/asm/kvm_arm.h b/arch/arm64/include/asm/kvm_arm.h new file mode 100644 index 000000000000..a5f28e2720c7 --- /dev/null +++ b/arch/arm64/include/asm/kvm_arm.h @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2012,2013 - ARM Ltd + * Author: Marc Zyngier + * + * 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 . + */ + +#ifndef __ARM64_KVM_ARM_H__ +#define __ARM64_KVM_ARM_H__ + +#include + +/* Hyp Configuration Register (HCR) bits */ +#define HCR_ID (UL(1) << 33) +#define HCR_CD (UL(1) << 32) +#define HCR_RW_SHIFT 31 +#define HCR_RW (UL(1) << HCR_RW_SHIFT) +#define HCR_TRVM (UL(1) << 30) +#define HCR_HCD (UL(1) << 29) +#define HCR_TDZ (UL(1) << 28) +#define HCR_TGE (UL(1) << 27) +#define HCR_TVM (UL(1) << 26) +#define HCR_TTLB (UL(1) << 25) +#define HCR_TPU (UL(1) << 24) +#define HCR_TPC (UL(1) << 23) +#define HCR_TSW (UL(1) << 22) +#define HCR_TAC (UL(1) << 21) +#define HCR_TIDCP (UL(1) << 20) +#define HCR_TSC (UL(1) << 19) +#define HCR_TID3 (UL(1) << 18) +#define HCR_TID2 (UL(1) << 17) +#define HCR_TID1 (UL(1) << 16) +#define HCR_TID0 (UL(1) << 15) +#define HCR_TWE (UL(1) << 14) +#define HCR_TWI (UL(1) << 13) +#define HCR_DC (UL(1) << 12) +#define HCR_BSU (3 << 10) +#define HCR_BSU_IS (UL(1) << 10) +#define HCR_FB (UL(1) << 9) +#define HCR_VA (UL(1) << 8) +#define HCR_VI (UL(1) << 7) +#define HCR_VF (UL(1) << 6) +#define HCR_AMO (UL(1) << 5) +#define HCR_IMO (UL(1) << 4) +#define HCR_FMO (UL(1) << 3) +#define HCR_PTW (UL(1) << 2) +#define HCR_SWIO (UL(1) << 1) +#define HCR_VM (UL(1) << 0) + +/* + * The bits we set in HCR: + * RW: 64bit by default, can be overriden for 32bit VMs + * TAC: Trap ACTLR + * TSC: Trap SMC + * TSW: Trap cache operations by set/way + * TWI: Trap WFI + * TIDCP: Trap L2CTLR/L2ECTLR + * BSU_IS: Upgrade barriers to the inner shareable domain + * FB: Force broadcast of all maintainance operations + * AMO: Override CPSR.A and enable signaling with VA + * IMO: Override CPSR.I and enable signaling with VI + * FMO: Override CPSR.F and enable signaling with VF + * SWIO: Turn set/way invalidates into set/way clean+invalidate + */ +#define HCR_GUEST_FLAGS (HCR_TSC | HCR_TSW | HCR_TWI | HCR_VM | HCR_BSU_IS | \ + HCR_FB | HCR_TAC | HCR_AMO | HCR_IMO | HCR_FMO | \ + HCR_SWIO | HCR_TIDCP | HCR_RW) +#define HCR_VIRT_EXCP_MASK (HCR_VA | HCR_VI | HCR_VF) + +/* Hyp System Control Register (SCTLR_EL2) bits */ +#define SCTLR_EL2_EE (1 << 25) +#define SCTLR_EL2_WXN (1 << 19) +#define SCTLR_EL2_I (1 << 12) +#define SCTLR_EL2_SA (1 << 3) +#define SCTLR_EL2_C (1 << 2) +#define SCTLR_EL2_A (1 << 1) +#define SCTLR_EL2_M 1 +#define SCTLR_EL2_FLAGS (SCTLR_EL2_M | SCTLR_EL2_A | SCTLR_EL2_C | \ + SCTLR_EL2_SA | SCTLR_EL2_I) + +/* TCR_EL2 Registers bits */ +#define TCR_EL2_TBI (1 << 20) +#define TCR_EL2_PS (7 << 16) +#define TCR_EL2_PS_40B (2 << 16) +#define TCR_EL2_TG0 (1 << 14) +#define TCR_EL2_SH0 (3 << 12) +#define TCR_EL2_ORGN0 (3 << 10) +#define TCR_EL2_IRGN0 (3 << 8) +#define TCR_EL2_T0SZ 0x3f +#define TCR_EL2_MASK (TCR_EL2_TG0 | TCR_EL2_SH0 | \ + TCR_EL2_ORGN0 | TCR_EL2_IRGN0 | TCR_EL2_T0SZ) + +#define TCR_EL2_FLAGS (TCR_EL2_PS_40B) + +/* VTCR_EL2 Registers bits */ +#define VTCR_EL2_PS_MASK (7 << 16) +#define VTCR_EL2_PS_40B (2 << 16) +#define VTCR_EL2_TG0_MASK (1 << 14) +#define VTCR_EL2_TG0_4K (0 << 14) +#define VTCR_EL2_TG0_64K (1 << 14) +#define VTCR_EL2_SH0_MASK (3 << 12) +#define VTCR_EL2_SH0_INNER (3 << 12) +#define VTCR_EL2_ORGN0_MASK (3 << 10) +#define VTCR_EL2_ORGN0_WBWA (1 << 10) +#define VTCR_EL2_IRGN0_MASK (3 << 8) +#define VTCR_EL2_IRGN0_WBWA (1 << 8) +#define VTCR_EL2_SL0_MASK (3 << 6) +#define VTCR_EL2_SL0_LVL1 (1 << 6) +#define VTCR_EL2_T0SZ_MASK 0x3f +#define VTCR_EL2_T0SZ_40B 24 + +#ifdef CONFIG_ARM64_64K_PAGES +/* + * Stage2 translation configuration: + * 40bits output (PS = 2) + * 40bits input (T0SZ = 24) + * 64kB pages (TG0 = 1) + * 2 level page tables (SL = 1) + */ +#define VTCR_EL2_FLAGS (VTCR_EL2_PS_40B | VTCR_EL2_TG0_64K | \ + VTCR_EL2_SH0_INNER | VTCR_EL2_ORGN0_WBWA | \ + VTCR_EL2_IRGN0_WBWA | VTCR_EL2_SL0_LVL1 | \ + VTCR_EL2_T0SZ_40B) +#define VTTBR_X (38 - VTCR_EL2_T0SZ_40B) +#else +/* + * Stage2 translation configuration: + * 40bits output (PS = 2) + * 40bits input (T0SZ = 24) + * 4kB pages (TG0 = 0) + * 3 level page tables (SL = 1) + */ +#define VTCR_EL2_FLAGS (VTCR_EL2_PS_40B | VTCR_EL2_TG0_4K | \ + VTCR_EL2_SH0_INNER | VTCR_EL2_ORGN0_WBWA | \ + VTCR_EL2_IRGN0_WBWA | VTCR_EL2_SL0_LVL1 | \ + VTCR_EL2_T0SZ_40B) +#define VTTBR_X (37 - VTCR_EL2_T0SZ_40B) +#endif + +#define VTTBR_BADDR_SHIFT (VTTBR_X - 1) +#define VTTBR_BADDR_MASK (((1LLU << (40 - VTTBR_X)) - 1) << VTTBR_BADDR_SHIFT) +#define VTTBR_VMID_SHIFT (48LLU) +#define VTTBR_VMID_MASK (0xffLLU << VTTBR_VMID_SHIFT) + +/* Hyp System Trap Register */ +#define HSTR_EL2_TTEE (1 << 16) +#define HSTR_EL2_T(x) (1 << x) + +/* Hyp Coprocessor Trap Register */ +#define CPTR_EL2_TCPAC (1 << 31) +#define CPTR_EL2_TTA (1 << 20) +#define CPTR_EL2_TFP (1 << 10) + +/* Hyp Debug Configuration Register bits */ +#define MDCR_EL2_TDRA (1 << 11) +#define MDCR_EL2_TDOSA (1 << 10) +#define MDCR_EL2_TDA (1 << 9) +#define MDCR_EL2_TDE (1 << 8) +#define MDCR_EL2_HPME (1 << 7) +#define MDCR_EL2_TPM (1 << 6) +#define MDCR_EL2_TPMCR (1 << 5) +#define MDCR_EL2_HPMN_MASK (0x1F) + +/* Exception Syndrome Register (ESR) bits */ +#define ESR_EL2_EC_SHIFT (26) +#define ESR_EL2_EC (0x3fU << ESR_EL2_EC_SHIFT) +#define ESR_EL2_IL (1U << 25) +#define ESR_EL2_ISS (ESR_EL2_IL - 1) +#define ESR_EL2_ISV_SHIFT (24) +#define ESR_EL2_ISV (1U << ESR_EL2_ISV_SHIFT) +#define ESR_EL2_SAS_SHIFT (22) +#define ESR_EL2_SAS (3U << ESR_EL2_SAS_SHIFT) +#define ESR_EL2_SSE (1 << 21) +#define ESR_EL2_SRT_SHIFT (16) +#define ESR_EL2_SRT_MASK (0x1f << ESR_EL2_SRT_SHIFT) +#define ESR_EL2_SF (1 << 15) +#define ESR_EL2_AR (1 << 14) +#define ESR_EL2_EA (1 << 9) +#define ESR_EL2_CM (1 << 8) +#define ESR_EL2_S1PTW (1 << 7) +#define ESR_EL2_WNR (1 << 6) +#define ESR_EL2_FSC (0x3f) +#define ESR_EL2_FSC_TYPE (0x3c) + +#define ESR_EL2_CV_SHIFT (24) +#define ESR_EL2_CV (1U << ESR_EL2_CV_SHIFT) +#define ESR_EL2_COND_SHIFT (20) +#define ESR_EL2_COND (0xfU << ESR_EL2_COND_SHIFT) + + +#define FSC_FAULT (0x04) +#define FSC_PERM (0x0c) + +/* Hyp Prefetch Fault Address Register (HPFAR/HDFAR) */ +#define HPFAR_MASK (~0xFUL) + +#define ESR_EL2_EC_UNKNOWN (0x00) +#define ESR_EL2_EC_WFI (0x01) +#define ESR_EL2_EC_CP15_32 (0x03) +#define ESR_EL2_EC_CP15_64 (0x04) +#define ESR_EL2_EC_CP14_MR (0x05) +#define ESR_EL2_EC_CP14_LS (0x06) +#define ESR_EL2_EC_FP_ASIMD (0x07) +#define ESR_EL2_EC_CP10_ID (0x08) +#define ESR_EL2_EC_CP14_64 (0x0C) +#define ESR_EL2_EC_ILL_ISS (0x0E) +#define ESR_EL2_EC_SVC32 (0x11) +#define ESR_EL2_EC_HVC32 (0x12) +#define ESR_EL2_EC_SMC32 (0x13) +#define ESR_EL2_EC_SVC64 (0x15) +#define ESR_EL2_EC_HVC64 (0x16) +#define ESR_EL2_EC_SMC64 (0x17) +#define ESR_EL2_EC_SYS64 (0x18) +#define ESR_EL2_EC_IABT (0x20) +#define ESR_EL2_EC_IABT_HYP (0x21) +#define ESR_EL2_EC_PC_ALIGN (0x22) +#define ESR_EL2_EC_DABT (0x24) +#define ESR_EL2_EC_DABT_HYP (0x25) +#define ESR_EL2_EC_SP_ALIGN (0x26) +#define ESR_EL2_EC_FP_EXC32 (0x28) +#define ESR_EL2_EC_FP_EXC64 (0x2C) +#define ESR_EL2_EC_SERRROR (0x2F) +#define ESR_EL2_EC_BREAKPT (0x30) +#define ESR_EL2_EC_BREAKPT_HYP (0x31) +#define ESR_EL2_EC_SOFTSTP (0x32) +#define ESR_EL2_EC_SOFTSTP_HYP (0x33) +#define ESR_EL2_EC_WATCHPT (0x34) +#define ESR_EL2_EC_WATCHPT_HYP (0x35) +#define ESR_EL2_EC_BKPT32 (0x38) +#define ESR_EL2_EC_VECTOR32 (0x3A) +#define ESR_EL2_EC_BRK64 (0x3C) + +#define ESR_EL2_EC_xABT_xFSR_EXTABT 0x10 + +#endif /* __ARM64_KVM_ARM_H__ */ -- cgit v1.2.3 From fd9fc9f73cc2070d2637a7ee082800a817fd45f3 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 10 Dec 2012 11:16:40 +0000 Subject: arm64: KVM: system register definitions for 64bit guests Define the saved/restored registers for 64bit guests. Reviewed-by: Christopher Covington Reviewed-by: Catalin Marinas Signed-off-by: Marc Zyngier --- arch/arm64/include/asm/kvm_asm.h | 68 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 arch/arm64/include/asm/kvm_asm.h diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h new file mode 100644 index 000000000000..591ac219964a --- /dev/null +++ b/arch/arm64/include/asm/kvm_asm.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2012,2013 - ARM Ltd + * Author: Marc Zyngier + * + * 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 . + */ + +#ifndef __ARM_KVM_ASM_H__ +#define __ARM_KVM_ASM_H__ + +/* + * 0 is reserved as an invalid value. + * Order *must* be kept in sync with the hyp switch code. + */ +#define MPIDR_EL1 1 /* MultiProcessor Affinity Register */ +#define CSSELR_EL1 2 /* Cache Size Selection Register */ +#define SCTLR_EL1 3 /* System Control Register */ +#define ACTLR_EL1 4 /* Auxilliary Control Register */ +#define CPACR_EL1 5 /* Coprocessor Access Control */ +#define TTBR0_EL1 6 /* Translation Table Base Register 0 */ +#define TTBR1_EL1 7 /* Translation Table Base Register 1 */ +#define TCR_EL1 8 /* Translation Control Register */ +#define ESR_EL1 9 /* Exception Syndrome Register */ +#define AFSR0_EL1 10 /* Auxilary Fault Status Register 0 */ +#define AFSR1_EL1 11 /* Auxilary Fault Status Register 1 */ +#define FAR_EL1 12 /* Fault Address Register */ +#define MAIR_EL1 13 /* Memory Attribute Indirection Register */ +#define VBAR_EL1 14 /* Vector Base Address Register */ +#define CONTEXTIDR_EL1 15 /* Context ID Register */ +#define TPIDR_EL0 16 /* Thread ID, User R/W */ +#define TPIDRRO_EL0 17 /* Thread ID, User R/O */ +#define TPIDR_EL1 18 /* Thread ID, Privileged */ +#define AMAIR_EL1 19 /* Aux Memory Attribute Indirection Register */ +#define CNTKCTL_EL1 20 /* Timer Control Register (EL1) */ +#define NR_SYS_REGS 21 + +#define ARM_EXCEPTION_IRQ 0 +#define ARM_EXCEPTION_TRAP 1 + +#ifndef __ASSEMBLY__ +struct kvm; +struct kvm_vcpu; + +extern char __kvm_hyp_init[]; +extern char __kvm_hyp_init_end[]; + +extern char __kvm_hyp_vector[]; + +extern char __kvm_hyp_code_start[]; +extern char __kvm_hyp_code_end[]; + +extern void __kvm_flush_vm_context(void); +extern void __kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa); + +extern int __kvm_vcpu_run(struct kvm_vcpu *vcpu); +#endif + +#endif /* __ARM_KVM_ASM_H__ */ -- cgit v1.2.3 From 83a4979483c8e597b69d4403794f87fea51fa549 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 10 Dec 2012 13:27:52 +0000 Subject: arm64: KVM: Basic ESR_EL2 helpers and vcpu register access Implements helpers for dealing with the EL2 syndrome register as well as accessing the vcpu registers. Reviewed-by: Christopher Covington Reviewed-by: Catalin Marinas Signed-off-by: Marc Zyngier --- arch/arm64/include/asm/kvm_emulate.h | 158 +++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 arch/arm64/include/asm/kvm_emulate.h diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h new file mode 100644 index 000000000000..6c1725e93b0b --- /dev/null +++ b/arch/arm64/include/asm/kvm_emulate.h @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2012,2013 - ARM Ltd + * Author: Marc Zyngier + * + * Derived from arch/arm/include/kvm_emulate.h + * Copyright (C) 2012 - Virtual Open Systems and Columbia University + * Author: Christoffer Dall + * + * 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 . + */ + +#ifndef __ARM64_KVM_EMULATE_H__ +#define __ARM64_KVM_EMULATE_H__ + +#include +#include +#include +#include +#include + +void kvm_inject_undefined(struct kvm_vcpu *vcpu); +void kvm_inject_dabt(struct kvm_vcpu *vcpu, unsigned long addr); +void kvm_inject_pabt(struct kvm_vcpu *vcpu, unsigned long addr); + +static inline unsigned long *vcpu_pc(const struct kvm_vcpu *vcpu) +{ + return (unsigned long *)&vcpu_gp_regs(vcpu)->regs.pc; +} + +static inline unsigned long *vcpu_elr_el1(const struct kvm_vcpu *vcpu) +{ + return (unsigned long *)&vcpu_gp_regs(vcpu)->elr_el1; +} + +static inline unsigned long *vcpu_cpsr(const struct kvm_vcpu *vcpu) +{ + return (unsigned long *)&vcpu_gp_regs(vcpu)->regs.pstate; +} + +static inline bool vcpu_mode_is_32bit(const struct kvm_vcpu *vcpu) +{ + return false; /* 32bit? Bahhh... */ +} + +static inline bool kvm_condition_valid(const struct kvm_vcpu *vcpu) +{ + return true; /* No conditionals on arm64 */ +} + +static inline void kvm_skip_instr(struct kvm_vcpu *vcpu, bool is_wide_instr) +{ + *vcpu_pc(vcpu) += 4; +} + +static inline void vcpu_set_thumb(struct kvm_vcpu *vcpu) +{ +} + +static inline unsigned long *vcpu_reg(const struct kvm_vcpu *vcpu, u8 reg_num) +{ + return (unsigned long *)&vcpu_gp_regs(vcpu)->regs.regs[reg_num]; +} + +/* Get vcpu SPSR for current mode */ +static inline unsigned long *vcpu_spsr(const struct kvm_vcpu *vcpu) +{ + return (unsigned long *)&vcpu_gp_regs(vcpu)->spsr[KVM_SPSR_EL1]; +} + +static inline bool vcpu_mode_priv(const struct kvm_vcpu *vcpu) +{ + u32 mode = *vcpu_cpsr(vcpu) & PSR_MODE_MASK; + + return mode != PSR_MODE_EL0t; +} + +static inline u32 kvm_vcpu_get_hsr(const struct kvm_vcpu *vcpu) +{ + return vcpu->arch.fault.esr_el2; +} + +static inline unsigned long kvm_vcpu_get_hfar(const struct kvm_vcpu *vcpu) +{ + return vcpu->arch.fault.far_el2; +} + +static inline phys_addr_t kvm_vcpu_get_fault_ipa(const struct kvm_vcpu *vcpu) +{ + return ((phys_addr_t)vcpu->arch.fault.hpfar_el2 & HPFAR_MASK) << 8; +} + +static inline bool kvm_vcpu_dabt_isvalid(const struct kvm_vcpu *vcpu) +{ + return !!(kvm_vcpu_get_hsr(vcpu) & ESR_EL2_ISV); +} + +static inline bool kvm_vcpu_dabt_iswrite(const struct kvm_vcpu *vcpu) +{ + return !!(kvm_vcpu_get_hsr(vcpu) & ESR_EL2_WNR); +} + +static inline bool kvm_vcpu_dabt_issext(const struct kvm_vcpu *vcpu) +{ + return !!(kvm_vcpu_get_hsr(vcpu) & ESR_EL2_SSE); +} + +static inline int kvm_vcpu_dabt_get_rd(const struct kvm_vcpu *vcpu) +{ + return (kvm_vcpu_get_hsr(vcpu) & ESR_EL2_SRT_MASK) >> ESR_EL2_SRT_SHIFT; +} + +static inline bool kvm_vcpu_dabt_isextabt(const struct kvm_vcpu *vcpu) +{ + return !!(kvm_vcpu_get_hsr(vcpu) & ESR_EL2_EA); +} + +static inline bool kvm_vcpu_dabt_iss1tw(const struct kvm_vcpu *vcpu) +{ + return !!(kvm_vcpu_get_hsr(vcpu) & ESR_EL2_S1PTW); +} + +static inline int kvm_vcpu_dabt_get_as(const struct kvm_vcpu *vcpu) +{ + return 1 << ((kvm_vcpu_get_hsr(vcpu) & ESR_EL2_SAS) >> ESR_EL2_SAS_SHIFT); +} + +/* This one is not specific to Data Abort */ +static inline bool kvm_vcpu_trap_il_is32bit(const struct kvm_vcpu *vcpu) +{ + return !!(kvm_vcpu_get_hsr(vcpu) & ESR_EL2_IL); +} + +static inline u8 kvm_vcpu_trap_get_class(const struct kvm_vcpu *vcpu) +{ + return kvm_vcpu_get_hsr(vcpu) >> ESR_EL2_EC_SHIFT; +} + +static inline bool kvm_vcpu_trap_is_iabt(const struct kvm_vcpu *vcpu) +{ + return kvm_vcpu_trap_get_class(vcpu) == ESR_EL2_EC_IABT; +} + +static inline u8 kvm_vcpu_trap_get_fault(const struct kvm_vcpu *vcpu) +{ + return kvm_vcpu_get_hsr(vcpu) & ESR_EL2_FSC_TYPE; +} + +#endif /* __ARM64_KVM_EMULATE_H__ */ -- cgit v1.2.3 From aa8eff9bfbd531e0fcc8e68052f4ac545cd004c5 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 17 Dec 2012 12:27:42 +0000 Subject: arm64: KVM: fault injection into a guest Implement the injection of a fault (undefined, data abort or prefetch abort) into a 64bit guest. Reviewed-by: Christopher Covington Reviewed-by: Catalin Marinas Signed-off-by: Marc Zyngier --- arch/arm64/kvm/inject_fault.c | 126 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 arch/arm64/kvm/inject_fault.c diff --git a/arch/arm64/kvm/inject_fault.c b/arch/arm64/kvm/inject_fault.c new file mode 100644 index 000000000000..54f656271266 --- /dev/null +++ b/arch/arm64/kvm/inject_fault.c @@ -0,0 +1,126 @@ +/* + * Fault injection for 64bit guests. + * + * Copyright (C) 2012,2013 - ARM Ltd + * Author: Marc Zyngier + * + * Based on arch/arm/kvm/emulate.c + * Copyright (C) 2012 - Virtual Open Systems and Columbia University + * Author: Christoffer Dall + * + * 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 . + */ + +#include +#include +#include + +#define PSTATE_FAULT_BITS_64 (PSR_MODE_EL1h | PSR_A_BIT | PSR_F_BIT | \ + PSR_I_BIT | PSR_D_BIT) +#define EL1_EXCEPT_SYNC_OFFSET 0x200 + +static void inject_abt64(struct kvm_vcpu *vcpu, bool is_iabt, unsigned long addr) +{ + unsigned long cpsr = *vcpu_cpsr(vcpu); + bool is_aarch32; + u32 esr = 0; + + is_aarch32 = vcpu_mode_is_32bit(vcpu); + + *vcpu_spsr(vcpu) = cpsr; + *vcpu_elr_el1(vcpu) = *vcpu_pc(vcpu); + + *vcpu_cpsr(vcpu) = PSTATE_FAULT_BITS_64; + *vcpu_pc(vcpu) = vcpu_sys_reg(vcpu, VBAR_EL1) + EL1_EXCEPT_SYNC_OFFSET; + + vcpu_sys_reg(vcpu, FAR_EL1) = addr; + + /* + * Build an {i,d}abort, depending on the level and the + * instruction set. Report an external synchronous abort. + */ + if (kvm_vcpu_trap_il_is32bit(vcpu)) + esr |= ESR_EL1_IL; + + /* + * Here, the guest runs in AArch64 mode when in EL1. If we get + * an AArch32 fault, it means we managed to trap an EL0 fault. + */ + if (is_aarch32 || (cpsr & PSR_MODE_MASK) == PSR_MODE_EL0t) + esr |= (ESR_EL1_EC_IABT_EL0 << ESR_EL1_EC_SHIFT); + else + esr |= (ESR_EL1_EC_IABT_EL1 << ESR_EL1_EC_SHIFT); + + if (!is_iabt) + esr |= ESR_EL1_EC_DABT_EL0; + + vcpu_sys_reg(vcpu, ESR_EL1) = esr | ESR_EL2_EC_xABT_xFSR_EXTABT; +} + +static void inject_undef64(struct kvm_vcpu *vcpu) +{ + unsigned long cpsr = *vcpu_cpsr(vcpu); + u32 esr = (ESR_EL1_EC_UNKNOWN << ESR_EL1_EC_SHIFT); + + *vcpu_spsr(vcpu) = cpsr; + *vcpu_elr_el1(vcpu) = *vcpu_pc(vcpu); + + *vcpu_cpsr(vcpu) = PSTATE_FAULT_BITS_64; + *vcpu_pc(vcpu) = vcpu_sys_reg(vcpu, VBAR_EL1) + EL1_EXCEPT_SYNC_OFFSET; + + /* + * Build an unknown exception, depending on the instruction + * set. + */ + if (kvm_vcpu_trap_il_is32bit(vcpu)) + esr |= ESR_EL1_IL; + + vcpu_sys_reg(vcpu, ESR_EL1) = esr; +} + +/** + * kvm_inject_dabt - inject a data abort into the guest + * @vcpu: The VCPU to receive the undefined exception + * @addr: The address to report in the DFAR + * + * It is assumed that this code is called from the VCPU thread and that the + * VCPU therefore is not currently executing guest code. + */ +void kvm_inject_dabt(struct kvm_vcpu *vcpu, unsigned long addr) +{ + inject_abt64(vcpu, false, addr); +} + +/** + * kvm_inject_pabt - inject a prefetch abort into the guest + * @vcpu: The VCPU to receive the undefined exception + * @addr: The address to report in the DFAR + * + * It is assumed that this code is called from the VCPU thread and that the + * VCPU therefore is not currently executing guest code. + */ +void kvm_inject_pabt(struct kvm_vcpu *vcpu, unsigned long addr) +{ + inject_abt64(vcpu, true, addr); +} + +/** + * kvm_inject_undefined - inject an undefined instruction into the guest + * + * It is assumed that this code is called from the VCPU thread and that the + * VCPU therefore is not currently executing guest code. + */ +void kvm_inject_undefined(struct kvm_vcpu *vcpu) +{ + inject_undef64(vcpu); +} -- cgit v1.2.3 From 37c437532b0126d1df5685080db9cecf3d918175 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 10 Dec 2012 15:35:24 +0000 Subject: arm64: KVM: architecture specific MMU backend Define the arm64 specific MMU backend: - HYP/kernel VA offset - S2 4/64kB definitions - S2 page table populating and flushing - icache cleaning Reviewed-by: Christopher Covington Reviewed-by: Catalin Marinas Signed-off-by: Marc Zyngier --- arch/arm64/include/asm/kvm_mmu.h | 135 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 arch/arm64/include/asm/kvm_mmu.h diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h new file mode 100644 index 000000000000..efe609c6a3c9 --- /dev/null +++ b/arch/arm64/include/asm/kvm_mmu.h @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2012,2013 - ARM Ltd + * Author: Marc Zyngier + * + * 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 . + */ + +#ifndef __ARM64_KVM_MMU_H__ +#define __ARM64_KVM_MMU_H__ + +#include +#include + +/* + * As we only have the TTBR0_EL2 register, we cannot express + * "negative" addresses. This makes it impossible to directly share + * mappings with the kernel. + * + * Instead, give the HYP mode its own VA region at a fixed offset from + * the kernel by just masking the top bits (which are all ones for a + * kernel address). + */ +#define HYP_PAGE_OFFSET_SHIFT VA_BITS +#define HYP_PAGE_OFFSET_MASK ((UL(1) << HYP_PAGE_OFFSET_SHIFT) - 1) +#define HYP_PAGE_OFFSET (PAGE_OFFSET & HYP_PAGE_OFFSET_MASK) + +/* + * Our virtual mapping for the idmap-ed MMU-enable code. Must be + * shared across all the page-tables. Conveniently, we use the last + * possible page, where no kernel mapping will ever exist. + */ +#define TRAMPOLINE_VA (HYP_PAGE_OFFSET_MASK & PAGE_MASK) + +#ifdef __ASSEMBLY__ + +/* + * Convert a kernel VA into a HYP VA. + * reg: VA to be converted. + */ +.macro kern_hyp_va reg + and \reg, \reg, #HYP_PAGE_OFFSET_MASK +.endm + +#else + +#include +#include + +#define KERN_TO_HYP(kva) ((unsigned long)kva - PAGE_OFFSET + HYP_PAGE_OFFSET) + +/* + * Align KVM with the kernel's view of physical memory. Should be + * 40bit IPA, with PGD being 8kB aligned in the 4KB page configuration. + */ +#define KVM_PHYS_SHIFT PHYS_MASK_SHIFT +#define KVM_PHYS_SIZE (1UL << KVM_PHYS_SHIFT) +#define KVM_PHYS_MASK (KVM_PHYS_SIZE - 1UL) + +/* Make sure we get the right size, and thus the right alignment */ +#define PTRS_PER_S2_PGD (1 << (KVM_PHYS_SHIFT - PGDIR_SHIFT)) +#define S2_PGD_ORDER get_order(PTRS_PER_S2_PGD * sizeof(pgd_t)) + +int create_hyp_mappings(void *from, void *to); +int create_hyp_io_mappings(void *from, void *to, phys_addr_t); +void free_boot_hyp_pgd(void); +void free_hyp_pgds(void); + +int kvm_alloc_stage2_pgd(struct kvm *kvm); +void kvm_free_stage2_pgd(struct kvm *kvm); +int kvm_phys_addr_ioremap(struct kvm *kvm, phys_addr_t guest_ipa, + phys_addr_t pa, unsigned long size); + +int kvm_handle_guest_abort(struct kvm_vcpu *vcpu, struct kvm_run *run); + +void kvm_mmu_free_memory_caches(struct kvm_vcpu *vcpu); + +phys_addr_t kvm_mmu_get_httbr(void); +phys_addr_t kvm_mmu_get_boot_httbr(void); +phys_addr_t kvm_get_idmap_vector(void); +int kvm_mmu_init(void); +void kvm_clear_hyp_idmap(void); + +#define kvm_set_pte(ptep, pte) set_pte(ptep, pte) + +static inline bool kvm_is_write_fault(unsigned long esr) +{ + unsigned long esr_ec = esr >> ESR_EL2_EC_SHIFT; + + if (esr_ec == ESR_EL2_EC_IABT) + return false; + + if ((esr & ESR_EL2_ISV) && !(esr & ESR_EL2_WNR)) + return false; + + return true; +} + +static inline void kvm_clean_dcache_area(void *addr, size_t size) {} +static inline void kvm_clean_pgd(pgd_t *pgd) {} +static inline void kvm_clean_pmd_entry(pmd_t *pmd) {} +static inline void kvm_clean_pte(pte_t *pte) {} +static inline void kvm_clean_pte_entry(pte_t *pte) {} + +static inline void kvm_set_s2pte_writable(pte_t *pte) +{ + pte_val(*pte) |= PTE_S2_RDWR; +} + +struct kvm; + +static inline void coherent_icache_guest_page(struct kvm *kvm, gfn_t gfn) +{ + if (!icache_is_aliasing()) { /* PIPT */ + unsigned long hva = gfn_to_hva(kvm, gfn); + flush_icache_range(hva, hva + PAGE_SIZE); + } else if (!icache_is_aivivt()) { /* non ASID-tagged VIVT */ + /* any kind of VIPT cache */ + __flush_icache_all(); + } +} + +#define kvm_flush_dcache_to_poc(a,l) __flush_dcache_area((a), (l)) + +#endif /* __ASSEMBLY__ */ +#endif /* __ARM64_KVM_MMU_H__ */ -- cgit v1.2.3 From 54f81d0eb93896da73d1636bca84cf90f52cabdf Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 10 Dec 2012 16:29:28 +0000 Subject: arm64: KVM: user space interface Provide the kvm.h file that defines the user space visible interface. Reviewed-by: Christopher Covington Reviewed-by: Catalin Marinas Signed-off-by: Marc Zyngier --- arch/arm64/include/uapi/asm/kvm.h | 117 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 arch/arm64/include/uapi/asm/kvm.h diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h new file mode 100644 index 000000000000..4e64570a20c9 --- /dev/null +++ b/arch/arm64/include/uapi/asm/kvm.h @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2012,2013 - ARM Ltd + * Author: Marc Zyngier + * + * Derived from arch/arm/include/uapi/asm/kvm.h: + * Copyright (C) 2012 - Virtual Open Systems and Columbia University + * Author: Christoffer Dall + * + * 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 . + */ + +#ifndef __ARM_KVM_H__ +#define __ARM_KVM_H__ + +#define KVM_SPSR_EL1 0 +#define KVM_NR_SPSR 1 + +#ifndef __ASSEMBLY__ +#include +#include + +#define __KVM_HAVE_GUEST_DEBUG +#define __KVM_HAVE_IRQ_LINE + +#define KVM_REG_SIZE(id) \ + (1U << (((id) & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT)) + +struct kvm_regs { + struct user_pt_regs regs; /* sp = sp_el0 */ + + __u64 sp_el1; + __u64 elr_el1; + + __u64 spsr[KVM_NR_SPSR]; + + struct user_fpsimd_state fp_regs; +}; + +/* Supported Processor Types */ +#define KVM_ARM_TARGET_AEM_V8 0 +#define KVM_ARM_TARGET_FOUNDATION_V8 1 +#define KVM_ARM_TARGET_CORTEX_A57 2 + +#define KVM_ARM_NUM_TARGETS 3 + +/* KVM_ARM_SET_DEVICE_ADDR ioctl id encoding */ +#define KVM_ARM_DEVICE_TYPE_SHIFT 0 +#define KVM_ARM_DEVICE_TYPE_MASK (0xffff << KVM_ARM_DEVICE_TYPE_SHIFT) +#define KVM_ARM_DEVICE_ID_SHIFT 16 +#define KVM_ARM_DEVICE_ID_MASK (0xffff << KVM_ARM_DEVICE_ID_SHIFT) + +/* Supported device IDs */ +#define KVM_ARM_DEVICE_VGIC_V2 0 + +/* Supported VGIC address types */ +#define KVM_VGIC_V2_ADDR_TYPE_DIST 0 +#define KVM_VGIC_V2_ADDR_TYPE_CPU 1 + +#define KVM_VGIC_V2_DIST_SIZE 0x1000 +#define KVM_VGIC_V2_CPU_SIZE 0x2000 + +struct kvm_vcpu_init { + __u32 target; + __u32 features[7]; +}; + +struct kvm_sregs { +}; + +struct kvm_fpu { +}; + +struct kvm_guest_debug_arch { +}; + +struct kvm_debug_exit_arch { +}; + +struct kvm_sync_regs { +}; + +struct kvm_arch_memory_slot { +}; + +/* KVM_IRQ_LINE irq field index values */ +#define KVM_ARM_IRQ_TYPE_SHIFT 24 +#define KVM_ARM_IRQ_TYPE_MASK 0xff +#define KVM_ARM_IRQ_VCPU_SHIFT 16 +#define KVM_ARM_IRQ_VCPU_MASK 0xff +#define KVM_ARM_IRQ_NUM_SHIFT 0 +#define KVM_ARM_IRQ_NUM_MASK 0xffff + +/* irq_type field */ +#define KVM_ARM_IRQ_TYPE_CPU 0 +#define KVM_ARM_IRQ_TYPE_SPI 1 +#define KVM_ARM_IRQ_TYPE_PPI 2 + +/* out-of-kernel GIC cpu interrupt injection irq_number field */ +#define KVM_ARM_IRQ_CPU_IRQ 0 +#define KVM_ARM_IRQ_CPU_FIQ 1 + +/* Highest supported SPI, from VGIC_NR_IRQS */ +#define KVM_ARM_IRQ_GIC_MAX 127 + +#endif + +#endif /* __ARM_KVM_H__ */ -- cgit v1.2.3 From 7c8c5e6a9101ea57a1c2c9faff0917e79251a21e Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 10 Dec 2012 16:15:34 +0000 Subject: arm64: KVM: system register handling Provide 64bit system register handling, modeled after the cp15 handling for ARM. Reviewed-by: Christopher Covington Reviewed-by: Catalin Marinas Signed-off-by: Marc Zyngier --- arch/arm64/include/asm/kvm_coproc.h | 51 +++ arch/arm64/include/uapi/asm/kvm.h | 29 ++ arch/arm64/kvm/sys_regs.c | 883 ++++++++++++++++++++++++++++++++++++ arch/arm64/kvm/sys_regs.h | 138 ++++++ include/uapi/linux/kvm.h | 1 + 5 files changed, 1102 insertions(+) create mode 100644 arch/arm64/include/asm/kvm_coproc.h create mode 100644 arch/arm64/kvm/sys_regs.c create mode 100644 arch/arm64/kvm/sys_regs.h diff --git a/arch/arm64/include/asm/kvm_coproc.h b/arch/arm64/include/asm/kvm_coproc.h new file mode 100644 index 000000000000..9b4477acb554 --- /dev/null +++ b/arch/arm64/include/asm/kvm_coproc.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2012,2013 - ARM Ltd + * Author: Marc Zyngier + * + * Derived from arch/arm/include/asm/kvm_coproc.h + * Copyright (C) 2012 Rusty Russell IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __ARM64_KVM_COPROC_H__ +#define __ARM64_KVM_COPROC_H__ + +#include + +void kvm_reset_sys_regs(struct kvm_vcpu *vcpu); + +struct kvm_sys_reg_table { + const struct sys_reg_desc *table; + size_t num; +}; + +struct kvm_sys_reg_target_table { + struct kvm_sys_reg_table table64; +}; + +void kvm_register_target_sys_reg_table(unsigned int target, + struct kvm_sys_reg_target_table *table); + +int kvm_handle_sys_reg(struct kvm_vcpu *vcpu, struct kvm_run *run); + +#define kvm_coproc_table_init kvm_sys_reg_table_init +void kvm_sys_reg_table_init(void); + +struct kvm_one_reg; +int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices); +int kvm_arm_sys_reg_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *); +int kvm_arm_sys_reg_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *); +unsigned long kvm_arm_num_sys_reg_descs(struct kvm_vcpu *vcpu); + +#endif /* __ARM64_KVM_COPROC_H__ */ diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h index 4e64570a20c9..ebac919dc0ca 100644 --- a/arch/arm64/include/uapi/asm/kvm.h +++ b/arch/arm64/include/uapi/asm/kvm.h @@ -92,6 +92,35 @@ struct kvm_sync_regs { struct kvm_arch_memory_slot { }; +/* If you need to interpret the index values, here is the key: */ +#define KVM_REG_ARM_COPROC_MASK 0x000000000FFF0000 +#define KVM_REG_ARM_COPROC_SHIFT 16 + +/* Normal registers are mapped as coprocessor 16. */ +#define KVM_REG_ARM_CORE (0x0010 << KVM_REG_ARM_COPROC_SHIFT) +#define KVM_REG_ARM_CORE_REG(name) (offsetof(struct kvm_regs, name) / sizeof(__u32)) + +/* Some registers need more space to represent values. */ +#define KVM_REG_ARM_DEMUX (0x0011 << KVM_REG_ARM_COPROC_SHIFT) +#define KVM_REG_ARM_DEMUX_ID_MASK 0x000000000000FF00 +#define KVM_REG_ARM_DEMUX_ID_SHIFT 8 +#define KVM_REG_ARM_DEMUX_ID_CCSIDR (0x00 << KVM_REG_ARM_DEMUX_ID_SHIFT) +#define KVM_REG_ARM_DEMUX_VAL_MASK 0x00000000000000FF +#define KVM_REG_ARM_DEMUX_VAL_SHIFT 0 + +/* AArch64 system registers */ +#define KVM_REG_ARM64_SYSREG (0x0013 << KVM_REG_ARM_COPROC_SHIFT) +#define KVM_REG_ARM64_SYSREG_OP0_MASK 0x000000000000c000 +#define KVM_REG_ARM64_SYSREG_OP0_SHIFT 14 +#define KVM_REG_ARM64_SYSREG_OP1_MASK 0x0000000000003800 +#define KVM_REG_ARM64_SYSREG_OP1_SHIFT 11 +#define KVM_REG_ARM64_SYSREG_CRN_MASK 0x0000000000000780 +#define KVM_REG_ARM64_SYSREG_CRN_SHIFT 7 +#define KVM_REG_ARM64_SYSREG_CRM_MASK 0x0000000000000078 +#define KVM_REG_ARM64_SYSREG_CRM_SHIFT 3 +#define KVM_REG_ARM64_SYSREG_OP2_MASK 0x0000000000000007 +#define KVM_REG_ARM64_SYSREG_OP2_SHIFT 0 + /* KVM_IRQ_LINE irq field index values */ #define KVM_ARM_IRQ_TYPE_SHIFT 24 #define KVM_ARM_IRQ_TYPE_MASK 0xff diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c new file mode 100644 index 000000000000..52fff0ae3442 --- /dev/null +++ b/arch/arm64/kvm/sys_regs.c @@ -0,0 +1,883 @@ +/* + * Copyright (C) 2012,2013 - ARM Ltd + * Author: Marc Zyngier + * + * Derived from arch/arm/kvm/coproc.c: + * Copyright (C) 2012 - Virtual Open Systems and Columbia University + * Authors: Rusty Russell + * Christoffer Dall + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sys_regs.h" + +/* + * All of this file is extremly similar to the ARM coproc.c, but the + * types are different. My gut feeling is that it should be pretty + * easy to merge, but that would be an ABI breakage -- again. VFP + * would also need to be abstracted. + */ + +/* 3 bits per cache level, as per CLIDR, but non-existent caches always 0 */ +static u32 cache_levels; + +/* CSSELR values; used to index KVM_REG_ARM_DEMUX_ID_CCSIDR */ +#define CSSELR_MAX 12 + +/* Which cache CCSIDR represents depends on CSSELR value. */ +static u32 get_ccsidr(u32 csselr) +{ + u32 ccsidr; + + /* Make sure noone else changes CSSELR during this! */ + local_irq_disable(); + /* Put value into CSSELR */ + asm volatile("msr csselr_el1, %x0" : : "r" (csselr)); + isb(); + /* Read result out of CCSIDR */ + asm volatile("mrs %0, ccsidr_el1" : "=r" (ccsidr)); + local_irq_enable(); + + return ccsidr; +} + +static void do_dc_cisw(u32 val) +{ + asm volatile("dc cisw, %x0" : : "r" (val)); + dsb(); +} + +static void do_dc_csw(u32 val) +{ + asm volatile("dc csw, %x0" : : "r" (val)); + dsb(); +} + +/* See note at ARM ARM B1.14.4 */ +static bool access_dcsw(struct kvm_vcpu *vcpu, + const struct sys_reg_params *p, + const struct sys_reg_desc *r) +{ + unsigned long val; + int cpu; + + if (!p->is_write) + return read_from_write_only(vcpu, p); + + cpu = get_cpu(); + + cpumask_setall(&vcpu->arch.require_dcache_flush); + cpumask_clear_cpu(cpu, &vcpu->arch.require_dcache_flush); + + /* If we were already preempted, take the long way around */ + if (cpu != vcpu->arch.last_pcpu) { + flush_cache_all(); + goto done; + } + + val = *vcpu_reg(vcpu, p->Rt); + + switch (p->CRm) { + case 6: /* Upgrade DCISW to DCCISW, as per HCR.SWIO */ + case 14: /* DCCISW */ + do_dc_cisw(val); + break; + + case 10: /* DCCSW */ + do_dc_csw(val); + break; + } + +done: + put_cpu(); + + return true; +} + +/* + * We could trap ID_DFR0 and tell the guest we don't support performance + * monitoring. Unfortunately the patch to make the kernel check ID_DFR0 was + * NAKed, so it will read the PMCR anyway. + * + * Therefore we tell the guest we have 0 counters. Unfortunately, we + * must always support PMCCNTR (the cycle counter): we just RAZ/WI for + * all PM registers, which doesn't crash the guest kernel at least. + */ +static bool pm_fake(struct kvm_vcpu *vcpu, + const struct sys_reg_params *p, + const struct sys_reg_desc *r) +{ + if (p->is_write) + return ignore_write(vcpu, p); + else + return read_zero(vcpu, p); +} + +static void reset_amair_el1(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r) +{ + u64 amair; + + asm volatile("mrs %0, amair_el1\n" : "=r" (amair)); + vcpu_sys_reg(vcpu, AMAIR_EL1) = amair; +} + +static void reset_mpidr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r) +{ + /* + * Simply map the vcpu_id into the Aff0 field of the MPIDR. + */ + vcpu_sys_reg(vcpu, MPIDR_EL1) = (1UL << 31) | (vcpu->vcpu_id & 0xff); +} + +/* + * Architected system registers. + * Important: Must be sorted ascending by Op0, Op1, CRn, CRm, Op2 + */ +static const struct sys_reg_desc sys_reg_descs[] = { + /* DC ISW */ + { Op0(0b01), Op1(0b000), CRn(0b0111), CRm(0b0110), Op2(0b010), + access_dcsw }, + /* DC CSW */ + { Op0(0b01), Op1(0b000), CRn(0b0111), CRm(0b1010), Op2(0b010), + access_dcsw }, + /* DC CISW */ + { Op0(0b01), Op1(0b000), CRn(0b0111), CRm(0b1110), Op2(0b010), + access_dcsw }, + + /* MPIDR_EL1 */ + { Op0(0b11), Op1(0b000), CRn(0b0000), CRm(0b0000), Op2(0b101), + NULL, reset_mpidr, MPIDR_EL1 }, + /* SCTLR_EL1 */ + { Op0(0b11), Op1(0b000), CRn(0b0001), CRm(0b0000), Op2(0b000), + NULL, reset_val, SCTLR_EL1, 0x00C50078 }, + /* CPACR_EL1 */ + { Op0(0b11), Op1(0b000), CRn(0b0001), CRm(0b0000), Op2(0b010), + NULL, reset_val, CPACR_EL1, 0 }, + /* TTBR0_EL1 */ + { Op0(0b11), Op1(0b000), CRn(0b0010), CRm(0b0000), Op2(0b000), + NULL, reset_unknown, TTBR0_EL1 }, + /* TTBR1_EL1 */ + { Op0(0b11), Op1(0b000), CRn(0b0010), CRm(0b0000), Op2(0b001), + NULL, reset_unknown, TTBR1_EL1 }, + /* TCR_EL1 */ + { Op0(0b11), Op1(0b000), CRn(0b0010), CRm(0b0000), Op2(0b010), + NULL, reset_val, TCR_EL1, 0 }, + + /* AFSR0_EL1 */ + { Op0(0b11), Op1(0b000), CRn(0b0101), CRm(0b0001), Op2(0b000), + NULL, reset_unknown, AFSR0_EL1 }, + /* AFSR1_EL1 */ + { Op0(0b11), Op1(0b000), CRn(0b0101), CRm(0b0001), Op2(0b001), + NULL, reset_unknown, AFSR1_EL1 }, + /* ESR_EL1 */ + { Op0(0b11), Op1(0b000), CRn(0b0101), CRm(0b0010), Op2(0b000), + NULL, reset_unknown, ESR_EL1 }, + /* FAR_EL1 */ + { Op0(0b11), Op1(0b000), CRn(0b0110), CRm(0b0000), Op2(0b000), + NULL, reset_unknown, FAR_EL1 }, + + /* PMINTENSET_EL1 */ + { Op0(0b11), Op1(0b000), CRn(0b1001), CRm(0b1110), Op2(0b001), + pm_fake }, + /* PMINTENCLR_EL1 */ + { Op0(0b11), Op1(0b000), CRn(0b1001), CRm(0b1110), Op2(0b010), + pm_fake }, + + /* MAIR_EL1 */ + { Op0(0b11), Op1(0b000), CRn(0b1010), CRm(0b0010), Op2(0b000), + NULL, reset_unknown, MAIR_EL1 }, + /* AMAIR_EL1 */ + { Op0(0b11), Op1(0b000), CRn(0b1010), CRm(0b0011), Op2(0b000), + NULL, reset_amair_el1, AMAIR_EL1 }, + + /* VBAR_EL1 */ + { Op0(0b11), Op1(0b000), CRn(0b1100), CRm(0b0000), Op2(0b000), + NULL, reset_val, VBAR_EL1, 0 }, + /* CONTEXTIDR_EL1 */ + { Op0(0b11), Op1(0b000), CRn(0b1101), CRm(0b0000), Op2(0b001), + NULL, reset_val, CONTEXTIDR_EL1, 0 }, + /* TPIDR_EL1 */ + { Op0(0b11), Op1(0b000), CRn(0b1101), CRm(0b0000), Op2(0b100), + NULL, reset_unknown, TPIDR_EL1 }, + + /* CNTKCTL_EL1 */ + { Op0(0b11), Op1(0b000), CRn(0b1110), CRm(0b0001), Op2(0b000), + NULL, reset_val, CNTKCTL_EL1, 0}, + + /* CSSELR_EL1 */ + { Op0(0b11), Op1(0b010), CRn(0b0000), CRm(0b0000), Op2(0b000), + NULL, reset_unknown, CSSELR_EL1 }, + + /* PMCR_EL0 */ + { Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1100), Op2(0b000), + pm_fake }, + /* PMCNTENSET_EL0 */ + { Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1100), Op2(0b001), + pm_fake }, + /* PMCNTENCLR_EL0 */ + { Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1100), Op2(0b010), + pm_fake }, + /* PMOVSCLR_EL0 */ + { Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1100), Op2(0b011), + pm_fake }, + /* PMSWINC_EL0 */ + { Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1100), Op2(0b100), + pm_fake }, + /* PMSELR_EL0 */ + { Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1100), Op2(0b101), + pm_fake }, + /* PMCEID0_EL0 */ + { Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1100), Op2(0b110), + pm_fake }, + /* PMCEID1_EL0 */ + { Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1100), Op2(0b111), + pm_fake }, + /* PMCCNTR_EL0 */ + { Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1101), Op2(0b000), + pm_fake }, + /* PMXEVTYPER_EL0 */ + { Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1101), Op2(0b001), + pm_fake }, + /* PMXEVCNTR_EL0 */ + { Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1101), Op2(0b010), + pm_fake }, + /* PMUSERENR_EL0 */ + { Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1110), Op2(0b000), + pm_fake }, + /* PMOVSSET_EL0 */ + { Op0(0b11), Op1(0b011), CRn(0b1001), CRm(0b1110), Op2(0b011), + pm_fake }, + + /* TPIDR_EL0 */ + { Op0(0b11), Op1(0b011), CRn(0b1101), CRm(0b0000), Op2(0b010), + NULL, reset_unknown, TPIDR_EL0 }, + /* TPIDRRO_EL0 */ + { Op0(0b11), Op1(0b011), CRn(0b1101), CRm(0b0000), Op2(0b011), + NULL, reset_unknown, TPIDRRO_EL0 }, +}; + +/* Target specific emulation tables */ +static struct kvm_sys_reg_target_table *target_tables[KVM_ARM_NUM_TARGETS]; + +void kvm_register_target_sys_reg_table(unsigned int target, + struct kvm_sys_reg_target_table *table) +{ + target_tables[target] = table; +} + +/* Get specific register table for this target. */ +static const struct sys_reg_desc *get_target_table(unsigned target, size_t *num) +{ + struct kvm_sys_reg_target_table *table; + + table = target_tables[target]; + *num = table->table64.num; + return table->table64.table; +} + +static const struct sys_reg_desc *find_reg(const struct sys_reg_params *params, + const struct sys_reg_desc table[], + unsigned int num) +{ + unsigned int i; + + for (i = 0; i < num; i++) { + const struct sys_reg_desc *r = &table[i]; + + if (params->Op0 != r->Op0) + continue; + if (params->Op1 != r->Op1) + continue; + if (params->CRn != r->CRn) + continue; + if (params->CRm != r->CRm) + continue; + if (params->Op2 != r->Op2) + continue; + + return r; + } + return NULL; +} + +static int emulate_sys_reg(struct kvm_vcpu *vcpu, + const struct sys_reg_params *params) +{ + size_t num; + const struct sys_reg_desc *table, *r; + + table = get_target_table(vcpu->arch.target, &num); + + /* Search target-specific then generic table. */ + r = find_reg(params, table, num); + if (!r) + r = find_reg(params, sys_reg_descs, ARRAY_SIZE(sys_reg_descs)); + + if (likely(r)) { + /* + * Not having an accessor means that we have + * configured a trap that we don't know how to + * handle. This certainly qualifies as a gross bug + * that should be fixed right away. + */ + BUG_ON(!r->access); + + if (likely(r->access(vcpu, params, r))) { + /* Skip instruction, since it was emulated */ + kvm_skip_instr(vcpu, kvm_vcpu_trap_il_is32bit(vcpu)); + return 1; + } + /* If access function fails, it should complain. */ + } else { + kvm_err("Unsupported guest sys_reg access at: %lx\n", + *vcpu_pc(vcpu)); + print_sys_reg_instr(params); + } + kvm_inject_undefined(vcpu); + return 1; +} + +static void reset_sys_reg_descs(struct kvm_vcpu *vcpu, + const struct sys_reg_desc *table, size_t num) +{ + unsigned long i; + + for (i = 0; i < num; i++) + if (table[i].reset) + table[i].reset(vcpu, &table[i]); +} + +/** + * kvm_handle_sys_reg -- handles a mrs/msr trap on a guest sys_reg access + * @vcpu: The VCPU pointer + * @run: The kvm_run struct + */ +int kvm_handle_sys_reg(struct kvm_vcpu *vcpu, struct kvm_run *run) +{ + struct sys_reg_params params; + unsigned long esr = kvm_vcpu_get_hsr(vcpu); + + params.Op0 = (esr >> 20) & 3; + params.Op1 = (esr >> 14) & 0x7; + params.CRn = (esr >> 10) & 0xf; + params.CRm = (esr >> 1) & 0xf; + params.Op2 = (esr >> 17) & 0x7; + params.Rt = (esr >> 5) & 0x1f; + params.is_write = !(esr & 1); + + return emulate_sys_reg(vcpu, ¶ms); +} + +/****************************************************************************** + * Userspace API + *****************************************************************************/ + +static bool index_to_params(u64 id, struct sys_reg_params *params) +{ + switch (id & KVM_REG_SIZE_MASK) { + case KVM_REG_SIZE_U64: + /* Any unused index bits means it's not valid. */ + if (id & ~(KVM_REG_ARCH_MASK | KVM_REG_SIZE_MASK + | KVM_REG_ARM_COPROC_MASK + | KVM_REG_ARM64_SYSREG_OP0_MASK + | KVM_REG_ARM64_SYSREG_OP1_MASK + | KVM_REG_ARM64_SYSREG_CRN_MASK + | KVM_REG_ARM64_SYSREG_CRM_MASK + | KVM_REG_ARM64_SYSREG_OP2_MASK)) + return false; + params->Op0 = ((id & KVM_REG_ARM64_SYSREG_OP0_MASK) + >> KVM_REG_ARM64_SYSREG_OP0_SHIFT); + params->Op1 = ((id & KVM_REG_ARM64_SYSREG_OP1_MASK) + >> KVM_REG_ARM64_SYSREG_OP1_SHIFT); + params->CRn = ((id & KVM_REG_ARM64_SYSREG_CRN_MASK) + >> KVM_REG_ARM64_SYSREG_CRN_SHIFT); + params->CRm = ((id & KVM_REG_ARM64_SYSREG_CRM_MASK) + >> KVM_REG_ARM64_SYSREG_CRM_SHIFT); + params->Op2 = ((id & KVM_REG_ARM64_SYSREG_OP2_MASK) + >> KVM_REG_ARM64_SYSREG_OP2_SHIFT); + return true; + default: + return false; + } +} + +/* Decode an index value, and find the sys_reg_desc entry. */ +static const struct sys_reg_desc *index_to_sys_reg_desc(struct kvm_vcpu *vcpu, + u64 id) +{ + size_t num; + const struct sys_reg_desc *table, *r; + struct sys_reg_params params; + + /* We only do sys_reg for now. */ + if ((id & KVM_REG_ARM_COPROC_MASK) != KVM_REG_ARM64_SYSREG) + return NULL; + + if (!index_to_params(id, ¶ms)) + return NULL; + + table = get_target_table(vcpu->arch.target, &num); + r = find_reg(¶ms, table, num); + if (!r) + r = find_reg(¶ms, sys_reg_descs, ARRAY_SIZE(sys_reg_descs)); + + /* Not saved in the sys_reg array? */ + if (r && !r->reg) + r = NULL; + + return r; +} + +/* + * These are the invariant sys_reg registers: we let the guest see the + * host versions of these, so they're part of the guest state. + * + * A future CPU may provide a mechanism to present different values to + * the guest, or a future kvm may trap them. + */ + +#define FUNCTION_INVARIANT(reg) \ + static void get_##reg(struct kvm_vcpu *v, \ + const struct sys_reg_desc *r) \ + { \ + u64 val; \ + \ + asm volatile("mrs %0, " __stringify(reg) "\n" \ + : "=r" (val)); \ + ((struct sys_reg_desc *)r)->val = val; \ + } + +FUNCTION_INVARIANT(midr_el1) +FUNCTION_INVARIANT(ctr_el0) +FUNCTION_INVARIANT(revidr_el1) +FUNCTION_INVARIANT(id_pfr0_el1) +FUNCTION_INVARIANT(id_pfr1_el1) +FUNCTION_INVARIANT(id_dfr0_el1) +FUNCTION_INVARIANT(id_afr0_el1) +FUNCTION_INVARIANT(id_mmfr0_el1) +FUNCTION_INVARIANT(id_mmfr1_el1) +FUNCTION_INVARIANT(id_mmfr2_el1) +FUNCTION_INVARIANT(id_mmfr3_el1) +FUNCTION_INVARIANT(id_isar0_el1) +FUNCTION_INVARIANT(id_isar1_el1) +FUNCTION_INVARIANT(id_isar2_el1) +FUNCTION_INVARIANT(id_isar3_el1) +FUNCTION_INVARIANT(id_isar4_el1) +FUNCTION_INVARIANT(id_isar5_el1) +FUNCTION_INVARIANT(clidr_el1) +FUNCTION_INVARIANT(aidr_el1) + +/* ->val is filled in by kvm_sys_reg_table_init() */ +static struct sys_reg_desc invariant_sys_regs[] = { + { Op0(0b11), Op1(0b000), CRn(0b0000), CRm(0b0000), Op2(0b000), + NULL, get_midr_el1 }, + { Op0(0b11), Op1(0b000), CRn(0b0000), CRm(0b0000), Op2(0b110), + NULL, get_revidr_el1 }, + { Op0(0b11), Op1(0b000), CRn(0b0000), CRm(0b0001), Op2(0b000), + NULL, get_id_pfr0_el1 }, + { Op0(0b11), Op1(0b000), CRn(0b0000), CRm(0b0001), Op2(0b001), + NULL, get_id_pfr1_el1 }, + { Op0(0b11), Op1(0b000), CRn(0b0000), CRm(0b0001), Op2(0b010), + NULL, get_id_dfr0_el1 }, + { Op0(0b11), Op1(0b000), CRn(0b0000), CRm(0b0001), Op2(0b011), + NULL, get_id_afr0_el1 }, + { Op0(0b11), Op1(0b000), CRn(0b0000), CRm(0b0001), Op2(0b100), + NULL, get_id_mmfr0_el1 }, + { Op0(0b11), Op1(0b000), CRn(0b0000), CRm(0b0001), Op2(0b101), + NULL, get_id_mmfr1_el1 }, + { Op0(0b11), Op1(0b000), CRn(0b0000), CRm(0b0001), Op2(0b110), + NULL, get_id_mmfr2_el1 }, + { Op0(0b11), Op1(0b000), CRn(0b0000), CRm(0b0001), Op2(0b111), + NULL, get_id_mmfr3_el1 }, + { Op0(0b11), Op1(0b000), CRn(0b0000), CRm(0b0010), Op2(0b000), + NULL, get_id_isar0_el1 }, + { Op0(0b11), Op1(0b000), CRn(0b0000), CRm(0b0010), Op2(0b001), + NULL, get_id_isar1_el1 }, + { Op0(0b11), Op1(0b000), CRn(0b0000), CRm(0b0010), Op2(0b010), + NULL, get_id_isar2_el1 }, + { Op0(0b11), Op1(0b000), CRn(0b0000), CRm(0b0010), Op2(0b011), + NULL, get_id_isar3_el1 }, + { Op0(0b11), Op1(0b000), CRn(0b0000), CRm(0b0010), Op2(0b100), + NULL, get_id_isar4_el1 }, + { Op0(0b11), Op1(0b000), CRn(0b0000), CRm(0b0010), Op2(0b101), + NULL, get_id_isar5_el1 }, + { Op0(0b11), Op1(0b001), CRn(0b0000), CRm(0b0000), Op2(0b001), + NULL, get_clidr_el1 }, + { Op0(0b11), Op1(0b001), CRn(0b0000), CRm(0b0000), Op2(0b111), + NULL, get_aidr_el1 }, + { Op0(0b11), Op1(0b011), CRn(0b0000), CRm(0b0000), Op2(0b001), + NULL, get_ctr_el0 }, +}; + +static int reg_from_user(void *val, const void __user *uaddr, u64 id) +{ + /* This Just Works because we are little endian. */ + if (copy_from_user(val, uaddr, KVM_REG_SIZE(id)) != 0) + return -EFAULT; + return 0; +} + +static int reg_to_user(void __user *uaddr, const void *val, u64 id) +{ + /* This Just Works because we are little endian. */ + if (copy_to_user(uaddr, val, KVM_REG_SIZE(id)) != 0) + return -EFAULT; + return 0; +} + +static int get_invariant_sys_reg(u64 id, void __user *uaddr) +{ + struct sys_reg_params params; + const struct sys_reg_desc *r; + + if (!index_to_params(id, ¶ms)) + return -ENOENT; + + r = find_reg(¶ms, invariant_sys_regs, ARRAY_SIZE(invariant_sys_regs)); + if (!r) + return -ENOENT; + + return reg_to_user(uaddr, &r->val, id); +} + +static int set_invariant_sys_reg(u64 id, void __user *uaddr) +{ + struct sys_reg_params params; + const struct sys_reg_desc *r; + int err; + u64 val = 0; /* Make sure high bits are 0 for 32-bit regs */ + + if (!index_to_params(id, ¶ms)) + return -ENOENT; + r = find_reg(¶ms, invariant_sys_regs, ARRAY_SIZE(invariant_sys_regs)); + if (!r) + return -ENOENT; + + err = reg_from_user(&val, uaddr, id); + if (err) + return err; + + /* This is what we mean by invariant: you can't change it. */ + if (r->val != val) + return -EINVAL; + + return 0; +} + +static bool is_valid_cache(u32 val) +{ + u32 level, ctype; + + if (val >= CSSELR_MAX) + return -ENOENT; + + /* Bottom bit is Instruction or Data bit. Next 3 bits are level. */ + level = (val >> 1); + ctype = (cache_levels >> (level * 3)) & 7; + + switch (ctype) { + case 0: /* No cache */ + return false; + case 1: /* Instruction cache only */ + return (val & 1); + case 2: /* Data cache only */ + case 4: /* Unified cache */ + return !(val & 1); + case 3: /* Separate instruction and data caches */ + return true; + default: /* Reserved: we can't know instruction or data. */ + return false; + } +} + +static int demux_c15_get(u64 id, void __user *uaddr) +{ + u32 val; + u32 __user *uval = uaddr; + + /* Fail if we have unknown bits set. */ + if (id & ~(KVM_REG_ARCH_MASK|KVM_REG_SIZE_MASK|KVM_REG_ARM_COPROC_MASK + | ((1 << KVM_REG_ARM_COPROC_SHIFT)-1))) + return -ENOENT; + + switch (id & KVM_REG_ARM_DEMUX_ID_MASK) { + case KVM_REG_ARM_DEMUX_ID_CCSIDR: + if (KVM_REG_SIZE(id) != 4) + return -ENOENT; + val = (id & KVM_REG_ARM_DEMUX_VAL_MASK) + >> KVM_REG_ARM_DEMUX_VAL_SHIFT; + if (!is_valid_cache(val)) + return -ENOENT; + + return put_user(get_ccsidr(val), uval); + default: + return -ENOENT; + } +} + +static int demux_c15_set(u64 id, void __user *uaddr) +{ + u32 val, newval; + u32 __user *uval = uaddr; + + /* Fail if we have unknown bits set. */ + if (id & ~(KVM_REG_ARCH_MASK|KVM_REG_SIZE_MASK|KVM_REG_ARM_COPROC_MASK + | ((1 << KVM_REG_ARM_COPROC_SHIFT)-1))) + return -ENOENT; + + switch (id & KVM_REG_ARM_DEMUX_ID_MASK) { + case KVM_REG_ARM_DEMUX_ID_CCSIDR: + if (KVM_REG_SIZE(id) != 4) + return -ENOENT; + val = (id & KVM_REG_ARM_DEMUX_VAL_MASK) + >> KVM_REG_ARM_DEMUX_VAL_SHIFT; + if (!is_valid_cache(val)) + return -ENOENT; + + if (get_user(newval, uval)) + return -EFAULT; + + /* This is also invariant: you can't change it. */ + if (newval != get_ccsidr(val)) + return -EINVAL; + return 0; + default: + return -ENOENT; + } +} + +int kvm_arm_sys_reg_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) +{ + const struct sys_reg_desc *r; + void __user *uaddr = (void __user *)(unsigned long)reg->addr; + + if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_DEMUX) + return demux_c15_get(reg->id, uaddr); + + if (KVM_REG_SIZE(reg->id) != sizeof(__u64)) + return -ENOENT; + + r = index_to_sys_reg_desc(vcpu, reg->id); + if (!r) + return get_invariant_sys_reg(reg->id, uaddr); + + return reg_to_user(uaddr, &vcpu_sys_reg(vcpu, r->reg), reg->id); +} + +int kvm_arm_sys_reg_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) +{ + const struct sys_reg_desc *r; + void __user *uaddr = (void __user *)(unsigned long)reg->addr; + + if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_DEMUX) + return demux_c15_set(reg->id, uaddr); + + if (KVM_REG_SIZE(reg->id) != sizeof(__u64)) + return -ENOENT; + + r = index_to_sys_reg_desc(vcpu, reg->id); + if (!r) + return set_invariant_sys_reg(reg->id, uaddr); + + return reg_from_user(&vcpu_sys_reg(vcpu, r->reg), uaddr, reg->id); +} + +static unsigned int num_demux_regs(void) +{ + unsigned int i, count = 0; + + for (i = 0; i < CSSELR_MAX; i++) + if (is_valid_cache(i)) + count++; + + return count; +} + +static int write_demux_regids(u64 __user *uindices) +{ + u64 val = KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_DEMUX; + unsigned int i; + + val |= KVM_REG_ARM_DEMUX_ID_CCSIDR; + for (i = 0; i < CSSELR_MAX; i++) { + if (!is_valid_cache(i)) + continue; + if (put_user(val | i, uindices)) + return -EFAULT; + uindices++; + } + return 0; +} + +static u64 sys_reg_to_index(const struct sys_reg_desc *reg) +{ + return (KVM_REG_ARM64 | KVM_REG_SIZE_U64 | + KVM_REG_ARM64_SYSREG | + (reg->Op0 << KVM_REG_ARM64_SYSREG_OP0_SHIFT) | + (reg->Op1 << KVM_REG_ARM64_SYSREG_OP1_SHIFT) | + (reg->CRn << KVM_REG_ARM64_SYSREG_CRN_SHIFT) | + (reg->CRm << KVM_REG_ARM64_SYSREG_CRM_SHIFT) | + (reg->Op2 << KVM_REG_ARM64_SYSREG_OP2_SHIFT)); +} + +static bool copy_reg_to_user(const struct sys_reg_desc *reg, u64 __user **uind) +{ + if (!*uind) + return true; + + if (put_user(sys_reg_to_index(reg), *uind)) + return false; + + (*uind)++; + return true; +} + +/* Assumed ordered tables, see kvm_sys_reg_table_init. */ +static int walk_sys_regs(struct kvm_vcpu *vcpu, u64 __user *uind) +{ + const struct sys_reg_desc *i1, *i2, *end1, *end2; + unsigned int total = 0; + size_t num; + + /* We check for duplicates here, to allow arch-specific overrides. */ + i1 = get_target_table(vcpu->arch.target, &num); + end1 = i1 + num; + i2 = sys_reg_descs; + end2 = sys_reg_descs + ARRAY_SIZE(sys_reg_descs); + + BUG_ON(i1 == end1 || i2 == end2); + + /* Walk carefully, as both tables may refer to the same register. */ + while (i1 || i2) { + int cmp = cmp_sys_reg(i1, i2); + /* target-specific overrides generic entry. */ + if (cmp <= 0) { + /* Ignore registers we trap but don't save. */ + if (i1->reg) { + if (!copy_reg_to_user(i1, &uind)) + return -EFAULT; + total++; + } + } else { + /* Ignore registers we trap but don't save. */ + if (i2->reg) { + if (!copy_reg_to_user(i2, &uind)) + return -EFAULT; + total++; + } + } + + if (cmp <= 0 && ++i1 == end1) + i1 = NULL; + if (cmp >= 0 && ++i2 == end2) + i2 = NULL; + } + return total; +} + +unsigned long kvm_arm_num_sys_reg_descs(struct kvm_vcpu *vcpu) +{ + return ARRAY_SIZE(invariant_sys_regs) + + num_demux_regs() + + walk_sys_regs(vcpu, (u64 __user *)NULL); +} + +int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices) +{ + unsigned int i; + int err; + + /* Then give them all the invariant registers' indices. */ + for (i = 0; i < ARRAY_SIZE(invariant_sys_regs); i++) { + if (put_user(sys_reg_to_index(&invariant_sys_regs[i]), uindices)) + return -EFAULT; + uindices++; + } + + err = walk_sys_regs(vcpu, uindices); + if (err < 0) + return err; + uindices += err; + + return write_demux_regids(uindices); +} + +void kvm_sys_reg_table_init(void) +{ + unsigned int i; + struct sys_reg_desc clidr; + + /* Make sure tables are unique and in order. */ + for (i = 1; i < ARRAY_SIZE(sys_reg_descs); i++) + BUG_ON(cmp_sys_reg(&sys_reg_descs[i-1], &sys_reg_descs[i]) >= 0); + + /* We abuse the reset function to overwrite the table itself. */ + for (i = 0; i < ARRAY_SIZE(invariant_sys_regs); i++) + invariant_sys_regs[i].reset(NULL, &invariant_sys_regs[i]); + + /* + * CLIDR format is awkward, so clean it up. See ARM B4.1.20: + * + * If software reads the Cache Type fields from Ctype1 + * upwards, once it has seen a value of 0b000, no caches + * exist at further-out levels of the hierarchy. So, for + * example, if Ctype3 is the first Cache Type field with a + * value of 0b000, the values of Ctype4 to Ctype7 must be + * ignored. + */ + get_clidr_el1(NULL, &clidr); /* Ugly... */ + cache_levels = clidr.val; + for (i = 0; i < 7; i++) + if (((cache_levels >> (i*3)) & 7) == 0) + break; + /* Clear all higher bits. */ + cache_levels &= (1 << (i*3))-1; +} + +/** + * kvm_reset_sys_regs - sets system registers to reset value + * @vcpu: The VCPU pointer + * + * This function finds the right table above and sets the registers on the + * virtual CPU struct to their architecturally defined reset values. + */ +void kvm_reset_sys_regs(struct kvm_vcpu *vcpu) +{ + size_t num; + const struct sys_reg_desc *table; + + /* Catch someone adding a register without putting in reset entry. */ + memset(&vcpu->arch.ctxt.sys_regs, 0x42, sizeof(vcpu->arch.ctxt.sys_regs)); + + /* Generic chip reset first (so target could override). */ + reset_sys_reg_descs(vcpu, sys_reg_descs, ARRAY_SIZE(sys_reg_descs)); + + table = get_target_table(vcpu->arch.target, &num); + reset_sys_reg_descs(vcpu, table, num); + + for (num = 1; num < NR_SYS_REGS; num++) + if (vcpu_sys_reg(vcpu, num) == 0x4242424242424242) + panic("Didn't reset vcpu_sys_reg(%zi)", num); +} diff --git a/arch/arm64/kvm/sys_regs.h b/arch/arm64/kvm/sys_regs.h new file mode 100644 index 000000000000..d50d3722998e --- /dev/null +++ b/arch/arm64/kvm/sys_regs.h @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2012,2013 - ARM Ltd + * Author: Marc Zyngier + * + * Derived from arch/arm/kvm/coproc.h + * Copyright (C) 2012 - Virtual Open Systems and Columbia University + * Authors: Christoffer Dall + * + * 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 . + */ + +#ifndef __ARM64_KVM_SYS_REGS_LOCAL_H__ +#define __ARM64_KVM_SYS_REGS_LOCAL_H__ + +struct sys_reg_params { + u8 Op0; + u8 Op1; + u8 CRn; + u8 CRm; + u8 Op2; + u8 Rt; + bool is_write; +}; + +struct sys_reg_desc { + /* MRS/MSR instruction which accesses it. */ + u8 Op0; + u8 Op1; + u8 CRn; + u8 CRm; + u8 Op2; + + /* Trapped access from guest, if non-NULL. */ + bool (*access)(struct kvm_vcpu *, + const struct sys_reg_params *, + const struct sys_reg_desc *); + + /* Initialization for vcpu. */ + void (*reset)(struct kvm_vcpu *, const struct sys_reg_desc *); + + /* Index into sys_reg[], or 0 if we don't need to save it. */ + int reg; + + /* Value (usually reset value) */ + u64 val; +}; + +static inline void print_sys_reg_instr(const struct sys_reg_params *p) +{ + /* Look, we even formatted it for you to paste into the table! */ + kvm_pr_unimpl(" { Op0(%2u), Op1(%2u), CRn(%2u), CRm(%2u), Op2(%2u), func_%s },\n", + p->Op0, p->Op1, p->CRn, p->CRm, p->Op2, p->is_write ? "write" : "read"); +} + +static inline bool ignore_write(struct kvm_vcpu *vcpu, + const struct sys_reg_params *p) +{ + return true; +} + +static inline bool read_zero(struct kvm_vcpu *vcpu, + const struct sys_reg_params *p) +{ + *vcpu_reg(vcpu, p->Rt) = 0; + return true; +} + +static inline bool write_to_read_only(struct kvm_vcpu *vcpu, + const struct sys_reg_params *params) +{ + kvm_debug("sys_reg write to read-only register at: %lx\n", + *vcpu_pc(vcpu)); + print_sys_reg_instr(params); + return false; +} + +static inline bool read_from_write_only(struct kvm_vcpu *vcpu, + const struct sys_reg_params *params) +{ + kvm_debug("sys_reg read to write-only register at: %lx\n", + *vcpu_pc(vcpu)); + print_sys_reg_instr(params); + return false; +} + +/* Reset functions */ +static inline void reset_unknown(struct kvm_vcpu *vcpu, + const struct sys_reg_desc *r) +{ + BUG_ON(!r->reg); + BUG_ON(r->reg >= NR_SYS_REGS); + vcpu_sys_reg(vcpu, r->reg) = 0x1de7ec7edbadc0deULL; +} + +static inline void reset_val(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r) +{ + BUG_ON(!r->reg); + BUG_ON(r->reg >= NR_SYS_REGS); + vcpu_sys_reg(vcpu, r->reg) = r->val; +} + +static inline int cmp_sys_reg(const struct sys_reg_desc *i1, + const struct sys_reg_desc *i2) +{ + BUG_ON(i1 == i2); + if (!i1) + return 1; + else if (!i2) + return -1; + if (i1->Op0 != i2->Op0) + return i1->Op0 - i2->Op0; + if (i1->Op1 != i2->Op1) + return i1->Op1 - i2->Op1; + if (i1->CRn != i2->CRn) + return i1->CRn - i2->CRn; + if (i1->CRm != i2->CRm) + return i1->CRm - i2->CRm; + return i1->Op2 - i2->Op2; +} + + +#define Op0(_x) .Op0 = _x +#define Op1(_x) .Op1 = _x +#define CRn(_x) .CRn = _x +#define CRm(_x) .CRm = _x +#define Op2(_x) .Op2 = _x + +#endif /* __ARM64_KVM_SYS_REGS_LOCAL_H__ */ diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index a5c86fc34a37..2d1bcb891468 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -783,6 +783,7 @@ struct kvm_dirty_tlb { #define KVM_REG_IA64 0x3000000000000000ULL #define KVM_REG_ARM 0x4000000000000000ULL #define KVM_REG_S390 0x5000000000000000ULL +#define KVM_REG_ARM64 0x6000000000000000ULL #define KVM_REG_SIZE_SHIFT 52 #define KVM_REG_SIZE_MASK 0x00f0000000000000ULL -- cgit v1.2.3 From b990a9d3152bddca62cc1f8bf80518430b98737b Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 6 Feb 2013 17:30:48 +0000 Subject: arm64: KVM: CPU specific system registers handling Add the support code for CPU specific system registers. Not much here yet. Reviewed-by: Christopher Covington Reviewed-by: Catalin Marinas Signed-off-by: Marc Zyngier --- arch/arm64/kvm/sys_regs_generic_v8.c | 85 ++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 arch/arm64/kvm/sys_regs_generic_v8.c diff --git a/arch/arm64/kvm/sys_regs_generic_v8.c b/arch/arm64/kvm/sys_regs_generic_v8.c new file mode 100644 index 000000000000..d4e803907312 --- /dev/null +++ b/arch/arm64/kvm/sys_regs_generic_v8.c @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2012,2013 - ARM Ltd + * Author: Marc Zyngier + * + * Based on arch/arm/kvm/coproc_a15.c: + * Copyright (C) 2012 - Virtual Open Systems and Columbia University + * Authors: Rusty Russell + * Christoffer Dall + * + * 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 . + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sys_regs.h" + +static bool access_actlr(struct kvm_vcpu *vcpu, + const struct sys_reg_params *p, + const struct sys_reg_desc *r) +{ + if (p->is_write) + return ignore_write(vcpu, p); + + *vcpu_reg(vcpu, p->Rt) = vcpu_sys_reg(vcpu, ACTLR_EL1); + return true; +} + +static void reset_actlr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r) +{ + u64 actlr; + + asm volatile("mrs %0, actlr_el1\n" : "=r" (actlr)); + vcpu_sys_reg(vcpu, ACTLR_EL1) = actlr; +} + +/* + * Implementation specific sys-reg registers. + * Important: Must be sorted ascending by Op0, Op1, CRn, CRm, Op2 + */ +static const struct sys_reg_desc genericv8_sys_regs[] = { + /* ACTLR_EL1 */ + { Op0(0b11), Op1(0b000), CRn(0b0001), CRm(0b0000), Op2(0b001), + access_actlr, reset_actlr, ACTLR_EL1 }, +}; + +static struct kvm_sys_reg_target_table genericv8_target_table = { + .table64 = { + .table = genericv8_sys_regs, + .num = ARRAY_SIZE(genericv8_sys_regs), + }, +}; + +static int __init sys_reg_genericv8_init(void) +{ + unsigned int i; + + for (i = 1; i < ARRAY_SIZE(genericv8_sys_regs); i++) + BUG_ON(cmp_sys_reg(&genericv8_sys_regs[i-1], + &genericv8_sys_regs[i]) >= 0); + + kvm_register_target_sys_reg_table(KVM_ARM_TARGET_AEM_V8, + &genericv8_target_table); + kvm_register_target_sys_reg_table(KVM_ARM_TARGET_FOUNDATION_V8, + &genericv8_target_table); + kvm_register_target_sys_reg_table(KVM_ARM_TARGET_CORTEX_A57, + &genericv8_target_table); + return 0; +} +late_initcall(sys_reg_genericv8_init); -- cgit v1.2.3 From f4672752c321ea36ce099cebdd7a082a8f327505 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 10 Dec 2012 16:23:59 +0000 Subject: arm64: KVM: virtual CPU reset Provide the reset code for a virtual CPU booted in 64bit mode. Reviewed-by: Christopher Covington Reviewed-by: Catalin Marinas Signed-off-by: Marc Zyngier --- arch/arm64/kvm/reset.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 arch/arm64/kvm/reset.c diff --git a/arch/arm64/kvm/reset.c b/arch/arm64/kvm/reset.c new file mode 100644 index 000000000000..f6536a06231a --- /dev/null +++ b/arch/arm64/kvm/reset.c @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2012,2013 - ARM Ltd + * Author: Marc Zyngier + * + * Derived from arch/arm/kvm/reset.c + * Copyright (C) 2012 - Virtual Open Systems and Columbia University + * Author: Christoffer Dall + * + * 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 . + */ + +#include +#include +#include + +#include +#include +#include +#include + +/* + * ARMv8 Reset Values + */ +static const struct kvm_regs default_regs_reset = { + .regs.pstate = (PSR_MODE_EL1h | PSR_A_BIT | PSR_I_BIT | + PSR_F_BIT | PSR_D_BIT), +}; + +int kvm_arch_dev_ioctl_check_extension(long ext) +{ + int r; + + switch (ext) { + default: + r = 0; + } + + return r; +} + +/** + * kvm_reset_vcpu - sets core registers and sys_regs to reset value + * @vcpu: The VCPU pointer + * + * This function finds the right table above and sets the registers on + * the virtual CPU struct to their architectually defined reset + * values. + */ +int kvm_reset_vcpu(struct kvm_vcpu *vcpu) +{ + const struct kvm_regs *cpu_reset; + + switch (vcpu->arch.target) { + default: + cpu_reset = &default_regs_reset; + break; + } + + /* Reset core registers */ + memcpy(vcpu_gp_regs(vcpu), cpu_reset, sizeof(*cpu_reset)); + + /* Reset system registers */ + kvm_reset_sys_regs(vcpu); + + return 0; +} -- cgit v1.2.3 From 4f8d6632ec71372a3b8dbb4775662c2c9025d173 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 10 Dec 2012 16:29:28 +0000 Subject: arm64: KVM: kvm_arch and kvm_vcpu_arch definitions Provide the architecture dependent structures for VM and vcpu abstractions. Reviewed-by: Christopher Covington Reviewed-by: Catalin Marinas Signed-off-by: Marc Zyngier --- arch/arm64/include/asm/kvm_host.h | 186 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 arch/arm64/include/asm/kvm_host.h diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h new file mode 100644 index 000000000000..4a2622f5e81f --- /dev/null +++ b/arch/arm64/include/asm/kvm_host.h @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2012,2013 - ARM Ltd + * Author: Marc Zyngier + * + * Derived from arch/arm/include/asm/kvm_host.h: + * Copyright (C) 2012 - Virtual Open Systems and Columbia University + * Author: Christoffer Dall + * + * 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 . + */ + +#ifndef __ARM64_KVM_HOST_H__ +#define __ARM64_KVM_HOST_H__ + +#include +#include +#include + +#define KVM_MAX_VCPUS 4 +#define KVM_USER_MEM_SLOTS 32 +#define KVM_PRIVATE_MEM_SLOTS 4 +#define KVM_COALESCED_MMIO_PAGE_OFFSET 1 + +#include +#include + +#define KVM_VCPU_MAX_FEATURES 0 + +/* We don't currently support large pages. */ +#define KVM_HPAGE_GFN_SHIFT(x) 0 +#define KVM_NR_PAGE_SIZES 1 +#define KVM_PAGES_PER_HPAGE(x) (1UL<<31) + +struct kvm_vcpu; +int kvm_target_cpu(void); +int kvm_reset_vcpu(struct kvm_vcpu *vcpu); +int kvm_arch_dev_ioctl_check_extension(long ext); + +struct kvm_arch { + /* The VMID generation used for the virt. memory system */ + u64 vmid_gen; + u32 vmid; + + /* 1-level 2nd stage table and lock */ + spinlock_t pgd_lock; + pgd_t *pgd; + + /* VTTBR value associated with above pgd and vmid */ + u64 vttbr; + + /* Interrupt controller */ + struct vgic_dist vgic; + + /* Timer */ + struct arch_timer_kvm timer; +}; + +#define KVM_NR_MEM_OBJS 40 + +/* + * We don't want allocation failures within the mmu code, so we preallocate + * enough memory for a single page fault in a cache. + */ +struct kvm_mmu_memory_cache { + int nobjs; + void *objects[KVM_NR_MEM_OBJS]; +}; + +struct kvm_vcpu_fault_info { + u32 esr_el2; /* Hyp Syndrom Register */ + u64 far_el2; /* Hyp Fault Address Register */ + u64 hpfar_el2; /* Hyp IPA Fault Address Register */ +}; + +struct kvm_cpu_context { + struct kvm_regs gp_regs; + u64 sys_regs[NR_SYS_REGS]; +}; + +typedef struct kvm_cpu_context kvm_cpu_context_t; + +struct kvm_vcpu_arch { + struct kvm_cpu_context ctxt; + + /* HYP configuration */ + u64 hcr_el2; + + /* Exception Information */ + struct kvm_vcpu_fault_info fault; + + /* Pointer to host CPU context */ + kvm_cpu_context_t *host_cpu_context; + + /* VGIC state */ + struct vgic_cpu vgic_cpu; + struct arch_timer_cpu timer_cpu; + + /* + * Anything that is not used directly from assembly code goes + * here. + */ + /* dcache set/way operation pending */ + int last_pcpu; + cpumask_t require_dcache_flush; + + /* Don't run the guest */ + bool pause; + + /* IO related fields */ + struct kvm_decode mmio_decode; + + /* Interrupt related fields */ + u64 irq_lines; /* IRQ and FIQ levels */ + + /* Cache some mmu pages needed inside spinlock regions */ + struct kvm_mmu_memory_cache mmu_page_cache; + + /* Target CPU and feature flags */ + u32 target; + DECLARE_BITMAP(features, KVM_VCPU_MAX_FEATURES); + + /* Detect first run of a vcpu */ + bool has_run_once; +}; + +#define vcpu_gp_regs(v) (&(v)->arch.ctxt.gp_regs) +#define vcpu_sys_reg(v,r) ((v)->arch.ctxt.sys_regs[(r)]) +#define vcpu_cp15(v,r) ((v)->arch.ctxt.cp15[(r)]) + +struct kvm_vm_stat { + u32 remote_tlb_flush; +}; + +struct kvm_vcpu_stat { + u32 halt_wakeup; +}; + +struct kvm_vcpu_init; +int kvm_vcpu_set_target(struct kvm_vcpu *vcpu, + const struct kvm_vcpu_init *init); +unsigned long kvm_arm_num_regs(struct kvm_vcpu *vcpu); +int kvm_arm_copy_reg_indices(struct kvm_vcpu *vcpu, u64 __user *indices); +struct kvm_one_reg; +int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg); +int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg); + +#define KVM_ARCH_WANT_MMU_NOTIFIER +struct kvm; +int kvm_unmap_hva(struct kvm *kvm, unsigned long hva); +int kvm_unmap_hva_range(struct kvm *kvm, + unsigned long start, unsigned long end); +void kvm_set_spte_hva(struct kvm *kvm, unsigned long hva, pte_t pte); + +/* We do not have shadow page tables, hence the empty hooks */ +static inline int kvm_age_hva(struct kvm *kvm, unsigned long hva) +{ + return 0; +} + +static inline int kvm_test_age_hva(struct kvm *kvm, unsigned long hva) +{ + return 0; +} + +struct kvm_vcpu *kvm_arm_get_running_vcpu(void); +struct kvm_vcpu __percpu **kvm_get_running_vcpus(void); + +u64 kvm_call_hyp(void *hypfn, ...); + +int handle_exit(struct kvm_vcpu *vcpu, struct kvm_run *run, + int exception_index); + +int kvm_perf_init(void); +int kvm_perf_teardown(void); + +#endif /* __ARM64_KVM_HOST_H__ */ -- cgit v1.2.3 From d7246bf3571a82834984a42db52261525bc11159 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 10 Dec 2012 16:29:50 +0000 Subject: arm64: KVM: MMIO access backend Define the necessary structures to perform an MMIO access. Reviewed-by: Christopher Covington Reviewed-by: Catalin Marinas Signed-off-by: Marc Zyngier --- arch/arm64/include/asm/kvm_mmio.h | 59 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 arch/arm64/include/asm/kvm_mmio.h diff --git a/arch/arm64/include/asm/kvm_mmio.h b/arch/arm64/include/asm/kvm_mmio.h new file mode 100644 index 000000000000..fc2f689c0694 --- /dev/null +++ b/arch/arm64/include/asm/kvm_mmio.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2012 - Virtual Open Systems and Columbia University + * Author: Christoffer Dall + * + * 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 . + */ + +#ifndef __ARM64_KVM_MMIO_H__ +#define __ARM64_KVM_MMIO_H__ + +#include +#include +#include + +/* + * This is annoying. The mmio code requires this, even if we don't + * need any decoding. To be fixed. + */ +struct kvm_decode { + unsigned long rt; + bool sign_extend; +}; + +/* + * The in-kernel MMIO emulation code wants to use a copy of run->mmio, + * which is an anonymous type. Use our own type instead. + */ +struct kvm_exit_mmio { + phys_addr_t phys_addr; + u8 data[8]; + u32 len; + bool is_write; +}; + +static inline void kvm_prepare_mmio(struct kvm_run *run, + struct kvm_exit_mmio *mmio) +{ + run->mmio.phys_addr = mmio->phys_addr; + run->mmio.len = mmio->len; + run->mmio.is_write = mmio->is_write; + memcpy(run->mmio.data, mmio->data, mmio->len); + run->exit_reason = KVM_EXIT_MMIO; +} + +int kvm_handle_mmio_return(struct kvm_vcpu *vcpu, struct kvm_run *run); +int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run, + phys_addr_t fault_ipa); + +#endif /* __ARM64_KVM_MMIO_H__ */ -- cgit v1.2.3 From 2f4a07c5f9fe4a5cdb9867e1e2fcab3165846ea7 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 10 Dec 2012 16:37:02 +0000 Subject: arm64: KVM: guest one-reg interface Let userspace play with the guest registers. Reviewed-by: Christopher Covington Signed-off-by: Marc Zyngier --- arch/arm64/kvm/guest.c | 259 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 259 insertions(+) create mode 100644 arch/arm64/kvm/guest.c diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c new file mode 100644 index 000000000000..3d7518a7ebaa --- /dev/null +++ b/arch/arm64/kvm/guest.c @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2012,2013 - ARM Ltd + * Author: Marc Zyngier + * + * Derived from arch/arm/kvm/guest.c: + * Copyright (C) 2012 - Virtual Open Systems and Columbia University + * Author: Christoffer Dall + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct kvm_stats_debugfs_item debugfs_entries[] = { + { NULL } +}; + +int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu) +{ + vcpu->arch.hcr_el2 = HCR_GUEST_FLAGS; + return 0; +} + +static u64 core_reg_offset_from_id(u64 id) +{ + return id & ~(KVM_REG_ARCH_MASK | KVM_REG_SIZE_MASK | KVM_REG_ARM_CORE); +} + +static int get_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) +{ + /* + * Because the kvm_regs structure is a mix of 32, 64 and + * 128bit fields, we index it as if it was a 32bit + * array. Hence below, nr_regs is the number of entries, and + * off the index in the "array". + */ + __u32 __user *uaddr = (__u32 __user *)(unsigned long)reg->addr; + struct kvm_regs *regs = vcpu_gp_regs(vcpu); + int nr_regs = sizeof(*regs) / sizeof(__u32); + u32 off; + + /* Our ID is an index into the kvm_regs struct. */ + off = core_reg_offset_from_id(reg->id); + if (off >= nr_regs || + (off + (KVM_REG_SIZE(reg->id) / sizeof(__u32))) >= nr_regs) + return -ENOENT; + + if (copy_to_user(uaddr, ((u32 *)regs) + off, KVM_REG_SIZE(reg->id))) + return -EFAULT; + + return 0; +} + +static int set_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) +{ + __u32 __user *uaddr = (__u32 __user *)(unsigned long)reg->addr; + struct kvm_regs *regs = vcpu_gp_regs(vcpu); + int nr_regs = sizeof(*regs) / sizeof(__u32); + __uint128_t tmp; + void *valp = &tmp; + u64 off; + int err = 0; + + /* Our ID is an index into the kvm_regs struct. */ + off = core_reg_offset_from_id(reg->id); + if (off >= nr_regs || + (off + (KVM_REG_SIZE(reg->id) / sizeof(__u32))) >= nr_regs) + return -ENOENT; + + if (KVM_REG_SIZE(reg->id) > sizeof(tmp)) + return -EINVAL; + + if (copy_from_user(valp, uaddr, KVM_REG_SIZE(reg->id))) { + err = -EFAULT; + goto out; + } + + if (off == KVM_REG_ARM_CORE_REG(regs.pstate)) { + u32 mode = (*(u32 *)valp) & COMPAT_PSR_MODE_MASK; + switch (mode) { + case PSR_MODE_EL0t: + case PSR_MODE_EL1t: + case PSR_MODE_EL1h: + break; + default: + err = -EINVAL; + goto out; + } + } + + memcpy((u32 *)regs + off, valp, KVM_REG_SIZE(reg->id)); +out: + return err; +} + +int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs) +{ + return -EINVAL; +} + +int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs) +{ + return -EINVAL; +} + +static unsigned long num_core_regs(void) +{ + return sizeof(struct kvm_regs) / sizeof(__u32); +} + +/** + * kvm_arm_num_regs - how many registers do we present via KVM_GET_ONE_REG + * + * This is for all registers. + */ +unsigned long kvm_arm_num_regs(struct kvm_vcpu *vcpu) +{ + return num_core_regs() + kvm_arm_num_sys_reg_descs(vcpu); +} + +/** + * kvm_arm_copy_reg_indices - get indices of all registers. + * + * We do core registers right here, then we apppend system regs. + */ +int kvm_arm_copy_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices) +{ + unsigned int i; + const u64 core_reg = KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE; + + for (i = 0; i < sizeof(struct kvm_regs) / sizeof(__u32); i++) { + if (put_user(core_reg | i, uindices)) + return -EFAULT; + uindices++; + } + + return kvm_arm_copy_sys_reg_indices(vcpu, uindices); +} + +int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) +{ + /* We currently use nothing arch-specific in upper 32 bits */ + if ((reg->id & ~KVM_REG_SIZE_MASK) >> 32 != KVM_REG_ARM64 >> 32) + return -EINVAL; + + /* Register group 16 means we want a core register. */ + if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_CORE) + return get_core_reg(vcpu, reg); + + return kvm_arm_sys_reg_get_reg(vcpu, reg); +} + +int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) +{ + /* We currently use nothing arch-specific in upper 32 bits */ + if ((reg->id & ~KVM_REG_SIZE_MASK) >> 32 != KVM_REG_ARM64 >> 32) + return -EINVAL; + + /* Register group 16 means we set a core register. */ + if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_CORE) + return set_core_reg(vcpu, reg); + + return kvm_arm_sys_reg_set_reg(vcpu, reg); +} + +int kvm_arch_vcpu_ioctl_get_sregs(struct kvm_vcpu *vcpu, + struct kvm_sregs *sregs) +{ + return -EINVAL; +} + +int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu, + struct kvm_sregs *sregs) +{ + return -EINVAL; +} + +int __attribute_const__ kvm_target_cpu(void) +{ + unsigned long implementor = read_cpuid_implementor(); + unsigned long part_number = read_cpuid_part_number(); + + if (implementor != ARM_CPU_IMP_ARM) + return -EINVAL; + + switch (part_number) { + case ARM_CPU_PART_AEM_V8: + return KVM_ARM_TARGET_AEM_V8; + case ARM_CPU_PART_FOUNDATION: + return KVM_ARM_TARGET_FOUNDATION_V8; + case ARM_CPU_PART_CORTEX_A57: + /* Currently handled by the generic backend */ + return KVM_ARM_TARGET_CORTEX_A57; + default: + return -EINVAL; + } +} + +int kvm_vcpu_set_target(struct kvm_vcpu *vcpu, + const struct kvm_vcpu_init *init) +{ + unsigned int i; + int phys_target = kvm_target_cpu(); + + if (init->target != phys_target) + return -EINVAL; + + vcpu->arch.target = phys_target; + bitmap_zero(vcpu->arch.features, KVM_VCPU_MAX_FEATURES); + + /* -ENOENT for unknown features, -EINVAL for invalid combinations. */ + for (i = 0; i < sizeof(init->features) * 8; i++) { + if (init->features[i / 32] & (1 << (i % 32))) { + if (i >= KVM_VCPU_MAX_FEATURES) + return -ENOENT; + set_bit(i, vcpu->arch.features); + } + } + + /* Now we know what it is, we can reset it. */ + return kvm_reset_vcpu(vcpu); +} + +int kvm_arch_vcpu_ioctl_get_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu) +{ + return -EINVAL; +} + +int kvm_arch_vcpu_ioctl_set_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu) +{ + return -EINVAL; +} + +int kvm_arch_vcpu_ioctl_translate(struct kvm_vcpu *vcpu, + struct kvm_translation *tr) +{ + return -EINVAL; +} -- cgit v1.2.3 From 092bd143cbb481b4ce1d55247a2987eaaf61f967 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 17 Dec 2012 17:07:52 +0000 Subject: arm64: KVM: hypervisor initialization code Provide EL2 with page tables and stack, and set the vectors to point to the full blown world-switch code. Signed-off-by: Marc Zyngier --- arch/arm64/include/asm/kvm_host.h | 13 +++++ arch/arm64/kvm/hyp-init.S | 107 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 arch/arm64/kvm/hyp-init.S diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index 4a2622f5e81f..2500eb6a4d2a 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -183,4 +183,17 @@ int handle_exit(struct kvm_vcpu *vcpu, struct kvm_run *run, int kvm_perf_init(void); int kvm_perf_teardown(void); +static inline void __cpu_init_hyp_mode(phys_addr_t boot_pgd_ptr, + phys_addr_t pgd_ptr, + unsigned long hyp_stack_ptr, + unsigned long vector_ptr) +{ + /* + * Call initialization code, and switch to the full blown + * HYP code. + */ + kvm_call_hyp((void *)boot_pgd_ptr, pgd_ptr, + hyp_stack_ptr, vector_ptr); +} + #endif /* __ARM64_KVM_HOST_H__ */ diff --git a/arch/arm64/kvm/hyp-init.S b/arch/arm64/kvm/hyp-init.S new file mode 100644 index 000000000000..ba84e6705e20 --- /dev/null +++ b/arch/arm64/kvm/hyp-init.S @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2012,2013 - ARM Ltd + * Author: Marc Zyngier + * + * 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 . + */ + +#include + +#include +#include +#include + + .text + .pushsection .hyp.idmap.text, "ax" + + .align 11 + +ENTRY(__kvm_hyp_init) + ventry __invalid // Synchronous EL2t + ventry __invalid // IRQ EL2t + ventry __invalid // FIQ EL2t + ventry __invalid // Error EL2t + + ventry __invalid // Synchronous EL2h + ventry __invalid // IRQ EL2h + ventry __invalid // FIQ EL2h + ventry __invalid // Error EL2h + + ventry __do_hyp_init // Synchronous 64-bit EL1 + ventry __invalid // IRQ 64-bit EL1 + ventry __invalid // FIQ 64-bit EL1 + ventry __invalid // Error 64-bit EL1 + + ventry __invalid // Synchronous 32-bit EL1 + ventry __invalid // IRQ 32-bit EL1 + ventry __invalid // FIQ 32-bit EL1 + ventry __invalid // Error 32-bit EL1 + +__invalid: + b . + + /* + * x0: HYP boot pgd + * x1: HYP pgd + * x2: HYP stack + * x3: HYP vectors + */ +__do_hyp_init: + + msr ttbr0_el2, x0 + + mrs x4, tcr_el1 + ldr x5, =TCR_EL2_MASK + and x4, x4, x5 + ldr x5, =TCR_EL2_FLAGS + orr x4, x4, x5 + msr tcr_el2, x4 + + ldr x4, =VTCR_EL2_FLAGS + msr vtcr_el2, x4 + + mrs x4, mair_el1 + msr mair_el2, x4 + isb + + mov x4, #SCTLR_EL2_FLAGS + msr sctlr_el2, x4 + isb + + /* MMU is now enabled. Get ready for the trampoline dance */ + ldr x4, =TRAMPOLINE_VA + adr x5, target + bfi x4, x5, #0, #PAGE_SHIFT + br x4 + +target: /* We're now in the trampoline code, switch page tables */ + msr ttbr0_el2, x1 + isb + + /* Invalidate the old TLBs */ + tlbi alle2 + dsb sy + + /* Set the stack and new vectors */ + kern_hyp_va x2 + mov sp, x2 + kern_hyp_va x3 + msr vbar_el2, x3 + + /* Hello, World! */ + eret +ENDPROC(__kvm_hyp_init) + + .ltorg + + .popsection -- cgit v1.2.3 From c65355bbefaf02d8819a810aacfd566634e3b146 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 6 Jun 2013 16:53:41 -0300 Subject: drm/i915: Track when we dirty the scanout with render commands This is required for tracking render damage for use with FBC and will be used in subsequent patches. Signed-off-by: Chris Wilson Reviewed-by: Rodrigo Vivi Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_execbuffer.c | 2 +- drivers/gpu/drm/i915/intel_display.c | 13 +++++++++---- drivers/gpu/drm/i915/intel_drv.h | 3 ++- drivers/gpu/drm/i915/intel_ringbuffer.h | 1 + 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index a8bb62ca8756..c98333d74111 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -786,7 +786,7 @@ i915_gem_execbuffer_move_to_active(struct list_head *objects, obj->dirty = 1; obj->last_write_seqno = intel_ring_get_seqno(ring); if (obj->pin_count) /* check for potential scanout */ - intel_mark_fb_busy(obj); + intel_mark_fb_busy(obj, ring); } trace_i915_gem_object_change_domain(obj, old_read, old_write); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 3f025ee299dd..7b9eb7ccc09a 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7112,7 +7112,8 @@ void intel_mark_idle(struct drm_device *dev) } } -void intel_mark_fb_busy(struct drm_i915_gem_object *obj) +void intel_mark_fb_busy(struct drm_i915_gem_object *obj, + struct intel_ring_buffer *ring) { struct drm_device *dev = obj->base.dev; struct drm_crtc *crtc; @@ -7124,8 +7125,12 @@ void intel_mark_fb_busy(struct drm_i915_gem_object *obj) if (!crtc->fb) continue; - if (to_intel_framebuffer(crtc->fb)->obj == obj) - intel_increase_pllclock(crtc); + if (to_intel_framebuffer(crtc->fb)->obj != obj) + continue; + + intel_increase_pllclock(crtc); + if (ring && intel_fbc_enabled(dev)) + ring->fbc_dirty = true; } } @@ -7575,7 +7580,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, goto cleanup_pending; intel_disable_fbc(dev); - intel_mark_fb_busy(obj); + intel_mark_fb_busy(obj, NULL); mutex_unlock(&dev->struct_mutex); trace_i915_flip_request(intel_crtc->plane, obj); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index b76a956cfb9a..bb4822a6c41e 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -574,7 +574,8 @@ extern bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, extern void intel_dvo_init(struct drm_device *dev); extern void intel_tv_init(struct drm_device *dev); extern void intel_mark_busy(struct drm_device *dev); -extern void intel_mark_fb_busy(struct drm_i915_gem_object *obj); +extern void intel_mark_fb_busy(struct drm_i915_gem_object *obj, + struct intel_ring_buffer *ring); extern void intel_mark_idle(struct drm_device *dev); extern bool intel_lvds_init(struct drm_device *dev); extern bool intel_is_dual_link_lvds(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index 4c7e103e6fa4..efc403d1e3e0 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -140,6 +140,7 @@ struct intel_ring_buffer { */ u32 outstanding_lazy_request; bool gpu_caches_dirty; + bool fbc_dirty; wait_queue_head_t irq_queue; -- cgit v1.2.3 From fd3da6c95b6d865446fa9b29df6edff4343e385a Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Thu, 6 Jun 2013 16:58:16 -0300 Subject: drm/i915: WA: FBC Render Nuke. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WaFbcNukeOn3DBlt for IVB, HSW. According BSPec: "Workaround: Do not enable Render Command Streamer tracking for FBC. Instead insert a LRI to address 0x50380 with data 0x00000004 after the PIPE_CONTROL that follows each render submission." v2: Chris noticed that flush_domains check was missing here and also suggested to do LRI only when fbc is enabled. To avoid do a I915_READ on every flush lets use the module parameter check. v3: Adding Wa name as Damien suggested. v4: Ville noticed VLV doesn't support fbc at all and comment came wrong from spec. v5: Ville noticed than on blt a Cache Clean LRI should be used instead the Nuke one. v6: Check for flush domain on blt (by Ville). Check for scanout dirty (by Chris). v7: Apply proper fbc_dirty implemented by Chris. v8: remove unused variables. Cc: Ville Syrjälä Cc: Chris Wilson Signed-off-by: Rodrigo Vivi Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 4 ++++ drivers/gpu/drm/i915/intel_pm.c | 2 +- drivers/gpu/drm/i915/intel_ringbuffer.c | 29 +++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index ff9f71af9347..b1fdca9e96bb 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -1020,6 +1020,10 @@ #define IPS_CTL 0x43408 #define IPS_ENABLE (1 << 31) +#define MSG_FBC_REND_STATE 0x50380 +#define FBC_REND_NUKE (1<<2) +#define FBC_REND_CACHE_CLEAN (1<<1) + #define _HSW_PIPE_SLICE_CHICKEN_1_A 0x420B0 #define _HSW_PIPE_SLICE_CHICKEN_1_B 0x420B4 #define HSW_BYPASS_FBC_QUEUE (1<<22) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 8faa43fc9bc9..adc44e42b23c 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -274,7 +274,7 @@ static void gen7_enable_fbc(struct drm_crtc *crtc, unsigned long interval) struct drm_i915_gem_object *obj = intel_fb->obj; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - I915_WRITE(IVB_FBC_RT_BASE, obj->gtt_offset | ILK_FBC_RT_VALID); + I915_WRITE(IVB_FBC_RT_BASE, obj->gtt_offset); I915_WRITE(ILK_DPFC_CONTROL, DPFC_CTL_EN | DPFC_CTL_LIMIT_1X | IVB_DPFC_CTL_FENCE_EN | diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 0e72da6ad0fa..1ef081cd1706 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -280,6 +280,27 @@ gen7_render_ring_cs_stall_wa(struct intel_ring_buffer *ring) return 0; } +static int gen7_ring_fbc_flush(struct intel_ring_buffer *ring, u32 value) +{ + int ret; + + if (!ring->fbc_dirty) + return 0; + + ret = intel_ring_begin(ring, 4); + if (ret) + return ret; + intel_ring_emit(ring, MI_NOOP); + /* WaFbcNukeOn3DBlt:ivb/hsw */ + intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(1)); + intel_ring_emit(ring, MSG_FBC_REND_STATE); + intel_ring_emit(ring, value); + intel_ring_advance(ring); + + ring->fbc_dirty = false; + return 0; +} + static int gen7_render_ring_flush(struct intel_ring_buffer *ring, u32 invalidate_domains, u32 flush_domains) @@ -336,6 +357,9 @@ gen7_render_ring_flush(struct intel_ring_buffer *ring, intel_ring_emit(ring, 0); intel_ring_advance(ring); + if (flush_domains) + return gen7_ring_fbc_flush(ring, FBC_REND_NUKE); + return 0; } @@ -1685,6 +1709,7 @@ gen6_ring_dispatch_execbuffer(struct intel_ring_buffer *ring, static int gen6_ring_flush(struct intel_ring_buffer *ring, u32 invalidate, u32 flush) { + struct drm_device *dev = ring->dev; uint32_t cmd; int ret; @@ -1707,6 +1732,10 @@ static int gen6_ring_flush(struct intel_ring_buffer *ring, intel_ring_emit(ring, 0); intel_ring_emit(ring, MI_NOOP); intel_ring_advance(ring); + + if (IS_GEN7(dev) && flush) + return gen7_ring_fbc_flush(ring, FBC_REND_CACHE_CLEAN); + return 0; } -- cgit v1.2.3 From 22e407d749a418b4bb4cc93ef76e0429a9f83c82 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Fri, 7 Jun 2013 18:52:24 +0300 Subject: drm/i915: Make g4x_fixup_plane() operational again MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Don't enable the cursor until g4x_fixup_plane() had a chance to do cast its magic spell. Egbert writes: "Today I had the chance to test this. First I tried if I can still reproduce the blank with this patch added when I disable my voodoo g4x_fixup_plane(): It turned out it still happens however very rarely (like 1 out of 20 tries). When I reenabled my voodoo the issue still occurred. I had to switch two lines around, ie: intel_enable_plane(dev_priv, plane, pipe); if (IS_G4X(dev)) g4x_fixup_plane(dev_priv, pipe); + intel_crtc_update_cursor(crtc, true); to avoid the blank screen issue - which is it didn't happen in ~75 tries." v2: Add a comment to remind people of the ordering constraints Acked-by: Egbert Eich Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 7b9eb7ccc09a..6b37b7555f24 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3700,9 +3700,10 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc) intel_enable_pipe(dev_priv, pipe, false); intel_enable_plane(dev_priv, plane, pipe); intel_enable_planes(crtc); - intel_crtc_update_cursor(crtc, true); + /* The fixup needs to happen before cursor is enabled */ if (IS_G4X(dev)) g4x_fixup_plane(dev_priv, pipe); + intel_crtc_update_cursor(crtc, true); /* Give the overlay scaler a chance to enable if it's on this pipe */ intel_crtc_dpms_overlay(intel_crtc, true); -- cgit v1.2.3 From 01fafcab20fbbd2930691c7fdcf177eaa190d499 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 28 Feb 2012 11:50:32 +0000 Subject: ARM: nommu: add entry point for secondary CPUs to head-nommu.S MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds a secondary_startup entry point to head-nommu.S so that we can boot secondary CPUs on an SMP nommu configuration. Signed-off-by: Will Deacon CC: Uwe Kleine-König CC: Nicolas Pitre --- arch/arm/kernel/head-nommu.S | 50 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/arch/arm/kernel/head-nommu.S b/arch/arm/kernel/head-nommu.S index 8812ce88f7a1..06ba9c8e62be 100644 --- a/arch/arm/kernel/head-nommu.S +++ b/arch/arm/kernel/head-nommu.S @@ -63,12 +63,56 @@ ENTRY(stext) movs r10, r5 @ invalid processor (r5=0)? beq __error_p @ yes, error 'p' - adr lr, BSYM(__after_proc_init) @ return (PIC) address + ldr r13, =__mmap_switched @ address to jump to after + @ initialising sctlr + adr lr, BSYM(1f) @ return (PIC) address ARM( add pc, r10, #PROCINFO_INITFUNC ) THUMB( add r12, r10, #PROCINFO_INITFUNC ) THUMB( mov pc, r12 ) + 1: b __after_proc_init ENDPROC(stext) +#ifdef CONFIG_SMP + __CPUINIT +ENTRY(secondary_startup) + /* + * Common entry point for secondary CPUs. + * + * Ensure that we're in SVC mode, and IRQs are disabled. Lookup + * the processor type - there is no need to check the machine type + * as it has already been validated by the primary processor. + */ + setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 +#ifndef CONFIG_CPU_CP15 + ldr r9, =CONFIG_PROCESSOR_ID +#else + mrc p15, 0, r9, c0, c0 @ get processor id +#endif + bl __lookup_processor_type @ r5=procinfo r9=cpuid + movs r10, r5 @ invalid processor? + beq __error_p @ yes, error 'p' + + adr r4, __secondary_data + ldmia r4, {r7, r12} + adr lr, BSYM(__after_proc_init) @ return address + mov r13, r12 @ __secondary_switched address + ARM( add pc, r10, #PROCINFO_INITFUNC ) + THUMB( add r12, r10, #PROCINFO_INITFUNC ) + THUMB( mov pc, r12 ) +ENDPROC(secondary_startup) + +ENTRY(__secondary_switched) + ldr sp, [r7, #8] @ set up the stack pointer + mov fp, #0 + b secondary_start_kernel +ENDPROC(__secondary_switched) + + .type __secondary_data, %object +__secondary_data: + .long secondary_data + .long __secondary_switched +#endif /* CONFIG_SMP */ + /* * Set the Control Register and Read the process ID. */ @@ -99,9 +143,7 @@ __after_proc_init: #endif mcr p15, 0, r0, c1, c0, 0 @ write control reg #endif /* CONFIG_CPU_CP15 */ - - b __mmap_switched @ clear the BSS and jump - @ to start_kernel + mov pc, r13 ENDPROC(__after_proc_init) .ltorg -- cgit v1.2.3 From 5c709e699881afd1cf9244c719eb063c3476a405 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 28 Feb 2012 12:56:06 +0000 Subject: ARM: nommu: define dummy TLB operations for nommu configurations nommu platforms do not perform address translation and therefore clearly don't have TLBs. However, some SMP code assumes the presence of the TLB flushing routines and will therefore fail to compile for a nommu system. This patch defines dummy local_* TLB operations and #defines tlb_ops_need_broadcast() as 0, therefore causing the usual ARM SMP TLB operations to call the local variants instead. Signed-off-by: Will Deacon CC: Lorenzo Pieralisi CC: Nicolas Pitre --- arch/arm/include/asm/smp_plat.h | 4 ++++ arch/arm/include/asm/tlbflush.h | 23 ++++++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/arch/arm/include/asm/smp_plat.h b/arch/arm/include/asm/smp_plat.h index aaa61b6f50ff..1c7b6f8101ae 100644 --- a/arch/arm/include/asm/smp_plat.h +++ b/arch/arm/include/asm/smp_plat.h @@ -26,6 +26,9 @@ static inline bool is_smp(void) } /* all SMP configurations have the extended CPUID registers */ +#ifndef CONFIG_MMU +#define tlb_ops_need_broadcast() 0 +#else static inline int tlb_ops_need_broadcast(void) { if (!is_smp()) @@ -33,6 +36,7 @@ static inline int tlb_ops_need_broadcast(void) return ((read_cpuid_ext(CPUID_EXT_MMFR3) >> 12) & 0xf) < 2; } +#endif #if !defined(CONFIG_SMP) || __LINUX_ARM_ARCH__ >= 7 #define cache_ops_need_broadcast() 0 diff --git a/arch/arm/include/asm/tlbflush.h b/arch/arm/include/asm/tlbflush.h index a3625d141c1d..cc8517e0f132 100644 --- a/arch/arm/include/asm/tlbflush.h +++ b/arch/arm/include/asm/tlbflush.h @@ -537,6 +537,27 @@ static inline void update_mmu_cache(struct vm_area_struct *vma, #endif -#endif /* CONFIG_MMU */ +#elif defined(CONFIG_SMP) /* !CONFIG_MMU */ + +#ifndef __ASSEMBLY__ + +#include + +static inline void local_flush_tlb_all(void) { } +static inline void local_flush_tlb_mm(struct mm_struct *mm) { } +static inline void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr) { } +static inline void local_flush_tlb_kernel_page(unsigned long kaddr) { } +static inline void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end) { } +static inline void local_flush_tlb_kernel_range(unsigned long start, unsigned long end) { } + +extern void flush_tlb_all(void); +extern void flush_tlb_mm(struct mm_struct *mm); +extern void flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr); +extern void flush_tlb_kernel_page(unsigned long kaddr); +extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end); +extern void flush_tlb_kernel_range(unsigned long start, unsigned long end); +#endif /* __ASSEMBLY__ */ + +#endif #endif -- cgit v1.2.3 From 02ed1c7bba57b66c9a2f3146c935af12a93f2d76 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 28 Feb 2012 14:26:42 +0000 Subject: ARM: nommu: provide dummy cpu_switch_mm implementation cpu_switch_mm is a logical nop on nommu systems, so define it as such when !CONFIG_MMU. Signed-off-by: Will Deacon --- arch/arm/include/asm/proc-fns.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/include/asm/proc-fns.h b/arch/arm/include/asm/proc-fns.h index f3628fb3d2b3..a6c99fe62b82 100644 --- a/arch/arm/include/asm/proc-fns.h +++ b/arch/arm/include/asm/proc-fns.h @@ -137,6 +137,10 @@ extern void cpu_resume(void); }) #endif +#else /*!CONFIG_MMU */ + +#define cpu_switch_mm(pgd,mm) { } + #endif #endif /* __ASSEMBLY__ */ -- cgit v1.2.3 From c4a1f032ed35d744e3d74b8aebe8d85f29aecd88 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 28 Feb 2012 13:02:59 +0000 Subject: ARM: nommu: do not initialise page tables in secondary_data structure nommu systems do not require any page tables, so don't try to initialise them when bringing up secondary cores. Signed-off-by: Will Deacon --- arch/arm/kernel/smp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index 550d63cef68e..44d1c00dc45f 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -87,8 +87,10 @@ int __cpuinit __cpu_up(unsigned int cpu, struct task_struct *idle) * its stack and the page tables. */ secondary_data.stack = task_stack_page(idle) + THREAD_START_SP; +#ifdef CONFIG_MMU secondary_data.pgdir = virt_to_phys(idmap_pgd); secondary_data.swapper_pg_dir = virt_to_phys(swapper_pg_dir); +#endif __cpuc_flush_dcache_area(&secondary_data, sizeof(secondary_data)); outer_clean_range(__pa(&secondary_data), __pa(&secondary_data + 1)); -- cgit v1.2.3 From aa1aadc3305c4917c39f0291613a5ec81dd4c73b Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Thu, 23 Feb 2012 13:51:38 +0000 Subject: ARM: suspend: fix CPU suspend code for !CONFIG_MMU configurations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ARM CPU suspend code can be selected even for a !CONFIG_MMU configuration. The resulting kernel will not compile and, even if it did, would access undefined co-processor registers when executing. This patch fixes the v6 and v7 CPU suspend code for the nommu case. Signed-off-by: Will Deacon Tested-by: Jonathan Austin CC: Lorenzo Pieralisi (commit_signer:1/3=33%) CC: Santosh Shilimkar (commit_signer:1/3=33%) CC: Uwe Kleine-König --- arch/arm/kernel/suspend.c | 64 ++++++++++++++++++++++++++--------------------- arch/arm/mm/proc-v6.S | 6 ++++- arch/arm/mm/proc-v7.S | 14 +++++++---- 3 files changed, 50 insertions(+), 34 deletions(-) diff --git a/arch/arm/kernel/suspend.c b/arch/arm/kernel/suspend.c index c59c97ea8268..38a50676213b 100644 --- a/arch/arm/kernel/suspend.c +++ b/arch/arm/kernel/suspend.c @@ -10,6 +10,42 @@ extern int __cpu_suspend(unsigned long, int (*)(unsigned long)); extern void cpu_resume_mmu(void); +#ifdef CONFIG_MMU +/* + * Hide the first two arguments to __cpu_suspend - these are an implementation + * detail which platform code shouldn't have to know about. + */ +int cpu_suspend(unsigned long arg, int (*fn)(unsigned long)) +{ + struct mm_struct *mm = current->active_mm; + int ret; + + if (!idmap_pgd) + return -EINVAL; + + /* + * Provide a temporary page table with an identity mapping for + * the MMU-enable code, required for resuming. On successful + * resume (indicated by a zero return code), we need to switch + * back to the correct page tables. + */ + ret = __cpu_suspend(arg, fn); + if (ret == 0) { + cpu_switch_mm(mm->pgd, mm); + local_flush_bp_all(); + local_flush_tlb_all(); + } + + return ret; +} +#else +int cpu_suspend(unsigned long arg, int (*fn)(unsigned long)) +{ + return __cpu_suspend(arg, fn); +} +#define idmap_pgd NULL +#endif + /* * This is called by __cpu_suspend() to save the state, and do whatever * flushing is required to ensure that when the CPU goes to sleep we have @@ -46,31 +82,3 @@ void __cpu_suspend_save(u32 *ptr, u32 ptrsz, u32 sp, u32 *save_ptr) outer_clean_range(virt_to_phys(save_ptr), virt_to_phys(save_ptr) + sizeof(*save_ptr)); } - -/* - * Hide the first two arguments to __cpu_suspend - these are an implementation - * detail which platform code shouldn't have to know about. - */ -int cpu_suspend(unsigned long arg, int (*fn)(unsigned long)) -{ - struct mm_struct *mm = current->active_mm; - int ret; - - if (!idmap_pgd) - return -EINVAL; - - /* - * Provide a temporary page table with an identity mapping for - * the MMU-enable code, required for resuming. On successful - * resume (indicated by a zero return code), we need to switch - * back to the correct page tables. - */ - ret = __cpu_suspend(arg, fn); - if (ret == 0) { - cpu_switch_mm(mm->pgd, mm); - local_flush_bp_all(); - local_flush_tlb_all(); - } - - return ret; -} diff --git a/arch/arm/mm/proc-v6.S b/arch/arm/mm/proc-v6.S index 919405e20b80..2d1ef87328a1 100644 --- a/arch/arm/mm/proc-v6.S +++ b/arch/arm/mm/proc-v6.S @@ -140,8 +140,10 @@ ENTRY(cpu_v6_set_pte_ext) ENTRY(cpu_v6_do_suspend) stmfd sp!, {r4 - r9, lr} mrc p15, 0, r4, c13, c0, 0 @ FCSE/PID +#ifdef CONFIG_MMU mrc p15, 0, r5, c3, c0, 0 @ Domain ID mrc p15, 0, r6, c2, c0, 1 @ Translation table base 1 +#endif mrc p15, 0, r7, c1, c0, 1 @ auxiliary control register mrc p15, 0, r8, c1, c0, 2 @ co-processor access control mrc p15, 0, r9, c1, c0, 0 @ control register @@ -158,14 +160,16 @@ ENTRY(cpu_v6_do_resume) mcr p15, 0, ip, c13, c0, 1 @ set reserved context ID ldmia r0, {r4 - r9} mcr p15, 0, r4, c13, c0, 0 @ FCSE/PID +#ifdef CONFIG_MMU mcr p15, 0, r5, c3, c0, 0 @ Domain ID ALT_SMP(orr r1, r1, #TTB_FLAGS_SMP) ALT_UP(orr r1, r1, #TTB_FLAGS_UP) mcr p15, 0, r1, c2, c0, 0 @ Translation table base 0 mcr p15, 0, r6, c2, c0, 1 @ Translation table base 1 + mcr p15, 0, ip, c2, c0, 2 @ TTB control register +#endif mcr p15, 0, r7, c1, c0, 1 @ auxiliary control register mcr p15, 0, r8, c1, c0, 2 @ co-processor access control - mcr p15, 0, ip, c2, c0, 2 @ TTB control register mcr p15, 0, ip, c7, c5, 4 @ ISB mov r0, r9 @ control register b cpu_resume_mmu diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S index 2c73a7301ff7..a851e3433afe 100644 --- a/arch/arm/mm/proc-v7.S +++ b/arch/arm/mm/proc-v7.S @@ -98,9 +98,11 @@ ENTRY(cpu_v7_do_suspend) mrc p15, 0, r4, c13, c0, 0 @ FCSE/PID mrc p15, 0, r5, c13, c0, 3 @ User r/o thread ID stmia r0!, {r4 - r5} +#ifdef CONFIG_MMU mrc p15, 0, r6, c3, c0, 0 @ Domain ID mrc p15, 0, r7, c2, c0, 1 @ TTB 1 mrc p15, 0, r11, c2, c0, 2 @ TTB control register +#endif mrc p15, 0, r8, c1, c0, 0 @ Control register mrc p15, 0, r9, c1, c0, 1 @ Auxiliary control register mrc p15, 0, r10, c1, c0, 2 @ Co-processor access control @@ -110,13 +112,14 @@ ENDPROC(cpu_v7_do_suspend) ENTRY(cpu_v7_do_resume) mov ip, #0 - mcr p15, 0, ip, c8, c7, 0 @ invalidate TLBs mcr p15, 0, ip, c7, c5, 0 @ invalidate I cache mcr p15, 0, ip, c13, c0, 1 @ set reserved context ID ldmia r0!, {r4 - r5} mcr p15, 0, r4, c13, c0, 0 @ FCSE/PID mcr p15, 0, r5, c13, c0, 3 @ User r/o thread ID ldmia r0, {r6 - r11} +#ifdef CONFIG_MMU + mcr p15, 0, ip, c8, c7, 0 @ invalidate TLBs mcr p15, 0, r6, c3, c0, 0 @ Domain ID #ifndef CONFIG_ARM_LPAE ALT_SMP(orr r1, r1, #TTB_FLAGS_SMP) @@ -125,14 +128,15 @@ ENTRY(cpu_v7_do_resume) mcr p15, 0, r1, c2, c0, 0 @ TTB 0 mcr p15, 0, r7, c2, c0, 1 @ TTB 1 mcr p15, 0, r11, c2, c0, 2 @ TTB control register - mrc p15, 0, r4, c1, c0, 1 @ Read Auxiliary control register - teq r4, r9 @ Is it already set? - mcrne p15, 0, r9, c1, c0, 1 @ No, so write it - mcr p15, 0, r10, c1, c0, 2 @ Co-processor access control ldr r4, =PRRR @ PRRR ldr r5, =NMRR @ NMRR mcr p15, 0, r4, c10, c2, 0 @ write PRRR mcr p15, 0, r5, c10, c2, 1 @ write NMRR +#endif /* CONFIG_MMU */ + mrc p15, 0, r4, c1, c0, 1 @ Read Auxiliary control register + teq r4, r9 @ Is it already set? + mcrne p15, 0, r9, c1, c0, 1 @ No, so write it + mcr p15, 0, r10, c1, c0, 2 @ Co-processor access control isb dsb mov r0, r8 @ control register -- cgit v1.2.3 From 8006b4d1a7c70f27a87cb753b5ed90483f0cfe26 Mon Sep 17 00:00:00 2001 From: Jonathan Austin Date: Wed, 24 Apr 2013 11:51:38 +0100 Subject: ARM: nommu: Don't build smp_tlb.c for !CONFIG_MMU Without an MMU we don't need to do any TLB maintenance. Until the addition of 93dc68876b60 (ARM: 7684/1: errata: Workaround for Cortex-A15 erratum 798181 (TLBI/DSB operations)) building the tlb maintenance ops in smp_tlb.c worked, though none of the contents were used. Since that commit, however, SMP NOMMU has not been able to build. This patch restores that ability by making the building of smp_tlb.c dependent on MMU. Signed-off-by: Jonathan Austin Acked-by: Catalin Marinas CC: Will Deacon --- arch/arm/kernel/Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index f4285b5ffb05..fccfbdb03df1 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -38,7 +38,10 @@ obj-$(CONFIG_ARTHUR) += arthur.o obj-$(CONFIG_ISA_DMA) += dma-isa.o obj-$(CONFIG_PCI) += bios32.o isa.o obj-$(CONFIG_ARM_CPU_SUSPEND) += sleep.o suspend.o -obj-$(CONFIG_SMP) += smp.o smp_tlb.o +obj-$(CONFIG_SMP) += smp.o +ifdef CONFIG_MMU +obj-$(CONFIG_SMP) += smp_tlb.o +endif obj-$(CONFIG_HAVE_ARM_SCU) += smp_scu.o obj-$(CONFIG_HAVE_ARM_TWD) += smp_twd.o obj-$(CONFIG_ARM_ARCH_TIMER) += arch_timer.o -- cgit v1.2.3 From 8d655d835bc5c0cb7d485d147ba96249e356a697 Mon Sep 17 00:00:00 2001 From: Jonathan Austin Date: Wed, 24 Apr 2013 11:56:09 +0100 Subject: ARM: nommu: add stub local_flush_bp_all() for !CONFIG_MMUU Since the merging of Will's tlb-ops branch, specifically 89c7e4b8bbb3 (ARM: 7661/1: mm: perform explicit branch predictor maintenance when required), building SMP without CONFIG_MMU has been broken. The local_flush_bp_all function is only called for operations related to changing the kernel's view of memory and ASID rollover - both of which are irrelevant to an !MMU kernel. This patch adds a stub local_flush_bp_all() function to the other tlb maintenance stubs and restores the ability to build an SMP !MMU kernel. Signed-off-by: Jonathan Austin Acked-by: Will Deacon --- arch/arm/include/asm/tlbflush.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/include/asm/tlbflush.h b/arch/arm/include/asm/tlbflush.h index cc8517e0f132..ded7c16f80cc 100644 --- a/arch/arm/include/asm/tlbflush.h +++ b/arch/arm/include/asm/tlbflush.h @@ -549,6 +549,7 @@ static inline void local_flush_tlb_page(struct vm_area_struct *vma, unsigned lon static inline void local_flush_tlb_kernel_page(unsigned long kaddr) { } static inline void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end) { } static inline void local_flush_tlb_kernel_range(unsigned long start, unsigned long end) { } +static inline void local_flush_bp_all(void) { } extern void flush_tlb_all(void); extern void flush_tlb_mm(struct mm_struct *mm); @@ -556,6 +557,7 @@ extern void flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr); extern void flush_tlb_kernel_page(unsigned long kaddr); extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end); extern void flush_tlb_kernel_range(unsigned long start, unsigned long end); +extern void flush_bp_all(void); #endif /* __ASSEMBLY__ */ #endif -- cgit v1.2.3 From 66567618f37269ec55febee4dcec2a1dec1033a0 Mon Sep 17 00:00:00 2001 From: Jonathan Austin Date: Thu, 12 Jul 2012 14:38:46 +0100 Subject: ARM: select CPU_CPU15_MMU/MPU appropriately Currently CPU_V7 selects CPU_CP15_MMU, however in the case of a V7 CPU implementing the PMSA, such as the Cortex-R7, the CP15_MMU operations are not available. Selecting CPU_CP15_MPU is appropriate in this case. This patch makes CPU_CP15_MMU dependent on the use of the MMU, selecting CPU_CP15_MPU for v7 processors when !MMU is chosen. Signed-off-by: Jonathan Austin --- arch/arm/mm/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index 9e8101ecd63e..6cacdc8dd654 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -392,7 +392,8 @@ config CPU_V7 select CPU_CACHE_V7 select CPU_CACHE_VIPT select CPU_COPY_V6 if MMU - select CPU_CP15_MMU + select CPU_CP15_MMU if MMU + select CPU_CP15_MPU if !MMU select CPU_HAS_ASID if MMU select CPU_PABRT_V7 select CPU_TLB_V7 if MMU -- cgit v1.2.3 From c90ad5c940583525f46938149b91187e75acc546 Mon Sep 17 00:00:00 2001 From: Jonathan Austin Date: Thu, 15 Mar 2012 14:27:07 +0000 Subject: ARM: add Cortex-R7 Processor Info This patch adds processor info for ARM Ltd. Cortex-R7. The R7 has many similarities to the A9 and though the ACTLR layout is not identical, the bits associated with cache operations broadcasting and SMP modes are the same for A9, A5 and R7 (Though in the A-class processors the same bits toggle TLB-ops broadcasting as well as cache-ops) Signed-off-by: Jonathan Austin Reviewed-by: Will Deacon CC: Catalin Marinas CC: Stephen Boyd --- arch/arm/mm/proc-v7.S | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S index a851e3433afe..f85ae8cad17f 100644 --- a/arch/arm/mm/proc-v7.S +++ b/arch/arm/mm/proc-v7.S @@ -159,7 +159,8 @@ ENDPROC(cpu_v7_do_resume) */ __v7_ca5mp_setup: __v7_ca9mp_setup: - mov r10, #(1 << 0) @ TLB ops broadcasting +__v7_cr7mp_setup: + mov r10, #(1 << 0) @ Cache/TLB ops broadcasting b 1f __v7_ca7mp_setup: __v7_ca15mp_setup: @@ -418,6 +419,16 @@ __v7_pj4b_proc_info: __v7_proc __v7_pj4b_setup .size __v7_pj4b_proc_info, . - __v7_pj4b_proc_info + /* + * ARM Ltd. Cortex R7 processor. + */ + .type __v7_cr7mp_proc_info, #object +__v7_cr7mp_proc_info: + .long 0x410fc170 + .long 0xff0ffff0 + __v7_proc __v7_cr7mp_setup + .size __v7_cr7mp_proc_info, . - __v7_cr7mp_proc_info + /* * ARM Ltd. Cortex A7 processor. */ -- cgit v1.2.3 From ed18bdc875328921114139e17ade1d2569e82c85 Mon Sep 17 00:00:00 2001 From: Jonathan Austin Date: Thu, 30 Aug 2012 13:46:44 +0100 Subject: ARM: vexpress: Add Cortex-R Series UART, selectable via DEBUG_LL The Cortex-R series processors on Versatile Express have a different memory map to the RS1 and CA9X4 tiles. Most of the platform difference can be expressed in device-trees, but the UART definitions for LL_DEBUG cannot. This patch defines the UART location for R-Series processors on versatile-express, allowing low-level debug and output from the decompressor. These definitions are selectable via Kconfig Signed-off-by: Jonathan Austin CC: Pawel Moll --- arch/arm/Kconfig.debug | 10 +++++++++- arch/arm/include/debug/vexpress.S | 10 ++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug index 1d41908d5cda..f2623b25ff9a 100644 --- a/arch/arm/Kconfig.debug +++ b/arch/arm/Kconfig.debug @@ -476,6 +476,13 @@ choice of the tiles using the RS1 memory map, including all new A-class core tiles, FPGA-based SMMs and software models. + config DEBUG_VEXPRESS_UART0_CRX + bool "Use PL011 UART0 at 0xb0090000 (Cortex-R compliant tiles)" + depends on ARCH_VEXPRESS && !MMU + help + This option selects UART0 at 0xb0090000. This is appropriate for + Cortex-R series tiles and SMMs, such as Cortex-R5 and Cortex-R7 + config DEBUG_VT8500_UART0 bool "Use UART0 on VIA/Wondermedia SoCs" depends on ARCH_VT8500 @@ -645,7 +652,8 @@ config DEBUG_LL_INCLUDE default "debug/tegra.S" if DEBUG_TEGRA_UART default "debug/ux500.S" if DEBUG_UX500_UART default "debug/vexpress.S" if DEBUG_VEXPRESS_UART0_DETECT || \ - DEBUG_VEXPRESS_UART0_CA9 || DEBUG_VEXPRESS_UART0_RS1 + DEBUG_VEXPRESS_UART0_CA9 || DEBUG_VEXPRESS_UART0_RS1 || \ + DEBUG_VEXPRESS_UART0_CRX default "debug/vt8500.S" if DEBUG_VT8500_UART0 default "debug/zynq.S" if DEBUG_ZYNQ_UART0 || DEBUG_ZYNQ_UART1 default "mach/debug-macro.S" diff --git a/arch/arm/include/debug/vexpress.S b/arch/arm/include/debug/vexpress.S index dc8e882a6257..acafb229e2b6 100644 --- a/arch/arm/include/debug/vexpress.S +++ b/arch/arm/include/debug/vexpress.S @@ -16,6 +16,8 @@ #define DEBUG_LL_PHYS_BASE_RS1 0x1c000000 #define DEBUG_LL_UART_OFFSET_RS1 0x00090000 +#define DEBUG_LL_UART_PHYS_CRX 0xb0090000 + #define DEBUG_LL_VIRT_BASE 0xf8000000 #if defined(CONFIG_DEBUG_VEXPRESS_UART0_DETECT) @@ -67,6 +69,14 @@ #include +#elif defined(CONFIG_DEBUG_VEXPRESS_UART0_CRX) + + .macro addruart,rp,tmp,tmp2 + ldr \rp, =DEBUG_LL_UART_PHYS_CRX + .endm + +#include + #else /* CONFIG_DEBUG_LL_UART_NONE */ .macro addruart, rp, rv, tmp -- cgit v1.2.3 From aca7e5920c8e80a2a49c1e37664675d78b23398b Mon Sep 17 00:00:00 2001 From: Jonathan Austin Date: Thu, 21 Feb 2013 15:21:34 +0000 Subject: ARM: mpu: add PMSA related registers and bitfields to existing headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds the following definitions relevant to the PMSA: Add SCTLR bit 17, (CR_BR - Background Region bit) to the list of CR_* bitfields. This bit determines whether to use the architecturally defined memory map Add the MPUIR to the available registers when using read_cpuid macro. The MPUIR is the MPU type register. Signed-off-by: Jonathan Austin Reviewed-by: Will Deacon CC:"Uwe Kleine-König" --- arch/arm/include/asm/cp15.h | 5 +++++ arch/arm/include/asm/cputype.h | 1 + 2 files changed, 6 insertions(+) diff --git a/arch/arm/include/asm/cp15.h b/arch/arm/include/asm/cp15.h index 1f3262e99d81..a524a23d8627 100644 --- a/arch/arm/include/asm/cp15.h +++ b/arch/arm/include/asm/cp15.h @@ -23,6 +23,11 @@ #define CR_RR (1 << 14) /* Round Robin cache replacement */ #define CR_L4 (1 << 15) /* LDR pc can set T bit */ #define CR_DT (1 << 16) +#ifdef CONFIG_MMU +#define CR_HA (1 << 17) /* Hardware management of Access Flag */ +#else +#define CR_BR (1 << 17) /* MPU Background region enable (PMSA) */ +#endif #define CR_IT (1 << 18) #define CR_ST (1 << 19) #define CR_FI (1 << 21) /* Fast interrupt (lower latency mode) */ diff --git a/arch/arm/include/asm/cputype.h b/arch/arm/include/asm/cputype.h index ec635ff32f49..3b704dfb6c58 100644 --- a/arch/arm/include/asm/cputype.h +++ b/arch/arm/include/asm/cputype.h @@ -8,6 +8,7 @@ #define CPUID_CACHETYPE 1 #define CPUID_TCM 2 #define CPUID_TLBTYPE 3 +#define CPUID_MPUIR 4 #define CPUID_MPIDR 5 #ifdef CONFIG_CPU_V7M -- cgit v1.2.3 From a2b45b0da8bbb98cb2f062cf16c6fdebab4243a1 Mon Sep 17 00:00:00 2001 From: Jonathan Austin Date: Thu, 21 Feb 2013 17:49:24 +0000 Subject: ARM: mpu: add header for MPU register layouts and region data This commit adds definitions relevant to the ARM v7 PMSA compliant MPU. The register layouts and region configuration data is made accessible to asm as well as C-code so that it can be used in early bring-up of the MPU. The mpu region information structs assume that the properties for the I/D side are the same, though the implementation could be trivially extended for future platforms where this is no-longer true. The MPU_*_REGION defines are used for the basic, static MPU region setup. Signed-off-by: Jonathan Austin Reviewed-by: Will Deacon --- arch/arm/include/asm/mpu.h | 72 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 arch/arm/include/asm/mpu.h diff --git a/arch/arm/include/asm/mpu.h b/arch/arm/include/asm/mpu.h new file mode 100644 index 000000000000..bd48c0cb544e --- /dev/null +++ b/arch/arm/include/asm/mpu.h @@ -0,0 +1,72 @@ +#ifndef __ARM_MPU_H +#define __ARM_MPU_H + +#ifdef CONFIG_ARM_MPU + +/* MPUIR layout */ +#define MPUIR_nU 1 +#define MPUIR_DREGION 8 +#define MPUIR_IREGION 16 +#define MPUIR_DREGION_SZMASK (0xFF << MPUIR_DREGION) +#define MPUIR_IREGION_SZMASK (0xFF << MPUIR_IREGION) + +/* ID_MMFR0 data relevant to MPU */ +#define MMFR0_PMSA (0xF << 4) +#define MMFR0_PMSAv7 (3 << 4) + +/* MPU D/I Size Register fields */ +#define MPU_RSR_SZ 1 +#define MPU_RSR_EN 0 + +/* The D/I RSR value for an enabled region spanning the whole of memory */ +#define MPU_RSR_ALL_MEM 63 + +/* Individual bits in the DR/IR ACR */ +#define MPU_ACR_XN (1 << 12) +#define MPU_ACR_SHARED (1 << 2) + +/* C, B and TEX[2:0] bits only have semantic meanings when grouped */ +#define MPU_RGN_CACHEABLE 0xB +#define MPU_RGN_SHARED_CACHEABLE (MPU_RGN_CACHEABLE | MPU_ACR_SHARED) +#define MPU_RGN_STRONGLY_ORDERED 0 + +/* Main region should only be shared for SMP */ +#ifdef CONFIG_SMP +#define MPU_RGN_NORMAL (MPU_RGN_CACHEABLE | MPU_ACR_SHARED) +#else +#define MPU_RGN_NORMAL MPU_RGN_CACHEABLE +#endif + +/* Access permission bits of ACR (only define those that we use)*/ +#define MPU_AP_PL1RW_PL0RW (0x3 << 8) +#define MPU_AP_PL1RW_PL0R0 (0x2 << 8) +#define MPU_AP_PL1RW_PL0NA (0x1 << 8) + +/* For minimal static MPU region configurations */ +#define MPU_PROBE_REGION 0 +#define MPU_BG_REGION 1 +#define MPU_RAM_REGION 2 + +/* Maximum number of regions Linux is interested in */ +#define MPU_MAX_REGIONS 16 + +#ifndef __ASSEMBLY__ + +struct mpu_rgn { + /* Assume same attributes for d/i-side */ + u32 drbar; + u32 drsr; + u32 dracr; +}; + +struct mpu_rgn_info { + u32 mpuir; + struct mpu_rgn rgns[MPU_MAX_REGIONS]; +}; +extern struct mpu_rgn_info mpu_rgn_info; + +#endif /* __ASSEMBLY__ */ + +#endif /* CONFIG_ARM_MPU */ + +#endif -- cgit v1.2.3 From 67c9845beab16a0c97b9c07f72a4b36b7175bb86 Mon Sep 17 00:00:00 2001 From: Jonathan Austin Date: Fri, 22 Feb 2013 17:48:56 +0000 Subject: ARM: mpu: add early bring-up code for the ARMv7 PMSA-compliant MPU This patch adds initial support for using the MPU, which is necessary for SMP operation on PMSAv7 processors because it is the only way to ensure memory is shared. This is an initial patch and full SMP support is added later in this series. The setup of the MPU is performed in a way analagous to that for the MMU: Very early initialisation before the C environment is brought up, followed by a sanity check and more complete initialisation in C. This patch provides the simplest possible memory region configuration: MPU_PROBE_REGION: Reserved for probing MPU details, not enabled MPU_BG_REGION: A 'background' region that specifies all memory strongly ordered MPU_RAM_REGION: A single shared, cacheable, normal region for the valid RAM. In this early initialisation code we simply map the whole of the address space with the BG_REGION and (at least) the kernel with the RAM_REGION. The MPU has region alignment constraints that require us to round past the end of the kernel. As region 2 has a higher priority than region 1, it overrides the strongly- ordered behaviour for RAM only. Subsequent patches will add more complete initialisation from the C-world and support for bringing up secondary CPUs. Signed-off-by: Jonathan Austin Reviewed-by: Will Deacon CC: Hyok S. Choi --- arch/arm/include/asm/mpu.h | 3 ++ arch/arm/kernel/head-nommu.S | 87 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) diff --git a/arch/arm/include/asm/mpu.h b/arch/arm/include/asm/mpu.h index bd48c0cb544e..001483465aa4 100644 --- a/arch/arm/include/asm/mpu.h +++ b/arch/arm/include/asm/mpu.h @@ -50,6 +50,9 @@ /* Maximum number of regions Linux is interested in */ #define MPU_MAX_REGIONS 16 +#define MPU_DATA_SIDE 0 +#define MPU_INSTR_SIDE 1 + #ifndef __ASSEMBLY__ struct mpu_rgn { diff --git a/arch/arm/kernel/head-nommu.S b/arch/arm/kernel/head-nommu.S index 06ba9c8e62be..659912c49571 100644 --- a/arch/arm/kernel/head-nommu.S +++ b/arch/arm/kernel/head-nommu.S @@ -17,9 +17,11 @@ #include #include #include +#include #include #include #include +#include /* * Kernel startup entry point. @@ -63,6 +65,17 @@ ENTRY(stext) movs r10, r5 @ invalid processor (r5=0)? beq __error_p @ yes, error 'p' +#ifdef CONFIG_ARM_MPU + /* Calculate the size of a region covering just the kernel */ + ldr r5, =PHYS_OFFSET @ Region start: PHYS_OFFSET + ldr r6, =(_end) @ Cover whole kernel + sub r6, r6, r5 @ Minimum size of region to map + clz r6, r6 @ Region size must be 2^N... + rsb r6, r6, #31 @ ...so round up region size + lsl r6, r6, #MPU_RSR_SZ @ Put size in right field + orr r6, r6, #(1 << MPU_RSR_EN) @ Set region enabled bit + bl __setup_mpu +#endif ldr r13, =__mmap_switched @ address to jump to after @ initialising sctlr adr lr, BSYM(1f) @ return (PIC) address @@ -147,4 +160,78 @@ __after_proc_init: ENDPROC(__after_proc_init) .ltorg +#ifdef CONFIG_ARM_MPU + + +/* Set which MPU region should be programmed */ +.macro set_region_nr tmp, rgnr + mov \tmp, \rgnr @ Use static region numbers + mcr p15, 0, \tmp, c6, c2, 0 @ Write RGNR +.endm + +/* Setup a single MPU region, either D or I side (D-side for unified) */ +.macro setup_region bar, acr, sr, side = MPU_DATA_SIDE + mcr p15, 0, \bar, c6, c1, (0 + \side) @ I/DRBAR + mcr p15, 0, \acr, c6, c1, (4 + \side) @ I/DRACR + mcr p15, 0, \sr, c6, c1, (2 + \side) @ I/DRSR +.endm + +/* + * Setup the MPU and initial MPU Regions. We create the following regions: + * Region 0: Use this for probing the MPU details, so leave disabled. + * Region 1: Background region - covers the whole of RAM as strongly ordered + * Region 2: Normal, Shared, cacheable for RAM. From PHYS_OFFSET, size from r6 + * + * r6: Value to be written to DRSR (and IRSR if required) for MPU_RAM_REGION +*/ + +ENTRY(__setup_mpu) + + /* Probe for v7 PMSA compliance */ + mrc p15, 0, r0, c0, c1, 4 @ Read ID_MMFR0 + and r0, r0, #(MMFR0_PMSA) @ PMSA field + teq r0, #(MMFR0_PMSAv7) @ PMSA v7 + bne __error_p @ Fail: ARM_MPU on NOT v7 PMSA + + /* Determine whether the D/I-side memory map is unified. We set the + * flags here and continue to use them for the rest of this function */ + mrc p15, 0, r0, c0, c0, 4 @ MPUIR + ands r5, r0, #MPUIR_DREGION_SZMASK @ 0 size d region => No MPU + beq __error_p @ Fail: ARM_MPU and no MPU + tst r0, #MPUIR_nU @ MPUIR_nU = 0 for unified + + /* Setup second region first to free up r6 */ + set_region_nr r0, #MPU_RAM_REGION + isb + /* Full access from PL0, PL1, shared for CONFIG_SMP, cacheable */ + ldr r0, =PHYS_OFFSET @ RAM starts at PHYS_OFFSET + ldr r5,=(MPU_AP_PL1RW_PL0RW | MPU_RGN_NORMAL) + + setup_region r0, r5, r6, MPU_DATA_SIDE @ PHYS_OFFSET, shared, enabled + beq 1f @ Memory-map not unified + setup_region r0, r5, r6, MPU_INSTR_SIDE @ PHYS_OFFSET, shared, enabled +1: isb + + /* First/background region */ + set_region_nr r0, #MPU_BG_REGION + isb + /* Execute Never, strongly ordered, inaccessible to PL0, rw PL1 */ + mov r0, #0 @ BG region starts at 0x0 + ldr r5,=(MPU_ACR_XN | MPU_RGN_STRONGLY_ORDERED | MPU_AP_PL1RW_PL0NA) + mov r6, #MPU_RSR_ALL_MEM @ 4GB region, enabled + + setup_region r0, r5, r6, MPU_DATA_SIDE @ 0x0, BG region, enabled + beq 2f @ Memory-map not unified + setup_region r0, r5, r6, MPU_INSTR_SIDE @ 0x0, BG region, enabled +2: isb + + /* Enable the MPU */ + mrc p15, 0, r0, c1, c0, 0 @ Read SCTLR + bic r0, r0, #CR_BR @ Disable the 'default mem-map' + orr r0, r0, #CR_M @ Set SCTRL.M (MPU on) + mcr p15, 0, r0, c1, c0, 0 @ Enable MPU + isb + mov pc,lr +ENDPROC(__setup_mpu) +#endif #include "head-common.S" -- cgit v1.2.3 From 5ad7dcbe40ae52bee67d7ed61efb6a3fccb6dc2b Mon Sep 17 00:00:00 2001 From: Jonathan Austin Date: Thu, 28 Feb 2013 17:46:36 +0000 Subject: ARM: mpu: add MPU probe and initialisation functions in C This patch adds new functions for probing and initialising the ARMv7 PMSA-compliant MPU. These use the pre-defined and reserved MPU_PROBE_REGION for establishing properties of the MPU, which is necessary because certain probe operations require modifying region properties and reading back the results. This patch also introduces a minimal sanity_check_meminfo_mpu function, that ensures that the memory set-up passed to the kernel can be used in conjunction with the MPU. The base address of a region must be aligned to the region size, otherwise behavior is unpredictable and region sizes can only be specified as a power-of-two. To simplify the satisfaction of these requirements this implementation currently enforces that all memory is contiguous from PHYS_OFFSET, merging banks that are contiguous but passed in separately. The functions are added in this patch but wired in to the boot process later in the series. Signed-off-by: Jonathan Austin Reviewed-by: Will Deacon CC: Hyok S. Choi --- arch/arm/mm/nommu.c | 249 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 249 insertions(+) diff --git a/arch/arm/mm/nommu.c b/arch/arm/mm/nommu.c index dd3a6c670f08..0897d6bd9c2d 100644 --- a/arch/arm/mm/nommu.c +++ b/arch/arm/mm/nommu.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -15,9 +16,257 @@ #include #include #include +#include +#include #include "mm.h" +#ifdef CONFIG_ARM_MPU +struct mpu_rgn_info mpu_rgn_info; + +/* Region number */ +static void rgnr_write(u32 v) +{ + asm("mcr p15, 0, %0, c6, c2, 0" : : "r" (v)); +} + +/* Data-side / unified region attributes */ + +/* Region access control register */ +static void dracr_write(u32 v) +{ + asm("mcr p15, 0, %0, c6, c1, 4" : : "r" (v)); +} + +/* Region size register */ +static void drsr_write(u32 v) +{ + asm("mcr p15, 0, %0, c6, c1, 2" : : "r" (v)); +} + +/* Region base address register */ +static void drbar_write(u32 v) +{ + asm("mcr p15, 0, %0, c6, c1, 0" : : "r" (v)); +} + +static u32 drbar_read(void) +{ + u32 v; + asm("mrc p15, 0, %0, c6, c1, 0" : "=r" (v)); + return v; +} +/* Optional instruction-side region attributes */ + +/* I-side Region access control register */ +static void iracr_write(u32 v) +{ + asm("mcr p15, 0, %0, c6, c1, 5" : : "r" (v)); +} + +/* I-side Region size register */ +static void irsr_write(u32 v) +{ + asm("mcr p15, 0, %0, c6, c1, 3" : : "r" (v)); +} + +/* I-side Region base address register */ +static void irbar_write(u32 v) +{ + asm("mcr p15, 0, %0, c6, c1, 1" : : "r" (v)); +} + +static unsigned long irbar_read(void) +{ + unsigned long v; + asm("mrc p15, 0, %0, c6, c1, 1" : "=r" (v)); + return v; +} + +/* MPU initialisation functions */ +void __init sanity_check_meminfo_mpu(void) +{ + int i; + struct membank *bank = meminfo.bank; + phys_addr_t phys_offset = PHYS_OFFSET; + phys_addr_t aligned_region_size, specified_mem_size, rounded_mem_size; + + /* Initially only use memory continuous from PHYS_OFFSET */ + if (bank_phys_start(&bank[0]) != phys_offset) + panic("First memory bank must be contiguous from PHYS_OFFSET"); + + /* Banks have already been sorted by start address */ + for (i = 1; i < meminfo.nr_banks; i++) { + if (bank[i].start <= bank_phys_end(&bank[0]) && + bank_phys_end(&bank[i]) > bank_phys_end(&bank[0])) { + bank[0].size = bank_phys_end(&bank[i]) - bank[0].start; + } else { + pr_notice("Ignoring RAM after 0x%.8lx. " + "First non-contiguous (ignored) bank start: 0x%.8lx\n", + (unsigned long)bank_phys_end(&bank[0]), + (unsigned long)bank_phys_start(&bank[i])); + break; + } + } + /* All contiguous banks are now merged in to the first bank */ + meminfo.nr_banks = 1; + specified_mem_size = bank[0].size; + + /* + * MPU has curious alignment requirements: Size must be power of 2, and + * region start must be aligned to the region size + */ + if (phys_offset != 0) + pr_info("PHYS_OFFSET != 0 => MPU Region size constrained by alignment requirements\n"); + + /* + * Maximum aligned region might overflow phys_addr_t if phys_offset is + * 0. Hence we keep everything below 4G until we take the smaller of + * the aligned_region_size and rounded_mem_size, one of which is + * guaranteed to be smaller than the maximum physical address. + */ + aligned_region_size = (phys_offset - 1) ^ (phys_offset); + /* Find the max power-of-two sized region that fits inside our bank */ + rounded_mem_size = (1 << __fls(bank[0].size)) - 1; + + /* The actual region size is the smaller of the two */ + aligned_region_size = aligned_region_size < rounded_mem_size + ? aligned_region_size + 1 + : rounded_mem_size + 1; + + if (aligned_region_size != specified_mem_size) + pr_warn("Truncating memory from 0x%.8lx to 0x%.8lx (MPU region constraints)", + (unsigned long)specified_mem_size, + (unsigned long)aligned_region_size); + + meminfo.bank[0].size = aligned_region_size; + pr_debug("MPU Region from 0x%.8lx size 0x%.8lx (end 0x%.8lx))\n", + (unsigned long)phys_offset, + (unsigned long)aligned_region_size, + (unsigned long)bank_phys_end(&bank[0])); + +} + +static int mpu_present(void) +{ + return ((read_cpuid_ext(CPUID_EXT_MMFR0) & MMFR0_PMSA) == MMFR0_PMSAv7); +} + +static int mpu_max_regions(void) +{ + /* + * We don't support a different number of I/D side regions so if we + * have separate instruction and data memory maps then return + * whichever side has a smaller number of supported regions. + */ + u32 dregions, iregions, mpuir; + mpuir = read_cpuid(CPUID_MPUIR); + + dregions = iregions = (mpuir & MPUIR_DREGION_SZMASK) >> MPUIR_DREGION; + + /* Check for separate d-side and i-side memory maps */ + if (mpuir & MPUIR_nU) + iregions = (mpuir & MPUIR_IREGION_SZMASK) >> MPUIR_IREGION; + + /* Use the smallest of the two maxima */ + return min(dregions, iregions); +} + +static int mpu_iside_independent(void) +{ + /* MPUIR.nU specifies whether there is *not* a unified memory map */ + return read_cpuid(CPUID_MPUIR) & MPUIR_nU; +} + +static int mpu_min_region_order(void) +{ + u32 drbar_result, irbar_result; + /* We've kept a region free for this probing */ + rgnr_write(MPU_PROBE_REGION); + isb(); + /* + * As per ARM ARM, write 0xFFFFFFFC to DRBAR to find the minimum + * region order + */ + drbar_write(0xFFFFFFFC); + drbar_result = irbar_result = drbar_read(); + drbar_write(0x0); + /* If the MPU is non-unified, we use the larger of the two minima*/ + if (mpu_iside_independent()) { + irbar_write(0xFFFFFFFC); + irbar_result = irbar_read(); + irbar_write(0x0); + } + isb(); /* Ensure that MPU region operations have completed */ + /* Return whichever result is larger */ + return __ffs(max(drbar_result, irbar_result)); +} + +static int mpu_setup_region(unsigned int number, phys_addr_t start, + unsigned int size_order, unsigned int properties) +{ + u32 size_data; + + /* We kept a region free for probing resolution of MPU regions*/ + if (number > mpu_max_regions() || number == MPU_PROBE_REGION) + return -ENOENT; + + if (size_order > 32) + return -ENOMEM; + + if (size_order < mpu_min_region_order()) + return -ENOMEM; + + /* Writing N to bits 5:1 (RSR_SZ) specifies region size 2^N+1 */ + size_data = ((size_order - 1) << MPU_RSR_SZ) | 1 << MPU_RSR_EN; + + dsb(); /* Ensure all previous data accesses occur with old mappings */ + rgnr_write(number); + isb(); + drbar_write(start); + dracr_write(properties); + isb(); /* Propagate properties before enabling region */ + drsr_write(size_data); + + /* Check for independent I-side registers */ + if (mpu_iside_independent()) { + irbar_write(start); + iracr_write(properties); + isb(); + irsr_write(size_data); + } + isb(); + + /* Store region info (we treat i/d side the same, so only store d) */ + mpu_rgn_info.rgns[number].dracr = properties; + mpu_rgn_info.rgns[number].drbar = start; + mpu_rgn_info.rgns[number].drsr = size_data; + return 0; +} + +/* +* Set up default MPU regions, doing nothing if there is no MPU +*/ +void __init mpu_setup(void) +{ + int region_err; + if (!mpu_present()) + return; + + region_err = mpu_setup_region(MPU_RAM_REGION, PHYS_OFFSET, + ilog2(meminfo.bank[0].size), + MPU_AP_PL1RW_PL0RW | MPU_RGN_NORMAL); + if (region_err) { + panic("MPU region initialization failure! %d", region_err); + } else { + pr_info("Using ARMv7 PMSA Compliant MPU. " + "Region independence: %s, Max regions: %d\n", + mpu_iside_independent() ? "Yes" : "No", + mpu_max_regions()); + } +} +#endif /* CONFIG_ARM_MPU */ + void __init arm_mm_memblock_reserve(void) { #ifndef CONFIG_CPU_V7M -- cgit v1.2.3 From 9a271567fe9980a7e7ded0c6250d56200c3678ee Mon Sep 17 00:00:00 2001 From: Jonathan Austin Date: Thu, 28 Feb 2013 17:51:05 +0000 Subject: ARM: mpu: Complete initialisation of the MPU after reaching the C-world Much like with the MMU, MPU initialisation is performed in two stages; the first in the pre-C world and the 'real' initialisation during arch setup. This patch wires in previously added MPU initialisation functions so that the whole of memory is mapped with the appropriate region properties for 'normal' RAM (the appropriate properties depend on whether the system is SMP). Stub initialisation functions are added for the case that there MPU support is not configured in to the kernel. Signed-off-by: Jonathan Austin Reviewed-by: Will Deacon CC: Hyok S. Choi --- arch/arm/mm/nommu.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/arch/arm/mm/nommu.c b/arch/arm/mm/nommu.c index 0897d6bd9c2d..c1b494d43ed1 100644 --- a/arch/arm/mm/nommu.c +++ b/arch/arm/mm/nommu.c @@ -265,6 +265,9 @@ void __init mpu_setup(void) mpu_max_regions()); } } +#else +static void sanity_check_meminfo_mpu(void) {} +static void __init mpu_setup(void) {} #endif /* CONFIG_ARM_MPU */ void __init arm_mm_memblock_reserve(void) @@ -286,7 +289,9 @@ void __init arm_mm_memblock_reserve(void) void __init sanity_check_meminfo(void) { - phys_addr_t end = bank_phys_end(&meminfo.bank[meminfo.nr_banks - 1]); + phys_addr_t end; + sanity_check_meminfo_mpu(); + end = bank_phys_end(&meminfo.bank[meminfo.nr_banks - 1]); high_memory = __va(end - 1) + 1; } @@ -297,6 +302,7 @@ void __init sanity_check_meminfo(void) void __init paging_init(struct machine_desc *mdesc) { early_trap_init((void *)CONFIG_VECTORS_BASE); + mpu_setup(); bootmem_init(); } -- cgit v1.2.3 From eb08375ea66e63c5e11dea69b43c5633d531ce81 Mon Sep 17 00:00:00 2001 From: Jonathan Austin Date: Fri, 22 Feb 2013 18:51:30 +0000 Subject: ARM: mpu: add MPU initialisation for secondary cores The MPU initialisation on the primary core is performed in two stages, one minimal stage to ensure the CPU can boot and a second one after sanity_check_meminfo. As the memory configuration is known by the time we boot secondary cores only a single step is necessary, provided the values for DRSR are passed to secondaries. This patch implements this arrangement. The configuration generated for the MPU regions is made available to the secondary core, which can then use the asm MPU intialisation code to program a complete region configuration. This is necessary for SMP configurations without an MMU, as the MPU initialisation is the only way to ensure that memory is specified as 'shared'. Signed-off-by: Jonathan Austin Reviewed-by: Will Deacon CC: Nicolas Pitre --- arch/arm/include/asm/smp.h | 5 ++++- arch/arm/kernel/head-nommu.S | 7 +++++++ arch/arm/kernel/smp.c | 8 ++++++-- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/arch/arm/include/asm/smp.h b/arch/arm/include/asm/smp.h index d3a22bebe6ce..a8cae71caceb 100644 --- a/arch/arm/include/asm/smp.h +++ b/arch/arm/include/asm/smp.h @@ -65,7 +65,10 @@ asmlinkage void secondary_start_kernel(void); * Initial data for bringing up a secondary CPU. */ struct secondary_data { - unsigned long pgdir; + union { + unsigned long mpu_rgn_szr; + unsigned long pgdir; + }; unsigned long swapper_pg_dir; void *stack; }; diff --git a/arch/arm/kernel/head-nommu.S b/arch/arm/kernel/head-nommu.S index 659912c49571..13741d004de5 100644 --- a/arch/arm/kernel/head-nommu.S +++ b/arch/arm/kernel/head-nommu.S @@ -107,6 +107,13 @@ ENTRY(secondary_startup) adr r4, __secondary_data ldmia r4, {r7, r12} + +#ifdef CONFIG_ARM_MPU + /* Use MPU region info supplied by __cpu_up */ + ldr r6, [r7] @ get secondary_data.mpu_szr + bl __setup_mpu @ Initialize the MPU +#endif + adr lr, BSYM(__after_proc_init) @ return address mov r13, r12 @ __secondary_switched address ARM( add pc, r10, #PROCINFO_INITFUNC ) diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index 44d1c00dc45f..e17d9346baee 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -45,6 +45,7 @@ #include #include #include +#include /* * as from 2.5, kernels no longer have an init_tasks structure @@ -87,6 +88,10 @@ int __cpuinit __cpu_up(unsigned int cpu, struct task_struct *idle) * its stack and the page tables. */ secondary_data.stack = task_stack_page(idle) + THREAD_START_SP; +#ifdef CONFIG_ARM_MPU + secondary_data.mpu_rgn_szr = mpu_rgn_info.rgns[MPU_RAM_REGION].drsr; +#endif + #ifdef CONFIG_MMU secondary_data.pgdir = virt_to_phys(idmap_pgd); secondary_data.swapper_pg_dir = virt_to_phys(swapper_pg_dir); @@ -114,9 +119,8 @@ int __cpuinit __cpu_up(unsigned int cpu, struct task_struct *idle) pr_err("CPU%u: failed to boot: %d\n", cpu, ret); } - secondary_data.stack = NULL; - secondary_data.pgdir = 0; + memset(&secondary_data, 0, sizeof(secondary_data)); return ret; } -- cgit v1.2.3 From 1e1bb58da2ae080002578c35f80495dadda50940 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Fri, 7 Jun 2013 17:11:24 +0100 Subject: regulator: ab8500: Ensure AB8500 external registers are probed first This patch changes the order in which the AB8500 regulator drivers are registered and subsequently probed. It saves a lot of -EPROBE_DEFEER nonsense and bootlog noise, as some AB8500 core regulators depend on the external ones for supply voltage. Signed-off-by: Lee Jones Signed-off-by: Mark Brown --- drivers/regulator/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 47a34ff88f98..0f4964ee5d30 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -12,7 +12,7 @@ obj-$(CONFIG_REGULATOR_USERSPACE_CONSUMER) += userspace-consumer.o obj-$(CONFIG_REGULATOR_88PM8607) += 88pm8607.o obj-$(CONFIG_REGULATOR_AAT2870) += aat2870-regulator.o obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o -obj-$(CONFIG_REGULATOR_AB8500) += ab8500.o ab8500-ext.o +obj-$(CONFIG_REGULATOR_AB8500) += ab8500-ext.o ab8500.o obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o obj-$(CONFIG_REGULATOR_ARIZONA) += arizona-micsupp.o arizona-ldo1.o -- cgit v1.2.3 From 33fb880249b1f2f349461c64d19bdfe2e969c1ba Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Fri, 7 Jun 2013 17:11:25 +0100 Subject: regulator: ab8500-ext: Provide a set_voltage call-back operation When registering regulators which have a single voltage through Device Tree, the framework insists that the specified voltage is actually set. Well in order to do that we need to provide this call-back, where we check that the value is sane and return without error. Not that the selector isn't populated, but in our case list_voltage doesn't actually use it, so we're good. Signed-off-by: Lee Jones Signed-off-by: Mark Brown --- drivers/regulator/ab8500-ext.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/regulator/ab8500-ext.c b/drivers/regulator/ab8500-ext.c index b4d45472aae6..e4975bc61e81 100644 --- a/drivers/regulator/ab8500-ext.c +++ b/drivers/regulator/ab8500-ext.c @@ -229,6 +229,28 @@ static unsigned int ab8500_ext_regulator_get_mode(struct regulator_dev *rdev) return ret; } +static int ab8500_ext_set_voltage(struct regulator_dev *rdev, int min_uV, + int max_uV, unsigned *selector) +{ + struct regulation_constraints *regu_constraints = rdev->constraints; + + if (!regu_constraints) { + dev_err(rdev_get_dev(rdev), "No regulator constraints\n"); + return -EINVAL; + } + + if (regu_constraints->min_uV == min_uV && + regu_constraints->max_uV == max_uV) + return 0; + + dev_err(rdev_get_dev(rdev), + "Requested min %duV max %duV != constrained min %duV max %duV\n", + min_uV, max_uV, + regu_constraints->min_uV, regu_constraints->max_uV); + + return -EINVAL; +} + static int ab8500_ext_list_voltage(struct regulator_dev *rdev, unsigned selector) { @@ -252,6 +274,7 @@ static struct regulator_ops ab8500_ext_regulator_ops = { .is_enabled = ab8500_ext_regulator_is_enabled, .set_mode = ab8500_ext_regulator_set_mode, .get_mode = ab8500_ext_regulator_get_mode, + .set_voltage = ab8500_ext_set_voltage, .list_voltage = ab8500_ext_list_voltage, }; -- cgit v1.2.3 From 6f0d94790efe9f4481bbd7c174ef0e9b5e5db7c4 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Fri, 7 Jun 2013 18:26:09 +0200 Subject: ASoC: atmel-ssc: prepare clk before calling enable Replace clk_enable/disable with clk_prepare_enable/disable_unprepare to avoid common clk framework warnings. Signed-off-by: Boris BREZILLON Signed-off-by: Mark Brown --- drivers/misc/atmel-ssc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/misc/atmel-ssc.c b/drivers/misc/atmel-ssc.c index c09c28f92055..e6191976d990 100644 --- a/drivers/misc/atmel-ssc.c +++ b/drivers/misc/atmel-ssc.c @@ -58,7 +58,7 @@ struct ssc_device *ssc_request(unsigned int ssc_num) ssc->user++; spin_unlock(&user_lock); - clk_enable(ssc->clk); + clk_prepare_enable(ssc->clk); return ssc; } @@ -69,7 +69,7 @@ void ssc_free(struct ssc_device *ssc) spin_lock(&user_lock); if (ssc->user) { ssc->user--; - clk_disable(ssc->clk); + clk_disable_unprepare(ssc->clk); } else { dev_dbg(&ssc->pdev->dev, "device already free\n"); } @@ -172,10 +172,10 @@ static int ssc_probe(struct platform_device *pdev) } /* disable all interrupts */ - clk_enable(ssc->clk); + clk_prepare_enable(ssc->clk); ssc_writel(ssc->regs, IDR, -1); ssc_readl(ssc->regs, SR); - clk_disable(ssc->clk); + clk_disable_unprepare(ssc->clk); ssc->irq = platform_get_irq(pdev, 0); if (!ssc->irq) { -- cgit v1.2.3 From d25749afc6f2a40471a70c04a35633e30cbe59a5 Mon Sep 17 00:00:00 2001 From: Damian Hobson-Garcia Date: Tue, 30 Apr 2013 04:02:13 +0100 Subject: arm64: Provide default implementation for dma_{alloc,free}_attrs Most architectures that define CONFIG_HAS_DMA, have implementations for both dma_alloc_attrs() and dma_free_attrs(). All achitectures that do not define CONFIG_HAS_DMA also have both of these definitions provided by dma-mapping-broken.h. Add default implementations for these functions on arm64. Signed-off-by: Damian Hobson-Garcia Signed-off-by: Catalin Marinas --- arch/arm64/include/asm/dma-mapping.h | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/arch/arm64/include/asm/dma-mapping.h b/arch/arm64/include/asm/dma-mapping.h index 994776894198..8d1810001aef 100644 --- a/arch/arm64/include/asm/dma-mapping.h +++ b/arch/arm64/include/asm/dma-mapping.h @@ -81,8 +81,12 @@ static inline void dma_mark_clean(void *addr, size_t size) { } -static inline void *dma_alloc_coherent(struct device *dev, size_t size, - dma_addr_t *dma_handle, gfp_t flags) +#define dma_alloc_coherent(d, s, h, f) dma_alloc_attrs(d, s, h, f, NULL) +#define dma_free_coherent(d, s, h, f) dma_free_attrs(d, s, h, f, NULL) + +static inline void *dma_alloc_attrs(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t flags, + struct dma_attrs *attrs) { struct dma_map_ops *ops = get_dma_ops(dev); void *vaddr; @@ -90,13 +94,14 @@ static inline void *dma_alloc_coherent(struct device *dev, size_t size, if (dma_alloc_from_coherent(dev, size, dma_handle, &vaddr)) return vaddr; - vaddr = ops->alloc(dev, size, dma_handle, flags, NULL); + vaddr = ops->alloc(dev, size, dma_handle, flags, attrs); debug_dma_alloc_coherent(dev, size, *dma_handle, vaddr); return vaddr; } -static inline void dma_free_coherent(struct device *dev, size_t size, - void *vaddr, dma_addr_t dev_addr) +static inline void dma_free_attrs(struct device *dev, size_t size, + void *vaddr, dma_addr_t dev_addr, + struct dma_attrs *attrs) { struct dma_map_ops *ops = get_dma_ops(dev); @@ -104,7 +109,7 @@ static inline void dma_free_coherent(struct device *dev, size_t size, return; debug_dma_free_coherent(dev, size, vaddr, dev_addr); - ops->free(dev, size, vaddr, dev_addr, NULL); + ops->free(dev, size, vaddr, dev_addr, attrs); } /* -- cgit v1.2.3 From b5b6c9e9149d8a7c3f1d7b9d0c046c6184e1dd17 Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Wed, 1 May 2013 12:23:05 +0100 Subject: arm64: Avoid cache flushing in flush_dcache_page() The flush_dcache_page() function is called when the kernel modified a page cache page. Since the D-cache on AArch64 does not have aliases this function can simply mark the page as dirty for later flushing via set_pte_at()/__sync_icache_dcache() if the page is executable (to ensure the I-D cache coherency). Signed-off-by: Catalin Marinas Reported-by: Will Deacon Acked-by: Will Deacon --- arch/arm64/mm/flush.c | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/arch/arm64/mm/flush.c b/arch/arm64/mm/flush.c index 88611c3a421a..b9cd7a4deeca 100644 --- a/arch/arm64/mm/flush.c +++ b/arch/arm64/mm/flush.c @@ -94,28 +94,14 @@ void __sync_icache_dcache(pte_t pte, unsigned long addr) } /* - * Ensure cache coherency between kernel mapping and userspace mapping of this - * page. + * This function is called when a page has been modified by the kernel. Mark + * it as dirty for later flushing when mapped in user space (if executable, + * see __sync_icache_dcache). */ void flush_dcache_page(struct page *page) { - struct address_space *mapping; - - /* - * The zero page is never written to, so never has any dirty cache - * lines, and therefore never needs to be flushed. - */ - if (page == ZERO_PAGE(0)) - return; - - mapping = page_mapping(page); - if (mapping && mapping_mapped(mapping)) { - __flush_dcache_page(page); - __flush_icache_all(); - set_bit(PG_dcache_clean, &page->flags); - } else { + if (test_bit(PG_dcache_clean, &page->flags)) clear_bit(PG_dcache_clean, &page->flags); - } } EXPORT_SYMBOL(flush_dcache_page); -- cgit v1.2.3 From 7249b79f6b4cc3c2aa9138dca52e535a4c789107 Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Wed, 1 May 2013 16:34:22 +0100 Subject: arm64: Do not flush the D-cache for anonymous pages The D-cache on AArch64 is VIPT non-aliasing, so there is no need to flush it for anonymous pages. Signed-off-by: Catalin Marinas Reported-by: Will Deacon Acked-by: Will Deacon --- arch/arm64/mm/flush.c | 8 +++----- arch/arm64/mm/mmu.c | 1 - 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/arch/arm64/mm/flush.c b/arch/arm64/mm/flush.c index b9cd7a4deeca..7c716634a671 100644 --- a/arch/arm64/mm/flush.c +++ b/arch/arm64/mm/flush.c @@ -77,14 +77,12 @@ void __flush_dcache_page(struct page *page) void __sync_icache_dcache(pte_t pte, unsigned long addr) { - unsigned long pfn; - struct page *page; + struct page *page = pte_page(pte); - pfn = pte_pfn(pte); - if (!pfn_valid(pfn)) + /* no flushing needed for anonymous pages */ + if (!page_mapping(page)) return; - page = pfn_to_page(pfn); if (!test_and_set_bit(PG_dcache_clean, &page->flags)) { __flush_dcache_page(page); __flush_icache_all(); diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index eeecc9c8ed68..80a369eab637 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -339,7 +339,6 @@ void __init paging_init(void) bootmem_init(); empty_zero_page = virt_to_page(zero_page); - __flush_dcache_page(empty_zero_page); /* * TTBR0 is only used for the identity mapping at this stage. Make it -- cgit v1.2.3 From ebd88367de80f9509bd30a09342d0a19c925b23e Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Wed, 1 May 2013 16:38:23 +0100 Subject: arm64: Remove __flush_dcache_page() This function is only used in __sync_icache_dcache(), so remove it and call __flush_dcache_area() directly. The flush_icache_user_range() function is not used in the arm64 kernel. Signed-off-by: Catalin Marinas Reported-by: Will Deacon Acked-by: Will Deacon --- arch/arm64/include/asm/cacheflush.h | 3 --- arch/arm64/mm/flush.c | 7 +------ arch/arm64/mm/mm.h | 1 - 3 files changed, 1 insertion(+), 10 deletions(-) diff --git a/arch/arm64/include/asm/cacheflush.h b/arch/arm64/include/asm/cacheflush.h index 3300cbd18a89..fea9ee327206 100644 --- a/arch/arm64/include/asm/cacheflush.h +++ b/arch/arm64/include/asm/cacheflush.h @@ -123,9 +123,6 @@ static inline void __flush_icache_all(void) #define flush_dcache_mmap_unlock(mapping) \ spin_unlock_irq(&(mapping)->tree_lock) -#define flush_icache_user_range(vma,page,addr,len) \ - flush_dcache_page(page) - /* * We don't appear to need to do anything here. In fact, if we did, we'd * duplicate cache flushing elsewhere performed by flush_dcache_page(). diff --git a/arch/arm64/mm/flush.c b/arch/arm64/mm/flush.c index 7c716634a671..e4193e3adc7f 100644 --- a/arch/arm64/mm/flush.c +++ b/arch/arm64/mm/flush.c @@ -70,11 +70,6 @@ void copy_to_user_page(struct vm_area_struct *vma, struct page *page, #endif } -void __flush_dcache_page(struct page *page) -{ - __flush_dcache_area(page_address(page), PAGE_SIZE); -} - void __sync_icache_dcache(pte_t pte, unsigned long addr) { struct page *page = pte_page(pte); @@ -84,7 +79,7 @@ void __sync_icache_dcache(pte_t pte, unsigned long addr) return; if (!test_and_set_bit(PG_dcache_clean, &page->flags)) { - __flush_dcache_page(page); + __flush_dcache_area(page_address(page), PAGE_SIZE); __flush_icache_all(); } else if (icache_is_aivivt()) { __flush_icache_all(); diff --git a/arch/arm64/mm/mm.h b/arch/arm64/mm/mm.h index 916701e6d040..d519f4f50c8c 100644 --- a/arch/arm64/mm/mm.h +++ b/arch/arm64/mm/mm.h @@ -1,3 +1,2 @@ -extern void __flush_dcache_page(struct page *page); extern void __init bootmem_init(void); extern void __init arm64_swiotlb_init(void); -- cgit v1.2.3 From 4ecf7ccb1973fd826456b6ab1e6dfafe9023c753 Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Fri, 31 May 2013 16:30:58 +0100 Subject: arm64: spinlock: retry trylock operation if strex fails on free lock An exclusive store instruction may fail for reasons other than lock contention (e.g. a cache eviction during the critical section) so, in line with other architectures using similar exclusive instructions (alpha, mips, powerpc), retry the trylock operation if the lock appears to be free but the strex reported failure. Signed-off-by: Catalin Marinas Reported-by: Tony Thompson Acked-by: Will Deacon --- arch/arm64/include/asm/spinlock.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm64/include/asm/spinlock.h b/arch/arm64/include/asm/spinlock.h index 7065e920149d..0defa0728a9b 100644 --- a/arch/arm64/include/asm/spinlock.h +++ b/arch/arm64/include/asm/spinlock.h @@ -59,9 +59,10 @@ static inline int arch_spin_trylock(arch_spinlock_t *lock) unsigned int tmp; asm volatile( - " ldaxr %w0, %1\n" + "2: ldaxr %w0, %1\n" " cbnz %w0, 1f\n" " stxr %w0, %w2, %1\n" + " cbnz %w0, 2b\n" "1:\n" : "=&r" (tmp), "+Q" (lock->lock) : "r" (1) -- cgit v1.2.3 From 737c16dffc458e58ee7840556d43b874cb8e16a0 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Wed, 5 Jun 2013 12:40:34 +0100 Subject: arm64: mm: don't bother invalidating the icache in switch_mm We don't support software broadcast of cache maintenance operations, so this flush is not required (__sync_icache_dcache will always affect all CPUs). Signed-off-by: Will Deacon Signed-off-by: Catalin Marinas --- arch/arm64/include/asm/mmu_context.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/arch/arm64/include/asm/mmu_context.h b/arch/arm64/include/asm/mmu_context.h index e2bc385adb6b..a9eee33dfa62 100644 --- a/arch/arm64/include/asm/mmu_context.h +++ b/arch/arm64/include/asm/mmu_context.h @@ -151,12 +151,6 @@ switch_mm(struct mm_struct *prev, struct mm_struct *next, { unsigned int cpu = smp_processor_id(); -#ifdef CONFIG_SMP - /* check for possible thread migration */ - if (!cpumask_empty(mm_cpumask(next)) && - !cpumask_test_cpu(cpu, mm_cpumask(next))) - __flush_icache_all(); -#endif if (!cpumask_test_and_set_cpu(cpu, mm_cpumask(next)) || prev != next) check_and_switch_context(next, tsk); } -- cgit v1.2.3 From a649dbfea36bdcead7ffddc622c272844b95a13e Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Sat, 25 May 2013 21:48:33 +0800 Subject: ia64/PCI: Clean up pci_scan_root_bus() usage pci_scan_root_bus() already set bus->sysdata, so no need to explicitly set it again in function sn_pci_controller_fixup(). Signed-off-by: Jiang Liu Signed-off-by: Bjorn Helgaas Cc: Tony Luck Cc: Fenghua Yu --- arch/ia64/sn/kernel/io_init.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/arch/ia64/sn/kernel/io_init.c b/arch/ia64/sn/kernel/io_init.c index 238e2c511d94..e2c7733e18a7 100644 --- a/arch/ia64/sn/kernel/io_init.c +++ b/arch/ia64/sn/kernel/io_init.c @@ -326,16 +326,7 @@ sn_pci_controller_fixup(int segment, int busnum, struct pci_bus *bus) bus = pci_scan_root_bus(NULL, busnum, &pci_root_ops, controller, &resources); if (bus == NULL) - goto error_return; /* error, or bus already scanned */ - - bus->sysdata = controller; - - return; - -error_return: - - kfree(controller); - return; + kfree(controller); } /* -- cgit v1.2.3 From 343df771e671d821478dd3ef525a0610b808dbf8 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Fri, 7 Jun 2013 01:10:08 +0800 Subject: PCI: Fix refcount issue in pci_create_root_bus() error recovery path After calling device_register(&bridge->dev), the bridge is reference- counted, and it is illegal to call kfree() on it except in the release function. [bhelgaas: changelog, use put_device() after device_register() failure] Signed-off-by: Jiang Liu Signed-off-by: Bjorn Helgaas Cc: stable@vger.kernel.org --- drivers/pci/probe.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index ed5ce185eed9..15c39cb09619 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1711,12 +1711,16 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, bridge->dev.release = pci_release_bus_bridge_dev; dev_set_name(&bridge->dev, "pci%04x:%02x", pci_domain_nr(b), bus); error = pcibios_root_bridge_prepare(bridge); - if (error) - goto bridge_dev_reg_err; + if (error) { + kfree(bridge); + goto err_out; + } error = device_register(&bridge->dev); - if (error) - goto bridge_dev_reg_err; + if (error) { + put_device(&bridge->dev); + goto err_out; + } b->bridge = get_device(&bridge->dev); device_enable_async_suspend(b->bridge); pci_set_bus_of_node(b); @@ -1772,8 +1776,6 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, class_dev_reg_err: put_device(&bridge->dev); device_unregister(&bridge->dev); -bridge_dev_reg_err: - kfree(bridge); err_out: kfree(b); return NULL; -- cgit v1.2.3 From d70a3f887a38cb0acc7233a1d05f15c2b6e0be2b Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Thu, 6 Jun 2013 14:08:13 +0000 Subject: doc: packet: simplify tpacket example code This patch simplifies the tpacket_v3 example code a bit by getting rid of unecessary macro wrappers, removing some debugging code so that it is more to the point, and also adds a header comment. Now this example code is the very minimum one needs to start from when dealing with tpacket_v3 and ~100 lines smaller than before. Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller --- Documentation/networking/packet_mmap.txt | 133 +++++++------------------------ 1 file changed, 28 insertions(+), 105 deletions(-) diff --git a/Documentation/networking/packet_mmap.txt b/Documentation/networking/packet_mmap.txt index 23dd80e82b8e..8572796b1eb6 100644 --- a/Documentation/networking/packet_mmap.txt +++ b/Documentation/networking/packet_mmap.txt @@ -704,6 +704,12 @@ So it seems to be a good candidate to be used with packet fanout. Minimal example code by Daniel Borkmann based on Chetan Loke's lolpcap (compile it with gcc -Wall -O2 blob.c, and try things like "./a.out eth0", etc.): +/* Written from scratch, but kernel-to-user space API usage + * dissected from lolpcap: + * Copyright 2011, Chetan Loke + * License: GPL, version 2.0 + */ + #include #include #include @@ -722,27 +728,6 @@ it with gcc -Wall -O2 blob.c, and try things like "./a.out eth0", etc.): #include #include -#define BLOCK_SIZE (1 << 22) -#define FRAME_SIZE 2048 - -#define NUM_BLOCKS 64 -#define NUM_FRAMES ((BLOCK_SIZE * NUM_BLOCKS) / FRAME_SIZE) - -#define BLOCK_RETIRE_TOV_IN_MS 64 -#define BLOCK_PRIV_AREA_SZ 13 - -#define ALIGN_8(x) (((x) + 8 - 1) & ~(8 - 1)) - -#define BLOCK_STATUS(x) ((x)->h1.block_status) -#define BLOCK_NUM_PKTS(x) ((x)->h1.num_pkts) -#define BLOCK_O2FP(x) ((x)->h1.offset_to_first_pkt) -#define BLOCK_LEN(x) ((x)->h1.blk_len) -#define BLOCK_SNUM(x) ((x)->h1.seq_num) -#define BLOCK_O2PRIV(x) ((x)->offset_to_priv) -#define BLOCK_PRIV(x) ((void *) ((uint8_t *) (x) + BLOCK_O2PRIV(x))) -#define BLOCK_HDR_LEN (ALIGN_8(sizeof(struct block_desc))) -#define BLOCK_PLUS_PRIV(sz_pri) (BLOCK_HDR_LEN + ALIGN_8((sz_pri))) - #ifndef likely # define likely(x) __builtin_expect(!!(x), 1) #endif @@ -765,7 +750,7 @@ struct ring { static unsigned long packets_total = 0, bytes_total = 0; static sig_atomic_t sigint = 0; -void sighandler(int num) +static void sighandler(int num) { sigint = 1; } @@ -774,6 +759,8 @@ static int setup_socket(struct ring *ring, char *netdev) { int err, i, fd, v = TPACKET_V3; struct sockaddr_ll ll; + unsigned int blocksiz = 1 << 22, framesiz = 1 << 11; + unsigned int blocknum = 64; fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (fd < 0) { @@ -788,13 +775,12 @@ static int setup_socket(struct ring *ring, char *netdev) } memset(&ring->req, 0, sizeof(ring->req)); - ring->req.tp_block_size = BLOCK_SIZE; - ring->req.tp_frame_size = FRAME_SIZE; - ring->req.tp_block_nr = NUM_BLOCKS; - ring->req.tp_frame_nr = NUM_FRAMES; - ring->req.tp_retire_blk_tov = BLOCK_RETIRE_TOV_IN_MS; - ring->req.tp_sizeof_priv = BLOCK_PRIV_AREA_SZ; - ring->req.tp_feature_req_word |= TP_FT_REQ_FILL_RXHASH; + ring->req.tp_block_size = blocksiz; + ring->req.tp_frame_size = framesiz; + ring->req.tp_block_nr = blocknum; + ring->req.tp_frame_nr = (blocksiz * blocknum) / framesiz; + ring->req.tp_retire_blk_tov = 60; + ring->req.tp_feature_req_word = TP_FT_REQ_FILL_RXHASH; err = setsockopt(fd, SOL_PACKET, PACKET_RX_RING, &ring->req, sizeof(ring->req)); @@ -804,8 +790,7 @@ static int setup_socket(struct ring *ring, char *netdev) } ring->map = mmap(NULL, ring->req.tp_block_size * ring->req.tp_block_nr, - PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, - fd, 0); + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, fd, 0); if (ring->map == MAP_FAILED) { perror("mmap"); exit(1); @@ -835,58 +820,6 @@ static int setup_socket(struct ring *ring, char *netdev) return fd; } -#ifdef __checked -static uint64_t prev_block_seq_num = 0; - -void assert_block_seq_num(struct block_desc *pbd) -{ - if (unlikely(prev_block_seq_num + 1 != BLOCK_SNUM(pbd))) { - printf("prev_block_seq_num:%"PRIu64", expected seq:%"PRIu64" != " - "actual seq:%"PRIu64"\n", prev_block_seq_num, - prev_block_seq_num + 1, (uint64_t) BLOCK_SNUM(pbd)); - exit(1); - } - - prev_block_seq_num = BLOCK_SNUM(pbd); -} - -static void assert_block_len(struct block_desc *pbd, uint32_t bytes, int block_num) -{ - if (BLOCK_NUM_PKTS(pbd)) { - if (unlikely(bytes != BLOCK_LEN(pbd))) { - printf("block:%u with %upackets, expected len:%u != actual len:%u\n", - block_num, BLOCK_NUM_PKTS(pbd), bytes, BLOCK_LEN(pbd)); - exit(1); - } - } else { - if (unlikely(BLOCK_LEN(pbd) != BLOCK_PLUS_PRIV(BLOCK_PRIV_AREA_SZ))) { - printf("block:%u, expected len:%lu != actual len:%u\n", - block_num, BLOCK_HDR_LEN, BLOCK_LEN(pbd)); - exit(1); - } - } -} - -static void assert_block_header(struct block_desc *pbd, const int block_num) -{ - uint32_t block_status = BLOCK_STATUS(pbd); - - if (unlikely((block_status & TP_STATUS_USER) == 0)) { - printf("block:%u, not in TP_STATUS_USER\n", block_num); - exit(1); - } - - assert_block_seq_num(pbd); -} -#else -static inline void assert_block_header(struct block_desc *pbd, const int block_num) -{ -} -static void assert_block_len(struct block_desc *pbd, uint32_t bytes, int block_num) -{ -} -#endif - static void display(struct tpacket3_hdr *ppd) { struct ethhdr *eth = (struct ethhdr *) ((uint8_t *) ppd + ppd->tp_mac); @@ -916,37 +849,27 @@ static void display(struct tpacket3_hdr *ppd) static void walk_block(struct block_desc *pbd, const int block_num) { - int num_pkts = BLOCK_NUM_PKTS(pbd), i; + int num_pkts = pbd->h1.num_pkts, i; unsigned long bytes = 0; - unsigned long bytes_with_padding = BLOCK_PLUS_PRIV(BLOCK_PRIV_AREA_SZ); struct tpacket3_hdr *ppd; - assert_block_header(pbd, block_num); - - ppd = (struct tpacket3_hdr *) ((uint8_t *) pbd + BLOCK_O2FP(pbd)); + ppd = (struct tpacket3_hdr *) ((uint8_t *) pbd + + pbd->h1.offset_to_first_pkt); for (i = 0; i < num_pkts; ++i) { bytes += ppd->tp_snaplen; - if (ppd->tp_next_offset) - bytes_with_padding += ppd->tp_next_offset; - else - bytes_with_padding += ALIGN_8(ppd->tp_snaplen + ppd->tp_mac); - display(ppd); - ppd = (struct tpacket3_hdr *) ((uint8_t *) ppd + ppd->tp_next_offset); - __sync_synchronize(); + ppd = (struct tpacket3_hdr *) ((uint8_t *) ppd + + ppd->tp_next_offset); } - assert_block_len(pbd, bytes_with_padding, block_num); - packets_total += num_pkts; bytes_total += bytes; } -void flush_block(struct block_desc *pbd) +static void flush_block(struct block_desc *pbd) { - BLOCK_STATUS(pbd) = TP_STATUS_KERNEL; - __sync_synchronize(); + pbd->h1.block_status = TP_STATUS_KERNEL; } static void teardown_socket(struct ring *ring, int fd) @@ -962,7 +885,7 @@ int main(int argc, char **argp) socklen_t len; struct ring ring; struct pollfd pfd; - unsigned int block_num = 0; + unsigned int block_num = 0, blocks = 64; struct block_desc *pbd; struct tpacket_stats_v3 stats; @@ -984,15 +907,15 @@ int main(int argc, char **argp) while (likely(!sigint)) { pbd = (struct block_desc *) ring.rd[block_num].iov_base; -retry_block: - if ((BLOCK_STATUS(pbd) & TP_STATUS_USER) == 0) { + + if ((pbd->h1.block_status & TP_STATUS_USER) == 0) { poll(&pfd, 1, -1); - goto retry_block; + continue; } walk_block(pbd, block_num); flush_block(pbd); - block_num = (block_num + 1) % NUM_BLOCKS; + block_num = (block_num + 1) % blocks; } len = sizeof(stats); -- cgit v1.2.3 From 350e7805706e11fef0bda2ab4b5cd5a0881480b9 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Thu, 6 Jun 2013 14:08:31 +0000 Subject: net: vlan: minor: remove unused HAVE_VLAN_PUT_TAG Remove the definition of HAVE_VLAN_PUT_TAG since it's not used or exported anywhere. Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/linux/if_vlan.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index 52bd03b38962..7a9c8cf31659 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -243,8 +243,6 @@ static inline struct sk_buff *__vlan_hwaccel_put_tag(struct sk_buff *skb, return skb; } -#define HAVE_VLAN_PUT_TAG - /** * vlan_put_tag - inserts VLAN tag according to device features * @skb: skbuff to tag -- cgit v1.2.3 From 5ee98591577aa63dbb9e78a0d142abc86b9063d0 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Fri, 7 Jun 2013 05:11:45 +0000 Subject: net: minor: tcp: use tcp_skb_mss helper in tcp_tso_segment We have the minimal inline helper tcp_skb_mss to access skb_shinfo(skb)->gso_size, so also use it here to get mss. Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller --- net/ipv4/tcp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index b5d4ad988053..6a1cf95abc98 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2905,7 +2905,7 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb, oldlen = (u16)~skb->len; __skb_pull(skb, thlen); - mss = skb_shinfo(skb)->gso_size; + mss = tcp_skb_mss(skb); if (unlikely(skb->len <= mss)) goto out; @@ -3071,7 +3071,7 @@ found: flush |= *(u32 *)((u8 *)th + i) ^ *(u32 *)((u8 *)th2 + i); - mss = skb_shinfo(p)->gso_size; + mss = tcp_skb_mss(p); flush |= (len - 1) >= mss; flush |= (ntohl(th2->seq) + skb_gro_len(p)) ^ ntohl(th->seq); -- cgit v1.2.3 From 28850dc7c71da9d0c0e39246e9ff6913f41f8d0a Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Fri, 7 Jun 2013 05:11:46 +0000 Subject: net: tcp: move GRO/GSO functions to tcp_offload Would be good to make things explicit and move those functions to a new file called tcp_offload.c, thus make this similar to tcpv6_offload.c. While moving all related functions into tcp_offload.c, we can also make some of them static, since they are only used there. Also, add an explicit registration function. Suggested-by: Eric Dumazet Signed-off-by: Daniel Borkmann Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/tcp.h | 9 +- net/ipv4/Makefile | 2 +- net/ipv4/af_inet.c | 13 +- net/ipv4/tcp.c | 241 ----------------------------------- net/ipv4/tcp_ipv4.c | 66 +--------- net/ipv4/tcp_offload.c | 332 +++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 341 insertions(+), 322 deletions(-) create mode 100644 net/ipv4/tcp_offload.c diff --git a/include/net/tcp.h b/include/net/tcp.h index bf1cc3dced5e..0d637e9403a5 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1541,15 +1541,14 @@ extern struct request_sock_ops tcp6_request_sock_ops; extern void tcp_v4_destroy_sock(struct sock *sk); -extern int tcp_v4_gso_send_check(struct sk_buff *skb); extern struct sk_buff *tcp_tso_segment(struct sk_buff *skb, netdev_features_t features); extern struct sk_buff **tcp_gro_receive(struct sk_buff **head, struct sk_buff *skb); -extern struct sk_buff **tcp4_gro_receive(struct sk_buff **head, - struct sk_buff *skb); extern int tcp_gro_complete(struct sk_buff *skb); -extern int tcp4_gro_complete(struct sk_buff *skb); + +extern void __tcp_v4_send_check(struct sk_buff *skb, __be32 saddr, + __be32 daddr); #ifdef CONFIG_PROC_FS extern int tcp4_proc_init(void); @@ -1584,6 +1583,8 @@ struct tcp_request_sock_ops { #endif }; +extern int tcpv4_offload_init(void); + extern void tcp_v4_init(void); extern void tcp_init(void); diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile index 089cb9f36387..4d3e138c564f 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile @@ -8,7 +8,7 @@ obj-y := route.o inetpeer.o protocol.o \ inet_timewait_sock.o inet_connection_sock.o \ tcp.o tcp_input.o tcp_output.o tcp_timer.o tcp_ipv4.o \ tcp_minisocks.o tcp_cong.o tcp_metrics.o tcp_fastopen.o \ - datagram.o raw.o udp.o udplite.o \ + tcp_offload.o datagram.o raw.o udp.o udplite.o \ arp.o icmp.o devinet.o af_inet.o igmp.o \ fib_frontend.o fib_semantics.o fib_trie.o \ inet_fragment.o ping.o diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 9c090c7daea1..7b514290efc6 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1559,15 +1559,6 @@ static const struct net_protocol tcp_protocol = { .netns_ok = 1, }; -static const struct net_offload tcp_offload = { - .callbacks = { - .gso_send_check = tcp_v4_gso_send_check, - .gso_segment = tcp_tso_segment, - .gro_receive = tcp4_gro_receive, - .gro_complete = tcp4_gro_complete, - }, -}; - static const struct net_protocol udp_protocol = { .handler = udp_rcv, .err_handler = udp_err, @@ -1683,8 +1674,8 @@ static int __init ipv4_offload_init(void) */ if (inet_add_offload(&udp_offload, IPPROTO_UDP) < 0) pr_crit("%s: Cannot add UDP protocol offload\n", __func__); - if (inet_add_offload(&tcp_offload, IPPROTO_TCP) < 0) - pr_crit("%s: Cannot add TCP protocol offlaod\n", __func__); + if (tcpv4_offload_init() < 0) + pr_crit("%s: Cannot add TCP protocol offload\n", __func__); dev_add_offload(&ip_packet_offload); return 0; diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 6a1cf95abc98..bc4246940f6c 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2877,247 +2877,6 @@ int compat_tcp_getsockopt(struct sock *sk, int level, int optname, EXPORT_SYMBOL(compat_tcp_getsockopt); #endif -struct sk_buff *tcp_tso_segment(struct sk_buff *skb, - netdev_features_t features) -{ - struct sk_buff *segs = ERR_PTR(-EINVAL); - struct tcphdr *th; - unsigned int thlen; - unsigned int seq; - __be32 delta; - unsigned int oldlen; - unsigned int mss; - struct sk_buff *gso_skb = skb; - __sum16 newcheck; - bool ooo_okay, copy_destructor; - - if (!pskb_may_pull(skb, sizeof(*th))) - goto out; - - th = tcp_hdr(skb); - thlen = th->doff * 4; - if (thlen < sizeof(*th)) - goto out; - - if (!pskb_may_pull(skb, thlen)) - goto out; - - oldlen = (u16)~skb->len; - __skb_pull(skb, thlen); - - mss = tcp_skb_mss(skb); - if (unlikely(skb->len <= mss)) - goto out; - - if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) { - /* Packet is from an untrusted source, reset gso_segs. */ - int type = skb_shinfo(skb)->gso_type; - - if (unlikely(type & - ~(SKB_GSO_TCPV4 | - SKB_GSO_DODGY | - SKB_GSO_TCP_ECN | - SKB_GSO_TCPV6 | - SKB_GSO_GRE | - SKB_GSO_MPLS | - SKB_GSO_UDP_TUNNEL | - 0) || - !(type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)))) - goto out; - - skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss); - - segs = NULL; - goto out; - } - - copy_destructor = gso_skb->destructor == tcp_wfree; - ooo_okay = gso_skb->ooo_okay; - /* All segments but the first should have ooo_okay cleared */ - skb->ooo_okay = 0; - - segs = skb_segment(skb, features); - if (IS_ERR(segs)) - goto out; - - /* Only first segment might have ooo_okay set */ - segs->ooo_okay = ooo_okay; - - delta = htonl(oldlen + (thlen + mss)); - - skb = segs; - th = tcp_hdr(skb); - seq = ntohl(th->seq); - - newcheck = ~csum_fold((__force __wsum)((__force u32)th->check + - (__force u32)delta)); - - do { - th->fin = th->psh = 0; - th->check = newcheck; - - if (skb->ip_summed != CHECKSUM_PARTIAL) - th->check = - csum_fold(csum_partial(skb_transport_header(skb), - thlen, skb->csum)); - - seq += mss; - if (copy_destructor) { - skb->destructor = gso_skb->destructor; - skb->sk = gso_skb->sk; - /* {tcp|sock}_wfree() use exact truesize accounting : - * sum(skb->truesize) MUST be exactly be gso_skb->truesize - * So we account mss bytes of 'true size' for each segment. - * The last segment will contain the remaining. - */ - skb->truesize = mss; - gso_skb->truesize -= mss; - } - skb = skb->next; - th = tcp_hdr(skb); - - th->seq = htonl(seq); - th->cwr = 0; - } while (skb->next); - - /* Following permits TCP Small Queues to work well with GSO : - * The callback to TCP stack will be called at the time last frag - * is freed at TX completion, and not right now when gso_skb - * is freed by GSO engine - */ - if (copy_destructor) { - swap(gso_skb->sk, skb->sk); - swap(gso_skb->destructor, skb->destructor); - swap(gso_skb->truesize, skb->truesize); - } - - delta = htonl(oldlen + (skb_tail_pointer(skb) - - skb_transport_header(skb)) + - skb->data_len); - th->check = ~csum_fold((__force __wsum)((__force u32)th->check + - (__force u32)delta)); - if (skb->ip_summed != CHECKSUM_PARTIAL) - th->check = csum_fold(csum_partial(skb_transport_header(skb), - thlen, skb->csum)); - -out: - return segs; -} -EXPORT_SYMBOL(tcp_tso_segment); - -struct sk_buff **tcp_gro_receive(struct sk_buff **head, struct sk_buff *skb) -{ - struct sk_buff **pp = NULL; - struct sk_buff *p; - struct tcphdr *th; - struct tcphdr *th2; - unsigned int len; - unsigned int thlen; - __be32 flags; - unsigned int mss = 1; - unsigned int hlen; - unsigned int off; - int flush = 1; - int i; - - off = skb_gro_offset(skb); - hlen = off + sizeof(*th); - th = skb_gro_header_fast(skb, off); - if (skb_gro_header_hard(skb, hlen)) { - th = skb_gro_header_slow(skb, hlen, off); - if (unlikely(!th)) - goto out; - } - - thlen = th->doff * 4; - if (thlen < sizeof(*th)) - goto out; - - hlen = off + thlen; - if (skb_gro_header_hard(skb, hlen)) { - th = skb_gro_header_slow(skb, hlen, off); - if (unlikely(!th)) - goto out; - } - - skb_gro_pull(skb, thlen); - - len = skb_gro_len(skb); - flags = tcp_flag_word(th); - - for (; (p = *head); head = &p->next) { - if (!NAPI_GRO_CB(p)->same_flow) - continue; - - th2 = tcp_hdr(p); - - if (*(u32 *)&th->source ^ *(u32 *)&th2->source) { - NAPI_GRO_CB(p)->same_flow = 0; - continue; - } - - goto found; - } - - goto out_check_final; - -found: - flush = NAPI_GRO_CB(p)->flush; - flush |= (__force int)(flags & TCP_FLAG_CWR); - flush |= (__force int)((flags ^ tcp_flag_word(th2)) & - ~(TCP_FLAG_CWR | TCP_FLAG_FIN | TCP_FLAG_PSH)); - flush |= (__force int)(th->ack_seq ^ th2->ack_seq); - for (i = sizeof(*th); i < thlen; i += 4) - flush |= *(u32 *)((u8 *)th + i) ^ - *(u32 *)((u8 *)th2 + i); - - mss = tcp_skb_mss(p); - - flush |= (len - 1) >= mss; - flush |= (ntohl(th2->seq) + skb_gro_len(p)) ^ ntohl(th->seq); - - if (flush || skb_gro_receive(head, skb)) { - mss = 1; - goto out_check_final; - } - - p = *head; - th2 = tcp_hdr(p); - tcp_flag_word(th2) |= flags & (TCP_FLAG_FIN | TCP_FLAG_PSH); - -out_check_final: - flush = len < mss; - flush |= (__force int)(flags & (TCP_FLAG_URG | TCP_FLAG_PSH | - TCP_FLAG_RST | TCP_FLAG_SYN | - TCP_FLAG_FIN)); - - if (p && (!NAPI_GRO_CB(skb)->same_flow || flush)) - pp = head; - -out: - NAPI_GRO_CB(skb)->flush |= flush; - - return pp; -} -EXPORT_SYMBOL(tcp_gro_receive); - -int tcp_gro_complete(struct sk_buff *skb) -{ - struct tcphdr *th = tcp_hdr(skb); - - skb->csum_start = skb_transport_header(skb) - skb->head; - skb->csum_offset = offsetof(struct tcphdr, check); - skb->ip_summed = CHECKSUM_PARTIAL; - - skb_shinfo(skb)->gso_segs = NAPI_GRO_CB(skb)->count; - - if (th->cwr) - skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ECN; - - return 0; -} -EXPORT_SYMBOL(tcp_gro_complete); - #ifdef CONFIG_TCP_MD5SIG static struct tcp_md5sig_pool __percpu *tcp_md5sig_pool __read_mostly; static DEFINE_MUTEX(tcp_md5sig_mutex); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index d20ede0c9593..289039b4d8de 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -545,8 +545,7 @@ out: sock_put(sk); } -static void __tcp_v4_send_check(struct sk_buff *skb, - __be32 saddr, __be32 daddr) +void __tcp_v4_send_check(struct sk_buff *skb, __be32 saddr, __be32 daddr) { struct tcphdr *th = tcp_hdr(skb); @@ -571,23 +570,6 @@ void tcp_v4_send_check(struct sock *sk, struct sk_buff *skb) } EXPORT_SYMBOL(tcp_v4_send_check); -int tcp_v4_gso_send_check(struct sk_buff *skb) -{ - const struct iphdr *iph; - struct tcphdr *th; - - if (!pskb_may_pull(skb, sizeof(*th))) - return -EINVAL; - - iph = ip_hdr(skb); - th = tcp_hdr(skb); - - th->check = 0; - skb->ip_summed = CHECKSUM_PARTIAL; - __tcp_v4_send_check(skb, iph->saddr, iph->daddr); - return 0; -} - /* * This routine will send an RST to the other tcp. * @@ -2795,52 +2777,6 @@ void tcp4_proc_exit(void) } #endif /* CONFIG_PROC_FS */ -struct sk_buff **tcp4_gro_receive(struct sk_buff **head, struct sk_buff *skb) -{ - const struct iphdr *iph = skb_gro_network_header(skb); - __wsum wsum; - __sum16 sum; - - switch (skb->ip_summed) { - case CHECKSUM_COMPLETE: - if (!tcp_v4_check(skb_gro_len(skb), iph->saddr, iph->daddr, - skb->csum)) { - skb->ip_summed = CHECKSUM_UNNECESSARY; - break; - } -flush: - NAPI_GRO_CB(skb)->flush = 1; - return NULL; - - case CHECKSUM_NONE: - wsum = csum_tcpudp_nofold(iph->saddr, iph->daddr, - skb_gro_len(skb), IPPROTO_TCP, 0); - sum = csum_fold(skb_checksum(skb, - skb_gro_offset(skb), - skb_gro_len(skb), - wsum)); - if (sum) - goto flush; - - skb->ip_summed = CHECKSUM_UNNECESSARY; - break; - } - - return tcp_gro_receive(head, skb); -} - -int tcp4_gro_complete(struct sk_buff *skb) -{ - const struct iphdr *iph = ip_hdr(skb); - struct tcphdr *th = tcp_hdr(skb); - - th->check = ~tcp_v4_check(skb->len - skb_transport_offset(skb), - iph->saddr, iph->daddr, 0); - skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4; - - return tcp_gro_complete(skb); -} - struct proto tcp_prot = { .name = "TCP", .owner = THIS_MODULE, diff --git a/net/ipv4/tcp_offload.c b/net/ipv4/tcp_offload.c new file mode 100644 index 000000000000..3a7525e6c086 --- /dev/null +++ b/net/ipv4/tcp_offload.c @@ -0,0 +1,332 @@ +/* + * IPV4 GSO/GRO offload support + * Linux INET implementation + * + * 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. + * + * TCPv4 GSO/GRO support + */ + +#include +#include +#include + +struct sk_buff *tcp_tso_segment(struct sk_buff *skb, + netdev_features_t features) +{ + struct sk_buff *segs = ERR_PTR(-EINVAL); + struct tcphdr *th; + unsigned int thlen; + unsigned int seq; + __be32 delta; + unsigned int oldlen; + unsigned int mss; + struct sk_buff *gso_skb = skb; + __sum16 newcheck; + bool ooo_okay, copy_destructor; + + if (!pskb_may_pull(skb, sizeof(*th))) + goto out; + + th = tcp_hdr(skb); + thlen = th->doff * 4; + if (thlen < sizeof(*th)) + goto out; + + if (!pskb_may_pull(skb, thlen)) + goto out; + + oldlen = (u16)~skb->len; + __skb_pull(skb, thlen); + + mss = tcp_skb_mss(skb); + if (unlikely(skb->len <= mss)) + goto out; + + if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) { + /* Packet is from an untrusted source, reset gso_segs. */ + int type = skb_shinfo(skb)->gso_type; + + if (unlikely(type & + ~(SKB_GSO_TCPV4 | + SKB_GSO_DODGY | + SKB_GSO_TCP_ECN | + SKB_GSO_TCPV6 | + SKB_GSO_GRE | + SKB_GSO_MPLS | + SKB_GSO_UDP_TUNNEL | + 0) || + !(type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)))) + goto out; + + skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss); + + segs = NULL; + goto out; + } + + copy_destructor = gso_skb->destructor == tcp_wfree; + ooo_okay = gso_skb->ooo_okay; + /* All segments but the first should have ooo_okay cleared */ + skb->ooo_okay = 0; + + segs = skb_segment(skb, features); + if (IS_ERR(segs)) + goto out; + + /* Only first segment might have ooo_okay set */ + segs->ooo_okay = ooo_okay; + + delta = htonl(oldlen + (thlen + mss)); + + skb = segs; + th = tcp_hdr(skb); + seq = ntohl(th->seq); + + newcheck = ~csum_fold((__force __wsum)((__force u32)th->check + + (__force u32)delta)); + + do { + th->fin = th->psh = 0; + th->check = newcheck; + + if (skb->ip_summed != CHECKSUM_PARTIAL) + th->check = + csum_fold(csum_partial(skb_transport_header(skb), + thlen, skb->csum)); + + seq += mss; + if (copy_destructor) { + skb->destructor = gso_skb->destructor; + skb->sk = gso_skb->sk; + /* {tcp|sock}_wfree() use exact truesize accounting : + * sum(skb->truesize) MUST be exactly be gso_skb->truesize + * So we account mss bytes of 'true size' for each segment. + * The last segment will contain the remaining. + */ + skb->truesize = mss; + gso_skb->truesize -= mss; + } + skb = skb->next; + th = tcp_hdr(skb); + + th->seq = htonl(seq); + th->cwr = 0; + } while (skb->next); + + /* Following permits TCP Small Queues to work well with GSO : + * The callback to TCP stack will be called at the time last frag + * is freed at TX completion, and not right now when gso_skb + * is freed by GSO engine + */ + if (copy_destructor) { + swap(gso_skb->sk, skb->sk); + swap(gso_skb->destructor, skb->destructor); + swap(gso_skb->truesize, skb->truesize); + } + + delta = htonl(oldlen + (skb_tail_pointer(skb) - + skb_transport_header(skb)) + + skb->data_len); + th->check = ~csum_fold((__force __wsum)((__force u32)th->check + + (__force u32)delta)); + if (skb->ip_summed != CHECKSUM_PARTIAL) + th->check = csum_fold(csum_partial(skb_transport_header(skb), + thlen, skb->csum)); +out: + return segs; +} +EXPORT_SYMBOL(tcp_tso_segment); + +struct sk_buff **tcp_gro_receive(struct sk_buff **head, struct sk_buff *skb) +{ + struct sk_buff **pp = NULL; + struct sk_buff *p; + struct tcphdr *th; + struct tcphdr *th2; + unsigned int len; + unsigned int thlen; + __be32 flags; + unsigned int mss = 1; + unsigned int hlen; + unsigned int off; + int flush = 1; + int i; + + off = skb_gro_offset(skb); + hlen = off + sizeof(*th); + th = skb_gro_header_fast(skb, off); + if (skb_gro_header_hard(skb, hlen)) { + th = skb_gro_header_slow(skb, hlen, off); + if (unlikely(!th)) + goto out; + } + + thlen = th->doff * 4; + if (thlen < sizeof(*th)) + goto out; + + hlen = off + thlen; + if (skb_gro_header_hard(skb, hlen)) { + th = skb_gro_header_slow(skb, hlen, off); + if (unlikely(!th)) + goto out; + } + + skb_gro_pull(skb, thlen); + + len = skb_gro_len(skb); + flags = tcp_flag_word(th); + + for (; (p = *head); head = &p->next) { + if (!NAPI_GRO_CB(p)->same_flow) + continue; + + th2 = tcp_hdr(p); + + if (*(u32 *)&th->source ^ *(u32 *)&th2->source) { + NAPI_GRO_CB(p)->same_flow = 0; + continue; + } + + goto found; + } + + goto out_check_final; + +found: + flush = NAPI_GRO_CB(p)->flush; + flush |= (__force int)(flags & TCP_FLAG_CWR); + flush |= (__force int)((flags ^ tcp_flag_word(th2)) & + ~(TCP_FLAG_CWR | TCP_FLAG_FIN | TCP_FLAG_PSH)); + flush |= (__force int)(th->ack_seq ^ th2->ack_seq); + for (i = sizeof(*th); i < thlen; i += 4) + flush |= *(u32 *)((u8 *)th + i) ^ + *(u32 *)((u8 *)th2 + i); + + mss = tcp_skb_mss(p); + + flush |= (len - 1) >= mss; + flush |= (ntohl(th2->seq) + skb_gro_len(p)) ^ ntohl(th->seq); + + if (flush || skb_gro_receive(head, skb)) { + mss = 1; + goto out_check_final; + } + + p = *head; + th2 = tcp_hdr(p); + tcp_flag_word(th2) |= flags & (TCP_FLAG_FIN | TCP_FLAG_PSH); + +out_check_final: + flush = len < mss; + flush |= (__force int)(flags & (TCP_FLAG_URG | TCP_FLAG_PSH | + TCP_FLAG_RST | TCP_FLAG_SYN | + TCP_FLAG_FIN)); + + if (p && (!NAPI_GRO_CB(skb)->same_flow || flush)) + pp = head; + +out: + NAPI_GRO_CB(skb)->flush |= flush; + + return pp; +} +EXPORT_SYMBOL(tcp_gro_receive); + +int tcp_gro_complete(struct sk_buff *skb) +{ + struct tcphdr *th = tcp_hdr(skb); + + skb->csum_start = skb_transport_header(skb) - skb->head; + skb->csum_offset = offsetof(struct tcphdr, check); + skb->ip_summed = CHECKSUM_PARTIAL; + + skb_shinfo(skb)->gso_segs = NAPI_GRO_CB(skb)->count; + + if (th->cwr) + skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ECN; + + return 0; +} +EXPORT_SYMBOL(tcp_gro_complete); + +static int tcp_v4_gso_send_check(struct sk_buff *skb) +{ + const struct iphdr *iph; + struct tcphdr *th; + + if (!pskb_may_pull(skb, sizeof(*th))) + return -EINVAL; + + iph = ip_hdr(skb); + th = tcp_hdr(skb); + + th->check = 0; + skb->ip_summed = CHECKSUM_PARTIAL; + __tcp_v4_send_check(skb, iph->saddr, iph->daddr); + return 0; +} + +static struct sk_buff **tcp4_gro_receive(struct sk_buff **head, struct sk_buff *skb) +{ + const struct iphdr *iph = skb_gro_network_header(skb); + __wsum wsum; + __sum16 sum; + + switch (skb->ip_summed) { + case CHECKSUM_COMPLETE: + if (!tcp_v4_check(skb_gro_len(skb), iph->saddr, iph->daddr, + skb->csum)) { + skb->ip_summed = CHECKSUM_UNNECESSARY; + break; + } +flush: + NAPI_GRO_CB(skb)->flush = 1; + return NULL; + + case CHECKSUM_NONE: + wsum = csum_tcpudp_nofold(iph->saddr, iph->daddr, + skb_gro_len(skb), IPPROTO_TCP, 0); + sum = csum_fold(skb_checksum(skb, + skb_gro_offset(skb), + skb_gro_len(skb), + wsum)); + if (sum) + goto flush; + + skb->ip_summed = CHECKSUM_UNNECESSARY; + break; + } + + return tcp_gro_receive(head, skb); +} + +static int tcp4_gro_complete(struct sk_buff *skb) +{ + const struct iphdr *iph = ip_hdr(skb); + struct tcphdr *th = tcp_hdr(skb); + + th->check = ~tcp_v4_check(skb->len - skb_transport_offset(skb), + iph->saddr, iph->daddr, 0); + skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4; + + return tcp_gro_complete(skb); +} + +static const struct net_offload tcpv4_offload = { + .callbacks = { + .gso_send_check = tcp_v4_gso_send_check, + .gso_segment = tcp_tso_segment, + .gro_receive = tcp4_gro_receive, + .gro_complete = tcp4_gro_complete, + }, +}; + +int __init tcpv4_offload_init(void) +{ + return inet_add_offload(&tcpv4_offload, IPPROTO_TCP); +} -- cgit v1.2.3 From ca162a82f56921442f5db72a3a472010e5a62c4b Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 7 Jun 2013 10:48:00 +0000 Subject: fec: Only pass pdev in fec_ptp_init() Passing pdev in fec_ptp_init() is enough, since we can get ndev locally. Signed-off-by: Fabio Estevam Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fec.h | 2 +- drivers/net/ethernet/freescale/fec_main.c | 2 +- drivers/net/ethernet/freescale/fec_ptp.c | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h index b11cdbcc04ff..e3ed6c5ae801 100644 --- a/drivers/net/ethernet/freescale/fec.h +++ b/drivers/net/ethernet/freescale/fec.h @@ -275,7 +275,7 @@ struct fec_enet_private { struct regulator *reg_phy; }; -void fec_ptp_init(struct net_device *ndev, struct platform_device *pdev); +void fec_ptp_init(struct platform_device *pdev); void fec_ptp_start_cyclecounter(struct net_device *ndev); int fec_ptp_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd); diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index c42c42a0cc57..fa4a68fc01cb 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -1945,7 +1945,7 @@ fec_probe(struct platform_device *pdev) fec_reset_phy(pdev); if (fep->bufdesc_ex) - fec_ptp_init(ndev, pdev); + fec_ptp_init(pdev); ret = fec_enet_init(ndev); if (ret) diff --git a/drivers/net/ethernet/freescale/fec_ptp.c b/drivers/net/ethernet/freescale/fec_ptp.c index 25fc960cbf0e..5007e4f9fff9 100644 --- a/drivers/net/ethernet/freescale/fec_ptp.c +++ b/drivers/net/ethernet/freescale/fec_ptp.c @@ -347,8 +347,9 @@ static void fec_time_keep(unsigned long _data) * cyclecounter init routine and exits. */ -void fec_ptp_init(struct net_device *ndev, struct platform_device *pdev) +void fec_ptp_init(struct platform_device *pdev) { + struct net_device *ndev = platform_get_drvdata(pdev); struct fec_enet_private *fep = netdev_priv(ndev); fep->ptp_caps.owner = THIS_MODULE; -- cgit v1.2.3 From 303d1cbf610eef31e039efc2e8da30cf94cf5ebc Mon Sep 17 00:00:00 2001 From: Jay Vosburgh Date: Fri, 31 May 2013 11:57:30 +0000 Subject: bonding: Convert hw addr handling to sync/unsync, support ucast addresses This patch converts bonding to use the dev_uc/mc_sync and dev_uc/mc_sync_multiple functions for updating the hardware addresses of bonding slaves. The existing functions to add or remove addresses are removed, and their functionality is replaced with calls to dev_mc_sync or dev_mc_sync_multiple, depending upon the bonding mode. Calls to dev_uc_sync and dev_uc_sync_multiple are also added, so that unicast addresses added to a bond will be properly synced with its slaves. Various functions are renamed to better reflect the new situation, and relevant comments are updated. Signed-off-by: Jay Vosburgh Cc: Vlad Yasevich Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 168 ++++++++++------------------------------ drivers/net/bonding/bonding.h | 1 - 2 files changed, 43 insertions(+), 126 deletions(-) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index f4489d65bf33..4953f66a29a4 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -706,45 +706,6 @@ static int bond_set_allmulti(struct bonding *bond, int inc) return err; } -/* - * Add a Multicast address to slaves - * according to mode - */ -static void bond_mc_add(struct bonding *bond, void *addr) -{ - if (USES_PRIMARY(bond->params.mode)) { - /* write lock already acquired */ - if (bond->curr_active_slave) - dev_mc_add(bond->curr_active_slave->dev, addr); - } else { - struct slave *slave; - int i; - - bond_for_each_slave(bond, slave, i) - dev_mc_add(slave->dev, addr); - } -} - -/* - * Remove a multicast address from slave - * according to mode - */ -static void bond_mc_del(struct bonding *bond, void *addr) -{ - if (USES_PRIMARY(bond->params.mode)) { - /* write lock already acquired */ - if (bond->curr_active_slave) - dev_mc_del(bond->curr_active_slave->dev, addr); - } else { - struct slave *slave; - int i; - bond_for_each_slave(bond, slave, i) { - dev_mc_del(slave->dev, addr); - } - } -} - - static void __bond_resend_igmp_join_requests(struct net_device *dev) { struct in_device *in_dev; @@ -803,17 +764,15 @@ static void bond_resend_igmp_join_requests_delayed(struct work_struct *work) bond_resend_igmp_join_requests(bond); } -/* - * flush all members of flush->mc_list from device dev->mc_list +/* Flush bond's hardware addresses from slave */ -static void bond_mc_list_flush(struct net_device *bond_dev, +static void bond_hw_addr_flush(struct net_device *bond_dev, struct net_device *slave_dev) { struct bonding *bond = netdev_priv(bond_dev); - struct netdev_hw_addr *ha; - netdev_for_each_mc_addr(ha, bond_dev) - dev_mc_del(slave_dev, ha->addr); + dev_uc_unsync(slave_dev, bond_dev); + dev_mc_unsync(slave_dev, bond_dev); if (bond->params.mode == BOND_MODE_8023AD) { /* del lacpdu mc addr from mc list */ @@ -825,22 +784,14 @@ static void bond_mc_list_flush(struct net_device *bond_dev, /*--------------------------- Active slave change ---------------------------*/ -/* - * Update the mc list and multicast-related flags for the new and - * old active slaves (if any) according to the multicast mode, and - * promiscuous flags unconditionally. +/* Update the hardware address list and promisc/allmulti for the new and + * old active slaves (if any). Modes that are !USES_PRIMARY keep all + * slaves up date at all times; only the USES_PRIMARY modes need to call + * this function to swap these settings during a failover. */ -static void bond_mc_swap(struct bonding *bond, struct slave *new_active, - struct slave *old_active) +static void bond_hw_addr_swap(struct bonding *bond, struct slave *new_active, + struct slave *old_active) { - struct netdev_hw_addr *ha; - - if (!USES_PRIMARY(bond->params.mode)) - /* nothing to do - mc list is already up-to-date on - * all slaves - */ - return; - if (old_active) { if (bond->dev->flags & IFF_PROMISC) dev_set_promiscuity(old_active->dev, -1); @@ -848,10 +799,7 @@ static void bond_mc_swap(struct bonding *bond, struct slave *new_active, if (bond->dev->flags & IFF_ALLMULTI) dev_set_allmulti(old_active->dev, -1); - netif_addr_lock_bh(bond->dev); - netdev_for_each_mc_addr(ha, bond->dev) - dev_mc_del(old_active->dev, ha->addr); - netif_addr_unlock_bh(bond->dev); + bond_hw_addr_flush(bond->dev, old_active->dev); } if (new_active) { @@ -863,8 +811,8 @@ static void bond_mc_swap(struct bonding *bond, struct slave *new_active, dev_set_allmulti(new_active->dev, 1); netif_addr_lock_bh(bond->dev); - netdev_for_each_mc_addr(ha, bond->dev) - dev_mc_add(new_active->dev, ha->addr); + dev_uc_sync(new_active->dev, bond->dev); + dev_mc_sync(new_active->dev, bond->dev); netif_addr_unlock_bh(bond->dev); } } @@ -1083,7 +1031,7 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active) } if (USES_PRIMARY(bond->params.mode)) - bond_mc_swap(bond, new_active, old_active); + bond_hw_addr_swap(bond, new_active, old_active); if (bond_is_lb(bond)) { bond_alb_handle_active_change(bond, new_active); @@ -1526,7 +1474,6 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) struct bonding *bond = netdev_priv(bond_dev); const struct net_device_ops *slave_ops = slave_dev->netdev_ops; struct slave *new_slave = NULL; - struct netdev_hw_addr *ha; struct sockaddr addr; int link_reporting; int res = 0; @@ -1706,10 +1653,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) goto err_close; } - /* If the mode USES_PRIMARY, then the new slave gets the - * master's promisc (and mc) settings only if it becomes the - * curr_active_slave, and that is taken care of later when calling - * bond_change_active() + /* If the mode USES_PRIMARY, then the following is handled by + * bond_change_active_slave(). */ if (!USES_PRIMARY(bond->params.mode)) { /* set promiscuity level to new slave */ @@ -1727,9 +1672,10 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) } netif_addr_lock_bh(bond_dev); - /* upload master's mc_list to new slave */ - netdev_for_each_mc_addr(ha, bond_dev) - dev_mc_add(slave_dev, ha->addr); + + dev_mc_sync_multiple(slave_dev, bond_dev); + dev_uc_sync_multiple(slave_dev, bond_dev); + netif_addr_unlock_bh(bond_dev); } @@ -1908,11 +1854,9 @@ err_dest_symlinks: bond_destroy_slave_symlinks(bond_dev, slave_dev); err_detach: - if (!USES_PRIMARY(bond->params.mode)) { - netif_addr_lock_bh(bond_dev); - bond_mc_list_flush(bond_dev, slave_dev); - netif_addr_unlock_bh(bond_dev); - } + if (!USES_PRIMARY(bond->params.mode)) + bond_hw_addr_flush(bond_dev, slave_dev); + bond_del_vlans_from_slave(bond, slave_dev); write_lock_bh(&bond->lock); bond_detach_slave(bond, new_slave); @@ -2107,9 +2051,8 @@ static int __bond_release_one(struct net_device *bond_dev, bond_del_vlans_from_slave(bond, slave_dev); - /* If the mode USES_PRIMARY, then we should only remove its - * promisc and mc settings if it was the curr_active_slave, but that was - * already taken care of above when we detached the slave + /* If the mode USES_PRIMARY, then this cases was handled above by + * bond_change_active_slave(..., NULL) */ if (!USES_PRIMARY(bond->params.mode)) { /* unset promiscuity level from slave */ @@ -2120,10 +2063,7 @@ static int __bond_release_one(struct net_device *bond_dev, if (bond_dev->flags & IFF_ALLMULTI) dev_set_allmulti(slave_dev, -1); - /* flush master's mc_list from slave */ - netif_addr_lock_bh(bond_dev); - bond_mc_list_flush(bond_dev, slave_dev); - netif_addr_unlock_bh(bond_dev); + bond_hw_addr_flush(bond_dev, slave_dev); } bond_upper_dev_unlink(bond_dev, slave_dev); @@ -3660,19 +3600,6 @@ static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd return res; } -static bool bond_addr_in_mc_list(unsigned char *addr, - struct netdev_hw_addr_list *list, - int addrlen) -{ - struct netdev_hw_addr *ha; - - netdev_hw_addr_list_for_each(ha, list) - if (!memcmp(ha->addr, addr, addrlen)) - return true; - - return false; -} - static void bond_change_rx_flags(struct net_device *bond_dev, int change) { struct bonding *bond = netdev_priv(bond_dev); @@ -3686,35 +3613,29 @@ static void bond_change_rx_flags(struct net_device *bond_dev, int change) bond_dev->flags & IFF_ALLMULTI ? 1 : -1); } -static void bond_set_multicast_list(struct net_device *bond_dev) +static void bond_set_rx_mode(struct net_device *bond_dev) { struct bonding *bond = netdev_priv(bond_dev); - struct netdev_hw_addr *ha; - bool found; + struct slave *slave; + int i; read_lock(&bond->lock); - /* looking for addresses to add to slaves' mc list */ - netdev_for_each_mc_addr(ha, bond_dev) { - found = bond_addr_in_mc_list(ha->addr, &bond->mc_list, - bond_dev->addr_len); - if (!found) - bond_mc_add(bond, ha->addr); - } - - /* looking for addresses to delete from slaves' list */ - netdev_hw_addr_list_for_each(ha, &bond->mc_list) { - found = bond_addr_in_mc_list(ha->addr, &bond_dev->mc, - bond_dev->addr_len); - if (!found) - bond_mc_del(bond, ha->addr); + if (USES_PRIMARY(bond->params.mode)) { + read_lock(&bond->curr_slave_lock); + slave = bond->curr_active_slave; + if (slave) { + dev_uc_sync(slave->dev, bond_dev); + dev_mc_sync(slave->dev, bond_dev); + } + read_unlock(&bond->curr_slave_lock); + } else { + bond_for_each_slave(bond, slave, i) { + dev_uc_sync_multiple(slave->dev, bond_dev); + dev_mc_sync_multiple(slave->dev, bond_dev); + } } - /* save master's multicast list */ - __hw_addr_flush(&bond->mc_list); - __hw_addr_add_multiple(&bond->mc_list, &bond_dev->mc, - bond_dev->addr_len, NETDEV_HW_ADDR_T_MULTICAST); - read_unlock(&bond->lock); } @@ -4321,7 +4242,7 @@ static const struct net_device_ops bond_netdev_ops = { .ndo_get_stats64 = bond_get_stats, .ndo_do_ioctl = bond_do_ioctl, .ndo_change_rx_flags = bond_change_rx_flags, - .ndo_set_rx_mode = bond_set_multicast_list, + .ndo_set_rx_mode = bond_set_rx_mode, .ndo_change_mtu = bond_change_mtu, .ndo_set_mac_address = bond_set_mac_address, .ndo_neigh_setup = bond_neigh_setup, @@ -4426,8 +4347,6 @@ static void bond_uninit(struct net_device *bond_dev) bond_debug_unregister(bond); - __hw_addr_flush(&bond->mc_list); - list_for_each_entry_safe(vlan, tmp, &bond->vlan_list, vlan_list) { list_del(&vlan->vlan_list); kfree(vlan); @@ -4838,7 +4757,6 @@ static int bond_init(struct net_device *bond_dev) bond->dev_addr_from_first = true; } - __hw_addr_init(&bond->mc_list); return 0; } diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h index 2baec24388b1..b38609b967b7 100644 --- a/drivers/net/bonding/bonding.h +++ b/drivers/net/bonding/bonding.h @@ -231,7 +231,6 @@ struct bonding { char proc_file_name[IFNAMSIZ]; #endif /* CONFIG_PROC_FS */ struct list_head bond_list; - struct netdev_hw_addr_list mc_list; int (*xmit_hash_policy)(struct sk_buff *, int); u16 rr_tx_counter; struct ad_bond_info ad_info; -- cgit v1.2.3 From 1b5acd292336da029535de010af568533df9b665 Mon Sep 17 00:00:00 2001 From: Jay Vosburgh Date: Fri, 31 May 2013 11:57:31 +0000 Subject: bonding: disallow change of MAC if fail_over_mac enabled Currently, if fail_over_mac is set to active, then attempts to change the MAC of the bond itself silently fail. However, if fail_over_mac is set to follow, changes are permitted. Permitting the bond's MAC to change with fail_over_mac=follow will disrupt the follow functionality, which normally controls the assignment of MAC address to the bond and its slaves, and can cause multiple ports to be assigned the same MAC address. which will interfere with the functioning of the device (where the device here is a virtualization-aware card for s390, qeth). Signed-off-by: Jay Vosburgh Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 4953f66a29a4..bc1246f6f86a 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -3780,11 +3780,10 @@ static int bond_set_mac_address(struct net_device *bond_dev, void *addr) pr_debug("bond=%p, name=%s\n", bond, bond_dev ? bond_dev->name : "None"); - /* - * If fail_over_mac is set to active, do nothing and return - * success. Returning an error causes ifenslave to fail. + /* If fail_over_mac is enabled, do nothing and return success. + * Returning an error causes ifenslave to fail. */ - if (bond->params.fail_over_mac == BOND_FOM_ACTIVE) + if (bond->params.fail_over_mac) return 0; if (!is_valid_ether_addr(sa->sa_data)) -- cgit v1.2.3 From 70efde2a2920c12f2b14eb640944ca7e61b2c02d Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Fri, 7 Jun 2013 16:16:51 -0600 Subject: PCI: Rename pci_release_bus_bridge_dev() to pci_release_host_bridge_dev() This renames pci_release_bus_bridge_dev() to pci_release_host_bridge_dev() and moves it next to pci_alloc_host_bridge(). No functional change. [bhelgaas: split rename & move out of create/destroy symmetry patch] Signed-off-by: Jiang Liu Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 15c39cb09619..a723b2b93ab4 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -468,6 +468,18 @@ static struct pci_bus * pci_alloc_bus(void) return b; } +static void pci_release_host_bridge_dev(struct device *dev) +{ + struct pci_host_bridge *bridge = to_pci_host_bridge(dev); + + if (bridge->release_fn) + bridge->release_fn(bridge); + + pci_free_resource_list(&bridge->windows); + + kfree(bridge); +} + static struct pci_host_bridge *pci_alloc_host_bridge(struct pci_bus *b) { struct pci_host_bridge *bridge; @@ -1189,18 +1201,6 @@ int pci_cfg_space_size(struct pci_dev *dev) return PCI_CFG_SPACE_SIZE; } -static void pci_release_bus_bridge_dev(struct device *dev) -{ - struct pci_host_bridge *bridge = to_pci_host_bridge(dev); - - if (bridge->release_fn) - bridge->release_fn(bridge); - - pci_free_resource_list(&bridge->windows); - - kfree(bridge); -} - struct pci_dev *pci_alloc_dev(struct pci_bus *bus) { struct pci_dev *dev; @@ -1708,7 +1708,7 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, goto err_out; bridge->dev.parent = parent; - bridge->dev.release = pci_release_bus_bridge_dev; + bridge->dev.release = pci_release_host_bridge_dev; dev_set_name(&bridge->dev, "pci%04x:%02x", pci_domain_nr(b), bus); error = pcibios_root_bridge_prepare(bridge); if (error) { -- cgit v1.2.3 From c05cdb1b864f548c0c3d8ae3b51264e6739a69b1 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 3 Jun 2013 09:46:28 +0000 Subject: netlink: allow large data transfers from user-space I can hit ENOBUFS in the sendmsg() path with a large batch that is composed of many netlink messages. Here that limit is 8 MBytes of skbuff data area as kmalloc does not manage to get more than that. While discussing atomic rule-set for nftables with Patrick McHardy, we decided to put all rule-set updates that need to be applied atomically in one single batch to simplify the existing approach. However, as explained above, the existing netlink code limits us to a maximum of ~20000 rules that fit in one single batch without hitting ENOBUFS. iptables does not have such limitation as it is using vmalloc. This patch adds netlink_alloc_large_skb() which is only used in the netlink_sendmsg() path. It uses alloc_skb if the memory requested is <= one memory page, that should be the common case for most subsystems, else vmalloc for higher memory allocations. Signed-off-by: Pablo Neira Ayuso Signed-off-by: David S. Miller --- net/netlink/af_netlink.c | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index d0b3dd60d386..68c167374394 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -750,6 +750,10 @@ static void netlink_skb_destructor(struct sk_buff *skb) skb->head = NULL; } #endif + if (is_vmalloc_addr(skb->head)) { + vfree(skb->head); + skb->head = NULL; + } if (skb->sk != NULL) sock_rfree(skb); } @@ -1420,6 +1424,35 @@ struct sock *netlink_getsockbyfilp(struct file *filp) return sock; } +static struct sk_buff *netlink_alloc_large_skb(unsigned int size) +{ + struct sk_buff *skb; + void *data; + + if (size <= NLMSG_GOODSIZE) + return alloc_skb(size, GFP_KERNEL); + + skb = alloc_skb_head(GFP_KERNEL); + if (skb == NULL) + return NULL; + + data = vmalloc(size); + if (data == NULL) + goto err; + + skb->head = data; + skb->data = data; + skb_reset_tail_pointer(skb); + skb->end = skb->tail + size; + skb->len = 0; + skb->destructor = netlink_skb_destructor; + + return skb; +err: + kfree_skb(skb); + return NULL; +} + /* * Attach a skb to a netlink socket. * The caller must hold a reference to the destination socket. On error, the @@ -1510,7 +1543,7 @@ static struct sk_buff *netlink_trim(struct sk_buff *skb, gfp_t allocation) return skb; delta = skb->end - skb->tail; - if (delta * 2 < skb->truesize) + if (is_vmalloc_addr(skb->head) || delta * 2 < skb->truesize) return skb; if (skb_shared(skb)) { @@ -2096,7 +2129,7 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock, if (len > sk->sk_sndbuf - 32) goto out; err = -ENOBUFS; - skb = alloc_skb(len, GFP_KERNEL); + skb = netlink_alloc_large_skb(len); if (skb == NULL) goto out; -- cgit v1.2.3 From fc8b14d338f6265533a8f82db1325fa112b530ba Mon Sep 17 00:00:00 2001 From: Thomas Meyer Date: Sat, 1 Jun 2013 11:39:36 +0200 Subject: eCryptfs: Cocci spatch "memdup.spatch" Signed-off-by: Thomas Meyer Signed-off-by: Tyler Hicks --- fs/ecryptfs/messaging.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/ecryptfs/messaging.c b/fs/ecryptfs/messaging.c index 49ff8ea08f1c..e57380e5f6bd 100644 --- a/fs/ecryptfs/messaging.c +++ b/fs/ecryptfs/messaging.c @@ -247,14 +247,13 @@ int ecryptfs_process_response(struct ecryptfs_daemon *daemon, goto unlock; } msg_size = (sizeof(*msg) + msg->data_len); - msg_ctx->msg = kmalloc(msg_size, GFP_KERNEL); + msg_ctx->msg = kmemdup(msg, msg_size, GFP_KERNEL); if (!msg_ctx->msg) { rc = -ENOMEM; printk(KERN_ERR "%s: Failed to allocate [%zd] bytes of " "GFP_KERNEL memory\n", __func__, msg_size); goto unlock; } - memcpy(msg_ctx->msg, msg, msg_size); msg_ctx->state = ECRYPTFS_MSG_CTX_STATE_DONE; wake_up_process(msg_ctx->task); rc = 0; -- cgit v1.2.3 From 12003e5b18ca33807b3f9448309ec92184192b85 Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Tue, 16 Apr 2013 23:21:24 -0700 Subject: eCryptfs: Use entire helper page during page crypto operations When encrypting eCryptfs pages and decrypting pages from the lower filesystem, utilize the entire helper page rather than only the first 4096 bytes. This only affects architectures where PAGE_CACHE_SIZE is larger than 4096 bytes. Signed-off-by: Tyler Hicks --- fs/ecryptfs/crypto.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index f71ec125290d..e8976c004669 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -450,10 +450,11 @@ static int ecryptfs_encrypt_extent(struct page *enc_extent_page, (unsigned long long)(extent_base + extent_offset), rc); goto out; } - rc = ecryptfs_encrypt_page_offset(crypt_stat, enc_extent_page, 0, - page, (extent_offset - * crypt_stat->extent_size), - crypt_stat->extent_size, extent_iv); + rc = ecryptfs_encrypt_page_offset(crypt_stat, enc_extent_page, + extent_offset * crypt_stat->extent_size, + page, + extent_offset * crypt_stat->extent_size, + crypt_stat->extent_size, extent_iv); if (rc < 0) { printk(KERN_ERR "%s: Error attempting to encrypt page with " "page->index = [%ld], extent_offset = [%ld]; " @@ -520,8 +521,9 @@ int ecryptfs_encrypt_page(struct page *page) * (PAGE_CACHE_SIZE / crypt_stat->extent_size)) + extent_offset), crypt_stat); - rc = ecryptfs_write_lower(ecryptfs_inode, enc_extent_virt, - offset, crypt_stat->extent_size); + rc = ecryptfs_write_lower(ecryptfs_inode, (enc_extent_virt + + extent_offset * crypt_stat->extent_size), + offset, crypt_stat->extent_size); if (rc < 0) { ecryptfs_printk(KERN_ERR, "Error attempting " "to write lower page; rc = [%d]" @@ -558,10 +560,10 @@ static int ecryptfs_decrypt_extent(struct page *page, goto out; } rc = ecryptfs_decrypt_page_offset(crypt_stat, page, - (extent_offset - * crypt_stat->extent_size), - enc_extent_page, 0, - crypt_stat->extent_size, extent_iv); + extent_offset * crypt_stat->extent_size, + enc_extent_page, + extent_offset * crypt_stat->extent_size, + crypt_stat->extent_size, extent_iv); if (rc < 0) { printk(KERN_ERR "%s: Error attempting to decrypt to page with " "page->index = [%ld], extent_offset = [%ld]; " @@ -620,9 +622,10 @@ int ecryptfs_decrypt_page(struct page *page) &offset, ((page->index * (PAGE_CACHE_SIZE / crypt_stat->extent_size)) + extent_offset), crypt_stat); - rc = ecryptfs_read_lower(enc_extent_virt, offset, - crypt_stat->extent_size, - ecryptfs_inode); + rc = ecryptfs_read_lower((enc_extent_virt + + extent_offset * crypt_stat->extent_size), + offset, crypt_stat->extent_size, + ecryptfs_inode); if (rc < 0) { ecryptfs_printk(KERN_ERR, "Error attempting " "to read lower page; rc = [%d]" -- cgit v1.2.3 From 0f89617623fed9541ead9497043e907466848a9f Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Mon, 15 Apr 2013 16:16:24 -0700 Subject: eCryptfs: Read/write entire page during page IO When reading and writing encrypted pages, perform IO using the entire page all at once rather than 4096 bytes at a time. This only affects architectures where PAGE_CACHE_SIZE is larger than 4096 bytes. Signed-off-by: Tyler Hicks --- fs/ecryptfs/crypto.c | 66 ++++++++++++++++++++++++---------------------------- 1 file changed, 31 insertions(+), 35 deletions(-) diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index e8976c004669..4185584594f5 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -490,6 +490,7 @@ int ecryptfs_encrypt_page(struct page *page) char *enc_extent_virt; struct page *enc_extent_page = NULL; loff_t extent_offset; + loff_t lower_offset; int rc = 0; ecryptfs_inode = page->mapping->host; @@ -503,12 +504,10 @@ int ecryptfs_encrypt_page(struct page *page) "encrypted extent\n"); goto out; } - enc_extent_virt = kmap(enc_extent_page); + for (extent_offset = 0; extent_offset < (PAGE_CACHE_SIZE / crypt_stat->extent_size); extent_offset++) { - loff_t offset; - rc = ecryptfs_encrypt_extent(enc_extent_page, crypt_stat, page, extent_offset); if (rc) { @@ -516,25 +515,24 @@ int ecryptfs_encrypt_page(struct page *page) "rc = [%d]\n", __func__, rc); goto out; } - ecryptfs_lower_offset_for_extent( - &offset, ((((loff_t)page->index) - * (PAGE_CACHE_SIZE - / crypt_stat->extent_size)) - + extent_offset), crypt_stat); - rc = ecryptfs_write_lower(ecryptfs_inode, (enc_extent_virt + - extent_offset * crypt_stat->extent_size), - offset, crypt_stat->extent_size); - if (rc < 0) { - ecryptfs_printk(KERN_ERR, "Error attempting " - "to write lower page; rc = [%d]" - "\n", rc); - goto out; - } + } + + ecryptfs_lower_offset_for_extent(&lower_offset, + page->index * (PAGE_CACHE_SIZE / crypt_stat->extent_size), + crypt_stat); + enc_extent_virt = kmap(enc_extent_page); + rc = ecryptfs_write_lower(ecryptfs_inode, enc_extent_virt, lower_offset, + PAGE_CACHE_SIZE); + kunmap(enc_extent_page); + if (rc < 0) { + ecryptfs_printk(KERN_ERR, + "Error attempting to write lower page; rc = [%d]\n", + rc); + goto out; } rc = 0; out: if (enc_extent_page) { - kunmap(enc_extent_page); __free_page(enc_extent_page); } return rc; @@ -599,6 +597,7 @@ int ecryptfs_decrypt_page(struct page *page) char *enc_extent_virt; struct page *enc_extent_page = NULL; unsigned long extent_offset; + loff_t lower_offset; int rc = 0; ecryptfs_inode = page->mapping->host; @@ -612,26 +611,24 @@ int ecryptfs_decrypt_page(struct page *page) "encrypted extent\n"); goto out; } + + ecryptfs_lower_offset_for_extent(&lower_offset, + page->index * (PAGE_CACHE_SIZE / crypt_stat->extent_size), + crypt_stat); enc_extent_virt = kmap(enc_extent_page); + rc = ecryptfs_read_lower(enc_extent_virt, lower_offset, PAGE_CACHE_SIZE, + ecryptfs_inode); + kunmap(enc_extent_page); + if (rc < 0) { + ecryptfs_printk(KERN_ERR, + "Error attempting to read lower page; rc = [%d]\n", + rc); + goto out; + } + for (extent_offset = 0; extent_offset < (PAGE_CACHE_SIZE / crypt_stat->extent_size); extent_offset++) { - loff_t offset; - - ecryptfs_lower_offset_for_extent( - &offset, ((page->index * (PAGE_CACHE_SIZE - / crypt_stat->extent_size)) - + extent_offset), crypt_stat); - rc = ecryptfs_read_lower((enc_extent_virt + - extent_offset * crypt_stat->extent_size), - offset, crypt_stat->extent_size, - ecryptfs_inode); - if (rc < 0) { - ecryptfs_printk(KERN_ERR, "Error attempting " - "to read lower page; rc = [%d]" - "\n", rc); - goto out; - } rc = ecryptfs_decrypt_extent(page, crypt_stat, enc_extent_page, extent_offset); if (rc) { @@ -642,7 +639,6 @@ int ecryptfs_decrypt_page(struct page *page) } out: if (enc_extent_page) { - kunmap(enc_extent_page); __free_page(enc_extent_page); } return rc; -- cgit v1.2.3 From 24d15266bd86b7961f309a962fa3aa177a78c49f Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Mon, 15 Apr 2013 17:28:09 -0700 Subject: eCryptfs: Simplify lower file offset calculation Now that lower filesystem IO operations occur for complete PAGE_CACHE_SIZE bytes, the calculation for converting an eCryptfs extent index into a lower file offset can be simplified. Signed-off-by: Tyler Hicks --- fs/ecryptfs/crypto.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index 4185584594f5..3547708fb4e1 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -407,15 +407,15 @@ out: } /** - * ecryptfs_lower_offset_for_extent + * lower_offset_for_page * * Convert an eCryptfs page index into a lower byte offset */ -static void ecryptfs_lower_offset_for_extent(loff_t *offset, loff_t extent_num, - struct ecryptfs_crypt_stat *crypt_stat) +static loff_t lower_offset_for_page(struct ecryptfs_crypt_stat *crypt_stat, + struct page *page) { - (*offset) = ecryptfs_lower_header_size(crypt_stat) - + (crypt_stat->extent_size * extent_num); + return ecryptfs_lower_header_size(crypt_stat) + + (page->index << PAGE_CACHE_SHIFT); } /** @@ -517,9 +517,7 @@ int ecryptfs_encrypt_page(struct page *page) } } - ecryptfs_lower_offset_for_extent(&lower_offset, - page->index * (PAGE_CACHE_SIZE / crypt_stat->extent_size), - crypt_stat); + lower_offset = lower_offset_for_page(crypt_stat, page); enc_extent_virt = kmap(enc_extent_page); rc = ecryptfs_write_lower(ecryptfs_inode, enc_extent_virt, lower_offset, PAGE_CACHE_SIZE); @@ -612,9 +610,7 @@ int ecryptfs_decrypt_page(struct page *page) goto out; } - ecryptfs_lower_offset_for_extent(&lower_offset, - page->index * (PAGE_CACHE_SIZE / crypt_stat->extent_size), - crypt_stat); + lower_offset = lower_offset_for_page(crypt_stat, page); enc_extent_virt = kmap(enc_extent_page); rc = ecryptfs_read_lower(enc_extent_virt, lower_offset, PAGE_CACHE_SIZE, ecryptfs_inode); -- cgit v1.2.3 From 28916d1ac1dd658773717e8eddc7c4ceeefc19b8 Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Mon, 15 Apr 2013 16:37:27 -0700 Subject: eCryptfs: Accept one offset parameter in page offset crypto functions There is no longer a need to accept different offset values for the source and destination pages when encrypting/decrypting an extent in an eCryptfs page. The two offsets can be collapsed into a single parameter. Signed-off-by: Tyler Hicks --- fs/ecryptfs/crypto.c | 42 ++++++++++++++++-------------------------- 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index 3547708fb4e1..ec640ebcdea8 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -39,14 +39,12 @@ static int ecryptfs_decrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat, - struct page *dst_page, int dst_offset, - struct page *src_page, int src_offset, int size, - unsigned char *iv); + struct page *dst_page, struct page *src_page, + int offset, int size, unsigned char *iv); static int ecryptfs_encrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat, - struct page *dst_page, int dst_offset, - struct page *src_page, int src_offset, int size, - unsigned char *iv); + struct page *dst_page, struct page *src_page, + int offset, int size, unsigned char *iv); /** * ecryptfs_to_hex @@ -450,9 +448,7 @@ static int ecryptfs_encrypt_extent(struct page *enc_extent_page, (unsigned long long)(extent_base + extent_offset), rc); goto out; } - rc = ecryptfs_encrypt_page_offset(crypt_stat, enc_extent_page, - extent_offset * crypt_stat->extent_size, - page, + rc = ecryptfs_encrypt_page_offset(crypt_stat, enc_extent_page, page, extent_offset * crypt_stat->extent_size, crypt_stat->extent_size, extent_iv); if (rc < 0) { @@ -555,9 +551,7 @@ static int ecryptfs_decrypt_extent(struct page *page, (unsigned long long)(extent_base + extent_offset), rc); goto out; } - rc = ecryptfs_decrypt_page_offset(crypt_stat, page, - extent_offset * crypt_stat->extent_size, - enc_extent_page, + rc = ecryptfs_decrypt_page_offset(crypt_stat, page, enc_extent_page, extent_offset * crypt_stat->extent_size, crypt_stat->extent_size, extent_iv); if (rc < 0) { @@ -716,9 +710,8 @@ out: * ecryptfs_encrypt_page_offset * @crypt_stat: The cryptographic context * @dst_page: The page to encrypt into - * @dst_offset: The offset in the page to encrypt into * @src_page: The page to encrypt from - * @src_offset: The offset in the page to encrypt from + * @offset: The byte offset into the dst_page and src_page * @size: The number of bytes to encrypt * @iv: The initialization vector to use for the encryption * @@ -726,17 +719,16 @@ out: */ static int ecryptfs_encrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat, - struct page *dst_page, int dst_offset, - struct page *src_page, int src_offset, int size, - unsigned char *iv) + struct page *dst_page, struct page *src_page, + int offset, int size, unsigned char *iv) { struct scatterlist src_sg, dst_sg; sg_init_table(&src_sg, 1); sg_init_table(&dst_sg, 1); - sg_set_page(&src_sg, src_page, size, src_offset); - sg_set_page(&dst_sg, dst_page, size, dst_offset); + sg_set_page(&src_sg, src_page, size, offset); + sg_set_page(&dst_sg, dst_page, size, offset); return encrypt_scatterlist(crypt_stat, &dst_sg, &src_sg, size, iv); } @@ -744,9 +736,8 @@ ecryptfs_encrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat, * ecryptfs_decrypt_page_offset * @crypt_stat: The cryptographic context * @dst_page: The page to decrypt into - * @dst_offset: The offset in the page to decrypt into * @src_page: The page to decrypt from - * @src_offset: The offset in the page to decrypt from + * @offset: The byte offset into the dst_page and src_page * @size: The number of bytes to decrypt * @iv: The initialization vector to use for the decryption * @@ -754,17 +745,16 @@ ecryptfs_encrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat, */ static int ecryptfs_decrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat, - struct page *dst_page, int dst_offset, - struct page *src_page, int src_offset, int size, - unsigned char *iv) + struct page *dst_page, struct page *src_page, + int offset, int size, unsigned char *iv) { struct scatterlist src_sg, dst_sg; sg_init_table(&src_sg, 1); - sg_set_page(&src_sg, src_page, size, src_offset); + sg_set_page(&src_sg, src_page, size, offset); sg_init_table(&dst_sg, 1); - sg_set_page(&dst_sg, dst_page, size, dst_offset); + sg_set_page(&dst_sg, dst_page, size, offset); return decrypt_scatterlist(crypt_stat, &dst_sg, &src_sg, size, iv); } -- cgit v1.2.3 From 9c6043f41222b448a314b0b8370f33b579f777ea Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Sat, 6 Apr 2013 00:41:48 -0700 Subject: eCryptfs: Decrypt pages in-place When reading in a page, eCryptfs would allocate a helper page, fill it with encrypted data from the lower filesytem, and then decrypt the data from the encrypted page and store the result in the eCryptfs page cache page. The crypto API supports in-place crypto operations which means that the allocation of the helper page is unnecessary when decrypting. This patch gets rid of the unneeded page allocation by reading encrypted data from the lower filesystem directly into the page cache page. The page cache page is then decrypted in-place. Signed-off-by: Tyler Hicks --- fs/ecryptfs/crypto.c | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index ec640ebcdea8..35b409bda841 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -586,8 +586,7 @@ int ecryptfs_decrypt_page(struct page *page) { struct inode *ecryptfs_inode; struct ecryptfs_crypt_stat *crypt_stat; - char *enc_extent_virt; - struct page *enc_extent_page = NULL; + char *page_virt; unsigned long extent_offset; loff_t lower_offset; int rc = 0; @@ -596,19 +595,12 @@ int ecryptfs_decrypt_page(struct page *page) crypt_stat = &(ecryptfs_inode_to_private(ecryptfs_inode)->crypt_stat); BUG_ON(!(crypt_stat->flags & ECRYPTFS_ENCRYPTED)); - enc_extent_page = alloc_page(GFP_USER); - if (!enc_extent_page) { - rc = -ENOMEM; - ecryptfs_printk(KERN_ERR, "Error allocating memory for " - "encrypted extent\n"); - goto out; - } lower_offset = lower_offset_for_page(crypt_stat, page); - enc_extent_virt = kmap(enc_extent_page); - rc = ecryptfs_read_lower(enc_extent_virt, lower_offset, PAGE_CACHE_SIZE, + page_virt = kmap(page); + rc = ecryptfs_read_lower(page_virt, lower_offset, PAGE_CACHE_SIZE, ecryptfs_inode); - kunmap(enc_extent_page); + kunmap(page); if (rc < 0) { ecryptfs_printk(KERN_ERR, "Error attempting to read lower page; rc = [%d]\n", @@ -619,7 +611,7 @@ int ecryptfs_decrypt_page(struct page *page) for (extent_offset = 0; extent_offset < (PAGE_CACHE_SIZE / crypt_stat->extent_size); extent_offset++) { - rc = ecryptfs_decrypt_extent(page, crypt_stat, enc_extent_page, + rc = ecryptfs_decrypt_extent(page, crypt_stat, page, extent_offset); if (rc) { printk(KERN_ERR "%s: Error encrypting extent; " @@ -628,9 +620,6 @@ int ecryptfs_decrypt_page(struct page *page) } } out: - if (enc_extent_page) { - __free_page(enc_extent_page); - } return rc; } -- cgit v1.2.3 From 00a699400a707953368e970b37bb8765fdb08015 Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Fri, 5 Apr 2013 23:26:22 -0700 Subject: eCryptfs: Combine encrypt_scatterlist() and decrypt_scatterlist() These two functions are identical except for a debug printk and whether they call crypto_ablkcipher_encrypt() or crypto_ablkcipher_decrypt(), so they can be safely merged if the caller can indicate if encryption or decryption should occur. The debug printk is useless so it is removed. Two new #define's are created to indicate if an ENCRYPT or DECRYPT operation is desired. Signed-off-by: Tyler Hicks --- fs/ecryptfs/crypto.c | 106 ++++++++++----------------------------------------- 1 file changed, 20 insertions(+), 86 deletions(-) diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index 35b409bda841..fb54a0182f2e 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -37,6 +37,9 @@ #include #include "ecryptfs_kernel.h" +#define DECRYPT 0 +#define ENCRYPT 1 + static int ecryptfs_decrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat, struct page *dst_page, struct page *src_page, @@ -334,19 +337,20 @@ static void extent_crypt_complete(struct crypto_async_request *req, int rc) } /** - * encrypt_scatterlist + * crypt_scatterlist * @crypt_stat: Pointer to the crypt_stat struct to initialize. - * @dest_sg: Destination of encrypted data - * @src_sg: Data to be encrypted - * @size: Length of data to be encrypted - * @iv: iv to use during encryption + * @dest_sg: Destination of the data after performing the crypto operation + * @src_sg: Data to be encrypted or decrypted + * @size: Length of data + * @iv: IV to use + * @op: ENCRYPT or DECRYPT to indicate the desired operation * - * Returns the number of bytes encrypted; negative value on error + * Returns the number of bytes encrypted or decrypted; negative value on error */ -static int encrypt_scatterlist(struct ecryptfs_crypt_stat *crypt_stat, - struct scatterlist *dest_sg, - struct scatterlist *src_sg, int size, - unsigned char *iv) +static int crypt_scatterlist(struct ecryptfs_crypt_stat *crypt_stat, + struct scatterlist *dest_sg, + struct scatterlist *src_sg, int size, + unsigned char *iv, int op) { struct ablkcipher_request *req = NULL; struct extent_crypt_result ecr; @@ -389,9 +393,9 @@ static int encrypt_scatterlist(struct ecryptfs_crypt_stat *crypt_stat, crypt_stat->flags |= ECRYPTFS_KEY_SET; } mutex_unlock(&crypt_stat->cs_tfm_mutex); - ecryptfs_printk(KERN_DEBUG, "Encrypting [%d] bytes.\n", size); ablkcipher_request_set_crypt(req, src_sg, dest_sg, size, iv); - rc = crypto_ablkcipher_encrypt(req); + rc = op == ENCRYPT ? crypto_ablkcipher_encrypt(req) : + crypto_ablkcipher_decrypt(req); if (rc == -EINPROGRESS || rc == -EBUSY) { struct extent_crypt_result *ecr = req->base.data; @@ -623,78 +627,6 @@ out: return rc; } -/** - * decrypt_scatterlist - * @crypt_stat: Cryptographic context - * @dest_sg: The destination scatterlist to decrypt into - * @src_sg: The source scatterlist to decrypt from - * @size: The number of bytes to decrypt - * @iv: The initialization vector to use for the decryption - * - * Returns the number of bytes decrypted; negative value on error - */ -static int decrypt_scatterlist(struct ecryptfs_crypt_stat *crypt_stat, - struct scatterlist *dest_sg, - struct scatterlist *src_sg, int size, - unsigned char *iv) -{ - struct ablkcipher_request *req = NULL; - struct extent_crypt_result ecr; - int rc = 0; - - BUG_ON(!crypt_stat || !crypt_stat->tfm - || !(crypt_stat->flags & ECRYPTFS_STRUCT_INITIALIZED)); - if (unlikely(ecryptfs_verbosity > 0)) { - ecryptfs_printk(KERN_DEBUG, "Key size [%zd]; key:\n", - crypt_stat->key_size); - ecryptfs_dump_hex(crypt_stat->key, - crypt_stat->key_size); - } - - init_completion(&ecr.completion); - - mutex_lock(&crypt_stat->cs_tfm_mutex); - req = ablkcipher_request_alloc(crypt_stat->tfm, GFP_NOFS); - if (!req) { - mutex_unlock(&crypt_stat->cs_tfm_mutex); - rc = -ENOMEM; - goto out; - } - - ablkcipher_request_set_callback(req, - CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, - extent_crypt_complete, &ecr); - /* Consider doing this once, when the file is opened */ - if (!(crypt_stat->flags & ECRYPTFS_KEY_SET)) { - rc = crypto_ablkcipher_setkey(crypt_stat->tfm, crypt_stat->key, - crypt_stat->key_size); - if (rc) { - ecryptfs_printk(KERN_ERR, - "Error setting key; rc = [%d]\n", - rc); - mutex_unlock(&crypt_stat->cs_tfm_mutex); - rc = -EINVAL; - goto out; - } - crypt_stat->flags |= ECRYPTFS_KEY_SET; - } - mutex_unlock(&crypt_stat->cs_tfm_mutex); - ecryptfs_printk(KERN_DEBUG, "Decrypting [%d] bytes.\n", size); - ablkcipher_request_set_crypt(req, src_sg, dest_sg, size, iv); - rc = crypto_ablkcipher_decrypt(req); - if (rc == -EINPROGRESS || rc == -EBUSY) { - struct extent_crypt_result *ecr = req->base.data; - - wait_for_completion(&ecr->completion); - rc = ecr->rc; - INIT_COMPLETION(ecr->completion); - } -out: - ablkcipher_request_free(req); - return rc; - -} - /** * ecryptfs_encrypt_page_offset * @crypt_stat: The cryptographic context @@ -718,7 +650,8 @@ ecryptfs_encrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat, sg_set_page(&src_sg, src_page, size, offset); sg_set_page(&dst_sg, dst_page, size, offset); - return encrypt_scatterlist(crypt_stat, &dst_sg, &src_sg, size, iv); + return crypt_scatterlist(crypt_stat, &dst_sg, &src_sg, + size, iv, ENCRYPT); } /** @@ -745,7 +678,8 @@ ecryptfs_decrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat, sg_init_table(&dst_sg, 1); sg_set_page(&dst_sg, dst_page, size, offset); - return decrypt_scatterlist(crypt_stat, &dst_sg, &src_sg, size, iv); + return crypt_scatterlist(crypt_stat, &dst_sg, &src_sg, + size, iv, DECRYPT); } #define ECRYPTFS_MAX_SCATTERLIST_LEN 4 -- cgit v1.2.3 From a8ca90e2071edb3d3f3272ae73d73411f0b70b54 Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Fri, 5 Apr 2013 23:37:51 -0700 Subject: eCryptfs: Combine page_offset crypto functions Combine ecryptfs_encrypt_page_offset() and ecryptfs_decrypt_page_offset(). These two functions are functionally identical so they can be safely merged if the caller can indicate whether an encryption or decryption operation should occur. Signed-off-by: Tyler Hicks --- fs/ecryptfs/crypto.c | 69 ++++++++++++++-------------------------------------- 1 file changed, 18 insertions(+), 51 deletions(-) diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index fb54a0182f2e..609efc01d5c2 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -40,14 +40,9 @@ #define DECRYPT 0 #define ENCRYPT 1 -static int -ecryptfs_decrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat, - struct page *dst_page, struct page *src_page, - int offset, int size, unsigned char *iv); -static int -ecryptfs_encrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat, +static int crypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat, struct page *dst_page, struct page *src_page, - int offset, int size, unsigned char *iv); + int offset, int size, unsigned char *iv, int op); /** * ecryptfs_to_hex @@ -452,9 +447,9 @@ static int ecryptfs_encrypt_extent(struct page *enc_extent_page, (unsigned long long)(extent_base + extent_offset), rc); goto out; } - rc = ecryptfs_encrypt_page_offset(crypt_stat, enc_extent_page, page, - extent_offset * crypt_stat->extent_size, - crypt_stat->extent_size, extent_iv); + rc = crypt_page_offset(crypt_stat, enc_extent_page, page, + (extent_offset * crypt_stat->extent_size), + crypt_stat->extent_size, extent_iv, ENCRYPT); if (rc < 0) { printk(KERN_ERR "%s: Error attempting to encrypt page with " "page->index = [%ld], extent_offset = [%ld]; " @@ -555,9 +550,9 @@ static int ecryptfs_decrypt_extent(struct page *page, (unsigned long long)(extent_base + extent_offset), rc); goto out; } - rc = ecryptfs_decrypt_page_offset(crypt_stat, page, enc_extent_page, - extent_offset * crypt_stat->extent_size, - crypt_stat->extent_size, extent_iv); + rc = crypt_page_offset(crypt_stat, page, enc_extent_page, + (extent_offset * crypt_stat->extent_size), + crypt_stat->extent_size, extent_iv, DECRYPT); if (rc < 0) { printk(KERN_ERR "%s: Error attempting to decrypt to page with " "page->index = [%ld], extent_offset = [%ld]; " @@ -628,20 +623,20 @@ out: } /** - * ecryptfs_encrypt_page_offset + * crypt_page_offset * @crypt_stat: The cryptographic context - * @dst_page: The page to encrypt into - * @src_page: The page to encrypt from + * @dst_page: The page to write the result into + * @src_page: The page to read from * @offset: The byte offset into the dst_page and src_page - * @size: The number of bytes to encrypt - * @iv: The initialization vector to use for the encryption + * @size: The number of bytes of data + * @iv: The initialization vector to use for the crypto operation + * @op: ENCRYPT or DECRYPT to indicate the desired operation * - * Returns the number of bytes encrypted + * Returns the number of bytes encrypted or decrypted */ -static int -ecryptfs_encrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat, +static int crypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat, struct page *dst_page, struct page *src_page, - int offset, int size, unsigned char *iv) + int offset, int size, unsigned char *iv, int op) { struct scatterlist src_sg, dst_sg; @@ -650,36 +645,8 @@ ecryptfs_encrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat, sg_set_page(&src_sg, src_page, size, offset); sg_set_page(&dst_sg, dst_page, size, offset); - return crypt_scatterlist(crypt_stat, &dst_sg, &src_sg, - size, iv, ENCRYPT); -} - -/** - * ecryptfs_decrypt_page_offset - * @crypt_stat: The cryptographic context - * @dst_page: The page to decrypt into - * @src_page: The page to decrypt from - * @offset: The byte offset into the dst_page and src_page - * @size: The number of bytes to decrypt - * @iv: The initialization vector to use for the decryption - * - * Returns the number of bytes decrypted - */ -static int -ecryptfs_decrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat, - struct page *dst_page, struct page *src_page, - int offset, int size, unsigned char *iv) -{ - struct scatterlist src_sg, dst_sg; - - sg_init_table(&src_sg, 1); - sg_set_page(&src_sg, src_page, size, offset); - - sg_init_table(&dst_sg, 1); - sg_set_page(&dst_sg, dst_page, size, offset); - return crypt_scatterlist(crypt_stat, &dst_sg, &src_sg, - size, iv, DECRYPT); + return crypt_scatterlist(crypt_stat, &dst_sg, &src_sg, size, iv, op); } #define ECRYPTFS_MAX_SCATTERLIST_LEN 4 -- cgit v1.2.3 From d78de618962d1e9d28c602e3c75991fe9c94e961 Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Sat, 6 Apr 2013 00:08:48 -0700 Subject: eCryptfs: Merge ecryptfs_encrypt_extent() and ecryptfs_decrypt_extent() They are identical except if the src_page or dst_page index is used, so they can be merged safely if page_index is conditionally assigned. Signed-off-by: Tyler Hicks --- fs/ecryptfs/crypto.c | 74 ++++++++++++++-------------------------------------- 1 file changed, 20 insertions(+), 54 deletions(-) diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index 609efc01d5c2..9845d2fd2506 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -416,28 +416,29 @@ static loff_t lower_offset_for_page(struct ecryptfs_crypt_stat *crypt_stat, } /** - * ecryptfs_encrypt_extent - * @enc_extent_page: Allocated page into which to encrypt the data in - * @page + * crypt_extent + * @dst_page: The page to write the result into * @crypt_stat: crypt_stat containing cryptographic context for the * encryption operation - * @page: Page containing plaintext data extent to encrypt + * @src_page: The page to read from * @extent_offset: Page extent offset for use in generating IV + * @op: ENCRYPT or DECRYPT to indicate the desired operation * - * Encrypts one extent of data. + * Encrypts or decrypts one extent of data. * * Return zero on success; non-zero otherwise */ -static int ecryptfs_encrypt_extent(struct page *enc_extent_page, - struct ecryptfs_crypt_stat *crypt_stat, - struct page *page, - unsigned long extent_offset) +static int crypt_extent(struct page *dst_page, + struct ecryptfs_crypt_stat *crypt_stat, + struct page *src_page, + unsigned long extent_offset, int op) { + pgoff_t page_index = op == ENCRYPT ? src_page->index : dst_page->index; loff_t extent_base; char extent_iv[ECRYPTFS_MAX_IV_BYTES]; int rc; - extent_base = (((loff_t)page->index) + extent_base = (((loff_t)page_index) * (PAGE_CACHE_SIZE / crypt_stat->extent_size)); rc = ecryptfs_derive_iv(extent_iv, crypt_stat, (extent_base + extent_offset)); @@ -447,14 +448,13 @@ static int ecryptfs_encrypt_extent(struct page *enc_extent_page, (unsigned long long)(extent_base + extent_offset), rc); goto out; } - rc = crypt_page_offset(crypt_stat, enc_extent_page, page, + rc = crypt_page_offset(crypt_stat, dst_page, src_page, (extent_offset * crypt_stat->extent_size), - crypt_stat->extent_size, extent_iv, ENCRYPT); + crypt_stat->extent_size, extent_iv, op); if (rc < 0) { - printk(KERN_ERR "%s: Error attempting to encrypt page with " - "page->index = [%ld], extent_offset = [%ld]; " - "rc = [%d]\n", __func__, page->index, extent_offset, - rc); + printk(KERN_ERR "%s: Error attempting to crypt page with " + "page_index = [%ld], extent_offset = [%ld]; " + "rc = [%d]\n", __func__, page_index, extent_offset, rc); goto out; } rc = 0; @@ -503,8 +503,8 @@ int ecryptfs_encrypt_page(struct page *page) for (extent_offset = 0; extent_offset < (PAGE_CACHE_SIZE / crypt_stat->extent_size); extent_offset++) { - rc = ecryptfs_encrypt_extent(enc_extent_page, crypt_stat, page, - extent_offset); + rc = crypt_extent(enc_extent_page, crypt_stat, page, + extent_offset, ENCRYPT); if (rc) { printk(KERN_ERR "%s: Error encrypting extent; " "rc = [%d]\n", __func__, rc); @@ -531,40 +531,6 @@ out: return rc; } -static int ecryptfs_decrypt_extent(struct page *page, - struct ecryptfs_crypt_stat *crypt_stat, - struct page *enc_extent_page, - unsigned long extent_offset) -{ - loff_t extent_base; - char extent_iv[ECRYPTFS_MAX_IV_BYTES]; - int rc; - - extent_base = (((loff_t)page->index) - * (PAGE_CACHE_SIZE / crypt_stat->extent_size)); - rc = ecryptfs_derive_iv(extent_iv, crypt_stat, - (extent_base + extent_offset)); - if (rc) { - ecryptfs_printk(KERN_ERR, "Error attempting to derive IV for " - "extent [0x%.16llx]; rc = [%d]\n", - (unsigned long long)(extent_base + extent_offset), rc); - goto out; - } - rc = crypt_page_offset(crypt_stat, page, enc_extent_page, - (extent_offset * crypt_stat->extent_size), - crypt_stat->extent_size, extent_iv, DECRYPT); - if (rc < 0) { - printk(KERN_ERR "%s: Error attempting to decrypt to page with " - "page->index = [%ld], extent_offset = [%ld]; " - "rc = [%d]\n", __func__, page->index, extent_offset, - rc); - goto out; - } - rc = 0; -out: - return rc; -} - /** * ecryptfs_decrypt_page * @page: Page mapped from the eCryptfs inode for the file; data read @@ -610,8 +576,8 @@ int ecryptfs_decrypt_page(struct page *page) for (extent_offset = 0; extent_offset < (PAGE_CACHE_SIZE / crypt_stat->extent_size); extent_offset++) { - rc = ecryptfs_decrypt_extent(page, crypt_stat, page, - extent_offset); + rc = crypt_extent(page, crypt_stat, page, + extent_offset, DECRYPT); if (rc) { printk(KERN_ERR "%s: Error encrypting extent; " "rc = [%d]\n", __func__, rc); -- cgit v1.2.3 From 406c93df09ae7a345b510cf6619f881b42a3d553 Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Mon, 15 Apr 2013 17:49:31 -0700 Subject: eCryptfs: Collapse crypt_page_offset() into crypt_extent() crypt_page_offset() simply initialized the two scatterlists and called crypt_scatterlist() so it is simple enough to move into the only function that calls it. Signed-off-by: Tyler Hicks --- fs/ecryptfs/crypto.c | 50 ++++++++++++++------------------------------------ 1 file changed, 14 insertions(+), 36 deletions(-) diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index 9845d2fd2506..9947388ccd8d 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -40,10 +40,6 @@ #define DECRYPT 0 #define ENCRYPT 1 -static int crypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat, - struct page *dst_page, struct page *src_page, - int offset, int size, unsigned char *iv, int op); - /** * ecryptfs_to_hex * @dst: Buffer to take hex character representation of contents of @@ -436,10 +432,11 @@ static int crypt_extent(struct page *dst_page, pgoff_t page_index = op == ENCRYPT ? src_page->index : dst_page->index; loff_t extent_base; char extent_iv[ECRYPTFS_MAX_IV_BYTES]; + struct scatterlist src_sg, dst_sg; + size_t extent_size = crypt_stat->extent_size; int rc; - extent_base = (((loff_t)page_index) - * (PAGE_CACHE_SIZE / crypt_stat->extent_size)); + extent_base = (((loff_t)page_index) * (PAGE_CACHE_SIZE / extent_size)); rc = ecryptfs_derive_iv(extent_iv, crypt_stat, (extent_base + extent_offset)); if (rc) { @@ -448,9 +445,17 @@ static int crypt_extent(struct page *dst_page, (unsigned long long)(extent_base + extent_offset), rc); goto out; } - rc = crypt_page_offset(crypt_stat, dst_page, src_page, - (extent_offset * crypt_stat->extent_size), - crypt_stat->extent_size, extent_iv, op); + + sg_init_table(&src_sg, 1); + sg_init_table(&dst_sg, 1); + + sg_set_page(&src_sg, src_page, extent_size, + extent_offset * extent_size); + sg_set_page(&dst_sg, dst_page, extent_size, + extent_offset * extent_size); + + rc = crypt_scatterlist(crypt_stat, &dst_sg, &src_sg, extent_size, + extent_iv, op); if (rc < 0) { printk(KERN_ERR "%s: Error attempting to crypt page with " "page_index = [%ld], extent_offset = [%ld]; " @@ -588,33 +593,6 @@ out: return rc; } -/** - * crypt_page_offset - * @crypt_stat: The cryptographic context - * @dst_page: The page to write the result into - * @src_page: The page to read from - * @offset: The byte offset into the dst_page and src_page - * @size: The number of bytes of data - * @iv: The initialization vector to use for the crypto operation - * @op: ENCRYPT or DECRYPT to indicate the desired operation - * - * Returns the number of bytes encrypted or decrypted - */ -static int crypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat, - struct page *dst_page, struct page *src_page, - int offset, int size, unsigned char *iv, int op) -{ - struct scatterlist src_sg, dst_sg; - - sg_init_table(&src_sg, 1); - sg_init_table(&dst_sg, 1); - - sg_set_page(&src_sg, src_page, size, offset); - sg_set_page(&dst_sg, dst_page, size, offset); - - return crypt_scatterlist(crypt_stat, &dst_sg, &src_sg, size, iv, op); -} - #define ECRYPTFS_MAX_SCATTERLIST_LEN 4 /** -- cgit v1.2.3 From 0df5ed65c14e2c36ed842fcff58118662009f1a1 Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Thu, 23 May 2013 12:08:40 -0700 Subject: eCryptfs: Make extent and scatterlist crypt function parameters similar The 'dest' abbreviation is only used in crypt_scatterlist(), while all other functions in crypto.c use 'dst' so dest_sg should be renamed to dst_sg. The crypt_stat parameter is typically the first parameter in internal eCryptfs functions so crypt_stat and dst_page should be swapped in crypt_extent(). Signed-off-by: Tyler Hicks --- fs/ecryptfs/crypto.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index 9947388ccd8d..46a6f6a4a705 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -330,7 +330,7 @@ static void extent_crypt_complete(struct crypto_async_request *req, int rc) /** * crypt_scatterlist * @crypt_stat: Pointer to the crypt_stat struct to initialize. - * @dest_sg: Destination of the data after performing the crypto operation + * @dst_sg: Destination of the data after performing the crypto operation * @src_sg: Data to be encrypted or decrypted * @size: Length of data * @iv: IV to use @@ -339,7 +339,7 @@ static void extent_crypt_complete(struct crypto_async_request *req, int rc) * Returns the number of bytes encrypted or decrypted; negative value on error */ static int crypt_scatterlist(struct ecryptfs_crypt_stat *crypt_stat, - struct scatterlist *dest_sg, + struct scatterlist *dst_sg, struct scatterlist *src_sg, int size, unsigned char *iv, int op) { @@ -384,7 +384,7 @@ static int crypt_scatterlist(struct ecryptfs_crypt_stat *crypt_stat, crypt_stat->flags |= ECRYPTFS_KEY_SET; } mutex_unlock(&crypt_stat->cs_tfm_mutex); - ablkcipher_request_set_crypt(req, src_sg, dest_sg, size, iv); + ablkcipher_request_set_crypt(req, src_sg, dst_sg, size, iv); rc = op == ENCRYPT ? crypto_ablkcipher_encrypt(req) : crypto_ablkcipher_decrypt(req); if (rc == -EINPROGRESS || rc == -EBUSY) { @@ -413,9 +413,9 @@ static loff_t lower_offset_for_page(struct ecryptfs_crypt_stat *crypt_stat, /** * crypt_extent - * @dst_page: The page to write the result into * @crypt_stat: crypt_stat containing cryptographic context for the * encryption operation + * @dst_page: The page to write the result into * @src_page: The page to read from * @extent_offset: Page extent offset for use in generating IV * @op: ENCRYPT or DECRYPT to indicate the desired operation @@ -424,8 +424,8 @@ static loff_t lower_offset_for_page(struct ecryptfs_crypt_stat *crypt_stat, * * Return zero on success; non-zero otherwise */ -static int crypt_extent(struct page *dst_page, - struct ecryptfs_crypt_stat *crypt_stat, +static int crypt_extent(struct ecryptfs_crypt_stat *crypt_stat, + struct page *dst_page, struct page *src_page, unsigned long extent_offset, int op) { @@ -508,7 +508,7 @@ int ecryptfs_encrypt_page(struct page *page) for (extent_offset = 0; extent_offset < (PAGE_CACHE_SIZE / crypt_stat->extent_size); extent_offset++) { - rc = crypt_extent(enc_extent_page, crypt_stat, page, + rc = crypt_extent(crypt_stat, enc_extent_page, page, extent_offset, ENCRYPT); if (rc) { printk(KERN_ERR "%s: Error encrypting extent; " @@ -581,7 +581,7 @@ int ecryptfs_decrypt_page(struct page *page) for (extent_offset = 0; extent_offset < (PAGE_CACHE_SIZE / crypt_stat->extent_size); extent_offset++) { - rc = crypt_extent(page, crypt_stat, page, + rc = crypt_extent(crypt_stat, page, page, extent_offset, DECRYPT); if (rc) { printk(KERN_ERR "%s: Error encrypting extent; " -- cgit v1.2.3 From e8b265e8ba322a01bb746504f6f64b5506853eac Mon Sep 17 00:00:00 2001 From: Rami Rosen Date: Fri, 7 Jun 2013 20:16:19 +0000 Subject: doc:networking: Fix default value (icmp_ignore_bogus_error_responses). This patch fixes icmp_ignore_bogus_error_responses default value to be 1 instead of FALSE. It is initialized to 1 in icmp_sk_init(). Signed-off-by: Rami Rosen Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 398d0fb1dd0e..9cea5d3b3c50 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -729,7 +729,7 @@ icmp_ignore_bogus_error_responses - BOOLEAN frames. Such violations are normally logged via a kernel warning. If this is set to TRUE, the kernel will not give such warnings, which will avoid log file clutter. - Default: FALSE + Default: 1 icmp_errors_use_inbound_ifaddr - BOOLEAN -- cgit v1.2.3 From afe391ad4b05f8f00ca955e165f3b37b480506b7 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Fri, 7 Jun 2013 13:54:02 +0000 Subject: sh_eth: create initial ID table We are trying to get away from the current driver's scheme of identifying a SoC based on #ifdef's and the platform device ID table matching seems to be a good replacement -- we can use the 'driver_data' field of 'struct platform_device_id' as a pointer to a 'struct sh_eth_cpu_data'. Start by creating the initial table with driver's name as the only entry without the driver data. Check the driver data in the probe() method and if it's not NULL override 'mdp->cd' from it. Signed-off-by: Sergei Shtylyov Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/sh_eth.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 0699b1ec7e32..cf062de40b65 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -2519,6 +2519,7 @@ static int sh_eth_drv_probe(struct platform_device *pdev) struct net_device *ndev = NULL; struct sh_eth_private *mdp = NULL; struct sh_eth_plat_data *pd = pdev->dev.platform_data; + const struct platform_device_id *id = platform_get_device_id(pdev); /* get base addr */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -2582,6 +2583,8 @@ static int sh_eth_drv_probe(struct platform_device *pdev) #else mdp->cd = &sh_eth_my_cpu_data; #endif + if (id->driver_data) + mdp->cd = (struct sh_eth_cpu_data *)id->driver_data; sh_eth_set_default_cpu_data(mdp->cd); /* set function */ @@ -2696,9 +2699,16 @@ static const struct dev_pm_ops sh_eth_dev_pm_ops = { #define SH_ETH_PM_OPS NULL #endif +static struct platform_device_id sh_eth_id_table[] = { + { CARDNAME }, + { } +}; +MODULE_DEVICE_TABLE(platform, sh_eth_id_table); + static struct platform_driver sh_eth_driver = { .probe = sh_eth_drv_probe, .remove = sh_eth_drv_remove, + .id_table = sh_eth_id_table, .driver = { .name = CARDNAME, .pm = SH_ETH_PM_OPS, -- cgit v1.2.3 From 7bbe150d8c57c59689c8c50fd7b9915f4a7e10da Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Fri, 7 Jun 2013 13:55:08 +0000 Subject: sh_eth: get SH771x support out of #ifdef Get the SH771[02] data in the driver out of #ifdef by adding "sh771x-ether" to the platform driver's ID table. Change the Ether platform device's name in the SH platform code accordingly. Signed-off-by: Sergei Shtylyov Signed-off-by: David S. Miller --- arch/sh/boards/mach-se/770x/setup.c | 8 ++++---- drivers/net/ethernet/renesas/sh_eth.c | 7 ++++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/arch/sh/boards/mach-se/770x/setup.c b/arch/sh/boards/mach-se/770x/setup.c index 9759d6ba7ffb..658326f44df8 100644 --- a/arch/sh/boards/mach-se/770x/setup.c +++ b/arch/sh/boards/mach-se/770x/setup.c @@ -128,8 +128,8 @@ static struct resource sh_eth0_resources[] = { }; static struct platform_device sh_eth0_device = { - .name = "sh-eth", - .id = 0, + .name = "sh771x-ether", + .id = 0, .dev = { .platform_data = PHY_ID, }, @@ -151,8 +151,8 @@ static struct resource sh_eth1_resources[] = { }; static struct platform_device sh_eth1_device = { - .name = "sh-eth", - .id = 1, + .name = "sh771x-ether", + .id = 1, .dev = { .platform_data = PHY_ID, }, diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index cf062de40b65..f26546aa1792 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -690,12 +690,12 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = { .tpauser = 1, .hw_swap = 1, }; -#elif defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712) -static struct sh_eth_cpu_data sh_eth_my_cpu_data = { +#endif + +static struct sh_eth_cpu_data sh771x_data = { .eesipr_value = DMAC_M_RFRMER | DMAC_M_ECI | 0x003fffff, .tsu = 1, }; -#endif static void sh_eth_set_default_cpu_data(struct sh_eth_cpu_data *cd) { @@ -2700,6 +2700,7 @@ static const struct dev_pm_ops sh_eth_dev_pm_ops = { #endif static struct platform_device_id sh_eth_id_table[] = { + { "sh771x-ether", (kernel_ulong_t)&sh771x_data }, { CARDNAME }, { } }; -- cgit v1.2.3 From c18a79abe31f555ec3b363b5b8c1d003230053b6 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Fri, 7 Jun 2013 13:56:05 +0000 Subject: sh_eth: get SH7619 support out of #ifdef Get the SH7619 data in the driver out of #ifdef by adding "sh7619-ether" to the platform driver's ID table. Change the Ether platform device's name in the SH platform code accordingly. Signed-off-by: Sergei Shtylyov Signed-off-by: David S. Miller --- arch/sh/kernel/cpu/sh2/setup-sh7619.c | 4 ++-- drivers/net/ethernet/renesas/sh_eth.c | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/sh/kernel/cpu/sh2/setup-sh7619.c b/arch/sh/kernel/cpu/sh2/setup-sh7619.c index e0b740c831c7..bb11e1925178 100644 --- a/arch/sh/kernel/cpu/sh2/setup-sh7619.c +++ b/arch/sh/kernel/cpu/sh2/setup-sh7619.c @@ -124,8 +124,8 @@ static struct resource eth_resources[] = { }; static struct platform_device eth_device = { - .name = "sh-eth", - .id = -1, + .name = "sh7619-ether", + .id = -1, .dev = { .platform_data = (void *)1, }, diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index f26546aa1792..e0568a9352e5 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -680,9 +680,9 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = { .tsu = 1, .select_mii = 1, }; +#endif -#elif defined(CONFIG_CPU_SUBTYPE_SH7619) -static struct sh_eth_cpu_data sh_eth_my_cpu_data = { +static struct sh_eth_cpu_data sh7619_data = { .eesipr_value = DMAC_M_RFRMER | DMAC_M_ECI | 0x003fffff, .apr = 1, @@ -690,7 +690,6 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = { .tpauser = 1, .hw_swap = 1, }; -#endif static struct sh_eth_cpu_data sh771x_data = { .eesipr_value = DMAC_M_RFRMER | DMAC_M_ECI | 0x003fffff, @@ -2700,6 +2699,7 @@ static const struct dev_pm_ops sh_eth_dev_pm_ops = { #endif static struct platform_device_id sh_eth_id_table[] = { + { "sh7619-ether", (kernel_ulong_t)&sh7619_data }, { "sh771x-ether", (kernel_ulong_t)&sh771x_data }, { CARDNAME }, { } -- cgit v1.2.3 From e5c9b4cd665106d9b5397114ea81a53059410b6a Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Fri, 7 Jun 2013 13:57:12 +0000 Subject: sh_eth: get R8A7740 support out of #ifdef Get the R8A7740 code/data in the driver out of #ifdef by adding "r8a7740-gether" to the platform driver's ID table. Change the GEther platform device's name in the ARM platform code accordingly. Signed-off-by: Sergei Shtylyov Signed-off-by: David S. Miller --- arch/arm/mach-shmobile/board-armadillo800eva.c | 2 +- arch/arm/mach-shmobile/clock-r8a7740.c | 2 +- drivers/net/ethernet/renesas/sh_eth.c | 16 +++++++--------- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/arch/arm/mach-shmobile/board-armadillo800eva.c b/arch/arm/mach-shmobile/board-armadillo800eva.c index b85b2882dbd0..2b04c8011e13 100644 --- a/arch/arm/mach-shmobile/board-armadillo800eva.c +++ b/arch/arm/mach-shmobile/board-armadillo800eva.c @@ -377,7 +377,7 @@ static struct resource sh_eth_resources[] = { }; static struct platform_device sh_eth_device = { - .name = "sh-eth", + .name = "r8a7740-gether", .id = -1, .dev = { .platform_data = &sh_eth_platdata, diff --git a/arch/arm/mach-shmobile/clock-r8a7740.c b/arch/arm/mach-shmobile/clock-r8a7740.c index c0d39aa6de50..ae93f94d15bd 100644 --- a/arch/arm/mach-shmobile/clock-r8a7740.c +++ b/arch/arm/mach-shmobile/clock-r8a7740.c @@ -591,7 +591,7 @@ static struct clk_lookup lookups[] = { CLKDEV_DEV_ID("e6860000.sdhi", &mstp_clks[MSTP313]), CLKDEV_DEV_ID("sh_mmcif", &mstp_clks[MSTP312]), CLKDEV_DEV_ID("e6bd0000.mmcif", &mstp_clks[MSTP312]), - CLKDEV_DEV_ID("sh-eth", &mstp_clks[MSTP309]), + CLKDEV_DEV_ID("r8a7740-gether", &mstp_clks[MSTP309]), CLKDEV_DEV_ID("sh_mobile_sdhi.2", &mstp_clks[MSTP415]), CLKDEV_DEV_ID("e6870000.sdhi", &mstp_clks[MSTP415]), diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index e0568a9352e5..da620ecd4151 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -619,11 +619,9 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = { .irq_flags = IRQF_SHARED, #endif }; +#endif - -#elif defined(CONFIG_ARCH_R8A7740) - -static void sh_eth_chip_reset(struct net_device *ndev) +static void sh_eth_chip_reset_r8a7740(struct net_device *ndev) { struct sh_eth_private *mdp = netdev_priv(ndev); @@ -634,7 +632,7 @@ static void sh_eth_chip_reset(struct net_device *ndev) sh_eth_select_mii(ndev); } -static void sh_eth_set_rate(struct net_device *ndev) +static void sh_eth_set_rate_gether(struct net_device *ndev) { struct sh_eth_private *mdp = netdev_priv(ndev); @@ -654,10 +652,10 @@ static void sh_eth_set_rate(struct net_device *ndev) } /* R8A7740 */ -static struct sh_eth_cpu_data sh_eth_my_cpu_data = { - .chip_reset = sh_eth_chip_reset, +static struct sh_eth_cpu_data r8a7740_data = { + .chip_reset = sh_eth_chip_reset_r8a7740, .set_duplex = sh_eth_set_duplex, - .set_rate = sh_eth_set_rate, + .set_rate = sh_eth_set_rate_gether, .ecsr_value = ECSR_ICD | ECSR_MPD, .ecsipr_value = ECSIPR_LCHNGIP | ECSIPR_ICDIP | ECSIPR_MPDIP, @@ -680,7 +678,6 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = { .tsu = 1, .select_mii = 1, }; -#endif static struct sh_eth_cpu_data sh7619_data = { .eesipr_value = DMAC_M_RFRMER | DMAC_M_ECI | 0x003fffff, @@ -2701,6 +2698,7 @@ static const struct dev_pm_ops sh_eth_dev_pm_ops = { static struct platform_device_id sh_eth_id_table[] = { { "sh7619-ether", (kernel_ulong_t)&sh7619_data }, { "sh771x-ether", (kernel_ulong_t)&sh771x_data }, + { "r8a7740-gether", (kernel_ulong_t)&r8a7740_data }, { CARDNAME }, { } }; -- cgit v1.2.3 From f5d12767c8fd77e29d3d6771de59fd9ac3e540bb Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Fri, 7 Jun 2013 13:58:18 +0000 Subject: sh_eth: get SH77{34|63} support out of #ifdef Get the SH77{34|63} specific code/data in the driver out of #ifdef by adding "sh7734-gether" and "sh7763-gether" to the platform driver's ID table. Note that we have to split the 'struct sh_eth_cpu_data' instance into two due to #ifdef inside it; note that we can kill the duplicate sh_eth_set_rate_gether(). Change the GEther platform device's name in the SH platform code accordingly. Signed-off-by: Sergei Shtylyov Signed-off-by: David S. Miller --- arch/sh/boards/board-espt.c | 2 +- arch/sh/boards/mach-sh7763rdp/setup.c | 2 +- arch/sh/kernel/cpu/sh4a/clock-sh7734.c | 2 +- drivers/net/ethernet/renesas/sh_eth.c | 67 ++++++++++++++++++---------------- 4 files changed, 39 insertions(+), 34 deletions(-) diff --git a/arch/sh/boards/board-espt.c b/arch/sh/boards/board-espt.c index d71a0bcf8145..4d94dff9015c 100644 --- a/arch/sh/boards/board-espt.c +++ b/arch/sh/boards/board-espt.c @@ -85,7 +85,7 @@ static struct sh_eth_plat_data sh7763_eth_pdata = { }; static struct platform_device espt_eth_device = { - .name = "sh-eth", + .name = "sh7763-gether", .resource = sh_eth_resources, .num_resources = ARRAY_SIZE(sh_eth_resources), .dev = { diff --git a/arch/sh/boards/mach-sh7763rdp/setup.c b/arch/sh/boards/mach-sh7763rdp/setup.c index b7c75298dfb5..50ba481fa240 100644 --- a/arch/sh/boards/mach-sh7763rdp/setup.c +++ b/arch/sh/boards/mach-sh7763rdp/setup.c @@ -93,7 +93,7 @@ static struct sh_eth_plat_data sh7763_eth_pdata = { }; static struct platform_device sh7763rdp_eth_device = { - .name = "sh-eth", + .name = "sh7763-gether", .resource = sh_eth_resources, .num_resources = ARRAY_SIZE(sh_eth_resources), .dev = { diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7734.c b/arch/sh/kernel/cpu/sh4a/clock-sh7734.c index deb683abacf0..ed9501519ab3 100644 --- a/arch/sh/kernel/cpu/sh4a/clock-sh7734.c +++ b/arch/sh/kernel/cpu/sh4a/clock-sh7734.c @@ -238,7 +238,7 @@ static struct clk_lookup lookups[] = { CLKDEV_CON_ID("adc0", &mstp_clks[MSTP313]), CLKDEV_CON_ID("mtu0", &mstp_clks[MSTP312]), CLKDEV_CON_ID("iebus0", &mstp_clks[MSTP304]), - CLKDEV_DEV_ID("sh-eth.0", &mstp_clks[MSTP114]), + CLKDEV_DEV_ID("sh7734-gether.0", &mstp_clks[MSTP114]), CLKDEV_CON_ID("rtc0", &mstp_clks[MSTP303]), CLKDEV_CON_ID("hif0", &mstp_clks[MSTP302]), CLKDEV_CON_ID("stif0", &mstp_clks[MSTP301]), diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index da620ecd4151..6d44a43e5884 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -556,8 +556,7 @@ static struct sh_eth_cpu_data *sh_eth_get_cpu_data(struct sh_eth_private *mdp) else return &sh_eth_my_cpu_data; } - -#elif defined(CONFIG_CPU_SUBTYPE_SH7734) || defined(CONFIG_CPU_SUBTYPE_SH7763) +#endif static void sh_eth_chip_reset(struct net_device *ndev) { @@ -568,7 +567,7 @@ static void sh_eth_chip_reset(struct net_device *ndev) mdelay(1); } -static void sh_eth_set_rate(struct net_device *ndev) +static void sh_eth_set_rate_gether(struct net_device *ndev) { struct sh_eth_private *mdp = netdev_priv(ndev); @@ -587,11 +586,40 @@ static void sh_eth_set_rate(struct net_device *ndev) } } -/* sh7763 */ -static struct sh_eth_cpu_data sh_eth_my_cpu_data = { +/* SH7734 */ +static struct sh_eth_cpu_data sh7734_data = { .chip_reset = sh_eth_chip_reset, .set_duplex = sh_eth_set_duplex, - .set_rate = sh_eth_set_rate, + .set_rate = sh_eth_set_rate_gether, + + .ecsr_value = ECSR_ICD | ECSR_MPD, + .ecsipr_value = ECSIPR_LCHNGIP | ECSIPR_ICDIP | ECSIPR_MPDIP, + .eesipr_value = DMAC_M_RFRMER | DMAC_M_ECI | 0x003fffff, + + .tx_check = EESR_TC1 | EESR_FTC, + .eesr_err_check = EESR_TWB1 | EESR_TWB | EESR_TABT | EESR_RABT | \ + EESR_RDE | EESR_RFRMER | EESR_TFE | EESR_TDE | \ + EESR_ECI, + .tx_error_check = EESR_TWB1 | EESR_TWB | EESR_TABT | EESR_TDE | \ + EESR_TFE, + + .apr = 1, + .mpr = 1, + .tpauser = 1, + .bculr = 1, + .hw_swap = 1, + .no_trimd = 1, + .no_ade = 1, + .tsu = 1, + .hw_crc = 1, + .select_mii = 1, +}; + +/* SH7763 */ +static struct sh_eth_cpu_data sh7763_data = { + .chip_reset = sh_eth_chip_reset, + .set_duplex = sh_eth_set_duplex, + .set_rate = sh_eth_set_rate_gether, .ecsr_value = ECSR_ICD | ECSR_MPD, .ecsipr_value = ECSIPR_LCHNGIP | ECSIPR_ICDIP | ECSIPR_MPDIP, @@ -612,14 +640,8 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = { .no_trimd = 1, .no_ade = 1, .tsu = 1, -#if defined(CONFIG_CPU_SUBTYPE_SH7734) - .hw_crc = 1, - .select_mii = 1, -#else .irq_flags = IRQF_SHARED, -#endif }; -#endif static void sh_eth_chip_reset_r8a7740(struct net_device *ndev) { @@ -632,25 +654,6 @@ static void sh_eth_chip_reset_r8a7740(struct net_device *ndev) sh_eth_select_mii(ndev); } -static void sh_eth_set_rate_gether(struct net_device *ndev) -{ - struct sh_eth_private *mdp = netdev_priv(ndev); - - switch (mdp->speed) { - case 10: /* 10BASE */ - sh_eth_write(ndev, GECMR_10, GECMR); - break; - case 100:/* 100BASE */ - sh_eth_write(ndev, GECMR_100, GECMR); - break; - case 1000: /* 1000BASE */ - sh_eth_write(ndev, GECMR_1000, GECMR); - break; - default: - break; - } -} - /* R8A7740 */ static struct sh_eth_cpu_data r8a7740_data = { .chip_reset = sh_eth_chip_reset_r8a7740, @@ -2698,6 +2701,8 @@ static const struct dev_pm_ops sh_eth_dev_pm_ops = { static struct platform_device_id sh_eth_id_table[] = { { "sh7619-ether", (kernel_ulong_t)&sh7619_data }, { "sh771x-ether", (kernel_ulong_t)&sh771x_data }, + { "sh7734-gether", (kernel_ulong_t)&sh7734_data }, + { "sh7763-gether", (kernel_ulong_t)&sh7763_data }, { "r8a7740-gether", (kernel_ulong_t)&r8a7740_data }, { CARDNAME }, { } -- cgit v1.2.3 From 24549e2a0f33628b5160eac16c6aebf1cfaf22f1 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Fri, 7 Jun 2013 13:59:21 +0000 Subject: sh_eth: get SH7757 support out of #ifdef Get the SH7757 code/data in the driver out of #ifdef by adding "sh7757-ether" and "sh7757-gether" to the platform driver's ID table. Note that we can remove SH_ETH_HAS_BOTH_MODULES and sh_eth_get_cpu_data(). Change the Ether/GEther platform devices' names in the SH platform code accordingly. Signed-off-by: Sergei Shtylyov Signed-off-by: David S. Miller --- arch/sh/boards/board-sh7757lcr.c | 8 ++++---- drivers/net/ethernet/renesas/sh_eth.c | 28 ++++++++-------------------- 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/arch/sh/boards/board-sh7757lcr.c b/arch/sh/boards/board-sh7757lcr.c index 41f86702eb9f..4f114d1cd019 100644 --- a/arch/sh/boards/board-sh7757lcr.c +++ b/arch/sh/boards/board-sh7757lcr.c @@ -82,7 +82,7 @@ static struct sh_eth_plat_data sh7757_eth0_pdata = { }; static struct platform_device sh7757_eth0_device = { - .name = "sh-eth", + .name = "sh7757-ether", .resource = sh_eth0_resources, .id = 0, .num_resources = ARRAY_SIZE(sh_eth0_resources), @@ -111,7 +111,7 @@ static struct sh_eth_plat_data sh7757_eth1_pdata = { }; static struct platform_device sh7757_eth1_device = { - .name = "sh-eth", + .name = "sh7757-ether", .resource = sh_eth1_resources, .id = 1, .num_resources = ARRAY_SIZE(sh_eth1_resources), @@ -157,7 +157,7 @@ static struct sh_eth_plat_data sh7757_eth_giga0_pdata = { }; static struct platform_device sh7757_eth_giga0_device = { - .name = "sh-eth", + .name = "sh7757-gether", .resource = sh_eth_giga0_resources, .id = 2, .num_resources = ARRAY_SIZE(sh_eth_giga0_resources), @@ -192,7 +192,7 @@ static struct sh_eth_plat_data sh7757_eth_giga1_pdata = { }; static struct platform_device sh7757_eth_giga1_device = { - .name = "sh-eth", + .name = "sh7757-gether", .resource = sh_eth_giga1_resources, .id = 3, .num_resources = ARRAY_SIZE(sh_eth_giga1_resources), diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 6d44a43e5884..9860fc028f3d 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -431,10 +431,9 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = { .rpadir = 1, .rpadir_value = 0x00020000, /* NET_IP_ALIGN assumed to be 2 */ }; -#elif defined(CONFIG_CPU_SUBTYPE_SH7757) -#define SH_ETH_HAS_BOTH_MODULES 1 +#endif -static void sh_eth_set_rate(struct net_device *ndev) +static void sh_eth_set_rate_sh7757(struct net_device *ndev) { struct sh_eth_private *mdp = netdev_priv(ndev); @@ -451,9 +450,9 @@ static void sh_eth_set_rate(struct net_device *ndev) } /* SH7757 */ -static struct sh_eth_cpu_data sh_eth_my_cpu_data = { - .set_duplex = sh_eth_set_duplex, - .set_rate = sh_eth_set_rate, +static struct sh_eth_cpu_data sh7757_data = { + .set_duplex = sh_eth_set_duplex, + .set_rate = sh_eth_set_rate_sh7757, .eesipr_value = DMAC_M_RFRMER | DMAC_M_ECI | 0x003fffff, .rmcr_value = 0x00000001, @@ -518,7 +517,7 @@ static void sh_eth_set_rate_giga(struct net_device *ndev) } /* SH7757(GETHERC) */ -static struct sh_eth_cpu_data sh_eth_my_cpu_data_giga = { +static struct sh_eth_cpu_data sh7757_data_giga = { .chip_reset = sh_eth_chip_reset_giga, .set_duplex = sh_eth_set_duplex, .set_rate = sh_eth_set_rate_giga, @@ -549,15 +548,6 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data_giga = { .tsu = 1, }; -static struct sh_eth_cpu_data *sh_eth_get_cpu_data(struct sh_eth_private *mdp) -{ - if (sh_eth_is_gether(mdp)) - return &sh_eth_my_cpu_data_giga; - else - return &sh_eth_my_cpu_data; -} -#endif - static void sh_eth_chip_reset(struct net_device *ndev) { struct sh_eth_private *mdp = netdev_priv(ndev); @@ -2577,11 +2567,7 @@ static int sh_eth_drv_probe(struct platform_device *pdev) mdp->reg_offset = sh_eth_get_register_offset(pd->register_type); /* set cpu data */ -#if defined(SH_ETH_HAS_BOTH_MODULES) - mdp->cd = sh_eth_get_cpu_data(mdp); -#else mdp->cd = &sh_eth_my_cpu_data; -#endif if (id->driver_data) mdp->cd = (struct sh_eth_cpu_data *)id->driver_data; sh_eth_set_default_cpu_data(mdp->cd); @@ -2702,6 +2688,8 @@ static struct platform_device_id sh_eth_id_table[] = { { "sh7619-ether", (kernel_ulong_t)&sh7619_data }, { "sh771x-ether", (kernel_ulong_t)&sh771x_data }, { "sh7734-gether", (kernel_ulong_t)&sh7734_data }, + { "sh7757-ether", (kernel_ulong_t)&sh7757_data }, + { "sh7757-gether", (kernel_ulong_t)&sh7757_data_giga }, { "sh7763-gether", (kernel_ulong_t)&sh7763_data }, { "r8a7740-gether", (kernel_ulong_t)&r8a7740_data }, { CARDNAME }, -- cgit v1.2.3 From 9c3beaabb951d672b1534c7f56f84054b088f879 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Fri, 7 Jun 2013 14:03:37 +0000 Subject: sh_eth: get SH7724 support out of #ifdef Get the SH7724 code/data in the driver out of #ifdef by adding "r8a7724-ether" to the platform driver's ID table. Change the Ether platform device's name in the SH platform code accordingly. Signed-off-by: Sergei Shtylyov Signed-off-by: David S. Miller --- arch/sh/boards/mach-ecovec24/setup.c | 4 ++-- arch/sh/boards/mach-se/7724/setup.c | 4 ++-- arch/sh/kernel/cpu/sh4a/clock-sh7724.c | 2 +- drivers/net/ethernet/renesas/sh_eth.c | 10 +++++----- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/arch/sh/boards/mach-ecovec24/setup.c b/arch/sh/boards/mach-ecovec24/setup.c index 764530c85aa9..61fade0ffa96 100644 --- a/arch/sh/boards/mach-ecovec24/setup.c +++ b/arch/sh/boards/mach-ecovec24/setup.c @@ -165,8 +165,8 @@ static struct sh_eth_plat_data sh_eth_plat = { }; static struct platform_device sh_eth_device = { - .name = "sh-eth", - .id = 0, + .name = "sh7724-ether", + .id = 0, .dev = { .platform_data = &sh_eth_plat, }, diff --git a/arch/sh/boards/mach-se/7724/setup.c b/arch/sh/boards/mach-se/7724/setup.c index 4010e63e82d8..b70180ef3e29 100644 --- a/arch/sh/boards/mach-se/7724/setup.c +++ b/arch/sh/boards/mach-se/7724/setup.c @@ -380,8 +380,8 @@ static struct sh_eth_plat_data sh_eth_plat = { }; static struct platform_device sh_eth_device = { - .name = "sh-eth", - .id = 0, + .name = "sh7724-ether", + .id = 0, .dev = { .platform_data = &sh_eth_plat, }, diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7724.c b/arch/sh/kernel/cpu/sh4a/clock-sh7724.c index 5f30f805d2f2..0128af3399b7 100644 --- a/arch/sh/kernel/cpu/sh4a/clock-sh7724.c +++ b/arch/sh/kernel/cpu/sh4a/clock-sh7724.c @@ -329,7 +329,7 @@ static struct clk_lookup lookups[] = { CLKDEV_DEV_ID("i2c-sh_mobile.0", &mstp_clks[HWBLK_IIC0]), CLKDEV_DEV_ID("i2c-sh_mobile.1", &mstp_clks[HWBLK_IIC1]), CLKDEV_DEV_ID("sh_mmcif.0", &mstp_clks[HWBLK_MMC]), - CLKDEV_DEV_ID("sh-eth.0", &mstp_clks[HWBLK_ETHER]), + CLKDEV_DEV_ID("sh7724-ether.0", &mstp_clks[HWBLK_ETHER]), CLKDEV_CON_ID("atapi0", &mstp_clks[HWBLK_ATAPI]), CLKDEV_CON_ID("tpu0", &mstp_clks[HWBLK_TPU]), CLKDEV_CON_ID("irda0", &mstp_clks[HWBLK_IRDA]), diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 9860fc028f3d..143e22e1b329 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -392,9 +392,9 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = { .tpauser = 1, .hw_swap = 1, }; -#elif defined(CONFIG_CPU_SUBTYPE_SH7724) +#endif -static void sh_eth_set_rate(struct net_device *ndev) +static void sh_eth_set_rate_sh7724(struct net_device *ndev) { struct sh_eth_private *mdp = netdev_priv(ndev); @@ -411,9 +411,9 @@ static void sh_eth_set_rate(struct net_device *ndev) } /* SH7724 */ -static struct sh_eth_cpu_data sh_eth_my_cpu_data = { +static struct sh_eth_cpu_data sh7724_data = { .set_duplex = sh_eth_set_duplex, - .set_rate = sh_eth_set_rate, + .set_rate = sh_eth_set_rate_sh7724, .ecsr_value = ECSR_PSRTO | ECSR_LCHNG | ECSR_ICD, .ecsipr_value = ECSIPR_PSRTOIP | ECSIPR_LCHNGIP | ECSIPR_ICDIP, @@ -431,7 +431,6 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = { .rpadir = 1, .rpadir_value = 0x00020000, /* NET_IP_ALIGN assumed to be 2 */ }; -#endif static void sh_eth_set_rate_sh7757(struct net_device *ndev) { @@ -2687,6 +2686,7 @@ static const struct dev_pm_ops sh_eth_dev_pm_ops = { static struct platform_device_id sh_eth_id_table[] = { { "sh7619-ether", (kernel_ulong_t)&sh7619_data }, { "sh771x-ether", (kernel_ulong_t)&sh771x_data }, + { "sh7724-ether", (kernel_ulong_t)&sh7724_data }, { "sh7734-gether", (kernel_ulong_t)&sh7734_data }, { "sh7757-ether", (kernel_ulong_t)&sh7757_data }, { "sh7757-gether", (kernel_ulong_t)&sh7757_data_giga }, -- cgit v1.2.3 From 589ebdef7e3107401bf96a9c660753d397329ee9 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Fri, 7 Jun 2013 14:05:59 +0000 Subject: sh_eth: get R8A777x support out of #ifdef Get the R-Car code/data in the driver out of #ifdef by adding "r8a777x-ether" to the platfrom driver's ID table; since it's the last #ifdef, we remove CARDNAME from the ID table and no longer check the driver data before assigning it to 'mdp->cd'... Change the Ether platform device's name in the ARM platform code accordingly. Signed-off-by: Sergei Shtylyov Signed-off-by: David S. Miller --- arch/arm/mach-shmobile/clock-r8a7778.c | 2 +- arch/arm/mach-shmobile/clock-r8a7779.c | 2 +- drivers/net/ethernet/renesas/sh_eth.c | 14 +++++--------- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/arch/arm/mach-shmobile/clock-r8a7778.c b/arch/arm/mach-shmobile/clock-r8a7778.c index cd6855290b1f..9614b07254b2 100644 --- a/arch/arm/mach-shmobile/clock-r8a7778.c +++ b/arch/arm/mach-shmobile/clock-r8a7778.c @@ -77,7 +77,7 @@ static struct clk mstp_clks[MSTP_NR] = { static struct clk_lookup lookups[] = { /* MSTP32 clocks */ - CLKDEV_DEV_ID("sh-eth", &mstp_clks[MSTP114]), /* Ether */ + CLKDEV_DEV_ID("r8a777x-ether", &mstp_clks[MSTP114]), /* Ether */ CLKDEV_DEV_ID("sh-sci.0", &mstp_clks[MSTP026]), /* SCIF0 */ CLKDEV_DEV_ID("sh-sci.1", &mstp_clks[MSTP025]), /* SCIF1 */ CLKDEV_DEV_ID("sh-sci.2", &mstp_clks[MSTP024]), /* SCIF2 */ diff --git a/arch/arm/mach-shmobile/clock-r8a7779.c b/arch/arm/mach-shmobile/clock-r8a7779.c index 31d5cd4d9787..2f7e5245a690 100644 --- a/arch/arm/mach-shmobile/clock-r8a7779.c +++ b/arch/arm/mach-shmobile/clock-r8a7779.c @@ -163,7 +163,7 @@ static struct clk_lookup lookups[] = { /* MSTP32 clocks */ CLKDEV_DEV_ID("sata_rcar", &mstp_clks[MSTP115]), /* SATA */ CLKDEV_DEV_ID("fc600000.sata", &mstp_clks[MSTP115]), /* SATA w/DT */ - CLKDEV_DEV_ID("sh-eth", &mstp_clks[MSTP114]), /* Ether */ + CLKDEV_DEV_ID("r8a777x-ether", &mstp_clks[MSTP114]), /* Ether */ CLKDEV_DEV_ID("ehci-platform.1", &mstp_clks[MSTP101]), /* USB EHCI port2 */ CLKDEV_DEV_ID("ohci-platform.1", &mstp_clks[MSTP101]), /* USB OHCI port2 */ CLKDEV_DEV_ID("ehci-platform.0", &mstp_clks[MSTP100]), /* USB EHCI port0/1 */ diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 143e22e1b329..cff697118119 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -356,8 +356,7 @@ static void __maybe_unused sh_eth_set_duplex(struct net_device *ndev) } /* There is CPU dependent code */ -#if defined(CONFIG_ARCH_R8A7778) || defined(CONFIG_ARCH_R8A7779) -static void sh_eth_set_rate(struct net_device *ndev) +static void sh_eth_set_rate_r8a777x(struct net_device *ndev) { struct sh_eth_private *mdp = netdev_priv(ndev); @@ -374,9 +373,9 @@ static void sh_eth_set_rate(struct net_device *ndev) } /* R8A7778/9 */ -static struct sh_eth_cpu_data sh_eth_my_cpu_data = { +static struct sh_eth_cpu_data r8a777x_data = { .set_duplex = sh_eth_set_duplex, - .set_rate = sh_eth_set_rate, + .set_rate = sh_eth_set_rate_r8a777x, .ecsr_value = ECSR_PSRTO | ECSR_LCHNG | ECSR_ICD, .ecsipr_value = ECSIPR_PSRTOIP | ECSIPR_LCHNGIP | ECSIPR_ICDIP, @@ -392,7 +391,6 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = { .tpauser = 1, .hw_swap = 1, }; -#endif static void sh_eth_set_rate_sh7724(struct net_device *ndev) { @@ -2566,9 +2564,7 @@ static int sh_eth_drv_probe(struct platform_device *pdev) mdp->reg_offset = sh_eth_get_register_offset(pd->register_type); /* set cpu data */ - mdp->cd = &sh_eth_my_cpu_data; - if (id->driver_data) - mdp->cd = (struct sh_eth_cpu_data *)id->driver_data; + mdp->cd = (struct sh_eth_cpu_data *)id->driver_data; sh_eth_set_default_cpu_data(mdp->cd); /* set function */ @@ -2692,7 +2688,7 @@ static struct platform_device_id sh_eth_id_table[] = { { "sh7757-gether", (kernel_ulong_t)&sh7757_data_giga }, { "sh7763-gether", (kernel_ulong_t)&sh7763_data }, { "r8a7740-gether", (kernel_ulong_t)&r8a7740_data }, - { CARDNAME }, + { "r8a777x-ether", (kernel_ulong_t)&r8a777x_data }, { } }; MODULE_DEVICE_TABLE(platform, sh_eth_id_table); -- cgit v1.2.3 From eb770bf430bc1d4e1d23af353a80cfae2b237e66 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Fri, 7 Jun 2013 14:07:13 +0000 Subject: sh_eth: remove dependencies from Kconfig Since dependence on the certain SoCs is no longer necessary to compile the driver, remove the dependency list from its Kconfig entry which is a popular demand anyway... Signed-off-by: Sergei Shtylyov Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/Kconfig | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/net/ethernet/renesas/Kconfig b/drivers/net/ethernet/renesas/Kconfig index bed9841d728c..267eac054fa4 100644 --- a/drivers/net/ethernet/renesas/Kconfig +++ b/drivers/net/ethernet/renesas/Kconfig @@ -4,12 +4,6 @@ config SH_ETH tristate "Renesas SuperH Ethernet support" - depends on (SUPERH || ARCH_SHMOBILE) && \ - (CPU_SUBTYPE_SH7710 || CPU_SUBTYPE_SH7712 || \ - CPU_SUBTYPE_SH7763 || CPU_SUBTYPE_SH7619 || \ - CPU_SUBTYPE_SH7724 || CPU_SUBTYPE_SH7734 || \ - CPU_SUBTYPE_SH7757 || ARCH_R8A7740 || \ - ARCH_R8A7778 || ARCH_R8A7779) select CRC32 select NET_CORE select MII -- cgit v1.2.3 From e403d295817cf9f8be3110eb7ee1a03981287495 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 7 Jun 2013 23:40:41 -0700 Subject: sh_eth: Fix warnings on 64-bit. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Don't cast a plain integer to a pointer. drivers/net/ethernet/renesas/sh_eth.c: In function ‘sh_eth_chip_reset_giga’: drivers/net/ethernet/renesas/sh_eth.c:482:22: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast] drivers/net/ethernet/renesas/sh_eth.c:483:22: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast] drivers/net/ethernet/renesas/sh_eth.c:492:22: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast] drivers/net/ethernet/renesas/sh_eth.c:493:22: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast] Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/sh_eth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index cff697118119..43d8490cfe0d 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -469,7 +469,7 @@ static struct sh_eth_cpu_data sh7757_data = { .rpadir_value = 2 << 16, }; -#define SH_GIGA_ETH_BASE 0xfee00000 +#define SH_GIGA_ETH_BASE 0xfee00000UL #define GIGA_MALR(port) (SH_GIGA_ETH_BASE + 0x800 * (port) + 0x05c8) #define GIGA_MAHR(port) (SH_GIGA_ETH_BASE + 0x800 * (port) + 0x05c0) static void sh_eth_chip_reset_giga(struct net_device *ndev) -- cgit v1.2.3 From ed0483fa06e0efb86a82e382a00dbad02b62807c Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 5 Jun 2013 23:54:33 +0000 Subject: macvtap: fix a possible race between queue selection and changing queues Complier may generate codes that re-read the vlan->numvtaps during macvtap_get_queue(). This may lead a race if vlan->numvtaps were changed in the same time and which can lead unexpected result (e.g. very huge value). We need prevent the compiler from generating such codes by adding an ACCESS_ONCE() to make sure vlan->numvtaps were only read once. Acked-by: Michael S. Tsirkin Signed-off-by: Jason Wang Signed-off-by: David S. Miller --- drivers/net/macvtap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index 68efb91a5633..5e485e307424 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -172,7 +172,7 @@ static struct macvtap_queue *macvtap_get_queue(struct net_device *dev, { struct macvlan_dev *vlan = netdev_priv(dev); struct macvtap_queue *tap = NULL; - int numvtaps = vlan->numvtaps; + int numvtaps = ACCESS_ONCE(vlan->numvtaps); __u32 rxq; if (!numvtaps) -- cgit v1.2.3 From 89cee917deb5bf0dc9da269a38622588d06cb6be Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 5 Jun 2013 23:54:34 +0000 Subject: macvtap: do not add self to waitqueue if doing a nonblock read There's no need to add self to waitqueue if doing a nonblock read. This could help to avoid the spinlock contention. Acked-by: Michael S. Tsirkin Signed-off-by: Jason Wang Signed-off-by: David S. Miller --- drivers/net/macvtap.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index 5e485e307424..8949631c080c 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -845,7 +845,9 @@ static ssize_t macvtap_do_read(struct macvtap_queue *q, struct kiocb *iocb, ssize_t ret = 0; while (len) { - prepare_to_wait(sk_sleep(&q->sk), &wait, TASK_INTERRUPTIBLE); + if (!noblock) + prepare_to_wait(sk_sleep(&q->sk), &wait, + TASK_INTERRUPTIBLE); /* Read frames from the queue */ skb = skb_dequeue(&q->sk.sk_receive_queue); @@ -867,7 +869,8 @@ static ssize_t macvtap_do_read(struct macvtap_queue *q, struct kiocb *iocb, break; } - finish_wait(sk_sleep(&q->sk), &wait); + if (!noblock) + finish_wait(sk_sleep(&q->sk), &wait); return ret; } -- cgit v1.2.3 From 1d2f41ed23343e083566339574807ca7ea75dbba Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 5 Jun 2013 23:54:35 +0000 Subject: macvlan: switch to use IS_ENABLED() Acked-by: Michael S. Tsirkin Signed-off-by: Jason Wang Signed-off-by: David S. Miller --- include/linux/if_macvlan.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/if_macvlan.h b/include/linux/if_macvlan.h index 84dde1dd1da4..e47ad46a477c 100644 --- a/include/linux/if_macvlan.h +++ b/include/linux/if_macvlan.h @@ -8,7 +8,7 @@ #include #include -#if defined(CONFIG_MACVTAP) || defined(CONFIG_MACVTAP_MODULE) +#if IS_ENABLED(CONFIG_MACVTAP) struct socket *macvtap_get_socket(struct file *); #else #include -- cgit v1.2.3 From 8f475a318aaa37f0168a8ab99f2faa251e5638de Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 5 Jun 2013 23:54:36 +0000 Subject: macvtap: introduce macvtap_get_vlan() Factor out the device holding logic to a macvtap_get_vlan(), this will be also used by multiqueue API. Signed-off-by: Jason Wang Acked-by: Michael S. Tsirkin Signed-off-by: David S. Miller --- drivers/net/macvtap.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index 8949631c080c..d18130b3fe0d 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -893,6 +893,24 @@ out: return ret; } +static struct macvlan_dev *macvtap_get_vlan(struct macvtap_queue *q) +{ + struct macvlan_dev *vlan; + + rcu_read_lock_bh(); + vlan = rcu_dereference_bh(q->vlan); + if (vlan) + dev_hold(vlan->dev); + rcu_read_unlock_bh(); + + return vlan; +} + +static void macvtap_put_vlan(struct macvlan_dev *vlan) +{ + dev_put(vlan->dev); +} + /* * provide compatibility with generic tun/tap interface */ @@ -924,12 +942,7 @@ static long macvtap_ioctl(struct file *file, unsigned int cmd, return ret; case TUNGETIFF: - rcu_read_lock_bh(); - vlan = rcu_dereference_bh(q->vlan); - if (vlan) - dev_hold(vlan->dev); - rcu_read_unlock_bh(); - + vlan = macvtap_get_vlan(q); if (!vlan) return -ENOLINK; @@ -937,7 +950,7 @@ static long macvtap_ioctl(struct file *file, unsigned int cmd, if (copy_to_user(&ifr->ifr_name, vlan->dev->name, IFNAMSIZ) || put_user(q->flags, &ifr->ifr_flags)) ret = -EFAULT; - dev_put(vlan->dev); + macvtap_put_vlan(vlan); return ret; case TUNGETFEATURES: -- cgit v1.2.3 From f0afce01aa639e5164cf73f063b81a8b95619c3a Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 5 Jun 2013 23:54:37 +0000 Subject: macvlan: change the max number of queues to 16 Macvtap should be at least compatible with tap, so change the max number to 16. Signed-off-by: Jason Wang Acked-by: Michael S. Tsirkin Signed-off-by: David S. Miller --- include/linux/if_macvlan.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/if_macvlan.h b/include/linux/if_macvlan.h index e47ad46a477c..62d8bda67874 100644 --- a/include/linux/if_macvlan.h +++ b/include/linux/if_macvlan.h @@ -50,7 +50,7 @@ struct macvlan_pcpu_stats { * Maximum times a macvtap device can be opened. This can be used to * configure the number of receive queue, e.g. for multiqueue virtio. */ -#define MAX_MACVTAP_QUEUES (NR_CPUS < 16 ? NR_CPUS : 16) +#define MAX_MACVTAP_QUEUES 16 #define MACVLAN_MC_FILTER_BITS 8 #define MACVLAN_MC_FILTER_SZ (1 << MACVLAN_MC_FILTER_BITS) -- cgit v1.2.3 From 376b1aabe1f53aad80615d05ddb8c43670be6a5c Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 5 Jun 2013 23:54:38 +0000 Subject: macvtap: eliminate linear search Linear search were used in both get_slot() and macvtap_get_queue(), this is because: - macvtap didn't reshuffle the array of taps when create or destroy a queue, so when adding a new queue, macvtap must do linear search to find a location for the new queue. This will also complicate the TUNSETQUEUE implementation for multiqueue API. - the queue itself didn't track the queue index, so the we must do a linear search in the array to find the location of a existed queue. The solution is straightforward: reshuffle the array and introduce a queue_index to macvtap_queue. Signed-off-by: Jason Wang Acked-by: Michael S. Tsirkin Signed-off-by: David S. Miller --- drivers/net/macvtap.c | 64 ++++++++++++++++----------------------------------- 1 file changed, 20 insertions(+), 44 deletions(-) diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index d18130b3fe0d..5ccba99b15f4 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -44,6 +44,7 @@ struct macvtap_queue { struct macvlan_dev __rcu *vlan; struct file *file; unsigned int flags; + u16 queue_index; }; static struct proto macvtap_proto = { @@ -84,30 +85,10 @@ static const struct proto_ops macvtap_socket_ops; */ static DEFINE_SPINLOCK(macvtap_lock); -/* - * get_slot: return a [unused/occupied] slot in vlan->taps[]: - * - if 'q' is NULL, return the first empty slot; - * - otherwise, return the slot this pointer occupies. - */ -static int get_slot(struct macvlan_dev *vlan, struct macvtap_queue *q) -{ - int i; - - for (i = 0; i < MAX_MACVTAP_QUEUES; i++) { - if (rcu_dereference_protected(vlan->taps[i], - lockdep_is_held(&macvtap_lock)) == q) - return i; - } - - /* Should never happen */ - BUG_ON(1); -} - static int macvtap_set_queue(struct net_device *dev, struct file *file, struct macvtap_queue *q) { struct macvlan_dev *vlan = netdev_priv(dev); - int index; int err = -EBUSY; spin_lock(&macvtap_lock); @@ -115,12 +96,12 @@ static int macvtap_set_queue(struct net_device *dev, struct file *file, goto out; err = 0; - index = get_slot(vlan, NULL); rcu_assign_pointer(q->vlan, vlan); - rcu_assign_pointer(vlan->taps[index], q); + rcu_assign_pointer(vlan->taps[vlan->numvtaps], q); sock_hold(&q->sk); q->file = file; + q->queue_index = vlan->numvtaps; file->private_data = q; vlan->numvtaps++; @@ -140,15 +121,22 @@ out: */ static void macvtap_put_queue(struct macvtap_queue *q) { + struct macvtap_queue *nq; struct macvlan_dev *vlan; spin_lock(&macvtap_lock); vlan = rcu_dereference_protected(q->vlan, lockdep_is_held(&macvtap_lock)); if (vlan) { - int index = get_slot(vlan, q); + int index = q->queue_index; + BUG_ON(index >= vlan->numvtaps); + + nq = rcu_dereference_protected(vlan->taps[vlan->numvtaps - 1], + lockdep_is_held(&macvtap_lock)); + rcu_assign_pointer(vlan->taps[index], nq); + nq->queue_index = index; - RCU_INIT_POINTER(vlan->taps[index], NULL); + RCU_INIT_POINTER(vlan->taps[vlan->numvtaps - 1], NULL); RCU_INIT_POINTER(q->vlan, NULL); sock_put(&q->sk); --vlan->numvtaps; @@ -182,8 +170,7 @@ static struct macvtap_queue *macvtap_get_queue(struct net_device *dev, rxq = skb_get_rxhash(skb); if (rxq) { tap = rcu_dereference(vlan->taps[rxq % numvtaps]); - if (tap) - goto out; + goto out; } if (likely(skb_rx_queue_recorded(skb))) { @@ -193,17 +180,10 @@ static struct macvtap_queue *macvtap_get_queue(struct net_device *dev, rxq -= numvtaps; tap = rcu_dereference(vlan->taps[rxq]); - if (tap) - goto out; - } - - /* Everything failed - find first available queue */ - for (rxq = 0; rxq < MAX_MACVTAP_QUEUES; rxq++) { - tap = rcu_dereference(vlan->taps[rxq]); - if (tap) - break; + goto out; } + tap = rcu_dereference(vlan->taps[0]); out: return tap; } @@ -219,19 +199,15 @@ static void macvtap_del_queues(struct net_device *dev) struct macvtap_queue *q, *qlist[MAX_MACVTAP_QUEUES]; int i, j = 0; - /* macvtap_put_queue can free some slots, so go through all slots */ spin_lock(&macvtap_lock); - for (i = 0; i < MAX_MACVTAP_QUEUES && vlan->numvtaps; i++) { + for (i = 0; i < vlan->numvtaps; i++) { q = rcu_dereference_protected(vlan->taps[i], lockdep_is_held(&macvtap_lock)); - if (q) { - qlist[j++] = q; - RCU_INIT_POINTER(vlan->taps[i], NULL); - RCU_INIT_POINTER(q->vlan, NULL); - vlan->numvtaps--; - } + BUG_ON(q == NULL); + qlist[j++] = q; + RCU_INIT_POINTER(vlan->taps[i], NULL); + RCU_INIT_POINTER(q->vlan, NULL); } - BUG_ON(vlan->numvtaps != 0); /* guarantee that any future macvtap_set_queue will fail */ vlan->numvtaps = MAX_MACVTAP_QUEUES; spin_unlock(&macvtap_lock); -- cgit v1.2.3 From 815f236d622721b54f3963ba59dad98b02cdeabf Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 5 Jun 2013 23:54:39 +0000 Subject: macvtap: add TUNSETQUEUE ioctl This patch adds TUNSETQUEUE ioctl to let userspace can temporarily disable or enable a queue of macvtap. This is used to be compatible at API layer of tuntap to simplify the userspace to manage the queues. This is done through introducing a linked list to track all taps while using vlan->taps array to only track active taps. Signed-off-by: Jason Wang Acked-by: Michael S. Tsirkin Signed-off-by: David S. Miller --- drivers/net/macvtap.c | 135 ++++++++++++++++++++++++++++++++++++++------- include/linux/if_macvlan.h | 4 ++ 2 files changed, 120 insertions(+), 19 deletions(-) diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index 5ccba99b15f4..d2d1d5578b61 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -45,6 +45,8 @@ struct macvtap_queue { struct file *file; unsigned int flags; u16 queue_index; + bool enabled; + struct list_head next; }; static struct proto macvtap_proto = { @@ -85,14 +87,36 @@ static const struct proto_ops macvtap_socket_ops; */ static DEFINE_SPINLOCK(macvtap_lock); -static int macvtap_set_queue(struct net_device *dev, struct file *file, +static int macvtap_enable_queue(struct net_device *dev, struct file *file, struct macvtap_queue *q) +{ + struct macvlan_dev *vlan = netdev_priv(dev); + int err = -EINVAL; + + spin_lock(&macvtap_lock); + + if (q->enabled) + goto out; + + err = 0; + rcu_assign_pointer(vlan->taps[vlan->numvtaps], q); + q->queue_index = vlan->numvtaps; + q->enabled = true; + + vlan->numvtaps++; +out: + spin_unlock(&macvtap_lock); + return err; +} + +static int macvtap_set_queue(struct net_device *dev, struct file *file, + struct macvtap_queue *q) { struct macvlan_dev *vlan = netdev_priv(dev); int err = -EBUSY; spin_lock(&macvtap_lock); - if (vlan->numvtaps == MAX_MACVTAP_QUEUES) + if (vlan->numqueues == MAX_MACVTAP_QUEUES) goto out; err = 0; @@ -102,15 +126,57 @@ static int macvtap_set_queue(struct net_device *dev, struct file *file, q->file = file; q->queue_index = vlan->numvtaps; + q->enabled = true; file->private_data = q; + list_add_tail(&q->next, &vlan->queue_list); vlan->numvtaps++; + vlan->numqueues++; out: spin_unlock(&macvtap_lock); return err; } +static int __macvtap_disable_queue(struct macvtap_queue *q) +{ + struct macvlan_dev *vlan; + struct macvtap_queue *nq; + + vlan = rcu_dereference_protected(q->vlan, + lockdep_is_held(&macvtap_lock)); + + if (!q->enabled) + return -EINVAL; + + if (vlan) { + int index = q->queue_index; + BUG_ON(index >= vlan->numvtaps); + nq = rcu_dereference_protected(vlan->taps[vlan->numvtaps - 1], + lockdep_is_held(&macvtap_lock)); + nq->queue_index = index; + + rcu_assign_pointer(vlan->taps[index], nq); + RCU_INIT_POINTER(vlan->taps[vlan->numvtaps - 1], NULL); + q->enabled = false; + + vlan->numvtaps--; + } + + return 0; +} + +static int macvtap_disable_queue(struct macvtap_queue *q) +{ + int err; + + spin_lock(&macvtap_lock); + err = __macvtap_disable_queue(q); + spin_unlock(&macvtap_lock); + + return err; +} + /* * The file owning the queue got closed, give up both * the reference that the files holds as well as the @@ -121,25 +187,19 @@ out: */ static void macvtap_put_queue(struct macvtap_queue *q) { - struct macvtap_queue *nq; struct macvlan_dev *vlan; spin_lock(&macvtap_lock); vlan = rcu_dereference_protected(q->vlan, lockdep_is_held(&macvtap_lock)); if (vlan) { - int index = q->queue_index; - BUG_ON(index >= vlan->numvtaps); - - nq = rcu_dereference_protected(vlan->taps[vlan->numvtaps - 1], - lockdep_is_held(&macvtap_lock)); - rcu_assign_pointer(vlan->taps[index], nq); - nq->queue_index = index; + if (q->enabled) + BUG_ON(__macvtap_disable_queue(q)); - RCU_INIT_POINTER(vlan->taps[vlan->numvtaps - 1], NULL); + vlan->numqueues--; RCU_INIT_POINTER(q->vlan, NULL); sock_put(&q->sk); - --vlan->numvtaps; + list_del_init(&q->next); } spin_unlock(&macvtap_lock); @@ -160,6 +220,11 @@ static struct macvtap_queue *macvtap_get_queue(struct net_device *dev, { struct macvlan_dev *vlan = netdev_priv(dev); struct macvtap_queue *tap = NULL; + /* Access to taps array is protected by rcu, but access to numvtaps + * isn't. Below we use it to lookup a queue, but treat it as a hint + * and validate that the result isn't NULL - in case we are + * racing against queue removal. + */ int numvtaps = ACCESS_ONCE(vlan->numvtaps); __u32 rxq; @@ -196,18 +261,22 @@ out: static void macvtap_del_queues(struct net_device *dev) { struct macvlan_dev *vlan = netdev_priv(dev); - struct macvtap_queue *q, *qlist[MAX_MACVTAP_QUEUES]; + struct macvtap_queue *q, *tmp, *qlist[MAX_MACVTAP_QUEUES]; int i, j = 0; spin_lock(&macvtap_lock); - for (i = 0; i < vlan->numvtaps; i++) { - q = rcu_dereference_protected(vlan->taps[i], - lockdep_is_held(&macvtap_lock)); - BUG_ON(q == NULL); + list_for_each_entry_safe(q, tmp, &vlan->queue_list, next) { + list_del_init(&q->next); qlist[j++] = q; - RCU_INIT_POINTER(vlan->taps[i], NULL); RCU_INIT_POINTER(q->vlan, NULL); + if (q->enabled) + vlan->numvtaps--; + vlan->numqueues--; } + for (i = 0; i < vlan->numvtaps; i++) + RCU_INIT_POINTER(vlan->taps[i], NULL); + BUG_ON(vlan->numvtaps); + BUG_ON(vlan->numqueues); /* guarantee that any future macvtap_set_queue will fail */ vlan->numvtaps = MAX_MACVTAP_QUEUES; spin_unlock(&macvtap_lock); @@ -298,6 +367,9 @@ static int macvtap_newlink(struct net *src_net, struct nlattr *tb[], struct nlattr *data[]) { + struct macvlan_dev *vlan = netdev_priv(dev); + INIT_LIST_HEAD(&vlan->queue_list); + /* Don't put anything that may fail after macvlan_common_newlink * because we can't undo what it does. */ @@ -887,6 +959,25 @@ static void macvtap_put_vlan(struct macvlan_dev *vlan) dev_put(vlan->dev); } +static int macvtap_ioctl_set_queue(struct file *file, unsigned int flags) +{ + struct macvtap_queue *q = file->private_data; + struct macvlan_dev *vlan; + int ret; + + vlan = macvtap_get_vlan(q); + if (!vlan) + return -EINVAL; + + if (flags & IFF_ATTACH_QUEUE) + ret = macvtap_enable_queue(vlan->dev, file, q); + else if (flags & IFF_DETACH_QUEUE) + ret = macvtap_disable_queue(q); + + macvtap_put_vlan(vlan); + return ret; +} + /* * provide compatibility with generic tun/tap interface */ @@ -910,7 +1001,8 @@ static long macvtap_ioctl(struct file *file, unsigned int cmd, return -EFAULT; ret = 0; - if ((u & ~IFF_VNET_HDR) != (IFF_NO_PI | IFF_TAP)) + if ((u & ~(IFF_VNET_HDR | IFF_MULTI_QUEUE)) != + (IFF_NO_PI | IFF_TAP)) ret = -EINVAL; else q->flags = u; @@ -929,6 +1021,11 @@ static long macvtap_ioctl(struct file *file, unsigned int cmd, macvtap_put_vlan(vlan); return ret; + case TUNSETQUEUE: + if (get_user(u, &ifr->ifr_flags)) + return -EFAULT; + return macvtap_ioctl_set_queue(file, u); + case TUNGETFEATURES: if (put_user(IFF_TAP | IFF_NO_PI | IFF_VNET_HDR, up)) return -EFAULT; diff --git a/include/linux/if_macvlan.h b/include/linux/if_macvlan.h index 62d8bda67874..19121331a5ed 100644 --- a/include/linux/if_macvlan.h +++ b/include/linux/if_macvlan.h @@ -69,8 +69,12 @@ struct macvlan_dev { u16 flags; int (*receive)(struct sk_buff *skb); int (*forward)(struct net_device *dev, struct sk_buff *skb); + /* This array tracks active taps. */ struct macvtap_queue *taps[MAX_MACVTAP_QUEUES]; + /* This list tracks all taps (both enabled and disabled) */ + struct list_head queue_list; int numvtaps; + int numqueues; int minor; }; -- cgit v1.2.3 From df09b36f2225b84571b3d5eda2e2683412320713 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Wed, 5 Jun 2013 23:54:40 +0000 Subject: macvtap: enable multiqueue flag To notify the userspace about our capability of multiqueue. Signed-off-by: Jason Wang Acked-by: Michael S. Tsirkin Signed-off-by: David S. Miller --- drivers/net/macvtap.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index d2d1d5578b61..992151ce08a4 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -31,10 +31,6 @@ * macvtap_proto is used to allocate queues through the sock allocation * mechanism. * - * TODO: multiqueue support is currently not implemented, even though - * macvtap is basically prepared for that. We will need to add this - * here as well as in virtio-net and qemu to get line rate on 10gbit - * adapters from a guest. */ struct macvtap_queue { struct sock sk; @@ -1027,7 +1023,8 @@ static long macvtap_ioctl(struct file *file, unsigned int cmd, return macvtap_ioctl_set_queue(file, u); case TUNGETFEATURES: - if (put_user(IFF_TAP | IFF_NO_PI | IFF_VNET_HDR, up)) + if (put_user(IFF_TAP | IFF_NO_PI | IFF_VNET_HDR | + IFF_MULTI_QUEUE, up)) return -EFAULT; return 0; -- cgit v1.2.3 From 1c5aafa6eee2d5712f774676d407e5ab6dae9a1b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 16 Apr 2013 14:14:52 +0200 Subject: drm/gem: Split drm_gem_mmap() into object search and object mapping The drm_gem_mmap() function first finds the GEM object to be mapped based on the fake mmap offset and then maps the object. Split the object mapping code into a standalone drm_gem_mmap_obj() function that can be used to implement dma-buf mmap() operations. Signed-off-by: Laurent Pinchart Reviewed-by: Rob Clark --- drivers/gpu/drm/drm_gem.c | 83 +++++++++++++++++++++++++++++------------------ include/drm/drmP.h | 2 ++ 2 files changed, 54 insertions(+), 31 deletions(-) diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index cf919e36e8ae..43217138816d 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -644,6 +644,55 @@ void drm_gem_vm_close(struct vm_area_struct *vma) } EXPORT_SYMBOL(drm_gem_vm_close); +/** + * drm_gem_mmap_obj - memory map a GEM object + * @obj: the GEM object to map + * @obj_size: the object size to be mapped, in bytes + * @vma: VMA for the area to be mapped + * + * Set up the VMA to prepare mapping of the GEM object using the gem_vm_ops + * provided by the driver. Depending on their requirements, drivers can either + * provide a fault handler in their gem_vm_ops (in which case any accesses to + * the object will be trapped, to perform migration, GTT binding, surface + * register allocation, or performance monitoring), or mmap the buffer memory + * synchronously after calling drm_gem_mmap_obj. + * + * This function is mainly intended to implement the DMABUF mmap operation, when + * the GEM object is not looked up based on its fake offset. To implement the + * DRM mmap operation, drivers should use the drm_gem_mmap() function. + * + * Return 0 or success or -EINVAL if the object size is smaller than the VMA + * size, or if no gem_vm_ops are provided. + */ +int drm_gem_mmap_obj(struct drm_gem_object *obj, unsigned long obj_size, + struct vm_area_struct *vma) +{ + struct drm_device *dev = obj->dev; + + /* Check for valid size. */ + if (obj_size < vma->vm_end - vma->vm_start) + return -EINVAL; + + if (!dev->driver->gem_vm_ops) + return -EINVAL; + + vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP; + vma->vm_ops = dev->driver->gem_vm_ops; + vma->vm_private_data = obj; + vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags)); + + /* Take a ref for this mapping of the object, so that the fault + * handler can dereference the mmap offset's pointer to the object. + * This reference is cleaned up by the corresponding vm_close + * (which should happen whether the vma was created by this call, or + * by a vm_open due to mremap or partial unmap or whatever). + */ + drm_gem_object_reference(obj); + + drm_vm_open_locked(dev, vma); + return 0; +} +EXPORT_SYMBOL(drm_gem_mmap_obj); /** * drm_gem_mmap - memory map routine for GEM objects @@ -653,11 +702,9 @@ EXPORT_SYMBOL(drm_gem_vm_close); * If a driver supports GEM object mapping, mmap calls on the DRM file * descriptor will end up here. * - * If we find the object based on the offset passed in (vma->vm_pgoff will + * Look up the GEM object based on the offset passed in (vma->vm_pgoff will * contain the fake offset we created when the GTT map ioctl was called on - * the object), we set up the driver fault handler so that any accesses - * to the object can be trapped, to perform migration, GTT binding, surface - * register allocation, or performance monitoring. + * the object) and map it with a call to drm_gem_mmap_obj(). */ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) { @@ -665,7 +712,6 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) struct drm_device *dev = priv->minor->dev; struct drm_gem_mm *mm = dev->mm_private; struct drm_local_map *map = NULL; - struct drm_gem_object *obj; struct drm_hash_item *hash; int ret = 0; @@ -686,32 +732,7 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) goto out_unlock; } - /* Check for valid size. */ - if (map->size < vma->vm_end - vma->vm_start) { - ret = -EINVAL; - goto out_unlock; - } - - obj = map->handle; - if (!obj->dev->driver->gem_vm_ops) { - ret = -EINVAL; - goto out_unlock; - } - - vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP; - vma->vm_ops = obj->dev->driver->gem_vm_ops; - vma->vm_private_data = map->handle; - vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags)); - - /* Take a ref for this mapping of the object, so that the fault - * handler can dereference the mmap offset's pointer to the object. - * This reference is cleaned up by the corresponding vm_close - * (which should happen whether the vma was created by this call, or - * by a vm_open due to mremap or partial unmap or whatever). - */ - drm_gem_object_reference(obj); - - drm_vm_open_locked(dev, vma); + ret = drm_gem_mmap_obj(map->handle, map->size, vma); out_unlock: mutex_unlock(&dev->struct_mutex); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index b06f5afe10ce..79fb4c7b6c72 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1616,6 +1616,8 @@ int drm_gem_private_object_init(struct drm_device *dev, void drm_gem_object_handle_free(struct drm_gem_object *obj); void drm_gem_vm_open(struct vm_area_struct *vma); void drm_gem_vm_close(struct vm_area_struct *vma); +int drm_gem_mmap_obj(struct drm_gem_object *obj, unsigned long obj_size, + struct vm_area_struct *vma); int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma); #include -- cgit v1.2.3 From bda3fdaa0f43fc7faaf07b4fc4f2bc4daea12451 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 16 Apr 2013 14:21:23 +0200 Subject: drm/omap: Use drm_gem_mmap_obj() to implement dma-buf mmap The dma-buf mmap code was copied from the GEM mmap implementation. Replace it with the new drm_gem_mmap_obj() function. Signed-off-by: Laurent Pinchart Reviewed-by: Rob Clark --- drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c | 32 +++---------------------------- 1 file changed, 3 insertions(+), 29 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c b/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c index be7cd97a0db0..3256693de110 100644 --- a/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c +++ b/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c @@ -136,10 +136,6 @@ static void omap_gem_dmabuf_kunmap(struct dma_buf *buffer, kunmap(pages[page_num]); } -/* - * TODO maybe we can split up drm_gem_mmap to avoid duplicating - * some here.. or at least have a drm_dmabuf_mmap helper. - */ static int omap_gem_dmabuf_mmap(struct dma_buf *buffer, struct vm_area_struct *vma) { @@ -149,31 +145,9 @@ static int omap_gem_dmabuf_mmap(struct dma_buf *buffer, if (WARN_ON(!obj->filp)) return -EINVAL; - /* Check for valid size. */ - if (omap_gem_mmap_size(obj) < vma->vm_end - vma->vm_start) { - ret = -EINVAL; - goto out_unlock; - } - - if (!obj->dev->driver->gem_vm_ops) { - ret = -EINVAL; - goto out_unlock; - } - - vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP; - vma->vm_ops = obj->dev->driver->gem_vm_ops; - vma->vm_private_data = obj; - vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags)); - - /* Take a ref for this mapping of the object, so that the fault - * handler can dereference the mmap offset's pointer to the object. - * This reference is cleaned up by the corresponding vm_close - * (which should happen whether the vma was created by this call, or - * by a vm_open due to mremap or partial unmap or whatever). - */ - vma->vm_ops->open(vma); - -out_unlock: + ret = drm_gem_mmap_obj(obj, omap_gem_mmap_size(obj), vma); + if (ret < 0) + return ret; return omap_gem_mmap_obj(obj, vma); } -- cgit v1.2.3 From a5ed8940d3f99f05d460ce31583182539f07fa0e Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 17 Feb 2013 01:54:26 +0100 Subject: drm: GEM CMA: Split object creation into object alloc and DMA memory alloc This allows creating a GEM CMA object without an associated DMA memory buffer, and will be used to implement DRM PRIME support. Signed-off-by: Laurent Pinchart Reviewed-by: Rob Clark --- drivers/gpu/drm/drm_gem_cma_helper.c | 83 +++++++++++++++++++++--------------- 1 file changed, 48 insertions(+), 35 deletions(-) diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c index 0a7e011509bd..8cce3302b690 100644 --- a/drivers/gpu/drm/drm_gem_cma_helper.c +++ b/drivers/gpu/drm/drm_gem_cma_helper.c @@ -32,62 +32,73 @@ static unsigned int get_gem_mmap_offset(struct drm_gem_object *obj) return (unsigned int)obj->map_list.hash.key << PAGE_SHIFT; } -static void drm_gem_cma_buf_destroy(struct drm_device *drm, - struct drm_gem_cma_object *cma_obj) -{ - dma_free_writecombine(drm->dev, cma_obj->base.size, cma_obj->vaddr, - cma_obj->paddr); -} - /* - * drm_gem_cma_create - allocate an object with the given size + * __drm_gem_cma_create - Create a GEM CMA object without allocating memory + * @drm: The drm device + * @size: The GEM object size * - * returns a struct drm_gem_cma_object* on success or ERR_PTR values - * on failure. + * This function creates and initializes a GEM CMA object of the given size, but + * doesn't allocate any memory to back the object. + * + * Return a struct drm_gem_cma_object* on success or ERR_PTR values on failure. */ -struct drm_gem_cma_object *drm_gem_cma_create(struct drm_device *drm, - unsigned int size) +static struct drm_gem_cma_object * +__drm_gem_cma_create(struct drm_device *drm, unsigned int size) { struct drm_gem_cma_object *cma_obj; struct drm_gem_object *gem_obj; int ret; - size = round_up(size, PAGE_SIZE); - cma_obj = kzalloc(sizeof(*cma_obj), GFP_KERNEL); if (!cma_obj) return ERR_PTR(-ENOMEM); - cma_obj->vaddr = dma_alloc_writecombine(drm->dev, size, - &cma_obj->paddr, GFP_KERNEL | __GFP_NOWARN); - if (!cma_obj->vaddr) { - dev_err(drm->dev, "failed to allocate buffer with size %d\n", size); - ret = -ENOMEM; - goto err_dma_alloc; - } - gem_obj = &cma_obj->base; ret = drm_gem_object_init(drm, gem_obj, size); if (ret) - goto err_obj_init; + goto error; ret = drm_gem_create_mmap_offset(gem_obj); - if (ret) - goto err_create_mmap_offset; + if (ret) { + drm_gem_object_release(gem_obj); + goto error; + } return cma_obj; -err_create_mmap_offset: - drm_gem_object_release(gem_obj); +error: + kfree(cma_obj); + return ERR_PTR(ret); +} -err_obj_init: - drm_gem_cma_buf_destroy(drm, cma_obj); +/* + * drm_gem_cma_create - allocate an object with the given size + * + * returns a struct drm_gem_cma_object* on success or ERR_PTR values + * on failure. + */ +struct drm_gem_cma_object *drm_gem_cma_create(struct drm_device *drm, + unsigned int size) +{ + struct drm_gem_cma_object *cma_obj; -err_dma_alloc: - kfree(cma_obj); + size = round_up(size, PAGE_SIZE); - return ERR_PTR(ret); + cma_obj = __drm_gem_cma_create(drm, size); + if (IS_ERR(cma_obj)) + return cma_obj; + + cma_obj->vaddr = dma_alloc_writecombine(drm->dev, size, + &cma_obj->paddr, GFP_KERNEL | __GFP_NOWARN); + if (!cma_obj->vaddr) { + dev_err(drm->dev, "failed to allocate buffer with size %d\n", + size); + drm_gem_cma_free_object(&cma_obj->base); + return ERR_PTR(-ENOMEM); + } + + return cma_obj; } EXPORT_SYMBOL_GPL(drm_gem_cma_create); @@ -143,11 +154,13 @@ void drm_gem_cma_free_object(struct drm_gem_object *gem_obj) if (gem_obj->map_list.map) drm_gem_free_mmap_offset(gem_obj); - drm_gem_object_release(gem_obj); - cma_obj = to_drm_gem_cma_obj(gem_obj); - drm_gem_cma_buf_destroy(gem_obj->dev, cma_obj); + if (cma_obj->vaddr) + dma_free_writecombine(gem_obj->dev->dev, cma_obj->base.size, + cma_obj->vaddr, cma_obj->paddr); + + drm_gem_object_release(gem_obj); kfree(cma_obj); } -- cgit v1.2.3 From ebaf9e033e6dc9b584176c1731f4e07360d4d231 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 16 Apr 2013 14:32:34 +0200 Subject: drm: GEM CMA: Split object mapping into GEM mapping and CMA mapping The CMA-specific mapping code will be used to implement dma-buf mmap support. Signed-off-by: Laurent Pinchart Signed-off-by: Rob Clark --- drivers/gpu/drm/drm_gem_cma_helper.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c index 8cce3302b690..7a4db4e7a1ea 100644 --- a/drivers/gpu/drm/drm_gem_cma_helper.c +++ b/drivers/gpu/drm/drm_gem_cma_helper.c @@ -228,13 +228,26 @@ const struct vm_operations_struct drm_gem_cma_vm_ops = { }; EXPORT_SYMBOL_GPL(drm_gem_cma_vm_ops); +static int drm_gem_cma_mmap_obj(struct drm_gem_cma_object *cma_obj, + struct vm_area_struct *vma) +{ + int ret; + + ret = remap_pfn_range(vma, vma->vm_start, cma_obj->paddr >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, vma->vm_page_prot); + if (ret) + drm_gem_vm_close(vma); + + return ret; +} + /* * drm_gem_cma_mmap - (struct file_operation)->mmap callback function */ int drm_gem_cma_mmap(struct file *filp, struct vm_area_struct *vma) { - struct drm_gem_object *gem_obj; struct drm_gem_cma_object *cma_obj; + struct drm_gem_object *gem_obj; int ret; ret = drm_gem_mmap(filp, vma); @@ -244,12 +257,7 @@ int drm_gem_cma_mmap(struct file *filp, struct vm_area_struct *vma) gem_obj = vma->vm_private_data; cma_obj = to_drm_gem_cma_obj(gem_obj); - ret = remap_pfn_range(vma, vma->vm_start, cma_obj->paddr >> PAGE_SHIFT, - vma->vm_end - vma->vm_start, vma->vm_page_prot); - if (ret) - drm_gem_vm_close(vma); - - return ret; + return drm_gem_cma_mmap_obj(cma_obj, vma); } EXPORT_SYMBOL_GPL(drm_gem_cma_mmap); -- cgit v1.2.3 From 71d7282a0f1abb488e5be4d154893579624bc683 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 17 Feb 2013 01:57:30 +0100 Subject: drm: GEM CMA: Add DRM PRIME support Signed-off-by: Laurent Pinchart Reviewed-by: Rob Clark --- drivers/gpu/drm/drm_gem_cma_helper.c | 317 ++++++++++++++++++++++++++++++++++- include/drm/drm_gem_cma_helper.h | 9 + 2 files changed, 323 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c index 7a4db4e7a1ea..f94fcd752815 100644 --- a/drivers/gpu/drm/drm_gem_cma_helper.c +++ b/drivers/gpu/drm/drm_gem_cma_helper.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -82,6 +83,8 @@ struct drm_gem_cma_object *drm_gem_cma_create(struct drm_device *drm, unsigned int size) { struct drm_gem_cma_object *cma_obj; + struct sg_table *sgt = NULL; + int ret; size = round_up(size, PAGE_SIZE); @@ -94,11 +97,29 @@ struct drm_gem_cma_object *drm_gem_cma_create(struct drm_device *drm, if (!cma_obj->vaddr) { dev_err(drm->dev, "failed to allocate buffer with size %d\n", size); - drm_gem_cma_free_object(&cma_obj->base); - return ERR_PTR(-ENOMEM); + ret = -ENOMEM; + goto error; + } + + sgt = kzalloc(sizeof(*cma_obj->sgt), GFP_KERNEL); + if (sgt == NULL) { + ret = -ENOMEM; + goto error; } + ret = dma_get_sgtable(drm->dev, sgt, cma_obj->vaddr, + cma_obj->paddr, size); + if (ret < 0) + goto error; + + cma_obj->sgt = sgt; + return cma_obj; + +error: + kfree(sgt); + drm_gem_cma_free_object(&cma_obj->base); + return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(drm_gem_cma_create); @@ -156,9 +177,16 @@ void drm_gem_cma_free_object(struct drm_gem_object *gem_obj) cma_obj = to_drm_gem_cma_obj(gem_obj); - if (cma_obj->vaddr) + if (cma_obj->vaddr) { dma_free_writecombine(gem_obj->dev->dev, cma_obj->base.size, cma_obj->vaddr, cma_obj->paddr); + if (cma_obj->sgt) { + sg_free_table(cma_obj->sgt); + kfree(cma_obj->sgt); + } + } else if (gem_obj->import_attach) { + drm_prime_gem_destroy(gem_obj, cma_obj->sgt); + } drm_gem_object_release(gem_obj); @@ -291,3 +319,286 @@ void drm_gem_cma_describe(struct drm_gem_cma_object *cma_obj, struct seq_file *m } EXPORT_SYMBOL_GPL(drm_gem_cma_describe); #endif + +/* ----------------------------------------------------------------------------- + * DMA-BUF + */ + +struct drm_gem_cma_dmabuf_attachment { + struct sg_table sgt; + enum dma_data_direction dir; +}; + +static int drm_gem_cma_dmabuf_attach(struct dma_buf *dmabuf, struct device *dev, + struct dma_buf_attachment *attach) +{ + struct drm_gem_cma_dmabuf_attachment *cma_attach; + + cma_attach = kzalloc(sizeof(*cma_attach), GFP_KERNEL); + if (!cma_attach) + return -ENOMEM; + + cma_attach->dir = DMA_NONE; + attach->priv = cma_attach; + + return 0; +} + +static void drm_gem_cma_dmabuf_detach(struct dma_buf *dmabuf, + struct dma_buf_attachment *attach) +{ + struct drm_gem_cma_dmabuf_attachment *cma_attach = attach->priv; + struct sg_table *sgt; + + if (cma_attach == NULL) + return; + + sgt = &cma_attach->sgt; + + if (cma_attach->dir != DMA_NONE) + dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents, + cma_attach->dir); + + sg_free_table(sgt); + kfree(cma_attach); + attach->priv = NULL; +} + +static struct sg_table * +drm_gem_cma_dmabuf_map(struct dma_buf_attachment *attach, + enum dma_data_direction dir) +{ + struct drm_gem_cma_dmabuf_attachment *cma_attach = attach->priv; + struct drm_gem_cma_object *cma_obj = attach->dmabuf->priv; + struct drm_device *drm = cma_obj->base.dev; + struct scatterlist *rd, *wr; + struct sg_table *sgt; + unsigned int i; + int nents, ret; + + DRM_DEBUG_PRIME("\n"); + + if (WARN_ON(dir == DMA_NONE)) + return ERR_PTR(-EINVAL); + + /* Return the cached mapping when possible. */ + if (cma_attach->dir == dir) + return &cma_attach->sgt; + + /* Two mappings with different directions for the same attachment are + * not allowed. + */ + if (WARN_ON(cma_attach->dir != DMA_NONE)) + return ERR_PTR(-EBUSY); + + sgt = &cma_attach->sgt; + + ret = sg_alloc_table(sgt, cma_obj->sgt->orig_nents, GFP_KERNEL); + if (ret) { + DRM_ERROR("failed to alloc sgt.\n"); + return ERR_PTR(-ENOMEM); + } + + mutex_lock(&drm->struct_mutex); + + rd = cma_obj->sgt->sgl; + wr = sgt->sgl; + for (i = 0; i < sgt->orig_nents; ++i) { + sg_set_page(wr, sg_page(rd), rd->length, rd->offset); + rd = sg_next(rd); + wr = sg_next(wr); + } + + nents = dma_map_sg(attach->dev, sgt->sgl, sgt->orig_nents, dir); + if (!nents) { + DRM_ERROR("failed to map sgl with iommu.\n"); + sg_free_table(sgt); + sgt = ERR_PTR(-EIO); + goto done; + } + + cma_attach->dir = dir; + attach->priv = cma_attach; + + DRM_DEBUG_PRIME("buffer size = %zu\n", cma_obj->base.size); + +done: + mutex_unlock(&drm->struct_mutex); + return sgt; +} + +static void drm_gem_cma_dmabuf_unmap(struct dma_buf_attachment *attach, + struct sg_table *sgt, + enum dma_data_direction dir) +{ + /* Nothing to do. */ +} + +static void drm_gem_cma_dmabuf_release(struct dma_buf *dmabuf) +{ + struct drm_gem_cma_object *cma_obj = dmabuf->priv; + + DRM_DEBUG_PRIME("%s\n", __FILE__); + + /* + * drm_gem_cma_dmabuf_release() call means that file object's + * f_count is 0 and it calls drm_gem_object_handle_unreference() + * to drop the references that these values had been increased + * at drm_prime_handle_to_fd() + */ + if (cma_obj->base.export_dma_buf == dmabuf) { + cma_obj->base.export_dma_buf = NULL; + + /* + * drop this gem object refcount to release allocated buffer + * and resources. + */ + drm_gem_object_unreference_unlocked(&cma_obj->base); + } +} + +static void *drm_gem_cma_dmabuf_kmap_atomic(struct dma_buf *dmabuf, + unsigned long page_num) +{ + /* TODO */ + + return NULL; +} + +static void drm_gem_cma_dmabuf_kunmap_atomic(struct dma_buf *dmabuf, + unsigned long page_num, void *addr) +{ + /* TODO */ +} + +static void *drm_gem_cma_dmabuf_kmap(struct dma_buf *dmabuf, + unsigned long page_num) +{ + /* TODO */ + + return NULL; +} + +static void drm_gem_cma_dmabuf_kunmap(struct dma_buf *dmabuf, + unsigned long page_num, void *addr) +{ + /* TODO */ +} + +static int drm_gem_cma_dmabuf_mmap(struct dma_buf *dmabuf, + struct vm_area_struct *vma) +{ + struct drm_gem_cma_object *cma_obj = dmabuf->priv; + struct drm_gem_object *gem_obj = &cma_obj->base; + int ret; + + ret = drm_gem_mmap_obj(gem_obj, gem_obj->size, vma); + if (ret < 0) + return ret; + + return drm_gem_cma_mmap_obj(cma_obj, vma); +} + +static void *drm_gem_cma_dmabuf_vmap(struct dma_buf *dmabuf) +{ + struct drm_gem_cma_object *cma_obj = dmabuf->priv; + + return cma_obj->vaddr; +} + +static struct dma_buf_ops drm_gem_cma_dmabuf_ops = { + .attach = drm_gem_cma_dmabuf_attach, + .detach = drm_gem_cma_dmabuf_detach, + .map_dma_buf = drm_gem_cma_dmabuf_map, + .unmap_dma_buf = drm_gem_cma_dmabuf_unmap, + .kmap = drm_gem_cma_dmabuf_kmap, + .kmap_atomic = drm_gem_cma_dmabuf_kmap_atomic, + .kunmap = drm_gem_cma_dmabuf_kunmap, + .kunmap_atomic = drm_gem_cma_dmabuf_kunmap_atomic, + .mmap = drm_gem_cma_dmabuf_mmap, + .vmap = drm_gem_cma_dmabuf_vmap, + .release = drm_gem_cma_dmabuf_release, +}; + +struct dma_buf *drm_gem_cma_dmabuf_export(struct drm_device *drm, + struct drm_gem_object *obj, int flags) +{ + struct drm_gem_cma_object *cma_obj = to_drm_gem_cma_obj(obj); + + return dma_buf_export(cma_obj, &drm_gem_cma_dmabuf_ops, + cma_obj->base.size, flags); +} +EXPORT_SYMBOL_GPL(drm_gem_cma_dmabuf_export); + +struct drm_gem_object *drm_gem_cma_dmabuf_import(struct drm_device *drm, + struct dma_buf *dma_buf) +{ + struct drm_gem_cma_object *cma_obj; + struct dma_buf_attachment *attach; + struct sg_table *sgt; + int ret; + + DRM_DEBUG_PRIME("%s\n", __FILE__); + + /* is this one of own objects? */ + if (dma_buf->ops == &drm_gem_cma_dmabuf_ops) { + struct drm_gem_object *obj; + + cma_obj = dma_buf->priv; + obj = &cma_obj->base; + + /* is it from our device? */ + if (obj->dev == drm) { + /* + * Importing dmabuf exported from out own gem increases + * refcount on gem itself instead of f_count of dmabuf. + */ + drm_gem_object_reference(obj); + dma_buf_put(dma_buf); + return obj; + } + } + + /* Create a CMA GEM buffer. */ + cma_obj = __drm_gem_cma_create(drm, dma_buf->size); + if (IS_ERR(cma_obj)) + return ERR_PTR(PTR_ERR(cma_obj)); + + /* Attach to the buffer and map it. Make sure the mapping is contiguous + * on the device memory bus, as that's all we support. + */ + attach = dma_buf_attach(dma_buf, drm->dev); + if (IS_ERR(attach)) { + ret = -EINVAL; + goto error_gem_free; + } + + sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); + if (IS_ERR_OR_NULL(sgt)) { + ret = sgt ? PTR_ERR(sgt) : -ENOMEM; + goto error_buf_detach; + } + + if (sgt->nents != 1) { + ret = -EINVAL; + goto error_buf_unmap; + } + + cma_obj->base.import_attach = attach; + cma_obj->paddr = sg_dma_address(sgt->sgl); + cma_obj->sgt = sgt; + + DRM_DEBUG_PRIME("dma_addr = 0x%x, size = %zu\n", cma_obj->paddr, + dma_buf->size); + + return &cma_obj->base; + +error_buf_unmap: + dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL); +error_buf_detach: + dma_buf_detach(dma_buf, attach); +error_gem_free: + drm_gem_cma_free_object(&cma_obj->base); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(drm_gem_cma_dmabuf_import); diff --git a/include/drm/drm_gem_cma_helper.h b/include/drm/drm_gem_cma_helper.h index 63397ced9254..6e17251e9b28 100644 --- a/include/drm/drm_gem_cma_helper.h +++ b/include/drm/drm_gem_cma_helper.h @@ -4,6 +4,9 @@ struct drm_gem_cma_object { struct drm_gem_object base; dma_addr_t paddr; + struct sg_table *sgt; + + /* For objects with DMA memory allocated by GEM CMA */ void *vaddr; }; @@ -45,4 +48,10 @@ extern const struct vm_operations_struct drm_gem_cma_vm_ops; void drm_gem_cma_describe(struct drm_gem_cma_object *obj, struct seq_file *m); #endif +struct dma_buf *drm_gem_cma_dmabuf_export(struct drm_device *drm_dev, + struct drm_gem_object *obj, + int flags); +struct drm_gem_object *drm_gem_cma_dmabuf_import(struct drm_device *drm_dev, + struct dma_buf *dma_buf); + #endif /* __DRM_GEM_CMA_HELPER_H__ */ -- cgit v1.2.3 From c7f62fc87bb4f3ee7f21fed959795de2bd415ccf Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Sat, 1 Jun 2013 16:08:22 +0900 Subject: EDAC: Replace strict_strtoul() with kstrtoul() The usage of strict_strtoul() is not preferred, because strict_strtoul() is obsolete. Thus, kstrtoul() should be used. Signed-off-by: Jingoo Han Signed-off-by: Borislav Petkov --- drivers/edac/amd64_edac_inj.c | 10 +++++----- drivers/edac/edac_mc_sysfs.c | 2 +- drivers/edac/i7core_edac.c | 10 +++++----- drivers/edac/mce_amd_inj.c | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/edac/amd64_edac_inj.c b/drivers/edac/amd64_edac_inj.c index 845f04786c2d..0d66ae68d468 100644 --- a/drivers/edac/amd64_edac_inj.c +++ b/drivers/edac/amd64_edac_inj.c @@ -24,7 +24,7 @@ static ssize_t amd64_inject_section_store(struct device *dev, unsigned long value; int ret; - ret = strict_strtoul(data, 10, &value); + ret = kstrtoul(data, 10, &value); if (ret < 0) return ret; @@ -61,7 +61,7 @@ static ssize_t amd64_inject_word_store(struct device *dev, unsigned long value; int ret; - ret = strict_strtoul(data, 10, &value); + ret = kstrtoul(data, 10, &value); if (ret < 0) return ret; @@ -97,7 +97,7 @@ static ssize_t amd64_inject_ecc_vector_store(struct device *dev, unsigned long value; int ret; - ret = strict_strtoul(data, 16, &value); + ret = kstrtoul(data, 16, &value); if (ret < 0) return ret; @@ -124,7 +124,7 @@ static ssize_t amd64_inject_read_store(struct device *dev, u32 section, word_bits; int ret; - ret = strict_strtoul(data, 10, &value); + ret = kstrtoul(data, 10, &value); if (ret < 0) return ret; @@ -157,7 +157,7 @@ static ssize_t amd64_inject_write_store(struct device *dev, unsigned long value; int ret; - ret = strict_strtoul(data, 10, &value); + ret = kstrtoul(data, 10, &value); if (ret < 0) return ret; diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index 67610a6ebf87..ef15a7e613bc 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -678,7 +678,7 @@ static ssize_t mci_sdram_scrub_rate_store(struct device *dev, unsigned long bandwidth = 0; int new_bw = 0; - if (strict_strtoul(data, 10, &bandwidth) < 0) + if (kstrtoul(data, 10, &bandwidth) < 0) return -EINVAL; new_bw = mci->set_sdram_scrub_rate(mci, bandwidth); diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 0ec3e95a12cd..80a963d64e58 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -704,7 +704,7 @@ static ssize_t i7core_inject_section_store(struct device *dev, if (pvt->inject.enable) disable_inject(mci); - rc = strict_strtoul(data, 10, &value); + rc = kstrtoul(data, 10, &value); if ((rc < 0) || (value > 3)) return -EIO; @@ -741,7 +741,7 @@ struct i7core_pvt *pvt = mci->pvt_info; if (pvt->inject.enable) disable_inject(mci); - rc = strict_strtoul(data, 10, &value); + rc = kstrtoul(data, 10, &value); if ((rc < 0) || (value > 7)) return -EIO; @@ -781,7 +781,7 @@ static ssize_t i7core_inject_eccmask_store(struct device *dev, if (pvt->inject.enable) disable_inject(mci); - rc = strict_strtoul(data, 10, &value); + rc = kstrtoul(data, 10, &value); if (rc < 0) return -EIO; @@ -830,7 +830,7 @@ static ssize_t i7core_inject_store_##param( \ if (!strcasecmp(data, "any") || !strcasecmp(data, "any\n"))\ value = -1; \ else { \ - rc = strict_strtoul(data, 10, &value); \ + rc = kstrtoul(data, 10, &value); \ if ((rc < 0) || (value >= limit)) \ return -EIO; \ } \ @@ -934,7 +934,7 @@ static ssize_t i7core_inject_enable_store(struct device *dev, if (!pvt->pci_ch[pvt->inject.channel][0]) return 0; - rc = strict_strtoul(data, 10, &enable); + rc = kstrtoul(data, 10, &enable); if ((rc < 0)) return 0; diff --git a/drivers/edac/mce_amd_inj.c b/drivers/edac/mce_amd_inj.c index 2ae78f20cc28..5e46a9fea31b 100644 --- a/drivers/edac/mce_amd_inj.c +++ b/drivers/edac/mce_amd_inj.c @@ -43,7 +43,7 @@ static ssize_t edac_inject_##reg##_store(struct kobject *kobj, \ int ret = 0; \ unsigned long value; \ \ - ret = strict_strtoul(data, 16, &value); \ + ret = kstrtoul(data, 16, &value); \ if (ret < 0) \ printk(KERN_ERR "Error writing MCE " #reg " field.\n"); \ \ @@ -83,7 +83,7 @@ static ssize_t edac_inject_bank_store(struct kobject *kobj, int ret = 0; unsigned long value; - ret = strict_strtoul(data, 10, &value); + ret = kstrtoul(data, 10, &value); if (ret < 0) { printk(KERN_ERR "Invalid bank value!\n"); return -EINVAL; -- cgit v1.2.3 From aad19e51769d761ffc0608b381313e18f0bd82b3 Mon Sep 17 00:00:00 2001 From: Aravind Gopalakrishnan Date: Wed, 5 Jun 2013 15:50:03 -0500 Subject: EDAC, MCE, AMD: Add an MCE signature for new Fam15h models Add a new error signature for Family 15h, models 30h-3fh. Patch has been tested on Fam15h using mce_amd_inj facility and has been verified to work correctly. Signed-off-by: Aravind Gopalakrishnan [ cleanup commit message and error string ] Signed-off-by: Borislav Petkov --- drivers/edac/mce_amd.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/edac/mce_amd.c b/drivers/edac/mce_amd.c index f3f0c930d550..30f7309446a6 100644 --- a/drivers/edac/mce_amd.c +++ b/drivers/edac/mce_amd.c @@ -134,7 +134,8 @@ static const char * const mc5_mce_desc[] = { "Physical register file AG0 port", "Physical register file AG1 port", "Flag register file", - "DE error occurred" + "DE error occurred", + "Retire status queue" }; static bool f12h_mc0_mce(u16 ec, u8 xec) @@ -624,7 +625,7 @@ static void decode_mc5_mce(struct mce *m) if (xec == 0x0 || xec == 0xc) pr_cont("%s.\n", mc5_mce_desc[xec]); - else if (xec < 0xd) + else if (xec <= 0xd) pr_cont("%s parity error.\n", mc5_mce_desc[xec]); else goto wrong_mc5_mce; -- cgit v1.2.3 From d47be3dfecaf20255af89a57460285c82d5271ad Mon Sep 17 00:00:00 2001 From: David Quigley Date: Wed, 22 May 2013 12:50:34 -0400 Subject: Security: Add hook to calculate context based on a negative dentry. There is a time where we need to calculate a context without the inode having been created yet. To do this we take the negative dentry and calculate a context based on the process and the parent directory contexts. Acked-by: Eric Paris Acked-by: James Morris Signed-off-by: Matthew N. Dodd Signed-off-by: Miguel Rodel Felipe Signed-off-by: Phua Eu Gene Signed-off-by: Khin Mi Mi Aung Signed-off-by: Steve Dickson Signed-off-by: Trond Myklebust --- include/linux/security.h | 27 +++++++++++++++++++++++++++ security/capability.c | 8 ++++++++ security/security.c | 10 ++++++++++ security/selinux/hooks.c | 35 +++++++++++++++++++++++++++++++++++ 4 files changed, 80 insertions(+) diff --git a/include/linux/security.h b/include/linux/security.h index 4686491852a7..c2af46264ae0 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -26,6 +26,7 @@ #include #include #include +#include struct linux_binprm; struct cred; @@ -306,6 +307,15 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * Parse a string of security data filling in the opts structure * @options string containing all mount options known by the LSM * @opts binary data structure usable by the LSM + * @dentry_init_security: + * Compute a context for a dentry as the inode is not yet available + * since NFSv4 has no label backed by an EA anyway. + * @dentry dentry to use in calculating the context. + * @mode mode used to determine resource type. + * @name name of the last path component used to create file + * @ctx pointer to place the pointer to the resulting context in. + * @ctxlen point to place the length of the resulting context. + * * * Security hooks for inode operations. * @@ -1443,6 +1453,10 @@ struct security_operations { int (*sb_clone_mnt_opts) (const struct super_block *oldsb, struct super_block *newsb); int (*sb_parse_opts_str) (char *options, struct security_mnt_opts *opts); + int (*dentry_init_security) (struct dentry *dentry, int mode, + struct qstr *name, void **ctx, + u32 *ctxlen); + #ifdef CONFIG_SECURITY_PATH int (*path_unlink) (struct path *dir, struct dentry *dentry); @@ -1729,6 +1743,9 @@ int security_sb_set_mnt_opts(struct super_block *sb, struct security_mnt_opts *o int security_sb_clone_mnt_opts(const struct super_block *oldsb, struct super_block *newsb); int security_sb_parse_opts_str(char *options, struct security_mnt_opts *opts); +int security_dentry_init_security(struct dentry *dentry, int mode, + struct qstr *name, void **ctx, + u32 *ctxlen); int security_inode_alloc(struct inode *inode); void security_inode_free(struct inode *inode); @@ -2035,6 +2052,16 @@ static inline int security_inode_alloc(struct inode *inode) static inline void security_inode_free(struct inode *inode) { } +static inline int security_dentry_init_security(struct dentry *dentry, + int mode, + struct qstr *name, + void **ctx, + u32 *ctxlen) +{ + return -EOPNOTSUPP; +} + + static inline int security_inode_init_security(struct inode *inode, struct inode *dir, const struct qstr *qstr, diff --git a/security/capability.c b/security/capability.c index 1728d4e375db..58578b4bdad4 100644 --- a/security/capability.c +++ b/security/capability.c @@ -109,6 +109,13 @@ static int cap_sb_parse_opts_str(char *options, struct security_mnt_opts *opts) return 0; } +static int cap_dentry_init_security(struct dentry *dentry, int mode, + struct qstr *name, void **ctx, + u32 *ctxlen) +{ + return 0; +} + static int cap_inode_alloc_security(struct inode *inode) { return 0; @@ -931,6 +938,7 @@ void __init security_fixup_ops(struct security_operations *ops) set_to_cap_if_null(ops, sb_set_mnt_opts); set_to_cap_if_null(ops, sb_clone_mnt_opts); set_to_cap_if_null(ops, sb_parse_opts_str); + set_to_cap_if_null(ops, dentry_init_security); set_to_cap_if_null(ops, inode_alloc_security); set_to_cap_if_null(ops, inode_free_security); set_to_cap_if_null(ops, inode_init_security); diff --git a/security/security.c b/security/security.c index a3dce87d1aef..0fe2b2ee9545 100644 --- a/security/security.c +++ b/security/security.c @@ -12,6 +12,7 @@ */ #include +#include #include #include #include @@ -324,6 +325,15 @@ void security_inode_free(struct inode *inode) security_ops->inode_free_security(inode); } +int security_dentry_init_security(struct dentry *dentry, int mode, + struct qstr *name, void **ctx, + u32 *ctxlen) +{ + return security_ops->dentry_init_security(dentry, mode, name, + ctx, ctxlen); +} +EXPORT_SYMBOL(security_dentry_init_security); + int security_inode_init_security(struct inode *inode, struct inode *dir, const struct qstr *qstr, const initxattrs initxattrs, void *fs_data) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 5c6f2cd2d095..b1f7bd727bd9 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2515,6 +2515,40 @@ static void selinux_inode_free_security(struct inode *inode) inode_free_security(inode); } +static int selinux_dentry_init_security(struct dentry *dentry, int mode, + struct qstr *name, void **ctx, + u32 *ctxlen) +{ + const struct cred *cred = current_cred(); + struct task_security_struct *tsec; + struct inode_security_struct *dsec; + struct superblock_security_struct *sbsec; + struct inode *dir = dentry->d_parent->d_inode; + u32 newsid; + int rc; + + tsec = cred->security; + dsec = dir->i_security; + sbsec = dir->i_sb->s_security; + + if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) { + newsid = tsec->create_sid; + } else { + rc = security_transition_sid(tsec->sid, dsec->sid, + inode_mode_to_security_class(mode), + name, + &newsid); + if (rc) { + printk(KERN_WARNING + "%s: security_transition_sid failed, rc=%d\n", + __func__, -rc); + return rc; + } + } + + return security_sid_to_context(newsid, (char **)ctx, ctxlen); +} + static int selinux_inode_init_security(struct inode *inode, struct inode *dir, const struct qstr *qstr, char **name, void **value, size_t *len) @@ -5562,6 +5596,7 @@ static struct security_operations selinux_ops = { .sb_clone_mnt_opts = selinux_sb_clone_mnt_opts, .sb_parse_opts_str = selinux_parse_opts_str, + .dentry_init_security = selinux_dentry_init_security, .inode_alloc_security = selinux_inode_alloc_security, .inode_free_security = selinux_inode_free_security, -- cgit v1.2.3 From 746df9b59c8a5f162c907796c7295d3c4c0d8995 Mon Sep 17 00:00:00 2001 From: David Quigley Date: Wed, 22 May 2013 12:50:35 -0400 Subject: Security: Add Hook to test if the particular xattr is part of a MAC model. The interface to request security labels from user space is the xattr interface. When requesting the security label from an NFS server it is important to make sure the requested xattr actually is a MAC label. This allows us to make sure that we get the desired semantics from the attribute instead of something else such as capabilities or a time based LSM. Acked-by: Eric Paris Acked-by: James Morris Signed-off-by: Matthew N. Dodd Signed-off-by: Miguel Rodel Felipe Signed-off-by: Phua Eu Gene Signed-off-by: Khin Mi Mi Aung Signed-off-by: Trond Myklebust --- include/linux/security.h | 14 ++++++++++++++ security/capability.c | 6 ++++++ security/security.c | 6 ++++++ security/selinux/hooks.c | 6 ++++++ security/smack/smack_lsm.c | 11 +++++++++++ 5 files changed, 43 insertions(+) diff --git a/include/linux/security.h b/include/linux/security.h index c2af46264ae0..cff3e4fc4281 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1323,6 +1323,13 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * @pages contains the number of pages. * Return 0 if permission is granted. * + * @ismaclabel: + * Check if the extended attribute specified by @name + * represents a MAC label. Returns 1 if name is a MAC + * attribute otherwise returns 0. + * @name full extended attribute name to check against + * LSM as a MAC label. + * * @secid_to_secctx: * Convert secid to security context. If secdata is NULL the length of * the result will be returned in seclen, but no secdata will be returned. @@ -1604,6 +1611,7 @@ struct security_operations { int (*getprocattr) (struct task_struct *p, char *name, char **value); int (*setprocattr) (struct task_struct *p, char *name, void *value, size_t size); + int (*ismaclabel) (const char *name); int (*secid_to_secctx) (u32 secid, char **secdata, u32 *seclen); int (*secctx_to_secid) (const char *secdata, u32 seclen, u32 *secid); void (*release_secctx) (char *secdata, u32 seclen); @@ -1857,6 +1865,7 @@ void security_d_instantiate(struct dentry *dentry, struct inode *inode); int security_getprocattr(struct task_struct *p, char *name, char **value); int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size); int security_netlink_send(struct sock *sk, struct sk_buff *skb); +int security_ismaclabel(const char *name); int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen); int security_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid); void security_release_secctx(char *secdata, u32 seclen); @@ -2547,6 +2556,11 @@ static inline int security_netlink_send(struct sock *sk, struct sk_buff *skb) return cap_netlink_send(sk, skb); } +static inline int security_ismaclabel(const char *name) +{ + return 0; +} + static inline int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen) { return -EOPNOTSUPP; diff --git a/security/capability.c b/security/capability.c index 58578b4bdad4..71f9682bfb54 100644 --- a/security/capability.c +++ b/security/capability.c @@ -823,6 +823,11 @@ static int cap_setprocattr(struct task_struct *p, char *name, void *value, return -EINVAL; } +static int cap_ismaclabel(const char *name) +{ + return 0; +} + static int cap_secid_to_secctx(u32 secid, char **secdata, u32 *seclen) { return -EOPNOTSUPP; @@ -1042,6 +1047,7 @@ void __init security_fixup_ops(struct security_operations *ops) set_to_cap_if_null(ops, d_instantiate); set_to_cap_if_null(ops, getprocattr); set_to_cap_if_null(ops, setprocattr); + set_to_cap_if_null(ops, ismaclabel); set_to_cap_if_null(ops, secid_to_secctx); set_to_cap_if_null(ops, secctx_to_secid); set_to_cap_if_null(ops, release_secctx); diff --git a/security/security.c b/security/security.c index 0fe2b2ee9545..c3ceb754e705 100644 --- a/security/security.c +++ b/security/security.c @@ -1057,6 +1057,12 @@ int security_netlink_send(struct sock *sk, struct sk_buff *skb) return security_ops->netlink_send(sk, skb); } +int security_ismaclabel(const char *name) +{ + return security_ops->ismaclabel(name); +} +EXPORT_SYMBOL(security_ismaclabel); + int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen) { return security_ops->secid_to_secctx(secid, secdata, seclen); diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index b1f7bd727bd9..bbf219a494d0 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -5454,6 +5454,11 @@ abort_change: return error; } +static int selinux_ismaclabel(const char *name) +{ + return (strcmp(name, XATTR_SELINUX_SUFFIX) == 0); +} + static int selinux_secid_to_secctx(u32 secid, char **secdata, u32 *seclen) { return security_sid_to_context(secid, secdata, seclen); @@ -5692,6 +5697,7 @@ static struct security_operations selinux_ops = { .getprocattr = selinux_getprocattr, .setprocattr = selinux_setprocattr, + .ismaclabel = selinux_ismaclabel, .secid_to_secctx = selinux_secid_to_secctx, .secctx_to_secid = selinux_secctx_to_secid, .release_secctx = selinux_release_secctx, diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index d52c780bdb78..a7f485bb4e2e 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -3328,6 +3328,16 @@ static void smack_audit_rule_free(void *vrule) #endif /* CONFIG_AUDIT */ +/** + * smack_ismaclabel - check if xattr @name references a smack MAC label + * @name: Full xattr name to check. + */ +static int smack_ismaclabel(const char *name) +{ + return (strcmp(name, XATTR_SMACK_SUFFIX) == 0); +} + + /** * smack_secid_to_secctx - return the smack label for a secid * @secid: incoming integer @@ -3524,6 +3534,7 @@ struct security_operations smack_ops = { .audit_rule_free = smack_audit_rule_free, #endif /* CONFIG_AUDIT */ + .ismaclabel = smack_ismaclabel, .secid_to_secctx = smack_secid_to_secctx, .secctx_to_secid = smack_secctx_to_secid, .release_secctx = smack_release_secctx, -- cgit v1.2.3 From 649f6e7718891fe7691e5084ce3fa623acba3129 Mon Sep 17 00:00:00 2001 From: David Quigley Date: Wed, 22 May 2013 12:50:36 -0400 Subject: LSM: Add flags field to security_sb_set_mnt_opts for in kernel mount data. There is no way to differentiate if a text mount option is passed from user space or the kernel. A flags field is being added to the security_sb_set_mnt_opts hook to allow for in kernel security flags to be sent to the LSM for processing in addition to the text options received from mount. This patch also updated existing code to fix compilation errors. Acked-by: Eric Paris Acked-by: James Morris Signed-off-by: David P. Quigley Signed-off-by: Miguel Rodel Felipe Signed-off-by: Phua Eu Gene Signed-off-by: Khin Mi Mi Aung Signed-off-by: Trond Myklebust --- fs/nfs/super.c | 3 ++- include/linux/security.h | 13 ++++++++++--- security/capability.c | 5 ++++- security/security.c | 7 +++++-- security/selinux/hooks.c | 12 ++++++++++-- 5 files changed, 31 insertions(+), 9 deletions(-) diff --git a/fs/nfs/super.c b/fs/nfs/super.c index a366107a7331..c1bbb53d444a 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -2411,7 +2411,8 @@ static int nfs_bdi_register(struct nfs_server *server) int nfs_set_sb_security(struct super_block *s, struct dentry *mntroot, struct nfs_mount_info *mount_info) { - return security_sb_set_mnt_opts(s, &mount_info->parsed->lsm_opts); + return security_sb_set_mnt_opts(s, &mount_info->parsed->lsm_opts, + 0, NULL); } EXPORT_SYMBOL_GPL(nfs_set_sb_security); diff --git a/include/linux/security.h b/include/linux/security.h index cff3e4fc4281..aa656fbc4308 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1456,7 +1456,9 @@ struct security_operations { int (*sb_pivotroot) (struct path *old_path, struct path *new_path); int (*sb_set_mnt_opts) (struct super_block *sb, - struct security_mnt_opts *opts); + struct security_mnt_opts *opts, + unsigned long kern_flags, + unsigned long *set_kern_flags); int (*sb_clone_mnt_opts) (const struct super_block *oldsb, struct super_block *newsb); int (*sb_parse_opts_str) (char *options, struct security_mnt_opts *opts); @@ -1747,7 +1749,10 @@ int security_sb_mount(const char *dev_name, struct path *path, const char *type, unsigned long flags, void *data); int security_sb_umount(struct vfsmount *mnt, int flags); int security_sb_pivotroot(struct path *old_path, struct path *new_path); -int security_sb_set_mnt_opts(struct super_block *sb, struct security_mnt_opts *opts); +int security_sb_set_mnt_opts(struct super_block *sb, + struct security_mnt_opts *opts, + unsigned long kern_flags, + unsigned long *set_kern_flags); int security_sb_clone_mnt_opts(const struct super_block *oldsb, struct super_block *newsb); int security_sb_parse_opts_str(char *options, struct security_mnt_opts *opts); @@ -2037,7 +2042,9 @@ static inline int security_sb_pivotroot(struct path *old_path, } static inline int security_sb_set_mnt_opts(struct super_block *sb, - struct security_mnt_opts *opts) + struct security_mnt_opts *opts, + unsigned long kern_flags, + unsigned long *set_kern_flags) { return 0; } diff --git a/security/capability.c b/security/capability.c index 71f9682bfb54..d32e16e3c6ae 100644 --- a/security/capability.c +++ b/security/capability.c @@ -91,7 +91,10 @@ static int cap_sb_pivotroot(struct path *old_path, struct path *new_path) } static int cap_sb_set_mnt_opts(struct super_block *sb, - struct security_mnt_opts *opts) + struct security_mnt_opts *opts, + unsigned long kern_flags, + unsigned long *set_kern_flags) + { if (unlikely(opts->num_mnt_opts)) return -EOPNOTSUPP; diff --git a/security/security.c b/security/security.c index c3ceb754e705..8d0b9a79611a 100644 --- a/security/security.c +++ b/security/security.c @@ -294,9 +294,12 @@ int security_sb_pivotroot(struct path *old_path, struct path *new_path) } int security_sb_set_mnt_opts(struct super_block *sb, - struct security_mnt_opts *opts) + struct security_mnt_opts *opts, + unsigned long kern_flags, + unsigned long *set_kern_flags) { - return security_ops->sb_set_mnt_opts(sb, opts); + return security_ops->sb_set_mnt_opts(sb, opts, kern_flags, + set_kern_flags); } EXPORT_SYMBOL(security_sb_set_mnt_opts); diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index bbf219a494d0..f3b54466a037 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -552,7 +552,9 @@ static int bad_option(struct superblock_security_struct *sbsec, char flag, * labeling information. */ static int selinux_set_mnt_opts(struct super_block *sb, - struct security_mnt_opts *opts) + struct security_mnt_opts *opts, + unsigned long kern_flags, + unsigned long *set_kern_flags) { const struct cred *cred = current_cred(); int rc = 0, i; @@ -580,6 +582,12 @@ static int selinux_set_mnt_opts(struct super_block *sb, "before the security server is initialized\n"); goto out; } + if (kern_flags && !set_kern_flags) { + /* Specifying internal flags without providing a place to + * place the results is not allowed */ + rc = -EINVAL; + goto out; + } /* * Binary mount data FS will come through this function twice. Once @@ -980,7 +988,7 @@ static int superblock_doinit(struct super_block *sb, void *data) goto out_err; out: - rc = selinux_set_mnt_opts(sb, &opts); + rc = selinux_set_mnt_opts(sb, &opts, 0, NULL); out_err: security_free_mnt_opts(&opts); -- cgit v1.2.3 From eb9ae686507bc5a5ca78e6b3fbe629cd5cc67864 Mon Sep 17 00:00:00 2001 From: David Quigley Date: Wed, 22 May 2013 12:50:37 -0400 Subject: SELinux: Add new labeling type native labels There currently doesn't exist a labeling type that is adequate for use with labeled NFS. Since NFS doesn't really support xattrs we can't use the use xattr labeling behavior. For this we developed a new labeling type. The native labeling type is used solely by NFS to ensure NFS inodes are labeled at runtime by the NFS code instead of relying on the SELinux security server on the client end. Acked-by: Eric Paris Acked-by: James Morris Signed-off-by: Matthew N. Dodd Signed-off-by: Miguel Rodel Felipe Signed-off-by: Phua Eu Gene Signed-off-by: Khin Mi Mi Aung Signed-off-by: Trond Myklebust --- include/linux/security.h | 3 +++ security/selinux/hooks.c | 35 ++++++++++++++++++++++++++--------- security/selinux/include/security.h | 2 ++ security/selinux/ss/policydb.c | 5 ++++- 4 files changed, 35 insertions(+), 10 deletions(-) diff --git a/include/linux/security.h b/include/linux/security.h index aa656fbc4308..a585a9085e46 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -61,6 +61,9 @@ struct mm_struct; #define SECURITY_CAP_NOAUDIT 0 #define SECURITY_CAP_AUDIT 1 +/* LSM Agnostic defines for sb_set_mnt_opts */ +#define SECURITY_LSM_NATIVE_LABELS 1 + struct ctl_table; struct audit_krule; struct user_namespace; diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index f3b54466a037..6149633ff715 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -81,6 +81,7 @@ #include #include #include +#include #include #include @@ -284,13 +285,14 @@ static void superblock_free_security(struct super_block *sb) /* The file system's label must be initialized prior to use. */ -static const char *labeling_behaviors[6] = { +static const char *labeling_behaviors[7] = { "uses xattr", "uses transition SIDs", "uses task SIDs", "uses genfs_contexts", "not configured for labeling", "uses mountpoint labeling", + "uses native labeling", }; static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry); @@ -678,14 +680,21 @@ static int selinux_set_mnt_opts(struct super_block *sb, if (strcmp(sb->s_type->name, "proc") == 0) sbsec->flags |= SE_SBPROC; - /* Determine the labeling behavior to use for this filesystem type. */ - rc = security_fs_use((sbsec->flags & SE_SBPROC) ? "proc" : sb->s_type->name, &sbsec->behavior, &sbsec->sid); - if (rc) { - printk(KERN_WARNING "%s: security_fs_use(%s) returned %d\n", - __func__, sb->s_type->name, rc); - goto out; + if (!sbsec->behavior) { + /* + * Determine the labeling behavior to use for this + * filesystem type. + */ + rc = security_fs_use((sbsec->flags & SE_SBPROC) ? + "proc" : sb->s_type->name, + &sbsec->behavior, &sbsec->sid); + if (rc) { + printk(KERN_WARNING + "%s: security_fs_use(%s) returned %d\n", + __func__, sb->s_type->name, rc); + goto out; + } } - /* sets the context of the superblock for the fs being mounted. */ if (fscontext_sid) { rc = may_context_mount_sb_relabel(fscontext_sid, sbsec, cred); @@ -700,6 +709,11 @@ static int selinux_set_mnt_opts(struct super_block *sb, * sets the label used on all file below the mountpoint, and will set * the superblock context if not already set. */ + if (kern_flags & SECURITY_LSM_NATIVE_LABELS && !context_sid) { + sbsec->behavior = SECURITY_FS_USE_NATIVE; + *set_kern_flags |= SECURITY_LSM_NATIVE_LABELS; + } + if (context_sid) { if (!fscontext_sid) { rc = may_context_mount_sb_relabel(context_sid, sbsec, @@ -731,7 +745,8 @@ static int selinux_set_mnt_opts(struct super_block *sb, } if (defcontext_sid) { - if (sbsec->behavior != SECURITY_FS_USE_XATTR) { + if (sbsec->behavior != SECURITY_FS_USE_XATTR && + sbsec->behavior != SECURITY_FS_USE_NATIVE) { rc = -EINVAL; printk(KERN_WARNING "SELinux: defcontext option is " "invalid for this filesystem type\n"); @@ -1230,6 +1245,8 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent } switch (sbsec->behavior) { + case SECURITY_FS_USE_NATIVE: + break; case SECURITY_FS_USE_XATTR: if (!inode->i_op->getxattr) { isec->sid = sbsec->def_sid; diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 6d3885165d14..8fd8e18ea340 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -169,6 +169,8 @@ int security_get_allow_unknown(void); #define SECURITY_FS_USE_GENFS 4 /* use the genfs support */ #define SECURITY_FS_USE_NONE 5 /* no labeling support */ #define SECURITY_FS_USE_MNTPOINT 6 /* use mountpoint labeling */ +#define SECURITY_FS_USE_NATIVE 7 /* use native label support */ +#define SECURITY_FS_USE_MAX 7 /* Highest SECURITY_FS_USE_XXX */ int security_fs_use(const char *fstype, unsigned int *behavior, u32 *sid); diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 9cd9b7c661ec..c8adde3aff8f 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -2168,7 +2168,10 @@ static int ocontext_read(struct policydb *p, struct policydb_compat_info *info, rc = -EINVAL; c->v.behavior = le32_to_cpu(buf[0]); - if (c->v.behavior > SECURITY_FS_USE_NONE) + /* Determined at runtime, not in policy DB. */ + if (c->v.behavior == SECURITY_FS_USE_MNTPOINT) + goto out; + if (c->v.behavior > SECURITY_FS_USE_MAX) goto out; rc = -ENOMEM; -- cgit v1.2.3 From 42c2c4249cd0192e29eec71e3e94d7bbc383c8de Mon Sep 17 00:00:00 2001 From: Steve Dickson Date: Wed, 22 May 2013 12:50:38 -0400 Subject: NFSv4.2: Added NFS v4.2 support to the NFS client This enable NFSv4.2 support. To enable this code the CONFIG_NFS_V4_2 Kconfig define needs to be set and the -o v4.2 mount option need to be used. Signed-off-by: Steve Dickson Signed-off-by: Trond Myklebust --- fs/nfs/Kconfig | 9 +++++++++ fs/nfs/callback.c | 1 + fs/nfs/callback_xdr.c | 6 +++--- fs/nfs/nfs4client.c | 5 +++++ fs/nfs/nfs4proc.c | 15 +++++++++++++++ fs/nfs/super.c | 7 ++++++- include/linux/nfs4.h | 4 ++++ 7 files changed, 43 insertions(+), 4 deletions(-) diff --git a/fs/nfs/Kconfig b/fs/nfs/Kconfig index 13ca196385f5..a048928340a7 100644 --- a/fs/nfs/Kconfig +++ b/fs/nfs/Kconfig @@ -104,6 +104,15 @@ config NFS_V4_1 If unsure, say N. +config NFS_V4_2 + bool "NFS client support for NFSv4.2" + depends on NFS_V4_1 + help + This option enables support for minor version 2 of the NFSv4 protocol + in the kernel's NFS client. + + If unsure, say N. + config PNFS_FILE_LAYOUT tristate depends on NFS_V4_1 diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index cff089a412c7..78e368d8186d 100644 --- a/fs/nfs/callback.c +++ b/fs/nfs/callback.c @@ -282,6 +282,7 @@ static int nfs_callback_up_net(int minorversion, struct svc_serv *serv, struct n ret = nfs4_callback_up_net(serv, net); break; case 1: + case 2: ret = nfs41_callback_up_net(serv, net); break; default: diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c index 59461c957d9d..e7ee62929811 100644 --- a/fs/nfs/callback_xdr.c +++ b/fs/nfs/callback_xdr.c @@ -166,9 +166,9 @@ static __be32 decode_compound_hdr_arg(struct xdr_stream *xdr, struct cb_compound if (unlikely(p == NULL)) return htonl(NFS4ERR_RESOURCE); hdr->minorversion = ntohl(*p++); - /* Check minor version is zero or one. */ - if (hdr->minorversion <= 1) { - hdr->cb_ident = ntohl(*p++); /* ignored by v4.1 */ + /* Check minor version is zero or one or two. */ + if (hdr->minorversion <= 2) { + hdr->cb_ident = ntohl(*p++); /* ignored by v4.1 and v4.2 */ } else { pr_warn_ratelimited("NFS: %s: NFSv4 server callback with " "illegal minor version %u!\n", diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c index 947b0c908aa9..2a297eeda474 100644 --- a/fs/nfs/nfs4client.c +++ b/fs/nfs/nfs4client.c @@ -66,6 +66,11 @@ struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *cl_init) if (err) goto error; + if (cl_init->minorversion > NFS4_MAX_MINOR_VERSION) { + err = -EINVAL; + goto error; + } + spin_lock_init(&clp->cl_lock); INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state); rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client"); diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 8fbc10054115..abf46f4b5d19 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -7004,11 +7004,26 @@ static const struct nfs4_minor_version_ops nfs_v4_1_minor_ops = { }; #endif +#if defined(CONFIG_NFS_V4_2) +static const struct nfs4_minor_version_ops nfs_v4_2_minor_ops = { + .minor_version = 2, + .call_sync = nfs4_call_sync_sequence, + .match_stateid = nfs41_match_stateid, + .find_root_sec = nfs41_find_root_sec, + .reboot_recovery_ops = &nfs41_reboot_recovery_ops, + .nograce_recovery_ops = &nfs41_nograce_recovery_ops, + .state_renewal_ops = &nfs41_state_renewal_ops, +}; +#endif + const struct nfs4_minor_version_ops *nfs_v4_minor_ops[] = { [0] = &nfs_v4_0_minor_ops, #if defined(CONFIG_NFS_V4_1) [1] = &nfs_v4_1_minor_ops, #endif +#if defined(CONFIG_NFS_V4_2) + [2] = &nfs_v4_2_minor_ops, +#endif }; const struct inode_operations nfs4_dir_inode_operations = { diff --git a/fs/nfs/super.c b/fs/nfs/super.c index c1bbb53d444a..2e94f2168b03 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -269,7 +269,7 @@ static match_table_t nfs_local_lock_tokens = { enum { Opt_vers_2, Opt_vers_3, Opt_vers_4, Opt_vers_4_0, - Opt_vers_4_1, + Opt_vers_4_1, Opt_vers_4_2, Opt_vers_err }; @@ -280,6 +280,7 @@ static match_table_t nfs_vers_tokens = { { Opt_vers_4, "4" }, { Opt_vers_4_0, "4.0" }, { Opt_vers_4_1, "4.1" }, + { Opt_vers_4_2, "4.2" }, { Opt_vers_err, NULL } }; @@ -1097,6 +1098,10 @@ static int nfs_parse_version_string(char *string, mnt->version = 4; mnt->minorversion = 1; break; + case Opt_vers_4_2: + mnt->version = 4; + mnt->minorversion = 2; + break; default: return 0; } diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h index 7764aca1c6b7..42046004a2f6 100644 --- a/include/linux/nfs4.h +++ b/include/linux/nfs4.h @@ -399,11 +399,15 @@ enum lock_type4 { #define NFS4_VERSION 4 #define NFS4_MINOR_VERSION 0 +#if defined(CONFIG_NFS_V4_2) +#define NFS4_MAX_MINOR_VERSION 2 +#else #if defined(CONFIG_NFS_V4_1) #define NFS4_MAX_MINOR_VERSION 1 #else #define NFS4_MAX_MINOR_VERSION 0 #endif /* CONFIG_NFS_V4_1 */ +#endif /* CONFIG_NFS_V4_2 */ #define NFS4_DEBUG 1 -- cgit v1.2.3 From e64a4210f69010d0ff349d5889b50fed51f8bdd0 Mon Sep 17 00:00:00 2001 From: David Quigley Date: Wed, 22 May 2013 12:50:39 -0400 Subject: NFSv4: Add label recommended attribute and NFSv4 flags This patch adds several new flags to allow the NFS client and server to determine if this attribute is supported and if it is being sent over the wire. Signed-off-by: Matthew N. Dodd Signed-off-by: Miguel Rodel Felipe Signed-off-by: Phua Eu Gene Signed-off-by: Khin Mi Mi Aung Signed-off-by: Trond Myklebust --- include/linux/nfs_fs_sb.h | 1 + include/linux/nfs_xdr.h | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 3b7fa2abecca..2ddd00a0848e 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -200,5 +200,6 @@ struct nfs_server { #define NFS_CAP_UIDGID_NOMAP (1U << 15) #define NFS_CAP_STATEID_NFSV41 (1U << 16) #define NFS_CAP_ATOMIC_OPEN_V1 (1U << 17) +#define NFS_CAP_SECURITY_LABEL (1U << 18) #endif diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 104b62f23ee0..bfdf6e042838 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -101,6 +101,7 @@ struct nfs_fattr { #define NFS_ATTR_FATTR_MOUNTED_ON_FILEID (1U << 22) #define NFS_ATTR_FATTR_OWNER_NAME (1U << 23) #define NFS_ATTR_FATTR_GROUP_NAME (1U << 24) +#define NFS_ATTR_FATTR_V4_SECURITY_LABEL (1U << 25) #define NFS_ATTR_FATTR (NFS_ATTR_FATTR_TYPE \ | NFS_ATTR_FATTR_MODE \ @@ -120,7 +121,8 @@ struct nfs_fattr { #define NFS_ATTR_FATTR_V3 (NFS_ATTR_FATTR \ | NFS_ATTR_FATTR_SPACE_USED) #define NFS_ATTR_FATTR_V4 (NFS_ATTR_FATTR \ - | NFS_ATTR_FATTR_SPACE_USED) + | NFS_ATTR_FATTR_SPACE_USED \ + | NFS_ATTR_FATTR_V4_SECURITY_LABEL) /* * Info on the file system -- cgit v1.2.3 From e058f70b8070608fedfd3e39c2ead935beecb552 Mon Sep 17 00:00:00 2001 From: Steve Dickson Date: Wed, 22 May 2013 12:50:40 -0400 Subject: NFSv4: Introduce new label structure In order to mimic the way that NFSv4 ACLs are implemented we have created a structure to be used to pass label data up and down the call chain. This patch adds the new structure and new members to the required NFSv4 call structures. Signed-off-by: Matthew N. Dodd Signed-off-by: Miguel Rodel Felipe Signed-off-by: Phua Eu Gene Signed-off-by: Khin Mi Mi Aung Signed-off-by: Steve Dickson Signed-off-by: Trond Myklebust --- fs/nfs/inode.c | 28 ++++++++++++++++++++++++++++ include/linux/nfs4.h | 9 +++++++++ include/linux/nfs_fs.h | 18 ++++++++++++++++++ include/linux/nfs_xdr.h | 10 ++++++++++ 4 files changed, 65 insertions(+) diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index c1c7a9d78722..07fcf0b90669 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -257,6 +257,34 @@ nfs_init_locked(struct inode *inode, void *opaque) return 0; } +#ifdef CONFIG_NFS_V4_SECURITY_LABEL +struct nfs4_label *nfs4_label_alloc(struct nfs_server *server, gfp_t flags) +{ + struct nfs4_label *label = NULL; + int minor_version = server->nfs_client->cl_minorversion; + + if (minor_version < 2) + return label; + + if (!(server->caps & NFS_CAP_SECURITY_LABEL)) + return label; + + label = kzalloc(sizeof(struct nfs4_label), flags); + if (label == NULL) + return ERR_PTR(-ENOMEM); + + label->label = kzalloc(NFS4_MAXLABELLEN, flags); + if (label->label == NULL) { + kfree(label); + return ERR_PTR(-ENOMEM); + } + label->len = NFS4_MAXLABELLEN; + + return label; +} +EXPORT_SYMBOL_GPL(nfs4_label_alloc); +#endif + /* * This is our front-end to iget that looks up inodes by file handle * instead of inode number. diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h index 42046004a2f6..e36dee52f224 100644 --- a/include/linux/nfs4.h +++ b/include/linux/nfs4.h @@ -32,6 +32,15 @@ struct nfs4_acl { struct nfs4_ace aces[0]; }; +#define NFS4_MAXLABELLEN 2048 + +struct nfs4_label { + uint32_t lfs; + uint32_t pi; + u32 len; + char *label; +}; + typedef struct { char data[NFS4_VERIFIER_SIZE]; } nfs4_verifier; struct nfs_stateid4 { diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index fc01d5cb4cf1..39b24041a4c7 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -496,6 +496,24 @@ extern const struct inode_operations nfs_referral_inode_operations; extern int nfs_mountpoint_expiry_timeout; extern void nfs_release_automount_timer(void); +/* + * linux/fs/nfs/nfs4proc.c + */ +#ifdef CONFIG_NFS_V4_SECURITY_LABEL +extern struct nfs4_label *nfs4_label_alloc(struct nfs_server *server, gfp_t flags); +static inline void nfs4_label_free(struct nfs4_label *label) +{ + if (label) { + kfree(label->label); + kfree(label); + } + return; +} +#else +static inline struct nfs4_label *nfs4_label_alloc(struct nfs_server *server, gfp_t flags) { return NULL; } +static inline void nfs4_label_free(void *label) {} +#endif + /* * linux/fs/nfs/unlink.c */ diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index bfdf6e042838..d799b9f86820 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -349,6 +349,7 @@ struct nfs_openargs { const u32 * open_bitmap; __u32 claim; enum createmode4 createmode; + const struct nfs4_label *label; }; struct nfs_openres { @@ -358,6 +359,7 @@ struct nfs_openres { struct nfs4_change_info cinfo; __u32 rflags; struct nfs_fattr * f_attr; + struct nfs4_label *f_label; struct nfs_seqid * seqid; const struct nfs_server *server; fmode_t delegation_type; @@ -600,6 +602,7 @@ struct nfs_entry { int eof; struct nfs_fh * fh; struct nfs_fattr * fattr; + struct nfs4_label *label; unsigned char d_type; struct nfs_server * server; }; @@ -632,6 +635,7 @@ struct nfs_setattrargs { struct iattr * iap; const struct nfs_server * server; /* Needed for name mapping */ const u32 * bitmask; + const struct nfs4_label *label; }; struct nfs_setaclargs { @@ -667,6 +671,7 @@ struct nfs_getaclres { struct nfs_setattrres { struct nfs4_sequence_res seq_res; struct nfs_fattr * fattr; + struct nfs4_label *label; const struct nfs_server * server; }; @@ -864,6 +869,7 @@ struct nfs4_create_arg { const struct iattr * attrs; const struct nfs_fh * dir_fh; const u32 * bitmask; + const struct nfs4_label *label; }; struct nfs4_create_res { @@ -871,6 +877,7 @@ struct nfs4_create_res { const struct nfs_server * server; struct nfs_fh * fh; struct nfs_fattr * fattr; + struct nfs4_label *label; struct nfs4_change_info dir_cinfo; }; @@ -895,6 +902,7 @@ struct nfs4_getattr_res { struct nfs4_sequence_res seq_res; const struct nfs_server * server; struct nfs_fattr * fattr; + struct nfs4_label *label; }; struct nfs4_link_arg { @@ -909,6 +917,7 @@ struct nfs4_link_res { struct nfs4_sequence_res seq_res; const struct nfs_server * server; struct nfs_fattr * fattr; + struct nfs4_label *label; struct nfs4_change_info cinfo; struct nfs_fattr * dir_attr; }; @@ -926,6 +935,7 @@ struct nfs4_lookup_res { const struct nfs_server * server; struct nfs_fattr * fattr; struct nfs_fh * fh; + struct nfs4_label *label; }; struct nfs4_lookup_root_arg { -- cgit v1.2.3 From a09df2ca2313fd49f0f3e1f2caa546bcacf7b6df Mon Sep 17 00:00:00 2001 From: David Quigley Date: Wed, 22 May 2013 12:50:41 -0400 Subject: NFSv4: Extend fattr bitmaps to support all 3 words The fattr handling bitmap code only uses the first two fattr words sofar. This patch adds the 3rd word to being sent but doesn't populate it yet. Signed-off-by: Miguel Rodel Felipe Signed-off-by: Phua Eu Gene Signed-off-by: Khin Mi Mi Aung Signed-off-by: Trond Myklebust --- fs/nfs/nfs4_fs.h | 6 +++--- fs/nfs/nfs4proc.c | 10 +++++----- fs/nfs/nfs4xdr.c | 20 ++++++++++++-------- fs/nfs/super.c | 1 + include/linux/nfs_fs_sb.h | 2 +- 5 files changed, 22 insertions(+), 17 deletions(-) diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index a1dd768d0a35..283fd284bdb8 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -303,10 +303,10 @@ is_ds_client(struct nfs_client *clp) extern const struct nfs4_minor_version_ops *nfs_v4_minor_ops[]; extern const u32 nfs4_fattr_bitmap[3]; -extern const u32 nfs4_statfs_bitmap[2]; -extern const u32 nfs4_pathconf_bitmap[2]; +extern const u32 nfs4_statfs_bitmap[3]; +extern const u32 nfs4_pathconf_bitmap[3]; extern const u32 nfs4_fsinfo_bitmap[3]; -extern const u32 nfs4_fs_locations_bitmap[2]; +extern const u32 nfs4_fs_locations_bitmap[3]; void nfs4_free_client(struct nfs_client *); diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index abf46f4b5d19..bcf60f15213c 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -161,7 +161,7 @@ static const u32 nfs4_open_noattr_bitmap[3] = { | FATTR4_WORD0_FILEID, }; -const u32 nfs4_statfs_bitmap[2] = { +const u32 nfs4_statfs_bitmap[3] = { FATTR4_WORD0_FILES_AVAIL | FATTR4_WORD0_FILES_FREE | FATTR4_WORD0_FILES_TOTAL, @@ -170,7 +170,7 @@ const u32 nfs4_statfs_bitmap[2] = { | FATTR4_WORD1_SPACE_TOTAL }; -const u32 nfs4_pathconf_bitmap[2] = { +const u32 nfs4_pathconf_bitmap[3] = { FATTR4_WORD0_MAXLINK | FATTR4_WORD0_MAXNAME, 0 @@ -185,7 +185,7 @@ const u32 nfs4_fsinfo_bitmap[3] = { FATTR4_WORD0_MAXFILESIZE FATTR4_WORD2_LAYOUT_BLKSIZE }; -const u32 nfs4_fs_locations_bitmap[2] = { +const u32 nfs4_fs_locations_bitmap[3] = { FATTR4_WORD0_TYPE | FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE @@ -201,7 +201,7 @@ const u32 nfs4_fs_locations_bitmap[2] = { | FATTR4_WORD1_TIME_ACCESS | FATTR4_WORD1_TIME_METADATA | FATTR4_WORD1_TIME_MODIFY - | FATTR4_WORD1_MOUNTED_ON_FILEID + | FATTR4_WORD1_MOUNTED_ON_FILEID, }; static void nfs4_setup_readdir(u64 cookie, __be32 *verifier, struct dentry *dentry, @@ -5318,7 +5318,7 @@ static int _nfs4_proc_fs_locations(struct rpc_clnt *client, struct inode *dir, struct page *page) { struct nfs_server *server = NFS_SERVER(dir); - u32 bitmask[2] = { + u32 bitmask[3] = { [0] = FATTR4_WORD0_FSID | FATTR4_WORD0_FS_LOCATIONS, }; struct nfs4_fs_locations_arg args = { diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 4be8d135ed61..727cfe080eae 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -979,15 +979,16 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const int len; uint32_t bmval0 = 0; uint32_t bmval1 = 0; + uint32_t bmval2 = 0; /* * We reserve enough space to write the entire attribute buffer at once. * In the worst-case, this would be - * 12(bitmap) + 4(attrlen) + 8(size) + 4(mode) + 4(atime) + 4(mtime) - * = 36 bytes, plus any contribution from variable-length fields + * 16(bitmap) + 4(attrlen) + 8(size) + 4(mode) + 4(atime) + 4(mtime) + * = 40 bytes, plus any contribution from variable-length fields * such as owner/group. */ - len = 16; + len = 20; /* Sigh */ if (iap->ia_valid & ATTR_SIZE) @@ -1031,9 +1032,9 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const * We write the bitmap length now, but leave the bitmap and the attribute * buffer length to be backfilled at the end of this routine. */ - *p++ = cpu_to_be32(2); + *p++ = cpu_to_be32(3); q = p; - p += 3; + p += 4; if (iap->ia_valid & ATTR_SIZE) { bmval0 |= FATTR4_WORD0_SIZE; @@ -1080,9 +1081,10 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const len, ((char *)p - (char *)q) + 4); BUG(); } - len = (char *)p - (char *)q - 12; + len = (char *)p - (char *)q - 16; *q++ = htonl(bmval0); *q++ = htonl(bmval1); + *q++ = htonl(bmval2); *q = htonl(len); /* out: */ @@ -1188,8 +1190,10 @@ encode_getattr_three(struct xdr_stream *xdr, static void encode_getfattr(struct xdr_stream *xdr, const u32* bitmask, struct compound_hdr *hdr) { - encode_getattr_two(xdr, bitmask[0] & nfs4_fattr_bitmap[0], - bitmask[1] & nfs4_fattr_bitmap[1], hdr); + encode_getattr_three(xdr, bitmask[0] & nfs4_fattr_bitmap[0], + bitmask[1] & nfs4_fattr_bitmap[1], + bitmask[2] & nfs4_fattr_bitmap[2], + hdr); } static void encode_getfattr_open(struct xdr_stream *xdr, const u32 *bitmask, diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 2e94f2168b03..b30c003b47d1 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -833,6 +833,7 @@ int nfs_show_stats(struct seq_file *m, struct dentry *root) seq_printf(m, "\n\tnfsv4:\t"); seq_printf(m, "bm0=0x%x", nfss->attr_bitmask[0]); seq_printf(m, ",bm1=0x%x", nfss->attr_bitmask[1]); + seq_printf(m, ",bm2=0x%x", nfss->attr_bitmask[2]); seq_printf(m, ",acl=0x%x", nfss->acl_bitmask); show_sessions(m, nfss); show_pnfs(m, nfss); diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 2ddd00a0848e..d4348ab1e0e4 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -146,7 +146,7 @@ struct nfs_server { u32 attr_bitmask[3];/* V4 bitmask representing the set of attributes supported on this filesystem */ - u32 cache_consistency_bitmask[2]; + u32 cache_consistency_bitmask[3]; /* V4 bitmask representing the subset of change attribute, size, ctime and mtime attributes supported by -- cgit v1.2.3 From 1775fd3e805b6a852ef376256967de69284d7962 Mon Sep 17 00:00:00 2001 From: David Quigley Date: Wed, 22 May 2013 12:50:42 -0400 Subject: NFS:Add labels to client function prototypes After looking at all of the nfsv4 operations the label structure has been added to the prototypes of the functions which can transmit label data. Signed-off-by: Matthew N. Dodd Signed-off-by: Miguel Rodel Felipe Signed-off-by: Phua Eu Gene Signed-off-by: Khin Mi Mi Aung Signed-off-by: Steve Dickson Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 2 +- fs/nfs/dir.c | 19 +++++----- fs/nfs/getroot.c | 2 +- fs/nfs/inode.c | 5 +-- fs/nfs/namespace.c | 2 +- fs/nfs/nfs3proc.c | 7 ++-- fs/nfs/nfs4proc.c | 94 ++++++++++++++++++++++++++++++++----------------- fs/nfs/proc.c | 13 +++---- include/linux/nfs_fs.h | 5 +-- include/linux/nfs_xdr.h | 5 +-- 10 files changed, 95 insertions(+), 59 deletions(-) diff --git a/fs/nfs/client.c b/fs/nfs/client.c index c513b0cc835f..c426528deff4 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -1076,7 +1076,7 @@ struct nfs_server *nfs_create_server(struct nfs_mount_info *mount_info, } if (!(fattr->valid & NFS_ATTR_FATTR)) { - error = nfs_mod->rpc_ops->getattr(server, mount_info->mntfh, fattr); + error = nfs_mod->rpc_ops->getattr(server, mount_info->mntfh, fattr, NULL); if (error < 0) { dprintk("nfs_create_server: getattr error = %d\n", -error); goto error; diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index e093e73178b7..e9ab2cd9dd3d 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -460,7 +460,7 @@ void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry) if (dentry == NULL) return; - inode = nfs_fhget(dentry->d_sb, entry->fh, entry->fattr); + inode = nfs_fhget(dentry->d_sb, entry->fh, entry->fattr, entry->label); if (IS_ERR(inode)) goto out; @@ -1040,6 +1040,7 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags) struct dentry *parent; struct nfs_fh *fhandle = NULL; struct nfs_fattr *fattr = NULL; + struct nfs4_label *label = NULL; int error; if (flags & LOOKUP_RCU) @@ -1082,7 +1083,7 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags) if (fhandle == NULL || fattr == NULL) goto out_error; - error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr); + error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, label); if (error) goto out_bad; if (nfs_compare_fh(NFS_FH(inode), fhandle)) @@ -1256,6 +1257,7 @@ struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned in struct inode *inode = NULL; struct nfs_fh *fhandle = NULL; struct nfs_fattr *fattr = NULL; + struct nfs4_label *label = NULL; int error; dfprintk(VFS, "NFS: lookup(%s/%s)\n", @@ -1285,14 +1287,14 @@ struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned in parent = dentry->d_parent; /* Protect against concurrent sillydeletes */ nfs_block_sillyrename(parent); - error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr); + error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, label); if (error == -ENOENT) goto no_entry; if (error < 0) { res = ERR_PTR(error); goto out_unblock_sillyrename; } - inode = nfs_fhget(dentry->d_sb, fhandle, fattr); + inode = nfs_fhget(dentry->d_sb, fhandle, fattr, label); res = ERR_CAST(inode); if (IS_ERR(res)) goto out_unblock_sillyrename; @@ -1528,7 +1530,8 @@ no_open: * Code common to create, mkdir, and mknod. */ int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle, - struct nfs_fattr *fattr) + struct nfs_fattr *fattr, + struct nfs4_label *label) { struct dentry *parent = dget_parent(dentry); struct inode *dir = parent->d_inode; @@ -1541,18 +1544,18 @@ int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle, if (dentry->d_inode) goto out; if (fhandle->size == 0) { - error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr); + error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, NULL); if (error) goto out_error; } nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); if (!(fattr->valid & NFS_ATTR_FATTR)) { struct nfs_server *server = NFS_SB(dentry->d_sb); - error = server->nfs_client->rpc_ops->getattr(server, fhandle, fattr); + error = server->nfs_client->rpc_ops->getattr(server, fhandle, fattr, NULL); if (error < 0) goto out_error; } - inode = nfs_fhget(dentry->d_sb, fhandle, fattr); + inode = nfs_fhget(dentry->d_sb, fhandle, fattr, label); error = PTR_ERR(inode); if (IS_ERR(inode)) goto out_error; diff --git a/fs/nfs/getroot.c b/fs/nfs/getroot.c index 44efaa8c5f78..66984a9aafaa 100644 --- a/fs/nfs/getroot.c +++ b/fs/nfs/getroot.c @@ -95,7 +95,7 @@ struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh, goto out; } - inode = nfs_fhget(sb, mntfh, fsinfo.fattr); + inode = nfs_fhget(sb, mntfh, fsinfo.fattr, NULL); if (IS_ERR(inode)) { dprintk("nfs_get_root: get root inode failed\n"); ret = ERR_CAST(inode); diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 07fcf0b90669..58e7bf876e6c 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -290,7 +290,7 @@ EXPORT_SYMBOL_GPL(nfs4_label_alloc); * instead of inode number. */ struct inode * -nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr) +nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr, struct nfs4_label *label) { struct nfs_find_desc desc = { .fh = fh, @@ -818,6 +818,7 @@ int __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) { int status = -ESTALE; + struct nfs4_label *label = NULL; struct nfs_fattr *fattr = NULL; struct nfs_inode *nfsi = NFS_I(inode); @@ -835,7 +836,7 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) goto out; nfs_inc_stats(inode, NFSIOS_INODEREVALIDATE); - status = NFS_PROTO(inode)->getattr(server, NFS_FH(inode), fattr); + status = NFS_PROTO(inode)->getattr(server, NFS_FH(inode), fattr, label); if (status != 0) { dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) getattr failed, error=%d\n", inode->i_sb->s_id, diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c index fc8dc20fdeb9..348b535cd786 100644 --- a/fs/nfs/namespace.c +++ b/fs/nfs/namespace.c @@ -280,7 +280,7 @@ struct vfsmount *nfs_submount(struct nfs_server *server, struct dentry *dentry, struct dentry *parent = dget_parent(dentry); /* Look it up again to get its attributes */ - err = server->nfs_client->rpc_ops->lookup(parent->d_inode, &dentry->d_name, fh, fattr); + err = server->nfs_client->rpc_ops->lookup(parent->d_inode, &dentry->d_name, fh, fattr, NULL); dput(parent); if (err != 0) return ERR_PTR(err); diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 43ea96ced28c..39c185b03cc0 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -98,7 +98,7 @@ nfs3_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, */ static int nfs3_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_fattr *fattr) + struct nfs_fattr *fattr, struct nfs4_label *label) { struct rpc_message msg = { .rpc_proc = &nfs3_procedures[NFS3PROC_GETATTR], @@ -143,7 +143,8 @@ nfs3_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr, static int nfs3_proc_lookup(struct inode *dir, struct qstr *name, - struct nfs_fh *fhandle, struct nfs_fattr *fattr) + struct nfs_fh *fhandle, struct nfs_fattr *fattr, + struct nfs4_label *label) { struct nfs3_diropargs arg = { .fh = NFS_FH(dir), @@ -300,7 +301,7 @@ static int nfs3_do_create(struct inode *dir, struct dentry *dentry, struct nfs3_ status = rpc_call_sync(NFS_CLIENT(dir), &data->msg, 0); nfs_post_op_update_inode(dir, data->res.dir_attr); if (status == 0) - status = nfs_instantiate(dentry, data->res.fh, data->res.fattr); + status = nfs_instantiate(dentry, data->res.fh, data->res.fattr, NULL); return status; } diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index bcf60f15213c..004de2081554 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -77,11 +77,12 @@ static int _nfs4_recover_proc_open(struct nfs4_opendata *data); static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *); static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *, struct nfs4_state *); static void nfs_fixup_referral_attributes(struct nfs_fattr *fattr); -static int nfs4_proc_getattr(struct nfs_server *, struct nfs_fh *, struct nfs_fattr *); -static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr); +static int nfs4_proc_getattr(struct nfs_server *, struct nfs_fh *, struct nfs_fattr *, struct nfs4_label *label); +static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr, struct nfs4_label *label); static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred, struct nfs_fattr *fattr, struct iattr *sattr, - struct nfs4_state *state); + struct nfs4_state *state, struct nfs4_label *ilabel, + struct nfs4_label *olabel); #ifdef CONFIG_NFS_V4_1 static int nfs41_test_stateid(struct nfs_server *, nfs4_stateid *); static int nfs41_free_stateid(struct nfs_server *, nfs4_stateid *); @@ -762,6 +763,7 @@ struct nfs4_opendata { struct nfs4_string owner_name; struct nfs4_string group_name; struct nfs_fattr f_attr; + struct nfs4_label *f_label; struct dentry *dir; struct dentry *dentry; struct nfs4_state_owner *owner; @@ -807,6 +809,7 @@ nfs4_map_atomic_open_claim(struct nfs_server *server, static void nfs4_init_opendata_res(struct nfs4_opendata *p) { p->o_res.f_attr = &p->f_attr; + p->o_res.f_label = p->f_label; p->o_res.seqid = p->o_arg.seqid; p->c_res.seqid = p->c_arg.seqid; p->o_res.server = p->o_arg.server; @@ -818,6 +821,7 @@ static void nfs4_init_opendata_res(struct nfs4_opendata *p) static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry, struct nfs4_state_owner *sp, fmode_t fmode, int flags, const struct iattr *attrs, + struct nfs4_label *label, enum open_claim_type4 claim, gfp_t gfp_mask) { @@ -854,6 +858,7 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry, p->o_arg.server = server; p->o_arg.bitmask = server->attr_bitmask; p->o_arg.open_bitmap = &nfs4_fattr_bitmap[0]; + p->o_arg.label = label; p->o_arg.claim = nfs4_map_atomic_open_claim(server, claim); switch (p->o_arg.claim) { case NFS4_OPEN_CLAIM_NULL: @@ -1205,7 +1210,7 @@ _nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data) ret = -EAGAIN; if (!(data->f_attr.valid & NFS_ATTR_FATTR)) goto err; - inode = nfs_fhget(data->dir->d_sb, &data->o_res.fh, &data->f_attr); + inode = nfs_fhget(data->dir->d_sb, &data->o_res.fh, &data->f_attr, data->f_label); ret = PTR_ERR(inode); if (IS_ERR(inode)) goto err; @@ -1258,7 +1263,7 @@ static struct nfs4_opendata *nfs4_open_recoverdata_alloc(struct nfs_open_context struct nfs4_opendata *opendata; opendata = nfs4_opendata_alloc(ctx->dentry, state->owner, 0, 0, - NULL, claim, GFP_NOFS); + NULL, NULL, claim, GFP_NOFS); if (opendata == NULL) return ERR_PTR(-ENOMEM); opendata->state = state; @@ -1784,7 +1789,7 @@ static int _nfs4_proc_open(struct nfs4_opendata *data) return status; } if (!(o_res->f_attr->valid & NFS_ATTR_FATTR)) - _nfs4_proc_getattr(server, &o_res->fh, o_res->f_attr); + _nfs4_proc_getattr(server, &o_res->fh, o_res->f_attr, o_res->f_label); return 0; } @@ -1982,6 +1987,7 @@ static int _nfs4_do_open(struct inode *dir, fmode_t fmode, int flags, struct iattr *sattr, + struct nfs4_label *label, struct rpc_cred *cred, struct nfs4_state **res, struct nfs4_threshold **ctx_th) @@ -1991,6 +1997,7 @@ static int _nfs4_do_open(struct inode *dir, struct nfs_server *server = NFS_SERVER(dir); struct nfs4_opendata *opendata; enum open_claim_type4 claim = NFS4_OPEN_CLAIM_NULL; + struct nfs4_label *olabel = NULL; int status; /* Protect against reboot recovery conflicts */ @@ -2009,7 +2016,7 @@ static int _nfs4_do_open(struct inode *dir, if (dentry->d_inode) claim = NFS4_OPEN_CLAIM_FH; opendata = nfs4_opendata_alloc(dentry, sp, fmode, flags, sattr, - claim, GFP_KERNEL); + label, claim, GFP_KERNEL); if (opendata == NULL) goto err_put_state_owner; @@ -2033,10 +2040,11 @@ static int _nfs4_do_open(struct inode *dir, nfs_fattr_init(opendata->o_res.f_attr); status = nfs4_do_setattr(state->inode, cred, opendata->o_res.f_attr, sattr, - state); - if (status == 0) + state, label, olabel); + if (status == 0) { nfs_setattr_update_inode(state->inode, sattr); - nfs_post_op_update_inode(state->inode, opendata->o_res.f_attr); + nfs_post_op_update_inode(state->inode, opendata->o_res.f_attr); + } } if (pnfs_use_threshold(ctx_th, opendata->f_attr.mdsthreshold, server)) @@ -2065,6 +2073,7 @@ static struct nfs4_state *nfs4_do_open(struct inode *dir, fmode_t fmode, int flags, struct iattr *sattr, + struct nfs4_label *label, struct rpc_cred *cred, struct nfs4_threshold **ctx_th) { @@ -2075,7 +2084,7 @@ static struct nfs4_state *nfs4_do_open(struct inode *dir, fmode &= FMODE_READ|FMODE_WRITE|FMODE_EXEC; do { - status = _nfs4_do_open(dir, dentry, fmode, flags, sattr, cred, + status = _nfs4_do_open(dir, dentry, fmode, flags, sattr, label, cred, &res, ctx_th); if (status == 0) break; @@ -2122,7 +2131,8 @@ static struct nfs4_state *nfs4_do_open(struct inode *dir, static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred, struct nfs_fattr *fattr, struct iattr *sattr, - struct nfs4_state *state) + struct nfs4_state *state, struct nfs4_label *ilabel, + struct nfs4_label *olabel) { struct nfs_server *server = NFS_SERVER(inode); struct nfs_setattrargs arg = { @@ -2130,9 +2140,11 @@ static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred, .iap = sattr, .server = server, .bitmask = server->attr_bitmask, + .label = ilabel, }; struct nfs_setattrres res = { .fattr = fattr, + .label = olabel, .server = server, }; struct rpc_message msg = { @@ -2172,7 +2184,8 @@ static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred, static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred, struct nfs_fattr *fattr, struct iattr *sattr, - struct nfs4_state *state) + struct nfs4_state *state, struct nfs4_label *ilabel, + struct nfs4_label *olabel) { struct nfs_server *server = NFS_SERVER(inode); struct nfs4_exception exception = { @@ -2181,7 +2194,7 @@ static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred, }; int err; do { - err = _nfs4_do_setattr(inode, cred, fattr, sattr, state); + err = _nfs4_do_setattr(inode, cred, fattr, sattr, state, ilabel, olabel); switch (err) { case -NFS4ERR_OPENMODE: if (!(sattr->ia_valid & ATTR_SIZE)) { @@ -2426,9 +2439,10 @@ static struct inode * nfs4_atomic_open(struct inode *dir, struct nfs_open_context *ctx, int open_flags, struct iattr *attr) { struct nfs4_state *state; + struct nfs4_label *label = NULL; /* Protect against concurrent sillydeletes */ - state = nfs4_do_open(dir, ctx->dentry, ctx->mode, open_flags, attr, + state = nfs4_do_open(dir, ctx->dentry, ctx->mode, open_flags, attr, label, ctx->cred, &ctx->mdsthreshold); if (IS_ERR(state)) return ERR_CAST(state); @@ -2648,6 +2662,7 @@ static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *mntfh, { int error; struct nfs_fattr *fattr = info->fattr; + struct nfs4_label *label = NULL; error = nfs4_server_capabilities(server, mntfh); if (error < 0) { @@ -2655,7 +2670,7 @@ static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *mntfh, return error; } - error = nfs4_proc_getattr(server, mntfh, fattr); + error = nfs4_proc_getattr(server, mntfh, fattr, label); if (error < 0) { dprintk("nfs4_get_root: getattr error = %d\n", -error); return error; @@ -2711,7 +2726,8 @@ out: return status; } -static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr) +static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, + struct nfs_fattr *fattr, struct nfs4_label *label) { struct nfs4_getattr_arg args = { .fh = fhandle, @@ -2719,6 +2735,7 @@ static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, }; struct nfs4_getattr_res res = { .fattr = fattr, + .label = label, .server = server, }; struct rpc_message msg = { @@ -2731,13 +2748,14 @@ static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0); } -static int nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr) +static int nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, + struct nfs_fattr *fattr, struct nfs4_label *label) { struct nfs4_exception exception = { }; int err; do { err = nfs4_handle_exception(server, - _nfs4_proc_getattr(server, fhandle, fattr), + _nfs4_proc_getattr(server, fhandle, fattr, label), &exception); } while (exception.retry); return err; @@ -2793,7 +2811,7 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr, } } - status = nfs4_do_setattr(inode, cred, fattr, sattr, state); + status = nfs4_do_setattr(inode, cred, fattr, sattr, state, NULL, NULL); if (status == 0) nfs_setattr_update_inode(inode, sattr); return status; @@ -2801,7 +2819,7 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr, static int _nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir, const struct qstr *name, struct nfs_fh *fhandle, - struct nfs_fattr *fattr) + struct nfs_fattr *fattr, struct nfs4_label *label) { struct nfs_server *server = NFS_SERVER(dir); int status; @@ -2839,13 +2857,13 @@ static void nfs_fixup_secinfo_attributes(struct nfs_fattr *fattr) static int nfs4_proc_lookup_common(struct rpc_clnt **clnt, struct inode *dir, struct qstr *name, struct nfs_fh *fhandle, - struct nfs_fattr *fattr) + struct nfs_fattr *fattr, struct nfs4_label *label) { struct nfs4_exception exception = { }; struct rpc_clnt *client = *clnt; int err; do { - err = _nfs4_proc_lookup(client, dir, name, fhandle, fattr); + err = _nfs4_proc_lookup(client, dir, name, fhandle, fattr, label); switch (err) { case -NFS4ERR_BADNAME: err = -ENOENT; @@ -2879,12 +2897,13 @@ out: } static int nfs4_proc_lookup(struct inode *dir, struct qstr *name, - struct nfs_fh *fhandle, struct nfs_fattr *fattr) + struct nfs_fh *fhandle, struct nfs_fattr *fattr, + struct nfs4_label *label) { int status; struct rpc_clnt *client = NFS_CLIENT(dir); - status = nfs4_proc_lookup_common(&client, dir, name, fhandle, fattr); + status = nfs4_proc_lookup_common(&client, dir, name, fhandle, fattr, label); if (client != NFS_CLIENT(dir)) { rpc_shutdown_client(client); nfs_fixup_secinfo_attributes(fattr); @@ -2899,7 +2918,7 @@ nfs4_proc_lookup_mountpoint(struct inode *dir, struct qstr *name, int status; struct rpc_clnt *client = rpc_clone_client(NFS_CLIENT(dir)); - status = nfs4_proc_lookup_common(&client, dir, name, fhandle, fattr); + status = nfs4_proc_lookup_common(&client, dir, name, fhandle, fattr, NULL); if (status < 0) { rpc_shutdown_client(client); return ERR_PTR(status); @@ -3029,6 +3048,7 @@ static int nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, int flags) { + struct nfs4_label *ilabel = NULL; struct nfs_open_context *ctx; struct nfs4_state *state; int status = 0; @@ -3039,7 +3059,7 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, sattr->ia_mode &= ~current_umask(); state = nfs4_do_open(dir, dentry, ctx->mode, - flags, sattr, ctx->cred, + flags, sattr, ilabel, ctx->cred, &ctx->mdsthreshold); d_drop(dentry); if (IS_ERR(state)) { @@ -3207,6 +3227,7 @@ static int _nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr * }; struct nfs4_link_res res = { .server = server, + .label = NULL, }; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LINK], @@ -3247,6 +3268,7 @@ struct nfs4_createdata { struct nfs4_create_res res; struct nfs_fh fh; struct nfs_fattr fattr; + struct nfs4_label *label; }; static struct nfs4_createdata *nfs4_alloc_createdata(struct inode *dir, @@ -3270,6 +3292,7 @@ static struct nfs4_createdata *nfs4_alloc_createdata(struct inode *dir, data->res.server = server; data->res.fh = &data->fh; data->res.fattr = &data->fattr; + data->res.label = data->label; nfs_fattr_init(data->res.fattr); } return data; @@ -3281,7 +3304,7 @@ static int nfs4_do_create(struct inode *dir, struct dentry *dentry, struct nfs4_ &data->arg.seq_args, &data->res.seq_res, 1); if (status == 0) { update_changeattr(dir, &data->res.dir_cinfo); - status = nfs_instantiate(dentry, data->res.fh, data->res.fattr); + status = nfs_instantiate(dentry, data->res.fh, data->res.fattr, data->res.label); } return status; } @@ -3292,7 +3315,8 @@ static void nfs4_free_createdata(struct nfs4_createdata *data) } static int _nfs4_proc_symlink(struct inode *dir, struct dentry *dentry, - struct page *page, unsigned int len, struct iattr *sattr) + struct page *page, unsigned int len, struct iattr *sattr, + struct nfs4_label *label) { struct nfs4_createdata *data; int status = -ENAMETOOLONG; @@ -3308,6 +3332,7 @@ static int _nfs4_proc_symlink(struct inode *dir, struct dentry *dentry, data->msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SYMLINK]; data->arg.u.symlink.pages = &page; data->arg.u.symlink.len = len; + data->arg.label = label; status = nfs4_do_create(dir, dentry, data); @@ -3320,18 +3345,19 @@ static int nfs4_proc_symlink(struct inode *dir, struct dentry *dentry, struct page *page, unsigned int len, struct iattr *sattr) { struct nfs4_exception exception = { }; + struct nfs4_label *label = NULL; int err; do { err = nfs4_handle_exception(NFS_SERVER(dir), _nfs4_proc_symlink(dir, dentry, page, - len, sattr), + len, sattr, label), &exception); } while (exception.retry); return err; } static int _nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry, - struct iattr *sattr) + struct iattr *sattr, struct nfs4_label *label) { struct nfs4_createdata *data; int status = -ENOMEM; @@ -3340,6 +3366,7 @@ static int _nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry, if (data == NULL) goto out; + data->arg.label = label; status = nfs4_do_create(dir, dentry, data); nfs4_free_createdata(data); @@ -3351,12 +3378,13 @@ static int nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry, struct iattr *sattr) { struct nfs4_exception exception = { }; + struct nfs4_label *label = NULL; int err; sattr->ia_mode &= ~current_umask(); do { err = nfs4_handle_exception(NFS_SERVER(dir), - _nfs4_proc_mkdir(dir, dentry, sattr), + _nfs4_proc_mkdir(dir, dentry, sattr, label), &exception); } while (exception.retry); return err; @@ -3441,7 +3469,7 @@ static int _nfs4_proc_mknod(struct inode *dir, struct dentry *dentry, status = -EINVAL; goto out_free; } - + status = nfs4_do_create(dir, dentry, data); out_free: nfs4_free_createdata(data); diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index fc8de9016acf..c041c41f7a52 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -98,7 +98,7 @@ nfs_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle, */ static int nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, - struct nfs_fattr *fattr) + struct nfs_fattr *fattr, struct nfs4_label *label) { struct rpc_message msg = { .rpc_proc = &nfs_procedures[NFSPROC_GETATTR], @@ -146,7 +146,8 @@ nfs_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr, static int nfs_proc_lookup(struct inode *dir, struct qstr *name, - struct nfs_fh *fhandle, struct nfs_fattr *fattr) + struct nfs_fh *fhandle, struct nfs_fattr *fattr, + struct nfs4_label *label) { struct nfs_diropargs arg = { .fh = NFS_FH(dir), @@ -243,7 +244,7 @@ nfs_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); nfs_mark_for_revalidate(dir); if (status == 0) - status = nfs_instantiate(dentry, data->res.fh, data->res.fattr); + status = nfs_instantiate(dentry, data->res.fh, data->res.fattr, NULL); nfs_free_createdata(data); out: dprintk("NFS reply create: %d\n", status); @@ -290,7 +291,7 @@ nfs_proc_mknod(struct inode *dir, struct dentry *dentry, struct iattr *sattr, status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); } if (status == 0) - status = nfs_instantiate(dentry, data->res.fh, data->res.fattr); + status = nfs_instantiate(dentry, data->res.fh, data->res.fattr, NULL); nfs_free_createdata(data); out: dprintk("NFS reply mknod: %d\n", status); @@ -442,7 +443,7 @@ nfs_proc_symlink(struct inode *dir, struct dentry *dentry, struct page *page, * should fill in the data with a LOOKUP call on the wire. */ if (status == 0) - status = nfs_instantiate(dentry, fh, fattr); + status = nfs_instantiate(dentry, fh, fattr, NULL); out_free: nfs_free_fattr(fattr); @@ -471,7 +472,7 @@ nfs_proc_mkdir(struct inode *dir, struct dentry *dentry, struct iattr *sattr) status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); nfs_mark_for_revalidate(dir); if (status == 0) - status = nfs_instantiate(dentry, data->res.fh, data->res.fattr); + status = nfs_instantiate(dentry, data->res.fh, data->res.fattr, NULL); nfs_free_createdata(data); out: dprintk("NFS reply mkdir: %d\n", status); diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 39b24041a4c7..3489015950b4 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -336,7 +336,7 @@ extern void nfs_zap_mapping(struct inode *inode, struct address_space *mapping); extern void nfs_zap_caches(struct inode *); extern void nfs_invalidate_atime(struct inode *); extern struct inode *nfs_fhget(struct super_block *, struct nfs_fh *, - struct nfs_fattr *); + struct nfs_fattr *, struct nfs4_label *); extern int nfs_refresh_inode(struct inode *, struct nfs_fattr *); extern int nfs_post_op_update_inode(struct inode *inode, struct nfs_fattr *fattr); extern int nfs_post_op_update_inode_force_wcc(struct inode *inode, struct nfs_fattr *fattr); @@ -468,7 +468,8 @@ extern const struct file_operations nfs_dir_operations; extern const struct dentry_operations nfs_dentry_operations; extern void nfs_force_lookup_revalidate(struct inode *dir); -extern int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fh, struct nfs_fattr *fattr); +extern int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fh, + struct nfs_fattr *fattr, struct nfs4_label *label); extern int nfs_may_open(struct inode *inode, struct rpc_cred *cred, int openflags); extern void nfs_access_zap_cache(struct inode *inode); diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index d799b9f86820..ed31ba7a6f9e 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1378,11 +1378,12 @@ struct nfs_rpc_ops { struct dentry *(*try_mount) (int, const char *, struct nfs_mount_info *, struct nfs_subversion *); int (*getattr) (struct nfs_server *, struct nfs_fh *, - struct nfs_fattr *); + struct nfs_fattr *, struct nfs4_label *); int (*setattr) (struct dentry *, struct nfs_fattr *, struct iattr *); int (*lookup) (struct inode *, struct qstr *, - struct nfs_fh *, struct nfs_fattr *); + struct nfs_fh *, struct nfs_fattr *, + struct nfs4_label *); int (*access) (struct inode *, struct nfs_access_entry *); int (*readlink)(struct inode *, struct page *, unsigned int, unsigned int); -- cgit v1.2.3 From 14c43f767818c42f91ec7ffa586ee975845f68c8 Mon Sep 17 00:00:00 2001 From: David Quigley Date: Wed, 22 May 2013 12:50:43 -0400 Subject: NFS: Add label lifecycle management This patch adds the lifecycle management for the security label structure introduced in an earlier patch. The label is not used yet but allocations and freeing of the structure is handled. Signed-off-by: Matthew N. Dodd Signed-off-by: Miguel Rodel Felipe Signed-off-by: Phua Eu Gene Signed-off-by: Khin Mi Mi Aung Signed-off-by: Steve Dickson Signed-off-by: Trond Myklebust --- fs/nfs/dir.c | 23 ++++++++++++++++++- fs/nfs/inode.c | 15 +++++++++--- fs/nfs/nfs4proc.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 96 insertions(+), 11 deletions(-) diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index e9ab2cd9dd3d..736b607ac8a8 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -585,10 +585,16 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page, if (entry.fh == NULL || entry.fattr == NULL) goto out; + entry.label = nfs4_label_alloc(NFS_SERVER(inode), GFP_NOWAIT); + if (IS_ERR(entry.label)) { + status = PTR_ERR(entry.label); + goto out; + } + array = nfs_readdir_get_array(page); if (IS_ERR(array)) { status = PTR_ERR(array); - goto out; + goto out_label_free; } memset(array, 0, sizeof(struct nfs_cache_array)); array->eof_index = -1; @@ -614,6 +620,8 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page, nfs_readdir_free_large_page(pages_ptr, pages, array_size); out_release_array: nfs_readdir_release_array(page); +out_label_free: + nfs4_label_free(entry.label); out: nfs_free_fattr(entry.fattr); nfs_free_fhandle(entry.fh); @@ -1083,6 +1091,10 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags) if (fhandle == NULL || fattr == NULL) goto out_error; + label = nfs4_label_alloc(NFS_SERVER(inode), GFP_NOWAIT); + if (IS_ERR(label)) + goto out_error; + error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, label); if (error) goto out_bad; @@ -1093,6 +1105,8 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags) nfs_free_fattr(fattr); nfs_free_fhandle(fhandle); + nfs4_label_free(label); + out_set_verifier: nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); out_valid: @@ -1109,6 +1123,7 @@ out_zap_parent: out_bad: nfs_free_fattr(fattr); nfs_free_fhandle(fhandle); + nfs4_label_free(label); nfs_mark_for_revalidate(dir); if (inode && S_ISDIR(inode->i_mode)) { /* Purge readdir caches. */ @@ -1129,6 +1144,7 @@ out_zap_parent: out_error: nfs_free_fattr(fattr); nfs_free_fhandle(fhandle); + nfs4_label_free(label); dput(parent); dfprintk(LOOKUPCACHE, "NFS: %s(%s/%s) lookup returned error %d\n", __func__, dentry->d_parent->d_name.name, @@ -1284,6 +1300,10 @@ struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned in if (fhandle == NULL || fattr == NULL) goto out; + label = nfs4_label_alloc(NFS_SERVER(dir), GFP_NOWAIT); + if (IS_ERR(label)) + goto out; + parent = dentry->d_parent; /* Protect against concurrent sillydeletes */ nfs_block_sillyrename(parent); @@ -1312,6 +1332,7 @@ no_entry: nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); out_unblock_sillyrename: nfs_unblock_sillyrename(parent); + nfs4_label_free(label); out: nfs_free_fattr(fattr); nfs_free_fhandle(fhandle); diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 58e7bf876e6c..12e8ad85ae50 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -836,6 +836,13 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) goto out; nfs_inc_stats(inode, NFSIOS_INODEREVALIDATE); + + label = nfs4_label_alloc(NFS_SERVER(inode), GFP_KERNEL); + if (IS_ERR(label)) { + status = PTR_ERR(label); + goto out; + } + status = NFS_PROTO(inode)->getattr(server, NFS_FH(inode), fattr, label); if (status != 0) { dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) getattr failed, error=%d\n", @@ -846,7 +853,7 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) if (!S_ISDIR(inode->i_mode)) set_bit(NFS_INO_STALE, &NFS_I(inode)->flags); } - goto out; + goto err_out; } status = nfs_refresh_inode(inode, fattr); @@ -854,7 +861,7 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) refresh failed, error=%d\n", inode->i_sb->s_id, (long long)NFS_FILEID(inode), status); - goto out; + goto err_out; } if (nfsi->cache_validity & NFS_INO_INVALID_ACL) @@ -864,7 +871,9 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) inode->i_sb->s_id, (long long)NFS_FILEID(inode)); - out: +err_out: + nfs4_label_free(label); +out: nfs_free_fattr(fattr); return status; } diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 004de2081554..e9488f5e1037 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -833,9 +833,14 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry, p = kzalloc(sizeof(*p), gfp_mask); if (p == NULL) goto err; + + p->f_label = nfs4_label_alloc(server, gfp_mask); + if (IS_ERR(p->f_label)) + goto err_free_p; + p->o_arg.seqid = nfs_alloc_seqid(&sp->so_seqid, gfp_mask); if (p->o_arg.seqid == NULL) - goto err_free; + goto err_free_label; nfs_sb_active(dentry->d_sb); p->dentry = dget(dentry); p->dir = parent; @@ -889,7 +894,10 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry, nfs4_init_opendata_res(p); kref_init(&p->kref); return p; -err_free: + +err_free_label: + nfs4_label_free(p->f_label); +err_free_p: kfree(p); err: dput(parent); @@ -906,6 +914,9 @@ static void nfs4_opendata_free(struct kref *kref) if (p->state != NULL) nfs4_put_open_state(p->state); nfs4_put_state_owner(p->owner); + + nfs4_label_free(p->f_label); + dput(p->dir); dput(p->dentry); nfs_sb_deactive(sb); @@ -2020,10 +2031,18 @@ static int _nfs4_do_open(struct inode *dir, if (opendata == NULL) goto err_put_state_owner; + if (label) { + olabel = nfs4_label_alloc(server, GFP_KERNEL); + if (IS_ERR(olabel)) { + status = PTR_ERR(olabel); + goto err_opendata_put; + } + } + if (ctx_th && server->attr_bitmask[2] & FATTR4_WORD2_MDSTHRESHOLD) { opendata->f_attr.mdsthreshold = pnfs_mdsthreshold_alloc(); if (!opendata->f_attr.mdsthreshold) - goto err_opendata_put; + goto err_free_label; opendata->o_arg.open_bitmap = &nfs4_pnfs_open_bitmap[0]; } if (dentry->d_inode != NULL) @@ -2031,7 +2050,7 @@ static int _nfs4_do_open(struct inode *dir, status = _nfs4_open_and_get_state(opendata, fmode, flags, &state); if (status != 0) - goto err_opendata_put; + goto err_free_label; if ((opendata->o_arg.open_flags & O_EXCL) && (opendata->o_arg.createmode != NFS4_CREATE_GUARDED)) { @@ -2053,10 +2072,14 @@ static int _nfs4_do_open(struct inode *dir, kfree(opendata->f_attr.mdsthreshold); opendata->f_attr.mdsthreshold = NULL; + nfs4_label_free(olabel); + nfs4_opendata_put(opendata); nfs4_put_state_owner(sp); *res = state; return 0; +err_free_label: + nfs4_label_free(olabel); err_opendata_put: kfree(opendata->f_attr.mdsthreshold); nfs4_opendata_put(opendata); @@ -2670,16 +2693,23 @@ static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *mntfh, return error; } + label = nfs4_label_alloc(server, GFP_KERNEL); + if (IS_ERR(label)) + return PTR_ERR(label); + error = nfs4_proc_getattr(server, mntfh, fattr, label); if (error < 0) { dprintk("nfs4_get_root: getattr error = %d\n", -error); - return error; + goto err_free_label; } if (fattr->valid & NFS_ATTR_FATTR_FSID && !nfs_fsid_equal(&server->fsid, &fattr->fsid)) memcpy(&server->fsid, &fattr->fsid, sizeof(server->fsid)); +err_free_label: + nfs4_label_free(label); + return error; } @@ -2785,6 +2815,7 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr, struct inode *inode = dentry->d_inode; struct rpc_cred *cred = NULL; struct nfs4_state *state = NULL; + struct nfs4_label *label = NULL; int status; if (pnfs_ld_layoutret_on_setattr(inode)) @@ -2811,9 +2842,15 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr, } } - status = nfs4_do_setattr(inode, cred, fattr, sattr, state, NULL, NULL); + label = nfs4_label_alloc(NFS_SERVER(inode), GFP_KERNEL); + if (IS_ERR(label)) + return PTR_ERR(label); + + status = nfs4_do_setattr(inode, cred, fattr, sattr, state, NULL, label); if (status == 0) nfs_setattr_update_inode(inode, sattr); + + nfs4_label_free(label); return status; } @@ -3193,7 +3230,7 @@ static int _nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name, .rpc_resp = &res, }; int status = -ENOMEM; - + status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1); if (!status) { update_changeattr(old_dir, &res.old_cinfo); @@ -3240,11 +3277,21 @@ static int _nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr * if (res.fattr == NULL) goto out; + res.label = nfs4_label_alloc(server, GFP_KERNEL); + if (IS_ERR(res.label)) { + status = PTR_ERR(res.label); + goto out; + } + status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1); if (!status) { update_changeattr(dir, &res.cinfo); nfs_post_op_update_inode(inode, res.fattr); } + + + nfs4_label_free(res.label); + out: nfs_free_fattr(res.fattr); return status; @@ -3280,6 +3327,10 @@ static struct nfs4_createdata *nfs4_alloc_createdata(struct inode *dir, if (data != NULL) { struct nfs_server *server = NFS_SERVER(dir); + data->label = nfs4_label_alloc(server, GFP_KERNEL); + if (IS_ERR(data->label)) + goto out_free; + data->msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CREATE]; data->msg.rpc_argp = &data->arg; data->msg.rpc_resp = &data->res; @@ -3296,6 +3347,9 @@ static struct nfs4_createdata *nfs4_alloc_createdata(struct inode *dir, nfs_fattr_init(data->res.fattr); } return data; +out_free: + kfree(data); + return NULL; } static int nfs4_do_create(struct inode *dir, struct dentry *dentry, struct nfs4_createdata *data) @@ -3311,6 +3365,7 @@ static int nfs4_do_create(struct inode *dir, struct dentry *dentry, struct nfs4_ static void nfs4_free_createdata(struct nfs4_createdata *data) { + nfs4_label_free(data->label); kfree(data); } -- cgit v1.2.3 From aa9c2669626ca7e5e5bab28e6caeb583fd40099b Mon Sep 17 00:00:00 2001 From: David Quigley Date: Wed, 22 May 2013 12:50:44 -0400 Subject: NFS: Client implementation of Labeled-NFS This patch implements the client transport and handling support for labeled NFS. The patch adds two functions to encode and decode the security label recommended attribute which makes use of the LSM hooks added earlier. It also adds code to grab the label from the file attribute structures and encode the label to be sent back to the server. Acked-by: James Morris Signed-off-by: Matthew N. Dodd Signed-off-by: Miguel Rodel Felipe Signed-off-by: Phua Eu Gene Signed-off-by: Khin Mi Mi Aung Signed-off-by: Steve Dickson Signed-off-by: Trond Myklebust --- fs/nfs/dir.c | 7 +- fs/nfs/inode.c | 61 ++++++++-- fs/nfs/nfs4proc.c | 292 +++++++++++++++++++++++++++++++++++++++++++--- fs/nfs/nfs4xdr.c | 154 +++++++++++++++++++----- fs/nfs/super.c | 17 ++- include/linux/nfs_fs.h | 3 + include/linux/nfs_fs_sb.h | 5 + security/selinux/hooks.c | 4 + 8 files changed, 489 insertions(+), 54 deletions(-) diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 736b607ac8a8..743d3b524fc5 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -435,6 +435,7 @@ void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry) struct dentry *alias; struct inode *dir = parent->d_inode; struct inode *inode; + int status; if (filename.name[0] == '.') { if (filename.len == 1) @@ -447,7 +448,9 @@ void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry) dentry = d_lookup(parent, &filename); if (dentry != NULL) { if (nfs_same_file(dentry, entry)) { - nfs_refresh_inode(dentry->d_inode, entry->fattr); + status = nfs_refresh_inode(dentry->d_inode, entry->fattr); + if (!status) + nfs_setsecurity(dentry->d_inode, entry->fattr, entry->label); goto out; } else { if (d_invalidate(dentry) != 0) @@ -1103,6 +1106,8 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags) if ((error = nfs_refresh_inode(inode, fattr)) != 0) goto out_bad; + nfs_setsecurity(inode, fattr, label); + nfs_free_fattr(fattr); nfs_free_fhandle(fhandle); nfs4_label_free(label); diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 12e8ad85ae50..f908af672197 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -162,11 +162,19 @@ static void nfs_zap_caches_locked(struct inode *inode) memset(NFS_I(inode)->cookieverf, 0, sizeof(NFS_I(inode)->cookieverf)); if (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)) { - nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL|NFS_INO_REVAL_PAGECACHE; nfs_fscache_invalidate(inode); - } else { - nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL|NFS_INO_REVAL_PAGECACHE; - } + nfsi->cache_validity |= NFS_INO_INVALID_ATTR + | NFS_INO_INVALID_LABEL + | NFS_INO_INVALID_DATA + | NFS_INO_INVALID_ACCESS + | NFS_INO_INVALID_ACL + | NFS_INO_REVAL_PAGECACHE; + } else + nfsi->cache_validity |= NFS_INO_INVALID_ATTR + | NFS_INO_INVALID_LABEL + | NFS_INO_INVALID_ACCESS + | NFS_INO_INVALID_ACL + | NFS_INO_REVAL_PAGECACHE; } void nfs_zap_caches(struct inode *inode) @@ -258,6 +266,32 @@ nfs_init_locked(struct inode *inode, void *opaque) } #ifdef CONFIG_NFS_V4_SECURITY_LABEL +void nfs_setsecurity(struct inode *inode, struct nfs_fattr *fattr, + struct nfs4_label *label) +{ + int error; + + if (label == NULL) + return; + + if (nfs_server_capable(inode, NFS_CAP_SECURITY_LABEL) == 0) + return; + + if (NFS_SERVER(inode)->nfs_client->cl_minorversion < 2) + return; + + if ((fattr->valid & NFS_ATTR_FATTR_V4_SECURITY_LABEL) && inode->i_security) { + error = security_inode_notifysecctx(inode, label->label, + label->len); + if (error) + printk(KERN_ERR "%s() %s %d " + "security_inode_notifysecctx() %d\n", + __func__, + (char *)label->label, + label->len, error); + } +} + struct nfs4_label *nfs4_label_alloc(struct nfs_server *server, gfp_t flags) { struct nfs4_label *label = NULL; @@ -283,7 +317,13 @@ struct nfs4_label *nfs4_label_alloc(struct nfs_server *server, gfp_t flags) return label; } EXPORT_SYMBOL_GPL(nfs4_label_alloc); +#else +void inline nfs_setsecurity(struct inode *inode, struct nfs_fattr *fattr, + struct nfs4_label *label) +{ +} #endif +EXPORT_SYMBOL_GPL(nfs_setsecurity); /* * This is our front-end to iget that looks up inodes by file handle @@ -412,6 +452,9 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr, st */ inode->i_blocks = nfs_calc_block_size(fattr->du.nfs3.used); } + + nfs_setsecurity(inode, fattr, label); + nfsi->attrtimeo = NFS_MINATTRTIMEO(inode); nfsi->attrtimeo_timestamp = now; nfsi->access_cache = RB_ROOT; @@ -421,6 +464,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr, st unlock_new_inode(inode); } else nfs_refresh_inode(inode, fattr); + nfs_setsecurity(inode, fattr, label); dprintk("NFS: nfs_fhget(%s/%Ld fh_crc=0x%08x ct=%d)\n", inode->i_sb->s_id, (long long)NFS_FILEID(inode), @@ -477,7 +521,7 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr) NFS_PROTO(inode)->return_delegation(inode); error = NFS_PROTO(inode)->setattr(dentry, fattr, attr); if (error == 0) - nfs_refresh_inode(inode, fattr); + error = nfs_refresh_inode(inode, fattr); nfs_free_fattr(fattr); out: return error; @@ -901,7 +945,8 @@ static int nfs_attribute_cache_expired(struct inode *inode) */ int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) { - if (!(NFS_I(inode)->cache_validity & NFS_INO_INVALID_ATTR) + if (!(NFS_I(inode)->cache_validity & + (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_LABEL)) && !nfs_attribute_cache_expired(inode)) return NFS_STALE(inode) ? -ESTALE : 0; return __nfs_revalidate_inode(server, inode); @@ -1281,6 +1326,7 @@ int nfs_post_op_update_inode(struct inode *inode, struct nfs_fattr *fattr) spin_lock(&inode->i_lock); status = nfs_post_op_update_inode_locked(inode, fattr); spin_unlock(&inode->i_lock); + return status; } EXPORT_SYMBOL_GPL(nfs_post_op_update_inode); @@ -1521,7 +1567,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr) inode->i_blocks = fattr->du.nfs2.blocks; /* Update attrtimeo value if we're out of the unstable period */ - if (invalid & NFS_INO_INVALID_ATTR) { + if (invalid & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_LABEL)) { nfs_inc_stats(inode, NFSIOS_ATTRINVALIDATE); nfsi->attrtimeo = NFS_MINATTRTIMEO(inode); nfsi->attrtimeo_timestamp = now; @@ -1534,6 +1580,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr) } } invalid &= ~NFS_INO_INVALID_ATTR; + invalid &= ~NFS_INO_INVALID_LABEL; /* Don't invalidate the data if we were to blame */ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index e9488f5e1037..1dc7aec8e946 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -87,6 +87,56 @@ static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred, static int nfs41_test_stateid(struct nfs_server *, nfs4_stateid *); static int nfs41_free_stateid(struct nfs_server *, nfs4_stateid *); #endif + +#ifdef CONFIG_NFS_V4_SECURITY_LABEL +static inline struct nfs4_label * +nfs4_label_init_security(struct inode *dir, struct dentry *dentry, + struct iattr *sattr, struct nfs4_label *label) +{ + int err; + + if (label == NULL) + return NULL; + + if (nfs_server_capable(dir, NFS_CAP_SECURITY_LABEL) == 0) + return NULL; + + if (NFS_SERVER(dir)->nfs_client->cl_minorversion < 2) + return NULL; + + err = security_dentry_init_security(dentry, sattr->ia_mode, + &dentry->d_name, (void **)&label->label, &label->len); + if (err == 0) + return label; + + return NULL; +} +static inline void +nfs4_label_release_security(struct nfs4_label *label) +{ + if (label) + security_release_secctx(label->label, label->len); +} +static inline u32 *nfs4_bitmask(struct nfs_server *server, struct nfs4_label *label) +{ + if (label) + return server->attr_bitmask; + + return server->attr_bitmask_nl; +} +#else +static inline struct nfs4_label * +nfs4_label_init_security(struct inode *dir, struct dentry *dentry, + struct iattr *sattr, struct nfs4_label *l) +{ return NULL; } +static inline void +nfs4_label_release_security(struct nfs4_label *label) +{ return; } +static inline u32 * +nfs4_bitmask(struct nfs_server *server, struct nfs4_label *label) +{ return server->attr_bitmask; } +#endif + /* Prevent leaks of NFSv4 errors into userland */ static int nfs4_map_errors(int err) { @@ -135,7 +185,10 @@ const u32 nfs4_fattr_bitmap[3] = { | FATTR4_WORD1_SPACE_USED | FATTR4_WORD1_TIME_ACCESS | FATTR4_WORD1_TIME_METADATA - | FATTR4_WORD1_TIME_MODIFY + | FATTR4_WORD1_TIME_MODIFY, +#ifdef CONFIG_NFS_V4_SECURITY_LABEL + FATTR4_WORD2_SECURITY_LABEL +#endif }; static const u32 nfs4_pnfs_open_bitmap[3] = { @@ -861,7 +914,7 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry, p->o_arg.id.uniquifier = sp->so_seqid.owner_id; p->o_arg.name = &dentry->d_name; p->o_arg.server = server; - p->o_arg.bitmask = server->attr_bitmask; + p->o_arg.bitmask = nfs4_bitmask(server, label); p->o_arg.open_bitmap = &nfs4_fattr_bitmap[0]; p->o_arg.label = label; p->o_arg.claim = nfs4_map_atomic_open_claim(server, claim); @@ -1195,6 +1248,8 @@ _nfs4_opendata_reclaim_to_nfs4_state(struct nfs4_opendata *data) if (ret) goto err; + nfs_setsecurity(inode, &data->f_attr, data->f_label); + if (data->o_res.delegation_type != 0) nfs4_opendata_check_deleg(data, state); update_open_stateid(state, &data->o_res.stateid, NULL, @@ -2063,6 +2118,7 @@ static int _nfs4_do_open(struct inode *dir, if (status == 0) { nfs_setattr_update_inode(state->inode, sattr); nfs_post_op_update_inode(state->inode, opendata->o_res.f_attr); + nfs_setsecurity(state->inode, opendata->o_res.f_attr, olabel); } } @@ -2181,6 +2237,10 @@ static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred, bool truncate; int status; + arg.bitmask = nfs4_bitmask(server, ilabel); + if (ilabel) + arg.bitmask = nfs4_bitmask(server, olabel); + nfs_fattr_init(fattr); /* Servers should only apply open mode checks for file size changes */ @@ -2462,11 +2522,16 @@ static struct inode * nfs4_atomic_open(struct inode *dir, struct nfs_open_context *ctx, int open_flags, struct iattr *attr) { struct nfs4_state *state; - struct nfs4_label *label = NULL; + struct nfs4_label l = {0, 0, 0, NULL}, *label = NULL; + + label = nfs4_label_init_security(dir, ctx->dentry, attr, &l); /* Protect against concurrent sillydeletes */ state = nfs4_do_open(dir, ctx->dentry, ctx->mode, open_flags, attr, label, ctx->cred, &ctx->mdsthreshold); + + nfs4_label_release_security(label); + if (IS_ERR(state)) return ERR_CAST(state); ctx->state = state; @@ -2526,7 +2591,17 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f server->caps |= NFS_CAP_CTIME; if (res.attr_bitmask[1] & FATTR4_WORD1_TIME_MODIFY) server->caps |= NFS_CAP_MTIME; +#ifdef CONFIG_NFS_V4_SECURITY_LABEL + if (res.attr_bitmask[2] & FATTR4_WORD2_SECURITY_LABEL) + server->caps |= NFS_CAP_SECURITY_LABEL; +#endif + memcpy(server->attr_bitmask_nl, res.attr_bitmask, + sizeof(server->attr_bitmask)); + if (server->caps & NFS_CAP_SECURITY_LABEL) { + server->attr_bitmask_nl[2] &= ~FATTR4_WORD2_SECURITY_LABEL; + res.attr_bitmask[2] &= ~FATTR4_WORD2_SECURITY_LABEL; + } memcpy(server->cache_consistency_bitmask, res.attr_bitmask, sizeof(server->cache_consistency_bitmask)); server->cache_consistency_bitmask[0] &= FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE; server->cache_consistency_bitmask[1] &= FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY; @@ -2552,8 +2627,9 @@ int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle) static int _nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsinfo *info) { + u32 bitmask[3]; struct nfs4_lookup_root_arg args = { - .bitmask = nfs4_fattr_bitmap, + .bitmask = bitmask, }; struct nfs4_lookup_res res = { .server = server, @@ -2566,6 +2642,13 @@ static int _nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle, .rpc_resp = &res, }; + bitmask[0] = nfs4_fattr_bitmap[0]; + bitmask[1] = nfs4_fattr_bitmap[1]; + /* + * Process the label in the upcoming getfattr + */ + bitmask[2] = nfs4_fattr_bitmap[2] & ~FATTR4_WORD2_SECURITY_LABEL; + nfs_fattr_init(info->fattr); return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0); } @@ -2773,7 +2856,9 @@ static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, .rpc_argp = &args, .rpc_resp = &res, }; - + + args.bitmask = nfs4_bitmask(server, label); + nfs_fattr_init(fattr); return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0); } @@ -2847,9 +2932,10 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr, return PTR_ERR(label); status = nfs4_do_setattr(inode, cred, fattr, sattr, state, NULL, label); - if (status == 0) + if (status == 0) { nfs_setattr_update_inode(inode, sattr); - + nfs_setsecurity(inode, fattr, label); + } nfs4_label_free(label); return status; } @@ -2868,6 +2954,7 @@ static int _nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir, struct nfs4_lookup_res res = { .server = server, .fattr = fattr, + .label = label, .fh = fhandle, }; struct rpc_message msg = { @@ -2876,6 +2963,8 @@ static int _nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir, .rpc_resp = &res, }; + args.bitmask = nfs4_bitmask(server, label); + nfs_fattr_init(fattr); dprintk("NFS call lookup %s\n", name->name); @@ -2980,7 +3069,7 @@ static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry .rpc_cred = entry->cred, }; int mode = entry->mask; - int status; + int status = 0; /* * Determine which access bits we want to ask for... @@ -3085,7 +3174,7 @@ static int nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, int flags) { - struct nfs4_label *ilabel = NULL; + struct nfs4_label l, *ilabel = NULL; struct nfs_open_context *ctx; struct nfs4_state *state; int status = 0; @@ -3094,6 +3183,8 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, if (IS_ERR(ctx)) return PTR_ERR(ctx); + ilabel = nfs4_label_init_security(dir, dentry, sattr, &l); + sattr->ia_mode &= ~current_umask(); state = nfs4_do_open(dir, dentry, ctx->mode, flags, sattr, ilabel, ctx->cred, @@ -3107,6 +3198,7 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); ctx->state = state; out: + nfs4_label_release_security(ilabel); put_nfs_open_context(ctx); return status; } @@ -3155,6 +3247,8 @@ static void nfs4_proc_unlink_setup(struct rpc_message *msg, struct inode *dir) res->server = server; msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVE]; nfs41_init_sequence(&args->seq_args, &res->seq_res, 1); + + nfs_fattr_init(res->dir_attr); } static void nfs4_proc_unlink_rpc_prepare(struct rpc_task *task, struct nfs_unlinkdata *data) @@ -3282,11 +3376,14 @@ static int _nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr * status = PTR_ERR(res.label); goto out; } + arg.bitmask = nfs4_bitmask(server, res.label); status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1); if (!status) { update_changeattr(dir, &res.cinfo); - nfs_post_op_update_inode(inode, res.fattr); + status = nfs_post_op_update_inode(inode, res.fattr); + if (!status) + nfs_setsecurity(inode, res.fattr, res.label); } @@ -3339,7 +3436,7 @@ static struct nfs4_createdata *nfs4_alloc_createdata(struct inode *dir, data->arg.name = name; data->arg.attrs = sattr; data->arg.ftype = ftype; - data->arg.bitmask = server->attr_bitmask; + data->arg.bitmask = nfs4_bitmask(server, data->label); data->res.server = server; data->res.fh = &data->fh; data->res.fattr = &data->fattr; @@ -3400,14 +3497,19 @@ static int nfs4_proc_symlink(struct inode *dir, struct dentry *dentry, struct page *page, unsigned int len, struct iattr *sattr) { struct nfs4_exception exception = { }; - struct nfs4_label *label = NULL; + struct nfs4_label l, *label = NULL; int err; + + label = nfs4_label_init_security(dir, dentry, sattr, &l); + do { err = nfs4_handle_exception(NFS_SERVER(dir), _nfs4_proc_symlink(dir, dentry, page, len, sattr, label), &exception); } while (exception.retry); + + nfs4_label_release_security(label); return err; } @@ -3433,15 +3535,19 @@ static int nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry, struct iattr *sattr) { struct nfs4_exception exception = { }; - struct nfs4_label *label = NULL; + struct nfs4_label l, *label = NULL; int err; + label = nfs4_label_init_security(dir, dentry, sattr, &l); + sattr->ia_mode &= ~current_umask(); do { err = nfs4_handle_exception(NFS_SERVER(dir), _nfs4_proc_mkdir(dir, dentry, sattr, label), &exception); } while (exception.retry); + nfs4_label_release_security(label); + return err; } @@ -3499,7 +3605,7 @@ static int nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, } static int _nfs4_proc_mknod(struct inode *dir, struct dentry *dentry, - struct iattr *sattr, dev_t rdev) + struct iattr *sattr, struct nfs4_label *label, dev_t rdev) { struct nfs4_createdata *data; int mode = sattr->ia_mode; @@ -3525,6 +3631,7 @@ static int _nfs4_proc_mknod(struct inode *dir, struct dentry *dentry, goto out_free; } + data->arg.label = label; status = nfs4_do_create(dir, dentry, data); out_free: nfs4_free_createdata(data); @@ -3536,14 +3643,20 @@ static int nfs4_proc_mknod(struct inode *dir, struct dentry *dentry, struct iattr *sattr, dev_t rdev) { struct nfs4_exception exception = { }; + struct nfs4_label l, *label = NULL; int err; + label = nfs4_label_init_security(dir, dentry, sattr, &l); + sattr->ia_mode &= ~current_umask(); do { err = nfs4_handle_exception(NFS_SERVER(dir), - _nfs4_proc_mknod(dir, dentry, sattr, rdev), + _nfs4_proc_mknod(dir, dentry, sattr, label, rdev), &exception); } while (exception.retry); + + nfs4_label_release_security(label); + return err; } @@ -4270,6 +4383,155 @@ static int nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen return err; } +#ifdef CONFIG_NFS_V4_SECURITY_LABEL +static int _nfs4_get_security_label(struct inode *inode, void *buf, + size_t buflen) +{ + struct nfs_server *server = NFS_SERVER(inode); + struct nfs_fattr fattr; + struct nfs4_label label = {0, 0, buflen, buf}; + + u32 bitmask[3] = { 0, 0, FATTR4_WORD2_SECURITY_LABEL }; + struct nfs4_getattr_arg args = { + .fh = NFS_FH(inode), + .bitmask = bitmask, + }; + struct nfs4_getattr_res res = { + .fattr = &fattr, + .label = &label, + .server = server, + }; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETATTR], + .rpc_argp = &args, + .rpc_resp = &res, + }; + int ret; + + nfs_fattr_init(&fattr); + + ret = rpc_call_sync(server->client, &msg, 0); + if (ret) + return ret; + if (!(fattr.valid & NFS_ATTR_FATTR_V4_SECURITY_LABEL)) + return -ENOENT; + if (buflen < label.len) + return -ERANGE; + return 0; +} + +static int nfs4_get_security_label(struct inode *inode, void *buf, + size_t buflen) +{ + struct nfs4_exception exception = { }; + int err; + + if (!nfs_server_capable(inode, NFS_CAP_SECURITY_LABEL)) + return -EOPNOTSUPP; + + do { + err = nfs4_handle_exception(NFS_SERVER(inode), + _nfs4_get_security_label(inode, buf, buflen), + &exception); + } while (exception.retry); + return err; +} + +static int _nfs4_do_set_security_label(struct inode *inode, + struct nfs4_label *ilabel, + struct nfs_fattr *fattr, + struct nfs4_label *olabel) +{ + + struct iattr sattr = {0}; + struct nfs_server *server = NFS_SERVER(inode); + const u32 bitmask[3] = { 0, 0, FATTR4_WORD2_SECURITY_LABEL }; + struct nfs_setattrargs args = { + .fh = NFS_FH(inode), + .iap = &sattr, + .server = server, + .bitmask = bitmask, + .label = ilabel, + }; + struct nfs_setattrres res = { + .fattr = fattr, + .label = olabel, + .server = server, + }; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETATTR], + .rpc_argp = &args, + .rpc_resp = &res, + }; + int status; + + nfs4_stateid_copy(&args.stateid, &zero_stateid); + + status = rpc_call_sync(server->client, &msg, 0); + if (status) + dprintk("%s failed: %d\n", __func__, status); + + return status; +} + +static int nfs4_do_set_security_label(struct inode *inode, + struct nfs4_label *ilabel, + struct nfs_fattr *fattr, + struct nfs4_label *olabel) +{ + struct nfs4_exception exception = { }; + int err; + + do { + err = nfs4_handle_exception(NFS_SERVER(inode), + _nfs4_do_set_security_label(inode, ilabel, + fattr, olabel), + &exception); + } while (exception.retry); + return err; +} + +static int +nfs4_set_security_label(struct dentry *dentry, const void *buf, size_t buflen) +{ + struct nfs4_label ilabel, *olabel = NULL; + struct nfs_fattr fattr; + struct rpc_cred *cred; + struct inode *inode = dentry->d_inode; + int status; + + if (!nfs_server_capable(inode, NFS_CAP_SECURITY_LABEL)) + return -EOPNOTSUPP; + + nfs_fattr_init(&fattr); + + ilabel.pi = 0; + ilabel.lfs = 0; + ilabel.label = (char *)buf; + ilabel.len = buflen; + + cred = rpc_lookup_cred(); + if (IS_ERR(cred)) + return PTR_ERR(cred); + + olabel = nfs4_label_alloc(NFS_SERVER(inode), GFP_KERNEL); + if (IS_ERR(olabel)) { + status = -PTR_ERR(olabel); + goto out; + } + + status = nfs4_do_set_security_label(inode, &ilabel, &fattr, olabel); + if (status == 0) + nfs_setsecurity(inode, &fattr, olabel); + + nfs4_label_free(olabel); +out: + put_rpccred(cred); + return status; +} +#endif /* CONFIG_NFS_V4_SECURITY_LABEL */ + + static int nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server, struct nfs4_state *state) { diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 727cfe080eae..2a3f77e14db6 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -102,12 +102,23 @@ static int nfs4_stat_to_errno(int); #define nfs4_path_maxsz (1 + ((3 + NFS4_MAXPATHLEN) >> 2)) #define nfs4_owner_maxsz (1 + XDR_QUADLEN(IDMAP_NAMESZ)) #define nfs4_group_maxsz (1 + XDR_QUADLEN(IDMAP_NAMESZ)) +#ifdef CONFIG_NFS_V4_SECURITY_LABEL +/* PI(4 bytes) + LFS(4 bytes) + 1(for null terminator?) + MAXLABELLEN */ +#define nfs4_label_maxsz (4 + 4 + 1 + XDR_QUADLEN(NFS4_MAXLABELLEN)) +#define encode_readdir_space 24 +#define encode_readdir_bitmask_sz 3 +#else +#define nfs4_label_maxsz 0 +#define encode_readdir_space 20 +#define encode_readdir_bitmask_sz 2 +#endif /* We support only one layout type per file system */ #define decode_mdsthreshold_maxsz (1 + 1 + nfs4_fattr_bitmap_maxsz + 1 + 8) /* This is based on getfattr, which uses the most attributes: */ #define nfs4_fattr_value_maxsz (1 + (1 + 2 + 2 + 4 + 2 + 1 + 1 + 2 + 2 + \ 3 + 3 + 3 + nfs4_owner_maxsz + \ - nfs4_group_maxsz + decode_mdsthreshold_maxsz)) + nfs4_group_maxsz + nfs4_label_maxsz + \ + decode_mdsthreshold_maxsz)) #define nfs4_fattr_maxsz (nfs4_fattr_bitmap_maxsz + \ nfs4_fattr_value_maxsz) #define decode_getattr_maxsz (op_decode_hdr_maxsz + nfs4_fattr_maxsz) @@ -115,6 +126,7 @@ static int nfs4_stat_to_errno(int); 1 + 2 + 1 + \ nfs4_owner_maxsz + \ nfs4_group_maxsz + \ + nfs4_label_maxsz + \ 4 + 4) #define encode_savefh_maxsz (op_encode_hdr_maxsz) #define decode_savefh_maxsz (op_decode_hdr_maxsz) @@ -192,9 +204,11 @@ static int nfs4_stat_to_errno(int); encode_stateid_maxsz + 3) #define decode_read_maxsz (op_decode_hdr_maxsz + 2) #define encode_readdir_maxsz (op_encode_hdr_maxsz + \ - 2 + encode_verifier_maxsz + 5) + 2 + encode_verifier_maxsz + 5 + \ + nfs4_label_maxsz) #define decode_readdir_maxsz (op_decode_hdr_maxsz + \ - decode_verifier_maxsz) + decode_verifier_maxsz + \ + nfs4_label_maxsz + nfs4_fattr_maxsz) #define encode_readlink_maxsz (op_encode_hdr_maxsz) #define decode_readlink_maxsz (op_decode_hdr_maxsz + 1) #define encode_write_maxsz (op_encode_hdr_maxsz + \ @@ -968,7 +982,9 @@ static void encode_nfs4_verifier(struct xdr_stream *xdr, const nfs4_verifier *ve encode_opaque_fixed(xdr, verf->data, NFS4_VERIFIER_SIZE); } -static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const struct nfs_server *server) +static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, + const struct nfs4_label *label, + const struct nfs_server *server) { char owner_name[IDMAP_NAMESZ]; char owner_group[IDMAP_NAMESZ]; @@ -1018,6 +1034,8 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const } len += 4 + (XDR_QUADLEN(owner_grouplen) << 2); } + if (label) + len += 4 + 4 + 4 + (XDR_QUADLEN(label->len) << 2); if (iap->ia_valid & ATTR_ATIME_SET) len += 16; else if (iap->ia_valid & ATTR_ATIME) @@ -1072,6 +1090,13 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const bmval1 |= FATTR4_WORD1_TIME_MODIFY_SET; *p++ = cpu_to_be32(NFS4_SET_TO_SERVER_TIME); } + if (label) { + bmval2 |= FATTR4_WORD2_SECURITY_LABEL; + *p++ = cpu_to_be32(label->lfs); + *p++ = cpu_to_be32(label->pi); + *p++ = cpu_to_be32(label->len); + p = xdr_encode_opaque_fixed(p, label->label, label->len); + } /* * Now we backfill the bitmap and the attribute buffer length. @@ -1138,7 +1163,7 @@ static void encode_create(struct xdr_stream *xdr, const struct nfs4_create_arg * } encode_string(xdr, create->name->len, create->name->name); - encode_attrs(xdr, create->attrs, create->server); + encode_attrs(xdr, create->attrs, create->label, create->server); } static void encode_getattr_one(struct xdr_stream *xdr, uint32_t bitmap, struct compound_hdr *hdr) @@ -1371,11 +1396,11 @@ static inline void encode_createmode(struct xdr_stream *xdr, const struct nfs_op switch(arg->createmode) { case NFS4_CREATE_UNCHECKED: *p = cpu_to_be32(NFS4_CREATE_UNCHECKED); - encode_attrs(xdr, arg->u.attrs, arg->server); + encode_attrs(xdr, arg->u.attrs, arg->label, arg->server); break; case NFS4_CREATE_GUARDED: *p = cpu_to_be32(NFS4_CREATE_GUARDED); - encode_attrs(xdr, arg->u.attrs, arg->server); + encode_attrs(xdr, arg->u.attrs, arg->label, arg->server); break; case NFS4_CREATE_EXCLUSIVE: *p = cpu_to_be32(NFS4_CREATE_EXCLUSIVE); @@ -1385,7 +1410,7 @@ static inline void encode_createmode(struct xdr_stream *xdr, const struct nfs_op *p = cpu_to_be32(NFS4_CREATE_EXCLUSIVE4_1); encode_nfs4_verifier(xdr, &arg->u.verifier); dummy.ia_valid = 0; - encode_attrs(xdr, &dummy, arg->server); + encode_attrs(xdr, &dummy, arg->label, arg->server); } } @@ -1536,7 +1561,7 @@ static void encode_read(struct xdr_stream *xdr, const struct nfs_readargs *args, static void encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg *readdir, struct rpc_rqst *req, struct compound_hdr *hdr) { - uint32_t attrs[2] = { + uint32_t attrs[3] = { FATTR4_WORD0_RDATTR_ERROR, FATTR4_WORD1_MOUNTED_ON_FILEID, }; @@ -1559,20 +1584,26 @@ static void encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg encode_op_hdr(xdr, OP_READDIR, decode_readdir_maxsz, hdr); encode_uint64(xdr, readdir->cookie); encode_nfs4_verifier(xdr, &readdir->verifier); - p = reserve_space(xdr, 20); + p = reserve_space(xdr, encode_readdir_space); *p++ = cpu_to_be32(dircount); *p++ = cpu_to_be32(readdir->count); - *p++ = cpu_to_be32(2); - + *p++ = cpu_to_be32(encode_readdir_bitmask_sz); *p++ = cpu_to_be32(attrs[0] & readdir->bitmask[0]); - *p = cpu_to_be32(attrs[1] & readdir->bitmask[1]); + *p = cpu_to_be32(attrs[1] & readdir->bitmask[1]); + if (encode_readdir_bitmask_sz > 2) { + if (hdr->minorversion > 1) + attrs[2] |= FATTR4_WORD2_SECURITY_LABEL; + p++, *p++ = cpu_to_be32(attrs[2] & readdir->bitmask[2]); + } memcpy(verf, readdir->verifier.data, sizeof(verf)); - dprintk("%s: cookie = %Lu, verifier = %08x:%08x, bitmap = %08x:%08x\n", + + dprintk("%s: cookie = %llu, verifier = %08x:%08x, bitmap = %08x:%08x:%08x\n", __func__, (unsigned long long)readdir->cookie, verf[0], verf[1], attrs[0] & readdir->bitmask[0], - attrs[1] & readdir->bitmask[1]); + attrs[1] & readdir->bitmask[1], + attrs[2] & readdir->bitmask[2]); } static void encode_readlink(struct xdr_stream *xdr, const struct nfs4_readlink *readlink, struct rpc_rqst *req, struct compound_hdr *hdr) @@ -1631,7 +1662,7 @@ static void encode_setattr(struct xdr_stream *xdr, const struct nfs_setattrargs { encode_op_hdr(xdr, OP_SETATTR, decode_setattr_maxsz, hdr); encode_nfs4_stateid(xdr, &arg->stateid); - encode_attrs(xdr, arg->iap, server); + encode_attrs(xdr, arg->iap, arg->label, server); } static void encode_setclientid(struct xdr_stream *xdr, const struct nfs4_setclientid *setclientid, struct compound_hdr *hdr) @@ -4042,6 +4073,56 @@ static int decode_attr_time_delta(struct xdr_stream *xdr, uint32_t *bitmap, return status; } +static int decode_attr_security_label(struct xdr_stream *xdr, uint32_t *bitmap, + struct nfs4_label *label) +{ + uint32_t pi = 0; + uint32_t lfs = 0; + __u32 len; + __be32 *p; + int status = 0; + + if (unlikely(bitmap[2] & (FATTR4_WORD2_SECURITY_LABEL - 1U))) + return -EIO; + if (likely(bitmap[2] & FATTR4_WORD2_SECURITY_LABEL)) { + p = xdr_inline_decode(xdr, 4); + if (unlikely(!p)) + goto out_overflow; + lfs = be32_to_cpup(p++); + p = xdr_inline_decode(xdr, 4); + if (unlikely(!p)) + goto out_overflow; + pi = be32_to_cpup(p++); + p = xdr_inline_decode(xdr, 4); + if (unlikely(!p)) + goto out_overflow; + len = be32_to_cpup(p++); + p = xdr_inline_decode(xdr, len); + if (unlikely(!p)) + goto out_overflow; + if (len < NFS4_MAXLABELLEN) { + if (label) { + memcpy(label->label, p, len); + label->len = len; + label->pi = pi; + label->lfs = lfs; + status = NFS_ATTR_FATTR_V4_SECURITY_LABEL; + } + bitmap[2] &= ~FATTR4_WORD2_SECURITY_LABEL; + } else + printk(KERN_WARNING "%s: label too long (%u)!\n", + __func__, len); + } + if (label && label->label) + dprintk("%s: label=%s, len=%d, PI=%d, LFS=%d\n", __func__, + (char *)label->label, label->len, label->pi, label->lfs); + return status; + +out_overflow: + print_overflow_msg(__func__, xdr); + return -EIO; +} + static int decode_attr_time_modify(struct xdr_stream *xdr, uint32_t *bitmap, struct timespec *time) { int status = 0; @@ -4384,7 +4465,7 @@ out_overflow: static int decode_getfattr_attrs(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs_fattr *fattr, struct nfs_fh *fh, - struct nfs4_fs_locations *fs_loc, + struct nfs4_fs_locations *fs_loc, struct nfs4_label *label, const struct nfs_server *server) { int status; @@ -4492,6 +4573,13 @@ static int decode_getfattr_attrs(struct xdr_stream *xdr, uint32_t *bitmap, if (status < 0) goto xdr_error; + if (label) { + status = decode_attr_security_label(xdr, bitmap, label); + if (status < 0) + goto xdr_error; + fattr->valid |= status; + } + xdr_error: dprintk("%s: xdr returned %d\n", __func__, -status); return status; @@ -4499,7 +4587,7 @@ xdr_error: static int decode_getfattr_generic(struct xdr_stream *xdr, struct nfs_fattr *fattr, struct nfs_fh *fh, struct nfs4_fs_locations *fs_loc, - const struct nfs_server *server) + struct nfs4_label *label, const struct nfs_server *server) { unsigned int savep; uint32_t attrlen, @@ -4518,7 +4606,8 @@ static int decode_getfattr_generic(struct xdr_stream *xdr, struct nfs_fattr *fat if (status < 0) goto xdr_error; - status = decode_getfattr_attrs(xdr, bitmap, fattr, fh, fs_loc, server); + status = decode_getfattr_attrs(xdr, bitmap, fattr, fh, fs_loc, + label, server); if (status < 0) goto xdr_error; @@ -4528,10 +4617,16 @@ xdr_error: return status; } +static int decode_getfattr_label(struct xdr_stream *xdr, struct nfs_fattr *fattr, + struct nfs4_label *label, const struct nfs_server *server) +{ + return decode_getfattr_generic(xdr, fattr, NULL, NULL, label, server); +} + static int decode_getfattr(struct xdr_stream *xdr, struct nfs_fattr *fattr, const struct nfs_server *server) { - return decode_getfattr_generic(xdr, fattr, NULL, NULL, server); + return decode_getfattr_generic(xdr, fattr, NULL, NULL, NULL, server); } /* @@ -5923,7 +6018,7 @@ static int nfs4_xdr_dec_lookup(struct rpc_rqst *rqstp, struct xdr_stream *xdr, status = decode_getfh(xdr, res->fh); if (status) goto out; - status = decode_getfattr(xdr, res->fattr, res->server); + status = decode_getfattr_label(xdr, res->fattr, res->label, res->server); out: return status; } @@ -5949,7 +6044,8 @@ static int nfs4_xdr_dec_lookup_root(struct rpc_rqst *rqstp, goto out; status = decode_getfh(xdr, res->fh); if (status == 0) - status = decode_getfattr(xdr, res->fattr, res->server); + status = decode_getfattr_label(xdr, res->fattr, + res->label, res->server); out: return status; } @@ -6040,7 +6136,7 @@ static int nfs4_xdr_dec_link(struct rpc_rqst *rqstp, struct xdr_stream *xdr, status = decode_restorefh(xdr); if (status) goto out; - decode_getfattr(xdr, res->fattr, res->server); + decode_getfattr_label(xdr, res->fattr, res->label, res->server); out: return status; } @@ -6069,7 +6165,7 @@ static int nfs4_xdr_dec_create(struct rpc_rqst *rqstp, struct xdr_stream *xdr, status = decode_getfh(xdr, res->fh); if (status) goto out; - decode_getfattr(xdr, res->fattr, res->server); + decode_getfattr_label(xdr, res->fattr, res->label, res->server); out: return status; } @@ -6101,7 +6197,7 @@ static int nfs4_xdr_dec_getattr(struct rpc_rqst *rqstp, struct xdr_stream *xdr, status = decode_putfh(xdr); if (status) goto out; - status = decode_getfattr(xdr, res->fattr, res->server); + status = decode_getfattr_label(xdr, res->fattr, res->label, res->server); out: return status; } @@ -6234,7 +6330,7 @@ static int nfs4_xdr_dec_open(struct rpc_rqst *rqstp, struct xdr_stream *xdr, goto out; if (res->access_request) decode_access(xdr, &res->access_supported, &res->access_result); - decode_getfattr(xdr, res->f_attr, res->server); + decode_getfattr_label(xdr, res->f_attr, res->f_label, res->server); out: return status; } @@ -6311,7 +6407,7 @@ static int nfs4_xdr_dec_setattr(struct rpc_rqst *rqstp, status = decode_setattr(xdr); if (status) goto out; - decode_getfattr(xdr, res->fattr, res->server); + decode_getfattr_label(xdr, res->fattr, res->label, res->server); out: return status; } @@ -6700,7 +6796,7 @@ static int nfs4_xdr_dec_fs_locations(struct rpc_rqst *req, xdr_enter_page(xdr, PAGE_SIZE); status = decode_getfattr_generic(xdr, &res->fs_locations->fattr, NULL, res->fs_locations, - res->fs_locations->server); + NULL, res->fs_locations->server); out: return status; } @@ -7113,7 +7209,7 @@ int nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, goto out_overflow; if (decode_getfattr_attrs(xdr, bitmap, entry->fattr, entry->fh, - NULL, entry->server) < 0) + NULL, entry->label, entry->server) < 0) goto out_overflow; if (entry->fattr->valid & NFS_ATTR_FATTR_MOUNTED_ON_FILEID) entry->ino = entry->fattr->mounted_on_fileid; diff --git a/fs/nfs/super.c b/fs/nfs/super.c index b30c003b47d1..76e1ee5d03ed 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -2417,8 +2417,21 @@ static int nfs_bdi_register(struct nfs_server *server) int nfs_set_sb_security(struct super_block *s, struct dentry *mntroot, struct nfs_mount_info *mount_info) { - return security_sb_set_mnt_opts(s, &mount_info->parsed->lsm_opts, - 0, NULL); + int error; + unsigned long kflags = 0, kflags_out = 0; + if (NFS_SB(s)->caps & NFS_CAP_SECURITY_LABEL) + kflags |= SECURITY_LSM_NATIVE_LABELS; + + error = security_sb_set_mnt_opts(s, &mount_info->parsed->lsm_opts, + kflags, &kflags_out); + if (error) + goto err; + + if (NFS_SB(s)->caps & NFS_CAP_SECURITY_LABEL && + !(kflags_out & SECURITY_LSM_NATIVE_LABELS)) + NFS_SB(s)->caps &= ~NFS_CAP_SECURITY_LABEL; +err: + return error; } EXPORT_SYMBOL_GPL(nfs_set_sb_security); diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 3489015950b4..d4b003d9e78d 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -207,6 +207,7 @@ struct nfs_inode { #define NFS_INO_INVALID_ACL 0x0010 /* cached acls are invalid */ #define NFS_INO_REVAL_PAGECACHE 0x0020 /* must revalidate pagecache */ #define NFS_INO_REVAL_FORCED 0x0040 /* force revalidation ignoring a delegation */ +#define NFS_INO_INVALID_LABEL 0x0080 /* cached label is invalid */ /* * Bit offsets in flags field @@ -352,6 +353,8 @@ extern int __nfs_revalidate_inode(struct nfs_server *, struct inode *); extern int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping); extern int nfs_setattr(struct dentry *, struct iattr *); extern void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr); +extern void nfs_setsecurity(struct inode *inode, struct nfs_fattr *fattr, + struct nfs4_label *label); extern struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx); extern void put_nfs_open_context(struct nfs_open_context *ctx); extern struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_cred *cred, fmode_t mode); diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index d4348ab1e0e4..d2212432c456 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -146,6 +146,11 @@ struct nfs_server { u32 attr_bitmask[3];/* V4 bitmask representing the set of attributes supported on this filesystem */ + u32 attr_bitmask_nl[3]; + /* V4 bitmask representing the + set of attributes supported + on this filesystem excluding + the label support bit. */ u32 cache_consistency_bitmask[3]; /* V4 bitmask representing the subset of change attribute, size, ctime diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 6149633ff715..9f8e9b2e717a 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2908,7 +2908,10 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name, return; } + isec->sclass = inode_mode_to_security_class(inode->i_mode); isec->sid = newsid; + isec->initialized = 1; + return; } @@ -2996,6 +2999,7 @@ static int selinux_inode_setsecurity(struct inode *inode, const char *name, if (rc) return rc; + isec->sclass = inode_mode_to_security_class(inode->i_mode); isec->sid = newsid; isec->initialized = 1; return 0; -- cgit v1.2.3 From c9bccef6b98ae563f725f6e315d8adc685239781 Mon Sep 17 00:00:00 2001 From: David Quigley Date: Wed, 22 May 2013 12:50:45 -0400 Subject: NFS: Extend NFS xattr handlers to accept the security namespace The existing NFSv4 xattr handlers do not accept xattr calls to the security namespace. This patch extends these handlers to accept xattrs from the security namespace in addition to the default NFSv4 ACL namespace. Acked-by: James Morris Signed-off-by: Matthew N. Dodd Signed-off-by: Miguel Rodel Felipe Signed-off-by: Phua Eu Gene Signed-off-by: Khin Mi Mi Aung Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ security/security.c | 1 + 2 files changed, 51 insertions(+) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 1dc7aec8e946..dfef23f1a66a 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -5640,6 +5640,53 @@ static size_t nfs4_xattr_list_nfs4_acl(struct dentry *dentry, char *list, return len; } +#ifdef CONFIG_NFS_V4_SECURITY_LABEL +static inline int nfs4_server_supports_labels(struct nfs_server *server) +{ + return server->caps & NFS_CAP_SECURITY_LABEL; +} + +static int nfs4_xattr_set_nfs4_label(struct dentry *dentry, const char *key, + const void *buf, size_t buflen, + int flags, int type) +{ + if (security_ismaclabel(key)) + return nfs4_set_security_label(dentry, buf, buflen); + + return -EOPNOTSUPP; +} + +static int nfs4_xattr_get_nfs4_label(struct dentry *dentry, const char *key, + void *buf, size_t buflen, int type) +{ + if (security_ismaclabel(key)) + return nfs4_get_security_label(dentry->d_inode, buf, buflen); + return -EOPNOTSUPP; +} + +static size_t nfs4_xattr_list_nfs4_label(struct dentry *dentry, char *list, + size_t list_len, const char *name, + size_t name_len, int type) +{ + size_t len = 0; + + if (nfs_server_capable(dentry->d_inode, NFS_CAP_SECURITY_LABEL)) { + len = security_inode_listsecurity(dentry->d_inode, NULL, 0); + if (list && len <= list_len) + security_inode_listsecurity(dentry->d_inode, list, len); + } + return len; +} + +static const struct xattr_handler nfs4_xattr_nfs4_label_handler = { + .prefix = XATTR_SECURITY_PREFIX, + .list = nfs4_xattr_list_nfs4_label, + .get = nfs4_xattr_get_nfs4_label, + .set = nfs4_xattr_set_nfs4_label, +}; +#endif + + /* * nfs_fhget will use either the mounted_on_fileid or the fileid */ @@ -7468,6 +7515,9 @@ static const struct xattr_handler nfs4_xattr_nfs4_acl_handler = { const struct xattr_handler *nfs4_xattr_handlers[] = { &nfs4_xattr_nfs4_acl_handler, +#ifdef CONFIG_NFS_V4_SECURITY_LABEL + &nfs4_xattr_nfs4_label_handler, +#endif NULL }; diff --git a/security/security.c b/security/security.c index 8d0b9a79611a..94b35aef6871 100644 --- a/security/security.c +++ b/security/security.c @@ -660,6 +660,7 @@ int security_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer return 0; return security_ops->inode_listsecurity(inode, buffer, buffer_size); } +EXPORT_SYMBOL(security_inode_listsecurity); void security_inode_getsecid(const struct inode *inode, u32 *secid) { -- cgit v1.2.3 From f58eda9bc2f0793da4c5c1098d55df2b31e0d682 Mon Sep 17 00:00:00 2001 From: Steve Dickson Date: Wed, 22 May 2013 12:50:46 -0400 Subject: Kconfig: Add Kconfig entry for Labeled NFS V4 client This patch adds the NFS_V4_SECURITY_LABEL entry which enables security label support for the NFSv4 client Signed-off-by: Steve Dickson [trond: Make this non-interactive] Signed-off-by: Trond Myklebust --- fs/nfs/Kconfig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/nfs/Kconfig b/fs/nfs/Kconfig index a048928340a7..b5e80b0af315 100644 --- a/fs/nfs/Kconfig +++ b/fs/nfs/Kconfig @@ -140,6 +140,11 @@ config NFS_V4_1_IMPLEMENTATION_ID_DOMAIN If the NFS client is unchanged from the upstream kernel, this option should be set to the default "kernel.org". +config NFS_V4_SECURITY_LABEL + bool + depends on NFS_V4_2 && SECURITY + default y + config ROOT_NFS bool "Root file system on NFS" depends on NFS_FS=y && IP_PNP -- cgit v1.2.3 From 459de2edb9105a5d091f8215650e12c0812d59f3 Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Wed, 5 Jun 2013 11:15:01 -0400 Subject: NFS: Make callbacks minor version generic I found a few places that hardcode the minor version number rather than making it dependent on the protocol the callback came in over. This patch makes it easier to add new minor versions in the future. Signed-off-by: Bryan Schumaker Signed-off-by: Trond Myklebust --- fs/nfs/callback.h | 1 + fs/nfs/callback_proc.c | 3 ++- fs/nfs/callback_xdr.c | 16 ++++++++-------- fs/nfs/internal.h | 2 +- fs/nfs/nfs4client.c | 6 +++--- 5 files changed, 15 insertions(+), 13 deletions(-) diff --git a/fs/nfs/callback.h b/fs/nfs/callback.h index efd54f0a4c46..41cf8934f4e7 100644 --- a/fs/nfs/callback.h +++ b/fs/nfs/callback.h @@ -39,6 +39,7 @@ struct cb_process_state { __be32 drc_status; struct nfs_client *clp; u32 slotid; + u32 minorversion; struct net *net; }; diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c index a13d26ede254..3d2af31e004c 100644 --- a/fs/nfs/callback_proc.c +++ b/fs/nfs/callback_proc.c @@ -406,7 +406,8 @@ __be32 nfs4_callback_sequence(struct cb_sequenceargs *args, int i; __be32 status = htonl(NFS4ERR_BADSESSION); - clp = nfs4_find_client_sessionid(cps->net, args->csa_addr, &args->csa_sessionid); + clp = nfs4_find_client_sessionid(cps->net, args->csa_addr, + &args->csa_sessionid, cps->minorversion); if (clp == NULL) goto out; diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c index e7ee62929811..77c0b881520f 100644 --- a/fs/nfs/callback_xdr.c +++ b/fs/nfs/callback_xdr.c @@ -166,8 +166,8 @@ static __be32 decode_compound_hdr_arg(struct xdr_stream *xdr, struct cb_compound if (unlikely(p == NULL)) return htonl(NFS4ERR_RESOURCE); hdr->minorversion = ntohl(*p++); - /* Check minor version is zero or one or two. */ - if (hdr->minorversion <= 2) { + /* Check for minor version support */ + if (hdr->minorversion <= NFS4_MAX_MINOR_VERSION) { hdr->cb_ident = ntohl(*p++); /* ignored by v4.1 and v4.2 */ } else { pr_warn_ratelimited("NFS: %s: NFSv4 server callback with " @@ -801,8 +801,7 @@ preprocess_nfs4_op(unsigned int op_nr, struct callback_op **op) return htonl(NFS_OK); } -static __be32 process_op(uint32_t minorversion, int nop, - struct svc_rqst *rqstp, +static __be32 process_op(int nop, struct svc_rqst *rqstp, struct xdr_stream *xdr_in, void *argp, struct xdr_stream *xdr_out, void *resp, struct cb_process_state *cps) @@ -819,9 +818,9 @@ static __be32 process_op(uint32_t minorversion, int nop, return status; dprintk("%s: minorversion=%d nop=%d op_nr=%u\n", - __func__, minorversion, nop, op_nr); + __func__, cps->minorversion, nop, op_nr); - status = minorversion ? preprocess_nfs41_op(nop, op_nr, &op) : + status = cps->minorversion ? preprocess_nfs41_op(nop, op_nr, &op) : preprocess_nfs4_op(op_nr, &op); if (status == htonl(NFS4ERR_OP_ILLEGAL)) op_nr = OP_CB_ILLEGAL; @@ -885,14 +884,15 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r return rpc_drop_reply; } + cps.minorversion = hdr_arg.minorversion; hdr_res.taglen = hdr_arg.taglen; hdr_res.tag = hdr_arg.tag; if (encode_compound_hdr_res(&xdr_out, &hdr_res) != 0) return rpc_system_err; while (status == 0 && nops != hdr_arg.nops) { - status = process_op(hdr_arg.minorversion, nops, rqstp, - &xdr_in, argp, &xdr_out, resp, &cps); + status = process_op(nops, rqstp, &xdr_in, + argp, &xdr_out, resp, &cps); nops++; } diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 91e59a39fc08..97ec2ef6a220 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -165,7 +165,7 @@ extern void nfs_free_client(struct nfs_client *); extern struct nfs_client *nfs4_find_client_ident(struct net *, int); extern struct nfs_client * nfs4_find_client_sessionid(struct net *, const struct sockaddr *, - struct nfs4_sessionid *); + struct nfs4_sessionid *, u32); extern struct nfs_server *nfs_create_server(struct nfs_mount_info *, struct nfs_subversion *); extern struct nfs_server *nfs4_create_server( diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c index 2a297eeda474..18191919cc0b 100644 --- a/fs/nfs/nfs4client.c +++ b/fs/nfs/nfs4client.c @@ -567,14 +567,14 @@ static bool nfs4_cb_match_client(const struct sockaddr *addr, */ struct nfs_client * nfs4_find_client_sessionid(struct net *net, const struct sockaddr *addr, - struct nfs4_sessionid *sid) + struct nfs4_sessionid *sid, u32 minorversion) { struct nfs_client *clp; struct nfs_net *nn = net_generic(net, nfs_net_id); spin_lock(&nn->nfs_client_lock); list_for_each_entry(clp, &nn->nfs_client_list, cl_share_link) { - if (nfs4_cb_match_client(addr, clp, 1) == false) + if (nfs4_cb_match_client(addr, clp, minorversion) == false) continue; if (!nfs4_has_session(clp)) @@ -597,7 +597,7 @@ nfs4_find_client_sessionid(struct net *net, const struct sockaddr *addr, struct nfs_client * nfs4_find_client_sessionid(struct net *net, const struct sockaddr *addr, - struct nfs4_sessionid *sid) + struct nfs4_sessionid *sid, u32 minorversion) { return NULL; } -- cgit v1.2.3 From 6b140b85d92bd65db44f0a7a065b2e39a91e9a9d Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Wed, 5 Jun 2013 11:15:02 -0400 Subject: NFS: Add in v4.2 callback operation NFS v4.2 adds a CB_OFFLOAD operation used by COPY and WRITE_PLUS. Since neither of these operations have been implemented yet, simply return NFS4ERR_NOTSUPP. Signed-off-by: Bryan Schumaker Signed-off-by: Trond Myklebust --- fs/nfs/callback.h | 2 ++ fs/nfs/callback_xdr.c | 36 ++++++++++++++++++++++++++++++++++-- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/fs/nfs/callback.h b/fs/nfs/callback.h index 41cf8934f4e7..84326e9fb47a 100644 --- a/fs/nfs/callback.h +++ b/fs/nfs/callback.h @@ -32,6 +32,8 @@ enum nfs4_callback_opnum { OP_CB_WANTS_CANCELLED = 12, OP_CB_NOTIFY_LOCK = 13, OP_CB_NOTIFY_DEVICEID = 14, +/* Callback operations new to NFSv4.2 */ + OP_CB_OFFLOAD = 15, OP_CB_ILLEGAL = 10044, }; diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c index 77c0b881520f..d450c21bc382 100644 --- a/fs/nfs/callback_xdr.c +++ b/fs/nfs/callback_xdr.c @@ -786,6 +786,26 @@ static void nfs4_cb_free_slot(struct cb_process_state *cps) } #endif /* CONFIG_NFS_V4_1 */ +#ifdef CONFIG_NFS_V4_2 +static __be32 +preprocess_nfs42_op(int nop, unsigned int op_nr, struct callback_op **op) +{ + __be32 status = preprocess_nfs41_op(nop, op_nr, op); + if (status != htonl(NFS4ERR_OP_ILLEGAL)) + return status; + + if (op_nr == OP_CB_OFFLOAD) + return htonl(NFS4ERR_NOTSUPP); + return htonl(NFS4ERR_OP_ILLEGAL); +} +#else /* CONFIG_NFS_V4_2 */ +static __be32 +preprocess_nfs42_op(int nop, unsigned int op_nr, struct callback_op **op) +{ + return htonl(NFS4ERR_MINOR_VERS_MISMATCH); +} +#endif /* CONFIG_NFS_V4_2 */ + static __be32 preprocess_nfs4_op(unsigned int op_nr, struct callback_op **op) { @@ -820,8 +840,20 @@ static __be32 process_op(int nop, struct svc_rqst *rqstp, dprintk("%s: minorversion=%d nop=%d op_nr=%u\n", __func__, cps->minorversion, nop, op_nr); - status = cps->minorversion ? preprocess_nfs41_op(nop, op_nr, &op) : - preprocess_nfs4_op(op_nr, &op); + switch (cps->minorversion) { + case 0: + status = preprocess_nfs4_op(op_nr, &op); + break; + case 1: + status = preprocess_nfs41_op(nop, op_nr, &op); + break; + case 2: + status = preprocess_nfs42_op(nop, op_nr, &op); + break; + default: + status = htonl(NFS4ERR_MINOR_VERS_MISMATCH); + } + if (status == htonl(NFS4ERR_OP_ILLEGAL)) op_nr = OP_CB_ILLEGAL; if (status) -- cgit v1.2.3 From e44193d39e8d4d1de5d996fcd37ed75e5c704f10 Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Sun, 9 Jun 2013 17:14:22 +0800 Subject: cpuset: let hotplug propagation work wait for task attaching Instead of triggering propagation work in cpuset_attach(), we make hotplug propagation work wait until there's no task attaching in progress. IMO this is more robust. We won't see empty masks in cpuset_attach(). Also it's a preparation for removing propagation work. Without asynchronous propagation we can't call move_tasks_in_empty_cpuset() in cpuset_attach(), because otherwise we'll deadlock on cgroup_mutex. tj: typo fixes. Signed-off-by: Li Zefan Signed-off-by: Tejun Heo --- kernel/cpuset.c | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/kernel/cpuset.c b/kernel/cpuset.c index 535dce685eec..e902473f76bf 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -59,6 +59,7 @@ #include #include #include +#include /* * Tracks how many cpusets are currently defined in system. @@ -275,6 +276,8 @@ static void schedule_cpuset_propagate_hotplug(struct cpuset *cs); static DECLARE_WORK(cpuset_hotplug_work, cpuset_hotplug_workfn); +static DECLARE_WAIT_QUEUE_HEAD(cpuset_attach_wq); + /* * This is ugly, but preserves the userspace API for existing cpuset * users. If someone tries to mount the "cpuset" filesystem, we @@ -1436,14 +1439,8 @@ static void cpuset_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) } cs->attach_in_progress--; - - /* - * We may have raced with CPU/memory hotunplug. Trigger hotplug - * propagation if @cs doesn't have any CPU or memory. It will move - * the newly added tasks to the nearest parent which can execute. - */ - if (cpumask_empty(cs->cpus_allowed) || nodes_empty(cs->mems_allowed)) - schedule_cpuset_propagate_hotplug(cs); + if (!cs->attach_in_progress) + wake_up(&cpuset_attach_wq); mutex_unlock(&cpuset_mutex); } @@ -1555,10 +1552,6 @@ static int cpuset_write_resmask(struct cgroup *cgrp, struct cftype *cft, * resources, wait for the previously scheduled operations before * proceeding, so that we don't end up keep removing tasks added * after execution capability is restored. - * - * Flushing cpuset_hotplug_work is enough to synchronize against - * hotplug hanlding; however, cpuset_attach() may schedule - * propagation work directly. Flush the workqueue too. */ flush_work(&cpuset_hotplug_work); flush_workqueue(cpuset_propagate_hotplug_wq); @@ -2005,8 +1998,20 @@ static void cpuset_propagate_hotplug_workfn(struct work_struct *work) struct cpuset *cs = container_of(work, struct cpuset, hotplug_work); bool is_empty; +retry: + wait_event(cpuset_attach_wq, cs->attach_in_progress == 0); + mutex_lock(&cpuset_mutex); + /* + * We have raced with task attaching. We wait until attaching + * is finished, so we won't attach a task to an empty cpuset. + */ + if (cs->attach_in_progress) { + mutex_unlock(&cpuset_mutex); + goto retry; + } + cpumask_andnot(&off_cpus, cs->cpus_allowed, top_cpuset.cpus_allowed); nodes_andnot(off_mems, cs->mems_allowed, top_cpuset.mems_allowed); -- cgit v1.2.3 From 388afd8549dc8be0920e00ae9404341593b6bd7c Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Sun, 9 Jun 2013 17:14:47 +0800 Subject: cpuset: remove async hotplug propagation work As we can drop rcu read lock while iterating cgroup hierarchy, we don't have to do propagation asynchronously via workqueue. Signed-off-by: Li Zefan Signed-off-by: Tejun Heo --- kernel/cpuset.c | 69 +++++++++++++-------------------------------------------- 1 file changed, 16 insertions(+), 53 deletions(-) diff --git a/kernel/cpuset.c b/kernel/cpuset.c index e902473f76bf..608fe1308b22 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -101,8 +101,6 @@ struct cpuset { /* for custom sched domain */ int relax_domain_level; - - struct work_struct hotplug_work; }; /* Retrieve the cpuset for a cgroup */ @@ -268,12 +266,7 @@ static DEFINE_MUTEX(callback_mutex); /* * CPU / memory hotplug is handled asynchronously. */ -static struct workqueue_struct *cpuset_propagate_hotplug_wq; - static void cpuset_hotplug_workfn(struct work_struct *work); -static void cpuset_propagate_hotplug_workfn(struct work_struct *work); -static void schedule_cpuset_propagate_hotplug(struct cpuset *cs); - static DECLARE_WORK(cpuset_hotplug_work, cpuset_hotplug_workfn); static DECLARE_WAIT_QUEUE_HEAD(cpuset_attach_wq); @@ -1554,7 +1547,6 @@ static int cpuset_write_resmask(struct cgroup *cgrp, struct cftype *cft, * after execution capability is restored. */ flush_work(&cpuset_hotplug_work); - flush_workqueue(cpuset_propagate_hotplug_wq); mutex_lock(&cpuset_mutex); if (!is_cpuset_online(cs)) @@ -1821,7 +1813,6 @@ static struct cgroup_subsys_state *cpuset_css_alloc(struct cgroup *cont) cpumask_clear(cs->cpus_allowed); nodes_clear(cs->mems_allowed); fmeter_init(&cs->fmeter); - INIT_WORK(&cs->hotplug_work, cpuset_propagate_hotplug_workfn); cs->relax_domain_level = -1; return &cs->css; @@ -1984,18 +1975,17 @@ static void remove_tasks_in_empty_cpuset(struct cpuset *cs) } /** - * cpuset_propagate_hotplug_workfn - propagate CPU/memory hotplug to a cpuset + * cpuset_hotplug_update_tasks - update tasks in a cpuset for hotunplug * @cs: cpuset in interest * * Compare @cs's cpu and mem masks against top_cpuset and if some have gone * offline, update @cs accordingly. If @cs ends up with no CPU or memory, * all its tasks are moved to the nearest ancestor with both resources. */ -static void cpuset_propagate_hotplug_workfn(struct work_struct *work) +static void cpuset_hotplug_update_tasks(struct cpuset *cs) { static cpumask_t off_cpus; static nodemask_t off_mems, tmp_mems; - struct cpuset *cs = container_of(work, struct cpuset, hotplug_work); bool is_empty; retry: @@ -2044,34 +2034,6 @@ retry: */ if (is_empty) remove_tasks_in_empty_cpuset(cs); - - /* the following may free @cs, should be the last operation */ - css_put(&cs->css); -} - -/** - * schedule_cpuset_propagate_hotplug - schedule hotplug propagation to a cpuset - * @cs: cpuset of interest - * - * Schedule cpuset_propagate_hotplug_workfn() which will update CPU and - * memory masks according to top_cpuset. - */ -static void schedule_cpuset_propagate_hotplug(struct cpuset *cs) -{ - /* - * Pin @cs. The refcnt will be released when the work item - * finishes executing. - */ - if (!css_tryget(&cs->css)) - return; - - /* - * Queue @cs->hotplug_work. If already pending, lose the css ref. - * cpuset_propagate_hotplug_wq is ordered and propagation will - * happen in the order this function is called. - */ - if (!queue_work(cpuset_propagate_hotplug_wq, &cs->hotplug_work)) - css_put(&cs->css); } /** @@ -2084,8 +2046,8 @@ static void schedule_cpuset_propagate_hotplug(struct cpuset *cs) * actively using CPU hotplug but making no active use of cpusets. * * Non-root cpusets are only affected by offlining. If any CPUs or memory - * nodes have been taken down, cpuset_propagate_hotplug() is invoked on all - * descendants. + * nodes have been taken down, cpuset_hotplug_update_tasks() is invoked on + * all descendants. * * Note that CPU offlining during suspend is ignored. We don't modify * cpusets across suspend/resume cycles at all. @@ -2128,21 +2090,26 @@ static void cpuset_hotplug_workfn(struct work_struct *work) update_tasks_nodemask(&top_cpuset, &tmp_mems, NULL); } + mutex_unlock(&cpuset_mutex); + /* if cpus or mems went down, we need to propagate to descendants */ if (cpus_offlined || mems_offlined) { struct cpuset *cs; struct cgroup *pos_cgrp; rcu_read_lock(); - cpuset_for_each_descendant_pre(cs, pos_cgrp, &top_cpuset) - schedule_cpuset_propagate_hotplug(cs); - rcu_read_unlock(); - } + cpuset_for_each_descendant_pre(cs, pos_cgrp, &top_cpuset) { + if (!css_tryget(&cs->css)) + continue; + rcu_read_unlock(); - mutex_unlock(&cpuset_mutex); + cpuset_hotplug_update_tasks(cs); - /* wait for propagations to finish */ - flush_workqueue(cpuset_propagate_hotplug_wq); + rcu_read_lock(); + css_put(&cs->css); + } + rcu_read_unlock(); + } /* rebuild sched domains if cpus_allowed has changed */ if (cpus_updated) @@ -2193,10 +2160,6 @@ void __init cpuset_init_smp(void) top_cpuset.mems_allowed = node_states[N_MEMORY]; register_hotmemory_notifier(&cpuset_track_online_nodes_nb); - - cpuset_propagate_hotplug_wq = - alloc_ordered_workqueue("cpuset_hotplug", 0); - BUG_ON(!cpuset_propagate_hotplug_wq); } /** -- cgit v1.2.3 From 94a87157cde95d38b9cdf1116e4f0fd93f6d25df Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sun, 9 Jun 2013 18:15:00 +0200 Subject: firewire: introduce fw_driver.probe and .remove methods FireWire upper layer drivers are converted from generic struct driver.probe() and .remove() to bus-specific struct fw_driver.probe() and .remove(). The new .probe() adds a const struct ieee1394_device_id *id argument, indicating the entry in the driver's device identifiers table which matched the fw_unit to be probed. This new argument is used by the snd-firewire-speakers driver to look up device-specific parameters and methods. There is at least one other FireWire audio driver currently in development in which this will be useful too. The new .remove() drops the unused error return code. Although all in-tree drivers are being converted to the new methods, support for the old methods is left in place in this commit. This allows public developer trees to merge this commit and then move to the new fw_driver methods. Signed-off-by: Stefan Richter Acked-by: Clemens Ladisch (for sound/firewire/) Cc: Peter Hurley (for drivers/staging/fwserial/) --- drivers/firewire/core-device.c | 45 ++++++++++++--- drivers/firewire/net.c | 50 ++++++++--------- drivers/firewire/sbp2.c | 17 +++--- drivers/media/firewire/firedtv-fw.c | 19 +++---- drivers/staging/fwserial/fwserial.c | 14 ++--- include/linux/firewire.h | 2 + sound/firewire/isight.c | 44 +++++++-------- sound/firewire/scs1x.c | 39 ++++++------- sound/firewire/speakers.c | 106 +++++++++++++++--------------------- 9 files changed, 166 insertions(+), 170 deletions(-) diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c index 664a6ff0a823..c152edd6cd3a 100644 --- a/drivers/firewire/core-device.c +++ b/drivers/firewire/core-device.c @@ -165,25 +165,50 @@ static bool match_ids(const struct ieee1394_device_id *id_table, int *id) return (match & id_table->match_flags) == id_table->match_flags; } -static bool is_fw_unit(struct device *dev); - -static int fw_unit_match(struct device *dev, struct device_driver *drv) +static const struct ieee1394_device_id *unit_match(struct device *dev, + struct device_driver *drv) { const struct ieee1394_device_id *id_table = container_of(drv, struct fw_driver, driver)->id_table; int id[] = {0, 0, 0, 0}; - /* We only allow binding to fw_units. */ - if (!is_fw_unit(dev)) - return 0; - get_modalias_ids(fw_unit(dev), id); for (; id_table->match_flags != 0; id_table++) if (match_ids(id_table, id)) - return 1; + return id_table; - return 0; + return NULL; +} + +static bool is_fw_unit(struct device *dev); + +static int fw_unit_match(struct device *dev, struct device_driver *drv) +{ + /* We only allow binding to fw_units. */ + return is_fw_unit(dev) && unit_match(dev, drv) != NULL; +} + +static int fw_unit_probe(struct device *dev) +{ + struct fw_driver *driver = + container_of(dev->driver, struct fw_driver, driver); + + if (driver->probe) + return driver->probe(fw_unit(dev), unit_match(dev, dev->driver)); + else + return driver->driver.probe(dev); +} + +static int fw_unit_remove(struct device *dev) +{ + struct fw_driver *driver = + container_of(dev->driver, struct fw_driver, driver); + + if (driver->remove) + return driver->remove(fw_unit(dev)), 0; + else + return driver->driver.remove(dev); } static int get_modalias(struct fw_unit *unit, char *buffer, size_t buffer_size) @@ -213,6 +238,8 @@ static int fw_unit_uevent(struct device *dev, struct kobj_uevent_env *env) struct bus_type fw_bus_type = { .name = "firewire", .match = fw_unit_match, + .probe = fw_unit_probe, + .remove = fw_unit_remove, }; EXPORT_SYMBOL(fw_bus_type); diff --git a/drivers/firewire/net.c b/drivers/firewire/net.c index 815b0fcbe918..6b895986dc22 100644 --- a/drivers/firewire/net.c +++ b/drivers/firewire/net.c @@ -1440,9 +1440,9 @@ static int fwnet_add_peer(struct fwnet_device *dev, return 0; } -static int fwnet_probe(struct device *_dev) +static int fwnet_probe(struct fw_unit *unit, + const struct ieee1394_device_id *id) { - struct fw_unit *unit = fw_unit(_dev); struct fw_device *device = fw_parent_device(unit); struct fw_card *card = device->card; struct net_device *net; @@ -1526,6 +1526,24 @@ static int fwnet_probe(struct device *_dev) return ret; } +/* + * FIXME abort partially sent fragmented datagrams, + * discard partially received fragmented datagrams + */ +static void fwnet_update(struct fw_unit *unit) +{ + struct fw_device *device = fw_parent_device(unit); + struct fwnet_peer *peer = dev_get_drvdata(&unit->device); + int generation; + + generation = device->generation; + + spin_lock_irq(&peer->dev->lock); + peer->node_id = device->node_id; + peer->generation = generation; + spin_unlock_irq(&peer->dev->lock); +} + static void fwnet_remove_peer(struct fwnet_peer *peer, struct fwnet_device *dev) { struct fwnet_partial_datagram *pd, *pd_next; @@ -1542,9 +1560,9 @@ static void fwnet_remove_peer(struct fwnet_peer *peer, struct fwnet_device *dev) kfree(peer); } -static int fwnet_remove(struct device *_dev) +static void fwnet_remove(struct fw_unit *unit) { - struct fwnet_peer *peer = dev_get_drvdata(_dev); + struct fwnet_peer *peer = dev_get_drvdata(&unit->device); struct fwnet_device *dev = peer->dev; struct net_device *net; int i; @@ -1569,26 +1587,6 @@ static int fwnet_remove(struct device *_dev) } mutex_unlock(&fwnet_device_mutex); - - return 0; -} - -/* - * FIXME abort partially sent fragmented datagrams, - * discard partially received fragmented datagrams - */ -static void fwnet_update(struct fw_unit *unit) -{ - struct fw_device *device = fw_parent_device(unit); - struct fwnet_peer *peer = dev_get_drvdata(&unit->device); - int generation; - - generation = device->generation; - - spin_lock_irq(&peer->dev->lock); - peer->node_id = device->node_id; - peer->generation = generation; - spin_unlock_irq(&peer->dev->lock); } static const struct ieee1394_device_id fwnet_id_table[] = { @@ -1614,10 +1612,10 @@ static struct fw_driver fwnet_driver = { .owner = THIS_MODULE, .name = KBUILD_MODNAME, .bus = &fw_bus_type, - .probe = fwnet_probe, - .remove = fwnet_remove, }, + .probe = fwnet_probe, .update = fwnet_update, + .remove = fwnet_remove, .id_table = fwnet_id_table, }; diff --git a/drivers/firewire/sbp2.c b/drivers/firewire/sbp2.c index 47674b913843..281029daf98c 100644 --- a/drivers/firewire/sbp2.c +++ b/drivers/firewire/sbp2.c @@ -1128,11 +1128,10 @@ static void sbp2_init_workarounds(struct sbp2_target *tgt, u32 model, } static struct scsi_host_template scsi_driver_template; -static int sbp2_remove(struct device *dev); +static void sbp2_remove(struct fw_unit *unit); -static int sbp2_probe(struct device *dev) +static int sbp2_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) { - struct fw_unit *unit = fw_unit(dev); struct fw_device *device = fw_parent_device(unit); struct sbp2_target *tgt; struct sbp2_logical_unit *lu; @@ -1196,7 +1195,7 @@ static int sbp2_probe(struct device *dev) return 0; fail_remove: - sbp2_remove(dev); + sbp2_remove(unit); return -ENOMEM; fail_shost_put: @@ -1222,9 +1221,8 @@ static void sbp2_update(struct fw_unit *unit) } } -static int sbp2_remove(struct device *dev) +static void sbp2_remove(struct fw_unit *unit) { - struct fw_unit *unit = fw_unit(dev); struct fw_device *device = fw_parent_device(unit); struct sbp2_target *tgt = dev_get_drvdata(&unit->device); struct sbp2_logical_unit *lu, *next; @@ -1261,10 +1259,9 @@ static int sbp2_remove(struct device *dev) kfree(lu); } scsi_remove_host(shost); - dev_notice(dev, "released target %d:0:0\n", shost->host_no); + dev_notice(&unit->device, "released target %d:0:0\n", shost->host_no); scsi_host_put(shost); - return 0; } #define SBP2_UNIT_SPEC_ID_ENTRY 0x0000609e @@ -1285,10 +1282,10 @@ static struct fw_driver sbp2_driver = { .owner = THIS_MODULE, .name = KBUILD_MODNAME, .bus = &fw_bus_type, - .probe = sbp2_probe, - .remove = sbp2_remove, }, + .probe = sbp2_probe, .update = sbp2_update, + .remove = sbp2_remove, .id_table = sbp2_id_table, }; diff --git a/drivers/media/firewire/firedtv-fw.c b/drivers/media/firewire/firedtv-fw.c index e24ec539a5fd..247f0e7cb5f7 100644 --- a/drivers/media/firewire/firedtv-fw.c +++ b/drivers/media/firewire/firedtv-fw.c @@ -248,7 +248,7 @@ static const char * const model_names[] = { /* Adjust the template string if models with longer names appear. */ #define MAX_MODEL_NAME_LEN sizeof("FireDTV ????") -static int node_probe(struct device *dev) +static int node_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) { struct firedtv *fdtv; char name[MAX_MODEL_NAME_LEN]; @@ -258,8 +258,8 @@ static int node_probe(struct device *dev) if (!fdtv) return -ENOMEM; - dev_set_drvdata(dev, fdtv); - fdtv->device = dev; + dev_set_drvdata(&unit->device, fdtv); + fdtv->device = &unit->device; fdtv->isochannel = -1; fdtv->voltage = 0xff; fdtv->tone = 0xff; @@ -269,7 +269,7 @@ static int node_probe(struct device *dev) mutex_init(&fdtv->demux_mutex); INIT_WORK(&fdtv->remote_ctrl_work, avc_remote_ctrl_work); - name_len = fw_csr_string(fw_unit(dev)->directory, CSR_MODEL, + name_len = fw_csr_string(unit->directory, CSR_MODEL, name, sizeof(name)); for (i = ARRAY_SIZE(model_names); --i; ) if (strlen(model_names[i]) <= name_len && @@ -277,7 +277,7 @@ static int node_probe(struct device *dev) break; fdtv->type = i; - err = fdtv_register_rc(fdtv, dev); + err = fdtv_register_rc(fdtv, &unit->device); if (err) goto fail_free; @@ -307,9 +307,9 @@ fail_free: return err; } -static int node_remove(struct device *dev) +static void node_remove(struct fw_unit *unit) { - struct firedtv *fdtv = dev_get_drvdata(dev); + struct firedtv *fdtv = dev_get_drvdata(&unit->device); fdtv_dvb_unregister(fdtv); @@ -320,7 +320,6 @@ static int node_remove(struct device *dev) fdtv_unregister_rc(fdtv); kfree(fdtv); - return 0; } static void node_update(struct fw_unit *unit) @@ -391,10 +390,10 @@ static struct fw_driver fdtv_driver = { .owner = THIS_MODULE, .name = "firedtv", .bus = &fw_bus_type, - .probe = node_probe, - .remove = node_remove, }, + .probe = node_probe, .update = node_update, + .remove = node_remove, .id_table = fdtv_id_table, }; diff --git a/drivers/staging/fwserial/fwserial.c b/drivers/staging/fwserial/fwserial.c index e5818a1c2262..a8399f9c9392 100644 --- a/drivers/staging/fwserial/fwserial.c +++ b/drivers/staging/fwserial/fwserial.c @@ -2438,9 +2438,9 @@ free_ports: * last peer for a given fw_card triggering the destruction of the same * fw_serial for the same fw_card. */ -static int fwserial_probe(struct device *dev) +static int fwserial_probe(struct fw_unit *unit, + const struct ieee1394_device_id *id) { - struct fw_unit *unit = fw_unit(dev); struct fw_serial *serial; int err; @@ -2462,9 +2462,9 @@ static int fwserial_probe(struct device *dev) * specific fw_card). If this is the last peer being removed, then trigger * the destruction of the underlying TTYs. */ -static int fwserial_remove(struct device *dev) +static void fwserial_remove(struct fw_unit *unit) { - struct fwtty_peer *peer = dev_get_drvdata(dev); + struct fwtty_peer *peer = dev_get_drvdata(&unit->device); struct fw_serial *serial = peer->serial; int i; @@ -2484,8 +2484,6 @@ static int fwserial_remove(struct device *dev) kref_put(&serial->kref, fwserial_destroy); } mutex_unlock(&fwserial_list_mutex); - - return 0; } /** @@ -2530,10 +2528,10 @@ static struct fw_driver fwserial_driver = { .owner = THIS_MODULE, .name = KBUILD_MODNAME, .bus = &fw_bus_type, - .probe = fwserial_probe, - .remove = fwserial_remove, }, + .probe = fwserial_probe, .update = fwserial_update, + .remove = fwserial_remove, .id_table = fwserial_id_table, }; diff --git a/include/linux/firewire.h b/include/linux/firewire.h index 191501afd7fb..3b0e820375ab 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -251,8 +251,10 @@ struct ieee1394_device_id; struct fw_driver { struct device_driver driver; + int (*probe)(struct fw_unit *unit, const struct ieee1394_device_id *id); /* Called when the parent device sits through a bus reset. */ void (*update)(struct fw_unit *unit); + void (*remove)(struct fw_unit *unit); const struct ieee1394_device_id *id_table; }; diff --git a/sound/firewire/isight.c b/sound/firewire/isight.c index d428ffede4f3..58a5afefdc69 100644 --- a/sound/firewire/isight.c +++ b/sound/firewire/isight.c @@ -626,9 +626,9 @@ static u64 get_unit_base(struct fw_unit *unit) return 0; } -static int isight_probe(struct device *unit_dev) +static int isight_probe(struct fw_unit *unit, + const struct ieee1394_device_id *id) { - struct fw_unit *unit = fw_unit(unit_dev); struct fw_device *fw_dev = fw_parent_device(unit); struct snd_card *card; struct isight *isight; @@ -637,7 +637,7 @@ static int isight_probe(struct device *unit_dev) err = snd_card_create(-1, NULL, THIS_MODULE, sizeof(*isight), &card); if (err < 0) return err; - snd_card_set_dev(card, unit_dev); + snd_card_set_dev(card, &unit->device); isight = card->private_data; isight->card = card; @@ -674,7 +674,7 @@ static int isight_probe(struct device *unit_dev) if (err < 0) goto error; - dev_set_drvdata(unit_dev, isight); + dev_set_drvdata(&unit->device, isight); return 0; @@ -686,23 +686,6 @@ error: return err; } -static int isight_remove(struct device *dev) -{ - struct isight *isight = dev_get_drvdata(dev); - - isight_pcm_abort(isight); - - snd_card_disconnect(isight->card); - - mutex_lock(&isight->mutex); - isight_stop_streaming(isight); - mutex_unlock(&isight->mutex); - - snd_card_free_when_closed(isight->card); - - return 0; -} - static void isight_bus_reset(struct fw_unit *unit) { struct isight *isight = dev_get_drvdata(&unit->device); @@ -716,6 +699,21 @@ static void isight_bus_reset(struct fw_unit *unit) } } +static void isight_remove(struct fw_unit *unit) +{ + struct isight *isight = dev_get_drvdata(&unit->device); + + isight_pcm_abort(isight); + + snd_card_disconnect(isight->card); + + mutex_lock(&isight->mutex); + isight_stop_streaming(isight); + mutex_unlock(&isight->mutex); + + snd_card_free_when_closed(isight->card); +} + static const struct ieee1394_device_id isight_id_table[] = { { .match_flags = IEEE1394_MATCH_SPECIFIER_ID | @@ -732,10 +730,10 @@ static struct fw_driver isight_driver = { .owner = THIS_MODULE, .name = KBUILD_MODNAME, .bus = &fw_bus_type, - .probe = isight_probe, - .remove = isight_remove, }, + .probe = isight_probe, .update = isight_bus_reset, + .remove = isight_remove, .id_table = isight_id_table, }; diff --git a/sound/firewire/scs1x.c b/sound/firewire/scs1x.c index 844a555c3b1e..1f5920d6eacc 100644 --- a/sound/firewire/scs1x.c +++ b/sound/firewire/scs1x.c @@ -384,9 +384,8 @@ static void scs_card_free(struct snd_card *card) kfree(scs->buffer); } -static int scs_probe(struct device *unit_dev) +static int scs_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) { - struct fw_unit *unit = fw_unit(unit_dev); struct fw_device *fw_dev = fw_parent_device(unit); struct snd_card *card; struct scs *scs; @@ -395,7 +394,7 @@ static int scs_probe(struct device *unit_dev) err = snd_card_create(-16, NULL, THIS_MODULE, sizeof(*scs), &card); if (err < 0) return err; - snd_card_set_dev(card, unit_dev); + snd_card_set_dev(card, &unit->device); scs = card->private_data; scs->card = card; @@ -440,7 +439,7 @@ static int scs_probe(struct device *unit_dev) if (err < 0) goto err_card; - dev_set_drvdata(unit_dev, scs); + dev_set_drvdata(&unit->device, scs); return 0; @@ -451,9 +450,20 @@ err_card: return err; } -static int scs_remove(struct device *dev) +static void scs_update(struct fw_unit *unit) { - struct scs *scs = dev_get_drvdata(dev); + struct scs *scs = dev_get_drvdata(&unit->device); + __be64 data; + + data = cpu_to_be64(((u64)HSS1394_TAG_CHANGE_ADDRESS << 56) | + scs->hss_handler.offset); + snd_fw_transaction(scs->unit, TCODE_WRITE_BLOCK_REQUEST, + HSS1394_ADDRESS, &data, 8); +} + +static void scs_remove(struct fw_unit *unit) +{ + struct scs *scs = dev_get_drvdata(&unit->device); snd_card_disconnect(scs->card); @@ -465,19 +475,6 @@ static int scs_remove(struct device *dev) tasklet_kill(&scs->tasklet); snd_card_free_when_closed(scs->card); - - return 0; -} - -static void scs_update(struct fw_unit *unit) -{ - struct scs *scs = dev_get_drvdata(&unit->device); - __be64 data; - - data = cpu_to_be64(((u64)HSS1394_TAG_CHANGE_ADDRESS << 56) | - scs->hss_handler.offset); - snd_fw_transaction(scs->unit, TCODE_WRITE_BLOCK_REQUEST, - HSS1394_ADDRESS, &data, 8); } static const struct ieee1394_device_id scs_id_table[] = { @@ -506,10 +503,10 @@ static struct fw_driver scs_driver = { .owner = THIS_MODULE, .name = KBUILD_MODNAME, .bus = &fw_bus_type, - .probe = scs_probe, - .remove = scs_remove, }, + .probe = scs_probe, .update = scs_update, + .remove = scs_remove, .id_table = scs_id_table, }; diff --git a/sound/firewire/speakers.c b/sound/firewire/speakers.c index d6846557f270..2c6386503940 100644 --- a/sound/firewire/speakers.c +++ b/sound/firewire/speakers.c @@ -663,45 +663,9 @@ static void fwspk_card_free(struct snd_card *card) mutex_destroy(&fwspk->mutex); } -static const struct device_info *fwspk_detect(struct fw_device *dev) +static int fwspk_probe(struct fw_unit *unit, + const struct ieee1394_device_id *id) { - static const struct device_info griffin_firewave = { - .driver_name = "FireWave", - .short_name = "FireWave", - .long_name = "Griffin FireWave Surround", - .pcm_constraints = firewave_constraints, - .mixer_channels = 6, - .mute_fb_id = 0x01, - .volume_fb_id = 0x02, - }; - static const struct device_info lacie_speakers = { - .driver_name = "FWSpeakers", - .short_name = "FireWire Speakers", - .long_name = "LaCie FireWire Speakers", - .pcm_constraints = lacie_speakers_constraints, - .mixer_channels = 1, - .mute_fb_id = 0x01, - .volume_fb_id = 0x01, - }; - struct fw_csr_iterator i; - int key, value; - - fw_csr_iterator_init(&i, dev->config_rom); - while (fw_csr_iterator_next(&i, &key, &value)) - if (key == CSR_VENDOR) - switch (value) { - case VENDOR_GRIFFIN: - return &griffin_firewave; - case VENDOR_LACIE: - return &lacie_speakers; - } - - return NULL; -} - -static int fwspk_probe(struct device *unit_dev) -{ - struct fw_unit *unit = fw_unit(unit_dev); struct fw_device *fw_dev = fw_parent_device(unit); struct snd_card *card; struct fwspk *fwspk; @@ -711,17 +675,13 @@ static int fwspk_probe(struct device *unit_dev) err = snd_card_create(-1, NULL, THIS_MODULE, sizeof(*fwspk), &card); if (err < 0) return err; - snd_card_set_dev(card, unit_dev); + snd_card_set_dev(card, &unit->device); fwspk = card->private_data; fwspk->card = card; mutex_init(&fwspk->mutex); fwspk->unit = fw_unit_get(unit); - fwspk->device_info = fwspk_detect(fw_dev); - if (!fwspk->device_info) { - err = -ENODEV; - goto err_unit; - } + fwspk->device_info = (const struct device_info *)id->driver_data; err = cmp_connection_init(&fwspk->connection, unit, 0); if (err < 0) @@ -756,7 +716,7 @@ static int fwspk_probe(struct device *unit_dev) if (err < 0) goto error; - dev_set_drvdata(unit_dev, fwspk); + dev_set_drvdata(&unit->device, fwspk); return 0; @@ -770,22 +730,6 @@ error: return err; } -static int fwspk_remove(struct device *dev) -{ - struct fwspk *fwspk = dev_get_drvdata(dev); - - amdtp_out_stream_pcm_abort(&fwspk->stream); - snd_card_disconnect(fwspk->card); - - mutex_lock(&fwspk->mutex); - fwspk_stop_stream(fwspk); - mutex_unlock(&fwspk->mutex); - - snd_card_free_when_closed(fwspk->card); - - return 0; -} - static void fwspk_bus_reset(struct fw_unit *unit) { struct fwspk *fwspk = dev_get_drvdata(&unit->device); @@ -803,6 +747,40 @@ static void fwspk_bus_reset(struct fw_unit *unit) amdtp_out_stream_update(&fwspk->stream); } +static void fwspk_remove(struct fw_unit *unit) +{ + struct fwspk *fwspk = dev_get_drvdata(&unit->device); + + amdtp_out_stream_pcm_abort(&fwspk->stream); + snd_card_disconnect(fwspk->card); + + mutex_lock(&fwspk->mutex); + fwspk_stop_stream(fwspk); + mutex_unlock(&fwspk->mutex); + + snd_card_free_when_closed(fwspk->card); +} + +static const struct device_info griffin_firewave = { + .driver_name = "FireWave", + .short_name = "FireWave", + .long_name = "Griffin FireWave Surround", + .pcm_constraints = firewave_constraints, + .mixer_channels = 6, + .mute_fb_id = 0x01, + .volume_fb_id = 0x02, +}; + +static const struct device_info lacie_speakers = { + .driver_name = "FWSpeakers", + .short_name = "FireWire Speakers", + .long_name = "LaCie FireWire Speakers", + .pcm_constraints = lacie_speakers_constraints, + .mixer_channels = 1, + .mute_fb_id = 0x01, + .volume_fb_id = 0x01, +}; + static const struct ieee1394_device_id fwspk_id_table[] = { { .match_flags = IEEE1394_MATCH_VENDOR_ID | @@ -813,6 +791,7 @@ static const struct ieee1394_device_id fwspk_id_table[] = { .model_id = 0x00f970, .specifier_id = SPECIFIER_1394TA, .version = VERSION_AVC, + .driver_data = (kernel_ulong_t)&griffin_firewave, }, { .match_flags = IEEE1394_MATCH_VENDOR_ID | @@ -823,6 +802,7 @@ static const struct ieee1394_device_id fwspk_id_table[] = { .model_id = 0x00f970, .specifier_id = SPECIFIER_1394TA, .version = VERSION_AVC, + .driver_data = (kernel_ulong_t)&lacie_speakers, }, { } }; @@ -833,10 +813,10 @@ static struct fw_driver fwspk_driver = { .owner = THIS_MODULE, .name = KBUILD_MODNAME, .bus = &fw_bus_type, - .probe = fwspk_probe, - .remove = fwspk_remove, }, + .probe = fwspk_probe, .update = fwspk_bus_reset, + .remove = fwspk_remove, .id_table = fwspk_id_table, }; -- cgit v1.2.3 From bcabcfd2e09ceb8599a33001e812e7cbad00fc4d Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sun, 9 Jun 2013 18:15:00 +0200 Subject: firewire: remove support of fw_driver.driver.probe and .remove methods After all IEEE 1394 high-level drivers being converted to bus-specific .probe/.remove methods, remove support of the obsolete generic methods. Signed-off-by: Stefan Richter --- drivers/firewire/core-device.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c index c152edd6cd3a..de4aa409abe2 100644 --- a/drivers/firewire/core-device.c +++ b/drivers/firewire/core-device.c @@ -194,10 +194,7 @@ static int fw_unit_probe(struct device *dev) struct fw_driver *driver = container_of(dev->driver, struct fw_driver, driver); - if (driver->probe) - return driver->probe(fw_unit(dev), unit_match(dev, dev->driver)); - else - return driver->driver.probe(dev); + return driver->probe(fw_unit(dev), unit_match(dev, dev->driver)); } static int fw_unit_remove(struct device *dev) @@ -205,10 +202,7 @@ static int fw_unit_remove(struct device *dev) struct fw_driver *driver = container_of(dev->driver, struct fw_driver, driver); - if (driver->remove) - return driver->remove(fw_unit(dev)), 0; - else - return driver->driver.remove(dev); + return driver->remove(fw_unit(dev)), 0; } static int get_modalias(struct fw_unit *unit, char *buffer, size_t buffer_size) -- cgit v1.2.3 From 9d5e2a023eee96b2326f9b0830a753b400917280 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Sun, 9 Jun 2013 09:41:23 -0700 Subject: MAINTAINERS: Update email address for Anton Vorontsov Old email addresses seem to gradually stop working so people are getting bounces. This patch updates my email address. Suggested-by: Andy Shevchenko Signed-off-by: Anton Vorontsov --- MAINTAINERS | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index f35a259a6564..9e63f919922f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -742,7 +742,7 @@ S: Maintained F: arch/arm/mach-highbank/ ARM/CAVIUM NETWORKS CNS3XXX MACHINE SUPPORT -M: Anton Vorontsov +M: Anton Vorontsov S: Maintained F: arch/arm/mach-cns3xxx/ T: git git://git.infradead.org/users/cbou/linux-cns3xxx.git @@ -6314,7 +6314,7 @@ F: include/linux/timer* F: kernel/*timer* POWER SUPPLY CLASS/SUBSYSTEM and DRIVERS -M: Anton Vorontsov +M: Anton Vorontsov M: David Woodhouse T: git git://git.infradead.org/battery-2.6.git S: Maintained @@ -6424,7 +6424,7 @@ S: Maintained F: drivers/block/ps3vram.c PSTORE FILESYSTEM -M: Anton Vorontsov +M: Anton Vorontsov M: Colin Cross M: Kees Cook M: Tony Luck @@ -7122,7 +7122,7 @@ F: drivers/mmc/host/sdhci.* F: drivers/mmc/host/sdhci-pltfm.[ch] SECURE DIGITAL HOST CONTROLLER INTERFACE, OPEN FIRMWARE BINDINGS (SDHCI-OF) -M: Anton Vorontsov +M: Anton Vorontsov L: linuxppc-dev@lists.ozlabs.org L: linux-mmc@vger.kernel.org S: Maintained -- cgit v1.2.3 From e24142178fc2f60824af4312d326edacdcd667a3 Mon Sep 17 00:00:00 2001 From: Andrew Chew Date: Thu, 6 Jun 2013 14:12:43 -0700 Subject: tps65090-charger: Fix AC detect The VACG interrupt was not being enabled. Thus, interrupts were never generated when AC status changes. In addition, interrupts were never cleared after taking and processing the interrupt. Added the register offset for the INTR_MASK register, since this is needed to unmask the VACG interrupt. Enabled the VACG interrupt in tps65090_config_charger(). Cleared interrupts after processing, in tps65090_charger_isr(). Also removed unused variable "enable" in tps65090_enable_charging(), and fixed a typo in one of the dev_err() prints. Signed-off-by: Andrew Chew Tested-by: Rhyland Klein Acked-by: Rhyland Klein Signed-off-by: Anton Vorontsov --- drivers/power/tps65090-charger.c | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/drivers/power/tps65090-charger.c b/drivers/power/tps65090-charger.c index 77ab8561fa34..e628f9813e08 100644 --- a/drivers/power/tps65090-charger.c +++ b/drivers/power/tps65090-charger.c @@ -27,6 +27,7 @@ #include #define TPS65090_REG_INTR_STS 0x00 +#define TPS65090_REG_INTR_MASK 0x02 #define TPS65090_REG_CG_CTRL0 0x04 #define TPS65090_REG_CG_CTRL1 0x05 #define TPS65090_REG_CG_CTRL2 0x06 @@ -67,8 +68,7 @@ static int tps65090_low_chrg_current(struct tps65090_charger *charger) return 0; } -static int tps65090_enable_charging(struct tps65090_charger *charger, - uint8_t enable) +static int tps65090_enable_charging(struct tps65090_charger *charger) { int ret; uint8_t ctrl0 = 0; @@ -84,7 +84,7 @@ static int tps65090_enable_charging(struct tps65090_charger *charger, ret = tps65090_write(charger->dev->parent, TPS65090_REG_CG_CTRL0, (ctrl0 | TPS65090_CHARGER_ENABLE)); if (ret < 0) { - dev_err(charger->dev, "%s(): error reading in register 0x%x\n", + dev_err(charger->dev, "%s(): error writing in register 0x%x\n", __func__, TPS65090_REG_CG_CTRL0); return ret; } @@ -93,6 +93,7 @@ static int tps65090_enable_charging(struct tps65090_charger *charger, static int tps65090_config_charger(struct tps65090_charger *charger) { + uint8_t intrmask = 0; int ret; if (charger->pdata->enable_low_current_chrg) { @@ -104,6 +105,23 @@ static int tps65090_config_charger(struct tps65090_charger *charger) } } + /* Enable the VACG interrupt for AC power detect */ + ret = tps65090_read(charger->dev->parent, TPS65090_REG_INTR_MASK, + &intrmask); + if (ret < 0) { + dev_err(charger->dev, "%s(): error reading in register 0x%x\n", + __func__, TPS65090_REG_INTR_MASK); + return ret; + } + + ret = tps65090_write(charger->dev->parent, TPS65090_REG_INTR_MASK, + (intrmask | TPS65090_VACG)); + if (ret < 0) { + dev_err(charger->dev, "%s(): error writing in register 0x%x\n", + __func__, TPS65090_REG_CG_CTRL0); + return ret; + } + return 0; } @@ -146,7 +164,7 @@ static irqreturn_t tps65090_charger_isr(int irq, void *dev_id) } if (intrsts & TPS65090_VACG) { - ret = tps65090_enable_charging(charger, 1); + ret = tps65090_enable_charging(charger); if (ret < 0) return IRQ_HANDLED; charger->ac_online = 1; @@ -154,6 +172,13 @@ static irqreturn_t tps65090_charger_isr(int irq, void *dev_id) charger->ac_online = 0; } + /* Clear interrupts. */ + ret = tps65090_write(charger->dev->parent, TPS65090_REG_INTR_STS, 0x00); + if (ret < 0) { + dev_err(charger->dev, "%s(): Error in writing reg 0x%x\n", + __func__, TPS65090_REG_INTR_STS); + } + if (charger->prev_ac_online != charger->ac_online) power_supply_changed(&charger->ac); @@ -270,7 +295,7 @@ static int tps65090_charger_probe(struct platform_device *pdev) } if (status1 != 0) { - ret = tps65090_enable_charging(cdata, 1); + ret = tps65090_enable_charging(cdata); if (ret < 0) { dev_err(cdata->dev, "error enabling charger\n"); goto fail_free_irq; -- cgit v1.2.3 From e5409cbd8c0bf44f65e7528635cf05ed298e0c61 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 6 Jun 2013 18:25:12 -0700 Subject: charger-manager: Add missing newlines, fix a couple of typos, add pr_fmt Make sure that dev_ calls are newline terminated. Add #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt to prefix all pr_ calls with "charger-manager: " Fix a couple of typos. Fix formats with terminating n that should be \n. Coalesce formats for easier grep. Align arguments to open parenthesis for these dev_ calls. Add missing spaces after coalescing multiple string segments. Signed-off-by: Joe Perches Acked-by: Kees Cook Signed-off-by: Anton Vorontsov --- drivers/power/charger-manager.c | 137 +++++++++++++++++----------------------- 1 file changed, 57 insertions(+), 80 deletions(-) diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c index 98de1ddce458..ba420292628e 100644 --- a/drivers/power/charger-manager.c +++ b/drivers/power/charger-manager.c @@ -12,6 +12,8 @@ * published by the Free Software Foundation. **/ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -195,8 +197,8 @@ static bool is_charging(struct charger_manager *cm) cm->charger_stat[i], POWER_SUPPLY_PROP_ONLINE, &val); if (ret) { - dev_warn(cm->dev, "Cannot read ONLINE value from %s.\n", - cm->desc->psy_charger_stat[i]); + dev_warn(cm->dev, "Cannot read ONLINE value from %s\n", + cm->desc->psy_charger_stat[i]); continue; } if (val.intval == 0) @@ -210,8 +212,8 @@ static bool is_charging(struct charger_manager *cm) cm->charger_stat[i], POWER_SUPPLY_PROP_STATUS, &val); if (ret) { - dev_warn(cm->dev, "Cannot read STATUS value from %s.\n", - cm->desc->psy_charger_stat[i]); + dev_warn(cm->dev, "Cannot read STATUS value from %s\n", + cm->desc->psy_charger_stat[i]); continue; } if (val.intval == POWER_SUPPLY_STATUS_FULL || @@ -289,7 +291,7 @@ static bool is_polling_required(struct charger_manager *cm) return is_charging(cm); default: dev_warn(cm->dev, "Incorrect polling_mode (%d)\n", - cm->desc->polling_mode); + cm->desc->polling_mode); } return false; @@ -331,9 +333,8 @@ static int try_charger_enable(struct charger_manager *cm, bool enable) err = regulator_enable(desc->charger_regulators[i].consumer); if (err < 0) { - dev_warn(cm->dev, - "Cannot enable %s regulator\n", - desc->charger_regulators[i].regulator_name); + dev_warn(cm->dev, "Cannot enable %s regulator\n", + desc->charger_regulators[i].regulator_name); } } } else { @@ -350,9 +351,8 @@ static int try_charger_enable(struct charger_manager *cm, bool enable) err = regulator_disable(desc->charger_regulators[i].consumer); if (err < 0) { - dev_warn(cm->dev, - "Cannot disable %s regulator\n", - desc->charger_regulators[i].regulator_name); + dev_warn(cm->dev, "Cannot disable %s regulator\n", + desc->charger_regulators[i].regulator_name); } } @@ -365,9 +365,8 @@ static int try_charger_enable(struct charger_manager *cm, bool enable) desc->charger_regulators[i].consumer)) { regulator_force_disable( desc->charger_regulators[i].consumer); - dev_warn(cm->dev, - "Disable regulator(%s) forcibly.\n", - desc->charger_regulators[i].regulator_name); + dev_warn(cm->dev, "Disable regulator(%s) forcibly\n", + desc->charger_regulators[i].regulator_name); } } } @@ -450,7 +449,7 @@ static void uevent_notify(struct charger_manager *cm, const char *event) strncpy(env_str, event, UEVENT_BUF_SIZE); kobject_uevent(&cm->dev->kobj, KOBJ_CHANGE); - dev_info(cm->dev, "%s", event); + dev_info(cm->dev, "%s\n", event); } /** @@ -478,7 +477,7 @@ static void fullbatt_vchk(struct work_struct *work) err = get_batt_uV(cm, &batt_uV); if (err) { - dev_err(cm->dev, "%s: get_batt_uV error(%d).\n", __func__, err); + dev_err(cm->dev, "%s: get_batt_uV error(%d)\n", __func__, err); return; } @@ -486,7 +485,7 @@ static void fullbatt_vchk(struct work_struct *work) if (diff < 0) return; - dev_info(cm->dev, "VBATT dropped %duV after full-batt.\n", diff); + dev_info(cm->dev, "VBATT dropped %duV after full-batt\n", diff); if (diff > desc->fullbatt_vchkdrop_uV) { try_charger_restart(cm); @@ -519,7 +518,7 @@ static int check_charging_duration(struct charger_manager *cm) duration = curr - cm->charging_start_time; if (duration > desc->charging_max_duration_ms) { - dev_info(cm->dev, "Charging duration exceed %lldms", + dev_info(cm->dev, "Charging duration exceed %lldms\n", desc->charging_max_duration_ms); uevent_notify(cm, "Discharging"); try_charger_enable(cm, false); @@ -530,9 +529,9 @@ static int check_charging_duration(struct charger_manager *cm) if (duration > desc->charging_max_duration_ms && is_ext_pwr_online(cm)) { - dev_info(cm->dev, "DisCharging duration exceed %lldms", + dev_info(cm->dev, "Discharging duration exceed %lldms\n", desc->discharging_max_duration_ms); - uevent_notify(cm, "Recharing"); + uevent_notify(cm, "Recharging"); try_charger_enable(cm, true); ret = true; } @@ -579,7 +578,7 @@ static bool _cm_monitor(struct charger_manager *cm) */ } else if (!cm->emergency_stop && check_charging_duration(cm)) { dev_dbg(cm->dev, - "Charging/Discharging duration is out of range"); + "Charging/Discharging duration is out of range\n"); /* * Check dropped voltage of battery. If battery voltage is more * dropped than fullbatt_vchkdrop_uV after fully charged state, @@ -595,7 +594,7 @@ static bool _cm_monitor(struct charger_manager *cm) */ } else if (!cm->emergency_stop && is_full_charged(cm) && cm->charger_enabled) { - dev_info(cm->dev, "EVENT_HANDLE: Battery Fully Charged.\n"); + dev_info(cm->dev, "EVENT_HANDLE: Battery Fully Charged\n"); uevent_notify(cm, default_event_names[CM_EVENT_BATT_FULL]); try_charger_enable(cm, false); @@ -725,7 +724,7 @@ static void fullbatt_handler(struct charger_manager *cm) cm->fullbatt_vchk_jiffies_at = 1; out: - dev_info(cm->dev, "EVENT_HANDLE: Battery Fully Charged.\n"); + dev_info(cm->dev, "EVENT_HANDLE: Battery Fully Charged\n"); uevent_notify(cm, default_event_names[CM_EVENT_BATT_FULL]); } @@ -972,7 +971,7 @@ static bool cm_setup_timer(void) mutex_unlock(&cm_list_mtx); if (wakeup_ms < UINT_MAX && wakeup_ms > 0) { - pr_info("Charger Manager wakeup timer: %u ms.\n", wakeup_ms); + pr_info("Charger Manager wakeup timer: %u ms\n", wakeup_ms); if (rtc_dev) { struct rtc_wkalrm tmp; unsigned long time, now; @@ -1005,8 +1004,7 @@ static bool cm_setup_timer(void) ret = false; } - pr_info("Waking up after %lu secs.\n", - time - now); + pr_info("Waking up after %lu secs\n", time - now); rtc_time_to_tm(time, &tmp.time); rtc_set_alarm(rtc_dev, &tmp); @@ -1101,7 +1099,7 @@ int setup_charger_manager(struct charger_global_desc *gd) g_desc = NULL; if (!gd->rtc_only_wakeup) { - pr_err("The callback rtc_only_wakeup is not given.\n"); + pr_err("The callback rtc_only_wakeup is not given\n"); return -EINVAL; } @@ -1112,7 +1110,7 @@ int setup_charger_manager(struct charger_global_desc *gd) /* Retry at probe. RTC may be not registered yet */ } } else { - pr_warn("No wakeup timer is given for charger manager." + pr_warn("No wakeup timer is given for charger manager. " "In-suspend monitoring won't work.\n"); } @@ -1138,13 +1136,13 @@ static void charger_extcon_work(struct work_struct *work) cable->min_uA, cable->max_uA); if (ret < 0) { pr_err("Cannot set current limit of %s (%s)\n", - cable->charger->regulator_name, cable->name); + cable->charger->regulator_name, cable->name); return; } pr_info("Set current limit of %s : %duA ~ %duA\n", - cable->charger->regulator_name, - cable->min_uA, cable->max_uA); + cable->charger->regulator_name, + cable->min_uA, cable->max_uA); } try_charger_enable(cable->cm, cable->attached); @@ -1210,9 +1208,8 @@ static int charger_extcon_init(struct charger_manager *cm, ret = extcon_register_interest(&cable->extcon_dev, cable->extcon_name, cable->name, &cable->nb); if (ret < 0) { - pr_info("Cannot register extcon_dev for %s(cable: %s).\n", - cable->extcon_name, - cable->name); + pr_info("Cannot register extcon_dev for %s(cable: %s)\n", + cable->extcon_name, cable->name); ret = -EINVAL; } @@ -1243,8 +1240,8 @@ static int charger_manager_register_extcon(struct charger_manager *cm) charger->consumer = regulator_get(cm->dev, charger->regulator_name); if (charger->consumer == NULL) { - dev_err(cm->dev, "Cannot find charger(%s)n", - charger->regulator_name); + dev_err(cm->dev, "Cannot find charger(%s)\n", + charger->regulator_name); ret = -EINVAL; goto err; } @@ -1255,8 +1252,8 @@ static int charger_manager_register_extcon(struct charger_manager *cm) ret = charger_extcon_init(cm, cable); if (ret < 0) { - dev_err(cm->dev, "Cannot initialize charger(%s)n", - charger->regulator_name); + dev_err(cm->dev, "Cannot initialize charger(%s)\n", + charger->regulator_name); goto err; } cable->charger = charger; @@ -1347,10 +1344,8 @@ static ssize_t charger_externally_control_store(struct device *dev, } } else { dev_warn(cm->dev, - "'%s' regulator should be controlled " - "in charger-manager because charger-manager " - "must need at least one charger for charging\n", - charger->regulator_name); + "'%s' regulator should be controlled in charger-manager because charger-manager must need at least one charger for charging\n", + charger->regulator_name); } return count; @@ -1386,8 +1381,6 @@ static int charger_manager_register_sysfs(struct charger_manager *cm) snprintf(buf, 10, "charger.%d", i); str = kzalloc(sizeof(char) * (strlen(buf) + 1), GFP_KERNEL); if (!str) { - dev_err(cm->dev, "Cannot allocate memory: %s\n", - charger->regulator_name); ret = -ENOMEM; goto err; } @@ -1423,26 +1416,21 @@ static int charger_manager_register_sysfs(struct charger_manager *cm) !chargers_externally_control) chargers_externally_control = 0; - dev_info(cm->dev, "'%s' regulator's externally_control" - "is %d\n", charger->regulator_name, - charger->externally_control); + dev_info(cm->dev, "'%s' regulator's externally_control is %d\n", + charger->regulator_name, charger->externally_control); ret = sysfs_create_group(&cm->charger_psy.dev->kobj, &charger->attr_g); if (ret < 0) { - dev_err(cm->dev, "Cannot create sysfs entry" - "of %s regulator\n", - charger->regulator_name); + dev_err(cm->dev, "Cannot create sysfs entry of %s regulator\n", + charger->regulator_name); ret = -EINVAL; goto err; } } if (chargers_externally_control) { - dev_err(cm->dev, "Cannot register regulator because " - "charger-manager must need at least " - "one charger for charging battery\n"); - + dev_err(cm->dev, "Cannot register regulator because charger-manager must need at least one charger for charging battery\n"); ret = -EINVAL; goto err; } @@ -1463,7 +1451,7 @@ static int charger_manager_probe(struct platform_device *pdev) rtc_dev = rtc_class_open(g_desc->rtc_name); if (IS_ERR_OR_NULL(rtc_dev)) { rtc_dev = NULL; - dev_err(&pdev->dev, "Cannot get RTC %s.\n", + dev_err(&pdev->dev, "Cannot get RTC %s\n", g_desc->rtc_name); ret = -ENODEV; goto err_alloc; @@ -1471,14 +1459,13 @@ static int charger_manager_probe(struct platform_device *pdev) } if (!desc) { - dev_err(&pdev->dev, "No platform data (desc) found.\n"); + dev_err(&pdev->dev, "No platform data (desc) found\n"); ret = -ENODEV; goto err_alloc; } cm = kzalloc(sizeof(struct charger_manager), GFP_KERNEL); if (!cm) { - dev_err(&pdev->dev, "Cannot allocate memory.\n"); ret = -ENOMEM; goto err_alloc; } @@ -1487,7 +1474,6 @@ static int charger_manager_probe(struct platform_device *pdev) cm->dev = &pdev->dev; cm->desc = kmemdup(desc, sizeof(struct charger_desc), GFP_KERNEL); if (!cm->desc) { - dev_err(&pdev->dev, "Cannot allocate memory.\n"); ret = -ENOMEM; goto err_alloc_desc; } @@ -1498,33 +1484,28 @@ static int charger_manager_probe(struct platform_device *pdev) * Users may intentionally ignore those two features. */ if (desc->fullbatt_uV == 0) { - dev_info(&pdev->dev, "Ignoring full-battery voltage threshold" - " as it is not supplied."); + dev_info(&pdev->dev, "Ignoring full-battery voltage threshold as it is not supplied\n"); } if (!desc->fullbatt_vchkdrop_ms || !desc->fullbatt_vchkdrop_uV) { - dev_info(&pdev->dev, "Disabling full-battery voltage drop " - "checking mechanism as it is not supplied."); + dev_info(&pdev->dev, "Disabling full-battery voltage drop checking mechanism as it is not supplied\n"); desc->fullbatt_vchkdrop_ms = 0; desc->fullbatt_vchkdrop_uV = 0; } if (desc->fullbatt_soc == 0) { - dev_info(&pdev->dev, "Ignoring full-battery soc(state of" - " charge) threshold as it is not" - " supplied."); + dev_info(&pdev->dev, "Ignoring full-battery soc(state of charge) threshold as it is not supplied\n"); } if (desc->fullbatt_full_capacity == 0) { - dev_info(&pdev->dev, "Ignoring full-battery full capacity" - " threshold as it is not supplied."); + dev_info(&pdev->dev, "Ignoring full-battery full capacity threshold as it is not supplied\n"); } if (!desc->charger_regulators || desc->num_charger_regulators < 1) { ret = -EINVAL; - dev_err(&pdev->dev, "charger_regulators undefined.\n"); + dev_err(&pdev->dev, "charger_regulators undefined\n"); goto err_no_charger; } if (!desc->psy_charger_stat || !desc->psy_charger_stat[0]) { - dev_err(&pdev->dev, "No power supply defined.\n"); + dev_err(&pdev->dev, "No power supply defined\n"); ret = -EINVAL; goto err_no_charger_stat; } @@ -1544,9 +1525,8 @@ static int charger_manager_probe(struct platform_device *pdev) cm->charger_stat[i] = power_supply_get_by_name( desc->psy_charger_stat[i]); if (!cm->charger_stat[i]) { - dev_err(&pdev->dev, "Cannot find power supply " - "\"%s\"\n", - desc->psy_charger_stat[i]); + dev_err(&pdev->dev, "Cannot find power supply \"%s\"\n", + desc->psy_charger_stat[i]); ret = -ENODEV; goto err_chg_stat; } @@ -1555,7 +1535,7 @@ static int charger_manager_probe(struct platform_device *pdev) cm->fuel_gauge = power_supply_get_by_name(desc->psy_fuel_gauge); if (!cm->fuel_gauge) { dev_err(&pdev->dev, "Cannot find power supply \"%s\"\n", - desc->psy_fuel_gauge); + desc->psy_fuel_gauge); ret = -ENODEV; goto err_chg_stat; } @@ -1575,9 +1555,7 @@ static int charger_manager_probe(struct platform_device *pdev) if (!desc->charging_max_duration_ms || !desc->discharging_max_duration_ms) { - dev_info(&pdev->dev, "Cannot limit charging duration " - "checking mechanism to prevent overcharge/overheat " - "and control discharging duration"); + dev_info(&pdev->dev, "Cannot limit charging duration checking mechanism to prevent overcharge/overheat and control discharging duration\n"); desc->charging_max_duration_ms = 0; desc->discharging_max_duration_ms = 0; } @@ -1598,7 +1576,6 @@ static int charger_manager_probe(struct platform_device *pdev) NUM_CHARGER_PSY_OPTIONAL), GFP_KERNEL); if (!cm->charger_psy.properties) { - dev_err(&pdev->dev, "Cannot allocate for psy properties.\n"); ret = -ENOMEM; goto err_chg_stat; } @@ -1636,8 +1613,8 @@ static int charger_manager_probe(struct platform_device *pdev) ret = power_supply_register(NULL, &cm->charger_psy); if (ret) { - dev_err(&pdev->dev, "Cannot register charger-manager with" - " name \"%s\".\n", cm->charger_psy.name); + dev_err(&pdev->dev, "Cannot register charger-manager with name \"%s\"\n", + cm->charger_psy.name); goto err_register; } @@ -1948,7 +1925,7 @@ void cm_notify_event(struct power_supply *psy, enum cm_event_types type, uevent_notify(cm, msg ? msg : default_event_names[type]); break; default: - dev_err(cm->dev, "%s type not specified.\n", __func__); + dev_err(cm->dev, "%s: type not specified\n", __func__); break; } } -- cgit v1.2.3 From 912b9ac683b112615d5605686f1dc086402ce9f7 Mon Sep 17 00:00:00 2001 From: Shane Huang Date: Sat, 8 Jun 2013 16:00:16 +0800 Subject: ahci: remove pmp link online check in FBS EH ata_link_online() check in ahci_error_intr() is unnecessary, it should be removed otherwise may lead to lockup with FBS enabled PMP. http://marc.info/?l=linux-ide&m=137050421603272&w=2 Reported-by: Yu Liu Signed-off-by: Shane Huang Signed-off-by: Tejun Heo Cc: stable@vger.kernel.org --- drivers/ata/libahci.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index a70ff154f586..7b9bdd822c62 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -1560,8 +1560,7 @@ static void ahci_error_intr(struct ata_port *ap, u32 irq_stat) u32 fbs = readl(port_mmio + PORT_FBS); int pmp = fbs >> PORT_FBS_DWE_OFFSET; - if ((fbs & PORT_FBS_SDE) && (pmp < ap->nr_pmp_links) && - ata_link_online(&ap->pmp_link[pmp])) { + if ((fbs & PORT_FBS_SDE) && (pmp < ap->nr_pmp_links)) { link = &ap->pmp_link[pmp]; fbs_need_dec = true; } -- cgit v1.2.3 From 483180281f0ac60d1138710eb21f4b9961901294 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 7 Apr 2013 21:13:19 -0700 Subject: Input: evdev - flush queues during EVIOCGKEY-like ioctls If userspace requests current KEY-state, they very likely assume that no such events are pending in the output queue of the evdev device. Otherwise, they will parse events which they already handled via EVIOCGKEY(). For XKB applications this can cause irreversible keyboard states if a modifier is locked multiple times because a CTRL-DOWN event is handled once via EVIOCGKEY() and once from the queue via read(), even though it should handle it only once. Therefore, lets do the only logical thing and flush the evdev queue atomically during this ioctl. We only flush events that are affected by the given ioctl. This only affects boolean events like KEY, SND, SW and LED. ABS, REL and others are not affected as duplicate events can be handled gracefully by user-space. Note: This actually breaks semantics of the evdev ABI. However, investigations showed that userspace already expects the new semantics and we end up fixing at least all XKB applications. All applications that are aware of this race-condition mirror the KEY state for each open-file and detect/drop duplicate events. Hence, they do not care whether duplicates are posted or not and work fine with this fix. Also note that we need proper locking to guarantee atomicity and avoid dead-locks. event_lock must be locked before queue_lock (see input-core). However, we can safely release event_lock while flushing the queue. This allows the input-core to proceed with pending events and only stop if it needs our queue_lock to post new events. This should guarantee that we don't block event-dispatching for too long while flushing a single event queue. Signed-off-by: David Herrmann Acked-by: Peter Hutterer Signed-off-by: Dmitry Torokhov --- drivers/input/evdev.c | 133 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 129 insertions(+), 4 deletions(-) diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index f0f8928b3c8a..d2b34fbbc42e 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -52,6 +52,82 @@ struct evdev_client { struct input_event buffer[]; }; +/* flush queued events of type @type, caller must hold client->buffer_lock */ +static void __evdev_flush_queue(struct evdev_client *client, unsigned int type) +{ + unsigned int i, head, num; + unsigned int mask = client->bufsize - 1; + bool is_report; + struct input_event *ev; + + BUG_ON(type == EV_SYN); + + head = client->tail; + client->packet_head = client->tail; + + /* init to 1 so a leading SYN_REPORT will not be dropped */ + num = 1; + + for (i = client->tail; i != client->head; i = (i + 1) & mask) { + ev = &client->buffer[i]; + is_report = ev->type == EV_SYN && ev->code == SYN_REPORT; + + if (ev->type == type) { + /* drop matched entry */ + continue; + } else if (is_report && !num) { + /* drop empty SYN_REPORT groups */ + continue; + } else if (head != i) { + /* move entry to fill the gap */ + client->buffer[head].time = ev->time; + client->buffer[head].type = ev->type; + client->buffer[head].code = ev->code; + client->buffer[head].value = ev->value; + } + + num++; + head = (head + 1) & mask; + + if (is_report) { + num = 0; + client->packet_head = head; + } + } + + client->head = head; +} + +/* queue SYN_DROPPED event */ +static void evdev_queue_syn_dropped(struct evdev_client *client) +{ + unsigned long flags; + struct input_event ev; + ktime_t time; + + time = ktime_get(); + if (client->clkid != CLOCK_MONOTONIC) + time = ktime_sub(time, ktime_get_monotonic_offset()); + + ev.time = ktime_to_timeval(time); + ev.type = EV_SYN; + ev.code = SYN_DROPPED; + ev.value = 0; + + spin_lock_irqsave(&client->buffer_lock, flags); + + client->buffer[client->head++] = ev; + client->head &= client->bufsize - 1; + + if (unlikely(client->head == client->tail)) { + /* drop queue but keep our SYN_DROPPED event */ + client->tail = (client->head - 1) & (client->bufsize - 1); + client->packet_head = client->tail; + } + + spin_unlock_irqrestore(&client->buffer_lock, flags); +} + static void __pass_event(struct evdev_client *client, const struct input_event *event) { @@ -650,6 +726,51 @@ static int evdev_handle_set_keycode_v2(struct input_dev *dev, void __user *p) return input_set_keycode(dev, &ke); } +/* + * If we transfer state to the user, we should flush all pending events + * of the same type from the client's queue. Otherwise, they might end up + * with duplicate events, which can screw up client's state tracking. + * If bits_to_user fails after flushing the queue, we queue a SYN_DROPPED + * event so user-space will notice missing events. + * + * LOCKING: + * We need to take event_lock before buffer_lock to avoid dead-locks. But we + * need the even_lock only to guarantee consistent state. We can safely release + * it while flushing the queue. This allows input-core to handle filters while + * we flush the queue. + */ +static int evdev_handle_get_val(struct evdev_client *client, + struct input_dev *dev, unsigned int type, + unsigned long *bits, unsigned int max, + unsigned int size, void __user *p, int compat) +{ + int ret; + unsigned long *mem; + + mem = kmalloc(sizeof(unsigned long) * max, GFP_KERNEL); + if (!mem) + return -ENOMEM; + + spin_lock_irq(&dev->event_lock); + spin_lock(&client->buffer_lock); + + memcpy(mem, bits, sizeof(unsigned long) * max); + + spin_unlock(&dev->event_lock); + + __evdev_flush_queue(client, type); + + spin_unlock_irq(&client->buffer_lock); + + ret = bits_to_user(mem, max, size, p, compat); + if (ret < 0) + evdev_queue_syn_dropped(client); + + kfree(mem); + + return ret; +} + static int evdev_handle_mt_request(struct input_dev *dev, unsigned int size, int __user *ip) @@ -771,16 +892,20 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, return evdev_handle_mt_request(dev, size, ip); case EVIOCGKEY(0): - return bits_to_user(dev->key, KEY_MAX, size, p, compat_mode); + return evdev_handle_get_val(client, dev, EV_KEY, dev->key, + KEY_MAX, size, p, compat_mode); case EVIOCGLED(0): - return bits_to_user(dev->led, LED_MAX, size, p, compat_mode); + return evdev_handle_get_val(client, dev, EV_LED, dev->led, + LED_MAX, size, p, compat_mode); case EVIOCGSND(0): - return bits_to_user(dev->snd, SND_MAX, size, p, compat_mode); + return evdev_handle_get_val(client, dev, EV_SND, dev->snd, + SND_MAX, size, p, compat_mode); case EVIOCGSW(0): - return bits_to_user(dev->sw, SW_MAX, size, p, compat_mode); + return evdev_handle_get_val(client, dev, EV_SW, dev->sw, + SW_MAX, size, p, compat_mode); case EVIOCGNAME(0): return str_to_user(dev->name, size, p); -- cgit v1.2.3 From e596a02ccfc6503ae5c37b14c74b6b743eefe102 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sun, 9 Jun 2013 16:02:05 +0100 Subject: drm/i915: Remove dead code from SDVO initialisation The hotplug_mask is no longer used as the hpd interrupt setup is now handled in the core. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_sdvo.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 960358d8e336..ac923b74924a 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -2850,7 +2850,6 @@ bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob) struct drm_i915_private *dev_priv = dev->dev_private; struct intel_encoder *intel_encoder; struct intel_sdvo *intel_sdvo; - u32 hotplug_mask; int i; intel_sdvo = kzalloc(sizeof(struct intel_sdvo), GFP_KERNEL); if (!intel_sdvo) @@ -2879,18 +2878,6 @@ bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob) } } - hotplug_mask = 0; - if (IS_G4X(dev)) { - hotplug_mask = intel_sdvo->is_sdvob ? - SDVOB_HOTPLUG_INT_STATUS_G4X : SDVOC_HOTPLUG_INT_STATUS_G4X; - } else if (IS_GEN4(dev)) { - hotplug_mask = intel_sdvo->is_sdvob ? - SDVOB_HOTPLUG_INT_STATUS_I965 : SDVOC_HOTPLUG_INT_STATUS_I965; - } else { - hotplug_mask = intel_sdvo->is_sdvob ? - SDVOB_HOTPLUG_INT_STATUS_I915 : SDVOC_HOTPLUG_INT_STATUS_I915; - } - /* Only enable the hotplug irq if we need it, to work around noisy * hotplug lines. */ -- cgit v1.2.3 From 9e13f345887c179068bbc1f7389b7177bf88f57e Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sun, 9 Jun 2013 22:07:46 -0300 Subject: ASoC: sgtl5000: Let the codec acquire its clock On a mx6qsabrelite board the following error happens on probe: sgtl5000: probe of 0-000a failed with error -5 imx-sgtl5000 sound.13: ASoC: CODEC (null) not registered imx-sgtl5000 sound.13: snd_soc_register_card failed (-517) platform sound.13: Driver imx-sgtl5000 requests probe defer Prior to reading the codec ID we need to turn the SYS_MCLK clock, so let's enable the codec clock inside sgtl5000_i2c_probe(). Also remove the codec clock enable/disable functions from the machine driver. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown --- sound/soc/codecs/sgtl5000.c | 34 ++++++++++++++++++++++++++++++---- sound/soc/fsl/imx-sgtl5000.c | 30 +++++++----------------------- 2 files changed, 37 insertions(+), 27 deletions(-) diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index c8f2afb74706..2e0227bda8e0 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -114,6 +114,7 @@ struct sgtl5000_priv { struct regulator_bulk_data supplies[SGTL5000_SUPPLY_NUM]; struct ldo_regulator *ldo; struct regmap *regmap; + struct clk *mclk; }; /* @@ -1522,16 +1523,28 @@ static int sgtl5000_i2c_probe(struct i2c_client *client, return ret; } + sgtl5000->mclk = devm_clk_get(&client->dev, NULL); + if (IS_ERR(sgtl5000->mclk)) { + ret = PTR_ERR(sgtl5000->mclk); + dev_err(&client->dev, "Failed to get mclock: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(sgtl5000->mclk); + if (ret) + return ret; + /* read chip information */ ret = regmap_read(sgtl5000->regmap, SGTL5000_CHIP_ID, ®); if (ret) - return ret; + goto disable_clk; if (((reg & SGTL5000_PARTID_MASK) >> SGTL5000_PARTID_SHIFT) != SGTL5000_PARTID_PART_ID) { dev_err(&client->dev, "Device with ID register %x is not a sgtl5000\n", reg); - return -ENODEV; + ret = -ENODEV; + goto disable_clk; } rev = (reg & SGTL5000_REVID_MASK) >> SGTL5000_REVID_SHIFT; @@ -1542,17 +1555,30 @@ static int sgtl5000_i2c_probe(struct i2c_client *client, /* Ensure sgtl5000 will start with sane register values */ ret = sgtl5000_fill_defaults(sgtl5000); if (ret) - return ret; + goto disable_clk; ret = snd_soc_register_codec(&client->dev, &sgtl5000_driver, &sgtl5000_dai, 1); + if (ret) + goto disable_clk; + + return 0; + +disable_clk: + clk_disable_unprepare(sgtl5000->mclk); return ret; } static int sgtl5000_i2c_remove(struct i2c_client *client) { - snd_soc_unregister_codec(&client->dev); + struct sgtl5000_priv *sgtl5000; + sgtl5000 = devm_kzalloc(&client->dev, sizeof(struct sgtl5000_priv), + GFP_KERNEL); + if (!sgtl5000) + return -ENOMEM; + snd_soc_unregister_codec(&client->dev); + clk_disable_unprepare(sgtl5000->mclk); return 0; } diff --git a/sound/soc/fsl/imx-sgtl5000.c b/sound/soc/fsl/imx-sgtl5000.c index a60aaa053d28..823151b7653b 100644 --- a/sound/soc/fsl/imx-sgtl5000.c +++ b/sound/soc/fsl/imx-sgtl5000.c @@ -129,20 +129,10 @@ static int imx_sgtl5000_probe(struct platform_device *pdev) } data->codec_clk = clk_get(&codec_dev->dev, NULL); - if (IS_ERR(data->codec_clk)) { - /* assuming clock enabled by default */ - data->codec_clk = NULL; - ret = of_property_read_u32(codec_np, "clock-frequency", - &data->clk_frequency); - if (ret) { - dev_err(&codec_dev->dev, - "clock-frequency missing or invalid\n"); - goto fail; - } - } else { - data->clk_frequency = clk_get_rate(data->codec_clk); - clk_prepare_enable(data->codec_clk); - } + if (IS_ERR(data->codec_clk)) + goto fail; + + data->clk_frequency = clk_get_rate(data->codec_clk); data->dai.name = "HiFi"; data->dai.stream_name = "HiFi"; @@ -157,10 +147,10 @@ static int imx_sgtl5000_probe(struct platform_device *pdev) data->card.dev = &pdev->dev; ret = snd_soc_of_parse_card_name(&data->card, "model"); if (ret) - goto clk_fail; + goto fail; ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing"); if (ret) - goto clk_fail; + goto fail; data->card.num_links = 1; data->card.owner = THIS_MODULE; data->card.dai_link = &data->dai; @@ -170,7 +160,7 @@ static int imx_sgtl5000_probe(struct platform_device *pdev) ret = snd_soc_register_card(&data->card); if (ret) { dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); - goto clk_fail; + goto fail; } platform_set_drvdata(pdev, data); @@ -179,8 +169,6 @@ static int imx_sgtl5000_probe(struct platform_device *pdev) return 0; -clk_fail: - clk_put(data->codec_clk); fail: if (ssi_np) of_node_put(ssi_np); @@ -194,10 +182,6 @@ static int imx_sgtl5000_remove(struct platform_device *pdev) { struct imx_sgtl5000_data *data = platform_get_drvdata(pdev); - if (data->codec_clk) { - clk_disable_unprepare(data->codec_clk); - clk_put(data->codec_clk); - } snd_soc_unregister_card(&data->card); return 0; -- cgit v1.2.3 From e33d085d11e54bc9fb07b2555cd104d8e7b3089b Mon Sep 17 00:00:00 2001 From: "dan.carpenter@oracle.com" Date: Sun, 9 Jun 2013 16:07:28 +0300 Subject: spi: spi-xilinx: cleanup a check in xilinx_spi_txrx_bufs() '!' has higher precedence than comparisons so the original condition is equivalent to "if (xspi->remaining_bytes == 0)". This makes the static checkers complain. xspi->remaining_bytes is signed and from looking at the code briefly, I think it might be able to go negative. I suspect that going negative may cause a bug, but I don't have the hardware and can't test. Signed-off-by: Dan Carpenter Signed-off-by: Mark Brown --- drivers/spi/spi-xilinx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-xilinx.c b/drivers/spi/spi-xilinx.c index 34d18dcfa0db..109a535b639c 100644 --- a/drivers/spi/spi-xilinx.c +++ b/drivers/spi/spi-xilinx.c @@ -315,7 +315,7 @@ static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) } /* See if there is more data to send */ - if (!xspi->remaining_bytes > 0) + if (xspi->remaining_bytes <= 0) break; } -- cgit v1.2.3 From 77f0b9d2ff0c8ff7b36033dfc5b29dab7a0ebd9a Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 3 Jun 2013 22:27:17 +0200 Subject: pwm: devm: alloc correct pointer size The allocated object should be the size of what the pointer is pointing to and not the size of the pointer itself. Signed-off-by: Wolfram Sang Reviewed-by: Andy Shevchenko Signed-off-by: Thierry Reding --- drivers/pwm/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 32221cb0cbe7..0cf0f65eb037 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -694,7 +694,7 @@ struct pwm_device *devm_pwm_get(struct device *dev, const char *con_id) { struct pwm_device **ptr, *pwm; - ptr = devres_alloc(devm_pwm_release, sizeof(**ptr), GFP_KERNEL); + ptr = devres_alloc(devm_pwm_release, sizeof(*ptr), GFP_KERNEL); if (!ptr) return ERR_PTR(-ENOMEM); @@ -724,7 +724,7 @@ struct pwm_device *devm_of_pwm_get(struct device *dev, struct device_node *np, { struct pwm_device **ptr, *pwm; - ptr = devres_alloc(devm_pwm_release, sizeof(**ptr), GFP_KERNEL); + ptr = devres_alloc(devm_pwm_release, sizeof(*ptr), GFP_KERNEL); if (!ptr) return ERR_PTR(-ENOMEM); -- cgit v1.2.3 From 5e1cda5b8ae93f5f02e8c5a30390ac9b4d2c20e6 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Wed, 29 May 2013 03:10:53 +0100 Subject: irqdomain: Relax failure path on setting up mappings Commit 98aa468e, "irqdomain: Support for static IRQ mapping and association" introduced an API for directly associating blocks of hwirqs to linux irqs. However, if any irq in that block failed to map (say if the mapping functions returns an error because the irq is already mapped) then the whole thing will fail and roll back. This is probably too aggressive since there are valid reasons why a mapping may fail. ie. Firmware may have a particular IRQ marked as unusable. This patch drops the error path out of irq_domain_associate(). If a mapping fails, then it is simply skipped. There is no reason to fail the entire allocation. v2: Still output an information message on failed mappings and make sure attempted mapping gets cleared out of the irq_data structure. Signed-off-by: Grant Likely Cc: Paul Mundt Cc: Benjamin Herrenschmidt Cc: Thomas Gleixner --- kernel/irq/irqdomain.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index 20b677dd0b27..61d6d3c80fee 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -464,23 +464,15 @@ int irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base, /* * If map() returns -EPERM, this interrupt is protected * by the firmware or some other service and shall not - * be mapped. - * - * Since on some platforms we blindly try to map everything - * we end up with a log full of backtraces. - * - * So instead, we silently fail on -EPERM, it is the - * responsibility of the PIC driver to display a relevant - * message if needed. + * be mapped. Don't bother telling the user about it. */ if (ret != -EPERM) { - pr_err("irq-%i==>hwirq-0x%lx mapping failed: %d\n", - virq, hwirq, ret); - WARN_ON(1); + pr_info("%s didn't like hwirq-0x%lx to VIRQ%i mapping (rc=%d)\n", + of_node_full_name(domain->of_node), hwirq, virq, ret); } irq_data->domain = NULL; irq_data->hwirq = 0; - goto err_unmap; + continue; } } -- cgit v1.2.3 From 9bbf877d3b6b8c5991000296f40a3f0fe66fa89b Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 6 Jun 2013 12:10:24 +0100 Subject: irqdomain: Replace LEGACY mapping with LINEAR The LEGACY mapping unnecessarily complicates the irqdomain code and can easily be implemented with a linear mapping. By ripping it out and replacing it with the LINEAR mapping the object size of irqdomain.c shrinks by about 330 bytes (ARMv7) which offsets the additional allocation required by the linear map. It also makes it possible for current LEGACY map users to pre-allocate irq_descs for a subset of the hwirqs and dynamically allocate the rest as needed. Signed-off-by: Grant Likely Cc: Paul Mundt Cc: Benjamin Herrenschmidt Cc: Thomas Gleixner Cc: Rob Herring --- include/linux/irqdomain.h | 7 ---- kernel/irq/irqdomain.c | 84 ++++------------------------------------------- 2 files changed, 6 insertions(+), 85 deletions(-) diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index ba2c708adcff..6f062416e5bf 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h @@ -93,11 +93,6 @@ struct irq_domain { /* type of reverse mapping_technique */ unsigned int revmap_type; union { - struct { - unsigned int size; - unsigned int first_irq; - irq_hw_number_t first_hwirq; - } legacy; struct { unsigned int size; unsigned int *revmap; @@ -117,8 +112,6 @@ struct irq_domain { struct irq_domain_chip_generic *gc; }; -#define IRQ_DOMAIN_MAP_LEGACY 0 /* driver allocated fixed range of irqs. - * ie. legacy 8259, gets irqs 1..15 */ #define IRQ_DOMAIN_MAP_NOMAP 1 /* no fast reverse mapping */ #define IRQ_DOMAIN_MAP_LINEAR 2 /* linear map of interrupts */ #define IRQ_DOMAIN_MAP_TREE 3 /* radix tree */ diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index 61d6d3c80fee..1ac8cf41b9a5 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -82,13 +82,6 @@ void irq_domain_remove(struct irq_domain *domain) mutex_lock(&irq_domain_mutex); switch (domain->revmap_type) { - case IRQ_DOMAIN_MAP_LEGACY: - /* - * Legacy domains don't manage their own irq_desc - * allocations, we expect the caller to handle irq_desc - * freeing on their own. - */ - break; case IRQ_DOMAIN_MAP_TREE: /* * radix_tree_delete() takes care of destroying the root @@ -122,17 +115,6 @@ void irq_domain_remove(struct irq_domain *domain) } EXPORT_SYMBOL_GPL(irq_domain_remove); -static unsigned int irq_domain_legacy_revmap(struct irq_domain *domain, - irq_hw_number_t hwirq) -{ - irq_hw_number_t first_hwirq = domain->revmap_data.legacy.first_hwirq; - int size = domain->revmap_data.legacy.size; - - if (WARN_ON(hwirq < first_hwirq || hwirq >= first_hwirq + size)) - return 0; - return hwirq - first_hwirq + domain->revmap_data.legacy.first_irq; -} - /** * irq_domain_add_simple() - Allocate and register a simple irq_domain. * @of_node: pointer to interrupt controller's device tree node. @@ -213,57 +195,17 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node, void *host_data) { struct irq_domain *domain; - unsigned int i; - domain = irq_domain_alloc(of_node, IRQ_DOMAIN_MAP_LEGACY, ops, host_data); + pr_debug("Setting up legacy domain virq[%i:%i] ==> hwirq[%i:%i]\n", + first_irq, first_irq + size - 1, + (int)first_hwirq, (int)first_hwirq + size -1); + + domain = irq_domain_add_linear(of_node, first_hwirq + size, ops, host_data); if (!domain) return NULL; - domain->revmap_data.legacy.first_irq = first_irq; - domain->revmap_data.legacy.first_hwirq = first_hwirq; - domain->revmap_data.legacy.size = size; - - mutex_lock(&irq_domain_mutex); - /* Verify that all the irqs are available */ - for (i = 0; i < size; i++) { - int irq = first_irq + i; - struct irq_data *irq_data = irq_get_irq_data(irq); - - if (WARN_ON(!irq_data || irq_data->domain)) { - mutex_unlock(&irq_domain_mutex); - irq_domain_free(domain); - return NULL; - } - } + WARN_ON(irq_domain_associate_many(domain, first_irq, first_hwirq, size)); - /* Claim all of the irqs before registering a legacy domain */ - for (i = 0; i < size; i++) { - struct irq_data *irq_data = irq_get_irq_data(first_irq + i); - irq_data->hwirq = first_hwirq + i; - irq_data->domain = domain; - } - mutex_unlock(&irq_domain_mutex); - - for (i = 0; i < size; i++) { - int irq = first_irq + i; - int hwirq = first_hwirq + i; - - /* IRQ0 gets ignored */ - if (!irq) - continue; - - /* Legacy flags are left to default at this point, - * one can then use irq_create_mapping() to - * explicitly change them - */ - if (ops->map) - ops->map(domain, irq, hwirq); - - /* Clear norequest flags */ - irq_clear_status_flags(irq, IRQ_NOREQUEST); - } - - irq_domain_add(domain); return domain; } EXPORT_SYMBOL_GPL(irq_domain_add_legacy); @@ -492,10 +434,6 @@ int irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base, } return 0; - - err_unmap: - irq_domain_disassociate_many(domain, irq_base, i); - return -EINVAL; } EXPORT_SYMBOL_GPL(irq_domain_associate_many); @@ -575,10 +513,6 @@ unsigned int irq_create_mapping(struct irq_domain *domain, return virq; } - /* Get a virtual interrupt number */ - if (domain->revmap_type == IRQ_DOMAIN_MAP_LEGACY) - return irq_domain_legacy_revmap(domain, hwirq); - /* Allocate a virtual interrupt number */ hint = hwirq % nr_irqs; if (hint == 0) @@ -706,10 +640,6 @@ void irq_dispose_mapping(unsigned int virq) if (WARN_ON(domain == NULL)) return; - /* Never unmap legacy interrupts */ - if (domain->revmap_type == IRQ_DOMAIN_MAP_LEGACY) - return; - irq_domain_disassociate_many(domain, virq, 1); irq_free_desc(virq); } @@ -732,8 +662,6 @@ unsigned int irq_find_mapping(struct irq_domain *domain, return 0; switch (domain->revmap_type) { - case IRQ_DOMAIN_MAP_LEGACY: - return irq_domain_legacy_revmap(domain, hwirq); case IRQ_DOMAIN_MAP_LINEAR: return irq_linear_revmap(domain, hwirq); case IRQ_DOMAIN_MAP_TREE: -- cgit v1.2.3 From 0bb4afb45dd1add73ca643a865daa38716aeff0c Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 6 Jun 2013 14:23:30 +0100 Subject: irqdomain: Add a name field This patch adds a name field to the irq_domain structure to help mere mortals understand the mappings between irq domains and virqs. It also converts a number of places that have open-coded some kind of fudging an irqdomain name to use the new field. This means a more consistent display of names in irq domain log messages and debugfs output. Signed-off-by: Grant Likely --- include/linux/irqdomain.h | 1 + kernel/irq/generic-chip.c | 1 + kernel/irq/irqdomain.c | 19 ++++++------------- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index 6f062416e5bf..e5e513c2d104 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h @@ -89,6 +89,7 @@ struct irq_domain_chip_generic; */ struct irq_domain { struct list_head link; + const char *name; /* type of reverse mapping_technique */ unsigned int revmap_type; diff --git a/kernel/irq/generic-chip.c b/kernel/irq/generic-chip.c index 95575d8d5392..ca98cc5d6308 100644 --- a/kernel/irq/generic-chip.c +++ b/kernel/irq/generic-chip.c @@ -305,6 +305,7 @@ int irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip, /* Calc pointer to the next generic chip */ tmp += sizeof(*gc) + num_ct * sizeof(struct irq_chip_type); } + d->name = name; return 0; } EXPORT_SYMBOL_GPL(irq_alloc_domain_generic_chips); diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index 1ac8cf41b9a5..b1b5e6793fd2 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -410,12 +410,15 @@ int irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base, */ if (ret != -EPERM) { pr_info("%s didn't like hwirq-0x%lx to VIRQ%i mapping (rc=%d)\n", - of_node_full_name(domain->of_node), hwirq, virq, ret); + domain->name, hwirq, virq, ret); } irq_data->domain = NULL; irq_data->hwirq = 0; continue; } + /* If not already assigned, give the domain the chip's name */ + if (!domain->name && irq_data->chip) + domain->name = irq_data->chip->name; } switch (domain->revmap_type) { @@ -708,8 +711,6 @@ static int virq_debug_show(struct seq_file *m, void *private) { unsigned long flags; struct irq_desc *desc; - const char *p; - static const char none[] = "none"; void *data; int i; @@ -731,20 +732,12 @@ static int virq_debug_show(struct seq_file *m, void *private) seq_printf(m, "0x%05lx ", desc->irq_data.hwirq); chip = irq_desc_get_chip(desc); - if (chip && chip->name) - p = chip->name; - else - p = none; - seq_printf(m, "%-15s ", p); + seq_printf(m, "%-15s ", (chip && chip->name) ? chip->name : "none"); data = irq_desc_get_chip_data(desc); seq_printf(m, data ? "0x%p " : " %p ", data); - if (desc->irq_data.domain) - p = of_node_full_name(desc->irq_data.domain->of_node); - else - p = none; - seq_printf(m, "%s\n", p); + seq_printf(m, "%s\n", desc->irq_data.domain->name); } raw_spin_unlock_irqrestore(&desc->lock, flags); -- cgit v1.2.3 From cef5075c8c238ffd04c86a77a5a9bdbd18031137 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Wed, 11 Jul 2012 17:24:31 +0100 Subject: irqdomain: merge linear and tree reverse mappings. Keeping them separate makes irq_domain more complex and adds a lot of code (as proven by the diffstat). Merging them simplifies the whole scheme. This change makes it so both the tree and linear methods can be used by the same irq_domain instance. If the hwirq is less than the ->linear_size, then the linear map is used to reverse map the hwirq. Otherwise the radix tree is used. The test for which map to use is no more expensive that the existing code, so the performance of fast path is preserved. It also means that complex interrupt controllers can use both the linear map and a tree in the same domain. This may be useful for an interrupt controller with a base set of core irqs and a large number of GPIOs which might be used as irqs. The linear map could cover the core irqs, and the tree used for thas irqs. The linear map could cover the core irqs, and the tree used for the gpios. v2: Drop reorganization of revmap data Signed-off-by: Grant Likely Cc: Paul Mundt Cc: Benjamin Herrenschmidt Cc: Thomas Gleixner Cc: Rob Herring --- include/linux/irqdomain.h | 18 ++++---- kernel/irq/irqdomain.c | 107 +++++++++++++--------------------------------- 2 files changed, 39 insertions(+), 86 deletions(-) diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index e5e513c2d104..1cbb7413c121 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h @@ -75,7 +75,6 @@ struct irq_domain_chip_generic; * @link: Element in global irq_domain list. * @revmap_type: Method used for reverse mapping hwirq numbers to linux irq. This * will be one of the IRQ_DOMAIN_MAP_* values. - * @revmap_data: Revmap method specific data. * @ops: pointer to irq_domain methods * @host_data: private data pointer for use by owner. Not touched by irq_domain * core code. @@ -93,10 +92,9 @@ struct irq_domain { /* type of reverse mapping_technique */ unsigned int revmap_type; - union { + struct { struct { unsigned int size; - unsigned int *revmap; } linear; struct { unsigned int max_irq; @@ -111,11 +109,13 @@ struct irq_domain { struct device_node *of_node; /* Optional pointer to generic interrupt chips */ struct irq_domain_chip_generic *gc; + + /* Linear reverse map */ + unsigned int linear_revmap[]; }; #define IRQ_DOMAIN_MAP_NOMAP 1 /* no fast reverse mapping */ #define IRQ_DOMAIN_MAP_LINEAR 2 /* linear map of interrupts */ -#define IRQ_DOMAIN_MAP_TREE 3 /* radix tree */ #ifdef CONFIG_IRQ_DOMAIN struct irq_domain *irq_domain_add_simple(struct device_node *of_node, @@ -137,10 +137,6 @@ struct irq_domain *irq_domain_add_nomap(struct device_node *of_node, unsigned int max_irq, const struct irq_domain_ops *ops, void *host_data); -struct irq_domain *irq_domain_add_tree(struct device_node *of_node, - const struct irq_domain_ops *ops, - void *host_data); - extern struct irq_domain *irq_find_host(struct device_node *node); extern void irq_set_default_host(struct irq_domain *host); @@ -152,6 +148,12 @@ static inline struct irq_domain *irq_domain_add_legacy_isa( return irq_domain_add_legacy(of_node, NUM_ISA_INTERRUPTS, 0, 0, ops, host_data); } +static inline struct irq_domain *irq_domain_add_tree(struct device_node *of_node, + const struct irq_domain_ops *ops, + void *host_data) +{ + return irq_domain_add_linear(of_node, 0, ops, host_data); +} extern void irq_domain_remove(struct irq_domain *host); diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index b1b5e6793fd2..5a1d8ec8509e 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -34,22 +34,24 @@ static struct irq_domain *irq_default_domain; * to IRQ domain, or NULL on failure. */ static struct irq_domain *irq_domain_alloc(struct device_node *of_node, - unsigned int revmap_type, + unsigned int revmap_type, int size, const struct irq_domain_ops *ops, void *host_data) { struct irq_domain *domain; - domain = kzalloc_node(sizeof(*domain), GFP_KERNEL, - of_node_to_nid(of_node)); + domain = kzalloc_node(sizeof(*domain) + (sizeof(unsigned int) * size), + GFP_KERNEL, of_node_to_nid(of_node)); if (WARN_ON(!domain)) return NULL; /* Fill structure */ + INIT_RADIX_TREE(&domain->revmap_data.tree, GFP_KERNEL); domain->revmap_type = revmap_type; domain->ops = ops; domain->host_data = host_data; domain->of_node = of_node_get(of_node); + domain->revmap_data.linear.size = size; return domain; } @@ -81,22 +83,12 @@ void irq_domain_remove(struct irq_domain *domain) { mutex_lock(&irq_domain_mutex); - switch (domain->revmap_type) { - case IRQ_DOMAIN_MAP_TREE: - /* - * radix_tree_delete() takes care of destroying the root - * node when all entries are removed. Shout if there are - * any mappings left. - */ - WARN_ON(domain->revmap_data.tree.height); - break; - case IRQ_DOMAIN_MAP_LINEAR: - kfree(domain->revmap_data.linear.revmap); - domain->revmap_data.linear.size = 0; - break; - case IRQ_DOMAIN_MAP_NOMAP: - break; - } + /* + * radix_tree_delete() takes care of destroying the root + * node when all entries are removed. Shout if there are + * any mappings left. + */ + WARN_ON(domain->revmap_data.tree.height); list_del(&domain->link); @@ -223,20 +215,11 @@ struct irq_domain *irq_domain_add_linear(struct device_node *of_node, void *host_data) { struct irq_domain *domain; - unsigned int *revmap; - revmap = kzalloc_node(sizeof(*revmap) * size, GFP_KERNEL, - of_node_to_nid(of_node)); - if (WARN_ON(!revmap)) + domain = irq_domain_alloc(of_node, IRQ_DOMAIN_MAP_LINEAR, size, ops, host_data); + if (!domain) return NULL; - domain = irq_domain_alloc(of_node, IRQ_DOMAIN_MAP_LINEAR, ops, host_data); - if (!domain) { - kfree(revmap); - return NULL; - } - domain->revmap_data.linear.size = size; - domain->revmap_data.linear.revmap = revmap; irq_domain_add(domain); return domain; } @@ -248,7 +231,7 @@ struct irq_domain *irq_domain_add_nomap(struct device_node *of_node, void *host_data) { struct irq_domain *domain = irq_domain_alloc(of_node, - IRQ_DOMAIN_MAP_NOMAP, ops, host_data); + IRQ_DOMAIN_MAP_NOMAP, 0, ops, host_data); if (domain) { domain->revmap_data.nomap.max_irq = max_irq ? max_irq : ~0; irq_domain_add(domain); @@ -257,28 +240,6 @@ struct irq_domain *irq_domain_add_nomap(struct device_node *of_node, } EXPORT_SYMBOL_GPL(irq_domain_add_nomap); -/** - * irq_domain_add_tree() - * @of_node: pointer to interrupt controller's device tree node. - * @ops: map/unmap domain callbacks - * - * Note: The radix tree will be allocated later during boot automatically - * (the reverse mapping will use the slow path until that happens). - */ -struct irq_domain *irq_domain_add_tree(struct device_node *of_node, - const struct irq_domain_ops *ops, - void *host_data) -{ - struct irq_domain *domain = irq_domain_alloc(of_node, - IRQ_DOMAIN_MAP_TREE, ops, host_data); - if (domain) { - INIT_RADIX_TREE(&domain->revmap_data.tree, GFP_KERNEL); - irq_domain_add(domain); - } - return domain; -} -EXPORT_SYMBOL_GPL(irq_domain_add_tree); - /** * irq_find_host() - Locates a domain for a given device node * @node: device-tree node of the interrupt controller @@ -359,17 +320,13 @@ static void irq_domain_disassociate_many(struct irq_domain *domain, irq_data->domain = NULL; irq_data->hwirq = 0; - /* Clear reverse map */ - switch(domain->revmap_type) { - case IRQ_DOMAIN_MAP_LINEAR: - if (hwirq < domain->revmap_data.linear.size) - domain->revmap_data.linear.revmap[hwirq] = 0; - break; - case IRQ_DOMAIN_MAP_TREE: + /* Clear reverse map for this hwirq */ + if (hwirq < domain->revmap_data.linear.size) { + domain->linear_revmap[hwirq] = 0; + } else { mutex_lock(&revmap_trees_mutex); radix_tree_delete(&domain->revmap_data.tree, hwirq); mutex_unlock(&revmap_trees_mutex); - break; } } } @@ -421,16 +378,12 @@ int irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base, domain->name = irq_data->chip->name; } - switch (domain->revmap_type) { - case IRQ_DOMAIN_MAP_LINEAR: - if (hwirq < domain->revmap_data.linear.size) - domain->revmap_data.linear.revmap[hwirq] = virq; - break; - case IRQ_DOMAIN_MAP_TREE: + if (hwirq < domain->revmap_data.linear.size) { + domain->linear_revmap[hwirq] = virq; + } else { mutex_lock(&revmap_trees_mutex); radix_tree_insert(&domain->revmap_data.tree, hwirq, irq_data); mutex_unlock(&revmap_trees_mutex); - break; } irq_clear_status_flags(virq, IRQ_NOREQUEST); @@ -667,13 +620,6 @@ unsigned int irq_find_mapping(struct irq_domain *domain, switch (domain->revmap_type) { case IRQ_DOMAIN_MAP_LINEAR: return irq_linear_revmap(domain, hwirq); - case IRQ_DOMAIN_MAP_TREE: - rcu_read_lock(); - data = radix_tree_lookup(&domain->revmap_data.tree, hwirq); - rcu_read_unlock(); - if (data) - return data->irq; - break; case IRQ_DOMAIN_MAP_NOMAP: data = irq_get_irq_data(hwirq); if (data && (data->domain == domain) && (data->hwirq == hwirq)) @@ -696,13 +642,18 @@ EXPORT_SYMBOL_GPL(irq_find_mapping); unsigned int irq_linear_revmap(struct irq_domain *domain, irq_hw_number_t hwirq) { + struct irq_data *data; BUG_ON(domain->revmap_type != IRQ_DOMAIN_MAP_LINEAR); /* Check revmap bounds; complain if exceeded */ - if (WARN_ON(hwirq >= domain->revmap_data.linear.size)) - return 0; + if (hwirq >= domain->revmap_data.linear.size) { + rcu_read_lock(); + data = radix_tree_lookup(&domain->revmap_data.tree, hwirq); + rcu_read_unlock(); + return data ? data->irq : 0; + } - return domain->revmap_data.linear.revmap[hwirq]; + return domain->linear_revmap[hwirq]; } EXPORT_SYMBOL_GPL(irq_linear_revmap); -- cgit v1.2.3 From 1aa0dd94ca07df818cf14588c9031ab1d7fd84d3 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Sat, 8 Jun 2013 12:03:59 +0100 Subject: irqdomain: Eliminate revmap type The NOMAP irq_domain type is only used by a handful of interrupt controllers and it unnecessarily complicates the code by adding special cases on how to look up mappings and different revmap functions are used for each type which need to validate the correct type is passed to it before performing the reverse map. Eliminating the revmap_type and making a single reverse mapping function simplifies the code. It also shouldn't be any slower than having separate revmap functions because the type of the revmap needed to be checked anyway. The linear and tree revmap types were already merged in a previous patch. This patch rolls the NOMAP or direct mapping behaviour into the same domain code making is possible for an irq domain to do any mapping type; linear, tree or direct; and that the mapping will be transparent to the interrupt controller driver. With this change, direct mappings will get stored in the linear or tree mapping for consistency. Reverse mapping from the hwirq to virq will go through the normal lookup process. However, any controller using a direct mapping can take advantage of knowing that hwirq==virq for any mapped interrupts skip doing a revmap lookup when handling IRQs. Signed-off-by: Grant Likely --- include/linux/irqdomain.h | 48 +++++++++++++++++------------------------ kernel/irq/generic-chip.c | 5 +---- kernel/irq/irqdomain.c | 55 +++++++++++++++++++---------------------------- 3 files changed, 43 insertions(+), 65 deletions(-) diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index 1cbb7413c121..51ef84a3c990 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h @@ -73,50 +73,42 @@ struct irq_domain_chip_generic; /** * struct irq_domain - Hardware interrupt number translation object * @link: Element in global irq_domain list. - * @revmap_type: Method used for reverse mapping hwirq numbers to linux irq. This - * will be one of the IRQ_DOMAIN_MAP_* values. + * @name: Name of interrupt domain * @ops: pointer to irq_domain methods * @host_data: private data pointer for use by owner. Not touched by irq_domain * core code. - * @irq_base: Start of irq_desc range assigned to the irq_domain. The creator - * of the irq_domain is responsible for allocating the array of - * irq_desc structures. - * @nr_irq: Number of irqs managed by the irq domain - * @hwirq_base: Starting number for hwirqs managed by the irq domain - * @of_node: (optional) Pointer to device tree nodes associated with the - * irq_domain. Used when decoding device tree interrupt specifiers. + * + * Optional elements + * @of_node: Pointer to device tree nodes associated with the irq_domain. Used + * when decoding device tree interrupt specifiers. + * @gc: Pointer to a list of generic chips. There is a helper function for + * setting up one or more generic chips for interrupt controllers + * drivers using the generic chip library which uses this pointer. + * + * Revmap data, used internally by irq_domain + * @revmap_direct_max_irq: The largest hwirq that can be set for controllers that + * support direct mapping + * @revmap_size: Size of the linear map table @linear_revmap[] + * @revmap_tree: Radix map tree for hwirqs that don't fit in the linear map + * @linear_revmap: Linear table of hwirq->virq reverse mappings */ struct irq_domain { struct list_head link; const char *name; - - /* type of reverse mapping_technique */ - unsigned int revmap_type; - struct { - struct { - unsigned int size; - } linear; - struct { - unsigned int max_irq; - } nomap; - struct radix_tree_root tree; - } revmap_data; const struct irq_domain_ops *ops; void *host_data; - irq_hw_number_t inval_irq; - /* Optional device node pointer */ + /* Optional data */ struct device_node *of_node; - /* Optional pointer to generic interrupt chips */ struct irq_domain_chip_generic *gc; - /* Linear reverse map */ + /* reverse map data. The linear map gets appended to the irq_domain */ + unsigned int revmap_direct_max_irq; + unsigned int revmap_size; + struct radix_tree_root revmap_tree; unsigned int linear_revmap[]; }; -#define IRQ_DOMAIN_MAP_NOMAP 1 /* no fast reverse mapping */ -#define IRQ_DOMAIN_MAP_LINEAR 2 /* linear map of interrupts */ - #ifdef CONFIG_IRQ_DOMAIN struct irq_domain *irq_domain_add_simple(struct device_node *of_node, unsigned int size, diff --git a/kernel/irq/generic-chip.c b/kernel/irq/generic-chip.c index ca98cc5d6308..4b011064e146 100644 --- a/kernel/irq/generic-chip.c +++ b/kernel/irq/generic-chip.c @@ -270,10 +270,7 @@ int irq_alloc_domain_generic_chips(struct irq_domain *d, int irqs_per_chip, if (d->gc) return -EBUSY; - if (d->revmap_type != IRQ_DOMAIN_MAP_LINEAR) - return -EINVAL; - - numchips = d->revmap_data.linear.size / irqs_per_chip; + numchips = d->revmap_size / irqs_per_chip; if (!numchips) return -EINVAL; diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index 5a1d8ec8509e..c38be78fceb4 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -25,7 +25,6 @@ static struct irq_domain *irq_default_domain; /** * irq_domain_alloc() - Allocate a new irq_domain data structure * @of_node: optional device-tree node of the interrupt controller - * @revmap_type: type of reverse mapping to use * @ops: map/unmap domain callbacks * @host_data: Controller private data pointer * @@ -34,7 +33,7 @@ static struct irq_domain *irq_default_domain; * to IRQ domain, or NULL on failure. */ static struct irq_domain *irq_domain_alloc(struct device_node *of_node, - unsigned int revmap_type, int size, + int size, const struct irq_domain_ops *ops, void *host_data) { @@ -46,12 +45,11 @@ static struct irq_domain *irq_domain_alloc(struct device_node *of_node, return NULL; /* Fill structure */ - INIT_RADIX_TREE(&domain->revmap_data.tree, GFP_KERNEL); - domain->revmap_type = revmap_type; + INIT_RADIX_TREE(&domain->revmap_tree, GFP_KERNEL); domain->ops = ops; domain->host_data = host_data; domain->of_node = of_node_get(of_node); - domain->revmap_data.linear.size = size; + domain->revmap_size = size; return domain; } @@ -67,8 +65,7 @@ static void irq_domain_add(struct irq_domain *domain) mutex_lock(&irq_domain_mutex); list_add(&domain->link, &irq_domain_list); mutex_unlock(&irq_domain_mutex); - pr_debug("Allocated domain of type %d @0x%p\n", - domain->revmap_type, domain); + pr_debug("Added domain %s\n", domain->name); } /** @@ -88,7 +85,7 @@ void irq_domain_remove(struct irq_domain *domain) * node when all entries are removed. Shout if there are * any mappings left. */ - WARN_ON(domain->revmap_data.tree.height); + WARN_ON(domain->revmap_tree.height); list_del(&domain->link); @@ -100,8 +97,7 @@ void irq_domain_remove(struct irq_domain *domain) mutex_unlock(&irq_domain_mutex); - pr_debug("Removed domain of type %d @0x%p\n", - domain->revmap_type, domain); + pr_debug("Removed domain %s\n", domain->name); irq_domain_free(domain); } @@ -216,7 +212,7 @@ struct irq_domain *irq_domain_add_linear(struct device_node *of_node, { struct irq_domain *domain; - domain = irq_domain_alloc(of_node, IRQ_DOMAIN_MAP_LINEAR, size, ops, host_data); + domain = irq_domain_alloc(of_node, size, ops, host_data); if (!domain) return NULL; @@ -230,10 +226,9 @@ struct irq_domain *irq_domain_add_nomap(struct device_node *of_node, const struct irq_domain_ops *ops, void *host_data) { - struct irq_domain *domain = irq_domain_alloc(of_node, - IRQ_DOMAIN_MAP_NOMAP, 0, ops, host_data); + struct irq_domain *domain = irq_domain_alloc(of_node, 0, ops, host_data); if (domain) { - domain->revmap_data.nomap.max_irq = max_irq ? max_irq : ~0; + domain->revmap_direct_max_irq = max_irq ? max_irq : ~0; irq_domain_add(domain); } return domain; @@ -321,11 +316,11 @@ static void irq_domain_disassociate_many(struct irq_domain *domain, irq_data->hwirq = 0; /* Clear reverse map for this hwirq */ - if (hwirq < domain->revmap_data.linear.size) { + if (hwirq < domain->revmap_size) { domain->linear_revmap[hwirq] = 0; } else { mutex_lock(&revmap_trees_mutex); - radix_tree_delete(&domain->revmap_data.tree, hwirq); + radix_tree_delete(&domain->revmap_tree, hwirq); mutex_unlock(&revmap_trees_mutex); } } @@ -378,11 +373,11 @@ int irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base, domain->name = irq_data->chip->name; } - if (hwirq < domain->revmap_data.linear.size) { + if (hwirq < domain->revmap_size) { domain->linear_revmap[hwirq] = virq; } else { mutex_lock(&revmap_trees_mutex); - radix_tree_insert(&domain->revmap_data.tree, hwirq, irq_data); + radix_tree_insert(&domain->revmap_tree, hwirq, irq_data); mutex_unlock(&revmap_trees_mutex); } @@ -399,7 +394,9 @@ EXPORT_SYMBOL_GPL(irq_domain_associate_many); * * This routine is used for irq controllers which can choose the hardware * interrupt numbers they generate. In such a case it's simplest to use - * the linux irq as the hardware interrupt number. + * the linux irq as the hardware interrupt number. It still uses the linear + * or radix tree to store the mapping, but the irq controller can optimize + * the revmap path by using the hwirq directly. */ unsigned int irq_create_direct_mapping(struct irq_domain *domain) { @@ -408,17 +405,14 @@ unsigned int irq_create_direct_mapping(struct irq_domain *domain) if (domain == NULL) domain = irq_default_domain; - if (WARN_ON(!domain || domain->revmap_type != IRQ_DOMAIN_MAP_NOMAP)) - return 0; - virq = irq_alloc_desc_from(1, of_node_to_nid(domain->of_node)); if (!virq) { pr_debug("create_direct virq allocation failed\n"); return 0; } - if (virq >= domain->revmap_data.nomap.max_irq) { + if (virq >= domain->revmap_direct_max_irq) { pr_err("ERROR: no free irqs available below %i maximum\n", - domain->revmap_data.nomap.max_irq); + domain->revmap_direct_max_irq); irq_free_desc(virq); return 0; } @@ -617,17 +611,13 @@ unsigned int irq_find_mapping(struct irq_domain *domain, if (domain == NULL) return 0; - switch (domain->revmap_type) { - case IRQ_DOMAIN_MAP_LINEAR: - return irq_linear_revmap(domain, hwirq); - case IRQ_DOMAIN_MAP_NOMAP: + if (hwirq < domain->revmap_direct_max_irq) { data = irq_get_irq_data(hwirq); if (data && (data->domain == domain) && (data->hwirq == hwirq)) return hwirq; - break; } - return 0; + return irq_linear_revmap(domain, hwirq); } EXPORT_SYMBOL_GPL(irq_find_mapping); @@ -643,12 +633,11 @@ unsigned int irq_linear_revmap(struct irq_domain *domain, irq_hw_number_t hwirq) { struct irq_data *data; - BUG_ON(domain->revmap_type != IRQ_DOMAIN_MAP_LINEAR); /* Check revmap bounds; complain if exceeded */ - if (hwirq >= domain->revmap_data.linear.size) { + if (hwirq >= domain->revmap_size) { rcu_read_lock(); - data = radix_tree_lookup(&domain->revmap_data.tree, hwirq); + data = radix_tree_lookup(&domain->revmap_tree, hwirq); rcu_read_unlock(); return data ? data->irq : 0; } -- cgit v1.2.3 From fa40f377577752b83252b9d2b3165d4acee0eb7c Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Sat, 8 Jun 2013 12:57:40 +0100 Subject: irqdomain: Clean up aftermath of irq_domain refactoring After refactoring the irqdomain code, there are a number of API functions that are merely empty wrappers around core code. Drop those wrappers out of the C file and replace them with static inlines in the header. Signed-off-by: Grant Likely --- arch/powerpc/platforms/cell/beat_interrupt.c | 2 +- arch/powerpc/platforms/powermac/smp.c | 2 +- include/linux/irqdomain.h | 31 +++++-- kernel/irq/irqdomain.c | 127 ++++++++------------------- 4 files changed, 62 insertions(+), 100 deletions(-) diff --git a/arch/powerpc/platforms/cell/beat_interrupt.c b/arch/powerpc/platforms/cell/beat_interrupt.c index 8c6dc42ecf65..9e5dfbcc00af 100644 --- a/arch/powerpc/platforms/cell/beat_interrupt.c +++ b/arch/powerpc/platforms/cell/beat_interrupt.c @@ -239,7 +239,7 @@ void __init beatic_init_IRQ(void) ppc_md.get_irq = beatic_get_irq; /* Allocate an irq host */ - beatic_host = irq_domain_add_nomap(NULL, 0, &beatic_pic_host_ops, NULL); + beatic_host = irq_domain_add_nomap(NULL, ~0, &beatic_pic_host_ops, NULL); BUG_ON(beatic_host == NULL); irq_set_default_host(beatic_host); } diff --git a/arch/powerpc/platforms/powermac/smp.c b/arch/powerpc/platforms/powermac/smp.c index bdb738a69e41..f921067d924d 100644 --- a/arch/powerpc/platforms/powermac/smp.c +++ b/arch/powerpc/platforms/powermac/smp.c @@ -192,7 +192,7 @@ static int psurge_secondary_ipi_init(void) { int rc = -ENOMEM; - psurge_host = irq_domain_add_nomap(NULL, 0, &psurge_host_ops, NULL); + psurge_host = irq_domain_add_nomap(NULL, ~0, &psurge_host_ops, NULL); if (psurge_host) psurge_secondary_virq = irq_create_direct_mapping(psurge_host); diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index 51ef84a3c990..fd4b26f8f44c 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h @@ -110,6 +110,10 @@ struct irq_domain { }; #ifdef CONFIG_IRQ_DOMAIN +struct irq_domain *__irq_domain_add(struct device_node *of_node, + int size, int direct_max, + const struct irq_domain_ops *ops, + void *host_data); struct irq_domain *irq_domain_add_simple(struct device_node *of_node, unsigned int size, unsigned int first_irq, @@ -121,17 +125,30 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node, irq_hw_number_t first_hwirq, const struct irq_domain_ops *ops, void *host_data); -struct irq_domain *irq_domain_add_linear(struct device_node *of_node, +extern struct irq_domain *irq_find_host(struct device_node *node); +extern void irq_set_default_host(struct irq_domain *host); + +/** + * irq_domain_add_linear() - Allocate and register a linear revmap irq_domain. + * @of_node: pointer to interrupt controller's device tree node. + * @size: Number of interrupts in the domain. + * @ops: map/unmap domain callbacks + * @host_data: Controller private data pointer + */ +static inline struct irq_domain *irq_domain_add_linear(struct device_node *of_node, unsigned int size, const struct irq_domain_ops *ops, - void *host_data); -struct irq_domain *irq_domain_add_nomap(struct device_node *of_node, + void *host_data) +{ + return __irq_domain_add(of_node, size, 0, ops, host_data); +} +static inline struct irq_domain *irq_domain_add_nomap(struct device_node *of_node, unsigned int max_irq, const struct irq_domain_ops *ops, - void *host_data); -extern struct irq_domain *irq_find_host(struct device_node *node); -extern void irq_set_default_host(struct irq_domain *host); - + void *host_data) +{ + return __irq_domain_add(of_node, 0, max_irq, ops, host_data); +} static inline struct irq_domain *irq_domain_add_legacy_isa( struct device_node *of_node, const struct irq_domain_ops *ops, diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index c38be78fceb4..e0db59e2eef6 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -23,8 +23,11 @@ static DEFINE_MUTEX(revmap_trees_mutex); static struct irq_domain *irq_default_domain; /** - * irq_domain_alloc() - Allocate a new irq_domain data structure + * __irq_domain_add() - Allocate a new irq_domain data structure * @of_node: optional device-tree node of the interrupt controller + * @size: Size of linear map; 0 for radix mapping only + * @direct_max: Maximum value of direct maps; Use ~0 for no limit; 0 for no + * direct mapping * @ops: map/unmap domain callbacks * @host_data: Controller private data pointer * @@ -32,10 +35,10 @@ static struct irq_domain *irq_default_domain; * register allocated irq_domain with irq_domain_register(). Returns pointer * to IRQ domain, or NULL on failure. */ -static struct irq_domain *irq_domain_alloc(struct device_node *of_node, - int size, - const struct irq_domain_ops *ops, - void *host_data) +struct irq_domain *__irq_domain_add(struct device_node *of_node, + int size, int direct_max, + const struct irq_domain_ops *ops, + void *host_data) { struct irq_domain *domain; @@ -50,23 +53,16 @@ static struct irq_domain *irq_domain_alloc(struct device_node *of_node, domain->host_data = host_data; domain->of_node = of_node_get(of_node); domain->revmap_size = size; + domain->revmap_direct_max_irq = direct_max; - return domain; -} - -static void irq_domain_free(struct irq_domain *domain) -{ - of_node_put(domain->of_node); - kfree(domain); -} - -static void irq_domain_add(struct irq_domain *domain) -{ mutex_lock(&irq_domain_mutex); list_add(&domain->link, &irq_domain_list); mutex_unlock(&irq_domain_mutex); + pr_debug("Added domain %s\n", domain->name); + return domain; } +EXPORT_SYMBOL_GPL(__irq_domain_add); /** * irq_domain_remove() - Remove an irq domain. @@ -99,30 +95,28 @@ void irq_domain_remove(struct irq_domain *domain) pr_debug("Removed domain %s\n", domain->name); - irq_domain_free(domain); + of_node_put(domain->of_node); + kfree(domain); } EXPORT_SYMBOL_GPL(irq_domain_remove); /** - * irq_domain_add_simple() - Allocate and register a simple irq_domain. + * irq_domain_add_simple() - Register an irq_domain and optionally map a range of irqs * @of_node: pointer to interrupt controller's device tree node. * @size: total number of irqs in mapping * @first_irq: first number of irq block assigned to the domain, - * pass zero to assign irqs on-the-fly. This will result in a - * linear IRQ domain so it is important to use irq_create_mapping() - * for each used IRQ, especially when SPARSE_IRQ is enabled. + * pass zero to assign irqs on-the-fly. If first_irq is non-zero, then + * pre-map all of the irqs in the domain to virqs starting at first_irq. * @ops: map/unmap domain callbacks * @host_data: Controller private data pointer * - * Allocates a legacy irq_domain if irq_base is positive or a linear - * domain otherwise. For the legacy domain, IRQ descriptors will also - * be allocated. + * Allocates an irq_domain, and optionally if first_irq is positive then also + * allocate irq_descs and map all of the hwirqs to virqs starting at first_irq. * * This is intended to implement the expected behaviour for most - * interrupt controllers which is that a linear mapping should - * normally be used unless the system requires a legacy mapping in - * order to support supplying interrupt numbers during non-DT - * registration of devices. + * interrupt controllers. If device tree is used, then first_irq will be 0 and + * irqs get mapped dynamically on the fly. However, if the controller requires + * static virq assignments (non-DT boot) then it will set that up correctly. */ struct irq_domain *irq_domain_add_simple(struct device_node *of_node, unsigned int size, @@ -130,33 +124,25 @@ struct irq_domain *irq_domain_add_simple(struct device_node *of_node, const struct irq_domain_ops *ops, void *host_data) { - if (first_irq > 0) { - int irq_base; + struct irq_domain *domain; + + domain = __irq_domain_add(of_node, size, 0, ops, host_data); + if (!domain) + return NULL; + if (first_irq > 0) { if (IS_ENABLED(CONFIG_SPARSE_IRQ)) { - /* - * Set the descriptor allocator to search for a - * 1-to-1 mapping, such as irq_alloc_desc_at(). - * Use of_node_to_nid() which is defined to - * numa_node_id() on platforms that have no custom - * implementation. - */ - irq_base = irq_alloc_descs(first_irq, first_irq, size, - of_node_to_nid(of_node)); - if (irq_base < 0) { + /* attempt to allocated irq_descs */ + int rc = irq_alloc_descs(first_irq, first_irq, size, + of_node_to_nid(of_node)); + if (rc < 0) pr_info("Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n", first_irq); - irq_base = first_irq; - } - } else - irq_base = first_irq; - - return irq_domain_add_legacy(of_node, size, irq_base, 0, - ops, host_data); + } + WARN_ON(irq_domain_associate_many(domain, first_irq, 0, size)); } - /* A linear domain is the default */ - return irq_domain_add_linear(of_node, size, ops, host_data); + return domain; } EXPORT_SYMBOL_GPL(irq_domain_add_simple); @@ -184,11 +170,7 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node, { struct irq_domain *domain; - pr_debug("Setting up legacy domain virq[%i:%i] ==> hwirq[%i:%i]\n", - first_irq, first_irq + size - 1, - (int)first_hwirq, (int)first_hwirq + size -1); - - domain = irq_domain_add_linear(of_node, first_hwirq + size, ops, host_data); + domain = __irq_domain_add(of_node, first_hwirq + size, 0, ops, host_data); if (!domain) return NULL; @@ -198,43 +180,6 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node, } EXPORT_SYMBOL_GPL(irq_domain_add_legacy); -/** - * irq_domain_add_linear() - Allocate and register a linear revmap irq_domain. - * @of_node: pointer to interrupt controller's device tree node. - * @size: Number of interrupts in the domain. - * @ops: map/unmap domain callbacks - * @host_data: Controller private data pointer - */ -struct irq_domain *irq_domain_add_linear(struct device_node *of_node, - unsigned int size, - const struct irq_domain_ops *ops, - void *host_data) -{ - struct irq_domain *domain; - - domain = irq_domain_alloc(of_node, size, ops, host_data); - if (!domain) - return NULL; - - irq_domain_add(domain); - return domain; -} -EXPORT_SYMBOL_GPL(irq_domain_add_linear); - -struct irq_domain *irq_domain_add_nomap(struct device_node *of_node, - unsigned int max_irq, - const struct irq_domain_ops *ops, - void *host_data) -{ - struct irq_domain *domain = irq_domain_alloc(of_node, 0, ops, host_data); - if (domain) { - domain->revmap_direct_max_irq = max_irq ? max_irq : ~0; - irq_domain_add(domain); - } - return domain; -} -EXPORT_SYMBOL_GPL(irq_domain_add_nomap); - /** * irq_find_host() - Locates a domain for a given device node * @node: device-tree node of the interrupt controller -- cgit v1.2.3 From 1400ea86025a22862f97e7fe544433751b43ecec Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 6 Jun 2013 22:20:44 +0100 Subject: irqdomain: Beef up debugfs output This patch increases the amount of output produced by the irq_domain_mapping debugfs file by first listing all of the registered irq domains at the beginning of the output, and then by including all mapped IRQs in the output, not just the active ones. It is very useful when debugging irqdomain issues to be able to see the entire list of mapped irqs, not just the ones that happen to be connected to devices. Signed-off-by: Grant Likely --- kernel/irq/irqdomain.c | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index e0db59e2eef6..280b8047d8db 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -596,12 +596,29 @@ static int virq_debug_show(struct seq_file *m, void *private) { unsigned long flags; struct irq_desc *desc; - void *data; + struct irq_domain *domain; + struct radix_tree_iter iter; + void *data, **slot; int i; - seq_printf(m, "%-5s %-7s %-15s %-*s %s\n", "irq", "hwirq", + seq_printf(m, " %-16s %-6s %-10s %-10s %s\n", + "name", "mapped", "linear-max", "direct-max", "devtree-node"); + mutex_lock(&irq_domain_mutex); + list_for_each_entry(domain, &irq_domain_list, link) { + int count = 0; + radix_tree_for_each_slot(slot, &domain->revmap_tree, &iter, 0) + count++; + seq_printf(m, "%c%-16s %6u %10u %10u %s\n", + domain == irq_default_domain ? '*' : ' ', domain->name, + domain->revmap_size + count, domain->revmap_size, + domain->revmap_direct_max_irq, + domain->of_node ? of_node_full_name(domain->of_node) : ""); + } + mutex_unlock(&irq_domain_mutex); + + seq_printf(m, "%-5s %-7s %-15s %-*s %6s %-14s %s\n", "irq", "hwirq", "chip name", (int)(2 * sizeof(void *) + 2), "chip data", - "domain name"); + "active", "type", "domain"); for (i = 1; i < nr_irqs; i++) { desc = irq_to_desc(i); @@ -609,12 +626,15 @@ static int virq_debug_show(struct seq_file *m, void *private) continue; raw_spin_lock_irqsave(&desc->lock, flags); + domain = desc->irq_data.domain; - if (desc->action && desc->action->handler) { + if (domain) { struct irq_chip *chip; + int hwirq = desc->irq_data.hwirq; + bool direct; seq_printf(m, "%5d ", i); - seq_printf(m, "0x%05lx ", desc->irq_data.hwirq); + seq_printf(m, "0x%05x ", hwirq); chip = irq_desc_get_chip(desc); seq_printf(m, "%-15s ", (chip && chip->name) ? chip->name : "none"); @@ -622,6 +642,11 @@ static int virq_debug_show(struct seq_file *m, void *private) data = irq_desc_get_chip_data(desc); seq_printf(m, data ? "0x%p " : " %p ", data); + seq_printf(m, " %c ", (desc->action && desc->action->handler) ? '*' : ' '); + direct = (i == hwirq) && (i < domain->revmap_direct_max_irq); + seq_printf(m, "%6s%-8s ", + (hwirq < domain->revmap_size) ? "LINEAR" : "RADIX", + direct ? "(DIRECT)" : ""); seq_printf(m, "%s\n", desc->irq_data.domain->name); } -- cgit v1.2.3 From ad194288355a9e4b76c7404d3d5ecbeec8c532a9 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 10 Jun 2013 13:27:06 +0200 Subject: iwlwifi: mvm: remove iwl_mvm_dbgfs_set_fw_dbg_log declaration This function doesn't actually exist, remove its declaration. Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/mvm/mvm.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index 4e10aae71038..ad39a22f79fa 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -689,16 +689,11 @@ void iwl_mvm_bt_coex_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif); void iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif, struct iwl_beacon_filter_cmd *cmd); -int iwl_mvm_dbgfs_set_fw_dbg_log(struct iwl_mvm *mvm); #else static inline void iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif, struct iwl_beacon_filter_cmd *cmd) {} -static inline int iwl_mvm_dbgfs_set_fw_dbg_log(struct iwl_mvm *mvm) -{ - return 0; -} #endif int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, struct ieee80211_vif *vif); -- cgit v1.2.3 From 405010df1dd657151543d93b01578b0c533788b5 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Fri, 31 May 2013 19:59:20 +0800 Subject: xen-pciback: fix error return code in pcistub_irq_handler_switch() Fix to return -ENOENT in the pcistub_device_find() and pci_get_drvdata() error handling case instead of 0(overwrite to 0 by str_to_slot()), as done elsewhere in this function. Acked-by: Jan Beulich Signed-off-by: Wei Yongjun Signed-off-by: Konrad Rzeszutek Wilk --- drivers/xen/xen-pciback/pci_stub.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/xen/xen-pciback/pci_stub.c b/drivers/xen/xen-pciback/pci_stub.c index 4e8ba38aa0c9..002089918260 100644 --- a/drivers/xen/xen-pciback/pci_stub.c +++ b/drivers/xen/xen-pciback/pci_stub.c @@ -1196,19 +1196,23 @@ static ssize_t pcistub_irq_handler_switch(struct device_driver *drv, struct pcistub_device *psdev; struct xen_pcibk_dev_data *dev_data; int domain, bus, slot, func; - int err = -ENOENT; + int err; err = str_to_slot(buf, &domain, &bus, &slot, &func); if (err) return err; psdev = pcistub_device_find(domain, bus, slot, func); - if (!psdev) + if (!psdev) { + err = -ENOENT; goto out; + } dev_data = pci_get_drvdata(psdev->dev); - if (!dev_data) + if (!dev_data) { + err = -ENOENT; goto out; + } dev_dbg(&psdev->dev->dev, "%s fake irq handler: %d->%d\n", dev_data->irq_name, dev_data->isr_on, -- cgit v1.2.3 From 53b94fdc8fa0ccd88f97b72a6149672d7ddc0c50 Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Tue, 4 Jun 2013 16:31:34 -0400 Subject: xen/smp: Coalesce the free_irq calls in one function. There are two functions that do a bunch of 'free_irq' on the per_cpu IRQ. Instead of having duplicate code just move it to one function. This is just code movement. Signed-off-by: Konrad Rzeszutek Wilk --- arch/x86/xen/smp.c | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/arch/x86/xen/smp.c b/arch/x86/xen/smp.c index fb44426fe931..19fc9f39e9cc 100644 --- a/arch/x86/xen/smp.c +++ b/arch/x86/xen/smp.c @@ -98,6 +98,23 @@ static void __cpuinit cpu_bringup_and_idle(void) cpu_startup_entry(CPUHP_ONLINE); } +static void xen_smp_intr_free(unsigned int cpu) +{ + if (per_cpu(xen_resched_irq, cpu) >= 0) + unbind_from_irqhandler(per_cpu(xen_resched_irq, cpu), NULL); + if (per_cpu(xen_callfunc_irq, cpu) >= 0) + unbind_from_irqhandler(per_cpu(xen_callfunc_irq, cpu), NULL); + if (per_cpu(xen_debug_irq, cpu) >= 0) + unbind_from_irqhandler(per_cpu(xen_debug_irq, cpu), NULL); + if (per_cpu(xen_callfuncsingle_irq, cpu) >= 0) + unbind_from_irqhandler(per_cpu(xen_callfuncsingle_irq, cpu), + NULL); + if (xen_hvm_domain()) + return; + + if (per_cpu(xen_irq_work, cpu) >= 0) + unbind_from_irqhandler(per_cpu(xen_irq_work, cpu), NULL); +}; static int xen_smp_intr_init(unsigned int cpu) { int rc; @@ -165,21 +182,7 @@ static int xen_smp_intr_init(unsigned int cpu) return 0; fail: - if (per_cpu(xen_resched_irq, cpu) >= 0) - unbind_from_irqhandler(per_cpu(xen_resched_irq, cpu), NULL); - if (per_cpu(xen_callfunc_irq, cpu) >= 0) - unbind_from_irqhandler(per_cpu(xen_callfunc_irq, cpu), NULL); - if (per_cpu(xen_debug_irq, cpu) >= 0) - unbind_from_irqhandler(per_cpu(xen_debug_irq, cpu), NULL); - if (per_cpu(xen_callfuncsingle_irq, cpu) >= 0) - unbind_from_irqhandler(per_cpu(xen_callfuncsingle_irq, cpu), - NULL); - if (xen_hvm_domain()) - return rc; - - if (per_cpu(xen_irq_work, cpu) >= 0) - unbind_from_irqhandler(per_cpu(xen_irq_work, cpu), NULL); - + xen_smp_intr_free(cpu); return rc; } @@ -432,12 +435,7 @@ static void xen_cpu_die(unsigned int cpu) current->state = TASK_UNINTERRUPTIBLE; schedule_timeout(HZ/10); } - unbind_from_irqhandler(per_cpu(xen_resched_irq, cpu), NULL); - unbind_from_irqhandler(per_cpu(xen_callfunc_irq, cpu), NULL); - unbind_from_irqhandler(per_cpu(xen_debug_irq, cpu), NULL); - unbind_from_irqhandler(per_cpu(xen_callfuncsingle_irq, cpu), NULL); - if (!xen_hvm_domain()) - unbind_from_irqhandler(per_cpu(xen_irq_work, cpu), NULL); + xen_smp_intr_free(cpu); xen_uninit_lock_cpu(cpu); xen_teardown_timer(cpu); } -- cgit v1.2.3 From 9547689fcdf0b223967edcbbe588d9f0489ee5aa Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Tue, 4 Jun 2013 16:37:44 -0400 Subject: xen/smp: Introduce a common structure to contain the IRQ name and interrupt line. This patch adds a new structure to contain the common two things that each of the per-cpu interrupts need: - an interrupt number, - and the name of the interrupt (to be added in 'xen/smp: Don't leak interrupt name when offlining'). This allows us to carry the tuple of the per-cpu interrupt data structure and expand it as we need in the future. Signed-off-by: Konrad Rzeszutek Wilk --- arch/x86/xen/smp.c | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/arch/x86/xen/smp.c b/arch/x86/xen/smp.c index 19fc9f39e9cc..f5b29ecdf18d 100644 --- a/arch/x86/xen/smp.c +++ b/arch/x86/xen/smp.c @@ -39,11 +39,15 @@ cpumask_var_t xen_cpu_initialized_map; -static DEFINE_PER_CPU(int, xen_resched_irq); -static DEFINE_PER_CPU(int, xen_callfunc_irq); -static DEFINE_PER_CPU(int, xen_callfuncsingle_irq); -static DEFINE_PER_CPU(int, xen_irq_work); -static DEFINE_PER_CPU(int, xen_debug_irq) = -1; +struct xen_common_irq { + int irq; + char *name; +}; +static DEFINE_PER_CPU(struct xen_common_irq, xen_resched_irq); +static DEFINE_PER_CPU(struct xen_common_irq, xen_callfunc_irq); +static DEFINE_PER_CPU(struct xen_common_irq, xen_callfuncsingle_irq); +static DEFINE_PER_CPU(struct xen_common_irq, xen_irq_work); +static DEFINE_PER_CPU(struct xen_common_irq, xen_debug_irq) = { .irq = -1 }; static irqreturn_t xen_call_function_interrupt(int irq, void *dev_id); static irqreturn_t xen_call_function_single_interrupt(int irq, void *dev_id); @@ -100,20 +104,20 @@ static void __cpuinit cpu_bringup_and_idle(void) static void xen_smp_intr_free(unsigned int cpu) { - if (per_cpu(xen_resched_irq, cpu) >= 0) - unbind_from_irqhandler(per_cpu(xen_resched_irq, cpu), NULL); - if (per_cpu(xen_callfunc_irq, cpu) >= 0) - unbind_from_irqhandler(per_cpu(xen_callfunc_irq, cpu), NULL); - if (per_cpu(xen_debug_irq, cpu) >= 0) - unbind_from_irqhandler(per_cpu(xen_debug_irq, cpu), NULL); - if (per_cpu(xen_callfuncsingle_irq, cpu) >= 0) - unbind_from_irqhandler(per_cpu(xen_callfuncsingle_irq, cpu), + if (per_cpu(xen_resched_irq, cpu).irq >= 0) + unbind_from_irqhandler(per_cpu(xen_resched_irq, cpu).irq, NULL); + if (per_cpu(xen_callfunc_irq, cpu).irq >= 0) + unbind_from_irqhandler(per_cpu(xen_callfunc_irq, cpu).irq, NULL); + if (per_cpu(xen_debug_irq, cpu).irq >= 0) + unbind_from_irqhandler(per_cpu(xen_debug_irq, cpu).irq, NULL); + if (per_cpu(xen_callfuncsingle_irq, cpu).irq >= 0) + unbind_from_irqhandler(per_cpu(xen_callfuncsingle_irq, cpu).irq, NULL); if (xen_hvm_domain()) return; - if (per_cpu(xen_irq_work, cpu) >= 0) - unbind_from_irqhandler(per_cpu(xen_irq_work, cpu), NULL); + if (per_cpu(xen_irq_work, cpu).irq >= 0) + unbind_from_irqhandler(per_cpu(xen_irq_work, cpu).irq, NULL); }; static int xen_smp_intr_init(unsigned int cpu) { @@ -129,7 +133,7 @@ static int xen_smp_intr_init(unsigned int cpu) NULL); if (rc < 0) goto fail; - per_cpu(xen_resched_irq, cpu) = rc; + per_cpu(xen_resched_irq, cpu).irq = rc; callfunc_name = kasprintf(GFP_KERNEL, "callfunc%d", cpu); rc = bind_ipi_to_irqhandler(XEN_CALL_FUNCTION_VECTOR, @@ -140,7 +144,7 @@ static int xen_smp_intr_init(unsigned int cpu) NULL); if (rc < 0) goto fail; - per_cpu(xen_callfunc_irq, cpu) = rc; + per_cpu(xen_callfunc_irq, cpu).irq = rc; debug_name = kasprintf(GFP_KERNEL, "debug%d", cpu); rc = bind_virq_to_irqhandler(VIRQ_DEBUG, cpu, xen_debug_interrupt, @@ -148,7 +152,7 @@ static int xen_smp_intr_init(unsigned int cpu) debug_name, NULL); if (rc < 0) goto fail; - per_cpu(xen_debug_irq, cpu) = rc; + per_cpu(xen_debug_irq, cpu).irq = rc; callfunc_name = kasprintf(GFP_KERNEL, "callfuncsingle%d", cpu); rc = bind_ipi_to_irqhandler(XEN_CALL_FUNCTION_SINGLE_VECTOR, @@ -159,7 +163,7 @@ static int xen_smp_intr_init(unsigned int cpu) NULL); if (rc < 0) goto fail; - per_cpu(xen_callfuncsingle_irq, cpu) = rc; + per_cpu(xen_callfuncsingle_irq, cpu).irq = rc; /* * The IRQ worker on PVHVM goes through the native path and uses the @@ -177,7 +181,7 @@ static int xen_smp_intr_init(unsigned int cpu) NULL); if (rc < 0) goto fail; - per_cpu(xen_irq_work, cpu) = rc; + per_cpu(xen_irq_work, cpu).irq = rc; return 0; -- cgit v1.2.3 From ee336e10d5650d408efb66f634d462b9eb39c191 Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Tue, 4 Jun 2013 16:42:29 -0400 Subject: xen/smp: Set the per-cpu IRQ number to a valid default. When we free it we want to make sure to set it to a default value of -1 so that we don't double-free it (in case somebody calls us twice). Signed-off-by: Konrad Rzeszutek Wilk --- arch/x86/xen/smp.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/arch/x86/xen/smp.c b/arch/x86/xen/smp.c index f5b29ecdf18d..6a483cdd28c9 100644 --- a/arch/x86/xen/smp.c +++ b/arch/x86/xen/smp.c @@ -43,10 +43,10 @@ struct xen_common_irq { int irq; char *name; }; -static DEFINE_PER_CPU(struct xen_common_irq, xen_resched_irq); -static DEFINE_PER_CPU(struct xen_common_irq, xen_callfunc_irq); -static DEFINE_PER_CPU(struct xen_common_irq, xen_callfuncsingle_irq); -static DEFINE_PER_CPU(struct xen_common_irq, xen_irq_work); +static DEFINE_PER_CPU(struct xen_common_irq, xen_resched_irq) = { .irq = -1 }; +static DEFINE_PER_CPU(struct xen_common_irq, xen_callfunc_irq) = { .irq = -1 }; +static DEFINE_PER_CPU(struct xen_common_irq, xen_callfuncsingle_irq) = { .irq = -1 }; +static DEFINE_PER_CPU(struct xen_common_irq, xen_irq_work) = { .irq = -1 }; static DEFINE_PER_CPU(struct xen_common_irq, xen_debug_irq) = { .irq = -1 }; static irqreturn_t xen_call_function_interrupt(int irq, void *dev_id); @@ -104,20 +104,30 @@ static void __cpuinit cpu_bringup_and_idle(void) static void xen_smp_intr_free(unsigned int cpu) { - if (per_cpu(xen_resched_irq, cpu).irq >= 0) + if (per_cpu(xen_resched_irq, cpu).irq >= 0) { unbind_from_irqhandler(per_cpu(xen_resched_irq, cpu).irq, NULL); - if (per_cpu(xen_callfunc_irq, cpu).irq >= 0) + per_cpu(xen_resched_irq, cpu).irq = -1; + } + if (per_cpu(xen_callfunc_irq, cpu).irq >= 0) { unbind_from_irqhandler(per_cpu(xen_callfunc_irq, cpu).irq, NULL); - if (per_cpu(xen_debug_irq, cpu).irq >= 0) + per_cpu(xen_callfunc_irq, cpu).irq = -1; + } + if (per_cpu(xen_debug_irq, cpu).irq >= 0) { unbind_from_irqhandler(per_cpu(xen_debug_irq, cpu).irq, NULL); - if (per_cpu(xen_callfuncsingle_irq, cpu).irq >= 0) + per_cpu(xen_debug_irq, cpu).irq = -1; + } + if (per_cpu(xen_callfuncsingle_irq, cpu).irq >= 0) { unbind_from_irqhandler(per_cpu(xen_callfuncsingle_irq, cpu).irq, NULL); + per_cpu(xen_callfuncsingle_irq, cpu).irq = -1; + } if (xen_hvm_domain()) return; - if (per_cpu(xen_irq_work, cpu).irq >= 0) + if (per_cpu(xen_irq_work, cpu).irq >= 0) { unbind_from_irqhandler(per_cpu(xen_irq_work, cpu).irq, NULL); + per_cpu(xen_irq_work, cpu).irq = -1; + } }; static int xen_smp_intr_init(unsigned int cpu) { -- cgit v1.2.3 From b85fffec7f5ba1c43171c63c046a97bac30a4561 Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Tue, 4 Jun 2013 16:47:17 -0400 Subject: xen/smp: Don't leak interrupt name when offlining. When the user does: echo 0 > /sys/devices/system/cpu/cpu1/online echo 1 > /sys/devices/system/cpu/cpu1/online kmemleak reports: kmemleak: 7 new suspected memory leaks (see /sys/kernel/debug/kmemleak) unreferenced object 0xffff88003fa51240 (size 32): comm "swapper/0", pid 1, jiffies 4294667339 (age 1027.789s) hex dump (first 32 bytes): 72 65 73 63 68 65 64 31 00 00 00 00 00 00 00 00 resched1........ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ backtrace: [] kmemleak_alloc+0x21/0x50 [] __kmalloc_track_caller+0xec/0x2a0 [] kvasprintf+0x5b/0x90 [] kasprintf+0x38/0x40 [] xen_smp_intr_init+0x41/0x2c0 [] xen_cpu_up+0x393/0x3e8 [] _cpu_up+0xd1/0x14b [] cpu_up+0xd9/0xec [] smp_init+0x4b/0xa3 [] kernel_init_freeable+0xdb/0x1e6 [] kernel_init+0x9/0xf0 [] ret_from_fork+0x7c/0xb0 [] 0xffffffffffffffff This patch fixes some of it by using the 'struct xen_common_irq->name' field to stash away the char so that it can be freed when the interrupt line is destroyed. Signed-off-by: Konrad Rzeszutek Wilk --- arch/x86/xen/smp.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/arch/x86/xen/smp.c b/arch/x86/xen/smp.c index 6a483cdd28c9..37fbe71795c1 100644 --- a/arch/x86/xen/smp.c +++ b/arch/x86/xen/smp.c @@ -107,19 +107,27 @@ static void xen_smp_intr_free(unsigned int cpu) if (per_cpu(xen_resched_irq, cpu).irq >= 0) { unbind_from_irqhandler(per_cpu(xen_resched_irq, cpu).irq, NULL); per_cpu(xen_resched_irq, cpu).irq = -1; + kfree(per_cpu(xen_resched_irq, cpu).name); + per_cpu(xen_resched_irq, cpu).name = NULL; } if (per_cpu(xen_callfunc_irq, cpu).irq >= 0) { unbind_from_irqhandler(per_cpu(xen_callfunc_irq, cpu).irq, NULL); per_cpu(xen_callfunc_irq, cpu).irq = -1; + kfree(per_cpu(xen_callfunc_irq, cpu).name); + per_cpu(xen_callfunc_irq, cpu).name = NULL; } if (per_cpu(xen_debug_irq, cpu).irq >= 0) { unbind_from_irqhandler(per_cpu(xen_debug_irq, cpu).irq, NULL); per_cpu(xen_debug_irq, cpu).irq = -1; + kfree(per_cpu(xen_debug_irq, cpu).name); + per_cpu(xen_debug_irq, cpu).name = NULL; } if (per_cpu(xen_callfuncsingle_irq, cpu).irq >= 0) { unbind_from_irqhandler(per_cpu(xen_callfuncsingle_irq, cpu).irq, NULL); per_cpu(xen_callfuncsingle_irq, cpu).irq = -1; + kfree(per_cpu(xen_callfuncsingle_irq, cpu).name); + per_cpu(xen_callfuncsingle_irq, cpu).name = NULL; } if (xen_hvm_domain()) return; @@ -127,12 +135,14 @@ static void xen_smp_intr_free(unsigned int cpu) if (per_cpu(xen_irq_work, cpu).irq >= 0) { unbind_from_irqhandler(per_cpu(xen_irq_work, cpu).irq, NULL); per_cpu(xen_irq_work, cpu).irq = -1; + kfree(per_cpu(xen_irq_work, cpu).name); + per_cpu(xen_irq_work, cpu).name = NULL; } }; static int xen_smp_intr_init(unsigned int cpu) { int rc; - const char *resched_name, *callfunc_name, *debug_name; + char *resched_name, *callfunc_name, *debug_name; resched_name = kasprintf(GFP_KERNEL, "resched%d", cpu); rc = bind_ipi_to_irqhandler(XEN_RESCHEDULE_VECTOR, @@ -144,6 +154,7 @@ static int xen_smp_intr_init(unsigned int cpu) if (rc < 0) goto fail; per_cpu(xen_resched_irq, cpu).irq = rc; + per_cpu(xen_resched_irq, cpu).name = resched_name; callfunc_name = kasprintf(GFP_KERNEL, "callfunc%d", cpu); rc = bind_ipi_to_irqhandler(XEN_CALL_FUNCTION_VECTOR, @@ -155,6 +166,7 @@ static int xen_smp_intr_init(unsigned int cpu) if (rc < 0) goto fail; per_cpu(xen_callfunc_irq, cpu).irq = rc; + per_cpu(xen_callfunc_irq, cpu).name = callfunc_name; debug_name = kasprintf(GFP_KERNEL, "debug%d", cpu); rc = bind_virq_to_irqhandler(VIRQ_DEBUG, cpu, xen_debug_interrupt, @@ -163,6 +175,7 @@ static int xen_smp_intr_init(unsigned int cpu) if (rc < 0) goto fail; per_cpu(xen_debug_irq, cpu).irq = rc; + per_cpu(xen_debug_irq, cpu).name = debug_name; callfunc_name = kasprintf(GFP_KERNEL, "callfuncsingle%d", cpu); rc = bind_ipi_to_irqhandler(XEN_CALL_FUNCTION_SINGLE_VECTOR, @@ -174,6 +187,7 @@ static int xen_smp_intr_init(unsigned int cpu) if (rc < 0) goto fail; per_cpu(xen_callfuncsingle_irq, cpu).irq = rc; + per_cpu(xen_callfuncsingle_irq, cpu).name = callfunc_name; /* * The IRQ worker on PVHVM goes through the native path and uses the @@ -192,6 +206,7 @@ static int xen_smp_intr_init(unsigned int cpu) if (rc < 0) goto fail; per_cpu(xen_irq_work, cpu).irq = rc; + per_cpu(xen_irq_work, cpu).name = callfunc_name; return 0; -- cgit v1.2.3 From 354e7b761992a8e3923badaf705c4acbb9d5659d Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Wed, 5 Jun 2013 10:44:47 -0400 Subject: xen/spinlock: Don't leak interrupt name when offlining. When the user does: echo 0 > /sys/devices/system/cpu/cpu1/online echo 1 > /sys/devices/system/cpu/cpu1/online kmemleak reports: kmemleak: 7 new suspected memory leaks (see /sys/kernel/debug/kmemleak) unreferenced object 0xffff88003fa51260 (size 32): comm "swapper/0", pid 1, jiffies 4294667339 (age 1027.789s) hex dump (first 32 bytes): 73 70 69 6e 6c 6f 63 6b 31 00 00 00 00 00 00 00 spinlock1....... 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ backtrace: [] kmemleak_alloc+0x21/0x50 [] __kmalloc_track_caller+0xec/0x2a0 [] kvasprintf+0x5b/0x90 [] kasprintf+0x38/0x40 [] xen_init_lock_cpu+0x61/0xbe [] xen_cpu_up+0x66/0x3e8 [] _cpu_up+0xd1/0x14b [] cpu_up+0xd9/0xec [] smp_init+0x4b/0xa3 [] kernel_init_freeable+0xdb/0x1e6 [] kernel_init+0x9/0xf0 [] ret_from_fork+0x7c/0xb0 [] 0xffffffffffffffff Instead of doing it like the "xen/smp: Don't leak interrupt name when offlining" patch did (which has a per-cpu structure which contains both the IRQ number and char*) we use a per-cpu pointers to a *char. The reason is that the "__this_cpu_read(lock_kicker_irq);" macro blows up with "__bad_size_call_parameter()" as the size of the returned structure is not within the parameters of what it expects and optimizes for. Signed-off-by: Konrad Rzeszutek Wilk --- arch/x86/xen/spinlock.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/x86/xen/spinlock.c b/arch/x86/xen/spinlock.c index 3002ec1bb71a..a40f8508e760 100644 --- a/arch/x86/xen/spinlock.c +++ b/arch/x86/xen/spinlock.c @@ -7,6 +7,7 @@ #include #include #include +#include #include @@ -165,6 +166,7 @@ static int xen_spin_trylock(struct arch_spinlock *lock) return old == 0; } +static DEFINE_PER_CPU(char *, irq_name); static DEFINE_PER_CPU(int, lock_kicker_irq) = -1; static DEFINE_PER_CPU(struct xen_spinlock *, lock_spinners); @@ -362,7 +364,7 @@ static irqreturn_t dummy_handler(int irq, void *dev_id) void __cpuinit xen_init_lock_cpu(int cpu) { int irq; - const char *name; + char *name; WARN(per_cpu(lock_kicker_irq, cpu) >= 0, "spinlock on CPU%d exists on IRQ%d!\n", cpu, per_cpu(lock_kicker_irq, cpu)); @@ -385,6 +387,7 @@ void __cpuinit xen_init_lock_cpu(int cpu) if (irq >= 0) { disable_irq(irq); /* make sure it's never delivered */ per_cpu(lock_kicker_irq, cpu) = irq; + per_cpu(irq_name, cpu) = name; } printk("cpu %d spinlock event irq %d\n", cpu, irq); @@ -401,6 +404,8 @@ void xen_uninit_lock_cpu(int cpu) unbind_from_irqhandler(per_cpu(lock_kicker_irq, cpu), NULL); per_cpu(lock_kicker_irq, cpu) = -1; + kfree(per_cpu(irq_name, cpu)); + per_cpu(irq_name, cpu) = NULL; } void __init xen_init_spinlocks(void) -- cgit v1.2.3 From 31620a198cf6891dfdf5477607621da9aa092380 Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Tue, 4 Jun 2013 17:06:36 -0400 Subject: xen/time: Encapsulate the struct clock_event_device in another structure. We don't do any code movement. We just encapsulate the struct clock_event_device in a new structure which contains said structure and a pointer to a char *name. The 'name' will be used in 'xen/time: Don't leak interrupt name when offlining'. Signed-off-by: Konrad Rzeszutek Wilk --- arch/x86/xen/time.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/arch/x86/xen/time.c b/arch/x86/xen/time.c index 3d88bfdf9e1c..5190687ca569 100644 --- a/arch/x86/xen/time.c +++ b/arch/x86/xen/time.c @@ -377,11 +377,16 @@ static const struct clock_event_device xen_vcpuop_clockevent = { static const struct clock_event_device *xen_clockevent = &xen_timerop_clockevent; -static DEFINE_PER_CPU(struct clock_event_device, xen_clock_events) = { .irq = -1 }; + +struct xen_clock_event_device { + struct clock_event_device evt; + char *name; +}; +static DEFINE_PER_CPU(struct xen_clock_event_device, xen_clock_events) = { .evt.irq = -1 }; static irqreturn_t xen_timer_interrupt(int irq, void *dev_id) { - struct clock_event_device *evt = &__get_cpu_var(xen_clock_events); + struct clock_event_device *evt = &__get_cpu_var(xen_clock_events).evt; irqreturn_t ret; ret = IRQ_NONE; @@ -401,7 +406,7 @@ void xen_setup_timer(int cpu) struct clock_event_device *evt; int irq; - evt = &per_cpu(xen_clock_events, cpu); + evt = &per_cpu(xen_clock_events, cpu).evt; WARN(evt->irq >= 0, "IRQ%d for CPU%d is already allocated\n", evt->irq, cpu); printk(KERN_INFO "installing Xen timer for CPU %d\n", cpu); @@ -426,7 +431,7 @@ void xen_teardown_timer(int cpu) { struct clock_event_device *evt; BUG_ON(cpu == 0); - evt = &per_cpu(xen_clock_events, cpu); + evt = &per_cpu(xen_clock_events, cpu).evt; unbind_from_irqhandler(evt->irq, NULL); evt->irq = -1; } @@ -435,7 +440,7 @@ void xen_setup_cpu_clockevents(void) { BUG_ON(preemptible()); - clockevents_register_device(&__get_cpu_var(xen_clock_events)); + clockevents_register_device(&__get_cpu_var(xen_clock_events).evt); } void xen_timer_resume(void) -- cgit v1.2.3 From c9d76a24a28917c1ef6833f40c4ceff2e81b3ebb Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Tue, 4 Jun 2013 17:09:36 -0400 Subject: xen/time: Don't leak interrupt name when offlining. When the user does: echo 0 > /sys/devices/system/cpu/cpu1/online echo 1 > /sys/devices/system/cpu/cpu1/online kmemleak reports: kmemleak: 7 new suspected memory leaks (see /sys/kernel/debug/kmemleak) One of the leaks is from xen/time: unreferenced object 0xffff88003fa51280 (size 32): comm "swapper/0", pid 1, jiffies 4294667339 (age 1027.789s) hex dump (first 32 bytes): 74 69 6d 65 72 31 00 00 00 00 00 00 00 00 00 00 timer1.......... 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ backtrace: [] kmemleak_alloc+0x21/0x50 [] __kmalloc_track_caller+0xec/0x2a0 [] kvasprintf+0x5b/0x90 [] kasprintf+0x38/0x40 [] xen_setup_timer+0x51/0xf0 [] xen_cpu_up+0x5f/0x3e8 [] _cpu_up+0xd1/0x14b [] cpu_up+0xd9/0xec [] smp_init+0x4b/0xa3 [] kernel_init_freeable+0xdb/0x1e6 [] kernel_init+0x9/0xf0 [] ret_from_fork+0x7c/0xb0 [] 0xffffffffffffffff This patch fixes it by stashing away the 'name' in the per-cpu data structure and freeing it when offlining the CPU. Signed-off-by: Konrad Rzeszutek Wilk --- arch/x86/xen/time.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/x86/xen/time.c b/arch/x86/xen/time.c index 5190687ca569..011f1bf85765 100644 --- a/arch/x86/xen/time.c +++ b/arch/x86/xen/time.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -402,7 +403,7 @@ static irqreturn_t xen_timer_interrupt(int irq, void *dev_id) void xen_setup_timer(int cpu) { - const char *name; + char *name; struct clock_event_device *evt; int irq; @@ -425,6 +426,7 @@ void xen_setup_timer(int cpu) evt->cpumask = cpumask_of(cpu); evt->irq = irq; + per_cpu(xen_clock_events, cpu).name = name; } void xen_teardown_timer(int cpu) @@ -434,6 +436,8 @@ void xen_teardown_timer(int cpu) evt = &per_cpu(xen_clock_events, cpu).evt; unbind_from_irqhandler(evt->irq, NULL); evt->irq = -1; + kfree(per_cpu(xen_clock_events, cpu).name); + per_cpu(xen_clock_events, cpu).name = NULL; } void xen_setup_cpu_clockevents(void) -- cgit v1.2.3 From a05e2c371fbe73403793d126ceab93787cb4afd4 Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Tue, 4 Jun 2013 17:11:52 -0400 Subject: xen/time: Check that the per_cpu data structure has data before freeing. We don't check whether the per_cpu data structure has actually been freed in the past. This checks it and if it has been freed in the past then just continues on without double-freeing. Signed-off-by: Konrad Rzeszutek Wilk --- arch/x86/xen/time.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/arch/x86/xen/time.c b/arch/x86/xen/time.c index 011f1bf85765..6a56ae092994 100644 --- a/arch/x86/xen/time.c +++ b/arch/x86/xen/time.c @@ -434,10 +434,13 @@ void xen_teardown_timer(int cpu) struct clock_event_device *evt; BUG_ON(cpu == 0); evt = &per_cpu(xen_clock_events, cpu).evt; - unbind_from_irqhandler(evt->irq, NULL); - evt->irq = -1; - kfree(per_cpu(xen_clock_events, cpu).name); - per_cpu(xen_clock_events, cpu).name = NULL; + + if (evt->irq >= 0) { + unbind_from_irqhandler(evt->irq, NULL); + evt->irq = -1; + kfree(per_cpu(xen_clock_events, cpu).name); + per_cpu(xen_clock_events, cpu).name = NULL; + } } void xen_setup_cpu_clockevents(void) -- cgit v1.2.3 From 09e99da766a6a701eb4d72004872d1144291d53b Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Tue, 4 Jun 2013 17:13:29 -0400 Subject: xen/time: Free onlined per-cpu data structure if we want to online it again. If the per-cpu time data structure has been onlined already and we are trying to online it again, then free the previous copy before blindly over-writting it. A developer naturally should not call this function multiple times but just in case. Signed-off-by: Konrad Rzeszutek Wilk --- arch/x86/xen/time.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/arch/x86/xen/time.c b/arch/x86/xen/time.c index 6a56ae092994..aec0b14b6d76 100644 --- a/arch/x86/xen/time.c +++ b/arch/x86/xen/time.c @@ -401,6 +401,20 @@ static irqreturn_t xen_timer_interrupt(int irq, void *dev_id) return ret; } +void xen_teardown_timer(int cpu) +{ + struct clock_event_device *evt; + BUG_ON(cpu == 0); + evt = &per_cpu(xen_clock_events, cpu).evt; + + if (evt->irq >= 0) { + unbind_from_irqhandler(evt->irq, NULL); + evt->irq = -1; + kfree(per_cpu(xen_clock_events, cpu).name); + per_cpu(xen_clock_events, cpu).name = NULL; + } +} + void xen_setup_timer(int cpu) { char *name; @@ -409,6 +423,8 @@ void xen_setup_timer(int cpu) evt = &per_cpu(xen_clock_events, cpu).evt; WARN(evt->irq >= 0, "IRQ%d for CPU%d is already allocated\n", evt->irq, cpu); + if (evt->irq >= 0) + xen_teardown_timer(cpu); printk(KERN_INFO "installing Xen timer for CPU %d\n", cpu); @@ -429,19 +445,6 @@ void xen_setup_timer(int cpu) per_cpu(xen_clock_events, cpu).name = name; } -void xen_teardown_timer(int cpu) -{ - struct clock_event_device *evt; - BUG_ON(cpu == 0); - evt = &per_cpu(xen_clock_events, cpu).evt; - - if (evt->irq >= 0) { - unbind_from_irqhandler(evt->irq, NULL); - evt->irq = -1; - kfree(per_cpu(xen_clock_events, cpu).name); - per_cpu(xen_clock_events, cpu).name = NULL; - } -} void xen_setup_cpu_clockevents(void) { -- cgit v1.2.3 From 9c24b1672283644adf871244771ebf387dd73f90 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 7 Jun 2013 16:19:58 +0100 Subject: ASoC: wm8962: Restore device state after reset in runtime resume After the device has been reset we need to repeat the same initialisation we do on probe to make sure that the device is in a known state. Tested-by: Nicolin Chen Signed-off-by: Mark Brown --- sound/soc/codecs/wm8962.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 26219ea2bbb5..7a7a0567e547 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -3756,6 +3756,21 @@ static int wm8962_runtime_resume(struct device *dev) wm8962_reset(wm8962); + /* SYSCLK defaults to on; make sure it is off so we can safely + * write to registers if the device is declocked. + */ + regmap_update_bits(wm8962->regmap, WM8962_CLOCKING2, + WM8962_SYSCLK_ENA, 0); + + /* Ensure we have soft control over all registers */ + regmap_update_bits(wm8962->regmap, WM8962_CLOCKING2, + WM8962_CLKREG_OVD, WM8962_CLKREG_OVD); + + /* Ensure that the oscillator and PLLs are disabled */ + regmap_update_bits(wm8962->regmap, WM8962_PLL2, + WM8962_OSC_ENA | WM8962_PLL2_ENA | WM8962_PLL3_ENA, + 0); + regcache_sync(wm8962->regmap); return 0; -- cgit v1.2.3 From b6f3eff7130bbdb3d3ca5b7bbff2384b362606b4 Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Mon, 10 Jun 2013 15:48:09 +0100 Subject: drm/i915: Use FBINFO_STATE defines instead of 0 and 1 This makes, arguably, the condition on state easier to read. Suggested-by: Chris Wilson Signed-off-by: Damien Lespiau Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.c | 6 +++--- drivers/gpu/drm/i915/intel_fb.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index b23cd63b9fda..381c9dd4ab60 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -546,7 +546,7 @@ static int i915_drm_freeze(struct drm_device *dev) intel_opregion_fini(dev); console_lock(); - intel_fbdev_set_suspend(dev, 1); + intel_fbdev_set_suspend(dev, FBINFO_STATE_SUSPENDED); console_unlock(); return 0; @@ -590,7 +590,7 @@ void intel_console_resume(struct work_struct *work) struct drm_device *dev = dev_priv->dev; console_lock(); - intel_fbdev_set_suspend(dev, 0); + intel_fbdev_set_suspend(dev, FBINFO_STATE_RUNNING); console_unlock(); } @@ -659,7 +659,7 @@ static int __i915_drm_thaw(struct drm_device *dev) * path of resume if possible. */ if (console_trylock()) { - intel_fbdev_set_suspend(dev, 0); + intel_fbdev_set_suspend(dev, FBINFO_STATE_RUNNING); console_unlock(); } else { schedule_work(&dev_priv->console_resume_work); diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index 7a77d4cf96c2..244060ad354b 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -275,7 +275,7 @@ void intel_fbdev_set_suspend(struct drm_device *dev, int state) * been restored from swap. If the object is stolen however, it will be * full of whatever garbage was left in there. */ - if (!state && ifbdev->ifb.obj->stolen) + if (state == FBINFO_STATE_RUNNING && ifbdev->ifb.obj->stolen) memset_io(info->screen_base, 0, info->screen_size); fb_set_suspend(info, state); -- cgit v1.2.3 From 6916b14ea140ff5c915895eefe9431888a39a84d Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Tue, 21 May 2013 10:46:05 +0100 Subject: arm64: kernel: compiling issue, need delete read_current_timer() Under arm64, we will calibrate the delay loop statically using a known timer frequency, so delete read_current_timer(), or it will cause compiling issue with allmodconfig. The related error: ERROR: "read_current_timer" [lib/rbtree_test.ko] undefined! ERROR: "read_current_timer" [lib/interval_tree_test.ko] undefined! ERROR: "read_current_timer" [fs/ext4/ext4.ko] undefined! ERROR: "read_current_timer" [crypto/tcrypt.ko] undefined! Signed-off-by: Chen Gang Acked-by: Marc Zyngier Signed-off-by: Catalin Marinas --- arch/arm64/include/asm/timex.h | 6 +++--- arch/arm64/kernel/time.c | 6 ------ 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/arch/arm64/include/asm/timex.h b/arch/arm64/include/asm/timex.h index b24a31a7e2c9..81a076eb37fa 100644 --- a/arch/arm64/include/asm/timex.h +++ b/arch/arm64/include/asm/timex.h @@ -16,14 +16,14 @@ #ifndef __ASM_TIMEX_H #define __ASM_TIMEX_H +#include + /* * Use the current timer as a cycle counter since this is what we use for * the delay loop. */ -#define get_cycles() ({ cycles_t c; read_current_timer(&c); c; }) +#define get_cycles() arch_counter_get_cntvct() #include -#define ARCH_HAS_READ_CURRENT_TIMER - #endif diff --git a/arch/arm64/kernel/time.c b/arch/arm64/kernel/time.c index a551f88ae2c1..03dc3718eb13 100644 --- a/arch/arm64/kernel/time.c +++ b/arch/arm64/kernel/time.c @@ -68,12 +68,6 @@ unsigned long long notrace sched_clock(void) return arch_timer_read_counter() * sched_clock_mult; } -int read_current_timer(unsigned long *timer_value) -{ - *timer_value = arch_timer_read_counter(); - return 0; -} - void __init time_init(void) { u32 arch_timer_rate; -- cgit v1.2.3 From bd0c50240b71a69ba6cc690797e2371002cd241c Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Sun, 9 Jun 2013 11:47:49 +0800 Subject: PCI: Fix comment typo for pcie_pme_remove() Fix trivial comment typo for pcie_pme_remove(). Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/pme.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/pcie/pme.c b/drivers/pci/pcie/pme.c index 795db1f9d50c..e56e594ce112 100644 --- a/drivers/pci/pcie/pme.c +++ b/drivers/pci/pcie/pme.c @@ -408,7 +408,7 @@ static int pcie_pme_resume(struct pcie_device *srv) /** * pcie_pme_remove - Prepare PCIe PME service device for removal. - * @srv - PCIe service device to resume. + * @srv - PCIe service device to remove. */ static void pcie_pme_remove(struct pcie_device *srv) { -- cgit v1.2.3 From efe4e06de34953888504f4ea1d36c86db2267ea9 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 30 May 2013 13:08:34 +0300 Subject: PM / AVS: SmartReflex: disable errgen before vpbound disable vpboundsintr_en is available inside the IP block as an re-sycned version and one which is not. Due to this, there is an 1 sysclk cycle window where the SR_SInterruptz signal could be asserted low. IF, intr_en is cleared on the exact same cycle as the irqclr, an additional pulse is generated which indicates for VP that an additional adjustment of voltage is required. This results in VP doing two voltage adjustments for the SRERR (based on configuration, upto 4 steps), instead of the needed 1 step. Due to the unexpected pulse from AVS which breaks the AVS-VP communication protocol, VP also ends up in a stuck condition by entering a state where VP module remains non-responsive to any futher AVS adjustment events. This creates the symptom called "TRANXDONE Timeout" scenario. By disabling errgen prior to disable of intr_en, this situation can be avoided. Signed-off-by: Vincent Bour Signed-off-by: Leonardo Affortunati Signed-off-by: Nishanth Menon Signed-off-by: Andrii.Tseglytskyi Signed-off-by: Kevin Hilman --- drivers/power/avs/smartreflex.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/power/avs/smartreflex.c b/drivers/power/avs/smartreflex.c index 6b2238bb6a81..f34d34d46fc1 100644 --- a/drivers/power/avs/smartreflex.c +++ b/drivers/power/avs/smartreflex.c @@ -449,12 +449,17 @@ int sr_disable_errgen(struct voltagedomain *voltdm) return -EINVAL; } - /* Disable the interrupts of ERROR module */ - sr_modify_reg(sr, errconfig_offs, vpboundint_en | vpboundint_st, 0); - /* Disable the Sensor and errorgen */ sr_modify_reg(sr, SRCONFIG, SRCONFIG_SENENABLE | SRCONFIG_ERRGEN_EN, 0); + /* + * Disable the interrupts of ERROR module + * NOTE: modify is a read, modify,write - an implicit OCP barrier + * which is required is present here - sequencing is critical + * at this point (after errgen is disabled, vpboundint disable) + */ + sr_modify_reg(sr, errconfig_offs, vpboundint_en | vpboundint_st, 0); + return 0; } -- cgit v1.2.3 From bd4a36bec0e63941881608ad38351778748675e0 Mon Sep 17 00:00:00 2001 From: Andrii Tseglytskyi Date: Thu, 30 May 2013 13:08:35 +0300 Subject: PM / AVS: SmartReflex: disable runtime PM on driver remove Runtime PM should be disabled for device on driver remove, otherwise runtime PM will be not balanced, and this will cause an error message, on next driver probe. Signed-off-by: Andrii Tseglytskyi Acked-by: Nishanth Menon Signed-off-by: Kevin Hilman --- drivers/power/avs/smartreflex.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/power/avs/smartreflex.c b/drivers/power/avs/smartreflex.c index f34d34d46fc1..9b566482be38 100644 --- a/drivers/power/avs/smartreflex.c +++ b/drivers/power/avs/smartreflex.c @@ -1032,6 +1032,7 @@ static int omap_sr_remove(struct platform_device *pdev) if (sr_info->dbg_dir) debugfs_remove_recursive(sr_info->dbg_dir); + pm_runtime_disable(&pdev->dev); list_del(&sr_info->node); iounmap(sr_info->base); kfree(sr_info->name); -- cgit v1.2.3 From 33da28246f8cba3f1ffbca9434622d93afcde013 Mon Sep 17 00:00:00 2001 From: Andrii Tseglytskyi Date: Thu, 30 May 2013 13:08:36 +0300 Subject: PM / AVS: SmartReflex: fix driver name DRIVER_NAME was undefined for SmartReflex. Now it is defined with valid value "smartreflex". It is needed to define proper value for: MODULE_ALIAS("platform:" DRIVER_NAME); Signed-off-by: Andrii Tseglytskyi Acked-by: Nishanth Menon Signed-off-by: Kevin Hilman --- drivers/power/avs/smartreflex.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/power/avs/smartreflex.c b/drivers/power/avs/smartreflex.c index 9b566482be38..002005ee48d2 100644 --- a/drivers/power/avs/smartreflex.c +++ b/drivers/power/avs/smartreflex.c @@ -27,6 +27,7 @@ #include #include +#define DRIVER_NAME "smartreflex" #define SMARTREFLEX_NAME_LEN 16 #define NVALUE_NAME_LEN 40 #define SR_DISABLE_TIMEOUT 200 @@ -1070,7 +1071,7 @@ static struct platform_driver smartreflex_driver = { .remove = omap_sr_remove, .shutdown = omap_sr_shutdown, .driver = { - .name = "smartreflex", + .name = DRIVER_NAME, }, }; -- cgit v1.2.3 From cdbd2316a03f68b25a135a34d1d24f01ddef0c53 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 5 Jun 2013 13:34:03 +0200 Subject: drm/i915: fix up pch pll handling in ->mode_set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We ->mode_set is called we can't just blindly reuse an existing pll since that might be shared with a different, still active pch output. v2: Only update the pll settings when the pch pll is know to be unused, otherwise we can wreak havoc with a running pipe. Which in the case of DP will likely result in a black screen due to loss of link lock. v3: Tighten up the asserts a bit more, especially make sure that the pch pll is still enabled when we try to disable it. This would have caught the bug fixed in this patch. Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 6b37b7555f24..1d0aa24bb899 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1427,7 +1427,8 @@ static void ironlake_enable_pch_pll(struct intel_crtc *intel_crtc) /* PCH refclock must be enabled first */ assert_pch_refclk_enabled(dev_priv); - if (pll->active++ && pll->on) { + if (pll->active++) { + WARN_ON(!pll->on); assert_pch_pll_enabled(dev_priv, pll, NULL); return; } @@ -1468,10 +1469,9 @@ static void intel_disable_pch_pll(struct intel_crtc *intel_crtc) return; } - if (--pll->active) { - assert_pch_pll_enabled(dev_priv, pll, NULL); + assert_pch_pll_enabled(dev_priv, pll, NULL); + if (--pll->active) return; - } DRM_DEBUG_KMS("disabling PCH PLL %x\n", pll->pll_reg); @@ -3081,9 +3081,9 @@ static struct intel_pch_pll *intel_get_pch_pll(struct intel_crtc *intel_crtc, u3 pll = intel_crtc->pch_pll; if (pll) { - DRM_DEBUG_KMS("CRTC:%d reusing existing PCH PLL %x\n", + DRM_DEBUG_KMS("CRTC:%d dropping existing PCH PLL %x\n", intel_crtc->base.base.id, pll->pll_reg); - goto prepare; + intel_put_pch_pll(intel_crtc); } if (HAS_PCH_IBX(dev_priv->dev)) { @@ -3128,19 +3128,22 @@ static struct intel_pch_pll *intel_get_pch_pll(struct intel_crtc *intel_crtc, u3 found: intel_crtc->pch_pll = pll; - pll->refcount++; DRM_DEBUG_DRIVER("using pll %d for pipe %c\n", i, pipe_name(intel_crtc->pipe)); -prepare: /* separate function? */ - DRM_DEBUG_DRIVER("switching PLL %x off\n", pll->pll_reg); + if (pll->active == 0) { + DRM_DEBUG_DRIVER("setting up pll %d\n", i); + WARN_ON(pll->on); + assert_pch_pll_disabled(dev_priv, pll, NULL); - /* Wait for the clocks to stabilize before rewriting the regs */ - I915_WRITE(pll->pll_reg, dpll & ~DPLL_VCO_ENABLE); - POSTING_READ(pll->pll_reg); - udelay(150); + /* Wait for the clocks to stabilize before rewriting the regs */ + I915_WRITE(pll->pll_reg, dpll & ~DPLL_VCO_ENABLE); + POSTING_READ(pll->pll_reg); + udelay(150); + + I915_WRITE(pll->fp0_reg, fp); + I915_WRITE(pll->pll_reg, dpll & ~DPLL_VCO_ENABLE); + } + pll->refcount++; - I915_WRITE(pll->fp0_reg, fp); - I915_WRITE(pll->pll_reg, dpll & ~DPLL_VCO_ENABLE); - pll->on = false; return pll; } -- cgit v1.2.3 From d925c59a8174c8c150da7e0a38e35d89a8e7149c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 5 Jun 2013 13:34:04 +0200 Subject: drm/i915: conditionally disable pch resources in ilk_crtc_disable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simlar to how disable already works on haswell. This is possible since we now carefully track the pch state in the pipe config. Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 69 +++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 1d0aa24bb899..39984fb07fa9 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3436,7 +3436,9 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) intel_disable_planes(crtc); intel_disable_plane(dev_priv, plane, pipe); - intel_set_pch_fifo_underrun_reporting(dev, pipe, false); + if (intel_crtc->config.has_pch_encoder) + intel_set_pch_fifo_underrun_reporting(dev, pipe, false); + intel_disable_pipe(dev_priv, pipe); ironlake_pfit_disable(intel_crtc); @@ -3445,42 +3447,45 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) if (encoder->post_disable) encoder->post_disable(encoder); - ironlake_fdi_disable(crtc); + if (intel_crtc->config.has_pch_encoder) { + ironlake_fdi_disable(crtc); - ironlake_disable_pch_transcoder(dev_priv, pipe); - intel_set_pch_fifo_underrun_reporting(dev, pipe, true); + ironlake_disable_pch_transcoder(dev_priv, pipe); + intel_set_pch_fifo_underrun_reporting(dev, pipe, true); - 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); - temp |= TRANS_DP_PORT_SEL_NONE; - I915_WRITE(reg, temp); - - /* disable DPLL_SEL */ - temp = I915_READ(PCH_DPLL_SEL); - switch (pipe) { - case 0: - temp &= ~(TRANSA_DPLL_ENABLE | TRANSA_DPLLB_SEL); - break; - case 1: - temp &= ~(TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL); - break; - case 2: - /* C shares PLL A or B */ - temp &= ~(TRANSC_DPLL_ENABLE | TRANSC_DPLLB_SEL); - break; - default: - BUG(); /* wtf */ + 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); + temp |= TRANS_DP_PORT_SEL_NONE; + I915_WRITE(reg, temp); + + /* disable DPLL_SEL */ + temp = I915_READ(PCH_DPLL_SEL); + switch (pipe) { + case 0: + temp &= ~(TRANSA_DPLL_ENABLE | TRANSA_DPLLB_SEL); + break; + case 1: + temp &= ~(TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL); + break; + case 2: + /* C shares PLL A or B */ + temp &= ~(TRANSC_DPLL_ENABLE | TRANSC_DPLLB_SEL); + break; + default: + BUG(); /* wtf */ + } + I915_WRITE(PCH_DPLL_SEL, temp); } - I915_WRITE(PCH_DPLL_SEL, temp); - } - /* disable PCH DPLL */ - intel_disable_pch_pll(intel_crtc); + /* disable PCH DPLL */ + intel_disable_pch_pll(intel_crtc); - ironlake_fdi_pll_disable(intel_crtc); + ironlake_fdi_pll_disable(intel_crtc); + } intel_crtc->active = false; intel_update_watermarks(dev); -- cgit v1.2.3 From f4a091c71baa55dc8822614ab716525779623c1c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 10 Jun 2013 17:28:22 +0200 Subject: drm/i915: lock down pch pll accouting some more MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before I start to make a complete mess out of this, crank up the paranoia level a bit. v2: Kill the has_pch_encoder check in put_shared_dpll - it's invalid as spotted by Ville since we currently only put the dpll when we already have the new pipe config. So a direct pch port -> cpu edp transition will hit this. v3: Now that I've lifted my blinders add the WARN_ON Ville requested. Cc: Ville Syrjälä Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 39984fb07fa9..34e6bf3b1142 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1432,6 +1432,7 @@ static void ironlake_enable_pch_pll(struct intel_crtc *intel_crtc) assert_pch_pll_enabled(dev_priv, pll, NULL); return; } + WARN_ON(pll->on); DRM_DEBUG_KMS("enabling PCH PLL %x\n", pll->pll_reg); @@ -1470,6 +1471,7 @@ static void intel_disable_pch_pll(struct intel_crtc *intel_crtc) } assert_pch_pll_enabled(dev_priv, pll, NULL); + WARN_ON(!pll->on); if (--pll->active) return; @@ -3069,7 +3071,11 @@ static void intel_put_pch_pll(struct intel_crtc *intel_crtc) return; } - --pll->refcount; + if (--pll->refcount == 0) { + WARN_ON(pll->on); + WARN_ON(pll->active); + } + intel_crtc->pch_pll = NULL; } -- cgit v1.2.3 From 3dfc35ffd938abe67f2559db6b517536a207df24 Mon Sep 17 00:00:00 2001 From: Andrii Tseglytskyi Date: Mon, 27 May 2013 14:09:22 +0300 Subject: PM / AVS: SmartReflex: use omap_sr * for errgen interfaces SmartReflex driver interface is natively divided to two parts: - external SmartReflex interface - interface between SmartReflex driver and SmartReflex Class Functions which belong to AVS class interface can use struct omap_sr* instead of struct voltatedomain*, to provide a direct connection between SR driver and SR class. This allows us to optimize and not do additional lookups where none is required. sr_disable_errgen() and sr_configure_errgen() are interface functions between SR driver and SR class. They are typically used by Class driver to configure error generator module during SmartReflex enable/disable sequence. Now they take struct omap_sr* as input parameter. Signed-off-by: Andrii Tseglytskyi Acked-by: Nishanth Menon Signed-off-by: Kevin Hilman --- arch/arm/mach-omap2/smartreflex-class3.c | 4 ++-- drivers/power/avs/smartreflex.c | 26 +++++++++++++------------- include/linux/power/smartreflex.h | 4 ++-- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/arch/arm/mach-omap2/smartreflex-class3.c b/arch/arm/mach-omap2/smartreflex-class3.c index aee3c8940a30..6c26dc15815c 100644 --- a/arch/arm/mach-omap2/smartreflex-class3.c +++ b/arch/arm/mach-omap2/smartreflex-class3.c @@ -31,7 +31,7 @@ static int sr_class3_enable(struct omap_sr *sr) static int sr_class3_disable(struct omap_sr *sr, int is_volt_reset) { - sr_disable_errgen(sr->voltdm); + sr_disable_errgen(sr); omap_vp_disable(sr->voltdm); sr_disable(sr->voltdm); if (is_volt_reset) @@ -42,7 +42,7 @@ static int sr_class3_disable(struct omap_sr *sr, int is_volt_reset) static int sr_class3_configure(struct omap_sr *sr) { - return sr_configure_errgen(sr->voltdm); + return sr_configure_errgen(sr); } /* SR class3 structure */ diff --git a/drivers/power/avs/smartreflex.c b/drivers/power/avs/smartreflex.c index 002005ee48d2..fccb62743d19 100644 --- a/drivers/power/avs/smartreflex.c +++ b/drivers/power/avs/smartreflex.c @@ -342,9 +342,9 @@ static struct omap_sr_nvalue_table *sr_retrieve_nvalue_row( /* Public Functions */ /** - * sr_configure_errgen() - Configures the smrtreflex to perform AVS using the + * sr_configure_errgen() - Configures the SmartReflex to perform AVS using the * error generator module. - * @voltdm: VDD pointer to which the SR module to be configured belongs to. + * @sr: SR module to be configured. * * This API is to be called from the smartreflex class driver to * configure the error generator module inside the smartreflex module. @@ -353,17 +353,17 @@ static struct omap_sr_nvalue_table *sr_retrieve_nvalue_row( * SR CLASS 2 can choose between ERROR module and MINMAXAVG * module. Returns 0 on success and error value in case of failure. */ -int sr_configure_errgen(struct voltagedomain *voltdm) +int sr_configure_errgen(struct omap_sr *sr) { u32 sr_config, sr_errconfig, errconfig_offs; u32 vpboundint_en, vpboundint_st; u32 senp_en = 0, senn_en = 0; u8 senp_shift, senn_shift; - struct omap_sr *sr = _sr_lookup(voltdm); - if (IS_ERR(sr)) { - pr_warning("%s: omap_sr struct for voltdm not found\n", __func__); - return PTR_ERR(sr); + if (!sr) { + pr_warn("%s: NULL omap_sr from %pF\n", __func__, + (void *)_RET_IP_); + return -EINVAL; } if (!sr->clk_length) @@ -415,22 +415,22 @@ int sr_configure_errgen(struct voltagedomain *voltdm) /** * sr_disable_errgen() - Disables SmartReflex AVS module's errgen component - * @voltdm: VDD pointer to which the SR module to be configured belongs to. + * @sr: SR module to be configured. * * This API is to be called from the smartreflex class driver to * disable the error generator module inside the smartreflex module. * * Returns 0 on success and error value in case of failure. */ -int sr_disable_errgen(struct voltagedomain *voltdm) +int sr_disable_errgen(struct omap_sr *sr) { u32 errconfig_offs; u32 vpboundint_en, vpboundint_st; - struct omap_sr *sr = _sr_lookup(voltdm); - if (IS_ERR(sr)) { - pr_warning("%s: omap_sr struct for voltdm not found\n", __func__); - return PTR_ERR(sr); + if (!sr) { + pr_warn("%s: NULL omap_sr from %pF\n", __func__, + (void *)_RET_IP_); + return -EINVAL; } switch (sr->ip_type) { diff --git a/include/linux/power/smartreflex.h b/include/linux/power/smartreflex.h index c0f44c2b006d..9c3b9ad17095 100644 --- a/include/linux/power/smartreflex.h +++ b/include/linux/power/smartreflex.h @@ -301,8 +301,8 @@ void omap_sr_register_pmic(struct omap_sr_pmic_data *pmic_data); /* Smartreflex driver hooks to be called from Smartreflex class driver */ int sr_enable(struct voltagedomain *voltdm, unsigned long volt); void sr_disable(struct voltagedomain *voltdm); -int sr_configure_errgen(struct voltagedomain *voltdm); -int sr_disable_errgen(struct voltagedomain *voltdm); +int sr_configure_errgen(struct omap_sr *sr); +int sr_disable_errgen(struct omap_sr *sr); int sr_configure_minmax(struct voltagedomain *voltdm); /* API to register the smartreflex class driver with the smartreflex driver */ -- cgit v1.2.3 From 6c80573415fe47450579d5d8bfab53b304d803ed Mon Sep 17 00:00:00 2001 From: Andrii Tseglytskyi Date: Mon, 27 May 2013 14:09:23 +0300 Subject: PM / AVS: SmartReflex: use omap_sr * for minmax interfaces SmartReflex driver interface is natively divided to two parts: - external SmartReflex interface - interface between SmartReflex driver and SmartReflex Class Functions which belong to AVS class interface can use struct omap_sr* instead of struct voltatedomain*, to provide a direct connection between SR driver and SR class. This allows us to optimize and not do additional lookups where none is required. sr_configure_minmax() is interface function between SR driver and SR class. It is typically used by Class driver to configure MINMAXAVG module inside SmartReflex module. Now it takes struct omap_sr* as input parameter. Signed-off-by: Andrii Tseglytskyi Acked-by: Nishanth Menon Signed-off-by: Kevin Hilman --- drivers/power/avs/smartreflex.c | 14 +++++++------- include/linux/power/smartreflex.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/power/avs/smartreflex.c b/drivers/power/avs/smartreflex.c index fccb62743d19..08a8a299f1fd 100644 --- a/drivers/power/avs/smartreflex.c +++ b/drivers/power/avs/smartreflex.c @@ -465,9 +465,9 @@ int sr_disable_errgen(struct omap_sr *sr) } /** - * sr_configure_minmax() - Configures the smrtreflex to perform AVS using the + * sr_configure_minmax() - Configures the SmartReflex to perform AVS using the * minmaxavg module. - * @voltdm: VDD pointer to which the SR module to be configured belongs to. + * @sr: SR module to be configured. * * This API is to be called from the smartreflex class driver to * configure the minmaxavg module inside the smartreflex module. @@ -476,16 +476,16 @@ int sr_disable_errgen(struct omap_sr *sr) * SR CLASS 2 can choose between ERROR module and MINMAXAVG * module. Returns 0 on success and error value in case of failure. */ -int sr_configure_minmax(struct voltagedomain *voltdm) +int sr_configure_minmax(struct omap_sr *sr) { u32 sr_config, sr_avgwt; u32 senp_en = 0, senn_en = 0; u8 senp_shift, senn_shift; - struct omap_sr *sr = _sr_lookup(voltdm); - if (IS_ERR(sr)) { - pr_warning("%s: omap_sr struct for voltdm not found\n", __func__); - return PTR_ERR(sr); + if (!sr) { + pr_warn("%s: NULL omap_sr from %pF\n", __func__, + (void *)_RET_IP_); + return -EINVAL; } if (!sr->clk_length) diff --git a/include/linux/power/smartreflex.h b/include/linux/power/smartreflex.h index 9c3b9ad17095..648be776b661 100644 --- a/include/linux/power/smartreflex.h +++ b/include/linux/power/smartreflex.h @@ -303,7 +303,7 @@ int sr_enable(struct voltagedomain *voltdm, unsigned long volt); void sr_disable(struct voltagedomain *voltdm); int sr_configure_errgen(struct omap_sr *sr); int sr_disable_errgen(struct omap_sr *sr); -int sr_configure_minmax(struct voltagedomain *voltdm); +int sr_configure_minmax(struct omap_sr *sr); /* API to register the smartreflex class driver with the smartreflex driver */ int sr_register_class(struct omap_sr_class_data *class_data); -- cgit v1.2.3 From 299066bb376ef7720cc3d8de95d5b967c5446863 Mon Sep 17 00:00:00 2001 From: Andrii Tseglytskyi Date: Mon, 27 May 2013 14:09:24 +0300 Subject: PM / AVS: SmartReflex: use omap_sr * for enable/disable interface SmartReflex driver interface is natively divided to two parts: - external SmartReflex interface - interface between SmartReflex driver and SmartReflex Class Functions which belong to AVS class interface can use struct omap_sr* instead of struct voltatedomain*, to provide a direct connection between SR driver and SR class. This allows us to optimize and not do additional lookups where none is required. sr_enable() and sr_disable() are interface functions between SR driver and SR class. They are typically used by Class driver to enable/disable SmartReflex hardware module. Now they take struct omap_sr* as input parameter. Signed-off-by: Andrii Tseglytskyi Acked-by: Nishanth Menon Signed-off-by: Kevin Hilman --- arch/arm/mach-omap2/smartreflex-class3.c | 4 ++-- drivers/power/avs/smartreflex.c | 23 +++++++++++------------ include/linux/power/smartreflex.h | 4 ++-- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/arch/arm/mach-omap2/smartreflex-class3.c b/arch/arm/mach-omap2/smartreflex-class3.c index 6c26dc15815c..7a42e1960c3b 100644 --- a/arch/arm/mach-omap2/smartreflex-class3.c +++ b/arch/arm/mach-omap2/smartreflex-class3.c @@ -26,14 +26,14 @@ static int sr_class3_enable(struct omap_sr *sr) } omap_vp_enable(sr->voltdm); - return sr_enable(sr->voltdm, volt); + return sr_enable(sr, volt); } static int sr_class3_disable(struct omap_sr *sr, int is_volt_reset) { sr_disable_errgen(sr); omap_vp_disable(sr->voltdm); - sr_disable(sr->voltdm); + sr_disable(sr); if (is_volt_reset) voltdm_reset(sr->voltdm); diff --git a/drivers/power/avs/smartreflex.c b/drivers/power/avs/smartreflex.c index 08a8a299f1fd..14cce7addbb9 100644 --- a/drivers/power/avs/smartreflex.c +++ b/drivers/power/avs/smartreflex.c @@ -552,7 +552,7 @@ int sr_configure_minmax(struct omap_sr *sr) /** * sr_enable() - Enables the smartreflex module. - * @voltdm: VDD pointer to which the SR module to be configured belongs to. + * @sr: pointer to which the SR module to be configured belongs to. * @volt: The voltage at which the Voltage domain associated with * the smartreflex module is operating at. * This is required only to program the correct Ntarget value. @@ -561,16 +561,16 @@ int sr_configure_minmax(struct omap_sr *sr) * enable a smartreflex module. Returns 0 on success. Returns error * value if the voltage passed is wrong or if ntarget value is wrong. */ -int sr_enable(struct voltagedomain *voltdm, unsigned long volt) +int sr_enable(struct omap_sr *sr, unsigned long volt) { struct omap_volt_data *volt_data; - struct omap_sr *sr = _sr_lookup(voltdm); struct omap_sr_nvalue_table *nvalue_row; int ret; - if (IS_ERR(sr)) { - pr_warning("%s: omap_sr struct for voltdm not found\n", __func__); - return PTR_ERR(sr); + if (!sr) { + pr_warn("%s: NULL omap_sr from %pF\n", __func__, + (void *)_RET_IP_); + return -EINVAL; } volt_data = omap_voltage_get_voltdata(sr->voltdm, volt); @@ -612,17 +612,16 @@ int sr_enable(struct voltagedomain *voltdm, unsigned long volt) /** * sr_disable() - Disables the smartreflex module. - * @voltdm: VDD pointer to which the SR module to be configured belongs to. + * @sr: pointer to which the SR module to be configured belongs to. * * This API is to be called from the smartreflex class driver to * disable a smartreflex module. */ -void sr_disable(struct voltagedomain *voltdm) +void sr_disable(struct omap_sr *sr) { - struct omap_sr *sr = _sr_lookup(voltdm); - - if (IS_ERR(sr)) { - pr_warning("%s: omap_sr struct for voltdm not found\n", __func__); + if (!sr) { + pr_warn("%s: NULL omap_sr from %pF\n", __func__, + (void *)_RET_IP_); return; } diff --git a/include/linux/power/smartreflex.h b/include/linux/power/smartreflex.h index 648be776b661..d8b187c3925d 100644 --- a/include/linux/power/smartreflex.h +++ b/include/linux/power/smartreflex.h @@ -299,8 +299,8 @@ void omap_sr_disable_reset_volt(struct voltagedomain *voltdm); void omap_sr_register_pmic(struct omap_sr_pmic_data *pmic_data); /* Smartreflex driver hooks to be called from Smartreflex class driver */ -int sr_enable(struct voltagedomain *voltdm, unsigned long volt); -void sr_disable(struct voltagedomain *voltdm); +int sr_enable(struct omap_sr *sr, unsigned long volt); +void sr_disable(struct omap_sr *sr); int sr_configure_errgen(struct omap_sr *sr); int sr_disable_errgen(struct omap_sr *sr); int sr_configure_minmax(struct omap_sr *sr); -- cgit v1.2.3 From e72f9fbf99c4277b2ccfd4d55d66aa6caf922f42 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 5 Jun 2013 13:34:06 +0200 Subject: drm/i915: s/pch_pll/shared_dpll/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For fastboot we need some support to read out the sharing state of plls, at least for platforms where they can be shared (or freely assigned at least). Now for ivb we already have pretty extensive infrastructure for tracking pch plls, and it took us an aweful lot of tries to get that remotely right. Note that hsw could also share plls, but even now they're already freely assignable. So we need this on more than just ivb. So on top of the usual fastboot fun pll sharing seems to be an additional step up in fragility. Hence a common infrastructure for all shared/freely assignable display plls seems to be in order. The plan is to have a bit of dpll hw state readout code, which can be used individually, but also to fill in the pipe config. The hw state cross check code will then use that information to make sure that after every modeset every pipe still is connected to a pll which still has the correct configuration - a lot of the pch pll sharing bugs where due to incorrect sharing. We start this endeavour with a simple s/pch_pll/shared_dpll/ rename job. Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.c | 14 ++--- drivers/gpu/drm/i915/i915_drv.h | 6 +- drivers/gpu/drm/i915/intel_display.c | 112 +++++++++++++++++------------------ drivers/gpu/drm/i915/intel_drv.h | 2 +- 4 files changed, 67 insertions(+), 67 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 381c9dd4ab60..b0a4e6834c1d 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -433,7 +433,7 @@ void intel_detect_pch(struct drm_device *dev) */ if (INTEL_INFO(dev)->num_pipes == 0) { dev_priv->pch_type = PCH_NOP; - dev_priv->num_pch_pll = 0; + dev_priv->num_shared_dpll = 0; return; } @@ -452,34 +452,34 @@ void intel_detect_pch(struct drm_device *dev) if (id == INTEL_PCH_IBX_DEVICE_ID_TYPE) { dev_priv->pch_type = PCH_IBX; - dev_priv->num_pch_pll = 2; + dev_priv->num_shared_dpll = 2; DRM_DEBUG_KMS("Found Ibex Peak PCH\n"); WARN_ON(!IS_GEN5(dev)); } else if (id == INTEL_PCH_CPT_DEVICE_ID_TYPE) { dev_priv->pch_type = PCH_CPT; - dev_priv->num_pch_pll = 2; + dev_priv->num_shared_dpll = 2; DRM_DEBUG_KMS("Found CougarPoint PCH\n"); WARN_ON(!(IS_GEN6(dev) || IS_IVYBRIDGE(dev))); } else if (id == INTEL_PCH_PPT_DEVICE_ID_TYPE) { /* PantherPoint is CPT compatible */ dev_priv->pch_type = PCH_CPT; - dev_priv->num_pch_pll = 2; + dev_priv->num_shared_dpll = 2; DRM_DEBUG_KMS("Found PatherPoint PCH\n"); WARN_ON(!(IS_GEN6(dev) || IS_IVYBRIDGE(dev))); } else if (id == INTEL_PCH_LPT_DEVICE_ID_TYPE) { dev_priv->pch_type = PCH_LPT; - dev_priv->num_pch_pll = 0; + dev_priv->num_shared_dpll = 0; DRM_DEBUG_KMS("Found LynxPoint PCH\n"); WARN_ON(!IS_HASWELL(dev)); WARN_ON(IS_ULT(dev)); } else if (id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE) { dev_priv->pch_type = PCH_LPT; - dev_priv->num_pch_pll = 0; + dev_priv->num_shared_dpll = 0; DRM_DEBUG_KMS("Found LynxPoint LP PCH\n"); WARN_ON(!IS_HASWELL(dev)); WARN_ON(!IS_ULT(dev)); } - BUG_ON(dev_priv->num_pch_pll > I915_NUM_PLLS); + BUG_ON(dev_priv->num_shared_dpll > I915_NUM_PLLS); } pci_dev_put(pch); } diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 87f7f88b1030..f69ba390a649 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -132,7 +132,7 @@ enum hpd_pin { list_for_each_entry((intel_encoder), &(dev)->mode_config.encoder_list, base.head) \ if ((intel_encoder)->base.crtc == (__crtc)) -struct intel_pch_pll { +struct intel_shared_dpll { int refcount; /* count of number of CRTCs sharing this PLL */ int active; /* count of number of active CRTCs (i.e. DPMS on) */ bool on; /* is the PLL actually active? Disabled during modeset */ @@ -1026,7 +1026,6 @@ typedef struct drm_i915_private { u32 hpd_event_bits; struct timer_list hotplug_reenable_timer; - int num_pch_pll; int num_plane; unsigned long cfb_size; @@ -1087,7 +1086,8 @@ typedef struct drm_i915_private { struct drm_crtc *pipe_to_crtc_mapping[3]; wait_queue_head_t pending_flip_queue; - struct intel_pch_pll pch_plls[I915_NUM_PLLS]; + int num_shared_dpll; + struct intel_shared_dpll shared_dplls[I915_NUM_PLLS]; struct intel_ddi_plls ddi_plls; /* Reclocking support */ diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 34e6bf3b1142..d138b302f275 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -910,10 +910,10 @@ static void assert_pll(struct drm_i915_private *dev_priv, #define assert_pll_disabled(d, p) assert_pll(d, p, false) /* For ILK+ */ -static void assert_pch_pll(struct drm_i915_private *dev_priv, - struct intel_pch_pll *pll, - struct intel_crtc *crtc, - bool state) +static void assert_shared_dpll(struct drm_i915_private *dev_priv, + struct intel_shared_dpll *pll, + struct intel_crtc *crtc, + bool state) { u32 val; bool cur_state; @@ -952,8 +952,8 @@ static void assert_pch_pll(struct drm_i915_private *dev_priv, } } } -#define assert_pch_pll_enabled(d, p, c) assert_pch_pll(d, p, c, true) -#define assert_pch_pll_disabled(d, p, c) assert_pch_pll(d, p, c, false) +#define assert_shared_dpll_enabled(d, p, c) assert_shared_dpll(d, p, c, true) +#define assert_shared_dpll_disabled(d, p, c) assert_shared_dpll(d, p, c, false) static void assert_fdi_tx(struct drm_i915_private *dev_priv, enum pipe pipe, bool state) @@ -1397,23 +1397,23 @@ void vlv_wait_port_ready(struct drm_i915_private *dev_priv, int port) } /** - * ironlake_enable_pch_pll - enable PCH PLL + * ironlake_enable_shared_dpll - enable PCH PLL * @dev_priv: i915 private structure * @pipe: pipe PLL to enable * * The PCH PLL needs to be enabled before the PCH transcoder, since it * drives the transcoder clock. */ -static void ironlake_enable_pch_pll(struct intel_crtc *intel_crtc) +static void ironlake_enable_shared_dpll(struct intel_crtc *intel_crtc) { struct drm_i915_private *dev_priv = intel_crtc->base.dev->dev_private; - struct intel_pch_pll *pll; + struct intel_shared_dpll *pll; int reg; u32 val; /* PCH PLLs only available on ILK, SNB and IVB */ BUG_ON(dev_priv->info->gen < 5); - pll = intel_crtc->pch_pll; + pll = intel_crtc->shared_dpll; if (pll == NULL) return; @@ -1429,7 +1429,7 @@ static void ironlake_enable_pch_pll(struct intel_crtc *intel_crtc) if (pll->active++) { WARN_ON(!pll->on); - assert_pch_pll_enabled(dev_priv, pll, NULL); + assert_shared_dpll_enabled(dev_priv, pll, NULL); return; } WARN_ON(pll->on); @@ -1446,10 +1446,10 @@ static void ironlake_enable_pch_pll(struct intel_crtc *intel_crtc) pll->on = true; } -static void intel_disable_pch_pll(struct intel_crtc *intel_crtc) +static void intel_disable_shared_dpll(struct intel_crtc *intel_crtc) { struct drm_i915_private *dev_priv = intel_crtc->base.dev->dev_private; - struct intel_pch_pll *pll = intel_crtc->pch_pll; + struct intel_shared_dpll *pll = intel_crtc->shared_dpll; int reg; u32 val; @@ -1466,11 +1466,11 @@ static void intel_disable_pch_pll(struct intel_crtc *intel_crtc) intel_crtc->base.base.id); if (WARN_ON(pll->active == 0)) { - assert_pch_pll_disabled(dev_priv, pll, NULL); + assert_shared_dpll_disabled(dev_priv, pll, NULL); return; } - assert_pch_pll_enabled(dev_priv, pll, NULL); + assert_shared_dpll_enabled(dev_priv, pll, NULL); WARN_ON(!pll->on); if (--pll->active) return; @@ -1501,9 +1501,9 @@ static void ironlake_enable_pch_transcoder(struct drm_i915_private *dev_priv, BUG_ON(dev_priv->info->gen < 5); /* Make sure PCH DPLL is enabled */ - assert_pch_pll_enabled(dev_priv, - to_intel_crtc(crtc)->pch_pll, - to_intel_crtc(crtc)); + assert_shared_dpll_enabled(dev_priv, + to_intel_crtc(crtc)->shared_dpll, + to_intel_crtc(crtc)); /* FDI must be feeding us bits for PCH ports */ assert_fdi_tx_enabled(dev_priv, pipe); @@ -2966,10 +2966,10 @@ static void ironlake_pch_enable(struct drm_crtc *crtc) * transcoder, and we actually should do this to not upset any PCH * transcoder that already use the clock when we share it. * - * Note that enable_pch_pll tries to do the right thing, but get_pch_pll - * unconditionally resets the pll - we need that to have the right LVDS - * enable sequence. */ - ironlake_enable_pch_pll(intel_crtc); + * Note that enable_shared_dpll tries to do the right thing, but + * get_shared_dpll unconditionally resets the pll - we need that to have + * the right LVDS enable sequence. */ + ironlake_enable_shared_dpll(intel_crtc); if (HAS_PCH_CPT(dev)) { u32 sel; @@ -2990,7 +2990,7 @@ static void ironlake_pch_enable(struct drm_crtc *crtc) sel = TRANSC_DPLLB_SEL; break; } - if (intel_crtc->pch_pll->pll_reg == _PCH_DPLL_B) + if (intel_crtc->shared_dpll->pll_reg == _PCH_DPLL_B) temp |= sel; else temp &= ~sel; @@ -3059,9 +3059,9 @@ static void lpt_pch_enable(struct drm_crtc *crtc) lpt_enable_pch_transcoder(dev_priv, cpu_transcoder); } -static void intel_put_pch_pll(struct intel_crtc *intel_crtc) +static void intel_put_shared_dpll(struct intel_crtc *intel_crtc) { - struct intel_pch_pll *pll = intel_crtc->pch_pll; + struct intel_shared_dpll *pll = intel_crtc->shared_dpll; if (pll == NULL) return; @@ -3076,26 +3076,26 @@ static void intel_put_pch_pll(struct intel_crtc *intel_crtc) WARN_ON(pll->active); } - intel_crtc->pch_pll = NULL; + intel_crtc->shared_dpll = NULL; } -static struct intel_pch_pll *intel_get_pch_pll(struct intel_crtc *intel_crtc, u32 dpll, u32 fp) +static struct intel_shared_dpll *intel_get_shared_dpll(struct intel_crtc *intel_crtc, u32 dpll, u32 fp) { struct drm_i915_private *dev_priv = intel_crtc->base.dev->dev_private; - struct intel_pch_pll *pll; + struct intel_shared_dpll *pll; int i; - pll = intel_crtc->pch_pll; + pll = intel_crtc->shared_dpll; if (pll) { DRM_DEBUG_KMS("CRTC:%d dropping existing PCH PLL %x\n", intel_crtc->base.base.id, pll->pll_reg); - intel_put_pch_pll(intel_crtc); + intel_put_shared_dpll(intel_crtc); } if (HAS_PCH_IBX(dev_priv->dev)) { /* Ironlake PCH has a fixed PLL->PCH pipe mapping. */ i = intel_crtc->pipe; - pll = &dev_priv->pch_plls[i]; + pll = &dev_priv->shared_dplls[i]; DRM_DEBUG_KMS("CRTC:%d using pre-allocated PCH PLL %x\n", intel_crtc->base.base.id, pll->pll_reg); @@ -3103,8 +3103,8 @@ static struct intel_pch_pll *intel_get_pch_pll(struct intel_crtc *intel_crtc, u3 goto found; } - for (i = 0; i < dev_priv->num_pch_pll; i++) { - pll = &dev_priv->pch_plls[i]; + for (i = 0; i < dev_priv->num_shared_dpll; i++) { + pll = &dev_priv->shared_dplls[i]; /* Only want to check enabled timings first */ if (pll->refcount == 0) @@ -3121,8 +3121,8 @@ static struct intel_pch_pll *intel_get_pch_pll(struct intel_crtc *intel_crtc, u3 } /* Ok no matching timings, maybe there's a free one? */ - for (i = 0; i < dev_priv->num_pch_pll; i++) { - pll = &dev_priv->pch_plls[i]; + for (i = 0; i < dev_priv->num_shared_dpll; i++) { + pll = &dev_priv->shared_dplls[i]; if (pll->refcount == 0) { DRM_DEBUG_KMS("CRTC:%d allocated PCH PLL %x\n", intel_crtc->base.base.id, pll->pll_reg); @@ -3133,12 +3133,12 @@ static struct intel_pch_pll *intel_get_pch_pll(struct intel_crtc *intel_crtc, u3 return NULL; found: - intel_crtc->pch_pll = pll; + intel_crtc->shared_dpll = pll; DRM_DEBUG_DRIVER("using pll %d for pipe %c\n", i, pipe_name(intel_crtc->pipe)); if (pll->active == 0) { DRM_DEBUG_DRIVER("setting up pll %d\n", i); WARN_ON(pll->on); - assert_pch_pll_disabled(dev_priv, pll, NULL); + assert_shared_dpll_disabled(dev_priv, pll, NULL); /* Wait for the clocks to stabilize before rewriting the regs */ I915_WRITE(pll->pll_reg, dpll & ~DPLL_VCO_ENABLE); @@ -3488,7 +3488,7 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) } /* disable PCH DPLL */ - intel_disable_pch_pll(intel_crtc); + intel_disable_shared_dpll(intel_crtc); ironlake_fdi_pll_disable(intel_crtc); } @@ -3561,7 +3561,7 @@ static void haswell_crtc_disable(struct drm_crtc *crtc) static void ironlake_crtc_off(struct drm_crtc *crtc) { struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - intel_put_pch_pll(intel_crtc); + intel_put_shared_dpll(intel_crtc); } static void haswell_crtc_off(struct drm_crtc *crtc) @@ -5765,7 +5765,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, /* CPU eDP is the only output that doesn't need a PCH PLL of its own. */ if (intel_crtc->config.has_pch_encoder) { - struct intel_pch_pll *pll; + struct intel_shared_dpll *pll; fp = i9xx_dpll_compute_fp(&intel_crtc->config.dpll); if (has_reduced_clock) @@ -5775,14 +5775,14 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, &fp, &reduced_clock, has_reduced_clock ? &fp2 : NULL); - pll = intel_get_pch_pll(intel_crtc, dpll, fp); + pll = intel_get_shared_dpll(intel_crtc, dpll, fp); if (pll == NULL) { DRM_DEBUG_DRIVER("failed to find PLL for pipe %c\n", pipe_name(pipe)); return -EINVAL; } } else - intel_put_pch_pll(intel_crtc); + intel_put_shared_dpll(intel_crtc); if (intel_crtc->config.has_dp_encoder) intel_dp_set_m_n(intel_crtc); @@ -5791,11 +5791,11 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, if (encoder->pre_pll_enable) encoder->pre_pll_enable(encoder); - if (intel_crtc->pch_pll) { - I915_WRITE(intel_crtc->pch_pll->pll_reg, dpll); + if (intel_crtc->shared_dpll) { + I915_WRITE(intel_crtc->shared_dpll->pll_reg, dpll); /* Wait for the clocks to stabilize. */ - POSTING_READ(intel_crtc->pch_pll->pll_reg); + POSTING_READ(intel_crtc->shared_dpll->pll_reg); udelay(150); /* The pixel multiplier can only be updated once the @@ -5803,16 +5803,16 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, * * So write it again. */ - I915_WRITE(intel_crtc->pch_pll->pll_reg, dpll); + I915_WRITE(intel_crtc->shared_dpll->pll_reg, dpll); } intel_crtc->lowfreq_avail = false; - if (intel_crtc->pch_pll) { + if (intel_crtc->shared_dpll) { if (is_lvds && has_reduced_clock && i915_powersave) { - I915_WRITE(intel_crtc->pch_pll->fp1_reg, fp2); + I915_WRITE(intel_crtc->shared_dpll->fp1_reg, fp2); intel_crtc->lowfreq_avail = true; } else { - I915_WRITE(intel_crtc->pch_pll->fp1_reg, fp); + I915_WRITE(intel_crtc->shared_dpll->fp1_reg, fp); } } @@ -8723,20 +8723,20 @@ static void intel_cpu_pll_init(struct drm_device *dev) intel_ddi_pll_init(dev); } -static void intel_pch_pll_init(struct drm_device *dev) +static void intel_shared_dpll_init(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; int i; - if (dev_priv->num_pch_pll == 0) { + if (dev_priv->num_shared_dpll == 0) { DRM_DEBUG_KMS("No PCH PLLs on this hardware, skipping initialisation\n"); return; } - for (i = 0; i < dev_priv->num_pch_pll; i++) { - dev_priv->pch_plls[i].pll_reg = _PCH_DPLL(i); - dev_priv->pch_plls[i].fp0_reg = _PCH_FP0(i); - dev_priv->pch_plls[i].fp1_reg = _PCH_FP1(i); + for (i = 0; i < dev_priv->num_shared_dpll; i++) { + dev_priv->shared_dplls[i].pll_reg = _PCH_DPLL(i); + dev_priv->shared_dplls[i].fp0_reg = _PCH_FP0(i); + dev_priv->shared_dplls[i].fp1_reg = _PCH_FP1(i); } } @@ -9428,7 +9428,7 @@ void intel_modeset_init(struct drm_device *dev) } intel_cpu_pll_init(dev); - intel_pch_pll_init(dev); + intel_shared_dpll_init(dev); /* Just disable it once at startup */ i915_disable_vga(dev); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index bb4822a6c41e..b094260f1e2b 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -317,7 +317,7 @@ struct intel_crtc { struct intel_crtc_config config; /* We can share PLLs across outputs if the timings match */ - struct intel_pch_pll *pch_pll; + struct intel_shared_dpll *shared_dpll; uint32_t ddi_pll_sel; /* reset counter value when the last flip was submitted */ -- cgit v1.2.3 From e2b78267421c2b407409772119a4aa500da56cc8 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 7 Jun 2013 23:10:03 +0200 Subject: drm/i915: switch crtc->shared_dpll from a pointer to an enum MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dealing with discrete enum values is simpler for hw state readout and pipe config computations than pointers - having neat names instead of chasing pointers should look better in the code. This isn't a that good reason for pch plls, but on haswell we actually have 3 different types of plls: WRPLL, SPLL and the DP clocks. Having explicit names should help there. Since this also adds the intel_crtc_to_shared_dpll helper to further abstract away the crtc -> dpll relationship this will also help to make the next patch simpler, which moves the shared dpll into the pipe configuration. Also note that for uniformity we have two special dpll ids: NONE for pipes which need a shared pll but don't have one (yet) and private for when there's a non-shared pll (e.g. per-pipe or per-port pll). I've thought whether we should also add a 2nd enum for the type of the pll we want (for really generic pll selection code) but thrown that idea out again - likely there's too much platform craziness going on to be able to share the pll selection logic much. Since this touched all the shared_pll functions a bit I've also done an s/intel_crtc/crtc/ replacement on a few of them. v2: Kill DPLL_ID_NONE. It's probably better to call it DPLL_ID_INVALID and use it to check that the compute config stage assigns a dpll to every pipe. But since that code isn't ready yet until we move the dpll selection out of the ->mode_set callback, there's no use for it. Cc: Ville Syrjälä Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 7 +++ drivers/gpu/drm/i915/intel_display.c | 90 ++++++++++++++++++++---------------- drivers/gpu/drm/i915/intel_drv.h | 2 +- 3 files changed, 58 insertions(+), 41 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index f69ba390a649..d83c80a9c0b9 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -140,6 +140,13 @@ struct intel_shared_dpll { int fp0_reg; int fp1_reg; }; + +enum intel_dpll_id { + DPLL_ID_PRIVATE = -1, /* non-shared dpll in use */ + /* real shared dpll ids must be >= 0 */ + DPLL_ID_PCH_PLL_A, + DPLL_ID_PCH_PLL_B, +}; #define I915_NUM_PLLS 2 /* Used by dp and fdi links */ diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index d138b302f275..60df867f6fc6 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -909,6 +909,17 @@ static void assert_pll(struct drm_i915_private *dev_priv, #define assert_pll_enabled(d, p) assert_pll(d, p, true) #define assert_pll_disabled(d, p) assert_pll(d, p, false) +static struct intel_shared_dpll * +intel_crtc_to_shared_dpll(struct intel_crtc *crtc) +{ + struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; + + if (crtc->shared_dpll < 0) + return NULL; + + return &dev_priv->shared_dplls[crtc->shared_dpll]; +} + /* For ILK+ */ static void assert_shared_dpll(struct drm_i915_private *dev_priv, struct intel_shared_dpll *pll, @@ -1404,16 +1415,15 @@ void vlv_wait_port_ready(struct drm_i915_private *dev_priv, int port) * The PCH PLL needs to be enabled before the PCH transcoder, since it * drives the transcoder clock. */ -static void ironlake_enable_shared_dpll(struct intel_crtc *intel_crtc) +static void ironlake_enable_shared_dpll(struct intel_crtc *crtc) { - struct drm_i915_private *dev_priv = intel_crtc->base.dev->dev_private; - struct intel_shared_dpll *pll; + struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; + struct intel_shared_dpll *pll = intel_crtc_to_shared_dpll(crtc); int reg; u32 val; /* PCH PLLs only available on ILK, SNB and IVB */ BUG_ON(dev_priv->info->gen < 5); - pll = intel_crtc->shared_dpll; if (pll == NULL) return; @@ -1422,7 +1432,7 @@ static void ironlake_enable_shared_dpll(struct intel_crtc *intel_crtc) DRM_DEBUG_KMS("enable PCH PLL %x (active %d, on? %d)for crtc %d\n", pll->pll_reg, pll->active, pll->on, - intel_crtc->base.base.id); + crtc->base.base.id); /* PCH refclock must be enabled first */ assert_pch_refclk_enabled(dev_priv); @@ -1446,10 +1456,10 @@ static void ironlake_enable_shared_dpll(struct intel_crtc *intel_crtc) pll->on = true; } -static void intel_disable_shared_dpll(struct intel_crtc *intel_crtc) +static void intel_disable_shared_dpll(struct intel_crtc *crtc) { - struct drm_i915_private *dev_priv = intel_crtc->base.dev->dev_private; - struct intel_shared_dpll *pll = intel_crtc->shared_dpll; + struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; + struct intel_shared_dpll *pll = intel_crtc_to_shared_dpll(crtc); int reg; u32 val; @@ -1463,7 +1473,7 @@ static void intel_disable_shared_dpll(struct intel_crtc *intel_crtc) DRM_DEBUG_KMS("disable PCH PLL %x (active %d, on? %d) for crtc %d\n", pll->pll_reg, pll->active, pll->on, - intel_crtc->base.base.id); + crtc->base.base.id); if (WARN_ON(pll->active == 0)) { assert_shared_dpll_disabled(dev_priv, pll, NULL); @@ -1478,7 +1488,7 @@ static void intel_disable_shared_dpll(struct intel_crtc *intel_crtc) DRM_DEBUG_KMS("disabling PCH PLL %x\n", pll->pll_reg); /* Make sure transcoder isn't still depending on us */ - assert_pch_transcoder_disabled(dev_priv, intel_crtc->pipe); + assert_pch_transcoder_disabled(dev_priv, crtc->pipe); reg = pll->pll_reg; val = I915_READ(reg); @@ -1495,6 +1505,7 @@ static void ironlake_enable_pch_transcoder(struct drm_i915_private *dev_priv, { struct drm_device *dev = dev_priv->dev; struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe]; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); uint32_t reg, val, pipeconf_val; /* PCH only available on ILK+ */ @@ -1502,8 +1513,8 @@ static void ironlake_enable_pch_transcoder(struct drm_i915_private *dev_priv, /* Make sure PCH DPLL is enabled */ assert_shared_dpll_enabled(dev_priv, - to_intel_crtc(crtc)->shared_dpll, - to_intel_crtc(crtc)); + intel_crtc_to_shared_dpll(intel_crtc), + intel_crtc); /* FDI must be feeding us bits for PCH ports */ assert_fdi_tx_enabled(dev_priv, pipe); @@ -2990,7 +3001,7 @@ static void ironlake_pch_enable(struct drm_crtc *crtc) sel = TRANSC_DPLLB_SEL; break; } - if (intel_crtc->shared_dpll->pll_reg == _PCH_DPLL_B) + if (intel_crtc->shared_dpll == DPLL_ID_PCH_PLL_B) temp |= sel; else temp &= ~sel; @@ -3059,9 +3070,9 @@ static void lpt_pch_enable(struct drm_crtc *crtc) lpt_enable_pch_transcoder(dev_priv, cpu_transcoder); } -static void intel_put_shared_dpll(struct intel_crtc *intel_crtc) +static void intel_put_shared_dpll(struct intel_crtc *crtc) { - struct intel_shared_dpll *pll = intel_crtc->shared_dpll; + struct intel_shared_dpll *pll = intel_crtc_to_shared_dpll(crtc); if (pll == NULL) return; @@ -3076,29 +3087,28 @@ static void intel_put_shared_dpll(struct intel_crtc *intel_crtc) WARN_ON(pll->active); } - intel_crtc->shared_dpll = NULL; + crtc->shared_dpll = DPLL_ID_PRIVATE; } -static struct intel_shared_dpll *intel_get_shared_dpll(struct intel_crtc *intel_crtc, u32 dpll, u32 fp) +static struct intel_shared_dpll *intel_get_shared_dpll(struct intel_crtc *crtc, u32 dpll, u32 fp) { - struct drm_i915_private *dev_priv = intel_crtc->base.dev->dev_private; - struct intel_shared_dpll *pll; - int i; + struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; + struct intel_shared_dpll *pll = intel_crtc_to_shared_dpll(crtc); + enum intel_dpll_id i; - pll = intel_crtc->shared_dpll; if (pll) { DRM_DEBUG_KMS("CRTC:%d dropping existing PCH PLL %x\n", - intel_crtc->base.base.id, pll->pll_reg); - intel_put_shared_dpll(intel_crtc); + crtc->base.base.id, pll->pll_reg); + intel_put_shared_dpll(crtc); } if (HAS_PCH_IBX(dev_priv->dev)) { /* Ironlake PCH has a fixed PLL->PCH pipe mapping. */ - i = intel_crtc->pipe; + i = crtc->pipe; pll = &dev_priv->shared_dplls[i]; DRM_DEBUG_KMS("CRTC:%d using pre-allocated PCH PLL %x\n", - intel_crtc->base.base.id, pll->pll_reg); + crtc->base.base.id, pll->pll_reg); goto found; } @@ -3113,7 +3123,7 @@ static struct intel_shared_dpll *intel_get_shared_dpll(struct intel_crtc *intel_ if (dpll == (I915_READ(pll->pll_reg) & 0x7fffffff) && fp == I915_READ(pll->fp0_reg)) { DRM_DEBUG_KMS("CRTC:%d sharing existing PCH PLL %x (refcount %d, ative %d)\n", - intel_crtc->base.base.id, + crtc->base.base.id, pll->pll_reg, pll->refcount, pll->active); goto found; @@ -3125,7 +3135,7 @@ static struct intel_shared_dpll *intel_get_shared_dpll(struct intel_crtc *intel_ pll = &dev_priv->shared_dplls[i]; if (pll->refcount == 0) { DRM_DEBUG_KMS("CRTC:%d allocated PCH PLL %x\n", - intel_crtc->base.base.id, pll->pll_reg); + crtc->base.base.id, pll->pll_reg); goto found; } } @@ -3133,8 +3143,8 @@ static struct intel_shared_dpll *intel_get_shared_dpll(struct intel_crtc *intel_ return NULL; found: - intel_crtc->shared_dpll = pll; - DRM_DEBUG_DRIVER("using pll %d for pipe %c\n", i, pipe_name(intel_crtc->pipe)); + crtc->shared_dpll = i; + DRM_DEBUG_DRIVER("using pll %d for pipe %c\n", i, pipe_name(crtc->pipe)); if (pll->active == 0) { DRM_DEBUG_DRIVER("setting up pll %d\n", i); WARN_ON(pll->on); @@ -5730,6 +5740,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, bool ok, has_reduced_clock = false; bool is_lvds = false; struct intel_encoder *encoder; + struct intel_shared_dpll *pll; int ret; for_each_encoder_on_crtc(dev, crtc, encoder) { @@ -5765,8 +5776,6 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, /* CPU eDP is the only output that doesn't need a PCH PLL of its own. */ if (intel_crtc->config.has_pch_encoder) { - struct intel_shared_dpll *pll; - fp = i9xx_dpll_compute_fp(&intel_crtc->config.dpll); if (has_reduced_clock) fp2 = i9xx_dpll_compute_fp(&reduced_clock); @@ -5791,11 +5800,15 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, if (encoder->pre_pll_enable) encoder->pre_pll_enable(encoder); - if (intel_crtc->shared_dpll) { - I915_WRITE(intel_crtc->shared_dpll->pll_reg, dpll); + intel_crtc->lowfreq_avail = false; + + if (intel_crtc->config.has_pch_encoder) { + pll = intel_crtc_to_shared_dpll(intel_crtc); + + I915_WRITE(pll->pll_reg, dpll); /* Wait for the clocks to stabilize. */ - POSTING_READ(intel_crtc->shared_dpll->pll_reg); + POSTING_READ(pll->pll_reg); udelay(150); /* The pixel multiplier can only be updated once the @@ -5803,16 +5816,13 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, * * So write it again. */ - I915_WRITE(intel_crtc->shared_dpll->pll_reg, dpll); - } + I915_WRITE(pll->pll_reg, dpll); - intel_crtc->lowfreq_avail = false; - if (intel_crtc->shared_dpll) { if (is_lvds && has_reduced_clock && i915_powersave) { - I915_WRITE(intel_crtc->shared_dpll->fp1_reg, fp2); + I915_WRITE(pll->fp1_reg, fp2); intel_crtc->lowfreq_avail = true; } else { - I915_WRITE(intel_crtc->shared_dpll->fp1_reg, fp); + I915_WRITE(pll->fp1_reg, fp); } } diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index b094260f1e2b..1d4ec204b712 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -317,7 +317,7 @@ struct intel_crtc { struct intel_crtc_config config; /* We can share PLLs across outputs if the timings match */ - struct intel_shared_dpll *shared_dpll; + enum intel_dpll_id shared_dpll; uint32_t ddi_pll_sel; /* reset counter value when the last flip was submitted */ -- cgit v1.2.3 From a43f6e0fd6219e806268d5fef67db722875393a0 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 7 Jun 2013 23:10:32 +0200 Subject: drm/i915: move shared_dpll into the pipe config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the big sed-job prep work done this is now really simple. With the exception that we only assign the right shared dpll id in the ->mode_set callback but also depend upon the old one still being around. Until that mess is fixed up we need to jump through a few hoops to keep the old value save. v2: Kill the funny whitespace spotted by Chris. v3: Move the shared_dpll pipe config fixup into this patch as noticed by Ville. Also unconditionally set the shared_dpll with the current one, since otherwise we won't handle direct pch port -> cpu edp transitions correctly. Cc: Ville Syrjälä Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 26 +++++++++++++++----------- drivers/gpu/drm/i915/intel_drv.h | 5 +++-- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 60df867f6fc6..a5ccce07386b 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -914,10 +914,10 @@ intel_crtc_to_shared_dpll(struct intel_crtc *crtc) { struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; - if (crtc->shared_dpll < 0) + if (crtc->config.shared_dpll < 0) return NULL; - return &dev_priv->shared_dplls[crtc->shared_dpll]; + return &dev_priv->shared_dplls[crtc->config.shared_dpll]; } /* For ILK+ */ @@ -3001,7 +3001,7 @@ static void ironlake_pch_enable(struct drm_crtc *crtc) sel = TRANSC_DPLLB_SEL; break; } - if (intel_crtc->shared_dpll == DPLL_ID_PCH_PLL_B) + if (intel_crtc->config.shared_dpll == DPLL_ID_PCH_PLL_B) temp |= sel; else temp &= ~sel; @@ -3087,7 +3087,7 @@ static void intel_put_shared_dpll(struct intel_crtc *crtc) WARN_ON(pll->active); } - crtc->shared_dpll = DPLL_ID_PRIVATE; + crtc->config.shared_dpll = DPLL_ID_PRIVATE; } static struct intel_shared_dpll *intel_get_shared_dpll(struct intel_crtc *crtc, u32 dpll, u32 fp) @@ -3143,7 +3143,7 @@ static struct intel_shared_dpll *intel_get_shared_dpll(struct intel_crtc *crtc, return NULL; found: - crtc->shared_dpll = i; + crtc->config.shared_dpll = i; DRM_DEBUG_DRIVER("using pll %d for pipe %c\n", i, pipe_name(crtc->pipe)); if (pll->active == 0) { DRM_DEBUG_DRIVER("setting up pll %d\n", i); @@ -4106,12 +4106,11 @@ static void hsw_compute_ips_config(struct intel_crtc *crtc, pipe_config->pipe_bpp == 24; } -static int intel_crtc_compute_config(struct drm_crtc *crtc, +static int intel_crtc_compute_config(struct intel_crtc *crtc, struct intel_crtc_config *pipe_config) { - struct drm_device *dev = crtc->dev; + struct drm_device *dev = crtc->base.dev; struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode; - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); if (HAS_PCH_SPLIT(dev)) { /* FDI link clock is fixed at 2.7G */ @@ -4142,10 +4141,15 @@ static int intel_crtc_compute_config(struct drm_crtc *crtc, } if (IS_HASWELL(dev)) - hsw_compute_ips_config(intel_crtc, pipe_config); + hsw_compute_ips_config(crtc, pipe_config); + + /* XXX: PCH clock sharing is done in ->mode_set, so make sure the old + * clock survives for now. */ + if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) + pipe_config->shared_dpll = crtc->config.shared_dpll; if (pipe_config->has_pch_encoder) - return ironlake_fdi_compute_config(intel_crtc, pipe_config); + return ironlake_fdi_compute_config(crtc, pipe_config); return 0; } @@ -7910,7 +7914,7 @@ encoder_retry: if (!pipe_config->port_clock) pipe_config->port_clock = pipe_config->adjusted_mode.clock; - ret = intel_crtc_compute_config(crtc, pipe_config); + ret = intel_crtc_compute_config(to_intel_crtc(crtc), pipe_config); if (ret < 0) { DRM_DEBUG_KMS("CRTC fixup failed\n"); goto fail; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 1d4ec204b712..b77df049b773 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -252,6 +252,9 @@ struct intel_crtc_config { * haswell. */ struct dpll dpll; + /* Selected dpll when shared or DPLL_ID_PRIVATE. */ + enum intel_dpll_id shared_dpll; + int pipe_bpp; struct intel_link_m_n dp_m_n; @@ -316,8 +319,6 @@ struct intel_crtc { struct intel_crtc_config config; - /* We can share PLLs across outputs if the timings match */ - enum intel_dpll_id shared_dpll; uint32_t ddi_pll_sel; /* reset counter value when the last flip was submitted */ -- cgit v1.2.3 From 1188739757d0e78810de5fe83dbe0128f624b9e8 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 5 Jun 2013 13:34:09 +0200 Subject: drm/i915: refactor PCH_DPLL_SEL #defines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bits are evenly space, so we can cut down on two big switch blocks. This also greatly simplifies the hw state readout which follows in the next patch. Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 12 +++--------- drivers/gpu/drm/i915/intel_display.c | 32 +++----------------------------- 2 files changed, 6 insertions(+), 38 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index b1fdca9e96bb..99638fc6285b 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -3978,15 +3978,9 @@ #define PCH_SSC4_AUX_PARMS 0xc6214 #define PCH_DPLL_SEL 0xc7000 -#define TRANSA_DPLL_ENABLE (1<<3) -#define TRANSA_DPLLB_SEL (1<<0) -#define TRANSA_DPLLA_SEL 0 -#define TRANSB_DPLL_ENABLE (1<<7) -#define TRANSB_DPLLB_SEL (1<<4) -#define TRANSB_DPLLA_SEL (0) -#define TRANSC_DPLL_ENABLE (1<<11) -#define TRANSC_DPLLB_SEL (1<<8) -#define TRANSC_DPLLA_SEL (0) +#define TRANS_DPLLB_SEL(pipe) (1 << (pipe * 4)) +#define TRANS_DPLLA_SEL(pipe) 0 +#define TRANS_DPLL_ENABLE(pipe) (1 << (pipe * 4 + 3)) /* transcoder */ diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index a5ccce07386b..d5932ef76cde 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2986,21 +2986,8 @@ static void ironlake_pch_enable(struct drm_crtc *crtc) u32 sel; temp = I915_READ(PCH_DPLL_SEL); - switch (pipe) { - default: - case 0: - temp |= TRANSA_DPLL_ENABLE; - sel = TRANSA_DPLLB_SEL; - break; - case 1: - temp |= TRANSB_DPLL_ENABLE; - sel = TRANSB_DPLLB_SEL; - break; - case 2: - temp |= TRANSC_DPLL_ENABLE; - sel = TRANSC_DPLLB_SEL; - break; - } + temp |= TRANS_DPLL_ENABLE(pipe); + sel = TRANS_DPLLB_SEL(pipe); if (intel_crtc->config.shared_dpll == DPLL_ID_PCH_PLL_B) temp |= sel; else @@ -3480,20 +3467,7 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) /* disable DPLL_SEL */ temp = I915_READ(PCH_DPLL_SEL); - switch (pipe) { - case 0: - temp &= ~(TRANSA_DPLL_ENABLE | TRANSA_DPLLB_SEL); - break; - case 1: - temp &= ~(TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL); - break; - case 2: - /* C shares PLL A or B */ - temp &= ~(TRANSC_DPLL_ENABLE | TRANSC_DPLLB_SEL); - break; - default: - BUG(); /* wtf */ - } + temp &= ~(TRANS_DPLL_ENABLE(pipe) | TRANS_DPLLB_SEL(pipe)); I915_WRITE(PCH_DPLL_SEL, temp); } -- cgit v1.2.3 From c0d43d62233b3d4523a62fe88896bfc7a609e572 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 7 Jun 2013 23:11:08 +0200 Subject: drm/i915: hw state readout for shared pch plls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Well, the first step of a long road at least, it only reads out the pipe -> shared dpll association thus far. Other state which needs to follow: - hw state of the dpll (on/off + dpll registers). Currently we just read that out from the hw state, but that doesn't work too well when the dpll is in use, but not yet fully enabled. We get away since most likely it already has been enabled and so the correct state is left behind in the registers. But that doesn't hold for atomic modesets when we want to enable all pipes at once. - Refcount reconstruction for each dpll. - Cross-checking of all the above. For that we need to keep the dpll register state both in the pipe and in the shared_dpll struct, so that we can check that every pipe is still connected to a correctly configured dpll. Note that since the refcount resconstruction isn't done yet this will spill a few WARNs at boot-up while trying to disable pch plls which have bogus refcounts. But since there's still a pile of refactoring to do I'd like to lock down the state handling as soon as possible hence decided against reordering the patches to quiet these WARNs - after all the issues they're complaining about have existed since forever, as Jesse can testify by having pch pll states blow up consistently in his fastboot patches ... v2: We need to preserve the old shared_dpll since currently the shared dpll refcount dropping/getting is done in ->mode_set. With the usual pipe_config infrastructure the old dpll id is already lost at that point, hence preserve it in the new config. v3: Rebase on top of the ips patch from Paulo. v4: We need to unconditionally take over the shared_dpll id from the old pipe config when e.g. doing a direct pch port -> cpu edp transition. v5: Move the saving of the old shared_dpll id to an ealier patch. Cc: Ville Syrjälä Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index d5932ef76cde..51670ba56611 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4998,6 +4998,7 @@ static bool i9xx_get_pipe_config(struct intel_crtc *crtc, uint32_t tmp; pipe_config->cpu_transcoder = crtc->pipe; + pipe_config->shared_dpll = DPLL_ID_PRIVATE; tmp = I915_READ(PIPECONF(crtc->pipe)); if (!(tmp & PIPECONF_ENABLE)) @@ -5874,6 +5875,7 @@ static bool ironlake_get_pipe_config(struct intel_crtc *crtc, uint32_t tmp; pipe_config->cpu_transcoder = crtc->pipe; + pipe_config->shared_dpll = DPLL_ID_PRIVATE; tmp = I915_READ(PIPECONF(crtc->pipe)); if (!(tmp & PIPECONF_ENABLE)) @@ -5891,6 +5893,16 @@ static bool ironlake_get_pipe_config(struct intel_crtc *crtc, /* XXX: Can't properly read out the pch dpll pixel multiplier * since we don't have state tracking for pch clocks yet. */ pipe_config->pixel_multiplier = 1; + + if (HAS_PCH_IBX(dev_priv->dev)) { + pipe_config->shared_dpll = crtc->pipe; + } else { + tmp = I915_READ(PCH_DPLL_SEL); + if (tmp & TRANS_DPLLB_SEL(crtc->pipe)) + pipe_config->shared_dpll = DPLL_ID_PCH_PLL_B; + else + pipe_config->shared_dpll = DPLL_ID_PCH_PLL_A; + } } else { pipe_config->pixel_multiplier = 1; } @@ -5971,6 +5983,8 @@ static bool haswell_get_pipe_config(struct intel_crtc *crtc, uint32_t tmp; pipe_config->cpu_transcoder = crtc->pipe; + pipe_config->shared_dpll = DPLL_ID_PRIVATE; + tmp = I915_READ(TRANS_DDI_FUNC_CTL(TRANSCODER_EDP)); if (tmp & TRANS_DDI_FUNC_ENABLE) { enum pipe trans_edp_pipe; @@ -7840,6 +7854,7 @@ intel_modeset_pipe_config(struct drm_crtc *crtc, drm_mode_copy(&pipe_config->adjusted_mode, mode); drm_mode_copy(&pipe_config->requested_mode, mode); pipe_config->cpu_transcoder = to_intel_crtc(crtc)->pipe; + pipe_config->shared_dpll = DPLL_ID_PRIVATE; /* Compute a starting value for pipe_config->pipe_bpp taking the source * plane pixel format and any sink constraints into account. Returns the @@ -8159,6 +8174,8 @@ intel_pipe_config_compare(struct drm_device *dev, PIPE_CONF_CHECK_I(ips_enabled); + PIPE_CONF_CHECK_I(shared_dpll); + #undef PIPE_CONF_CHECK_I #undef PIPE_CONF_CHECK_FLAGS #undef PIPE_CONF_QUIRK -- cgit v1.2.3 From efca406b940e93e6af38c597eecd5fb79b39b7ea Mon Sep 17 00:00:00 2001 From: Andrii Tseglytskyi Date: Thu, 30 May 2013 13:43:56 +0300 Subject: PM / AVS: SmartReflex: use devm_* API to initialize SmartReflex Use of of devm_* API for resource allocation provides benefits such as auto handling of resource free. This reduces possibility have memory leaks in case of wrong error handling. All direct release calls should be removed to avoid races. Reported-by: Grygorii Strashko Signed-off-by: Andrii Tseglytskyi Signed-off-by: Kevin Hilman --- drivers/power/avs/smartreflex.c | 76 ++++++++++++----------------------------- 1 file changed, 21 insertions(+), 55 deletions(-) diff --git a/drivers/power/avs/smartreflex.c b/drivers/power/avs/smartreflex.c index 14cce7addbb9..db9973bb53f1 100644 --- a/drivers/power/avs/smartreflex.c +++ b/drivers/power/avs/smartreflex.c @@ -28,7 +28,7 @@ #include #define DRIVER_NAME "smartreflex" -#define SMARTREFLEX_NAME_LEN 16 +#define SMARTREFLEX_NAME_LEN 32 #define NVALUE_NAME_LEN 40 #define SR_DISABLE_TIMEOUT 200 @@ -208,12 +208,11 @@ static void sr_stop_vddautocomp(struct omap_sr *sr) static int sr_late_init(struct omap_sr *sr_info) { struct omap_sr_data *pdata = sr_info->pdev->dev.platform_data; - struct resource *mem; int ret = 0; if (sr_class->notify && sr_class->notify_flags && sr_info->irq) { - ret = request_irq(sr_info->irq, sr_interrupt, - 0, sr_info->name, sr_info); + ret = devm_request_irq(&sr_info->pdev->dev, sr_info->irq, + sr_interrupt, 0, sr_info->name, sr_info); if (ret) goto error; disable_irq(sr_info->irq); @@ -225,14 +224,10 @@ static int sr_late_init(struct omap_sr *sr_info) return ret; error: - iounmap(sr_info->base); - mem = platform_get_resource(sr_info->pdev, IORESOURCE_MEM, 0); - release_mem_region(mem->start, resource_size(mem)); list_del(&sr_info->node); dev_err(&sr_info->pdev->dev, "%s: ERROR in registering" "interrupt handler. Smartreflex will" "not function as desired\n", __func__); - kfree(sr_info); return ret; } @@ -852,34 +847,33 @@ static int __init omap_sr_probe(struct platform_device *pdev) struct dentry *nvalue_dir; int i, ret = 0; - sr_info = kzalloc(sizeof(struct omap_sr), GFP_KERNEL); + sr_info = devm_kzalloc(&pdev->dev, sizeof(struct omap_sr), GFP_KERNEL); if (!sr_info) { dev_err(&pdev->dev, "%s: unable to allocate sr_info\n", __func__); return -ENOMEM; } + sr_info->name = devm_kzalloc(&pdev->dev, + SMARTREFLEX_NAME_LEN, GFP_KERNEL); + if (!sr_info->name) { + dev_err(&pdev->dev, "%s: unable to allocate SR instance name\n", + __func__); + return -ENOMEM; + } + platform_set_drvdata(pdev, sr_info); if (!pdata) { dev_err(&pdev->dev, "%s: platform data missing\n", __func__); - ret = -EINVAL; - goto err_free_devinfo; + return -EINVAL; } mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) { - dev_err(&pdev->dev, "%s: no mem resource\n", __func__); - ret = -ENODEV; - goto err_free_devinfo; - } - - mem = request_mem_region(mem->start, resource_size(mem), - dev_name(&pdev->dev)); - if (!mem) { - dev_err(&pdev->dev, "%s: no mem region\n", __func__); - ret = -EBUSY; - goto err_free_devinfo; + sr_info->base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(sr_info->base)) { + dev_err(&pdev->dev, "%s: ioremap fail\n", __func__); + return PTR_ERR(sr_info->base); } irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); @@ -887,13 +881,7 @@ static int __init omap_sr_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); pm_runtime_irq_safe(&pdev->dev); - sr_info->name = kasprintf(GFP_KERNEL, "%s", pdata->name); - if (!sr_info->name) { - dev_err(&pdev->dev, "%s: Unable to alloc SR instance name\n", - __func__); - ret = -ENOMEM; - goto err_release_region; - } + snprintf(sr_info->name, SMARTREFLEX_NAME_LEN, "%s", pdata->name); sr_info->pdev = pdev; sr_info->srid = pdev->id; @@ -910,13 +898,6 @@ static int __init omap_sr_probe(struct platform_device *pdev) sr_info->autocomp_active = false; sr_info->ip_type = pdata->ip_type; - sr_info->base = ioremap(mem->start, resource_size(mem)); - if (!sr_info->base) { - dev_err(&pdev->dev, "%s: ioremap fail\n", __func__); - ret = -ENOMEM; - goto err_free_name; - } - if (irq) sr_info->irq = irq->start; @@ -932,7 +913,7 @@ static int __init omap_sr_probe(struct platform_device *pdev) ret = sr_late_init(sr_info); if (ret) { pr_warning("%s: Error in SR late init\n", __func__); - goto err_iounmap; + goto err_list_del; } } @@ -943,7 +924,7 @@ static int __init omap_sr_probe(struct platform_device *pdev) ret = PTR_ERR(sr_dbg_dir); pr_err("%s:sr debugfs dir creation failed(%d)\n", __func__, ret); - goto err_iounmap; + goto err_list_del; } } @@ -996,16 +977,8 @@ static int __init omap_sr_probe(struct platform_device *pdev) err_debugfs: debugfs_remove_recursive(sr_info->dbg_dir); -err_iounmap: +err_list_del: list_del(&sr_info->node); - iounmap(sr_info->base); -err_free_name: - kfree(sr_info->name); -err_release_region: - release_mem_region(mem->start, resource_size(mem)); -err_free_devinfo: - kfree(sr_info); - return ret; } @@ -1013,7 +986,6 @@ static int omap_sr_remove(struct platform_device *pdev) { struct omap_sr_data *pdata = pdev->dev.platform_data; struct omap_sr *sr_info; - struct resource *mem; if (!pdata) { dev_err(&pdev->dev, "%s: platform data missing\n", __func__); @@ -1034,12 +1006,6 @@ static int omap_sr_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); list_del(&sr_info->node); - iounmap(sr_info->base); - kfree(sr_info->name); - kfree(sr_info); - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(mem->start, resource_size(mem)); - return 0; } -- cgit v1.2.3 From 7c74ade1de5b5311e7c886de27aa54e3285bd220 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 5 Jun 2013 13:34:11 +0200 Subject: drm/i915: consolidate ->num_shared_dplls assignement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the future this won't be just for pch plls, so move it into the shared dpll init code. v2: Bikeshed the uncessary {} away while applying to appease checkpatch. Reviewed-by: Ville Syrjälä (v1) Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.c | 7 ------- drivers/gpu/drm/i915/intel_display.c | 21 ++++++++++++++++----- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index b0a4e6834c1d..c3e4f2915e5b 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -433,7 +433,6 @@ void intel_detect_pch(struct drm_device *dev) */ if (INTEL_INFO(dev)->num_pipes == 0) { dev_priv->pch_type = PCH_NOP; - dev_priv->num_shared_dpll = 0; return; } @@ -452,34 +451,28 @@ void intel_detect_pch(struct drm_device *dev) if (id == INTEL_PCH_IBX_DEVICE_ID_TYPE) { dev_priv->pch_type = PCH_IBX; - dev_priv->num_shared_dpll = 2; DRM_DEBUG_KMS("Found Ibex Peak PCH\n"); WARN_ON(!IS_GEN5(dev)); } else if (id == INTEL_PCH_CPT_DEVICE_ID_TYPE) { dev_priv->pch_type = PCH_CPT; - dev_priv->num_shared_dpll = 2; DRM_DEBUG_KMS("Found CougarPoint PCH\n"); WARN_ON(!(IS_GEN6(dev) || IS_IVYBRIDGE(dev))); } else if (id == INTEL_PCH_PPT_DEVICE_ID_TYPE) { /* PantherPoint is CPT compatible */ dev_priv->pch_type = PCH_CPT; - dev_priv->num_shared_dpll = 2; DRM_DEBUG_KMS("Found PatherPoint PCH\n"); WARN_ON(!(IS_GEN6(dev) || IS_IVYBRIDGE(dev))); } else if (id == INTEL_PCH_LPT_DEVICE_ID_TYPE) { dev_priv->pch_type = PCH_LPT; - dev_priv->num_shared_dpll = 0; DRM_DEBUG_KMS("Found LynxPoint PCH\n"); WARN_ON(!IS_HASWELL(dev)); WARN_ON(IS_ULT(dev)); } else if (id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE) { dev_priv->pch_type = PCH_LPT; - dev_priv->num_shared_dpll = 0; DRM_DEBUG_KMS("Found LynxPoint LP PCH\n"); WARN_ON(!IS_HASWELL(dev)); WARN_ON(!IS_ULT(dev)); } - BUG_ON(dev_priv->num_shared_dpll > I915_NUM_PLLS); } pci_dev_put(pch); } diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 51670ba56611..665602a13ea4 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8728,15 +8728,12 @@ static void intel_cpu_pll_init(struct drm_device *dev) intel_ddi_pll_init(dev); } -static void intel_shared_dpll_init(struct drm_device *dev) +static void ibx_pch_dpll_init(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; int i; - if (dev_priv->num_shared_dpll == 0) { - DRM_DEBUG_KMS("No PCH PLLs on this hardware, skipping initialisation\n"); - return; - } + dev_priv->num_shared_dpll = 2; for (i = 0; i < dev_priv->num_shared_dpll; i++) { dev_priv->shared_dplls[i].pll_reg = _PCH_DPLL(i); @@ -8745,6 +8742,20 @@ static void intel_shared_dpll_init(struct drm_device *dev) } } +static void intel_shared_dpll_init(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + + if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) + ibx_pch_dpll_init(dev); + else + dev_priv->num_shared_dpll = 0; + + BUG_ON(dev_priv->num_shared_dpll > I915_NUM_PLLS); + DRM_DEBUG_KMS("%i shared PLLs initialized\n", + dev_priv->num_shared_dpll); +} + static void intel_crtc_init(struct drm_device *dev, int pipe) { drm_i915_private_t *dev_priv = dev->dev_private; -- cgit v1.2.3 From 46edb027df0d4bd423f7430dd473609caad4674b Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 5 Jun 2013 13:34:12 +0200 Subject: drm/i915: metadata for shared dplls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An id to match the idx (useful for register access macros) and a name fore neater debug output. Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 19 ++++++++------ drivers/gpu/drm/i915/intel_display.c | 48 +++++++++++++++++++++--------------- 2 files changed, 39 insertions(+), 28 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index d83c80a9c0b9..0fc8d616cefb 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -132,23 +132,26 @@ enum hpd_pin { list_for_each_entry((intel_encoder), &(dev)->mode_config.encoder_list, base.head) \ if ((intel_encoder)->base.crtc == (__crtc)) +enum intel_dpll_id { + DPLL_ID_PRIVATE = -1, /* non-shared dpll in use */ + /* real shared dpll ids must be >= 0 */ + DPLL_ID_PCH_PLL_A, + DPLL_ID_PCH_PLL_B, +}; +#define I915_NUM_PLLS 2 + struct intel_shared_dpll { int refcount; /* count of number of CRTCs sharing this PLL */ int active; /* count of number of active CRTCs (i.e. DPMS on) */ bool on; /* is the PLL actually active? Disabled during modeset */ + const char *name; + /* should match the index in the dev_priv->shared_dplls array */ + enum intel_dpll_id id; int pll_reg; int fp0_reg; int fp1_reg; }; -enum intel_dpll_id { - DPLL_ID_PRIVATE = -1, /* non-shared dpll in use */ - /* real shared dpll ids must be >= 0 */ - DPLL_ID_PCH_PLL_A, - DPLL_ID_PCH_PLL_B, -}; -#define I915_NUM_PLLS 2 - /* Used by dp and fdi links */ struct intel_link_m_n { uint32_t tu; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 665602a13ea4..1ee16e9195b9 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -935,14 +935,14 @@ static void assert_shared_dpll(struct drm_i915_private *dev_priv, } if (WARN (!pll, - "asserting PCH PLL %s with no PLL\n", state_string(state))) + "asserting DPLL %s with no DPLL\n", state_string(state))) return; val = I915_READ(pll->pll_reg); cur_state = !!(val & DPLL_VCO_ENABLE); WARN(cur_state != state, - "PCH PLL state for reg %x assertion failure (expected %s, current %s), val=%08x\n", - pll->pll_reg, state_string(state), state_string(cur_state), val); + "%s assertion failure (expected %s, current %s), val=%08x\n", + pll->name, state_string(state), state_string(cur_state), val); /* Make sure the selected PLL is correctly attached to the transcoder */ if (crtc && HAS_PCH_CPT(dev_priv->dev)) { @@ -1430,8 +1430,8 @@ static void ironlake_enable_shared_dpll(struct intel_crtc *crtc) if (WARN_ON(pll->refcount == 0)) return; - DRM_DEBUG_KMS("enable PCH PLL %x (active %d, on? %d)for crtc %d\n", - pll->pll_reg, pll->active, pll->on, + DRM_DEBUG_KMS("enable %s (active %d, on? %d)for crtc %d\n", + pll->name, pll->active, pll->on, crtc->base.base.id); /* PCH refclock must be enabled first */ @@ -1444,7 +1444,7 @@ static void ironlake_enable_shared_dpll(struct intel_crtc *crtc) } WARN_ON(pll->on); - DRM_DEBUG_KMS("enabling PCH PLL %x\n", pll->pll_reg); + DRM_DEBUG_KMS("enabling %s\n", pll->name); reg = pll->pll_reg; val = I915_READ(reg); @@ -1471,8 +1471,8 @@ static void intel_disable_shared_dpll(struct intel_crtc *crtc) if (WARN_ON(pll->refcount == 0)) return; - DRM_DEBUG_KMS("disable PCH PLL %x (active %d, on? %d) for crtc %d\n", - pll->pll_reg, pll->active, pll->on, + DRM_DEBUG_KMS("disable %s (active %d, on? %d) for crtc %d\n", + pll->name, pll->active, pll->on, crtc->base.base.id); if (WARN_ON(pll->active == 0)) { @@ -1485,7 +1485,7 @@ static void intel_disable_shared_dpll(struct intel_crtc *crtc) if (--pll->active) return; - DRM_DEBUG_KMS("disabling PCH PLL %x\n", pll->pll_reg); + DRM_DEBUG_KMS("disabling %s\n", pll->name); /* Make sure transcoder isn't still depending on us */ assert_pch_transcoder_disabled(dev_priv, crtc->pipe); @@ -3065,7 +3065,7 @@ static void intel_put_shared_dpll(struct intel_crtc *crtc) return; if (pll->refcount == 0) { - WARN(1, "bad PCH PLL refcount\n"); + WARN(1, "bad %s refcount\n", pll->name); return; } @@ -3084,8 +3084,8 @@ static struct intel_shared_dpll *intel_get_shared_dpll(struct intel_crtc *crtc, enum intel_dpll_id i; if (pll) { - DRM_DEBUG_KMS("CRTC:%d dropping existing PCH PLL %x\n", - crtc->base.base.id, pll->pll_reg); + DRM_DEBUG_KMS("CRTC:%d dropping existing %s\n", + crtc->base.base.id, pll->name); intel_put_shared_dpll(crtc); } @@ -3094,8 +3094,8 @@ static struct intel_shared_dpll *intel_get_shared_dpll(struct intel_crtc *crtc, i = crtc->pipe; pll = &dev_priv->shared_dplls[i]; - DRM_DEBUG_KMS("CRTC:%d using pre-allocated PCH PLL %x\n", - crtc->base.base.id, pll->pll_reg); + DRM_DEBUG_KMS("CRTC:%d using pre-allocated %s\n", + crtc->base.base.id, pll->name); goto found; } @@ -3109,9 +3109,9 @@ static struct intel_shared_dpll *intel_get_shared_dpll(struct intel_crtc *crtc, if (dpll == (I915_READ(pll->pll_reg) & 0x7fffffff) && fp == I915_READ(pll->fp0_reg)) { - DRM_DEBUG_KMS("CRTC:%d sharing existing PCH PLL %x (refcount %d, ative %d)\n", + DRM_DEBUG_KMS("CRTC:%d sharing existing %s (refcount %d, ative %d)\n", crtc->base.base.id, - pll->pll_reg, pll->refcount, pll->active); + pll->name, pll->refcount, pll->active); goto found; } @@ -3121,8 +3121,8 @@ static struct intel_shared_dpll *intel_get_shared_dpll(struct intel_crtc *crtc, for (i = 0; i < dev_priv->num_shared_dpll; i++) { pll = &dev_priv->shared_dplls[i]; if (pll->refcount == 0) { - DRM_DEBUG_KMS("CRTC:%d allocated PCH PLL %x\n", - crtc->base.base.id, pll->pll_reg); + DRM_DEBUG_KMS("CRTC:%d allocated %s\n", + crtc->base.base.id, pll->name); goto found; } } @@ -3131,9 +3131,10 @@ static struct intel_shared_dpll *intel_get_shared_dpll(struct intel_crtc *crtc, found: crtc->config.shared_dpll = i; - DRM_DEBUG_DRIVER("using pll %d for pipe %c\n", i, pipe_name(crtc->pipe)); + DRM_DEBUG_DRIVER("using %s for pipe %c\n", pll->name, + pipe_name(crtc->pipe)); if (pll->active == 0) { - DRM_DEBUG_DRIVER("setting up pll %d\n", i); + DRM_DEBUG_DRIVER("setting up %s\n", pll->name); WARN_ON(pll->on); assert_shared_dpll_disabled(dev_priv, pll, NULL); @@ -8728,6 +8729,11 @@ static void intel_cpu_pll_init(struct drm_device *dev) intel_ddi_pll_init(dev); } +static char *ibx_pch_dpll_names[] = { + "PCH DPLL A", + "PCH DPLL B", +}; + static void ibx_pch_dpll_init(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; @@ -8736,6 +8742,8 @@ static void ibx_pch_dpll_init(struct drm_device *dev) dev_priv->num_shared_dpll = 2; for (i = 0; i < dev_priv->num_shared_dpll; i++) { + dev_priv->shared_dplls[i].id = i; + dev_priv->shared_dplls[i].name = ibx_pch_dpll_names[i]; dev_priv->shared_dplls[i].pll_reg = _PCH_DPLL(i); dev_priv->shared_dplls[i].fp0_reg = _PCH_FP0(i); dev_priv->shared_dplls[i].fp1_reg = _PCH_FP1(i); -- cgit v1.2.3 From e9a632a578e0205c1fb015bb01af49c2ae71d6f2 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 5 Jun 2013 13:34:13 +0200 Subject: drm/i915: scrap register address storage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using ids in register macros is much more common in our driver. Also this way we can reduce the platform specific stuff a bit. Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 3 --- drivers/gpu/drm/i915/i915_reg.h | 6 +++--- drivers/gpu/drm/i915/i915_ums.c | 2 +- drivers/gpu/drm/i915/intel_display.c | 35 ++++++++++++++++------------------- 4 files changed, 20 insertions(+), 26 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 0fc8d616cefb..bb1ad20e39a1 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -147,9 +147,6 @@ struct intel_shared_dpll { const char *name; /* should match the index in the dev_priv->shared_dplls array */ enum intel_dpll_id id; - int pll_reg; - int fp0_reg; - int fp1_reg; }; /* Used by dp and fdi links */ diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 99638fc6285b..01e8783f1a97 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -3930,15 +3930,15 @@ #define _PCH_DPLL_A 0xc6014 #define _PCH_DPLL_B 0xc6018 -#define _PCH_DPLL(pll) (pll == 0 ? _PCH_DPLL_A : _PCH_DPLL_B) +#define PCH_DPLL(pll) (pll == 0 ? _PCH_DPLL_A : _PCH_DPLL_B) #define _PCH_FPA0 0xc6040 #define FP_CB_TUNE (0x3<<22) #define _PCH_FPA1 0xc6044 #define _PCH_FPB0 0xc6048 #define _PCH_FPB1 0xc604c -#define _PCH_FP0(pll) (pll == 0 ? _PCH_FPA0 : _PCH_FPB0) -#define _PCH_FP1(pll) (pll == 0 ? _PCH_FPA1 : _PCH_FPB1) +#define PCH_FP0(pll) (pll == 0 ? _PCH_FPA0 : _PCH_FPB0) +#define PCH_FP1(pll) (pll == 0 ? _PCH_FPA1 : _PCH_FPB1) #define PCH_DPLL_TEST 0xc606c diff --git a/drivers/gpu/drm/i915/i915_ums.c b/drivers/gpu/drm/i915/i915_ums.c index 5ef30b2e6bc6..967da4772c44 100644 --- a/drivers/gpu/drm/i915/i915_ums.c +++ b/drivers/gpu/drm/i915/i915_ums.c @@ -41,7 +41,7 @@ static bool i915_pipe_enabled(struct drm_device *dev, enum pipe pipe) return false; if (HAS_PCH_SPLIT(dev)) - dpll_reg = _PCH_DPLL(pipe); + dpll_reg = PCH_DPLL(pipe); else dpll_reg = (pipe == PIPE_A) ? _DPLL_A : _DPLL_B; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 1ee16e9195b9..1825ca5466e8 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -938,7 +938,7 @@ static void assert_shared_dpll(struct drm_i915_private *dev_priv, "asserting DPLL %s with no DPLL\n", state_string(state))) return; - val = I915_READ(pll->pll_reg); + val = I915_READ(PCH_DPLL(pll->id)); cur_state = !!(val & DPLL_VCO_ENABLE); WARN(cur_state != state, "%s assertion failure (expected %s, current %s), val=%08x\n", @@ -949,14 +949,14 @@ static void assert_shared_dpll(struct drm_i915_private *dev_priv, u32 pch_dpll; pch_dpll = I915_READ(PCH_DPLL_SEL); - cur_state = pll->pll_reg == _PCH_DPLL_B; + cur_state = pll->id == DPLL_ID_PCH_PLL_B; if (!WARN(((pch_dpll >> (4 * crtc->pipe)) & 1) != cur_state, "PLL[%d] not attached to this transcoder %c: %08x\n", cur_state, pipe_name(crtc->pipe), pch_dpll)) { cur_state = !!(val >> (4*crtc->pipe + 3)); WARN(cur_state != state, "PLL[%d] not %s on this transcoder %c: %08x\n", - pll->pll_reg == _PCH_DPLL_B, + pll->id == DPLL_ID_PCH_PLL_B, state_string(state), pipe_name(crtc->pipe), val); @@ -1446,7 +1446,7 @@ static void ironlake_enable_shared_dpll(struct intel_crtc *crtc) DRM_DEBUG_KMS("enabling %s\n", pll->name); - reg = pll->pll_reg; + reg = PCH_DPLL(pll->id); val = I915_READ(reg); val |= DPLL_VCO_ENABLE; I915_WRITE(reg, val); @@ -1490,7 +1490,7 @@ static void intel_disable_shared_dpll(struct intel_crtc *crtc) /* Make sure transcoder isn't still depending on us */ assert_pch_transcoder_disabled(dev_priv, crtc->pipe); - reg = pll->pll_reg; + reg = PCH_DPLL(pll->id); val = I915_READ(reg); val &= ~DPLL_VCO_ENABLE; I915_WRITE(reg, val); @@ -3107,8 +3107,8 @@ static struct intel_shared_dpll *intel_get_shared_dpll(struct intel_crtc *crtc, if (pll->refcount == 0) continue; - if (dpll == (I915_READ(pll->pll_reg) & 0x7fffffff) && - fp == I915_READ(pll->fp0_reg)) { + if (dpll == (I915_READ(PCH_DPLL(pll->id)) & 0x7fffffff) && + fp == I915_READ(PCH_FP0(pll->id))) { DRM_DEBUG_KMS("CRTC:%d sharing existing %s (refcount %d, ative %d)\n", crtc->base.base.id, pll->name, pll->refcount, pll->active); @@ -3139,12 +3139,12 @@ found: assert_shared_dpll_disabled(dev_priv, pll, NULL); /* Wait for the clocks to stabilize before rewriting the regs */ - I915_WRITE(pll->pll_reg, dpll & ~DPLL_VCO_ENABLE); - POSTING_READ(pll->pll_reg); + I915_WRITE(PCH_DPLL(pll->id), dpll & ~DPLL_VCO_ENABLE); + POSTING_READ(PCH_DPLL(pll->id)); udelay(150); - I915_WRITE(pll->fp0_reg, fp); - I915_WRITE(pll->pll_reg, dpll & ~DPLL_VCO_ENABLE); + I915_WRITE(PCH_FP0(pll->id), fp); + I915_WRITE(PCH_DPLL(pll->id), dpll & ~DPLL_VCO_ENABLE); } pll->refcount++; @@ -5785,10 +5785,10 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, if (intel_crtc->config.has_pch_encoder) { pll = intel_crtc_to_shared_dpll(intel_crtc); - I915_WRITE(pll->pll_reg, dpll); + I915_WRITE(PCH_DPLL(pll->id), dpll); /* Wait for the clocks to stabilize. */ - POSTING_READ(pll->pll_reg); + POSTING_READ(PCH_DPLL(pll->id)); udelay(150); /* The pixel multiplier can only be updated once the @@ -5796,13 +5796,13 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, * * So write it again. */ - I915_WRITE(pll->pll_reg, dpll); + I915_WRITE(PCH_DPLL(pll->id), dpll); if (is_lvds && has_reduced_clock && i915_powersave) { - I915_WRITE(pll->fp1_reg, fp2); + I915_WRITE(PCH_FP1(pll->id), fp2); intel_crtc->lowfreq_avail = true; } else { - I915_WRITE(pll->fp1_reg, fp); + I915_WRITE(PCH_FP1(pll->id), fp); } } @@ -8744,9 +8744,6 @@ static void ibx_pch_dpll_init(struct drm_device *dev) for (i = 0; i < dev_priv->num_shared_dpll; i++) { dev_priv->shared_dplls[i].id = i; dev_priv->shared_dplls[i].name = ibx_pch_dpll_names[i]; - dev_priv->shared_dplls[i].pll_reg = _PCH_DPLL(i); - dev_priv->shared_dplls[i].fp0_reg = _PCH_FP0(i); - dev_priv->shared_dplls[i].fp1_reg = _PCH_FP1(i); } } -- cgit v1.2.3 From e7b903d2525fe3be12b6535a27915186529a51b4 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 5 Jun 2013 13:34:14 +0200 Subject: drm/i915: enable/disable hooks for shared dplls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Looks at first like a bit of overkill, but - Haswell actually wants different enable/disable functions for different plls. - And once we have full dpll hw state tracking we can move the full register setup into the ->enable hook. Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 7 +++- drivers/gpu/drm/i915/intel_display.c | 71 ++++++++++++++++++++++-------------- 2 files changed, 49 insertions(+), 29 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index bb1ad20e39a1..eaa04a6808cb 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -132,6 +132,8 @@ enum hpd_pin { list_for_each_entry((intel_encoder), &(dev)->mode_config.encoder_list, base.head) \ if ((intel_encoder)->base.crtc == (__crtc)) +struct drm_i915_private; + enum intel_dpll_id { DPLL_ID_PRIVATE = -1, /* non-shared dpll in use */ /* real shared dpll ids must be >= 0 */ @@ -147,6 +149,10 @@ struct intel_shared_dpll { const char *name; /* should match the index in the dev_priv->shared_dplls array */ enum intel_dpll_id id; + void (*enable)(struct drm_i915_private *dev_priv, + struct intel_shared_dpll *pll); + void (*disable)(struct drm_i915_private *dev_priv, + struct intel_shared_dpll *pll); }; /* Used by dp and fdi links */ @@ -202,7 +208,6 @@ struct opregion_header; struct opregion_acpi; struct opregion_swsci; struct opregion_asle; -struct drm_i915_private; struct intel_opregion { struct opregion_header __iomem *header; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 1825ca5466e8..85f8888afce9 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1419,8 +1419,6 @@ static void ironlake_enable_shared_dpll(struct intel_crtc *crtc) { struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; struct intel_shared_dpll *pll = intel_crtc_to_shared_dpll(crtc); - int reg; - u32 val; /* PCH PLLs only available on ILK, SNB and IVB */ BUG_ON(dev_priv->info->gen < 5); @@ -1434,9 +1432,6 @@ static void ironlake_enable_shared_dpll(struct intel_crtc *crtc) pll->name, pll->active, pll->on, crtc->base.base.id); - /* PCH refclock must be enabled first */ - assert_pch_refclk_enabled(dev_priv); - if (pll->active++) { WARN_ON(!pll->on); assert_shared_dpll_enabled(dev_priv, pll, NULL); @@ -1445,14 +1440,7 @@ static void ironlake_enable_shared_dpll(struct intel_crtc *crtc) WARN_ON(pll->on); DRM_DEBUG_KMS("enabling %s\n", pll->name); - - reg = PCH_DPLL(pll->id); - val = I915_READ(reg); - val |= DPLL_VCO_ENABLE; - I915_WRITE(reg, val); - POSTING_READ(reg); - udelay(200); - + pll->enable(dev_priv, pll); pll->on = true; } @@ -1460,8 +1448,6 @@ static void intel_disable_shared_dpll(struct intel_crtc *crtc) { struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; struct intel_shared_dpll *pll = intel_crtc_to_shared_dpll(crtc); - int reg; - u32 val; /* PCH only available on ILK+ */ BUG_ON(dev_priv->info->gen < 5); @@ -1486,17 +1472,7 @@ static void intel_disable_shared_dpll(struct intel_crtc *crtc) return; DRM_DEBUG_KMS("disabling %s\n", pll->name); - - /* Make sure transcoder isn't still depending on us */ - assert_pch_transcoder_disabled(dev_priv, crtc->pipe); - - reg = PCH_DPLL(pll->id); - val = I915_READ(reg); - val &= ~DPLL_VCO_ENABLE; - I915_WRITE(reg, val); - POSTING_READ(reg); - udelay(200); - + pll->disable(dev_priv, pll); pll->on = false; } @@ -8729,6 +8705,43 @@ static void intel_cpu_pll_init(struct drm_device *dev) intel_ddi_pll_init(dev); } +static void ibx_pch_dpll_enable(struct drm_i915_private *dev_priv, + struct intel_shared_dpll *pll) +{ + uint32_t reg, val; + + /* PCH refclock must be enabled first */ + assert_pch_refclk_enabled(dev_priv); + + reg = PCH_DPLL(pll->id); + val = I915_READ(reg); + val |= DPLL_VCO_ENABLE; + I915_WRITE(reg, val); + POSTING_READ(reg); + udelay(200); +} + +static void ibx_pch_dpll_disable(struct drm_i915_private *dev_priv, + struct intel_shared_dpll *pll) +{ + struct drm_device *dev = dev_priv->dev; + struct intel_crtc *crtc; + uint32_t reg, val; + + /* Make sure no transcoder isn't still depending on us. */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head) { + if (intel_crtc_to_shared_dpll(crtc) == pll) + assert_pch_transcoder_disabled(dev_priv, crtc->pipe); + } + + reg = PCH_DPLL(pll->id); + val = I915_READ(reg); + val &= ~DPLL_VCO_ENABLE; + I915_WRITE(reg, val); + POSTING_READ(reg); + udelay(200); +} + static char *ibx_pch_dpll_names[] = { "PCH DPLL A", "PCH DPLL B", @@ -8736,7 +8749,7 @@ static char *ibx_pch_dpll_names[] = { static void ibx_pch_dpll_init(struct drm_device *dev) { - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; int i; dev_priv->num_shared_dpll = 2; @@ -8744,12 +8757,14 @@ static void ibx_pch_dpll_init(struct drm_device *dev) for (i = 0; i < dev_priv->num_shared_dpll; i++) { dev_priv->shared_dplls[i].id = i; dev_priv->shared_dplls[i].name = ibx_pch_dpll_names[i]; + dev_priv->shared_dplls[i].enable = ibx_pch_dpll_enable; + dev_priv->shared_dplls[i].disable = ibx_pch_dpll_disable; } } static void intel_shared_dpll_init(struct drm_device *dev) { - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) ibx_pch_dpll_init(dev); -- cgit v1.2.3 From e9d6944ed75dbf16ae34bb73bd1eeca7cb183b67 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 5 Jun 2013 13:34:15 +0200 Subject: drm/i915: drop crtc checking from assert_shared_dpll MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hw state readout code for the pipe config will now check this for us, so rip out this hand-rolled complexity. Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 35 +++++++---------------------------- 1 file changed, 7 insertions(+), 28 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 85f8888afce9..bef9086bf542 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -923,7 +923,6 @@ intel_crtc_to_shared_dpll(struct intel_crtc *crtc) /* For ILK+ */ static void assert_shared_dpll(struct drm_i915_private *dev_priv, struct intel_shared_dpll *pll, - struct intel_crtc *crtc, bool state) { u32 val; @@ -943,28 +942,9 @@ static void assert_shared_dpll(struct drm_i915_private *dev_priv, WARN(cur_state != state, "%s assertion failure (expected %s, current %s), val=%08x\n", pll->name, state_string(state), state_string(cur_state), val); - - /* Make sure the selected PLL is correctly attached to the transcoder */ - if (crtc && HAS_PCH_CPT(dev_priv->dev)) { - u32 pch_dpll; - - pch_dpll = I915_READ(PCH_DPLL_SEL); - cur_state = pll->id == DPLL_ID_PCH_PLL_B; - if (!WARN(((pch_dpll >> (4 * crtc->pipe)) & 1) != cur_state, - "PLL[%d] not attached to this transcoder %c: %08x\n", - cur_state, pipe_name(crtc->pipe), pch_dpll)) { - cur_state = !!(val >> (4*crtc->pipe + 3)); - WARN(cur_state != state, - "PLL[%d] not %s on this transcoder %c: %08x\n", - pll->id == DPLL_ID_PCH_PLL_B, - state_string(state), - pipe_name(crtc->pipe), - val); - } - } } -#define assert_shared_dpll_enabled(d, p, c) assert_shared_dpll(d, p, c, true) -#define assert_shared_dpll_disabled(d, p, c) assert_shared_dpll(d, p, c, false) +#define assert_shared_dpll_enabled(d, p) assert_shared_dpll(d, p, true) +#define assert_shared_dpll_disabled(d, p) assert_shared_dpll(d, p, false) static void assert_fdi_tx(struct drm_i915_private *dev_priv, enum pipe pipe, bool state) @@ -1434,7 +1414,7 @@ static void ironlake_enable_shared_dpll(struct intel_crtc *crtc) if (pll->active++) { WARN_ON(!pll->on); - assert_shared_dpll_enabled(dev_priv, pll, NULL); + assert_shared_dpll_enabled(dev_priv, pll); return; } WARN_ON(pll->on); @@ -1462,11 +1442,11 @@ static void intel_disable_shared_dpll(struct intel_crtc *crtc) crtc->base.base.id); if (WARN_ON(pll->active == 0)) { - assert_shared_dpll_disabled(dev_priv, pll, NULL); + assert_shared_dpll_disabled(dev_priv, pll); return; } - assert_shared_dpll_enabled(dev_priv, pll, NULL); + assert_shared_dpll_enabled(dev_priv, pll); WARN_ON(!pll->on); if (--pll->active) return; @@ -1489,8 +1469,7 @@ static void ironlake_enable_pch_transcoder(struct drm_i915_private *dev_priv, /* Make sure PCH DPLL is enabled */ assert_shared_dpll_enabled(dev_priv, - intel_crtc_to_shared_dpll(intel_crtc), - intel_crtc); + intel_crtc_to_shared_dpll(intel_crtc)); /* FDI must be feeding us bits for PCH ports */ assert_fdi_tx_enabled(dev_priv, pipe); @@ -3112,7 +3091,7 @@ found: if (pll->active == 0) { DRM_DEBUG_DRIVER("setting up %s\n", pll->name); WARN_ON(pll->on); - assert_shared_dpll_disabled(dev_priv, pll, NULL); + assert_shared_dpll_disabled(dev_priv, pll); /* Wait for the clocks to stabilize before rewriting the regs */ I915_WRITE(PCH_DPLL(pll->id), dpll & ~DPLL_VCO_ENABLE); -- cgit v1.2.3 From 19d415a25e9078dba26adb54396d4e5b30d3b572 Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Mon, 10 Jun 2013 13:28:42 +0100 Subject: drm/i915: Fix old reference to i830_sdvo_get_capabilities() It's now intel_sdvo_get_capabilities(). Signed-off-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_sdvo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index ac923b74924a..e77ffadb1779 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -80,7 +80,7 @@ struct intel_sdvo { /* * Capabilities of the SDVO device returned by - * i830_sdvo_get_capabilities() + * intel_sdvo_get_capabilities() */ struct intel_sdvo_caps caps; -- cgit v1.2.3 From 2f28c50bb3881f11afee8f8c9041dad2c96653aa Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Mon, 10 Jun 2013 13:49:25 +0100 Subject: drm/i915: Initialize active_outputs to never read unitialized values In case of intel_sdvo_get_active_outputs() failing, we end up reading a value from the stack. Signed-off-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_sdvo.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index e77ffadb1779..97d3099aea23 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1277,7 +1277,7 @@ static bool intel_sdvo_connector_get_hw_state(struct intel_connector *connector) struct intel_sdvo_connector *intel_sdvo_connector = to_intel_sdvo_connector(&connector->base); struct intel_sdvo *intel_sdvo = intel_attached_sdvo(&connector->base); - u16 active_outputs; + u16 active_outputs = 0; intel_sdvo_get_active_outputs(intel_sdvo, &active_outputs); @@ -1293,7 +1293,7 @@ static bool intel_sdvo_get_hw_state(struct intel_encoder *encoder, struct drm_device *dev = encoder->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_sdvo *intel_sdvo = to_intel_sdvo(&encoder->base); - u16 active_outputs; + u16 active_outputs = 0; u32 tmp; tmp = I915_READ(intel_sdvo->sdvo_reg); -- cgit v1.2.3 From cc2588eabbe46820a86e55fccec8b741e15f647f Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 4 Jun 2013 07:44:50 -0700 Subject: cw1200: hwio: Remove an unnecessary goto goto after return is wrong. The other code in this block needs to set an error value then goto an error release block. This one doesn't need to release anything and was likely a copy/paste remainder. Signed-off-by: Joe Perches Acked-By: Solomon Peachy Signed-off-by: John W. Linville --- drivers/net/wireless/cw1200/hwio.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/cw1200/hwio.c b/drivers/net/wireless/cw1200/hwio.c index 142f45efa204..dad3fb331818 100644 --- a/drivers/net/wireless/cw1200/hwio.c +++ b/drivers/net/wireless/cw1200/hwio.c @@ -178,7 +178,6 @@ int cw1200_indirect_read(struct cw1200_common *priv, u32 addr, void *buf, if ((buf_len / 2) >= 0x1000) { pr_err("Can't read more than 0xfff words.\n"); return -EINVAL; - goto out; } priv->hwbus_ops->lock(priv->hwbus_priv); -- cgit v1.2.3 From aa63e18e3ddad4eb15d4af34ae66e7f4dcc7a6d0 Mon Sep 17 00:00:00 2001 From: Solomon Peachy Date: Tue, 4 Jun 2013 23:37:05 -0400 Subject: cw1200: Sanity-check arguments in copy_from_user() The optional debugfs interface to the vendor's engineering tools wasn't bounds checking at all, which made it trivial to perform a buffer overflow if this interface was compiled in and then explicitly enabled at runtime. This patch checks both the length supplied as part of the data to ensure it is sane, and also the amount of data compared to the remaining buffer space. If either is too large, fail immediately. (This bug was spotted by Dan Carpenter ) Signed-off-by: Solomon Peachy Signed-off-by: John W. Linville --- drivers/net/wireless/cw1200/debug.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/cw1200/debug.c b/drivers/net/wireless/cw1200/debug.c index eb40c9c61a51..1596b7042cbd 100644 --- a/drivers/net/wireless/cw1200/debug.c +++ b/drivers/net/wireless/cw1200/debug.c @@ -397,13 +397,13 @@ struct etf_req_msg; static int etf_request(struct cw1200_common *priv, struct etf_req_msg *msg, u32 len); -#define MAX_RX_SZE 2600 +#define MAX_RX_SIZE 2600 struct etf_in_state { struct cw1200_common *priv; - u32 total_len; - u8 buf[MAX_RX_SZE]; - u32 written; + u16 total_len; + u16 written; + u8 buf[MAX_RX_SIZE]; }; static int cw1200_etf_in_open(struct inode *inode, struct file *file) @@ -448,6 +448,11 @@ static ssize_t cw1200_etf_in_write(struct file *file, return -EFAULT; } + if (etf->total_len > MAX_RX_SIZE) { + pr_err("requested length > MAX_RX_SIZE\n"); + return -EINVAL; + } + written += sizeof(etf->total_len); count -= sizeof(etf->total_len); } @@ -455,6 +460,11 @@ static ssize_t cw1200_etf_in_write(struct file *file, if (!count) goto done; + if (count > (etf->total_len - written)) { + pr_err("Tried to write > MAX_RX_SIZE\n"); + return -EINVAL; + } + if (copy_from_user(etf->buf + etf->written, user_buf + written, count)) { pr_err("copy_from_user (payload %zu) failed\n", count); -- cgit v1.2.3 From f28bc92c9530fe39eccfcff392879f19bd234578 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 6 Jun 2013 10:43:00 +0300 Subject: cw1200: read beyond end of array in debug code This has only one caller and rates[] is an array with IEEE80211_TX_MAX_RATES (4) elements. Signed-off-by: Dan Carpenter Signed-off-by: John W. Linville --- drivers/net/wireless/cw1200/txrx.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/cw1200/txrx.c b/drivers/net/wireless/cw1200/txrx.c index 0e40890e1993..a2547473ec5c 100644 --- a/drivers/net/wireless/cw1200/txrx.c +++ b/drivers/net/wireless/cw1200/txrx.c @@ -190,13 +190,12 @@ static void tx_policy_build(const struct cw1200_common *priv, policy->retry_count += retries; } - pr_debug("[TX policy] Policy (%zu): %d:%d, %d:%d, %d:%d, %d:%d, %d:%d\n", + pr_debug("[TX policy] Policy (%zu): %d:%d, %d:%d, %d:%d, %d:%d\n", count, rates[0].idx, rates[0].count, rates[1].idx, rates[1].count, rates[2].idx, rates[2].count, - rates[3].idx, rates[3].count, - rates[4].idx, rates[4].count); + rates[3].idx, rates[3].count); } static inline bool tx_policy_is_equal(const struct tx_policy *wanted, -- cgit v1.2.3 From f7a01cac7305391433d609bd23241e4138e2fb84 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 6 Jun 2013 03:57:46 -0700 Subject: cw1200: handle allocation failure in wsm_event_indication() Check for allocation failures and return -ENOMEM. The caller already expects it. Signed-off-by: Dan Carpenter Signed-off-by: John W. Linville --- drivers/net/wireless/cw1200/wsm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/cw1200/wsm.c b/drivers/net/wireless/cw1200/wsm.c index f3fd9b218724..3ab25f661793 100644 --- a/drivers/net/wireless/cw1200/wsm.c +++ b/drivers/net/wireless/cw1200/wsm.c @@ -929,6 +929,8 @@ static int wsm_event_indication(struct cw1200_common *priv, struct wsm_buf *buf) } event = kzalloc(sizeof(struct cw1200_wsm_event), GFP_KERNEL); + if (!event) + return -ENOMEM; event->evt.id = __le32_to_cpu(WSM_GET32(buf)); event->evt.data = __le32_to_cpu(WSM_GET32(buf)); -- cgit v1.2.3 From ec71997eff2231098212a99934c0fb987a9e6b04 Mon Sep 17 00:00:00 2001 From: Yunlian Jiang Date: Fri, 31 May 2013 14:45:21 -0700 Subject: rtlwifi: initialize local array and set value. GCC 4.8 is spitting out uninitialized-variable warnings against "drivers/net/wireless/rtlwifi/rtl8192de/dm.c". drivers/net/wireless/rtlwifi/rtl8192de/dm.c:941:31: error: 'ofdm_index_old[1]' may be used uninitialized in this function [-Werror=maybe-uninitialized] rtlpriv->dm.ofdm_index[i] = ofdm_index_old[i]; This patch adds initialization to the variable and properly sets its value. Signed-off-by: Yunlian Jiang Acked-by: Larry Finger Signed-off-by: John W. Linville --- drivers/net/wireless/rtlwifi/rtl8192de/dm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/dm.c b/drivers/net/wireless/rtlwifi/rtl8192de/dm.c index 19a765532603..47875ba09ff8 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/dm.c @@ -842,7 +842,7 @@ static void rtl92d_dm_txpower_tracking_callback_thermalmeter( long val_y, ele_c = 0; u8 ofdm_index[2]; s8 cck_index = 0; - u8 ofdm_index_old[2]; + u8 ofdm_index_old[2] = {0, 0}; s8 cck_index_old = 0; u8 index; int i; -- cgit v1.2.3 From 10af87c3d99112b3ca279cadd874cd542e4f6699 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Mon, 3 Jun 2013 23:39:53 +0200 Subject: rt2x00: rt2x00queue: initialize data_queue fields earlier Support for rt2800 device is broken since my 'rt2x00: rt2x00dev: use rt2x00dev->tx->limit' patch. The changelog of that commit says that the TX data queue is initialized already when the rt2x00lib_probe_hw() function is called. However as Jakub noticed it, this statement is not correct. The queue->limit field is initialized in the rt2x00queue_alloc_entries routine and that is not yet called when rt2x00lib_probe_hw() runs. Because the value of tx->limit contains zero, the driver tries to allocate a kernel fifo with zero size and kfifo_alloc rejects that with -EINVAL. PCI: Enabling device 0000:01:00.0 (0000 -> 0002) ieee80211 phy1: rt2x00_set_rt: Info - RT chipset 3071, rev 021c detected ieee80211 phy1: rt2x00_set_rf: Info - RF chipset 0008 detected ieee80211 phy1: rt2x00lib_probe_dev: Error - Failed to initialize hw rt2800pci: probe of 0000:01:00.0 failed with error -22 Move the data_queue field initialization from the rt2x00queue_alloc_entries routine into the rt2x00queue_init function. The initialization code is not strictly related to the allocation, and the change ensures that the queue_data fields can be used in the probe routines. The patch also introduces a helper function in order to be able to get the correct data_queue_desc structure for a given queue. This helper is only needed temporarily and it will be removed later. Reported-by: Jakub Kicinski Signed-off-by: Gabor Juhos Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00queue.c | 44 ++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c index 2c12311467a9..5efbbbdca701 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c @@ -1170,12 +1170,6 @@ static int rt2x00queue_alloc_entries(struct data_queue *queue, rt2x00queue_reset(queue); - queue->limit = qdesc->entry_num; - queue->threshold = DIV_ROUND_UP(qdesc->entry_num, 10); - queue->data_size = qdesc->data_size; - queue->desc_size = qdesc->desc_size; - queue->winfo_size = qdesc->winfo_size; - /* * Allocate all queue entries. */ @@ -1284,9 +1278,38 @@ void rt2x00queue_uninitialize(struct rt2x00_dev *rt2x00dev) } } +static const struct data_queue_desc * +rt2x00queue_get_qdesc_by_qid(struct rt2x00_dev *rt2x00dev, + enum data_queue_qid qid) +{ + switch (qid) { + case QID_RX: + return rt2x00dev->ops->rx; + + case QID_AC_BE: + case QID_AC_BK: + case QID_AC_VO: + case QID_AC_VI: + return rt2x00dev->ops->tx; + + case QID_BEACON: + return rt2x00dev->ops->bcn; + + case QID_ATIM: + return rt2x00dev->ops->atim; + + default: + break; + } + + return NULL; +} + static void rt2x00queue_init(struct rt2x00_dev *rt2x00dev, struct data_queue *queue, enum data_queue_qid qid) { + const struct data_queue_desc *qdesc; + mutex_init(&queue->status_lock); spin_lock_init(&queue->tx_lock); spin_lock_init(&queue->index_lock); @@ -1297,6 +1320,15 @@ static void rt2x00queue_init(struct rt2x00_dev *rt2x00dev, queue->aifs = 2; queue->cw_min = 5; queue->cw_max = 10; + + qdesc = rt2x00queue_get_qdesc_by_qid(rt2x00dev, qid); + BUG_ON(!qdesc); + + queue->limit = qdesc->entry_num; + queue->threshold = DIV_ROUND_UP(qdesc->entry_num, 10); + queue->data_size = qdesc->data_size; + queue->desc_size = qdesc->desc_size; + queue->winfo_size = qdesc->winfo_size; } int rt2x00queue_allocate(struct rt2x00_dev *rt2x00dev) -- cgit v1.2.3 From 7260aac97447a2b2cb9e8684d1162118c4426354 Mon Sep 17 00:00:00 2001 From: Maxime Bizon Date: Tue, 4 Jun 2013 22:53:33 +0100 Subject: bcm63xx_enet: implement reset autoneg ethtool callback Implement the rset_nway ethtool callback which uses libphy generic autonegotiation restart function. Signed-off-by: Maxime Bizon Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bcm63xx_enet.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/net/ethernet/broadcom/bcm63xx_enet.c b/drivers/net/ethernet/broadcom/bcm63xx_enet.c index e46466cb3627..bc1a9948892b 100644 --- a/drivers/net/ethernet/broadcom/bcm63xx_enet.c +++ b/drivers/net/ethernet/broadcom/bcm63xx_enet.c @@ -1328,6 +1328,20 @@ static void bcm_enet_get_ethtool_stats(struct net_device *netdev, mutex_unlock(&priv->mib_update_lock); } +static int bcm_enet_nway_reset(struct net_device *dev) +{ + struct bcm_enet_priv *priv; + + priv = netdev_priv(dev); + if (priv->has_phy) { + if (!priv->phydev) + return -ENODEV; + return genphy_restart_aneg(priv->phydev); + } + + return -EOPNOTSUPP; +} + static int bcm_enet_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) { @@ -1470,6 +1484,7 @@ static const struct ethtool_ops bcm_enet_ethtool_ops = { .get_strings = bcm_enet_get_strings, .get_sset_count = bcm_enet_get_sset_count, .get_ethtool_stats = bcm_enet_get_ethtool_stats, + .nway_reset = bcm_enet_nway_reset, .get_settings = bcm_enet_get_settings, .set_settings = bcm_enet_set_settings, .get_drvinfo = bcm_enet_get_drvinfo, -- cgit v1.2.3 From 0ae99b5fede6f3a8d252d50bb4aba29544295219 Mon Sep 17 00:00:00 2001 From: Maxime Bizon Date: Tue, 4 Jun 2013 22:53:34 +0100 Subject: bcm63xx_enet: split DMA channel register accesses The current bcm63xx_enet driver always uses bcmenet_shared_base whenever it needs to access DMA channel configuration space or access the DMA channel state RAM. Split these register in 3 parts to be more accurate: - global DMA configuration - per DMA channel configuration space - per DMA channel state RAM space This is preliminary to support new chips where the global DMA configuration remains the same, but there is a varying number of DMA channels located at a different memory offset. Signed-off-by: Maxime Bizon Signed-off-by: Jonas Gorski Signed-off-by: David S. Miller --- arch/mips/bcm63xx/dev-enet.c | 23 +++- arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h | 4 +- drivers/net/ethernet/broadcom/bcm63xx_enet.c | 139 ++++++++++++++--------- 3 files changed, 105 insertions(+), 61 deletions(-) diff --git a/arch/mips/bcm63xx/dev-enet.c b/arch/mips/bcm63xx/dev-enet.c index 39c23366c5c7..df5bf6686942 100644 --- a/arch/mips/bcm63xx/dev-enet.c +++ b/arch/mips/bcm63xx/dev-enet.c @@ -19,6 +19,16 @@ static struct resource shared_res[] = { .end = -1, /* filled at runtime */ .flags = IORESOURCE_MEM, }, + { + .start = -1, /* filled at runtime */ + .end = -1, /* filled at runtime */ + .flags = IORESOURCE_MEM, + }, + { + .start = -1, /* filled at runtime */ + .end = -1, /* filled at runtime */ + .flags = IORESOURCE_MEM, + }, }; static struct platform_device bcm63xx_enet_shared_device = { @@ -110,10 +120,15 @@ int __init bcm63xx_enet_register(int unit, if (!shared_device_registered) { shared_res[0].start = bcm63xx_regset_address(RSET_ENETDMA); shared_res[0].end = shared_res[0].start; - if (BCMCPU_IS_6338()) - shared_res[0].end += (RSET_ENETDMA_SIZE / 2) - 1; - else - shared_res[0].end += (RSET_ENETDMA_SIZE) - 1; + shared_res[0].end += (RSET_ENETDMA_SIZE) - 1; + + shared_res[1].start = bcm63xx_regset_address(RSET_ENETDMAC); + shared_res[1].end = shared_res[1].start; + shared_res[1].end += RSET_ENETDMAC_SIZE(16) - 1; + + shared_res[2].start = bcm63xx_regset_address(RSET_ENETDMAS); + shared_res[2].end = shared_res[2].start; + shared_res[2].end += RSET_ENETDMAS_SIZE(16) - 1; ret = platform_device_register(&bcm63xx_enet_shared_device); if (ret) diff --git a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h index 336228990808..9981f4f0e42f 100644 --- a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h +++ b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h @@ -173,7 +173,9 @@ enum bcm63xx_regs_set { #define BCM_6358_RSET_SPI_SIZE 1804 #define BCM_6368_RSET_SPI_SIZE 1804 #define RSET_ENET_SIZE 2048 -#define RSET_ENETDMA_SIZE 2048 +#define RSET_ENETDMA_SIZE 256 +#define RSET_ENETDMAC_SIZE(chans) (16 * (chans)) +#define RSET_ENETDMAS_SIZE(chans) (16 * (chans)) #define RSET_ENETSW_SIZE 65536 #define RSET_UART_SIZE 24 #define RSET_UDC_SIZE 256 diff --git a/drivers/net/ethernet/broadcom/bcm63xx_enet.c b/drivers/net/ethernet/broadcom/bcm63xx_enet.c index bc1a9948892b..edaf76dc2487 100644 --- a/drivers/net/ethernet/broadcom/bcm63xx_enet.c +++ b/drivers/net/ethernet/broadcom/bcm63xx_enet.c @@ -41,8 +41,8 @@ static int copybreak __read_mostly = 128; module_param(copybreak, int, 0); MODULE_PARM_DESC(copybreak, "Receive copy threshold"); -/* io memory shared between all devices */ -static void __iomem *bcm_enet_shared_base; +/* io registers memory shared between all devices */ +static void __iomem *bcm_enet_shared_base[3]; /* * io helpers to access mac registers @@ -63,13 +63,35 @@ static inline void enet_writel(struct bcm_enet_priv *priv, */ static inline u32 enet_dma_readl(struct bcm_enet_priv *priv, u32 off) { - return bcm_readl(bcm_enet_shared_base + off); + return bcm_readl(bcm_enet_shared_base[0] + off); } static inline void enet_dma_writel(struct bcm_enet_priv *priv, u32 val, u32 off) { - bcm_writel(val, bcm_enet_shared_base + off); + bcm_writel(val, bcm_enet_shared_base[0] + off); +} + +static inline u32 enet_dmac_readl(struct bcm_enet_priv *priv, u32 off) +{ + return bcm_readl(bcm_enet_shared_base[1] + off); +} + +static inline void enet_dmac_writel(struct bcm_enet_priv *priv, + u32 val, u32 off) +{ + bcm_writel(val, bcm_enet_shared_base[1] + off); +} + +static inline u32 enet_dmas_readl(struct bcm_enet_priv *priv, u32 off) +{ + return bcm_readl(bcm_enet_shared_base[2] + off); +} + +static inline void enet_dmas_writel(struct bcm_enet_priv *priv, + u32 val, u32 off) +{ + bcm_writel(val, bcm_enet_shared_base[2] + off); } /* @@ -353,8 +375,8 @@ static int bcm_enet_receive_queue(struct net_device *dev, int budget) bcm_enet_refill_rx(dev); /* kick rx dma */ - enet_dma_writel(priv, ENETDMA_CHANCFG_EN_MASK, - ENETDMA_CHANCFG_REG(priv->rx_chan)); + enet_dmac_writel(priv, ENETDMAC_CHANCFG_EN_MASK, + ENETDMAC_CHANCFG_REG(priv->rx_chan)); } return processed; @@ -429,10 +451,10 @@ static int bcm_enet_poll(struct napi_struct *napi, int budget) dev = priv->net_dev; /* ack interrupts */ - enet_dma_writel(priv, ENETDMA_IR_PKTDONE_MASK, - ENETDMA_IR_REG(priv->rx_chan)); - enet_dma_writel(priv, ENETDMA_IR_PKTDONE_MASK, - ENETDMA_IR_REG(priv->tx_chan)); + enet_dmac_writel(priv, ENETDMAC_IR_PKTDONE_MASK, + ENETDMAC_IR_REG(priv->rx_chan)); + enet_dmac_writel(priv, ENETDMAC_IR_PKTDONE_MASK, + ENETDMAC_IR_REG(priv->tx_chan)); /* reclaim sent skb */ tx_work_done = bcm_enet_tx_reclaim(dev, 0); @@ -451,10 +473,10 @@ static int bcm_enet_poll(struct napi_struct *napi, int budget) napi_complete(napi); /* restore rx/tx interrupt */ - enet_dma_writel(priv, ENETDMA_IR_PKTDONE_MASK, - ENETDMA_IRMASK_REG(priv->rx_chan)); - enet_dma_writel(priv, ENETDMA_IR_PKTDONE_MASK, - ENETDMA_IRMASK_REG(priv->tx_chan)); + enet_dmac_writel(priv, ENETDMAC_IR_PKTDONE_MASK, + ENETDMAC_IRMASK_REG(priv->rx_chan)); + enet_dmac_writel(priv, ENETDMAC_IR_PKTDONE_MASK, + ENETDMAC_IRMASK_REG(priv->tx_chan)); return rx_work_done; } @@ -497,8 +519,8 @@ static irqreturn_t bcm_enet_isr_dma(int irq, void *dev_id) priv = netdev_priv(dev); /* mask rx/tx interrupts */ - enet_dma_writel(priv, 0, ENETDMA_IRMASK_REG(priv->rx_chan)); - enet_dma_writel(priv, 0, ENETDMA_IRMASK_REG(priv->tx_chan)); + enet_dmac_writel(priv, 0, ENETDMAC_IRMASK_REG(priv->rx_chan)); + enet_dmac_writel(priv, 0, ENETDMAC_IRMASK_REG(priv->tx_chan)); napi_schedule(&priv->napi); @@ -557,8 +579,8 @@ static int bcm_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) wmb(); /* kick tx dma */ - enet_dma_writel(priv, ENETDMA_CHANCFG_EN_MASK, - ENETDMA_CHANCFG_REG(priv->tx_chan)); + enet_dmac_writel(priv, ENETDMAC_CHANCFG_EN_MASK, + ENETDMAC_CHANCFG_REG(priv->tx_chan)); /* stop queue if no more desc available */ if (!priv->tx_desc_count) @@ -833,8 +855,8 @@ static int bcm_enet_open(struct net_device *dev) /* mask all interrupts and request them */ enet_writel(priv, 0, ENET_IRMASK_REG); - enet_dma_writel(priv, 0, ENETDMA_IRMASK_REG(priv->rx_chan)); - enet_dma_writel(priv, 0, ENETDMA_IRMASK_REG(priv->tx_chan)); + enet_dmac_writel(priv, 0, ENETDMAC_IRMASK_REG(priv->rx_chan)); + enet_dmac_writel(priv, 0, ENETDMAC_IRMASK_REG(priv->tx_chan)); ret = request_irq(dev->irq, bcm_enet_isr_mac, 0, dev->name, dev); if (ret) @@ -919,28 +941,28 @@ static int bcm_enet_open(struct net_device *dev) } /* write rx & tx ring addresses */ - enet_dma_writel(priv, priv->rx_desc_dma, - ENETDMA_RSTART_REG(priv->rx_chan)); - enet_dma_writel(priv, priv->tx_desc_dma, - ENETDMA_RSTART_REG(priv->tx_chan)); + enet_dmas_writel(priv, priv->rx_desc_dma, + ENETDMAS_RSTART_REG(priv->rx_chan)); + enet_dmas_writel(priv, priv->tx_desc_dma, + ENETDMAS_RSTART_REG(priv->tx_chan)); /* clear remaining state ram for rx & tx channel */ - enet_dma_writel(priv, 0, ENETDMA_SRAM2_REG(priv->rx_chan)); - enet_dma_writel(priv, 0, ENETDMA_SRAM2_REG(priv->tx_chan)); - enet_dma_writel(priv, 0, ENETDMA_SRAM3_REG(priv->rx_chan)); - enet_dma_writel(priv, 0, ENETDMA_SRAM3_REG(priv->tx_chan)); - enet_dma_writel(priv, 0, ENETDMA_SRAM4_REG(priv->rx_chan)); - enet_dma_writel(priv, 0, ENETDMA_SRAM4_REG(priv->tx_chan)); + enet_dmas_writel(priv, 0, ENETDMAS_SRAM2_REG(priv->rx_chan)); + enet_dmas_writel(priv, 0, ENETDMAS_SRAM2_REG(priv->tx_chan)); + enet_dmas_writel(priv, 0, ENETDMAS_SRAM3_REG(priv->rx_chan)); + enet_dmas_writel(priv, 0, ENETDMAS_SRAM3_REG(priv->tx_chan)); + enet_dmas_writel(priv, 0, ENETDMAS_SRAM4_REG(priv->rx_chan)); + enet_dmas_writel(priv, 0, ENETDMAS_SRAM4_REG(priv->tx_chan)); /* set max rx/tx length */ enet_writel(priv, priv->hw_mtu, ENET_RXMAXLEN_REG); enet_writel(priv, priv->hw_mtu, ENET_TXMAXLEN_REG); /* set dma maximum burst len */ - enet_dma_writel(priv, BCMENET_DMA_MAXBURST, - ENETDMA_MAXBURST_REG(priv->rx_chan)); - enet_dma_writel(priv, BCMENET_DMA_MAXBURST, - ENETDMA_MAXBURST_REG(priv->tx_chan)); + enet_dmac_writel(priv, BCMENET_DMA_MAXBURST, + ENETDMAC_MAXBURST_REG(priv->rx_chan)); + enet_dmac_writel(priv, BCMENET_DMA_MAXBURST, + ENETDMAC_MAXBURST_REG(priv->tx_chan)); /* set correct transmit fifo watermark */ enet_writel(priv, BCMENET_TX_FIFO_TRESH, ENET_TXWMARK_REG); @@ -958,26 +980,26 @@ static int bcm_enet_open(struct net_device *dev) val |= ENET_CTL_ENABLE_MASK; enet_writel(priv, val, ENET_CTL_REG); enet_dma_writel(priv, ENETDMA_CFG_EN_MASK, ENETDMA_CFG_REG); - enet_dma_writel(priv, ENETDMA_CHANCFG_EN_MASK, - ENETDMA_CHANCFG_REG(priv->rx_chan)); + enet_dmac_writel(priv, ENETDMAC_CHANCFG_EN_MASK, + ENETDMAC_CHANCFG_REG(priv->rx_chan)); /* watch "mib counters about to overflow" interrupt */ enet_writel(priv, ENET_IR_MIB, ENET_IR_REG); enet_writel(priv, ENET_IR_MIB, ENET_IRMASK_REG); /* watch "packet transferred" interrupt in rx and tx */ - enet_dma_writel(priv, ENETDMA_IR_PKTDONE_MASK, - ENETDMA_IR_REG(priv->rx_chan)); - enet_dma_writel(priv, ENETDMA_IR_PKTDONE_MASK, - ENETDMA_IR_REG(priv->tx_chan)); + enet_dmac_writel(priv, ENETDMAC_IR_PKTDONE_MASK, + ENETDMAC_IR_REG(priv->rx_chan)); + enet_dmac_writel(priv, ENETDMAC_IR_PKTDONE_MASK, + ENETDMAC_IR_REG(priv->tx_chan)); /* make sure we enable napi before rx interrupt */ napi_enable(&priv->napi); - enet_dma_writel(priv, ENETDMA_IR_PKTDONE_MASK, - ENETDMA_IRMASK_REG(priv->rx_chan)); - enet_dma_writel(priv, ENETDMA_IR_PKTDONE_MASK, - ENETDMA_IRMASK_REG(priv->tx_chan)); + enet_dmac_writel(priv, ENETDMAC_IR_PKTDONE_MASK, + ENETDMAC_IRMASK_REG(priv->rx_chan)); + enet_dmac_writel(priv, ENETDMAC_IR_PKTDONE_MASK, + ENETDMAC_IRMASK_REG(priv->tx_chan)); if (priv->has_phy) phy_start(priv->phydev); @@ -1057,14 +1079,14 @@ static void bcm_enet_disable_dma(struct bcm_enet_priv *priv, int chan) { int limit; - enet_dma_writel(priv, 0, ENETDMA_CHANCFG_REG(chan)); + enet_dmac_writel(priv, 0, ENETDMAC_CHANCFG_REG(chan)); limit = 1000; do { u32 val; - val = enet_dma_readl(priv, ENETDMA_CHANCFG_REG(chan)); - if (!(val & ENETDMA_CHANCFG_EN_MASK)) + val = enet_dmac_readl(priv, ENETDMAC_CHANCFG_REG(chan)); + if (!(val & ENETDMAC_CHANCFG_EN_MASK)) break; udelay(1); } while (limit--); @@ -1090,8 +1112,8 @@ static int bcm_enet_stop(struct net_device *dev) /* mask all interrupts */ enet_writel(priv, 0, ENET_IRMASK_REG); - enet_dma_writel(priv, 0, ENETDMA_IRMASK_REG(priv->rx_chan)); - enet_dma_writel(priv, 0, ENETDMA_IRMASK_REG(priv->tx_chan)); + enet_dmac_writel(priv, 0, ENETDMAC_IRMASK_REG(priv->rx_chan)); + enet_dmac_writel(priv, 0, ENETDMAC_IRMASK_REG(priv->tx_chan)); /* make sure no mib update is scheduled */ cancel_work_sync(&priv->mib_update_task); @@ -1636,7 +1658,7 @@ static int bcm_enet_probe(struct platform_device *pdev) /* stop if shared driver failed, assume driver->probe will be * called in the same order we register devices (correct ?) */ - if (!bcm_enet_shared_base) + if (!bcm_enet_shared_base[0]) return -ENODEV; res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -1881,14 +1903,19 @@ struct platform_driver bcm63xx_enet_driver = { static int bcm_enet_shared_probe(struct platform_device *pdev) { struct resource *res; + void __iomem *p[3]; + unsigned int i; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; + memset(bcm_enet_shared_base, 0, sizeof(bcm_enet_shared_base)); - bcm_enet_shared_base = devm_request_and_ioremap(&pdev->dev, res); - if (!bcm_enet_shared_base) - return -ENOMEM; + for (i = 0; i < 3; i++) { + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + p[i] = devm_ioremap_resource(&pdev->dev, res); + if (!p[i]) + return -ENOMEM; + } + + memcpy(bcm_enet_shared_base, p, sizeof(bcm_enet_shared_base)); return 0; } -- cgit v1.2.3 From 6f00a0229627ca189529cad3f9154ac2f9e5c7db Mon Sep 17 00:00:00 2001 From: Maxime Bizon Date: Tue, 4 Jun 2013 22:53:35 +0100 Subject: bcm63xx_enet: add support for Broadcom BCM63xx integrated gigabit switch Newer Broadcom BCM63xx SoCs: 6328, 6362 and 6368 have an integrated switch which needs to be driven slightly differently from the traditional external switches. This patch introduces changes in arch/mips/bcm63xx in order to: - register a bcm63xx_enetsw driver instead of bcm63xx_enet driver - update DMA channels configuration & state RAM base addresses - add a new platform data configuration knob to define the number of ports per switch/device and force link on some ports - define the required switch registers On the driver side, the following changes are required: - the switch ports need to be polled to ensure the link is up and running and RX/TX can properly work - basic switch configuration needs to be performed for the switch to forward packets to the CPU - update the MIB counters since the integrated Signed-off-by: Maxime Bizon Signed-off-by: Jonas Gorski Signed-off-by: David S. Miller --- arch/mips/bcm63xx/boards/board_bcm963xx.c | 4 + arch/mips/bcm63xx/dev-enet.c | 113 ++- .../include/asm/mach-bcm63xx/bcm63xx_dev_enet.h | 28 + arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h | 50 ++ .../mips/include/asm/mach-bcm63xx/board_bcm963xx.h | 2 + drivers/net/ethernet/broadcom/bcm63xx_enet.c | 995 ++++++++++++++++++++- drivers/net/ethernet/broadcom/bcm63xx_enet.h | 71 ++ 7 files changed, 1205 insertions(+), 58 deletions(-) diff --git a/arch/mips/bcm63xx/boards/board_bcm963xx.c b/arch/mips/bcm63xx/boards/board_bcm963xx.c index a9505c4867e8..9c0ddafafb6c 100644 --- a/arch/mips/bcm63xx/boards/board_bcm963xx.c +++ b/arch/mips/bcm63xx/boards/board_bcm963xx.c @@ -845,6 +845,10 @@ int __init board_register_devices(void) !bcm63xx_nvram_get_mac_address(board.enet1.mac_addr)) bcm63xx_enet_register(1, &board.enet1); + if (board.has_enetsw && + !bcm63xx_nvram_get_mac_address(board.enetsw.mac_addr)) + bcm63xx_enetsw_register(&board.enetsw); + if (board.has_usbd) bcm63xx_usbd_register(&board.usbd); diff --git a/arch/mips/bcm63xx/dev-enet.c b/arch/mips/bcm63xx/dev-enet.c index df5bf6686942..6cbaee0f6d70 100644 --- a/arch/mips/bcm63xx/dev-enet.c +++ b/arch/mips/bcm63xx/dev-enet.c @@ -104,6 +104,64 @@ static struct platform_device bcm63xx_enet1_device = { }, }; +static struct resource enetsw_res[] = { + { + /* start & end filled at runtime */ + .flags = IORESOURCE_MEM, + }, + { + /* start filled at runtime */ + .flags = IORESOURCE_IRQ, + }, + { + /* start filled at runtime */ + .flags = IORESOURCE_IRQ, + }, +}; + +static struct bcm63xx_enetsw_platform_data enetsw_pd; + +static struct platform_device bcm63xx_enetsw_device = { + .name = "bcm63xx_enetsw", + .num_resources = ARRAY_SIZE(enetsw_res), + .resource = enetsw_res, + .dev = { + .platform_data = &enetsw_pd, + }, +}; + +static int __init register_shared(void) +{ + int ret, chan_count; + + if (shared_device_registered) + return 0; + + shared_res[0].start = bcm63xx_regset_address(RSET_ENETDMA); + shared_res[0].end = shared_res[0].start; + shared_res[0].end += (RSET_ENETDMA_SIZE) - 1; + + if (BCMCPU_IS_6328() || BCMCPU_IS_6362() || BCMCPU_IS_6368()) + chan_count = 32; + else + chan_count = 16; + + shared_res[1].start = bcm63xx_regset_address(RSET_ENETDMAC); + shared_res[1].end = shared_res[1].start; + shared_res[1].end += RSET_ENETDMAC_SIZE(chan_count) - 1; + + shared_res[2].start = bcm63xx_regset_address(RSET_ENETDMAS); + shared_res[2].end = shared_res[2].start; + shared_res[2].end += RSET_ENETDMAS_SIZE(chan_count) - 1; + + ret = platform_device_register(&bcm63xx_enet_shared_device); + if (ret) + return ret; + shared_device_registered = 1; + + return 0; +} + int __init bcm63xx_enet_register(int unit, const struct bcm63xx_enet_platform_data *pd) { @@ -117,24 +175,9 @@ int __init bcm63xx_enet_register(int unit, if (unit == 1 && BCMCPU_IS_6338()) return -ENODEV; - if (!shared_device_registered) { - shared_res[0].start = bcm63xx_regset_address(RSET_ENETDMA); - shared_res[0].end = shared_res[0].start; - shared_res[0].end += (RSET_ENETDMA_SIZE) - 1; - - shared_res[1].start = bcm63xx_regset_address(RSET_ENETDMAC); - shared_res[1].end = shared_res[1].start; - shared_res[1].end += RSET_ENETDMAC_SIZE(16) - 1; - - shared_res[2].start = bcm63xx_regset_address(RSET_ENETDMAS); - shared_res[2].end = shared_res[2].start; - shared_res[2].end += RSET_ENETDMAS_SIZE(16) - 1; - - ret = platform_device_register(&bcm63xx_enet_shared_device); - if (ret) - return ret; - shared_device_registered = 1; - } + ret = register_shared(); + if (ret) + return ret; if (unit == 0) { enet0_res[0].start = bcm63xx_regset_address(RSET_ENET0); @@ -175,3 +218,37 @@ int __init bcm63xx_enet_register(int unit, return ret; return 0; } + +int __init +bcm63xx_enetsw_register(const struct bcm63xx_enetsw_platform_data *pd) +{ + int ret; + + if (!BCMCPU_IS_6328() && !BCMCPU_IS_6362() && !BCMCPU_IS_6368()) + return -ENODEV; + + ret = register_shared(); + if (ret) + return ret; + + enetsw_res[0].start = bcm63xx_regset_address(RSET_ENETSW); + enetsw_res[0].end = enetsw_res[0].start; + enetsw_res[0].end += RSET_ENETSW_SIZE - 1; + enetsw_res[1].start = bcm63xx_get_irq_number(IRQ_ENETSW_RXDMA0); + enetsw_res[2].start = bcm63xx_get_irq_number(IRQ_ENETSW_TXDMA0); + if (!enetsw_res[2].start) + enetsw_res[2].start = -1; + + memcpy(bcm63xx_enetsw_device.dev.platform_data, pd, sizeof(*pd)); + + if (BCMCPU_IS_6328()) + enetsw_pd.num_ports = ENETSW_PORTS_6328; + else if (BCMCPU_IS_6362() || BCMCPU_IS_6368()) + enetsw_pd.num_ports = ENETSW_PORTS_6368; + + ret = platform_device_register(&bcm63xx_enetsw_device); + if (ret) + return ret; + + return 0; +} diff --git a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_dev_enet.h b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_dev_enet.h index d53f611184b9..118e3c938841 100644 --- a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_dev_enet.h +++ b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_dev_enet.h @@ -39,7 +39,35 @@ struct bcm63xx_enet_platform_data { int phy_id, int reg, int val)); }; +/* + * on board ethernet switch platform data + */ +#define ENETSW_MAX_PORT 8 +#define ENETSW_PORTS_6328 5 /* 4 FE PHY + 1 RGMII */ +#define ENETSW_PORTS_6368 6 /* 4 FE PHY + 2 RGMII */ + +#define ENETSW_RGMII_PORT0 4 + +struct bcm63xx_enetsw_port { + int used; + int phy_id; + + int bypass_link; + int force_speed; + int force_duplex_full; + + const char *name; +}; + +struct bcm63xx_enetsw_platform_data { + char mac_addr[ETH_ALEN]; + int num_ports; + struct bcm63xx_enetsw_port used_ports[ENETSW_MAX_PORT]; +}; + int __init bcm63xx_enet_register(int unit, const struct bcm63xx_enet_platform_data *pd); +int bcm63xx_enetsw_register(const struct bcm63xx_enetsw_platform_data *pd); + #endif /* ! BCM63XX_DEV_ENET_H_ */ diff --git a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h index 3203fe49b34d..0a2121abb1a6 100644 --- a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h +++ b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h @@ -830,10 +830,60 @@ * _REG relative to RSET_ENETSW *************************************************************************/ +/* Port traffic control */ +#define ENETSW_PTCTRL_REG(x) (0x0 + (x)) +#define ENETSW_PTCTRL_RXDIS_MASK (1 << 0) +#define ENETSW_PTCTRL_TXDIS_MASK (1 << 1) + +/* Switch mode register */ +#define ENETSW_SWMODE_REG (0xb) +#define ENETSW_SWMODE_FWD_EN_MASK (1 << 1) + +/* IMP override Register */ +#define ENETSW_IMPOV_REG (0xe) +#define ENETSW_IMPOV_FORCE_MASK (1 << 7) +#define ENETSW_IMPOV_TXFLOW_MASK (1 << 5) +#define ENETSW_IMPOV_RXFLOW_MASK (1 << 4) +#define ENETSW_IMPOV_1000_MASK (1 << 3) +#define ENETSW_IMPOV_100_MASK (1 << 2) +#define ENETSW_IMPOV_FDX_MASK (1 << 1) +#define ENETSW_IMPOV_LINKUP_MASK (1 << 0) + +/* Port override Register */ +#define ENETSW_PORTOV_REG(x) (0x58 + (x)) +#define ENETSW_PORTOV_ENABLE_MASK (1 << 6) +#define ENETSW_PORTOV_TXFLOW_MASK (1 << 5) +#define ENETSW_PORTOV_RXFLOW_MASK (1 << 4) +#define ENETSW_PORTOV_1000_MASK (1 << 3) +#define ENETSW_PORTOV_100_MASK (1 << 2) +#define ENETSW_PORTOV_FDX_MASK (1 << 1) +#define ENETSW_PORTOV_LINKUP_MASK (1 << 0) + +/* MDIO control register */ +#define ENETSW_MDIOC_REG (0xb0) +#define ENETSW_MDIOC_EXT_MASK (1 << 16) +#define ENETSW_MDIOC_REG_SHIFT 20 +#define ENETSW_MDIOC_PHYID_SHIFT 25 +#define ENETSW_MDIOC_RD_MASK (1 << 30) +#define ENETSW_MDIOC_WR_MASK (1 << 31) + +/* MDIO data register */ +#define ENETSW_MDIOD_REG (0xb4) + +/* Global Management Configuration Register */ +#define ENETSW_GMCR_REG (0x200) +#define ENETSW_GMCR_RST_MIB_MASK (1 << 0) + /* MIB register */ #define ENETSW_MIB_REG(x) (0x2800 + (x) * 4) #define ENETSW_MIB_REG_COUNT 47 +/* Jumbo control register port mask register */ +#define ENETSW_JMBCTL_PORT_REG (0x4004) + +/* Jumbo control mib good frame register */ +#define ENETSW_JMBCTL_MAXSIZE_REG (0x4008) + /************************************************************************* * _REG relative to RSET_OHCI_PRIV diff --git a/arch/mips/include/asm/mach-bcm63xx/board_bcm963xx.h b/arch/mips/include/asm/mach-bcm63xx/board_bcm963xx.h index 682bcf3b492a..d9aee1a833f3 100644 --- a/arch/mips/include/asm/mach-bcm63xx/board_bcm963xx.h +++ b/arch/mips/include/asm/mach-bcm63xx/board_bcm963xx.h @@ -24,6 +24,7 @@ struct board_info { /* enabled feature/device */ unsigned int has_enet0:1; unsigned int has_enet1:1; + unsigned int has_enetsw:1; unsigned int has_pci:1; unsigned int has_pccard:1; unsigned int has_ohci0:1; @@ -36,6 +37,7 @@ struct board_info { /* ethernet config */ struct bcm63xx_enet_platform_data enet0; struct bcm63xx_enet_platform_data enet1; + struct bcm63xx_enetsw_platform_data enetsw; /* USB config */ struct bcm63xx_usbd_platform_data usbd; diff --git a/drivers/net/ethernet/broadcom/bcm63xx_enet.c b/drivers/net/ethernet/broadcom/bcm63xx_enet.c index edaf76dc2487..fbbfc4acd53f 100644 --- a/drivers/net/ethernet/broadcom/bcm63xx_enet.c +++ b/drivers/net/ethernet/broadcom/bcm63xx_enet.c @@ -59,8 +59,43 @@ static inline void enet_writel(struct bcm_enet_priv *priv, } /* - * io helpers to access shared registers + * io helpers to access switch registers */ +static inline u32 enetsw_readl(struct bcm_enet_priv *priv, u32 off) +{ + return bcm_readl(priv->base + off); +} + +static inline void enetsw_writel(struct bcm_enet_priv *priv, + u32 val, u32 off) +{ + bcm_writel(val, priv->base + off); +} + +static inline u16 enetsw_readw(struct bcm_enet_priv *priv, u32 off) +{ + return bcm_readw(priv->base + off); +} + +static inline void enetsw_writew(struct bcm_enet_priv *priv, + u16 val, u32 off) +{ + bcm_writew(val, priv->base + off); +} + +static inline u8 enetsw_readb(struct bcm_enet_priv *priv, u32 off) +{ + return bcm_readb(priv->base + off); +} + +static inline void enetsw_writeb(struct bcm_enet_priv *priv, + u8 val, u32 off) +{ + bcm_writeb(val, priv->base + off); +} + + +/* io helpers to access shared registers */ static inline u32 enet_dma_readl(struct bcm_enet_priv *priv, u32 off) { return bcm_readl(bcm_enet_shared_base[0] + off); @@ -218,7 +253,6 @@ static int bcm_enet_refill_rx(struct net_device *dev) if (!skb) break; priv->rx_skb[desc_idx] = skb; - p = dma_map_single(&priv->pdev->dev, skb->data, priv->rx_skb_size, DMA_FROM_DEVICE); @@ -321,7 +355,8 @@ static int bcm_enet_receive_queue(struct net_device *dev, int budget) } /* recycle packet if it's marked as bad */ - if (unlikely(len_stat & DMADESC_ERR_MASK)) { + if (!priv->enet_is_sw && + unlikely(len_stat & DMADESC_ERR_MASK)) { dev->stats.rx_errors++; if (len_stat & DMADESC_OVSIZE_MASK) @@ -552,6 +587,26 @@ static int bcm_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) goto out_unlock; } + /* pad small packets sent on a switch device */ + if (priv->enet_is_sw && skb->len < 64) { + int needed = 64 - skb->len; + char *data; + + if (unlikely(skb_tailroom(skb) < needed)) { + struct sk_buff *nskb; + + nskb = skb_copy_expand(skb, 0, needed, GFP_ATOMIC); + if (!nskb) { + ret = NETDEV_TX_BUSY; + goto out_unlock; + } + dev_kfree_skb(skb); + skb = nskb; + } + data = skb_put(skb, needed); + memset(data, 0, needed); + } + /* point to the next available desc */ desc = &priv->tx_desc_cpu[priv->tx_curr_desc]; priv->tx_skb[priv->tx_curr_desc] = skb; @@ -959,9 +1014,9 @@ static int bcm_enet_open(struct net_device *dev) enet_writel(priv, priv->hw_mtu, ENET_TXMAXLEN_REG); /* set dma maximum burst len */ - enet_dmac_writel(priv, BCMENET_DMA_MAXBURST, + enet_dmac_writel(priv, priv->dma_maxburst, ENETDMAC_MAXBURST_REG(priv->rx_chan)); - enet_dmac_writel(priv, BCMENET_DMA_MAXBURST, + enet_dmac_writel(priv, priv->dma_maxburst, ENETDMAC_MAXBURST_REG(priv->tx_chan)); /* set correct transmit fifo watermark */ @@ -1567,7 +1622,7 @@ static int compute_hw_mtu(struct bcm_enet_priv *priv, int mtu) * it's appended */ priv->rx_skb_size = ALIGN(actual_mtu + ETH_FCS_LEN, - BCMENET_DMA_MAXBURST * 4); + priv->dma_maxburst * 4); return 0; } @@ -1674,6 +1729,9 @@ static int bcm_enet_probe(struct platform_device *pdev) return -ENOMEM; priv = netdev_priv(dev); + priv->enet_is_sw = false; + priv->dma_maxburst = BCMENET_DMA_MAXBURST; + ret = compute_hw_mtu(priv, dev->mtu); if (ret) goto out; @@ -1898,60 +1956,916 @@ struct platform_driver bcm63xx_enet_driver = { }; /* - * reserve & remap memory space shared between all macs + * switch mii access callbacks */ -static int bcm_enet_shared_probe(struct platform_device *pdev) +static int bcmenet_sw_mdio_read(struct bcm_enet_priv *priv, + int ext, int phy_id, int location) { - struct resource *res; - void __iomem *p[3]; - unsigned int i; + u32 reg; + int ret; - memset(bcm_enet_shared_base, 0, sizeof(bcm_enet_shared_base)); + spin_lock_bh(&priv->enetsw_mdio_lock); + enetsw_writel(priv, 0, ENETSW_MDIOC_REG); - for (i = 0; i < 3; i++) { - res = platform_get_resource(pdev, IORESOURCE_MEM, i); - p[i] = devm_ioremap_resource(&pdev->dev, res); - if (!p[i]) - return -ENOMEM; - } + reg = ENETSW_MDIOC_RD_MASK | + (phy_id << ENETSW_MDIOC_PHYID_SHIFT) | + (location << ENETSW_MDIOC_REG_SHIFT); - memcpy(bcm_enet_shared_base, p, sizeof(bcm_enet_shared_base)); + if (ext) + reg |= ENETSW_MDIOC_EXT_MASK; - return 0; + enetsw_writel(priv, reg, ENETSW_MDIOC_REG); + udelay(50); + ret = enetsw_readw(priv, ENETSW_MDIOD_REG); + spin_unlock_bh(&priv->enetsw_mdio_lock); + return ret; } -static int bcm_enet_shared_remove(struct platform_device *pdev) +static void bcmenet_sw_mdio_write(struct bcm_enet_priv *priv, + int ext, int phy_id, int location, + uint16_t data) { - return 0; + u32 reg; + + spin_lock_bh(&priv->enetsw_mdio_lock); + enetsw_writel(priv, 0, ENETSW_MDIOC_REG); + + reg = ENETSW_MDIOC_WR_MASK | + (phy_id << ENETSW_MDIOC_PHYID_SHIFT) | + (location << ENETSW_MDIOC_REG_SHIFT); + + if (ext) + reg |= ENETSW_MDIOC_EXT_MASK; + + reg |= data; + + enetsw_writel(priv, reg, ENETSW_MDIOC_REG); + udelay(50); + spin_unlock_bh(&priv->enetsw_mdio_lock); +} + +static inline int bcm_enet_port_is_rgmii(int portid) +{ + return portid >= ENETSW_RGMII_PORT0; } /* - * this "shared" driver is needed because both macs share a single - * address space + * enet sw PHY polling */ -struct platform_driver bcm63xx_enet_shared_driver = { - .probe = bcm_enet_shared_probe, - .remove = bcm_enet_shared_remove, - .driver = { - .name = "bcm63xx_enet_shared", - .owner = THIS_MODULE, - }, -}; +static void swphy_poll_timer(unsigned long data) +{ + struct bcm_enet_priv *priv = (struct bcm_enet_priv *)data; + unsigned int i; + + for (i = 0; i < priv->num_ports; i++) { + struct bcm63xx_enetsw_port *port; + int val, j, up, advertise, lpa, lpa2, speed, duplex, media; + int external_phy = bcm_enet_port_is_rgmii(i); + u8 override; + + port = &priv->used_ports[i]; + if (!port->used) + continue; + + if (port->bypass_link) + continue; + + /* dummy read to clear */ + for (j = 0; j < 2; j++) + val = bcmenet_sw_mdio_read(priv, external_phy, + port->phy_id, MII_BMSR); + + if (val == 0xffff) + continue; + + up = (val & BMSR_LSTATUS) ? 1 : 0; + if (!(up ^ priv->sw_port_link[i])) + continue; + + priv->sw_port_link[i] = up; + + /* link changed */ + if (!up) { + dev_info(&priv->pdev->dev, "link DOWN on %s\n", + port->name); + enetsw_writeb(priv, ENETSW_PORTOV_ENABLE_MASK, + ENETSW_PORTOV_REG(i)); + enetsw_writeb(priv, ENETSW_PTCTRL_RXDIS_MASK | + ENETSW_PTCTRL_TXDIS_MASK, + ENETSW_PTCTRL_REG(i)); + continue; + } + + advertise = bcmenet_sw_mdio_read(priv, external_phy, + port->phy_id, MII_ADVERTISE); + + lpa = bcmenet_sw_mdio_read(priv, external_phy, port->phy_id, + MII_LPA); + + lpa2 = bcmenet_sw_mdio_read(priv, external_phy, port->phy_id, + MII_STAT1000); + + /* figure out media and duplex from advertise and LPA values */ + media = mii_nway_result(lpa & advertise); + duplex = (media & ADVERTISE_FULL) ? 1 : 0; + if (lpa2 & LPA_1000FULL) + duplex = 1; + + if (lpa2 & (LPA_1000FULL | LPA_1000HALF)) + speed = 1000; + else { + if (media & (ADVERTISE_100FULL | ADVERTISE_100HALF)) + speed = 100; + else + speed = 10; + } + + dev_info(&priv->pdev->dev, + "link UP on %s, %dMbps, %s-duplex\n", + port->name, speed, duplex ? "full" : "half"); + + override = ENETSW_PORTOV_ENABLE_MASK | + ENETSW_PORTOV_LINKUP_MASK; + + if (speed == 1000) + override |= ENETSW_IMPOV_1000_MASK; + else if (speed == 100) + override |= ENETSW_IMPOV_100_MASK; + if (duplex) + override |= ENETSW_IMPOV_FDX_MASK; + + enetsw_writeb(priv, override, ENETSW_PORTOV_REG(i)); + enetsw_writeb(priv, 0, ENETSW_PTCTRL_REG(i)); + } + + priv->swphy_poll.expires = jiffies + HZ; + add_timer(&priv->swphy_poll); +} /* - * entry point + * open callback, allocate dma rings & buffers and start rx operation */ -static int __init bcm_enet_init(void) +static int bcm_enetsw_open(struct net_device *dev) { - int ret; + struct bcm_enet_priv *priv; + struct device *kdev; + int i, ret; + unsigned int size; + void *p; + u32 val; - ret = platform_driver_register(&bcm63xx_enet_shared_driver); - if (ret) - return ret; + priv = netdev_priv(dev); + kdev = &priv->pdev->dev; - ret = platform_driver_register(&bcm63xx_enet_driver); + /* mask all interrupts and request them */ + enet_dmac_writel(priv, 0, ENETDMAC_IRMASK_REG(priv->rx_chan)); + enet_dmac_writel(priv, 0, ENETDMAC_IRMASK_REG(priv->tx_chan)); + + ret = request_irq(priv->irq_rx, bcm_enet_isr_dma, + IRQF_DISABLED, dev->name, dev); if (ret) - platform_driver_unregister(&bcm63xx_enet_shared_driver); + goto out_freeirq; + + if (priv->irq_tx != -1) { + ret = request_irq(priv->irq_tx, bcm_enet_isr_dma, + IRQF_DISABLED, dev->name, dev); + if (ret) + goto out_freeirq_rx; + } + + /* allocate rx dma ring */ + size = priv->rx_ring_size * sizeof(struct bcm_enet_desc); + p = dma_alloc_coherent(kdev, size, &priv->rx_desc_dma, GFP_KERNEL); + if (!p) { + dev_err(kdev, "cannot allocate rx ring %u\n", size); + ret = -ENOMEM; + goto out_freeirq_tx; + } + + memset(p, 0, size); + priv->rx_desc_alloc_size = size; + priv->rx_desc_cpu = p; + + /* allocate tx dma ring */ + size = priv->tx_ring_size * sizeof(struct bcm_enet_desc); + p = dma_alloc_coherent(kdev, size, &priv->tx_desc_dma, GFP_KERNEL); + if (!p) { + dev_err(kdev, "cannot allocate tx ring\n"); + ret = -ENOMEM; + goto out_free_rx_ring; + } + + memset(p, 0, size); + priv->tx_desc_alloc_size = size; + priv->tx_desc_cpu = p; + + priv->tx_skb = kzalloc(sizeof(struct sk_buff *) * priv->tx_ring_size, + GFP_KERNEL); + if (!priv->tx_skb) { + dev_err(kdev, "cannot allocate rx skb queue\n"); + ret = -ENOMEM; + goto out_free_tx_ring; + } + + priv->tx_desc_count = priv->tx_ring_size; + priv->tx_dirty_desc = 0; + priv->tx_curr_desc = 0; + spin_lock_init(&priv->tx_lock); + + /* init & fill rx ring with skbs */ + priv->rx_skb = kzalloc(sizeof(struct sk_buff *) * priv->rx_ring_size, + GFP_KERNEL); + if (!priv->rx_skb) { + dev_err(kdev, "cannot allocate rx skb queue\n"); + ret = -ENOMEM; + goto out_free_tx_skb; + } + + priv->rx_desc_count = 0; + priv->rx_dirty_desc = 0; + priv->rx_curr_desc = 0; + + /* disable all ports */ + for (i = 0; i < priv->num_ports; i++) { + enetsw_writeb(priv, ENETSW_PORTOV_ENABLE_MASK, + ENETSW_PORTOV_REG(i)); + enetsw_writeb(priv, ENETSW_PTCTRL_RXDIS_MASK | + ENETSW_PTCTRL_TXDIS_MASK, + ENETSW_PTCTRL_REG(i)); + + priv->sw_port_link[i] = 0; + } + + /* reset mib */ + val = enetsw_readb(priv, ENETSW_GMCR_REG); + val |= ENETSW_GMCR_RST_MIB_MASK; + enetsw_writeb(priv, val, ENETSW_GMCR_REG); + mdelay(1); + val &= ~ENETSW_GMCR_RST_MIB_MASK; + enetsw_writeb(priv, val, ENETSW_GMCR_REG); + mdelay(1); + + /* force CPU port state */ + val = enetsw_readb(priv, ENETSW_IMPOV_REG); + val |= ENETSW_IMPOV_FORCE_MASK | ENETSW_IMPOV_LINKUP_MASK; + enetsw_writeb(priv, val, ENETSW_IMPOV_REG); + + /* enable switch forward engine */ + val = enetsw_readb(priv, ENETSW_SWMODE_REG); + val |= ENETSW_SWMODE_FWD_EN_MASK; + enetsw_writeb(priv, val, ENETSW_SWMODE_REG); + + /* enable jumbo on all ports */ + enetsw_writel(priv, 0x1ff, ENETSW_JMBCTL_PORT_REG); + enetsw_writew(priv, 9728, ENETSW_JMBCTL_MAXSIZE_REG); + + /* initialize flow control buffer allocation */ + enet_dma_writel(priv, ENETDMA_BUFALLOC_FORCE_MASK | 0, + ENETDMA_BUFALLOC_REG(priv->rx_chan)); + + if (bcm_enet_refill_rx(dev)) { + dev_err(kdev, "cannot allocate rx skb queue\n"); + ret = -ENOMEM; + goto out; + } + + /* write rx & tx ring addresses */ + enet_dmas_writel(priv, priv->rx_desc_dma, + ENETDMAS_RSTART_REG(priv->rx_chan)); + enet_dmas_writel(priv, priv->tx_desc_dma, + ENETDMAS_RSTART_REG(priv->tx_chan)); + + /* clear remaining state ram for rx & tx channel */ + enet_dmas_writel(priv, 0, ENETDMAS_SRAM2_REG(priv->rx_chan)); + enet_dmas_writel(priv, 0, ENETDMAS_SRAM2_REG(priv->tx_chan)); + enet_dmas_writel(priv, 0, ENETDMAS_SRAM3_REG(priv->rx_chan)); + enet_dmas_writel(priv, 0, ENETDMAS_SRAM3_REG(priv->tx_chan)); + enet_dmas_writel(priv, 0, ENETDMAS_SRAM4_REG(priv->rx_chan)); + enet_dmas_writel(priv, 0, ENETDMAS_SRAM4_REG(priv->tx_chan)); + + /* set dma maximum burst len */ + enet_dmac_writel(priv, priv->dma_maxburst, + ENETDMAC_MAXBURST_REG(priv->rx_chan)); + enet_dmac_writel(priv, priv->dma_maxburst, + ENETDMAC_MAXBURST_REG(priv->tx_chan)); + + /* set flow control low/high threshold to 1/3 / 2/3 */ + val = priv->rx_ring_size / 3; + enet_dma_writel(priv, val, ENETDMA_FLOWCL_REG(priv->rx_chan)); + val = (priv->rx_ring_size * 2) / 3; + enet_dma_writel(priv, val, ENETDMA_FLOWCH_REG(priv->rx_chan)); + + /* all set, enable mac and interrupts, start dma engine and + * kick rx dma channel + */ + wmb(); + enet_dma_writel(priv, ENETDMA_CFG_EN_MASK, ENETDMA_CFG_REG); + enet_dmac_writel(priv, ENETDMAC_CHANCFG_EN_MASK, + ENETDMAC_CHANCFG_REG(priv->rx_chan)); + + /* watch "packet transferred" interrupt in rx and tx */ + enet_dmac_writel(priv, ENETDMAC_IR_PKTDONE_MASK, + ENETDMAC_IR_REG(priv->rx_chan)); + enet_dmac_writel(priv, ENETDMAC_IR_PKTDONE_MASK, + ENETDMAC_IR_REG(priv->tx_chan)); + + /* make sure we enable napi before rx interrupt */ + napi_enable(&priv->napi); + + enet_dmac_writel(priv, ENETDMAC_IR_PKTDONE_MASK, + ENETDMAC_IRMASK_REG(priv->rx_chan)); + enet_dmac_writel(priv, ENETDMAC_IR_PKTDONE_MASK, + ENETDMAC_IRMASK_REG(priv->tx_chan)); + + netif_carrier_on(dev); + netif_start_queue(dev); + + /* apply override config for bypass_link ports here. */ + for (i = 0; i < priv->num_ports; i++) { + struct bcm63xx_enetsw_port *port; + u8 override; + port = &priv->used_ports[i]; + if (!port->used) + continue; + + if (!port->bypass_link) + continue; + + override = ENETSW_PORTOV_ENABLE_MASK | + ENETSW_PORTOV_LINKUP_MASK; + + switch (port->force_speed) { + case 1000: + override |= ENETSW_IMPOV_1000_MASK; + break; + case 100: + override |= ENETSW_IMPOV_100_MASK; + break; + case 10: + break; + default: + pr_warn("invalid forced speed on port %s: assume 10\n", + port->name); + break; + } + + if (port->force_duplex_full) + override |= ENETSW_IMPOV_FDX_MASK; + + + enetsw_writeb(priv, override, ENETSW_PORTOV_REG(i)); + enetsw_writeb(priv, 0, ENETSW_PTCTRL_REG(i)); + } + + /* start phy polling timer */ + init_timer(&priv->swphy_poll); + priv->swphy_poll.function = swphy_poll_timer; + priv->swphy_poll.data = (unsigned long)priv; + priv->swphy_poll.expires = jiffies; + add_timer(&priv->swphy_poll); + return 0; + +out: + for (i = 0; i < priv->rx_ring_size; i++) { + struct bcm_enet_desc *desc; + + if (!priv->rx_skb[i]) + continue; + + desc = &priv->rx_desc_cpu[i]; + dma_unmap_single(kdev, desc->address, priv->rx_skb_size, + DMA_FROM_DEVICE); + kfree_skb(priv->rx_skb[i]); + } + kfree(priv->rx_skb); + +out_free_tx_skb: + kfree(priv->tx_skb); + +out_free_tx_ring: + dma_free_coherent(kdev, priv->tx_desc_alloc_size, + priv->tx_desc_cpu, priv->tx_desc_dma); + +out_free_rx_ring: + dma_free_coherent(kdev, priv->rx_desc_alloc_size, + priv->rx_desc_cpu, priv->rx_desc_dma); + +out_freeirq_tx: + if (priv->irq_tx != -1) + free_irq(priv->irq_tx, dev); + +out_freeirq_rx: + free_irq(priv->irq_rx, dev); + +out_freeirq: + return ret; +} + +/* stop callback */ +static int bcm_enetsw_stop(struct net_device *dev) +{ + struct bcm_enet_priv *priv; + struct device *kdev; + int i; + + priv = netdev_priv(dev); + kdev = &priv->pdev->dev; + + del_timer_sync(&priv->swphy_poll); + netif_stop_queue(dev); + napi_disable(&priv->napi); + del_timer_sync(&priv->rx_timeout); + + /* mask all interrupts */ + enet_dmac_writel(priv, 0, ENETDMAC_IRMASK_REG(priv->rx_chan)); + enet_dmac_writel(priv, 0, ENETDMAC_IRMASK_REG(priv->tx_chan)); + + /* disable dma & mac */ + bcm_enet_disable_dma(priv, priv->tx_chan); + bcm_enet_disable_dma(priv, priv->rx_chan); + + /* force reclaim of all tx buffers */ + bcm_enet_tx_reclaim(dev, 1); + + /* free the rx skb ring */ + for (i = 0; i < priv->rx_ring_size; i++) { + struct bcm_enet_desc *desc; + + if (!priv->rx_skb[i]) + continue; + + desc = &priv->rx_desc_cpu[i]; + dma_unmap_single(kdev, desc->address, priv->rx_skb_size, + DMA_FROM_DEVICE); + kfree_skb(priv->rx_skb[i]); + } + + /* free remaining allocated memory */ + kfree(priv->rx_skb); + kfree(priv->tx_skb); + dma_free_coherent(kdev, priv->rx_desc_alloc_size, + priv->rx_desc_cpu, priv->rx_desc_dma); + dma_free_coherent(kdev, priv->tx_desc_alloc_size, + priv->tx_desc_cpu, priv->tx_desc_dma); + if (priv->irq_tx != -1) + free_irq(priv->irq_tx, dev); + free_irq(priv->irq_rx, dev); + + return 0; +} + +/* try to sort out phy external status by walking the used_port field + * in the bcm_enet_priv structure. in case the phy address is not + * assigned to any physical port on the switch, assume it is external + * (and yell at the user). + */ +static int bcm_enetsw_phy_is_external(struct bcm_enet_priv *priv, int phy_id) +{ + int i; + + for (i = 0; i < priv->num_ports; ++i) { + if (!priv->used_ports[i].used) + continue; + if (priv->used_ports[i].phy_id == phy_id) + return bcm_enet_port_is_rgmii(i); + } + + printk_once(KERN_WARNING "bcm63xx_enet: could not find a used port with phy_id %i, assuming phy is external\n", + phy_id); + return 1; +} + +/* can't use bcmenet_sw_mdio_read directly as we need to sort out + * external/internal status of the given phy_id first. + */ +static int bcm_enetsw_mii_mdio_read(struct net_device *dev, int phy_id, + int location) +{ + struct bcm_enet_priv *priv; + + priv = netdev_priv(dev); + return bcmenet_sw_mdio_read(priv, + bcm_enetsw_phy_is_external(priv, phy_id), + phy_id, location); +} + +/* can't use bcmenet_sw_mdio_write directly as we need to sort out + * external/internal status of the given phy_id first. + */ +static void bcm_enetsw_mii_mdio_write(struct net_device *dev, int phy_id, + int location, + int val) +{ + struct bcm_enet_priv *priv; + + priv = netdev_priv(dev); + bcmenet_sw_mdio_write(priv, bcm_enetsw_phy_is_external(priv, phy_id), + phy_id, location, val); +} + +static int bcm_enetsw_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct mii_if_info mii; + + mii.dev = dev; + mii.mdio_read = bcm_enetsw_mii_mdio_read; + mii.mdio_write = bcm_enetsw_mii_mdio_write; + mii.phy_id = 0; + mii.phy_id_mask = 0x3f; + mii.reg_num_mask = 0x1f; + return generic_mii_ioctl(&mii, if_mii(rq), cmd, NULL); + +} + +static const struct net_device_ops bcm_enetsw_ops = { + .ndo_open = bcm_enetsw_open, + .ndo_stop = bcm_enetsw_stop, + .ndo_start_xmit = bcm_enet_start_xmit, + .ndo_change_mtu = bcm_enet_change_mtu, + .ndo_do_ioctl = bcm_enetsw_ioctl, +}; + + +static const struct bcm_enet_stats bcm_enetsw_gstrings_stats[] = { + { "rx_packets", DEV_STAT(rx_packets), -1 }, + { "tx_packets", DEV_STAT(tx_packets), -1 }, + { "rx_bytes", DEV_STAT(rx_bytes), -1 }, + { "tx_bytes", DEV_STAT(tx_bytes), -1 }, + { "rx_errors", DEV_STAT(rx_errors), -1 }, + { "tx_errors", DEV_STAT(tx_errors), -1 }, + { "rx_dropped", DEV_STAT(rx_dropped), -1 }, + { "tx_dropped", DEV_STAT(tx_dropped), -1 }, + + { "tx_good_octets", GEN_STAT(mib.tx_gd_octets), ETHSW_MIB_RX_GD_OCT }, + { "tx_unicast", GEN_STAT(mib.tx_unicast), ETHSW_MIB_RX_BRDCAST }, + { "tx_broadcast", GEN_STAT(mib.tx_brdcast), ETHSW_MIB_RX_BRDCAST }, + { "tx_multicast", GEN_STAT(mib.tx_mult), ETHSW_MIB_RX_MULT }, + { "tx_64_octets", GEN_STAT(mib.tx_64), ETHSW_MIB_RX_64 }, + { "tx_65_127_oct", GEN_STAT(mib.tx_65_127), ETHSW_MIB_RX_65_127 }, + { "tx_128_255_oct", GEN_STAT(mib.tx_128_255), ETHSW_MIB_RX_128_255 }, + { "tx_256_511_oct", GEN_STAT(mib.tx_256_511), ETHSW_MIB_RX_256_511 }, + { "tx_512_1023_oct", GEN_STAT(mib.tx_512_1023), ETHSW_MIB_RX_512_1023}, + { "tx_1024_1522_oct", GEN_STAT(mib.tx_1024_max), + ETHSW_MIB_RX_1024_1522 }, + { "tx_1523_2047_oct", GEN_STAT(mib.tx_1523_2047), + ETHSW_MIB_RX_1523_2047 }, + { "tx_2048_4095_oct", GEN_STAT(mib.tx_2048_4095), + ETHSW_MIB_RX_2048_4095 }, + { "tx_4096_8191_oct", GEN_STAT(mib.tx_4096_8191), + ETHSW_MIB_RX_4096_8191 }, + { "tx_8192_9728_oct", GEN_STAT(mib.tx_8192_9728), + ETHSW_MIB_RX_8192_9728 }, + { "tx_oversize", GEN_STAT(mib.tx_ovr), ETHSW_MIB_RX_OVR }, + { "tx_oversize_drop", GEN_STAT(mib.tx_ovr), ETHSW_MIB_RX_OVR_DISC }, + { "tx_dropped", GEN_STAT(mib.tx_drop), ETHSW_MIB_RX_DROP }, + { "tx_undersize", GEN_STAT(mib.tx_underrun), ETHSW_MIB_RX_UND }, + { "tx_pause", GEN_STAT(mib.tx_pause), ETHSW_MIB_RX_PAUSE }, + + { "rx_good_octets", GEN_STAT(mib.rx_gd_octets), ETHSW_MIB_TX_ALL_OCT }, + { "rx_broadcast", GEN_STAT(mib.rx_brdcast), ETHSW_MIB_TX_BRDCAST }, + { "rx_multicast", GEN_STAT(mib.rx_mult), ETHSW_MIB_TX_MULT }, + { "rx_unicast", GEN_STAT(mib.rx_unicast), ETHSW_MIB_TX_MULT }, + { "rx_pause", GEN_STAT(mib.rx_pause), ETHSW_MIB_TX_PAUSE }, + { "rx_dropped", GEN_STAT(mib.rx_drop), ETHSW_MIB_TX_DROP_PKTS }, + +}; + +#define BCM_ENETSW_STATS_LEN \ + (sizeof(bcm_enetsw_gstrings_stats) / sizeof(struct bcm_enet_stats)) + +static void bcm_enetsw_get_strings(struct net_device *netdev, + u32 stringset, u8 *data) +{ + int i; + + switch (stringset) { + case ETH_SS_STATS: + for (i = 0; i < BCM_ENETSW_STATS_LEN; i++) { + memcpy(data + i * ETH_GSTRING_LEN, + bcm_enetsw_gstrings_stats[i].stat_string, + ETH_GSTRING_LEN); + } + break; + } +} + +static int bcm_enetsw_get_sset_count(struct net_device *netdev, + int string_set) +{ + switch (string_set) { + case ETH_SS_STATS: + return BCM_ENETSW_STATS_LEN; + default: + return -EINVAL; + } +} + +static void bcm_enetsw_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *drvinfo) +{ + strncpy(drvinfo->driver, bcm_enet_driver_name, 32); + strncpy(drvinfo->version, bcm_enet_driver_version, 32); + strncpy(drvinfo->fw_version, "N/A", 32); + strncpy(drvinfo->bus_info, "bcm63xx", 32); + drvinfo->n_stats = BCM_ENETSW_STATS_LEN; +} + +static void bcm_enetsw_get_ethtool_stats(struct net_device *netdev, + struct ethtool_stats *stats, + u64 *data) +{ + struct bcm_enet_priv *priv; + int i; + + priv = netdev_priv(netdev); + + for (i = 0; i < BCM_ENETSW_STATS_LEN; i++) { + const struct bcm_enet_stats *s; + u32 lo, hi; + char *p; + int reg; + + s = &bcm_enetsw_gstrings_stats[i]; + + reg = s->mib_reg; + if (reg == -1) + continue; + + lo = enetsw_readl(priv, ENETSW_MIB_REG(reg)); + p = (char *)priv + s->stat_offset; + + if (s->sizeof_stat == sizeof(u64)) { + hi = enetsw_readl(priv, ENETSW_MIB_REG(reg + 1)); + *(u64 *)p = ((u64)hi << 32 | lo); + } else { + *(u32 *)p = lo; + } + } + + for (i = 0; i < BCM_ENETSW_STATS_LEN; i++) { + const struct bcm_enet_stats *s; + char *p; + + s = &bcm_enetsw_gstrings_stats[i]; + + if (s->mib_reg == -1) + p = (char *)&netdev->stats + s->stat_offset; + else + p = (char *)priv + s->stat_offset; + + data[i] = (s->sizeof_stat == sizeof(u64)) ? + *(u64 *)p : *(u32 *)p; + } +} + +static void bcm_enetsw_get_ringparam(struct net_device *dev, + struct ethtool_ringparam *ering) +{ + struct bcm_enet_priv *priv; + + priv = netdev_priv(dev); + + /* rx/tx ring is actually only limited by memory */ + ering->rx_max_pending = 8192; + ering->tx_max_pending = 8192; + ering->rx_mini_max_pending = 0; + ering->rx_jumbo_max_pending = 0; + ering->rx_pending = priv->rx_ring_size; + ering->tx_pending = priv->tx_ring_size; +} + +static int bcm_enetsw_set_ringparam(struct net_device *dev, + struct ethtool_ringparam *ering) +{ + struct bcm_enet_priv *priv; + int was_running; + + priv = netdev_priv(dev); + + was_running = 0; + if (netif_running(dev)) { + bcm_enetsw_stop(dev); + was_running = 1; + } + + priv->rx_ring_size = ering->rx_pending; + priv->tx_ring_size = ering->tx_pending; + + if (was_running) { + int err; + + err = bcm_enetsw_open(dev); + if (err) + dev_close(dev); + } + return 0; +} + +static struct ethtool_ops bcm_enetsw_ethtool_ops = { + .get_strings = bcm_enetsw_get_strings, + .get_sset_count = bcm_enetsw_get_sset_count, + .get_ethtool_stats = bcm_enetsw_get_ethtool_stats, + .get_drvinfo = bcm_enetsw_get_drvinfo, + .get_ringparam = bcm_enetsw_get_ringparam, + .set_ringparam = bcm_enetsw_set_ringparam, +}; + +/* allocate netdevice, request register memory and register device. */ +static int bcm_enetsw_probe(struct platform_device *pdev) +{ + struct bcm_enet_priv *priv; + struct net_device *dev; + struct bcm63xx_enetsw_platform_data *pd; + struct resource *res_mem; + int ret, irq_rx, irq_tx; + + /* stop if shared driver failed, assume driver->probe will be + * called in the same order we register devices (correct ?) + */ + if (!bcm_enet_shared_base[0]) + return -ENODEV; + + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq_rx = platform_get_irq(pdev, 0); + irq_tx = platform_get_irq(pdev, 1); + if (!res_mem || irq_rx < 0) + return -ENODEV; + + ret = 0; + dev = alloc_etherdev(sizeof(*priv)); + if (!dev) + return -ENOMEM; + priv = netdev_priv(dev); + memset(priv, 0, sizeof(*priv)); + + /* initialize default and fetch platform data */ + priv->enet_is_sw = true; + priv->irq_rx = irq_rx; + priv->irq_tx = irq_tx; + priv->rx_ring_size = BCMENET_DEF_RX_DESC; + priv->tx_ring_size = BCMENET_DEF_TX_DESC; + priv->dma_maxburst = BCMENETSW_DMA_MAXBURST; + + pd = pdev->dev.platform_data; + if (pd) { + memcpy(dev->dev_addr, pd->mac_addr, ETH_ALEN); + memcpy(priv->used_ports, pd->used_ports, + sizeof(pd->used_ports)); + priv->num_ports = pd->num_ports; + } + + ret = compute_hw_mtu(priv, dev->mtu); + if (ret) + goto out; + + if (!request_mem_region(res_mem->start, resource_size(res_mem), + "bcm63xx_enetsw")) { + ret = -EBUSY; + goto out; + } + + priv->base = ioremap(res_mem->start, resource_size(res_mem)); + if (priv->base == NULL) { + ret = -ENOMEM; + goto out_release_mem; + } + + priv->mac_clk = clk_get(&pdev->dev, "enetsw"); + if (IS_ERR(priv->mac_clk)) { + ret = PTR_ERR(priv->mac_clk); + goto out_unmap; + } + clk_enable(priv->mac_clk); + + priv->rx_chan = 0; + priv->tx_chan = 1; + spin_lock_init(&priv->rx_lock); + + /* init rx timeout (used for oom) */ + init_timer(&priv->rx_timeout); + priv->rx_timeout.function = bcm_enet_refill_rx_timer; + priv->rx_timeout.data = (unsigned long)dev; + + /* register netdevice */ + dev->netdev_ops = &bcm_enetsw_ops; + netif_napi_add(dev, &priv->napi, bcm_enet_poll, 16); + SET_ETHTOOL_OPS(dev, &bcm_enetsw_ethtool_ops); + SET_NETDEV_DEV(dev, &pdev->dev); + + spin_lock_init(&priv->enetsw_mdio_lock); + + ret = register_netdev(dev); + if (ret) + goto out_put_clk; + + netif_carrier_off(dev); + platform_set_drvdata(pdev, dev); + priv->pdev = pdev; + priv->net_dev = dev; + + return 0; + +out_put_clk: + clk_put(priv->mac_clk); + +out_unmap: + iounmap(priv->base); + +out_release_mem: + release_mem_region(res_mem->start, resource_size(res_mem)); +out: + free_netdev(dev); + return ret; +} + + +/* exit func, stops hardware and unregisters netdevice */ +static int bcm_enetsw_remove(struct platform_device *pdev) +{ + struct bcm_enet_priv *priv; + struct net_device *dev; + struct resource *res; + + /* stop netdevice */ + dev = platform_get_drvdata(pdev); + priv = netdev_priv(dev); + unregister_netdev(dev); + + /* release device resources */ + iounmap(priv->base); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + + platform_set_drvdata(pdev, NULL); + free_netdev(dev); + return 0; +} + +struct platform_driver bcm63xx_enetsw_driver = { + .probe = bcm_enetsw_probe, + .remove = bcm_enetsw_remove, + .driver = { + .name = "bcm63xx_enetsw", + .owner = THIS_MODULE, + }, +}; + +/* reserve & remap memory space shared between all macs */ +static int bcm_enet_shared_probe(struct platform_device *pdev) +{ + struct resource *res; + void __iomem *p[3]; + unsigned int i; + + memset(bcm_enet_shared_base, 0, sizeof(bcm_enet_shared_base)); + + for (i = 0; i < 3; i++) { + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + p[i] = devm_ioremap_resource(&pdev->dev, res); + if (!p[i]) + return -ENOMEM; + } + + memcpy(bcm_enet_shared_base, p, sizeof(bcm_enet_shared_base)); + + return 0; +} + +static int bcm_enet_shared_remove(struct platform_device *pdev) +{ + return 0; +} + +/* this "shared" driver is needed because both macs share a single + * address space + */ +struct platform_driver bcm63xx_enet_shared_driver = { + .probe = bcm_enet_shared_probe, + .remove = bcm_enet_shared_remove, + .driver = { + .name = "bcm63xx_enet_shared", + .owner = THIS_MODULE, + }, +}; + +/* entry point */ +static int __init bcm_enet_init(void) +{ + int ret; + + ret = platform_driver_register(&bcm63xx_enet_shared_driver); + if (ret) + return ret; + + ret = platform_driver_register(&bcm63xx_enet_driver); + if (ret) + platform_driver_unregister(&bcm63xx_enet_shared_driver); + + ret = platform_driver_register(&bcm63xx_enetsw_driver); + if (ret) { + platform_driver_unregister(&bcm63xx_enet_driver); + platform_driver_unregister(&bcm63xx_enet_shared_driver); + } return ret; } @@ -1959,6 +2873,7 @@ static int __init bcm_enet_init(void) static void __exit bcm_enet_exit(void) { platform_driver_unregister(&bcm63xx_enet_driver); + platform_driver_unregister(&bcm63xx_enetsw_driver); platform_driver_unregister(&bcm63xx_enet_shared_driver); } diff --git a/drivers/net/ethernet/broadcom/bcm63xx_enet.h b/drivers/net/ethernet/broadcom/bcm63xx_enet.h index 133d5857b9e2..721ffbaef8d2 100644 --- a/drivers/net/ethernet/broadcom/bcm63xx_enet.h +++ b/drivers/net/ethernet/broadcom/bcm63xx_enet.h @@ -18,6 +18,7 @@ /* maximum burst len for dma (4 bytes unit) */ #define BCMENET_DMA_MAXBURST 16 +#define BCMENETSW_DMA_MAXBURST 8 /* tx transmit threshold (4 bytes unit), fifo is 256 bytes, the value * must be low enough so that a DMA transfer of above burst length can @@ -84,11 +85,60 @@ #define ETH_MIB_RX_CNTRL 54 +/* + * SW MIB Counters register definitions +*/ +#define ETHSW_MIB_TX_ALL_OCT 0 +#define ETHSW_MIB_TX_DROP_PKTS 2 +#define ETHSW_MIB_TX_QOS_PKTS 3 +#define ETHSW_MIB_TX_BRDCAST 4 +#define ETHSW_MIB_TX_MULT 5 +#define ETHSW_MIB_TX_UNI 6 +#define ETHSW_MIB_TX_COL 7 +#define ETHSW_MIB_TX_1_COL 8 +#define ETHSW_MIB_TX_M_COL 9 +#define ETHSW_MIB_TX_DEF 10 +#define ETHSW_MIB_TX_LATE 11 +#define ETHSW_MIB_TX_EX_COL 12 +#define ETHSW_MIB_TX_PAUSE 14 +#define ETHSW_MIB_TX_QOS_OCT 15 + +#define ETHSW_MIB_RX_ALL_OCT 17 +#define ETHSW_MIB_RX_UND 19 +#define ETHSW_MIB_RX_PAUSE 20 +#define ETHSW_MIB_RX_64 21 +#define ETHSW_MIB_RX_65_127 22 +#define ETHSW_MIB_RX_128_255 23 +#define ETHSW_MIB_RX_256_511 24 +#define ETHSW_MIB_RX_512_1023 25 +#define ETHSW_MIB_RX_1024_1522 26 +#define ETHSW_MIB_RX_OVR 27 +#define ETHSW_MIB_RX_JAB 28 +#define ETHSW_MIB_RX_ALIGN 29 +#define ETHSW_MIB_RX_CRC 30 +#define ETHSW_MIB_RX_GD_OCT 31 +#define ETHSW_MIB_RX_DROP 33 +#define ETHSW_MIB_RX_UNI 34 +#define ETHSW_MIB_RX_MULT 35 +#define ETHSW_MIB_RX_BRDCAST 36 +#define ETHSW_MIB_RX_SA_CHANGE 37 +#define ETHSW_MIB_RX_FRAG 38 +#define ETHSW_MIB_RX_OVR_DISC 39 +#define ETHSW_MIB_RX_SYM 40 +#define ETHSW_MIB_RX_QOS_PKTS 41 +#define ETHSW_MIB_RX_QOS_OCT 42 +#define ETHSW_MIB_RX_1523_2047 44 +#define ETHSW_MIB_RX_2048_4095 45 +#define ETHSW_MIB_RX_4096_8191 46 +#define ETHSW_MIB_RX_8192_9728 47 + + struct bcm_enet_mib_counters { u64 tx_gd_octets; u32 tx_gd_pkts; u32 tx_all_octets; u32 tx_all_pkts; + u32 tx_unicast; u32 tx_brdcast; u32 tx_mult; u32 tx_64; @@ -97,7 +147,12 @@ struct bcm_enet_mib_counters { u32 tx_256_511; u32 tx_512_1023; u32 tx_1024_max; + u32 tx_1523_2047; + u32 tx_2048_4095; + u32 tx_4096_8191; + u32 tx_8192_9728; u32 tx_jab; + u32 tx_drop; u32 tx_ovr; u32 tx_frag; u32 tx_underrun; @@ -114,6 +169,7 @@ struct bcm_enet_mib_counters { u32 rx_all_octets; u32 rx_all_pkts; u32 rx_brdcast; + u32 rx_unicast; u32 rx_mult; u32 rx_64; u32 rx_65_127; @@ -197,6 +253,9 @@ struct bcm_enet_priv { /* number of dma desc in tx ring */ int tx_ring_size; + /* maximum dma burst size */ + int dma_maxburst; + /* cpu view of rx dma ring */ struct bcm_enet_desc *tx_desc_cpu; @@ -269,6 +328,18 @@ struct bcm_enet_priv { /* maximum hardware transmit/receive size */ unsigned int hw_mtu; + + bool enet_is_sw; + + /* port mapping for switch devices */ + int num_ports; + struct bcm63xx_enetsw_port used_ports[ENETSW_MAX_PORT]; + int sw_port_link[ENETSW_MAX_PORT]; + + /* used to poll switch port state */ + struct timer_list swphy_poll; + spinlock_t enetsw_mdio_lock; }; + #endif /* ! BCM63XX_ENET_H_ */ -- cgit v1.2.3 From 102d6dba306c825cd5c310f73868b130931f47aa Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 9 Apr 2013 09:18:44 +0200 Subject: drm: add unpin function to prime helpers Prevents buffers from being pinned forever. Signed-off-by: Maarten Lankhorst Reviewed-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_prime.c | 13 +++++++++++-- include/drm/drmP.h | 1 + 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index dcde35231e25..1a36b0c22732 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -92,10 +92,13 @@ static void drm_gem_unmap_dma_buf(struct dma_buf_attachment *attach, static void drm_gem_dmabuf_release(struct dma_buf *dma_buf) { struct drm_gem_object *obj = dma_buf->priv; + struct drm_device *dev = obj->dev; if (obj->export_dma_buf == dma_buf) { /* drop the reference on the export fd holds */ obj->export_dma_buf = NULL; + if (dev->driver->gem_prime_unpin) + dev->driver->gem_prime_unpin(obj); drm_gem_object_unreference_unlocked(obj); } } @@ -185,13 +188,19 @@ static const struct dma_buf_ops drm_gem_prime_dmabuf_ops = { struct dma_buf *drm_gem_prime_export(struct drm_device *dev, struct drm_gem_object *obj, int flags) { + struct dma_buf *buf; + if (dev->driver->gem_prime_pin) { int ret = dev->driver->gem_prime_pin(obj); if (ret) return ERR_PTR(ret); } - return dma_buf_export(obj, &drm_gem_prime_dmabuf_ops, obj->size, - 0600); + buf = dma_buf_export(obj, &drm_gem_prime_dmabuf_ops, obj->size, + 0600); + + if (IS_ERR(buf) && dev->driver->gem_prime_unpin) + dev->driver->gem_prime_unpin(obj); + return buf; } EXPORT_SYMBOL(drm_gem_prime_export); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index b06f5afe10ce..57f60a743da0 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -930,6 +930,7 @@ struct drm_driver { struct dma_buf *dma_buf); /* low-level interface used by drm_gem_prime_{import,export} */ int (*gem_prime_pin)(struct drm_gem_object *obj); + void (*gem_prime_unpin)(struct drm_gem_object *obj); struct sg_table *(*gem_prime_get_sg_table)(struct drm_gem_object *obj); struct drm_gem_object *(*gem_prime_import_sg_table)( struct drm_device *dev, size_t size, -- cgit v1.2.3 From 135419501a88af815f0b352d6599534c854d3281 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 10 Jun 2013 10:02:39 +0530 Subject: doc: clk: Fix incorrect wording Signed-off-by: Sachin Kamat Acked-by: Rob Landley Signed-off-by: Jiri Kosina --- Documentation/clk.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/clk.txt b/Documentation/clk.txt index b9911c27f496..6f68ba0d1e01 100644 --- a/Documentation/clk.txt +++ b/Documentation/clk.txt @@ -32,7 +32,7 @@ hardware-specific bits for the hypothetical "foo" hardware. Tying the two halves of this interface together is struct clk_hw, which is defined in struct clk_foo and pointed to within struct clk. This -allows easy for navigation between the two discrete halves of the common +allows for easy navigation between the two discrete halves of the common clock interface. Part 2 - common data structures and api -- cgit v1.2.3 From ca793f75d91d61fd0009c4179eb88aac54a74239 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 9 Apr 2013 09:52:54 +0200 Subject: drm: move pinning/unpinning to buffer attach This allows importing bo's to own device to work without requiring that the buffer is pinned in GART. Signed-off-by: Maarten Lankhorst Reviewed-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_prime.c | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index 1a36b0c22732..58ac6770a8c6 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -64,6 +64,29 @@ struct drm_prime_member { }; static int drm_prime_add_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf, uint32_t handle); +static int drm_gem_map_attach(struct dma_buf *dma_buf, + struct device *target_dev, + struct dma_buf_attachment *attach) +{ + struct drm_gem_object *obj = dma_buf->priv; + struct drm_device *dev = obj->dev; + + if (!dev->driver->gem_prime_pin) + return 0; + + return dev->driver->gem_prime_pin(obj); +} + +static void drm_gem_map_detach(struct dma_buf *dma_buf, + struct dma_buf_attachment *attach) +{ + struct drm_gem_object *obj = dma_buf->priv; + struct drm_device *dev = obj->dev; + + if (dev->driver->gem_prime_unpin) + dev->driver->gem_prime_unpin(obj); +} + static struct sg_table *drm_gem_map_dma_buf(struct dma_buf_attachment *attach, enum dma_data_direction dir) { @@ -92,13 +115,10 @@ static void drm_gem_unmap_dma_buf(struct dma_buf_attachment *attach, static void drm_gem_dmabuf_release(struct dma_buf *dma_buf) { struct drm_gem_object *obj = dma_buf->priv; - struct drm_device *dev = obj->dev; if (obj->export_dma_buf == dma_buf) { /* drop the reference on the export fd holds */ obj->export_dma_buf = NULL; - if (dev->driver->gem_prime_unpin) - dev->driver->gem_prime_unpin(obj); drm_gem_object_unreference_unlocked(obj); } } @@ -149,6 +169,8 @@ static int drm_gem_dmabuf_mmap(struct dma_buf *dma_buf, } static const struct dma_buf_ops drm_gem_prime_dmabuf_ops = { + .attach = drm_gem_map_attach, + .detach = drm_gem_map_detach, .map_dma_buf = drm_gem_map_dma_buf, .unmap_dma_buf = drm_gem_unmap_dma_buf, .release = drm_gem_dmabuf_release, @@ -188,19 +210,8 @@ static const struct dma_buf_ops drm_gem_prime_dmabuf_ops = { struct dma_buf *drm_gem_prime_export(struct drm_device *dev, struct drm_gem_object *obj, int flags) { - struct dma_buf *buf; - - if (dev->driver->gem_prime_pin) { - int ret = dev->driver->gem_prime_pin(obj); - if (ret) - return ERR_PTR(ret); - } - buf = dma_buf_export(obj, &drm_gem_prime_dmabuf_ops, obj->size, + return dma_buf_export(obj, &drm_gem_prime_dmabuf_ops, obj->size, 0600); - - if (IS_ERR(buf) && dev->driver->gem_prime_unpin) - dev->driver->gem_prime_unpin(obj); - return buf; } EXPORT_SYMBOL(drm_gem_prime_export); -- cgit v1.2.3 From 6ba6d03e69125ef42a63e90d45e49c659ea3c34f Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Mon, 10 Jun 2013 11:15:10 +0300 Subject: drm: Print pretty names for pixel formats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than just printing the pixel format as a hex number, decode the fourcc into human readable form, and also decode the LE vs. BE flag. Keep printing the raw hex number too in case it contains non-printable characters. Some examples what the new drm_get_format_name() produces: DRM_FORMAT_XRGB8888: "XR24 little-endian (0x34325258)" DRM_FORMAT_YUYV: "YUYV little-endian (0x56595559)" DRM_FORMAT_RGB565|DRM_FORMAT_BIG_ENDIAN: "RG16 big-endian (0xb6314752)" Unprintable characters: "D??? big-endian (0xff7f0244)" v2: Fix patch author Signed-off-by: Ville Syrjälä Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_crtc.c | 29 +++++++++++++++++++++++++++-- include/drm/drm_crtc.h | 1 + 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index e7e92429d10f..079996ad02a7 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -29,6 +29,7 @@ * Dave Airlie * Jesse Barnes */ +#include #include #include #include @@ -252,6 +253,28 @@ char *drm_get_connector_status_name(enum drm_connector_status status) } EXPORT_SYMBOL(drm_get_connector_status_name); +static char printable_char(int c) +{ + return isascii(c) && isprint(c) ? c : '?'; +} + +char *drm_get_format_name(uint32_t format) +{ + static char buf[32]; + + snprintf(buf, sizeof(buf), + "%c%c%c%c %s-endian (0x%08x)", + printable_char(format & 0xff), + printable_char((format >> 8) & 0xff), + printable_char((format >> 16) & 0xff), + printable_char((format >> 24) & 0x7f), + format & DRM_FORMAT_BIG_ENDIAN ? "big" : "little", + format); + + return buf; +} +EXPORT_SYMBOL(drm_get_format_name); + /** * drm_mode_object_get - allocate a new modeset identifier * @dev: DRM device @@ -1834,7 +1857,8 @@ int drm_mode_setplane(struct drm_device *dev, void *data, if (fb->pixel_format == plane->format_types[i]) break; if (i == plane->format_count) { - DRM_DEBUG_KMS("Invalid pixel format 0x%08x\n", fb->pixel_format); + DRM_DEBUG_KMS("Invalid pixel format %s\n", + drm_get_format_name(fb->pixel_format)); ret = -EINVAL; goto out; } @@ -2312,7 +2336,8 @@ static int framebuffer_check(const struct drm_mode_fb_cmd2 *r) ret = format_check(r); if (ret) { - DRM_DEBUG_KMS("bad framebuffer format 0x%08x\n", r->pixel_format); + DRM_DEBUG_KMS("bad framebuffer format %s\n", + drm_get_format_name(r->pixel_format)); return ret; } diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index adb3f9b625f6..2cbbfd44c6df 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1094,5 +1094,6 @@ extern int drm_format_num_planes(uint32_t format); extern int drm_format_plane_cpp(uint32_t format, int plane); extern int drm_format_horz_chroma_subsampling(uint32_t format); extern int drm_format_vert_chroma_subsampling(uint32_t format); +extern char *drm_get_format_name(uint32_t format); #endif /* __DRM_CRTC_H__ */ -- cgit v1.2.3 From 4ee62c7669be1a6f1dd407e5ba7e38c0e2204e92 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Fri, 7 Jun 2013 15:43:05 +0000 Subject: drm/i915: Print pretty names for pixel formats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use drm_get_format_name to print more readable pixel format names in debug output. Also unify the debug messages to say "unsupported pixel format", which better describes what is going on. Signed-off-by: Ville Syrjälä Reviewed-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/intel_display.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index e81eb20e2c88..a4c9f56afb38 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8945,7 +8945,8 @@ int intel_framebuffer_init(struct drm_device *dev, case DRM_FORMAT_XRGB1555: case DRM_FORMAT_ARGB1555: if (INTEL_INFO(dev)->gen > 3) { - DRM_DEBUG("invalid format: 0x%08x\n", mode_cmd->pixel_format); + DRM_DEBUG("unsupported pixel format: %s\n", + drm_get_format_name(mode_cmd->pixel_format)); return -EINVAL; } break; @@ -8956,7 +8957,8 @@ int intel_framebuffer_init(struct drm_device *dev, case DRM_FORMAT_XBGR2101010: case DRM_FORMAT_ABGR2101010: if (INTEL_INFO(dev)->gen < 4) { - DRM_DEBUG("invalid format: 0x%08x\n", mode_cmd->pixel_format); + DRM_DEBUG("unsupported pixel format: %s\n", + drm_get_format_name(mode_cmd->pixel_format)); return -EINVAL; } break; @@ -8965,12 +8967,14 @@ int intel_framebuffer_init(struct drm_device *dev, case DRM_FORMAT_YVYU: case DRM_FORMAT_VYUY: if (INTEL_INFO(dev)->gen < 5) { - DRM_DEBUG("invalid format: 0x%08x\n", mode_cmd->pixel_format); + DRM_DEBUG("unsupported pixel format: %s\n", + drm_get_format_name(mode_cmd->pixel_format)); return -EINVAL; } break; default: - DRM_DEBUG("unsupported pixel format 0x%08x\n", mode_cmd->pixel_format); + DRM_DEBUG("unsupported pixel format: %s\n", + drm_get_format_name(mode_cmd->pixel_format)); return -EINVAL; } -- cgit v1.2.3 From d20d3174806ef6589cb912a488657d21fcd7ece2 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Fri, 7 Jun 2013 15:43:07 +0000 Subject: drm: Constify the pretty-print functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The structures and strings involved with various pretty-print functions aren't meant to be modified, so make them all const. The exception is drm_connector_enum_list which does get modified in drm_connector_init(). While at it move the drm_get_connector_status_name() prototype from drmP.h to drm_crtc.h where it belongs. Signed-off-by: Ville Syrjälä Reviewed-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_crtc.c | 30 +++++++++++++++--------------- include/drm/drmP.h | 1 - include/drm/drm_crtc.h | 17 +++++++++-------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 079996ad02a7..44c3421899bb 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -92,7 +92,7 @@ EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked); /* Avoid boilerplate. I'm tired of typing. */ #define DRM_ENUM_NAME_FN(fnname, list) \ - char *fnname(int val) \ + const char *fnname(int val) \ { \ int i; \ for (i = 0; i < ARRAY_SIZE(list); i++) { \ @@ -105,7 +105,7 @@ EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked); /* * Global properties */ -static struct drm_prop_enum_list drm_dpms_enum_list[] = +static const struct drm_prop_enum_list drm_dpms_enum_list[] = { { DRM_MODE_DPMS_ON, "On" }, { DRM_MODE_DPMS_STANDBY, "Standby" }, { DRM_MODE_DPMS_SUSPEND, "Suspend" }, @@ -117,7 +117,7 @@ DRM_ENUM_NAME_FN(drm_get_dpms_name, drm_dpms_enum_list) /* * Optional properties */ -static struct drm_prop_enum_list drm_scaling_mode_enum_list[] = +static const struct drm_prop_enum_list drm_scaling_mode_enum_list[] = { { DRM_MODE_SCALE_NONE, "None" }, { DRM_MODE_SCALE_FULLSCREEN, "Full" }, @@ -125,7 +125,7 @@ static struct drm_prop_enum_list drm_scaling_mode_enum_list[] = { DRM_MODE_SCALE_ASPECT, "Full aspect" }, }; -static struct drm_prop_enum_list drm_dithering_mode_enum_list[] = +static const struct drm_prop_enum_list drm_dithering_mode_enum_list[] = { { DRM_MODE_DITHERING_OFF, "Off" }, { DRM_MODE_DITHERING_ON, "On" }, @@ -135,7 +135,7 @@ static struct drm_prop_enum_list drm_dithering_mode_enum_list[] = /* * Non-global properties, but "required" for certain connectors. */ -static struct drm_prop_enum_list drm_dvi_i_select_enum_list[] = +static const struct drm_prop_enum_list drm_dvi_i_select_enum_list[] = { { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */ { DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */ @@ -144,7 +144,7 @@ static struct drm_prop_enum_list drm_dvi_i_select_enum_list[] = DRM_ENUM_NAME_FN(drm_get_dvi_i_select_name, drm_dvi_i_select_enum_list) -static struct drm_prop_enum_list drm_dvi_i_subconnector_enum_list[] = +static const struct drm_prop_enum_list drm_dvi_i_subconnector_enum_list[] = { { DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I and TV-out */ { DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */ @@ -154,7 +154,7 @@ static struct drm_prop_enum_list drm_dvi_i_subconnector_enum_list[] = DRM_ENUM_NAME_FN(drm_get_dvi_i_subconnector_name, drm_dvi_i_subconnector_enum_list) -static struct drm_prop_enum_list drm_tv_select_enum_list[] = +static const struct drm_prop_enum_list drm_tv_select_enum_list[] = { { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */ { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */ @@ -165,7 +165,7 @@ static struct drm_prop_enum_list drm_tv_select_enum_list[] = DRM_ENUM_NAME_FN(drm_get_tv_select_name, drm_tv_select_enum_list) -static struct drm_prop_enum_list drm_tv_subconnector_enum_list[] = +static const struct drm_prop_enum_list drm_tv_subconnector_enum_list[] = { { DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I and TV-out */ { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */ @@ -177,7 +177,7 @@ static struct drm_prop_enum_list drm_tv_subconnector_enum_list[] = DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name, drm_tv_subconnector_enum_list) -static struct drm_prop_enum_list drm_dirty_info_enum_list[] = { +static const struct drm_prop_enum_list drm_dirty_info_enum_list[] = { { DRM_MODE_DIRTY_OFF, "Off" }, { DRM_MODE_DIRTY_ON, "On" }, { DRM_MODE_DIRTY_ANNOTATE, "Annotate" }, @@ -185,7 +185,7 @@ static struct drm_prop_enum_list drm_dirty_info_enum_list[] = { struct drm_conn_prop_enum_list { int type; - char *name; + const char *name; int count; }; @@ -211,7 +211,7 @@ static struct drm_conn_prop_enum_list drm_connector_enum_list[] = { DRM_MODE_CONNECTOR_VIRTUAL, "Virtual", 0}, }; -static struct drm_prop_enum_list drm_encoder_enum_list[] = +static const struct drm_prop_enum_list drm_encoder_enum_list[] = { { DRM_MODE_ENCODER_NONE, "None" }, { DRM_MODE_ENCODER_DAC, "DAC" }, { DRM_MODE_ENCODER_TMDS, "TMDS" }, @@ -220,7 +220,7 @@ static struct drm_prop_enum_list drm_encoder_enum_list[] = { DRM_MODE_ENCODER_VIRTUAL, "Virtual" }, }; -char *drm_get_encoder_name(struct drm_encoder *encoder) +const char *drm_get_encoder_name(const struct drm_encoder *encoder) { static char buf[32]; @@ -231,7 +231,7 @@ char *drm_get_encoder_name(struct drm_encoder *encoder) } EXPORT_SYMBOL(drm_get_encoder_name); -char *drm_get_connector_name(struct drm_connector *connector) +const char *drm_get_connector_name(const struct drm_connector *connector) { static char buf[32]; @@ -242,7 +242,7 @@ char *drm_get_connector_name(struct drm_connector *connector) } EXPORT_SYMBOL(drm_get_connector_name); -char *drm_get_connector_status_name(enum drm_connector_status status) +const char *drm_get_connector_status_name(enum drm_connector_status status) { if (status == connector_status_connected) return "connected"; @@ -258,7 +258,7 @@ static char printable_char(int c) return isascii(c) && isprint(c) ? c : '?'; } -char *drm_get_format_name(uint32_t format) +const char *drm_get_format_name(uint32_t format) { static char buf[32]; diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 57f60a743da0..e4f765c80fb0 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1599,7 +1599,6 @@ extern void drm_sysfs_destroy(void); extern int drm_sysfs_device_add(struct drm_minor *minor); extern void drm_sysfs_hotplug_event(struct drm_device *dev); extern void drm_sysfs_device_remove(struct drm_minor *minor); -extern char *drm_get_connector_status_name(enum drm_connector_status status); extern int drm_sysfs_connector_add(struct drm_connector *connector); extern void drm_sysfs_connector_remove(struct drm_connector *connector); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 2cbbfd44c6df..53c33e28a2f7 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -897,12 +897,13 @@ extern void drm_plane_cleanup(struct drm_plane *plane); extern void drm_encoder_cleanup(struct drm_encoder *encoder); -extern char *drm_get_connector_name(struct drm_connector *connector); -extern char *drm_get_dpms_name(int val); -extern char *drm_get_dvi_i_subconnector_name(int val); -extern char *drm_get_dvi_i_select_name(int val); -extern char *drm_get_tv_subconnector_name(int val); -extern char *drm_get_tv_select_name(int val); +extern const char *drm_get_connector_name(const struct drm_connector *connector); +extern const char *drm_get_connector_status_name(enum drm_connector_status status); +extern const char *drm_get_dpms_name(int val); +extern const char *drm_get_dvi_i_subconnector_name(int val); +extern const char *drm_get_dvi_i_select_name(int val); +extern const char *drm_get_tv_subconnector_name(int val); +extern const char *drm_get_tv_select_name(int val); extern void drm_fb_release(struct drm_file *file_priv); extern int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group); extern bool drm_probe_ddc(struct i2c_adapter *adapter); @@ -994,7 +995,7 @@ extern int drm_mode_create_tv_properties(struct drm_device *dev, int num_formats extern int drm_mode_create_scaling_mode_property(struct drm_device *dev); extern int drm_mode_create_dithering_property(struct drm_device *dev); extern int drm_mode_create_dirty_info_property(struct drm_device *dev); -extern char *drm_get_encoder_name(struct drm_encoder *encoder); +extern const char *drm_get_encoder_name(const struct drm_encoder *encoder); extern int drm_mode_connector_attach_encoder(struct drm_connector *connector, struct drm_encoder *encoder); @@ -1094,6 +1095,6 @@ extern int drm_format_num_planes(uint32_t format); extern int drm_format_plane_cpp(uint32_t format, int plane); extern int drm_format_horz_chroma_subsampling(uint32_t format); extern int drm_format_vert_chroma_subsampling(uint32_t format); -extern char *drm_get_format_name(uint32_t format); +extern const char *drm_get_format_name(uint32_t format); #endif /* __DRM_CRTC_H__ */ -- cgit v1.2.3 From 081c9025f49da427faf50b5c14143f98a21c5e85 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Sun, 2 Jun 2013 22:20:55 +0800 Subject: clk: divider: do not propagate rate change request when unnecessary If the current rate of parent clock is sufficient to provide child a requested rate with a proper divider setting, the rate change request should not be propagated. Instead, changing the divider setting is good enough to get child clock run at the requested rate. On an imx6q clock configuration illustrated below, ahb --> ipg --> ipg_per 132M 66M 66M calling clk_set_rate(ipg_per, 22M) with the current clk_divider_bestdiv() implementation will result in the rate change up to ahb level like the following, because of the unnecessary/incorrect rate change propagation. ahb --> ipg --> ipg_per 66M 22M 22M Fix the problem by trying to see if the requested rate can be achieved by simply changing the divider value, and in that case return the divider immediately from function clk_divider_bestdiv() as the best one, so that all those unnecessary rate change propagation can be saved. Reported-by: Anson Huang Signed-off-by: Shawn Guo Signed-off-by: Mike Turquette --- drivers/clk/clk-divider.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 6d9674160430..6024e60e49aa 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -150,6 +150,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, struct clk_divider *divider = to_clk_divider(hw); int i, bestdiv = 0; unsigned long parent_rate, best = 0, now, maxdiv; + unsigned long parent_rate_saved = *best_parent_rate; if (!rate) rate = 1; @@ -173,6 +174,15 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, for (i = 1; i <= maxdiv; i++) { if (!_is_valid_div(divider, i)) continue; + if (rate * i == parent_rate_saved) { + /* + * It's the most ideal case if the requested rate can be + * divided from parent clock without needing to change + * parent rate, so return the divider immediately. + */ + *best_parent_rate = parent_rate_saved; + return i; + } parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), MULT_ROUND_UP(rate, i)); now = parent_rate / i; -- cgit v1.2.3 From 8d06cd0af6c50a26d87ce78310ad38b9b40fa795 Mon Sep 17 00:00:00 2001 From: Thomas Meyer Date: Wed, 22 May 2013 21:06:30 +0000 Subject: drm: Cocci spatch "memdup.spatch" Signed-off-by: Thomas Meyer Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_edid_load.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/drm_edid_load.c b/drivers/gpu/drm/drm_edid_load.c index fa445dd4dc00..a4f5ce14dc1c 100644 --- a/drivers/gpu/drm/drm_edid_load.c +++ b/drivers/gpu/drm/drm_edid_load.c @@ -186,12 +186,11 @@ static u8 *edid_load(struct drm_connector *connector, char *name, goto relfw_out; } - edid = kmalloc(fwsize, GFP_KERNEL); + edid = kmemdup(fwdata, fwsize, GFP_KERNEL); if (edid == NULL) { err = -ENOMEM; goto relfw_out; } - memcpy(edid, fwdata, fwsize); if (!drm_edid_block_valid(edid, 0, print_bad_edid)) { connector->bad_edid_counter++; -- cgit v1.2.3 From f2a5da4ff2d4085ef5c777cad8a57e083d037809 Mon Sep 17 00:00:00 2001 From: Thomas Meyer Date: Sat, 1 Jun 2013 10:09:27 +0000 Subject: drm/prime: Cocci spatch "err_cast.spatch" Signed-off-by: Thomas Meyer Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_prime.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index 58ac6770a8c6..d92853e06dbb 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -297,7 +297,7 @@ struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev, attach = dma_buf_attach(dma_buf, dev->dev); if (IS_ERR(attach)) - return ERR_PTR(PTR_ERR(attach)); + return ERR_CAST(attach); get_dma_buf(dma_buf); -- cgit v1.2.3 From f6fb754dc6f1def4bdc28f9391d835860612b9b4 Mon Sep 17 00:00:00 2001 From: Thomas Meyer Date: Sat, 1 Jun 2013 09:56:46 +0000 Subject: drm/cma: Cocci spatch "ptr_ret.spatch" Signed-off-by: Thomas Meyer Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_gem_cma_helper.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c index f94fcd752815..9efabceed42e 100644 --- a/drivers/gpu/drm/drm_gem_cma_helper.c +++ b/drivers/gpu/drm/drm_gem_cma_helper.c @@ -215,10 +215,7 @@ int drm_gem_cma_dumb_create(struct drm_file *file_priv, cma_obj = drm_gem_cma_create_with_handle(file_priv, dev, args->size, &args->handle); - if (IS_ERR(cma_obj)) - return PTR_ERR(cma_obj); - - return 0; + return PTR_RET(cma_obj); } EXPORT_SYMBOL_GPL(drm_gem_cma_dumb_create); -- cgit v1.2.3 From 72e45e9267bfbf299f2f4c14c8c923a4f6444030 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Fri, 31 May 2013 12:17:06 +0000 Subject: drm: Preserve the list head in drm_mode_copy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Preserve the destination mode's list head in drm_mode_copy. Just in case someone decides that it's a good idea to overwrite a mode which happens to be on some list, Signed-off-by: Ville Syrjälä Reviewed-by: Alex Deucher Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_modes.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 09785422e654..69153385c63f 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -789,16 +789,17 @@ EXPORT_SYMBOL(drm_mode_set_crtcinfo); * LOCKING: * None. * - * Copy an existing mode into another mode, preserving the object id - * of the destination mode. + * Copy an existing mode into another mode, preserving the object id and + * list head of the destination mode. */ void drm_mode_copy(struct drm_display_mode *dst, const struct drm_display_mode *src) { int id = dst->base.id; + struct list_head head = dst->head; *dst = *src; dst->base.id = id; - INIT_LIST_HEAD(&dst->head); + dst->head = head; } EXPORT_SYMBOL(drm_mode_copy); -- cgit v1.2.3 From 990256aec2f10800595dddf4d1c3441fcd6b2616 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Fri, 31 May 2013 12:17:07 +0000 Subject: drm: Add probed modes in probe order MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keeping the modes in the same order as we probe them makes it a bit easier to track what's happening. Signed-off-by: Ville Syrjälä Reviewed-by: Alex Deucher Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_crtc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 44c3421899bb..3385b5cb084f 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -680,7 +680,7 @@ EXPORT_SYMBOL(drm_crtc_cleanup); void drm_mode_probed_add(struct drm_connector *connector, struct drm_display_mode *mode) { - list_add(&mode->head, &connector->probed_modes); + list_add_tail(&mode->head, &connector->probed_modes); } EXPORT_SYMBOL(drm_mode_probed_add); -- cgit v1.2.3 From 9bc3cd5673d84d29272fa7181a4dfca83cbb48c1 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Fri, 31 May 2013 12:17:08 +0000 Subject: drm: Sort connector modes based on vrefresh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keeping the modes sorted by vrefresh before the pixel clock makes the mode list somehow more pleasing to the eye. Signed-off-by: Ville Syrjälä Reviewed-by: Alex Deucher Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_crtc_helper.c | 5 +++-- drivers/gpu/drm/drm_modes.c | 5 +++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index ed1334e27c33..f554516ec2a4 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -189,13 +189,14 @@ prune: if (list_empty(&connector->modes)) return 0; + list_for_each_entry(mode, &connector->modes, head) + mode->vrefresh = drm_mode_vrefresh(mode); + drm_mode_sort(&connector->modes); DRM_DEBUG_KMS("[CONNECTOR:%d:%s] probed modes :\n", connector->base.id, drm_get_connector_name(connector)); list_for_each_entry(mode, &connector->modes, head) { - mode->vrefresh = drm_mode_vrefresh(mode); - drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); drm_mode_debug_printmodeline(mode); } diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 69153385c63f..a6729bfe6860 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -1020,6 +1020,11 @@ static int drm_mode_compare(void *priv, struct list_head *lh_a, struct list_head diff = b->hdisplay * b->vdisplay - a->hdisplay * a->vdisplay; if (diff) return diff; + + diff = b->vrefresh - a->vrefresh; + if (diff) + return diff; + diff = b->clock - a->clock; return diff; } -- cgit v1.2.3 From ad6f5c343364e52a49b6a0a9153bda7f0e101416 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Wed, 5 Jun 2013 12:39:55 +0000 Subject: drm: Improve drm_crtc documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Ville Syrjälä Reviewed-by: Alex Deucher Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_crtc.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 3385b5cb084f..baee5752ca6b 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -616,7 +616,7 @@ EXPORT_SYMBOL(drm_framebuffer_remove); * @crtc: CRTC object to init * @funcs: callbacks for the new CRTC * - * Inits a new object created as base part of an driver crtc object. + * Inits a new object created as base part of a driver crtc object. * * RETURNS: * Zero on success, error code on failure. @@ -651,11 +651,12 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, EXPORT_SYMBOL(drm_crtc_init); /** - * drm_crtc_cleanup - Cleans up the core crtc usage. + * drm_crtc_cleanup - Clean up the core crtc usage * @crtc: CRTC to cleanup * - * Cleanup @crtc. Removes from drm modesetting space - * does NOT free object, caller does that. + * This function cleans up @crtc and removes it from the DRM mode setting + * core. Note that the function does *not* free the crtc structure itself, + * this is the responsibility of the caller. */ void drm_crtc_cleanup(struct drm_crtc *crtc) { -- cgit v1.2.3 From a5d0f5766f2e9cb04f0f775bf679d41ae1a54d50 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Mon, 3 Jun 2013 16:10:41 +0300 Subject: drm/vmwgfx: Don't access file_priv in cursor_set when handle==0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We want to disable the cursor by calling ->cursor_set() with handle=0 from places where we don't have a file_priv, so don't try to access it unless necessary. Signed-off-by: Ville Syrjälä Reviewed-by: Jakob Bornecrantz Signed-off-by: Dave Airlie --- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 3e3c7ab33ca2..d4607b2530d6 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -174,7 +174,6 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, uint32_t handle, uint32_t width, uint32_t height) { struct vmw_private *dev_priv = vmw_priv(crtc->dev); - struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; struct vmw_display_unit *du = vmw_crtc_to_du(crtc); struct vmw_surface *surface = NULL; struct vmw_dma_buffer *dmabuf = NULL; @@ -197,6 +196,8 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, } if (handle) { + struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; + ret = vmw_user_lookup_handle(dev_priv, tfile, handle, &surface, &dmabuf); if (ret) { -- cgit v1.2.3 From e6e792092e816bea0797995c886fb057c91d4546 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Fri, 31 May 2013 15:23:41 +0300 Subject: drm/edid: Add both 60Hz and 59.94Hz CEA modes to connector's mode list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Having both modes can be beneficial for video playback cases. If you can match the video framerate exactly, and the audio and video clocks come from the same source, you should be able to avoid dropped/repeated frames without expensive operations such as resampling the audio to match video output rate. Rather than add both variants based on the CEA extension short video descriptors in do_cea_modes(), add only one variant there. Once all the EDID has been fully probed, do a loop over the entire probed mode list, during which we add the other variants for all modes that match CEA modes. This allows us to match modes that didn't come via the CEA short video descriptors. For example one Samsung TV here doesn't have the 640x480-60 mode as a SVD, but instead it's specified via a detailed timing descriptor. Signed-off-by: Ville Syrjälä Reviewed-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_edid.c | 102 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 88 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 9e62bbedb5ad..e8d17eebde71 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -2321,6 +2321,31 @@ u8 *drm_find_cea_extension(struct edid *edid) } EXPORT_SYMBOL(drm_find_cea_extension); +/* + * Calculate the alternate clock for the CEA mode + * (60Hz vs. 59.94Hz etc.) + */ +static unsigned int +cea_mode_alternate_clock(const struct drm_display_mode *cea_mode) +{ + unsigned int clock = cea_mode->clock; + + if (cea_mode->vrefresh % 6 != 0) + return clock; + + /* + * edid_cea_modes contains the 59.94Hz + * variant for 240 and 480 line modes, + * and the 60Hz variant otherwise. + */ + if (cea_mode->vdisplay == 240 || cea_mode->vdisplay == 480) + clock = clock * 1001 / 1000; + else + clock = DIV_ROUND_UP(clock * 1000, 1001); + + return clock; +} + /** * drm_match_cea_mode - look for a CEA mode matching given mode * @to_match: display mode @@ -2339,21 +2364,9 @@ u8 drm_match_cea_mode(const struct drm_display_mode *to_match) const struct drm_display_mode *cea_mode = &edid_cea_modes[mode]; unsigned int clock1, clock2; - clock1 = clock2 = cea_mode->clock; - /* Check both 60Hz and 59.94Hz */ - if (cea_mode->vrefresh % 6 == 0) { - /* - * edid_cea_modes contains the 59.94Hz - * variant for 240 and 480 line modes, - * and the 60Hz variant otherwise. - */ - if (cea_mode->vdisplay == 240 || - cea_mode->vdisplay == 480) - clock1 = clock1 * 1001 / 1000; - else - clock2 = DIV_ROUND_UP(clock2 * 1000, 1001); - } + clock1 = cea_mode->clock; + clock2 = cea_mode_alternate_clock(cea_mode); if ((KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock1) || KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock2)) && @@ -2364,6 +2377,66 @@ u8 drm_match_cea_mode(const struct drm_display_mode *to_match) } EXPORT_SYMBOL(drm_match_cea_mode); +static int +add_alternate_cea_modes(struct drm_connector *connector, struct edid *edid) +{ + struct drm_device *dev = connector->dev; + struct drm_display_mode *mode, *tmp; + LIST_HEAD(list); + int modes = 0; + + /* Don't add CEA modes if the CEA extension block is missing */ + if (!drm_find_cea_extension(edid)) + return 0; + + /* + * Go through all probed modes and create a new mode + * with the alternate clock for certain CEA modes. + */ + list_for_each_entry(mode, &connector->probed_modes, head) { + const struct drm_display_mode *cea_mode; + struct drm_display_mode *newmode; + u8 cea_mode_idx = drm_match_cea_mode(mode) - 1; + unsigned int clock1, clock2; + + if (cea_mode_idx >= ARRAY_SIZE(edid_cea_modes)) + continue; + + cea_mode = &edid_cea_modes[cea_mode_idx]; + + clock1 = cea_mode->clock; + clock2 = cea_mode_alternate_clock(cea_mode); + + if (clock1 == clock2) + continue; + + if (mode->clock != clock1 && mode->clock != clock2) + continue; + + newmode = drm_mode_duplicate(dev, cea_mode); + if (!newmode) + continue; + + /* + * The current mode could be either variant. Make + * sure to pick the "other" clock for the new mode. + */ + if (mode->clock != clock1) + newmode->clock = clock1; + else + newmode->clock = clock2; + + list_add_tail(&newmode->head, &list); + } + + list_for_each_entry_safe(mode, tmp, &list, head) { + list_del(&mode->head); + drm_mode_probed_add(connector, mode); + modes++; + } + + return modes; +} static int do_cea_modes (struct drm_connector *connector, u8 *db, u8 len) @@ -2946,6 +3019,7 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) if (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF) num_modes += add_inferred_modes(connector, edid); num_modes += add_cea_modes(connector, edid); + num_modes += add_alternate_cea_modes(connector, edid); if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75)) edid_fixup_preferred(connector, quirks); -- cgit v1.2.3 From af12fa6e46aa651e7b86a4c4117b562518fef184 Mon Sep 17 00:00:00 2001 From: Eliezer Tamir Date: Mon, 10 Jun 2013 11:39:41 +0300 Subject: net: add napi_id and hash Adds a napi_id and a hashing mechanism to lookup a napi by id. This will be used by subsequent patches to implement low latency Ethernet device polling. Based on a code sample by Eric Dumazet. Signed-off-by: Eliezer Tamir Signed-off-by: Eric Dumazet Tested-by: Willem de Bruijn Signed-off-by: David S. Miller --- include/linux/netdevice.h | 29 +++++++++++++++++++++++ net/core/dev.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 8f967e34142b..39bbd462d68e 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -324,12 +324,15 @@ struct napi_struct { struct sk_buff *gro_list; struct sk_buff *skb; struct list_head dev_list; + struct hlist_node napi_hash_node; + unsigned int napi_id; }; enum { NAPI_STATE_SCHED, /* Poll is scheduled */ NAPI_STATE_DISABLE, /* Disable pending */ NAPI_STATE_NPSVC, /* Netpoll - don't dequeue from poll_list */ + NAPI_STATE_HASHED, /* In NAPI hash */ }; enum gro_result { @@ -445,6 +448,32 @@ static inline bool napi_reschedule(struct napi_struct *napi) extern void __napi_complete(struct napi_struct *n); extern void napi_complete(struct napi_struct *n); +/** + * napi_by_id - lookup a NAPI by napi_id + * @napi_id: hashed napi_id + * + * lookup @napi_id in napi_hash table + * must be called under rcu_read_lock() + */ +extern struct napi_struct *napi_by_id(unsigned int napi_id); + +/** + * napi_hash_add - add a NAPI to global hashtable + * @napi: napi context + * + * generate a new napi_id and store a @napi under it in napi_hash + */ +extern void napi_hash_add(struct napi_struct *napi); + +/** + * napi_hash_del - remove a NAPI from global table + * @napi: napi context + * + * Warning: caller must observe rcu grace period + * before freeing memory containing @napi + */ +extern void napi_hash_del(struct napi_struct *napi); + /** * napi_disable - prevent NAPI from scheduling * @n: napi context diff --git a/net/core/dev.c b/net/core/dev.c index 9c18557f93c6..fa007dba6beb 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -129,6 +129,7 @@ #include #include #include +#include #include "net-sysfs.h" @@ -166,6 +167,12 @@ static struct list_head offload_base __read_mostly; DEFINE_RWLOCK(dev_base_lock); EXPORT_SYMBOL(dev_base_lock); +/* protects napi_hash addition/deletion and napi_gen_id */ +static DEFINE_SPINLOCK(napi_hash_lock); + +static unsigned int napi_gen_id; +static DEFINE_HASHTABLE(napi_hash, 8); + seqcount_t devnet_rename_seq; static inline void dev_base_seq_inc(struct net *net) @@ -4136,6 +4143,58 @@ void napi_complete(struct napi_struct *n) } EXPORT_SYMBOL(napi_complete); +/* must be called under rcu_read_lock(), as we dont take a reference */ +struct napi_struct *napi_by_id(unsigned int napi_id) +{ + unsigned int hash = napi_id % HASH_SIZE(napi_hash); + struct napi_struct *napi; + + hlist_for_each_entry_rcu(napi, &napi_hash[hash], napi_hash_node) + if (napi->napi_id == napi_id) + return napi; + + return NULL; +} +EXPORT_SYMBOL_GPL(napi_by_id); + +void napi_hash_add(struct napi_struct *napi) +{ + if (!test_and_set_bit(NAPI_STATE_HASHED, &napi->state)) { + + spin_lock(&napi_hash_lock); + + /* 0 is not a valid id, we also skip an id that is taken + * we expect both events to be extremely rare + */ + napi->napi_id = 0; + while (!napi->napi_id) { + napi->napi_id = ++napi_gen_id; + if (napi_by_id(napi->napi_id)) + napi->napi_id = 0; + } + + hlist_add_head_rcu(&napi->napi_hash_node, + &napi_hash[napi->napi_id % HASH_SIZE(napi_hash)]); + + spin_unlock(&napi_hash_lock); + } +} +EXPORT_SYMBOL_GPL(napi_hash_add); + +/* Warning : caller is responsible to make sure rcu grace period + * is respected before freeing memory containing @napi + */ +void napi_hash_del(struct napi_struct *napi) +{ + spin_lock(&napi_hash_lock); + + if (test_and_clear_bit(NAPI_STATE_HASHED, &napi->state)) + hlist_del_rcu(&napi->napi_hash_node); + + spin_unlock(&napi_hash_lock); +} +EXPORT_SYMBOL_GPL(napi_hash_del); + void netif_napi_add(struct net_device *dev, struct napi_struct *napi, int (*poll)(struct napi_struct *, int), int weight) { -- cgit v1.2.3 From 060212928670593fb89243640bf05cf89560b023 Mon Sep 17 00:00:00 2001 From: Eliezer Tamir Date: Mon, 10 Jun 2013 11:39:50 +0300 Subject: net: add low latency socket poll Adds an ndo_ll_poll method and the code that supports it. This method can be used by low latency applications to busy-poll Ethernet device queues directly from the socket code. sysctl_net_ll_poll controls how many microseconds to poll. Default is zero (disabled). Individual protocol support will be added by subsequent patches. Signed-off-by: Alexander Duyck Signed-off-by: Jesse Brandeburg Signed-off-by: Eliezer Tamir Acked-by: Eric Dumazet Tested-by: Willem de Bruijn Signed-off-by: David S. Miller --- Documentation/sysctl/net.txt | 7 ++ include/linux/netdevice.h | 3 + include/linux/skbuff.h | 8 ++- include/net/ll_poll.h | 148 +++++++++++++++++++++++++++++++++++++++++++ include/net/sock.h | 4 ++ include/uapi/linux/snmp.h | 1 + net/Kconfig | 12 ++++ net/core/skbuff.c | 4 ++ net/core/sock.c | 6 ++ net/core/sysctl_net_core.c | 10 +++ net/ipv4/proc.c | 1 + net/socket.c | 6 ++ 12 files changed, 208 insertions(+), 2 deletions(-) create mode 100644 include/net/ll_poll.h diff --git a/Documentation/sysctl/net.txt b/Documentation/sysctl/net.txt index c1f8640c2fc8..85ab72dcdc3c 100644 --- a/Documentation/sysctl/net.txt +++ b/Documentation/sysctl/net.txt @@ -50,6 +50,13 @@ The maximum number of packets that kernel can handle on a NAPI interrupt, it's a Per-CPU variable. Default: 64 +low_latency_poll +---------------- +Low latency busy poll timeout. (needs CONFIG_NET_LL_RX_POLL) +Approximate time in us to spin waiting for packets on the device queue. +Recommended value is 50. May increase power usage. +Default: 0 (off) + rmem_default ------------ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 39bbd462d68e..2ecb96d9a1e5 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -971,6 +971,9 @@ struct net_device_ops { struct netpoll_info *info, gfp_t gfp); void (*ndo_netpoll_cleanup)(struct net_device *dev); +#endif +#ifdef CONFIG_NET_LL_RX_POLL + int (*ndo_ll_poll)(struct napi_struct *dev); #endif int (*ndo_set_vf_mac)(struct net_device *dev, int queue, u8 *mac); diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 9995834d2cb6..400d82ae2b03 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -386,6 +386,7 @@ typedef unsigned char *sk_buff_data_t; * @no_fcs: Request NIC to treat last 4 bytes as Ethernet FCS * @dma_cookie: a cookie to one of several possible DMA operations * done by skb DMA functions + * @napi_id: id of the NAPI struct this skb came from * @secmark: security marking * @mark: Generic packet mark * @dropcount: total number of sk_receive_queue overflows @@ -500,8 +501,11 @@ struct sk_buff { /* 7/9 bit hole (depending on ndisc_nodetype presence) */ kmemcheck_bitfield_end(flags2); -#ifdef CONFIG_NET_DMA - dma_cookie_t dma_cookie; +#if defined CONFIG_NET_DMA || defined CONFIG_NET_LL_RX_POLL + union { + unsigned int napi_id; + dma_cookie_t dma_cookie; + }; #endif #ifdef CONFIG_NETWORK_SECMARK __u32 secmark; diff --git a/include/net/ll_poll.h b/include/net/ll_poll.h new file mode 100644 index 000000000000..bc262f88173f --- /dev/null +++ b/include/net/ll_poll.h @@ -0,0 +1,148 @@ +/* + * Low Latency Sockets + * Copyright(c) 2013 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Author: Eliezer Tamir + * + * Contact Information: + * e1000-devel Mailing List + */ + +/* + * For now this depends on CONFIG_X86_TSC + */ + +#ifndef _LINUX_NET_LL_POLL_H +#define _LINUX_NET_LL_POLL_H + +#include +#include + +#ifdef CONFIG_NET_LL_RX_POLL + +struct napi_struct; +extern unsigned long sysctl_net_ll_poll __read_mostly; + +/* return values from ndo_ll_poll */ +#define LL_FLUSH_FAILED -1 +#define LL_FLUSH_BUSY -2 + +/* we don't mind a ~2.5% imprecision */ +#define TSC_MHZ (tsc_khz >> 10) + +static inline cycles_t ll_end_time(void) +{ + return TSC_MHZ * ACCESS_ONCE(sysctl_net_ll_poll) + get_cycles(); +} + +static inline bool sk_valid_ll(struct sock *sk) +{ + return sysctl_net_ll_poll && sk->sk_napi_id && + !need_resched() && !signal_pending(current); +} + +static inline bool can_poll_ll(cycles_t end_time) +{ + return !time_after((unsigned long)get_cycles(), + (unsigned long)end_time); +} + +static inline bool sk_poll_ll(struct sock *sk, int nonblock) +{ + cycles_t end_time = ll_end_time(); + const struct net_device_ops *ops; + struct napi_struct *napi; + int rc = false; + + /* + * rcu read lock for napi hash + * bh so we don't race with net_rx_action + */ + rcu_read_lock_bh(); + + napi = napi_by_id(sk->sk_napi_id); + if (!napi) + goto out; + + ops = napi->dev->netdev_ops; + if (!ops->ndo_ll_poll) + goto out; + + do { + + rc = ops->ndo_ll_poll(napi); + + if (rc == LL_FLUSH_FAILED) + break; /* permanent failure */ + + if (rc > 0) + /* local bh are disabled so it is ok to use _BH */ + NET_ADD_STATS_BH(sock_net(sk), + LINUX_MIB_LOWLATENCYRXPACKETS, rc); + + } while (skb_queue_empty(&sk->sk_receive_queue) + && can_poll_ll(end_time) && !nonblock); + + rc = !skb_queue_empty(&sk->sk_receive_queue); +out: + rcu_read_unlock_bh(); + return rc; +} + +/* used in the NIC receive handler to mark the skb */ +static inline void skb_mark_ll(struct sk_buff *skb, struct napi_struct *napi) +{ + skb->napi_id = napi->napi_id; +} + +/* used in the protocol hanlder to propagate the napi_id to the socket */ +static inline void sk_mark_ll(struct sock *sk, struct sk_buff *skb) +{ + sk->sk_napi_id = skb->napi_id; +} + +#else /* CONFIG_NET_LL_RX_POLL */ + +static inline cycles_t ll_end_time(void) +{ + return 0; +} + +static inline bool sk_valid_ll(struct sock *sk) +{ + return false; +} + +static inline bool sk_poll_ll(struct sock *sk, int nonblock) +{ + return false; +} + +static inline void skb_mark_ll(struct sk_buff *skb, struct napi_struct *napi) +{ +} + +static inline void sk_mark_ll(struct sock *sk, struct sk_buff *skb) +{ +} + +static inline bool can_poll_ll(cycles_t end_time) +{ + return false; +} + +#endif /* CONFIG_NET_LL_RX_POLL */ +#endif /* _LINUX_NET_LL_POLL_H */ diff --git a/include/net/sock.h b/include/net/sock.h index 66772cf8c3c5..ac8e1818380c 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -229,6 +229,7 @@ struct cg_proto; * @sk_omem_alloc: "o" is "option" or "other" * @sk_wmem_queued: persistent queue size * @sk_forward_alloc: space allocated forward + * @sk_napi_id: id of the last napi context to receive data for sk * @sk_allocation: allocation mode * @sk_sndbuf: size of send buffer in bytes * @sk_flags: %SO_LINGER (l_onoff), %SO_BROADCAST, %SO_KEEPALIVE, @@ -324,6 +325,9 @@ struct sock { int sk_forward_alloc; #ifdef CONFIG_RPS __u32 sk_rxhash; +#endif +#ifdef CONFIG_NET_LL_RX_POLL + unsigned int sk_napi_id; #endif atomic_t sk_drops; int sk_rcvbuf; diff --git a/include/uapi/linux/snmp.h b/include/uapi/linux/snmp.h index df2e8b4f9c03..26cbf76f8058 100644 --- a/include/uapi/linux/snmp.h +++ b/include/uapi/linux/snmp.h @@ -253,6 +253,7 @@ enum LINUX_MIB_TCPFASTOPENLISTENOVERFLOW, /* TCPFastOpenListenOverflow */ LINUX_MIB_TCPFASTOPENCOOKIEREQD, /* TCPFastOpenCookieReqd */ LINUX_MIB_TCPSPURIOUS_RTX_HOSTQUEUES, /* TCPSpuriousRtxHostQueues */ + LINUX_MIB_LOWLATENCYRXPACKETS, /* LowLatencyRxPackets */ __LINUX_MIB_MAX }; diff --git a/net/Kconfig b/net/Kconfig index 523e43e6da1b..d6a9ce6e1800 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -243,6 +243,18 @@ config NETPRIO_CGROUP Cgroup subsystem for use in assigning processes to network priorities on a per-interface basis +config NET_LL_RX_POLL + bool "Low Latency Receive Poll" + depends on X86_TSC + default n + ---help--- + Support Low Latency Receive Queue Poll. + (For network card drivers which support this option.) + When waiting for data in read or poll call directly into the the device driver + to flush packets which may be pending on the device queues into the stack. + + If unsure, say N. + config BQL boolean depends on SYSFS diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 73f57a0e1523..4a4181e16c1a 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -733,6 +733,10 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old) new->vlan_tci = old->vlan_tci; skb_copy_secmark(new, old); + +#ifdef CONFIG_NET_LL_RX_POLL + new->napi_id = old->napi_id; +#endif } /* diff --git a/net/core/sock.c b/net/core/sock.c index 88868a9d21da..788c0da5eed1 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -139,6 +139,8 @@ #include #endif +#include + static DEFINE_MUTEX(proto_list_mutex); static LIST_HEAD(proto_list); @@ -2284,6 +2286,10 @@ void sock_init_data(struct socket *sock, struct sock *sk) sk->sk_stamp = ktime_set(-1L, 0); +#ifdef CONFIG_NET_LL_RX_POLL + sk->sk_napi_id = 0; +#endif + /* * Before updating sk_refcnt, we must commit prior changes to memory * (Documentation/RCU/rculist_nulls.txt for details) diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c index 741db5fc7806..4b48f39582b0 100644 --- a/net/core/sysctl_net_core.c +++ b/net/core/sysctl_net_core.c @@ -19,6 +19,7 @@ #include #include #include +#include static int one = 1; @@ -284,6 +285,15 @@ static struct ctl_table net_core_table[] = { .proc_handler = flow_limit_table_len_sysctl }, #endif /* CONFIG_NET_FLOW_LIMIT */ +#ifdef CONFIG_NET_LL_RX_POLL + { + .procname = "low_latency_poll", + .data = &sysctl_net_ll_poll, + .maxlen = sizeof(unsigned long), + .mode = 0644, + .proc_handler = proc_doulongvec_minmax + }, +#endif #endif /* CONFIG_NET */ { .procname = "netdev_budget", diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index 2a5bf86d2415..6577a1149a47 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -273,6 +273,7 @@ static const struct snmp_mib snmp4_net_list[] = { SNMP_MIB_ITEM("TCPFastOpenListenOverflow", LINUX_MIB_TCPFASTOPENLISTENOVERFLOW), SNMP_MIB_ITEM("TCPFastOpenCookieReqd", LINUX_MIB_TCPFASTOPENCOOKIEREQD), SNMP_MIB_ITEM("TCPSpuriousRtxHostQueues", LINUX_MIB_TCPSPURIOUS_RTX_HOSTQUEUES), + SNMP_MIB_ITEM("LowLatencyRxPackets", LINUX_MIB_LOWLATENCYRXPACKETS), SNMP_MIB_SENTINEL }; diff --git a/net/socket.c b/net/socket.c index 3ebdcb805c51..21fd29f63ed2 100644 --- a/net/socket.c +++ b/net/socket.c @@ -104,6 +104,12 @@ #include #include #include +#include + +#ifdef CONFIG_NET_LL_RX_POLL +unsigned long sysctl_net_ll_poll __read_mostly; +EXPORT_SYMBOL_GPL(sysctl_net_ll_poll); +#endif static int sock_no_open(struct inode *irrelevant, struct file *dontcare); static ssize_t sock_aio_read(struct kiocb *iocb, const struct iovec *iov, -- cgit v1.2.3 From a5b50476f77a8fcc8055c955720d05a7c2d9c532 Mon Sep 17 00:00:00 2001 From: Eliezer Tamir Date: Mon, 10 Jun 2013 11:40:00 +0300 Subject: udp: add low latency socket poll support Add upport for busy-polling on UDP sockets. In __udp[46]_lib_rcv add a call to sk_mark_ll() to copy the napi_id from the skb into the sk. This is done at the earliest possible moment, right after we identify which socket this skb is for. In __skb_recv_datagram When there is no data and the user tries to read we busy poll. Signed-off-by: Alexander Duyck Signed-off-by: Jesse Brandeburg Signed-off-by: Eliezer Tamir Acked-by: Eric Dumazet Tested-by: Willem de Bruijn Signed-off-by: David S. Miller --- net/core/datagram.c | 4 ++++ net/ipv4/udp.c | 6 +++++- net/ipv6/udp.c | 6 +++++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/net/core/datagram.c b/net/core/datagram.c index b71423db7785..9cbaba98ce4c 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c @@ -56,6 +56,7 @@ #include #include #include +#include /* * Is a socket 'connection oriented' ? @@ -207,6 +208,9 @@ struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned int flags, } spin_unlock_irqrestore(&queue->lock, cpu_flags); + if (sk_valid_ll(sk) && sk_poll_ll(sk, flags & MSG_DONTWAIT)) + continue; + /* User doesn't want to wait */ error = -EAGAIN; if (!timeo) diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index c7338ec79cc0..2955b25aee6d 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -109,6 +109,7 @@ #include #include #include +#include #include "udp_impl.h" struct udp_table udp_table __read_mostly; @@ -1709,7 +1710,10 @@ int __udp4_lib_rcv(struct sk_buff *skb, struct udp_table *udptable, sk = __udp4_lib_lookup_skb(skb, uh->source, uh->dest, udptable); if (sk != NULL) { - int ret = udp_queue_rcv_skb(sk, skb); + int ret; + + sk_mark_ll(sk, skb); + ret = udp_queue_rcv_skb(sk, skb); sock_put(sk); /* a return value > 0 means to resubmit the input, but diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index b5808539cd5c..f77e34c5a0e2 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -46,6 +46,7 @@ #include #include #include +#include #include #include @@ -841,7 +842,10 @@ int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable, */ sk = __udp6_lib_lookup_skb(skb, uh->source, uh->dest, udptable); if (sk != NULL) { - int ret = udpv6_queue_rcv_skb(sk, skb); + int ret; + + sk_mark_ll(sk, skb); + ret = udpv6_queue_rcv_skb(sk, skb); sock_put(sk); /* a return value > 0 means to resubmit the input, but -- cgit v1.2.3 From d30e383bb856f614ddb5bbbb5a7d3f86240e41ec Mon Sep 17 00:00:00 2001 From: Eliezer Tamir Date: Mon, 10 Jun 2013 11:40:10 +0300 Subject: tcp: add low latency socket poll support. Adds low latency socket poll support for TCP. In tcp_v[46]_rcv() add a call to sk_mark_ll() to copy the napi_id from the skb to the sk. In tcp_recvmsg(), when there is no data in the socket we busy-poll. This is a good example of how to add busy-poll support to more protocols. Signed-off-by: Alexander Duyck Signed-off-by: Jesse Brandeburg Signed-off-by: Eliezer Tamir Acked-by: Eric Dumazet Tested-by: Willem de Bruijn Signed-off-by: David S. Miller --- net/ipv4/tcp.c | 5 +++++ net/ipv4/tcp_ipv4.c | 2 ++ net/ipv6/tcp_ipv6.c | 2 ++ 3 files changed, 9 insertions(+) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index bc4246940f6c..46ed9afd1f5e 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -279,6 +279,7 @@ #include #include +#include int sysctl_tcp_fin_timeout __read_mostly = TCP_FIN_TIMEOUT; @@ -1553,6 +1554,10 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, struct sk_buff *skb; u32 urg_hole = 0; + if (sk_valid_ll(sk) && skb_queue_empty(&sk->sk_receive_queue) + && (sk->sk_state == TCP_ESTABLISHED)) + sk_poll_ll(sk, nonblock); + lock_sock(sk); err = -ENOTCONN; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 289039b4d8de..1063bb83e342 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -75,6 +75,7 @@ #include #include #include +#include #include #include @@ -1993,6 +1994,7 @@ process: if (sk_filter(sk, skb)) goto discard_and_relse; + sk_mark_ll(sk, skb); skb->dev = NULL; bh_lock_sock_nested(sk); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 0a17ed9eaf39..5cffa5c3e6b8 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -63,6 +63,7 @@ #include #include #include +#include #include @@ -1498,6 +1499,7 @@ process: if (sk_filter(sk, skb)) goto discard_and_relse; + sk_mark_ll(sk, skb); skb->dev = NULL; bh_lock_sock_nested(sk); -- cgit v1.2.3 From 5a85e737f30ce7b939a34d93cca816400342208c Mon Sep 17 00:00:00 2001 From: Eliezer Tamir Date: Mon, 10 Jun 2013 11:40:20 +0300 Subject: ixgbe: add support for ndo_ll_poll Add the ixgbe driver code implementing ndo_ll_poll. Adds ndo_ll_poll method and locking between it and the napi poll. When receiving a packet we use skb_mark_ll to record the napi it came from. Add each napi to the napi_hash right after netif_napi_add(). Signed-off-by: Alexander Duyck Signed-off-by: Jesse Brandeburg Signed-off-by: Eliezer Tamir Reviewed-by: Eric Dumazet Signed-off-by: David S. Miller --- drivers/net/ethernet/intel/ixgbe/ixgbe.h | 120 ++++++++++++++++++++++++++ drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c | 2 + drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 63 ++++++++++++-- 3 files changed, 177 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h index ca932387a80f..e9d98629b583 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h @@ -52,6 +52,8 @@ #include #endif +#include + /* common prefix used by pr_<> macros */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -356,9 +358,127 @@ struct ixgbe_q_vector { struct rcu_head rcu; /* to avoid race with update stats on free */ char name[IFNAMSIZ + 9]; +#ifdef CONFIG_NET_LL_RX_POLL + unsigned int state; +#define IXGBE_QV_STATE_IDLE 0 +#define IXGBE_QV_STATE_NAPI 1 /* NAPI owns this QV */ +#define IXGBE_QV_STATE_POLL 2 /* poll owns this QV */ +#define IXGBE_QV_LOCKED (IXGBE_QV_STATE_NAPI | IXGBE_QV_STATE_POLL) +#define IXGBE_QV_STATE_NAPI_YIELD 4 /* NAPI yielded this QV */ +#define IXGBE_QV_STATE_POLL_YIELD 8 /* poll yielded this QV */ +#define IXGBE_QV_YIELD (IXGBE_QV_STATE_NAPI_YIELD | IXGBE_QV_STATE_POLL_YIELD) +#define IXGBE_QV_USER_PEND (IXGBE_QV_STATE_POLL | IXGBE_QV_STATE_POLL_YIELD) + spinlock_t lock; +#endif /* CONFIG_NET_LL_RX_POLL */ + /* for dynamic allocation of rings associated with this q_vector */ struct ixgbe_ring ring[0] ____cacheline_internodealigned_in_smp; }; +#ifdef CONFIG_NET_LL_RX_POLL +static inline void ixgbe_qv_init_lock(struct ixgbe_q_vector *q_vector) +{ + + spin_lock_init(&q_vector->lock); + q_vector->state = IXGBE_QV_STATE_IDLE; +} + +/* called from the device poll routine to get ownership of a q_vector */ +static inline bool ixgbe_qv_lock_napi(struct ixgbe_q_vector *q_vector) +{ + int rc = true; + spin_lock(&q_vector->lock); + if (q_vector->state & IXGBE_QV_LOCKED) { + WARN_ON(q_vector->state & IXGBE_QV_STATE_NAPI); + q_vector->state |= IXGBE_QV_STATE_NAPI_YIELD; + rc = false; + } else + /* we don't care if someone yielded */ + q_vector->state = IXGBE_QV_STATE_NAPI; + spin_unlock(&q_vector->lock); + return rc; +} + +/* returns true is someone tried to get the qv while napi had it */ +static inline bool ixgbe_qv_unlock_napi(struct ixgbe_q_vector *q_vector) +{ + int rc = false; + spin_lock(&q_vector->lock); + WARN_ON(q_vector->state & (IXGBE_QV_STATE_POLL | + IXGBE_QV_STATE_NAPI_YIELD)); + + if (q_vector->state & IXGBE_QV_STATE_POLL_YIELD) + rc = true; + q_vector->state = IXGBE_QV_STATE_IDLE; + spin_unlock(&q_vector->lock); + return rc; +} + +/* called from ixgbe_low_latency_poll() */ +static inline bool ixgbe_qv_lock_poll(struct ixgbe_q_vector *q_vector) +{ + int rc = true; + spin_lock_bh(&q_vector->lock); + if ((q_vector->state & IXGBE_QV_LOCKED)) { + q_vector->state |= IXGBE_QV_STATE_POLL_YIELD; + rc = false; + } else + /* preserve yield marks */ + q_vector->state |= IXGBE_QV_STATE_POLL; + spin_unlock_bh(&q_vector->lock); + return rc; +} + +/* returns true if someone tried to get the qv while it was locked */ +static inline bool ixgbe_qv_unlock_poll(struct ixgbe_q_vector *q_vector) +{ + int rc = false; + spin_lock_bh(&q_vector->lock); + WARN_ON(q_vector->state & (IXGBE_QV_STATE_NAPI)); + + if (q_vector->state & IXGBE_QV_STATE_POLL_YIELD) + rc = true; + q_vector->state = IXGBE_QV_STATE_IDLE; + spin_unlock_bh(&q_vector->lock); + return rc; +} + +/* true if a socket is polling, even if it did not get the lock */ +static inline bool ixgbe_qv_ll_polling(struct ixgbe_q_vector *q_vector) +{ + WARN_ON(!(q_vector->state & IXGBE_QV_LOCKED)); + return q_vector->state & IXGBE_QV_USER_PEND; +} +#else /* CONFIG_NET_LL_RX_POLL */ +static inline void ixgbe_qv_init_lock(struct ixgbe_q_vector *q_vector) +{ +} + +static inline bool ixgbe_qv_lock_napi(struct ixgbe_q_vector *q_vector) +{ + return true; +} + +static inline bool ixgbe_qv_unlock_napi(struct ixgbe_q_vector *q_vector) +{ + return false; +} + +static inline bool ixgbe_qv_lock_poll(struct ixgbe_q_vector *q_vector) +{ + return false; +} + +static inline bool ixgbe_qv_unlock_poll(struct ixgbe_q_vector *q_vector) +{ + return false; +} + +static inline bool ixgbe_qv_ll_polling(struct ixgbe_q_vector *q_vector) +{ + return false; +} +#endif /* CONFIG_NET_LL_RX_POLL */ + #ifdef CONFIG_IXGBE_HWMON #define IXGBE_HWMON_TYPE_LOC 0 diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c index ef5f7a678ce1..90b4e1089ecc 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c @@ -811,6 +811,7 @@ static int ixgbe_alloc_q_vector(struct ixgbe_adapter *adapter, /* initialize NAPI */ netif_napi_add(adapter->netdev, &q_vector->napi, ixgbe_poll, 64); + napi_hash_add(&q_vector->napi); /* tie q_vector and adapter together */ adapter->q_vector[v_idx] = q_vector; @@ -931,6 +932,7 @@ static void ixgbe_free_q_vector(struct ixgbe_adapter *adapter, int v_idx) adapter->rx_ring[ring->queue_index] = NULL; adapter->q_vector[v_idx] = NULL; + napi_hash_del(&q_vector->napi); netif_napi_del(&q_vector->napi); /* diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index d30fbdd81fca..9a7dc405e7ed 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -1504,7 +1504,9 @@ static void ixgbe_rx_skb(struct ixgbe_q_vector *q_vector, { struct ixgbe_adapter *adapter = q_vector->adapter; - if (!(adapter->flags & IXGBE_FLAG_IN_NETPOLL)) + if (ixgbe_qv_ll_polling(q_vector)) + netif_receive_skb(skb); + else if (!(adapter->flags & IXGBE_FLAG_IN_NETPOLL)) napi_gro_receive(&q_vector->napi, skb); else netif_rx(skb); @@ -1892,9 +1894,9 @@ dma_sync: * expensive overhead for IOMMU access this provides a means of avoiding * it by maintaining the mapping of the page to the syste. * - * Returns true if all work is completed without reaching budget + * Returns amount of work completed **/ -static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, +static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, struct ixgbe_ring *rx_ring, const int budget) { @@ -1976,6 +1978,7 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, } #endif /* IXGBE_FCOE */ + skb_mark_ll(skb, &q_vector->napi); ixgbe_rx_skb(q_vector, skb); /* update budget accounting */ @@ -1992,9 +1995,37 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, if (cleaned_count) ixgbe_alloc_rx_buffers(rx_ring, cleaned_count); - return (total_rx_packets < budget); + return total_rx_packets; } +#ifdef CONFIG_NET_LL_RX_POLL +/* must be called with local_bh_disable()d */ +static int ixgbe_low_latency_recv(struct napi_struct *napi) +{ + struct ixgbe_q_vector *q_vector = + container_of(napi, struct ixgbe_q_vector, napi); + struct ixgbe_adapter *adapter = q_vector->adapter; + struct ixgbe_ring *ring; + int found = 0; + + if (test_bit(__IXGBE_DOWN, &adapter->state)) + return LL_FLUSH_FAILED; + + if (!ixgbe_qv_lock_poll(q_vector)) + return LL_FLUSH_BUSY; + + ixgbe_for_each_ring(ring, q_vector->rx) { + found = ixgbe_clean_rx_irq(q_vector, ring, 4); + if (found) + break; + } + + ixgbe_qv_unlock_poll(q_vector); + + return found; +} +#endif /* CONFIG_NET_LL_RX_POLL */ + /** * ixgbe_configure_msix - Configure MSI-X hardware * @adapter: board private structure @@ -2550,6 +2581,9 @@ int ixgbe_poll(struct napi_struct *napi, int budget) ixgbe_for_each_ring(ring, q_vector->tx) clean_complete &= !!ixgbe_clean_tx_irq(q_vector, ring); + if (!ixgbe_qv_lock_napi(q_vector)) + return budget; + /* attempt to distribute budget to each queue fairly, but don't allow * the budget to go below 1 because we'll exit polling */ if (q_vector->rx.count > 1) @@ -2558,9 +2592,10 @@ int ixgbe_poll(struct napi_struct *napi, int budget) per_ring_budget = budget; ixgbe_for_each_ring(ring, q_vector->rx) - clean_complete &= ixgbe_clean_rx_irq(q_vector, ring, - per_ring_budget); + clean_complete &= (ixgbe_clean_rx_irq(q_vector, ring, + per_ring_budget) < per_ring_budget); + ixgbe_qv_unlock_napi(q_vector); /* If all work not completed, return budget and keep polling */ if (!clean_complete) return budget; @@ -3747,16 +3782,25 @@ static void ixgbe_napi_enable_all(struct ixgbe_adapter *adapter) { int q_idx; - for (q_idx = 0; q_idx < adapter->num_q_vectors; q_idx++) + for (q_idx = 0; q_idx < adapter->num_q_vectors; q_idx++) { + ixgbe_qv_init_lock(adapter->q_vector[q_idx]); napi_enable(&adapter->q_vector[q_idx]->napi); + } } static void ixgbe_napi_disable_all(struct ixgbe_adapter *adapter) { int q_idx; - for (q_idx = 0; q_idx < adapter->num_q_vectors; q_idx++) + local_bh_disable(); /* for ixgbe_qv_lock_napi() */ + for (q_idx = 0; q_idx < adapter->num_q_vectors; q_idx++) { napi_disable(&adapter->q_vector[q_idx]->napi); + while (!ixgbe_qv_lock_napi(adapter->q_vector[q_idx])) { + pr_info("QV %d locked\n", q_idx); + mdelay(1); + } + } + local_bh_enable(); } #ifdef CONFIG_IXGBE_DCB @@ -7177,6 +7221,9 @@ static const struct net_device_ops ixgbe_netdev_ops = { #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = ixgbe_netpoll, #endif +#ifdef CONFIG_NET_LL_RX_POLL + .ndo_ll_poll = ixgbe_low_latency_recv, +#endif #ifdef IXGBE_FCOE .ndo_fcoe_ddp_setup = ixgbe_fcoe_ddp_get, .ndo_fcoe_ddp_target = ixgbe_fcoe_ddp_target, -- cgit v1.2.3 From 7e15b90ff9b796d14aa0d1aabc0dbb54632c673c Mon Sep 17 00:00:00 2001 From: Eliezer Tamir Date: Mon, 10 Jun 2013 11:40:31 +0300 Subject: ixgbe: add extra stats for ndo_ll_poll Add additional statistics to the ixgbe driver for ndo_ll_poll Defined under LL_EXTENDED_STATS Signed-off-by: Alexander Duyck Signed-off-by: Jesse Brandeburg Signed-off-by: Eliezer Tamir Signed-off-by: David S. Miller --- drivers/net/ethernet/intel/ixgbe/ixgbe.h | 14 +++++++++ drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c | 40 ++++++++++++++++++++++++ drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 6 ++++ 3 files changed, 60 insertions(+) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h index e9d98629b583..fb098b46c6a6 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h @@ -54,6 +54,9 @@ #include +#ifdef CONFIG_NET_LL_RX_POLL +#define LL_EXTENDED_STATS +#endif /* common prefix used by pr_<> macros */ #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -184,6 +187,11 @@ struct ixgbe_rx_buffer { struct ixgbe_queue_stats { u64 packets; u64 bytes; +#ifdef LL_EXTENDED_STATS + u64 yields; + u64 misses; + u64 cleaned; +#endif /* LL_EXTENDED_STATS */ }; struct ixgbe_tx_queue_stats { @@ -391,6 +399,9 @@ static inline bool ixgbe_qv_lock_napi(struct ixgbe_q_vector *q_vector) WARN_ON(q_vector->state & IXGBE_QV_STATE_NAPI); q_vector->state |= IXGBE_QV_STATE_NAPI_YIELD; rc = false; +#ifdef LL_EXTENDED_STATS + q_vector->tx.ring->stats.yields++; +#endif } else /* we don't care if someone yielded */ q_vector->state = IXGBE_QV_STATE_NAPI; @@ -421,6 +432,9 @@ static inline bool ixgbe_qv_lock_poll(struct ixgbe_q_vector *q_vector) if ((q_vector->state & IXGBE_QV_LOCKED)) { q_vector->state |= IXGBE_QV_STATE_POLL_YIELD; rc = false; +#ifdef LL_EXTENDED_STATS + q_vector->rx.ring->stats.yields++; +#endif } else /* preserve yield marks */ q_vector->state |= IXGBE_QV_STATE_POLL; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c index d3754722adb4..24e2e7aafda2 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c @@ -1054,6 +1054,12 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev, data[i] = 0; data[i+1] = 0; i += 2; +#ifdef LL_EXTENDED_STATS + data[i] = 0; + data[i+1] = 0; + data[i+2] = 0; + i += 3; +#endif continue; } @@ -1063,6 +1069,12 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev, data[i+1] = ring->stats.bytes; } while (u64_stats_fetch_retry_bh(&ring->syncp, start)); i += 2; +#ifdef LL_EXTENDED_STATS + data[i] = ring->stats.yields; + data[i+1] = ring->stats.misses; + data[i+2] = ring->stats.cleaned; + i += 3; +#endif } for (j = 0; j < IXGBE_NUM_RX_QUEUES; j++) { ring = adapter->rx_ring[j]; @@ -1070,6 +1082,12 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev, data[i] = 0; data[i+1] = 0; i += 2; +#ifdef LL_EXTENDED_STATS + data[i] = 0; + data[i+1] = 0; + data[i+2] = 0; + i += 3; +#endif continue; } @@ -1079,6 +1097,12 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev, data[i+1] = ring->stats.bytes; } while (u64_stats_fetch_retry_bh(&ring->syncp, start)); i += 2; +#ifdef LL_EXTENDED_STATS + data[i] = ring->stats.yields; + data[i+1] = ring->stats.misses; + data[i+2] = ring->stats.cleaned; + i += 3; +#endif } for (j = 0; j < IXGBE_MAX_PACKET_BUFFERS; j++) { @@ -1115,12 +1139,28 @@ static void ixgbe_get_strings(struct net_device *netdev, u32 stringset, p += ETH_GSTRING_LEN; sprintf(p, "tx_queue_%u_bytes", i); p += ETH_GSTRING_LEN; +#ifdef LL_EXTENDED_STATS + sprintf(p, "tx_q_%u_napi_yield", i); + p += ETH_GSTRING_LEN; + sprintf(p, "tx_q_%u_misses", i); + p += ETH_GSTRING_LEN; + sprintf(p, "tx_q_%u_cleaned", i); + p += ETH_GSTRING_LEN; +#endif /* LL_EXTENDED_STATS */ } for (i = 0; i < IXGBE_NUM_RX_QUEUES; i++) { sprintf(p, "rx_queue_%u_packets", i); p += ETH_GSTRING_LEN; sprintf(p, "rx_queue_%u_bytes", i); p += ETH_GSTRING_LEN; +#ifdef LL_EXTENDED_STATS + sprintf(p, "rx_q_%u_ll_poll_yield", i); + p += ETH_GSTRING_LEN; + sprintf(p, "rx_q_%u_misses", i); + p += ETH_GSTRING_LEN; + sprintf(p, "rx_q_%u_cleaned", i); + p += ETH_GSTRING_LEN; +#endif /* LL_EXTENDED_STATS */ } for (i = 0; i < IXGBE_MAX_PACKET_BUFFERS; i++) { sprintf(p, "tx_pb_%u_pxon", i); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 9a7dc405e7ed..047ebaaf0141 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -2016,6 +2016,12 @@ static int ixgbe_low_latency_recv(struct napi_struct *napi) ixgbe_for_each_ring(ring, q_vector->rx) { found = ixgbe_clean_rx_irq(q_vector, ring, 4); +#ifdef LL_EXTENDED_STATS + if (found) + ring->stats.cleaned += found; + else + ring->stats.misses++; +#endif if (found) break; } -- cgit v1.2.3 From 30f3a40f9a2a2869a560a9cb9ef488d10c803e14 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Wed, 5 Jun 2013 20:14:10 +0800 Subject: net: remove last caller of skb_tail_offset() and itself Similar to the following commits: commit 00f97da17a0c8d656d0c9 (netpoll: fix position of network header) commit 525cebedb32a87fa48584 (pktgen: Fix position of ip and udp header) using skb_tail_offset() seems not correct since the offset is based on head pointer. With the last caller removed, skb_tail_offset() can be killed finally. Cc: Thomas Graf Cc: Daniel Borkmann Cc: David S. Miller Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- include/linux/skbuff.h | 8 -------- net/ipv4/ipmr.c | 8 +------- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 400d82ae2b03..a7393adea0b5 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1396,10 +1396,6 @@ static inline void skb_set_tail_pointer(struct sk_buff *skb, const int offset) skb->tail += offset; } -static inline unsigned long skb_tail_offset(const struct sk_buff *skb) -{ - return skb->tail; -} #else /* NET_SKBUFF_DATA_USES_OFFSET */ static inline unsigned char *skb_tail_pointer(const struct sk_buff *skb) { @@ -1416,10 +1412,6 @@ static inline void skb_set_tail_pointer(struct sk_buff *skb, const int offset) skb->tail = skb->data + offset; } -static inline unsigned long skb_tail_offset(const struct sk_buff *skb) -{ - return skb->tail - skb->head; -} #endif /* NET_SKBUFF_DATA_USES_OFFSET */ /* diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index df97f0ac1a1c..132a09664704 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -945,7 +945,6 @@ static int ipmr_cache_report(struct mr_table *mrt, struct igmpmsg *msg; struct sock *mroute_sk; int ret; - unsigned long tail_offset; #ifdef CONFIG_IP_PIMSM if (assert == IGMPMSG_WHOLEPKT) @@ -981,12 +980,7 @@ static int ipmr_cache_report(struct mr_table *mrt, /* Copy the IP header */ - tail_offset = skb_tail_offset(skb); - if (tail_offset > 0xffff) { - kfree_skb(skb); - return -EINVAL; - } - skb_set_network_header(skb, tail_offset); + skb_set_network_header(skb, skb->len); skb_put(skb, ihl); skb_copy_to_linear_data(skb, pkt->data, ihl); ip_hdr(skb)->protocol = 0; /* Flag to the kernel this is a route add */ -- cgit v1.2.3 From 7a87718d92760fc688628ad6a430643dafa16f1f Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 11 Jun 2013 00:11:36 -0700 Subject: libata: skip SRST for all SIMG [34]7x port-multipliers For some reason, a lot of port-multipliers have issues with softreset. SIMG [34]7x series port-multipliers have been quite erratic in this regard. I recall that it was better with some firmware revisions and the current list of quirks worked fine for a while. I think it got worse with later firmwares or maybe my test coverage wasn't good enough. Anyways, HPA is reporting that his 3726 setup suffers SRST failures and then the PMP gets confused and fails to probe the last port. The hope was that we try to stick to the standard as much as possible and soonish the PMPs and their firmwares will improve in quality, so the quirk list was kept to minimum. Well, it seems like that's never gonna happen. Let's set NO_SRST for all [34]7x PMPs so that whatever remaining userbase of the device suffer the least. Maybe we should do the same for 57xx's but unfortunately I don't have any device left to test and I'm not even sure 57xx's have ever been made widely available, so let's leave those alone for now. Signed-off-by: Tejun Heo Reported-by: "H. Peter Anvin" Cc: stable@vger.kernel.org --- drivers/ata/libata-pmp.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/drivers/ata/libata-pmp.c b/drivers/ata/libata-pmp.c index 61c59ee45ce9..1c41722bb7e2 100644 --- a/drivers/ata/libata-pmp.c +++ b/drivers/ata/libata-pmp.c @@ -389,9 +389,13 @@ static void sata_pmp_quirks(struct ata_port *ap) /* link reports offline after LPM */ link->flags |= ATA_LFLAG_NO_LPM; - /* Class code report is unreliable. */ + /* + * Class code report is unreliable and SRST times + * out under certain configurations. + */ if (link->pmp < 5) - link->flags |= ATA_LFLAG_ASSUME_ATA; + link->flags |= ATA_LFLAG_NO_SRST | + ATA_LFLAG_ASSUME_ATA; /* port 5 is for SEMB device and it doesn't like SRST */ if (link->pmp == 5) @@ -399,20 +403,17 @@ static void sata_pmp_quirks(struct ata_port *ap) ATA_LFLAG_ASSUME_SEMB; } } else if (vendor == 0x1095 && devid == 0x4723) { - /* sil4723 quirks */ - ata_for_each_link(link, ap, EDGE) { - /* link reports offline after LPM */ - link->flags |= ATA_LFLAG_NO_LPM; - - /* class code report is unreliable */ - if (link->pmp < 2) - link->flags |= ATA_LFLAG_ASSUME_ATA; - - /* the config device at port 2 locks up on SRST */ - if (link->pmp == 2) - link->flags |= ATA_LFLAG_NO_SRST | - ATA_LFLAG_ASSUME_ATA; - } + /* + * sil4723 quirks + * + * Link reports offline after LPM. Class code report is + * unreliable. SIMG PMPs never got SRST reliable and the + * config device at port 2 locks up on SRST. + */ + ata_for_each_link(link, ap, EDGE) + link->flags |= ATA_LFLAG_NO_LPM | + ATA_LFLAG_NO_SRST | + ATA_LFLAG_ASSUME_ATA; } else if (vendor == 0x1095 && devid == 0x4726) { /* sil4726 quirks */ ata_for_each_link(link, ap, EDGE) { -- cgit v1.2.3 From 9ba18891f75535eca3ef53138b48970eb60f5255 Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Wed, 5 Jun 2013 10:08:00 -0400 Subject: bridge: Add flag to control mac learning. Allow user to control whether mac learning is enabled on the port. By default, mac learning is enabled. Disabling mac learning will cause new dynamic FDB entries to not be created for a particular port. Signed-off-by: Vlad Yasevich Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- include/uapi/linux/if_link.h | 1 + net/bridge/br_if.c | 2 +- net/bridge/br_input.c | 6 ++++-- net/bridge/br_netlink.c | 6 +++++- net/bridge/br_private.h | 1 + net/bridge/br_sysfs_if.c | 2 ++ 6 files changed, 14 insertions(+), 4 deletions(-) diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index b05823cae784..8643809d8417 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -221,6 +221,7 @@ enum { IFLA_BRPORT_GUARD, /* bpdu guard */ IFLA_BRPORT_PROTECT, /* root port protection */ IFLA_BRPORT_FAST_LEAVE, /* multicast fast leave */ + IFLA_BRPORT_LEARNING, /* mac learning */ __IFLA_BRPORT_MAX }; #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 4cdba60926ff..2c08911df578 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -221,7 +221,7 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br, p->path_cost = port_cost(dev); p->priority = 0x8000 >> BR_PORT_BITS; p->port_no = index; - p->flags = 0; + p->flags = BR_LEARNING; br_init_port(p); p->state = BR_STATE_DISABLED; br_stp_port_timer_init(p); diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index 828e2bcc1f52..7e993667d4bf 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -75,7 +75,8 @@ int br_handle_frame_finish(struct sk_buff *skb) /* insert into forwarding database after filtering to avoid spoofing */ br = p->br; - br_fdb_update(br, p, eth_hdr(skb)->h_source, vid); + if (p->flags & BR_LEARNING) + br_fdb_update(br, p, eth_hdr(skb)->h_source, vid); if (!is_broadcast_ether_addr(dest) && is_multicast_ether_addr(dest) && br_multicast_rcv(br, p, skb)) @@ -142,7 +143,8 @@ static int br_handle_local_finish(struct sk_buff *skb) u16 vid = 0; br_vlan_get_tag(skb, &vid); - br_fdb_update(p->br, p, eth_hdr(skb)->h_source, vid); + if (p->flags & BR_LEARNING) + br_fdb_update(p->br, p, eth_hdr(skb)->h_source, vid); return 0; /* process further */ } diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 8e3abf564798..ce902bf8a618 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -30,6 +30,7 @@ static inline size_t br_port_info_size(void) + nla_total_size(1) /* IFLA_BRPORT_GUARD */ + nla_total_size(1) /* IFLA_BRPORT_PROTECT */ + nla_total_size(1) /* IFLA_BRPORT_FAST_LEAVE */ + + nla_total_size(1) /* IFLA_BRPORT_LEARNING */ + 0; } @@ -56,7 +57,8 @@ static int br_port_fill_attrs(struct sk_buff *skb, nla_put_u8(skb, IFLA_BRPORT_MODE, mode) || nla_put_u8(skb, IFLA_BRPORT_GUARD, !!(p->flags & BR_BPDU_GUARD)) || nla_put_u8(skb, IFLA_BRPORT_PROTECT, !!(p->flags & BR_ROOT_BLOCK)) || - nla_put_u8(skb, IFLA_BRPORT_FAST_LEAVE, !!(p->flags & BR_MULTICAST_FAST_LEAVE))) + nla_put_u8(skb, IFLA_BRPORT_FAST_LEAVE, !!(p->flags & BR_MULTICAST_FAST_LEAVE)) || + nla_put_u8(skb, IFLA_BRPORT_LEARNING, !!(p->flags & BR_LEARNING))) return -EMSGSIZE; return 0; @@ -281,6 +283,7 @@ static const struct nla_policy ifla_brport_policy[IFLA_BRPORT_MAX + 1] = { [IFLA_BRPORT_MODE] = { .type = NLA_U8 }, [IFLA_BRPORT_GUARD] = { .type = NLA_U8 }, [IFLA_BRPORT_PROTECT] = { .type = NLA_U8 }, + [IFLA_BRPORT_LEARNING] = { .type = NLA_U8 }, }; /* Change the state of the port and notify spanning tree */ @@ -328,6 +331,7 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[]) br_set_port_flag(p, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD); br_set_port_flag(p, tb, IFLA_BRPORT_FAST_LEAVE, BR_MULTICAST_FAST_LEAVE); br_set_port_flag(p, tb, IFLA_BRPORT_PROTECT, BR_ROOT_BLOCK); + br_set_port_flag(p, tb, IFLA_BRPORT_LEARNING, BR_LEARNING); if (tb[IFLA_BRPORT_COST]) { err = br_stp_set_path_cost(p, nla_get_u32(tb[IFLA_BRPORT_COST])); diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 1b0ac95a5c37..04d7f43508f7 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -158,6 +158,7 @@ struct net_bridge_port #define BR_ROOT_BLOCK 0x00000004 #define BR_MULTICAST_FAST_LEAVE 0x00000008 #define BR_ADMIN_COST 0x00000010 +#define BR_LEARNING 0x00000020 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING u32 multicast_startup_queries_sent; diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c index a1ef1b6e14dc..707f3628e9cd 100644 --- a/net/bridge/br_sysfs_if.c +++ b/net/bridge/br_sysfs_if.c @@ -158,6 +158,7 @@ static BRPORT_ATTR(flush, S_IWUSR, NULL, store_flush); BRPORT_ATTR_FLAG(hairpin_mode, BR_HAIRPIN_MODE); BRPORT_ATTR_FLAG(bpdu_guard, BR_BPDU_GUARD); BRPORT_ATTR_FLAG(root_block, BR_ROOT_BLOCK); +BRPORT_ATTR_FLAG(learning, BR_LEARNING); #ifdef CONFIG_BRIDGE_IGMP_SNOOPING static ssize_t show_multicast_router(struct net_bridge_port *p, char *buf) @@ -195,6 +196,7 @@ static const struct brport_attribute *brport_attrs[] = { &brport_attr_hairpin_mode, &brport_attr_bpdu_guard, &brport_attr_root_block, + &brport_attr_learning, #ifdef CONFIG_BRIDGE_IGMP_SNOOPING &brport_attr_multicast_router, &brport_attr_multicast_fast_leave, -- cgit v1.2.3 From 867a59436fc35593ae0e0efcd56cc6d2f8506586 Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Wed, 5 Jun 2013 10:08:01 -0400 Subject: bridge: Add a flag to control unicast packet flood. Add a flag to control flood of unicast traffic. By default, flood is on and the bridge will flood unicast traffic if it doesn't know the destination. When the flag is turned off, unicast traffic without an FDB will not be forwarded to the specified port. Signed-off-by: Vlad Yasevich Reviewed-by: Michael S. Tsirkin Signed-off-by: David S. Miller --- include/uapi/linux/if_link.h | 1 + net/bridge/br_device.c | 8 ++++---- net/bridge/br_forward.c | 14 +++++++++----- net/bridge/br_if.c | 2 +- net/bridge/br_input.c | 9 ++++++--- net/bridge/br_netlink.c | 6 +++++- net/bridge/br_private.h | 6 ++++-- net/bridge/br_sysfs_if.c | 2 ++ 8 files changed, 32 insertions(+), 16 deletions(-) diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 8643809d8417..da05a2698cb5 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -222,6 +222,7 @@ enum { IFLA_BRPORT_PROTECT, /* root port protection */ IFLA_BRPORT_FAST_LEAVE, /* multicast fast leave */ IFLA_BRPORT_LEARNING, /* mac learning */ + IFLA_BRPORT_UNICAST_FLOOD, /* flood unicast traffic */ __IFLA_BRPORT_MAX }; #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 75f3239130f8..2ef66781fedb 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -58,10 +58,10 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) skb_pull(skb, ETH_HLEN); if (is_broadcast_ether_addr(dest)) - br_flood_deliver(br, skb); + br_flood_deliver(br, skb, false); else if (is_multicast_ether_addr(dest)) { if (unlikely(netpoll_tx_running(dev))) { - br_flood_deliver(br, skb); + br_flood_deliver(br, skb, false); goto out; } if (br_multicast_rcv(br, NULL, skb)) { @@ -73,11 +73,11 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) if (mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) br_multicast_deliver(mdst, skb); else - br_flood_deliver(br, skb); + br_flood_deliver(br, skb, false); } else if ((dst = __br_fdb_get(br, dest, vid)) != NULL) br_deliver(dst->dst, skb); else - br_flood_deliver(br, skb); + br_flood_deliver(br, skb, true); out: rcu_read_unlock(); diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index 092b20e4ee4c..4b81b1471789 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -174,7 +174,8 @@ out: static void br_flood(struct net_bridge *br, struct sk_buff *skb, struct sk_buff *skb0, void (*__packet_hook)(const struct net_bridge_port *p, - struct sk_buff *skb)) + struct sk_buff *skb), + bool unicast) { struct net_bridge_port *p; struct net_bridge_port *prev; @@ -182,6 +183,9 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb, prev = NULL; list_for_each_entry_rcu(p, &br->port_list, list) { + /* Do not flood unicast traffic to ports that turn it off */ + if (unicast && !(p->flags & BR_FLOOD)) + continue; prev = maybe_deliver(prev, p, skb, __packet_hook); if (IS_ERR(prev)) goto out; @@ -203,16 +207,16 @@ out: /* called with rcu_read_lock */ -void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb) +void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb, bool unicast) { - br_flood(br, skb, NULL, __br_deliver); + br_flood(br, skb, NULL, __br_deliver, unicast); } /* called under bridge lock */ void br_flood_forward(struct net_bridge *br, struct sk_buff *skb, - struct sk_buff *skb2) + struct sk_buff *skb2, bool unicast) { - br_flood(br, skb, skb2, __br_forward); + br_flood(br, skb, skb2, __br_forward, unicast); } #ifdef CONFIG_BRIDGE_IGMP_SNOOPING diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 2c08911df578..5623be6b9ecd 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -221,7 +221,7 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br, p->path_cost = port_cost(dev); p->priority = 0x8000 >> BR_PORT_BITS; p->port_no = index; - p->flags = BR_LEARNING; + p->flags = BR_LEARNING | BR_FLOOD; br_init_port(p); p->state = BR_STATE_DISABLED; br_stp_port_timer_init(p); diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index 7e993667d4bf..1b8b8b824cd7 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -65,6 +65,7 @@ int br_handle_frame_finish(struct sk_buff *skb) struct net_bridge_fdb_entry *dst; struct net_bridge_mdb_entry *mdst; struct sk_buff *skb2; + bool unicast = true; u16 vid = 0; if (!p || p->state == BR_STATE_DISABLED) @@ -95,9 +96,10 @@ int br_handle_frame_finish(struct sk_buff *skb) dst = NULL; - if (is_broadcast_ether_addr(dest)) + if (is_broadcast_ether_addr(dest)) { skb2 = skb; - else if (is_multicast_ether_addr(dest)) { + unicast = false; + } else if (is_multicast_ether_addr(dest)) { mdst = br_mdb_get(br, skb, vid); if (mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) { if ((mdst && mdst->mglist) || @@ -110,6 +112,7 @@ int br_handle_frame_finish(struct sk_buff *skb) } else skb2 = skb; + unicast = false; br->dev->stats.multicast++; } else if ((dst = __br_fdb_get(br, dest, vid)) && dst->is_local) { @@ -123,7 +126,7 @@ int br_handle_frame_finish(struct sk_buff *skb) dst->used = jiffies; br_forward(dst->dst, skb, skb2); } else - br_flood_forward(br, skb, skb2); + br_flood_forward(br, skb, skb2, unicast); } if (skb2) diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index ce902bf8a618..1fc30abd3a52 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -31,6 +31,7 @@ static inline size_t br_port_info_size(void) + nla_total_size(1) /* IFLA_BRPORT_PROTECT */ + nla_total_size(1) /* IFLA_BRPORT_FAST_LEAVE */ + nla_total_size(1) /* IFLA_BRPORT_LEARNING */ + + nla_total_size(1) /* IFLA_BRPORT_UNICAST_FLOOD */ + 0; } @@ -58,7 +59,8 @@ static int br_port_fill_attrs(struct sk_buff *skb, nla_put_u8(skb, IFLA_BRPORT_GUARD, !!(p->flags & BR_BPDU_GUARD)) || nla_put_u8(skb, IFLA_BRPORT_PROTECT, !!(p->flags & BR_ROOT_BLOCK)) || nla_put_u8(skb, IFLA_BRPORT_FAST_LEAVE, !!(p->flags & BR_MULTICAST_FAST_LEAVE)) || - nla_put_u8(skb, IFLA_BRPORT_LEARNING, !!(p->flags & BR_LEARNING))) + nla_put_u8(skb, IFLA_BRPORT_LEARNING, !!(p->flags & BR_LEARNING)) || + nla_put_u8(skb, IFLA_BRPORT_UNICAST_FLOOD, !!(p->flags & BR_FLOOD))) return -EMSGSIZE; return 0; @@ -284,6 +286,7 @@ static const struct nla_policy ifla_brport_policy[IFLA_BRPORT_MAX + 1] = { [IFLA_BRPORT_GUARD] = { .type = NLA_U8 }, [IFLA_BRPORT_PROTECT] = { .type = NLA_U8 }, [IFLA_BRPORT_LEARNING] = { .type = NLA_U8 }, + [IFLA_BRPORT_UNICAST_FLOOD] = { .type = NLA_U8 }, }; /* Change the state of the port and notify spanning tree */ @@ -332,6 +335,7 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[]) br_set_port_flag(p, tb, IFLA_BRPORT_FAST_LEAVE, BR_MULTICAST_FAST_LEAVE); br_set_port_flag(p, tb, IFLA_BRPORT_PROTECT, BR_ROOT_BLOCK); br_set_port_flag(p, tb, IFLA_BRPORT_LEARNING, BR_LEARNING); + br_set_port_flag(p, tb, IFLA_BRPORT_UNICAST_FLOOD, BR_FLOOD); if (tb[IFLA_BRPORT_COST]) { err = br_stp_set_path_cost(p, nla_get_u32(tb[IFLA_BRPORT_COST])); diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 04d7f43508f7..3be89b3ce17b 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -159,6 +159,7 @@ struct net_bridge_port #define BR_MULTICAST_FAST_LEAVE 0x00000008 #define BR_ADMIN_COST 0x00000010 #define BR_LEARNING 0x00000020 +#define BR_FLOOD 0x00000040 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING u32 multicast_startup_queries_sent; @@ -414,9 +415,10 @@ extern int br_dev_queue_push_xmit(struct sk_buff *skb); extern void br_forward(const struct net_bridge_port *to, struct sk_buff *skb, struct sk_buff *skb0); extern int br_forward_finish(struct sk_buff *skb); -extern void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb); +extern void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb, + bool unicast); extern void br_flood_forward(struct net_bridge *br, struct sk_buff *skb, - struct sk_buff *skb2); + struct sk_buff *skb2, bool unicast); /* br_if.c */ extern void br_port_carrier_check(struct net_bridge_port *p); diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c index 707f3628e9cd..2a2cdb756d51 100644 --- a/net/bridge/br_sysfs_if.c +++ b/net/bridge/br_sysfs_if.c @@ -159,6 +159,7 @@ BRPORT_ATTR_FLAG(hairpin_mode, BR_HAIRPIN_MODE); BRPORT_ATTR_FLAG(bpdu_guard, BR_BPDU_GUARD); BRPORT_ATTR_FLAG(root_block, BR_ROOT_BLOCK); BRPORT_ATTR_FLAG(learning, BR_LEARNING); +BRPORT_ATTR_FLAG(unicast_flood, BR_FLOOD); #ifdef CONFIG_BRIDGE_IGMP_SNOOPING static ssize_t show_multicast_router(struct net_bridge_port *p, char *buf) @@ -197,6 +198,7 @@ static const struct brport_attribute *brport_attrs[] = { &brport_attr_bpdu_guard, &brport_attr_root_block, &brport_attr_learning, + &brport_attr_unicast_flood, #ifdef CONFIG_BRIDGE_IGMP_SNOOPING &brport_attr_multicast_router, &brport_attr_multicast_fast_leave, -- cgit v1.2.3 From 8249152c472e10c18936b774737fd58c60335154 Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Thu, 6 Jun 2013 14:35:18 +0800 Subject: xen-netfront: use skb_partial_csum_set() to simplify the codes use skb_partial_csum_set() to simplify the codes Cc: Jason Wang Signed-off-by: Li RongQing Acked-by: Jason Wang Signed-off-by: David S. Miller --- drivers/net/xen-netfront.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index 62238a08cb51..76a22365d4e9 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -858,7 +858,6 @@ static RING_IDX xennet_fill_frags(struct netfront_info *np, static int checksum_setup(struct net_device *dev, struct sk_buff *skb) { struct iphdr *iph; - unsigned char *th; int err = -EPROTO; int recalculate_partial_csum = 0; @@ -883,27 +882,27 @@ static int checksum_setup(struct net_device *dev, struct sk_buff *skb) goto out; iph = (void *)skb->data; - th = skb->data + 4 * iph->ihl; - if (th >= skb_tail_pointer(skb)) - goto out; - skb->csum_start = th - skb->head; switch (iph->protocol) { case IPPROTO_TCP: - skb->csum_offset = offsetof(struct tcphdr, check); + if (!skb_partial_csum_set(skb, 4 * iph->ihl, + offsetof(struct tcphdr, check))) + goto out; if (recalculate_partial_csum) { - struct tcphdr *tcph = (struct tcphdr *)th; + struct tcphdr *tcph = tcp_hdr(skb); tcph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len - iph->ihl*4, IPPROTO_TCP, 0); } break; case IPPROTO_UDP: - skb->csum_offset = offsetof(struct udphdr, check); + if (!skb_partial_csum_set(skb, 4 * iph->ihl, + offsetof(struct udphdr, check))) + goto out; if (recalculate_partial_csum) { - struct udphdr *udph = (struct udphdr *)th; + struct udphdr *udph = udp_hdr(skb); udph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len - iph->ihl*4, IPPROTO_UDP, 0); @@ -917,9 +916,6 @@ static int checksum_setup(struct net_device *dev, struct sk_buff *skb) goto out; } - if ((th + skb->csum_offset + 2) > skb_tail_pointer(skb)) - goto out; - err = 0; out: -- cgit v1.2.3 From da12c90e099789a63073fc82a19542ce54d4efb9 Mon Sep 17 00:00:00 2001 From: Gao feng Date: Thu, 6 Jun 2013 14:49:11 +0800 Subject: netlink: Add compare function for netlink_table As we know, netlink sockets are private resource of net namespace, they can communicate with each other only when they in the same net namespace. this works well until we try to add namespace support for other subsystems which use netlink. Don't like ipv4 and route table.., it is not suited to make these subsytems belong to net namespace, Such as audit and crypto subsystems,they are more suitable to user namespace. So we must have the ability to make the netlink sockets in same user namespace can communicate with each other. This patch adds a new function pointer "compare" for netlink_table, we can decide if the netlink sockets can communicate with each other through this netlink_table self-defined compare function. The behavior isn't changed if we don't provide the compare function for netlink_table. Signed-off-by: Gao feng Acked-by: Serge E. Hallyn Signed-off-by: David S. Miller --- include/linux/netlink.h | 1 + net/netlink/af_netlink.c | 33 +++++++++++++++++++++++++-------- net/netlink/af_netlink.h | 1 + 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/include/linux/netlink.h b/include/linux/netlink.h index 6358da5eeee8..f78b430f4af5 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -46,6 +46,7 @@ struct netlink_kernel_cfg { void (*input)(struct sk_buff *skb); struct mutex *cb_mutex; void (*bind)(int group); + bool (*compare)(struct net *net, struct sock *sk); }; extern struct sock *__netlink_kernel_create(struct net *net, int unit, diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 68c167374394..9b6b115e008f 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -858,16 +858,23 @@ netlink_unlock_table(void) wake_up(&nl_table_wait); } +static bool netlink_compare(struct net *net, struct sock *sk) +{ + return net_eq(sock_net(sk), net); +} + static struct sock *netlink_lookup(struct net *net, int protocol, u32 portid) { - struct nl_portid_hash *hash = &nl_table[protocol].hash; + struct netlink_table *table = &nl_table[protocol]; + struct nl_portid_hash *hash = &table->hash; struct hlist_head *head; struct sock *sk; read_lock(&nl_table_lock); head = nl_portid_hashfn(hash, portid); sk_for_each(sk, head) { - if (net_eq(sock_net(sk), net) && (nlk_sk(sk)->portid == portid)) { + if (table->compare(net, sk) && + (nlk_sk(sk)->portid == portid)) { sock_hold(sk); goto found; } @@ -980,7 +987,8 @@ netlink_update_listeners(struct sock *sk) static int netlink_insert(struct sock *sk, struct net *net, u32 portid) { - struct nl_portid_hash *hash = &nl_table[sk->sk_protocol].hash; + struct netlink_table *table = &nl_table[sk->sk_protocol]; + struct nl_portid_hash *hash = &table->hash; struct hlist_head *head; int err = -EADDRINUSE; struct sock *osk; @@ -990,7 +998,8 @@ static int netlink_insert(struct sock *sk, struct net *net, u32 portid) head = nl_portid_hashfn(hash, portid); len = 0; sk_for_each(osk, head) { - if (net_eq(sock_net(osk), net) && (nlk_sk(osk)->portid == portid)) + if (table->compare(net, osk) && + (nlk_sk(osk)->portid == portid)) break; len++; } @@ -1165,6 +1174,7 @@ static int netlink_release(struct socket *sock) kfree_rcu(old, rcu); nl_table[sk->sk_protocol].module = NULL; nl_table[sk->sk_protocol].bind = NULL; + nl_table[sk->sk_protocol].compare = NULL; nl_table[sk->sk_protocol].flags = 0; nl_table[sk->sk_protocol].registered = 0; } @@ -1187,7 +1197,8 @@ static int netlink_autobind(struct socket *sock) { struct sock *sk = sock->sk; struct net *net = sock_net(sk); - struct nl_portid_hash *hash = &nl_table[sk->sk_protocol].hash; + struct netlink_table *table = &nl_table[sk->sk_protocol]; + struct nl_portid_hash *hash = &table->hash; struct hlist_head *head; struct sock *osk; s32 portid = task_tgid_vnr(current); @@ -1199,7 +1210,7 @@ retry: netlink_table_grab(); head = nl_portid_hashfn(hash, portid); sk_for_each(osk, head) { - if (!net_eq(sock_net(osk), net)) + if (!table->compare(net, osk)) continue; if (nlk_sk(osk)->portid == portid) { /* Bind collision, search negative portid values. */ @@ -2315,9 +2326,12 @@ __netlink_kernel_create(struct net *net, int unit, struct module *module, rcu_assign_pointer(nl_table[unit].listeners, listeners); nl_table[unit].cb_mutex = cb_mutex; nl_table[unit].module = module; + nl_table[unit].compare = netlink_compare; if (cfg) { nl_table[unit].bind = cfg->bind; nl_table[unit].flags = cfg->flags; + if (cfg->compare) + nl_table[unit].compare = cfg->compare; } nl_table[unit].registered = 1; } else { @@ -2740,6 +2754,7 @@ static void *netlink_seq_next(struct seq_file *seq, void *v, loff_t *pos) { struct sock *s; struct nl_seq_iter *iter; + struct net *net; int i, j; ++*pos; @@ -2747,11 +2762,12 @@ static void *netlink_seq_next(struct seq_file *seq, void *v, loff_t *pos) if (v == SEQ_START_TOKEN) return netlink_seq_socket_idx(seq, 0); + net = seq_file_net(seq); iter = seq->private; s = v; do { s = sk_next(s); - } while (s && sock_net(s) != seq_file_net(seq)); + } while (s && !nl_table[s->sk_protocol].compare(net, s)); if (s) return s; @@ -2763,7 +2779,8 @@ static void *netlink_seq_next(struct seq_file *seq, void *v, loff_t *pos) for (; j <= hash->mask; j++) { s = sk_head(&hash->table[j]); - while (s && sock_net(s) != seq_file_net(seq)) + + while (s && !nl_table[s->sk_protocol].compare(net, s)) s = sk_next(s); if (s) { iter->link = i; diff --git a/net/netlink/af_netlink.h b/net/netlink/af_netlink.h index ed8522265f4e..eaa88d187cdc 100644 --- a/net/netlink/af_netlink.h +++ b/net/netlink/af_netlink.h @@ -73,6 +73,7 @@ struct netlink_table { struct mutex *cb_mutex; struct module *module; void (*bind)(int group); + bool (*compare)(struct net *net, struct sock *sock); int registered; }; -- cgit v1.2.3 From 50f018dff180942dc40e601de6e97145a4aaeaa9 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 10 Jun 2013 11:20:19 +0100 Subject: drm/i915: Initialize ring->hangcheck upon ring init When we reset and restart a ring, we also want to clear any existing hangcheck. Signed-off-by: Chris Wilson Reviewed-by: Mika Kuoppala Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ringbuffer.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 1ef081cd1706..a3cfa35b0573 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -453,6 +453,8 @@ static int init_ring_common(struct intel_ring_buffer *ring) ring->last_retired_head = -1; } + memset(&ring->hangcheck, 0, sizeof(ring->hangcheck)); + out: if (HAS_FORCE_WAKE(dev)) gen6_gt_force_wake_put(dev_priv); -- cgit v1.2.3 From 9107e9d227e3b0893829baee4ac59feb874d4c23 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 10 Jun 2013 11:20:20 +0100 Subject: drm/i915: Only slightly increment hangcheck score if we succesfully kick a ring After kicking a ring, it should be free to make progress again and so should not be accused of being stuck until hangcheck fires once more. In order to catch a denial-of-service within a batch or across multiple batches, we still do increment the hangcheck score - just not as severely so that it takes multiple kicks to fail. This should address part of Ben's justified criticism of commit 05407ff889ceebe383aa5907219f86582ef96b72 Author: Mika Kuoppala Date: Thu May 30 09:04:29 2013 +0300 drm/i915: detect hang using per ring hangcheck_score "There's also another corner case on the kick. If the seqno = 2 (though not stuck), and on the 3rd hangcheck, the ring is stuck, and we try to kick it... we don't actually try to find out if the kick helped." v2: Make sure we catch DoS attempts with batches full of invalid WAITs. v3: Preserve the ability to detect loops by always charging the ring if it is busy on the same request. v4: Make sure we queue another check if on a new batch References: https://bugs.freedesktop.org/show_bug.cgi?id=65394 Signed-off-by: Chris Wilson Cc: Mika Kuoppala Cc: Ben Widawsky Reviewed-by: Mika Kuoppala Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 110 +++++++++++++++++++++------------------- 1 file changed, 58 insertions(+), 52 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index c482e8ae58dc..6a757691488e 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -2314,21 +2314,11 @@ ring_last_seqno(struct intel_ring_buffer *ring) struct drm_i915_gem_request, list)->seqno; } -static bool i915_hangcheck_ring_idle(struct intel_ring_buffer *ring, - u32 ring_seqno, bool *err) -{ - if (list_empty(&ring->request_list) || - i915_seqno_passed(ring_seqno, ring_last_seqno(ring))) { - /* Issue a wake-up to catch stuck h/w. */ - if (waitqueue_active(&ring->irq_queue)) { - DRM_ERROR("Hangcheck timer elapsed... %s idle\n", - ring->name); - wake_up_all(&ring->irq_queue); - *err = true; - } - return true; - } - return false; +static bool +ring_idle(struct intel_ring_buffer *ring, u32 seqno) +{ + return (list_empty(&ring->request_list) || + i915_seqno_passed(seqno, ring_last_seqno(ring))); } static bool semaphore_passed(struct intel_ring_buffer *ring) @@ -2362,16 +2352,26 @@ static bool semaphore_passed(struct intel_ring_buffer *ring) ioread32(ring->virtual_start+acthd+4)+1); } -static bool kick_ring(struct intel_ring_buffer *ring) +static bool ring_hung(struct intel_ring_buffer *ring) { struct drm_device *dev = ring->dev; struct drm_i915_private *dev_priv = dev->dev_private; - u32 tmp = I915_READ_CTL(ring); + u32 tmp; + + if (IS_GEN2(dev)) + return true; + + /* 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. + */ + tmp = I915_READ_CTL(ring); if (tmp & RING_WAIT) { DRM_ERROR("Kicking stuck wait on %s\n", ring->name); I915_WRITE_CTL(ring, tmp); - return true; + return false; } if (INTEL_INFO(dev)->gen >= 6 && @@ -2380,22 +2380,10 @@ static bool kick_ring(struct intel_ring_buffer *ring) DRM_ERROR("Kicking stuck semaphore on %s\n", ring->name); I915_WRITE_CTL(ring, tmp); - return true; - } - return false; -} - -static bool i915_hangcheck_ring_hung(struct intel_ring_buffer *ring) -{ - if (IS_GEN2(ring->dev)) return false; + } - /* 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. - */ - return !kick_ring(ring); + return true; } /** @@ -2413,45 +2401,63 @@ void i915_hangcheck_elapsed(unsigned long data) struct intel_ring_buffer *ring; int i; int busy_count = 0, rings_hung = 0; - bool stuck[I915_NUM_RINGS]; + bool stuck[I915_NUM_RINGS] = { 0 }; +#define BUSY 1 +#define KICK 5 +#define HUNG 20 +#define FIRE 30 if (!i915_enable_hangcheck) return; for_each_ring(ring, dev_priv, i) { u32 seqno, acthd; - bool idle, err = false; + bool busy = true; seqno = ring->get_seqno(ring, false); acthd = intel_ring_get_active_head(ring); - idle = i915_hangcheck_ring_idle(ring, seqno, &err); - stuck[i] = ring->hangcheck.acthd == acthd; - - if (idle) { - if (err) - ring->hangcheck.score += 2; - else - ring->hangcheck.score = 0; - } else { - busy_count++; - if (ring->hangcheck.seqno == seqno) { - ring->hangcheck.score++; - - /* Kick ring if stuck*/ - if (stuck[i]) - i915_hangcheck_ring_hung(ring); + if (ring->hangcheck.seqno == seqno) { + if (ring_idle(ring, seqno)) { + if (waitqueue_active(&ring->irq_queue)) { + /* Issue a wake-up to catch stuck h/w. */ + DRM_ERROR("Hangcheck timer elapsed... %s idle\n", + ring->name); + wake_up_all(&ring->irq_queue); + ring->hangcheck.score += HUNG; + } else + busy = false; } else { - ring->hangcheck.score = 0; + int score; + + stuck[i] = ring->hangcheck.acthd == acthd; + if (stuck[i]) { + /* Every time we kick the ring, add a + * small increment to the hangcheck + * score so that we can catch a + * batch that is repeatedly kicked. + */ + score = ring_hung(ring) ? HUNG : KICK; + } else + score = BUSY; + + ring->hangcheck.score += score; } + } else { + /* Gradually reduce the count so that we catch DoS + * attempts across multiple batches. + */ + if (ring->hangcheck.score > 0) + ring->hangcheck.score--; } ring->hangcheck.seqno = seqno; ring->hangcheck.acthd = acthd; + busy_count += busy; } for_each_ring(ring, dev_priv, i) { - if (ring->hangcheck.score > 2) { + if (ring->hangcheck.score > FIRE) { rings_hung++; DRM_ERROR("%s: %s on %s 0x%x\n", ring->name, stuck[i] ? "stuck" : "no progress", -- cgit v1.2.3 From b41abb42bf62a85a32c41dab873220598a6ee266 Mon Sep 17 00:00:00 2001 From: "Peter Pan(潘卫平)" Date: Thu, 6 Jun 2013 21:27:21 +0800 Subject: net: pass correct parameter to skb_headers_offset_update() Since commit 1a37e412a022(net: Use 16bits for *_headers fields of struct skbuff), skb->*_header are relative to skb->head, so copy_skb_header() should not call skb_headers_offset_update() now, and we should pass correct parameter to skb_headers_offset_update() in pskb_expand_head() and skb_copy_expand(). Signed-off-by: Weiping Pan Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- net/core/skbuff.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 4a4181e16c1a..edf37578e21e 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -909,18 +909,8 @@ static void skb_headers_offset_update(struct sk_buff *skb, int off) static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old) { -#ifndef NET_SKBUFF_DATA_USES_OFFSET - /* - * Shift between the two data areas in bytes - */ - unsigned long offset = new->data - old->data; -#endif - __copy_skb_header(new, old); -#ifndef NET_SKBUFF_DATA_USES_OFFSET - skb_headers_offset_update(new, offset); -#endif skb_shinfo(new)->gso_size = skb_shinfo(old)->gso_size; skb_shinfo(new)->gso_segs = skb_shinfo(old)->gso_segs; skb_shinfo(new)->gso_type = skb_shinfo(old)->gso_type; @@ -1112,7 +1102,7 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, skb->end = skb->head + size; #endif skb->tail += off; - skb_headers_offset_update(skb, off); + skb_headers_offset_update(skb, nhead); /* Only adjust this if it actually is csum_start rather than csum */ if (skb->ip_summed == CHECKSUM_PARTIAL) skb->csum_start += nhead; @@ -1207,9 +1197,8 @@ struct sk_buff *skb_copy_expand(const struct sk_buff *skb, off = newheadroom - oldheadroom; if (n->ip_summed == CHECKSUM_PARTIAL) n->csum_start += off; -#ifdef NET_SKBUFF_DATA_USES_OFFSET + skb_headers_offset_update(n, off); -#endif return n; } -- cgit v1.2.3 From 6274f2126a0454d3c3df1bc9cc6f5e18302696f7 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 10 Jun 2013 11:20:21 +0100 Subject: drm/i915: Don't count semaphore waits towards a stuck ring If we detect a ring is in a valid wait for another, just let it be. Eventually it will either begin to progress again, or the entire system will come grinding to a halt and then hangcheck will fire as soon as the deadlock is detected. This error was foretold by Ben in commit 05407ff889ceebe383aa5907219f86582ef96b72 Author: Mika Kuoppala Date: Thu May 30 09:04:29 2013 +0300 drm/i915: detect hang using per ring hangcheck_score "If ring B is waiting on ring A via semaphore, and ring A is making progress, albeit slowly - the hangcheck will fire. The check will determine that A is moving, however ring B will appear hung because the ACTHD doesn't move. I honestly can't say if that's actually a realistic problem to hit it probably implies the timeout value is too low." v2: Make sure we don't even incur the KICK cost whilst waiting. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=65394 Signed-off-by: Chris Wilson Cc: Mika Kuoppala Cc: Ben Widawsky Reviewed-by: Mika Kuoppala Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 121 +++++++++++++++++++++++--------- drivers/gpu/drm/i915/intel_ringbuffer.h | 1 + 2 files changed, 90 insertions(+), 32 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 6a757691488e..563abe79bb20 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -2321,21 +2321,21 @@ ring_idle(struct intel_ring_buffer *ring, u32 seqno) i915_seqno_passed(seqno, ring_last_seqno(ring))); } -static bool semaphore_passed(struct intel_ring_buffer *ring) +static struct intel_ring_buffer * +semaphore_waits_for(struct intel_ring_buffer *ring, u32 *seqno) { struct drm_i915_private *dev_priv = ring->dev->dev_private; - u32 acthd = intel_ring_get_active_head(ring) & HEAD_ADDR; - struct intel_ring_buffer *signaller; - u32 cmd, ipehr, acthd_min; + u32 cmd, ipehr, acthd, acthd_min; ipehr = I915_READ(RING_IPEHR(ring->mmio_base)); if ((ipehr & ~(0x3 << 16)) != (MI_SEMAPHORE_MBOX | MI_SEMAPHORE_COMPARE | MI_SEMAPHORE_REGISTER)) - return false; + return NULL; /* ACTHD is likely pointing to the dword after the actual command, * so scan backwards until we find the MBOX. */ + acthd = intel_ring_get_active_head(ring) & HEAD_ADDR; acthd_min = max((int)acthd - 3 * 4, 0); do { cmd = ioread32(ring->virtual_start + acthd); @@ -2344,22 +2344,53 @@ static bool semaphore_passed(struct intel_ring_buffer *ring) acthd -= 4; if (acthd < acthd_min) - return false; + return NULL; } while (1); - signaller = &dev_priv->ring[(ring->id + (((ipehr >> 17) & 1) + 1)) % 3]; - return i915_seqno_passed(signaller->get_seqno(signaller, false), - ioread32(ring->virtual_start+acthd+4)+1); + *seqno = ioread32(ring->virtual_start+acthd+4)+1; + return &dev_priv->ring[(ring->id + (((ipehr >> 17) & 1) + 1)) % 3]; +} + +static int semaphore_passed(struct intel_ring_buffer *ring) +{ + struct drm_i915_private *dev_priv = ring->dev->dev_private; + struct intel_ring_buffer *signaller; + u32 seqno, ctl; + + ring->hangcheck.deadlock = true; + + signaller = semaphore_waits_for(ring, &seqno); + if (signaller == NULL || signaller->hangcheck.deadlock) + return -1; + + /* cursory check for an unkickable deadlock */ + ctl = I915_READ_CTL(signaller); + if (ctl & RING_WAIT_SEMAPHORE && semaphore_passed(signaller) < 0) + return -1; + + return i915_seqno_passed(signaller->get_seqno(signaller, false), seqno); +} + +static void semaphore_clear_deadlocks(struct drm_i915_private *dev_priv) +{ + struct intel_ring_buffer *ring; + int i; + + for_each_ring(ring, dev_priv, i) + ring->hangcheck.deadlock = false; } -static bool ring_hung(struct intel_ring_buffer *ring) +static enum { wait, active, kick, hung } ring_stuck(struct intel_ring_buffer *ring, u32 acthd) { struct drm_device *dev = ring->dev; struct drm_i915_private *dev_priv = dev->dev_private; u32 tmp; + if (ring->hangcheck.acthd != acthd) + return active; + if (IS_GEN2(dev)) - return true; + return hung; /* Is the chip hanging on a WAIT_FOR_EVENT? * If so we can simply poke the RB_WAIT bit @@ -2371,19 +2402,24 @@ static bool ring_hung(struct intel_ring_buffer *ring) DRM_ERROR("Kicking stuck wait on %s\n", ring->name); I915_WRITE_CTL(ring, tmp); - return false; - } - - if (INTEL_INFO(dev)->gen >= 6 && - tmp & RING_WAIT_SEMAPHORE && - semaphore_passed(ring)) { - DRM_ERROR("Kicking stuck semaphore on %s\n", - ring->name); - I915_WRITE_CTL(ring, tmp); - return false; + return kick; + } + + if (INTEL_INFO(dev)->gen >= 6 && tmp & RING_WAIT_SEMAPHORE) { + switch (semaphore_passed(ring)) { + default: + return hung; + case 1: + DRM_ERROR("Kicking stuck semaphore on %s\n", + ring->name); + I915_WRITE_CTL(ring, tmp); + return kick; + case 0: + return wait; + } } - return true; + return hung; } /** @@ -2414,6 +2450,8 @@ void i915_hangcheck_elapsed(unsigned long data) u32 seqno, acthd; bool busy = true; + semaphore_clear_deadlocks(dev_priv); + seqno = ring->get_seqno(ring, false); acthd = intel_ring_get_active_head(ring); @@ -2430,17 +2468,36 @@ void i915_hangcheck_elapsed(unsigned long data) } else { int score; - stuck[i] = ring->hangcheck.acthd == acthd; - if (stuck[i]) { - /* Every time we kick the ring, add a - * small increment to the hangcheck - * score so that we can catch a - * batch that is repeatedly kicked. - */ - score = ring_hung(ring) ? HUNG : KICK; - } else + /* We always increment the hangcheck score + * if the ring is busy and still processing + * the same request, so that no single request + * can run indefinitely (such as a chain of + * batches). The only time we do not increment + * the hangcheck score on this ring, if this + * ring is in a legitimate wait for another + * ring. In that case the waiting ring is a + * victim and we want to be sure we catch the + * right culprit. Then every time we do kick + * the ring, add a small increment to the + * score so that we can catch a batch that is + * being repeatedly kicked and so responsible + * for stalling the machine. + */ + switch (ring_stuck(ring, acthd)) { + case wait: + score = 0; + break; + case active: score = BUSY; - + break; + case kick: + score = KICK; + break; + case hung: + score = HUNG; + stuck[i] = true; + break; + } ring->hangcheck.score += score; } } else { diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index efc403d1e3e0..a3e96103dbe5 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -38,6 +38,7 @@ struct intel_hw_status_page { #define I915_READ_SYNC_1(ring) I915_READ(RING_SYNC_1((ring)->mmio_base)) struct intel_ring_hangcheck { + bool deadlock; u32 seqno; u32 acthd; int score; -- cgit v1.2.3 From a43adf0747ecde0d211f29adcbebf067f92e9cbb Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 10 Jun 2013 11:20:22 +0100 Subject: drm/i915: Eliminate the addr/seqno from the hangcheck warning This is of no value to the developer reading the report, let alone the bamboozled user. Signed-off-by: Chris Wilson Acked-by: Mika Kuoppala Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 563abe79bb20..b26243f09c91 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -2515,12 +2515,10 @@ void i915_hangcheck_elapsed(unsigned long data) for_each_ring(ring, dev_priv, i) { if (ring->hangcheck.score > FIRE) { - rings_hung++; - DRM_ERROR("%s: %s on %s 0x%x\n", ring->name, + DRM_ERROR("%s on %s ring\n", stuck[i] ? "stuck" : "no progress", - stuck[i] ? "addr" : "seqno", - stuck[i] ? ring->hangcheck.acthd & HEAD_ADDR : - ring->hangcheck.seqno); + ring->name); + rings_hung++; } } -- cgit v1.2.3 From 45203a3b380cee28f570475c0d28c169f908c209 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 6 Jun 2013 08:43:22 -0700 Subject: net_sched: add 64bit rate estimators struct gnet_stats_rate_est contains u32 fields, so the bytes per second field can wrap at 34360Mbit. Add a new gnet_stats_rate_est64 structure to get 64bit bps/pps fields, and switch the kernel to use this structure natively. This structure is dumped to user space as a new attribute : TCA_STATS_RATE_EST64 Old tc command will now display the capped bps (to 34360Mbit), instead of wrapped values, and updated tc command will display correct information. Old tc command output, after patch : eric:~# tc -s -d qd sh dev lo qdisc pfifo 8001: root refcnt 2 limit 1000p Sent 80868245400 bytes 1978837 pkt (dropped 0, overlimits 0 requeues 0) rate 34360Mbit 189696pps backlog 0b 0p requeues 0 This patch carefully reorganizes "struct Qdisc" layout to get optimal performance on SMP. Signed-off-by: Eric Dumazet Cc: Ben Hutchings Signed-off-by: David S. Miller --- include/net/act_api.h | 2 +- include/net/gen_stats.h | 10 +++++----- include/net/netfilter/xt_rateest.h | 2 +- include/net/sch_generic.h | 13 +++++++------ include/uapi/linux/gen_stats.h | 11 +++++++++++ net/core/gen_estimator.c | 12 ++++++------ net/core/gen_stats.c | 22 +++++++++++++++++----- net/netfilter/xt_rateest.c | 2 +- net/sched/sch_cbq.c | 2 +- net/sched/sch_drr.c | 2 +- net/sched/sch_hfsc.c | 2 +- net/sched/sch_htb.c | 2 +- net/sched/sch_qfq.c | 2 +- 13 files changed, 54 insertions(+), 30 deletions(-) diff --git a/include/net/act_api.h b/include/net/act_api.h index 06ef7e926a66..b8ffac7b6bab 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h @@ -18,7 +18,7 @@ struct tcf_common { struct tcf_t tcfc_tm; struct gnet_stats_basic_packed tcfc_bstats; struct gnet_stats_queue tcfc_qstats; - struct gnet_stats_rate_est tcfc_rate_est; + struct gnet_stats_rate_est64 tcfc_rate_est; spinlock_t tcfc_lock; struct rcu_head tcfc_rcu; }; diff --git a/include/net/gen_stats.h b/include/net/gen_stats.h index a79b6cfb02a8..cf8439ba4d11 100644 --- a/include/net/gen_stats.h +++ b/include/net/gen_stats.h @@ -30,7 +30,7 @@ extern int gnet_stats_copy_basic(struct gnet_dump *d, struct gnet_stats_basic_packed *b); extern int gnet_stats_copy_rate_est(struct gnet_dump *d, const struct gnet_stats_basic_packed *b, - struct gnet_stats_rate_est *r); + struct gnet_stats_rate_est64 *r); extern int gnet_stats_copy_queue(struct gnet_dump *d, struct gnet_stats_queue *q); extern int gnet_stats_copy_app(struct gnet_dump *d, void *st, int len); @@ -38,13 +38,13 @@ extern int gnet_stats_copy_app(struct gnet_dump *d, void *st, int len); extern int gnet_stats_finish_copy(struct gnet_dump *d); extern int gen_new_estimator(struct gnet_stats_basic_packed *bstats, - struct gnet_stats_rate_est *rate_est, + struct gnet_stats_rate_est64 *rate_est, spinlock_t *stats_lock, struct nlattr *opt); extern void gen_kill_estimator(struct gnet_stats_basic_packed *bstats, - struct gnet_stats_rate_est *rate_est); + struct gnet_stats_rate_est64 *rate_est); extern int gen_replace_estimator(struct gnet_stats_basic_packed *bstats, - struct gnet_stats_rate_est *rate_est, + struct gnet_stats_rate_est64 *rate_est, spinlock_t *stats_lock, struct nlattr *opt); extern bool gen_estimator_active(const struct gnet_stats_basic_packed *bstats, - const struct gnet_stats_rate_est *rate_est); + const struct gnet_stats_rate_est64 *rate_est); #endif diff --git a/include/net/netfilter/xt_rateest.h b/include/net/netfilter/xt_rateest.h index 5a2978d1cb22..495c71f66e7e 100644 --- a/include/net/netfilter/xt_rateest.h +++ b/include/net/netfilter/xt_rateest.h @@ -6,7 +6,7 @@ struct xt_rateest { struct gnet_stats_basic_packed bstats; spinlock_t lock; /* keep rstats and lock on same cache line to speedup xt_rateest_mt() */ - struct gnet_stats_rate_est rstats; + struct gnet_stats_rate_est64 rstats; /* following fields not accessed in hot path */ struct hlist_node list; diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index e7f4e21cc3e1..df5676029827 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -58,14 +58,12 @@ struct Qdisc { * multiqueue device. */ #define TCQ_F_WARN_NONWC (1 << 16) - int padded; + u32 limit; const struct Qdisc_ops *ops; struct qdisc_size_table __rcu *stab; struct list_head list; u32 handle; u32 parent; - atomic_t refcnt; - struct gnet_stats_rate_est rate_est; int (*reshape_fail)(struct sk_buff *skb, struct Qdisc *q); @@ -76,8 +74,9 @@ struct Qdisc { */ struct Qdisc *__parent; struct netdev_queue *dev_queue; - struct Qdisc *next_sched; + struct gnet_stats_rate_est64 rate_est; + struct Qdisc *next_sched; struct sk_buff *gso_skb; /* * For performance sake on SMP, we put highly modified fields at the end @@ -88,8 +87,10 @@ struct Qdisc { unsigned int __state; struct gnet_stats_queue qstats; struct rcu_head rcu_head; - spinlock_t busylock; - u32 limit; + int padded; + atomic_t refcnt; + + spinlock_t busylock ____cacheline_aligned_in_smp; }; static inline bool qdisc_is_running(const struct Qdisc *qdisc) diff --git a/include/uapi/linux/gen_stats.h b/include/uapi/linux/gen_stats.h index 552c8a0a12d1..6487317ea619 100644 --- a/include/uapi/linux/gen_stats.h +++ b/include/uapi/linux/gen_stats.h @@ -9,6 +9,7 @@ enum { TCA_STATS_RATE_EST, TCA_STATS_QUEUE, TCA_STATS_APP, + TCA_STATS_RATE_EST64, __TCA_STATS_MAX, }; #define TCA_STATS_MAX (__TCA_STATS_MAX - 1) @@ -37,6 +38,16 @@ struct gnet_stats_rate_est { __u32 pps; }; +/** + * struct gnet_stats_rate_est64 - rate estimator + * @bps: current byte rate + * @pps: current packet rate + */ +struct gnet_stats_rate_est64 { + __u64 bps; + __u64 pps; +}; + /** * struct gnet_stats_queue - queuing statistics * @qlen: queue length diff --git a/net/core/gen_estimator.c b/net/core/gen_estimator.c index d9d198aa9fed..6b5b6e7013ca 100644 --- a/net/core/gen_estimator.c +++ b/net/core/gen_estimator.c @@ -82,7 +82,7 @@ struct gen_estimator { struct list_head list; struct gnet_stats_basic_packed *bstats; - struct gnet_stats_rate_est *rate_est; + struct gnet_stats_rate_est64 *rate_est; spinlock_t *stats_lock; int ewma_log; u64 last_bytes; @@ -167,7 +167,7 @@ static void gen_add_node(struct gen_estimator *est) static struct gen_estimator *gen_find_node(const struct gnet_stats_basic_packed *bstats, - const struct gnet_stats_rate_est *rate_est) + const struct gnet_stats_rate_est64 *rate_est) { struct rb_node *p = est_root.rb_node; @@ -203,7 +203,7 @@ struct gen_estimator *gen_find_node(const struct gnet_stats_basic_packed *bstats * */ int gen_new_estimator(struct gnet_stats_basic_packed *bstats, - struct gnet_stats_rate_est *rate_est, + struct gnet_stats_rate_est64 *rate_est, spinlock_t *stats_lock, struct nlattr *opt) { @@ -258,7 +258,7 @@ EXPORT_SYMBOL(gen_new_estimator); * Note : Caller should respect an RCU grace period before freeing stats_lock */ void gen_kill_estimator(struct gnet_stats_basic_packed *bstats, - struct gnet_stats_rate_est *rate_est) + struct gnet_stats_rate_est64 *rate_est) { struct gen_estimator *e; @@ -290,7 +290,7 @@ EXPORT_SYMBOL(gen_kill_estimator); * Returns 0 on success or a negative error code. */ int gen_replace_estimator(struct gnet_stats_basic_packed *bstats, - struct gnet_stats_rate_est *rate_est, + struct gnet_stats_rate_est64 *rate_est, spinlock_t *stats_lock, struct nlattr *opt) { gen_kill_estimator(bstats, rate_est); @@ -306,7 +306,7 @@ EXPORT_SYMBOL(gen_replace_estimator); * Returns true if estimator is active, and false if not. */ bool gen_estimator_active(const struct gnet_stats_basic_packed *bstats, - const struct gnet_stats_rate_est *rate_est) + const struct gnet_stats_rate_est64 *rate_est) { bool res; diff --git a/net/core/gen_stats.c b/net/core/gen_stats.c index ddedf211e588..9d3d9e78397b 100644 --- a/net/core/gen_stats.c +++ b/net/core/gen_stats.c @@ -143,18 +143,30 @@ EXPORT_SYMBOL(gnet_stats_copy_basic); int gnet_stats_copy_rate_est(struct gnet_dump *d, const struct gnet_stats_basic_packed *b, - struct gnet_stats_rate_est *r) + struct gnet_stats_rate_est64 *r) { + struct gnet_stats_rate_est est; + int res; + if (b && !gen_estimator_active(b, r)) return 0; + est.bps = min_t(u64, UINT_MAX, r->bps); + /* we have some time before reaching 2^32 packets per second */ + est.pps = r->pps; + if (d->compat_tc_stats) { - d->tc_stats.bps = r->bps; - d->tc_stats.pps = r->pps; + d->tc_stats.bps = est.bps; + d->tc_stats.pps = est.pps; } - if (d->tail) - return gnet_stats_copy(d, TCA_STATS_RATE_EST, r, sizeof(*r)); + if (d->tail) { + res = gnet_stats_copy(d, TCA_STATS_RATE_EST, &est, sizeof(est)); + if (res < 0 || est.bps == r->bps) + return res; + /* emit 64bit stats only if needed */ + return gnet_stats_copy(d, TCA_STATS_RATE_EST64, r, sizeof(*r)); + } return 0; } diff --git a/net/netfilter/xt_rateest.c b/net/netfilter/xt_rateest.c index ed0db15ab00e..7720b036d76a 100644 --- a/net/netfilter/xt_rateest.c +++ b/net/netfilter/xt_rateest.c @@ -18,7 +18,7 @@ static bool xt_rateest_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_rateest_match_info *info = par->matchinfo; - struct gnet_stats_rate_est *r; + struct gnet_stats_rate_est64 *r; u_int32_t bps1, bps2, pps1, pps2; bool ret = true; diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c index 1bc210ffcba2..71a568862557 100644 --- a/net/sched/sch_cbq.c +++ b/net/sched/sch_cbq.c @@ -130,7 +130,7 @@ struct cbq_class { psched_time_t penalized; struct gnet_stats_basic_packed bstats; struct gnet_stats_queue qstats; - struct gnet_stats_rate_est rate_est; + struct gnet_stats_rate_est64 rate_est; struct tc_cbq_xstats xstats; struct tcf_proto *filter_list; diff --git a/net/sched/sch_drr.c b/net/sched/sch_drr.c index 759b308d1a8d..8302717ea303 100644 --- a/net/sched/sch_drr.c +++ b/net/sched/sch_drr.c @@ -25,7 +25,7 @@ struct drr_class { struct gnet_stats_basic_packed bstats; struct gnet_stats_queue qstats; - struct gnet_stats_rate_est rate_est; + struct gnet_stats_rate_est64 rate_est; struct list_head alist; struct Qdisc *qdisc; diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c index 9facea03faeb..c4075610502c 100644 --- a/net/sched/sch_hfsc.c +++ b/net/sched/sch_hfsc.c @@ -114,7 +114,7 @@ struct hfsc_class { struct gnet_stats_basic_packed bstats; struct gnet_stats_queue qstats; - struct gnet_stats_rate_est rate_est; + struct gnet_stats_rate_est64 rate_est; unsigned int level; /* class level in hierarchy */ struct tcf_proto *filter_list; /* filter list */ unsigned int filter_cnt; /* filter count */ diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index adaedd79389c..162fb800754c 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -78,7 +78,7 @@ struct htb_class { /* general class parameters */ struct gnet_stats_basic_packed bstats; struct gnet_stats_queue qstats; - struct gnet_stats_rate_est rate_est; + struct gnet_stats_rate_est64 rate_est; struct tc_htb_xstats xstats; /* our special stats */ int refcnt; /* usage count of this class */ diff --git a/net/sched/sch_qfq.c b/net/sched/sch_qfq.c index d51852bba01c..7c195d972bf0 100644 --- a/net/sched/sch_qfq.c +++ b/net/sched/sch_qfq.c @@ -138,7 +138,7 @@ struct qfq_class { struct gnet_stats_basic_packed bstats; struct gnet_stats_queue qstats; - struct gnet_stats_rate_est rate_est; + struct gnet_stats_rate_est64 rate_est; struct Qdisc *qdisc; struct list_head alist; /* Link for active-classes list. */ struct qfq_aggregate *agg; /* Parent aggregate. */ -- cgit v1.2.3 From ecccd072b07e7fd09c54d0f86f9374e2645cde97 Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Mon, 10 Jun 2013 13:17:21 -0700 Subject: mac80211: fix mesh deadlock The patch "cfg80211/mac80211: use cfg80211 wdev mutex in mac80211" introduced several deadlocks by converting the ifmsh->mtx to wdev->mtx. Solve these by: 1. drop the cancel_work_sync() in ieee80211_stop_mesh(). Instead make the mesh work conditional on whether the mesh is running or not. 2. lock the mesh work with sdata_lock() to protect beacon updates and prevent races with wdev->mesh_id_len or cfg80211. Signed-off-by: Thomas Pedersen Signed-off-by: Johannes Berg --- net/mac80211/mesh.c | 29 +++++++++++++++++------------ net/mac80211/mesh_plink.c | 7 +------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 73a597bad6e0..d5faf91632c1 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -579,9 +579,7 @@ static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata) mesh_path_expire(sdata); changed = mesh_accept_plinks_update(sdata); - sdata_lock(sdata); ieee80211_mbss_info_change_notify(sdata, changed); - sdata_unlock(sdata); mod_timer(&ifmsh->housekeeping_timer, round_jiffies(jiffies + @@ -788,12 +786,10 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata) sdata->vif.bss_conf.enable_beacon = false; clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); - sdata_lock(sdata); bcn = rcu_dereference_protected(ifmsh->beacon, lockdep_is_held(&sdata->wdev.mtx)); rcu_assign_pointer(ifmsh->beacon, NULL); kfree_rcu(bcn, rcu_head); - sdata_unlock(sdata); /* flush STAs and mpaths on this iface */ sta_info_flush(sdata); @@ -806,14 +802,6 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata) del_timer_sync(&sdata->u.mesh.housekeeping_timer); del_timer_sync(&sdata->u.mesh.mesh_path_root_timer); del_timer_sync(&sdata->u.mesh.mesh_path_timer); - /* - * If the timer fired while we waited for it, it will have - * requeued the work. Now the work will be running again - * but will not rearm the timer again because it checks - * whether the interface is running, which, at this point, - * it no longer is. - */ - cancel_work_sync(&sdata->work); local->fif_other_bss--; atomic_dec(&local->iff_allmultis); @@ -954,6 +942,12 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt; u16 stype; + sdata_lock(sdata); + + /* mesh already went down */ + if (!sdata->wdev.mesh_id_len) + goto out; + rx_status = IEEE80211_SKB_RXCB(skb); mgmt = (struct ieee80211_mgmt *) skb->data; stype = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE; @@ -971,12 +965,20 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, ieee80211_mesh_rx_mgmt_action(sdata, mgmt, skb->len, rx_status); break; } +out: + sdata_unlock(sdata); } void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + sdata_lock(sdata); + + /* mesh already went down */ + if (!sdata->wdev.mesh_id_len) + goto out; + if (ifmsh->preq_queue_len && time_after(jiffies, ifmsh->last_preq + msecs_to_jiffies(ifmsh->mshcfg.dot11MeshHWMPpreqMinInterval))) @@ -996,6 +998,9 @@ void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata) if (test_and_clear_bit(MESH_WORK_DRIFT_ADJUST, &ifmsh->wrkq_flags)) mesh_sync_adjust_tbtt(sdata); + +out: + sdata_unlock(sdata); } void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local) diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 6c4da99bc4fb..09bebed99416 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -517,9 +517,7 @@ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata, ieee80211_mps_frame_release(sta, elems); out: rcu_read_unlock(); - sdata_lock(sdata); ieee80211_mbss_info_change_notify(sdata, changed); - sdata_unlock(sdata); } static void mesh_plink_timer(unsigned long data) @@ -1070,9 +1068,6 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, rcu_read_unlock(); - if (changed) { - sdata_lock(sdata); + if (changed) ieee80211_mbss_info_change_notify(sdata, changed); - sdata_unlock(sdata); - } } -- cgit v1.2.3 From 82467a5a885ddd9f80309682159da8db510e7832 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Fri, 7 Jun 2013 21:53:09 +0000 Subject: cpuidle: simplify multiple driver support Commit bf4d1b5 (cpuidle: support multiple drivers) introduced support for using multiple cpuidle drivers at the same time. It added a couple of new APIs to register the driver per CPU, but that led to some unnecessary code complexity related to the kernel config options deciding whether or not the multiple driver support is enabled. The code has to work as it did before when the multiple driver support is not enabled and the multiple driver support has to be compatible with the previously existing API. Remove the new API, not used by any driver in the tree yet (but needed for the HMP cpuidle drivers that will be submitted soon), and add a new cpumask pointer to the cpuidle driver structure that will point to the mask of CPUs handled by the given driver. That will allow the cpuidle_[un]register_driver() API to be used for the multiple driver support along with the cpuidle_[un]register() functions added recently. [rjw: Changelog] Signed-off-by: Daniel Lezcano Signed-off-by: Rafael J. Wysocki --- drivers/cpuidle/cpuidle.c | 4 +- drivers/cpuidle/driver.c | 192 +++++++++++++++++++--------------------------- include/linux/cpuidle.h | 6 +- 3 files changed, 84 insertions(+), 118 deletions(-) diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index c3a93fece819..fdc432f18022 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -466,7 +466,7 @@ void cpuidle_unregister(struct cpuidle_driver *drv) int cpu; struct cpuidle_device *device; - for_each_possible_cpu(cpu) { + for_each_cpu(cpu, drv->cpumask) { device = &per_cpu(cpuidle_dev, cpu); cpuidle_unregister_device(device); } @@ -498,7 +498,7 @@ int cpuidle_register(struct cpuidle_driver *drv, return ret; } - for_each_possible_cpu(cpu) { + for_each_cpu(cpu, drv->cpumask) { device = &per_cpu(cpuidle_dev, cpu); device->cpu = cpu; diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c index 8dfaaae94444..e75fa5472a91 100644 --- a/drivers/cpuidle/driver.c +++ b/drivers/cpuidle/driver.c @@ -18,167 +18,140 @@ DEFINE_SPINLOCK(cpuidle_driver_lock); -static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu); -static struct cpuidle_driver * __cpuidle_get_cpu_driver(int cpu); +#ifdef CONFIG_CPU_IDLE_MULTIPLE_DRIVERS -static void cpuidle_setup_broadcast_timer(void *arg) +static DEFINE_PER_CPU(struct cpuidle_driver *, cpuidle_drivers); + +static struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu) { - int cpu = smp_processor_id(); - clockevents_notify((long)(arg), &cpu); + return per_cpu(cpuidle_drivers, cpu); } -static void __cpuidle_driver_init(struct cpuidle_driver *drv, int cpu) +static inline void __cpuidle_unset_driver(struct cpuidle_driver *drv) { - int i; - - drv->refcnt = 0; + int cpu; - for (i = drv->state_count - 1; i >= 0 ; i--) { + for_each_cpu(cpu, drv->cpumask) { - if (!(drv->states[i].flags & CPUIDLE_FLAG_TIMER_STOP)) + if (drv != __cpuidle_get_cpu_driver(cpu)) continue; - drv->bctimer = 1; - on_each_cpu_mask(get_cpu_mask(cpu), cpuidle_setup_broadcast_timer, - (void *)CLOCK_EVT_NOTIFY_BROADCAST_ON, 1); - break; + per_cpu(cpuidle_drivers, cpu) = NULL; } } -static int __cpuidle_register_driver(struct cpuidle_driver *drv, int cpu) +static inline int __cpuidle_set_driver(struct cpuidle_driver *drv) { - if (!drv || !drv->state_count) - return -EINVAL; - - if (cpuidle_disabled()) - return -ENODEV; + int cpu; - if (__cpuidle_get_cpu_driver(cpu)) - return -EBUSY; + for_each_cpu(cpu, drv->cpumask) { - __cpuidle_driver_init(drv, cpu); + if (__cpuidle_get_cpu_driver(cpu)) { + __cpuidle_unset_driver(drv); + return -EBUSY; + } - __cpuidle_set_cpu_driver(drv, cpu); + per_cpu(cpuidle_drivers, cpu) = drv; + } return 0; } -static void __cpuidle_unregister_driver(struct cpuidle_driver *drv, int cpu) -{ - if (drv != __cpuidle_get_cpu_driver(cpu)) - return; +#else - if (!WARN_ON(drv->refcnt > 0)) - __cpuidle_set_cpu_driver(NULL, cpu); +static struct cpuidle_driver *cpuidle_curr_driver; - if (drv->bctimer) { - drv->bctimer = 0; - on_each_cpu_mask(get_cpu_mask(cpu), cpuidle_setup_broadcast_timer, - (void *)CLOCK_EVT_NOTIFY_BROADCAST_OFF, 1); - } +static inline struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu) +{ + return cpuidle_curr_driver; } -#ifdef CONFIG_CPU_IDLE_MULTIPLE_DRIVERS +static inline int __cpuidle_set_driver(struct cpuidle_driver *drv) +{ + if (cpuidle_curr_driver) + return -EBUSY; -static DEFINE_PER_CPU(struct cpuidle_driver *, cpuidle_drivers); + cpuidle_curr_driver = drv; -static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu) -{ - per_cpu(cpuidle_drivers, cpu) = drv; + return 0; } -static struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu) +static inline void __cpuidle_unset_driver(struct cpuidle_driver *drv) { - return per_cpu(cpuidle_drivers, cpu); + if (drv == cpuidle_curr_driver) + cpuidle_curr_driver = NULL; } -static void __cpuidle_unregister_all_cpu_driver(struct cpuidle_driver *drv) +#endif + +static void cpuidle_setup_broadcast_timer(void *arg) { - int cpu; - for_each_present_cpu(cpu) - __cpuidle_unregister_driver(drv, cpu); + int cpu = smp_processor_id(); + clockevents_notify((long)(arg), &cpu); } -static int __cpuidle_register_all_cpu_driver(struct cpuidle_driver *drv) +static int __cpuidle_driver_init(struct cpuidle_driver *drv) { - int ret = 0; - int i, cpu; + int i; - for_each_present_cpu(cpu) { - ret = __cpuidle_register_driver(drv, cpu); - if (ret) - break; - } + drv->refcnt = 0; - if (ret) - for_each_present_cpu(i) { - if (i == cpu) - break; - __cpuidle_unregister_driver(drv, i); - } + if (!drv->cpumask) + drv->cpumask = (struct cpumask *)cpu_possible_mask; + for (i = drv->state_count - 1; i >= 0 ; i--) { - return ret; + if (!(drv->states[i].flags & CPUIDLE_FLAG_TIMER_STOP)) + continue; + + drv->bctimer = 1; + break; + } + + return 0; } -int cpuidle_register_cpu_driver(struct cpuidle_driver *drv, int cpu) +static int __cpuidle_register_driver(struct cpuidle_driver *drv) { int ret; - spin_lock(&cpuidle_driver_lock); - ret = __cpuidle_register_driver(drv, cpu); - spin_unlock(&cpuidle_driver_lock); + if (!drv || !drv->state_count) + return -EINVAL; - return ret; -} + if (cpuidle_disabled()) + return -ENODEV; -void cpuidle_unregister_cpu_driver(struct cpuidle_driver *drv, int cpu) -{ - spin_lock(&cpuidle_driver_lock); - __cpuidle_unregister_driver(drv, cpu); - spin_unlock(&cpuidle_driver_lock); -} + ret = __cpuidle_driver_init(drv); + if (ret) + return ret; -/** - * cpuidle_register_driver - registers a driver - * @drv: the driver - */ -int cpuidle_register_driver(struct cpuidle_driver *drv) -{ - int ret; + ret = __cpuidle_set_driver(drv); + if (ret) + return ret; - spin_lock(&cpuidle_driver_lock); - ret = __cpuidle_register_all_cpu_driver(drv); - spin_unlock(&cpuidle_driver_lock); + if (drv->bctimer) + on_each_cpu_mask(drv->cpumask, cpuidle_setup_broadcast_timer, + (void *)CLOCK_EVT_NOTIFY_BROADCAST_ON, 1); - return ret; + return 0; } -EXPORT_SYMBOL_GPL(cpuidle_register_driver); /** * cpuidle_unregister_driver - unregisters a driver * @drv: the driver */ -void cpuidle_unregister_driver(struct cpuidle_driver *drv) +static void __cpuidle_unregister_driver(struct cpuidle_driver *drv) { - spin_lock(&cpuidle_driver_lock); - __cpuidle_unregister_all_cpu_driver(drv); - spin_unlock(&cpuidle_driver_lock); -} -EXPORT_SYMBOL_GPL(cpuidle_unregister_driver); - -#else - -static struct cpuidle_driver *cpuidle_curr_driver; + if (WARN_ON(drv->refcnt > 0)) + return; -static inline void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu) -{ - cpuidle_curr_driver = drv; -} + if (drv->bctimer) { + drv->bctimer = 0; + on_each_cpu_mask(drv->cpumask, cpuidle_setup_broadcast_timer, + (void *)CLOCK_EVT_NOTIFY_BROADCAST_OFF, 1); + } -static inline struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu) -{ - return cpuidle_curr_driver; + __cpuidle_unset_driver(drv); } /** @@ -187,13 +160,11 @@ static inline struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu) */ int cpuidle_register_driver(struct cpuidle_driver *drv) { - int ret, cpu; + int ret; - cpu = get_cpu(); spin_lock(&cpuidle_driver_lock); - ret = __cpuidle_register_driver(drv, cpu); + ret = __cpuidle_register_driver(drv); spin_unlock(&cpuidle_driver_lock); - put_cpu(); return ret; } @@ -205,16 +176,11 @@ EXPORT_SYMBOL_GPL(cpuidle_register_driver); */ void cpuidle_unregister_driver(struct cpuidle_driver *drv) { - int cpu; - - cpu = get_cpu(); spin_lock(&cpuidle_driver_lock); - __cpuidle_unregister_driver(drv, cpu); + __cpuidle_unregister_driver(drv); spin_unlock(&cpuidle_driver_lock); - put_cpu(); } EXPORT_SYMBOL_GPL(cpuidle_unregister_driver); -#endif /** * cpuidle_get_driver - return the current driver diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index 8f0406230a0a..0bc4b74668e9 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -111,6 +111,9 @@ struct cpuidle_driver { struct cpuidle_state states[CPUIDLE_STATE_MAX]; int state_count; int safe_state_index; + + /* the driver handles the cpus in cpumask */ + struct cpumask *cpumask; }; #ifdef CONFIG_CPU_IDLE @@ -135,9 +138,6 @@ extern void cpuidle_disable_device(struct cpuidle_device *dev); extern int cpuidle_play_dead(void); extern struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev); -extern int cpuidle_register_cpu_driver(struct cpuidle_driver *drv, int cpu); -extern void cpuidle_unregister_cpu_driver(struct cpuidle_driver *drv, int cpu); - #else static inline void disable_cpuidle(void) { } static inline int cpuidle_idle_call(void) { return -ENODEV; } -- cgit v1.2.3 From 5a49b4a527e5b72ae3a4f64f078759f83fbd98b5 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Fri, 7 Jun 2013 17:11:26 +0100 Subject: regulator: ab8500-ext: Register as a device in its own right Some platforms don't support the AB8500 external regulators, so instead of having a list of is_() calls prior to calling ab8500_ext_regulator_init() from ab8500_regulator_probe(), we can only register as a platform device on platforms which require them. It means we also have more control over them when booting with Device Tree. Signed-off-by: Lee Jones Signed-off-by: Mark Brown --- drivers/regulator/ab8500-ext.c | 33 +++++++++++++++++++++++++++++++-- drivers/regulator/ab8500.c | 17 +---------------- include/linux/regulator/ab8500.h | 4 ---- 3 files changed, 32 insertions(+), 22 deletions(-) diff --git a/drivers/regulator/ab8500-ext.c b/drivers/regulator/ab8500-ext.c index e4975bc61e81..95f495f51d00 100644 --- a/drivers/regulator/ab8500-ext.c +++ b/drivers/regulator/ab8500-ext.c @@ -333,7 +333,7 @@ static struct ab8500_ext_regulator_info }, }; -int ab8500_ext_regulator_init(struct platform_device *pdev) +int ab8500_ext_regulator_probe(struct platform_device *pdev) { struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent); struct ab8500_platform_data *ppdata; @@ -409,7 +409,7 @@ int ab8500_ext_regulator_init(struct platform_device *pdev) return 0; } -void ab8500_ext_regulator_exit(struct platform_device *pdev) +int ab8500_ext_regulator_remove(struct platform_device *pdev) { int i; @@ -422,7 +422,36 @@ void ab8500_ext_regulator_exit(struct platform_device *pdev) regulator_unregister(info->rdev); } + + return 0; +} + +static struct platform_driver ab8500_ext_regulator_driver = { + .probe = ab8500_ext_regulator_probe, + .remove = ab8500_ext_regulator_remove, + .driver = { + .name = "ab8500-ext-regulator", + .owner = THIS_MODULE, + }, +}; + +static int __init ab8500_ext_regulator_init(void) +{ + int ret; + + ret = platform_driver_register(&ab8500_ext_regulator_driver); + if (ret) + pr_err("Failed to register ab8500 ext regulator: %d\n", ret); + + return ret; +} +subsys_initcall(ab8500_ext_regulator_init); + +static void __exit ab8500_ext_regulator_exit(void) +{ + platform_driver_unregister(&ab8500_ext_regulator_driver); } +module_exit(ab8500_ext_regulator_exit); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Bengt Jonsson "); diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c index f6656b8c28b6..dfc790196b34 100644 --- a/drivers/regulator/ab8500.c +++ b/drivers/regulator/ab8500.c @@ -3156,22 +3156,12 @@ static int ab8500_regulator_probe(struct platform_device *pdev) return err; } - if (!is_ab8505(ab8500)) { - /* register external regulators (before Vaux1, 2 and 3) */ - err = ab8500_ext_regulator_init(pdev); - if (err) - return err; - } - /* register all regulators */ for (i = 0; i < abx500_regulator.info_size; i++) { err = ab8500_regulator_register(pdev, &pdata->regulator[i], i, NULL); - if (err < 0) { - if (!is_ab8505(ab8500)) - ab8500_ext_regulator_exit(pdev); + if (err < 0) return err; - } } return 0; @@ -3180,7 +3170,6 @@ static int ab8500_regulator_probe(struct platform_device *pdev) static int ab8500_regulator_remove(struct platform_device *pdev) { int i, err; - struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent); for (i = 0; i < abx500_regulator.info_size; i++) { struct ab8500_regulator_info *info = NULL; @@ -3192,10 +3181,6 @@ static int ab8500_regulator_remove(struct platform_device *pdev) regulator_unregister(info->regulator); } - /* remove external regulators (after Vaux1, 2 and 3) */ - if (!is_ab8505(ab8500)) - ab8500_ext_regulator_exit(pdev); - /* remove regulator debug */ err = ab8500_regulator_debug_exit(pdev); if (err) diff --git a/include/linux/regulator/ab8500.h b/include/linux/regulator/ab8500.h index 7c5ff0c55773..75307447cef9 100644 --- a/include/linux/regulator/ab8500.h +++ b/include/linux/regulator/ab8500.h @@ -336,8 +336,4 @@ static inline int ab8500_regulator_debug_exit(struct platform_device *pdev) } #endif -/* AB8500 external regulator functions. */ -int ab8500_ext_regulator_init(struct platform_device *pdev); -void ab8500_ext_regulator_exit(struct platform_device *pdev); - #endif -- cgit v1.2.3 From 30aa4b26c00f5c5ae6081f85e7adca47dcb24e29 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Fri, 7 Jun 2013 17:11:27 +0100 Subject: regulator: ab8500-ext: Enable for Device Tree Here we use the OF regulator match facility to collect and populate initialisation data from Device Tree if we're booting with it enabled. Signed-off-by: Lee Jones Signed-off-by: Mark Brown --- drivers/regulator/ab8500-ext.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/ab8500-ext.c b/drivers/regulator/ab8500-ext.c index 95f495f51d00..4137a2fe42c3 100644 --- a/drivers/regulator/ab8500-ext.c +++ b/drivers/regulator/ab8500-ext.c @@ -16,9 +16,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -333,18 +335,37 @@ static struct ab8500_ext_regulator_info }, }; +static struct of_regulator_match ab8500_ext_regulator_match[] = { + { .name = "ab8500_ext1", .driver_data = (void *) AB8500_EXT_SUPPLY1, }, + { .name = "ab8500_ext2", .driver_data = (void *) AB8500_EXT_SUPPLY2, }, + { .name = "ab8500_ext3", .driver_data = (void *) AB8500_EXT_SUPPLY3, }, +}; + int ab8500_ext_regulator_probe(struct platform_device *pdev) { struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent); struct ab8500_platform_data *ppdata; struct ab8500_regulator_platform_data *pdata; + struct device_node *np = pdev->dev.of_node; struct regulator_config config = { }; int i, err; + if (np) { + err = of_regulator_match(&pdev->dev, np, + ab8500_ext_regulator_match, + ARRAY_SIZE(ab8500_ext_regulator_match)); + if (err < 0) { + dev_err(&pdev->dev, + "Error parsing regulator init data: %d\n", err); + return err; + } + } + if (!ab8500) { dev_err(&pdev->dev, "null mfd parent\n"); return -EINVAL; } + ppdata = dev_get_platdata(ab8500->dev); if (!ppdata) { dev_err(&pdev->dev, "null parent pdata\n"); @@ -385,8 +406,11 @@ int ab8500_ext_regulator_probe(struct platform_device *pdev) pdata->ext_regulator[i].driver_data; config.dev = &pdev->dev; - config.init_data = &pdata->ext_regulator[i]; config.driver_data = info; + config.of_node = ab8500_ext_regulator_match[i].of_node; + config.init_data = (np) ? + ab8500_ext_regulator_match[i].init_data : + &pdata->ext_regulator[i]; /* register regulator with framework */ info->rdev = regulator_register(&info->desc, &config); -- cgit v1.2.3 From ce6f5ea3a7240c15e5775d5b9c5c57629943ca6c Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Fri, 7 Jun 2013 17:11:28 +0100 Subject: regulator: ab8500: Provide supply names for the AUX regulators On some platforms the AUX[1|2|3] regulators are supplied voltage by a separate regulator. For example on Snowball these are provided by the EXT3. If we list them here, we can supply voltage to them by simply listing them in as a consumer of EXT3 in the Snowball case. Signed-off-by: Lee Jones Signed-off-by: Mark Brown --- drivers/regulator/ab8500.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c index dfc790196b34..bb02108c95e1 100644 --- a/drivers/regulator/ab8500.c +++ b/drivers/regulator/ab8500.c @@ -719,6 +719,7 @@ static struct ab8500_regulator_info .n_voltages = ARRAY_SIZE(ldo_vauxn_voltages), .volt_table = ldo_vauxn_voltages, .enable_time = 200, + .supply_name = "vin", }, .load_lp_uA = 5000, .update_bank = 0x04, @@ -741,6 +742,7 @@ static struct ab8500_regulator_info .n_voltages = ARRAY_SIZE(ldo_vauxn_voltages), .volt_table = ldo_vauxn_voltages, .enable_time = 200, + .supply_name = "vin", }, .load_lp_uA = 5000, .update_bank = 0x04, @@ -763,6 +765,7 @@ static struct ab8500_regulator_info .n_voltages = ARRAY_SIZE(ldo_vaux3_voltages), .volt_table = ldo_vaux3_voltages, .enable_time = 450, + .supply_name = "vin", }, .load_lp_uA = 5000, .update_bank = 0x04, -- cgit v1.2.3 From a0a2af765543a96bdf188ec57dd201e708195302 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 3 Jun 2013 18:10:45 +0200 Subject: nl80211: add kernel-doc for NL80211_FEATURE_ACTIVE_MONITOR Signed-off-by: Felix Fietkau Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 5920715278c2..1f0019d4cce6 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -3579,6 +3579,10 @@ enum nl80211_ap_sme_features { * Peering Management entity which may be implemented by registering for * beacons or NL80211_CMD_NEW_PEER_CANDIDATE events. The mesh beacon is * still generated by the driver. + * @NL80211_FEATURE_ACTIVE_MONITOR: This driver supports an active monitor + * interface. An active monitor interface behaves like a normal monitor + * interface, but gets added to the driver. It ensures that incoming + * unicast packets directed at the configured interface address get ACKed. */ enum nl80211_feature_flags { NL80211_FEATURE_SK_TX_STATUS = 1 << 0, -- cgit v1.2.3 From 8e7c053853b7d299e8a2b8733659b0df8eee51f7 Mon Sep 17 00:00:00 2001 From: Colleen Twitty Date: Mon, 3 Jun 2013 09:53:39 -0700 Subject: {nl,cfg}80211: make peer link expiration time configurable If a STA has a peer that it hasn't seen any tx activity from for a certain length of time, the peer link is expired. This means the inactive STA is removed from the list of peers and that STA is not considered a peer again unless it re-peers. Previously, this inactivity time was always 30 minutes. Now, add it to the mesh configuration and allow it to be configured. Retain 30 minutes as a default value. Signed-off-by: Colleen Twitty Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 4 ++++ include/uapi/linux/nl80211.h | 5 +++++ net/wireless/mesh.c | 2 ++ net/wireless/nl80211.c | 8 +++++++- 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 855c76ccff38..31ca11672ca8 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1124,6 +1124,9 @@ struct bss_parameters { * setting for new peer links. * @dot11MeshAwakeWindowDuration: The duration in TUs the STA will remain awake * after transmitting its beacon. + * @plink_timeout: If no tx activity is seen from a STA we've established + * peering with for longer than this time (in seconds), then remove it + * from the STA's list of peers. Default is 30 minutes. */ struct mesh_config { u16 dot11MeshRetryTimeout; @@ -1153,6 +1156,7 @@ struct mesh_config { u16 dot11MeshHWMPconfirmationInterval; enum nl80211_mesh_power_mode power_mode; u16 dot11MeshAwakeWindowDuration; + u32 plink_timeout; }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 1f0019d4cce6..ca6facf4df0c 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2577,6 +2577,10 @@ enum nl80211_mesh_power_mode { * * @NL80211_MESHCONF_AWAKE_WINDOW: awake window duration (in TUs) * + * @NL80211_MESHCONF_PLINK_TIMEOUT: If no tx activity is seen from a STA we've + * established peering with for longer than this time (in seconds), then + * remove it from the STA's list of peers. Default is 30 minutes. + * * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use */ enum nl80211_meshconf_params { @@ -2608,6 +2612,7 @@ enum nl80211_meshconf_params { NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, NL80211_MESHCONF_POWER_MODE, NL80211_MESHCONF_AWAKE_WINDOW, + NL80211_MESHCONF_PLINK_TIMEOUT, /* keep last */ __NL80211_MESHCONF_ATTR_AFTER_LAST, diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 5dfb289ab761..0daaf72e1b81 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -18,6 +18,7 @@ #define MESH_PATH_TO_ROOT_TIMEOUT 6000 #define MESH_ROOT_INTERVAL 5000 #define MESH_ROOT_CONFIRMATION_INTERVAL 2000 +#define MESH_DEFAULT_PLINK_TIMEOUT 1800 /* timeout in seconds */ /* * Minimum interval between two consecutive PREQs originated by the same @@ -75,6 +76,7 @@ const struct mesh_config default_mesh_config = { .dot11MeshHWMPconfirmationInterval = MESH_ROOT_CONFIRMATION_INTERVAL, .power_mode = NL80211_MESH_POWER_ACTIVE, .dot11MeshAwakeWindowDuration = MESH_DEFAULT_AWAKE_WINDOW, + .plink_timeout = MESH_DEFAULT_PLINK_TIMEOUT, }; const struct mesh_setup default_mesh_setup = { diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 88e820b73674..8aa83c04d4eb 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4575,7 +4575,9 @@ static int nl80211_get_mesh_config(struct sk_buff *skb, nla_put_u32(msg, NL80211_MESHCONF_POWER_MODE, cur_params.power_mode) || nla_put_u16(msg, NL80211_MESHCONF_AWAKE_WINDOW, - cur_params.dot11MeshAwakeWindowDuration)) + cur_params.dot11MeshAwakeWindowDuration) || + nla_put_u32(msg, NL80211_MESHCONF_PLINK_TIMEOUT, + cur_params.plink_timeout)) goto nla_put_failure; nla_nest_end(msg, pinfoattr); genlmsg_end(msg, hdr); @@ -4616,6 +4618,7 @@ static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_A [NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL] = { .type = NLA_U16 }, [NL80211_MESHCONF_POWER_MODE] = { .type = NLA_U32 }, [NL80211_MESHCONF_AWAKE_WINDOW] = { .type = NLA_U16 }, + [NL80211_MESHCONF_PLINK_TIMEOUT] = { .type = NLA_U32 }, }; static const struct nla_policy @@ -4753,6 +4756,9 @@ do { \ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshAwakeWindowDuration, 0, 65535, mask, NL80211_MESHCONF_AWAKE_WINDOW, nla_get_u16); + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, plink_timeout, 1, 0xffffffff, + mask, NL80211_MESHCONF_PLINK_TIMEOUT, + nla_get_u32); if (mask_out) *mask_out = mask; -- cgit v1.2.3 From 66de671374f003467b5ef7c65ecbe1930480c8c9 Mon Sep 17 00:00:00 2001 From: Colleen Twitty Date: Mon, 3 Jun 2013 09:53:40 -0700 Subject: mac80211: expire mesh peers based on mesh configuration The time it takes to see the peer link expire may differ by a minute since sta_expire() is run once a minute as a mesh housekeeping task. Signed-off-by: Colleen Twitty Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 2 ++ net/mac80211/mesh.c | 2 +- net/mac80211/mesh.h | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 30622101d3b5..344a57968079 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1871,6 +1871,8 @@ static int ieee80211_update_mesh_config(struct wiphy *wiphy, if (_chg_mesh_attr(NL80211_MESHCONF_AWAKE_WINDOW, mask)) conf->dot11MeshAwakeWindowDuration = nconf->dot11MeshAwakeWindowDuration; + if (_chg_mesh_attr(NL80211_MESHCONF_PLINK_TIMEOUT, mask)) + conf->plink_timeout = nconf->plink_timeout; ieee80211_mbss_info_change_notify(sdata, BSS_CHANGED_BEACON); return 0; } diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index d5faf91632c1..4ee527f78677 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -575,7 +575,7 @@ static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata) struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; u32 changed; - ieee80211_sta_expire(sdata, IEEE80211_MESH_PEER_INACTIVITY_LIMIT); + ieee80211_sta_expire(sdata, ifmsh->mshcfg.plink_timeout * HZ); mesh_path_expire(sdata); changed = mesh_accept_plinks_update(sdata); diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 8b4d9a3e9eee..01a28bca6e9b 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -188,7 +188,6 @@ struct mesh_rmc { u32 idx_mask; }; -#define IEEE80211_MESH_PEER_INACTIVITY_LIMIT (1800 * HZ) #define IEEE80211_MESH_HOUSEKEEPING_INTERVAL (60 * HZ) #define MESH_PATH_EXPIRE (600 * HZ) -- cgit v1.2.3 From 6d19cb93d60a5403753756c502699751116c954c Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Fri, 7 Jun 2013 21:53:10 +0000 Subject: cpuidle: Comment the driver's framework code Add kerneldoc (and other) comments to the cpuidle driver's framework code. Signed-off-by: Daniel Lezcano Signed-off-by: Rafael J. Wysocki --- drivers/cpuidle/driver.c | 132 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 126 insertions(+), 6 deletions(-) diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c index e75fa5472a91..3ac499d5a207 100644 --- a/drivers/cpuidle/driver.c +++ b/drivers/cpuidle/driver.c @@ -22,11 +22,26 @@ DEFINE_SPINLOCK(cpuidle_driver_lock); static DEFINE_PER_CPU(struct cpuidle_driver *, cpuidle_drivers); +/** + * __cpuidle_get_cpu_driver - return the cpuidle driver tied to a CPU. + * @cpu: the CPU handled by the driver + * + * Returns a pointer to struct cpuidle_driver or NULL if no driver has been + * registered for @cpu. + */ static struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu) { return per_cpu(cpuidle_drivers, cpu); } +/** + * __cpuidle_unset_driver - unset per CPU driver variables. + * @drv: a valid pointer to a struct cpuidle_driver + * + * For each CPU in the driver's CPU mask, unset the registered driver per CPU + * variable. If @drv is different from the registered driver, the corresponding + * variable is not cleared. + */ static inline void __cpuidle_unset_driver(struct cpuidle_driver *drv) { int cpu; @@ -40,6 +55,15 @@ static inline void __cpuidle_unset_driver(struct cpuidle_driver *drv) } } +/** + * __cpuidle_set_driver - set per CPU driver variables the the given driver. + * @drv: a valid pointer to a struct cpuidle_driver + * + * For each CPU in the driver's cpumask, unset the registered driver per CPU + * to @drv. + * + * Returns 0 on success, -EBUSY if the CPUs have driver(s) already. + */ static inline int __cpuidle_set_driver(struct cpuidle_driver *drv) { int cpu; @@ -61,11 +85,24 @@ static inline int __cpuidle_set_driver(struct cpuidle_driver *drv) static struct cpuidle_driver *cpuidle_curr_driver; +/** + * __cpuidle_get_cpu_driver - return the global cpuidle driver pointer. + * @cpu: ignored without the multiple driver support + * + * Return a pointer to a struct cpuidle_driver object or NULL if no driver was + * previously registered. + */ static inline struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu) { return cpuidle_curr_driver; } +/** + * __cpuidle_set_driver - assign the global cpuidle driver variable. + * @drv: pointer to a struct cpuidle_driver object + * + * Returns 0 on success, -EBUSY if the driver is already registered. + */ static inline int __cpuidle_set_driver(struct cpuidle_driver *drv) { if (cpuidle_curr_driver) @@ -76,6 +113,13 @@ static inline int __cpuidle_set_driver(struct cpuidle_driver *drv) return 0; } +/** + * __cpuidle_unset_driver - unset the global cpuidle driver variable. + * @drv: a pointer to a struct cpuidle_driver + * + * Reset the global cpuidle variable to NULL. If @drv does not match the + * registered driver, do nothing. + */ static inline void __cpuidle_unset_driver(struct cpuidle_driver *drv) { if (drv == cpuidle_curr_driver) @@ -84,21 +128,49 @@ static inline void __cpuidle_unset_driver(struct cpuidle_driver *drv) #endif +/** + * cpuidle_setup_broadcast_timer - enable/disable the broadcast timer + * @arg: a void pointer used to match the SMP cross call API + * + * @arg is used as a value of type 'long' with on of the two values: + * - CLOCK_EVT_NOTIFY_BROADCAST_ON + * - CLOCK_EVT_NOTIFY_BROADCAST_OFF + * + * Set the broadcast timer notification for the current CPU. This function + * is executed per CPU by an SMP cross call. It not supposed to be called + * directly. + */ static void cpuidle_setup_broadcast_timer(void *arg) { int cpu = smp_processor_id(); clockevents_notify((long)(arg), &cpu); } +/** + * __cpuidle_driver_init - initialize the driver's internal data + * @drv: a valid pointer to a struct cpuidle_driver + * + * Returns 0 on success, a negative error code otherwise. + */ static int __cpuidle_driver_init(struct cpuidle_driver *drv) { int i; drv->refcnt = 0; + /* + * Use all possible CPUs as the default, because if the kernel boots + * with some CPUs offline and then we online one of them, the CPU + * notifier has to know which driver to assign. + */ if (!drv->cpumask) drv->cpumask = (struct cpumask *)cpu_possible_mask; + /* + * Look for the timer stop flag in the different states, so that we know + * if the broadcast timer has to be set up. The loop is in the reverse + * order, because usually on of the the deeper states has this flag set. + */ for (i = drv->state_count - 1; i >= 0 ; i--) { if (!(drv->states[i].flags & CPUIDLE_FLAG_TIMER_STOP)) @@ -111,6 +183,19 @@ static int __cpuidle_driver_init(struct cpuidle_driver *drv) return 0; } +/** + * __cpuidle_register_driver: register the driver + * @drv: a valid pointer to a struct cpuidle_driver + * + * Do some sanity checks, initialize the driver, assign the driver to the + * global cpuidle driver variable(s) and set up the broadcast timer if the + * cpuidle driver has some states that shut down the local timer. + * + * Returns 0 on success, a negative error code otherwise: + * * -EINVAL if the driver pointer is NULL or no idle states are available + * * -ENODEV if the cpuidle framework is disabled + * * -EBUSY if the driver is already assigned to the global variable(s) + */ static int __cpuidle_register_driver(struct cpuidle_driver *drv) { int ret; @@ -137,8 +222,13 @@ static int __cpuidle_register_driver(struct cpuidle_driver *drv) } /** - * cpuidle_unregister_driver - unregisters a driver - * @drv: the driver + * __cpuidle_unregister_driver - unregister the driver + * @drv: a valid pointer to a struct cpuidle_driver + * + * Check if the driver is no longer in use, reset the global cpuidle driver + * variable(s) and disable the timer broadcast notification mechanism if it was + * in use. + * */ static void __cpuidle_unregister_driver(struct cpuidle_driver *drv) { @@ -156,7 +246,13 @@ static void __cpuidle_unregister_driver(struct cpuidle_driver *drv) /** * cpuidle_register_driver - registers a driver - * @drv: the driver + * @drv: a pointer to a valid struct cpuidle_driver + * + * Register the driver under a lock to prevent concurrent attempts to + * [un]register the driver from occuring at the same time. + * + * Returns 0 on success, a negative error code (returned by + * __cpuidle_register_driver()) otherwise. */ int cpuidle_register_driver(struct cpuidle_driver *drv) { @@ -172,7 +268,11 @@ EXPORT_SYMBOL_GPL(cpuidle_register_driver); /** * cpuidle_unregister_driver - unregisters a driver - * @drv: the driver + * @drv: a pointer to a valid struct cpuidle_driver + * + * Unregisters the cpuidle driver under a lock to prevent concurrent attempts + * to [un]register the driver from occuring at the same time. @drv has to + * match the currently registered driver. */ void cpuidle_unregister_driver(struct cpuidle_driver *drv) { @@ -183,7 +283,9 @@ void cpuidle_unregister_driver(struct cpuidle_driver *drv) EXPORT_SYMBOL_GPL(cpuidle_unregister_driver); /** - * cpuidle_get_driver - return the current driver + * cpuidle_get_driver - return the driver tied to the current CPU. + * + * Returns a struct cpuidle_driver pointer, or NULL if no driver is registered. */ struct cpuidle_driver *cpuidle_get_driver(void) { @@ -199,7 +301,11 @@ struct cpuidle_driver *cpuidle_get_driver(void) EXPORT_SYMBOL_GPL(cpuidle_get_driver); /** - * cpuidle_get_cpu_driver - return the driver tied with a cpu + * cpuidle_get_cpu_driver - return the driver registered for a CPU. + * @dev: a valid pointer to a struct cpuidle_device + * + * Returns a struct cpuidle_driver pointer, or NULL if no driver is registered + * for the CPU associated with @dev. */ struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev) { @@ -210,6 +316,14 @@ struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev) } EXPORT_SYMBOL_GPL(cpuidle_get_cpu_driver); +/** + * cpuidle_driver_ref - get a reference to the driver. + * + * Increment the reference counter of the cpuidle driver associated with + * the current CPU. + * + * Returns a pointer to the driver, or NULL if the current CPU has no driver. + */ struct cpuidle_driver *cpuidle_driver_ref(void) { struct cpuidle_driver *drv; @@ -223,6 +337,12 @@ struct cpuidle_driver *cpuidle_driver_ref(void) return drv; } +/** + * cpuidle_driver_unref - puts down the refcount for the driver + * + * Decrement the reference counter of the cpuidle driver associated with + * the current CPU. + */ void cpuidle_driver_unref(void) { struct cpuidle_driver *drv = cpuidle_get_driver(); -- cgit v1.2.3 From b39b0981b0811943d724915a8a0150d6ac5110e0 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Tue, 11 Jun 2013 08:09:45 +0000 Subject: cpuidle: Fix ARCH_NEEDS_CPU_IDLE_COUPLED dependency warning Before commit d6f346f (cpuidle: improve governor Kconfig options), the CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED option didn't depend on CONFIG_CPU_IDLE but now it has been moved under the CPU_IDLE menuconfig. That raises the following warnings: warning: (ARCH_OMAP4 && ARCH_TEGRA_2x_SOC) selects ARCH_NEEDS_CPU_IDLE_COUPLED which has unmet direct dependencies (CPU_IDLE) warning: (ARCH_OMAP4 && ARCH_TEGRA_2x_SOC) selects ARCH_NEEDS_CPU_IDLE_COUPLED which has unmet direct dependencies (CPU_IDLE) because the tegra2 and omap4 Kconfig files select this option without checking if CPU_IDLE is set. Fix that by moving ARCH_NEEDS_CPU_IDLE_COUPLED outside of CPU_IDLE. [rjw: Changelog] Signed-off-by: Daniel Lezcano Signed-off-by: Rafael J. Wysocki --- drivers/cpuidle/Kconfig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig index a7d2e833f70c..81de5d96749b 100644 --- a/drivers/cpuidle/Kconfig +++ b/drivers/cpuidle/Kconfig @@ -29,9 +29,6 @@ config CPU_IDLE_GOV_MENU bool "Menu governor (for tickless system)" default y -config ARCH_NEEDS_CPU_IDLE_COUPLED - def_bool n - config CPU_IDLE_CALXEDA bool "CPU Idle Driver for Calxeda processors" depends on ARCH_HIGHBANK @@ -45,3 +42,6 @@ config CPU_IDLE_ZYNQ Select this to enable cpuidle on Xilinx Zynq processors. endif + +config ARCH_NEEDS_CPU_IDLE_COUPLED + def_bool n -- cgit v1.2.3 From ffb3cf3000aa12facdccbdfcb10bfebda7199209 Mon Sep 17 00:00:00 2001 From: Ashok Nagarajan Date: Mon, 3 Jun 2013 10:33:36 -0700 Subject: {nl,mac,cfg}80211: Allow user to configure basic rates for mesh Currently mesh uses mandatory rates as the default basic rates. Allow basic rates to be configured during mesh join. Basic rates are applied only if channel is also provided with mesh join command. Signed-off-by: Ashok Nagarajan [some whitespace fixes, refuse basic rates w/o channel] Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 ++ net/mac80211/cfg.c | 1 + net/mac80211/mesh.c | 4 ---- net/wireless/mesh.c | 10 ++++++++++ net/wireless/nl80211.c | 17 +++++++++++++++++ 5 files changed, 30 insertions(+), 4 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 31ca11672ca8..6a43c34ce96f 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1176,6 +1176,7 @@ struct mesh_config { * @dtim_period: DTIM period to use * @beacon_interval: beacon interval to use * @mcast_rate: multicat rate for Mesh Node [6Mbps is the default for 802.11a] + * @basic_rates: basic rates to use when creating the mesh * * These parameters are fixed when the mesh is created. */ @@ -1195,6 +1196,7 @@ struct mesh_setup { u8 dtim_period; u16 beacon_interval; int mcast_rate[IEEE80211_NUM_BANDS]; + u32 basic_rates; }; /** diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 344a57968079..cd6f35f6e714 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1759,6 +1759,7 @@ static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh, /* mcast rate setting in Mesh Node */ memcpy(sdata->vif.bss_conf.mcast_rate, setup->mcast_rate, sizeof(setup->mcast_rate)); + sdata->vif.bss_conf.basic_rates = setup->basic_rates; sdata->vif.bss_conf.beacon_int = setup->beacon_interval; sdata->vif.bss_conf.dtim_period = setup->dtim_period; diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 4ee527f78677..6c33af482df4 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -738,9 +738,6 @@ int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) BSS_CHANGED_HT | BSS_CHANGED_BASIC_RATES | BSS_CHANGED_BEACON_INT; - enum ieee80211_band band = ieee80211_get_sdata_band(sdata); - struct ieee80211_supported_band *sband = - sdata->local->hw.wiphy->bands[band]; local->fif_other_bss++; /* mesh ifaces must set allmulti to forward mcast traffic */ @@ -758,7 +755,6 @@ int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) sdata->vif.bss_conf.ht_operation_mode = ifmsh->mshcfg.ht_opmode; sdata->vif.bss_conf.enable_beacon = true; - sdata->vif.bss_conf.basic_rates = ieee80211_mandatory_rates(sband); changed |= ieee80211_mps_local_status_update(sdata); diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 0daaf72e1b81..30c49202ee4d 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -162,6 +162,16 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, setup->chandef.center_freq1 = setup->chandef.chan->center_freq; } + /* + * check if basic rates are available otherwise use mandatory rates as + * basic rates + */ + if (!setup->basic_rates) { + struct ieee80211_supported_band *sband = + rdev->wiphy.bands[setup->chandef.chan->band]; + setup->basic_rates = ieee80211_mandatory_rates(sband); + } + if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef)) return -EINVAL; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 8aa83c04d4eb..687cb6497598 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -7487,6 +7487,23 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info) setup.chandef.chan = NULL; } + if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) { + u8 *rates = nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); + int n_rates = + nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); + struct ieee80211_supported_band *sband; + + if (!setup.chandef.chan) + return -EINVAL; + + sband = rdev->wiphy.bands[setup.chandef.chan->band]; + + err = ieee80211_get_ratemask(sband, rates, n_rates, + &setup.basic_rates); + if (err) + return err; + } + return cfg80211_join_mesh(rdev, dev, &setup, &cfg); } -- cgit v1.2.3 From 3d124ea27ae2fc895f81725f0b4c7f3d9c733df4 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Mon, 27 May 2013 18:24:02 +0300 Subject: cfg80211: fix VHT TDLS peer AID verification I (Johannes) accidentally applied the first version of the patch ("Allow TDLS peer AID to be configured for VHT"). Now apply just the changes between v1 and v2 to get the AID verification and prefer the new attribute over the old one. Signed-off-by: Jouni Malinen Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 687cb6497598..7183410fcd41 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3972,10 +3972,10 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) params.listen_interval = nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); - if (info->attrs[NL80211_ATTR_STA_AID]) - params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]); - else + if (info->attrs[NL80211_ATTR_PEER_AID]) params.aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]); + else + params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]); if (!params.aid || params.aid > IEEE80211_MAX_AID) return -EINVAL; @@ -4027,7 +4027,8 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD; /* TDLS peers cannot be added */ - if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) + if ((params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) || + info->attrs[NL80211_ATTR_PEER_AID]) return -EINVAL; /* but don't bother the driver with it */ params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER); @@ -4053,7 +4054,8 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_ASSOCIATED)) return -EINVAL; /* TDLS peers cannot be added */ - if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) + if ((params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) || + info->attrs[NL80211_ATTR_PEER_AID]) return -EINVAL; break; case NL80211_IFTYPE_STATION: -- cgit v1.2.3 From f7aeb6fb1a3d6b09623b169518314bc7869fffec Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Tue, 11 Jun 2013 14:20:00 +0200 Subject: mac80211: make mgmt_tx accept a NULL channel cfg80211 passes a NULL channel to mgmt_tx if the frame has to be sent on the one currently in use by the device. Make the implementation of mgmt_tx correctly handle this case. Fail if offchan is required. Signed-off-by: Antonio Quartulli [fix RCU locking] Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index cd6f35f6e714..64cf294c2b96 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2841,6 +2841,12 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, return -EOPNOTSUPP; } + /* configurations requiring offchan cannot work if no channel has been + * specified + */ + if (need_offchan && !chan) + return -EINVAL; + mutex_lock(&local->mtx); /* Check if the operating channel is the requested channel */ @@ -2850,10 +2856,15 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, rcu_read_lock(); chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); - if (chanctx_conf) - need_offchan = chan != chanctx_conf->def.chan; - else + if (chanctx_conf) { + need_offchan = chan && (chan != chanctx_conf->def.chan); + } else if (!chan) { + ret = -EINVAL; + rcu_read_unlock(); + goto out_unlock; + } else { need_offchan = true; + } rcu_read_unlock(); } -- cgit v1.2.3 From c2ff8cad64233b539c71a27e2a6e324001143ef0 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Tue, 11 Jun 2013 14:20:01 +0200 Subject: brcm80211: make mgmt_tx in brcmfmac accept a NULL channel cfg80211 passes a NULL channel to mgmt_tx if the frame has to be sent on the one currently in use by the device. Make the implementation of mgmt_tx correctly handle this case Cc: brcm80211-dev-list@broadcom.com Acked-by: Arend van Spriel Signed-off-by: Antonio Quartulli Signed-off-by: Johannes Berg --- drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 6d758f285352..8bd256ba66ab 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -3917,6 +3917,7 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, struct brcmf_fil_af_params_le *af_params; bool ack; s32 chan_nr; + u32 freq; brcmf_dbg(TRACE, "Enter\n"); @@ -3929,6 +3930,8 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, return -EPERM; } + vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); + if (ieee80211_is_probe_resp(mgmt->frame_control)) { /* Right now the only reason to get a probe response */ /* is for p2p listen response or for p2p GO from */ @@ -3944,7 +3947,6 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN; ie_len = len - ie_offset; - vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif) vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif; err = brcmf_vif_set_mgmt_ie(vif, @@ -3968,8 +3970,15 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, memcpy(&af_params->bssid[0], &mgmt->bssid[0], ETH_ALEN); /* Add the length exepted for 802.11 header */ action_frame->len = cpu_to_le16(len - DOT11_MGMT_HDR_LEN); - /* Add the channel */ - chan_nr = ieee80211_frequency_to_channel(chan->center_freq); + /* Add the channel. Use the one specified as parameter if any or + * the current one (got from the firmware) otherwise + */ + if (chan) + freq = chan->center_freq; + else + brcmf_fil_cmd_int_get(vif->ifp, BRCMF_C_GET_CHANNEL, + &freq); + chan_nr = ieee80211_frequency_to_channel(freq); af_params->channel = cpu_to_le32(chan_nr); memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN], -- cgit v1.2.3 From bfd634d01e02071404e3e36e3946728009ed023f Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Tue, 11 Jun 2013 14:20:02 +0200 Subject: ath6kl: make mgmt_tx accept a NULL channel cfg80211 passes a NULL channel to mgmt_tx if the frame has to be sent on the one currently in use by the device. Make the implementation of mgmt_tx correctly handle this case Cc: Nicolas Cavallari Acked-by: Kalle Valo Signed-off-by: Antonio Quartulli Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index f7995b2b12a4..2437ad26949d 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -3175,10 +3175,21 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, { struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev); struct ath6kl *ar = ath6kl_priv(vif->ndev); - u32 id; + u32 id, freq; const struct ieee80211_mgmt *mgmt; bool more_data, queued; + /* default to the current channel, but use the one specified as argument + * if any + */ + freq = vif->ch_hint; + if (chan) + freq = chan->center_freq; + + /* never send freq zero to the firmware */ + if (WARN_ON(freq == 0)) + return -EINVAL; + mgmt = (const struct ieee80211_mgmt *) buf; if (vif->nw_type == AP_NETWORK && test_bit(CONNECTED, &vif->flags) && ieee80211_is_probe_resp(mgmt->frame_control) && @@ -3188,8 +3199,7 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, * command to allow the target to fill in the generic IEs. */ *cookie = 0; /* TX status not supported */ - return ath6kl_send_go_probe_resp(vif, buf, len, - chan->center_freq); + return ath6kl_send_go_probe_resp(vif, buf, len, freq); } id = vif->send_action_id++; @@ -3205,17 +3215,14 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, /* AP mode Power saving processing */ if (vif->nw_type == AP_NETWORK) { - queued = ath6kl_mgmt_powersave_ap(vif, - id, chan->center_freq, - wait, buf, - len, &more_data, no_cck); + queued = ath6kl_mgmt_powersave_ap(vif, id, freq, wait, buf, len, + &more_data, no_cck); if (queued) return 0; } - return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id, - chan->center_freq, wait, - buf, len, no_cck); + return ath6kl_wmi_send_mgmt_cmd(ar->wmi, vif->fw_vif_idx, id, freq, + wait, buf, len, no_cck); } static void ath6kl_mgmt_frame_register(struct wiphy *wiphy, -- cgit v1.2.3 From ea141b75ae29636b5c9e9d2e2e77b3dd1ab4c934 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Tue, 11 Jun 2013 14:20:03 +0200 Subject: nl80211: allow sending CMD_FRAME without specifying any frequency Users may want to send a frame on the current channel without specifying it. This is particularly useful for the correct implementation of the IBSS/RSN support in wpa_supplicant which requires to receive and send AUTH frames. Make mgmt_tx pass a NULL channel to the driver if none has been specified by the user. Signed-off-by: Antonio Quartulli Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 7183410fcd41..398ce2c59686 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -7147,6 +7147,9 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) return -EOPNOTSUPP; switch (wdev->iftype) { + case NL80211_IFTYPE_P2P_DEVICE: + if (!info->attrs[NL80211_ATTR_WIPHY_FREQ]) + return -EINVAL; case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_P2P_CLIENT: @@ -7154,7 +7157,6 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_P2P_GO: - case NL80211_IFTYPE_P2P_DEVICE: break; default: return -EOPNOTSUPP; @@ -7182,9 +7184,18 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]); - err = nl80211_parse_chandef(rdev, info, &chandef); - if (err) - return err; + /* get the channel if any has been specified, otherwise pass NULL to + * the driver. The latter will use the current one + */ + chandef.chan = NULL; + if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { + err = nl80211_parse_chandef(rdev, info, &chandef); + if (err) + return err; + } + + if (!chandef.chan && offchan) + return -EINVAL; if (!dont_wait_for_ack) { msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); -- cgit v1.2.3 From 940d0ac9dbe3fb9d4806e96f006286c2e476deed Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 11 Jun 2013 16:51:03 +0200 Subject: cfg80211: fix rtnl leak in wiphy dump error cases In two wiphy dump error cases, most often when the dump allocation must be increased, the RTNL is leaked. This quickly results in a complete system lockup. Release the RTNL correctly. Reported-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 398ce2c59686..e4028197b75d 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1541,8 +1541,10 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb) int ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); netdev = dev_get_by_index(sock_net(skb->sk), ifidx); - if (!netdev) + if (!netdev) { + rtnl_unlock(); return -ENODEV; + } if (netdev->ieee80211_ptr) { dev = wiphy_to_dev( netdev->ieee80211_ptr->wiphy); @@ -1586,6 +1588,7 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb) !skb->len && cb->min_dump_alloc < 4096) { cb->min_dump_alloc = 4096; + rtnl_unlock(); return 1; } idx--; -- cgit v1.2.3 From fa8eeae102570dfdf3fd14347a0671cff8a2cfe4 Mon Sep 17 00:00:00 2001 From: Solomon Peachy Date: Tue, 11 Jun 2013 09:49:38 -0400 Subject: cw1200: Remove "ITP" debug subsystem. This can live on as an out-of-tree patch for those that care. Signed-off-by: Solomon Peachy Signed-off-by: John W. Linville --- drivers/net/wireless/cw1200/Kconfig | 5 - drivers/net/wireless/cw1200/Makefile | 1 - drivers/net/wireless/cw1200/debug.c | 6 +- drivers/net/wireless/cw1200/debug.h | 5 - drivers/net/wireless/cw1200/itp.c | 730 ----------------------------------- drivers/net/wireless/cw1200/itp.h | 144 ------- drivers/net/wireless/cw1200/txrx.c | 10 +- drivers/net/wireless/cw1200/wsm.c | 7 - 8 files changed, 3 insertions(+), 905 deletions(-) delete mode 100644 drivers/net/wireless/cw1200/itp.c delete mode 100644 drivers/net/wireless/cw1200/itp.h diff --git a/drivers/net/wireless/cw1200/Kconfig b/drivers/net/wireless/cw1200/Kconfig index 7a585d4e9c8e..fc1b472460c8 100644 --- a/drivers/net/wireless/cw1200/Kconfig +++ b/drivers/net/wireless/cw1200/Kconfig @@ -35,11 +35,6 @@ config CW1200_ETF help If you don't know what this is, just say N. -config CW1200_ITP - bool "Enable ITP access" - help - If you don't know what this is, just say N. - endmenu endif diff --git a/drivers/net/wireless/cw1200/Makefile b/drivers/net/wireless/cw1200/Makefile index bc6cbf91f26e..b086aac6547a 100644 --- a/drivers/net/wireless/cw1200/Makefile +++ b/drivers/net/wireless/cw1200/Makefile @@ -9,7 +9,6 @@ cw1200_core-y := \ sta.o \ scan.o \ debug.o -cw1200_core-$(CONFIG_CW1200_ITP) += itp.o cw1200_core-$(CONFIG_PM) += pm.o # CFLAGS_sta.o += -DDEBUG diff --git a/drivers/net/wireless/cw1200/debug.c b/drivers/net/wireless/cw1200/debug.c index 1596b7042cbd..ac9c219c6330 100644 --- a/drivers/net/wireless/cw1200/debug.c +++ b/drivers/net/wireless/cw1200/debug.c @@ -559,10 +559,6 @@ int cw1200_debug_init(struct cw1200_common *priv) priv, &fops_wsm_dumps)) goto err; - ret = cw1200_itp_init(priv); - if (ret) - goto err; - return 0; err: @@ -576,7 +572,7 @@ void cw1200_debug_release(struct cw1200_common *priv) { struct cw1200_debug_priv *d = priv->debug; if (d) { - cw1200_itp_release(priv); + debugfs_remove_recursive(d->debugfs_phy); priv->debug = NULL; kfree(d); } diff --git a/drivers/net/wireless/cw1200/debug.h b/drivers/net/wireless/cw1200/debug.h index 1fea5b29a819..b525aba53bfc 100644 --- a/drivers/net/wireless/cw1200/debug.h +++ b/drivers/net/wireless/cw1200/debug.h @@ -12,8 +12,6 @@ #ifndef CW1200_DEBUG_H_INCLUDED #define CW1200_DEBUG_H_INCLUDED -#include "itp.h" - struct cw1200_debug_priv { struct dentry *debugfs_phy; int tx; @@ -30,9 +28,6 @@ struct cw1200_debug_priv { int ba_acc; int ba_cnt_rx; int ba_acc_rx; -#ifdef CONFIG_CW1200_ITP - struct cw1200_itp itp; -#endif /* CONFIG_CW1200_ITP */ }; int cw1200_debug_init(struct cw1200_common *priv); diff --git a/drivers/net/wireless/cw1200/itp.c b/drivers/net/wireless/cw1200/itp.c deleted file mode 100644 index c0730bb49b75..000000000000 --- a/drivers/net/wireless/cw1200/itp.c +++ /dev/null @@ -1,730 +0,0 @@ -/* - * mac80211 glue code for mac80211 ST-Ericsson CW1200 drivers - * ITP code - * - * Copyright (c) 2010, ST-Ericsson - * Author: Dmitry Tarnyagin - * - * 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 -#include -#include -#include -#include -#include -#include -#include "cw1200.h" -#include "debug.h" -#include "itp.h" -#include "sta.h" - -static int __cw1200_itp_open(struct cw1200_common *priv); -static int __cw1200_itp_close(struct cw1200_common *priv); -static void cw1200_itp_rx_start(struct cw1200_common *priv); -static void cw1200_itp_rx_stop(struct cw1200_common *priv); -static void cw1200_itp_rx_stats(struct cw1200_common *priv); -static void cw1200_itp_rx_reset(struct cw1200_common *priv); -static void cw1200_itp_tx_stop(struct cw1200_common *priv); -static void cw1200_itp_handle(struct cw1200_common *priv, - struct sk_buff *skb); -static void cw1200_itp_err(struct cw1200_common *priv, - int err, - int arg); -static void __cw1200_itp_tx_stop(struct cw1200_common *priv); - -static ssize_t cw1200_itp_read(struct file *file, - char __user *user_buf, size_t count, loff_t *ppos) -{ - struct cw1200_common *priv = file->private_data; - struct cw1200_itp *itp = &priv->debug->itp; - struct sk_buff *skb; - int ret; - - if (skb_queue_empty(&itp->log_queue)) - return 0; - - skb = skb_dequeue(&itp->log_queue); - ret = copy_to_user(user_buf, skb->data, skb->len); - *ppos += skb->len; - skb->data[skb->len] = 0; - pr_debug("[ITP] >>> %s", skb->data); - consume_skb(skb); - - return skb->len - ret; -} - -static ssize_t cw1200_itp_write(struct file *file, - const char __user *user_buf, size_t count, loff_t *ppos) -{ - struct cw1200_common *priv = file->private_data; - struct sk_buff *skb; - - if (!count || count > 1024) - return -EINVAL; - skb = dev_alloc_skb(count + 1); - if (!skb) - return -ENOMEM; - skb_trim(skb, 0); - skb_put(skb, count + 1); - if (copy_from_user(skb->data, user_buf, count)) { - kfree_skb(skb); - return -EFAULT; - } - skb->data[count] = 0; - - cw1200_itp_handle(priv, skb); - consume_skb(skb); - return count; -} - -static unsigned int cw1200_itp_poll(struct file *file, poll_table *wait) -{ - struct cw1200_common *priv = file->private_data; - struct cw1200_itp *itp = &priv->debug->itp; - unsigned int mask = 0; - - poll_wait(file, &itp->read_wait, wait); - - if (!skb_queue_empty(&itp->log_queue)) - mask |= POLLIN | POLLRDNORM; - - mask |= POLLOUT | POLLWRNORM; - - return mask; -} - -static int cw1200_itp_open(struct inode *inode, struct file *file) -{ - struct cw1200_common *priv = inode->i_private; - struct cw1200_itp *itp = &priv->debug->itp; - int ret = 0; - - file->private_data = priv; - if (atomic_inc_return(&itp->open_count) == 1) { - ret = __cw1200_itp_open(priv); - if (ret && !atomic_dec_return(&itp->open_count)) - __cw1200_itp_close(priv); - } else { - atomic_dec(&itp->open_count); - ret = -EBUSY; - } - - return ret; -} - -static int cw1200_itp_close(struct inode *inode, struct file *file) -{ - struct cw1200_common *priv = file->private_data; - struct cw1200_itp *itp = &priv->debug->itp; - if (!atomic_dec_return(&itp->open_count)) { - __cw1200_itp_close(priv); - wake_up(&itp->close_wait); - } - return 0; -} - -static const struct file_operations fops_itp = { - .open = cw1200_itp_open, - .read = cw1200_itp_read, - .write = cw1200_itp_write, - .poll = cw1200_itp_poll, - .release = cw1200_itp_close, - .llseek = default_llseek, - .owner = THIS_MODULE, -}; - -static void cw1200_itp_fill_pattern(u8 *data, int size, - enum cw1200_itp_data_modes mode) -{ - if (size <= 0) - return; - - switch (mode) { - default: - case ITP_DATA_ZEROS: - memset(data, 0x0, size); - break; - case ITP_DATA_ONES: - memset(data, 0xff, size); - break; - case ITP_DATA_ZERONES: - memset(data, 0x55, size); - break; - case ITP_DATA_RANDOM: - get_random_bytes(data, size); - break; - } - return; -} - -static void cw1200_itp_tx_work(struct work_struct *work) -{ - struct cw1200_itp *itp = container_of(work, struct cw1200_itp, - tx_work.work); - struct cw1200_common *priv = itp->priv; - atomic_set(&priv->bh_tx, 1); - wake_up(&priv->bh_wq); -} - -static void cw1200_itp_tx_finish(struct work_struct *work) -{ - struct cw1200_itp *itp = container_of(work, struct cw1200_itp, - tx_finish.work); - __cw1200_itp_tx_stop(itp->priv); -} - -int cw1200_itp_init(struct cw1200_common *priv) -{ - struct cw1200_itp *itp = &priv->debug->itp; - - itp->priv = priv; - atomic_set(&itp->open_count, 0); - atomic_set(&itp->stop_tx, 0); - atomic_set(&itp->awaiting_confirm, 0); - skb_queue_head_init(&itp->log_queue); - spin_lock_init(&itp->tx_lock); - init_waitqueue_head(&itp->read_wait); - init_waitqueue_head(&itp->write_wait); - init_waitqueue_head(&itp->close_wait); - INIT_DELAYED_WORK(&itp->tx_work, cw1200_itp_tx_work); - INIT_DELAYED_WORK(&itp->tx_finish, cw1200_itp_tx_finish); - itp->data = NULL; - itp->hdr_len = WSM_TX_EXTRA_HEADROOM + - sizeof(struct ieee80211_hdr_3addr); - - if (!debugfs_create_file("itp", S_IRUSR | S_IWUSR, - priv->debug->debugfs_phy, priv, &fops_itp)) - return -ENOMEM; - - return 0; -} - -void cw1200_itp_release(struct cw1200_common *priv) -{ - struct cw1200_itp *itp = &priv->debug->itp; - - wait_event_interruptible(itp->close_wait, - !atomic_read(&itp->open_count)); - - WARN_ON(atomic_read(&itp->open_count)); - - skb_queue_purge(&itp->log_queue); - cw1200_itp_tx_stop(priv); -} - -static int __cw1200_itp_open(struct cw1200_common *priv) -{ - struct cw1200_itp *itp = &priv->debug->itp; - - if (!priv->vif) - return -EINVAL; - if (priv->join_status) - return -EINVAL; - itp->saved_channel = priv->channel; - if (!priv->channel) - priv->channel = &priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ]->channels[0]; - wsm_set_bssid_filtering(priv, false); - cw1200_itp_rx_reset(priv); - return 0; -} - -static int __cw1200_itp_close(struct cw1200_common *priv) -{ - struct cw1200_itp *itp = &priv->debug->itp; - if (atomic_read(&itp->test_mode) == TEST_MODE_RX_TEST) - cw1200_itp_rx_stop(priv); - cw1200_itp_tx_stop(priv); - cw1200_disable_listening(priv); - cw1200_update_filtering(priv); - priv->channel = itp->saved_channel; - return 0; -} - -bool cw1200_is_itp(struct cw1200_common *priv) -{ - struct cw1200_itp *itp = &priv->debug->itp; - return atomic_read(&itp->open_count) != 0; -} - -static void cw1200_itp_rx_reset(struct cw1200_common *priv) -{ - struct cw1200_itp *itp = &priv->debug->itp; - itp->rx_cnt = 0; - itp->rx_rssi = 0; - itp->rx_rssi_max = -1000; - itp->rx_rssi_min = 1000; -} - -static void cw1200_itp_rx_start(struct cw1200_common *priv) -{ - struct cw1200_itp *itp = &priv->debug->itp; - - pr_debug("[ITP] RX start, band = %d, ch = %d\n", - itp->band, itp->ch); - atomic_set(&itp->test_mode, TEST_MODE_RX_TEST); - cw1200_update_listening(priv, false); - priv->channel = &priv->hw-> - wiphy->bands[itp->band]->channels[itp->ch]; - cw1200_update_listening(priv, true); - wsm_set_bssid_filtering(priv, false); -} - -static void cw1200_itp_rx_stop(struct cw1200_common *priv) -{ - struct cw1200_itp *itp = &priv->debug->itp; - pr_debug("[ITP] RX stop\n"); - atomic_set(&itp->test_mode, TEST_MODE_NO_TEST); - cw1200_itp_rx_reset(priv); -} - -static void cw1200_itp_rx_stats(struct cw1200_common *priv) -{ - struct cw1200_itp *itp = &priv->debug->itp; - struct sk_buff *skb; - char buf[128]; - int len, ret; - struct wsm_mib_counters_table counters; - - ret = wsm_get_counters_table(priv, &counters); - - if (ret) - cw1200_itp_err(priv, -EBUSY, 20); - - if (!itp->rx_cnt) - len = snprintf(buf, sizeof(buf), "1,0,0,0,0,%d\n", - counters.rx_packet_errors); - else - len = snprintf(buf, sizeof(buf), "1,%d,%ld,%d,%d,%d\n", - itp->rx_cnt, - itp->rx_cnt ? itp->rx_rssi / itp->rx_cnt : 0, - itp->rx_rssi_min, itp->rx_rssi_max, - counters.rx_packet_errors); - - if (len <= 0) { - cw1200_itp_err(priv, -EBUSY, 21); - return; - } - - skb = dev_alloc_skb(len); - if (!skb) { - cw1200_itp_err(priv, -ENOMEM, 22); - return; - } - - itp->rx_cnt = 0; - itp->rx_rssi = 0; - itp->rx_rssi_max = -1000; - itp->rx_rssi_min = 1000; - - skb_trim(skb, 0); - skb_put(skb, len); - - memcpy(skb->data, buf, len); - skb_queue_tail(&itp->log_queue, skb); - wake_up(&itp->read_wait); -} - -static void cw1200_itp_tx_start(struct cw1200_common *priv) -{ - struct wsm_tx *tx; - struct ieee80211_hdr_3addr *hdr; - struct cw1200_itp *itp = &priv->debug->itp; - struct wsm_mib_association_mode assoc_mode = { - .flags = WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE, - .preamble = itp->preamble, - }; - int len; - u8 da_addr[6] = ITP_DEFAULT_DA_ADDR; - - /* Rates index 4 and 5 are not supported */ - if (itp->rate > 3) - itp->rate += 2; - - pr_debug("[ITP] TX start: band = %d, ch = %d, rate = %d, preamble = %d, number = %d, data_mode = %d, interval = %d, power = %d, data_len = %d\n", - itp->band, itp->ch, itp->rate, itp->preamble, - itp->number, itp->data_mode, itp->interval_us, - itp->power, itp->data_len); - - len = itp->hdr_len + itp->data_len; - - itp->data = kmalloc(len, GFP_KERNEL); - tx = (struct wsm_tx *)itp->data; - tx->hdr.len = itp->data_len + itp->hdr_len; - tx->hdr.id = __cpu_to_le16(0x0004 | 1 << 6); - tx->max_tx_rate = itp->rate; - tx->queue_id = 3; - tx->more = 0; - tx->flags = 0xc; - tx->packet_id = 0x55ff55; - tx->reserved = 0; - tx->expire_time = 1; - - if (itp->preamble == ITP_PREAMBLE_GREENFIELD) - tx->ht_tx_parameters = WSM_HT_TX_GREENFIELD; - else if (itp->preamble == ITP_PREAMBLE_MIXED) - tx->ht_tx_parameters = WSM_HT_TX_MIXED; - - hdr = (struct ieee80211_hdr_3addr *)&itp->data[sizeof(struct wsm_tx)]; - memset(hdr, 0, sizeof(*hdr)); - hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_FCTL_TODS); - memcpy(hdr->addr1, da_addr, ETH_ALEN); - memcpy(hdr->addr2, priv->vif->addr, ETH_ALEN); - memcpy(hdr->addr3, da_addr, ETH_ALEN); - - cw1200_itp_fill_pattern(&itp->data[itp->hdr_len], - itp->data_len, itp->data_mode); - - cw1200_update_listening(priv, false); - priv->channel = &priv->hw->wiphy->bands[itp->band]->channels[itp->ch]; - WARN_ON(wsm_set_output_power(priv, itp->power)); - if (itp->preamble == ITP_PREAMBLE_SHORT || - itp->preamble == ITP_PREAMBLE_LONG) - WARN_ON(wsm_set_association_mode(priv, - &assoc_mode)); - wsm_set_bssid_filtering(priv, false); - cw1200_update_listening(priv, true); - - spin_lock_bh(&itp->tx_lock); - atomic_set(&itp->test_mode, TEST_MODE_TX_TEST); - atomic_set(&itp->awaiting_confirm, 0); - atomic_set(&itp->stop_tx, 0); - atomic_set(&priv->bh_tx, 1); - ktime_get_ts(&itp->last_sent); - wake_up(&priv->bh_wq); - spin_unlock_bh(&itp->tx_lock); -} - -void __cw1200_itp_tx_stop(struct cw1200_common *priv) -{ - struct cw1200_itp *itp = &priv->debug->itp; - spin_lock_bh(&itp->tx_lock); - kfree(itp->data); - itp->data = NULL; - atomic_set(&itp->test_mode, TEST_MODE_NO_TEST); - spin_unlock_bh(&itp->tx_lock); -} - -static void cw1200_itp_tx_stop(struct cw1200_common *priv) -{ - struct cw1200_itp *itp = &priv->debug->itp; - pr_debug("[ITP] TX stop\n"); - atomic_set(&itp->stop_tx, 1); - flush_workqueue(priv->workqueue); - - /* time for FW to confirm all tx requests */ - msleep(500); - - __cw1200_itp_tx_stop(priv); -} - -static int cw1200_print_fw_version(struct cw1200_common *priv, - u8 *buf, size_t len) -{ - return snprintf(buf, len, "%s %d.%d", - cw1200_fw_types[priv->wsm_caps.fw_type], - priv->wsm_caps.fw_ver, - priv->wsm_caps.fw_build); -} - -static void cw1200_itp_get_version(struct cw1200_common *priv, - enum cw1200_itp_version_type type) -{ - struct cw1200_itp *itp = &priv->debug->itp; - struct sk_buff *skb; - char buf[ITP_BUF_SIZE]; - size_t size = 0; - int len; - pr_debug("[ITP] print %s version\n", - type == ITP_CHIP_ID ? "chip" : "firmware"); - - len = snprintf(buf, ITP_BUF_SIZE, "2,"); - if (len <= 0) { - cw1200_itp_err(priv, -EINVAL, 40); - return; - } - size += len; - - switch (type) { - case ITP_CHIP_ID: - len = cw1200_print_fw_version(priv, buf+size, - ITP_BUF_SIZE - size); - - if (len <= 0) { - cw1200_itp_err(priv, -EINVAL, 41); - return; - } - size += len; - break; - case ITP_FW_VER: - len = snprintf(buf+size, ITP_BUF_SIZE - size, - "%d.%d", priv->wsm_caps.hw_id, - priv->wsm_caps.hw_subid); - if (len <= 0) { - cw1200_itp_err(priv, -EINVAL, 42); - return; - } - size += len; - break; - default: - cw1200_itp_err(priv, -EINVAL, 43); - break; - } - - len = snprintf(buf+size, ITP_BUF_SIZE-size, "\n"); - if (len <= 0) { - cw1200_itp_err(priv, -EINVAL, 44); - return; - } - size += len; - - skb = dev_alloc_skb(size); - if (!skb) { - cw1200_itp_err(priv, -ENOMEM, 45); - return; - } - - skb_trim(skb, 0); - skb_put(skb, size); - - memcpy(skb->data, buf, size); - skb_queue_tail(&itp->log_queue, skb); - wake_up(&itp->read_wait); -} - -int cw1200_itp_get_tx(struct cw1200_common *priv, u8 **data, - size_t *tx_len, int *burst) -{ - struct cw1200_itp *itp; - struct timespec now; - int time_left_us; - - if (!priv->debug) - return 0; - - itp = &priv->debug->itp; - - if (!itp) - return 0; - - spin_lock_bh(&itp->tx_lock); - if (atomic_read(&itp->test_mode) != TEST_MODE_TX_TEST) - goto out; - - if (atomic_read(&itp->stop_tx)) - goto out; - - if (itp->number == 0) { - atomic_set(&itp->stop_tx, 1); - queue_delayed_work(priv->workqueue, &itp->tx_finish, HZ/10); - goto out; - } - - if (!itp->data) - goto out; - - if (priv->hw_bufs_used >= 2) { - if (!atomic_read(&priv->bh_rx)) - atomic_set(&priv->bh_rx, 1); - atomic_set(&priv->bh_tx, 1); - goto out; - } - - ktime_get_ts(&now); - time_left_us = (itp->last_sent.tv_sec - now.tv_sec)*1000000 + - (itp->last_sent.tv_nsec - now.tv_nsec)/1000 + - itp->interval_us; - - if (time_left_us > ITP_TIME_THRES_US) { - queue_delayed_work(priv->workqueue, &itp->tx_work, - ITP_US_TO_MS(time_left_us)*HZ/1000); - goto out; - } - - if (time_left_us > 50) - udelay(time_left_us); - - if (itp->number > 0) - itp->number--; - - *data = itp->data; - *tx_len = itp->data_len + itp->hdr_len; - - if (itp->data_mode == ITP_DATA_RANDOM) - cw1200_itp_fill_pattern(&itp->data[itp->hdr_len], - itp->data_len, itp->data_mode); - *burst = 2; - atomic_set(&priv->bh_tx, 1); - ktime_get_ts(&itp->last_sent); - atomic_add(1, &itp->awaiting_confirm); - spin_unlock_bh(&itp->tx_lock); - return 1; - -out: - spin_unlock_bh(&itp->tx_lock); - return 0; -} - -bool cw1200_itp_rxed(struct cw1200_common *priv, struct sk_buff *skb) -{ - struct cw1200_itp *itp = &priv->debug->itp; - struct ieee80211_rx_status *rx = IEEE80211_SKB_RXCB(skb); - int signal; - - if (atomic_read(&itp->test_mode) != TEST_MODE_RX_TEST) - return cw1200_is_itp(priv); - if (rx->freq != priv->channel->center_freq) - return true; - - signal = rx->signal; - itp->rx_cnt++; - itp->rx_rssi += signal; - if (itp->rx_rssi_min > rx->signal) - itp->rx_rssi_min = rx->signal; - if (itp->rx_rssi_max < rx->signal) - itp->rx_rssi_max = rx->signal; - - return true; -} - -void cw1200_itp_wake_up_tx(struct cw1200_common *priv) -{ - wake_up(&priv->debug->itp.write_wait); -} - -bool cw1200_itp_tx_running(struct cw1200_common *priv) -{ - if (atomic_read(&priv->debug->itp.awaiting_confirm) || - atomic_read(&priv->debug->itp.test_mode) == - TEST_MODE_TX_TEST) { - atomic_sub(1, &priv->debug->itp.awaiting_confirm); - return true; - } - return false; -} - -static void cw1200_itp_handle(struct cw1200_common *priv, - struct sk_buff *skb) -{ - struct cw1200_itp *itp = &priv->debug->itp; - const struct wiphy *wiphy = priv->hw->wiphy; - int cmd; - int ret; - - pr_debug("[ITP] <<< %s", skb->data); - if (sscanf(skb->data, "%d", &cmd) != 1) { - cw1200_itp_err(priv, -EINVAL, 1); - return; - } - - switch (cmd) { - case 1: /* RX test */ - if (atomic_read(&itp->test_mode)) { - cw1200_itp_err(priv, -EBUSY, 0); - return; - } - ret = sscanf(skb->data, "%d,%d,%d", - &cmd, &itp->band, &itp->ch); - if (ret != 3) { - cw1200_itp_err(priv, -EINVAL, ret + 1); - return; - } - if (itp->band >= 2) { - cw1200_itp_err(priv, -EINVAL, 2); - } else if (!wiphy->bands[itp->band]) { - cw1200_itp_err(priv, -EINVAL, 2); - } else if (itp->ch >= wiphy->bands[itp->band]->n_channels) { - cw1200_itp_err(priv, -EINVAL, 3); - } else { - cw1200_itp_rx_stats(priv); - cw1200_itp_rx_start(priv); - } - break; - case 2: /* RX stat */ - cw1200_itp_rx_stats(priv); - break; - case 3: /* RX/TX stop */ - if (atomic_read(&itp->test_mode) == TEST_MODE_RX_TEST) { - cw1200_itp_rx_stats(priv); - cw1200_itp_rx_stop(priv); - } else if (atomic_read(&itp->test_mode) == TEST_MODE_TX_TEST) { - cw1200_itp_tx_stop(priv); - } else { - cw1200_itp_err(priv, -EBUSY, 0); - } - break; - case 4: /* TX start */ - if (atomic_read(&itp->test_mode) != TEST_MODE_NO_TEST) { - cw1200_itp_err(priv, -EBUSY, 0); - return; - } - ret = sscanf(skb->data, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", - &cmd, &itp->band, &itp->ch, &itp->rate, - &itp->preamble, &itp->number, &itp->data_mode, - &itp->interval_us, &itp->power, &itp->data_len); - if (ret != 10) { - cw1200_itp_err(priv, -EINVAL, ret + 1); - return; - } - if (itp->band >= 2) { - cw1200_itp_err(priv, -EINVAL, 2); - } else if (!wiphy->bands[itp->band]) { - cw1200_itp_err(priv, -EINVAL, 2); - } else if (itp->ch >= wiphy->bands[itp->band]->n_channels) { - cw1200_itp_err(priv, -EINVAL, 3); - } else if (itp->rate >= 20) { - cw1200_itp_err(priv, -EINVAL, 4); - } else if (itp->preamble >= ITP_PREAMBLE_MAX) { - cw1200_itp_err(priv, -EINVAL, 5); - } else if (itp->data_mode >= ITP_DATA_MAX_MODE) { - cw1200_itp_err(priv, -EINVAL, 7); - } else if (itp->data_len < ITP_MIN_DATA_SIZE || - itp->data_len > (priv->wsm_caps.input_buffer_size - itp->hdr_len)) { - cw1200_itp_err(priv, -EINVAL, 8); - } else { - cw1200_itp_tx_start(priv); - } - break; - case 5: - cw1200_itp_get_version(priv, ITP_CHIP_ID); - break; - case 6: - cw1200_itp_get_version(priv, ITP_FW_VER); - break; - } -} - -static void cw1200_itp_err(struct cw1200_common *priv, - int err, int arg) -{ - struct cw1200_itp *itp = &priv->debug->itp; - struct sk_buff *skb; - static char buf[255]; - int len; - - len = snprintf(buf, sizeof(buf), "%d,%d\n", - err, arg); - if (len <= 0) - return; - - skb = dev_alloc_skb(len); - if (!skb) - return; - - skb_trim(skb, 0); - skb_put(skb, len); - - memcpy(skb->data, buf, len); - skb_queue_tail(&itp->log_queue, skb); - wake_up(&itp->read_wait); - - len = sprint_symbol(buf, - (unsigned long)__builtin_return_address(0)); - if (len <= 0) - return; - pr_debug("[ITP] error %d,%d from %s\n", - err, arg, buf); -} diff --git a/drivers/net/wireless/cw1200/itp.h b/drivers/net/wireless/cw1200/itp.h deleted file mode 100644 index 1e9dfb7fcadc..000000000000 --- a/drivers/net/wireless/cw1200/itp.h +++ /dev/null @@ -1,144 +0,0 @@ -/* - * ITP code for ST-Ericsson CW1200 mac80211 driver - * - * Copyright (c) 2011, ST-Ericsson - * Author: Dmitry Tarnyagin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef CW1200_ITP_H_INCLUDED -#define CW1200_ITP_H_INCLUDED - -struct cw200_common; -struct wsm_tx_confirm; -struct dentry; - -#ifdef CONFIG_CW1200_ITP - -/*extern*/ struct ieee80211_channel; - -#define TEST_MODE_NO_TEST (0) -#define TEST_MODE_RX_TEST (1) -#define TEST_MODE_TX_TEST (2) -#define ITP_DEFAULT_DA_ADDR {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} -#define ITP_MIN_DATA_SIZE 6 -#define ITP_MAX_DATA_SIZE 1600 -#define ITP_TIME_THRES_US 10000 -#define ITP_US_TO_MS(x) ((x)/1000) -#define ITP_MS_TO_US(x) ((x)*1000) -#define ITP_BUF_SIZE 255 - - -enum cw1200_itp_data_modes { - ITP_DATA_ZEROS, - ITP_DATA_ONES, - ITP_DATA_ZERONES, - ITP_DATA_RANDOM, - ITP_DATA_MAX_MODE, -}; - -enum cw1200_itp_version_type { - ITP_CHIP_ID, - ITP_FW_VER, -}; - -enum cw1200_itp_preamble_type { - ITP_PREAMBLE_LONG, - ITP_PREAMBLE_SHORT, - ITP_PREAMBLE_OFDM, - ITP_PREAMBLE_MIXED, - ITP_PREAMBLE_GREENFIELD, - ITP_PREAMBLE_MAX, -}; - - -struct cw1200_itp { - struct cw1200_common *priv; - atomic_t open_count; - atomic_t awaiting_confirm; - struct sk_buff_head log_queue; - wait_queue_head_t read_wait; - wait_queue_head_t write_wait; - wait_queue_head_t close_wait; - struct ieee80211_channel *saved_channel; - atomic_t stop_tx; - struct delayed_work tx_work; - struct delayed_work tx_finish; - spinlock_t tx_lock; - struct timespec last_sent; - atomic_t test_mode; - int rx_cnt; - long rx_rssi; - int rx_rssi_max; - int rx_rssi_min; - unsigned band; - unsigned ch; - unsigned rate; - unsigned preamble; - unsigned int number; - unsigned data_mode; - int interval_us; - int power; - u8 *data; - int hdr_len; - int data_len; -}; - -int cw1200_itp_init(struct cw1200_common *priv); -void cw1200_itp_release(struct cw1200_common *priv); - -bool cw1200_is_itp(struct cw1200_common *priv); -bool cw1200_itp_rxed(struct cw1200_common *priv, struct sk_buff *skb); -void cw1200_itp_wake_up_tx(struct cw1200_common *priv); -int cw1200_itp_get_tx(struct cw1200_common *priv, u8 **data, - size_t *tx_len, int *burst); -bool cw1200_itp_tx_running(struct cw1200_common *priv); - -#else /* CONFIG_CW1200_ITP */ - -static inline int cw1200_itp_init(struct cw1200_common *priv) -{ - return 0; -} - -static inline void cw1200_itp_release(struct cw1200_common *priv) -{ -} - -static inline bool cw1200_is_itp(struct cw1200_common *priv) -{ - return false; -} - -static inline bool cw1200_itp_rxed(struct cw1200_common *priv, - struct sk_buff *skb) -{ - return false; -} - - -static inline void cw1200_itp_consume_txed(struct cw1200_common *priv) -{ -} - -static inline void cw1200_itp_wake_up_tx(struct cw1200_common *priv) -{ -} - -static inline int cw1200_itp_get_tx(struct cw1200_common *priv, u8 **data, - size_t *tx_len, int *burst) -{ - return 0; -} - -static inline bool cw1200_itp_tx_running(struct cw1200_common *priv) -{ - return false; -} - -#endif /* CONFIG_CW1200_ITP */ - -#endif /* CW1200_ITP_H_INCLUDED */ diff --git a/drivers/net/wireless/cw1200/txrx.c b/drivers/net/wireless/cw1200/txrx.c index a2547473ec5c..451d74625f90 100644 --- a/drivers/net/wireless/cw1200/txrx.c +++ b/drivers/net/wireless/cw1200/txrx.c @@ -861,9 +861,6 @@ void cw1200_tx_confirm_cb(struct cw1200_common *priv, pr_debug("[TX] TX confirm: %d, %d.\n", arg->status, arg->ack_failures); - if (cw1200_itp_tx_running(priv)) - return; - if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) { /* STA is stopped. */ return; @@ -1001,8 +998,7 @@ void cw1200_skb_dtor(struct cw1200_common *priv, txpriv->raw_link_id, txpriv->tid); tx_policy_put(priv, txpriv->rate_id); } - if (!cw1200_is_itp(priv)) - ieee80211_tx_status(priv->hw, skb); + ieee80211_tx_status(priv->hw, skb); } void cw1200_rx_cb(struct cw1200_common *priv, @@ -1205,9 +1201,7 @@ void cw1200_rx_cb(struct cw1200_common *priv, grace_period = 1 * HZ; cw1200_pm_stay_awake(&priv->pm_state, grace_period); - if (cw1200_itp_rxed(priv, skb)) { - consume_skb(skb); - } else if (early_data) { + if (early_data) { spin_lock_bh(&priv->ps_state_lock); /* Double-check status with lock held */ if (entry->status == CW1200_LINK_SOFT) diff --git a/drivers/net/wireless/cw1200/wsm.c b/drivers/net/wireless/cw1200/wsm.c index 3ab25f661793..e99f34e7604d 100644 --- a/drivers/net/wireless/cw1200/wsm.c +++ b/drivers/net/wireless/cw1200/wsm.c @@ -21,7 +21,6 @@ #include "bh.h" #include "sta.h" #include "debug.h" -#include "itp.h" #define WSM_CMD_TIMEOUT (2 * HZ) /* With respect to interrupt loss */ #define WSM_CMD_START_TIMEOUT (7 * HZ) @@ -1733,12 +1732,6 @@ int wsm_get_tx(struct cw1200_common *priv, u8 **data, /* More is used only for broadcasts. */ bool more = false; -#ifdef CONFIG_CW1200_ITP - count = cw1200_itp_get_tx(priv, data, tx_len, burst); - if (count) - return count; -#endif - if (priv->wsm_cmd.ptr) { /* CMD request */ ++count; spin_lock(&priv->wsm_cmd.lock); -- cgit v1.2.3 From 19db577868e94c80dc9a569d937109f95c34d0f4 Mon Sep 17 00:00:00 2001 From: Solomon Peachy Date: Tue, 11 Jun 2013 09:49:39 -0400 Subject: cw1200: Eliminate the ETF debug/engineering code. This is only really useful for people who are bringing up new hardware designs and have access to the proprietary vendor tools that interface with this mode. It'll live out of tree until it's rewritten to use a less kludgy interface. Signed-off-by: Solomon Peachy Signed-off-by: John W. Linville --- drivers/net/wireless/cw1200/Kconfig | 10 -- drivers/net/wireless/cw1200/cw1200.h | 9 -- drivers/net/wireless/cw1200/debug.c | 236 ----------------------------------- drivers/net/wireless/cw1200/fwio.c | 5 - drivers/net/wireless/cw1200/main.c | 30 +---- drivers/net/wireless/cw1200/wsm.c | 56 +-------- drivers/net/wireless/cw1200/wsm.h | 5 - 7 files changed, 2 insertions(+), 349 deletions(-) diff --git a/drivers/net/wireless/cw1200/Kconfig b/drivers/net/wireless/cw1200/Kconfig index fc1b472460c8..0880742eab17 100644 --- a/drivers/net/wireless/cw1200/Kconfig +++ b/drivers/net/wireless/cw1200/Kconfig @@ -27,14 +27,4 @@ config CW1200_WLAN_SPI need to add appropriate platform data glue in your board setup file. -menu "Driver debug features" - depends on CW1200 && DEBUG_FS - -config CW1200_ETF - bool "Enable CW1200 Engineering Test Framework hooks" - help - If you don't know what this is, just say N. - -endmenu - endif diff --git a/drivers/net/wireless/cw1200/cw1200.h b/drivers/net/wireless/cw1200/cw1200.h index 95320f2b25eb..91ff7f19c4f0 100644 --- a/drivers/net/wireless/cw1200/cw1200.h +++ b/drivers/net/wireless/cw1200/cw1200.h @@ -35,11 +35,6 @@ struct task_struct; struct cw1200_debug_priv; struct firmware; -#ifdef CONFIG_CW1200_ETF -extern int etf_mode; -extern char *etf_firmware; -#endif - #define CW1200_MAX_CTRL_FRAME_LEN (0x1000) #define CW1200_MAX_STA_IN_AP_MODE (5) @@ -287,10 +282,6 @@ struct cw1200_common { struct work_struct linkid_reset_work; u8 action_frame_sa[ETH_ALEN]; u8 action_linkid; - -#ifdef CONFIG_CW1200_ETF - struct sk_buff_head etf_q; -#endif }; struct cw1200_sta_priv { diff --git a/drivers/net/wireless/cw1200/debug.c b/drivers/net/wireless/cw1200/debug.c index ac9c219c6330..e323b4d54338 100644 --- a/drivers/net/wireless/cw1200/debug.c +++ b/drivers/net/wireless/cw1200/debug.c @@ -357,144 +357,6 @@ static const struct file_operations fops_counters = { .owner = THIS_MODULE, }; -#ifdef CONFIG_CW1200_ETF -static int cw1200_etf_out_show(struct seq_file *seq, void *v) -{ - struct cw1200_common *priv = seq->private; - struct sk_buff *skb; - u32 len = 0; - - skb = skb_dequeue(&priv->etf_q); - - if (skb) - len = skb->len; - - seq_write(seq, &len, sizeof(len)); - - if (skb) { - seq_write(seq, skb->data, len); - kfree_skb(skb); - } - - return 0; -} - -static int cw1200_etf_out_open(struct inode *inode, struct file *file) -{ - return single_open(file, &cw1200_etf_out_show, - inode->i_private); -} - -static const struct file_operations fops_etf_out = { - .open = cw1200_etf_out_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -struct etf_req_msg; -static int etf_request(struct cw1200_common *priv, - struct etf_req_msg *msg, u32 len); - -#define MAX_RX_SIZE 2600 - -struct etf_in_state { - struct cw1200_common *priv; - u16 total_len; - u16 written; - u8 buf[MAX_RX_SIZE]; -}; - -static int cw1200_etf_in_open(struct inode *inode, struct file *file) -{ - struct etf_in_state *etf = kmalloc(sizeof(struct etf_in_state), - GFP_KERNEL); - - if (!etf) - return -ENOMEM; - - etf->written = 0; - etf->total_len = 0; - etf->priv = inode->i_private; - - file->private_data = etf; - - return 0; -} - -static int cw1200_etf_in_release(struct inode *inode, struct file *file) -{ - kfree(file->private_data); - return 0; -} - -static ssize_t cw1200_etf_in_write(struct file *file, - const char __user *user_buf, size_t count, loff_t *ppos) -{ - struct etf_in_state *etf = file->private_data; - - ssize_t written = 0; - - if (!etf->total_len) { - if (count < sizeof(etf->total_len)) { - pr_err("count < sizeof(total_len)\n"); - return -EINVAL; - } - - if (copy_from_user(&etf->total_len, user_buf, - sizeof(etf->total_len))) { - pr_err("copy_from_user (len) failed\n"); - return -EFAULT; - } - - if (etf->total_len > MAX_RX_SIZE) { - pr_err("requested length > MAX_RX_SIZE\n"); - return -EINVAL; - } - - written += sizeof(etf->total_len); - count -= sizeof(etf->total_len); - } - - if (!count) - goto done; - - if (count > (etf->total_len - written)) { - pr_err("Tried to write > MAX_RX_SIZE\n"); - return -EINVAL; - } - - if (copy_from_user(etf->buf + etf->written, user_buf + written, - count)) { - pr_err("copy_from_user (payload %zu) failed\n", count); - return -EFAULT; - } - - written += count; - etf->written += count; - - if (etf->written >= etf->total_len) { - if (etf_request(etf->priv, (struct etf_req_msg *)etf->buf, - etf->total_len)) { - pr_err("etf_request failed\n"); - return -EIO; - } - } - -done: - return written; -} - -static const struct file_operations fops_etf_in = { - .open = cw1200_etf_in_open, - .release = cw1200_etf_in_release, - .write = cw1200_etf_in_write, - .llseek = default_llseek, - .owner = THIS_MODULE, -}; -#endif /* CONFIG_CW1200_ETF */ - static ssize_t cw1200_wsm_dumps(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { @@ -542,19 +404,6 @@ int cw1200_debug_init(struct cw1200_common *priv) priv, &fops_counters)) goto err; -#ifdef CONFIG_CW1200_ETF - if (etf_mode) { - skb_queue_head_init(&priv->etf_q); - - if (!debugfs_create_file("etf_out", S_IRUSR, d->debugfs_phy, - priv, &fops_etf_out)) - goto err; - if (!debugfs_create_file("etf_in", S_IWUSR, d->debugfs_phy, - priv, &fops_etf_in)) - goto err; - } -#endif /* CONFIG_CW1200_ETF */ - if (!debugfs_create_file("wsm_dumps", S_IWUSR, d->debugfs_phy, priv, &fops_wsm_dumps)) goto err; @@ -577,88 +426,3 @@ void cw1200_debug_release(struct cw1200_common *priv) kfree(d); } } - -#ifdef CONFIG_CW1200_ETF -struct cw1200_sdd { - u8 id; - u8 len; - u8 data[]; -}; - -struct etf_req_msg { - u32 id; - u32 len; - u8 data[]; -}; - -static int parse_sdd_file(struct cw1200_common *priv, u8 *data, u32 length) -{ - struct cw1200_sdd *ie; - - while (length > 0) { - ie = (struct cw1200_sdd *)data; - if (ie->id == SDD_REFERENCE_FREQUENCY_ELT_ID) { - priv->hw_refclk = cpu_to_le16(*((u16 *)ie->data)); - pr_info("Using Reference clock frequency %d KHz\n", - priv->hw_refclk); - break; - } - - length -= ie->len + sizeof(*ie); - data += ie->len + sizeof(*ie); - } - return 0; -} - -char *etf_firmware; - -#define ST90TDS_START_ADAPTER 0x09 /* Loads firmware too */ -#define ST90TDS_STOP_ADAPTER 0x0A -#define ST90TDS_CONFIG_ADAPTER 0x0E /* Send configuration params */ -#define ST90TDS_SBUS_READ 0x13 -#define ST90TDS_SBUS_WRITE 0x14 -#define ST90TDS_GET_DEVICE_OPTION 0x19 -#define ST90TDS_SET_DEVICE_OPTION 0x1A -#define ST90TDS_SEND_SDD 0x1D /* SDD File used to find DPLL */ - -#include "fwio.h" - -static int etf_request(struct cw1200_common *priv, - struct etf_req_msg *msg, - u32 len) -{ - int rval = -1; - switch (msg->id) { - case ST90TDS_START_ADAPTER: - etf_firmware = "cw1200_etf.bin"; - pr_info("ETF_START (len %d, '%s')\n", len, etf_firmware); - rval = cw1200_load_firmware(priv); - break; - case ST90TDS_STOP_ADAPTER: - pr_info("ETF_STOP (unhandled)\n"); - break; - case ST90TDS_SEND_SDD: - pr_info("ETF_SDD\n"); - rval = parse_sdd_file(priv, msg->data, msg->len); - break; - case ST90TDS_CONFIG_ADAPTER: - pr_info("ETF_CONFIG_ADAP (unhandled)\n"); - break; - case ST90TDS_SBUS_READ: - pr_info("ETF_SBUS_READ (unhandled)\n"); - break; - case ST90TDS_SBUS_WRITE: - pr_info("ETF_SBUS_WRITE (unhandled)\n"); - break; - case ST90TDS_SET_DEVICE_OPTION: - pr_info("ETF_SET_DEV_OPT (unhandled)\n"); - break; - default: - pr_info("ETF_PASSTHRU (0x%08x)\n", msg->id); - rval = wsm_raw_cmd(priv, (u8 *)msg, len); - break; - } - - return rval; -} -#endif /* CONFIG_CW1200_ETF */ diff --git a/drivers/net/wireless/cw1200/fwio.c b/drivers/net/wireless/cw1200/fwio.c index 427c9f24b94e..acdff0f7f952 100644 --- a/drivers/net/wireless/cw1200/fwio.c +++ b/drivers/net/wireless/cw1200/fwio.c @@ -139,11 +139,6 @@ static int cw1200_load_firmware_cw1200(struct cw1200_common *priv) val32 &= ~ST90TDS_CONFIG_CPU_CLK_DIS_BIT; REG_WRITE(ST90TDS_CONFIG_REG_ID, val32); -#ifdef CONFIG_CW1200_ETF - if (etf_mode) - fw_path = etf_firmware; -#endif - /* Load a firmware file */ ret = request_firmware(&firmware, fw_path, priv->pdev); if (ret) { diff --git a/drivers/net/wireless/cw1200/main.c b/drivers/net/wireless/cw1200/main.c index 2ea1bada7207..9f9adb4fbfb8 100644 --- a/drivers/net/wireless/cw1200/main.c +++ b/drivers/net/wireless/cw1200/main.c @@ -61,12 +61,6 @@ int cw1200_power_mode = wsm_power_mode_quiescent; module_param(cw1200_power_mode, int, 0644); MODULE_PARM_DESC(cw1200_power_mode, "WSM power mode. 0 == active, 1 == doze, 2 == quiescent (default)"); -#ifdef CONFIG_CW1200_ETF -int etf_mode; -module_param(etf_mode, int, 0644); -MODULE_PARM_DESC(etf_mode, "Enable EngineeringTestingFramework operation"); -#endif - #define RATETAB_ENT(_rate, _rateid, _flags) \ { \ .bitrate = (_rate), \ @@ -418,11 +412,6 @@ static int cw1200_register_common(struct ieee80211_hw *dev) struct cw1200_common *priv = dev->priv; int err; -#ifdef CONFIG_CW1200_ETF - if (etf_mode) - goto done; -#endif - #ifdef CONFIG_PM err = cw1200_pm_init(&priv->pm_state, priv); if (err) { @@ -442,9 +431,6 @@ static int cw1200_register_common(struct ieee80211_hw *dev) return err; } -#ifdef CONFIG_CW1200_ETF -done: -#endif cw1200_debug_init(priv); pr_info("Registered as '%s'\n", wiphy_name(dev->wiphy)); @@ -461,13 +447,7 @@ static void cw1200_unregister_common(struct ieee80211_hw *dev) struct cw1200_common *priv = dev->priv; int i; -#ifdef CONFIG_CW1200_ETF - if (!etf_mode) { -#endif - ieee80211_unregister_hw(dev); -#ifdef CONFIG_CW1200_ETF - } -#endif + ieee80211_unregister_hw(dev); del_timer_sync(&priv->mcast_timeout); cw1200_unregister_bh(priv); @@ -568,11 +548,6 @@ int cw1200_core_probe(const struct hwbus_ops *hwbus_ops, if (err) goto err1; -#ifdef CONFIG_CW1200_ETF - if (etf_mode) - goto skip_fw; -#endif - err = cw1200_load_firmware(priv); if (err) goto err2; @@ -594,9 +569,6 @@ int cw1200_core_probe(const struct hwbus_ops *hwbus_ops, /* Enable multi-TX confirmation */ wsm_use_multi_tx_conf(priv, true); -#ifdef CONFIG_CW1200_ETF -skip_fw: -#endif err = cw1200_register_common(dev); if (err) goto err2; diff --git a/drivers/net/wireless/cw1200/wsm.c b/drivers/net/wireless/cw1200/wsm.c index e99f34e7604d..d185f29efb7e 100644 --- a/drivers/net/wireless/cw1200/wsm.c +++ b/drivers/net/wireless/cw1200/wsm.c @@ -1111,10 +1111,7 @@ static int wsm_cmd_send(struct cw1200_common *priv, * pad the message by a few bytes to ensure * that it's completely received. */ -#ifdef CONFIG_CW1200_ETF - if (!etf_mode) -#endif - buf_len += 4; + buf_len += 4; /* Fill HI message header */ /* BH will add sequence number */ @@ -1165,29 +1162,6 @@ done: return ret; } -#ifdef CONFIG_CW1200_ETF -int wsm_raw_cmd(struct cw1200_common *priv, u8 *data, size_t len) -{ - struct wsm_buf *buf = &priv->wsm_cmd_buf; - int ret; - - u16 *cmd = (u16 *)(data + 2); - - wsm_cmd_lock(priv); - - WSM_PUT(buf, data + 4, len - 4); /* Skip over header (u16+u16) */ - - ret = wsm_cmd_send(priv, buf, NULL, __le16_to_cpu(*cmd), WSM_CMD_TIMEOUT); - - wsm_cmd_unlock(priv); - return ret; - -nomem: - wsm_cmd_unlock(priv); - return -ENOMEM; -} -#endif /* CONFIG_CW1200_ETF */ - /* ******************************************************************** */ /* WSM TX port control */ @@ -1343,34 +1317,6 @@ int wsm_handle_rx(struct cw1200_common *priv, u16 id, pr_debug("[WSM] <<< 0x%.4X (%td)\n", id, wsm_buf.end - wsm_buf.begin); -#ifdef CONFIG_CW1200_ETF - if (etf_mode) { - struct sk_buff *skb = alloc_skb(wsm_buf.end - wsm_buf.begin, GFP_KERNEL); - - /* Strip out Sequence num before passing up */ - wsm->id = __le16_to_cpu(wsm->id); - wsm->id &= 0x0FFF; - wsm->id = __cpu_to_le16(wsm->id); - - memcpy(skb_put(skb, wsm_buf.end - wsm_buf.begin), - wsm_buf.begin, - wsm_buf.end - wsm_buf.begin); - skb_queue_tail(&priv->etf_q, skb); - - /* Special case for startup */ - if (id == WSM_STARTUP_IND_ID) { - wsm_startup_indication(priv, &wsm_buf); - } else if (id & 0x0400) { - spin_lock(&priv->wsm_cmd.lock); - priv->wsm_cmd.done = 1; - spin_unlock(&priv->wsm_cmd.lock); - wake_up(&priv->wsm_cmd_wq); - } - - goto out; - } -#endif - if (id == WSM_TX_CONFIRM_IND_ID) { ret = wsm_tx_confirm(priv, &wsm_buf, link_id); } else if (id == WSM_MULTI_TX_CONFIRM_ID) { diff --git a/drivers/net/wireless/cw1200/wsm.h b/drivers/net/wireless/cw1200/wsm.h index 8d902d6a7dc4..4689dffa3d89 100644 --- a/drivers/net/wireless/cw1200/wsm.h +++ b/drivers/net/wireless/cw1200/wsm.h @@ -1871,9 +1871,4 @@ static inline u8 wsm_queue_id_to_wsm(u8 queue_id) return queue_mapping[queue_id]; } - -#ifdef CONFIG_CW1200_ETF -int wsm_raw_cmd(struct cw1200_common *priv, u8 *data, size_t len); -#endif - #endif /* CW1200_HWIO_H_INCLUDED */ -- cgit v1.2.3 From 8b3e7be437a6b62118d0485ad971e724afe23fdf Mon Sep 17 00:00:00 2001 From: Solomon Peachy Date: Tue, 11 Jun 2013 09:49:40 -0400 Subject: cw1200: Fix an assorted pile of checkpatch warnings. Signed-off-by: Solomon Peachy Signed-off-by: John W. Linville --- drivers/net/wireless/cw1200/bh.c | 9 +++-- drivers/net/wireless/cw1200/cw1200.h | 3 +- drivers/net/wireless/cw1200/cw1200_sdio.c | 3 +- drivers/net/wireless/cw1200/cw1200_spi.c | 4 +-- drivers/net/wireless/cw1200/hwio.h | 5 ++- drivers/net/wireless/cw1200/sta.c | 30 ++++++++--------- drivers/net/wireless/cw1200/txrx.c | 43 ++++++++++++++---------- drivers/net/wireless/cw1200/wsm.c | 3 +- drivers/net/wireless/cw1200/wsm.h | 55 +++++++++++++++---------------- 9 files changed, 80 insertions(+), 75 deletions(-) diff --git a/drivers/net/wireless/cw1200/bh.c b/drivers/net/wireless/cw1200/bh.c index 324b57001da0..c1ec2a4dd8c0 100644 --- a/drivers/net/wireless/cw1200/bh.c +++ b/drivers/net/wireless/cw1200/bh.c @@ -31,7 +31,8 @@ static int cw1200_bh(void *arg); #define DOWNLOAD_BLOCK_SIZE_WR (0x1000 - 4) /* an SPI message cannot be bigger than (2"12-1)*2 bytes - * "*2" to cvt to bytes */ + * "*2" to cvt to bytes + */ #define MAX_SZ_RD_WR_BUFFERS (DOWNLOAD_BLOCK_SIZE_WR*2) #define PIGGYBACK_CTRL_REG (2) #define EFFECTIVE_BUF_SIZE (MAX_SZ_RD_WR_BUFFERS - PIGGYBACK_CTRL_REG) @@ -217,7 +218,8 @@ static int cw1200_device_wakeup(struct cw1200_common *priv) return ret; /* If the device returns WLAN_RDY as 1, the device is active and will - * remain active. */ + * remain active. + */ if (ctrl_reg & ST90TDS_CONT_RDY_BIT) { pr_debug("[BH] Device awake.\n"); return 1; @@ -262,7 +264,8 @@ static int cw1200_bh_rx_helper(struct cw1200_common *priv, } /* Add SIZE of PIGGYBACK reg (CONTROL Reg) - * to the NEXT Message length + 2 Bytes for SKB */ + * to the NEXT Message length + 2 Bytes for SKB + */ read_len = read_len + 2; alloc_len = priv->hwbus_ops->align_size( diff --git a/drivers/net/wireless/cw1200/cw1200.h b/drivers/net/wireless/cw1200/cw1200.h index 91ff7f19c4f0..243e96353d13 100644 --- a/drivers/net/wireless/cw1200/cw1200.h +++ b/drivers/net/wireless/cw1200/cw1200.h @@ -207,7 +207,8 @@ struct cw1200_common { /* Scan status */ struct cw1200_scan scan; /* Keep cw1200 awake (WUP = 1) 1 second after each scan to avoid - * FW issue with sleeping/waking up. */ + * FW issue with sleeping/waking up. + */ atomic_t recent_scan; struct delayed_work clear_recent_scan_work; diff --git a/drivers/net/wireless/cw1200/cw1200_sdio.c b/drivers/net/wireless/cw1200/cw1200_sdio.c index bb1f405315e4..ebdcdf44f155 100644 --- a/drivers/net/wireless/cw1200/cw1200_sdio.c +++ b/drivers/net/wireless/cw1200/cw1200_sdio.c @@ -323,7 +323,8 @@ static int cw1200_sdio_probe(struct sdio_func *func, } /* Disconnect Function to be called by SDIO stack when - * device is disconnected */ + * device is disconnected + */ static void cw1200_sdio_disconnect(struct sdio_func *func) { struct hwbus_priv *self = sdio_get_drvdata(func); diff --git a/drivers/net/wireless/cw1200/cw1200_spi.c b/drivers/net/wireless/cw1200/cw1200_spi.c index e58f0a5bafa9..953bd1904d3d 100644 --- a/drivers/net/wireless/cw1200/cw1200_spi.c +++ b/drivers/net/wireless/cw1200/cw1200_spi.c @@ -47,15 +47,13 @@ struct hwbus_priv { #define SET_WRITE 0x7FFF /* usage: and operation */ #define SET_READ 0x8000 /* usage: or operation */ -/* - Notes on byte ordering: +/* Notes on byte ordering: LE: B0 B1 B2 B3 BE: B3 B2 B1 B0 Hardware expects 32-bit data to be written as 16-bit BE words: B1 B0 B3 B2 - */ static int cw1200_spi_memcpy_fromio(struct hwbus_priv *self, diff --git a/drivers/net/wireless/cw1200/hwio.h b/drivers/net/wireless/cw1200/hwio.h index 7ee73fe32ccf..563329cfead6 100644 --- a/drivers/net/wireless/cw1200/hwio.h +++ b/drivers/net/wireless/cw1200/hwio.h @@ -97,9 +97,8 @@ struct download_cntl_t { #define CW1200_APB(addr) (PAC_SHARED_MEMORY_SILICON + (addr)) -/* *************************************************************** -*Device register definitions -*************************************************************** */ +/* Device register definitions */ + /* WBF - SPI Register Addresses */ #define ST90TDS_ADDR_ID_BASE (0x0000) /* 16/32 bits */ diff --git a/drivers/net/wireless/cw1200/sta.c b/drivers/net/wireless/cw1200/sta.c index 679c55f15c67..4cd0352b508d 100644 --- a/drivers/net/wireless/cw1200/sta.c +++ b/drivers/net/wireless/cw1200/sta.c @@ -483,15 +483,14 @@ void cw1200_update_filtering(struct cw1200_common *priv) bf_tbl.num = __cpu_to_le32(3); } - /* - * When acting as p2p client being connected to p2p GO, in order to - * receive frames from a different p2p device, turn off bssid filter. - * - * WARNING: FW dependency! - * This can only be used with FW WSM371 and its successors. - * In that FW version even with bssid filter turned off, - * device will block most of the unwanted frames. - */ + /* When acting as p2p client being connected to p2p GO, in order to + * receive frames from a different p2p device, turn off bssid filter. + * + * WARNING: FW dependency! + * This can only be used with FW WSM371 and its successors. + * In that FW version even with bssid filter turned off, + * device will block most of the unwanted frames. + */ if (is_p2p) bssid_filtering = false; @@ -1015,17 +1014,17 @@ void cw1200_event_handler(struct work_struct *work) /* RSSI: signed Q8.0, RCPI: unsigned Q7.1 * RSSI = RCPI / 2 - 110 */ - int rcpiRssi = (int)(event->evt.data & 0xFF); + int rcpi_rssi = (int)(event->evt.data & 0xFF); int cqm_evt; if (priv->cqm_use_rssi) - rcpiRssi = (s8)rcpiRssi; + rcpi_rssi = (s8)rcpi_rssi; else - rcpiRssi = rcpiRssi / 2 - 110; + rcpi_rssi = rcpi_rssi / 2 - 110; - cqm_evt = (rcpiRssi <= priv->cqm_rssi_thold) ? + cqm_evt = (rcpi_rssi <= priv->cqm_rssi_thold) ? NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW : NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH; - pr_debug("[CQM] RSSI event: %d.\n", rcpiRssi); + pr_debug("[CQM] RSSI event: %d.\n", rcpi_rssi); ieee80211_cqm_rssi_notify(priv->vif, cqm_evt, GFP_KERNEL); break; @@ -1068,8 +1067,7 @@ void cw1200_bss_params_work(struct work_struct *work) /* ******************************************************************** */ /* Internal API */ -/* - * This function is called to Parse the SDD file +/* This function is called to Parse the SDD file * to extract listen_interval and PTA related information * sdd is a TLV: u8 id, u8 len, u8 data[] */ diff --git a/drivers/net/wireless/cw1200/txrx.c b/drivers/net/wireless/cw1200/txrx.c index 451d74625f90..44ca10cb0d39 100644 --- a/drivers/net/wireless/cw1200/txrx.c +++ b/drivers/net/wireless/cw1200/txrx.c @@ -75,9 +75,6 @@ static void tx_policy_build(const struct cw1200_common *priv, BUG_ON(rates[0].idx < 0); memset(policy, 0, sizeof(*policy)); - /* minstrel is buggy a little bit, so distille - * incoming rates first. */ - /* Sort rates in descending order. */ for (i = 1; i < count; ++i) { if (rates[i].idx < 0) { @@ -108,7 +105,8 @@ static void tx_policy_build(const struct cw1200_common *priv, count = i + 1; /* Re-fill policy trying to keep every requested rate and with - * respect to the global max tx retransmission count. */ + * respect to the global max tx retransmission count. + */ if (limit < count) limit = count; if (total > limit) { @@ -129,7 +127,6 @@ static void tx_policy_build(const struct cw1200_common *priv, if (count == 2 && !(rates[0].flags & IEEE80211_TX_RC_MCS) && rates[0].idx > 4 && rates[0].count > 2 && rates[1].idx < 2) { - /* ">> 1" is an equivalent of "/ 2", but faster */ int mid_rate = (rates[0].idx + 4) >> 1; /* Decrease number of retries for the initial rate */ @@ -151,7 +148,8 @@ static void tx_policy_build(const struct cw1200_common *priv, /* Fallback to 1 Mbps is a really bad thing, * so let's try to increase probability of * successful transmission on the lowest g rate - * even more */ + * even more + */ if (rates[0].count >= 3) { --rates[0].count; ++rates[2].count; @@ -220,7 +218,8 @@ static int tx_policy_find(struct tx_policy_cache *cache, { /* O(n) complexity. Not so good, but there's only 8 entries in * the cache. - * Also lru helps to reduce search time. */ + * Also lru helps to reduce search time. + */ struct tx_policy_cache_entry *it; /* First search for policy in "used" list */ list_for_each_entry(it, &cache->used, link) { @@ -264,7 +263,8 @@ void tx_policy_clean(struct cw1200_common *priv) for (idx = 0; idx < TX_POLICY_CACHE_SIZE; idx++) { entry = &cache->cache[idx]; /* Policy usage count should be 0 at this time as all queues - should be empty */ + should be empty + */ if (WARN_ON(entry->policy.usage_count)) { entry->policy.usage_count = 0; list_move(&entry->link, &cache->free); @@ -319,7 +319,8 @@ static int tx_policy_get(struct cw1200_common *priv, struct tx_policy_cache_entry *entry; *renew = true; /* If policy is not found create a new one - * using the oldest entry in "free" list */ + * using the oldest entry in "free" list + */ entry = list_entry(cache->free.prev, struct tx_policy_cache_entry, link); entry->policy = wanted; @@ -612,7 +613,8 @@ cw1200_tx_h_bt(struct cw1200_common *priv, priv->listen_interval, mgt_frame->u.assoc_req.listen_interval); /* Replace listen interval derieved from - * the one read from SDD */ + * the one read from SDD + */ mgt_frame->u.assoc_req.listen_interval = priv->listen_interval; } @@ -667,7 +669,8 @@ cw1200_tx_h_rate_policy(struct cw1200_common *priv, pr_debug("[TX] TX policy renew.\n"); /* It's not so optimal to stop TX queues every now and then. * Better to reimplement task scheduling with - * a counter. TODO. */ + * a counter. TODO. + */ wsm_lock_tx_async(priv); cw1200_tx_queues_lock(priv); if (queue_work(priv->workqueue, @@ -832,8 +835,7 @@ static int cw1200_handle_pspoll(struct cw1200_common *priv, priv->pspoll_mask |= pspoll_mask; drop = 0; - /* Do not report pspols if data for given link id is - * queued already. */ + /* Do not report pspols if data for given link id is queued already. */ for (i = 0; i < 4; ++i) { if (cw1200_queue_get_num_queued(&priv->tx_queue[i], pspoll_mask)) { @@ -924,7 +926,8 @@ void cw1200_tx_confirm_cb(struct cw1200_common *priv, cw1200_debug_txed(priv); if (arg->flags & WSM_TX_STATUS_AGGREGATION) { /* Do not report aggregation to mac80211: - * it confuses minstrel a lot. */ + * it confuses minstrel a lot. + */ /* tx->flags |= IEEE80211_TX_STAT_AMPDU; */ cw1200_debug_txed_agg(priv); } @@ -1044,7 +1047,8 @@ void cw1200_rx_cb(struct cw1200_common *priv, ieee80211_is_action(frame->frame_control) && (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) { /* Link ID already exists for the ACTION frame. - * Reset and Remap */ + * Reset and Remap + */ WARN_ON(work_pending(&priv->linkid_reset_work)); memcpy(&priv->action_frame_sa[0], ieee80211_get_SA(frame), ETH_ALEN); @@ -1074,7 +1078,6 @@ void cw1200_rx_cb(struct cw1200_common *priv, if (cw1200_handle_pspoll(priv, skb)) goto drop; - hdr->mactime = 0; /* Not supported by WSM */ hdr->band = ((arg->channel_number & 0xff00) || (arg->channel_number > 14)) ? IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ; @@ -1102,7 +1105,8 @@ void cw1200_rx_cb(struct cw1200_common *priv, hdr->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED; /* Oops... There is no fast way to ask mac80211 about - * IV/ICV lengths. Even defineas are not exposed.*/ + * IV/ICV lengths. Even defineas are not exposed. + */ switch (WSM_RX_STATUS_ENCRYPTION(arg->flags)) { case WSM_RX_STATUS_WEP: iv_len = 4 /* WEP_IV_LEN */; @@ -1149,6 +1153,8 @@ void cw1200_rx_cb(struct cw1200_common *priv, hdr->mactime = le64_to_cpu(hdr->mactime); if (skb->len >= 8) skb_trim(skb, skb->len - 8); + } else { + hdr->mactime = 0; } cw1200_debug_rxed(priv); @@ -1192,7 +1198,8 @@ void cw1200_rx_cb(struct cw1200_common *priv, /* Stay awake after frame is received to give * userspace chance to react and acquire appropriate - * wakelock. */ + * wakelock. + */ if (ieee80211_is_auth(frame->frame_control)) grace_period = 5 * HZ; else if (ieee80211_is_deauth(frame->frame_control)) diff --git a/drivers/net/wireless/cw1200/wsm.c b/drivers/net/wireless/cw1200/wsm.c index d185f29efb7e..d95094fdcc50 100644 --- a/drivers/net/wireless/cw1200/wsm.c +++ b/drivers/net/wireless/cw1200/wsm.c @@ -1106,8 +1106,7 @@ static int wsm_cmd_send(struct cw1200_common *priv, else pr_debug("[WSM] >>> 0x%.4X (%zu)\n", cmd, buf_len); - /* - * Due to buggy SPI on CW1200, we need to + /* Due to buggy SPI on CW1200, we need to * pad the message by a few bytes to ensure * that it's completely received. */ diff --git a/drivers/net/wireless/cw1200/wsm.h b/drivers/net/wireless/cw1200/wsm.h index 4689dffa3d89..2816171f7a1d 100644 --- a/drivers/net/wireless/cw1200/wsm.h +++ b/drivers/net/wireless/cw1200/wsm.h @@ -314,13 +314,16 @@ struct cw1200_common; #define WSM_JOIN_FLAGS_P2P_GO BIT(1) /* Force to join BSS with the BSSID and the * SSID specified without waiting for beacons. The - * ProbeForJoin parameter is ignored. */ + * ProbeForJoin parameter is ignored. + */ #define WSM_JOIN_FLAGS_FORCE BIT(2) /* Give probe request/response higher - * priority over the BT traffic */ + * priority over the BT traffic + */ #define WSM_JOIN_FLAGS_PRIO BIT(3) /* Issue immediate join confirmation and use - * join complete to notify about completion */ + * join complete to notify about completion + */ #define WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND BIT(5) /* Key types */ @@ -1015,22 +1018,19 @@ struct wsm_add_key { u16 reserved; union { struct { - u8 peer[6]; /* MAC address of the - * peer station */ + u8 peer[6]; /* MAC address of the peer station */ u8 reserved; u8 keylen; /* Key length in bytes */ u8 keydata[16]; /* Key data */ } __packed wep_pairwise; struct { - u8 keyid; /* Unique per key identifier - * (0..3) */ + u8 keyid; /* Unique per key identifier (0..3) */ u8 keylen; /* Key length in bytes */ u16 reserved; u8 keydata[16]; /* Key data */ } __packed wep_group; struct { - u8 peer[6]; /* MAC address of the - * peer station */ + u8 peer[6]; /* MAC address of the peer station */ u16 reserved; u8 keydata[16]; /* TKIP key data */ u8 rx_mic_key[8]; /* Rx MIC key */ @@ -1044,8 +1044,7 @@ struct wsm_add_key { u8 rx_seqnum[8]; /* Receive Sequence Counter */ } __packed tkip_group; struct { - u8 peer[6]; /* MAC address of the - * peer station */ + u8 peer[6]; /* MAC address of the peer station */ u16 reserved; u8 keydata[16]; /* AES key data */ } __packed aes_pairwise; @@ -1056,8 +1055,7 @@ struct wsm_add_key { u8 rx_seqnum[8]; /* Receive Sequence Counter */ } __packed aes_group; struct { - u8 peer[6]; /* MAC address of the - * peer station */ + u8 peer[6]; /* MAC address of the peer station */ u8 keyid; /* Key ID */ u8 reserved; u8 keydata[16]; /* WAPI key data */ @@ -1550,7 +1548,8 @@ struct wsm_tx_rate_retry_policy { * finishes. * BIT(3) - Count initial frame transmission as part of * rate retry counting but not as a retry - * attempt */ + * attempt + */ u8 flags; u8 rate_recoveries; u8 reserved[3]; @@ -1618,24 +1617,24 @@ static inline int wsm_set_udp_port_filter(struct cw1200_common *priv, #define D11_MAX_SSID_LEN (32) struct wsm_p2p_device_type { - __le16 categoryId; + __le16 category_id; u8 oui[4]; - __le16 subCategoryId; + __le16 subcategory_id; } __packed; struct wsm_p2p_device_info { struct wsm_p2p_device_type primaryDevice; u8 reserved1[3]; - u8 devNameSize; - u8 localDevName[D11_MAX_SSID_LEN]; + u8 devname_size; + u8 local_devname[D11_MAX_SSID_LEN]; u8 reserved2[3]; - u8 numSecDevSupported; - struct wsm_p2p_device_type secondaryDevices[0]; + u8 num_secdev_supported; + struct wsm_p2p_device_type secdevs[0]; } __packed; /* 4.36 SetWCDMABand - WO */ struct wsm_cdma_band { - u8 WCDMA_Band; + u8 wcdma_band; u8 reserved[3]; } __packed; @@ -1668,19 +1667,19 @@ struct wsm_ht_protection { #define WSM_GPIO_ALL_PINS 0xFF struct wsm_gpio_command { - u8 GPIO_Command; + u8 command; u8 pin; __le16 config; } __packed; /* 4.41 TSFCounter - RO */ struct wsm_tsf_counter { - __le64 TSF_Counter; + __le64 tsf_counter; } __packed; /* 4.43 Keep alive period */ struct wsm_keep_alive_period { - __le16 keepAlivePeriod; + __le16 period; u8 reserved[2]; } __packed; @@ -1688,7 +1687,7 @@ static inline int wsm_keep_alive_period(struct cw1200_common *priv, int period) { struct wsm_keep_alive_period arg = { - .keepAlivePeriod = __cpu_to_le16(period), + .period = __cpu_to_le16(period), }; return wsm_write_mib(priv, WSM_MIB_ID_KEEP_ALIVE_PERIOD, &arg, sizeof(arg)); @@ -1739,13 +1738,13 @@ static inline int wsm_set_arp_ipv4_filter(struct cw1200_common *priv, /* P2P Power Save Mode Info - 4.31 */ struct wsm_p2p_ps_modeinfo { - u8 oppPsCTWindow; + u8 opp_ps_ct_window; u8 count; u8 reserved; - u8 dtimCount; + u8 dtim_count; __le32 duration; __le32 interval; - __le32 startTime; + __le32 start_time; } __packed; static inline int wsm_set_p2p_ps_modeinfo(struct cw1200_common *priv, -- cgit v1.2.3 From 9ab6d02fddc6831b166812956ff387d7112ff626 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Mon, 10 Jun 2013 19:34:41 +0100 Subject: arm64: pgtable: use pte_index instead of __pte_index pte_index is a useful helper outside of arch/arm64, for things like the ARM SMMU driver, so rename __pte_index to pte_index to be consistent with both arch/arm/ and also the definitions of pmd_index and pgd_index. Signed-off-by: Will Deacon Signed-off-by: Catalin Marinas --- arch/arm64/include/asm/pgtable.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h index e333a243bfcc..b93bc2326f56 100644 --- a/arch/arm64/include/asm/pgtable.h +++ b/arch/arm64/include/asm/pgtable.h @@ -119,7 +119,7 @@ extern struct page *empty_zero_page; #define pte_none(pte) (!pte_val(pte)) #define pte_clear(mm,addr,ptep) set_pte(ptep, __pte(0)) #define pte_page(pte) (pfn_to_page(pte_pfn(pte))) -#define pte_offset_kernel(dir,addr) (pmd_page_vaddr(*(dir)) + __pte_index(addr)) +#define pte_offset_kernel(dir,addr) (pmd_page_vaddr(*(dir)) + pte_index(addr)) #define pte_offset_map(dir,addr) pte_offset_kernel((dir), (addr)) #define pte_offset_map_nested(dir,addr) pte_offset_kernel((dir), (addr)) @@ -263,7 +263,7 @@ static inline pmd_t *pmd_offset(pud_t *pud, unsigned long addr) #endif /* Find an entry in the third-level page table.. */ -#define __pte_index(addr) (((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1)) +#define pte_index(addr) (((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1)) static inline pte_t pte_modify(pte_t pte, pgprot_t newprot) { -- cgit v1.2.3 From 73150c983ac1f9b7653cfd3823b1ad4a44aad3bf Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Mon, 10 Jun 2013 19:34:42 +0100 Subject: arm64: device: add iommu pointer to device archdata When using an IOMMU for device mappings, it is necessary to keep a pointer between the device and the IOMMU to which it is attached in order to obtain the correct IOMMU when attaching the device to a domain. This patch adds an iommu pointer to the dev_archdata structure, in a similar manner to other architectures (ARM, PowerPC, x86, ...). Signed-off-by: Will Deacon Signed-off-by: Catalin Marinas --- arch/arm64/include/asm/device.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm64/include/asm/device.h b/arch/arm64/include/asm/device.h index 0d8453c755a8..cf98b362094b 100644 --- a/arch/arm64/include/asm/device.h +++ b/arch/arm64/include/asm/device.h @@ -18,6 +18,9 @@ struct dev_archdata { struct dma_map_ops *dma_ops; +#ifdef CONFIG_IOMMU_API + void *iommu; /* private IOMMU data */ +#endif }; struct pdev_archdata { -- cgit v1.2.3 From 45fcac1aad5d092f46fc95230aad818abb221b34 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 30 Apr 2013 00:00:19 +0200 Subject: mfd: Move ssbi driver into drivers/mfd There is no reason for ssbi to have its own top-level driver directory when the only users of this interface are all MFD drivers. The only mainline driver using it at the moment (PM8921) is marked broken and in fact does not compile. I have verified that fixing the trivial build breakage in pm8921 links in the new ssbi code just fine, but that can be a separate patch. Signed-off-by: Arnd Bergmann Acked-by: Greg Kroah-Hartman Acked-by: Nicolas Pitre Acked-by: David Brown Signed-off-by: Samuel Ortiz --- drivers/Kconfig | 2 - drivers/Makefile | 1 - drivers/mfd/Kconfig | 3 +- drivers/mfd/Makefile | 2 +- drivers/mfd/ssbi.c | 379 ++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/ssbi/Kconfig | 16 --- drivers/ssbi/Makefile | 1 - drivers/ssbi/ssbi.c | 379 -------------------------------------------------- 8 files changed, 382 insertions(+), 401 deletions(-) create mode 100644 drivers/mfd/ssbi.c delete mode 100644 drivers/ssbi/Kconfig delete mode 100644 drivers/ssbi/Makefile delete mode 100644 drivers/ssbi/ssbi.c diff --git a/drivers/Kconfig b/drivers/Kconfig index 9953a42809ec..b8ec9cf1cfb2 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -52,8 +52,6 @@ source "drivers/i2c/Kconfig" source "drivers/spi/Kconfig" -source "drivers/ssbi/Kconfig" - source "drivers/hsi/Kconfig" source "drivers/pps/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 130abc1dfd65..bbf3810d0d49 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -117,7 +117,6 @@ obj-y += firmware/ obj-$(CONFIG_CRYPTO) += crypto/ obj-$(CONFIG_SUPERH) += sh/ obj-$(CONFIG_ARCH_SHMOBILE) += sh/ -obj-$(CONFIG_SSBI) += ssbi/ ifndef CONFIG_ARCH_USES_GETTIMEOFFSET obj-y += clocksource/ endif diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index d54e985748b7..013b094f0f9e 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -419,7 +419,8 @@ config MFD_PM8XXX config MFD_PM8921_CORE tristate "Qualcomm PM8921 PMIC chip" - depends on SSBI && BROKEN + depends on (ARCH_MSM || HEXAGON) + depends on BROKEN select MFD_CORE select MFD_PM8XXX help diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 718e94a2a9a7..31d0f97d6e50 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -140,7 +140,7 @@ obj-$(CONFIG_MFD_SI476X_CORE) += si476x-core.o obj-$(CONFIG_MFD_CS5535) += cs5535-mfd.o obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o omap-usb-tll.o -obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o +obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o ssbi.o obj-$(CONFIG_MFD_PM8XXX_IRQ) += pm8xxx-irq.o obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o obj-$(CONFIG_MFD_TPS65090) += tps65090.o diff --git a/drivers/mfd/ssbi.c b/drivers/mfd/ssbi.c new file mode 100644 index 000000000000..f32da0258a8e --- /dev/null +++ b/drivers/mfd/ssbi.c @@ -0,0 +1,379 @@ +/* Copyright (c) 2009-2013, The Linux Foundation. All rights reserved. + * Copyright (c) 2010, Google Inc. + * + * Original authors: Code Aurora Forum + * + * Author: Dima Zavin + * - Largely rewritten from original to not be an i2c driver. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* SSBI 2.0 controller registers */ +#define SSBI2_CMD 0x0008 +#define SSBI2_RD 0x0010 +#define SSBI2_STATUS 0x0014 +#define SSBI2_MODE2 0x001C + +/* SSBI_CMD fields */ +#define SSBI_CMD_RDWRN (1 << 24) + +/* SSBI_STATUS fields */ +#define SSBI_STATUS_RD_READY (1 << 2) +#define SSBI_STATUS_READY (1 << 1) +#define SSBI_STATUS_MCHN_BUSY (1 << 0) + +/* SSBI_MODE2 fields */ +#define SSBI_MODE2_REG_ADDR_15_8_SHFT 0x04 +#define SSBI_MODE2_REG_ADDR_15_8_MASK (0x7f << SSBI_MODE2_REG_ADDR_15_8_SHFT) + +#define SET_SSBI_MODE2_REG_ADDR_15_8(MD, AD) \ + (((MD) & 0x0F) | ((((AD) >> 8) << SSBI_MODE2_REG_ADDR_15_8_SHFT) & \ + SSBI_MODE2_REG_ADDR_15_8_MASK)) + +/* SSBI PMIC Arbiter command registers */ +#define SSBI_PA_CMD 0x0000 +#define SSBI_PA_RD_STATUS 0x0004 + +/* SSBI_PA_CMD fields */ +#define SSBI_PA_CMD_RDWRN (1 << 24) +#define SSBI_PA_CMD_ADDR_MASK 0x7fff /* REG_ADDR_7_0, REG_ADDR_8_14*/ + +/* SSBI_PA_RD_STATUS fields */ +#define SSBI_PA_RD_STATUS_TRANS_DONE (1 << 27) +#define SSBI_PA_RD_STATUS_TRANS_DENIED (1 << 26) + +#define SSBI_TIMEOUT_US 100 + +struct ssbi { + struct device *slave; + void __iomem *base; + spinlock_t lock; + enum ssbi_controller_type controller_type; + int (*read)(struct ssbi *, u16 addr, u8 *buf, int len); + int (*write)(struct ssbi *, u16 addr, u8 *buf, int len); +}; + +#define to_ssbi(dev) platform_get_drvdata(to_platform_device(dev)) + +static inline u32 ssbi_readl(struct ssbi *ssbi, u32 reg) +{ + return readl(ssbi->base + reg); +} + +static inline void ssbi_writel(struct ssbi *ssbi, u32 val, u32 reg) +{ + writel(val, ssbi->base + reg); +} + +/* + * Via private exchange with one of the original authors, the hardware + * should generally finish a transaction in about 5us. The worst + * case, is when using the arbiter and both other CPUs have just + * started trying to use the SSBI bus will result in a time of about + * 20us. It should never take longer than this. + * + * As such, this wait merely spins, with a udelay. + */ +static int ssbi_wait_mask(struct ssbi *ssbi, u32 set_mask, u32 clr_mask) +{ + u32 timeout = SSBI_TIMEOUT_US; + u32 val; + + while (timeout--) { + val = ssbi_readl(ssbi, SSBI2_STATUS); + if (((val & set_mask) == set_mask) && ((val & clr_mask) == 0)) + return 0; + udelay(1); + } + + return -ETIMEDOUT; +} + +static int +ssbi_read_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len) +{ + u32 cmd = SSBI_CMD_RDWRN | ((addr & 0xff) << 16); + int ret = 0; + + if (ssbi->controller_type == MSM_SBI_CTRL_SSBI2) { + u32 mode2 = ssbi_readl(ssbi, SSBI2_MODE2); + mode2 = SET_SSBI_MODE2_REG_ADDR_15_8(mode2, addr); + ssbi_writel(ssbi, mode2, SSBI2_MODE2); + } + + while (len) { + ret = ssbi_wait_mask(ssbi, SSBI_STATUS_READY, 0); + if (ret) + goto err; + + ssbi_writel(ssbi, cmd, SSBI2_CMD); + ret = ssbi_wait_mask(ssbi, SSBI_STATUS_RD_READY, 0); + if (ret) + goto err; + *buf++ = ssbi_readl(ssbi, SSBI2_RD) & 0xff; + len--; + } + +err: + return ret; +} + +static int +ssbi_write_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len) +{ + int ret = 0; + + if (ssbi->controller_type == MSM_SBI_CTRL_SSBI2) { + u32 mode2 = ssbi_readl(ssbi, SSBI2_MODE2); + mode2 = SET_SSBI_MODE2_REG_ADDR_15_8(mode2, addr); + ssbi_writel(ssbi, mode2, SSBI2_MODE2); + } + + while (len) { + ret = ssbi_wait_mask(ssbi, SSBI_STATUS_READY, 0); + if (ret) + goto err; + + ssbi_writel(ssbi, ((addr & 0xff) << 16) | *buf, SSBI2_CMD); + ret = ssbi_wait_mask(ssbi, 0, SSBI_STATUS_MCHN_BUSY); + if (ret) + goto err; + buf++; + len--; + } + +err: + return ret; +} + +/* + * See ssbi_wait_mask for an explanation of the time and the + * busywait. + */ +static inline int +ssbi_pa_transfer(struct ssbi *ssbi, u32 cmd, u8 *data) +{ + u32 timeout = SSBI_TIMEOUT_US; + u32 rd_status = 0; + + ssbi_writel(ssbi, cmd, SSBI_PA_CMD); + + while (timeout--) { + rd_status = ssbi_readl(ssbi, SSBI_PA_RD_STATUS); + + if (rd_status & SSBI_PA_RD_STATUS_TRANS_DENIED) + return -EPERM; + + if (rd_status & SSBI_PA_RD_STATUS_TRANS_DONE) { + if (data) + *data = rd_status & 0xff; + return 0; + } + udelay(1); + } + + return -ETIMEDOUT; +} + +static int +ssbi_pa_read_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len) +{ + u32 cmd; + int ret = 0; + + cmd = SSBI_PA_CMD_RDWRN | (addr & SSBI_PA_CMD_ADDR_MASK) << 8; + + while (len) { + ret = ssbi_pa_transfer(ssbi, cmd, buf); + if (ret) + goto err; + buf++; + len--; + } + +err: + return ret; +} + +static int +ssbi_pa_write_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len) +{ + u32 cmd; + int ret = 0; + + while (len) { + cmd = (addr & SSBI_PA_CMD_ADDR_MASK) << 8 | *buf; + ret = ssbi_pa_transfer(ssbi, cmd, NULL); + if (ret) + goto err; + buf++; + len--; + } + +err: + return ret; +} + +int ssbi_read(struct device *dev, u16 addr, u8 *buf, int len) +{ + struct ssbi *ssbi = to_ssbi(dev); + unsigned long flags; + int ret; + + spin_lock_irqsave(&ssbi->lock, flags); + ret = ssbi->read(ssbi, addr, buf, len); + spin_unlock_irqrestore(&ssbi->lock, flags); + + return ret; +} +EXPORT_SYMBOL_GPL(ssbi_read); + +int ssbi_write(struct device *dev, u16 addr, u8 *buf, int len) +{ + struct ssbi *ssbi = to_ssbi(dev); + unsigned long flags; + int ret; + + spin_lock_irqsave(&ssbi->lock, flags); + ret = ssbi->write(ssbi, addr, buf, len); + spin_unlock_irqrestore(&ssbi->lock, flags); + + return ret; +} +EXPORT_SYMBOL_GPL(ssbi_write); + +static int ssbi_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct resource *mem_res; + struct ssbi *ssbi; + int ret = 0; + const char *type; + + ssbi = kzalloc(sizeof(struct ssbi), GFP_KERNEL); + if (!ssbi) { + pr_err("can not allocate ssbi_data\n"); + return -ENOMEM; + } + + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem_res) { + pr_err("missing mem resource\n"); + ret = -EINVAL; + goto err_get_mem_res; + } + + ssbi->base = ioremap(mem_res->start, resource_size(mem_res)); + if (!ssbi->base) { + pr_err("ioremap of 0x%p failed\n", (void *)mem_res->start); + ret = -EINVAL; + goto err_ioremap; + } + platform_set_drvdata(pdev, ssbi); + + type = of_get_property(np, "qcom,controller-type", NULL); + if (type == NULL) { + pr_err("Missing qcom,controller-type property\n"); + ret = -EINVAL; + goto err_ssbi_controller; + } + dev_info(&pdev->dev, "SSBI controller type: '%s'\n", type); + if (strcmp(type, "ssbi") == 0) + ssbi->controller_type = MSM_SBI_CTRL_SSBI; + else if (strcmp(type, "ssbi2") == 0) + ssbi->controller_type = MSM_SBI_CTRL_SSBI2; + else if (strcmp(type, "pmic-arbiter") == 0) + ssbi->controller_type = MSM_SBI_CTRL_PMIC_ARBITER; + else { + pr_err("Unknown qcom,controller-type\n"); + ret = -EINVAL; + goto err_ssbi_controller; + } + + if (ssbi->controller_type == MSM_SBI_CTRL_PMIC_ARBITER) { + ssbi->read = ssbi_pa_read_bytes; + ssbi->write = ssbi_pa_write_bytes; + } else { + ssbi->read = ssbi_read_bytes; + ssbi->write = ssbi_write_bytes; + } + + spin_lock_init(&ssbi->lock); + + ret = of_platform_populate(np, NULL, NULL, &pdev->dev); + if (ret) + goto err_ssbi_controller; + + return 0; + +err_ssbi_controller: + platform_set_drvdata(pdev, NULL); + iounmap(ssbi->base); +err_ioremap: +err_get_mem_res: + kfree(ssbi); + return ret; +} + +static int ssbi_remove(struct platform_device *pdev) +{ + struct ssbi *ssbi = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + iounmap(ssbi->base); + kfree(ssbi); + return 0; +} + +static struct of_device_id ssbi_match_table[] = { + { .compatible = "qcom,ssbi" }, + {} +}; + +static struct platform_driver ssbi_driver = { + .probe = ssbi_probe, + .remove = ssbi_remove, + .driver = { + .name = "ssbi", + .owner = THIS_MODULE, + .of_match_table = ssbi_match_table, + }, +}; + +static int __init ssbi_init(void) +{ + return platform_driver_register(&ssbi_driver); +} +module_init(ssbi_init); + +static void __exit ssbi_exit(void) +{ + platform_driver_unregister(&ssbi_driver); +} +module_exit(ssbi_exit) + +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("1.0"); +MODULE_ALIAS("platform:ssbi"); +MODULE_AUTHOR("Dima Zavin "); diff --git a/drivers/ssbi/Kconfig b/drivers/ssbi/Kconfig deleted file mode 100644 index 1ae4040afedd..000000000000 --- a/drivers/ssbi/Kconfig +++ /dev/null @@ -1,16 +0,0 @@ -# -# SSBI bus support -# - -menu "Qualcomm MSM SSBI bus support" - -config SSBI - tristate "Qualcomm Single-wire Serial Bus Interface (SSBI)" - help - If you say yes to this option, support will be included for the - built-in SSBI interface on Qualcomm MSM family processors. - - This is required for communicating with Qualcomm PMICs and - other devices that have the SSBI interface. - -endmenu diff --git a/drivers/ssbi/Makefile b/drivers/ssbi/Makefile deleted file mode 100644 index 38fb70c31caf..000000000000 --- a/drivers/ssbi/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-$(CONFIG_SSBI) += ssbi.o diff --git a/drivers/ssbi/ssbi.c b/drivers/ssbi/ssbi.c deleted file mode 100644 index f32da0258a8e..000000000000 --- a/drivers/ssbi/ssbi.c +++ /dev/null @@ -1,379 +0,0 @@ -/* Copyright (c) 2009-2013, The Linux Foundation. All rights reserved. - * Copyright (c) 2010, Google Inc. - * - * Original authors: Code Aurora Forum - * - * Author: Dima Zavin - * - Largely rewritten from original to not be an i2c driver. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#define pr_fmt(fmt) "%s: " fmt, __func__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* SSBI 2.0 controller registers */ -#define SSBI2_CMD 0x0008 -#define SSBI2_RD 0x0010 -#define SSBI2_STATUS 0x0014 -#define SSBI2_MODE2 0x001C - -/* SSBI_CMD fields */ -#define SSBI_CMD_RDWRN (1 << 24) - -/* SSBI_STATUS fields */ -#define SSBI_STATUS_RD_READY (1 << 2) -#define SSBI_STATUS_READY (1 << 1) -#define SSBI_STATUS_MCHN_BUSY (1 << 0) - -/* SSBI_MODE2 fields */ -#define SSBI_MODE2_REG_ADDR_15_8_SHFT 0x04 -#define SSBI_MODE2_REG_ADDR_15_8_MASK (0x7f << SSBI_MODE2_REG_ADDR_15_8_SHFT) - -#define SET_SSBI_MODE2_REG_ADDR_15_8(MD, AD) \ - (((MD) & 0x0F) | ((((AD) >> 8) << SSBI_MODE2_REG_ADDR_15_8_SHFT) & \ - SSBI_MODE2_REG_ADDR_15_8_MASK)) - -/* SSBI PMIC Arbiter command registers */ -#define SSBI_PA_CMD 0x0000 -#define SSBI_PA_RD_STATUS 0x0004 - -/* SSBI_PA_CMD fields */ -#define SSBI_PA_CMD_RDWRN (1 << 24) -#define SSBI_PA_CMD_ADDR_MASK 0x7fff /* REG_ADDR_7_0, REG_ADDR_8_14*/ - -/* SSBI_PA_RD_STATUS fields */ -#define SSBI_PA_RD_STATUS_TRANS_DONE (1 << 27) -#define SSBI_PA_RD_STATUS_TRANS_DENIED (1 << 26) - -#define SSBI_TIMEOUT_US 100 - -struct ssbi { - struct device *slave; - void __iomem *base; - spinlock_t lock; - enum ssbi_controller_type controller_type; - int (*read)(struct ssbi *, u16 addr, u8 *buf, int len); - int (*write)(struct ssbi *, u16 addr, u8 *buf, int len); -}; - -#define to_ssbi(dev) platform_get_drvdata(to_platform_device(dev)) - -static inline u32 ssbi_readl(struct ssbi *ssbi, u32 reg) -{ - return readl(ssbi->base + reg); -} - -static inline void ssbi_writel(struct ssbi *ssbi, u32 val, u32 reg) -{ - writel(val, ssbi->base + reg); -} - -/* - * Via private exchange with one of the original authors, the hardware - * should generally finish a transaction in about 5us. The worst - * case, is when using the arbiter and both other CPUs have just - * started trying to use the SSBI bus will result in a time of about - * 20us. It should never take longer than this. - * - * As such, this wait merely spins, with a udelay. - */ -static int ssbi_wait_mask(struct ssbi *ssbi, u32 set_mask, u32 clr_mask) -{ - u32 timeout = SSBI_TIMEOUT_US; - u32 val; - - while (timeout--) { - val = ssbi_readl(ssbi, SSBI2_STATUS); - if (((val & set_mask) == set_mask) && ((val & clr_mask) == 0)) - return 0; - udelay(1); - } - - return -ETIMEDOUT; -} - -static int -ssbi_read_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len) -{ - u32 cmd = SSBI_CMD_RDWRN | ((addr & 0xff) << 16); - int ret = 0; - - if (ssbi->controller_type == MSM_SBI_CTRL_SSBI2) { - u32 mode2 = ssbi_readl(ssbi, SSBI2_MODE2); - mode2 = SET_SSBI_MODE2_REG_ADDR_15_8(mode2, addr); - ssbi_writel(ssbi, mode2, SSBI2_MODE2); - } - - while (len) { - ret = ssbi_wait_mask(ssbi, SSBI_STATUS_READY, 0); - if (ret) - goto err; - - ssbi_writel(ssbi, cmd, SSBI2_CMD); - ret = ssbi_wait_mask(ssbi, SSBI_STATUS_RD_READY, 0); - if (ret) - goto err; - *buf++ = ssbi_readl(ssbi, SSBI2_RD) & 0xff; - len--; - } - -err: - return ret; -} - -static int -ssbi_write_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len) -{ - int ret = 0; - - if (ssbi->controller_type == MSM_SBI_CTRL_SSBI2) { - u32 mode2 = ssbi_readl(ssbi, SSBI2_MODE2); - mode2 = SET_SSBI_MODE2_REG_ADDR_15_8(mode2, addr); - ssbi_writel(ssbi, mode2, SSBI2_MODE2); - } - - while (len) { - ret = ssbi_wait_mask(ssbi, SSBI_STATUS_READY, 0); - if (ret) - goto err; - - ssbi_writel(ssbi, ((addr & 0xff) << 16) | *buf, SSBI2_CMD); - ret = ssbi_wait_mask(ssbi, 0, SSBI_STATUS_MCHN_BUSY); - if (ret) - goto err; - buf++; - len--; - } - -err: - return ret; -} - -/* - * See ssbi_wait_mask for an explanation of the time and the - * busywait. - */ -static inline int -ssbi_pa_transfer(struct ssbi *ssbi, u32 cmd, u8 *data) -{ - u32 timeout = SSBI_TIMEOUT_US; - u32 rd_status = 0; - - ssbi_writel(ssbi, cmd, SSBI_PA_CMD); - - while (timeout--) { - rd_status = ssbi_readl(ssbi, SSBI_PA_RD_STATUS); - - if (rd_status & SSBI_PA_RD_STATUS_TRANS_DENIED) - return -EPERM; - - if (rd_status & SSBI_PA_RD_STATUS_TRANS_DONE) { - if (data) - *data = rd_status & 0xff; - return 0; - } - udelay(1); - } - - return -ETIMEDOUT; -} - -static int -ssbi_pa_read_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len) -{ - u32 cmd; - int ret = 0; - - cmd = SSBI_PA_CMD_RDWRN | (addr & SSBI_PA_CMD_ADDR_MASK) << 8; - - while (len) { - ret = ssbi_pa_transfer(ssbi, cmd, buf); - if (ret) - goto err; - buf++; - len--; - } - -err: - return ret; -} - -static int -ssbi_pa_write_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len) -{ - u32 cmd; - int ret = 0; - - while (len) { - cmd = (addr & SSBI_PA_CMD_ADDR_MASK) << 8 | *buf; - ret = ssbi_pa_transfer(ssbi, cmd, NULL); - if (ret) - goto err; - buf++; - len--; - } - -err: - return ret; -} - -int ssbi_read(struct device *dev, u16 addr, u8 *buf, int len) -{ - struct ssbi *ssbi = to_ssbi(dev); - unsigned long flags; - int ret; - - spin_lock_irqsave(&ssbi->lock, flags); - ret = ssbi->read(ssbi, addr, buf, len); - spin_unlock_irqrestore(&ssbi->lock, flags); - - return ret; -} -EXPORT_SYMBOL_GPL(ssbi_read); - -int ssbi_write(struct device *dev, u16 addr, u8 *buf, int len) -{ - struct ssbi *ssbi = to_ssbi(dev); - unsigned long flags; - int ret; - - spin_lock_irqsave(&ssbi->lock, flags); - ret = ssbi->write(ssbi, addr, buf, len); - spin_unlock_irqrestore(&ssbi->lock, flags); - - return ret; -} -EXPORT_SYMBOL_GPL(ssbi_write); - -static int ssbi_probe(struct platform_device *pdev) -{ - struct device_node *np = pdev->dev.of_node; - struct resource *mem_res; - struct ssbi *ssbi; - int ret = 0; - const char *type; - - ssbi = kzalloc(sizeof(struct ssbi), GFP_KERNEL); - if (!ssbi) { - pr_err("can not allocate ssbi_data\n"); - return -ENOMEM; - } - - mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem_res) { - pr_err("missing mem resource\n"); - ret = -EINVAL; - goto err_get_mem_res; - } - - ssbi->base = ioremap(mem_res->start, resource_size(mem_res)); - if (!ssbi->base) { - pr_err("ioremap of 0x%p failed\n", (void *)mem_res->start); - ret = -EINVAL; - goto err_ioremap; - } - platform_set_drvdata(pdev, ssbi); - - type = of_get_property(np, "qcom,controller-type", NULL); - if (type == NULL) { - pr_err("Missing qcom,controller-type property\n"); - ret = -EINVAL; - goto err_ssbi_controller; - } - dev_info(&pdev->dev, "SSBI controller type: '%s'\n", type); - if (strcmp(type, "ssbi") == 0) - ssbi->controller_type = MSM_SBI_CTRL_SSBI; - else if (strcmp(type, "ssbi2") == 0) - ssbi->controller_type = MSM_SBI_CTRL_SSBI2; - else if (strcmp(type, "pmic-arbiter") == 0) - ssbi->controller_type = MSM_SBI_CTRL_PMIC_ARBITER; - else { - pr_err("Unknown qcom,controller-type\n"); - ret = -EINVAL; - goto err_ssbi_controller; - } - - if (ssbi->controller_type == MSM_SBI_CTRL_PMIC_ARBITER) { - ssbi->read = ssbi_pa_read_bytes; - ssbi->write = ssbi_pa_write_bytes; - } else { - ssbi->read = ssbi_read_bytes; - ssbi->write = ssbi_write_bytes; - } - - spin_lock_init(&ssbi->lock); - - ret = of_platform_populate(np, NULL, NULL, &pdev->dev); - if (ret) - goto err_ssbi_controller; - - return 0; - -err_ssbi_controller: - platform_set_drvdata(pdev, NULL); - iounmap(ssbi->base); -err_ioremap: -err_get_mem_res: - kfree(ssbi); - return ret; -} - -static int ssbi_remove(struct platform_device *pdev) -{ - struct ssbi *ssbi = platform_get_drvdata(pdev); - - platform_set_drvdata(pdev, NULL); - iounmap(ssbi->base); - kfree(ssbi); - return 0; -} - -static struct of_device_id ssbi_match_table[] = { - { .compatible = "qcom,ssbi" }, - {} -}; - -static struct platform_driver ssbi_driver = { - .probe = ssbi_probe, - .remove = ssbi_remove, - .driver = { - .name = "ssbi", - .owner = THIS_MODULE, - .of_match_table = ssbi_match_table, - }, -}; - -static int __init ssbi_init(void) -{ - return platform_driver_register(&ssbi_driver); -} -module_init(ssbi_init); - -static void __exit ssbi_exit(void) -{ - platform_driver_unregister(&ssbi_driver); -} -module_exit(ssbi_exit) - -MODULE_LICENSE("GPL v2"); -MODULE_VERSION("1.0"); -MODULE_ALIAS("platform:ssbi"); -MODULE_AUTHOR("Dima Zavin "); -- cgit v1.2.3 From 15447a46a56a6d191af76ba0e3155c9b1877f596 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Wed, 15 May 2013 18:00:48 +0300 Subject: mfd: sec-core: Remove explicit call to mfd_remove_devices In case mfd_add_devices will fail, it will call to mfd_remove_devices by itself and return non-zero value. Signed-off-by: Leon Romanovsky Signed-off-by: Samuel Ortiz --- drivers/mfd/sec-core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/mfd/sec-core.c b/drivers/mfd/sec-core.c index 77ee26ef5941..cc896d1c1cdd 100644 --- a/drivers/mfd/sec-core.c +++ b/drivers/mfd/sec-core.c @@ -230,13 +230,12 @@ static int sec_pmic_probe(struct i2c_client *i2c, BUG(); } - if (ret < 0) + if (ret) goto err; return ret; err: - mfd_remove_devices(sec_pmic->dev); sec_irq_exit(sec_pmic); i2c_unregister_device(sec_pmic->rtc); return ret; -- cgit v1.2.3 From 9032eabdd3fcfc00aa513a9eef54002cbabb8c9a Mon Sep 17 00:00:00 2001 From: Roger Tseng Date: Fri, 19 Apr 2013 21:52:42 +0800 Subject: mfd: rtsx: Add support for RTL8411B Adding support of model RTL8411B. Since the model is similar to RTL8411, differences are implemented in rtl8411.c. Signed-off-by: Roger Tseng Signed-off-by: Samuel Ortiz --- drivers/mfd/rtl8411.c | 132 +++++++++++++++++++++++++++++++++++++++++++ drivers/mfd/rtsx_pcr.c | 5 ++ drivers/mfd/rtsx_pcr.h | 1 + include/linux/mfd/rtsx_pci.h | 1 + 4 files changed, 139 insertions(+) diff --git a/drivers/mfd/rtl8411.c b/drivers/mfd/rtl8411.c index 2a2d31687b72..c436bf27e78d 100644 --- a/drivers/mfd/rtl8411.c +++ b/drivers/mfd/rtl8411.c @@ -35,12 +35,33 @@ static u8 rtl8411_get_ic_version(struct rtsx_pcr *pcr) return val & 0x0F; } +static int rtl8411b_is_qfn48(struct rtsx_pcr *pcr) +{ + u8 val = 0; + + rtsx_pci_read_register(pcr, RTL8411B_PACKAGE_MODE, &val); + + if (val & 0x2) + return 1; + else + return 0; +} + static int rtl8411_extra_init_hw(struct rtsx_pcr *pcr) { return rtsx_pci_write_register(pcr, CD_PAD_CTL, CD_DISABLE_MASK | CD_AUTO_DISABLE, CD_ENABLE); } +static int rtl8411b_extra_init_hw(struct rtsx_pcr *pcr) +{ + if (rtl8411b_is_qfn48(pcr)) + rtsx_pci_write_register(pcr, CARD_PULL_CTL3, 0xFF, 0xF5); + + return rtsx_pci_write_register(pcr, CD_PAD_CTL, + CD_DISABLE_MASK | CD_AUTO_DISABLE, CD_ENABLE); +} + static int rtl8411_turn_on_led(struct rtsx_pcr *pcr) { return rtsx_pci_write_register(pcr, CARD_GPIO, 0x01, 0x00); @@ -214,6 +235,20 @@ static const struct pcr_ops rtl8411_pcr_ops = { .conv_clk_and_div_n = rtl8411_conv_clk_and_div_n, }; +static const struct pcr_ops rtl8411b_pcr_ops = { + .extra_init_hw = rtl8411b_extra_init_hw, + .optimize_phy = NULL, + .turn_on_led = rtl8411_turn_on_led, + .turn_off_led = rtl8411_turn_off_led, + .enable_auto_blink = rtl8411_enable_auto_blink, + .disable_auto_blink = rtl8411_disable_auto_blink, + .card_power_on = rtl8411_card_power_on, + .card_power_off = rtl8411_card_power_off, + .switch_output_voltage = rtl8411_switch_output_voltage, + .cd_deglitch = rtl8411_cd_deglitch, + .conv_clk_and_div_n = rtl8411_conv_clk_and_div_n, +}; + /* SD Pull Control Enable: * SD_DAT[3:0] ==> pull up * SD_CD ==> pull up @@ -276,6 +311,74 @@ static const u32 rtl8411_ms_pull_ctl_disable_tbl[] = { 0, }; +static const u32 rtl8411b_qfn64_sd_pull_ctl_enable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL1, 0xAA), + RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA), + RTSX_REG_PAIR(CARD_PULL_CTL3, 0x09 | 0xD0), + RTSX_REG_PAIR(CARD_PULL_CTL4, 0x09 | 0x50), + RTSX_REG_PAIR(CARD_PULL_CTL5, 0x05 | 0x50), + RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04 | 0x11), + 0, +}; + +static const u32 rtl8411b_qfn48_sd_pull_ctl_enable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA), + RTSX_REG_PAIR(CARD_PULL_CTL3, 0x69 | 0x90), + RTSX_REG_PAIR(CARD_PULL_CTL6, 0x08 | 0x11), + 0, +}; + +static const u32 rtl8411b_qfn64_sd_pull_ctl_disable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL1, 0x65), + RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL3, 0x05 | 0xD0), + RTSX_REG_PAIR(CARD_PULL_CTL4, 0x09 | 0x50), + RTSX_REG_PAIR(CARD_PULL_CTL5, 0x05 | 0x50), + RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04 | 0x11), + 0, +}; + +static const u32 rtl8411b_qfn48_sd_pull_ctl_disable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL3, 0x65 | 0x90), + RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04 | 0x11), + 0, +}; + +static const u32 rtl8411b_qfn64_ms_pull_ctl_enable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL1, 0x65), + RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL3, 0x05 | 0xD0), + RTSX_REG_PAIR(CARD_PULL_CTL4, 0x05 | 0x50), + RTSX_REG_PAIR(CARD_PULL_CTL5, 0x05 | 0x50), + RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04 | 0x11), + 0, +}; + +static const u32 rtl8411b_qfn48_ms_pull_ctl_enable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL3, 0x65 | 0x90), + RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04 | 0x11), + 0, +}; + +static const u32 rtl8411b_qfn64_ms_pull_ctl_disable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL1, 0x65), + RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL3, 0x05 | 0xD0), + RTSX_REG_PAIR(CARD_PULL_CTL4, 0x09 | 0x50), + RTSX_REG_PAIR(CARD_PULL_CTL5, 0x05 | 0x50), + RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04 | 0x11), + 0, +}; + +static const u32 rtl8411b_qfn48_ms_pull_ctl_disable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL3, 0x65 | 0x90), + RTSX_REG_PAIR(CARD_PULL_CTL6, 0x04 | 0x11), + 0, +}; + void rtl8411_init_params(struct rtsx_pcr *pcr) { pcr->extra_caps = EXTRA_CAPS_SD_SDR50 | EXTRA_CAPS_SD_SDR104; @@ -288,3 +391,32 @@ void rtl8411_init_params(struct rtsx_pcr *pcr) pcr->ms_pull_ctl_enable_tbl = rtl8411_ms_pull_ctl_enable_tbl; pcr->ms_pull_ctl_disable_tbl = rtl8411_ms_pull_ctl_disable_tbl; } + +void rtl8411b_init_params(struct rtsx_pcr *pcr) +{ + pcr->extra_caps = EXTRA_CAPS_SD_SDR50 | EXTRA_CAPS_SD_SDR104; + pcr->num_slots = 2; + pcr->ops = &rtl8411b_pcr_ops; + + pcr->ic_version = rtl8411_get_ic_version(pcr); + + if (rtl8411b_is_qfn48(pcr)) { + pcr->sd_pull_ctl_enable_tbl = + rtl8411b_qfn48_sd_pull_ctl_enable_tbl; + pcr->sd_pull_ctl_disable_tbl = + rtl8411b_qfn48_sd_pull_ctl_disable_tbl; + pcr->ms_pull_ctl_enable_tbl = + rtl8411b_qfn48_ms_pull_ctl_enable_tbl; + pcr->ms_pull_ctl_disable_tbl = + rtl8411b_qfn48_ms_pull_ctl_disable_tbl; + } else { + pcr->sd_pull_ctl_enable_tbl = + rtl8411b_qfn64_sd_pull_ctl_enable_tbl; + pcr->sd_pull_ctl_disable_tbl = + rtl8411b_qfn64_sd_pull_ctl_disable_tbl; + pcr->ms_pull_ctl_enable_tbl = + rtl8411b_qfn64_ms_pull_ctl_enable_tbl; + pcr->ms_pull_ctl_disable_tbl = + rtl8411b_qfn64_ms_pull_ctl_disable_tbl; + } +} diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c index e968c01ca2ac..dd186c4103c1 100644 --- a/drivers/mfd/rtsx_pcr.c +++ b/drivers/mfd/rtsx_pcr.c @@ -57,6 +57,7 @@ static DEFINE_PCI_DEVICE_TABLE(rtsx_pci_ids) = { { PCI_DEVICE(0x10EC, 0x5289), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { PCI_DEVICE(0x10EC, 0x5227), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { PCI_DEVICE(0x10EC, 0x5249), PCI_CLASS_OTHERS << 16, 0xFF0000 }, + { PCI_DEVICE(0x10EC, 0x5287), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { 0, } }; @@ -1038,6 +1039,10 @@ static int rtsx_pci_init_chip(struct rtsx_pcr *pcr) case 0x5249: rts5249_init_params(pcr); break; + + case 0x5287: + rtl8411b_init_params(pcr); + break; } dev_dbg(&(pcr->pci->dev), "PID: 0x%04x, IC version: 0x%02x\n", diff --git a/drivers/mfd/rtsx_pcr.h b/drivers/mfd/rtsx_pcr.h index 55fcfc25c4e4..c0cac7e8972f 100644 --- a/drivers/mfd/rtsx_pcr.h +++ b/drivers/mfd/rtsx_pcr.h @@ -33,5 +33,6 @@ void rts5229_init_params(struct rtsx_pcr *pcr); void rtl8411_init_params(struct rtsx_pcr *pcr); void rts5227_init_params(struct rtsx_pcr *pcr); void rts5249_init_params(struct rtsx_pcr *pcr); +void rtl8411b_init_params(struct rtsx_pcr *pcr); #endif diff --git a/include/linux/mfd/rtsx_pci.h b/include/linux/mfd/rtsx_pci.h index 86bc635f8385..7a9f7089435d 100644 --- a/include/linux/mfd/rtsx_pci.h +++ b/include/linux/mfd/rtsx_pci.h @@ -575,6 +575,7 @@ #define CARD_PWR_CTL 0xFD50 #define CARD_CLK_SWITCH 0xFD51 +#define RTL8411B_PACKAGE_MODE 0xFD51 #define CARD_SHARE_MODE 0xFD52 #define CARD_DRIVE_SEL 0xFD53 #define CARD_STOP 0xFD54 -- cgit v1.2.3 From 032fa16d453c562c93b118e1c3e65f183c5f55cc Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Tue, 23 Apr 2013 16:19:10 +0300 Subject: mfd: twl-core: Convert to module_i2c_driver() Shift TWL initialization to module/device init layer, because I2C now is not initialized on subsys init layer and shifted to module/device init layer instead. The I2C <--> TWL dependency should be resolved in drivers/Makefile now. Cc: Santosh Shilimkar Signed-off-by: Grygorii Strashko Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 89ab4d970643..8d9bc10c5312 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -1305,17 +1305,7 @@ static struct i2c_driver twl_driver = { .remove = twl_remove, }; -static int __init twl_init(void) -{ - return i2c_add_driver(&twl_driver); -} -subsys_initcall(twl_init); - -static void __exit twl_exit(void) -{ - i2c_del_driver(&twl_driver); -} -module_exit(twl_exit); +module_i2c_driver(twl_driver); MODULE_AUTHOR("Texas Instruments, Inc."); MODULE_DESCRIPTION("I2C Core interface for TWL"); -- cgit v1.2.3 From 8477128fe0c3c455e9dfb1ba7ad7e6d09489d33c Mon Sep 17 00:00:00 2001 From: James Ralston Date: Thu, 9 May 2013 12:38:53 -0700 Subject: mfd: lpc_ich: Add support for Intel Avoton SoC This patch adds the LPC Controller Device IDs for Watchdog and GPIO for Intel Avoton SoC, to the lpc_ich driver. Signed-off-by: James Ralston Signed-off-by: Samuel Ortiz --- drivers/mfd/lpc_ich.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c index 9f12f91d6296..330cd44771be 100644 --- a/drivers/mfd/lpc_ich.c +++ b/drivers/mfd/lpc_ich.c @@ -51,6 +51,7 @@ * document number TBD : Lynx Point * document number TBD : Lynx Point-LP * document number TBD : Wellsburg + * document number TBD : Avoton SoC */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -207,6 +208,7 @@ enum lpc_chipsets { LPC_LPT, /* Lynx Point */ LPC_LPT_LP, /* Lynx Point-LP */ LPC_WBG, /* Wellsburg */ + LPC_AVN, /* Avoton SoC */ }; struct lpc_ich_info lpc_chipset_info[] = { @@ -491,6 +493,10 @@ struct lpc_ich_info lpc_chipset_info[] = { .name = "Wellsburg", .iTCO_version = 2, }, + [LPC_AVN] = { + .name = "Avoton SoC", + .iTCO_version = 1, + }, }; /* @@ -704,6 +710,10 @@ static DEFINE_PCI_DEVICE_TABLE(lpc_ich_ids) = { { PCI_VDEVICE(INTEL, 0x8d5d), LPC_WBG}, { PCI_VDEVICE(INTEL, 0x8d5e), LPC_WBG}, { PCI_VDEVICE(INTEL, 0x8d5f), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x1f38), LPC_AVN}, + { PCI_VDEVICE(INTEL, 0x1f39), LPC_AVN}, + { PCI_VDEVICE(INTEL, 0x1f3a), LPC_AVN}, + { PCI_VDEVICE(INTEL, 0x1f3b), LPC_AVN}, { 0, }, /* End of list */ }; MODULE_DEVICE_TABLE(pci, lpc_ich_ids); -- cgit v1.2.3 From 7d71cf29416a8c5f11e98d507054a01b9cb71ff5 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 6 May 2013 13:04:25 +0900 Subject: mfd: intel_msic: Remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Acked-by: Lee Jones Acked-by: Mika Westerberg Signed-off-by: Samuel Ortiz --- drivers/mfd/intel_msic.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mfd/intel_msic.c b/drivers/mfd/intel_msic.c index d8d5137f9717..4f2462f0963e 100644 --- a/drivers/mfd/intel_msic.c +++ b/drivers/mfd/intel_msic.c @@ -438,7 +438,6 @@ static int intel_msic_remove(struct platform_device *pdev) struct intel_msic *msic = platform_get_drvdata(pdev); intel_msic_remove_devices(msic); - platform_set_drvdata(pdev, NULL); return 0; } -- cgit v1.2.3 From 9f74f5b541c2994c7cad4c450323913bff67fab7 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 6 May 2013 13:05:43 +0900 Subject: mfd: jz4740-adc: Remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Acked-by: Lee Jones Signed-off-by: Samuel Ortiz --- drivers/mfd/jz4740-adc.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/mfd/jz4740-adc.c b/drivers/mfd/jz4740-adc.c index e80587f1a792..01e86ce438f1 100644 --- a/drivers/mfd/jz4740-adc.c +++ b/drivers/mfd/jz4740-adc.c @@ -294,7 +294,6 @@ static int jz4740_adc_probe(struct platform_device *pdev) err_clk_put: clk_put(adc->clk); err_iounmap: - platform_set_drvdata(pdev, NULL); iounmap(adc->base); err_release_mem_region: release_mem_region(adc->mem->start, resource_size(adc->mem)); @@ -317,8 +316,6 @@ static int jz4740_adc_remove(struct platform_device *pdev) clk_put(adc->clk); - platform_set_drvdata(pdev, NULL); - return 0; } -- cgit v1.2.3 From 96ac2d1a3a352ce1f4d669ad222677ba2ed76271 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 6 May 2013 13:06:31 +0900 Subject: mfd: twl4030-audio: Remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Acked-by: Lee Jones Signed-off-by: Samuel Ortiz --- drivers/mfd/twl4030-audio.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/mfd/twl4030-audio.c b/drivers/mfd/twl4030-audio.c index d2ab222138c2..a31fba96ef43 100644 --- a/drivers/mfd/twl4030-audio.c +++ b/drivers/mfd/twl4030-audio.c @@ -261,10 +261,8 @@ static int twl4030_audio_probe(struct platform_device *pdev) ret = -ENODEV; } - if (ret) { - platform_set_drvdata(pdev, NULL); + if (ret) twl4030_audio_dev = NULL; - } return ret; } @@ -272,7 +270,6 @@ static int twl4030_audio_probe(struct platform_device *pdev) static int twl4030_audio_remove(struct platform_device *pdev) { mfd_remove_devices(&pdev->dev); - platform_set_drvdata(pdev, NULL); twl4030_audio_dev = NULL; return 0; -- cgit v1.2.3 From c3d6a0a3194092aec01a579a60a791c4afc47411 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 6 May 2013 13:08:08 +0900 Subject: mfd: twl4030-madc: Remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Acked-by: Lee Jones Signed-off-by: Samuel Ortiz --- drivers/mfd/twl4030-madc.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/mfd/twl4030-madc.c b/drivers/mfd/twl4030-madc.c index 42bd3ea5df3c..1ea54d4d003a 100644 --- a/drivers/mfd/twl4030-madc.c +++ b/drivers/mfd/twl4030-madc.c @@ -775,12 +775,10 @@ static int twl4030_madc_probe(struct platform_device *pdev) IRQF_TRIGGER_RISING, "twl4030_madc", madc); if (ret) { dev_dbg(&pdev->dev, "could not request irq\n"); - goto err_irq; + goto err_i2c; } twl4030_madc = madc; return 0; -err_irq: - platform_set_drvdata(pdev, NULL); err_i2c: twl4030_madc_set_current_generator(madc, 0, 0); err_current_generator: @@ -796,7 +794,6 @@ static int twl4030_madc_remove(struct platform_device *pdev) struct twl4030_madc_data *madc = platform_get_drvdata(pdev); free_irq(platform_get_irq(pdev, 0), madc); - platform_set_drvdata(pdev, NULL); twl4030_madc_set_current_generator(madc, 0, 0); twl4030_madc_set_power(madc, 0); kfree(madc); -- cgit v1.2.3 From 3b87b4eb536d454ae506dc68144936a169fdcf94 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 6 May 2013 13:09:57 +0900 Subject: mfd: mcp-sa11x0: Remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Acked-by: Lee Jones Signed-off-by: Samuel Ortiz --- drivers/mfd/mcp-sa11x0.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/mfd/mcp-sa11x0.c b/drivers/mfd/mcp-sa11x0.c index f99d6299ec24..13198d937e36 100644 --- a/drivers/mfd/mcp-sa11x0.c +++ b/drivers/mfd/mcp-sa11x0.c @@ -225,8 +225,6 @@ static int mcp_sa11x0_probe(struct platform_device *dev) if (ret == 0) return 0; - platform_set_drvdata(dev, NULL); - err_ioremap: iounmap(m->base1); iounmap(m->base0); @@ -252,7 +250,6 @@ static int mcp_sa11x0_remove(struct platform_device *dev) mem0 = platform_get_resource(dev, IORESOURCE_MEM, 0); mem1 = platform_get_resource(dev, IORESOURCE_MEM, 1); - platform_set_drvdata(dev, NULL); mcp_host_del(mcp); iounmap(m->base1); iounmap(m->base0); -- cgit v1.2.3 From 3b69f59bef86498e38d8153cc08138de74da8ea6 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 6 May 2013 13:12:24 +0900 Subject: mfd: t7l66xb: Remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Acked-by: Lee Jones Signed-off-by: Samuel Ortiz --- drivers/mfd/t7l66xb.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mfd/t7l66xb.c b/drivers/mfd/t7l66xb.c index b32940ec9034..a21bff283a98 100644 --- a/drivers/mfd/t7l66xb.c +++ b/drivers/mfd/t7l66xb.c @@ -422,7 +422,6 @@ static int t7l66xb_remove(struct platform_device *dev) iounmap(t7l66xb->scr); release_resource(&t7l66xb->rscr); mfd_remove_devices(&dev->dev); - platform_set_drvdata(dev, NULL); kfree(t7l66xb); return ret; -- cgit v1.2.3 From ae8574864b52cae1e35e7881737b5e08036f0fa1 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 6 May 2013 13:13:05 +0900 Subject: mfd: tc6387xb: Remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Acked-by: Lee Jones Signed-off-by: Samuel Ortiz --- drivers/mfd/tc6387xb.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mfd/tc6387xb.c b/drivers/mfd/tc6387xb.c index 366f7b906278..65c425a517c5 100644 --- a/drivers/mfd/tc6387xb.c +++ b/drivers/mfd/tc6387xb.c @@ -217,7 +217,6 @@ static int tc6387xb_remove(struct platform_device *dev) release_resource(&tc6387xb->rscr); clk_disable(tc6387xb->clk32k); clk_put(tc6387xb->clk32k); - platform_set_drvdata(dev, NULL); kfree(tc6387xb); return 0; -- cgit v1.2.3 From fa67d4ebc7e492e07bd2d3bf2937dfae2bb514bd Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 6 May 2013 13:14:14 +0900 Subject: mfd: tc6393xb: Remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Acked-by: Lee Jones Signed-off-by: Samuel Ortiz --- drivers/mfd/tc6393xb.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c index 15e1463e5e13..a563dfa3cf87 100644 --- a/drivers/mfd/tc6393xb.c +++ b/drivers/mfd/tc6393xb.c @@ -756,7 +756,6 @@ static int tc6393xb_remove(struct platform_device *dev) clk_disable(tc6393xb->clk); iounmap(tc6393xb->scr); release_resource(&tc6393xb->rscr); - platform_set_drvdata(dev, NULL); clk_put(tc6393xb->clk); kfree(tc6393xb); -- cgit v1.2.3 From fc83f586adf3b86ff7046478497b4a53b2220be8 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 6 May 2013 09:46:29 +0200 Subject: mfd: cros_ec: Fix Kconfig typo - s/ned/need/ Acked-by: Simon Glass Signed-off-by: Geert Uytterhoeven Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 013b094f0f9e..c6eb930fb54d 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -53,7 +53,7 @@ config MFD_CROS_EC help If you say Y here you get support for the ChromeOS Embedded Controller (EC) providing keyboard, battery and power services. - You also ned to enable the driver for the bus you are using. The + You also need to enable the driver for the bus you are using. The protocol for talking to the EC is defined by the bus driver. config MFD_CROS_EC_I2C -- cgit v1.2.3 From 7c8844481a1c16c10fa9be4ce95be5725aed6ce3 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 6 May 2013 16:12:56 +0100 Subject: mfd: wm8994: Emulate level triggered interrupts if required The interrupt controller on the wm8994 series of devices requires a level triggered parent. If one is not available but a GPIO is available for the interrupt then emulate. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm8994-irq.c | 100 +++++++++++++++++++++++++++++++++++++-- include/linux/mfd/wm8994/core.h | 2 + include/linux/mfd/wm8994/pdata.h | 5 ++ 3 files changed, 103 insertions(+), 4 deletions(-) diff --git a/drivers/mfd/wm8994-irq.c b/drivers/mfd/wm8994-irq.c index a050e56a9bbd..d3a184a240f5 100644 --- a/drivers/mfd/wm8994-irq.c +++ b/drivers/mfd/wm8994-irq.c @@ -14,10 +14,12 @@ #include #include +#include #include #include #include #include +#include #include #include @@ -138,6 +140,55 @@ static struct regmap_irq_chip wm8994_irq_chip = { .runtime_pm = true, }; +static void wm8994_edge_irq_enable(struct irq_data *data) +{ +} + +static void wm8994_edge_irq_disable(struct irq_data *data) +{ +} + +static struct irq_chip wm8994_edge_irq_chip = { + .name = "wm8994_edge", + .irq_disable = wm8994_edge_irq_disable, + .irq_enable = wm8994_edge_irq_enable, +}; + +static irqreturn_t wm8994_edge_irq(int irq, void *data) +{ + struct wm8994 *wm8994 = data; + + while (gpio_get_value_cansleep(wm8994->pdata.irq_gpio)) + handle_nested_irq(irq_create_mapping(wm8994->edge_irq, 0)); + + return IRQ_HANDLED; +} + +static int wm8994_edge_irq_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + struct wm8994 *wm8994 = h->host_data; + + irq_set_chip_data(virq, wm8994); + irq_set_chip_and_handler(virq, &wm8994_edge_irq_chip, handle_edge_irq); + irq_set_nested_thread(virq, 1); + + /* ARM needs us to explicitly flag the IRQ as valid + * and will set them noprobe when we do so. */ +#ifdef CONFIG_ARM + set_irq_flags(virq, IRQF_VALID); +#else + irq_set_noprobe(virq); +#endif + + return 0; +} + +static struct irq_domain_ops wm8994_edge_irq_ops = { + .map = wm8994_edge_irq_map, + .xlate = irq_domain_xlate_twocell, +}; + int wm8994_irq_init(struct wm8994 *wm8994) { int ret; @@ -156,10 +207,51 @@ int wm8994_irq_init(struct wm8994 *wm8994) if (pdata->irq_flags) irqflags = pdata->irq_flags; - ret = regmap_add_irq_chip(wm8994->regmap, wm8994->irq, - irqflags, - wm8994->irq_base, &wm8994_irq_chip, - &wm8994->irq_data); + /* use a GPIO for edge triggered controllers */ + if (irqflags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) { + if (gpio_to_irq(pdata->irq_gpio) != wm8994->irq) { + dev_warn(wm8994->dev, "IRQ %d is not GPIO %d (%d)\n", + wm8994->irq, pdata->irq_gpio, + gpio_to_irq(pdata->irq_gpio)); + wm8994->irq = gpio_to_irq(pdata->irq_gpio); + } + + ret = devm_gpio_request_one(wm8994->dev, pdata->irq_gpio, + GPIOF_IN, "WM8994 IRQ"); + + if (ret != 0) { + dev_err(wm8994->dev, "Failed to get IRQ GPIO: %d\n", + ret); + return ret; + } + + wm8994->edge_irq = irq_domain_add_linear(NULL, 1, + &wm8994_edge_irq_ops, + wm8994); + + ret = regmap_add_irq_chip(wm8994->regmap, + irq_create_mapping(wm8994->edge_irq, + 0), + IRQF_ONESHOT, + wm8994->irq_base, &wm8994_irq_chip, + &wm8994->irq_data); + if (ret != 0) { + dev_err(wm8994->dev, "Failed to get IRQ: %d\n", + ret); + return ret; + } + + ret = request_threaded_irq(wm8994->irq, + NULL, wm8994_edge_irq, + irqflags, + "WM8994 edge", wm8994); + } else { + ret = regmap_add_irq_chip(wm8994->regmap, wm8994->irq, + irqflags, + wm8994->irq_base, &wm8994_irq_chip, + &wm8994->irq_data); + } + if (ret != 0) { dev_err(wm8994->dev, "Failed to register IRQ chip: %d\n", ret); return ret; diff --git a/include/linux/mfd/wm8994/core.h b/include/linux/mfd/wm8994/core.h index ae5c249530b4..40854ac0ba3d 100644 --- a/include/linux/mfd/wm8994/core.h +++ b/include/linux/mfd/wm8994/core.h @@ -29,6 +29,7 @@ enum wm8994_type { struct regulator_dev; struct regulator_bulk_data; +struct irq_domain; #define WM8994_NUM_GPIO_REGS 11 #define WM8994_NUM_LDO_REGS 2 @@ -73,6 +74,7 @@ struct wm8994 { int irq; struct regmap_irq_chip_data *irq_data; + struct irq_domain *edge_irq; /* Used over suspend/resume */ bool suspended; diff --git a/include/linux/mfd/wm8994/pdata.h b/include/linux/mfd/wm8994/pdata.h index 68e776594889..90e9bf86e643 100644 --- a/include/linux/mfd/wm8994/pdata.h +++ b/include/linux/mfd/wm8994/pdata.h @@ -223,6 +223,11 @@ struct wm8994_pdata { * lines is mastered. */ int max_channels_clocked[WM8994_NUM_AIF]; + + /** + * GPIO for the IRQ pin if host only supports edge triggering + */ + int irq_gpio; }; #endif -- cgit v1.2.3 From c0fa7e104bbae5286f4fcf359f66a536f849c040 Mon Sep 17 00:00:00 2001 From: Olof Johansson Date: Fri, 10 May 2013 10:11:59 -0700 Subject: mfd: max77687: Add registration of max77686-clk The MAX77686 clock driver has been in-tree for over 6 months, but never actually enabled through the MFD registration before. Add it to the table so the device will probe and configure properly. Signed-off-by: Olof Johansson Signed-off-by: Samuel Ortiz --- drivers/mfd/max77686.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c index 1cbb17609c8b..5bd24670e1b6 100644 --- a/drivers/mfd/max77686.c +++ b/drivers/mfd/max77686.c @@ -37,6 +37,7 @@ static struct mfd_cell max77686_devs[] = { { .name = "max77686-pmic", }, { .name = "max77686-rtc", }, + { .name = "max77686-clk", }, }; static struct regmap_config max77686_regmap_config = { -- cgit v1.2.3 From 404d5df42a812b81ec87b76cc3d2e492434e9ff7 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 12 May 2013 20:05:48 +0200 Subject: mfd: jz4740-adc: Use clk_prepare_enable/clk_disable_unprepare In preparation to switching the jz4740 clk driver to the common clk framework update the clk enable/disable calls to clk_prepare_enable/clk_disable_unprepare. Signed-off-by: Lars-Peter Clausen Signed-off-by: Samuel Ortiz --- drivers/mfd/jz4740-adc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/jz4740-adc.c b/drivers/mfd/jz4740-adc.c index 01e86ce438f1..3c0e8cf6916b 100644 --- a/drivers/mfd/jz4740-adc.c +++ b/drivers/mfd/jz4740-adc.c @@ -86,13 +86,13 @@ static void jz4740_adc_irq_demux(unsigned int irq, struct irq_desc *desc) static inline void jz4740_adc_clk_enable(struct jz4740_adc *adc) { if (atomic_inc_return(&adc->clk_ref) == 1) - clk_enable(adc->clk); + clk_prepare_enable(adc->clk); } static inline void jz4740_adc_clk_disable(struct jz4740_adc *adc) { if (atomic_dec_return(&adc->clk_ref) == 0) - clk_disable(adc->clk); + clk_disable_unprepare(adc->clk); } static inline void jz4740_adc_set_enabled(struct jz4740_adc *adc, int engine, -- cgit v1.2.3 From f7d3210e8b45eea8561c08d35502d2baca07593a Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Fri, 17 May 2013 00:53:42 +0200 Subject: MAINTAINERS: Add Lee Jones as the MFD co-maintainer Lee Jones from Linaro offered to help me with maitaining the MFD drivers. Also, fix the MFD tree URLs. Signed-off-by: Samuel Ortiz --- MAINTAINERS | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 250dc970c62d..39fd4e794b1e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5432,7 +5432,9 @@ F: include/media/mt9v032.h MULTIFUNCTION DEVICES (MFD) M: Samuel Ortiz -T: git git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6.git +M: Lee Jones +T: git git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-next.git +T: git git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-fixes.git S: Supported F: drivers/mfd/ -- cgit v1.2.3 From 629d0fa0da40958809e6577350c903b9368ef672 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 10 Jun 2013 13:42:40 +0300 Subject: iwlwifi: mvm: remove unused wait_for_ba field Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/mvm/sta.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.h b/drivers/net/wireless/iwlwifi/mvm/sta.h index 3efa0a0cc987..94b265eb32b8 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/iwlwifi/mvm/sta.h @@ -250,7 +250,6 @@ enum iwl_mvm_agg_state { * the first packet to be sent in legacy HW queue in Tx AGG stop flow. * Basically when next_reclaimed reaches ssn, we can tell mac80211 that * we are ready to finish the Tx AGG stop / start flow. - * @wait_for_ba: Expect block-ack before next Tx reply */ struct iwl_mvm_tid_data { u16 seq_number; @@ -260,7 +259,6 @@ struct iwl_mvm_tid_data { enum iwl_mvm_agg_state state; u16 txq_id; u16 ssn; - bool wait_for_ba; }; /** -- cgit v1.2.3 From 0742a75a72ee52a7424554a1d49882dd9db195e3 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 10 Jun 2013 14:10:33 +0300 Subject: iwlwifi: mvm: fix irrelevant comment This code moved and is now far away from the code that takes the mutex. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index f2976901b1d5..795f65cf6332 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -639,8 +639,7 @@ static void iwl_mvm_prepare_mac_removal(struct iwl_mvm *mvm, * By now, all the AC queues are empty. The AGG queues are * empty too. We already got all the Tx responses for all the * packets in the queues. The drain work can have been - * triggered. Flush it. This work item takes the mutex, so kill - * it before we take it. + * triggered. Flush it. */ flush_work(&mvm->sta_drained_wk); } -- cgit v1.2.3 From 476a9e09cbe37bad93de6e460b76f89f3ad22c9b Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 11 Jun 2013 15:17:21 +0300 Subject: iwlwifi: ignore 0-length PHY DB sections This can happen during development but can cause problems, WARN (once) and go on. Signed-off-by: Emmanuel Grumbach Reviewed-by: Guy Cohen Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/iwl-phy-db.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/iwlwifi/iwl-phy-db.c b/drivers/net/wireless/iwlwifi/iwl-phy-db.c index 25745daa0d5d..84acb4deca8a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-phy-db.c +++ b/drivers/net/wireless/iwlwifi/iwl-phy-db.c @@ -413,6 +413,9 @@ static int iwl_phy_db_send_all_channel_groups( if (!entry) return -EINVAL; + if (WARN_ON_ONCE(!entry->size)) + continue; + /* Send the requested PHY DB section */ err = iwl_send_phy_db_cmd(phy_db, type, -- cgit v1.2.3 From 565f5a942b0391b528c5fc6f3c535900f4617816 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 5 Jun 2013 17:36:26 +0200 Subject: iwlwifi: include export.h instead of module.h We only need EXPORT_SYMBOL_GPL, none of the other things from module.h, so only include export.h. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/iwl-drv.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.h b/drivers/net/wireless/iwlwifi/iwl-drv.h index 7d1450916308..429337a2b9a1 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.h +++ b/drivers/net/wireless/iwlwifi/iwl-drv.h @@ -62,8 +62,7 @@ #ifndef __iwl_drv_h__ #define __iwl_drv_h__ - -#include +#include /* for all modules */ #define DRV_NAME "iwlwifi" -- cgit v1.2.3 From 5af01772ee1d6e96849adf728ff837bd71b119c0 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 9 Jun 2013 12:59:24 +0300 Subject: iwlwifi: mvm: properly tell the fw that a STA is awake The firmware API wasn't being used correctly, fix that. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/mvm/sta.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c index 2278858d5658..f986eb50c4a4 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/iwlwifi/mvm/sta.c @@ -1292,17 +1292,11 @@ void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm, struct iwl_mvm_add_sta_cmd cmd = { .add_modify = STA_MODE_MODIFY, .sta_id = mvmsta->sta_id, - .modify_mask = STA_MODIFY_SLEEPING_STA_TX_COUNT, - .sleep_state_flags = cpu_to_le16(STA_SLEEP_STATE_AWAKE), + .station_flags_msk = cpu_to_le32(STA_FLG_PS), .mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color), }; int ret; - /* - * Same modify mask for sleep_tx_count and sleep_state_flags but this - * should be fine since if we set the STA as "awake", then - * sleep_tx_count is not relevant. - */ ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, sizeof(cmd), &cmd); if (ret) IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret); -- cgit v1.2.3 From 837fb69f10588caafc883c4473a864660e1403ce Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 9 Jun 2013 13:00:12 +0300 Subject: iwlwifi: mvm: don't set the MCAST queue in STA's queue list The MCAST queue should be enabled after DTIM only. According to fw API, the MCAST must not be attached to any station, but should appear in the mcast_qid of the AP's mac context only. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c | 5 +---- drivers/net/wireless/iwlwifi/mvm/sta.c | 3 --- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c index 46c7c0507c25..273b0cc197ab 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c @@ -193,14 +193,11 @@ static void iwl_mvm_mac_iface_iterator(void *_data, u8 *mac, u32 iwl_mvm_mac_get_queues_mask(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { - u32 qmask, ac; + u32 qmask = 0, ac; if (vif->type == NL80211_IFTYPE_P2P_DEVICE) return BIT(IWL_MVM_OFFCHANNEL_QUEUE); - qmask = (vif->cab_queue != IEEE80211_INVAL_HW_QUEUE) ? - BIT(vif->cab_queue) : 0; - for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) if (vif->hw_queue[ac] != IEEE80211_INVAL_HW_QUEUE) qmask |= BIT(vif->hw_queue[ac]); diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c index f986eb50c4a4..62fe5209093b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/iwlwifi/mvm/sta.c @@ -229,9 +229,6 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm, if (vif->hw_queue[i] != IEEE80211_INVAL_HW_QUEUE) mvm_sta->tfd_queue_msk |= BIT(vif->hw_queue[i]); - if (vif->cab_queue != IEEE80211_INVAL_HW_QUEUE) - mvm_sta->tfd_queue_msk |= BIT(vif->cab_queue); - /* for HW restart - need to reset the seq_number etc... */ memset(mvm_sta->tid_data, 0, sizeof(mvm_sta->tid_data)); -- cgit v1.2.3 From 8eb3871076875f247483f4a1fe6713e6d440c53e Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Sat, 1 Jun 2013 20:17:18 +0300 Subject: iwlwifi: mvm: Update the supported interface combinations iwlmvm does not support concurrent operation of AP with P2P Client/GO. Update the interface limits to reflect that iwlmvm supports only concurrent operation of station with AP and P2P Client/GO. Signed-off-by: Ilan Peer Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 1767a8849591..f091cf24b96c 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -81,12 +81,12 @@ static const struct ieee80211_iface_limit iwl_mvm_limits[] = { { .max = 1, - .types = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_AP), + .types = BIT(NL80211_IFTYPE_STATION), }, { .max = 1, - .types = BIT(NL80211_IFTYPE_P2P_CLIENT) | + .types = BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO), }, { -- cgit v1.2.3 From 9350de06be45a5a8b927ac6577c9d35de61c90ca Mon Sep 17 00:00:00 2001 From: Bernie Thompson Date: Sat, 1 Jun 2013 00:47:43 +0000 Subject: PM / wakeup: Adjust messaging for wake events during suspend This adds in a new message to the wakeup code which adds an indication to the log that suspend was cancelled due to a wake event occouring during the suspend sequence. It also adjusts the message printed in suspend.c to reflect the potential that a suspend was aborted, as opposed to a device failing to suspend. Without these message adjustments one can end up with a kernel log that says that a device failed to suspend with no actual device suspend failures, which can be confusing to the log examiner. Signed-off-by: Bernie Thompson Signed-off-by: Rafael J. Wysocki --- drivers/base/power/wakeup.c | 4 +++- kernel/power/suspend.c | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index 79715e7fa43e..407a2efa10bb 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -707,8 +707,10 @@ bool pm_wakeup_pending(void) } spin_unlock_irqrestore(&events_lock, flags); - if (ret) + if (ret) { + pr_info("PM: Wakeup pending, aborting suspend\n"); print_active_wakeup_sources(); + } return ret; } diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index bef86d121eb2..ece04223bb1e 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c @@ -269,7 +269,7 @@ int suspend_devices_and_enter(suspend_state_t state) suspend_test_start(); error = dpm_suspend_start(PMSG_SUSPEND); if (error) { - printk(KERN_ERR "PM: Some devices failed to suspend\n"); + pr_err("PM: Some devices failed to suspend, or early wake event detected\n"); goto Recover_platform; } suspend_test_finish("suspend devices"); -- cgit v1.2.3 From ad71d889b88055e61e3970a6744a271a51a94f42 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Tue, 30 Apr 2013 15:46:14 -0400 Subject: tracing: Add function probe to trigger a ftrace dump to console Add the "dump" command to have the ftrace buffer dumped to console if a function is hit. This is useful when debugging a tripple fault, where you have an idea of a function that is called just before the tripple fault occurs, and can tell ftrace to dump its content out to the console before it continues. Format is: :dump echo 'bad_address:dump' > /debug/tracing/set_ftrace_filter To remove this: echo '!bad_address:dump' > /debug/tracing/set_ftrace_filter Requested-by: Luis Claudio R. Goncalves Signed-off-by: Steven Rostedt --- Documentation/trace/ftrace.txt | 7 +++++ kernel/trace/trace_functions.c | 59 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 61 insertions(+), 5 deletions(-) diff --git a/Documentation/trace/ftrace.txt b/Documentation/trace/ftrace.txt index bfe8c29b1f1d..cc9ec57e157c 100644 --- a/Documentation/trace/ftrace.txt +++ b/Documentation/trace/ftrace.txt @@ -2430,6 +2430,13 @@ The following commands are supported: echo '!schedule:disable_event:sched:sched_switch' > \ set_ftrace_filter +- dump + When the function is hit, it will dump the contents of the ftrace + ring buffer to the console. This is useful if you need to debug + something, and want to dump the trace when a certain function + is hit. Perhaps its a function that is called before a tripple + fault happens and does not allow you to get a regular dump. + trace_pipe ---------- diff --git a/kernel/trace/trace_functions.c b/kernel/trace/trace_functions.c index c4d6d7191988..d7c8719734b8 100644 --- a/kernel/trace/trace_functions.c +++ b/kernel/trace/trace_functions.c @@ -290,6 +290,13 @@ ftrace_stacktrace_count(unsigned long ip, unsigned long parent_ip, void **data) trace_dump_stack(STACK_SKIP); } +static void +ftrace_dump_probe(unsigned long ip, unsigned long parent_ip, void **data) +{ + if (update_count(data)) + ftrace_dump(DUMP_ALL); +} + static int ftrace_probe_print(const char *name, struct seq_file *m, unsigned long ip, void *data) @@ -327,6 +334,13 @@ ftrace_stacktrace_print(struct seq_file *m, unsigned long ip, return ftrace_probe_print("stacktrace", m, ip, data); } +static int +ftrace_dump_print(struct seq_file *m, unsigned long ip, + struct ftrace_probe_ops *ops, void *data) +{ + return ftrace_probe_print("dump", m, ip, data); +} + static struct ftrace_probe_ops traceon_count_probe_ops = { .func = ftrace_traceon_count, .print = ftrace_traceon_print, @@ -342,6 +356,11 @@ static struct ftrace_probe_ops stacktrace_count_probe_ops = { .print = ftrace_stacktrace_print, }; +static struct ftrace_probe_ops dump_probe_ops = { + .func = ftrace_dump_probe, + .print = ftrace_dump_print, +}; + static struct ftrace_probe_ops traceon_probe_ops = { .func = ftrace_traceon, .print = ftrace_traceon_print, @@ -425,6 +444,19 @@ ftrace_stacktrace_callback(struct ftrace_hash *hash, param, enable); } +static int +ftrace_dump_callback(struct ftrace_hash *hash, + char *glob, char *cmd, char *param, int enable) +{ + struct ftrace_probe_ops *ops; + + ops = &dump_probe_ops; + + /* Only dump once. */ + return ftrace_trace_probe_callback(ops, hash, glob, cmd, + "1", enable); +} + static struct ftrace_func_command ftrace_traceon_cmd = { .name = "traceon", .func = ftrace_trace_onoff_callback, @@ -440,6 +472,11 @@ static struct ftrace_func_command ftrace_stacktrace_cmd = { .func = ftrace_stacktrace_callback, }; +static struct ftrace_func_command ftrace_dump_cmd = { + .name = "dump", + .func = ftrace_dump_callback, +}; + static int __init init_func_cmd_traceon(void) { int ret; @@ -450,13 +487,25 @@ static int __init init_func_cmd_traceon(void) ret = register_ftrace_command(&ftrace_traceon_cmd); if (ret) - unregister_ftrace_command(&ftrace_traceoff_cmd); + goto out_free_traceoff; ret = register_ftrace_command(&ftrace_stacktrace_cmd); - if (ret) { - unregister_ftrace_command(&ftrace_traceoff_cmd); - unregister_ftrace_command(&ftrace_traceon_cmd); - } + if (ret) + goto out_free_traceon; + + ret = register_ftrace_command(&ftrace_dump_cmd); + if (ret) + goto out_free_stacktrace; + + return 0; + + out_free_stacktrace: + unregister_ftrace_command(&ftrace_stacktrace_cmd); + out_free_traceon: + unregister_ftrace_command(&ftrace_traceon_cmd); + out_free_traceoff: + unregister_ftrace_command(&ftrace_traceoff_cmd); + return ret; } #else -- cgit v1.2.3 From 90e3c03c3a09a7b176b3fe59d78f5d9755ac8e37 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Tue, 30 Apr 2013 19:00:46 -0400 Subject: tracing: Add function probe to trigger a ftrace dump of current CPU trace Add the "cpudump" command to have the current CPU ftrace buffer dumped to console if a function is hit. This is useful when debugging a tripple fault, where you have an idea of a function that is called just before the tripple fault occurs, and can tell ftrace to dump its content out to the console before it continues. This differs from the "dump" command as it only dumps the content of the ring buffer for the currently executing CPU, and does not show the contents of the other CPUs. Format is: :cpudump echo 'bad_address:cpudump' > /debug/tracing/set_ftrace_filter To remove this: echo '!bad_address:cpudump' > /debug/tracing/set_ftrace_filter Signed-off-by: Steven Rostedt --- Documentation/trace/ftrace.txt | 6 ++++++ kernel/trace/trace_functions.c | 44 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/Documentation/trace/ftrace.txt b/Documentation/trace/ftrace.txt index cc9ec57e157c..b937c6e2163c 100644 --- a/Documentation/trace/ftrace.txt +++ b/Documentation/trace/ftrace.txt @@ -2437,6 +2437,12 @@ The following commands are supported: is hit. Perhaps its a function that is called before a tripple fault happens and does not allow you to get a regular dump. +- cpudump + When the function is hit, it will dump the contents of the ftrace + ring buffer for the current CPU to the console. Unlike the "dump" + command, it only prints out the contents of the ring buffer for the + CPU that executed the function that triggered the dump. + trace_pipe ---------- diff --git a/kernel/trace/trace_functions.c b/kernel/trace/trace_functions.c index d7c8719734b8..b863f93b30f3 100644 --- a/kernel/trace/trace_functions.c +++ b/kernel/trace/trace_functions.c @@ -297,6 +297,14 @@ ftrace_dump_probe(unsigned long ip, unsigned long parent_ip, void **data) ftrace_dump(DUMP_ALL); } +/* Only dump the current CPU buffer. */ +static void +ftrace_cpudump_probe(unsigned long ip, unsigned long parent_ip, void **data) +{ + if (update_count(data)) + ftrace_dump(DUMP_ORIG); +} + static int ftrace_probe_print(const char *name, struct seq_file *m, unsigned long ip, void *data) @@ -341,6 +349,13 @@ ftrace_dump_print(struct seq_file *m, unsigned long ip, return ftrace_probe_print("dump", m, ip, data); } +static int +ftrace_cpudump_print(struct seq_file *m, unsigned long ip, + struct ftrace_probe_ops *ops, void *data) +{ + return ftrace_probe_print("cpudump", m, ip, data); +} + static struct ftrace_probe_ops traceon_count_probe_ops = { .func = ftrace_traceon_count, .print = ftrace_traceon_print, @@ -361,6 +376,11 @@ static struct ftrace_probe_ops dump_probe_ops = { .print = ftrace_dump_print, }; +static struct ftrace_probe_ops cpudump_probe_ops = { + .func = ftrace_cpudump_probe, + .print = ftrace_cpudump_print, +}; + static struct ftrace_probe_ops traceon_probe_ops = { .func = ftrace_traceon, .print = ftrace_traceon_print, @@ -457,6 +477,19 @@ ftrace_dump_callback(struct ftrace_hash *hash, "1", enable); } +static int +ftrace_cpudump_callback(struct ftrace_hash *hash, + char *glob, char *cmd, char *param, int enable) +{ + struct ftrace_probe_ops *ops; + + ops = &cpudump_probe_ops; + + /* Only dump once. */ + return ftrace_trace_probe_callback(ops, hash, glob, cmd, + "1", enable); +} + static struct ftrace_func_command ftrace_traceon_cmd = { .name = "traceon", .func = ftrace_trace_onoff_callback, @@ -477,6 +510,11 @@ static struct ftrace_func_command ftrace_dump_cmd = { .func = ftrace_dump_callback, }; +static struct ftrace_func_command ftrace_cpudump_cmd = { + .name = "cpudump", + .func = ftrace_cpudump_callback, +}; + static int __init init_func_cmd_traceon(void) { int ret; @@ -497,8 +535,14 @@ static int __init init_func_cmd_traceon(void) if (ret) goto out_free_stacktrace; + ret = register_ftrace_command(&ftrace_cpudump_cmd); + if (ret) + goto out_free_dump; + return 0; + out_free_dump: + unregister_ftrace_command(&ftrace_dump_cmd); out_free_stacktrace: unregister_ftrace_command(&ftrace_stacktrace_cmd); out_free_traceon: -- cgit v1.2.3 From 8092e808a31839c502a52d391b15f31c1d8764f5 Mon Sep 17 00:00:00 2001 From: Harsh Prateek Bora Date: Fri, 24 May 2013 12:52:17 +0530 Subject: tracing/trivial: Consolidate error return condition Consolidate the checks for !enabled and !param to return -EINVAL in event_enable_func(). Link: http://lkml.kernel.org/r/1369380137-12452-1-git-send-email-harsh@linux.vnet.ibm.com Signed-off-by: Harsh Prateek Bora Signed-off-by: Steven Rostedt --- kernel/trace/trace_events.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 27963e2bf4bf..db086f172cf5 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -2011,10 +2011,7 @@ event_enable_func(struct ftrace_hash *hash, int ret; /* hash funcs only work with set_ftrace_filter */ - if (!enabled) - return -EINVAL; - - if (!param) + if (!enabled || !param) return -EINVAL; system = strsep(¶m, ":"); -- cgit v1.2.3 From 238ae93d699d59876b470bf6455de22bcfaa9a1b Mon Sep 17 00:00:00 2001 From: Wang YanQing Date: Sun, 26 May 2013 16:52:01 +0800 Subject: tracing: Fix file mode of free_buffer Commit 4f271a2a60c748599b30bb4dafff30d770439b96 (tracing: Add a proc file to stop tracing and free buffer) implement a method to free up ring buffer in kernel memory in the release code path of free_buffer's fd. Then we don't need read/write support for free_buffer, indeed we just have a dummy write fop, and don't implement read fop. So the 0200 is more reasonable file mode for free_buffer than the current file mode 0644. Link: http://lkml.kernel.org/r/20130526085201.GA3183@udknight Acked-by: Vaibhav Nagarnaik Acked-by: David Sharp Signed-off-by: Wang YanQing Signed-off-by: Steven Rostedt --- kernel/trace/trace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 1a41023a1f88..5f4a09c12e0b 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -5935,7 +5935,7 @@ init_tracer_debugfs(struct trace_array *tr, struct dentry *d_tracer) trace_create_file("buffer_total_size_kb", 0444, d_tracer, tr, &tracing_total_entries_fops); - trace_create_file("free_buffer", 0644, d_tracer, + trace_create_file("free_buffer", 0200, d_tracer, tr, &tracing_free_buffer_fops); trace_create_file("trace_marker", 0220, d_tracer, -- cgit v1.2.3 From 7614c3dc74733dff4b0e774f7a894b9ea6ec508c Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 28 May 2013 20:01:16 -0400 Subject: ftrace: Use schedule_on_each_cpu() as a heavy synchronize_sched() The function tracer uses preempt_disable/enable_notrace() for synchronization between reading registered ftrace_ops and unregistering them. Most of the ftrace_ops are global permanent structures that do not require this synchronization. That is, ops may be added and removed from the hlist but are never freed, and wont hurt if a synchronization is missed. But this is not true for dynamically created ftrace_ops or control_ops, which are used by the perf function tracing. The problem here is that the function tracer can be used to trace kernel/user context switches as well as going to and from idle. Basically, it can be used to trace blind spots of the RCU subsystem. This means that even though preempt_disable() is done, a synchronize_sched() will ignore CPUs that haven't made it out of user space or idle. These can include functions that are being traced just before entering or exiting the kernel sections. To implement the RCU synchronization, instead of using synchronize_sched() the use of schedule_on_each_cpu() is performed. This means that when a dynamically allocated ftrace_ops, or a control ops is being unregistered, all CPUs must be touched and execute a ftrace_sync() stub function via the work queues. This will rip CPUs out from idle or in dynamic tick mode. This only happens when a user disables perf function tracing or other dynamically allocated function tracers, but it allows us to continue to debug RCU and context tracking with function tracing. Link: http://lkml.kernel.org/r/1369785676.15552.55.camel@gandalf.local.home Cc: "Paul E. McKenney" Cc: Tejun Heo Cc: Ingo Molnar Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Peter Zijlstra Acked-by: Paul E. McKenney Signed-off-by: Steven Rostedt --- kernel/trace/ftrace.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 6c508ff33c62..800a8a2fbddb 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -413,6 +413,17 @@ static int __register_ftrace_function(struct ftrace_ops *ops) return 0; } +static void ftrace_sync(struct work_struct *work) +{ + /* + * This function is just a stub to implement a hard force + * of synchronize_sched(). This requires synchronizing + * tasks even in userspace and idle. + * + * Yes, function tracing is rude. + */ +} + static int __unregister_ftrace_function(struct ftrace_ops *ops) { int ret; @@ -440,8 +451,12 @@ static int __unregister_ftrace_function(struct ftrace_ops *ops) * so there'll be no new users. We must ensure * all current users are done before we free * the control data. + * Note synchronize_sched() is not enough, as we + * use preempt_disable() to do RCU, but the function + * tracer can be called where RCU is not active + * (before user_exit()). */ - synchronize_sched(); + schedule_on_each_cpu(ftrace_sync); control_ops_free(ops); } } else @@ -456,9 +471,13 @@ static int __unregister_ftrace_function(struct ftrace_ops *ops) /* * Dynamic ops may be freed, we must make sure that all * callers are done before leaving this function. + * + * Again, normal synchronize_sched() is not good enough. + * We need to do a hard force of sched synchronization. */ if (ops->flags & FTRACE_OPS_FL_DYNAMIC) - synchronize_sched(); + schedule_on_each_cpu(ftrace_sync); + return 0; } -- cgit v1.2.3 From 1b3d0623cd9da35fe1d52281ed06c2ff543cd660 Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Fri, 7 Jun 2013 17:02:53 +0800 Subject: ftrace: Remove ftrace_regex_lseek() This is a leftover after ftrace_regex_lseek() was renamed to ftrace_filter_lseek() and then ftrace_filter_lseek() was moved out side of CONFIG_DYNAMIC_FTRACE. Link: http://lkml.kernel.org/r/51B1A1BD.40905@huawei.com Signed-off-by: Li Zefan Signed-off-by: Steven Rostedt --- include/linux/ftrace.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 99d0fbcbaf79..e48ed1d7876d 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -566,10 +566,6 @@ static inline ssize_t ftrace_filter_write(struct file *file, const char __user * size_t cnt, loff_t *ppos) { return -ENODEV; } static inline ssize_t ftrace_notrace_write(struct file *file, const char __user *ubuf, size_t cnt, loff_t *ppos) { return -ENODEV; } -static inline loff_t ftrace_regex_lseek(struct file *file, loff_t offset, int whence) -{ - return -ENODEV; -} static inline int ftrace_regex_release(struct inode *inode, struct file *file) { return -ENODEV; } #endif /* CONFIG_DYNAMIC_FTRACE */ -- cgit v1.2.3 From aaf6ac0f0871cb7fc0f28f3a00edf329bc7adc29 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 7 Jun 2013 15:07:48 +0900 Subject: tracing: Do not call kmem_cache_free() on allocation failure There's no point calling it when _alloc() failed. Link: http://lkml.kernel.org/r/1370585268-29169-1-git-send-email-namhyung@kernel.org Signed-off-by: Namhyung Kim Signed-off-by: Steven Rostedt --- kernel/trace/trace_events.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index db086f172cf5..f57b01574a30 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -97,7 +97,7 @@ static int __trace_define_field(struct list_head *head, const char *type, field = kmem_cache_alloc(field_cachep, GFP_TRACE); if (!field) - goto err; + return -ENOMEM; field->name = name; field->type = type; @@ -114,11 +114,6 @@ static int __trace_define_field(struct list_head *head, const char *type, list_add(&field->link, head); return 0; - -err: - kmem_cache_free(field_cachep, field); - - return -ENOMEM; } int trace_define_field(struct ftrace_event_call *call, const char *type, -- cgit v1.2.3 From 053b525f6f0ef59ac905f0d2e38d39296f8e4fa6 Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Wed, 5 Jun 2013 15:56:41 +0300 Subject: clk: tegra: pllc and pllxc should use pdiv_map The pllc and pllxc code weren't always using the correct pdiv_map to map between the post divider value and the hw p field. This could result in illegal values being programmed in the hw. Signed-off-by: Peter De Schrijver Tested-by: Stephen Warren Acked-by: Stephen Warren Signed-off-by: Mike Turquette --- drivers/clk/tegra/clk-pll.c | 162 ++++++++++++++++++++++---------------------- 1 file changed, 82 insertions(+), 80 deletions(-) diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c index 17c2cc086eb4..85bec1dd87d8 100644 --- a/drivers/clk/tegra/clk-pll.c +++ b/drivers/clk/tegra/clk-pll.c @@ -143,24 +143,6 @@ #define divn_max(p) (divn_mask(p)) #define divp_max(p) (1 << (divp_mask(p))) - -#ifdef CONFIG_ARCH_TEGRA_114_SOC -/* PLLXC has 4-bit PDIV, but entry 15 is not allowed in h/w */ -#define PLLXC_PDIV_MAX 14 - -/* non-monotonic mapping below is not a typo */ -static u8 pllxc_p[PLLXC_PDIV_MAX + 1] = { - /* PDIV: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 */ - /* p: */ 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 12, 16, 20, 24, 32 -}; - -#define PLLCX_PDIV_MAX 7 -static u8 pllcx_p[PLLCX_PDIV_MAX + 1] = { - /* PDIV: 0, 1, 2, 3, 4, 5, 6, 7 */ - /* p: */ 1, 2, 3, 4, 6, 8, 12, 16 -}; -#endif - static void clk_pll_enable_lock(struct tegra_clk_pll *pll) { u32 val; @@ -297,6 +279,39 @@ static void clk_pll_disable(struct clk_hw *hw) spin_unlock_irqrestore(pll->lock, flags); } +static int _p_div_to_hw(struct clk_hw *hw, u8 p_div) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + struct pdiv_map *p_tohw = pll->params->pdiv_tohw; + + if (p_tohw) { + while (p_tohw->pdiv) { + if (p_div <= p_tohw->pdiv) + return p_tohw->hw_val; + p_tohw++; + } + return -EINVAL; + } + return -EINVAL; +} + +static int _hw_to_p_div(struct clk_hw *hw, u8 p_div_hw) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + struct pdiv_map *p_tohw = pll->params->pdiv_tohw; + + if (p_tohw) { + while (p_tohw->pdiv) { + if (p_div_hw == p_tohw->hw_val) + return p_tohw->pdiv; + p_tohw++; + } + return -EINVAL; + } + + return 1 << p_div_hw; +} + static int _get_table_rate(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, unsigned long rate, unsigned long parent_rate) @@ -326,9 +341,9 @@ static int _calc_rate(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, unsigned long rate, unsigned long parent_rate) { struct tegra_clk_pll *pll = to_clk_pll(hw); - struct pdiv_map *p_tohw = pll->params->pdiv_tohw; unsigned long cfreq; u32 p_div = 0; + int ret; switch (parent_rate) { case 12000000: @@ -369,20 +384,16 @@ static int _calc_rate(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, || cfg->output_rate > pll->params->vco_max) { pr_err("%s: Failed to set %s rate %lu\n", __func__, __clk_get_name(hw->clk), rate); + WARN_ON(1); return -EINVAL; } - if (p_tohw) { - p_div = 1 << p_div; - while (p_tohw->pdiv) { - if (p_div <= p_tohw->pdiv) { - cfg->p = p_tohw->hw_val; - break; - } - p_tohw++; - } - if (!p_tohw->pdiv) - return -EINVAL; + if (pll->params->pdiv_tohw) { + ret = _p_div_to_hw(hw, 1 << p_div); + if (ret < 0) + return ret; + else + cfg->p = ret; } else cfg->p = p_div; @@ -485,9 +496,10 @@ static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, } if (_get_table_rate(hw, &cfg, rate, parent_rate) && - _calc_rate(hw, &cfg, rate, parent_rate)) + _calc_rate(hw, &cfg, rate, parent_rate)) { + WARN_ON(1); return -EINVAL; - + } if (pll->lock) spin_lock_irqsave(pll->lock, flags); @@ -507,7 +519,6 @@ static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, { struct tegra_clk_pll *pll = to_clk_pll(hw); struct tegra_clk_pll_freq_table cfg; - u64 output_rate = *prate; if (pll->flags & TEGRA_PLL_FIXED) return pll->fixed_rate; @@ -517,13 +528,12 @@ static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, return __clk_get_rate(hw->clk); if (_get_table_rate(hw, &cfg, rate, *prate) && - _calc_rate(hw, &cfg, rate, *prate)) + _calc_rate(hw, &cfg, rate, *prate)) { + WARN_ON(1); return -EINVAL; + } - output_rate *= cfg.n; - do_div(output_rate, cfg.m * (1 << cfg.p)); - - return output_rate; + return cfg.output_rate; } static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, @@ -531,7 +541,6 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, { struct tegra_clk_pll *pll = to_clk_pll(hw); struct tegra_clk_pll_freq_table cfg; - struct pdiv_map *p_tohw = pll->params->pdiv_tohw; u32 val; u64 rate = parent_rate; int pdiv; @@ -553,21 +562,11 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, _get_pll_mnp(pll, &cfg); - if (p_tohw) { - while (p_tohw->pdiv) { - if (cfg.p == p_tohw->hw_val) { - pdiv = p_tohw->pdiv; - break; - } - p_tohw++; - } - - if (!p_tohw->pdiv) { - WARN_ON(1); - pdiv = 1; - } - } else - pdiv = 1 << cfg.p; + pdiv = _hw_to_p_div(hw, cfg.p); + if (pdiv < 0) { + WARN_ON(1); + pdiv = 1; + } cfg.m *= pdiv; @@ -769,16 +768,22 @@ static int _calc_dynamic_ramp_rate(struct clk_hw *hw, { struct tegra_clk_pll *pll = to_clk_pll(hw); unsigned int p; + int p_div; if (!rate) return -EINVAL; p = DIV_ROUND_UP(pll->params->vco_min, rate); cfg->m = _pll_fixed_mdiv(pll->params, parent_rate); - cfg->p = p; - cfg->output_rate = rate * cfg->p; + cfg->output_rate = rate * p; cfg->n = cfg->output_rate * cfg->m / parent_rate; + p_div = _p_div_to_hw(hw, p); + if (p_div < 0) + return p_div; + else + cfg->p = p_div; + if (cfg->n > divn_max(pll) || cfg->output_rate > pll->params->vco_max) return -EINVAL; @@ -790,18 +795,25 @@ static int _pll_ramp_calc_pll(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct tegra_clk_pll *pll = to_clk_pll(hw); - int err = 0; + int err = 0, p_div; err = _get_table_rate(hw, cfg, rate, parent_rate); if (err < 0) err = _calc_dynamic_ramp_rate(hw, cfg, rate, parent_rate); - else if (cfg->m != _pll_fixed_mdiv(pll->params, parent_rate)) { + else { + if (cfg->m != _pll_fixed_mdiv(pll->params, parent_rate)) { WARN_ON(1); err = -EINVAL; goto out; + } + p_div = _p_div_to_hw(hw, cfg->p); + if (p_div < 0) + return p_div; + else + cfg->p = p_div; } - if (!cfg->p || (cfg->p > pll->params->max_p)) + if (cfg->p > pll->params->max_p) err = -EINVAL; out: @@ -815,7 +827,6 @@ static int clk_pllxc_set_rate(struct clk_hw *hw, unsigned long rate, struct tegra_clk_pll_freq_table cfg, old_cfg; unsigned long flags = 0; int ret = 0; - u8 old_p; ret = _pll_ramp_calc_pll(hw, &cfg, rate, parent_rate); if (ret < 0) @@ -826,11 +837,8 @@ static int clk_pllxc_set_rate(struct clk_hw *hw, unsigned long rate, _get_pll_mnp(pll, &old_cfg); - old_p = pllxc_p[old_cfg.p]; - if (old_cfg.m != cfg.m || old_cfg.n != cfg.n || old_p != cfg.p) { - cfg.p -= 1; + if (old_cfg.m != cfg.m || old_cfg.n != cfg.n || old_cfg.p != cfg.p) ret = _program_pll(hw, &cfg, rate); - } if (pll->lock) spin_unlock_irqrestore(pll->lock, flags); @@ -842,15 +850,19 @@ static long clk_pll_ramp_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate) { struct tegra_clk_pll_freq_table cfg; - int ret = 0; + int ret = 0, p_div; u64 output_rate = *prate; ret = _pll_ramp_calc_pll(hw, &cfg, rate, *prate); if (ret < 0) return ret; + p_div = _hw_to_p_div(hw, cfg.p); + if (p_div < 0) + return p_div; + output_rate *= cfg.n; - do_div(output_rate, cfg.m * cfg.p); + do_div(output_rate, cfg.m * p_div); return output_rate; } @@ -881,8 +893,6 @@ static int clk_pllm_set_rate(struct clk_hw *hw, unsigned long rate, if (ret < 0) goto out; - cfg.p -= 1; - val = readl_relaxed(pll->pmc + PMC_PLLM_WB0_OVERRIDE); if (val & PMC_PLLP_WB0_OVERRIDE_PLLM_OVERRIDE) { val = readl_relaxed(pll->pmc + PMC_PLLM_WB0_OVERRIDE_2); @@ -1010,13 +1020,10 @@ static int _pllcx_update_dynamic_coef(struct tegra_clk_pll *pll, static int clk_pllc_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { - struct tegra_clk_pll_freq_table cfg; + struct tegra_clk_pll_freq_table cfg, old_cfg; struct tegra_clk_pll *pll = to_clk_pll(hw); unsigned long flags = 0; int state, ret = 0; - u32 val; - u16 old_m, old_n; - u8 old_p; if (pll->lock) spin_lock_irqsave(pll->lock, flags); @@ -1025,21 +1032,16 @@ static int clk_pllc_set_rate(struct clk_hw *hw, unsigned long rate, if (ret < 0) goto out; - val = pll_readl_base(pll); - old_m = (val >> pll->divm_shift) & (divm_mask(pll)); - old_n = (val >> pll->divn_shift) & (divn_mask(pll)); - old_p = pllcx_p[(val >> pll->divp_shift) & (divp_mask(pll))]; + _get_pll_mnp(pll, &old_cfg); - if (cfg.m != old_m) { + if (cfg.m != old_cfg.m) { WARN_ON(1); goto out; } - if (old_n == cfg.n && old_p == cfg.p) + if (old_cfg.n == cfg.n && old_cfg.p == cfg.p) goto out; - cfg.p -= 1; - state = clk_pll_is_enabled(hw); if (state) _clk_pllc_disable(hw); -- cgit v1.2.3 From c388eee21ad20929f440d6fae94c995791c5818b Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Wed, 5 Jun 2013 16:37:17 +0300 Subject: clk: tegra: pllp_out2 divider is int only The pllp_out2 should be integer only, the fractional bit should always be 0. Signed-off-by: Peter De Schrijver Tested-by: Stephen Warren Acked-by: Stephen Warren Signed-off-by: Mike Turquette --- drivers/clk/tegra/clk-tegra114.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clk/tegra/clk-tegra114.c b/drivers/clk/tegra/clk-tegra114.c index 797e58f2f811..2dd9b0094c97 100644 --- a/drivers/clk/tegra/clk-tegra114.c +++ b/drivers/clk/tegra/clk-tegra114.c @@ -1200,8 +1200,8 @@ static void __init tegra114_pll_init(void __iomem *clk_base, /* PLLP_OUT2 */ clk = tegra_clk_register_divider("pll_p_out2_div", "pll_p", clk_base + PLLP_OUTA, 0, TEGRA_DIVIDER_FIXED | - TEGRA_DIVIDER_ROUND_UP, 24, 8, 1, - &pll_div_lock); + TEGRA_DIVIDER_ROUND_UP | TEGRA_DIVIDER_INT, 24, + 8, 1, &pll_div_lock); clk = tegra_clk_register_pll_out("pll_p_out2", "pll_p_out2_div", clk_base + PLLP_OUTA, 17, 16, CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0, -- cgit v1.2.3 From aa6fefde62401a84154161a8026872874a70e4c1 Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Wed, 5 Jun 2013 16:51:25 +0300 Subject: clk: tegra: allow PLL m,n,p init from SoC files The m,n,p fields don't have the same bit offset and width across all PLLs. This patch allows SoC specific files to indicate the offset and width. Signed-off-by: Peter De Schrijver Tested-by: Stephen Warren Acked-by: Stephen Warren Signed-off-by: Mike Turquette --- drivers/clk/tegra/clk-pll.c | 60 +++++++++++++++++++++++++-------------------- drivers/clk/tegra/clk.h | 32 +++++++++++++++--------- 2 files changed, 53 insertions(+), 39 deletions(-) diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c index 85bec1dd87d8..3b778d3c828e 100644 --- a/drivers/clk/tegra/clk-pll.c +++ b/drivers/clk/tegra/clk-pll.c @@ -134,15 +134,24 @@ #define pll_writel_misc(val, p) pll_writel(val, p->params->misc_reg, p) #define mask(w) ((1 << (w)) - 1) -#define divm_mask(p) mask(p->divm_width) -#define divn_mask(p) mask(p->divn_width) +#define divm_mask(p) mask(p->params->div_nmp->divm_width) +#define divn_mask(p) mask(p->params->div_nmp->divn_width) #define divp_mask(p) (p->flags & TEGRA_PLLU ? PLLU_POST_DIVP_MASK : \ - mask(p->divp_width)) + mask(p->params->div_nmp->divp_width)) #define divm_max(p) (divm_mask(p)) #define divn_max(p) (divn_mask(p)) #define divp_max(p) (1 << (divp_mask(p))) +static struct div_nmp default_nmp = { + .divn_shift = PLL_BASE_DIVN_SHIFT, + .divn_width = PLL_BASE_DIVN_WIDTH, + .divm_shift = PLL_BASE_DIVM_SHIFT, + .divm_width = PLL_BASE_DIVM_WIDTH, + .divp_shift = PLL_BASE_DIVP_SHIFT, + .divp_width = PLL_BASE_DIVP_WIDTH, +}; + static void clk_pll_enable_lock(struct tegra_clk_pll *pll) { u32 val; @@ -407,12 +416,12 @@ static void _update_pll_mnp(struct tegra_clk_pll *pll, val = pll_readl_base(pll); - val &= ~((divm_mask(pll) << pll->divm_shift) | - (divn_mask(pll) << pll->divn_shift) | - (divp_mask(pll) << pll->divp_shift)); - val |= ((cfg->m << pll->divm_shift) | - (cfg->n << pll->divn_shift) | - (cfg->p << pll->divp_shift)); + val &= ~((divm_mask(pll) << pll->params->div_nmp->divm_shift) | + (divn_mask(pll) << pll->params->div_nmp->divn_shift) | + (divp_mask(pll) << pll->params->div_nmp->divp_shift)); + val |= ((cfg->m << pll->params->div_nmp->divm_shift) | + (cfg->n << pll->params->div_nmp->divn_shift) | + (cfg->p << pll->params->div_nmp->divp_shift)); pll_writel_base(val, pll); } @@ -424,9 +433,9 @@ static void _get_pll_mnp(struct tegra_clk_pll *pll, val = pll_readl_base(pll); - cfg->m = (val >> pll->divm_shift) & (divm_mask(pll)); - cfg->n = (val >> pll->divn_shift) & (divn_mask(pll)); - cfg->p = (val >> pll->divp_shift) & (divp_mask(pll)); + cfg->m = (val >> pll->params->div_nmp->divm_shift) & (divm_mask(pll)); + cfg->n = (val >> pll->params->div_nmp->divn_shift) & (divn_mask(pll)); + cfg->p = (val >> pll->params->div_nmp->divp_shift) & (divp_mask(pll)); } static void _update_pll_cpcon(struct tegra_clk_pll *pll, @@ -646,9 +655,9 @@ static int clk_plle_enable(struct clk_hw *hw) val = pll_readl_base(pll); val &= ~(divm_mask(pll) | divn_mask(pll) | divp_mask(pll)); val &= ~(PLLE_BASE_DIVCML_WIDTH << PLLE_BASE_DIVCML_SHIFT); - val |= sel.m << pll->divm_shift; - val |= sel.n << pll->divn_shift; - val |= sel.p << pll->divp_shift; + val |= sel.m << pll->params->div_nmp->divm_shift; + val |= sel.n << pll->params->div_nmp->divn_shift; + val |= sel.p << pll->params->div_nmp->divp_shift; val |= sel.cpcon << PLLE_BASE_DIVCML_SHIFT; pll_writel_base(val, pll); } @@ -679,9 +688,9 @@ static unsigned long clk_plle_recalc_rate(struct clk_hw *hw, u32 divn = 0, divm = 0, divp = 0; u64 rate = parent_rate; - divp = (val >> pll->divp_shift) & (divp_mask(pll)); - divn = (val >> pll->divn_shift) & (divn_mask(pll)); - divm = (val >> pll->divm_shift) & (divm_mask(pll)); + divp = (val >> pll->params->div_nmp->divp_shift) & (divp_mask(pll)); + divn = (val >> pll->params->div_nmp->divn_shift) & (divn_mask(pll)); + divm = (val >> pll->params->div_nmp->divm_shift) & (divm_mask(pll)); divm *= divp; rate *= divn; @@ -902,7 +911,8 @@ static int clk_pllm_set_rate(struct clk_hw *hw, unsigned long rate, val = readl_relaxed(pll->pmc + PMC_PLLM_WB0_OVERRIDE); val &= ~(divn_mask(pll) | divm_mask(pll)); - val |= (cfg.m << pll->divm_shift) | (cfg.n << pll->divn_shift); + val |= (cfg.m << pll->params->div_nmp->divm_shift) | + (cfg.n << pll->params->div_nmp->divn_shift); writel_relaxed(val, pll->pmc + PMC_PLLM_WB0_OVERRIDE); } else _update_pll_mnp(pll, &cfg); @@ -1180,8 +1190,8 @@ static int clk_plle_tegra114_enable(struct clk_hw *hw) val = pll_readl_base(pll); val &= ~(divm_mask(pll) | divn_mask(pll) | divp_mask(pll)); val &= ~(PLLE_BASE_DIVCML_WIDTH << PLLE_BASE_DIVCML_SHIFT); - val |= sel.m << pll->divm_shift; - val |= sel.n << pll->divn_shift; + val |= sel.m << pll->params->div_nmp->divm_shift; + val |= sel.n << pll->params->div_nmp->divn_shift; val |= sel.cpcon << PLLE_BASE_DIVCML_SHIFT; pll_writel_base(val, pll); udelay(1); @@ -1242,12 +1252,8 @@ static struct tegra_clk_pll *_tegra_init_pll(void __iomem *clk_base, pll->flags = pll_flags; pll->lock = lock; - pll->divp_shift = PLL_BASE_DIVP_SHIFT; - pll->divp_width = PLL_BASE_DIVP_WIDTH; - pll->divn_shift = PLL_BASE_DIVN_SHIFT; - pll->divn_width = PLL_BASE_DIVN_WIDTH; - pll->divm_shift = PLL_BASE_DIVM_SHIFT; - pll->divm_width = PLL_BASE_DIVM_WIDTH; + if (!pll_params->div_nmp) + pll_params->div_nmp = &default_nmp; return pll; } diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index 11278a80e63e..d70eb2d2957f 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -127,6 +127,25 @@ struct pdiv_map { u8 hw_val; }; +/** + * struct div_nmp - offset and width of m,n and p fields + * + * @divn_shift: shift to the feedback divider bit field + * @divn_width: width of the feedback divider bit field + * @divm_shift: shift to the input divider bit field + * @divm_width: width of the input divider bit field + * @divp_shift: shift to the post divider bit field + * @divp_width: width of the post divider bit field + */ +struct div_nmp { + u8 divn_shift; + u8 divn_width; + u8 divm_shift; + u8 divm_width; + u8 divp_shift; + u8 divp_width; +}; + /** * struct clk_pll_params - PLL parameters * @@ -166,6 +185,7 @@ struct tegra_clk_pll_params { int lock_delay; int max_p; struct pdiv_map *pdiv_tohw; + struct div_nmp *div_nmp; }; /** @@ -179,12 +199,6 @@ struct tegra_clk_pll_params { * @flags: PLL flags * @fixed_rate: PLL rate if it is fixed * @lock: register lock - * @divn_shift: shift to the feedback divider bit field - * @divn_width: width of the feedback divider bit field - * @divm_shift: shift to the input divider bit field - * @divm_width: width of the input divider bit field - * @divp_shift: shift to the post divider bit field - * @divp_width: width of the post divider bit field * * Flags: * TEGRA_PLL_USE_LOCK - This flag indicated to use lock bits for @@ -214,12 +228,6 @@ struct tegra_clk_pll { u32 flags; unsigned long fixed_rate; spinlock_t *lock; - u8 divn_shift; - u8 divn_width; - u8 divm_shift; - u8 divm_width; - u8 divp_shift; - u8 divp_width; struct tegra_clk_pll_freq_table *freq_table; struct tegra_clk_pll_params *params; }; -- cgit v1.2.3 From fd428ad87b1f4a12820de07ecb3a155c51c802c7 Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Wed, 5 Jun 2013 16:51:26 +0300 Subject: clk: tegra: PLL m,n,p init for Tegra114 Signed-off-by: Peter De Schrijver Tested-by: Stephen Warren Acked-by: Stephen Warren Signed-off-by: Mike Turquette --- drivers/clk/tegra/clk-tegra114.c | 77 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/drivers/clk/tegra/clk-tegra114.c b/drivers/clk/tegra/clk-tegra114.c index 2dd9b0094c97..db9b1ae1c743 100644 --- a/drivers/clk/tegra/clk-tegra114.c +++ b/drivers/clk/tegra/clk-tegra114.c @@ -265,6 +265,15 @@ static DEFINE_SPINLOCK(clk_doubler_lock); static DEFINE_SPINLOCK(clk_out_lock); static DEFINE_SPINLOCK(sysrate_lock); +static struct div_nmp pllxc_nmp = { + .divm_shift = 0, + .divm_width = 8, + .divn_shift = 8, + .divn_width = 8, + .divp_shift = 20, + .divp_width = 4, +}; + static struct pdiv_map pllxc_p[] = { { .pdiv = 1, .hw_val = 0 }, { .pdiv = 2, .hw_val = 1 }, @@ -313,6 +322,16 @@ static struct tegra_clk_pll_params pll_c_params = { .stepa_shift = 17, .stepb_shift = 9, .pdiv_tohw = pllxc_p, + .div_nmp = &pllxc_nmp, +}; + +static struct div_nmp pllcx_nmp = { + .divm_shift = 0, + .divm_width = 2, + .divn_shift = 8, + .divn_width = 8, + .divp_shift = 20, + .divp_width = 3, }; static struct pdiv_map pllc_p[] = { @@ -346,6 +365,8 @@ static struct tegra_clk_pll_params pll_c2_params = { .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, .pdiv_tohw = pllc_p, + .div_nmp = &pllcx_nmp, + .max_p = 7, .ext_misc_reg[0] = 0x4f0, .ext_misc_reg[1] = 0x4f4, .ext_misc_reg[2] = 0x4f8, @@ -364,11 +385,22 @@ static struct tegra_clk_pll_params pll_c3_params = { .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, .pdiv_tohw = pllc_p, + .div_nmp = &pllcx_nmp, + .max_p = 7, .ext_misc_reg[0] = 0x504, .ext_misc_reg[1] = 0x508, .ext_misc_reg[2] = 0x50c, }; +static struct div_nmp pllm_nmp = { + .divm_shift = 0, + .divm_width = 8, + .divn_shift = 8, + .divn_width = 8, + .divp_shift = 20, + .divp_width = 1, +}; + static struct pdiv_map pllm_p[] = { { .pdiv = 1, .hw_val = 0 }, { .pdiv = 2, .hw_val = 1 }, @@ -398,6 +430,16 @@ static struct tegra_clk_pll_params pll_m_params = { .lock_delay = 300, .max_p = 2, .pdiv_tohw = pllm_p, + .div_nmp = &pllm_nmp, +}; + +static struct div_nmp pllp_nmp = { + .divm_shift = 0, + .divm_width = 5, + .divn_shift = 8, + .divn_width = 10, + .divp_shift = 20, + .divp_width = 3, }; static struct tegra_clk_pll_freq_table pll_p_freq_table[] = { @@ -421,6 +463,7 @@ static struct tegra_clk_pll_params pll_p_params = { .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, + .div_nmp = &pllp_nmp, }; static struct tegra_clk_pll_freq_table pll_a_freq_table[] = { @@ -447,6 +490,7 @@ static struct tegra_clk_pll_params pll_a_params = { .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, + .div_nmp = &pllp_nmp, }; static struct tegra_clk_pll_freq_table pll_d_freq_table[] = { @@ -482,6 +526,7 @@ static struct tegra_clk_pll_params pll_d_params = { .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, .lock_delay = 1000, + .div_nmp = &pllp_nmp, }; static struct tegra_clk_pll_params pll_d2_params = { @@ -496,6 +541,7 @@ static struct tegra_clk_pll_params pll_d2_params = { .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, .lock_delay = 1000, + .div_nmp = &pllp_nmp, }; static struct pdiv_map pllu_p[] = { @@ -504,6 +550,15 @@ static struct pdiv_map pllu_p[] = { { .pdiv = 0, .hw_val = 0 }, }; +static struct div_nmp pllu_nmp = { + .divm_shift = 0, + .divm_width = 5, + .divn_shift = 8, + .divn_width = 10, + .divp_shift = 20, + .divp_width = 1, +}; + static struct tegra_clk_pll_freq_table pll_u_freq_table[] = { {12000000, 480000000, 960, 12, 0, 12}, {13000000, 480000000, 960, 13, 0, 12}, @@ -526,6 +581,7 @@ static struct tegra_clk_pll_params pll_u_params = { .lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE, .lock_delay = 1000, .pdiv_tohw = pllu_p, + .div_nmp = &pllu_nmp, }; static struct tegra_clk_pll_freq_table pll_x_freq_table[] = { @@ -558,6 +614,7 @@ static struct tegra_clk_pll_params pll_x_params = { .stepa_shift = 16, .stepb_shift = 24, .pdiv_tohw = pllxc_p, + .div_nmp = &pllxc_nmp, }; static struct tegra_clk_pll_freq_table pll_e_freq_table[] = { @@ -567,6 +624,15 @@ static struct tegra_clk_pll_freq_table pll_e_freq_table[] = { {0, 0, 0, 0, 0, 0}, }; +static struct div_nmp plle_nmp = { + .divm_shift = 0, + .divm_width = 8, + .divn_shift = 8, + .divn_width = 8, + .divp_shift = 24, + .divp_width = 4, +}; + static struct tegra_clk_pll_params pll_e_params = { .input_min = 12000000, .input_max = 1000000000, @@ -580,6 +646,16 @@ static struct tegra_clk_pll_params pll_e_params = { .lock_mask = PLLE_MISC_LOCK, .lock_enable_bit_idx = PLLE_MISC_LOCK_ENABLE, .lock_delay = 300, + .div_nmp = &plle_nmp, +}; + +static struct div_nmp pllre_nmp = { + .divm_shift = 0, + .divm_width = 8, + .divn_shift = 8, + .divn_width = 8, + .divp_shift = 16, + .divp_width = 4, }; static struct tegra_clk_pll_params pll_re_vco_params = { @@ -596,6 +672,7 @@ static struct tegra_clk_pll_params pll_re_vco_params = { .lock_delay = 300, .iddq_reg = PLLRE_MISC, .iddq_bit_idx = PLLRE_IDDQ_BIT, + .div_nmp = &pllre_nmp, }; /* Peripheral clock registers */ -- cgit v1.2.3 From 35d287a9f78dd4d7b080dee2eea3afce73d1bde1 Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Wed, 5 Jun 2013 17:21:46 +0300 Subject: clk: tegra: fix pllre initilization The PLLRE flags weren't set correctly. Fixed in this patch. Signed-off-by: Peter De Schrijver Tested-by: Stephen Warren Acked-by: Stephen Warren Signed-off-by: Mike Turquette --- drivers/clk/tegra/clk-pll.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c index 3b778d3c828e..393b60c9c49d 100644 --- a/drivers/clk/tegra/clk-pll.c +++ b/drivers/clk/tegra/clk-pll.c @@ -1409,7 +1409,7 @@ struct clk *tegra_clk_register_pllre(const char *name, const char *parent_name, struct tegra_clk_pll *pll; struct clk *clk; - pll_flags |= TEGRA_PLL_HAS_LOCK_ENABLE; + pll_flags |= TEGRA_PLL_HAS_LOCK_ENABLE | TEGRA_PLL_LOCK_MISC; pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags, freq_table, lock); if (IS_ERR(pll)) @@ -1436,7 +1436,6 @@ struct clk *tegra_clk_register_pllre(const char *name, const char *parent_name, val &= ~BIT(29); pll_writel_misc(val, pll); - pll_flags |= TEGRA_PLL_LOCK_MISC; clk = _tegra_clk_register_pll(pll, name, parent_name, flags, &tegra_clk_pllre_ops); if (IS_ERR(clk)) -- cgit v1.2.3 From 29b09447b648ed23ce290994389b3c281a1b6c69 Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Wed, 5 Jun 2013 17:29:28 +0300 Subject: clk: tegra: fix sclk_parents Use the correct parents for sclk according to the TRM. Signed-off-by: Peter De Schrijver Tested-by: Stephen Warren Acked-by: Stephen Warren Signed-off-by: Mike Turquette --- drivers/clk/tegra/clk-tegra114.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/tegra/clk-tegra114.c b/drivers/clk/tegra/clk-tegra114.c index db9b1ae1c743..435b2879b92c 100644 --- a/drivers/clk/tegra/clk-tegra114.c +++ b/drivers/clk/tegra/clk-tegra114.c @@ -1717,7 +1717,7 @@ static void __init tegra114_pmc_clk_init(void __iomem *pmc_base) } static const char *sclk_parents[] = { "clk_m", "pll_c_out1", "pll_p_out4", - "pll_p_out3", "pll_p_out2", "unused", + "pll_p", "pll_p_out2", "unused", "clk_32k", "pll_m_out1" }; static const char *cclk_g_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m", -- cgit v1.2.3 From 7b781c72c97563ba868599f192beb6772c55081b Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Thu, 6 Jun 2013 13:47:28 +0300 Subject: clk: tegra: Add fields for override bits PLLM can have override bits in the PMC. Describe those in the PLL parameters. Signed-off-by: Peter De Schrijver Tested-by: Stephen Warren Acked-by: Stephen Warren Signed-off-by: Mike Turquette --- drivers/clk/tegra/clk.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index d70eb2d2957f..e01ac4608fb0 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -136,6 +136,9 @@ struct pdiv_map { * @divm_width: width of the input divider bit field * @divp_shift: shift to the post divider bit field * @divp_width: width of the post divider bit field + * @override_divn_shift: shift to the feedback divider bitfield in override reg + * @override_divm_shift: shift to the input divider bitfield in override reg + * @override_divp_shift: shift to the post divider bitfield in override reg */ struct div_nmp { u8 divn_shift; @@ -144,6 +147,9 @@ struct div_nmp { u8 divm_width; u8 divp_shift; u8 divp_width; + u8 override_divn_shift; + u8 override_divm_shift; + u8 override_divp_shift; }; /** @@ -180,6 +186,8 @@ struct tegra_clk_pll_params { u32 aux_reg; u32 dyn_ramp_reg; u32 ext_misc_reg[3]; + u32 pmc_divnm_reg; + u32 pmc_divp_reg; int stepa_shift; int stepb_shift; int lock_delay; -- cgit v1.2.3 From d53442e94db0f214989287aa7cd3806cffd1d0b3 Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Thu, 6 Jun 2013 13:47:29 +0300 Subject: clk: tegra: override bits for Tegra114 PLLM Define override bits for Tegra114 PLLM. Signed-off-by: Peter De Schrijver Tested-by: Stephen Warren Acked-by: Stephen Warren Signed-off-by: Mike Turquette [mturquette@linaro.org: fixed up trivial merge conflict] --- drivers/clk/tegra/clk-tegra114.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/clk/tegra/clk-tegra114.c b/drivers/clk/tegra/clk-tegra114.c index 435b2879b92c..7fe36eaa993b 100644 --- a/drivers/clk/tegra/clk-tegra114.c +++ b/drivers/clk/tegra/clk-tegra114.c @@ -251,6 +251,10 @@ #define CLK_SOURCE_XUSB_DEV_SRC 0x60c #define CLK_SOURCE_EMC 0x19c +/* PLLM override registers */ +#define PMC_PLLM_WB0_OVERRIDE 0x1dc +#define PMC_PLLM_WB0_OVERRIDE_2 0x2b0 + static int periph_clk_enb_refcnt[CLK_OUT_ENB_NUM * 32]; static void __iomem *clk_base; @@ -395,10 +399,13 @@ static struct tegra_clk_pll_params pll_c3_params = { static struct div_nmp pllm_nmp = { .divm_shift = 0, .divm_width = 8, + .override_divm_shift = 0, .divn_shift = 8, .divn_width = 8, + .override_divn_shift = 8, .divp_shift = 20, .divp_width = 1, + .override_divp_shift = 27, }; static struct pdiv_map pllm_p[] = { @@ -431,6 +438,8 @@ static struct tegra_clk_pll_params pll_m_params = { .max_p = 2, .pdiv_tohw = pllm_p, .div_nmp = &pllm_nmp, + .pmc_divnm_reg = PMC_PLLM_WB0_OVERRIDE, + .pmc_divp_reg = PMC_PLLM_WB0_OVERRIDE_2, }; static struct div_nmp pllp_nmp = { -- cgit v1.2.3 From c09e32bb67a9fba318ae4387eaabba21e0d07a87 Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Thu, 6 Jun 2013 13:47:30 +0300 Subject: clk: tegra: override bits for Tegra30 PLLM Define override bits for Tegra30 PLLM. Signed-off-by: Peter De Schrijver Tested-by: Stephen Warren Acked-by: Stephen Warren Signed-off-by: Mike Turquette --- drivers/clk/tegra/clk-tegra30.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c index b62e140b9376..e5986565f36e 100644 --- a/drivers/clk/tegra/clk-tegra30.c +++ b/drivers/clk/tegra/clk-tegra30.c @@ -252,6 +252,9 @@ #define CLK_RESET_CCLK_RUN_POLICY 2 #define CLK_RESET_CCLK_BURST_POLICY_PLLX 8 +/* PLLM override registers */ +#define PMC_PLLM_WB0_OVERRIDE 0x1dc + #ifdef CONFIG_PM_SLEEP static struct cpu_clk_suspend_context { u32 pllx_misc; @@ -563,6 +566,18 @@ static struct tegra_clk_pll_params pll_c_params = { .lock_delay = 300, }; +static struct div_nmp pllm_nmp = { + .divn_shift = 8, + .divn_width = 10, + .override_divn_shift = 5, + .divm_shift = 0, + .divm_width = 5, + .override_divm_shift = 0, + .divp_shift = 20, + .divp_width = 3, + .override_divp_shift = 15, +}; + static struct tegra_clk_pll_params pll_m_params = { .input_min = 2000000, .input_max = 31000000, @@ -575,6 +590,9 @@ static struct tegra_clk_pll_params pll_m_params = { .lock_mask = PLL_BASE_LOCK, .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, .lock_delay = 300, + .div_nmp = &pllm_nmp, + .pmc_divnm_reg = PMC_PLLM_WB0_OVERRIDE, + .pmc_divp_reg = PMC_PLLM_WB0_OVERRIDE, }; static struct tegra_clk_pll_params pll_p_params = { -- cgit v1.2.3 From 408a24f8227f259eea97c6b4f66d1592e2f651b6 Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Thu, 6 Jun 2013 13:47:31 +0300 Subject: clk: tegra: Use override bits when needed PLLM has override bits in the PMC. Use those when PLLM_OVERRIDE_ENABLE is set. Signed-off-by: Peter De Schrijver Tested-by: Stephen Warren Acked-by: Stephen Warren Signed-off-by: Mike Turquette --- drivers/clk/tegra/clk-pll.c | 82 +++++++++++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 33 deletions(-) diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c index 393b60c9c49d..197074a57754 100644 --- a/drivers/clk/tegra/clk-pll.c +++ b/drivers/clk/tegra/clk-pll.c @@ -117,10 +117,6 @@ #define PLLCX_MISC2_DEFAULT 0x30211200 #define PLLCX_MISC3_DEFAULT 0x200 -#define PMC_PLLM_WB0_OVERRIDE 0x1dc -#define PMC_PLLM_WB0_OVERRIDE_2 0x2b0 -#define PMC_PLLM_WB0_OVERRIDE_2_DIVP_MASK BIT(27) - #define PMC_SATA_PWRGT 0x1ac #define PMC_SATA_PWRGT_PLLE_IDDQ_VALUE BIT(5) #define PMC_SATA_PWRGT_PLLE_IDDQ_SWCTL BIT(4) @@ -128,10 +124,12 @@ #define pll_readl(offset, p) readl_relaxed(p->clk_base + offset) #define pll_readl_base(p) pll_readl(p->params->base_reg, p) #define pll_readl_misc(p) pll_readl(p->params->misc_reg, p) +#define pll_override_readl(offset, p) readl_relaxed(p->pmc + offset) #define pll_writel(val, offset, p) writel_relaxed(val, p->clk_base + offset) #define pll_writel_base(val, p) pll_writel(val, p->params->base_reg, p) #define pll_writel_misc(val, p) pll_writel(val, p->params->misc_reg, p) +#define pll_override_writel(val, offset, p) writel(val, p->pmc + offset) #define mask(w) ((1 << (w)) - 1) #define divm_mask(p) mask(p->params->div_nmp->divm_width) @@ -413,29 +411,61 @@ static void _update_pll_mnp(struct tegra_clk_pll *pll, struct tegra_clk_pll_freq_table *cfg) { u32 val; + struct tegra_clk_pll_params *params = pll->params; + struct div_nmp *div_nmp = params->div_nmp; + + if ((pll->flags & TEGRA_PLLM) && + (pll_override_readl(PMC_PLLP_WB0_OVERRIDE, pll) & + PMC_PLLP_WB0_OVERRIDE_PLLM_OVERRIDE)) { + val = pll_override_readl(params->pmc_divp_reg, pll); + val &= ~(divp_mask(pll) << div_nmp->override_divp_shift); + val |= cfg->p << div_nmp->override_divp_shift; + pll_override_writel(val, params->pmc_divp_reg, pll); + + val = pll_override_readl(params->pmc_divnm_reg, pll); + val &= ~(divm_mask(pll) << div_nmp->override_divm_shift) | + ~(divn_mask(pll) << div_nmp->override_divn_shift); + val |= (cfg->m << div_nmp->override_divm_shift) | + (cfg->n << div_nmp->override_divn_shift); + pll_override_writel(val, params->pmc_divnm_reg, pll); + } else { + val = pll_readl_base(pll); - val = pll_readl_base(pll); + val &= ~((divm_mask(pll) << div_nmp->divm_shift) | + (divn_mask(pll) << div_nmp->divn_shift) | + (divp_mask(pll) << div_nmp->divp_shift)); - val &= ~((divm_mask(pll) << pll->params->div_nmp->divm_shift) | - (divn_mask(pll) << pll->params->div_nmp->divn_shift) | - (divp_mask(pll) << pll->params->div_nmp->divp_shift)); - val |= ((cfg->m << pll->params->div_nmp->divm_shift) | - (cfg->n << pll->params->div_nmp->divn_shift) | - (cfg->p << pll->params->div_nmp->divp_shift)); + val |= ((cfg->m << div_nmp->divm_shift) | + (cfg->n << div_nmp->divn_shift) | + (cfg->p << div_nmp->divp_shift)); - pll_writel_base(val, pll); + pll_writel_base(val, pll); + } } static void _get_pll_mnp(struct tegra_clk_pll *pll, struct tegra_clk_pll_freq_table *cfg) { u32 val; + struct tegra_clk_pll_params *params = pll->params; + struct div_nmp *div_nmp = params->div_nmp; + + if ((pll->flags & TEGRA_PLLM) && + (pll_override_readl(PMC_PLLP_WB0_OVERRIDE, pll) & + PMC_PLLP_WB0_OVERRIDE_PLLM_OVERRIDE)) { + val = pll_override_readl(params->pmc_divp_reg, pll); + cfg->p = (val >> div_nmp->override_divp_shift) & divp_mask(pll); + + val = pll_override_readl(params->pmc_divnm_reg, pll); + cfg->m = (val >> div_nmp->override_divm_shift) & divm_mask(pll); + cfg->n = (val >> div_nmp->override_divn_shift) & divn_mask(pll); + } else { + val = pll_readl_base(pll); - val = pll_readl_base(pll); - - cfg->m = (val >> pll->params->div_nmp->divm_shift) & (divm_mask(pll)); - cfg->n = (val >> pll->params->div_nmp->divn_shift) & (divn_mask(pll)); - cfg->p = (val >> pll->params->div_nmp->divp_shift) & (divp_mask(pll)); + cfg->m = (val >> div_nmp->divm_shift) & divm_mask(pll); + cfg->n = (val >> div_nmp->divn_shift) & divn_mask(pll); + cfg->p = (val >> div_nmp->divp_shift) & divp_mask(pll); + } } static void _update_pll_cpcon(struct tegra_clk_pll *pll, @@ -883,7 +913,6 @@ static int clk_pllm_set_rate(struct clk_hw *hw, unsigned long rate, struct tegra_clk_pll *pll = to_clk_pll(hw); unsigned long flags = 0; int state, ret = 0; - u32 val; if (pll->lock) spin_lock_irqsave(pll->lock, flags); @@ -902,21 +931,7 @@ static int clk_pllm_set_rate(struct clk_hw *hw, unsigned long rate, if (ret < 0) goto out; - val = readl_relaxed(pll->pmc + PMC_PLLM_WB0_OVERRIDE); - if (val & PMC_PLLP_WB0_OVERRIDE_PLLM_OVERRIDE) { - val = readl_relaxed(pll->pmc + PMC_PLLM_WB0_OVERRIDE_2); - val = cfg.p ? (val | PMC_PLLM_WB0_OVERRIDE_2_DIVP_MASK) : - (val & ~PMC_PLLM_WB0_OVERRIDE_2_DIVP_MASK); - writel_relaxed(val, pll->pmc + PMC_PLLM_WB0_OVERRIDE_2); - - val = readl_relaxed(pll->pmc + PMC_PLLM_WB0_OVERRIDE); - val &= ~(divn_mask(pll) | divm_mask(pll)); - val |= (cfg.m << pll->params->div_nmp->divm_shift) | - (cfg.n << pll->params->div_nmp->divn_shift); - writel_relaxed(val, pll->pmc + PMC_PLLM_WB0_OVERRIDE); - } else - _update_pll_mnp(pll, &cfg); - + _update_pll_mnp(pll, &cfg); out: if (pll->lock) @@ -1460,6 +1475,7 @@ struct clk *tegra_clk_register_pllm(const char *name, const char *parent_name, pll_flags |= TEGRA_PLL_BYPASS; pll_flags |= TEGRA_PLL_HAS_LOCK_ENABLE; + pll_flags |= TEGRA_PLLM; pll = _tegra_init_pll(clk_base, pmc, fixed_rate, pll_params, pll_flags, freq_table, lock); if (IS_ERR(pll)) -- cgit v1.2.3 From 670decdd9544eddbc2ecf14789da4845f8afdab0 Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Wed, 5 Jun 2013 18:06:35 +0300 Subject: clk: use clk_get_rate() for debugfs debugfs uses the rate field directly. However this ignores the CLK_GET_RATE_NOCACHE flag. Call clk_get_rate() instead. Tested-by: Mark Zhang Signed-off-by: Peter De Schrijver Signed-off-by: Mike Turquette --- drivers/clk/clk.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index af0dbccb375d..9b2f94197dbf 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -107,7 +107,7 @@ static void clk_summary_show_one(struct seq_file *s, struct clk *c, int level) seq_printf(s, "%*s%-*s %-11d %-12d %-10lu", level * 3 + 1, "", 30 - level * 3, c->name, - c->enable_count, c->prepare_count, c->rate); + c->enable_count, c->prepare_count, clk_get_rate(c)); seq_printf(s, "\n"); } @@ -166,7 +166,7 @@ static void clk_dump_one(struct seq_file *s, struct clk *c, int level) seq_printf(s, "\"%s\": { ", c->name); seq_printf(s, "\"enable_count\": %d,", c->enable_count); seq_printf(s, "\"prepare_count\": %d,", c->prepare_count); - seq_printf(s, "\"rate\": %lu", c->rate); + seq_printf(s, "\"rate\": %lu", clk_get_rate(c)); } static void clk_dump_subtree(struct seq_file *s, struct clk *c, int level) -- cgit v1.2.3 From 34e452a152efd25d654b7bc809df429337115b03 Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Wed, 5 Jun 2013 18:06:36 +0300 Subject: clk: honor CLK_GET_RATE_NOCACHE in clk_set_rate clk_set_rate() uses clk->rate directly. This causes problems if the clock is marked as CLK_GET_RATE_NOCACHE. Hence call clk_get_rate() to get the current rate. Signed-off-by: Peter De Schrijver Signed-off-by: Mike Turquette --- drivers/clk/clk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 9b2f94197dbf..2e669a87fb15 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1216,7 +1216,7 @@ int clk_set_rate(struct clk *clk, unsigned long rate) clk_prepare_lock(); /* bail early if nothing to do */ - if (rate == clk->rate) + if (rate == clk_get_rate(clk)) goto out; if ((clk->flags & CLK_SET_RATE_GATE) && clk->prepare_count) { -- cgit v1.2.3 From 60bea3b547005204311068b5617984bf3cfbf3d9 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 5 Jun 2013 09:50:30 -0600 Subject: MAINTAINERS: make drivers/clk entry match subdirs Modify the drivers/clk MAINTAINERS entry so that it matches the entire drivers/clk tree, with the exception of clkdev.c which has a separate entry. Make a similar change to pick up all clk-related header files. This causes get_maintainers.pl to spit out the expected results for any patches to clock drivers that are in sub-directories of drivers/clk, e.g. drivers/clk/tegra/. Signed-off-by: Stephen Warren Signed-off-by: Mike Turquette --- MAINTAINERS | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index fd3a495a0005..a82641aab110 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2120,9 +2120,10 @@ M: Mike Turquette L: linux-arm-kernel@lists.infradead.org (same as CLK API & CLKDEV) T: git git://git.linaro.org/people/mturquette/linux.git S: Maintained -F: drivers/clk/clk.c -F: drivers/clk/clk-* +F: drivers/clk/ +X: drivers/clk/clkdev.c F: include/linux/clk-pr* +F: include/linux/clk/ COMMON INTERNET FILE SYSTEM (CIFS) M: Steve French -- cgit v1.2.3 From ff0e0f4f568e8d7593e0035c0c58067bcaf4ab07 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 22 Apr 2013 10:33:32 +0200 Subject: dma: of: Remove restriction that #dma-cells can't be 0 There is no sensible reason why #dma-cells shouldn't be allowed to be 0. It is completely up to the DMA controller how many additional parameters, besides the phandle, it needs to identify a channel. E.g. for DMA controller with only one channel or for DMA controllers which don't have a restriction on which channel can be used for which peripheral it completely legitimate to not require any additional parameters. Also fixes the following warning: drivers/dma/of-dma.c: In function 'of_dma_controller_register': drivers/dma/of-dma.c:67:7: warning: 'nbcells' may be used uninitialized in this function Signed-off-by: Lars-Peter Clausen Acked-by: Arnd Bergmann Signed-off-by: Vinod Koul --- drivers/dma/of-dma.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/dma/of-dma.c b/drivers/dma/of-dma.c index 7aa0864cd487..268cc8ab34e8 100644 --- a/drivers/dma/of-dma.c +++ b/drivers/dma/of-dma.c @@ -64,7 +64,6 @@ int of_dma_controller_register(struct device_node *np, void *data) { struct of_dma *ofdma; - int nbcells; const __be32 *prop; if (!np || !of_dma_xlate) { @@ -77,18 +76,16 @@ int of_dma_controller_register(struct device_node *np, return -ENOMEM; prop = of_get_property(np, "#dma-cells", NULL); - if (prop) - nbcells = be32_to_cpup(prop); - - if (!prop || !nbcells) { - pr_err("%s: #dma-cells property is missing or invalid\n", + if (!prop) { + pr_err("%s: #dma-cells property is missing\n", __func__); kfree(ofdma); return -EINVAL; } + ofdma->of_node = np; - ofdma->of_dma_nbcells = nbcells; + ofdma->of_dma_nbcells = be32_to_cpup(prop); ofdma->of_dma_xlate = of_dma_xlate; ofdma->of_dma_data = data; -- cgit v1.2.3 From 8552bb4f16800d5ebc176a2cf5f2aa55b22731ea Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 22 Apr 2013 10:33:33 +0200 Subject: dma: of: Remove check on always true condition Both of_dma_nbcells field of the of_dma_controller and the args_count field of the dma_spec are initialized by parsing the #dma-cells attribute of their device tree node. So if the device tree nodes of a DMA controller and the dma_spec match this means that of_dma_nbcells and args_count will also match. So the second test in the of_dma_find_controller loop is redundant because given the first test yields true the second test will also yield true. So we can safely remove the test whether of_dma_nbcells matches args_count. Since this was the last user of the of_dma_nbcells field we can remove it altogether. Signed-off-by: Lars-Peter Clausen Acked-by: Arnd Bergmann Signed-off-by: Vinod Koul --- drivers/dma/of-dma.c | 14 +------------- include/linux/of_dma.h | 1 - 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/drivers/dma/of-dma.c b/drivers/dma/of-dma.c index 268cc8ab34e8..75334bdd2c56 100644 --- a/drivers/dma/of-dma.c +++ b/drivers/dma/of-dma.c @@ -35,8 +35,7 @@ static struct of_dma *of_dma_find_controller(struct of_phandle_args *dma_spec) struct of_dma *ofdma; list_for_each_entry(ofdma, &of_dma_list, of_dma_controllers) - if ((ofdma->of_node == dma_spec->np) && - (ofdma->of_dma_nbcells == dma_spec->args_count)) + if (ofdma->of_node == dma_spec->np) return ofdma; pr_debug("%s: can't find DMA controller %s\n", __func__, @@ -64,7 +63,6 @@ int of_dma_controller_register(struct device_node *np, void *data) { struct of_dma *ofdma; - const __be32 *prop; if (!np || !of_dma_xlate) { pr_err("%s: not enough information provided\n", __func__); @@ -75,17 +73,7 @@ int of_dma_controller_register(struct device_node *np, if (!ofdma) return -ENOMEM; - prop = of_get_property(np, "#dma-cells", NULL); - if (!prop) { - pr_err("%s: #dma-cells property is missing\n", - __func__); - kfree(ofdma); - return -EINVAL; - } - - ofdma->of_node = np; - ofdma->of_dma_nbcells = be32_to_cpup(prop); ofdma->of_dma_xlate = of_dma_xlate; ofdma->of_dma_data = data; diff --git a/include/linux/of_dma.h b/include/linux/of_dma.h index 364dda734877..ae36298ba076 100644 --- a/include/linux/of_dma.h +++ b/include/linux/of_dma.h @@ -21,7 +21,6 @@ struct device_node; struct of_dma { struct list_head of_dma_controllers; struct device_node *of_node; - int of_dma_nbcells; struct dma_chan *(*of_dma_xlate) (struct of_phandle_args *, struct of_dma *); void *of_dma_data; -- cgit v1.2.3 From 130d3d68b52097c7ae081109f700b02776adcb9c Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 6 Jun 2013 13:56:19 -0700 Subject: net_sched: psched_ratecfg_precompute() improvements Before allowing 64bits bytes rates, refactor psched_ratecfg_precompute() to get better comments and increased accuracy. rate_bps field is renamed to rate_bytes_ps, as we only have to worry about bytes per second. Signed-off-by: Eric Dumazet Cc: Ben Greear Signed-off-by: David S. Miller --- include/net/sch_generic.h | 4 ++-- net/sched/sch_generic.c | 44 ++++++++++++++++++++------------------------ 2 files changed, 22 insertions(+), 26 deletions(-) diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index df5676029827..6eab63363e59 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -680,7 +680,7 @@ static inline struct sk_buff *skb_act_clone(struct sk_buff *skb, gfp_t gfp_mask, #endif struct psched_ratecfg { - u64 rate_bps; + u64 rate_bytes_ps; /* bytes per second */ u32 mult; u16 overhead; u8 shift; @@ -698,7 +698,7 @@ static inline void psched_ratecfg_getrate(struct tc_ratespec *res, const struct psched_ratecfg *r) { memset(res, 0, sizeof(*res)); - res->rate = r->rate_bps >> 3; + res->rate = r->rate_bytes_ps; res->overhead = r->overhead; } diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 20224086cc28..4626cef4b76e 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -901,37 +901,33 @@ void dev_shutdown(struct net_device *dev) void psched_ratecfg_precompute(struct psched_ratecfg *r, const struct tc_ratespec *conf) { - u64 factor; - u64 mult; - int shift; - memset(r, 0, sizeof(*r)); r->overhead = conf->overhead; - r->rate_bps = (u64)conf->rate << 3; + r->rate_bytes_ps = conf->rate; r->mult = 1; /* - * Calibrate mult, shift so that token counting is accurate - * for smallest packet size (64 bytes). Token (time in ns) is - * computed as (bytes * 8) * NSEC_PER_SEC / rate_bps. It will - * work as long as the smallest packet transfer time can be - * accurately represented in nanosec. + * The deal here is to replace a divide by a reciprocal one + * in fast path (a reciprocal divide is a multiply and a shift) + * + * Normal formula would be : + * time_in_ns = (NSEC_PER_SEC * len) / rate_bps + * + * We compute mult/shift to use instead : + * time_in_ns = (len * mult) >> shift; + * + * We try to get the highest possible mult value for accuracy, + * but have to make sure no overflows will ever happen. */ - if (r->rate_bps > 0) { - /* - * Higher shift gives better accuracy. Find the largest - * shift such that mult fits in 32 bits. - */ - for (shift = 0; shift < 16; shift++) { - r->shift = shift; - factor = 8LLU * NSEC_PER_SEC * (1 << r->shift); - mult = div64_u64(factor, r->rate_bps); - if (mult > UINT_MAX) + if (r->rate_bytes_ps > 0) { + u64 factor = NSEC_PER_SEC; + + for (;;) { + r->mult = div64_u64(factor, r->rate_bytes_ps); + if (r->mult & (1U << 31) || factor & (1ULL << 63)) break; + factor <<= 1; + r->shift++; } - - r->shift = shift - 1; - factor = 8LLU * NSEC_PER_SEC * (1 << r->shift); - r->mult = div64_u64(factor, r->rate_bps); } } EXPORT_SYMBOL(psched_ratecfg_precompute); -- cgit v1.2.3 From 64153ce0a7b61b2a5cacb01805cbf670142339e9 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 6 Jun 2013 14:53:16 -0700 Subject: net_sched: htb: do not setup default rate estimators With a thousand htb classes, est_timer() spends ~5 million cpu cycles and throws out cpu cache, because each htb class has a default rate estimator (est 4sec 16sec). Most users do not use default rate estimators, so switch htb to not setup ones. Add a module parameter (htb_rate_est) so that users relying on this default rate estimator can revert the behavior. echo 1 >/sys/module/sch_htb/parameters/htb_rate_est Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/sched/sch_htb.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index 162fb800754c..1a3655a606c1 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -65,6 +65,10 @@ static int htb_hysteresis __read_mostly = 0; /* whether to use mode hysteresis f module_param (htb_hysteresis, int, 0640); MODULE_PARM_DESC(htb_hysteresis, "Hysteresis mode, less CPU load, less accurate"); +static int htb_rate_est = 0; /* htb classes have a default rate estimator */ +module_param(htb_rate_est, int, 0640); +MODULE_PARM_DESC(htb_rate_est, "setup a default rate estimator (4sec 16sec) for htb classes"); + /* used internaly to keep status of single class */ enum htb_cmode { HTB_CANT_SEND, /* class can't send and can't borrow */ @@ -1366,12 +1370,14 @@ static int htb_change_class(struct Qdisc *sch, u32 classid, if (!cl) goto failure; - err = gen_new_estimator(&cl->bstats, &cl->rate_est, - qdisc_root_sleeping_lock(sch), - tca[TCA_RATE] ? : &est.nla); - if (err) { - kfree(cl); - goto failure; + if (htb_rate_est || tca[TCA_RATE]) { + err = gen_new_estimator(&cl->bstats, &cl->rate_est, + qdisc_root_sleeping_lock(sch), + tca[TCA_RATE] ? : &est.nla); + if (err) { + kfree(cl); + goto failure; + } } cl->refcnt = 1; -- cgit v1.2.3 From e9897071350bd9d94a56b5b6f79c85b1a98fc7e7 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 7 Jun 2013 08:48:57 -0700 Subject: igmp: hash a hash table to speedup ip_check_mc_rcu() After IP route cache removal, multicast applications using a lot of multicast addresses hit a O(N) behavior in ip_check_mc_rcu() Add a per in_device hash table to get faster lookup. This hash table is created only if the number of items in mc_list is above 4. Reported-by: Shawn Bohrer Signed-off-by: Eric Dumazet Tested-by: Shawn Bohrer Reviewed-by: Cong Wang Signed-off-by: David S. Miller --- include/linux/igmp.h | 1 + include/linux/inetdevice.h | 5 ++++ net/ipv4/devinet.c | 1 + net/ipv4/igmp.c | 73 ++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 77 insertions(+), 3 deletions(-) diff --git a/include/linux/igmp.h b/include/linux/igmp.h index 7f2bf1518480..e3362b5f13e8 100644 --- a/include/linux/igmp.h +++ b/include/linux/igmp.h @@ -84,6 +84,7 @@ struct ip_mc_list { struct ip_mc_list *next; struct ip_mc_list __rcu *next_rcu; }; + struct ip_mc_list __rcu *next_hash; struct timer_list timer; int users; atomic_t refcnt; diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h index ea1e3b863890..b99cd23f3474 100644 --- a/include/linux/inetdevice.h +++ b/include/linux/inetdevice.h @@ -50,12 +50,17 @@ struct ipv4_devconf { DECLARE_BITMAP(state, IPV4_DEVCONF_MAX); }; +#define MC_HASH_SZ_LOG 9 + struct in_device { struct net_device *dev; atomic_t refcnt; int dead; struct in_ifaddr *ifa_list; /* IP ifaddr chain */ + struct ip_mc_list __rcu *mc_list; /* IP multicast filter chain */ + struct ip_mc_list __rcu * __rcu *mc_hash; + int mc_count; /* Number of installed mcasts */ spinlock_t mc_tomb_lock; struct ip_mc_list *mc_tomb; diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index b047e2d8a614..3469506c106d 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -215,6 +215,7 @@ void in_dev_finish_destroy(struct in_device *idev) WARN_ON(idev->ifa_list); WARN_ON(idev->mc_list); + kfree(rcu_dereference_protected(idev->mc_hash, 1)); #ifdef NET_REFCNT_DEBUG pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL"); #endif diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 450f625361e4..f72011df9c59 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -1217,6 +1217,57 @@ static void igmp_group_added(struct ip_mc_list *im) * Multicast list managers */ +static u32 ip_mc_hash(const struct ip_mc_list *im) +{ + return hash_32((u32)im->multiaddr, MC_HASH_SZ_LOG); +} + +static void ip_mc_hash_add(struct in_device *in_dev, + struct ip_mc_list *im) +{ + struct ip_mc_list __rcu **mc_hash; + u32 hash; + + mc_hash = rtnl_dereference(in_dev->mc_hash); + if (mc_hash) { + hash = ip_mc_hash(im); + im->next_hash = rtnl_dereference(mc_hash[hash]); + rcu_assign_pointer(mc_hash[hash], im); + return; + } + + /* do not use a hash table for small number of items */ + if (in_dev->mc_count < 4) + return; + + mc_hash = kzalloc(sizeof(struct ip_mc_list *) << MC_HASH_SZ_LOG, + GFP_KERNEL); + if (!mc_hash) + return; + + for_each_pmc_rtnl(in_dev, im) { + hash = ip_mc_hash(im); + im->next_hash = rtnl_dereference(mc_hash[hash]); + RCU_INIT_POINTER(mc_hash[hash], im); + } + + rcu_assign_pointer(in_dev->mc_hash, mc_hash); +} + +static void ip_mc_hash_remove(struct in_device *in_dev, + struct ip_mc_list *im) +{ + struct ip_mc_list __rcu **mc_hash = rtnl_dereference(in_dev->mc_hash); + struct ip_mc_list *aux; + + if (!mc_hash) + return; + mc_hash += ip_mc_hash(im); + while ((aux = rtnl_dereference(*mc_hash)) != im) + mc_hash = &aux->next_hash; + *mc_hash = im->next_hash; +} + /* * A socket has joined a multicast group on device dev. @@ -1258,6 +1309,8 @@ void ip_mc_inc_group(struct in_device *in_dev, __be32 addr) in_dev->mc_count++; rcu_assign_pointer(in_dev->mc_list, im); + ip_mc_hash_add(in_dev, im); + #ifdef CONFIG_IP_MULTICAST igmpv3_del_delrec(in_dev, im->multiaddr); #endif @@ -1314,6 +1367,7 @@ void ip_mc_dec_group(struct in_device *in_dev, __be32 addr) ip = &i->next_rcu) { if (i->multiaddr == addr) { if (--i->users == 0) { + ip_mc_hash_remove(in_dev, i); *ip = i->next_rcu; in_dev->mc_count--; igmp_group_dropped(i); @@ -2321,12 +2375,25 @@ void ip_mc_drop_socket(struct sock *sk) int ip_check_mc_rcu(struct in_device *in_dev, __be32 mc_addr, __be32 src_addr, u16 proto) { struct ip_mc_list *im; + struct ip_mc_list __rcu **mc_hash; struct ip_sf_list *psf; int rv = 0; - for_each_pmc_rcu(in_dev, im) { - if (im->multiaddr == mc_addr) - break; + mc_hash = rcu_dereference(in_dev->mc_hash); + if (mc_hash) { + u32 hash = hash_32((u32)mc_addr, MC_HASH_SZ_LOG); + + for (im = rcu_dereference(mc_hash[hash]); + im != NULL; + im = rcu_dereference(im->next_hash)) { + if (im->multiaddr == mc_addr) + break; + } + } else { + for_each_pmc_rcu(in_dev, im) { + if (im->multiaddr == mc_addr) + break; + } } if (im && proto == IPPROTO_IGMP) { rv = 1; -- cgit v1.2.3 From 946d3bd7231be3b6202759ea0bea59989ae28c4a Mon Sep 17 00:00:00 2001 From: Shawn Bohrer Date: Fri, 7 Jun 2013 12:34:43 -0500 Subject: igmp: remove unnecessary in_device member zeroing ip_mc_init_dev() is passed a freshly kzalloc'd in_device so it is unnecessary to explicitly zero out the members. Signed-off-by: Shawn Bohrer Signed-off-by: David S. Miller --- net/ipv4/igmp.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index f72011df9c59..a09190ddffba 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -1435,13 +1435,9 @@ void ip_mc_init_dev(struct in_device *in_dev) { ASSERT_RTNL(); - in_dev->mc_tomb = NULL; #ifdef CONFIG_IP_MULTICAST - in_dev->mr_gq_running = 0; setup_timer(&in_dev->mr_gq_timer, igmp_gq_timer_expire, (unsigned long)in_dev); - in_dev->mr_ifc_count = 0; - in_dev->mc_count = 0; setup_timer(&in_dev->mr_ifc_timer, igmp_ifc_timer_expire, (unsigned long)in_dev); in_dev->mr_qrv = IGMP_Unsolicited_Report_Count; -- cgit v1.2.3 From da5bab079f9b7d90ba234965a14914ace55e45e9 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Sat, 8 Jun 2013 12:56:03 +0200 Subject: net: udp4: move GSO functions to udp_offload Similarly to TCP offloading and UDPv6 offloading, move all related UDPv4 functions to udp_offload.c to make things more explicit. Also, by this, we can make those functions static. Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/net/udp.h | 7 ++-- net/ipv4/Makefile | 2 +- net/ipv4/af_inet.c | 9 +---- net/ipv4/udp.c | 75 +------------------------------------ net/ipv4/udp_offload.c | 100 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 108 insertions(+), 85 deletions(-) create mode 100644 net/ipv4/udp_offload.c diff --git a/include/net/udp.h b/include/net/udp.h index 065f379c6503..b30a71a51839 100644 --- a/include/net/udp.h +++ b/include/net/udp.h @@ -187,6 +187,8 @@ extern int udp_ioctl(struct sock *sk, int cmd, unsigned long arg); extern int udp_disconnect(struct sock *sk, int flags); extern unsigned int udp_poll(struct file *file, struct socket *sock, poll_table *wait); +extern struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb, + netdev_features_t features); extern int udp_lib_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen); extern int udp_lib_setsockopt(struct sock *sk, int level, int optname, @@ -262,11 +264,10 @@ extern int udp4_proc_init(void); extern void udp4_proc_exit(void); #endif +extern int udpv4_offload_init(void); + extern void udp_init(void); -extern int udp4_ufo_send_check(struct sk_buff *skb); -extern struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, - netdev_features_t features); extern void udp_encap_enable(void); #if IS_ENABLED(CONFIG_IPV6) extern void udpv6_encap_enable(void); diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile index 4d3e138c564f..7fcf8101d85f 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile @@ -9,7 +9,7 @@ obj-y := route.o inetpeer.o protocol.o \ tcp.o tcp_input.o tcp_output.o tcp_timer.o tcp_ipv4.o \ tcp_minisocks.o tcp_cong.o tcp_metrics.o tcp_fastopen.o \ tcp_offload.o datagram.o raw.o udp.o udplite.o \ - arp.o icmp.o devinet.o af_inet.o igmp.o \ + udp_offload.o arp.o icmp.o devinet.o af_inet.o igmp.o \ fib_frontend.o fib_semantics.o fib_trie.o \ inet_fragment.o ping.o diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 7b514290efc6..5598b06d62db 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1566,13 +1566,6 @@ static const struct net_protocol udp_protocol = { .netns_ok = 1, }; -static const struct net_offload udp_offload = { - .callbacks = { - .gso_send_check = udp4_ufo_send_check, - .gso_segment = udp4_ufo_fragment, - }, -}; - static const struct net_protocol icmp_protocol = { .handler = icmp_rcv, .err_handler = icmp_err, @@ -1672,7 +1665,7 @@ static int __init ipv4_offload_init(void) /* * Add offloads */ - if (inet_add_offload(&udp_offload, IPPROTO_UDP) < 0) + if (udpv4_offload_init() < 0) pr_crit("%s: Cannot add UDP protocol offload\n", __func__); if (tcpv4_offload_init() < 0) pr_crit("%s: Cannot add TCP protocol offload\n", __func__); diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 2955b25aee6d..f65bc32c0266 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -2290,29 +2290,8 @@ void __init udp_init(void) sysctl_udp_wmem_min = SK_MEM_QUANTUM; } -int udp4_ufo_send_check(struct sk_buff *skb) -{ - if (!pskb_may_pull(skb, sizeof(struct udphdr))) - return -EINVAL; - - if (likely(!skb->encapsulation)) { - const struct iphdr *iph; - struct udphdr *uh; - - iph = ip_hdr(skb); - uh = udp_hdr(skb); - - uh->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len, - IPPROTO_UDP, 0); - skb->csum_start = skb_transport_header(skb) - skb->head; - skb->csum_offset = offsetof(struct udphdr, check); - skb->ip_summed = CHECKSUM_PARTIAL; - } - return 0; -} - -static struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb, - netdev_features_t features) +struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb, + netdev_features_t features) { struct sk_buff *segs = ERR_PTR(-EINVAL); int mac_len = skb->mac_len; @@ -2371,53 +2350,3 @@ static struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb, out: return segs; } - -struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, - netdev_features_t features) -{ - struct sk_buff *segs = ERR_PTR(-EINVAL); - unsigned int mss; - mss = skb_shinfo(skb)->gso_size; - if (unlikely(skb->len <= mss)) - goto out; - - if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) { - /* Packet is from an untrusted source, reset gso_segs. */ - int type = skb_shinfo(skb)->gso_type; - - if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY | - SKB_GSO_UDP_TUNNEL | - SKB_GSO_GRE | SKB_GSO_MPLS) || - !(type & (SKB_GSO_UDP)))) - goto out; - - skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss); - - segs = NULL; - goto out; - } - - /* Fragment the skb. IP headers of the fragments are updated in - * inet_gso_segment() - */ - if (skb->encapsulation && skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL) - segs = skb_udp_tunnel_segment(skb, features); - else { - int offset; - __wsum csum; - - /* Do software UFO. Complete and fill in the UDP checksum as - * HW cannot do checksum of UDP packets sent as multiple - * IP fragments. - */ - offset = skb_checksum_start_offset(skb); - csum = skb_checksum(skb, offset, skb->len - offset, 0); - offset += skb->csum_offset; - *(__sum16 *)(skb->data + offset) = csum_fold(csum); - skb->ip_summed = CHECKSUM_NONE; - - segs = skb_segment(skb, features); - } -out: - return segs; -} diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c new file mode 100644 index 000000000000..f35eccaa855e --- /dev/null +++ b/net/ipv4/udp_offload.c @@ -0,0 +1,100 @@ +/* + * IPV4 GSO/GRO offload support + * Linux INET implementation + * + * 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. + * + * UDPv4 GSO support + */ + +#include +#include +#include + +static int udp4_ufo_send_check(struct sk_buff *skb) +{ + if (!pskb_may_pull(skb, sizeof(struct udphdr))) + return -EINVAL; + + if (likely(!skb->encapsulation)) { + const struct iphdr *iph; + struct udphdr *uh; + + iph = ip_hdr(skb); + uh = udp_hdr(skb); + + uh->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len, + IPPROTO_UDP, 0); + skb->csum_start = skb_transport_header(skb) - skb->head; + skb->csum_offset = offsetof(struct udphdr, check); + skb->ip_summed = CHECKSUM_PARTIAL; + } + + return 0; +} + +static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, + netdev_features_t features) +{ + struct sk_buff *segs = ERR_PTR(-EINVAL); + unsigned int mss; + + mss = skb_shinfo(skb)->gso_size; + if (unlikely(skb->len <= mss)) + goto out; + + if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) { + /* Packet is from an untrusted source, reset gso_segs. */ + int type = skb_shinfo(skb)->gso_type; + + if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY | + SKB_GSO_UDP_TUNNEL | + SKB_GSO_GRE | SKB_GSO_MPLS) || + !(type & (SKB_GSO_UDP)))) + goto out; + + skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss); + + segs = NULL; + goto out; + } + + /* Fragment the skb. IP headers of the fragments are updated in + * inet_gso_segment() + */ + if (skb->encapsulation && skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL) + segs = skb_udp_tunnel_segment(skb, features); + else { + int offset; + __wsum csum; + + /* Do software UFO. Complete and fill in the UDP checksum as + * HW cannot do checksum of UDP packets sent as multiple + * IP fragments. + */ + offset = skb_checksum_start_offset(skb); + csum = skb_checksum(skb, offset, skb->len - offset, 0); + offset += skb->csum_offset; + *(__sum16 *)(skb->data + offset) = csum_fold(csum); + skb->ip_summed = CHECKSUM_NONE; + + segs = skb_segment(skb, features); + } +out: + return segs; +} + +static const struct net_offload udpv4_offload = { + .callbacks = { + .gso_send_check = udp4_ufo_send_check, + .gso_segment = udp4_ufo_fragment, + }, +}; + +int __init udpv4_offload_init(void) +{ + return inet_add_offload(&udpv4_offload, IPPROTO_UDP); +} -- cgit v1.2.3 From 7a6e288d2745611bef5b614acf19644283765732 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Sat, 8 Jun 2013 14:18:16 +0200 Subject: pktgen: ipv6: numa: consolidate skb allocation to pktgen_alloc_skb We currently allow for numa-node aware skb allocation only within the fill_packet_ipv4() path, but not in fill_packet_ipv6(). Consolidate that code to a common allocation helper to enable numa-node aware skb allocation for ipv6, and use it in both paths. This also makes both functions a bit more readable. Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller --- net/core/pktgen.c | 52 +++++++++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/net/core/pktgen.c b/net/core/pktgen.c index 303412d8332b..9640972ec50e 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -2627,6 +2627,29 @@ static void pktgen_finalize_skb(struct pktgen_dev *pkt_dev, struct sk_buff *skb, pgh->tv_usec = htonl(timestamp.tv_usec); } +static struct sk_buff *pktgen_alloc_skb(struct net_device *dev, + struct pktgen_dev *pkt_dev, + unsigned int extralen) +{ + struct sk_buff *skb = NULL; + unsigned int size = pkt_dev->cur_pkt_size + 64 + extralen + + pkt_dev->pkt_overhead; + + if (pkt_dev->flags & F_NODE) { + int node = pkt_dev->node >= 0 ? pkt_dev->node : numa_node_id(); + + skb = __alloc_skb(NET_SKB_PAD + size, GFP_NOWAIT, 0, node); + if (likely(skb)) { + skb_reserve(skb, NET_SKB_PAD); + skb->dev = dev; + } + } else { + skb = __netdev_alloc_skb(dev, size, GFP_NOWAIT); + } + + return skb; +} + static struct sk_buff *fill_packet_ipv4(struct net_device *odev, struct pktgen_dev *pkt_dev) { @@ -2657,32 +2680,13 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev, datalen = (odev->hard_header_len + 16) & ~0xf; - if (pkt_dev->flags & F_NODE) { - int node; - - if (pkt_dev->node >= 0) - node = pkt_dev->node; - else - node = numa_node_id(); - - skb = __alloc_skb(NET_SKB_PAD + pkt_dev->cur_pkt_size + 64 - + datalen + pkt_dev->pkt_overhead, GFP_NOWAIT, 0, node); - if (likely(skb)) { - skb_reserve(skb, NET_SKB_PAD); - skb->dev = odev; - } - } - else - skb = __netdev_alloc_skb(odev, - pkt_dev->cur_pkt_size + 64 - + datalen + pkt_dev->pkt_overhead, GFP_NOWAIT); - + skb = pktgen_alloc_skb(odev, pkt_dev, datalen); if (!skb) { sprintf(pkt_dev->result, "No memory"); return NULL; } - prefetchw(skb->data); + prefetchw(skb->data); skb_reserve(skb, datalen); /* Reserve for ethernet and IP header */ @@ -2786,15 +2790,13 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev, mod_cur_headers(pkt_dev); queue_map = pkt_dev->cur_queue_map; - skb = __netdev_alloc_skb(odev, - pkt_dev->cur_pkt_size + 64 - + 16 + pkt_dev->pkt_overhead, GFP_NOWAIT); + skb = pktgen_alloc_skb(odev, pkt_dev, 16); if (!skb) { sprintf(pkt_dev->result, "No memory"); return NULL; } - prefetchw(skb->data); + prefetchw(skb->data); skb_reserve(skb, 16); /* Reserve for ethernet and IP header */ -- cgit v1.2.3 From 3f8b96379a820318db37f7b6e81e6e459ad56efe Mon Sep 17 00:00:00 2001 From: Hong zhi guo Date: Sun, 9 Jun 2013 20:15:20 +0800 Subject: veth: remove redundant call of dev_alloc_name it's called in the following register_netdevice. No need to call it here. Tested with "ip link add type veth" and "ip link add xxx%d type veth". Signed-off-by: Hong Zhiguo Signed-off-by: David S. Miller --- drivers/net/veth.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 177f911f5946..da866523cf20 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -379,12 +379,6 @@ static int veth_newlink(struct net *src_net, struct net_device *dev, else snprintf(dev->name, IFNAMSIZ, DRV_NAME "%%d"); - if (strchr(dev->name, '%')) { - err = dev_alloc_name(dev, dev->name); - if (err < 0) - goto err_alloc_name; - } - err = register_netdevice(dev); if (err < 0) goto err_register_dev; @@ -404,7 +398,6 @@ static int veth_newlink(struct net *src_net, struct net_device *dev, err_register_dev: /* nothing to do */ -err_alloc_name: err_configure_peer: unregister_netdevice(peer); return err; -- cgit v1.2.3 From afd6eae13cc4229e25d59334cdc46d042b24a4a5 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Mon, 10 Jun 2013 00:16:52 +0400 Subject: 3c59x: consolidate error cleanup in vortex_init_one() The PCI driver's probe() method duplicates the error cleanup code each time it has to do error exit. Consolidate the error cleanup code in one place and use *goto* to jump to the right places. Signed-off-by: Sergei Shtylyov Acked-by: Steffen Klassert Signed-off-by: David S. Miller --- drivers/net/ethernet/3com/3c59x.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/3com/3c59x.c b/drivers/net/ethernet/3com/3c59x.c index 30e74211a755..ad5272b348f0 100644 --- a/drivers/net/ethernet/3com/3c59x.c +++ b/drivers/net/ethernet/3com/3c59x.c @@ -1012,10 +1012,8 @@ static int vortex_init_one(struct pci_dev *pdev, goto out; rc = pci_request_regions(pdev, DRV_NAME); - if (rc < 0) { - pci_disable_device(pdev); - goto out; - } + if (rc < 0) + goto out_disable; unit = vortex_cards_found; @@ -1032,23 +1030,24 @@ static int vortex_init_one(struct pci_dev *pdev, if (!ioaddr) /* If mapping fails, fall-back to BAR 0... */ ioaddr = pci_iomap(pdev, 0, 0); if (!ioaddr) { - pci_release_regions(pdev); - pci_disable_device(pdev); rc = -ENOMEM; - goto out; + goto out_release; } rc = vortex_probe1(&pdev->dev, ioaddr, pdev->irq, ent->driver_data, unit); - if (rc < 0) { - pci_iounmap(pdev, ioaddr); - pci_release_regions(pdev); - pci_disable_device(pdev); - goto out; - } + if (rc < 0) + goto out_iounmap; vortex_cards_found++; + goto out; +out_iounmap: + pci_iounmap(pdev, ioaddr); +out_release: + pci_release_regions(pdev); +out_disable: + pci_disable_device(pdev); out: return rc; } -- cgit v1.2.3 From 941e173a538c58f7f60e3f70ab0e35d51563405b Mon Sep 17 00:00:00 2001 From: Tushar Behera Date: Mon, 10 Jun 2013 17:05:05 +0530 Subject: net: fec: Convert to use devm_ioremap_resource Commit 75096579c3ac ("lib: devres: Introduce devm_ioremap_resource()") introduced devm_ioremap_resource() and deprecated the use of devm_request_and_ioremap(). Signed-off-by: Tushar Behera CC: netdev@vger.kernel.org CC: "David S. Miller" Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fec_main.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index fa4a68fc01cb..c540055e4f4f 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -1876,17 +1876,17 @@ fec_probe(struct platform_device *pdev) (pdev->id_entry->driver_data & FEC_QUIRK_HAS_GBIT)) fep->pause_flag |= FEC_PAUSE_FLAG_AUTONEG; - fep->hwp = devm_request_and_ioremap(&pdev->dev, r); + fep->hwp = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(fep->hwp)) { + ret = PTR_ERR(fep->hwp); + goto failed_ioremap; + } + fep->pdev = pdev; fep->dev_id = dev_id++; fep->bufdesc_ex = 0; - if (!fep->hwp) { - ret = -ENOMEM; - goto failed_ioremap; - } - platform_set_drvdata(pdev, ndev); ret = of_get_phy_mode(pdev->dev.of_node); -- cgit v1.2.3 From eed5d29d7818cc7a84e60123555cae154e5b4a73 Mon Sep 17 00:00:00 2001 From: Tushar Behera Date: Mon, 10 Jun 2013 17:05:06 +0530 Subject: net: emaclite: Convert to use devm_ioremap_resource Commit 75096579c3ac ("lib: devres: Introduce devm_ioremap_resource()") introduced devm_ioremap_resource() and deprecated the use of devm_request_and_ioremap(). Signed-off-by: Tushar Behera CC: netdev@vger.kernel.org CC: "David S. Miller" CC: Michal Simek Signed-off-by: David S. Miller --- drivers/net/ethernet/xilinx/xilinx_emaclite.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/xilinx/xilinx_emaclite.c b/drivers/net/ethernet/xilinx/xilinx_emaclite.c index 1cd131bde680..fd4dbdae5331 100644 --- a/drivers/net/ethernet/xilinx/xilinx_emaclite.c +++ b/drivers/net/ethernet/xilinx/xilinx_emaclite.c @@ -1159,9 +1159,11 @@ static int xemaclite_of_probe(struct platform_device *ofdev) ndev->irq = res->start; res = platform_get_resource(ofdev, IORESOURCE_MEM, 0); - lp->base_addr = devm_request_and_ioremap(&ofdev->dev, res); - if (!lp->base_addr) + lp->base_addr = devm_ioremap_resource(&ofdev->dev, res); + if (IS_ERR(lp->base_addr)) { + rc = PTR_ERR(lp->base_addr); goto error; + } ndev->mem_start = res->start; ndev->mem_end = res->end; -- cgit v1.2.3 From 32766fff24a761079636bb96c389411d24a334c6 Mon Sep 17 00:00:00 2001 From: Tushar Behera Date: Mon, 10 Jun 2013 17:05:07 +0530 Subject: net: can: Convert to use devm_ioremap_resource Commit 75096579c3ac ("lib: devres: Introduce devm_ioremap_resource()") introduced devm_ioremap_resource() and deprecated the use of devm_request_and_ioremap(). Signed-off-by: Tushar Behera CC: netdev@vger.kernel.org CC: linux-can@vger.kernel.org CC: Marc Kleine-Budde CC: Wolfgang Grandegger Acked-by: Marc Kleine-Budde Signed-off-by: David S. Miller --- drivers/net/can/c_can/c_can_platform.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/can/c_can/c_can_platform.c b/drivers/net/can/c_can/c_can_platform.c index 6b6130b8bdc4..b918c7329426 100644 --- a/drivers/net/can/c_can/c_can_platform.c +++ b/drivers/net/can/c_can/c_can_platform.c @@ -201,8 +201,8 @@ static int c_can_plat_probe(struct platform_device *pdev) priv->instance = pdev->id; res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - priv->raminit_ctrlreg = devm_request_and_ioremap(&pdev->dev, res); - if (!priv->raminit_ctrlreg || priv->instance < 0) + priv->raminit_ctrlreg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->raminit_ctrlreg) || priv->instance < 0) dev_info(&pdev->dev, "control memory is not used for raminit\n"); else priv->raminit = c_can_hw_raminit; -- cgit v1.2.3 From 8a46f4f7f95f2bece108998a2e1b87b58f99d590 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Sat, 1 Jun 2013 00:22:52 +0200 Subject: of: remove #ifdef from linux/of_platform.h A lot of code uses the functions from of_platform.h when built for devicetree-enabled platforms but can also be built without them. In order to avoid using #ifdef everywhere in drivers, this makes all the function declarations visible, which means we can use "if (IS_ENABLED(CONFIG_OF))" in driver code and get build coverage over the code but let the compiler drop the reference in the object code. Signed-off-by: Arnd Bergmann Cc: Grant Likely Cc: Rob Herring Signed-off-by: Grant Likely --- include/linux/of_platform.h | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/include/linux/of_platform.h b/include/linux/of_platform.h index 2a93b64a3869..7747ad0027ae 100644 --- a/include/linux/of_platform.h +++ b/include/linux/of_platform.h @@ -13,8 +13,6 @@ #include #include - -#ifdef CONFIG_OF_DEVICE #include #include #include @@ -82,7 +80,6 @@ extern struct platform_device *of_device_alloc(struct device_node *np, struct device *parent); extern struct platform_device *of_find_device_by_node(struct device_node *np); -#ifdef CONFIG_OF_ADDRESS /* device reg helpers depend on OF_ADDRESS */ /* Platform devices and busses creation */ extern struct platform_device *of_platform_device_create(struct device_node *np, const char *bus_id, @@ -91,17 +88,12 @@ extern struct platform_device *of_platform_device_create(struct device_node *np, extern int of_platform_bus_probe(struct device_node *root, const struct of_device_id *matches, struct device *parent); +#ifdef CONFIG_OF_ADDRESS extern int of_platform_populate(struct device_node *root, const struct of_device_id *matches, const struct of_dev_auxdata *lookup, struct device *parent); -#endif /* CONFIG_OF_ADDRESS */ - -#endif /* CONFIG_OF_DEVICE */ - -#if !defined(CONFIG_OF_ADDRESS) -struct of_dev_auxdata; -struct device_node; +#else static inline int of_platform_populate(struct device_node *root, const struct of_device_id *matches, const struct of_dev_auxdata *lookup, @@ -109,6 +101,6 @@ static inline int of_platform_populate(struct device_node *root, { return -ENODEV; } -#endif /* !CONFIG_OF_ADDRESS */ +#endif #endif /* _LINUX_OF_PLATFORM_H */ -- cgit v1.2.3 From 792efb84670c5b4c58669645cb6fd7e943376ee0 Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Tue, 7 May 2013 11:34:11 +0530 Subject: of/base: fix typos the function of_property_read_u8/16/32_array() has a parameter out_values, but the description mentioned it as out_value. This patch fixes this typo. Signed-off-by: Lad, Prabhakar Signed-off-by: Grant Likely --- drivers/of/base.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/of/base.c b/drivers/of/base.c index f53b992f060a..c1cfc4553a5d 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -809,7 +809,7 @@ EXPORT_SYMBOL_GPL(of_property_read_u32_index); * * @np: device node from which the property value is to be read. * @propname: name of the property to be searched. - * @out_value: pointer to return value, modified only if return value is 0. + * @out_values: pointer to return value, modified only if return value is 0. * @sz: number of array elements to read * * Search for a property in a device node and read 8-bit value(s) from @@ -820,7 +820,7 @@ EXPORT_SYMBOL_GPL(of_property_read_u32_index); * dts entry of array should be like: * property = /bits/ 8 <0x50 0x60 0x70>; * - * The out_value is modified only if a valid u8 value can be decoded. + * The out_values is modified only if a valid u8 value can be decoded. */ int of_property_read_u8_array(const struct device_node *np, const char *propname, u8 *out_values, size_t sz) @@ -842,7 +842,7 @@ EXPORT_SYMBOL_GPL(of_property_read_u8_array); * * @np: device node from which the property value is to be read. * @propname: name of the property to be searched. - * @out_value: pointer to return value, modified only if return value is 0. + * @out_values: pointer to return value, modified only if return value is 0. * @sz: number of array elements to read * * Search for a property in a device node and read 16-bit value(s) from @@ -853,7 +853,7 @@ EXPORT_SYMBOL_GPL(of_property_read_u8_array); * dts entry of array should be like: * property = /bits/ 16 <0x5000 0x6000 0x7000>; * - * The out_value is modified only if a valid u16 value can be decoded. + * The out_values is modified only if a valid u16 value can be decoded. */ int of_property_read_u16_array(const struct device_node *np, const char *propname, u16 *out_values, size_t sz) @@ -876,7 +876,7 @@ EXPORT_SYMBOL_GPL(of_property_read_u16_array); * * @np: device node from which the property value is to be read. * @propname: name of the property to be searched. - * @out_value: pointer to return value, modified only if return value is 0. + * @out_values: pointer to return value, modified only if return value is 0. * @sz: number of array elements to read * * Search for a property in a device node and read 32-bit value(s) from @@ -884,7 +884,7 @@ EXPORT_SYMBOL_GPL(of_property_read_u16_array); * -ENODATA if property does not have a value, and -EOVERFLOW if the * property data isn't large enough. * - * The out_value is modified only if a valid u32 value can be decoded. + * The out_values is modified only if a valid u32 value can be decoded. */ int of_property_read_u32_array(const struct device_node *np, const char *propname, u32 *out_values, -- cgit v1.2.3 From 494235005bb7ed2f7f8713a492172897160b0e83 Mon Sep 17 00:00:00 2001 From: Florian Vaussard Date: Thu, 23 May 2013 10:11:37 +0200 Subject: ARM: dts: omap3-devkit8000: fix NAND memory binding Commit d36b4cd 'ARM: OMAP2+: Add additional GPMC timing parameters' updated GPMC binding, but omap3-devkit8000 was not updated accordingly, resulting in a broken configuration. Signed-off-by: Florian Vaussard Signed-off-by: Grant Likely --- arch/arm/boot/dts/omap3-devkit8000.dts | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/arch/arm/boot/dts/omap3-devkit8000.dts b/arch/arm/boot/dts/omap3-devkit8000.dts index 8a5cdcc6debd..e5b35f526b74 100644 --- a/arch/arm/boot/dts/omap3-devkit8000.dts +++ b/arch/arm/boot/dts/omap3-devkit8000.dts @@ -123,20 +123,21 @@ reg = <0 0 0>; /* CS0, offset 0 */ nand-bus-width = <16>; - gpmc,sync-clk = <0>; - gpmc,cs-on = <0>; - gpmc,cs-rd-off = <44>; - gpmc,cs-wr-off = <44>; - gpmc,adv-on = <6>; - gpmc,adv-rd-off = <34>; - gpmc,adv-wr-off = <44>; - gpmc,we-off = <40>; - gpmc,oe-off = <54>; - gpmc,access = <64>; - gpmc,rd-cycle = <82>; - gpmc,wr-cycle = <82>; - gpmc,wr-access = <40>; - gpmc,wr-data-mux-bus = <0>; + gpmc,device-nand; + gpmc,sync-clki-ps = <0>; + gpmc,cs-on-ns = <0>; + gpmc,cs-rd-off-ns = <44>; + gpmc,cs-wr-off-ns = <44>; + gpmc,adv-on-ns = <6>; + gpmc,adv-rd-off-ns = <34>; + gpmc,adv-wr-off-ns = <44>; + gpmc,we-off-ns = <40>; + gpmc,oe-off-ns = <54>; + gpmc,access-ns = <64>; + gpmc,rd-cycle-ns = <82>; + gpmc,wr-cycle-ns = <82>; + gpmc,wr-access-ns = <40>; + gpmc,wr-data-mux-bus-ns = <0>; #address-cells = <1>; #size-cells = <1>; -- cgit v1.2.3 From 8d561b60a9174defff2e48e109f68a5f2ae7dd9c Mon Sep 17 00:00:00 2001 From: J Keerthy Date: Thu, 6 Jun 2013 10:57:21 +0530 Subject: mfd: DT bindings for the palmas family MFD Add the various binding files for the palmas family of chips. There is a top level MFD binding then a seperate binding for regulators IP blocks on chips. Signed-off-by: Graeme Gregory Signed-off-by: J Keerthy Signed-off-by: Ian Lartey Reviewed-by: Stephen Warren Signed-off-by: Grant Likely --- Documentation/devicetree/bindings/mfd/palmas.txt | 49 +++++++++++++++ .../devicetree/bindings/regulator/palmas-pmic.txt | 72 ++++++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 Documentation/devicetree/bindings/mfd/palmas.txt create mode 100644 Documentation/devicetree/bindings/regulator/palmas-pmic.txt diff --git a/Documentation/devicetree/bindings/mfd/palmas.txt b/Documentation/devicetree/bindings/mfd/palmas.txt new file mode 100644 index 000000000000..892537d1a48f --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/palmas.txt @@ -0,0 +1,49 @@ +* palmas device tree bindings + +The TI palmas family current members :- +twl6035 (palmas) +twl6037 (palmas) +tps65913 (palmas) +tps65914 (palmas) + +Required properties: +- compatible : Should be from the list + ti,twl6035 + ti,twl6036 + ti,twl6037 + ti,tps65913 + ti,tps65914 + ti,tps80036 +and also the generic series names + ti,palmas +- interrupt-controller : palmas has its own internal IRQs +- #interrupt-cells : should be set to 2 for IRQ number and flags + The first cell is the IRQ number. + The second cell is the flags, encoded as the trigger masks from + Documentation/devicetree/bindings/interrupts.txt +- interrupt-parent : The parent interrupt controller. + +Optional properties: + ti,mux-padX : set the pad register X (1-2) to the correct muxing for the + hardware, if not set will use muxing in OTP. + +Example: + +palmas { + compatible = "ti,twl6035", "ti,palmas"; + reg = <0x48> + interrupt-parent = <&intc>; + interrupt-controller; + #interrupt-cells = <2>; + + ti,mux-pad1 = <0>; + ti,mux-pad2 = <0>; + + #address-cells = <1>; + #size-cells = <0>; + + pmic { + compatible = "ti,twl6035-pmic", "ti,palmas-pmic"; + .... + }; +} diff --git a/Documentation/devicetree/bindings/regulator/palmas-pmic.txt b/Documentation/devicetree/bindings/regulator/palmas-pmic.txt new file mode 100644 index 000000000000..d5a308629c57 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/palmas-pmic.txt @@ -0,0 +1,72 @@ +* palmas regulator IP block devicetree bindings + +Required properties: +- compatible : Should be from the list + ti,twl6035-pmic + ti,twl6036-pmic + ti,twl6037-pmic + ti,tps65913-pmic + ti,tps65914-pmic +and also the generic series names + ti,palmas-pmic +- interrupt-parent : The parent interrupt controller which is palmas. +- interrupts : The interrupt number and the type which can be looked up here: + arch/arm/boot/dts/include/dt-bindings/interrupt-controller/irq.h +- interrupts-name: The names of the individual interrupts. + +Optional properties: +- ti,ldo6-vibrator : ldo6 is in vibrator mode + +Optional nodes: +- regulators : Must contain a sub-node per regulator from the list below. + Each sub-node should contain the constraints and initialization + information for that regulator. See regulator.txt for a + description of standard properties for these sub-nodes. + Additional custom properties are listed below. + + For ti,palmas-pmic - smps12, smps123, smps3 depending on OTP, + smps45, smps457, smps7 depending on variant, smps6, smps[8-10], + ldo[1-9], ldoln, ldousb. + + Optional sub-node properties: + ti,warm-reset - maintain voltage during warm reset(boolean) + ti,roof-floor - control voltage selection by pin(boolean) + ti,sleep-mode - mode to adopt in pmic sleep 0 - off, 1 - auto, + 2 - eco, 3 - forced pwm + ti,tstep - slope control 0 - Jump, 1 10mV/us, 2 5mV/us, 3 2.5mV/us + ti,smps-range - OTP has the wrong range set for the hardware so override + 0 - low range, 1 - high range. + +Example: + +#include + +pmic { + compatible = "ti,twl6035-pmic", "ti,palmas-pmic"; + interrupt-parent = <&palmas>; + interrupts = <14 IRQ_TYPE_NONE>; + interrupts-name = "short-irq"; + + ti,ldo6-vibrator; + + regulators { + smps12_reg : smps12 { + regulator-name = "smps12"; + regulator-min-microvolt = < 600000>; + regulator-max-microvolt = <1500000>; + regulator-always-on; + regulator-boot-on; + ti,warm-reset; + ti,roof-floor; + ti,mode-sleep = <0>; + ti,tstep = <0>; + ti,smps-range = <1>; + }; + + ldo1_reg: ldo1 { + regulator-name = "ldo1"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + }; + }; +}; -- cgit v1.2.3 From dffebd2c5cd528a136b276a2a75c56222312d7a4 Mon Sep 17 00:00:00 2001 From: Narendra K Date: Mon, 10 Jun 2013 19:34:03 +0530 Subject: doc:networking: Update comment for dev_id field in netdevice.h This patch updates the comment for 'dev_id' field in 'include/linux/netdevice.h' to reflect the intended usage of 'dev_id'. References: http://marc.info/?l=linux-netdev&m=136992115300526&w=2 References: http://marc.info/?l=linux-netdev&m=137062569014612&w=2 Signed-off-by: Narendra K Reviewed-by: Ben Hutchings Signed-off-by: David S. Miller --- include/linux/netdevice.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 2ecb96d9a1e5..e5d65573b4d6 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1174,8 +1174,10 @@ struct net_device { unsigned char addr_assign_type; /* hw address assignment type */ unsigned char addr_len; /* hardware address length */ unsigned char neigh_priv_len; - unsigned short dev_id; /* for shared network cards */ - + unsigned short dev_id; /* Used to differentiate devices + * that share the same link + * layer address + */ spinlock_t addr_list_lock; struct netdev_hw_addr_list uc; /* Unicast mac addresses */ struct netdev_hw_addr_list mc; /* Multicast mac addresses */ -- cgit v1.2.3 From 6c31ff366c1116823e77019bae3e92e9d77a49f4 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 10 Jun 2013 17:42:23 +0200 Subject: team: remove synchronize_rcu() called during queue override change This patch removes synchronize_rcu() from function __team_queue_override_port_del(). That can be done because it is ok to do list_del_rcu() and list_add_tail_rcu() on the same list_head member without calling synchronize_rcu() in between. A bit of refactoring needed to be done because INIT_LIST_HEAD needed to be removed (to not kill the forward pointer) as well. Signed-off-by: Jiri Pirko Acked-by: Flavio Leitner Signed-off-by: David S. Miller --- drivers/net/team/team.c | 63 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 47 insertions(+), 16 deletions(-) diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 04e9adb748c5..9cbe09df7765 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -725,9 +725,9 @@ static bool team_queue_override_transmit(struct team *team, struct sk_buff *skb) static void __team_queue_override_port_del(struct team *team, struct team_port *port) { + if (!port->queue_id) + return; list_del_rcu(&port->qom_list); - synchronize_rcu(); - INIT_LIST_HEAD(&port->qom_list); } static bool team_queue_override_port_has_gt_prio_than(struct team_port *port, @@ -749,9 +749,8 @@ static void __team_queue_override_port_add(struct team *team, struct list_head *qom_list; struct list_head *node; - if (!port->queue_id || !team_port_enabled(port)) + if (!port->queue_id) return; - qom_list = __team_get_qom_list(team, port->queue_id); node = qom_list; list_for_each_entry(cur, qom_list, qom_list) { @@ -768,7 +767,7 @@ static void __team_queue_override_enabled_check(struct team *team) bool enabled = false; list_for_each_entry(port, &team->port_list, list) { - if (!list_empty(&port->qom_list)) { + if (port->queue_id) { enabled = true; break; } @@ -780,14 +779,44 @@ static void __team_queue_override_enabled_check(struct team *team) team->queue_override_enabled = enabled; } -static void team_queue_override_port_refresh(struct team *team, - struct team_port *port) +static void team_queue_override_port_prio_changed(struct team *team, + struct team_port *port) { + if (!port->queue_id || team_port_enabled(port)) + return; __team_queue_override_port_del(team, port); __team_queue_override_port_add(team, port); __team_queue_override_enabled_check(team); } +static void team_queue_override_port_change_queue_id(struct team *team, + struct team_port *port, + u16 new_queue_id) +{ + if (team_port_enabled(port)) { + __team_queue_override_port_del(team, port); + port->queue_id = new_queue_id; + __team_queue_override_port_add(team, port); + __team_queue_override_enabled_check(team); + } else { + port->queue_id = new_queue_id; + } +} + +static void team_queue_override_port_add(struct team *team, + struct team_port *port) +{ + __team_queue_override_port_add(team, port); + __team_queue_override_enabled_check(team); +} + +static void team_queue_override_port_del(struct team *team, + struct team_port *port) +{ + __team_queue_override_port_del(team, port); + __team_queue_override_enabled_check(team); +} + /**************** * Port handling @@ -819,7 +848,7 @@ static void team_port_enable(struct team *team, hlist_add_head_rcu(&port->hlist, team_port_index_hash(team, port->index)); team_adjust_ops(team); - team_queue_override_port_refresh(team, port); + team_queue_override_port_add(team, port); if (team->ops.port_enabled) team->ops.port_enabled(team, port); } @@ -848,7 +877,7 @@ static void team_port_disable(struct team *team, hlist_del_rcu(&port->hlist); __reconstruct_port_hlist(team, port->index); port->index = -1; - team_queue_override_port_refresh(team, port); + team_queue_override_port_del(team, port); __team_adjust_ops(team, team->en_port_count - 1); /* * Wait until readers see adjusted ops. This ensures that @@ -1259,9 +1288,12 @@ static int team_priority_option_set(struct team *team, struct team_gsetter_ctx *ctx) { struct team_port *port = ctx->info->port; + s32 priority = ctx->data.s32_val; - port->priority = ctx->data.s32_val; - team_queue_override_port_refresh(team, port); + if (port->priority == priority) + return 0; + port->priority = priority; + team_queue_override_port_prio_changed(team, port); return 0; } @@ -1278,17 +1310,16 @@ static int team_queue_id_option_set(struct team *team, struct team_gsetter_ctx *ctx) { struct team_port *port = ctx->info->port; + u16 new_queue_id = ctx->data.u32_val; - if (port->queue_id == ctx->data.u32_val) + if (port->queue_id == new_queue_id) return 0; - if (ctx->data.u32_val >= team->dev->real_num_tx_queues) + if (new_queue_id >= team->dev->real_num_tx_queues) return -EINVAL; - port->queue_id = ctx->data.u32_val; - team_queue_override_port_refresh(team, port); + team_queue_override_port_change_queue_id(team, port, new_queue_id); return 0; } - static const struct team_option team_options[] = { { .name = "mode", -- cgit v1.2.3 From d80b35beac78b52faad2359adf6a6b14e2725e51 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 10 Jun 2013 17:42:24 +0200 Subject: team: use kfree_rcu instead of synchronize_rcu in team_port_dev Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 3 +-- include/linux/if_team.h | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 9cbe09df7765..488ed4f535e5 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -1192,8 +1192,7 @@ static int team_port_del(struct team *team, struct net_device *port_dev) team_port_set_orig_dev_addr(port); dev_set_mtu(port_dev, port->orig.mtu); - synchronize_rcu(); - kfree(port); + kfree_rcu(port, rcu); netdev_info(dev, "Port device %s removed\n", portname); __team_compute_features(team); diff --git a/include/linux/if_team.h b/include/linux/if_team.h index 4474557904f6..705459524706 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h @@ -69,6 +69,7 @@ struct team_port { s32 priority; /* lower number ~ higher priority */ u16 queue_id; struct list_head qom_list; /* node in queue override mapping list */ + struct rcu_head rcu; long mode_priv[0]; }; -- cgit v1.2.3 From 735d381fa57c573935d35a24ea271ec99897ac63 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 10 Jun 2013 17:42:25 +0200 Subject: team: remove synchronize_rcu() called during port disable Check the unlikely case of team->en_port_count == 0 before modulo operation. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 20 +++++--------------- drivers/net/team/team_mode_loadbalance.c | 3 +-- drivers/net/team/team_mode_roundrobin.c | 3 ++- include/linux/if_team.h | 10 ++++++++++ 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 488ed4f535e5..e46fef38685b 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -525,31 +525,26 @@ static void team_set_no_mode(struct team *team) team->mode = &__team_no_mode; } -static void __team_adjust_ops(struct team *team, int en_port_count) +static void team_adjust_ops(struct team *team) { /* * To avoid checks in rx/tx skb paths, ensure here that non-null and * correct ops are always set. */ - if (!en_port_count || !team_is_mode_set(team) || + if (!team->en_port_count || !team_is_mode_set(team) || !team->mode->ops->transmit) team->ops.transmit = team_dummy_transmit; else team->ops.transmit = team->mode->ops->transmit; - if (!en_port_count || !team_is_mode_set(team) || + if (!team->en_port_count || !team_is_mode_set(team) || !team->mode->ops->receive) team->ops.receive = team_dummy_receive; else team->ops.receive = team->mode->ops->receive; } -static void team_adjust_ops(struct team *team) -{ - __team_adjust_ops(team, team->en_port_count); -} - /* * We can benefit from the fact that it's ensured no port is present * at the time of mode change. Therefore no packets are in fly so there's no @@ -877,14 +872,9 @@ static void team_port_disable(struct team *team, hlist_del_rcu(&port->hlist); __reconstruct_port_hlist(team, port->index); port->index = -1; - team_queue_override_port_del(team, port); - __team_adjust_ops(team, team->en_port_count - 1); - /* - * Wait until readers see adjusted ops. This ensures that - * readers never see team->en_port_count == 0 - */ - synchronize_rcu(); team->en_port_count--; + team_queue_override_port_del(team, port); + team_adjust_ops(team); } #define TEAM_VLAN_FEATURES (NETIF_F_ALL_CSUM | NETIF_F_SG | \ diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c index cdc31b5ea15e..829a9cd2b4da 100644 --- a/drivers/net/team/team_mode_loadbalance.c +++ b/drivers/net/team/team_mode_loadbalance.c @@ -112,9 +112,8 @@ static struct team_port *lb_hash_select_tx_port(struct team *team, struct sk_buff *skb, unsigned char hash) { - int port_index; + int port_index = team_num_to_port_index(team, hash); - port_index = hash % team->en_port_count; return team_get_port_by_index_rcu(team, port_index); } diff --git a/drivers/net/team/team_mode_roundrobin.c b/drivers/net/team/team_mode_roundrobin.c index d268e4de781b..90311c7375fb 100644 --- a/drivers/net/team/team_mode_roundrobin.c +++ b/drivers/net/team/team_mode_roundrobin.c @@ -30,7 +30,8 @@ static bool rr_transmit(struct team *team, struct sk_buff *skb) struct team_port *port; int port_index; - port_index = rr_priv(team)->sent_packets++ % team->en_port_count; + port_index = team_num_to_port_index(team, + rr_priv(team)->sent_packets++); port = team_get_port_by_index_rcu(team, port_index); port = team_get_first_port_txable_rcu(team, port); if (unlikely(!port)) diff --git a/include/linux/if_team.h b/include/linux/if_team.h index 705459524706..b662045a81c0 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h @@ -229,6 +229,16 @@ static inline struct team_port *team_get_port_by_index(struct team *team, return port; return NULL; } + +static inline int team_num_to_port_index(struct team *team, int num) +{ + int en_port_count = ACCESS_ONCE(team->en_port_count); + + if (unlikely(!en_port_count)) + return 0; + return num % en_port_count; +} + static inline struct team_port *team_get_port_by_index_rcu(struct team *team, int port_index) { -- cgit v1.2.3 From 503b47eafc39cb28db7b694aa0bb9276adccb89c Mon Sep 17 00:00:00 2001 From: Rami Rosen Date: Mon, 10 Jun 2013 18:58:16 +0300 Subject: ipv4: remove is_data also from ip_options documentation. commit ef722495c8867aacc1db0675a6737e5cf1e72e07 ( [IPV4]: Remove unused ip_options->is_data) removed the unused is_data member from ip_options struct. This patch removes is_data also from the documentation of the ip_options struct. Signed-off-by: Rami Rosen Signed-off-by: David S. Miller --- include/net/inet_sock.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index 7235ae73a1e8..b21a7f06d6a4 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -32,7 +32,6 @@ * * @faddr - Saved first hop address * @nexthop - Saved nexthop address in LSRR and SSRR - * @is_data - Options in __data, rather than skb * @is_strictroute - Strict source route * @srr_is_hit - Packet destination addr was our one * @is_changed - IP checksum more not valid -- cgit v1.2.3 From 6602041b8343fcdb6433cc72ca6cb6c2e189da6d Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Mon, 10 Jun 2013 18:03:17 +0100 Subject: sfc: Store port number in private data, not net_device::dev_id We should not use net_device::dev_id to indicate the port number, as this affects the way the local part of IPv6 addresses is normally generated. This field was intended for use where multiple devices may share a single assigned MAC address and need to have different IPv6 addresses. Siena's two ports each have their own MAC addresses. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/net_driver.h | 3 ++- drivers/net/ethernet/sfc/siena.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index 39d6bd77f015..9a2914cfd345 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -784,6 +784,7 @@ struct efx_nic { char name[IFNAMSIZ]; struct pci_dev *pci_dev; + unsigned int port_num; const struct efx_nic_type *type; int legacy_irq; bool legacy_irq_enabled; @@ -916,7 +917,7 @@ static inline int efx_dev_registered(struct efx_nic *efx) static inline unsigned int efx_port_num(struct efx_nic *efx) { - return efx->net_dev->dev_id; + return efx->port_num; } /** diff --git a/drivers/net/ethernet/sfc/siena.c b/drivers/net/ethernet/sfc/siena.c index 51669244d154..8c91775e3c5f 100644 --- a/drivers/net/ethernet/sfc/siena.c +++ b/drivers/net/ethernet/sfc/siena.c @@ -304,7 +304,7 @@ static int siena_probe_nic(struct efx_nic *efx) } efx_reado(efx, ®, FR_AZ_CS_DEBUG); - efx->net_dev->dev_id = EFX_OWORD_FIELD(reg, FRF_CZ_CS_PORT_NUM) - 1; + efx->port_num = EFX_OWORD_FIELD(reg, FRF_CZ_CS_PORT_NUM) - 1; efx_mcdi_init(efx); -- cgit v1.2.3 From 5eaedf31319d5f80eaaee1eec8dd18c0b452f0d1 Mon Sep 17 00:00:00 2001 From: Claudiu Manoil Date: Mon, 10 Jun 2013 20:19:48 +0300 Subject: gianfar: Add backwards compatible Single Queue mode polling Older Single Queue (SQ_SG_MODE) devices like TSEC (i.e. mpc83xx) don't feature the frame receive indication bits (RXF) in RSTAT. For these and for the rest of the SQ_SG_MODE devices, provide the appropiate polling routine that handles a single pair of Rx/Tx BD rings, removing the overhead incurred by the multiple queues/ multiple interrupt group devices (veTSEC/ eTSEC2.0 devices). So this is primarily a fix for the TSEC devices. Signed-off-by: Claudiu Manoil Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/gianfar.c | 51 ++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index 14f06947e70d..8d2db7b808b7 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -128,6 +128,7 @@ static void gfar_set_multi(struct net_device *dev); static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr); static void gfar_configure_serdes(struct net_device *dev); static int gfar_poll(struct napi_struct *napi, int budget); +static int gfar_poll_sq(struct napi_struct *napi, int budget); #ifdef CONFIG_NET_POLL_CONTROLLER static void gfar_netpoll(struct net_device *dev); #endif @@ -1038,9 +1039,13 @@ static int gfar_probe(struct platform_device *ofdev) dev->ethtool_ops = &gfar_ethtool_ops; /* Register for napi ...We are registering NAPI for each grp */ - for (i = 0; i < priv->num_grps; i++) - netif_napi_add(dev, &priv->gfargrp[i].napi, gfar_poll, + if (priv->mode == SQ_SG_MODE) + netif_napi_add(dev, &priv->gfargrp[0].napi, gfar_poll_sq, GFAR_DEV_WEIGHT); + else + for (i = 0; i < priv->num_grps; i++) + netif_napi_add(dev, &priv->gfargrp[i].napi, gfar_poll, + GFAR_DEV_WEIGHT); if (priv->device_flags & FSL_GIANFAR_DEV_HAS_CSUM) { dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_SG | @@ -2823,6 +2828,48 @@ int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit) return howmany; } +static int gfar_poll_sq(struct napi_struct *napi, int budget) +{ + struct gfar_priv_grp *gfargrp = + container_of(napi, struct gfar_priv_grp, napi); + struct gfar __iomem *regs = gfargrp->regs; + struct gfar_priv_tx_q *tx_queue = gfargrp->priv->tx_queue[0]; + struct gfar_priv_rx_q *rx_queue = gfargrp->priv->rx_queue[0]; + int work_done = 0; + + /* Clear IEVENT, so interrupts aren't called again + * because of the packets that have already arrived + */ + gfar_write(®s->ievent, IEVENT_RTX_MASK); + + /* run Tx cleanup to completion */ + if (tx_queue->tx_skbuff[tx_queue->skb_dirtytx]) + gfar_clean_tx_ring(tx_queue); + + work_done = gfar_clean_rx_ring(rx_queue, budget); + + if (work_done < budget) { + napi_complete(napi); + /* Clear the halt bit in RSTAT */ + gfar_write(®s->rstat, gfargrp->rstat); + + gfar_write(®s->imask, IMASK_DEFAULT); + + /* If we are coalescing interrupts, update the timer + * Otherwise, clear it + */ + gfar_write(®s->txic, 0); + if (likely(tx_queue->txcoalescing)) + gfar_write(®s->txic, tx_queue->txic); + + gfar_write(®s->rxic, 0); + if (unlikely(rx_queue->rxcoalescing)) + gfar_write(®s->rxic, rx_queue->rxic); + } + + return work_done; +} + static int gfar_poll(struct napi_struct *napi, int budget) { struct gfar_priv_grp *gfargrp = -- cgit v1.2.3 From adace89562c7a9645b8dc84f6e1ac7ba8756094e Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Wed, 8 May 2013 17:29:24 +0100 Subject: arm64: extable: sort the exception table at build time As is done for other architectures, sort the exception table at build-time rather than during boot. Since sortextable appears to be a standalone C program relying on the host elf.h to provide EM_AARCH64, I've had to add a conditional check in order to allow cross-compilation on machines that aren't running a bleeding-edge libc-dev. Signed-off-by: Will Deacon Signed-off-by: Catalin Marinas --- arch/arm64/Kconfig | 1 + arch/arm64/kernel/vmlinux.lds.S | 10 +--------- scripts/sortextable.c | 5 +++++ 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 56b3f6d447ae..c3e3a17215a0 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -7,6 +7,7 @@ config ARM64 select ARM_AMBA select ARM_ARCH_TIMER select ARM_GIC + select BUILDTIME_EXTABLE_SORT select CLONE_BACKWARDS select COMMON_CLK select GENERIC_CLOCKEVENTS diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S index 3fae2be8b016..5e06a1786e26 100644 --- a/arch/arm64/kernel/vmlinux.lds.S +++ b/arch/arm64/kernel/vmlinux.lds.S @@ -56,7 +56,7 @@ SECTIONS } RO_DATA(PAGE_SIZE) - + EXCEPTION_TABLE(8) _etext = .; /* End of text and rodata section */ . = ALIGN(PAGE_SIZE); @@ -98,14 +98,6 @@ SECTIONS CACHELINE_ALIGNED_DATA(64) READ_MOSTLY_DATA(64) - /* - * The exception fixup table (might need resorting at runtime) - */ - . = ALIGN(32); - __start___ex_table = .; - *(__ex_table) - __stop___ex_table = .; - /* * and the usual data section */ diff --git a/scripts/sortextable.c b/scripts/sortextable.c index 1f10e89d15b4..f9ce1160419b 100644 --- a/scripts/sortextable.c +++ b/scripts/sortextable.c @@ -31,6 +31,10 @@ #include #include +#ifndef EM_AARCH64 +#define EM_AARCH64 183 +#endif + static int fd_map; /* File descriptor for file being modified. */ static int mmap_failed; /* Boolean flag. */ static void *ehdr_curr; /* current ElfXX_Ehdr * for resource cleanup */ @@ -249,6 +253,7 @@ do_file(char const *const fname) custom_sort = sort_relative_table; break; case EM_ARM: + case EM_AARCH64: case EM_MIPS: break; } /* end switch */ -- cgit v1.2.3 From 1442b6ed249d2b3d2cfcf45b65ac64393495c96c Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Sat, 16 Mar 2013 08:48:13 +0000 Subject: arm64: debug: consolidate software breakpoint handlers The software breakpoint handlers are hooked in directly from ptrace, which makes it difficult to add additional handlers for things like kprobes and kgdb. This patch moves the handling code into debug-monitors.c, where we can dispatch to different debug subsystems more easily. Signed-off-by: Will Deacon Signed-off-by: Catalin Marinas --- arch/arm64/include/asm/debug-monitors.h | 9 +++++ arch/arm64/include/asm/ptrace.h | 2 - arch/arm64/kernel/debug-monitors.c | 66 ++++++++++++++++++++++++++++++++- arch/arm64/kernel/ptrace.c | 59 ----------------------------- arch/arm64/kernel/traps.c | 5 +-- 5 files changed, 75 insertions(+), 66 deletions(-) diff --git a/arch/arm64/include/asm/debug-monitors.h b/arch/arm64/include/asm/debug-monitors.h index 7eaa0b302493..ef8235c68c09 100644 --- a/arch/arm64/include/asm/debug-monitors.h +++ b/arch/arm64/include/asm/debug-monitors.h @@ -83,6 +83,15 @@ static inline int reinstall_suspended_bps(struct pt_regs *regs) } #endif +#ifdef CONFIG_COMPAT +int aarch32_break_handler(struct pt_regs *regs); +#else +static int aarch32_break_handler(struct pt_regs *regs) +{ + return -EFAULT; +} +#endif + #endif /* __ASSEMBLY */ #endif /* __KERNEL__ */ #endif /* __ASM_DEBUG_MONITORS_H */ diff --git a/arch/arm64/include/asm/ptrace.h b/arch/arm64/include/asm/ptrace.h index 41a71ee4c3df..0dacbbf9458b 100644 --- a/arch/arm64/include/asm/ptrace.h +++ b/arch/arm64/include/asm/ptrace.h @@ -171,7 +171,5 @@ extern unsigned long profile_pc(struct pt_regs *regs); #define profile_pc(regs) instruction_pointer(regs) #endif -extern int aarch32_break_trap(struct pt_regs *regs); - #endif /* __ASSEMBLY__ */ #endif diff --git a/arch/arm64/kernel/debug-monitors.c b/arch/arm64/kernel/debug-monitors.c index f4726dc054b3..08018e3df580 100644 --- a/arch/arm64/kernel/debug-monitors.c +++ b/arch/arm64/kernel/debug-monitors.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -226,13 +227,74 @@ static int single_step_handler(unsigned long addr, unsigned int esr, return 0; } -static int __init single_step_init(void) +static int brk_handler(unsigned long addr, unsigned int esr, + struct pt_regs *regs) +{ + siginfo_t info; + + if (!user_mode(regs)) + return -EFAULT; + + info = (siginfo_t) { + .si_signo = SIGTRAP, + .si_errno = 0, + .si_code = TRAP_BRKPT, + .si_addr = (void __user *)instruction_pointer(regs), + }; + + force_sig_info(SIGTRAP, &info, current); + return 0; +} + +int aarch32_break_handler(struct pt_regs *regs) +{ + siginfo_t info; + unsigned int instr; + bool bp = false; + void __user *pc = (void __user *)instruction_pointer(regs); + + if (!compat_user_mode(regs)) + return -EFAULT; + + if (compat_thumb_mode(regs)) { + /* get 16-bit Thumb instruction */ + get_user(instr, (u16 __user *)pc); + if (instr == AARCH32_BREAK_THUMB2_LO) { + /* get second half of 32-bit Thumb-2 instruction */ + get_user(instr, (u16 __user *)(pc + 2)); + bp = instr == AARCH32_BREAK_THUMB2_HI; + } else { + bp = instr == AARCH32_BREAK_THUMB; + } + } else { + /* 32-bit ARM instruction */ + get_user(instr, (u32 __user *)pc); + bp = (instr & ~0xf0000000) == AARCH32_BREAK_ARM; + } + + if (!bp) + return -EFAULT; + + info = (siginfo_t) { + .si_signo = SIGTRAP, + .si_errno = 0, + .si_code = TRAP_BRKPT, + .si_addr = pc, + }; + + force_sig_info(SIGTRAP, &info, current); + return 0; +} + +static int __init debug_traps_init(void) { hook_debug_fault_code(DBG_ESR_EVT_HWSS, single_step_handler, SIGTRAP, TRAP_HWBKPT, "single-step handler"); + hook_debug_fault_code(DBG_ESR_EVT_BRK, brk_handler, SIGTRAP, + TRAP_BRKPT, "ptrace BRK handler"); return 0; } -arch_initcall(single_step_init); +arch_initcall(debug_traps_init); /* Re-enable single step for syscall restarting. */ void user_rewind_single_step(struct task_struct *task) diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index 6e1e77f1831c..fecdbf7de82e 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -53,28 +53,6 @@ void ptrace_disable(struct task_struct *child) { } -/* - * Handle hitting a breakpoint. - */ -static int ptrace_break(struct pt_regs *regs) -{ - siginfo_t info = { - .si_signo = SIGTRAP, - .si_errno = 0, - .si_code = TRAP_BRKPT, - .si_addr = (void __user *)instruction_pointer(regs), - }; - - force_sig_info(SIGTRAP, &info, current); - return 0; -} - -static int arm64_break_trap(unsigned long addr, unsigned int esr, - struct pt_regs *regs) -{ - return ptrace_break(regs); -} - #ifdef CONFIG_HAVE_HW_BREAKPOINT /* * Handle hitting a HW-breakpoint. @@ -817,33 +795,6 @@ static const struct user_regset_view user_aarch32_view = { .regsets = aarch32_regsets, .n = ARRAY_SIZE(aarch32_regsets) }; -int aarch32_break_trap(struct pt_regs *regs) -{ - unsigned int instr; - bool bp = false; - void __user *pc = (void __user *)instruction_pointer(regs); - - if (compat_thumb_mode(regs)) { - /* get 16-bit Thumb instruction */ - get_user(instr, (u16 __user *)pc); - if (instr == AARCH32_BREAK_THUMB2_LO) { - /* get second half of 32-bit Thumb-2 instruction */ - get_user(instr, (u16 __user *)(pc + 2)); - bp = instr == AARCH32_BREAK_THUMB2_HI; - } else { - bp = instr == AARCH32_BREAK_THUMB; - } - } else { - /* 32-bit ARM instruction */ - get_user(instr, (u32 __user *)pc); - bp = (instr & ~0xf0000000) == AARCH32_BREAK_ARM; - } - - if (bp) - return ptrace_break(regs); - return 1; -} - static int compat_ptrace_read_user(struct task_struct *tsk, compat_ulong_t off, compat_ulong_t __user *ret) { @@ -1111,16 +1062,6 @@ long arch_ptrace(struct task_struct *child, long request, return ptrace_request(child, request, addr, data); } - -static int __init ptrace_break_init(void) -{ - hook_debug_fault_code(DBG_ESR_EVT_BRK, arm64_break_trap, SIGTRAP, - TRAP_BRKPT, "ptrace BRK handler"); - return 0; -} -core_initcall(ptrace_break_init); - - asmlinkage int syscall_trace(int dir, struct pt_regs *regs) { unsigned long saved_reg; diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c index f30852d28590..7ffadddb645d 100644 --- a/arch/arm64/kernel/traps.c +++ b/arch/arm64/kernel/traps.c @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -261,11 +262,9 @@ asmlinkage void __exception do_undefinstr(struct pt_regs *regs) siginfo_t info; void __user *pc = (void __user *)instruction_pointer(regs); -#ifdef CONFIG_COMPAT /* check for AArch32 breakpoint instructions */ - if (compat_user_mode(regs) && aarch32_break_trap(regs) == 0) + if (!aarch32_break_handler(regs)) return; -#endif if (show_unhandled_signals && unhandled_signal(current, SIGILL) && printk_ratelimit()) { -- cgit v1.2.3 From b8c3af766b7f9350c581bb44e6ffaaabc8c1c198 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 12 Jun 2013 11:29:47 +0100 Subject: drm/i915: WARN if the fence pin_count is invalid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stéphane Marchesin found a bug where the fences were not being restored, and in particular the fence pin_count was incorrect. Had we had a warning in place, this bug would have come to light much earlier. Better late than never? Signed-off-by: Chris Wilson Cc: Daniel Vetter Cc: Stéphane Marchesin Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index eaa04a6808cb..e5b8ae42859a 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1703,6 +1703,7 @@ i915_gem_object_unpin_fence(struct drm_i915_gem_object *obj) { if (obj->fence_reg != I915_FENCE_REG_NONE) { struct drm_i915_private *dev_priv = obj->base.dev->dev_private; + WARN_ON(dev_priv->fence_regs[obj->fence_reg].pin_count <= 0); dev_priv->fence_regs[obj->fence_reg].pin_count--; } } -- cgit v1.2.3 From 8796f9c5656dedd186eda88c07db6a58d1985938 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Fri, 7 Jun 2013 18:26:42 +0200 Subject: pwm: atmel-tcb: prepare clk before calling enable Replace clk_enable/disable with clk_prepare_enable/disable_unprepare to avoid common clk framework warnings. Signed-off-by: Boris BREZILLON Signed-off-by: Thierry Reding --- drivers/pwm/pwm-atmel-tcb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c index 0a7b6582edb1..09be626a61a0 100644 --- a/drivers/pwm/pwm-atmel-tcb.c +++ b/drivers/pwm/pwm-atmel-tcb.c @@ -76,7 +76,7 @@ static int atmel_tcb_pwm_request(struct pwm_chip *chip, if (!tcbpwm) return -ENOMEM; - ret = clk_enable(tc->clk[group]); + ret = clk_prepare_enable(tc->clk[group]); if (ret) { devm_kfree(chip->dev, tcbpwm); return ret; @@ -124,7 +124,7 @@ static void atmel_tcb_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) struct atmel_tcb_pwm_device *tcbpwm = pwm_get_chip_data(pwm); struct atmel_tc *tc = tcbpwmc->tc; - clk_disable(tc->clk[pwm->hwpwm / 2]); + clk_disable_unprepare(tc->clk[pwm->hwpwm / 2]); tcbpwmc->pwms[pwm->hwpwm] = NULL; devm_kfree(chip->dev, tcbpwm); } -- cgit v1.2.3 From 88b613e6234def882b0b601bf831bf89af2e27f0 Mon Sep 17 00:00:00 2001 From: Steffen Trumtrar Date: Thu, 30 May 2013 09:50:12 +0200 Subject: pwm: add pca9685 driver Add pwm driver for the NXP pca9685 16 channel pwm-led controller. The driver is really barebones at this stage. E.g. the OE' pin and therefore the corresponding registers are not supported. The driver was tested on a HW where this pin is tied to GND. Signed-off-by: Steffen Trumtrar [thierry.reding@gmail.com: style and whitespace cleanups] Signed-off-by: Thierry Reding --- .../devicetree/bindings/pwm/nxp,pca9685-pwm.txt | 27 ++ drivers/pwm/Kconfig | 9 + drivers/pwm/Makefile | 1 + drivers/pwm/pwm-pca9685.c | 298 +++++++++++++++++++++ 4 files changed, 335 insertions(+) create mode 100644 Documentation/devicetree/bindings/pwm/nxp,pca9685-pwm.txt create mode 100644 drivers/pwm/pwm-pca9685.c diff --git a/Documentation/devicetree/bindings/pwm/nxp,pca9685-pwm.txt b/Documentation/devicetree/bindings/pwm/nxp,pca9685-pwm.txt new file mode 100644 index 000000000000..1e3dfe7a4894 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/nxp,pca9685-pwm.txt @@ -0,0 +1,27 @@ +NXP PCA9685 16-channel 12-bit PWM LED controller +================================================ + +Required properties: + - compatible: "nxp,pca9685-pwm" + - #pwm-cells: should be 2. The first cell specifies the per-chip index + of the PWM to use and the second cell is the period in nanoseconds. + The index 16 is the ALLCALL channel, that sets all PWM channels at the same + time. + +Optional properties: + - invert (bool): boolean to enable inverted logic + - open-drain (bool): boolean to configure outputs with open-drain structure; + if omitted use totem-pole structure + +Example: + +For LEDs that are directly connected to the PCA, the following setting is +applicable: + +pca: pca@41 { + compatible = "nxp,pca9685-pwm"; + #pwm-cells = <2>; + reg = <0x41>; + invert; + open-drain; +}; diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 115b64453493..d3fe3205d296 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -97,6 +97,15 @@ config PWM_MXS To compile this driver as a module, choose M here: the module will be called pwm-mxs. +config PWM_PCA9685 + tristate "NXP PCA9685 PWM driver" + depends on OF && REGMAP_I2C + help + Generic PWM framework driver for NXP PCA9685 LED controller. + + To compile this driver as a module, choose M here: the module + will be called pwm-pca9685. + config PWM_PUV3 tristate "PKUnity NetBook-0916 PWM support" depends on ARCH_PUV3 diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 94ba21e24bd6..b3afc0a1800b 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_PWM_IMX) += pwm-imx.o obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o obj-$(CONFIG_PWM_MXS) += pwm-mxs.o +obj-$(CONFIG_PWM_PCA9685) += pwm-pca9685.o obj-$(CONFIG_PWM_PUV3) += pwm-puv3.o obj-$(CONFIG_PWM_PXA) += pwm-pxa.o obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c new file mode 100644 index 000000000000..2afc9043550a --- /dev/null +++ b/drivers/pwm/pwm-pca9685.c @@ -0,0 +1,298 @@ +/* + * Driver for PCA9685 16-channel 12-bit PWM LED controller + * + * Copyright (C) 2013 Steffen Trumtrar + * + * based on the pwm-twl-led.c driver + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include + +#define PCA9685_MODE1 0x00 +#define PCA9685_MODE2 0x01 +#define PCA9685_SUBADDR1 0x02 +#define PCA9685_SUBADDR2 0x03 +#define PCA9685_SUBADDR3 0x04 +#define PCA9685_ALLCALLADDR 0x05 +#define PCA9685_LEDX_ON_L 0x06 +#define PCA9685_LEDX_ON_H 0x07 +#define PCA9685_LEDX_OFF_L 0x08 +#define PCA9685_LEDX_OFF_H 0x09 + +#define PCA9685_ALL_LED_ON_L 0xFA +#define PCA9685_ALL_LED_ON_H 0xFB +#define PCA9685_ALL_LED_OFF_L 0xFC +#define PCA9685_ALL_LED_OFF_H 0xFD +#define PCA9685_PRESCALE 0xFE + +#define PCA9685_NUMREGS 0xFF +#define PCA9685_MAXCHAN 0x10 + +#define LED_FULL (1 << 4) +#define MODE1_SLEEP (1 << 4) +#define MODE2_INVRT (1 << 4) +#define MODE2_OUTDRV (1 << 2) + +#define LED_N_ON_H(N) (PCA9685_LEDX_ON_H + (4 * (N))) +#define LED_N_ON_L(N) (PCA9685_LEDX_ON_L + (4 * (N))) +#define LED_N_OFF_H(N) (PCA9685_LEDX_OFF_H + (4 * (N))) +#define LED_N_OFF_L(N) (PCA9685_LEDX_OFF_L + (4 * (N))) + +struct pca9685 { + struct pwm_chip chip; + struct regmap *regmap; + int active_cnt; +}; + +static inline struct pca9685 *to_pca(struct pwm_chip *chip) +{ + return container_of(chip, struct pca9685, chip); +} + +static int pca9685_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + struct pca9685 *pca = to_pca(chip); + unsigned long long duty; + unsigned int reg; + + if (duty_ns < 1) { + if (pwm->hwpwm >= PCA9685_MAXCHAN) + reg = PCA9685_ALL_LED_OFF_H; + else + reg = LED_N_OFF_H(pwm->hwpwm); + + regmap_write(pca->regmap, reg, LED_FULL); + + return 0; + } + + if (duty_ns == period_ns) { + if (pwm->hwpwm >= PCA9685_MAXCHAN) + reg = PCA9685_ALL_LED_ON_H; + else + reg = LED_N_ON_H(pwm->hwpwm); + + regmap_write(pca->regmap, reg, LED_FULL); + + return 0; + } + + duty = 4096 * (unsigned long long)duty_ns; + duty = DIV_ROUND_UP_ULL(duty, period_ns); + + if (pwm->hwpwm >= PCA9685_MAXCHAN) + reg = PCA9685_ALL_LED_OFF_L; + else + reg = LED_N_OFF_L(pwm->hwpwm); + + regmap_write(pca->regmap, reg, (int)duty & 0xff); + + if (pwm->hwpwm >= PCA9685_MAXCHAN) + reg = PCA9685_ALL_LED_OFF_H; + else + reg = LED_N_OFF_H(pwm->hwpwm); + + regmap_write(pca->regmap, reg, ((int)duty >> 8) & 0xf); + + return 0; +} + +static int pca9685_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct pca9685 *pca = to_pca(chip); + unsigned int reg; + + /* + * The PWM subsystem does not support a pre-delay. + * So, set the ON-timeout to 0 + */ + if (pwm->hwpwm >= PCA9685_MAXCHAN) + reg = PCA9685_ALL_LED_ON_L; + else + reg = LED_N_ON_L(pwm->hwpwm); + + regmap_write(pca->regmap, reg, 0); + + if (pwm->hwpwm >= PCA9685_MAXCHAN) + reg = PCA9685_ALL_LED_ON_H; + else + reg = LED_N_ON_H(pwm->hwpwm); + + regmap_write(pca->regmap, reg, 0); + + /* + * Clear the full-off bit. + * It has precedence over the others and must be off. + */ + if (pwm->hwpwm >= PCA9685_MAXCHAN) + reg = PCA9685_ALL_LED_OFF_H; + else + reg = LED_N_OFF_H(pwm->hwpwm); + + regmap_update_bits(pca->regmap, reg, LED_FULL, 0x0); + + return 0; +} + +static void pca9685_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct pca9685 *pca = to_pca(chip); + unsigned int reg; + + if (pwm->hwpwm >= PCA9685_MAXCHAN) + reg = PCA9685_ALL_LED_OFF_H; + else + reg = LED_N_OFF_H(pwm->hwpwm); + + regmap_write(pca->regmap, reg, LED_FULL); + + /* Clear the LED_OFF counter. */ + if (pwm->hwpwm >= PCA9685_MAXCHAN) + reg = PCA9685_ALL_LED_OFF_L; + else + reg = LED_N_OFF_L(pwm->hwpwm); + + regmap_write(pca->regmap, reg, 0x0); +} + +static int pca9685_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct pca9685 *pca = to_pca(chip); + + if (pca->active_cnt++ == 0) + return regmap_update_bits(pca->regmap, PCA9685_MODE1, + MODE1_SLEEP, 0x0); + + return 0; +} + +static void pca9685_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct pca9685 *pca = to_pca(chip); + + if (--pca->active_cnt == 0) + regmap_update_bits(pca->regmap, PCA9685_MODE1, MODE1_SLEEP, + 0x1); +} + +static const struct pwm_ops pca9685_pwm_ops = { + .enable = pca9685_pwm_enable, + .disable = pca9685_pwm_disable, + .config = pca9685_pwm_config, + .request = pca9685_pwm_request, + .free = pca9685_pwm_free, +}; + +static struct regmap_config pca9685_regmap_i2c_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = PCA9685_NUMREGS, + .cache_type = REGCACHE_NONE, +}; + +static int pca9685_pwm_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device_node *np = client->dev.of_node; + struct pca9685 *pca; + int ret; + int mode2; + + pca = devm_kzalloc(&client->dev, sizeof(*pca), GFP_KERNEL); + if (!pca) + return -ENOMEM; + + pca->regmap = devm_regmap_init_i2c(client, &pca9685_regmap_i2c_config); + if (IS_ERR(pca->regmap)) { + ret = PTR_ERR(pca->regmap); + dev_err(&client->dev, "Failed to initialize register map: %d\n", + ret); + return ret; + } + + i2c_set_clientdata(client, pca); + + regmap_read(pca->regmap, PCA9685_MODE2, &mode2); + + if (of_property_read_bool(np, "invert")) + mode2 |= MODE2_INVRT; + else + mode2 &= ~MODE2_INVRT; + + if (of_property_read_bool(np, "open-drain")) + mode2 &= ~MODE2_OUTDRV; + else + mode2 |= MODE2_OUTDRV; + + regmap_write(pca->regmap, PCA9685_MODE2, mode2); + + /* clear all "full off" bits */ + regmap_write(pca->regmap, PCA9685_ALL_LED_OFF_L, 0); + regmap_write(pca->regmap, PCA9685_ALL_LED_OFF_H, 0); + + pca->chip.ops = &pca9685_pwm_ops; + /* add an extra channel for ALL_LED */ + pca->chip.npwm = PCA9685_MAXCHAN + 1; + + pca->chip.dev = &client->dev; + pca->chip.base = -1; + pca->chip.can_sleep = true; + + return pwmchip_add(&pca->chip); +} + +static int pca9685_pwm_remove(struct i2c_client *client) +{ + struct pca9685 *pca = i2c_get_clientdata(client); + + regmap_update_bits(pca->regmap, PCA9685_MODE1, MODE1_SLEEP, 0x1); + + return pwmchip_remove(&pca->chip); +} + +static const struct i2c_device_id pca9685_id[] = { + { "pca9685", 0 }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(i2c, pca9685_id); + +static const struct of_device_id pca9685_dt_ids[] = { + { .compatible = "nxp,pca9685-pwm", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, pca9685_dt_ids); + +static struct i2c_driver pca9685_i2c_driver = { + .driver = { + .name = "pca9685-pwm", + .owner = THIS_MODULE, + .of_match_table = pca9685_dt_ids, + }, + .probe = pca9685_pwm_probe, + .remove = pca9685_pwm_remove, + .id_table = pca9685_id, +}; + +module_i2c_driver(pca9685_i2c_driver); + +MODULE_AUTHOR("Steffen Trumtrar "); +MODULE_DESCRIPTION("PWM driver for PCA9685"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 3dd0a909479c1d372341d749b4ff94cd638b57da Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 12 Jun 2013 13:18:29 +0200 Subject: pwm: Fill in missing .owner fields Some drivers don't set the .owner fields of the struct device_driver or struct pwm_ops, which causes the module usage count to become wrong. Signed-off-by: Thierry Reding --- drivers/pwm/pwm-atmel-tcb.c | 1 + drivers/pwm/pwm-bfin.c | 1 + drivers/pwm/pwm-imx.c | 1 + drivers/pwm/pwm-lpc32xx.c | 1 + drivers/pwm/pwm-mxs.c | 1 + drivers/pwm/pwm-pca9685.c | 1 + drivers/pwm/pwm-puv3.c | 1 + drivers/pwm/pwm-spear.c | 1 + drivers/pwm/pwm-tegra.c | 1 + 9 files changed, 9 insertions(+) diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c index 09be626a61a0..ba6ce01035e4 100644 --- a/drivers/pwm/pwm-atmel-tcb.c +++ b/drivers/pwm/pwm-atmel-tcb.c @@ -434,6 +434,7 @@ MODULE_DEVICE_TABLE(of, atmel_tcb_pwm_dt_ids); static struct platform_driver atmel_tcb_pwm_driver = { .driver = { .name = "atmel-tcb-pwm", + .owner = THIS_MODULE, .of_match_table = atmel_tcb_pwm_dt_ids, }, .probe = atmel_tcb_pwm_probe, diff --git a/drivers/pwm/pwm-bfin.c b/drivers/pwm/pwm-bfin.c index 7631ef194de7..9985d830e554 100644 --- a/drivers/pwm/pwm-bfin.c +++ b/drivers/pwm/pwm-bfin.c @@ -149,6 +149,7 @@ static int bfin_pwm_remove(struct platform_device *pdev) static struct platform_driver bfin_pwm_driver = { .driver = { .name = "bfin-pwm", + .owner = THIS_MODULE, }, .probe = bfin_pwm_probe, .remove = bfin_pwm_remove, diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c index c938bae18812..2b7c4f88b461 100644 --- a/drivers/pwm/pwm-imx.c +++ b/drivers/pwm/pwm-imx.c @@ -295,6 +295,7 @@ static int imx_pwm_remove(struct platform_device *pdev) static struct platform_driver imx_pwm_driver = { .driver = { .name = "imx-pwm", + .owner = THIS_MODULE, .of_match_table = of_match_ptr(imx_pwm_dt_ids), }, .probe = imx_pwm_probe, diff --git a/drivers/pwm/pwm-lpc32xx.c b/drivers/pwm/pwm-lpc32xx.c index 8272883c0d05..efb6c7bf8750 100644 --- a/drivers/pwm/pwm-lpc32xx.c +++ b/drivers/pwm/pwm-lpc32xx.c @@ -171,6 +171,7 @@ MODULE_DEVICE_TABLE(of, lpc32xx_pwm_dt_ids); static struct platform_driver lpc32xx_pwm_driver = { .driver = { .name = "lpc32xx-pwm", + .owner = THIS_MODULE, .of_match_table = of_match_ptr(lpc32xx_pwm_dt_ids), }, .probe = lpc32xx_pwm_probe, diff --git a/drivers/pwm/pwm-mxs.c b/drivers/pwm/pwm-mxs.c index 6489a4b75943..2c77b81da7c4 100644 --- a/drivers/pwm/pwm-mxs.c +++ b/drivers/pwm/pwm-mxs.c @@ -182,6 +182,7 @@ MODULE_DEVICE_TABLE(of, mxs_pwm_dt_ids); static struct platform_driver mxs_pwm_driver = { .driver = { .name = "mxs-pwm", + .owner = THIS_MODULE, .of_match_table = of_match_ptr(mxs_pwm_dt_ids), }, .probe = mxs_pwm_probe, diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c index 2afc9043550a..c9f9e65415f3 100644 --- a/drivers/pwm/pwm-pca9685.c +++ b/drivers/pwm/pwm-pca9685.c @@ -199,6 +199,7 @@ static const struct pwm_ops pca9685_pwm_ops = { .config = pca9685_pwm_config, .request = pca9685_pwm_request, .free = pca9685_pwm_free, + .owner = THIS_MODULE, }; static struct regmap_config pca9685_regmap_i2c_config = { diff --git a/drivers/pwm/pwm-puv3.c b/drivers/pwm/pwm-puv3.c index ed6007b27585..a9a28083f245 100644 --- a/drivers/pwm/pwm-puv3.c +++ b/drivers/pwm/pwm-puv3.c @@ -146,6 +146,7 @@ static int pwm_remove(struct platform_device *pdev) static struct platform_driver puv3_pwm_driver = { .driver = { .name = "PKUnity-v3-PWM", + .owner = THIS_MODULE, }, .probe = pwm_probe, .remove = pwm_remove, diff --git a/drivers/pwm/pwm-spear.c b/drivers/pwm/pwm-spear.c index 6d99e2cbdc73..a54d21401431 100644 --- a/drivers/pwm/pwm-spear.c +++ b/drivers/pwm/pwm-spear.c @@ -259,6 +259,7 @@ MODULE_DEVICE_TABLE(of, spear_pwm_of_match); static struct platform_driver spear_pwm_driver = { .driver = { .name = "spear-pwm", + .owner = THIS_MODULE, .of_match_table = spear_pwm_of_match, }, .probe = spear_pwm_probe, diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c index a5402933001f..74298c561c4e 100644 --- a/drivers/pwm/pwm-tegra.c +++ b/drivers/pwm/pwm-tegra.c @@ -239,6 +239,7 @@ MODULE_DEVICE_TABLE(of, tegra_pwm_of_match); static struct platform_driver tegra_pwm_driver = { .driver = { .name = "tegra-pwm", + .owner = THIS_MODULE, .of_match_table = tegra_pwm_of_match, }, .probe = tegra_pwm_probe, -- cgit v1.2.3 From 8915aa27d5efbb9185357175b0acf884325565f9 Mon Sep 17 00:00:00 2001 From: Marcelo Tosatti Date: Tue, 11 Jun 2013 23:31:12 -0300 Subject: KVM: x86: handle idiv overflow at kvm_write_tsc Its possible that idivl overflows (due to large delta stored in usdiff, valid scenario). Create an exception handler to catch the overflow exception (division by zero is protected by vcpu->arch.virtual_tsc_khz check), and interpret it accordingly (delta is larger than USEC_PER_SEC). Fixes https://bugzilla.redhat.com/show_bug.cgi?id=969644 Signed-off-by: Marcelo Tosatti Signed-off-by: Gleb Natapov --- arch/x86/kvm/x86.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 6402951d5f3b..737c804b310c 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -1194,20 +1194,37 @@ void kvm_write_tsc(struct kvm_vcpu *vcpu, struct msr_data *msr) elapsed = ns - kvm->arch.last_tsc_nsec; if (vcpu->arch.virtual_tsc_khz) { + int faulted = 0; + /* n.b - signed multiplication and division required */ usdiff = data - kvm->arch.last_tsc_write; #ifdef CONFIG_X86_64 usdiff = (usdiff * 1000) / vcpu->arch.virtual_tsc_khz; #else /* do_div() only does unsigned */ - asm("idivl %2; xor %%edx, %%edx" - : "=A"(usdiff) - : "A"(usdiff * 1000), "rm"(vcpu->arch.virtual_tsc_khz)); + asm("1: idivl %[divisor]\n" + "2: xor %%edx, %%edx\n" + " movl $0, %[faulted]\n" + "3:\n" + ".section .fixup,\"ax\"\n" + "4: movl $1, %[faulted]\n" + " jmp 3b\n" + ".previous\n" + + _ASM_EXTABLE(1b, 4b) + + : "=A"(usdiff), [faulted] "=r" (faulted) + : "A"(usdiff * 1000), [divisor] "rm"(vcpu->arch.virtual_tsc_khz)); + #endif do_div(elapsed, 1000); usdiff -= elapsed; if (usdiff < 0) usdiff = -usdiff; + + /* idivl overflow => difference is larger than USEC_PER_SEC */ + if (faulted) + usdiff = USEC_PER_SEC; } else usdiff = USEC_PER_SEC; /* disable TSC match window below */ -- cgit v1.2.3 From 10dbc5e39a60944536f3ca59bc9a8a8896355714 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Sun, 21 Apr 2013 16:38:31 -0500 Subject: driver core: move to_platform_driver to platform_device.h In converting the last remaining of_platform_driver (ibmebus) to a regular platform driver, to_platform_driver is needed to replace to_of_platform_driver. Signed-off-by: Rob Herring Acked-by: Arnd Bergmann Tested-by: Benjamin Herrenschmidt Signed-off-by: Grant Likely --- drivers/base/platform.c | 3 --- include/linux/platform_device.h | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 9eda84246ffd..1bcb2a792e95 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -29,9 +29,6 @@ /* For automatically allocated device IDs */ static DEFINE_IDA(platform_devid_ida); -#define to_platform_driver(drv) (container_of((drv), struct platform_driver, \ - driver)) - struct device platform_bus = { .init_name = "platform", }; diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h index 9abf1db6aea6..3413897474e1 100644 --- a/include/linux/platform_device.h +++ b/include/linux/platform_device.h @@ -180,6 +180,9 @@ struct platform_driver { const struct platform_device_id *id_table; }; +#define to_platform_driver(drv) (container_of((drv), struct platform_driver, \ + driver)) + extern int platform_driver_register(struct platform_driver *); extern void platform_driver_unregister(struct platform_driver *); -- cgit v1.2.3 From c45640e4a9f54f2f6f4bc51c2ba9644ffb3babde Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Sat, 20 Apr 2013 21:51:08 -0500 Subject: ibmebus: convert of_platform_driver to platform_driver ibmebus is the last remaining user of of_platform_driver and the conversion to a regular platform driver is trivial. Signed-off-by: Rob Herring Acked-by: Arnd Bergmann Tested-by: Benjamin Herrenschmidt Signed-off-by: Grant Likely --- arch/powerpc/include/asm/ibmebus.h | 4 ++-- arch/powerpc/kernel/ibmebus.c | 22 ++++++++++------------ drivers/infiniband/hw/ehca/ehca_main.c | 5 ++--- drivers/net/ethernet/ibm/ehea/ehea_main.c | 8 +++----- 4 files changed, 17 insertions(+), 22 deletions(-) diff --git a/arch/powerpc/include/asm/ibmebus.h b/arch/powerpc/include/asm/ibmebus.h index 1a9d9aea21fa..088f95b2e14f 100644 --- a/arch/powerpc/include/asm/ibmebus.h +++ b/arch/powerpc/include/asm/ibmebus.h @@ -48,8 +48,8 @@ extern struct bus_type ibmebus_bus_type; -int ibmebus_register_driver(struct of_platform_driver *drv); -void ibmebus_unregister_driver(struct of_platform_driver *drv); +int ibmebus_register_driver(struct platform_driver *drv); +void ibmebus_unregister_driver(struct platform_driver *drv); int ibmebus_request_irq(u32 ist, irq_handler_t handler, unsigned long irq_flags, const char *devname, diff --git a/arch/powerpc/kernel/ibmebus.c b/arch/powerpc/kernel/ibmebus.c index 8220baa46faf..16a7c2326d48 100644 --- a/arch/powerpc/kernel/ibmebus.c +++ b/arch/powerpc/kernel/ibmebus.c @@ -205,7 +205,7 @@ static int ibmebus_create_devices(const struct of_device_id *matches) return ret; } -int ibmebus_register_driver(struct of_platform_driver *drv) +int ibmebus_register_driver(struct platform_driver *drv) { /* If the driver uses devices that ibmebus doesn't know, add them */ ibmebus_create_devices(drv->driver.of_match_table); @@ -215,7 +215,7 @@ int ibmebus_register_driver(struct of_platform_driver *drv) } EXPORT_SYMBOL(ibmebus_register_driver); -void ibmebus_unregister_driver(struct of_platform_driver *drv) +void ibmebus_unregister_driver(struct platform_driver *drv) { driver_unregister(&drv->driver); } @@ -338,11 +338,10 @@ static int ibmebus_bus_bus_match(struct device *dev, struct device_driver *drv) static int ibmebus_bus_device_probe(struct device *dev) { int error = -ENODEV; - struct of_platform_driver *drv; + struct platform_driver *drv; struct platform_device *of_dev; - const struct of_device_id *match; - drv = to_of_platform_driver(dev->driver); + drv = to_platform_driver(dev->driver); of_dev = to_platform_device(dev); if (!drv->probe) @@ -350,9 +349,8 @@ static int ibmebus_bus_device_probe(struct device *dev) of_dev_get(of_dev); - match = of_match_device(drv->driver.of_match_table, dev); - if (match) - error = drv->probe(of_dev, match); + if (of_driver_match_device(dev, dev->driver)) + error = drv->probe(of_dev); if (error) of_dev_put(of_dev); @@ -362,7 +360,7 @@ static int ibmebus_bus_device_probe(struct device *dev) static int ibmebus_bus_device_remove(struct device *dev) { struct platform_device *of_dev = to_platform_device(dev); - struct of_platform_driver *drv = to_of_platform_driver(dev->driver); + struct platform_driver *drv = to_platform_driver(dev->driver); if (dev->driver && drv->remove) drv->remove(of_dev); @@ -372,7 +370,7 @@ static int ibmebus_bus_device_remove(struct device *dev) static void ibmebus_bus_device_shutdown(struct device *dev) { struct platform_device *of_dev = to_platform_device(dev); - struct of_platform_driver *drv = to_of_platform_driver(dev->driver); + struct platform_driver *drv = to_platform_driver(dev->driver); if (dev->driver && drv->shutdown) drv->shutdown(of_dev); @@ -419,7 +417,7 @@ struct device_attribute ibmebus_bus_device_attrs[] = { static int ibmebus_bus_legacy_suspend(struct device *dev, pm_message_t mesg) { struct platform_device *of_dev = to_platform_device(dev); - struct of_platform_driver *drv = to_of_platform_driver(dev->driver); + struct platform_driver *drv = to_platform_driver(dev->driver); int ret = 0; if (dev->driver && drv->suspend) @@ -430,7 +428,7 @@ static int ibmebus_bus_legacy_suspend(struct device *dev, pm_message_t mesg) static int ibmebus_bus_legacy_resume(struct device *dev) { struct platform_device *of_dev = to_platform_device(dev); - struct of_platform_driver *drv = to_of_platform_driver(dev->driver); + struct platform_driver *drv = to_platform_driver(dev->driver); int ret = 0; if (dev->driver && drv->resume) diff --git a/drivers/infiniband/hw/ehca/ehca_main.c b/drivers/infiniband/hw/ehca/ehca_main.c index f8a62918a88d..982e3efd98d3 100644 --- a/drivers/infiniband/hw/ehca/ehca_main.c +++ b/drivers/infiniband/hw/ehca/ehca_main.c @@ -713,8 +713,7 @@ static struct attribute_group ehca_dev_attr_grp = { .attrs = ehca_dev_attrs }; -static int ehca_probe(struct platform_device *dev, - const struct of_device_id *id) +static int ehca_probe(struct platform_device *dev) { struct ehca_shca *shca; const u64 *handle; @@ -937,7 +936,7 @@ static struct of_device_id ehca_device_table[] = }; MODULE_DEVICE_TABLE(of, ehca_device_table); -static struct of_platform_driver ehca_driver = { +static struct platform_driver ehca_driver = { .probe = ehca_probe, .remove = ehca_remove, .driver = { diff --git a/drivers/net/ethernet/ibm/ehea/ehea_main.c b/drivers/net/ethernet/ibm/ehea/ehea_main.c index 90ea0b1673ca..de2969cae262 100644 --- a/drivers/net/ethernet/ibm/ehea/ehea_main.c +++ b/drivers/net/ethernet/ibm/ehea/ehea_main.c @@ -98,8 +98,7 @@ static struct ehea_fw_handle_array ehea_fw_handles; static struct ehea_bcmc_reg_array ehea_bcmc_regs; -static int ehea_probe_adapter(struct platform_device *dev, - const struct of_device_id *id); +static int ehea_probe_adapter(struct platform_device *dev); static int ehea_remove(struct platform_device *dev); @@ -112,7 +111,7 @@ static struct of_device_id ehea_device_table[] = { }; MODULE_DEVICE_TABLE(of, ehea_device_table); -static struct of_platform_driver ehea_driver = { +static struct platform_driver ehea_driver = { .driver = { .name = "ehea", .owner = THIS_MODULE, @@ -3251,8 +3250,7 @@ static void ehea_remove_device_sysfs(struct platform_device *dev) device_remove_file(&dev->dev, &dev_attr_remove_port); } -static int ehea_probe_adapter(struct platform_device *dev, - const struct of_device_id *id) +static int ehea_probe_adapter(struct platform_device *dev) { struct ehea_adapter *adapter; const u64 *adapter_handle; -- cgit v1.2.3 From f70c64f8b095c0c386840f2036a79523bfd66725 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Sun, 21 Apr 2013 16:40:13 -0500 Subject: of: remove of_platform_driver The last user of of_platform_driver is converted to a regular platform_driver, so of_platform_driver can be removed now. Signed-off-by: Rob Herring Acked-by: Arnd Bergmann Tested-by: Benjamin Herrenschmidt Signed-off-by: Grant Likely --- include/linux/of_platform.h | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/include/linux/of_platform.h b/include/linux/of_platform.h index 7747ad0027ae..05cb4a928252 100644 --- a/include/linux/of_platform.h +++ b/include/linux/of_platform.h @@ -51,27 +51,6 @@ struct of_dev_auxdata { { .compatible = _compat, .phys_addr = _phys, .name = _name, \ .platform_data = _pdata } -/** - * of_platform_driver - Legacy of-aware driver for platform devices. - * - * An of_platform_driver driver is attached to a basic platform_device on - * the ibm ebus (ibmebus_bus_type). - */ -struct of_platform_driver -{ - int (*probe)(struct platform_device* dev, - const struct of_device_id *match); - int (*remove)(struct platform_device* dev); - - int (*suspend)(struct platform_device* dev, pm_message_t state); - int (*resume)(struct platform_device* dev); - int (*shutdown)(struct platform_device* dev); - - struct device_driver driver; -}; -#define to_of_platform_driver(drv) \ - container_of(drv,struct of_platform_driver, driver) - extern const struct of_device_id of_default_bus_match_table[]; /* Platform drivers register/unregister */ -- cgit v1.2.3 From 8973ba8b325f8c084815a3021c427c6fa3017a5d Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 19 Apr 2013 17:32:15 -0500 Subject: usb: chipidea: depend on CONFIG_OF instead of CONFIG_OF_DEVICE CONFIG_OF_DEVICE is going away, so use CONFIG_OF instead. It does not appear that CONFIG_OF_DEVICE was the correct dependency either. Signed-off-by: Rob Herring Acked-by: Arnd Bergmann Tested-by: Benjamin Herrenschmidt Signed-off-by: Grant Likely --- drivers/usb/chipidea/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/chipidea/Makefile b/drivers/usb/chipidea/Makefile index 4ab83e98219b..4113febdb432 100644 --- a/drivers/usb/chipidea/Makefile +++ b/drivers/usb/chipidea/Makefile @@ -16,6 +16,6 @@ ifneq ($(CONFIG_PCI),) obj-$(CONFIG_USB_CHIPIDEA) += ci13xxx_pci.o endif -ifneq ($(CONFIG_OF_DEVICE),) +ifneq ($(CONFIG_OF),) obj-$(CONFIG_USB_CHIPIDEA) += ci13xxx_imx.o usbmisc_imx.o endif -- cgit v1.2.3 From ba166e900b502b74b9425881caa94f94891b0a1f Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 19 Apr 2013 17:32:52 -0500 Subject: of: remove CONFIG_OF_DEVICE CONFIG_OF_DEVICE is always selected when CONFIG_OF is enabled, so remove it and simplify of_platform.h and of_device.h headers. This also fixes !OF compiles using of_platform_populate. Signed-off-by: Rob Herring Acked-by: Arnd Bergmann Tested-by: Benjamin Herrenschmidt Signed-off-by: Grant Likely --- drivers/of/Kconfig | 3 --- drivers/of/Makefile | 3 +-- include/linux/of_device.h | 6 +++--- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index d37bfcf5a3a2..80e5c13b930d 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -48,9 +48,6 @@ config OF_IRQ def_bool y depends on !SPARC -config OF_DEVICE - def_bool y - config OF_I2C def_tristate I2C depends on I2C diff --git a/drivers/of/Makefile b/drivers/of/Makefile index e027f444d10c..1f9c0c492ef9 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -1,9 +1,8 @@ -obj-y = base.o +obj-y = base.o device.o platform.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 obj-$(CONFIG_OF_I2C) += of_i2c.o obj-$(CONFIG_OF_NET) += of_net.o obj-$(CONFIG_OF_SELFTEST) += selftest.o diff --git a/include/linux/of_device.h b/include/linux/of_device.h index 901b7435e890..9d27475feec1 100644 --- a/include/linux/of_device.h +++ b/include/linux/of_device.h @@ -4,12 +4,12 @@ #include #include /* temporary until merge */ -#ifdef CONFIG_OF_DEVICE #include #include struct device; +#ifdef CONFIG_OF extern const struct of_device_id *of_match_device( const struct of_device_id *matches, const struct device *dev); extern void of_device_make_bus_id(struct device *dev); @@ -43,7 +43,7 @@ static inline void of_device_node_put(struct device *dev) of_node_put(dev->of_node); } -#else /* CONFIG_OF_DEVICE */ +#else /* CONFIG_OF */ static inline int of_driver_match_device(struct device *dev, struct device_driver *drv) @@ -67,6 +67,6 @@ static inline const struct of_device_id *of_match_device( { return NULL; } -#endif /* CONFIG_OF_DEVICE */ +#endif /* CONFIG_OF */ #endif /* _LINUX_OF_DEVICE_H */ -- cgit v1.2.3 From 9b1be4dc02bb6b9761fbd8927c1750d75ddd2a8c Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 7 Jun 2013 10:04:54 -0400 Subject: drm/radeon: fix UVD on big endian This fixes the kernel side so that the ring should come up and ring and IB tests should work. The userspace UVD drivers will also need big endian fixes. Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/r600.c | 13 ++++++++++--- drivers/gpu/drm/radeon/radeon_uvd.c | 34 +++++++++++++++++----------------- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index 0e5341695922..6948eb88c2b7 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -2687,6 +2687,9 @@ void r600_uvd_rbc_stop(struct radeon_device *rdev) int r600_uvd_init(struct radeon_device *rdev) { int i, j, r; + /* disable byte swapping */ + u32 lmi_swap_cntl = 0; + u32 mp_swap_cntl = 0; /* raise clocks while booting up the VCPU */ radeon_set_uvd_clocks(rdev, 53300, 40000); @@ -2711,9 +2714,13 @@ int r600_uvd_init(struct radeon_device *rdev) WREG32(UVD_LMI_CTRL, 0x40 | (1 << 8) | (1 << 13) | (1 << 21) | (1 << 9) | (1 << 20)); - /* disable byte swapping */ - WREG32(UVD_LMI_SWAP_CNTL, 0); - WREG32(UVD_MP_SWAP_CNTL, 0); +#ifdef __BIG_ENDIAN + /* swap (8 in 32) RB and IB */ + lmi_swap_cntl = 0xa; + mp_swap_cntl = 0; +#endif + WREG32(UVD_LMI_SWAP_CNTL, lmi_swap_cntl); + WREG32(UVD_MP_SWAP_CNTL, mp_swap_cntl); WREG32(UVD_MPC_SET_MUXA0, 0x40c2040); WREG32(UVD_MPC_SET_MUXA1, 0x0); diff --git a/drivers/gpu/drm/radeon/radeon_uvd.c b/drivers/gpu/drm/radeon/radeon_uvd.c index 9f55adefa8e0..cad735dd02c6 100644 --- a/drivers/gpu/drm/radeon/radeon_uvd.c +++ b/drivers/gpu/drm/radeon/radeon_uvd.c @@ -627,19 +627,19 @@ int radeon_uvd_get_create_msg(struct radeon_device *rdev, int ring, } /* stitch together an UVD create msg */ - msg[0] = 0x00000de4; - msg[1] = 0x00000000; - msg[2] = handle; - msg[3] = 0x00000000; - msg[4] = 0x00000000; - msg[5] = 0x00000000; - msg[6] = 0x00000000; - msg[7] = 0x00000780; - msg[8] = 0x00000440; - msg[9] = 0x00000000; - msg[10] = 0x01b37000; + msg[0] = cpu_to_le32(0x00000de4); + msg[1] = cpu_to_le32(0x00000000); + msg[2] = cpu_to_le32(handle); + msg[3] = cpu_to_le32(0x00000000); + msg[4] = cpu_to_le32(0x00000000); + msg[5] = cpu_to_le32(0x00000000); + msg[6] = cpu_to_le32(0x00000000); + msg[7] = cpu_to_le32(0x00000780); + msg[8] = cpu_to_le32(0x00000440); + msg[9] = cpu_to_le32(0x00000000); + msg[10] = cpu_to_le32(0x01b37000); for (i = 11; i < 1024; ++i) - msg[i] = 0x0; + msg[i] = cpu_to_le32(0x0); radeon_bo_kunmap(bo); radeon_bo_unreserve(bo); @@ -673,12 +673,12 @@ int radeon_uvd_get_destroy_msg(struct radeon_device *rdev, int ring, } /* stitch together an UVD destroy msg */ - msg[0] = 0x00000de4; - msg[1] = 0x00000002; - msg[2] = handle; - msg[3] = 0x00000000; + msg[0] = cpu_to_le32(0x00000de4); + msg[1] = cpu_to_le32(0x00000002); + msg[2] = cpu_to_le32(handle); + msg[3] = cpu_to_le32(0x00000000); for (i = 4; i < 1024; ++i) - msg[i] = 0x0; + msg[i] = cpu_to_le32(0x0); radeon_bo_kunmap(bo); radeon_bo_unreserve(bo); -- cgit v1.2.3 From f100380ecd8287b0909d3c5694784adc46e78a4a Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 7 Jun 2013 10:41:03 -0400 Subject: drm/radeon: fix AVI infoframe generation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - remove adding 2 to checksum, this is incorrect. This was incorrectly introduced in: 92db7f6c860b8190571a9dc1fcbc16d003422fe8 http://lists.freedesktop.org/archives/dri-devel/2011-December/017717.html However, the off by 2 was due to adding the version twice. From the examples in the URL above: [Rafał Miłecki][RV620] fglrx: 0x7454: 00 A8 5E 79 R600_HDMI_VIDEOINFOFRAME_0 0x7458: 00 28 00 10 R600_HDMI_VIDEOINFOFRAME_1 0x745C: 00 48 00 28 R600_HDMI_VIDEOINFOFRAME_2 0x7460: 02 00 00 48 R600_HDMI_VIDEOINFOFRAME_3 =================== (0x82 + 0x2 + 0xD) + 0x1F8 = 0x289 -0x289 = 0x77 However, the payload sum is not 0x1f8, it's 0x1f6. 00 + A8 + 5E + 00 + 00 + 28 + 00 + 10 + 00 + 48 + 00 + 28 + 00 + 48 = 0x1f6 Bits 25:24 of HDMI_VIDEOINFOFRAME_3 are the packet version, not part of the payload. So the total would be: (0x82 + 0x2 + 0xD) + 0x1f6 = 0x287 -0x287 = 0x79 - properly emit the AVI infoframe version. This was not being emitted previous which is probably what caused the issue above. This should fix blank screen when HDMI audio is enabled on certain monitors. Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org Cc: Rafał Miłecki --- drivers/gpu/drm/radeon/evergreen_hdmi.c | 11 ++--------- drivers/gpu/drm/radeon/r600_hdmi.c | 11 ++--------- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/radeon/evergreen_hdmi.c b/drivers/gpu/drm/radeon/evergreen_hdmi.c index ed7c8a768092..b9c6f7675e59 100644 --- a/drivers/gpu/drm/radeon/evergreen_hdmi.c +++ b/drivers/gpu/drm/radeon/evergreen_hdmi.c @@ -128,14 +128,7 @@ static void evergreen_hdmi_update_avi_infoframe(struct drm_encoder *encoder, struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; uint32_t offset = dig->afmt->offset; uint8_t *frame = buffer + 3; - - /* Our header values (type, version, length) should be alright, Intel - * is using the same. Checksum function also seems to be OK, it works - * fine for audio infoframe. However calculated value is always lower - * by 2 in comparison to fglrx. It breaks displaying anything in case - * of TVs that strictly check the checksum. Hack it manually here to - * workaround this issue. */ - frame[0x0] += 2; + uint8_t *header = buffer; WREG32(AFMT_AVI_INFO0 + offset, frame[0x0] | (frame[0x1] << 8) | (frame[0x2] << 16) | (frame[0x3] << 24)); @@ -144,7 +137,7 @@ static void evergreen_hdmi_update_avi_infoframe(struct drm_encoder *encoder, WREG32(AFMT_AVI_INFO2 + offset, frame[0x8] | (frame[0x9] << 8) | (frame[0xA] << 16) | (frame[0xB] << 24)); WREG32(AFMT_AVI_INFO3 + offset, - frame[0xC] | (frame[0xD] << 8)); + frame[0xC] | (frame[0xD] << 8) | (header[1] << 24)); } static void evergreen_audio_set_dto(struct drm_encoder *encoder, u32 clock) diff --git a/drivers/gpu/drm/radeon/r600_hdmi.c b/drivers/gpu/drm/radeon/r600_hdmi.c index 456750a0daa5..e73b2a73494a 100644 --- a/drivers/gpu/drm/radeon/r600_hdmi.c +++ b/drivers/gpu/drm/radeon/r600_hdmi.c @@ -133,14 +133,7 @@ static void r600_hdmi_update_avi_infoframe(struct drm_encoder *encoder, struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; uint32_t offset = dig->afmt->offset; uint8_t *frame = buffer + 3; - - /* Our header values (type, version, length) should be alright, Intel - * is using the same. Checksum function also seems to be OK, it works - * fine for audio infoframe. However calculated value is always lower - * by 2 in comparison to fglrx. It breaks displaying anything in case - * of TVs that strictly check the checksum. Hack it manually here to - * workaround this issue. */ - frame[0x0] += 2; + uint8_t *header = buffer; WREG32(HDMI0_AVI_INFO0 + offset, frame[0x0] | (frame[0x1] << 8) | (frame[0x2] << 16) | (frame[0x3] << 24)); @@ -149,7 +142,7 @@ static void r600_hdmi_update_avi_infoframe(struct drm_encoder *encoder, WREG32(HDMI0_AVI_INFO2 + offset, frame[0x8] | (frame[0x9] << 8) | (frame[0xA] << 16) | (frame[0xB] << 24)); WREG32(HDMI0_AVI_INFO3 + offset, - frame[0xC] | (frame[0xD] << 8)); + frame[0xC] | (frame[0xD] << 8) | (header[1] << 24)); } /* -- cgit v1.2.3 From 80101790670385a85aca35ecae4b89e3f2fceecc Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 10 Jun 2013 09:57:07 -0400 Subject: drm/radeon: add backlight quirk for hybrid mac Mac laptops with multiple GPUs apparently use the gmux driver for backlight control. Don't register a radeon backlight interface. We may need to add other pci ids for other hybrid mac laptops. Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=65377 Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org --- drivers/gpu/drm/radeon/atombios_encoders.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c index 8406c8251fbf..4120d355cadd 100644 --- a/drivers/gpu/drm/radeon/atombios_encoders.c +++ b/drivers/gpu/drm/radeon/atombios_encoders.c @@ -186,6 +186,13 @@ void radeon_atom_backlight_init(struct radeon_encoder *radeon_encoder, u8 backlight_level; char bl_name[16]; + /* Mac laptops with multiple GPUs use the gmux driver for backlight + * so don't register a backlight device + */ + if ((rdev->pdev->subsystem_vendor == PCI_VENDOR_ID_APPLE) && + (rdev->pdev->device == 0x6741)) + return; + if (!radeon_encoder->enc_priv) return; -- cgit v1.2.3 From fdafa9e276235225ce087695984cf1e52dd0c159 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 12 Jun 2013 11:47:24 +0200 Subject: drm/i915: disable sdvo pixel multiplier cross-check for HAS_PCH_SPLIT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't (yet) have proper pixel multiplier readout support on pch split platforms, so the cross check will naturally fail. v2: Fix spelling in the comment, spotted by Ville. v3: Since the ordering constraint is pretty tricky between the crtc get_pipe_config callback and the encoder->get_config callback add a few comments about it. Prompted by a discussion with Chris Wilson on irc about why this does work anywhere else than on i915g/gm. Reported-by: Chris Wilson Acked-by: Chris Wilson Cc: Chris Wilson Cc: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_drv.h | 3 ++- drivers/gpu/drm/i915/intel_sdvo.c | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index b77df049b773..4a69bdc4f8ca 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -140,7 +140,8 @@ struct intel_encoder { * it is connected to in the pipe parameter. */ bool (*get_hw_state)(struct intel_encoder *, enum pipe *pipe); /* Reconstructs the equivalent mode flags for the current hardware - * state. */ + * state. This must be called _after_ display->get_pipe_config has + * pre-filled the pipe config. */ void (*get_config)(struct intel_encoder *, struct intel_crtc_config *pipe_config); int crtc_mask; diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 97d3099aea23..b8e1623be30d 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1342,6 +1342,13 @@ static void intel_sdvo_get_config(struct intel_encoder *encoder, pipe_config->adjusted_mode.flags |= flags; + /* + * pixel multiplier readout is tricky: Only on i915g/gm it is stored in + * the sdvo port register, on all other platforms it is part of the dpll + * state. Since the general pipe state readout happens before the + * encoder->get_config we so already have a valid pixel multplier on all + * other platfroms. + */ if (IS_I915G(dev) || IS_I915GM(dev)) { sdvox = I915_READ(intel_sdvo->sdvo_reg); pipe_config->pixel_multiplier = @@ -1362,6 +1369,10 @@ static void intel_sdvo_get_config(struct intel_encoder *encoder, encoder_pixel_multiplier = 4; break; } + + if(HAS_PCH_SPLIT(dev)) + return; /* no pixel multiplier readout support yet */ + WARN(encoder_pixel_multiplier != pipe_config->pixel_multiplier, "SDVO pixel multiplier mismatch, port: %i, encoder: %i\n", pipe_config->pixel_multiplier, encoder_pixel_multiplier); -- cgit v1.2.3 From 7c647af43f1517b5b2604b8a69ea72a17073e15f Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 10 Jun 2013 10:24:41 -0300 Subject: ASoC: sgtl5000: Use i2c_get_clientdata() We should use i2c_get_clientdata() to get the codec private structure. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown --- sound/soc/codecs/sgtl5000.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index 2e0227bda8e0..d441559dc92c 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -1571,11 +1571,7 @@ disable_clk: static int sgtl5000_i2c_remove(struct i2c_client *client) { - struct sgtl5000_priv *sgtl5000; - sgtl5000 = devm_kzalloc(&client->dev, sizeof(struct sgtl5000_priv), - GFP_KERNEL); - if (!sgtl5000) - return -ENOMEM; + struct sgtl5000_priv *sgtl5000 = i2c_get_clientdata(client); snd_soc_unregister_codec(&client->dev); clk_disable_unprepare(sgtl5000->mclk); -- cgit v1.2.3 From 77845b11a35f293a344fe868852e8c61498ae777 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 10 Jun 2013 10:24:42 -0300 Subject: ASoC: sgtl5000: Add 'clocks' entry as a required propery Since commit 9e13f345 (ASoC: sgtl5000: Let the codec acquire its clock) , the 'clocks' entry is mandatory, so update the binding doc. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/sgtl5000.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/sgtl5000.txt b/Documentation/devicetree/bindings/sound/sgtl5000.txt index 9cc44449508d..955df60a118c 100644 --- a/Documentation/devicetree/bindings/sound/sgtl5000.txt +++ b/Documentation/devicetree/bindings/sound/sgtl5000.txt @@ -5,9 +5,12 @@ Required properties: - reg : the I2C address of the device +- clocks : the clock provider of SYS_MCLK + Example: codec: sgtl5000@0a { compatible = "fsl,sgtl5000"; reg = <0x0a>; + clocks = <&clks 150>; }; -- cgit v1.2.3 From b9840124d699614f1429748e43827b1fb35c1138 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 10 Jun 2013 13:26:05 -0300 Subject: ASoC: imx-sgtl5000: Use devm_clk_get() Commit 9e13f345 (ASoC: sgtl5000: Let the codec acquire its clock) removed the clk_put calls. Let's use devm_clk_get() instead, so that we do not need to call them anymore. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown --- sound/soc/fsl/imx-sgtl5000.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/fsl/imx-sgtl5000.c b/sound/soc/fsl/imx-sgtl5000.c index 823151b7653b..7a8bc1220b2e 100644 --- a/sound/soc/fsl/imx-sgtl5000.c +++ b/sound/soc/fsl/imx-sgtl5000.c @@ -128,7 +128,7 @@ static int imx_sgtl5000_probe(struct platform_device *pdev) goto fail; } - data->codec_clk = clk_get(&codec_dev->dev, NULL); + data->codec_clk = devm_clk_get(&codec_dev->dev, NULL); if (IS_ERR(data->codec_clk)) goto fail; -- cgit v1.2.3 From 8de2ae2a7f1fd71dc56d6b014029f93093e9c5d5 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Tue, 11 Jun 2013 02:43:30 +0800 Subject: ASoC: fsl: add imx-wm8962 machine driver This is the initial imx-wm8962 device-tree-only machine driver working with fsl_ssi driver. More features can be added on top of it later. Signed-off-by: Nicolin Chen Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/imx-audio-wm8962.txt | 46 +++ sound/soc/fsl/Kconfig | 12 + sound/soc/fsl/Makefile | 2 + sound/soc/fsl/imx-wm8962.c | 323 +++++++++++++++++++++ 4 files changed, 383 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt create mode 100644 sound/soc/fsl/imx-wm8962.c diff --git a/Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt b/Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt new file mode 100644 index 000000000000..f49450a87890 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt @@ -0,0 +1,46 @@ +Freescale i.MX audio complex with WM8962 codec + +Required properties: +- compatible : "fsl,imx-audio-wm8962" +- model : The user-visible name of this sound complex +- ssi-controller : The phandle of the i.MX SSI controller +- audio-codec : The phandle of the WM8962 audio codec +- audio-routing : A list of the connections between audio components. + Each entry is a pair of strings, the first being the connection's sink, + the second being the connection's source. Valid names could be power + supplies, WM8962 pins, and the jacks on the board: + + Power supplies: + * Mic Bias + + Board connectors: + * Mic Jack + * Headphone Jack + * Ext Spk + +- mux-int-port : The internal port of the i.MX audio muxer (AUDMUX) +- mux-ext-port : The external port of the i.MX audio muxer + +Note: The AUDMUX port numbering should start at 1, which is consistent with +hardware manual. + +Example: + +sound { + compatible = "fsl,imx6q-sabresd-wm8962", + "fsl,imx-audio-wm8962"; + model = "wm8962-audio"; + ssi-controller = <&ssi2>; + audio-codec = <&codec>; + audio-routing = + "Headphone Jack", "HPOUTL", + "Headphone Jack", "HPOUTR", + "Ext Spk", "SPKOUTL", + "Ext Spk", "SPKOUTR", + "MICBIAS", "AMIC", + "IN3R", "MICBIAS", + "DMIC", "MICBIAS", + "DMICDAT", "DMIC"; + mux-int-port = <2>; + mux-ext-port = <3>; +}; diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 7860cc27e5b2..aa438546c912 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -168,6 +168,18 @@ config SND_SOC_EUKREA_TLV320 Enable I2S based access to the TLV320AIC23B codec attached to the SSI interface +config SND_SOC_IMX_WM8962 + tristate "SoC Audio support for i.MX boards with wm8962" + depends on OF && I2C + select SND_SOC_WM8962 + select SND_SOC_IMX_PCM_DMA + select SND_SOC_IMX_AUDMUX + select SND_SOC_FSL_SSI + select SND_SOC_FSL_UTILS + help + Say Y if you want to add support for SoC audio on an i.MX board with + a wm8962 codec. + config SND_SOC_IMX_SGTL5000 tristate "SoC Audio support for i.MX boards with sgtl5000" depends on OF && I2C diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index 91883f8a2321..d4b4aa8b5649 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -42,6 +42,7 @@ snd-soc-phycore-ac97-objs := phycore-ac97.o snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o snd-soc-wm1133-ev1-objs := wm1133-ev1.o snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o +snd-soc-imx-wm8962-objs := imx-wm8962.o snd-soc-imx-mc13783-objs := imx-mc13783.o obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o @@ -49,4 +50,5 @@ obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o +obj-$(CONFIG_SND_SOC_IMX_WM8962) += snd-soc-imx-wm8962.o obj-$(CONFIG_SND_SOC_IMX_MC13783) += snd-soc-imx-mc13783.o diff --git a/sound/soc/fsl/imx-wm8962.c b/sound/soc/fsl/imx-wm8962.c new file mode 100644 index 000000000000..52a36a90f4f4 --- /dev/null +++ b/sound/soc/fsl/imx-wm8962.c @@ -0,0 +1,323 @@ +/* + * Copyright 2013 Freescale Semiconductor, Inc. + * + * Based on imx-sgtl5000.c + * Copyright 2012 Freescale Semiconductor, Inc. + * Copyright 2012 Linaro Ltd. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../codecs/wm8962.h" +#include "imx-audmux.h" + +#define DAI_NAME_SIZE 32 + +struct imx_wm8962_data { + struct snd_soc_dai_link dai; + struct snd_soc_card card; + char codec_dai_name[DAI_NAME_SIZE]; + char platform_name[DAI_NAME_SIZE]; + struct clk *codec_clk; + unsigned int clk_frequency; +}; + +struct imx_priv { + struct platform_device *pdev; +}; +static struct imx_priv card_priv; + +static const struct snd_soc_dapm_widget imx_wm8962_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_SPK("Ext Spk", NULL), + SND_SOC_DAPM_MIC("AMIC", NULL), + SND_SOC_DAPM_MIC("DMIC", NULL), +}; + +static int sample_rate = 44100; +static snd_pcm_format_t sample_format = SNDRV_PCM_FORMAT_S16_LE; + +static int imx_hifi_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + sample_rate = params_rate(params); + sample_format = params_format(params); + + return 0; +} + +static struct snd_soc_ops imx_hifi_ops = { + .hw_params = imx_hifi_hw_params, +}; + +static int imx_wm8962_set_bias_level(struct snd_soc_card *card, + struct snd_soc_dapm_context *dapm, + enum snd_soc_bias_level level) +{ + struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai; + struct imx_priv *priv = &card_priv; + struct imx_wm8962_data *data = platform_get_drvdata(priv->pdev); + struct device *dev = &priv->pdev->dev; + unsigned int pll_out; + int ret; + + if (dapm->dev != codec_dai->dev) + return 0; + + switch (level) { + case SND_SOC_BIAS_PREPARE: + if (dapm->bias_level == SND_SOC_BIAS_STANDBY) { + if (sample_format == SNDRV_PCM_FORMAT_S24_LE) + pll_out = sample_rate * 384; + else + pll_out = sample_rate * 256; + + ret = snd_soc_dai_set_pll(codec_dai, WM8962_FLL, + WM8962_FLL_MCLK, data->clk_frequency, + pll_out); + if (ret < 0) { + dev_err(dev, "failed to start FLL: %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_sysclk(codec_dai, + WM8962_SYSCLK_FLL, pll_out, + SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(dev, "failed to set SYSCLK: %d\n", ret); + return ret; + } + } + break; + + case SND_SOC_BIAS_STANDBY: + if (dapm->bias_level == SND_SOC_BIAS_PREPARE) { + ret = snd_soc_dai_set_sysclk(codec_dai, + WM8962_SYSCLK_MCLK, data->clk_frequency, + SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(dev, + "failed to switch away from FLL: %d\n", + ret); + return ret; + } + + ret = snd_soc_dai_set_pll(codec_dai, WM8962_FLL, + 0, 0, 0); + if (ret < 0) { + dev_err(dev, "failed to stop FLL: %d\n", ret); + return ret; + } + } + break; + + default: + break; + } + + dapm->bias_level = level; + + return 0; +} + +static int imx_wm8962_late_probe(struct snd_soc_card *card) +{ + struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai; + struct imx_priv *priv = &card_priv; + struct imx_wm8962_data *data = platform_get_drvdata(priv->pdev); + struct device *dev = &priv->pdev->dev; + int ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, WM8962_SYSCLK_MCLK, + data->clk_frequency, SND_SOC_CLOCK_IN); + if (ret < 0) + dev_err(dev, "failed to set sysclk in %s\n", __func__); + + return ret; +} + +static int imx_wm8962_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device_node *ssi_np, *codec_np; + struct platform_device *ssi_pdev; + struct imx_priv *priv = &card_priv; + struct i2c_client *codec_dev; + struct imx_wm8962_data *data; + int int_port, ext_port; + int ret; + + priv->pdev = pdev; + + ret = of_property_read_u32(np, "mux-int-port", &int_port); + if (ret) { + dev_err(&pdev->dev, "mux-int-port missing or invalid\n"); + return ret; + } + ret = of_property_read_u32(np, "mux-ext-port", &ext_port); + if (ret) { + dev_err(&pdev->dev, "mux-ext-port missing or invalid\n"); + return ret; + } + + /* + * The port numbering in the hardware manual starts at 1, while + * the audmux API expects it starts at 0. + */ + int_port--; + ext_port--; + ret = imx_audmux_v2_configure_port(int_port, + IMX_AUDMUX_V2_PTCR_SYN | + IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) | + IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) | + IMX_AUDMUX_V2_PTCR_TFSDIR | + IMX_AUDMUX_V2_PTCR_TCLKDIR, + IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port)); + if (ret) { + dev_err(&pdev->dev, "audmux internal port setup failed\n"); + return ret; + } + imx_audmux_v2_configure_port(ext_port, + IMX_AUDMUX_V2_PTCR_SYN, + IMX_AUDMUX_V2_PDCR_RXDSEL(int_port)); + if (ret) { + dev_err(&pdev->dev, "audmux external port setup failed\n"); + return ret; + } + + ssi_np = of_parse_phandle(pdev->dev.of_node, "ssi-controller", 0); + codec_np = of_parse_phandle(pdev->dev.of_node, "audio-codec", 0); + if (!ssi_np || !codec_np) { + dev_err(&pdev->dev, "phandle missing or invalid\n"); + ret = -EINVAL; + goto fail; + } + + ssi_pdev = of_find_device_by_node(ssi_np); + if (!ssi_pdev) { + dev_err(&pdev->dev, "failed to find SSI platform device\n"); + ret = -EINVAL; + goto fail; + } + codec_dev = of_find_i2c_device_by_node(codec_np); + if (!codec_dev || !codec_dev->driver) { + dev_err(&pdev->dev, "failed to find codec platform device\n"); + return -EINVAL; + } + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto fail; + } + + data->codec_clk = devm_clk_get(&codec_dev->dev, NULL); + if (IS_ERR(data->codec_clk)) { + ret = PTR_ERR(data->codec_clk); + dev_err(&codec_dev->dev, "failed to get codec clk: %d\n", ret); + goto fail; + } + + data->clk_frequency = clk_get_rate(data->codec_clk); + ret = clk_prepare_enable(data->codec_clk); + if (ret) { + dev_err(&codec_dev->dev, "failed to enable codec clk: %d\n", ret); + goto fail; + } + + data->dai.name = "HiFi"; + data->dai.stream_name = "HiFi"; + data->dai.codec_dai_name = "wm8962"; + data->dai.codec_of_node = codec_np; + data->dai.cpu_dai_name = dev_name(&ssi_pdev->dev); + data->dai.platform_of_node = ssi_np; + data->dai.ops = &imx_hifi_ops; + data->dai.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM; + + data->card.dev = &pdev->dev; + ret = snd_soc_of_parse_card_name(&data->card, "model"); + if (ret) + goto clk_fail; + ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing"); + if (ret) + goto clk_fail; + data->card.num_links = 1; + data->card.dai_link = &data->dai; + data->card.dapm_widgets = imx_wm8962_dapm_widgets; + data->card.num_dapm_widgets = ARRAY_SIZE(imx_wm8962_dapm_widgets); + + data->card.late_probe = imx_wm8962_late_probe; + data->card.set_bias_level = imx_wm8962_set_bias_level; + + ret = snd_soc_register_card(&data->card); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); + goto clk_fail; + } + + platform_set_drvdata(pdev, data); + of_node_put(ssi_np); + of_node_put(codec_np); + + return 0; + +clk_fail: + if (!IS_ERR(data->codec_clk)) + clk_disable_unprepare(data->codec_clk); +fail: + if (ssi_np) + of_node_put(ssi_np); + if (codec_np) + of_node_put(codec_np); + + return ret; +} + +static int imx_wm8962_remove(struct platform_device *pdev) +{ + struct imx_wm8962_data *data = platform_get_drvdata(pdev); + + if (!IS_ERR(data->codec_clk)) + clk_disable_unprepare(data->codec_clk); + snd_soc_unregister_card(&data->card); + + return 0; +} + +static const struct of_device_id imx_wm8962_dt_ids[] = { + { .compatible = "fsl,imx-audio-wm8962", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx_wm8962_dt_ids); + +static struct platform_driver imx_wm8962_driver = { + .driver = { + .name = "imx-wm8962", + .owner = THIS_MODULE, + .of_match_table = imx_wm8962_dt_ids, + }, + .probe = imx_wm8962_probe, + .remove = imx_wm8962_remove, +}; +module_platform_driver(imx_wm8962_driver); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("Freescale i.MX WM8962 ASoC machine driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:imx-wm8962"); -- cgit v1.2.3 From 5e83c160d83070152a595f57a6ca7c5bb1ce16b3 Mon Sep 17 00:00:00 2001 From: Rajeev Kumar Date: Tue, 11 Jun 2013 09:29:07 +0530 Subject: ASoC: dwc: debug message correction. Debug message correction. Signed-off-by: Rajeev Kumar Signed-off-by: Mark Brown --- sound/soc/dwc/designware_i2s.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index 593a3ea12d4c..2c625264223b 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -396,7 +396,7 @@ static int dw_i2s_probe(struct platform_device *pdev) } if (cap & DWC_I2S_PLAY) { - dev_dbg(&pdev->dev, " SPEAr: play supported\n"); + dev_dbg(&pdev->dev, " designware: play supported\n"); dw_i2s_dai->playback.channels_min = MIN_CHANNEL_NUM; dw_i2s_dai->playback.channels_max = pdata->channel; dw_i2s_dai->playback.formats = pdata->snd_fmts; @@ -404,7 +404,7 @@ static int dw_i2s_probe(struct platform_device *pdev) } if (cap & DWC_I2S_RECORD) { - dev_dbg(&pdev->dev, "SPEAr: record supported\n"); + dev_dbg(&pdev->dev, "designware: record supported\n"); dw_i2s_dai->capture.channels_min = MIN_CHANNEL_NUM; dw_i2s_dai->capture.channels_max = pdata->channel; dw_i2s_dai->capture.formats = pdata->snd_fmts; -- cgit v1.2.3 From 22a4adf25826d1128d116dd4a313f66175c703bd Mon Sep 17 00:00:00 2001 From: Rajeev Kumar Date: Tue, 11 Jun 2013 09:29:08 +0530 Subject: ASoC: dwc: Folder path correction in file header. Folder path correction in file header. Signed-off-by: Rajeev Kumar Signed-off-by: Mark Brown --- sound/soc/dwc/designware_i2s.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index 2c625264223b..70eb37a5dd16 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -1,7 +1,7 @@ /* * ALSA SoC Synopsys I2S Audio Layer * - * sound/soc/spear/designware_i2s.c + * sound/soc/dwc/designware_i2s.c * * Copyright (C) 2010 ST Microelectronics * Rajeev Kumar -- cgit v1.2.3 From 1aad4e574bced05b4036e79981a7800dd275cf1c Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 10 Jun 2013 22:23:53 +0800 Subject: ASoC: ssm2518: Fix trivial typo in checking tx_mask and rx_mask values Otherwise, ssm2518_set_tdm_slot() always returns error if slots != 0. Signed-off-by: Axel Lin Acked-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/codecs/ssm2518.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/ssm2518.c b/sound/soc/codecs/ssm2518.c index 3139a1bde295..95aed552139a 100644 --- a/sound/soc/codecs/ssm2518.c +++ b/sound/soc/codecs/ssm2518.c @@ -539,7 +539,7 @@ static int ssm2518_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, SSM2518_REG_SAI_CTRL1, SSM2518_SAI_CTRL1_SAI_MASK, SSM2518_SAI_CTRL1_SAI_I2S); - if (tx_mask == 0 || tx_mask != 0) + if (tx_mask == 0 || rx_mask != 0) return -EINVAL; if (slots == 1) { -- cgit v1.2.3 From f724ba3b07aa5a012b7b0be323484195b5026282 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Fri, 7 Jun 2013 13:53:04 +0200 Subject: ASoC: codecs: adau1701: factor out firmware reset Some runtime-determined constraints might need to be satisfied prior to firmware loading, so the actual download and releasing the device from reset has to be postponed. Factor it out first, so we have everything at one place. This also changes the behaviour in a way that adau1701_i2c_probe() will assert the reset line, and wait for the codec probe to release it. Signed-off-by: Daniel Mack Acked-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/codecs/adau1701.c | 49 +++++++++++++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c index 3fc176387351..b6b1a773bd37 100644 --- a/sound/soc/codecs/adau1701.c +++ b/sound/soc/codecs/adau1701.c @@ -90,6 +90,7 @@ #define ADAU1701_FIRMWARE "adau1701.bin" struct adau1701 { + int gpio_nreset; unsigned int dai_fmt; }; @@ -183,9 +184,37 @@ static unsigned int adau1701_read(struct snd_soc_codec *codec, unsigned int reg) return value; } -static int adau1701_load_firmware(struct i2c_client *client) +static void adau1701_reset(struct snd_soc_codec *codec) { - return process_sigma_firmware(client, ADAU1701_FIRMWARE); + struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec); + + if (!gpio_is_valid(adau1701->gpio_nreset)) + return; + + gpio_set_value(adau1701->gpio_nreset, 0); + /* minimum reset time is 20ns */ + udelay(1); + gpio_set_value(adau1701->gpio_nreset, 1); + /* power-up time may be as long as 85ms */ + mdelay(85); +} + +static int adau1701_init(struct snd_soc_codec *codec) +{ + int ret; + struct i2c_client *client = to_i2c_client(codec->dev); + + adau1701_reset(codec); + + ret = process_sigma_firmware(client, ADAU1701_FIRMWARE); + if (ret) { + dev_warn(codec->dev, "Failed to load firmware\n"); + return ret; + } + + snd_soc_write(codec, ADAU1701_DACSET, ADAU1701_DACSET_DACINIT); + + return 0; } static int adau1701_set_capture_pcm_format(struct snd_soc_codec *codec, @@ -466,15 +495,13 @@ MODULE_DEVICE_TABLE(of, adau1701_dt_ids); static int adau1701_probe(struct snd_soc_codec *codec) { int ret; - struct i2c_client *client = to_i2c_client(codec->dev); - codec->control_data = client; + codec->control_data = to_i2c_client(codec->dev); - ret = adau1701_load_firmware(client); + ret = adau1701_init(codec); if (ret) - dev_warn(codec->dev, "Failed to load firmware\n"); + return ret; - snd_soc_write(codec, ADAU1701_DACSET, ADAU1701_DACSET_DACINIT); snd_soc_write(codec, ADAU1701_DSPCTRL, ADAU1701_DSPCTRL_CR); return 0; @@ -524,14 +551,10 @@ static int adau1701_i2c_probe(struct i2c_client *client, "ADAU1701 Reset"); if (ret < 0) return ret; - - /* minimum reset time is 20ns */ - udelay(1); - gpio_set_value(gpio_nreset, 1); - /* power-up time may be as long as 85ms */ - mdelay(85); } + adau1701->gpio_nreset = gpio_nreset; + i2c_set_clientdata(client, adau1701); ret = snd_soc_register_codec(&client->dev, &adau1701_codec_drv, &adau1701_dai, 1); -- cgit v1.2.3 From 4119c0c0c6e4508236672c3ec714da25eed783ce Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 10 Jun 2013 21:20:44 +0800 Subject: ASoC: adav80x: Select SND_SOC_ADAV80X if SND_SOC_I2C_AND_SPI This driver is useless if both SPI and I2C are not configured. Thus don't build this driver if both SPI and I2C are not configured. This patch silences below build warning if both SPI and I2C are not configured. CC sound/soc/codecs/adav80x.o sound/soc/codecs/adav80x.c:842:12: warning: 'adav80x_bus_probe' defined but not used [-Wunused-function] sound/soc/codecs/adav80x.c:863:12: warning: 'adav80x_bus_remove' defined but not used [-Wunused-function] Signed-off-by: Axel Lin Acked-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 2f45f00e31b0..5841674b6993 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -19,7 +19,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_AD1980 if SND_SOC_AC97_BUS select SND_SOC_AD73311 select SND_SOC_ADAU1373 if I2C - select SND_SOC_ADAV80X + select SND_SOC_ADAV80X if SND_SOC_I2C_AND_SPI select SND_SOC_ADS117X select SND_SOC_AK4104 if SPI_MASTER select SND_SOC_AK4535 if I2C -- cgit v1.2.3 From e58070ee4fdf797c47cb296992ce8db3df715eca Mon Sep 17 00:00:00 2001 From: Rajeev Kumar Date: Mon, 10 Jun 2013 16:19:40 +0530 Subject: ASoC: Add Kconfig and Makefile to support SPEAr audio driver This patch adds Kconfig and Makefile to support SPEAr Audio driver. Signed-off-by: Rajeev Kumar Signed-off-by: Mark Brown --- sound/soc/Kconfig | 1 + sound/soc/Makefile | 1 + sound/soc/spear/Kconfig | 39 +++++++++++++++++++++++++++++++++++++++ sound/soc/spear/Makefile | 7 +++++++ 4 files changed, 48 insertions(+) create mode 100644 sound/soc/spear/Kconfig create mode 100644 sound/soc/spear/Makefile diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 9e675c76436c..45eeaa9f7fec 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -51,6 +51,7 @@ source "sound/soc/pxa/Kconfig" source "sound/soc/samsung/Kconfig" source "sound/soc/s6000/Kconfig" source "sound/soc/sh/Kconfig" +source "sound/soc/spear/Kconfig" source "sound/soc/tegra/Kconfig" source "sound/soc/txx9/Kconfig" source "sound/soc/ux500/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 197b6ae54c8d..bc0261476d7a 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_SND_SOC) += pxa/ obj-$(CONFIG_SND_SOC) += samsung/ obj-$(CONFIG_SND_SOC) += s6000/ obj-$(CONFIG_SND_SOC) += sh/ +obj-$(CONFIG_SND_SOC) += spear/ obj-$(CONFIG_SND_SOC) += tegra/ obj-$(CONFIG_SND_SOC) += txx9/ obj-$(CONFIG_SND_SOC) += ux500/ diff --git a/sound/soc/spear/Kconfig b/sound/soc/spear/Kconfig new file mode 100644 index 000000000000..3b7cdadc11cc --- /dev/null +++ b/sound/soc/spear/Kconfig @@ -0,0 +1,39 @@ +config SND_SPEAR_EVM + tristate "SoC Audio support for SPEAr EVM" + select SND_DESIGNWARE_I2S + select SND_SOC_STA529 + select SND_SPEAR_SOC + help + Say Y if you want to add support for SoC audio on SPEAr + platform + +config SND_SPEAR1340_EVM + tristate "SoC Audio support for SPEAr1340 EVM" + select SND_DESIGNWARE_I2S + select SND_SOC_STA529 + select SND_SPEAR_SPDIF_OUT + select SND_SPEAR_SPDIF_IN + select SND_SOC_SPDIF + select SND_SPEAR_SOC + help + Say Y if you want to add support for SoC audio on SPEAr1340 + platform + +config SND_SPEAR_SOC + tristate "SoC Audio for the ST chip" + depends on SND_DESIGNWARE_I2S || SND_SPEAR_SPDIF_OUT || \ + SND_SPEAR_SPDIF_IN + help + Say Y or M if you want to add support for any of the audio + controllers (I2S/SPDIF). You will also need to select + the audio interface codecs to support below. + +config SND_SPEAR_SPDIF_OUT + tristate "SPEAr SPDIF Out Device Driver" + help + Say Y or M if you want to add support for SPDIF OUT driver. + +config SND_SPEAR_SPDIF_IN + tristate "SPEAr SPDIF IN Device Driver" + help + Say Y or M if you want to add support for SPDIF IN driver. diff --git a/sound/soc/spear/Makefile b/sound/soc/spear/Makefile new file mode 100644 index 000000000000..b36512655bcf --- /dev/null +++ b/sound/soc/spear/Makefile @@ -0,0 +1,7 @@ +# SPEAR Platform Support +obj-$(CONFIG_SND_SPEAR_SOC) += spear_pcm.o +obj-$(CONFIG_SND_SPEAR_SPDIF_IN) += spdif_in.o +obj-$(CONFIG_SND_SPEAR_SPDIF_OUT) += spdif_out.o + +# SPEAR Machine Support +obj-$(CONFIG_SND_SPEAR_EVM) += spear_evb.o -- cgit v1.2.3 From 67252287871113deba96adf7e4df1752f3f08688 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Tue, 11 Jun 2013 13:18:15 +0100 Subject: regmap: Add regmap_field APIs It is common to access regmap registers at bit level, using regmap_update_bits or regmap_read functions, however the end user has to take care of a mask or shifting. This becomes overhead when such use cases are high. Having a common function to do this is much convenient and less error prone. The idea of regmap_field is simple, regmap_field gives a logical structure to bits of the regmap register, and the driver can use this logical entity without the knowledge of the bit positions and masks all over the code. This way code looks much neat and it need not handle the masks, shifts every time it access the those entities. With this new regmap_field_read/write apis the end user can setup a regmap field using regmap_field_init and use the return regmap_field to read write the register field without worrying about the masks or shifts. Also this apis will be useful for drivers which are based on regmaps, like some clocks or pinctrls which can work on the regmap_fields directly without having to worry about bit positions. Signed-off-by: Srinivas Kandagatla Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 8 +++ drivers/base/regmap/regmap.c | 130 +++++++++++++++++++++++++++++++++++++++++ include/linux/regmap.h | 31 ++++++++++ 3 files changed, 169 insertions(+) diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index c130536e0ab0..c5f6ebd0466d 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -174,6 +174,14 @@ struct regmap_range_node { unsigned int window_len; }; +struct regmap_field { + struct regmap *regmap; + unsigned int mask; + /* lsb */ + unsigned int shift; + unsigned int reg; +}; + #ifdef CONFIG_DEBUG_FS extern void regmap_debugfs_initcall(void); extern void regmap_debugfs_init(struct regmap *map, const char *name); diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index a941dcfe7590..fef6f13b96bb 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -801,6 +801,95 @@ struct regmap *devm_regmap_init(struct device *dev, } EXPORT_SYMBOL_GPL(devm_regmap_init); +static void regmap_field_init(struct regmap_field *rm_field, + struct regmap *regmap, struct reg_field reg_field) +{ + int field_bits = reg_field.msb - reg_field.lsb + 1; + rm_field->regmap = regmap; + rm_field->reg = reg_field.reg; + rm_field->shift = reg_field.lsb; + rm_field->mask = ((BIT(field_bits) - 1) << reg_field.lsb); +} + +/** + * devm_regmap_field_alloc(): Allocate and initialise a register field + * in a register map. + * + * @dev: Device that will be interacted with + * @regmap: regmap bank in which this register field is located. + * @reg_field: Register field with in the bank. + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct regmap_field. The regmap_field will be automatically freed + * by the device management code. + */ +struct regmap_field *devm_regmap_field_alloc(struct device *dev, + struct regmap *regmap, struct reg_field reg_field) +{ + struct regmap_field *rm_field = devm_kzalloc(dev, + sizeof(*rm_field), GFP_KERNEL); + if (!rm_field) + return ERR_PTR(-ENOMEM); + + regmap_field_init(rm_field, regmap, reg_field); + + return rm_field; + +} +EXPORT_SYMBOL_GPL(devm_regmap_field_alloc); + +/** + * devm_regmap_field_free(): Free register field allocated using + * devm_regmap_field_alloc. Usally drivers need not call this function, + * as the memory allocated via devm will be freed as per device-driver + * life-cyle. + * + * @dev: Device that will be interacted with + * @field: regmap field which should be freed. + */ +void devm_regmap_field_free(struct device *dev, + struct regmap_field *field) +{ + devm_kfree(dev, field); +} +EXPORT_SYMBOL_GPL(devm_regmap_field_free); + +/** + * regmap_field_alloc(): Allocate and initialise a register field + * in a register map. + * + * @regmap: regmap bank in which this register field is located. + * @reg_field: Register field with in the bank. + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct regmap_field. The regmap_field should be freed by the + * user once its finished working with it using regmap_field_free(). + */ +struct regmap_field *regmap_field_alloc(struct regmap *regmap, + struct reg_field reg_field) +{ + struct regmap_field *rm_field = kzalloc(sizeof(*rm_field), GFP_KERNEL); + + if (!rm_field) + return ERR_PTR(-ENOMEM); + + regmap_field_init(rm_field, regmap, reg_field); + + return rm_field; +} +EXPORT_SYMBOL_GPL(regmap_field_alloc); + +/** + * regmap_field_free(): Free register field allocated using regmap_field_alloc + * + * @field: regmap field which should be freed. + */ +void regmap_field_free(struct regmap_field *field) +{ + kfree(field); +} +EXPORT_SYMBOL_GPL(regmap_field_free); + /** * regmap_reinit_cache(): Reinitialise the current register cache * @@ -1249,6 +1338,22 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, } EXPORT_SYMBOL_GPL(regmap_raw_write); +/** + * regmap_field_write(): Write a value to a single register field + * + * @field: Register field to write to + * @val: Value to be written + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_field_write(struct regmap_field *field, unsigned int val) +{ + return regmap_update_bits(field->regmap, field->reg, + field->mask, val << field->shift); +} +EXPORT_SYMBOL_GPL(regmap_field_write); + /* * regmap_bulk_write(): Write multiple registers to the device * @@ -1531,6 +1636,31 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, } EXPORT_SYMBOL_GPL(regmap_raw_read); +/** + * regmap_field_read(): Read a value to a single register field + * + * @field: Register field to read from + * @val: Pointer to store read value + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_field_read(struct regmap_field *field, unsigned int *val) +{ + int ret; + unsigned int reg_val; + ret = regmap_read(field->regmap, field->reg, ®_val); + if (ret != 0) + return ret; + + reg_val &= field->mask; + reg_val >>= field->shift; + *val = reg_val; + + return ret; +} +EXPORT_SYMBOL_GPL(regmap_field_read); + /** * regmap_bulk_read(): Read multiple registers from the device * diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 02d84e24b7c2..eebea9b5f43e 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -23,6 +23,7 @@ struct irq_domain; struct spi_device; struct regmap; struct regmap_range_cfg; +struct regmap_field; /* An enum of all the supported cache types */ enum regcache_type { @@ -411,6 +412,36 @@ bool regmap_reg_in_ranges(unsigned int reg, const struct regmap_range *ranges, unsigned int nranges); +/** + * Description of an register field + * + * @reg: Offset of the register within the regmap bank + * @lsb: lsb of the register field. + * @reg: msb of the register field. + */ +struct reg_field { + unsigned int reg; + unsigned int lsb; + unsigned int msb; +}; + +#define REG_FIELD(_reg, _lsb, _msb) { \ + .reg = _reg, \ + .lsb = _lsb, \ + .msb = _msb, \ + } + +struct regmap_field *regmap_field_alloc(struct regmap *regmap, + struct reg_field reg_field); +void regmap_field_free(struct regmap_field *field); + +struct regmap_field *devm_regmap_field_alloc(struct device *dev, + struct regmap *regmap, struct reg_field reg_field); +void devm_regmap_field_free(struct device *dev, struct regmap_field *field); + +int regmap_field_read(struct regmap_field *field, unsigned int *val); +int regmap_field_write(struct regmap_field *field, unsigned int val); + /** * Description of an IRQ for the generic regmap irq_chip. * -- cgit v1.2.3 From 55c7401d92e16360e0987afe39355f1eb6300f31 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 10 Dec 2012 16:40:18 +0000 Subject: arm64: KVM: HYP mode world switch implementation The HYP mode world switch in all its glory. Implements save/restore of host/guest registers, EL2 trapping, IPA resolution, and additional services (tlb invalidation). Reviewed-by: Christopher Covington Signed-off-by: Marc Zyngier --- arch/arm64/kernel/asm-offsets.c | 33 +++ arch/arm64/kvm/hyp.S | 617 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 650 insertions(+) create mode 100644 arch/arm64/kvm/hyp.S diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c index a2a4d810bea3..49c162c03b69 100644 --- a/arch/arm64/kernel/asm-offsets.c +++ b/arch/arm64/kernel/asm-offsets.c @@ -104,5 +104,38 @@ int main(void) BLANK(); DEFINE(TZ_MINWEST, offsetof(struct timezone, tz_minuteswest)); DEFINE(TZ_DSTTIME, offsetof(struct timezone, tz_dsttime)); + BLANK(); +#ifdef CONFIG_KVM_ARM_HOST + DEFINE(VCPU_CONTEXT, offsetof(struct kvm_vcpu, arch.ctxt)); + DEFINE(CPU_GP_REGS, offsetof(struct kvm_cpu_context, gp_regs)); + DEFINE(CPU_USER_PT_REGS, offsetof(struct kvm_regs, regs)); + DEFINE(CPU_FP_REGS, offsetof(struct kvm_regs, fp_regs)); + DEFINE(CPU_SP_EL1, offsetof(struct kvm_regs, sp_el1)); + DEFINE(CPU_ELR_EL1, offsetof(struct kvm_regs, elr_el1)); + DEFINE(CPU_SPSR, offsetof(struct kvm_regs, spsr)); + DEFINE(CPU_SYSREGS, offsetof(struct kvm_cpu_context, sys_regs)); + DEFINE(VCPU_ESR_EL2, offsetof(struct kvm_vcpu, arch.fault.esr_el2)); + DEFINE(VCPU_FAR_EL2, offsetof(struct kvm_vcpu, arch.fault.far_el2)); + DEFINE(VCPU_HPFAR_EL2, offsetof(struct kvm_vcpu, arch.fault.hpfar_el2)); + DEFINE(VCPU_HCR_EL2, offsetof(struct kvm_vcpu, arch.hcr_el2)); + DEFINE(VCPU_IRQ_LINES, offsetof(struct kvm_vcpu, arch.irq_lines)); + DEFINE(VCPU_HOST_CONTEXT, offsetof(struct kvm_vcpu, arch.host_cpu_context)); + DEFINE(VCPU_TIMER_CNTV_CTL, offsetof(struct kvm_vcpu, arch.timer_cpu.cntv_ctl)); + DEFINE(VCPU_TIMER_CNTV_CVAL, offsetof(struct kvm_vcpu, arch.timer_cpu.cntv_cval)); + DEFINE(KVM_TIMER_CNTVOFF, offsetof(struct kvm, arch.timer.cntvoff)); + DEFINE(KVM_TIMER_ENABLED, offsetof(struct kvm, arch.timer.enabled)); + DEFINE(VCPU_KVM, offsetof(struct kvm_vcpu, kvm)); + DEFINE(VCPU_VGIC_CPU, offsetof(struct kvm_vcpu, arch.vgic_cpu)); + DEFINE(VGIC_CPU_HCR, offsetof(struct vgic_cpu, vgic_hcr)); + DEFINE(VGIC_CPU_VMCR, offsetof(struct vgic_cpu, vgic_vmcr)); + DEFINE(VGIC_CPU_MISR, offsetof(struct vgic_cpu, vgic_misr)); + DEFINE(VGIC_CPU_EISR, offsetof(struct vgic_cpu, vgic_eisr)); + DEFINE(VGIC_CPU_ELRSR, offsetof(struct vgic_cpu, vgic_elrsr)); + DEFINE(VGIC_CPU_APR, offsetof(struct vgic_cpu, vgic_apr)); + DEFINE(VGIC_CPU_LR, offsetof(struct vgic_cpu, vgic_lr)); + DEFINE(VGIC_CPU_NR_LR, offsetof(struct vgic_cpu, nr_lr)); + DEFINE(KVM_VTTBR, offsetof(struct kvm, arch.vttbr)); + DEFINE(KVM_VGIC_VCTRL, offsetof(struct kvm, arch.vgic.vctrl_base)); +#endif return 0; } diff --git a/arch/arm64/kvm/hyp.S b/arch/arm64/kvm/hyp.S new file mode 100644 index 000000000000..0b18c2e1e043 --- /dev/null +++ b/arch/arm64/kvm/hyp.S @@ -0,0 +1,617 @@ +/* + * Copyright (C) 2012,2013 - ARM Ltd + * Author: Marc Zyngier + * + * 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 . + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define CPU_GP_REG_OFFSET(x) (CPU_GP_REGS + x) +#define CPU_XREG_OFFSET(x) CPU_GP_REG_OFFSET(CPU_USER_PT_REGS + 8*x) +#define CPU_SPSR_OFFSET(x) CPU_GP_REG_OFFSET(CPU_SPSR + 8*x) +#define CPU_SYSREG_OFFSET(x) (CPU_SYSREGS + 8*x) + + .text + .pushsection .hyp.text, "ax" + .align PAGE_SHIFT + +__kvm_hyp_code_start: + .globl __kvm_hyp_code_start + +.macro save_common_regs + // x2: base address for cpu context + // x3: tmp register + + add x3, x2, #CPU_XREG_OFFSET(19) + stp x19, x20, [x3] + stp x21, x22, [x3, #16] + stp x23, x24, [x3, #32] + stp x25, x26, [x3, #48] + stp x27, x28, [x3, #64] + stp x29, lr, [x3, #80] + + mrs x19, sp_el0 + mrs x20, elr_el2 // EL1 PC + mrs x21, spsr_el2 // EL1 pstate + + stp x19, x20, [x3, #96] + str x21, [x3, #112] + + mrs x22, sp_el1 + mrs x23, elr_el1 + mrs x24, spsr_el1 + + str x22, [x2, #CPU_GP_REG_OFFSET(CPU_SP_EL1)] + str x23, [x2, #CPU_GP_REG_OFFSET(CPU_ELR_EL1)] + str x24, [x2, #CPU_SPSR_OFFSET(KVM_SPSR_EL1)] +.endm + +.macro restore_common_regs + // x2: base address for cpu context + // x3: tmp register + + ldr x22, [x2, #CPU_GP_REG_OFFSET(CPU_SP_EL1)] + ldr x23, [x2, #CPU_GP_REG_OFFSET(CPU_ELR_EL1)] + ldr x24, [x2, #CPU_SPSR_OFFSET(KVM_SPSR_EL1)] + + msr sp_el1, x22 + msr elr_el1, x23 + msr spsr_el1, x24 + + add x3, x2, #CPU_XREG_OFFSET(31) // SP_EL0 + ldp x19, x20, [x3] + ldr x21, [x3, #16] + + msr sp_el0, x19 + msr elr_el2, x20 // EL1 PC + msr spsr_el2, x21 // EL1 pstate + + add x3, x2, #CPU_XREG_OFFSET(19) + ldp x19, x20, [x3] + ldp x21, x22, [x3, #16] + ldp x23, x24, [x3, #32] + ldp x25, x26, [x3, #48] + ldp x27, x28, [x3, #64] + ldp x29, lr, [x3, #80] +.endm + +.macro save_host_regs + save_common_regs +.endm + +.macro restore_host_regs + restore_common_regs +.endm + +.macro save_fpsimd + // x2: cpu context address + // x3, x4: tmp regs + add x3, x2, #CPU_GP_REG_OFFSET(CPU_FP_REGS) + fpsimd_save x3, 4 +.endm + +.macro restore_fpsimd + // x2: cpu context address + // x3, x4: tmp regs + add x3, x2, #CPU_GP_REG_OFFSET(CPU_FP_REGS) + fpsimd_restore x3, 4 +.endm + +.macro save_guest_regs + // x0 is the vcpu address + // x1 is the return code, do not corrupt! + // x2 is the cpu context + // x3 is a tmp register + // Guest's x0-x3 are on the stack + + // Compute base to save registers + add x3, x2, #CPU_XREG_OFFSET(4) + stp x4, x5, [x3] + stp x6, x7, [x3, #16] + stp x8, x9, [x3, #32] + stp x10, x11, [x3, #48] + stp x12, x13, [x3, #64] + stp x14, x15, [x3, #80] + stp x16, x17, [x3, #96] + str x18, [x3, #112] + + pop x6, x7 // x2, x3 + pop x4, x5 // x0, x1 + + add x3, x2, #CPU_XREG_OFFSET(0) + stp x4, x5, [x3] + stp x6, x7, [x3, #16] + + save_common_regs +.endm + +.macro restore_guest_regs + // x0 is the vcpu address. + // x2 is the cpu context + // x3 is a tmp register + + // Prepare x0-x3 for later restore + add x3, x2, #CPU_XREG_OFFSET(0) + ldp x4, x5, [x3] + ldp x6, x7, [x3, #16] + push x4, x5 // Push x0-x3 on the stack + push x6, x7 + + // x4-x18 + ldp x4, x5, [x3, #32] + ldp x6, x7, [x3, #48] + ldp x8, x9, [x3, #64] + ldp x10, x11, [x3, #80] + ldp x12, x13, [x3, #96] + ldp x14, x15, [x3, #112] + ldp x16, x17, [x3, #128] + ldr x18, [x3, #144] + + // x19-x29, lr, sp*, elr*, spsr* + restore_common_regs + + // Last bits of the 64bit state + pop x2, x3 + pop x0, x1 + + // Do not touch any register after this! +.endm + +/* + * Macros to perform system register save/restore. + * + * Ordering here is absolutely critical, and must be kept consistent + * in {save,restore}_sysregs, {save,restore}_guest_32bit_state, + * and in kvm_asm.h. + * + * In other words, don't touch any of these unless you know what + * you are doing. + */ +.macro save_sysregs + // x2: base address for cpu context + // x3: tmp register + + add x3, x2, #CPU_SYSREG_OFFSET(MPIDR_EL1) + + mrs x4, vmpidr_el2 + mrs x5, csselr_el1 + mrs x6, sctlr_el1 + mrs x7, actlr_el1 + mrs x8, cpacr_el1 + mrs x9, ttbr0_el1 + mrs x10, ttbr1_el1 + mrs x11, tcr_el1 + mrs x12, esr_el1 + mrs x13, afsr0_el1 + mrs x14, afsr1_el1 + mrs x15, far_el1 + mrs x16, mair_el1 + mrs x17, vbar_el1 + mrs x18, contextidr_el1 + mrs x19, tpidr_el0 + mrs x20, tpidrro_el0 + mrs x21, tpidr_el1 + mrs x22, amair_el1 + mrs x23, cntkctl_el1 + + stp x4, x5, [x3] + stp x6, x7, [x3, #16] + stp x8, x9, [x3, #32] + stp x10, x11, [x3, #48] + stp x12, x13, [x3, #64] + stp x14, x15, [x3, #80] + stp x16, x17, [x3, #96] + stp x18, x19, [x3, #112] + stp x20, x21, [x3, #128] + stp x22, x23, [x3, #144] +.endm + +.macro restore_sysregs + // x2: base address for cpu context + // x3: tmp register + + add x3, x2, #CPU_SYSREG_OFFSET(MPIDR_EL1) + + ldp x4, x5, [x3] + ldp x6, x7, [x3, #16] + ldp x8, x9, [x3, #32] + ldp x10, x11, [x3, #48] + ldp x12, x13, [x3, #64] + ldp x14, x15, [x3, #80] + ldp x16, x17, [x3, #96] + ldp x18, x19, [x3, #112] + ldp x20, x21, [x3, #128] + ldp x22, x23, [x3, #144] + + msr vmpidr_el2, x4 + msr csselr_el1, x5 + msr sctlr_el1, x6 + msr actlr_el1, x7 + msr cpacr_el1, x8 + msr ttbr0_el1, x9 + msr ttbr1_el1, x10 + msr tcr_el1, x11 + msr esr_el1, x12 + msr afsr0_el1, x13 + msr afsr1_el1, x14 + msr far_el1, x15 + msr mair_el1, x16 + msr vbar_el1, x17 + msr contextidr_el1, x18 + msr tpidr_el0, x19 + msr tpidrro_el0, x20 + msr tpidr_el1, x21 + msr amair_el1, x22 + msr cntkctl_el1, x23 +.endm + +.macro activate_traps + ldr x2, [x0, #VCPU_IRQ_LINES] + ldr x1, [x0, #VCPU_HCR_EL2] + orr x2, x2, x1 + msr hcr_el2, x2 + + ldr x2, =(CPTR_EL2_TTA) + msr cptr_el2, x2 + + ldr x2, =(1 << 15) // Trap CP15 Cr=15 + msr hstr_el2, x2 + + mrs x2, mdcr_el2 + and x2, x2, #MDCR_EL2_HPMN_MASK + orr x2, x2, #(MDCR_EL2_TPM | MDCR_EL2_TPMCR) + msr mdcr_el2, x2 +.endm + +.macro deactivate_traps + mov x2, #HCR_RW + msr hcr_el2, x2 + msr cptr_el2, xzr + msr hstr_el2, xzr + + mrs x2, mdcr_el2 + and x2, x2, #MDCR_EL2_HPMN_MASK + msr mdcr_el2, x2 +.endm + +.macro activate_vm + ldr x1, [x0, #VCPU_KVM] + kern_hyp_va x1 + ldr x2, [x1, #KVM_VTTBR] + msr vttbr_el2, x2 +.endm + +.macro deactivate_vm + msr vttbr_el2, xzr +.endm + +__save_sysregs: + save_sysregs + ret + +__restore_sysregs: + restore_sysregs + ret + +__save_fpsimd: + save_fpsimd + ret + +__restore_fpsimd: + restore_fpsimd + ret + +/* + * u64 __kvm_vcpu_run(struct kvm_vcpu *vcpu); + * + * This is the world switch. The first half of the function + * deals with entering the guest, and anything from __kvm_vcpu_return + * to the end of the function deals with reentering the host. + * On the enter path, only x0 (vcpu pointer) must be preserved until + * the last moment. On the exit path, x0 (vcpu pointer) and x1 (exception + * code) must both be preserved until the epilogue. + * In both cases, x2 points to the CPU context we're saving/restoring from/to. + */ +ENTRY(__kvm_vcpu_run) + kern_hyp_va x0 + msr tpidr_el2, x0 // Save the vcpu register + + // Host context + ldr x2, [x0, #VCPU_HOST_CONTEXT] + kern_hyp_va x2 + + save_host_regs + bl __save_fpsimd + bl __save_sysregs + + activate_traps + activate_vm + + // Guest context + add x2, x0, #VCPU_CONTEXT + + bl __restore_sysregs + bl __restore_fpsimd + restore_guest_regs + + // That's it, no more messing around. + eret + +__kvm_vcpu_return: + // Assume x0 is the vcpu pointer, x1 the return code + // Guest's x0-x3 are on the stack + + // Guest context + add x2, x0, #VCPU_CONTEXT + + save_guest_regs + bl __save_fpsimd + bl __save_sysregs + + deactivate_traps + deactivate_vm + + // Host context + ldr x2, [x0, #VCPU_HOST_CONTEXT] + kern_hyp_va x2 + + bl __restore_sysregs + bl __restore_fpsimd + restore_host_regs + + mov x0, x1 + ret +END(__kvm_vcpu_run) + +// void __kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa); +ENTRY(__kvm_tlb_flush_vmid_ipa) + kern_hyp_va x0 + ldr x2, [x0, #KVM_VTTBR] + msr vttbr_el2, x2 + isb + + /* + * We could do so much better if we had the VA as well. + * Instead, we invalidate Stage-2 for this IPA, and the + * whole of Stage-1. Weep... + */ + tlbi ipas2e1is, x1 + dsb sy + tlbi vmalle1is + dsb sy + isb + + msr vttbr_el2, xzr + ret +ENDPROC(__kvm_tlb_flush_vmid_ipa) + +ENTRY(__kvm_flush_vm_context) + tlbi alle1is + ic ialluis + dsb sy + ret +ENDPROC(__kvm_flush_vm_context) + +__kvm_hyp_panic: + // Guess the context by looking at VTTBR: + // If zero, then we're already a host. + // Otherwise restore a minimal host context before panicing. + mrs x0, vttbr_el2 + cbz x0, 1f + + mrs x0, tpidr_el2 + + deactivate_traps + deactivate_vm + + ldr x2, [x0, #VCPU_HOST_CONTEXT] + kern_hyp_va x2 + + bl __restore_sysregs + +1: adr x0, __hyp_panic_str + adr x1, 2f + ldp x2, x3, [x1] + sub x0, x0, x2 + add x0, x0, x3 + mrs x1, spsr_el2 + mrs x2, elr_el2 + mrs x3, esr_el2 + mrs x4, far_el2 + mrs x5, hpfar_el2 + mrs x6, par_el1 + mrs x7, tpidr_el2 + + mov lr, #(PSR_F_BIT | PSR_I_BIT | PSR_A_BIT | PSR_D_BIT |\ + PSR_MODE_EL1h) + msr spsr_el2, lr + ldr lr, =panic + msr elr_el2, lr + eret + + .align 3 +2: .quad HYP_PAGE_OFFSET + .quad PAGE_OFFSET +ENDPROC(__kvm_hyp_panic) + +__hyp_panic_str: + .ascii "HYP panic:\nPS:%08x PC:%p ESR:%p\nFAR:%p HPFAR:%p PAR:%p\nVCPU:%p\n\0" + + .align 2 + +ENTRY(kvm_call_hyp) + hvc #0 + ret +ENDPROC(kvm_call_hyp) + +.macro invalid_vector label, target + .align 2 +\label: + b \target +ENDPROC(\label) +.endm + + /* None of these should ever happen */ + invalid_vector el2t_sync_invalid, __kvm_hyp_panic + invalid_vector el2t_irq_invalid, __kvm_hyp_panic + invalid_vector el2t_fiq_invalid, __kvm_hyp_panic + invalid_vector el2t_error_invalid, __kvm_hyp_panic + invalid_vector el2h_sync_invalid, __kvm_hyp_panic + invalid_vector el2h_irq_invalid, __kvm_hyp_panic + invalid_vector el2h_fiq_invalid, __kvm_hyp_panic + invalid_vector el2h_error_invalid, __kvm_hyp_panic + invalid_vector el1_sync_invalid, __kvm_hyp_panic + invalid_vector el1_irq_invalid, __kvm_hyp_panic + invalid_vector el1_fiq_invalid, __kvm_hyp_panic + invalid_vector el1_error_invalid, __kvm_hyp_panic + +el1_sync: // Guest trapped into EL2 + push x0, x1 + push x2, x3 + + mrs x1, esr_el2 + lsr x2, x1, #ESR_EL2_EC_SHIFT + + cmp x2, #ESR_EL2_EC_HVC64 + b.ne el1_trap + + mrs x3, vttbr_el2 // If vttbr is valid, the 64bit guest + cbnz x3, el1_trap // called HVC + + /* Here, we're pretty sure the host called HVC. */ + pop x2, x3 + pop x0, x1 + + push lr, xzr + + /* + * Compute the function address in EL2, and shuffle the parameters. + */ + kern_hyp_va x0 + mov lr, x0 + mov x0, x1 + mov x1, x2 + mov x2, x3 + blr lr + + pop lr, xzr + eret + +el1_trap: + /* + * x1: ESR + * x2: ESR_EC + */ + cmp x2, #ESR_EL2_EC_DABT + mov x0, #ESR_EL2_EC_IABT + ccmp x2, x0, #4, ne + b.ne 1f // Not an abort we care about + + /* This is an abort. Check for permission fault */ + and x2, x1, #ESR_EL2_FSC_TYPE + cmp x2, #FSC_PERM + b.ne 1f // Not a permission fault + + /* + * Check for Stage-1 page table walk, which is guaranteed + * to give a valid HPFAR_EL2. + */ + tbnz x1, #7, 1f // S1PTW is set + + /* + * Permission fault, HPFAR_EL2 is invalid. + * Resolve the IPA the hard way using the guest VA. + * Stage-1 translation already validated the memory access rights. + * As such, we can use the EL1 translation regime, and don't have + * to distinguish between EL0 and EL1 access. + */ + mrs x2, far_el2 + at s1e1r, x2 + isb + + /* Read result */ + mrs x3, par_el1 + tbnz x3, #0, 3f // Bail out if we failed the translation + ubfx x3, x3, #12, #36 // Extract IPA + lsl x3, x3, #4 // and present it like HPFAR + b 2f + +1: mrs x3, hpfar_el2 + mrs x2, far_el2 + +2: mrs x0, tpidr_el2 + str x1, [x0, #VCPU_ESR_EL2] + str x2, [x0, #VCPU_FAR_EL2] + str x3, [x0, #VCPU_HPFAR_EL2] + + mov x1, #ARM_EXCEPTION_TRAP + b __kvm_vcpu_return + + /* + * Translation failed. Just return to the guest and + * let it fault again. Another CPU is probably playing + * behind our back. + */ +3: pop x2, x3 + pop x0, x1 + + eret + +el1_irq: + push x0, x1 + push x2, x3 + mrs x0, tpidr_el2 + mov x1, #ARM_EXCEPTION_IRQ + b __kvm_vcpu_return + + .ltorg + + .align 11 + +ENTRY(__kvm_hyp_vector) + ventry el2t_sync_invalid // Synchronous EL2t + ventry el2t_irq_invalid // IRQ EL2t + ventry el2t_fiq_invalid // FIQ EL2t + ventry el2t_error_invalid // Error EL2t + + ventry el2h_sync_invalid // Synchronous EL2h + ventry el2h_irq_invalid // IRQ EL2h + ventry el2h_fiq_invalid // FIQ EL2h + ventry el2h_error_invalid // Error EL2h + + ventry el1_sync // Synchronous 64-bit EL1 + ventry el1_irq // IRQ 64-bit EL1 + ventry el1_fiq_invalid // FIQ 64-bit EL1 + ventry el1_error_invalid // Error 64-bit EL1 + + ventry el1_sync // Synchronous 32-bit EL1 + ventry el1_irq // IRQ 32-bit EL1 + ventry el1_fiq_invalid // FIQ 32-bit EL1 + ventry el1_error_invalid // Error 32-bit EL1 +ENDPROC(__kvm_hyp_vector) + +__kvm_hyp_code_end: + .globl __kvm_hyp_code_end + + .popsection -- cgit v1.2.3 From c4b1afd022e93eada6ee4b209be37101cd4b3494 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 10 Dec 2012 16:40:41 +0000 Subject: arm64: KVM: Exit handling Handle the exit of a VM, decoding the exit reason from HYP mode and calling the corresponding handler. Reviewed-by: Christopher Covington Reviewed-by: Catalin Marinas Signed-off-by: Marc Zyngier --- arch/arm64/kvm/handle_exit.c | 119 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 arch/arm64/kvm/handle_exit.c diff --git a/arch/arm64/kvm/handle_exit.c b/arch/arm64/kvm/handle_exit.c new file mode 100644 index 000000000000..c65d1154f969 --- /dev/null +++ b/arch/arm64/kvm/handle_exit.c @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2012,2013 - ARM Ltd + * Author: Marc Zyngier + * + * Derived from arch/arm/kvm/handle_exit.c: + * Copyright (C) 2012 - Virtual Open Systems and Columbia University + * Author: Christoffer Dall + * + * 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 . + */ + +#include +#include +#include +#include +#include + +typedef int (*exit_handle_fn)(struct kvm_vcpu *, struct kvm_run *); + +static int handle_hvc(struct kvm_vcpu *vcpu, struct kvm_run *run) +{ + /* + * Guest called HVC instruction: + * Let it know we don't want that by injecting an undefined exception. + */ + kvm_debug("hvc: %x (at %08lx)", kvm_vcpu_get_hsr(vcpu) & ((1 << 16) - 1), + *vcpu_pc(vcpu)); + kvm_debug(" HSR: %8x", kvm_vcpu_get_hsr(vcpu)); + kvm_inject_undefined(vcpu); + return 1; +} + +static int handle_smc(struct kvm_vcpu *vcpu, struct kvm_run *run) +{ + /* We don't support SMC; don't do that. */ + kvm_debug("smc: at %08lx", *vcpu_pc(vcpu)); + kvm_inject_undefined(vcpu); + return 1; +} + +/** + * kvm_handle_wfi - handle a wait-for-interrupts instruction executed by a guest + * @vcpu: the vcpu pointer + * + * Simply call kvm_vcpu_block(), which will halt execution of + * world-switches and schedule other host processes until there is an + * incoming IRQ or FIQ to the VM. + */ +static int kvm_handle_wfi(struct kvm_vcpu *vcpu, struct kvm_run *run) +{ + kvm_vcpu_block(vcpu); + return 1; +} + +static exit_handle_fn arm_exit_handlers[] = { + [ESR_EL2_EC_WFI] = kvm_handle_wfi, + [ESR_EL2_EC_HVC64] = handle_hvc, + [ESR_EL2_EC_SMC64] = handle_smc, + [ESR_EL2_EC_SYS64] = kvm_handle_sys_reg, + [ESR_EL2_EC_IABT] = kvm_handle_guest_abort, + [ESR_EL2_EC_DABT] = kvm_handle_guest_abort, +}; + +static exit_handle_fn kvm_get_exit_handler(struct kvm_vcpu *vcpu) +{ + u8 hsr_ec = kvm_vcpu_trap_get_class(vcpu); + + if (hsr_ec >= ARRAY_SIZE(arm_exit_handlers) || + !arm_exit_handlers[hsr_ec]) { + kvm_err("Unkown exception class: hsr: %#08x\n", + (unsigned int)kvm_vcpu_get_hsr(vcpu)); + BUG(); + } + + return arm_exit_handlers[hsr_ec]; +} + +/* + * Return > 0 to return to guest, < 0 on error, 0 (and set exit_reason) on + * proper exit to userspace. + */ +int handle_exit(struct kvm_vcpu *vcpu, struct kvm_run *run, + int exception_index) +{ + exit_handle_fn exit_handler; + + switch (exception_index) { + case ARM_EXCEPTION_IRQ: + return 1; + case ARM_EXCEPTION_TRAP: + /* + * See ARM ARM B1.14.1: "Hyp traps on instructions + * that fail their condition code check" + */ + if (!kvm_condition_valid(vcpu)) { + kvm_skip_instr(vcpu, kvm_vcpu_trap_il_is32bit(vcpu)); + return 1; + } + + exit_handler = kvm_get_exit_handler(vcpu); + + return exit_handler(vcpu, run); + default: + kvm_pr_unimpl("Unsupported exception type: %d", + exception_index); + run->exit_reason = KVM_EXIT_INTERNAL_ERROR; + return 0; + } +} -- cgit v1.2.3 From 1f17f3b6044d8a81a74dc6c962b3b38a7336106b Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 7 Dec 2012 17:54:54 +0000 Subject: arm64: KVM: Plug the VGIC Add support for the in-kernel GIC emulation. Reviewed-by: Christopher Covington Reviewed-by: Catalin Marinas Signed-off-by: Marc Zyngier --- arch/arm64/kvm/hyp.S | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/arch/arm64/kvm/hyp.S b/arch/arm64/kvm/hyp.S index 0b18c2e1e043..8dc27a367d77 100644 --- a/arch/arm64/kvm/hyp.S +++ b/arch/arm64/kvm/hyp.S @@ -306,6 +306,90 @@ __kvm_hyp_code_start: msr vttbr_el2, xzr .endm +/* + * Save the VGIC CPU state into memory + * x0: Register pointing to VCPU struct + * Do not corrupt x1!!! + */ +.macro save_vgic_state + /* Get VGIC VCTRL base into x2 */ + ldr x2, [x0, #VCPU_KVM] + kern_hyp_va x2 + ldr x2, [x2, #KVM_VGIC_VCTRL] + kern_hyp_va x2 + cbz x2, 2f // disabled + + /* Compute the address of struct vgic_cpu */ + add x3, x0, #VCPU_VGIC_CPU + + /* Save all interesting registers */ + ldr w4, [x2, #GICH_HCR] + ldr w5, [x2, #GICH_VMCR] + ldr w6, [x2, #GICH_MISR] + ldr w7, [x2, #GICH_EISR0] + ldr w8, [x2, #GICH_EISR1] + ldr w9, [x2, #GICH_ELRSR0] + ldr w10, [x2, #GICH_ELRSR1] + ldr w11, [x2, #GICH_APR] + + str w4, [x3, #VGIC_CPU_HCR] + str w5, [x3, #VGIC_CPU_VMCR] + str w6, [x3, #VGIC_CPU_MISR] + str w7, [x3, #VGIC_CPU_EISR] + str w8, [x3, #(VGIC_CPU_EISR + 4)] + str w9, [x3, #VGIC_CPU_ELRSR] + str w10, [x3, #(VGIC_CPU_ELRSR + 4)] + str w11, [x3, #VGIC_CPU_APR] + + /* Clear GICH_HCR */ + str wzr, [x2, #GICH_HCR] + + /* Save list registers */ + add x2, x2, #GICH_LR0 + ldr w4, [x3, #VGIC_CPU_NR_LR] + add x3, x3, #VGIC_CPU_LR +1: ldr w5, [x2], #4 + str w5, [x3], #4 + sub w4, w4, #1 + cbnz w4, 1b +2: +.endm + +/* + * Restore the VGIC CPU state from memory + * x0: Register pointing to VCPU struct + */ +.macro restore_vgic_state + /* Get VGIC VCTRL base into x2 */ + ldr x2, [x0, #VCPU_KVM] + kern_hyp_va x2 + ldr x2, [x2, #KVM_VGIC_VCTRL] + kern_hyp_va x2 + cbz x2, 2f // disabled + + /* Compute the address of struct vgic_cpu */ + add x3, x0, #VCPU_VGIC_CPU + + /* We only restore a minimal set of registers */ + ldr w4, [x3, #VGIC_CPU_HCR] + ldr w5, [x3, #VGIC_CPU_VMCR] + ldr w6, [x3, #VGIC_CPU_APR] + + str w4, [x2, #GICH_HCR] + str w5, [x2, #GICH_VMCR] + str w6, [x2, #GICH_APR] + + /* Restore list registers */ + add x2, x2, #GICH_LR0 + ldr w4, [x3, #VGIC_CPU_NR_LR] + add x3, x3, #VGIC_CPU_LR +1: ldr w5, [x3], #4 + str w5, [x2], #4 + sub w4, w4, #1 + cbnz w4, 1b +2: +.endm + __save_sysregs: save_sysregs ret @@ -348,6 +432,8 @@ ENTRY(__kvm_vcpu_run) activate_traps activate_vm + restore_vgic_state + // Guest context add x2, x0, #VCPU_CONTEXT @@ -369,6 +455,8 @@ __kvm_vcpu_return: bl __save_fpsimd bl __save_sysregs + save_vgic_state + deactivate_traps deactivate_vm -- cgit v1.2.3 From f61701e0a24a09aa4a44baf24e57dcc5e706afa8 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Thu, 30 May 2013 18:31:28 +0100 Subject: ARM: KVM: timer: allow DT matching for ARMv8 cores ARMv8 cores have the exact same timer as ARMv7 cores. Make sure the KVM timer code can match it in the device tree. Signed-off-by: Marc Zyngier --- arch/arm/kvm/arch_timer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/kvm/arch_timer.c b/arch/arm/kvm/arch_timer.c index c55b6089e923..49a7516d81c7 100644 --- a/arch/arm/kvm/arch_timer.c +++ b/arch/arm/kvm/arch_timer.c @@ -195,6 +195,7 @@ static struct notifier_block kvm_timer_cpu_nb = { static const struct of_device_id arch_timer_of_match[] = { { .compatible = "arm,armv7-timer", }, + { .compatible = "arm,armv8-timer", }, {}, }; -- cgit v1.2.3 From 003300de6c3e51934fb52eb2677f6f4fb4996cbd Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 7 Dec 2012 17:52:03 +0000 Subject: arm64: KVM: Plug the arch timer Add support for the in-kernel timer emulation. Reviewed-by: Christopher Covington Reviewed-by: Catalin Marinas Signed-off-by: Marc Zyngier --- arch/arm64/kvm/hyp.S | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++ arch/arm64/kvm/reset.c | 12 +++++++++++ 2 files changed, 68 insertions(+) diff --git a/arch/arm64/kvm/hyp.S b/arch/arm64/kvm/hyp.S index 8dc27a367d77..8b510835b440 100644 --- a/arch/arm64/kvm/hyp.S +++ b/arch/arm64/kvm/hyp.S @@ -390,6 +390,60 @@ __kvm_hyp_code_start: 2: .endm +.macro save_timer_state + // x0: vcpu pointer + ldr x2, [x0, #VCPU_KVM] + kern_hyp_va x2 + ldr w3, [x2, #KVM_TIMER_ENABLED] + cbz w3, 1f + + mrs x3, cntv_ctl_el0 + and x3, x3, #3 + str w3, [x0, #VCPU_TIMER_CNTV_CTL] + bic x3, x3, #1 // Clear Enable + msr cntv_ctl_el0, x3 + + isb + + mrs x3, cntv_cval_el0 + str x3, [x0, #VCPU_TIMER_CNTV_CVAL] + +1: + // Allow physical timer/counter access for the host + mrs x2, cnthctl_el2 + orr x2, x2, #3 + msr cnthctl_el2, x2 + + // Clear cntvoff for the host + msr cntvoff_el2, xzr +.endm + +.macro restore_timer_state + // x0: vcpu pointer + // Disallow physical timer access for the guest + // Physical counter access is allowed + mrs x2, cnthctl_el2 + orr x2, x2, #1 + bic x2, x2, #2 + msr cnthctl_el2, x2 + + ldr x2, [x0, #VCPU_KVM] + kern_hyp_va x2 + ldr w3, [x2, #KVM_TIMER_ENABLED] + cbz w3, 1f + + ldr x3, [x2, #KVM_TIMER_CNTVOFF] + msr cntvoff_el2, x3 + ldr x2, [x0, #VCPU_TIMER_CNTV_CVAL] + msr cntv_cval_el0, x2 + isb + + ldr w2, [x0, #VCPU_TIMER_CNTV_CTL] + and x2, x2, #3 + msr cntv_ctl_el0, x2 +1: +.endm + __save_sysregs: save_sysregs ret @@ -433,6 +487,7 @@ ENTRY(__kvm_vcpu_run) activate_vm restore_vgic_state + restore_timer_state // Guest context add x2, x0, #VCPU_CONTEXT @@ -455,6 +510,7 @@ __kvm_vcpu_return: bl __save_fpsimd bl __save_sysregs + save_timer_state save_vgic_state deactivate_traps diff --git a/arch/arm64/kvm/reset.c b/arch/arm64/kvm/reset.c index f6536a06231a..766150ac76ed 100644 --- a/arch/arm64/kvm/reset.c +++ b/arch/arm64/kvm/reset.c @@ -23,6 +23,8 @@ #include #include +#include + #include #include #include @@ -36,6 +38,11 @@ static const struct kvm_regs default_regs_reset = { PSR_F_BIT | PSR_D_BIT), }; +static const struct kvm_irq_level default_vtimer_irq = { + .irq = 27, + .level = 1, +}; + int kvm_arch_dev_ioctl_check_extension(long ext) { int r; @@ -58,11 +65,13 @@ int kvm_arch_dev_ioctl_check_extension(long ext) */ int kvm_reset_vcpu(struct kvm_vcpu *vcpu) { + const struct kvm_irq_level *cpu_vtimer_irq; const struct kvm_regs *cpu_reset; switch (vcpu->arch.target) { default: cpu_reset = &default_regs_reset; + cpu_vtimer_irq = &default_vtimer_irq; break; } @@ -72,5 +81,8 @@ int kvm_reset_vcpu(struct kvm_vcpu *vcpu) /* Reset system registers */ kvm_reset_sys_regs(vcpu); + /* Reset timer */ + kvm_timer_vcpu_reset(vcpu, cpu_vtimer_irq); + return 0; } -- cgit v1.2.3 From dcd2e40c1e1cce302498d16d095b0f8a30326f74 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 12 Dec 2012 18:52:05 +0000 Subject: arm64: KVM: PSCI implementation Wire the PSCI backend into the exit handling code. Reviewed-by: Christopher Covington Reviewed-by: Catalin Marinas Signed-off-by: Marc Zyngier --- arch/arm64/include/asm/kvm_host.h | 2 +- arch/arm64/include/asm/kvm_psci.h | 23 +++++++++++++++++++++++ arch/arm64/include/uapi/asm/kvm.h | 16 ++++++++++++++++ arch/arm64/kvm/handle_exit.c | 16 +++++++--------- 4 files changed, 47 insertions(+), 10 deletions(-) create mode 100644 arch/arm64/include/asm/kvm_psci.h diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index 2500eb6a4d2a..2fdeb326c3ee 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -34,7 +34,7 @@ #include #include -#define KVM_VCPU_MAX_FEATURES 0 +#define KVM_VCPU_MAX_FEATURES 1 /* We don't currently support large pages. */ #define KVM_HPAGE_GFN_SHIFT(x) 0 diff --git a/arch/arm64/include/asm/kvm_psci.h b/arch/arm64/include/asm/kvm_psci.h new file mode 100644 index 000000000000..e301a4816355 --- /dev/null +++ b/arch/arm64/include/asm/kvm_psci.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2012,2013 - ARM Ltd + * Author: Marc Zyngier + * + * 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 . + */ + +#ifndef __ARM64_KVM_PSCI_H__ +#define __ARM64_KVM_PSCI_H__ + +bool kvm_psci_call(struct kvm_vcpu *vcpu); + +#endif /* __ARM64_KVM_PSCI_H__ */ diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h index ebac919dc0ca..fb60f9037057 100644 --- a/arch/arm64/include/uapi/asm/kvm.h +++ b/arch/arm64/include/uapi/asm/kvm.h @@ -69,6 +69,8 @@ struct kvm_regs { #define KVM_VGIC_V2_DIST_SIZE 0x1000 #define KVM_VGIC_V2_CPU_SIZE 0x2000 +#define KVM_ARM_VCPU_POWER_OFF 0 /* CPU is started in OFF state */ + struct kvm_vcpu_init { __u32 target; __u32 features[7]; @@ -141,6 +143,20 @@ struct kvm_arch_memory_slot { /* Highest supported SPI, from VGIC_NR_IRQS */ #define KVM_ARM_IRQ_GIC_MAX 127 +/* PSCI interface */ +#define KVM_PSCI_FN_BASE 0x95c1ba5e +#define KVM_PSCI_FN(n) (KVM_PSCI_FN_BASE + (n)) + +#define KVM_PSCI_FN_CPU_SUSPEND KVM_PSCI_FN(0) +#define KVM_PSCI_FN_CPU_OFF KVM_PSCI_FN(1) +#define KVM_PSCI_FN_CPU_ON KVM_PSCI_FN(2) +#define KVM_PSCI_FN_MIGRATE KVM_PSCI_FN(3) + +#define KVM_PSCI_RET_SUCCESS 0 +#define KVM_PSCI_RET_NI ((unsigned long)-1) +#define KVM_PSCI_RET_INVAL ((unsigned long)-2) +#define KVM_PSCI_RET_DENIED ((unsigned long)-3) + #endif #endif /* __ARM_KVM_H__ */ diff --git a/arch/arm64/kvm/handle_exit.c b/arch/arm64/kvm/handle_exit.c index c65d1154f969..4766b7f3515e 100644 --- a/arch/arm64/kvm/handle_exit.c +++ b/arch/arm64/kvm/handle_exit.c @@ -24,26 +24,24 @@ #include #include #include +#include typedef int (*exit_handle_fn)(struct kvm_vcpu *, struct kvm_run *); static int handle_hvc(struct kvm_vcpu *vcpu, struct kvm_run *run) { - /* - * Guest called HVC instruction: - * Let it know we don't want that by injecting an undefined exception. - */ - kvm_debug("hvc: %x (at %08lx)", kvm_vcpu_get_hsr(vcpu) & ((1 << 16) - 1), - *vcpu_pc(vcpu)); - kvm_debug(" HSR: %8x", kvm_vcpu_get_hsr(vcpu)); + if (kvm_psci_call(vcpu)) + return 1; + kvm_inject_undefined(vcpu); return 1; } static int handle_smc(struct kvm_vcpu *vcpu, struct kvm_run *run) { - /* We don't support SMC; don't do that. */ - kvm_debug("smc: at %08lx", *vcpu_pc(vcpu)); + if (kvm_psci_call(vcpu)) + return 1; + kvm_inject_undefined(vcpu); return 1; } -- cgit v1.2.3 From 6211753fdfd05af9e08f54c8d0ba3ee516034878 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Mon, 10 Dec 2012 16:41:44 +0000 Subject: arm64: KVM: Build system integration Only the Makefile is plugged in. The Kconfig stuff is in a separate patch to allow for an easier merge process. Reviewed-by: Christopher Covington Signed-off-by: Marc Zyngier --- arch/arm64/Makefile | 1 + arch/arm64/kvm/Makefile | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 arch/arm64/kvm/Makefile diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile index c95c5cb212fd..d0ce30a275e0 100644 --- a/arch/arm64/Makefile +++ b/arch/arm64/Makefile @@ -37,6 +37,7 @@ TEXT_OFFSET := 0x00080000 export TEXT_OFFSET GZFLAGS core-y += arch/arm64/kernel/ arch/arm64/mm/ +core-$(CONFIG_KVM) += arch/arm64/kvm/ libs-y := arch/arm64/lib/ $(libs-y) libs-y += $(LIBGCC) diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile new file mode 100644 index 000000000000..dca110556683 --- /dev/null +++ b/arch/arm64/kvm/Makefile @@ -0,0 +1,23 @@ +# +# Makefile for Kernel-based Virtual Machine module +# + +ccflags-y += -Ivirt/kvm -Iarch/arm64/kvm +CFLAGS_arm.o := -I. +CFLAGS_mmu.o := -I. + +KVM=../../../virt/kvm +ARM=../../../arch/arm/kvm + +obj-$(CONFIG_KVM_ARM_HOST) += kvm.o + +kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o +kvm-$(CONFIG_KVM_ARM_HOST) += $(ARM)/arm.o $(ARM)/mmu.o $(ARM)/mmio.o +kvm-$(CONFIG_KVM_ARM_HOST) += $(ARM)/psci.o $(ARM)/perf.o + +kvm-$(CONFIG_KVM_ARM_HOST) += inject_fault.o +kvm-$(CONFIG_KVM_ARM_HOST) += hyp.o hyp-init.o handle_exit.o +kvm-$(CONFIG_KVM_ARM_HOST) += guest.o reset.o sys_regs.o sys_regs_generic_v8.o + +kvm-$(CONFIG_KVM_ARM_VGIC) += $(KVM)/arm/vgic.o +kvm-$(CONFIG_KVM_ARM_TIMER) += $(KVM)/arm/arch_timer.o -- cgit v1.2.3 From 40033a614ea3db196d57c477ca328f44eb1e4df0 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 6 Feb 2013 19:17:50 +0000 Subject: arm64: KVM: define 32bit specific registers Define the 32bit specific registers (SPSRs, cp15...). Most CPU registers are directly mapped to a 64bit register (r0->x0...). Only the SPSRs have separate registers. cp15 registers are also mapped into their 64bit counterpart in most cases. Reviewed-by: Christopher Covington Reviewed-by: Catalin Marinas Signed-off-by: Marc Zyngier --- arch/arm64/include/asm/kvm_asm.h | 38 +++++++++++++++++++++++++++++++++++++- arch/arm64/include/asm/kvm_host.h | 5 ++++- arch/arm64/include/uapi/asm/kvm.h | 7 ++++++- 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h index 591ac219964a..c92de4163eba 100644 --- a/arch/arm64/include/asm/kvm_asm.h +++ b/arch/arm64/include/asm/kvm_asm.h @@ -42,7 +42,43 @@ #define TPIDR_EL1 18 /* Thread ID, Privileged */ #define AMAIR_EL1 19 /* Aux Memory Attribute Indirection Register */ #define CNTKCTL_EL1 20 /* Timer Control Register (EL1) */ -#define NR_SYS_REGS 21 +/* 32bit specific registers. Keep them at the end of the range */ +#define DACR32_EL2 21 /* Domain Access Control Register */ +#define IFSR32_EL2 22 /* Instruction Fault Status Register */ +#define FPEXC32_EL2 23 /* Floating-Point Exception Control Register */ +#define DBGVCR32_EL2 24 /* Debug Vector Catch Register */ +#define TEECR32_EL1 25 /* ThumbEE Configuration Register */ +#define TEEHBR32_EL1 26 /* ThumbEE Handler Base Register */ +#define NR_SYS_REGS 27 + +/* 32bit mapping */ +#define c0_MPIDR (MPIDR_EL1 * 2) /* MultiProcessor ID Register */ +#define c0_CSSELR (CSSELR_EL1 * 2)/* Cache Size Selection Register */ +#define c1_SCTLR (SCTLR_EL1 * 2) /* System Control Register */ +#define c1_ACTLR (ACTLR_EL1 * 2) /* Auxiliary Control Register */ +#define c1_CPACR (CPACR_EL1 * 2) /* Coprocessor Access Control */ +#define c2_TTBR0 (TTBR0_EL1 * 2) /* Translation Table Base Register 0 */ +#define c2_TTBR0_high (c2_TTBR0 + 1) /* TTBR0 top 32 bits */ +#define c2_TTBR1 (TTBR1_EL1 * 2) /* Translation Table Base Register 1 */ +#define c2_TTBR1_high (c2_TTBR1 + 1) /* TTBR1 top 32 bits */ +#define c2_TTBCR (TCR_EL1 * 2) /* Translation Table Base Control R. */ +#define c3_DACR (DACR32_EL2 * 2)/* Domain Access Control Register */ +#define c5_DFSR (ESR_EL1 * 2) /* Data Fault Status Register */ +#define c5_IFSR (IFSR32_EL2 * 2)/* Instruction Fault Status Register */ +#define c5_ADFSR (AFSR0_EL1 * 2) /* Auxiliary Data Fault Status R */ +#define c5_AIFSR (AFSR1_EL1 * 2) /* Auxiliary Instr Fault Status R */ +#define c6_DFAR (FAR_EL1 * 2) /* Data Fault Address Register */ +#define c6_IFAR (c6_DFAR + 1) /* Instruction Fault Address Register */ +#define c10_PRRR (MAIR_EL1 * 2) /* Primary Region Remap Register */ +#define c10_NMRR (c10_PRRR + 1) /* Normal Memory Remap Register */ +#define c12_VBAR (VBAR_EL1 * 2) /* Vector Base Address Register */ +#define c13_CID (CONTEXTIDR_EL1 * 2) /* Context ID Register */ +#define c13_TID_URW (TPIDR_EL0 * 2) /* Thread ID, User R/W */ +#define c13_TID_URO (TPIDRRO_EL0 * 2)/* Thread ID, User R/O */ +#define c13_TID_PRIV (TPIDR_EL1 * 2) /* Thread ID, Privileged */ +#define c10_AMAIR (AMAIR_EL1 * 2) /* Aux Memory Attr Indirection Reg */ +#define c14_CNTKCTL (CNTKCTL_EL1 * 2) /* Timer Control Register (PL1) */ +#define NR_CP15_REGS (NR_SYS_REGS * 2) #define ARM_EXCEPTION_IRQ 0 #define ARM_EXCEPTION_TRAP 1 diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index 2fdeb326c3ee..3f5830b3ca3f 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -84,7 +84,10 @@ struct kvm_vcpu_fault_info { struct kvm_cpu_context { struct kvm_regs gp_regs; - u64 sys_regs[NR_SYS_REGS]; + union { + u64 sys_regs[NR_SYS_REGS]; + u32 cp15[NR_CP15_REGS]; + }; }; typedef struct kvm_cpu_context kvm_cpu_context_t; diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h index fb60f9037057..5b1110c49df5 100644 --- a/arch/arm64/include/uapi/asm/kvm.h +++ b/arch/arm64/include/uapi/asm/kvm.h @@ -23,7 +23,12 @@ #define __ARM_KVM_H__ #define KVM_SPSR_EL1 0 -#define KVM_NR_SPSR 1 +#define KVM_SPSR_SVC KVM_SPSR_EL1 +#define KVM_SPSR_ABT 1 +#define KVM_SPSR_UND 2 +#define KVM_SPSR_IRQ 3 +#define KVM_SPSR_FIQ 4 +#define KVM_NR_SPSR 5 #ifndef __ASSEMBLY__ #include -- cgit v1.2.3 From b547631fc64e249a3c507e6ce854642507fa7c1c Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 6 Feb 2013 19:40:29 +0000 Subject: arm64: KVM: 32bit GP register access Allow access to the 32bit register file through the usual API. Reviewed-by: Christopher Covington Reviewed-by: Catalin Marinas Signed-off-by: Marc Zyngier --- arch/arm64/include/asm/kvm_emulate.h | 15 +++- arch/arm64/kvm/Makefile | 2 +- arch/arm64/kvm/regmap.c | 168 +++++++++++++++++++++++++++++++++++ 3 files changed, 183 insertions(+), 2 deletions(-) create mode 100644 arch/arm64/kvm/regmap.c diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h index 6c1725e93b0b..20a1a3931d8d 100644 --- a/arch/arm64/include/asm/kvm_emulate.h +++ b/arch/arm64/include/asm/kvm_emulate.h @@ -28,6 +28,9 @@ #include #include +unsigned long *vcpu_reg32(const struct kvm_vcpu *vcpu, u8 reg_num); +unsigned long *vcpu_spsr32(const struct kvm_vcpu *vcpu); + void kvm_inject_undefined(struct kvm_vcpu *vcpu); void kvm_inject_dabt(struct kvm_vcpu *vcpu, unsigned long addr); void kvm_inject_pabt(struct kvm_vcpu *vcpu, unsigned long addr); @@ -49,7 +52,7 @@ static inline unsigned long *vcpu_cpsr(const struct kvm_vcpu *vcpu) static inline bool vcpu_mode_is_32bit(const struct kvm_vcpu *vcpu) { - return false; /* 32bit? Bahhh... */ + return !!(*vcpu_cpsr(vcpu) & PSR_MODE32_BIT); } static inline bool kvm_condition_valid(const struct kvm_vcpu *vcpu) @@ -64,16 +67,23 @@ static inline void kvm_skip_instr(struct kvm_vcpu *vcpu, bool is_wide_instr) static inline void vcpu_set_thumb(struct kvm_vcpu *vcpu) { + *vcpu_cpsr(vcpu) |= COMPAT_PSR_T_BIT; } static inline unsigned long *vcpu_reg(const struct kvm_vcpu *vcpu, u8 reg_num) { + if (vcpu_mode_is_32bit(vcpu)) + return vcpu_reg32(vcpu, reg_num); + return (unsigned long *)&vcpu_gp_regs(vcpu)->regs.regs[reg_num]; } /* Get vcpu SPSR for current mode */ static inline unsigned long *vcpu_spsr(const struct kvm_vcpu *vcpu) { + if (vcpu_mode_is_32bit(vcpu)) + return vcpu_spsr32(vcpu); + return (unsigned long *)&vcpu_gp_regs(vcpu)->spsr[KVM_SPSR_EL1]; } @@ -81,6 +91,9 @@ static inline bool vcpu_mode_priv(const struct kvm_vcpu *vcpu) { u32 mode = *vcpu_cpsr(vcpu) & PSR_MODE_MASK; + if (vcpu_mode_is_32bit(vcpu)) + return mode > COMPAT_PSR_MODE_USR; + return mode != PSR_MODE_EL0t; } diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile index dca110556683..a2169ec8d93b 100644 --- a/arch/arm64/kvm/Makefile +++ b/arch/arm64/kvm/Makefile @@ -15,7 +15,7 @@ kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o kvm-$(CONFIG_KVM_ARM_HOST) += $(ARM)/arm.o $(ARM)/mmu.o $(ARM)/mmio.o kvm-$(CONFIG_KVM_ARM_HOST) += $(ARM)/psci.o $(ARM)/perf.o -kvm-$(CONFIG_KVM_ARM_HOST) += inject_fault.o +kvm-$(CONFIG_KVM_ARM_HOST) += inject_fault.o regmap.o kvm-$(CONFIG_KVM_ARM_HOST) += hyp.o hyp-init.o handle_exit.o kvm-$(CONFIG_KVM_ARM_HOST) += guest.o reset.o sys_regs.o sys_regs_generic_v8.o diff --git a/arch/arm64/kvm/regmap.c b/arch/arm64/kvm/regmap.c new file mode 100644 index 000000000000..bbc6ae32e4af --- /dev/null +++ b/arch/arm64/kvm/regmap.c @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2012,2013 - ARM Ltd + * Author: Marc Zyngier + * + * Derived from arch/arm/kvm/emulate.c: + * Copyright (C) 2012 - Virtual Open Systems and Columbia University + * Author: Christoffer Dall + * + * 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 . + */ + +#include +#include +#include +#include + +#define VCPU_NR_MODES 6 +#define REG_OFFSET(_reg) \ + (offsetof(struct user_pt_regs, _reg) / sizeof(unsigned long)) + +#define USR_REG_OFFSET(R) REG_OFFSET(compat_usr(R)) + +static const unsigned long vcpu_reg_offsets[VCPU_NR_MODES][16] = { + /* USR Registers */ + { + USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2), + USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5), + USR_REG_OFFSET(6), USR_REG_OFFSET(7), USR_REG_OFFSET(8), + USR_REG_OFFSET(9), USR_REG_OFFSET(10), USR_REG_OFFSET(11), + USR_REG_OFFSET(12), USR_REG_OFFSET(13), USR_REG_OFFSET(14), + REG_OFFSET(pc) + }, + + /* FIQ Registers */ + { + USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2), + USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5), + USR_REG_OFFSET(6), USR_REG_OFFSET(7), + REG_OFFSET(compat_r8_fiq), /* r8 */ + REG_OFFSET(compat_r9_fiq), /* r9 */ + REG_OFFSET(compat_r10_fiq), /* r10 */ + REG_OFFSET(compat_r11_fiq), /* r11 */ + REG_OFFSET(compat_r12_fiq), /* r12 */ + REG_OFFSET(compat_sp_fiq), /* r13 */ + REG_OFFSET(compat_lr_fiq), /* r14 */ + REG_OFFSET(pc) + }, + + /* IRQ Registers */ + { + USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2), + USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5), + USR_REG_OFFSET(6), USR_REG_OFFSET(7), USR_REG_OFFSET(8), + USR_REG_OFFSET(9), USR_REG_OFFSET(10), USR_REG_OFFSET(11), + USR_REG_OFFSET(12), + REG_OFFSET(compat_sp_irq), /* r13 */ + REG_OFFSET(compat_lr_irq), /* r14 */ + REG_OFFSET(pc) + }, + + /* SVC Registers */ + { + USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2), + USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5), + USR_REG_OFFSET(6), USR_REG_OFFSET(7), USR_REG_OFFSET(8), + USR_REG_OFFSET(9), USR_REG_OFFSET(10), USR_REG_OFFSET(11), + USR_REG_OFFSET(12), + REG_OFFSET(compat_sp_svc), /* r13 */ + REG_OFFSET(compat_lr_svc), /* r14 */ + REG_OFFSET(pc) + }, + + /* ABT Registers */ + { + USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2), + USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5), + USR_REG_OFFSET(6), USR_REG_OFFSET(7), USR_REG_OFFSET(8), + USR_REG_OFFSET(9), USR_REG_OFFSET(10), USR_REG_OFFSET(11), + USR_REG_OFFSET(12), + REG_OFFSET(compat_sp_abt), /* r13 */ + REG_OFFSET(compat_lr_abt), /* r14 */ + REG_OFFSET(pc) + }, + + /* UND Registers */ + { + USR_REG_OFFSET(0), USR_REG_OFFSET(1), USR_REG_OFFSET(2), + USR_REG_OFFSET(3), USR_REG_OFFSET(4), USR_REG_OFFSET(5), + USR_REG_OFFSET(6), USR_REG_OFFSET(7), USR_REG_OFFSET(8), + USR_REG_OFFSET(9), USR_REG_OFFSET(10), USR_REG_OFFSET(11), + USR_REG_OFFSET(12), + REG_OFFSET(compat_sp_und), /* r13 */ + REG_OFFSET(compat_lr_und), /* r14 */ + REG_OFFSET(pc) + }, +}; + +/* + * Return a pointer to the register number valid in the current mode of + * the virtual CPU. + */ +unsigned long *vcpu_reg32(const struct kvm_vcpu *vcpu, u8 reg_num) +{ + unsigned long *reg_array = (unsigned long *)&vcpu->arch.ctxt.gp_regs.regs; + unsigned long mode = *vcpu_cpsr(vcpu) & COMPAT_PSR_MODE_MASK; + + switch (mode) { + case COMPAT_PSR_MODE_USR ... COMPAT_PSR_MODE_SVC: + mode &= ~PSR_MODE32_BIT; /* 0 ... 3 */ + break; + + case COMPAT_PSR_MODE_ABT: + mode = 4; + break; + + case COMPAT_PSR_MODE_UND: + mode = 5; + break; + + case COMPAT_PSR_MODE_SYS: + mode = 0; /* SYS maps to USR */ + break; + + default: + BUG(); + } + + return reg_array + vcpu_reg_offsets[mode][reg_num]; +} + +/* + * Return the SPSR for the current mode of the virtual CPU. + */ +unsigned long *vcpu_spsr32(const struct kvm_vcpu *vcpu) +{ + unsigned long mode = *vcpu_cpsr(vcpu) & COMPAT_PSR_MODE_MASK; + switch (mode) { + case COMPAT_PSR_MODE_SVC: + mode = KVM_SPSR_SVC; + break; + case COMPAT_PSR_MODE_ABT: + mode = KVM_SPSR_ABT; + break; + case COMPAT_PSR_MODE_UND: + mode = KVM_SPSR_UND; + break; + case COMPAT_PSR_MODE_IRQ: + mode = KVM_SPSR_IRQ; + break; + case COMPAT_PSR_MODE_FIQ: + mode = KVM_SPSR_FIQ; + break; + default: + BUG(); + } + + return (unsigned long *)&vcpu_gp_regs(vcpu)->spsr[mode]; +} -- cgit v1.2.3 From 27b190bd9fbfee34536cb858f0b5924d294aac38 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 6 Feb 2013 19:54:04 +0000 Subject: arm64: KVM: 32bit conditional execution emulation As conditional instructions can trap on AArch32, add the thinest possible emulation layer to keep 32bit guests happy. Reviewed-by: Christopher Covington Reviewed-by: Catalin Marinas Signed-off-by: Marc Zyngier --- arch/arm64/include/asm/kvm_emulate.h | 13 ++- arch/arm64/kvm/Makefile | 2 +- arch/arm64/kvm/emulate.c | 158 +++++++++++++++++++++++++++++++++++ 3 files changed, 170 insertions(+), 3 deletions(-) create mode 100644 arch/arm64/kvm/emulate.c diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h index 20a1a3931d8d..eec073875218 100644 --- a/arch/arm64/include/asm/kvm_emulate.h +++ b/arch/arm64/include/asm/kvm_emulate.h @@ -31,6 +31,9 @@ unsigned long *vcpu_reg32(const struct kvm_vcpu *vcpu, u8 reg_num); unsigned long *vcpu_spsr32(const struct kvm_vcpu *vcpu); +bool kvm_condition_valid32(const struct kvm_vcpu *vcpu); +void kvm_skip_instr32(struct kvm_vcpu *vcpu, bool is_wide_instr); + void kvm_inject_undefined(struct kvm_vcpu *vcpu); void kvm_inject_dabt(struct kvm_vcpu *vcpu, unsigned long addr); void kvm_inject_pabt(struct kvm_vcpu *vcpu, unsigned long addr); @@ -57,12 +60,18 @@ static inline bool vcpu_mode_is_32bit(const struct kvm_vcpu *vcpu) static inline bool kvm_condition_valid(const struct kvm_vcpu *vcpu) { - return true; /* No conditionals on arm64 */ + if (vcpu_mode_is_32bit(vcpu)) + return kvm_condition_valid32(vcpu); + + return true; } static inline void kvm_skip_instr(struct kvm_vcpu *vcpu, bool is_wide_instr) { - *vcpu_pc(vcpu) += 4; + if (vcpu_mode_is_32bit(vcpu)) + kvm_skip_instr32(vcpu, is_wide_instr); + else + *vcpu_pc(vcpu) += 4; } static inline void vcpu_set_thumb(struct kvm_vcpu *vcpu) diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile index a2169ec8d93b..72a9fd583ad3 100644 --- a/arch/arm64/kvm/Makefile +++ b/arch/arm64/kvm/Makefile @@ -15,7 +15,7 @@ kvm-$(CONFIG_KVM_ARM_HOST) += $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o kvm-$(CONFIG_KVM_ARM_HOST) += $(ARM)/arm.o $(ARM)/mmu.o $(ARM)/mmio.o kvm-$(CONFIG_KVM_ARM_HOST) += $(ARM)/psci.o $(ARM)/perf.o -kvm-$(CONFIG_KVM_ARM_HOST) += inject_fault.o regmap.o +kvm-$(CONFIG_KVM_ARM_HOST) += emulate.o inject_fault.o regmap.o kvm-$(CONFIG_KVM_ARM_HOST) += hyp.o hyp-init.o handle_exit.o kvm-$(CONFIG_KVM_ARM_HOST) += guest.o reset.o sys_regs.o sys_regs_generic_v8.o diff --git a/arch/arm64/kvm/emulate.c b/arch/arm64/kvm/emulate.c new file mode 100644 index 000000000000..124418d17049 --- /dev/null +++ b/arch/arm64/kvm/emulate.c @@ -0,0 +1,158 @@ +/* + * (not much of an) Emulation layer for 32bit guests. + * + * Copyright (C) 2012,2013 - ARM Ltd + * Author: Marc Zyngier + * + * based on arch/arm/kvm/emulate.c + * Copyright (C) 2012 - Virtual Open Systems and Columbia University + * Author: Christoffer Dall + * + * 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 . + */ + +#include +#include + +/* + * stolen from arch/arm/kernel/opcodes.c + * + * condition code lookup table + * index into the table is test code: EQ, NE, ... LT, GT, AL, NV + * + * bit position in short is condition code: NZCV + */ +static const unsigned short cc_map[16] = { + 0xF0F0, /* EQ == Z set */ + 0x0F0F, /* NE */ + 0xCCCC, /* CS == C set */ + 0x3333, /* CC */ + 0xFF00, /* MI == N set */ + 0x00FF, /* PL */ + 0xAAAA, /* VS == V set */ + 0x5555, /* VC */ + 0x0C0C, /* HI == C set && Z clear */ + 0xF3F3, /* LS == C clear || Z set */ + 0xAA55, /* GE == (N==V) */ + 0x55AA, /* LT == (N!=V) */ + 0x0A05, /* GT == (!Z && (N==V)) */ + 0xF5FA, /* LE == (Z || (N!=V)) */ + 0xFFFF, /* AL always */ + 0 /* NV */ +}; + +static int kvm_vcpu_get_condition(const struct kvm_vcpu *vcpu) +{ + u32 esr = kvm_vcpu_get_hsr(vcpu); + + if (esr & ESR_EL2_CV) + return (esr & ESR_EL2_COND) >> ESR_EL2_COND_SHIFT; + + return -1; +} + +/* + * Check if a trapped instruction should have been executed or not. + */ +bool kvm_condition_valid32(const struct kvm_vcpu *vcpu) +{ + unsigned long cpsr; + u32 cpsr_cond; + int cond; + + /* Top two bits non-zero? Unconditional. */ + if (kvm_vcpu_get_hsr(vcpu) >> 30) + return true; + + /* Is condition field valid? */ + cond = kvm_vcpu_get_condition(vcpu); + if (cond == 0xE) + return true; + + cpsr = *vcpu_cpsr(vcpu); + + if (cond < 0) { + /* This can happen in Thumb mode: examine IT state. */ + unsigned long it; + + it = ((cpsr >> 8) & 0xFC) | ((cpsr >> 25) & 0x3); + + /* it == 0 => unconditional. */ + if (it == 0) + return true; + + /* The cond for this insn works out as the top 4 bits. */ + cond = (it >> 4); + } + + cpsr_cond = cpsr >> 28; + + if (!((cc_map[cond] >> cpsr_cond) & 1)) + return false; + + return true; +} + +/** + * adjust_itstate - adjust ITSTATE when emulating instructions in IT-block + * @vcpu: The VCPU pointer + * + * When exceptions occur while instructions are executed in Thumb IF-THEN + * blocks, the ITSTATE field of the CPSR is not advanced (updated), so we have + * to do this little bit of work manually. The fields map like this: + * + * IT[7:0] -> CPSR[26:25],CPSR[15:10] + */ +static void kvm_adjust_itstate(struct kvm_vcpu *vcpu) +{ + unsigned long itbits, cond; + unsigned long cpsr = *vcpu_cpsr(vcpu); + bool is_arm = !(cpsr & COMPAT_PSR_T_BIT); + + BUG_ON(is_arm && (cpsr & COMPAT_PSR_IT_MASK)); + + if (!(cpsr & COMPAT_PSR_IT_MASK)) + return; + + cond = (cpsr & 0xe000) >> 13; + itbits = (cpsr & 0x1c00) >> (10 - 2); + itbits |= (cpsr & (0x3 << 25)) >> 25; + + /* Perform ITAdvance (see page A2-52 in ARM DDI 0406C) */ + if ((itbits & 0x7) == 0) + itbits = cond = 0; + else + itbits = (itbits << 1) & 0x1f; + + cpsr &= ~COMPAT_PSR_IT_MASK; + cpsr |= cond << 13; + cpsr |= (itbits & 0x1c) << (10 - 2); + cpsr |= (itbits & 0x3) << 25; + *vcpu_cpsr(vcpu) = cpsr; +} + +/** + * kvm_skip_instr - skip a trapped instruction and proceed to the next + * @vcpu: The vcpu pointer + */ +void kvm_skip_instr32(struct kvm_vcpu *vcpu, bool is_wide_instr) +{ + bool is_thumb; + + is_thumb = !!(*vcpu_cpsr(vcpu) & COMPAT_PSR_T_BIT); + if (is_thumb && !is_wide_instr) + *vcpu_pc(vcpu) += 2; + else + *vcpu_pc(vcpu) += 4; + kvm_adjust_itstate(vcpu); +} -- cgit v1.2.3 From 62a89c44954f09072bf07a714c8f68bda14ab87e Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Thu, 7 Feb 2013 10:32:33 +0000 Subject: arm64: KVM: 32bit handling of coprocessor traps Provide the necessary infrastructure to trap coprocessor accesses that occur when running 32bit guests. Also wire SMC and HVC trapped in 32bit mode while were at it. Reviewed-by: Christopher Covington Reviewed-by: Catalin Marinas Signed-off-by: Marc Zyngier --- arch/arm64/include/asm/kvm_coproc.h | 5 + arch/arm64/kvm/handle_exit.c | 7 ++ arch/arm64/kvm/sys_regs.c | 181 ++++++++++++++++++++++++++++++++++-- 3 files changed, 186 insertions(+), 7 deletions(-) diff --git a/arch/arm64/include/asm/kvm_coproc.h b/arch/arm64/include/asm/kvm_coproc.h index 9b4477acb554..9a59301cd014 100644 --- a/arch/arm64/include/asm/kvm_coproc.h +++ b/arch/arm64/include/asm/kvm_coproc.h @@ -32,11 +32,16 @@ struct kvm_sys_reg_table { struct kvm_sys_reg_target_table { struct kvm_sys_reg_table table64; + struct kvm_sys_reg_table table32; }; void kvm_register_target_sys_reg_table(unsigned int target, struct kvm_sys_reg_target_table *table); +int kvm_handle_cp14_load_store(struct kvm_vcpu *vcpu, struct kvm_run *run); +int kvm_handle_cp14_access(struct kvm_vcpu *vcpu, struct kvm_run *run); +int kvm_handle_cp15_32(struct kvm_vcpu *vcpu, struct kvm_run *run); +int kvm_handle_cp15_64(struct kvm_vcpu *vcpu, struct kvm_run *run); int kvm_handle_sys_reg(struct kvm_vcpu *vcpu, struct kvm_run *run); #define kvm_coproc_table_init kvm_sys_reg_table_init diff --git a/arch/arm64/kvm/handle_exit.c b/arch/arm64/kvm/handle_exit.c index 4766b7f3515e..9beaca033437 100644 --- a/arch/arm64/kvm/handle_exit.c +++ b/arch/arm64/kvm/handle_exit.c @@ -62,6 +62,13 @@ static int kvm_handle_wfi(struct kvm_vcpu *vcpu, struct kvm_run *run) static exit_handle_fn arm_exit_handlers[] = { [ESR_EL2_EC_WFI] = kvm_handle_wfi, + [ESR_EL2_EC_CP15_32] = kvm_handle_cp15_32, + [ESR_EL2_EC_CP15_64] = kvm_handle_cp15_64, + [ESR_EL2_EC_CP14_MR] = kvm_handle_cp14_access, + [ESR_EL2_EC_CP14_LS] = kvm_handle_cp14_load_store, + [ESR_EL2_EC_CP14_64] = kvm_handle_cp14_access, + [ESR_EL2_EC_HVC32] = handle_hvc, + [ESR_EL2_EC_SMC32] = handle_smc, [ESR_EL2_EC_HVC64] = handle_hvc, [ESR_EL2_EC_SMC64] = handle_smc, [ESR_EL2_EC_SYS64] = kvm_handle_sys_reg, diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index 52fff0ae3442..94923609753b 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -38,6 +38,10 @@ * types are different. My gut feeling is that it should be pretty * easy to merge, but that would be an ABI breakage -- again. VFP * would also need to be abstracted. + * + * For AArch32, we only take care of what is being trapped. Anything + * that has to do with init and userspace access has to go via the + * 64bit interface. */ /* 3 bits per cache level, as per CLIDR, but non-existent caches always 0 */ @@ -166,6 +170,16 @@ static const struct sys_reg_desc sys_reg_descs[] = { { Op0(0b01), Op1(0b000), CRn(0b0111), CRm(0b1110), Op2(0b010), access_dcsw }, + /* TEECR32_EL1 */ + { Op0(0b10), Op1(0b010), CRn(0b0000), CRm(0b0000), Op2(0b000), + NULL, reset_val, TEECR32_EL1, 0 }, + /* TEEHBR32_EL1 */ + { Op0(0b10), Op1(0b010), CRn(0b0001), CRm(0b0000), Op2(0b000), + NULL, reset_val, TEEHBR32_EL1, 0 }, + /* DBGVCR32_EL2 */ + { Op0(0b10), Op1(0b100), CRn(0b0000), CRm(0b0111), Op2(0b000), + NULL, reset_val, DBGVCR32_EL2, 0 }, + /* MPIDR_EL1 */ { Op0(0b11), Op1(0b000), CRn(0b0000), CRm(0b0000), Op2(0b101), NULL, reset_mpidr, MPIDR_EL1 }, @@ -276,6 +290,39 @@ static const struct sys_reg_desc sys_reg_descs[] = { /* TPIDRRO_EL0 */ { Op0(0b11), Op1(0b011), CRn(0b1101), CRm(0b0000), Op2(0b011), NULL, reset_unknown, TPIDRRO_EL0 }, + + /* DACR32_EL2 */ + { Op0(0b11), Op1(0b100), CRn(0b0011), CRm(0b0000), Op2(0b000), + NULL, reset_unknown, DACR32_EL2 }, + /* IFSR32_EL2 */ + { Op0(0b11), Op1(0b100), CRn(0b0101), CRm(0b0000), Op2(0b001), + NULL, reset_unknown, IFSR32_EL2 }, + /* FPEXC32_EL2 */ + { Op0(0b11), Op1(0b100), CRn(0b0101), CRm(0b0011), Op2(0b000), + NULL, reset_val, FPEXC32_EL2, 0x70 }, +}; + +/* Trapped cp15 registers */ +static const struct sys_reg_desc cp15_regs[] = { + /* + * DC{C,I,CI}SW operations: + */ + { Op1( 0), CRn( 7), CRm( 6), Op2( 2), access_dcsw }, + { Op1( 0), CRn( 7), CRm(10), Op2( 2), access_dcsw }, + { Op1( 0), CRn( 7), CRm(14), Op2( 2), access_dcsw }, + { Op1( 0), CRn( 9), CRm(12), Op2( 0), pm_fake }, + { Op1( 0), CRn( 9), CRm(12), Op2( 1), pm_fake }, + { Op1( 0), CRn( 9), CRm(12), Op2( 2), pm_fake }, + { Op1( 0), CRn( 9), CRm(12), Op2( 3), pm_fake }, + { Op1( 0), CRn( 9), CRm(12), Op2( 5), pm_fake }, + { Op1( 0), CRn( 9), CRm(12), Op2( 6), pm_fake }, + { Op1( 0), CRn( 9), CRm(12), Op2( 7), pm_fake }, + { Op1( 0), CRn( 9), CRm(13), Op2( 0), pm_fake }, + { Op1( 0), CRn( 9), CRm(13), Op2( 1), pm_fake }, + { Op1( 0), CRn( 9), CRm(13), Op2( 2), pm_fake }, + { Op1( 0), CRn( 9), CRm(14), Op2( 0), pm_fake }, + { Op1( 0), CRn( 9), CRm(14), Op2( 1), pm_fake }, + { Op1( 0), CRn( 9), CRm(14), Op2( 2), pm_fake }, }; /* Target specific emulation tables */ @@ -288,13 +335,20 @@ void kvm_register_target_sys_reg_table(unsigned int target, } /* Get specific register table for this target. */ -static const struct sys_reg_desc *get_target_table(unsigned target, size_t *num) +static const struct sys_reg_desc *get_target_table(unsigned target, + bool mode_is_64, + size_t *num) { struct kvm_sys_reg_target_table *table; table = target_tables[target]; - *num = table->table64.num; - return table->table64.table; + if (mode_is_64) { + *num = table->table64.num; + return table->table64.table; + } else { + *num = table->table32.num; + return table->table32.table; + } } static const struct sys_reg_desc *find_reg(const struct sys_reg_params *params, @@ -322,13 +376,126 @@ static const struct sys_reg_desc *find_reg(const struct sys_reg_params *params, return NULL; } +int kvm_handle_cp14_load_store(struct kvm_vcpu *vcpu, struct kvm_run *run) +{ + kvm_inject_undefined(vcpu); + return 1; +} + +int kvm_handle_cp14_access(struct kvm_vcpu *vcpu, struct kvm_run *run) +{ + kvm_inject_undefined(vcpu); + return 1; +} + +static void emulate_cp15(struct kvm_vcpu *vcpu, + const struct sys_reg_params *params) +{ + size_t num; + const struct sys_reg_desc *table, *r; + + table = get_target_table(vcpu->arch.target, false, &num); + + /* Search target-specific then generic table. */ + r = find_reg(params, table, num); + if (!r) + r = find_reg(params, cp15_regs, ARRAY_SIZE(cp15_regs)); + + if (likely(r)) { + /* + * Not having an accessor means that we have + * configured a trap that we don't know how to + * handle. This certainly qualifies as a gross bug + * that should be fixed right away. + */ + BUG_ON(!r->access); + + if (likely(r->access(vcpu, params, r))) { + /* Skip instruction, since it was emulated */ + kvm_skip_instr(vcpu, kvm_vcpu_trap_il_is32bit(vcpu)); + return; + } + /* If access function fails, it should complain. */ + } + + kvm_err("Unsupported guest CP15 access at: %08lx\n", *vcpu_pc(vcpu)); + print_sys_reg_instr(params); + kvm_inject_undefined(vcpu); +} + +/** + * kvm_handle_cp15_64 -- handles a mrrc/mcrr trap on a guest CP15 access + * @vcpu: The VCPU pointer + * @run: The kvm_run struct + */ +int kvm_handle_cp15_64(struct kvm_vcpu *vcpu, struct kvm_run *run) +{ + struct sys_reg_params params; + u32 hsr = kvm_vcpu_get_hsr(vcpu); + int Rt2 = (hsr >> 10) & 0xf; + + params.CRm = (hsr >> 1) & 0xf; + params.Rt = (hsr >> 5) & 0xf; + params.is_write = ((hsr & 1) == 0); + + params.Op0 = 0; + params.Op1 = (hsr >> 16) & 0xf; + params.Op2 = 0; + params.CRn = 0; + + /* + * Massive hack here. Store Rt2 in the top 32bits so we only + * have one register to deal with. As we use the same trap + * backends between AArch32 and AArch64, we get away with it. + */ + if (params.is_write) { + u64 val = *vcpu_reg(vcpu, params.Rt); + val &= 0xffffffff; + val |= *vcpu_reg(vcpu, Rt2) << 32; + *vcpu_reg(vcpu, params.Rt) = val; + } + + emulate_cp15(vcpu, ¶ms); + + /* Do the opposite hack for the read side */ + if (!params.is_write) { + u64 val = *vcpu_reg(vcpu, params.Rt); + val >>= 32; + *vcpu_reg(vcpu, Rt2) = val; + } + + return 1; +} + +/** + * kvm_handle_cp15_32 -- handles a mrc/mcr trap on a guest CP15 access + * @vcpu: The VCPU pointer + * @run: The kvm_run struct + */ +int kvm_handle_cp15_32(struct kvm_vcpu *vcpu, struct kvm_run *run) +{ + struct sys_reg_params params; + u32 hsr = kvm_vcpu_get_hsr(vcpu); + + params.CRm = (hsr >> 1) & 0xf; + params.Rt = (hsr >> 5) & 0xf; + params.is_write = ((hsr & 1) == 0); + params.CRn = (hsr >> 10) & 0xf; + params.Op0 = 0; + params.Op1 = (hsr >> 14) & 0x7; + params.Op2 = (hsr >> 17) & 0x7; + + emulate_cp15(vcpu, ¶ms); + return 1; +} + static int emulate_sys_reg(struct kvm_vcpu *vcpu, const struct sys_reg_params *params) { size_t num; const struct sys_reg_desc *table, *r; - table = get_target_table(vcpu->arch.target, &num); + table = get_target_table(vcpu->arch.target, true, &num); /* Search target-specific then generic table. */ r = find_reg(params, table, num); @@ -438,7 +605,7 @@ static const struct sys_reg_desc *index_to_sys_reg_desc(struct kvm_vcpu *vcpu, if (!index_to_params(id, ¶ms)) return NULL; - table = get_target_table(vcpu->arch.target, &num); + table = get_target_table(vcpu->arch.target, true, &num); r = find_reg(¶ms, table, num); if (!r) r = find_reg(¶ms, sys_reg_descs, ARRAY_SIZE(sys_reg_descs)); @@ -762,7 +929,7 @@ static int walk_sys_regs(struct kvm_vcpu *vcpu, u64 __user *uind) size_t num; /* We check for duplicates here, to allow arch-specific overrides. */ - i1 = get_target_table(vcpu->arch.target, &num); + i1 = get_target_table(vcpu->arch.target, true, &num); end1 = i1 + num; i2 = sys_reg_descs; end2 = sys_reg_descs + ARRAY_SIZE(sys_reg_descs); @@ -874,7 +1041,7 @@ void kvm_reset_sys_regs(struct kvm_vcpu *vcpu) /* Generic chip reset first (so target could override). */ reset_sys_reg_descs(vcpu, sys_reg_descs, ARRAY_SIZE(sys_reg_descs)); - table = get_target_table(vcpu->arch.target, &num); + table = get_target_table(vcpu->arch.target, true, &num); reset_sys_reg_descs(vcpu, table, num); for (num = 1; num < NR_SYS_REGS; num++) -- cgit v1.2.3 From 06c7654d2fb8bac7b1af4340ad59434a5d89b86a Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Thu, 7 Feb 2013 10:50:18 +0000 Subject: arm64: KVM: CPU specific 32bit coprocessor access Enable handling of CPU specific 32bit coprocessor access. Not much here either. Reviewed-by: Christopher Covington Reviewed-by: Catalin Marinas Signed-off-by: Marc Zyngier --- arch/arm64/kvm/sys_regs_generic_v8.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/arm64/kvm/sys_regs_generic_v8.c b/arch/arm64/kvm/sys_regs_generic_v8.c index d4e803907312..4268ab9356b1 100644 --- a/arch/arm64/kvm/sys_regs_generic_v8.c +++ b/arch/arm64/kvm/sys_regs_generic_v8.c @@ -59,11 +59,21 @@ static const struct sys_reg_desc genericv8_sys_regs[] = { access_actlr, reset_actlr, ACTLR_EL1 }, }; +static const struct sys_reg_desc genericv8_cp15_regs[] = { + /* ACTLR */ + { Op1(0b000), CRn(0b0001), CRm(0b0000), Op2(0b001), + access_actlr }, +}; + static struct kvm_sys_reg_target_table genericv8_target_table = { .table64 = { .table = genericv8_sys_regs, .num = ARRAY_SIZE(genericv8_sys_regs), }, + .table32 = { + .table = genericv8_cp15_regs, + .num = ARRAY_SIZE(genericv8_cp15_regs), + }, }; static int __init sys_reg_genericv8_init(void) -- cgit v1.2.3 From b4afad06c19e3489767532f86ff453a1d1e28b8c Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Thu, 7 Feb 2013 10:52:10 +0000 Subject: arm64: KVM: 32bit specific register world switch Allow registers specific to 32bit guests to be saved/restored during the world switch. Reviewed-by: Christopher Covington Reviewed-by: Catalin Marinas Signed-off-by: Marc Zyngier --- arch/arm64/kvm/hyp.S | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/arch/arm64/kvm/hyp.S b/arch/arm64/kvm/hyp.S index 8b510835b440..ff985e3d8b72 100644 --- a/arch/arm64/kvm/hyp.S +++ b/arch/arm64/kvm/hyp.S @@ -266,6 +266,74 @@ __kvm_hyp_code_start: msr cntkctl_el1, x23 .endm +.macro skip_32bit_state tmp, target + // Skip 32bit state if not needed + mrs \tmp, hcr_el2 + tbnz \tmp, #HCR_RW_SHIFT, \target +.endm + +.macro skip_tee_state tmp, target + // Skip ThumbEE state if not needed + mrs \tmp, id_pfr0_el1 + tbz \tmp, #12, \target +.endm + +.macro save_guest_32bit_state + skip_32bit_state x3, 1f + + add x3, x2, #CPU_SPSR_OFFSET(KVM_SPSR_ABT) + mrs x4, spsr_abt + mrs x5, spsr_und + mrs x6, spsr_irq + mrs x7, spsr_fiq + stp x4, x5, [x3] + stp x6, x7, [x3, #16] + + add x3, x2, #CPU_SYSREG_OFFSET(DACR32_EL2) + mrs x4, dacr32_el2 + mrs x5, ifsr32_el2 + mrs x6, fpexc32_el2 + mrs x7, dbgvcr32_el2 + stp x4, x5, [x3] + stp x6, x7, [x3, #16] + + skip_tee_state x8, 1f + + add x3, x2, #CPU_SYSREG_OFFSET(TEECR32_EL1) + mrs x4, teecr32_el1 + mrs x5, teehbr32_el1 + stp x4, x5, [x3] +1: +.endm + +.macro restore_guest_32bit_state + skip_32bit_state x3, 1f + + add x3, x2, #CPU_SPSR_OFFSET(KVM_SPSR_ABT) + ldp x4, x5, [x3] + ldp x6, x7, [x3, #16] + msr spsr_abt, x4 + msr spsr_und, x5 + msr spsr_irq, x6 + msr spsr_fiq, x7 + + add x3, x2, #CPU_SYSREG_OFFSET(DACR32_EL2) + ldp x4, x5, [x3] + ldp x6, x7, [x3, #16] + msr dacr32_el2, x4 + msr ifsr32_el2, x5 + msr fpexc32_el2, x6 + msr dbgvcr32_el2, x7 + + skip_tee_state x8, 1f + + add x3, x2, #CPU_SYSREG_OFFSET(TEECR32_EL1) + ldp x4, x5, [x3] + msr teecr32_el1, x4 + msr teehbr32_el1, x5 +1: +.endm + .macro activate_traps ldr x2, [x0, #VCPU_IRQ_LINES] ldr x1, [x0, #VCPU_HCR_EL2] @@ -494,6 +562,7 @@ ENTRY(__kvm_vcpu_run) bl __restore_sysregs bl __restore_fpsimd + restore_guest_32bit_state restore_guest_regs // That's it, no more messing around. @@ -509,6 +578,7 @@ __kvm_vcpu_return: save_guest_regs bl __save_fpsimd bl __save_sysregs + save_guest_32bit_state save_timer_state save_vgic_state -- cgit v1.2.3 From e82e030556e42e823e174e0c3bd97988d1a09d1f Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 6 Feb 2013 11:29:35 +0000 Subject: arm64: KVM: 32bit guest fault injection Add fault injection capability for 32bit guests. Reviewed-by: Christopher Covington Reviewed-by: Catalin Marinas Signed-off-by: Marc Zyngier --- arch/arm64/kvm/inject_fault.c | 79 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kvm/inject_fault.c b/arch/arm64/kvm/inject_fault.c index 54f656271266..81a02a8762b0 100644 --- a/arch/arm64/kvm/inject_fault.c +++ b/arch/arm64/kvm/inject_fault.c @@ -1,5 +1,5 @@ /* - * Fault injection for 64bit guests. + * Fault injection for both 32 and 64bit guests. * * Copyright (C) 2012,2013 - ARM Ltd * Author: Marc Zyngier @@ -29,6 +29,74 @@ PSR_I_BIT | PSR_D_BIT) #define EL1_EXCEPT_SYNC_OFFSET 0x200 +static void prepare_fault32(struct kvm_vcpu *vcpu, u32 mode, u32 vect_offset) +{ + unsigned long cpsr; + unsigned long new_spsr_value = *vcpu_cpsr(vcpu); + bool is_thumb = (new_spsr_value & COMPAT_PSR_T_BIT); + u32 return_offset = (is_thumb) ? 4 : 0; + u32 sctlr = vcpu_cp15(vcpu, c1_SCTLR); + + cpsr = mode | COMPAT_PSR_I_BIT; + + if (sctlr & (1 << 30)) + cpsr |= COMPAT_PSR_T_BIT; + if (sctlr & (1 << 25)) + cpsr |= COMPAT_PSR_E_BIT; + + *vcpu_cpsr(vcpu) = cpsr; + + /* Note: These now point to the banked copies */ + *vcpu_spsr(vcpu) = new_spsr_value; + *vcpu_reg(vcpu, 14) = *vcpu_pc(vcpu) + return_offset; + + /* Branch to exception vector */ + if (sctlr & (1 << 13)) + vect_offset += 0xffff0000; + else /* always have security exceptions */ + vect_offset += vcpu_cp15(vcpu, c12_VBAR); + + *vcpu_pc(vcpu) = vect_offset; +} + +static void inject_undef32(struct kvm_vcpu *vcpu) +{ + prepare_fault32(vcpu, COMPAT_PSR_MODE_UND, 4); +} + +/* + * Modelled after TakeDataAbortException() and TakePrefetchAbortException + * pseudocode. + */ +static void inject_abt32(struct kvm_vcpu *vcpu, bool is_pabt, + unsigned long addr) +{ + u32 vect_offset; + u32 *far, *fsr; + bool is_lpae; + + if (is_pabt) { + vect_offset = 12; + far = &vcpu_cp15(vcpu, c6_IFAR); + fsr = &vcpu_cp15(vcpu, c5_IFSR); + } else { /* !iabt */ + vect_offset = 16; + far = &vcpu_cp15(vcpu, c6_DFAR); + fsr = &vcpu_cp15(vcpu, c5_DFSR); + } + + prepare_fault32(vcpu, COMPAT_PSR_MODE_ABT | COMPAT_PSR_A_BIT, vect_offset); + + *far = addr; + + /* Give the guest an IMPLEMENTATION DEFINED exception */ + is_lpae = (vcpu_cp15(vcpu, c2_TTBCR) >> 31); + if (is_lpae) + *fsr = 1 << 9 | 0x34; + else + *fsr = 0x14; +} + static void inject_abt64(struct kvm_vcpu *vcpu, bool is_iabt, unsigned long addr) { unsigned long cpsr = *vcpu_cpsr(vcpu); @@ -98,6 +166,9 @@ static void inject_undef64(struct kvm_vcpu *vcpu) */ void kvm_inject_dabt(struct kvm_vcpu *vcpu, unsigned long addr) { + if (!(vcpu->arch.hcr_el2 & HCR_RW)) + inject_abt32(vcpu, false, addr); + inject_abt64(vcpu, false, addr); } @@ -111,6 +182,9 @@ void kvm_inject_dabt(struct kvm_vcpu *vcpu, unsigned long addr) */ void kvm_inject_pabt(struct kvm_vcpu *vcpu, unsigned long addr) { + if (!(vcpu->arch.hcr_el2 & HCR_RW)) + inject_abt32(vcpu, true, addr); + inject_abt64(vcpu, true, addr); } @@ -122,5 +196,8 @@ void kvm_inject_pabt(struct kvm_vcpu *vcpu, unsigned long addr) */ void kvm_inject_undefined(struct kvm_vcpu *vcpu) { + if (!(vcpu->arch.hcr_el2 & HCR_RW)) + inject_undef32(vcpu); + inject_undef64(vcpu); } -- cgit v1.2.3 From 0d854a60b1d7d39a37b25dd28f63cfa0df637b91 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Thu, 7 Feb 2013 10:46:46 +0000 Subject: arm64: KVM: enable initialization of a 32bit vcpu Wire the init of a 32bit vcpu by allowing 32bit modes in pstate, and providing sensible defaults out of reset state. This feature is of course conditioned by the presence of 32bit capability on the physical CPU, and is checked by the KVM_CAP_ARM_EL1_32BIT capability. Reviewed-by: Catalin Marinas Signed-off-by: Marc Zyngier --- arch/arm64/include/asm/kvm_host.h | 2 +- arch/arm64/include/uapi/asm/kvm.h | 1 + arch/arm64/kvm/guest.c | 6 ++++++ arch/arm64/kvm/reset.c | 26 +++++++++++++++++++++++++- include/uapi/linux/kvm.h | 1 + 5 files changed, 34 insertions(+), 2 deletions(-) diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index 3f5830b3ca3f..644d73956864 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -34,7 +34,7 @@ #include #include -#define KVM_VCPU_MAX_FEATURES 1 +#define KVM_VCPU_MAX_FEATURES 2 /* We don't currently support large pages. */ #define KVM_HPAGE_GFN_SHIFT(x) 0 diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h index 5b1110c49df5..5031f4263937 100644 --- a/arch/arm64/include/uapi/asm/kvm.h +++ b/arch/arm64/include/uapi/asm/kvm.h @@ -75,6 +75,7 @@ struct kvm_regs { #define KVM_VGIC_V2_CPU_SIZE 0x2000 #define KVM_ARM_VCPU_POWER_OFF 0 /* CPU is started in OFF state */ +#define KVM_ARM_VCPU_EL1_32BIT 1 /* CPU running a 32bit VM */ struct kvm_vcpu_init { __u32 target; diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c index 3d7518a7ebaa..2c3ff67a8ecb 100644 --- a/arch/arm64/kvm/guest.c +++ b/arch/arm64/kvm/guest.c @@ -99,6 +99,12 @@ static int set_core_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) if (off == KVM_REG_ARM_CORE_REG(regs.pstate)) { u32 mode = (*(u32 *)valp) & COMPAT_PSR_MODE_MASK; switch (mode) { + case COMPAT_PSR_MODE_USR: + case COMPAT_PSR_MODE_FIQ: + case COMPAT_PSR_MODE_IRQ: + case COMPAT_PSR_MODE_SVC: + case COMPAT_PSR_MODE_ABT: + case COMPAT_PSR_MODE_UND: case PSR_MODE_EL0t: case PSR_MODE_EL1t: case PSR_MODE_EL1h: diff --git a/arch/arm64/kvm/reset.c b/arch/arm64/kvm/reset.c index 766150ac76ed..70a7816535cd 100644 --- a/arch/arm64/kvm/reset.c +++ b/arch/arm64/kvm/reset.c @@ -38,16 +38,32 @@ static const struct kvm_regs default_regs_reset = { PSR_F_BIT | PSR_D_BIT), }; +static const struct kvm_regs default_regs_reset32 = { + .regs.pstate = (COMPAT_PSR_MODE_SVC | COMPAT_PSR_A_BIT | + COMPAT_PSR_I_BIT | COMPAT_PSR_F_BIT), +}; + static const struct kvm_irq_level default_vtimer_irq = { .irq = 27, .level = 1, }; +static bool cpu_has_32bit_el1(void) +{ + u64 pfr0; + + pfr0 = read_cpuid(ID_AA64PFR0_EL1); + return !!(pfr0 & 0x20); +} + int kvm_arch_dev_ioctl_check_extension(long ext) { int r; switch (ext) { + case KVM_CAP_ARM_EL1_32BIT: + r = cpu_has_32bit_el1(); + break; default: r = 0; } @@ -70,7 +86,15 @@ int kvm_reset_vcpu(struct kvm_vcpu *vcpu) switch (vcpu->arch.target) { default: - cpu_reset = &default_regs_reset; + if (test_bit(KVM_ARM_VCPU_EL1_32BIT, vcpu->arch.features)) { + if (!cpu_has_32bit_el1()) + return -EINVAL; + cpu_reset = &default_regs_reset32; + vcpu->arch.hcr_el2 &= ~HCR_RW; + } else { + cpu_reset = &default_regs_reset; + } + cpu_vtimer_irq = &default_vtimer_irq; break; } diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 2d1bcb891468..aac27640bec2 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -666,6 +666,7 @@ struct kvm_ppc_smmu_info { #define KVM_CAP_IRQ_MPIC 90 #define KVM_CAP_PPC_RTAS 91 #define KVM_CAP_IRQ_XICS 92 +#define KVM_CAP_ARM_EL1_32BIT 93 #ifdef KVM_CAP_IRQ_ROUTING -- cgit v1.2.3 From 379e04c79e8a9ded8a202f1e266f0c5830185bea Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 2 Apr 2013 17:46:31 +0100 Subject: arm64: KVM: userspace API documentation Unsurprisingly, the arm64 userspace API is extremely similar to the 32bit one, the only significant difference being the ONE_REG register mapping. Reviewed-by: Catalin Marinas Signed-off-by: Marc Zyngier --- Documentation/virtual/kvm/api.txt | 58 ++++++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index 5f91eda91647..9bfadeb8be31 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -280,7 +280,7 @@ kvm_run' (see below). 4.11 KVM_GET_REGS Capability: basic -Architectures: all except ARM +Architectures: all except ARM, arm64 Type: vcpu ioctl Parameters: struct kvm_regs (out) Returns: 0 on success, -1 on error @@ -301,7 +301,7 @@ struct kvm_regs { 4.12 KVM_SET_REGS Capability: basic -Architectures: all except ARM +Architectures: all except ARM, arm64 Type: vcpu ioctl Parameters: struct kvm_regs (in) Returns: 0 on success, -1 on error @@ -587,7 +587,7 @@ struct kvm_fpu { 4.24 KVM_CREATE_IRQCHIP Capability: KVM_CAP_IRQCHIP -Architectures: x86, ia64, ARM +Architectures: x86, ia64, ARM, arm64 Type: vm ioctl Parameters: none Returns: 0 on success, -1 on error @@ -595,14 +595,14 @@ Returns: 0 on success, -1 on error Creates an interrupt controller model in the kernel. On x86, creates a virtual ioapic, a virtual PIC (two PICs, nested), and sets up future vcpus to have a local APIC. IRQ routing for GSIs 0-15 is set to both PIC and IOAPIC; GSI 16-23 -only go to the IOAPIC. On ia64, a IOSAPIC is created. On ARM, a GIC is +only go to the IOAPIC. On ia64, a IOSAPIC is created. On ARM/arm64, a GIC is created. 4.25 KVM_IRQ_LINE Capability: KVM_CAP_IRQCHIP -Architectures: x86, ia64, arm +Architectures: x86, ia64, arm, arm64 Type: vm ioctl Parameters: struct kvm_irq_level Returns: 0 on success, -1 on error @@ -612,9 +612,10 @@ On some architectures it is required that an interrupt controller model has been previously created with KVM_CREATE_IRQCHIP. Note that edge-triggered interrupts require the level to be set to 1 and then back to 0. -ARM can signal an interrupt either at the CPU level, or at the in-kernel irqchip -(GIC), and for in-kernel irqchip can tell the GIC to use PPIs designated for -specific cpus. The irq field is interpreted like this: +ARM/arm64 can signal an interrupt either at the CPU level, or at the +in-kernel irqchip (GIC), and for in-kernel irqchip can tell the GIC to +use PPIs designated for specific cpus. The irq field is interpreted +like this:  bits: | 31 ... 24 | 23 ... 16 | 15 ... 0 | field: | irq_type | vcpu_index | irq_id | @@ -1831,6 +1832,22 @@ ARM 32-bit VFP control registers have the following id bit patterns: ARM 64-bit FP registers have the following id bit patterns: 0x4030 0000 0012 0 + +arm64 registers are mapped using the lower 32 bits. The upper 16 of +that is the register group type, or coprocessor number: + +arm64 core/FP-SIMD registers have the following id bit patterns. Note +that the size of the access is variable, as the kvm_regs structure +contains elements ranging from 32 to 128 bits. The index is a 32bit +value in the kvm_regs structure seen as a 32bit array. + 0x60x0 0000 0010 + +arm64 CCSIDR registers are demultiplexed by CSSELR value: + 0x6020 0000 0011 00 + +arm64 system registers have the following id bit patterns: + 0x6030 0000 0013 + 4.69 KVM_GET_ONE_REG Capability: KVM_CAP_ONE_REG @@ -2264,7 +2281,7 @@ current state. "addr" is ignored. 4.77 KVM_ARM_VCPU_INIT Capability: basic -Architectures: arm +Architectures: arm, arm64 Type: vcpu ioctl Parameters: struct struct kvm_vcpu_init (in) Returns: 0 on success; -1 on error @@ -2283,12 +2300,14 @@ should be created before this ioctl is invoked. Possible features: - KVM_ARM_VCPU_POWER_OFF: Starts the CPU in a power-off state. Depends on KVM_CAP_ARM_PSCI. + - KVM_ARM_VCPU_EL1_32BIT: Starts the CPU in a 32bit mode. + Depends on KVM_CAP_ARM_EL1_32BIT (arm64 only). 4.78 KVM_GET_REG_LIST Capability: basic -Architectures: arm +Architectures: arm, arm64 Type: vcpu ioctl Parameters: struct kvm_reg_list (in/out) Returns: 0 on success; -1 on error @@ -2308,7 +2327,7 @@ KVM_GET_ONE_REG/KVM_SET_ONE_REG calls. 4.80 KVM_ARM_SET_DEVICE_ADDR Capability: KVM_CAP_ARM_SET_DEVICE_ADDR -Architectures: arm +Architectures: arm, arm64 Type: vm ioctl Parameters: struct kvm_arm_device_address (in) Returns: 0 on success, -1 on error @@ -2329,18 +2348,19 @@ can access emulated or directly exposed devices, which the host kernel needs to know about. The id field is an architecture specific identifier for a specific device. -ARM divides the id field into two parts, a device id and an address type id -specific to the individual device. +ARM/arm64 divides the id field into two parts, a device id and an +address type id specific to the individual device.  bits: | 63 ... 32 | 31 ... 16 | 15 ... 0 | field: | 0x00000000 | device id | addr type id | -ARM currently only require this when using the in-kernel GIC support for the -hardware VGIC features, using KVM_ARM_DEVICE_VGIC_V2 as the device id. When -setting the base address for the guest's mapping of the VGIC virtual CPU -and distributor interface, the ioctl must be called after calling -KVM_CREATE_IRQCHIP, but before calling KVM_RUN on any of the VCPUs. Calling -this ioctl twice for any of the base addresses will return -EEXIST. +ARM/arm64 currently only require this when using the in-kernel GIC +support for the hardware VGIC features, using KVM_ARM_DEVICE_VGIC_V2 +as the device id. When setting the base address for the guest's +mapping of the VGIC virtual CPU and distributor interface, the ioctl +must be called after calling KVM_CREATE_IRQCHIP, but before calling +KVM_RUN on any of the VCPUs. Calling this ioctl twice for any of the +base addresses will return -EEXIST. 4.82 KVM_PPC_RTAS_DEFINE_TOKEN -- cgit v1.2.3 From 6394a3ec02ab39147aab9ea56d0dabafd3dcae60 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 2 Apr 2013 17:49:40 +0100 Subject: arm64: KVM: MAINTAINERS update Elect myself as the KVM/arm64 maintainer. Acked-by: Catalin Marinas Signed-off-by: Marc Zyngier --- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index f35a259a6564..56239fd7dc7c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4709,6 +4709,15 @@ F: arch/arm/include/uapi/asm/kvm* F: arch/arm/include/asm/kvm* F: arch/arm/kvm/ +KERNEL VIRTUAL MACHINE FOR ARM64 (KVM/arm64) +M: Marc Zyngier +L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) +L: kvmarm@lists.cs.columbia.edu +S: Maintained +F: arch/arm64/include/uapi/asm/kvm* +F: arch/arm64/include/asm/kvm* +F: arch/arm64/kvm/ + KEXEC M: Eric Biederman W: http://kernel.org/pub/linux/utils/kernel/kexec/ -- cgit v1.2.3 From aa4a73a0a23a65a2f531d01f1865d1e61c6acb55 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Thu, 2 May 2013 14:31:03 +0100 Subject: arm64: KVM: document kernel object mappings in HYP HYP mode has access to some of the kernel pages. Document the memory mapping and the offset between kernel VA and HYP VA. Reviewed-by: Catalin Marinas Signed-off-by: Marc Zyngier --- Documentation/arm64/memory.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Documentation/arm64/memory.txt b/Documentation/arm64/memory.txt index 5f583af0a6e1..78a377124ef0 100644 --- a/Documentation/arm64/memory.txt +++ b/Documentation/arm64/memory.txt @@ -73,3 +73,10 @@ Translation table lookup with 64KB pages: | | +--------------------------> [41:29] L2 index (only 38:29 used) | +-------------------------------> [47:42] L1 index (not used) +-------------------------------------------------> [63] TTBR0/1 + +When using KVM, the hypervisor maps kernel pages in EL2, at a fixed +offset from the kernel VA (top 24bits of the kernel VA set to zero): + +Start End Size Use +----------------------------------------------------------------------- +0000004000000000 0000007fffffffff 256GB kernel objects mapped in HYP -- cgit v1.2.3 From a9bce1b03c2199e66d36cda8aac675338bc074a7 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 5 Jun 2013 16:13:47 +0200 Subject: mfd: input: iio: ti_am335x_adc: use one structure for ti_tscadc_dev The mfd driver creates platform data for the child devices and it is the ti_tscadc_dev struct. This struct is copied for the two devices. The copy of the structure makes a common lock in this structure a little less usefull. Therefore the platform data is not a pointer to the structure and the same structure is used. While doing the change I noticed that the suspend/resume code assumes the wrong pointer for ti_tscadc_dev and this has been fixed as well. Signed-off-by: Sebastian Andrzej Siewior --- drivers/iio/adc/ti_am335x_adc.c | 5 +++-- drivers/input/touchscreen/ti_am335x_tsc.c | 16 +++++++++------- drivers/mfd/ti_am335x_tscadc.c | 8 ++++---- include/linux/mfd/ti_am335x_tscadc.h | 7 +++++++ 4 files changed, 23 insertions(+), 13 deletions(-) diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c index 5f9a7e7d3135..9db352e413e4 100644 --- a/drivers/iio/adc/ti_am335x_adc.c +++ b/drivers/iio/adc/ti_am335x_adc.c @@ -140,7 +140,7 @@ static int tiadc_probe(struct platform_device *pdev) { struct iio_dev *indio_dev; struct tiadc_device *adc_dev; - struct ti_tscadc_dev *tscadc_dev = pdev->dev.platform_data; + struct ti_tscadc_dev *tscadc_dev = ti_tscadc_dev_get(pdev); struct mfd_tscadc_board *pdata; int err; @@ -205,9 +205,10 @@ static int tiadc_suspend(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct tiadc_device *adc_dev = iio_priv(indio_dev); - struct ti_tscadc_dev *tscadc_dev = dev->platform_data; + struct ti_tscadc_dev *tscadc_dev; unsigned int idle; + tscadc_dev = ti_tscadc_dev_get(to_platform_device(dev)); if (!device_may_wakeup(tscadc_dev->dev)) { idle = tiadc_readl(adc_dev, REG_CTRL); idle &= ~(CNTRLREG_TSCSSENB); diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c index 51e7b87827a4..16077d3d80ba 100644 --- a/drivers/input/touchscreen/ti_am335x_tsc.c +++ b/drivers/input/touchscreen/ti_am335x_tsc.c @@ -262,7 +262,7 @@ static int titsc_probe(struct platform_device *pdev) { struct titsc *ts_dev; struct input_dev *input_dev; - struct ti_tscadc_dev *tscadc_dev = pdev->dev.platform_data; + struct ti_tscadc_dev *tscadc_dev = ti_tscadc_dev_get(pdev); struct mfd_tscadc_board *pdata; int err; @@ -329,8 +329,8 @@ err_free_mem: static int titsc_remove(struct platform_device *pdev) { - struct ti_tscadc_dev *tscadc_dev = pdev->dev.platform_data; - struct titsc *ts_dev = tscadc_dev->tsc; + struct titsc *ts_dev = platform_get_drvdata(pdev); + u32 steps; free_irq(ts_dev->irq, ts_dev); @@ -344,10 +344,11 @@ static int titsc_remove(struct platform_device *pdev) #ifdef CONFIG_PM static int titsc_suspend(struct device *dev) { - struct ti_tscadc_dev *tscadc_dev = dev->platform_data; - struct titsc *ts_dev = tscadc_dev->tsc; + struct titsc *ts_dev = dev_get_drvdata(dev); + struct ti_tscadc_dev *tscadc_dev; unsigned int idle; + tscadc_dev = ti_tscadc_dev_get(to_platform_device(dev)); if (device_may_wakeup(tscadc_dev->dev)) { idle = titsc_readl(ts_dev, REG_IRQENABLE); titsc_writel(ts_dev, REG_IRQENABLE, @@ -359,9 +360,10 @@ static int titsc_suspend(struct device *dev) static int titsc_resume(struct device *dev) { - struct ti_tscadc_dev *tscadc_dev = dev->platform_data; - struct titsc *ts_dev = tscadc_dev->tsc; + struct titsc *ts_dev = dev_get_drvdata(dev); + struct ti_tscadc_dev *tscadc_dev; + tscadc_dev = ti_tscadc_dev_get(to_platform_device(dev)); if (device_may_wakeup(tscadc_dev->dev)) { titsc_writel(ts_dev, REG_IRQWAKEUP, 0x00); diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c index e9f3fb510b44..772ea2adb539 100644 --- a/drivers/mfd/ti_am335x_tscadc.c +++ b/drivers/mfd/ti_am335x_tscadc.c @@ -176,14 +176,14 @@ static int ti_tscadc_probe(struct platform_device *pdev) /* TSC Cell */ cell = &tscadc->cells[TSC_CELL]; cell->name = "tsc"; - cell->platform_data = tscadc; - cell->pdata_size = sizeof(*tscadc); + cell->platform_data = &tscadc; + cell->pdata_size = sizeof(tscadc); /* ADC Cell */ cell = &tscadc->cells[ADC_CELL]; cell->name = "tiadc"; - cell->platform_data = tscadc; - cell->pdata_size = sizeof(*tscadc); + cell->platform_data = &tscadc; + cell->pdata_size = sizeof(tscadc); err = mfd_add_devices(&pdev->dev, pdev->id, tscadc->cells, TSCADC_CELLS, NULL, 0, NULL); diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h index c79ad5d2f271..8114e4e8b91b 100644 --- a/include/linux/mfd/ti_am335x_tscadc.h +++ b/include/linux/mfd/ti_am335x_tscadc.h @@ -149,4 +149,11 @@ struct ti_tscadc_dev { struct adc_device *adc; }; +static inline struct ti_tscadc_dev *ti_tscadc_dev_get(struct platform_device *p) +{ + struct ti_tscadc_dev **tscadc_dev = p->dev.platform_data; + + return *tscadc_dev; +} + #endif -- cgit v1.2.3 From f3fe53dd975306903be3616c87865a87a52fb20e Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Wed, 12 Jun 2013 09:57:57 +0200 Subject: ASoC: ux500: Move DMA parameters into ux500_msp Move struct ux500_msp_dma_params declaration from ux500_msp_i2s_drvdata to ux500_msp, this saves some confusing pointer passing and allows to access all DMA configuration fields from ux500_msp_i2s. Signed-off-by: Fabio Baltieri Acked-by: Linus Walleij Acked-by: Lee Jones Signed-off-by: Mark Brown --- sound/soc/ux500/ux500_msp_dai.c | 11 ++++------- sound/soc/ux500/ux500_msp_dai.h | 2 -- sound/soc/ux500/ux500_msp_i2s.c | 10 ++++++---- sound/soc/ux500/ux500_msp_i2s.h | 14 +++++++------- 4 files changed, 17 insertions(+), 20 deletions(-) diff --git a/sound/soc/ux500/ux500_msp_dai.c b/sound/soc/ux500/ux500_msp_dai.c index 7d5fc1328523..c6fb5cce980e 100644 --- a/sound/soc/ux500/ux500_msp_dai.c +++ b/sound/soc/ux500/ux500_msp_dai.c @@ -658,14 +658,11 @@ static int ux500_msp_dai_probe(struct snd_soc_dai *dai) { struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev); - drvdata->playback_dma_data.dma_cfg = drvdata->msp->dma_cfg_tx; - drvdata->capture_dma_data.dma_cfg = drvdata->msp->dma_cfg_rx; + dai->playback_dma_data = &drvdata->msp->playback_dma_data; + dai->capture_dma_data = &drvdata->msp->capture_dma_data; - dai->playback_dma_data = &drvdata->playback_dma_data; - dai->capture_dma_data = &drvdata->capture_dma_data; - - drvdata->playback_dma_data.data_size = drvdata->slot_width; - drvdata->capture_dma_data.data_size = drvdata->slot_width; + drvdata->msp->playback_dma_data.data_size = drvdata->slot_width; + drvdata->msp->capture_dma_data.data_size = drvdata->slot_width; return 0; } diff --git a/sound/soc/ux500/ux500_msp_dai.h b/sound/soc/ux500/ux500_msp_dai.h index c7212825fe4c..312ae535e351 100644 --- a/sound/soc/ux500/ux500_msp_dai.h +++ b/sound/soc/ux500/ux500_msp_dai.h @@ -51,8 +51,6 @@ enum ux500_msp_clock_id { struct ux500_msp_i2s_drvdata { struct ux500_msp *msp; struct regulator *reg_vape; - struct ux500_msp_dma_params playback_dma_data; - struct ux500_msp_dma_params capture_dma_data; unsigned int fmt; unsigned int tx_mask; unsigned int rx_mask; diff --git a/sound/soc/ux500/ux500_msp_i2s.c b/sound/soc/ux500/ux500_msp_i2s.c index cba0e86833e9..14a4a5bb60fc 100644 --- a/sound/soc/ux500/ux500_msp_i2s.c +++ b/sound/soc/ux500/ux500_msp_i2s.c @@ -367,12 +367,14 @@ static int enable_msp(struct ux500_msp *msp, struct ux500_msp_config *config) } /* Make sure the correct DMA-directions are configured */ - if ((config->direction & MSP_DIR_RX) && (!msp->dma_cfg_rx)) { + if ((config->direction & MSP_DIR_RX) && + !msp->capture_dma_data.dma_cfg) { dev_err(msp->dev, "%s: ERROR: MSP RX-mode is not configured!", __func__); return -EINVAL; } - if ((config->direction == MSP_DIR_TX) && (!msp->dma_cfg_tx)) { + if ((config->direction == MSP_DIR_TX) && + !msp->playback_dma_data.dma_cfg) { dev_err(msp->dev, "%s: ERROR: MSP TX-mode is not configured!", __func__); return -EINVAL; @@ -673,8 +675,8 @@ int ux500_msp_i2s_init_msp(struct platform_device *pdev, msp->id = platform_data->id; msp->dev = &pdev->dev; - msp->dma_cfg_rx = platform_data->msp_i2s_dma_rx; - msp->dma_cfg_tx = platform_data->msp_i2s_dma_tx; + msp->playback_dma_data.dma_cfg = platform_data->msp_i2s_dma_tx; + msp->capture_dma_data.dma_cfg = platform_data->msp_i2s_dma_rx; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) { diff --git a/sound/soc/ux500/ux500_msp_i2s.h b/sound/soc/ux500/ux500_msp_i2s.h index 189a3751993b..879617147fc8 100644 --- a/sound/soc/ux500/ux500_msp_i2s.h +++ b/sound/soc/ux500/ux500_msp_i2s.h @@ -468,12 +468,17 @@ struct ux500_msp_config { unsigned int iodelay; }; +struct ux500_msp_dma_params { + unsigned int data_size; + struct stedma40_chan_cfg *dma_cfg; +}; + struct ux500_msp { enum msp_i2s_id id; void __iomem *registers; struct device *dev; - struct stedma40_chan_cfg *dma_cfg_rx; - struct stedma40_chan_cfg *dma_cfg_tx; + struct ux500_msp_dma_params playback_dma_data; + struct ux500_msp_dma_params capture_dma_data; enum msp_state msp_state; int def_elem_len; unsigned int dir_busy; @@ -481,11 +486,6 @@ struct ux500_msp { unsigned int f_bitclk; }; -struct ux500_msp_dma_params { - unsigned int data_size; - struct stedma40_chan_cfg *dma_cfg; -}; - struct msp_i2s_platform_data; int ux500_msp_i2s_init_msp(struct platform_device *pdev, struct ux500_msp **msp_p, -- cgit v1.2.3 From 20413113ffdd8c56b2a985ca8195d9c91e9c602b Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Wed, 12 Jun 2013 09:57:58 +0200 Subject: ASoC: ux500: Set DMA address during device init Add a field with the tx/rx register address to the DMA parameters structure, and set it to the correct address during device initialization. This address used to be hardcoded in the DMA controller driver, it now needs to be explicitly figured out by the device driver. Signed-off-by: Fabio Baltieri Acked-by: Linus Walleij Acked-by: Lee Jones Signed-off-by: Mark Brown --- sound/soc/ux500/ux500_msp_i2s.c | 3 +++ sound/soc/ux500/ux500_msp_i2s.h | 1 + 2 files changed, 4 insertions(+) diff --git a/sound/soc/ux500/ux500_msp_i2s.c b/sound/soc/ux500/ux500_msp_i2s.c index 14a4a5bb60fc..1ca8b08ae993 100644 --- a/sound/soc/ux500/ux500_msp_i2s.c +++ b/sound/soc/ux500/ux500_msp_i2s.c @@ -685,6 +685,9 @@ int ux500_msp_i2s_init_msp(struct platform_device *pdev, return -ENOMEM; } + msp->playback_dma_data.tx_rx_addr = res->start + MSP_DR; + msp->capture_dma_data.tx_rx_addr = res->start + MSP_DR; + msp->registers = devm_ioremap(&pdev->dev, res->start, resource_size(res)); if (msp->registers == NULL) { diff --git a/sound/soc/ux500/ux500_msp_i2s.h b/sound/soc/ux500/ux500_msp_i2s.h index 879617147fc8..258d0bcee0bd 100644 --- a/sound/soc/ux500/ux500_msp_i2s.h +++ b/sound/soc/ux500/ux500_msp_i2s.h @@ -470,6 +470,7 @@ struct ux500_msp_config { struct ux500_msp_dma_params { unsigned int data_size; + dma_addr_t tx_rx_addr; struct stedma40_chan_cfg *dma_cfg; }; -- cgit v1.2.3 From eef6473ff3ce2383febebd2e799beceaece9adda Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Wed, 12 Jun 2013 09:57:59 +0200 Subject: ASoC: ux500: Add DMA slave config prepare routine Implement a DMA slave config prepare routine, as until now the MSP driver depended on the DMA controller completing the channel configuration on its own, but this is not the case anymore since the recent DMA driver updates. Signed-off-by: Fabio Baltieri Acked-by: Linus Walleij Acked-by: Lee Jones Signed-off-by: Mark Brown --- sound/soc/ux500/ux500_pcm.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/sound/soc/ux500/ux500_pcm.c b/sound/soc/ux500/ux500_pcm.c index b6e5ae277299..5f01c19776bf 100644 --- a/sound/soc/ux500/ux500_pcm.c +++ b/sound/soc/ux500/ux500_pcm.c @@ -103,10 +103,40 @@ static struct dma_chan *ux500_pcm_request_chan(struct snd_soc_pcm_runtime *rtd, return snd_dmaengine_pcm_request_channel(stedma40_filter, dma_cfg); } +static int ux500_pcm_prepare_slave_config(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct dma_slave_config *slave_config) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct ux500_msp_dma_params *dma_params; + struct stedma40_chan_cfg *dma_cfg; + int ret; + + dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + dma_cfg = dma_params->dma_cfg; + + ret = snd_hwparams_to_dma_slave_config(substream, params, slave_config); + if (ret) + return ret; + + slave_config->dst_maxburst = 4; + slave_config->dst_addr_width = dma_cfg->dst_info.data_width; + slave_config->src_maxburst = 4; + slave_config->src_addr_width = dma_cfg->src_info.data_width; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + slave_config->dst_addr = dma_params->tx_rx_addr; + else + slave_config->src_addr = dma_params->tx_rx_addr; + + return 0; +} + static const struct snd_dmaengine_pcm_config ux500_dmaengine_pcm_config = { .pcm_hardware = &ux500_pcm_hw, .compat_request_channel = ux500_pcm_request_chan, .prealloc_buffer_size = 128 * 1024, + .prepare_slave_config = ux500_pcm_prepare_slave_config, }; int ux500_pcm_register_platform(struct platform_device *pdev) -- cgit v1.2.3 From abeccee40320245a2a6a006dc8466a703cbd1d5e Mon Sep 17 00:00:00 2001 From: "Patil, Rachna" Date: Thu, 24 Jan 2013 03:45:05 +0000 Subject: input: ti_am33x_tsc: Step enable bits made configurable Current code has hard coded value written to step enable bits. Now the bits are updated based on how many steps are needed to be configured got from platform data. The user needs to take care not to exceed the count more than 16. While using ADC and TSC one should take care to set this parameter correctly. Sebastian added the common lock and moved the code, that manipulates the steps, from into the mfd module. Signed-off-by: Patil, Rachna Signed-off-by: Felipe Balbi Signed-off-by: Sebastian Andrzej Siewior --- drivers/iio/adc/ti_am335x_adc.c | 20 ++++++++++++++++++-- drivers/input/touchscreen/ti_am335x_tsc.c | 12 ++++++++++-- drivers/mfd/ti_am335x_tscadc.c | 29 ++++++++++++++++++++++++++++- include/linux/mfd/ti_am335x_tscadc.h | 8 ++++++-- 4 files changed, 62 insertions(+), 7 deletions(-) diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c index 9db352e413e4..543b9c42ac5f 100644 --- a/drivers/iio/adc/ti_am335x_adc.c +++ b/drivers/iio/adc/ti_am335x_adc.c @@ -42,10 +42,20 @@ static void tiadc_writel(struct tiadc_device *adc, unsigned int reg, writel(val, adc->mfd_tscadc->tscadc_base + reg); } +static u32 get_adc_step_mask(struct tiadc_device *adc_dev) +{ + u32 step_en; + + step_en = ((1 << adc_dev->channels) - 1); + step_en <<= TOTAL_STEPS - adc_dev->channels + 1; + return step_en; +} + static void tiadc_step_config(struct tiadc_device *adc_dev) { unsigned int stepconfig; int i, channels = 0, steps; + u32 step_en; /* * There are 16 configurable steps and 8 analog input @@ -69,7 +79,8 @@ static void tiadc_step_config(struct tiadc_device *adc_dev) STEPCONFIG_OPENDLY); channels++; } - tiadc_writel(adc_dev, REG_SE, STPENB_STEPENB); + step_en = get_adc_step_mask(adc_dev); + am335x_tsc_se_set(adc_dev->mfd_tscadc, step_en); } static int tiadc_channel_init(struct iio_dev *indio_dev, int channels) @@ -127,7 +138,7 @@ static int tiadc_read_raw(struct iio_dev *indio_dev, if (i == chan->channel) *val = readx1 & 0xfff; } - tiadc_writel(adc_dev, REG_SE, STPENB_STEPENB); + am335x_tsc_se_update(adc_dev->mfd_tscadc); return IIO_VAL_INT; } @@ -191,10 +202,15 @@ err_ret: static int tiadc_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct tiadc_device *adc_dev = iio_priv(indio_dev); + u32 step_en; iio_device_unregister(indio_dev); tiadc_channels_remove(indio_dev); + step_en = get_adc_step_mask(adc_dev); + am335x_tsc_se_clr(adc_dev->mfd_tscadc, step_en); + iio_device_free(indio_dev); return 0; diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c index 16077d3d80ba..23d6a4dacc88 100644 --- a/drivers/input/touchscreen/ti_am335x_tsc.c +++ b/drivers/input/touchscreen/ti_am335x_tsc.c @@ -57,6 +57,7 @@ static void titsc_writel(struct titsc *tsc, unsigned int reg, static void titsc_step_config(struct titsc *ts_dev) { unsigned int config; + unsigned int stepenable = 0; int i, total_steps; /* Configure the Step registers */ @@ -128,7 +129,9 @@ static void titsc_step_config(struct titsc *ts_dev) titsc_writel(ts_dev, REG_STEPDELAY(total_steps + 2), STEPCONFIG_OPENDLY); - titsc_writel(ts_dev, REG_SE, STPENB_STEPENB_TC); + /* The steps1 … end and bit 0 for TS_Charge */ + stepenable = (1 << (total_steps + 2)) - 1; + am335x_tsc_se_set(ts_dev->mfd_tscadc, stepenable); } static void titsc_read_coordinates(struct titsc *ts_dev, @@ -250,7 +253,7 @@ static irqreturn_t titsc_irq(int irq, void *dev) titsc_writel(ts_dev, REG_IRQSTATUS, irqclr); - titsc_writel(ts_dev, REG_SE, STPENB_STEPENB_TC); + am335x_tsc_se_update(ts_dev->mfd_tscadc); return IRQ_HANDLED; } @@ -334,6 +337,11 @@ static int titsc_remove(struct platform_device *pdev) free_irq(ts_dev->irq, ts_dev); + /* total steps followed by the enable mask */ + steps = 2 * ts_dev->steps_to_configure + 2; + steps = (1 << steps) - 1; + am335x_tsc_se_clr(ts_dev->mfd_tscadc, steps); + input_unregister_device(ts_dev->input); platform_set_drvdata(pdev, NULL); diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c index 772ea2adb539..90ccfc07e16b 100644 --- a/drivers/mfd/ti_am335x_tscadc.c +++ b/drivers/mfd/ti_am335x_tscadc.c @@ -48,6 +48,32 @@ static const struct regmap_config tscadc_regmap_config = { .val_bits = 32, }; +void am335x_tsc_se_update(struct ti_tscadc_dev *tsadc) +{ + tscadc_writel(tsadc, REG_SE, tsadc->reg_se_cache); +} +EXPORT_SYMBOL_GPL(am335x_tsc_se_update); + +void am335x_tsc_se_set(struct ti_tscadc_dev *tsadc, u32 val) +{ + spin_lock(&tsadc->reg_lock); + tsadc->reg_se_cache |= val; + spin_unlock(&tsadc->reg_lock); + + am335x_tsc_se_update(tsadc); +} +EXPORT_SYMBOL_GPL(am335x_tsc_se_set); + +void am335x_tsc_se_clr(struct ti_tscadc_dev *tsadc, u32 val) +{ + spin_lock(&tsadc->reg_lock); + tsadc->reg_se_cache &= ~val; + spin_unlock(&tsadc->reg_lock); + + am335x_tsc_se_update(tsadc); +} +EXPORT_SYMBOL_GPL(am335x_tsc_se_clr); + static void tscadc_idle_config(struct ti_tscadc_dev *config) { unsigned int idleconfig; @@ -129,6 +155,7 @@ static int ti_tscadc_probe(struct platform_device *pdev) goto ret; } + spin_lock_init(&tscadc->reg_lock); pm_runtime_enable(&pdev->dev); pm_runtime_get_sync(&pdev->dev); @@ -239,7 +266,7 @@ static int tscadc_resume(struct device *dev) CNTRLREG_STEPID | CNTRLREG_4WIRE; tscadc_writel(tscadc_dev, REG_CTRL, ctrl); tscadc_idle_config(tscadc_dev); - tscadc_writel(tscadc_dev, REG_SE, STPENB_STEPENB); + am335x_tsc_se_update(tscadc_dev); restore = tscadc_readl(tscadc_dev, REG_CTRL); tscadc_writel(tscadc_dev, REG_CTRL, (restore | CNTRLREG_TSCSSENB)); diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h index 8114e4e8b91b..4258627d076a 100644 --- a/include/linux/mfd/ti_am335x_tscadc.h +++ b/include/linux/mfd/ti_am335x_tscadc.h @@ -46,8 +46,6 @@ /* Step Enable */ #define STEPENB_MASK (0x1FFFF << 0) #define STEPENB(val) ((val) << 0) -#define STPENB_STEPENB STEPENB(0x1FFFF) -#define STPENB_STEPENB_TC STEPENB(0x1FFF) /* IRQ enable */ #define IRQENB_HW_PEN BIT(0) @@ -141,6 +139,8 @@ struct ti_tscadc_dev { void __iomem *tscadc_base; int irq; struct mfd_cell cells[TSCADC_CELLS]; + u32 reg_se_cache; + spinlock_t reg_lock; /* tsc device */ struct titsc *tsc; @@ -156,4 +156,8 @@ static inline struct ti_tscadc_dev *ti_tscadc_dev_get(struct platform_device *p) return *tscadc_dev; } +void am335x_tsc_se_update(struct ti_tscadc_dev *tsadc); +void am335x_tsc_se_set(struct ti_tscadc_dev *tsadc, u32 val); +void am335x_tsc_se_clr(struct ti_tscadc_dev *tsadc, u32 val); + #endif -- cgit v1.2.3 From bb76dc09ddfc135c6c5e8eb7d3c583bfa8bdd439 Mon Sep 17 00:00:00 2001 From: "Patil, Rachna" Date: Thu, 24 Jan 2013 03:45:06 +0000 Subject: input: ti_am33x_tsc: Order of TSC wires, made configurable The current driver expected touchscreen input wires(XP,XN,YP,YN) to be connected in a particular order. Making changes to accept this as platform data. Sebastian reworked the original patch and removed a lot of the not required pieces. Signed-off-by: Patil, Rachna Signed-off-by: Felipe Balbi Signed-off-by: Sebastian Andrzej Siewior --- drivers/input/touchscreen/ti_am335x_tsc.c | 102 +++++++++++++++++++++++++----- include/linux/input/ti_am335x_tsc.h | 12 ++++ include/linux/mfd/ti_am335x_tscadc.h | 3 - 3 files changed, 98 insertions(+), 19 deletions(-) diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c index 23d6a4dacc88..2bdd66cd76a7 100644 --- a/drivers/input/touchscreen/ti_am335x_tsc.c +++ b/drivers/input/touchscreen/ti_am335x_tsc.c @@ -33,6 +33,13 @@ #define SEQ_SETTLE 275 #define MAX_12BIT ((1 << 12) - 1) +static const int config_pins[] = { + STEPCONFIG_XPP, + STEPCONFIG_XNN, + STEPCONFIG_YPP, + STEPCONFIG_YNN, +}; + struct titsc { struct input_dev *input; struct ti_tscadc_dev *mfd_tscadc; @@ -41,6 +48,9 @@ struct titsc { unsigned int x_plate_resistance; bool pen_down; int steps_to_configure; + u32 config_inp[4]; + u32 bit_xp, bit_xn, bit_yp, bit_yn; + u32 inp_xp, inp_xn, inp_yp, inp_yn; }; static unsigned int titsc_readl(struct titsc *ts, unsigned int reg) @@ -54,6 +64,58 @@ static void titsc_writel(struct titsc *tsc, unsigned int reg, writel(val, tsc->mfd_tscadc->tscadc_base + reg); } +static int titsc_config_wires(struct titsc *ts_dev) +{ + u32 analog_line[4]; + u32 wire_order[4]; + int i, bit_cfg; + + for (i = 0; i < 4; i++) { + /* + * Get the order in which TSC wires are attached + * w.r.t. each of the analog input lines on the EVM. + */ + analog_line[i] = (ts_dev->config_inp[i] & 0xF0) >> 4; + wire_order[i] = ts_dev->config_inp[i] & 0x0F; + if (WARN_ON(analog_line[i] > 7)) + return -EINVAL; + if (WARN_ON(wire_order[i] > ARRAY_SIZE(config_pins))) + return -EINVAL; + } + + for (i = 0; i < 4; i++) { + int an_line; + int wi_order; + + an_line = analog_line[i]; + wi_order = wire_order[i]; + bit_cfg = config_pins[wi_order]; + if (bit_cfg == 0) + return -EINVAL; + switch (wi_order) { + case 0: + ts_dev->bit_xp = bit_cfg; + ts_dev->inp_xp = an_line; + break; + + case 1: + ts_dev->bit_xn = bit_cfg; + ts_dev->inp_xn = an_line; + break; + + case 2: + ts_dev->bit_yp = bit_cfg; + ts_dev->inp_yp = an_line; + break; + case 3: + ts_dev->bit_yn = bit_cfg; + ts_dev->inp_yn = an_line; + break; + } + } + return 0; +} + static void titsc_step_config(struct titsc *ts_dev) { unsigned int config; @@ -64,18 +126,18 @@ static void titsc_step_config(struct titsc *ts_dev) total_steps = 2 * ts_dev->steps_to_configure; config = STEPCONFIG_MODE_HWSYNC | - STEPCONFIG_AVG_16 | STEPCONFIG_XPP; + STEPCONFIG_AVG_16 | ts_dev->bit_xp; switch (ts_dev->wires) { case 4: - config |= STEPCONFIG_INP_AN2 | STEPCONFIG_XNN; + config |= STEPCONFIG_INP(ts_dev->inp_yp) | ts_dev->bit_xn; break; case 5: - config |= STEPCONFIG_YNN | - STEPCONFIG_INP_AN4 | STEPCONFIG_XNN | - STEPCONFIG_YPP; + config |= ts_dev->bit_yn | + STEPCONFIG_INP_AN4 | ts_dev->bit_xn | + ts_dev->bit_yp; break; case 8: - config |= STEPCONFIG_INP_AN2 | STEPCONFIG_XNN; + config |= STEPCONFIG_INP(ts_dev->inp_yp) | ts_dev->bit_xn; break; } @@ -86,18 +148,18 @@ static void titsc_step_config(struct titsc *ts_dev) config = 0; config = STEPCONFIG_MODE_HWSYNC | - STEPCONFIG_AVG_16 | STEPCONFIG_YNN | + STEPCONFIG_AVG_16 | ts_dev->bit_yn | STEPCONFIG_INM_ADCREFM | STEPCONFIG_FIFO1; switch (ts_dev->wires) { case 4: - config |= STEPCONFIG_YPP; + config |= ts_dev->bit_yp | STEPCONFIG_INP(ts_dev->inp_xp); break; case 5: - config |= STEPCONFIG_XPP | STEPCONFIG_INP_AN4 | - STEPCONFIG_XNP | STEPCONFIG_YPN; + config |= ts_dev->bit_xp | STEPCONFIG_INP_AN4 | + ts_dev->bit_xn | ts_dev->bit_yp; break; case 8: - config |= STEPCONFIG_YPP; + config |= ts_dev->bit_yp | STEPCONFIG_INP(ts_dev->inp_xp); break; } @@ -108,9 +170,9 @@ static void titsc_step_config(struct titsc *ts_dev) config = 0; /* Charge step configuration */ - config = STEPCONFIG_XPP | STEPCONFIG_YNN | + config = ts_dev->bit_xp | ts_dev->bit_yn | STEPCHARGE_RFP_XPUL | STEPCHARGE_RFM_XNUR | - STEPCHARGE_INM_AN1 | STEPCHARGE_INP_AN1; + STEPCHARGE_INM_AN1 | STEPCHARGE_INP(ts_dev->inp_yp); titsc_writel(ts_dev, REG_CHARGECONFIG, config); titsc_writel(ts_dev, REG_CHARGEDELAY, CHARGEDLY_OPENDLY); @@ -118,13 +180,14 @@ static void titsc_step_config(struct titsc *ts_dev) config = 0; /* Configure to calculate pressure */ config = STEPCONFIG_MODE_HWSYNC | - STEPCONFIG_AVG_16 | STEPCONFIG_YPP | - STEPCONFIG_XNN | STEPCONFIG_INM_ADCREFM; + STEPCONFIG_AVG_16 | ts_dev->bit_yp | + ts_dev->bit_xn | STEPCONFIG_INM_ADCREFM | + STEPCONFIG_INP(ts_dev->inp_xp); titsc_writel(ts_dev, REG_STEPCONFIG(total_steps + 1), config); titsc_writel(ts_dev, REG_STEPDELAY(total_steps + 1), STEPCONFIG_OPENDLY); - config |= STEPCONFIG_INP_AN3 | STEPCONFIG_FIFO1; + config |= STEPCONFIG_INP(ts_dev->inp_yn) | STEPCONFIG_FIFO1; titsc_writel(ts_dev, REG_STEPCONFIG(total_steps + 2), config); titsc_writel(ts_dev, REG_STEPDELAY(total_steps + 2), STEPCONFIG_OPENDLY); @@ -292,6 +355,8 @@ static int titsc_probe(struct platform_device *pdev) ts_dev->wires = pdata->tsc_init->wires; ts_dev->x_plate_resistance = pdata->tsc_init->x_plate_resistance; ts_dev->steps_to_configure = pdata->tsc_init->steps_to_configure; + memcpy(ts_dev->config_inp, pdata->tsc_init->wire_config, + sizeof(pdata->tsc_init->wire_config)); err = request_irq(ts_dev->irq, titsc_irq, 0, pdev->dev.driver->name, ts_dev); @@ -301,6 +366,11 @@ static int titsc_probe(struct platform_device *pdev) } titsc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO0THRES); + err = titsc_config_wires(ts_dev); + if (err) { + dev_err(&pdev->dev, "wrong i/p wire configuration\n"); + goto err_free_irq; + } titsc_step_config(ts_dev); titsc_writel(ts_dev, REG_FIFO0THR, ts_dev->steps_to_configure); diff --git a/include/linux/input/ti_am335x_tsc.h b/include/linux/input/ti_am335x_tsc.h index 49269a2aa329..6a66b4d1ac2c 100644 --- a/include/linux/input/ti_am335x_tsc.h +++ b/include/linux/input/ti_am335x_tsc.h @@ -12,12 +12,24 @@ * A step configured to read a single * co-ordinate value, can be applied * more number of times for better results. + * @wire_config: Different EVM's could have a different order + * for connecting wires on touchscreen. + * We need to provide an 8 bit number where in + * the 1st four bits represent the analog lines + * and the next 4 bits represent positive/ + * negative terminal on that input line. + * Notations to represent the input lines and + * terminals resoectively is as follows: + * AIN0 = 0, AIN1 = 1 and so on till AIN7 = 7. + * XP = 0, XN = 1, YP = 2, YN = 3. + * */ struct tsc_data { int wires; int x_plate_resistance; int steps_to_configure; + int wire_config[10]; }; #endif diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h index 4258627d076a..e36ae4184917 100644 --- a/include/linux/mfd/ti_am335x_tscadc.h +++ b/include/linux/mfd/ti_am335x_tscadc.h @@ -71,8 +71,6 @@ #define STEPCONFIG_INM_ADCREFM STEPCONFIG_INM(8) #define STEPCONFIG_INP_MASK (0xF << 19) #define STEPCONFIG_INP(val) ((val) << 19) -#define STEPCONFIG_INP_AN2 STEPCONFIG_INP(2) -#define STEPCONFIG_INP_AN3 STEPCONFIG_INP(3) #define STEPCONFIG_INP_AN4 STEPCONFIG_INP(4) #define STEPCONFIG_INP_ADCREFM STEPCONFIG_INP(8) #define STEPCONFIG_FIFO1 BIT(26) @@ -94,7 +92,6 @@ #define STEPCHARGE_INM_AN1 STEPCHARGE_INM(1) #define STEPCHARGE_INP_MASK (0xF << 19) #define STEPCHARGE_INP(val) ((val) << 19) -#define STEPCHARGE_INP_AN1 STEPCHARGE_INP(1) #define STEPCHARGE_RFM_MASK (3 << 23) #define STEPCHARGE_RFM(val) ((val) << 23) #define STEPCHARGE_RFM_XNUR STEPCHARGE_RFM(1) -- cgit v1.2.3 From af9c2fe3740fe8dac05eede8805d9aaa45972cb6 Mon Sep 17 00:00:00 2001 From: "Patil, Rachna" Date: Thu, 24 Jan 2013 03:45:07 +0000 Subject: input: ti_am33x_tsc: remove unwanted fifo flush When touchscreen and ADC are used together, this unwanted fifo flush leads to loss of ADC data. Signed-off-by: Patil, Rachna Signed-off-by: Felipe Balbi Signed-off-by: Sebastian Andrzej Siewior --- drivers/input/touchscreen/ti_am335x_tsc.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c index 2bdd66cd76a7..7b7de6035af7 100644 --- a/drivers/input/touchscreen/ti_am335x_tsc.c +++ b/drivers/input/touchscreen/ti_am335x_tsc.c @@ -252,8 +252,6 @@ static irqreturn_t titsc_irq(int irq, void *dev) unsigned int x = 0, y = 0; unsigned int z1, z2, z; unsigned int fsm; - unsigned int fifo1count, fifo0count; - int i; status = titsc_readl(ts_dev, REG_IRQSTATUS); if (status & IRQENB_FIFO0THRES) { @@ -262,14 +260,6 @@ static irqreturn_t titsc_irq(int irq, void *dev) z1 = titsc_readl(ts_dev, REG_FIFO0) & 0xfff; z2 = titsc_readl(ts_dev, REG_FIFO1) & 0xfff; - fifo1count = titsc_readl(ts_dev, REG_FIFO1CNT); - for (i = 0; i < fifo1count; i++) - titsc_readl(ts_dev, REG_FIFO1); - - fifo0count = titsc_readl(ts_dev, REG_FIFO0CNT); - for (i = 0; i < fifo0count; i++) - titsc_readl(ts_dev, REG_FIFO0); - if (ts_dev->pen_down && z1 != 0 && z2 != 0) { /* * Calculate pressure using formula -- cgit v1.2.3 From 0396310b0eba71595c1151ce7c8fde7a9f33f719 Mon Sep 17 00:00:00 2001 From: "Patil, Rachna" Date: Thu, 24 Jan 2013 03:45:10 +0000 Subject: input: ti_am33x_tsc: Add DT support This patch adds DT support to touch driver. It also provides a binding document which is used by the MFD and IIO part of the device. This patch also renames steps_to_configure to coordinate_readouts because the original name misleads the purpose of the variable. Signed-off-by: Pantelis Antoniou Signed-off-by: Patil, Rachna Signed-off-by: Felipe Balbi Signed-off-by: Sebastian Andrzej Siewior --- .../bindings/input/touchscreen/ti-tsc-adc.txt | 44 +++++++++ drivers/input/touchscreen/ti_am335x_tsc.c | 105 ++++++++++++++++----- drivers/mfd/ti_am335x_tscadc.c | 1 + 3 files changed, 127 insertions(+), 23 deletions(-) create mode 100644 Documentation/devicetree/bindings/input/touchscreen/ti-tsc-adc.txt diff --git a/Documentation/devicetree/bindings/input/touchscreen/ti-tsc-adc.txt b/Documentation/devicetree/bindings/input/touchscreen/ti-tsc-adc.txt new file mode 100644 index 000000000000..491c97b78384 --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/ti-tsc-adc.txt @@ -0,0 +1,44 @@ +* TI - TSC ADC (Touschscreen and analog digital converter) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Required properties: +- child "tsc" + ti,wires: Wires refer to application modes i.e. 4/5/8 wire touchscreen + support on the platform. + ti,x-plate-resistance: X plate resistance + ti,coordiante-readouts: The sequencer supports a total of 16 + programmable steps each step is used to + read a single coordinate. A single + readout is enough but multiple reads can + increase the quality. + A value of 5 means, 5 reads for X, 5 for + Y and 2 for Z (always). This utilises 12 + of the 16 software steps available. The + remaining 4 can be used by the ADC. + ti,wire-config: Different boards could have a different order for + connecting wires on touchscreen. We need to provide an + 8 bit number where in the 1st four bits represent the + analog lines and the next 4 bits represent positive/ + negative terminal on that input line. Notations to + represent the input lines and terminals resoectively + is as follows: + AIN0 = 0, AIN1 = 1 and so on till AIN7 = 7. + XP = 0, XN = 1, YP = 2, YN = 3. +- child "adc" + ti,adc-channels: List of analog inputs available for ADC. + AIN0 = 0, AIN1 = 1 and so on till AIN7 = 7. + +Example: + tscadc: tscadc@44e0d000 { + compatible = "ti,am3359-tscadc"; + tsc { + ti,wires = <4>; + ti,x-plate-resistance = <200>; + ti,coordiante-readouts = <5>; + ti,wire-config = <0x00 0x11 0x22 0x33>; + }; + + adc { + ti,adc-channels = <4 5 6 7>; + }; + } diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c index 7b7de6035af7..449c0fbbe1d6 100644 --- a/drivers/input/touchscreen/ti_am335x_tsc.c +++ b/drivers/input/touchscreen/ti_am335x_tsc.c @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include @@ -47,7 +49,7 @@ struct titsc { unsigned int wires; unsigned int x_plate_resistance; bool pen_down; - int steps_to_configure; + int coordinate_readouts; u32 config_inp[4]; u32 bit_xp, bit_xn, bit_yp, bit_yn; u32 inp_xp, inp_xn, inp_yp, inp_yn; @@ -123,7 +125,7 @@ static void titsc_step_config(struct titsc *ts_dev) int i, total_steps; /* Configure the Step registers */ - total_steps = 2 * ts_dev->steps_to_configure; + total_steps = 2 * ts_dev->coordinate_readouts; config = STEPCONFIG_MODE_HWSYNC | STEPCONFIG_AVG_16 | ts_dev->bit_xp; @@ -141,7 +143,7 @@ static void titsc_step_config(struct titsc *ts_dev) break; } - for (i = 1; i <= ts_dev->steps_to_configure; i++) { + for (i = 1; i <= ts_dev->coordinate_readouts; i++) { titsc_writel(ts_dev, REG_STEPCONFIG(i), config); titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); } @@ -163,7 +165,7 @@ static void titsc_step_config(struct titsc *ts_dev) break; } - for (i = (ts_dev->steps_to_configure + 1); i <= total_steps; i++) { + for (i = (ts_dev->coordinate_readouts + 1); i <= total_steps; i++) { titsc_writel(ts_dev, REG_STEPCONFIG(i), config); titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); } @@ -218,7 +220,7 @@ static void titsc_read_coordinates(struct titsc *ts_dev, read = titsc_readl(ts_dev, REG_FIFO0); channel = read & 0xf0000; channel = channel >> 0x10; - if ((channel >= 0) && (channel < ts_dev->steps_to_configure)) { + if ((channel >= 0) && (channel < ts_dev->coordinate_readouts)) { read &= 0xfff; diff = abs(read - prev_val_x); if (diff < prev_diff_x) { @@ -231,8 +233,8 @@ static void titsc_read_coordinates(struct titsc *ts_dev, read = titsc_readl(ts_dev, REG_FIFO1); channel = read & 0xf0000; channel = channel >> 0x10; - if ((channel >= ts_dev->steps_to_configure) && - (channel < (2 * ts_dev->steps_to_configure - 1))) { + if ((channel >= ts_dev->coordinate_readouts) && + (channel < (2 * ts_dev->coordinate_readouts - 1))) { read &= 0xfff; diff = abs(read - prev_val_y); if (diff < prev_diff_y) { @@ -310,6 +312,59 @@ static irqreturn_t titsc_irq(int irq, void *dev) return IRQ_HANDLED; } +static int titsc_parse_dt(struct platform_device *pdev, + struct titsc *ts_dev) +{ + struct device_node *node = pdev->dev.of_node; + int err; + + if (!node) + return -EINVAL; + + err = of_property_read_u32(node, "ti,wires", &ts_dev->wires); + if (err < 0) + return err; + switch (ts_dev->wires) { + case 4: + case 5: + case 8: + break; + default: + return -EINVAL; + } + + err = of_property_read_u32(node, "ti,x-plate-resistance", + &ts_dev->x_plate_resistance); + if (err < 0) + return err; + + err = of_property_read_u32(node, "ti,coordiante-readouts", + &ts_dev->coordinate_readouts); + if (err < 0) + return err; + + return of_property_read_u32_array(node, "ti,wire-config", + ts_dev->config_inp, ARRAY_SIZE(ts_dev->config_inp)); +} + +static int titsc_parse_pdata(struct ti_tscadc_dev *tscadc_dev, + struct titsc *ts_dev) +{ + struct mfd_tscadc_board *pdata = tscadc_dev->dev->platform_data; + + if (!pdata) + return -EINVAL; + + ts_dev->wires = pdata->tsc_init->wires; + ts_dev->x_plate_resistance = + pdata->tsc_init->x_plate_resistance; + ts_dev->steps_to_configure = + pdata->tsc_init->steps_to_configure; + memcpy(ts_dev->config_inp, pdata->tsc_init->wire_config, + sizeof(pdata->tsc_init->wire_config)); + return 0; +} + /* * The functions for inserting/removing driver as a module. */ @@ -319,16 +374,8 @@ static int titsc_probe(struct platform_device *pdev) struct titsc *ts_dev; struct input_dev *input_dev; struct ti_tscadc_dev *tscadc_dev = ti_tscadc_dev_get(pdev); - struct mfd_tscadc_board *pdata; int err; - pdata = tscadc_dev->dev->platform_data; - - if (!pdata) { - dev_err(&pdev->dev, "Could not find platform data\n"); - return -EINVAL; - } - /* Allocate memory for device */ ts_dev = kzalloc(sizeof(struct titsc), GFP_KERNEL); input_dev = input_allocate_device(); @@ -342,11 +389,16 @@ static int titsc_probe(struct platform_device *pdev) ts_dev->mfd_tscadc = tscadc_dev; ts_dev->input = input_dev; ts_dev->irq = tscadc_dev->irq; - ts_dev->wires = pdata->tsc_init->wires; - ts_dev->x_plate_resistance = pdata->tsc_init->x_plate_resistance; - ts_dev->steps_to_configure = pdata->tsc_init->steps_to_configure; - memcpy(ts_dev->config_inp, pdata->tsc_init->wire_config, - sizeof(pdata->tsc_init->wire_config)); + + if (tscadc_dev->dev->platform_data) + err = titsc_parse_pdata(tscadc_dev, ts_dev); + else + err = titsc_parse_dt(pdev, ts_dev); + + if (err) { + dev_err(&pdev->dev, "Could not find valid DT data.\n"); + goto err_free_mem; + } err = request_irq(ts_dev->irq, titsc_irq, 0, pdev->dev.driver->name, ts_dev); @@ -362,7 +414,7 @@ static int titsc_probe(struct platform_device *pdev) goto err_free_irq; } titsc_step_config(ts_dev); - titsc_writel(ts_dev, REG_FIFO0THR, ts_dev->steps_to_configure); + titsc_writel(ts_dev, REG_FIFO0THR, ts_dev->coordinate_readouts); input_dev->name = "ti-tsc"; input_dev->dev.parent = &pdev->dev; @@ -398,7 +450,7 @@ static int titsc_remove(struct platform_device *pdev) free_irq(ts_dev->irq, ts_dev); /* total steps followed by the enable mask */ - steps = 2 * ts_dev->steps_to_configure + 2; + steps = 2 * ts_dev->coordinate_readouts + 2; steps = (1 << steps) - 1; am335x_tsc_se_clr(ts_dev->mfd_tscadc, steps); @@ -439,7 +491,7 @@ static int titsc_resume(struct device *dev) } titsc_step_config(ts_dev); titsc_writel(ts_dev, REG_FIFO0THR, - ts_dev->steps_to_configure); + ts_dev->coordinate_readouts); return 0; } @@ -452,6 +504,12 @@ static const struct dev_pm_ops titsc_pm_ops = { #define TITSC_PM_OPS NULL #endif +static const struct of_device_id ti_tsc_dt_ids[] = { + { .compatible = "ti,am3359-tsc", }, + { } +}; +MODULE_DEVICE_TABLE(of, ti_tsc_dt_ids); + static struct platform_driver ti_tsc_driver = { .probe = titsc_probe, .remove = titsc_remove, @@ -459,6 +517,7 @@ static struct platform_driver ti_tsc_driver = { .name = "tsc", .owner = THIS_MODULE, .pm = TITSC_PM_OPS, + .of_match_table = of_match_ptr(ti_tsc_dt_ids), }, }; module_platform_driver(ti_tsc_driver); diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c index 90ccfc07e16b..f50976623a01 100644 --- a/drivers/mfd/ti_am335x_tscadc.c +++ b/drivers/mfd/ti_am335x_tscadc.c @@ -203,6 +203,7 @@ static int ti_tscadc_probe(struct platform_device *pdev) /* TSC Cell */ cell = &tscadc->cells[TSC_CELL]; cell->name = "tsc"; + cell->of_compatible = "ti,am3359-tsc"; cell->platform_data = &tscadc; cell->pdata_size = sizeof(tscadc); -- cgit v1.2.3 From b9194fdfa6e729b97ffc59ae00dc9d51c7ae314d Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 21 May 2013 17:39:13 +0200 Subject: input: ti_am33x_tsc: remove platform_data support This patch removes access to platform data mfd_tscadc_board because the platform is DT only. Acked-by: Dmitry Torokhov Signed-off-by: Sebastian Andrzej Siewior --- drivers/input/touchscreen/ti_am335x_tsc.c | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c index 449c0fbbe1d6..a1db55d1a862 100644 --- a/drivers/input/touchscreen/ti_am335x_tsc.c +++ b/drivers/input/touchscreen/ti_am335x_tsc.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -347,24 +346,6 @@ static int titsc_parse_dt(struct platform_device *pdev, ts_dev->config_inp, ARRAY_SIZE(ts_dev->config_inp)); } -static int titsc_parse_pdata(struct ti_tscadc_dev *tscadc_dev, - struct titsc *ts_dev) -{ - struct mfd_tscadc_board *pdata = tscadc_dev->dev->platform_data; - - if (!pdata) - return -EINVAL; - - ts_dev->wires = pdata->tsc_init->wires; - ts_dev->x_plate_resistance = - pdata->tsc_init->x_plate_resistance; - ts_dev->steps_to_configure = - pdata->tsc_init->steps_to_configure; - memcpy(ts_dev->config_inp, pdata->tsc_init->wire_config, - sizeof(pdata->tsc_init->wire_config)); - return 0; -} - /* * The functions for inserting/removing driver as a module. */ @@ -390,11 +371,7 @@ static int titsc_probe(struct platform_device *pdev) ts_dev->input = input_dev; ts_dev->irq = tscadc_dev->irq; - if (tscadc_dev->dev->platform_data) - err = titsc_parse_pdata(tscadc_dev, ts_dev); - else - err = titsc_parse_dt(pdev, ts_dev); - + err = titsc_parse_dt(pdev, ts_dev); if (err) { dev_err(&pdev->dev, "Could not find valid DT data.\n"); goto err_free_mem; -- cgit v1.2.3 From 6f39ac4e20c6211c98e8d9da2d8c51100a77d1df Mon Sep 17 00:00:00 2001 From: "Patil, Rachna" Date: Thu, 24 Jan 2013 03:45:11 +0000 Subject: iio: ti_am335x_adc: Add DT support Add DT support for client ADC driver. Acked-by: Jonathan Cameron Signed-off-by: Patil, Rachna Signed-off-by: Pantelis Antoniou Signed-off-by: Felipe Balbi Signed-off-by: Sebastian Andrzej Siewior --- drivers/iio/adc/ti_am335x_adc.c | 29 ++++++++++++++++++++++++----- drivers/mfd/ti_am335x_tscadc.c | 1 + 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c index 543b9c42ac5f..b24402cb5432 100644 --- a/drivers/iio/adc/ti_am335x_adc.c +++ b/drivers/iio/adc/ti_am335x_adc.c @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include @@ -152,11 +154,12 @@ static int tiadc_probe(struct platform_device *pdev) struct iio_dev *indio_dev; struct tiadc_device *adc_dev; struct ti_tscadc_dev *tscadc_dev = ti_tscadc_dev_get(pdev); - struct mfd_tscadc_board *pdata; + struct mfd_tscadc_board *pdata = tscadc_dev->dev->platform_data; + struct device_node *node = pdev->dev.of_node; int err; + u32 val32; - pdata = tscadc_dev->dev->platform_data; - if (!pdata || !pdata->adc_init) { + if (!pdata && !node) { dev_err(&pdev->dev, "Could not find platform data\n"); return -EINVAL; } @@ -169,8 +172,17 @@ static int tiadc_probe(struct platform_device *pdev) } adc_dev = iio_priv(indio_dev); - adc_dev->mfd_tscadc = tscadc_dev; - adc_dev->channels = pdata->adc_init->adc_channels; + adc_dev->mfd_tscadc = ti_tscadc_dev_get(pdev); + + if (pdata) + adc_dev->channels = pdata->adc_init->adc_channels; + else { + err = of_property_read_u32(node, + "ti,adc-channels", &val32); + if (err < 0) + goto err_free_device; + adc_dev->channels = val32; + } indio_dev->dev.parent = &pdev->dev; indio_dev->name = dev_name(&pdev->dev); @@ -260,11 +272,18 @@ static const struct dev_pm_ops tiadc_pm_ops = { #define TIADC_PM_OPS NULL #endif +static const struct of_device_id ti_adc_dt_ids[] = { + { .compatible = "ti,am3359-adc", }, + { } +}; +MODULE_DEVICE_TABLE(of, ti_adc_dt_ids); + static struct platform_driver tiadc_driver = { .driver = { .name = "tiadc", .owner = THIS_MODULE, .pm = TIADC_PM_OPS, + .of_match_table = of_match_ptr(ti_adc_dt_ids), }, .probe = tiadc_probe, .remove = tiadc_remove, diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c index f50976623a01..1d6c74049590 100644 --- a/drivers/mfd/ti_am335x_tscadc.c +++ b/drivers/mfd/ti_am335x_tscadc.c @@ -210,6 +210,7 @@ static int ti_tscadc_probe(struct platform_device *pdev) /* ADC Cell */ cell = &tscadc->cells[ADC_CELL]; cell->name = "tiadc"; + cell->of_compatible = "ti,am3359-adc"; cell->platform_data = &tscadc; cell->pdata_size = sizeof(tscadc); -- cgit v1.2.3 From 0ead4fb22a5f1b31fee966117604e3be9cdeb2fb Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 21 May 2013 17:49:22 +0200 Subject: iio: ti_am335x_adc: remove platform_data support This patch removes access to platform data mfd_tscadc_board because the platform is DT only. Acked-by: Jonathan Cameron Signed-off-by: Sebastian Andrzej Siewior --- drivers/iio/adc/ti_am335x_adc.c | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c index b24402cb5432..2868c0c1b7e0 100644 --- a/drivers/iio/adc/ti_am335x_adc.c +++ b/drivers/iio/adc/ti_am335x_adc.c @@ -26,7 +26,6 @@ #include #include -#include struct tiadc_device { struct ti_tscadc_dev *mfd_tscadc; @@ -153,14 +152,12 @@ static int tiadc_probe(struct platform_device *pdev) { struct iio_dev *indio_dev; struct tiadc_device *adc_dev; - struct ti_tscadc_dev *tscadc_dev = ti_tscadc_dev_get(pdev); - struct mfd_tscadc_board *pdata = tscadc_dev->dev->platform_data; struct device_node *node = pdev->dev.of_node; int err; u32 val32; - if (!pdata && !node) { - dev_err(&pdev->dev, "Could not find platform data\n"); + if (!node) { + dev_err(&pdev->dev, "Could not find valid DT data.\n"); return -EINVAL; } @@ -174,15 +171,11 @@ static int tiadc_probe(struct platform_device *pdev) adc_dev->mfd_tscadc = ti_tscadc_dev_get(pdev); - if (pdata) - adc_dev->channels = pdata->adc_init->adc_channels; - else { - err = of_property_read_u32(node, - "ti,adc-channels", &val32); - if (err < 0) - goto err_free_device; - adc_dev->channels = val32; - } + err = of_property_read_u32(node, + "ti,adc-channels", &val32); + if (err < 0) + goto err_free_device; + adc_dev->channels = val32; indio_dev->dev.parent = &pdev->dev; indio_dev->name = dev_name(&pdev->dev); -- cgit v1.2.3 From 997b05203b0a710e11f9b2732bef2d2fdc1d824b Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 11 Jun 2013 13:10:16 +0800 Subject: ASoC: add RT5640 CODEC driver This patch adds the ALC5640 codec driver. Signed-off-by: Stephen Warren Signed-off-by: Bard Liao Tested-by: Stephen Warren Signed-off-by: Mark Brown --- include/sound/rt5640.h | 22 + sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/rt5640.c | 2092 +++++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/rt5640.h | 2092 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 4212 insertions(+) create mode 100644 include/sound/rt5640.h create mode 100644 sound/soc/codecs/rt5640.c create mode 100644 sound/soc/codecs/rt5640.h diff --git a/include/sound/rt5640.h b/include/sound/rt5640.h new file mode 100644 index 000000000000..27cc75ed67f8 --- /dev/null +++ b/include/sound/rt5640.h @@ -0,0 +1,22 @@ +/* + * linux/sound/rt5640.h -- Platform data for RT5640 + * + * Copyright 2011 Realtek Microelectronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __LINUX_SND_RT5640_H +#define __LINUX_SND_RT5640_H + +struct rt5640_platform_data { + /* IN1 & IN2 can optionally be differential */ + bool in1_diff; + bool in2_diff; + + int ldo1_en; /* GPIO for LDO1_EN */ +}; + +#endif diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 2f45f00e31b0..04c87f77b7e8 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -56,6 +56,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_OMAP_HDMI_CODEC if OMAP4_DSS_HDMI select SND_SOC_PCM3008 select SND_SOC_RT5631 if I2C + select SND_SOC_RT5640 if I2C select SND_SOC_SGTL5000 if I2C select SND_SOC_SI476X if MFD_SI476X_CORE select SND_SOC_SN95031 if INTEL_SCU_IPC @@ -296,6 +297,9 @@ config SND_SOC_PCM3008 config SND_SOC_RT5631 tristate +config SND_SOC_RT5640 + tristate + #Freescale sgtl5000 codec config SND_SOC_SGTL5000 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index b9e41c9a1f4c..732c4a7bd6ac 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -44,6 +44,7 @@ snd-soc-ml26124-objs := ml26124.o snd-soc-omap-hdmi-codec-objs := omap-hdmi.o snd-soc-pcm3008-objs := pcm3008.o snd-soc-rt5631-objs := rt5631.o +snd-soc-rt5640-objs := rt5640.o snd-soc-sgtl5000-objs := sgtl5000.o snd-soc-alc5623-objs := alc5623.o snd-soc-alc5632-objs := alc5632.o @@ -171,6 +172,7 @@ obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o obj-$(CONFIG_SND_SOC_OMAP_HDMI_CODEC) += snd-soc-omap-hdmi-codec.o obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o +obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o obj-$(CONFIG_SND_SOC_SI476X) += snd-soc-si476x.o diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c new file mode 100644 index 000000000000..288c17cd6023 --- /dev/null +++ b/sound/soc/codecs/rt5640.c @@ -0,0 +1,2092 @@ +/* + * rt5640.c -- RT5640 ALSA SoC audio codec driver + * + * Copyright 2011 Realtek Semiconductor Corp. + * Author: Johnny Hsu + * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt5640.h" + +#define RT5640_DEVICE_ID 0x6231 + +#define RT5640_PR_RANGE_BASE (0xff + 1) +#define RT5640_PR_SPACING 0x100 + +#define RT5640_PR_BASE (RT5640_PR_RANGE_BASE + (0 * RT5640_PR_SPACING)) + +static const struct regmap_range_cfg rt5640_ranges[] = { + { .name = "PR", .range_min = RT5640_PR_BASE, + .range_max = RT5640_PR_BASE + 0xb4, + .selector_reg = RT5640_PRIV_INDEX, + .selector_mask = 0xff, + .selector_shift = 0x0, + .window_start = RT5640_PRIV_DATA, + .window_len = 0x1, }, +}; + +static struct reg_default init_list[] = { + {RT5640_PR_BASE + 0x3d, 0x3600}, + {RT5640_PR_BASE + 0x1c, 0x0D21}, + {RT5640_PR_BASE + 0x1b, 0x0000}, + {RT5640_PR_BASE + 0x12, 0x0aa8}, + {RT5640_PR_BASE + 0x14, 0x0aaa}, + {RT5640_PR_BASE + 0x20, 0x6110}, + {RT5640_PR_BASE + 0x21, 0xe0e0}, + {RT5640_PR_BASE + 0x23, 0x1804}, +}; +#define RT5640_INIT_REG_LEN ARRAY_SIZE(init_list) + +static const struct reg_default rt5640_reg[RT5640_VENDOR_ID2 + 1] = { + { 0x00, 0x000e }, + { 0x01, 0xc8c8 }, + { 0x02, 0xc8c8 }, + { 0x03, 0xc8c8 }, + { 0x04, 0x8000 }, + { 0x0d, 0x0000 }, + { 0x0e, 0x0000 }, + { 0x0f, 0x0808 }, + { 0x19, 0xafaf }, + { 0x1a, 0xafaf }, + { 0x1b, 0x0000 }, + { 0x1c, 0x2f2f }, + { 0x1d, 0x2f2f }, + { 0x1e, 0x0000 }, + { 0x27, 0x7060 }, + { 0x28, 0x7070 }, + { 0x29, 0x8080 }, + { 0x2a, 0x5454 }, + { 0x2b, 0x5454 }, + { 0x2c, 0xaa00 }, + { 0x2d, 0x0000 }, + { 0x2e, 0xa000 }, + { 0x2f, 0x0000 }, + { 0x3b, 0x0000 }, + { 0x3c, 0x007f }, + { 0x3d, 0x0000 }, + { 0x3e, 0x007f }, + { 0x45, 0xe000 }, + { 0x46, 0x003e }, + { 0x47, 0x003e }, + { 0x48, 0xf800 }, + { 0x49, 0x3800 }, + { 0x4a, 0x0004 }, + { 0x4c, 0xfc00 }, + { 0x4d, 0x0000 }, + { 0x4f, 0x01ff }, + { 0x50, 0x0000 }, + { 0x51, 0x0000 }, + { 0x52, 0x01ff }, + { 0x53, 0xf000 }, + { 0x61, 0x0000 }, + { 0x62, 0x0000 }, + { 0x63, 0x00c0 }, + { 0x64, 0x0000 }, + { 0x65, 0x0000 }, + { 0x66, 0x0000 }, + { 0x6a, 0x0000 }, + { 0x6c, 0x0000 }, + { 0x70, 0x8000 }, + { 0x71, 0x8000 }, + { 0x72, 0x8000 }, + { 0x73, 0x1114 }, + { 0x74, 0x0c00 }, + { 0x75, 0x1d00 }, + { 0x80, 0x0000 }, + { 0x81, 0x0000 }, + { 0x82, 0x0000 }, + { 0x83, 0x0000 }, + { 0x84, 0x0000 }, + { 0x85, 0x0008 }, + { 0x89, 0x0000 }, + { 0x8a, 0x0000 }, + { 0x8b, 0x0600 }, + { 0x8c, 0x0228 }, + { 0x8d, 0xa000 }, + { 0x8e, 0x0004 }, + { 0x8f, 0x1100 }, + { 0x90, 0x0646 }, + { 0x91, 0x0c00 }, + { 0x92, 0x0000 }, + { 0x93, 0x3000 }, + { 0xb0, 0x2080 }, + { 0xb1, 0x0000 }, + { 0xb4, 0x2206 }, + { 0xb5, 0x1f00 }, + { 0xb6, 0x0000 }, + { 0xb8, 0x034b }, + { 0xb9, 0x0066 }, + { 0xba, 0x000b }, + { 0xbb, 0x0000 }, + { 0xbc, 0x0000 }, + { 0xbd, 0x0000 }, + { 0xbe, 0x0000 }, + { 0xbf, 0x0000 }, + { 0xc0, 0x0400 }, + { 0xc2, 0x0000 }, + { 0xc4, 0x0000 }, + { 0xc5, 0x0000 }, + { 0xc6, 0x2000 }, + { 0xc8, 0x0000 }, + { 0xc9, 0x0000 }, + { 0xca, 0x0000 }, + { 0xcb, 0x0000 }, + { 0xcc, 0x0000 }, + { 0xcf, 0x0013 }, + { 0xd0, 0x0680 }, + { 0xd1, 0x1c17 }, + { 0xd2, 0x8c00 }, + { 0xd3, 0xaa20 }, + { 0xd6, 0x0400 }, + { 0xd9, 0x0809 }, + { 0xfe, 0x10ec }, + { 0xff, 0x6231 }, +}; + +static int rt5640_reset(struct snd_soc_codec *codec) +{ + return snd_soc_write(codec, RT5640_RESET, 0); +} + +static bool rt5640_volatile_register(struct device *dev, unsigned int reg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rt5640_ranges); i++) + if ((reg >= rt5640_ranges[i].window_start && + reg <= rt5640_ranges[i].window_start + + rt5640_ranges[i].window_len) || + (reg >= rt5640_ranges[i].range_min && + reg <= rt5640_ranges[i].range_max)) + return true; + + switch (reg) { + case RT5640_RESET: + case RT5640_ASRC_5: + case RT5640_EQ_CTRL1: + case RT5640_DRC_AGC_1: + case RT5640_ANC_CTRL1: + case RT5640_IRQ_CTRL2: + case RT5640_INT_IRQ_ST: + case RT5640_DSP_CTRL2: + case RT5640_DSP_CTRL3: + case RT5640_PRIV_INDEX: + case RT5640_PRIV_DATA: + case RT5640_PGM_REG_ARR1: + case RT5640_PGM_REG_ARR3: + case RT5640_VENDOR_ID: + case RT5640_VENDOR_ID1: + case RT5640_VENDOR_ID2: + return true; + default: + return false; + } +} + +static bool rt5640_readable_register(struct device *dev, unsigned int reg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(rt5640_ranges); i++) + if ((reg >= rt5640_ranges[i].window_start && + reg <= rt5640_ranges[i].window_start + + rt5640_ranges[i].window_len) || + (reg >= rt5640_ranges[i].range_min && + reg <= rt5640_ranges[i].range_max)) + return true; + + switch (reg) { + case RT5640_RESET: + case RT5640_SPK_VOL: + case RT5640_HP_VOL: + case RT5640_OUTPUT: + case RT5640_MONO_OUT: + case RT5640_IN1_IN2: + case RT5640_IN3_IN4: + case RT5640_INL_INR_VOL: + case RT5640_DAC1_DIG_VOL: + case RT5640_DAC2_DIG_VOL: + case RT5640_DAC2_CTRL: + case RT5640_ADC_DIG_VOL: + case RT5640_ADC_DATA: + case RT5640_ADC_BST_VOL: + case RT5640_STO_ADC_MIXER: + case RT5640_MONO_ADC_MIXER: + case RT5640_AD_DA_MIXER: + case RT5640_STO_DAC_MIXER: + case RT5640_MONO_DAC_MIXER: + case RT5640_DIG_MIXER: + case RT5640_DSP_PATH1: + case RT5640_DSP_PATH2: + case RT5640_DIG_INF_DATA: + case RT5640_REC_L1_MIXER: + case RT5640_REC_L2_MIXER: + case RT5640_REC_R1_MIXER: + case RT5640_REC_R2_MIXER: + case RT5640_HPO_MIXER: + case RT5640_SPK_L_MIXER: + case RT5640_SPK_R_MIXER: + case RT5640_SPO_L_MIXER: + case RT5640_SPO_R_MIXER: + case RT5640_SPO_CLSD_RATIO: + case RT5640_MONO_MIXER: + case RT5640_OUT_L1_MIXER: + case RT5640_OUT_L2_MIXER: + case RT5640_OUT_L3_MIXER: + case RT5640_OUT_R1_MIXER: + case RT5640_OUT_R2_MIXER: + case RT5640_OUT_R3_MIXER: + case RT5640_LOUT_MIXER: + case RT5640_PWR_DIG1: + case RT5640_PWR_DIG2: + case RT5640_PWR_ANLG1: + case RT5640_PWR_ANLG2: + case RT5640_PWR_MIXER: + case RT5640_PWR_VOL: + case RT5640_PRIV_INDEX: + case RT5640_PRIV_DATA: + case RT5640_I2S1_SDP: + case RT5640_I2S2_SDP: + case RT5640_ADDA_CLK1: + case RT5640_ADDA_CLK2: + case RT5640_DMIC: + case RT5640_GLB_CLK: + case RT5640_PLL_CTRL1: + case RT5640_PLL_CTRL2: + case RT5640_ASRC_1: + case RT5640_ASRC_2: + case RT5640_ASRC_3: + case RT5640_ASRC_4: + case RT5640_ASRC_5: + case RT5640_HP_OVCD: + case RT5640_CLS_D_OVCD: + case RT5640_CLS_D_OUT: + case RT5640_DEPOP_M1: + case RT5640_DEPOP_M2: + case RT5640_DEPOP_M3: + case RT5640_CHARGE_PUMP: + case RT5640_PV_DET_SPK_G: + case RT5640_MICBIAS: + case RT5640_EQ_CTRL1: + case RT5640_EQ_CTRL2: + case RT5640_WIND_FILTER: + case RT5640_DRC_AGC_1: + case RT5640_DRC_AGC_2: + case RT5640_DRC_AGC_3: + case RT5640_SVOL_ZC: + case RT5640_ANC_CTRL1: + case RT5640_ANC_CTRL2: + case RT5640_ANC_CTRL3: + case RT5640_JD_CTRL: + case RT5640_ANC_JD: + case RT5640_IRQ_CTRL1: + case RT5640_IRQ_CTRL2: + case RT5640_INT_IRQ_ST: + case RT5640_GPIO_CTRL1: + case RT5640_GPIO_CTRL2: + case RT5640_GPIO_CTRL3: + case RT5640_DSP_CTRL1: + case RT5640_DSP_CTRL2: + case RT5640_DSP_CTRL3: + case RT5640_DSP_CTRL4: + case RT5640_PGM_REG_ARR1: + case RT5640_PGM_REG_ARR2: + case RT5640_PGM_REG_ARR3: + case RT5640_PGM_REG_ARR4: + case RT5640_PGM_REG_ARR5: + case RT5640_SCB_FUNC: + case RT5640_SCB_CTRL: + case RT5640_BASE_BACK: + case RT5640_MP3_PLUS1: + case RT5640_MP3_PLUS2: + case RT5640_3D_HP: + case RT5640_ADJ_HPF: + case RT5640_HP_CALIB_AMP_DET: + case RT5640_HP_CALIB2: + case RT5640_SV_ZCD1: + case RT5640_SV_ZCD2: + case RT5640_DUMMY1: + case RT5640_DUMMY2: + case RT5640_DUMMY3: + case RT5640_VENDOR_ID: + case RT5640_VENDOR_ID1: + case RT5640_VENDOR_ID2: + return true; + default: + return false; + } +} + +static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0); +static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0); +static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0); +static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0); +static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0); + +/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */ +static unsigned int bst_tlv[] = { + TLV_DB_RANGE_HEAD(7), + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), + 1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0), + 2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0), + 3, 5, TLV_DB_SCALE_ITEM(3000, 500, 0), + 6, 6, TLV_DB_SCALE_ITEM(4400, 0, 0), + 7, 7, TLV_DB_SCALE_ITEM(5000, 0, 0), + 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0), +}; + +/* Interface data select */ +static const char * const rt5640_data_select[] = { + "Normal", "left copy to right", "right copy to left", "Swap"}; + +static const SOC_ENUM_SINGLE_DECL(rt5640_if1_dac_enum, RT5640_DIG_INF_DATA, + RT5640_IF1_DAC_SEL_SFT, rt5640_data_select); + +static const SOC_ENUM_SINGLE_DECL(rt5640_if1_adc_enum, RT5640_DIG_INF_DATA, + RT5640_IF1_ADC_SEL_SFT, rt5640_data_select); + +static const SOC_ENUM_SINGLE_DECL(rt5640_if2_dac_enum, RT5640_DIG_INF_DATA, + RT5640_IF2_DAC_SEL_SFT, rt5640_data_select); + +static const SOC_ENUM_SINGLE_DECL(rt5640_if2_adc_enum, RT5640_DIG_INF_DATA, + RT5640_IF2_ADC_SEL_SFT, rt5640_data_select); + +/* Class D speaker gain ratio */ +static const char * const rt5640_clsd_spk_ratio[] = {"1.66x", "1.83x", "1.94x", + "2x", "2.11x", "2.22x", "2.33x", "2.44x", "2.55x", "2.66x", "2.77x"}; + +static const SOC_ENUM_SINGLE_DECL( + rt5640_clsd_spk_ratio_enum, RT5640_CLS_D_OUT, + RT5640_CLSD_RATIO_SFT, rt5640_clsd_spk_ratio); + +static const struct snd_kcontrol_new rt5640_snd_controls[] = { + /* Speaker Output Volume */ + SOC_DOUBLE("Speaker Playback Switch", RT5640_SPK_VOL, + RT5640_L_MUTE_SFT, RT5640_R_MUTE_SFT, 1, 1), + SOC_DOUBLE("Speaker Channel Switch", RT5640_SPK_VOL, + RT5640_VOL_L_SFT, RT5640_VOL_R_SFT, 1, 1), + SOC_DOUBLE_TLV("Speaker Playback Volume", RT5640_SPK_VOL, + RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, 39, 1, out_vol_tlv), + /* Headphone Output Volume */ + SOC_DOUBLE("HP Playback Switch", RT5640_HP_VOL, + RT5640_L_MUTE_SFT, RT5640_R_MUTE_SFT, 1, 1), + SOC_DOUBLE("HP Channel Switch", RT5640_HP_VOL, + RT5640_VOL_L_SFT, RT5640_VOL_R_SFT, 1, 1), + SOC_DOUBLE_TLV("HP Playback Volume", RT5640_HP_VOL, + RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, 39, 1, out_vol_tlv), + /* OUTPUT Control */ + SOC_DOUBLE("OUT Playback Switch", RT5640_OUTPUT, + RT5640_L_MUTE_SFT, RT5640_R_MUTE_SFT, 1, 1), + SOC_DOUBLE("OUT Channel Switch", RT5640_OUTPUT, + RT5640_VOL_L_SFT, RT5640_VOL_R_SFT, 1, 1), + SOC_DOUBLE_TLV("OUT Playback Volume", RT5640_OUTPUT, + RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, 39, 1, out_vol_tlv), + /* MONO Output Control */ + SOC_SINGLE("Mono Playback Switch", RT5640_MONO_OUT, + RT5640_L_MUTE_SFT, 1, 1), + /* DAC Digital Volume */ + SOC_DOUBLE("DAC2 Playback Switch", RT5640_DAC2_CTRL, + RT5640_M_DAC_L2_VOL_SFT, RT5640_M_DAC_R2_VOL_SFT, 1, 1), + SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5640_DAC1_DIG_VOL, + RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, + 175, 0, dac_vol_tlv), + SOC_DOUBLE_TLV("Mono DAC Playback Volume", RT5640_DAC2_DIG_VOL, + RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, + 175, 0, dac_vol_tlv), + /* IN1/IN2 Control */ + SOC_SINGLE_TLV("IN1 Boost", RT5640_IN1_IN2, + RT5640_BST_SFT1, 8, 0, bst_tlv), + SOC_SINGLE_TLV("IN2 Boost", RT5640_IN3_IN4, + RT5640_BST_SFT2, 8, 0, bst_tlv), + /* INL/INR Volume Control */ + SOC_DOUBLE_TLV("IN Capture Volume", RT5640_INL_INR_VOL, + RT5640_INL_VOL_SFT, RT5640_INR_VOL_SFT, + 31, 1, in_vol_tlv), + /* ADC Digital Volume Control */ + SOC_DOUBLE("ADC Capture Switch", RT5640_ADC_DIG_VOL, + RT5640_L_MUTE_SFT, RT5640_R_MUTE_SFT, 1, 1), + SOC_DOUBLE_TLV("ADC Capture Volume", RT5640_ADC_DIG_VOL, + RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, + 127, 0, adc_vol_tlv), + SOC_DOUBLE_TLV("Mono ADC Capture Volume", RT5640_ADC_DATA, + RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, + 127, 0, adc_vol_tlv), + /* ADC Boost Volume Control */ + SOC_DOUBLE_TLV("ADC Boost Gain", RT5640_ADC_BST_VOL, + RT5640_ADC_L_BST_SFT, RT5640_ADC_R_BST_SFT, + 3, 0, adc_bst_tlv), + /* Class D speaker gain ratio */ + SOC_ENUM("Class D SPK Ratio Control", rt5640_clsd_spk_ratio_enum), + + SOC_ENUM("ADC IF1 Data Switch", rt5640_if1_adc_enum), + SOC_ENUM("DAC IF1 Data Switch", rt5640_if1_dac_enum), + SOC_ENUM("ADC IF2 Data Switch", rt5640_if2_adc_enum), + SOC_ENUM("DAC IF2 Data Switch", rt5640_if2_dac_enum), +}; + +/** + * set_dmic_clk - Set parameter of dmic. + * + * @w: DAPM widget. + * @kcontrol: The kcontrol of this widget. + * @event: Event id. + * + * Choose dmic clock between 1MHz and 3MHz. + * It is better for clock to approximate 3MHz. + */ +static int set_dmic_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec); + int div[] = {2, 3, 4, 6, 8, 12}; + int idx = -EINVAL, i; + int rate, red, bound, temp; + + rate = rt5640->sysclk; + red = 3000000 * 12; + for (i = 0; i < ARRAY_SIZE(div); i++) { + bound = div[i] * 3000000; + if (rate > bound) + continue; + temp = bound - rate; + if (temp < red) { + red = temp; + idx = i; + } + } + if (idx < 0) + dev_err(codec->dev, "Failed to set DMIC clock\n"); + else + snd_soc_update_bits(codec, RT5640_DMIC, RT5640_DMIC_CLK_MASK, + idx << RT5640_DMIC_CLK_SFT); + return idx; +} + +static int check_sysclk1_source(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + unsigned int val; + + val = snd_soc_read(source->codec, RT5640_GLB_CLK); + val &= RT5640_SCLK_SRC_MASK; + if (val == RT5640_SCLK_SRC_PLL1 || val == RT5640_SCLK_SRC_PLL1T) + return 1; + else + return 0; +} + +/* Digital Mixer */ +static const struct snd_kcontrol_new rt5640_sto_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5640_STO_ADC_MIXER, + RT5640_M_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5640_STO_ADC_MIXER, + RT5640_M_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_sto_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5640_STO_ADC_MIXER, + RT5640_M_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5640_STO_ADC_MIXER, + RT5640_M_ADC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_mono_adc_l_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5640_MONO_ADC_MIXER, + RT5640_M_MONO_ADC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5640_MONO_ADC_MIXER, + RT5640_M_MONO_ADC_L2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_mono_adc_r_mix[] = { + SOC_DAPM_SINGLE("ADC1 Switch", RT5640_MONO_ADC_MIXER, + RT5640_M_MONO_ADC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("ADC2 Switch", RT5640_MONO_ADC_MIXER, + RT5640_M_MONO_ADC_R2_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_dac_l_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5640_AD_DA_MIXER, + RT5640_M_ADCMIX_L_SFT, 1, 1), + SOC_DAPM_SINGLE("INF1 Switch", RT5640_AD_DA_MIXER, + RT5640_M_IF1_DAC_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_dac_r_mix[] = { + SOC_DAPM_SINGLE("Stereo ADC Switch", RT5640_AD_DA_MIXER, + RT5640_M_ADCMIX_R_SFT, 1, 1), + SOC_DAPM_SINGLE("INF1 Switch", RT5640_AD_DA_MIXER, + RT5640_M_IF1_DAC_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_sto_dac_l_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_STO_DAC_MIXER, + RT5640_M_DAC_L1_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_STO_DAC_MIXER, + RT5640_M_DAC_L2_SFT, 1, 1), + SOC_DAPM_SINGLE("ANC Switch", RT5640_STO_DAC_MIXER, + RT5640_M_ANC_DAC_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_sto_dac_r_mix[] = { + SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_STO_DAC_MIXER, + RT5640_M_DAC_R1_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_STO_DAC_MIXER, + RT5640_M_DAC_R2_SFT, 1, 1), + SOC_DAPM_SINGLE("ANC Switch", RT5640_STO_DAC_MIXER, + RT5640_M_ANC_DAC_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_mono_dac_l_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_MONO_DAC_MIXER, + RT5640_M_DAC_L1_MONO_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_MONO_DAC_MIXER, + RT5640_M_DAC_L2_MONO_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_MONO_DAC_MIXER, + RT5640_M_DAC_R2_MONO_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_mono_dac_r_mix[] = { + SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_MONO_DAC_MIXER, + RT5640_M_DAC_R1_MONO_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_MONO_DAC_MIXER, + RT5640_M_DAC_R2_MONO_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_MONO_DAC_MIXER, + RT5640_M_DAC_L2_MONO_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_dig_l_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_DIG_MIXER, + RT5640_M_STO_L_DAC_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_DIG_MIXER, + RT5640_M_DAC_L2_DAC_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_dig_r_mix[] = { + SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_DIG_MIXER, + RT5640_M_STO_R_DAC_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_DIG_MIXER, + RT5640_M_DAC_R2_DAC_R_SFT, 1, 1), +}; + +/* Analog Input Mixer */ +static const struct snd_kcontrol_new rt5640_rec_l_mix[] = { + SOC_DAPM_SINGLE("HPOL Switch", RT5640_REC_L2_MIXER, + RT5640_M_HP_L_RM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("INL Switch", RT5640_REC_L2_MIXER, + RT5640_M_IN_L_RM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5640_REC_L2_MIXER, + RT5640_M_BST4_RM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5640_REC_L2_MIXER, + RT5640_M_BST1_RM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("OUT MIXL Switch", RT5640_REC_L2_MIXER, + RT5640_M_OM_L_RM_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_rec_r_mix[] = { + SOC_DAPM_SINGLE("HPOR Switch", RT5640_REC_R2_MIXER, + RT5640_M_HP_R_RM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("INR Switch", RT5640_REC_R2_MIXER, + RT5640_M_IN_R_RM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5640_REC_R2_MIXER, + RT5640_M_BST4_RM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5640_REC_R2_MIXER, + RT5640_M_BST1_RM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("OUT MIXR Switch", RT5640_REC_R2_MIXER, + RT5640_M_OM_R_RM_R_SFT, 1, 1), +}; + +/* Analog Output Mixer */ +static const struct snd_kcontrol_new rt5640_spk_l_mix[] = { + SOC_DAPM_SINGLE("REC MIXL Switch", RT5640_SPK_L_MIXER, + RT5640_M_RM_L_SM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("INL Switch", RT5640_SPK_L_MIXER, + RT5640_M_IN_L_SM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_SPK_L_MIXER, + RT5640_M_DAC_L1_SM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_SPK_L_MIXER, + RT5640_M_DAC_L2_SM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("OUT MIXL Switch", RT5640_SPK_L_MIXER, + RT5640_M_OM_L_SM_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_spk_r_mix[] = { + SOC_DAPM_SINGLE("REC MIXR Switch", RT5640_SPK_R_MIXER, + RT5640_M_RM_R_SM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("INR Switch", RT5640_SPK_R_MIXER, + RT5640_M_IN_R_SM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_SPK_R_MIXER, + RT5640_M_DAC_R1_SM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_SPK_R_MIXER, + RT5640_M_DAC_R2_SM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("OUT MIXR Switch", RT5640_SPK_R_MIXER, + RT5640_M_OM_R_SM_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_out_l_mix[] = { + SOC_DAPM_SINGLE("SPK MIXL Switch", RT5640_OUT_L3_MIXER, + RT5640_M_SM_L_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5640_OUT_L3_MIXER, + RT5640_M_BST1_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("INL Switch", RT5640_OUT_L3_MIXER, + RT5640_M_IN_L_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("REC MIXL Switch", RT5640_OUT_L3_MIXER, + RT5640_M_RM_L_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_OUT_L3_MIXER, + RT5640_M_DAC_R2_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_OUT_L3_MIXER, + RT5640_M_DAC_L2_OM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_OUT_L3_MIXER, + RT5640_M_DAC_L1_OM_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_out_r_mix[] = { + SOC_DAPM_SINGLE("SPK MIXR Switch", RT5640_OUT_R3_MIXER, + RT5640_M_SM_L_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST2 Switch", RT5640_OUT_R3_MIXER, + RT5640_M_BST4_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5640_OUT_R3_MIXER, + RT5640_M_BST1_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("INR Switch", RT5640_OUT_R3_MIXER, + RT5640_M_IN_R_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("REC MIXR Switch", RT5640_OUT_R3_MIXER, + RT5640_M_RM_R_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_OUT_R3_MIXER, + RT5640_M_DAC_L2_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_OUT_R3_MIXER, + RT5640_M_DAC_R2_OM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_OUT_R3_MIXER, + RT5640_M_DAC_R1_OM_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_spo_l_mix[] = { + SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_SPO_L_MIXER, + RT5640_M_DAC_R1_SPM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_SPO_L_MIXER, + RT5640_M_DAC_L1_SPM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("SPKVOL R Switch", RT5640_SPO_L_MIXER, + RT5640_M_SV_R_SPM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("SPKVOL L Switch", RT5640_SPO_L_MIXER, + RT5640_M_SV_L_SPM_L_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5640_SPO_L_MIXER, + RT5640_M_BST1_SPM_L_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_spo_r_mix[] = { + SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_SPO_R_MIXER, + RT5640_M_DAC_R1_SPM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("SPKVOL R Switch", RT5640_SPO_R_MIXER, + RT5640_M_SV_R_SPM_R_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5640_SPO_R_MIXER, + RT5640_M_BST1_SPM_R_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_hpo_mix[] = { + SOC_DAPM_SINGLE("HPO MIX DAC2 Switch", RT5640_HPO_MIXER, + RT5640_M_DAC2_HM_SFT, 1, 1), + SOC_DAPM_SINGLE("HPO MIX DAC1 Switch", RT5640_HPO_MIXER, + RT5640_M_DAC1_HM_SFT, 1, 1), + SOC_DAPM_SINGLE("HPO MIX HPVOL Switch", RT5640_HPO_MIXER, + RT5640_M_HPVOL_HM_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_lout_mix[] = { + SOC_DAPM_SINGLE("DAC L1 Switch", RT5640_LOUT_MIXER, + RT5640_M_DAC_L1_LM_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC R1 Switch", RT5640_LOUT_MIXER, + RT5640_M_DAC_R1_LM_SFT, 1, 1), + SOC_DAPM_SINGLE("OUTVOL L Switch", RT5640_LOUT_MIXER, + RT5640_M_OV_L_LM_SFT, 1, 1), + SOC_DAPM_SINGLE("OUTVOL R Switch", RT5640_LOUT_MIXER, + RT5640_M_OV_R_LM_SFT, 1, 1), +}; + +static const struct snd_kcontrol_new rt5640_mono_mix[] = { + SOC_DAPM_SINGLE("DAC R2 Switch", RT5640_MONO_MIXER, + RT5640_M_DAC_R2_MM_SFT, 1, 1), + SOC_DAPM_SINGLE("DAC L2 Switch", RT5640_MONO_MIXER, + RT5640_M_DAC_L2_MM_SFT, 1, 1), + SOC_DAPM_SINGLE("OUTVOL R Switch", RT5640_MONO_MIXER, + RT5640_M_OV_R_MM_SFT, 1, 1), + SOC_DAPM_SINGLE("OUTVOL L Switch", RT5640_MONO_MIXER, + RT5640_M_OV_L_MM_SFT, 1, 1), + SOC_DAPM_SINGLE("BST1 Switch", RT5640_MONO_MIXER, + RT5640_M_BST1_MM_SFT, 1, 1), +}; + +/* INL/R source */ +static const char * const rt5640_inl_src[] = { + "IN2P", "MONOP" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5640_inl_enum, RT5640_INL_INR_VOL, + RT5640_INL_SEL_SFT, rt5640_inl_src); + +static const struct snd_kcontrol_new rt5640_inl_mux = + SOC_DAPM_ENUM("INL source", rt5640_inl_enum); + +static const char * const rt5640_inr_src[] = { + "IN2N", "MONON" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5640_inr_enum, RT5640_INL_INR_VOL, + RT5640_INR_SEL_SFT, rt5640_inr_src); + +static const struct snd_kcontrol_new rt5640_inr_mux = + SOC_DAPM_ENUM("INR source", rt5640_inr_enum); + +/* Stereo ADC source */ +static const char * const rt5640_stereo_adc1_src[] = { + "DIG MIX", "ADC" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5640_stereo_adc1_enum, RT5640_STO_ADC_MIXER, + RT5640_ADC_1_SRC_SFT, rt5640_stereo_adc1_src); + +static const struct snd_kcontrol_new rt5640_sto_adc_1_mux = + SOC_DAPM_ENUM("Stereo ADC1 Mux", rt5640_stereo_adc1_enum); + +static const char * const rt5640_stereo_adc2_src[] = { + "DMIC1", "DMIC2", "DIG MIX" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5640_stereo_adc2_enum, RT5640_STO_ADC_MIXER, + RT5640_ADC_2_SRC_SFT, rt5640_stereo_adc2_src); + +static const struct snd_kcontrol_new rt5640_sto_adc_2_mux = + SOC_DAPM_ENUM("Stereo ADC2 Mux", rt5640_stereo_adc2_enum); + +/* Mono ADC source */ +static const char * const rt5640_mono_adc_l1_src[] = { + "Mono DAC MIXL", "ADCL" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5640_mono_adc_l1_enum, RT5640_MONO_ADC_MIXER, + RT5640_MONO_ADC_L1_SRC_SFT, rt5640_mono_adc_l1_src); + +static const struct snd_kcontrol_new rt5640_mono_adc_l1_mux = + SOC_DAPM_ENUM("Mono ADC1 left source", rt5640_mono_adc_l1_enum); + +static const char * const rt5640_mono_adc_l2_src[] = { + "DMIC L1", "DMIC L2", "Mono DAC MIXL" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5640_mono_adc_l2_enum, RT5640_MONO_ADC_MIXER, + RT5640_MONO_ADC_L2_SRC_SFT, rt5640_mono_adc_l2_src); + +static const struct snd_kcontrol_new rt5640_mono_adc_l2_mux = + SOC_DAPM_ENUM("Mono ADC2 left source", rt5640_mono_adc_l2_enum); + +static const char * const rt5640_mono_adc_r1_src[] = { + "Mono DAC MIXR", "ADCR" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5640_mono_adc_r1_enum, RT5640_MONO_ADC_MIXER, + RT5640_MONO_ADC_R1_SRC_SFT, rt5640_mono_adc_r1_src); + +static const struct snd_kcontrol_new rt5640_mono_adc_r1_mux = + SOC_DAPM_ENUM("Mono ADC1 right source", rt5640_mono_adc_r1_enum); + +static const char * const rt5640_mono_adc_r2_src[] = { + "DMIC R1", "DMIC R2", "Mono DAC MIXR" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5640_mono_adc_r2_enum, RT5640_MONO_ADC_MIXER, + RT5640_MONO_ADC_R2_SRC_SFT, rt5640_mono_adc_r2_src); + +static const struct snd_kcontrol_new rt5640_mono_adc_r2_mux = + SOC_DAPM_ENUM("Mono ADC2 right source", rt5640_mono_adc_r2_enum); + +/* DAC2 channel source */ +static const char * const rt5640_dac_l2_src[] = { + "IF2", "Base L/R" +}; + +static int rt5640_dac_l2_values[] = { + 0, + 3, +}; + +static const SOC_VALUE_ENUM_SINGLE_DECL( + rt5640_dac_l2_enum, RT5640_DSP_PATH2, RT5640_DAC_L2_SEL_SFT, + 0x3, rt5640_dac_l2_src, rt5640_dac_l2_values); + +static const struct snd_kcontrol_new rt5640_dac_l2_mux = + SOC_DAPM_VALUE_ENUM("DAC2 left channel source", rt5640_dac_l2_enum); + +static const char * const rt5640_dac_r2_src[] = { + "IF2", +}; + +static int rt5640_dac_r2_values[] = { + 0, +}; + +static const SOC_VALUE_ENUM_SINGLE_DECL( + rt5640_dac_r2_enum, RT5640_DSP_PATH2, RT5640_DAC_R2_SEL_SFT, + 0x3, rt5640_dac_r2_src, rt5640_dac_r2_values); + +static const struct snd_kcontrol_new rt5640_dac_r2_mux = + SOC_DAPM_ENUM("DAC2 right channel source", rt5640_dac_r2_enum); + +/* digital interface and iis interface map */ +static const char * const rt5640_dai_iis_map[] = { + "1:1|2:2", "1:2|2:1", "1:1|2:1", "1:2|2:2" +}; + +static int rt5640_dai_iis_map_values[] = { + 0, + 5, + 6, + 7, +}; + +static const SOC_VALUE_ENUM_SINGLE_DECL( + rt5640_dai_iis_map_enum, RT5640_I2S1_SDP, RT5640_I2S_IF_SFT, + 0x7, rt5640_dai_iis_map, rt5640_dai_iis_map_values); + +static const struct snd_kcontrol_new rt5640_dai_mux = + SOC_DAPM_VALUE_ENUM("DAI select", rt5640_dai_iis_map_enum); + +/* SDI select */ +static const char * const rt5640_sdi_sel[] = { + "IF1", "IF2" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5640_sdi_sel_enum, RT5640_I2S2_SDP, + RT5640_I2S2_SDI_SFT, rt5640_sdi_sel); + +static const struct snd_kcontrol_new rt5640_sdi_mux = + SOC_DAPM_ENUM("SDI select", rt5640_sdi_sel_enum); + +static int spk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(rt5640->regmap, RT5640_PWR_DIG1, + 0x0001, 0x0001); + regmap_update_bits(rt5640->regmap, RT5640_PR_BASE + 0x1c, + 0xf000, 0xf000); + break; + + case SND_SOC_DAPM_PRE_PMD: + regmap_update_bits(rt5640->regmap, RT5640_PR_BASE + 0x1c, + 0xf000, 0x0000); + regmap_update_bits(rt5640->regmap, RT5640_PWR_DIG1, + 0x0001, 0x0000); + break; + + default: + return 0; + } + return 0; +} + +static int rt5640_set_dmic1_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, RT5640_GPIO_CTRL1, + RT5640_GP2_PIN_MASK | RT5640_GP3_PIN_MASK, + RT5640_GP2_PIN_DMIC1_SCL | RT5640_GP3_PIN_DMIC1_SDA); + snd_soc_update_bits(codec, RT5640_DMIC, + RT5640_DMIC_1L_LH_MASK | RT5640_DMIC_1R_LH_MASK | + RT5640_DMIC_1_DP_MASK, + RT5640_DMIC_1L_LH_FALLING | RT5640_DMIC_1R_LH_RISING | + RT5640_DMIC_1_DP_IN1P); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5640_set_dmic2_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, RT5640_GPIO_CTRL1, + RT5640_GP2_PIN_MASK | RT5640_GP4_PIN_MASK, + RT5640_GP2_PIN_DMIC1_SCL | RT5640_GP4_PIN_DMIC2_SDA); + snd_soc_update_bits(codec, RT5640_DMIC, + RT5640_DMIC_2L_LH_MASK | RT5640_DMIC_2R_LH_MASK | + RT5640_DMIC_2_DP_MASK, + RT5640_DMIC_2L_LH_FALLING | RT5640_DMIC_2R_LH_RISING | + RT5640_DMIC_2_DP_IN1N); + break; + + default: + return 0; + } + + return 0; +} + +static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("PLL1", RT5640_PWR_ANLG2, + RT5640_PWR_PLL_BIT, 0, NULL, 0), + /* Input Side */ + /* micbias */ + SND_SOC_DAPM_SUPPLY("LDO2", RT5640_PWR_ANLG1, + RT5640_PWR_LDO2_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MICBIAS1", RT5640_PWR_ANLG2, + RT5640_PWR_MB1_BIT, 0, 0, 0), + /* Input Lines */ + SND_SOC_DAPM_INPUT("DMIC1"), + SND_SOC_DAPM_INPUT("DMIC2"), + SND_SOC_DAPM_INPUT("IN1P"), + SND_SOC_DAPM_INPUT("IN1N"), + SND_SOC_DAPM_INPUT("IN2P"), + SND_SOC_DAPM_INPUT("IN2N"), + SND_SOC_DAPM_PGA("DMIC L1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DMIC R1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DMIC L2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DMIC R2", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0, + set_dmic_clk, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY("DMIC1 Power", RT5640_DMIC, + RT5640_DMIC_1_EN_SFT, 0, rt5640_set_dmic1_event, + SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY("DMIC2 Power", RT5640_DMIC, + RT5640_DMIC_2_EN_SFT, 0, rt5640_set_dmic2_event, + SND_SOC_DAPM_PRE_PMU), + /* Boost */ + SND_SOC_DAPM_PGA("BST1", RT5640_PWR_ANLG2, + RT5640_PWR_BST1_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("BST2", RT5640_PWR_ANLG2, + RT5640_PWR_BST4_BIT, 0, NULL, 0), + /* Input Volume */ + SND_SOC_DAPM_PGA("INL VOL", RT5640_PWR_VOL, + RT5640_PWR_IN_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("INR VOL", RT5640_PWR_VOL, + RT5640_PWR_IN_R_BIT, 0, NULL, 0), + /* IN Mux */ + SND_SOC_DAPM_MUX("INL Mux", SND_SOC_NOPM, 0, 0, &rt5640_inl_mux), + SND_SOC_DAPM_MUX("INR Mux", SND_SOC_NOPM, 0, 0, &rt5640_inr_mux), + /* REC Mixer */ + SND_SOC_DAPM_MIXER("RECMIXL", RT5640_PWR_MIXER, RT5640_PWR_RM_L_BIT, 0, + rt5640_rec_l_mix, ARRAY_SIZE(rt5640_rec_l_mix)), + SND_SOC_DAPM_MIXER("RECMIXR", RT5640_PWR_MIXER, RT5640_PWR_RM_R_BIT, 0, + rt5640_rec_r_mix, ARRAY_SIZE(rt5640_rec_r_mix)), + /* ADCs */ + SND_SOC_DAPM_ADC("ADC L", NULL, RT5640_PWR_DIG1, + RT5640_PWR_ADC_L_BIT, 0), + SND_SOC_DAPM_ADC("ADC R", NULL, RT5640_PWR_DIG1, + RT5640_PWR_ADC_R_BIT, 0), + /* ADC Mux */ + SND_SOC_DAPM_MUX("Stereo ADC L2 Mux", SND_SOC_NOPM, 0, 0, + &rt5640_sto_adc_2_mux), + SND_SOC_DAPM_MUX("Stereo ADC R2 Mux", SND_SOC_NOPM, 0, 0, + &rt5640_sto_adc_2_mux), + SND_SOC_DAPM_MUX("Stereo ADC L1 Mux", SND_SOC_NOPM, 0, 0, + &rt5640_sto_adc_1_mux), + SND_SOC_DAPM_MUX("Stereo ADC R1 Mux", SND_SOC_NOPM, 0, 0, + &rt5640_sto_adc_1_mux), + SND_SOC_DAPM_MUX("Mono ADC L2 Mux", SND_SOC_NOPM, 0, 0, + &rt5640_mono_adc_l2_mux), + SND_SOC_DAPM_MUX("Mono ADC L1 Mux", SND_SOC_NOPM, 0, 0, + &rt5640_mono_adc_l1_mux), + SND_SOC_DAPM_MUX("Mono ADC R1 Mux", SND_SOC_NOPM, 0, 0, + &rt5640_mono_adc_r1_mux), + SND_SOC_DAPM_MUX("Mono ADC R2 Mux", SND_SOC_NOPM, 0, 0, + &rt5640_mono_adc_r2_mux), + /* ADC Mixer */ + SND_SOC_DAPM_SUPPLY("Stereo Filter", RT5640_PWR_DIG2, + RT5640_PWR_ADC_SF_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Stereo ADC MIXL", SND_SOC_NOPM, 0, 0, + rt5640_sto_adc_l_mix, ARRAY_SIZE(rt5640_sto_adc_l_mix)), + SND_SOC_DAPM_MIXER("Stereo ADC MIXR", SND_SOC_NOPM, 0, 0, + rt5640_sto_adc_r_mix, ARRAY_SIZE(rt5640_sto_adc_r_mix)), + SND_SOC_DAPM_SUPPLY("Mono Left Filter", RT5640_PWR_DIG2, + RT5640_PWR_ADC_MF_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Mono ADC MIXL", SND_SOC_NOPM, 0, 0, + rt5640_mono_adc_l_mix, ARRAY_SIZE(rt5640_mono_adc_l_mix)), + SND_SOC_DAPM_SUPPLY("Mono Right Filter", RT5640_PWR_DIG2, + RT5640_PWR_ADC_MF_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Mono ADC MIXR", SND_SOC_NOPM, 0, 0, + rt5640_mono_adc_r_mix, ARRAY_SIZE(rt5640_mono_adc_r_mix)), + + /* Digital Interface */ + SND_SOC_DAPM_SUPPLY("I2S1", RT5640_PWR_DIG1, + RT5640_PWR_I2S1_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 ADC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 ADC L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF1 ADC R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("I2S2", RT5640_PWR_DIG1, + RT5640_PWR_I2S2_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 DAC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 DAC L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 DAC R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 ADC", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 ADC L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF2 ADC R", SND_SOC_NOPM, 0, 0, NULL, 0), + /* Digital Interface Select */ + SND_SOC_DAPM_MUX("DAI1 RX Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux), + SND_SOC_DAPM_MUX("DAI1 TX Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux), + SND_SOC_DAPM_MUX("DAI1 IF1 Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux), + SND_SOC_DAPM_MUX("DAI1 IF2 Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux), + SND_SOC_DAPM_MUX("SDI1 TX Mux", SND_SOC_NOPM, 0, 0, &rt5640_sdi_mux), + SND_SOC_DAPM_MUX("DAI2 RX Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux), + SND_SOC_DAPM_MUX("DAI2 TX Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux), + SND_SOC_DAPM_MUX("DAI2 IF1 Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux), + SND_SOC_DAPM_MUX("DAI2 IF2 Mux", SND_SOC_NOPM, 0, 0, &rt5640_dai_mux), + SND_SOC_DAPM_MUX("SDI2 TX Mux", SND_SOC_NOPM, 0, 0, &rt5640_sdi_mux), + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF2RX", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0), + /* Audio DSP */ + SND_SOC_DAPM_PGA("Audio DSP", SND_SOC_NOPM, 0, 0, NULL, 0), + /* ANC */ + SND_SOC_DAPM_PGA("ANC", SND_SOC_NOPM, 0, 0, NULL, 0), + /* Output Side */ + /* DAC mixer before sound effect */ + SND_SOC_DAPM_MIXER("DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5640_dac_l_mix, ARRAY_SIZE(rt5640_dac_l_mix)), + SND_SOC_DAPM_MIXER("DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5640_dac_r_mix, ARRAY_SIZE(rt5640_dac_r_mix)), + /* DAC2 channel Mux */ + SND_SOC_DAPM_MUX("DAC L2 Mux", SND_SOC_NOPM, 0, 0, + &rt5640_dac_l2_mux), + SND_SOC_DAPM_MUX("DAC R2 Mux", SND_SOC_NOPM, 0, 0, + &rt5640_dac_r2_mux), + /* DAC Mixer */ + SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5640_sto_dac_l_mix, ARRAY_SIZE(rt5640_sto_dac_l_mix)), + SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5640_sto_dac_r_mix, ARRAY_SIZE(rt5640_sto_dac_r_mix)), + SND_SOC_DAPM_MIXER("Mono DAC MIXL", SND_SOC_NOPM, 0, 0, + rt5640_mono_dac_l_mix, ARRAY_SIZE(rt5640_mono_dac_l_mix)), + SND_SOC_DAPM_MIXER("Mono DAC MIXR", SND_SOC_NOPM, 0, 0, + rt5640_mono_dac_r_mix, ARRAY_SIZE(rt5640_mono_dac_r_mix)), + SND_SOC_DAPM_MIXER("DIG MIXL", SND_SOC_NOPM, 0, 0, + rt5640_dig_l_mix, ARRAY_SIZE(rt5640_dig_l_mix)), + SND_SOC_DAPM_MIXER("DIG MIXR", SND_SOC_NOPM, 0, 0, + rt5640_dig_r_mix, ARRAY_SIZE(rt5640_dig_r_mix)), + /* DACs */ + SND_SOC_DAPM_DAC("DAC L1", NULL, RT5640_PWR_DIG1, + RT5640_PWR_DAC_L1_BIT, 0), + SND_SOC_DAPM_DAC("DAC L2", NULL, RT5640_PWR_DIG1, + RT5640_PWR_DAC_L2_BIT, 0), + SND_SOC_DAPM_DAC("DAC R1", NULL, RT5640_PWR_DIG1, + RT5640_PWR_DAC_R1_BIT, 0), + SND_SOC_DAPM_DAC("DAC R2", NULL, RT5640_PWR_DIG1, + RT5640_PWR_DAC_R2_BIT, 0), + /* SPK/OUT Mixer */ + SND_SOC_DAPM_MIXER("SPK MIXL", RT5640_PWR_MIXER, RT5640_PWR_SM_L_BIT, + 0, rt5640_spk_l_mix, ARRAY_SIZE(rt5640_spk_l_mix)), + SND_SOC_DAPM_MIXER("SPK MIXR", RT5640_PWR_MIXER, RT5640_PWR_SM_R_BIT, + 0, rt5640_spk_r_mix, ARRAY_SIZE(rt5640_spk_r_mix)), + SND_SOC_DAPM_MIXER("OUT MIXL", RT5640_PWR_MIXER, RT5640_PWR_OM_L_BIT, + 0, rt5640_out_l_mix, ARRAY_SIZE(rt5640_out_l_mix)), + SND_SOC_DAPM_MIXER("OUT MIXR", RT5640_PWR_MIXER, RT5640_PWR_OM_R_BIT, + 0, rt5640_out_r_mix, ARRAY_SIZE(rt5640_out_r_mix)), + /* Ouput Volume */ + SND_SOC_DAPM_PGA("SPKVOL L", RT5640_PWR_VOL, + RT5640_PWR_SV_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("SPKVOL R", RT5640_PWR_VOL, + RT5640_PWR_SV_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("OUTVOL L", RT5640_PWR_VOL, + RT5640_PWR_OV_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("OUTVOL R", RT5640_PWR_VOL, + RT5640_PWR_OV_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("HPOVOL L", RT5640_PWR_VOL, + RT5640_PWR_HV_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("HPOVOL R", RT5640_PWR_VOL, + RT5640_PWR_HV_R_BIT, 0, NULL, 0), + /* SPO/HPO/LOUT/Mono Mixer */ + SND_SOC_DAPM_MIXER("SPOL MIX", SND_SOC_NOPM, 0, + 0, rt5640_spo_l_mix, ARRAY_SIZE(rt5640_spo_l_mix)), + SND_SOC_DAPM_MIXER("SPOR MIX", SND_SOC_NOPM, 0, + 0, rt5640_spo_r_mix, ARRAY_SIZE(rt5640_spo_r_mix)), + SND_SOC_DAPM_MIXER("HPO MIX L", SND_SOC_NOPM, 0, 0, + rt5640_hpo_mix, ARRAY_SIZE(rt5640_hpo_mix)), + SND_SOC_DAPM_MIXER("HPO MIX R", SND_SOC_NOPM, 0, 0, + rt5640_hpo_mix, ARRAY_SIZE(rt5640_hpo_mix)), + SND_SOC_DAPM_MIXER("LOUT MIX", RT5640_PWR_ANLG1, RT5640_PWR_LM_BIT, 0, + rt5640_lout_mix, ARRAY_SIZE(rt5640_lout_mix)), + SND_SOC_DAPM_MIXER("Mono MIX", RT5640_PWR_ANLG1, RT5640_PWR_MM_BIT, 0, + rt5640_mono_mix, ARRAY_SIZE(rt5640_mono_mix)), + SND_SOC_DAPM_SUPPLY("Improve MONO Amp Drv", RT5640_PWR_ANLG1, + RT5640_PWR_MA_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Improve HP Amp Drv", RT5640_PWR_ANLG1, + SND_SOC_NOPM, 0, NULL, 0), + SND_SOC_DAPM_PGA("HP L Amp", RT5640_PWR_ANLG1, + RT5640_PWR_HP_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_PGA("HP R Amp", RT5640_PWR_ANLG1, + RT5640_PWR_HP_R_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Improve SPK Amp Drv", RT5640_PWR_DIG1, + SND_SOC_NOPM, 0, spk_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + /* Output Lines */ + SND_SOC_DAPM_OUTPUT("SPOLP"), + SND_SOC_DAPM_OUTPUT("SPOLN"), + SND_SOC_DAPM_OUTPUT("SPORP"), + SND_SOC_DAPM_OUTPUT("SPORN"), + SND_SOC_DAPM_OUTPUT("HPOL"), + SND_SOC_DAPM_OUTPUT("HPOR"), + SND_SOC_DAPM_OUTPUT("LOUTL"), + SND_SOC_DAPM_OUTPUT("LOUTR"), + SND_SOC_DAPM_OUTPUT("MONOP"), + SND_SOC_DAPM_OUTPUT("MONON"), +}; + +static const struct snd_soc_dapm_route rt5640_dapm_routes[] = { + {"IN1P", NULL, "LDO2"}, + {"IN2P", NULL, "LDO2"}, + + {"DMIC L1", NULL, "DMIC1"}, + {"DMIC R1", NULL, "DMIC1"}, + {"DMIC L2", NULL, "DMIC2"}, + {"DMIC R2", NULL, "DMIC2"}, + + {"BST1", NULL, "IN1P"}, + {"BST1", NULL, "IN1N"}, + {"BST2", NULL, "IN2P"}, + {"BST2", NULL, "IN2N"}, + + {"INL VOL", NULL, "IN2P"}, + {"INR VOL", NULL, "IN2N"}, + + {"RECMIXL", "HPOL Switch", "HPOL"}, + {"RECMIXL", "INL Switch", "INL VOL"}, + {"RECMIXL", "BST2 Switch", "BST2"}, + {"RECMIXL", "BST1 Switch", "BST1"}, + {"RECMIXL", "OUT MIXL Switch", "OUT MIXL"}, + + {"RECMIXR", "HPOR Switch", "HPOR"}, + {"RECMIXR", "INR Switch", "INR VOL"}, + {"RECMIXR", "BST2 Switch", "BST2"}, + {"RECMIXR", "BST1 Switch", "BST1"}, + {"RECMIXR", "OUT MIXR Switch", "OUT MIXR"}, + + {"ADC L", NULL, "RECMIXL"}, + {"ADC R", NULL, "RECMIXR"}, + + {"DMIC L1", NULL, "DMIC CLK"}, + {"DMIC L1", NULL, "DMIC1 Power"}, + {"DMIC R1", NULL, "DMIC CLK"}, + {"DMIC R1", NULL, "DMIC1 Power"}, + {"DMIC L2", NULL, "DMIC CLK"}, + {"DMIC L2", NULL, "DMIC2 Power"}, + {"DMIC R2", NULL, "DMIC CLK"}, + {"DMIC R2", NULL, "DMIC2 Power"}, + + {"Stereo ADC L2 Mux", "DMIC1", "DMIC L1"}, + {"Stereo ADC L2 Mux", "DMIC2", "DMIC L2"}, + {"Stereo ADC L2 Mux", "DIG MIX", "DIG MIXL"}, + {"Stereo ADC L1 Mux", "ADC", "ADC L"}, + {"Stereo ADC L1 Mux", "DIG MIX", "DIG MIXL"}, + + {"Stereo ADC R1 Mux", "ADC", "ADC R"}, + {"Stereo ADC R1 Mux", "DIG MIX", "DIG MIXR"}, + {"Stereo ADC R2 Mux", "DMIC1", "DMIC R1"}, + {"Stereo ADC R2 Mux", "DMIC2", "DMIC R2"}, + {"Stereo ADC R2 Mux", "DIG MIX", "DIG MIXR"}, + + {"Mono ADC L2 Mux", "DMIC L1", "DMIC L1"}, + {"Mono ADC L2 Mux", "DMIC L2", "DMIC L2"}, + {"Mono ADC L2 Mux", "Mono DAC MIXL", "Mono DAC MIXL"}, + {"Mono ADC L1 Mux", "Mono DAC MIXL", "Mono DAC MIXL"}, + {"Mono ADC L1 Mux", "ADCL", "ADC L"}, + + {"Mono ADC R1 Mux", "Mono DAC MIXR", "Mono DAC MIXR"}, + {"Mono ADC R1 Mux", "ADCR", "ADC R"}, + {"Mono ADC R2 Mux", "DMIC R1", "DMIC R1"}, + {"Mono ADC R2 Mux", "DMIC R2", "DMIC R2"}, + {"Mono ADC R2 Mux", "Mono DAC MIXR", "Mono DAC MIXR"}, + + {"Stereo ADC MIXL", "ADC1 Switch", "Stereo ADC L1 Mux"}, + {"Stereo ADC MIXL", "ADC2 Switch", "Stereo ADC L2 Mux"}, + {"Stereo ADC MIXL", NULL, "Stereo Filter"}, + {"Stereo Filter", NULL, "PLL1", check_sysclk1_source}, + + {"Stereo ADC MIXR", "ADC1 Switch", "Stereo ADC R1 Mux"}, + {"Stereo ADC MIXR", "ADC2 Switch", "Stereo ADC R2 Mux"}, + {"Stereo ADC MIXR", NULL, "Stereo Filter"}, + {"Stereo Filter", NULL, "PLL1", check_sysclk1_source}, + + {"Mono ADC MIXL", "ADC1 Switch", "Mono ADC L1 Mux"}, + {"Mono ADC MIXL", "ADC2 Switch", "Mono ADC L2 Mux"}, + {"Mono ADC MIXL", NULL, "Mono Left Filter"}, + {"Mono Left Filter", NULL, "PLL1", check_sysclk1_source}, + + {"Mono ADC MIXR", "ADC1 Switch", "Mono ADC R1 Mux"}, + {"Mono ADC MIXR", "ADC2 Switch", "Mono ADC R2 Mux"}, + {"Mono ADC MIXR", NULL, "Mono Right Filter"}, + {"Mono Right Filter", NULL, "PLL1", check_sysclk1_source}, + + {"IF2 ADC L", NULL, "Mono ADC MIXL"}, + {"IF2 ADC R", NULL, "Mono ADC MIXR"}, + {"IF1 ADC L", NULL, "Stereo ADC MIXL"}, + {"IF1 ADC R", NULL, "Stereo ADC MIXR"}, + + {"IF1 ADC", NULL, "I2S1"}, + {"IF1 ADC", NULL, "IF1 ADC L"}, + {"IF1 ADC", NULL, "IF1 ADC R"}, + {"IF2 ADC", NULL, "I2S2"}, + {"IF2 ADC", NULL, "IF2 ADC L"}, + {"IF2 ADC", NULL, "IF2 ADC R"}, + + {"DAI1 TX Mux", "1:1|2:2", "IF1 ADC"}, + {"DAI1 TX Mux", "1:2|2:1", "IF2 ADC"}, + {"DAI1 IF1 Mux", "1:1|2:1", "IF1 ADC"}, + {"DAI1 IF2 Mux", "1:1|2:1", "IF2 ADC"}, + {"SDI1 TX Mux", "IF1", "DAI1 IF1 Mux"}, + {"SDI1 TX Mux", "IF2", "DAI1 IF2 Mux"}, + + {"DAI2 TX Mux", "1:2|2:1", "IF1 ADC"}, + {"DAI2 TX Mux", "1:1|2:2", "IF2 ADC"}, + {"DAI2 IF1 Mux", "1:2|2:2", "IF1 ADC"}, + {"DAI2 IF2 Mux", "1:2|2:2", "IF2 ADC"}, + {"SDI2 TX Mux", "IF1", "DAI2 IF1 Mux"}, + {"SDI2 TX Mux", "IF2", "DAI2 IF2 Mux"}, + + {"AIF1TX", NULL, "DAI1 TX Mux"}, + {"AIF1TX", NULL, "SDI1 TX Mux"}, + {"AIF2TX", NULL, "DAI2 TX Mux"}, + {"AIF2TX", NULL, "SDI2 TX Mux"}, + + {"DAI1 RX Mux", "1:1|2:2", "AIF1RX"}, + {"DAI1 RX Mux", "1:1|2:1", "AIF1RX"}, + {"DAI1 RX Mux", "1:2|2:1", "AIF2RX"}, + {"DAI1 RX Mux", "1:2|2:2", "AIF2RX"}, + + {"DAI2 RX Mux", "1:2|2:1", "AIF1RX"}, + {"DAI2 RX Mux", "1:1|2:1", "AIF1RX"}, + {"DAI2 RX Mux", "1:1|2:2", "AIF2RX"}, + {"DAI2 RX Mux", "1:2|2:2", "AIF2RX"}, + + {"IF1 DAC", NULL, "I2S1"}, + {"IF1 DAC", NULL, "DAI1 RX Mux"}, + {"IF2 DAC", NULL, "I2S2"}, + {"IF2 DAC", NULL, "DAI2 RX Mux"}, + + {"IF1 DAC L", NULL, "IF1 DAC"}, + {"IF1 DAC R", NULL, "IF1 DAC"}, + {"IF2 DAC L", NULL, "IF2 DAC"}, + {"IF2 DAC R", NULL, "IF2 DAC"}, + + {"DAC MIXL", "Stereo ADC Switch", "Stereo ADC MIXL"}, + {"DAC MIXL", "INF1 Switch", "IF1 DAC L"}, + {"DAC MIXR", "Stereo ADC Switch", "Stereo ADC MIXR"}, + {"DAC MIXR", "INF1 Switch", "IF1 DAC R"}, + + {"ANC", NULL, "Stereo ADC MIXL"}, + {"ANC", NULL, "Stereo ADC MIXR"}, + + {"Audio DSP", NULL, "DAC MIXL"}, + {"Audio DSP", NULL, "DAC MIXR"}, + + {"DAC L2 Mux", "IF2", "IF2 DAC L"}, + {"DAC L2 Mux", "Base L/R", "Audio DSP"}, + + {"DAC R2 Mux", "IF2", "IF2 DAC R"}, + + {"Stereo DAC MIXL", "DAC L1 Switch", "DAC MIXL"}, + {"Stereo DAC MIXL", "DAC L2 Switch", "DAC L2 Mux"}, + {"Stereo DAC MIXL", "ANC Switch", "ANC"}, + {"Stereo DAC MIXR", "DAC R1 Switch", "DAC MIXR"}, + {"Stereo DAC MIXR", "DAC R2 Switch", "DAC R2 Mux"}, + {"Stereo DAC MIXR", "ANC Switch", "ANC"}, + + {"Mono DAC MIXL", "DAC L1 Switch", "DAC MIXL"}, + {"Mono DAC MIXL", "DAC L2 Switch", "DAC L2 Mux"}, + {"Mono DAC MIXL", "DAC R2 Switch", "DAC R2 Mux"}, + {"Mono DAC MIXR", "DAC R1 Switch", "DAC MIXR"}, + {"Mono DAC MIXR", "DAC R2 Switch", "DAC R2 Mux"}, + {"Mono DAC MIXR", "DAC L2 Switch", "DAC L2 Mux"}, + + {"DIG MIXL", "DAC L1 Switch", "DAC MIXL"}, + {"DIG MIXL", "DAC L2 Switch", "DAC L2 Mux"}, + {"DIG MIXR", "DAC R1 Switch", "DAC MIXR"}, + {"DIG MIXR", "DAC R2 Switch", "DAC R2 Mux"}, + + {"DAC L1", NULL, "Stereo DAC MIXL"}, + {"DAC L1", NULL, "PLL1", check_sysclk1_source}, + {"DAC R1", NULL, "Stereo DAC MIXR"}, + {"DAC R1", NULL, "PLL1", check_sysclk1_source}, + {"DAC L2", NULL, "Mono DAC MIXL"}, + {"DAC L2", NULL, "PLL1", check_sysclk1_source}, + {"DAC R2", NULL, "Mono DAC MIXR"}, + {"DAC R2", NULL, "PLL1", check_sysclk1_source}, + + {"SPK MIXL", "REC MIXL Switch", "RECMIXL"}, + {"SPK MIXL", "INL Switch", "INL VOL"}, + {"SPK MIXL", "DAC L1 Switch", "DAC L1"}, + {"SPK MIXL", "DAC L2 Switch", "DAC L2"}, + {"SPK MIXL", "OUT MIXL Switch", "OUT MIXL"}, + {"SPK MIXR", "REC MIXR Switch", "RECMIXR"}, + {"SPK MIXR", "INR Switch", "INR VOL"}, + {"SPK MIXR", "DAC R1 Switch", "DAC R1"}, + {"SPK MIXR", "DAC R2 Switch", "DAC R2"}, + {"SPK MIXR", "OUT MIXR Switch", "OUT MIXR"}, + + {"OUT MIXL", "SPK MIXL Switch", "SPK MIXL"}, + {"OUT MIXL", "BST1 Switch", "BST1"}, + {"OUT MIXL", "INL Switch", "INL VOL"}, + {"OUT MIXL", "REC MIXL Switch", "RECMIXL"}, + {"OUT MIXL", "DAC R2 Switch", "DAC R2"}, + {"OUT MIXL", "DAC L2 Switch", "DAC L2"}, + {"OUT MIXL", "DAC L1 Switch", "DAC L1"}, + + {"OUT MIXR", "SPK MIXR Switch", "SPK MIXR"}, + {"OUT MIXR", "BST2 Switch", "BST2"}, + {"OUT MIXR", "BST1 Switch", "BST1"}, + {"OUT MIXR", "INR Switch", "INR VOL"}, + {"OUT MIXR", "REC MIXR Switch", "RECMIXR"}, + {"OUT MIXR", "DAC L2 Switch", "DAC L2"}, + {"OUT MIXR", "DAC R2 Switch", "DAC R2"}, + {"OUT MIXR", "DAC R1 Switch", "DAC R1"}, + + {"SPKVOL L", NULL, "SPK MIXL"}, + {"SPKVOL R", NULL, "SPK MIXR"}, + {"HPOVOL L", NULL, "OUT MIXL"}, + {"HPOVOL R", NULL, "OUT MIXR"}, + {"OUTVOL L", NULL, "OUT MIXL"}, + {"OUTVOL R", NULL, "OUT MIXR"}, + + {"SPOL MIX", "DAC R1 Switch", "DAC R1"}, + {"SPOL MIX", "DAC L1 Switch", "DAC L1"}, + {"SPOL MIX", "SPKVOL R Switch", "SPKVOL R"}, + {"SPOL MIX", "SPKVOL L Switch", "SPKVOL L"}, + {"SPOL MIX", "BST1 Switch", "BST1"}, + {"SPOR MIX", "DAC R1 Switch", "DAC R1"}, + {"SPOR MIX", "SPKVOL R Switch", "SPKVOL R"}, + {"SPOR MIX", "BST1 Switch", "BST1"}, + + {"HPO MIX L", "HPO MIX DAC2 Switch", "DAC L2"}, + {"HPO MIX L", "HPO MIX DAC1 Switch", "DAC L1"}, + {"HPO MIX L", "HPO MIX HPVOL Switch", "HPOVOL L"}, + {"HPO MIX R", "HPO MIX DAC2 Switch", "DAC R2"}, + {"HPO MIX R", "HPO MIX DAC1 Switch", "DAC R1"}, + {"HPO MIX R", "HPO MIX HPVOL Switch", "HPOVOL R"}, + + {"LOUT MIX", "DAC L1 Switch", "DAC L1"}, + {"LOUT MIX", "DAC R1 Switch", "DAC R1"}, + {"LOUT MIX", "OUTVOL L Switch", "OUTVOL L"}, + {"LOUT MIX", "OUTVOL R Switch", "OUTVOL R"}, + + {"Mono MIX", "DAC R2 Switch", "DAC R2"}, + {"Mono MIX", "DAC L2 Switch", "DAC L2"}, + {"Mono MIX", "OUTVOL R Switch", "OUTVOL R"}, + {"Mono MIX", "OUTVOL L Switch", "OUTVOL L"}, + {"Mono MIX", "BST1 Switch", "BST1"}, + + {"HP L Amp", NULL, "HPO MIX L"}, + {"HP R Amp", NULL, "HPO MIX R"}, + + {"SPOLP", NULL, "SPOL MIX"}, + {"SPOLN", NULL, "SPOL MIX"}, + {"SPORP", NULL, "SPOR MIX"}, + {"SPORN", NULL, "SPOR MIX"}, + + {"SPOLP", NULL, "Improve SPK Amp Drv"}, + {"SPOLN", NULL, "Improve SPK Amp Drv"}, + {"SPORP", NULL, "Improve SPK Amp Drv"}, + {"SPORN", NULL, "Improve SPK Amp Drv"}, + + {"HPOL", NULL, "Improve HP Amp Drv"}, + {"HPOR", NULL, "Improve HP Amp Drv"}, + + {"HPOL", NULL, "HP L Amp"}, + {"HPOR", NULL, "HP R Amp"}, + {"LOUTL", NULL, "LOUT MIX"}, + {"LOUTR", NULL, "LOUT MIX"}, + {"MONOP", NULL, "Mono MIX"}, + {"MONON", NULL, "Mono MIX"}, + {"MONOP", NULL, "Improve MONO Amp Drv"}, +}; + +static int get_sdp_info(struct snd_soc_codec *codec, int dai_id) +{ + int ret = 0, val; + + if (codec == NULL) + return -EINVAL; + + val = snd_soc_read(codec, RT5640_I2S1_SDP); + val = (val & RT5640_I2S_IF_MASK) >> RT5640_I2S_IF_SFT; + switch (dai_id) { + case RT5640_AIF1: + switch (val) { + case RT5640_IF_123: + case RT5640_IF_132: + ret |= RT5640_U_IF1; + break; + case RT5640_IF_113: + ret |= RT5640_U_IF1; + case RT5640_IF_312: + case RT5640_IF_213: + ret |= RT5640_U_IF2; + break; + } + break; + + case RT5640_AIF2: + switch (val) { + case RT5640_IF_231: + case RT5640_IF_213: + ret |= RT5640_U_IF1; + break; + case RT5640_IF_223: + ret |= RT5640_U_IF1; + case RT5640_IF_123: + case RT5640_IF_321: + ret |= RT5640_U_IF2; + break; + } + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int get_clk_info(int sclk, int rate) +{ + int i, pd[] = {1, 2, 3, 4, 6, 8, 12, 16}; + + if (sclk <= 0 || rate <= 0) + return -EINVAL; + + rate = rate << 8; + for (i = 0; i < ARRAY_SIZE(pd); i++) + if (sclk == rate * pd[i]) + return i; + + return -EINVAL; +} + +static int rt5640_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec *codec = rtd->codec; + struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec); + unsigned int val_len = 0, val_clk, mask_clk, dai_sel; + int pre_div, bclk_ms, frame_size; + + rt5640->lrck[dai->id] = params_rate(params); + pre_div = get_clk_info(rt5640->sysclk, rt5640->lrck[dai->id]); + if (pre_div < 0) { + dev_err(codec->dev, "Unsupported clock setting\n"); + return -EINVAL; + } + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size < 0) { + dev_err(codec->dev, "Unsupported frame size: %d\n", frame_size); + return frame_size; + } + if (frame_size > 32) + bclk_ms = 1; + else + bclk_ms = 0; + rt5640->bclk[dai->id] = rt5640->lrck[dai->id] * (32 << bclk_ms); + + dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n", + rt5640->bclk[dai->id], rt5640->lrck[dai->id]); + dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n", + bclk_ms, pre_div, dai->id); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + break; + case SNDRV_PCM_FORMAT_S20_3LE: + val_len |= RT5640_I2S_DL_20; + break; + case SNDRV_PCM_FORMAT_S24_LE: + val_len |= RT5640_I2S_DL_24; + break; + case SNDRV_PCM_FORMAT_S8: + val_len |= RT5640_I2S_DL_8; + break; + default: + return -EINVAL; + } + + dai_sel = get_sdp_info(codec, dai->id); + if (dai_sel < 0) { + dev_err(codec->dev, "Failed to get sdp info: %d\n", dai_sel); + return -EINVAL; + } + if (dai_sel & RT5640_U_IF1) { + mask_clk = RT5640_I2S_BCLK_MS1_MASK | RT5640_I2S_PD1_MASK; + val_clk = bclk_ms << RT5640_I2S_BCLK_MS1_SFT | + pre_div << RT5640_I2S_PD1_SFT; + snd_soc_update_bits(codec, RT5640_I2S1_SDP, + RT5640_I2S_DL_MASK, val_len); + snd_soc_update_bits(codec, RT5640_ADDA_CLK1, mask_clk, val_clk); + } + if (dai_sel & RT5640_U_IF2) { + mask_clk = RT5640_I2S_BCLK_MS2_MASK | RT5640_I2S_PD2_MASK; + val_clk = bclk_ms << RT5640_I2S_BCLK_MS2_SFT | + pre_div << RT5640_I2S_PD2_SFT; + snd_soc_update_bits(codec, RT5640_I2S2_SDP, + RT5640_I2S_DL_MASK, val_len); + snd_soc_update_bits(codec, RT5640_ADDA_CLK1, mask_clk, val_clk); + } + + return 0; +} + +static int rt5640_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec); + unsigned int reg_val = 0, dai_sel; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + rt5640->master[dai->id] = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + reg_val |= RT5640_I2S_MS_S; + rt5640->master[dai->id] = 0; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + reg_val |= RT5640_I2S_BP_INV; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_LEFT_J: + reg_val |= RT5640_I2S_DF_LEFT; + break; + case SND_SOC_DAIFMT_DSP_A: + reg_val |= RT5640_I2S_DF_PCM_A; + break; + case SND_SOC_DAIFMT_DSP_B: + reg_val |= RT5640_I2S_DF_PCM_B; + break; + default: + return -EINVAL; + } + + dai_sel = get_sdp_info(codec, dai->id); + if (dai_sel < 0) { + dev_err(codec->dev, "Failed to get sdp info: %d\n", dai_sel); + return -EINVAL; + } + if (dai_sel & RT5640_U_IF1) { + snd_soc_update_bits(codec, RT5640_I2S1_SDP, + RT5640_I2S_MS_MASK | RT5640_I2S_BP_MASK | + RT5640_I2S_DF_MASK, reg_val); + } + if (dai_sel & RT5640_U_IF2) { + snd_soc_update_bits(codec, RT5640_I2S2_SDP, + RT5640_I2S_MS_MASK | RT5640_I2S_BP_MASK | + RT5640_I2S_DF_MASK, reg_val); + } + + return 0; +} + +static int rt5640_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec); + unsigned int reg_val = 0; + + if (freq == rt5640->sysclk && clk_id == rt5640->sysclk_src) + return 0; + + switch (clk_id) { + case RT5640_SCLK_S_MCLK: + reg_val |= RT5640_SCLK_SRC_MCLK; + break; + case RT5640_SCLK_S_PLL1: + reg_val |= RT5640_SCLK_SRC_PLL1; + break; + case RT5640_SCLK_S_PLL1_TK: + reg_val |= RT5640_SCLK_SRC_PLL1T; + break; + case RT5640_SCLK_S_RCCLK: + reg_val |= RT5640_SCLK_SRC_RCCLK; + break; + default: + dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id); + return -EINVAL; + } + snd_soc_update_bits(codec, RT5640_GLB_CLK, + RT5640_SCLK_SRC_MASK, reg_val); + rt5640->sysclk = freq; + rt5640->sysclk_src = clk_id; + + dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id); + return 0; +} + +/** + * rt5640_pll_calc - Calculate PLL M/N/K code. + * @freq_in: external clock provided to codec. + * @freq_out: target clock which codec works on. + * @pll_code: Pointer to structure with M, N, K and bypass flag. + * + * Calculate M/N/K code to configure PLL for codec. And K is assigned to 2 + * which make calculation more efficiently. + * + * Returns 0 for success or negative error code. + */ +static int rt5640_pll_calc(const unsigned int freq_in, + const unsigned int freq_out, struct rt5640_pll_code *pll_code) +{ + int max_n = RT5640_PLL_N_MAX, max_m = RT5640_PLL_M_MAX; + int n = 0, m = 0, red, n_t, m_t, in_t, out_t; + int red_t = abs(freq_out - freq_in); + bool bypass = false; + + if (RT5640_PLL_INP_MAX < freq_in || RT5640_PLL_INP_MIN > freq_in) + return -EINVAL; + + for (n_t = 0; n_t <= max_n; n_t++) { + in_t = (freq_in >> 1) + (freq_in >> 2) * n_t; + if (in_t < 0) + continue; + if (in_t == freq_out) { + bypass = true; + n = n_t; + goto code_find; + } + for (m_t = 0; m_t <= max_m; m_t++) { + out_t = in_t / (m_t + 2); + red = abs(out_t - freq_out); + if (red < red_t) { + n = n_t; + m = m_t; + if (red == 0) + goto code_find; + red_t = red; + } + } + } + pr_debug("Only get approximation about PLL\n"); + +code_find: + pll_code->m_bp = bypass; + pll_code->m_code = m; + pll_code->n_code = n; + pll_code->k_code = 2; + return 0; +} + +static int rt5640_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_codec *codec = dai->codec; + struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec); + struct rt5640_pll_code *pll_code = &rt5640->pll_code; + int ret, dai_sel; + + if (source == rt5640->pll_src && freq_in == rt5640->pll_in && + freq_out == rt5640->pll_out) + return 0; + + if (!freq_in || !freq_out) { + dev_dbg(codec->dev, "PLL disabled\n"); + + rt5640->pll_in = 0; + rt5640->pll_out = 0; + snd_soc_update_bits(codec, RT5640_GLB_CLK, + RT5640_SCLK_SRC_MASK, RT5640_SCLK_SRC_MCLK); + return 0; + } + + switch (source) { + case RT5640_PLL1_S_MCLK: + snd_soc_update_bits(codec, RT5640_GLB_CLK, + RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_MCLK); + break; + case RT5640_PLL1_S_BCLK1: + case RT5640_PLL1_S_BCLK2: + dai_sel = get_sdp_info(codec, dai->id); + if (dai_sel < 0) { + dev_err(codec->dev, + "Failed to get sdp info: %d\n", dai_sel); + return -EINVAL; + } + if (dai_sel & RT5640_U_IF1) { + snd_soc_update_bits(codec, RT5640_GLB_CLK, + RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_BCLK1); + } + if (dai_sel & RT5640_U_IF2) { + snd_soc_update_bits(codec, RT5640_GLB_CLK, + RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_BCLK2); + } + break; + default: + dev_err(codec->dev, "Unknown PLL source %d\n", source); + return -EINVAL; + } + + ret = rt5640_pll_calc(freq_in, freq_out, pll_code); + if (ret < 0) { + dev_err(codec->dev, "Unsupport input clock %d\n", freq_in); + return ret; + } + + dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=2\n", pll_code->m_bp, + (pll_code->m_bp ? 0 : pll_code->m_code), pll_code->n_code); + + snd_soc_write(codec, RT5640_PLL_CTRL1, + pll_code->n_code << RT5640_PLL_N_SFT | pll_code->k_code); + snd_soc_write(codec, RT5640_PLL_CTRL2, + (pll_code->m_bp ? 0 : pll_code->m_code) << RT5640_PLL_M_SFT | + pll_code->m_bp << RT5640_PLL_M_BP_SFT); + + rt5640->pll_in = freq_in; + rt5640->pll_out = freq_out; + rt5640->pll_src = source; + + return 0; +} + +static int rt5640_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec); + switch (level) { + case SND_SOC_BIAS_STANDBY: + if (SND_SOC_BIAS_OFF == codec->dapm.bias_level) { + regcache_cache_only(rt5640->regmap, false); + snd_soc_update_bits(codec, RT5640_PWR_ANLG1, + RT5640_PWR_VREF1 | RT5640_PWR_MB | + RT5640_PWR_BG | RT5640_PWR_VREF2, + RT5640_PWR_VREF1 | RT5640_PWR_MB | + RT5640_PWR_BG | RT5640_PWR_VREF2); + mdelay(10); + snd_soc_update_bits(codec, RT5640_PWR_ANLG1, + RT5640_PWR_FV1 | RT5640_PWR_FV2, + RT5640_PWR_FV1 | RT5640_PWR_FV2); + regcache_sync(rt5640->regmap); + snd_soc_update_bits(codec, RT5640_DUMMY1, + 0x0301, 0x0301); + snd_soc_update_bits(codec, RT5640_DEPOP_M1, + 0x001d, 0x0019); + snd_soc_update_bits(codec, RT5640_DEPOP_M2, + 0x2000, 0x2000); + snd_soc_update_bits(codec, RT5640_MICBIAS, + 0x0030, 0x0030); + } + break; + + case SND_SOC_BIAS_OFF: + snd_soc_write(codec, RT5640_DEPOP_M1, 0x0004); + snd_soc_write(codec, RT5640_DEPOP_M2, 0x1100); + snd_soc_update_bits(codec, RT5640_DUMMY1, 0x1, 0); + snd_soc_write(codec, RT5640_PWR_DIG1, 0x0000); + snd_soc_write(codec, RT5640_PWR_DIG2, 0x0000); + snd_soc_write(codec, RT5640_PWR_VOL, 0x0000); + snd_soc_write(codec, RT5640_PWR_MIXER, 0x0000); + snd_soc_write(codec, RT5640_PWR_ANLG1, 0x0000); + snd_soc_write(codec, RT5640_PWR_ANLG2, 0x0000); + break; + + default: + break; + } + codec->dapm.bias_level = level; + + return 0; +} + +static int rt5640_probe(struct snd_soc_codec *codec) +{ + struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec); + int ret; + + rt5640->codec = codec; + codec->control_data = rt5640->regmap; + + ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_REGMAP); + if (ret != 0) { + dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); + return ret; + } + + codec->dapm.idle_bias_off = 1; + rt5640_set_bias_level(codec, SND_SOC_BIAS_OFF); + + snd_soc_update_bits(codec, RT5640_DUMMY1, 0x0301, 0x0301); + snd_soc_update_bits(codec, RT5640_DEPOP_M1, 0x001d, 0x0019); + snd_soc_update_bits(codec, RT5640_DEPOP_M2, 0x2000, 0x2000); + snd_soc_update_bits(codec, RT5640_MICBIAS, 0x0030, 0x0030); + snd_soc_update_bits(codec, RT5640_DSP_PATH2, 0xfc00, 0x0c00); + + return 0; +} + +static int rt5640_remove(struct snd_soc_codec *codec) +{ + rt5640_reset(codec); + + return 0; +} + +#ifdef CONFIG_PM +static int rt5640_suspend(struct snd_soc_codec *codec) +{ + struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec); + + rt5640_set_bias_level(codec, SND_SOC_BIAS_OFF); + rt5640_reset(codec); + regcache_cache_only(rt5640->regmap, true); + regcache_mark_dirty(rt5640->regmap); + + return 0; +} + +static int rt5640_resume(struct snd_soc_codec *codec) +{ + rt5640_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + return 0; +} +#else +#define rt5640_suspend NULL +#define rt5640_resume NULL +#endif + +#define RT5640_STEREO_RATES SNDRV_PCM_RATE_8000_96000 +#define RT5640_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) + +struct snd_soc_dai_ops rt5640_aif_dai_ops = { + .hw_params = rt5640_hw_params, + .set_fmt = rt5640_set_dai_fmt, + .set_sysclk = rt5640_set_dai_sysclk, + .set_pll = rt5640_set_dai_pll, +}; + +struct snd_soc_dai_driver rt5640_dai[] = { + { + .name = "rt5640-aif1", + .id = RT5640_AIF1, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5640_STEREO_RATES, + .formats = RT5640_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5640_STEREO_RATES, + .formats = RT5640_FORMATS, + }, + .ops = &rt5640_aif_dai_ops, + }, + { + .name = "rt5640-aif2", + .id = RT5640_AIF2, + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT5640_STEREO_RATES, + .formats = RT5640_FORMATS, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT5640_STEREO_RATES, + .formats = RT5640_FORMATS, + }, + .ops = &rt5640_aif_dai_ops, + }, +}; + +static struct snd_soc_codec_driver soc_codec_dev_rt5640 = { + .probe = rt5640_probe, + .remove = rt5640_remove, + .suspend = rt5640_suspend, + .resume = rt5640_resume, + .set_bias_level = rt5640_set_bias_level, + .controls = rt5640_snd_controls, + .num_controls = ARRAY_SIZE(rt5640_snd_controls), + .dapm_widgets = rt5640_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt5640_dapm_widgets), + .dapm_routes = rt5640_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt5640_dapm_routes), +}; + +static const struct regmap_config rt5640_regmap = { + .reg_bits = 8, + .val_bits = 16, + + .max_register = RT5640_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5640_ranges) * + RT5640_PR_SPACING), + .volatile_reg = rt5640_volatile_register, + .readable_reg = rt5640_readable_register, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = rt5640_reg, + .num_reg_defaults = ARRAY_SIZE(rt5640_reg), + .ranges = rt5640_ranges, + .num_ranges = ARRAY_SIZE(rt5640_ranges), +}; + +static const struct i2c_device_id rt5640_i2c_id[] = { + { "rt5640", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rt5640_i2c_id); + +static int rt5640_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct rt5640_platform_data *pdata = dev_get_platdata(&i2c->dev); + struct rt5640_priv *rt5640; + int ret; + unsigned int val; + + rt5640 = devm_kzalloc(&i2c->dev, + sizeof(struct rt5640_priv), + GFP_KERNEL); + if (NULL == rt5640) + return -ENOMEM; + + rt5640->regmap = devm_regmap_init_i2c(i2c, &rt5640_regmap); + if (IS_ERR(rt5640->regmap)) { + ret = PTR_ERR(rt5640->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + if (pdata) + rt5640->pdata = *pdata; + + i2c_set_clientdata(i2c, rt5640); + + if (rt5640->pdata.ldo1_en) { + ret = devm_gpio_request_one(&i2c->dev, rt5640->pdata.ldo1_en, + GPIOF_OUT_INIT_HIGH, + "RT5640 LDO1_EN"); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to request LDO1_EN %d: %d\n", + rt5640->pdata.ldo1_en, ret); + return ret; + } + msleep(400); + } + + regmap_read(rt5640->regmap, RT5640_VENDOR_ID2, &val); + if ((val != RT5640_DEVICE_ID)) { + dev_err(&i2c->dev, + "Device with ID register %x is not rt5640/39\n", val); + return -ENODEV; + } + + regmap_write(rt5640->regmap, RT5640_RESET, 0); + + ret = regmap_register_patch(rt5640->regmap, init_list, + ARRAY_SIZE(init_list)); + if (ret != 0) + dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret); + + if (rt5640->pdata.in1_diff) + regmap_update_bits(rt5640->regmap, RT5640_IN1_IN2, + RT5640_IN_DF1, RT5640_IN_DF1); + + if (rt5640->pdata.in2_diff) + regmap_update_bits(rt5640->regmap, RT5640_IN3_IN4, + RT5640_IN_DF2, RT5640_IN_DF2); + + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5640, + rt5640_dai, ARRAY_SIZE(rt5640_dai)); + if (ret < 0) + goto err; + + return 0; +err: + return ret; +} + +static int rt5640_i2c_remove(struct i2c_client *i2c) +{ + snd_soc_unregister_codec(&i2c->dev); + + return 0; +} + +struct i2c_driver rt5640_i2c_driver = { + .driver = { + .name = "rt5640", + .owner = THIS_MODULE, + }, + .probe = rt5640_i2c_probe, + .remove = rt5640_i2c_remove, + .id_table = rt5640_i2c_id, +}; +module_i2c_driver(rt5640_i2c_driver); + +MODULE_DESCRIPTION("ASoC RT5640 driver"); +MODULE_AUTHOR("Johnny Hsu "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h new file mode 100644 index 000000000000..c48286d7118f --- /dev/null +++ b/sound/soc/codecs/rt5640.h @@ -0,0 +1,2092 @@ +/* + * rt5640.h -- RT5640 ALSA SoC audio driver + * + * Copyright 2011 Realtek Microelectronics + * Author: Johnny Hsu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _RT5640_H +#define _RT5640_H + +#include + +/* Info */ +#define RT5640_RESET 0x00 +#define RT5640_VENDOR_ID 0xfd +#define RT5640_VENDOR_ID1 0xfe +#define RT5640_VENDOR_ID2 0xff +/* I/O - Output */ +#define RT5640_SPK_VOL 0x01 +#define RT5640_HP_VOL 0x02 +#define RT5640_OUTPUT 0x03 +#define RT5640_MONO_OUT 0x04 +/* I/O - Input */ +#define RT5640_IN1_IN2 0x0d +#define RT5640_IN3_IN4 0x0e +#define RT5640_INL_INR_VOL 0x0f +/* I/O - ADC/DAC/DMIC */ +#define RT5640_DAC1_DIG_VOL 0x19 +#define RT5640_DAC2_DIG_VOL 0x1a +#define RT5640_DAC2_CTRL 0x1b +#define RT5640_ADC_DIG_VOL 0x1c +#define RT5640_ADC_DATA 0x1d +#define RT5640_ADC_BST_VOL 0x1e +/* Mixer - D-D */ +#define RT5640_STO_ADC_MIXER 0x27 +#define RT5640_MONO_ADC_MIXER 0x28 +#define RT5640_AD_DA_MIXER 0x29 +#define RT5640_STO_DAC_MIXER 0x2a +#define RT5640_MONO_DAC_MIXER 0x2b +#define RT5640_DIG_MIXER 0x2c +#define RT5640_DSP_PATH1 0x2d +#define RT5640_DSP_PATH2 0x2e +#define RT5640_DIG_INF_DATA 0x2f +/* Mixer - ADC */ +#define RT5640_REC_L1_MIXER 0x3b +#define RT5640_REC_L2_MIXER 0x3c +#define RT5640_REC_R1_MIXER 0x3d +#define RT5640_REC_R2_MIXER 0x3e +/* Mixer - DAC */ +#define RT5640_HPO_MIXER 0x45 +#define RT5640_SPK_L_MIXER 0x46 +#define RT5640_SPK_R_MIXER 0x47 +#define RT5640_SPO_L_MIXER 0x48 +#define RT5640_SPO_R_MIXER 0x49 +#define RT5640_SPO_CLSD_RATIO 0x4a +#define RT5640_MONO_MIXER 0x4c +#define RT5640_OUT_L1_MIXER 0x4d +#define RT5640_OUT_L2_MIXER 0x4e +#define RT5640_OUT_L3_MIXER 0x4f +#define RT5640_OUT_R1_MIXER 0x50 +#define RT5640_OUT_R2_MIXER 0x51 +#define RT5640_OUT_R3_MIXER 0x52 +#define RT5640_LOUT_MIXER 0x53 +/* Power */ +#define RT5640_PWR_DIG1 0x61 +#define RT5640_PWR_DIG2 0x62 +#define RT5640_PWR_ANLG1 0x63 +#define RT5640_PWR_ANLG2 0x64 +#define RT5640_PWR_MIXER 0x65 +#define RT5640_PWR_VOL 0x66 +/* Private Register Control */ +#define RT5640_PRIV_INDEX 0x6a +#define RT5640_PRIV_DATA 0x6c +/* Format - ADC/DAC */ +#define RT5640_I2S1_SDP 0x70 +#define RT5640_I2S2_SDP 0x71 +#define RT5640_ADDA_CLK1 0x73 +#define RT5640_ADDA_CLK2 0x74 +#define RT5640_DMIC 0x75 +/* Function - Analog */ +#define RT5640_GLB_CLK 0x80 +#define RT5640_PLL_CTRL1 0x81 +#define RT5640_PLL_CTRL2 0x82 +#define RT5640_ASRC_1 0x83 +#define RT5640_ASRC_2 0x84 +#define RT5640_ASRC_3 0x85 +#define RT5640_ASRC_4 0x89 +#define RT5640_ASRC_5 0x8a +#define RT5640_HP_OVCD 0x8b +#define RT5640_CLS_D_OVCD 0x8c +#define RT5640_CLS_D_OUT 0x8d +#define RT5640_DEPOP_M1 0x8e +#define RT5640_DEPOP_M2 0x8f +#define RT5640_DEPOP_M3 0x90 +#define RT5640_CHARGE_PUMP 0x91 +#define RT5640_PV_DET_SPK_G 0x92 +#define RT5640_MICBIAS 0x93 +/* Function - Digital */ +#define RT5640_EQ_CTRL1 0xb0 +#define RT5640_EQ_CTRL2 0xb1 +#define RT5640_WIND_FILTER 0xb2 +#define RT5640_DRC_AGC_1 0xb4 +#define RT5640_DRC_AGC_2 0xb5 +#define RT5640_DRC_AGC_3 0xb6 +#define RT5640_SVOL_ZC 0xb7 +#define RT5640_ANC_CTRL1 0xb8 +#define RT5640_ANC_CTRL2 0xb9 +#define RT5640_ANC_CTRL3 0xba +#define RT5640_JD_CTRL 0xbb +#define RT5640_ANC_JD 0xbc +#define RT5640_IRQ_CTRL1 0xbd +#define RT5640_IRQ_CTRL2 0xbe +#define RT5640_INT_IRQ_ST 0xbf +#define RT5640_GPIO_CTRL1 0xc0 +#define RT5640_GPIO_CTRL2 0xc1 +#define RT5640_GPIO_CTRL3 0xc2 +#define RT5640_DSP_CTRL1 0xc4 +#define RT5640_DSP_CTRL2 0xc5 +#define RT5640_DSP_CTRL3 0xc6 +#define RT5640_DSP_CTRL4 0xc7 +#define RT5640_PGM_REG_ARR1 0xc8 +#define RT5640_PGM_REG_ARR2 0xc9 +#define RT5640_PGM_REG_ARR3 0xca +#define RT5640_PGM_REG_ARR4 0xcb +#define RT5640_PGM_REG_ARR5 0xcc +#define RT5640_SCB_FUNC 0xcd +#define RT5640_SCB_CTRL 0xce +#define RT5640_BASE_BACK 0xcf +#define RT5640_MP3_PLUS1 0xd0 +#define RT5640_MP3_PLUS2 0xd1 +#define RT5640_3D_HP 0xd2 +#define RT5640_ADJ_HPF 0xd3 +#define RT5640_HP_CALIB_AMP_DET 0xd6 +#define RT5640_HP_CALIB2 0xd7 +#define RT5640_SV_ZCD1 0xd9 +#define RT5640_SV_ZCD2 0xda +/* Dummy Register */ +#define RT5640_DUMMY1 0xfa +#define RT5640_DUMMY2 0xfb +#define RT5640_DUMMY3 0xfc + + +/* Index of Codec Private Register definition */ +#define RT5640_3D_SPK 0x63 +#define RT5640_WND_1 0x6c +#define RT5640_WND_2 0x6d +#define RT5640_WND_3 0x6e +#define RT5640_WND_4 0x6f +#define RT5640_WND_5 0x70 +#define RT5640_WND_8 0x73 +#define RT5640_DIP_SPK_INF 0x75 +#define RT5640_EQ_BW_LOP 0xa0 +#define RT5640_EQ_GN_LOP 0xa1 +#define RT5640_EQ_FC_BP1 0xa2 +#define RT5640_EQ_BW_BP1 0xa3 +#define RT5640_EQ_GN_BP1 0xa4 +#define RT5640_EQ_FC_BP2 0xa5 +#define RT5640_EQ_BW_BP2 0xa6 +#define RT5640_EQ_GN_BP2 0xa7 +#define RT5640_EQ_FC_BP3 0xa8 +#define RT5640_EQ_BW_BP3 0xa9 +#define RT5640_EQ_GN_BP3 0xaa +#define RT5640_EQ_FC_BP4 0xab +#define RT5640_EQ_BW_BP4 0xac +#define RT5640_EQ_GN_BP4 0xad +#define RT5640_EQ_FC_HIP1 0xae +#define RT5640_EQ_GN_HIP1 0xaf +#define RT5640_EQ_FC_HIP2 0xb0 +#define RT5640_EQ_BW_HIP2 0xb1 +#define RT5640_EQ_GN_HIP2 0xb2 +#define RT5640_EQ_PRE_VOL 0xb3 +#define RT5640_EQ_PST_VOL 0xb4 + +/* global definition */ +#define RT5640_L_MUTE (0x1 << 15) +#define RT5640_L_MUTE_SFT 15 +#define RT5640_VOL_L_MUTE (0x1 << 14) +#define RT5640_VOL_L_SFT 14 +#define RT5640_R_MUTE (0x1 << 7) +#define RT5640_R_MUTE_SFT 7 +#define RT5640_VOL_R_MUTE (0x1 << 6) +#define RT5640_VOL_R_SFT 6 +#define RT5640_L_VOL_MASK (0x3f << 8) +#define RT5640_L_VOL_SFT 8 +#define RT5640_R_VOL_MASK (0x3f) +#define RT5640_R_VOL_SFT 0 + +/* IN1 and IN2 Control (0x0d) */ +/* IN3 and IN4 Control (0x0e) */ +#define RT5640_BST_SFT1 12 +#define RT5640_BST_SFT2 8 +#define RT5640_IN_DF1 (0x1 << 7) +#define RT5640_IN_SFT1 7 +#define RT5640_IN_DF2 (0x1 << 6) +#define RT5640_IN_SFT2 6 + +/* INL and INR Volume Control (0x0f) */ +#define RT5640_INL_SEL_MASK (0x1 << 15) +#define RT5640_INL_SEL_SFT 15 +#define RT5640_INL_SEL_IN4P (0x0 << 15) +#define RT5640_INL_SEL_MONOP (0x1 << 15) +#define RT5640_INL_VOL_MASK (0x1f << 8) +#define RT5640_INL_VOL_SFT 8 +#define RT5640_INR_SEL_MASK (0x1 << 7) +#define RT5640_INR_SEL_SFT 7 +#define RT5640_INR_SEL_IN4N (0x0 << 7) +#define RT5640_INR_SEL_MONON (0x1 << 7) +#define RT5640_INR_VOL_MASK (0x1f) +#define RT5640_INR_VOL_SFT 0 + +/* DAC1 Digital Volume (0x19) */ +#define RT5640_DAC_L1_VOL_MASK (0xff << 8) +#define RT5640_DAC_L1_VOL_SFT 8 +#define RT5640_DAC_R1_VOL_MASK (0xff) +#define RT5640_DAC_R1_VOL_SFT 0 + +/* DAC2 Digital Volume (0x1a) */ +#define RT5640_DAC_L2_VOL_MASK (0xff << 8) +#define RT5640_DAC_L2_VOL_SFT 8 +#define RT5640_DAC_R2_VOL_MASK (0xff) +#define RT5640_DAC_R2_VOL_SFT 0 + +/* DAC2 Control (0x1b) */ +#define RT5640_M_DAC_L2_VOL (0x1 << 13) +#define RT5640_M_DAC_L2_VOL_SFT 13 +#define RT5640_M_DAC_R2_VOL (0x1 << 12) +#define RT5640_M_DAC_R2_VOL_SFT 12 + +/* ADC Digital Volume Control (0x1c) */ +#define RT5640_ADC_L_VOL_MASK (0x7f << 8) +#define RT5640_ADC_L_VOL_SFT 8 +#define RT5640_ADC_R_VOL_MASK (0x7f) +#define RT5640_ADC_R_VOL_SFT 0 + +/* Mono ADC Digital Volume Control (0x1d) */ +#define RT5640_MONO_ADC_L_VOL_MASK (0x7f << 8) +#define RT5640_MONO_ADC_L_VOL_SFT 8 +#define RT5640_MONO_ADC_R_VOL_MASK (0x7f) +#define RT5640_MONO_ADC_R_VOL_SFT 0 + +/* ADC Boost Volume Control (0x1e) */ +#define RT5640_ADC_L_BST_MASK (0x3 << 14) +#define RT5640_ADC_L_BST_SFT 14 +#define RT5640_ADC_R_BST_MASK (0x3 << 12) +#define RT5640_ADC_R_BST_SFT 12 +#define RT5640_ADC_COMP_MASK (0x3 << 10) +#define RT5640_ADC_COMP_SFT 10 + +/* Stereo ADC Mixer Control (0x27) */ +#define RT5640_M_ADC_L1 (0x1 << 14) +#define RT5640_M_ADC_L1_SFT 14 +#define RT5640_M_ADC_L2 (0x1 << 13) +#define RT5640_M_ADC_L2_SFT 13 +#define RT5640_ADC_1_SRC_MASK (0x1 << 12) +#define RT5640_ADC_1_SRC_SFT 12 +#define RT5640_ADC_1_SRC_ADC (0x1 << 12) +#define RT5640_ADC_1_SRC_DACMIX (0x0 << 12) +#define RT5640_ADC_2_SRC_MASK (0x3 << 10) +#define RT5640_ADC_2_SRC_SFT 10 +#define RT5640_ADC_2_SRC_DMIC1 (0x0 << 10) +#define RT5640_ADC_2_SRC_DMIC2 (0x1 << 10) +#define RT5640_ADC_2_SRC_DACMIX (0x2 << 10) +#define RT5640_M_ADC_R1 (0x1 << 6) +#define RT5640_M_ADC_R1_SFT 6 +#define RT5640_M_ADC_R2 (0x1 << 5) +#define RT5640_M_ADC_R2_SFT 5 + +/* Mono ADC Mixer Control (0x28) */ +#define RT5640_M_MONO_ADC_L1 (0x1 << 14) +#define RT5640_M_MONO_ADC_L1_SFT 14 +#define RT5640_M_MONO_ADC_L2 (0x1 << 13) +#define RT5640_M_MONO_ADC_L2_SFT 13 +#define RT5640_MONO_ADC_L1_SRC_MASK (0x1 << 12) +#define RT5640_MONO_ADC_L1_SRC_SFT 12 +#define RT5640_MONO_ADC_L1_SRC_DACMIXL (0x0 << 12) +#define RT5640_MONO_ADC_L1_SRC_ADCL (0x1 << 12) +#define RT5640_MONO_ADC_L2_SRC_MASK (0x3 << 10) +#define RT5640_MONO_ADC_L2_SRC_SFT 10 +#define RT5640_MONO_ADC_L2_SRC_DMIC_L1 (0x0 << 10) +#define RT5640_MONO_ADC_L2_SRC_DMIC_L2 (0x1 << 10) +#define RT5640_MONO_ADC_L2_SRC_DACMIXL (0x2 << 10) +#define RT5640_M_MONO_ADC_R1 (0x1 << 6) +#define RT5640_M_MONO_ADC_R1_SFT 6 +#define RT5640_M_MONO_ADC_R2 (0x1 << 5) +#define RT5640_M_MONO_ADC_R2_SFT 5 +#define RT5640_MONO_ADC_R1_SRC_MASK (0x1 << 4) +#define RT5640_MONO_ADC_R1_SRC_SFT 4 +#define RT5640_MONO_ADC_R1_SRC_ADCR (0x1 << 4) +#define RT5640_MONO_ADC_R1_SRC_DACMIXR (0x0 << 4) +#define RT5640_MONO_ADC_R2_SRC_MASK (0x3 << 2) +#define RT5640_MONO_ADC_R2_SRC_SFT 2 +#define RT5640_MONO_ADC_R2_SRC_DMIC_R1 (0x0 << 2) +#define RT5640_MONO_ADC_R2_SRC_DMIC_R2 (0x1 << 2) +#define RT5640_MONO_ADC_R2_SRC_DACMIXR (0x2 << 2) + +/* ADC Mixer to DAC Mixer Control (0x29) */ +#define RT5640_M_ADCMIX_L (0x1 << 15) +#define RT5640_M_ADCMIX_L_SFT 15 +#define RT5640_M_IF1_DAC_L (0x1 << 14) +#define RT5640_M_IF1_DAC_L_SFT 14 +#define RT5640_M_ADCMIX_R (0x1 << 7) +#define RT5640_M_ADCMIX_R_SFT 7 +#define RT5640_M_IF1_DAC_R (0x1 << 6) +#define RT5640_M_IF1_DAC_R_SFT 6 + +/* Stereo DAC Mixer Control (0x2a) */ +#define RT5640_M_DAC_L1 (0x1 << 14) +#define RT5640_M_DAC_L1_SFT 14 +#define RT5640_DAC_L1_STO_L_VOL_MASK (0x1 << 13) +#define RT5640_DAC_L1_STO_L_VOL_SFT 13 +#define RT5640_M_DAC_L2 (0x1 << 12) +#define RT5640_M_DAC_L2_SFT 12 +#define RT5640_DAC_L2_STO_L_VOL_MASK (0x1 << 11) +#define RT5640_DAC_L2_STO_L_VOL_SFT 11 +#define RT5640_M_ANC_DAC_L (0x1 << 10) +#define RT5640_M_ANC_DAC_L_SFT 10 +#define RT5640_M_DAC_R1 (0x1 << 6) +#define RT5640_M_DAC_R1_SFT 6 +#define RT5640_DAC_R1_STO_R_VOL_MASK (0x1 << 5) +#define RT5640_DAC_R1_STO_R_VOL_SFT 5 +#define RT5640_M_DAC_R2 (0x1 << 4) +#define RT5640_M_DAC_R2_SFT 4 +#define RT5640_DAC_R2_STO_R_VOL_MASK (0x1 << 3) +#define RT5640_DAC_R2_STO_R_VOL_SFT 3 +#define RT5640_M_ANC_DAC_R (0x1 << 2) +#define RT5640_M_ANC_DAC_R_SFT 2 + +/* Mono DAC Mixer Control (0x2b) */ +#define RT5640_M_DAC_L1_MONO_L (0x1 << 14) +#define RT5640_M_DAC_L1_MONO_L_SFT 14 +#define RT5640_DAC_L1_MONO_L_VOL_MASK (0x1 << 13) +#define RT5640_DAC_L1_MONO_L_VOL_SFT 13 +#define RT5640_M_DAC_L2_MONO_L (0x1 << 12) +#define RT5640_M_DAC_L2_MONO_L_SFT 12 +#define RT5640_DAC_L2_MONO_L_VOL_MASK (0x1 << 11) +#define RT5640_DAC_L2_MONO_L_VOL_SFT 11 +#define RT5640_M_DAC_R2_MONO_L (0x1 << 10) +#define RT5640_M_DAC_R2_MONO_L_SFT 10 +#define RT5640_DAC_R2_MONO_L_VOL_MASK (0x1 << 9) +#define RT5640_DAC_R2_MONO_L_VOL_SFT 9 +#define RT5640_M_DAC_R1_MONO_R (0x1 << 6) +#define RT5640_M_DAC_R1_MONO_R_SFT 6 +#define RT5640_DAC_R1_MONO_R_VOL_MASK (0x1 << 5) +#define RT5640_DAC_R1_MONO_R_VOL_SFT 5 +#define RT5640_M_DAC_R2_MONO_R (0x1 << 4) +#define RT5640_M_DAC_R2_MONO_R_SFT 4 +#define RT5640_DAC_R2_MONO_R_VOL_MASK (0x1 << 3) +#define RT5640_DAC_R2_MONO_R_VOL_SFT 3 +#define RT5640_M_DAC_L2_MONO_R (0x1 << 2) +#define RT5640_M_DAC_L2_MONO_R_SFT 2 +#define RT5640_DAC_L2_MONO_R_VOL_MASK (0x1 << 1) +#define RT5640_DAC_L2_MONO_R_VOL_SFT 1 + +/* Digital Mixer Control (0x2c) */ +#define RT5640_M_STO_L_DAC_L (0x1 << 15) +#define RT5640_M_STO_L_DAC_L_SFT 15 +#define RT5640_STO_L_DAC_L_VOL_MASK (0x1 << 14) +#define RT5640_STO_L_DAC_L_VOL_SFT 14 +#define RT5640_M_DAC_L2_DAC_L (0x1 << 13) +#define RT5640_M_DAC_L2_DAC_L_SFT 13 +#define RT5640_DAC_L2_DAC_L_VOL_MASK (0x1 << 12) +#define RT5640_DAC_L2_DAC_L_VOL_SFT 12 +#define RT5640_M_STO_R_DAC_R (0x1 << 11) +#define RT5640_M_STO_R_DAC_R_SFT 11 +#define RT5640_STO_R_DAC_R_VOL_MASK (0x1 << 10) +#define RT5640_STO_R_DAC_R_VOL_SFT 10 +#define RT5640_M_DAC_R2_DAC_R (0x1 << 9) +#define RT5640_M_DAC_R2_DAC_R_SFT 9 +#define RT5640_DAC_R2_DAC_R_VOL_MASK (0x1 << 8) +#define RT5640_DAC_R2_DAC_R_VOL_SFT 8 + +/* DSP Path Control 1 (0x2d) */ +#define RT5640_RXDP_SRC_MASK (0x1 << 15) +#define RT5640_RXDP_SRC_SFT 15 +#define RT5640_RXDP_SRC_NOR (0x0 << 15) +#define RT5640_RXDP_SRC_DIV3 (0x1 << 15) +#define RT5640_TXDP_SRC_MASK (0x1 << 14) +#define RT5640_TXDP_SRC_SFT 14 +#define RT5640_TXDP_SRC_NOR (0x0 << 14) +#define RT5640_TXDP_SRC_DIV3 (0x1 << 14) + +/* DSP Path Control 2 (0x2e) */ +#define RT5640_DAC_L2_SEL_MASK (0x3 << 14) +#define RT5640_DAC_L2_SEL_SFT 14 +#define RT5640_DAC_L2_SEL_IF2 (0x0 << 14) +#define RT5640_DAC_L2_SEL_IF3 (0x1 << 14) +#define RT5640_DAC_L2_SEL_TXDC (0x2 << 14) +#define RT5640_DAC_L2_SEL_BASS (0x3 << 14) +#define RT5640_DAC_R2_SEL_MASK (0x3 << 12) +#define RT5640_DAC_R2_SEL_SFT 12 +#define RT5640_DAC_R2_SEL_IF2 (0x0 << 12) +#define RT5640_DAC_R2_SEL_IF3 (0x1 << 12) +#define RT5640_DAC_R2_SEL_TXDC (0x2 << 12) +#define RT5640_IF2_ADC_L_SEL_MASK (0x1 << 11) +#define RT5640_IF2_ADC_L_SEL_SFT 11 +#define RT5640_IF2_ADC_L_SEL_TXDP (0x0 << 11) +#define RT5640_IF2_ADC_L_SEL_PASS (0x1 << 11) +#define RT5640_IF2_ADC_R_SEL_MASK (0x1 << 10) +#define RT5640_IF2_ADC_R_SEL_SFT 10 +#define RT5640_IF2_ADC_R_SEL_TXDP (0x0 << 10) +#define RT5640_IF2_ADC_R_SEL_PASS (0x1 << 10) +#define RT5640_RXDC_SEL_MASK (0x3 << 8) +#define RT5640_RXDC_SEL_SFT 8 +#define RT5640_RXDC_SEL_NOR (0x0 << 8) +#define RT5640_RXDC_SEL_L2R (0x1 << 8) +#define RT5640_RXDC_SEL_R2L (0x2 << 8) +#define RT5640_RXDC_SEL_SWAP (0x3 << 8) +#define RT5640_RXDP_SEL_MASK (0x3 << 6) +#define RT5640_RXDP_SEL_SFT 6 +#define RT5640_RXDP_SEL_NOR (0x0 << 6) +#define RT5640_RXDP_SEL_L2R (0x1 << 6) +#define RT5640_RXDP_SEL_R2L (0x2 << 6) +#define RT5640_RXDP_SEL_SWAP (0x3 << 6) +#define RT5640_TXDC_SEL_MASK (0x3 << 4) +#define RT5640_TXDC_SEL_SFT 4 +#define RT5640_TXDC_SEL_NOR (0x0 << 4) +#define RT5640_TXDC_SEL_L2R (0x1 << 4) +#define RT5640_TXDC_SEL_R2L (0x2 << 4) +#define RT5640_TXDC_SEL_SWAP (0x3 << 4) +#define RT5640_TXDP_SEL_MASK (0x3 << 2) +#define RT5640_TXDP_SEL_SFT 2 +#define RT5640_TXDP_SEL_NOR (0x0 << 2) +#define RT5640_TXDP_SEL_L2R (0x1 << 2) +#define RT5640_TXDP_SEL_R2L (0x2 << 2) +#define RT5640_TRXDP_SEL_SWAP (0x3 << 2) + +/* Digital Interface Data Control (0x2f) */ +#define RT5640_IF1_DAC_SEL_MASK (0x3 << 14) +#define RT5640_IF1_DAC_SEL_SFT 14 +#define RT5640_IF1_DAC_SEL_NOR (0x0 << 14) +#define RT5640_IF1_DAC_SEL_L2R (0x1 << 14) +#define RT5640_IF1_DAC_SEL_R2L (0x2 << 14) +#define RT5640_IF1_DAC_SEL_SWAP (0x3 << 14) +#define RT5640_IF1_ADC_SEL_MASK (0x3 << 12) +#define RT5640_IF1_ADC_SEL_SFT 12 +#define RT5640_IF1_ADC_SEL_NOR (0x0 << 12) +#define RT5640_IF1_ADC_SEL_L2R (0x1 << 12) +#define RT5640_IF1_ADC_SEL_R2L (0x2 << 12) +#define RT5640_IF1_ADC_SEL_SWAP (0x3 << 12) +#define RT5640_IF2_DAC_SEL_MASK (0x3 << 10) +#define RT5640_IF2_DAC_SEL_SFT 10 +#define RT5640_IF2_DAC_SEL_NOR (0x0 << 10) +#define RT5640_IF2_DAC_SEL_L2R (0x1 << 10) +#define RT5640_IF2_DAC_SEL_R2L (0x2 << 10) +#define RT5640_IF2_DAC_SEL_SWAP (0x3 << 10) +#define RT5640_IF2_ADC_SEL_MASK (0x3 << 8) +#define RT5640_IF2_ADC_SEL_SFT 8 +#define RT5640_IF2_ADC_SEL_NOR (0x0 << 8) +#define RT5640_IF2_ADC_SEL_L2R (0x1 << 8) +#define RT5640_IF2_ADC_SEL_R2L (0x2 << 8) +#define RT5640_IF2_ADC_SEL_SWAP (0x3 << 8) +#define RT5640_IF3_DAC_SEL_MASK (0x3 << 6) +#define RT5640_IF3_DAC_SEL_SFT 6 +#define RT5640_IF3_DAC_SEL_NOR (0x0 << 6) +#define RT5640_IF3_DAC_SEL_L2R (0x1 << 6) +#define RT5640_IF3_DAC_SEL_R2L (0x2 << 6) +#define RT5640_IF3_DAC_SEL_SWAP (0x3 << 6) +#define RT5640_IF3_ADC_SEL_MASK (0x3 << 4) +#define RT5640_IF3_ADC_SEL_SFT 4 +#define RT5640_IF3_ADC_SEL_NOR (0x0 << 4) +#define RT5640_IF3_ADC_SEL_L2R (0x1 << 4) +#define RT5640_IF3_ADC_SEL_R2L (0x2 << 4) +#define RT5640_IF3_ADC_SEL_SWAP (0x3 << 4) + +/* REC Left Mixer Control 1 (0x3b) */ +#define RT5640_G_HP_L_RM_L_MASK (0x7 << 13) +#define RT5640_G_HP_L_RM_L_SFT 13 +#define RT5640_G_IN_L_RM_L_MASK (0x7 << 10) +#define RT5640_G_IN_L_RM_L_SFT 10 +#define RT5640_G_BST4_RM_L_MASK (0x7 << 7) +#define RT5640_G_BST4_RM_L_SFT 7 +#define RT5640_G_BST3_RM_L_MASK (0x7 << 4) +#define RT5640_G_BST3_RM_L_SFT 4 +#define RT5640_G_BST2_RM_L_MASK (0x7 << 1) +#define RT5640_G_BST2_RM_L_SFT 1 + +/* REC Left Mixer Control 2 (0x3c) */ +#define RT5640_G_BST1_RM_L_MASK (0x7 << 13) +#define RT5640_G_BST1_RM_L_SFT 13 +#define RT5640_G_OM_L_RM_L_MASK (0x7 << 10) +#define RT5640_G_OM_L_RM_L_SFT 10 +#define RT5640_M_HP_L_RM_L (0x1 << 6) +#define RT5640_M_HP_L_RM_L_SFT 6 +#define RT5640_M_IN_L_RM_L (0x1 << 5) +#define RT5640_M_IN_L_RM_L_SFT 5 +#define RT5640_M_BST4_RM_L (0x1 << 4) +#define RT5640_M_BST4_RM_L_SFT 4 +#define RT5640_M_BST3_RM_L (0x1 << 3) +#define RT5640_M_BST3_RM_L_SFT 3 +#define RT5640_M_BST2_RM_L (0x1 << 2) +#define RT5640_M_BST2_RM_L_SFT 2 +#define RT5640_M_BST1_RM_L (0x1 << 1) +#define RT5640_M_BST1_RM_L_SFT 1 +#define RT5640_M_OM_L_RM_L (0x1) +#define RT5640_M_OM_L_RM_L_SFT 0 + +/* REC Right Mixer Control 1 (0x3d) */ +#define RT5640_G_HP_R_RM_R_MASK (0x7 << 13) +#define RT5640_G_HP_R_RM_R_SFT 13 +#define RT5640_G_IN_R_RM_R_MASK (0x7 << 10) +#define RT5640_G_IN_R_RM_R_SFT 10 +#define RT5640_G_BST4_RM_R_MASK (0x7 << 7) +#define RT5640_G_BST4_RM_R_SFT 7 +#define RT5640_G_BST3_RM_R_MASK (0x7 << 4) +#define RT5640_G_BST3_RM_R_SFT 4 +#define RT5640_G_BST2_RM_R_MASK (0x7 << 1) +#define RT5640_G_BST2_RM_R_SFT 1 + +/* REC Right Mixer Control 2 (0x3e) */ +#define RT5640_G_BST1_RM_R_MASK (0x7 << 13) +#define RT5640_G_BST1_RM_R_SFT 13 +#define RT5640_G_OM_R_RM_R_MASK (0x7 << 10) +#define RT5640_G_OM_R_RM_R_SFT 10 +#define RT5640_M_HP_R_RM_R (0x1 << 6) +#define RT5640_M_HP_R_RM_R_SFT 6 +#define RT5640_M_IN_R_RM_R (0x1 << 5) +#define RT5640_M_IN_R_RM_R_SFT 5 +#define RT5640_M_BST4_RM_R (0x1 << 4) +#define RT5640_M_BST4_RM_R_SFT 4 +#define RT5640_M_BST3_RM_R (0x1 << 3) +#define RT5640_M_BST3_RM_R_SFT 3 +#define RT5640_M_BST2_RM_R (0x1 << 2) +#define RT5640_M_BST2_RM_R_SFT 2 +#define RT5640_M_BST1_RM_R (0x1 << 1) +#define RT5640_M_BST1_RM_R_SFT 1 +#define RT5640_M_OM_R_RM_R (0x1) +#define RT5640_M_OM_R_RM_R_SFT 0 + +/* HPMIX Control (0x45) */ +#define RT5640_M_DAC2_HM (0x1 << 15) +#define RT5640_M_DAC2_HM_SFT 15 +#define RT5640_M_DAC1_HM (0x1 << 14) +#define RT5640_M_DAC1_HM_SFT 14 +#define RT5640_M_HPVOL_HM (0x1 << 13) +#define RT5640_M_HPVOL_HM_SFT 13 +#define RT5640_G_HPOMIX_MASK (0x1 << 12) +#define RT5640_G_HPOMIX_SFT 12 + +/* SPK Left Mixer Control (0x46) */ +#define RT5640_G_RM_L_SM_L_MASK (0x3 << 14) +#define RT5640_G_RM_L_SM_L_SFT 14 +#define RT5640_G_IN_L_SM_L_MASK (0x3 << 12) +#define RT5640_G_IN_L_SM_L_SFT 12 +#define RT5640_G_DAC_L1_SM_L_MASK (0x3 << 10) +#define RT5640_G_DAC_L1_SM_L_SFT 10 +#define RT5640_G_DAC_L2_SM_L_MASK (0x3 << 8) +#define RT5640_G_DAC_L2_SM_L_SFT 8 +#define RT5640_G_OM_L_SM_L_MASK (0x3 << 6) +#define RT5640_G_OM_L_SM_L_SFT 6 +#define RT5640_M_RM_L_SM_L (0x1 << 5) +#define RT5640_M_RM_L_SM_L_SFT 5 +#define RT5640_M_IN_L_SM_L (0x1 << 4) +#define RT5640_M_IN_L_SM_L_SFT 4 +#define RT5640_M_DAC_L1_SM_L (0x1 << 3) +#define RT5640_M_DAC_L1_SM_L_SFT 3 +#define RT5640_M_DAC_L2_SM_L (0x1 << 2) +#define RT5640_M_DAC_L2_SM_L_SFT 2 +#define RT5640_M_OM_L_SM_L (0x1 << 1) +#define RT5640_M_OM_L_SM_L_SFT 1 + +/* SPK Right Mixer Control (0x47) */ +#define RT5640_G_RM_R_SM_R_MASK (0x3 << 14) +#define RT5640_G_RM_R_SM_R_SFT 14 +#define RT5640_G_IN_R_SM_R_MASK (0x3 << 12) +#define RT5640_G_IN_R_SM_R_SFT 12 +#define RT5640_G_DAC_R1_SM_R_MASK (0x3 << 10) +#define RT5640_G_DAC_R1_SM_R_SFT 10 +#define RT5640_G_DAC_R2_SM_R_MASK (0x3 << 8) +#define RT5640_G_DAC_R2_SM_R_SFT 8 +#define RT5640_G_OM_R_SM_R_MASK (0x3 << 6) +#define RT5640_G_OM_R_SM_R_SFT 6 +#define RT5640_M_RM_R_SM_R (0x1 << 5) +#define RT5640_M_RM_R_SM_R_SFT 5 +#define RT5640_M_IN_R_SM_R (0x1 << 4) +#define RT5640_M_IN_R_SM_R_SFT 4 +#define RT5640_M_DAC_R1_SM_R (0x1 << 3) +#define RT5640_M_DAC_R1_SM_R_SFT 3 +#define RT5640_M_DAC_R2_SM_R (0x1 << 2) +#define RT5640_M_DAC_R2_SM_R_SFT 2 +#define RT5640_M_OM_R_SM_R (0x1 << 1) +#define RT5640_M_OM_R_SM_R_SFT 1 + +/* SPOLMIX Control (0x48) */ +#define RT5640_M_DAC_R1_SPM_L (0x1 << 15) +#define RT5640_M_DAC_R1_SPM_L_SFT 15 +#define RT5640_M_DAC_L1_SPM_L (0x1 << 14) +#define RT5640_M_DAC_L1_SPM_L_SFT 14 +#define RT5640_M_SV_R_SPM_L (0x1 << 13) +#define RT5640_M_SV_R_SPM_L_SFT 13 +#define RT5640_M_SV_L_SPM_L (0x1 << 12) +#define RT5640_M_SV_L_SPM_L_SFT 12 +#define RT5640_M_BST1_SPM_L (0x1 << 11) +#define RT5640_M_BST1_SPM_L_SFT 11 + +/* SPORMIX Control (0x49) */ +#define RT5640_M_DAC_R1_SPM_R (0x1 << 13) +#define RT5640_M_DAC_R1_SPM_R_SFT 13 +#define RT5640_M_SV_R_SPM_R (0x1 << 12) +#define RT5640_M_SV_R_SPM_R_SFT 12 +#define RT5640_M_BST1_SPM_R (0x1 << 11) +#define RT5640_M_BST1_SPM_R_SFT 11 + +/* SPOLMIX / SPORMIX Ratio Control (0x4a) */ +#define RT5640_SPO_CLSD_RATIO_MASK (0x7) +#define RT5640_SPO_CLSD_RATIO_SFT 0 + +/* Mono Output Mixer Control (0x4c) */ +#define RT5640_M_DAC_R2_MM (0x1 << 15) +#define RT5640_M_DAC_R2_MM_SFT 15 +#define RT5640_M_DAC_L2_MM (0x1 << 14) +#define RT5640_M_DAC_L2_MM_SFT 14 +#define RT5640_M_OV_R_MM (0x1 << 13) +#define RT5640_M_OV_R_MM_SFT 13 +#define RT5640_M_OV_L_MM (0x1 << 12) +#define RT5640_M_OV_L_MM_SFT 12 +#define RT5640_M_BST1_MM (0x1 << 11) +#define RT5640_M_BST1_MM_SFT 11 +#define RT5640_G_MONOMIX_MASK (0x1 << 10) +#define RT5640_G_MONOMIX_SFT 10 + +/* Output Left Mixer Control 1 (0x4d) */ +#define RT5640_G_BST3_OM_L_MASK (0x7 << 13) +#define RT5640_G_BST3_OM_L_SFT 13 +#define RT5640_G_BST2_OM_L_MASK (0x7 << 10) +#define RT5640_G_BST2_OM_L_SFT 10 +#define RT5640_G_BST1_OM_L_MASK (0x7 << 7) +#define RT5640_G_BST1_OM_L_SFT 7 +#define RT5640_G_IN_L_OM_L_MASK (0x7 << 4) +#define RT5640_G_IN_L_OM_L_SFT 4 +#define RT5640_G_RM_L_OM_L_MASK (0x7 << 1) +#define RT5640_G_RM_L_OM_L_SFT 1 + +/* Output Left Mixer Control 2 (0x4e) */ +#define RT5640_G_DAC_R2_OM_L_MASK (0x7 << 13) +#define RT5640_G_DAC_R2_OM_L_SFT 13 +#define RT5640_G_DAC_L2_OM_L_MASK (0x7 << 10) +#define RT5640_G_DAC_L2_OM_L_SFT 10 +#define RT5640_G_DAC_L1_OM_L_MASK (0x7 << 7) +#define RT5640_G_DAC_L1_OM_L_SFT 7 + +/* Output Left Mixer Control 3 (0x4f) */ +#define RT5640_M_SM_L_OM_L (0x1 << 8) +#define RT5640_M_SM_L_OM_L_SFT 8 +#define RT5640_M_BST3_OM_L (0x1 << 7) +#define RT5640_M_BST3_OM_L_SFT 7 +#define RT5640_M_BST2_OM_L (0x1 << 6) +#define RT5640_M_BST2_OM_L_SFT 6 +#define RT5640_M_BST1_OM_L (0x1 << 5) +#define RT5640_M_BST1_OM_L_SFT 5 +#define RT5640_M_IN_L_OM_L (0x1 << 4) +#define RT5640_M_IN_L_OM_L_SFT 4 +#define RT5640_M_RM_L_OM_L (0x1 << 3) +#define RT5640_M_RM_L_OM_L_SFT 3 +#define RT5640_M_DAC_R2_OM_L (0x1 << 2) +#define RT5640_M_DAC_R2_OM_L_SFT 2 +#define RT5640_M_DAC_L2_OM_L (0x1 << 1) +#define RT5640_M_DAC_L2_OM_L_SFT 1 +#define RT5640_M_DAC_L1_OM_L (0x1) +#define RT5640_M_DAC_L1_OM_L_SFT 0 + +/* Output Right Mixer Control 1 (0x50) */ +#define RT5640_G_BST4_OM_R_MASK (0x7 << 13) +#define RT5640_G_BST4_OM_R_SFT 13 +#define RT5640_G_BST2_OM_R_MASK (0x7 << 10) +#define RT5640_G_BST2_OM_R_SFT 10 +#define RT5640_G_BST1_OM_R_MASK (0x7 << 7) +#define RT5640_G_BST1_OM_R_SFT 7 +#define RT5640_G_IN_R_OM_R_MASK (0x7 << 4) +#define RT5640_G_IN_R_OM_R_SFT 4 +#define RT5640_G_RM_R_OM_R_MASK (0x7 << 1) +#define RT5640_G_RM_R_OM_R_SFT 1 + +/* Output Right Mixer Control 2 (0x51) */ +#define RT5640_G_DAC_L2_OM_R_MASK (0x7 << 13) +#define RT5640_G_DAC_L2_OM_R_SFT 13 +#define RT5640_G_DAC_R2_OM_R_MASK (0x7 << 10) +#define RT5640_G_DAC_R2_OM_R_SFT 10 +#define RT5640_G_DAC_R1_OM_R_MASK (0x7 << 7) +#define RT5640_G_DAC_R1_OM_R_SFT 7 + +/* Output Right Mixer Control 3 (0x52) */ +#define RT5640_M_SM_L_OM_R (0x1 << 8) +#define RT5640_M_SM_L_OM_R_SFT 8 +#define RT5640_M_BST4_OM_R (0x1 << 7) +#define RT5640_M_BST4_OM_R_SFT 7 +#define RT5640_M_BST2_OM_R (0x1 << 6) +#define RT5640_M_BST2_OM_R_SFT 6 +#define RT5640_M_BST1_OM_R (0x1 << 5) +#define RT5640_M_BST1_OM_R_SFT 5 +#define RT5640_M_IN_R_OM_R (0x1 << 4) +#define RT5640_M_IN_R_OM_R_SFT 4 +#define RT5640_M_RM_R_OM_R (0x1 << 3) +#define RT5640_M_RM_R_OM_R_SFT 3 +#define RT5640_M_DAC_L2_OM_R (0x1 << 2) +#define RT5640_M_DAC_L2_OM_R_SFT 2 +#define RT5640_M_DAC_R2_OM_R (0x1 << 1) +#define RT5640_M_DAC_R2_OM_R_SFT 1 +#define RT5640_M_DAC_R1_OM_R (0x1) +#define RT5640_M_DAC_R1_OM_R_SFT 0 + +/* LOUT Mixer Control (0x53) */ +#define RT5640_M_DAC_L1_LM (0x1 << 15) +#define RT5640_M_DAC_L1_LM_SFT 15 +#define RT5640_M_DAC_R1_LM (0x1 << 14) +#define RT5640_M_DAC_R1_LM_SFT 14 +#define RT5640_M_OV_L_LM (0x1 << 13) +#define RT5640_M_OV_L_LM_SFT 13 +#define RT5640_M_OV_R_LM (0x1 << 12) +#define RT5640_M_OV_R_LM_SFT 12 +#define RT5640_G_LOUTMIX_MASK (0x1 << 11) +#define RT5640_G_LOUTMIX_SFT 11 + +/* Power Management for Digital 1 (0x61) */ +#define RT5640_PWR_I2S1 (0x1 << 15) +#define RT5640_PWR_I2S1_BIT 15 +#define RT5640_PWR_I2S2 (0x1 << 14) +#define RT5640_PWR_I2S2_BIT 14 +#define RT5640_PWR_DAC_L1 (0x1 << 12) +#define RT5640_PWR_DAC_L1_BIT 12 +#define RT5640_PWR_DAC_R1 (0x1 << 11) +#define RT5640_PWR_DAC_R1_BIT 11 +#define RT5640_PWR_DAC_L2 (0x1 << 7) +#define RT5640_PWR_DAC_L2_BIT 7 +#define RT5640_PWR_DAC_R2 (0x1 << 6) +#define RT5640_PWR_DAC_R2_BIT 6 +#define RT5640_PWR_ADC_L (0x1 << 2) +#define RT5640_PWR_ADC_L_BIT 2 +#define RT5640_PWR_ADC_R (0x1 << 1) +#define RT5640_PWR_ADC_R_BIT 1 +#define RT5640_PWR_CLS_D (0x1) +#define RT5640_PWR_CLS_D_BIT 0 + +/* Power Management for Digital 2 (0x62) */ +#define RT5640_PWR_ADC_SF (0x1 << 15) +#define RT5640_PWR_ADC_SF_BIT 15 +#define RT5640_PWR_ADC_MF_L (0x1 << 14) +#define RT5640_PWR_ADC_MF_L_BIT 14 +#define RT5640_PWR_ADC_MF_R (0x1 << 13) +#define RT5640_PWR_ADC_MF_R_BIT 13 +#define RT5640_PWR_I2S_DSP (0x1 << 12) +#define RT5640_PWR_I2S_DSP_BIT 12 + +/* Power Management for Analog 1 (0x63) */ +#define RT5640_PWR_VREF1 (0x1 << 15) +#define RT5640_PWR_VREF1_BIT 15 +#define RT5640_PWR_FV1 (0x1 << 14) +#define RT5640_PWR_FV1_BIT 14 +#define RT5640_PWR_MB (0x1 << 13) +#define RT5640_PWR_MB_BIT 13 +#define RT5640_PWR_LM (0x1 << 12) +#define RT5640_PWR_LM_BIT 12 +#define RT5640_PWR_BG (0x1 << 11) +#define RT5640_PWR_BG_BIT 11 +#define RT5640_PWR_MM (0x1 << 10) +#define RT5640_PWR_MM_BIT 10 +#define RT5640_PWR_MA (0x1 << 8) +#define RT5640_PWR_MA_BIT 8 +#define RT5640_PWR_HP_L (0x1 << 7) +#define RT5640_PWR_HP_L_BIT 7 +#define RT5640_PWR_HP_R (0x1 << 6) +#define RT5640_PWR_HP_R_BIT 6 +#define RT5640_PWR_HA (0x1 << 5) +#define RT5640_PWR_HA_BIT 5 +#define RT5640_PWR_VREF2 (0x1 << 4) +#define RT5640_PWR_VREF2_BIT 4 +#define RT5640_PWR_FV2 (0x1 << 3) +#define RT5640_PWR_FV2_BIT 3 +#define RT5640_PWR_LDO2 (0x1 << 2) +#define RT5640_PWR_LDO2_BIT 2 + +/* Power Management for Analog 2 (0x64) */ +#define RT5640_PWR_BST1 (0x1 << 15) +#define RT5640_PWR_BST1_BIT 15 +#define RT5640_PWR_BST2 (0x1 << 14) +#define RT5640_PWR_BST2_BIT 14 +#define RT5640_PWR_BST3 (0x1 << 13) +#define RT5640_PWR_BST3_BIT 13 +#define RT5640_PWR_BST4 (0x1 << 12) +#define RT5640_PWR_BST4_BIT 12 +#define RT5640_PWR_MB1 (0x1 << 11) +#define RT5640_PWR_MB1_BIT 11 +#define RT5640_PWR_PLL (0x1 << 9) +#define RT5640_PWR_PLL_BIT 9 + +/* Power Management for Mixer (0x65) */ +#define RT5640_PWR_OM_L (0x1 << 15) +#define RT5640_PWR_OM_L_BIT 15 +#define RT5640_PWR_OM_R (0x1 << 14) +#define RT5640_PWR_OM_R_BIT 14 +#define RT5640_PWR_SM_L (0x1 << 13) +#define RT5640_PWR_SM_L_BIT 13 +#define RT5640_PWR_SM_R (0x1 << 12) +#define RT5640_PWR_SM_R_BIT 12 +#define RT5640_PWR_RM_L (0x1 << 11) +#define RT5640_PWR_RM_L_BIT 11 +#define RT5640_PWR_RM_R (0x1 << 10) +#define RT5640_PWR_RM_R_BIT 10 + +/* Power Management for Volume (0x66) */ +#define RT5640_PWR_SV_L (0x1 << 15) +#define RT5640_PWR_SV_L_BIT 15 +#define RT5640_PWR_SV_R (0x1 << 14) +#define RT5640_PWR_SV_R_BIT 14 +#define RT5640_PWR_OV_L (0x1 << 13) +#define RT5640_PWR_OV_L_BIT 13 +#define RT5640_PWR_OV_R (0x1 << 12) +#define RT5640_PWR_OV_R_BIT 12 +#define RT5640_PWR_HV_L (0x1 << 11) +#define RT5640_PWR_HV_L_BIT 11 +#define RT5640_PWR_HV_R (0x1 << 10) +#define RT5640_PWR_HV_R_BIT 10 +#define RT5640_PWR_IN_L (0x1 << 9) +#define RT5640_PWR_IN_L_BIT 9 +#define RT5640_PWR_IN_R (0x1 << 8) +#define RT5640_PWR_IN_R_BIT 8 + +/* I2S1/2/3 Audio Serial Data Port Control (0x70 0x71 0x72) */ +#define RT5640_I2S_MS_MASK (0x1 << 15) +#define RT5640_I2S_MS_SFT 15 +#define RT5640_I2S_MS_M (0x0 << 15) +#define RT5640_I2S_MS_S (0x1 << 15) +#define RT5640_I2S_IF_MASK (0x7 << 12) +#define RT5640_I2S_IF_SFT 12 +#define RT5640_I2S_O_CP_MASK (0x3 << 10) +#define RT5640_I2S_O_CP_SFT 10 +#define RT5640_I2S_O_CP_OFF (0x0 << 10) +#define RT5640_I2S_O_CP_U_LAW (0x1 << 10) +#define RT5640_I2S_O_CP_A_LAW (0x2 << 10) +#define RT5640_I2S_I_CP_MASK (0x3 << 8) +#define RT5640_I2S_I_CP_SFT 8 +#define RT5640_I2S_I_CP_OFF (0x0 << 8) +#define RT5640_I2S_I_CP_U_LAW (0x1 << 8) +#define RT5640_I2S_I_CP_A_LAW (0x2 << 8) +#define RT5640_I2S_BP_MASK (0x1 << 7) +#define RT5640_I2S_BP_SFT 7 +#define RT5640_I2S_BP_NOR (0x0 << 7) +#define RT5640_I2S_BP_INV (0x1 << 7) +#define RT5640_I2S_DL_MASK (0x3 << 2) +#define RT5640_I2S_DL_SFT 2 +#define RT5640_I2S_DL_16 (0x0 << 2) +#define RT5640_I2S_DL_20 (0x1 << 2) +#define RT5640_I2S_DL_24 (0x2 << 2) +#define RT5640_I2S_DL_8 (0x3 << 2) +#define RT5640_I2S_DF_MASK (0x3) +#define RT5640_I2S_DF_SFT 0 +#define RT5640_I2S_DF_I2S (0x0) +#define RT5640_I2S_DF_LEFT (0x1) +#define RT5640_I2S_DF_PCM_A (0x2) +#define RT5640_I2S_DF_PCM_B (0x3) + +/* I2S2 Audio Serial Data Port Control (0x71) */ +#define RT5640_I2S2_SDI_MASK (0x1 << 6) +#define RT5640_I2S2_SDI_SFT 6 +#define RT5640_I2S2_SDI_I2S1 (0x0 << 6) +#define RT5640_I2S2_SDI_I2S2 (0x1 << 6) + +/* ADC/DAC Clock Control 1 (0x73) */ +#define RT5640_I2S_BCLK_MS1_MASK (0x1 << 15) +#define RT5640_I2S_BCLK_MS1_SFT 15 +#define RT5640_I2S_BCLK_MS1_32 (0x0 << 15) +#define RT5640_I2S_BCLK_MS1_64 (0x1 << 15) +#define RT5640_I2S_PD1_MASK (0x7 << 12) +#define RT5640_I2S_PD1_SFT 12 +#define RT5640_I2S_PD1_1 (0x0 << 12) +#define RT5640_I2S_PD1_2 (0x1 << 12) +#define RT5640_I2S_PD1_3 (0x2 << 12) +#define RT5640_I2S_PD1_4 (0x3 << 12) +#define RT5640_I2S_PD1_6 (0x4 << 12) +#define RT5640_I2S_PD1_8 (0x5 << 12) +#define RT5640_I2S_PD1_12 (0x6 << 12) +#define RT5640_I2S_PD1_16 (0x7 << 12) +#define RT5640_I2S_BCLK_MS2_MASK (0x1 << 11) +#define RT5640_I2S_BCLK_MS2_SFT 11 +#define RT5640_I2S_BCLK_MS2_32 (0x0 << 11) +#define RT5640_I2S_BCLK_MS2_64 (0x1 << 11) +#define RT5640_I2S_PD2_MASK (0x7 << 8) +#define RT5640_I2S_PD2_SFT 8 +#define RT5640_I2S_PD2_1 (0x0 << 8) +#define RT5640_I2S_PD2_2 (0x1 << 8) +#define RT5640_I2S_PD2_3 (0x2 << 8) +#define RT5640_I2S_PD2_4 (0x3 << 8) +#define RT5640_I2S_PD2_6 (0x4 << 8) +#define RT5640_I2S_PD2_8 (0x5 << 8) +#define RT5640_I2S_PD2_12 (0x6 << 8) +#define RT5640_I2S_PD2_16 (0x7 << 8) +#define RT5640_I2S_BCLK_MS3_MASK (0x1 << 7) +#define RT5640_I2S_BCLK_MS3_SFT 7 +#define RT5640_I2S_BCLK_MS3_32 (0x0 << 7) +#define RT5640_I2S_BCLK_MS3_64 (0x1 << 7) +#define RT5640_I2S_PD3_MASK (0x7 << 4) +#define RT5640_I2S_PD3_SFT 4 +#define RT5640_I2S_PD3_1 (0x0 << 4) +#define RT5640_I2S_PD3_2 (0x1 << 4) +#define RT5640_I2S_PD3_3 (0x2 << 4) +#define RT5640_I2S_PD3_4 (0x3 << 4) +#define RT5640_I2S_PD3_6 (0x4 << 4) +#define RT5640_I2S_PD3_8 (0x5 << 4) +#define RT5640_I2S_PD3_12 (0x6 << 4) +#define RT5640_I2S_PD3_16 (0x7 << 4) +#define RT5640_DAC_OSR_MASK (0x3 << 2) +#define RT5640_DAC_OSR_SFT 2 +#define RT5640_DAC_OSR_128 (0x0 << 2) +#define RT5640_DAC_OSR_64 (0x1 << 2) +#define RT5640_DAC_OSR_32 (0x2 << 2) +#define RT5640_DAC_OSR_16 (0x3 << 2) +#define RT5640_ADC_OSR_MASK (0x3) +#define RT5640_ADC_OSR_SFT 0 +#define RT5640_ADC_OSR_128 (0x0) +#define RT5640_ADC_OSR_64 (0x1) +#define RT5640_ADC_OSR_32 (0x2) +#define RT5640_ADC_OSR_16 (0x3) + +/* ADC/DAC Clock Control 2 (0x74) */ +#define RT5640_DAC_L_OSR_MASK (0x3 << 14) +#define RT5640_DAC_L_OSR_SFT 14 +#define RT5640_DAC_L_OSR_128 (0x0 << 14) +#define RT5640_DAC_L_OSR_64 (0x1 << 14) +#define RT5640_DAC_L_OSR_32 (0x2 << 14) +#define RT5640_DAC_L_OSR_16 (0x3 << 14) +#define RT5640_ADC_R_OSR_MASK (0x3 << 12) +#define RT5640_ADC_R_OSR_SFT 12 +#define RT5640_ADC_R_OSR_128 (0x0 << 12) +#define RT5640_ADC_R_OSR_64 (0x1 << 12) +#define RT5640_ADC_R_OSR_32 (0x2 << 12) +#define RT5640_ADC_R_OSR_16 (0x3 << 12) +#define RT5640_DAHPF_EN (0x1 << 11) +#define RT5640_DAHPF_EN_SFT 11 +#define RT5640_ADHPF_EN (0x1 << 10) +#define RT5640_ADHPF_EN_SFT 10 + +/* Digital Microphone Control (0x75) */ +#define RT5640_DMIC_1_EN_MASK (0x1 << 15) +#define RT5640_DMIC_1_EN_SFT 15 +#define RT5640_DMIC_1_DIS (0x0 << 15) +#define RT5640_DMIC_1_EN (0x1 << 15) +#define RT5640_DMIC_2_EN_MASK (0x1 << 14) +#define RT5640_DMIC_2_EN_SFT 14 +#define RT5640_DMIC_2_DIS (0x0 << 14) +#define RT5640_DMIC_2_EN (0x1 << 14) +#define RT5640_DMIC_1L_LH_MASK (0x1 << 13) +#define RT5640_DMIC_1L_LH_SFT 13 +#define RT5640_DMIC_1L_LH_FALLING (0x0 << 13) +#define RT5640_DMIC_1L_LH_RISING (0x1 << 13) +#define RT5640_DMIC_1R_LH_MASK (0x1 << 12) +#define RT5640_DMIC_1R_LH_SFT 12 +#define RT5640_DMIC_1R_LH_FALLING (0x0 << 12) +#define RT5640_DMIC_1R_LH_RISING (0x1 << 12) +#define RT5640_DMIC_1_DP_MASK (0x1 << 11) +#define RT5640_DMIC_1_DP_SFT 11 +#define RT5640_DMIC_1_DP_GPIO3 (0x0 << 11) +#define RT5640_DMIC_1_DP_IN1P (0x1 << 11) +#define RT5640_DMIC_2_DP_MASK (0x1 << 10) +#define RT5640_DMIC_2_DP_SFT 10 +#define RT5640_DMIC_2_DP_GPIO4 (0x0 << 10) +#define RT5640_DMIC_2_DP_IN1N (0x1 << 10) +#define RT5640_DMIC_2L_LH_MASK (0x1 << 9) +#define RT5640_DMIC_2L_LH_SFT 9 +#define RT5640_DMIC_2L_LH_FALLING (0x0 << 9) +#define RT5640_DMIC_2L_LH_RISING (0x1 << 9) +#define RT5640_DMIC_2R_LH_MASK (0x1 << 8) +#define RT5640_DMIC_2R_LH_SFT 8 +#define RT5640_DMIC_2R_LH_FALLING (0x0 << 8) +#define RT5640_DMIC_2R_LH_RISING (0x1 << 8) +#define RT5640_DMIC_CLK_MASK (0x7 << 5) +#define RT5640_DMIC_CLK_SFT 5 + +/* Global Clock Control (0x80) */ +#define RT5640_SCLK_SRC_MASK (0x3 << 14) +#define RT5640_SCLK_SRC_SFT 14 +#define RT5640_SCLK_SRC_MCLK (0x0 << 14) +#define RT5640_SCLK_SRC_PLL1 (0x1 << 14) +#define RT5640_SCLK_SRC_PLL1T (0x2 << 14) +#define RT5640_SCLK_SRC_RCCLK (0x3 << 14) /* 15MHz */ +#define RT5640_PLL1_SRC_MASK (0x3 << 12) +#define RT5640_PLL1_SRC_SFT 12 +#define RT5640_PLL1_SRC_MCLK (0x0 << 12) +#define RT5640_PLL1_SRC_BCLK1 (0x1 << 12) +#define RT5640_PLL1_SRC_BCLK2 (0x2 << 12) +#define RT5640_PLL1_SRC_BCLK3 (0x3 << 12) +#define RT5640_PLL1_PD_MASK (0x1 << 3) +#define RT5640_PLL1_PD_SFT 3 +#define RT5640_PLL1_PD_1 (0x0 << 3) +#define RT5640_PLL1_PD_2 (0x1 << 3) + +#define RT5640_PLL_INP_MAX 40000000 +#define RT5640_PLL_INP_MIN 256000 +/* PLL M/N/K Code Control 1 (0x81) */ +#define RT5640_PLL_N_MAX 0x1ff +#define RT5640_PLL_N_MASK (RT5640_PLL_N_MAX << 7) +#define RT5640_PLL_N_SFT 7 +#define RT5640_PLL_K_MAX 0x1f +#define RT5640_PLL_K_MASK (RT5640_PLL_K_MAX) +#define RT5640_PLL_K_SFT 0 + +/* PLL M/N/K Code Control 2 (0x82) */ +#define RT5640_PLL_M_MAX 0xf +#define RT5640_PLL_M_MASK (RT5640_PLL_M_MAX << 12) +#define RT5640_PLL_M_SFT 12 +#define RT5640_PLL_M_BP (0x1 << 11) +#define RT5640_PLL_M_BP_SFT 11 + +/* ASRC Control 1 (0x83) */ +#define RT5640_STO_T_MASK (0x1 << 15) +#define RT5640_STO_T_SFT 15 +#define RT5640_STO_T_SCLK (0x0 << 15) +#define RT5640_STO_T_LRCK1 (0x1 << 15) +#define RT5640_M1_T_MASK (0x1 << 14) +#define RT5640_M1_T_SFT 14 +#define RT5640_M1_T_I2S2 (0x0 << 14) +#define RT5640_M1_T_I2S2_D3 (0x1 << 14) +#define RT5640_I2S2_F_MASK (0x1 << 12) +#define RT5640_I2S2_F_SFT 12 +#define RT5640_I2S2_F_I2S2_D2 (0x0 << 12) +#define RT5640_I2S2_F_I2S1_TCLK (0x1 << 12) +#define RT5640_DMIC_1_M_MASK (0x1 << 9) +#define RT5640_DMIC_1_M_SFT 9 +#define RT5640_DMIC_1_M_NOR (0x0 << 9) +#define RT5640_DMIC_1_M_ASYN (0x1 << 9) +#define RT5640_DMIC_2_M_MASK (0x1 << 8) +#define RT5640_DMIC_2_M_SFT 8 +#define RT5640_DMIC_2_M_NOR (0x0 << 8) +#define RT5640_DMIC_2_M_ASYN (0x1 << 8) + +/* ASRC Control 2 (0x84) */ +#define RT5640_MDA_L_M_MASK (0x1 << 15) +#define RT5640_MDA_L_M_SFT 15 +#define RT5640_MDA_L_M_NOR (0x0 << 15) +#define RT5640_MDA_L_M_ASYN (0x1 << 15) +#define RT5640_MDA_R_M_MASK (0x1 << 14) +#define RT5640_MDA_R_M_SFT 14 +#define RT5640_MDA_R_M_NOR (0x0 << 14) +#define RT5640_MDA_R_M_ASYN (0x1 << 14) +#define RT5640_MAD_L_M_MASK (0x1 << 13) +#define RT5640_MAD_L_M_SFT 13 +#define RT5640_MAD_L_M_NOR (0x0 << 13) +#define RT5640_MAD_L_M_ASYN (0x1 << 13) +#define RT5640_MAD_R_M_MASK (0x1 << 12) +#define RT5640_MAD_R_M_SFT 12 +#define RT5640_MAD_R_M_NOR (0x0 << 12) +#define RT5640_MAD_R_M_ASYN (0x1 << 12) +#define RT5640_ADC_M_MASK (0x1 << 11) +#define RT5640_ADC_M_SFT 11 +#define RT5640_ADC_M_NOR (0x0 << 11) +#define RT5640_ADC_M_ASYN (0x1 << 11) +#define RT5640_STO_DAC_M_MASK (0x1 << 5) +#define RT5640_STO_DAC_M_SFT 5 +#define RT5640_STO_DAC_M_NOR (0x0 << 5) +#define RT5640_STO_DAC_M_ASYN (0x1 << 5) +#define RT5640_I2S1_R_D_MASK (0x1 << 4) +#define RT5640_I2S1_R_D_SFT 4 +#define RT5640_I2S1_R_D_DIS (0x0 << 4) +#define RT5640_I2S1_R_D_EN (0x1 << 4) +#define RT5640_I2S2_R_D_MASK (0x1 << 3) +#define RT5640_I2S2_R_D_SFT 3 +#define RT5640_I2S2_R_D_DIS (0x0 << 3) +#define RT5640_I2S2_R_D_EN (0x1 << 3) +#define RT5640_PRE_SCLK_MASK (0x3) +#define RT5640_PRE_SCLK_SFT 0 +#define RT5640_PRE_SCLK_512 (0x0) +#define RT5640_PRE_SCLK_1024 (0x1) +#define RT5640_PRE_SCLK_2048 (0x2) + +/* ASRC Control 3 (0x85) */ +#define RT5640_I2S1_RATE_MASK (0xf << 12) +#define RT5640_I2S1_RATE_SFT 12 +#define RT5640_I2S2_RATE_MASK (0xf << 8) +#define RT5640_I2S2_RATE_SFT 8 + +/* ASRC Control 4 (0x89) */ +#define RT5640_I2S1_PD_MASK (0x7 << 12) +#define RT5640_I2S1_PD_SFT 12 +#define RT5640_I2S2_PD_MASK (0x7 << 8) +#define RT5640_I2S2_PD_SFT 8 + +/* HPOUT Over Current Detection (0x8b) */ +#define RT5640_HP_OVCD_MASK (0x1 << 10) +#define RT5640_HP_OVCD_SFT 10 +#define RT5640_HP_OVCD_DIS (0x0 << 10) +#define RT5640_HP_OVCD_EN (0x1 << 10) +#define RT5640_HP_OC_TH_MASK (0x3 << 8) +#define RT5640_HP_OC_TH_SFT 8 +#define RT5640_HP_OC_TH_90 (0x0 << 8) +#define RT5640_HP_OC_TH_105 (0x1 << 8) +#define RT5640_HP_OC_TH_120 (0x2 << 8) +#define RT5640_HP_OC_TH_135 (0x3 << 8) + +/* Class D Over Current Control (0x8c) */ +#define RT5640_CLSD_OC_MASK (0x1 << 9) +#define RT5640_CLSD_OC_SFT 9 +#define RT5640_CLSD_OC_PU (0x0 << 9) +#define RT5640_CLSD_OC_PD (0x1 << 9) +#define RT5640_AUTO_PD_MASK (0x1 << 8) +#define RT5640_AUTO_PD_SFT 8 +#define RT5640_AUTO_PD_DIS (0x0 << 8) +#define RT5640_AUTO_PD_EN (0x1 << 8) +#define RT5640_CLSD_OC_TH_MASK (0x3f) +#define RT5640_CLSD_OC_TH_SFT 0 + +/* Class D Output Control (0x8d) */ +#define RT5640_CLSD_RATIO_MASK (0xf << 12) +#define RT5640_CLSD_RATIO_SFT 12 +#define RT5640_CLSD_OM_MASK (0x1 << 11) +#define RT5640_CLSD_OM_SFT 11 +#define RT5640_CLSD_OM_MONO (0x0 << 11) +#define RT5640_CLSD_OM_STO (0x1 << 11) +#define RT5640_CLSD_SCH_MASK (0x1 << 10) +#define RT5640_CLSD_SCH_SFT 10 +#define RT5640_CLSD_SCH_L (0x0 << 10) +#define RT5640_CLSD_SCH_S (0x1 << 10) + +/* Depop Mode Control 1 (0x8e) */ +#define RT5640_SMT_TRIG_MASK (0x1 << 15) +#define RT5640_SMT_TRIG_SFT 15 +#define RT5640_SMT_TRIG_DIS (0x0 << 15) +#define RT5640_SMT_TRIG_EN (0x1 << 15) +#define RT5640_HP_L_SMT_MASK (0x1 << 9) +#define RT5640_HP_L_SMT_SFT 9 +#define RT5640_HP_L_SMT_DIS (0x0 << 9) +#define RT5640_HP_L_SMT_EN (0x1 << 9) +#define RT5640_HP_R_SMT_MASK (0x1 << 8) +#define RT5640_HP_R_SMT_SFT 8 +#define RT5640_HP_R_SMT_DIS (0x0 << 8) +#define RT5640_HP_R_SMT_EN (0x1 << 8) +#define RT5640_HP_CD_PD_MASK (0x1 << 7) +#define RT5640_HP_CD_PD_SFT 7 +#define RT5640_HP_CD_PD_DIS (0x0 << 7) +#define RT5640_HP_CD_PD_EN (0x1 << 7) +#define RT5640_RSTN_MASK (0x1 << 6) +#define RT5640_RSTN_SFT 6 +#define RT5640_RSTN_DIS (0x0 << 6) +#define RT5640_RSTN_EN (0x1 << 6) +#define RT5640_RSTP_MASK (0x1 << 5) +#define RT5640_RSTP_SFT 5 +#define RT5640_RSTP_DIS (0x0 << 5) +#define RT5640_RSTP_EN (0x1 << 5) +#define RT5640_HP_CO_MASK (0x1 << 4) +#define RT5640_HP_CO_SFT 4 +#define RT5640_HP_CO_DIS (0x0 << 4) +#define RT5640_HP_CO_EN (0x1 << 4) +#define RT5640_HP_CP_MASK (0x1 << 3) +#define RT5640_HP_CP_SFT 3 +#define RT5640_HP_CP_PD (0x0 << 3) +#define RT5640_HP_CP_PU (0x1 << 3) +#define RT5640_HP_SG_MASK (0x1 << 2) +#define RT5640_HP_SG_SFT 2 +#define RT5640_HP_SG_DIS (0x0 << 2) +#define RT5640_HP_SG_EN (0x1 << 2) +#define RT5640_HP_DP_MASK (0x1 << 1) +#define RT5640_HP_DP_SFT 1 +#define RT5640_HP_DP_PD (0x0 << 1) +#define RT5640_HP_DP_PU (0x1 << 1) +#define RT5640_HP_CB_MASK (0x1) +#define RT5640_HP_CB_SFT 0 +#define RT5640_HP_CB_PD (0x0) +#define RT5640_HP_CB_PU (0x1) + +/* Depop Mode Control 2 (0x8f) */ +#define RT5640_DEPOP_MASK (0x1 << 13) +#define RT5640_DEPOP_SFT 13 +#define RT5640_DEPOP_AUTO (0x0 << 13) +#define RT5640_DEPOP_MAN (0x1 << 13) +#define RT5640_RAMP_MASK (0x1 << 12) +#define RT5640_RAMP_SFT 12 +#define RT5640_RAMP_DIS (0x0 << 12) +#define RT5640_RAMP_EN (0x1 << 12) +#define RT5640_BPS_MASK (0x1 << 11) +#define RT5640_BPS_SFT 11 +#define RT5640_BPS_DIS (0x0 << 11) +#define RT5640_BPS_EN (0x1 << 11) +#define RT5640_FAST_UPDN_MASK (0x1 << 10) +#define RT5640_FAST_UPDN_SFT 10 +#define RT5640_FAST_UPDN_DIS (0x0 << 10) +#define RT5640_FAST_UPDN_EN (0x1 << 10) +#define RT5640_MRES_MASK (0x3 << 8) +#define RT5640_MRES_SFT 8 +#define RT5640_MRES_15MO (0x0 << 8) +#define RT5640_MRES_25MO (0x1 << 8) +#define RT5640_MRES_35MO (0x2 << 8) +#define RT5640_MRES_45MO (0x3 << 8) +#define RT5640_VLO_MASK (0x1 << 7) +#define RT5640_VLO_SFT 7 +#define RT5640_VLO_3V (0x0 << 7) +#define RT5640_VLO_32V (0x1 << 7) +#define RT5640_DIG_DP_MASK (0x1 << 6) +#define RT5640_DIG_DP_SFT 6 +#define RT5640_DIG_DP_DIS (0x0 << 6) +#define RT5640_DIG_DP_EN (0x1 << 6) +#define RT5640_DP_TH_MASK (0x3 << 4) +#define RT5640_DP_TH_SFT 4 + +/* Depop Mode Control 3 (0x90) */ +#define RT5640_CP_SYS_MASK (0x7 << 12) +#define RT5640_CP_SYS_SFT 12 +#define RT5640_CP_FQ1_MASK (0x7 << 8) +#define RT5640_CP_FQ1_SFT 8 +#define RT5640_CP_FQ2_MASK (0x7 << 4) +#define RT5640_CP_FQ2_SFT 4 +#define RT5640_CP_FQ3_MASK (0x7) +#define RT5640_CP_FQ3_SFT 0 + +/* HPOUT charge pump (0x91) */ +#define RT5640_OSW_L_MASK (0x1 << 11) +#define RT5640_OSW_L_SFT 11 +#define RT5640_OSW_L_DIS (0x0 << 11) +#define RT5640_OSW_L_EN (0x1 << 11) +#define RT5640_OSW_R_MASK (0x1 << 10) +#define RT5640_OSW_R_SFT 10 +#define RT5640_OSW_R_DIS (0x0 << 10) +#define RT5640_OSW_R_EN (0x1 << 10) +#define RT5640_PM_HP_MASK (0x3 << 8) +#define RT5640_PM_HP_SFT 8 +#define RT5640_PM_HP_LV (0x0 << 8) +#define RT5640_PM_HP_MV (0x1 << 8) +#define RT5640_PM_HP_HV (0x2 << 8) +#define RT5640_IB_HP_MASK (0x3 << 6) +#define RT5640_IB_HP_SFT 6 +#define RT5640_IB_HP_125IL (0x0 << 6) +#define RT5640_IB_HP_25IL (0x1 << 6) +#define RT5640_IB_HP_5IL (0x2 << 6) +#define RT5640_IB_HP_1IL (0x3 << 6) + +/* PV detection and SPK gain control (0x92) */ +#define RT5640_PVDD_DET_MASK (0x1 << 15) +#define RT5640_PVDD_DET_SFT 15 +#define RT5640_PVDD_DET_DIS (0x0 << 15) +#define RT5640_PVDD_DET_EN (0x1 << 15) +#define RT5640_SPK_AG_MASK (0x1 << 14) +#define RT5640_SPK_AG_SFT 14 +#define RT5640_SPK_AG_DIS (0x0 << 14) +#define RT5640_SPK_AG_EN (0x1 << 14) + +/* Micbias Control (0x93) */ +#define RT5640_MIC1_BS_MASK (0x1 << 15) +#define RT5640_MIC1_BS_SFT 15 +#define RT5640_MIC1_BS_9AV (0x0 << 15) +#define RT5640_MIC1_BS_75AV (0x1 << 15) +#define RT5640_MIC2_BS_MASK (0x1 << 14) +#define RT5640_MIC2_BS_SFT 14 +#define RT5640_MIC2_BS_9AV (0x0 << 14) +#define RT5640_MIC2_BS_75AV (0x1 << 14) +#define RT5640_MIC1_CLK_MASK (0x1 << 13) +#define RT5640_MIC1_CLK_SFT 13 +#define RT5640_MIC1_CLK_DIS (0x0 << 13) +#define RT5640_MIC1_CLK_EN (0x1 << 13) +#define RT5640_MIC2_CLK_MASK (0x1 << 12) +#define RT5640_MIC2_CLK_SFT 12 +#define RT5640_MIC2_CLK_DIS (0x0 << 12) +#define RT5640_MIC2_CLK_EN (0x1 << 12) +#define RT5640_MIC1_OVCD_MASK (0x1 << 11) +#define RT5640_MIC1_OVCD_SFT 11 +#define RT5640_MIC1_OVCD_DIS (0x0 << 11) +#define RT5640_MIC1_OVCD_EN (0x1 << 11) +#define RT5640_MIC1_OVTH_MASK (0x3 << 9) +#define RT5640_MIC1_OVTH_SFT 9 +#define RT5640_MIC1_OVTH_600UA (0x0 << 9) +#define RT5640_MIC1_OVTH_1500UA (0x1 << 9) +#define RT5640_MIC1_OVTH_2000UA (0x2 << 9) +#define RT5640_MIC2_OVCD_MASK (0x1 << 8) +#define RT5640_MIC2_OVCD_SFT 8 +#define RT5640_MIC2_OVCD_DIS (0x0 << 8) +#define RT5640_MIC2_OVCD_EN (0x1 << 8) +#define RT5640_MIC2_OVTH_MASK (0x3 << 6) +#define RT5640_MIC2_OVTH_SFT 6 +#define RT5640_MIC2_OVTH_600UA (0x0 << 6) +#define RT5640_MIC2_OVTH_1500UA (0x1 << 6) +#define RT5640_MIC2_OVTH_2000UA (0x2 << 6) +#define RT5640_PWR_MB_MASK (0x1 << 5) +#define RT5640_PWR_MB_SFT 5 +#define RT5640_PWR_MB_PD (0x0 << 5) +#define RT5640_PWR_MB_PU (0x1 << 5) +#define RT5640_PWR_CLK25M_MASK (0x1 << 4) +#define RT5640_PWR_CLK25M_SFT 4 +#define RT5640_PWR_CLK25M_PD (0x0 << 4) +#define RT5640_PWR_CLK25M_PU (0x1 << 4) + +/* EQ Control 1 (0xb0) */ +#define RT5640_EQ_SRC_MASK (0x1 << 15) +#define RT5640_EQ_SRC_SFT 15 +#define RT5640_EQ_SRC_DAC (0x0 << 15) +#define RT5640_EQ_SRC_ADC (0x1 << 15) +#define RT5640_EQ_UPD (0x1 << 14) +#define RT5640_EQ_UPD_BIT 14 +#define RT5640_EQ_CD_MASK (0x1 << 13) +#define RT5640_EQ_CD_SFT 13 +#define RT5640_EQ_CD_DIS (0x0 << 13) +#define RT5640_EQ_CD_EN (0x1 << 13) +#define RT5640_EQ_DITH_MASK (0x3 << 8) +#define RT5640_EQ_DITH_SFT 8 +#define RT5640_EQ_DITH_NOR (0x0 << 8) +#define RT5640_EQ_DITH_LSB (0x1 << 8) +#define RT5640_EQ_DITH_LSB_1 (0x2 << 8) +#define RT5640_EQ_DITH_LSB_2 (0x3 << 8) + +/* EQ Control 2 (0xb1) */ +#define RT5640_EQ_HPF1_M_MASK (0x1 << 8) +#define RT5640_EQ_HPF1_M_SFT 8 +#define RT5640_EQ_HPF1_M_HI (0x0 << 8) +#define RT5640_EQ_HPF1_M_1ST (0x1 << 8) +#define RT5640_EQ_LPF1_M_MASK (0x1 << 7) +#define RT5640_EQ_LPF1_M_SFT 7 +#define RT5640_EQ_LPF1_M_LO (0x0 << 7) +#define RT5640_EQ_LPF1_M_1ST (0x1 << 7) +#define RT5640_EQ_HPF2_MASK (0x1 << 6) +#define RT5640_EQ_HPF2_SFT 6 +#define RT5640_EQ_HPF2_DIS (0x0 << 6) +#define RT5640_EQ_HPF2_EN (0x1 << 6) +#define RT5640_EQ_HPF1_MASK (0x1 << 5) +#define RT5640_EQ_HPF1_SFT 5 +#define RT5640_EQ_HPF1_DIS (0x0 << 5) +#define RT5640_EQ_HPF1_EN (0x1 << 5) +#define RT5640_EQ_BPF4_MASK (0x1 << 4) +#define RT5640_EQ_BPF4_SFT 4 +#define RT5640_EQ_BPF4_DIS (0x0 << 4) +#define RT5640_EQ_BPF4_EN (0x1 << 4) +#define RT5640_EQ_BPF3_MASK (0x1 << 3) +#define RT5640_EQ_BPF3_SFT 3 +#define RT5640_EQ_BPF3_DIS (0x0 << 3) +#define RT5640_EQ_BPF3_EN (0x1 << 3) +#define RT5640_EQ_BPF2_MASK (0x1 << 2) +#define RT5640_EQ_BPF2_SFT 2 +#define RT5640_EQ_BPF2_DIS (0x0 << 2) +#define RT5640_EQ_BPF2_EN (0x1 << 2) +#define RT5640_EQ_BPF1_MASK (0x1 << 1) +#define RT5640_EQ_BPF1_SFT 1 +#define RT5640_EQ_BPF1_DIS (0x0 << 1) +#define RT5640_EQ_BPF1_EN (0x1 << 1) +#define RT5640_EQ_LPF_MASK (0x1) +#define RT5640_EQ_LPF_SFT 0 +#define RT5640_EQ_LPF_DIS (0x0) +#define RT5640_EQ_LPF_EN (0x1) + +/* Memory Test (0xb2) */ +#define RT5640_MT_MASK (0x1 << 15) +#define RT5640_MT_SFT 15 +#define RT5640_MT_DIS (0x0 << 15) +#define RT5640_MT_EN (0x1 << 15) + +/* DRC/AGC Control 1 (0xb4) */ +#define RT5640_DRC_AGC_P_MASK (0x1 << 15) +#define RT5640_DRC_AGC_P_SFT 15 +#define RT5640_DRC_AGC_P_DAC (0x0 << 15) +#define RT5640_DRC_AGC_P_ADC (0x1 << 15) +#define RT5640_DRC_AGC_MASK (0x1 << 14) +#define RT5640_DRC_AGC_SFT 14 +#define RT5640_DRC_AGC_DIS (0x0 << 14) +#define RT5640_DRC_AGC_EN (0x1 << 14) +#define RT5640_DRC_AGC_UPD (0x1 << 13) +#define RT5640_DRC_AGC_UPD_BIT 13 +#define RT5640_DRC_AGC_AR_MASK (0x1f << 8) +#define RT5640_DRC_AGC_AR_SFT 8 +#define RT5640_DRC_AGC_R_MASK (0x7 << 5) +#define RT5640_DRC_AGC_R_SFT 5 +#define RT5640_DRC_AGC_R_48K (0x1 << 5) +#define RT5640_DRC_AGC_R_96K (0x2 << 5) +#define RT5640_DRC_AGC_R_192K (0x3 << 5) +#define RT5640_DRC_AGC_R_441K (0x5 << 5) +#define RT5640_DRC_AGC_R_882K (0x6 << 5) +#define RT5640_DRC_AGC_R_1764K (0x7 << 5) +#define RT5640_DRC_AGC_RC_MASK (0x1f) +#define RT5640_DRC_AGC_RC_SFT 0 + +/* DRC/AGC Control 2 (0xb5) */ +#define RT5640_DRC_AGC_POB_MASK (0x3f << 8) +#define RT5640_DRC_AGC_POB_SFT 8 +#define RT5640_DRC_AGC_CP_MASK (0x1 << 7) +#define RT5640_DRC_AGC_CP_SFT 7 +#define RT5640_DRC_AGC_CP_DIS (0x0 << 7) +#define RT5640_DRC_AGC_CP_EN (0x1 << 7) +#define RT5640_DRC_AGC_CPR_MASK (0x3 << 5) +#define RT5640_DRC_AGC_CPR_SFT 5 +#define RT5640_DRC_AGC_CPR_1_1 (0x0 << 5) +#define RT5640_DRC_AGC_CPR_1_2 (0x1 << 5) +#define RT5640_DRC_AGC_CPR_1_3 (0x2 << 5) +#define RT5640_DRC_AGC_CPR_1_4 (0x3 << 5) +#define RT5640_DRC_AGC_PRB_MASK (0x1f) +#define RT5640_DRC_AGC_PRB_SFT 0 + +/* DRC/AGC Control 3 (0xb6) */ +#define RT5640_DRC_AGC_NGB_MASK (0xf << 12) +#define RT5640_DRC_AGC_NGB_SFT 12 +#define RT5640_DRC_AGC_TAR_MASK (0x1f << 7) +#define RT5640_DRC_AGC_TAR_SFT 7 +#define RT5640_DRC_AGC_NG_MASK (0x1 << 6) +#define RT5640_DRC_AGC_NG_SFT 6 +#define RT5640_DRC_AGC_NG_DIS (0x0 << 6) +#define RT5640_DRC_AGC_NG_EN (0x1 << 6) +#define RT5640_DRC_AGC_NGH_MASK (0x1 << 5) +#define RT5640_DRC_AGC_NGH_SFT 5 +#define RT5640_DRC_AGC_NGH_DIS (0x0 << 5) +#define RT5640_DRC_AGC_NGH_EN (0x1 << 5) +#define RT5640_DRC_AGC_NGT_MASK (0x1f) +#define RT5640_DRC_AGC_NGT_SFT 0 + +/* ANC Control 1 (0xb8) */ +#define RT5640_ANC_M_MASK (0x1 << 15) +#define RT5640_ANC_M_SFT 15 +#define RT5640_ANC_M_NOR (0x0 << 15) +#define RT5640_ANC_M_REV (0x1 << 15) +#define RT5640_ANC_MASK (0x1 << 14) +#define RT5640_ANC_SFT 14 +#define RT5640_ANC_DIS (0x0 << 14) +#define RT5640_ANC_EN (0x1 << 14) +#define RT5640_ANC_MD_MASK (0x3 << 12) +#define RT5640_ANC_MD_SFT 12 +#define RT5640_ANC_MD_DIS (0x0 << 12) +#define RT5640_ANC_MD_67MS (0x1 << 12) +#define RT5640_ANC_MD_267MS (0x2 << 12) +#define RT5640_ANC_MD_1067MS (0x3 << 12) +#define RT5640_ANC_SN_MASK (0x1 << 11) +#define RT5640_ANC_SN_SFT 11 +#define RT5640_ANC_SN_DIS (0x0 << 11) +#define RT5640_ANC_SN_EN (0x1 << 11) +#define RT5640_ANC_CLK_MASK (0x1 << 10) +#define RT5640_ANC_CLK_SFT 10 +#define RT5640_ANC_CLK_ANC (0x0 << 10) +#define RT5640_ANC_CLK_REG (0x1 << 10) +#define RT5640_ANC_ZCD_MASK (0x3 << 8) +#define RT5640_ANC_ZCD_SFT 8 +#define RT5640_ANC_ZCD_DIS (0x0 << 8) +#define RT5640_ANC_ZCD_T1 (0x1 << 8) +#define RT5640_ANC_ZCD_T2 (0x2 << 8) +#define RT5640_ANC_ZCD_WT (0x3 << 8) +#define RT5640_ANC_CS_MASK (0x1 << 7) +#define RT5640_ANC_CS_SFT 7 +#define RT5640_ANC_CS_DIS (0x0 << 7) +#define RT5640_ANC_CS_EN (0x1 << 7) +#define RT5640_ANC_SW_MASK (0x1 << 6) +#define RT5640_ANC_SW_SFT 6 +#define RT5640_ANC_SW_NOR (0x0 << 6) +#define RT5640_ANC_SW_AUTO (0x1 << 6) +#define RT5640_ANC_CO_L_MASK (0x3f) +#define RT5640_ANC_CO_L_SFT 0 + +/* ANC Control 2 (0xb6) */ +#define RT5640_ANC_FG_R_MASK (0xf << 12) +#define RT5640_ANC_FG_R_SFT 12 +#define RT5640_ANC_FG_L_MASK (0xf << 8) +#define RT5640_ANC_FG_L_SFT 8 +#define RT5640_ANC_CG_R_MASK (0xf << 4) +#define RT5640_ANC_CG_R_SFT 4 +#define RT5640_ANC_CG_L_MASK (0xf) +#define RT5640_ANC_CG_L_SFT 0 + +/* ANC Control 3 (0xb6) */ +#define RT5640_ANC_CD_MASK (0x1 << 6) +#define RT5640_ANC_CD_SFT 6 +#define RT5640_ANC_CD_BOTH (0x0 << 6) +#define RT5640_ANC_CD_IND (0x1 << 6) +#define RT5640_ANC_CO_R_MASK (0x3f) +#define RT5640_ANC_CO_R_SFT 0 + +/* Jack Detect Control (0xbb) */ +#define RT5640_JD_MASK (0x7 << 13) +#define RT5640_JD_SFT 13 +#define RT5640_JD_DIS (0x0 << 13) +#define RT5640_JD_GPIO1 (0x1 << 13) +#define RT5640_JD_JD1_IN4P (0x2 << 13) +#define RT5640_JD_JD2_IN4N (0x3 << 13) +#define RT5640_JD_GPIO2 (0x4 << 13) +#define RT5640_JD_GPIO3 (0x5 << 13) +#define RT5640_JD_GPIO4 (0x6 << 13) +#define RT5640_JD_HP_MASK (0x1 << 11) +#define RT5640_JD_HP_SFT 11 +#define RT5640_JD_HP_DIS (0x0 << 11) +#define RT5640_JD_HP_EN (0x1 << 11) +#define RT5640_JD_HP_TRG_MASK (0x1 << 10) +#define RT5640_JD_HP_TRG_SFT 10 +#define RT5640_JD_HP_TRG_LO (0x0 << 10) +#define RT5640_JD_HP_TRG_HI (0x1 << 10) +#define RT5640_JD_SPL_MASK (0x1 << 9) +#define RT5640_JD_SPL_SFT 9 +#define RT5640_JD_SPL_DIS (0x0 << 9) +#define RT5640_JD_SPL_EN (0x1 << 9) +#define RT5640_JD_SPL_TRG_MASK (0x1 << 8) +#define RT5640_JD_SPL_TRG_SFT 8 +#define RT5640_JD_SPL_TRG_LO (0x0 << 8) +#define RT5640_JD_SPL_TRG_HI (0x1 << 8) +#define RT5640_JD_SPR_MASK (0x1 << 7) +#define RT5640_JD_SPR_SFT 7 +#define RT5640_JD_SPR_DIS (0x0 << 7) +#define RT5640_JD_SPR_EN (0x1 << 7) +#define RT5640_JD_SPR_TRG_MASK (0x1 << 6) +#define RT5640_JD_SPR_TRG_SFT 6 +#define RT5640_JD_SPR_TRG_LO (0x0 << 6) +#define RT5640_JD_SPR_TRG_HI (0x1 << 6) +#define RT5640_JD_MO_MASK (0x1 << 5) +#define RT5640_JD_MO_SFT 5 +#define RT5640_JD_MO_DIS (0x0 << 5) +#define RT5640_JD_MO_EN (0x1 << 5) +#define RT5640_JD_MO_TRG_MASK (0x1 << 4) +#define RT5640_JD_MO_TRG_SFT 4 +#define RT5640_JD_MO_TRG_LO (0x0 << 4) +#define RT5640_JD_MO_TRG_HI (0x1 << 4) +#define RT5640_JD_LO_MASK (0x1 << 3) +#define RT5640_JD_LO_SFT 3 +#define RT5640_JD_LO_DIS (0x0 << 3) +#define RT5640_JD_LO_EN (0x1 << 3) +#define RT5640_JD_LO_TRG_MASK (0x1 << 2) +#define RT5640_JD_LO_TRG_SFT 2 +#define RT5640_JD_LO_TRG_LO (0x0 << 2) +#define RT5640_JD_LO_TRG_HI (0x1 << 2) +#define RT5640_JD1_IN4P_MASK (0x1 << 1) +#define RT5640_JD1_IN4P_SFT 1 +#define RT5640_JD1_IN4P_DIS (0x0 << 1) +#define RT5640_JD1_IN4P_EN (0x1 << 1) +#define RT5640_JD2_IN4N_MASK (0x1) +#define RT5640_JD2_IN4N_SFT 0 +#define RT5640_JD2_IN4N_DIS (0x0) +#define RT5640_JD2_IN4N_EN (0x1) + +/* Jack detect for ANC (0xbc) */ +#define RT5640_ANC_DET_MASK (0x3 << 4) +#define RT5640_ANC_DET_SFT 4 +#define RT5640_ANC_DET_DIS (0x0 << 4) +#define RT5640_ANC_DET_MB1 (0x1 << 4) +#define RT5640_ANC_DET_MB2 (0x2 << 4) +#define RT5640_ANC_DET_JD (0x3 << 4) +#define RT5640_AD_TRG_MASK (0x1 << 3) +#define RT5640_AD_TRG_SFT 3 +#define RT5640_AD_TRG_LO (0x0 << 3) +#define RT5640_AD_TRG_HI (0x1 << 3) +#define RT5640_ANCM_DET_MASK (0x3 << 4) +#define RT5640_ANCM_DET_SFT 4 +#define RT5640_ANCM_DET_DIS (0x0 << 4) +#define RT5640_ANCM_DET_MB1 (0x1 << 4) +#define RT5640_ANCM_DET_MB2 (0x2 << 4) +#define RT5640_ANCM_DET_JD (0x3 << 4) +#define RT5640_AMD_TRG_MASK (0x1 << 3) +#define RT5640_AMD_TRG_SFT 3 +#define RT5640_AMD_TRG_LO (0x0 << 3) +#define RT5640_AMD_TRG_HI (0x1 << 3) + +/* IRQ Control 1 (0xbd) */ +#define RT5640_IRQ_JD_MASK (0x1 << 15) +#define RT5640_IRQ_JD_SFT 15 +#define RT5640_IRQ_JD_BP (0x0 << 15) +#define RT5640_IRQ_JD_NOR (0x1 << 15) +#define RT5640_IRQ_OT_MASK (0x1 << 14) +#define RT5640_IRQ_OT_SFT 14 +#define RT5640_IRQ_OT_BP (0x0 << 14) +#define RT5640_IRQ_OT_NOR (0x1 << 14) +#define RT5640_JD_STKY_MASK (0x1 << 13) +#define RT5640_JD_STKY_SFT 13 +#define RT5640_JD_STKY_DIS (0x0 << 13) +#define RT5640_JD_STKY_EN (0x1 << 13) +#define RT5640_OT_STKY_MASK (0x1 << 12) +#define RT5640_OT_STKY_SFT 12 +#define RT5640_OT_STKY_DIS (0x0 << 12) +#define RT5640_OT_STKY_EN (0x1 << 12) +#define RT5640_JD_P_MASK (0x1 << 11) +#define RT5640_JD_P_SFT 11 +#define RT5640_JD_P_NOR (0x0 << 11) +#define RT5640_JD_P_INV (0x1 << 11) +#define RT5640_OT_P_MASK (0x1 << 10) +#define RT5640_OT_P_SFT 10 +#define RT5640_OT_P_NOR (0x0 << 10) +#define RT5640_OT_P_INV (0x1 << 10) + +/* IRQ Control 2 (0xbe) */ +#define RT5640_IRQ_MB1_OC_MASK (0x1 << 15) +#define RT5640_IRQ_MB1_OC_SFT 15 +#define RT5640_IRQ_MB1_OC_BP (0x0 << 15) +#define RT5640_IRQ_MB1_OC_NOR (0x1 << 15) +#define RT5640_IRQ_MB2_OC_MASK (0x1 << 14) +#define RT5640_IRQ_MB2_OC_SFT 14 +#define RT5640_IRQ_MB2_OC_BP (0x0 << 14) +#define RT5640_IRQ_MB2_OC_NOR (0x1 << 14) +#define RT5640_MB1_OC_STKY_MASK (0x1 << 11) +#define RT5640_MB1_OC_STKY_SFT 11 +#define RT5640_MB1_OC_STKY_DIS (0x0 << 11) +#define RT5640_MB1_OC_STKY_EN (0x1 << 11) +#define RT5640_MB2_OC_STKY_MASK (0x1 << 10) +#define RT5640_MB2_OC_STKY_SFT 10 +#define RT5640_MB2_OC_STKY_DIS (0x0 << 10) +#define RT5640_MB2_OC_STKY_EN (0x1 << 10) +#define RT5640_MB1_OC_P_MASK (0x1 << 7) +#define RT5640_MB1_OC_P_SFT 7 +#define RT5640_MB1_OC_P_NOR (0x0 << 7) +#define RT5640_MB1_OC_P_INV (0x1 << 7) +#define RT5640_MB2_OC_P_MASK (0x1 << 6) +#define RT5640_MB2_OC_P_SFT 6 +#define RT5640_MB2_OC_P_NOR (0x0 << 6) +#define RT5640_MB2_OC_P_INV (0x1 << 6) +#define RT5640_MB1_OC_CLR (0x1 << 3) +#define RT5640_MB1_OC_CLR_SFT 3 +#define RT5640_MB2_OC_CLR (0x1 << 2) +#define RT5640_MB2_OC_CLR_SFT 2 + +/* GPIO Control 1 (0xc0) */ +#define RT5640_GP1_PIN_MASK (0x1 << 15) +#define RT5640_GP1_PIN_SFT 15 +#define RT5640_GP1_PIN_GPIO1 (0x0 << 15) +#define RT5640_GP1_PIN_IRQ (0x1 << 15) +#define RT5640_GP2_PIN_MASK (0x1 << 14) +#define RT5640_GP2_PIN_SFT 14 +#define RT5640_GP2_PIN_GPIO2 (0x0 << 14) +#define RT5640_GP2_PIN_DMIC1_SCL (0x1 << 14) +#define RT5640_GP3_PIN_MASK (0x3 << 12) +#define RT5640_GP3_PIN_SFT 12 +#define RT5640_GP3_PIN_GPIO3 (0x0 << 12) +#define RT5640_GP3_PIN_DMIC1_SDA (0x1 << 12) +#define RT5640_GP3_PIN_IRQ (0x2 << 12) +#define RT5640_GP4_PIN_MASK (0x1 << 11) +#define RT5640_GP4_PIN_SFT 11 +#define RT5640_GP4_PIN_GPIO4 (0x0 << 11) +#define RT5640_GP4_PIN_DMIC2_SDA (0x1 << 11) +#define RT5640_DP_SIG_MASK (0x1 << 10) +#define RT5640_DP_SIG_SFT 10 +#define RT5640_DP_SIG_TEST (0x0 << 10) +#define RT5640_DP_SIG_AP (0x1 << 10) +#define RT5640_GPIO_M_MASK (0x1 << 9) +#define RT5640_GPIO_M_SFT 9 +#define RT5640_GPIO_M_FLT (0x0 << 9) +#define RT5640_GPIO_M_PH (0x1 << 9) + +/* GPIO Control 3 (0xc2) */ +#define RT5640_GP4_PF_MASK (0x1 << 11) +#define RT5640_GP4_PF_SFT 11 +#define RT5640_GP4_PF_IN (0x0 << 11) +#define RT5640_GP4_PF_OUT (0x1 << 11) +#define RT5640_GP4_OUT_MASK (0x1 << 10) +#define RT5640_GP4_OUT_SFT 10 +#define RT5640_GP4_OUT_LO (0x0 << 10) +#define RT5640_GP4_OUT_HI (0x1 << 10) +#define RT5640_GP4_P_MASK (0x1 << 9) +#define RT5640_GP4_P_SFT 9 +#define RT5640_GP4_P_NOR (0x0 << 9) +#define RT5640_GP4_P_INV (0x1 << 9) +#define RT5640_GP3_PF_MASK (0x1 << 8) +#define RT5640_GP3_PF_SFT 8 +#define RT5640_GP3_PF_IN (0x0 << 8) +#define RT5640_GP3_PF_OUT (0x1 << 8) +#define RT5640_GP3_OUT_MASK (0x1 << 7) +#define RT5640_GP3_OUT_SFT 7 +#define RT5640_GP3_OUT_LO (0x0 << 7) +#define RT5640_GP3_OUT_HI (0x1 << 7) +#define RT5640_GP3_P_MASK (0x1 << 6) +#define RT5640_GP3_P_SFT 6 +#define RT5640_GP3_P_NOR (0x0 << 6) +#define RT5640_GP3_P_INV (0x1 << 6) +#define RT5640_GP2_PF_MASK (0x1 << 5) +#define RT5640_GP2_PF_SFT 5 +#define RT5640_GP2_PF_IN (0x0 << 5) +#define RT5640_GP2_PF_OUT (0x1 << 5) +#define RT5640_GP2_OUT_MASK (0x1 << 4) +#define RT5640_GP2_OUT_SFT 4 +#define RT5640_GP2_OUT_LO (0x0 << 4) +#define RT5640_GP2_OUT_HI (0x1 << 4) +#define RT5640_GP2_P_MASK (0x1 << 3) +#define RT5640_GP2_P_SFT 3 +#define RT5640_GP2_P_NOR (0x0 << 3) +#define RT5640_GP2_P_INV (0x1 << 3) +#define RT5640_GP1_PF_MASK (0x1 << 2) +#define RT5640_GP1_PF_SFT 2 +#define RT5640_GP1_PF_IN (0x0 << 2) +#define RT5640_GP1_PF_OUT (0x1 << 2) +#define RT5640_GP1_OUT_MASK (0x1 << 1) +#define RT5640_GP1_OUT_SFT 1 +#define RT5640_GP1_OUT_LO (0x0 << 1) +#define RT5640_GP1_OUT_HI (0x1 << 1) +#define RT5640_GP1_P_MASK (0x1) +#define RT5640_GP1_P_SFT 0 +#define RT5640_GP1_P_NOR (0x0) +#define RT5640_GP1_P_INV (0x1) + +/* FM34-500 Register Control 1 (0xc4) */ +#define RT5640_DSP_ADD_SFT 0 + +/* FM34-500 Register Control 2 (0xc5) */ +#define RT5640_DSP_DAT_SFT 0 + +/* FM34-500 Register Control 3 (0xc6) */ +#define RT5640_DSP_BUSY_MASK (0x1 << 15) +#define RT5640_DSP_BUSY_BIT 15 +#define RT5640_DSP_DS_MASK (0x1 << 14) +#define RT5640_DSP_DS_SFT 14 +#define RT5640_DSP_DS_FM3010 (0x1 << 14) +#define RT5640_DSP_DS_TEMP (0x1 << 14) +#define RT5640_DSP_CLK_MASK (0x3 << 12) +#define RT5640_DSP_CLK_SFT 12 +#define RT5640_DSP_CLK_384K (0x0 << 12) +#define RT5640_DSP_CLK_192K (0x1 << 12) +#define RT5640_DSP_CLK_96K (0x2 << 12) +#define RT5640_DSP_CLK_64K (0x3 << 12) +#define RT5640_DSP_PD_PIN_MASK (0x1 << 11) +#define RT5640_DSP_PD_PIN_SFT 11 +#define RT5640_DSP_PD_PIN_LO (0x0 << 11) +#define RT5640_DSP_PD_PIN_HI (0x1 << 11) +#define RT5640_DSP_RST_PIN_MASK (0x1 << 10) +#define RT5640_DSP_RST_PIN_SFT 10 +#define RT5640_DSP_RST_PIN_LO (0x0 << 10) +#define RT5640_DSP_RST_PIN_HI (0x1 << 10) +#define RT5640_DSP_R_EN (0x1 << 9) +#define RT5640_DSP_R_EN_BIT 9 +#define RT5640_DSP_W_EN (0x1 << 8) +#define RT5640_DSP_W_EN_BIT 8 +#define RT5640_DSP_CMD_MASK (0xff) +#define RT5640_DSP_CMD_SFT 0 +#define RT5640_DSP_CMD_MW (0x3B) /* Memory Write */ +#define RT5640_DSP_CMD_MR (0x37) /* Memory Read */ +#define RT5640_DSP_CMD_RR (0x60) /* Register Read */ +#define RT5640_DSP_CMD_RW (0x68) /* Register Write */ + +/* Programmable Register Array Control 1 (0xc8) */ +#define RT5640_REG_SEQ_MASK (0xf << 12) +#define RT5640_REG_SEQ_SFT 12 +#define RT5640_SEQ1_ST_MASK (0x1 << 11) /*RO*/ +#define RT5640_SEQ1_ST_SFT 11 +#define RT5640_SEQ1_ST_RUN (0x0 << 11) +#define RT5640_SEQ1_ST_FIN (0x1 << 11) +#define RT5640_SEQ2_ST_MASK (0x1 << 10) /*RO*/ +#define RT5640_SEQ2_ST_SFT 10 +#define RT5640_SEQ2_ST_RUN (0x0 << 10) +#define RT5640_SEQ2_ST_FIN (0x1 << 10) +#define RT5640_REG_LV_MASK (0x1 << 9) +#define RT5640_REG_LV_SFT 9 +#define RT5640_REG_LV_MX (0x0 << 9) +#define RT5640_REG_LV_PR (0x1 << 9) +#define RT5640_SEQ_2_PT_MASK (0x1 << 8) +#define RT5640_SEQ_2_PT_BIT 8 +#define RT5640_REG_IDX_MASK (0xff) +#define RT5640_REG_IDX_SFT 0 + +/* Programmable Register Array Control 2 (0xc9) */ +#define RT5640_REG_DAT_MASK (0xffff) +#define RT5640_REG_DAT_SFT 0 + +/* Programmable Register Array Control 3 (0xca) */ +#define RT5640_SEQ_DLY_MASK (0xff << 8) +#define RT5640_SEQ_DLY_SFT 8 +#define RT5640_PROG_MASK (0x1 << 7) +#define RT5640_PROG_SFT 7 +#define RT5640_PROG_DIS (0x0 << 7) +#define RT5640_PROG_EN (0x1 << 7) +#define RT5640_SEQ1_PT_RUN (0x1 << 6) +#define RT5640_SEQ1_PT_RUN_BIT 6 +#define RT5640_SEQ2_PT_RUN (0x1 << 5) +#define RT5640_SEQ2_PT_RUN_BIT 5 + +/* Programmable Register Array Control 4 (0xcb) */ +#define RT5640_SEQ1_START_MASK (0xf << 8) +#define RT5640_SEQ1_START_SFT 8 +#define RT5640_SEQ1_END_MASK (0xf) +#define RT5640_SEQ1_END_SFT 0 + +/* Programmable Register Array Control 5 (0xcc) */ +#define RT5640_SEQ2_START_MASK (0xf << 8) +#define RT5640_SEQ2_START_SFT 8 +#define RT5640_SEQ2_END_MASK (0xf) +#define RT5640_SEQ2_END_SFT 0 + +/* Scramble Function (0xcd) */ +#define RT5640_SCB_KEY_MASK (0xff) +#define RT5640_SCB_KEY_SFT 0 + +/* Scramble Control (0xce) */ +#define RT5640_SCB_SWAP_MASK (0x1 << 15) +#define RT5640_SCB_SWAP_SFT 15 +#define RT5640_SCB_SWAP_DIS (0x0 << 15) +#define RT5640_SCB_SWAP_EN (0x1 << 15) +#define RT5640_SCB_MASK (0x1 << 14) +#define RT5640_SCB_SFT 14 +#define RT5640_SCB_DIS (0x0 << 14) +#define RT5640_SCB_EN (0x1 << 14) + +/* Baseback Control (0xcf) */ +#define RT5640_BB_MASK (0x1 << 15) +#define RT5640_BB_SFT 15 +#define RT5640_BB_DIS (0x0 << 15) +#define RT5640_BB_EN (0x1 << 15) +#define RT5640_BB_CT_MASK (0x7 << 12) +#define RT5640_BB_CT_SFT 12 +#define RT5640_BB_CT_A (0x0 << 12) +#define RT5640_BB_CT_B (0x1 << 12) +#define RT5640_BB_CT_C (0x2 << 12) +#define RT5640_BB_CT_D (0x3 << 12) +#define RT5640_M_BB_L_MASK (0x1 << 9) +#define RT5640_M_BB_L_SFT 9 +#define RT5640_M_BB_R_MASK (0x1 << 8) +#define RT5640_M_BB_R_SFT 8 +#define RT5640_M_BB_HPF_L_MASK (0x1 << 7) +#define RT5640_M_BB_HPF_L_SFT 7 +#define RT5640_M_BB_HPF_R_MASK (0x1 << 6) +#define RT5640_M_BB_HPF_R_SFT 6 +#define RT5640_G_BB_BST_MASK (0x3f) +#define RT5640_G_BB_BST_SFT 0 + +/* MP3 Plus Control 1 (0xd0) */ +#define RT5640_M_MP3_L_MASK (0x1 << 15) +#define RT5640_M_MP3_L_SFT 15 +#define RT5640_M_MP3_R_MASK (0x1 << 14) +#define RT5640_M_MP3_R_SFT 14 +#define RT5640_M_MP3_MASK (0x1 << 13) +#define RT5640_M_MP3_SFT 13 +#define RT5640_M_MP3_DIS (0x0 << 13) +#define RT5640_M_MP3_EN (0x1 << 13) +#define RT5640_EG_MP3_MASK (0x1f << 8) +#define RT5640_EG_MP3_SFT 8 +#define RT5640_MP3_HLP_MASK (0x1 << 7) +#define RT5640_MP3_HLP_SFT 7 +#define RT5640_MP3_HLP_DIS (0x0 << 7) +#define RT5640_MP3_HLP_EN (0x1 << 7) +#define RT5640_M_MP3_ORG_L_MASK (0x1 << 6) +#define RT5640_M_MP3_ORG_L_SFT 6 +#define RT5640_M_MP3_ORG_R_MASK (0x1 << 5) +#define RT5640_M_MP3_ORG_R_SFT 5 + +/* MP3 Plus Control 2 (0xd1) */ +#define RT5640_MP3_WT_MASK (0x1 << 13) +#define RT5640_MP3_WT_SFT 13 +#define RT5640_MP3_WT_1_4 (0x0 << 13) +#define RT5640_MP3_WT_1_2 (0x1 << 13) +#define RT5640_OG_MP3_MASK (0x1f << 8) +#define RT5640_OG_MP3_SFT 8 +#define RT5640_HG_MP3_MASK (0x3f) +#define RT5640_HG_MP3_SFT 0 + +/* 3D HP Control 1 (0xd2) */ +#define RT5640_3D_CF_MASK (0x1 << 15) +#define RT5640_3D_CF_SFT 15 +#define RT5640_3D_CF_DIS (0x0 << 15) +#define RT5640_3D_CF_EN (0x1 << 15) +#define RT5640_3D_HP_MASK (0x1 << 14) +#define RT5640_3D_HP_SFT 14 +#define RT5640_3D_HP_DIS (0x0 << 14) +#define RT5640_3D_HP_EN (0x1 << 14) +#define RT5640_3D_BT_MASK (0x1 << 13) +#define RT5640_3D_BT_SFT 13 +#define RT5640_3D_BT_DIS (0x0 << 13) +#define RT5640_3D_BT_EN (0x1 << 13) +#define RT5640_3D_1F_MIX_MASK (0x3 << 11) +#define RT5640_3D_1F_MIX_SFT 11 +#define RT5640_3D_HP_M_MASK (0x1 << 10) +#define RT5640_3D_HP_M_SFT 10 +#define RT5640_3D_HP_M_SUR (0x0 << 10) +#define RT5640_3D_HP_M_FRO (0x1 << 10) +#define RT5640_M_3D_HRTF_MASK (0x1 << 9) +#define RT5640_M_3D_HRTF_SFT 9 +#define RT5640_M_3D_D2H_MASK (0x1 << 8) +#define RT5640_M_3D_D2H_SFT 8 +#define RT5640_M_3D_D2R_MASK (0x1 << 7) +#define RT5640_M_3D_D2R_SFT 7 +#define RT5640_M_3D_REVB_MASK (0x1 << 6) +#define RT5640_M_3D_REVB_SFT 6 + +/* Adjustable high pass filter control 1 (0xd3) */ +#define RT5640_2ND_HPF_MASK (0x1 << 15) +#define RT5640_2ND_HPF_SFT 15 +#define RT5640_2ND_HPF_DIS (0x0 << 15) +#define RT5640_2ND_HPF_EN (0x1 << 15) +#define RT5640_HPF_CF_L_MASK (0x7 << 12) +#define RT5640_HPF_CF_L_SFT 12 +#define RT5640_1ST_HPF_MASK (0x1 << 11) +#define RT5640_1ST_HPF_SFT 11 +#define RT5640_1ST_HPF_DIS (0x0 << 11) +#define RT5640_1ST_HPF_EN (0x1 << 11) +#define RT5640_HPF_CF_R_MASK (0x7 << 8) +#define RT5640_HPF_CF_R_SFT 8 +#define RT5640_ZD_T_MASK (0x3 << 6) +#define RT5640_ZD_T_SFT 6 +#define RT5640_ZD_F_MASK (0x3 << 4) +#define RT5640_ZD_F_SFT 4 +#define RT5640_ZD_F_IM (0x0 << 4) +#define RT5640_ZD_F_ZC_IM (0x1 << 4) +#define RT5640_ZD_F_ZC_IOD (0x2 << 4) +#define RT5640_ZD_F_UN (0x3 << 4) + +/* HP calibration control and Amp detection (0xd6) */ +#define RT5640_SI_DAC_MASK (0x1 << 11) +#define RT5640_SI_DAC_SFT 11 +#define RT5640_SI_DAC_AUTO (0x0 << 11) +#define RT5640_SI_DAC_TEST (0x1 << 11) +#define RT5640_DC_CAL_M_MASK (0x1 << 10) +#define RT5640_DC_CAL_M_SFT 10 +#define RT5640_DC_CAL_M_CAL (0x0 << 10) +#define RT5640_DC_CAL_M_NOR (0x1 << 10) +#define RT5640_DC_CAL_MASK (0x1 << 9) +#define RT5640_DC_CAL_SFT 9 +#define RT5640_DC_CAL_DIS (0x0 << 9) +#define RT5640_DC_CAL_EN (0x1 << 9) +#define RT5640_HPD_RCV_MASK (0x7 << 6) +#define RT5640_HPD_RCV_SFT 6 +#define RT5640_HPD_PS_MASK (0x1 << 5) +#define RT5640_HPD_PS_SFT 5 +#define RT5640_HPD_PS_DIS (0x0 << 5) +#define RT5640_HPD_PS_EN (0x1 << 5) +#define RT5640_CAL_M_MASK (0x1 << 4) +#define RT5640_CAL_M_SFT 4 +#define RT5640_CAL_M_DEP (0x0 << 4) +#define RT5640_CAL_M_CAL (0x1 << 4) +#define RT5640_CAL_MASK (0x1 << 3) +#define RT5640_CAL_SFT 3 +#define RT5640_CAL_DIS (0x0 << 3) +#define RT5640_CAL_EN (0x1 << 3) +#define RT5640_CAL_TEST_MASK (0x1 << 2) +#define RT5640_CAL_TEST_SFT 2 +#define RT5640_CAL_TEST_DIS (0x0 << 2) +#define RT5640_CAL_TEST_EN (0x1 << 2) +#define RT5640_CAL_P_MASK (0x3) +#define RT5640_CAL_P_SFT 0 +#define RT5640_CAL_P_NONE (0x0) +#define RT5640_CAL_P_CAL (0x1) +#define RT5640_CAL_P_DAC_CAL (0x2) + +/* Soft volume and zero cross control 1 (0xd9) */ +#define RT5640_SV_MASK (0x1 << 15) +#define RT5640_SV_SFT 15 +#define RT5640_SV_DIS (0x0 << 15) +#define RT5640_SV_EN (0x1 << 15) +#define RT5640_SPO_SV_MASK (0x1 << 14) +#define RT5640_SPO_SV_SFT 14 +#define RT5640_SPO_SV_DIS (0x0 << 14) +#define RT5640_SPO_SV_EN (0x1 << 14) +#define RT5640_OUT_SV_MASK (0x1 << 13) +#define RT5640_OUT_SV_SFT 13 +#define RT5640_OUT_SV_DIS (0x0 << 13) +#define RT5640_OUT_SV_EN (0x1 << 13) +#define RT5640_HP_SV_MASK (0x1 << 12) +#define RT5640_HP_SV_SFT 12 +#define RT5640_HP_SV_DIS (0x0 << 12) +#define RT5640_HP_SV_EN (0x1 << 12) +#define RT5640_ZCD_DIG_MASK (0x1 << 11) +#define RT5640_ZCD_DIG_SFT 11 +#define RT5640_ZCD_DIG_DIS (0x0 << 11) +#define RT5640_ZCD_DIG_EN (0x1 << 11) +#define RT5640_ZCD_MASK (0x1 << 10) +#define RT5640_ZCD_SFT 10 +#define RT5640_ZCD_PD (0x0 << 10) +#define RT5640_ZCD_PU (0x1 << 10) +#define RT5640_M_ZCD_MASK (0x3f << 4) +#define RT5640_M_ZCD_SFT 4 +#define RT5640_M_ZCD_RM_L (0x1 << 9) +#define RT5640_M_ZCD_RM_R (0x1 << 8) +#define RT5640_M_ZCD_SM_L (0x1 << 7) +#define RT5640_M_ZCD_SM_R (0x1 << 6) +#define RT5640_M_ZCD_OM_L (0x1 << 5) +#define RT5640_M_ZCD_OM_R (0x1 << 4) +#define RT5640_SV_DLY_MASK (0xf) +#define RT5640_SV_DLY_SFT 0 + +/* Soft volume and zero cross control 2 (0xda) */ +#define RT5640_ZCD_HP_MASK (0x1 << 15) +#define RT5640_ZCD_HP_SFT 15 +#define RT5640_ZCD_HP_DIS (0x0 << 15) +#define RT5640_ZCD_HP_EN (0x1 << 15) + + +/* Codec Private Register definition */ +/* 3D Speaker Control (0x63) */ +#define RT5640_3D_SPK_MASK (0x1 << 15) +#define RT5640_3D_SPK_SFT 15 +#define RT5640_3D_SPK_DIS (0x0 << 15) +#define RT5640_3D_SPK_EN (0x1 << 15) +#define RT5640_3D_SPK_M_MASK (0x3 << 13) +#define RT5640_3D_SPK_M_SFT 13 +#define RT5640_3D_SPK_CG_MASK (0x1f << 8) +#define RT5640_3D_SPK_CG_SFT 8 +#define RT5640_3D_SPK_SG_MASK (0x1f) +#define RT5640_3D_SPK_SG_SFT 0 + +/* Wind Noise Detection Control 1 (0x6c) */ +#define RT5640_WND_MASK (0x1 << 15) +#define RT5640_WND_SFT 15 +#define RT5640_WND_DIS (0x0 << 15) +#define RT5640_WND_EN (0x1 << 15) + +/* Wind Noise Detection Control 2 (0x6d) */ +#define RT5640_WND_FC_NW_MASK (0x3f << 10) +#define RT5640_WND_FC_NW_SFT 10 +#define RT5640_WND_FC_WK_MASK (0x3f << 4) +#define RT5640_WND_FC_WK_SFT 4 + +/* Wind Noise Detection Control 3 (0x6e) */ +#define RT5640_HPF_FC_MASK (0x3f << 6) +#define RT5640_HPF_FC_SFT 6 +#define RT5640_WND_FC_ST_MASK (0x3f) +#define RT5640_WND_FC_ST_SFT 0 + +/* Wind Noise Detection Control 4 (0x6f) */ +#define RT5640_WND_TH_LO_MASK (0x3ff) +#define RT5640_WND_TH_LO_SFT 0 + +/* Wind Noise Detection Control 5 (0x70) */ +#define RT5640_WND_TH_HI_MASK (0x3ff) +#define RT5640_WND_TH_HI_SFT 0 + +/* Wind Noise Detection Control 8 (0x73) */ +#define RT5640_WND_WIND_MASK (0x1 << 13) /* Read-Only */ +#define RT5640_WND_WIND_SFT 13 +#define RT5640_WND_STRONG_MASK (0x1 << 12) /* Read-Only */ +#define RT5640_WND_STRONG_SFT 12 +enum { + RT5640_NO_WIND, + RT5640_BREEZE, + RT5640_STORM, +}; + +/* Dipole Speaker Interface (0x75) */ +#define RT5640_DP_ATT_MASK (0x3 << 14) +#define RT5640_DP_ATT_SFT 14 +#define RT5640_DP_SPK_MASK (0x1 << 10) +#define RT5640_DP_SPK_SFT 10 +#define RT5640_DP_SPK_DIS (0x0 << 10) +#define RT5640_DP_SPK_EN (0x1 << 10) + +/* EQ Pre Volume Control (0xb3) */ +#define RT5640_EQ_PRE_VOL_MASK (0xffff) +#define RT5640_EQ_PRE_VOL_SFT 0 + +/* EQ Post Volume Control (0xb4) */ +#define RT5640_EQ_PST_VOL_MASK (0xffff) +#define RT5640_EQ_PST_VOL_SFT 0 + +#define RT5640_NO_JACK BIT(0) +#define RT5640_HEADSET_DET BIT(1) +#define RT5640_HEADPHO_DET BIT(2) + +/* System Clock Source */ +#define RT5640_SCLK_S_MCLK 0 +#define RT5640_SCLK_S_PLL1 1 +#define RT5640_SCLK_S_PLL1_TK 2 +#define RT5640_SCLK_S_RCCLK 3 + +/* PLL1 Source */ +#define RT5640_PLL1_S_MCLK 0 +#define RT5640_PLL1_S_BCLK1 1 +#define RT5640_PLL1_S_BCLK2 2 +#define RT5640_PLL1_S_BCLK3 3 + + +enum { + RT5640_AIF1, + RT5640_AIF2, + RT5640_AIF3, + RT5640_AIFS, +}; + +enum { + RT5640_U_IF1 = 0x1, + RT5640_U_IF2 = 0x2, + RT5640_U_IF3 = 0x4, +}; + +enum { + RT5640_IF_123, + RT5640_IF_132, + RT5640_IF_312, + RT5640_IF_321, + RT5640_IF_231, + RT5640_IF_213, + RT5640_IF_113, + RT5640_IF_223, + RT5640_IF_ALL, +}; + +enum { + RT5640_DMIC_DIS, + RT5640_DMIC1, + RT5640_DMIC2, +}; + +struct rt5640_pll_code { + bool m_bp; /* Indicates bypass m code or not. */ + int m_code; + int n_code; + int k_code; +}; + +struct rt5640_priv { + struct snd_soc_codec *codec; + struct rt5640_platform_data pdata; + struct regmap *regmap; + + int sysclk; + int sysclk_src; + int lrck[RT5640_AIFS]; + int bclk[RT5640_AIFS]; + int master[RT5640_AIFS]; + + struct rt5640_pll_code pll_code; + int pll_src; + int pll_in; + int pll_out; + + int dmic_en; +}; + +#endif -- cgit v1.2.3 From a6543a1cb56ab1bf4f722226ae36dd51eeaea97e Mon Sep 17 00:00:00 2001 From: "Patil, Rachna" Date: Thu, 24 Jan 2013 03:45:09 +0000 Subject: mfd: ti_am335x_tscadc: Add DT support Add DT support in the MFD core driver. The node name is "am3359" because it was tested on this platform. Signed-off-by: Pantelis Antoniou Signed-off-by: Patil, Rachna Signed-off-by: Felipe Balbi Signed-off-by: Sebastian Andrzej Siewior --- drivers/mfd/ti_am335x_tscadc.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c index 1d6c74049590..292d34eaed54 100644 --- a/drivers/mfd/ti_am335x_tscadc.c +++ b/drivers/mfd/ti_am335x_tscadc.c @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include @@ -90,20 +92,31 @@ static int ti_tscadc_probe(struct platform_device *pdev) struct resource *res; struct clk *clk; struct mfd_tscadc_board *pdata = pdev->dev.platform_data; + struct device_node *node = pdev->dev.of_node; struct mfd_cell *cell; int err, ctrl; int clk_value, clock_rate; - int tsc_wires, adc_channels = 0, total_channels; + int tsc_wires = 0, adc_channels = 0, total_channels; - if (!pdata) { + if (!pdata && !pdev->dev.of_node) { dev_err(&pdev->dev, "Could not find platform data\n"); return -EINVAL; } - if (pdata->adc_init) - adc_channels = pdata->adc_init->adc_channels; + if (pdev->dev.platform_data) { + if (pdata->tsc_init) + tsc_wires = pdata->tsc_init->wires; + + if (pdata->adc_init) + adc_channels = pdata->adc_init->adc_channels; + } else { + node = of_get_child_by_name(pdev->dev.of_node, "tsc"); + of_property_read_u32(node, "ti,wires", &tsc_wires); + + node = of_get_child_by_name(pdev->dev.of_node, "adc"); + of_property_read_u32(node, "ti,adc-channels", &adc_channels); + } - tsc_wires = pdata->tsc_init->wires; total_channels = tsc_wires + adc_channels; if (total_channels > 8) { @@ -285,11 +298,18 @@ static const struct dev_pm_ops tscadc_pm_ops = { #define TSCADC_PM_OPS NULL #endif +static const struct of_device_id ti_tscadc_dt_ids[] = { + { .compatible = "ti,am3359-tscadc", }, + { } +}; +MODULE_DEVICE_TABLE(of, ti_tscadc_dt_ids); + static struct platform_driver ti_tscadc_driver = { .driver = { - .name = "ti_tscadc", + .name = "ti_am3359-tscadc", .owner = THIS_MODULE, .pm = TSCADC_PM_OPS, + .of_match_table = of_match_ptr(ti_tscadc_dt_ids), }, .probe = ti_tscadc_probe, .remove = ti_tscadc_remove, -- cgit v1.2.3 From 9e5775f31289fc9915e4a2e4077950ea9a5da0b4 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 21 May 2013 17:56:49 +0200 Subject: mfd: ti_am335x_tscadc: remove platform_data support This patch removes access to platform data mfd_tscadc_board because the platform is DT only. Signed-off-by: Sebastian Andrzej Siewior --- drivers/mfd/ti_am335x_tscadc.c | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c index 292d34eaed54..e78b9df590c0 100644 --- a/drivers/mfd/ti_am335x_tscadc.c +++ b/drivers/mfd/ti_am335x_tscadc.c @@ -26,8 +26,6 @@ #include #include -#include -#include static unsigned int tscadc_readl(struct ti_tscadc_dev *tsadc, unsigned int reg) { @@ -91,31 +89,22 @@ static int ti_tscadc_probe(struct platform_device *pdev) struct ti_tscadc_dev *tscadc; struct resource *res; struct clk *clk; - struct mfd_tscadc_board *pdata = pdev->dev.platform_data; struct device_node *node = pdev->dev.of_node; struct mfd_cell *cell; int err, ctrl; int clk_value, clock_rate; int tsc_wires = 0, adc_channels = 0, total_channels; - if (!pdata && !pdev->dev.of_node) { - dev_err(&pdev->dev, "Could not find platform data\n"); + if (!pdev->dev.of_node) { + dev_err(&pdev->dev, "Could not find valid DT data.\n"); return -EINVAL; } - if (pdev->dev.platform_data) { - if (pdata->tsc_init) - tsc_wires = pdata->tsc_init->wires; + node = of_get_child_by_name(pdev->dev.of_node, "tsc"); + of_property_read_u32(node, "ti,wires", &tsc_wires); - if (pdata->adc_init) - adc_channels = pdata->adc_init->adc_channels; - } else { - node = of_get_child_by_name(pdev->dev.of_node, "tsc"); - of_property_read_u32(node, "ti,wires", &tsc_wires); - - node = of_get_child_by_name(pdev->dev.of_node, "adc"); - of_property_read_u32(node, "ti,adc-channels", &adc_channels); - } + node = of_get_child_by_name(pdev->dev.of_node, "adc"); + of_property_read_u32(node, "ti,adc-channels", &adc_channels); total_channels = tsc_wires + adc_channels; -- cgit v1.2.3 From c80df483f61d0464224dc4386ced470c7275d78f Mon Sep 17 00:00:00 2001 From: Pantelis Antoniou Date: Sat, 13 Oct 2012 16:37:24 +0300 Subject: iio: ti_tscadc: provide datasheet_name and scan_type This patch provides the members "datasheet_name" and scan_type. This is the remaining part of the earlier patch where I (bigeasy) removed iio_map because it is now supplied by the device tree. It also static names as suggested by Jonathan. Acked-by: Jonathan Cameron Signed-off-by: Pantelis Antoniou Signed-off-by: Felipe Balbi Signed-off-by: Sebastian Andrzej Siewior --- drivers/iio/adc/ti_am335x_adc.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c index 2868c0c1b7e0..9939810954f1 100644 --- a/drivers/iio/adc/ti_am335x_adc.c +++ b/drivers/iio/adc/ti_am335x_adc.c @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include @@ -84,29 +86,46 @@ static void tiadc_step_config(struct tiadc_device *adc_dev) am335x_tsc_se_set(adc_dev->mfd_tscadc, step_en); } +static const char * const chan_name_ain[] = { + "AIN0", + "AIN1", + "AIN2", + "AIN3", + "AIN4", + "AIN5", + "AIN6", + "AIN7", +}; + static int tiadc_channel_init(struct iio_dev *indio_dev, int channels) { + struct tiadc_device *adc_dev = iio_priv(indio_dev); struct iio_chan_spec *chan_array; + struct iio_chan_spec *chan; int i; indio_dev->num_channels = channels; - chan_array = kcalloc(indio_dev->num_channels, + chan_array = kcalloc(channels, sizeof(struct iio_chan_spec), GFP_KERNEL); - if (chan_array == NULL) return -ENOMEM; - for (i = 0; i < (indio_dev->num_channels); i++) { - struct iio_chan_spec *chan = chan_array + i; + chan = chan_array; + for (i = 0; i < channels; i++, chan++) { + chan->type = IIO_VOLTAGE; chan->indexed = 1; chan->channel = i; chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); + chan->datasheet_name = chan_name_ain[i]; + chan->scan_type.sign = 'u'; + chan->scan_type.realbits = 12; + chan->scan_type.storagebits = 32; } indio_dev->channels = chan_array; - return indio_dev->num_channels; + return 0; } static void tiadc_channels_remove(struct iio_dev *indio_dev) -- cgit v1.2.3 From 24d5c82f8227d4dedf177df3f062eb35db15aaf6 Mon Sep 17 00:00:00 2001 From: Pantelis Antoniou Date: Sat, 13 Oct 2012 16:37:24 +0300 Subject: mfd: ti_tscadc: deal with partial activation Fix the mfd device in the case where a subdevice might not be activated. Signed-off-by: Pantelis Antoniou Signed-off-by: Felipe Balbi Signed-off-by: Sebastian Andrzej Siewior --- drivers/mfd/ti_am335x_tscadc.c | 38 ++++++++++++++++++++++++------------ include/linux/mfd/ti_am335x_tscadc.h | 8 +++----- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c index e78b9df590c0..d05fcba6f13a 100644 --- a/drivers/mfd/ti_am335x_tscadc.c +++ b/drivers/mfd/ti_am335x_tscadc.c @@ -107,11 +107,14 @@ static int ti_tscadc_probe(struct platform_device *pdev) of_property_read_u32(node, "ti,adc-channels", &adc_channels); total_channels = tsc_wires + adc_channels; - if (total_channels > 8) { dev_err(&pdev->dev, "Number of i/p channels more than 8\n"); return -EINVAL; } + if (total_channels == 0) { + dev_err(&pdev->dev, "Need atleast one channel.\n"); + return -EINVAL; + } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { @@ -202,28 +205,37 @@ static int ti_tscadc_probe(struct platform_device *pdev) ctrl |= CNTRLREG_TSCSSENB; tscadc_writel(tscadc, REG_CTRL, ctrl); + tscadc->used_cells = 0; + tscadc->tsc_cell = -1; + tscadc->adc_cell = -1; + /* TSC Cell */ - cell = &tscadc->cells[TSC_CELL]; - cell->name = "tsc"; - cell->of_compatible = "ti,am3359-tsc"; - cell->platform_data = &tscadc; - cell->pdata_size = sizeof(tscadc); + if (tsc_wires > 0) { + tscadc->tsc_cell = tscadc->used_cells; + cell = &tscadc->cells[tscadc->used_cells++]; + cell->name = "tsc"; + cell->of_compatible = "ti,am3359-tsc"; + cell->platform_data = &tscadc; + cell->pdata_size = sizeof(tscadc); + } /* ADC Cell */ - cell = &tscadc->cells[ADC_CELL]; - cell->name = "tiadc"; - cell->of_compatible = "ti,am3359-adc"; - cell->platform_data = &tscadc; - cell->pdata_size = sizeof(tscadc); + if (adc_channels > 0) { + tscadc->adc_cell = tscadc->used_cells; + cell = &tscadc->cells[tscadc->used_cells++]; + cell->name = "tiadc"; + cell->of_compatible = "ti,am3359-adc"; + cell->platform_data = &tscadc; + cell->pdata_size = sizeof(tscadc); + } err = mfd_add_devices(&pdev->dev, pdev->id, tscadc->cells, - TSCADC_CELLS, NULL, 0, NULL); + tscadc->used_cells, NULL, 0, NULL); if (err < 0) goto err_disable_clk; device_init_wakeup(&pdev->dev, true); platform_set_drvdata(pdev, tscadc); - return 0; err_disable_clk: diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h index e36ae4184917..fe54ba4a3b2f 100644 --- a/include/linux/mfd/ti_am335x_tscadc.h +++ b/include/linux/mfd/ti_am335x_tscadc.h @@ -120,11 +120,6 @@ #define TSCADC_CELLS 2 -enum tscadc_cells { - TSC_CELL, - ADC_CELL, -}; - struct mfd_tscadc_board { struct tsc_data *tsc_init; struct adc_data *adc_init; @@ -135,6 +130,9 @@ struct ti_tscadc_dev { struct regmap *regmap_tscadc; void __iomem *tscadc_base; int irq; + int used_cells; /* 1-2 */ + int tsc_cell; /* -1 if not used */ + int adc_cell; /* -1 if not used */ struct mfd_cell cells[TSCADC_CELLS]; u32 reg_se_cache; spinlock_t reg_lock; -- cgit v1.2.3 From a82279dd6d3e500fea457f1cfea46a6b7ecd7e1f Mon Sep 17 00:00:00 2001 From: "Patil, Rachna" Date: Thu, 24 Jan 2013 03:45:12 +0000 Subject: arm: am33xx: add TSC/ADC mfd device support Add support for core multifunctional device along with its clients touchscreen and ADC. Signed-off-by: Pantelis Antoniou Signed-off-by: Patil, Rachna Signed-off-by: Felipe Balbi Signed-off-by: Sebastian Andrzej Siewior --- arch/arm/boot/dts/am335x-evm.dts | 14 ++++++++++++++ arch/arm/boot/dts/am33xx.dtsi | 18 ++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/arch/arm/boot/dts/am335x-evm.dts b/arch/arm/boot/dts/am335x-evm.dts index 0423298a26fe..26fea97dd6e0 100644 --- a/arch/arm/boot/dts/am335x-evm.dts +++ b/arch/arm/boot/dts/am335x-evm.dts @@ -244,3 +244,17 @@ &cpsw_emac1 { phy_id = <&davinci_mdio>, <1>; }; + +&tscadc { + status = "okay"; + tsc { + ti,wires = <4>; + ti,x-plate-resistance = <200>; + ti,coordiante-readouts = <5>; + ti,wire-config = <0x00 0x11 0x22 0x33>; + }; + + adc { + ti,adc-channels = <4>; + }; +}; diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi index 1460d9b88adf..4ad7797b97b6 100644 --- a/arch/arm/boot/dts/am33xx.dtsi +++ b/arch/arm/boot/dts/am33xx.dtsi @@ -404,6 +404,24 @@ ti,hwmods = "wkup_m3"; }; + tscadc: tscadc@44e0d000 { + compatible = "ti,am3359-tscadc"; + reg = <0x44e0d000 0x1000>; + interrupt-parent = <&intc>; + interrupts = <16>; + ti,hwmods = "adc_tsc"; + status = "disabled"; + + tsc { + compatible = "ti,am3359-tsc"; + }; + am335x_adc: adc { + #io-channel-cells = <1>; + compatible = "ti,am3359-adc"; + }; + + }; + gpmc: gpmc@50000000 { compatible = "ti,am3352-gpmc"; ti,hwmods = "gpmc"; -- cgit v1.2.3 From a3e509bb328287beba05017037e505bc53b62724 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 21 May 2013 18:49:58 +0200 Subject: input: mfd: ti_am335x_tsc remove remaining platform data pieces The two header files removed here are unused and have no users as this platform was never used with platform devices. Signed-off-by: Sebastian Andrzej Siewior --- include/linux/input/ti_am335x_tsc.h | 35 ----------------------------- include/linux/mfd/ti_am335x_tscadc.h | 5 ----- include/linux/platform_data/ti_am335x_adc.h | 14 ------------ 3 files changed, 54 deletions(-) delete mode 100644 include/linux/input/ti_am335x_tsc.h delete mode 100644 include/linux/platform_data/ti_am335x_adc.h diff --git a/include/linux/input/ti_am335x_tsc.h b/include/linux/input/ti_am335x_tsc.h deleted file mode 100644 index 6a66b4d1ac2c..000000000000 --- a/include/linux/input/ti_am335x_tsc.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef __LINUX_TI_AM335X_TSC_H -#define __LINUX_TI_AM335X_TSC_H - -/** - * struct tsc_data Touchscreen wire configuration - * @wires: Wires refer to application modes - * i.e. 4/5/8 wire touchscreen support - * on the platform. - * @x_plate_resistance: X plate resistance. - * @steps_to_configure: The sequencer supports a total of - * 16 programmable steps. - * A step configured to read a single - * co-ordinate value, can be applied - * more number of times for better results. - * @wire_config: Different EVM's could have a different order - * for connecting wires on touchscreen. - * We need to provide an 8 bit number where in - * the 1st four bits represent the analog lines - * and the next 4 bits represent positive/ - * negative terminal on that input line. - * Notations to represent the input lines and - * terminals resoectively is as follows: - * AIN0 = 0, AIN1 = 1 and so on till AIN7 = 7. - * XP = 0, XN = 1, YP = 2, YN = 3. - * - */ - -struct tsc_data { - int wires; - int x_plate_resistance; - int steps_to_configure; - int wire_config[10]; -}; - -#endif diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h index fe54ba4a3b2f..533f200e6d0e 100644 --- a/include/linux/mfd/ti_am335x_tscadc.h +++ b/include/linux/mfd/ti_am335x_tscadc.h @@ -120,11 +120,6 @@ #define TSCADC_CELLS 2 -struct mfd_tscadc_board { - struct tsc_data *tsc_init; - struct adc_data *adc_init; -}; - struct ti_tscadc_dev { struct device *dev; struct regmap *regmap_tscadc; diff --git a/include/linux/platform_data/ti_am335x_adc.h b/include/linux/platform_data/ti_am335x_adc.h deleted file mode 100644 index e41d5834cb84..000000000000 --- a/include/linux/platform_data/ti_am335x_adc.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef __LINUX_TI_AM335X_ADC_H -#define __LINUX_TI_AM335X_ADC_H - -/** - * struct adc_data ADC Input information - * @adc_channels: Number of analog inputs - * available for ADC. - */ - -struct adc_data { - unsigned int adc_channels; -}; - -#endif -- cgit v1.2.3 From 5f184e63c61f92ab499273e682bb8898e88209a8 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 27 May 2013 17:08:28 +0200 Subject: mfd: input: ti_am335x_tsc: rename device from tsc to TI-am335x-tsc tsc is a very generic name. This patch adds a TI and HW prefix to it less generic. Signed-off-by: Sebastian Andrzej Siewior --- drivers/input/touchscreen/ti_am335x_tsc.c | 2 +- drivers/mfd/ti_am335x_tscadc.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c index a1db55d1a862..ff3215ddf9f5 100644 --- a/drivers/input/touchscreen/ti_am335x_tsc.c +++ b/drivers/input/touchscreen/ti_am335x_tsc.c @@ -491,7 +491,7 @@ static struct platform_driver ti_tsc_driver = { .probe = titsc_probe, .remove = titsc_remove, .driver = { - .name = "tsc", + .name = "TI-am335x-tsc", .owner = THIS_MODULE, .pm = TITSC_PM_OPS, .of_match_table = of_match_ptr(ti_tsc_dt_ids), diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c index d05fcba6f13a..5fb8b1dace8e 100644 --- a/drivers/mfd/ti_am335x_tscadc.c +++ b/drivers/mfd/ti_am335x_tscadc.c @@ -213,7 +213,7 @@ static int ti_tscadc_probe(struct platform_device *pdev) if (tsc_wires > 0) { tscadc->tsc_cell = tscadc->used_cells; cell = &tscadc->cells[tscadc->used_cells++]; - cell->name = "tsc"; + cell->name = "TI-am335x-tsc"; cell->of_compatible = "ti,am3359-tsc"; cell->platform_data = &tscadc; cell->pdata_size = sizeof(tscadc); -- cgit v1.2.3 From 9f99928fe0a03dd2ba5894b7bb942cc50b5d7c5e Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 27 May 2013 17:12:52 +0200 Subject: mfd: iio: ti_am335x_adc: rename device from tiadc to TI-am335x-adc TI-adc reads a little better compared to tiadc. And if we add am335x to it then we have the same naming scheme as the tsc side. Acked-by: Jonathan Cameron Signed-off-by: Sebastian Andrzej Siewior --- drivers/iio/adc/ti_am335x_adc.c | 3 +-- drivers/mfd/ti_am335x_tscadc.c | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c index 9939810954f1..4bec91e40bf7 100644 --- a/drivers/iio/adc/ti_am335x_adc.c +++ b/drivers/iio/adc/ti_am335x_adc.c @@ -292,7 +292,7 @@ MODULE_DEVICE_TABLE(of, ti_adc_dt_ids); static struct platform_driver tiadc_driver = { .driver = { - .name = "tiadc", + .name = "TI-am335x-adc", .owner = THIS_MODULE, .pm = TIADC_PM_OPS, .of_match_table = of_match_ptr(ti_adc_dt_ids), @@ -300,7 +300,6 @@ static struct platform_driver tiadc_driver = { .probe = tiadc_probe, .remove = tiadc_remove, }; - module_platform_driver(tiadc_driver); MODULE_DESCRIPTION("TI ADC controller driver"); diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c index 5fb8b1dace8e..253233915009 100644 --- a/drivers/mfd/ti_am335x_tscadc.c +++ b/drivers/mfd/ti_am335x_tscadc.c @@ -223,7 +223,7 @@ static int ti_tscadc_probe(struct platform_device *pdev) if (adc_channels > 0) { tscadc->adc_cell = tscadc->used_cells; cell = &tscadc->cells[tscadc->used_cells++]; - cell->name = "tiadc"; + cell->name = "TI-am335x-adc"; cell->of_compatible = "ti,am3359-adc"; cell->platform_data = &tscadc; cell->pdata_size = sizeof(tscadc); -- cgit v1.2.3 From 8c896308feae7fb2e8da4ae4c09fe2d2ca18ad7b Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 29 May 2013 14:46:21 +0200 Subject: input: ti_am335x_adc: use only FIFO0 and clean up a little The driver programs a threshold of "coordinate_readouts" say 5. The REG_FIFO0THR registers says it should it be programmed to "threshold minus one". The driver does not expect just 5 coordinates but 5 * 2 + 2. Multiplied by two because 5 for X and 5 for Y and plus 2 because we have two Z. The whole thing kind of works because It reads the 5 coordinates for X and Y from FIFO0 and FIFO1 and the last element in each FIFO is ignored within the loop and read later. Nothing guaranties that FIFO1 is ready by the time it is read. In fact I could see that that FIFO1 reaturns for Y channels 8,9, 10, 12, 6 and for Y channel 7 for Z. The problem is that channel 7 and channel 12 got somehow mixed up. The other Problem is that FIFO1 is also used by the IIO part leading to wrong results if both (tsc & adc) are used. The patch tries to clean up the whole thing a little: - Remove the +1 and -1 in REG_STEPCONFIG, REG_STEPDELAY and its counter part in the for loop. This is just confusing. - Use only FIFO0 in TSC. The fifo has space for 64 entries so should be fine. - Read the whole FIFO in one function and check the channel. - in case we dawdle around, make sure we only read a multiple of our coordinate set. On the second interrupt we will cleanup the remaining enties. Acked-by: Dmitry Torokhov Signed-off-by: Sebastian Andrzej Siewior --- drivers/iio/adc/ti_am335x_adc.c | 2 +- drivers/input/touchscreen/ti_am335x_tsc.c | 78 ++++++++++++++++--------------- include/linux/mfd/ti_am335x_tscadc.h | 4 +- 3 files changed, 44 insertions(+), 40 deletions(-) diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c index 4bec91e40bf7..307a7c07be47 100644 --- a/drivers/iio/adc/ti_am335x_adc.c +++ b/drivers/iio/adc/ti_am335x_adc.c @@ -75,7 +75,7 @@ static void tiadc_step_config(struct tiadc_device *adc_dev) stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1; - for (i = (steps + 1); i <= TOTAL_STEPS; i++) { + for (i = steps; i < TOTAL_STEPS; i++) { tiadc_writel(adc_dev, REG_STEPCONFIG(i), stepconfig | STEPCONFIG_INP(channels)); tiadc_writel(adc_dev, REG_STEPDELAY(i), diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c index ff3215ddf9f5..1bceb2591fc7 100644 --- a/drivers/input/touchscreen/ti_am335x_tsc.c +++ b/drivers/input/touchscreen/ti_am335x_tsc.c @@ -120,11 +120,9 @@ static int titsc_config_wires(struct titsc *ts_dev) static void titsc_step_config(struct titsc *ts_dev) { unsigned int config; - unsigned int stepenable = 0; - int i, total_steps; - - /* Configure the Step registers */ - total_steps = 2 * ts_dev->coordinate_readouts; + int i; + int end_step; + u32 stepenable; config = STEPCONFIG_MODE_HWSYNC | STEPCONFIG_AVG_16 | ts_dev->bit_xp; @@ -142,7 +140,9 @@ static void titsc_step_config(struct titsc *ts_dev) break; } - for (i = 1; i <= ts_dev->coordinate_readouts; i++) { + /* 1 … coordinate_readouts is for X */ + end_step = ts_dev->coordinate_readouts; + for (i = 0; i < end_step; i++) { titsc_writel(ts_dev, REG_STEPCONFIG(i), config); titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); } @@ -150,7 +150,7 @@ static void titsc_step_config(struct titsc *ts_dev) config = 0; config = STEPCONFIG_MODE_HWSYNC | STEPCONFIG_AVG_16 | ts_dev->bit_yn | - STEPCONFIG_INM_ADCREFM | STEPCONFIG_FIFO1; + STEPCONFIG_INM_ADCREFM; switch (ts_dev->wires) { case 4: config |= ts_dev->bit_yp | STEPCONFIG_INP(ts_dev->inp_xp); @@ -164,12 +164,13 @@ static void titsc_step_config(struct titsc *ts_dev) break; } - for (i = (ts_dev->coordinate_readouts + 1); i <= total_steps; i++) { + /* coordinate_readouts … coordinate_readouts * 2 is for Y */ + end_step = ts_dev->coordinate_readouts * 2; + for (i = ts_dev->coordinate_readouts; i < end_step; i++) { titsc_writel(ts_dev, REG_STEPCONFIG(i), config); titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); } - config = 0; /* Charge step configuration */ config = ts_dev->bit_xp | ts_dev->bit_yn | STEPCHARGE_RFP_XPUL | STEPCHARGE_RFM_XNUR | @@ -178,35 +179,39 @@ static void titsc_step_config(struct titsc *ts_dev) titsc_writel(ts_dev, REG_CHARGECONFIG, config); titsc_writel(ts_dev, REG_CHARGEDELAY, CHARGEDLY_OPENDLY); - config = 0; - /* Configure to calculate pressure */ + /* coordinate_readouts * 2 … coordinate_readouts * 2 + 2 is for Z */ config = STEPCONFIG_MODE_HWSYNC | STEPCONFIG_AVG_16 | ts_dev->bit_yp | ts_dev->bit_xn | STEPCONFIG_INM_ADCREFM | STEPCONFIG_INP(ts_dev->inp_xp); - titsc_writel(ts_dev, REG_STEPCONFIG(total_steps + 1), config); - titsc_writel(ts_dev, REG_STEPDELAY(total_steps + 1), + titsc_writel(ts_dev, REG_STEPCONFIG(end_step), config); + titsc_writel(ts_dev, REG_STEPDELAY(end_step), STEPCONFIG_OPENDLY); - config |= STEPCONFIG_INP(ts_dev->inp_yn) | STEPCONFIG_FIFO1; - titsc_writel(ts_dev, REG_STEPCONFIG(total_steps + 2), config); - titsc_writel(ts_dev, REG_STEPDELAY(total_steps + 2), + end_step++; + config |= STEPCONFIG_INP(ts_dev->inp_yn); + titsc_writel(ts_dev, REG_STEPCONFIG(end_step), config); + titsc_writel(ts_dev, REG_STEPDELAY(end_step), STEPCONFIG_OPENDLY); /* The steps1 … end and bit 0 for TS_Charge */ - stepenable = (1 << (total_steps + 2)) - 1; + stepenable = (1 << (end_step + 2)) - 1; am335x_tsc_se_set(ts_dev->mfd_tscadc, stepenable); } static void titsc_read_coordinates(struct titsc *ts_dev, - unsigned int *x, unsigned int *y) + u32 *x, u32 *y, u32 *z1, u32 *z2) { unsigned int fifocount = titsc_readl(ts_dev, REG_FIFO0CNT); unsigned int prev_val_x = ~0, prev_val_y = ~0; unsigned int prev_diff_x = ~0, prev_diff_y = ~0; unsigned int read, diff; unsigned int i, channel; + unsigned int creads = ts_dev->coordinate_readouts; + *z1 = *z2 = 0; + if (fifocount % (creads * 2 + 2)) + fifocount -= fifocount % (creads * 2 + 2); /* * Delta filter is used to remove large variations in sampled * values from ADC. The filter tries to predict where the next @@ -215,32 +220,32 @@ static void titsc_read_coordinates(struct titsc *ts_dev, * algorithm compares the difference with that of a present value, * if true the value is reported to the sub system. */ - for (i = 0; i < fifocount - 1; i++) { + for (i = 0; i < fifocount; i++) { read = titsc_readl(ts_dev, REG_FIFO0); - channel = read & 0xf0000; - channel = channel >> 0x10; - if ((channel >= 0) && (channel < ts_dev->coordinate_readouts)) { - read &= 0xfff; + + channel = (read & 0xf0000) >> 16; + read &= 0xfff; + if (channel < creads) { diff = abs(read - prev_val_x); if (diff < prev_diff_x) { prev_diff_x = diff; *x = read; } prev_val_x = read; - } - read = titsc_readl(ts_dev, REG_FIFO1); - channel = read & 0xf0000; - channel = channel >> 0x10; - if ((channel >= ts_dev->coordinate_readouts) && - (channel < (2 * ts_dev->coordinate_readouts - 1))) { - read &= 0xfff; + } else if (channel < creads * 2) { diff = abs(read - prev_val_y); if (diff < prev_diff_y) { prev_diff_y = diff; *y = read; } prev_val_y = read; + + } else if (channel < creads * 2 + 1) { + *z1 = read; + + } else if (channel < creads * 2 + 2) { + *z2 = read; } } } @@ -256,10 +261,8 @@ static irqreturn_t titsc_irq(int irq, void *dev) status = titsc_readl(ts_dev, REG_IRQSTATUS); if (status & IRQENB_FIFO0THRES) { - titsc_read_coordinates(ts_dev, &x, &y); - z1 = titsc_readl(ts_dev, REG_FIFO0) & 0xfff; - z2 = titsc_readl(ts_dev, REG_FIFO1) & 0xfff; + titsc_read_coordinates(ts_dev, &x, &y, &z1, &z2); if (ts_dev->pen_down && z1 != 0 && z2 != 0) { /* @@ -267,10 +270,10 @@ static irqreturn_t titsc_irq(int irq, void *dev) * Resistance(touch) = x plate resistance * * x postion/4096 * ((z2 / z1) - 1) */ - z = z2 - z1; + z = z1 - z2; z *= x; z *= ts_dev->x_plate_resistance; - z /= z1; + z /= z2; z = (z + 2047) >> 12; if (z <= MAX_12BIT) { @@ -391,7 +394,8 @@ static int titsc_probe(struct platform_device *pdev) goto err_free_irq; } titsc_step_config(ts_dev); - titsc_writel(ts_dev, REG_FIFO0THR, ts_dev->coordinate_readouts); + titsc_writel(ts_dev, REG_FIFO0THR, + ts_dev->coordinate_readouts * 2 + 2 - 1); input_dev->name = "ti-tsc"; input_dev->dev.parent = &pdev->dev; @@ -468,7 +472,7 @@ static int titsc_resume(struct device *dev) } titsc_step_config(ts_dev); titsc_writel(ts_dev, REG_FIFO0THR, - ts_dev->coordinate_readouts); + ts_dev->coordinate_readouts * 2 + 2 - 1); return 0; } diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h index 533f200e6d0e..8d73fe29796a 100644 --- a/include/linux/mfd/ti_am335x_tscadc.h +++ b/include/linux/mfd/ti_am335x_tscadc.h @@ -30,8 +30,8 @@ #define REG_IDLECONFIG 0x058 #define REG_CHARGECONFIG 0x05C #define REG_CHARGEDELAY 0x060 -#define REG_STEPCONFIG(n) (0x64 + ((n - 1) * 8)) -#define REG_STEPDELAY(n) (0x68 + ((n - 1) * 8)) +#define REG_STEPCONFIG(n) (0x64 + ((n) * 8)) +#define REG_STEPDELAY(n) (0x68 + ((n) * 8)) #define REG_FIFO0CNT 0xE4 #define REG_FIFO0THR 0xE8 #define REG_FIFO1CNT 0xF0 -- cgit v1.2.3 From 00789e5deb0af08826bd0c602d21baa9016b54b5 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 5 Jun 2013 16:23:18 +0200 Subject: input: ti_am335x_tsc: ACK the HW_PEN irq in ISR The interrupt source IRQENB_HW_PEN is enabled in suspend and suposed to be used as a wake up source. Once this interrupt source is unmaksed, the devices ends up in ISR and never continues. This change ACKs the interrupt and disables it so the system does not freeze. Signed-off-by: Sebastian Andrzej Siewior --- drivers/input/touchscreen/ti_am335x_tsc.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c index 1bceb2591fc7..2ba77039ab91 100644 --- a/drivers/input/touchscreen/ti_am335x_tsc.c +++ b/drivers/input/touchscreen/ti_am335x_tsc.c @@ -308,6 +308,12 @@ static irqreturn_t titsc_irq(int irq, void *dev) irqclr |= IRQENB_PENUP; } + if (status & IRQENB_HW_PEN) { + + titsc_writel(ts_dev, REG_IRQWAKEUP, 0x00); + titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN); + } + titsc_writel(ts_dev, REG_IRQSTATUS, irqclr); am335x_tsc_se_update(ts_dev->mfd_tscadc); -- cgit v1.2.3 From 9a28b8834c55f7315fb1a7c487f836472fd37bf9 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 5 Jun 2013 16:30:00 +0200 Subject: input: ti_am335x_tsc: return IRQ_NONE if there was no IRQ for us The previous patch ("input/ti_am335x_tsc: ACK the HW_PEN irq in ISR") acked the interrupt so we don't freeze if we don't handle an enabled interrupt source. The interrupt core has a mechanism for this and to get it work one should only say that it handled an interrupt if it is actually the case. Signed-off-by: Sebastian Andrzej Siewior --- drivers/input/touchscreen/ti_am335x_tsc.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c index 2ba77039ab91..0e9f02aeae6b 100644 --- a/drivers/input/touchscreen/ti_am335x_tsc.c +++ b/drivers/input/touchscreen/ti_am335x_tsc.c @@ -314,10 +314,12 @@ static irqreturn_t titsc_irq(int irq, void *dev) titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN); } - titsc_writel(ts_dev, REG_IRQSTATUS, irqclr); - - am335x_tsc_se_update(ts_dev->mfd_tscadc); - return IRQ_HANDLED; + if (irqclr) { + titsc_writel(ts_dev, REG_IRQSTATUS, irqclr); + am335x_tsc_se_update(ts_dev->mfd_tscadc); + return IRQ_HANDLED; + } + return IRQ_NONE; } static int titsc_parse_dt(struct platform_device *pdev, -- cgit v1.2.3 From 18926edebcb82ca325abf843293801d4ff43436a Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 29 May 2013 17:39:02 +0200 Subject: iio: ti_am335x_adc: Allow to specify input line The TSC part allows to specify the input lines. The IIO part assumes that it usues always the last few, that means if IIO has adc-channels set to 2 it will use channel 6 and 7. However it might make sense to use only 6. This patch changes the device property (which was introduced recently and was never in an official release) in a way that the user can specify which of the AIN lines should be used. In Addition to this, the name is now AINx where x is the channel number i.e. for AIN6 we would have 6. Prior this, it always started counting at 0 which is confusing. In addition to this, it also checks for correct step number during reading and does not rely on proper FIFO depth. Acked-by: Jonathan Cameron Signed-off-by: Sebastian Andrzej Siewior --- arch/arm/boot/dts/am335x-evm.dts | 2 +- drivers/iio/adc/ti_am335x_adc.c | 57 ++++++++++++++++++++++++++-------------- drivers/mfd/ti_am335x_tscadc.c | 20 ++++++++++++-- 3 files changed, 56 insertions(+), 23 deletions(-) diff --git a/arch/arm/boot/dts/am335x-evm.dts b/arch/arm/boot/dts/am335x-evm.dts index 26fea97dd6e0..0fa4c7f9539f 100644 --- a/arch/arm/boot/dts/am335x-evm.dts +++ b/arch/arm/boot/dts/am335x-evm.dts @@ -255,6 +255,6 @@ }; adc { - ti,adc-channels = <4>; + ti,adc-channels = <4 5 6 7>; }; }; diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c index 307a7c07be47..8ffe52d58829 100644 --- a/drivers/iio/adc/ti_am335x_adc.c +++ b/drivers/iio/adc/ti_am335x_adc.c @@ -32,6 +32,8 @@ struct tiadc_device { struct ti_tscadc_dev *mfd_tscadc; int channels; + u8 channel_line[8]; + u8 channel_step[8]; }; static unsigned int tiadc_readl(struct tiadc_device *adc, unsigned int reg) @@ -57,7 +59,7 @@ static u32 get_adc_step_mask(struct tiadc_device *adc_dev) static void tiadc_step_config(struct tiadc_device *adc_dev) { unsigned int stepconfig; - int i, channels = 0, steps; + int i, steps; u32 step_en; /* @@ -71,16 +73,18 @@ static void tiadc_step_config(struct tiadc_device *adc_dev) */ steps = TOTAL_STEPS - adc_dev->channels; - channels = TOTAL_CHANNELS - adc_dev->channels; - stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1; - for (i = steps; i < TOTAL_STEPS; i++) { - tiadc_writel(adc_dev, REG_STEPCONFIG(i), - stepconfig | STEPCONFIG_INP(channels)); - tiadc_writel(adc_dev, REG_STEPDELAY(i), + for (i = 0; i < adc_dev->channels; i++) { + int chan; + + chan = adc_dev->channel_line[i]; + tiadc_writel(adc_dev, REG_STEPCONFIG(steps), + stepconfig | STEPCONFIG_INP(chan)); + tiadc_writel(adc_dev, REG_STEPDELAY(steps), STEPCONFIG_OPENDLY); - channels++; + adc_dev->channel_step[i] = steps; + steps++; } step_en = get_adc_step_mask(adc_dev); am335x_tsc_se_set(adc_dev->mfd_tscadc, step_en); @@ -115,9 +119,9 @@ static int tiadc_channel_init(struct iio_dev *indio_dev, int channels) chan->type = IIO_VOLTAGE; chan->indexed = 1; - chan->channel = i; + chan->channel = adc_dev->channel_line[i]; chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); - chan->datasheet_name = chan_name_ain[i]; + chan->datasheet_name = chan_name_ain[chan->channel]; chan->scan_type.sign = 'u'; chan->scan_type.realbits = 12; chan->scan_type.storagebits = 32; @@ -139,7 +143,8 @@ static int tiadc_read_raw(struct iio_dev *indio_dev, { struct tiadc_device *adc_dev = iio_priv(indio_dev); int i; - unsigned int fifo1count, readx1; + unsigned int fifo1count, read; + u32 step = UINT_MAX; /* * When the sub-system is first enabled, @@ -152,11 +157,20 @@ static int tiadc_read_raw(struct iio_dev *indio_dev, * Hence we need to flush out this data. */ + for (i = 0; i < ARRAY_SIZE(adc_dev->channel_step); i++) { + if (chan->channel == adc_dev->channel_line[i]) { + step = adc_dev->channel_step[i]; + break; + } + } + if (WARN_ON_ONCE(step == UINT_MAX)) + return -EINVAL; + fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT); for (i = 0; i < fifo1count; i++) { - readx1 = tiadc_readl(adc_dev, REG_FIFO1); - if (i == chan->channel) - *val = readx1 & 0xfff; + read = tiadc_readl(adc_dev, REG_FIFO1); + if (read >> 16 == step) + *val = read & 0xfff; } am335x_tsc_se_update(adc_dev->mfd_tscadc); @@ -172,8 +186,11 @@ static int tiadc_probe(struct platform_device *pdev) struct iio_dev *indio_dev; struct tiadc_device *adc_dev; struct device_node *node = pdev->dev.of_node; + struct property *prop; + const __be32 *cur; int err; - u32 val32; + u32 val; + int channels = 0; if (!node) { dev_err(&pdev->dev, "Could not find valid DT data.\n"); @@ -190,11 +207,11 @@ static int tiadc_probe(struct platform_device *pdev) adc_dev->mfd_tscadc = ti_tscadc_dev_get(pdev); - err = of_property_read_u32(node, - "ti,adc-channels", &val32); - if (err < 0) - goto err_free_device; - adc_dev->channels = val32; + of_property_for_each_u32(node, "ti,adc-channels", prop, cur, val) { + adc_dev->channel_line[channels] = val; + channels++; + } + adc_dev->channels = channels; indio_dev->dev.parent = &pdev->dev; indio_dev->name = dev_name(&pdev->dev); diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c index 253233915009..b003a16ba227 100644 --- a/drivers/mfd/ti_am335x_tscadc.c +++ b/drivers/mfd/ti_am335x_tscadc.c @@ -91,9 +91,13 @@ static int ti_tscadc_probe(struct platform_device *pdev) struct clk *clk; struct device_node *node = pdev->dev.of_node; struct mfd_cell *cell; + struct property *prop; + const __be32 *cur; + u32 val; int err, ctrl; int clk_value, clock_rate; int tsc_wires = 0, adc_channels = 0, total_channels; + int readouts = 0; if (!pdev->dev.of_node) { dev_err(&pdev->dev, "Could not find valid DT data.\n"); @@ -102,10 +106,17 @@ static int ti_tscadc_probe(struct platform_device *pdev) node = of_get_child_by_name(pdev->dev.of_node, "tsc"); of_property_read_u32(node, "ti,wires", &tsc_wires); + of_property_read_u32(node, "ti,coordiante-readouts", &readouts); node = of_get_child_by_name(pdev->dev.of_node, "adc"); - of_property_read_u32(node, "ti,adc-channels", &adc_channels); - + of_property_for_each_u32(node, "ti,adc-channels", prop, cur, val) { + adc_channels++; + if (val > 7) { + dev_err(&pdev->dev, " PIN numbers are 0..7 (not %d)\n", + val); + return -EINVAL; + } + } total_channels = tsc_wires + adc_channels; if (total_channels > 8) { dev_err(&pdev->dev, "Number of i/p channels more than 8\n"); @@ -116,6 +127,11 @@ static int ti_tscadc_probe(struct platform_device *pdev) return -EINVAL; } + if (readouts * 2 + 2 + adc_channels > 16) { + dev_err(&pdev->dev, "Too many step configurations requested\n"); + return -EINVAL; + } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&pdev->dev, "no memory resource defined.\n"); -- cgit v1.2.3 From 1460c152c53335b5403045d056502eda1204c33a Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 29 May 2013 18:49:55 +0200 Subject: iio: ti_am335x_adc: check if we found the value Usually we get all the values we wanted but it is possible, that te ADC unit is busy performing the conversation for the HW events. In that case -EBUSY is returned and the user may re-call the function. Acked-by: Jonathan Cameron Signed-off-by: Sebastian Andrzej Siewior --- drivers/iio/adc/ti_am335x_adc.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c index 8ffe52d58829..4427e8e46a7f 100644 --- a/drivers/iio/adc/ti_am335x_adc.c +++ b/drivers/iio/adc/ti_am335x_adc.c @@ -145,6 +145,7 @@ static int tiadc_read_raw(struct iio_dev *indio_dev, int i; unsigned int fifo1count, read; u32 step = UINT_MAX; + bool found = false; /* * When the sub-system is first enabled, @@ -169,11 +170,14 @@ static int tiadc_read_raw(struct iio_dev *indio_dev, fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT); for (i = 0; i < fifo1count; i++) { read = tiadc_readl(adc_dev, REG_FIFO1); - if (read >> 16 == step) + if (read >> 16 == step) { *val = read & 0xfff; + found = true; + } } am335x_tsc_se_update(adc_dev->mfd_tscadc); - + if (found == false) + return -EBUSY; return IIO_VAL_INT; } -- cgit v1.2.3 From 1b4d7d9787beb45a51c1ccf2f0a7fcee9213fb38 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 12 Jun 2013 17:44:07 +0100 Subject: mfd: wm5102: Expose DRE control registers Certain use cases may require specific DRE settings so expose the necessary registers. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/mfd/wm5102-tables.c | 9 ++++++- include/linux/mfd/arizona/registers.h | 44 +++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index 155c4a1a6a99..802dd3cb18cf 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -65,7 +65,8 @@ static const struct reg_default wm5102_revb_patch[] = { { 0x418, 0xa080 }, { 0x420, 0xa080 }, { 0x428, 0xe000 }, - { 0x443, 0xDC1A }, + { 0x442, 0x3F0A }, + { 0x443, 0xDC1F }, { 0x4B0, 0x0066 }, { 0x458, 0x000b }, { 0x212, 0x0000 }, @@ -424,6 +425,9 @@ static const struct reg_default wm5102_reg_default[] = { { 0x00000435, 0x0180 }, /* R1077 - DAC Digital Volume 5R */ { 0x00000436, 0x0081 }, /* R1078 - DAC Volume Limit 5R */ { 0x00000437, 0x0200 }, /* R1079 - Noise Gate Select 5R */ + { 0x00000440, 0x8FFF }, /* R1088 - DRE Enable */ + { 0x00000442, 0x3F0A }, /* R1090 - DRE Control 2 */ + { 0x00000443, 0xDC1F }, /* R1090 - DRE Control 3 */ { 0x00000450, 0x0000 }, /* R1104 - DAC AEC Control 1 */ { 0x00000458, 0x000B }, /* R1112 - Noise Gate Control */ { 0x00000490, 0x0069 }, /* R1168 - PDM SPK1 CTRL 1 */ @@ -1197,6 +1201,9 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg) case ARIZONA_DAC_DIGITAL_VOLUME_5R: case ARIZONA_DAC_VOLUME_LIMIT_5R: case ARIZONA_NOISE_GATE_SELECT_5R: + case ARIZONA_DRE_ENABLE: + case ARIZONA_DRE_CONTROL_2: + case ARIZONA_DRE_CONTROL_3: case ARIZONA_DAC_AEC_CONTROL_1: case ARIZONA_NOISE_GATE_CONTROL: case ARIZONA_PDM_SPK1_CTRL_1: diff --git a/include/linux/mfd/arizona/registers.h b/include/linux/mfd/arizona/registers.h index 4730b5c576e2..4706d3d46e56 100644 --- a/include/linux/mfd/arizona/registers.h +++ b/include/linux/mfd/arizona/registers.h @@ -215,6 +215,9 @@ #define ARIZONA_DAC_DIGITAL_VOLUME_6R 0x43D #define ARIZONA_DAC_VOLUME_LIMIT_6R 0x43E #define ARIZONA_NOISE_GATE_SELECT_6R 0x43F +#define ARIZONA_DRE_ENABLE 0x440 +#define ARIZONA_DRE_CONTROL_2 0x442 +#define ARIZONA_DRE_CONTROL_3 0x443 #define ARIZONA_DAC_AEC_CONTROL_1 0x450 #define ARIZONA_NOISE_GATE_CONTROL 0x458 #define ARIZONA_PDM_SPK1_CTRL_1 0x490 @@ -3132,6 +3135,47 @@ #define ARIZONA_OUT6R_NGATE_SRC_SHIFT 0 /* OUT6R_NGATE_SRC - [11:0] */ #define ARIZONA_OUT6R_NGATE_SRC_WIDTH 12 /* OUT6R_NGATE_SRC - [11:0] */ +/* + * R1088 (0x440) - DRE Enable + */ +#define ARIZONA_DRE3L_ENA 0x0010 /* DRE3L_ENA */ +#define ARIZONA_DRE3L_ENA_MASK 0x0010 /* DRE3L_ENA */ +#define ARIZONA_DRE3L_ENA_SHIFT 4 /* DRE3L_ENA */ +#define ARIZONA_DRE3L_ENA_WIDTH 1 /* DRE3L_ENA */ +#define ARIZONA_DRE2R_ENA 0x0008 /* DRE2R_ENA */ +#define ARIZONA_DRE2R_ENA_MASK 0x0008 /* DRE2R_ENA */ +#define ARIZONA_DRE2R_ENA_SHIFT 3 /* DRE2R_ENA */ +#define ARIZONA_DRE2R_ENA_WIDTH 1 /* DRE2R_ENA */ +#define ARIZONA_DRE2L_ENA 0x0004 /* DRE2L_ENA */ +#define ARIZONA_DRE2L_ENA_MASK 0x0004 /* DRE2L_ENA */ +#define ARIZONA_DRE2L_ENA_SHIFT 2 /* DRE2L_ENA */ +#define ARIZONA_DRE2L_ENA_WIDTH 1 /* DRE2L_ENA */ +#define ARIZONA_DRE1R_ENA 0x0002 /* DRE1R_ENA */ +#define ARIZONA_DRE1R_ENA_MASK 0x0002 /* DRE1R_ENA */ +#define ARIZONA_DRE1R_ENA_SHIFT 1 /* DRE1R_ENA */ +#define ARIZONA_DRE1R_ENA_WIDTH 1 /* DRE1R_ENA */ +#define ARIZONA_DRE1L_ENA 0x0001 /* DRE1L_ENA */ +#define ARIZONA_DRE1L_ENA_MASK 0x0001 /* DRE1L_ENA */ +#define ARIZONA_DRE1L_ENA_SHIFT 0 /* DRE1L_ENA */ +#define ARIZONA_DRE1L_ENA_WIDTH 1 /* DRE1L_ENA */ + +/* + * R1090 (0x442) - DRE Control 2 + */ +#define ARIZONA_DRE_T_LOW_MASK 0x3F00 /* DRE_T_LOW - [13:8] */ +#define ARIZONA_DRE_T_LOW_SHIFT 8 /* DRE_T_LOW - [13:8] */ +#define ARIZONA_DRE_T_LOW_WIDTH 6 /* DRE_T_LOW - [13:8] */ + +/* + * R1091 (0x443) - DRE Control 3 + */ +#define ARIZONA_DRE_GAIN_SHIFT_MASK 0xC000 /* DRE_GAIN_SHIFT - [15:14] */ +#define ARIZONA_DRE_GAIN_SHIFT_SHIFT 14 /* DRE_GAIN_SHIFT - [15:14] */ +#define ARIZONA_DRE_GAIN_SHIFT_WIDTH 2 /* DRE_GAIN_SHIFT - [15:14] */ +#define ARIZONA_DRE_LOW_LEVEL_ABS_MASK 0x000F /* LOW_LEVEL_ABS - [3:0] */ +#define ARIZONA_DRE_LOW_LEVEL_ABS_SHIFT 0 /* LOW_LEVEL_ABS - [3:0] */ +#define ARIZONA_DRE_LOW_LEVEL_ABS_WIDTH 4 /* LOW_LEVEL_ABS - [3:0] */ + /* * R1104 (0x450) - DAC AEC Control 1 */ -- cgit v1.2.3 From 7c470373e097822ce6ca7bbac44b3afec0e7c1f8 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 12 Jun 2013 17:44:08 +0100 Subject: ASoC: wm5102: Expose controls for DRE Certain use cases may require specific DRE settings so expose control of these. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- sound/soc/codecs/wm5102.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index 842adfdcb2a9..e723ac0c2113 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -816,6 +816,19 @@ SOC_VALUE_ENUM("HPOUT1 OSR", wm5102_hpout_osr[0]), SOC_VALUE_ENUM("HPOUT2 OSR", wm5102_hpout_osr[1]), SOC_VALUE_ENUM("EPOUT OSR", wm5102_hpout_osr[2]), +SOC_DOUBLE("HPOUT1 DRE Switch", ARIZONA_DRE_ENABLE, + ARIZONA_DRE1L_ENA_SHIFT, ARIZONA_DRE1R_ENA_SHIFT, 1, 0), +SOC_DOUBLE("HPOUT2 DRE Switch", ARIZONA_DRE_ENABLE, + ARIZONA_DRE2L_ENA_SHIFT, ARIZONA_DRE2R_ENA_SHIFT, 1, 0), +SOC_SINGLE("EPOUT DRE Switch", ARIZONA_DRE_ENABLE, + ARIZONA_DRE3L_ENA_SHIFT, 1, 0), + +SOC_SINGLE("DRE Threshold", ARIZONA_DRE_CONTROL_2, + ARIZONA_DRE_T_LOW_SHIFT, 63, 0), + +SOC_SINGLE("DRE Low Level ABS", ARIZONA_DRE_CONTROL_3, + ARIZONA_DRE_LOW_LEVEL_ABS_SHIFT, 15, 0), + SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp), SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp), -- cgit v1.2.3 From 5e3dd157d7e70f0e3cea3f2573ed69fb156a19d5 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 12 Jun 2013 20:52:10 +0300 Subject: ath10k: mac80211 driver for Qualcomm Atheros 802.11ac CQA98xx devices Here's a new mac80211 driver for Qualcomm Atheros 802.11ac QCA98xx devices. A major difference from ath9k is that there's now a firmware and that's why we had to implement a new driver. The wiki page for the driver is: http://wireless.kernel.org/en/users/Drivers/ath10k The driver has had many authors, they are listed here alphabetically: Bartosz Markowski Janusz Dziedzic Kalle Valo Marek Kwaczynski Marek Puzyniak Michal Kazior Sujith Manoharan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/Kconfig | 1 + drivers/net/wireless/ath/Makefile | 1 + drivers/net/wireless/ath/ath10k/Kconfig | 39 + drivers/net/wireless/ath/ath10k/Makefile | 20 + drivers/net/wireless/ath/ath10k/bmi.c | 295 +++ drivers/net/wireless/ath/ath10k/bmi.h | 224 ++ drivers/net/wireless/ath/ath10k/ce.c | 1189 +++++++++++ drivers/net/wireless/ath/ath10k/ce.h | 516 +++++ drivers/net/wireless/ath/ath10k/core.c | 665 ++++++ drivers/net/wireless/ath/ath10k/core.h | 369 ++++ drivers/net/wireless/ath/ath10k/debug.c | 503 +++++ drivers/net/wireless/ath/ath10k/debug.h | 90 + drivers/net/wireless/ath/ath10k/hif.h | 137 ++ drivers/net/wireless/ath/ath10k/htc.c | 1000 +++++++++ drivers/net/wireless/ath/ath10k/htc.h | 368 ++++ drivers/net/wireless/ath/ath10k/htt.c | 152 ++ drivers/net/wireless/ath/ath10k/htt.h | 1338 ++++++++++++ drivers/net/wireless/ath/ath10k/htt_rx.c | 1167 ++++++++++ drivers/net/wireless/ath/ath10k/htt_tx.c | 510 +++++ drivers/net/wireless/ath/ath10k/hw.h | 304 +++ drivers/net/wireless/ath/ath10k/mac.c | 3066 +++++++++++++++++++++++++++ drivers/net/wireless/ath/ath10k/mac.h | 61 + drivers/net/wireless/ath/ath10k/pci.c | 2506 ++++++++++++++++++++++ drivers/net/wireless/ath/ath10k/pci.h | 355 ++++ drivers/net/wireless/ath/ath10k/rx_desc.h | 990 +++++++++ drivers/net/wireless/ath/ath10k/targaddrs.h | 449 ++++ drivers/net/wireless/ath/ath10k/trace.c | 20 + drivers/net/wireless/ath/ath10k/trace.h | 170 ++ drivers/net/wireless/ath/ath10k/txrx.c | 417 ++++ drivers/net/wireless/ath/ath10k/txrx.h | 39 + drivers/net/wireless/ath/ath10k/wmi.c | 2081 ++++++++++++++++++ drivers/net/wireless/ath/ath10k/wmi.h | 3052 ++++++++++++++++++++++++++ 32 files changed, 22094 insertions(+) create mode 100644 drivers/net/wireless/ath/ath10k/Kconfig create mode 100644 drivers/net/wireless/ath/ath10k/Makefile create mode 100644 drivers/net/wireless/ath/ath10k/bmi.c create mode 100644 drivers/net/wireless/ath/ath10k/bmi.h create mode 100644 drivers/net/wireless/ath/ath10k/ce.c create mode 100644 drivers/net/wireless/ath/ath10k/ce.h create mode 100644 drivers/net/wireless/ath/ath10k/core.c create mode 100644 drivers/net/wireless/ath/ath10k/core.h create mode 100644 drivers/net/wireless/ath/ath10k/debug.c create mode 100644 drivers/net/wireless/ath/ath10k/debug.h create mode 100644 drivers/net/wireless/ath/ath10k/hif.h create mode 100644 drivers/net/wireless/ath/ath10k/htc.c create mode 100644 drivers/net/wireless/ath/ath10k/htc.h create mode 100644 drivers/net/wireless/ath/ath10k/htt.c create mode 100644 drivers/net/wireless/ath/ath10k/htt.h create mode 100644 drivers/net/wireless/ath/ath10k/htt_rx.c create mode 100644 drivers/net/wireless/ath/ath10k/htt_tx.c create mode 100644 drivers/net/wireless/ath/ath10k/hw.h create mode 100644 drivers/net/wireless/ath/ath10k/mac.c create mode 100644 drivers/net/wireless/ath/ath10k/mac.h create mode 100644 drivers/net/wireless/ath/ath10k/pci.c create mode 100644 drivers/net/wireless/ath/ath10k/pci.h create mode 100644 drivers/net/wireless/ath/ath10k/rx_desc.h create mode 100644 drivers/net/wireless/ath/ath10k/targaddrs.h create mode 100644 drivers/net/wireless/ath/ath10k/trace.c create mode 100644 drivers/net/wireless/ath/ath10k/trace.h create mode 100644 drivers/net/wireless/ath/ath10k/txrx.c create mode 100644 drivers/net/wireless/ath/ath10k/txrx.h create mode 100644 drivers/net/wireless/ath/ath10k/wmi.c create mode 100644 drivers/net/wireless/ath/ath10k/wmi.h diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig index 2c02b4e84094..1abf1d421173 100644 --- a/drivers/net/wireless/ath/Kconfig +++ b/drivers/net/wireless/ath/Kconfig @@ -31,5 +31,6 @@ source "drivers/net/wireless/ath/carl9170/Kconfig" source "drivers/net/wireless/ath/ath6kl/Kconfig" source "drivers/net/wireless/ath/ar5523/Kconfig" source "drivers/net/wireless/ath/wil6210/Kconfig" +source "drivers/net/wireless/ath/ath10k/Kconfig" endif diff --git a/drivers/net/wireless/ath/Makefile b/drivers/net/wireless/ath/Makefile index 97b964ded2be..fb05cfd19361 100644 --- a/drivers/net/wireless/ath/Makefile +++ b/drivers/net/wireless/ath/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_CARL9170) += carl9170/ obj-$(CONFIG_ATH6KL) += ath6kl/ obj-$(CONFIG_AR5523) += ar5523/ obj-$(CONFIG_WIL6210) += wil6210/ +obj-$(CONFIG_ATH10K) += ath10k/ obj-$(CONFIG_ATH_COMMON) += ath.o diff --git a/drivers/net/wireless/ath/ath10k/Kconfig b/drivers/net/wireless/ath/ath10k/Kconfig new file mode 100644 index 000000000000..cde58fe96254 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/Kconfig @@ -0,0 +1,39 @@ +config ATH10K + tristate "Atheros 802.11ac wireless cards support" + depends on MAC80211 + select ATH_COMMON + ---help--- + This module adds support for wireless adapters based on + Atheros IEEE 802.11ac family of chipsets. + + If you choose to build a module, it'll be called ath10k. + +config ATH10K_PCI + tristate "Atheros ath10k PCI support" + depends on ATH10K && PCI + ---help--- + This module adds support for PCIE bus + +config ATH10K_DEBUG + bool "Atheros ath10k debugging" + depends on ATH10K + ---help--- + Enables debug support + + If unsure, say Y to make it easier to debug problems. + +config ATH10K_DEBUGFS + bool "Atheros ath10k debugfs support" + depends on ATH10K + ---help--- + Enabled debugfs support + + If unsure, say Y to make it easier to debug problems. + +config ATH10K_TRACING + bool "Atheros ath10k tracing support" + depends on ATH10K + depends on EVENT_TRACING + ---help--- + Select this to ath10k use tracing infrastructure. + diff --git a/drivers/net/wireless/ath/ath10k/Makefile b/drivers/net/wireless/ath/ath10k/Makefile new file mode 100644 index 000000000000..a4179f49ee1f --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/Makefile @@ -0,0 +1,20 @@ +obj-$(CONFIG_ATH10K) += ath10k_core.o +ath10k_core-y += mac.o \ + debug.o \ + core.o \ + htc.o \ + htt.o \ + htt_rx.o \ + htt_tx.o \ + txrx.o \ + wmi.o \ + bmi.o + +ath10k_core-$(CONFIG_ATH10K_TRACING) += trace.o + +obj-$(CONFIG_ATH10K_PCI) += ath10k_pci.o +ath10k_pci-y += pci.o \ + ce.o + +# for tracing framework to find trace.h +CFLAGS_trace.o := -I$(src) diff --git a/drivers/net/wireless/ath/ath10k/bmi.c b/drivers/net/wireless/ath/ath10k/bmi.c new file mode 100644 index 000000000000..1a2ef51b69d9 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/bmi.c @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "bmi.h" +#include "hif.h" +#include "debug.h" +#include "htc.h" + +int ath10k_bmi_done(struct ath10k *ar) +{ + struct bmi_cmd cmd; + u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.done); + int ret; + + if (ar->bmi.done_sent) { + ath10k_dbg(ATH10K_DBG_CORE, "%s skipped\n", __func__); + return 0; + } + + ar->bmi.done_sent = true; + cmd.id = __cpu_to_le32(BMI_DONE); + + ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL); + if (ret) { + ath10k_warn("unable to write to the device: %d\n", ret); + return ret; + } + + ath10k_dbg(ATH10K_DBG_CORE, "BMI done\n"); + return 0; +} + +int ath10k_bmi_get_target_info(struct ath10k *ar, + struct bmi_target_info *target_info) +{ + struct bmi_cmd cmd; + union bmi_resp resp; + u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.get_target_info); + u32 resplen = sizeof(resp.get_target_info); + int ret; + + if (ar->bmi.done_sent) { + ath10k_warn("BMI Get Target Info Command disallowed\n"); + return -EBUSY; + } + + cmd.id = __cpu_to_le32(BMI_GET_TARGET_INFO); + + ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen); + if (ret) { + ath10k_warn("unable to get target info from device\n"); + return ret; + } + + if (resplen < sizeof(resp.get_target_info)) { + ath10k_warn("invalid get_target_info response length (%d)\n", + resplen); + return -EIO; + } + + target_info->version = __le32_to_cpu(resp.get_target_info.version); + target_info->type = __le32_to_cpu(resp.get_target_info.type); + return 0; +} + +int ath10k_bmi_read_memory(struct ath10k *ar, + u32 address, void *buffer, u32 length) +{ + struct bmi_cmd cmd; + union bmi_resp resp; + u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.read_mem); + u32 rxlen; + int ret; + + if (ar->bmi.done_sent) { + ath10k_warn("command disallowed\n"); + return -EBUSY; + } + + ath10k_dbg(ATH10K_DBG_CORE, + "%s: (device: 0x%p, address: 0x%x, length: %d)\n", + __func__, ar, address, length); + + while (length) { + rxlen = min_t(u32, length, BMI_MAX_DATA_SIZE); + + cmd.id = __cpu_to_le32(BMI_READ_MEMORY); + cmd.read_mem.addr = __cpu_to_le32(address); + cmd.read_mem.len = __cpu_to_le32(rxlen); + + ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, + &resp, &rxlen); + if (ret) { + ath10k_warn("unable to read from the device\n"); + return ret; + } + + memcpy(buffer, resp.read_mem.payload, rxlen); + address += rxlen; + buffer += rxlen; + length -= rxlen; + } + + return 0; +} + +int ath10k_bmi_write_memory(struct ath10k *ar, + u32 address, const void *buffer, u32 length) +{ + struct bmi_cmd cmd; + u32 hdrlen = sizeof(cmd.id) + sizeof(cmd.write_mem); + u32 txlen; + int ret; + + if (ar->bmi.done_sent) { + ath10k_warn("command disallowed\n"); + return -EBUSY; + } + + ath10k_dbg(ATH10K_DBG_CORE, + "%s: (device: 0x%p, address: 0x%x, length: %d)\n", + __func__, ar, address, length); + + while (length) { + txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen); + + /* copy before roundup to avoid reading beyond buffer*/ + memcpy(cmd.write_mem.payload, buffer, txlen); + txlen = roundup(txlen, 4); + + cmd.id = __cpu_to_le32(BMI_WRITE_MEMORY); + cmd.write_mem.addr = __cpu_to_le32(address); + cmd.write_mem.len = __cpu_to_le32(txlen); + + ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen, + NULL, NULL); + if (ret) { + ath10k_warn("unable to write to the device\n"); + return ret; + } + + /* fixup roundup() so `length` zeroes out for last chunk */ + txlen = min(txlen, length); + + address += txlen; + buffer += txlen; + length -= txlen; + } + + return 0; +} + +int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 *param) +{ + struct bmi_cmd cmd; + union bmi_resp resp; + u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.execute); + u32 resplen = sizeof(resp.execute); + int ret; + + if (ar->bmi.done_sent) { + ath10k_warn("command disallowed\n"); + return -EBUSY; + } + + ath10k_dbg(ATH10K_DBG_CORE, + "%s: (device: 0x%p, address: 0x%x, param: %d)\n", + __func__, ar, address, *param); + + cmd.id = __cpu_to_le32(BMI_EXECUTE); + cmd.execute.addr = __cpu_to_le32(address); + cmd.execute.param = __cpu_to_le32(*param); + + ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen); + if (ret) { + ath10k_warn("unable to read from the device\n"); + return ret; + } + + if (resplen < sizeof(resp.execute)) { + ath10k_warn("invalid execute response length (%d)\n", + resplen); + return ret; + } + + *param = __le32_to_cpu(resp.execute.result); + return 0; +} + +int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length) +{ + struct bmi_cmd cmd; + u32 hdrlen = sizeof(cmd.id) + sizeof(cmd.lz_data); + u32 txlen; + int ret; + + if (ar->bmi.done_sent) { + ath10k_warn("command disallowed\n"); + return -EBUSY; + } + + while (length) { + txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen); + + WARN_ON_ONCE(txlen & 3); + + cmd.id = __cpu_to_le32(BMI_LZ_DATA); + cmd.lz_data.len = __cpu_to_le32(txlen); + memcpy(cmd.lz_data.payload, buffer, txlen); + + ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen, + NULL, NULL); + if (ret) { + ath10k_warn("unable to write to the device\n"); + return ret; + } + + buffer += txlen; + length -= txlen; + } + + return 0; +} + +int ath10k_bmi_lz_stream_start(struct ath10k *ar, u32 address) +{ + struct bmi_cmd cmd; + u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.lz_start); + int ret; + + if (ar->bmi.done_sent) { + ath10k_warn("command disallowed\n"); + return -EBUSY; + } + + cmd.id = __cpu_to_le32(BMI_LZ_STREAM_START); + cmd.lz_start.addr = __cpu_to_le32(address); + + ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL); + if (ret) { + ath10k_warn("unable to Start LZ Stream to the device\n"); + return ret; + } + + return 0; +} + +int ath10k_bmi_fast_download(struct ath10k *ar, + u32 address, const void *buffer, u32 length) +{ + u8 trailer[4] = {}; + u32 head_len = rounddown(length, 4); + u32 trailer_len = length - head_len; + int ret; + + ret = ath10k_bmi_lz_stream_start(ar, address); + if (ret) + return ret; + + /* copy the last word into a zero padded buffer */ + if (trailer_len > 0) + memcpy(trailer, buffer + head_len, trailer_len); + + ret = ath10k_bmi_lz_data(ar, buffer, head_len); + if (ret) + return ret; + + if (trailer_len > 0) + ret = ath10k_bmi_lz_data(ar, trailer, 4); + + if (ret != 0) + return ret; + + /* + * Close compressed stream and open a new (fake) one. + * This serves mainly to flush Target caches. + */ + ret = ath10k_bmi_lz_stream_start(ar, 0x00); + + return ret; +} diff --git a/drivers/net/wireless/ath/ath10k/bmi.h b/drivers/net/wireless/ath/ath10k/bmi.h new file mode 100644 index 000000000000..32c56aa33a5e --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/bmi.h @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _BMI_H_ +#define _BMI_H_ + +#include "core.h" + +/* + * Bootloader Messaging Interface (BMI) + * + * BMI is a very simple messaging interface used during initialization + * to read memory, write memory, execute code, and to define an + * application entry PC. + * + * It is used to download an application to QCA988x, to provide + * patches to code that is already resident on QCA988x, and generally + * to examine and modify state. The Host has an opportunity to use + * BMI only once during bootup. Once the Host issues a BMI_DONE + * command, this opportunity ends. + * + * The Host writes BMI requests to mailbox0, and reads BMI responses + * from mailbox0. BMI requests all begin with a command + * (see below for specific commands), and are followed by + * command-specific data. + * + * Flow control: + * The Host can only issue a command once the Target gives it a + * "BMI Command Credit", using AR8K Counter #4. As soon as the + * Target has completed a command, it issues another BMI Command + * Credit (so the Host can issue the next command). + * + * BMI handles all required Target-side cache flushing. + */ + +/* Maximum data size used for BMI transfers */ +#define BMI_MAX_DATA_SIZE 256 + +/* len = cmd + addr + length */ +#define BMI_MAX_CMDBUF_SIZE (BMI_MAX_DATA_SIZE + \ + sizeof(u32) + \ + sizeof(u32) + \ + sizeof(u32)) + +/* BMI Commands */ + +enum bmi_cmd_id { + BMI_NO_COMMAND = 0, + BMI_DONE = 1, + BMI_READ_MEMORY = 2, + BMI_WRITE_MEMORY = 3, + BMI_EXECUTE = 4, + BMI_SET_APP_START = 5, + BMI_READ_SOC_REGISTER = 6, + BMI_READ_SOC_WORD = 6, + BMI_WRITE_SOC_REGISTER = 7, + BMI_WRITE_SOC_WORD = 7, + BMI_GET_TARGET_ID = 8, + BMI_GET_TARGET_INFO = 8, + BMI_ROMPATCH_INSTALL = 9, + BMI_ROMPATCH_UNINSTALL = 10, + BMI_ROMPATCH_ACTIVATE = 11, + BMI_ROMPATCH_DEACTIVATE = 12, + BMI_LZ_STREAM_START = 13, /* should be followed by LZ_DATA */ + BMI_LZ_DATA = 14, + BMI_NVRAM_PROCESS = 15, +}; + +#define BMI_NVRAM_SEG_NAME_SZ 16 + +struct bmi_cmd { + __le32 id; /* enum bmi_cmd_id */ + union { + struct { + } done; + struct { + __le32 addr; + __le32 len; + } read_mem; + struct { + __le32 addr; + __le32 len; + u8 payload[0]; + } write_mem; + struct { + __le32 addr; + __le32 param; + } execute; + struct { + __le32 addr; + } set_app_start; + struct { + __le32 addr; + } read_soc_reg; + struct { + __le32 addr; + __le32 value; + } write_soc_reg; + struct { + } get_target_info; + struct { + __le32 rom_addr; + __le32 ram_addr; /* or value */ + __le32 size; + __le32 activate; /* 0=install, but dont activate */ + } rompatch_install; + struct { + __le32 patch_id; + } rompatch_uninstall; + struct { + __le32 count; + __le32 patch_ids[0]; /* length of @count */ + } rompatch_activate; + struct { + __le32 count; + __le32 patch_ids[0]; /* length of @count */ + } rompatch_deactivate; + struct { + __le32 addr; + } lz_start; + struct { + __le32 len; /* max BMI_MAX_DATA_SIZE */ + u8 payload[0]; /* length of @len */ + } lz_data; + struct { + u8 name[BMI_NVRAM_SEG_NAME_SZ]; + } nvram_process; + u8 payload[BMI_MAX_CMDBUF_SIZE]; + }; +} __packed; + +union bmi_resp { + struct { + u8 payload[0]; + } read_mem; + struct { + __le32 result; + } execute; + struct { + __le32 value; + } read_soc_reg; + struct { + __le32 len; + __le32 version; + __le32 type; + } get_target_info; + struct { + __le32 patch_id; + } rompatch_install; + struct { + __le32 patch_id; + } rompatch_uninstall; + struct { + /* 0 = nothing executed + * otherwise = NVRAM segment return value */ + __le32 result; + } nvram_process; + u8 payload[BMI_MAX_CMDBUF_SIZE]; +} __packed; + +struct bmi_target_info { + u32 version; + u32 type; +}; + + +/* in msec */ +#define BMI_COMMUNICATION_TIMEOUT_HZ (1*HZ) + +#define BMI_CE_NUM_TO_TARG 0 +#define BMI_CE_NUM_TO_HOST 1 + +int ath10k_bmi_done(struct ath10k *ar); +int ath10k_bmi_get_target_info(struct ath10k *ar, + struct bmi_target_info *target_info); +int ath10k_bmi_read_memory(struct ath10k *ar, u32 address, + void *buffer, u32 length); +int ath10k_bmi_write_memory(struct ath10k *ar, u32 address, + const void *buffer, u32 length); + +#define ath10k_bmi_read32(ar, item, val) \ + ({ \ + int ret; \ + u32 addr; \ + __le32 tmp; \ + \ + addr = host_interest_item_address(HI_ITEM(item)); \ + ret = ath10k_bmi_read_memory(ar, addr, (u8 *)&tmp, 4); \ + *val = __le32_to_cpu(tmp); \ + ret; \ + }) + +#define ath10k_bmi_write32(ar, item, val) \ + ({ \ + int ret; \ + u32 address; \ + __le32 v = __cpu_to_le32(val); \ + \ + address = host_interest_item_address(HI_ITEM(item)); \ + ret = ath10k_bmi_write_memory(ar, address, \ + (u8 *)&v, sizeof(v)); \ + ret; \ + }) + +int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 *param); +int ath10k_bmi_lz_stream_start(struct ath10k *ar, u32 address); +int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length); +int ath10k_bmi_fast_download(struct ath10k *ar, u32 address, + const void *buffer, u32 length); +#endif /* _BMI_H_ */ diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c new file mode 100644 index 000000000000..61a8ac70d3ca --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/ce.c @@ -0,0 +1,1189 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "hif.h" +#include "pci.h" +#include "ce.h" +#include "debug.h" + +/* + * Support for Copy Engine hardware, which is mainly used for + * communication between Host and Target over a PCIe interconnect. + */ + +/* + * A single CopyEngine (CE) comprises two "rings": + * a source ring + * a destination ring + * + * Each ring consists of a number of descriptors which specify + * an address, length, and meta-data. + * + * Typically, one side of the PCIe interconnect (Host or Target) + * controls one ring and the other side controls the other ring. + * The source side chooses when to initiate a transfer and it + * chooses what to send (buffer address, length). The destination + * side keeps a supply of "anonymous receive buffers" available and + * it handles incoming data as it arrives (when the destination + * recieves an interrupt). + * + * The sender may send a simple buffer (address/length) or it may + * send a small list of buffers. When a small list is sent, hardware + * "gathers" these and they end up in a single destination buffer + * with a single interrupt. + * + * There are several "contexts" managed by this layer -- more, it + * may seem -- than should be needed. These are provided mainly for + * maximum flexibility and especially to facilitate a simpler HIF + * implementation. There are per-CopyEngine recv, send, and watermark + * contexts. These are supplied by the caller when a recv, send, + * or watermark handler is established and they are echoed back to + * the caller when the respective callbacks are invoked. There is + * also a per-transfer context supplied by the caller when a buffer + * (or sendlist) is sent and when a buffer is enqueued for recv. + * These per-transfer contexts are echoed back to the caller when + * the buffer is sent/received. + */ + +static inline void ath10k_ce_dest_ring_write_index_set(struct ath10k *ar, + u32 ce_ctrl_addr, + unsigned int n) +{ + ath10k_pci_write32(ar, ce_ctrl_addr + DST_WR_INDEX_ADDRESS, n); +} + +static inline u32 ath10k_ce_dest_ring_write_index_get(struct ath10k *ar, + u32 ce_ctrl_addr) +{ + return ath10k_pci_read32(ar, ce_ctrl_addr + DST_WR_INDEX_ADDRESS); +} + +static inline void ath10k_ce_src_ring_write_index_set(struct ath10k *ar, + u32 ce_ctrl_addr, + unsigned int n) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + void __iomem *indicator_addr; + + if (!test_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features)) { + ath10k_pci_write32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS, n); + return; + } + + /* workaround for QCA988x_1.0 HW CE */ + indicator_addr = ar_pci->mem + ce_ctrl_addr + DST_WATERMARK_ADDRESS; + + if (ce_ctrl_addr == ath10k_ce_base_address(CDC_WAR_DATA_CE)) { + iowrite32((CDC_WAR_MAGIC_STR | n), indicator_addr); + } else { + unsigned long irq_flags; + local_irq_save(irq_flags); + iowrite32(1, indicator_addr); + + /* + * PCIE write waits for ACK in IPQ8K, there is no + * need to read back value. + */ + (void)ioread32(indicator_addr); + (void)ioread32(indicator_addr); /* conservative */ + + ath10k_pci_write32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS, n); + + iowrite32(0, indicator_addr); + local_irq_restore(irq_flags); + } +} + +static inline u32 ath10k_ce_src_ring_write_index_get(struct ath10k *ar, + u32 ce_ctrl_addr) +{ + return ath10k_pci_read32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS); +} + +static inline u32 ath10k_ce_src_ring_read_index_get(struct ath10k *ar, + u32 ce_ctrl_addr) +{ + return ath10k_pci_read32(ar, ce_ctrl_addr + CURRENT_SRRI_ADDRESS); +} + +static inline void ath10k_ce_src_ring_base_addr_set(struct ath10k *ar, + u32 ce_ctrl_addr, + unsigned int addr) +{ + ath10k_pci_write32(ar, ce_ctrl_addr + SR_BA_ADDRESS, addr); +} + +static inline void ath10k_ce_src_ring_size_set(struct ath10k *ar, + u32 ce_ctrl_addr, + unsigned int n) +{ + ath10k_pci_write32(ar, ce_ctrl_addr + SR_SIZE_ADDRESS, n); +} + +static inline void ath10k_ce_src_ring_dmax_set(struct ath10k *ar, + u32 ce_ctrl_addr, + unsigned int n) +{ + u32 ctrl1_addr = ath10k_pci_read32((ar), + (ce_ctrl_addr) + CE_CTRL1_ADDRESS); + + ath10k_pci_write32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS, + (ctrl1_addr & ~CE_CTRL1_DMAX_LENGTH_MASK) | + CE_CTRL1_DMAX_LENGTH_SET(n)); +} + +static inline void ath10k_ce_src_ring_byte_swap_set(struct ath10k *ar, + u32 ce_ctrl_addr, + unsigned int n) +{ + u32 ctrl1_addr = ath10k_pci_read32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS); + + ath10k_pci_write32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS, + (ctrl1_addr & ~CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK) | + CE_CTRL1_SRC_RING_BYTE_SWAP_EN_SET(n)); +} + +static inline void ath10k_ce_dest_ring_byte_swap_set(struct ath10k *ar, + u32 ce_ctrl_addr, + unsigned int n) +{ + u32 ctrl1_addr = ath10k_pci_read32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS); + + ath10k_pci_write32(ar, ce_ctrl_addr + CE_CTRL1_ADDRESS, + (ctrl1_addr & ~CE_CTRL1_DST_RING_BYTE_SWAP_EN_MASK) | + CE_CTRL1_DST_RING_BYTE_SWAP_EN_SET(n)); +} + +static inline u32 ath10k_ce_dest_ring_read_index_get(struct ath10k *ar, + u32 ce_ctrl_addr) +{ + return ath10k_pci_read32(ar, ce_ctrl_addr + CURRENT_DRRI_ADDRESS); +} + +static inline void ath10k_ce_dest_ring_base_addr_set(struct ath10k *ar, + u32 ce_ctrl_addr, + u32 addr) +{ + ath10k_pci_write32(ar, ce_ctrl_addr + DR_BA_ADDRESS, addr); +} + +static inline void ath10k_ce_dest_ring_size_set(struct ath10k *ar, + u32 ce_ctrl_addr, + unsigned int n) +{ + ath10k_pci_write32(ar, ce_ctrl_addr + DR_SIZE_ADDRESS, n); +} + +static inline void ath10k_ce_src_ring_highmark_set(struct ath10k *ar, + u32 ce_ctrl_addr, + unsigned int n) +{ + u32 addr = ath10k_pci_read32(ar, ce_ctrl_addr + SRC_WATERMARK_ADDRESS); + + ath10k_pci_write32(ar, ce_ctrl_addr + SRC_WATERMARK_ADDRESS, + (addr & ~SRC_WATERMARK_HIGH_MASK) | + SRC_WATERMARK_HIGH_SET(n)); +} + +static inline void ath10k_ce_src_ring_lowmark_set(struct ath10k *ar, + u32 ce_ctrl_addr, + unsigned int n) +{ + u32 addr = ath10k_pci_read32(ar, ce_ctrl_addr + SRC_WATERMARK_ADDRESS); + + ath10k_pci_write32(ar, ce_ctrl_addr + SRC_WATERMARK_ADDRESS, + (addr & ~SRC_WATERMARK_LOW_MASK) | + SRC_WATERMARK_LOW_SET(n)); +} + +static inline void ath10k_ce_dest_ring_highmark_set(struct ath10k *ar, + u32 ce_ctrl_addr, + unsigned int n) +{ + u32 addr = ath10k_pci_read32(ar, ce_ctrl_addr + DST_WATERMARK_ADDRESS); + + ath10k_pci_write32(ar, ce_ctrl_addr + DST_WATERMARK_ADDRESS, + (addr & ~DST_WATERMARK_HIGH_MASK) | + DST_WATERMARK_HIGH_SET(n)); +} + +static inline void ath10k_ce_dest_ring_lowmark_set(struct ath10k *ar, + u32 ce_ctrl_addr, + unsigned int n) +{ + u32 addr = ath10k_pci_read32(ar, ce_ctrl_addr + DST_WATERMARK_ADDRESS); + + ath10k_pci_write32(ar, ce_ctrl_addr + DST_WATERMARK_ADDRESS, + (addr & ~DST_WATERMARK_LOW_MASK) | + DST_WATERMARK_LOW_SET(n)); +} + +static inline void ath10k_ce_copy_complete_inter_enable(struct ath10k *ar, + u32 ce_ctrl_addr) +{ + u32 host_ie_addr = ath10k_pci_read32(ar, + ce_ctrl_addr + HOST_IE_ADDRESS); + + ath10k_pci_write32(ar, ce_ctrl_addr + HOST_IE_ADDRESS, + host_ie_addr | HOST_IE_COPY_COMPLETE_MASK); +} + +static inline void ath10k_ce_copy_complete_intr_disable(struct ath10k *ar, + u32 ce_ctrl_addr) +{ + u32 host_ie_addr = ath10k_pci_read32(ar, + ce_ctrl_addr + HOST_IE_ADDRESS); + + ath10k_pci_write32(ar, ce_ctrl_addr + HOST_IE_ADDRESS, + host_ie_addr & ~HOST_IE_COPY_COMPLETE_MASK); +} + +static inline void ath10k_ce_watermark_intr_disable(struct ath10k *ar, + u32 ce_ctrl_addr) +{ + u32 host_ie_addr = ath10k_pci_read32(ar, + ce_ctrl_addr + HOST_IE_ADDRESS); + + ath10k_pci_write32(ar, ce_ctrl_addr + HOST_IE_ADDRESS, + host_ie_addr & ~CE_WATERMARK_MASK); +} + +static inline void ath10k_ce_error_intr_enable(struct ath10k *ar, + u32 ce_ctrl_addr) +{ + u32 misc_ie_addr = ath10k_pci_read32(ar, + ce_ctrl_addr + MISC_IE_ADDRESS); + + ath10k_pci_write32(ar, ce_ctrl_addr + MISC_IE_ADDRESS, + misc_ie_addr | CE_ERROR_MASK); +} + +static inline void ath10k_ce_engine_int_status_clear(struct ath10k *ar, + u32 ce_ctrl_addr, + unsigned int mask) +{ + ath10k_pci_write32(ar, ce_ctrl_addr + HOST_IS_ADDRESS, mask); +} + + +/* + * Guts of ath10k_ce_send, used by both ath10k_ce_send and + * ath10k_ce_sendlist_send. + * The caller takes responsibility for any needed locking. + */ +static int ath10k_ce_send_nolock(struct ce_state *ce_state, + void *per_transfer_context, + u32 buffer, + unsigned int nbytes, + unsigned int transfer_id, + unsigned int flags) +{ + struct ath10k *ar = ce_state->ar; + struct ce_ring_state *src_ring = ce_state->src_ring; + struct ce_desc *desc, *sdesc; + unsigned int nentries_mask = src_ring->nentries_mask; + unsigned int sw_index = src_ring->sw_index; + unsigned int write_index = src_ring->write_index; + u32 ctrl_addr = ce_state->ctrl_addr; + u32 desc_flags = 0; + int ret = 0; + + if (nbytes > ce_state->src_sz_max) + ath10k_warn("%s: send more we can (nbytes: %d, max: %d)\n", + __func__, nbytes, ce_state->src_sz_max); + + ath10k_pci_wake(ar); + + if (unlikely(CE_RING_DELTA(nentries_mask, + write_index, sw_index - 1) <= 0)) { + ret = -EIO; + goto exit; + } + + desc = CE_SRC_RING_TO_DESC(src_ring->base_addr_owner_space, + write_index); + sdesc = CE_SRC_RING_TO_DESC(src_ring->shadow_base, write_index); + + desc_flags |= SM(transfer_id, CE_DESC_FLAGS_META_DATA); + + if (flags & CE_SEND_FLAG_GATHER) + desc_flags |= CE_DESC_FLAGS_GATHER; + if (flags & CE_SEND_FLAG_BYTE_SWAP) + desc_flags |= CE_DESC_FLAGS_BYTE_SWAP; + + sdesc->addr = __cpu_to_le32(buffer); + sdesc->nbytes = __cpu_to_le16(nbytes); + sdesc->flags = __cpu_to_le16(desc_flags); + + *desc = *sdesc; + + src_ring->per_transfer_context[write_index] = per_transfer_context; + + /* Update Source Ring Write Index */ + write_index = CE_RING_IDX_INCR(nentries_mask, write_index); + + /* WORKAROUND */ + if (!(flags & CE_SEND_FLAG_GATHER)) + ath10k_ce_src_ring_write_index_set(ar, ctrl_addr, write_index); + + src_ring->write_index = write_index; +exit: + ath10k_pci_sleep(ar); + return ret; +} + +int ath10k_ce_send(struct ce_state *ce_state, + void *per_transfer_context, + u32 buffer, + unsigned int nbytes, + unsigned int transfer_id, + unsigned int flags) +{ + struct ath10k *ar = ce_state->ar; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int ret; + + spin_lock_bh(&ar_pci->ce_lock); + ret = ath10k_ce_send_nolock(ce_state, per_transfer_context, + buffer, nbytes, transfer_id, flags); + spin_unlock_bh(&ar_pci->ce_lock); + + return ret; +} + +void ath10k_ce_sendlist_buf_add(struct ce_sendlist *sendlist, u32 buffer, + unsigned int nbytes, u32 flags) +{ + unsigned int num_items = sendlist->num_items; + struct ce_sendlist_item *item; + + item = &sendlist->item[num_items]; + item->data = buffer; + item->u.nbytes = nbytes; + item->flags = flags; + sendlist->num_items++; +} + +int ath10k_ce_sendlist_send(struct ce_state *ce_state, + void *per_transfer_context, + struct ce_sendlist *sendlist, + unsigned int transfer_id) +{ + struct ce_ring_state *src_ring = ce_state->src_ring; + struct ce_sendlist_item *item; + struct ath10k *ar = ce_state->ar; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + unsigned int nentries_mask = src_ring->nentries_mask; + unsigned int num_items = sendlist->num_items; + unsigned int sw_index; + unsigned int write_index; + int i, delta, ret = -ENOMEM; + + spin_lock_bh(&ar_pci->ce_lock); + + sw_index = src_ring->sw_index; + write_index = src_ring->write_index; + + delta = CE_RING_DELTA(nentries_mask, write_index, sw_index - 1); + + if (delta >= num_items) { + /* + * Handle all but the last item uniformly. + */ + for (i = 0; i < num_items - 1; i++) { + item = &sendlist->item[i]; + ret = ath10k_ce_send_nolock(ce_state, + CE_SENDLIST_ITEM_CTXT, + (u32) item->data, + item->u.nbytes, transfer_id, + item->flags | + CE_SEND_FLAG_GATHER); + if (ret) + ath10k_warn("CE send failed for item: %d\n", i); + } + /* + * Provide valid context pointer for final item. + */ + item = &sendlist->item[i]; + ret = ath10k_ce_send_nolock(ce_state, per_transfer_context, + (u32) item->data, item->u.nbytes, + transfer_id, item->flags); + if (ret) + ath10k_warn("CE send failed for last item: %d\n", i); + } + + spin_unlock_bh(&ar_pci->ce_lock); + + return ret; +} + +int ath10k_ce_recv_buf_enqueue(struct ce_state *ce_state, + void *per_recv_context, + u32 buffer) +{ + struct ce_ring_state *dest_ring = ce_state->dest_ring; + u32 ctrl_addr = ce_state->ctrl_addr; + struct ath10k *ar = ce_state->ar; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + unsigned int nentries_mask = dest_ring->nentries_mask; + unsigned int write_index; + unsigned int sw_index; + int ret; + + spin_lock_bh(&ar_pci->ce_lock); + write_index = dest_ring->write_index; + sw_index = dest_ring->sw_index; + + ath10k_pci_wake(ar); + + if (CE_RING_DELTA(nentries_mask, write_index, sw_index - 1) > 0) { + struct ce_desc *base = dest_ring->base_addr_owner_space; + struct ce_desc *desc = CE_DEST_RING_TO_DESC(base, write_index); + + /* Update destination descriptor */ + desc->addr = __cpu_to_le32(buffer); + desc->nbytes = 0; + + dest_ring->per_transfer_context[write_index] = + per_recv_context; + + /* Update Destination Ring Write Index */ + write_index = CE_RING_IDX_INCR(nentries_mask, write_index); + ath10k_ce_dest_ring_write_index_set(ar, ctrl_addr, write_index); + dest_ring->write_index = write_index; + ret = 0; + } else { + ret = -EIO; + } + ath10k_pci_sleep(ar); + spin_unlock_bh(&ar_pci->ce_lock); + + return ret; +} + +/* + * Guts of ath10k_ce_completed_recv_next. + * The caller takes responsibility for any necessary locking. + */ +static int ath10k_ce_completed_recv_next_nolock(struct ce_state *ce_state, + void **per_transfer_contextp, + u32 *bufferp, + unsigned int *nbytesp, + unsigned int *transfer_idp, + unsigned int *flagsp) +{ + struct ce_ring_state *dest_ring = ce_state->dest_ring; + unsigned int nentries_mask = dest_ring->nentries_mask; + unsigned int sw_index = dest_ring->sw_index; + + struct ce_desc *base = dest_ring->base_addr_owner_space; + struct ce_desc *desc = CE_DEST_RING_TO_DESC(base, sw_index); + struct ce_desc sdesc; + u16 nbytes; + + /* Copy in one go for performance reasons */ + sdesc = *desc; + + nbytes = __le16_to_cpu(sdesc.nbytes); + if (nbytes == 0) { + /* + * This closes a relatively unusual race where the Host + * sees the updated DRRI before the update to the + * corresponding descriptor has completed. We treat this + * as a descriptor that is not yet done. + */ + return -EIO; + } + + desc->nbytes = 0; + + /* Return data from completed destination descriptor */ + *bufferp = __le32_to_cpu(sdesc.addr); + *nbytesp = nbytes; + *transfer_idp = MS(__le16_to_cpu(sdesc.flags), CE_DESC_FLAGS_META_DATA); + + if (__le16_to_cpu(sdesc.flags) & CE_DESC_FLAGS_BYTE_SWAP) + *flagsp = CE_RECV_FLAG_SWAPPED; + else + *flagsp = 0; + + if (per_transfer_contextp) + *per_transfer_contextp = + dest_ring->per_transfer_context[sw_index]; + + /* sanity */ + dest_ring->per_transfer_context[sw_index] = NULL; + + /* Update sw_index */ + sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index); + dest_ring->sw_index = sw_index; + + return 0; +} + +int ath10k_ce_completed_recv_next(struct ce_state *ce_state, + void **per_transfer_contextp, + u32 *bufferp, + unsigned int *nbytesp, + unsigned int *transfer_idp, + unsigned int *flagsp) +{ + struct ath10k *ar = ce_state->ar; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int ret; + + spin_lock_bh(&ar_pci->ce_lock); + ret = ath10k_ce_completed_recv_next_nolock(ce_state, + per_transfer_contextp, + bufferp, nbytesp, + transfer_idp, flagsp); + spin_unlock_bh(&ar_pci->ce_lock); + + return ret; +} + +int ath10k_ce_revoke_recv_next(struct ce_state *ce_state, + void **per_transfer_contextp, + u32 *bufferp) +{ + struct ce_ring_state *dest_ring; + unsigned int nentries_mask; + unsigned int sw_index; + unsigned int write_index; + int ret; + struct ath10k *ar; + struct ath10k_pci *ar_pci; + + dest_ring = ce_state->dest_ring; + + if (!dest_ring) + return -EIO; + + ar = ce_state->ar; + ar_pci = ath10k_pci_priv(ar); + + spin_lock_bh(&ar_pci->ce_lock); + + nentries_mask = dest_ring->nentries_mask; + sw_index = dest_ring->sw_index; + write_index = dest_ring->write_index; + if (write_index != sw_index) { + struct ce_desc *base = dest_ring->base_addr_owner_space; + struct ce_desc *desc = CE_DEST_RING_TO_DESC(base, sw_index); + + /* Return data from completed destination descriptor */ + *bufferp = __le32_to_cpu(desc->addr); + + if (per_transfer_contextp) + *per_transfer_contextp = + dest_ring->per_transfer_context[sw_index]; + + /* sanity */ + dest_ring->per_transfer_context[sw_index] = NULL; + + /* Update sw_index */ + sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index); + dest_ring->sw_index = sw_index; + ret = 0; + } else { + ret = -EIO; + } + + spin_unlock_bh(&ar_pci->ce_lock); + + return ret; +} + +/* + * Guts of ath10k_ce_completed_send_next. + * The caller takes responsibility for any necessary locking. + */ +static int ath10k_ce_completed_send_next_nolock(struct ce_state *ce_state, + void **per_transfer_contextp, + u32 *bufferp, + unsigned int *nbytesp, + unsigned int *transfer_idp) +{ + struct ce_ring_state *src_ring = ce_state->src_ring; + u32 ctrl_addr = ce_state->ctrl_addr; + struct ath10k *ar = ce_state->ar; + unsigned int nentries_mask = src_ring->nentries_mask; + unsigned int sw_index = src_ring->sw_index; + unsigned int read_index; + int ret = -EIO; + + if (src_ring->hw_index == sw_index) { + /* + * The SW completion index has caught up with the cached + * version of the HW completion index. + * Update the cached HW completion index to see whether + * the SW has really caught up to the HW, or if the cached + * value of the HW index has become stale. + */ + ath10k_pci_wake(ar); + src_ring->hw_index = + ath10k_ce_src_ring_read_index_get(ar, ctrl_addr); + ath10k_pci_sleep(ar); + } + read_index = src_ring->hw_index; + + if ((read_index != sw_index) && (read_index != 0xffffffff)) { + struct ce_desc *sbase = src_ring->shadow_base; + struct ce_desc *sdesc = CE_SRC_RING_TO_DESC(sbase, sw_index); + + /* Return data from completed source descriptor */ + *bufferp = __le32_to_cpu(sdesc->addr); + *nbytesp = __le16_to_cpu(sdesc->nbytes); + *transfer_idp = MS(__le16_to_cpu(sdesc->flags), + CE_DESC_FLAGS_META_DATA); + + if (per_transfer_contextp) + *per_transfer_contextp = + src_ring->per_transfer_context[sw_index]; + + /* sanity */ + src_ring->per_transfer_context[sw_index] = NULL; + + /* Update sw_index */ + sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index); + src_ring->sw_index = sw_index; + ret = 0; + } + + return ret; +} + +/* NB: Modeled after ath10k_ce_completed_send_next */ +int ath10k_ce_cancel_send_next(struct ce_state *ce_state, + void **per_transfer_contextp, + u32 *bufferp, + unsigned int *nbytesp, + unsigned int *transfer_idp) +{ + struct ce_ring_state *src_ring; + unsigned int nentries_mask; + unsigned int sw_index; + unsigned int write_index; + int ret; + struct ath10k *ar; + struct ath10k_pci *ar_pci; + + src_ring = ce_state->src_ring; + + if (!src_ring) + return -EIO; + + ar = ce_state->ar; + ar_pci = ath10k_pci_priv(ar); + + spin_lock_bh(&ar_pci->ce_lock); + + nentries_mask = src_ring->nentries_mask; + sw_index = src_ring->sw_index; + write_index = src_ring->write_index; + + if (write_index != sw_index) { + struct ce_desc *base = src_ring->base_addr_owner_space; + struct ce_desc *desc = CE_SRC_RING_TO_DESC(base, sw_index); + + /* Return data from completed source descriptor */ + *bufferp = __le32_to_cpu(desc->addr); + *nbytesp = __le16_to_cpu(desc->nbytes); + *transfer_idp = MS(__le16_to_cpu(desc->flags), + CE_DESC_FLAGS_META_DATA); + + if (per_transfer_contextp) + *per_transfer_contextp = + src_ring->per_transfer_context[sw_index]; + + /* sanity */ + src_ring->per_transfer_context[sw_index] = NULL; + + /* Update sw_index */ + sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index); + src_ring->sw_index = sw_index; + ret = 0; + } else { + ret = -EIO; + } + + spin_unlock_bh(&ar_pci->ce_lock); + + return ret; +} + +int ath10k_ce_completed_send_next(struct ce_state *ce_state, + void **per_transfer_contextp, + u32 *bufferp, + unsigned int *nbytesp, + unsigned int *transfer_idp) +{ + struct ath10k *ar = ce_state->ar; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int ret; + + spin_lock_bh(&ar_pci->ce_lock); + ret = ath10k_ce_completed_send_next_nolock(ce_state, + per_transfer_contextp, + bufferp, nbytesp, + transfer_idp); + spin_unlock_bh(&ar_pci->ce_lock); + + return ret; +} + +/* + * Guts of interrupt handler for per-engine interrupts on a particular CE. + * + * Invokes registered callbacks for recv_complete, + * send_complete, and watermarks. + */ +void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ce_state *ce_state = ar_pci->ce_id_to_state[ce_id]; + u32 ctrl_addr = ce_state->ctrl_addr; + void *transfer_context; + u32 buf; + unsigned int nbytes; + unsigned int id; + unsigned int flags; + + ath10k_pci_wake(ar); + spin_lock_bh(&ar_pci->ce_lock); + + /* Clear the copy-complete interrupts that will be handled here. */ + ath10k_ce_engine_int_status_clear(ar, ctrl_addr, + HOST_IS_COPY_COMPLETE_MASK); + + if (ce_state->recv_cb) { + /* + * Pop completed recv buffers and call the registered + * recv callback for each + */ + while (ath10k_ce_completed_recv_next_nolock(ce_state, + &transfer_context, + &buf, &nbytes, + &id, &flags) == 0) { + spin_unlock_bh(&ar_pci->ce_lock); + ce_state->recv_cb(ce_state, transfer_context, buf, + nbytes, id, flags); + spin_lock_bh(&ar_pci->ce_lock); + } + } + + if (ce_state->send_cb) { + /* + * Pop completed send buffers and call the registered + * send callback for each + */ + while (ath10k_ce_completed_send_next_nolock(ce_state, + &transfer_context, + &buf, + &nbytes, + &id) == 0) { + spin_unlock_bh(&ar_pci->ce_lock); + ce_state->send_cb(ce_state, transfer_context, + buf, nbytes, id); + spin_lock_bh(&ar_pci->ce_lock); + } + } + + /* + * Misc CE interrupts are not being handled, but still need + * to be cleared. + */ + ath10k_ce_engine_int_status_clear(ar, ctrl_addr, CE_WATERMARK_MASK); + + spin_unlock_bh(&ar_pci->ce_lock); + ath10k_pci_sleep(ar); +} + +/* + * Handler for per-engine interrupts on ALL active CEs. + * This is used in cases where the system is sharing a + * single interrput for all CEs + */ + +void ath10k_ce_per_engine_service_any(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int ce_id; + u32 intr_summary; + + ath10k_pci_wake(ar); + intr_summary = CE_INTERRUPT_SUMMARY(ar); + + for (ce_id = 0; intr_summary && (ce_id < ar_pci->ce_count); ce_id++) { + if (intr_summary & (1 << ce_id)) + intr_summary &= ~(1 << ce_id); + else + /* no intr pending on this CE */ + continue; + + ath10k_ce_per_engine_service(ar, ce_id); + } + + ath10k_pci_sleep(ar); +} + +/* + * Adjust interrupts for the copy complete handler. + * If it's needed for either send or recv, then unmask + * this interrupt; otherwise, mask it. + * + * Called with ce_lock held. + */ +static void ath10k_ce_per_engine_handler_adjust(struct ce_state *ce_state, + int disable_copy_compl_intr) +{ + u32 ctrl_addr = ce_state->ctrl_addr; + struct ath10k *ar = ce_state->ar; + + ath10k_pci_wake(ar); + + if ((!disable_copy_compl_intr) && + (ce_state->send_cb || ce_state->recv_cb)) + ath10k_ce_copy_complete_inter_enable(ar, ctrl_addr); + else + ath10k_ce_copy_complete_intr_disable(ar, ctrl_addr); + + ath10k_ce_watermark_intr_disable(ar, ctrl_addr); + + ath10k_pci_sleep(ar); +} + +void ath10k_ce_disable_interrupts(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int ce_id; + + ath10k_pci_wake(ar); + for (ce_id = 0; ce_id < ar_pci->ce_count; ce_id++) { + struct ce_state *ce_state = ar_pci->ce_id_to_state[ce_id]; + u32 ctrl_addr = ce_state->ctrl_addr; + + ath10k_ce_copy_complete_intr_disable(ar, ctrl_addr); + } + ath10k_pci_sleep(ar); +} + +void ath10k_ce_send_cb_register(struct ce_state *ce_state, + void (*send_cb) (struct ce_state *ce_state, + void *transfer_context, + u32 buffer, + unsigned int nbytes, + unsigned int transfer_id), + int disable_interrupts) +{ + struct ath10k *ar = ce_state->ar; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + + spin_lock_bh(&ar_pci->ce_lock); + ce_state->send_cb = send_cb; + ath10k_ce_per_engine_handler_adjust(ce_state, disable_interrupts); + spin_unlock_bh(&ar_pci->ce_lock); +} + +void ath10k_ce_recv_cb_register(struct ce_state *ce_state, + void (*recv_cb) (struct ce_state *ce_state, + void *transfer_context, + u32 buffer, + unsigned int nbytes, + unsigned int transfer_id, + unsigned int flags)) +{ + struct ath10k *ar = ce_state->ar; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + + spin_lock_bh(&ar_pci->ce_lock); + ce_state->recv_cb = recv_cb; + ath10k_ce_per_engine_handler_adjust(ce_state, 0); + spin_unlock_bh(&ar_pci->ce_lock); +} + +static int ath10k_ce_init_src_ring(struct ath10k *ar, + unsigned int ce_id, + struct ce_state *ce_state, + const struct ce_attr *attr) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ce_ring_state *src_ring; + unsigned int nentries = attr->src_nentries; + unsigned int ce_nbytes; + u32 ctrl_addr = ath10k_ce_base_address(ce_id); + dma_addr_t base_addr; + char *ptr; + + nentries = roundup_pow_of_two(nentries); + + if (ce_state->src_ring) { + WARN_ON(ce_state->src_ring->nentries != nentries); + return 0; + } + + ce_nbytes = sizeof(struct ce_ring_state) + (nentries * sizeof(void *)); + ptr = kzalloc(ce_nbytes, GFP_KERNEL); + if (ptr == NULL) + return -ENOMEM; + + ce_state->src_ring = (struct ce_ring_state *)ptr; + src_ring = ce_state->src_ring; + + ptr += sizeof(struct ce_ring_state); + src_ring->nentries = nentries; + src_ring->nentries_mask = nentries - 1; + + ath10k_pci_wake(ar); + src_ring->sw_index = ath10k_ce_src_ring_read_index_get(ar, ctrl_addr); + src_ring->hw_index = src_ring->sw_index; + + src_ring->write_index = + ath10k_ce_src_ring_write_index_get(ar, ctrl_addr); + ath10k_pci_sleep(ar); + + src_ring->per_transfer_context = (void **)ptr; + + /* + * Legacy platforms that do not support cache + * coherent DMA are unsupported + */ + src_ring->base_addr_owner_space_unaligned = + pci_alloc_consistent(ar_pci->pdev, + (nentries * sizeof(struct ce_desc) + + CE_DESC_RING_ALIGN), + &base_addr); + src_ring->base_addr_ce_space_unaligned = base_addr; + + src_ring->base_addr_owner_space = PTR_ALIGN( + src_ring->base_addr_owner_space_unaligned, + CE_DESC_RING_ALIGN); + src_ring->base_addr_ce_space = ALIGN( + src_ring->base_addr_ce_space_unaligned, + CE_DESC_RING_ALIGN); + + /* + * Also allocate a shadow src ring in regular + * mem to use for faster access. + */ + src_ring->shadow_base_unaligned = + kmalloc((nentries * sizeof(struct ce_desc) + + CE_DESC_RING_ALIGN), GFP_KERNEL); + + src_ring->shadow_base = PTR_ALIGN( + src_ring->shadow_base_unaligned, + CE_DESC_RING_ALIGN); + + ath10k_pci_wake(ar); + ath10k_ce_src_ring_base_addr_set(ar, ctrl_addr, + src_ring->base_addr_ce_space); + ath10k_ce_src_ring_size_set(ar, ctrl_addr, nentries); + ath10k_ce_src_ring_dmax_set(ar, ctrl_addr, attr->src_sz_max); + ath10k_ce_src_ring_byte_swap_set(ar, ctrl_addr, 0); + ath10k_ce_src_ring_lowmark_set(ar, ctrl_addr, 0); + ath10k_ce_src_ring_highmark_set(ar, ctrl_addr, nentries); + ath10k_pci_sleep(ar); + + return 0; +} + +static int ath10k_ce_init_dest_ring(struct ath10k *ar, + unsigned int ce_id, + struct ce_state *ce_state, + const struct ce_attr *attr) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ce_ring_state *dest_ring; + unsigned int nentries = attr->dest_nentries; + unsigned int ce_nbytes; + u32 ctrl_addr = ath10k_ce_base_address(ce_id); + dma_addr_t base_addr; + char *ptr; + + nentries = roundup_pow_of_two(nentries); + + if (ce_state->dest_ring) { + WARN_ON(ce_state->dest_ring->nentries != nentries); + return 0; + } + + ce_nbytes = sizeof(struct ce_ring_state) + (nentries * sizeof(void *)); + ptr = kzalloc(ce_nbytes, GFP_KERNEL); + if (ptr == NULL) + return -ENOMEM; + + ce_state->dest_ring = (struct ce_ring_state *)ptr; + dest_ring = ce_state->dest_ring; + + ptr += sizeof(struct ce_ring_state); + dest_ring->nentries = nentries; + dest_ring->nentries_mask = nentries - 1; + + ath10k_pci_wake(ar); + dest_ring->sw_index = ath10k_ce_dest_ring_read_index_get(ar, ctrl_addr); + dest_ring->write_index = + ath10k_ce_dest_ring_write_index_get(ar, ctrl_addr); + ath10k_pci_sleep(ar); + + dest_ring->per_transfer_context = (void **)ptr; + + /* + * Legacy platforms that do not support cache + * coherent DMA are unsupported + */ + dest_ring->base_addr_owner_space_unaligned = + pci_alloc_consistent(ar_pci->pdev, + (nentries * sizeof(struct ce_desc) + + CE_DESC_RING_ALIGN), + &base_addr); + dest_ring->base_addr_ce_space_unaligned = base_addr; + + /* + * Correctly initialize memory to 0 to prevent garbage + * data crashing system when download firmware + */ + memset(dest_ring->base_addr_owner_space_unaligned, 0, + nentries * sizeof(struct ce_desc) + CE_DESC_RING_ALIGN); + + dest_ring->base_addr_owner_space = PTR_ALIGN( + dest_ring->base_addr_owner_space_unaligned, + CE_DESC_RING_ALIGN); + dest_ring->base_addr_ce_space = ALIGN( + dest_ring->base_addr_ce_space_unaligned, + CE_DESC_RING_ALIGN); + + ath10k_pci_wake(ar); + ath10k_ce_dest_ring_base_addr_set(ar, ctrl_addr, + dest_ring->base_addr_ce_space); + ath10k_ce_dest_ring_size_set(ar, ctrl_addr, nentries); + ath10k_ce_dest_ring_byte_swap_set(ar, ctrl_addr, 0); + ath10k_ce_dest_ring_lowmark_set(ar, ctrl_addr, 0); + ath10k_ce_dest_ring_highmark_set(ar, ctrl_addr, nentries); + ath10k_pci_sleep(ar); + + return 0; +} + +static struct ce_state *ath10k_ce_init_state(struct ath10k *ar, + unsigned int ce_id, + const struct ce_attr *attr) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ce_state *ce_state = NULL; + u32 ctrl_addr = ath10k_ce_base_address(ce_id); + + spin_lock_bh(&ar_pci->ce_lock); + + if (!ar_pci->ce_id_to_state[ce_id]) { + ce_state = kzalloc(sizeof(*ce_state), GFP_ATOMIC); + if (ce_state == NULL) { + spin_unlock_bh(&ar_pci->ce_lock); + return NULL; + } + + ar_pci->ce_id_to_state[ce_id] = ce_state; + ce_state->ar = ar; + ce_state->id = ce_id; + ce_state->ctrl_addr = ctrl_addr; + ce_state->state = CE_RUNNING; + /* Save attribute flags */ + ce_state->attr_flags = attr->flags; + ce_state->src_sz_max = attr->src_sz_max; + } + + spin_unlock_bh(&ar_pci->ce_lock); + + return ce_state; +} + +/* + * Initialize a Copy Engine based on caller-supplied attributes. + * This may be called once to initialize both source and destination + * rings or it may be called twice for separate source and destination + * initialization. It may be that only one side or the other is + * initialized by software/firmware. + */ +struct ce_state *ath10k_ce_init(struct ath10k *ar, + unsigned int ce_id, + const struct ce_attr *attr) +{ + struct ce_state *ce_state; + u32 ctrl_addr = ath10k_ce_base_address(ce_id); + + ce_state = ath10k_ce_init_state(ar, ce_id, attr); + if (!ce_state) { + ath10k_err("Failed to initialize CE state for ID: %d\n", ce_id); + return NULL; + } + + if (attr->src_nentries) { + if (ath10k_ce_init_src_ring(ar, ce_id, ce_state, attr)) { + ath10k_err("Failed to initialize CE src ring for ID: %d\n", + ce_id); + ath10k_ce_deinit(ce_state); + return NULL; + } + } + + if (attr->dest_nentries) { + if (ath10k_ce_init_dest_ring(ar, ce_id, ce_state, attr)) { + ath10k_err("Failed to initialize CE dest ring for ID: %d\n", + ce_id); + ath10k_ce_deinit(ce_state); + return NULL; + } + } + + /* Enable CE error interrupts */ + ath10k_pci_wake(ar); + ath10k_ce_error_intr_enable(ar, ctrl_addr); + ath10k_pci_sleep(ar); + + return ce_state; +} + +void ath10k_ce_deinit(struct ce_state *ce_state) +{ + unsigned int ce_id = ce_state->id; + struct ath10k *ar = ce_state->ar; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + + ce_state->state = CE_UNUSED; + ar_pci->ce_id_to_state[ce_id] = NULL; + + if (ce_state->src_ring) { + kfree(ce_state->src_ring->shadow_base_unaligned); + pci_free_consistent(ar_pci->pdev, + (ce_state->src_ring->nentries * + sizeof(struct ce_desc) + + CE_DESC_RING_ALIGN), + ce_state->src_ring->base_addr_owner_space, + ce_state->src_ring->base_addr_ce_space); + kfree(ce_state->src_ring); + } + + if (ce_state->dest_ring) { + pci_free_consistent(ar_pci->pdev, + (ce_state->dest_ring->nentries * + sizeof(struct ce_desc) + + CE_DESC_RING_ALIGN), + ce_state->dest_ring->base_addr_owner_space, + ce_state->dest_ring->base_addr_ce_space); + kfree(ce_state->dest_ring); + } + kfree(ce_state); +} diff --git a/drivers/net/wireless/ath/ath10k/ce.h b/drivers/net/wireless/ath/ath10k/ce.h new file mode 100644 index 000000000000..c17f07c026f4 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/ce.h @@ -0,0 +1,516 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _CE_H_ +#define _CE_H_ + +#include "hif.h" + + +/* Maximum number of Copy Engine's supported */ +#define CE_COUNT_MAX 8 +#define CE_HTT_H2T_MSG_SRC_NENTRIES 2048 + +/* Descriptor rings must be aligned to this boundary */ +#define CE_DESC_RING_ALIGN 8 +#define CE_SENDLIST_ITEMS_MAX 12 +#define CE_SEND_FLAG_GATHER 0x00010000 + +/* + * Copy Engine support: low-level Target-side Copy Engine API. + * This is a hardware access layer used by code that understands + * how to use copy engines. + */ + +struct ce_state; + + +/* Copy Engine operational state */ +enum ce_op_state { + CE_UNUSED, + CE_PAUSED, + CE_RUNNING, +}; + +#define CE_DESC_FLAGS_GATHER (1 << 0) +#define CE_DESC_FLAGS_BYTE_SWAP (1 << 1) +#define CE_DESC_FLAGS_META_DATA_MASK 0xFFFC +#define CE_DESC_FLAGS_META_DATA_LSB 3 + +struct ce_desc { + __le32 addr; + __le16 nbytes; + __le16 flags; /* %CE_DESC_FLAGS_ */ +}; + +/* Copy Engine Ring internal state */ +struct ce_ring_state { + /* Number of entries in this ring; must be power of 2 */ + unsigned int nentries; + unsigned int nentries_mask; + + /* + * For dest ring, this is the next index to be processed + * by software after it was/is received into. + * + * For src ring, this is the last descriptor that was sent + * and completion processed by software. + * + * Regardless of src or dest ring, this is an invariant + * (modulo ring size): + * write index >= read index >= sw_index + */ + unsigned int sw_index; + /* cached copy */ + unsigned int write_index; + /* + * For src ring, this is the next index not yet processed by HW. + * This is a cached copy of the real HW index (read index), used + * for avoiding reading the HW index register more often than + * necessary. + * This extends the invariant: + * write index >= read index >= hw_index >= sw_index + * + * For dest ring, this is currently unused. + */ + /* cached copy */ + unsigned int hw_index; + + /* Start of DMA-coherent area reserved for descriptors */ + /* Host address space */ + void *base_addr_owner_space_unaligned; + /* CE address space */ + u32 base_addr_ce_space_unaligned; + + /* + * Actual start of descriptors. + * Aligned to descriptor-size boundary. + * Points into reserved DMA-coherent area, above. + */ + /* Host address space */ + void *base_addr_owner_space; + + /* CE address space */ + u32 base_addr_ce_space; + /* + * Start of shadow copy of descriptors, within regular memory. + * Aligned to descriptor-size boundary. + */ + void *shadow_base_unaligned; + struct ce_desc *shadow_base; + + void **per_transfer_context; +}; + +/* Copy Engine internal state */ +struct ce_state { + struct ath10k *ar; + unsigned int id; + + unsigned int attr_flags; + + u32 ctrl_addr; + enum ce_op_state state; + + void (*send_cb) (struct ce_state *ce_state, + void *per_transfer_send_context, + u32 buffer, + unsigned int nbytes, + unsigned int transfer_id); + void (*recv_cb) (struct ce_state *ce_state, + void *per_transfer_recv_context, + u32 buffer, + unsigned int nbytes, + unsigned int transfer_id, + unsigned int flags); + + unsigned int src_sz_max; + struct ce_ring_state *src_ring; + struct ce_ring_state *dest_ring; +}; + +struct ce_sendlist_item { + /* e.g. buffer or desc list */ + dma_addr_t data; + union { + /* simple buffer */ + unsigned int nbytes; + /* Rx descriptor list */ + unsigned int ndesc; + } u; + /* externally-specified flags; OR-ed with internal flags */ + u32 flags; +}; + +struct ce_sendlist { + unsigned int num_items; + struct ce_sendlist_item item[CE_SENDLIST_ITEMS_MAX]; +}; + +/* Copy Engine settable attributes */ +struct ce_attr; + +/*==================Send====================*/ + +/* ath10k_ce_send flags */ +#define CE_SEND_FLAG_BYTE_SWAP 1 + +/* + * Queue a source buffer to be sent to an anonymous destination buffer. + * ce - which copy engine to use + * buffer - address of buffer + * nbytes - number of bytes to send + * transfer_id - arbitrary ID; reflected to destination + * flags - CE_SEND_FLAG_* values + * Returns 0 on success; otherwise an error status. + * + * Note: If no flags are specified, use CE's default data swap mode. + * + * Implementation note: pushes 1 buffer to Source ring + */ +int ath10k_ce_send(struct ce_state *ce_state, + void *per_transfer_send_context, + u32 buffer, + unsigned int nbytes, + /* 14 bits */ + unsigned int transfer_id, + unsigned int flags); + +void ath10k_ce_send_cb_register(struct ce_state *ce_state, + void (*send_cb) (struct ce_state *ce_state, + void *transfer_context, + u32 buffer, + unsigned int nbytes, + unsigned int transfer_id), + int disable_interrupts); + +/* Append a simple buffer (address/length) to a sendlist. */ +void ath10k_ce_sendlist_buf_add(struct ce_sendlist *sendlist, + u32 buffer, + unsigned int nbytes, + /* OR-ed with internal flags */ + u32 flags); + +/* + * Queue a "sendlist" of buffers to be sent using gather to a single + * anonymous destination buffer + * ce - which copy engine to use + * sendlist - list of simple buffers to send using gather + * transfer_id - arbitrary ID; reflected to destination + * Returns 0 on success; otherwise an error status. + * + * Implemenation note: Pushes multiple buffers with Gather to Source ring. + */ +int ath10k_ce_sendlist_send(struct ce_state *ce_state, + void *per_transfer_send_context, + struct ce_sendlist *sendlist, + /* 14 bits */ + unsigned int transfer_id); + +/*==================Recv=======================*/ + +/* + * Make a buffer available to receive. The buffer must be at least of a + * minimal size appropriate for this copy engine (src_sz_max attribute). + * ce - which copy engine to use + * per_transfer_recv_context - context passed back to caller's recv_cb + * buffer - address of buffer in CE space + * Returns 0 on success; otherwise an error status. + * + * Implemenation note: Pushes a buffer to Dest ring. + */ +int ath10k_ce_recv_buf_enqueue(struct ce_state *ce_state, + void *per_transfer_recv_context, + u32 buffer); + +void ath10k_ce_recv_cb_register(struct ce_state *ce_state, + void (*recv_cb) (struct ce_state *ce_state, + void *transfer_context, + u32 buffer, + unsigned int nbytes, + unsigned int transfer_id, + unsigned int flags)); + +/* recv flags */ +/* Data is byte-swapped */ +#define CE_RECV_FLAG_SWAPPED 1 + +/* + * Supply data for the next completed unprocessed receive descriptor. + * Pops buffer from Dest ring. + */ +int ath10k_ce_completed_recv_next(struct ce_state *ce_state, + void **per_transfer_contextp, + u32 *bufferp, + unsigned int *nbytesp, + unsigned int *transfer_idp, + unsigned int *flagsp); +/* + * Supply data for the next completed unprocessed send descriptor. + * Pops 1 completed send buffer from Source ring. + */ +int ath10k_ce_completed_send_next(struct ce_state *ce_state, + void **per_transfer_contextp, + u32 *bufferp, + unsigned int *nbytesp, + unsigned int *transfer_idp); + +/*==================CE Engine Initialization=======================*/ + +/* Initialize an instance of a CE */ +struct ce_state *ath10k_ce_init(struct ath10k *ar, + unsigned int ce_id, + const struct ce_attr *attr); + +/*==================CE Engine Shutdown=======================*/ +/* + * Support clean shutdown by allowing the caller to revoke + * receive buffers. Target DMA must be stopped before using + * this API. + */ +int ath10k_ce_revoke_recv_next(struct ce_state *ce_state, + void **per_transfer_contextp, + u32 *bufferp); + +/* + * Support clean shutdown by allowing the caller to cancel + * pending sends. Target DMA must be stopped before using + * this API. + */ +int ath10k_ce_cancel_send_next(struct ce_state *ce_state, + void **per_transfer_contextp, + u32 *bufferp, + unsigned int *nbytesp, + unsigned int *transfer_idp); + +void ath10k_ce_deinit(struct ce_state *ce_state); + +/*==================CE Interrupt Handlers====================*/ +void ath10k_ce_per_engine_service_any(struct ath10k *ar); +void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id); +void ath10k_ce_disable_interrupts(struct ath10k *ar); + +/* ce_attr.flags values */ +/* Use NonSnooping PCIe accesses? */ +#define CE_ATTR_NO_SNOOP 1 + +/* Byte swap data words */ +#define CE_ATTR_BYTE_SWAP_DATA 2 + +/* Swizzle descriptors? */ +#define CE_ATTR_SWIZZLE_DESCRIPTORS 4 + +/* no interrupt on copy completion */ +#define CE_ATTR_DIS_INTR 8 + +/* Attributes of an instance of a Copy Engine */ +struct ce_attr { + /* CE_ATTR_* values */ + unsigned int flags; + + /* currently not in use */ + unsigned int priority; + + /* #entries in source ring - Must be a power of 2 */ + unsigned int src_nentries; + + /* + * Max source send size for this CE. + * This is also the minimum size of a destination buffer. + */ + unsigned int src_sz_max; + + /* #entries in destination ring - Must be a power of 2 */ + unsigned int dest_nentries; + + /* Future use */ + void *reserved; +}; + +/* + * When using sendlist_send to transfer multiple buffer fragments, the + * transfer context of each fragment, except last one, will be filled + * with CE_SENDLIST_ITEM_CTXT. ce_completed_send will return success for + * each fragment done with send and the transfer context would be + * CE_SENDLIST_ITEM_CTXT. Upper layer could use this to identify the + * status of a send completion. + */ +#define CE_SENDLIST_ITEM_CTXT ((void *)0xcecebeef) + +#define SR_BA_ADDRESS 0x0000 +#define SR_SIZE_ADDRESS 0x0004 +#define DR_BA_ADDRESS 0x0008 +#define DR_SIZE_ADDRESS 0x000c +#define CE_CMD_ADDRESS 0x0018 + +#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_MSB 17 +#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_LSB 17 +#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_MASK 0x00020000 +#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_SET(x) \ + (((0 | (x)) << CE_CTRL1_DST_RING_BYTE_SWAP_EN_LSB) & \ + CE_CTRL1_DST_RING_BYTE_SWAP_EN_MASK) + +#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MSB 16 +#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_LSB 16 +#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK 0x00010000 +#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_GET(x) \ + (((x) & CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK) >> \ + CE_CTRL1_SRC_RING_BYTE_SWAP_EN_LSB) +#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_SET(x) \ + (((0 | (x)) << CE_CTRL1_SRC_RING_BYTE_SWAP_EN_LSB) & \ + CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK) + +#define CE_CTRL1_DMAX_LENGTH_MSB 15 +#define CE_CTRL1_DMAX_LENGTH_LSB 0 +#define CE_CTRL1_DMAX_LENGTH_MASK 0x0000ffff +#define CE_CTRL1_DMAX_LENGTH_GET(x) \ + (((x) & CE_CTRL1_DMAX_LENGTH_MASK) >> CE_CTRL1_DMAX_LENGTH_LSB) +#define CE_CTRL1_DMAX_LENGTH_SET(x) \ + (((0 | (x)) << CE_CTRL1_DMAX_LENGTH_LSB) & CE_CTRL1_DMAX_LENGTH_MASK) + +#define CE_CTRL1_ADDRESS 0x0010 +#define CE_CTRL1_HW_MASK 0x0007ffff +#define CE_CTRL1_SW_MASK 0x0007ffff +#define CE_CTRL1_HW_WRITE_MASK 0x00000000 +#define CE_CTRL1_SW_WRITE_MASK 0x0007ffff +#define CE_CTRL1_RSTMASK 0xffffffff +#define CE_CTRL1_RESET 0x00000080 + +#define CE_CMD_HALT_STATUS_MSB 3 +#define CE_CMD_HALT_STATUS_LSB 3 +#define CE_CMD_HALT_STATUS_MASK 0x00000008 +#define CE_CMD_HALT_STATUS_GET(x) \ + (((x) & CE_CMD_HALT_STATUS_MASK) >> CE_CMD_HALT_STATUS_LSB) +#define CE_CMD_HALT_STATUS_SET(x) \ + (((0 | (x)) << CE_CMD_HALT_STATUS_LSB) & CE_CMD_HALT_STATUS_MASK) +#define CE_CMD_HALT_STATUS_RESET 0 +#define CE_CMD_HALT_MSB 0 +#define CE_CMD_HALT_MASK 0x00000001 + +#define HOST_IE_COPY_COMPLETE_MSB 0 +#define HOST_IE_COPY_COMPLETE_LSB 0 +#define HOST_IE_COPY_COMPLETE_MASK 0x00000001 +#define HOST_IE_COPY_COMPLETE_GET(x) \ + (((x) & HOST_IE_COPY_COMPLETE_MASK) >> HOST_IE_COPY_COMPLETE_LSB) +#define HOST_IE_COPY_COMPLETE_SET(x) \ + (((0 | (x)) << HOST_IE_COPY_COMPLETE_LSB) & HOST_IE_COPY_COMPLETE_MASK) +#define HOST_IE_COPY_COMPLETE_RESET 0 +#define HOST_IE_ADDRESS 0x002c + +#define HOST_IS_DST_RING_LOW_WATERMARK_MASK 0x00000010 +#define HOST_IS_DST_RING_HIGH_WATERMARK_MASK 0x00000008 +#define HOST_IS_SRC_RING_LOW_WATERMARK_MASK 0x00000004 +#define HOST_IS_SRC_RING_HIGH_WATERMARK_MASK 0x00000002 +#define HOST_IS_COPY_COMPLETE_MASK 0x00000001 +#define HOST_IS_ADDRESS 0x0030 + +#define MISC_IE_ADDRESS 0x0034 + +#define MISC_IS_AXI_ERR_MASK 0x00000400 + +#define MISC_IS_DST_ADDR_ERR_MASK 0x00000200 +#define MISC_IS_SRC_LEN_ERR_MASK 0x00000100 +#define MISC_IS_DST_MAX_LEN_VIO_MASK 0x00000080 +#define MISC_IS_DST_RING_OVERFLOW_MASK 0x00000040 +#define MISC_IS_SRC_RING_OVERFLOW_MASK 0x00000020 + +#define MISC_IS_ADDRESS 0x0038 + +#define SR_WR_INDEX_ADDRESS 0x003c + +#define DST_WR_INDEX_ADDRESS 0x0040 + +#define CURRENT_SRRI_ADDRESS 0x0044 + +#define CURRENT_DRRI_ADDRESS 0x0048 + +#define SRC_WATERMARK_LOW_MSB 31 +#define SRC_WATERMARK_LOW_LSB 16 +#define SRC_WATERMARK_LOW_MASK 0xffff0000 +#define SRC_WATERMARK_LOW_GET(x) \ + (((x) & SRC_WATERMARK_LOW_MASK) >> SRC_WATERMARK_LOW_LSB) +#define SRC_WATERMARK_LOW_SET(x) \ + (((0 | (x)) << SRC_WATERMARK_LOW_LSB) & SRC_WATERMARK_LOW_MASK) +#define SRC_WATERMARK_LOW_RESET 0 +#define SRC_WATERMARK_HIGH_MSB 15 +#define SRC_WATERMARK_HIGH_LSB 0 +#define SRC_WATERMARK_HIGH_MASK 0x0000ffff +#define SRC_WATERMARK_HIGH_GET(x) \ + (((x) & SRC_WATERMARK_HIGH_MASK) >> SRC_WATERMARK_HIGH_LSB) +#define SRC_WATERMARK_HIGH_SET(x) \ + (((0 | (x)) << SRC_WATERMARK_HIGH_LSB) & SRC_WATERMARK_HIGH_MASK) +#define SRC_WATERMARK_HIGH_RESET 0 +#define SRC_WATERMARK_ADDRESS 0x004c + +#define DST_WATERMARK_LOW_LSB 16 +#define DST_WATERMARK_LOW_MASK 0xffff0000 +#define DST_WATERMARK_LOW_SET(x) \ + (((0 | (x)) << DST_WATERMARK_LOW_LSB) & DST_WATERMARK_LOW_MASK) +#define DST_WATERMARK_LOW_RESET 0 +#define DST_WATERMARK_HIGH_MSB 15 +#define DST_WATERMARK_HIGH_LSB 0 +#define DST_WATERMARK_HIGH_MASK 0x0000ffff +#define DST_WATERMARK_HIGH_GET(x) \ + (((x) & DST_WATERMARK_HIGH_MASK) >> DST_WATERMARK_HIGH_LSB) +#define DST_WATERMARK_HIGH_SET(x) \ + (((0 | (x)) << DST_WATERMARK_HIGH_LSB) & DST_WATERMARK_HIGH_MASK) +#define DST_WATERMARK_HIGH_RESET 0 +#define DST_WATERMARK_ADDRESS 0x0050 + + +static inline u32 ath10k_ce_base_address(unsigned int ce_id) +{ + return CE0_BASE_ADDRESS + (CE1_BASE_ADDRESS - CE0_BASE_ADDRESS) * ce_id; +} + +#define CE_WATERMARK_MASK (HOST_IS_SRC_RING_LOW_WATERMARK_MASK | \ + HOST_IS_SRC_RING_HIGH_WATERMARK_MASK | \ + HOST_IS_DST_RING_LOW_WATERMARK_MASK | \ + HOST_IS_DST_RING_HIGH_WATERMARK_MASK) + +#define CE_ERROR_MASK (MISC_IS_AXI_ERR_MASK | \ + MISC_IS_DST_ADDR_ERR_MASK | \ + MISC_IS_SRC_LEN_ERR_MASK | \ + MISC_IS_DST_MAX_LEN_VIO_MASK | \ + MISC_IS_DST_RING_OVERFLOW_MASK | \ + MISC_IS_SRC_RING_OVERFLOW_MASK) + +#define CE_SRC_RING_TO_DESC(baddr, idx) \ + (&(((struct ce_desc *)baddr)[idx])) + +#define CE_DEST_RING_TO_DESC(baddr, idx) \ + (&(((struct ce_desc *)baddr)[idx])) + +/* Ring arithmetic (modulus number of entries in ring, which is a pwr of 2). */ +#define CE_RING_DELTA(nentries_mask, fromidx, toidx) \ + (((int)(toidx)-(int)(fromidx)) & (nentries_mask)) + +#define CE_RING_IDX_INCR(nentries_mask, idx) (((idx) + 1) & (nentries_mask)) + +#define CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_LSB 8 +#define CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_MASK 0x0000ff00 +#define CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_GET(x) \ + (((x) & CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_MASK) >> \ + CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_LSB) +#define CE_WRAPPER_INTERRUPT_SUMMARY_ADDRESS 0x0000 + +#define CE_INTERRUPT_SUMMARY(ar) \ + CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_GET( \ + ath10k_pci_read32((ar), CE_WRAPPER_BASE_ADDRESS + \ + CE_WRAPPER_INTERRUPT_SUMMARY_ADDRESS)) + +#endif /* _CE_H_ */ diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c new file mode 100644 index 000000000000..2b3426b1ff3f --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -0,0 +1,665 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include "core.h" +#include "mac.h" +#include "htc.h" +#include "hif.h" +#include "wmi.h" +#include "bmi.h" +#include "debug.h" +#include "htt.h" + +unsigned int ath10k_debug_mask; +static bool uart_print; +static unsigned int ath10k_p2p; +module_param_named(debug_mask, ath10k_debug_mask, uint, 0644); +module_param(uart_print, bool, 0644); +module_param_named(p2p, ath10k_p2p, uint, 0644); +MODULE_PARM_DESC(debug_mask, "Debugging mask"); +MODULE_PARM_DESC(uart_print, "Uart target debugging"); +MODULE_PARM_DESC(p2p, "Enable ath10k P2P support"); + +static const struct ath10k_hw_params ath10k_hw_params_list[] = { + { + .id = QCA988X_HW_1_0_VERSION, + .name = "qca988x hw1.0", + .patch_load_addr = QCA988X_HW_1_0_PATCH_LOAD_ADDR, + .fw = { + .dir = QCA988X_HW_1_0_FW_DIR, + .fw = QCA988X_HW_1_0_FW_FILE, + .otp = QCA988X_HW_1_0_OTP_FILE, + .board = QCA988X_HW_1_0_BOARD_DATA_FILE, + }, + }, + { + .id = QCA988X_HW_2_0_VERSION, + .name = "qca988x hw2.0", + .patch_load_addr = QCA988X_HW_2_0_PATCH_LOAD_ADDR, + .fw = { + .dir = QCA988X_HW_2_0_FW_DIR, + .fw = QCA988X_HW_2_0_FW_FILE, + .otp = QCA988X_HW_2_0_OTP_FILE, + .board = QCA988X_HW_2_0_BOARD_DATA_FILE, + }, + }, +}; + +static void ath10k_send_suspend_complete(struct ath10k *ar) +{ + ath10k_dbg(ATH10K_DBG_CORE, "%s\n", __func__); + + ar->is_target_paused = true; + wake_up(&ar->event_queue); +} + +static int ath10k_check_fw_version(struct ath10k *ar) +{ + char version[32]; + + if (ar->fw_version_major >= SUPPORTED_FW_MAJOR && + ar->fw_version_minor >= SUPPORTED_FW_MINOR && + ar->fw_version_release >= SUPPORTED_FW_RELEASE && + ar->fw_version_build >= SUPPORTED_FW_BUILD) + return 0; + + snprintf(version, sizeof(version), "%u.%u.%u.%u", + SUPPORTED_FW_MAJOR, SUPPORTED_FW_MINOR, + SUPPORTED_FW_RELEASE, SUPPORTED_FW_BUILD); + + ath10k_warn("WARNING: Firmware version %s is not officially supported.\n", + ar->hw->wiphy->fw_version); + ath10k_warn("Please upgrade to version %s (or newer)\n", version); + + return 0; +} + +static int ath10k_init_connect_htc(struct ath10k *ar) +{ + int status; + + status = ath10k_wmi_connect_htc_service(ar); + if (status) + goto conn_fail; + + /* Start HTC */ + status = ath10k_htc_start(ar->htc); + if (status) + goto conn_fail; + + /* Wait for WMI event to be ready */ + status = ath10k_wmi_wait_for_service_ready(ar); + if (status <= 0) { + ath10k_warn("wmi service ready event not received"); + status = -ETIMEDOUT; + goto timeout; + } + + ath10k_dbg(ATH10K_DBG_CORE, "core wmi ready\n"); + return 0; + +timeout: + ath10k_htc_stop(ar->htc); +conn_fail: + return status; +} + +static int ath10k_init_configure_target(struct ath10k *ar) +{ + u32 param_host; + int ret; + + /* tell target which HTC version it is used*/ + ret = ath10k_bmi_write32(ar, hi_app_host_interest, + HTC_PROTOCOL_VERSION); + if (ret) { + ath10k_err("settings HTC version failed\n"); + return ret; + } + + /* set the firmware mode to STA/IBSS/AP */ + ret = ath10k_bmi_read32(ar, hi_option_flag, ¶m_host); + if (ret) { + ath10k_err("setting firmware mode (1/2) failed\n"); + return ret; + } + + /* TODO following parameters need to be re-visited. */ + /* num_device */ + param_host |= (1 << HI_OPTION_NUM_DEV_SHIFT); + /* Firmware mode */ + /* FIXME: Why FW_MODE_AP ??.*/ + param_host |= (HI_OPTION_FW_MODE_AP << HI_OPTION_FW_MODE_SHIFT); + /* mac_addr_method */ + param_host |= (1 << HI_OPTION_MAC_ADDR_METHOD_SHIFT); + /* firmware_bridge */ + param_host |= (0 << HI_OPTION_FW_BRIDGE_SHIFT); + /* fwsubmode */ + param_host |= (0 << HI_OPTION_FW_SUBMODE_SHIFT); + + ret = ath10k_bmi_write32(ar, hi_option_flag, param_host); + if (ret) { + ath10k_err("setting firmware mode (2/2) failed\n"); + return ret; + } + + /* We do all byte-swapping on the host */ + ret = ath10k_bmi_write32(ar, hi_be, 0); + if (ret) { + ath10k_err("setting host CPU BE mode failed\n"); + return ret; + } + + /* FW descriptor/Data swap flags */ + ret = ath10k_bmi_write32(ar, hi_fw_swap, 0); + + if (ret) { + ath10k_err("setting FW data/desc swap flags failed\n"); + return ret; + } + + return 0; +} + +static const struct firmware *ath10k_fetch_fw_file(struct ath10k *ar, + const char *dir, + const char *file) +{ + char filename[100]; + const struct firmware *fw; + int ret; + + if (file == NULL) + return ERR_PTR(-ENOENT); + + if (dir == NULL) + dir = "."; + + snprintf(filename, sizeof(filename), "%s/%s", dir, file); + ret = request_firmware(&fw, filename, ar->dev); + if (ret) + return ERR_PTR(ret); + + return fw; +} + +static int ath10k_push_board_ext_data(struct ath10k *ar, + const struct firmware *fw) +{ + u32 board_data_size = QCA988X_BOARD_DATA_SZ; + u32 board_ext_data_size = QCA988X_BOARD_EXT_DATA_SZ; + u32 board_ext_data_addr; + int ret; + + ret = ath10k_bmi_read32(ar, hi_board_ext_data, &board_ext_data_addr); + if (ret) { + ath10k_err("could not read board ext data addr (%d)\n", ret); + return ret; + } + + ath10k_dbg(ATH10K_DBG_CORE, + "ath10k: Board extended Data download addr: 0x%x\n", + board_ext_data_addr); + + if (board_ext_data_addr == 0) + return 0; + + if (fw->size != (board_data_size + board_ext_data_size)) { + ath10k_err("invalid board (ext) data sizes %zu != %d+%d\n", + fw->size, board_data_size, board_ext_data_size); + return -EINVAL; + } + + ret = ath10k_bmi_write_memory(ar, board_ext_data_addr, + fw->data + board_data_size, + board_ext_data_size); + if (ret) { + ath10k_err("could not write board ext data (%d)\n", ret); + return ret; + } + + ret = ath10k_bmi_write32(ar, hi_board_ext_data_config, + (board_ext_data_size << 16) | 1); + if (ret) { + ath10k_err("could not write board ext data bit (%d)\n", ret); + return ret; + } + + return 0; +} + +static int ath10k_download_board_data(struct ath10k *ar) +{ + u32 board_data_size = QCA988X_BOARD_DATA_SZ; + u32 address; + const struct firmware *fw; + int ret; + + fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir, + ar->hw_params.fw.board); + if (IS_ERR(fw)) { + ath10k_err("could not fetch board data fw file (%ld)\n", + PTR_ERR(fw)); + return PTR_ERR(fw); + } + + ret = ath10k_push_board_ext_data(ar, fw); + if (ret) { + ath10k_err("could not push board ext data (%d)\n", ret); + goto exit; + } + + ret = ath10k_bmi_read32(ar, hi_board_data, &address); + if (ret) { + ath10k_err("could not read board data addr (%d)\n", ret); + goto exit; + } + + ret = ath10k_bmi_write_memory(ar, address, fw->data, + min_t(u32, board_data_size, fw->size)); + if (ret) { + ath10k_err("could not write board data (%d)\n", ret); + goto exit; + } + + ret = ath10k_bmi_write32(ar, hi_board_data_initialized, 1); + if (ret) { + ath10k_err("could not write board data bit (%d)\n", ret); + goto exit; + } + +exit: + release_firmware(fw); + return ret; +} + +static int ath10k_download_and_run_otp(struct ath10k *ar) +{ + const struct firmware *fw; + u32 address; + u32 exec_param; + int ret; + + /* OTP is optional */ + + if (ar->hw_params.fw.otp == NULL) { + ath10k_info("otp file not defined\n"); + return 0; + } + + address = ar->hw_params.patch_load_addr; + + fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir, + ar->hw_params.fw.otp); + if (IS_ERR(fw)) { + ath10k_warn("could not fetch otp (%ld)\n", PTR_ERR(fw)); + return 0; + } + + ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size); + if (ret) { + ath10k_err("could not write otp (%d)\n", ret); + goto exit; + } + + exec_param = 0; + ret = ath10k_bmi_execute(ar, address, &exec_param); + if (ret) { + ath10k_err("could not execute otp (%d)\n", ret); + goto exit; + } + +exit: + release_firmware(fw); + return ret; +} + +static int ath10k_download_fw(struct ath10k *ar) +{ + const struct firmware *fw; + u32 address; + int ret; + + if (ar->hw_params.fw.fw == NULL) + return -EINVAL; + + address = ar->hw_params.patch_load_addr; + + fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir, + ar->hw_params.fw.fw); + if (IS_ERR(fw)) { + ath10k_err("could not fetch fw (%ld)\n", PTR_ERR(fw)); + return PTR_ERR(fw); + } + + ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size); + if (ret) { + ath10k_err("could not write fw (%d)\n", ret); + goto exit; + } + +exit: + release_firmware(fw); + return ret; +} + +static int ath10k_init_download_firmware(struct ath10k *ar) +{ + int ret; + + ret = ath10k_download_board_data(ar); + if (ret) + return ret; + + ret = ath10k_download_and_run_otp(ar); + if (ret) + return ret; + + ret = ath10k_download_fw(ar); + if (ret) + return ret; + + return ret; +} + +static int ath10k_init_uart(struct ath10k *ar) +{ + int ret; + + /* + * Explicitly setting UART prints to zero as target turns it on + * based on scratch registers. + */ + ret = ath10k_bmi_write32(ar, hi_serial_enable, 0); + if (ret) { + ath10k_warn("could not disable UART prints (%d)\n", ret); + return ret; + } + + if (!uart_print) { + ath10k_info("UART prints disabled\n"); + return 0; + } + + ret = ath10k_bmi_write32(ar, hi_dbg_uart_txpin, 7); + if (ret) { + ath10k_warn("could not enable UART prints (%d)\n", ret); + return ret; + } + + ret = ath10k_bmi_write32(ar, hi_serial_enable, 1); + if (ret) { + ath10k_warn("could not enable UART prints (%d)\n", ret); + return ret; + } + + ath10k_info("UART prints enabled\n"); + return 0; +} + +static int ath10k_init_hw_params(struct ath10k *ar) +{ + const struct ath10k_hw_params *uninitialized_var(hw_params); + int i; + + for (i = 0; i < ARRAY_SIZE(ath10k_hw_params_list); i++) { + hw_params = &ath10k_hw_params_list[i]; + + if (hw_params->id == ar->target_version) + break; + } + + if (i == ARRAY_SIZE(ath10k_hw_params_list)) { + ath10k_err("Unsupported hardware version: 0x%x\n", + ar->target_version); + return -EINVAL; + } + + ar->hw_params = *hw_params; + + ath10k_info("Hardware name %s version 0x%x\n", + ar->hw_params.name, ar->target_version); + + return 0; +} + +struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, + enum ath10k_bus bus, + const struct ath10k_hif_ops *hif_ops) +{ + struct ath10k *ar; + + ar = ath10k_mac_create(); + if (!ar) + return NULL; + + ar->ath_common.priv = ar; + ar->ath_common.hw = ar->hw; + + ar->p2p = !!ath10k_p2p; + ar->dev = dev; + + ar->hif.priv = hif_priv; + ar->hif.ops = hif_ops; + ar->hif.bus = bus; + + ar->free_vdev_map = 0xFF; /* 8 vdevs */ + + init_completion(&ar->scan.started); + init_completion(&ar->scan.completed); + init_completion(&ar->scan.on_channel); + + init_completion(&ar->install_key_done); + init_completion(&ar->vdev_setup_done); + + setup_timer(&ar->scan.timeout, ath10k_reset_scan, (unsigned long)ar); + + ar->workqueue = create_singlethread_workqueue("ath10k_wq"); + if (!ar->workqueue) + goto err_wq; + + mutex_init(&ar->conf_mutex); + spin_lock_init(&ar->data_lock); + + INIT_LIST_HEAD(&ar->peers); + init_waitqueue_head(&ar->peer_mapping_wq); + + init_completion(&ar->offchan_tx_completed); + INIT_WORK(&ar->offchan_tx_work, ath10k_offchan_tx_work); + skb_queue_head_init(&ar->offchan_tx_queue); + + init_waitqueue_head(&ar->event_queue); + + return ar; + +err_wq: + ath10k_mac_destroy(ar); + return NULL; +} +EXPORT_SYMBOL(ath10k_core_create); + +void ath10k_core_destroy(struct ath10k *ar) +{ + flush_workqueue(ar->workqueue); + destroy_workqueue(ar->workqueue); + + ath10k_mac_destroy(ar); +} +EXPORT_SYMBOL(ath10k_core_destroy); + + +int ath10k_core_register(struct ath10k *ar) +{ + struct ath10k_htc_ops htc_ops; + struct bmi_target_info target_info; + int status; + + memset(&target_info, 0, sizeof(target_info)); + status = ath10k_bmi_get_target_info(ar, &target_info); + if (status) + goto err; + + ar->target_version = target_info.version; + ar->hw->wiphy->hw_version = target_info.version; + + status = ath10k_init_hw_params(ar); + if (status) + goto err; + + if (ath10k_init_configure_target(ar)) { + status = -EINVAL; + goto err; + } + + status = ath10k_init_download_firmware(ar); + if (status) + goto err; + + status = ath10k_init_uart(ar); + if (status) + goto err; + + htc_ops.target_send_suspend_complete = ath10k_send_suspend_complete; + + ar->htc = ath10k_htc_create(ar, &htc_ops); + if (IS_ERR(ar->htc)) { + status = PTR_ERR(ar->htc); + ath10k_err("could not create HTC (%d)\n", status); + goto err; + } + + status = ath10k_bmi_done(ar); + if (status) + goto err_htc_destroy; + + status = ath10k_wmi_attach(ar); + if (status) { + ath10k_err("WMI attach failed: %d\n", status); + goto err_htc_destroy; + } + + status = ath10k_htc_wait_target(ar->htc); + if (status) + goto err_wmi_detach; + + ar->htt = ath10k_htt_attach(ar); + if (!ar->htt) { + status = -ENOMEM; + goto err_wmi_detach; + } + + status = ath10k_init_connect_htc(ar); + if (status) + goto err_htt_detach; + + ath10k_info("firmware %s booted\n", ar->hw->wiphy->fw_version); + + status = ath10k_check_fw_version(ar); + if (status) + goto err_disconnect_htc; + + status = ath10k_wmi_cmd_init(ar); + if (status) { + ath10k_err("could not send WMI init command (%d)\n", status); + goto err_disconnect_htc; + } + + status = ath10k_wmi_wait_for_unified_ready(ar); + if (status <= 0) { + ath10k_err("wmi unified ready event not received\n"); + status = -ETIMEDOUT; + goto err_disconnect_htc; + } + + status = ath10k_htt_attach_target(ar->htt); + if (status) + goto err_disconnect_htc; + + status = ath10k_mac_register(ar); + if (status) + goto err_disconnect_htc; + + status = ath10k_debug_create(ar); + if (status) { + ath10k_err("unable to initialize debugfs\n"); + goto err_unregister_mac; + } + + return 0; + +err_unregister_mac: + ath10k_mac_unregister(ar); +err_disconnect_htc: + ath10k_htc_stop(ar->htc); +err_htt_detach: + ath10k_htt_detach(ar->htt); +err_wmi_detach: + ath10k_wmi_detach(ar); +err_htc_destroy: + ath10k_htc_destroy(ar->htc); +err: + return status; +} +EXPORT_SYMBOL(ath10k_core_register); + +void ath10k_core_unregister(struct ath10k *ar) +{ + /* We must unregister from mac80211 before we stop HTC and HIF. + * Otherwise we will fail to submit commands to FW and mac80211 will be + * unhappy about callback failures. */ + ath10k_mac_unregister(ar); + ath10k_htc_stop(ar->htc); + ath10k_htt_detach(ar->htt); + ath10k_wmi_detach(ar); + ath10k_htc_destroy(ar->htc); +} +EXPORT_SYMBOL(ath10k_core_unregister); + +int ath10k_core_target_suspend(struct ath10k *ar) +{ + int ret; + + ath10k_dbg(ATH10K_DBG_CORE, "%s: called", __func__); + + ret = ath10k_wmi_pdev_suspend_target(ar); + if (ret) + ath10k_warn("could not suspend target (%d)\n", ret); + + return ret; +} +EXPORT_SYMBOL(ath10k_core_target_suspend); + +int ath10k_core_target_resume(struct ath10k *ar) +{ + int ret; + + ath10k_dbg(ATH10K_DBG_CORE, "%s: called", __func__); + + ret = ath10k_wmi_pdev_resume_target(ar); + if (ret) + ath10k_warn("could not resume target (%d)\n", ret); + + return ret; +} +EXPORT_SYMBOL(ath10k_core_target_resume); + +MODULE_AUTHOR("Qualcomm Atheros"); +MODULE_DESCRIPTION("Core module for QCA988X PCIe devices."); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h new file mode 100644 index 000000000000..539336d1be4b --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -0,0 +1,369 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _CORE_H_ +#define _CORE_H_ + +#include +#include +#include +#include + +#include "htc.h" +#include "hw.h" +#include "targaddrs.h" +#include "wmi.h" +#include "../ath.h" +#include "../regd.h" + +#define MS(_v, _f) (((_v) & _f##_MASK) >> _f##_LSB) +#define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK) +#define WO(_f) ((_f##_OFFSET) >> 2) + +#define ATH10K_SCAN_ID 0 +#define WMI_READY_TIMEOUT (5 * HZ) +#define ATH10K_FLUSH_TIMEOUT_HZ (5*HZ) + +/* Antenna noise floor */ +#define ATH10K_DEFAULT_NOISE_FLOOR -95 + +struct ath10k; + +enum ath10k_bus { + ATH10K_BUS_PCI, +}; + +struct ath10k_skb_cb { + dma_addr_t paddr; + bool is_mapped; + bool is_aborted; + + struct { + u8 vdev_id; + u16 msdu_id; + u8 tid; + bool is_offchan; + bool is_conf; + bool discard; + bool no_ack; + u8 refcount; + struct sk_buff *txfrag; + struct sk_buff *msdu; + } __packed htt; + + /* 4 bytes left on 64bit arch */ +} __packed; + +static inline struct ath10k_skb_cb *ATH10K_SKB_CB(struct sk_buff *skb) +{ + BUILD_BUG_ON(sizeof(struct ath10k_skb_cb) > + IEEE80211_TX_INFO_DRIVER_DATA_SIZE); + return (struct ath10k_skb_cb *)&IEEE80211_SKB_CB(skb)->driver_data; +} + +static inline int ath10k_skb_map(struct device *dev, struct sk_buff *skb) +{ + if (ATH10K_SKB_CB(skb)->is_mapped) + return -EINVAL; + + ATH10K_SKB_CB(skb)->paddr = dma_map_single(dev, skb->data, skb->len, + DMA_TO_DEVICE); + + if (unlikely(dma_mapping_error(dev, ATH10K_SKB_CB(skb)->paddr))) + return -EIO; + + ATH10K_SKB_CB(skb)->is_mapped = true; + return 0; +} + +static inline int ath10k_skb_unmap(struct device *dev, struct sk_buff *skb) +{ + if (!ATH10K_SKB_CB(skb)->is_mapped) + return -EINVAL; + + dma_unmap_single(dev, ATH10K_SKB_CB(skb)->paddr, skb->len, + DMA_TO_DEVICE); + ATH10K_SKB_CB(skb)->is_mapped = false; + return 0; +} + +static inline u32 host_interest_item_address(u32 item_offset) +{ + return QCA988X_HOST_INTEREST_ADDRESS + item_offset; +} + +struct ath10k_bmi { + bool done_sent; +}; + +struct ath10k_wmi { + enum ath10k_htc_ep_id eid; + struct completion service_ready; + struct completion unified_ready; + atomic_t pending_tx_count; + wait_queue_head_t wq; + + struct sk_buff_head wmi_event_list; + struct work_struct wmi_event_work; +}; + +struct ath10k_peer_stat { + u8 peer_macaddr[ETH_ALEN]; + u32 peer_rssi; + u32 peer_tx_rate; +}; + +struct ath10k_target_stats { + /* PDEV stats */ + s32 ch_noise_floor; + u32 tx_frame_count; + u32 rx_frame_count; + u32 rx_clear_count; + u32 cycle_count; + u32 phy_err_count; + u32 chan_tx_power; + + /* PDEV TX stats */ + s32 comp_queued; + s32 comp_delivered; + s32 msdu_enqued; + s32 mpdu_enqued; + s32 wmm_drop; + s32 local_enqued; + s32 local_freed; + s32 hw_queued; + s32 hw_reaped; + s32 underrun; + s32 tx_abort; + s32 mpdus_requed; + u32 tx_ko; + u32 data_rc; + u32 self_triggers; + u32 sw_retry_failure; + u32 illgl_rate_phy_err; + u32 pdev_cont_xretry; + u32 pdev_tx_timeout; + u32 pdev_resets; + u32 phy_underrun; + u32 txop_ovf; + + /* PDEV RX stats */ + s32 mid_ppdu_route_change; + s32 status_rcvd; + s32 r0_frags; + s32 r1_frags; + s32 r2_frags; + s32 r3_frags; + s32 htt_msdus; + s32 htt_mpdus; + s32 loc_msdus; + s32 loc_mpdus; + s32 oversize_amsdu; + s32 phy_errs; + s32 phy_err_drop; + s32 mpdu_errs; + + /* VDEV STATS */ + + /* PEER STATS */ + u8 peers; + struct ath10k_peer_stat peer_stat[TARGET_NUM_PEERS]; + + /* TODO: Beacon filter stats */ + +}; + +#define ATH10K_MAX_NUM_PEER_IDS (1 << 11) /* htt rx_desc limit */ + +struct ath10k_peer { + struct list_head list; + int vdev_id; + u8 addr[ETH_ALEN]; + DECLARE_BITMAP(peer_ids, ATH10K_MAX_NUM_PEER_IDS); + struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1]; +}; + +#define ATH10K_VDEV_SETUP_TIMEOUT_HZ (5*HZ) + +struct ath10k_vif { + u32 vdev_id; + enum wmi_vdev_type vdev_type; + enum wmi_vdev_subtype vdev_subtype; + u32 beacon_interval; + u32 dtim_period; + + struct ath10k *ar; + struct ieee80211_vif *vif; + + struct ieee80211_key_conf *wep_keys[WMI_MAX_KEY_INDEX + 1]; + u8 def_wep_key_index; + + u16 tx_seq_no; + + union { + struct { + u8 bssid[ETH_ALEN]; + u32 uapsd; + } sta; + struct { + /* 127 stations; wmi limit */ + u8 tim_bitmap[16]; + u8 tim_len; + u32 ssid_len; + u8 ssid[IEEE80211_MAX_SSID_LEN]; + bool hidden_ssid; + /* P2P_IE with NoA attribute for P2P_GO case */ + u32 noa_len; + u8 *noa_data; + } ap; + struct { + u8 bssid[ETH_ALEN]; + } ibss; + } u; +}; + +struct ath10k_vif_iter { + u32 vdev_id; + struct ath10k_vif *arvif; +}; + +struct ath10k_debug { + struct dentry *debugfs_phy; + + struct ath10k_target_stats target_stats; + u32 wmi_service_bitmap[WMI_SERVICE_BM_SIZE]; + + struct completion event_stats_compl; +}; + +struct ath10k { + struct ath_common ath_common; + struct ieee80211_hw *hw; + struct device *dev; + u8 mac_addr[ETH_ALEN]; + + u32 target_version; + u8 fw_version_major; + u32 fw_version_minor; + u16 fw_version_release; + u16 fw_version_build; + u32 phy_capability; + u32 hw_min_tx_power; + u32 hw_max_tx_power; + u32 ht_cap_info; + u32 vht_cap_info; + + struct targetdef *targetdef; + struct hostdef *hostdef; + + bool p2p; + + struct { + void *priv; + enum ath10k_bus bus; + const struct ath10k_hif_ops *ops; + } hif; + + struct ath10k_wmi wmi; + + wait_queue_head_t event_queue; + bool is_target_paused; + + struct ath10k_bmi bmi; + + struct ath10k_htc *htc; + struct ath10k_htt *htt; + + struct ath10k_hw_params { + u32 id; + const char *name; + u32 patch_load_addr; + + struct ath10k_hw_params_fw { + const char *dir; + const char *fw; + const char *otp; + const char *board; + } fw; + } hw_params; + + struct { + struct completion started; + struct completion completed; + struct completion on_channel; + struct timer_list timeout; + bool is_roc; + bool in_progress; + bool aborting; + int vdev_id; + int roc_freq; + } scan; + + struct { + struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS]; + } mac; + + /* should never be NULL; needed for regular htt rx */ + struct ieee80211_channel *rx_channel; + + /* valid during scan; needed for mgmt rx during scan */ + struct ieee80211_channel *scan_channel; + + int free_vdev_map; + int monitor_vdev_id; + bool monitor_enabled; + bool monitor_present; + unsigned int filter_flags; + + struct wmi_pdev_set_wmm_params_arg wmm_params; + struct completion install_key_done; + + struct completion vdev_setup_done; + + struct workqueue_struct *workqueue; + + /* prevents concurrent FW reconfiguration */ + struct mutex conf_mutex; + + /* protects shared structure data */ + spinlock_t data_lock; + + struct list_head peers; + wait_queue_head_t peer_mapping_wq; + + struct work_struct offchan_tx_work; + struct sk_buff_head offchan_tx_queue; + struct completion offchan_tx_completed; + struct sk_buff *offchan_tx_skb; + +#ifdef CONFIG_ATH10K_DEBUGFS + struct ath10k_debug debug; +#endif +}; + +struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, + enum ath10k_bus bus, + const struct ath10k_hif_ops *hif_ops); +void ath10k_core_destroy(struct ath10k *ar); + +int ath10k_core_register(struct ath10k *ar); +void ath10k_core_unregister(struct ath10k *ar); + +int ath10k_core_target_suspend(struct ath10k *ar); +int ath10k_core_target_resume(struct ath10k *ar); + +#endif /* _CORE_H_ */ diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c new file mode 100644 index 000000000000..499034b873d1 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -0,0 +1,503 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include "core.h" +#include "debug.h" + +static int ath10k_printk(const char *level, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + int rtn; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + rtn = printk("%sath10k: %pV", level, &vaf); + + va_end(args); + + return rtn; +} + +int ath10k_info(const char *fmt, ...) +{ + struct va_format vaf = { + .fmt = fmt, + }; + va_list args; + int ret; + + va_start(args, fmt); + vaf.va = &args; + ret = ath10k_printk(KERN_INFO, "%pV", &vaf); + trace_ath10k_log_info(&vaf); + va_end(args); + + return ret; +} +EXPORT_SYMBOL(ath10k_info); + +int ath10k_err(const char *fmt, ...) +{ + struct va_format vaf = { + .fmt = fmt, + }; + va_list args; + int ret; + + va_start(args, fmt); + vaf.va = &args; + ret = ath10k_printk(KERN_ERR, "%pV", &vaf); + trace_ath10k_log_err(&vaf); + va_end(args); + + return ret; +} +EXPORT_SYMBOL(ath10k_err); + +int ath10k_warn(const char *fmt, ...) +{ + struct va_format vaf = { + .fmt = fmt, + }; + va_list args; + int ret = 0; + + va_start(args, fmt); + vaf.va = &args; + + if (net_ratelimit()) + ret = ath10k_printk(KERN_WARNING, "%pV", &vaf); + + trace_ath10k_log_warn(&vaf); + + va_end(args); + + return ret; +} +EXPORT_SYMBOL(ath10k_warn); + +#ifdef CONFIG_ATH10K_DEBUGFS + +void ath10k_debug_read_service_map(struct ath10k *ar, + void *service_map, + size_t map_size) +{ + memcpy(ar->debug.wmi_service_bitmap, service_map, map_size); +} + +static ssize_t ath10k_read_wmi_services(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + char *buf; + unsigned int len = 0, buf_len = 1500; + const char *status; + ssize_t ret_cnt; + int i; + + buf = kzalloc(buf_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + mutex_lock(&ar->conf_mutex); + + if (len > buf_len) + len = buf_len; + + for (i = 0; i < WMI_SERVICE_LAST; i++) { + if (WMI_SERVICE_IS_ENABLED(ar->debug.wmi_service_bitmap, i)) + status = "enabled"; + else + status = "disabled"; + + len += scnprintf(buf + len, buf_len - len, + "0x%02x - %20s - %s\n", + i, wmi_service_name(i), status); + } + + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + + mutex_unlock(&ar->conf_mutex); + + kfree(buf); + return ret_cnt; +} + +static const struct file_operations fops_wmi_services = { + .read = ath10k_read_wmi_services, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +void ath10k_debug_read_target_stats(struct ath10k *ar, + struct wmi_stats_event *ev) +{ + u8 *tmp = ev->data; + struct ath10k_target_stats *stats; + int num_pdev_stats, num_vdev_stats, num_peer_stats; + struct wmi_pdev_stats *ps; + int i; + + mutex_lock(&ar->conf_mutex); + + stats = &ar->debug.target_stats; + + num_pdev_stats = __le32_to_cpu(ev->num_pdev_stats); /* 0 or 1 */ + num_vdev_stats = __le32_to_cpu(ev->num_vdev_stats); /* 0 or max vdevs */ + num_peer_stats = __le32_to_cpu(ev->num_peer_stats); /* 0 or max peers */ + + if (num_pdev_stats) { + ps = (struct wmi_pdev_stats *)tmp; + + stats->ch_noise_floor = __le32_to_cpu(ps->chan_nf); + stats->tx_frame_count = __le32_to_cpu(ps->tx_frame_count); + stats->rx_frame_count = __le32_to_cpu(ps->rx_frame_count); + stats->rx_clear_count = __le32_to_cpu(ps->rx_clear_count); + stats->cycle_count = __le32_to_cpu(ps->cycle_count); + stats->phy_err_count = __le32_to_cpu(ps->phy_err_count); + stats->chan_tx_power = __le32_to_cpu(ps->chan_tx_pwr); + + stats->comp_queued = __le32_to_cpu(ps->wal.tx.comp_queued); + stats->comp_delivered = + __le32_to_cpu(ps->wal.tx.comp_delivered); + stats->msdu_enqued = __le32_to_cpu(ps->wal.tx.msdu_enqued); + stats->mpdu_enqued = __le32_to_cpu(ps->wal.tx.mpdu_enqued); + stats->wmm_drop = __le32_to_cpu(ps->wal.tx.wmm_drop); + stats->local_enqued = __le32_to_cpu(ps->wal.tx.local_enqued); + stats->local_freed = __le32_to_cpu(ps->wal.tx.local_freed); + stats->hw_queued = __le32_to_cpu(ps->wal.tx.hw_queued); + stats->hw_reaped = __le32_to_cpu(ps->wal.tx.hw_reaped); + stats->underrun = __le32_to_cpu(ps->wal.tx.underrun); + stats->tx_abort = __le32_to_cpu(ps->wal.tx.tx_abort); + stats->mpdus_requed = __le32_to_cpu(ps->wal.tx.mpdus_requed); + stats->tx_ko = __le32_to_cpu(ps->wal.tx.tx_ko); + stats->data_rc = __le32_to_cpu(ps->wal.tx.data_rc); + stats->self_triggers = __le32_to_cpu(ps->wal.tx.self_triggers); + stats->sw_retry_failure = + __le32_to_cpu(ps->wal.tx.sw_retry_failure); + stats->illgl_rate_phy_err = + __le32_to_cpu(ps->wal.tx.illgl_rate_phy_err); + stats->pdev_cont_xretry = + __le32_to_cpu(ps->wal.tx.pdev_cont_xretry); + stats->pdev_tx_timeout = + __le32_to_cpu(ps->wal.tx.pdev_tx_timeout); + stats->pdev_resets = __le32_to_cpu(ps->wal.tx.pdev_resets); + stats->phy_underrun = __le32_to_cpu(ps->wal.tx.phy_underrun); + stats->txop_ovf = __le32_to_cpu(ps->wal.tx.txop_ovf); + + stats->mid_ppdu_route_change = + __le32_to_cpu(ps->wal.rx.mid_ppdu_route_change); + stats->status_rcvd = __le32_to_cpu(ps->wal.rx.status_rcvd); + stats->r0_frags = __le32_to_cpu(ps->wal.rx.r0_frags); + stats->r1_frags = __le32_to_cpu(ps->wal.rx.r1_frags); + stats->r2_frags = __le32_to_cpu(ps->wal.rx.r2_frags); + stats->r3_frags = __le32_to_cpu(ps->wal.rx.r3_frags); + stats->htt_msdus = __le32_to_cpu(ps->wal.rx.htt_msdus); + stats->htt_mpdus = __le32_to_cpu(ps->wal.rx.htt_mpdus); + stats->loc_msdus = __le32_to_cpu(ps->wal.rx.loc_msdus); + stats->loc_mpdus = __le32_to_cpu(ps->wal.rx.loc_mpdus); + stats->oversize_amsdu = + __le32_to_cpu(ps->wal.rx.oversize_amsdu); + stats->phy_errs = __le32_to_cpu(ps->wal.rx.phy_errs); + stats->phy_err_drop = __le32_to_cpu(ps->wal.rx.phy_err_drop); + stats->mpdu_errs = __le32_to_cpu(ps->wal.rx.mpdu_errs); + + tmp += sizeof(struct wmi_pdev_stats); + } + + /* 0 or max vdevs */ + /* Currently firmware does not support VDEV stats */ + if (num_vdev_stats) { + struct wmi_vdev_stats *vdev_stats; + + for (i = 0; i < num_vdev_stats; i++) { + vdev_stats = (struct wmi_vdev_stats *)tmp; + tmp += sizeof(struct wmi_vdev_stats); + } + } + + if (num_peer_stats) { + struct wmi_peer_stats *peer_stats; + struct ath10k_peer_stat *s; + + stats->peers = num_peer_stats; + + for (i = 0; i < num_peer_stats; i++) { + peer_stats = (struct wmi_peer_stats *)tmp; + s = &stats->peer_stat[i]; + + WMI_MAC_ADDR_TO_CHAR_ARRAY(&peer_stats->peer_macaddr, + s->peer_macaddr); + s->peer_rssi = __le32_to_cpu(peer_stats->peer_rssi); + s->peer_tx_rate = + __le32_to_cpu(peer_stats->peer_tx_rate); + + tmp += sizeof(struct wmi_peer_stats); + } + } + + mutex_unlock(&ar->conf_mutex); + complete(&ar->debug.event_stats_compl); +} + +static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + struct ath10k_target_stats *fw_stats; + char *buf; + unsigned int len = 0, buf_len = 2500; + ssize_t ret_cnt; + long left; + int i; + int ret; + + fw_stats = &ar->debug.target_stats; + + buf = kzalloc(buf_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = ath10k_wmi_request_stats(ar, WMI_REQUEST_PEER_STAT); + if (ret) { + ath10k_warn("could not request stats (%d)\n", ret); + kfree(buf); + return -EIO; + } + + left = wait_for_completion_timeout(&ar->debug.event_stats_compl, 1*HZ); + + if (left <= 0) { + kfree(buf); + return -ETIMEDOUT; + } + + mutex_lock(&ar->conf_mutex); + + len += scnprintf(buf + len, buf_len - len, "\n"); + len += scnprintf(buf + len, buf_len - len, "%30s\n", + "ath10k PDEV stats"); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "================="); + + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Channel noise floor", fw_stats->ch_noise_floor); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Channel TX power", fw_stats->chan_tx_power); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "TX frame count", fw_stats->tx_frame_count); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "RX frame count", fw_stats->rx_frame_count); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "RX clear count", fw_stats->rx_clear_count); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Cycle count", fw_stats->cycle_count); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "PHY error count", fw_stats->phy_err_count); + + len += scnprintf(buf + len, buf_len - len, "\n"); + len += scnprintf(buf + len, buf_len - len, "%30s\n", + "ath10k PDEV TX stats"); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "================="); + + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "HTT cookies queued", fw_stats->comp_queued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "HTT cookies disp.", fw_stats->comp_delivered); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MSDU queued", fw_stats->msdu_enqued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDU queued", fw_stats->mpdu_enqued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MSDUs dropped", fw_stats->wmm_drop); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Local enqued", fw_stats->local_enqued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Local freed", fw_stats->local_freed); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "HW queued", fw_stats->hw_queued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "PPDUs reaped", fw_stats->hw_reaped); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Num underruns", fw_stats->underrun); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "PPDUs cleaned", fw_stats->tx_abort); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDUs requed", fw_stats->mpdus_requed); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Excessive retries", fw_stats->tx_ko); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "HW rate", fw_stats->data_rc); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Sched self tiggers", fw_stats->self_triggers); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Dropped due to SW retries", + fw_stats->sw_retry_failure); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Illegal rate phy errors", + fw_stats->illgl_rate_phy_err); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Pdev continous xretry", fw_stats->pdev_cont_xretry); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "TX timeout", fw_stats->pdev_tx_timeout); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "PDEV resets", fw_stats->pdev_resets); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "PHY underrun", fw_stats->phy_underrun); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDU is more than txop limit", fw_stats->txop_ovf); + + len += scnprintf(buf + len, buf_len - len, "\n"); + len += scnprintf(buf + len, buf_len - len, "%30s\n", + "ath10k PDEV RX stats"); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "================="); + + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Mid PPDU route change", + fw_stats->mid_ppdu_route_change); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Tot. number of statuses", fw_stats->status_rcvd); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Extra frags on rings 0", fw_stats->r0_frags); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Extra frags on rings 1", fw_stats->r1_frags); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Extra frags on rings 2", fw_stats->r2_frags); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Extra frags on rings 3", fw_stats->r3_frags); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MSDUs delivered to HTT", fw_stats->htt_msdus); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDUs delivered to HTT", fw_stats->htt_mpdus); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MSDUs delivered to stack", fw_stats->loc_msdus); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDUs delivered to stack", fw_stats->loc_mpdus); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Oversized AMSUs", fw_stats->oversize_amsdu); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "PHY errors", fw_stats->phy_errs); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "PHY errors drops", fw_stats->phy_err_drop); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDU errors (FCS, MIC, ENC)", fw_stats->mpdu_errs); + + len += scnprintf(buf + len, buf_len - len, "\n"); + len += scnprintf(buf + len, buf_len - len, "%30s\n", + "ath10k PEER stats"); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "================="); + + for (i = 0; i < fw_stats->peers; i++) { + len += scnprintf(buf + len, buf_len - len, "%30s %pM\n", + "Peer MAC address", + fw_stats->peer_stat[i].peer_macaddr); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "Peer RSSI", fw_stats->peer_stat[i].peer_rssi); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "Peer TX rate", + fw_stats->peer_stat[i].peer_tx_rate); + len += scnprintf(buf + len, buf_len - len, "\n"); + } + + if (len > buf_len) + len = buf_len; + + ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); + + mutex_unlock(&ar->conf_mutex); + + kfree(buf); + return ret_cnt; +} + +static const struct file_operations fops_fw_stats = { + .read = ath10k_read_fw_stats, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +int ath10k_debug_create(struct ath10k *ar) +{ + ar->debug.debugfs_phy = debugfs_create_dir("ath10k", + ar->hw->wiphy->debugfsdir); + + if (!ar->debug.debugfs_phy) + return -ENOMEM; + + init_completion(&ar->debug.event_stats_compl); + + debugfs_create_file("fw_stats", S_IRUSR, ar->debug.debugfs_phy, ar, + &fops_fw_stats); + + debugfs_create_file("wmi_services", S_IRUSR, ar->debug.debugfs_phy, ar, + &fops_wmi_services); + + return 0; +} +#endif /* CONFIG_ATH10K_DEBUGFS */ + +#ifdef CONFIG_ATH10K_DEBUG +void ath10k_dbg(enum ath10k_debug_mask mask, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + if (ath10k_debug_mask & mask) + ath10k_printk(KERN_DEBUG, "%pV", &vaf); + + trace_ath10k_log_dbg(mask, &vaf); + + va_end(args); +} +EXPORT_SYMBOL(ath10k_dbg); + +void ath10k_dbg_dump(enum ath10k_debug_mask mask, + const char *msg, const char *prefix, + const void *buf, size_t len) +{ + if (ath10k_debug_mask & mask) { + if (msg) + ath10k_dbg(mask, "%s\n", msg); + + print_hex_dump_bytes(prefix, DUMP_PREFIX_OFFSET, buf, len); + } + + /* tracing code doesn't like null strings :/ */ + trace_ath10k_log_dbg_dump(msg ? msg : "", prefix ? prefix : "", + buf, len); +} +EXPORT_SYMBOL(ath10k_dbg_dump); + +#endif /* CONFIG_ATH10K_DEBUG */ diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h new file mode 100644 index 000000000000..168140c54028 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/debug.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _DEBUG_H_ +#define _DEBUG_H_ + +#include +#include "trace.h" + +enum ath10k_debug_mask { + ATH10K_DBG_PCI = 0x00000001, + ATH10K_DBG_WMI = 0x00000002, + ATH10K_DBG_HTC = 0x00000004, + ATH10K_DBG_HTT = 0x00000008, + ATH10K_DBG_MAC = 0x00000010, + ATH10K_DBG_CORE = 0x00000020, + ATH10K_DBG_PCI_DUMP = 0x00000040, + ATH10K_DBG_HTT_DUMP = 0x00000080, + ATH10K_DBG_MGMT = 0x00000100, + ATH10K_DBG_DATA = 0x00000200, + ATH10K_DBG_ANY = 0xffffffff, +}; + +extern unsigned int ath10k_debug_mask; + +extern __printf(1, 2) int ath10k_info(const char *fmt, ...); +extern __printf(1, 2) int ath10k_err(const char *fmt, ...); +extern __printf(1, 2) int ath10k_warn(const char *fmt, ...); + +#ifdef CONFIG_ATH10K_DEBUGFS +int ath10k_debug_create(struct ath10k *ar); +void ath10k_debug_read_service_map(struct ath10k *ar, + void *service_map, + size_t map_size); +void ath10k_debug_read_target_stats(struct ath10k *ar, + struct wmi_stats_event *ev); + +#else +static inline int ath10k_debug_create(struct ath10k *ar) +{ + return 0; +} + +static inline void ath10k_debug_read_service_map(struct ath10k *ar, + void *service_map, + size_t map_size) +{ +} + +static inline void ath10k_debug_read_target_stats(struct ath10k *ar, + struct wmi_stats_event *ev) +{ +} +#endif /* CONFIG_ATH10K_DEBUGFS */ + +#ifdef CONFIG_ATH10K_DEBUG +extern __printf(2, 3) void ath10k_dbg(enum ath10k_debug_mask mask, + const char *fmt, ...); +void ath10k_dbg_dump(enum ath10k_debug_mask mask, + const char *msg, const char *prefix, + const void *buf, size_t len); +#else /* CONFIG_ATH10K_DEBUG */ + +static inline int ath10k_dbg(enum ath10k_debug_mask dbg_mask, + const char *fmt, ...) +{ + return 0; +} + +static inline void ath10k_dbg_dump(enum ath10k_debug_mask mask, + const char *msg, const char *prefix, + const void *buf, size_t len) +{ +} +#endif /* CONFIG_ATH10K_DEBUG */ +#endif /* _DEBUG_H_ */ diff --git a/drivers/net/wireless/ath/ath10k/hif.h b/drivers/net/wireless/ath/ath10k/hif.h new file mode 100644 index 000000000000..73a24d44d1b4 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/hif.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HIF_H_ +#define _HIF_H_ + +#include +#include "core.h" + +struct ath10k_hif_cb { + int (*tx_completion)(struct ath10k *ar, + struct sk_buff *wbuf, + unsigned transfer_id); + int (*rx_completion)(struct ath10k *ar, + struct sk_buff *wbuf, + u8 pipe_id); +}; + +struct ath10k_hif_ops { + /* Send the head of a buffer to HIF for transmission to the target. */ + int (*send_head)(struct ath10k *ar, u8 pipe_id, + unsigned int transfer_id, + unsigned int nbytes, + struct sk_buff *buf); + + /* + * API to handle HIF-specific BMI message exchanges, this API is + * synchronous and only allowed to be called from a context that + * can block (sleep) + */ + int (*exchange_bmi_msg)(struct ath10k *ar, + void *request, u32 request_len, + void *response, u32 *response_len); + + int (*start)(struct ath10k *ar); + + void (*stop)(struct ath10k *ar); + + int (*map_service_to_pipe)(struct ath10k *ar, u16 service_id, + u8 *ul_pipe, u8 *dl_pipe, + int *ul_is_polled, int *dl_is_polled); + + void (*get_default_pipe)(struct ath10k *ar, u8 *ul_pipe, u8 *dl_pipe); + + /* + * Check if prior sends have completed. + * + * Check whether the pipe in question has any completed + * sends that have not yet been processed. + * This function is only relevant for HIF pipes that are configured + * to be polled rather than interrupt-driven. + */ + void (*send_complete_check)(struct ath10k *ar, u8 pipe_id, int force); + + void (*init)(struct ath10k *ar, + struct ath10k_hif_cb *callbacks); + + u16 (*get_free_queue_number)(struct ath10k *ar, u8 pipe_id); +}; + + +static inline int ath10k_hif_send_head(struct ath10k *ar, u8 pipe_id, + unsigned int transfer_id, + unsigned int nbytes, + struct sk_buff *buf) +{ + return ar->hif.ops->send_head(ar, pipe_id, transfer_id, nbytes, buf); +} + +static inline int ath10k_hif_exchange_bmi_msg(struct ath10k *ar, + void *request, u32 request_len, + void *response, u32 *response_len) +{ + return ar->hif.ops->exchange_bmi_msg(ar, request, request_len, + response, response_len); +} + +static inline int ath10k_hif_start(struct ath10k *ar) +{ + return ar->hif.ops->start(ar); +} + +static inline void ath10k_hif_stop(struct ath10k *ar) +{ + return ar->hif.ops->stop(ar); +} + +static inline int ath10k_hif_map_service_to_pipe(struct ath10k *ar, + u16 service_id, + u8 *ul_pipe, u8 *dl_pipe, + int *ul_is_polled, + int *dl_is_polled) +{ + return ar->hif.ops->map_service_to_pipe(ar, service_id, + ul_pipe, dl_pipe, + ul_is_polled, dl_is_polled); +} + +static inline void ath10k_hif_get_default_pipe(struct ath10k *ar, + u8 *ul_pipe, u8 *dl_pipe) +{ + ar->hif.ops->get_default_pipe(ar, ul_pipe, dl_pipe); +} + +static inline void ath10k_hif_send_complete_check(struct ath10k *ar, + u8 pipe_id, int force) +{ + ar->hif.ops->send_complete_check(ar, pipe_id, force); +} + +static inline void ath10k_hif_init(struct ath10k *ar, + struct ath10k_hif_cb *callbacks) +{ + ar->hif.ops->init(ar, callbacks); +} + +static inline u16 ath10k_hif_get_free_queue_number(struct ath10k *ar, + u8 pipe_id) +{ + return ar->hif.ops->get_free_queue_number(ar, pipe_id); +} + +#endif /* _HIF_H_ */ diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c new file mode 100644 index 000000000000..74363c949392 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/htc.c @@ -0,0 +1,1000 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "core.h" +#include "hif.h" +#include "debug.h" + +/********/ +/* Send */ +/********/ + +static inline void ath10k_htc_send_complete_check(struct ath10k_htc_ep *ep, + int force) +{ + /* + * Check whether HIF has any prior sends that have finished, + * have not had the post-processing done. + */ + ath10k_hif_send_complete_check(ep->htc->ar, ep->ul_pipe_id, force); +} + +static void ath10k_htc_control_tx_complete(struct ath10k *ar, + struct sk_buff *skb) +{ + kfree_skb(skb); +} + +static struct sk_buff *ath10k_htc_build_tx_ctrl_skb(void *ar) +{ + struct sk_buff *skb; + struct ath10k_skb_cb *skb_cb; + + skb = dev_alloc_skb(ATH10K_HTC_CONTROL_BUFFER_SIZE); + if (!skb) { + ath10k_warn("Unable to allocate ctrl skb\n"); + return NULL; + } + + skb_reserve(skb, 20); /* FIXME: why 20 bytes? */ + WARN_ONCE((unsigned long)skb->data & 3, "unaligned skb"); + + skb_cb = ATH10K_SKB_CB(skb); + memset(skb_cb, 0, sizeof(*skb_cb)); + + ath10k_dbg(ATH10K_DBG_HTC, "%s: skb %p\n", __func__, skb); + return skb; +} + +static inline void ath10k_htc_restore_tx_skb(struct ath10k_htc *htc, + struct sk_buff *skb) +{ + ath10k_skb_unmap(htc->ar->dev, skb); + skb_pull(skb, sizeof(struct ath10k_htc_hdr)); +} + +static void ath10k_htc_notify_tx_completion(struct ath10k_htc_ep *ep, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_HTC, "%s: ep %d skb %p\n", __func__, + ep->eid, skb); + + ath10k_htc_restore_tx_skb(ep->htc, skb); + + if (!ep->ep_ops.ep_tx_complete) { + ath10k_warn("no tx handler for eid %d\n", ep->eid); + dev_kfree_skb_any(skb); + return; + } + + ep->ep_ops.ep_tx_complete(ep->htc->ar, skb); +} + +/* assumes tx_lock is held */ +static bool ath10k_htc_ep_need_credit_update(struct ath10k_htc_ep *ep) +{ + if (!ep->tx_credit_flow_enabled) + return false; + if (ep->tx_credits >= ep->tx_credits_per_max_message) + return false; + + ath10k_dbg(ATH10K_DBG_HTC, "HTC: endpoint %d needs credit update\n", + ep->eid); + return true; +} + +static void ath10k_htc_prepare_tx_skb(struct ath10k_htc_ep *ep, + struct sk_buff *skb) +{ + struct ath10k_htc_hdr *hdr; + + hdr = (struct ath10k_htc_hdr *)skb->data; + memset(hdr, 0, sizeof(*hdr)); + + hdr->eid = ep->eid; + hdr->len = __cpu_to_le16(skb->len - sizeof(*hdr)); + + spin_lock_bh(&ep->htc->tx_lock); + hdr->seq_no = ep->seq_no++; + + if (ath10k_htc_ep_need_credit_update(ep)) + hdr->flags |= ATH10K_HTC_FLAG_NEED_CREDIT_UPDATE; + + spin_unlock_bh(&ep->htc->tx_lock); +} + +static int ath10k_htc_issue_skb(struct ath10k_htc *htc, + struct ath10k_htc_ep *ep, + struct sk_buff *skb, + u8 credits) +{ + struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb); + int ret; + + ath10k_dbg(ATH10K_DBG_HTC, "%s: ep %d skb %p\n", __func__, + ep->eid, skb); + + ath10k_htc_prepare_tx_skb(ep, skb); + + ret = ath10k_skb_map(htc->ar->dev, skb); + if (ret) + goto err; + + ret = ath10k_hif_send_head(htc->ar, + ep->ul_pipe_id, + ep->eid, + skb->len, + skb); + if (unlikely(ret)) + goto err; + + return 0; +err: + ath10k_warn("HTC issue failed: %d\n", ret); + + spin_lock_bh(&htc->tx_lock); + ep->tx_credits += credits; + spin_unlock_bh(&htc->tx_lock); + + /* this is the simplest way to handle out-of-resources for non-credit + * based endpoints. credit based endpoints can still get -ENOSR, but + * this is highly unlikely as credit reservation should prevent that */ + if (ret == -ENOSR) { + spin_lock_bh(&htc->tx_lock); + __skb_queue_head(&ep->tx_queue, skb); + spin_unlock_bh(&htc->tx_lock); + + return ret; + } + + skb_cb->is_aborted = true; + ath10k_htc_notify_tx_completion(ep, skb); + + return ret; +} + +static struct sk_buff *ath10k_htc_get_skb_credit_based(struct ath10k_htc *htc, + struct ath10k_htc_ep *ep, + u8 *credits) +{ + struct sk_buff *skb; + struct ath10k_skb_cb *skb_cb; + int credits_required; + int remainder; + unsigned int transfer_len; + + lockdep_assert_held(&htc->tx_lock); + + skb = __skb_dequeue(&ep->tx_queue); + if (!skb) + return NULL; + + skb_cb = ATH10K_SKB_CB(skb); + transfer_len = skb->len; + + if (likely(transfer_len <= htc->target_credit_size)) { + credits_required = 1; + } else { + /* figure out how many credits this message requires */ + credits_required = transfer_len / htc->target_credit_size; + remainder = transfer_len % htc->target_credit_size; + + if (remainder) + credits_required++; + } + + ath10k_dbg(ATH10K_DBG_HTC, "Credits required %d got %d\n", + credits_required, ep->tx_credits); + + if (ep->tx_credits < credits_required) { + __skb_queue_head(&ep->tx_queue, skb); + return NULL; + } + + ep->tx_credits -= credits_required; + *credits = credits_required; + return skb; +} + +static void ath10k_htc_send_work(struct work_struct *work) +{ + struct ath10k_htc_ep *ep = container_of(work, + struct ath10k_htc_ep, send_work); + struct ath10k_htc *htc = ep->htc; + struct sk_buff *skb; + u8 credits = 0; + int ret; + + while (true) { + if (ep->ul_is_polled) + ath10k_htc_send_complete_check(ep, 0); + + spin_lock_bh(&htc->tx_lock); + if (ep->tx_credit_flow_enabled) + skb = ath10k_htc_get_skb_credit_based(htc, ep, + &credits); + else + skb = __skb_dequeue(&ep->tx_queue); + spin_unlock_bh(&htc->tx_lock); + + if (!skb) + break; + + ret = ath10k_htc_issue_skb(htc, ep, skb, credits); + if (ret == -ENOSR) + break; + } +} + +int ath10k_htc_send(struct ath10k_htc *htc, + enum ath10k_htc_ep_id eid, + struct sk_buff *skb) +{ + struct ath10k_htc_ep *ep = &htc->endpoint[eid]; + + if (eid >= ATH10K_HTC_EP_COUNT) { + ath10k_warn("Invalid endpoint id: %d\n", eid); + return -ENOENT; + } + + skb_push(skb, sizeof(struct ath10k_htc_hdr)); + + spin_lock_bh(&htc->tx_lock); + __skb_queue_tail(&ep->tx_queue, skb); + spin_unlock_bh(&htc->tx_lock); + + queue_work(htc->ar->workqueue, &ep->send_work); + return 0; +} + +static int ath10k_htc_tx_completion_handler(struct ath10k *ar, + struct sk_buff *skb, + unsigned int eid) +{ + struct ath10k_htc *htc = ar->htc; + struct ath10k_htc_ep *ep = &htc->endpoint[eid]; + bool stopping; + + ath10k_htc_notify_tx_completion(ep, skb); + /* the skb now belongs to the completion handler */ + + spin_lock_bh(&htc->tx_lock); + stopping = htc->stopping; + spin_unlock_bh(&htc->tx_lock); + + if (!ep->tx_credit_flow_enabled && !stopping) + /* + * note: when using TX credit flow, the re-checking of + * queues happens when credits flow back from the target. + * in the non-TX credit case, we recheck after the packet + * completes + */ + queue_work(ar->workqueue, &ep->send_work); + + return 0; +} + +/* flush endpoint TX queue */ +static void ath10k_htc_flush_endpoint_tx(struct ath10k_htc *htc, + struct ath10k_htc_ep *ep) +{ + struct sk_buff *skb; + struct ath10k_skb_cb *skb_cb; + + spin_lock_bh(&htc->tx_lock); + for (;;) { + skb = __skb_dequeue(&ep->tx_queue); + if (!skb) + break; + + skb_cb = ATH10K_SKB_CB(skb); + skb_cb->is_aborted = true; + ath10k_htc_notify_tx_completion(ep, skb); + } + spin_unlock_bh(&htc->tx_lock); + + cancel_work_sync(&ep->send_work); +} + +/***********/ +/* Receive */ +/***********/ + +static void +ath10k_htc_process_credit_report(struct ath10k_htc *htc, + const struct ath10k_htc_credit_report *report, + int len, + enum ath10k_htc_ep_id eid) +{ + struct ath10k_htc_ep *ep; + int i, n_reports; + + if (len % sizeof(*report)) + ath10k_warn("Uneven credit report len %d", len); + + n_reports = len / sizeof(*report); + + spin_lock_bh(&htc->tx_lock); + for (i = 0; i < n_reports; i++, report++) { + if (report->eid >= ATH10K_HTC_EP_COUNT) + break; + + ath10k_dbg(ATH10K_DBG_HTC, "ep %d got %d credits\n", + report->eid, report->credits); + + ep = &htc->endpoint[report->eid]; + ep->tx_credits += report->credits; + + if (ep->tx_credits && !skb_queue_empty(&ep->tx_queue)) + queue_work(htc->ar->workqueue, &ep->send_work); + } + spin_unlock_bh(&htc->tx_lock); +} + +static int ath10k_htc_process_trailer(struct ath10k_htc *htc, + u8 *buffer, + int length, + enum ath10k_htc_ep_id src_eid) +{ + int status = 0; + struct ath10k_htc_record *record; + u8 *orig_buffer; + int orig_length; + size_t len; + + orig_buffer = buffer; + orig_length = length; + + while (length > 0) { + record = (struct ath10k_htc_record *)buffer; + + if (length < sizeof(record->hdr)) { + status = -EINVAL; + break; + } + + if (record->hdr.len > length) { + /* no room left in buffer for record */ + ath10k_warn("Invalid record length: %d\n", + record->hdr.len); + status = -EINVAL; + break; + } + + switch (record->hdr.id) { + case ATH10K_HTC_RECORD_CREDITS: + len = sizeof(struct ath10k_htc_credit_report); + if (record->hdr.len < len) { + ath10k_warn("Credit report too long\n"); + status = -EINVAL; + break; + } + ath10k_htc_process_credit_report(htc, + record->credit_report, + record->hdr.len, + src_eid); + break; + default: + ath10k_warn("Unhandled record: id:%d length:%d\n", + record->hdr.id, record->hdr.len); + break; + } + + if (status) + break; + + /* multiple records may be present in a trailer */ + buffer += sizeof(record->hdr) + record->hdr.len; + length -= sizeof(record->hdr) + record->hdr.len; + } + + if (status) + ath10k_dbg_dump(ATH10K_DBG_HTC, "htc rx bad trailer", "", + orig_buffer, orig_length); + + return status; +} + +static int ath10k_htc_rx_completion_handler(struct ath10k *ar, + struct sk_buff *skb, + u8 pipe_id) +{ + int status = 0; + struct ath10k_htc *htc = ar->htc; + struct ath10k_htc_hdr *hdr; + struct ath10k_htc_ep *ep; + u16 payload_len; + u32 trailer_len = 0; + size_t min_len; + u8 eid; + bool trailer_present; + + hdr = (struct ath10k_htc_hdr *)skb->data; + skb_pull(skb, sizeof(*hdr)); + + eid = hdr->eid; + + if (eid >= ATH10K_HTC_EP_COUNT) { + ath10k_warn("HTC Rx: invalid eid %d\n", eid); + ath10k_dbg_dump(ATH10K_DBG_HTC, "htc bad header", "", + hdr, sizeof(*hdr)); + status = -EINVAL; + goto out; + } + + ep = &htc->endpoint[eid]; + + /* + * If this endpoint that received a message from the target has + * a to-target HIF pipe whose send completions are polled rather + * than interrupt-driven, this is a good point to ask HIF to check + * whether it has any completed sends to handle. + */ + if (ep->ul_is_polled) + ath10k_htc_send_complete_check(ep, 1); + + payload_len = __le16_to_cpu(hdr->len); + + if (payload_len + sizeof(*hdr) > ATH10K_HTC_MAX_LEN) { + ath10k_warn("HTC rx frame too long, len: %zu\n", + payload_len + sizeof(*hdr)); + ath10k_dbg_dump(ATH10K_DBG_HTC, "htc bad rx pkt len", "", + hdr, sizeof(*hdr)); + status = -EINVAL; + goto out; + } + + if (skb->len < payload_len) { + ath10k_dbg(ATH10K_DBG_HTC, + "HTC Rx: insufficient length, got %d, expected %d\n", + skb->len, payload_len); + ath10k_dbg_dump(ATH10K_DBG_HTC, "htc bad rx pkt len", + "", hdr, sizeof(*hdr)); + status = -EINVAL; + goto out; + } + + /* get flags to check for trailer */ + trailer_present = hdr->flags & ATH10K_HTC_FLAG_TRAILER_PRESENT; + if (trailer_present) { + u8 *trailer; + + trailer_len = hdr->trailer_len; + min_len = sizeof(struct ath10k_ath10k_htc_record_hdr); + + if ((trailer_len < min_len) || + (trailer_len > payload_len)) { + ath10k_warn("Invalid trailer length: %d\n", + trailer_len); + status = -EPROTO; + goto out; + } + + trailer = (u8 *)hdr; + trailer += sizeof(*hdr); + trailer += payload_len; + trailer -= trailer_len; + status = ath10k_htc_process_trailer(htc, trailer, + trailer_len, hdr->eid); + if (status) + goto out; + + skb_trim(skb, skb->len - trailer_len); + } + + if (((int)payload_len - (int)trailer_len) <= 0) + /* zero length packet with trailer data, just drop these */ + goto out; + + if (eid == ATH10K_HTC_EP_0) { + struct ath10k_htc_msg *msg = (struct ath10k_htc_msg *)skb->data; + + switch (__le16_to_cpu(msg->hdr.message_id)) { + default: + /* handle HTC control message */ + if (completion_done(&htc->ctl_resp)) { + /* + * this is a fatal error, target should not be + * sending unsolicited messages on the ep 0 + */ + ath10k_warn("HTC rx ctrl still processing\n"); + status = -EINVAL; + complete(&htc->ctl_resp); + goto out; + } + + htc->control_resp_len = + min_t(int, skb->len, + ATH10K_HTC_MAX_CTRL_MSG_LEN); + + memcpy(htc->control_resp_buffer, skb->data, + htc->control_resp_len); + + complete(&htc->ctl_resp); + break; + case ATH10K_HTC_MSG_SEND_SUSPEND_COMPLETE: + htc->htc_ops.target_send_suspend_complete(ar); + } + goto out; + } + + ath10k_dbg(ATH10K_DBG_HTC, "htc rx completion ep %d skb %p\n", + eid, skb); + ep->ep_ops.ep_rx_complete(ar, skb); + + /* skb is now owned by the rx completion handler */ + skb = NULL; +out: + kfree_skb(skb); + + return status; +} + +static void ath10k_htc_control_rx_complete(struct ath10k *ar, + struct sk_buff *skb) +{ + /* This is unexpected. FW is not supposed to send regular rx on this + * endpoint. */ + ath10k_warn("unexpected htc rx\n"); + kfree_skb(skb); +} + +/***************/ +/* Init/Deinit */ +/***************/ + +static const char *htc_service_name(enum ath10k_htc_svc_id id) +{ + switch (id) { + case ATH10K_HTC_SVC_ID_RESERVED: + return "Reserved"; + case ATH10K_HTC_SVC_ID_RSVD_CTRL: + return "Control"; + case ATH10K_HTC_SVC_ID_WMI_CONTROL: + return "WMI"; + case ATH10K_HTC_SVC_ID_WMI_DATA_BE: + return "DATA BE"; + case ATH10K_HTC_SVC_ID_WMI_DATA_BK: + return "DATA BK"; + case ATH10K_HTC_SVC_ID_WMI_DATA_VI: + return "DATA VI"; + case ATH10K_HTC_SVC_ID_WMI_DATA_VO: + return "DATA VO"; + case ATH10K_HTC_SVC_ID_NMI_CONTROL: + return "NMI Control"; + case ATH10K_HTC_SVC_ID_NMI_DATA: + return "NMI Data"; + case ATH10K_HTC_SVC_ID_HTT_DATA_MSG: + return "HTT Data"; + case ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS: + return "RAW"; + } + + return "Unknown"; +} + +static void ath10k_htc_reset_endpoint_states(struct ath10k_htc *htc) +{ + struct ath10k_htc_ep *ep; + int i; + + for (i = ATH10K_HTC_EP_0; i < ATH10K_HTC_EP_COUNT; i++) { + ep = &htc->endpoint[i]; + ep->service_id = ATH10K_HTC_SVC_ID_UNUSED; + ep->max_ep_message_len = 0; + ep->max_tx_queue_depth = 0; + ep->eid = i; + skb_queue_head_init(&ep->tx_queue); + ep->htc = htc; + ep->tx_credit_flow_enabled = true; + INIT_WORK(&ep->send_work, ath10k_htc_send_work); + } +} + +static void ath10k_htc_setup_target_buffer_assignments(struct ath10k_htc *htc) +{ + struct ath10k_htc_svc_tx_credits *entry; + + entry = &htc->service_tx_alloc[0]; + + /* + * for PCIE allocate all credists/HTC buffers to WMI. + * no buffers are used/required for data. data always + * remains on host. + */ + entry++; + entry->service_id = ATH10K_HTC_SVC_ID_WMI_CONTROL; + entry->credit_allocation = htc->total_transmit_credits; +} + +static u8 ath10k_htc_get_credit_allocation(struct ath10k_htc *htc, + u16 service_id) +{ + u8 allocation = 0; + int i; + + for (i = 0; i < ATH10K_HTC_EP_COUNT; i++) { + if (htc->service_tx_alloc[i].service_id == service_id) + allocation = + htc->service_tx_alloc[i].credit_allocation; + } + + return allocation; +} + +int ath10k_htc_wait_target(struct ath10k_htc *htc) +{ + int status = 0; + struct ath10k_htc_svc_conn_req conn_req; + struct ath10k_htc_svc_conn_resp conn_resp; + struct ath10k_htc_msg *msg; + u16 message_id; + u16 credit_count; + u16 credit_size; + + INIT_COMPLETION(htc->ctl_resp); + + status = ath10k_hif_start(htc->ar); + if (status) { + ath10k_err("could not start HIF (%d)\n", status); + goto err_start; + } + + status = wait_for_completion_timeout(&htc->ctl_resp, + ATH10K_HTC_WAIT_TIMEOUT_HZ); + if (status <= 0) { + if (status == 0) + status = -ETIMEDOUT; + + ath10k_err("ctl_resp never came in (%d)\n", status); + goto err_target; + } + + if (htc->control_resp_len < sizeof(msg->hdr) + sizeof(msg->ready)) { + ath10k_err("Invalid HTC ready msg len:%d\n", + htc->control_resp_len); + + status = -ECOMM; + goto err_target; + } + + msg = (struct ath10k_htc_msg *)htc->control_resp_buffer; + message_id = __le16_to_cpu(msg->hdr.message_id); + credit_count = __le16_to_cpu(msg->ready.credit_count); + credit_size = __le16_to_cpu(msg->ready.credit_size); + + if (message_id != ATH10K_HTC_MSG_READY_ID) { + ath10k_err("Invalid HTC ready msg: 0x%x\n", message_id); + status = -ECOMM; + goto err_target; + } + + htc->total_transmit_credits = credit_count; + htc->target_credit_size = credit_size; + + ath10k_dbg(ATH10K_DBG_HTC, + "Target ready! transmit resources: %d size:%d\n", + htc->total_transmit_credits, + htc->target_credit_size); + + if ((htc->total_transmit_credits == 0) || + (htc->target_credit_size == 0)) { + status = -ECOMM; + ath10k_err("Invalid credit size received\n"); + goto err_target; + } + + ath10k_htc_setup_target_buffer_assignments(htc); + + /* setup our pseudo HTC control endpoint connection */ + memset(&conn_req, 0, sizeof(conn_req)); + memset(&conn_resp, 0, sizeof(conn_resp)); + conn_req.ep_ops.ep_tx_complete = ath10k_htc_control_tx_complete; + conn_req.ep_ops.ep_rx_complete = ath10k_htc_control_rx_complete; + conn_req.max_send_queue_depth = ATH10K_NUM_CONTROL_TX_BUFFERS; + conn_req.service_id = ATH10K_HTC_SVC_ID_RSVD_CTRL; + + /* connect fake service */ + status = ath10k_htc_connect_service(htc, &conn_req, &conn_resp); + if (status) { + ath10k_err("could not connect to htc service (%d)\n", status); + goto err_target; + } + + return 0; +err_target: + ath10k_hif_stop(htc->ar); +err_start: + return status; +} + +int ath10k_htc_connect_service(struct ath10k_htc *htc, + struct ath10k_htc_svc_conn_req *conn_req, + struct ath10k_htc_svc_conn_resp *conn_resp) +{ + struct ath10k_htc_msg *msg; + struct ath10k_htc_conn_svc *req_msg; + struct ath10k_htc_conn_svc_response resp_msg_dummy; + struct ath10k_htc_conn_svc_response *resp_msg = &resp_msg_dummy; + enum ath10k_htc_ep_id assigned_eid = ATH10K_HTC_EP_COUNT; + struct ath10k_htc_ep *ep; + struct sk_buff *skb; + unsigned int max_msg_size = 0; + int length, status; + bool disable_credit_flow_ctrl = false; + u16 message_id, service_id, flags = 0; + u8 tx_alloc = 0; + + /* special case for HTC pseudo control service */ + if (conn_req->service_id == ATH10K_HTC_SVC_ID_RSVD_CTRL) { + disable_credit_flow_ctrl = true; + assigned_eid = ATH10K_HTC_EP_0; + max_msg_size = ATH10K_HTC_MAX_CTRL_MSG_LEN; + memset(&resp_msg_dummy, 0, sizeof(resp_msg_dummy)); + goto setup; + } + + tx_alloc = ath10k_htc_get_credit_allocation(htc, + conn_req->service_id); + if (!tx_alloc) + ath10k_warn("HTC Service %s does not allocate target credits\n", + htc_service_name(conn_req->service_id)); + + skb = ath10k_htc_build_tx_ctrl_skb(htc->ar); + if (!skb) { + ath10k_err("Failed to allocate HTC packet\n"); + return -ENOMEM; + } + + length = sizeof(msg->hdr) + sizeof(msg->connect_service); + skb_put(skb, length); + memset(skb->data, 0, length); + + msg = (struct ath10k_htc_msg *)skb->data; + msg->hdr.message_id = + __cpu_to_le16(ATH10K_HTC_MSG_CONNECT_SERVICE_ID); + + flags |= SM(tx_alloc, ATH10K_HTC_CONN_FLAGS_RECV_ALLOC); + + req_msg = &msg->connect_service; + req_msg->flags = __cpu_to_le16(flags); + req_msg->service_id = __cpu_to_le16(conn_req->service_id); + + /* Only enable credit flow control for WMI ctrl service */ + if (conn_req->service_id != ATH10K_HTC_SVC_ID_WMI_CONTROL) { + flags |= ATH10K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL; + disable_credit_flow_ctrl = true; + } + + INIT_COMPLETION(htc->ctl_resp); + + status = ath10k_htc_send(htc, ATH10K_HTC_EP_0, skb); + if (status) { + kfree_skb(skb); + return status; + } + + /* wait for response */ + status = wait_for_completion_timeout(&htc->ctl_resp, + ATH10K_HTC_CONN_SVC_TIMEOUT_HZ); + if (status <= 0) { + if (status == 0) + status = -ETIMEDOUT; + ath10k_err("Service connect timeout: %d\n", status); + return status; + } + + /* we controlled the buffer creation, it's aligned */ + msg = (struct ath10k_htc_msg *)htc->control_resp_buffer; + resp_msg = &msg->connect_service_response; + message_id = __le16_to_cpu(msg->hdr.message_id); + service_id = __le16_to_cpu(resp_msg->service_id); + + if ((message_id != ATH10K_HTC_MSG_CONNECT_SERVICE_RESP_ID) || + (htc->control_resp_len < sizeof(msg->hdr) + + sizeof(msg->connect_service_response))) { + ath10k_err("Invalid resp message ID 0x%x", message_id); + return -EPROTO; + } + + ath10k_dbg(ATH10K_DBG_HTC, + "HTC Service %s connect response: status: 0x%x, assigned ep: 0x%x\n", + htc_service_name(service_id), + resp_msg->status, resp_msg->eid); + + conn_resp->connect_resp_code = resp_msg->status; + + /* check response status */ + if (resp_msg->status != ATH10K_HTC_CONN_SVC_STATUS_SUCCESS) { + ath10k_err("HTC Service %s connect request failed: 0x%x)\n", + htc_service_name(service_id), + resp_msg->status); + return -EPROTO; + } + + assigned_eid = (enum ath10k_htc_ep_id)resp_msg->eid; + max_msg_size = __le16_to_cpu(resp_msg->max_msg_size); + +setup: + + if (assigned_eid >= ATH10K_HTC_EP_COUNT) + return -EPROTO; + + if (max_msg_size == 0) + return -EPROTO; + + ep = &htc->endpoint[assigned_eid]; + ep->eid = assigned_eid; + + if (ep->service_id != ATH10K_HTC_SVC_ID_UNUSED) + return -EPROTO; + + /* return assigned endpoint to caller */ + conn_resp->eid = assigned_eid; + conn_resp->max_msg_len = __le16_to_cpu(resp_msg->max_msg_size); + + /* setup the endpoint */ + ep->service_id = conn_req->service_id; + ep->max_tx_queue_depth = conn_req->max_send_queue_depth; + ep->max_ep_message_len = __le16_to_cpu(resp_msg->max_msg_size); + ep->tx_credits = tx_alloc; + ep->tx_credit_size = htc->target_credit_size; + ep->tx_credits_per_max_message = ep->max_ep_message_len / + htc->target_credit_size; + + if (ep->max_ep_message_len % htc->target_credit_size) + ep->tx_credits_per_max_message++; + + /* copy all the callbacks */ + ep->ep_ops = conn_req->ep_ops; + + status = ath10k_hif_map_service_to_pipe(htc->ar, + ep->service_id, + &ep->ul_pipe_id, + &ep->dl_pipe_id, + &ep->ul_is_polled, + &ep->dl_is_polled); + if (status) + return status; + + ath10k_dbg(ATH10K_DBG_HTC, + "HTC service: %s UL pipe: %d DL pipe: %d eid: %d ready\n", + htc_service_name(ep->service_id), ep->ul_pipe_id, + ep->dl_pipe_id, ep->eid); + + ath10k_dbg(ATH10K_DBG_HTC, + "EP %d UL polled: %d, DL polled: %d\n", + ep->eid, ep->ul_is_polled, ep->dl_is_polled); + + if (disable_credit_flow_ctrl && ep->tx_credit_flow_enabled) { + ep->tx_credit_flow_enabled = false; + ath10k_dbg(ATH10K_DBG_HTC, + "HTC service: %s eid: %d TX flow control disabled\n", + htc_service_name(ep->service_id), assigned_eid); + } + + return status; +} + +struct sk_buff *ath10k_htc_alloc_skb(int size) +{ + struct sk_buff *skb; + + skb = dev_alloc_skb(size + sizeof(struct ath10k_htc_hdr)); + if (!skb) { + ath10k_warn("could not allocate HTC tx skb\n"); + return NULL; + } + + skb_reserve(skb, sizeof(struct ath10k_htc_hdr)); + + /* FW/HTC requires 4-byte aligned streams */ + if (!IS_ALIGNED((unsigned long)skb->data, 4)) + ath10k_warn("Unaligned HTC tx skb\n"); + + return skb; +} + +int ath10k_htc_start(struct ath10k_htc *htc) +{ + struct sk_buff *skb; + int status = 0; + struct ath10k_htc_msg *msg; + + skb = ath10k_htc_build_tx_ctrl_skb(htc->ar); + if (!skb) + return -ENOMEM; + + skb_put(skb, sizeof(msg->hdr) + sizeof(msg->setup_complete_ext)); + memset(skb->data, 0, skb->len); + + msg = (struct ath10k_htc_msg *)skb->data; + msg->hdr.message_id = + __cpu_to_le16(ATH10K_HTC_MSG_SETUP_COMPLETE_EX_ID); + + ath10k_dbg(ATH10K_DBG_HTC, "HTC is using TX credit flow control\n"); + + status = ath10k_htc_send(htc, ATH10K_HTC_EP_0, skb); + if (status) { + kfree_skb(skb); + return status; + } + + return 0; +} + +/* + * stop HTC communications, i.e. stop interrupt reception, and flush all + * queued buffers + */ +void ath10k_htc_stop(struct ath10k_htc *htc) +{ + int i; + struct ath10k_htc_ep *ep; + + spin_lock_bh(&htc->tx_lock); + htc->stopping = true; + spin_unlock_bh(&htc->tx_lock); + + for (i = ATH10K_HTC_EP_0; i < ATH10K_HTC_EP_COUNT; i++) { + ep = &htc->endpoint[i]; + ath10k_htc_flush_endpoint_tx(htc, ep); + } + + ath10k_hif_stop(htc->ar); + ath10k_htc_reset_endpoint_states(htc); +} + +/* registered target arrival callback from the HIF layer */ +struct ath10k_htc *ath10k_htc_create(struct ath10k *ar, + struct ath10k_htc_ops *htc_ops) +{ + struct ath10k_hif_cb htc_callbacks; + struct ath10k_htc_ep *ep = NULL; + struct ath10k_htc *htc = NULL; + + /* FIXME: use struct ath10k instead */ + htc = kzalloc(sizeof(struct ath10k_htc), GFP_KERNEL); + if (!htc) + return ERR_PTR(-ENOMEM); + + spin_lock_init(&htc->tx_lock); + + memcpy(&htc->htc_ops, htc_ops, sizeof(struct ath10k_htc_ops)); + + ath10k_htc_reset_endpoint_states(htc); + + /* setup HIF layer callbacks */ + htc_callbacks.rx_completion = ath10k_htc_rx_completion_handler; + htc_callbacks.tx_completion = ath10k_htc_tx_completion_handler; + htc->ar = ar; + + /* Get HIF default pipe for HTC message exchange */ + ep = &htc->endpoint[ATH10K_HTC_EP_0]; + + ath10k_hif_init(ar, &htc_callbacks); + ath10k_hif_get_default_pipe(ar, &ep->ul_pipe_id, &ep->dl_pipe_id); + + init_completion(&htc->ctl_resp); + + return htc; +} + +void ath10k_htc_destroy(struct ath10k_htc *htc) +{ + kfree(htc); +} diff --git a/drivers/net/wireless/ath/ath10k/htc.h b/drivers/net/wireless/ath/ath10k/htc.h new file mode 100644 index 000000000000..fa45844b59fb --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/htc.h @@ -0,0 +1,368 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HTC_H_ +#define _HTC_H_ + +#include +#include +#include +#include +#include +#include + +struct ath10k; + +/****************/ +/* HTC protocol */ +/****************/ + +/* + * HTC - host-target control protocol + * + * tx packets are generally + * rx packets are more complex: + * + * The payload + trailer length is stored in len. + * To get payload-only length one needs to payload - trailer_len. + * + * Trailer contains (possibly) multiple . + * Each record is a id-len-value. + * + * HTC header flags, control_byte0, control_byte1 + * have different meaning depending whether its tx + * or rx. + * + * Alignment: htc_hdr, payload and trailer are + * 4-byte aligned. + */ + +enum ath10k_htc_tx_flags { + ATH10K_HTC_FLAG_NEED_CREDIT_UPDATE = 0x01, + ATH10K_HTC_FLAG_SEND_BUNDLE = 0x02 +}; + +enum ath10k_htc_rx_flags { + ATH10K_HTC_FLAG_TRAILER_PRESENT = 0x02, + ATH10K_HTC_FLAG_BUNDLE_MASK = 0xF0 +}; + +struct ath10k_htc_hdr { + u8 eid; /* @enum ath10k_htc_ep_id */ + u8 flags; /* @enum ath10k_htc_tx_flags, ath10k_htc_rx_flags */ + __le16 len; + union { + u8 trailer_len; /* for rx */ + u8 control_byte0; + } __packed; + union { + u8 seq_no; /* for tx */ + u8 control_byte1; + } __packed; + u8 pad0; + u8 pad1; +} __packed __aligned(4); + +enum ath10k_ath10k_htc_msg_id { + ATH10K_HTC_MSG_READY_ID = 1, + ATH10K_HTC_MSG_CONNECT_SERVICE_ID = 2, + ATH10K_HTC_MSG_CONNECT_SERVICE_RESP_ID = 3, + ATH10K_HTC_MSG_SETUP_COMPLETE_ID = 4, + ATH10K_HTC_MSG_SETUP_COMPLETE_EX_ID = 5, + ATH10K_HTC_MSG_SEND_SUSPEND_COMPLETE = 6 +}; + +enum ath10k_htc_version { + ATH10K_HTC_VERSION_2P0 = 0x00, /* 2.0 */ + ATH10K_HTC_VERSION_2P1 = 0x01, /* 2.1 */ +}; + +enum ath10k_htc_conn_flags { + ATH10K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_ONE_FOURTH = 0x0, + ATH10K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_ONE_HALF = 0x1, + ATH10K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_THREE_FOURTHS = 0x2, + ATH10K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_UNITY = 0x3, +#define ATH10K_HTC_CONN_FLAGS_THRESHOLD_LEVEL_MASK 0x3 + ATH10K_HTC_CONN_FLAGS_REDUCE_CREDIT_DRIBBLE = 1 << 2, + ATH10K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL = 1 << 3 +#define ATH10K_HTC_CONN_FLAGS_RECV_ALLOC_MASK 0xFF00 +#define ATH10K_HTC_CONN_FLAGS_RECV_ALLOC_LSB 8 +}; + +enum ath10k_htc_conn_svc_status { + ATH10K_HTC_CONN_SVC_STATUS_SUCCESS = 0, + ATH10K_HTC_CONN_SVC_STATUS_NOT_FOUND = 1, + ATH10K_HTC_CONN_SVC_STATUS_FAILED = 2, + ATH10K_HTC_CONN_SVC_STATUS_NO_RESOURCES = 3, + ATH10K_HTC_CONN_SVC_STATUS_NO_MORE_EP = 4 +}; + +struct ath10k_ath10k_htc_msg_hdr { + __le16 message_id; /* @enum htc_message_id */ +} __packed; + +struct ath10k_htc_unknown { + u8 pad0; + u8 pad1; +} __packed; + +struct ath10k_htc_ready { + __le16 credit_count; + __le16 credit_size; + u8 max_endpoints; + u8 pad0; +} __packed; + +struct ath10k_htc_ready_extended { + struct ath10k_htc_ready base; + u8 htc_version; /* @enum ath10k_htc_version */ + u8 max_msgs_per_htc_bundle; + u8 pad0; + u8 pad1; +} __packed; + +struct ath10k_htc_conn_svc { + __le16 service_id; + __le16 flags; /* @enum ath10k_htc_conn_flags */ + u8 pad0; + u8 pad1; +} __packed; + +struct ath10k_htc_conn_svc_response { + __le16 service_id; + u8 status; /* @enum ath10k_htc_conn_svc_status */ + u8 eid; + __le16 max_msg_size; +} __packed; + +struct ath10k_htc_setup_complete_extended { + u8 pad0; + u8 pad1; + __le32 flags; /* @enum htc_setup_complete_flags */ + u8 max_msgs_per_bundled_recv; + u8 pad2; + u8 pad3; + u8 pad4; +} __packed; + +struct ath10k_htc_msg { + struct ath10k_ath10k_htc_msg_hdr hdr; + union { + /* host-to-target */ + struct ath10k_htc_conn_svc connect_service; + struct ath10k_htc_ready ready; + struct ath10k_htc_ready_extended ready_ext; + struct ath10k_htc_unknown unknown; + struct ath10k_htc_setup_complete_extended setup_complete_ext; + + /* target-to-host */ + struct ath10k_htc_conn_svc_response connect_service_response; + }; +} __packed __aligned(4); + +enum ath10k_ath10k_htc_record_id { + ATH10K_HTC_RECORD_NULL = 0, + ATH10K_HTC_RECORD_CREDITS = 1 +}; + +struct ath10k_ath10k_htc_record_hdr { + u8 id; /* @enum ath10k_ath10k_htc_record_id */ + u8 len; + u8 pad0; + u8 pad1; +} __packed; + +struct ath10k_htc_credit_report { + u8 eid; /* @enum ath10k_htc_ep_id */ + u8 credits; + u8 pad0; + u8 pad1; +} __packed; + +struct ath10k_htc_record { + struct ath10k_ath10k_htc_record_hdr hdr; + union { + struct ath10k_htc_credit_report credit_report[0]; + u8 pauload[0]; + }; +} __packed __aligned(4); + +/* + * note: the trailer offset is dynamic depending + * on payload length. this is only a struct layout draft + */ +struct ath10k_htc_frame { + struct ath10k_htc_hdr hdr; + union { + struct ath10k_htc_msg msg; + u8 payload[0]; + }; + struct ath10k_htc_record trailer[0]; +} __packed __aligned(4); + + +/*******************/ +/* Host-side stuff */ +/*******************/ + +enum ath10k_htc_svc_gid { + ATH10K_HTC_SVC_GRP_RSVD = 0, + ATH10K_HTC_SVC_GRP_WMI = 1, + ATH10K_HTC_SVC_GRP_NMI = 2, + ATH10K_HTC_SVC_GRP_HTT = 3, + + ATH10K_HTC_SVC_GRP_TEST = 254, + ATH10K_HTC_SVC_GRP_LAST = 255, +}; + +#define SVC(group, idx) \ + (int)(((int)(group) << 8) | (int)(idx)) + +enum ath10k_htc_svc_id { + /* NOTE: service ID of 0x0000 is reserved and should never be used */ + ATH10K_HTC_SVC_ID_RESERVED = 0x0000, + ATH10K_HTC_SVC_ID_UNUSED = ATH10K_HTC_SVC_ID_RESERVED, + + ATH10K_HTC_SVC_ID_RSVD_CTRL = SVC(ATH10K_HTC_SVC_GRP_RSVD, 1), + ATH10K_HTC_SVC_ID_WMI_CONTROL = SVC(ATH10K_HTC_SVC_GRP_WMI, 0), + ATH10K_HTC_SVC_ID_WMI_DATA_BE = SVC(ATH10K_HTC_SVC_GRP_WMI, 1), + ATH10K_HTC_SVC_ID_WMI_DATA_BK = SVC(ATH10K_HTC_SVC_GRP_WMI, 2), + ATH10K_HTC_SVC_ID_WMI_DATA_VI = SVC(ATH10K_HTC_SVC_GRP_WMI, 3), + ATH10K_HTC_SVC_ID_WMI_DATA_VO = SVC(ATH10K_HTC_SVC_GRP_WMI, 4), + + ATH10K_HTC_SVC_ID_NMI_CONTROL = SVC(ATH10K_HTC_SVC_GRP_NMI, 0), + ATH10K_HTC_SVC_ID_NMI_DATA = SVC(ATH10K_HTC_SVC_GRP_NMI, 1), + + ATH10K_HTC_SVC_ID_HTT_DATA_MSG = SVC(ATH10K_HTC_SVC_GRP_HTT, 0), + + /* raw stream service (i.e. flash, tcmd, calibration apps) */ + ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS = SVC(ATH10K_HTC_SVC_GRP_TEST, 0), +}; + +#undef SVC + +enum ath10k_htc_ep_id { + ATH10K_HTC_EP_UNUSED = -1, + ATH10K_HTC_EP_0 = 0, + ATH10K_HTC_EP_1 = 1, + ATH10K_HTC_EP_2, + ATH10K_HTC_EP_3, + ATH10K_HTC_EP_4, + ATH10K_HTC_EP_5, + ATH10K_HTC_EP_6, + ATH10K_HTC_EP_7, + ATH10K_HTC_EP_8, + ATH10K_HTC_EP_COUNT, +}; + +struct ath10k_htc_ops { + void (*target_send_suspend_complete)(struct ath10k *ar); +}; + +struct ath10k_htc_ep_ops { + void (*ep_tx_complete)(struct ath10k *, struct sk_buff *); + void (*ep_rx_complete)(struct ath10k *, struct sk_buff *); +}; + +/* service connection information */ +struct ath10k_htc_svc_conn_req { + u16 service_id; + struct ath10k_htc_ep_ops ep_ops; + int max_send_queue_depth; +}; + +/* service connection response information */ +struct ath10k_htc_svc_conn_resp { + u8 buffer_len; + u8 actual_len; + enum ath10k_htc_ep_id eid; + unsigned int max_msg_len; + u8 connect_resp_code; +}; + +#define ATH10K_NUM_CONTROL_TX_BUFFERS 2 +#define ATH10K_HTC_MAX_LEN 4096 +#define ATH10K_HTC_MAX_CTRL_MSG_LEN 256 +#define ATH10K_HTC_WAIT_TIMEOUT_HZ (1*HZ) +#define ATH10K_HTC_CONTROL_BUFFER_SIZE (ATH10K_HTC_MAX_CTRL_MSG_LEN + \ + sizeof(struct ath10k_htc_hdr)) +#define ATH10K_HTC_CONN_SVC_TIMEOUT_HZ (1*HZ) + +struct ath10k_htc_ep { + struct ath10k_htc *htc; + enum ath10k_htc_ep_id eid; + enum ath10k_htc_svc_id service_id; + struct ath10k_htc_ep_ops ep_ops; + + int max_tx_queue_depth; + int max_ep_message_len; + u8 ul_pipe_id; + u8 dl_pipe_id; + int ul_is_polled; /* call HIF to get tx completions */ + int dl_is_polled; /* call HIF to fetch rx (not implemented) */ + + struct sk_buff_head tx_queue; + + u8 seq_no; /* for debugging */ + int tx_credits; + int tx_credit_size; + int tx_credits_per_max_message; + bool tx_credit_flow_enabled; + + struct work_struct send_work; +}; + +struct ath10k_htc_svc_tx_credits { + u16 service_id; + u8 credit_allocation; +}; + +struct ath10k_htc { + struct ath10k *ar; + struct ath10k_htc_ep endpoint[ATH10K_HTC_EP_COUNT]; + + /* protects endpoint and stopping fields */ + spinlock_t tx_lock; + + struct ath10k_htc_ops htc_ops; + + u8 control_resp_buffer[ATH10K_HTC_MAX_CTRL_MSG_LEN]; + int control_resp_len; + + struct completion ctl_resp; + + int total_transmit_credits; + struct ath10k_htc_svc_tx_credits service_tx_alloc[ATH10K_HTC_EP_COUNT]; + int target_credit_size; + + bool stopping; +}; + +struct ath10k_htc *ath10k_htc_create(struct ath10k *ar, + struct ath10k_htc_ops *htc_ops); +int ath10k_htc_wait_target(struct ath10k_htc *htc); +int ath10k_htc_start(struct ath10k_htc *htc); +int ath10k_htc_connect_service(struct ath10k_htc *htc, + struct ath10k_htc_svc_conn_req *conn_req, + struct ath10k_htc_svc_conn_resp *conn_resp); +int ath10k_htc_send(struct ath10k_htc *htc, enum ath10k_htc_ep_id eid, + struct sk_buff *packet); +void ath10k_htc_stop(struct ath10k_htc *htc); +void ath10k_htc_destroy(struct ath10k_htc *htc); +struct sk_buff *ath10k_htc_alloc_skb(int size); + +#endif diff --git a/drivers/net/wireless/ath/ath10k/htt.c b/drivers/net/wireless/ath/ath10k/htt.c new file mode 100644 index 000000000000..185a5468a2f2 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/htt.c @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "htt.h" +#include "core.h" +#include "debug.h" + +static int ath10k_htt_htc_attach(struct ath10k_htt *htt) +{ + struct ath10k_htc_svc_conn_req conn_req; + struct ath10k_htc_svc_conn_resp conn_resp; + int status; + + memset(&conn_req, 0, sizeof(conn_req)); + memset(&conn_resp, 0, sizeof(conn_resp)); + + conn_req.ep_ops.ep_tx_complete = ath10k_htt_htc_tx_complete; + conn_req.ep_ops.ep_rx_complete = ath10k_htt_t2h_msg_handler; + + /* connect to control service */ + conn_req.service_id = ATH10K_HTC_SVC_ID_HTT_DATA_MSG; + + status = ath10k_htc_connect_service(htt->ar->htc, &conn_req, + &conn_resp); + + if (status) + return status; + + htt->eid = conn_resp.eid; + + return 0; +} + +struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar) +{ + struct ath10k_htt *htt; + int ret; + + htt = kzalloc(sizeof(*htt), GFP_KERNEL); + if (!htt) + return NULL; + + htt->ar = ar; + htt->max_throughput_mbps = 800; + + /* + * Connect to HTC service. + * This has to be done before calling ath10k_htt_rx_attach, + * since ath10k_htt_rx_attach involves sending a rx ring configure + * message to the target. + */ + if (ath10k_htt_htc_attach(htt)) + goto err_htc_attach; + + ret = ath10k_htt_tx_attach(htt); + if (ret) { + ath10k_err("could not attach htt tx (%d)\n", ret); + goto err_htc_attach; + } + + if (ath10k_htt_rx_attach(htt)) + goto err_rx_attach; + + /* + * Prefetch enough data to satisfy target + * classification engine. + * This is for LL chips. HL chips will probably + * transfer all frame in the tx fragment. + */ + htt->prefetch_len = + 36 + /* 802.11 + qos + ht */ + 4 + /* 802.1q */ + 8 + /* llc snap */ + 2; /* ip4 dscp or ip6 priority */ + + return htt; + +err_rx_attach: + ath10k_htt_tx_detach(htt); +err_htc_attach: + kfree(htt); + return NULL; +} + +#define HTT_TARGET_VERSION_TIMEOUT_HZ (3*HZ) + +static int ath10k_htt_verify_version(struct ath10k_htt *htt) +{ + ath10k_dbg(ATH10K_DBG_HTT, + "htt target version %d.%d; host version %d.%d\n", + htt->target_version_major, + htt->target_version_minor, + HTT_CURRENT_VERSION_MAJOR, + HTT_CURRENT_VERSION_MINOR); + + if (htt->target_version_major != HTT_CURRENT_VERSION_MAJOR) { + ath10k_err("htt major versions are incompatible!\n"); + return -ENOTSUPP; + } + + if (htt->target_version_minor != HTT_CURRENT_VERSION_MINOR) + ath10k_warn("htt minor version differ but still compatible\n"); + + return 0; +} + +int ath10k_htt_attach_target(struct ath10k_htt *htt) +{ + int status; + + init_completion(&htt->target_version_received); + + status = ath10k_htt_h2t_ver_req_msg(htt); + if (status) + return status; + + status = wait_for_completion_timeout(&htt->target_version_received, + HTT_TARGET_VERSION_TIMEOUT_HZ); + if (status <= 0) { + ath10k_warn("htt version request timed out\n"); + return -ETIMEDOUT; + } + + status = ath10k_htt_verify_version(htt); + if (status) + return status; + + return ath10k_htt_send_rx_ring_cfg_ll(htt); +} + +void ath10k_htt_detach(struct ath10k_htt *htt) +{ + ath10k_htt_rx_detach(htt); + ath10k_htt_tx_detach(htt); + kfree(htt); +} diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h new file mode 100644 index 000000000000..a7a7aa040536 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -0,0 +1,1338 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HTT_H_ +#define _HTT_H_ + +#include + +#include "core.h" +#include "htc.h" +#include "rx_desc.h" + +#define HTT_CURRENT_VERSION_MAJOR 2 +#define HTT_CURRENT_VERSION_MINOR 1 + +enum htt_dbg_stats_type { + HTT_DBG_STATS_WAL_PDEV_TXRX = 1 << 0, + HTT_DBG_STATS_RX_REORDER = 1 << 1, + HTT_DBG_STATS_RX_RATE_INFO = 1 << 2, + HTT_DBG_STATS_TX_PPDU_LOG = 1 << 3, + HTT_DBG_STATS_TX_RATE_INFO = 1 << 4, + /* bits 5-23 currently reserved */ + + HTT_DBG_NUM_STATS /* keep this last */ +}; + +enum htt_h2t_msg_type { /* host-to-target */ + HTT_H2T_MSG_TYPE_VERSION_REQ = 0, + HTT_H2T_MSG_TYPE_TX_FRM = 1, + HTT_H2T_MSG_TYPE_RX_RING_CFG = 2, + HTT_H2T_MSG_TYPE_STATS_REQ = 3, + HTT_H2T_MSG_TYPE_SYNC = 4, + HTT_H2T_MSG_TYPE_AGGR_CFG = 5, + HTT_H2T_MSG_TYPE_FRAG_DESC_BANK_CFG = 6, + HTT_H2T_MSG_TYPE_MGMT_TX = 7, + + HTT_H2T_NUM_MSGS /* keep this last */ +}; + +struct htt_cmd_hdr { + u8 msg_type; +} __packed; + +struct htt_ver_req { + u8 pad[sizeof(u32) - sizeof(struct htt_cmd_hdr)]; +} __packed; + +/* + * HTT tx MSDU descriptor + * + * The HTT tx MSDU descriptor is created by the host HTT SW for each + * tx MSDU. The HTT tx MSDU descriptor contains the information that + * the target firmware needs for the FW's tx processing, particularly + * for creating the HW msdu descriptor. + * The same HTT tx descriptor is used for HL and LL systems, though + * a few fields within the tx descriptor are used only by LL or + * only by HL. + * The HTT tx descriptor is defined in two manners: by a struct with + * bitfields, and by a series of [dword offset, bit mask, bit shift] + * definitions. + * The target should use the struct def, for simplicitly and clarity, + * but the host shall use the bit-mast + bit-shift defs, to be endian- + * neutral. Specifically, the host shall use the get/set macros built + * around the mask + shift defs. + */ +struct htt_data_tx_desc_frag { + __le32 paddr; + __le32 len; +} __packed; + +enum htt_data_tx_desc_flags0 { + HTT_DATA_TX_DESC_FLAGS0_MAC_HDR_PRESENT = 1 << 0, + HTT_DATA_TX_DESC_FLAGS0_NO_AGGR = 1 << 1, + HTT_DATA_TX_DESC_FLAGS0_NO_ENCRYPT = 1 << 2, + HTT_DATA_TX_DESC_FLAGS0_NO_CLASSIFY = 1 << 3, + HTT_DATA_TX_DESC_FLAGS0_RSVD0 = 1 << 4 +#define HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE_MASK 0xE0 +#define HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE_LSB 5 +}; + +enum htt_data_tx_desc_flags1 { +#define HTT_DATA_TX_DESC_FLAGS1_VDEV_ID_BITS 6 +#define HTT_DATA_TX_DESC_FLAGS1_VDEV_ID_MASK 0x003F +#define HTT_DATA_TX_DESC_FLAGS1_VDEV_ID_LSB 0 +#define HTT_DATA_TX_DESC_FLAGS1_EXT_TID_BITS 5 +#define HTT_DATA_TX_DESC_FLAGS1_EXT_TID_MASK 0x07C0 +#define HTT_DATA_TX_DESC_FLAGS1_EXT_TID_LSB 6 + HTT_DATA_TX_DESC_FLAGS1_POSTPONED = 1 << 11, + HTT_DATA_TX_DESC_FLAGS1_MORE_IN_BATCH = 1 << 12, + HTT_DATA_TX_DESC_FLAGS1_CKSUM_L3_OFFLOAD = 1 << 13, + HTT_DATA_TX_DESC_FLAGS1_CKSUM_L4_OFFLOAD = 1 << 14, + HTT_DATA_TX_DESC_FLAGS1_RSVD1 = 1 << 15 +}; + +enum htt_data_tx_ext_tid { + HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST = 16, + HTT_DATA_TX_EXT_TID_MGMT = 17, + HTT_DATA_TX_EXT_TID_INVALID = 31 +}; + +#define HTT_INVALID_PEERID 0xFFFF + +/* + * htt_data_tx_desc - used for data tx path + * + * Note: vdev_id irrelevant for pkt_type == raw and no_classify == 1. + * ext_tid: for qos-data frames (0-15), see %HTT_DATA_TX_EXT_TID_ + * for special kinds of tids + * postponed: only for HL hosts. indicates if this is a resend + * (HL hosts manage queues on the host ) + * more_in_batch: only for HL hosts. indicates if more packets are + * pending. this allows target to wait and aggregate + */ +struct htt_data_tx_desc { + u8 flags0; /* %HTT_DATA_TX_DESC_FLAGS0_ */ + __le16 flags1; /* %HTT_DATA_TX_DESC_FLAGS1_ */ + __le16 len; + __le16 id; + __le32 frags_paddr; + __le32 peerid; + u8 prefetch[0]; /* start of frame, for FW classification engine */ +} __packed; + +enum htt_rx_ring_flags { + HTT_RX_RING_FLAGS_MAC80211_HDR = 1 << 0, + HTT_RX_RING_FLAGS_MSDU_PAYLOAD = 1 << 1, + HTT_RX_RING_FLAGS_PPDU_START = 1 << 2, + HTT_RX_RING_FLAGS_PPDU_END = 1 << 3, + HTT_RX_RING_FLAGS_MPDU_START = 1 << 4, + HTT_RX_RING_FLAGS_MPDU_END = 1 << 5, + HTT_RX_RING_FLAGS_MSDU_START = 1 << 6, + HTT_RX_RING_FLAGS_MSDU_END = 1 << 7, + HTT_RX_RING_FLAGS_RX_ATTENTION = 1 << 8, + HTT_RX_RING_FLAGS_FRAG_INFO = 1 << 9, + HTT_RX_RING_FLAGS_UNICAST_RX = 1 << 10, + HTT_RX_RING_FLAGS_MULTICAST_RX = 1 << 11, + HTT_RX_RING_FLAGS_CTRL_RX = 1 << 12, + HTT_RX_RING_FLAGS_MGMT_RX = 1 << 13, + HTT_RX_RING_FLAGS_NULL_RX = 1 << 14, + HTT_RX_RING_FLAGS_PHY_DATA_RX = 1 << 15 +}; + +struct htt_rx_ring_setup_ring { + __le32 fw_idx_shadow_reg_paddr; + __le32 rx_ring_base_paddr; + __le16 rx_ring_len; /* in 4-byte words */ + __le16 rx_ring_bufsize; /* rx skb size - in bytes */ + __le16 flags; /* %HTT_RX_RING_FLAGS_ */ + __le16 fw_idx_init_val; + + /* the following offsets are in 4-byte units */ + __le16 mac80211_hdr_offset; + __le16 msdu_payload_offset; + __le16 ppdu_start_offset; + __le16 ppdu_end_offset; + __le16 mpdu_start_offset; + __le16 mpdu_end_offset; + __le16 msdu_start_offset; + __le16 msdu_end_offset; + __le16 rx_attention_offset; + __le16 frag_info_offset; +} __packed; + +struct htt_rx_ring_setup_hdr { + u8 num_rings; /* supported values: 1, 2 */ + __le16 rsvd0; +} __packed; + +struct htt_rx_ring_setup { + struct htt_rx_ring_setup_hdr hdr; + struct htt_rx_ring_setup_ring rings[0]; +} __packed; + +/* + * htt_stats_req - request target to send specified statistics + * + * @msg_type: hardcoded %HTT_H2T_MSG_TYPE_STATS_REQ + * @upload_types: see %htt_dbg_stats_type. this is 24bit field actually + * so make sure its little-endian. + * @reset_types: see %htt_dbg_stats_type. this is 24bit field actually + * so make sure its little-endian. + * @cfg_val: stat_type specific configuration + * @stat_type: see %htt_dbg_stats_type + * @cookie_lsb: used for confirmation message from target->host + * @cookie_msb: ditto as %cookie + */ +struct htt_stats_req { + u8 upload_types[3]; + u8 rsvd0; + u8 reset_types[3]; + struct { + u8 mpdu_bytes; + u8 mpdu_num_msdus; + u8 msdu_bytes; + } __packed; + u8 stat_type; + __le32 cookie_lsb; + __le32 cookie_msb; +} __packed; + +#define HTT_STATS_REQ_CFG_STAT_TYPE_INVALID 0xff + +/* + * htt_oob_sync_req - request out-of-band sync + * + * The HTT SYNC tells the target to suspend processing of subsequent + * HTT host-to-target messages until some other target agent locally + * informs the target HTT FW that the current sync counter is equal to + * or greater than (in a modulo sense) the sync counter specified in + * the SYNC message. + * + * This allows other host-target components to synchronize their operation + * with HTT, e.g. to ensure that tx frames don't get transmitted until a + * security key has been downloaded to and activated by the target. + * In the absence of any explicit synchronization counter value + * specification, the target HTT FW will use zero as the default current + * sync value. + * + * The HTT target FW will suspend its host->target message processing as long + * as 0 < (in-band sync counter - out-of-band sync counter) & 0xff < 128. + */ +struct htt_oob_sync_req { + u8 sync_count; + __le16 rsvd0; +} __packed; + +#define HTT_AGGR_CONF_MAX_NUM_AMSDU_SUBFRAMES_MASK 0x1F +#define HTT_AGGR_CONF_MAX_NUM_AMSDU_SUBFRAMES_LSB 0 + +struct htt_aggr_conf { + u8 max_num_ampdu_subframes; + union { + /* dont use bitfields; undefined behaviour */ + u8 flags; /* see %HTT_AGGR_CONF_MAX_NUM_AMSDU_SUBFRAMES_ */ + u8 max_num_amsdu_subframes:5; + } __packed; +} __packed; + +#define HTT_MGMT_FRM_HDR_DOWNLOAD_LEN 32 + +struct htt_mgmt_tx_desc { + u8 pad[sizeof(u32) - sizeof(struct htt_cmd_hdr)]; + __le32 msdu_paddr; + __le32 desc_id; + __le32 len; + __le32 vdev_id; + u8 hdr[HTT_MGMT_FRM_HDR_DOWNLOAD_LEN]; +} __packed; + +enum htt_mgmt_tx_status { + HTT_MGMT_TX_STATUS_OK = 0, + HTT_MGMT_TX_STATUS_RETRY = 1, + HTT_MGMT_TX_STATUS_DROP = 2 +}; + +/*=== target -> host messages ===============================================*/ + + +enum htt_t2h_msg_type { + HTT_T2H_MSG_TYPE_VERSION_CONF = 0x0, + HTT_T2H_MSG_TYPE_RX_IND = 0x1, + HTT_T2H_MSG_TYPE_RX_FLUSH = 0x2, + HTT_T2H_MSG_TYPE_PEER_MAP = 0x3, + HTT_T2H_MSG_TYPE_PEER_UNMAP = 0x4, + HTT_T2H_MSG_TYPE_RX_ADDBA = 0x5, + HTT_T2H_MSG_TYPE_RX_DELBA = 0x6, + HTT_T2H_MSG_TYPE_TX_COMPL_IND = 0x7, + HTT_T2H_MSG_TYPE_PKTLOG = 0x8, + HTT_T2H_MSG_TYPE_STATS_CONF = 0x9, + HTT_T2H_MSG_TYPE_RX_FRAG_IND = 0xa, + HTT_T2H_MSG_TYPE_SEC_IND = 0xb, + HTT_T2H_MSG_TYPE_RC_UPDATE_IND = 0xc, + HTT_T2H_MSG_TYPE_TX_INSPECT_IND = 0xd, + HTT_T2H_MSG_TYPE_MGMT_TX_COMPLETION = 0xe, + HTT_T2H_MSG_TYPE_TEST, + /* keep this last */ + HTT_T2H_NUM_MSGS +}; + +/* + * htt_resp_hdr - header for target-to-host messages + * + * msg_type: see htt_t2h_msg_type + */ +struct htt_resp_hdr { + u8 msg_type; +} __packed; + +#define HTT_RESP_HDR_MSG_TYPE_OFFSET 0 +#define HTT_RESP_HDR_MSG_TYPE_MASK 0xff +#define HTT_RESP_HDR_MSG_TYPE_LSB 0 + +/* htt_ver_resp - response sent for htt_ver_req */ +struct htt_ver_resp { + u8 minor; + u8 major; + u8 rsvd0; +} __packed; + +struct htt_mgmt_tx_completion { + u8 rsvd0; + u8 rsvd1; + u8 rsvd2; + __le32 desc_id; + __le32 status; +} __packed; + +#define HTT_RX_INDICATION_INFO0_EXT_TID_MASK (0x3F) +#define HTT_RX_INDICATION_INFO0_EXT_TID_LSB (0) +#define HTT_RX_INDICATION_INFO0_FLUSH_VALID (1 << 6) +#define HTT_RX_INDICATION_INFO0_RELEASE_VALID (1 << 7) + +#define HTT_RX_INDICATION_INFO1_FLUSH_START_SEQNO_MASK 0x0000003F +#define HTT_RX_INDICATION_INFO1_FLUSH_START_SEQNO_LSB 0 +#define HTT_RX_INDICATION_INFO1_FLUSH_END_SEQNO_MASK 0x00000FC0 +#define HTT_RX_INDICATION_INFO1_FLUSH_END_SEQNO_LSB 6 +#define HTT_RX_INDICATION_INFO1_RELEASE_START_SEQNO_MASK 0x0003F000 +#define HTT_RX_INDICATION_INFO1_RELEASE_START_SEQNO_LSB 12 +#define HTT_RX_INDICATION_INFO1_RELEASE_END_SEQNO_MASK 0x00FC0000 +#define HTT_RX_INDICATION_INFO1_RELEASE_END_SEQNO_LSB 18 +#define HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES_MASK 0xFF000000 +#define HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES_LSB 24 + +struct htt_rx_indication_hdr { + u8 info0; /* %HTT_RX_INDICATION_INFO0_ */ + __le16 peer_id; + __le32 info1; /* %HTT_RX_INDICATION_INFO1_ */ +} __packed; + +#define HTT_RX_INDICATION_INFO0_PHY_ERR_VALID (1 << 0) +#define HTT_RX_INDICATION_INFO0_LEGACY_RATE_MASK (0x1E) +#define HTT_RX_INDICATION_INFO0_LEGACY_RATE_LSB (1) +#define HTT_RX_INDICATION_INFO0_LEGACY_RATE_CCK (1 << 5) +#define HTT_RX_INDICATION_INFO0_END_VALID (1 << 6) +#define HTT_RX_INDICATION_INFO0_START_VALID (1 << 7) + +#define HTT_RX_INDICATION_INFO1_VHT_SIG_A1_MASK 0x00FFFFFF +#define HTT_RX_INDICATION_INFO1_VHT_SIG_A1_LSB 0 +#define HTT_RX_INDICATION_INFO1_PREAMBLE_TYPE_MASK 0xFF000000 +#define HTT_RX_INDICATION_INFO1_PREAMBLE_TYPE_LSB 24 + +#define HTT_RX_INDICATION_INFO2_VHT_SIG_A1_MASK 0x00FFFFFF +#define HTT_RX_INDICATION_INFO2_VHT_SIG_A1_LSB 0 +#define HTT_RX_INDICATION_INFO2_SERVICE_MASK 0xFF000000 +#define HTT_RX_INDICATION_INFO2_SERVICE_LSB 24 + +enum htt_rx_legacy_rate { + HTT_RX_OFDM_48 = 0, + HTT_RX_OFDM_24 = 1, + HTT_RX_OFDM_12, + HTT_RX_OFDM_6, + HTT_RX_OFDM_54, + HTT_RX_OFDM_36, + HTT_RX_OFDM_18, + HTT_RX_OFDM_9, + + /* long preamble */ + HTT_RX_CCK_11_LP = 0, + HTT_RX_CCK_5_5_LP = 1, + HTT_RX_CCK_2_LP, + HTT_RX_CCK_1_LP, + /* short preamble */ + HTT_RX_CCK_11_SP, + HTT_RX_CCK_5_5_SP, + HTT_RX_CCK_2_SP +}; + +enum htt_rx_legacy_rate_type { + HTT_RX_LEGACY_RATE_OFDM = 0, + HTT_RX_LEGACY_RATE_CCK +}; + +enum htt_rx_preamble_type { + HTT_RX_LEGACY = 0x4, + HTT_RX_HT = 0x8, + HTT_RX_HT_WITH_TXBF = 0x9, + HTT_RX_VHT = 0xC, + HTT_RX_VHT_WITH_TXBF = 0xD, +}; + +/* + * Fields: phy_err_valid, phy_err_code, tsf, + * usec_timestamp, sub_usec_timestamp + * ..are valid only if end_valid == 1. + * + * Fields: rssi_chains, legacy_rate_type, + * legacy_rate_cck, preamble_type, service, + * vht_sig_* + * ..are valid only if start_valid == 1; + */ +struct htt_rx_indication_ppdu { + u8 combined_rssi; + u8 sub_usec_timestamp; + u8 phy_err_code; + u8 info0; /* HTT_RX_INDICATION_INFO0_ */ + struct { + u8 pri20_db; + u8 ext20_db; + u8 ext40_db; + u8 ext80_db; + } __packed rssi_chains[4]; + __le32 tsf; + __le32 usec_timestamp; + __le32 info1; /* HTT_RX_INDICATION_INFO1_ */ + __le32 info2; /* HTT_RX_INDICATION_INFO2_ */ +} __packed; + +enum htt_rx_mpdu_status { + HTT_RX_IND_MPDU_STATUS_UNKNOWN = 0x0, + HTT_RX_IND_MPDU_STATUS_OK, + HTT_RX_IND_MPDU_STATUS_ERR_FCS, + HTT_RX_IND_MPDU_STATUS_ERR_DUP, + HTT_RX_IND_MPDU_STATUS_ERR_REPLAY, + HTT_RX_IND_MPDU_STATUS_ERR_INV_PEER, + /* only accept EAPOL frames */ + HTT_RX_IND_MPDU_STATUS_UNAUTH_PEER, + HTT_RX_IND_MPDU_STATUS_OUT_OF_SYNC, + /* Non-data in promiscous mode */ + HTT_RX_IND_MPDU_STATUS_MGMT_CTRL, + HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR, + HTT_RX_IND_MPDU_STATUS_DECRYPT_ERR, + HTT_RX_IND_MPDU_STATUS_MPDU_LENGTH_ERR, + HTT_RX_IND_MPDU_STATUS_ENCRYPT_REQUIRED_ERR, + HTT_RX_IND_MPDU_STATUS_PRIVACY_ERR, + + /* + * MISC: discard for unspecified reasons. + * Leave this enum value last. + */ + HTT_RX_IND_MPDU_STATUS_ERR_MISC = 0xFF +}; + +struct htt_rx_indication_mpdu_range { + u8 mpdu_count; + u8 mpdu_range_status; /* %htt_rx_mpdu_status */ + u8 pad0; + u8 pad1; +} __packed; + +struct htt_rx_indication_prefix { + __le16 fw_rx_desc_bytes; + u8 pad0; + u8 pad1; +}; + +struct htt_rx_indication { + struct htt_rx_indication_hdr hdr; + struct htt_rx_indication_ppdu ppdu; + struct htt_rx_indication_prefix prefix; + + /* + * the following fields are both dynamically sized, so + * take care addressing them + */ + + /* the size of this is %fw_rx_desc_bytes */ + struct fw_rx_desc_base fw_desc; + + /* + * %mpdu_ranges starts after &%prefix + roundup(%fw_rx_desc_bytes, 4) + * and has %num_mpdu_ranges elements. + */ + struct htt_rx_indication_mpdu_range mpdu_ranges[0]; +} __packed; + +static inline struct htt_rx_indication_mpdu_range * + htt_rx_ind_get_mpdu_ranges(struct htt_rx_indication *rx_ind) +{ + void *ptr = rx_ind; + + ptr += sizeof(rx_ind->hdr) + + sizeof(rx_ind->ppdu) + + sizeof(rx_ind->prefix) + + roundup(__le16_to_cpu(rx_ind->prefix.fw_rx_desc_bytes), 4); + return ptr; +} + +enum htt_rx_flush_mpdu_status { + HTT_RX_FLUSH_MPDU_DISCARD = 0, + HTT_RX_FLUSH_MPDU_REORDER = 1, +}; + +/* + * htt_rx_flush - discard or reorder given range of mpdus + * + * Note: host must check if all sequence numbers between + * [seq_num_start, seq_num_end-1] are valid. + */ +struct htt_rx_flush { + __le16 peer_id; + u8 tid; + u8 rsvd0; + u8 mpdu_status; /* %htt_rx_flush_mpdu_status */ + u8 seq_num_start; /* it is 6 LSBs of 802.11 seq no */ + u8 seq_num_end; /* it is 6 LSBs of 802.11 seq no */ +}; + +struct htt_rx_peer_map { + u8 vdev_id; + __le16 peer_id; + u8 addr[6]; + u8 rsvd0; + u8 rsvd1; +} __packed; + +struct htt_rx_peer_unmap { + u8 rsvd0; + __le16 peer_id; +} __packed; + +enum htt_security_types { + HTT_SECURITY_NONE, + HTT_SECURITY_WEP128, + HTT_SECURITY_WEP104, + HTT_SECURITY_WEP40, + HTT_SECURITY_TKIP, + HTT_SECURITY_TKIP_NOMIC, + HTT_SECURITY_AES_CCMP, + HTT_SECURITY_WAPI, + + HTT_NUM_SECURITY_TYPES /* keep this last! */ +}; + +enum htt_security_flags { +#define HTT_SECURITY_TYPE_MASK 0x7F +#define HTT_SECURITY_TYPE_LSB 0 + HTT_SECURITY_IS_UNICAST = 1 << 7 +}; + +struct htt_security_indication { + union { + /* dont use bitfields; undefined behaviour */ + u8 flags; /* %htt_security_flags */ + struct { + u8 security_type:7, /* %htt_security_types */ + is_unicast:1; + } __packed; + } __packed; + __le16 peer_id; + u8 michael_key[8]; + u8 wapi_rsc[16]; +} __packed; + +#define HTT_RX_BA_INFO0_TID_MASK 0x000F +#define HTT_RX_BA_INFO0_TID_LSB 0 +#define HTT_RX_BA_INFO0_PEER_ID_MASK 0xFFF0 +#define HTT_RX_BA_INFO0_PEER_ID_LSB 4 + +struct htt_rx_addba { + u8 window_size; + __le16 info0; /* %HTT_RX_BA_INFO0_ */ +} __packed; + +struct htt_rx_delba { + u8 rsvd0; + __le16 info0; /* %HTT_RX_BA_INFO0_ */ +} __packed; + +enum htt_data_tx_status { + HTT_DATA_TX_STATUS_OK = 0, + HTT_DATA_TX_STATUS_DISCARD = 1, + HTT_DATA_TX_STATUS_NO_ACK = 2, + HTT_DATA_TX_STATUS_POSTPONE = 3, /* HL only */ + HTT_DATA_TX_STATUS_DOWNLOAD_FAIL = 128 +}; + +enum htt_data_tx_flags { +#define HTT_DATA_TX_STATUS_MASK 0x07 +#define HTT_DATA_TX_STATUS_LSB 0 +#define HTT_DATA_TX_TID_MASK 0x78 +#define HTT_DATA_TX_TID_LSB 3 + HTT_DATA_TX_TID_INVALID = 1 << 7 +}; + +#define HTT_TX_COMPL_INV_MSDU_ID 0xFFFF + +struct htt_data_tx_completion { + union { + u8 flags; + struct { + u8 status:3, + tid:4, + tid_invalid:1; + } __packed; + } __packed; + u8 num_msdus; + u8 rsvd0; + __le16 msdus[0]; /* variable length based on %num_msdus */ +} __packed; + +struct htt_tx_compl_ind_base { + u32 hdr; + u16 payload[1/*or more*/]; +} __packed; + +struct htt_rc_tx_done_params { + u32 rate_code; + u32 rate_code_flags; + u32 flags; + u32 num_enqued; /* 1 for non-AMPDU */ + u32 num_retries; + u32 num_failed; /* for AMPDU */ + u32 ack_rssi; + u32 time_stamp; + u32 is_probe; +}; + +struct htt_rc_update { + u8 vdev_id; + __le16 peer_id; + u8 addr[6]; + u8 num_elems; + u8 rsvd0; + struct htt_rc_tx_done_params params[0]; /* variable length %num_elems */ +} __packed; + +/* see htt_rx_indication for similar fields and descriptions */ +struct htt_rx_fragment_indication { + union { + u8 info0; /* %HTT_RX_FRAG_IND_INFO0_ */ + struct { + u8 ext_tid:5, + flush_valid:1; + } __packed; + } __packed; + __le16 peer_id; + __le32 info1; /* %HTT_RX_FRAG_IND_INFO1_ */ + __le16 fw_rx_desc_bytes; + __le16 rsvd0; + + u8 fw_msdu_rx_desc[0]; +} __packed; + +#define HTT_RX_FRAG_IND_INFO0_EXT_TID_MASK 0x1F +#define HTT_RX_FRAG_IND_INFO0_EXT_TID_LSB 0 +#define HTT_RX_FRAG_IND_INFO0_FLUSH_VALID_MASK 0x20 +#define HTT_RX_FRAG_IND_INFO0_FLUSH_VALID_LSB 5 + +#define HTT_RX_FRAG_IND_INFO1_FLUSH_SEQ_NUM_START_MASK 0x0000003F +#define HTT_RX_FRAG_IND_INFO1_FLUSH_SEQ_NUM_START_LSB 0 +#define HTT_RX_FRAG_IND_INFO1_FLUSH_SEQ_NUM_END_MASK 0x00000FC0 +#define HTT_RX_FRAG_IND_INFO1_FLUSH_SEQ_NUM_END_LSB 6 + +/* + * target -> host test message definition + * + * The following field definitions describe the format of the test + * message sent from the target to the host. + * The message consists of a 4-octet header, followed by a variable + * number of 32-bit integer values, followed by a variable number + * of 8-bit character values. + * + * |31 16|15 8|7 0| + * |-----------------------------------------------------------| + * | num chars | num ints | msg type | + * |-----------------------------------------------------------| + * | int 0 | + * |-----------------------------------------------------------| + * | int 1 | + * |-----------------------------------------------------------| + * | ... | + * |-----------------------------------------------------------| + * | char 3 | char 2 | char 1 | char 0 | + * |-----------------------------------------------------------| + * | | | ... | char 4 | + * |-----------------------------------------------------------| + * - MSG_TYPE + * Bits 7:0 + * Purpose: identifies this as a test message + * Value: HTT_MSG_TYPE_TEST + * - NUM_INTS + * Bits 15:8 + * Purpose: indicate how many 32-bit integers follow the message header + * - NUM_CHARS + * Bits 31:16 + * Purpose: indicate how many 8-bit charaters follow the series of integers + */ +struct htt_rx_test { + u8 num_ints; + __le16 num_chars; + + /* payload consists of 2 lists: + * a) num_ints * sizeof(__le32) + * b) num_chars * sizeof(u8) aligned to 4bytes */ + u8 payload[0]; +} __packed; + +static inline __le32 *htt_rx_test_get_ints(struct htt_rx_test *rx_test) +{ + return (__le32 *)rx_test->payload; +} + +static inline u8 *htt_rx_test_get_chars(struct htt_rx_test *rx_test) +{ + return rx_test->payload + (rx_test->num_ints * sizeof(__le32)); +} + +/* + * target -> host packet log message + * + * The following field definitions describe the format of the packet log + * message sent from the target to the host. + * The message consists of a 4-octet header,followed by a variable number + * of 32-bit character values. + * + * |31 24|23 16|15 8|7 0| + * |-----------------------------------------------------------| + * | | | | msg type | + * |-----------------------------------------------------------| + * | payload | + * |-----------------------------------------------------------| + * - MSG_TYPE + * Bits 7:0 + * Purpose: identifies this as a test message + * Value: HTT_MSG_TYPE_PACKETLOG + */ +struct htt_pktlog_msg { + u8 pad[3]; + __le32 payload[1 /* or more */]; +} __packed; + +struct htt_dbg_stats_rx_reorder_stats { + /* Non QoS MPDUs received */ + __le32 deliver_non_qos; + + /* MPDUs received in-order */ + __le32 deliver_in_order; + + /* Flush due to reorder timer expired */ + __le32 deliver_flush_timeout; + + /* Flush due to move out of window */ + __le32 deliver_flush_oow; + + /* Flush due to DELBA */ + __le32 deliver_flush_delba; + + /* MPDUs dropped due to FCS error */ + __le32 fcs_error; + + /* MPDUs dropped due to monitor mode non-data packet */ + __le32 mgmt_ctrl; + + /* MPDUs dropped due to invalid peer */ + __le32 invalid_peer; + + /* MPDUs dropped due to duplication (non aggregation) */ + __le32 dup_non_aggr; + + /* MPDUs dropped due to processed before */ + __le32 dup_past; + + /* MPDUs dropped due to duplicate in reorder queue */ + __le32 dup_in_reorder; + + /* Reorder timeout happened */ + __le32 reorder_timeout; + + /* invalid bar ssn */ + __le32 invalid_bar_ssn; + + /* reorder reset due to bar ssn */ + __le32 ssn_reset; +}; + +struct htt_dbg_stats_wal_tx_stats { + /* Num HTT cookies queued to dispatch list */ + __le32 comp_queued; + + /* Num HTT cookies dispatched */ + __le32 comp_delivered; + + /* Num MSDU queued to WAL */ + __le32 msdu_enqued; + + /* Num MPDU queue to WAL */ + __le32 mpdu_enqued; + + /* Num MSDUs dropped by WMM limit */ + __le32 wmm_drop; + + /* Num Local frames queued */ + __le32 local_enqued; + + /* Num Local frames done */ + __le32 local_freed; + + /* Num queued to HW */ + __le32 hw_queued; + + /* Num PPDU reaped from HW */ + __le32 hw_reaped; + + /* Num underruns */ + __le32 underrun; + + /* Num PPDUs cleaned up in TX abort */ + __le32 tx_abort; + + /* Num MPDUs requed by SW */ + __le32 mpdus_requed; + + /* excessive retries */ + __le32 tx_ko; + + /* data hw rate code */ + __le32 data_rc; + + /* Scheduler self triggers */ + __le32 self_triggers; + + /* frames dropped due to excessive sw retries */ + __le32 sw_retry_failure; + + /* illegal rate phy errors */ + __le32 illgl_rate_phy_err; + + /* wal pdev continous xretry */ + __le32 pdev_cont_xretry; + + /* wal pdev continous xretry */ + __le32 pdev_tx_timeout; + + /* wal pdev resets */ + __le32 pdev_resets; + + __le32 phy_underrun; + + /* MPDU is more than txop limit */ + __le32 txop_ovf; +} __packed; + +struct htt_dbg_stats_wal_rx_stats { + /* Cnts any change in ring routing mid-ppdu */ + __le32 mid_ppdu_route_change; + + /* Total number of statuses processed */ + __le32 status_rcvd; + + /* Extra frags on rings 0-3 */ + __le32 r0_frags; + __le32 r1_frags; + __le32 r2_frags; + __le32 r3_frags; + + /* MSDUs / MPDUs delivered to HTT */ + __le32 htt_msdus; + __le32 htt_mpdus; + + /* MSDUs / MPDUs delivered to local stack */ + __le32 loc_msdus; + __le32 loc_mpdus; + + /* AMSDUs that have more MSDUs than the status ring size */ + __le32 oversize_amsdu; + + /* Number of PHY errors */ + __le32 phy_errs; + + /* Number of PHY errors drops */ + __le32 phy_err_drop; + + /* Number of mpdu errors - FCS, MIC, ENC etc. */ + __le32 mpdu_errs; +} __packed; + +struct htt_dbg_stats_wal_peer_stats { + __le32 dummy; /* REMOVE THIS ONCE REAL PEER STAT COUNTERS ARE ADDED */ +} __packed; + +struct htt_dbg_stats_wal_pdev_txrx { + struct htt_dbg_stats_wal_tx_stats tx_stats; + struct htt_dbg_stats_wal_rx_stats rx_stats; + struct htt_dbg_stats_wal_peer_stats peer_stats; +} __packed; + +struct htt_dbg_stats_rx_rate_info { + __le32 mcs[10]; + __le32 sgi[10]; + __le32 nss[4]; + __le32 stbc[10]; + __le32 bw[3]; + __le32 pream[6]; + __le32 ldpc; + __le32 txbf; +}; + +/* + * htt_dbg_stats_status - + * present - The requested stats have been delivered in full. + * This indicates that either the stats information was contained + * in its entirety within this message, or else this message + * completes the delivery of the requested stats info that was + * partially delivered through earlier STATS_CONF messages. + * partial - The requested stats have been delivered in part. + * One or more subsequent STATS_CONF messages with the same + * cookie value will be sent to deliver the remainder of the + * information. + * error - The requested stats could not be delivered, for example due + * to a shortage of memory to construct a message holding the + * requested stats. + * invalid - The requested stat type is either not recognized, or the + * target is configured to not gather the stats type in question. + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * series_done - This special value indicates that no further stats info + * elements are present within a series of stats info elems + * (within a stats upload confirmation message). + */ +enum htt_dbg_stats_status { + HTT_DBG_STATS_STATUS_PRESENT = 0, + HTT_DBG_STATS_STATUS_PARTIAL = 1, + HTT_DBG_STATS_STATUS_ERROR = 2, + HTT_DBG_STATS_STATUS_INVALID = 3, + HTT_DBG_STATS_STATUS_SERIES_DONE = 7 +}; + +/* + * target -> host statistics upload + * + * The following field definitions describe the format of the HTT target + * to host stats upload confirmation message. + * The message contains a cookie echoed from the HTT host->target stats + * upload request, which identifies which request the confirmation is + * for, and a series of tag-length-value stats information elements. + * The tag-length header for each stats info element also includes a + * status field, to indicate whether the request for the stat type in + * question was fully met, partially met, unable to be met, or invalid + * (if the stat type in question is disabled in the target). + * A special value of all 1's in this status field is used to indicate + * the end of the series of stats info elements. + * + * + * |31 16|15 8|7 5|4 0| + * |------------------------------------------------------------| + * | reserved | msg type | + * |------------------------------------------------------------| + * | cookie LSBs | + * |------------------------------------------------------------| + * | cookie MSBs | + * |------------------------------------------------------------| + * | stats entry length | reserved | S |stat type| + * |------------------------------------------------------------| + * | | + * | type-specific stats info | + * | | + * |------------------------------------------------------------| + * | stats entry length | reserved | S |stat type| + * |------------------------------------------------------------| + * | | + * | type-specific stats info | + * | | + * |------------------------------------------------------------| + * | n/a | reserved | 111 | n/a | + * |------------------------------------------------------------| + * Header fields: + * - MSG_TYPE + * Bits 7:0 + * Purpose: identifies this is a statistics upload confirmation message + * Value: 0x9 + * - COOKIE_LSBS + * Bits 31:0 + * Purpose: Provide a mechanism to match a target->host stats confirmation + * message with its preceding host->target stats request message. + * Value: LSBs of the opaque cookie specified by the host-side requestor + * - COOKIE_MSBS + * Bits 31:0 + * Purpose: Provide a mechanism to match a target->host stats confirmation + * message with its preceding host->target stats request message. + * Value: MSBs of the opaque cookie specified by the host-side requestor + * + * Stats Information Element tag-length header fields: + * - STAT_TYPE + * Bits 4:0 + * Purpose: identifies the type of statistics info held in the + * following information element + * Value: htt_dbg_stats_type + * - STATUS + * Bits 7:5 + * Purpose: indicate whether the requested stats are present + * Value: htt_dbg_stats_status, including a special value (0x7) to mark + * the completion of the stats entry series + * - LENGTH + * Bits 31:16 + * Purpose: indicate the stats information size + * Value: This field specifies the number of bytes of stats information + * that follows the element tag-length header. + * It is expected but not required that this length is a multiple of + * 4 bytes. Even if the length is not an integer multiple of 4, the + * subsequent stats entry header will begin on a 4-byte aligned + * boundary. + */ + +#define HTT_STATS_CONF_ITEM_INFO_STAT_TYPE_MASK 0x1F +#define HTT_STATS_CONF_ITEM_INFO_STAT_TYPE_LSB 0 +#define HTT_STATS_CONF_ITEM_INFO_STATUS_MASK 0xE0 +#define HTT_STATS_CONF_ITEM_INFO_STATUS_LSB 5 + +struct htt_stats_conf_item { + union { + u8 info; + struct { + u8 stat_type:5; /* %HTT_DBG_STATS_ */ + u8 status:3; /* %HTT_DBG_STATS_STATUS_ */ + } __packed; + } __packed; + u8 pad; + __le16 length; + u8 payload[0]; /* roundup(length, 4) long */ +} __packed; + +struct htt_stats_conf { + u8 pad[3]; + __le32 cookie_lsb; + __le32 cookie_msb; + + /* each item has variable length! */ + struct htt_stats_conf_item items[0]; +} __packed; + +static inline struct htt_stats_conf_item *htt_stats_conf_next_item( + const struct htt_stats_conf_item *item) +{ + return (void *)item + sizeof(*item) + roundup(item->length, 4); +} +/* + * host -> target FRAG DESCRIPTOR/MSDU_EXT DESC bank + * + * The following field definitions describe the format of the HTT host + * to target frag_desc/msdu_ext bank configuration message. + * The message contains the based address and the min and max id of the + * MSDU_EXT/FRAG_DESC that will be used by the HTT to map MSDU DESC and + * MSDU_EXT/FRAG_DESC. + * HTT will use id in HTT descriptor instead sending the frag_desc_ptr. + * For QCA988X HW the firmware will use fragment_desc_ptr but in WIFI2.0 + * the hardware does the mapping/translation. + * + * Total banks that can be configured is configured to 16. + * + * This should be called before any TX has be initiated by the HTT + * + * |31 16|15 8|7 5|4 0| + * |------------------------------------------------------------| + * | DESC_SIZE | NUM_BANKS | RES |SWP|pdev| msg type | + * |------------------------------------------------------------| + * | BANK0_BASE_ADDRESS | + * |------------------------------------------------------------| + * | ... | + * |------------------------------------------------------------| + * | BANK15_BASE_ADDRESS | + * |------------------------------------------------------------| + * | BANK0_MAX_ID | BANK0_MIN_ID | + * |------------------------------------------------------------| + * | ... | + * |------------------------------------------------------------| + * | BANK15_MAX_ID | BANK15_MIN_ID | + * |------------------------------------------------------------| + * Header fields: + * - MSG_TYPE + * Bits 7:0 + * Value: 0x6 + * - BANKx_BASE_ADDRESS + * Bits 31:0 + * Purpose: Provide a mechanism to specify the base address of the MSDU_EXT + * bank physical/bus address. + * - BANKx_MIN_ID + * Bits 15:0 + * Purpose: Provide a mechanism to specify the min index that needs to + * mapped. + * - BANKx_MAX_ID + * Bits 31:16 + * Purpose: Provide a mechanism to specify the max index that needs to + * + */ +struct htt_frag_desc_bank_id { + __le16 bank_min_id; + __le16 bank_max_id; +} __packed; + +/* real is 16 but it wouldn't fit in the max htt message size + * so we use a conservatively safe value for now */ +#define HTT_FRAG_DESC_BANK_MAX 4 + +#define HTT_FRAG_DESC_BANK_CFG_INFO_PDEV_ID_MASK 0x03 +#define HTT_FRAG_DESC_BANK_CFG_INFO_PDEV_ID_LSB 0 +#define HTT_FRAG_DESC_BANK_CFG_INFO_SWAP (1 << 2) + +struct htt_frag_desc_bank_cfg { + u8 info; /* HTT_FRAG_DESC_BANK_CFG_INFO_ */ + u8 num_banks; + u8 desc_size; + __le32 bank_base_addrs[HTT_FRAG_DESC_BANK_MAX]; + struct htt_frag_desc_bank_id bank_id[HTT_FRAG_DESC_BANK_MAX]; +} __packed; + +union htt_rx_pn_t { + /* WEP: 24-bit PN */ + u32 pn24; + + /* TKIP or CCMP: 48-bit PN */ + u_int64_t pn48; + + /* WAPI: 128-bit PN */ + u_int64_t pn128[2]; +}; + +struct htt_cmd { + struct htt_cmd_hdr hdr; + union { + struct htt_ver_req ver_req; + struct htt_mgmt_tx_desc mgmt_tx; + struct htt_data_tx_desc data_tx; + struct htt_rx_ring_setup rx_setup; + struct htt_stats_req stats_req; + struct htt_oob_sync_req oob_sync_req; + struct htt_aggr_conf aggr_conf; + struct htt_frag_desc_bank_cfg frag_desc_bank_cfg; + }; +} __packed; + +struct htt_resp { + struct htt_resp_hdr hdr; + union { + struct htt_ver_resp ver_resp; + struct htt_mgmt_tx_completion mgmt_tx_completion; + struct htt_data_tx_completion data_tx_completion; + struct htt_rx_indication rx_ind; + struct htt_rx_fragment_indication rx_frag_ind; + struct htt_rx_peer_map peer_map; + struct htt_rx_peer_unmap peer_unmap; + struct htt_rx_flush rx_flush; + struct htt_rx_addba rx_addba; + struct htt_rx_delba rx_delba; + struct htt_security_indication security_indication; + struct htt_rc_update rc_update; + struct htt_rx_test rx_test; + struct htt_pktlog_msg pktlog_msg; + struct htt_stats_conf stats_conf; + }; +} __packed; + + +/*** host side structures follow ***/ + +struct htt_tx_done { + u32 msdu_id; + bool discard; + bool no_ack; +}; + +struct htt_peer_map_event { + u8 vdev_id; + u16 peer_id; + u8 addr[ETH_ALEN]; +}; + +struct htt_peer_unmap_event { + u16 peer_id; +}; + +struct htt_rx_info { + struct sk_buff *skb; + enum htt_rx_mpdu_status status; + enum htt_rx_mpdu_encrypt_type encrypt_type; + s8 signal; + struct { + u8 info0; + u32 info1; + u32 info2; + } rate; + bool fcs_err; +}; + +struct ath10k_htt { + struct ath10k *ar; + enum ath10k_htc_ep_id eid; + + int max_throughput_mbps; + u8 target_version_major; + u8 target_version_minor; + struct completion target_version_received; + + struct { + /* + * Ring of network buffer objects - This ring is + * used exclusively by the host SW. This ring + * mirrors the dev_addrs_ring that is shared + * between the host SW and the MAC HW. The host SW + * uses this netbufs ring to locate the network + * buffer objects whose data buffers the HW has + * filled. + */ + struct sk_buff **netbufs_ring; + /* + * Ring of buffer addresses - + * This ring holds the "physical" device address of the + * rx buffers the host SW provides for the MAC HW to + * fill. + */ + __le32 *paddrs_ring; + + /* + * Base address of ring, as a "physical" device address + * rather than a CPU address. + */ + dma_addr_t base_paddr; + + /* how many elems in the ring (power of 2) */ + int size; + + /* size - 1 */ + unsigned size_mask; + + /* how many rx buffers to keep in the ring */ + int fill_level; + + /* how many rx buffers (full+empty) are in the ring */ + int fill_cnt; + + /* + * alloc_idx - where HTT SW has deposited empty buffers + * This is allocated in consistent mem, so that the FW can + * read this variable, and program the HW's FW_IDX reg with + * the value of this shadow register. + */ + struct { + __le32 *vaddr; + dma_addr_t paddr; + } alloc_idx; + + /* where HTT SW has processed bufs filled by rx MAC DMA */ + struct { + unsigned msdu_payld; + } sw_rd_idx; + + /* + * refill_retry_timer - timer triggered when the ring is + * not refilled to the level expected + */ + struct timer_list refill_retry_timer; + + /* Protects access to all rx ring buffer state variables */ + spinlock_t lock; + } rx_ring; + + unsigned int prefetch_len; + + /* Protects access to %pending_tx, %used_msdu_ids */ + spinlock_t tx_lock; + int max_num_pending_tx; + int num_pending_tx; + struct sk_buff **pending_tx; + unsigned long *used_msdu_ids; /* bitmap */ + wait_queue_head_t empty_tx_wq; + + /* set if host-fw communication goes haywire + * used to avoid further failures */ + bool rx_confused; +}; + +#define RX_HTT_HDR_STATUS_LEN 64 + +/* This structure layout is programmed via rx ring setup + * so that FW knows how to transfer the rx descriptor to the host. + * Buffers like this are placed on the rx ring. */ +struct htt_rx_desc { + union { + /* This field is filled on the host using the msdu buffer + * from htt_rx_indication */ + struct fw_rx_desc_base fw_desc; + u32 pad; + } __packed; + struct { + struct rx_attention attention; + struct rx_frag_info frag_info; + struct rx_mpdu_start mpdu_start; + struct rx_msdu_start msdu_start; + struct rx_msdu_end msdu_end; + struct rx_mpdu_end mpdu_end; + struct rx_ppdu_start ppdu_start; + struct rx_ppdu_end ppdu_end; + } __packed; + u8 rx_hdr_status[RX_HTT_HDR_STATUS_LEN]; + u8 msdu_payload[0]; +}; + +#define HTT_RX_DESC_ALIGN 8 + +#define HTT_MAC_ADDR_LEN 6 + +/* + * FIX THIS + * Should be: sizeof(struct htt_host_rx_desc) + max rx MSDU size, + * rounded up to a cache line size. + */ +#define HTT_RX_BUF_SIZE 1920 +#define HTT_RX_MSDU_SIZE (HTT_RX_BUF_SIZE - (int)sizeof(struct htt_rx_desc)) + +/* + * DMA_MAP expects the buffer to be an integral number of cache lines. + * Rather than checking the actual cache line size, this code makes a + * conservative estimate of what the cache line size could be. + */ +#define HTT_LOG2_MAX_CACHE_LINE_SIZE 7 /* 2^7 = 128 */ +#define HTT_MAX_CACHE_LINE_SIZE_MASK ((1 << HTT_LOG2_MAX_CACHE_LINE_SIZE) - 1) + +struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar); +int ath10k_htt_attach_target(struct ath10k_htt *htt); +void ath10k_htt_detach(struct ath10k_htt *htt); + +int ath10k_htt_tx_attach(struct ath10k_htt *htt); +void ath10k_htt_tx_detach(struct ath10k_htt *htt); +int ath10k_htt_rx_attach(struct ath10k_htt *htt); +void ath10k_htt_rx_detach(struct ath10k_htt *htt); +void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb); +void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb); +int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt); +int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt); + +void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt); +int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt); +void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id); +int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *); +int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *); +#endif diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c new file mode 100644 index 000000000000..de058d7adca8 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -0,0 +1,1167 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "htc.h" +#include "htt.h" +#include "txrx.h" +#include "debug.h" + +#include + +/* slightly larger than one large A-MPDU */ +#define HTT_RX_RING_SIZE_MIN 128 + +/* roughly 20 ms @ 1 Gbps of 1500B MSDUs */ +#define HTT_RX_RING_SIZE_MAX 2048 + +#define HTT_RX_AVG_FRM_BYTES 1000 + +/* ms, very conservative */ +#define HTT_RX_HOST_LATENCY_MAX_MS 20 + +/* ms, conservative */ +#define HTT_RX_HOST_LATENCY_WORST_LIKELY_MS 10 + +/* when under memory pressure rx ring refill may fail and needs a retry */ +#define HTT_RX_RING_REFILL_RETRY_MS 50 + +static int ath10k_htt_rx_ring_size(struct ath10k_htt *htt) +{ + int size; + + /* + * It is expected that the host CPU will typically be able to + * service the rx indication from one A-MPDU before the rx + * indication from the subsequent A-MPDU happens, roughly 1-2 ms + * later. However, the rx ring should be sized very conservatively, + * to accomodate the worst reasonable delay before the host CPU + * services a rx indication interrupt. + * + * The rx ring need not be kept full of empty buffers. In theory, + * the htt host SW can dynamically track the low-water mark in the + * rx ring, and dynamically adjust the level to which the rx ring + * is filled with empty buffers, to dynamically meet the desired + * low-water mark. + * + * In contrast, it's difficult to resize the rx ring itself, once + * it's in use. Thus, the ring itself should be sized very + * conservatively, while the degree to which the ring is filled + * with empty buffers should be sized moderately conservatively. + */ + + /* 1e6 bps/mbps / 1e3 ms per sec = 1000 */ + size = + htt->max_throughput_mbps + + 1000 / + (8 * HTT_RX_AVG_FRM_BYTES) * HTT_RX_HOST_LATENCY_MAX_MS; + + if (size < HTT_RX_RING_SIZE_MIN) + size = HTT_RX_RING_SIZE_MIN; + + if (size > HTT_RX_RING_SIZE_MAX) + size = HTT_RX_RING_SIZE_MAX; + + size = roundup_pow_of_two(size); + + return size; +} + +static int ath10k_htt_rx_ring_fill_level(struct ath10k_htt *htt) +{ + int size; + + /* 1e6 bps/mbps / 1e3 ms per sec = 1000 */ + size = + htt->max_throughput_mbps * + 1000 / + (8 * HTT_RX_AVG_FRM_BYTES) * HTT_RX_HOST_LATENCY_WORST_LIKELY_MS; + + /* + * Make sure the fill level is at least 1 less than the ring size. + * Leaving 1 element empty allows the SW to easily distinguish + * between a full ring vs. an empty ring. + */ + if (size >= htt->rx_ring.size) + size = htt->rx_ring.size - 1; + + return size; +} + +static void ath10k_htt_rx_ring_free(struct ath10k_htt *htt) +{ + struct sk_buff *skb; + struct ath10k_skb_cb *cb; + int i; + + for (i = 0; i < htt->rx_ring.fill_cnt; i++) { + skb = htt->rx_ring.netbufs_ring[i]; + cb = ATH10K_SKB_CB(skb); + dma_unmap_single(htt->ar->dev, cb->paddr, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + dev_kfree_skb_any(skb); + } + + htt->rx_ring.fill_cnt = 0; +} + +static int __ath10k_htt_rx_ring_fill_n(struct ath10k_htt *htt, int num) +{ + struct htt_rx_desc *rx_desc; + struct sk_buff *skb; + dma_addr_t paddr; + int ret = 0, idx; + + idx = __le32_to_cpu(*(htt->rx_ring.alloc_idx.vaddr)); + while (num > 0) { + skb = dev_alloc_skb(HTT_RX_BUF_SIZE + HTT_RX_DESC_ALIGN); + if (!skb) { + ret = -ENOMEM; + goto fail; + } + + if (!IS_ALIGNED((unsigned long)skb->data, HTT_RX_DESC_ALIGN)) + skb_pull(skb, + PTR_ALIGN(skb->data, HTT_RX_DESC_ALIGN) - + skb->data); + + /* Clear rx_desc attention word before posting to Rx ring */ + rx_desc = (struct htt_rx_desc *)skb->data; + rx_desc->attention.flags = __cpu_to_le32(0); + + paddr = dma_map_single(htt->ar->dev, skb->data, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + + if (unlikely(dma_mapping_error(htt->ar->dev, paddr))) { + dev_kfree_skb_any(skb); + ret = -ENOMEM; + goto fail; + } + + ATH10K_SKB_CB(skb)->paddr = paddr; + htt->rx_ring.netbufs_ring[idx] = skb; + htt->rx_ring.paddrs_ring[idx] = __cpu_to_le32(paddr); + htt->rx_ring.fill_cnt++; + + num--; + idx++; + idx &= htt->rx_ring.size_mask; + } + +fail: + *(htt->rx_ring.alloc_idx.vaddr) = __cpu_to_le32(idx); + return ret; +} + +static int ath10k_htt_rx_ring_fill_n(struct ath10k_htt *htt, int num) +{ + lockdep_assert_held(&htt->rx_ring.lock); + return __ath10k_htt_rx_ring_fill_n(htt, num); +} + +static void ath10k_htt_rx_msdu_buff_replenish(struct ath10k_htt *htt) +{ + int ret, num_to_fill; + + spin_lock_bh(&htt->rx_ring.lock); + num_to_fill = htt->rx_ring.fill_level - htt->rx_ring.fill_cnt; + ret = ath10k_htt_rx_ring_fill_n(htt, num_to_fill); + if (ret == -ENOMEM) { + /* + * Failed to fill it to the desired level - + * we'll start a timer and try again next time. + * As long as enough buffers are left in the ring for + * another A-MPDU rx, no special recovery is needed. + */ + mod_timer(&htt->rx_ring.refill_retry_timer, jiffies + + msecs_to_jiffies(HTT_RX_RING_REFILL_RETRY_MS)); + } + spin_unlock_bh(&htt->rx_ring.lock); +} + +static void ath10k_htt_rx_ring_refill_retry(unsigned long arg) +{ + struct ath10k_htt *htt = (struct ath10k_htt *)arg; + ath10k_htt_rx_msdu_buff_replenish(htt); +} + +static unsigned ath10k_htt_rx_ring_elems(struct ath10k_htt *htt) +{ + return (__le32_to_cpu(*htt->rx_ring.alloc_idx.vaddr) - + htt->rx_ring.sw_rd_idx.msdu_payld) & htt->rx_ring.size_mask; +} + +void ath10k_htt_rx_detach(struct ath10k_htt *htt) +{ + int sw_rd_idx = htt->rx_ring.sw_rd_idx.msdu_payld; + + del_timer_sync(&htt->rx_ring.refill_retry_timer); + + while (sw_rd_idx != __le32_to_cpu(*(htt->rx_ring.alloc_idx.vaddr))) { + struct sk_buff *skb = + htt->rx_ring.netbufs_ring[sw_rd_idx]; + struct ath10k_skb_cb *cb = ATH10K_SKB_CB(skb); + + dma_unmap_single(htt->ar->dev, cb->paddr, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + dev_kfree_skb_any(htt->rx_ring.netbufs_ring[sw_rd_idx]); + sw_rd_idx++; + sw_rd_idx &= htt->rx_ring.size_mask; + } + + dma_free_coherent(htt->ar->dev, + (htt->rx_ring.size * + sizeof(htt->rx_ring.paddrs_ring)), + htt->rx_ring.paddrs_ring, + htt->rx_ring.base_paddr); + + dma_free_coherent(htt->ar->dev, + sizeof(*htt->rx_ring.alloc_idx.vaddr), + htt->rx_ring.alloc_idx.vaddr, + htt->rx_ring.alloc_idx.paddr); + + kfree(htt->rx_ring.netbufs_ring); +} + +static inline struct sk_buff *ath10k_htt_rx_netbuf_pop(struct ath10k_htt *htt) +{ + int idx; + struct sk_buff *msdu; + + spin_lock_bh(&htt->rx_ring.lock); + + if (ath10k_htt_rx_ring_elems(htt) == 0) + ath10k_warn("htt rx ring is empty!\n"); + + idx = htt->rx_ring.sw_rd_idx.msdu_payld; + msdu = htt->rx_ring.netbufs_ring[idx]; + + idx++; + idx &= htt->rx_ring.size_mask; + htt->rx_ring.sw_rd_idx.msdu_payld = idx; + htt->rx_ring.fill_cnt--; + + spin_unlock_bh(&htt->rx_ring.lock); + return msdu; +} + +static void ath10k_htt_rx_free_msdu_chain(struct sk_buff *skb) +{ + struct sk_buff *next; + + while (skb) { + next = skb->next; + dev_kfree_skb_any(skb); + skb = next; + } +} + +static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, + u8 **fw_desc, int *fw_desc_len, + struct sk_buff **head_msdu, + struct sk_buff **tail_msdu) +{ + int msdu_len, msdu_chaining = 0; + struct sk_buff *msdu; + struct htt_rx_desc *rx_desc; + + if (ath10k_htt_rx_ring_elems(htt) == 0) + ath10k_warn("htt rx ring is empty!\n"); + + if (htt->rx_confused) { + ath10k_warn("htt is confused. refusing rx\n"); + return 0; + } + + msdu = *head_msdu = ath10k_htt_rx_netbuf_pop(htt); + while (msdu) { + int last_msdu, msdu_len_invalid, msdu_chained; + + dma_unmap_single(htt->ar->dev, + ATH10K_SKB_CB(msdu)->paddr, + msdu->len + skb_tailroom(msdu), + DMA_FROM_DEVICE); + + ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx: ", + msdu->data, msdu->len + skb_tailroom(msdu)); + + rx_desc = (struct htt_rx_desc *)msdu->data; + + /* FIXME: we must report msdu payload since this is what caller + * expects now */ + skb_put(msdu, offsetof(struct htt_rx_desc, msdu_payload)); + skb_pull(msdu, offsetof(struct htt_rx_desc, msdu_payload)); + + /* + * Sanity check - confirm the HW is finished filling in the + * rx data. + * If the HW and SW are working correctly, then it's guaranteed + * that the HW's MAC DMA is done before this point in the SW. + * To prevent the case that we handle a stale Rx descriptor, + * just assert for now until we have a way to recover. + */ + if (!(__le32_to_cpu(rx_desc->attention.flags) + & RX_ATTENTION_FLAGS_MSDU_DONE)) { + ath10k_htt_rx_free_msdu_chain(*head_msdu); + *head_msdu = NULL; + msdu = NULL; + ath10k_err("htt rx stopped. cannot recover\n"); + htt->rx_confused = true; + break; + } + + /* + * Copy the FW rx descriptor for this MSDU from the rx + * indication message into the MSDU's netbuf. HL uses the + * same rx indication message definition as LL, and simply + * appends new info (fields from the HW rx desc, and the + * MSDU payload itself). So, the offset into the rx + * indication message only has to account for the standard + * offset of the per-MSDU FW rx desc info within the + * message, and how many bytes of the per-MSDU FW rx desc + * info have already been consumed. (And the endianness of + * the host, since for a big-endian host, the rx ind + * message contents, including the per-MSDU rx desc bytes, + * were byteswapped during upload.) + */ + if (*fw_desc_len > 0) { + rx_desc->fw_desc.info0 = **fw_desc; + /* + * The target is expected to only provide the basic + * per-MSDU rx descriptors. Just to be sure, verify + * that the target has not attached extension data + * (e.g. LRO flow ID). + */ + + /* or more, if there's extension data */ + (*fw_desc)++; + (*fw_desc_len)--; + } else { + /* + * When an oversized AMSDU happened, FW will lost + * some of MSDU status - in this case, the FW + * descriptors provided will be less than the + * actual MSDUs inside this MPDU. Mark the FW + * descriptors so that it will still deliver to + * upper stack, if no CRC error for this MPDU. + * + * FIX THIS - the FW descriptors are actually for + * MSDUs in the end of this A-MSDU instead of the + * beginning. + */ + rx_desc->fw_desc.info0 = 0; + } + + msdu_len_invalid = !!(__le32_to_cpu(rx_desc->attention.flags) + & (RX_ATTENTION_FLAGS_MPDU_LENGTH_ERR | + RX_ATTENTION_FLAGS_MSDU_LENGTH_ERR)); + msdu_len = MS(__le32_to_cpu(rx_desc->msdu_start.info0), + RX_MSDU_START_INFO0_MSDU_LENGTH); + msdu_chained = rx_desc->frag_info.ring2_more_count; + + if (msdu_len_invalid) + msdu_len = 0; + + skb_trim(msdu, 0); + skb_put(msdu, min(msdu_len, HTT_RX_MSDU_SIZE)); + msdu_len -= msdu->len; + + /* FIXME: Do chained buffers include htt_rx_desc or not? */ + while (msdu_chained--) { + struct sk_buff *next = ath10k_htt_rx_netbuf_pop(htt); + + dma_unmap_single(htt->ar->dev, + ATH10K_SKB_CB(next)->paddr, + next->len + skb_tailroom(next), + DMA_FROM_DEVICE); + + ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx: ", + next->data, + next->len + skb_tailroom(next)); + + skb_trim(next, 0); + skb_put(next, min(msdu_len, HTT_RX_BUF_SIZE)); + msdu_len -= next->len; + + msdu->next = next; + msdu = next; + msdu_chaining = 1; + } + + if (msdu_len > 0) { + /* This may suggest FW bug? */ + ath10k_warn("htt rx msdu len not consumed (%d)\n", + msdu_len); + } + + last_msdu = __le32_to_cpu(rx_desc->msdu_end.info0) & + RX_MSDU_END_INFO0_LAST_MSDU; + + if (last_msdu) { + msdu->next = NULL; + break; + } else { + struct sk_buff *next = ath10k_htt_rx_netbuf_pop(htt); + msdu->next = next; + msdu = next; + } + } + *tail_msdu = msdu; + + /* + * Don't refill the ring yet. + * + * First, the elements popped here are still in use - it is not + * safe to overwrite them until the matching call to + * mpdu_desc_list_next. Second, for efficiency it is preferable to + * refill the rx ring with 1 PPDU's worth of rx buffers (something + * like 32 x 3 buffers), rather than one MPDU's worth of rx buffers + * (something like 3 buffers). Consequently, we'll rely on the txrx + * SW to tell us when it is done pulling all the PPDU's rx buffers + * out of the rx ring, and then refill it just once. + */ + + return msdu_chaining; +} + +int ath10k_htt_rx_attach(struct ath10k_htt *htt) +{ + dma_addr_t paddr; + void *vaddr; + struct timer_list *timer = &htt->rx_ring.refill_retry_timer; + + htt->rx_ring.size = ath10k_htt_rx_ring_size(htt); + if (!is_power_of_2(htt->rx_ring.size)) { + ath10k_warn("htt rx ring size is not power of 2\n"); + return -EINVAL; + } + + htt->rx_ring.size_mask = htt->rx_ring.size - 1; + + /* + * Set the initial value for the level to which the rx ring + * should be filled, based on the max throughput and the + * worst likely latency for the host to fill the rx ring + * with new buffers. In theory, this fill level can be + * dynamically adjusted from the initial value set here, to + * reflect the actual host latency rather than a + * conservative assumption about the host latency. + */ + htt->rx_ring.fill_level = ath10k_htt_rx_ring_fill_level(htt); + + htt->rx_ring.netbufs_ring = + kmalloc(htt->rx_ring.size * sizeof(struct sk_buff *), + GFP_KERNEL); + if (!htt->rx_ring.netbufs_ring) + goto err_netbuf; + + vaddr = dma_alloc_coherent(htt->ar->dev, + (htt->rx_ring.size * sizeof(htt->rx_ring.paddrs_ring)), + &paddr, GFP_DMA); + if (!vaddr) + goto err_dma_ring; + + htt->rx_ring.paddrs_ring = vaddr; + htt->rx_ring.base_paddr = paddr; + + vaddr = dma_alloc_coherent(htt->ar->dev, + sizeof(*htt->rx_ring.alloc_idx.vaddr), + &paddr, GFP_DMA); + if (!vaddr) + goto err_dma_idx; + + htt->rx_ring.alloc_idx.vaddr = vaddr; + htt->rx_ring.alloc_idx.paddr = paddr; + htt->rx_ring.sw_rd_idx.msdu_payld = 0; + *htt->rx_ring.alloc_idx.vaddr = 0; + + /* Initialize the Rx refill retry timer */ + setup_timer(timer, ath10k_htt_rx_ring_refill_retry, (unsigned long)htt); + + spin_lock_init(&htt->rx_ring.lock); + + htt->rx_ring.fill_cnt = 0; + if (__ath10k_htt_rx_ring_fill_n(htt, htt->rx_ring.fill_level)) + goto err_fill_ring; + + ath10k_dbg(ATH10K_DBG_HTT, "HTT RX ring size: %d, fill_level: %d\n", + htt->rx_ring.size, htt->rx_ring.fill_level); + return 0; + +err_fill_ring: + ath10k_htt_rx_ring_free(htt); + dma_free_coherent(htt->ar->dev, + sizeof(*htt->rx_ring.alloc_idx.vaddr), + htt->rx_ring.alloc_idx.vaddr, + htt->rx_ring.alloc_idx.paddr); +err_dma_idx: + dma_free_coherent(htt->ar->dev, + (htt->rx_ring.size * + sizeof(htt->rx_ring.paddrs_ring)), + htt->rx_ring.paddrs_ring, + htt->rx_ring.base_paddr); +err_dma_ring: + kfree(htt->rx_ring.netbufs_ring); +err_netbuf: + return -ENOMEM; +} + +static int ath10k_htt_rx_crypto_param_len(enum htt_rx_mpdu_encrypt_type type) +{ + switch (type) { + case HTT_RX_MPDU_ENCRYPT_WEP40: + case HTT_RX_MPDU_ENCRYPT_WEP104: + return 4; + case HTT_RX_MPDU_ENCRYPT_TKIP_WITHOUT_MIC: + case HTT_RX_MPDU_ENCRYPT_WEP128: /* not tested */ + case HTT_RX_MPDU_ENCRYPT_TKIP_WPA: + case HTT_RX_MPDU_ENCRYPT_WAPI: /* not tested */ + case HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2: + return 8; + case HTT_RX_MPDU_ENCRYPT_NONE: + return 0; + } + + ath10k_warn("unknown encryption type %d\n", type); + return 0; +} + +static int ath10k_htt_rx_crypto_tail_len(enum htt_rx_mpdu_encrypt_type type) +{ + switch (type) { + case HTT_RX_MPDU_ENCRYPT_NONE: + case HTT_RX_MPDU_ENCRYPT_WEP40: + case HTT_RX_MPDU_ENCRYPT_WEP104: + case HTT_RX_MPDU_ENCRYPT_WEP128: + case HTT_RX_MPDU_ENCRYPT_WAPI: + return 0; + case HTT_RX_MPDU_ENCRYPT_TKIP_WITHOUT_MIC: + case HTT_RX_MPDU_ENCRYPT_TKIP_WPA: + return 4; + case HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2: + return 8; + } + + ath10k_warn("unknown encryption type %d\n", type); + return 0; +} + +/* Applies for first msdu in chain, before altering it. */ +static struct ieee80211_hdr *ath10k_htt_rx_skb_get_hdr(struct sk_buff *skb) +{ + struct htt_rx_desc *rxd; + enum rx_msdu_decap_format fmt; + + rxd = (void *)skb->data - sizeof(*rxd); + fmt = MS(__le32_to_cpu(rxd->msdu_start.info1), + RX_MSDU_START_INFO1_DECAP_FORMAT); + + if (fmt == RX_MSDU_DECAP_RAW) + return (void *)skb->data; + else + return (void *)skb->data - RX_HTT_HDR_STATUS_LEN; +} + +/* This function only applies for first msdu in an msdu chain */ +static bool ath10k_htt_rx_hdr_is_amsdu(struct ieee80211_hdr *hdr) +{ + if (ieee80211_is_data_qos(hdr->frame_control)) { + u8 *qc = ieee80211_get_qos_ctl(hdr); + if (qc[0] & 0x80) + return true; + } + return false; +} + +static int ath10k_htt_rx_amsdu(struct ath10k_htt *htt, + struct htt_rx_info *info) +{ + struct htt_rx_desc *rxd; + struct sk_buff *amsdu; + struct sk_buff *first; + struct ieee80211_hdr *hdr; + struct sk_buff *skb = info->skb; + enum rx_msdu_decap_format fmt; + enum htt_rx_mpdu_encrypt_type enctype; + unsigned int hdr_len; + int crypto_len; + + rxd = (void *)skb->data - sizeof(*rxd); + fmt = MS(__le32_to_cpu(rxd->msdu_start.info1), + RX_MSDU_START_INFO1_DECAP_FORMAT); + enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0), + RX_MPDU_START_INFO0_ENCRYPT_TYPE); + + /* FIXME: No idea what assumptions are safe here. Need logs */ + if ((fmt == RX_MSDU_DECAP_RAW && skb->next) || + (fmt == RX_MSDU_DECAP_8023_SNAP_LLC)) { + ath10k_htt_rx_free_msdu_chain(skb->next); + skb->next = NULL; + return -ENOTSUPP; + } + + /* A-MSDU max is a little less than 8K */ + amsdu = dev_alloc_skb(8*1024); + if (!amsdu) { + ath10k_warn("A-MSDU allocation failed\n"); + ath10k_htt_rx_free_msdu_chain(skb->next); + skb->next = NULL; + return -ENOMEM; + } + + if (fmt >= RX_MSDU_DECAP_NATIVE_WIFI) { + int hdrlen; + + hdr = (void *)rxd->rx_hdr_status; + hdrlen = ieee80211_hdrlen(hdr->frame_control); + memcpy(skb_put(amsdu, hdrlen), hdr, hdrlen); + } + + first = skb; + while (skb) { + void *decap_hdr; + int decap_len = 0; + + rxd = (void *)skb->data - sizeof(*rxd); + fmt = MS(__le32_to_cpu(rxd->msdu_start.info1), + RX_MSDU_START_INFO1_DECAP_FORMAT); + decap_hdr = (void *)rxd->rx_hdr_status; + + if (skb == first) { + /* We receive linked A-MSDU subframe skbuffs. The + * first one contains the original 802.11 header (and + * possible crypto param) in the RX descriptor. The + * A-MSDU subframe header follows that. Each part is + * aligned to 4 byte boundary. */ + + hdr = (void *)amsdu->data; + hdr_len = ieee80211_hdrlen(hdr->frame_control); + crypto_len = ath10k_htt_rx_crypto_param_len(enctype); + + decap_hdr += roundup(hdr_len, 4); + decap_hdr += roundup(crypto_len, 4); + } + + if (fmt == RX_MSDU_DECAP_ETHERNET2_DIX) { + /* Ethernet2 decap inserts ethernet header in place of + * A-MSDU subframe header. */ + skb_pull(skb, 6 + 6 + 2); + + /* A-MSDU subframe header length */ + decap_len += 6 + 6 + 2; + + /* Ethernet2 decap also strips the LLC/SNAP so we need + * to re-insert it. The LLC/SNAP follows A-MSDU + * subframe header. */ + /* FIXME: Not all LLCs are 8 bytes long */ + decap_len += 8; + + memcpy(skb_put(amsdu, decap_len), decap_hdr, decap_len); + } + + if (fmt == RX_MSDU_DECAP_NATIVE_WIFI) { + /* Native Wifi decap inserts regular 802.11 header + * in place of A-MSDU subframe header. */ + hdr = (struct ieee80211_hdr *)skb->data; + skb_pull(skb, ieee80211_hdrlen(hdr->frame_control)); + + /* A-MSDU subframe header length */ + decap_len += 6 + 6 + 2; + + memcpy(skb_put(amsdu, decap_len), decap_hdr, decap_len); + } + + if (fmt == RX_MSDU_DECAP_RAW) + skb_trim(skb, skb->len - 4); /* remove FCS */ + + memcpy(skb_put(amsdu, skb->len), skb->data, skb->len); + + /* A-MSDU subframes are padded to 4bytes + * but relative to first subframe, not the whole MPDU */ + if (skb->next && ((decap_len + skb->len) & 3)) { + int padlen = 4 - ((decap_len + skb->len) & 3); + memset(skb_put(amsdu, padlen), 0, padlen); + } + + skb = skb->next; + } + + info->skb = amsdu; + info->encrypt_type = enctype; + + ath10k_htt_rx_free_msdu_chain(first); + + return 0; +} + +static int ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct htt_rx_info *info) +{ + struct sk_buff *skb = info->skb; + struct htt_rx_desc *rxd; + struct ieee80211_hdr *hdr; + enum rx_msdu_decap_format fmt; + enum htt_rx_mpdu_encrypt_type enctype; + + /* This shouldn't happen. If it does than it may be a FW bug. */ + if (skb->next) { + ath10k_warn("received chained non A-MSDU frame\n"); + ath10k_htt_rx_free_msdu_chain(skb->next); + skb->next = NULL; + } + + rxd = (void *)skb->data - sizeof(*rxd); + fmt = MS(__le32_to_cpu(rxd->msdu_start.info1), + RX_MSDU_START_INFO1_DECAP_FORMAT); + enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0), + RX_MPDU_START_INFO0_ENCRYPT_TYPE); + hdr = (void *)skb->data - RX_HTT_HDR_STATUS_LEN; + + switch (fmt) { + case RX_MSDU_DECAP_RAW: + /* remove trailing FCS */ + skb_trim(skb, skb->len - 4); + break; + case RX_MSDU_DECAP_NATIVE_WIFI: + /* nothing to do here */ + break; + case RX_MSDU_DECAP_ETHERNET2_DIX: + /* macaddr[6] + macaddr[6] + ethertype[2] */ + skb_pull(skb, 6 + 6 + 2); + break; + case RX_MSDU_DECAP_8023_SNAP_LLC: + /* macaddr[6] + macaddr[6] + len[2] */ + /* we don't need this for non-A-MSDU */ + skb_pull(skb, 6 + 6 + 2); + break; + } + + if (fmt == RX_MSDU_DECAP_ETHERNET2_DIX) { + void *llc; + int llclen; + + llclen = 8; + llc = hdr; + llc += roundup(ieee80211_hdrlen(hdr->frame_control), 4); + llc += roundup(ath10k_htt_rx_crypto_param_len(enctype), 4); + + skb_push(skb, llclen); + memcpy(skb->data, llc, llclen); + } + + if (fmt >= RX_MSDU_DECAP_ETHERNET2_DIX) { + int len = ieee80211_hdrlen(hdr->frame_control); + skb_push(skb, len); + memcpy(skb->data, hdr, len); + } + + info->skb = skb; + info->encrypt_type = enctype; + return 0; +} + +static bool ath10k_htt_rx_has_decrypt_err(struct sk_buff *skb) +{ + struct htt_rx_desc *rxd; + u32 flags; + + rxd = (void *)skb->data - sizeof(*rxd); + flags = __le32_to_cpu(rxd->attention.flags); + + if (flags & RX_ATTENTION_FLAGS_DECRYPT_ERR) + return true; + + return false; +} + +static bool ath10k_htt_rx_has_fcs_err(struct sk_buff *skb) +{ + struct htt_rx_desc *rxd; + u32 flags; + + rxd = (void *)skb->data - sizeof(*rxd); + flags = __le32_to_cpu(rxd->attention.flags); + + if (flags & RX_ATTENTION_FLAGS_FCS_ERR) + return true; + + return false; +} + +static void ath10k_htt_rx_handler(struct ath10k_htt *htt, + struct htt_rx_indication *rx) +{ + struct htt_rx_info info; + struct htt_rx_indication_mpdu_range *mpdu_ranges; + struct ieee80211_hdr *hdr; + int num_mpdu_ranges; + int fw_desc_len; + u8 *fw_desc; + int i, j; + int ret; + + memset(&info, 0, sizeof(info)); + + fw_desc_len = __le16_to_cpu(rx->prefix.fw_rx_desc_bytes); + fw_desc = (u8 *)&rx->fw_desc; + + num_mpdu_ranges = MS(__le32_to_cpu(rx->hdr.info1), + HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES); + mpdu_ranges = htt_rx_ind_get_mpdu_ranges(rx); + + ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx ind: ", + rx, sizeof(*rx) + + (sizeof(struct htt_rx_indication_mpdu_range) * + num_mpdu_ranges)); + + for (i = 0; i < num_mpdu_ranges; i++) { + info.status = mpdu_ranges[i].mpdu_range_status; + + for (j = 0; j < mpdu_ranges[i].mpdu_count; j++) { + struct sk_buff *msdu_head, *msdu_tail; + enum htt_rx_mpdu_status status; + int msdu_chaining; + + msdu_head = NULL; + msdu_tail = NULL; + msdu_chaining = ath10k_htt_rx_amsdu_pop(htt, + &fw_desc, + &fw_desc_len, + &msdu_head, + &msdu_tail); + + if (!msdu_head) { + ath10k_warn("htt rx no data!\n"); + continue; + } + + if (msdu_head->len == 0) { + ath10k_dbg(ATH10K_DBG_HTT, + "htt rx dropping due to zero-len\n"); + ath10k_htt_rx_free_msdu_chain(msdu_head); + continue; + } + + if (ath10k_htt_rx_has_decrypt_err(msdu_head)) { + ath10k_htt_rx_free_msdu_chain(msdu_head); + continue; + } + + status = info.status; + + /* Skip mgmt frames while we handle this in WMI */ + if (status == HTT_RX_IND_MPDU_STATUS_MGMT_CTRL) { + ath10k_htt_rx_free_msdu_chain(msdu_head); + continue; + } + + if (status != HTT_RX_IND_MPDU_STATUS_OK && + status != HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR && + !htt->ar->monitor_enabled) { + ath10k_dbg(ATH10K_DBG_HTT, + "htt rx ignoring frame w/ status %d\n", + status); + ath10k_htt_rx_free_msdu_chain(msdu_head); + continue; + } + + /* FIXME: we do not support chaining yet. + * this needs investigation */ + if (msdu_chaining) { + ath10k_warn("msdu_chaining is true\n"); + ath10k_htt_rx_free_msdu_chain(msdu_head); + continue; + } + + info.skb = msdu_head; + info.fcs_err = ath10k_htt_rx_has_fcs_err(msdu_head); + info.signal = ATH10K_DEFAULT_NOISE_FLOOR; + info.signal += rx->ppdu.combined_rssi; + + info.rate.info0 = rx->ppdu.info0; + info.rate.info1 = __le32_to_cpu(rx->ppdu.info1); + info.rate.info2 = __le32_to_cpu(rx->ppdu.info2); + + hdr = ath10k_htt_rx_skb_get_hdr(msdu_head); + + if (ath10k_htt_rx_hdr_is_amsdu(hdr)) + ret = ath10k_htt_rx_amsdu(htt, &info); + else + ret = ath10k_htt_rx_msdu(htt, &info); + + if (ret && !info.fcs_err) { + ath10k_warn("error processing msdus %d\n", ret); + dev_kfree_skb_any(info.skb); + continue; + } + + if (ath10k_htt_rx_hdr_is_amsdu((void *)info.skb->data)) + ath10k_dbg(ATH10K_DBG_HTT, "htt mpdu is amsdu\n"); + + ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt mpdu: ", + info.skb->data, info.skb->len); + ath10k_process_rx(htt->ar, &info); + } + } + + ath10k_htt_rx_msdu_buff_replenish(htt); +} + +static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt, + struct htt_rx_fragment_indication *frag) +{ + struct sk_buff *msdu_head, *msdu_tail; + struct htt_rx_desc *rxd; + enum rx_msdu_decap_format fmt; + struct htt_rx_info info = {}; + struct ieee80211_hdr *hdr; + int msdu_chaining; + bool tkip_mic_err; + bool decrypt_err; + u8 *fw_desc; + int fw_desc_len, hdrlen, paramlen; + int trim; + + fw_desc_len = __le16_to_cpu(frag->fw_rx_desc_bytes); + fw_desc = (u8 *)frag->fw_msdu_rx_desc; + + msdu_head = NULL; + msdu_tail = NULL; + msdu_chaining = ath10k_htt_rx_amsdu_pop(htt, &fw_desc, &fw_desc_len, + &msdu_head, &msdu_tail); + + ath10k_dbg(ATH10K_DBG_HTT_DUMP, "htt rx frag ahead\n"); + + if (!msdu_head) { + ath10k_warn("htt rx frag no data\n"); + return; + } + + if (msdu_chaining || msdu_head != msdu_tail) { + ath10k_warn("aggregation with fragmentation?!\n"); + ath10k_htt_rx_free_msdu_chain(msdu_head); + return; + } + + /* FIXME: implement signal strength */ + + hdr = (struct ieee80211_hdr *)msdu_head->data; + rxd = (void *)msdu_head->data - sizeof(*rxd); + tkip_mic_err = !!(__le32_to_cpu(rxd->attention.flags) & + RX_ATTENTION_FLAGS_TKIP_MIC_ERR); + decrypt_err = !!(__le32_to_cpu(rxd->attention.flags) & + RX_ATTENTION_FLAGS_DECRYPT_ERR); + fmt = MS(__le32_to_cpu(rxd->msdu_start.info1), + RX_MSDU_START_INFO1_DECAP_FORMAT); + + if (fmt != RX_MSDU_DECAP_RAW) { + ath10k_warn("we dont support non-raw fragmented rx yet\n"); + dev_kfree_skb_any(msdu_head); + goto end; + } + + info.skb = msdu_head; + info.status = HTT_RX_IND_MPDU_STATUS_OK; + info.encrypt_type = MS(__le32_to_cpu(rxd->mpdu_start.info0), + RX_MPDU_START_INFO0_ENCRYPT_TYPE); + + if (tkip_mic_err) { + ath10k_warn("tkip mic error\n"); + info.status = HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR; + } + + if (decrypt_err) { + ath10k_warn("decryption err in fragmented rx\n"); + dev_kfree_skb_any(info.skb); + goto end; + } + + if (info.encrypt_type != HTT_RX_MPDU_ENCRYPT_NONE) { + hdrlen = ieee80211_hdrlen(hdr->frame_control); + paramlen = ath10k_htt_rx_crypto_param_len(info.encrypt_type); + + /* It is more efficient to move the header than the payload */ + memmove((void *)info.skb->data + paramlen, + (void *)info.skb->data, + hdrlen); + skb_pull(info.skb, paramlen); + hdr = (struct ieee80211_hdr *)info.skb->data; + } + + /* remove trailing FCS */ + trim = 4; + + /* remove crypto trailer */ + trim += ath10k_htt_rx_crypto_tail_len(info.encrypt_type); + + /* last fragment of TKIP frags has MIC */ + if (!ieee80211_has_morefrags(hdr->frame_control) && + info.encrypt_type == HTT_RX_MPDU_ENCRYPT_TKIP_WPA) + trim += 8; + + if (trim > info.skb->len) { + ath10k_warn("htt rx fragment: trailer longer than the frame itself? drop\n"); + dev_kfree_skb_any(info.skb); + goto end; + } + + skb_trim(info.skb, info.skb->len - trim); + + ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt frag mpdu: ", + info.skb->data, info.skb->len); + ath10k_process_rx(htt->ar, &info); + +end: + if (fw_desc_len > 0) { + ath10k_dbg(ATH10K_DBG_HTT, + "expecting more fragmented rx in one indication %d\n", + fw_desc_len); + } +} + +void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) +{ + struct ath10k_htt *htt = ar->htt; + struct htt_resp *resp = (struct htt_resp *)skb->data; + + /* confirm alignment */ + if (!IS_ALIGNED((unsigned long)skb->data, 4)) + ath10k_warn("unaligned htt message, expect trouble\n"); + + ath10k_dbg(ATH10K_DBG_HTT, "HTT RX, msg_type: 0x%0X\n", + resp->hdr.msg_type); + switch (resp->hdr.msg_type) { + case HTT_T2H_MSG_TYPE_VERSION_CONF: { + htt->target_version_major = resp->ver_resp.major; + htt->target_version_minor = resp->ver_resp.minor; + complete(&htt->target_version_received); + break; + } + case HTT_T2H_MSG_TYPE_RX_IND: { + ath10k_htt_rx_handler(htt, &resp->rx_ind); + break; + } + case HTT_T2H_MSG_TYPE_PEER_MAP: { + struct htt_peer_map_event ev = { + .vdev_id = resp->peer_map.vdev_id, + .peer_id = __le16_to_cpu(resp->peer_map.peer_id), + }; + memcpy(ev.addr, resp->peer_map.addr, sizeof(ev.addr)); + ath10k_peer_map_event(htt, &ev); + break; + } + case HTT_T2H_MSG_TYPE_PEER_UNMAP: { + struct htt_peer_unmap_event ev = { + .peer_id = __le16_to_cpu(resp->peer_unmap.peer_id), + }; + ath10k_peer_unmap_event(htt, &ev); + break; + } + case HTT_T2H_MSG_TYPE_MGMT_TX_COMPLETION: { + struct htt_tx_done tx_done = {}; + int status = __le32_to_cpu(resp->mgmt_tx_completion.status); + + tx_done.msdu_id = + __le32_to_cpu(resp->mgmt_tx_completion.desc_id); + + switch (status) { + case HTT_MGMT_TX_STATUS_OK: + break; + case HTT_MGMT_TX_STATUS_RETRY: + tx_done.no_ack = true; + break; + case HTT_MGMT_TX_STATUS_DROP: + tx_done.discard = true; + break; + } + + ath10k_txrx_tx_completed(htt, &tx_done); + break; + } + case HTT_T2H_MSG_TYPE_TX_COMPL_IND: { + struct htt_tx_done tx_done = {}; + int status = MS(resp->data_tx_completion.flags, + HTT_DATA_TX_STATUS); + __le16 msdu_id; + int i; + + switch (status) { + case HTT_DATA_TX_STATUS_NO_ACK: + tx_done.no_ack = true; + break; + case HTT_DATA_TX_STATUS_OK: + break; + case HTT_DATA_TX_STATUS_DISCARD: + case HTT_DATA_TX_STATUS_POSTPONE: + case HTT_DATA_TX_STATUS_DOWNLOAD_FAIL: + tx_done.discard = true; + break; + default: + ath10k_warn("unhandled tx completion status %d\n", + status); + tx_done.discard = true; + break; + } + + ath10k_dbg(ATH10K_DBG_HTT, "htt tx completion num_msdus %d\n", + resp->data_tx_completion.num_msdus); + + for (i = 0; i < resp->data_tx_completion.num_msdus; i++) { + msdu_id = resp->data_tx_completion.msdus[i]; + tx_done.msdu_id = __le16_to_cpu(msdu_id); + ath10k_txrx_tx_completed(htt, &tx_done); + } + break; + } + case HTT_T2H_MSG_TYPE_SEC_IND: { + struct ath10k *ar = htt->ar; + struct htt_security_indication *ev = &resp->security_indication; + + ath10k_dbg(ATH10K_DBG_HTT, + "sec ind peer_id %d unicast %d type %d\n", + __le16_to_cpu(ev->peer_id), + !!(ev->flags & HTT_SECURITY_IS_UNICAST), + MS(ev->flags, HTT_SECURITY_TYPE)); + complete(&ar->install_key_done); + break; + } + case HTT_T2H_MSG_TYPE_RX_FRAG_IND: { + ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt event: ", + skb->data, skb->len); + ath10k_htt_rx_frag_handler(htt, &resp->rx_frag_ind); + break; + } + case HTT_T2H_MSG_TYPE_TEST: + /* FIX THIS */ + break; + case HTT_T2H_MSG_TYPE_TX_INSPECT_IND: + case HTT_T2H_MSG_TYPE_STATS_CONF: + case HTT_T2H_MSG_TYPE_RX_ADDBA: + case HTT_T2H_MSG_TYPE_RX_DELBA: + case HTT_T2H_MSG_TYPE_RX_FLUSH: + default: + ath10k_dbg(ATH10K_DBG_HTT, "htt event (%d) not handled\n", + resp->hdr.msg_type); + ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt event: ", + skb->data, skb->len); + break; + }; + + /* Free the indication buffer */ + dev_kfree_skb_any(skb); +} diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c new file mode 100644 index 000000000000..ef79106db247 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -0,0 +1,510 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include "htt.h" +#include "mac.h" +#include "hif.h" +#include "txrx.h" +#include "debug.h" + +void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt) +{ + htt->num_pending_tx--; + if (htt->num_pending_tx == htt->max_num_pending_tx - 1) + ieee80211_wake_queues(htt->ar->hw); +} + +static void ath10k_htt_tx_dec_pending(struct ath10k_htt *htt) +{ + spin_lock_bh(&htt->tx_lock); + __ath10k_htt_tx_dec_pending(htt); + spin_unlock_bh(&htt->tx_lock); +} + +static int ath10k_htt_tx_inc_pending(struct ath10k_htt *htt) +{ + int ret = 0; + + spin_lock_bh(&htt->tx_lock); + + if (htt->num_pending_tx >= htt->max_num_pending_tx) { + ret = -EBUSY; + goto exit; + } + + htt->num_pending_tx++; + if (htt->num_pending_tx == htt->max_num_pending_tx) + ieee80211_stop_queues(htt->ar->hw); + +exit: + spin_unlock_bh(&htt->tx_lock); + return ret; +} + +int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt) +{ + int msdu_id; + + lockdep_assert_held(&htt->tx_lock); + + msdu_id = find_first_zero_bit(htt->used_msdu_ids, + htt->max_num_pending_tx); + if (msdu_id == htt->max_num_pending_tx) + return -ENOBUFS; + + ath10k_dbg(ATH10K_DBG_HTT, "htt tx alloc msdu_id %d\n", msdu_id); + __set_bit(msdu_id, htt->used_msdu_ids); + return msdu_id; +} + +void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id) +{ + lockdep_assert_held(&htt->tx_lock); + + if (!test_bit(msdu_id, htt->used_msdu_ids)) + ath10k_warn("trying to free unallocated msdu_id %d\n", msdu_id); + + ath10k_dbg(ATH10K_DBG_HTT, "htt tx free msdu_id %hu\n", msdu_id); + __clear_bit(msdu_id, htt->used_msdu_ids); +} + +int ath10k_htt_tx_attach(struct ath10k_htt *htt) +{ + u8 pipe; + + spin_lock_init(&htt->tx_lock); + init_waitqueue_head(&htt->empty_tx_wq); + + /* At the beginning free queue number should hint us the maximum + * queue length */ + pipe = htt->ar->htc->endpoint[htt->eid].ul_pipe_id; + htt->max_num_pending_tx = ath10k_hif_get_free_queue_number(htt->ar, + pipe); + + ath10k_dbg(ATH10K_DBG_HTT, "htt tx max num pending tx %d\n", + htt->max_num_pending_tx); + + htt->pending_tx = kzalloc(sizeof(*htt->pending_tx) * + htt->max_num_pending_tx, GFP_KERNEL); + if (!htt->pending_tx) + return -ENOMEM; + + htt->used_msdu_ids = kzalloc(sizeof(unsigned long) * + BITS_TO_LONGS(htt->max_num_pending_tx), + GFP_KERNEL); + if (!htt->used_msdu_ids) { + kfree(htt->pending_tx); + return -ENOMEM; + } + + return 0; +} + +static void ath10k_htt_tx_cleanup_pending(struct ath10k_htt *htt) +{ + struct sk_buff *txdesc; + int msdu_id; + + /* No locks needed. Called after communication with the device has + * been stopped. */ + + for (msdu_id = 0; msdu_id < htt->max_num_pending_tx; msdu_id++) { + if (!test_bit(msdu_id, htt->used_msdu_ids)) + continue; + + txdesc = htt->pending_tx[msdu_id]; + if (!txdesc) + continue; + + ath10k_dbg(ATH10K_DBG_HTT, "force cleanup msdu_id %hu\n", + msdu_id); + + if (ATH10K_SKB_CB(txdesc)->htt.refcount > 0) + ATH10K_SKB_CB(txdesc)->htt.refcount = 1; + + ATH10K_SKB_CB(txdesc)->htt.discard = true; + ath10k_txrx_tx_unref(htt, txdesc); + } +} + +void ath10k_htt_tx_detach(struct ath10k_htt *htt) +{ + ath10k_htt_tx_cleanup_pending(htt); + kfree(htt->pending_tx); + kfree(htt->used_msdu_ids); + return; +} + +void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb) +{ + struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb); + struct ath10k_htt *htt = ar->htt; + + if (skb_cb->htt.is_conf) { + dev_kfree_skb_any(skb); + return; + } + + if (skb_cb->is_aborted) { + skb_cb->htt.discard = true; + + /* if the skbuff is aborted we need to make sure we'll free up + * the tx resources, we can't simply run tx_unref() 2 times + * because if htt tx completion came in earlier we'd access + * unallocated memory */ + if (skb_cb->htt.refcount > 1) + skb_cb->htt.refcount = 1; + } + + ath10k_txrx_tx_unref(htt, skb); +} + +int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt) +{ + struct sk_buff *skb; + struct htt_cmd *cmd; + int len = 0; + int ret; + + len += sizeof(cmd->hdr); + len += sizeof(cmd->ver_req); + + skb = ath10k_htc_alloc_skb(len); + if (!skb) + return -ENOMEM; + + skb_put(skb, len); + cmd = (struct htt_cmd *)skb->data; + cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_VERSION_REQ; + + ATH10K_SKB_CB(skb)->htt.is_conf = true; + + ret = ath10k_htc_send(htt->ar->htc, htt->eid, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + + return 0; +} + +int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt) +{ + struct sk_buff *skb; + struct htt_cmd *cmd; + struct htt_rx_ring_setup_ring *ring; + const int num_rx_ring = 1; + u16 flags; + u32 fw_idx; + int len; + int ret; + + /* + * the HW expects the buffer to be an integral number of 4-byte + * "words" + */ + BUILD_BUG_ON(!IS_ALIGNED(HTT_RX_BUF_SIZE, 4)); + BUILD_BUG_ON((HTT_RX_BUF_SIZE & HTT_MAX_CACHE_LINE_SIZE_MASK) != 0); + + len = sizeof(cmd->hdr) + sizeof(cmd->rx_setup.hdr) + + (sizeof(*ring) * num_rx_ring); + skb = ath10k_htc_alloc_skb(len); + if (!skb) + return -ENOMEM; + + skb_put(skb, len); + + cmd = (struct htt_cmd *)skb->data; + ring = &cmd->rx_setup.rings[0]; + + cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_RX_RING_CFG; + cmd->rx_setup.hdr.num_rings = 1; + + /* FIXME: do we need all of this? */ + flags = 0; + flags |= HTT_RX_RING_FLAGS_MAC80211_HDR; + flags |= HTT_RX_RING_FLAGS_MSDU_PAYLOAD; + flags |= HTT_RX_RING_FLAGS_PPDU_START; + flags |= HTT_RX_RING_FLAGS_PPDU_END; + flags |= HTT_RX_RING_FLAGS_MPDU_START; + flags |= HTT_RX_RING_FLAGS_MPDU_END; + flags |= HTT_RX_RING_FLAGS_MSDU_START; + flags |= HTT_RX_RING_FLAGS_MSDU_END; + flags |= HTT_RX_RING_FLAGS_RX_ATTENTION; + flags |= HTT_RX_RING_FLAGS_FRAG_INFO; + flags |= HTT_RX_RING_FLAGS_UNICAST_RX; + flags |= HTT_RX_RING_FLAGS_MULTICAST_RX; + flags |= HTT_RX_RING_FLAGS_CTRL_RX; + flags |= HTT_RX_RING_FLAGS_MGMT_RX; + flags |= HTT_RX_RING_FLAGS_NULL_RX; + flags |= HTT_RX_RING_FLAGS_PHY_DATA_RX; + + fw_idx = __le32_to_cpu(*htt->rx_ring.alloc_idx.vaddr); + + ring->fw_idx_shadow_reg_paddr = + __cpu_to_le32(htt->rx_ring.alloc_idx.paddr); + ring->rx_ring_base_paddr = __cpu_to_le32(htt->rx_ring.base_paddr); + ring->rx_ring_len = __cpu_to_le16(htt->rx_ring.size); + ring->rx_ring_bufsize = __cpu_to_le16(HTT_RX_BUF_SIZE); + ring->flags = __cpu_to_le16(flags); + ring->fw_idx_init_val = __cpu_to_le16(fw_idx); + +#define desc_offset(x) (offsetof(struct htt_rx_desc, x) / 4) + + ring->mac80211_hdr_offset = __cpu_to_le16(desc_offset(rx_hdr_status)); + ring->msdu_payload_offset = __cpu_to_le16(desc_offset(msdu_payload)); + ring->ppdu_start_offset = __cpu_to_le16(desc_offset(ppdu_start)); + ring->ppdu_end_offset = __cpu_to_le16(desc_offset(ppdu_end)); + ring->mpdu_start_offset = __cpu_to_le16(desc_offset(mpdu_start)); + ring->mpdu_end_offset = __cpu_to_le16(desc_offset(mpdu_end)); + ring->msdu_start_offset = __cpu_to_le16(desc_offset(msdu_start)); + ring->msdu_end_offset = __cpu_to_le16(desc_offset(msdu_end)); + ring->rx_attention_offset = __cpu_to_le16(desc_offset(attention)); + ring->frag_info_offset = __cpu_to_le16(desc_offset(frag_info)); + +#undef desc_offset + + ATH10K_SKB_CB(skb)->htt.is_conf = true; + + ret = ath10k_htc_send(htt->ar->htc, htt->eid, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + + return 0; +} + +int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) +{ + struct device *dev = htt->ar->dev; + struct ath10k_skb_cb *skb_cb; + struct sk_buff *txdesc = NULL; + struct htt_cmd *cmd; + u8 vdev_id = ATH10K_SKB_CB(msdu)->htt.vdev_id; + int len = 0; + int msdu_id = -1; + int res; + + + res = ath10k_htt_tx_inc_pending(htt); + if (res) + return res; + + len += sizeof(cmd->hdr); + len += sizeof(cmd->mgmt_tx); + + txdesc = ath10k_htc_alloc_skb(len); + if (!txdesc) { + res = -ENOMEM; + goto err; + } + + spin_lock_bh(&htt->tx_lock); + msdu_id = ath10k_htt_tx_alloc_msdu_id(htt); + if (msdu_id < 0) { + spin_unlock_bh(&htt->tx_lock); + res = msdu_id; + goto err; + } + htt->pending_tx[msdu_id] = txdesc; + spin_unlock_bh(&htt->tx_lock); + + res = ath10k_skb_map(dev, msdu); + if (res) + goto err; + + skb_put(txdesc, len); + cmd = (struct htt_cmd *)txdesc->data; + cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_MGMT_TX; + cmd->mgmt_tx.msdu_paddr = __cpu_to_le32(ATH10K_SKB_CB(msdu)->paddr); + cmd->mgmt_tx.len = __cpu_to_le32(msdu->len); + cmd->mgmt_tx.desc_id = __cpu_to_le32(msdu_id); + cmd->mgmt_tx.vdev_id = __cpu_to_le32(vdev_id); + memcpy(cmd->mgmt_tx.hdr, msdu->data, + min_t(int, msdu->len, HTT_MGMT_FRM_HDR_DOWNLOAD_LEN)); + + /* refcount is decremented by HTC and HTT completions until it reaches + * zero and is freed */ + skb_cb = ATH10K_SKB_CB(txdesc); + skb_cb->htt.msdu_id = msdu_id; + skb_cb->htt.refcount = 2; + skb_cb->htt.msdu = msdu; + + res = ath10k_htc_send(htt->ar->htc, htt->eid, txdesc); + if (res) + goto err; + + return 0; + +err: + ath10k_skb_unmap(dev, msdu); + + if (txdesc) + dev_kfree_skb_any(txdesc); + if (msdu_id >= 0) { + spin_lock_bh(&htt->tx_lock); + htt->pending_tx[msdu_id] = NULL; + ath10k_htt_tx_free_msdu_id(htt, msdu_id); + spin_unlock_bh(&htt->tx_lock); + } + ath10k_htt_tx_dec_pending(htt); + return res; +} + +int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) +{ + struct device *dev = htt->ar->dev; + struct htt_cmd *cmd; + struct htt_data_tx_desc_frag *tx_frags; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data; + struct ath10k_skb_cb *skb_cb; + struct sk_buff *txdesc = NULL; + struct sk_buff *txfrag = NULL; + u8 vdev_id = ATH10K_SKB_CB(msdu)->htt.vdev_id; + u8 tid; + int prefetch_len, desc_len, frag_len; + dma_addr_t frags_paddr; + int msdu_id = -1; + int res; + u8 flags0; + u16 flags1; + + res = ath10k_htt_tx_inc_pending(htt); + if (res) + return res; + + prefetch_len = min(htt->prefetch_len, msdu->len); + prefetch_len = roundup(prefetch_len, 4); + + desc_len = sizeof(cmd->hdr) + sizeof(cmd->data_tx) + prefetch_len; + frag_len = sizeof(*tx_frags) * 2; + + txdesc = ath10k_htc_alloc_skb(desc_len); + if (!txdesc) { + res = -ENOMEM; + goto err; + } + + txfrag = dev_alloc_skb(frag_len); + if (!txfrag) { + res = -ENOMEM; + goto err; + } + + if (!IS_ALIGNED((unsigned long)txdesc->data, 4)) { + ath10k_warn("htt alignment check failed. dropping packet.\n"); + res = -EIO; + goto err; + } + + spin_lock_bh(&htt->tx_lock); + msdu_id = ath10k_htt_tx_alloc_msdu_id(htt); + if (msdu_id < 0) { + spin_unlock_bh(&htt->tx_lock); + res = msdu_id; + goto err; + } + htt->pending_tx[msdu_id] = txdesc; + spin_unlock_bh(&htt->tx_lock); + + res = ath10k_skb_map(dev, msdu); + if (res) + goto err; + + /* tx fragment list must be terminated with zero-entry */ + skb_put(txfrag, frag_len); + tx_frags = (struct htt_data_tx_desc_frag *)txfrag->data; + tx_frags[0].paddr = __cpu_to_le32(ATH10K_SKB_CB(msdu)->paddr); + tx_frags[0].len = __cpu_to_le32(msdu->len); + tx_frags[1].paddr = __cpu_to_le32(0); + tx_frags[1].len = __cpu_to_le32(0); + + res = ath10k_skb_map(dev, txfrag); + if (res) + goto err; + + ath10k_dbg(ATH10K_DBG_HTT, "txfrag 0x%llx msdu 0x%llx\n", + (unsigned long long) ATH10K_SKB_CB(txfrag)->paddr, + (unsigned long long) ATH10K_SKB_CB(msdu)->paddr); + ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "txfrag: ", + txfrag->data, frag_len); + ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "msdu: ", + msdu->data, msdu->len); + + skb_put(txdesc, desc_len); + cmd = (struct htt_cmd *)txdesc->data; + memset(cmd, 0, desc_len); + + tid = ATH10K_SKB_CB(msdu)->htt.tid; + + ath10k_dbg(ATH10K_DBG_HTT, "htt data tx using tid %hhu\n", tid); + + flags0 = 0; + if (!ieee80211_has_protected(hdr->frame_control)) + flags0 |= HTT_DATA_TX_DESC_FLAGS0_NO_ENCRYPT; + flags0 |= HTT_DATA_TX_DESC_FLAGS0_MAC_HDR_PRESENT; + flags0 |= SM(ATH10K_HW_TXRX_NATIVE_WIFI, + HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE); + + flags1 = 0; + flags1 |= SM((u16)vdev_id, HTT_DATA_TX_DESC_FLAGS1_VDEV_ID); + flags1 |= SM((u16)tid, HTT_DATA_TX_DESC_FLAGS1_EXT_TID); + + frags_paddr = ATH10K_SKB_CB(txfrag)->paddr; + + cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_TX_FRM; + cmd->data_tx.flags0 = flags0; + cmd->data_tx.flags1 = __cpu_to_le16(flags1); + cmd->data_tx.len = __cpu_to_le16(msdu->len); + cmd->data_tx.id = __cpu_to_le16(msdu_id); + cmd->data_tx.frags_paddr = __cpu_to_le32(frags_paddr); + cmd->data_tx.peerid = __cpu_to_le32(HTT_INVALID_PEERID); + + memcpy(cmd->data_tx.prefetch, msdu->data, prefetch_len); + + /* refcount is decremented by HTC and HTT completions until it reaches + * zero and is freed */ + skb_cb = ATH10K_SKB_CB(txdesc); + skb_cb->htt.msdu_id = msdu_id; + skb_cb->htt.refcount = 2; + skb_cb->htt.txfrag = txfrag; + skb_cb->htt.msdu = msdu; + + res = ath10k_htc_send(htt->ar->htc, htt->eid, txdesc); + if (res) + goto err; + + return 0; +err: + if (txfrag) + ath10k_skb_unmap(dev, txfrag); + if (txdesc) + dev_kfree_skb_any(txdesc); + if (txfrag) + dev_kfree_skb_any(txfrag); + if (msdu_id >= 0) { + spin_lock_bh(&htt->tx_lock); + htt->pending_tx[msdu_id] = NULL; + ath10k_htt_tx_free_msdu_id(htt, msdu_id); + spin_unlock_bh(&htt->tx_lock); + } + ath10k_htt_tx_dec_pending(htt); + ath10k_skb_unmap(dev, msdu); + return res; +} diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h new file mode 100644 index 000000000000..44ed5af0a204 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HW_H_ +#define _HW_H_ + +#include "targaddrs.h" + +/* Supported FW version */ +#define SUPPORTED_FW_MAJOR 1 +#define SUPPORTED_FW_MINOR 0 +#define SUPPORTED_FW_RELEASE 0 +#define SUPPORTED_FW_BUILD 629 + +/* QCA988X 1.0 definitions */ +#define QCA988X_HW_1_0_VERSION 0x4000002c +#define QCA988X_HW_1_0_FW_DIR "ath10k/QCA988X/hw1.0" +#define QCA988X_HW_1_0_FW_FILE "firmware.bin" +#define QCA988X_HW_1_0_OTP_FILE "otp.bin" +#define QCA988X_HW_1_0_BOARD_DATA_FILE "board.bin" +#define QCA988X_HW_1_0_PATCH_LOAD_ADDR 0x1234 + +/* QCA988X 2.0 definitions */ +#define QCA988X_HW_2_0_VERSION 0x4100016c +#define QCA988X_HW_2_0_FW_DIR "ath10k/QCA988X/hw2.0" +#define QCA988X_HW_2_0_FW_FILE "firmware.bin" +#define QCA988X_HW_2_0_OTP_FILE "otp.bin" +#define QCA988X_HW_2_0_BOARD_DATA_FILE "board.bin" +#define QCA988X_HW_2_0_PATCH_LOAD_ADDR 0x1234 + +/* Known pecularities: + * - current FW doesn't support raw rx mode (last tested v599) + * - current FW dumps upon raw tx mode (last tested v599) + * - raw appears in nwifi decap, raw and nwifi appear in ethernet decap + * - raw have FCS, nwifi doesn't + * - ethernet frames have 802.11 header decapped and parts (base hdr, cipher + * param, llc/snap) are aligned to 4byte boundaries each */ +enum ath10k_hw_txrx_mode { + ATH10K_HW_TXRX_RAW = 0, + ATH10K_HW_TXRX_NATIVE_WIFI = 1, + ATH10K_HW_TXRX_ETHERNET = 2, +}; + +enum ath10k_mcast2ucast_mode { + ATH10K_MCAST2UCAST_DISABLED = 0, + ATH10K_MCAST2UCAST_ENABLED = 1, +}; + +#define TARGET_NUM_VDEVS 8 +#define TARGET_NUM_PEER_AST 2 +#define TARGET_NUM_WDS_ENTRIES 32 +#define TARGET_DMA_BURST_SIZE 0 +#define TARGET_MAC_AGGR_DELIM 0 +#define TARGET_AST_SKID_LIMIT 16 +#define TARGET_NUM_PEERS 16 +#define TARGET_NUM_OFFLOAD_PEERS 0 +#define TARGET_NUM_OFFLOAD_REORDER_BUFS 0 +#define TARGET_NUM_PEER_KEYS 2 +#define TARGET_NUM_TIDS (2 * ((TARGET_NUM_PEERS) + (TARGET_NUM_VDEVS))) +#define TARGET_TX_CHAIN_MASK (BIT(0) | BIT(1) | BIT(2)) +#define TARGET_RX_CHAIN_MASK (BIT(0) | BIT(1) | BIT(2)) +#define TARGET_RX_TIMEOUT_LO_PRI 100 +#define TARGET_RX_TIMEOUT_HI_PRI 40 +#define TARGET_RX_DECAP_MODE ATH10K_HW_TXRX_ETHERNET +#define TARGET_SCAN_MAX_PENDING_REQS 4 +#define TARGET_BMISS_OFFLOAD_MAX_VDEV 3 +#define TARGET_ROAM_OFFLOAD_MAX_VDEV 3 +#define TARGET_ROAM_OFFLOAD_MAX_AP_PROFILES 8 +#define TARGET_GTK_OFFLOAD_MAX_VDEV 3 +#define TARGET_NUM_MCAST_GROUPS 0 +#define TARGET_NUM_MCAST_TABLE_ELEMS 0 +#define TARGET_MCAST2UCAST_MODE ATH10K_MCAST2UCAST_DISABLED +#define TARGET_TX_DBG_LOG_SIZE 1024 +#define TARGET_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK 0 +#define TARGET_VOW_CONFIG 0 +#define TARGET_NUM_MSDU_DESC (1024 + 400) +#define TARGET_MAX_FRAG_ENTRIES 0 + + +/* Number of Copy Engines supported */ +#define CE_COUNT 8 + +/* + * Total number of PCIe MSI interrupts requested for all interrupt sources. + * PCIe standard forces this to be a power of 2. + * Some Host OS's limit MSI requests that can be granted to 8 + * so for now we abide by this limit and avoid requesting more + * than that. + */ +#define MSI_NUM_REQUEST_LOG2 3 +#define MSI_NUM_REQUEST (1<> RTC_STATE_V_LSB) + +#endif /* _HW_H_ */ diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c new file mode 100644 index 000000000000..1285554a1025 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -0,0 +1,3066 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "mac.h" + +#include +#include + +#include "core.h" +#include "debug.h" +#include "wmi.h" +#include "htt.h" +#include "txrx.h" + +/**********/ +/* Crypto */ +/**********/ + +static int ath10k_send_key(struct ath10k_vif *arvif, + struct ieee80211_key_conf *key, + enum set_key_cmd cmd, + const u8 *macaddr) +{ + struct wmi_vdev_install_key_arg arg = { + .vdev_id = arvif->vdev_id, + .key_idx = key->keyidx, + .key_len = key->keylen, + .key_data = key->key, + .macaddr = macaddr, + }; + + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) + arg.key_flags = WMI_KEY_PAIRWISE; + else + arg.key_flags = WMI_KEY_GROUP; + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_CCMP: + arg.key_cipher = WMI_CIPHER_AES_CCM; + key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX; + break; + case WLAN_CIPHER_SUITE_TKIP: + key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; + arg.key_cipher = WMI_CIPHER_TKIP; + arg.key_txmic_len = 8; + arg.key_rxmic_len = 8; + break; + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + arg.key_cipher = WMI_CIPHER_WEP; + /* AP/IBSS mode requires self-key to be groupwise + * Otherwise pairwise key must be set */ + if (memcmp(macaddr, arvif->vif->addr, ETH_ALEN)) + arg.key_flags = WMI_KEY_PAIRWISE; + break; + default: + ath10k_warn("cipher %d is not supported\n", key->cipher); + return -EOPNOTSUPP; + } + + if (cmd == DISABLE_KEY) { + arg.key_cipher = WMI_CIPHER_NONE; + arg.key_data = NULL; + } + + return ath10k_wmi_vdev_install_key(arvif->ar, &arg); +} + +static int ath10k_install_key(struct ath10k_vif *arvif, + struct ieee80211_key_conf *key, + enum set_key_cmd cmd, + const u8 *macaddr) +{ + struct ath10k *ar = arvif->ar; + int ret; + + INIT_COMPLETION(ar->install_key_done); + + ret = ath10k_send_key(arvif, key, cmd, macaddr); + if (ret) + return ret; + + ret = wait_for_completion_timeout(&ar->install_key_done, 3*HZ); + if (ret == 0) + return -ETIMEDOUT; + + return 0; +} + +static int ath10k_install_peer_wep_keys(struct ath10k_vif *arvif, + const u8 *addr) +{ + struct ath10k *ar = arvif->ar; + struct ath10k_peer *peer; + int ret; + int i; + + lockdep_assert_held(&ar->conf_mutex); + + spin_lock_bh(&ar->data_lock); + peer = ath10k_peer_find(ar, arvif->vdev_id, addr); + spin_unlock_bh(&ar->data_lock); + + if (!peer) + return -ENOENT; + + for (i = 0; i < ARRAY_SIZE(arvif->wep_keys); i++) { + if (arvif->wep_keys[i] == NULL) + continue; + + ret = ath10k_install_key(arvif, arvif->wep_keys[i], SET_KEY, + addr); + if (ret) + return ret; + + peer->keys[i] = arvif->wep_keys[i]; + } + + return 0; +} + +static int ath10k_clear_peer_keys(struct ath10k_vif *arvif, + const u8 *addr) +{ + struct ath10k *ar = arvif->ar; + struct ath10k_peer *peer; + int first_errno = 0; + int ret; + int i; + + lockdep_assert_held(&ar->conf_mutex); + + spin_lock_bh(&ar->data_lock); + peer = ath10k_peer_find(ar, arvif->vdev_id, addr); + spin_unlock_bh(&ar->data_lock); + + if (!peer) + return -ENOENT; + + for (i = 0; i < ARRAY_SIZE(peer->keys); i++) { + if (peer->keys[i] == NULL) + continue; + + ret = ath10k_install_key(arvif, peer->keys[i], + DISABLE_KEY, addr); + if (ret && first_errno == 0) + first_errno = ret; + + if (ret) + ath10k_warn("could not remove peer wep key %d (%d)\n", + i, ret); + + peer->keys[i] = NULL; + } + + return first_errno; +} + +static int ath10k_clear_vdev_key(struct ath10k_vif *arvif, + struct ieee80211_key_conf *key) +{ + struct ath10k *ar = arvif->ar; + struct ath10k_peer *peer; + u8 addr[ETH_ALEN]; + int first_errno = 0; + int ret; + int i; + + lockdep_assert_held(&ar->conf_mutex); + + for (;;) { + /* since ath10k_install_key we can't hold data_lock all the + * time, so we try to remove the keys incrementally */ + spin_lock_bh(&ar->data_lock); + i = 0; + list_for_each_entry(peer, &ar->peers, list) { + for (i = 0; i < ARRAY_SIZE(peer->keys); i++) { + if (peer->keys[i] == key) { + memcpy(addr, peer->addr, ETH_ALEN); + peer->keys[i] = NULL; + break; + } + } + + if (i < ARRAY_SIZE(peer->keys)) + break; + } + spin_unlock_bh(&ar->data_lock); + + if (i == ARRAY_SIZE(peer->keys)) + break; + + ret = ath10k_install_key(arvif, key, DISABLE_KEY, addr); + if (ret && first_errno == 0) + first_errno = ret; + + if (ret) + ath10k_warn("could not remove key for %pM\n", addr); + } + + return first_errno; +} + + +/*********************/ +/* General utilities */ +/*********************/ + +static inline enum wmi_phy_mode +chan_to_phymode(const struct cfg80211_chan_def *chandef) +{ + enum wmi_phy_mode phymode = MODE_UNKNOWN; + + switch (chandef->chan->band) { + case IEEE80211_BAND_2GHZ: + switch (chandef->width) { + case NL80211_CHAN_WIDTH_20_NOHT: + phymode = MODE_11G; + break; + case NL80211_CHAN_WIDTH_20: + phymode = MODE_11NG_HT20; + break; + case NL80211_CHAN_WIDTH_40: + phymode = MODE_11NG_HT40; + break; + case NL80211_CHAN_WIDTH_80: + case NL80211_CHAN_WIDTH_80P80: + case NL80211_CHAN_WIDTH_160: + phymode = MODE_UNKNOWN; + break; + } + break; + case IEEE80211_BAND_5GHZ: + switch (chandef->width) { + case NL80211_CHAN_WIDTH_20_NOHT: + phymode = MODE_11A; + break; + case NL80211_CHAN_WIDTH_20: + phymode = MODE_11NA_HT20; + break; + case NL80211_CHAN_WIDTH_40: + phymode = MODE_11NA_HT40; + break; + case NL80211_CHAN_WIDTH_80: + phymode = MODE_11AC_VHT80; + break; + case NL80211_CHAN_WIDTH_80P80: + case NL80211_CHAN_WIDTH_160: + phymode = MODE_UNKNOWN; + break; + } + break; + default: + break; + } + + WARN_ON(phymode == MODE_UNKNOWN); + return phymode; +} + +static u8 ath10k_parse_mpdudensity(u8 mpdudensity) +{ +/* + * 802.11n D2.0 defined values for "Minimum MPDU Start Spacing": + * 0 for no restriction + * 1 for 1/4 us + * 2 for 1/2 us + * 3 for 1 us + * 4 for 2 us + * 5 for 4 us + * 6 for 8 us + * 7 for 16 us + */ + switch (mpdudensity) { + case 0: + return 0; + case 1: + case 2: + case 3: + /* Our lower layer calculations limit our precision to + 1 microsecond */ + return 1; + case 4: + return 2; + case 5: + return 4; + case 6: + return 8; + case 7: + return 16; + default: + return 0; + } +} + +static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr) +{ + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + ret = ath10k_wmi_peer_create(ar, vdev_id, addr); + if (ret) + return ret; + + ret = ath10k_wait_for_peer_created(ar, vdev_id, addr); + if (ret) + return ret; + + return 0; +} + +static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr) +{ + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + ret = ath10k_wmi_peer_delete(ar, vdev_id, addr); + if (ret) + return ret; + + ret = ath10k_wait_for_peer_deleted(ar, vdev_id, addr); + if (ret) + return ret; + + return 0; +} + +static void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id) +{ + struct ath10k_peer *peer, *tmp; + + lockdep_assert_held(&ar->conf_mutex); + + spin_lock_bh(&ar->data_lock); + list_for_each_entry_safe(peer, tmp, &ar->peers, list) { + if (peer->vdev_id != vdev_id) + continue; + + ath10k_warn("removing stale peer %pM from vdev_id %d\n", + peer->addr, vdev_id); + + list_del(&peer->list); + kfree(peer); + } + spin_unlock_bh(&ar->data_lock); +} + +/************************/ +/* Interface management */ +/************************/ + +static inline int ath10k_vdev_setup_sync(struct ath10k *ar) +{ + int ret; + + ret = wait_for_completion_timeout(&ar->vdev_setup_done, + ATH10K_VDEV_SETUP_TIMEOUT_HZ); + if (ret == 0) + return -ETIMEDOUT; + + return 0; +} + +static int ath10k_vdev_start(struct ath10k_vif *arvif) +{ + struct ath10k *ar = arvif->ar; + struct ieee80211_conf *conf = &ar->hw->conf; + struct ieee80211_channel *channel = conf->chandef.chan; + struct wmi_vdev_start_request_arg arg = {}; + int ret = 0; + + lockdep_assert_held(&ar->conf_mutex); + + INIT_COMPLETION(ar->vdev_setup_done); + + arg.vdev_id = arvif->vdev_id; + arg.dtim_period = arvif->dtim_period; + arg.bcn_intval = arvif->beacon_interval; + + arg.channel.freq = channel->center_freq; + + arg.channel.band_center_freq1 = conf->chandef.center_freq1; + + arg.channel.mode = chan_to_phymode(&conf->chandef); + + arg.channel.min_power = channel->max_power * 3; + arg.channel.max_power = channel->max_power * 4; + arg.channel.max_reg_power = channel->max_reg_power * 4; + arg.channel.max_antenna_gain = channel->max_antenna_gain; + + if (arvif->vdev_type == WMI_VDEV_TYPE_AP) { + arg.ssid = arvif->u.ap.ssid; + arg.ssid_len = arvif->u.ap.ssid_len; + arg.hidden_ssid = arvif->u.ap.hidden_ssid; + } else if (arvif->vdev_type == WMI_VDEV_TYPE_IBSS) { + arg.ssid = arvif->vif->bss_conf.ssid; + arg.ssid_len = arvif->vif->bss_conf.ssid_len; + } + + ret = ath10k_wmi_vdev_start(ar, &arg); + if (ret) { + ath10k_warn("WMI vdev start failed: ret %d\n", ret); + return ret; + } + + ret = ath10k_vdev_setup_sync(ar); + if (ret) { + ath10k_warn("vdev setup failed %d\n", ret); + return ret; + } + + return ret; +} + +static int ath10k_vdev_stop(struct ath10k_vif *arvif) +{ + struct ath10k *ar = arvif->ar; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + INIT_COMPLETION(ar->vdev_setup_done); + + ret = ath10k_wmi_vdev_stop(ar, arvif->vdev_id); + if (ret) { + ath10k_warn("WMI vdev stop failed: ret %d\n", ret); + return ret; + } + + ret = ath10k_vdev_setup_sync(ar); + if (ret) { + ath10k_warn("vdev setup failed %d\n", ret); + return ret; + } + + return ret; +} + +static int ath10k_monitor_start(struct ath10k *ar, int vdev_id) +{ + struct ieee80211_channel *channel = ar->hw->conf.chandef.chan; + struct wmi_vdev_start_request_arg arg = {}; + enum nl80211_channel_type type; + int ret = 0; + + lockdep_assert_held(&ar->conf_mutex); + + type = cfg80211_get_chandef_type(&ar->hw->conf.chandef); + + arg.vdev_id = vdev_id; + arg.channel.freq = channel->center_freq; + arg.channel.band_center_freq1 = ar->hw->conf.chandef.center_freq1; + + /* TODO setup this dynamically, what in case we + don't have any vifs? */ + arg.channel.mode = chan_to_phymode(&ar->hw->conf.chandef); + + arg.channel.min_power = channel->max_power * 3; + arg.channel.max_power = channel->max_power * 4; + arg.channel.max_reg_power = channel->max_reg_power * 4; + arg.channel.max_antenna_gain = channel->max_antenna_gain; + + ret = ath10k_wmi_vdev_start(ar, &arg); + if (ret) { + ath10k_warn("Monitor vdev start failed: ret %d\n", ret); + return ret; + } + + ret = ath10k_vdev_setup_sync(ar); + if (ret) { + ath10k_warn("Monitor vdev setup failed %d\n", ret); + return ret; + } + + ret = ath10k_wmi_vdev_up(ar, vdev_id, 0, ar->mac_addr); + if (ret) { + ath10k_warn("Monitor vdev up failed: %d\n", ret); + goto vdev_stop; + } + + ar->monitor_vdev_id = vdev_id; + ar->monitor_enabled = true; + + return 0; + +vdev_stop: + ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id); + if (ret) + ath10k_warn("Monitor vdev stop failed: %d\n", ret); + + return ret; +} + +static int ath10k_monitor_stop(struct ath10k *ar) +{ + int ret = 0; + + lockdep_assert_held(&ar->conf_mutex); + + /* For some reasons, ath10k_wmi_vdev_down() here couse + * often ath10k_wmi_vdev_stop() to fail. Next we could + * not run monitor vdev and driver reload + * required. Don't see such problems we skip + * ath10k_wmi_vdev_down() here. + */ + + ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id); + if (ret) + ath10k_warn("Monitor vdev stop failed: %d\n", ret); + + ret = ath10k_vdev_setup_sync(ar); + if (ret) + ath10k_warn("Monitor_down sync failed: %d\n", ret); + + ar->monitor_enabled = false; + return ret; +} + +static int ath10k_monitor_create(struct ath10k *ar) +{ + int bit, ret = 0; + + lockdep_assert_held(&ar->conf_mutex); + + if (ar->monitor_present) { + ath10k_warn("Monitor mode already enabled\n"); + return 0; + } + + bit = ffs(ar->free_vdev_map); + if (bit == 0) { + ath10k_warn("No free VDEV slots\n"); + return -ENOMEM; + } + + ar->monitor_vdev_id = bit - 1; + ar->free_vdev_map &= ~(1 << ar->monitor_vdev_id); + + ret = ath10k_wmi_vdev_create(ar, ar->monitor_vdev_id, + WMI_VDEV_TYPE_MONITOR, + 0, ar->mac_addr); + if (ret) { + ath10k_warn("WMI vdev monitor create failed: ret %d\n", ret); + goto vdev_fail; + } + + ath10k_dbg(ATH10K_DBG_MAC, "Monitor interface created, vdev id: %d\n", + ar->monitor_vdev_id); + + ar->monitor_present = true; + return 0; + +vdev_fail: + /* + * Restore the ID to the global map. + */ + ar->free_vdev_map |= 1 << (ar->monitor_vdev_id); + return ret; +} + +static int ath10k_monitor_destroy(struct ath10k *ar) +{ + int ret = 0; + + lockdep_assert_held(&ar->conf_mutex); + + if (!ar->monitor_present) + return 0; + + ret = ath10k_wmi_vdev_delete(ar, ar->monitor_vdev_id); + if (ret) { + ath10k_warn("WMI vdev monitor delete failed: %d\n", ret); + return ret; + } + + ar->free_vdev_map |= 1 << (ar->monitor_vdev_id); + ar->monitor_present = false; + + ath10k_dbg(ATH10K_DBG_MAC, "Monitor interface destroyed, vdev id: %d\n", + ar->monitor_vdev_id); + return ret; +} + +static void ath10k_control_beaconing(struct ath10k_vif *arvif, + struct ieee80211_bss_conf *info) +{ + int ret = 0; + + if (!info->enable_beacon) { + ath10k_vdev_stop(arvif); + return; + } + + arvif->tx_seq_no = 0x1000; + + ret = ath10k_vdev_start(arvif); + if (ret) + return; + + ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, 0, info->bssid); + if (ret) { + ath10k_warn("Failed to bring up VDEV: %d\n", + arvif->vdev_id); + return; + } + ath10k_dbg(ATH10K_DBG_MAC, "VDEV: %d up\n", arvif->vdev_id); +} + +static void ath10k_control_ibss(struct ath10k_vif *arvif, + struct ieee80211_bss_conf *info, + const u8 self_peer[ETH_ALEN]) +{ + int ret = 0; + + if (!info->ibss_joined) { + ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, self_peer); + if (ret) + ath10k_warn("Failed to delete IBSS self peer:%pM for VDEV:%d ret:%d\n", + self_peer, arvif->vdev_id, ret); + + if (is_zero_ether_addr(arvif->u.ibss.bssid)) + return; + + ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, + arvif->u.ibss.bssid); + if (ret) { + ath10k_warn("Failed to delete IBSS BSSID peer:%pM for VDEV:%d ret:%d\n", + arvif->u.ibss.bssid, arvif->vdev_id, ret); + return; + } + + memset(arvif->u.ibss.bssid, 0, ETH_ALEN); + + return; + } + + ret = ath10k_peer_create(arvif->ar, arvif->vdev_id, self_peer); + if (ret) { + ath10k_warn("Failed to create IBSS self peer:%pM for VDEV:%d ret:%d\n", + self_peer, arvif->vdev_id, ret); + return; + } + + ret = ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id, + WMI_VDEV_PARAM_ATIM_WINDOW, + ATH10K_DEFAULT_ATIM); + if (ret) + ath10k_warn("Failed to set IBSS ATIM for VDEV:%d ret:%d\n", + arvif->vdev_id, ret); +} + +/* + * Review this when mac80211 gains per-interface powersave support. + */ +static void ath10k_ps_iter(void *data, u8 *mac, struct ieee80211_vif *vif) +{ + struct ath10k_generic_iter *ar_iter = data; + struct ieee80211_conf *conf = &ar_iter->ar->hw->conf; + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + enum wmi_sta_powersave_param param; + enum wmi_sta_ps_mode psmode; + int ret; + + if (vif->type != NL80211_IFTYPE_STATION) + return; + + if (conf->flags & IEEE80211_CONF_PS) { + psmode = WMI_STA_PS_MODE_ENABLED; + param = WMI_STA_PS_PARAM_INACTIVITY_TIME; + + ret = ath10k_wmi_set_sta_ps_param(ar_iter->ar, + arvif->vdev_id, + param, + conf->dynamic_ps_timeout); + if (ret) { + ath10k_warn("Failed to set inactivity time for VDEV: %d\n", + arvif->vdev_id); + return; + } + + ar_iter->ret = ret; + } else { + psmode = WMI_STA_PS_MODE_DISABLED; + } + + ar_iter->ret = ath10k_wmi_set_psmode(ar_iter->ar, arvif->vdev_id, + psmode); + if (ar_iter->ret) + ath10k_warn("Failed to set PS Mode: %d for VDEV: %d\n", + psmode, arvif->vdev_id); + else + ath10k_dbg(ATH10K_DBG_MAC, "Set PS Mode: %d for VDEV: %d\n", + psmode, arvif->vdev_id); +} + +/**********************/ +/* Station management */ +/**********************/ + +static void ath10k_peer_assoc_h_basic(struct ath10k *ar, + struct ath10k_vif *arvif, + struct ieee80211_sta *sta, + struct ieee80211_bss_conf *bss_conf, + struct wmi_peer_assoc_complete_arg *arg) +{ + memcpy(arg->addr, sta->addr, ETH_ALEN); + arg->vdev_id = arvif->vdev_id; + arg->peer_aid = sta->aid; + arg->peer_flags |= WMI_PEER_AUTH; + + if (arvif->vdev_type == WMI_VDEV_TYPE_STA) + /* + * Seems FW have problems with Power Save in STA + * mode when we setup this parameter to high (eg. 5). + * Often we see that FW don't send NULL (with clean P flags) + * frame even there is info about buffered frames in beacons. + * Sometimes we have to wait more than 10 seconds before FW + * will wakeup. Often sending one ping from AP to our device + * just fail (more than 50%). + * + * Seems setting this FW parameter to 1 couse FW + * will check every beacon and will wakup immediately + * after detection buffered data. + */ + arg->peer_listen_intval = 1; + else + arg->peer_listen_intval = ar->hw->conf.listen_interval; + + arg->peer_num_spatial_streams = 1; + + /* + * The assoc capabilities are available only in managed mode. + */ + if (arvif->vdev_type == WMI_VDEV_TYPE_STA && bss_conf) + arg->peer_caps = bss_conf->assoc_capability; +} + +static void ath10k_peer_assoc_h_crypto(struct ath10k *ar, + struct ath10k_vif *arvif, + struct wmi_peer_assoc_complete_arg *arg) +{ + struct ieee80211_vif *vif = arvif->vif; + struct ieee80211_bss_conf *info = &vif->bss_conf; + struct cfg80211_bss *bss; + const u8 *rsnie = NULL; + const u8 *wpaie = NULL; + + bss = cfg80211_get_bss(ar->hw->wiphy, ar->hw->conf.chandef.chan, + info->bssid, NULL, 0, 0, 0); + if (bss) { + const struct cfg80211_bss_ies *ies; + + rcu_read_lock(); + rsnie = ieee80211_bss_get_ie(bss, WLAN_EID_RSN); + + ies = rcu_dereference(bss->ies); + + wpaie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, + WLAN_OUI_TYPE_MICROSOFT_WPA, + ies->data, + ies->len); + rcu_read_unlock(); + cfg80211_put_bss(ar->hw->wiphy, bss); + } + + /* FIXME: base on RSN IE/WPA IE is a correct idea? */ + if (rsnie || wpaie) { + ath10k_dbg(ATH10K_DBG_WMI, "%s: rsn ie found\n", __func__); + arg->peer_flags |= WMI_PEER_NEED_PTK_4_WAY; + } + + if (wpaie) { + ath10k_dbg(ATH10K_DBG_WMI, "%s: wpa ie found\n", __func__); + arg->peer_flags |= WMI_PEER_NEED_GTK_2_WAY; + } +} + +static void ath10k_peer_assoc_h_rates(struct ath10k *ar, + struct ieee80211_sta *sta, + struct wmi_peer_assoc_complete_arg *arg) +{ + struct wmi_rate_set_arg *rateset = &arg->peer_legacy_rates; + const struct ieee80211_supported_band *sband; + const struct ieee80211_rate *rates; + u32 ratemask; + int i; + + sband = ar->hw->wiphy->bands[ar->hw->conf.chandef.chan->band]; + ratemask = sta->supp_rates[ar->hw->conf.chandef.chan->band]; + rates = sband->bitrates; + + rateset->num_rates = 0; + + for (i = 0; i < 32; i++, ratemask >>= 1, rates++) { + if (!(ratemask & 1)) + continue; + + rateset->rates[rateset->num_rates] = rates->hw_value; + rateset->num_rates++; + } +} + +static void ath10k_peer_assoc_h_ht(struct ath10k *ar, + struct ieee80211_sta *sta, + struct wmi_peer_assoc_complete_arg *arg) +{ + const struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; + int smps; + int i, n; + + if (!ht_cap->ht_supported) + return; + + arg->peer_flags |= WMI_PEER_HT; + arg->peer_max_mpdu = (1 << (IEEE80211_HT_MAX_AMPDU_FACTOR + + ht_cap->ampdu_factor)) - 1; + + arg->peer_mpdu_density = + ath10k_parse_mpdudensity(ht_cap->ampdu_density); + + arg->peer_ht_caps = ht_cap->cap; + arg->peer_rate_caps |= WMI_RC_HT_FLAG; + + if (ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING) + arg->peer_flags |= WMI_PEER_LDPC; + + if (sta->bandwidth >= IEEE80211_STA_RX_BW_40) { + arg->peer_flags |= WMI_PEER_40MHZ; + arg->peer_rate_caps |= WMI_RC_CW40_FLAG; + } + + if (ht_cap->cap & IEEE80211_HT_CAP_SGI_20) + arg->peer_rate_caps |= WMI_RC_SGI_FLAG; + + if (ht_cap->cap & IEEE80211_HT_CAP_SGI_40) + arg->peer_rate_caps |= WMI_RC_SGI_FLAG; + + if (ht_cap->cap & IEEE80211_HT_CAP_TX_STBC) { + arg->peer_rate_caps |= WMI_RC_TX_STBC_FLAG; + arg->peer_flags |= WMI_PEER_STBC; + } + + if (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC) { + u32 stbc; + stbc = ht_cap->cap & IEEE80211_HT_CAP_RX_STBC; + stbc = stbc >> IEEE80211_HT_CAP_RX_STBC_SHIFT; + stbc = stbc << WMI_RC_RX_STBC_FLAG_S; + arg->peer_rate_caps |= stbc; + arg->peer_flags |= WMI_PEER_STBC; + } + + smps = ht_cap->cap & IEEE80211_HT_CAP_SM_PS; + smps >>= IEEE80211_HT_CAP_SM_PS_SHIFT; + + if (smps == WLAN_HT_CAP_SM_PS_STATIC) { + arg->peer_flags |= WMI_PEER_SPATIAL_MUX; + arg->peer_flags |= WMI_PEER_STATIC_MIMOPS; + } else if (smps == WLAN_HT_CAP_SM_PS_DYNAMIC) { + arg->peer_flags |= WMI_PEER_SPATIAL_MUX; + arg->peer_flags |= WMI_PEER_DYN_MIMOPS; + } + + if (ht_cap->mcs.rx_mask[1] && ht_cap->mcs.rx_mask[2]) + arg->peer_rate_caps |= WMI_RC_TS_FLAG; + else if (ht_cap->mcs.rx_mask[1]) + arg->peer_rate_caps |= WMI_RC_DS_FLAG; + + for (i = 0, n = 0; i < IEEE80211_HT_MCS_MASK_LEN*8; i++) + if (ht_cap->mcs.rx_mask[i/8] & (1 << i%8)) + arg->peer_ht_rates.rates[n++] = i; + + arg->peer_ht_rates.num_rates = n; + arg->peer_num_spatial_streams = max((n+7) / 8, 1); + + ath10k_dbg(ATH10K_DBG_MAC, "mcs cnt %d nss %d\n", + arg->peer_ht_rates.num_rates, + arg->peer_num_spatial_streams); +} + +static void ath10k_peer_assoc_h_qos_ap(struct ath10k *ar, + struct ath10k_vif *arvif, + struct ieee80211_sta *sta, + struct ieee80211_bss_conf *bss_conf, + struct wmi_peer_assoc_complete_arg *arg) +{ + u32 uapsd = 0; + u32 max_sp = 0; + + if (sta->wme) + arg->peer_flags |= WMI_PEER_QOS; + + if (sta->wme && sta->uapsd_queues) { + ath10k_dbg(ATH10K_DBG_MAC, "uapsd_queues: 0x%X, max_sp: %d\n", + sta->uapsd_queues, sta->max_sp); + + arg->peer_flags |= WMI_PEER_APSD; + arg->peer_flags |= WMI_RC_UAPSD_FLAG; + + if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) + uapsd |= WMI_AP_PS_UAPSD_AC3_DELIVERY_EN | + WMI_AP_PS_UAPSD_AC3_TRIGGER_EN; + if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI) + uapsd |= WMI_AP_PS_UAPSD_AC2_DELIVERY_EN | + WMI_AP_PS_UAPSD_AC2_TRIGGER_EN; + if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK) + uapsd |= WMI_AP_PS_UAPSD_AC1_DELIVERY_EN | + WMI_AP_PS_UAPSD_AC1_TRIGGER_EN; + if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE) + uapsd |= WMI_AP_PS_UAPSD_AC0_DELIVERY_EN | + WMI_AP_PS_UAPSD_AC0_TRIGGER_EN; + + + if (sta->max_sp < MAX_WMI_AP_PS_PEER_PARAM_MAX_SP) + max_sp = sta->max_sp; + + ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id, + sta->addr, + WMI_AP_PS_PEER_PARAM_UAPSD, + uapsd); + + ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id, + sta->addr, + WMI_AP_PS_PEER_PARAM_MAX_SP, + max_sp); + + /* TODO setup this based on STA listen interval and + beacon interval. Currently we don't know + sta->listen_interval - mac80211 patch required. + Currently use 10 seconds */ + ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id, + sta->addr, + WMI_AP_PS_PEER_PARAM_AGEOUT_TIME, + 10); + } +} + +static void ath10k_peer_assoc_h_qos_sta(struct ath10k *ar, + struct ath10k_vif *arvif, + struct ieee80211_sta *sta, + struct ieee80211_bss_conf *bss_conf, + struct wmi_peer_assoc_complete_arg *arg) +{ + if (bss_conf->qos) + arg->peer_flags |= WMI_PEER_QOS; +} + +static void ath10k_peer_assoc_h_vht(struct ath10k *ar, + struct ieee80211_sta *sta, + struct wmi_peer_assoc_complete_arg *arg) +{ + const struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; + + if (!vht_cap->vht_supported) + return; + + arg->peer_flags |= WMI_PEER_VHT; + + arg->peer_vht_caps = vht_cap->cap; + + if (sta->bandwidth == IEEE80211_STA_RX_BW_80) + arg->peer_flags |= WMI_PEER_80MHZ; + + arg->peer_vht_rates.rx_max_rate = + __le16_to_cpu(vht_cap->vht_mcs.rx_highest); + arg->peer_vht_rates.rx_mcs_set = + __le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map); + arg->peer_vht_rates.tx_max_rate = + __le16_to_cpu(vht_cap->vht_mcs.tx_highest); + arg->peer_vht_rates.tx_mcs_set = + __le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map); + + ath10k_dbg(ATH10K_DBG_MAC, "mac vht peer\n"); +} + +static void ath10k_peer_assoc_h_qos(struct ath10k *ar, + struct ath10k_vif *arvif, + struct ieee80211_sta *sta, + struct ieee80211_bss_conf *bss_conf, + struct wmi_peer_assoc_complete_arg *arg) +{ + switch (arvif->vdev_type) { + case WMI_VDEV_TYPE_AP: + ath10k_peer_assoc_h_qos_ap(ar, arvif, sta, bss_conf, arg); + break; + case WMI_VDEV_TYPE_STA: + ath10k_peer_assoc_h_qos_sta(ar, arvif, sta, bss_conf, arg); + break; + default: + break; + } +} + +static void ath10k_peer_assoc_h_phymode(struct ath10k *ar, + struct ath10k_vif *arvif, + struct ieee80211_sta *sta, + struct wmi_peer_assoc_complete_arg *arg) +{ + enum wmi_phy_mode phymode = MODE_UNKNOWN; + + /* FIXME: add VHT */ + + switch (ar->hw->conf.chandef.chan->band) { + case IEEE80211_BAND_2GHZ: + if (sta->ht_cap.ht_supported) { + if (sta->bandwidth == IEEE80211_STA_RX_BW_40) + phymode = MODE_11NG_HT40; + else + phymode = MODE_11NG_HT20; + } else { + phymode = MODE_11G; + } + + break; + case IEEE80211_BAND_5GHZ: + if (sta->ht_cap.ht_supported) { + if (sta->bandwidth == IEEE80211_STA_RX_BW_40) + phymode = MODE_11NA_HT40; + else + phymode = MODE_11NA_HT20; + } else { + phymode = MODE_11A; + } + + break; + default: + break; + } + + arg->peer_phymode = phymode; + WARN_ON(phymode == MODE_UNKNOWN); +} + +static int ath10k_peer_assoc(struct ath10k *ar, + struct ath10k_vif *arvif, + struct ieee80211_sta *sta, + struct ieee80211_bss_conf *bss_conf) +{ + struct wmi_peer_assoc_complete_arg arg; + + memset(&arg, 0, sizeof(struct wmi_peer_assoc_complete_arg)); + + ath10k_peer_assoc_h_basic(ar, arvif, sta, bss_conf, &arg); + ath10k_peer_assoc_h_crypto(ar, arvif, &arg); + ath10k_peer_assoc_h_rates(ar, sta, &arg); + ath10k_peer_assoc_h_ht(ar, sta, &arg); + ath10k_peer_assoc_h_vht(ar, sta, &arg); + ath10k_peer_assoc_h_qos(ar, arvif, sta, bss_conf, &arg); + ath10k_peer_assoc_h_phymode(ar, arvif, sta, &arg); + + return ath10k_wmi_peer_assoc(ar, &arg); +} + +/* can be called only in mac80211 callbacks due to `key_count` usage */ +static void ath10k_bss_assoc(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf) +{ + struct ath10k *ar = hw->priv; + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + struct ieee80211_sta *ap_sta; + int ret; + + rcu_read_lock(); + + ap_sta = ieee80211_find_sta(vif, bss_conf->bssid); + if (!ap_sta) { + ath10k_warn("Failed to find station entry for %pM\n", + bss_conf->bssid); + rcu_read_unlock(); + return; + } + + ret = ath10k_peer_assoc(ar, arvif, ap_sta, bss_conf); + if (ret) { + ath10k_warn("Peer assoc failed for %pM\n", bss_conf->bssid); + rcu_read_unlock(); + return; + } + + rcu_read_unlock(); + + ret = ath10k_wmi_vdev_up(ar, arvif->vdev_id, bss_conf->aid, + bss_conf->bssid); + if (ret) + ath10k_warn("VDEV: %d up failed: ret %d\n", + arvif->vdev_id, ret); + else + ath10k_dbg(ATH10K_DBG_MAC, + "VDEV: %d associated, BSSID: %pM, AID: %d\n", + arvif->vdev_id, bss_conf->bssid, bss_conf->aid); +} + +/* + * FIXME: flush TIDs + */ +static void ath10k_bss_disassoc(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ath10k *ar = hw->priv; + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + int ret; + + /* + * For some reason, calling VDEV-DOWN before VDEV-STOP + * makes the FW to send frames via HTT after disassociation. + * No idea why this happens, even though VDEV-DOWN is supposed + * to be analogous to link down, so just stop the VDEV. + */ + ret = ath10k_vdev_stop(arvif); + if (!ret) + ath10k_dbg(ATH10K_DBG_MAC, "VDEV: %d stopped\n", + arvif->vdev_id); + + /* + * If we don't call VDEV-DOWN after VDEV-STOP FW will remain active and + * report beacons from previously associated network through HTT. + * This in turn would spam mac80211 WARN_ON if we bring down all + * interfaces as it expects there is no rx when no interface is + * running. + */ + ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id); + if (ret) + ath10k_dbg(ATH10K_DBG_MAC, "VDEV: %d ath10k_wmi_vdev_down failed (%d)\n", + arvif->vdev_id, ret); + + ath10k_wmi_flush_tx(ar); + + arvif->def_wep_key_index = 0; +} + +static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif, + struct ieee80211_sta *sta) +{ + int ret = 0; + + ret = ath10k_peer_assoc(ar, arvif, sta, NULL); + if (ret) { + ath10k_warn("WMI peer assoc failed for %pM\n", sta->addr); + return ret; + } + + ret = ath10k_install_peer_wep_keys(arvif, sta->addr); + if (ret) { + ath10k_warn("could not install peer wep keys (%d)\n", ret); + return ret; + } + + return ret; +} + +static int ath10k_station_disassoc(struct ath10k *ar, struct ath10k_vif *arvif, + struct ieee80211_sta *sta) +{ + int ret = 0; + + ret = ath10k_clear_peer_keys(arvif, sta->addr); + if (ret) { + ath10k_warn("could not clear all peer wep keys (%d)\n", ret); + return ret; + } + + return ret; +} + +/**************/ +/* Regulatory */ +/**************/ + +static int ath10k_update_channel_list(struct ath10k *ar) +{ + struct ieee80211_hw *hw = ar->hw; + struct ieee80211_supported_band **bands; + enum ieee80211_band band; + struct ieee80211_channel *channel; + struct wmi_scan_chan_list_arg arg = {0}; + struct wmi_channel_arg *ch; + bool passive; + int len; + int ret; + int i; + + bands = hw->wiphy->bands; + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + if (!bands[band]) + continue; + + for (i = 0; i < bands[band]->n_channels; i++) { + if (bands[band]->channels[i].flags & + IEEE80211_CHAN_DISABLED) + continue; + + arg.n_channels++; + } + } + + len = sizeof(struct wmi_channel_arg) * arg.n_channels; + arg.channels = kzalloc(len, GFP_KERNEL); + if (!arg.channels) + return -ENOMEM; + + ch = arg.channels; + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + if (!bands[band]) + continue; + + for (i = 0; i < bands[band]->n_channels; i++) { + channel = &bands[band]->channels[i]; + + if (channel->flags & IEEE80211_CHAN_DISABLED) + continue; + + ch->allow_ht = true; + + /* FIXME: when should we really allow VHT? */ + ch->allow_vht = true; + + ch->allow_ibss = + !(channel->flags & IEEE80211_CHAN_NO_IBSS); + + ch->ht40plus = + !(channel->flags & IEEE80211_CHAN_NO_HT40PLUS); + + passive = channel->flags & IEEE80211_CHAN_PASSIVE_SCAN; + ch->passive = passive; + + ch->freq = channel->center_freq; + ch->min_power = channel->max_power * 3; + ch->max_power = channel->max_power * 4; + ch->max_reg_power = channel->max_reg_power * 4; + ch->max_antenna_gain = channel->max_antenna_gain; + ch->reg_class_id = 0; /* FIXME */ + + /* FIXME: why use only legacy modes, why not any + * HT/VHT modes? Would that even make any + * difference? */ + if (channel->band == IEEE80211_BAND_2GHZ) + ch->mode = MODE_11G; + else + ch->mode = MODE_11A; + + if (WARN_ON_ONCE(ch->mode == MODE_UNKNOWN)) + continue; + + ath10k_dbg(ATH10K_DBG_WMI, + "%s: [%zd/%d] freq %d maxpower %d regpower %d antenna %d mode %d\n", + __func__, ch - arg.channels, arg.n_channels, + ch->freq, ch->max_power, ch->max_reg_power, + ch->max_antenna_gain, ch->mode); + + ch++; + } + } + + ret = ath10k_wmi_scan_chan_list(ar, &arg); + kfree(arg.channels); + + return ret; +} + +static void ath10k_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct reg_dmn_pair_mapping *regpair; + struct ath10k *ar = hw->priv; + int ret; + + ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory); + + ret = ath10k_update_channel_list(ar); + if (ret) + ath10k_warn("could not update channel list (%d)\n", ret); + + regpair = ar->ath_common.regulatory.regpair; + /* Target allows setting up per-band regdomain but ath_common provides + * a combined one only */ + ret = ath10k_wmi_pdev_set_regdomain(ar, + regpair->regDmnEnum, + regpair->regDmnEnum, /* 2ghz */ + regpair->regDmnEnum, /* 5ghz */ + regpair->reg_2ghz_ctl, + regpair->reg_5ghz_ctl); + if (ret) + ath10k_warn("could not set pdev regdomain (%d)\n", ret); +} + +/***************/ +/* TX handlers */ +/***************/ + +/* + * Frames sent to the FW have to be in "Native Wifi" format. + * Strip the QoS field from the 802.11 header. + */ +static void ath10k_tx_h_qos_workaround(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (void *)skb->data; + u8 *qos_ctl; + + if (!ieee80211_is_data_qos(hdr->frame_control)) + return; + + qos_ctl = ieee80211_get_qos_ctl(hdr); + memmove(qos_ctl, qos_ctl + IEEE80211_QOS_CTL_LEN, + skb->len - ieee80211_hdrlen(hdr->frame_control)); + skb_trim(skb, skb->len - IEEE80211_QOS_CTL_LEN); +} + +static void ath10k_tx_h_update_wep_key(struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_vif *vif = info->control.vif; + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + struct ath10k *ar = arvif->ar; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_key_conf *key = info->control.hw_key; + int ret; + + /* TODO AP mode should be implemented */ + if (vif->type != NL80211_IFTYPE_STATION) + return; + + if (!ieee80211_has_protected(hdr->frame_control)) + return; + + if (!key) + return; + + if (key->cipher != WLAN_CIPHER_SUITE_WEP40 && + key->cipher != WLAN_CIPHER_SUITE_WEP104) + return; + + if (key->keyidx == arvif->def_wep_key_index) + return; + + ath10k_dbg(ATH10K_DBG_MAC, "new wep keyidx will be %d\n", key->keyidx); + + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, + WMI_VDEV_PARAM_DEF_KEYID, + key->keyidx); + if (ret) { + ath10k_warn("could not update wep keyidx (%d)\n", ret); + return; + } + + arvif->def_wep_key_index = key->keyidx; +} + +static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar, struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_vif *vif = info->control.vif; + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + + /* This is case only for P2P_GO */ + if (arvif->vdev_type != WMI_VDEV_TYPE_AP || + arvif->vdev_subtype != WMI_VDEV_SUBTYPE_P2P_GO) + return; + + if (unlikely(ieee80211_is_probe_resp(hdr->frame_control))) { + spin_lock_bh(&ar->data_lock); + if (arvif->u.ap.noa_data) + if (!pskb_expand_head(skb, 0, arvif->u.ap.noa_len, + GFP_ATOMIC)) + memcpy(skb_put(skb, arvif->u.ap.noa_len), + arvif->u.ap.noa_data, + arvif->u.ap.noa_len); + spin_unlock_bh(&ar->data_lock); + } +} + +static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + int ret; + + if (ieee80211_is_mgmt(hdr->frame_control)) + ret = ath10k_htt_mgmt_tx(ar->htt, skb); + else if (ieee80211_is_nullfunc(hdr->frame_control)) + /* FW does not report tx status properly for NullFunc frames + * unless they are sent through mgmt tx path. mac80211 sends + * those frames when it detects link/beacon loss and depends on + * the tx status to be correct. */ + ret = ath10k_htt_mgmt_tx(ar->htt, skb); + else + ret = ath10k_htt_tx(ar->htt, skb); + + if (ret) { + ath10k_warn("tx failed (%d). dropping packet.\n", ret); + ieee80211_free_txskb(ar->hw, skb); + } +} + +void ath10k_offchan_tx_purge(struct ath10k *ar) +{ + struct sk_buff *skb; + + for (;;) { + skb = skb_dequeue(&ar->offchan_tx_queue); + if (!skb) + break; + + ieee80211_free_txskb(ar->hw, skb); + } +} + +void ath10k_offchan_tx_work(struct work_struct *work) +{ + struct ath10k *ar = container_of(work, struct ath10k, offchan_tx_work); + struct ath10k_peer *peer; + struct ieee80211_hdr *hdr; + struct sk_buff *skb; + const u8 *peer_addr; + int vdev_id; + int ret; + + /* FW requirement: We must create a peer before FW will send out + * an offchannel frame. Otherwise the frame will be stuck and + * never transmitted. We delete the peer upon tx completion. + * It is unlikely that a peer for offchannel tx will already be + * present. However it may be in some rare cases so account for that. + * Otherwise we might remove a legitimate peer and break stuff. */ + + for (;;) { + skb = skb_dequeue(&ar->offchan_tx_queue); + if (!skb) + break; + + mutex_lock(&ar->conf_mutex); + + ath10k_dbg(ATH10K_DBG_MAC, "processing offchannel skb %p\n", + skb); + + hdr = (struct ieee80211_hdr *)skb->data; + peer_addr = ieee80211_get_DA(hdr); + vdev_id = ATH10K_SKB_CB(skb)->htt.vdev_id; + + spin_lock_bh(&ar->data_lock); + peer = ath10k_peer_find(ar, vdev_id, peer_addr); + spin_unlock_bh(&ar->data_lock); + + if (peer) + ath10k_dbg(ATH10K_DBG_MAC, "peer %pM on vdev %d already present\n", + peer_addr, vdev_id); + + if (!peer) { + ret = ath10k_peer_create(ar, vdev_id, peer_addr); + if (ret) + ath10k_warn("peer %pM on vdev %d not created (%d)\n", + peer_addr, vdev_id, ret); + } + + spin_lock_bh(&ar->data_lock); + INIT_COMPLETION(ar->offchan_tx_completed); + ar->offchan_tx_skb = skb; + spin_unlock_bh(&ar->data_lock); + + ath10k_tx_htt(ar, skb); + + ret = wait_for_completion_timeout(&ar->offchan_tx_completed, + 3 * HZ); + if (ret <= 0) + ath10k_warn("timed out waiting for offchannel skb %p\n", + skb); + + if (!peer) { + ret = ath10k_peer_delete(ar, vdev_id, peer_addr); + if (ret) + ath10k_warn("peer %pM on vdev %d not deleted (%d)\n", + peer_addr, vdev_id, ret); + } + + mutex_unlock(&ar->conf_mutex); + } +} + +/************/ +/* Scanning */ +/************/ + +/* + * This gets called if we dont get a heart-beat during scan. + * This may indicate the FW has hung and we need to abort the + * scan manually to prevent cancel_hw_scan() from deadlocking + */ +void ath10k_reset_scan(unsigned long ptr) +{ + struct ath10k *ar = (struct ath10k *)ptr; + + spin_lock_bh(&ar->data_lock); + if (!ar->scan.in_progress) { + spin_unlock_bh(&ar->data_lock); + return; + } + + ath10k_warn("scan timeout. resetting. fw issue?\n"); + + if (ar->scan.is_roc) + ieee80211_remain_on_channel_expired(ar->hw); + else + ieee80211_scan_completed(ar->hw, 1 /* aborted */); + + ar->scan.in_progress = false; + complete_all(&ar->scan.completed); + spin_unlock_bh(&ar->data_lock); +} + +static int ath10k_abort_scan(struct ath10k *ar) +{ + struct wmi_stop_scan_arg arg = { + .req_id = 1, /* FIXME */ + .req_type = WMI_SCAN_STOP_ONE, + .u.scan_id = ATH10K_SCAN_ID, + }; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + del_timer_sync(&ar->scan.timeout); + + spin_lock_bh(&ar->data_lock); + if (!ar->scan.in_progress) { + spin_unlock_bh(&ar->data_lock); + return 0; + } + + ar->scan.aborting = true; + spin_unlock_bh(&ar->data_lock); + + ret = ath10k_wmi_stop_scan(ar, &arg); + if (ret) { + ath10k_warn("could not submit wmi stop scan (%d)\n", ret); + return -EIO; + } + + ath10k_wmi_flush_tx(ar); + + ret = wait_for_completion_timeout(&ar->scan.completed, 3*HZ); + if (ret == 0) + ath10k_warn("timed out while waiting for scan to stop\n"); + + /* scan completion may be done right after we timeout here, so let's + * check the in_progress and tell mac80211 scan is completed. if we + * don't do that and FW fails to send us scan completion indication + * then userspace won't be able to scan anymore */ + ret = 0; + + spin_lock_bh(&ar->data_lock); + if (ar->scan.in_progress) { + ath10k_warn("could not stop scan. its still in progress\n"); + ar->scan.in_progress = false; + ath10k_offchan_tx_purge(ar); + ret = -ETIMEDOUT; + } + spin_unlock_bh(&ar->data_lock); + + return ret; +} + +static int ath10k_start_scan(struct ath10k *ar, + const struct wmi_start_scan_arg *arg) +{ + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + ret = ath10k_wmi_start_scan(ar, arg); + if (ret) + return ret; + + /* make sure we submit the command so the completion + * timeout makes sense */ + ath10k_wmi_flush_tx(ar); + + ret = wait_for_completion_timeout(&ar->scan.started, 1*HZ); + if (ret == 0) { + ath10k_abort_scan(ar); + return ret; + } + + /* the scan can complete earlier, before we even + * start the timer. in that case the timer handler + * checks ar->scan.in_progress and bails out if its + * false. Add a 200ms margin to account event/command + * processing. */ + mod_timer(&ar->scan.timeout, jiffies + + msecs_to_jiffies(arg->max_scan_time+200)); + return 0; +} + +/**********************/ +/* mac80211 callbacks */ +/**********************/ + +static void ath10k_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ath10k *ar = hw->priv; + struct ath10k_vif *arvif = NULL; + u32 vdev_id = 0; + u8 tid; + + if (info->control.vif) { + arvif = ath10k_vif_to_arvif(info->control.vif); + vdev_id = arvif->vdev_id; + } else if (ar->monitor_enabled) { + vdev_id = ar->monitor_vdev_id; + } + + /* We should disable CCK RATE due to P2P */ + if (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE) + ath10k_dbg(ATH10K_DBG_MAC, "IEEE80211_TX_CTL_NO_CCK_RATE\n"); + + /* we must calculate tid before we apply qos workaround + * as we'd lose the qos control field */ + tid = HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST; + if (ieee80211_is_data_qos(hdr->frame_control) && + is_unicast_ether_addr(ieee80211_get_DA(hdr))) { + u8 *qc = ieee80211_get_qos_ctl(hdr); + tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; + } + + ath10k_tx_h_qos_workaround(hw, control, skb); + ath10k_tx_h_update_wep_key(skb); + ath10k_tx_h_add_p2p_noa_ie(ar, skb); + ath10k_tx_h_seq_no(skb); + + memset(ATH10K_SKB_CB(skb), 0, sizeof(*ATH10K_SKB_CB(skb))); + ATH10K_SKB_CB(skb)->htt.vdev_id = vdev_id; + ATH10K_SKB_CB(skb)->htt.tid = tid; + + if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) { + spin_lock_bh(&ar->data_lock); + ATH10K_SKB_CB(skb)->htt.is_offchan = true; + ATH10K_SKB_CB(skb)->htt.vdev_id = ar->scan.vdev_id; + spin_unlock_bh(&ar->data_lock); + + ath10k_dbg(ATH10K_DBG_MAC, "queued offchannel skb %p\n", skb); + + skb_queue_tail(&ar->offchan_tx_queue, skb); + ieee80211_queue_work(hw, &ar->offchan_tx_work); + return; + } + + ath10k_tx_htt(ar, skb); +} + +/* + * Initialize various parameters with default vaules. + */ +static int ath10k_start(struct ieee80211_hw *hw) +{ + struct ath10k *ar = hw->priv; + int ret; + + ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_PMF_QOS, 1); + if (ret) + ath10k_warn("could not enable WMI_PDEV_PARAM_PMF_QOS (%d)\n", + ret); + + ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_DYNAMIC_BW, 0); + if (ret) + ath10k_warn("could not init WMI_PDEV_PARAM_DYNAMIC_BW (%d)\n", + ret); + + return 0; +} + +static void ath10k_stop(struct ieee80211_hw *hw) +{ + struct ath10k *ar = hw->priv; + + /* avoid leaks in case FW never confirms scan for offchannel */ + cancel_work_sync(&ar->offchan_tx_work); + ath10k_offchan_tx_purge(ar); +} + +static int ath10k_config(struct ieee80211_hw *hw, u32 changed) +{ + struct ath10k_generic_iter ar_iter; + struct ath10k *ar = hw->priv; + struct ieee80211_conf *conf = &hw->conf; + int ret = 0; + u32 flags; + + mutex_lock(&ar->conf_mutex); + + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + ath10k_dbg(ATH10K_DBG_MAC, "Config channel %d mhz\n", + conf->chandef.chan->center_freq); + spin_lock_bh(&ar->data_lock); + ar->rx_channel = conf->chandef.chan; + spin_unlock_bh(&ar->data_lock); + } + + if (changed & IEEE80211_CONF_CHANGE_PS) { + memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter)); + ar_iter.ar = ar; + flags = IEEE80211_IFACE_ITER_RESUME_ALL; + + ieee80211_iterate_active_interfaces_atomic(hw, + flags, + ath10k_ps_iter, + &ar_iter); + + ret = ar_iter.ret; + } + + if (changed & IEEE80211_CONF_CHANGE_MONITOR) { + if (conf->flags & IEEE80211_CONF_MONITOR) + ret = ath10k_monitor_create(ar); + else + ret = ath10k_monitor_destroy(ar); + } + + mutex_unlock(&ar->conf_mutex); + return ret; +} + +/* + * TODO: + * Figure out how to handle WMI_VDEV_SUBTYPE_P2P_DEVICE, + * because we will send mgmt frames without CCK. This requirement + * for P2P_FIND/GO_NEG should be handled by checking CCK flag + * in the TX packet. + */ +static int ath10k_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ath10k *ar = hw->priv; + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + enum wmi_sta_powersave_param param; + int ret = 0; + u32 value; + int bit; + + mutex_lock(&ar->conf_mutex); + + arvif->ar = ar; + arvif->vif = vif; + + if ((vif->type == NL80211_IFTYPE_MONITOR) && ar->monitor_present) { + ath10k_warn("Only one monitor interface allowed\n"); + ret = -EBUSY; + goto exit; + } + + bit = ffs(ar->free_vdev_map); + if (bit == 0) { + ret = -EBUSY; + goto exit; + } + + arvif->vdev_id = bit - 1; + arvif->vdev_subtype = WMI_VDEV_SUBTYPE_NONE; + ar->free_vdev_map &= ~(1 << arvif->vdev_id); + + if (ar->p2p) + arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_DEVICE; + + switch (vif->type) { + case NL80211_IFTYPE_UNSPECIFIED: + case NL80211_IFTYPE_STATION: + arvif->vdev_type = WMI_VDEV_TYPE_STA; + if (vif->p2p) + arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_CLIENT; + break; + case NL80211_IFTYPE_ADHOC: + arvif->vdev_type = WMI_VDEV_TYPE_IBSS; + break; + case NL80211_IFTYPE_AP: + arvif->vdev_type = WMI_VDEV_TYPE_AP; + + if (vif->p2p) + arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_GO; + break; + case NL80211_IFTYPE_MONITOR: + arvif->vdev_type = WMI_VDEV_TYPE_MONITOR; + break; + default: + WARN_ON(1); + break; + } + + ath10k_dbg(ATH10K_DBG_MAC, "Add interface: id %d type %d subtype %d\n", + arvif->vdev_id, arvif->vdev_type, arvif->vdev_subtype); + + ret = ath10k_wmi_vdev_create(ar, arvif->vdev_id, arvif->vdev_type, + arvif->vdev_subtype, vif->addr); + if (ret) { + ath10k_warn("WMI vdev create failed: ret %d\n", ret); + goto exit; + } + + ret = ath10k_wmi_vdev_set_param(ar, 0, WMI_VDEV_PARAM_DEF_KEYID, + arvif->def_wep_key_index); + if (ret) + ath10k_warn("Failed to set default keyid: %d\n", ret); + + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, + WMI_VDEV_PARAM_TX_ENCAP_TYPE, + ATH10K_HW_TXRX_NATIVE_WIFI); + if (ret) + ath10k_warn("Failed to set TX encap: %d\n", ret); + + if (arvif->vdev_type == WMI_VDEV_TYPE_AP) { + ret = ath10k_peer_create(ar, arvif->vdev_id, vif->addr); + if (ret) { + ath10k_warn("Failed to create peer for AP: %d\n", ret); + goto exit; + } + } + + if (arvif->vdev_type == WMI_VDEV_TYPE_STA) { + param = WMI_STA_PS_PARAM_RX_WAKE_POLICY; + value = WMI_STA_PS_RX_WAKE_POLICY_WAKE; + ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, + param, value); + if (ret) + ath10k_warn("Failed to set RX wake policy: %d\n", ret); + + param = WMI_STA_PS_PARAM_TX_WAKE_THRESHOLD; + value = WMI_STA_PS_TX_WAKE_THRESHOLD_ALWAYS; + ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, + param, value); + if (ret) + ath10k_warn("Failed to set TX wake thresh: %d\n", ret); + + param = WMI_STA_PS_PARAM_PSPOLL_COUNT; + value = WMI_STA_PS_PSPOLL_COUNT_NO_MAX; + ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, + param, value); + if (ret) + ath10k_warn("Failed to set PSPOLL count: %d\n", ret); + } + + if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) + ar->monitor_present = true; + +exit: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static void ath10k_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ath10k *ar = hw->priv; + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + int ret; + + mutex_lock(&ar->conf_mutex); + + ath10k_dbg(ATH10K_DBG_MAC, "Remove interface: id %d\n", arvif->vdev_id); + + ar->free_vdev_map |= 1 << (arvif->vdev_id); + + if (arvif->vdev_type == WMI_VDEV_TYPE_AP) { + ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, vif->addr); + if (ret) + ath10k_warn("Failed to remove peer for AP: %d\n", ret); + + kfree(arvif->u.ap.noa_data); + } + + ret = ath10k_wmi_vdev_delete(ar, arvif->vdev_id); + if (ret) + ath10k_warn("WMI vdev delete failed: %d\n", ret); + + if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) + ar->monitor_present = false; + + ath10k_peer_cleanup(ar, arvif->vdev_id); + + mutex_unlock(&ar->conf_mutex); +} + +/* + * FIXME: Has to be verified. + */ +#define SUPPORTED_FILTERS \ + (FIF_PROMISC_IN_BSS | \ + FIF_ALLMULTI | \ + FIF_CONTROL | \ + FIF_PSPOLL | \ + FIF_OTHER_BSS | \ + FIF_BCN_PRBRESP_PROMISC | \ + FIF_PROBE_REQ | \ + FIF_FCSFAIL) + +static void ath10k_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) +{ + struct ath10k *ar = hw->priv; + int ret; + + mutex_lock(&ar->conf_mutex); + + changed_flags &= SUPPORTED_FILTERS; + *total_flags &= SUPPORTED_FILTERS; + ar->filter_flags = *total_flags; + + if ((ar->filter_flags & FIF_PROMISC_IN_BSS) && + !ar->monitor_enabled) { + ret = ath10k_monitor_start(ar, ar->monitor_vdev_id); + if (ret) + ath10k_warn("Unable to start monitor mode\n"); + else + ath10k_dbg(ATH10K_DBG_MAC, "Monitor mode started\n"); + } else if (!(ar->filter_flags & FIF_PROMISC_IN_BSS) && + ar->monitor_enabled) { + ret = ath10k_monitor_stop(ar); + if (ret) + ath10k_warn("Unable to stop monitor mode\n"); + else + ath10k_dbg(ATH10K_DBG_MAC, "Monitor mode stopped\n"); + } + + mutex_unlock(&ar->conf_mutex); +} + +static void ath10k_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u32 changed) +{ + struct ath10k *ar = hw->priv; + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + int ret = 0; + + mutex_lock(&ar->conf_mutex); + + if (changed & BSS_CHANGED_IBSS) + ath10k_control_ibss(arvif, info, vif->addr); + + if (changed & BSS_CHANGED_BEACON_INT) { + arvif->beacon_interval = info->beacon_int; + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, + WMI_VDEV_PARAM_BEACON_INTERVAL, + arvif->beacon_interval); + if (ret) + ath10k_warn("Failed to set beacon interval for VDEV: %d\n", + arvif->vdev_id); + else + ath10k_dbg(ATH10K_DBG_MAC, + "Beacon interval: %d set for VDEV: %d\n", + arvif->beacon_interval, arvif->vdev_id); + } + + if (changed & BSS_CHANGED_BEACON) { + ret = ath10k_wmi_pdev_set_param(ar, + WMI_PDEV_PARAM_BEACON_TX_MODE, + WMI_BEACON_STAGGERED_MODE); + if (ret) + ath10k_warn("Failed to set beacon mode for VDEV: %d\n", + arvif->vdev_id); + else + ath10k_dbg(ATH10K_DBG_MAC, + "Set staggered beacon mode for VDEV: %d\n", + arvif->vdev_id); + } + + if (changed & BSS_CHANGED_DTIM_PERIOD) { + arvif->dtim_period = info->dtim_period; + + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, + WMI_VDEV_PARAM_DTIM_PERIOD, + arvif->dtim_period); + if (ret) + ath10k_warn("Failed to set dtim period for VDEV: %d\n", + arvif->vdev_id); + else + ath10k_dbg(ATH10K_DBG_MAC, + "Set dtim period: %d for VDEV: %d\n", + arvif->dtim_period, arvif->vdev_id); + } + + if (changed & BSS_CHANGED_SSID && + vif->type == NL80211_IFTYPE_AP) { + arvif->u.ap.ssid_len = info->ssid_len; + if (info->ssid_len) + memcpy(arvif->u.ap.ssid, info->ssid, info->ssid_len); + arvif->u.ap.hidden_ssid = info->hidden_ssid; + } + + if (changed & BSS_CHANGED_BSSID) { + if (!is_zero_ether_addr(info->bssid)) { + ret = ath10k_peer_create(ar, arvif->vdev_id, + info->bssid); + if (ret) + ath10k_warn("Failed to add peer: %pM for VDEV: %d\n", + info->bssid, arvif->vdev_id); + else + ath10k_dbg(ATH10K_DBG_MAC, + "Added peer: %pM for VDEV: %d\n", + info->bssid, arvif->vdev_id); + + + if (vif->type == NL80211_IFTYPE_STATION) { + /* + * this is never erased as we it for crypto key + * clearing; this is FW requirement + */ + memcpy(arvif->u.sta.bssid, info->bssid, + ETH_ALEN); + + ret = ath10k_vdev_start(arvif); + if (!ret) + ath10k_dbg(ATH10K_DBG_MAC, + "VDEV: %d started with BSSID: %pM\n", + arvif->vdev_id, info->bssid); + } + + /* + * Mac80211 does not keep IBSS bssid when leaving IBSS, + * so driver need to store it. It is needed when leaving + * IBSS in order to remove BSSID peer. + */ + if (vif->type == NL80211_IFTYPE_ADHOC) + memcpy(arvif->u.ibss.bssid, info->bssid, + ETH_ALEN); + } + } + + if (changed & BSS_CHANGED_BEACON_ENABLED) + ath10k_control_beaconing(arvif, info); + + if (changed & BSS_CHANGED_ERP_CTS_PROT) { + u32 cts_prot; + if (info->use_cts_prot) + cts_prot = 1; + else + cts_prot = 0; + + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, + WMI_VDEV_PARAM_ENABLE_RTSCTS, + cts_prot); + if (ret) + ath10k_warn("Failed to set CTS prot for VDEV: %d\n", + arvif->vdev_id); + else + ath10k_dbg(ATH10K_DBG_MAC, + "Set CTS prot: %d for VDEV: %d\n", + cts_prot, arvif->vdev_id); + } + + if (changed & BSS_CHANGED_ERP_SLOT) { + u32 slottime; + if (info->use_short_slot) + slottime = WMI_VDEV_SLOT_TIME_SHORT; /* 9us */ + + else + slottime = WMI_VDEV_SLOT_TIME_LONG; /* 20us */ + + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, + WMI_VDEV_PARAM_SLOT_TIME, + slottime); + if (ret) + ath10k_warn("Failed to set erp slot for VDEV: %d\n", + arvif->vdev_id); + else + ath10k_dbg(ATH10K_DBG_MAC, + "Set slottime: %d for VDEV: %d\n", + slottime, arvif->vdev_id); + } + + if (changed & BSS_CHANGED_ERP_PREAMBLE) { + u32 preamble; + if (info->use_short_preamble) + preamble = WMI_VDEV_PREAMBLE_SHORT; + else + preamble = WMI_VDEV_PREAMBLE_LONG; + + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, + WMI_VDEV_PARAM_PREAMBLE, + preamble); + if (ret) + ath10k_warn("Failed to set preamble for VDEV: %d\n", + arvif->vdev_id); + else + ath10k_dbg(ATH10K_DBG_MAC, + "Set preamble: %d for VDEV: %d\n", + preamble, arvif->vdev_id); + } + + if (changed & BSS_CHANGED_ASSOC) { + if (info->assoc) + ath10k_bss_assoc(hw, vif, info); + } + + mutex_unlock(&ar->conf_mutex); +} + +static int ath10k_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_scan_request *req) +{ + struct ath10k *ar = hw->priv; + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + struct wmi_start_scan_arg arg; + int ret = 0; + int i; + + mutex_lock(&ar->conf_mutex); + + spin_lock_bh(&ar->data_lock); + if (ar->scan.in_progress) { + spin_unlock_bh(&ar->data_lock); + ret = -EBUSY; + goto exit; + } + + INIT_COMPLETION(ar->scan.started); + INIT_COMPLETION(ar->scan.completed); + ar->scan.in_progress = true; + ar->scan.aborting = false; + ar->scan.is_roc = false; + ar->scan.vdev_id = arvif->vdev_id; + spin_unlock_bh(&ar->data_lock); + + memset(&arg, 0, sizeof(arg)); + ath10k_wmi_start_scan_init(ar, &arg); + arg.vdev_id = arvif->vdev_id; + arg.scan_id = ATH10K_SCAN_ID; + + if (!req->no_cck) + arg.scan_ctrl_flags |= WMI_SCAN_ADD_CCK_RATES; + + if (req->ie_len) { + arg.ie_len = req->ie_len; + memcpy(arg.ie, req->ie, arg.ie_len); + } + + if (req->n_ssids) { + arg.n_ssids = req->n_ssids; + for (i = 0; i < arg.n_ssids; i++) { + arg.ssids[i].len = req->ssids[i].ssid_len; + arg.ssids[i].ssid = req->ssids[i].ssid; + } + } + + if (req->n_channels) { + arg.n_channels = req->n_channels; + for (i = 0; i < arg.n_channels; i++) + arg.channels[i] = req->channels[i]->center_freq; + } + + ret = ath10k_start_scan(ar, &arg); + if (ret) { + ath10k_warn("could not start hw scan (%d)\n", ret); + spin_lock_bh(&ar->data_lock); + ar->scan.in_progress = false; + spin_unlock_bh(&ar->data_lock); + } + +exit: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static void ath10k_cancel_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ath10k *ar = hw->priv; + int ret; + + mutex_lock(&ar->conf_mutex); + ret = ath10k_abort_scan(ar); + if (ret) { + ath10k_warn("couldn't abort scan (%d). forcefully sending scan completion to mac80211\n", + ret); + ieee80211_scan_completed(hw, 1 /* aborted */); + } + mutex_unlock(&ar->conf_mutex); +} + +static int ath10k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct ath10k *ar = hw->priv; + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + struct ath10k_peer *peer; + const u8 *peer_addr; + bool is_wep = key->cipher == WLAN_CIPHER_SUITE_WEP40 || + key->cipher == WLAN_CIPHER_SUITE_WEP104; + int ret = 0; + + if (key->keyidx > WMI_MAX_KEY_INDEX) + return -ENOSPC; + + mutex_lock(&ar->conf_mutex); + + if (sta) + peer_addr = sta->addr; + else if (arvif->vdev_type == WMI_VDEV_TYPE_STA) + peer_addr = vif->bss_conf.bssid; + else + peer_addr = vif->addr; + + key->hw_key_idx = key->keyidx; + + /* the peer should not disappear in mid-way (unless FW goes awry) since + * we already hold conf_mutex. we just make sure its there now. */ + spin_lock_bh(&ar->data_lock); + peer = ath10k_peer_find(ar, arvif->vdev_id, peer_addr); + spin_unlock_bh(&ar->data_lock); + + if (!peer) { + if (cmd == SET_KEY) { + ath10k_warn("cannot install key for non-existent peer %pM\n", + peer_addr); + ret = -EOPNOTSUPP; + goto exit; + } else { + /* if the peer doesn't exist there is no key to disable + * anymore */ + goto exit; + } + } + + if (is_wep) { + if (cmd == SET_KEY) + arvif->wep_keys[key->keyidx] = key; + else + arvif->wep_keys[key->keyidx] = NULL; + + if (cmd == DISABLE_KEY) + ath10k_clear_vdev_key(arvif, key); + } + + ret = ath10k_install_key(arvif, key, cmd, peer_addr); + if (ret) { + ath10k_warn("ath10k_install_key failed (%d)\n", ret); + goto exit; + } + + spin_lock_bh(&ar->data_lock); + peer = ath10k_peer_find(ar, arvif->vdev_id, peer_addr); + if (peer && cmd == SET_KEY) + peer->keys[key->keyidx] = key; + else if (peer && cmd == DISABLE_KEY) + peer->keys[key->keyidx] = NULL; + else if (peer == NULL) + /* impossible unless FW goes crazy */ + ath10k_warn("peer %pM disappeared!\n", peer_addr); + spin_unlock_bh(&ar->data_lock); + +exit: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static int ath10k_sta_state(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + enum ieee80211_sta_state old_state, + enum ieee80211_sta_state new_state) +{ + struct ath10k *ar = hw->priv; + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + int ret = 0; + + mutex_lock(&ar->conf_mutex); + + if (old_state == IEEE80211_STA_NOTEXIST && + new_state == IEEE80211_STA_NONE && + vif->type != NL80211_IFTYPE_STATION) { + /* + * New station addition. + */ + ret = ath10k_peer_create(ar, arvif->vdev_id, sta->addr); + if (ret) + ath10k_warn("Failed to add peer: %pM for VDEV: %d\n", + sta->addr, arvif->vdev_id); + else + ath10k_dbg(ATH10K_DBG_MAC, + "Added peer: %pM for VDEV: %d\n", + sta->addr, arvif->vdev_id); + } else if ((old_state == IEEE80211_STA_NONE && + new_state == IEEE80211_STA_NOTEXIST)) { + /* + * Existing station deletion. + */ + ret = ath10k_peer_delete(ar, arvif->vdev_id, sta->addr); + if (ret) + ath10k_warn("Failed to delete peer: %pM for VDEV: %d\n", + sta->addr, arvif->vdev_id); + else + ath10k_dbg(ATH10K_DBG_MAC, + "Removed peer: %pM for VDEV: %d\n", + sta->addr, arvif->vdev_id); + + if (vif->type == NL80211_IFTYPE_STATION) + ath10k_bss_disassoc(hw, vif); + } else if (old_state == IEEE80211_STA_AUTH && + new_state == IEEE80211_STA_ASSOC && + (vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_ADHOC)) { + /* + * New association. + */ + ret = ath10k_station_assoc(ar, arvif, sta); + if (ret) + ath10k_warn("Failed to associate station: %pM\n", + sta->addr); + else + ath10k_dbg(ATH10K_DBG_MAC, + "Station %pM moved to assoc state\n", + sta->addr); + } else if (old_state == IEEE80211_STA_ASSOC && + new_state == IEEE80211_STA_AUTH && + (vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_ADHOC)) { + /* + * Disassociation. + */ + ret = ath10k_station_disassoc(ar, arvif, sta); + if (ret) + ath10k_warn("Failed to disassociate station: %pM\n", + sta->addr); + else + ath10k_dbg(ATH10K_DBG_MAC, + "Station %pM moved to disassociated state\n", + sta->addr); + } + + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static int ath10k_conf_tx_uapsd(struct ath10k *ar, struct ieee80211_vif *vif, + u16 ac, bool enable) +{ + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + u32 value = 0; + int ret = 0; + + if (arvif->vdev_type != WMI_VDEV_TYPE_STA) + return 0; + + switch (ac) { + case IEEE80211_AC_VO: + value = WMI_STA_PS_UAPSD_AC3_DELIVERY_EN | + WMI_STA_PS_UAPSD_AC3_TRIGGER_EN; + break; + case IEEE80211_AC_VI: + value = WMI_STA_PS_UAPSD_AC2_DELIVERY_EN | + WMI_STA_PS_UAPSD_AC2_TRIGGER_EN; + break; + case IEEE80211_AC_BE: + value = WMI_STA_PS_UAPSD_AC1_DELIVERY_EN | + WMI_STA_PS_UAPSD_AC1_TRIGGER_EN; + break; + case IEEE80211_AC_BK: + value = WMI_STA_PS_UAPSD_AC0_DELIVERY_EN | + WMI_STA_PS_UAPSD_AC0_TRIGGER_EN; + break; + } + + if (enable) + arvif->u.sta.uapsd |= value; + else + arvif->u.sta.uapsd &= ~value; + + ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, + WMI_STA_PS_PARAM_UAPSD, + arvif->u.sta.uapsd); + if (ret) { + ath10k_warn("could not set uapsd params %d\n", ret); + goto exit; + } + + if (arvif->u.sta.uapsd) + value = WMI_STA_PS_RX_WAKE_POLICY_POLL_UAPSD; + else + value = WMI_STA_PS_RX_WAKE_POLICY_WAKE; + + ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, + WMI_STA_PS_PARAM_RX_WAKE_POLICY, + value); + if (ret) + ath10k_warn("could not set rx wake param %d\n", ret); + +exit: + return ret; +} + +static int ath10k_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 ac, + const struct ieee80211_tx_queue_params *params) +{ + struct ath10k *ar = hw->priv; + struct wmi_wmm_params_arg *p = NULL; + int ret; + + mutex_lock(&ar->conf_mutex); + + switch (ac) { + case IEEE80211_AC_VO: + p = &ar->wmm_params.ac_vo; + break; + case IEEE80211_AC_VI: + p = &ar->wmm_params.ac_vi; + break; + case IEEE80211_AC_BE: + p = &ar->wmm_params.ac_be; + break; + case IEEE80211_AC_BK: + p = &ar->wmm_params.ac_bk; + break; + } + + if (WARN_ON(!p)) { + ret = -EINVAL; + goto exit; + } + + p->cwmin = params->cw_min; + p->cwmax = params->cw_max; + p->aifs = params->aifs; + + /* + * The channel time duration programmed in the HW is in absolute + * microseconds, while mac80211 gives the txop in units of + * 32 microseconds. + */ + p->txop = params->txop * 32; + + /* FIXME: FW accepts wmm params per hw, not per vif */ + ret = ath10k_wmi_pdev_set_wmm_params(ar, &ar->wmm_params); + if (ret) { + ath10k_warn("could not set wmm params %d\n", ret); + goto exit; + } + + ret = ath10k_conf_tx_uapsd(ar, vif, ac, params->uapsd); + if (ret) + ath10k_warn("could not set sta uapsd %d\n", ret); + +exit: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +#define ATH10K_ROC_TIMEOUT_HZ (2*HZ) + +static int ath10k_remain_on_channel(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_channel *chan, + int duration, + enum ieee80211_roc_type type) +{ + struct ath10k *ar = hw->priv; + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + struct wmi_start_scan_arg arg; + int ret; + + mutex_lock(&ar->conf_mutex); + + spin_lock_bh(&ar->data_lock); + if (ar->scan.in_progress) { + spin_unlock_bh(&ar->data_lock); + ret = -EBUSY; + goto exit; + } + + INIT_COMPLETION(ar->scan.started); + INIT_COMPLETION(ar->scan.completed); + INIT_COMPLETION(ar->scan.on_channel); + ar->scan.in_progress = true; + ar->scan.aborting = false; + ar->scan.is_roc = true; + ar->scan.vdev_id = arvif->vdev_id; + ar->scan.roc_freq = chan->center_freq; + spin_unlock_bh(&ar->data_lock); + + memset(&arg, 0, sizeof(arg)); + ath10k_wmi_start_scan_init(ar, &arg); + arg.vdev_id = arvif->vdev_id; + arg.scan_id = ATH10K_SCAN_ID; + arg.n_channels = 1; + arg.channels[0] = chan->center_freq; + arg.dwell_time_active = duration; + arg.dwell_time_passive = duration; + arg.max_scan_time = 2 * duration; + arg.scan_ctrl_flags |= WMI_SCAN_FLAG_PASSIVE; + arg.scan_ctrl_flags |= WMI_SCAN_FILTER_PROBE_REQ; + + ret = ath10k_start_scan(ar, &arg); + if (ret) { + ath10k_warn("could not start roc scan (%d)\n", ret); + spin_lock_bh(&ar->data_lock); + ar->scan.in_progress = false; + spin_unlock_bh(&ar->data_lock); + goto exit; + } + + ret = wait_for_completion_timeout(&ar->scan.on_channel, 3*HZ); + if (ret == 0) { + ath10k_warn("could not switch to channel for roc scan\n"); + ath10k_abort_scan(ar); + ret = -ETIMEDOUT; + goto exit; + } + + ret = 0; +exit: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static int ath10k_cancel_remain_on_channel(struct ieee80211_hw *hw) +{ + struct ath10k *ar = hw->priv; + + mutex_lock(&ar->conf_mutex); + ath10k_abort_scan(ar); + mutex_unlock(&ar->conf_mutex); + + return 0; +} + +/* + * Both RTS and Fragmentation threshold are interface-specific + * in ath10k, but device-specific in mac80211. + */ +static void ath10k_set_rts_iter(void *data, u8 *mac, struct ieee80211_vif *vif) +{ + struct ath10k_generic_iter *ar_iter = data; + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + u32 rts = ar_iter->ar->hw->wiphy->rts_threshold; + + rts = min_t(u32, rts, ATH10K_RTS_MAX); + + ar_iter->ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id, + WMI_VDEV_PARAM_RTS_THRESHOLD, + rts); + if (ar_iter->ret) + ath10k_warn("Failed to set RTS threshold for VDEV: %d\n", + arvif->vdev_id); + else + ath10k_dbg(ATH10K_DBG_MAC, + "Set RTS threshold: %d for VDEV: %d\n", + rts, arvif->vdev_id); +} + +static int ath10k_set_rts_threshold(struct ieee80211_hw *hw, u32 value) +{ + struct ath10k_generic_iter ar_iter; + struct ath10k *ar = hw->priv; + + memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter)); + ar_iter.ar = ar; + + mutex_lock(&ar->conf_mutex); + ieee80211_iterate_active_interfaces(hw, IEEE80211_IFACE_ITER_RESUME_ALL, + ath10k_set_rts_iter, &ar_iter); + mutex_unlock(&ar->conf_mutex); + + return ar_iter.ret; +} + +static void ath10k_set_frag_iter(void *data, u8 *mac, struct ieee80211_vif *vif) +{ + struct ath10k_generic_iter *ar_iter = data; + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + u32 frag = ar_iter->ar->hw->wiphy->frag_threshold; + int ret; + + frag = clamp_t(u32, frag, + ATH10K_FRAGMT_THRESHOLD_MIN, + ATH10K_FRAGMT_THRESHOLD_MAX); + + ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id, + WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD, + frag); + + ar_iter->ret = ret; + if (ar_iter->ret) + ath10k_warn("Failed to set frag threshold for VDEV: %d\n", + arvif->vdev_id); + else + ath10k_dbg(ATH10K_DBG_MAC, + "Set frag threshold: %d for VDEV: %d\n", + frag, arvif->vdev_id); +} + +static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value) +{ + struct ath10k_generic_iter ar_iter; + struct ath10k *ar = hw->priv; + + memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter)); + ar_iter.ar = ar; + + mutex_lock(&ar->conf_mutex); + ieee80211_iterate_active_interfaces(hw, IEEE80211_IFACE_ITER_RESUME_ALL, + ath10k_set_frag_iter, &ar_iter); + mutex_unlock(&ar->conf_mutex); + + return ar_iter.ret; +} + +static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop) +{ + struct ath10k *ar = hw->priv; + int ret; + + /* mac80211 doesn't care if we really xmit queued frames or not + * we'll collect those frames either way if we stop/delete vdevs */ + if (drop) + return; + + ret = wait_event_timeout(ar->htt->empty_tx_wq, ({ + bool empty; + spin_lock_bh(&ar->htt->tx_lock); + empty = bitmap_empty(ar->htt->used_msdu_ids, + ar->htt->max_num_pending_tx); + spin_unlock_bh(&ar->htt->tx_lock); + (empty); + }), ATH10K_FLUSH_TIMEOUT_HZ); + if (ret <= 0) + ath10k_warn("tx not flushed\n"); +} + +/* TODO: Implement this function properly + * For now it is needed to reply to Probe Requests in IBSS mode. + * Propably we need this information from FW. + */ +static int ath10k_tx_last_beacon(struct ieee80211_hw *hw) +{ + return 1; +} + +static const struct ieee80211_ops ath10k_ops = { + .tx = ath10k_tx, + .start = ath10k_start, + .stop = ath10k_stop, + .config = ath10k_config, + .add_interface = ath10k_add_interface, + .remove_interface = ath10k_remove_interface, + .configure_filter = ath10k_configure_filter, + .bss_info_changed = ath10k_bss_info_changed, + .hw_scan = ath10k_hw_scan, + .cancel_hw_scan = ath10k_cancel_hw_scan, + .set_key = ath10k_set_key, + .sta_state = ath10k_sta_state, + .conf_tx = ath10k_conf_tx, + .remain_on_channel = ath10k_remain_on_channel, + .cancel_remain_on_channel = ath10k_cancel_remain_on_channel, + .set_rts_threshold = ath10k_set_rts_threshold, + .set_frag_threshold = ath10k_set_frag_threshold, + .flush = ath10k_flush, + .tx_last_beacon = ath10k_tx_last_beacon, +}; + +#define RATETAB_ENT(_rate, _rateid, _flags) { \ + .bitrate = (_rate), \ + .flags = (_flags), \ + .hw_value = (_rateid), \ +} + +#define CHAN2G(_channel, _freq, _flags) { \ + .band = IEEE80211_BAND_2GHZ, \ + .hw_value = (_channel), \ + .center_freq = (_freq), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +#define CHAN5G(_channel, _freq, _flags) { \ + .band = IEEE80211_BAND_5GHZ, \ + .hw_value = (_channel), \ + .center_freq = (_freq), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +static const struct ieee80211_channel ath10k_2ghz_channels[] = { + CHAN2G(1, 2412, 0), + CHAN2G(2, 2417, 0), + CHAN2G(3, 2422, 0), + CHAN2G(4, 2427, 0), + CHAN2G(5, 2432, 0), + CHAN2G(6, 2437, 0), + CHAN2G(7, 2442, 0), + CHAN2G(8, 2447, 0), + CHAN2G(9, 2452, 0), + CHAN2G(10, 2457, 0), + CHAN2G(11, 2462, 0), + CHAN2G(12, 2467, 0), + CHAN2G(13, 2472, 0), + CHAN2G(14, 2484, 0), +}; + +static const struct ieee80211_channel ath10k_5ghz_channels[] = { + CHAN5G(36, 5180, 14), + CHAN5G(40, 5200, 15), + CHAN5G(44, 5220, 16), + CHAN5G(48, 5240, 17), + CHAN5G(52, 5260, 18), + CHAN5G(56, 5280, 19), + CHAN5G(60, 5300, 20), + CHAN5G(64, 5320, 21), + CHAN5G(100, 5500, 22), + CHAN5G(104, 5520, 23), + CHAN5G(108, 5540, 24), + CHAN5G(112, 5560, 25), + CHAN5G(116, 5580, 26), + CHAN5G(120, 5600, 27), + CHAN5G(124, 5620, 28), + CHAN5G(128, 5640, 29), + CHAN5G(132, 5660, 30), + CHAN5G(136, 5680, 31), + CHAN5G(140, 5700, 32), + CHAN5G(149, 5745, 33), + CHAN5G(153, 5765, 34), + CHAN5G(157, 5785, 35), + CHAN5G(161, 5805, 36), + CHAN5G(165, 5825, 37), +}; + +static struct ieee80211_rate ath10k_rates[] = { + /* CCK */ + RATETAB_ENT(10, 0x82, 0), + RATETAB_ENT(20, 0x84, 0), + RATETAB_ENT(55, 0x8b, 0), + RATETAB_ENT(110, 0x96, 0), + /* OFDM */ + RATETAB_ENT(60, 0x0c, 0), + RATETAB_ENT(90, 0x12, 0), + RATETAB_ENT(120, 0x18, 0), + RATETAB_ENT(180, 0x24, 0), + RATETAB_ENT(240, 0x30, 0), + RATETAB_ENT(360, 0x48, 0), + RATETAB_ENT(480, 0x60, 0), + RATETAB_ENT(540, 0x6c, 0), +}; + +#define ath10k_a_rates (ath10k_rates + 4) +#define ath10k_a_rates_size (ARRAY_SIZE(ath10k_rates) - 4) +#define ath10k_g_rates (ath10k_rates + 0) +#define ath10k_g_rates_size (ARRAY_SIZE(ath10k_rates)) + +struct ath10k *ath10k_mac_create(void) +{ + struct ieee80211_hw *hw; + struct ath10k *ar; + + hw = ieee80211_alloc_hw(sizeof(struct ath10k), &ath10k_ops); + if (!hw) + return NULL; + + ar = hw->priv; + ar->hw = hw; + + return ar; +} + +void ath10k_mac_destroy(struct ath10k *ar) +{ + ieee80211_free_hw(ar->hw); +} + +static const struct ieee80211_iface_limit ath10k_if_limits[] = { + { + .max = 8, + .types = BIT(NL80211_IFTYPE_STATION) + | BIT(NL80211_IFTYPE_P2P_CLIENT) + | BIT(NL80211_IFTYPE_P2P_GO) + | BIT(NL80211_IFTYPE_AP) + } +}; + +static const struct ieee80211_iface_combination ath10k_if_comb = { + .limits = ath10k_if_limits, + .n_limits = ARRAY_SIZE(ath10k_if_limits), + .max_interfaces = 8, + .num_different_channels = 1, + .beacon_int_infra_match = true, +}; + +static struct ieee80211_sta_vht_cap ath10k_create_vht_cap(struct ath10k *ar) +{ + struct ieee80211_sta_vht_cap vht_cap = {0}; + u16 mcs_map; + + vht_cap.vht_supported = 1; + vht_cap.cap = ar->vht_cap_info; + + /* FIXME: check dynamically how many streams board supports */ + mcs_map = IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | + IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 | + IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 | + IEEE80211_VHT_MCS_NOT_SUPPORTED << 6 | + IEEE80211_VHT_MCS_NOT_SUPPORTED << 8 | + IEEE80211_VHT_MCS_NOT_SUPPORTED << 10 | + IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 | + IEEE80211_VHT_MCS_NOT_SUPPORTED << 14; + + vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(mcs_map); + vht_cap.vht_mcs.tx_mcs_map = cpu_to_le16(mcs_map); + + return vht_cap; +} + +static struct ieee80211_sta_ht_cap ath10k_get_ht_cap(struct ath10k *ar) +{ + int i; + struct ieee80211_sta_ht_cap ht_cap = {0}; + + if (!(ar->ht_cap_info & WMI_HT_CAP_ENABLED)) + return ht_cap; + + ht_cap.ht_supported = 1; + ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; + ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_8; + ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40; + ht_cap.cap |= WLAN_HT_CAP_SM_PS_STATIC << IEEE80211_HT_CAP_SM_PS_SHIFT; + + if (ar->ht_cap_info & WMI_HT_CAP_HT20_SGI) + ht_cap.cap |= IEEE80211_HT_CAP_SGI_20; + + if (ar->ht_cap_info & WMI_HT_CAP_HT40_SGI) + ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; + + if (ar->ht_cap_info & WMI_HT_CAP_DYNAMIC_SMPS) { + u32 smps; + + smps = WLAN_HT_CAP_SM_PS_DYNAMIC; + smps <<= IEEE80211_HT_CAP_SM_PS_SHIFT; + + ht_cap.cap |= smps; + } + + if (ar->ht_cap_info & WMI_HT_CAP_TX_STBC) + ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC; + + if (ar->ht_cap_info & WMI_HT_CAP_RX_STBC) { + u32 stbc; + + stbc = ar->ht_cap_info; + stbc &= WMI_HT_CAP_RX_STBC; + stbc >>= WMI_HT_CAP_RX_STBC_MASK_SHIFT; + stbc <<= IEEE80211_HT_CAP_RX_STBC_SHIFT; + stbc &= IEEE80211_HT_CAP_RX_STBC; + + ht_cap.cap |= stbc; + } + + if (ar->ht_cap_info & WMI_HT_CAP_LDPC) + ht_cap.cap |= IEEE80211_HT_CAP_LDPC_CODING; + + if (ar->ht_cap_info & WMI_HT_CAP_L_SIG_TXOP_PROT) + ht_cap.cap |= IEEE80211_HT_CAP_LSIG_TXOP_PROT; + + /* max AMSDU is implicitly taken from vht_cap_info */ + if (ar->vht_cap_info & WMI_VHT_CAP_MAX_MPDU_LEN_MASK) + ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU; + + for (i = 0; i < WMI_MAX_SPATIAL_STREAM; i++) + ht_cap.mcs.rx_mask[i] = 0xFF; + + ht_cap.mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED; + + return ht_cap; +} + + +static void ath10k_get_arvif_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct ath10k_vif_iter *arvif_iter = data; + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + + if (arvif->vdev_id == arvif_iter->vdev_id) + arvif_iter->arvif = arvif; +} + +struct ath10k_vif *ath10k_get_arvif(struct ath10k *ar, u32 vdev_id) +{ + struct ath10k_vif_iter arvif_iter; + u32 flags; + + memset(&arvif_iter, 0, sizeof(struct ath10k_vif_iter)); + arvif_iter.vdev_id = vdev_id; + + flags = IEEE80211_IFACE_ITER_RESUME_ALL; + ieee80211_iterate_active_interfaces_atomic(ar->hw, + flags, + ath10k_get_arvif_iter, + &arvif_iter); + if (!arvif_iter.arvif) { + ath10k_warn("No VIF found for VDEV: %d\n", vdev_id); + return NULL; + } + + return arvif_iter.arvif; +} + +int ath10k_mac_register(struct ath10k *ar) +{ + struct ieee80211_supported_band *band; + struct ieee80211_sta_vht_cap vht_cap; + struct ieee80211_sta_ht_cap ht_cap; + void *channels; + int ret; + + SET_IEEE80211_PERM_ADDR(ar->hw, ar->mac_addr); + + SET_IEEE80211_DEV(ar->hw, ar->dev); + + ht_cap = ath10k_get_ht_cap(ar); + vht_cap = ath10k_create_vht_cap(ar); + + if (ar->phy_capability & WHAL_WLAN_11G_CAPABILITY) { + channels = kmemdup(ath10k_2ghz_channels, + sizeof(ath10k_2ghz_channels), + GFP_KERNEL); + if (!channels) + return -ENOMEM; + + band = &ar->mac.sbands[IEEE80211_BAND_2GHZ]; + band->n_channels = ARRAY_SIZE(ath10k_2ghz_channels); + band->channels = channels; + band->n_bitrates = ath10k_g_rates_size; + band->bitrates = ath10k_g_rates; + band->ht_cap = ht_cap; + + /* vht is not supported in 2.4 GHz */ + + ar->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = band; + } + + if (ar->phy_capability & WHAL_WLAN_11A_CAPABILITY) { + channels = kmemdup(ath10k_5ghz_channels, + sizeof(ath10k_5ghz_channels), + GFP_KERNEL); + if (!channels) { + if (ar->phy_capability & WHAL_WLAN_11G_CAPABILITY) { + band = &ar->mac.sbands[IEEE80211_BAND_2GHZ]; + kfree(band->channels); + } + return -ENOMEM; + } + + band = &ar->mac.sbands[IEEE80211_BAND_5GHZ]; + band->n_channels = ARRAY_SIZE(ath10k_5ghz_channels); + band->channels = channels; + band->n_bitrates = ath10k_a_rates_size; + band->bitrates = ath10k_a_rates; + band->ht_cap = ht_cap; + band->vht_cap = vht_cap; + ar->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = band; + } + + ar->hw->wiphy->interface_modes = + BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO); + + ar->hw->flags = IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_SUPPORTS_DYNAMIC_PS | + IEEE80211_HW_SUPPORTS_UAPSD | + IEEE80211_HW_MFP_CAPABLE | + IEEE80211_HW_REPORTS_TX_ACK_STATUS | + IEEE80211_HW_HAS_RATE_CONTROL | + IEEE80211_HW_SUPPORTS_STATIC_SMPS | + IEEE80211_HW_WANT_MONITOR_VIF | + IEEE80211_HW_AP_LINK_PS; + + if (ar->ht_cap_info & WMI_HT_CAP_DYNAMIC_SMPS) + ar->hw->flags |= IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS; + + if (ar->ht_cap_info & WMI_HT_CAP_ENABLED) { + ar->hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION; + ar->hw->flags |= IEEE80211_HW_TX_AMPDU_SETUP_IN_HW; + } + + ar->hw->wiphy->max_scan_ssids = WLAN_SCAN_PARAMS_MAX_SSID; + ar->hw->wiphy->max_scan_ie_len = WLAN_SCAN_PARAMS_MAX_IE_LEN; + + ar->hw->vif_data_size = sizeof(struct ath10k_vif); + + ar->hw->channel_change_time = 5000; + ar->hw->max_listen_interval = ATH10K_MAX_HW_LISTEN_INTERVAL; + + ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; + ar->hw->wiphy->max_remain_on_channel_duration = 5000; + + ar->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; + /* + * on LL hardware queues are managed entirely by the FW + * so we only advertise to mac we can do the queues thing + */ + ar->hw->queues = 4; + + ar->hw->wiphy->iface_combinations = &ath10k_if_comb; + ar->hw->wiphy->n_iface_combinations = 1; + + ret = ath_regd_init(&ar->ath_common.regulatory, ar->hw->wiphy, + ath10k_reg_notifier); + if (ret) { + ath10k_err("Regulatory initialization failed\n"); + return ret; + } + + ret = ieee80211_register_hw(ar->hw); + if (ret) { + ath10k_err("ieee80211 registration failed: %d\n", ret); + return ret; + } + + if (!ath_is_world_regd(&ar->ath_common.regulatory)) { + ret = regulatory_hint(ar->hw->wiphy, + ar->ath_common.regulatory.alpha2); + if (ret) + goto exit; + } + + return 0; +exit: + ieee80211_unregister_hw(ar->hw); + return ret; +} + +void ath10k_mac_unregister(struct ath10k *ar) +{ + ieee80211_unregister_hw(ar->hw); + + kfree(ar->mac.sbands[IEEE80211_BAND_2GHZ].channels); + kfree(ar->mac.sbands[IEEE80211_BAND_5GHZ].channels); + + SET_IEEE80211_DEV(ar->hw, NULL); +} diff --git a/drivers/net/wireless/ath/ath10k/mac.h b/drivers/net/wireless/ath/ath10k/mac.h new file mode 100644 index 000000000000..27fc92e58829 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/mac.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _MAC_H_ +#define _MAC_H_ + +#include +#include "core.h" + +struct ath10k_generic_iter { + struct ath10k *ar; + int ret; +}; + +struct ath10k *ath10k_mac_create(void); +void ath10k_mac_destroy(struct ath10k *ar); +int ath10k_mac_register(struct ath10k *ar); +void ath10k_mac_unregister(struct ath10k *ar); +struct ath10k_vif *ath10k_get_arvif(struct ath10k *ar, u32 vdev_id); +void ath10k_reset_scan(unsigned long ptr); +void ath10k_offchan_tx_purge(struct ath10k *ar); +void ath10k_offchan_tx_work(struct work_struct *work); + +static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif) +{ + return (struct ath10k_vif *)vif->drv_priv; +} + +static inline void ath10k_tx_h_seq_no(struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_vif *vif = info->control.vif; + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + + if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { + if (arvif->tx_seq_no == 0) + arvif->tx_seq_no = 0x1000; + + if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) + arvif->tx_seq_no += 0x10; + hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); + hdr->seq_ctrl |= cpu_to_le16(arvif->tx_seq_no); + } +} + +#endif /* _MAC_H_ */ diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c new file mode 100644 index 000000000000..8e4e8327d33e --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -0,0 +1,2506 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include "core.h" +#include "debug.h" + +#include "targaddrs.h" +#include "bmi.h" + +#include "hif.h" +#include "htc.h" + +#include "ce.h" +#include "pci.h" + +unsigned int ath10k_target_ps; +module_param(ath10k_target_ps, uint, 0644); +MODULE_PARM_DESC(ath10k_target_ps, "Enable ath10k Target (SoC) PS option"); + +#define QCA988X_1_0_DEVICE_ID (0xabcd) +#define QCA988X_2_0_DEVICE_ID (0x003c) + +static DEFINE_PCI_DEVICE_TABLE(ath10k_pci_id_table) = { + { PCI_VDEVICE(ATHEROS, QCA988X_1_0_DEVICE_ID) }, /* PCI-E QCA988X V1 */ + { PCI_VDEVICE(ATHEROS, QCA988X_2_0_DEVICE_ID) }, /* PCI-E QCA988X V2 */ + {0} +}; + +static int ath10k_pci_diag_read_access(struct ath10k *ar, u32 address, + u32 *data); + +static void ath10k_pci_process_ce(struct ath10k *ar); +static int ath10k_pci_post_rx(struct ath10k *ar); +static int ath10k_pci_post_rx_pipe(struct hif_ce_pipe_info *pipe_info, + int num); +static void ath10k_pci_rx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info); +static void ath10k_pci_stop_ce(struct ath10k *ar); + +static const struct ce_attr host_ce_config_wlan[] = { + /* host->target HTC control and raw streams */ + { /* CE0 */ CE_ATTR_FLAGS, 0, 16, 256, 0, NULL,}, + /* could be moved to share CE3 */ + /* target->host HTT + HTC control */ + { /* CE1 */ CE_ATTR_FLAGS, 0, 0, 512, 512, NULL,}, + /* target->host WMI */ + { /* CE2 */ CE_ATTR_FLAGS, 0, 0, 2048, 32, NULL,}, + /* host->target WMI */ + { /* CE3 */ CE_ATTR_FLAGS, 0, 32, 2048, 0, NULL,}, + /* host->target HTT */ + { /* CE4 */ CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, 0, + CE_HTT_H2T_MSG_SRC_NENTRIES, 256, 0, NULL,}, + /* unused */ + { /* CE5 */ CE_ATTR_FLAGS, 0, 0, 0, 0, NULL,}, + /* Target autonomous hif_memcpy */ + { /* CE6 */ CE_ATTR_FLAGS, 0, 0, 0, 0, NULL,}, + /* ce_diag, the Diagnostic Window */ + { /* CE7 */ CE_ATTR_FLAGS, 0, 2, DIAG_TRANSFER_LIMIT, 2, NULL,}, +}; + +/* Target firmware's Copy Engine configuration. */ +static const struct ce_pipe_config target_ce_config_wlan[] = { + /* host->target HTC control and raw streams */ + { /* CE0 */ 0, PIPEDIR_OUT, 32, 256, CE_ATTR_FLAGS, 0,}, + /* target->host HTT + HTC control */ + { /* CE1 */ 1, PIPEDIR_IN, 32, 512, CE_ATTR_FLAGS, 0,}, + /* target->host WMI */ + { /* CE2 */ 2, PIPEDIR_IN, 32, 2048, CE_ATTR_FLAGS, 0,}, + /* host->target WMI */ + { /* CE3 */ 3, PIPEDIR_OUT, 32, 2048, CE_ATTR_FLAGS, 0,}, + /* host->target HTT */ + { /* CE4 */ 4, PIPEDIR_OUT, 256, 256, CE_ATTR_FLAGS, 0,}, + /* NB: 50% of src nentries, since tx has 2 frags */ + /* unused */ + { /* CE5 */ 5, PIPEDIR_OUT, 32, 2048, CE_ATTR_FLAGS, 0,}, + /* Reserved for target autonomous hif_memcpy */ + { /* CE6 */ 6, PIPEDIR_INOUT, 32, 4096, CE_ATTR_FLAGS, 0,}, + /* CE7 used only by Host */ +}; + +/* + * Diagnostic read/write access is provided for startup/config/debug usage. + * Caller must guarantee proper alignment, when applicable, and single user + * at any moment. + */ +static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data, + int nbytes) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int ret = 0; + u32 buf; + unsigned int completed_nbytes, orig_nbytes, remaining_bytes; + unsigned int id; + unsigned int flags; + struct ce_state *ce_diag; + /* Host buffer address in CE space */ + u32 ce_data; + dma_addr_t ce_data_base = 0; + void *data_buf = NULL; + int i; + + /* + * This code cannot handle reads to non-memory space. Redirect to the + * register read fn but preserve the multi word read capability of + * this fn + */ + if (address < DRAM_BASE_ADDRESS) { + if (!IS_ALIGNED(address, 4) || + !IS_ALIGNED((unsigned long)data, 4)) + return -EIO; + + while ((nbytes >= 4) && ((ret = ath10k_pci_diag_read_access( + ar, address, (u32 *)data)) == 0)) { + nbytes -= sizeof(u32); + address += sizeof(u32); + data += sizeof(u32); + } + return ret; + } + + ce_diag = ar_pci->ce_diag; + + /* + * Allocate a temporary bounce buffer to hold caller's data + * to be DMA'ed from Target. This guarantees + * 1) 4-byte alignment + * 2) Buffer in DMA-able space + */ + orig_nbytes = nbytes; + data_buf = (unsigned char *)pci_alloc_consistent(ar_pci->pdev, + orig_nbytes, + &ce_data_base); + + if (!data_buf) { + ret = -ENOMEM; + goto done; + } + memset(data_buf, 0, orig_nbytes); + + remaining_bytes = orig_nbytes; + ce_data = ce_data_base; + while (remaining_bytes) { + nbytes = min_t(unsigned int, remaining_bytes, + DIAG_TRANSFER_LIMIT); + + ret = ath10k_ce_recv_buf_enqueue(ce_diag, NULL, ce_data); + if (ret != 0) + goto done; + + /* Request CE to send from Target(!) address to Host buffer */ + /* + * The address supplied by the caller is in the + * Target CPU virtual address space. + * + * In order to use this address with the diagnostic CE, + * convert it from Target CPU virtual address space + * to CE address space + */ + ath10k_pci_wake(ar); + address = TARG_CPU_SPACE_TO_CE_SPACE(ar, ar_pci->mem, + address); + ath10k_pci_sleep(ar); + + ret = ath10k_ce_send(ce_diag, NULL, (u32)address, nbytes, 0, + 0); + if (ret) + goto done; + + i = 0; + while (ath10k_ce_completed_send_next(ce_diag, NULL, &buf, + &completed_nbytes, + &id) != 0) { + mdelay(1); + if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) { + ret = -EBUSY; + goto done; + } + } + + if (nbytes != completed_nbytes) { + ret = -EIO; + goto done; + } + + if (buf != (u32) address) { + ret = -EIO; + goto done; + } + + i = 0; + while (ath10k_ce_completed_recv_next(ce_diag, NULL, &buf, + &completed_nbytes, + &id, &flags) != 0) { + mdelay(1); + + if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) { + ret = -EBUSY; + goto done; + } + } + + if (nbytes != completed_nbytes) { + ret = -EIO; + goto done; + } + + if (buf != ce_data) { + ret = -EIO; + goto done; + } + + remaining_bytes -= nbytes; + address += nbytes; + ce_data += nbytes; + } + +done: + if (ret == 0) { + /* Copy data from allocated DMA buf to caller's buf */ + WARN_ON_ONCE(orig_nbytes & 3); + for (i = 0; i < orig_nbytes / sizeof(__le32); i++) { + ((u32 *)data)[i] = + __le32_to_cpu(((__le32 *)data_buf)[i]); + } + } else + ath10k_dbg(ATH10K_DBG_PCI, "%s failure (0x%x)\n", + __func__, address); + + if (data_buf) + pci_free_consistent(ar_pci->pdev, orig_nbytes, + data_buf, ce_data_base); + + return ret; +} + +/* Read 4-byte aligned data from Target memory or register */ +static int ath10k_pci_diag_read_access(struct ath10k *ar, u32 address, + u32 *data) +{ + /* Assume range doesn't cross this boundary */ + if (address >= DRAM_BASE_ADDRESS) + return ath10k_pci_diag_read_mem(ar, address, data, sizeof(u32)); + + ath10k_pci_wake(ar); + *data = ath10k_pci_read32(ar, address); + ath10k_pci_sleep(ar); + return 0; +} + +static int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address, + const void *data, int nbytes) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int ret = 0; + u32 buf; + unsigned int completed_nbytes, orig_nbytes, remaining_bytes; + unsigned int id; + unsigned int flags; + struct ce_state *ce_diag; + void *data_buf = NULL; + u32 ce_data; /* Host buffer address in CE space */ + dma_addr_t ce_data_base = 0; + int i; + + ce_diag = ar_pci->ce_diag; + + /* + * Allocate a temporary bounce buffer to hold caller's data + * to be DMA'ed to Target. This guarantees + * 1) 4-byte alignment + * 2) Buffer in DMA-able space + */ + orig_nbytes = nbytes; + data_buf = (unsigned char *)pci_alloc_consistent(ar_pci->pdev, + orig_nbytes, + &ce_data_base); + if (!data_buf) { + ret = -ENOMEM; + goto done; + } + + /* Copy caller's data to allocated DMA buf */ + WARN_ON_ONCE(orig_nbytes & 3); + for (i = 0; i < orig_nbytes / sizeof(__le32); i++) + ((__le32 *)data_buf)[i] = __cpu_to_le32(((u32 *)data)[i]); + + /* + * The address supplied by the caller is in the + * Target CPU virtual address space. + * + * In order to use this address with the diagnostic CE, + * convert it from + * Target CPU virtual address space + * to + * CE address space + */ + ath10k_pci_wake(ar); + address = TARG_CPU_SPACE_TO_CE_SPACE(ar, ar_pci->mem, address); + ath10k_pci_sleep(ar); + + remaining_bytes = orig_nbytes; + ce_data = ce_data_base; + while (remaining_bytes) { + /* FIXME: check cast */ + nbytes = min_t(int, remaining_bytes, DIAG_TRANSFER_LIMIT); + + /* Set up to receive directly into Target(!) address */ + ret = ath10k_ce_recv_buf_enqueue(ce_diag, NULL, address); + if (ret != 0) + goto done; + + /* + * Request CE to send caller-supplied data that + * was copied to bounce buffer to Target(!) address. + */ + ret = ath10k_ce_send(ce_diag, NULL, (u32) ce_data, + nbytes, 0, 0); + if (ret != 0) + goto done; + + i = 0; + while (ath10k_ce_completed_send_next(ce_diag, NULL, &buf, + &completed_nbytes, + &id) != 0) { + mdelay(1); + + if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) { + ret = -EBUSY; + goto done; + } + } + + if (nbytes != completed_nbytes) { + ret = -EIO; + goto done; + } + + if (buf != ce_data) { + ret = -EIO; + goto done; + } + + i = 0; + while (ath10k_ce_completed_recv_next(ce_diag, NULL, &buf, + &completed_nbytes, + &id, &flags) != 0) { + mdelay(1); + + if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) { + ret = -EBUSY; + goto done; + } + } + + if (nbytes != completed_nbytes) { + ret = -EIO; + goto done; + } + + if (buf != address) { + ret = -EIO; + goto done; + } + + remaining_bytes -= nbytes; + address += nbytes; + ce_data += nbytes; + } + +done: + if (data_buf) { + pci_free_consistent(ar_pci->pdev, orig_nbytes, data_buf, + ce_data_base); + } + + if (ret != 0) + ath10k_dbg(ATH10K_DBG_PCI, "%s failure (0x%x)\n", __func__, + address); + + return ret; +} + +/* Write 4B data to Target memory or register */ +static int ath10k_pci_diag_write_access(struct ath10k *ar, u32 address, + u32 data) +{ + /* Assume range doesn't cross this boundary */ + if (address >= DRAM_BASE_ADDRESS) + return ath10k_pci_diag_write_mem(ar, address, &data, + sizeof(u32)); + + ath10k_pci_wake(ar); + ath10k_pci_write32(ar, address, data); + ath10k_pci_sleep(ar); + return 0; +} + +static bool ath10k_pci_target_is_awake(struct ath10k *ar) +{ + void __iomem *mem = ath10k_pci_priv(ar)->mem; + u32 val; + val = ioread32(mem + PCIE_LOCAL_BASE_ADDRESS + + RTC_STATE_ADDRESS); + return (RTC_STATE_V_GET(val) == RTC_STATE_V_ON); +} + +static void ath10k_pci_wait(struct ath10k *ar) +{ + int n = 100; + + while (n-- && !ath10k_pci_target_is_awake(ar)) + msleep(10); + + if (n < 0) + ath10k_warn("Unable to wakeup target\n"); +} + +void ath10k_do_pci_wake(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + void __iomem *pci_addr = ar_pci->mem; + int tot_delay = 0; + int curr_delay = 5; + + if (atomic_read(&ar_pci->keep_awake_count) == 0) { + /* Force AWAKE */ + iowrite32(PCIE_SOC_WAKE_V_MASK, + pci_addr + PCIE_LOCAL_BASE_ADDRESS + + PCIE_SOC_WAKE_ADDRESS); + } + atomic_inc(&ar_pci->keep_awake_count); + + if (ar_pci->verified_awake) + return; + + for (;;) { + if (ath10k_pci_target_is_awake(ar)) { + ar_pci->verified_awake = true; + break; + } + + if (tot_delay > PCIE_WAKE_TIMEOUT) { + ath10k_warn("target takes too long to wake up (awake count %d)\n", + atomic_read(&ar_pci->keep_awake_count)); + break; + } + + udelay(curr_delay); + tot_delay += curr_delay; + + if (curr_delay < 50) + curr_delay += 5; + } +} + +void ath10k_do_pci_sleep(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + void __iomem *pci_addr = ar_pci->mem; + + if (atomic_dec_and_test(&ar_pci->keep_awake_count)) { + /* Allow sleep */ + ar_pci->verified_awake = false; + iowrite32(PCIE_SOC_WAKE_RESET, + pci_addr + PCIE_LOCAL_BASE_ADDRESS + + PCIE_SOC_WAKE_ADDRESS); + } +} + +/* + * FIXME: Handle OOM properly. + */ +static inline +struct ath10k_pci_compl *get_free_compl(struct hif_ce_pipe_info *pipe_info) +{ + struct ath10k_pci_compl *compl = NULL; + + spin_lock_bh(&pipe_info->pipe_lock); + if (list_empty(&pipe_info->compl_free)) { + ath10k_warn("Completion buffers are full\n"); + goto exit; + } + compl = list_first_entry(&pipe_info->compl_free, + struct ath10k_pci_compl, list); + list_del(&compl->list); +exit: + spin_unlock_bh(&pipe_info->pipe_lock); + return compl; +} + +/* Called by lower (CE) layer when a send to Target completes. */ +static void ath10k_pci_ce_send_done(struct ce_state *ce_state, + void *transfer_context, + u32 ce_data, + unsigned int nbytes, + unsigned int transfer_id) +{ + struct ath10k *ar = ce_state->ar; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct hif_ce_pipe_info *pipe_info = &ar_pci->pipe_info[ce_state->id]; + struct ath10k_pci_compl *compl; + bool process = false; + + do { + /* + * For the send completion of an item in sendlist, just + * increment num_sends_allowed. The upper layer callback will + * be triggered when last fragment is done with send. + */ + if (transfer_context == CE_SENDLIST_ITEM_CTXT) { + spin_lock_bh(&pipe_info->pipe_lock); + pipe_info->num_sends_allowed++; + spin_unlock_bh(&pipe_info->pipe_lock); + continue; + } + + compl = get_free_compl(pipe_info); + if (!compl) + break; + + compl->send_or_recv = HIF_CE_COMPLETE_SEND; + compl->ce_state = ce_state; + compl->pipe_info = pipe_info; + compl->transfer_context = transfer_context; + compl->nbytes = nbytes; + compl->transfer_id = transfer_id; + compl->flags = 0; + + /* + * Add the completion to the processing queue. + */ + spin_lock_bh(&ar_pci->compl_lock); + list_add_tail(&compl->list, &ar_pci->compl_process); + spin_unlock_bh(&ar_pci->compl_lock); + + process = true; + } while (ath10k_ce_completed_send_next(ce_state, + &transfer_context, + &ce_data, &nbytes, + &transfer_id) == 0); + + /* + * If only some of the items within a sendlist have completed, + * don't invoke completion processing until the entire sendlist + * has been sent. + */ + if (!process) + return; + + ath10k_pci_process_ce(ar); +} + +/* Called by lower (CE) layer when data is received from the Target. */ +static void ath10k_pci_ce_recv_data(struct ce_state *ce_state, + void *transfer_context, u32 ce_data, + unsigned int nbytes, + unsigned int transfer_id, + unsigned int flags) +{ + struct ath10k *ar = ce_state->ar; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct hif_ce_pipe_info *pipe_info = &ar_pci->pipe_info[ce_state->id]; + struct ath10k_pci_compl *compl; + struct sk_buff *skb; + + do { + compl = get_free_compl(pipe_info); + if (!compl) + break; + + compl->send_or_recv = HIF_CE_COMPLETE_RECV; + compl->ce_state = ce_state; + compl->pipe_info = pipe_info; + compl->transfer_context = transfer_context; + compl->nbytes = nbytes; + compl->transfer_id = transfer_id; + compl->flags = flags; + + skb = transfer_context; + dma_unmap_single(ar->dev, ATH10K_SKB_CB(skb)->paddr, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + /* + * Add the completion to the processing queue. + */ + spin_lock_bh(&ar_pci->compl_lock); + list_add_tail(&compl->list, &ar_pci->compl_process); + spin_unlock_bh(&ar_pci->compl_lock); + + } while (ath10k_ce_completed_recv_next(ce_state, + &transfer_context, + &ce_data, &nbytes, + &transfer_id, + &flags) == 0); + + ath10k_pci_process_ce(ar); +} + +/* Send the first nbytes bytes of the buffer */ +static int ath10k_pci_hif_send_head(struct ath10k *ar, u8 pipe_id, + unsigned int transfer_id, + unsigned int bytes, struct sk_buff *nbuf) +{ + struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(nbuf); + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct hif_ce_pipe_info *pipe_info = &(ar_pci->pipe_info[pipe_id]); + struct ce_state *ce_hdl = pipe_info->ce_hdl; + struct ce_sendlist sendlist; + unsigned int len; + u32 flags = 0; + int ret; + + memset(&sendlist, 0, sizeof(struct ce_sendlist)); + + len = min(bytes, nbuf->len); + bytes -= len; + + if (len & 3) + ath10k_warn("skb not aligned to 4-byte boundary (%d)\n", len); + + ath10k_dbg(ATH10K_DBG_PCI, + "pci send data vaddr %p paddr 0x%llx len %d as %d bytes\n", + nbuf->data, (unsigned long long) skb_cb->paddr, + nbuf->len, len); + ath10k_dbg_dump(ATH10K_DBG_PCI_DUMP, NULL, + "ath10k tx: data: ", + nbuf->data, nbuf->len); + + ath10k_ce_sendlist_buf_add(&sendlist, skb_cb->paddr, len, flags); + + /* Make sure we have resources to handle this request */ + spin_lock_bh(&pipe_info->pipe_lock); + if (!pipe_info->num_sends_allowed) { + ath10k_warn("Pipe: %d is full\n", pipe_id); + spin_unlock_bh(&pipe_info->pipe_lock); + return -ENOSR; + } + pipe_info->num_sends_allowed--; + spin_unlock_bh(&pipe_info->pipe_lock); + + ret = ath10k_ce_sendlist_send(ce_hdl, nbuf, &sendlist, transfer_id); + if (ret) + ath10k_warn("CE send failed: %p\n", nbuf); + + return ret; +} + +static u16 ath10k_pci_hif_get_free_queue_number(struct ath10k *ar, u8 pipe) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct hif_ce_pipe_info *pipe_info = &(ar_pci->pipe_info[pipe]); + int ret; + + spin_lock_bh(&pipe_info->pipe_lock); + ret = pipe_info->num_sends_allowed; + spin_unlock_bh(&pipe_info->pipe_lock); + + return ret; +} + +static void ath10k_pci_hif_dump_area(struct ath10k *ar) +{ + u32 reg_dump_area = 0; + u32 reg_dump_values[REG_DUMP_COUNT_QCA988X] = {}; + u32 host_addr; + int ret; + u32 i; + + ath10k_err("firmware crashed!\n"); + ath10k_err("hardware name %s version 0x%x\n", + ar->hw_params.name, ar->target_version); + ath10k_err("firmware version: %u.%u.%u.%u\n", ar->fw_version_major, + ar->fw_version_minor, ar->fw_version_release, + ar->fw_version_build); + + host_addr = host_interest_item_address(HI_ITEM(hi_failure_state)); + if (ath10k_pci_diag_read_mem(ar, host_addr, + ®_dump_area, sizeof(u32)) != 0) { + ath10k_warn("could not read hi_failure_state\n"); + return; + } + + ath10k_err("target register Dump Location: 0x%08X\n", reg_dump_area); + + ret = ath10k_pci_diag_read_mem(ar, reg_dump_area, + ®_dump_values[0], + REG_DUMP_COUNT_QCA988X * sizeof(u32)); + if (ret != 0) { + ath10k_err("could not dump FW Dump Area\n"); + return; + } + + BUILD_BUG_ON(REG_DUMP_COUNT_QCA988X % 4); + + ath10k_err("target Register Dump\n"); + for (i = 0; i < REG_DUMP_COUNT_QCA988X; i += 4) + ath10k_err("[%02d]: 0x%08X 0x%08X 0x%08X 0x%08X\n", + i, + reg_dump_values[i], + reg_dump_values[i + 1], + reg_dump_values[i + 2], + reg_dump_values[i + 3]); +} + +static void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe, + int force) +{ + if (!force) { + int resources; + /* + * Decide whether to actually poll for completions, or just + * wait for a later chance. + * If there seem to be plenty of resources left, then just wait + * since checking involves reading a CE register, which is a + * relatively expensive operation. + */ + resources = ath10k_pci_hif_get_free_queue_number(ar, pipe); + + /* + * If at least 50% of the total resources are still available, + * don't bother checking again yet. + */ + if (resources > (host_ce_config_wlan[pipe].src_nentries >> 1)) + return; + } + ath10k_ce_per_engine_service(ar, pipe); +} + +static void ath10k_pci_hif_post_init(struct ath10k *ar, + struct ath10k_hif_cb *callbacks) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + + ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__); + + memcpy(&ar_pci->msg_callbacks_current, callbacks, + sizeof(ar_pci->msg_callbacks_current)); +} + +static int ath10k_pci_start_ce(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ce_state *ce_diag = ar_pci->ce_diag; + const struct ce_attr *attr; + struct hif_ce_pipe_info *pipe_info; + struct ath10k_pci_compl *compl; + int i, pipe_num, completions, disable_interrupts; + + spin_lock_init(&ar_pci->compl_lock); + INIT_LIST_HEAD(&ar_pci->compl_process); + + for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) { + pipe_info = &ar_pci->pipe_info[pipe_num]; + + spin_lock_init(&pipe_info->pipe_lock); + INIT_LIST_HEAD(&pipe_info->compl_free); + + /* Handle Diagnostic CE specially */ + if (pipe_info->ce_hdl == ce_diag) + continue; + + attr = &host_ce_config_wlan[pipe_num]; + completions = 0; + + if (attr->src_nentries) { + disable_interrupts = attr->flags & CE_ATTR_DIS_INTR; + ath10k_ce_send_cb_register(pipe_info->ce_hdl, + ath10k_pci_ce_send_done, + disable_interrupts); + completions += attr->src_nentries; + pipe_info->num_sends_allowed = attr->src_nentries - 1; + } + + if (attr->dest_nentries) { + ath10k_ce_recv_cb_register(pipe_info->ce_hdl, + ath10k_pci_ce_recv_data); + completions += attr->dest_nentries; + } + + if (completions == 0) + continue; + + for (i = 0; i < completions; i++) { + compl = kmalloc(sizeof(struct ath10k_pci_compl), + GFP_KERNEL); + if (!compl) { + ath10k_warn("No memory for completion state\n"); + ath10k_pci_stop_ce(ar); + return -ENOMEM; + } + + compl->send_or_recv = HIF_CE_COMPLETE_FREE; + list_add_tail(&compl->list, &pipe_info->compl_free); + } + } + + return 0; +} + +static void ath10k_pci_stop_ce(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ath10k_pci_compl *compl; + struct sk_buff *skb; + int i; + + ath10k_ce_disable_interrupts(ar); + + /* Cancel the pending tasklet */ + tasklet_kill(&ar_pci->intr_tq); + + for (i = 0; i < CE_COUNT; i++) + tasklet_kill(&ar_pci->pipe_info[i].intr); + + /* Mark pending completions as aborted, so that upper layers free up + * their associated resources */ + spin_lock_bh(&ar_pci->compl_lock); + list_for_each_entry(compl, &ar_pci->compl_process, list) { + skb = (struct sk_buff *)compl->transfer_context; + ATH10K_SKB_CB(skb)->is_aborted = true; + } + spin_unlock_bh(&ar_pci->compl_lock); +} + +static void ath10k_pci_cleanup_ce(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ath10k_pci_compl *compl, *tmp; + struct hif_ce_pipe_info *pipe_info; + struct sk_buff *netbuf; + int pipe_num; + + /* Free pending completions. */ + spin_lock_bh(&ar_pci->compl_lock); + if (!list_empty(&ar_pci->compl_process)) + ath10k_warn("pending completions still present! possible memory leaks.\n"); + + list_for_each_entry_safe(compl, tmp, &ar_pci->compl_process, list) { + list_del(&compl->list); + netbuf = (struct sk_buff *)compl->transfer_context; + dev_kfree_skb_any(netbuf); + kfree(compl); + } + spin_unlock_bh(&ar_pci->compl_lock); + + /* Free unused completions for each pipe. */ + for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) { + pipe_info = &ar_pci->pipe_info[pipe_num]; + + spin_lock_bh(&pipe_info->pipe_lock); + list_for_each_entry_safe(compl, tmp, + &pipe_info->compl_free, list) { + list_del(&compl->list); + kfree(compl); + } + spin_unlock_bh(&pipe_info->pipe_lock); + } +} + +static void ath10k_pci_process_ce(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ar->hif.priv; + struct ath10k_hif_cb *cb = &ar_pci->msg_callbacks_current; + struct ath10k_pci_compl *compl; + struct sk_buff *skb; + unsigned int nbytes; + int ret, send_done = 0; + + /* Upper layers aren't ready to handle tx/rx completions in parallel so + * we must serialize all completion processing. */ + + spin_lock_bh(&ar_pci->compl_lock); + if (ar_pci->compl_processing) { + spin_unlock_bh(&ar_pci->compl_lock); + return; + } + ar_pci->compl_processing = true; + spin_unlock_bh(&ar_pci->compl_lock); + + for (;;) { + spin_lock_bh(&ar_pci->compl_lock); + if (list_empty(&ar_pci->compl_process)) { + spin_unlock_bh(&ar_pci->compl_lock); + break; + } + compl = list_first_entry(&ar_pci->compl_process, + struct ath10k_pci_compl, list); + list_del(&compl->list); + spin_unlock_bh(&ar_pci->compl_lock); + + if (compl->send_or_recv == HIF_CE_COMPLETE_SEND) { + cb->tx_completion(ar, + compl->transfer_context, + compl->transfer_id); + send_done = 1; + } else { + ret = ath10k_pci_post_rx_pipe(compl->pipe_info, 1); + if (ret) { + ath10k_warn("Unable to post recv buffer for pipe: %d\n", + compl->pipe_info->pipe_num); + break; + } + + skb = (struct sk_buff *)compl->transfer_context; + nbytes = compl->nbytes; + + ath10k_dbg(ATH10K_DBG_PCI, + "ath10k_pci_ce_recv_data netbuf=%p nbytes=%d\n", + skb, nbytes); + ath10k_dbg_dump(ATH10K_DBG_PCI_DUMP, NULL, + "ath10k rx: ", skb->data, nbytes); + + if (skb->len + skb_tailroom(skb) >= nbytes) { + skb_trim(skb, 0); + skb_put(skb, nbytes); + cb->rx_completion(ar, skb, + compl->pipe_info->pipe_num); + } else { + ath10k_warn("rxed more than expected (nbytes %d, max %d)", + nbytes, + skb->len + skb_tailroom(skb)); + } + } + + compl->send_or_recv = HIF_CE_COMPLETE_FREE; + + /* + * Add completion back to the pipe's free list. + */ + spin_lock_bh(&compl->pipe_info->pipe_lock); + list_add_tail(&compl->list, &compl->pipe_info->compl_free); + compl->pipe_info->num_sends_allowed += send_done; + spin_unlock_bh(&compl->pipe_info->pipe_lock); + } + + spin_lock_bh(&ar_pci->compl_lock); + ar_pci->compl_processing = false; + spin_unlock_bh(&ar_pci->compl_lock); +} + +/* TODO - temporary mapping while we have too few CE's */ +static int ath10k_pci_hif_map_service_to_pipe(struct ath10k *ar, + u16 service_id, u8 *ul_pipe, + u8 *dl_pipe, int *ul_is_polled, + int *dl_is_polled) +{ + int ret = 0; + + /* polling for received messages not supported */ + *dl_is_polled = 0; + + switch (service_id) { + case ATH10K_HTC_SVC_ID_HTT_DATA_MSG: + /* + * Host->target HTT gets its own pipe, so it can be polled + * while other pipes are interrupt driven. + */ + *ul_pipe = 4; + /* + * Use the same target->host pipe for HTC ctrl, HTC raw + * streams, and HTT. + */ + *dl_pipe = 1; + break; + + case ATH10K_HTC_SVC_ID_RSVD_CTRL: + case ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS: + /* + * Note: HTC_RAW_STREAMS_SVC is currently unused, and + * HTC_CTRL_RSVD_SVC could share the same pipe as the + * WMI services. So, if another CE is needed, change + * this to *ul_pipe = 3, which frees up CE 0. + */ + /* *ul_pipe = 3; */ + *ul_pipe = 0; + *dl_pipe = 1; + break; + + case ATH10K_HTC_SVC_ID_WMI_DATA_BK: + case ATH10K_HTC_SVC_ID_WMI_DATA_BE: + case ATH10K_HTC_SVC_ID_WMI_DATA_VI: + case ATH10K_HTC_SVC_ID_WMI_DATA_VO: + + case ATH10K_HTC_SVC_ID_WMI_CONTROL: + *ul_pipe = 3; + *dl_pipe = 2; + break; + + /* pipe 5 unused */ + /* pipe 6 reserved */ + /* pipe 7 reserved */ + + default: + ret = -1; + break; + } + *ul_is_polled = + (host_ce_config_wlan[*ul_pipe].flags & CE_ATTR_DIS_INTR) != 0; + + return ret; +} + +static void ath10k_pci_hif_get_default_pipe(struct ath10k *ar, + u8 *ul_pipe, u8 *dl_pipe) +{ + int ul_is_polled, dl_is_polled; + + (void)ath10k_pci_hif_map_service_to_pipe(ar, + ATH10K_HTC_SVC_ID_RSVD_CTRL, + ul_pipe, + dl_pipe, + &ul_is_polled, + &dl_is_polled); +} + +static int ath10k_pci_post_rx_pipe(struct hif_ce_pipe_info *pipe_info, + int num) +{ + struct ath10k *ar = pipe_info->hif_ce_state; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ce_state *ce_state = pipe_info->ce_hdl; + struct sk_buff *skb; + dma_addr_t ce_data; + int i, ret = 0; + + if (pipe_info->buf_sz == 0) + return 0; + + for (i = 0; i < num; i++) { + skb = dev_alloc_skb(pipe_info->buf_sz); + if (!skb) { + ath10k_warn("could not allocate skbuff for pipe %d\n", + num); + ret = -ENOMEM; + goto err; + } + + WARN_ONCE((unsigned long)skb->data & 3, "unaligned skb"); + + ce_data = dma_map_single(ar->dev, skb->data, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + + if (unlikely(dma_mapping_error(ar->dev, ce_data))) { + ath10k_warn("could not dma map skbuff\n"); + dev_kfree_skb_any(skb); + ret = -EIO; + goto err; + } + + ATH10K_SKB_CB(skb)->paddr = ce_data; + + pci_dma_sync_single_for_device(ar_pci->pdev, ce_data, + pipe_info->buf_sz, + PCI_DMA_FROMDEVICE); + + ret = ath10k_ce_recv_buf_enqueue(ce_state, (void *)skb, + ce_data); + if (ret) { + ath10k_warn("could not enqueue to pipe %d (%d)\n", + num, ret); + goto err; + } + } + + return ret; + +err: + ath10k_pci_rx_pipe_cleanup(pipe_info); + return ret; +} + +static int ath10k_pci_post_rx(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct hif_ce_pipe_info *pipe_info; + const struct ce_attr *attr; + int pipe_num, ret = 0; + + for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) { + pipe_info = &ar_pci->pipe_info[pipe_num]; + attr = &host_ce_config_wlan[pipe_num]; + + if (attr->dest_nentries == 0) + continue; + + ret = ath10k_pci_post_rx_pipe(pipe_info, + attr->dest_nentries - 1); + if (ret) { + ath10k_warn("Unable to replenish recv buffers for pipe: %d\n", + pipe_num); + + for (; pipe_num >= 0; pipe_num--) { + pipe_info = &ar_pci->pipe_info[pipe_num]; + ath10k_pci_rx_pipe_cleanup(pipe_info); + } + return ret; + } + } + + return 0; +} + +static int ath10k_pci_hif_start(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int ret; + + ret = ath10k_pci_start_ce(ar); + if (ret) { + ath10k_warn("could not start CE (%d)\n", ret); + return ret; + } + + /* Post buffers once to start things off. */ + ret = ath10k_pci_post_rx(ar); + if (ret) { + ath10k_warn("could not post rx pipes (%d)\n", ret); + return ret; + } + + ar_pci->started = 1; + return 0; +} + +static void ath10k_pci_rx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info) +{ + struct ath10k *ar; + struct ath10k_pci *ar_pci; + struct ce_state *ce_hdl; + u32 buf_sz; + struct sk_buff *netbuf; + u32 ce_data; + + buf_sz = pipe_info->buf_sz; + + /* Unused Copy Engine */ + if (buf_sz == 0) + return; + + ar = pipe_info->hif_ce_state; + ar_pci = ath10k_pci_priv(ar); + + if (!ar_pci->started) + return; + + ce_hdl = pipe_info->ce_hdl; + + while (ath10k_ce_revoke_recv_next(ce_hdl, (void **)&netbuf, + &ce_data) == 0) { + dma_unmap_single(ar->dev, ATH10K_SKB_CB(netbuf)->paddr, + netbuf->len + skb_tailroom(netbuf), + DMA_FROM_DEVICE); + dev_kfree_skb_any(netbuf); + } +} + +static void ath10k_pci_tx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info) +{ + struct ath10k *ar; + struct ath10k_pci *ar_pci; + struct ce_state *ce_hdl; + struct sk_buff *netbuf; + u32 ce_data; + unsigned int nbytes; + unsigned int id; + u32 buf_sz; + + buf_sz = pipe_info->buf_sz; + + /* Unused Copy Engine */ + if (buf_sz == 0) + return; + + ar = pipe_info->hif_ce_state; + ar_pci = ath10k_pci_priv(ar); + + if (!ar_pci->started) + return; + + ce_hdl = pipe_info->ce_hdl; + + while (ath10k_ce_cancel_send_next(ce_hdl, (void **)&netbuf, + &ce_data, &nbytes, &id) == 0) { + if (netbuf != CE_SENDLIST_ITEM_CTXT) + /* + * Indicate the completion to higer layer to free + * the buffer + */ + ATH10K_SKB_CB(netbuf)->is_aborted = true; + ar_pci->msg_callbacks_current.tx_completion(ar, + netbuf, + id); + } +} + +/* + * Cleanup residual buffers for device shutdown: + * buffers that were enqueued for receive + * buffers that were to be sent + * Note: Buffers that had completed but which were + * not yet processed are on a completion queue. They + * are handled when the completion thread shuts down. + */ +static void ath10k_pci_buffer_cleanup(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int pipe_num; + + for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) { + struct hif_ce_pipe_info *pipe_info; + + pipe_info = &ar_pci->pipe_info[pipe_num]; + ath10k_pci_rx_pipe_cleanup(pipe_info); + ath10k_pci_tx_pipe_cleanup(pipe_info); + } +} + +static void ath10k_pci_ce_deinit(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct hif_ce_pipe_info *pipe_info; + int pipe_num; + + for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) { + pipe_info = &ar_pci->pipe_info[pipe_num]; + if (pipe_info->ce_hdl) { + ath10k_ce_deinit(pipe_info->ce_hdl); + pipe_info->ce_hdl = NULL; + pipe_info->buf_sz = 0; + } + } +} + +static void ath10k_pci_hif_stop(struct ath10k *ar) +{ + ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__); + + ath10k_pci_stop_ce(ar); + + /* At this point, asynchronous threads are stopped, the target should + * not DMA nor interrupt. We process the leftovers and then free + * everything else up. */ + + ath10k_pci_process_ce(ar); + ath10k_pci_cleanup_ce(ar); + ath10k_pci_buffer_cleanup(ar); + ath10k_pci_ce_deinit(ar); +} + +static int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar, + void *req, u32 req_len, + void *resp, u32 *resp_len) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ce_state *ce_tx = ar_pci->pipe_info[BMI_CE_NUM_TO_TARG].ce_hdl; + struct ce_state *ce_rx = ar_pci->pipe_info[BMI_CE_NUM_TO_HOST].ce_hdl; + dma_addr_t req_paddr = 0; + dma_addr_t resp_paddr = 0; + struct bmi_xfer xfer = {}; + void *treq, *tresp = NULL; + int ret = 0; + + if (resp && !resp_len) + return -EINVAL; + + if (resp && resp_len && *resp_len == 0) + return -EINVAL; + + treq = kmemdup(req, req_len, GFP_KERNEL); + if (!treq) + return -ENOMEM; + + req_paddr = dma_map_single(ar->dev, treq, req_len, DMA_TO_DEVICE); + ret = dma_mapping_error(ar->dev, req_paddr); + if (ret) + goto err_dma; + + if (resp && resp_len) { + tresp = kzalloc(*resp_len, GFP_KERNEL); + if (!tresp) { + ret = -ENOMEM; + goto err_req; + } + + resp_paddr = dma_map_single(ar->dev, tresp, *resp_len, + DMA_FROM_DEVICE); + ret = dma_mapping_error(ar->dev, resp_paddr); + if (ret) + goto err_req; + + xfer.wait_for_resp = true; + xfer.resp_len = 0; + + ath10k_ce_recv_buf_enqueue(ce_rx, &xfer, resp_paddr); + } + + init_completion(&xfer.done); + + ret = ath10k_ce_send(ce_tx, &xfer, req_paddr, req_len, -1, 0); + if (ret) + goto err_resp; + + ret = wait_for_completion_timeout(&xfer.done, + BMI_COMMUNICATION_TIMEOUT_HZ); + if (ret <= 0) { + u32 unused_buffer; + unsigned int unused_nbytes; + unsigned int unused_id; + + ret = -ETIMEDOUT; + ath10k_ce_cancel_send_next(ce_tx, NULL, &unused_buffer, + &unused_nbytes, &unused_id); + } else { + /* non-zero means we did not time out */ + ret = 0; + } + +err_resp: + if (resp) { + u32 unused_buffer; + + ath10k_ce_revoke_recv_next(ce_rx, NULL, &unused_buffer); + dma_unmap_single(ar->dev, resp_paddr, + *resp_len, DMA_FROM_DEVICE); + } +err_req: + dma_unmap_single(ar->dev, req_paddr, req_len, DMA_TO_DEVICE); + + if (ret == 0 && resp_len) { + *resp_len = min(*resp_len, xfer.resp_len); + memcpy(resp, tresp, xfer.resp_len); + } +err_dma: + kfree(treq); + kfree(tresp); + + return ret; +} + +static void ath10k_pci_bmi_send_done(struct ce_state *ce_state, + void *transfer_context, + u32 data, + unsigned int nbytes, + unsigned int transfer_id) +{ + struct bmi_xfer *xfer = transfer_context; + + if (xfer->wait_for_resp) + return; + + complete(&xfer->done); +} + +static void ath10k_pci_bmi_recv_data(struct ce_state *ce_state, + void *transfer_context, + u32 data, + unsigned int nbytes, + unsigned int transfer_id, + unsigned int flags) +{ + struct bmi_xfer *xfer = transfer_context; + + if (!xfer->wait_for_resp) { + ath10k_warn("unexpected: BMI data received; ignoring\n"); + return; + } + + xfer->resp_len = nbytes; + complete(&xfer->done); +} + +/* + * Map from service/endpoint to Copy Engine. + * This table is derived from the CE_PCI TABLE, above. + * It is passed to the Target at startup for use by firmware. + */ +static const struct service_to_pipe target_service_to_ce_map_wlan[] = { + { + ATH10K_HTC_SVC_ID_WMI_DATA_VO, + PIPEDIR_OUT, /* out = UL = host -> target */ + 3, + }, + { + ATH10K_HTC_SVC_ID_WMI_DATA_VO, + PIPEDIR_IN, /* in = DL = target -> host */ + 2, + }, + { + ATH10K_HTC_SVC_ID_WMI_DATA_BK, + PIPEDIR_OUT, /* out = UL = host -> target */ + 3, + }, + { + ATH10K_HTC_SVC_ID_WMI_DATA_BK, + PIPEDIR_IN, /* in = DL = target -> host */ + 2, + }, + { + ATH10K_HTC_SVC_ID_WMI_DATA_BE, + PIPEDIR_OUT, /* out = UL = host -> target */ + 3, + }, + { + ATH10K_HTC_SVC_ID_WMI_DATA_BE, + PIPEDIR_IN, /* in = DL = target -> host */ + 2, + }, + { + ATH10K_HTC_SVC_ID_WMI_DATA_VI, + PIPEDIR_OUT, /* out = UL = host -> target */ + 3, + }, + { + ATH10K_HTC_SVC_ID_WMI_DATA_VI, + PIPEDIR_IN, /* in = DL = target -> host */ + 2, + }, + { + ATH10K_HTC_SVC_ID_WMI_CONTROL, + PIPEDIR_OUT, /* out = UL = host -> target */ + 3, + }, + { + ATH10K_HTC_SVC_ID_WMI_CONTROL, + PIPEDIR_IN, /* in = DL = target -> host */ + 2, + }, + { + ATH10K_HTC_SVC_ID_RSVD_CTRL, + PIPEDIR_OUT, /* out = UL = host -> target */ + 0, /* could be moved to 3 (share with WMI) */ + }, + { + ATH10K_HTC_SVC_ID_RSVD_CTRL, + PIPEDIR_IN, /* in = DL = target -> host */ + 1, + }, + { + ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS, /* not currently used */ + PIPEDIR_OUT, /* out = UL = host -> target */ + 0, + }, + { + ATH10K_HTC_SVC_ID_TEST_RAW_STREAMS, /* not currently used */ + PIPEDIR_IN, /* in = DL = target -> host */ + 1, + }, + { + ATH10K_HTC_SVC_ID_HTT_DATA_MSG, + PIPEDIR_OUT, /* out = UL = host -> target */ + 4, + }, + { + ATH10K_HTC_SVC_ID_HTT_DATA_MSG, + PIPEDIR_IN, /* in = DL = target -> host */ + 1, + }, + + /* (Additions here) */ + + { /* Must be last */ + 0, + 0, + 0, + }, +}; + +/* + * Send an interrupt to the device to wake up the Target CPU + * so it has an opportunity to notice any changed state. + */ +static int ath10k_pci_wake_target_cpu(struct ath10k *ar) +{ + int ret; + u32 core_ctrl; + + ret = ath10k_pci_diag_read_access(ar, SOC_CORE_BASE_ADDRESS | + CORE_CTRL_ADDRESS, + &core_ctrl); + if (ret) { + ath10k_warn("Unable to read core ctrl\n"); + return ret; + } + + /* A_INUM_FIRMWARE interrupt to Target CPU */ + core_ctrl |= CORE_CTRL_CPU_INTR_MASK; + + ret = ath10k_pci_diag_write_access(ar, SOC_CORE_BASE_ADDRESS | + CORE_CTRL_ADDRESS, + core_ctrl); + if (ret) + ath10k_warn("Unable to set interrupt mask\n"); + + return ret; +} + +static int ath10k_pci_init_config(struct ath10k *ar) +{ + u32 interconnect_targ_addr; + u32 pcie_state_targ_addr = 0; + u32 pipe_cfg_targ_addr = 0; + u32 svc_to_pipe_map = 0; + u32 pcie_config_flags = 0; + u32 ealloc_value; + u32 ealloc_targ_addr; + u32 flag2_value; + u32 flag2_targ_addr; + int ret = 0; + + /* Download to Target the CE Config and the service-to-CE map */ + interconnect_targ_addr = + host_interest_item_address(HI_ITEM(hi_interconnect_state)); + + /* Supply Target-side CE configuration */ + ret = ath10k_pci_diag_read_access(ar, interconnect_targ_addr, + &pcie_state_targ_addr); + if (ret != 0) { + ath10k_err("Failed to get pcie state addr: %d\n", ret); + return ret; + } + + if (pcie_state_targ_addr == 0) { + ret = -EIO; + ath10k_err("Invalid pcie state addr\n"); + return ret; + } + + ret = ath10k_pci_diag_read_access(ar, pcie_state_targ_addr + + offsetof(struct pcie_state, + pipe_cfg_addr), + &pipe_cfg_targ_addr); + if (ret != 0) { + ath10k_err("Failed to get pipe cfg addr: %d\n", ret); + return ret; + } + + if (pipe_cfg_targ_addr == 0) { + ret = -EIO; + ath10k_err("Invalid pipe cfg addr\n"); + return ret; + } + + ret = ath10k_pci_diag_write_mem(ar, pipe_cfg_targ_addr, + target_ce_config_wlan, + sizeof(target_ce_config_wlan)); + + if (ret != 0) { + ath10k_err("Failed to write pipe cfg: %d\n", ret); + return ret; + } + + ret = ath10k_pci_diag_read_access(ar, pcie_state_targ_addr + + offsetof(struct pcie_state, + svc_to_pipe_map), + &svc_to_pipe_map); + if (ret != 0) { + ath10k_err("Failed to get svc/pipe map: %d\n", ret); + return ret; + } + + if (svc_to_pipe_map == 0) { + ret = -EIO; + ath10k_err("Invalid svc_to_pipe map\n"); + return ret; + } + + ret = ath10k_pci_diag_write_mem(ar, svc_to_pipe_map, + target_service_to_ce_map_wlan, + sizeof(target_service_to_ce_map_wlan)); + if (ret != 0) { + ath10k_err("Failed to write svc/pipe map: %d\n", ret); + return ret; + } + + ret = ath10k_pci_diag_read_access(ar, pcie_state_targ_addr + + offsetof(struct pcie_state, + config_flags), + &pcie_config_flags); + if (ret != 0) { + ath10k_err("Failed to get pcie config_flags: %d\n", ret); + return ret; + } + + pcie_config_flags &= ~PCIE_CONFIG_FLAG_ENABLE_L1; + + ret = ath10k_pci_diag_write_mem(ar, pcie_state_targ_addr + + offsetof(struct pcie_state, config_flags), + &pcie_config_flags, + sizeof(pcie_config_flags)); + if (ret != 0) { + ath10k_err("Failed to write pcie config_flags: %d\n", ret); + return ret; + } + + /* configure early allocation */ + ealloc_targ_addr = host_interest_item_address(HI_ITEM(hi_early_alloc)); + + ret = ath10k_pci_diag_read_access(ar, ealloc_targ_addr, &ealloc_value); + if (ret != 0) { + ath10k_err("Faile to get early alloc val: %d\n", ret); + return ret; + } + + /* first bank is switched to IRAM */ + ealloc_value |= ((HI_EARLY_ALLOC_MAGIC << HI_EARLY_ALLOC_MAGIC_SHIFT) & + HI_EARLY_ALLOC_MAGIC_MASK); + ealloc_value |= ((1 << HI_EARLY_ALLOC_IRAM_BANKS_SHIFT) & + HI_EARLY_ALLOC_IRAM_BANKS_MASK); + + ret = ath10k_pci_diag_write_access(ar, ealloc_targ_addr, ealloc_value); + if (ret != 0) { + ath10k_err("Failed to set early alloc val: %d\n", ret); + return ret; + } + + /* Tell Target to proceed with initialization */ + flag2_targ_addr = host_interest_item_address(HI_ITEM(hi_option_flag2)); + + ret = ath10k_pci_diag_read_access(ar, flag2_targ_addr, &flag2_value); + if (ret != 0) { + ath10k_err("Failed to get option val: %d\n", ret); + return ret; + } + + flag2_value |= HI_OPTION_EARLY_CFG_DONE; + + ret = ath10k_pci_diag_write_access(ar, flag2_targ_addr, flag2_value); + if (ret != 0) { + ath10k_err("Failed to set option val: %d\n", ret); + return ret; + } + + return 0; +} + + + +static int ath10k_pci_ce_init(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct hif_ce_pipe_info *pipe_info; + const struct ce_attr *attr; + int pipe_num; + + for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) { + pipe_info = &ar_pci->pipe_info[pipe_num]; + pipe_info->pipe_num = pipe_num; + pipe_info->hif_ce_state = ar; + attr = &host_ce_config_wlan[pipe_num]; + + pipe_info->ce_hdl = ath10k_ce_init(ar, pipe_num, attr); + if (pipe_info->ce_hdl == NULL) { + ath10k_err("Unable to initialize CE for pipe: %d\n", + pipe_num); + + /* It is safe to call it here. It checks if ce_hdl is + * valid for each pipe */ + ath10k_pci_ce_deinit(ar); + return -1; + } + + if (pipe_num == ar_pci->ce_count - 1) { + /* + * Reserve the ultimate CE for + * diagnostic Window support + */ + ar_pci->ce_diag = + ar_pci->pipe_info[ar_pci->ce_count - 1].ce_hdl; + continue; + } + + pipe_info->buf_sz = (size_t) (attr->src_sz_max); + } + + /* + * Initially, establish CE completion handlers for use with BMI. + * These are overwritten with generic handlers after we exit BMI phase. + */ + pipe_info = &ar_pci->pipe_info[BMI_CE_NUM_TO_TARG]; + ath10k_ce_send_cb_register(pipe_info->ce_hdl, + ath10k_pci_bmi_send_done, 0); + + pipe_info = &ar_pci->pipe_info[BMI_CE_NUM_TO_HOST]; + ath10k_ce_recv_cb_register(pipe_info->ce_hdl, + ath10k_pci_bmi_recv_data); + + return 0; +} + +static void ath10k_pci_fw_interrupt_handler(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + u32 fw_indicator_address, fw_indicator; + + ath10k_pci_wake(ar); + + fw_indicator_address = ar_pci->fw_indicator_address; + fw_indicator = ath10k_pci_read32(ar, fw_indicator_address); + + if (fw_indicator & FW_IND_EVENT_PENDING) { + /* ACK: clear Target-side pending event */ + ath10k_pci_write32(ar, fw_indicator_address, + fw_indicator & ~FW_IND_EVENT_PENDING); + + if (ar_pci->started) { + ath10k_pci_hif_dump_area(ar); + } else { + /* + * Probable Target failure before we're prepared + * to handle it. Generally unexpected. + */ + ath10k_warn("early firmware event indicated\n"); + } + } + + ath10k_pci_sleep(ar); +} + +static const struct ath10k_hif_ops ath10k_pci_hif_ops = { + .send_head = ath10k_pci_hif_send_head, + .exchange_bmi_msg = ath10k_pci_hif_exchange_bmi_msg, + .start = ath10k_pci_hif_start, + .stop = ath10k_pci_hif_stop, + .map_service_to_pipe = ath10k_pci_hif_map_service_to_pipe, + .get_default_pipe = ath10k_pci_hif_get_default_pipe, + .send_complete_check = ath10k_pci_hif_send_complete_check, + .init = ath10k_pci_hif_post_init, + .get_free_queue_number = ath10k_pci_hif_get_free_queue_number, +}; + +static void ath10k_pci_ce_tasklet(unsigned long ptr) +{ + struct hif_ce_pipe_info *pipe = (struct hif_ce_pipe_info *)ptr; + struct ath10k_pci *ar_pci = pipe->ar_pci; + + ath10k_ce_per_engine_service(ar_pci->ar, pipe->pipe_num); +} + +static void ath10k_msi_err_tasklet(unsigned long data) +{ + struct ath10k *ar = (struct ath10k *)data; + + ath10k_pci_fw_interrupt_handler(ar); +} + +/* + * Handler for a per-engine interrupt on a PARTICULAR CE. + * This is used in cases where each CE has a private MSI interrupt. + */ +static irqreturn_t ath10k_pci_per_engine_handler(int irq, void *arg) +{ + struct ath10k *ar = arg; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int ce_id = irq - ar_pci->pdev->irq - MSI_ASSIGN_CE_INITIAL; + + if (ce_id < 0 || ce_id > ARRAY_SIZE(ar_pci->pipe_info)) { + ath10k_warn("unexpected/invalid irq %d ce_id %d\n", irq, ce_id); + return IRQ_HANDLED; + } + + /* + * NOTE: We are able to derive ce_id from irq because we + * use a one-to-one mapping for CE's 0..5. + * CE's 6 & 7 do not use interrupts at all. + * + * This mapping must be kept in sync with the mapping + * used by firmware. + */ + tasklet_schedule(&ar_pci->pipe_info[ce_id].intr); + return IRQ_HANDLED; +} + +static irqreturn_t ath10k_pci_msi_fw_handler(int irq, void *arg) +{ + struct ath10k *ar = arg; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + + tasklet_schedule(&ar_pci->msi_fw_err); + return IRQ_HANDLED; +} + +/* + * Top-level interrupt handler for all PCI interrupts from a Target. + * When a block of MSI interrupts is allocated, this top-level handler + * is not used; instead, we directly call the correct sub-handler. + */ +static irqreturn_t ath10k_pci_interrupt_handler(int irq, void *arg) +{ + struct ath10k *ar = arg; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + + if (ar_pci->num_msi_intrs == 0) { + /* + * IMPORTANT: INTR_CLR regiser has to be set after + * INTR_ENABLE is set to 0, otherwise interrupt can not be + * really cleared. + */ + iowrite32(0, ar_pci->mem + + (SOC_CORE_BASE_ADDRESS | + PCIE_INTR_ENABLE_ADDRESS)); + iowrite32(PCIE_INTR_FIRMWARE_MASK | + PCIE_INTR_CE_MASK_ALL, + ar_pci->mem + (SOC_CORE_BASE_ADDRESS | + PCIE_INTR_CLR_ADDRESS)); + /* + * IMPORTANT: this extra read transaction is required to + * flush the posted write buffer. + */ + (void) ioread32(ar_pci->mem + + (SOC_CORE_BASE_ADDRESS | + PCIE_INTR_ENABLE_ADDRESS)); + } + + tasklet_schedule(&ar_pci->intr_tq); + + return IRQ_HANDLED; +} + +static void ath10k_pci_tasklet(unsigned long data) +{ + struct ath10k *ar = (struct ath10k *)data; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + + ath10k_pci_fw_interrupt_handler(ar); /* FIXME: Handle FW error */ + ath10k_ce_per_engine_service_any(ar); + + if (ar_pci->num_msi_intrs == 0) { + /* Enable Legacy PCI line interrupts */ + iowrite32(PCIE_INTR_FIRMWARE_MASK | + PCIE_INTR_CE_MASK_ALL, + ar_pci->mem + (SOC_CORE_BASE_ADDRESS | + PCIE_INTR_ENABLE_ADDRESS)); + /* + * IMPORTANT: this extra read transaction is required to + * flush the posted write buffer + */ + (void) ioread32(ar_pci->mem + + (SOC_CORE_BASE_ADDRESS | + PCIE_INTR_ENABLE_ADDRESS)); + } +} + +static int ath10k_pci_start_intr_msix(struct ath10k *ar, int num) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int ret; + int i; + + ret = pci_enable_msi_block(ar_pci->pdev, num); + if (ret) + return ret; + + ret = request_irq(ar_pci->pdev->irq + MSI_ASSIGN_FW, + ath10k_pci_msi_fw_handler, + IRQF_SHARED, "ath10k_pci", ar); + if (ret) + return ret; + + for (i = MSI_ASSIGN_CE_INITIAL; i <= MSI_ASSIGN_CE_MAX; i++) { + ret = request_irq(ar_pci->pdev->irq + i, + ath10k_pci_per_engine_handler, + IRQF_SHARED, "ath10k_pci", ar); + if (ret) { + ath10k_warn("request_irq(%d) failed %d\n", + ar_pci->pdev->irq + i, ret); + + for (; i >= MSI_ASSIGN_CE_INITIAL; i--) + free_irq(ar_pci->pdev->irq, ar); + + pci_disable_msi(ar_pci->pdev); + return ret; + } + } + + ath10k_info("MSI-X interrupt handling (%d intrs)\n", num); + return 0; +} + +static int ath10k_pci_start_intr_msi(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int ret; + + ret = pci_enable_msi(ar_pci->pdev); + if (ret < 0) + return ret; + + ret = request_irq(ar_pci->pdev->irq, + ath10k_pci_interrupt_handler, + IRQF_SHARED, "ath10k_pci", ar); + if (ret < 0) { + pci_disable_msi(ar_pci->pdev); + return ret; + } + + ath10k_info("MSI interrupt handling\n"); + return 0; +} + +static int ath10k_pci_start_intr_legacy(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int ret; + + ret = request_irq(ar_pci->pdev->irq, + ath10k_pci_interrupt_handler, + IRQF_SHARED, "ath10k_pci", ar); + if (ret < 0) + return ret; + + /* + * Make sure to wake the Target before enabling Legacy + * Interrupt. + */ + iowrite32(PCIE_SOC_WAKE_V_MASK, + ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS + + PCIE_SOC_WAKE_ADDRESS); + + ath10k_pci_wait(ar); + + /* + * A potential race occurs here: The CORE_BASE write + * depends on target correctly decoding AXI address but + * host won't know when target writes BAR to CORE_CTRL. + * This write might get lost if target has NOT written BAR. + * For now, fix the race by repeating the write in below + * synchronization checking. + */ + iowrite32(PCIE_INTR_FIRMWARE_MASK | + PCIE_INTR_CE_MASK_ALL, + ar_pci->mem + (SOC_CORE_BASE_ADDRESS | + PCIE_INTR_ENABLE_ADDRESS)); + iowrite32(PCIE_SOC_WAKE_RESET, + ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS + + PCIE_SOC_WAKE_ADDRESS); + + ath10k_info("legacy interrupt handling\n"); + return 0; +} + +static int ath10k_pci_start_intr(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int num = MSI_NUM_REQUEST; + int ret; + int i; + + tasklet_init(&ar_pci->intr_tq, ath10k_pci_tasklet, (unsigned long) ar); + tasklet_init(&ar_pci->msi_fw_err, ath10k_msi_err_tasklet, + (unsigned long) ar); + + for (i = 0; i < CE_COUNT; i++) { + ar_pci->pipe_info[i].ar_pci = ar_pci; + tasklet_init(&ar_pci->pipe_info[i].intr, + ath10k_pci_ce_tasklet, + (unsigned long)&ar_pci->pipe_info[i]); + } + + if (!test_bit(ATH10K_PCI_FEATURE_MSI_X, ar_pci->features)) + num = 1; + + if (num > 1) { + ret = ath10k_pci_start_intr_msix(ar, num); + if (ret == 0) + goto exit; + + ath10k_warn("MSI-X didn't succeed (%d), trying MSI\n", ret); + num = 1; + } + + if (num == 1) { + ret = ath10k_pci_start_intr_msi(ar); + if (ret == 0) + goto exit; + + ath10k_warn("MSI didn't succeed (%d), trying legacy INTR\n", + ret); + num = 0; + } + + ret = ath10k_pci_start_intr_legacy(ar); + +exit: + ar_pci->num_msi_intrs = num; + ar_pci->ce_count = CE_COUNT; + return ret; +} + +static void ath10k_pci_stop_intr(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int i; + + /* There's at least one interrupt irregardless whether its legacy INTR + * or MSI or MSI-X */ + for (i = 0; i < max(1, ar_pci->num_msi_intrs); i++) + free_irq(ar_pci->pdev->irq + i, ar); + + if (ar_pci->num_msi_intrs > 0) + pci_disable_msi(ar_pci->pdev); +} + +static int ath10k_pci_reset_target(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int wait_limit = 300; /* 3 sec */ + + /* Wait for Target to finish initialization before we proceed. */ + iowrite32(PCIE_SOC_WAKE_V_MASK, + ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS + + PCIE_SOC_WAKE_ADDRESS); + + ath10k_pci_wait(ar); + + while (wait_limit-- && + !(ioread32(ar_pci->mem + FW_INDICATOR_ADDRESS) & + FW_IND_INITIALIZED)) { + if (ar_pci->num_msi_intrs == 0) + /* Fix potential race by repeating CORE_BASE writes */ + iowrite32(PCIE_INTR_FIRMWARE_MASK | + PCIE_INTR_CE_MASK_ALL, + ar_pci->mem + (SOC_CORE_BASE_ADDRESS | + PCIE_INTR_ENABLE_ADDRESS)); + mdelay(10); + } + + if (wait_limit < 0) { + ath10k_err("Target stalled\n"); + iowrite32(PCIE_SOC_WAKE_RESET, + ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS + + PCIE_SOC_WAKE_ADDRESS); + return -EIO; + } + + iowrite32(PCIE_SOC_WAKE_RESET, + ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS + + PCIE_SOC_WAKE_ADDRESS); + + return 0; +} + +static void ath10k_pci_device_reset(struct ath10k_pci *ar_pci) +{ + struct ath10k *ar = ar_pci->ar; + void __iomem *mem = ar_pci->mem; + int i; + u32 val; + + if (!SOC_GLOBAL_RESET_ADDRESS) + return; + + if (!mem) + return; + + ath10k_pci_reg_write32(mem, PCIE_SOC_WAKE_ADDRESS, + PCIE_SOC_WAKE_V_MASK); + for (i = 0; i < ATH_PCI_RESET_WAIT_MAX; i++) { + if (ath10k_pci_target_is_awake(ar)) + break; + msleep(1); + } + + /* Put Target, including PCIe, into RESET. */ + val = ath10k_pci_reg_read32(mem, SOC_GLOBAL_RESET_ADDRESS); + val |= 1; + ath10k_pci_reg_write32(mem, SOC_GLOBAL_RESET_ADDRESS, val); + + for (i = 0; i < ATH_PCI_RESET_WAIT_MAX; i++) { + if (ath10k_pci_reg_read32(mem, RTC_STATE_ADDRESS) & + RTC_STATE_COLD_RESET_MASK) + break; + msleep(1); + } + + /* Pull Target, including PCIe, out of RESET. */ + val &= ~1; + ath10k_pci_reg_write32(mem, SOC_GLOBAL_RESET_ADDRESS, val); + + for (i = 0; i < ATH_PCI_RESET_WAIT_MAX; i++) { + if (!(ath10k_pci_reg_read32(mem, RTC_STATE_ADDRESS) & + RTC_STATE_COLD_RESET_MASK)) + break; + msleep(1); + } + + ath10k_pci_reg_write32(mem, PCIE_SOC_WAKE_ADDRESS, PCIE_SOC_WAKE_RESET); +} + +static void ath10k_pci_dump_features(struct ath10k_pci *ar_pci) +{ + int i; + + for (i = 0; i < ATH10K_PCI_FEATURE_COUNT; i++) { + if (!test_bit(i, ar_pci->features)) + continue; + + switch (i) { + case ATH10K_PCI_FEATURE_MSI_X: + ath10k_dbg(ATH10K_DBG_PCI, "device supports MSI-X\n"); + break; + case ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND: + ath10k_dbg(ATH10K_DBG_PCI, "QCA988X_1.0 workaround enabled\n"); + break; + } + } +} + +static int ath10k_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *pci_dev) +{ + void __iomem *mem; + int ret = 0; + struct ath10k *ar; + struct ath10k_pci *ar_pci; + u32 lcr_val; + + ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__); + + ar_pci = kzalloc(sizeof(*ar_pci), GFP_KERNEL); + if (ar_pci == NULL) + return -ENOMEM; + + ar_pci->pdev = pdev; + ar_pci->dev = &pdev->dev; + + switch (pci_dev->device) { + case QCA988X_1_0_DEVICE_ID: + set_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features); + break; + case QCA988X_2_0_DEVICE_ID: + set_bit(ATH10K_PCI_FEATURE_MSI_X, ar_pci->features); + break; + default: + ret = -ENODEV; + ath10k_err("Unkown device ID: %d\n", pci_dev->device); + goto err_ar_pci; + } + + ath10k_pci_dump_features(ar_pci); + + ar = ath10k_core_create(ar_pci, ar_pci->dev, ATH10K_BUS_PCI, + &ath10k_pci_hif_ops); + if (!ar) { + ath10k_err("ath10k_core_create failed!\n"); + ret = -EINVAL; + goto err_ar_pci; + } + + /* Enable QCA988X_1.0 HW workarounds */ + if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features)) + spin_lock_init(&ar_pci->hw_v1_workaround_lock); + + ar_pci->ar = ar; + ar_pci->fw_indicator_address = FW_INDICATOR_ADDRESS; + atomic_set(&ar_pci->keep_awake_count, 0); + + pci_set_drvdata(pdev, ar); + + /* + * Without any knowledge of the Host, the Target may have been reset or + * power cycled and its Config Space may no longer reflect the PCI + * address space that was assigned earlier by the PCI infrastructure. + * Refresh it now. + */ + ret = pci_assign_resource(pdev, BAR_NUM); + if (ret) { + ath10k_err("cannot assign PCI space: %d\n", ret); + goto err_ar; + } + + ret = pci_enable_device(pdev); + if (ret) { + ath10k_err("cannot enable PCI device: %d\n", ret); + goto err_ar; + } + + /* Request MMIO resources */ + ret = pci_request_region(pdev, BAR_NUM, "ath"); + if (ret) { + ath10k_err("PCI MMIO reservation error: %d\n", ret); + goto err_device; + } + + /* + * Target structures have a limit of 32 bit DMA pointers. + * DMA pointers can be wider than 32 bits by default on some systems. + */ + ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (ret) { + ath10k_err("32-bit DMA not available: %d\n", ret); + goto err_region; + } + + ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); + if (ret) { + ath10k_err("cannot enable 32-bit consistent DMA\n"); + goto err_region; + } + + /* Set bus master bit in PCI_COMMAND to enable DMA */ + pci_set_master(pdev); + + /* + * Temporary FIX: disable ASPM + * Will be removed after the OTP is programmed + */ + pci_read_config_dword(pdev, 0x80, &lcr_val); + pci_write_config_dword(pdev, 0x80, (lcr_val & 0xffffff00)); + + /* Arrange for access to Target SoC registers. */ + mem = pci_iomap(pdev, BAR_NUM, 0); + if (!mem) { + ath10k_err("PCI iomap error\n"); + ret = -EIO; + goto err_master; + } + + ar_pci->mem = mem; + + spin_lock_init(&ar_pci->ce_lock); + + ar_pci->cacheline_sz = dma_get_cache_alignment(); + + ret = ath10k_pci_start_intr(ar); + if (ret) { + ath10k_err("could not start interrupt handling (%d)\n", ret); + goto err_iomap; + } + + /* + * Bring the target up cleanly. + * + * The target may be in an undefined state with an AUX-powered Target + * and a Host in WoW mode. If the Host crashes, loses power, or is + * restarted (without unloading the driver) then the Target is left + * (aux) powered and running. On a subsequent driver load, the Target + * is in an unexpected state. We try to catch that here in order to + * reset the Target and retry the probe. + */ + ath10k_pci_device_reset(ar_pci); + + ret = ath10k_pci_reset_target(ar); + if (ret) + goto err_intr; + + if (ath10k_target_ps) { + ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save enabled\n"); + } else { + /* Force AWAKE forever */ + ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save disabled\n"); + ath10k_do_pci_wake(ar); + } + + ret = ath10k_pci_ce_init(ar); + if (ret) + goto err_intr; + + ret = ath10k_pci_init_config(ar); + if (ret) + goto err_ce; + + ret = ath10k_pci_wake_target_cpu(ar); + if (ret) { + ath10k_err("could not wake up target CPU (%d)\n", ret); + goto err_ce; + } + + ret = ath10k_core_register(ar); + if (ret) { + ath10k_err("could not register driver core (%d)\n", ret); + goto err_ce; + } + + return 0; + +err_ce: + ath10k_pci_ce_deinit(ar); +err_intr: + ath10k_pci_stop_intr(ar); +err_iomap: + pci_iounmap(pdev, mem); +err_master: + pci_clear_master(pdev); +err_region: + pci_release_region(pdev, BAR_NUM); +err_device: + pci_disable_device(pdev); +err_ar: + pci_set_drvdata(pdev, NULL); + ath10k_core_destroy(ar); +err_ar_pci: + /* call HIF PCI free here */ + kfree(ar_pci); + + return ret; +} + +static void ath10k_pci_remove(struct pci_dev *pdev) +{ + struct ath10k *ar = pci_get_drvdata(pdev); + struct ath10k_pci *ar_pci; + + ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__); + + if (!ar) + return; + + ar_pci = ath10k_pci_priv(ar); + + if (!ar_pci) + return; + + tasklet_kill(&ar_pci->msi_fw_err); + + ath10k_core_unregister(ar); + ath10k_pci_stop_intr(ar); + + pci_set_drvdata(pdev, NULL); + pci_iounmap(pdev, ar_pci->mem); + pci_release_region(pdev, BAR_NUM); + pci_clear_master(pdev); + pci_disable_device(pdev); + + ath10k_core_destroy(ar); + kfree(ar_pci); +} + +#if defined(CONFIG_PM_SLEEP) + +#define ATH10K_PCI_PM_CONTROL 0x44 + +static int ath10k_pci_suspend(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct ath10k *ar = pci_get_drvdata(pdev); + struct ath10k_pci *ar_pci; + u32 val; + int ret, retval; + + ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__); + + if (!ar) + return -ENODEV; + + ar_pci = ath10k_pci_priv(ar); + if (!ar_pci) + return -ENODEV; + + if (ath10k_core_target_suspend(ar)) + return -EBUSY; + + ret = wait_event_interruptible_timeout(ar->event_queue, + ar->is_target_paused == true, + 1 * HZ); + if (ret < 0) { + ath10k_warn("suspend interrupted (%d)\n", ret); + retval = ret; + goto resume; + } else if (ret == 0) { + ath10k_warn("suspend timed out - target pause event never came\n"); + retval = EIO; + goto resume; + } + + /* + * reset is_target_paused and host can check that in next time, + * or it will always be TRUE and host just skip the waiting + * condition, it causes target assert due to host already + * suspend + */ + ar->is_target_paused = false; + + pci_read_config_dword(pdev, ATH10K_PCI_PM_CONTROL, &val); + + if ((val & 0x000000ff) != 0x3) { + pci_save_state(pdev); + pci_disable_device(pdev); + pci_write_config_dword(pdev, ATH10K_PCI_PM_CONTROL, + (val & 0xffffff00) | 0x03); + } + + return 0; +resume: + ret = ath10k_core_target_resume(ar); + if (ret) + ath10k_warn("could not resume (%d)\n", ret); + + return retval; +} + +static int ath10k_pci_resume(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct ath10k *ar = pci_get_drvdata(pdev); + struct ath10k_pci *ar_pci; + int ret; + u32 val; + + ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__); + + if (!ar) + return -ENODEV; + ar_pci = ath10k_pci_priv(ar); + + if (!ar_pci) + return -ENODEV; + + ret = pci_enable_device(pdev); + if (ret) { + ath10k_warn("cannot enable PCI device: %d\n", ret); + return ret; + } + + pci_read_config_dword(pdev, ATH10K_PCI_PM_CONTROL, &val); + + if ((val & 0x000000ff) != 0) { + pci_restore_state(pdev); + pci_write_config_dword(pdev, ATH10K_PCI_PM_CONTROL, + val & 0xffffff00); + /* + * Suspend/Resume resets the PCI configuration space, + * so we have to re-disable the RETRY_TIMEOUT register (0x41) + * to keep PCI Tx retries from interfering with C3 CPU state + */ + pci_read_config_dword(pdev, 0x40, &val); + + if ((val & 0x0000ff00) != 0) + pci_write_config_dword(pdev, 0x40, val & 0xffff00ff); + } + + ret = ath10k_core_target_resume(ar); + if (ret) + ath10k_warn("target resume failed: %d\n", ret); + + return ret; +} + +static SIMPLE_DEV_PM_OPS(ath10k_dev_pm_ops, + ath10k_pci_suspend, + ath10k_pci_resume); + +#define ATH10K_PCI_PM_OPS (&ath10k_dev_pm_ops) + +#else + +#define ATH10K_PCI_PM_OPS NULL + +#endif /* CONFIG_PM_SLEEP */ + +MODULE_DEVICE_TABLE(pci, ath10k_pci_id_table); + +static struct pci_driver ath10k_pci_driver = { + .name = "ath10k_pci", + .id_table = ath10k_pci_id_table, + .probe = ath10k_pci_probe, + .remove = ath10k_pci_remove, + .driver.pm = ATH10K_PCI_PM_OPS, +}; + +static int __init ath10k_pci_init(void) +{ + int ret; + + ret = pci_register_driver(&ath10k_pci_driver); + if (ret) + ath10k_err("pci_register_driver failed [%d]\n", ret); + + return ret; +} +module_init(ath10k_pci_init); + +static void __exit ath10k_pci_exit(void) +{ + pci_unregister_driver(&ath10k_pci_driver); +} + +module_exit(ath10k_pci_exit); + +MODULE_AUTHOR("Qualcomm Atheros"); +MODULE_DESCRIPTION("Driver support for Atheros QCA988X PCIe devices"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_FIRMWARE(QCA988X_HW_1_0_FW_DIR "/" QCA988X_HW_1_0_FW_FILE); +MODULE_FIRMWARE(QCA988X_HW_1_0_FW_DIR "/" QCA988X_HW_1_0_OTP_FILE); +MODULE_FIRMWARE(QCA988X_HW_1_0_FW_DIR "/" QCA988X_HW_1_0_BOARD_DATA_FILE); +MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_FW_FILE); +MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_OTP_FILE); +MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_BOARD_DATA_FILE); diff --git a/drivers/net/wireless/ath/ath10k/pci.h b/drivers/net/wireless/ath/ath10k/pci.h new file mode 100644 index 000000000000..d2a055a07dc6 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/pci.h @@ -0,0 +1,355 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _PCI_H_ +#define _PCI_H_ + +#include + +#include "hw.h" +#include "ce.h" + +/* FW dump area */ +#define REG_DUMP_COUNT_QCA988X 60 + +/* + * maximum number of bytes that can be handled atomically by DiagRead/DiagWrite + */ +#define DIAG_TRANSFER_LIMIT 2048 + +/* + * maximum number of bytes that can be + * handled atomically by DiagRead/DiagWrite + */ +#define DIAG_TRANSFER_LIMIT 2048 + +struct bmi_xfer { + struct completion done; + bool wait_for_resp; + u32 resp_len; +}; + +struct ath10k_pci_compl { + struct list_head list; + int send_or_recv; + struct ce_state *ce_state; + struct hif_ce_pipe_info *pipe_info; + void *transfer_context; + unsigned int nbytes; + unsigned int transfer_id; + unsigned int flags; +}; + +/* compl_state.send_or_recv */ +#define HIF_CE_COMPLETE_FREE 0 +#define HIF_CE_COMPLETE_SEND 1 +#define HIF_CE_COMPLETE_RECV 2 + +/* + * PCI-specific Target state + * + * NOTE: Structure is shared between Host software and Target firmware! + * + * Much of this may be of interest to the Host so + * HOST_INTEREST->hi_interconnect_state points here + * (and all members are 32-bit quantities in order to + * facilitate Host access). In particular, Host software is + * required to initialize pipe_cfg_addr and svc_to_pipe_map. + */ +struct pcie_state { + /* Pipe configuration Target address */ + /* NB: ce_pipe_config[CE_COUNT] */ + u32 pipe_cfg_addr; + + /* Service to pipe map Target address */ + /* NB: service_to_pipe[PIPE_TO_CE_MAP_CN] */ + u32 svc_to_pipe_map; + + /* number of MSI interrupts requested */ + u32 msi_requested; + + /* number of MSI interrupts granted */ + u32 msi_granted; + + /* Message Signalled Interrupt address */ + u32 msi_addr; + + /* Base data */ + u32 msi_data; + + /* + * Data for firmware interrupt; + * MSI data for other interrupts are + * in various SoC registers + */ + u32 msi_fw_intr_data; + + /* PCIE_PWR_METHOD_* */ + u32 power_mgmt_method; + + /* PCIE_CONFIG_FLAG_* */ + u32 config_flags; +}; + +/* PCIE_CONFIG_FLAG definitions */ +#define PCIE_CONFIG_FLAG_ENABLE_L1 0x0000001 + +/* Host software's Copy Engine configuration. */ +#define CE_ATTR_FLAGS 0 + +/* + * Configuration information for a Copy Engine pipe. + * Passed from Host to Target during startup (one per CE). + * + * NOTE: Structure is shared between Host software and Target firmware! + */ +struct ce_pipe_config { + u32 pipenum; + u32 pipedir; + u32 nentries; + u32 nbytes_max; + u32 flags; + u32 reserved; +}; + +/* + * Directions for interconnect pipe configuration. + * These definitions may be used during configuration and are shared + * between Host and Target. + * + * Pipe Directions are relative to the Host, so PIPEDIR_IN means + * "coming IN over air through Target to Host" as with a WiFi Rx operation. + * Conversely, PIPEDIR_OUT means "going OUT from Host through Target over air" + * as with a WiFi Tx operation. This is somewhat awkward for the "middle-man" + * Target since things that are "PIPEDIR_OUT" are coming IN to the Target + * over the interconnect. + */ +#define PIPEDIR_NONE 0 +#define PIPEDIR_IN 1 /* Target-->Host, WiFi Rx direction */ +#define PIPEDIR_OUT 2 /* Host->Target, WiFi Tx direction */ +#define PIPEDIR_INOUT 3 /* bidirectional */ + +/* Establish a mapping between a service/direction and a pipe. */ +struct service_to_pipe { + u32 service_id; + u32 pipedir; + u32 pipenum; +}; + +enum ath10k_pci_features { + ATH10K_PCI_FEATURE_MSI_X = 0, + ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND = 1, + + /* keep last */ + ATH10K_PCI_FEATURE_COUNT +}; + +/* Per-pipe state. */ +struct hif_ce_pipe_info { + /* Handle of underlying Copy Engine */ + struct ce_state *ce_hdl; + + /* Our pipe number; facilitiates use of pipe_info ptrs. */ + u8 pipe_num; + + /* Convenience back pointer to hif_ce_state. */ + struct ath10k *hif_ce_state; + + size_t buf_sz; + + /* protects compl_free and num_send_allowed */ + spinlock_t pipe_lock; + + /* List of free CE completion slots */ + struct list_head compl_free; + + /* Limit the number of outstanding send requests. */ + int num_sends_allowed; + + struct ath10k_pci *ar_pci; + struct tasklet_struct intr; +}; + +struct ath10k_pci { + struct pci_dev *pdev; + struct device *dev; + struct ath10k *ar; + void __iomem *mem; + int cacheline_sz; + + DECLARE_BITMAP(features, ATH10K_PCI_FEATURE_COUNT); + + /* + * Number of MSI interrupts granted, 0 --> using legacy PCI line + * interrupts. + */ + int num_msi_intrs; + + struct tasklet_struct intr_tq; + struct tasklet_struct msi_fw_err; + + /* Number of Copy Engines supported */ + unsigned int ce_count; + + int started; + + atomic_t keep_awake_count; + bool verified_awake; + + /* List of CE completions to be processed */ + struct list_head compl_process; + + /* protects compl_processing and compl_process */ + spinlock_t compl_lock; + + bool compl_processing; + + struct hif_ce_pipe_info pipe_info[CE_COUNT_MAX]; + + struct ath10k_hif_cb msg_callbacks_current; + + /* Target address used to signal a pending firmware event */ + u32 fw_indicator_address; + + /* Copy Engine used for Diagnostic Accesses */ + struct ce_state *ce_diag; + + /* FIXME: document what this really protects */ + spinlock_t ce_lock; + + /* Map CE id to ce_state */ + struct ce_state *ce_id_to_state[CE_COUNT_MAX]; + + /* makes sure that dummy reads are atomic */ + spinlock_t hw_v1_workaround_lock; +}; + +static inline struct ath10k_pci *ath10k_pci_priv(struct ath10k *ar) +{ + return ar->hif.priv; +} + +static inline u32 ath10k_pci_reg_read32(void __iomem *mem, u32 addr) +{ + return ioread32(mem + PCIE_LOCAL_BASE_ADDRESS + addr); +} + +static inline void ath10k_pci_reg_write32(void __iomem *mem, u32 addr, u32 val) +{ + iowrite32(val, mem + PCIE_LOCAL_BASE_ADDRESS + addr); +} + +#define ATH_PCI_RESET_WAIT_MAX 10 /* ms */ +#define PCIE_WAKE_TIMEOUT 5000 /* 5ms */ + +#define BAR_NUM 0 + +#define CDC_WAR_MAGIC_STR 0xceef0000 +#define CDC_WAR_DATA_CE 4 + +/* + * TODO: Should be a function call specific to each Target-type. + * This convoluted macro converts from Target CPU Virtual Address Space to CE + * Address Space. As part of this process, we conservatively fetch the current + * PCIE_BAR. MOST of the time, this should match the upper bits of PCI space + * for this device; but that's not guaranteed. + */ +#define TARG_CPU_SPACE_TO_CE_SPACE(ar, pci_addr, addr) \ + (((ioread32((pci_addr)+(SOC_CORE_BASE_ADDRESS| \ + CORE_CTRL_ADDRESS)) & 0x7ff) << 21) | \ + 0x100000 | ((addr) & 0xfffff)) + +/* Wait up to this many Ms for a Diagnostic Access CE operation to complete */ +#define DIAG_ACCESS_CE_TIMEOUT_MS 10 + +/* + * This API allows the Host to access Target registers directly + * and relatively efficiently over PCIe. + * This allows the Host to avoid extra overhead associated with + * sending a message to firmware and waiting for a response message + * from firmware, as is done on other interconnects. + * + * Yet there is some complexity with direct accesses because the + * Target's power state is not known a priori. The Host must issue + * special PCIe reads/writes in order to explicitly wake the Target + * and to verify that it is awake and will remain awake. + * + * Usage: + * + * Use ath10k_pci_read32 and ath10k_pci_write32 to access Target space. + * These calls must be bracketed by ath10k_pci_wake and + * ath10k_pci_sleep. A single BEGIN/END pair is adequate for + * multiple READ/WRITE operations. + * + * Use ath10k_pci_wake to put the Target in a state in + * which it is legal for the Host to directly access it. This + * may involve waking the Target from a low power state, which + * may take up to 2Ms! + * + * Use ath10k_pci_sleep to tell the Target that as far as + * this code path is concerned, it no longer needs to remain + * directly accessible. BEGIN/END is under a reference counter; + * multiple code paths may issue BEGIN/END on a single targid. + */ +static inline void ath10k_pci_write32(struct ath10k *ar, u32 offset, + u32 value) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + void __iomem *addr = ar_pci->mem; + + if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features)) { + unsigned long irq_flags; + + spin_lock_irqsave(&ar_pci->hw_v1_workaround_lock, irq_flags); + + ioread32(addr+offset+4); /* 3rd read prior to write */ + ioread32(addr+offset+4); /* 2nd read prior to write */ + ioread32(addr+offset+4); /* 1st read prior to write */ + iowrite32(value, addr+offset); + + spin_unlock_irqrestore(&ar_pci->hw_v1_workaround_lock, + irq_flags); + } else { + iowrite32(value, addr+offset); + } +} + +static inline u32 ath10k_pci_read32(struct ath10k *ar, u32 offset) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + + return ioread32(ar_pci->mem + offset); +} + +extern unsigned int ath10k_target_ps; + +void ath10k_do_pci_wake(struct ath10k *ar); +void ath10k_do_pci_sleep(struct ath10k *ar); + +static inline void ath10k_pci_wake(struct ath10k *ar) +{ + if (ath10k_target_ps) + ath10k_do_pci_wake(ar); +} + +static inline void ath10k_pci_sleep(struct ath10k *ar) +{ + if (ath10k_target_ps) + ath10k_do_pci_sleep(ar); +} + +#endif /* _PCI_H_ */ diff --git a/drivers/net/wireless/ath/ath10k/rx_desc.h b/drivers/net/wireless/ath/ath10k/rx_desc.h new file mode 100644 index 000000000000..bfec6c8f2ecb --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/rx_desc.h @@ -0,0 +1,990 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _RX_DESC_H_ +#define _RX_DESC_H_ + +enum rx_attention_flags { + RX_ATTENTION_FLAGS_FIRST_MPDU = 1 << 0, + RX_ATTENTION_FLAGS_LAST_MPDU = 1 << 1, + RX_ATTENTION_FLAGS_MCAST_BCAST = 1 << 2, + RX_ATTENTION_FLAGS_PEER_IDX_INVALID = 1 << 3, + RX_ATTENTION_FLAGS_PEER_IDX_TIMEOUT = 1 << 4, + RX_ATTENTION_FLAGS_POWER_MGMT = 1 << 5, + RX_ATTENTION_FLAGS_NON_QOS = 1 << 6, + RX_ATTENTION_FLAGS_NULL_DATA = 1 << 7, + RX_ATTENTION_FLAGS_MGMT_TYPE = 1 << 8, + RX_ATTENTION_FLAGS_CTRL_TYPE = 1 << 9, + RX_ATTENTION_FLAGS_MORE_DATA = 1 << 10, + RX_ATTENTION_FLAGS_EOSP = 1 << 11, + RX_ATTENTION_FLAGS_U_APSD_TRIGGER = 1 << 12, + RX_ATTENTION_FLAGS_FRAGMENT = 1 << 13, + RX_ATTENTION_FLAGS_ORDER = 1 << 14, + RX_ATTENTION_FLAGS_CLASSIFICATION = 1 << 15, + RX_ATTENTION_FLAGS_OVERFLOW_ERR = 1 << 16, + RX_ATTENTION_FLAGS_MSDU_LENGTH_ERR = 1 << 17, + RX_ATTENTION_FLAGS_TCP_UDP_CHKSUM_FAIL = 1 << 18, + RX_ATTENTION_FLAGS_IP_CHKSUM_FAIL = 1 << 19, + RX_ATTENTION_FLAGS_SA_IDX_INVALID = 1 << 20, + RX_ATTENTION_FLAGS_DA_IDX_INVALID = 1 << 21, + RX_ATTENTION_FLAGS_SA_IDX_TIMEOUT = 1 << 22, + RX_ATTENTION_FLAGS_DA_IDX_TIMEOUT = 1 << 23, + RX_ATTENTION_FLAGS_ENCRYPT_REQUIRED = 1 << 24, + RX_ATTENTION_FLAGS_DIRECTED = 1 << 25, + RX_ATTENTION_FLAGS_BUFFER_FRAGMENT = 1 << 26, + RX_ATTENTION_FLAGS_MPDU_LENGTH_ERR = 1 << 27, + RX_ATTENTION_FLAGS_TKIP_MIC_ERR = 1 << 28, + RX_ATTENTION_FLAGS_DECRYPT_ERR = 1 << 29, + RX_ATTENTION_FLAGS_FCS_ERR = 1 << 30, + RX_ATTENTION_FLAGS_MSDU_DONE = 1 << 31, +}; + +struct rx_attention { + __le32 flags; /* %RX_ATTENTION_FLAGS_ */ +} __packed; + +/* + * first_mpdu + * Indicates the first MSDU of the PPDU. If both first_mpdu + * and last_mpdu are set in the MSDU then this is a not an + * A-MPDU frame but a stand alone MPDU. Interior MPDU in an + * A-MPDU shall have both first_mpdu and last_mpdu bits set to + * 0. The PPDU start status will only be valid when this bit + * is set. + * + * last_mpdu + * Indicates the last MSDU of the last MPDU of the PPDU. The + * PPDU end status will only be valid when this bit is set. + * + * mcast_bcast + * Multicast / broadcast indicator. Only set when the MAC + * address 1 bit 0 is set indicating mcast/bcast and the BSSID + * matches one of the 4 BSSID registers. Only set when + * first_msdu is set. + * + * peer_idx_invalid + * Indicates no matching entries within the the max search + * count. Only set when first_msdu is set. + * + * peer_idx_timeout + * Indicates an unsuccessful search for the peer index due to + * timeout. Only set when first_msdu is set. + * + * power_mgmt + * Power management bit set in the 802.11 header. Only set + * when first_msdu is set. + * + * non_qos + * Set if packet is not a non-QoS data frame. Only set when + * first_msdu is set. + * + * null_data + * Set if frame type indicates either null data or QoS null + * data format. Only set when first_msdu is set. + * + * mgmt_type + * Set if packet is a management packet. Only set when + * first_msdu is set. + * + * ctrl_type + * Set if packet is a control packet. Only set when first_msdu + * is set. + * + * more_data + * Set if more bit in frame control is set. Only set when + * first_msdu is set. + * + * eosp + * Set if the EOSP (end of service period) bit in the QoS + * control field is set. Only set when first_msdu is set. + * + * u_apsd_trigger + * Set if packet is U-APSD trigger. Key table will have bits + * per TID to indicate U-APSD trigger. + * + * fragment + * Indicates that this is an 802.11 fragment frame. This is + * set when either the more_frag bit is set in the frame + * control or the fragment number is not zero. Only set when + * first_msdu is set. + * + * order + * Set if the order bit in the frame control is set. Only set + * when first_msdu is set. + * + * classification + * Indicates that this status has a corresponding MSDU that + * requires FW processing. The OLE will have classification + * ring mask registers which will indicate the ring(s) for + * packets and descriptors which need FW attention. + * + * overflow_err + * PCU Receive FIFO does not have enough space to store the + * full receive packet. Enough space is reserved in the + * receive FIFO for the status is written. This MPDU remaining + * packets in the PPDU will be filtered and no Ack response + * will be transmitted. + * + * msdu_length_err + * Indicates that the MSDU length from the 802.3 encapsulated + * length field extends beyond the MPDU boundary. + * + * tcp_udp_chksum_fail + * Indicates that the computed checksum (tcp_udp_chksum) did + * not match the checksum in the TCP/UDP header. + * + * ip_chksum_fail + * Indicates that the computed checksum did not match the + * checksum in the IP header. + * + * sa_idx_invalid + * Indicates no matching entry was found in the address search + * table for the source MAC address. + * + * da_idx_invalid + * Indicates no matching entry was found in the address search + * table for the destination MAC address. + * + * sa_idx_timeout + * Indicates an unsuccessful search for the source MAC address + * due to the expiring of the search timer. + * + * da_idx_timeout + * Indicates an unsuccessful search for the destination MAC + * address due to the expiring of the search timer. + * + * encrypt_required + * Indicates that this data type frame is not encrypted even if + * the policy for this MPDU requires encryption as indicated in + * the peer table key type. + * + * directed + * MPDU is a directed packet which means that the RA matched + * our STA addresses. In proxySTA it means that the TA matched + * an entry in our address search table with the corresponding + * 'no_ack' bit is the address search entry cleared. + * + * buffer_fragment + * Indicates that at least one of the rx buffers has been + * fragmented. If set the FW should look at the rx_frag_info + * descriptor described below. + * + * mpdu_length_err + * Indicates that the MPDU was pre-maturely terminated + * resulting in a truncated MPDU. Don't trust the MPDU length + * field. + * + * tkip_mic_err + * Indicates that the MPDU Michael integrity check failed + * + * decrypt_err + * Indicates that the MPDU decrypt integrity check failed + * + * fcs_err + * Indicates that the MPDU FCS check failed + * + * msdu_done + * If set indicates that the RX packet data, RX header data, RX + * PPDU start descriptor, RX MPDU start/end descriptor, RX MSDU + * start/end descriptors and RX Attention descriptor are all + * valid. This bit must be in the last octet of the + * descriptor. + */ + +struct rx_frag_info { + u8 ring0_more_count; + u8 ring1_more_count; + u8 ring2_more_count; + u8 ring3_more_count; +} __packed; + +/* + * ring0_more_count + * Indicates the number of more buffers associated with RX DMA + * ring 0. Field is filled in by the RX_DMA. + * + * ring1_more_count + * Indicates the number of more buffers associated with RX DMA + * ring 1. Field is filled in by the RX_DMA. + * + * ring2_more_count + * Indicates the number of more buffers associated with RX DMA + * ring 2. Field is filled in by the RX_DMA. + * + * ring3_more_count + * Indicates the number of more buffers associated with RX DMA + * ring 3. Field is filled in by the RX_DMA. + */ + +enum htt_rx_mpdu_encrypt_type { + HTT_RX_MPDU_ENCRYPT_WEP40 = 0, + HTT_RX_MPDU_ENCRYPT_WEP104 = 1, + HTT_RX_MPDU_ENCRYPT_TKIP_WITHOUT_MIC = 2, + HTT_RX_MPDU_ENCRYPT_WEP128 = 3, + HTT_RX_MPDU_ENCRYPT_TKIP_WPA = 4, + HTT_RX_MPDU_ENCRYPT_WAPI = 5, + HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2 = 6, + HTT_RX_MPDU_ENCRYPT_NONE = 7, +}; + +#define RX_MPDU_START_INFO0_PEER_IDX_MASK 0x000007ff +#define RX_MPDU_START_INFO0_PEER_IDX_LSB 0 +#define RX_MPDU_START_INFO0_SEQ_NUM_MASK 0x0fff0000 +#define RX_MPDU_START_INFO0_SEQ_NUM_LSB 16 +#define RX_MPDU_START_INFO0_ENCRYPT_TYPE_MASK 0xf0000000 +#define RX_MPDU_START_INFO0_ENCRYPT_TYPE_LSB 28 +#define RX_MPDU_START_INFO0_FROM_DS (1 << 11) +#define RX_MPDU_START_INFO0_TO_DS (1 << 12) +#define RX_MPDU_START_INFO0_ENCRYPTED (1 << 13) +#define RX_MPDU_START_INFO0_RETRY (1 << 14) +#define RX_MPDU_START_INFO0_TXBF_H_INFO (1 << 15) + +#define RX_MPDU_START_INFO1_TID_MASK 0xf0000000 +#define RX_MPDU_START_INFO1_TID_LSB 28 +#define RX_MPDU_START_INFO1_DIRECTED (1 << 16) + +struct rx_mpdu_start { + __le32 info0; + union { + struct { + __le32 pn31_0; + __le32 info1; /* %RX_MPDU_START_INFO1_ */ + } __packed; + struct { + u8 pn[6]; + } __packed; + } __packed; +} __packed; + +/* + * peer_idx + * The index of the address search table which associated with + * the peer table entry corresponding to this MPDU. Only valid + * when first_msdu is set. + * + * fr_ds + * Set if the from DS bit is set in the frame control. Only + * valid when first_msdu is set. + * + * to_ds + * Set if the to DS bit is set in the frame control. Only + * valid when first_msdu is set. + * + * encrypted + * Protected bit from the frame control. Only valid when + * first_msdu is set. + * + * retry + * Retry bit from the frame control. Only valid when + * first_msdu is set. + * + * txbf_h_info + * The MPDU data will contain H information. Primarily used + * for debug. + * + * seq_num + * The sequence number from the 802.11 header. Only valid when + * first_msdu is set. + * + * encrypt_type + * Indicates type of decrypt cipher used (as defined in the + * peer table) + * 0: WEP40 + * 1: WEP104 + * 2: TKIP without MIC + * 3: WEP128 + * 4: TKIP (WPA) + * 5: WAPI + * 6: AES-CCM (WPA2) + * 7: No cipher + * Only valid when first_msdu_is set + * + * pn_31_0 + * Bits [31:0] of the PN number extracted from the IV field + * WEP: IV = {key_id_octet, pn2, pn1, pn0}. Only pn[23:0] is + * valid. + * TKIP: IV = {pn5, pn4, pn3, pn2, key_id_octet, pn0, + * WEPSeed[1], pn1}. Only pn[47:0] is valid. + * AES-CCM: IV = {pn5, pn4, pn3, pn2, key_id_octet, 0x0, pn1, + * pn0}. Only pn[47:0] is valid. + * WAPI: IV = {key_id_octet, 0x0, pn15, pn14, pn13, pn12, pn11, + * pn10, pn9, pn8, pn7, pn6, pn5, pn4, pn3, pn2, pn1, pn0}. + * The ext_wapi_pn[127:48] in the rx_msdu_misc descriptor and + * pn[47:0] are valid. + * Only valid when first_msdu is set. + * + * pn_47_32 + * Bits [47:32] of the PN number. See description for + * pn_31_0. The remaining PN fields are in the rx_msdu_end + * descriptor + * + * pn + * Use this field to access the pn without worrying about + * byte-order and bitmasking/bitshifting. + * + * directed + * See definition in RX attention descriptor + * + * reserved_2 + * Reserved: HW should fill with zero. FW should ignore. + * + * tid + * The TID field in the QoS control field + */ + +#define RX_MPDU_END_INFO0_RESERVED_0_MASK 0x00001fff +#define RX_MPDU_END_INFO0_RESERVED_0_LSB 0 +#define RX_MPDU_END_INFO0_POST_DELIM_CNT_MASK 0x0fff0000 +#define RX_MPDU_END_INFO0_POST_DELIM_CNT_LSB 16 +#define RX_MPDU_END_INFO0_OVERFLOW_ERR (1 << 13) +#define RX_MPDU_END_INFO0_LAST_MPDU (1 << 14) +#define RX_MPDU_END_INFO0_POST_DELIM_ERR (1 << 15) +#define RX_MPDU_END_INFO0_MPDU_LENGTH_ERR (1 << 28) +#define RX_MPDU_END_INFO0_TKIP_MIC_ERR (1 << 29) +#define RX_MPDU_END_INFO0_DECRYPT_ERR (1 << 30) +#define RX_MPDU_END_INFO0_FCS_ERR (1 << 31) + +struct rx_mpdu_end { + __le32 info0; +} __packed; + +/* + * reserved_0 + * Reserved + * + * overflow_err + * PCU Receive FIFO does not have enough space to store the + * full receive packet. Enough space is reserved in the + * receive FIFO for the status is written. This MPDU remaining + * packets in the PPDU will be filtered and no Ack response + * will be transmitted. + * + * last_mpdu + * Indicates that this is the last MPDU of a PPDU. + * + * post_delim_err + * Indicates that a delimiter FCS error occurred after this + * MPDU before the next MPDU. Only valid when last_msdu is + * set. + * + * post_delim_cnt + * Count of the delimiters after this MPDU. This requires the + * last MPDU to be held until all the EOF descriptors have been + * received. This may be inefficient in the future when + * ML-MIMO is used. Only valid when last_mpdu is set. + * + * mpdu_length_err + * See definition in RX attention descriptor + * + * tkip_mic_err + * See definition in RX attention descriptor + * + * decrypt_err + * See definition in RX attention descriptor + * + * fcs_err + * See definition in RX attention descriptor + */ + +#define RX_MSDU_START_INFO0_MSDU_LENGTH_MASK 0x00003fff +#define RX_MSDU_START_INFO0_MSDU_LENGTH_LSB 0 +#define RX_MSDU_START_INFO0_IP_OFFSET_MASK 0x000fc000 +#define RX_MSDU_START_INFO0_IP_OFFSET_LSB 14 +#define RX_MSDU_START_INFO0_RING_MASK_MASK 0x00f00000 +#define RX_MSDU_START_INFO0_RING_MASK_LSB 20 +#define RX_MSDU_START_INFO0_TCP_UDP_OFFSET_MASK 0x7f000000 +#define RX_MSDU_START_INFO0_TCP_UDP_OFFSET_LSB 24 + +#define RX_MSDU_START_INFO1_MSDU_NUMBER_MASK 0x000000ff +#define RX_MSDU_START_INFO1_MSDU_NUMBER_LSB 0 +#define RX_MSDU_START_INFO1_DECAP_FORMAT_MASK 0x00000300 +#define RX_MSDU_START_INFO1_DECAP_FORMAT_LSB 8 +#define RX_MSDU_START_INFO1_SA_IDX_MASK 0x07ff0000 +#define RX_MSDU_START_INFO1_SA_IDX_LSB 16 +#define RX_MSDU_START_INFO1_IPV4_PROTO (1 << 10) +#define RX_MSDU_START_INFO1_IPV6_PROTO (1 << 11) +#define RX_MSDU_START_INFO1_TCP_PROTO (1 << 12) +#define RX_MSDU_START_INFO1_UDP_PROTO (1 << 13) +#define RX_MSDU_START_INFO1_IP_FRAG (1 << 14) +#define RX_MSDU_START_INFO1_TCP_ONLY_ACK (1 << 15) + +enum rx_msdu_decap_format { + RX_MSDU_DECAP_RAW = 0, + RX_MSDU_DECAP_NATIVE_WIFI = 1, + RX_MSDU_DECAP_ETHERNET2_DIX = 2, + RX_MSDU_DECAP_8023_SNAP_LLC = 3 +}; + +struct rx_msdu_start { + __le32 info0; /* %RX_MSDU_START_INFO0_ */ + __le32 flow_id_crc; + __le32 info1; /* %RX_MSDU_START_INFO1_ */ +} __packed; + +/* + * msdu_length + * MSDU length in bytes after decapsulation. This field is + * still valid for MPDU frames without A-MSDU. It still + * represents MSDU length after decapsulation + * + * ip_offset + * Indicates the IP offset in bytes from the start of the + * packet after decapsulation. Only valid if ipv4_proto or + * ipv6_proto is set. + * + * ring_mask + * Indicates the destination RX rings for this MSDU. + * + * tcp_udp_offset + * Indicates the offset in bytes to the start of TCP or UDP + * header from the start of the IP header after decapsulation. + * Only valid if tcp_prot or udp_prot is set. The value 0 + * indicates that the offset is longer than 127 bytes. + * + * reserved_0c + * Reserved: HW should fill with zero. FW should ignore. + * + * flow_id_crc + * The flow_id_crc runs CRC32 on the following information: + * IPv4 option: dest_addr[31:0], src_addr [31:0], {24'b0, + * protocol[7:0]}. + * IPv6 option: dest_addr[127:0], src_addr [127:0], {24'b0, + * next_header[7:0]} + * UDP case: sort_port[15:0], dest_port[15:0] + * TCP case: sort_port[15:0], dest_port[15:0], + * {header_length[3:0], 6'b0, flags[5:0], window_size[15:0]}, + * {16'b0, urgent_ptr[15:0]}, all options except 32-bit + * timestamp. + * + * msdu_number + * Indicates the MSDU number within a MPDU. This value is + * reset to zero at the start of each MPDU. If the number of + * MSDU exceeds 255 this number will wrap using modulo 256. + * + * decap_format + * Indicates the format after decapsulation: + * 0: RAW: No decapsulation + * 1: Native WiFi + * 2: Ethernet 2 (DIX) + * 3: 802.3 (SNAP/LLC) + * + * ipv4_proto + * Set if L2 layer indicates IPv4 protocol. + * + * ipv6_proto + * Set if L2 layer indicates IPv6 protocol. + * + * tcp_proto + * Set if the ipv4_proto or ipv6_proto are set and the IP + * protocol indicates TCP. + * + * udp_proto + * Set if the ipv4_proto or ipv6_proto are set and the IP + * protocol indicates UDP. + * + * ip_frag + * Indicates that either the IP More frag bit is set or IP frag + * number is non-zero. If set indicates that this is a + * fragmented IP packet. + * + * tcp_only_ack + * Set if only the TCP Ack bit is set in the TCP flags and if + * the TCP payload is 0. + * + * sa_idx + * The offset in the address table which matches the MAC source + * address. + * + * reserved_2b + * Reserved: HW should fill with zero. FW should ignore. + */ + +#define RX_MSDU_END_INFO0_REPORTED_MPDU_LENGTH_MASK 0x00003fff +#define RX_MSDU_END_INFO0_REPORTED_MPDU_LENGTH_LSB 0 +#define RX_MSDU_END_INFO0_FIRST_MSDU (1 << 14) +#define RX_MSDU_END_INFO0_LAST_MSDU (1 << 15) +#define RX_MSDU_END_INFO0_PRE_DELIM_ERR (1 << 30) +#define RX_MSDU_END_INFO0_RESERVED_3B (1 << 31) + +struct rx_msdu_end { + __le16 ip_hdr_cksum; + __le16 tcp_hdr_cksum; + u8 key_id_octet; + u8 classification_filter; + u8 wapi_pn[10]; + __le32 info0; +} __packed; + +/* + *ip_hdr_chksum + * This can include the IP header checksum or the pseudo header + * checksum used by TCP/UDP checksum. + * + *tcp_udp_chksum + * The value of the computed TCP/UDP checksum. A mode bit + * selects whether this checksum is the full checksum or the + * partial checksum which does not include the pseudo header. + * + *key_id_octet + * The key ID octet from the IV. Only valid when first_msdu is + * set. + * + *classification_filter + * Indicates the number classification filter rule + * + *ext_wapi_pn_63_48 + * Extension PN (packet number) which is only used by WAPI. + * This corresponds to WAPI PN bits [63:48] (pn6 and pn7). The + * WAPI PN bits [63:0] are in the pn field of the rx_mpdu_start + * descriptor. + * + *ext_wapi_pn_95_64 + * Extension PN (packet number) which is only used by WAPI. + * This corresponds to WAPI PN bits [95:64] (pn8, pn9, pn10 and + * pn11). + * + *ext_wapi_pn_127_96 + * Extension PN (packet number) which is only used by WAPI. + * This corresponds to WAPI PN bits [127:96] (pn12, pn13, pn14, + * pn15). + * + *reported_mpdu_length + * MPDU length before decapsulation. Only valid when + * first_msdu is set. This field is taken directly from the + * length field of the A-MPDU delimiter or the preamble length + * field for non-A-MPDU frames. + * + *first_msdu + * Indicates the first MSDU of A-MSDU. If both first_msdu and + * last_msdu are set in the MSDU then this is a non-aggregated + * MSDU frame: normal MPDU. Interior MSDU in an A-MSDU shall + * have both first_mpdu and last_mpdu bits set to 0. + * + *last_msdu + * Indicates the last MSDU of the A-MSDU. MPDU end status is + * only valid when last_msdu is set. + * + *reserved_3a + * Reserved: HW should fill with zero. FW should ignore. + * + *pre_delim_err + * Indicates that the first delimiter had a FCS failure. Only + * valid when first_mpdu and first_msdu are set. + * + *reserved_3b + * Reserved: HW should fill with zero. FW should ignore. + */ + +#define RX_PPDU_START_SIG_RATE_SELECT_OFDM 0 +#define RX_PPDU_START_SIG_RATE_SELECT_CCK 1 + +#define RX_PPDU_START_SIG_RATE_OFDM_48 0 +#define RX_PPDU_START_SIG_RATE_OFDM_24 1 +#define RX_PPDU_START_SIG_RATE_OFDM_12 2 +#define RX_PPDU_START_SIG_RATE_OFDM_6 3 +#define RX_PPDU_START_SIG_RATE_OFDM_54 4 +#define RX_PPDU_START_SIG_RATE_OFDM_36 5 +#define RX_PPDU_START_SIG_RATE_OFDM_18 6 +#define RX_PPDU_START_SIG_RATE_OFDM_9 7 + +#define RX_PPDU_START_SIG_RATE_CCK_LP_11 0 +#define RX_PPDU_START_SIG_RATE_CCK_LP_5_5 1 +#define RX_PPDU_START_SIG_RATE_CCK_LP_2 2 +#define RX_PPDU_START_SIG_RATE_CCK_LP_1 3 +#define RX_PPDU_START_SIG_RATE_CCK_SP_11 4 +#define RX_PPDU_START_SIG_RATE_CCK_SP_5_5 5 +#define RX_PPDU_START_SIG_RATE_CCK_SP_2 6 + +#define HTT_RX_PPDU_START_PREAMBLE_LEGACY 0x04 +#define HTT_RX_PPDU_START_PREAMBLE_HT 0x08 +#define HTT_RX_PPDU_START_PREAMBLE_HT_WITH_TXBF 0x09 +#define HTT_RX_PPDU_START_PREAMBLE_VHT 0x0C +#define HTT_RX_PPDU_START_PREAMBLE_VHT_WITH_TXBF 0x0D + +#define RX_PPDU_START_INFO0_IS_GREENFIELD (1 << 0) + +#define RX_PPDU_START_INFO1_L_SIG_RATE_MASK 0x0000000f +#define RX_PPDU_START_INFO1_L_SIG_RATE_LSB 0 +#define RX_PPDU_START_INFO1_L_SIG_LENGTH_MASK 0x0001ffe0 +#define RX_PPDU_START_INFO1_L_SIG_LENGTH_LSB 5 +#define RX_PPDU_START_INFO1_L_SIG_TAIL_MASK 0x00fc0000 +#define RX_PPDU_START_INFO1_L_SIG_TAIL_LSB 18 +#define RX_PPDU_START_INFO1_PREAMBLE_TYPE_MASK 0xff000000 +#define RX_PPDU_START_INFO1_PREAMBLE_TYPE_LSB 24 +#define RX_PPDU_START_INFO1_L_SIG_RATE_SELECT (1 << 4) +#define RX_PPDU_START_INFO1_L_SIG_PARITY (1 << 17) + +#define RX_PPDU_START_INFO2_HT_SIG_VHT_SIG_A_1_MASK 0x00ffffff +#define RX_PPDU_START_INFO2_HT_SIG_VHT_SIG_A_1_LSB 0 + +#define RX_PPDU_START_INFO3_HT_SIG_VHT_SIG_A_2_MASK 0x00ffffff +#define RX_PPDU_START_INFO3_HT_SIG_VHT_SIG_A_2_LSB 0 +#define RX_PPDU_START_INFO3_TXBF_H_INFO (1 << 24) + +#define RX_PPDU_START_INFO4_VHT_SIG_B_MASK 0x1fffffff +#define RX_PPDU_START_INFO4_VHT_SIG_B_LSB 0 + +#define RX_PPDU_START_INFO5_SERVICE_MASK 0x0000ffff +#define RX_PPDU_START_INFO5_SERVICE_LSB 0 + +struct rx_ppdu_start { + struct { + u8 pri20_mhz; + u8 ext20_mhz; + u8 ext40_mhz; + u8 ext80_mhz; + } rssi_chains[4]; + u8 rssi_comb; + __le16 rsvd0; + u8 info0; /* %RX_PPDU_START_INFO0_ */ + __le32 info1; /* %RX_PPDU_START_INFO1_ */ + __le32 info2; /* %RX_PPDU_START_INFO2_ */ + __le32 info3; /* %RX_PPDU_START_INFO3_ */ + __le32 info4; /* %RX_PPDU_START_INFO4_ */ + __le32 info5; /* %RX_PPDU_START_INFO5_ */ +} __packed; + +/* + * rssi_chain0_pri20 + * RSSI of RX PPDU on chain 0 of primary 20 MHz bandwidth. + * Value of 0x80 indicates invalid. + * + * rssi_chain0_sec20 + * RSSI of RX PPDU on chain 0 of secondary 20 MHz bandwidth. + * Value of 0x80 indicates invalid. + * + * rssi_chain0_sec40 + * RSSI of RX PPDU on chain 0 of secondary 40 MHz bandwidth. + * Value of 0x80 indicates invalid. + * + * rssi_chain0_sec80 + * RSSI of RX PPDU on chain 0 of secondary 80 MHz bandwidth. + * Value of 0x80 indicates invalid. + * + * rssi_chain1_pri20 + * RSSI of RX PPDU on chain 1 of primary 20 MHz bandwidth. + * Value of 0x80 indicates invalid. + * + * rssi_chain1_sec20 + * RSSI of RX PPDU on chain 1 of secondary 20 MHz bandwidth. + * Value of 0x80 indicates invalid. + * + * rssi_chain1_sec40 + * RSSI of RX PPDU on chain 1 of secondary 40 MHz bandwidth. + * Value of 0x80 indicates invalid. + * + * rssi_chain1_sec80 + * RSSI of RX PPDU on chain 1 of secondary 80 MHz bandwidth. + * Value of 0x80 indicates invalid. + * + * rssi_chain2_pri20 + * RSSI of RX PPDU on chain 2 of primary 20 MHz bandwidth. + * Value of 0x80 indicates invalid. + * + * rssi_chain2_sec20 + * RSSI of RX PPDU on chain 2 of secondary 20 MHz bandwidth. + * Value of 0x80 indicates invalid. + * + * rssi_chain2_sec40 + * RSSI of RX PPDU on chain 2 of secondary 40 MHz bandwidth. + * Value of 0x80 indicates invalid. + * + * rssi_chain2_sec80 + * RSSI of RX PPDU on chain 2 of secondary 80 MHz bandwidth. + * Value of 0x80 indicates invalid. + * + * rssi_chain3_pri20 + * RSSI of RX PPDU on chain 3 of primary 20 MHz bandwidth. + * Value of 0x80 indicates invalid. + * + * rssi_chain3_sec20 + * RSSI of RX PPDU on chain 3 of secondary 20 MHz bandwidth. + * Value of 0x80 indicates invalid. + * + * rssi_chain3_sec40 + * RSSI of RX PPDU on chain 3 of secondary 40 MHz bandwidth. + * Value of 0x80 indicates invalid. + * + * rssi_chain3_sec80 + * RSSI of RX PPDU on chain 3 of secondary 80 MHz bandwidth. + * Value of 0x80 indicates invalid. + * + * rssi_comb + * The combined RSSI of RX PPDU of all active chains and + * bandwidths. Value of 0x80 indicates invalid. + * + * reserved_4a + * Reserved: HW should fill with 0, FW should ignore. + * + * is_greenfield + * Do we really support this? + * + * reserved_4b + * Reserved: HW should fill with 0, FW should ignore. + * + * l_sig_rate + * If l_sig_rate_select is 0: + * 0x8: OFDM 48 Mbps + * 0x9: OFDM 24 Mbps + * 0xA: OFDM 12 Mbps + * 0xB: OFDM 6 Mbps + * 0xC: OFDM 54 Mbps + * 0xD: OFDM 36 Mbps + * 0xE: OFDM 18 Mbps + * 0xF: OFDM 9 Mbps + * If l_sig_rate_select is 1: + * 0x8: CCK 11 Mbps long preamble + * 0x9: CCK 5.5 Mbps long preamble + * 0xA: CCK 2 Mbps long preamble + * 0xB: CCK 1 Mbps long preamble + * 0xC: CCK 11 Mbps short preamble + * 0xD: CCK 5.5 Mbps short preamble + * 0xE: CCK 2 Mbps short preamble + * + * l_sig_rate_select + * Legacy signal rate select. If set then l_sig_rate indicates + * CCK rates. If clear then l_sig_rate indicates OFDM rates. + * + * l_sig_length + * Length of legacy frame in octets. + * + * l_sig_parity + * Odd parity over l_sig_rate and l_sig_length + * + * l_sig_tail + * Tail bits for Viterbi decoder + * + * preamble_type + * Indicates the type of preamble ahead: + * 0x4: Legacy (OFDM/CCK) + * 0x8: HT + * 0x9: HT with TxBF + * 0xC: VHT + * 0xD: VHT with TxBF + * 0x80 - 0xFF: Reserved for special baseband data types such + * as radar and spectral scan. + * + * ht_sig_vht_sig_a_1 + * If preamble_type == 0x8 or 0x9 + * HT-SIG (first 24 bits) + * If preamble_type == 0xC or 0xD + * VHT-SIG A (first 24 bits) + * Else + * Reserved + * + * reserved_6 + * Reserved: HW should fill with 0, FW should ignore. + * + * ht_sig_vht_sig_a_2 + * If preamble_type == 0x8 or 0x9 + * HT-SIG (last 24 bits) + * If preamble_type == 0xC or 0xD + * VHT-SIG A (last 24 bits) + * Else + * Reserved + * + * txbf_h_info + * Indicates that the packet data carries H information which + * is used for TxBF debug. + * + * reserved_7 + * Reserved: HW should fill with 0, FW should ignore. + * + * vht_sig_b + * WiFi 1.0 and WiFi 2.0 will likely have this field to be all + * 0s since the BB does not plan on decoding VHT SIG-B. + * + * reserved_8 + * Reserved: HW should fill with 0, FW should ignore. + * + * service + * Service field from BB for OFDM, HT and VHT packets. CCK + * packets will have service field of 0. + * + * reserved_9 + * Reserved: HW should fill with 0, FW should ignore. +*/ + + +#define RX_PPDU_END_FLAGS_PHY_ERR (1 << 0) +#define RX_PPDU_END_FLAGS_RX_LOCATION (1 << 1) +#define RX_PPDU_END_FLAGS_TXBF_H_INFO (1 << 2) + +#define RX_PPDU_END_INFO0_RX_ANTENNA_MASK 0x00ffffff +#define RX_PPDU_END_INFO0_RX_ANTENNA_LSB 0 +#define RX_PPDU_END_INFO0_FLAGS_TX_HT_VHT_ACK (1 << 24) +#define RX_PPDU_END_INFO0_BB_CAPTURED_CHANNEL (1 << 25) + +#define RX_PPDU_END_INFO1_PPDU_DONE (1 << 15) + +struct rx_ppdu_end { + __le32 evm_p0; + __le32 evm_p1; + __le32 evm_p2; + __le32 evm_p3; + __le32 evm_p4; + __le32 evm_p5; + __le32 evm_p6; + __le32 evm_p7; + __le32 evm_p8; + __le32 evm_p9; + __le32 evm_p10; + __le32 evm_p11; + __le32 evm_p12; + __le32 evm_p13; + __le32 evm_p14; + __le32 evm_p15; + __le32 tsf_timestamp; + __le32 wb_timestamp; + u8 locationing_timestamp; + u8 phy_err_code; + __le16 flags; /* %RX_PPDU_END_FLAGS_ */ + __le32 info0; /* %RX_PPDU_END_INFO0_ */ + __le16 bb_length; + __le16 info1; /* %RX_PPDU_END_INFO1_ */ +} __packed; + +/* + * evm_p0 + * EVM for pilot 0. Contain EVM for streams: 0, 1, 2 and 3. + * + * evm_p1 + * EVM for pilot 1. Contain EVM for streams: 0, 1, 2 and 3. + * + * evm_p2 + * EVM for pilot 2. Contain EVM for streams: 0, 1, 2 and 3. + * + * evm_p3 + * EVM for pilot 3. Contain EVM for streams: 0, 1, 2 and 3. + * + * evm_p4 + * EVM for pilot 4. Contain EVM for streams: 0, 1, 2 and 3. + * + * evm_p5 + * EVM for pilot 5. Contain EVM for streams: 0, 1, 2 and 3. + * + * evm_p6 + * EVM for pilot 6. Contain EVM for streams: 0, 1, 2 and 3. + * + * evm_p7 + * EVM for pilot 7. Contain EVM for streams: 0, 1, 2 and 3. + * + * evm_p8 + * EVM for pilot 8. Contain EVM for streams: 0, 1, 2 and 3. + * + * evm_p9 + * EVM for pilot 9. Contain EVM for streams: 0, 1, 2 and 3. + * + * evm_p10 + * EVM for pilot 10. Contain EVM for streams: 0, 1, 2 and 3. + * + * evm_p11 + * EVM for pilot 11. Contain EVM for streams: 0, 1, 2 and 3. + * + * evm_p12 + * EVM for pilot 12. Contain EVM for streams: 0, 1, 2 and 3. + * + * evm_p13 + * EVM for pilot 13. Contain EVM for streams: 0, 1, 2 and 3. + * + * evm_p14 + * EVM for pilot 14. Contain EVM for streams: 0, 1, 2 and 3. + * + * evm_p15 + * EVM for pilot 15. Contain EVM for streams: 0, 1, 2 and 3. + * + * tsf_timestamp + * Receive TSF timestamp sampled on the rising edge of + * rx_clear. For PHY errors this may be the current TSF when + * phy_error is asserted if the rx_clear does not assert before + * the end of the PHY error. + * + * wb_timestamp + * WLAN/BT timestamp is a 1 usec resolution timestamp which + * does not get updated based on receive beacon like TSF. The + * same rules for capturing tsf_timestamp are used to capture + * the wb_timestamp. + * + * locationing_timestamp + * Timestamp used for locationing. This timestamp is used to + * indicate fractions of usec. For example if the MAC clock is + * running at 80 MHz, the timestamp will increment every 12.5 + * nsec. The value starts at 0 and increments to 79 and + * returns to 0 and repeats. This information is valid for + * every PPDU. This information can be used in conjunction + * with wb_timestamp to capture large delta times. + * + * phy_err_code + * See the 1.10.8.1.2 for the list of the PHY error codes. + * + * phy_err + * Indicates a PHY error was detected for this PPDU. + * + * rx_location + * Indicates that location information was requested. + * + * txbf_h_info + * Indicates that the packet data carries H information which + * is used for TxBF debug. + * + * reserved_18 + * Reserved: HW should fill with 0, FW should ignore. + * + * rx_antenna + * Receive antenna value + * + * tx_ht_vht_ack + * Indicates that a HT or VHT Ack/BA frame was transmitted in + * response to this receive packet. + * + * bb_captured_channel + * Indicates that the BB has captured a channel dump. FW can + * then read the channel dump memory. This may indicate that + * the channel was captured either based on PCU setting the + * capture_channel bit BB descriptor or FW setting the + * capture_channel mode bit. + * + * reserved_19 + * Reserved: HW should fill with 0, FW should ignore. + * + * bb_length + * Indicates the number of bytes of baseband information for + * PPDUs where the BB descriptor preamble type is 0x80 to 0xFF + * which indicates that this is not a normal PPDU but rather + * contains baseband debug information. + * + * reserved_20 + * Reserved: HW should fill with 0, FW should ignore. + * + * ppdu_done + * PPDU end status is only valid when ppdu_done bit is set. + * Every time HW sets this bit in memory FW/SW must clear this + * bit in memory. FW will initialize all the ppdu_done dword + * to 0. +*/ + +#define FW_RX_DESC_INFO0_DISCARD (1 << 0) +#define FW_RX_DESC_INFO0_FORWARD (1 << 1) +#define FW_RX_DESC_INFO0_INSPECT (1 << 5) +#define FW_RX_DESC_INFO0_EXT_MASK 0xC0 +#define FW_RX_DESC_INFO0_EXT_LSB 6 + +struct fw_rx_desc_base { + u8 info0; +} __packed; + +#endif /* _RX_DESC_H_ */ diff --git a/drivers/net/wireless/ath/ath10k/targaddrs.h b/drivers/net/wireless/ath/ath10k/targaddrs.h new file mode 100644 index 000000000000..be7ba1e78afe --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/targaddrs.h @@ -0,0 +1,449 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __TARGADDRS_H__ +#define __TARGADDRS_H__ + +/* + * xxx_HOST_INTEREST_ADDRESS is the address in Target RAM of the + * host_interest structure. It must match the address of the _host_interest + * symbol (see linker script). + * + * Host Interest is shared between Host and Target in order to coordinate + * between the two, and is intended to remain constant (with additions only + * at the end) across software releases. + * + * All addresses are available here so that it's possible to + * write a single binary that works with all Target Types. + * May be used in assembler code as well as C. + */ +#define QCA988X_HOST_INTEREST_ADDRESS 0x00400800 +#define HOST_INTEREST_MAX_SIZE 0x200 + +/* + * These are items that the Host may need to access via BMI or via the + * Diagnostic Window. The position of items in this structure must remain + * constant across firmware revisions! Types for each item must be fixed + * size across target and host platforms. More items may be added at the end. + */ +struct host_interest { + /* + * Pointer to application-defined area, if any. + * Set by Target application during startup. + */ + u32 hi_app_host_interest; /* 0x00 */ + + /* Pointer to register dump area, valid after Target crash. */ + u32 hi_failure_state; /* 0x04 */ + + /* Pointer to debug logging header */ + u32 hi_dbglog_hdr; /* 0x08 */ + + u32 hi_unused0c; /* 0x0c */ + + /* + * General-purpose flag bits, similar to SOC_OPTION_* flags. + * Can be used by application rather than by OS. + */ + u32 hi_option_flag; /* 0x10 */ + + /* + * Boolean that determines whether or not to + * display messages on the serial port. + */ + u32 hi_serial_enable; /* 0x14 */ + + /* Start address of DataSet index, if any */ + u32 hi_dset_list_head; /* 0x18 */ + + /* Override Target application start address */ + u32 hi_app_start; /* 0x1c */ + + /* Clock and voltage tuning */ + u32 hi_skip_clock_init; /* 0x20 */ + u32 hi_core_clock_setting; /* 0x24 */ + u32 hi_cpu_clock_setting; /* 0x28 */ + u32 hi_system_sleep_setting; /* 0x2c */ + u32 hi_xtal_control_setting; /* 0x30 */ + u32 hi_pll_ctrl_setting_24ghz; /* 0x34 */ + u32 hi_pll_ctrl_setting_5ghz; /* 0x38 */ + u32 hi_ref_voltage_trim_setting; /* 0x3c */ + u32 hi_clock_info; /* 0x40 */ + + /* Host uses BE CPU or not */ + u32 hi_be; /* 0x44 */ + + u32 hi_stack; /* normal stack */ /* 0x48 */ + u32 hi_err_stack; /* error stack */ /* 0x4c */ + u32 hi_desired_cpu_speed_hz; /* 0x50 */ + + /* Pointer to Board Data */ + u32 hi_board_data; /* 0x54 */ + + /* + * Indication of Board Data state: + * 0: board data is not yet initialized. + * 1: board data is initialized; unknown size + * >1: number of bytes of initialized board data + */ + u32 hi_board_data_initialized; /* 0x58 */ + + u32 hi_dset_ram_index_table; /* 0x5c */ + + u32 hi_desired_baud_rate; /* 0x60 */ + u32 hi_dbglog_config; /* 0x64 */ + u32 hi_end_ram_reserve_sz; /* 0x68 */ + u32 hi_mbox_io_block_sz; /* 0x6c */ + + u32 hi_num_bpatch_streams; /* 0x70 -- unused */ + u32 hi_mbox_isr_yield_limit; /* 0x74 */ + + u32 hi_refclk_hz; /* 0x78 */ + u32 hi_ext_clk_detected; /* 0x7c */ + u32 hi_dbg_uart_txpin; /* 0x80 */ + u32 hi_dbg_uart_rxpin; /* 0x84 */ + u32 hi_hci_uart_baud; /* 0x88 */ + u32 hi_hci_uart_pin_assignments; /* 0x8C */ + + u32 hi_hci_uart_baud_scale_val; /* 0x90 */ + u32 hi_hci_uart_baud_step_val; /* 0x94 */ + + u32 hi_allocram_start; /* 0x98 */ + u32 hi_allocram_sz; /* 0x9c */ + u32 hi_hci_bridge_flags; /* 0xa0 */ + u32 hi_hci_uart_support_pins; /* 0xa4 */ + + u32 hi_hci_uart_pwr_mgmt_params; /* 0xa8 */ + + /* + * 0xa8 - [1]: 0 = UART FC active low, 1 = UART FC active high + * [31:16]: wakeup timeout in ms + */ + /* Pointer to extended board Data */ + u32 hi_board_ext_data; /* 0xac */ + u32 hi_board_ext_data_config; /* 0xb0 */ + /* + * Bit [0] : valid + * Bit[31:16: size + */ + /* + * hi_reset_flag is used to do some stuff when target reset. + * such as restore app_start after warm reset or + * preserve host Interest area, or preserve ROM data, literals etc. + */ + u32 hi_reset_flag; /* 0xb4 */ + /* indicate hi_reset_flag is valid */ + u32 hi_reset_flag_valid; /* 0xb8 */ + u32 hi_hci_uart_pwr_mgmt_params_ext; /* 0xbc */ + /* 0xbc - [31:0]: idle timeout in ms */ + /* ACS flags */ + u32 hi_acs_flags; /* 0xc0 */ + u32 hi_console_flags; /* 0xc4 */ + u32 hi_nvram_state; /* 0xc8 */ + u32 hi_option_flag2; /* 0xcc */ + + /* If non-zero, override values sent to Host in WMI_READY event. */ + u32 hi_sw_version_override; /* 0xd0 */ + u32 hi_abi_version_override; /* 0xd4 */ + + /* + * Percentage of high priority RX traffic to total expected RX traffic + * applicable only to ar6004 + */ + u32 hi_hp_rx_traffic_ratio; /* 0xd8 */ + + /* test applications flags */ + u32 hi_test_apps_related; /* 0xdc */ + /* location of test script */ + u32 hi_ota_testscript; /* 0xe0 */ + /* location of CAL data */ + u32 hi_cal_data; /* 0xe4 */ + + /* Number of packet log buffers */ + u32 hi_pktlog_num_buffers; /* 0xe8 */ + + /* wow extension configuration */ + u32 hi_wow_ext_config; /* 0xec */ + u32 hi_pwr_save_flags; /* 0xf0 */ + + /* Spatial Multiplexing Power Save (SMPS) options */ + u32 hi_smps_options; /* 0xf4 */ + + /* Interconnect-specific state */ + u32 hi_interconnect_state; /* 0xf8 */ + + /* Coex configuration flags */ + u32 hi_coex_config; /* 0xfc */ + + /* Early allocation support */ + u32 hi_early_alloc; /* 0x100 */ + /* FW swap field */ + /* + * Bits of this 32bit word will be used to pass specific swap + * instruction to FW + */ + /* + * Bit 0 -- AP Nart descriptor no swap. When this bit is set + * FW will not swap TX descriptor. Meaning packets are formed + * on the target processor. + */ + /* Bit 1 - unused */ + u32 hi_fw_swap; /* 0x104 */ +} __packed; + +#define HI_ITEM(item) offsetof(struct host_interest, item) + +/* Bits defined in hi_option_flag */ + +/* Enable timer workaround */ +#define HI_OPTION_TIMER_WAR 0x01 +/* Limit BMI command credits */ +#define HI_OPTION_BMI_CRED_LIMIT 0x02 +/* Relay Dot11 hdr to/from host */ +#define HI_OPTION_RELAY_DOT11_HDR 0x04 +/* MAC addr method 0-locally administred 1-globally unique addrs */ +#define HI_OPTION_MAC_ADDR_METHOD 0x08 +/* Firmware Bridging */ +#define HI_OPTION_FW_BRIDGE 0x10 +/* Enable CPU profiling */ +#define HI_OPTION_ENABLE_PROFILE 0x20 +/* Disable debug logging */ +#define HI_OPTION_DISABLE_DBGLOG 0x40 +/* Skip Era Tracking */ +#define HI_OPTION_SKIP_ERA_TRACKING 0x80 +/* Disable PAPRD (debug) */ +#define HI_OPTION_PAPRD_DISABLE 0x100 +#define HI_OPTION_NUM_DEV_LSB 0x200 +#define HI_OPTION_NUM_DEV_MSB 0x800 +#define HI_OPTION_DEV_MODE_LSB 0x1000 +#define HI_OPTION_DEV_MODE_MSB 0x8000000 +/* Disable LowFreq Timer Stabilization */ +#define HI_OPTION_NO_LFT_STBL 0x10000000 +/* Skip regulatory scan */ +#define HI_OPTION_SKIP_REG_SCAN 0x20000000 +/* + * Do regulatory scan during init before + * sending WMI ready event to host + */ +#define HI_OPTION_INIT_REG_SCAN 0x40000000 + +/* REV6: Do not adjust memory map */ +#define HI_OPTION_SKIP_MEMMAP 0x80000000 + +#define HI_OPTION_MAC_ADDR_METHOD_SHIFT 3 + +/* 2 bits of hi_option_flag are used to represent 3 modes */ +#define HI_OPTION_FW_MODE_IBSS 0x0 /* IBSS Mode */ +#define HI_OPTION_FW_MODE_BSS_STA 0x1 /* STA Mode */ +#define HI_OPTION_FW_MODE_AP 0x2 /* AP Mode */ +#define HI_OPTION_FW_MODE_BT30AMP 0x3 /* BT30 AMP Mode */ + +/* 2 bits of hi_option flag are usedto represent 4 submodes */ +#define HI_OPTION_FW_SUBMODE_NONE 0x0 /* Normal mode */ +#define HI_OPTION_FW_SUBMODE_P2PDEV 0x1 /* p2p device mode */ +#define HI_OPTION_FW_SUBMODE_P2PCLIENT 0x2 /* p2p client mode */ +#define HI_OPTION_FW_SUBMODE_P2PGO 0x3 /* p2p go mode */ + +/* Num dev Mask */ +#define HI_OPTION_NUM_DEV_MASK 0x7 +#define HI_OPTION_NUM_DEV_SHIFT 0x9 + +/* firmware bridging */ +#define HI_OPTION_FW_BRIDGE_SHIFT 0x04 + +/* +Fw Mode/SubMode Mask +|-----------------------------------------------------------------------------| +| SUB | SUB | SUB | SUB | | | | | +|MODE[3] | MODE[2] | MODE[1] | MODE[0] | MODE[3] | MODE[2] | MODE[1] | MODE[0]| +| (2) | (2) | (2) | (2) | (2) | (2) | (2) | (2) | +|-----------------------------------------------------------------------------| +*/ +#define HI_OPTION_FW_MODE_BITS 0x2 +#define HI_OPTION_FW_MODE_MASK 0x3 +#define HI_OPTION_FW_MODE_SHIFT 0xC +#define HI_OPTION_ALL_FW_MODE_MASK 0xFF + +#define HI_OPTION_FW_SUBMODE_BITS 0x2 +#define HI_OPTION_FW_SUBMODE_MASK 0x3 +#define HI_OPTION_FW_SUBMODE_SHIFT 0x14 +#define HI_OPTION_ALL_FW_SUBMODE_MASK 0xFF00 +#define HI_OPTION_ALL_FW_SUBMODE_SHIFT 0x8 + + +/* hi_option_flag2 options */ +#define HI_OPTION_OFFLOAD_AMSDU 0x01 +#define HI_OPTION_DFS_SUPPORT 0x02 /* Enable DFS support */ +#define HI_OPTION_ENABLE_RFKILL 0x04 /* RFKill Enable Feature*/ +#define HI_OPTION_RADIO_RETENTION_DISABLE 0x08 /* Disable radio retention */ +#define HI_OPTION_EARLY_CFG_DONE 0x10 /* Early configuration is complete */ + +#define HI_OPTION_RF_KILL_SHIFT 0x2 +#define HI_OPTION_RF_KILL_MASK 0x1 + +/* hi_reset_flag */ +/* preserve App Start address */ +#define HI_RESET_FLAG_PRESERVE_APP_START 0x01 +/* preserve host interest */ +#define HI_RESET_FLAG_PRESERVE_HOST_INTEREST 0x02 +/* preserve ROM data */ +#define HI_RESET_FLAG_PRESERVE_ROMDATA 0x04 +#define HI_RESET_FLAG_PRESERVE_NVRAM_STATE 0x08 +#define HI_RESET_FLAG_PRESERVE_BOOT_INFO 0x10 +#define HI_RESET_FLAG_WARM_RESET 0x20 + +/* define hi_fw_swap bits */ +#define HI_DESC_IN_FW_BIT 0x01 + +/* indicate the reset flag is valid */ +#define HI_RESET_FLAG_IS_VALID 0x12345678 + +/* ACS is enabled */ +#define HI_ACS_FLAGS_ENABLED (1 << 0) +/* Use physical WWAN device */ +#define HI_ACS_FLAGS_USE_WWAN (1 << 1) +/* Use test VAP */ +#define HI_ACS_FLAGS_TEST_VAP (1 << 2) + +/* + * CONSOLE FLAGS + * + * Bit Range Meaning + * --------- -------------------------------- + * 2..0 UART ID (0 = Default) + * 3 Baud Select (0 = 9600, 1 = 115200) + * 30..4 Reserved + * 31 Enable Console + * + */ + +#define HI_CONSOLE_FLAGS_ENABLE (1 << 31) +#define HI_CONSOLE_FLAGS_UART_MASK (0x7) +#define HI_CONSOLE_FLAGS_UART_SHIFT 0 +#define HI_CONSOLE_FLAGS_BAUD_SELECT (1 << 3) + +/* SM power save options */ +#define HI_SMPS_ALLOW_MASK (0x00000001) +#define HI_SMPS_MODE_MASK (0x00000002) +#define HI_SMPS_MODE_STATIC (0x00000000) +#define HI_SMPS_MODE_DYNAMIC (0x00000002) +#define HI_SMPS_DISABLE_AUTO_MODE (0x00000004) +#define HI_SMPS_DATA_THRESH_MASK (0x000007f8) +#define HI_SMPS_DATA_THRESH_SHIFT (3) +#define HI_SMPS_RSSI_THRESH_MASK (0x0007f800) +#define HI_SMPS_RSSI_THRESH_SHIFT (11) +#define HI_SMPS_LOWPWR_CM_MASK (0x00380000) +#define HI_SMPS_LOWPWR_CM_SHIFT (15) +#define HI_SMPS_HIPWR_CM_MASK (0x03c00000) +#define HI_SMPS_HIPWR_CM_SHIFT (19) + +/* + * WOW Extension configuration + * + * Bit Range Meaning + * --------- -------------------------------- + * 8..0 Size of each WOW pattern (max 511) + * 15..9 Number of patterns per list (max 127) + * 17..16 Number of lists (max 4) + * 30..18 Reserved + * 31 Enabled + * + * set values (except enable) to zeros for default settings + */ + +#define HI_WOW_EXT_ENABLED_MASK (1 << 31) +#define HI_WOW_EXT_NUM_LIST_SHIFT 16 +#define HI_WOW_EXT_NUM_LIST_MASK (0x3 << HI_WOW_EXT_NUM_LIST_SHIFT) +#define HI_WOW_EXT_NUM_PATTERNS_SHIFT 9 +#define HI_WOW_EXT_NUM_PATTERNS_MASK (0x7F << HI_WOW_EXT_NUM_PATTERNS_SHIFT) +#define HI_WOW_EXT_PATTERN_SIZE_SHIFT 0 +#define HI_WOW_EXT_PATTERN_SIZE_MASK (0x1FF << HI_WOW_EXT_PATTERN_SIZE_SHIFT) + +#define HI_WOW_EXT_MAKE_CONFIG(num_lists, count, size) \ + ((((num_lists) << HI_WOW_EXT_NUM_LIST_SHIFT) & \ + HI_WOW_EXT_NUM_LIST_MASK) | \ + (((count) << HI_WOW_EXT_NUM_PATTERNS_SHIFT) & \ + HI_WOW_EXT_NUM_PATTERNS_MASK) | \ + (((size) << HI_WOW_EXT_PATTERN_SIZE_SHIFT) & \ + HI_WOW_EXT_PATTERN_SIZE_MASK)) + +#define HI_WOW_EXT_GET_NUM_LISTS(config) \ + (((config) & HI_WOW_EXT_NUM_LIST_MASK) >> HI_WOW_EXT_NUM_LIST_SHIFT) +#define HI_WOW_EXT_GET_NUM_PATTERNS(config) \ + (((config) & HI_WOW_EXT_NUM_PATTERNS_MASK) >> \ + HI_WOW_EXT_NUM_PATTERNS_SHIFT) +#define HI_WOW_EXT_GET_PATTERN_SIZE(config) \ + (((config) & HI_WOW_EXT_PATTERN_SIZE_MASK) >> \ + HI_WOW_EXT_PATTERN_SIZE_SHIFT) + +/* + * Early allocation configuration + * Support RAM bank configuration before BMI done and this eases the memory + * allocation at very early stage + * Bit Range Meaning + * --------- ---------------------------------- + * [0:3] number of bank assigned to be IRAM + * [4:15] reserved + * [16:31] magic number + * + * Note: + * 1. target firmware would check magic number and if it's a match, firmware + * would consider the bits[0:15] are valid and base on that to calculate + * the end of DRAM. Early allocation would be located at that area and + * may be reclaimed when necesary + * 2. if no magic number is found, early allocation would happen at "_end" + * symbol of ROM which is located before the app-data and might NOT be + * re-claimable. If this is adopted, link script should keep this in + * mind to avoid data corruption. + */ +#define HI_EARLY_ALLOC_MAGIC 0x6d8a +#define HI_EARLY_ALLOC_MAGIC_MASK 0xffff0000 +#define HI_EARLY_ALLOC_MAGIC_SHIFT 16 +#define HI_EARLY_ALLOC_IRAM_BANKS_MASK 0x0000000f +#define HI_EARLY_ALLOC_IRAM_BANKS_SHIFT 0 + +#define HI_EARLY_ALLOC_VALID() \ + ((((HOST_INTEREST->hi_early_alloc) & HI_EARLY_ALLOC_MAGIC_MASK) >> \ + HI_EARLY_ALLOC_MAGIC_SHIFT) == (HI_EARLY_ALLOC_MAGIC)) +#define HI_EARLY_ALLOC_GET_IRAM_BANKS() \ + (((HOST_INTEREST->hi_early_alloc) & HI_EARLY_ALLOC_IRAM_BANKS_MASK) \ + >> HI_EARLY_ALLOC_IRAM_BANKS_SHIFT) + +/*power save flag bit definitions*/ +#define HI_PWR_SAVE_LPL_ENABLED 0x1 +/*b1-b3 reserved*/ +/*b4-b5 : dev0 LPL type : 0 - none + 1- Reduce Pwr Search + 2- Reduce Pwr Listen*/ +/*b6-b7 : dev1 LPL type and so on for Max 8 devices*/ +#define HI_PWR_SAVE_LPL_DEV0_LSB 4 +#define HI_PWR_SAVE_LPL_DEV_MASK 0x3 +/*power save related utility macros*/ +#define HI_LPL_ENABLED() \ + ((HOST_INTEREST->hi_pwr_save_flags & HI_PWR_SAVE_LPL_ENABLED)) +#define HI_DEV_LPL_TYPE_GET(_devix) \ + (HOST_INTEREST->hi_pwr_save_flags & ((HI_PWR_SAVE_LPL_DEV_MASK) << \ + (HI_PWR_SAVE_LPL_DEV0_LSB + (_devix)*2))) + +#define HOST_INTEREST_SMPS_IS_ALLOWED() \ + ((HOST_INTEREST->hi_smps_options & HI_SMPS_ALLOW_MASK)) + +/* Reserve 1024 bytes for extended board data */ +#define QCA988X_BOARD_DATA_SZ 7168 +#define QCA988X_BOARD_EXT_DATA_SZ 0 + +#endif /* __TARGADDRS_H__ */ diff --git a/drivers/net/wireless/ath/ath10k/trace.c b/drivers/net/wireless/ath/ath10k/trace.c new file mode 100644 index 000000000000..4a31e2c6fbd4 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/trace.c @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#define CREATE_TRACE_POINTS +#include "trace.h" diff --git a/drivers/net/wireless/ath/ath10k/trace.h b/drivers/net/wireless/ath/ath10k/trace.h new file mode 100644 index 000000000000..85e806bf7257 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/trace.h @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ) + +#include + +#define _TRACE_H_ + +/* create empty functions when tracing is disabled */ +#if !defined(CONFIG_ATH10K_TRACING) +#undef TRACE_EVENT +#define TRACE_EVENT(name, proto, ...) \ +static inline void trace_ ## name(proto) {} +#undef DECLARE_EVENT_CLASS +#define DECLARE_EVENT_CLASS(...) +#undef DEFINE_EVENT +#define DEFINE_EVENT(evt_class, name, proto, ...) \ +static inline void trace_ ## name(proto) {} +#endif /* !CONFIG_ATH10K_TRACING || __CHECKER__ */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM ath10k + +#define ATH10K_MSG_MAX 200 + +DECLARE_EVENT_CLASS(ath10k_log_event, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf), + TP_STRUCT__entry( + __dynamic_array(char, msg, ATH10K_MSG_MAX) + ), + TP_fast_assign( + WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg), + ATH10K_MSG_MAX, + vaf->fmt, + *vaf->va) >= ATH10K_MSG_MAX); + ), + TP_printk("%s", __get_str(msg)) +); + +DEFINE_EVENT(ath10k_log_event, ath10k_log_err, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +DEFINE_EVENT(ath10k_log_event, ath10k_log_warn, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +DEFINE_EVENT(ath10k_log_event, ath10k_log_info, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +TRACE_EVENT(ath10k_log_dbg, + TP_PROTO(unsigned int level, struct va_format *vaf), + TP_ARGS(level, vaf), + TP_STRUCT__entry( + __field(unsigned int, level) + __dynamic_array(char, msg, ATH10K_MSG_MAX) + ), + TP_fast_assign( + __entry->level = level; + WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg), + ATH10K_MSG_MAX, + vaf->fmt, + *vaf->va) >= ATH10K_MSG_MAX); + ), + TP_printk("%s", __get_str(msg)) +); + +TRACE_EVENT(ath10k_log_dbg_dump, + TP_PROTO(const char *msg, const char *prefix, + const void *buf, size_t buf_len), + + TP_ARGS(msg, prefix, buf, buf_len), + + TP_STRUCT__entry( + __string(msg, msg) + __string(prefix, prefix) + __field(size_t, buf_len) + __dynamic_array(u8, buf, buf_len) + ), + + TP_fast_assign( + __assign_str(msg, msg); + __assign_str(prefix, prefix); + __entry->buf_len = buf_len; + memcpy(__get_dynamic_array(buf), buf, buf_len); + ), + + TP_printk( + "%s/%s\n", __get_str(prefix), __get_str(msg) + ) +); + +TRACE_EVENT(ath10k_wmi_cmd, + TP_PROTO(int id, void *buf, size_t buf_len), + + TP_ARGS(id, buf, buf_len), + + TP_STRUCT__entry( + __field(unsigned int, id) + __field(size_t, buf_len) + __dynamic_array(u8, buf, buf_len) + ), + + TP_fast_assign( + __entry->id = id; + __entry->buf_len = buf_len; + memcpy(__get_dynamic_array(buf), buf, buf_len); + ), + + TP_printk( + "id %d len %zu", + __entry->id, + __entry->buf_len + ) +); + +TRACE_EVENT(ath10k_wmi_event, + TP_PROTO(int id, void *buf, size_t buf_len), + + TP_ARGS(id, buf, buf_len), + + TP_STRUCT__entry( + __field(unsigned int, id) + __field(size_t, buf_len) + __dynamic_array(u8, buf, buf_len) + ), + + TP_fast_assign( + __entry->id = id; + __entry->buf_len = buf_len; + memcpy(__get_dynamic_array(buf), buf, buf_len); + ), + + TP_printk( + "id %d len %zu", + __entry->id, + __entry->buf_len + ) +); + +#endif /* _TRACE_H_ || TRACE_HEADER_MULTI_READ*/ + +/* we don't want to use include/trace/events */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE trace + +/* This part must be outside protection */ +#include diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c new file mode 100644 index 000000000000..68b6faefd1d8 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/txrx.c @@ -0,0 +1,417 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "core.h" +#include "txrx.h" +#include "htt.h" +#include "mac.h" +#include "debug.h" + +static void ath10k_report_offchan_tx(struct ath10k *ar, struct sk_buff *skb) +{ + if (!ATH10K_SKB_CB(skb)->htt.is_offchan) + return; + + /* If the original wait_for_completion() timed out before + * {data,mgmt}_tx_completed() was called then we could complete + * offchan_tx_completed for a different skb. Prevent this by using + * offchan_tx_skb. */ + spin_lock_bh(&ar->data_lock); + if (ar->offchan_tx_skb != skb) { + ath10k_warn("completed old offchannel frame\n"); + goto out; + } + + complete(&ar->offchan_tx_completed); + ar->offchan_tx_skb = NULL; /* just for sanity */ + + ath10k_dbg(ATH10K_DBG_HTT, "completed offchannel skb %p\n", skb); +out: + spin_unlock_bh(&ar->data_lock); +} + +void ath10k_txrx_tx_unref(struct ath10k_htt *htt, struct sk_buff *txdesc) +{ + struct device *dev = htt->ar->dev; + struct ieee80211_tx_info *info; + struct sk_buff *txfrag = ATH10K_SKB_CB(txdesc)->htt.txfrag; + struct sk_buff *msdu = ATH10K_SKB_CB(txdesc)->htt.msdu; + int ret; + + if (ATH10K_SKB_CB(txdesc)->htt.refcount == 0) + return; + + ATH10K_SKB_CB(txdesc)->htt.refcount--; + + if (ATH10K_SKB_CB(txdesc)->htt.refcount > 0) + return; + + if (txfrag) { + ret = ath10k_skb_unmap(dev, txfrag); + if (ret) + ath10k_warn("txfrag unmap failed (%d)\n", ret); + + dev_kfree_skb_any(txfrag); + } + + ret = ath10k_skb_unmap(dev, msdu); + if (ret) + ath10k_warn("data skb unmap failed (%d)\n", ret); + + ath10k_report_offchan_tx(htt->ar, msdu); + + info = IEEE80211_SKB_CB(msdu); + memset(&info->status, 0, sizeof(info->status)); + + if (ATH10K_SKB_CB(txdesc)->htt.discard) { + ieee80211_free_txskb(htt->ar->hw, msdu); + goto exit; + } + + if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) + info->flags |= IEEE80211_TX_STAT_ACK; + + if (ATH10K_SKB_CB(txdesc)->htt.no_ack) + info->flags &= ~IEEE80211_TX_STAT_ACK; + + ieee80211_tx_status(htt->ar->hw, msdu); + /* we do not own the msdu anymore */ + +exit: + spin_lock_bh(&htt->tx_lock); + htt->pending_tx[ATH10K_SKB_CB(txdesc)->htt.msdu_id] = NULL; + ath10k_htt_tx_free_msdu_id(htt, ATH10K_SKB_CB(txdesc)->htt.msdu_id); + __ath10k_htt_tx_dec_pending(htt); + if (bitmap_empty(htt->used_msdu_ids, htt->max_num_pending_tx)) + wake_up(&htt->empty_tx_wq); + spin_unlock_bh(&htt->tx_lock); + + dev_kfree_skb_any(txdesc); +} + +void ath10k_txrx_tx_completed(struct ath10k_htt *htt, + const struct htt_tx_done *tx_done) +{ + struct sk_buff *txdesc; + + ath10k_dbg(ATH10K_DBG_HTT, "htt tx completion msdu_id %u discard %d no_ack %d\n", + tx_done->msdu_id, !!tx_done->discard, !!tx_done->no_ack); + + if (tx_done->msdu_id >= htt->max_num_pending_tx) { + ath10k_warn("warning: msdu_id %d too big, ignoring\n", + tx_done->msdu_id); + return; + } + + txdesc = htt->pending_tx[tx_done->msdu_id]; + + ATH10K_SKB_CB(txdesc)->htt.discard = tx_done->discard; + ATH10K_SKB_CB(txdesc)->htt.no_ack = tx_done->no_ack; + + ath10k_txrx_tx_unref(htt, txdesc); +} + +static const u8 rx_legacy_rate_idx[] = { + 3, /* 0x00 - 11Mbps */ + 2, /* 0x01 - 5.5Mbps */ + 1, /* 0x02 - 2Mbps */ + 0, /* 0x03 - 1Mbps */ + 3, /* 0x04 - 11Mbps */ + 2, /* 0x05 - 5.5Mbps */ + 1, /* 0x06 - 2Mbps */ + 0, /* 0x07 - 1Mbps */ + 10, /* 0x08 - 48Mbps */ + 8, /* 0x09 - 24Mbps */ + 6, /* 0x0A - 12Mbps */ + 4, /* 0x0B - 6Mbps */ + 11, /* 0x0C - 54Mbps */ + 9, /* 0x0D - 36Mbps */ + 7, /* 0x0E - 18Mbps */ + 5, /* 0x0F - 9Mbps */ +}; + +static void process_rx_rates(struct ath10k *ar, struct htt_rx_info *info, + enum ieee80211_band band, + struct ieee80211_rx_status *status) +{ + u8 cck, rate, rate_idx, bw, sgi, mcs, nss; + u8 info0 = info->rate.info0; + u32 info1 = info->rate.info1; + u32 info2 = info->rate.info2; + u8 preamble = 0; + + /* Check if valid fields */ + if (!(info0 & HTT_RX_INDICATION_INFO0_START_VALID)) + return; + + preamble = MS(info1, HTT_RX_INDICATION_INFO1_PREAMBLE_TYPE); + + switch (preamble) { + case HTT_RX_LEGACY: + cck = info0 & HTT_RX_INDICATION_INFO0_LEGACY_RATE_CCK; + rate = MS(info0, HTT_RX_INDICATION_INFO0_LEGACY_RATE); + rate_idx = 0; + + if (rate < 0x08 || rate > 0x0F) + break; + + switch (band) { + case IEEE80211_BAND_2GHZ: + if (cck) + rate &= ~BIT(3); + rate_idx = rx_legacy_rate_idx[rate]; + break; + case IEEE80211_BAND_5GHZ: + rate_idx = rx_legacy_rate_idx[rate]; + /* We are using same rate table registering + HW - ath10k_rates[]. In case of 5GHz skip + CCK rates, so -4 here */ + rate_idx -= 4; + break; + default: + break; + } + + status->rate_idx = rate_idx; + break; + case HTT_RX_HT: + case HTT_RX_HT_WITH_TXBF: + /* HT-SIG - Table 20-11 in info1 and info2 */ + mcs = info1 & 0x1F; + nss = mcs >> 3; + bw = (info1 >> 7) & 1; + sgi = (info2 >> 7) & 1; + + status->rate_idx = mcs; + status->flag |= RX_FLAG_HT; + if (sgi) + status->flag |= RX_FLAG_SHORT_GI; + if (bw) + status->flag |= RX_FLAG_40MHZ; + break; + case HTT_RX_VHT: + case HTT_RX_VHT_WITH_TXBF: + /* VHT-SIG-A1 in info 1, VHT-SIG-A2 in info2 + TODO check this */ + mcs = (info2 >> 4) & 0x0F; + nss = (info1 >> 10) & 0x07; + bw = info1 & 3; + sgi = info2 & 1; + + status->rate_idx = mcs; + status->vht_nss = nss; + + if (sgi) + status->flag |= RX_FLAG_SHORT_GI; + + switch (bw) { + /* 20MHZ */ + case 0: + break; + /* 40MHZ */ + case 1: + status->flag |= RX_FLAG_40MHZ; + break; + /* 80MHZ */ + case 2: + status->flag |= RX_FLAG_80MHZ; + } + + status->flag |= RX_FLAG_VHT; + break; + default: + break; + } +} + +void ath10k_process_rx(struct ath10k *ar, struct htt_rx_info *info) +{ + struct ieee80211_rx_status *status; + struct ieee80211_channel *ch; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)info->skb->data; + + status = IEEE80211_SKB_RXCB(info->skb); + memset(status, 0, sizeof(*status)); + + if (info->encrypt_type != HTT_RX_MPDU_ENCRYPT_NONE) { + status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED | + RX_FLAG_MMIC_STRIPPED; + hdr->frame_control = __cpu_to_le16( + __le16_to_cpu(hdr->frame_control) & + ~IEEE80211_FCTL_PROTECTED); + } + + if (info->status == HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR) + status->flag |= RX_FLAG_MMIC_ERROR; + + if (info->fcs_err) + status->flag |= RX_FLAG_FAILED_FCS_CRC; + + status->signal = info->signal; + + spin_lock_bh(&ar->data_lock); + ch = ar->scan_channel; + if (!ch) + ch = ar->rx_channel; + spin_unlock_bh(&ar->data_lock); + + if (!ch) { + ath10k_warn("no channel configured; ignoring frame!\n"); + dev_kfree_skb_any(info->skb); + return; + } + + process_rx_rates(ar, info, ch->band, status); + status->band = ch->band; + status->freq = ch->center_freq; + + ath10k_dbg(ATH10K_DBG_DATA, + "rx skb %p len %u %s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u\n", + info->skb, + info->skb->len, + status->flag == 0 ? "legacy" : "", + status->flag & RX_FLAG_HT ? "ht" : "", + status->flag & RX_FLAG_VHT ? "vht" : "", + status->flag & RX_FLAG_40MHZ ? "40" : "", + status->flag & RX_FLAG_80MHZ ? "80" : "", + status->flag & RX_FLAG_SHORT_GI ? "sgi " : "", + status->rate_idx, + status->vht_nss, + status->freq, + status->band); + + ieee80211_rx(ar->hw, info->skb); +} + +struct ath10k_peer *ath10k_peer_find(struct ath10k *ar, int vdev_id, + const u8 *addr) +{ + struct ath10k_peer *peer; + + lockdep_assert_held(&ar->data_lock); + + list_for_each_entry(peer, &ar->peers, list) { + if (peer->vdev_id != vdev_id) + continue; + if (memcmp(peer->addr, addr, ETH_ALEN)) + continue; + + return peer; + } + + return NULL; +} + +static struct ath10k_peer *ath10k_peer_find_by_id(struct ath10k *ar, + int peer_id) +{ + struct ath10k_peer *peer; + + lockdep_assert_held(&ar->data_lock); + + list_for_each_entry(peer, &ar->peers, list) + if (test_bit(peer_id, peer->peer_ids)) + return peer; + + return NULL; +} + +static int ath10k_wait_for_peer_common(struct ath10k *ar, int vdev_id, + const u8 *addr, bool expect_mapped) +{ + int ret; + + ret = wait_event_timeout(ar->peer_mapping_wq, ({ + bool mapped; + + spin_lock_bh(&ar->data_lock); + mapped = !!ath10k_peer_find(ar, vdev_id, addr); + spin_unlock_bh(&ar->data_lock); + + mapped == expect_mapped; + }), 3*HZ); + + if (ret <= 0) + return -ETIMEDOUT; + + return 0; +} + +int ath10k_wait_for_peer_created(struct ath10k *ar, int vdev_id, const u8 *addr) +{ + return ath10k_wait_for_peer_common(ar, vdev_id, addr, true); +} + +int ath10k_wait_for_peer_deleted(struct ath10k *ar, int vdev_id, const u8 *addr) +{ + return ath10k_wait_for_peer_common(ar, vdev_id, addr, false); +} + +void ath10k_peer_map_event(struct ath10k_htt *htt, + struct htt_peer_map_event *ev) +{ + struct ath10k *ar = htt->ar; + struct ath10k_peer *peer; + + spin_lock_bh(&ar->data_lock); + peer = ath10k_peer_find(ar, ev->vdev_id, ev->addr); + if (!peer) { + peer = kzalloc(sizeof(*peer), GFP_ATOMIC); + if (!peer) + goto exit; + + peer->vdev_id = ev->vdev_id; + memcpy(peer->addr, ev->addr, ETH_ALEN); + list_add(&peer->list, &ar->peers); + wake_up(&ar->peer_mapping_wq); + } + + ath10k_dbg(ATH10K_DBG_HTT, "htt peer map vdev %d peer %pM id %d\n", + ev->vdev_id, ev->addr, ev->peer_id); + + set_bit(ev->peer_id, peer->peer_ids); +exit: + spin_unlock_bh(&ar->data_lock); +} + +void ath10k_peer_unmap_event(struct ath10k_htt *htt, + struct htt_peer_unmap_event *ev) +{ + struct ath10k *ar = htt->ar; + struct ath10k_peer *peer; + + spin_lock_bh(&ar->data_lock); + peer = ath10k_peer_find_by_id(ar, ev->peer_id); + if (!peer) { + ath10k_warn("unknown peer id %d\n", ev->peer_id); + goto exit; + } + + ath10k_dbg(ATH10K_DBG_HTT, "htt peer unmap vdev %d peer %pM id %d\n", + peer->vdev_id, peer->addr, ev->peer_id); + + clear_bit(ev->peer_id, peer->peer_ids); + + if (bitmap_empty(peer->peer_ids, ATH10K_MAX_NUM_PEER_IDS)) { + list_del(&peer->list); + kfree(peer); + wake_up(&ar->peer_mapping_wq); + } + +exit: + spin_unlock_bh(&ar->data_lock); +} diff --git a/drivers/net/wireless/ath/ath10k/txrx.h b/drivers/net/wireless/ath/ath10k/txrx.h new file mode 100644 index 000000000000..e78632a76df7 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/txrx.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef _TXRX_H_ +#define _TXRX_H_ + +#include "htt.h" + +void ath10k_txrx_tx_unref(struct ath10k_htt *htt, struct sk_buff *txdesc); +void ath10k_txrx_tx_completed(struct ath10k_htt *htt, + const struct htt_tx_done *tx_done); +void ath10k_process_rx(struct ath10k *ar, struct htt_rx_info *info); + +struct ath10k_peer *ath10k_peer_find(struct ath10k *ar, int vdev_id, + const u8 *addr); +int ath10k_wait_for_peer_created(struct ath10k *ar, int vdev_id, + const u8 *addr); +int ath10k_wait_for_peer_deleted(struct ath10k *ar, int vdev_id, + const u8 *addr); + +void ath10k_peer_map_event(struct ath10k_htt *htt, + struct htt_peer_map_event *ev); +void ath10k_peer_unmap_event(struct ath10k_htt *htt, + struct htt_peer_unmap_event *ev); + +#endif diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c new file mode 100644 index 000000000000..7d4b7987422d --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -0,0 +1,2081 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "core.h" +#include "htc.h" +#include "debug.h" +#include "wmi.h" +#include "mac.h" + +void ath10k_wmi_flush_tx(struct ath10k *ar) +{ + int ret; + + ret = wait_event_timeout(ar->wmi.wq, + atomic_read(&ar->wmi.pending_tx_count) == 0, + 5*HZ); + if (atomic_read(&ar->wmi.pending_tx_count) == 0) + return; + + if (ret == 0) + ret = -ETIMEDOUT; + + if (ret < 0) + ath10k_warn("wmi flush failed (%d)\n", ret); +} + +int ath10k_wmi_wait_for_service_ready(struct ath10k *ar) +{ + int ret; + ret = wait_for_completion_timeout(&ar->wmi.service_ready, + WMI_SERVICE_READY_TIMEOUT_HZ); + return ret; +} + +int ath10k_wmi_wait_for_unified_ready(struct ath10k *ar) +{ + int ret; + ret = wait_for_completion_timeout(&ar->wmi.unified_ready, + WMI_UNIFIED_READY_TIMEOUT_HZ); + return ret; +} + +static struct sk_buff *ath10k_wmi_alloc_skb(u32 len) +{ + struct sk_buff *skb; + u32 round_len = roundup(len, 4); + + skb = ath10k_htc_alloc_skb(WMI_SKB_HEADROOM + round_len); + if (!skb) + return NULL; + + skb_reserve(skb, WMI_SKB_HEADROOM); + if (!IS_ALIGNED((unsigned long)skb->data, 4)) + ath10k_warn("Unaligned WMI skb\n"); + + skb_put(skb, round_len); + memset(skb->data, 0, round_len); + + return skb; +} + +static void ath10k_wmi_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb) +{ + dev_kfree_skb(skb); + + if (atomic_sub_return(1, &ar->wmi.pending_tx_count) == 0) + wake_up(&ar->wmi.wq); +} + +/* WMI command API */ +static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, + enum wmi_cmd_id cmd_id) +{ + struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb); + struct wmi_cmd_hdr *cmd_hdr; + int status; + u32 cmd = 0; + + if (skb_push(skb, sizeof(struct wmi_cmd_hdr)) == NULL) + return -ENOMEM; + + cmd |= SM(cmd_id, WMI_CMD_HDR_CMD_ID); + + cmd_hdr = (struct wmi_cmd_hdr *)skb->data; + cmd_hdr->cmd_id = __cpu_to_le32(cmd); + + if (atomic_add_return(1, &ar->wmi.pending_tx_count) > + WMI_MAX_PENDING_TX_COUNT) { + /* avoid using up memory when FW hangs */ + atomic_dec(&ar->wmi.pending_tx_count); + return -EBUSY; + } + + memset(skb_cb, 0, sizeof(*skb_cb)); + + trace_ath10k_wmi_cmd(cmd_id, skb->data, skb->len); + + status = ath10k_htc_send(ar->htc, ar->wmi.eid, skb); + if (status) { + dev_kfree_skb_any(skb); + atomic_dec(&ar->wmi.pending_tx_count); + return status; + } + + return 0; +} + +static int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb) +{ + struct wmi_scan_event *event = (struct wmi_scan_event *)skb->data; + enum wmi_scan_event_type event_type; + enum wmi_scan_completion_reason reason; + u32 freq; + u32 req_id; + u32 scan_id; + u32 vdev_id; + + event_type = __le32_to_cpu(event->event_type); + reason = __le32_to_cpu(event->reason); + freq = __le32_to_cpu(event->channel_freq); + req_id = __le32_to_cpu(event->scan_req_id); + scan_id = __le32_to_cpu(event->scan_id); + vdev_id = __le32_to_cpu(event->vdev_id); + + ath10k_dbg(ATH10K_DBG_WMI, "WMI_SCAN_EVENTID\n"); + ath10k_dbg(ATH10K_DBG_WMI, + "scan event type %d reason %d freq %d req_id %d " + "scan_id %d vdev_id %d\n", + event_type, reason, freq, req_id, scan_id, vdev_id); + + spin_lock_bh(&ar->data_lock); + + switch (event_type) { + case WMI_SCAN_EVENT_STARTED: + ath10k_dbg(ATH10K_DBG_WMI, "SCAN_EVENT_STARTED\n"); + if (ar->scan.in_progress && ar->scan.is_roc) + ieee80211_ready_on_channel(ar->hw); + + complete(&ar->scan.started); + break; + case WMI_SCAN_EVENT_COMPLETED: + ath10k_dbg(ATH10K_DBG_WMI, "SCAN_EVENT_COMPLETED\n"); + switch (reason) { + case WMI_SCAN_REASON_COMPLETED: + ath10k_dbg(ATH10K_DBG_WMI, "SCAN_REASON_COMPLETED\n"); + break; + case WMI_SCAN_REASON_CANCELLED: + ath10k_dbg(ATH10K_DBG_WMI, "SCAN_REASON_CANCELED\n"); + break; + case WMI_SCAN_REASON_PREEMPTED: + ath10k_dbg(ATH10K_DBG_WMI, "SCAN_REASON_PREEMPTED\n"); + break; + case WMI_SCAN_REASON_TIMEDOUT: + ath10k_dbg(ATH10K_DBG_WMI, "SCAN_REASON_TIMEDOUT\n"); + break; + default: + break; + } + + ar->scan_channel = NULL; + if (!ar->scan.in_progress) { + ath10k_warn("no scan requested, ignoring\n"); + break; + } + + if (ar->scan.is_roc) { + ath10k_offchan_tx_purge(ar); + + if (!ar->scan.aborting) + ieee80211_remain_on_channel_expired(ar->hw); + } else { + ieee80211_scan_completed(ar->hw, ar->scan.aborting); + } + + del_timer(&ar->scan.timeout); + complete_all(&ar->scan.completed); + ar->scan.in_progress = false; + break; + case WMI_SCAN_EVENT_BSS_CHANNEL: + ath10k_dbg(ATH10K_DBG_WMI, "SCAN_EVENT_BSS_CHANNEL\n"); + ar->scan_channel = NULL; + break; + case WMI_SCAN_EVENT_FOREIGN_CHANNEL: + ath10k_dbg(ATH10K_DBG_WMI, "SCAN_EVENT_FOREIGN_CHANNEL\n"); + ar->scan_channel = ieee80211_get_channel(ar->hw->wiphy, freq); + if (ar->scan.in_progress && ar->scan.is_roc && + ar->scan.roc_freq == freq) { + complete(&ar->scan.on_channel); + } + break; + case WMI_SCAN_EVENT_DEQUEUED: + ath10k_dbg(ATH10K_DBG_WMI, "SCAN_EVENT_DEQUEUED\n"); + break; + case WMI_SCAN_EVENT_PREEMPTED: + ath10k_dbg(ATH10K_DBG_WMI, "WMI_SCAN_EVENT_PREEMPTED\n"); + break; + case WMI_SCAN_EVENT_START_FAILED: + ath10k_dbg(ATH10K_DBG_WMI, "WMI_SCAN_EVENT_START_FAILED\n"); + break; + default: + break; + } + + spin_unlock_bh(&ar->data_lock); + return 0; +} + +static inline enum ieee80211_band phy_mode_to_band(u32 phy_mode) +{ + enum ieee80211_band band; + + switch (phy_mode) { + case MODE_11A: + case MODE_11NA_HT20: + case MODE_11NA_HT40: + case MODE_11AC_VHT20: + case MODE_11AC_VHT40: + case MODE_11AC_VHT80: + band = IEEE80211_BAND_5GHZ; + break; + case MODE_11G: + case MODE_11B: + case MODE_11GONLY: + case MODE_11NG_HT20: + case MODE_11NG_HT40: + case MODE_11AC_VHT20_2G: + case MODE_11AC_VHT40_2G: + case MODE_11AC_VHT80_2G: + default: + band = IEEE80211_BAND_2GHZ; + } + + return band; +} + +static inline u8 get_rate_idx(u32 rate, enum ieee80211_band band) +{ + u8 rate_idx = 0; + + /* rate in Kbps */ + switch (rate) { + case 1000: + rate_idx = 0; + break; + case 2000: + rate_idx = 1; + break; + case 5500: + rate_idx = 2; + break; + case 11000: + rate_idx = 3; + break; + case 6000: + rate_idx = 4; + break; + case 9000: + rate_idx = 5; + break; + case 12000: + rate_idx = 6; + break; + case 18000: + rate_idx = 7; + break; + case 24000: + rate_idx = 8; + break; + case 36000: + rate_idx = 9; + break; + case 48000: + rate_idx = 10; + break; + case 54000: + rate_idx = 11; + break; + default: + break; + } + + if (band == IEEE80211_BAND_5GHZ) { + if (rate_idx > 3) + /* Omit CCK rates */ + rate_idx -= 4; + else + rate_idx = 0; + } + + return rate_idx; +} + +static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) +{ + struct wmi_mgmt_rx_event *event = (struct wmi_mgmt_rx_event *)skb->data; + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); + struct ieee80211_hdr *hdr; + u32 rx_status; + u32 channel; + u32 phy_mode; + u32 snr; + u32 rate; + u32 buf_len; + u16 fc; + + channel = __le32_to_cpu(event->hdr.channel); + buf_len = __le32_to_cpu(event->hdr.buf_len); + rx_status = __le32_to_cpu(event->hdr.status); + snr = __le32_to_cpu(event->hdr.snr); + phy_mode = __le32_to_cpu(event->hdr.phy_mode); + rate = __le32_to_cpu(event->hdr.rate); + + memset(status, 0, sizeof(*status)); + + ath10k_dbg(ATH10K_DBG_MGMT, + "event mgmt rx status %08x\n", rx_status); + + if (rx_status & WMI_RX_STATUS_ERR_DECRYPT) { + dev_kfree_skb(skb); + return 0; + } + + if (rx_status & WMI_RX_STATUS_ERR_KEY_CACHE_MISS) { + dev_kfree_skb(skb); + return 0; + } + + if (rx_status & WMI_RX_STATUS_ERR_CRC) + status->flag |= RX_FLAG_FAILED_FCS_CRC; + if (rx_status & WMI_RX_STATUS_ERR_MIC) + status->flag |= RX_FLAG_MMIC_ERROR; + + status->band = phy_mode_to_band(phy_mode); + status->freq = ieee80211_channel_to_frequency(channel, status->band); + status->signal = snr + ATH10K_DEFAULT_NOISE_FLOOR; + status->rate_idx = get_rate_idx(rate, status->band); + + skb_pull(skb, sizeof(event->hdr)); + + hdr = (struct ieee80211_hdr *)skb->data; + fc = le16_to_cpu(hdr->frame_control); + + if (fc & IEEE80211_FCTL_PROTECTED) { + status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED | + RX_FLAG_MMIC_STRIPPED; + hdr->frame_control = __cpu_to_le16(fc & + ~IEEE80211_FCTL_PROTECTED); + } + + ath10k_dbg(ATH10K_DBG_MGMT, + "event mgmt rx skb %p len %d ftype %02x stype %02x\n", + skb, skb->len, + fc & IEEE80211_FCTL_FTYPE, fc & IEEE80211_FCTL_STYPE); + + ath10k_dbg(ATH10K_DBG_MGMT, + "event mgmt rx freq %d band %d snr %d, rate_idx %d\n", + status->freq, status->band, status->signal, + status->rate_idx); + + /* + * packets from HTC come aligned to 4byte boundaries + * because they can originally come in along with a trailer + */ + skb_trim(skb, buf_len); + + ieee80211_rx(ar->hw, skb); + return 0; +} + +static void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_CHAN_INFO_EVENTID\n"); +} + +static void ath10k_wmi_event_echo(struct ath10k *ar, struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_ECHO_EVENTID\n"); +} + +static void ath10k_wmi_event_debug_mesg(struct ath10k *ar, struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_DEBUG_MESG_EVENTID\n"); +} + +static void ath10k_wmi_event_update_stats(struct ath10k *ar, + struct sk_buff *skb) +{ + struct wmi_stats_event *ev = (struct wmi_stats_event *)skb->data; + + ath10k_dbg(ATH10K_DBG_WMI, "WMI_UPDATE_STATS_EVENTID\n"); + + ath10k_debug_read_target_stats(ar, ev); +} + +static void ath10k_wmi_event_vdev_start_resp(struct ath10k *ar, + struct sk_buff *skb) +{ + struct wmi_vdev_start_response_event *ev; + + ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_START_RESP_EVENTID\n"); + + ev = (struct wmi_vdev_start_response_event *)skb->data; + + if (WARN_ON(__le32_to_cpu(ev->status))) + return; + + complete(&ar->vdev_setup_done); +} + +static void ath10k_wmi_event_vdev_stopped(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_STOPPED_EVENTID\n"); + complete(&ar->vdev_setup_done); +} + +static void ath10k_wmi_event_peer_sta_kickout(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_PEER_STA_KICKOUT_EVENTID\n"); +} + +/* + * FIXME + * + * We don't report to mac80211 sleep state of connected + * stations. Due to this mac80211 can't fill in TIM IE + * correctly. + * + * I know of no way of getting nullfunc frames that contain + * sleep transition from connected stations - these do not + * seem to be sent from the target to the host. There also + * doesn't seem to be a dedicated event for that. So the + * only way left to do this would be to read tim_bitmap + * during SWBA. + * + * We could probably try using tim_bitmap from SWBA to tell + * mac80211 which stations are asleep and which are not. The + * problem here is calling mac80211 functions so many times + * could take too long and make us miss the time to submit + * the beacon to the target. + * + * So as a workaround we try to extend the TIM IE if there + * is unicast buffered for stations with aid > 7 and fill it + * in ourselves. + */ +static void ath10k_wmi_update_tim(struct ath10k *ar, + struct ath10k_vif *arvif, + struct sk_buff *bcn, + struct wmi_bcn_info *bcn_info) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)bcn->data; + struct ieee80211_tim_ie *tim; + u8 *ies, *ie; + u8 ie_len, pvm_len; + + /* if next SWBA has no tim_changed the tim_bitmap is garbage. + * we must copy the bitmap upon change and reuse it later */ + if (__le32_to_cpu(bcn_info->tim_info.tim_changed)) { + int i; + + BUILD_BUG_ON(sizeof(arvif->u.ap.tim_bitmap) != + sizeof(bcn_info->tim_info.tim_bitmap)); + + for (i = 0; i < sizeof(arvif->u.ap.tim_bitmap); i++) { + __le32 t = bcn_info->tim_info.tim_bitmap[i / 4]; + u32 v = __le32_to_cpu(t); + arvif->u.ap.tim_bitmap[i] = (v >> ((i % 4) * 8)) & 0xFF; + } + + /* FW reports either length 0 or 16 + * so we calculate this on our own */ + arvif->u.ap.tim_len = 0; + for (i = 0; i < sizeof(arvif->u.ap.tim_bitmap); i++) + if (arvif->u.ap.tim_bitmap[i]) + arvif->u.ap.tim_len = i; + + arvif->u.ap.tim_len++; + } + + ies = bcn->data; + ies += ieee80211_hdrlen(hdr->frame_control); + ies += 12; /* fixed parameters */ + + ie = (u8 *)cfg80211_find_ie(WLAN_EID_TIM, ies, + (u8 *)skb_tail_pointer(bcn) - ies); + if (!ie) { + /* highly unlikely for mac80211 */ + ath10k_warn("no tim ie found;\n"); + return; + } + + tim = (void *)ie + 2; + ie_len = ie[1]; + pvm_len = ie_len - 3; /* exclude dtim count, dtim period, bmap ctl */ + + if (pvm_len < arvif->u.ap.tim_len) { + int expand_size = sizeof(arvif->u.ap.tim_bitmap) - pvm_len; + int move_size = skb_tail_pointer(bcn) - (ie + 2 + ie_len); + void *next_ie = ie + 2 + ie_len; + + if (skb_put(bcn, expand_size)) { + memmove(next_ie + expand_size, next_ie, move_size); + + ie[1] += expand_size; + ie_len += expand_size; + pvm_len += expand_size; + } else { + ath10k_warn("tim expansion failed\n"); + } + } + + if (pvm_len > sizeof(arvif->u.ap.tim_bitmap)) { + ath10k_warn("tim pvm length is too great (%d)\n", pvm_len); + return; + } + + tim->bitmap_ctrl = !!__le32_to_cpu(bcn_info->tim_info.tim_mcast); + memcpy(tim->virtual_map, arvif->u.ap.tim_bitmap, pvm_len); + + ath10k_dbg(ATH10K_DBG_MGMT, "dtim %d/%d mcast %d pvmlen %d\n", + tim->dtim_count, tim->dtim_period, + tim->bitmap_ctrl, pvm_len); +} + +static void ath10k_p2p_fill_noa_ie(u8 *data, u32 len, + struct wmi_p2p_noa_info *noa) +{ + struct ieee80211_p2p_noa_attr *noa_attr; + u8 ctwindow_oppps = noa->ctwindow_oppps; + u8 ctwindow = ctwindow_oppps >> WMI_P2P_OPPPS_CTWINDOW_OFFSET; + bool oppps = !!(ctwindow_oppps & WMI_P2P_OPPPS_ENABLE_BIT); + __le16 *noa_attr_len; + u16 attr_len; + u8 noa_descriptors = noa->num_descriptors; + int i; + + /* P2P IE */ + data[0] = WLAN_EID_VENDOR_SPECIFIC; + data[1] = len - 2; + data[2] = (WLAN_OUI_WFA >> 16) & 0xff; + data[3] = (WLAN_OUI_WFA >> 8) & 0xff; + data[4] = (WLAN_OUI_WFA >> 0) & 0xff; + data[5] = WLAN_OUI_TYPE_WFA_P2P; + + /* NOA ATTR */ + data[6] = IEEE80211_P2P_ATTR_ABSENCE_NOTICE; + noa_attr_len = (__le16 *)&data[7]; /* 2 bytes */ + noa_attr = (struct ieee80211_p2p_noa_attr *)&data[9]; + + noa_attr->index = noa->index; + noa_attr->oppps_ctwindow = ctwindow; + if (oppps) + noa_attr->oppps_ctwindow |= IEEE80211_P2P_OPPPS_ENABLE_BIT; + + for (i = 0; i < noa_descriptors; i++) { + noa_attr->desc[i].count = + __le32_to_cpu(noa->descriptors[i].type_count); + noa_attr->desc[i].duration = noa->descriptors[i].duration; + noa_attr->desc[i].interval = noa->descriptors[i].interval; + noa_attr->desc[i].start_time = noa->descriptors[i].start_time; + } + + attr_len = 2; /* index + oppps_ctwindow */ + attr_len += noa_descriptors * sizeof(struct ieee80211_p2p_noa_desc); + *noa_attr_len = __cpu_to_le16(attr_len); +} + +static u32 ath10k_p2p_calc_noa_ie_len(struct wmi_p2p_noa_info *noa) +{ + u32 len = 0; + u8 noa_descriptors = noa->num_descriptors; + u8 opp_ps_info = noa->ctwindow_oppps; + bool opps_enabled = !!(opp_ps_info & WMI_P2P_OPPPS_ENABLE_BIT); + + + if (!noa_descriptors && !opps_enabled) + return len; + + len += 1 + 1 + 4; /* EID + len + OUI */ + len += 1 + 2; /* noa attr + attr len */ + len += 1 + 1; /* index + oppps_ctwindow */ + len += noa_descriptors * sizeof(struct ieee80211_p2p_noa_desc); + + return len; +} + +static void ath10k_wmi_update_noa(struct ath10k *ar, struct ath10k_vif *arvif, + struct sk_buff *bcn, + struct wmi_bcn_info *bcn_info) +{ + struct wmi_p2p_noa_info *noa = &bcn_info->p2p_noa_info; + u8 *new_data, *old_data = arvif->u.ap.noa_data; + u32 new_len; + + if (arvif->vdev_subtype != WMI_VDEV_SUBTYPE_P2P_GO) + return; + + ath10k_dbg(ATH10K_DBG_MGMT, "noa changed: %d\n", noa->changed); + if (noa->changed & WMI_P2P_NOA_CHANGED_BIT) { + new_len = ath10k_p2p_calc_noa_ie_len(noa); + if (!new_len) + goto cleanup; + + new_data = kmalloc(new_len, GFP_ATOMIC); + if (!new_data) + goto cleanup; + + ath10k_p2p_fill_noa_ie(new_data, new_len, noa); + + spin_lock_bh(&ar->data_lock); + arvif->u.ap.noa_data = new_data; + arvif->u.ap.noa_len = new_len; + spin_unlock_bh(&ar->data_lock); + kfree(old_data); + } + + if (arvif->u.ap.noa_data) + if (!pskb_expand_head(bcn, 0, arvif->u.ap.noa_len, GFP_ATOMIC)) + memcpy(skb_put(bcn, arvif->u.ap.noa_len), + arvif->u.ap.noa_data, + arvif->u.ap.noa_len); + return; + +cleanup: + spin_lock_bh(&ar->data_lock); + arvif->u.ap.noa_data = NULL; + arvif->u.ap.noa_len = 0; + spin_unlock_bh(&ar->data_lock); + kfree(old_data); +} + + +static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb) +{ + struct wmi_host_swba_event *ev; + u32 map; + int i = -1; + struct wmi_bcn_info *bcn_info; + struct ath10k_vif *arvif; + struct wmi_bcn_tx_arg arg; + struct sk_buff *bcn; + int vdev_id = 0; + int ret; + + ath10k_dbg(ATH10K_DBG_MGMT, "WMI_HOST_SWBA_EVENTID\n"); + + ev = (struct wmi_host_swba_event *)skb->data; + map = __le32_to_cpu(ev->vdev_map); + + ath10k_dbg(ATH10K_DBG_MGMT, "host swba:\n" + "-vdev map 0x%x\n", + ev->vdev_map); + + for (; map; map >>= 1, vdev_id++) { + if (!(map & 0x1)) + continue; + + i++; + + if (i >= WMI_MAX_AP_VDEV) { + ath10k_warn("swba has corrupted vdev map\n"); + break; + } + + bcn_info = &ev->bcn_info[i]; + + ath10k_dbg(ATH10K_DBG_MGMT, + "-bcn_info[%d]:\n" + "--tim_len %d\n" + "--tim_mcast %d\n" + "--tim_changed %d\n" + "--tim_num_ps_pending %d\n" + "--tim_bitmap 0x%08x%08x%08x%08x\n", + i, + __le32_to_cpu(bcn_info->tim_info.tim_len), + __le32_to_cpu(bcn_info->tim_info.tim_mcast), + __le32_to_cpu(bcn_info->tim_info.tim_changed), + __le32_to_cpu(bcn_info->tim_info.tim_num_ps_pending), + __le32_to_cpu(bcn_info->tim_info.tim_bitmap[3]), + __le32_to_cpu(bcn_info->tim_info.tim_bitmap[2]), + __le32_to_cpu(bcn_info->tim_info.tim_bitmap[1]), + __le32_to_cpu(bcn_info->tim_info.tim_bitmap[0])); + + arvif = ath10k_get_arvif(ar, vdev_id); + if (arvif == NULL) { + ath10k_warn("no vif for vdev_id %d found\n", vdev_id); + continue; + } + + bcn = ieee80211_beacon_get(ar->hw, arvif->vif); + if (!bcn) { + ath10k_warn("could not get mac80211 beacon\n"); + continue; + } + + ath10k_tx_h_seq_no(bcn); + ath10k_wmi_update_tim(ar, arvif, bcn, bcn_info); + ath10k_wmi_update_noa(ar, arvif, bcn, bcn_info); + + arg.vdev_id = arvif->vdev_id; + arg.tx_rate = 0; + arg.tx_power = 0; + arg.bcn = bcn->data; + arg.bcn_len = bcn->len; + + ret = ath10k_wmi_beacon_send(ar, &arg); + if (ret) + ath10k_warn("could not send beacon (%d)\n", ret); + + dev_kfree_skb_any(bcn); + } +} + +static void ath10k_wmi_event_tbttoffset_update(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_TBTTOFFSET_UPDATE_EVENTID\n"); +} + +static void ath10k_wmi_event_phyerr(struct ath10k *ar, struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_PHYERR_EVENTID\n"); +} + +static void ath10k_wmi_event_roam(struct ath10k *ar, struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_ROAM_EVENTID\n"); +} + +static void ath10k_wmi_event_profile_match(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_PROFILE_MATCH\n"); +} + +static void ath10k_wmi_event_debug_print(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_DEBUG_PRINT_EVENTID\n"); +} + +static void ath10k_wmi_event_pdev_qvit(struct ath10k *ar, struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_PDEV_QVIT_EVENTID\n"); +} + +static void ath10k_wmi_event_wlan_profile_data(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_WLAN_PROFILE_DATA_EVENTID\n"); +} + +static void ath10k_wmi_event_rtt_measurement_report(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_RTT_MEASUREMENT_REPORT_EVENTID\n"); +} + +static void ath10k_wmi_event_tsf_measurement_report(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_TSF_MEASUREMENT_REPORT_EVENTID\n"); +} + +static void ath10k_wmi_event_rtt_error_report(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_RTT_ERROR_REPORT_EVENTID\n"); +} + +static void ath10k_wmi_event_wow_wakeup_host(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_WOW_WAKEUP_HOST_EVENTID\n"); +} + +static void ath10k_wmi_event_dcs_interference(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_DCS_INTERFERENCE_EVENTID\n"); +} + +static void ath10k_wmi_event_pdev_tpc_config(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_PDEV_TPC_CONFIG_EVENTID\n"); +} + +static void ath10k_wmi_event_pdev_ftm_intg(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_PDEV_FTM_INTG_EVENTID\n"); +} + +static void ath10k_wmi_event_gtk_offload_status(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_GTK_OFFLOAD_STATUS_EVENTID\n"); +} + +static void ath10k_wmi_event_gtk_rekey_fail(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_GTK_REKEY_FAIL_EVENTID\n"); +} + +static void ath10k_wmi_event_delba_complete(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_TX_DELBA_COMPLETE_EVENTID\n"); +} + +static void ath10k_wmi_event_addba_complete(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_TX_ADDBA_COMPLETE_EVENTID\n"); +} + +static void ath10k_wmi_event_vdev_install_key_complete(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID\n"); +} + +static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar, + struct sk_buff *skb) +{ + struct wmi_service_ready_event *ev = (void *)skb->data; + + if (skb->len < sizeof(*ev)) { + ath10k_warn("Service ready event was %d B but expected %zu B. Wrong firmware version?\n", + skb->len, sizeof(*ev)); + return; + } + + ar->hw_min_tx_power = __le32_to_cpu(ev->hw_min_tx_power); + ar->hw_max_tx_power = __le32_to_cpu(ev->hw_max_tx_power); + ar->ht_cap_info = __le32_to_cpu(ev->ht_cap_info); + ar->vht_cap_info = __le32_to_cpu(ev->vht_cap_info); + ar->fw_version_major = + (__le32_to_cpu(ev->sw_version) & 0xff000000) >> 24; + ar->fw_version_minor = (__le32_to_cpu(ev->sw_version) & 0x00ffffff); + ar->fw_version_release = + (__le32_to_cpu(ev->sw_version_1) & 0xffff0000) >> 16; + ar->fw_version_build = (__le32_to_cpu(ev->sw_version_1) & 0x0000ffff); + ar->phy_capability = __le32_to_cpu(ev->phy_capability); + + ar->ath_common.regulatory.current_rd = + __le32_to_cpu(ev->hal_reg_capabilities.eeprom_rd); + + ath10k_debug_read_service_map(ar, ev->wmi_service_bitmap, + sizeof(ev->wmi_service_bitmap)); + + if (strlen(ar->hw->wiphy->fw_version) == 0) { + snprintf(ar->hw->wiphy->fw_version, + sizeof(ar->hw->wiphy->fw_version), + "%u.%u.%u.%u", + ar->fw_version_major, + ar->fw_version_minor, + ar->fw_version_release, + ar->fw_version_build); + } + + /* FIXME: it probably should be better to support this */ + if (__le32_to_cpu(ev->num_mem_reqs) > 0) { + ath10k_warn("target requested %d memory chunks; ignoring\n", + __le32_to_cpu(ev->num_mem_reqs)); + } + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi event service ready sw_ver 0x%08x sw_ver1 0x%08x abi_ver %u phy_cap 0x%08x ht_cap 0x%08x vht_cap 0x%08x vht_supp_msc 0x%08x sys_cap_info 0x%08x mem_reqs %u\n", + __le32_to_cpu(ev->sw_version), + __le32_to_cpu(ev->sw_version_1), + __le32_to_cpu(ev->abi_version), + __le32_to_cpu(ev->phy_capability), + __le32_to_cpu(ev->ht_cap_info), + __le32_to_cpu(ev->vht_cap_info), + __le32_to_cpu(ev->vht_supp_mcs), + __le32_to_cpu(ev->sys_cap_info), + __le32_to_cpu(ev->num_mem_reqs)); + + complete(&ar->wmi.service_ready); +} + +static int ath10k_wmi_ready_event_rx(struct ath10k *ar, struct sk_buff *skb) +{ + struct wmi_ready_event *ev = (struct wmi_ready_event *)skb->data; + + if (WARN_ON(skb->len < sizeof(*ev))) + return -EINVAL; + + memcpy(ar->mac_addr, ev->mac_addr.addr, ETH_ALEN); + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi event ready sw_version %u abi_version %u mac_addr %pM status %d\n", + __le32_to_cpu(ev->sw_version), + __le32_to_cpu(ev->abi_version), + ev->mac_addr.addr, + __le32_to_cpu(ev->status)); + + complete(&ar->wmi.unified_ready); + return 0; +} + +static void ath10k_wmi_event_process(struct ath10k *ar, struct sk_buff *skb) +{ + struct wmi_cmd_hdr *cmd_hdr; + enum wmi_event_id id; + u16 len; + + cmd_hdr = (struct wmi_cmd_hdr *)skb->data; + id = MS(__le32_to_cpu(cmd_hdr->cmd_id), WMI_CMD_HDR_CMD_ID); + + if (skb_pull(skb, sizeof(struct wmi_cmd_hdr)) == NULL) + return; + + len = skb->len; + + trace_ath10k_wmi_event(id, skb->data, skb->len); + + switch (id) { + case WMI_MGMT_RX_EVENTID: + ath10k_wmi_event_mgmt_rx(ar, skb); + /* mgmt_rx() owns the skb now! */ + return; + case WMI_SCAN_EVENTID: + ath10k_wmi_event_scan(ar, skb); + break; + case WMI_CHAN_INFO_EVENTID: + ath10k_wmi_event_chan_info(ar, skb); + break; + case WMI_ECHO_EVENTID: + ath10k_wmi_event_echo(ar, skb); + break; + case WMI_DEBUG_MESG_EVENTID: + ath10k_wmi_event_debug_mesg(ar, skb); + break; + case WMI_UPDATE_STATS_EVENTID: + ath10k_wmi_event_update_stats(ar, skb); + break; + case WMI_VDEV_START_RESP_EVENTID: + ath10k_wmi_event_vdev_start_resp(ar, skb); + break; + case WMI_VDEV_STOPPED_EVENTID: + ath10k_wmi_event_vdev_stopped(ar, skb); + break; + case WMI_PEER_STA_KICKOUT_EVENTID: + ath10k_wmi_event_peer_sta_kickout(ar, skb); + break; + case WMI_HOST_SWBA_EVENTID: + ath10k_wmi_event_host_swba(ar, skb); + break; + case WMI_TBTTOFFSET_UPDATE_EVENTID: + ath10k_wmi_event_tbttoffset_update(ar, skb); + break; + case WMI_PHYERR_EVENTID: + ath10k_wmi_event_phyerr(ar, skb); + break; + case WMI_ROAM_EVENTID: + ath10k_wmi_event_roam(ar, skb); + break; + case WMI_PROFILE_MATCH: + ath10k_wmi_event_profile_match(ar, skb); + break; + case WMI_DEBUG_PRINT_EVENTID: + ath10k_wmi_event_debug_print(ar, skb); + break; + case WMI_PDEV_QVIT_EVENTID: + ath10k_wmi_event_pdev_qvit(ar, skb); + break; + case WMI_WLAN_PROFILE_DATA_EVENTID: + ath10k_wmi_event_wlan_profile_data(ar, skb); + break; + case WMI_RTT_MEASUREMENT_REPORT_EVENTID: + ath10k_wmi_event_rtt_measurement_report(ar, skb); + break; + case WMI_TSF_MEASUREMENT_REPORT_EVENTID: + ath10k_wmi_event_tsf_measurement_report(ar, skb); + break; + case WMI_RTT_ERROR_REPORT_EVENTID: + ath10k_wmi_event_rtt_error_report(ar, skb); + break; + case WMI_WOW_WAKEUP_HOST_EVENTID: + ath10k_wmi_event_wow_wakeup_host(ar, skb); + break; + case WMI_DCS_INTERFERENCE_EVENTID: + ath10k_wmi_event_dcs_interference(ar, skb); + break; + case WMI_PDEV_TPC_CONFIG_EVENTID: + ath10k_wmi_event_pdev_tpc_config(ar, skb); + break; + case WMI_PDEV_FTM_INTG_EVENTID: + ath10k_wmi_event_pdev_ftm_intg(ar, skb); + break; + case WMI_GTK_OFFLOAD_STATUS_EVENTID: + ath10k_wmi_event_gtk_offload_status(ar, skb); + break; + case WMI_GTK_REKEY_FAIL_EVENTID: + ath10k_wmi_event_gtk_rekey_fail(ar, skb); + break; + case WMI_TX_DELBA_COMPLETE_EVENTID: + ath10k_wmi_event_delba_complete(ar, skb); + break; + case WMI_TX_ADDBA_COMPLETE_EVENTID: + ath10k_wmi_event_addba_complete(ar, skb); + break; + case WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID: + ath10k_wmi_event_vdev_install_key_complete(ar, skb); + break; + case WMI_SERVICE_READY_EVENTID: + ath10k_wmi_service_ready_event_rx(ar, skb); + break; + case WMI_READY_EVENTID: + ath10k_wmi_ready_event_rx(ar, skb); + break; + default: + ath10k_warn("Unknown eventid: %d\n", id); + break; + } + + dev_kfree_skb(skb); +} + +static void ath10k_wmi_event_work(struct work_struct *work) +{ + struct ath10k *ar = container_of(work, struct ath10k, + wmi.wmi_event_work); + struct sk_buff *skb; + + for (;;) { + skb = skb_dequeue(&ar->wmi.wmi_event_list); + if (!skb) + break; + + ath10k_wmi_event_process(ar, skb); + } +} + +static void ath10k_wmi_process_rx(struct ath10k *ar, struct sk_buff *skb) +{ + struct wmi_cmd_hdr *cmd_hdr = (struct wmi_cmd_hdr *)skb->data; + enum wmi_event_id event_id; + + event_id = MS(__le32_to_cpu(cmd_hdr->cmd_id), WMI_CMD_HDR_CMD_ID); + + /* some events require to be handled ASAP + * thus can't be defered to a worker thread */ + switch (event_id) { + case WMI_HOST_SWBA_EVENTID: + case WMI_MGMT_RX_EVENTID: + ath10k_wmi_event_process(ar, skb); + return; + default: + break; + } + + skb_queue_tail(&ar->wmi.wmi_event_list, skb); + queue_work(ar->workqueue, &ar->wmi.wmi_event_work); +} + +/* WMI Initialization functions */ +int ath10k_wmi_attach(struct ath10k *ar) +{ + init_completion(&ar->wmi.service_ready); + init_completion(&ar->wmi.unified_ready); + init_waitqueue_head(&ar->wmi.wq); + + skb_queue_head_init(&ar->wmi.wmi_event_list); + INIT_WORK(&ar->wmi.wmi_event_work, ath10k_wmi_event_work); + + return 0; +} + +void ath10k_wmi_detach(struct ath10k *ar) +{ + /* HTC should've drained the packets already */ + if (WARN_ON(atomic_read(&ar->wmi.pending_tx_count) > 0)) + ath10k_warn("there are still pending packets\n"); + + cancel_work_sync(&ar->wmi.wmi_event_work); + skb_queue_purge(&ar->wmi.wmi_event_list); +} + +int ath10k_wmi_connect_htc_service(struct ath10k *ar) +{ + int status; + struct ath10k_htc_svc_conn_req conn_req; + struct ath10k_htc_svc_conn_resp conn_resp; + + memset(&conn_req, 0, sizeof(conn_req)); + memset(&conn_resp, 0, sizeof(conn_resp)); + + /* these fields are the same for all service endpoints */ + conn_req.ep_ops.ep_tx_complete = ath10k_wmi_htc_tx_complete; + conn_req.ep_ops.ep_rx_complete = ath10k_wmi_process_rx; + + /* connect to control service */ + conn_req.service_id = ATH10K_HTC_SVC_ID_WMI_CONTROL; + + status = ath10k_htc_connect_service(ar->htc, &conn_req, &conn_resp); + if (status) { + ath10k_warn("failed to connect to WMI CONTROL service status: %d\n", + status); + return status; + } + + ar->wmi.eid = conn_resp.eid; + return 0; +} + +int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g, + u16 rd5g, u16 ctl2g, u16 ctl5g) +{ + struct wmi_pdev_set_regdomain_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_pdev_set_regdomain_cmd *)skb->data; + cmd->reg_domain = __cpu_to_le32(rd); + cmd->reg_domain_2G = __cpu_to_le32(rd2g); + cmd->reg_domain_5G = __cpu_to_le32(rd5g); + cmd->conformance_test_limit_2G = __cpu_to_le32(ctl2g); + cmd->conformance_test_limit_5G = __cpu_to_le32(ctl5g); + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi pdev regdomain rd %x rd2g %x rd5g %x ctl2g %x ctl5g %x\n", + rd, rd2g, rd5g, ctl2g, ctl5g); + + return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_REGDOMAIN_CMDID); +} + +int ath10k_wmi_pdev_set_channel(struct ath10k *ar, + const struct wmi_channel_arg *arg) +{ + struct wmi_set_channel_cmd *cmd; + struct sk_buff *skb; + + if (arg->passive) + return -EINVAL; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_set_channel_cmd *)skb->data; + cmd->chan.mhz = __cpu_to_le32(arg->freq); + cmd->chan.band_center_freq1 = __cpu_to_le32(arg->freq); + cmd->chan.mode = arg->mode; + cmd->chan.min_power = arg->min_power; + cmd->chan.max_power = arg->max_power; + cmd->chan.reg_power = arg->max_reg_power; + cmd->chan.reg_classid = arg->reg_class_id; + cmd->chan.antenna_max = arg->max_antenna_gain; + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi set channel mode %d freq %d\n", + arg->mode, arg->freq); + + return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_CHANNEL_CMDID); +} + +int ath10k_wmi_pdev_suspend_target(struct ath10k *ar) +{ + struct wmi_pdev_suspend_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_pdev_suspend_cmd *)skb->data; + cmd->suspend_opt = WMI_PDEV_SUSPEND; + + return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SUSPEND_CMDID); +} + +int ath10k_wmi_pdev_resume_target(struct ath10k *ar) +{ + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(0); + if (skb == NULL) + return -ENOMEM; + + return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_RESUME_CMDID); +} + +int ath10k_wmi_pdev_set_param(struct ath10k *ar, enum wmi_pdev_param id, + u32 value) +{ + struct wmi_pdev_set_param_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_pdev_set_param_cmd *)skb->data; + cmd->param_id = __cpu_to_le32(id); + cmd->param_value = __cpu_to_le32(value); + + ath10k_dbg(ATH10K_DBG_WMI, "wmi pdev set param %d value %d\n", + id, value); + return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_PARAM_CMDID); +} + +int ath10k_wmi_cmd_init(struct ath10k *ar) +{ + struct wmi_init_cmd *cmd; + struct sk_buff *buf; + struct wmi_resource_config config = {}; + u32 val; + + config.num_vdevs = __cpu_to_le32(TARGET_NUM_VDEVS); + config.num_peers = __cpu_to_le32(TARGET_NUM_PEERS + TARGET_NUM_VDEVS); + config.num_offload_peers = __cpu_to_le32(TARGET_NUM_OFFLOAD_PEERS); + + config.num_offload_reorder_bufs = + __cpu_to_le32(TARGET_NUM_OFFLOAD_REORDER_BUFS); + + config.num_peer_keys = __cpu_to_le32(TARGET_NUM_PEER_KEYS); + config.num_tids = __cpu_to_le32(TARGET_NUM_TIDS); + config.ast_skid_limit = __cpu_to_le32(TARGET_AST_SKID_LIMIT); + config.tx_chain_mask = __cpu_to_le32(TARGET_TX_CHAIN_MASK); + config.rx_chain_mask = __cpu_to_le32(TARGET_RX_CHAIN_MASK); + config.rx_timeout_pri_vo = __cpu_to_le32(TARGET_RX_TIMEOUT_LO_PRI); + config.rx_timeout_pri_vi = __cpu_to_le32(TARGET_RX_TIMEOUT_LO_PRI); + config.rx_timeout_pri_be = __cpu_to_le32(TARGET_RX_TIMEOUT_LO_PRI); + config.rx_timeout_pri_bk = __cpu_to_le32(TARGET_RX_TIMEOUT_HI_PRI); + config.rx_decap_mode = __cpu_to_le32(TARGET_RX_DECAP_MODE); + + config.scan_max_pending_reqs = + __cpu_to_le32(TARGET_SCAN_MAX_PENDING_REQS); + + config.bmiss_offload_max_vdev = + __cpu_to_le32(TARGET_BMISS_OFFLOAD_MAX_VDEV); + + config.roam_offload_max_vdev = + __cpu_to_le32(TARGET_ROAM_OFFLOAD_MAX_VDEV); + + config.roam_offload_max_ap_profiles = + __cpu_to_le32(TARGET_ROAM_OFFLOAD_MAX_AP_PROFILES); + + config.num_mcast_groups = __cpu_to_le32(TARGET_NUM_MCAST_GROUPS); + config.num_mcast_table_elems = + __cpu_to_le32(TARGET_NUM_MCAST_TABLE_ELEMS); + + config.mcast2ucast_mode = __cpu_to_le32(TARGET_MCAST2UCAST_MODE); + config.tx_dbg_log_size = __cpu_to_le32(TARGET_TX_DBG_LOG_SIZE); + config.num_wds_entries = __cpu_to_le32(TARGET_NUM_WDS_ENTRIES); + config.dma_burst_size = __cpu_to_le32(TARGET_DMA_BURST_SIZE); + config.mac_aggr_delim = __cpu_to_le32(TARGET_MAC_AGGR_DELIM); + + val = TARGET_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK; + config.rx_skip_defrag_timeout_dup_detection_check = __cpu_to_le32(val); + + config.vow_config = __cpu_to_le32(TARGET_VOW_CONFIG); + + config.gtk_offload_max_vdev = + __cpu_to_le32(TARGET_GTK_OFFLOAD_MAX_VDEV); + + config.num_msdu_desc = __cpu_to_le32(TARGET_NUM_MSDU_DESC); + config.max_frag_entries = __cpu_to_le32(TARGET_MAX_FRAG_ENTRIES); + + buf = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!buf) + return -ENOMEM; + + cmd = (struct wmi_init_cmd *)buf->data; + cmd->num_host_mem_chunks = 0; + memcpy(&cmd->resource_config, &config, sizeof(config)); + + ath10k_dbg(ATH10K_DBG_WMI, "wmi init\n"); + return ath10k_wmi_cmd_send(ar, buf, WMI_INIT_CMDID); +} + +static int ath10k_wmi_start_scan_calc_len(const struct wmi_start_scan_arg *arg) +{ + int len; + + len = sizeof(struct wmi_start_scan_cmd); + + if (arg->ie_len) { + if (!arg->ie) + return -EINVAL; + if (arg->ie_len > WLAN_SCAN_PARAMS_MAX_IE_LEN) + return -EINVAL; + + len += sizeof(struct wmi_ie_data); + len += roundup(arg->ie_len, 4); + } + + if (arg->n_channels) { + if (!arg->channels) + return -EINVAL; + if (arg->n_channels > ARRAY_SIZE(arg->channels)) + return -EINVAL; + + len += sizeof(struct wmi_chan_list); + len += sizeof(__le32) * arg->n_channels; + } + + if (arg->n_ssids) { + if (!arg->ssids) + return -EINVAL; + if (arg->n_ssids > WLAN_SCAN_PARAMS_MAX_SSID) + return -EINVAL; + + len += sizeof(struct wmi_ssid_list); + len += sizeof(struct wmi_ssid) * arg->n_ssids; + } + + if (arg->n_bssids) { + if (!arg->bssids) + return -EINVAL; + if (arg->n_bssids > WLAN_SCAN_PARAMS_MAX_BSSID) + return -EINVAL; + + len += sizeof(struct wmi_bssid_list); + len += sizeof(struct wmi_mac_addr) * arg->n_bssids; + } + + return len; +} + +int ath10k_wmi_start_scan(struct ath10k *ar, + const struct wmi_start_scan_arg *arg) +{ + struct wmi_start_scan_cmd *cmd; + struct sk_buff *skb; + struct wmi_ie_data *ie; + struct wmi_chan_list *channels; + struct wmi_ssid_list *ssids; + struct wmi_bssid_list *bssids; + u32 scan_id; + u32 scan_req_id; + int off; + int len = 0; + int i; + + len = ath10k_wmi_start_scan_calc_len(arg); + if (len < 0) + return len; /* len contains error code here */ + + skb = ath10k_wmi_alloc_skb(len); + if (!skb) + return -ENOMEM; + + scan_id = WMI_HOST_SCAN_REQ_ID_PREFIX; + scan_id |= arg->scan_id; + + scan_req_id = WMI_HOST_SCAN_REQUESTOR_ID_PREFIX; + scan_req_id |= arg->scan_req_id; + + cmd = (struct wmi_start_scan_cmd *)skb->data; + cmd->scan_id = __cpu_to_le32(scan_id); + cmd->scan_req_id = __cpu_to_le32(scan_req_id); + cmd->vdev_id = __cpu_to_le32(arg->vdev_id); + cmd->scan_priority = __cpu_to_le32(arg->scan_priority); + cmd->notify_scan_events = __cpu_to_le32(arg->notify_scan_events); + cmd->dwell_time_active = __cpu_to_le32(arg->dwell_time_active); + cmd->dwell_time_passive = __cpu_to_le32(arg->dwell_time_passive); + cmd->min_rest_time = __cpu_to_le32(arg->min_rest_time); + cmd->max_rest_time = __cpu_to_le32(arg->max_rest_time); + cmd->repeat_probe_time = __cpu_to_le32(arg->repeat_probe_time); + cmd->probe_spacing_time = __cpu_to_le32(arg->probe_spacing_time); + cmd->idle_time = __cpu_to_le32(arg->idle_time); + cmd->max_scan_time = __cpu_to_le32(arg->max_scan_time); + cmd->probe_delay = __cpu_to_le32(arg->probe_delay); + cmd->scan_ctrl_flags = __cpu_to_le32(arg->scan_ctrl_flags); + + /* TLV list starts after fields included in the struct */ + off = sizeof(*cmd); + + if (arg->n_channels) { + channels = (void *)skb->data + off; + channels->tag = __cpu_to_le32(WMI_CHAN_LIST_TAG); + channels->num_chan = __cpu_to_le32(arg->n_channels); + + for (i = 0; i < arg->n_channels; i++) + channels->channel_list[i] = + __cpu_to_le32(arg->channels[i]); + + off += sizeof(*channels); + off += sizeof(__le32) * arg->n_channels; + } + + if (arg->n_ssids) { + ssids = (void *)skb->data + off; + ssids->tag = __cpu_to_le32(WMI_SSID_LIST_TAG); + ssids->num_ssids = __cpu_to_le32(arg->n_ssids); + + for (i = 0; i < arg->n_ssids; i++) { + ssids->ssids[i].ssid_len = + __cpu_to_le32(arg->ssids[i].len); + memcpy(&ssids->ssids[i].ssid, + arg->ssids[i].ssid, + arg->ssids[i].len); + } + + off += sizeof(*ssids); + off += sizeof(struct wmi_ssid) * arg->n_ssids; + } + + if (arg->n_bssids) { + bssids = (void *)skb->data + off; + bssids->tag = __cpu_to_le32(WMI_BSSID_LIST_TAG); + bssids->num_bssid = __cpu_to_le32(arg->n_bssids); + + for (i = 0; i < arg->n_bssids; i++) + memcpy(&bssids->bssid_list[i], + arg->bssids[i].bssid, + ETH_ALEN); + + off += sizeof(*bssids); + off += sizeof(struct wmi_mac_addr) * arg->n_bssids; + } + + if (arg->ie_len) { + ie = (void *)skb->data + off; + ie->tag = __cpu_to_le32(WMI_IE_TAG); + ie->ie_len = __cpu_to_le32(arg->ie_len); + memcpy(ie->ie_data, arg->ie, arg->ie_len); + + off += sizeof(*ie); + off += roundup(arg->ie_len, 4); + } + + if (off != skb->len) { + dev_kfree_skb(skb); + return -EINVAL; + } + + ath10k_dbg(ATH10K_DBG_WMI, "wmi start scan\n"); + return ath10k_wmi_cmd_send(ar, skb, WMI_START_SCAN_CMDID); +} + +void ath10k_wmi_start_scan_init(struct ath10k *ar, + struct wmi_start_scan_arg *arg) +{ + /* setup commonly used values */ + arg->scan_req_id = 1; + arg->scan_priority = WMI_SCAN_PRIORITY_LOW; + arg->dwell_time_active = 50; + arg->dwell_time_passive = 150; + arg->min_rest_time = 50; + arg->max_rest_time = 500; + arg->repeat_probe_time = 0; + arg->probe_spacing_time = 0; + arg->idle_time = 0; + arg->max_scan_time = 5000; + arg->probe_delay = 5; + arg->notify_scan_events = WMI_SCAN_EVENT_STARTED + | WMI_SCAN_EVENT_COMPLETED + | WMI_SCAN_EVENT_BSS_CHANNEL + | WMI_SCAN_EVENT_FOREIGN_CHANNEL + | WMI_SCAN_EVENT_DEQUEUED; + arg->scan_ctrl_flags |= WMI_SCAN_ADD_OFDM_RATES; + arg->scan_ctrl_flags |= WMI_SCAN_CHAN_STAT_EVENT; + arg->n_bssids = 1; + arg->bssids[0].bssid = "\xFF\xFF\xFF\xFF\xFF\xFF"; +} + +int ath10k_wmi_stop_scan(struct ath10k *ar, const struct wmi_stop_scan_arg *arg) +{ + struct wmi_stop_scan_cmd *cmd; + struct sk_buff *skb; + u32 scan_id; + u32 req_id; + + if (arg->req_id > 0xFFF) + return -EINVAL; + if (arg->req_type == WMI_SCAN_STOP_ONE && arg->u.scan_id > 0xFFF) + return -EINVAL; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + scan_id = arg->u.scan_id; + scan_id |= WMI_HOST_SCAN_REQ_ID_PREFIX; + + req_id = arg->req_id; + req_id |= WMI_HOST_SCAN_REQUESTOR_ID_PREFIX; + + cmd = (struct wmi_stop_scan_cmd *)skb->data; + cmd->req_type = __cpu_to_le32(arg->req_type); + cmd->vdev_id = __cpu_to_le32(arg->u.vdev_id); + cmd->scan_id = __cpu_to_le32(scan_id); + cmd->scan_req_id = __cpu_to_le32(req_id); + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi stop scan reqid %d req_type %d vdev/scan_id %d\n", + arg->req_id, arg->req_type, arg->u.scan_id); + return ath10k_wmi_cmd_send(ar, skb, WMI_STOP_SCAN_CMDID); +} + +int ath10k_wmi_vdev_create(struct ath10k *ar, u32 vdev_id, + enum wmi_vdev_type type, + enum wmi_vdev_subtype subtype, + const u8 macaddr[ETH_ALEN]) +{ + struct wmi_vdev_create_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_vdev_create_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->vdev_type = __cpu_to_le32(type); + cmd->vdev_subtype = __cpu_to_le32(subtype); + memcpy(cmd->vdev_macaddr.addr, macaddr, ETH_ALEN); + + ath10k_dbg(ATH10K_DBG_WMI, + "WMI vdev create: id %d type %d subtype %d macaddr %pM\n", + vdev_id, type, subtype, macaddr); + + return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_CREATE_CMDID); +} + +int ath10k_wmi_vdev_delete(struct ath10k *ar, u32 vdev_id) +{ + struct wmi_vdev_delete_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_vdev_delete_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(vdev_id); + + ath10k_dbg(ATH10K_DBG_WMI, + "WMI vdev delete id %d\n", vdev_id); + + return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_DELETE_CMDID); +} + +static int ath10k_wmi_vdev_start_restart(struct ath10k *ar, + const struct wmi_vdev_start_request_arg *arg, + enum wmi_cmd_id cmd_id) +{ + struct wmi_vdev_start_request_cmd *cmd; + struct sk_buff *skb; + const char *cmdname; + u32 flags = 0; + + if (cmd_id != WMI_VDEV_START_REQUEST_CMDID && + cmd_id != WMI_VDEV_RESTART_REQUEST_CMDID) + return -EINVAL; + if (WARN_ON(arg->ssid && arg->ssid_len == 0)) + return -EINVAL; + if (WARN_ON(arg->hidden_ssid && !arg->ssid)) + return -EINVAL; + if (WARN_ON(arg->ssid_len > sizeof(cmd->ssid.ssid))) + return -EINVAL; + + if (cmd_id == WMI_VDEV_START_REQUEST_CMDID) + cmdname = "start"; + else if (cmd_id == WMI_VDEV_RESTART_REQUEST_CMDID) + cmdname = "restart"; + else + return -EINVAL; /* should not happen, we already check cmd_id */ + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + if (arg->hidden_ssid) + flags |= WMI_VDEV_START_HIDDEN_SSID; + if (arg->pmf_enabled) + flags |= WMI_VDEV_START_PMF_ENABLED; + + cmd = (struct wmi_vdev_start_request_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(arg->vdev_id); + cmd->disable_hw_ack = __cpu_to_le32(arg->disable_hw_ack); + cmd->beacon_interval = __cpu_to_le32(arg->bcn_intval); + cmd->dtim_period = __cpu_to_le32(arg->dtim_period); + cmd->flags = __cpu_to_le32(flags); + cmd->bcn_tx_rate = __cpu_to_le32(arg->bcn_tx_rate); + cmd->bcn_tx_power = __cpu_to_le32(arg->bcn_tx_power); + + if (arg->ssid) { + cmd->ssid.ssid_len = __cpu_to_le32(arg->ssid_len); + memcpy(cmd->ssid.ssid, arg->ssid, arg->ssid_len); + } + + cmd->chan.mhz = __cpu_to_le32(arg->channel.freq); + + cmd->chan.band_center_freq1 = + __cpu_to_le32(arg->channel.band_center_freq1); + + cmd->chan.mode = arg->channel.mode; + cmd->chan.min_power = arg->channel.min_power; + cmd->chan.max_power = arg->channel.max_power; + cmd->chan.reg_power = arg->channel.max_reg_power; + cmd->chan.reg_classid = arg->channel.reg_class_id; + cmd->chan.antenna_max = arg->channel.max_antenna_gain; + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi vdev %s id 0x%x freq %d, mode %d, ch_flags: 0x%0X," + "max_power: %d\n", cmdname, arg->vdev_id, arg->channel.freq, + arg->channel.mode, flags, arg->channel.max_power); + + return ath10k_wmi_cmd_send(ar, skb, cmd_id); +} + +int ath10k_wmi_vdev_start(struct ath10k *ar, + const struct wmi_vdev_start_request_arg *arg) +{ + return ath10k_wmi_vdev_start_restart(ar, arg, + WMI_VDEV_START_REQUEST_CMDID); +} + +int ath10k_wmi_vdev_restart(struct ath10k *ar, + const struct wmi_vdev_start_request_arg *arg) +{ + return ath10k_wmi_vdev_start_restart(ar, arg, + WMI_VDEV_RESTART_REQUEST_CMDID); +} + +int ath10k_wmi_vdev_stop(struct ath10k *ar, u32 vdev_id) +{ + struct wmi_vdev_stop_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_vdev_stop_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(vdev_id); + + ath10k_dbg(ATH10K_DBG_WMI, "wmi vdev stop id 0x%x\n", vdev_id); + + return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_STOP_CMDID); +} + +int ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, const u8 *bssid) +{ + struct wmi_vdev_up_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_vdev_up_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->vdev_assoc_id = __cpu_to_le32(aid); + memcpy(&cmd->vdev_bssid.addr, bssid, 6); + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi mgmt vdev up id 0x%x assoc id %d bssid %pM\n", + vdev_id, aid, bssid); + + return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_UP_CMDID); +} + +int ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id) +{ + struct wmi_vdev_down_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_vdev_down_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(vdev_id); + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi mgmt vdev down id 0x%x\n", vdev_id); + + return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_DOWN_CMDID); +} + +int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id, + enum wmi_vdev_param param_id, u32 param_value) +{ + struct wmi_vdev_set_param_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_vdev_set_param_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->param_id = __cpu_to_le32(param_id); + cmd->param_value = __cpu_to_le32(param_value); + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi vdev id 0x%x set param %d value %d\n", + vdev_id, param_id, param_value); + + return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_SET_PARAM_CMDID); +} + +int ath10k_wmi_vdev_install_key(struct ath10k *ar, + const struct wmi_vdev_install_key_arg *arg) +{ + struct wmi_vdev_install_key_cmd *cmd; + struct sk_buff *skb; + + if (arg->key_cipher == WMI_CIPHER_NONE && arg->key_data != NULL) + return -EINVAL; + if (arg->key_cipher != WMI_CIPHER_NONE && arg->key_data == NULL) + return -EINVAL; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd) + arg->key_len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_vdev_install_key_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(arg->vdev_id); + cmd->key_idx = __cpu_to_le32(arg->key_idx); + cmd->key_flags = __cpu_to_le32(arg->key_flags); + cmd->key_cipher = __cpu_to_le32(arg->key_cipher); + cmd->key_len = __cpu_to_le32(arg->key_len); + cmd->key_txmic_len = __cpu_to_le32(arg->key_txmic_len); + cmd->key_rxmic_len = __cpu_to_le32(arg->key_rxmic_len); + + if (arg->macaddr) + memcpy(cmd->peer_macaddr.addr, arg->macaddr, ETH_ALEN); + if (arg->key_data) + memcpy(cmd->key_data, arg->key_data, arg->key_len); + + return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_INSTALL_KEY_CMDID); +} + +int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id, + const u8 peer_addr[ETH_ALEN]) +{ + struct wmi_peer_create_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_peer_create_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(vdev_id); + memcpy(cmd->peer_macaddr.addr, peer_addr, ETH_ALEN); + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi peer create vdev_id %d peer_addr %pM\n", + vdev_id, peer_addr); + return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_CREATE_CMDID); +} + +int ath10k_wmi_peer_delete(struct ath10k *ar, u32 vdev_id, + const u8 peer_addr[ETH_ALEN]) +{ + struct wmi_peer_delete_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_peer_delete_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(vdev_id); + memcpy(cmd->peer_macaddr.addr, peer_addr, ETH_ALEN); + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi peer delete vdev_id %d peer_addr %pM\n", + vdev_id, peer_addr); + return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_DELETE_CMDID); +} + +int ath10k_wmi_peer_flush(struct ath10k *ar, u32 vdev_id, + const u8 peer_addr[ETH_ALEN], u32 tid_bitmap) +{ + struct wmi_peer_flush_tids_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_peer_flush_tids_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->peer_tid_bitmap = __cpu_to_le32(tid_bitmap); + memcpy(cmd->peer_macaddr.addr, peer_addr, ETH_ALEN); + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi peer flush vdev_id %d peer_addr %pM tids %08x\n", + vdev_id, peer_addr, tid_bitmap); + return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_FLUSH_TIDS_CMDID); +} + +int ath10k_wmi_peer_set_param(struct ath10k *ar, u32 vdev_id, + const u8 *peer_addr, enum wmi_peer_param param_id, + u32 param_value) +{ + struct wmi_peer_set_param_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_peer_set_param_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->param_id = __cpu_to_le32(param_id); + cmd->param_value = __cpu_to_le32(param_value); + memcpy(&cmd->peer_macaddr.addr, peer_addr, 6); + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi vdev %d peer 0x%pM set param %d value %d\n", + vdev_id, peer_addr, param_id, param_value); + + return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_SET_PARAM_CMDID); +} + +int ath10k_wmi_set_psmode(struct ath10k *ar, u32 vdev_id, + enum wmi_sta_ps_mode psmode) +{ + struct wmi_sta_powersave_mode_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_sta_powersave_mode_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->sta_ps_mode = __cpu_to_le32(psmode); + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi set powersave id 0x%x mode %d\n", + vdev_id, psmode); + + return ath10k_wmi_cmd_send(ar, skb, WMI_STA_POWERSAVE_MODE_CMDID); +} + +int ath10k_wmi_set_sta_ps_param(struct ath10k *ar, u32 vdev_id, + enum wmi_sta_powersave_param param_id, + u32 value) +{ + struct wmi_sta_powersave_param_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_sta_powersave_param_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->param_id = __cpu_to_le32(param_id); + cmd->param_value = __cpu_to_le32(value); + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi sta ps param vdev_id 0x%x param %d value %d\n", + vdev_id, param_id, value); + return ath10k_wmi_cmd_send(ar, skb, WMI_STA_POWERSAVE_PARAM_CMDID); +} + +int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac, + enum wmi_ap_ps_peer_param param_id, u32 value) +{ + struct wmi_ap_ps_peer_cmd *cmd; + struct sk_buff *skb; + + if (!mac) + return -EINVAL; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_ap_ps_peer_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->param_id = __cpu_to_le32(param_id); + cmd->param_value = __cpu_to_le32(value); + memcpy(&cmd->peer_macaddr, mac, ETH_ALEN); + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi ap ps param vdev_id 0x%X param %d value %d mac_addr %pM\n", + vdev_id, param_id, value, mac); + + return ath10k_wmi_cmd_send(ar, skb, WMI_AP_PS_PEER_PARAM_CMDID); +} + +int ath10k_wmi_scan_chan_list(struct ath10k *ar, + const struct wmi_scan_chan_list_arg *arg) +{ + struct wmi_scan_chan_list_cmd *cmd; + struct sk_buff *skb; + struct wmi_channel_arg *ch; + struct wmi_channel *ci; + int len; + int i; + + len = sizeof(*cmd) + arg->n_channels * sizeof(struct wmi_channel); + + skb = ath10k_wmi_alloc_skb(len); + if (!skb) + return -EINVAL; + + cmd = (struct wmi_scan_chan_list_cmd *)skb->data; + cmd->num_scan_chans = __cpu_to_le32(arg->n_channels); + + for (i = 0; i < arg->n_channels; i++) { + u32 flags = 0; + + ch = &arg->channels[i]; + ci = &cmd->chan_info[i]; + + if (ch->passive) + flags |= WMI_CHAN_FLAG_PASSIVE; + if (ch->allow_ibss) + flags |= WMI_CHAN_FLAG_ADHOC_ALLOWED; + if (ch->allow_ht) + flags |= WMI_CHAN_FLAG_ALLOW_HT; + if (ch->allow_vht) + flags |= WMI_CHAN_FLAG_ALLOW_VHT; + if (ch->ht40plus) + flags |= WMI_CHAN_FLAG_HT40_PLUS; + + ci->mhz = __cpu_to_le32(ch->freq); + ci->band_center_freq1 = __cpu_to_le32(ch->freq); + ci->band_center_freq2 = 0; + ci->min_power = ch->min_power; + ci->max_power = ch->max_power; + ci->reg_power = ch->max_reg_power; + ci->antenna_max = ch->max_antenna_gain; + ci->antenna_max = 0; + + /* mode & flags share storage */ + ci->mode = ch->mode; + ci->flags |= __cpu_to_le32(flags); + } + + return ath10k_wmi_cmd_send(ar, skb, WMI_SCAN_CHAN_LIST_CMDID); +} + +int ath10k_wmi_peer_assoc(struct ath10k *ar, + const struct wmi_peer_assoc_complete_arg *arg) +{ + struct wmi_peer_assoc_complete_cmd *cmd; + struct sk_buff *skb; + + if (arg->peer_mpdu_density > 16) + return -EINVAL; + if (arg->peer_legacy_rates.num_rates > MAX_SUPPORTED_RATES) + return -EINVAL; + if (arg->peer_ht_rates.num_rates > MAX_SUPPORTED_RATES) + return -EINVAL; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_peer_assoc_complete_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(arg->vdev_id); + cmd->peer_new_assoc = __cpu_to_le32(arg->peer_reassoc ? 0 : 1); + cmd->peer_associd = __cpu_to_le32(arg->peer_aid); + cmd->peer_flags = __cpu_to_le32(arg->peer_flags); + cmd->peer_caps = __cpu_to_le32(arg->peer_caps); + cmd->peer_listen_intval = __cpu_to_le32(arg->peer_listen_intval); + cmd->peer_ht_caps = __cpu_to_le32(arg->peer_ht_caps); + cmd->peer_max_mpdu = __cpu_to_le32(arg->peer_max_mpdu); + cmd->peer_mpdu_density = __cpu_to_le32(arg->peer_mpdu_density); + cmd->peer_rate_caps = __cpu_to_le32(arg->peer_rate_caps); + cmd->peer_nss = __cpu_to_le32(arg->peer_num_spatial_streams); + cmd->peer_vht_caps = __cpu_to_le32(arg->peer_vht_caps); + cmd->peer_phymode = __cpu_to_le32(arg->peer_phymode); + + memcpy(cmd->peer_macaddr.addr, arg->addr, ETH_ALEN); + + cmd->peer_legacy_rates.num_rates = + __cpu_to_le32(arg->peer_legacy_rates.num_rates); + memcpy(cmd->peer_legacy_rates.rates, arg->peer_legacy_rates.rates, + arg->peer_legacy_rates.num_rates); + + cmd->peer_ht_rates.num_rates = + __cpu_to_le32(arg->peer_ht_rates.num_rates); + memcpy(cmd->peer_ht_rates.rates, arg->peer_ht_rates.rates, + arg->peer_ht_rates.num_rates); + + cmd->peer_vht_rates.rx_max_rate = + __cpu_to_le32(arg->peer_vht_rates.rx_max_rate); + cmd->peer_vht_rates.rx_mcs_set = + __cpu_to_le32(arg->peer_vht_rates.rx_mcs_set); + cmd->peer_vht_rates.tx_max_rate = + __cpu_to_le32(arg->peer_vht_rates.tx_max_rate); + cmd->peer_vht_rates.tx_mcs_set = + __cpu_to_le32(arg->peer_vht_rates.tx_mcs_set); + + return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_ASSOC_CMDID); +} + +int ath10k_wmi_beacon_send(struct ath10k *ar, const struct wmi_bcn_tx_arg *arg) +{ + struct wmi_bcn_tx_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd) + arg->bcn_len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_bcn_tx_cmd *)skb->data; + cmd->hdr.vdev_id = __cpu_to_le32(arg->vdev_id); + cmd->hdr.tx_rate = __cpu_to_le32(arg->tx_rate); + cmd->hdr.tx_power = __cpu_to_le32(arg->tx_power); + cmd->hdr.bcn_len = __cpu_to_le32(arg->bcn_len); + memcpy(cmd->bcn, arg->bcn, arg->bcn_len); + + return ath10k_wmi_cmd_send(ar, skb, WMI_BCN_TX_CMDID); +} + +static void ath10k_wmi_pdev_set_wmm_param(struct wmi_wmm_params *params, + const struct wmi_wmm_params_arg *arg) +{ + params->cwmin = __cpu_to_le32(arg->cwmin); + params->cwmax = __cpu_to_le32(arg->cwmax); + params->aifs = __cpu_to_le32(arg->aifs); + params->txop = __cpu_to_le32(arg->txop); + params->acm = __cpu_to_le32(arg->acm); + params->no_ack = __cpu_to_le32(arg->no_ack); +} + +int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar, + const struct wmi_pdev_set_wmm_params_arg *arg) +{ + struct wmi_pdev_set_wmm_params *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_pdev_set_wmm_params *)skb->data; + ath10k_wmi_pdev_set_wmm_param(&cmd->ac_be, &arg->ac_be); + ath10k_wmi_pdev_set_wmm_param(&cmd->ac_bk, &arg->ac_bk); + ath10k_wmi_pdev_set_wmm_param(&cmd->ac_vi, &arg->ac_vi); + ath10k_wmi_pdev_set_wmm_param(&cmd->ac_vo, &arg->ac_vo); + + ath10k_dbg(ATH10K_DBG_WMI, "wmi pdev set wmm params\n"); + return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_WMM_PARAMS_CMDID); +} + +int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id) +{ + struct wmi_request_stats_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_request_stats_cmd *)skb->data; + cmd->stats_id = __cpu_to_le32(stats_id); + + ath10k_dbg(ATH10K_DBG_WMI, "wmi request stats %d\n", (int)stats_id); + return ath10k_wmi_cmd_send(ar, skb, WMI_REQUEST_STATS_CMDID); +} diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h new file mode 100644 index 000000000000..9555f5a0e041 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -0,0 +1,3052 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _WMI_H_ +#define _WMI_H_ + +#include +#include + +/* + * This file specifies the WMI interface for the Unified Software + * Architecture. + * + * It includes definitions of all the commands and events. Commands are + * messages from the host to the target. Events and Replies are messages + * from the target to the host. + * + * Ownership of correctness in regards to WMI commands belongs to the host + * driver and the target is not required to validate parameters for value, + * proper range, or any other checking. + * + * Guidelines for extending this interface are below. + * + * 1. Add new WMI commands ONLY within the specified range - 0x9000 - 0x9fff + * + * 2. Use ONLY u32 type for defining member variables within WMI + * command/event structures. Do not use u8, u16, bool or + * enum types within these structures. + * + * 3. DO NOT define bit fields within structures. Implement bit fields + * using masks if necessary. Do not use the programming language's bit + * field definition. + * + * 4. Define macros for encode/decode of u8, u16 fields within + * the u32 variables. Use these macros for set/get of these fields. + * Try to use this to optimize the structure without bloating it with + * u32 variables for every lower sized field. + * + * 5. Do not use PACK/UNPACK attributes for the structures as each member + * variable is already 4-byte aligned by virtue of being a u32 + * type. + * + * 6. Comment each parameter part of the WMI command/event structure by + * using the 2 stars at the begining of C comment instead of one star to + * enable HTML document generation using Doxygen. + * + */ + +/* Control Path */ +struct wmi_cmd_hdr { + __le32 cmd_id; +} __packed; + +#define WMI_CMD_HDR_CMD_ID_MASK 0x00FFFFFF +#define WMI_CMD_HDR_CMD_ID_LSB 0 +#define WMI_CMD_HDR_PLT_PRIV_MASK 0xFF000000 +#define WMI_CMD_HDR_PLT_PRIV_LSB 24 + +#define HTC_PROTOCOL_VERSION 0x0002 +#define WMI_PROTOCOL_VERSION 0x0002 + +enum wmi_service_id { + WMI_SERVICE_BEACON_OFFLOAD = 0, /* beacon offload */ + WMI_SERVICE_SCAN_OFFLOAD, /* scan offload */ + WMI_SERVICE_ROAM_OFFLOAD, /* roam offload */ + WMI_SERVICE_BCN_MISS_OFFLOAD, /* beacon miss offload */ + WMI_SERVICE_STA_PWRSAVE, /* fake sleep + basic power save */ + WMI_SERVICE_STA_ADVANCED_PWRSAVE, /* uapsd, pspoll, force sleep */ + WMI_SERVICE_AP_UAPSD, /* uapsd on AP */ + WMI_SERVICE_AP_DFS, /* DFS on AP */ + WMI_SERVICE_11AC, /* supports 11ac */ + WMI_SERVICE_BLOCKACK, /* Supports triggering ADDBA/DELBA from host*/ + WMI_SERVICE_PHYERR, /* PHY error */ + WMI_SERVICE_BCN_FILTER, /* Beacon filter support */ + WMI_SERVICE_RTT, /* RTT (round trip time) support */ + WMI_SERVICE_RATECTRL, /* Rate-control */ + WMI_SERVICE_WOW, /* WOW Support */ + WMI_SERVICE_RATECTRL_CACHE, /* Rate-control caching */ + WMI_SERVICE_IRAM_TIDS, /* TIDs in IRAM */ + WMI_SERVICE_ARPNS_OFFLOAD, /* ARP NS Offload support */ + WMI_SERVICE_NLO, /* Network list offload service */ + WMI_SERVICE_GTK_OFFLOAD, /* GTK offload */ + WMI_SERVICE_SCAN_SCH, /* Scan Scheduler Service */ + WMI_SERVICE_CSA_OFFLOAD, /* CSA offload service */ + WMI_SERVICE_CHATTER, /* Chatter service */ + WMI_SERVICE_COEX_FREQAVOID, /* FW report freq range to avoid */ + WMI_SERVICE_PACKET_POWER_SAVE, /* packet power save service */ + WMI_SERVICE_FORCE_FW_HANG, /* To test fw recovery mechanism */ + WMI_SERVICE_GPIO, /* GPIO service */ + WMI_SERVICE_STA_DTIM_PS_MODULATED_DTIM, /* Modulated DTIM support */ + WMI_STA_UAPSD_BASIC_AUTO_TRIG, /* UAPSD AC Trigger Generation */ + WMI_STA_UAPSD_VAR_AUTO_TRIG, /* -do- */ + WMI_SERVICE_STA_KEEP_ALIVE, /* STA keep alive mechanism support */ + WMI_SERVICE_TX_ENCAP, /* Packet type for TX encapsulation */ + + WMI_SERVICE_LAST, + WMI_MAX_SERVICE = 64 /* max service */ +}; + +static inline char *wmi_service_name(int service_id) +{ + switch (service_id) { + case WMI_SERVICE_BEACON_OFFLOAD: + return "BEACON_OFFLOAD"; + case WMI_SERVICE_SCAN_OFFLOAD: + return "SCAN_OFFLOAD"; + case WMI_SERVICE_ROAM_OFFLOAD: + return "ROAM_OFFLOAD"; + case WMI_SERVICE_BCN_MISS_OFFLOAD: + return "BCN_MISS_OFFLOAD"; + case WMI_SERVICE_STA_PWRSAVE: + return "STA_PWRSAVE"; + case WMI_SERVICE_STA_ADVANCED_PWRSAVE: + return "STA_ADVANCED_PWRSAVE"; + case WMI_SERVICE_AP_UAPSD: + return "AP_UAPSD"; + case WMI_SERVICE_AP_DFS: + return "AP_DFS"; + case WMI_SERVICE_11AC: + return "11AC"; + case WMI_SERVICE_BLOCKACK: + return "BLOCKACK"; + case WMI_SERVICE_PHYERR: + return "PHYERR"; + case WMI_SERVICE_BCN_FILTER: + return "BCN_FILTER"; + case WMI_SERVICE_RTT: + return "RTT"; + case WMI_SERVICE_RATECTRL: + return "RATECTRL"; + case WMI_SERVICE_WOW: + return "WOW"; + case WMI_SERVICE_RATECTRL_CACHE: + return "RATECTRL CACHE"; + case WMI_SERVICE_IRAM_TIDS: + return "IRAM TIDS"; + case WMI_SERVICE_ARPNS_OFFLOAD: + return "ARPNS_OFFLOAD"; + case WMI_SERVICE_NLO: + return "NLO"; + case WMI_SERVICE_GTK_OFFLOAD: + return "GTK_OFFLOAD"; + case WMI_SERVICE_SCAN_SCH: + return "SCAN_SCH"; + case WMI_SERVICE_CSA_OFFLOAD: + return "CSA_OFFLOAD"; + case WMI_SERVICE_CHATTER: + return "CHATTER"; + case WMI_SERVICE_COEX_FREQAVOID: + return "COEX_FREQAVOID"; + case WMI_SERVICE_PACKET_POWER_SAVE: + return "PACKET_POWER_SAVE"; + case WMI_SERVICE_FORCE_FW_HANG: + return "FORCE FW HANG"; + case WMI_SERVICE_GPIO: + return "GPIO"; + case WMI_SERVICE_STA_DTIM_PS_MODULATED_DTIM: + return "MODULATED DTIM"; + case WMI_STA_UAPSD_BASIC_AUTO_TRIG: + return "BASIC UAPSD"; + case WMI_STA_UAPSD_VAR_AUTO_TRIG: + return "VAR UAPSD"; + case WMI_SERVICE_STA_KEEP_ALIVE: + return "STA KEEP ALIVE"; + case WMI_SERVICE_TX_ENCAP: + return "TX ENCAP"; + default: + return "UNKNOWN SERVICE\n"; + } +} + + +#define WMI_SERVICE_BM_SIZE \ + ((WMI_MAX_SERVICE + sizeof(u32) - 1)/sizeof(u32)) + +/* 2 word representation of MAC addr */ +struct wmi_mac_addr { + union { + u8 addr[6]; + struct { + u32 word0; + u32 word1; + } __packed; + } __packed; +} __packed; + +/* macro to convert MAC address from WMI word format to char array */ +#define WMI_MAC_ADDR_TO_CHAR_ARRAY(pwmi_mac_addr, c_macaddr) do { \ + (c_macaddr)[0] = ((pwmi_mac_addr)->word0) & 0xff; \ + (c_macaddr)[1] = (((pwmi_mac_addr)->word0) >> 8) & 0xff; \ + (c_macaddr)[2] = (((pwmi_mac_addr)->word0) >> 16) & 0xff; \ + (c_macaddr)[3] = (((pwmi_mac_addr)->word0) >> 24) & 0xff; \ + (c_macaddr)[4] = ((pwmi_mac_addr)->word1) & 0xff; \ + (c_macaddr)[5] = (((pwmi_mac_addr)->word1) >> 8) & 0xff; \ + } while (0) + +/* + * wmi command groups. + */ +enum wmi_cmd_group { + /* 0 to 2 are reserved */ + WMI_GRP_START = 0x3, + WMI_GRP_SCAN = WMI_GRP_START, + WMI_GRP_PDEV, + WMI_GRP_VDEV, + WMI_GRP_PEER, + WMI_GRP_MGMT, + WMI_GRP_BA_NEG, + WMI_GRP_STA_PS, + WMI_GRP_DFS, + WMI_GRP_ROAM, + WMI_GRP_OFL_SCAN, + WMI_GRP_P2P, + WMI_GRP_AP_PS, + WMI_GRP_RATE_CTRL, + WMI_GRP_PROFILE, + WMI_GRP_SUSPEND, + WMI_GRP_BCN_FILTER, + WMI_GRP_WOW, + WMI_GRP_RTT, + WMI_GRP_SPECTRAL, + WMI_GRP_STATS, + WMI_GRP_ARP_NS_OFL, + WMI_GRP_NLO_OFL, + WMI_GRP_GTK_OFL, + WMI_GRP_CSA_OFL, + WMI_GRP_CHATTER, + WMI_GRP_TID_ADDBA, + WMI_GRP_MISC, + WMI_GRP_GPIO, +}; + +#define WMI_CMD_GRP(grp_id) (((grp_id) << 12) | 0x1) +#define WMI_EVT_GRP_START_ID(grp_id) (((grp_id) << 12) | 0x1) + +/* Command IDs and commande events. */ +enum wmi_cmd_id { + WMI_INIT_CMDID = 0x1, + + /* Scan specific commands */ + WMI_START_SCAN_CMDID = WMI_CMD_GRP(WMI_GRP_SCAN), + WMI_STOP_SCAN_CMDID, + WMI_SCAN_CHAN_LIST_CMDID, + WMI_SCAN_SCH_PRIO_TBL_CMDID, + + /* PDEV (physical device) specific commands */ + WMI_PDEV_SET_REGDOMAIN_CMDID = WMI_CMD_GRP(WMI_GRP_PDEV), + WMI_PDEV_SET_CHANNEL_CMDID, + WMI_PDEV_SET_PARAM_CMDID, + WMI_PDEV_PKTLOG_ENABLE_CMDID, + WMI_PDEV_PKTLOG_DISABLE_CMDID, + WMI_PDEV_SET_WMM_PARAMS_CMDID, + WMI_PDEV_SET_HT_CAP_IE_CMDID, + WMI_PDEV_SET_VHT_CAP_IE_CMDID, + WMI_PDEV_SET_DSCP_TID_MAP_CMDID, + WMI_PDEV_SET_QUIET_MODE_CMDID, + WMI_PDEV_GREEN_AP_PS_ENABLE_CMDID, + WMI_PDEV_GET_TPC_CONFIG_CMDID, + WMI_PDEV_SET_BASE_MACADDR_CMDID, + + /* VDEV (virtual device) specific commands */ + WMI_VDEV_CREATE_CMDID = WMI_CMD_GRP(WMI_GRP_VDEV), + WMI_VDEV_DELETE_CMDID, + WMI_VDEV_START_REQUEST_CMDID, + WMI_VDEV_RESTART_REQUEST_CMDID, + WMI_VDEV_UP_CMDID, + WMI_VDEV_STOP_CMDID, + WMI_VDEV_DOWN_CMDID, + WMI_VDEV_SET_PARAM_CMDID, + WMI_VDEV_INSTALL_KEY_CMDID, + + /* peer specific commands */ + WMI_PEER_CREATE_CMDID = WMI_CMD_GRP(WMI_GRP_PEER), + WMI_PEER_DELETE_CMDID, + WMI_PEER_FLUSH_TIDS_CMDID, + WMI_PEER_SET_PARAM_CMDID, + WMI_PEER_ASSOC_CMDID, + WMI_PEER_ADD_WDS_ENTRY_CMDID, + WMI_PEER_REMOVE_WDS_ENTRY_CMDID, + WMI_PEER_MCAST_GROUP_CMDID, + + /* beacon/management specific commands */ + WMI_BCN_TX_CMDID = WMI_CMD_GRP(WMI_GRP_MGMT), + WMI_PDEV_SEND_BCN_CMDID, + WMI_BCN_TMPL_CMDID, + WMI_BCN_FILTER_RX_CMDID, + WMI_PRB_REQ_FILTER_RX_CMDID, + WMI_MGMT_TX_CMDID, + WMI_PRB_TMPL_CMDID, + + /* commands to directly control BA negotiation directly from host. */ + WMI_ADDBA_CLEAR_RESP_CMDID = WMI_CMD_GRP(WMI_GRP_BA_NEG), + WMI_ADDBA_SEND_CMDID, + WMI_ADDBA_STATUS_CMDID, + WMI_DELBA_SEND_CMDID, + WMI_ADDBA_SET_RESP_CMDID, + WMI_SEND_SINGLEAMSDU_CMDID, + + /* Station power save specific config */ + WMI_STA_POWERSAVE_MODE_CMDID = WMI_CMD_GRP(WMI_GRP_STA_PS), + WMI_STA_POWERSAVE_PARAM_CMDID, + WMI_STA_MIMO_PS_MODE_CMDID, + + /** DFS-specific commands */ + WMI_PDEV_DFS_ENABLE_CMDID = WMI_CMD_GRP(WMI_GRP_DFS), + WMI_PDEV_DFS_DISABLE_CMDID, + + /* Roaming specific commands */ + WMI_ROAM_SCAN_MODE = WMI_CMD_GRP(WMI_GRP_ROAM), + WMI_ROAM_SCAN_RSSI_THRESHOLD, + WMI_ROAM_SCAN_PERIOD, + WMI_ROAM_SCAN_RSSI_CHANGE_THRESHOLD, + WMI_ROAM_AP_PROFILE, + + /* offload scan specific commands */ + WMI_OFL_SCAN_ADD_AP_PROFILE = WMI_CMD_GRP(WMI_GRP_OFL_SCAN), + WMI_OFL_SCAN_REMOVE_AP_PROFILE, + WMI_OFL_SCAN_PERIOD, + + /* P2P specific commands */ + WMI_P2P_DEV_SET_DEVICE_INFO = WMI_CMD_GRP(WMI_GRP_P2P), + WMI_P2P_DEV_SET_DISCOVERABILITY, + WMI_P2P_GO_SET_BEACON_IE, + WMI_P2P_GO_SET_PROBE_RESP_IE, + WMI_P2P_SET_VENDOR_IE_DATA_CMDID, + + /* AP power save specific config */ + WMI_AP_PS_PEER_PARAM_CMDID = WMI_CMD_GRP(WMI_GRP_AP_PS), + WMI_AP_PS_PEER_UAPSD_COEX_CMDID, + + /* Rate-control specific commands */ + WMI_PEER_RATE_RETRY_SCHED_CMDID = + WMI_CMD_GRP(WMI_GRP_RATE_CTRL), + + /* WLAN Profiling commands. */ + WMI_WLAN_PROFILE_TRIGGER_CMDID = WMI_CMD_GRP(WMI_GRP_PROFILE), + WMI_WLAN_PROFILE_SET_HIST_INTVL_CMDID, + WMI_WLAN_PROFILE_GET_PROFILE_DATA_CMDID, + WMI_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID, + WMI_WLAN_PROFILE_LIST_PROFILE_ID_CMDID, + + /* Suspend resume command Ids */ + WMI_PDEV_SUSPEND_CMDID = WMI_CMD_GRP(WMI_GRP_SUSPEND), + WMI_PDEV_RESUME_CMDID, + + /* Beacon filter commands */ + WMI_ADD_BCN_FILTER_CMDID = WMI_CMD_GRP(WMI_GRP_BCN_FILTER), + WMI_RMV_BCN_FILTER_CMDID, + + /* WOW Specific WMI commands*/ + WMI_WOW_ADD_WAKE_PATTERN_CMDID = WMI_CMD_GRP(WMI_GRP_WOW), + WMI_WOW_DEL_WAKE_PATTERN_CMDID, + WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID, + WMI_WOW_ENABLE_CMDID, + WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID, + + /* RTT measurement related cmd */ + WMI_RTT_MEASREQ_CMDID = WMI_CMD_GRP(WMI_GRP_RTT), + WMI_RTT_TSF_CMDID, + + /* spectral scan commands */ + WMI_VDEV_SPECTRAL_SCAN_CONFIGURE_CMDID = WMI_CMD_GRP(WMI_GRP_SPECTRAL), + WMI_VDEV_SPECTRAL_SCAN_ENABLE_CMDID, + + /* F/W stats */ + WMI_REQUEST_STATS_CMDID = WMI_CMD_GRP(WMI_GRP_STATS), + + /* ARP OFFLOAD REQUEST*/ + WMI_SET_ARP_NS_OFFLOAD_CMDID = WMI_CMD_GRP(WMI_GRP_ARP_NS_OFL), + + /* NS offload confid*/ + WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID = WMI_CMD_GRP(WMI_GRP_NLO_OFL), + + /* GTK offload Specific WMI commands*/ + WMI_GTK_OFFLOAD_CMDID = WMI_CMD_GRP(WMI_GRP_GTK_OFL), + + /* CSA offload Specific WMI commands*/ + WMI_CSA_OFFLOAD_ENABLE_CMDID = WMI_CMD_GRP(WMI_GRP_CSA_OFL), + WMI_CSA_OFFLOAD_CHANSWITCH_CMDID, + + /* Chatter commands*/ + WMI_CHATTER_SET_MODE_CMDID = WMI_CMD_GRP(WMI_GRP_CHATTER), + + /* addba specific commands */ + WMI_PEER_TID_ADDBA_CMDID = WMI_CMD_GRP(WMI_GRP_TID_ADDBA), + WMI_PEER_TID_DELBA_CMDID, + + /* set station mimo powersave method */ + WMI_STA_DTIM_PS_METHOD_CMDID, + /* Configure the Station UAPSD AC Auto Trigger Parameters */ + WMI_STA_UAPSD_AUTO_TRIG_CMDID, + + /* STA Keep alive parameter configuration, + Requires WMI_SERVICE_STA_KEEP_ALIVE */ + WMI_STA_KEEPALIVE_CMD, + + /* misc command group */ + WMI_ECHO_CMDID = WMI_CMD_GRP(WMI_GRP_MISC), + WMI_PDEV_UTF_CMDID, + WMI_DBGLOG_CFG_CMDID, + WMI_PDEV_QVIT_CMDID, + WMI_PDEV_FTM_INTG_CMDID, + WMI_VDEV_SET_KEEPALIVE_CMDID, + WMI_VDEV_GET_KEEPALIVE_CMDID, + + /* GPIO Configuration */ + WMI_GPIO_CONFIG_CMDID = WMI_CMD_GRP(WMI_GRP_GPIO), + WMI_GPIO_OUTPUT_CMDID, +}; + +enum wmi_event_id { + WMI_SERVICE_READY_EVENTID = 0x1, + WMI_READY_EVENTID, + + /* Scan specific events */ + WMI_SCAN_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_SCAN), + + /* PDEV specific events */ + WMI_PDEV_TPC_CONFIG_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_PDEV), + WMI_CHAN_INFO_EVENTID, + WMI_PHYERR_EVENTID, + + /* VDEV specific events */ + WMI_VDEV_START_RESP_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_VDEV), + WMI_VDEV_STOPPED_EVENTID, + WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID, + + /* peer specific events */ + WMI_PEER_STA_KICKOUT_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_PEER), + + /* beacon/mgmt specific events */ + WMI_MGMT_RX_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_MGMT), + WMI_HOST_SWBA_EVENTID, + WMI_TBTTOFFSET_UPDATE_EVENTID, + + /* ADDBA Related WMI Events*/ + WMI_TX_DELBA_COMPLETE_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_BA_NEG), + WMI_TX_ADDBA_COMPLETE_EVENTID, + + /* Roam event to trigger roaming on host */ + WMI_ROAM_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_ROAM), + WMI_PROFILE_MATCH, + + /* WoW */ + WMI_WOW_WAKEUP_HOST_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_WOW), + + /* RTT */ + WMI_RTT_MEASUREMENT_REPORT_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_RTT), + WMI_TSF_MEASUREMENT_REPORT_EVENTID, + WMI_RTT_ERROR_REPORT_EVENTID, + + /* GTK offload */ + WMI_GTK_OFFLOAD_STATUS_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_GTK_OFL), + WMI_GTK_REKEY_FAIL_EVENTID, + + /* CSA IE received event */ + WMI_CSA_HANDLING_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_CSA_OFL), + + /* Misc events */ + WMI_ECHO_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_MISC), + WMI_PDEV_UTF_EVENTID, + WMI_DEBUG_MESG_EVENTID, + WMI_UPDATE_STATS_EVENTID, + WMI_DEBUG_PRINT_EVENTID, + WMI_DCS_INTERFERENCE_EVENTID, + WMI_PDEV_QVIT_EVENTID, + WMI_WLAN_PROFILE_DATA_EVENTID, + WMI_PDEV_FTM_INTG_EVENTID, + WMI_WLAN_FREQ_AVOID_EVENTID, + WMI_VDEV_GET_KEEPALIVE_EVENTID, + + /* GPIO Event */ + WMI_GPIO_INPUT_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_GPIO), +}; + +enum wmi_phy_mode { + MODE_11A = 0, /* 11a Mode */ + MODE_11G = 1, /* 11b/g Mode */ + MODE_11B = 2, /* 11b Mode */ + MODE_11GONLY = 3, /* 11g only Mode */ + MODE_11NA_HT20 = 4, /* 11a HT20 mode */ + MODE_11NG_HT20 = 5, /* 11g HT20 mode */ + MODE_11NA_HT40 = 6, /* 11a HT40 mode */ + MODE_11NG_HT40 = 7, /* 11g HT40 mode */ + MODE_11AC_VHT20 = 8, + MODE_11AC_VHT40 = 9, + MODE_11AC_VHT80 = 10, + /* MODE_11AC_VHT160 = 11, */ + MODE_11AC_VHT20_2G = 11, + MODE_11AC_VHT40_2G = 12, + MODE_11AC_VHT80_2G = 13, + MODE_UNKNOWN = 14, + MODE_MAX = 14 +}; + +#define WMI_CHAN_LIST_TAG 0x1 +#define WMI_SSID_LIST_TAG 0x2 +#define WMI_BSSID_LIST_TAG 0x3 +#define WMI_IE_TAG 0x4 + +struct wmi_channel { + __le32 mhz; + __le32 band_center_freq1; + __le32 band_center_freq2; /* valid for 11ac, 80plus80 */ + union { + __le32 flags; /* WMI_CHAN_FLAG_ */ + struct { + u8 mode; /* only 6 LSBs */ + } __packed; + } __packed; + union { + __le32 reginfo0; + struct { + u8 min_power; + u8 max_power; + u8 reg_power; + u8 reg_classid; + } __packed; + } __packed; + union { + __le32 reginfo1; + struct { + u8 antenna_max; + } __packed; + } __packed; +} __packed; + +struct wmi_channel_arg { + u32 freq; + u32 band_center_freq1; + bool passive; + bool allow_ibss; + bool allow_ht; + bool allow_vht; + bool ht40plus; + /* note: power unit is 1/4th of dBm */ + u32 min_power; + u32 max_power; + u32 max_reg_power; + u32 max_antenna_gain; + u32 reg_class_id; + enum wmi_phy_mode mode; +}; + +enum wmi_channel_change_cause { + WMI_CHANNEL_CHANGE_CAUSE_NONE = 0, + WMI_CHANNEL_CHANGE_CAUSE_CSA, +}; + +#define WMI_CHAN_FLAG_HT40_PLUS (1 << 6) +#define WMI_CHAN_FLAG_PASSIVE (1 << 7) +#define WMI_CHAN_FLAG_ADHOC_ALLOWED (1 << 8) +#define WMI_CHAN_FLAG_AP_DISABLED (1 << 9) +#define WMI_CHAN_FLAG_DFS (1 << 10) +#define WMI_CHAN_FLAG_ALLOW_HT (1 << 11) +#define WMI_CHAN_FLAG_ALLOW_VHT (1 << 12) + +/* Indicate reason for channel switch */ +#define WMI_CHANNEL_CHANGE_CAUSE_CSA (1 << 13) + +#define WMI_MAX_SPATIAL_STREAM 3 + +/* HT Capabilities*/ +#define WMI_HT_CAP_ENABLED 0x0001 /* HT Enabled/ disabled */ +#define WMI_HT_CAP_HT20_SGI 0x0002 /* Short Guard Interval with HT20 */ +#define WMI_HT_CAP_DYNAMIC_SMPS 0x0004 /* Dynamic MIMO powersave */ +#define WMI_HT_CAP_TX_STBC 0x0008 /* B3 TX STBC */ +#define WMI_HT_CAP_TX_STBC_MASK_SHIFT 3 +#define WMI_HT_CAP_RX_STBC 0x0030 /* B4-B5 RX STBC */ +#define WMI_HT_CAP_RX_STBC_MASK_SHIFT 4 +#define WMI_HT_CAP_LDPC 0x0040 /* LDPC supported */ +#define WMI_HT_CAP_L_SIG_TXOP_PROT 0x0080 /* L-SIG TXOP Protection */ +#define WMI_HT_CAP_MPDU_DENSITY 0x0700 /* MPDU Density */ +#define WMI_HT_CAP_MPDU_DENSITY_MASK_SHIFT 8 +#define WMI_HT_CAP_HT40_SGI 0x0800 + +#define WMI_HT_CAP_DEFAULT_ALL (WMI_HT_CAP_ENABLED | \ + WMI_HT_CAP_HT20_SGI | \ + WMI_HT_CAP_HT40_SGI | \ + WMI_HT_CAP_TX_STBC | \ + WMI_HT_CAP_RX_STBC | \ + WMI_HT_CAP_LDPC) + + +/* + * WMI_VHT_CAP_* these maps to ieee 802.11ac vht capability information + * field. The fields not defined here are not supported, or reserved. + * Do not change these masks and if you have to add new one follow the + * bitmask as specified by 802.11ac draft. + */ + +#define WMI_VHT_CAP_MAX_MPDU_LEN_MASK 0x00000003 +#define WMI_VHT_CAP_RX_LDPC 0x00000010 +#define WMI_VHT_CAP_SGI_80MHZ 0x00000020 +#define WMI_VHT_CAP_TX_STBC 0x00000080 +#define WMI_VHT_CAP_RX_STBC_MASK 0x00000300 +#define WMI_VHT_CAP_RX_STBC_MASK_SHIFT 8 +#define WMI_VHT_CAP_MAX_AMPDU_LEN_EXP 0x03800000 +#define WMI_VHT_CAP_MAX_AMPDU_LEN_EXP_SHIFT 23 +#define WMI_VHT_CAP_RX_FIXED_ANT 0x10000000 +#define WMI_VHT_CAP_TX_FIXED_ANT 0x20000000 + +/* The following also refer for max HT AMSDU */ +#define WMI_VHT_CAP_MAX_MPDU_LEN_3839 0x00000000 +#define WMI_VHT_CAP_MAX_MPDU_LEN_7935 0x00000001 +#define WMI_VHT_CAP_MAX_MPDU_LEN_11454 0x00000002 + +#define WMI_VHT_CAP_DEFAULT_ALL (WMI_VHT_CAP_MAX_MPDU_LEN_11454 | \ + WMI_VHT_CAP_RX_LDPC | \ + WMI_VHT_CAP_SGI_80MHZ | \ + WMI_VHT_CAP_TX_STBC | \ + WMI_VHT_CAP_RX_STBC_MASK | \ + WMI_VHT_CAP_MAX_AMPDU_LEN_EXP | \ + WMI_VHT_CAP_RX_FIXED_ANT | \ + WMI_VHT_CAP_TX_FIXED_ANT) + +/* + * Interested readers refer to Rx/Tx MCS Map definition as defined in + * 802.11ac + */ +#define WMI_VHT_MAX_MCS_4_SS_MASK(r, ss) ((3 & (r)) << (((ss) - 1) << 1)) +#define WMI_VHT_MAX_SUPP_RATE_MASK 0x1fff0000 +#define WMI_VHT_MAX_SUPP_RATE_MASK_SHIFT 16 + +enum { + REGDMN_MODE_11A = 0x00001, /* 11a channels */ + REGDMN_MODE_TURBO = 0x00002, /* 11a turbo-only channels */ + REGDMN_MODE_11B = 0x00004, /* 11b channels */ + REGDMN_MODE_PUREG = 0x00008, /* 11g channels (OFDM only) */ + REGDMN_MODE_11G = 0x00008, /* XXX historical */ + REGDMN_MODE_108G = 0x00020, /* 11a+Turbo channels */ + REGDMN_MODE_108A = 0x00040, /* 11g+Turbo channels */ + REGDMN_MODE_XR = 0x00100, /* XR channels */ + REGDMN_MODE_11A_HALF_RATE = 0x00200, /* 11A half rate channels */ + REGDMN_MODE_11A_QUARTER_RATE = 0x00400, /* 11A quarter rate channels */ + REGDMN_MODE_11NG_HT20 = 0x00800, /* 11N-G HT20 channels */ + REGDMN_MODE_11NA_HT20 = 0x01000, /* 11N-A HT20 channels */ + REGDMN_MODE_11NG_HT40PLUS = 0x02000, /* 11N-G HT40 + channels */ + REGDMN_MODE_11NG_HT40MINUS = 0x04000, /* 11N-G HT40 - channels */ + REGDMN_MODE_11NA_HT40PLUS = 0x08000, /* 11N-A HT40 + channels */ + REGDMN_MODE_11NA_HT40MINUS = 0x10000, /* 11N-A HT40 - channels */ + REGDMN_MODE_11AC_VHT20 = 0x20000, /* 5Ghz, VHT20 */ + REGDMN_MODE_11AC_VHT40PLUS = 0x40000, /* 5Ghz, VHT40 + channels */ + REGDMN_MODE_11AC_VHT40MINUS = 0x80000, /* 5Ghz VHT40 - channels */ + REGDMN_MODE_11AC_VHT80 = 0x100000, /* 5Ghz, VHT80 channels */ + REGDMN_MODE_ALL = 0xffffffff +}; + +#define REGDMN_CAP1_CHAN_HALF_RATE 0x00000001 +#define REGDMN_CAP1_CHAN_QUARTER_RATE 0x00000002 +#define REGDMN_CAP1_CHAN_HAL49GHZ 0x00000004 + +/* regulatory capabilities */ +#define REGDMN_EEPROM_EEREGCAP_EN_FCC_MIDBAND 0x0040 +#define REGDMN_EEPROM_EEREGCAP_EN_KK_U1_EVEN 0x0080 +#define REGDMN_EEPROM_EEREGCAP_EN_KK_U2 0x0100 +#define REGDMN_EEPROM_EEREGCAP_EN_KK_MIDBAND 0x0200 +#define REGDMN_EEPROM_EEREGCAP_EN_KK_U1_ODD 0x0400 +#define REGDMN_EEPROM_EEREGCAP_EN_KK_NEW_11A 0x0800 + +struct hal_reg_capabilities { + /* regdomain value specified in EEPROM */ + __le32 eeprom_rd; + /*regdomain */ + __le32 eeprom_rd_ext; + /* CAP1 capabilities bit map. */ + __le32 regcap1; + /* REGDMN EEPROM CAP. */ + __le32 regcap2; + /* REGDMN MODE */ + __le32 wireless_modes; + __le32 low_2ghz_chan; + __le32 high_2ghz_chan; + __le32 low_5ghz_chan; + __le32 high_5ghz_chan; +} __packed; + +enum wlan_mode_capability { + WHAL_WLAN_11A_CAPABILITY = 0x1, + WHAL_WLAN_11G_CAPABILITY = 0x2, + WHAL_WLAN_11AG_CAPABILITY = 0x3, +}; + +/* structure used by FW for requesting host memory */ +struct wlan_host_mem_req { + /* ID of the request */ + __le32 req_id; + /* size of the of each unit */ + __le32 unit_size; + /* flags to indicate that + * the number units is dependent + * on number of resources(num vdevs num peers .. etc) + */ + __le32 num_unit_info; + /* + * actual number of units to allocate . if flags in the num_unit_info + * indicate that number of units is tied to number of a particular + * resource to allocate then num_units filed is set to 0 and host + * will derive the number units from number of the resources it is + * requesting. + */ + __le32 num_units; +} __packed; + +#define WMI_SERVICE_IS_ENABLED(wmi_svc_bmap, svc_id) \ + ((((wmi_svc_bmap)[(svc_id)/(sizeof(u32))]) & \ + (1 << ((svc_id)%(sizeof(u32))))) != 0) + +/* + * The following struct holds optional payload for + * wmi_service_ready_event,e.g., 11ac pass some of the + * device capability to the host. + */ +struct wmi_service_ready_event { + __le32 sw_version; + __le32 sw_version_1; + __le32 abi_version; + /* WMI_PHY_CAPABILITY */ + __le32 phy_capability; + /* Maximum number of frag table entries that SW will populate less 1 */ + __le32 max_frag_entry; + __le32 wmi_service_bitmap[WMI_SERVICE_BM_SIZE]; + __le32 num_rf_chains; + /* + * The following field is only valid for service type + * WMI_SERVICE_11AC + */ + __le32 ht_cap_info; /* WMI HT Capability */ + __le32 vht_cap_info; /* VHT capability info field of 802.11ac */ + __le32 vht_supp_mcs; /* VHT Supported MCS Set field Rx/Tx same */ + __le32 hw_min_tx_power; + __le32 hw_max_tx_power; + struct hal_reg_capabilities hal_reg_capabilities; + __le32 sys_cap_info; + __le32 min_pkt_size_enable; /* Enterprise mode short pkt enable */ + /* + * Max beacon and Probe Response IE offload size + * (includes optional P2P IEs) + */ + __le32 max_bcn_ie_size; + /* + * request to host to allocate a chuck of memory and pss it down to FW + * via WM_INIT. FW uses this as FW extesnsion memory for saving its + * data structures. Only valid for low latency interfaces like PCIE + * where FW can access this memory directly (or) by DMA. + */ + __le32 num_mem_reqs; + struct wlan_host_mem_req mem_reqs[1]; +} __packed; + +/* + * status consists of upper 16 bits fo int status and lower 16 bits of + * module ID that retuned status + */ +#define WLAN_INIT_STATUS_SUCCESS 0x0 +#define WLAN_GET_INIT_STATUS_REASON(status) ((status) & 0xffff) +#define WLAN_GET_INIT_STATUS_MODULE_ID(status) (((status) >> 16) & 0xffff) + +#define WMI_SERVICE_READY_TIMEOUT_HZ (5*HZ) +#define WMI_UNIFIED_READY_TIMEOUT_HZ (5*HZ) + +struct wmi_ready_event { + __le32 sw_version; + __le32 abi_version; + struct wmi_mac_addr mac_addr; + __le32 status; +} __packed; + +struct wmi_resource_config { + /* number of virtual devices (VAPs) to support */ + __le32 num_vdevs; + + /* number of peer nodes to support */ + __le32 num_peers; + + /* + * In offload mode target supports features like WOW, chatter and + * other protocol offloads. In order to support them some + * functionalities like reorder buffering, PN checking need to be + * done in target. This determines maximum number of peers suported + * by target in offload mode + */ + __le32 num_offload_peers; + + /* For target-based RX reordering */ + __le32 num_offload_reorder_bufs; + + /* number of keys per peer */ + __le32 num_peer_keys; + + /* total number of TX/RX data TIDs */ + __le32 num_tids; + + /* + * max skid for resolving hash collisions + * + * The address search table is sparse, so that if two MAC addresses + * result in the same hash value, the second of these conflicting + * entries can slide to the next index in the address search table, + * and use it, if it is unoccupied. This ast_skid_limit parameter + * specifies the upper bound on how many subsequent indices to search + * over to find an unoccupied space. + */ + __le32 ast_skid_limit; + + /* + * the nominal chain mask for transmit + * + * The chain mask may be modified dynamically, e.g. to operate AP + * tx with a reduced number of chains if no clients are associated. + * This configuration parameter specifies the nominal chain-mask that + * should be used when not operating with a reduced set of tx chains. + */ + __le32 tx_chain_mask; + + /* + * the nominal chain mask for receive + * + * The chain mask may be modified dynamically, e.g. for a client + * to use a reduced number of chains for receive if the traffic to + * the client is low enough that it doesn't require downlink MIMO + * or antenna diversity. + * This configuration parameter specifies the nominal chain-mask that + * should be used when not operating with a reduced set of rx chains. + */ + __le32 rx_chain_mask; + + /* + * what rx reorder timeout (ms) to use for the AC + * + * Each WMM access class (voice, video, best-effort, background) will + * have its own timeout value to dictate how long to wait for missing + * rx MPDUs to arrive before flushing subsequent MPDUs that have + * already been received. + * This parameter specifies the timeout in milliseconds for each + * class. + */ + __le32 rx_timeout_pri_vi; + __le32 rx_timeout_pri_vo; + __le32 rx_timeout_pri_be; + __le32 rx_timeout_pri_bk; + + /* + * what mode the rx should decap packets to + * + * MAC can decap to RAW (no decap), native wifi or Ethernet types + * THis setting also determines the default TX behavior, however TX + * behavior can be modified on a per VAP basis during VAP init + */ + __le32 rx_decap_mode; + + /* what is the maximum scan requests than can be queued */ + __le32 scan_max_pending_reqs; + + /* maximum VDEV that could use BMISS offload */ + __le32 bmiss_offload_max_vdev; + + /* maximum VDEV that could use offload roaming */ + __le32 roam_offload_max_vdev; + + /* maximum AP profiles that would push to offload roaming */ + __le32 roam_offload_max_ap_profiles; + + /* + * how many groups to use for mcast->ucast conversion + * + * The target's WAL maintains a table to hold information regarding + * which peers belong to a given multicast group, so that if + * multicast->unicast conversion is enabled, the target can convert + * multicast tx frames to a series of unicast tx frames, to each + * peer within the multicast group. + This num_mcast_groups configuration parameter tells the target how + * many multicast groups to provide storage for within its multicast + * group membership table. + */ + __le32 num_mcast_groups; + + /* + * size to alloc for the mcast membership table + * + * This num_mcast_table_elems configuration parameter tells the + * target how many peer elements it needs to provide storage for in + * its multicast group membership table. + * These multicast group membership table elements are shared by the + * multicast groups stored within the table. + */ + __le32 num_mcast_table_elems; + + /* + * whether/how to do multicast->unicast conversion + * + * This configuration parameter specifies whether the target should + * perform multicast --> unicast conversion on transmit, and if so, + * what to do if it finds no entries in its multicast group + * membership table for the multicast IP address in the tx frame. + * Configuration value: + * 0 -> Do not perform multicast to unicast conversion. + * 1 -> Convert multicast frames to unicast, if the IP multicast + * address from the tx frame is found in the multicast group + * membership table. If the IP multicast address is not found, + * drop the frame. + * 2 -> Convert multicast frames to unicast, if the IP multicast + * address from the tx frame is found in the multicast group + * membership table. If the IP multicast address is not found, + * transmit the frame as multicast. + */ + __le32 mcast2ucast_mode; + + /* + * how much memory to allocate for a tx PPDU dbg log + * + * This parameter controls how much memory the target will allocate + * to store a log of tx PPDU meta-information (how large the PPDU + * was, when it was sent, whether it was successful, etc.) + */ + __le32 tx_dbg_log_size; + + /* how many AST entries to be allocated for WDS */ + __le32 num_wds_entries; + + /* + * MAC DMA burst size, e.g., For target PCI limit can be + * 0 -default, 1 256B + */ + __le32 dma_burst_size; + + /* + * Fixed delimiters to be inserted after every MPDU to + * account for interface latency to avoid underrun. + */ + __le32 mac_aggr_delim; + + /* + * determine whether target is responsible for detecting duplicate + * non-aggregate MPDU and timing out stale fragments. + * + * A-MPDU reordering is always performed on the target. + * + * 0: target responsible for frag timeout and dup checking + * 1: host responsible for frag timeout and dup checking + */ + __le32 rx_skip_defrag_timeout_dup_detection_check; + + /* + * Configuration for VoW : + * No of Video Nodes to be supported + * and Max no of descriptors for each Video link (node). + */ + __le32 vow_config; + + /* maximum VDEV that could use GTK offload */ + __le32 gtk_offload_max_vdev; + + /* Number of msdu descriptors target should use */ + __le32 num_msdu_desc; + + /* + * Max. number of Tx fragments per MSDU + * This parameter controls the max number of Tx fragments per MSDU. + * This is sent by the target as part of the WMI_SERVICE_READY event + * and is overriden by the OS shim as required. + */ + __le32 max_frag_entries; +} __packed; + +/* strucutre describing host memory chunk. */ +struct host_memory_chunk { + /* id of the request that is passed up in service ready */ + __le32 req_id; + /* the physical address the memory chunk */ + __le32 ptr; + /* size of the chunk */ + __le32 size; +} __packed; + +struct wmi_init_cmd { + struct wmi_resource_config resource_config; + __le32 num_host_mem_chunks; + + /* + * variable number of host memory chunks. + * This should be the last element in the structure + */ + struct host_memory_chunk host_mem_chunks[1]; +} __packed; + +/* TLV for channel list */ +struct wmi_chan_list { + __le32 tag; /* WMI_CHAN_LIST_TAG */ + __le32 num_chan; + __le32 channel_list[0]; +} __packed; + +struct wmi_bssid_list { + __le32 tag; /* WMI_BSSID_LIST_TAG */ + __le32 num_bssid; + struct wmi_mac_addr bssid_list[0]; +} __packed; + +struct wmi_ie_data { + __le32 tag; /* WMI_IE_TAG */ + __le32 ie_len; + u8 ie_data[0]; +} __packed; + +struct wmi_ssid { + __le32 ssid_len; + u8 ssid[32]; +} __packed; + +struct wmi_ssid_list { + __le32 tag; /* WMI_SSID_LIST_TAG */ + __le32 num_ssids; + struct wmi_ssid ssids[0]; +} __packed; + +/* prefix used by scan requestor ids on the host */ +#define WMI_HOST_SCAN_REQUESTOR_ID_PREFIX 0xA000 + +/* prefix used by scan request ids generated on the host */ +/* host cycles through the lower 12 bits to generate ids */ +#define WMI_HOST_SCAN_REQ_ID_PREFIX 0xA000 + +#define WLAN_SCAN_PARAMS_MAX_SSID 16 +#define WLAN_SCAN_PARAMS_MAX_BSSID 4 +#define WLAN_SCAN_PARAMS_MAX_IE_LEN 256 + +/* Scan priority numbers must be sequential, starting with 0 */ +enum wmi_scan_priority { + WMI_SCAN_PRIORITY_VERY_LOW = 0, + WMI_SCAN_PRIORITY_LOW, + WMI_SCAN_PRIORITY_MEDIUM, + WMI_SCAN_PRIORITY_HIGH, + WMI_SCAN_PRIORITY_VERY_HIGH, + WMI_SCAN_PRIORITY_COUNT /* number of priorities supported */ +}; + +struct wmi_start_scan_cmd { + /* Scan ID */ + __le32 scan_id; + /* Scan requestor ID */ + __le32 scan_req_id; + /* VDEV id(interface) that is requesting scan */ + __le32 vdev_id; + /* Scan Priority, input to scan scheduler */ + __le32 scan_priority; + /* Scan events subscription */ + __le32 notify_scan_events; + /* dwell time in msec on active channels */ + __le32 dwell_time_active; + /* dwell time in msec on passive channels */ + __le32 dwell_time_passive; + /* + * min time in msec on the BSS channel,only valid if atleast one + * VDEV is active + */ + __le32 min_rest_time; + /* + * max rest time in msec on the BSS channel,only valid if at least + * one VDEV is active + */ + /* + * the scanner will rest on the bss channel at least min_rest_time + * after min_rest_time the scanner will start checking for tx/rx + * activity on all VDEVs. if there is no activity the scanner will + * switch to off channel. if there is activity the scanner will let + * the radio on the bss channel until max_rest_time expires.at + * max_rest_time scanner will switch to off channel irrespective of + * activity. activity is determined by the idle_time parameter. + */ + __le32 max_rest_time; + /* + * time before sending next set of probe requests. + * The scanner keeps repeating probe requests transmission with + * period specified by repeat_probe_time. + * The number of probe requests specified depends on the ssid_list + * and bssid_list + */ + __le32 repeat_probe_time; + /* time in msec between 2 consequetive probe requests with in a set. */ + __le32 probe_spacing_time; + /* + * data inactivity time in msec on bss channel that will be used by + * scanner for measuring the inactivity. + */ + __le32 idle_time; + /* maximum time in msec allowed for scan */ + __le32 max_scan_time; + /* + * delay in msec before sending first probe request after switching + * to a channel + */ + __le32 probe_delay; + /* Scan control flags */ + __le32 scan_ctrl_flags; + + /* Burst duration time in msecs */ + __le32 burst_duration; + /* + * TLV (tag length value ) paramerters follow the scan_cmd structure. + * TLV can contain channel list, bssid list, ssid list and + * ie. the TLV tags are defined above; + */ +} __packed; + +struct wmi_ssid_arg { + int len; + const u8 *ssid; +}; + +struct wmi_bssid_arg { + const u8 *bssid; +}; + +struct wmi_start_scan_arg { + u32 scan_id; + u32 scan_req_id; + u32 vdev_id; + u32 scan_priority; + u32 notify_scan_events; + u32 dwell_time_active; + u32 dwell_time_passive; + u32 min_rest_time; + u32 max_rest_time; + u32 repeat_probe_time; + u32 probe_spacing_time; + u32 idle_time; + u32 max_scan_time; + u32 probe_delay; + u32 scan_ctrl_flags; + + u32 ie_len; + u32 n_channels; + u32 n_ssids; + u32 n_bssids; + + u8 ie[WLAN_SCAN_PARAMS_MAX_IE_LEN]; + u32 channels[64]; + struct wmi_ssid_arg ssids[WLAN_SCAN_PARAMS_MAX_SSID]; + struct wmi_bssid_arg bssids[WLAN_SCAN_PARAMS_MAX_BSSID]; +}; + +/* scan control flags */ + +/* passively scan all channels including active channels */ +#define WMI_SCAN_FLAG_PASSIVE 0x1 +/* add wild card ssid probe request even though ssid_list is specified. */ +#define WMI_SCAN_ADD_BCAST_PROBE_REQ 0x2 +/* add cck rates to rates/xrate ie for the generated probe request */ +#define WMI_SCAN_ADD_CCK_RATES 0x4 +/* add ofdm rates to rates/xrate ie for the generated probe request */ +#define WMI_SCAN_ADD_OFDM_RATES 0x8 +/* To enable indication of Chan load and Noise floor to host */ +#define WMI_SCAN_CHAN_STAT_EVENT 0x10 +/* Filter Probe request frames */ +#define WMI_SCAN_FILTER_PROBE_REQ 0x20 +/* When set, DFS channels will not be scanned */ +#define WMI_SCAN_BYPASS_DFS_CHN 0x40 +/* Different FW scan engine may choose to bail out on errors. + * Allow the driver to have influence over that. */ +#define WMI_SCAN_CONTINUE_ON_ERROR 0x80 + +/* WMI_SCAN_CLASS_MASK must be the same value as IEEE80211_SCAN_CLASS_MASK */ +#define WMI_SCAN_CLASS_MASK 0xFF000000 + + +enum wmi_stop_scan_type { + WMI_SCAN_STOP_ONE = 0x00000000, /* stop by scan_id */ + WMI_SCAN_STOP_VDEV_ALL = 0x01000000, /* stop by vdev_id */ + WMI_SCAN_STOP_ALL = 0x04000000, /* stop all scans */ +}; + +struct wmi_stop_scan_cmd { + __le32 scan_req_id; + __le32 scan_id; + __le32 req_type; + __le32 vdev_id; +} __packed; + +struct wmi_stop_scan_arg { + u32 req_id; + enum wmi_stop_scan_type req_type; + union { + u32 scan_id; + u32 vdev_id; + } u; +}; + +struct wmi_scan_chan_list_cmd { + __le32 num_scan_chans; + struct wmi_channel chan_info[0]; +} __packed; + +struct wmi_scan_chan_list_arg { + u32 n_channels; + struct wmi_channel_arg *channels; +}; + +enum wmi_bss_filter { + WMI_BSS_FILTER_NONE = 0, /* no beacons forwarded */ + WMI_BSS_FILTER_ALL, /* all beacons forwarded */ + WMI_BSS_FILTER_PROFILE, /* only beacons matching profile */ + WMI_BSS_FILTER_ALL_BUT_PROFILE, /* all but beacons matching profile */ + WMI_BSS_FILTER_CURRENT_BSS, /* only beacons matching current BSS */ + WMI_BSS_FILTER_ALL_BUT_BSS, /* all but beacons matching BSS */ + WMI_BSS_FILTER_PROBED_SSID, /* beacons matching probed ssid */ + WMI_BSS_FILTER_LAST_BSS, /* marker only */ +}; + +enum wmi_scan_event_type { + WMI_SCAN_EVENT_STARTED = 0x1, + WMI_SCAN_EVENT_COMPLETED = 0x2, + WMI_SCAN_EVENT_BSS_CHANNEL = 0x4, + WMI_SCAN_EVENT_FOREIGN_CHANNEL = 0x8, + WMI_SCAN_EVENT_DEQUEUED = 0x10, + WMI_SCAN_EVENT_PREEMPTED = 0x20, /* possibly by high-prio scan */ + WMI_SCAN_EVENT_START_FAILED = 0x40, + WMI_SCAN_EVENT_RESTARTED = 0x80, + WMI_SCAN_EVENT_MAX = 0x8000 +}; + +enum wmi_scan_completion_reason { + WMI_SCAN_REASON_COMPLETED, + WMI_SCAN_REASON_CANCELLED, + WMI_SCAN_REASON_PREEMPTED, + WMI_SCAN_REASON_TIMEDOUT, + WMI_SCAN_REASON_MAX, +}; + +struct wmi_scan_event { + __le32 event_type; /* %WMI_SCAN_EVENT_ */ + __le32 reason; /* %WMI_SCAN_REASON_ */ + __le32 channel_freq; /* only valid for WMI_SCAN_EVENT_FOREIGN_CHANNEL */ + __le32 scan_req_id; + __le32 scan_id; + __le32 vdev_id; +} __packed; + +/* + * This defines how much headroom is kept in the + * receive frame between the descriptor and the + * payload, in order for the WMI PHY error and + * management handler to insert header contents. + * + * This is in bytes. + */ +#define WMI_MGMT_RX_HDR_HEADROOM 52 + +/* + * This event will be used for sending scan results + * as well as rx mgmt frames to the host. The rx buffer + * will be sent as part of this WMI event. It would be a + * good idea to pass all the fields in the RX status + * descriptor up to the host. + */ +struct wmi_mgmt_rx_hdr { + __le32 channel; + __le32 snr; + __le32 rate; + __le32 phy_mode; + __le32 buf_len; + __le32 status; /* %WMI_RX_STATUS_ */ +} __packed; + +struct wmi_mgmt_rx_event { + struct wmi_mgmt_rx_hdr hdr; + u8 buf[0]; +} __packed; + +#define WMI_RX_STATUS_OK 0x00 +#define WMI_RX_STATUS_ERR_CRC 0x01 +#define WMI_RX_STATUS_ERR_DECRYPT 0x08 +#define WMI_RX_STATUS_ERR_MIC 0x10 +#define WMI_RX_STATUS_ERR_KEY_CACHE_MISS 0x20 + +struct wmi_single_phyerr_rx_hdr { + /* TSF timestamp */ + __le32 tsf_timestamp; + + /* + * Current freq1, freq2 + * + * [7:0]: freq1[lo] + * [15:8] : freq1[hi] + * [23:16]: freq2[lo] + * [31:24]: freq2[hi] + */ + __le16 freq1; + __le16 freq2; + + /* + * Combined RSSI over all chains and channel width for this PHY error + * + * [7:0]: RSSI combined + * [15:8]: Channel width (MHz) + * [23:16]: PHY error code + * [24:16]: reserved (future use) + */ + u8 rssi_combined; + u8 chan_width_mhz; + u8 phy_err_code; + u8 rsvd0; + + /* + * RSSI on chain 0 through 3 + * + * This is formatted the same as the PPDU_START RX descriptor + * field: + * + * [7:0]: pri20 + * [15:8]: sec20 + * [23:16]: sec40 + * [31:24]: sec80 + */ + + __le32 rssi_chain0; + __le32 rssi_chain1; + __le32 rssi_chain2; + __le32 rssi_chain3; + + /* + * Last calibrated NF value for chain 0 through 3 + * + * nf_list_1: + * + * + [15:0] - chain 0 + * + [31:16] - chain 1 + * + * nf_list_2: + * + * + [15:0] - chain 2 + * + [31:16] - chain 3 + */ + __le32 nf_list_1; + __le32 nf_list_2; + + + /* Length of the frame */ + __le32 buf_len; +} __packed; + +struct wmi_single_phyerr_rx_event { + /* Phy error event header */ + struct wmi_single_phyerr_rx_hdr hdr; + /* frame buffer */ + u8 bufp[0]; +} __packed; + +struct wmi_comb_phyerr_rx_hdr { + /* Phy error phy error count */ + __le32 num_phyerr_events; + __le32 tsf_l32; + __le32 tsf_u32; +} __packed; + +struct wmi_comb_phyerr_rx_event { + /* Phy error phy error count */ + struct wmi_comb_phyerr_rx_hdr hdr; + /* + * frame buffer - contains multiple payloads in the order: + * header - payload, header - payload... + * (The header is of type: wmi_single_phyerr_rx_hdr) + */ + u8 bufp[0]; +} __packed; + +struct wmi_mgmt_tx_hdr { + __le32 vdev_id; + struct wmi_mac_addr peer_macaddr; + __le32 tx_rate; + __le32 tx_power; + __le32 buf_len; +} __packed; + +struct wmi_mgmt_tx_cmd { + struct wmi_mgmt_tx_hdr hdr; + u8 buf[0]; +} __packed; + +struct wmi_echo_event { + __le32 value; +} __packed; + +struct wmi_echo_cmd { + __le32 value; +} __packed; + + +struct wmi_pdev_set_regdomain_cmd { + __le32 reg_domain; + __le32 reg_domain_2G; + __le32 reg_domain_5G; + __le32 conformance_test_limit_2G; + __le32 conformance_test_limit_5G; +} __packed; + +/* Command to set/unset chip in quiet mode */ +struct wmi_pdev_set_quiet_cmd { + /* period in TUs */ + __le32 period; + + /* duration in TUs */ + __le32 duration; + + /* offset in TUs */ + __le32 next_start; + + /* enable/disable */ + __le32 enabled; +} __packed; + + +/* + * 802.11g protection mode. + */ +enum ath10k_protmode { + ATH10K_PROT_NONE = 0, /* no protection */ + ATH10K_PROT_CTSONLY = 1, /* CTS to self */ + ATH10K_PROT_RTSCTS = 2, /* RTS-CTS */ +}; + +enum wmi_beacon_gen_mode { + WMI_BEACON_STAGGERED_MODE = 0, + WMI_BEACON_BURST_MODE = 1 +}; + +enum wmi_csa_event_ies_present_flag { + WMI_CSA_IE_PRESENT = 0x00000001, + WMI_XCSA_IE_PRESENT = 0x00000002, + WMI_WBW_IE_PRESENT = 0x00000004, + WMI_CSWARP_IE_PRESENT = 0x00000008, +}; + +/* wmi CSA receive event from beacon frame */ +struct wmi_csa_event { + __le32 i_fc_dur; + /* Bit 0-15: FC */ + /* Bit 16-31: DUR */ + struct wmi_mac_addr i_addr1; + struct wmi_mac_addr i_addr2; + __le32 csa_ie[2]; + __le32 xcsa_ie[2]; + __le32 wb_ie[2]; + __le32 cswarp_ie; + __le32 ies_present_flag; /* wmi_csa_event_ies_present_flag */ +} __packed; + +/* the definition of different PDEV parameters */ +#define PDEV_DEFAULT_STATS_UPDATE_PERIOD 500 +#define VDEV_DEFAULT_STATS_UPDATE_PERIOD 500 +#define PEER_DEFAULT_STATS_UPDATE_PERIOD 500 + +enum wmi_pdev_param { + /* TX chian mask */ + WMI_PDEV_PARAM_TX_CHAIN_MASK = 0x1, + /* RX chian mask */ + WMI_PDEV_PARAM_RX_CHAIN_MASK, + /* TX power limit for 2G Radio */ + WMI_PDEV_PARAM_TXPOWER_LIMIT2G, + /* TX power limit for 5G Radio */ + WMI_PDEV_PARAM_TXPOWER_LIMIT5G, + /* TX power scale */ + WMI_PDEV_PARAM_TXPOWER_SCALE, + /* Beacon generation mode . 0: host, 1: target */ + WMI_PDEV_PARAM_BEACON_GEN_MODE, + /* Beacon generation mode . 0: staggered 1: bursted */ + WMI_PDEV_PARAM_BEACON_TX_MODE, + /* + * Resource manager off chan mode . + * 0: turn off off chan mode. 1: turn on offchan mode + */ + WMI_PDEV_PARAM_RESMGR_OFFCHAN_MODE, + /* + * Protection mode: + * 0: no protection 1:use CTS-to-self 2: use RTS/CTS + */ + WMI_PDEV_PARAM_PROTECTION_MODE, + /* Dynamic bandwidth 0: disable 1: enable */ + WMI_PDEV_PARAM_DYNAMIC_BW, + /* Non aggregrate/ 11g sw retry threshold.0-disable */ + WMI_PDEV_PARAM_NON_AGG_SW_RETRY_TH, + /* aggregrate sw retry threshold. 0-disable*/ + WMI_PDEV_PARAM_AGG_SW_RETRY_TH, + /* Station kickout threshold (non of consecutive failures).0-disable */ + WMI_PDEV_PARAM_STA_KICKOUT_TH, + /* Aggerate size scaling configuration per AC */ + WMI_PDEV_PARAM_AC_AGGRSIZE_SCALING, + /* LTR enable */ + WMI_PDEV_PARAM_LTR_ENABLE, + /* LTR latency for BE, in us */ + WMI_PDEV_PARAM_LTR_AC_LATENCY_BE, + /* LTR latency for BK, in us */ + WMI_PDEV_PARAM_LTR_AC_LATENCY_BK, + /* LTR latency for VI, in us */ + WMI_PDEV_PARAM_LTR_AC_LATENCY_VI, + /* LTR latency for VO, in us */ + WMI_PDEV_PARAM_LTR_AC_LATENCY_VO, + /* LTR AC latency timeout, in ms */ + WMI_PDEV_PARAM_LTR_AC_LATENCY_TIMEOUT, + /* LTR platform latency override, in us */ + WMI_PDEV_PARAM_LTR_SLEEP_OVERRIDE, + /* LTR-RX override, in us */ + WMI_PDEV_PARAM_LTR_RX_OVERRIDE, + /* Tx activity timeout for LTR, in us */ + WMI_PDEV_PARAM_LTR_TX_ACTIVITY_TIMEOUT, + /* L1SS state machine enable */ + WMI_PDEV_PARAM_L1SS_ENABLE, + /* Deep sleep state machine enable */ + WMI_PDEV_PARAM_DSLEEP_ENABLE, + /* RX buffering flush enable */ + WMI_PDEV_PARAM_PCIELP_TXBUF_FLUSH, + /* RX buffering matermark */ + WMI_PDEV_PARAM_PCIELP_TXBUF_WATERMARK, + /* RX buffering timeout enable */ + WMI_PDEV_PARAM_PCIELP_TXBUF_TMO_EN, + /* RX buffering timeout value */ + WMI_PDEV_PARAM_PCIELP_TXBUF_TMO_VALUE, + /* pdev level stats update period in ms */ + WMI_PDEV_PARAM_PDEV_STATS_UPDATE_PERIOD, + /* vdev level stats update period in ms */ + WMI_PDEV_PARAM_VDEV_STATS_UPDATE_PERIOD, + /* peer level stats update period in ms */ + WMI_PDEV_PARAM_PEER_STATS_UPDATE_PERIOD, + /* beacon filter status update period */ + WMI_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD, + /* QOS Mgmt frame protection MFP/PMF 0: disable, 1: enable */ + WMI_PDEV_PARAM_PMF_QOS, + /* Access category on which ARP frames are sent */ + WMI_PDEV_PARAM_ARP_AC_OVERRIDE, + /* DCS configuration */ + WMI_PDEV_PARAM_DCS, + /* Enable/Disable ANI on target */ + WMI_PDEV_PARAM_ANI_ENABLE, + /* configure the ANI polling period */ + WMI_PDEV_PARAM_ANI_POLL_PERIOD, + /* configure the ANI listening period */ + WMI_PDEV_PARAM_ANI_LISTEN_PERIOD, + /* configure OFDM immunity level */ + WMI_PDEV_PARAM_ANI_OFDM_LEVEL, + /* configure CCK immunity level */ + WMI_PDEV_PARAM_ANI_CCK_LEVEL, + /* Enable/Disable CDD for 1x1 STAs in rate control module */ + WMI_PDEV_PARAM_DYNTXCHAIN, + /* Enable/Disable proxy STA */ + WMI_PDEV_PARAM_PROXY_STA, + /* Enable/Disable low power state when all VDEVs are inactive/idle. */ + WMI_PDEV_PARAM_IDLE_PS_CONFIG, + /* Enable/Disable power gating sleep */ + WMI_PDEV_PARAM_POWER_GATING_SLEEP, +}; + +struct wmi_pdev_set_param_cmd { + __le32 param_id; + __le32 param_value; +} __packed; + +struct wmi_pdev_get_tpc_config_cmd { + /* parameter */ + __le32 param; +} __packed; + +#define WMI_TPC_RATE_MAX 160 +#define WMI_TPC_TX_N_CHAIN 4 + +enum wmi_tpc_config_event_flag { + WMI_TPC_CONFIG_EVENT_FLAG_TABLE_CDD = 0x1, + WMI_TPC_CONFIG_EVENT_FLAG_TABLE_STBC = 0x2, + WMI_TPC_CONFIG_EVENT_FLAG_TABLE_TXBF = 0x4, +}; + +struct wmi_pdev_tpc_config_event { + __le32 reg_domain; + __le32 chan_freq; + __le32 phy_mode; + __le32 twice_antenna_reduction; + __le32 twice_max_rd_power; + s32 twice_antenna_gain; + __le32 power_limit; + __le32 rate_max; + __le32 num_tx_chain; + __le32 ctl; + __le32 flags; + s8 max_reg_allow_pow[WMI_TPC_TX_N_CHAIN]; + s8 max_reg_allow_pow_agcdd[WMI_TPC_TX_N_CHAIN][WMI_TPC_TX_N_CHAIN]; + s8 max_reg_allow_pow_agstbc[WMI_TPC_TX_N_CHAIN][WMI_TPC_TX_N_CHAIN]; + s8 max_reg_allow_pow_agtxbf[WMI_TPC_TX_N_CHAIN][WMI_TPC_TX_N_CHAIN]; + u8 rates_array[WMI_TPC_RATE_MAX]; +} __packed; + +/* Transmit power scale factor. */ +enum wmi_tp_scale { + WMI_TP_SCALE_MAX = 0, /* no scaling (default) */ + WMI_TP_SCALE_50 = 1, /* 50% of max (-3 dBm) */ + WMI_TP_SCALE_25 = 2, /* 25% of max (-6 dBm) */ + WMI_TP_SCALE_12 = 3, /* 12% of max (-9 dBm) */ + WMI_TP_SCALE_MIN = 4, /* min, but still on */ + WMI_TP_SCALE_SIZE = 5, /* max num of enum */ +}; + +struct wmi_set_channel_cmd { + /* channel (only frequency and mode info are used) */ + struct wmi_channel chan; +} __packed; + +struct wmi_pdev_chanlist_update_event { + /* number of channels */ + __le32 num_chan; + /* array of channels */ + struct wmi_channel channel_list[1]; +} __packed; + +#define WMI_MAX_DEBUG_MESG (sizeof(u32) * 32) + +struct wmi_debug_mesg_event { + /* message buffer, NULL terminated */ + char bufp[WMI_MAX_DEBUG_MESG]; +} __packed; + +enum { + /* P2P device */ + VDEV_SUBTYPE_P2PDEV = 0, + /* P2P client */ + VDEV_SUBTYPE_P2PCLI, + /* P2P GO */ + VDEV_SUBTYPE_P2PGO, + /* BT3.0 HS */ + VDEV_SUBTYPE_BT, +}; + +struct wmi_pdev_set_channel_cmd { + /* idnore power , only use flags , mode and freq */ + struct wmi_channel chan; +} __packed; + +/* Customize the DSCP (bit) to TID (0-7) mapping for QOS */ +#define WMI_DSCP_MAP_MAX (64) +struct wmi_pdev_set_dscp_tid_map_cmd { + /* map indicating DSCP to TID conversion */ + __le32 dscp_to_tid_map[WMI_DSCP_MAP_MAX]; +} __packed; + +enum mcast_bcast_rate_id { + WMI_SET_MCAST_RATE, + WMI_SET_BCAST_RATE +}; + +struct mcast_bcast_rate { + enum mcast_bcast_rate_id rate_id; + __le32 rate; +} __packed; + +struct wmi_wmm_params { + __le32 cwmin; + __le32 cwmax; + __le32 aifs; + __le32 txop; + __le32 acm; + __le32 no_ack; +} __packed; + +struct wmi_pdev_set_wmm_params { + struct wmi_wmm_params ac_be; + struct wmi_wmm_params ac_bk; + struct wmi_wmm_params ac_vi; + struct wmi_wmm_params ac_vo; +} __packed; + +struct wmi_wmm_params_arg { + u32 cwmin; + u32 cwmax; + u32 aifs; + u32 txop; + u32 acm; + u32 no_ack; +}; + +struct wmi_pdev_set_wmm_params_arg { + struct wmi_wmm_params_arg ac_be; + struct wmi_wmm_params_arg ac_bk; + struct wmi_wmm_params_arg ac_vi; + struct wmi_wmm_params_arg ac_vo; +}; + +struct wal_dbg_tx_stats { + /* Num HTT cookies queued to dispatch list */ + __le32 comp_queued; + + /* Num HTT cookies dispatched */ + __le32 comp_delivered; + + /* Num MSDU queued to WAL */ + __le32 msdu_enqued; + + /* Num MPDU queue to WAL */ + __le32 mpdu_enqued; + + /* Num MSDUs dropped by WMM limit */ + __le32 wmm_drop; + + /* Num Local frames queued */ + __le32 local_enqued; + + /* Num Local frames done */ + __le32 local_freed; + + /* Num queued to HW */ + __le32 hw_queued; + + /* Num PPDU reaped from HW */ + __le32 hw_reaped; + + /* Num underruns */ + __le32 underrun; + + /* Num PPDUs cleaned up in TX abort */ + __le32 tx_abort; + + /* Num MPDUs requed by SW */ + __le32 mpdus_requed; + + /* excessive retries */ + __le32 tx_ko; + + /* data hw rate code */ + __le32 data_rc; + + /* Scheduler self triggers */ + __le32 self_triggers; + + /* frames dropped due to excessive sw retries */ + __le32 sw_retry_failure; + + /* illegal rate phy errors */ + __le32 illgl_rate_phy_err; + + /* wal pdev continous xretry */ + __le32 pdev_cont_xretry; + + /* wal pdev continous xretry */ + __le32 pdev_tx_timeout; + + /* wal pdev resets */ + __le32 pdev_resets; + + __le32 phy_underrun; + + /* MPDU is more than txop limit */ + __le32 txop_ovf; +} __packed; + +struct wal_dbg_rx_stats { + /* Cnts any change in ring routing mid-ppdu */ + __le32 mid_ppdu_route_change; + + /* Total number of statuses processed */ + __le32 status_rcvd; + + /* Extra frags on rings 0-3 */ + __le32 r0_frags; + __le32 r1_frags; + __le32 r2_frags; + __le32 r3_frags; + + /* MSDUs / MPDUs delivered to HTT */ + __le32 htt_msdus; + __le32 htt_mpdus; + + /* MSDUs / MPDUs delivered to local stack */ + __le32 loc_msdus; + __le32 loc_mpdus; + + /* AMSDUs that have more MSDUs than the status ring size */ + __le32 oversize_amsdu; + + /* Number of PHY errors */ + __le32 phy_errs; + + /* Number of PHY errors drops */ + __le32 phy_err_drop; + + /* Number of mpdu errors - FCS, MIC, ENC etc. */ + __le32 mpdu_errs; +} __packed; + +struct wal_dbg_peer_stats { + /* REMOVE THIS ONCE REAL PEER STAT COUNTERS ARE ADDED */ + __le32 dummy; +} __packed; + +struct wal_dbg_stats { + struct wal_dbg_tx_stats tx; + struct wal_dbg_rx_stats rx; + struct wal_dbg_peer_stats peer; +} __packed; + +enum wmi_stats_id { + WMI_REQUEST_PEER_STAT = 0x01, + WMI_REQUEST_AP_STAT = 0x02 +}; + +struct wmi_request_stats_cmd { + __le32 stats_id; + + /* + * Space to add parameters like + * peer mac addr + */ +} __packed; + +/* Suspend option */ +enum { + /* suspend */ + WMI_PDEV_SUSPEND, + + /* suspend and disable all interrupts */ + WMI_PDEV_SUSPEND_AND_DISABLE_INTR, +}; + +struct wmi_pdev_suspend_cmd { + /* suspend option sent to target */ + __le32 suspend_opt; +} __packed; + +struct wmi_stats_event { + __le32 stats_id; /* %WMI_REQUEST_ */ + /* + * number of pdev stats event structures + * (wmi_pdev_stats) 0 or 1 + */ + __le32 num_pdev_stats; + /* + * number of vdev stats event structures + * (wmi_vdev_stats) 0 or max vdevs + */ + __le32 num_vdev_stats; + /* + * number of peer stats event structures + * (wmi_peer_stats) 0 or max peers + */ + __le32 num_peer_stats; + __le32 num_bcnflt_stats; + /* + * followed by + * num_pdev_stats * size of(struct wmi_pdev_stats) + * num_vdev_stats * size of(struct wmi_vdev_stats) + * num_peer_stats * size of(struct wmi_peer_stats) + * + * By having a zero sized array, the pointer to data area + * becomes available without increasing the struct size + */ + u8 data[0]; +} __packed; + +/* + * PDEV statistics + * TODO: add all PDEV stats here + */ +struct wmi_pdev_stats { + __le32 chan_nf; /* Channel noise floor */ + __le32 tx_frame_count; /* TX frame count */ + __le32 rx_frame_count; /* RX frame count */ + __le32 rx_clear_count; /* rx clear count */ + __le32 cycle_count; /* cycle count */ + __le32 phy_err_count; /* Phy error count */ + __le32 chan_tx_pwr; /* channel tx power */ + struct wal_dbg_stats wal; /* WAL dbg stats */ +} __packed; + +/* + * VDEV statistics + * TODO: add all VDEV stats here + */ +struct wmi_vdev_stats { + __le32 vdev_id; +} __packed; + +/* + * peer statistics. + * TODO: add more stats + */ +struct wmi_peer_stats { + struct wmi_mac_addr peer_macaddr; + __le32 peer_rssi; + __le32 peer_tx_rate; +} __packed; + +struct wmi_vdev_create_cmd { + __le32 vdev_id; + __le32 vdev_type; + __le32 vdev_subtype; + struct wmi_mac_addr vdev_macaddr; +} __packed; + +enum wmi_vdev_type { + WMI_VDEV_TYPE_AP = 1, + WMI_VDEV_TYPE_STA = 2, + WMI_VDEV_TYPE_IBSS = 3, + WMI_VDEV_TYPE_MONITOR = 4, +}; + +enum wmi_vdev_subtype { + WMI_VDEV_SUBTYPE_NONE = 0, + WMI_VDEV_SUBTYPE_P2P_DEVICE = 1, + WMI_VDEV_SUBTYPE_P2P_CLIENT = 2, + WMI_VDEV_SUBTYPE_P2P_GO = 3, +}; + +/* values for vdev_subtype */ + +/* values for vdev_start_request flags */ +/* + * Indicates that AP VDEV uses hidden ssid. only valid for + * AP/GO */ +#define WMI_VDEV_START_HIDDEN_SSID (1<<0) +/* + * Indicates if robust management frame/management frame + * protection is enabled. For GO/AP vdevs, it indicates that + * it may support station/client associations with RMF enabled. + * For STA/client vdevs, it indicates that sta will + * associate with AP with RMF enabled. */ +#define WMI_VDEV_START_PMF_ENABLED (1<<1) + +struct wmi_p2p_noa_descriptor { + __le32 type_count; /* 255: continuous schedule, 0: reserved */ + __le32 duration; /* Absent period duration in micro seconds */ + __le32 interval; /* Absent period interval in micro seconds */ + __le32 start_time; /* 32 bit tsf time when in starts */ +} __packed; + +struct wmi_vdev_start_request_cmd { + /* WMI channel */ + struct wmi_channel chan; + /* unique id identifying the VDEV, generated by the caller */ + __le32 vdev_id; + /* requestor id identifying the caller module */ + __le32 requestor_id; + /* beacon interval from received beacon */ + __le32 beacon_interval; + /* DTIM Period from the received beacon */ + __le32 dtim_period; + /* Flags */ + __le32 flags; + /* ssid field. Only valid for AP/GO/IBSS/BTAmp VDEV type. */ + struct wmi_ssid ssid; + /* beacon/probe reponse xmit rate. Applicable for SoftAP. */ + __le32 bcn_tx_rate; + /* beacon/probe reponse xmit power. Applicable for SoftAP. */ + __le32 bcn_tx_power; + /* number of p2p NOA descriptor(s) from scan entry */ + __le32 num_noa_descriptors; + /* + * Disable H/W ack. This used by WMI_VDEV_RESTART_REQUEST_CMDID. + * During CAC, Our HW shouldn't ack ditected frames + */ + __le32 disable_hw_ack; + /* actual p2p NOA descriptor from scan entry */ + struct wmi_p2p_noa_descriptor noa_descriptors[2]; +} __packed; + +struct wmi_vdev_restart_request_cmd { + struct wmi_vdev_start_request_cmd vdev_start_request_cmd; +} __packed; + +struct wmi_vdev_start_request_arg { + u32 vdev_id; + struct wmi_channel_arg channel; + u32 bcn_intval; + u32 dtim_period; + u8 *ssid; + u32 ssid_len; + u32 bcn_tx_rate; + u32 bcn_tx_power; + bool disable_hw_ack; + bool hidden_ssid; + bool pmf_enabled; +}; + +struct wmi_vdev_delete_cmd { + /* unique id identifying the VDEV, generated by the caller */ + __le32 vdev_id; +} __packed; + +struct wmi_vdev_up_cmd { + __le32 vdev_id; + __le32 vdev_assoc_id; + struct wmi_mac_addr vdev_bssid; +} __packed; + +struct wmi_vdev_stop_cmd { + __le32 vdev_id; +} __packed; + +struct wmi_vdev_down_cmd { + __le32 vdev_id; +} __packed; + +struct wmi_vdev_standby_response_cmd { + /* unique id identifying the VDEV, generated by the caller */ + __le32 vdev_id; +} __packed; + +struct wmi_vdev_resume_response_cmd { + /* unique id identifying the VDEV, generated by the caller */ + __le32 vdev_id; +} __packed; + +struct wmi_vdev_set_param_cmd { + __le32 vdev_id; + __le32 param_id; + __le32 param_value; +} __packed; + +#define WMI_MAX_KEY_INDEX 3 +#define WMI_MAX_KEY_LEN 32 + +#define WMI_KEY_PAIRWISE 0x00 +#define WMI_KEY_GROUP 0x01 +#define WMI_KEY_TX_USAGE 0x02 /* default tx key - static wep */ + +struct wmi_key_seq_counter { + __le32 key_seq_counter_l; + __le32 key_seq_counter_h; +} __packed; + +#define WMI_CIPHER_NONE 0x0 /* clear key */ +#define WMI_CIPHER_WEP 0x1 +#define WMI_CIPHER_TKIP 0x2 +#define WMI_CIPHER_AES_OCB 0x3 +#define WMI_CIPHER_AES_CCM 0x4 +#define WMI_CIPHER_WAPI 0x5 +#define WMI_CIPHER_CKIP 0x6 +#define WMI_CIPHER_AES_CMAC 0x7 + +struct wmi_vdev_install_key_cmd { + __le32 vdev_id; + struct wmi_mac_addr peer_macaddr; + __le32 key_idx; + __le32 key_flags; + __le32 key_cipher; /* %WMI_CIPHER_ */ + struct wmi_key_seq_counter key_rsc_counter; + struct wmi_key_seq_counter key_global_rsc_counter; + struct wmi_key_seq_counter key_tsc_counter; + u8 wpi_key_rsc_counter[16]; + u8 wpi_key_tsc_counter[16]; + __le32 key_len; + __le32 key_txmic_len; + __le32 key_rxmic_len; + + /* contains key followed by tx mic followed by rx mic */ + u8 key_data[0]; +} __packed; + +struct wmi_vdev_install_key_arg { + u32 vdev_id; + const u8 *macaddr; + u32 key_idx; + u32 key_flags; + u32 key_cipher; + u32 key_len; + u32 key_txmic_len; + u32 key_rxmic_len; + const void *key_data; +}; + +/* Preamble types to be used with VDEV fixed rate configuration */ +enum wmi_rate_preamble { + WMI_RATE_PREAMBLE_OFDM, + WMI_RATE_PREAMBLE_CCK, + WMI_RATE_PREAMBLE_HT, + WMI_RATE_PREAMBLE_VHT, +}; + +/* Value to disable fixed rate setting */ +#define WMI_FIXED_RATE_NONE (0xff) + +/* the definition of different VDEV parameters */ +enum wmi_vdev_param { + /* RTS Threshold */ + WMI_VDEV_PARAM_RTS_THRESHOLD = 0x1, + /* Fragmentation threshold */ + WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD, + /* beacon interval in TUs */ + WMI_VDEV_PARAM_BEACON_INTERVAL, + /* Listen interval in TUs */ + WMI_VDEV_PARAM_LISTEN_INTERVAL, + /* muticast rate in Mbps */ + WMI_VDEV_PARAM_MULTICAST_RATE, + /* management frame rate in Mbps */ + WMI_VDEV_PARAM_MGMT_TX_RATE, + /* slot time (long vs short) */ + WMI_VDEV_PARAM_SLOT_TIME, + /* preamble (long vs short) */ + WMI_VDEV_PARAM_PREAMBLE, + /* SWBA time (time before tbtt in msec) */ + WMI_VDEV_PARAM_SWBA_TIME, + /* time period for updating VDEV stats */ + WMI_VDEV_STATS_UPDATE_PERIOD, + /* age out time in msec for frames queued for station in power save */ + WMI_VDEV_PWRSAVE_AGEOUT_TIME, + /* + * Host SWBA interval (time in msec before tbtt for SWBA event + * generation). + */ + WMI_VDEV_HOST_SWBA_INTERVAL, + /* DTIM period (specified in units of num beacon intervals) */ + WMI_VDEV_PARAM_DTIM_PERIOD, + /* + * scheduler air time limit for this VDEV. used by off chan + * scheduler. + */ + WMI_VDEV_OC_SCHEDULER_AIR_TIME_LIMIT, + /* enable/dsiable WDS for this VDEV */ + WMI_VDEV_PARAM_WDS, + /* ATIM Window */ + WMI_VDEV_PARAM_ATIM_WINDOW, + /* BMISS max */ + WMI_VDEV_PARAM_BMISS_COUNT_MAX, + /* BMISS first time */ + WMI_VDEV_PARAM_BMISS_FIRST_BCNT, + /* BMISS final time */ + WMI_VDEV_PARAM_BMISS_FINAL_BCNT, + /* WMM enables/disabled */ + WMI_VDEV_PARAM_FEATURE_WMM, + /* Channel width */ + WMI_VDEV_PARAM_CHWIDTH, + /* Channel Offset */ + WMI_VDEV_PARAM_CHEXTOFFSET, + /* Disable HT Protection */ + WMI_VDEV_PARAM_DISABLE_HTPROTECTION, + /* Quick STA Kickout */ + WMI_VDEV_PARAM_STA_QUICKKICKOUT, + /* Rate to be used with Management frames */ + WMI_VDEV_PARAM_MGMT_RATE, + /* Protection Mode */ + WMI_VDEV_PARAM_PROTECTION_MODE, + /* Fixed rate setting */ + WMI_VDEV_PARAM_FIXED_RATE, + /* Short GI Enable/Disable */ + WMI_VDEV_PARAM_SGI, + /* Enable LDPC */ + WMI_VDEV_PARAM_LDPC, + /* Enable Tx STBC */ + WMI_VDEV_PARAM_TX_STBC, + /* Enable Rx STBC */ + WMI_VDEV_PARAM_RX_STBC, + /* Intra BSS forwarding */ + WMI_VDEV_PARAM_INTRA_BSS_FWD, + /* Setting Default xmit key for Vdev */ + WMI_VDEV_PARAM_DEF_KEYID, + /* NSS width */ + WMI_VDEV_PARAM_NSS, + /* Set the custom rate for the broadcast data frames */ + WMI_VDEV_PARAM_BCAST_DATA_RATE, + /* Set the custom rate (rate-code) for multicast data frames */ + WMI_VDEV_PARAM_MCAST_DATA_RATE, + /* Tx multicast packet indicate Enable/Disable */ + WMI_VDEV_PARAM_MCAST_INDICATE, + /* Tx DHCP packet indicate Enable/Disable */ + WMI_VDEV_PARAM_DHCP_INDICATE, + /* Enable host inspection of Tx unicast packet to unknown destination */ + WMI_VDEV_PARAM_UNKNOWN_DEST_INDICATE, + + /* The minimum amount of time AP begins to consider STA inactive */ + WMI_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS, + + /* + * An associated STA is considered inactive when there is no recent + * TX/RX activity and no downlink frames are buffered for it. Once a + * STA exceeds the maximum idle inactive time, the AP will send an + * 802.11 data-null as a keep alive to verify the STA is still + * associated. If the STA does ACK the data-null, or if the data-null + * is buffered and the STA does not retrieve it, the STA will be + * considered unresponsive + * (see WMI_VDEV_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS). + */ + WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS, + + /* + * An associated STA is considered unresponsive if there is no recent + * TX/RX activity and downlink frames are buffered for it. Once a STA + * exceeds the maximum unresponsive time, the AP will send a + * WMI_STA_KICKOUT event to the host so the STA can be deleted. */ + WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS, + + /* Enable NAWDS : MCAST INSPECT Enable, NAWDS Flag set */ + WMI_VDEV_PARAM_AP_ENABLE_NAWDS, + /* Enable/Disable RTS-CTS */ + WMI_VDEV_PARAM_ENABLE_RTSCTS, + /* Enable TXBFee/er */ + WMI_VDEV_PARAM_TXBF, + + /* Set packet power save */ + WMI_VDEV_PARAM_PACKET_POWERSAVE, + + /* + * Drops un-encrypted packets if eceived in an encrypted connection + * otherwise forwards to host. + */ + WMI_VDEV_PARAM_DROP_UNENCRY, + + /* + * Set the encapsulation type for frames. + */ + WMI_VDEV_PARAM_TX_ENCAP_TYPE, +}; + +/* slot time long */ +#define WMI_VDEV_SLOT_TIME_LONG 0x1 +/* slot time short */ +#define WMI_VDEV_SLOT_TIME_SHORT 0x2 +/* preablbe long */ +#define WMI_VDEV_PREAMBLE_LONG 0x1 +/* preablbe short */ +#define WMI_VDEV_PREAMBLE_SHORT 0x2 + +enum wmi_start_event_param { + WMI_VDEV_RESP_START_EVENT = 0, + WMI_VDEV_RESP_RESTART_EVENT, +}; + +struct wmi_vdev_start_response_event { + __le32 vdev_id; + __le32 req_id; + __le32 resp_type; /* %WMI_VDEV_RESP_ */ + __le32 status; +} __packed; + +struct wmi_vdev_standby_req_event { + /* unique id identifying the VDEV, generated by the caller */ + __le32 vdev_id; +} __packed; + +struct wmi_vdev_resume_req_event { + /* unique id identifying the VDEV, generated by the caller */ + __le32 vdev_id; +} __packed; + +struct wmi_vdev_stopped_event { + /* unique id identifying the VDEV, generated by the caller */ + __le32 vdev_id; +} __packed; + +/* + * common structure used for simple events + * (stopped, resume_req, standby response) + */ +struct wmi_vdev_simple_event { + /* unique id identifying the VDEV, generated by the caller */ + __le32 vdev_id; +} __packed; + +/* VDEV start response status codes */ +/* VDEV succesfully started */ +#define WMI_INIFIED_VDEV_START_RESPONSE_STATUS_SUCCESS 0x0 + +/* requested VDEV not found */ +#define WMI_INIFIED_VDEV_START_RESPONSE_INVALID_VDEVID 0x1 + +/* unsupported VDEV combination */ +#define WMI_INIFIED_VDEV_START_RESPONSE_NOT_SUPPORTED 0x2 + +/* Beacon processing related command and event structures */ +struct wmi_bcn_tx_hdr { + __le32 vdev_id; + __le32 tx_rate; + __le32 tx_power; + __le32 bcn_len; +} __packed; + +struct wmi_bcn_tx_cmd { + struct wmi_bcn_tx_hdr hdr; + u8 *bcn[0]; +} __packed; + +struct wmi_bcn_tx_arg { + u32 vdev_id; + u32 tx_rate; + u32 tx_power; + u32 bcn_len; + const void *bcn; +}; + +/* Beacon filter */ +#define WMI_BCN_FILTER_ALL 0 /* Filter all beacons */ +#define WMI_BCN_FILTER_NONE 1 /* Pass all beacons */ +#define WMI_BCN_FILTER_RSSI 2 /* Pass Beacons RSSI >= RSSI threshold */ +#define WMI_BCN_FILTER_BSSID 3 /* Pass Beacons with matching BSSID */ +#define WMI_BCN_FILTER_SSID 4 /* Pass Beacons with matching SSID */ + +struct wmi_bcn_filter_rx_cmd { + /* Filter ID */ + __le32 bcn_filter_id; + /* Filter type - wmi_bcn_filter */ + __le32 bcn_filter; + /* Buffer len */ + __le32 bcn_filter_len; + /* Filter info (threshold, BSSID, RSSI) */ + u8 *bcn_filter_buf; +} __packed; + +/* Capabilities and IEs to be passed to firmware */ +struct wmi_bcn_prb_info { + /* Capabilities */ + __le32 caps; + /* ERP info */ + __le32 erp; + /* Advanced capabilities */ + /* HT capabilities */ + /* HT Info */ + /* ibss_dfs */ + /* wpa Info */ + /* rsn Info */ + /* rrm info */ + /* ath_ext */ + /* app IE */ +} __packed; + +struct wmi_bcn_tmpl_cmd { + /* unique id identifying the VDEV, generated by the caller */ + __le32 vdev_id; + /* TIM IE offset from the beginning of the template. */ + __le32 tim_ie_offset; + /* beacon probe capabilities and IEs */ + struct wmi_bcn_prb_info bcn_prb_info; + /* beacon buffer length */ + __le32 buf_len; + /* variable length data */ + u8 data[1]; +} __packed; + +struct wmi_prb_tmpl_cmd { + /* unique id identifying the VDEV, generated by the caller */ + __le32 vdev_id; + /* beacon probe capabilities and IEs */ + struct wmi_bcn_prb_info bcn_prb_info; + /* beacon buffer length */ + __le32 buf_len; + /* Variable length data */ + u8 data[1]; +} __packed; + +enum wmi_sta_ps_mode { + /* enable power save for the given STA VDEV */ + WMI_STA_PS_MODE_DISABLED = 0, + /* disable power save for a given STA VDEV */ + WMI_STA_PS_MODE_ENABLED = 1, +}; + +struct wmi_sta_powersave_mode_cmd { + /* unique id identifying the VDEV, generated by the caller */ + __le32 vdev_id; + + /* + * Power save mode + * (see enum wmi_sta_ps_mode) + */ + __le32 sta_ps_mode; +} __packed; + +enum wmi_csa_offload_en { + WMI_CSA_OFFLOAD_DISABLE = 0, + WMI_CSA_OFFLOAD_ENABLE = 1, +}; + +struct wmi_csa_offload_enable_cmd { + __le32 vdev_id; + __le32 csa_offload_enable; +} __packed; + +struct wmi_csa_offload_chanswitch_cmd { + __le32 vdev_id; + struct wmi_channel chan; +} __packed; + +/* + * This parameter controls the policy for retrieving frames from AP while the + * STA is in sleep state. + * + * Only takes affect if the sta_ps_mode is enabled + */ +enum wmi_sta_ps_param_rx_wake_policy { + /* + * Wake up when ever there is an RX activity on the VDEV. In this mode + * the Power save SM(state machine) will come out of sleep by either + * sending null frame (or) a data frame (with PS==0) in response to TIM + * bit set in the received beacon frame from AP. + */ + WMI_STA_PS_RX_WAKE_POLICY_WAKE = 0, + + /* + * Here the power save state machine will not wakeup in response to TIM + * bit, instead it will send a PSPOLL (or) UASPD trigger based on UAPSD + * configuration setup by WMISET_PS_SET_UAPSD WMI command. When all + * access categories are delivery-enabled, the station will send a + * UAPSD trigger frame, otherwise it will send a PS-Poll. + */ + WMI_STA_PS_RX_WAKE_POLICY_POLL_UAPSD = 1, +}; + +/* + * Number of tx frames/beacon that cause the power save SM to wake up. + * + * Value 1 causes the SM to wake up for every TX. Value 0 has a special + * meaning, It will cause the SM to never wake up. This is useful if you want + * to keep the system to sleep all the time for some kind of test mode . host + * can change this parameter any time. It will affect at the next tx frame. + */ +enum wmi_sta_ps_param_tx_wake_threshold { + WMI_STA_PS_TX_WAKE_THRESHOLD_NEVER = 0, + WMI_STA_PS_TX_WAKE_THRESHOLD_ALWAYS = 1, + + /* + * Values greater than one indicate that many TX attempts per beacon + * interval before the STA will wake up + */ +}; + +/* + * The maximum number of PS-Poll frames the FW will send in response to + * traffic advertised in TIM before waking up (by sending a null frame with PS + * = 0). Value 0 has a special meaning: there is no maximum count and the FW + * will send as many PS-Poll as are necessary to retrieve buffered BU. This + * parameter is used when the RX wake policy is + * WMI_STA_PS_RX_WAKE_POLICY_POLL_UAPSD and ignored when the RX wake + * policy is WMI_STA_PS_RX_WAKE_POLICY_WAKE. + */ +enum wmi_sta_ps_param_pspoll_count { + WMI_STA_PS_PSPOLL_COUNT_NO_MAX = 0, + /* + * Values greater than 0 indicate the maximum numer of PS-Poll frames + * FW will send before waking up. + */ +}; + +/* + * This will include the delivery and trigger enabled state for every AC. + * This is the negotiated state with AP. The host MLME needs to set this based + * on AP capability and the state Set in the association request by the + * station MLME.Lower 8 bits of the value specify the UAPSD configuration. + */ +#define WMI_UAPSD_AC_TYPE_DELI 0 +#define WMI_UAPSD_AC_TYPE_TRIG 1 + +#define WMI_UAPSD_AC_BIT_MASK(ac, type) \ + ((type == WMI_UAPSD_AC_TYPE_DELI) ? (1<<(ac<<1)) : (1<<((ac<<1)+1))) + +enum wmi_sta_ps_param_uapsd { + WMI_STA_PS_UAPSD_AC0_DELIVERY_EN = (1 << 0), + WMI_STA_PS_UAPSD_AC0_TRIGGER_EN = (1 << 1), + WMI_STA_PS_UAPSD_AC1_DELIVERY_EN = (1 << 2), + WMI_STA_PS_UAPSD_AC1_TRIGGER_EN = (1 << 3), + WMI_STA_PS_UAPSD_AC2_DELIVERY_EN = (1 << 4), + WMI_STA_PS_UAPSD_AC2_TRIGGER_EN = (1 << 5), + WMI_STA_PS_UAPSD_AC3_DELIVERY_EN = (1 << 6), + WMI_STA_PS_UAPSD_AC3_TRIGGER_EN = (1 << 7), +}; + +enum wmi_sta_powersave_param { + /* + * Controls how frames are retrievd from AP while STA is sleeping + * + * (see enum wmi_sta_ps_param_rx_wake_policy) + */ + WMI_STA_PS_PARAM_RX_WAKE_POLICY = 0, + + /* + * The STA will go active after this many TX + * + * (see enum wmi_sta_ps_param_tx_wake_threshold) + */ + WMI_STA_PS_PARAM_TX_WAKE_THRESHOLD = 1, + + /* + * Number of PS-Poll to send before STA wakes up + * + * (see enum wmi_sta_ps_param_pspoll_count) + * + */ + WMI_STA_PS_PARAM_PSPOLL_COUNT = 2, + + /* + * TX/RX inactivity time in msec before going to sleep. + * + * The power save SM will monitor tx/rx activity on the VDEV, if no + * activity for the specified msec of the parameter the Power save + * SM will go to sleep. + */ + WMI_STA_PS_PARAM_INACTIVITY_TIME = 3, + + /* + * Set uapsd configuration. + * + * (see enum wmi_sta_ps_param_uapsd) + */ + WMI_STA_PS_PARAM_UAPSD = 4, +}; + +struct wmi_sta_powersave_param_cmd { + __le32 vdev_id; + __le32 param_id; /* %WMI_STA_PS_PARAM_ */ + __le32 param_value; +} __packed; + +/* No MIMO power save */ +#define WMI_STA_MIMO_PS_MODE_DISABLE +/* mimo powersave mode static*/ +#define WMI_STA_MIMO_PS_MODE_STATIC +/* mimo powersave mode dynamic */ +#define WMI_STA_MIMO_PS_MODE_DYNAMIC + +struct wmi_sta_mimo_ps_mode_cmd { + /* unique id identifying the VDEV, generated by the caller */ + __le32 vdev_id; + /* mimo powersave mode as defined above */ + __le32 mimo_pwrsave_mode; +} __packed; + +/* U-APSD configuration of peer station from (re)assoc request and TSPECs */ +enum wmi_ap_ps_param_uapsd { + WMI_AP_PS_UAPSD_AC0_DELIVERY_EN = (1 << 0), + WMI_AP_PS_UAPSD_AC0_TRIGGER_EN = (1 << 1), + WMI_AP_PS_UAPSD_AC1_DELIVERY_EN = (1 << 2), + WMI_AP_PS_UAPSD_AC1_TRIGGER_EN = (1 << 3), + WMI_AP_PS_UAPSD_AC2_DELIVERY_EN = (1 << 4), + WMI_AP_PS_UAPSD_AC2_TRIGGER_EN = (1 << 5), + WMI_AP_PS_UAPSD_AC3_DELIVERY_EN = (1 << 6), + WMI_AP_PS_UAPSD_AC3_TRIGGER_EN = (1 << 7), +}; + +/* U-APSD maximum service period of peer station */ +enum wmi_ap_ps_peer_param_max_sp { + WMI_AP_PS_PEER_PARAM_MAX_SP_UNLIMITED = 0, + WMI_AP_PS_PEER_PARAM_MAX_SP_2 = 1, + WMI_AP_PS_PEER_PARAM_MAX_SP_4 = 2, + WMI_AP_PS_PEER_PARAM_MAX_SP_6 = 3, + MAX_WMI_AP_PS_PEER_PARAM_MAX_SP, +}; + +/* + * AP power save parameter + * Set a power save specific parameter for a peer station + */ +enum wmi_ap_ps_peer_param { + /* Set uapsd configuration for a given peer. + * + * Include the delivery and trigger enabled state for every AC. + * The host MLME needs to set this based on AP capability and stations + * request Set in the association request received from the station. + * + * Lower 8 bits of the value specify the UAPSD configuration. + * + * (see enum wmi_ap_ps_param_uapsd) + * The default value is 0. + */ + WMI_AP_PS_PEER_PARAM_UAPSD = 0, + + /* + * Set the service period for a UAPSD capable station + * + * The service period from wme ie in the (re)assoc request frame. + * + * (see enum wmi_ap_ps_peer_param_max_sp) + */ + WMI_AP_PS_PEER_PARAM_MAX_SP = 1, + + /* Time in seconds for aging out buffered frames for STA in PS */ + WMI_AP_PS_PEER_PARAM_AGEOUT_TIME = 2, +}; + +struct wmi_ap_ps_peer_cmd { + /* unique id identifying the VDEV, generated by the caller */ + __le32 vdev_id; + + /* peer MAC address */ + struct wmi_mac_addr peer_macaddr; + + /* AP powersave param (see enum wmi_ap_ps_peer_param) */ + __le32 param_id; + + /* AP powersave param value */ + __le32 param_value; +} __packed; + +/* 128 clients = 4 words */ +#define WMI_TIM_BITMAP_ARRAY_SIZE 4 + +struct wmi_tim_info { + __le32 tim_len; + __le32 tim_mcast; + __le32 tim_bitmap[WMI_TIM_BITMAP_ARRAY_SIZE]; + __le32 tim_changed; + __le32 tim_num_ps_pending; +} __packed; + +/* Maximum number of NOA Descriptors supported */ +#define WMI_P2P_MAX_NOA_DESCRIPTORS 4 +#define WMI_P2P_OPPPS_ENABLE_BIT BIT(0) +#define WMI_P2P_OPPPS_CTWINDOW_OFFSET 1 +#define WMI_P2P_NOA_CHANGED_BIT BIT(0) + +struct wmi_p2p_noa_info { + /* Bit 0 - Flag to indicate an update in NOA schedule + Bits 7-1 - Reserved */ + u8 changed; + /* NOA index */ + u8 index; + /* Bit 0 - Opp PS state of the AP + Bits 1-7 - Ctwindow in TUs */ + u8 ctwindow_oppps; + /* Number of NOA descriptors */ + u8 num_descriptors; + + struct wmi_p2p_noa_descriptor descriptors[WMI_P2P_MAX_NOA_DESCRIPTORS]; +} __packed; + +struct wmi_bcn_info { + struct wmi_tim_info tim_info; + struct wmi_p2p_noa_info p2p_noa_info; +} __packed; + +struct wmi_host_swba_event { + __le32 vdev_map; + struct wmi_bcn_info bcn_info[1]; +} __packed; + +#define WMI_MAX_AP_VDEV 16 + +struct wmi_tbtt_offset_event { + __le32 vdev_map; + __le32 tbttoffset_list[WMI_MAX_AP_VDEV]; +} __packed; + + +struct wmi_peer_create_cmd { + __le32 vdev_id; + struct wmi_mac_addr peer_macaddr; +} __packed; + +struct wmi_peer_delete_cmd { + __le32 vdev_id; + struct wmi_mac_addr peer_macaddr; +} __packed; + +struct wmi_peer_flush_tids_cmd { + __le32 vdev_id; + struct wmi_mac_addr peer_macaddr; + __le32 peer_tid_bitmap; +} __packed; + +struct wmi_fixed_rate { + /* + * rate mode . 0: disable fixed rate (auto rate) + * 1: legacy (non 11n) rate specified as ieee rate 2*Mbps + * 2: ht20 11n rate specified as mcs index + * 3: ht40 11n rate specified as mcs index + */ + __le32 rate_mode; + /* + * 4 rate values for 4 rate series. series 0 is stored in byte 0 (LSB) + * and series 3 is stored at byte 3 (MSB) + */ + __le32 rate_series; + /* + * 4 retry counts for 4 rate series. retry count for rate 0 is stored + * in byte 0 (LSB) and retry count for rate 3 is stored at byte 3 + * (MSB) + */ + __le32 rate_retries; +} __packed; + +struct wmi_peer_fixed_rate_cmd { + /* unique id identifying the VDEV, generated by the caller */ + __le32 vdev_id; + /* peer MAC address */ + struct wmi_mac_addr peer_macaddr; + /* fixed rate */ + struct wmi_fixed_rate peer_fixed_rate; +} __packed; + +#define WMI_MGMT_TID 17 + +struct wmi_addba_clear_resp_cmd { + /* unique id identifying the VDEV, generated by the caller */ + __le32 vdev_id; + /* peer MAC address */ + struct wmi_mac_addr peer_macaddr; +} __packed; + +struct wmi_addba_send_cmd { + /* unique id identifying the VDEV, generated by the caller */ + __le32 vdev_id; + /* peer MAC address */ + struct wmi_mac_addr peer_macaddr; + /* Tid number */ + __le32 tid; + /* Buffer/Window size*/ + __le32 buffersize; +} __packed; + +struct wmi_delba_send_cmd { + /* unique id identifying the VDEV, generated by the caller */ + __le32 vdev_id; + /* peer MAC address */ + struct wmi_mac_addr peer_macaddr; + /* Tid number */ + __le32 tid; + /* Is Initiator */ + __le32 initiator; + /* Reason code */ + __le32 reasoncode; +} __packed; + +struct wmi_addba_setresponse_cmd { + /* unique id identifying the vdev, generated by the caller */ + __le32 vdev_id; + /* peer mac address */ + struct wmi_mac_addr peer_macaddr; + /* Tid number */ + __le32 tid; + /* status code */ + __le32 statuscode; +} __packed; + +struct wmi_send_singleamsdu_cmd { + /* unique id identifying the vdev, generated by the caller */ + __le32 vdev_id; + /* peer mac address */ + struct wmi_mac_addr peer_macaddr; + /* Tid number */ + __le32 tid; +} __packed; + +enum wmi_peer_smps_state { + WMI_PEER_SMPS_PS_NONE = 0x0, + WMI_PEER_SMPS_STATIC = 0x1, + WMI_PEER_SMPS_DYNAMIC = 0x2 +}; + +enum wmi_peer_param { + WMI_PEER_SMPS_STATE = 0x1, /* see %wmi_peer_smps_state */ + WMI_PEER_AMPDU = 0x2, + WMI_PEER_AUTHORIZE = 0x3, + WMI_PEER_CHAN_WIDTH = 0x4, + WMI_PEER_NSS = 0x5, + WMI_PEER_USE_4ADDR = 0x6 +}; + +struct wmi_peer_set_param_cmd { + __le32 vdev_id; + struct wmi_mac_addr peer_macaddr; + __le32 param_id; + __le32 param_value; +} __packed; + +#define MAX_SUPPORTED_RATES 128 + +struct wmi_rate_set { + /* total number of rates */ + __le32 num_rates; + /* + * rates (each 8bit value) packed into a 32 bit word. + * the rates are filled from least significant byte to most + * significant byte. + */ + __le32 rates[(MAX_SUPPORTED_RATES/4)+1]; +} __packed; + +struct wmi_rate_set_arg { + unsigned int num_rates; + u8 rates[MAX_SUPPORTED_RATES]; +}; + +/* + * NOTE: It would bea good idea to represent the Tx MCS + * info in one word and Rx in another word. This is split + * into multiple words for convenience + */ +struct wmi_vht_rate_set { + __le32 rx_max_rate; /* Max Rx data rate */ + __le32 rx_mcs_set; /* Negotiated RX VHT rates */ + __le32 tx_max_rate; /* Max Tx data rate */ + __le32 tx_mcs_set; /* Negotiated TX VHT rates */ +} __packed; + +struct wmi_vht_rate_set_arg { + u32 rx_max_rate; + u32 rx_mcs_set; + u32 tx_max_rate; + u32 tx_mcs_set; +}; + +struct wmi_peer_set_rates_cmd { + /* peer MAC address */ + struct wmi_mac_addr peer_macaddr; + /* legacy rate set */ + struct wmi_rate_set peer_legacy_rates; + /* ht rate set */ + struct wmi_rate_set peer_ht_rates; +} __packed; + +struct wmi_peer_set_q_empty_callback_cmd { + /* unique id identifying the VDEV, generated by the caller */ + __le32 vdev_id; + /* peer MAC address */ + struct wmi_mac_addr peer_macaddr; + __le32 callback_enable; +} __packed; + +#define WMI_PEER_AUTH 0x00000001 +#define WMI_PEER_QOS 0x00000002 +#define WMI_PEER_NEED_PTK_4_WAY 0x00000004 +#define WMI_PEER_NEED_GTK_2_WAY 0x00000010 +#define WMI_PEER_APSD 0x00000800 +#define WMI_PEER_HT 0x00001000 +#define WMI_PEER_40MHZ 0x00002000 +#define WMI_PEER_STBC 0x00008000 +#define WMI_PEER_LDPC 0x00010000 +#define WMI_PEER_DYN_MIMOPS 0x00020000 +#define WMI_PEER_STATIC_MIMOPS 0x00040000 +#define WMI_PEER_SPATIAL_MUX 0x00200000 +#define WMI_PEER_VHT 0x02000000 +#define WMI_PEER_80MHZ 0x04000000 +#define WMI_PEER_PMF 0x08000000 + +/* + * Peer rate capabilities. + * + * This is of interest to the ratecontrol + * module which resides in the firmware. The bit definitions are + * consistent with that defined in if_athrate.c. + */ +#define WMI_RC_DS_FLAG 0x01 +#define WMI_RC_CW40_FLAG 0x02 +#define WMI_RC_SGI_FLAG 0x04 +#define WMI_RC_HT_FLAG 0x08 +#define WMI_RC_RTSCTS_FLAG 0x10 +#define WMI_RC_TX_STBC_FLAG 0x20 +#define WMI_RC_RX_STBC_FLAG 0xC0 +#define WMI_RC_RX_STBC_FLAG_S 6 +#define WMI_RC_WEP_TKIP_FLAG 0x100 +#define WMI_RC_TS_FLAG 0x200 +#define WMI_RC_UAPSD_FLAG 0x400 + +/* Maximum listen interval supported by hw in units of beacon interval */ +#define ATH10K_MAX_HW_LISTEN_INTERVAL 5 + +struct wmi_peer_assoc_complete_cmd { + struct wmi_mac_addr peer_macaddr; + __le32 vdev_id; + __le32 peer_new_assoc; /* 1=assoc, 0=reassoc */ + __le32 peer_associd; /* 16 LSBs */ + __le32 peer_flags; + __le32 peer_caps; /* 16 LSBs */ + __le32 peer_listen_intval; + __le32 peer_ht_caps; + __le32 peer_max_mpdu; + __le32 peer_mpdu_density; /* 0..16 */ + __le32 peer_rate_caps; + struct wmi_rate_set peer_legacy_rates; + struct wmi_rate_set peer_ht_rates; + __le32 peer_nss; /* num of spatial streams */ + __le32 peer_vht_caps; + __le32 peer_phymode; + struct wmi_vht_rate_set peer_vht_rates; + /* HT Operation Element of the peer. Five bytes packed in 2 + * INT32 array and filled from lsb to msb. */ + __le32 peer_ht_info[2]; +} __packed; + +struct wmi_peer_assoc_complete_arg { + u8 addr[ETH_ALEN]; + u32 vdev_id; + bool peer_reassoc; + u16 peer_aid; + u32 peer_flags; /* see %WMI_PEER_ */ + u16 peer_caps; + u32 peer_listen_intval; + u32 peer_ht_caps; + u32 peer_max_mpdu; + u32 peer_mpdu_density; /* 0..16 */ + u32 peer_rate_caps; /* see %WMI_RC_ */ + struct wmi_rate_set_arg peer_legacy_rates; + struct wmi_rate_set_arg peer_ht_rates; + u32 peer_num_spatial_streams; + u32 peer_vht_caps; + enum wmi_phy_mode peer_phymode; + struct wmi_vht_rate_set_arg peer_vht_rates; +}; + +struct wmi_peer_add_wds_entry_cmd { + /* peer MAC address */ + struct wmi_mac_addr peer_macaddr; + /* wds MAC addr */ + struct wmi_mac_addr wds_macaddr; +} __packed; + +struct wmi_peer_remove_wds_entry_cmd { + /* wds MAC addr */ + struct wmi_mac_addr wds_macaddr; +} __packed; + +struct wmi_peer_q_empty_callback_event { + /* peer MAC address */ + struct wmi_mac_addr peer_macaddr; +} __packed; + +/* + * Channel info WMI event + */ +struct wmi_chan_info_event { + __le32 err_code; + __le32 freq; + __le32 cmd_flags; + __le32 noise_floor; + __le32 rx_clear_count; + __le32 cycle_count; +} __packed; + +/* Beacon filter wmi command info */ +#define BCN_FLT_MAX_SUPPORTED_IES 256 +#define BCN_FLT_MAX_ELEMS_IE_LIST (BCN_FLT_MAX_SUPPORTED_IES / 32) + +struct bss_bcn_stats { + __le32 vdev_id; + __le32 bss_bcnsdropped; + __le32 bss_bcnsdelivered; +} __packed; + +struct bcn_filter_stats { + __le32 bcns_dropped; + __le32 bcns_delivered; + __le32 activefilters; + struct bss_bcn_stats bss_stats; +} __packed; + +struct wmi_add_bcn_filter_cmd { + u32 vdev_id; + u32 ie_map[BCN_FLT_MAX_ELEMS_IE_LIST]; +} __packed; + +enum wmi_sta_keepalive_method { + WMI_STA_KEEPALIVE_METHOD_NULL_FRAME = 1, + WMI_STA_KEEPALIVE_METHOD_UNSOLICITATED_ARP_RESPONSE = 2, +}; + +/* note: ip4 addresses are in network byte order, i.e. big endian */ +struct wmi_sta_keepalive_arp_resp { + __be32 src_ip4_addr; + __be32 dest_ip4_addr; + struct wmi_mac_addr dest_mac_addr; +} __packed; + +struct wmi_sta_keepalive_cmd { + __le32 vdev_id; + __le32 enabled; + __le32 method; /* WMI_STA_KEEPALIVE_METHOD_ */ + __le32 interval; /* in seconds */ + struct wmi_sta_keepalive_arp_resp arp_resp; +} __packed; + +#define ATH10K_RTS_MAX 2347 +#define ATH10K_FRAGMT_THRESHOLD_MIN 540 +#define ATH10K_FRAGMT_THRESHOLD_MAX 2346 + +#define WMI_MAX_EVENT 0x1000 +/* Maximum number of pending TXed WMI packets */ +#define WMI_MAX_PENDING_TX_COUNT 128 +#define WMI_SKB_HEADROOM sizeof(struct wmi_cmd_hdr) + +/* By default disable power save for IBSS */ +#define ATH10K_DEFAULT_ATIM 0 + +struct ath10k; +struct ath10k_vif; + +int ath10k_wmi_attach(struct ath10k *ar); +void ath10k_wmi_detach(struct ath10k *ar); +int ath10k_wmi_wait_for_service_ready(struct ath10k *ar); +int ath10k_wmi_wait_for_unified_ready(struct ath10k *ar); +void ath10k_wmi_flush_tx(struct ath10k *ar); + +int ath10k_wmi_connect_htc_service(struct ath10k *ar); +int ath10k_wmi_pdev_set_channel(struct ath10k *ar, + const struct wmi_channel_arg *); +int ath10k_wmi_pdev_suspend_target(struct ath10k *ar); +int ath10k_wmi_pdev_resume_target(struct ath10k *ar); +int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g, + u16 rd5g, u16 ctl2g, u16 ctl5g); +int ath10k_wmi_pdev_set_param(struct ath10k *ar, enum wmi_pdev_param id, + u32 value); +int ath10k_wmi_cmd_init(struct ath10k *ar); +int ath10k_wmi_start_scan(struct ath10k *ar, const struct wmi_start_scan_arg *); +void ath10k_wmi_start_scan_init(struct ath10k *ar, struct wmi_start_scan_arg *); +int ath10k_wmi_stop_scan(struct ath10k *ar, + const struct wmi_stop_scan_arg *arg); +int ath10k_wmi_vdev_create(struct ath10k *ar, u32 vdev_id, + enum wmi_vdev_type type, + enum wmi_vdev_subtype subtype, + const u8 macaddr[ETH_ALEN]); +int ath10k_wmi_vdev_delete(struct ath10k *ar, u32 vdev_id); +int ath10k_wmi_vdev_start(struct ath10k *ar, + const struct wmi_vdev_start_request_arg *); +int ath10k_wmi_vdev_restart(struct ath10k *ar, + const struct wmi_vdev_start_request_arg *); +int ath10k_wmi_vdev_stop(struct ath10k *ar, u32 vdev_id); +int ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, + const u8 *bssid); +int ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id); +int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id, + enum wmi_vdev_param param_id, u32 param_value); +int ath10k_wmi_vdev_install_key(struct ath10k *ar, + const struct wmi_vdev_install_key_arg *arg); +int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id, + const u8 peer_addr[ETH_ALEN]); +int ath10k_wmi_peer_delete(struct ath10k *ar, u32 vdev_id, + const u8 peer_addr[ETH_ALEN]); +int ath10k_wmi_peer_flush(struct ath10k *ar, u32 vdev_id, + const u8 peer_addr[ETH_ALEN], u32 tid_bitmap); +int ath10k_wmi_peer_set_param(struct ath10k *ar, u32 vdev_id, + const u8 *peer_addr, + enum wmi_peer_param param_id, u32 param_value); +int ath10k_wmi_peer_assoc(struct ath10k *ar, + const struct wmi_peer_assoc_complete_arg *arg); +int ath10k_wmi_set_psmode(struct ath10k *ar, u32 vdev_id, + enum wmi_sta_ps_mode psmode); +int ath10k_wmi_set_sta_ps_param(struct ath10k *ar, u32 vdev_id, + enum wmi_sta_powersave_param param_id, + u32 value); +int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac, + enum wmi_ap_ps_peer_param param_id, u32 value); +int ath10k_wmi_scan_chan_list(struct ath10k *ar, + const struct wmi_scan_chan_list_arg *arg); +int ath10k_wmi_beacon_send(struct ath10k *ar, const struct wmi_bcn_tx_arg *arg); +int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar, + const struct wmi_pdev_set_wmm_params_arg *arg); +int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id); + +#endif /* _WMI_H_ */ -- cgit v1.2.3 From dcad9f031240d59e9e1475a8e5b2cb427da94f6e Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 12 Jun 2013 11:34:30 -0600 Subject: ASoC: rt5640: add device tree support Modify the RT5640 driver to parse platform data from device tree. Write a DT binding document to describe those properties. Slight re-ordering of rt5640_i2c_probe() to better fit the DT parsing. Since ldo1_en is optional, guard usage of it with gpio_is_valid(), rather than open-coding an if (gpio) check. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/rt5640.txt | 30 ++++++++++++++ sound/soc/codecs/rt5640.c | 48 +++++++++++++++++++--- 2 files changed, 72 insertions(+), 6 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/rt5640.txt diff --git a/Documentation/devicetree/bindings/sound/rt5640.txt b/Documentation/devicetree/bindings/sound/rt5640.txt new file mode 100644 index 000000000000..005bcb24d72d --- /dev/null +++ b/Documentation/devicetree/bindings/sound/rt5640.txt @@ -0,0 +1,30 @@ +RT5640 audio CODEC + +This device supports I2C only. + +Required properties: + +- compatible : "realtek,rt5640". + +- reg : The I2C address of the device. + +- interrupts : The CODEC's interrupt output. + +Optional properties: + +- realtek,in1-differential +- realtek,in2-differential + Boolean. Indicate MIC1/2 input are differential, rather than single-ended. + +- realtek,ldo1-en-gpios : The GPIO that controls the CODEC's LDO1_EN pin. + +Example: + +rt5640 { + compatible = "realtek,rt5640"; + reg = <0x1c>; + interrupt-parent = <&gpio>; + interrupts = ; + realtek,ldo1-en-gpios = + <&gpio TEGRA_GPIO(V, 3) GPIO_ACTIVE_HIGH>; +}; diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index 288c17cd6023..8761552882a5 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -1998,6 +1999,28 @@ static const struct i2c_device_id rt5640_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, rt5640_i2c_id); +static int rt5640_parse_dt(struct rt5640_priv *rt5640, struct device_node *np) +{ + rt5640->pdata.in1_diff = of_property_read_bool(np, + "realtek,in1-differential"); + rt5640->pdata.in2_diff = of_property_read_bool(np, + "realtek,in2-differential"); + + rt5640->pdata.ldo1_en = of_get_named_gpio(np, + "realtek,ldo1-en-gpios", 0); + /* + * LDO1_EN is optional (it may be statically tied on the board). + * -ENOENT means that the property doesn't exist, i.e. there is no + * GPIO, so is not an error. Any other error code means the property + * exists, but could not be parsed. + */ + if (!gpio_is_valid(rt5640->pdata.ldo1_en) && + (rt5640->pdata.ldo1_en != -ENOENT)) + return rt5640->pdata.ldo1_en; + + return 0; +} + static int rt5640_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -2011,6 +2034,24 @@ static int rt5640_i2c_probe(struct i2c_client *i2c, GFP_KERNEL); if (NULL == rt5640) return -ENOMEM; + i2c_set_clientdata(i2c, rt5640); + + if (pdata) { + rt5640->pdata = *pdata; + /* + * Translate zero'd out (default) pdata value to an invalid + * GPIO ID. This makes the pdata and DT paths consistent in + * terms of the value left in this field when no GPIO is + * specified, but means we can't actually use GPIO 0. + */ + if (!rt5640->pdata.ldo1_en) + rt5640->pdata.ldo1_en = -EINVAL; + } else if (i2c->dev.of_node) { + ret = rt5640_parse_dt(rt5640, i2c->dev.of_node); + if (ret) + return ret; + } else + rt5640->pdata.ldo1_en = -EINVAL; rt5640->regmap = devm_regmap_init_i2c(i2c, &rt5640_regmap); if (IS_ERR(rt5640->regmap)) { @@ -2020,12 +2061,7 @@ static int rt5640_i2c_probe(struct i2c_client *i2c, return ret; } - if (pdata) - rt5640->pdata = *pdata; - - i2c_set_clientdata(i2c, rt5640); - - if (rt5640->pdata.ldo1_en) { + if (gpio_is_valid(rt5640->pdata.ldo1_en)) { ret = devm_gpio_request_one(&i2c->dev, rt5640->pdata.ldo1_en, GPIOF_OUT_INIT_HIGH, "RT5640 LDO1_EN"); -- cgit v1.2.3 From 040a62cf1c040362fb11587fb9f02e1881f4c237 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 12 Jun 2013 11:35:34 -0600 Subject: ASoC: tegra: add tegra+RT5640 machine driver Initially, this binding and driver only describe/support playback to headphones and speakers. This driver will support Beaver and Dalmore. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- .../bindings/sound/nvidia,tegra-audio-rt5640.txt | 71 ++++++ sound/soc/tegra/Kconfig | 10 + sound/soc/tegra/Makefile | 2 + sound/soc/tegra/tegra_rt5640.c | 257 +++++++++++++++++++++ 4 files changed, 340 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5640.txt create mode 100644 sound/soc/tegra/tegra_rt5640.c diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5640.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5640.txt new file mode 100644 index 000000000000..d130818700b2 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5640.txt @@ -0,0 +1,71 @@ +NVIDIA Tegra audio complex, with RT5640 CODEC + +Required properties: +- compatible : "nvidia,tegra-audio-rt5640" +- clocks : Must contain an entry for each entry in clock-names. +- clock-names : Must include the following entries: + "pll_a" (The Tegra clock of that name), + "pll_a_out0" (The Tegra clock of that name), + "mclk" (The Tegra cdev1/extern1 clock, which feeds the CODEC's mclk) +- nvidia,model : The user-visible name of this sound complex. +- nvidia,audio-routing : A list of the connections between audio components. + Each entry is a pair of strings, the first being the connection's sink, + the second being the connection's source. Valid names for sources and + sinks are the RT5640's pins, and the jacks on the board: + + RT5640 pins: + + * DMIC1 + * DMIC2 + * MICBIAS1 + * IN1P + * IN1R + * IN2P + * IN2R + * HPOL + * HPOR + * LOUTL + * LOUTR + * MONOP + * MONON + * SPOLP + * SPOLN + * SPORP + * SPORN + + Board connectors: + + * Headphones + * Speakers + +- nvidia,i2s-controller : The phandle of the Tegra I2S controller that's + connected to the CODEC. +- nvidia,audio-codec : The phandle of the RT5640 audio codec. This binding + assumes that AIF1 on the CODEC is connected to Tegra. + +Optional properties: +- nvidia,hp-det-gpios : The GPIO that detects headphones are plugged in + +Example: + +sound { + compatible = "nvidia,tegra-audio-rt5640-dalmore", + "nvidia,tegra-audio-rt5640"; + nvidia,model = "NVIDIA Tegra Dalmore"; + + nvidia,audio-routing = + "Headphones", "HPOR", + "Headphones", "HPOL", + "Speakers", "SPORP", + "Speakers", "SPORN", + "Speakers", "SPOLP", + "Speakers", "SPOLN"; + + nvidia,i2s-controller = <&tegra_i2s1>; + nvidia,audio-codec = <&rt5640>; + + nvidia,hp-det-gpios = <&gpio 143 0>; /* GPIO PR7 */ + + clocks = <&tegra_car 216>, <&tegra_car 217>, <&tegra_car 120>; + clock-names = "pll_a", "pll_a_out0", "mclk"; +}; diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig index b1c9d573da05..995b120c2cd0 100644 --- a/sound/soc/tegra/Kconfig +++ b/sound/soc/tegra/Kconfig @@ -59,6 +59,16 @@ config SND_SOC_TEGRA30_I2S Tegra30 I2S interface. You will also need to select the individual machine drivers to support below. +config SND_SOC_TEGRA_RT5640 + tristate "SoC Audio support for Tegra boards using an RT5640 codec" + depends on SND_SOC_TEGRA && I2C + select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC + select SND_SOC_TEGRA30_I2S if ARCH_TEGRA_3x_SOC + select SND_SOC_RT5640 + help + Say Y or M here if you want to add support for SoC audio on Tegra + boards using the RT5640 codec, such as Dalmore. + config SND_SOC_TEGRA_WM8753 tristate "SoC Audio support for Tegra boards using a WM8753 codec" depends on SND_SOC_TEGRA && I2C diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile index 416a14bde41b..21d2550a08a4 100644 --- a/sound/soc/tegra/Makefile +++ b/sound/soc/tegra/Makefile @@ -18,12 +18,14 @@ obj-$(CONFIG_SND_SOC_TEGRA30_AHUB) += snd-soc-tegra30-ahub.o obj-$(CONFIG_SND_SOC_TEGRA30_I2S) += snd-soc-tegra30-i2s.o # Tegra machine Support +snd-soc-tegra-rt5640-objs := tegra_rt5640.o snd-soc-tegra-wm8753-objs := tegra_wm8753.o snd-soc-tegra-wm8903-objs := tegra_wm8903.o snd-soc-tegra-wm9712-objs := tegra_wm9712.o snd-soc-tegra-trimslice-objs := trimslice.o snd-soc-tegra-alc5632-objs := tegra_alc5632.o +obj-$(CONFIG_SND_SOC_TEGRA_RT5640) += snd-soc-tegra-rt5640.o obj-$(CONFIG_SND_SOC_TEGRA_WM8753) += snd-soc-tegra-wm8753.o obj-$(CONFIG_SND_SOC_TEGRA_WM8903) += snd-soc-tegra-wm8903.o obj-$(CONFIG_SND_SOC_TEGRA_WM9712) += snd-soc-tegra-wm9712.o diff --git a/sound/soc/tegra/tegra_rt5640.c b/sound/soc/tegra/tegra_rt5640.c new file mode 100644 index 000000000000..08794f915a94 --- /dev/null +++ b/sound/soc/tegra/tegra_rt5640.c @@ -0,0 +1,257 @@ +/* +* tegra_rt5640.c - Tegra machine ASoC driver for boards using WM8903 codec. + * + * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Based on code copyright/by: + * + * Copyright (C) 2010-2012 - NVIDIA, Inc. + * Copyright (C) 2011 The AC100 Kernel Team + * (c) 2009, 2010 Nvidia Graphics Pvt. Ltd. + * Copyright 2007 Wolfson Microelectronics PLC. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "../codecs/rt5640.h" + +#include "tegra_asoc_utils.h" + +#define DRV_NAME "tegra-snd-rt5640" + +struct tegra_rt5640 { + struct tegra_asoc_utils_data util_data; + int gpio_hp_det; +}; + +static int tegra_rt5640_asoc_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_codec *codec = codec_dai->codec; + struct snd_soc_card *card = codec->card; + struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card); + int srate, mclk; + int err; + + srate = params_rate(params); + mclk = 256 * srate; + + err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk); + if (err < 0) { + dev_err(card->dev, "Can't configure clocks\n"); + return err; + } + + err = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_MCLK, mclk, + SND_SOC_CLOCK_IN); + if (err < 0) { + dev_err(card->dev, "codec_dai clock not set\n"); + return err; + } + + return 0; +} + +static struct snd_soc_ops tegra_rt5640_ops = { + .hw_params = tegra_rt5640_asoc_hw_params, +}; + +static struct snd_soc_jack tegra_rt5640_hp_jack; + +static struct snd_soc_jack_pin tegra_rt5640_hp_jack_pins[] = { + { + .pin = "Headphones", + .mask = SND_JACK_HEADPHONE, + }, +}; + +static struct snd_soc_jack_gpio tegra_rt5640_hp_jack_gpio = { + .name = "Headphone detection", + .report = SND_JACK_HEADPHONE, + .debounce_time = 150, + .invert = 1, +}; + +static const struct snd_soc_dapm_widget tegra_rt5640_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphones", NULL), + SND_SOC_DAPM_SPK("Speakers", NULL), +}; + +static const struct snd_kcontrol_new tegra_rt5640_controls[] = { + SOC_DAPM_PIN_SWITCH("Speakers"), +}; + +static int tegra_rt5640_asoc_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_codec *codec = codec_dai->codec; + struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(codec->card); + + snd_soc_jack_new(codec, "Headphones", SND_JACK_HEADPHONE, + &tegra_rt5640_hp_jack); + snd_soc_jack_add_pins(&tegra_rt5640_hp_jack, + ARRAY_SIZE(tegra_rt5640_hp_jack_pins), + tegra_rt5640_hp_jack_pins); + + if (gpio_is_valid(machine->gpio_hp_det)) { + tegra_rt5640_hp_jack_gpio.gpio = machine->gpio_hp_det; + snd_soc_jack_add_gpios(&tegra_rt5640_hp_jack, + 1, + &tegra_rt5640_hp_jack_gpio); + } + + return 0; +} + +static struct snd_soc_dai_link tegra_rt5640_dai = { + .name = "RT5640", + .stream_name = "RT5640 PCM", + .codec_dai_name = "rt5640-aif1", + .init = tegra_rt5640_asoc_init, + .ops = &tegra_rt5640_ops, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, +}; + +static struct snd_soc_card snd_soc_tegra_rt5640 = { + .name = "tegra-rt5640", + .owner = THIS_MODULE, + .dai_link = &tegra_rt5640_dai, + .num_links = 1, + .controls = tegra_rt5640_controls, + .num_controls = ARRAY_SIZE(tegra_rt5640_controls), + .dapm_widgets = tegra_rt5640_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tegra_rt5640_dapm_widgets), + .fully_routed = true, +}; + +static int tegra_rt5640_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct snd_soc_card *card = &snd_soc_tegra_rt5640; + struct tegra_rt5640 *machine; + int ret; + + machine = devm_kzalloc(&pdev->dev, + sizeof(struct tegra_rt5640), GFP_KERNEL); + if (!machine) { + dev_err(&pdev->dev, "Can't allocate tegra_rt5640\n"); + return -ENOMEM; + } + + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + snd_soc_card_set_drvdata(card, machine); + + machine->gpio_hp_det = of_get_named_gpio(np, "nvidia,hp-det-gpios", 0); + if (machine->gpio_hp_det == -EPROBE_DEFER) + return -EPROBE_DEFER; + + ret = snd_soc_of_parse_card_name(card, "nvidia,model"); + if (ret) + goto err; + + ret = snd_soc_of_parse_audio_routing(card, "nvidia,audio-routing"); + if (ret) + goto err; + + tegra_rt5640_dai.codec_of_node = of_parse_phandle(np, + "nvidia,audio-codec", 0); + if (!tegra_rt5640_dai.codec_of_node) { + dev_err(&pdev->dev, + "Property 'nvidia,audio-codec' missing or invalid\n"); + ret = -EINVAL; + goto err; + } + + tegra_rt5640_dai.cpu_of_node = of_parse_phandle(np, + "nvidia,i2s-controller", 0); + if (!tegra_rt5640_dai.cpu_of_node) { + dev_err(&pdev->dev, + "Property 'nvidia,i2s-controller' missing or invalid\n"); + ret = -EINVAL; + goto err; + } + + tegra_rt5640_dai.platform_of_node = tegra_rt5640_dai.cpu_of_node; + + ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev); + if (ret) + goto err; + + ret = snd_soc_register_card(card); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", + ret); + goto err_fini_utils; + } + + return 0; + +err_fini_utils: + tegra_asoc_utils_fini(&machine->util_data); +err: + return ret; +} + +static int tegra_rt5640_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card); + + snd_soc_jack_free_gpios(&tegra_rt5640_hp_jack, 1, + &tegra_rt5640_hp_jack_gpio); + + snd_soc_unregister_card(card); + + tegra_asoc_utils_fini(&machine->util_data); + + return 0; +} + +static const struct of_device_id tegra_rt5640_of_match[] = { + { .compatible = "nvidia,tegra-audio-rt5640", }, + {}, +}; + +static struct platform_driver tegra_rt5640_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + .of_match_table = tegra_rt5640_of_match, + }, + .probe = tegra_rt5640_probe, + .remove = tegra_rt5640_remove, +}; +module_platform_driver(tegra_rt5640_driver); + +MODULE_AUTHOR("Stephen Warren "); +MODULE_DESCRIPTION("Tegra+RT5640 machine ASoC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, tegra_rt5640_of_match); -- cgit v1.2.3 From 76b1f723c4f54c2ab05307da3cc5c39e421b029b Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 6 May 2013 15:05:50 -0300 Subject: i2c: imx: Let device core handle pinctrl Since commit ab78029 (drivers/pinctrl: grab default handles from device core), we can rely on device core for handling pinctrl. So remove devm_pinctrl_get_select_default() from the driver. Signed-off-by: Fabio Estevam Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-imx.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 82f20c60bb7b..8c7526ca912e 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -51,7 +51,6 @@ #include #include #include -#include #include /** Defines ******************************************************************** @@ -493,7 +492,6 @@ static int __init i2c_imx_probe(struct platform_device *pdev) struct imx_i2c_struct *i2c_imx; struct resource *res; struct imxi2c_platform_data *pdata = pdev->dev.platform_data; - struct pinctrl *pinctrl; void __iomem *base; int irq, ret; u32 bitrate; @@ -535,12 +533,6 @@ static int __init i2c_imx_probe(struct platform_device *pdev) i2c_imx->adapter.dev.of_node = pdev->dev.of_node; i2c_imx->base = base; - pinctrl = devm_pinctrl_get_select_default(&pdev->dev); - if (IS_ERR(pinctrl)) { - dev_err(&pdev->dev, "can't get/select pinctrl\n"); - return PTR_ERR(pinctrl); - } - /* Get I2C clock */ i2c_imx->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(i2c_imx->clk)) { -- cgit v1.2.3 From dfda7d8f09323163cad26dd35fe6293b4f7cee85 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 6 May 2013 15:05:51 -0300 Subject: i2c: mxs: Let device core handle pinctrl Since commit ab78029 (drivers/pinctrl: grab default handles from device core), we can rely on device core for handling pinctrl. So remove devm_pinctrl_get_select_default() from the driver. Signed-off-by: Fabio Estevam Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mxs.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/i2c/busses/i2c-mxs.c b/drivers/i2c/busses/i2c-mxs.c index 2039f230482d..df8ff5aea5b5 100644 --- a/drivers/i2c/busses/i2c-mxs.c +++ b/drivers/i2c/busses/i2c-mxs.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -638,15 +637,10 @@ static int mxs_i2c_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct mxs_i2c_dev *i2c; struct i2c_adapter *adap; - struct pinctrl *pinctrl; struct resource *res; resource_size_t res_size; int err, irq; - pinctrl = devm_pinctrl_get_select_default(dev); - if (IS_ERR(pinctrl)) - return PTR_ERR(pinctrl); - i2c = devm_kzalloc(dev, sizeof(struct mxs_i2c_dev), GFP_KERNEL); if (!i2c) return -ENOMEM; -- cgit v1.2.3 From e42dba569fceca5d59a88571370785e9ce9775b8 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 22 May 2013 13:03:11 +0300 Subject: i2c: designware: prevent signals from aborting I2C transfers If a process receives signal while it is waiting for I2C transfer to complete, an error is returned to the caller and the transfer is aborted. This can cause the driver to fail subsequent transfers. Also according to commit d295a86eab2 (i2c: mv64xxx: work around signals causing I2C transactions to be aborted) I2C drivers aren't supposed to abort transactions on signals. To prevent this switch to use wait_for_completion_timeout() instead of wait_for_completion_interruptible_timeout() in the designware I2C driver. Signed-off-by: Mika Westerberg Reviewed-by: Christian Ruppert Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-designware-core.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/i2c/busses/i2c-designware-core.c b/drivers/i2c/busses/i2c-designware-core.c index c41ca6354fc5..db20a2841b75 100644 --- a/drivers/i2c/busses/i2c-designware-core.c +++ b/drivers/i2c/busses/i2c-designware-core.c @@ -580,14 +580,13 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) i2c_dw_xfer_init(dev); /* wait for tx to complete */ - ret = wait_for_completion_interruptible_timeout(&dev->cmd_complete, HZ); + ret = wait_for_completion_timeout(&dev->cmd_complete, HZ); if (ret == 0) { dev_err(dev->dev, "controller timed out\n"); i2c_dw_init(dev); ret = -ETIMEDOUT; goto done; - } else if (ret < 0) - goto done; + } if (dev->msg_err) { ret = dev->msg_err; -- cgit v1.2.3 From 3cc2d009bc210516c61536273b304c4f6ccd797c Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 10 May 2013 10:16:54 +0200 Subject: drivers/i2c/busses: don't check resource with devm_ioremap_resource devm_ioremap_resource does sanity checks on the given resource. No need to duplicate this in the driver. Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-davinci.c | 8 +------- drivers/i2c/busses/i2c-designware-platdrv.c | 8 +------- drivers/i2c/busses/i2c-imx.c | 6 +----- drivers/i2c/busses/i2c-omap.c | 8 +------- drivers/i2c/busses/i2c-rcar.c | 7 +------ 5 files changed, 5 insertions(+), 32 deletions(-) diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c index cf20e06a88e1..fa556057d224 100644 --- a/drivers/i2c/busses/i2c-davinci.c +++ b/drivers/i2c/busses/i2c-davinci.c @@ -646,13 +646,6 @@ static int davinci_i2c_probe(struct platform_device *pdev) struct resource *mem, *irq; int r; - /* NOTE: driver uses the static register mapping */ - 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"); @@ -697,6 +690,7 @@ static int davinci_i2c_probe(struct platform_device *pdev) return -ENODEV; clk_prepare_enable(dev->clk); + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); dev->base = devm_ioremap_resource(&pdev->dev, mem); if (IS_ERR(dev->base)) { r = PTR_ERR(dev->base); diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index 35b70a1edf57..ee46c92d7e3c 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -87,13 +87,6 @@ static int dw_i2c_probe(struct platform_device *pdev) struct resource *mem; int irq, r; - /* NOTE: driver uses the static register mapping */ - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) { - dev_err(&pdev->dev, "no mem resource?\n"); - return -EINVAL; - } - irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(&pdev->dev, "no irq resource?\n"); @@ -104,6 +97,7 @@ static int dw_i2c_probe(struct platform_device *pdev) if (!dev) return -ENOMEM; + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); dev->base = devm_ioremap_resource(&pdev->dev, mem); if (IS_ERR(dev->base)) return PTR_ERR(dev->base); diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 8c7526ca912e..6406aa960f2a 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -498,17 +498,13 @@ static int __init i2c_imx_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "<%s>\n", __func__); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "can't get device resources\n"); - return -ENOENT; - } irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(&pdev->dev, "can't get irq number\n"); return -ENOENT; } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(base)) return PTR_ERR(base); diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index e02f9e36a7b2..352f3c38d1f7 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -1084,13 +1084,6 @@ omap_i2c_probe(struct platform_device *pdev) u32 rev; u16 minor, major, scheme; - /* NOTE: driver uses the static register mapping */ - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) { - dev_err(&pdev->dev, "no mem resource?\n"); - return -ENODEV; - } - irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(&pdev->dev, "no irq resource?\n"); @@ -1103,6 +1096,7 @@ omap_i2c_probe(struct platform_device *pdev) return -ENOMEM; } + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); dev->base = devm_ioremap_resource(&pdev->dev, mem); if (IS_ERR(dev->base)) return PTR_ERR(dev->base); diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index 4ba4a95b6b26..0fc585861610 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c @@ -623,12 +623,6 @@ static int rcar_i2c_probe(struct platform_device *pdev) u32 bus_speed; int ret; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, "no mmio resources\n"); - return -ENODEV; - } - priv = devm_kzalloc(dev, sizeof(struct rcar_i2c_priv), GFP_KERNEL); if (!priv) { dev_err(dev, "no mem for private data\n"); @@ -642,6 +636,7 @@ static int rcar_i2c_probe(struct platform_device *pdev) if (ret < 0) return ret; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); priv->io = devm_ioremap_resource(dev, res); if (IS_ERR(priv->io)) return PTR_ERR(priv->io); -- cgit v1.2.3 From c2c64954723b9d365f35f36c0cd089e740bb0a0a Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 23 May 2013 19:22:40 +0900 Subject: i2c: use platform_{get,set}_drvdata() Use the wrapper functions for getting and setting the driver data using platform_device instead of using dev_{get,set}_drvdata() with &pdev->dev, so we can directly pass a struct platform_device. Signed-off-by: Jingoo Han Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-cpm.c | 4 ++-- drivers/i2c/busses/i2c-ibm_iic.c | 4 ++-- drivers/i2c/busses/i2c-mpc.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/i2c/busses/i2c-cpm.c b/drivers/i2c/busses/i2c-cpm.c index 3823623baa48..eccf5c2d4a12 100644 --- a/drivers/i2c/busses/i2c-cpm.c +++ b/drivers/i2c/busses/i2c-cpm.c @@ -646,7 +646,7 @@ static int cpm_i2c_probe(struct platform_device *ofdev) cpm->ofdev = ofdev; - dev_set_drvdata(&ofdev->dev, cpm); + platform_set_drvdata(ofdev, cpm); cpm->adap = cpm_ops; i2c_set_adapdata(&cpm->adap, cpm); @@ -689,7 +689,7 @@ out_free: static int cpm_i2c_remove(struct platform_device *ofdev) { - struct cpm_i2c *cpm = dev_get_drvdata(&ofdev->dev); + struct cpm_i2c *cpm = platform_get_drvdata(ofdev); i2c_del_adapter(&cpm->adap); diff --git a/drivers/i2c/busses/i2c-ibm_iic.c b/drivers/i2c/busses/i2c-ibm_iic.c index 405a2e240454..973f51688276 100644 --- a/drivers/i2c/busses/i2c-ibm_iic.c +++ b/drivers/i2c/busses/i2c-ibm_iic.c @@ -705,7 +705,7 @@ static int iic_probe(struct platform_device *ofdev) return -ENOMEM; } - dev_set_drvdata(&ofdev->dev, dev); + platform_set_drvdata(ofdev, dev); dev->vaddr = of_iomap(np, 0); if (dev->vaddr == NULL) { @@ -782,7 +782,7 @@ error_cleanup: */ static int iic_remove(struct platform_device *ofdev) { - struct ibm_iic_private *dev = dev_get_drvdata(&ofdev->dev); + struct ibm_iic_private *dev = platform_get_drvdata(ofdev); i2c_del_adapter(&dev->adap); diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c index 5e705ee02f4a..7607dc061918 100644 --- a/drivers/i2c/busses/i2c-mpc.c +++ b/drivers/i2c/busses/i2c-mpc.c @@ -679,7 +679,7 @@ static int fsl_i2c_probe(struct platform_device *op) } dev_info(i2c->dev, "timeout %u us\n", mpc_ops.timeout * 1000000 / HZ); - dev_set_drvdata(&op->dev, i2c); + platform_set_drvdata(op, i2c); i2c->adap = mpc_ops; i2c_set_adapdata(&i2c->adap, i2c); @@ -707,7 +707,7 @@ static int fsl_i2c_probe(struct platform_device *op) static int fsl_i2c_remove(struct platform_device *op) { - struct mpc_i2c *i2c = dev_get_drvdata(&op->dev); + struct mpc_i2c *i2c = platform_get_drvdata(op); i2c_del_adapter(&i2c->adap); -- cgit v1.2.3 From c80f52847c50109ca248c22efbf71ff10553dca4 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 13 May 2013 22:18:21 +0200 Subject: i2c: core: make it possible to match a pure device tree driver This tries to address an issue found when writing an MFD driver for the Nomadik STw481x PMICs: as the platform is using device tree exclusively I want to specify the driver matching like this: static const struct of_device_id stw481x_match[] = { { .compatible = "st,stw4810", }, { .compatible = "st,stw4811", }, {}, }; static struct i2c_driver stw481x_driver = { .driver = { .name = "stw481x", .of_match_table = stw481x_match, }, .probe = stw481x_probe, .remove = stw481x_remove, }; However that turns out not to be possible: the I2C probe code is written so that the probe() call is always passed a match from i2c_match_id() using non-devicetree matches. This is probably why most devices using device tree for I2C clients currently will pass no .of_match_table *at all* but instead just use .id_table from struct i2c_driver to match the device. As you realize that means that the whole idea with compatible strings is discarded, and that is why we find strange device tree I2C device compatible strings like "product" instead of "vendor,product" as you could expect. Let's figure out how to fix this before the mess spreads. This patch will allow probeing devices with only an of_match_table as per above, and will pass NULL as the second argument to the probe() function. If the driver wants to deduce secondary info from the struct of_device_id .data field, it has to call of_match_device() on its own match table in the probe function device tree probe path. If drivers define both an .of_match_table *AND* a i2c_driver .id_table, the .of_match_table will take precedence, just as is done in the i2c_device_match() function in i2c-core.c. I2C devices probed from device tree should subsequently be fixed to handle the case where of_match_table() is used (I think none of them do that today), and platforms should fix their device trees to use compatible strings for I2C devices instead of setting the name to Linux device driver names as is done in multiple cases today. Cc: Rob Herring Cc: Grant Likely Signed-off-by: Linus Walleij Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 48e31ed69dbf..93fc5bfdb9b4 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -240,7 +240,7 @@ static int i2c_device_probe(struct device *dev) return 0; driver = to_i2c_driver(dev->driver); - if (!driver->probe || !driver->id_table) + if (!driver->probe || (!driver->id_table && !dev->driver->of_match_table)) return -ENODEV; client->driver = driver; if (!device_can_wakeup(&client->dev)) @@ -248,7 +248,12 @@ static int i2c_device_probe(struct device *dev) client->flags & I2C_CLIENT_WAKE); dev_dbg(dev, "probe\n"); - status = driver->probe(client, i2c_match_id(driver->id_table, client)); + if (of_match_device(dev->driver->of_match_table, dev)) + /* Device tree matching */ + status = driver->probe(client, NULL); + else + /* Fall back to matching the id_table */ + status = driver->probe(client, i2c_match_id(driver->id_table, client)); if (status) { client->driver = NULL; i2c_set_clientdata(client, NULL); -- cgit v1.2.3 From 8419c8debdc600b71fb89f0ffad80a6f436d80fe Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Tue, 28 May 2013 18:41:09 +0800 Subject: i2c: bfin-twi: Read and write the FIFO in loop TWI transfer interrupts may be lost when system is heavily handling other interrupts, while current transfer handler depends on each accurate interrupt and misses some data in this case. Because there are 2 2-byte FIFOs in blackfin TWI controller, the occurrence of the data loss can be reduced by reading till the RX FIFO is empty and writing till the TX FIFO is full. Reported-by: Bob Maris Signed-off-by: Sonic Zhang Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-bfin-twi.c | 47 ++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/drivers/i2c/busses/i2c-bfin-twi.c b/drivers/i2c/busses/i2c-bfin-twi.c index 05080c449c6b..13ea1c29873d 100644 --- a/drivers/i2c/busses/i2c-bfin-twi.c +++ b/drivers/i2c/busses/i2c-bfin-twi.c @@ -39,33 +39,40 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface, unsigned short mast_stat = read_MASTER_STAT(iface); if (twi_int_status & XMTSERV) { + if (iface->writeNum <= 0) { + /* start receive immediately after complete sending in + * combine mode. + */ + if (iface->cur_mode == TWI_I2C_MODE_COMBINED) + write_MASTER_CTL(iface, + read_MASTER_CTL(iface) | MDIR); + else if (iface->manual_stop) + write_MASTER_CTL(iface, + read_MASTER_CTL(iface) | STOP); + else if (iface->cur_mode == TWI_I2C_MODE_REPEAT && + iface->cur_msg + 1 < iface->msg_num) { + if (iface->pmsg[iface->cur_msg + 1].flags & + I2C_M_RD) + write_MASTER_CTL(iface, + read_MASTER_CTL(iface) | + MDIR); + else + write_MASTER_CTL(iface, + read_MASTER_CTL(iface) & + ~MDIR); + } + } /* Transmit next data */ - if (iface->writeNum > 0) { + while (iface->writeNum > 0 && + (read_FIFO_STAT(iface) & XMTSTAT) != XMT_FULL) { SSYNC(); write_XMT_DATA8(iface, *(iface->transPtr++)); iface->writeNum--; } - /* start receive immediately after complete sending in - * combine mode. - */ - else if (iface->cur_mode == TWI_I2C_MODE_COMBINED) - write_MASTER_CTL(iface, - read_MASTER_CTL(iface) | MDIR); - else if (iface->manual_stop) - write_MASTER_CTL(iface, - read_MASTER_CTL(iface) | STOP); - else if (iface->cur_mode == TWI_I2C_MODE_REPEAT && - iface->cur_msg + 1 < iface->msg_num) { - if (iface->pmsg[iface->cur_msg + 1].flags & I2C_M_RD) - write_MASTER_CTL(iface, - read_MASTER_CTL(iface) | MDIR); - else - write_MASTER_CTL(iface, - read_MASTER_CTL(iface) & ~MDIR); - } } if (twi_int_status & RCVSERV) { - if (iface->readNum > 0) { + while (iface->readNum > 0 && + (read_FIFO_STAT(iface) & RCVSTAT)) { /* Receive next data */ *(iface->transPtr) = read_RCV_DATA8(iface); if (iface->cur_mode == TWI_I2C_MODE_COMBINED) { -- cgit v1.2.3 From 80b4205bc1da3fdcb665f43d1a7fbbb202e9cd41 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Tue, 4 Jun 2013 15:41:30 +0530 Subject: ath9k: Fix OFDM weak signal detection for AP mode The commit "ath9k_hw: improve ANI processing and rx desensitizing parameters" changed the OFDM weak signal detection logic to disable it for AP mode, which is not allowed. Fix this and enable it always for AP mode. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ani.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index e91725bf401c..3ec4c5388c28 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -177,10 +177,15 @@ static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel, BEACON_RSSI(ah) <= ATH9K_ANI_RSSI_THR_HIGH) weak_sig = true; - if (aniState->ofdmWeakSigDetect != weak_sig) - ath9k_hw_ani_control(ah, - ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, - entry_ofdm->ofdm_weak_signal_on); + /* + * OFDM Weak signal detection is always enabled for AP mode. + */ + if (ah->opmode != NL80211_IFTYPE_AP && + aniState->ofdmWeakSigDetect != weak_sig) { + ath9k_hw_ani_control(ah, + ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, + entry_ofdm->ofdm_weak_signal_on); + } if (aniState->ofdmNoiseImmunityLevel >= ATH9K_ANI_OFDM_DEF_LEVEL) { ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH; -- cgit v1.2.3 From a04eb985f18b7e0f3b071a29600ebc4741475ce9 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Tue, 4 Jun 2013 15:41:31 +0530 Subject: ath9k: Fix ANI monitoring The commit "ath9k_hw: improve ANI processing and rx desensitizing parameters" changed various ANI operational parameters to address a specific card/environment. This is not really applicable for other cards in general usage. As per internal documentation, lowering the immunity level can be done only after 5 periods have passed and the CCK/OFDM errors are below the low watermak threshold - which have been fixed at 300 and 400 respectively by the sytems team. Raising the immunity level can be done when CCK/OFDM errors exceed 600 and 1000 (per second). Set these values once during attach. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ani.c | 29 +++++++++++++++++------------ drivers/net/wireless/ath/ath9k/ani.h | 2 -- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index 3ec4c5388c28..1520e5508210 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -186,14 +186,6 @@ static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, entry_ofdm->ofdm_weak_signal_on); } - - if (aniState->ofdmNoiseImmunityLevel >= ATH9K_ANI_OFDM_DEF_LEVEL) { - ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH; - ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW_ABOVE_INI; - } else { - ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH_BELOW_INI; - ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW; - } } static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hw *ah) @@ -439,12 +431,25 @@ void ath9k_hw_ani_monitor(struct ath_hw *ah, struct ath9k_channel *chan) ofdmPhyErrRate, aniState->cckNoiseImmunityLevel, cckPhyErrRate, aniState->ofdmsTurn); - if (aniState->listenTime > ah->aniperiod) { - if (cckPhyErrRate < ah->config.cck_trig_low && - ofdmPhyErrRate < ah->config.ofdm_trig_low) { + if (aniState->listenTime > 5 * ah->aniperiod) { + /* + * Check if we need to lower immunity if + * 5 ani_periods have passed. + */ + if (ofdmPhyErrRate <= ah->config.ofdm_trig_low && + cckPhyErrRate <= ah->config.cck_trig_low) { ath9k_hw_ani_lower_immunity(ah); aniState->ofdmsTurn = !aniState->ofdmsTurn; - } else if (ofdmPhyErrRate > ah->config.ofdm_trig_high) { + } + ath9k_ani_restart(ah); + } else if (aniState->listenTime > ah->aniperiod) { + /* + * Check if immunity has to be raised, + * (either OFDM or CCK). + */ + if (ofdmPhyErrRate > ah->config.ofdm_trig_high && + (cckPhyErrRate <= ah->config.cck_trig_high || + aniState->ofdmsTurn)) { ath9k_hw_ani_ofdm_err_trigger(ah); aniState->ofdmsTurn = false; } else if (cckPhyErrRate > ah->config.cck_trig_high) { diff --git a/drivers/net/wireless/ath/ath9k/ani.h b/drivers/net/wireless/ath/ath9k/ani.h index 78b9fa9f6455..10884728d30e 100644 --- a/drivers/net/wireless/ath/ath9k/ani.h +++ b/drivers/net/wireless/ath/ath9k/ani.h @@ -21,11 +21,9 @@ /* units are errors per second */ #define ATH9K_ANI_OFDM_TRIG_HIGH 3500 -#define ATH9K_ANI_OFDM_TRIG_HIGH_BELOW_INI 1000 /* units are errors per second */ #define ATH9K_ANI_OFDM_TRIG_LOW 400 -#define ATH9K_ANI_OFDM_TRIG_LOW_ABOVE_INI 900 /* units are errors per second */ #define ATH9K_ANI_CCK_TRIG_HIGH 600 -- cgit v1.2.3 From b99553fb60c1a1d3569993fdb5e21fb7214dbbf0 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Tue, 4 Jun 2013 15:41:32 +0530 Subject: ath9k: Fix ANI levels The commit, "ath9k_hw: improve ANI processing and rx desensitizing parameters" modified the immunity level tables for both CCK and OFDM. Fix them so that the tables are in sync with the internal driver/codebase. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ani.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index 1520e5508210..c8595f41b298 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -46,8 +46,8 @@ static const struct ani_ofdm_level_entry ofdm_level_table[] = { { 5, 4, 1 }, /* lvl 5 */ { 6, 5, 1 }, /* lvl 6 */ { 7, 6, 1 }, /* lvl 7 */ - { 7, 6, 0 }, /* lvl 8 */ - { 7, 7, 0 } /* lvl 9 */ + { 7, 7, 1 }, /* lvl 8 */ + { 7, 8, 0 } /* lvl 9 */ }; #define ATH9K_ANI_OFDM_NUM_LEVEL \ ARRAY_SIZE(ofdm_level_table) @@ -91,8 +91,8 @@ static const struct ani_cck_level_entry cck_level_table[] = { { 4, 0 }, /* lvl 4 */ { 5, 0 }, /* lvl 5 */ { 6, 0 }, /* lvl 6 */ - { 6, 0 }, /* lvl 7 (only for high rssi) */ - { 7, 0 } /* lvl 8 (only for high rssi) */ + { 7, 0 }, /* lvl 7 (only for high rssi) */ + { 8, 0 } /* lvl 8 (only for high rssi) */ }; #define ATH9K_ANI_CCK_NUM_LEVEL \ -- cgit v1.2.3 From ff23e0845b1ba33ec2ee25989758e2cf3df6d4c5 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Tue, 4 Jun 2013 15:41:33 +0530 Subject: ath9k: Fix ofdm weak signal configuration The commit, "ath9k_hw: improve ANI processing and rx desensitizing parameters" removed code setting various phy registers holding threshold values. This is likely required for OFDM weak signal detection to function correctly, so add them, but skip AR9462 and AR9565. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_phy.c | 60 +++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index 83e03857c014..bc483128efcd 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -906,6 +906,11 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah, struct ath_common *common = ath9k_hw_common(ah); struct ath9k_channel *chan = ah->curchan; struct ar5416AniState *aniState = &ah->ani; + int m1ThreshLow, m2ThreshLow; + int m1Thresh, m2Thresh; + int m2CountThr, m2CountThrLow; + int m1ThreshLowExt, m2ThreshLowExt; + int m1ThreshExt, m2ThreshExt; s32 value, value2; switch (cmd & ah->ani_function) { @@ -919,6 +924,61 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah, */ u32 on = param ? 1 : 0; + if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) + goto skip_ws_det; + + m1ThreshLow = on ? + aniState->iniDef.m1ThreshLow : m1ThreshLow_off; + m2ThreshLow = on ? + aniState->iniDef.m2ThreshLow : m2ThreshLow_off; + m1Thresh = on ? + aniState->iniDef.m1Thresh : m1Thresh_off; + m2Thresh = on ? + aniState->iniDef.m2Thresh : m2Thresh_off; + m2CountThr = on ? + aniState->iniDef.m2CountThr : m2CountThr_off; + m2CountThrLow = on ? + aniState->iniDef.m2CountThrLow : m2CountThrLow_off; + m1ThreshLowExt = on ? + aniState->iniDef.m1ThreshLowExt : m1ThreshLowExt_off; + m2ThreshLowExt = on ? + aniState->iniDef.m2ThreshLowExt : m2ThreshLowExt_off; + m1ThreshExt = on ? + aniState->iniDef.m1ThreshExt : m1ThreshExt_off; + m2ThreshExt = on ? + aniState->iniDef.m2ThreshExt : m2ThreshExt_off; + + REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, + AR_PHY_SFCORR_LOW_M1_THRESH_LOW, + m1ThreshLow); + REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, + AR_PHY_SFCORR_LOW_M2_THRESH_LOW, + m2ThreshLow); + REG_RMW_FIELD(ah, AR_PHY_SFCORR, + AR_PHY_SFCORR_M1_THRESH, + m1Thresh); + REG_RMW_FIELD(ah, AR_PHY_SFCORR, + AR_PHY_SFCORR_M2_THRESH, + m2Thresh); + REG_RMW_FIELD(ah, AR_PHY_SFCORR, + AR_PHY_SFCORR_M2COUNT_THR, + m2CountThr); + REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, + AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW, + m2CountThrLow); + REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, + AR_PHY_SFCORR_EXT_M1_THRESH_LOW, + m1ThreshLowExt); + REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, + AR_PHY_SFCORR_EXT_M2_THRESH_LOW, + m2ThreshLowExt); + REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, + AR_PHY_SFCORR_EXT_M1_THRESH, + m1ThreshExt); + REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, + AR_PHY_SFCORR_EXT_M2_THRESH, + m2ThreshExt); +skip_ws_det: if (on) REG_SET_BIT(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); -- cgit v1.2.3 From 50a0f5bc3075b37ccaad97780d4b9238309fdf15 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Tue, 4 Jun 2013 15:41:34 +0530 Subject: ath9k: Remove redundant code The phy error mask registers are programmed already in ath9k_ani_restart(), so there is no need to set them in ath9k_ani_reset(). Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ani.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index c8595f41b298..a68beb19ce4d 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -360,18 +360,7 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning) ath9k_hw_set_ofdm_nil(ah, ofdm_nil, is_scanning); ath9k_hw_set_cck_nil(ah, cck_nil, is_scanning); - /* - * enable phy counters if hw supports or if not, enable phy - * interrupts (so we can count each one) - */ ath9k_ani_restart(ah); - - ENABLE_REGWRITE_BUFFER(ah); - - REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING); - REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING); - - REGWRITE_BUFFER_FLUSH(ah); } static bool ath9k_hw_ani_read_counters(struct ath_hw *ah) -- cgit v1.2.3 From 057c1dd6cd5041e3b6a6fb65708ac5a4d9c12b36 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Tue, 4 Jun 2013 15:41:35 +0530 Subject: ath9k: Remove unused ANI macros Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ani.h | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ani.h b/drivers/net/wireless/ath/ath9k/ani.h index 10884728d30e..b75aea23b313 100644 --- a/drivers/net/wireless/ath/ath9k/ani.h +++ b/drivers/net/wireless/ath/ath9k/ani.h @@ -21,17 +21,10 @@ /* units are errors per second */ #define ATH9K_ANI_OFDM_TRIG_HIGH 3500 - -/* units are errors per second */ #define ATH9K_ANI_OFDM_TRIG_LOW 400 - -/* units are errors per second */ #define ATH9K_ANI_CCK_TRIG_HIGH 600 - -/* units are errors per second */ #define ATH9K_ANI_CCK_TRIG_LOW 300 -#define ATH9K_ANI_NOISE_IMMUNE_LVL 4 #define ATH9K_ANI_SPUR_IMMUNE_LVL 3 #define ATH9K_ANI_FIRSTEP_LVL 2 @@ -43,10 +36,6 @@ /* in ms */ #define ATH9K_ANI_POLLINTERVAL 1000 -#define HAL_NOISE_IMMUNE_MAX 4 -#define HAL_SPUR_IMMUNE_MAX 7 -#define HAL_FIRST_STEP_MAX 2 - #define ATH9K_SIG_FIRSTEP_SETTING_MIN 0 #define ATH9K_SIG_FIRSTEP_SETTING_MAX 20 #define ATH9K_SIG_SPUR_IMM_SETTING_MIN 0 -- cgit v1.2.3 From 568f7a438f6d5f0f0909c94e66b7c5c8b96ebf7a Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Tue, 4 Jun 2013 13:40:38 +0200 Subject: rt2x00: rt2x00queue: add priv_size field to struct data_queue Add a new field into struct data_queue and store the size of the per-queue_entry private data in that. Additionally, use the new field in the rt2x00queue_alloc_entries function to compute the size of the queue entries for a given queue. The patch does not change the current behaviour but makes it possible to remove the queue_desc parameter of the rt2x00queue_alloc_entries function. That will be done by a subsequent patch. Signed-off-by: Gabor Juhos Acked-by: Stanislaw Gruszka Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00queue.c | 5 +++-- drivers/net/wireless/rt2x00/rt2x00queue.h | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c index 5efbbbdca701..7f938a513f3a 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c @@ -1173,7 +1173,7 @@ static int rt2x00queue_alloc_entries(struct data_queue *queue, /* * Allocate all queue entries. */ - entry_size = sizeof(*entries) + qdesc->priv_size; + entry_size = sizeof(*entries) + queue->priv_size; entries = kcalloc(queue->limit, entry_size, GFP_KERNEL); if (!entries) return -ENOMEM; @@ -1189,7 +1189,7 @@ static int rt2x00queue_alloc_entries(struct data_queue *queue, entries[i].entry_idx = i; entries[i].priv_data = QUEUE_ENTRY_PRIV_OFFSET(entries, i, queue->limit, - sizeof(*entries), qdesc->priv_size); + sizeof(*entries), queue->priv_size); } #undef QUEUE_ENTRY_PRIV_OFFSET @@ -1329,6 +1329,7 @@ static void rt2x00queue_init(struct rt2x00_dev *rt2x00dev, queue->data_size = qdesc->data_size; queue->desc_size = qdesc->desc_size; queue->winfo_size = qdesc->winfo_size; + queue->priv_size = qdesc->priv_size; } int rt2x00queue_allocate(struct rt2x00_dev *rt2x00dev) diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.h b/drivers/net/wireless/rt2x00/rt2x00queue.h index 4a7b34e9261b..2cf4903ce112 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.h +++ b/drivers/net/wireless/rt2x00/rt2x00queue.h @@ -453,6 +453,7 @@ enum data_queue_flags { * @cw_max: The cw max value for outgoing frames (field ignored in RX queue). * @data_size: Maximum data size for the frames in this queue. * @desc_size: Hardware descriptor size for the data in this queue. + * @priv_size: Size of per-queue_entry private data. * @usb_endpoint: Device endpoint used for communication (USB only) * @usb_maxpacket: Max packet size for given endpoint (USB only) */ @@ -481,6 +482,7 @@ struct data_queue { unsigned short data_size; unsigned char desc_size; unsigned char winfo_size; + unsigned short priv_size; unsigned short usb_endpoint; unsigned short usb_maxpacket; -- cgit v1.2.3 From 15d6c07929a2efa8802b5cc9bb8c91bdf5ba219b Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Tue, 4 Jun 2013 13:40:39 +0200 Subject: rt2x00: rt2x00queue: remove qdesc parameter of rt2x00queue_alloc_entries The qdesc parameter is not used anymore, so remove that. Signed-off-by: Gabor Juhos Acked-by: Stanislaw Gruszka Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00queue.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c index 7f938a513f3a..c12d1c8ae618 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c @@ -1161,8 +1161,7 @@ void rt2x00queue_init_queues(struct rt2x00_dev *rt2x00dev) } } -static int rt2x00queue_alloc_entries(struct data_queue *queue, - const struct data_queue_desc *qdesc) +static int rt2x00queue_alloc_entries(struct data_queue *queue) { struct queue_entry *entries; unsigned int entry_size; @@ -1231,23 +1230,22 @@ int rt2x00queue_initialize(struct rt2x00_dev *rt2x00dev) struct data_queue *queue; int status; - status = rt2x00queue_alloc_entries(rt2x00dev->rx, rt2x00dev->ops->rx); + status = rt2x00queue_alloc_entries(rt2x00dev->rx); if (status) goto exit; tx_queue_for_each(rt2x00dev, queue) { - status = rt2x00queue_alloc_entries(queue, rt2x00dev->ops->tx); + status = rt2x00queue_alloc_entries(queue); if (status) goto exit; } - status = rt2x00queue_alloc_entries(rt2x00dev->bcn, rt2x00dev->ops->bcn); + status = rt2x00queue_alloc_entries(rt2x00dev->bcn); if (status) goto exit; if (test_bit(REQUIRE_ATIM_QUEUE, &rt2x00dev->cap_flags)) { - status = rt2x00queue_alloc_entries(rt2x00dev->atim, - rt2x00dev->ops->atim); + status = rt2x00queue_alloc_entries(rt2x00dev->atim); if (status) goto exit; } -- cgit v1.2.3 From 56e8256351c358d7a2003f6fe10dcb5e2bc394f7 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Tue, 4 Jun 2013 13:40:40 +0200 Subject: rt2x00: rt2x00dev: use rt2x00dev->bcn->limit The beacon data queue is initialized already, so fetch the number of the queue entries from that instead of using the entry_num field of the data queue descriptor. The two values are the same, and the use of the rt2x00dev->bcn->limit value allows us to get rid of a superfluous pointer dereference. Signed-off-by: Gabor Juhos Acked-by: Stanislaw Gruszka Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 6a201725bc50..dff5012f0548 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -1336,7 +1336,7 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) * beacon entries. */ rt2x00dev->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); - if (rt2x00dev->ops->bcn->entry_num > 0) + if (rt2x00dev->bcn->limit > 0) rt2x00dev->hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP) | -- cgit v1.2.3 From 04453e9bda9da510e79c66f56ab463215d042aa8 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Tue, 4 Jun 2013 13:40:41 +0200 Subject: rt2x00: rt2x00queue: setup queue->threshold from queue->limit Use the queue->limit value instead of the qdesc->entry_num to compute the threshold. The two source values are the same and the data queue descriptor structure will be removed by a later patch. Also separate the computation from the rest of the init code to make further changes easier. Signed-off-by: Gabor Juhos Acked-by: Stanislaw Gruszka Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00queue.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c index c12d1c8ae618..3ae2264fdabc 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c @@ -1323,11 +1323,12 @@ static void rt2x00queue_init(struct rt2x00_dev *rt2x00dev, BUG_ON(!qdesc); queue->limit = qdesc->entry_num; - queue->threshold = DIV_ROUND_UP(qdesc->entry_num, 10); queue->data_size = qdesc->data_size; queue->desc_size = qdesc->desc_size; queue->winfo_size = qdesc->winfo_size; queue->priv_size = qdesc->priv_size; + + queue->threshold = DIV_ROUND_UP(queue->limit, 10); } int rt2x00queue_allocate(struct rt2x00_dev *rt2x00dev) -- cgit v1.2.3 From 25bf6ce41d2692db2e991447015fb4bfbf3c3982 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Tue, 4 Jun 2013 13:40:42 +0200 Subject: rt2x00: add queue_init callback to rt2x00_ops The driver uses static data structures for initializing specific fields of a given data queue. These static queue data descriptor structures are containing values which related to a given chipset. Even though the values are chip specific, the actual selection of the used structure is based on device specific vendor/product identifiers. This approach works, but it is not always reliable. Sometimes the vendor and/or device IDs of the PCI and USB devices contains improper values which makes it impossible to select the correct structure for such devices. The patch adds a new callback to tr2x00_ops which is called after the chipset detection is finished. This allows the drivers to do dynamic initialization of the data_queue structure for a given queue based on the actual chipset. After each driver implements the queue_init callback, the data_queue_desc structure will be removed. Signed-off-by: Gabor Juhos Acked-by: Stanislaw Gruszka Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00.h | 1 + drivers/net/wireless/rt2x00/rt2x00queue.c | 22 +++++++++++++--------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index 7510723a8c37..2d2db6472c04 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -653,6 +653,7 @@ struct rt2x00_ops { const struct data_queue_desc *tx; const struct data_queue_desc *bcn; const struct data_queue_desc *atim; + void (*queue_init)(struct data_queue *queue); const struct rt2x00lib_ops *lib; const void *drv; const struct ieee80211_ops *hw; diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c index 3ae2264fdabc..2a99ff1714ac 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c @@ -1306,8 +1306,6 @@ rt2x00queue_get_qdesc_by_qid(struct rt2x00_dev *rt2x00dev, static void rt2x00queue_init(struct rt2x00_dev *rt2x00dev, struct data_queue *queue, enum data_queue_qid qid) { - const struct data_queue_desc *qdesc; - mutex_init(&queue->status_lock); spin_lock_init(&queue->tx_lock); spin_lock_init(&queue->index_lock); @@ -1319,14 +1317,20 @@ static void rt2x00queue_init(struct rt2x00_dev *rt2x00dev, queue->cw_min = 5; queue->cw_max = 10; - qdesc = rt2x00queue_get_qdesc_by_qid(rt2x00dev, qid); - BUG_ON(!qdesc); + if (rt2x00dev->ops->queue_init) { + rt2x00dev->ops->queue_init(queue); + } else { + const struct data_queue_desc *qdesc; + + qdesc = rt2x00queue_get_qdesc_by_qid(rt2x00dev, qid); + BUG_ON(!qdesc); - queue->limit = qdesc->entry_num; - queue->data_size = qdesc->data_size; - queue->desc_size = qdesc->desc_size; - queue->winfo_size = qdesc->winfo_size; - queue->priv_size = qdesc->priv_size; + queue->limit = qdesc->entry_num; + queue->data_size = qdesc->data_size; + queue->desc_size = qdesc->desc_size; + queue->winfo_size = qdesc->winfo_size; + queue->priv_size = qdesc->priv_size; + } queue->threshold = DIV_ROUND_UP(queue->limit, 10); } -- cgit v1.2.3 From d36d13a3ec1ef25c411d39c1e14dd2cdff98edbb Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Tue, 4 Jun 2013 13:40:43 +0200 Subject: rt2x00: rt2800usb: implement queue_init callback The generic rt2x00 code has been changed to allow the drivers toimplement dynamic data_queue initialization. Remove the static data queue descriptor structures and implement the queue_init callback instead. The actual chipset is already known when the callback is used. This allows us to use a single callback for all supported devices. Signed-off-by: Gabor Juhos Acked-by: Stanislaw Gruszka Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800usb.c | 100 +++++++++++++++----------------- 1 file changed, 48 insertions(+), 52 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c index c71a48da9a31..b81d5098816a 100644 --- a/drivers/net/wireless/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/rt2x00/rt2800usb.c @@ -849,29 +849,54 @@ static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = { .sta_remove = rt2800_sta_remove, }; -static const struct data_queue_desc rt2800usb_queue_rx = { - .entry_num = 128, - .data_size = AGGREGATION_SIZE, - .desc_size = RXINFO_DESC_SIZE, - .winfo_size = RXWI_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_usb), -}; +static void rt2800usb_queue_init(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + unsigned short txwi_size, rxwi_size; -static const struct data_queue_desc rt2800usb_queue_tx = { - .entry_num = 16, - .data_size = AGGREGATION_SIZE, - .desc_size = TXINFO_DESC_SIZE, - .winfo_size = TXWI_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_usb), -}; + if (rt2x00_rt(rt2x00dev, RT5592)) { + txwi_size = TXWI_DESC_SIZE_5592; + rxwi_size = RXWI_DESC_SIZE_5592; + } else { + txwi_size = TXWI_DESC_SIZE; + rxwi_size = RXWI_DESC_SIZE; + } -static const struct data_queue_desc rt2800usb_queue_bcn = { - .entry_num = 8, - .data_size = MGMT_FRAME_SIZE, - .desc_size = TXINFO_DESC_SIZE, - .winfo_size = TXWI_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_usb), -}; + switch (queue->qid) { + case QID_RX: + queue->limit = 128; + queue->data_size = AGGREGATION_SIZE; + queue->desc_size = RXINFO_DESC_SIZE; + queue->winfo_size = rxwi_size; + queue->priv_size = sizeof(struct queue_entry_priv_usb); + break; + + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + queue->limit = 16; + queue->data_size = AGGREGATION_SIZE; + queue->desc_size = TXINFO_DESC_SIZE; + queue->winfo_size = txwi_size; + queue->priv_size = sizeof(struct queue_entry_priv_usb); + break; + + case QID_BEACON: + queue->limit = 8; + queue->data_size = MGMT_FRAME_SIZE; + queue->desc_size = TXINFO_DESC_SIZE; + queue->winfo_size = txwi_size; + queue->priv_size = sizeof(struct queue_entry_priv_usb); + break; + + case QID_ATIM: + /* fallthrough */ + default: + BUG(); + break; + } +} static const struct rt2x00_ops rt2800usb_ops = { .name = KBUILD_MODNAME, @@ -881,9 +906,7 @@ static const struct rt2x00_ops rt2800usb_ops = { .rf_size = RF_SIZE, .tx_queues = NUM_TX_QUEUES, .extra_tx_headroom = TXINFO_DESC_SIZE + TXWI_DESC_SIZE, - .rx = &rt2800usb_queue_rx, - .tx = &rt2800usb_queue_tx, - .bcn = &rt2800usb_queue_bcn, + .queue_init = rt2800usb_queue_init, .lib = &rt2800usb_rt2x00_ops, .drv = &rt2800usb_rt2800_ops, .hw = &rt2800usb_mac80211_ops, @@ -892,31 +915,6 @@ static const struct rt2x00_ops rt2800usb_ops = { #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ }; -static const struct data_queue_desc rt2800usb_queue_rx_5592 = { - .entry_num = 128, - .data_size = AGGREGATION_SIZE, - .desc_size = RXINFO_DESC_SIZE, - .winfo_size = RXWI_DESC_SIZE_5592, - .priv_size = sizeof(struct queue_entry_priv_usb), -}; - -static const struct data_queue_desc rt2800usb_queue_tx_5592 = { - .entry_num = 16, - .data_size = AGGREGATION_SIZE, - .desc_size = TXINFO_DESC_SIZE, - .winfo_size = TXWI_DESC_SIZE_5592, - .priv_size = sizeof(struct queue_entry_priv_usb), -}; - -static const struct data_queue_desc rt2800usb_queue_bcn_5592 = { - .entry_num = 8, - .data_size = MGMT_FRAME_SIZE, - .desc_size = TXINFO_DESC_SIZE, - .winfo_size = TXWI_DESC_SIZE_5592, - .priv_size = sizeof(struct queue_entry_priv_usb), -}; - - static const struct rt2x00_ops rt2800usb_ops_5592 = { .name = KBUILD_MODNAME, .drv_data_size = sizeof(struct rt2800_drv_data), @@ -925,9 +923,7 @@ static const struct rt2x00_ops rt2800usb_ops_5592 = { .rf_size = RF_SIZE, .tx_queues = NUM_TX_QUEUES, .extra_tx_headroom = TXINFO_DESC_SIZE + TXWI_DESC_SIZE_5592, - .rx = &rt2800usb_queue_rx_5592, - .tx = &rt2800usb_queue_tx_5592, - .bcn = &rt2800usb_queue_bcn_5592, + .queue_init = rt2800usb_queue_init, .lib = &rt2800usb_rt2x00_ops, .drv = &rt2800usb_rt2800_ops, .hw = &rt2800usb_mac80211_ops, -- cgit v1.2.3 From 1896b760c3024e90db3b3577bf0ed31c008a3543 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Tue, 4 Jun 2013 13:40:44 +0200 Subject: rt2x00: rt2800pci: implement queue_init callback The generic rt2x00 code has been changed to allow the drivers toimplement dynamic data_queue initialization. Remove the static data queue descriptor structures and implement the queue_init callback instead. Signed-off-by: Gabor Juhos Acked-by: Stanislaw Gruszka Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800pci.c | 60 ++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c index 330f1d25726d..260c8b4d7399 100644 --- a/drivers/net/wireless/rt2x00/rt2800pci.c +++ b/drivers/net/wireless/rt2x00/rt2800pci.c @@ -1186,29 +1186,43 @@ static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = { .sta_remove = rt2800_sta_remove, }; -static const struct data_queue_desc rt2800pci_queue_rx = { - .entry_num = 128, - .data_size = AGGREGATION_SIZE, - .desc_size = RXD_DESC_SIZE, - .winfo_size = RXWI_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_mmio), -}; +static void rt2800pci_queue_init(struct data_queue *queue) +{ + switch (queue->qid) { + case QID_RX: + queue->limit = 128; + queue->data_size = AGGREGATION_SIZE; + queue->desc_size = RXD_DESC_SIZE; + queue->winfo_size = RXWI_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; -static const struct data_queue_desc rt2800pci_queue_tx = { - .entry_num = 64, - .data_size = AGGREGATION_SIZE, - .desc_size = TXD_DESC_SIZE, - .winfo_size = TXWI_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_mmio), -}; + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + queue->limit = 64; + queue->data_size = AGGREGATION_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->winfo_size = TXWI_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; -static const struct data_queue_desc rt2800pci_queue_bcn = { - .entry_num = 8, - .data_size = 0, /* No DMA required for beacons */ - .desc_size = TXD_DESC_SIZE, - .winfo_size = TXWI_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_mmio), -}; + case QID_BEACON: + queue->limit = 8; + queue->data_size = 0; /* No DMA required for beacons */ + queue->desc_size = TXD_DESC_SIZE; + queue->winfo_size = TXWI_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + case QID_ATIM: + /* fallthrough */ + default: + BUG(); + break; + } +} static const struct rt2x00_ops rt2800pci_ops = { .name = KBUILD_MODNAME, @@ -1218,9 +1232,7 @@ static const struct rt2x00_ops rt2800pci_ops = { .rf_size = RF_SIZE, .tx_queues = NUM_TX_QUEUES, .extra_tx_headroom = TXWI_DESC_SIZE, - .rx = &rt2800pci_queue_rx, - .tx = &rt2800pci_queue_tx, - .bcn = &rt2800pci_queue_bcn, + .queue_init = rt2800pci_queue_init, .lib = &rt2800pci_rt2x00_ops, .drv = &rt2800pci_rt2800_ops, .hw = &rt2800pci_mac80211_ops, -- cgit v1.2.3 From 0d7aada3bcbc0bde68379831d29c1a015f2fd613 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Tue, 4 Jun 2013 13:40:45 +0200 Subject: rt2x00: rt73usb: implement queue_init callback The generic rt2x00 code has been changed to allow the drivers toimplement dynamic data_queue initialization. Remove the static data queue descriptor structures and implement the queue_init callback instead. Signed-off-by: Gabor Juhos Acked-by: Stanislaw Gruszka Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt73usb.c | 54 +++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c index 377e09bb0b81..b2e346a482cc 100644 --- a/drivers/net/wireless/rt2x00/rt73usb.c +++ b/drivers/net/wireless/rt2x00/rt73usb.c @@ -2359,26 +2359,40 @@ static const struct rt2x00lib_ops rt73usb_rt2x00_ops = { .config = rt73usb_config, }; -static const struct data_queue_desc rt73usb_queue_rx = { - .entry_num = 32, - .data_size = DATA_FRAME_SIZE, - .desc_size = RXD_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_usb), -}; +static void rt73usb_queue_init(struct data_queue *queue) +{ + switch (queue->qid) { + case QID_RX: + queue->limit = 32; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = RXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_usb); + break; -static const struct data_queue_desc rt73usb_queue_tx = { - .entry_num = 32, - .data_size = DATA_FRAME_SIZE, - .desc_size = TXD_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_usb), -}; + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + queue->limit = 32; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_usb); + break; -static const struct data_queue_desc rt73usb_queue_bcn = { - .entry_num = 4, - .data_size = MGMT_FRAME_SIZE, - .desc_size = TXINFO_SIZE, - .priv_size = sizeof(struct queue_entry_priv_usb), -}; + case QID_BEACON: + queue->limit = 4; + queue->data_size = MGMT_FRAME_SIZE; + queue->desc_size = TXINFO_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_usb); + break; + + case QID_ATIM: + /* fallthrough */ + default: + BUG(); + break; + } +} static const struct rt2x00_ops rt73usb_ops = { .name = KBUILD_MODNAME, @@ -2387,9 +2401,7 @@ static const struct rt2x00_ops rt73usb_ops = { .rf_size = RF_SIZE, .tx_queues = NUM_TX_QUEUES, .extra_tx_headroom = TXD_DESC_SIZE, - .rx = &rt73usb_queue_rx, - .tx = &rt73usb_queue_tx, - .bcn = &rt73usb_queue_bcn, + .queue_init = rt73usb_queue_init, .lib = &rt73usb_rt2x00_ops, .hw = &rt73usb_mac80211_ops, #ifdef CONFIG_RT2X00_LIB_DEBUGFS -- cgit v1.2.3 From 3d8979ba7d873cd203bf98a17a2143a20eef488c Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Tue, 4 Jun 2013 13:40:46 +0200 Subject: rt2x00: rt2400pci: implement queue_init callback The generic rt2x00 code has been changed to allow the drivers toimplement dynamic data_queue initialization. Remove the static data queue descriptor structures and implement the queue_init callback instead. Compile tested only. Signed-off-by: Gabor Juhos Acked-by: Stanislaw Gruszka Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2400pci.c | 65 +++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2400pci.c b/drivers/net/wireless/rt2x00/rt2400pci.c index f7143733d7e9..e1ec9a4517d1 100644 --- a/drivers/net/wireless/rt2x00/rt2400pci.c +++ b/drivers/net/wireless/rt2x00/rt2400pci.c @@ -1767,33 +1767,45 @@ static const struct rt2x00lib_ops rt2400pci_rt2x00_ops = { .config = rt2400pci_config, }; -static const struct data_queue_desc rt2400pci_queue_rx = { - .entry_num = 24, - .data_size = DATA_FRAME_SIZE, - .desc_size = RXD_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_mmio), -}; +static void rt2400pci_queue_init(struct data_queue *queue) +{ + switch (queue->qid) { + case QID_RX: + queue->limit = 24; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = RXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; -static const struct data_queue_desc rt2400pci_queue_tx = { - .entry_num = 24, - .data_size = DATA_FRAME_SIZE, - .desc_size = TXD_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_mmio), -}; + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + queue->limit = 24; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; -static const struct data_queue_desc rt2400pci_queue_bcn = { - .entry_num = 1, - .data_size = MGMT_FRAME_SIZE, - .desc_size = TXD_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_mmio), -}; + case QID_BEACON: + queue->limit = 1; + queue->data_size = MGMT_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; -static const struct data_queue_desc rt2400pci_queue_atim = { - .entry_num = 8, - .data_size = DATA_FRAME_SIZE, - .desc_size = TXD_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_mmio), -}; + case QID_ATIM: + queue->limit = 8; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + default: + BUG(); + break; + } +} static const struct rt2x00_ops rt2400pci_ops = { .name = KBUILD_MODNAME, @@ -1802,10 +1814,7 @@ static const struct rt2x00_ops rt2400pci_ops = { .rf_size = RF_SIZE, .tx_queues = NUM_TX_QUEUES, .extra_tx_headroom = 0, - .rx = &rt2400pci_queue_rx, - .tx = &rt2400pci_queue_tx, - .bcn = &rt2400pci_queue_bcn, - .atim = &rt2400pci_queue_atim, + .queue_init = rt2400pci_queue_init, .lib = &rt2400pci_rt2x00_ops, .hw = &rt2400pci_mac80211_ops, #ifdef CONFIG_RT2X00_LIB_DEBUGFS -- cgit v1.2.3 From 7c030821ede91bca94b425d27afd504842bb1865 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Tue, 4 Jun 2013 13:40:47 +0200 Subject: rt2x00: rt2500pci: implement queue_init callback The generic rt2x00 code has been changed to allow the drivers toimplement dynamic data_queue initialization. Remove the static data queue descriptor structures and implement the queue_init callback instead. Compile tested only. Signed-off-by: Gabor Juhos Acked-by: Stanislaw Gruszka Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2500pci.c | 65 +++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2500pci.c b/drivers/net/wireless/rt2x00/rt2500pci.c index 77e45b223d15..a1670e8967ed 100644 --- a/drivers/net/wireless/rt2x00/rt2500pci.c +++ b/drivers/net/wireless/rt2x00/rt2500pci.c @@ -2056,33 +2056,45 @@ static const struct rt2x00lib_ops rt2500pci_rt2x00_ops = { .config = rt2500pci_config, }; -static const struct data_queue_desc rt2500pci_queue_rx = { - .entry_num = 32, - .data_size = DATA_FRAME_SIZE, - .desc_size = RXD_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_mmio), -}; +static void rt2500pci_queue_init(struct data_queue *queue) +{ + switch (queue->qid) { + case QID_RX: + queue->limit = 32; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = RXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; -static const struct data_queue_desc rt2500pci_queue_tx = { - .entry_num = 32, - .data_size = DATA_FRAME_SIZE, - .desc_size = TXD_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_mmio), -}; + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + queue->limit = 32; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; -static const struct data_queue_desc rt2500pci_queue_bcn = { - .entry_num = 1, - .data_size = MGMT_FRAME_SIZE, - .desc_size = TXD_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_mmio), -}; + case QID_BEACON: + queue->limit = 1; + queue->data_size = MGMT_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; -static const struct data_queue_desc rt2500pci_queue_atim = { - .entry_num = 8, - .data_size = DATA_FRAME_SIZE, - .desc_size = TXD_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_mmio), -}; + case QID_ATIM: + queue->limit = 8; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + default: + BUG(); + break; + } +} static const struct rt2x00_ops rt2500pci_ops = { .name = KBUILD_MODNAME, @@ -2091,10 +2103,7 @@ static const struct rt2x00_ops rt2500pci_ops = { .rf_size = RF_SIZE, .tx_queues = NUM_TX_QUEUES, .extra_tx_headroom = 0, - .rx = &rt2500pci_queue_rx, - .tx = &rt2500pci_queue_tx, - .bcn = &rt2500pci_queue_bcn, - .atim = &rt2500pci_queue_atim, + .queue_init = rt2500pci_queue_init, .lib = &rt2500pci_rt2x00_ops, .hw = &rt2500pci_mac80211_ops, #ifdef CONFIG_RT2X00_LIB_DEBUGFS -- cgit v1.2.3 From 7106d97bc475b2c0f2a134f804b245bf5eaebffc Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Tue, 4 Jun 2013 13:40:48 +0200 Subject: rt2x00: rt61pci: implement queue_init callback The generic rt2x00 code has been changed to allow the drivers toimplement dynamic data_queue initialization. Remove the static data queue descriptor structures and implement the queue_init callback instead. Signed-off-by: Gabor Juhos Tested-by: Jakub Kicinski Acked-by: Stanislaw Gruszka Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt61pci.c | 54 +++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c index 7e1759b3e49a..17507d12d5ee 100644 --- a/drivers/net/wireless/rt2x00/rt61pci.c +++ b/drivers/net/wireless/rt2x00/rt61pci.c @@ -3025,26 +3025,40 @@ static const struct rt2x00lib_ops rt61pci_rt2x00_ops = { .config = rt61pci_config, }; -static const struct data_queue_desc rt61pci_queue_rx = { - .entry_num = 32, - .data_size = DATA_FRAME_SIZE, - .desc_size = RXD_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_mmio), -}; +static void rt61pci_queue_init(struct data_queue *queue) +{ + switch (queue->qid) { + case QID_RX: + queue->limit = 32; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = RXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; -static const struct data_queue_desc rt61pci_queue_tx = { - .entry_num = 32, - .data_size = DATA_FRAME_SIZE, - .desc_size = TXD_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_mmio), -}; + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + queue->limit = 32; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; -static const struct data_queue_desc rt61pci_queue_bcn = { - .entry_num = 4, - .data_size = 0, /* No DMA required for beacons */ - .desc_size = TXINFO_SIZE, - .priv_size = sizeof(struct queue_entry_priv_mmio), -}; + case QID_BEACON: + queue->limit = 4; + queue->data_size = 0; /* No DMA required for beacons */ + queue->desc_size = TXINFO_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + case QID_ATIM: + /* fallthrough */ + default: + BUG(); + break; + } +} static const struct rt2x00_ops rt61pci_ops = { .name = KBUILD_MODNAME, @@ -3053,9 +3067,7 @@ static const struct rt2x00_ops rt61pci_ops = { .rf_size = RF_SIZE, .tx_queues = NUM_TX_QUEUES, .extra_tx_headroom = 0, - .rx = &rt61pci_queue_rx, - .tx = &rt61pci_queue_tx, - .bcn = &rt61pci_queue_bcn, + .queue_init = rt61pci_queue_init, .lib = &rt61pci_rt2x00_ops, .hw = &rt61pci_mac80211_ops, #ifdef CONFIG_RT2X00_LIB_DEBUGFS -- cgit v1.2.3 From c29a32c8f15c81470d16aee82f52432eb477aa9b Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Tue, 4 Jun 2013 13:40:49 +0200 Subject: rt2x00: rt2500usb: implement queue_init callback The generic rt2x00 code has been changed to allow the drivers toimplement dynamic data_queue initialization. Remove the static data queue descriptor structures and implement the queue_init callback instead. Compile tested only. Signed-off-by: Gabor Juhos Acked-by: Stanislaw Gruszka Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2500usb.c | 65 +++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c index a7f7b365eff4..e5e5479bcf93 100644 --- a/drivers/net/wireless/rt2x00/rt2500usb.c +++ b/drivers/net/wireless/rt2x00/rt2500usb.c @@ -1867,33 +1867,45 @@ static const struct rt2x00lib_ops rt2500usb_rt2x00_ops = { .config = rt2500usb_config, }; -static const struct data_queue_desc rt2500usb_queue_rx = { - .entry_num = 32, - .data_size = DATA_FRAME_SIZE, - .desc_size = RXD_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_usb), -}; +static void rt2500usb_queue_init(struct data_queue *queue) +{ + switch (queue->qid) { + case QID_RX: + queue->limit = 32; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = RXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_usb); + break; -static const struct data_queue_desc rt2500usb_queue_tx = { - .entry_num = 32, - .data_size = DATA_FRAME_SIZE, - .desc_size = TXD_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_usb), -}; + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + queue->limit = 32; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_usb); + break; -static const struct data_queue_desc rt2500usb_queue_bcn = { - .entry_num = 1, - .data_size = MGMT_FRAME_SIZE, - .desc_size = TXD_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_usb_bcn), -}; + case QID_BEACON: + queue->limit = 1; + queue->data_size = MGMT_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_usb_bcn); + break; -static const struct data_queue_desc rt2500usb_queue_atim = { - .entry_num = 8, - .data_size = DATA_FRAME_SIZE, - .desc_size = TXD_DESC_SIZE, - .priv_size = sizeof(struct queue_entry_priv_usb), -}; + case QID_ATIM: + queue->limit = 8; + queue->data_size = DATA_FRAME_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->priv_size = sizeof(struct queue_entry_priv_usb); + break; + + default: + BUG(); + break; + } +} static const struct rt2x00_ops rt2500usb_ops = { .name = KBUILD_MODNAME, @@ -1902,10 +1914,7 @@ static const struct rt2x00_ops rt2500usb_ops = { .rf_size = RF_SIZE, .tx_queues = NUM_TX_QUEUES, .extra_tx_headroom = TXD_DESC_SIZE, - .rx = &rt2500usb_queue_rx, - .tx = &rt2500usb_queue_tx, - .bcn = &rt2500usb_queue_bcn, - .atim = &rt2500usb_queue_atim, + .queue_init = rt2500usb_queue_init, .lib = &rt2500usb_rt2x00_ops, .hw = &rt2500usb_mac80211_ops, #ifdef CONFIG_RT2X00_LIB_DEBUGFS -- cgit v1.2.3 From 705802bf56b1b7a64e543805ba196a6e1fb80ec6 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Tue, 4 Jun 2013 13:40:50 +0200 Subject: rt2x00: remove data_queue_desc struct If the queue_init callback is implemented by a driver it gets used instead of the data_queue_desc based initialization. The queue_init callback is implemented for each drivers now, so the old initialization method is not used anymore. Remove the unused data_queue_desc structure and all of the related code. Signed-off-by: Gabor Juhos Acked-by: Stanislaw Gruszka Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00.h | 4 --- drivers/net/wireless/rt2x00/rt2x00queue.c | 42 +------------------------------ drivers/net/wireless/rt2x00/rt2x00queue.h | 19 -------------- 3 files changed, 1 insertion(+), 64 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index 2d2db6472c04..2a17f7e4ed5e 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -649,10 +649,6 @@ struct rt2x00_ops { const unsigned int rf_size; const unsigned int tx_queues; const unsigned int extra_tx_headroom; - const struct data_queue_desc *rx; - const struct data_queue_desc *tx; - const struct data_queue_desc *bcn; - const struct data_queue_desc *atim; void (*queue_init)(struct data_queue *queue); const struct rt2x00lib_ops *lib; const void *drv; diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c index 2a99ff1714ac..c4f1e2b8dc8e 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c @@ -1276,33 +1276,6 @@ void rt2x00queue_uninitialize(struct rt2x00_dev *rt2x00dev) } } -static const struct data_queue_desc * -rt2x00queue_get_qdesc_by_qid(struct rt2x00_dev *rt2x00dev, - enum data_queue_qid qid) -{ - switch (qid) { - case QID_RX: - return rt2x00dev->ops->rx; - - case QID_AC_BE: - case QID_AC_BK: - case QID_AC_VO: - case QID_AC_VI: - return rt2x00dev->ops->tx; - - case QID_BEACON: - return rt2x00dev->ops->bcn; - - case QID_ATIM: - return rt2x00dev->ops->atim; - - default: - break; - } - - return NULL; -} - static void rt2x00queue_init(struct rt2x00_dev *rt2x00dev, struct data_queue *queue, enum data_queue_qid qid) { @@ -1317,20 +1290,7 @@ static void rt2x00queue_init(struct rt2x00_dev *rt2x00dev, queue->cw_min = 5; queue->cw_max = 10; - if (rt2x00dev->ops->queue_init) { - rt2x00dev->ops->queue_init(queue); - } else { - const struct data_queue_desc *qdesc; - - qdesc = rt2x00queue_get_qdesc_by_qid(rt2x00dev, qid); - BUG_ON(!qdesc); - - queue->limit = qdesc->entry_num; - queue->data_size = qdesc->data_size; - queue->desc_size = qdesc->desc_size; - queue->winfo_size = qdesc->winfo_size; - queue->priv_size = qdesc->priv_size; - } + rt2x00dev->ops->queue_init(queue); queue->threshold = DIV_ROUND_UP(queue->limit, 10); } diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.h b/drivers/net/wireless/rt2x00/rt2x00queue.h index 2cf4903ce112..ebe117224979 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.h +++ b/drivers/net/wireless/rt2x00/rt2x00queue.h @@ -488,25 +488,6 @@ struct data_queue { unsigned short usb_maxpacket; }; -/** - * struct data_queue_desc: Data queue description - * - * The information in this structure is used by drivers - * to inform rt2x00lib about the creation of the data queue. - * - * @entry_num: Maximum number of entries for a queue. - * @data_size: Maximum data size for the frames in this queue. - * @desc_size: Hardware descriptor size for the data in this queue. - * @priv_size: Size of per-queue_entry private data. - */ -struct data_queue_desc { - unsigned short entry_num; - unsigned short data_size; - unsigned char desc_size; - unsigned char winfo_size; - unsigned short priv_size; -}; - /** * queue_end - Return pointer to the last queue (HELPER MACRO). * @__dev: Pointer to &struct rt2x00_dev -- cgit v1.2.3 From df8100836e05b457af7b1627c601afe82e1d3ebe Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Tue, 4 Jun 2013 16:19:56 -0700 Subject: mwifiex: fix regression issue for usb interface PATCH "mwifiex: scan delay timer cleanup in unload path" adds code to cancel scan delay timer in unload path. It causes a regression for USB interface. USB8797 card gets enumerated twice. First enumeration is for firmware download and second enumeration expects firmware initialization. It was observed that we are trying del_timer_sync() without setting up the timer when remove handler is called after first enumeration. This patch moves setup_timer() call to appropriate place so that timer is setup for both the enumerations. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/init.c | 81 ------------------------------------ drivers/net/wireless/mwifiex/main.c | 82 +++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 81 deletions(-) diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c index c7f11c0c3bb7..2fe31dcaa6ef 100644 --- a/drivers/net/wireless/mwifiex/init.c +++ b/drivers/net/wireless/mwifiex/init.c @@ -52,84 +52,6 @@ static int mwifiex_add_bss_prio_tbl(struct mwifiex_private *priv) return 0; } -static void scan_delay_timer_fn(unsigned long data) -{ - struct mwifiex_private *priv = (struct mwifiex_private *)data; - struct mwifiex_adapter *adapter = priv->adapter; - struct cmd_ctrl_node *cmd_node, *tmp_node; - unsigned long flags; - - if (adapter->surprise_removed) - return; - - if (adapter->scan_delay_cnt == MWIFIEX_MAX_SCAN_DELAY_CNT) { - /* - * Abort scan operation by cancelling all pending scan - * commands - */ - spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); - list_for_each_entry_safe(cmd_node, tmp_node, - &adapter->scan_pending_q, list) { - list_del(&cmd_node->list); - mwifiex_insert_cmd_to_free_q(adapter, cmd_node); - } - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); - - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); - adapter->scan_processing = false; - adapter->scan_delay_cnt = 0; - adapter->empty_tx_q_cnt = 0; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); - - if (priv->scan_request) { - dev_dbg(adapter->dev, "info: aborting scan\n"); - cfg80211_scan_done(priv->scan_request, 1); - priv->scan_request = NULL; - } else { - priv->scan_aborting = false; - dev_dbg(adapter->dev, "info: scan already aborted\n"); - } - goto done; - } - - if (!atomic_read(&priv->adapter->is_tx_received)) { - adapter->empty_tx_q_cnt++; - if (adapter->empty_tx_q_cnt == MWIFIEX_MAX_EMPTY_TX_Q_CNT) { - /* - * No Tx traffic for 200msec. Get scan command from - * scan pending queue and put to cmd pending queue to - * resume scan operation - */ - adapter->scan_delay_cnt = 0; - adapter->empty_tx_q_cnt = 0; - spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); - cmd_node = list_first_entry(&adapter->scan_pending_q, - struct cmd_ctrl_node, list); - list_del(&cmd_node->list); - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, - flags); - - mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, - true); - queue_work(adapter->workqueue, &adapter->main_work); - goto done; - } - } else { - adapter->empty_tx_q_cnt = 0; - } - - /* Delay scan operation further by 20msec */ - mod_timer(&priv->scan_delay_timer, jiffies + - msecs_to_jiffies(MWIFIEX_SCAN_DELAY_MSEC)); - adapter->scan_delay_cnt++; - -done: - if (atomic_read(&priv->adapter->is_tx_received)) - atomic_set(&priv->adapter->is_tx_received, false); - - return; -} - /* * This function initializes the private structure and sets default * values to the members. @@ -211,9 +133,6 @@ int mwifiex_init_priv(struct mwifiex_private *priv) priv->scan_block = false; - setup_timer(&priv->scan_delay_timer, scan_delay_timer_fn, - (unsigned long)priv); - return mwifiex_add_bss_prio_tbl(priv); } diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index 5bc7ef8d04d6..485871940d3c 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -28,6 +28,84 @@ const char driver_version[] = "mwifiex " VERSION " (%s) "; static char *cal_data_cfg; module_param(cal_data_cfg, charp, 0); +static void scan_delay_timer_fn(unsigned long data) +{ + struct mwifiex_private *priv = (struct mwifiex_private *)data; + struct mwifiex_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *cmd_node, *tmp_node; + unsigned long flags; + + if (adapter->surprise_removed) + return; + + if (adapter->scan_delay_cnt == MWIFIEX_MAX_SCAN_DELAY_CNT) { + /* + * Abort scan operation by cancelling all pending scan + * commands + */ + spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); + list_for_each_entry_safe(cmd_node, tmp_node, + &adapter->scan_pending_q, list) { + list_del(&cmd_node->list); + mwifiex_insert_cmd_to_free_q(adapter, cmd_node); + } + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); + + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); + adapter->scan_processing = false; + adapter->scan_delay_cnt = 0; + adapter->empty_tx_q_cnt = 0; + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); + + if (priv->scan_request) { + dev_dbg(adapter->dev, "info: aborting scan\n"); + cfg80211_scan_done(priv->scan_request, 1); + priv->scan_request = NULL; + } else { + priv->scan_aborting = false; + dev_dbg(adapter->dev, "info: scan already aborted\n"); + } + goto done; + } + + if (!atomic_read(&priv->adapter->is_tx_received)) { + adapter->empty_tx_q_cnt++; + if (adapter->empty_tx_q_cnt == MWIFIEX_MAX_EMPTY_TX_Q_CNT) { + /* + * No Tx traffic for 200msec. Get scan command from + * scan pending queue and put to cmd pending queue to + * resume scan operation + */ + adapter->scan_delay_cnt = 0; + adapter->empty_tx_q_cnt = 0; + spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); + cmd_node = list_first_entry(&adapter->scan_pending_q, + struct cmd_ctrl_node, list); + list_del(&cmd_node->list); + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, + flags); + + mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, + true); + queue_work(adapter->workqueue, &adapter->main_work); + goto done; + } + } else { + adapter->empty_tx_q_cnt = 0; + } + + /* Delay scan operation further by 20msec */ + mod_timer(&priv->scan_delay_timer, jiffies + + msecs_to_jiffies(MWIFIEX_SCAN_DELAY_MSEC)); + adapter->scan_delay_cnt++; + +done: + if (atomic_read(&priv->adapter->is_tx_received)) + atomic_set(&priv->adapter->is_tx_received, false); + + return; +} + /* * This function registers the device and performs all the necessary * initializations. @@ -75,6 +153,10 @@ static int mwifiex_register(void *card, struct mwifiex_if_ops *if_ops, adapter->priv[i]->adapter = adapter; adapter->priv_num++; + + setup_timer(&adapter->priv[i]->scan_delay_timer, + scan_delay_timer_fn, + (unsigned long)adapter->priv[i]); } mwifiex_init_lock_list(adapter); -- cgit v1.2.3 From c9e2404c9ffcbb31b0bd1348be5fc7a4de6f90d6 Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Tue, 4 Jun 2013 16:20:38 -0700 Subject: mwifiex: enable/disable tx_amsdu support via module parameter This patch disables tx_amsdu support in mwifiex by default. tx_amdsu support can be enabled via module parameter at load time. Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/wmm.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c index 4be3d33ceae8..944e8846f6fc 100644 --- a/drivers/net/wireless/mwifiex/wmm.c +++ b/drivers/net/wireless/mwifiex/wmm.c @@ -37,6 +37,9 @@ /* Offset for TOS field in the IP header */ #define IPTOS_OFFSET 5 +static bool enable_tx_amsdu; +module_param(enable_tx_amsdu, bool, 0644); + /* WMM information IE */ static const u8 wmm_info_ie[] = { WLAN_EID_VENDOR_SPECIFIC, 0x07, 0x00, 0x50, 0xf2, 0x02, @@ -1233,7 +1236,7 @@ mwifiex_dequeue_tx_packet(struct mwifiex_adapter *adapter) mwifiex_send_delba(priv, tid_del, ra, 1); } } - if (mwifiex_is_amsdu_allowed(priv, tid) && + if (enable_tx_amsdu && mwifiex_is_amsdu_allowed(priv, tid) && mwifiex_is_11n_aggragation_possible(priv, ptr, adapter->tx_buf_size)) mwifiex_11n_aggregate_pkt(priv, ptr, INTF_HEADER_LEN, -- cgit v1.2.3 From ca3ae51396c049fcb9a93ded6f90ec81f7360ffa Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 5 Jun 2013 10:16:33 +0800 Subject: iwlegacy: fix error return code in il3945_pci_probe() Fix to return a negative error code in the il3945_hw_set_hw_params() error handling case instead of 0, as done elsewhere in this function. Signed-off-by: Wei Yongjun Acked-by: Stanislaw Gruszka Signed-off-by: John W. Linville --- drivers/net/wireless/iwlegacy/3945-mac.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlegacy/3945-mac.c b/drivers/net/wireless/iwlegacy/3945-mac.c index b37a582ccbe7..866ce6c68405 100644 --- a/drivers/net/wireless/iwlegacy/3945-mac.c +++ b/drivers/net/wireless/iwlegacy/3945-mac.c @@ -3727,7 +3727,8 @@ il3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) * 5. Setup HW Constants * ********************/ /* Device-specific setup */ - if (il3945_hw_set_hw_params(il)) { + err = il3945_hw_set_hw_params(il); + if (err) { IL_ERR("failed to set hw settings\n"); goto out_eeprom_free; } -- cgit v1.2.3 From d619c62f161a43db1f40e5039ff1285dd29b8658 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Wed, 5 Jun 2013 20:45:36 -0700 Subject: ath: add VHT80 support for regulatory domains This adds VHT80 support for the QCA world regulatory domains. Cc: kvalo@qca.qualcomm.com Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/regd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c index ccc4c718f124..7d077c752dd5 100644 --- a/drivers/net/wireless/ath/regd.c +++ b/drivers/net/wireless/ath/regd.c @@ -42,11 +42,11 @@ static int __ath_regd_init(struct ath_regulatory *reg); NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_OFDM) /* We allow IBSS on these on a case by case basis by regulatory domain */ -#define ATH9K_5GHZ_5150_5350 REG_RULE(5150-10, 5350+10, 40, 0, 30,\ +#define ATH9K_5GHZ_5150_5350 REG_RULE(5150-10, 5350+10, 80, 0, 30,\ NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS) -#define ATH9K_5GHZ_5470_5850 REG_RULE(5470-10, 5850+10, 40, 0, 30,\ +#define ATH9K_5GHZ_5470_5850 REG_RULE(5470-10, 5850+10, 80, 0, 30,\ NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS) -#define ATH9K_5GHZ_5725_5850 REG_RULE(5725-10, 5850+10, 40, 0, 30,\ +#define ATH9K_5GHZ_5725_5850 REG_RULE(5725-10, 5850+10, 80, 0, 30,\ NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS) #define ATH9K_2GHZ_ALL ATH9K_2GHZ_CH01_11, \ -- cgit v1.2.3 From 5616a6efb2a255330319f9f09f3bcf0fc4680b5f Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Thu, 6 Jun 2013 09:36:19 +0200 Subject: rt2x00: move extra_tx_headroom field from rt2x00_ops to rt2x00_dev The extra_tx_headroom field of struct rt2x00_ops indicates the extra TX headroom size required for a given device. This data is redundant, the value can be computed from the desc_size and winfo_size fields of the TX queues. Move the extra_tx_headroom field to struct rt2x00_dev, compute its value in the probe routine and use the cached value in the rest of the code. Signed-off-by: Gabor Juhos Acked-by: Stanislaw Gruszka Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2400pci.c | 1 - drivers/net/wireless/rt2x00/rt2500pci.c | 1 - drivers/net/wireless/rt2x00/rt2500usb.c | 1 - drivers/net/wireless/rt2x00/rt2800pci.c | 1 - drivers/net/wireless/rt2x00/rt2800usb.c | 2 -- drivers/net/wireless/rt2x00/rt2x00.h | 4 +++- drivers/net/wireless/rt2x00/rt2x00dev.c | 18 ++++++++++++++++-- drivers/net/wireless/rt2x00/rt2x00queue.c | 6 +++--- drivers/net/wireless/rt2x00/rt61pci.c | 1 - drivers/net/wireless/rt2x00/rt73usb.c | 1 - 10 files changed, 22 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2400pci.c b/drivers/net/wireless/rt2x00/rt2400pci.c index e1ec9a4517d1..3d53a09da5a1 100644 --- a/drivers/net/wireless/rt2x00/rt2400pci.c +++ b/drivers/net/wireless/rt2x00/rt2400pci.c @@ -1813,7 +1813,6 @@ static const struct rt2x00_ops rt2400pci_ops = { .eeprom_size = EEPROM_SIZE, .rf_size = RF_SIZE, .tx_queues = NUM_TX_QUEUES, - .extra_tx_headroom = 0, .queue_init = rt2400pci_queue_init, .lib = &rt2400pci_rt2x00_ops, .hw = &rt2400pci_mac80211_ops, diff --git a/drivers/net/wireless/rt2x00/rt2500pci.c b/drivers/net/wireless/rt2x00/rt2500pci.c index a1670e8967ed..0ac5c589ddce 100644 --- a/drivers/net/wireless/rt2x00/rt2500pci.c +++ b/drivers/net/wireless/rt2x00/rt2500pci.c @@ -2102,7 +2102,6 @@ static const struct rt2x00_ops rt2500pci_ops = { .eeprom_size = EEPROM_SIZE, .rf_size = RF_SIZE, .tx_queues = NUM_TX_QUEUES, - .extra_tx_headroom = 0, .queue_init = rt2500pci_queue_init, .lib = &rt2500pci_rt2x00_ops, .hw = &rt2500pci_mac80211_ops, diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c index e5e5479bcf93..85acc79f68b8 100644 --- a/drivers/net/wireless/rt2x00/rt2500usb.c +++ b/drivers/net/wireless/rt2x00/rt2500usb.c @@ -1913,7 +1913,6 @@ static const struct rt2x00_ops rt2500usb_ops = { .eeprom_size = EEPROM_SIZE, .rf_size = RF_SIZE, .tx_queues = NUM_TX_QUEUES, - .extra_tx_headroom = TXD_DESC_SIZE, .queue_init = rt2500usb_queue_init, .lib = &rt2500usb_rt2x00_ops, .hw = &rt2500usb_mac80211_ops, diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c index 260c8b4d7399..7c7478219bbc 100644 --- a/drivers/net/wireless/rt2x00/rt2800pci.c +++ b/drivers/net/wireless/rt2x00/rt2800pci.c @@ -1231,7 +1231,6 @@ static const struct rt2x00_ops rt2800pci_ops = { .eeprom_size = EEPROM_SIZE, .rf_size = RF_SIZE, .tx_queues = NUM_TX_QUEUES, - .extra_tx_headroom = TXWI_DESC_SIZE, .queue_init = rt2800pci_queue_init, .lib = &rt2800pci_rt2x00_ops, .drv = &rt2800pci_rt2800_ops, diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c index b81d5098816a..68ea00e169d2 100644 --- a/drivers/net/wireless/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/rt2x00/rt2800usb.c @@ -905,7 +905,6 @@ static const struct rt2x00_ops rt2800usb_ops = { .eeprom_size = EEPROM_SIZE, .rf_size = RF_SIZE, .tx_queues = NUM_TX_QUEUES, - .extra_tx_headroom = TXINFO_DESC_SIZE + TXWI_DESC_SIZE, .queue_init = rt2800usb_queue_init, .lib = &rt2800usb_rt2x00_ops, .drv = &rt2800usb_rt2800_ops, @@ -922,7 +921,6 @@ static const struct rt2x00_ops rt2800usb_ops_5592 = { .eeprom_size = EEPROM_SIZE, .rf_size = RF_SIZE, .tx_queues = NUM_TX_QUEUES, - .extra_tx_headroom = TXINFO_DESC_SIZE + TXWI_DESC_SIZE_5592, .queue_init = rt2800usb_queue_init, .lib = &rt2800usb_rt2x00_ops, .drv = &rt2800usb_rt2800_ops, diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index 2a17f7e4ed5e..ee3fc570b11d 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -648,7 +648,6 @@ struct rt2x00_ops { const unsigned int eeprom_size; const unsigned int rf_size; const unsigned int tx_queues; - const unsigned int extra_tx_headroom; void (*queue_init)(struct data_queue *queue); const struct rt2x00lib_ops *lib; const void *drv; @@ -1007,6 +1006,9 @@ struct rt2x00_dev { */ struct list_head bar_list; spinlock_t bar_list_lock; + + /* Extra TX headroom required for alignment purposes. */ + unsigned int extra_tx_headroom; }; struct rt2x00_bar_list_entry { diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index dff5012f0548..f03e3bba51c3 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -334,7 +334,7 @@ void rt2x00lib_txdone(struct queue_entry *entry, /* * Remove the extra tx headroom from the skb. */ - skb_pull(entry->skb, rt2x00dev->ops->extra_tx_headroom); + skb_pull(entry->skb, rt2x00dev->extra_tx_headroom); /* * Signal that the TX descriptor is no longer in the skb. @@ -1049,7 +1049,7 @@ static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev) */ rt2x00dev->hw->extra_tx_headroom = max_t(unsigned int, IEEE80211_TX_STATUS_HEADROOM, - rt2x00dev->ops->extra_tx_headroom); + rt2x00dev->extra_tx_headroom); /* * Take TX headroom required for alignment into account. @@ -1256,6 +1256,17 @@ static inline void rt2x00lib_set_if_combinations(struct rt2x00_dev *rt2x00dev) rt2x00dev->hw->wiphy->n_iface_combinations = 1; } +static unsigned int rt2x00dev_extra_tx_headroom(struct rt2x00_dev *rt2x00dev) +{ + if (WARN_ON(!rt2x00dev->tx)) + return 0; + + if (rt2x00_is_usb(rt2x00dev)) + return rt2x00dev->tx[0].winfo_size + rt2x00dev->tx[0].desc_size; + + return rt2x00dev->tx[0].winfo_size; +} + /* * driver allocation handlers. */ @@ -1330,6 +1341,9 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) if (retval) goto exit; + /* Cache TX headroom value */ + rt2x00dev->extra_tx_headroom = rt2x00dev_extra_tx_headroom(rt2x00dev); + /* * Determine which operating modes are supported, all modes * which require beaconing, depend on the availability of diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c index c4f1e2b8dc8e..6c0a91ff963c 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c @@ -542,8 +542,8 @@ static int rt2x00queue_write_tx_data(struct queue_entry *entry, /* * Add the requested extra tx headroom in front of the skb. */ - skb_push(entry->skb, rt2x00dev->ops->extra_tx_headroom); - memset(entry->skb->data, 0, rt2x00dev->ops->extra_tx_headroom); + skb_push(entry->skb, rt2x00dev->extra_tx_headroom); + memset(entry->skb->data, 0, rt2x00dev->extra_tx_headroom); /* * Call the driver's write_tx_data function, if it exists. @@ -596,7 +596,7 @@ static void rt2x00queue_bar_check(struct queue_entry *entry) { struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; struct ieee80211_bar *bar = (void *) (entry->skb->data + - rt2x00dev->ops->extra_tx_headroom); + rt2x00dev->extra_tx_headroom); struct rt2x00_bar_list_entry *bar_entry; if (likely(!ieee80211_is_back_req(bar->frame_control))) diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c index 17507d12d5ee..53754bc66d05 100644 --- a/drivers/net/wireless/rt2x00/rt61pci.c +++ b/drivers/net/wireless/rt2x00/rt61pci.c @@ -3066,7 +3066,6 @@ static const struct rt2x00_ops rt61pci_ops = { .eeprom_size = EEPROM_SIZE, .rf_size = RF_SIZE, .tx_queues = NUM_TX_QUEUES, - .extra_tx_headroom = 0, .queue_init = rt61pci_queue_init, .lib = &rt61pci_rt2x00_ops, .hw = &rt61pci_mac80211_ops, diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c index b2e346a482cc..1616ed484ceb 100644 --- a/drivers/net/wireless/rt2x00/rt73usb.c +++ b/drivers/net/wireless/rt2x00/rt73usb.c @@ -2400,7 +2400,6 @@ static const struct rt2x00_ops rt73usb_ops = { .eeprom_size = EEPROM_SIZE, .rf_size = RF_SIZE, .tx_queues = NUM_TX_QUEUES, - .extra_tx_headroom = TXD_DESC_SIZE, .queue_init = rt73usb_queue_init, .lib = &rt73usb_rt2x00_ops, .hw = &rt73usb_mac80211_ops, -- cgit v1.2.3 From 7ce0bad5c4586e4e9fb9d3e90d89101cd06fb999 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Thu, 6 Jun 2013 09:36:20 +0200 Subject: rt2x00: rt2800usb: nuke rt2800usb_ops_5592 It is exactly the same like the generic rt2800usb_ops. Remove the duplicate and use the generic ops for all devices. Signed-off-by: Gabor Juhos Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800usb.c | 29 +++++------------------------ 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c index 68ea00e169d2..7edd903dd749 100644 --- a/drivers/net/wireless/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/rt2x00/rt2800usb.c @@ -914,22 +914,6 @@ static const struct rt2x00_ops rt2800usb_ops = { #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ }; -static const struct rt2x00_ops rt2800usb_ops_5592 = { - .name = KBUILD_MODNAME, - .drv_data_size = sizeof(struct rt2800_drv_data), - .max_ap_intf = 8, - .eeprom_size = EEPROM_SIZE, - .rf_size = RF_SIZE, - .tx_queues = NUM_TX_QUEUES, - .queue_init = rt2800usb_queue_init, - .lib = &rt2800usb_rt2x00_ops, - .drv = &rt2800usb_rt2800_ops, - .hw = &rt2800usb_mac80211_ops, -#ifdef CONFIG_RT2X00_LIB_DEBUGFS - .debugfs = &rt2800_rt2x00debug, -#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ -}; - /* * rt2800usb module information. */ @@ -1242,15 +1226,15 @@ static struct usb_device_id rt2800usb_device_table[] = { #endif #ifdef CONFIG_RT2800USB_RT55XX /* Arcadyan */ - { USB_DEVICE(0x043e, 0x7a32), .driver_info = 5592 }, + { USB_DEVICE(0x043e, 0x7a32) }, /* AVM GmbH */ - { USB_DEVICE(0x057c, 0x8501), .driver_info = 5592 }, + { USB_DEVICE(0x057c, 0x8501) }, /* D-Link DWA-160-B2 */ - { USB_DEVICE(0x2001, 0x3c1a), .driver_info = 5592 }, + { USB_DEVICE(0x2001, 0x3c1a) }, /* Proware */ - { USB_DEVICE(0x043e, 0x7a13), .driver_info = 5592 }, + { USB_DEVICE(0x043e, 0x7a13) }, /* Ralink */ - { USB_DEVICE(0x148f, 0x5572), .driver_info = 5592 }, + { USB_DEVICE(0x148f, 0x5572) }, #endif #ifdef CONFIG_RT2800USB_UNKNOWN /* @@ -1355,9 +1339,6 @@ MODULE_LICENSE("GPL"); static int rt2800usb_probe(struct usb_interface *usb_intf, const struct usb_device_id *id) { - if (id->driver_info == 5592) - return rt2x00usb_probe(usb_intf, &rt2800usb_ops_5592); - return rt2x00usb_probe(usb_intf, &rt2800usb_ops); } -- cgit v1.2.3 From f55d94a600ab5db0df4eccbc15d889ae104d058e Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Thu, 6 Jun 2013 13:17:46 +0200 Subject: brcmfmac: allow firmware-signal tlv to be longer than specified The firmware-signal API specification defines length for the different tlv. During testing on different devices it turned out not all firmware used the tlv length according specification. Therefore the length check is made less strict with this patch. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index 5352dc1fdf3c..d6f05ae85de3 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -1433,7 +1433,7 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, if (data_len < len + 2) break; - if (len != brcmf_fws_get_tlv_len(fws, type)) + if (len < brcmf_fws_get_tlv_len(fws, type)) break; err = BRCMF_FWS_RET_OK_NOSCHEDULE; -- cgit v1.2.3 From 51f6dd9da27359d9218046ed0003f71e05a673c1 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Thu, 6 Jun 2013 13:17:47 +0200 Subject: brcmfmac: remove fifo bitfield from brcmf_skbuff_cb::if_flags The brcmf_skbuff_cb structure contain if_flags and htod fields. Both have a bitfield defined to hold the fifo number. With a small code change we get rid of the fifo bitfield in if_flags. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index d6f05ae85de3..bc2edc04e525 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -195,7 +195,6 @@ struct brcmf_skbuff_cb { * b[9] - packet is a tx packet. * b[8] - packet uses FIFO credit (non-pspoll). * b[7] - interface in AP mode. - * b[6:4] - AC FIFO number. * b[3:0] - interface index. */ #define BRCMF_SKB_IF_FLAGS_REQUESTED_MASK 0x0800 @@ -208,8 +207,6 @@ struct brcmf_skbuff_cb { #define BRCMF_SKB_IF_FLAGS_CREDITCHECK_SHIFT 8 #define BRCMF_SKB_IF_FLAGS_IF_AP_MASK 0x0080 #define BRCMF_SKB_IF_FLAGS_IF_AP_SHIFT 7 -#define BRCMF_SKB_IF_FLAGS_FIFO_MASK 0x0070 -#define BRCMF_SKB_IF_FLAGS_FIFO_SHIFT 4 #define BRCMF_SKB_IF_FLAGS_INDEX_MASK 0x000f #define BRCMF_SKB_IF_FLAGS_INDEX_SHIFT 0 @@ -1608,7 +1605,8 @@ static int brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo, } static void -brcmf_fws_rollback_toq(struct brcmf_fws_info *fws, struct sk_buff *skb) +brcmf_fws_rollback_toq(struct brcmf_fws_info *fws, + struct sk_buff *skb, int fifo) { /* put the packet back to the head of queue @@ -1622,11 +1620,9 @@ brcmf_fws_rollback_toq(struct brcmf_fws_info *fws, struct sk_buff *skb) enum brcmf_fws_skb_state state; struct sk_buff *pktout; int rc = 0; - int fifo; int hslot; u8 ifidx; - fifo = brcmf_skb_if_flags_get_field(skb, FIFO); state = brcmf_skbcb(skb)->state; entry = brcmf_skbcb(skb)->mac; @@ -1794,7 +1790,7 @@ static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo, return rc; rollback: - brcmf_fws_rollback_toq(fws, skb); + brcmf_fws_rollback_toq(fws, skb, fifo); return rc; } @@ -1831,7 +1827,6 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) brcmf_skb_if_flags_set_field(skb, INDEX, ifp->ifidx); if (!multicast) fifo = brcmf_fws_prio2fifo[skb->priority]; - brcmf_skb_if_flags_set_field(skb, FIFO, fifo); brcmf_dbg(TRACE, "ea=%pM, multi=%d, fifo=%d\n", eh->h_dest, multicast, fifo); -- cgit v1.2.3 From df50f756966cc07addaae5449a6fd45a17bdb06c Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Thu, 6 Jun 2013 13:17:48 +0200 Subject: brcmfmac: Take bus flowcontrol at credit mgmt into account. On bus flow control (no more host bus resources to send packets to device) the netif flow control was toggled, however credit management should also take this status into account. Since there are multiple sources handling this flow control necessary spinlocks were added to protect flow control related data/states. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd.h | 2 ++ drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c | 8 ++++++++ drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 21 +++++++++++++++++++-- drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h | 1 + drivers/net/wireless/brcm80211/brcmfmac/usb.c | 8 ++++++++ 6 files changed, 39 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h index 28db9cf39672..86cbfe2c7c6c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h @@ -583,6 +583,7 @@ enum brcmf_netif_stop_reason { * @bssidx: index of bss associated with this interface. * @mac_addr: assigned mac address. * @netif_stop: bitmap indicates reason why netif queues are stopped. + * @netif_stop_lock: spinlock for update netif_stop from multiple sources. * @pend_8021x_cnt: tracks outstanding number of 802.1x frames. * @pend_8021x_wait: used for signalling change in count. */ @@ -598,6 +599,7 @@ struct brcmf_if { s32 bssidx; u8 mac_addr[ETH_ALEN]; u8 netif_stop; + spinlock_t netif_stop_lock; atomic_t pend_8021x_cnt; wait_queue_head_t pend_8021x_wait; }; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index b98f2235978e..df37645f493e 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -240,11 +240,15 @@ done: void brcmf_txflowblock_if(struct brcmf_if *ifp, enum brcmf_netif_stop_reason reason, bool state) { + unsigned long flags; + if (!ifp) return; brcmf_dbg(TRACE, "enter: idx=%d stop=0x%X reason=%d state=%d\n", ifp->bssidx, ifp->netif_stop, reason, state); + + spin_lock_irqsave(&ifp->netif_stop_lock, flags); if (state) { if (!ifp->netif_stop) netif_stop_queue(ifp->ndev); @@ -254,6 +258,7 @@ void brcmf_txflowblock_if(struct brcmf_if *ifp, if (!ifp->netif_stop) netif_wake_queue(ifp->ndev); } + spin_unlock_irqrestore(&ifp->netif_stop_lock, flags); } void brcmf_txflowblock(struct device *dev, bool state) @@ -264,6 +269,8 @@ void brcmf_txflowblock(struct device *dev, bool state) brcmf_dbg(TRACE, "Enter\n"); + brcmf_fws_bus_blocked(drvr, state); + for (i = 0; i < BRCMF_MAX_IFS; i++) brcmf_txflowblock_if(drvr->iflist[i], BRCMF_NETIF_STOP_REASON_BLOCK_BUS, state); @@ -779,6 +786,7 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, ifp->bssidx = bssidx; init_waitqueue_head(&ifp->pend_8021x_wait); + spin_lock_init(&ifp->netif_stop_lock); if (mac_addr != NULL) memcpy(ifp->mac_addr, mac_addr, ETH_ALEN); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index d2487518bd2a..6f3d181659ef 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -2369,12 +2369,12 @@ static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt) } else { ret = 0; } - spin_unlock_bh(&bus->txqlock); if (pktq_len(&bus->txq) >= TXHI) { bus->txoff = true; brcmf_txflowblock(bus->sdiodev->dev, true); } + spin_unlock_bh(&bus->txqlock); #ifdef DEBUG if (pktq_plen(&bus->txq, prec) > qcount[prec]) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index bc2edc04e525..c1930ef5c55f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -431,6 +431,7 @@ struct brcmf_fws_info { u32 fifo_credit_map; u32 fifo_delay_map; unsigned long borrow_defer_timestamp; + bool bus_flow_blocked; }; /* @@ -1833,6 +1834,7 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) brcmf_fws_lock(drvr, flags); if (skcb->mac->suppressed || + fws->bus_flow_blocked || brcmf_fws_mac_desc_closed(fws, skcb->mac, fifo) || brcmu_pktq_mlen(&skcb->mac->psq, 3 << (fifo * 2)) || (!multicast && @@ -1905,7 +1907,8 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker) brcmf_dbg(TRACE, "enter: fws=%p\n", fws); brcmf_fws_lock(fws->drvr, flags); - for (fifo = NL80211_NUM_ACS; fifo >= 0; fifo--) { + for (fifo = NL80211_NUM_ACS; fifo >= 0 && !fws->bus_flow_blocked; + fifo--) { brcmf_dbg(TRACE, "fifo %d credit %d\n", fifo, fws->fifo_credit[fifo]); for (credit = 0; credit < fws->fifo_credit[fifo]; /* nop */) { @@ -1915,9 +1918,12 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker) if (brcmf_skbcb(skb)->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK) credit++; + if (fws->bus_flow_blocked) + break; } if ((fifo == BRCMF_FWS_FIFO_AC_BE) && - (credit == fws->fifo_credit[fifo])) { + (credit == fws->fifo_credit[fifo]) && + (!fws->bus_flow_blocked)) { fws->fifo_credit[fifo] -= credit; while (brcmf_fws_borrow_credit(fws) == 0) { skb = brcmf_fws_deq(fws, fifo); @@ -1929,6 +1935,8 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker) brcmf_fws_return_credits(fws, fifo, 1); break; } + if (fws->bus_flow_blocked) + break; } } else { fws->fifo_credit[fifo] -= credit; @@ -2060,3 +2068,12 @@ void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb) } brcmf_fws_unlock(fws->drvr, flags); } + +void brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked) +{ + struct brcmf_fws_info *fws = drvr->fws; + + fws->bus_flow_blocked = flow_blocked; + if (!flow_blocked) + brcmf_fws_schedule_deq(fws); +} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h index fbe483d23752..9fc860910bd8 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h @@ -29,5 +29,6 @@ void brcmf_fws_reset_interface(struct brcmf_if *ifp); void brcmf_fws_add_interface(struct brcmf_if *ifp); void brcmf_fws_del_interface(struct brcmf_if *ifp); void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb); +void brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked); #endif /* FWSIGNAL_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index 01aed7ad6bec..322cadc51ded 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -82,6 +82,7 @@ struct brcmf_usbdev_info { int tx_high_watermark; int tx_freecount; bool tx_flowblock; + spinlock_t tx_flowblock_lock; struct brcmf_usbreq *tx_reqs; struct brcmf_usbreq *rx_reqs; @@ -411,6 +412,7 @@ static void brcmf_usb_tx_complete(struct urb *urb) { struct brcmf_usbreq *req = (struct brcmf_usbreq *)urb->context; struct brcmf_usbdev_info *devinfo = req->devinfo; + unsigned long flags; brcmf_dbg(USB, "Enter, urb->status=%d, skb=%p\n", urb->status, req->skb); @@ -419,11 +421,13 @@ static void brcmf_usb_tx_complete(struct urb *urb) brcmf_txcomplete(devinfo->dev, req->skb, urb->status == 0); req->skb = NULL; brcmf_usb_enq(devinfo, &devinfo->tx_freeq, req, &devinfo->tx_freecount); + spin_lock_irqsave(&devinfo->tx_flowblock_lock, flags); if (devinfo->tx_freecount > devinfo->tx_high_watermark && devinfo->tx_flowblock) { brcmf_txflowblock(devinfo->dev, false); devinfo->tx_flowblock = false; } + spin_unlock_irqrestore(&devinfo->tx_flowblock_lock, flags); } static void brcmf_usb_rx_complete(struct urb *urb) @@ -568,6 +572,7 @@ static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb) struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); struct brcmf_usbreq *req; int ret; + unsigned long flags; brcmf_dbg(USB, "Enter, skb=%p\n", skb); if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP) { @@ -599,11 +604,13 @@ static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb) goto fail; } + spin_lock_irqsave(&devinfo->tx_flowblock_lock, flags); if (devinfo->tx_freecount < devinfo->tx_low_watermark && !devinfo->tx_flowblock) { brcmf_txflowblock(dev, true); devinfo->tx_flowblock = true; } + spin_unlock_irqrestore(&devinfo->tx_flowblock_lock, flags); return 0; fail: @@ -1164,6 +1171,7 @@ struct brcmf_usbdev *brcmf_usb_attach(struct brcmf_usbdev_info *devinfo, /* Initialize the spinlocks */ spin_lock_init(&devinfo->qlock); + spin_lock_init(&devinfo->tx_flowblock_lock); INIT_LIST_HEAD(&devinfo->rx_freeq); INIT_LIST_HEAD(&devinfo->rx_postq); -- cgit v1.2.3 From fe353b24c385f219aca1e19767119017618deca7 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Thu, 6 Jun 2013 13:17:49 +0200 Subject: brcmfmac: rework credit pickup to assure consistent handling Reworked brcmf_skb_pick_up_credit() so it can be used for both fcmode flavours in the same way. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 30 ++++++++++------------ 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index c1930ef5c55f..a7a02473e7e9 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -1040,24 +1040,21 @@ static void brcmf_fws_schedule_deq(struct brcmf_fws_info *fws) queue_work(fws->fws_wq, &fws->fws_dequeue_work); } -static void brcmf_skb_pick_up_credit(struct brcmf_fws_info *fws, int fifo, +static void brcmf_fws_skb_pickup_credit(struct brcmf_fws_info *fws, int fifo, struct sk_buff *p) { struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(p)->mac; - if (brcmf_skbcb(p)->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK) { - if (fws->fcmode != BRCMF_FWS_FCMODE_IMPLIED_CREDIT) - return; + if (brcmf_skbcb(p)->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK) brcmf_fws_return_credits(fws, fifo, 1); - } else { + else if (!brcmf_skb_if_flags_get_field(p, REQUESTED)) /* * if this packet did not count against FIFO credit, it * must have taken a requested_credit from the destination * entry (for pspoll etc.) */ - if (!brcmf_skb_if_flags_get_field(p, REQUESTED)) - entry->requested_credit++; - } + entry->requested_credit++; + brcmf_fws_schedule_deq(fws); } @@ -1272,7 +1269,9 @@ brcmf_fws_txstatus_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot, /* pick up the implicit credit from this packet */ fifo = brcmf_skb_htod_tag_get_field(skb, FIFO); - brcmf_skb_pick_up_credit(fws, fifo, skb); + if (fws->fcmode == BRCMF_FWS_FCMODE_IMPLIED_CREDIT || + !(brcmf_skbcb(skb)->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK)) + brcmf_fws_skb_pickup_credit(fws, fifo, skb); if (!remove_from_hanger) ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, genbit); @@ -1845,7 +1844,7 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) } else { if (brcmf_fws_commit_skb(fws, fifo, skb)) if (!multicast) - brcmf_skb_pick_up_credit(fws, fifo, skb); + brcmf_fws_skb_pickup_credit(fws, fifo, skb); } brcmf_fws_unlock(drvr, flags); return 0; @@ -2053,18 +2052,15 @@ bool brcmf_fws_fc_active(struct brcmf_fws_info *fws) void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb) { ulong flags; + int fifo; brcmf_fws_lock(fws->drvr, flags); brcmf_fws_txstatus_process(fws, BRCMF_FWS_TXSTATUS_FW_TOSSED, brcmf_skb_htod_tag_get_field(skb, HSLOT), 0); /* the packet never reached firmware so reclaim credit */ - if (fws->fcmode == BRCMF_FWS_FCMODE_EXPLICIT_CREDIT && - brcmf_skbcb(skb)->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK) { - brcmf_fws_return_credits(fws, - brcmf_skb_htod_tag_get_field(skb, - FIFO), - 1); - brcmf_fws_schedule_deq(fws); + if (fws->fcmode == BRCMF_FWS_FCMODE_EXPLICIT_CREDIT) { + fifo = brcmf_skb_htod_tag_get_field(skb, FIFO); + brcmf_fws_skb_pickup_credit(fws, fifo, skb); } brcmf_fws_unlock(fws->drvr, flags); } -- cgit v1.2.3 From 289ec1c71911fe4b1b9bab25ae4a2ab75036b72d Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Thu, 6 Jun 2013 13:17:50 +0200 Subject: brcmfmac: explicitly indicate sk_buff is sent upon request credit Firmware can request the driver for transmit packets using two different signals. Only for one signal a flag was set in the sk_buff control buffer. This patch adds explicit flag for the other signal as well. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 122 +++++++++------------ 1 file changed, 53 insertions(+), 69 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index a7a02473e7e9..d080874a14ab 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -190,13 +190,16 @@ struct brcmf_skbuff_cb { /* * sk_buff control if flags * - * b[11] - packet sent upon firmware request. + * b[12] - packet sent upon credit request. + * b[11] - packet sent upon packet request. * b[10] - packet only contains signalling data. * b[9] - packet is a tx packet. * b[8] - packet uses FIFO credit (non-pspoll). * b[7] - interface in AP mode. * b[3:0] - interface index. */ +#define BRCMF_SKB_IF_FLAGS_REQ_CREDIT_MASK 0x1000 +#define BRCMF_SKB_IF_FLAGS_REQ_CREDIT_SHIFT 12 #define BRCMF_SKB_IF_FLAGS_REQUESTED_MASK 0x0800 #define BRCMF_SKB_IF_FLAGS_REQUESTED_SHIFT 11 #define BRCMF_SKB_IF_FLAGS_SIGNAL_ONLY_MASK 0x0400 @@ -998,6 +1001,37 @@ static int brcmf_fws_request_indicate(struct brcmf_fws_info *fws, u8 type, return BRCMF_FWS_RET_OK_SCHEDULE; } +static int brcmf_fws_macdesc_use_credit(struct brcmf_fws_mac_descriptor *entry, + struct sk_buff *skb) +{ + int use_credit = 1; + + if (entry->state == BRCMF_FWS_STATE_CLOSE) { + if (entry->requested_credit > 0) { + /* + * if the packet was pulled out while destination is in + * closed state but had a non-zero packets requested, + * then this should not count against the FIFO credit. + * That is due to the fact that the firmware will + * most likely hold onto this packet until a suitable + * time later to push it to the appropriate AC FIFO. + */ + entry->requested_credit--; + brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 1); + use_credit = 0; + } else if (entry->requested_packet > 0) { + entry->requested_packet--; + brcmf_skb_if_flags_set_field(skb, REQUESTED, 1); + use_credit = 0; + } + } else { + WARN_ON(entry->requested_credit); + WARN_ON(entry->requested_packet); + } + brcmf_skb_if_flags_set_field(skb, CREDITCHECK, use_credit); + return use_credit; +} + static void brcmf_fws_return_credits(struct brcmf_fws_info *fws, u8 fifo, u8 credits) { @@ -1047,10 +1081,11 @@ static void brcmf_fws_skb_pickup_credit(struct brcmf_fws_info *fws, int fifo, if (brcmf_skbcb(p)->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK) brcmf_fws_return_credits(fws, fifo, 1); - else if (!brcmf_skb_if_flags_get_field(p, REQUESTED)) + else if (brcmf_skb_if_flags_get_field(p, REQ_CREDIT) && + entry->state == BRCMF_FWS_STATE_CLOSE) /* * if this packet did not count against FIFO credit, it - * must have taken a requested_credit from the destination + * could have taken a requested_credit from the destination * entry (for pspoll etc.) */ entry->requested_credit++; @@ -1108,7 +1143,6 @@ static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo) struct brcmf_fws_mac_descriptor *table; struct brcmf_fws_mac_descriptor *entry; struct sk_buff *p; - int use_credit = 1; int num_nodes; int node_pos; int prec_out; @@ -1143,26 +1177,7 @@ static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo) if (p == NULL) continue; - /* did the packet come from suppress sub-queue? */ - if (entry->requested_credit > 0) { - entry->requested_credit--; - /* - * if the packet was pulled out while destination is in - * closed state but had a non-zero packets requested, - * then this should not count against the FIFO credit. - * That is due to the fact that the firmware will - * most likely hold onto this packet until a suitable - * time later to push it to the appropriate AC FIFO. - */ - if (entry->state == BRCMF_FWS_STATE_CLOSE) - use_credit = 0; - } else if (entry->requested_packet > 0) { - entry->requested_packet--; - brcmf_skb_if_flags_set_field(p, REQUESTED, 1); - if (entry->state == BRCMF_FWS_STATE_CLOSE) - use_credit = 0; - } - brcmf_skb_if_flags_set_field(p, CREDITCHECK, use_credit); + brcmf_fws_macdesc_use_credit(entry, p); /* move dequeue position to ensure fair round-robin */ fws->deq_node_pos[fifo] = (node_pos + i + 1) % num_nodes; @@ -1664,13 +1679,6 @@ brcmf_fws_rollback_toq(struct brcmf_fws_info *fws, /* decrement sequence count */ entry->seq[fifo]--; } - /* - if this packet did not count against FIFO credit, it must have - taken a requested_credit from the firmware (for pspoll etc.) - */ - if (!(brcmf_skbcb(skb)->if_flags & - BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK)) - entry->requested_credit++; } else { brcmf_err("no mac entry linked\n"); rc = -ENOENT; @@ -1679,10 +1687,12 @@ brcmf_fws_rollback_toq(struct brcmf_fws_info *fws, fail: if (rc) { - brcmf_txfinalize(fws->drvr, skb, false); + brcmf_fws_bustxfail(fws, skb); fws->stats.rollback_failed++; - } else + } else { fws->stats.rollback_success++; + brcmf_fws_skb_pickup_credit(fws, fifo, skb); + } } static int brcmf_fws_borrow_credit(struct brcmf_fws_info *fws) @@ -1710,30 +1720,10 @@ static int brcmf_fws_consume_credit(struct brcmf_fws_info *fws, int fifo, { struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac; int *credit = &fws->fifo_credit[fifo]; - int use_credit = 1; brcmf_dbg(TRACE, "enter: ac=%d, credits=%d\n", fifo, *credit); - if (entry->requested_credit > 0) { - /* - * if the packet was pulled out while destination is in - * closed state but had a non-zero packets requested, - * then this should not count against the FIFO credit. - * That is due to the fact that the firmware will - * most likely hold onto this packet until a suitable - * time later to push it to the appropriate AC FIFO. - */ - entry->requested_credit--; - if (entry->state == BRCMF_FWS_STATE_CLOSE) - use_credit = 0; - } else if (entry->requested_packet > 0) { - entry->requested_packet--; - brcmf_skb_if_flags_set_field(skb, REQUESTED, 1); - if (entry->state == BRCMF_FWS_STATE_CLOSE) - use_credit = 0; - } - brcmf_skb_if_flags_set_field(skb, CREDITCHECK, use_credit); - if (!use_credit) { + if (!brcmf_fws_macdesc_use_credit(entry, skb)) { brcmf_dbg(TRACE, "exit: no creditcheck set\n"); return 0; } @@ -1842,9 +1832,7 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) drvr->fws->fifo_delay_map |= 1 << fifo; brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_DELAYED, fifo, skb); } else { - if (brcmf_fws_commit_skb(fws, fifo, skb)) - if (!multicast) - brcmf_fws_skb_pickup_credit(fws, fifo, skb); + brcmf_fws_commit_skb(fws, fifo, skb); } brcmf_fws_unlock(drvr, flags); return 0; @@ -1900,7 +1888,6 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker) struct sk_buff *skb; ulong flags; int fifo; - int credit; fws = container_of(worker, struct brcmf_fws_info, fws_dequeue_work); @@ -1910,35 +1897,32 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker) fifo--) { brcmf_dbg(TRACE, "fifo %d credit %d\n", fifo, fws->fifo_credit[fifo]); - for (credit = 0; credit < fws->fifo_credit[fifo]; /* nop */) { + while (fws->fifo_credit[fifo]) { skb = brcmf_fws_deq(fws, fifo); - if (!skb || brcmf_fws_commit_skb(fws, fifo, skb)) + if (!skb) break; if (brcmf_skbcb(skb)->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK) - credit++; + fws->fifo_credit[fifo]--; + + if (brcmf_fws_commit_skb(fws, fifo, skb)) + break; if (fws->bus_flow_blocked) break; } if ((fifo == BRCMF_FWS_FIFO_AC_BE) && - (credit == fws->fifo_credit[fifo]) && + (fws->fifo_credit[fifo] == 0) && (!fws->bus_flow_blocked)) { - fws->fifo_credit[fifo] -= credit; while (brcmf_fws_borrow_credit(fws) == 0) { skb = brcmf_fws_deq(fws, fifo); if (!skb) { brcmf_fws_return_credits(fws, fifo, 1); break; } - if (brcmf_fws_commit_skb(fws, fifo, skb)) { - brcmf_fws_return_credits(fws, fifo, 1); - break; - } + brcmf_fws_commit_skb(fws, fifo, skb); if (fws->bus_flow_blocked) break; } - } else { - fws->fifo_credit[fifo] -= credit; } } brcmf_fws_unlock(fws->drvr, flags); -- cgit v1.2.3 From 8071fd61b421f5c9b9a67a7c222c1f5581bcf0f3 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Thu, 6 Jun 2013 13:17:51 +0200 Subject: brcmfmac: reducing debug logging in firmware-signalling code The debug logging in firmware-signalling code was rather extensive and for a large part in the data path. This patch removes large part or the level is changed to DATA level. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/dhd_linux.c | 4 +- drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 124 +++++++++++---------- 2 files changed, 66 insertions(+), 62 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index df37645f493e..9450d10b0fb9 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -179,7 +179,7 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, struct brcmf_pub *drvr = ifp->drvr; struct ethhdr *eh; - brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx); + brcmf_dbg(DATA, "Enter, idx=%d\n", ifp->bssidx); /* Can the device send data? */ if (drvr->bus_if->state != BRCMF_BUS_DATA) { @@ -287,7 +287,7 @@ void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list) u8 ifidx; int ret; - brcmf_dbg(TRACE, "Enter\n"); + brcmf_dbg(DATA, "Enter\n"); skb_queue_walk_safe(skb_list, skb, pnext) { skb_unlink(skb, skb_list); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index d080874a14ab..73e3e1dc073a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -343,6 +343,7 @@ enum brcmf_fws_mac_desc_state { * @transit_count: packet in transit to firmware. */ struct brcmf_fws_mac_descriptor { + char name[16]; u8 occupied; u8 mac_handle; u8 interface_id; @@ -508,7 +509,6 @@ static void brcmf_fws_hanger_init(struct brcmf_fws_hanger *hanger) { int i; - brcmf_dbg(TRACE, "enter\n"); memset(hanger, 0, sizeof(*hanger)); for (i = 0; i < ARRAY_SIZE(hanger->items); i++) hanger->items[i].state = BRCMF_FWS_HANGER_ITEM_STATE_FREE; @@ -518,7 +518,6 @@ static u32 brcmf_fws_hanger_get_free_slot(struct brcmf_fws_hanger *h) { u32 i; - brcmf_dbg(TRACE, "enter\n"); i = (h->slot_pos + 1) % BRCMF_FWS_HANGER_MAXITEMS; while (i != h->slot_pos) { @@ -534,14 +533,12 @@ static u32 brcmf_fws_hanger_get_free_slot(struct brcmf_fws_hanger *h) h->failed_slotfind++; i = BRCMF_FWS_HANGER_MAXITEMS; done: - brcmf_dbg(TRACE, "exit: %d\n", i); return i; } static int brcmf_fws_hanger_pushpkt(struct brcmf_fws_hanger *h, struct sk_buff *pkt, u32 slot_id) { - brcmf_dbg(TRACE, "enter\n"); if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS) return -ENOENT; @@ -561,7 +558,6 @@ static int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h, u32 slot_id, struct sk_buff **pktout, bool remove_item) { - brcmf_dbg(TRACE, "enter\n"); if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS) return -ENOENT; @@ -584,8 +580,6 @@ static int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h, static int brcmf_fws_hanger_mark_suppressed(struct brcmf_fws_hanger *h, u32 slot_id, u8 gen) { - brcmf_dbg(TRACE, "enter\n"); - if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS) return -ENOENT; @@ -604,7 +598,6 @@ static int brcmf_fws_hanger_get_genbit(struct brcmf_fws_hanger *hanger, struct sk_buff *pkt, u32 slot_id, int *gen) { - brcmf_dbg(TRACE, "enter\n"); *gen = 0xff; if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS) @@ -628,7 +621,6 @@ static void brcmf_fws_hanger_cleanup(struct brcmf_fws_info *fws, int i; enum brcmf_fws_hanger_item_state s; - brcmf_dbg(TRACE, "enter: ifidx=%d\n", ifidx); for (i = 0; i < ARRAY_SIZE(h->items); i++) { s = h->items[i].state; if (s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE || @@ -645,6 +637,19 @@ static void brcmf_fws_hanger_cleanup(struct brcmf_fws_info *fws, } } +static void brcmf_fws_macdesc_set_name(struct brcmf_fws_info *fws, + struct brcmf_fws_mac_descriptor *desc) +{ + if (desc == &fws->desc.other) + strlcpy(desc->name, "MAC-OTHER", sizeof(desc->name)); + else if (desc->mac_handle) + scnprintf(desc->name, sizeof(desc->name), "MAC-%d:%d", + desc->mac_handle, desc->interface_id); + else + scnprintf(desc->name, sizeof(desc->name), "MACIF:%d", + desc->interface_id); +} + static void brcmf_fws_init_mac_descriptor(struct brcmf_fws_mac_descriptor *desc, u8 *addr, u8 ifidx) { @@ -676,7 +681,6 @@ brcmf_fws_mac_descriptor_lookup(struct brcmf_fws_info *fws, u8 *ea) struct brcmf_fws_mac_descriptor *entry; int i; - brcmf_dbg(TRACE, "enter: ea=%pM\n", ea); if (ea == NULL) return ERR_PTR(-EINVAL); @@ -698,8 +702,6 @@ brcmf_fws_find_mac_desc(struct brcmf_fws_info *fws, struct brcmf_if *ifp, bool multicast; enum nl80211_iftype iftype; - brcmf_dbg(TRACE, "enter: idx=%d\n", ifp->bssidx); - multicast = is_multicast_ether_addr(da); iftype = brcmf_cfg80211_get_iftype(ifp); @@ -720,7 +722,6 @@ brcmf_fws_find_mac_desc(struct brcmf_fws_info *fws, struct brcmf_if *ifp, entry = &fws->desc.other; done: - brcmf_dbg(TRACE, "exit: entry=%p\n", entry); return entry; } @@ -753,11 +754,7 @@ static void brcmf_fws_mac_desc_cleanup(struct brcmf_fws_info *fws, struct brcmf_fws_mac_descriptor *entry, int ifidx) { - brcmf_dbg(TRACE, "enter: entry=(ea=%pM, ifid=%d), ifidx=%d\n", - entry->ea, entry->interface_id, ifidx); if (entry->occupied && (ifidx == -1 || ifidx == entry->interface_id)) { - brcmf_dbg(TRACE, "flush psq: ifidx=%d, qlen=%d\n", - ifidx, entry->psq.len); brcmf_fws_psq_flush(fws, &entry->psq, ifidx); entry->occupied = !!(entry->psq.len); } @@ -773,7 +770,6 @@ static void brcmf_fws_bus_txq_cleanup(struct brcmf_fws_info *fws, int prec; u32 hslot; - brcmf_dbg(TRACE, "enter: ifidx=%d\n", ifidx); txq = brcmf_bus_gettxq(fws->drvr->bus_if); if (IS_ERR(txq)) { brcmf_dbg(TRACE, "no txq to clean up\n"); @@ -799,7 +795,6 @@ static void brcmf_fws_cleanup(struct brcmf_fws_info *fws, int ifidx) struct brcmf_fws_mac_descriptor *table; bool (*matchfn)(struct sk_buff *, void *) = NULL; - brcmf_dbg(TRACE, "enter: ifidx=%d\n", ifidx); if (fws == NULL) return; @@ -820,7 +815,6 @@ static void brcmf_fws_tim_update(struct brcmf_fws_info *ctx, struct brcmf_fws_mac_descriptor *entry, int prec) { - brcmf_dbg(TRACE, "enter: ea=%pM\n", entry->ea); if (entry->state == BRCMF_FWS_STATE_CLOSE) { /* check delayedQ and suppressQ in one call using bitmap */ if (brcmu_pktq_mlen(&entry->psq, 3 << (prec * 2)) == 0) @@ -877,8 +871,9 @@ int brcmf_fws_macdesc_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data) entry = &fws->desc.nodes[mac_handle & 0x1F]; if (type == BRCMF_FWS_TYPE_MACDESC_DEL) { - brcmf_dbg(TRACE, "deleting mac %pM idx %d\n", addr, ifidx); if (entry->occupied) { + brcmf_dbg(TRACE, "deleting %s mac %pM\n", + entry->name, addr); brcmf_fws_mac_desc_cleanup(fws, entry, -1); brcmf_fws_clear_mac_descriptor(entry); } else @@ -886,25 +881,28 @@ int brcmf_fws_macdesc_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data) return 0; } - brcmf_dbg(TRACE, - "add mac %pM handle %u idx %d\n", addr, mac_handle, ifidx); existing = brcmf_fws_mac_descriptor_lookup(fws, addr); if (IS_ERR(existing)) { if (!entry->occupied) { entry->mac_handle = mac_handle; brcmf_fws_init_mac_descriptor(entry, addr, ifidx); + brcmf_fws_macdesc_set_name(fws, entry); brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT, BRCMF_FWS_PSQ_LEN); + brcmf_dbg(TRACE, "add %s mac %pM\n", entry->name, addr); } else { fws->stats.mac_update_failed++; } } else { if (entry != existing) { - brcmf_dbg(TRACE, "relocate mac\n"); + brcmf_dbg(TRACE, "copy mac %s\n", existing->name); memcpy(entry, existing, offsetof(struct brcmf_fws_mac_descriptor, psq)); entry->mac_handle = mac_handle; brcmf_fws_clear_mac_descriptor(existing); + brcmf_fws_macdesc_set_name(fws, entry); + brcmf_dbg(TRACE, "relocate %s mac %pM\n", entry->name, + addr); } else { brcmf_dbg(TRACE, "use existing\n"); WARN_ON(entry->mac_handle != mac_handle); @@ -928,6 +926,8 @@ static int brcmf_fws_macdesc_state_indicate(struct brcmf_fws_info *fws, return -ESRCH; } + brcmf_dbg(TRACE, "%s (%d): %s\n", brcmf_fws_get_tlv_name(type), type, + entry->name); /* a state update should wipe old credits? */ entry->requested_credit = 0; if (type == BRCMF_FWS_TYPE_MAC_OPEN) { @@ -950,7 +950,6 @@ static int brcmf_fws_interface_state_indicate(struct brcmf_fws_info *fws, ifidx = data[0]; - brcmf_dbg(TRACE, "enter: ifidx=%d\n", ifidx); if (ifidx >= BRCMF_MAX_IFS) { ret = -ERANGE; goto fail; @@ -962,6 +961,8 @@ static int brcmf_fws_interface_state_indicate(struct brcmf_fws_info *fws, goto fail; } + brcmf_dbg(TRACE, "%s (%d): %s\n", brcmf_fws_get_tlv_name(type), type, + entry->name); switch (type) { case BRCMF_FWS_TYPE_INTERFACE_OPEN: entry->state = BRCMF_FWS_STATE_OPEN; @@ -992,6 +993,9 @@ static int brcmf_fws_request_indicate(struct brcmf_fws_info *fws, u8 type, return -ESRCH; } + brcmf_dbg(TRACE, "%s (%d): %s cnt %d bmp %d\n", + brcmf_fws_get_tlv_name(type), type, entry->name, + data[0], data[2]); if (type == BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT) entry->requested_credit = data[0]; else @@ -1108,7 +1112,7 @@ static int brcmf_fws_enq(struct brcmf_fws_info *fws, return -ENOENT; } - brcmf_dbg(TRACE, "enter: ea=%pM, qlen=%d\n", entry->ea, entry->psq.len); + brcmf_dbg(DATA, "enter: fifo %d skb %p\n", fifo, p); if (state == BRCMF_FWS_SKBSTATE_SUPPRESSED) { prec += 1; qfull_stat = &fws->stats.supprq_full_error; @@ -1202,7 +1206,7 @@ static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo) } p = NULL; done: - brcmf_dbg(TRACE, "exit: fifo %d skb %p\n", fifo, p); + brcmf_dbg(DATA, "exit: fifo %d skb %p\n", fifo, p); return p; } @@ -1221,6 +1225,9 @@ static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo, entry->suppress_count = brcmu_pktq_mlen(&entry->psq, 1 << (fifo * 2 + 1)); entry->suppr_transit_count = entry->transit_count; + brcmf_dbg(DATA, "suppress %s: supp_cnt %d transit %d\n", + entry->name, entry->suppress_count, + entry->transit_count); } entry->generation = genbit; @@ -1244,17 +1251,17 @@ static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo, } static int -brcmf_fws_txstatus_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot, +brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot, u32 genbit) { u32 fifo; int ret; bool remove_from_hanger = true; struct sk_buff *skb; + struct brcmf_skbuff_cb *skcb; struct brcmf_fws_mac_descriptor *entry = NULL; - brcmf_dbg(TRACE, "status: flags=0x%X, hslot=%d\n", - flags, hslot); + brcmf_dbg(DATA, "flags %d\n", flags); if (flags == BRCMF_FWS_TXSTATUS_DISCARD) fws->stats.txs_discard++; @@ -1276,12 +1283,16 @@ brcmf_fws_txstatus_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot, return ret; } - entry = brcmf_skbcb(skb)->mac; + skcb = brcmf_skbcb(skb); + entry = skcb->mac; if (WARN_ON(!entry)) { brcmu_pkt_buf_free_skb(skb); return -EINVAL; } + brcmf_dbg(DATA, "%s flags %X htod %X\n", entry->name, skcb->if_flags, + skcb->htod); + /* pick up the implicit credit from this packet */ fifo = brcmf_skb_htod_tag_get_field(skb, FIFO); if (fws->fcmode == BRCMF_FWS_FCMODE_IMPLIED_CREDIT || @@ -1311,11 +1322,11 @@ static int brcmf_fws_fifocreditback_indicate(struct brcmf_fws_info *fws, return BRCMF_FWS_RET_OK_NOSCHEDULE; } - brcmf_dbg(TRACE, "enter: data %pM\n", data); + brcmf_dbg(DATA, "enter: data %pM\n", data); for (i = 0; i < BRCMF_FWS_FIFO_COUNT; i++) brcmf_fws_return_credits(fws, i, data[i]); - brcmf_dbg(INFO, "map: credit %x delay %x\n", fws->fifo_credit_map, + brcmf_dbg(DATA, "map: credit %x delay %x\n", fws->fifo_credit_map, fws->fifo_delay_map); return BRCMF_FWS_RET_OK_SCHEDULE; } @@ -1335,7 +1346,7 @@ static int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 *data) hslot = brcmf_txstatus_get_field(status, HSLOT); genbit = brcmf_txstatus_get_field(status, GENERATION); - return brcmf_fws_txstatus_process(fws, flags, hslot, genbit); + return brcmf_fws_txs_process(fws, flags, hslot, genbit); } static int brcmf_fws_dbg_seqnum_check(struct brcmf_fws_info *fws, u8 *data) @@ -1343,7 +1354,7 @@ static int brcmf_fws_dbg_seqnum_check(struct brcmf_fws_info *fws, u8 *data) __le32 timestamp; memcpy(×tamp, &data[2], sizeof(timestamp)); - brcmf_dbg(INFO, "received: seq %d, timestamp %d\n", data[1], + brcmf_dbg(CTL, "received: seq %d, timestamp %d\n", data[1], le32_to_cpu(timestamp)); return 0; } @@ -1404,7 +1415,7 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, s32 status; s32 err; - brcmf_dbg(TRACE, "enter: ifidx %d, skblen %u, sig %d\n", + brcmf_dbg(HDRS, "enter: ifidx %d, skblen %u, sig %d\n", ifidx, skb->len, signal_len); WARN_ON(signal_len > skb->len); @@ -1438,8 +1449,9 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, len = signal_data[1]; data = signal_data + 2; - brcmf_dbg(INFO, "tlv type=%d (%s), len=%d, data[0]=%d\n", type, - brcmf_fws_get_tlv_name(type), len, *data); + brcmf_dbg(HDRS, "tlv type=%s (%d), len=%d (%d)\n", + brcmf_fws_get_tlv_name(type), type, len, + brcmf_fws_get_tlv_len(fws, type)); /* abort parsing when length invalid */ if (data_len < len + 2) @@ -1522,8 +1534,6 @@ static int brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb) u8 fillers; __le32 pkttag = cpu_to_le32(brcmf_skbcb(skb)->htod); - brcmf_dbg(TRACE, "enter: ea=%pM, ifidx=%u, pkttag=0x%08X\n", - entry->ea, entry->interface_id, le32_to_cpu(pkttag)); if (entry->send_tim_signal) data_offset += 2 + BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN; @@ -1708,7 +1718,7 @@ static int brcmf_fws_borrow_credit(struct brcmf_fws_info *fws) fws->fifo_credit[lender_ac]--; if (fws->fifo_credit[lender_ac] == 0) fws->fifo_credit_map &= ~(1 << lender_ac); - brcmf_dbg(TRACE, "borrow credit from: %d\n", lender_ac); + brcmf_dbg(DATA, "borrow credit from: %d\n", lender_ac); return 0; } } @@ -1721,12 +1731,8 @@ static int brcmf_fws_consume_credit(struct brcmf_fws_info *fws, int fifo, struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac; int *credit = &fws->fifo_credit[fifo]; - brcmf_dbg(TRACE, "enter: ac=%d, credits=%d\n", fifo, *credit); - - if (!brcmf_fws_macdesc_use_credit(entry, skb)) { - brcmf_dbg(TRACE, "exit: no creditcheck set\n"); + if (!brcmf_fws_macdesc_use_credit(entry, skb)) return 0; - } if (fifo != BRCMF_FWS_FIFO_AC_BE) fws->borrow_defer_timestamp = jiffies + @@ -1738,13 +1744,13 @@ static int brcmf_fws_consume_credit(struct brcmf_fws_info *fws, int fifo, brcmf_fws_borrow_credit(fws) == 0) return 0; - brcmf_dbg(TRACE, "exit: ac=%d, credits depleted\n", fifo); + brcmf_dbg(DATA, "exit: ac=%d, credits depleted\n", fifo); return -ENAVAIL; } (*credit)--; if (!(*credit)) fws->fifo_credit_map &= ~(1 << fifo); - brcmf_dbg(TRACE, "exit: ac=%d, credits=%d\n", fifo, *credit); + brcmf_dbg(DATA, "exit: ac=%d, credits=%d\n", fifo, *credit); return 0; } @@ -1766,6 +1772,8 @@ static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo, goto rollback; } + brcmf_dbg(DATA, "%s flags %X htod %X\n", entry->name, skcb->if_flags, + skcb->htod); rc = brcmf_bus_txdata(bus, skb); if (rc < 0) goto rollback; @@ -1818,8 +1826,8 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) if (!multicast) fifo = brcmf_fws_prio2fifo[skb->priority]; - brcmf_dbg(TRACE, "ea=%pM, multi=%d, fifo=%d\n", eh->h_dest, - multicast, fifo); + brcmf_dbg(DATA, "%s mac %pM multi %d fifo %d\n", skcb->mac->name, + eh->h_dest, multicast, fifo); brcmf_fws_lock(drvr, flags); if (skcb->mac->suppressed || @@ -1854,16 +1862,16 @@ void brcmf_fws_add_interface(struct brcmf_if *ifp) struct brcmf_fws_info *fws = ifp->drvr->fws; struct brcmf_fws_mac_descriptor *entry; - brcmf_dbg(TRACE, "enter: idx=%d, mac=%pM\n", - ifp->bssidx, ifp->mac_addr); if (!ifp->ndev || !ifp->drvr->fw_signals) return; entry = &fws->desc.iface[ifp->ifidx]; ifp->fws_desc = entry; brcmf_fws_init_mac_descriptor(entry, ifp->mac_addr, ifp->ifidx); + brcmf_fws_macdesc_set_name(fws, entry); brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT, BRCMF_FWS_PSQ_LEN); + brcmf_dbg(TRACE, "added %s\n", entry->name); } void brcmf_fws_del_interface(struct brcmf_if *ifp) @@ -1871,12 +1879,12 @@ void brcmf_fws_del_interface(struct brcmf_if *ifp) struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc; ulong flags; - brcmf_dbg(TRACE, "enter: idx=%d\n", ifp->bssidx); if (!entry) return; brcmf_fws_lock(ifp->drvr, flags); ifp->fws_desc = NULL; + brcmf_dbg(TRACE, "deleting %s\n", entry->name); brcmf_fws_clear_mac_descriptor(entry); brcmf_fws_cleanup(ifp->drvr->fws, ifp->ifidx); brcmf_fws_unlock(ifp->drvr, flags); @@ -1891,12 +1899,9 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker) fws = container_of(worker, struct brcmf_fws_info, fws_dequeue_work); - brcmf_dbg(TRACE, "enter: fws=%p\n", fws); brcmf_fws_lock(fws->drvr, flags); for (fifo = NL80211_NUM_ACS; fifo >= 0 && !fws->bus_flow_blocked; fifo--) { - brcmf_dbg(TRACE, "fifo %d credit %d\n", fifo, - fws->fifo_credit[fifo]); while (fws->fifo_credit[fifo]) { skb = brcmf_fws_deq(fws, fifo); if (!skb) @@ -1980,14 +1985,14 @@ int brcmf_fws_init(struct brcmf_pub *drvr) brcmf_fws_hanger_init(&drvr->fws->hanger); brcmf_fws_init_mac_descriptor(&drvr->fws->desc.other, NULL, 0); + brcmf_fws_macdesc_set_name(drvr->fws, &drvr->fws->desc.other); brcmu_pktq_init(&drvr->fws->desc.other.psq, BRCMF_FWS_PSQ_PREC_COUNT, BRCMF_FWS_PSQ_LEN); /* create debugfs file for statistics */ brcmf_debugfs_create_fws_stats(drvr, &drvr->fws->stats); - /* TODO: remove upon feature delivery */ - brcmf_err("%s bdcv2 tlv signaling [%x]\n", + brcmf_dbg(INFO, "%s bdcv2 tlv signaling [%x]\n", drvr->fw_signals ? "enabled" : "disabled", tlv); return 0; @@ -2029,7 +2034,6 @@ bool brcmf_fws_fc_active(struct brcmf_fws_info *fws) if (!fws) return false; - brcmf_dbg(TRACE, "enter: mode=%d\n", fws->fcmode); return fws->fcmode != BRCMF_FWS_FCMODE_NONE; } @@ -2039,7 +2043,7 @@ void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb) int fifo; brcmf_fws_lock(fws->drvr, flags); - brcmf_fws_txstatus_process(fws, BRCMF_FWS_TXSTATUS_FW_TOSSED, + brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_FW_TOSSED, brcmf_skb_htod_tag_get_field(skb, HSLOT), 0); /* the packet never reached firmware so reclaim credit */ if (fws->fcmode == BRCMF_FWS_FCMODE_EXPLICIT_CREDIT) { -- cgit v1.2.3 From ce17194ead199bec9f4721bdcffa624d7564845d Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Thu, 6 Jun 2013 13:17:52 +0200 Subject: brcmfmac: On bus flow control use fw signalling or netif. Currently on a bus flow control both fws is informed and netif queue gets closed. In case of fw signalling enabled, let the flow control be handled by fw signalling only. Reviewed-by: Arend Van Spriel Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index 9450d10b0fb9..63cadf63ccfe 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -269,11 +269,14 @@ void brcmf_txflowblock(struct device *dev, bool state) brcmf_dbg(TRACE, "Enter\n"); - brcmf_fws_bus_blocked(drvr, state); - - for (i = 0; i < BRCMF_MAX_IFS; i++) - brcmf_txflowblock_if(drvr->iflist[i], - BRCMF_NETIF_STOP_REASON_BLOCK_BUS, state); + if (brcmf_fws_fc_active(drvr->fws)) { + brcmf_fws_bus_blocked(drvr, state); + } else { + for (i = 0; i < BRCMF_MAX_IFS; i++) + brcmf_txflowblock_if(drvr->iflist[i], + BRCMF_NETIF_STOP_REASON_BLOCK_BUS, + state); + } } void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list) -- cgit v1.2.3 From be4910adf1817d357bfb20d28e0f8b6b0fecd945 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Thu, 6 Jun 2013 13:17:53 +0200 Subject: brcmfmac: For FW signalling it is necessary to track gen bit. Store gen bit on suppressed packet per entry and use latest stored version for each packet which gets transmitted to fw. Reviewed-by: Arend Van Spriel Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 92 +++++++--------------- 1 file changed, 29 insertions(+), 63 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index 73e3e1dc073a..682ac62459ea 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -246,7 +246,7 @@ struct brcmf_skbuff_cb { #define BRCMF_SKB_HTOD_TAG_HSLOT_MASK 0x00ffff00 #define BRCMF_SKB_HTOD_TAG_HSLOT_SHIFT 8 #define BRCMF_SKB_HTOD_TAG_FREERUN_MASK 0x000000ff -#define BRCMF_SKB_HTOD_TAG_FREERUN_SHIFT 0 +#define BRCMF_SKB_HTOD_TAG_FREERUN_SHIFT 0 #define brcmf_skb_htod_tag_set_field(skb, field, value) \ brcmu_maskset32(&(brcmf_skbcb(skb)->htod), \ @@ -384,12 +384,10 @@ enum brcmf_fws_hanger_item_state { * struct brcmf_fws_hanger_item - single entry for tx pending packet. * * @state: entry is either free or occupied. - * @gen: generation. * @pkt: packet itself. */ struct brcmf_fws_hanger_item { enum brcmf_fws_hanger_item_state state; - u8 gen; struct sk_buff *pkt; }; @@ -537,7 +535,7 @@ done: } static int brcmf_fws_hanger_pushpkt(struct brcmf_fws_hanger *h, - struct sk_buff *pkt, u32 slot_id) + struct sk_buff *pkt, u32 slot_id) { if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS) return -ENOENT; @@ -571,20 +569,17 @@ static int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h, if (remove_item) { h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_FREE; h->items[slot_id].pkt = NULL; - h->items[slot_id].gen = 0xff; h->popped++; } return 0; } static int brcmf_fws_hanger_mark_suppressed(struct brcmf_fws_hanger *h, - u32 slot_id, u8 gen) + u32 slot_id) { if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS) return -ENOENT; - h->items[slot_id].gen = gen; - if (h->items[slot_id].state != BRCMF_FWS_HANGER_ITEM_STATE_INUSE) { brcmf_err("entry not in use\n"); return -EINVAL; @@ -594,24 +589,6 @@ static int brcmf_fws_hanger_mark_suppressed(struct brcmf_fws_hanger *h, return 0; } -static int brcmf_fws_hanger_get_genbit(struct brcmf_fws_hanger *hanger, - struct sk_buff *pkt, u32 slot_id, - int *gen) -{ - *gen = 0xff; - - if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS) - return -ENOENT; - - if (hanger->items[slot_id].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) { - brcmf_err("slot not in use\n"); - return -EINVAL; - } - - *gen = hanger->items[slot_id].gen; - return 0; -} - static void brcmf_fws_hanger_cleanup(struct brcmf_fws_info *fws, bool (*fn)(struct sk_buff *, void *), int ifidx) @@ -838,9 +815,6 @@ brcmf_fws_flow_control_check(struct brcmf_fws_info *fws, struct pktq *pq, if (WARN_ON(!ifp)) return; - brcmf_dbg(TRACE, - "enter: bssidx=%d, ifidx=%d\n", ifp->bssidx, ifp->ifidx); - if ((ifp->netif_stop & BRCMF_NETIF_STOP_REASON_FWS_FC) && pq->len <= BRCMF_FWS_FLOWCONTROL_LOWATER) brcmf_txflowblock_if(ifp, @@ -1220,7 +1194,7 @@ static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo, hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); /* this packet was suppressed */ - if (!entry->suppressed || entry->generation != genbit) { + if (!entry->suppressed) { entry->suppressed = true; entry->suppress_count = brcmu_pktq_mlen(&entry->psq, 1 << (fifo * 2 + 1)); @@ -1242,8 +1216,7 @@ static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo, * Mark suppressed to avoid a double free during * wlfc cleanup */ - brcmf_fws_hanger_mark_suppressed(&fws->hanger, hslot, - genbit); + brcmf_fws_hanger_mark_suppressed(&fws->hanger, hslot); entry->suppress_count++; } @@ -1573,15 +1546,34 @@ static int brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo, struct brcmf_skbuff_cb *skcb = brcmf_skbcb(p); struct brcmf_fws_mac_descriptor *entry = skcb->mac; int rc = 0; - bool header_needed; + bool first_time; int hslot = BRCMF_FWS_HANGER_MAXITEMS; u8 free_ctr; u8 ifidx; u8 flags; - header_needed = skcb->state != BRCMF_FWS_SKBSTATE_SUPPRESSED; + first_time = skcb->state != BRCMF_FWS_SKBSTATE_SUPPRESSED; - if (header_needed) { + if (!first_time) { + rc = brcmf_proto_hdrpull(fws->drvr, false, &ifidx, p); + if (rc) { + brcmf_err("hdrpull failed\n"); + return rc; + } + } + brcmf_skb_if_flags_set_field(p, TRANSMIT, 1); + brcmf_skb_htod_tag_set_field(p, FIFO, fifo); + brcmf_skb_htod_tag_set_field(p, GENERATION, entry->generation); + flags = BRCMF_FWS_HTOD_FLAG_PKTFROMHOST; + if (!(skcb->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK)) { + /* + * Indicate that this packet is being sent in response to an + * explicit request from the firmware side. + */ + flags |= BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED; + } + brcmf_skb_htod_tag_set_field(p, FLAGS, flags); + if (first_time) { /* obtaining free slot may fail, but that will be caught * by the hanger push. This assures the packet has a BDC * header upon return. @@ -1590,40 +1582,14 @@ static int brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo, free_ctr = entry->seq[fifo]; brcmf_skb_htod_tag_set_field(p, HSLOT, hslot); brcmf_skb_htod_tag_set_field(p, FREERUN, free_ctr); - brcmf_skb_htod_tag_set_field(p, GENERATION, 1); entry->transit_count++; } - brcmf_skb_if_flags_set_field(p, TRANSMIT, 1); - brcmf_skb_htod_tag_set_field(p, FIFO, fifo); - flags = BRCMF_FWS_HTOD_FLAG_PKTFROMHOST; - if (!(skcb->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK)) { - /* - Indicate that this packet is being sent in response to an - explicit request from the firmware side. - */ - flags |= BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED; - } - brcmf_skb_htod_tag_set_field(p, FLAGS, flags); - if (header_needed) { - brcmf_fws_hdrpush(fws, p); + brcmf_fws_hdrpush(fws, p); + if (first_time) { rc = brcmf_fws_hanger_pushpkt(&fws->hanger, p, hslot); if (rc) brcmf_err("hanger push failed: rc=%d\n", rc); - } else { - int gen; - - /* remove old header */ - rc = brcmf_proto_hdrpull(fws->drvr, false, &ifidx, p); - if (rc == 0) { - hslot = brcmf_skb_htod_tag_get_field(p, HSLOT); - brcmf_fws_hanger_get_genbit(&fws->hanger, p, - hslot, &gen); - brcmf_skb_htod_tag_set_field(p, GENERATION, gen); - - /* push new header */ - brcmf_fws_hdrpush(fws, p); - } } return rc; -- cgit v1.2.3 From afc3bbfcd62efe87041571369b2d12beb00f4bc3 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Thu, 6 Jun 2013 13:17:54 +0200 Subject: brcmfmac: Correct creditmap when credit borrowing is active. When credit borrowing is active the BE credits have been depleted, however the worker should still be scheduled. In case of credit borrowing correct credit map to make sure worker remains active. Reviewed-by: Arend Van Spriel Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index 682ac62459ea..abba7f78a6fe 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -1020,6 +1020,8 @@ static void brcmf_fws_return_credits(struct brcmf_fws_info *fws, if (!credits) return; + fws->fifo_credit_map |= 1 << fifo; + if ((fifo == BRCMF_FWS_FIFO_AC_BE) && (fws->credits_borrowed[0])) { for (lender_ac = BRCMF_FWS_FIFO_AC_VO; lender_ac >= 0; @@ -1041,7 +1043,6 @@ static void brcmf_fws_return_credits(struct brcmf_fws_info *fws, } } - fws->fifo_credit_map |= 1 << fifo; fws->fifo_credit[fifo] += credits; } @@ -1675,8 +1676,10 @@ static int brcmf_fws_borrow_credit(struct brcmf_fws_info *fws) { int lender_ac; - if (time_after(fws->borrow_defer_timestamp, jiffies)) + if (time_after(fws->borrow_defer_timestamp, jiffies)) { + fws->fifo_credit_map &= ~(1 << BRCMF_FWS_FIFO_AC_BE); return -ENAVAIL; + } for (lender_ac = 0; lender_ac <= BRCMF_FWS_FIFO_AC_VO; lender_ac++) { if (fws->fifo_credit[lender_ac]) { @@ -1684,10 +1687,12 @@ static int brcmf_fws_borrow_credit(struct brcmf_fws_info *fws) fws->fifo_credit[lender_ac]--; if (fws->fifo_credit[lender_ac] == 0) fws->fifo_credit_map &= ~(1 << lender_ac); + fws->fifo_credit_map |= (1 << BRCMF_FWS_FIFO_AC_BE); brcmf_dbg(DATA, "borrow credit from: %d\n", lender_ac); return 0; } } + fws->fifo_credit_map &= ~(1 << BRCMF_FWS_FIFO_AC_BE); return -ENAVAIL; } -- cgit v1.2.3 From 5cd51c2bad56625e4447739426845cfa37fc11a5 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Thu, 6 Jun 2013 13:17:56 +0200 Subject: brcmfmac: Find correct MAC descriptor in case of TDLS. In case of TDLS find the correct MAC descriptor for fw signalling data. In case of TDLS each destination gets its own entry. This was not handled correctly for P2P client. Reviewed-by: Arend Van Spriel Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index abba7f78a6fe..876ea423048d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -677,26 +677,21 @@ brcmf_fws_find_mac_desc(struct brcmf_fws_info *fws, struct brcmf_if *ifp, { struct brcmf_fws_mac_descriptor *entry = &fws->desc.other; bool multicast; - enum nl80211_iftype iftype; multicast = is_multicast_ether_addr(da); - iftype = brcmf_cfg80211_get_iftype(ifp); - /* Multicast destination and P2P clients get the interface entry. - * STA gets the interface entry if there is no exact match. For - * example, TDLS destinations have their own entry. + /* Multicast destination, STA and P2P clients get the interface entry. + * STA/GC gets the Mac Entry for TDLS destinations, TDLS destinations + * have their own entry. */ - entry = NULL; - if ((multicast || iftype == NL80211_IFTYPE_STATION || - iftype == NL80211_IFTYPE_P2P_CLIENT) && ifp->fws_desc) + if (multicast && ifp->fws_desc) { entry = ifp->fws_desc; - - if (entry != NULL && iftype != NL80211_IFTYPE_STATION) goto done; + } entry = brcmf_fws_mac_descriptor_lookup(fws, da); if (IS_ERR(entry)) - entry = &fws->desc.other; + entry = ifp->fws_desc; done: return entry; -- cgit v1.2.3 From 402e3ba20285f0ebe4f4bebdecb7ab24999f3626 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Thu, 6 Jun 2013 13:17:57 +0200 Subject: brcmfmac: fix invalid ifp lookup in firmware-signalling The destination entries for firmware-signalled flow control have the interface id stored. This needs to be translated to bsscfg index when looking up the ifp object for the interface. Reviewed-by: Arend Van Spriel Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index 876ea423048d..5f3874288d6b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -805,7 +805,7 @@ static void brcmf_fws_flow_control_check(struct brcmf_fws_info *fws, struct pktq *pq, u8 if_id) { - struct brcmf_if *ifp = fws->drvr->iflist[if_id]; + struct brcmf_if *ifp = fws->drvr->iflist[!if_id ? 0 : if_id + 1]; if (WARN_ON(!ifp)) return; -- cgit v1.2.3 From 0d24b0eade7d4b6a5d06fe02645449b1fd0f8e17 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Thu, 6 Jun 2013 13:17:58 +0200 Subject: brcmfmac: Accept only first creditmap event. During P2P testing it turned out that the firmware sents multiple multiple creditmap event messages. Only the first message from the firmware should be processed. Otherwise the firmware-signalled flow control can run haywire when it has packets outstanding in firmware. Reviewed-by: Arend Van Spriel Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index 5f3874288d6b..bba4ff04ee19 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -434,6 +434,7 @@ struct brcmf_fws_info { u32 fifo_delay_map; unsigned long borrow_defer_timestamp; bool bus_flow_blocked; + bool creditmap_received; }; /* @@ -1356,6 +1357,10 @@ static int brcmf_fws_notify_credit_map(struct brcmf_if *ifp, brcmf_err("event payload too small (%d)\n", e->datalen); return -EINVAL; } + if (fws->creditmap_received) + return 0; + + fws->creditmap_received = true; brcmf_dbg(TRACE, "enter: credits %pM\n", credits); brcmf_fws_lock(ifp->drvr, flags); -- cgit v1.2.3 From 2747e5f7f83d215cbc9bdb66f69411ff3dedeeee Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Thu, 6 Jun 2013 13:17:59 +0200 Subject: brcmfmac: Signalling header push and pull on logic places. Currently suppressed packets get enque-ed with header which then gets pulled before transmit. It is more logical and clean to pull the header on return and push it unconditionally on xmit. Reviewed-by: Arend Van Spriel Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 42 +++++++--------------- 1 file changed, 13 insertions(+), 29 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index bba4ff04ee19..6255312d5986 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -1187,6 +1187,7 @@ static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo, struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac; u32 hslot; int ret; + u8 ifidx; hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); @@ -1203,9 +1204,12 @@ static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo, entry->generation = genbit; - ret = brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_SUPPRESSED, fifo, skb); + ret = brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb); + if (ret == 0) + ret = brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_SUPPRESSED, fifo, + skb); if (ret != 0) { - /* suppress q is full, drop this packet */ + /* suppress q is full or hdrpull failed, drop this packet */ brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb, true); } else { @@ -1550,18 +1554,10 @@ static int brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo, bool first_time; int hslot = BRCMF_FWS_HANGER_MAXITEMS; u8 free_ctr; - u8 ifidx; u8 flags; first_time = skcb->state != BRCMF_FWS_SKBSTATE_SUPPRESSED; - if (!first_time) { - rc = brcmf_proto_hdrpull(fws->drvr, false, &ifidx, p); - if (rc) { - brcmf_err("hdrpull failed\n"); - return rc; - } - } brcmf_skb_if_flags_set_field(p, TRANSMIT, 1); brcmf_skb_htod_tag_set_field(p, FIFO, fifo); brcmf_skb_htod_tag_set_field(p, GENERATION, entry->generation); @@ -1584,15 +1580,14 @@ static int brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo, brcmf_skb_htod_tag_set_field(p, HSLOT, hslot); brcmf_skb_htod_tag_set_field(p, FREERUN, free_ctr); entry->transit_count++; - } - - brcmf_fws_hdrpush(fws, p); - if (first_time) { rc = brcmf_fws_hanger_pushpkt(&fws->hanger, p, hslot); if (rc) brcmf_err("hanger push failed: rc=%d\n", rc); } + if (rc == 0) + brcmf_fws_hdrpush(fws, p); + return rc; } @@ -1613,7 +1608,6 @@ brcmf_fws_rollback_toq(struct brcmf_fws_info *fws, struct sk_buff *pktout; int rc = 0; int hslot; - u8 ifidx; state = brcmf_skbcb(skb)->state; entry = brcmf_skbcb(skb)->mac; @@ -1630,17 +1624,6 @@ brcmf_fws_rollback_toq(struct brcmf_fws_info *fws, } else { hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); - /* remove header first */ - rc = brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb); - if (rc) { - brcmf_err("header removal failed\n"); - /* free the hanger slot */ - brcmf_fws_hanger_poppkt(&fws->hanger, hslot, - &pktout, true); - rc = -EINVAL; - goto fail; - } - /* delay-q packets are going to delay-q */ pktout = brcmu_pktq_penq_head(&entry->psq, 2 * fifo, skb); @@ -1661,8 +1644,6 @@ brcmf_fws_rollback_toq(struct brcmf_fws_info *fws, rc = -ENOENT; } - -fail: if (rc) { brcmf_fws_bustxfail(fws, skb); fws->stats.rollback_failed++; @@ -1732,6 +1713,7 @@ static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo, struct brcmf_fws_mac_descriptor *entry; struct brcmf_bus *bus = fws->drvr->bus_if; int rc; + u8 ifidx; entry = skcb->mac; if (IS_ERR(entry)) @@ -1746,8 +1728,10 @@ static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo, brcmf_dbg(DATA, "%s flags %X htod %X\n", entry->name, skcb->if_flags, skcb->htod); rc = brcmf_bus_txdata(bus, skb); - if (rc < 0) + if (rc < 0) { + brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb); goto rollback; + } entry->seq[fifo]++; fws->stats.pkt2bus++; -- cgit v1.2.3 From eb2410cdd92e5232e6b7e8d95cb60b9e0cea434d Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Thu, 6 Jun 2013 13:18:00 +0200 Subject: brcmfmac: Fix endless loop when brcmf_fws_commit_skb fails. Reviewed-by: Arend Van Spriel Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index 6255312d5986..881c0b2f7412 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -1879,7 +1879,8 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker) brcmf_fws_return_credits(fws, fifo, 1); break; } - brcmf_fws_commit_skb(fws, fifo, skb); + if (brcmf_fws_commit_skb(fws, fifo, skb)) + break; if (fws->bus_flow_blocked) break; } -- cgit v1.2.3 From 8c5140f63a7aa01e6836fc9eace201b0a3f1475f Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Thu, 6 Jun 2013 13:18:01 +0200 Subject: brcmfmac: Simplify counting transit count. Reviewed-by: Arend Van Spriel Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 31 +++++++++------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index 881c0b2f7412..dc40eec8a5cc 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -357,7 +357,6 @@ struct brcmf_fws_mac_descriptor { u8 seq[BRCMF_FWS_FIFO_COUNT]; struct pktq psq; int transit_count; - int suppress_count; int suppr_transit_count; bool send_tim_signal; u8 traffic_pending_bmp; @@ -1100,8 +1099,6 @@ static int brcmf_fws_enq(struct brcmf_fws_info *fws, /* update the sk_buff state */ brcmf_skbcb(p)->state = state; - if (state == BRCMF_FWS_SKBSTATE_SUPPRESSED) - entry->suppress_count++; /* * A packet has been pushed so update traffic @@ -1141,9 +1138,8 @@ static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo) p = brcmu_pktq_mdeq(&entry->psq, pmsk << (fifo * 2), &prec_out); if (p == NULL) { if (entry->suppressed) { - if (entry->suppr_transit_count > - entry->suppress_count) - return NULL; + if (entry->suppr_transit_count) + continue; entry->suppressed = false; p = brcmu_pktq_mdeq(&entry->psq, 1 << (fifo * 2), &prec_out); @@ -1194,12 +1190,9 @@ static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo, /* this packet was suppressed */ if (!entry->suppressed) { entry->suppressed = true; - entry->suppress_count = brcmu_pktq_mlen(&entry->psq, - 1 << (fifo * 2 + 1)); entry->suppr_transit_count = entry->transit_count; - brcmf_dbg(DATA, "suppress %s: supp_cnt %d transit %d\n", - entry->name, entry->suppress_count, - entry->transit_count); + brcmf_dbg(DATA, "suppress %s: transit %d\n", + entry->name, entry->transit_count); } entry->generation = genbit; @@ -1218,7 +1211,6 @@ static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo, * wlfc cleanup */ brcmf_fws_hanger_mark_suppressed(&fws->hanger, hslot); - entry->suppress_count++; } return ret; @@ -1263,6 +1255,9 @@ brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot, brcmu_pkt_buf_free_skb(skb); return -EINVAL; } + entry->transit_count--; + if (entry->suppressed && entry->suppr_transit_count) + entry->suppr_transit_count--; brcmf_dbg(DATA, "%s flags %X htod %X\n", entry->name, skcb->if_flags, skcb->htod); @@ -1276,13 +1271,9 @@ brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot, if (!remove_from_hanger) ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, genbit); - if (remove_from_hanger || ret) { - entry->transit_count--; - if (entry->suppressed) - entry->suppr_transit_count--; - + if (remove_from_hanger || ret) brcmf_txfinalize(fws->drvr, skb, true); - } + return 0; } @@ -1579,7 +1570,6 @@ static int brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo, free_ctr = entry->seq[fifo]; brcmf_skb_htod_tag_set_field(p, HSLOT, hslot); brcmf_skb_htod_tag_set_field(p, FREERUN, free_ctr); - entry->transit_count++; rc = brcmf_fws_hanger_pushpkt(&fws->hanger, p, hslot); if (rc) brcmf_err("hanger push failed: rc=%d\n", rc); @@ -1733,6 +1723,9 @@ static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo, goto rollback; } + entry->transit_count++; + if (entry->suppressed) + entry->suppr_transit_count++; entry->seq[fifo]++; fws->stats.pkt2bus++; if (brcmf_skbcb(skb)->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK) { -- cgit v1.2.3 From cf3a6872b981037e29517b3e61e199bde14abc65 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Thu, 6 Jun 2013 13:18:02 +0200 Subject: brcmfmac: fix send_pkts statistic counter in firmware-signalling The statistic counter send_pkts was wrongly counted conditionally. Correcting the mistake. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index dc40eec8a5cc..04aa4b286611 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -1728,10 +1728,9 @@ static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo, entry->suppr_transit_count++; entry->seq[fifo]++; fws->stats.pkt2bus++; - if (brcmf_skbcb(skb)->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK) { - fws->stats.send_pkts[fifo]++; + fws->stats.send_pkts[fifo]++; + if (brcmf_skbcb(skb)->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK) fws->stats.fifo_credits_sent[fifo]++; - } return rc; -- cgit v1.2.3 From ea0737d6e24b44b632e9094108bb987b0338ea74 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Thu, 6 Jun 2013 13:18:05 +0200 Subject: brcmfmac: add trace event for capturing BDC header The BDC header contains PropTx TLV signals that are useful to capture for debugging. This event captures the header and tlv's in binary form. This can be post-processed using trace-cmd plugin. Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c | 3 +++ .../net/wireless/brcm80211/brcmfmac/tracepoint.h | 21 +++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c index 59c77aa3b959..dd85401063cb 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c @@ -30,6 +30,7 @@ #include "dhd_bus.h" #include "fwsignal.h" #include "dhd_dbg.h" +#include "tracepoint.h" struct brcmf_proto_cdc_dcmd { __le32 cmd; /* dongle command value */ @@ -292,6 +293,7 @@ void brcmf_proto_hdrpush(struct brcmf_pub *drvr, int ifidx, u8 offset, h->flags2 = 0; h->data_offset = offset; BDC_SET_IF_IDX(h, ifidx); + trace_brcmf_bdchdr(pktbuf->data); } int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx, @@ -309,6 +311,7 @@ int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx, return -EBADE; } + trace_brcmf_bdchdr(pktbuf->data); h = (struct brcmf_proto_bdc_header *)(pktbuf->data); *ifidx = BDC_GET_IF_IDX(h); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.h b/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.h index 9df1f7a681e0..bc2917112899 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.h @@ -87,6 +87,27 @@ TRACE_EVENT(brcmf_hexdump, TP_printk("hexdump [length=%lu]", __entry->len) ); +TRACE_EVENT(brcmf_bdchdr, + TP_PROTO(void *data), + TP_ARGS(data), + TP_STRUCT__entry( + __field(u8, flags) + __field(u8, prio) + __field(u8, flags2) + __field(u32, siglen) + __dynamic_array(u8, signal, *((u8 *)data + 3) * 4) + ), + TP_fast_assign( + __entry->flags = *(u8 *)data; + __entry->prio = *((u8 *)data + 1); + __entry->flags2 = *((u8 *)data + 2); + __entry->siglen = *((u8 *)data + 3) * 4; + memcpy(__get_dynamic_array(signal), + (u8 *)data + 4, __entry->siglen); + ), + TP_printk("bdc: prio=%d siglen=%d", __entry->prio, __entry->siglen) +); + #ifdef CONFIG_BRCM_TRACING #undef TRACE_INCLUDE_PATH -- cgit v1.2.3 From 672774f124dd07b2aaa1c8fdd56d98d786434fbb Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Thu, 6 Jun 2013 13:18:06 +0200 Subject: brcmfmac: increment hard_header_len instead of overriding In brcmf_net_attach() the hard_header_len is set to sum of ETH_HLEN and the headroom needed by the bus interface. Better use increment instead as hard_header_len is already initialized upon alloc_netdev(). Reviewed-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index 63cadf63ccfe..29828865ed69 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -640,7 +640,7 @@ int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked) /* set appropriate operations */ ndev->netdev_ops = &brcmf_netdev_ops_pri; - ndev->hard_header_len = ETH_HLEN + drvr->hdrlen; + ndev->hard_header_len += drvr->hdrlen; ndev->ethtool_ops = &brcmf_ethtool_ops; drvr->rxsz = ndev->mtu + ndev->hard_header_len + -- cgit v1.2.3 From c7773fc1ef55b7d1ed44a27900b86dc28351dec4 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Thu, 6 Jun 2013 13:49:32 +0200 Subject: brcmfmac: Sent TIM information in case of data available. When data is available and fw signalling is enabled then TIM information should be sent to firmware. If it can piggy back on existing packet then do that otherwise create dummy packet to get information out. Cc: Dan Carpenter Reviewed-by: Arend Van Spriel Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 167 +++++++++++++-------- 1 file changed, 103 insertions(+), 64 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index 04aa4b286611..ff920601898c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -157,11 +157,13 @@ static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id) * @BRCMF_FWS_SKBSTATE_NEW: sk_buff is newly arrived in the driver. * @BRCMF_FWS_SKBSTATE_DELAYED: sk_buff had to wait on queue. * @BRCMF_FWS_SKBSTATE_SUPPRESSED: sk_buff has been suppressed by firmware. + * @BRCMF_FWS_SKBSTATE_TIM: allocated for TIM update info. */ enum brcmf_fws_skb_state { BRCMF_FWS_SKBSTATE_NEW, BRCMF_FWS_SKBSTATE_DELAYED, - BRCMF_FWS_SKBSTATE_SUPPRESSED + BRCMF_FWS_SKBSTATE_SUPPRESSED, + BRCMF_FWS_SKBSTATE_TIM }; /** @@ -278,6 +280,7 @@ struct brcmf_skbuff_cb { /** * enum brcmf_fws_fifo - fifo indices used by dongle firmware. * + * @BRCMF_FWS_FIFO_FIRST: first fifo, ie. background. * @BRCMF_FWS_FIFO_AC_BK: fifo for background traffic. * @BRCMF_FWS_FIFO_AC_BE: fifo for best-effort traffic. * @BRCMF_FWS_FIFO_AC_VI: fifo for video traffic. @@ -287,7 +290,8 @@ struct brcmf_skbuff_cb { * @BRCMF_FWS_FIFO_COUNT: number of fifos. */ enum brcmf_fws_fifo { - BRCMF_FWS_FIFO_AC_BK, + BRCMF_FWS_FIFO_FIRST, + BRCMF_FWS_FIFO_AC_BK = BRCMF_FWS_FIFO_FIRST, BRCMF_FWS_FIFO_AC_BE, BRCMF_FWS_FIFO_AC_VI, BRCMF_FWS_FIFO_AC_VO, @@ -783,22 +787,95 @@ static void brcmf_fws_cleanup(struct brcmf_fws_info *fws, int ifidx) brcmf_fws_hanger_cleanup(fws, matchfn, ifidx); } -static void brcmf_fws_tim_update(struct brcmf_fws_info *ctx, - struct brcmf_fws_mac_descriptor *entry, - int prec) +static int brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb) { - if (entry->state == BRCMF_FWS_STATE_CLOSE) { - /* check delayedQ and suppressQ in one call using bitmap */ - if (brcmu_pktq_mlen(&entry->psq, 3 << (prec * 2)) == 0) - entry->traffic_pending_bmp = - entry->traffic_pending_bmp & ~NBITVAL(prec); - else - entry->traffic_pending_bmp = - entry->traffic_pending_bmp | NBITVAL(prec); + struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac; + u8 *wlh; + u16 data_offset = 0; + u8 fillers; + __le32 pkttag = cpu_to_le32(brcmf_skbcb(skb)->htod); + + brcmf_dbg(TRACE, "enter: ea=%pM, ifidx=%u (%u), pkttag=0x%08X, hslot=%d\n", + entry->ea, entry->interface_id, + brcmf_skb_if_flags_get_field(skb, INDEX), + le32_to_cpu(pkttag), (le32_to_cpu(pkttag) >> 8) & 0xffff); + if (entry->send_tim_signal) + data_offset += 2 + BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN; + + /* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */ + data_offset += 2 + BRCMF_FWS_TYPE_PKTTAG_LEN; + fillers = round_up(data_offset, 4) - data_offset; + data_offset += fillers; + + skb_push(skb, data_offset); + wlh = skb->data; + + wlh[0] = BRCMF_FWS_TYPE_PKTTAG; + wlh[1] = BRCMF_FWS_TYPE_PKTTAG_LEN; + memcpy(&wlh[2], &pkttag, sizeof(pkttag)); + wlh += BRCMF_FWS_TYPE_PKTTAG_LEN + 2; + + if (entry->send_tim_signal) { + entry->send_tim_signal = 0; + wlh[0] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP; + wlh[1] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN; + wlh[2] = entry->mac_handle; + wlh[3] = entry->traffic_pending_bmp; + brcmf_dbg(TRACE, "adding TIM info: %02X:%02X:%02X:%02X\n", + wlh[0], wlh[1], wlh[2], wlh[3]); + wlh += BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2; + entry->traffic_lastreported_bmp = entry->traffic_pending_bmp; } - /* request a TIM update to firmware at the next piggyback opportunity */ + if (fillers) + memset(wlh, BRCMF_FWS_TYPE_FILLER, fillers); + + brcmf_proto_hdrpush(fws->drvr, brcmf_skb_if_flags_get_field(skb, INDEX), + data_offset >> 2, skb); + return 0; +} + +static bool brcmf_fws_tim_update(struct brcmf_fws_info *fws, + struct brcmf_fws_mac_descriptor *entry, + int prec, bool send_immediately) +{ + struct sk_buff *skb; + struct brcmf_bus *bus; + struct brcmf_skbuff_cb *skcb; + s32 err; + u32 len; + + /* check delayedQ and suppressQ in one call using bitmap */ + if (brcmu_pktq_mlen(&entry->psq, 3 << (prec * 2)) == 0) + entry->traffic_pending_bmp &= ~NBITVAL(prec); + else + entry->traffic_pending_bmp |= NBITVAL(prec); + + entry->send_tim_signal = false; if (entry->traffic_lastreported_bmp != entry->traffic_pending_bmp) entry->send_tim_signal = true; + if (send_immediately && entry->send_tim_signal && + entry->state == BRCMF_FWS_STATE_CLOSE) { + /* create a dummy packet and sent that. The traffic */ + /* bitmap info will automatically be attached to that packet */ + len = BRCMF_FWS_TYPE_PKTTAG_LEN + 2 + + BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2 + + 4 + fws->drvr->hdrlen; + skb = brcmu_pkt_buf_get_skb(len); + if (skb == NULL) + return false; + skb_pull(skb, len); + skcb = brcmf_skbcb(skb); + skcb->mac = entry; + skcb->state = BRCMF_FWS_SKBSTATE_TIM; + bus = fws->drvr->bus_if; + err = brcmf_fws_hdrpush(fws, skb); + if (err == 0) + err = brcmf_bus_txdata(bus, skb); + if (err) + brcmu_pkt_buf_free_skb(skb); + return true; + } + return false; } static void @@ -886,7 +963,6 @@ static int brcmf_fws_macdesc_state_indicate(struct brcmf_fws_info *fws, { struct brcmf_fws_mac_descriptor *entry; u8 mac_handle; - int i; mac_handle = data[0]; entry = &fws->desc.nodes[mac_handle & 0x1F]; @@ -894,18 +970,18 @@ static int brcmf_fws_macdesc_state_indicate(struct brcmf_fws_info *fws, fws->stats.mac_ps_update_failed++; return -ESRCH; } - - brcmf_dbg(TRACE, "%s (%d): %s\n", brcmf_fws_get_tlv_name(type), type, - entry->name); - /* a state update should wipe old credits? */ + /* a state update should wipe old credits */ entry->requested_credit = 0; + entry->requested_packet = 0; if (type == BRCMF_FWS_TYPE_MAC_OPEN) { entry->state = BRCMF_FWS_STATE_OPEN; return BRCMF_FWS_RET_OK_SCHEDULE; } else { entry->state = BRCMF_FWS_STATE_CLOSE; - for (i = BRCMF_FWS_FIFO_AC_BE; i < NL80211_NUM_ACS; i++) - brcmf_fws_tim_update(fws, entry, i); + brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_BK, false); + brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_BE, false); + brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_VI, false); + brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_VO, true); } return BRCMF_FWS_RET_OK_NOSCHEDULE; } @@ -1104,7 +1180,7 @@ static int brcmf_fws_enq(struct brcmf_fws_info *fws, * A packet has been pushed so update traffic * availability bitmap, if applicable */ - brcmf_fws_tim_update(fws, entry, fifo); + brcmf_fws_tim_update(fws, entry, fifo, true); brcmf_fws_flow_control_check(fws, &entry->psq, brcmf_skb_if_flags_get_field(p, INDEX)); return 0; @@ -1160,7 +1236,7 @@ static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo) * A packet has been picked up, update traffic * availability bitmap, if applicable */ - brcmf_fws_tim_update(fws, entry, fifo); + brcmf_fws_tim_update(fws, entry, fifo, false); /* * decrement total enqueued fifo packets and @@ -1495,47 +1571,6 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, return 0; } -static int brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb) -{ - struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac; - u8 *wlh; - u16 data_offset = 0; - u8 fillers; - __le32 pkttag = cpu_to_le32(brcmf_skbcb(skb)->htod); - - if (entry->send_tim_signal) - data_offset += 2 + BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN; - - /* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */ - data_offset += 2 + BRCMF_FWS_TYPE_PKTTAG_LEN; - fillers = round_up(data_offset, 4) - data_offset; - data_offset += fillers; - - skb_push(skb, data_offset); - wlh = skb->data; - - wlh[0] = BRCMF_FWS_TYPE_PKTTAG; - wlh[1] = BRCMF_FWS_TYPE_PKTTAG_LEN; - memcpy(&wlh[2], &pkttag, sizeof(pkttag)); - wlh += BRCMF_FWS_TYPE_PKTTAG_LEN + 2; - - if (entry->send_tim_signal) { - entry->send_tim_signal = 0; - wlh[0] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP; - wlh[1] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN; - wlh[2] = entry->mac_handle; - wlh[3] = entry->traffic_pending_bmp; - wlh += BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2; - entry->traffic_lastreported_bmp = entry->traffic_pending_bmp; - } - if (fillers) - memset(wlh, BRCMF_FWS_TYPE_FILLER, fillers); - - brcmf_proto_hdrpush(fws->drvr, brcmf_skb_if_flags_get_field(skb, INDEX), - data_offset >> 2, skb); - return 0; -} - static int brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo, struct sk_buff *p) { @@ -1990,6 +2025,10 @@ void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb) ulong flags; int fifo; + if (brcmf_skbcb(skb)->state == BRCMF_FWS_SKBSTATE_TIM) { + brcmu_pkt_buf_free_skb(skb); + return; + } brcmf_fws_lock(fws->drvr, flags); brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_FW_TOSSED, brcmf_skb_htod_tag_get_field(skb, HSLOT), 0); -- cgit v1.2.3 From ee06fcad742ab52a1ba67ba734fee6a98af51621 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Fri, 7 Jun 2013 11:03:00 +0200 Subject: brcmfmac: free primary net_device when brcmf_bus_start() fails When initialization within brcmf_bus_start() fails on steps before the brcmf_net_attach() the net_device for the primary interface needs to be freed. This patch resolves a panic during kernel boot as reported by Stephen Warren. ref.: http://mid.gmane.org/51AD1F22.2080004@wwwdotorg.org Tested-by: Stephen Warren Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index 29828865ed69..8c402e7b97eb 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -941,6 +941,10 @@ fail: brcmf_fws_del_interface(ifp); brcmf_fws_deinit(drvr); } + if (drvr->iflist[0]) { + free_netdev(ifp->ndev); + drvr->iflist[0] = NULL; + } if (p2p_ifp) { free_netdev(p2p_ifp->ndev); drvr->iflist[1] = NULL; -- cgit v1.2.3 From f2c7a793374be88eb680a5dfcf400b4cd97f29d4 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 7 Jun 2013 18:12:00 +0200 Subject: ath9k: add support for IEEE80211_TX_CTL_PS_RESPONSE Use the UAPSD hardware queue to get PS-Poll responses out as fast as possible and without backoff. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 1 + drivers/net/wireless/ath/ath9k/init.c | 2 ++ drivers/net/wireless/ath/ath9k/xmit.c | 43 +++++++++++++++++++++------------- 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 18fcee4e9d68..4f798276802c 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -296,6 +296,7 @@ struct ath_tx { struct ath_txq txq[ATH9K_NUM_TX_QUEUES]; struct ath_descdma txdma; struct ath_txq *txq_map[IEEE80211_NUM_ACS]; + struct ath_txq *uapsdq; u32 txq_max_pending[IEEE80211_NUM_ACS]; u16 max_aggr_framelen[IEEE80211_NUM_ACS][4][32]; }; diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 389ee1b59976..d65ee6e6437b 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -432,6 +432,8 @@ static int ath9k_init_queues(struct ath_softc *sc) sc->config.cabqReadytime = ATH_CABQ_READY_TIME; ath_cabq_update(sc); + sc->tx.uapsdq = ath_txq_setup(sc, ATH9K_TX_QUEUE_UAPSD, 0); + for (i = 0; i < IEEE80211_NUM_ACS; i++) { sc->tx.txq_map[i] = ath_txq_setup(sc, ATH9K_TX_QUEUE_DATA, i); sc->tx.txq_map[i]->mac80211_qnum = i; diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 1c9b1bac8b0d..3931bd82629d 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -1681,8 +1681,9 @@ static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq, } } -static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_atx_tid *tid, - struct sk_buff *skb, struct ath_tx_control *txctl) +static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_txq *txq, + struct ath_atx_tid *tid, struct sk_buff *skb, + struct ath_tx_control *txctl) { struct ath_frame_info *fi = get_frame_info(skb); struct list_head bf_head; @@ -1695,21 +1696,22 @@ static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_atx_tid *tid, * - seqno is not within block-ack window * - h/w queue depth exceeds low water mark */ - if (!skb_queue_empty(&tid->buf_q) || tid->paused || - !BAW_WITHIN(tid->seq_start, tid->baw_size, tid->seq_next) || - txctl->txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH) { + if ((!skb_queue_empty(&tid->buf_q) || tid->paused || + !BAW_WITHIN(tid->seq_start, tid->baw_size, tid->seq_next) || + txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH) && + txq != sc->tx.uapsdq) { /* * Add this frame to software queue for scheduling later * for aggregation. */ - TX_STAT_INC(txctl->txq->axq_qnum, a_queued_sw); + TX_STAT_INC(txq->axq_qnum, a_queued_sw); __skb_queue_tail(&tid->buf_q, skb); if (!txctl->an || !txctl->an->sleeping) - ath_tx_queue_tid(txctl->txq, tid); + ath_tx_queue_tid(txq, tid); return; } - bf = ath_tx_setup_buffer(sc, txctl->txq, tid, skb); + bf = ath_tx_setup_buffer(sc, txq, tid, skb); if (!bf) { ieee80211_free_txskb(sc->hw, skb); return; @@ -1724,10 +1726,10 @@ static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_atx_tid *tid, ath_tx_addto_baw(sc, tid, bf->bf_state.seqno); /* Queue to h/w without aggregation */ - TX_STAT_INC(txctl->txq->axq_qnum, a_queued_hw); + TX_STAT_INC(txq->axq_qnum, a_queued_hw); bf->bf_lastbf = bf; - ath_tx_fill_desc(sc, bf, txctl->txq, fi->framelen); - ath_tx_txqaddbuf(sc, txctl->txq, &bf_head, false); + ath_tx_fill_desc(sc, bf, txq, fi->framelen); + ath_tx_txqaddbuf(sc, txq, &bf_head, false); } static void ath_tx_send_normal(struct ath_softc *sc, struct ath_txq *txq, @@ -1935,6 +1937,12 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, txq->stopped = true; } + if (info->flags & IEEE80211_TX_CTL_PS_RESPONSE) { + ath_txq_unlock(sc, txq); + txq = sc->tx.uapsdq; + ath_txq_lock(sc, txq); + } + if (txctl->an && ieee80211_is_data_qos(hdr->frame_control)) { tidno = ieee80211_get_qos_ctl(hdr)[0] & IEEE80211_QOS_CTL_TID_MASK; @@ -1948,11 +1956,11 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, * Try aggregation if it's a unicast data frame * and the destination is HT capable. */ - ath_tx_send_ampdu(sc, tid, skb, txctl); + ath_tx_send_ampdu(sc, txq, tid, skb, txctl); goto out; } - bf = ath_tx_setup_buffer(sc, txctl->txq, tid, skb); + bf = ath_tx_setup_buffer(sc, txq, tid, skb); if (!bf) { if (txctl->paprd) dev_kfree_skb_any(skb); @@ -1967,7 +1975,7 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, bf->bf_state.bfs_paprd_timestamp = jiffies; ath_set_rates(vif, sta, bf); - ath_tx_send_normal(sc, txctl->txq, tid, skb); + ath_tx_send_normal(sc, txq, tid, skb); out: ath_txq_unlock(sc, txq); @@ -2020,7 +2028,12 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, } spin_unlock_irqrestore(&sc->sc_pm_lock, flags); + __skb_queue_tail(&txq->complete_q, skb); + q = skb_get_queue_mapping(skb); + if (txq == sc->tx.uapsdq) + txq = sc->tx.txq_map[q]; + if (txq == sc->tx.txq_map[q]) { if (WARN_ON(--txq->pending_frames < 0)) txq->pending_frames = 0; @@ -2031,8 +2044,6 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, txq->stopped = false; } } - - __skb_queue_tail(&txq->complete_q, skb); } static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf, -- cgit v1.2.3 From 86a22acfcb40ed9cf4ceee789b45da6a3314ed77 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 7 Jun 2013 18:12:01 +0200 Subject: ath9k: implement support for .release_buffered_frames() This adds support for PS-Poll and U-APSD driver-buffered frames (part of an aggregation session). Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 5 + drivers/net/wireless/ath/ath9k/main.c | 1 + drivers/net/wireless/ath/ath9k/xmit.c | 175 ++++++++++++++++++++++++++------- 3 files changed, 145 insertions(+), 36 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 4f798276802c..bd35a54a3f04 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -354,6 +354,11 @@ void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an); void ath_tx_aggr_sleep(struct ieee80211_sta *sta, struct ath_softc *sc, struct ath_node *an); +void ath9k_release_buffered_frames(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + u16 tids, int nframes, + enum ieee80211_frame_release_type reason, + bool more_data); /********/ /* VIFs */ diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index e5b186b04b29..bf61a1c3f14c 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -2347,6 +2347,7 @@ struct ieee80211_ops ath9k_ops = { .flush = ath9k_flush, .tx_frames_pending = ath9k_tx_frames_pending, .tx_last_beacon = ath9k_tx_last_beacon, + .release_buffered_frames = ath9k_release_buffered_frames, .get_stats = ath9k_get_stats, .set_antenna = ath9k_set_antenna, .get_antenna = ath9k_get_antenna, diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 3931bd82629d..6c9ff9c9730f 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -518,6 +518,10 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, ath_tx_complete_buf(sc, bf, txq, &bf_head, ts, !txfail); } else { + if (tx_info->flags & IEEE80211_TX_STATUS_EOSP) { + tx_info->flags &= ~IEEE80211_TX_STATUS_EOSP; + ieee80211_sta_eosp(sta); + } /* retry the un-acked ones */ if (bf->bf_next == NULL && bf_last->bf_stale) { struct ath_buf *tbf; @@ -786,25 +790,20 @@ static int ath_compute_num_delims(struct ath_softc *sc, struct ath_atx_tid *tid, return ndelim; } -static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc, - struct ath_txq *txq, - struct ath_atx_tid *tid, - struct list_head *bf_q, - int *aggr_len) +static struct ath_buf * +ath_tx_get_tid_subframe(struct ath_softc *sc, struct ath_txq *txq, + struct ath_atx_tid *tid) { -#define PADBYTES(_len) ((4 - ((_len) % 4)) % 4) - struct ath_buf *bf, *bf_first = NULL, *bf_prev = NULL; - int rl = 0, nframes = 0, ndelim, prev_al = 0; - u16 aggr_limit = 0, al = 0, bpad = 0, - al_delta, h_baw = tid->baw_size / 2; - enum ATH_AGGR_STATUS status = ATH_AGGR_DONE; - struct ieee80211_tx_info *tx_info; struct ath_frame_info *fi; struct sk_buff *skb; + struct ath_buf *bf; u16 seqno; - do { + while (1) { skb = skb_peek(&tid->buf_q); + if (!skb) + break; + fi = get_frame_info(skb); bf = fi->bf; if (!fi->bf) @@ -820,10 +819,8 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc, seqno = bf->bf_state.seqno; /* do not step over block-ack window */ - if (!BAW_WITHIN(tid->seq_start, tid->baw_size, seqno)) { - status = ATH_AGGR_BAW_CLOSED; + if (!BAW_WITHIN(tid->seq_start, tid->baw_size, seqno)) break; - } if (tid->bar_index > ATH_BA_INDEX(tid->seq_start, seqno)) { struct ath_tx_status ts = {}; @@ -837,6 +834,40 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc, continue; } + bf->bf_next = NULL; + bf->bf_lastbf = bf; + return bf; + } + + return NULL; +} + +static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc, + struct ath_txq *txq, + struct ath_atx_tid *tid, + struct list_head *bf_q, + int *aggr_len) +{ +#define PADBYTES(_len) ((4 - ((_len) % 4)) % 4) + struct ath_buf *bf, *bf_first = NULL, *bf_prev = NULL; + int rl = 0, nframes = 0, ndelim, prev_al = 0; + u16 aggr_limit = 0, al = 0, bpad = 0, + al_delta, h_baw = tid->baw_size / 2; + enum ATH_AGGR_STATUS status = ATH_AGGR_DONE; + struct ieee80211_tx_info *tx_info; + struct ath_frame_info *fi; + struct sk_buff *skb; + + do { + bf = ath_tx_get_tid_subframe(sc, txq, tid); + if (!bf) { + status = ATH_AGGR_BAW_CLOSED; + break; + } + + skb = bf->bf_mpdu; + fi = get_frame_info(skb); + if (!bf_first) bf_first = bf; @@ -882,7 +913,7 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc, /* link buffers of this frame to the aggregate */ if (!fi->retries) - ath_tx_addto_baw(sc, tid, seqno); + ath_tx_addto_baw(sc, tid, bf->bf_state.seqno); bf->bf_state.ndelim = ndelim; __skb_unlink(skb, &tid->buf_q); @@ -1090,10 +1121,8 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf, struct ath_txq *txq, int len) { struct ath_hw *ah = sc->sc_ah; - struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(bf->bf_mpdu); - struct ath_buf *bf_first = bf; + struct ath_buf *bf_first = NULL; struct ath_tx_info info; - bool aggr = !!(bf->bf_state.bf_type & BUF_AGGR); memset(&info, 0, sizeof(info)); info.is_first = true; @@ -1101,24 +1130,11 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf, info.txpower = MAX_RATE_POWER; info.qcu = txq->axq_qnum; - info.flags = ATH9K_TXDESC_INTREQ; - if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK) - info.flags |= ATH9K_TXDESC_NOACK; - if (tx_info->flags & IEEE80211_TX_CTL_LDPC) - info.flags |= ATH9K_TXDESC_LDPC; - - ath_buf_set_rate(sc, bf, &info, len); - - if (tx_info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT) - info.flags |= ATH9K_TXDESC_CLRDMASK; - - if (bf->bf_state.bfs_paprd) - info.flags |= (u32) bf->bf_state.bfs_paprd << ATH9K_TXDESC_PAPRD_S; - - while (bf) { struct sk_buff *skb = bf->bf_mpdu; + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ath_frame_info *fi = get_frame_info(skb); + bool aggr = !!(bf->bf_state.bf_type & BUF_AGGR); info.type = get_hw_packet_type(skb); if (bf->bf_next) @@ -1126,6 +1142,26 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf, else info.link = 0; + if (!bf_first) { + bf_first = bf; + + info.flags = ATH9K_TXDESC_INTREQ; + if ((tx_info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT) || + txq == sc->tx.uapsdq) + info.flags |= ATH9K_TXDESC_CLRDMASK; + + if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK) + info.flags |= ATH9K_TXDESC_NOACK; + if (tx_info->flags & IEEE80211_TX_CTL_LDPC) + info.flags |= ATH9K_TXDESC_LDPC; + + if (bf->bf_state.bfs_paprd) + info.flags |= (u32) bf->bf_state.bfs_paprd << + ATH9K_TXDESC_PAPRD_S; + + ath_buf_set_rate(sc, bf, &info, len); + } + info.buf_addr[0] = bf->bf_buf_addr; info.buf_len[0] = skb->len; info.pkt_len = fi->framelen; @@ -1135,7 +1171,7 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf, if (aggr) { if (bf == bf_first) info.aggr = AGGR_BUF_FIRST; - else if (!bf->bf_next) + else if (bf == bf_first->bf_lastbf) info.aggr = AGGR_BUF_LAST; else info.aggr = AGGR_BUF_MIDDLE; @@ -1144,6 +1180,9 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf, info.aggr_len = len; } + if (bf == bf_first->bf_lastbf) + bf_first = NULL; + ath9k_hw_set_txdesc(ah, bf->bf_desc, &info); bf = bf->bf_next; } @@ -1328,6 +1367,70 @@ void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, ath_txq_unlock_complete(sc, txq); } +void ath9k_release_buffered_frames(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + u16 tids, int nframes, + enum ieee80211_frame_release_type reason, + bool more_data) +{ + struct ath_softc *sc = hw->priv; + struct ath_node *an = (struct ath_node *)sta->drv_priv; + struct ath_txq *txq = sc->tx.uapsdq; + struct ieee80211_tx_info *info; + struct list_head bf_q; + struct ath_buf *bf_tail = NULL, *bf; + int sent = 0; + int i; + + INIT_LIST_HEAD(&bf_q); + for (i = 0; tids && nframes; i++, tids >>= 1) { + struct ath_atx_tid *tid; + + if (!(tids & 1)) + continue; + + tid = ATH_AN_2_TID(an, i); + if (tid->paused) + continue; + + ath_txq_lock(sc, tid->ac->txq); + while (!skb_queue_empty(&tid->buf_q) && nframes > 0) { + bf = ath_tx_get_tid_subframe(sc, sc->tx.uapsdq, tid); + if (!bf) + break; + + __skb_unlink(bf->bf_mpdu, &tid->buf_q); + list_add_tail(&bf->list, &bf_q); + ath_set_rates(tid->an->vif, tid->an->sta, bf); + ath_tx_addto_baw(sc, tid, bf->bf_state.seqno); + bf->bf_state.bf_type &= ~BUF_AGGR; + if (bf_tail) + bf_tail->bf_next = bf; + + bf_tail = bf; + nframes--; + sent++; + TX_STAT_INC(txq->axq_qnum, a_queued_hw); + + if (skb_queue_empty(&tid->buf_q)) + ieee80211_sta_set_buffered(an->sta, i, false); + } + ath_txq_unlock_complete(sc, tid->ac->txq); + } + + if (list_empty(&bf_q)) + return; + + info = IEEE80211_SKB_CB(bf_tail->bf_mpdu); + info->flags |= IEEE80211_TX_STATUS_EOSP; + + bf = list_first_entry(&bf_q, struct ath_buf, list); + ath_txq_lock(sc, txq); + ath_tx_fill_desc(sc, bf, txq, 0); + ath_tx_txqaddbuf(sc, txq, &bf_q, false); + ath_txq_unlock(sc, txq); +} + /********************/ /* Queue Management */ /********************/ -- cgit v1.2.3 From 59505c02e106ce9388d816799cd64b0405f98f2f Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 7 Jun 2013 18:12:02 +0200 Subject: ath9k: limit multicast buffer hardware queue depth The CAB (Content after Beacon) queue is used for beacon-triggered transmission of buffered multicast frames. If lots of multicast frames were buffered and this queue fills up, it drowns out all regular traffic. To limit the damage that buffered traffic can do, try to limit the queued data to becaon_interval / 8. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 2 + drivers/net/wireless/ath/ath9k/beacon.c | 23 +------ drivers/net/wireless/ath/ath9k/xmit.c | 115 +++++++++++++++++++++++++++----- 3 files changed, 104 insertions(+), 36 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index bd35a54a3f04..a6e666bcc0a7 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -344,6 +344,8 @@ int ath_txq_update(struct ath_softc *sc, int qnum, void ath_update_max_aggr_framelen(struct ath_softc *sc, int queue, int txop); int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, struct ath_tx_control *txctl); +void ath_tx_cabq(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct sk_buff *skb); void ath_tx_tasklet(struct ath_softc *sc); void ath_tx_edma_tasklet(struct ath_softc *sc); int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta, diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index fd1eebab8647..1a17732bb089 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -108,23 +108,6 @@ static void ath9k_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif, ath9k_hw_set_txdesc(ah, bf->bf_desc, &info); } -static void ath9k_tx_cabq(struct ieee80211_hw *hw, struct sk_buff *skb) -{ - struct ath_softc *sc = hw->priv; - struct ath_common *common = ath9k_hw_common(sc->sc_ah); - struct ath_tx_control txctl; - - memset(&txctl, 0, sizeof(struct ath_tx_control)); - txctl.txq = sc->beacon.cabq; - - ath_dbg(common, XMIT, "transmitting CABQ packet, skb: %p\n", skb); - - if (ath_tx_start(hw, skb, &txctl) != 0) { - ath_dbg(common, XMIT, "CABQ TX failed\n"); - ieee80211_free_txskb(hw, skb); - } -} - static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { @@ -206,10 +189,8 @@ static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw, ath9k_beacon_setup(sc, vif, bf, info->control.rates[0].idx); - while (skb) { - ath9k_tx_cabq(hw, skb); - skb = ieee80211_get_buffered_bc(hw, vif); - } + if (skb) + ath_tx_cabq(hw, vif, skb); return bf; } diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 6c9ff9c9730f..7e19d9b5214e 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -1970,22 +1970,16 @@ static struct ath_buf *ath_tx_setup_buffer(struct ath_softc *sc, return bf; } -/* Upon failure caller should free skb */ -int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, - struct ath_tx_control *txctl) +static int ath_tx_prepare(struct ieee80211_hw *hw, struct sk_buff *skb, + struct ath_tx_control *txctl) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_sta *sta = txctl->sta; struct ieee80211_vif *vif = info->control.vif; struct ath_softc *sc = hw->priv; - struct ath_txq *txq = txctl->txq; - struct ath_atx_tid *tid = NULL; - struct ath_buf *bf; - int padpos, padsize; int frmlen = skb->len + FCS_LEN; - u8 tidno; - int q; + int padpos, padsize; /* NOTE: sta can be NULL according to net/mac80211.h */ if (sta) @@ -2006,6 +2000,11 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no); } + if ((vif && vif->type != NL80211_IFTYPE_AP && + vif->type != NL80211_IFTYPE_AP_VLAN) || + !ieee80211_is_data(hdr->frame_control)) + info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT; + /* Add the padding after the header if this is not already done */ padpos = ieee80211_hdrlen(hdr->frame_control); padsize = padpos & 3; @@ -2015,16 +2014,34 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, skb_push(skb, padsize); memmove(skb->data, skb->data + padsize, padpos); - hdr = (struct ieee80211_hdr *) skb->data; } - if ((vif && vif->type != NL80211_IFTYPE_AP && - vif->type != NL80211_IFTYPE_AP_VLAN) || - !ieee80211_is_data(hdr->frame_control)) - info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT; - setup_frame_info(hw, sta, skb, frmlen); + return 0; +} + +/* Upon failure caller should free skb */ +int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, + struct ath_tx_control *txctl) +{ + struct ieee80211_hdr *hdr; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_sta *sta = txctl->sta; + struct ieee80211_vif *vif = info->control.vif; + struct ath_softc *sc = hw->priv; + struct ath_txq *txq = txctl->txq; + struct ath_atx_tid *tid = NULL; + struct ath_buf *bf; + u8 tidno; + int q; + int ret; + + ret = ath_tx_prepare(hw, skb, txctl); + if (ret) + return ret; + + hdr = (struct ieee80211_hdr *) skb->data; /* * At this point, the vif, hw_key and sta pointers in the tx control * info are no longer valid (overwritten by the ath_frame_info data. @@ -2086,6 +2103,74 @@ out: return 0; } +void ath_tx_cabq(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct sk_buff *skb) +{ + struct ath_softc *sc = hw->priv; + struct ath_tx_control txctl = { + .txq = sc->beacon.cabq + }; + struct ath_tx_info info = {}; + struct ieee80211_hdr *hdr; + struct ath_buf *bf_tail = NULL; + struct ath_buf *bf; + LIST_HEAD(bf_q); + int duration = 0; + int max_duration; + + max_duration = + sc->cur_beacon_conf.beacon_interval * 1000 * + sc->cur_beacon_conf.dtim_period / ATH_BCBUF; + + do { + struct ath_frame_info *fi = get_frame_info(skb); + + if (ath_tx_prepare(hw, skb, &txctl)) + break; + + bf = ath_tx_setup_buffer(sc, txctl.txq, NULL, skb); + if (!bf) + break; + + bf->bf_lastbf = bf; + ath_set_rates(vif, NULL, bf); + ath_buf_set_rate(sc, bf, &info, fi->framelen); + duration += info.rates[0].PktDuration; + if (bf_tail) + bf_tail->bf_next = bf; + + list_add_tail(&bf->list, &bf_q); + bf_tail = bf; + skb = NULL; + + if (duration > max_duration) + break; + + skb = ieee80211_get_buffered_bc(hw, vif); + } while(skb); + + if (skb) + ieee80211_free_txskb(hw, skb); + + if (list_empty(&bf_q)) + return; + + bf = list_first_entry(&bf_q, struct ath_buf, list); + hdr = (struct ieee80211_hdr *) bf->bf_mpdu->data; + + if (hdr->frame_control & IEEE80211_FCTL_MOREDATA) { + hdr->frame_control &= ~IEEE80211_FCTL_MOREDATA; + dma_sync_single_for_device(sc->dev, bf->bf_buf_addr, + sizeof(*hdr), DMA_TO_DEVICE); + } + + ath_txq_lock(sc, txctl.txq); + ath_tx_fill_desc(sc, bf, txctl.txq, 0); + ath_tx_txqaddbuf(sc, txctl.txq, &bf_q, false); + TX_STAT_INC(txctl.txq->axq_qnum, queued); + ath_txq_unlock(sc, txctl.txq); +} + /*****************/ /* TX Completion */ /*****************/ -- cgit v1.2.3 From 8b5c7f6c2b16a31b9f0fc4dba72524a7028e5e43 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Sun, 9 Jun 2013 09:12:50 +0300 Subject: wil6210: fix timeout for start_pcp It may take up to 3500ms for the FW to start AP/PCP. Increase accordingly, adding some safety margin. Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/wmi.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 527ffb543821..aeb64f21273a 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -739,8 +739,12 @@ int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, u8 chan) if (!wil->secure_pcp) cmd.disable_sec = 1; + /* + * Processing time may be huge, in case of secure AP it takes about + * 3500ms for FW to start AP + */ rc = wmi_call(wil, WMI_PCP_START_CMDID, &cmd, sizeof(cmd), - WMI_PCP_STARTED_EVENTID, &reply, sizeof(reply), 100); + WMI_PCP_STARTED_EVENTID, &reply, sizeof(reply), 5000); if (rc) return rc; -- cgit v1.2.3 From af6b48db9239922391952c09a973c00fde235e92 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Sun, 9 Jun 2013 09:12:51 +0300 Subject: wil6210: map more FW memory map card's back-door debug data Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/wmi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index aeb64f21273a..cfcaf7bf4da6 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -75,10 +75,11 @@ static const struct { {0x800000, 0x808000, 0x900000}, /* FW data RAM 32k */ {0x840000, 0x860000, 0x908000}, /* peripheral data RAM 128k/96k used */ {0x880000, 0x88a000, 0x880000}, /* various RGF */ - {0x8c0000, 0x932000, 0x8c0000}, /* trivial mapping for upper area */ + {0x8c0000, 0x949000, 0x8c0000}, /* trivial mapping for upper area */ /* * 920000..930000 ucode code RAM * 930000..932000 ucode data RAM + * 932000..949000 back-door debug data */ }; -- cgit v1.2.3 From f27dbf78c669902adcf45a9561d22f15362a6907 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Sun, 9 Jun 2013 09:12:52 +0300 Subject: wil6210: improve frame type reporting Report FC from the frame itself, as auxiliary information includes only frame subtype. This is preparation for future changes, when DMG beacon (extension frame) may be reported through wmi_evt_rx_mgmt() Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/wmi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index cfcaf7bf4da6..ac4d26b34097 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -315,8 +315,8 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len) wil_dbg_wmi(wil, "MGMT: channel %d MCS %d SNR %d\n", data->info.channel, data->info.mcs, data->info.snr); - wil_dbg_wmi(wil, "status 0x%04x len %d stype %04x\n", d_status, d_len, - le16_to_cpu(data->info.stype)); + wil_dbg_wmi(wil, "status 0x%04x len %d fc 0x%04x\n", d_status, d_len, + le16_to_cpu(fc)); wil_dbg_wmi(wil, "qid %d mid %d cid %d\n", data->info.qid, data->info.mid, data->info.cid); -- cgit v1.2.3 From 92646c9f1f8fa2a31a3c12b026c2a8cd0e168341 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Sun, 9 Jun 2013 09:12:53 +0300 Subject: wil6210: Derive IE's for AP When starting secure AP, in some cases wpa_s provides probe template but not probe/assoc IE's. In this case, derive missing IE's from probe. Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/cfg80211.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 4eb05d0818c3..189c3078ef7d 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -402,6 +402,30 @@ static int wil_cfg80211_set_default_key(struct wiphy *wiphy, return 0; } +static int wil_fix_bcon(struct wil6210_priv *wil, + struct cfg80211_beacon_data *bcon) +{ + struct ieee80211_mgmt *f = (struct ieee80211_mgmt *)bcon->probe_resp; + size_t hlen = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); + int rc = 0; + + if (bcon->probe_resp_len <= hlen) + return 0; + + if (!bcon->proberesp_ies) { + bcon->proberesp_ies = f->u.probe_resp.variable; + bcon->proberesp_ies_len = bcon->probe_resp_len - hlen; + rc = 1; + } + if (!bcon->assocresp_ies) { + bcon->assocresp_ies = f->u.probe_resp.variable; + bcon->assocresp_ies_len = bcon->probe_resp_len - hlen; + rc = 1; + } + + return rc; +} + static int wil_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, struct cfg80211_ap_settings *info) @@ -423,6 +447,9 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy, print_hex_dump_bytes("SSID ", DUMP_PREFIX_OFFSET, info->ssid, info->ssid_len); + if (wil_fix_bcon(wil, bcon)) + wil_dbg_misc(wil, "Fixed bcon\n"); + rc = wil_reset(wil); if (rc) return rc; -- cgit v1.2.3 From d58db4e49f58152a28dd598ebbec56f1fe92aa80 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Sun, 9 Jun 2013 09:12:54 +0300 Subject: wil6210: Send EAPOL frames using normal Tx queue No more need for special processing of EAPOL, FW can now send EAPOL frames using normal Tx queue for TID 0 This fixes "schedule while atomic" bug - start_xmit called in softirq context; while WMI mechanism that was used may sleep. Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/txrx.c | 20 ++++++++---------- drivers/net/wireless/ath/wil6210/wil6210.h | 1 - drivers/net/wireless/ath/wil6210/wmi.c | 34 ------------------------------ 3 files changed, 9 insertions(+), 46 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 00dffeda983e..e1c492b9dfef 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -768,18 +768,16 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) wil_err(wil, "Xmit in monitor mode not supported\n"); goto drop; } - if (skb->protocol == cpu_to_be16(ETH_P_PAE)) { - rc = wmi_tx_eapol(wil, skb); - } else { - /* find vring */ - vring = wil_find_tx_vring(wil, skb); - if (!vring) { - wil_err(wil, "No Tx VRING available\n"); - goto drop; - } - /* set up vring entry */ - rc = wil_tx_vring(wil, vring, skb); + + /* find vring */ + vring = wil_find_tx_vring(wil, skb); + if (!vring) { + wil_err(wil, "No Tx VRING available\n"); + goto drop; } + /* set up vring entry */ + rc = wil_tx_vring(wil, vring, skb); + switch (rc) { case 0: /* statistics will be updated on the tx_complete */ diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 373cf656f5b0..44fdab51de7e 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -329,7 +329,6 @@ int wmi_set_ssid(struct wil6210_priv *wil, u8 ssid_len, const void *ssid); int wmi_get_ssid(struct wil6210_priv *wil, u8 *ssid_len, void *ssid); int wmi_set_channel(struct wil6210_priv *wil, int channel); int wmi_get_channel(struct wil6210_priv *wil, int *channel); -int wmi_tx_eapol(struct wil6210_priv *wil, struct sk_buff *skb); int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index, const void *mac_addr); int wmi_add_cipher_key(struct wil6210_priv *wil, u8 key_index, diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index ac4d26b34097..dc8059ad4bab 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -839,40 +839,6 @@ int wmi_p2p_cfg(struct wil6210_priv *wil, int channel) return wmi_send(wil, WMI_P2P_CFG_CMDID, &cmd, sizeof(cmd)); } -int wmi_tx_eapol(struct wil6210_priv *wil, struct sk_buff *skb) -{ - struct wmi_eapol_tx_cmd *cmd; - struct ethhdr *eth; - u16 eapol_len = skb->len - ETH_HLEN; - void *eapol = skb->data + ETH_HLEN; - uint i; - int rc; - - skb_set_mac_header(skb, 0); - eth = eth_hdr(skb); - wil_dbg_wmi(wil, "EAPOL %d bytes to %pM\n", eapol_len, eth->h_dest); - for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) { - if (memcmp(wil->dst_addr[i], eth->h_dest, ETH_ALEN) == 0) - goto found_dest; - } - - return -EINVAL; - - found_dest: - /* find out eapol data & len */ - cmd = kzalloc(sizeof(*cmd) + eapol_len, GFP_KERNEL); - if (!cmd) - return -EINVAL; - - memcpy(cmd->dst_mac, eth->h_dest, ETH_ALEN); - cmd->eapol_len = cpu_to_le16(eapol_len); - memcpy(cmd->eapol, eapol, eapol_len); - rc = wmi_send(wil, WMI_EAPOL_TX_CMDID, cmd, sizeof(*cmd) + eapol_len); - kfree(cmd); - - return rc; -} - int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index, const void *mac_addr) { -- cgit v1.2.3 From e31b25627f6ddcc0905d552c3a9642b1060657bb Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Sun, 9 Jun 2013 09:12:55 +0300 Subject: wil6210: Init Rx vring right after reset at the vring initialisation, memory pool get allocated in the FW. Make it 1-st because FW need this memory pool to precess next commands Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/cfg80211.c | 7 +++++-- drivers/net/wireless/ath/wil6210/main.c | 8 +++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 189c3078ef7d..61c302a6bdea 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -454,6 +454,11 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy, if (rc) return rc; + /* Rx VRING. */ + rc = wil_rx_init(wil); + if (rc) + return rc; + rc = wmi_set_ssid(wil, info->ssid_len, info->ssid); if (rc) return rc; @@ -482,8 +487,6 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy, if (rc) return rc; - /* Rx VRING. After MAC and beacon */ - rc = wil_rx_init(wil); netif_carrier_on(ndev); diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index c97b864667c5..8cafa456398a 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -295,6 +295,11 @@ static int __wil_up(struct wil6210_priv *wil) if (rc) return rc; + /* Rx VRING. After MAC and beacon */ + rc = wil_rx_init(wil); + if (rc) + return rc; + /* FIXME Firmware works now in PBSS mode(ToDS=0, FromDS=0) */ wmi_nettype = wil_iftype_nl2wmi(NL80211_IFTYPE_ADHOC); switch (wdev->iftype) { @@ -356,9 +361,6 @@ static int __wil_up(struct wil6210_priv *wil) return rc; } - /* Rx VRING. After MAC and beacon */ - wil_rx_init(wil); - napi_enable(&wil->napi_rx); napi_enable(&wil->napi_tx); -- cgit v1.2.3 From eb4928cfc6263c3377d049a41830e36d1af15b31 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Sun, 9 Jun 2013 13:10:05 +0200 Subject: wil6210: fix name of tracing config option Tracing in wil6210 is activated with WIL6210_TRACING and not with ATH6KL_TRACING, this is used for the ath6kl driver. Rename the config option. Signed-off-by: Hauke Mehrtens Acked-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/wil6210/Kconfig b/drivers/net/wireless/ath/wil6210/Kconfig index 5644ac54facc..ce8c0381825e 100644 --- a/drivers/net/wireless/ath/wil6210/Kconfig +++ b/drivers/net/wireless/ath/wil6210/Kconfig @@ -28,7 +28,7 @@ config WIL6210_ISR_COR such monitoring impossible. Say y unless you debug interrupts -config ATH6KL_TRACING +config WIL6210_TRACING bool "wil6210 tracing support" depends on WIL6210 depends on EVENT_TRACING -- cgit v1.2.3 From 4c895f41a750b3f81bf0890d2730ac6b867357f8 Mon Sep 17 00:00:00 2001 From: Kirshenbaum Erez Date: Sun, 9 Jun 2013 17:35:28 +0300 Subject: wil6210: Fix AP/PCP start flow WMI PCP Start flow should not be handled through: net_device_ops->ndo_open()->wil_up()->__wil_up() because it missing mandatory FW parameters (SSID,Channel,IEs, Security...). Prior to AP starting __wil_up() may be called with iftype set cfg80211_ops->change_virtual_intf(NL80211_IFTYPE_AP or STATION) depend on the application hostapd/wpa_supplicant/iw. there should not be an attempt to start an AP flow, AP/PCP start flow will be started latter by cfg80211_ops->start_ap(). Signed-off-by: Kirshenbaum Erez Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/main.c | 34 --------------------------------- 1 file changed, 34 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 8cafa456398a..0a2844c48a60 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -286,10 +286,7 @@ static int __wil_up(struct wil6210_priv *wil) { struct net_device *ndev = wil_to_ndev(wil); struct wireless_dev *wdev = wil->wdev; - struct ieee80211_channel *channel = wdev->preset_chandef.chan; int rc; - int bi; - u16 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype); rc = wil_reset(wil); if (rc) @@ -300,32 +297,25 @@ static int __wil_up(struct wil6210_priv *wil) if (rc) return rc; - /* FIXME Firmware works now in PBSS mode(ToDS=0, FromDS=0) */ - wmi_nettype = wil_iftype_nl2wmi(NL80211_IFTYPE_ADHOC); switch (wdev->iftype) { case NL80211_IFTYPE_STATION: wil_dbg_misc(wil, "type: STATION\n"); - bi = 0; ndev->type = ARPHRD_ETHER; break; case NL80211_IFTYPE_AP: wil_dbg_misc(wil, "type: AP\n"); - bi = 100; ndev->type = ARPHRD_ETHER; break; case NL80211_IFTYPE_P2P_CLIENT: wil_dbg_misc(wil, "type: P2P_CLIENT\n"); - bi = 0; ndev->type = ARPHRD_ETHER; break; case NL80211_IFTYPE_P2P_GO: wil_dbg_misc(wil, "type: P2P_GO\n"); - bi = 100; ndev->type = ARPHRD_ETHER; break; case NL80211_IFTYPE_MONITOR: wil_dbg_misc(wil, "type: Monitor\n"); - bi = 0; ndev->type = ARPHRD_IEEE80211_RADIOTAP; /* ARPHRD_IEEE80211 or ARPHRD_IEEE80211_RADIOTAP ? */ break; @@ -333,33 +323,9 @@ static int __wil_up(struct wil6210_priv *wil) return -EOPNOTSUPP; } - /* Apply profile in the following order: */ - /* SSID and channel for the AP */ - switch (wdev->iftype) { - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_P2P_GO: - if (wdev->ssid_len == 0) { - wil_err(wil, "SSID not set\n"); - return -EINVAL; - } - rc = wmi_set_ssid(wil, wdev->ssid_len, wdev->ssid); - if (rc) - return rc; - break; - default: - break; - } - /* MAC address - pre-requisite for other commands */ wmi_set_mac_address(wil, ndev->dev_addr); - /* Set up beaconing if required. */ - if (bi > 0) { - rc = wmi_pcp_start(wil, bi, wmi_nettype, - (channel ? channel->hw_value : 0)); - if (rc) - return rc; - } napi_enable(&wil->napi_rx); napi_enable(&wil->napi_tx); -- cgit v1.2.3 From a226c3d96d920ba88a28f463ba77c9693988ff1e Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Sun, 9 Jun 2013 18:51:24 +0200 Subject: ath9k_htc: add STBC TX support All known ar7010+ar* device and current FW support STBC TX. This patch make use of it and suggest to send STBC if peer support it. I use wort "suggest" since currenly we have separate rate controller in FW which will make decision based on rate and hardware. Signed-off-by: Oleksij Rempel Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/htc.h | 1 + drivers/net/wireless/ath/ath9k/htc_drv_init.c | 3 +++ drivers/net/wireless/ath/ath9k/htc_drv_main.c | 2 ++ 3 files changed, 6 insertions(+) diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h index 0085e643132f..69581031f2cd 100644 --- a/drivers/net/wireless/ath/ath9k/htc.h +++ b/drivers/net/wireless/ath/ath9k/htc.h @@ -142,6 +142,7 @@ struct ath9k_htc_target_aggr { #define WLAN_RC_40_FLAG 0x02 #define WLAN_RC_SGI_FLAG 0x04 #define WLAN_RC_HT_FLAG 0x08 +#define ATH_RC_TX_STBC_FLAG 0x20 struct ath9k_htc_rateset { u8 rs_nrates; diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index 59f64367e8ca..bb0ba9e3e083 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -517,6 +517,9 @@ static void setup_ht_cap(struct ath9k_htc_priv *priv, ath_dbg(common, CONFIG, "TX streams %d, RX streams: %d\n", tx_streams, rx_streams); + if (tx_streams >= 2) + ht_info->cap |= IEEE80211_HT_CAP_TX_STBC; + if (tx_streams != rx_streams) { ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF; ht_info->mcs.tx_params |= ((tx_streams - 1) << diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index 34869c2405aa..eaa94feb4333 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -627,6 +627,8 @@ static void ath9k_htc_setup_rate(struct ath9k_htc_priv *priv, trate->rates.ht_rates.rs_nrates = j; caps = WLAN_RC_HT_FLAG; + if (sta->ht_cap.cap & IEEE80211_HT_CAP_RX_STBC) + caps |= ATH_RC_TX_STBC_FLAG; if (sta->ht_cap.mcs.rx_mask[1]) caps |= WLAN_RC_DS_FLAG; if ((sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) && -- cgit v1.2.3 From 693026ef2e751fd94d2e6c71028e68343cc875d5 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Sun, 9 Jun 2013 18:53:58 +0200 Subject: b43: ensue that BCMA is "y" when B43 is "y" When b43 gets build into the kernel and it should use bcma we have to ensure that bcma was also build into the kernel and not as a module. In this patch this is also done for SSB, although you can not build b43 without ssb support for now. This fixes a build problem reported by Randy Dunlap in 5187EB95.2060605@infradead.org Reported-By: Randy Dunlap Cc: Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/net/wireless/b43/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/b43/Kconfig b/drivers/net/wireless/b43/Kconfig index 078e6f3477a9..13f91ac9499e 100644 --- a/drivers/net/wireless/b43/Kconfig +++ b/drivers/net/wireless/b43/Kconfig @@ -28,7 +28,7 @@ config B43 config B43_BCMA bool "Support for BCMA bus" - depends on B43 && BCMA + depends on B43 && (BCMA = y || BCMA = B43) default y config B43_BCMA_EXTRA @@ -39,7 +39,7 @@ config B43_BCMA_EXTRA config B43_SSB bool - depends on B43 && SSB + depends on B43 && (SSB = y || SSB = B43) default y # Auto-select SSB PCI-HOST support, if possible -- cgit v1.2.3 From d186899fa55ae773ae26cf57aa274c50de4c508c Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Sun, 9 Jun 2013 18:59:42 +0200 Subject: bcma: activate PCI host option by default Most users are using bcma with a PCIe card, activate support for this by default. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/bcma/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/bcma/Kconfig b/drivers/bcma/Kconfig index 8b4221cfd118..380a2003231e 100644 --- a/drivers/bcma/Kconfig +++ b/drivers/bcma/Kconfig @@ -26,6 +26,7 @@ config BCMA_HOST_PCI_POSSIBLE config BCMA_HOST_PCI bool "Support for BCMA on PCI-host bus" depends on BCMA_HOST_PCI_POSSIBLE + default y config BCMA_DRIVER_PCI_HOSTMODE bool "Driver for PCI core working in hostmode" -- cgit v1.2.3 From 6b692f3b66e4a70dff73b3cdc60f7e6d6823300a Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Sun, 9 Jun 2013 18:59:43 +0200 Subject: b43: activate N-PHY and HT-PHY support by default N-PHY and HT-PHY support is more or less stable and should be activated by default. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/net/wireless/b43/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/b43/Kconfig b/drivers/net/wireless/b43/Kconfig index 13f91ac9499e..3f21e0ba39ba 100644 --- a/drivers/net/wireless/b43/Kconfig +++ b/drivers/net/wireless/b43/Kconfig @@ -111,6 +111,7 @@ config B43_PIO config B43_PHY_N bool "Support for 802.11n (N-PHY) devices" depends on B43 + default y ---help--- Support for the N-PHY. @@ -132,6 +133,7 @@ config B43_PHY_LP config B43_PHY_HT bool "Support for HT-PHY (high throughput) devices" depends on B43 && B43_BCMA + default y ---help--- Support for the HT-PHY. -- cgit v1.2.3 From 30d5b709da23f4ab9836c7f66d2d2e780a69cf12 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 10 Jun 2013 13:49:38 +0530 Subject: ath9k_hw: Assign default xlna config for AR9485 For AR9485 boards with XLNA, the default gpio config is not set correctly, fix this. Cc: stable@vger.kernel.org Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_eeprom.c | 8 ++++++-- drivers/net/wireless/ath/ath9k/ar9003_phy.h | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c index e6b92ff265fd..25b8bbbe74fe 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c @@ -3563,14 +3563,18 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz) { struct ath9k_hw_capabilities *pCap = &ah->caps; int chain; - u32 regval; + u32 regval, value; static const u32 switch_chain_reg[AR9300_MAX_CHAINS] = { AR_PHY_SWITCH_CHAIN_0, AR_PHY_SWITCH_CHAIN_1, AR_PHY_SWITCH_CHAIN_2, }; - u32 value = ar9003_hw_ant_ctrl_common_get(ah, is2ghz); + if (AR_SREV_9485(ah) && (ar9003_hw_get_rx_gain_idx(ah) == 0)) + ath9k_hw_cfg_output(ah, AR9300_EXT_LNA_CTL_GPIO_AR9485, + AR_GPIO_OUTPUT_MUX_AS_PCIE_ATTENTION_LED); + + value = ar9003_hw_ant_ctrl_common_get(ah, is2ghz); if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) { REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM, diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.h b/drivers/net/wireless/ath/ath9k/ar9003_phy.h index e71774196c01..5013c731f9f6 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.h @@ -351,6 +351,8 @@ #define AR_PHY_CCA_NOM_VAL_9330_2GHZ -118 +#define AR9300_EXT_LNA_CTL_GPIO_AR9485 9 + /* * AGC Field Definitions */ -- cgit v1.2.3 From 696df78509d1f81b651dd98ecdc1aecab616db6b Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 10 Jun 2013 13:49:39 +0530 Subject: ath9k: Fix noisefloor calibration The commits, "ath9k: Fix regression in channelwidth switch at the same channel" "ath9k: Fix invalid noisefloor reading due to channel update" attempted to fix noisefloor calibration when a channel switch happens due to HT20/HT40 bandwidth change. This is causing invalid readings resulting in messages like: "ath: phy16: NF[0] (-45) > MAX (-95), correcting to MAX". This results in an incorrect noise being used initially for reporting the signal level of received packets, until NF calibration is done and the history buffer is updated via the ANI timer, which happens much later. When a bandwidth change happens, it is appropriate to reset the internal history data for the channel. Do this correctly in the reset() routine by checking the "chanmode" variable. Cc: stable@vger.kernel.org Cc: Rajkumar Manoharan Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/hw.c | 3 ++- drivers/net/wireless/ath/ath9k/main.c | 7 ------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index d813ab8104d6..ca9d9cd14ddb 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -1870,7 +1870,8 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, ah->caldata = caldata; if (caldata && (chan->channel != caldata->channel || - chan->channelFlags != caldata->channelFlags)) { + chan->channelFlags != caldata->channelFlags || + chan->chanmode != caldata->chanmode)) { /* Operating channel changed, reset channel calibration data */ memset(caldata, 0, sizeof(*caldata)); ath9k_init_nfcal_hist_buffer(ah, chan); diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index bf61a1c3f14c..1737a3e33685 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1210,13 +1210,6 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) ath_update_survey_stats(sc); spin_unlock_irqrestore(&common->cc_lock, flags); - /* - * Preserve the current channel values, before updating - * the same channel - */ - if (ah->curchan && (old_pos == pos)) - ath9k_hw_getnf(ah, ah->curchan); - ath9k_cmn_update_ichannel(&sc->sc_ah->channels[pos], curchan, channel_type); -- cgit v1.2.3 From d3bcb7b24bbf09fde8405770e676fe0c11c79662 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 10 Jun 2013 13:49:40 +0530 Subject: ath9k: Do not assign noise for NULL caldata ah->noise is maintained globally and not per-channel. This is updated in the reset() routine after the NF history has been filled for the *current channel*, just before switching to the new channel. There is no need to do it inside getnf(), since ah->noise must contain a value for the new channel. Cc: stable@vger.kernel.org Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/calib.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c index 7304e7585009..5e8219a91e25 100644 --- a/drivers/net/wireless/ath/ath9k/calib.c +++ b/drivers/net/wireless/ath/ath9k/calib.c @@ -387,7 +387,6 @@ bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan) if (!caldata) { chan->noisefloor = nf; - ah->noise = ath9k_hw_getchan_noise(ah, chan); return false; } -- cgit v1.2.3 From 0967e01e8e713ed2982fb4eba8ba13794e9a6e89 Mon Sep 17 00:00:00 2001 From: Thomas Huehn Date: Tue, 11 Jun 2013 15:10:31 +0200 Subject: ath5k: make use of the new rate control API This patch enabels ath5k to use the new rate table to lookup each mrr rate and retry information per packet. Signed-off-by: Benjamin Vahl Signed-off-by: Thomas Huehn Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/base.c | 79 ++++++++++++++++++++++----- drivers/net/wireless/ath/ath5k/base.h | 14 +++-- drivers/net/wireless/ath/ath5k/mac80211-ops.c | 2 +- 3 files changed, 74 insertions(+), 21 deletions(-) diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 7f702fe3ecc2..ce67ab791eae 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -60,6 +60,7 @@ #include +#include #include "base.h" #include "reg.h" #include "debug.h" @@ -666,9 +667,46 @@ static enum ath5k_pkt_type get_hw_packet_type(struct sk_buff *skb) return htype; } +static struct ieee80211_rate * +ath5k_get_rate(const struct ieee80211_hw *hw, + const struct ieee80211_tx_info *info, + struct ath5k_buf *bf, int idx) +{ + /* + * convert a ieee80211_tx_rate RC-table entry to + * the respective ieee80211_rate struct + */ + if (bf->rates[idx].idx < 0) { + return NULL; + } + + return &hw->wiphy->bands[info->band]->bitrates[ bf->rates[idx].idx ]; +} + +static u16 +ath5k_get_rate_hw_value(const struct ieee80211_hw *hw, + const struct ieee80211_tx_info *info, + struct ath5k_buf *bf, int idx) +{ + struct ieee80211_rate *rate; + u16 hw_rate; + u8 rc_flags; + + rate = ath5k_get_rate(hw, info, bf, idx); + if (!rate) + return 0; + + rc_flags = bf->rates[idx].flags; + hw_rate = (rc_flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) ? + rate->hw_value_short : rate->hw_value; + + return hw_rate; +} + static int ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf, - struct ath5k_txq *txq, int padsize) + struct ath5k_txq *txq, int padsize, + struct ieee80211_tx_control *control) { struct ath5k_desc *ds = bf->desc; struct sk_buff *skb = bf->skb; @@ -688,7 +726,11 @@ ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf, bf->skbaddr = dma_map_single(ah->dev, skb->data, skb->len, DMA_TO_DEVICE); - rate = ieee80211_get_tx_rate(ah->hw, info); + ieee80211_get_tx_rates(info->control.vif, (control) ? control->sta : NULL, skb, bf->rates, + ARRAY_SIZE(bf->rates)); + + rate = ath5k_get_rate(ah->hw, info, bf, 0); + if (!rate) { ret = -EINVAL; goto err_unmap; @@ -698,8 +740,8 @@ ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf, flags |= AR5K_TXDESC_NOACK; rc_flags = info->control.rates[0].flags; - hw_rate = (rc_flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) ? - rate->hw_value_short : rate->hw_value; + + hw_rate = ath5k_get_rate_hw_value(ah->hw, info, bf, 0); pktlen = skb->len; @@ -722,12 +764,13 @@ ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf, duration = le16_to_cpu(ieee80211_ctstoself_duration(ah->hw, info->control.vif, pktlen, info)); } + ret = ah->ah_setup_tx_desc(ah, ds, pktlen, ieee80211_get_hdrlen_from_skb(skb), padsize, get_hw_packet_type(skb), (ah->ah_txpower.txp_requested * 2), hw_rate, - info->control.rates[0].count, keyidx, ah->ah_tx_ant, flags, + bf->rates[0].count, keyidx, ah->ah_tx_ant, flags, cts_rate, duration); if (ret) goto err_unmap; @@ -736,13 +779,15 @@ ath5k_txbuf_setup(struct ath5k_hw *ah, struct ath5k_buf *bf, if (ah->ah_capabilities.cap_has_mrr_support) { memset(mrr_rate, 0, sizeof(mrr_rate)); memset(mrr_tries, 0, sizeof(mrr_tries)); + for (i = 0; i < 3; i++) { - rate = ieee80211_get_alt_retry_rate(ah->hw, info, i); + + rate = ath5k_get_rate(ah->hw, info, bf, i); if (!rate) break; - mrr_rate[i] = rate->hw_value; - mrr_tries[i] = info->control.rates[i + 1].count; + mrr_rate[i] = ath5k_get_rate_hw_value(ah->hw, info, bf, i); + mrr_tries[i] = bf->rates[i].count; } ath5k_hw_setup_mrr_tx_desc(ah, ds, @@ -1515,7 +1560,7 @@ unlock: void ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb, - struct ath5k_txq *txq) + struct ath5k_txq *txq, struct ieee80211_tx_control *control) { struct ath5k_hw *ah = hw->priv; struct ath5k_buf *bf; @@ -1555,7 +1600,7 @@ ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb, bf->skb = skb; - if (ath5k_txbuf_setup(ah, bf, txq, padsize)) { + if (ath5k_txbuf_setup(ah, bf, txq, padsize, control)) { bf->skb = NULL; spin_lock_irqsave(&ah->txbuflock, flags); list_add_tail(&bf->list, &ah->txbuf); @@ -1571,11 +1616,13 @@ drop_packet: static void ath5k_tx_frame_completed(struct ath5k_hw *ah, struct sk_buff *skb, - struct ath5k_txq *txq, struct ath5k_tx_status *ts) + struct ath5k_txq *txq, struct ath5k_tx_status *ts, + struct ath5k_buf *bf) { struct ieee80211_tx_info *info; u8 tries[3]; int i; + int size = 0; ah->stats.tx_all_count++; ah->stats.tx_bytes_count += skb->len; @@ -1587,6 +1634,9 @@ ath5k_tx_frame_completed(struct ath5k_hw *ah, struct sk_buff *skb, ieee80211_tx_info_clear_status(info); + size = min_t(int, sizeof(info->status.rates), sizeof(bf->rates)); + memcpy(info->status.rates, bf->rates, size); + for (i = 0; i < ts->ts_final_idx; i++) { struct ieee80211_tx_rate *r = &info->status.rates[i]; @@ -1663,7 +1713,7 @@ ath5k_tx_processq(struct ath5k_hw *ah, struct ath5k_txq *txq) dma_unmap_single(ah->dev, bf->skbaddr, skb->len, DMA_TO_DEVICE); - ath5k_tx_frame_completed(ah, skb, txq, &ts); + ath5k_tx_frame_completed(ah, skb, txq, &ts, bf); } /* @@ -1917,7 +1967,7 @@ ath5k_beacon_send(struct ath5k_hw *ah) skb = ieee80211_get_buffered_bc(ah->hw, vif); while (skb) { - ath5k_tx_queue(ah->hw, skb, ah->cabq); + ath5k_tx_queue(ah->hw, skb, ah->cabq, NULL); if (ah->cabq->txq_len >= ah->cabq->txq_max) break; @@ -2442,7 +2492,8 @@ ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops) IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_MFP_CAPABLE | - IEEE80211_HW_REPORTS_TX_ACK_STATUS; + IEEE80211_HW_REPORTS_TX_ACK_STATUS | + IEEE80211_HW_SUPPORTS_RC_TABLE; hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_AP) | diff --git a/drivers/net/wireless/ath/ath5k/base.h b/drivers/net/wireless/ath/ath5k/base.h index 6c94c7ff2350..ca9a83ceeee1 100644 --- a/drivers/net/wireless/ath/ath5k/base.h +++ b/drivers/net/wireless/ath/ath5k/base.h @@ -47,6 +47,7 @@ struct ath5k_hw; struct ath5k_txq; struct ieee80211_channel; struct ath_bus_ops; +struct ieee80211_tx_control; enum nl80211_iftype; enum ath5k_srev_type { @@ -61,11 +62,12 @@ struct ath5k_srev_name { }; struct ath5k_buf { - struct list_head list; - struct ath5k_desc *desc; /* virtual addr of desc */ - dma_addr_t daddr; /* physical addr of desc */ - struct sk_buff *skb; /* skbuff for buf */ - dma_addr_t skbaddr;/* physical addr of skb data */ + struct list_head list; + struct ath5k_desc *desc; /* virtual addr of desc */ + dma_addr_t daddr; /* physical addr of desc */ + struct sk_buff *skb; /* skbuff for buf */ + dma_addr_t skbaddr; /* physical addr of skb data */ + struct ieee80211_tx_rate rates[4]; /* number of multi-rate stages */ }; struct ath5k_vif { @@ -103,7 +105,7 @@ int ath5k_chan_set(struct ath5k_hw *ah, struct ieee80211_channel *chan); void ath5k_txbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf); void ath5k_rxbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf); void ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb, - struct ath5k_txq *txq); + struct ath5k_txq *txq, struct ieee80211_tx_control *control); const char *ath5k_chip_name(enum ath5k_srev_type type, u_int16_t val); diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c index 06f86f435711..81b686c6a376 100644 --- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c +++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c @@ -66,7 +66,7 @@ ath5k_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, return; } - ath5k_tx_queue(hw, skb, &ah->txqs[qnum]); + ath5k_tx_queue(hw, skb, &ah->txqs[qnum], control); } -- cgit v1.2.3 From 42ce8943e14734dd388e6934fff31a11c1adad8f Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Wed, 12 Jun 2013 16:44:47 +0200 Subject: iwlegacy: small refactoring of il_{stop,wake}_queue Tested-by: Jake Edge Signed-off-by: Stanislaw Gruszka Signed-off-by: John W. Linville --- drivers/net/wireless/iwlegacy/common.h | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/iwlegacy/common.h b/drivers/net/wireless/iwlegacy/common.h index 4caaf52986a4..67da89b39e11 100644 --- a/drivers/net/wireless/iwlegacy/common.h +++ b/drivers/net/wireless/iwlegacy/common.h @@ -2256,6 +2256,19 @@ il_set_swq_id(struct il_tx_queue *txq, u8 ac, u8 hwq) txq->swq_id = (hwq << 2) | ac; } +static inline void +_il_wake_queue(struct il_priv *il, u8 ac) +{ + if (atomic_dec_return(&il->queue_stop_count[ac]) <= 0) + ieee80211_wake_queue(il->hw, ac); +} + +static inline void +_il_stop_queue(struct il_priv *il, u8 ac) +{ + if (atomic_inc_return(&il->queue_stop_count[ac]) > 0) + ieee80211_stop_queue(il->hw, ac); +} static inline void il_wake_queue(struct il_priv *il, struct il_tx_queue *txq) { @@ -2264,8 +2277,7 @@ il_wake_queue(struct il_priv *il, struct il_tx_queue *txq) u8 hwq = (queue >> 2) & 0x1f; if (test_and_clear_bit(hwq, il->queue_stopped)) - if (atomic_dec_return(&il->queue_stop_count[ac]) <= 0) - ieee80211_wake_queue(il->hw, ac); + _il_wake_queue(il, ac); } static inline void @@ -2276,8 +2288,7 @@ il_stop_queue(struct il_priv *il, struct il_tx_queue *txq) u8 hwq = (queue >> 2) & 0x1f; if (!test_and_set_bit(hwq, il->queue_stopped)) - if (atomic_inc_return(&il->queue_stop_count[ac]) > 0) - ieee80211_stop_queue(il->hw, ac); + _il_stop_queue(il, ac); } #ifdef ieee80211_stop_queue -- cgit v1.2.3 From c6af8074fd3654b2a8d7b31304d686519b062e14 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Wed, 12 Jun 2013 16:44:48 +0200 Subject: iwlegacy: add il_{stop,wake}_queues_by_reason functions Add functions that will stop/wake all queues. Make them safe regarding multiple calls and when some ac are stopped/woke independently. Tested-by: Jake Edge Signed-off-by: Stanislaw Gruszka Signed-off-by: John W. Linville --- drivers/net/wireless/iwlegacy/common.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/net/wireless/iwlegacy/common.h b/drivers/net/wireless/iwlegacy/common.h index 67da89b39e11..83f8ed8a5528 100644 --- a/drivers/net/wireless/iwlegacy/common.h +++ b/drivers/net/wireless/iwlegacy/common.h @@ -1299,6 +1299,8 @@ struct il_priv { /* queue refcounts */ #define IL_MAX_HW_QUEUES 32 unsigned long queue_stopped[BITS_TO_LONGS(IL_MAX_HW_QUEUES)]; +#define IL_STOP_REASON_PASSIVE 0 + unsigned long stop_reason; /* for each AC */ atomic_t queue_stop_count[4]; @@ -2291,6 +2293,26 @@ il_stop_queue(struct il_priv *il, struct il_tx_queue *txq) _il_stop_queue(il, ac); } +static inline void +il_wake_queues_by_reason(struct il_priv *il, int reason) +{ + u8 ac; + + if (test_and_clear_bit(reason, &il->stop_reason)) + for (ac = 0; ac < 4; ac++) + _il_wake_queue(il, ac); +} + +static inline void +il_stop_queues_by_reason(struct il_priv *il, int reason) +{ + u8 ac; + + if (!test_and_set_bit(reason, &il->stop_reason)) + for (ac = 0; ac < 4; ac++) + _il_stop_queue(il, ac); +} + #ifdef ieee80211_stop_queue #undef ieee80211_stop_queue #endif -- cgit v1.2.3 From 8cdbab7f07e82f26c48adcc761391c1c7ff339ff Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Wed, 12 Jun 2013 16:44:49 +0200 Subject: iwl4965: workaround for firmware frame tx rejection Firmware can reject to transmit frame on passive channel, when it did not yet received any frame with valid CRC on that channel. Workaround this problem in the driver. Tested-by: Jake Edge Signed-off-by: Stanislaw Gruszka Signed-off-by: John W. Linville --- drivers/net/wireless/iwlegacy/4965-mac.c | 18 ++++++++++++++++++ drivers/net/wireless/iwlegacy/common.c | 11 +++++++++++ 2 files changed, 29 insertions(+) diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c index 9a95045c97b6..1c44bb59f6c8 100644 --- a/drivers/net/wireless/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/iwlegacy/4965-mac.c @@ -588,6 +588,11 @@ il4965_pass_packet_to_mac80211(struct il_priv *il, struct ieee80211_hdr *hdr, return; } + if (unlikely(test_bit(IL_STOP_REASON_PASSIVE, &il->stop_reason))) { + il_wake_queues_by_reason(il, IL_STOP_REASON_PASSIVE); + D_INFO("Woke queues - frame received on passive channel\n"); + } + /* In case of HW accelerated crypto and bad decryption, drop */ if (!il->cfg->mod_params->sw_crypto && il_set_decrypted_flag(il, hdr, ampdu_status, stats)) @@ -2806,6 +2811,19 @@ il4965_hdl_tx(struct il_priv *il, struct il_rx_buf *rxb) return; } + /* + * Firmware will not transmit frame on passive channel, if it not yet + * received some valid frame on that channel. When this error happen + * we have to wait until firmware will unblock itself i.e. when we + * note received beacon or other frame. We unblock queues in + * il4965_pass_packet_to_mac80211 or in il_mac_bss_info_changed. + */ + if (unlikely((status & TX_STATUS_MSK) == TX_STATUS_FAIL_PASSIVE_NO_RX) && + il->iw_mode == NL80211_IFTYPE_STATION) { + il_stop_queues_by_reason(il, IL_STOP_REASON_PASSIVE); + D_INFO("Stopped queues - RX waiting on passive channel\n"); + } + spin_lock_irqsave(&il->sta_lock, flags); if (txq->sched_retry) { const u32 scd_ssn = il4965_get_scd_ssn(tx_resp); diff --git a/drivers/net/wireless/iwlegacy/common.c b/drivers/net/wireless/iwlegacy/common.c index e9a3cbc409ae..3195aad440dd 100644 --- a/drivers/net/wireless/iwlegacy/common.c +++ b/drivers/net/wireless/iwlegacy/common.c @@ -5306,6 +5306,17 @@ il_mac_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, if (changes & BSS_CHANGED_BSSID) { D_MAC80211("BSSID %pM\n", bss_conf->bssid); + /* + * On passive channel we wait with blocked queues to see if + * there is traffic on that channel. If no frame will be + * received (what is very unlikely since scan detects AP on + * that channel, but theoretically possible), mac80211 associate + * procedure will time out and mac80211 will call us with NULL + * bssid. We have to unblock queues on such condition. + */ + if (is_zero_ether_addr(bss_conf->bssid)) + il_wake_queues_by_reason(il, IL_STOP_REASON_PASSIVE); + /* * If there is currently a HW scan going on in the background, * then we need to cancel it, otherwise sometimes we are not -- cgit v1.2.3 From c72456c75ae48d4d747c2ad4f7fe82601484a949 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Wed, 12 Jun 2013 16:44:50 +0200 Subject: iwl3945: workaround for firmware frame tx rejection Firmware can reject to transmit frame on passive channel, when it did not yet received any frame with valid CRC on that channel. Workaround this problem in the driver. Signed-off-by: Stanislaw Gruszka Signed-off-by: John W. Linville --- drivers/net/wireless/iwlegacy/3945.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/net/wireless/iwlegacy/3945.c b/drivers/net/wireless/iwlegacy/3945.c index dc1e6da9976a..c092033945cc 100644 --- a/drivers/net/wireless/iwlegacy/3945.c +++ b/drivers/net/wireless/iwlegacy/3945.c @@ -331,6 +331,19 @@ il3945_hdl_tx(struct il_priv *il, struct il_rx_buf *rxb) return; } + /* + * Firmware will not transmit frame on passive channel, if it not yet + * received some valid frame on that channel. When this error happen + * we have to wait until firmware will unblock itself i.e. when we + * note received beacon or other frame. We unblock queues in + * il3945_pass_packet_to_mac80211 or in il_mac_bss_info_changed. + */ + if (unlikely((status & TX_STATUS_MSK) == TX_STATUS_FAIL_PASSIVE_NO_RX) && + il->iw_mode == NL80211_IFTYPE_STATION) { + il_stop_queues_by_reason(il, IL_STOP_REASON_PASSIVE); + D_INFO("Stopped queues - RX waiting on passive channel\n"); + } + txq->time_stamp = jiffies; info = IEEE80211_SKB_CB(txq->skbs[txq->q.read_ptr]); ieee80211_tx_info_clear_status(info); @@ -488,6 +501,11 @@ il3945_pass_packet_to_mac80211(struct il_priv *il, struct il_rx_buf *rxb, return; } + if (unlikely(test_bit(IL_STOP_REASON_PASSIVE, &il->stop_reason))) { + il_wake_queues_by_reason(il, IL_STOP_REASON_PASSIVE); + D_INFO("Woke queues - frame received on passive channel\n"); + } + skb = dev_alloc_skb(128); if (!skb) { IL_ERR("dev_alloc_skb failed\n"); -- cgit v1.2.3 From d5346171d721bcfaab5843bf996ac6a379f7bd93 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Wed, 12 Jun 2013 16:48:25 +0200 Subject: Revert "iwl4965: workaround connection regression on passive channel" This reverts commit dd9c46408fdc07098333655ff27edf8cac8d9fcf. With "iwl{4965,3495): workaround for firmware frame tx rejection" patches we can enable IEEE80211_HW_REPORTS_TX_ACK_STATUS again. Tested-by: Jake Edge Signed-off-by: Stanislaw Gruszka Signed-off-by: John W. Linville --- drivers/net/wireless/iwlegacy/4965-mac.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c index 1c44bb59f6c8..d287fd2dce42 100644 --- a/drivers/net/wireless/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/iwlegacy/4965-mac.c @@ -5759,7 +5759,8 @@ il4965_mac_setup_register(struct il_priv *il, u32 max_probe_length) hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_AMPDU_AGGREGATION | IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC | IEEE80211_HW_SPECTRUM_MGMT | - IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_DYNAMIC_PS; + IEEE80211_HW_REPORTS_TX_ACK_STATUS | IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_SUPPORTS_DYNAMIC_PS; if (il->cfg->sku & IL_SKU_N) hw->flags |= IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS | -- cgit v1.2.3 From fa587d4b2f968b3719190a668a8707989855228d Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Thu, 6 Jun 2013 13:18:03 +0200 Subject: brcmfmac: Always use fifo_credits, also for requested credits. Currently firmware requested credits do not require fifo credits. From a buffer management point of view this is incorrect. So firwmware requested credits require also fifo credits before the packet can be transferred to the host. Reviewed-by: Arend Van Spriel Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c | 10 +- drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h | 3 +- drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 163 +++++++++------------ 3 files changed, 79 insertions(+), 97 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c index 202869cd0932..ce50686a86c8 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c @@ -179,11 +179,11 @@ ssize_t brcmf_debugfs_fws_stats_read(struct file *f, char __user *data, fwstats->send_pkts[0], fwstats->send_pkts[1], fwstats->send_pkts[2], fwstats->send_pkts[3], fwstats->send_pkts[4], - fwstats->fifo_credits_sent[0], - fwstats->fifo_credits_sent[1], - fwstats->fifo_credits_sent[2], - fwstats->fifo_credits_sent[3], - fwstats->fifo_credits_sent[4]); + fwstats->requested_sent[0], + fwstats->requested_sent[1], + fwstats->requested_sent[2], + fwstats->requested_sent[3], + fwstats->requested_sent[4]); return simple_read_from_buffer(data, count, ppos, buf, res); } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h index 009c87bfd9ae..451ebac09520 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h @@ -141,8 +141,7 @@ struct brcmf_fws_stats { u32 header_pulls; u32 pkt2bus; u32 send_pkts[5]; - u32 fifo_credits_sent[5]; - u32 fifo_credits_back[6]; + u32 requested_sent[5]; u32 generic_error; u32 mac_update_failed; u32 mac_ps_update_failed; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index ff920601898c..79d60a658a67 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -192,24 +192,21 @@ struct brcmf_skbuff_cb { /* * sk_buff control if flags * - * b[12] - packet sent upon credit request. - * b[11] - packet sent upon packet request. + * b[11] - packet sent upon firmware request. * b[10] - packet only contains signalling data. * b[9] - packet is a tx packet. - * b[8] - packet uses FIFO credit (non-pspoll). + * b[8] - packet used requested credit * b[7] - interface in AP mode. * b[3:0] - interface index. */ -#define BRCMF_SKB_IF_FLAGS_REQ_CREDIT_MASK 0x1000 -#define BRCMF_SKB_IF_FLAGS_REQ_CREDIT_SHIFT 12 #define BRCMF_SKB_IF_FLAGS_REQUESTED_MASK 0x0800 #define BRCMF_SKB_IF_FLAGS_REQUESTED_SHIFT 11 #define BRCMF_SKB_IF_FLAGS_SIGNAL_ONLY_MASK 0x0400 #define BRCMF_SKB_IF_FLAGS_SIGNAL_ONLY_SHIFT 10 #define BRCMF_SKB_IF_FLAGS_TRANSMIT_MASK 0x0200 #define BRCMF_SKB_IF_FLAGS_TRANSMIT_SHIFT 9 -#define BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK 0x0100 -#define BRCMF_SKB_IF_FLAGS_CREDITCHECK_SHIFT 8 +#define BRCMF_SKB_IF_FLAGS_REQ_CREDIT_MASK 0x0100 +#define BRCMF_SKB_IF_FLAGS_REQ_CREDIT_SHIFT 8 #define BRCMF_SKB_IF_FLAGS_IF_AP_MASK 0x0080 #define BRCMF_SKB_IF_FLAGS_IF_AP_SHIFT 7 #define BRCMF_SKB_IF_FLAGS_INDEX_MASK 0x000f @@ -311,12 +308,15 @@ enum brcmf_fws_fifo { * firmware suppress the packet as device is already in PS mode. * @BRCMF_FWS_TXSTATUS_FW_TOSSED: * firmware tossed the packet. + * @BRCMF_FWS_TXSTATUS_HOST_TOSSED: + * host tossed the packet. */ enum brcmf_fws_txstatus { BRCMF_FWS_TXSTATUS_DISCARD, BRCMF_FWS_TXSTATUS_CORE_SUPPRESS, BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS, - BRCMF_FWS_TXSTATUS_FW_TOSSED + BRCMF_FWS_TXSTATUS_FW_TOSSED, + BRCMF_FWS_TXSTATUS_HOST_TOSSED }; enum brcmf_fws_fcmode { @@ -639,6 +639,7 @@ static void brcmf_fws_init_mac_descriptor(struct brcmf_fws_mac_descriptor *desc, desc->occupied = 1; desc->state = BRCMF_FWS_STATE_OPEN; desc->requested_credit = 0; + desc->requested_packet = 0; /* depending on use may need ifp->bssidx instead */ desc->interface_id = ifidx; desc->ac_bitmap = 0xff; /* update this when handling APSD */ @@ -654,6 +655,7 @@ void brcmf_fws_clear_mac_descriptor(struct brcmf_fws_mac_descriptor *desc) desc->occupied = 0; desc->state = BRCMF_FWS_STATE_CLOSE; desc->requested_credit = 0; + desc->requested_packet = 0; } static struct brcmf_fws_mac_descriptor * @@ -1050,35 +1052,35 @@ static int brcmf_fws_request_indicate(struct brcmf_fws_info *fws, u8 type, return BRCMF_FWS_RET_OK_SCHEDULE; } -static int brcmf_fws_macdesc_use_credit(struct brcmf_fws_mac_descriptor *entry, - struct sk_buff *skb) +static void +brcmf_fws_macdesc_use_req_credit(struct brcmf_fws_mac_descriptor *entry, + struct sk_buff *skb) { - int use_credit = 1; - - if (entry->state == BRCMF_FWS_STATE_CLOSE) { - if (entry->requested_credit > 0) { - /* - * if the packet was pulled out while destination is in - * closed state but had a non-zero packets requested, - * then this should not count against the FIFO credit. - * That is due to the fact that the firmware will - * most likely hold onto this packet until a suitable - * time later to push it to the appropriate AC FIFO. - */ - entry->requested_credit--; - brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 1); - use_credit = 0; - } else if (entry->requested_packet > 0) { - entry->requested_packet--; - brcmf_skb_if_flags_set_field(skb, REQUESTED, 1); - use_credit = 0; - } + if (entry->requested_credit > 0) { + entry->requested_credit--; + brcmf_skb_if_flags_set_field(skb, REQUESTED, 1); + brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 1); + if (entry->state != BRCMF_FWS_STATE_CLOSE) + brcmf_err("requested credit set while mac not closed!\n"); + } else if (entry->requested_packet > 0) { + entry->requested_packet--; + brcmf_skb_if_flags_set_field(skb, REQUESTED, 1); + brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 0); + if (entry->state != BRCMF_FWS_STATE_CLOSE) + brcmf_err("requested packet set while mac not closed!\n"); } else { - WARN_ON(entry->requested_credit); - WARN_ON(entry->requested_packet); + brcmf_skb_if_flags_set_field(skb, REQUESTED, 0); + brcmf_skb_if_flags_set_field(skb, REQ_CREDIT, 0); } - brcmf_skb_if_flags_set_field(skb, CREDITCHECK, use_credit); - return use_credit; +} + +static void brcmf_fws_macdesc_return_req_credit(struct sk_buff *skb) +{ + struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac; + + if ((brcmf_skb_if_flags_get_field(skb, REQ_CREDIT)) && + (entry->state == BRCMF_FWS_STATE_CLOSE)) + entry->requested_credit++; } static void brcmf_fws_return_credits(struct brcmf_fws_info *fws, @@ -1124,25 +1126,6 @@ static void brcmf_fws_schedule_deq(struct brcmf_fws_info *fws) queue_work(fws->fws_wq, &fws->fws_dequeue_work); } -static void brcmf_fws_skb_pickup_credit(struct brcmf_fws_info *fws, int fifo, - struct sk_buff *p) -{ - struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(p)->mac; - - if (brcmf_skbcb(p)->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK) - brcmf_fws_return_credits(fws, fifo, 1); - else if (brcmf_skb_if_flags_get_field(p, REQ_CREDIT) && - entry->state == BRCMF_FWS_STATE_CLOSE) - /* - * if this packet did not count against FIFO credit, it - * could have taken a requested_credit from the destination - * entry (for pspoll etc.) - */ - entry->requested_credit++; - - brcmf_fws_schedule_deq(fws); -} - static int brcmf_fws_enq(struct brcmf_fws_info *fws, enum brcmf_fws_skb_state state, int fifo, struct sk_buff *p) @@ -1224,7 +1207,7 @@ static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo) if (p == NULL) continue; - brcmf_fws_macdesc_use_credit(entry, p); + brcmf_fws_macdesc_use_req_credit(entry, p); /* move dequeue position to ensure fair round-robin */ fws->deq_node_pos[fifo] = (node_pos + i + 1) % num_nodes; @@ -1313,7 +1296,8 @@ brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot, } else if (flags == BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS) { fws->stats.txs_supp_ps++; remove_from_hanger = false; - } else if (flags == BRCMF_FWS_TXSTATUS_FW_TOSSED) + } else if ((flags == BRCMF_FWS_TXSTATUS_FW_TOSSED) || + (flags == BRCMF_FWS_TXSTATUS_HOST_TOSSED)) fws->stats.txs_tossed++; else brcmf_err("unexpected txstatus\n"); @@ -1340,9 +1324,13 @@ brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot, /* pick up the implicit credit from this packet */ fifo = brcmf_skb_htod_tag_get_field(skb, FIFO); - if (fws->fcmode == BRCMF_FWS_FCMODE_IMPLIED_CREDIT || - !(brcmf_skbcb(skb)->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK)) - brcmf_fws_skb_pickup_credit(fws, fifo, skb); + if ((fws->fcmode == BRCMF_FWS_FCMODE_IMPLIED_CREDIT) || + (brcmf_skb_if_flags_get_field(skb, REQ_CREDIT)) || + (flags == BRCMF_FWS_TXSTATUS_HOST_TOSSED)) { + brcmf_fws_return_credits(fws, fifo, 1); + brcmf_fws_schedule_deq(fws); + } + brcmf_fws_macdesc_return_req_credit(skb); if (!remove_from_hanger) ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, genbit); @@ -1588,7 +1576,7 @@ static int brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo, brcmf_skb_htod_tag_set_field(p, FIFO, fifo); brcmf_skb_htod_tag_set_field(p, GENERATION, entry->generation); flags = BRCMF_FWS_HTOD_FLAG_PKTFROMHOST; - if (!(skcb->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK)) { + if (brcmf_skb_if_flags_get_field(p, REQUESTED)) { /* * Indicate that this packet is being sent in response to an * explicit request from the firmware side. @@ -1636,6 +1624,7 @@ brcmf_fws_rollback_toq(struct brcmf_fws_info *fws, state = brcmf_skbcb(skb)->state; entry = brcmf_skbcb(skb)->mac; + hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); if (entry != NULL) { if (state == BRCMF_FWS_SKBSTATE_SUPPRESSED) { @@ -1647,8 +1636,6 @@ brcmf_fws_rollback_toq(struct brcmf_fws_info *fws, rc = -ENOSPC; } } else { - hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); - /* delay-q packets are going to delay-q */ pktout = brcmu_pktq_penq_head(&entry->psq, 2 * fifo, skb); @@ -1670,11 +1657,13 @@ brcmf_fws_rollback_toq(struct brcmf_fws_info *fws, } if (rc) { - brcmf_fws_bustxfail(fws, skb); fws->stats.rollback_failed++; + brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED, + hslot, 0); } else { fws->stats.rollback_success++; - brcmf_fws_skb_pickup_credit(fws, fifo, skb); + brcmf_fws_return_credits(fws, fifo, 1); + brcmf_fws_macdesc_return_req_credit(skb); } } @@ -1708,26 +1697,28 @@ static int brcmf_fws_consume_credit(struct brcmf_fws_info *fws, int fifo, struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac; int *credit = &fws->fifo_credit[fifo]; - if (!brcmf_fws_macdesc_use_credit(entry, skb)) - return 0; - if (fifo != BRCMF_FWS_FIFO_AC_BE) fws->borrow_defer_timestamp = jiffies + BRCMF_FWS_BORROW_DEFER_PERIOD; if (!(*credit)) { /* Try to borrow a credit from other queue */ - if (fifo == BRCMF_FWS_FIFO_AC_BE && - brcmf_fws_borrow_credit(fws) == 0) - return 0; - - brcmf_dbg(DATA, "exit: ac=%d, credits depleted\n", fifo); - return -ENAVAIL; + if (fifo != BRCMF_FWS_FIFO_AC_BE || + (brcmf_fws_borrow_credit(fws) != 0)) { + brcmf_dbg(DATA, "ac=%d, credits depleted\n", fifo); + return -ENAVAIL; + } + } else { + (*credit)--; + if (!(*credit)) + fws->fifo_credit_map &= ~(1 << fifo); } - (*credit)--; - if (!(*credit)) - fws->fifo_credit_map &= ~(1 << fifo); - brcmf_dbg(DATA, "exit: ac=%d, credits=%d\n", fifo, *credit); + + brcmf_fws_macdesc_use_req_credit(entry, skb); + + brcmf_dbg(DATA, "ac=%d, credits=%02d:%02d:%02d:%02d\n", fifo, + fws->fifo_credit[0], fws->fifo_credit[1], + fws->fifo_credit[2], fws->fifo_credit[3]); return 0; } @@ -1764,8 +1755,8 @@ static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo, entry->seq[fifo]++; fws->stats.pkt2bus++; fws->stats.send_pkts[fifo]++; - if (brcmf_skbcb(skb)->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK) - fws->stats.fifo_credits_sent[fifo]++; + if (brcmf_skb_if_flags_get_field(skb, REQUESTED)) + fws->stats.requested_sent[fifo]++; return rc; @@ -1888,10 +1879,7 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker) skb = brcmf_fws_deq(fws, fifo); if (!skb) break; - if (brcmf_skbcb(skb)->if_flags & - BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK) - fws->fifo_credit[fifo]--; - + fws->fifo_credit[fifo]--; if (brcmf_fws_commit_skb(fws, fifo, skb)) break; if (fws->bus_flow_blocked) @@ -2023,20 +2011,15 @@ bool brcmf_fws_fc_active(struct brcmf_fws_info *fws) void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb) { ulong flags; - int fifo; + u32 hslot; if (brcmf_skbcb(skb)->state == BRCMF_FWS_SKBSTATE_TIM) { brcmu_pkt_buf_free_skb(skb); return; } brcmf_fws_lock(fws->drvr, flags); - brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_FW_TOSSED, - brcmf_skb_htod_tag_get_field(skb, HSLOT), 0); - /* the packet never reached firmware so reclaim credit */ - if (fws->fcmode == BRCMF_FWS_FCMODE_EXPLICIT_CREDIT) { - fifo = brcmf_skb_htod_tag_get_field(skb, FIFO); - brcmf_fws_skb_pickup_credit(fws, fifo, skb); - } + hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); + brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED, hslot, 0); brcmf_fws_unlock(fws->drvr, flags); } -- cgit v1.2.3 From 5631becbb8703d73239e097d222b183aa4f24e40 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Thu, 6 Jun 2013 13:18:07 +0200 Subject: brcmfmac: add debugfs statistics for firmware-signalling Added statistics for flow-control and packets dropped by the driver. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c | 8 +++++++- drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h | 3 +++ drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 13 +++++++++---- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c index ce50686a86c8..c37b9d68e458 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c @@ -156,8 +156,11 @@ ssize_t brcmf_debugfs_fws_stats_read(struct file *f, char __user *data, "txs_suppr_core: %u\n" "txs_suppr_ps: %u\n" "txs_tossed: %u\n" + "txs_host_tossed: %u\n" + "bus_flow_block: %u\n" + "fws_flow_block: %u\n" "send_pkts: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n" - "fifo_credits_sent: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n", + "requested_sent: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n", fwstats->header_pulls, fwstats->header_only_pkt, fwstats->tlv_parse_failed, @@ -176,6 +179,9 @@ ssize_t brcmf_debugfs_fws_stats_read(struct file *f, char __user *data, fwstats->txs_supp_core, fwstats->txs_supp_ps, fwstats->txs_tossed, + fwstats->txs_host_tossed, + fwstats->bus_flow_block, + fwstats->fws_flow_block, fwstats->send_pkts[0], fwstats->send_pkts[1], fwstats->send_pkts[2], fwstats->send_pkts[3], fwstats->send_pkts[4], diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h index 451ebac09520..0af1f5dc583a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h @@ -157,6 +157,9 @@ struct brcmf_fws_stats { u32 txs_supp_core; u32 txs_supp_ps; u32 txs_tossed; + u32 txs_host_tossed; + u32 bus_flow_block; + u32 fws_flow_block; }; struct brcmf_pub; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index 79d60a658a67..70f70cea7f7d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -584,7 +584,7 @@ static int brcmf_fws_hanger_mark_suppressed(struct brcmf_fws_hanger *h, if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS) return -ENOENT; - if (h->items[slot_id].state != BRCMF_FWS_HANGER_ITEM_STATE_INUSE) { + if (h->items[slot_id].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) { brcmf_err("entry not in use\n"); return -EINVAL; } @@ -894,8 +894,10 @@ brcmf_fws_flow_control_check(struct brcmf_fws_info *fws, struct pktq *pq, brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_FWS_FC, false); if (!(ifp->netif_stop & BRCMF_NETIF_STOP_REASON_FWS_FC) && - pq->len >= BRCMF_FWS_FLOWCONTROL_HIWATER) + pq->len >= BRCMF_FWS_FLOWCONTROL_HIWATER) { + fws->stats.fws_flow_block++; brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_FWS_FC, true); + } return; } @@ -1296,9 +1298,10 @@ brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot, } else if (flags == BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS) { fws->stats.txs_supp_ps++; remove_from_hanger = false; - } else if ((flags == BRCMF_FWS_TXSTATUS_FW_TOSSED) || - (flags == BRCMF_FWS_TXSTATUS_HOST_TOSSED)) + } else if (flags == BRCMF_FWS_TXSTATUS_FW_TOSSED) fws->stats.txs_tossed++; + else if (flags == BRCMF_FWS_TXSTATUS_HOST_TOSSED) + fws->stats.txs_host_tossed++; else brcmf_err("unexpected txstatus\n"); @@ -2030,4 +2033,6 @@ void brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked) fws->bus_flow_blocked = flow_blocked; if (!flow_blocked) brcmf_fws_schedule_deq(fws); + else + fws->stats.bus_flow_block++; } -- cgit v1.2.3 From 7df00d7adb080122502a30ec48f237d2f90d36ad Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 21 May 2013 21:54:55 +0200 Subject: drm/i915: pnv dpll doesn't use m1! So don't try to store it in the DPLL_FP register. Otherwise it looks like the limits for pineview are correct: It has it's own clock computation code, which doesn't use an offset for n divisors, and the register value based m limits look sane enough. v2: Rebase on top of the pineview clock refactor and fixup up the commit message: It's m1 pnv doens't care about, not m2! Quoting Damien's review: - "n can vary between 2 and 6, but we declare the 3-6 as limits. - "p1 seems to be able to go up to 9 - "the m upper limit seems a bit big, but the docs are a bit shy on that values for pnv. "Otherwise, the change itself seems good:" Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index bef9086bf542..1dfaa1c5fa41 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4240,7 +4240,7 @@ static int i9xx_get_refclk(struct drm_crtc *crtc, int num_connectors) static uint32_t pnv_dpll_compute_fp(struct dpll *dpll) { - return (1 << dpll->n) << 16 | dpll->m1 << 8 | dpll->m2; + return (1 << dpll->n) << 16 | dpll->m2; } static uint32_t i9xx_dpll_compute_fp(struct dpll *dpll) -- cgit v1.2.3 From 5358901f99153085db59c3644db00b8753b50ac1 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 5 Jun 2013 13:34:16 +0200 Subject: drm/i915: display pll hw state readout and checking Currently still with an empty register state, this will follow in a next step. This one here just creates the new vfunc and uses it for cross-checking, initial state takeover and the dpll assert function. And add a FIXME for the ddi pll readout code, which still needs to be converted over. v2: - Add some hw state readout debug output. - Also cross check the enabled crtc counting. Note that I've botched up the patch ordering, and before this patch we've read out the pll selection correctly, but did not reconstruct the refcounts properly. See the bug link. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=65673 Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 7 ++++ drivers/gpu/drm/i915/intel_display.c | 77 +++++++++++++++++++++++++++++++++--- 2 files changed, 79 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index e5b8ae42859a..7b998dcae089 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -142,6 +142,9 @@ enum intel_dpll_id { }; #define I915_NUM_PLLS 2 +struct intel_dpll_hw_state { +}; + struct intel_shared_dpll { int refcount; /* count of number of CRTCs sharing this PLL */ int active; /* count of number of active CRTCs (i.e. DPMS on) */ @@ -149,10 +152,14 @@ struct intel_shared_dpll { const char *name; /* should match the index in the dev_priv->shared_dplls array */ enum intel_dpll_id id; + struct intel_dpll_hw_state hw_state; void (*enable)(struct drm_i915_private *dev_priv, struct intel_shared_dpll *pll); void (*disable)(struct drm_i915_private *dev_priv, struct intel_shared_dpll *pll); + bool (*get_hw_state)(struct drm_i915_private *dev_priv, + struct intel_shared_dpll *pll, + struct intel_dpll_hw_state *hw_state); }; /* Used by dp and fdi links */ diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 1dfaa1c5fa41..79a1081ed7c9 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -925,8 +925,8 @@ static void assert_shared_dpll(struct drm_i915_private *dev_priv, struct intel_shared_dpll *pll, bool state) { - u32 val; bool cur_state; + struct intel_dpll_hw_state hw_state; if (HAS_PCH_LPT(dev_priv->dev)) { DRM_DEBUG_DRIVER("LPT detected: skipping PCH PLL test\n"); @@ -937,11 +937,10 @@ static void assert_shared_dpll(struct drm_i915_private *dev_priv, "asserting DPLL %s with no DPLL\n", state_string(state))) return; - val = I915_READ(PCH_DPLL(pll->id)); - cur_state = !!(val & DPLL_VCO_ENABLE); + cur_state = pll->get_hw_state(dev_priv, pll, &hw_state); WARN(cur_state != state, - "%s assertion failure (expected %s, current %s), val=%08x\n", - pll->name, state_string(state), state_string(cur_state), val); + "%s assertion failure (expected %s, current %s)\n", + pll->name, state_string(state), state_string(cur_state)); } #define assert_shared_dpll_enabled(d, p) assert_shared_dpll(d, p, true) #define assert_shared_dpll_disabled(d, p) assert_shared_dpll(d, p, false) @@ -8147,6 +8146,8 @@ intel_modeset_check_state(struct drm_device *dev) struct intel_encoder *encoder; struct intel_connector *connector; struct intel_crtc_config pipe_config; + struct intel_dpll_hw_state dpll_hw_state; + int i; list_for_each_entry(connector, &dev->mode_config.connector_list, base.head) { @@ -8261,6 +8262,41 @@ intel_modeset_check_state(struct drm_device *dev) "[sw state]"); } } + + for (i = 0; i < dev_priv->num_shared_dpll; i++) { + struct intel_shared_dpll *pll = &dev_priv->shared_dplls[i]; + int enabled_crtcs = 0, active_crtcs = 0; + bool active; + + memset(&dpll_hw_state, 0, sizeof(dpll_hw_state)); + + DRM_DEBUG_KMS("%s\n", pll->name); + + active = pll->get_hw_state(dev_priv, pll, &dpll_hw_state); + + WARN(pll->active > pll->refcount, + "more active pll users than references: %i vs %i\n", + pll->active, pll->refcount); + WARN(pll->active && !pll->on, + "pll in active use but not on in sw tracking\n"); + WARN(pll->on != active, + "pll on state mismatch (expected %i, found %i)\n", + pll->on, active); + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, + base.head) { + if (crtc->base.enabled && intel_crtc_to_shared_dpll(crtc) == pll) + enabled_crtcs++; + if (crtc->active && intel_crtc_to_shared_dpll(crtc) == pll) + active_crtcs++; + } + WARN(pll->active != active_crtcs, + "pll active crtcs mismatch (expected %i, found %i)\n", + pll->active, active_crtcs); + WARN(pll->refcount != enabled_crtcs, + "pll enabled crtcs mismatch (expected %i, found %i)\n", + pll->refcount, enabled_crtcs); + } } static int __intel_set_mode(struct drm_crtc *crtc, @@ -8684,6 +8720,17 @@ static void intel_cpu_pll_init(struct drm_device *dev) intel_ddi_pll_init(dev); } +static bool ibx_pch_dpll_get_hw_state(struct drm_i915_private *dev_priv, + struct intel_shared_dpll *pll, + struct intel_dpll_hw_state *hw_state) +{ + uint32_t val; + + val = I915_READ(PCH_DPLL(pll->id)); + + return val & DPLL_VCO_ENABLE; +} + static void ibx_pch_dpll_enable(struct drm_i915_private *dev_priv, struct intel_shared_dpll *pll) { @@ -8738,6 +8785,8 @@ static void ibx_pch_dpll_init(struct drm_device *dev) dev_priv->shared_dplls[i].name = ibx_pch_dpll_names[i]; dev_priv->shared_dplls[i].enable = ibx_pch_dpll_enable; dev_priv->shared_dplls[i].disable = ibx_pch_dpll_disable; + dev_priv->shared_dplls[i].get_hw_state = + ibx_pch_dpll_get_hw_state; } } @@ -9655,6 +9704,7 @@ void intel_modeset_setup_hw_state(struct drm_device *dev, struct intel_crtc *crtc; struct intel_encoder *encoder; struct intel_connector *connector; + int i; list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head) { @@ -9670,9 +9720,26 @@ void intel_modeset_setup_hw_state(struct drm_device *dev, crtc->active ? "enabled" : "disabled"); } + /* FIXME: Smash this into the new shared dpll infrastructure. */ if (HAS_DDI(dev)) intel_ddi_setup_hw_pll_state(dev); + for (i = 0; i < dev_priv->num_shared_dpll; i++) { + struct intel_shared_dpll *pll = &dev_priv->shared_dplls[i]; + + pll->on = pll->get_hw_state(dev_priv, pll, &pll->hw_state); + pll->active = 0; + list_for_each_entry(crtc, &dev->mode_config.crtc_list, + base.head) { + if (crtc->active && intel_crtc_to_shared_dpll(crtc) == pll) + pll->active++; + } + pll->refcount = pll->active; + + DRM_DEBUG_KMS("%s hw state readout: refcount %i\n", + pll->name, pll->refcount); + } + list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) { pipe = 0; -- cgit v1.2.3 From 30e984df4c5633363b45108473b0561e7d89476d Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 5 Jun 2013 13:34:17 +0200 Subject: drm/i915: extract readout_hw_state from setup_hw_state Simply grew too big. This also makes the fixup and restore logic in setup_hw_state stand out a bit more clearly. Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 79a1081ed7c9..34ddd6f7c31d 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -9693,14 +9693,10 @@ void i915_redisable_vga(struct drm_device *dev) } } -/* Scan out the current hw modeset state, sanitizes it and maps it into the drm - * and i915 state tracking structures. */ -void intel_modeset_setup_hw_state(struct drm_device *dev, - bool force_restore) +static void intel_modeset_readout_hw_state(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; enum pipe pipe; - struct drm_plane *plane; struct intel_crtc *crtc; struct intel_encoder *encoder; struct intel_connector *connector; @@ -9776,6 +9772,20 @@ void intel_modeset_setup_hw_state(struct drm_device *dev, drm_get_connector_name(&connector->base), connector->base.encoder ? "enabled" : "disabled"); } +} + +/* Scan out the current hw modeset state, sanitizes it and maps it into the drm + * and i915 state tracking structures. */ +void intel_modeset_setup_hw_state(struct drm_device *dev, + bool force_restore) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + enum pipe pipe; + struct drm_plane *plane; + struct intel_crtc *crtc; + struct intel_encoder *encoder; + + intel_modeset_readout_hw_state(dev); /* HW state is read out, now we need to sanitize this mess. */ list_for_each_entry(encoder, &dev->mode_config.encoder_list, -- cgit v1.2.3 From 91d1b4bd14fd0bfe87e91a0fd115fcc15c9c9cc5 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 5 Jun 2013 13:34:18 +0200 Subject: drm/i915: split up intel_modeset_check_state Simply grew too large and needed to be split up into parts. Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 44 +++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 34ddd6f7c31d..0eea23a21f86 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8138,16 +8138,10 @@ intel_pipe_config_compare(struct drm_device *dev, return true; } -void -intel_modeset_check_state(struct drm_device *dev) +static void +check_connector_state(struct drm_device *dev) { - drm_i915_private_t *dev_priv = dev->dev_private; - struct intel_crtc *crtc; - struct intel_encoder *encoder; struct intel_connector *connector; - struct intel_crtc_config pipe_config; - struct intel_dpll_hw_state dpll_hw_state; - int i; list_for_each_entry(connector, &dev->mode_config.connector_list, base.head) { @@ -8158,6 +8152,13 @@ intel_modeset_check_state(struct drm_device *dev) WARN(&connector->new_encoder->base != connector->base.encoder, "connector's staged encoder doesn't match current encoder\n"); } +} + +static void +check_encoder_state(struct drm_device *dev) +{ + struct intel_encoder *encoder; + struct intel_connector *connector; list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) { @@ -8209,6 +8210,15 @@ intel_modeset_check_state(struct drm_device *dev) tracked_pipe, pipe); } +} + +static void +check_crtc_state(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + struct intel_crtc *crtc; + struct intel_encoder *encoder; + struct intel_crtc_config pipe_config; list_for_each_entry(crtc, &dev->mode_config.crtc_list, base.head) { @@ -8262,6 +8272,15 @@ intel_modeset_check_state(struct drm_device *dev) "[sw state]"); } } +} + +static void +check_shared_dpll_state(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + struct intel_crtc *crtc; + struct intel_dpll_hw_state dpll_hw_state; + int i; for (i = 0; i < dev_priv->num_shared_dpll; i++) { struct intel_shared_dpll *pll = &dev_priv->shared_dplls[i]; @@ -8299,6 +8318,15 @@ intel_modeset_check_state(struct drm_device *dev) } } +void +intel_modeset_check_state(struct drm_device *dev) +{ + check_connector_state(dev); + check_encoder_state(dev); + check_crtc_state(dev); + check_shared_dpll_state(dev); +} + static int __intel_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode, int x, int y, struct drm_framebuffer *fb) -- cgit v1.2.3 From 87a875bbffcfac7cb7c9a106fda40f04de1f60a2 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 5 Jun 2013 13:34:19 +0200 Subject: drm/i915: WARN on lack of shared dpll Now that we have proper hw state reconstruction we should never have a case where we don't have the software dpll state properly set up. So add WARNs to the respective !pll cases in enable/disabel_shared_dpll. Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 0eea23a21f86..cb724b6f8833 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1401,7 +1401,7 @@ static void ironlake_enable_shared_dpll(struct intel_crtc *crtc) /* PCH PLLs only available on ILK, SNB and IVB */ BUG_ON(dev_priv->info->gen < 5); - if (pll == NULL) + if (WARN_ON(pll == NULL)) return; if (WARN_ON(pll->refcount == 0)) @@ -1430,7 +1430,7 @@ static void intel_disable_shared_dpll(struct intel_crtc *crtc) /* PCH only available on ILK+ */ BUG_ON(dev_priv->info->gen < 5); - if (pll == NULL) + if (WARN_ON(pll == NULL)) return; if (WARN_ON(pll->refcount == 0)) -- cgit v1.2.3 From 66e985c035f4554939b8b63a8e21418271160ab0 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 5 Jun 2013 13:34:20 +0200 Subject: drm/i915: hw state readout and cross-checking for shared dplls Just the plumbing, all the modeset and enable code has not yet been switched over to use the new state. It seems to be decently broken anyway, at least wrt to handling of the special pixel mutliplier enabling sequence. Follow-up patches will clean up that mess. Another missing piece is more careful handling (and fixup) of the fp1 alternate divisor state. The BIOS most likely doesn't bother to program that one to what we expect. So we need to be more careful with comparing that state, both for cross checking but also when checking for dpll sharing when acquiring shared dpll. Otherwise fastboot will deny a few shared dpll configurations which would otherwise work. v2: We need to memcpy the pipe config dpll hw state into the pll, for otherwise the cross-check code will get angry. v3: Don't forget to read the pch pll state in the crtc get_pipe_config function for ibx/ilk platforms. Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 3 +++ drivers/gpu/drm/i915/intel_display.c | 38 ++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/i915/intel_drv.h | 3 +++ 3 files changed, 44 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 7b998dcae089..42ef7cb39e62 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -143,6 +143,9 @@ enum intel_dpll_id { #define I915_NUM_PLLS 2 struct intel_dpll_hw_state { + uint32_t dpll; + uint32_t fp0; + uint32_t fp1; }; struct intel_shared_dpll { diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index cb724b6f8833..5b2fd9063a2d 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3087,7 +3087,11 @@ found: crtc->config.shared_dpll = i; DRM_DEBUG_DRIVER("using %s for pipe %c\n", pll->name, pipe_name(crtc->pipe)); + if (pll->active == 0) { + memcpy(&pll->hw_state, &crtc->config.dpll_hw_state, + sizeof(pll->hw_state)); + DRM_DEBUG_DRIVER("setting up %s\n", pll->name); WARN_ON(pll->on); assert_shared_dpll_disabled(dev_priv, pll); @@ -5718,6 +5722,13 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, &fp, &reduced_clock, has_reduced_clock ? &fp2 : NULL); + intel_crtc->config.dpll_hw_state.dpll = dpll | DPLL_VCO_ENABLE; + intel_crtc->config.dpll_hw_state.fp0 = fp; + if (has_reduced_clock) + intel_crtc->config.dpll_hw_state.fp1 = fp2; + else + intel_crtc->config.dpll_hw_state.fp1 = fp; + pll = intel_get_shared_dpll(intel_crtc, dpll, fp); if (pll == NULL) { DRM_DEBUG_DRIVER("failed to find PLL for pipe %c\n", @@ -5837,6 +5848,8 @@ static bool ironlake_get_pipe_config(struct intel_crtc *crtc, return false; if (I915_READ(PCH_TRANSCONF(crtc->pipe)) & TRANS_ENABLE) { + struct intel_shared_dpll *pll; + pipe_config->has_pch_encoder = true; tmp = I915_READ(FDI_RX_CTL(crtc->pipe)); @@ -5858,6 +5871,11 @@ static bool ironlake_get_pipe_config(struct intel_crtc *crtc, else pipe_config->shared_dpll = DPLL_ID_PCH_PLL_A; } + + pll = &dev_priv->shared_dplls[pipe_config->shared_dpll]; + + WARN_ON(!pll->get_hw_state(dev_priv, pll, + &pipe_config->dpll_hw_state)); } else { pipe_config->pixel_multiplier = 1; } @@ -8054,6 +8072,15 @@ intel_pipe_config_compare(struct drm_device *dev, struct intel_crtc_config *current_config, struct intel_crtc_config *pipe_config) { +#define PIPE_CONF_CHECK_X(name) \ + if (current_config->name != pipe_config->name) { \ + DRM_ERROR("mismatch in " #name " " \ + "(expected 0x%08x, found 0x%08x)\n", \ + current_config->name, \ + pipe_config->name); \ + return false; \ + } + #define PIPE_CONF_CHECK_I(name) \ if (current_config->name != pipe_config->name) { \ DRM_ERROR("mismatch in " #name " " \ @@ -8130,7 +8157,11 @@ intel_pipe_config_compare(struct drm_device *dev, PIPE_CONF_CHECK_I(ips_enabled); PIPE_CONF_CHECK_I(shared_dpll); + PIPE_CONF_CHECK_X(dpll_hw_state.dpll); + PIPE_CONF_CHECK_X(dpll_hw_state.fp0); + PIPE_CONF_CHECK_X(dpll_hw_state.fp1); +#undef PIPE_CONF_CHECK_X #undef PIPE_CONF_CHECK_I #undef PIPE_CONF_CHECK_FLAGS #undef PIPE_CONF_QUIRK @@ -8315,6 +8346,10 @@ check_shared_dpll_state(struct drm_device *dev) WARN(pll->refcount != enabled_crtcs, "pll enabled crtcs mismatch (expected %i, found %i)\n", pll->refcount, enabled_crtcs); + + WARN(pll->on && memcmp(&pll->hw_state, &dpll_hw_state, + sizeof(dpll_hw_state)), + "pll hw state mismatch\n"); } } @@ -8755,6 +8790,9 @@ static bool ibx_pch_dpll_get_hw_state(struct drm_i915_private *dev_priv, uint32_t val; val = I915_READ(PCH_DPLL(pll->id)); + hw_state->dpll = val; + hw_state->fp0 = I915_READ(PCH_FP0(pll->id)); + hw_state->fp1 = I915_READ(PCH_FP1(pll->id)); return val & DPLL_VCO_ENABLE; } diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 4a69bdc4f8ca..02e5d6557066 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -256,6 +256,9 @@ struct intel_crtc_config { /* Selected dpll when shared or DPLL_ID_PRIVATE. */ enum intel_dpll_id shared_dpll; + /* Actual register state of the dpll, for shared dpll cross-checking. */ + struct intel_dpll_hw_state dpll_hw_state; + int pipe_bpp; struct intel_link_m_n dp_m_n; -- cgit v1.2.3 From 959e16d65d808602cd88b82530626f964f684c05 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 5 Jun 2013 13:34:21 +0200 Subject: drm/i915: fix up pch pll enabling for pixel multipliers We have a nice comment saying that the pixel multiplier only sticks once the vco is on and stable. The only problem is that the enable bit wasn't set at all. This patch fixes this and so brings the ilk+ pch pll code in line with the i8xx/i9xx pll code. Or at least improves matters a lot. This should fix sdvo on ilk-ivb for low-res modes. Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 5b2fd9063a2d..015614fb04d9 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5660,7 +5660,7 @@ static uint32_t ironlake_compute_dpll(struct intel_crtc *intel_crtc, else dpll |= PLL_REF_INPUT_DREFCLK; - return dpll; + return dpll | DPLL_VCO_ENABLE; } static int ironlake_crtc_mode_set(struct drm_crtc *crtc, @@ -5722,7 +5722,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, &fp, &reduced_clock, has_reduced_clock ? &fp2 : NULL); - intel_crtc->config.dpll_hw_state.dpll = dpll | DPLL_VCO_ENABLE; + intel_crtc->config.dpll_hw_state.dpll = dpll; intel_crtc->config.dpll_hw_state.fp0 = fp; if (has_reduced_clock) intel_crtc->config.dpll_hw_state.fp1 = fp2; -- cgit v1.2.3 From 11682a41618f8094cb7a9330b4b6a12ffaef5774 Mon Sep 17 00:00:00 2001 From: Marcus Gelderie Date: Tue, 4 Jun 2013 09:32:09 +0200 Subject: alarmtimer: Export symbols of functions declared in linux/alarmtimer.h Export symbols so they can be used by drivers/staging/android/alarm-dev.c if it is built as a module. So far alarm-dev is built-in but module support is planned (see drivers/staging/android/TODO). Signed-off-by: Marcus Gelderie [jstultz: tweaked commit message, also export newly added functions] Signed-off-by: John Stultz --- kernel/time/alarmtimer.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c index 3e5cba274475..eec50fcef9e4 100644 --- a/kernel/time/alarmtimer.c +++ b/kernel/time/alarmtimer.c @@ -204,6 +204,7 @@ ktime_t alarm_expires_remaining(const struct alarm *alarm) struct alarm_base *base = &alarm_bases[alarm->type]; return ktime_sub(alarm->node.expires, base->gettime()); } +EXPORT_SYMBOL_GPL(alarm_expires_remaining); #ifdef CONFIG_RTC_CLASS /** @@ -309,6 +310,7 @@ void alarm_init(struct alarm *alarm, enum alarmtimer_type type, alarm->type = type; alarm->state = ALARMTIMER_STATE_INACTIVE; } +EXPORT_SYMBOL_GPL(alarm_init); /** * alarm_start - Sets an absolute alarm to fire @@ -329,6 +331,7 @@ int alarm_start(struct alarm *alarm, ktime_t start) spin_unlock_irqrestore(&base->lock, flags); return ret; } +EXPORT_SYMBOL_GPL(alarm_start); /** * alarm_start_relative - Sets a relative alarm to fire @@ -342,6 +345,7 @@ int alarm_start_relative(struct alarm *alarm, ktime_t start) start = ktime_add(start, base->gettime()); return alarm_start(alarm, start); } +EXPORT_SYMBOL_GPL(alarm_start_relative); void alarm_restart(struct alarm *alarm) { @@ -354,6 +358,7 @@ void alarm_restart(struct alarm *alarm) alarmtimer_enqueue(base, alarm); spin_unlock_irqrestore(&base->lock, flags); } +EXPORT_SYMBOL_GPL(alarm_restart); /** * alarm_try_to_cancel - Tries to cancel an alarm timer @@ -375,6 +380,7 @@ int alarm_try_to_cancel(struct alarm *alarm) spin_unlock_irqrestore(&base->lock, flags); return ret; } +EXPORT_SYMBOL_GPL(alarm_try_to_cancel); /** @@ -392,6 +398,7 @@ int alarm_cancel(struct alarm *alarm) cpu_relax(); } } +EXPORT_SYMBOL_GPL(alarm_cancel); u64 alarm_forward(struct alarm *alarm, ktime_t now, ktime_t interval) @@ -424,6 +431,7 @@ u64 alarm_forward(struct alarm *alarm, ktime_t now, ktime_t interval) alarm->node.expires = ktime_add(alarm->node.expires, interval); return overrun; } +EXPORT_SYMBOL_GPL(alarm_forward); u64 alarm_forward_now(struct alarm *alarm, ktime_t interval) { @@ -431,7 +439,7 @@ u64 alarm_forward_now(struct alarm *alarm, ktime_t interval) return alarm_forward(alarm, base->gettime(), interval); } - +EXPORT_SYMBOL_GPL(alarm_forward_now); /** -- cgit v1.2.3 From 5a9b5855c248d0298eac4d5490c7bc11c2f1983b Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sat, 1 Jun 2013 23:39:38 -0700 Subject: ARM: sched_clock: Remove unused needs_suspend member The needs_suspend member is unused now that we always do the suspend/resume handling (see 6a4dae5 (ARM: 7565/1: sched: stop sched_clock() during suspend, 2012-10-23)). Acked-by: Will Deacon Signed-off-by: Stephen Boyd Signed-off-by: John Stultz --- arch/arm/kernel/sched_clock.c | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm/kernel/sched_clock.c b/arch/arm/kernel/sched_clock.c index e8edcaa0e432..45efe86e8b08 100644 --- a/arch/arm/kernel/sched_clock.c +++ b/arch/arm/kernel/sched_clock.c @@ -24,7 +24,6 @@ struct clock_data { u32 mult; u32 shift; bool suspended; - bool needs_suspend; }; static void sched_clock_poll(unsigned long wrap_ticks); -- cgit v1.2.3 From ffbfb5e316f0db486798ccf1db36a577ffe79637 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sat, 1 Jun 2013 23:39:39 -0700 Subject: ARM: sched_clock: Return suspended count earlier If we're suspended and sched_clock() is called we're going to read the hardware one more time and throw away that value and return back the cached value we saved during the suspend callback. This is wasteful. Let's short circuit all that and return the cached value as early as possible if we're suspended. Acked-by: Will Deacon Signed-off-by: Stephen Boyd Signed-off-by: John Stultz --- arch/arm/kernel/sched_clock.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm/kernel/sched_clock.c b/arch/arm/kernel/sched_clock.c index 45efe86e8b08..a781c59b93c0 100644 --- a/arch/arm/kernel/sched_clock.c +++ b/arch/arm/kernel/sched_clock.c @@ -55,9 +55,6 @@ static unsigned long long notrace cyc_to_sched_clock(u32 cyc, u32 mask) u64 epoch_ns; u32 epoch_cyc; - if (cd.suspended) - return cd.epoch_ns; - /* * Load the epoch_cyc and epoch_ns atomically. We do this by * ensuring that we always write epoch_cyc, epoch_ns and @@ -174,6 +171,9 @@ unsigned long long __read_mostly (*sched_clock_func)(void) = sched_clock_32; unsigned long long notrace sched_clock(void) { + if (cd.suspended) + return cd.epoch_ns; + return sched_clock_func(); } -- cgit v1.2.3 From 38ff87f77af0b5a93fc8581cff1d6e5692ab8970 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sat, 1 Jun 2013 23:39:40 -0700 Subject: sched_clock: Make ARM's sched_clock generic for all architectures Nothing about the sched_clock implementation in the ARM port is specific to the architecture. Generalize the code so that other architectures can use it by selecting GENERIC_SCHED_CLOCK. Signed-off-by: Stephen Boyd [jstultz: Merge minor collisions with other patches in my tree] Signed-off-by: John Stultz --- arch/arm/Kconfig | 1 + arch/arm/common/timer-sp.c | 2 +- arch/arm/include/asm/sched_clock.h | 16 --- arch/arm/kernel/Makefile | 2 +- arch/arm/kernel/arch_timer.c | 2 +- arch/arm/kernel/sched_clock.c | 216 ------------------------------ arch/arm/kernel/time.c | 4 +- arch/arm/mach-davinci/time.c | 2 +- arch/arm/mach-imx/time.c | 2 +- arch/arm/mach-integrator/integrator_ap.c | 2 +- arch/arm/mach-ixp4xx/common.c | 2 +- arch/arm/mach-mmp/time.c | 2 +- arch/arm/mach-msm/timer.c | 2 +- arch/arm/mach-omap1/time.c | 2 +- arch/arm/mach-omap2/timer.c | 2 +- arch/arm/mach-pxa/time.c | 2 +- arch/arm/mach-sa1100/time.c | 2 +- arch/arm/mach-u300/timer.c | 2 +- arch/arm/plat-iop/time.c | 2 +- arch/arm/plat-omap/counter_32k.c | 2 +- arch/arm/plat-orion/time.c | 2 +- arch/arm/plat-samsung/samsung-time.c | 2 +- arch/arm/plat-versatile/sched-clock.c | 2 +- drivers/clocksource/bcm2835_timer.c | 2 +- drivers/clocksource/clksrc-dbx500-prcmu.c | 3 +- drivers/clocksource/dw_apb_timer_of.c | 3 +- drivers/clocksource/mxs_timer.c | 2 +- drivers/clocksource/nomadik-mtu.c | 2 +- drivers/clocksource/samsung_pwm_timer.c | 2 +- drivers/clocksource/tegra20_timer.c | 2 +- drivers/clocksource/time-armada-370-xp.c | 2 +- drivers/clocksource/timer-marco.c | 2 +- drivers/clocksource/timer-prima2.c | 2 +- include/linux/sched_clock.h | 21 +++ init/Kconfig | 3 + init/main.c | 2 + kernel/time/Makefile | 1 + kernel/time/sched_clock.c | 215 +++++++++++++++++++++++++++++ 38 files changed, 273 insertions(+), 266 deletions(-) delete mode 100644 arch/arm/include/asm/sched_clock.h delete mode 100644 arch/arm/kernel/sched_clock.c create mode 100644 include/linux/sched_clock.h create mode 100644 kernel/time/sched_clock.c diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 49d993cee512..53d3a356f61f 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -14,6 +14,7 @@ config ARM select GENERIC_IRQ_PROBE select GENERIC_IRQ_SHOW select GENERIC_PCI_IOMAP + select GENERIC_SCHED_CLOCK select GENERIC_SMP_IDLE_THREAD select GENERIC_IDLE_POLL_SETUP select GENERIC_STRNCPY_FROM_USER diff --git a/arch/arm/common/timer-sp.c b/arch/arm/common/timer-sp.c index ddc740769601..023ee63827a2 100644 --- a/arch/arm/common/timer-sp.c +++ b/arch/arm/common/timer-sp.c @@ -28,8 +28,8 @@ #include #include #include +#include -#include #include #include diff --git a/arch/arm/include/asm/sched_clock.h b/arch/arm/include/asm/sched_clock.h deleted file mode 100644 index 3d520ddca61b..000000000000 --- a/arch/arm/include/asm/sched_clock.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * sched_clock.h: support for extending counters to full 64-bit ns counter - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#ifndef ASM_SCHED_CLOCK -#define ASM_SCHED_CLOCK - -extern void sched_clock_postinit(void); -extern void setup_sched_clock(u32 (*read)(void), int bits, unsigned long rate); - -extern unsigned long long (*sched_clock_func)(void); - -#endif diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index 5f3338eacad2..97cb0576d07c 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -16,7 +16,7 @@ CFLAGS_REMOVE_return_address.o = -pg # Object file lists. obj-y := elf.o entry-armv.o entry-common.o irq.o opcodes.o \ - process.o ptrace.o return_address.o sched_clock.o \ + process.o ptrace.o return_address.o \ setup.o signal.o stacktrace.o sys_arm.o time.o traps.o obj-$(CONFIG_ATAGS) += atags_parse.o diff --git a/arch/arm/kernel/arch_timer.c b/arch/arm/kernel/arch_timer.c index 59dcdced6e30..221f07b11ccb 100644 --- a/arch/arm/kernel/arch_timer.c +++ b/arch/arm/kernel/arch_timer.c @@ -11,9 +11,9 @@ #include #include #include +#include #include -#include #include diff --git a/arch/arm/kernel/sched_clock.c b/arch/arm/kernel/sched_clock.c deleted file mode 100644 index a781c59b93c0..000000000000 --- a/arch/arm/kernel/sched_clock.c +++ /dev/null @@ -1,216 +0,0 @@ -/* - * sched_clock.c: support for extending counters to full 64-bit ns counter - * - * 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 -#include -#include -#include -#include -#include -#include -#include - -#include - -struct clock_data { - u64 epoch_ns; - u32 epoch_cyc; - u32 epoch_cyc_copy; - unsigned long rate; - u32 mult; - u32 shift; - bool suspended; -}; - -static void sched_clock_poll(unsigned long wrap_ticks); -static DEFINE_TIMER(sched_clock_timer, sched_clock_poll, 0, 0); -static int irqtime = -1; - -core_param(irqtime, irqtime, int, 0400); - -static struct clock_data cd = { - .mult = NSEC_PER_SEC / HZ, -}; - -static u32 __read_mostly sched_clock_mask = 0xffffffff; - -static u32 notrace jiffy_sched_clock_read(void) -{ - return (u32)(jiffies - INITIAL_JIFFIES); -} - -static u32 __read_mostly (*read_sched_clock)(void) = jiffy_sched_clock_read; - -static inline u64 notrace cyc_to_ns(u64 cyc, u32 mult, u32 shift) -{ - return (cyc * mult) >> shift; -} - -static unsigned long long notrace cyc_to_sched_clock(u32 cyc, u32 mask) -{ - u64 epoch_ns; - u32 epoch_cyc; - - /* - * Load the epoch_cyc and epoch_ns atomically. We do this by - * ensuring that we always write epoch_cyc, epoch_ns and - * epoch_cyc_copy in strict order, and read them in strict order. - * If epoch_cyc and epoch_cyc_copy are not equal, then we're in - * the middle of an update, and we should repeat the load. - */ - do { - epoch_cyc = cd.epoch_cyc; - smp_rmb(); - epoch_ns = cd.epoch_ns; - smp_rmb(); - } while (epoch_cyc != cd.epoch_cyc_copy); - - return epoch_ns + cyc_to_ns((cyc - epoch_cyc) & mask, cd.mult, cd.shift); -} - -/* - * Atomically update the sched_clock epoch. - */ -static void notrace update_sched_clock(void) -{ - unsigned long flags; - u32 cyc; - u64 ns; - - cyc = read_sched_clock(); - ns = cd.epoch_ns + - cyc_to_ns((cyc - cd.epoch_cyc) & sched_clock_mask, - cd.mult, cd.shift); - /* - * Write epoch_cyc and epoch_ns in a way that the update is - * detectable in cyc_to_fixed_sched_clock(). - */ - raw_local_irq_save(flags); - cd.epoch_cyc_copy = cyc; - smp_wmb(); - cd.epoch_ns = ns; - smp_wmb(); - cd.epoch_cyc = cyc; - raw_local_irq_restore(flags); -} - -static void sched_clock_poll(unsigned long wrap_ticks) -{ - mod_timer(&sched_clock_timer, round_jiffies(jiffies + wrap_ticks)); - update_sched_clock(); -} - -void __init setup_sched_clock(u32 (*read)(void), int bits, unsigned long rate) -{ - unsigned long r, w; - u64 res, wrap; - char r_unit; - - if (cd.rate > rate) - return; - - BUG_ON(bits > 32); - WARN_ON(!irqs_disabled()); - read_sched_clock = read; - sched_clock_mask = (1 << bits) - 1; - cd.rate = rate; - - /* calculate the mult/shift to convert counter ticks to ns. */ - clocks_calc_mult_shift(&cd.mult, &cd.shift, rate, NSEC_PER_SEC, 0); - - r = rate; - if (r >= 4000000) { - r /= 1000000; - r_unit = 'M'; - } else if (r >= 1000) { - r /= 1000; - r_unit = 'k'; - } else - r_unit = ' '; - - /* calculate how many ns until we wrap */ - wrap = cyc_to_ns((1ULL << bits) - 1, cd.mult, cd.shift); - do_div(wrap, NSEC_PER_MSEC); - w = wrap; - - /* calculate the ns resolution of this counter */ - res = cyc_to_ns(1ULL, cd.mult, cd.shift); - pr_info("sched_clock: %u bits at %lu%cHz, resolution %lluns, wraps every %lums\n", - bits, r, r_unit, res, w); - - /* - * Start the timer to keep sched_clock() properly updated and - * sets the initial epoch. - */ - sched_clock_timer.data = msecs_to_jiffies(w - (w / 10)); - update_sched_clock(); - - /* - * Ensure that sched_clock() starts off at 0ns - */ - cd.epoch_ns = 0; - - /* Enable IRQ time accounting if we have a fast enough sched_clock */ - if (irqtime > 0 || (irqtime == -1 && rate >= 1000000)) - enable_sched_clock_irqtime(); - - pr_debug("Registered %pF as sched_clock source\n", read); -} - -static unsigned long long notrace sched_clock_32(void) -{ - u32 cyc = read_sched_clock(); - return cyc_to_sched_clock(cyc, sched_clock_mask); -} - -unsigned long long __read_mostly (*sched_clock_func)(void) = sched_clock_32; - -unsigned long long notrace sched_clock(void) -{ - if (cd.suspended) - return cd.epoch_ns; - - return sched_clock_func(); -} - -void __init sched_clock_postinit(void) -{ - /* - * If no sched_clock function has been provided at that point, - * make it the final one one. - */ - if (read_sched_clock == jiffy_sched_clock_read) - setup_sched_clock(jiffy_sched_clock_read, 32, HZ); - - sched_clock_poll(sched_clock_timer.data); -} - -static int sched_clock_suspend(void) -{ - sched_clock_poll(sched_clock_timer.data); - cd.suspended = true; - return 0; -} - -static void sched_clock_resume(void) -{ - cd.epoch_cyc = read_sched_clock(); - cd.epoch_cyc_copy = cd.epoch_cyc; - cd.suspended = false; -} - -static struct syscore_ops sched_clock_ops = { - .suspend = sched_clock_suspend, - .resume = sched_clock_resume, -}; - -static int __init sched_clock_syscore_init(void) -{ - register_syscore_ops(&sched_clock_ops); - return 0; -} -device_initcall(sched_clock_syscore_init); diff --git a/arch/arm/kernel/time.c b/arch/arm/kernel/time.c index abff4e9aaee0..98aee3258398 100644 --- a/arch/arm/kernel/time.c +++ b/arch/arm/kernel/time.c @@ -24,9 +24,9 @@ #include #include #include +#include #include -#include #include #include #include @@ -120,6 +120,4 @@ void __init time_init(void) machine_desc->init_time(); else clocksource_of_init(); - - sched_clock_postinit(); } diff --git a/arch/arm/mach-davinci/time.c b/arch/arm/mach-davinci/time.c index bad361ec1666..7a55b5c95971 100644 --- a/arch/arm/mach-davinci/time.c +++ b/arch/arm/mach-davinci/time.c @@ -18,8 +18,8 @@ #include #include #include +#include -#include #include #include diff --git a/arch/arm/mach-imx/time.c b/arch/arm/mach-imx/time.c index fea91313678b..cd46529e9eaa 100644 --- a/arch/arm/mach-imx/time.c +++ b/arch/arm/mach-imx/time.c @@ -26,8 +26,8 @@ #include #include #include +#include -#include #include #include "common.h" diff --git a/arch/arm/mach-integrator/integrator_ap.c b/arch/arm/mach-integrator/integrator_ap.c index b23c8e4f28e8..aa4346227c41 100644 --- a/arch/arm/mach-integrator/integrator_ap.c +++ b/arch/arm/mach-integrator/integrator_ap.c @@ -41,6 +41,7 @@ #include #include #include +#include #include - - - Both functions get the device IRQ by calling - drm_dev_to_irq. This inline function will call a - bus-specific operation to retrieve the IRQ number. For platform devices, - platform_get_irq(..., 0) is used to retrieve the - IRQ number. - - - drm_irq_install starts by calling the - irq_preinstall driver operation. The operation - is optional and must make sure that the interrupt will not get fired by - clearing all pending interrupt flags or disabling the interrupt. - - - The IRQ will then be requested by a call to - request_irq. If the DRIVER_IRQ_SHARED driver - feature flag is set, a shared (IRQF_SHARED) IRQ handler will be - requested. - - - The IRQ handler function must be provided as the mandatory irq_handler - driver operation. It will get passed directly to - request_irq and thus has the same prototype as all - IRQ handlers. It will get called with a pointer to the DRM device as the - second argument. - - - Finally the function calls the optional - irq_postinstall driver operation. The operation - usually enables interrupts (excluding the vblank interrupt, which is - enabled separately), but drivers may choose to enable/disable interrupts - at a different time. - - - drm_irq_uninstall is similarly used to uninstall an - IRQ handler. It starts by waking up all processes waiting on a vblank - interrupt to make sure they don't hang, and then calls the optional - irq_uninstall driver operation. The operation - must disable all hardware interrupts. Finally the function frees the IRQ - by calling free_irq. + support a single interrupt per device, devices that use more than one + IRQs need to be handled manually. + + Managed IRQ Registration + + Both the drm_irq_install and + drm_irq_uninstall functions get the device IRQ by + calling drm_dev_to_irq. This inline function will + call a bus-specific operation to retrieve the IRQ number. For platform + devices, platform_get_irq(..., 0) is used to + retrieve the IRQ number. + + + drm_irq_install starts by calling the + irq_preinstall driver operation. The operation + is optional and must make sure that the interrupt will not get fired by + clearing all pending interrupt flags or disabling the interrupt. + + + The IRQ will then be requested by a call to + request_irq. If the DRIVER_IRQ_SHARED driver + feature flag is set, a shared (IRQF_SHARED) IRQ handler will be + requested. + + + The IRQ handler function must be provided as the mandatory irq_handler + driver operation. It will get passed directly to + request_irq and thus has the same prototype as all + IRQ handlers. It will get called with a pointer to the DRM device as the + second argument. + + + Finally the function calls the optional + irq_postinstall driver operation. The operation + usually enables interrupts (excluding the vblank interrupt, which is + enabled separately), but drivers may choose to enable/disable interrupts + at a different time. + + + drm_irq_uninstall is similarly used to uninstall an + IRQ handler. It starts by waking up all processes waiting on a vblank + interrupt to make sure they don't hang, and then calls the optional + irq_uninstall driver operation. The operation + must disable all hardware interrupts. Finally the function frees the IRQ + by calling free_irq. + + + + Manual IRQ Registration + + Drivers that require multiple interrupt handlers can't use the managed + IRQ registration functions. In that case IRQs must be registered and + unregistered manually (usually with the request_irq + and free_irq functions, or their devm_* equivalent). + + + When manually registering IRQs, drivers must not set the DRIVER_HAVE_IRQ + driver feature flag, and must not provide the + irq_handler driver operation. They must set the + drm_device irq_enabled + field to 1 upon registration of the IRQs, and clear it to 0 after + unregistering the IRQs. + + Memory Manager Initialization -- cgit v1.2.3 From 02c030a718e7fc6e066c6169c195f53b461df408 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 22 Jun 2013 16:10:29 +0200 Subject: drm/doc: Remove outdated note about i915 driver not behaving properly The i915 driver has been fixed not to modify the mode argument of the encoder mode_fixup operation. Remove the related comment from the documentation. Signed-off-by: Laurent Pinchart Reviewed-by: Alex Deucher Signed-off-by: Dave Airlie --- Documentation/DocBook/drm.tmpl | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index a6080941f5e4..cea420dc712d 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -1868,10 +1868,6 @@ void intel_crt_init(struct drm_device *dev) bool (*mode_fixup)(struct drm_encoder *encoder, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode); - - FIXME: The mode argument be const, but the i915 driver modifies - mode->clock in intel_dp_mode_fixup. - Let encoders adjust the requested mode or reject it completely. This operation returns true if the mode is accepted (possibly after being -- cgit v1.2.3 From 421cda3e324fce40e3f7abdf3d31cae1d0deddcd Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 22 Jun 2013 16:10:30 +0200 Subject: drm/doc: Document the KMS property API Signed-off-by: Laurent Pinchart Reviewed-by: Alex Deucher Signed-off-by: Dave Airlie --- Documentation/DocBook/drm.tmpl | 143 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index cea420dc712d..4d54ac8b2032 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -1235,6 +1235,15 @@ int max_width, max_height; Miscellaneous + + void (*set_property)(struct drm_crtc *crtc, + struct drm_property *property, uint64_t value); + + Set the value of the given CRTC property to + value. See + for more information about properties. + + void (*gamma_set)(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, uint32_t start, uint32_t size); @@ -1385,6 +1394,15 @@ int max_width, max_height; . + + void (*set_property)(struct drm_plane *plane, + struct drm_property *property, uint64_t value); + + Set the value of the given plane property to + value. See + for more information about properties. + + @@ -1593,6 +1611,15 @@ int max_width, max_height; Miscellaneous + + void (*set_property)(struct drm_connector *connector, + struct drm_property *property, uint64_t value); + + Set the value of the given connector property to + value. See + for more information about properties. + + void (*destroy)(struct drm_connector *connector); @@ -2187,6 +2214,122 @@ void intel_crt_init(struct drm_device *dev) + + + + KMS Properties + + Drivers may need to expose additional parameters to applications than + those described in the previous sections. KMS supports attaching + properties to CRTCs, connectors and planes and offers a userspace API to + list, get and set the property values. + + + Properties are identified by a name that uniquely defines the property + purpose, and store an associated value. For all property types except blob + properties the value is a 64-bit unsigned integer. + + + KMS differentiates between properties and property instances. Drivers + first create properties and then create and associate individual instances + of those properties to objects. A property can be instantiated multiple + times and associated with different objects. Values are stored in property + instances, and all other property information are stored in the propery + and shared between all instances of the property. + + + Every property is created with a type that influences how the KMS core + handles the property. Supported property types are + + + DRM_MODE_PROP_RANGE + Range properties report their minimum and maximum + admissible values. The KMS core verifies that values set by + application fit in that range. + + + DRM_MODE_PROP_ENUM + Enumerated properties take a numerical value that + ranges from 0 to the number of enumerated values defined by the + property minus one, and associate a free-formed string name to each + value. Applications can retrieve the list of defined value-name pairs + and use the numerical value to get and set property instance values. + + + + DRM_MODE_PROP_BITMASK + Bitmask properties are enumeration properties that + additionally restrict all enumerated values to the 0..63 range. + Bitmask property instance values combine one or more of the + enumerated bits defined by the property. + + + DRM_MODE_PROP_BLOB + Blob properties store a binary blob without any format + restriction. The binary blobs are created as KMS standalone objects, + and blob property instance values store the ID of their associated + blob object. + Blob properties are only used for the connector EDID property + and cannot be created by drivers. + + + + + To create a property drivers call one of the following functions depending + on the property type. All property creation functions take property flags + and name, as well as type-specific arguments. + + + struct drm_property *drm_property_create_range(struct drm_device *dev, int flags, + const char *name, + uint64_t min, uint64_t max); + Create a range property with the given minimum and maximum + values. + + + struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags, + const char *name, + const struct drm_prop_enum_list *props, + int num_values); + Create an enumerated property. The props + argument points to an array of num_values + value-name pairs. + + + struct drm_property *drm_property_create_bitmask(struct drm_device *dev, + int flags, const char *name, + const struct drm_prop_enum_list *props, + int num_values); + Create a bitmask property. The props + argument points to an array of num_values + value-name pairs. + + + + + Properties can additionally be created as immutable, in which case they + will be read-only for applications but can be modified by the driver. To + create an immutable property drivers must set the DRM_MODE_PROP_IMMUTABLE + flag at property creation time. + + + When no array of value-name pairs is readily available at property + creation time for enumerated or range properties, drivers can create + the property using the drm_property_create function + and manually add enumeration value-name pairs by calling the + drm_property_add_enum function. Care must be taken to + properly specify the property type through the flags + argument. + + + After creating properties drivers can attach property instances to CRTC, + connector and plane objects by calling the + drm_object_attach_property. The function takes a + pointer to the target object, a pointer to the previously created property + and an initial instance value. + + + -- cgit v1.2.3 From 621e84d6f373dcb273ebfd772638b8e7dc3c2c48 Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Wed, 26 Jun 2013 16:11:27 +0200 Subject: dev: introduce skb_scrub_packet() The goal of this new function is to perform all needed cleanup before sending an skb into another netns. Signed-off-by: Nicolas Dichtel Signed-off-by: David S. Miller --- include/linux/skbuff.h | 1 + net/core/dev.c | 11 +---------- net/core/skbuff.c | 23 +++++++++++++++++++++++ 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index a7393adea0b5..6b06023e8a08 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -2384,6 +2384,7 @@ extern void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len); extern int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen); +extern void skb_scrub_packet(struct sk_buff *skb); extern struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features); diff --git a/net/core/dev.c b/net/core/dev.c index 722f633926e0..370354a9c5f6 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1652,22 +1652,13 @@ int dev_forward_skb(struct net_device *dev, struct sk_buff *skb) } } - skb_orphan(skb); - if (unlikely(!is_skb_forwardable(dev, skb))) { atomic_long_inc(&dev->rx_dropped); kfree_skb(skb); return NET_RX_DROP; } - skb->skb_iif = 0; - skb_dst_drop(skb); - skb->tstamp.tv64 = 0; - skb->pkt_type = PACKET_HOST; + skb_scrub_packet(skb); skb->protocol = eth_type_trans(skb, dev); - skb->mark = 0; - secpath_reset(skb); - nf_reset(skb); - nf_reset_trace(skb); return netif_rx(skb); } EXPORT_SYMBOL_GPL(dev_forward_skb); diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 9f73eca29fbe..b1fcb8727e56 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -3492,3 +3492,26 @@ bool skb_try_coalesce(struct sk_buff *to, struct sk_buff *from, return true; } EXPORT_SYMBOL(skb_try_coalesce); + +/** + * skb_scrub_packet - scrub an skb before sending it to another netns + * + * @skb: buffer to clean + * + * skb_scrub_packet can be used to clean an skb before injecting it in + * another namespace. We have to clear all information in the skb that + * could impact namespace isolation. + */ +void skb_scrub_packet(struct sk_buff *skb) +{ + skb_orphan(skb); + skb->tstamp.tv64 = 0; + skb->pkt_type = PACKET_HOST; + skb->skb_iif = 0; + skb_dst_drop(skb); + skb->mark = 0; + secpath_reset(skb); + nf_reset(skb); + nf_reset_trace(skb); +} +EXPORT_SYMBOL_GPL(skb_scrub_packet); -- cgit v1.2.3 From 5e6700b3bf98fe98d630bf9c939ad4c85ce95592 Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Wed, 26 Jun 2013 16:11:28 +0200 Subject: sit: add support of x-netns This patch allows to switch the netns when packet is encapsulated or decapsulated. In other word, the encapsulated packet is received in a netns, where the lookup is done to find the tunnel. Once the tunnel is found, the packet is decapsulated and injecting into the corresponding interface which stands to another netns. When one of the two netns is removed, the tunnel is destroyed. Signed-off-by: Nicolas Dichtel Signed-off-by: David S. Miller --- include/net/ip_tunnels.h | 1 + net/ipv4/ip_tunnel.c | 10 +++++++++- net/ipv6/sit.c | 42 ++++++++++++++++++++++++++++++++---------- 3 files changed, 42 insertions(+), 11 deletions(-) diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h index b0d982471a5c..781b3cf86a2f 100644 --- a/include/net/ip_tunnels.h +++ b/include/net/ip_tunnels.h @@ -42,6 +42,7 @@ struct ip_tunnel { struct ip_tunnel __rcu *next; struct hlist_node hash_node; struct net_device *dev; + struct net *net; /* netns for packet i/o */ int err_count; /* Number of arrived ICMP errors */ unsigned long err_time; /* Time when the last ICMP error diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index 3b00d81c8f1e..394cebc96d22 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c @@ -304,6 +304,7 @@ static struct net_device *__ip_tunnel_create(struct net *net, tunnel = netdev_priv(dev); tunnel->parms = *parms; + tunnel->net = net; err = register_netdevice(dev); if (err) @@ -453,6 +454,9 @@ int ip_tunnel_rcv(struct ip_tunnel *tunnel, struct sk_buff *skb, tstats->rx_bytes += skb->len; u64_stats_update_end(&tstats->syncp); + if (tunnel->net != dev_net(tunnel->dev)) + skb_scrub_packet(skb); + if (tunnel->dev->type == ARPHRD_ETHER) { skb->protocol = eth_type_trans(skb, tunnel->dev); skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN); @@ -541,7 +545,7 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, tos = ipv6_get_dsfield((const struct ipv6hdr *)inner_iph); } - rt = ip_route_output_tunnel(dev_net(dev), &fl4, + rt = ip_route_output_tunnel(tunnel->net, &fl4, protocol, dst, tnl_params->saddr, tunnel->parms.o_key, @@ -602,6 +606,9 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, } #endif + if (tunnel->net != dev_net(dev)) + skb_scrub_packet(skb); + if (tunnel->err_count > 0) { if (time_before(jiffies, tunnel->err_time + IPTUNNEL_ERR_TIMEO)) { @@ -888,6 +895,7 @@ int ip_tunnel_newlink(struct net_device *dev, struct nlattr *tb[], if (ip_tunnel_find(itn, p, dev->type)) return -EEXIST; + nt->net = net; nt->parms = *p; err = register_netdevice(dev); if (err) diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index f639866b3dcf..97a0bfe2c293 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -466,14 +466,14 @@ isatap_chksrc(struct sk_buff *skb, const struct iphdr *iph, struct ip_tunnel *t) static void ipip6_tunnel_uninit(struct net_device *dev) { - struct net *net = dev_net(dev); - struct sit_net *sitn = net_generic(net, sit_net_id); + struct ip_tunnel *tunnel = netdev_priv(dev); + struct sit_net *sitn = net_generic(tunnel->net, sit_net_id); if (dev == sitn->fb_tunnel_dev) { RCU_INIT_POINTER(sitn->tunnels_wc[0], NULL); } else { - ipip6_tunnel_unlink(sitn, netdev_priv(dev)); - ipip6_tunnel_del_prl(netdev_priv(dev), NULL); + ipip6_tunnel_unlink(sitn, tunnel); + ipip6_tunnel_del_prl(tunnel, NULL); } dev_put(dev); } @@ -621,6 +621,8 @@ static int ipip6_rcv(struct sk_buff *skb) tstats->rx_packets++; tstats->rx_bytes += skb->len; + if (tunnel->net != dev_net(tunnel->dev)) + skb_scrub_packet(skb); netif_rx(skb); return 0; @@ -803,7 +805,7 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, goto tx_error; } - rt = ip_route_output_ports(dev_net(dev), &fl4, NULL, + rt = ip_route_output_ports(tunnel->net, &fl4, NULL, dst, tiph->saddr, 0, 0, IPPROTO_IPV6, RT_TOS(tos), @@ -858,6 +860,9 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, tunnel->err_count = 0; } + if (tunnel->net != dev_net(dev)) + skb_scrub_packet(skb); + /* * Okay, now see if we can stuff it in the buffer as-is. */ @@ -944,7 +949,8 @@ static void ipip6_tunnel_bind_dev(struct net_device *dev) iph = &tunnel->parms.iph; if (iph->daddr) { - struct rtable *rt = ip_route_output_ports(dev_net(dev), &fl4, NULL, + struct rtable *rt = ip_route_output_ports(tunnel->net, &fl4, + NULL, iph->daddr, iph->saddr, 0, 0, IPPROTO_IPV6, @@ -959,7 +965,7 @@ static void ipip6_tunnel_bind_dev(struct net_device *dev) } if (!tdev && tunnel->parms.link) - tdev = __dev_get_by_index(dev_net(dev), tunnel->parms.link); + tdev = __dev_get_by_index(tunnel->net, tunnel->parms.link); if (tdev) { dev->hard_header_len = tdev->hard_header_len + sizeof(struct iphdr); @@ -972,7 +978,7 @@ static void ipip6_tunnel_bind_dev(struct net_device *dev) static void ipip6_tunnel_update(struct ip_tunnel *t, struct ip_tunnel_parm *p) { - struct net *net = dev_net(t->dev); + struct net *net = t->net; struct sit_net *sitn = net_generic(net, sit_net_id); ipip6_tunnel_unlink(sitn, t); @@ -1248,7 +1254,6 @@ static void ipip6_tunnel_setup(struct net_device *dev) dev->priv_flags &= ~IFF_XMIT_DST_RELEASE; dev->iflink = 0; dev->addr_len = 4; - dev->features |= NETIF_F_NETNS_LOCAL; dev->features |= NETIF_F_LLTX; } @@ -1257,6 +1262,7 @@ static int ipip6_tunnel_init(struct net_device *dev) struct ip_tunnel *tunnel = netdev_priv(dev); tunnel->dev = dev; + tunnel->net = dev_net(dev); memcpy(dev->dev_addr, &tunnel->parms.iph.saddr, 4); memcpy(dev->broadcast, &tunnel->parms.iph.daddr, 4); @@ -1277,6 +1283,7 @@ static int __net_init ipip6_fb_tunnel_init(struct net_device *dev) struct sit_net *sitn = net_generic(net, sit_net_id); tunnel->dev = dev; + tunnel->net = dev_net(dev); strcpy(tunnel->parms.name, dev->name); iph->version = 4; @@ -1564,8 +1571,14 @@ static struct xfrm_tunnel ipip_handler __read_mostly = { static void __net_exit sit_destroy_tunnels(struct sit_net *sitn, struct list_head *head) { + struct net *net = dev_net(sitn->fb_tunnel_dev); + struct net_device *dev, *aux; int prio; + for_each_netdev_safe(net, dev, aux) + if (dev->rtnl_link_ops == &sit_link_ops) + unregister_netdevice_queue(dev, head); + for (prio = 1; prio < 4; prio++) { int h; for (h = 0; h < HASH_SIZE; h++) { @@ -1573,7 +1586,12 @@ static void __net_exit sit_destroy_tunnels(struct sit_net *sitn, struct list_hea t = rtnl_dereference(sitn->tunnels[prio][h]); while (t != NULL) { - unregister_netdevice_queue(t->dev, head); + /* If dev is in the same netns, it has already + * been added to the list by the previous loop. + */ + if (dev_net(t->dev) != net) + unregister_netdevice_queue(t->dev, + head); t = rtnl_dereference(t->next); } } @@ -1598,6 +1616,10 @@ static int __net_init sit_init_net(struct net *net) goto err_alloc_dev; } dev_net_set(sitn->fb_tunnel_dev, net); + /* FB netdevice is special: we have one, and only one per netns. + * Allowing to move it to another netns is clearly unsafe. + */ + sitn->fb_tunnel_dev->features |= NETIF_F_NETNS_LOCAL; err = ipip6_fb_tunnel_init(sitn->fb_tunnel_dev); if (err) -- cgit v1.2.3 From 3a36515f729458c8efa0c124c7262d5843ad5c37 Mon Sep 17 00:00:00 2001 From: Pablo Neira Date: Fri, 28 Jun 2013 03:04:23 +0200 Subject: netlink: fix splat in skb_clone with large messages Since (c05cdb1 netlink: allow large data transfers from user-space), netlink splats if it invokes skb_clone on large netlink skbs since: * skb_shared_info was not correctly initialized. * skb->destructor is not set in the cloned skb. This was spotted by trinity: [ 894.990671] BUG: unable to handle kernel paging request at ffffc9000047b001 [ 894.991034] IP: [] skb_clone+0x24/0xc0 [...] [ 894.991034] Call Trace: [ 894.991034] [] nl_fib_input+0x6a/0x240 [ 894.991034] [] ? _raw_read_unlock+0x26/0x40 [ 894.991034] [] netlink_unicast+0x169/0x1e0 [ 894.991034] [] netlink_sendmsg+0x251/0x3d0 Fix it by: 1) introducing a new netlink_skb_clone function that is used in nl_fib_input, that sets our special skb->destructor in the cloned skb. Moreover, handle the release of the large cloned skb head area in the destructor path. 2) not allowing large skbuffs in the netlink broadcast path. I cannot find any reasonable use of the large data transfer using netlink in that path, moreover this helps to skip extra skb_clone handling. I found two more netlink clients that are cloning the skbs, but they are not in the sendmsg path. Therefore, the sole client cloning that I found seems to be the fib frontend. Thanks to Eric Dumazet for helping to address this issue. Reported-by: Fengguang Wu Signed-off-by: Pablo Neira Ayuso Signed-off-by: David S. Miller --- include/linux/netlink.h | 16 ++++++++++++++++ net/ipv4/fib_frontend.c | 2 +- net/netlink/af_netlink.c | 35 ++++++++++++++++++----------------- 3 files changed, 35 insertions(+), 18 deletions(-) diff --git a/include/linux/netlink.h b/include/linux/netlink.h index 86fde81ac2e6..7a6c396a263b 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -85,6 +85,22 @@ int netlink_attachskb(struct sock *sk, struct sk_buff *skb, void netlink_detachskb(struct sock *sk, struct sk_buff *skb); int netlink_sendskb(struct sock *sk, struct sk_buff *skb); +static inline struct sk_buff * +netlink_skb_clone(struct sk_buff *skb, gfp_t gfp_mask) +{ + struct sk_buff *nskb; + + nskb = skb_clone(skb, gfp_mask); + if (!nskb) + return NULL; + + /* This is a large skb, set destructor callback to release head */ + if (is_vmalloc_addr(skb->head)) + nskb->destructor = skb->destructor; + + return nskb; +} + /* * skb should fit one page. This choice is good for headerless malloc. * But we should limit to 8K so that userspace does not have to diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 05a4888dede9..b3f627ac4ed8 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -961,7 +961,7 @@ static void nl_fib_input(struct sk_buff *skb) nlmsg_len(nlh) < sizeof(*frn)) return; - skb = skb_clone(skb, GFP_KERNEL); + skb = netlink_skb_clone(skb, GFP_KERNEL); if (skb == NULL) return; nlh = nlmsg_hdr(skb); diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 6967fbcca6c5..0c61b59175dc 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -849,7 +849,10 @@ static void netlink_skb_destructor(struct sk_buff *skb) } #endif if (is_vmalloc_addr(skb->head)) { - vfree(skb->head); + if (!skb->cloned || + !atomic_dec_return(&(skb_shinfo(skb)->dataref))) + vfree(skb->head); + skb->head = NULL; } if (skb->sk != NULL) @@ -1532,33 +1535,31 @@ struct sock *netlink_getsockbyfilp(struct file *filp) return sock; } -static struct sk_buff *netlink_alloc_large_skb(unsigned int size) +static struct sk_buff *netlink_alloc_large_skb(unsigned int size, + int broadcast) { struct sk_buff *skb; void *data; - if (size <= NLMSG_GOODSIZE) + if (size <= NLMSG_GOODSIZE || broadcast) return alloc_skb(size, GFP_KERNEL); - skb = alloc_skb_head(GFP_KERNEL); - if (skb == NULL) - return NULL; + size = SKB_DATA_ALIGN(size) + + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); data = vmalloc(size); if (data == NULL) - goto err; + return NULL; - skb->head = data; - skb->data = data; - skb_reset_tail_pointer(skb); - skb->end = skb->tail + size; - skb->len = 0; - skb->destructor = netlink_skb_destructor; + skb = build_skb(data, size); + if (skb == NULL) + vfree(data); + else { + skb->head_frag = 0; + skb->destructor = netlink_skb_destructor; + } return skb; -err: - kfree_skb(skb); - return NULL; } /* @@ -2244,7 +2245,7 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock, if (len > sk->sk_sndbuf - 32) goto out; err = -ENOBUFS; - skb = netlink_alloc_large_skb(len); + skb = netlink_alloc_large_skb(len, dst_group); if (skb == NULL) goto out; -- cgit v1.2.3 From 8d2ada77f8a7f8f65fcbf71b23cbac54b64151a6 Mon Sep 17 00:00:00 2001 From: "nikolay@redhat.com" Date: Wed, 26 Jun 2013 17:13:37 +0200 Subject: bonding: remove unnecessary setup_by_slave member We have a member called setup_by_slave in struct bonding to denote if the bond dev has different type than ARPHRD_ETHER, but that is already denoted in bond's netdev type variable if it was setup by the slave, so use that instead of the member. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 5 +---- drivers/net/bonding/bonding.h | 1 - 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 142d55dc526e..2e8b9f1e2747 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1382,8 +1382,6 @@ done: static void bond_setup_by_slave(struct net_device *bond_dev, struct net_device *slave_dev) { - struct bonding *bond = netdev_priv(bond_dev); - bond_dev->header_ops = slave_dev->header_ops; bond_dev->type = slave_dev->type; @@ -1392,7 +1390,6 @@ static void bond_setup_by_slave(struct net_device *bond_dev, memcpy(bond_dev->broadcast, slave_dev->broadcast, slave_dev->addr_len); - bond->setup_by_slave = 1; } /* On bonding slaves other than the currently active slave, suppress @@ -3187,7 +3184,7 @@ static int bond_slave_netdev_event(unsigned long event, switch (event) { case NETDEV_UNREGISTER: - if (bond->setup_by_slave) + if (bond_dev->type != ARPHRD_ETHER) bond_release_and_destroy(bond_dev, slave_dev); else bond_release(bond_dev, slave_dev); diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h index 3fb73cc8c34a..c6c8d03562e5 100644 --- a/drivers/net/bonding/bonding.h +++ b/drivers/net/bonding/bonding.h @@ -226,7 +226,6 @@ struct bonding { rwlock_t lock; rwlock_t curr_slave_lock; u8 send_peer_notif; - s8 setup_by_slave; u8 igmp_retrans; #ifdef CONFIG_PROC_FS struct proc_dir_entry *proc_entry; -- cgit v1.2.3 From 97a1e6396b07581249506952a4c417dc6d2a4f9c Mon Sep 17 00:00:00 2001 From: "nikolay@redhat.com" Date: Wed, 26 Jun 2013 17:13:38 +0200 Subject: bonding: remove unnecessary dev_addr_from_first member In struct bonding there's a member called dev_addr_from_first which is used to denote when the bond dev should clone the first slave's MAC address but since we have netdev's addr_assign_type variable that is not necessary. We clone the first slave's MAC each time we have a random MAC set to the bond device. This has the nice side-effect of also fixing an inconsistency - when the MAC address of the bond dev is set after its creation, but prior to having slaves, it's not kept and the first slave's MAC is cloned. The only way to keep the MAC was to create the bond device with the MAC address set (e.g. through ip link). In all cases if the bond device is left without any slaves - its MAC gets reset to a random one as before. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 7 ++----- drivers/net/bonding/bonding.h | 1 - 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 2e8b9f1e2747..0da3c126c7c4 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1596,7 +1596,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) /* If this is the first slave, then we need to set the master's hardware * address to be the same as the slave's. */ - if (bond->slave_cnt == 0 && bond->dev_addr_from_first) + if (!bond->slave_cnt && bond->dev->addr_assign_type == NET_ADDR_RANDOM) bond_set_dev_addr(bond->dev, slave_dev); new_slave = kzalloc(sizeof(struct slave), GFP_KERNEL); @@ -2041,7 +2041,6 @@ static int __bond_release_one(struct net_device *bond_dev, if (bond->slave_cnt == 0) { bond_set_carrier(bond); eth_hw_addr_random(bond_dev); - bond->dev_addr_from_first = true; if (bond_vlan_used(bond)) { pr_warning("%s: Warning: clearing HW address of %s while it still has VLANs.\n", @@ -4800,10 +4799,8 @@ static int bond_init(struct net_device *bond_dev) /* Ensure valid dev_addr */ if (is_zero_ether_addr(bond_dev->dev_addr) && - bond_dev->addr_assign_type == NET_ADDR_PERM) { + bond_dev->addr_assign_type == NET_ADDR_PERM) eth_hw_addr_random(bond_dev); - bond->dev_addr_from_first = true; - } return 0; } diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h index c6c8d03562e5..42d1c6599cba 100644 --- a/drivers/net/bonding/bonding.h +++ b/drivers/net/bonding/bonding.h @@ -248,7 +248,6 @@ struct bonding { /* debugging support via debugfs */ struct dentry *debug_dir; #endif /* CONFIG_DEBUG_FS */ - bool dev_addr_from_first; }; static inline bool bond_vlan_used(struct bonding *bond) -- cgit v1.2.3 From ae0d67505ca30c635f7763564622c9710913f293 Mon Sep 17 00:00:00 2001 From: "nikolay@redhat.com" Date: Wed, 26 Jun 2013 17:13:39 +0200 Subject: bonding: when cloning a MAC use NET_ADDR_STOLEN A simple semantic change, when a slave's MAC is cloned by the bond master then set addr_assign_type to NET_ADDR_STOLEN instead of NET_ADDR_SET. Also use bond_set_dev_addr() in BOND_FOM_ACTIVE mode to change the bond's MAC address because the assign_type has to be set properly. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 0da3c126c7c4..ec44580b076f 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -833,6 +833,24 @@ static void bond_hw_addr_swap(struct bonding *bond, struct slave *new_active, } } +/** + * bond_set_dev_addr - clone slave's address to bond + * @bond_dev: bond net device + * @slave_dev: slave net device + * + * Should be called with RTNL held. + */ +static void bond_set_dev_addr(struct net_device *bond_dev, + struct net_device *slave_dev) +{ + pr_debug("bond_dev=%p\n", bond_dev); + pr_debug("slave_dev=%p\n", slave_dev); + pr_debug("slave_dev->addr_len=%d\n", slave_dev->addr_len); + memcpy(bond_dev->dev_addr, slave_dev->dev_addr, slave_dev->addr_len); + bond_dev->addr_assign_type = NET_ADDR_STOLEN; + call_netdevice_notifiers(NETDEV_CHANGEADDR, bond_dev); +} + /* * bond_do_fail_over_mac * @@ -855,11 +873,9 @@ static void bond_do_fail_over_mac(struct bonding *bond, switch (bond->params.fail_over_mac) { case BOND_FOM_ACTIVE: if (new_active) { - memcpy(bond->dev->dev_addr, new_active->dev->dev_addr, - new_active->dev->addr_len); write_unlock_bh(&bond->curr_slave_lock); read_unlock(&bond->lock); - call_netdevice_notifiers(NETDEV_CHANGEADDR, bond->dev); + bond_set_dev_addr(bond->dev, new_active->dev); read_lock(&bond->lock); write_lock_bh(&bond->curr_slave_lock); } @@ -1290,17 +1306,6 @@ static void bond_netpoll_cleanup(struct net_device *bond_dev) /*---------------------------------- IOCTL ----------------------------------*/ -static void bond_set_dev_addr(struct net_device *bond_dev, - struct net_device *slave_dev) -{ - pr_debug("bond_dev=%p\n", bond_dev); - pr_debug("slave_dev=%p\n", slave_dev); - pr_debug("slave_dev->addr_len=%d\n", slave_dev->addr_len); - memcpy(bond_dev->dev_addr, slave_dev->dev_addr, slave_dev->addr_len); - bond_dev->addr_assign_type = NET_ADDR_SET; - call_netdevice_notifiers(NETDEV_CHANGEADDR, bond_dev); -} - static netdev_features_t bond_fix_features(struct net_device *dev, netdev_features_t features) { -- cgit v1.2.3 From 20c3da9f9fc6cdedca887953bbded72f7f025492 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 26 Jun 2013 09:32:50 -0700 Subject: Input: nspire-keypad - remove redundant dev_err call in nspire_keypad_probe() There is a error message within devm_ioremap_resource already, so remove the dev_err call to avoid redundant error message. Signed-off-by: Wei Yongjun Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/nspire-keypad.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/input/keyboard/nspire-keypad.c b/drivers/input/keyboard/nspire-keypad.c index 1b0d04c68d45..e0a1339e40e6 100644 --- a/drivers/input/keyboard/nspire-keypad.c +++ b/drivers/input/keyboard/nspire-keypad.c @@ -209,10 +209,8 @@ static int nspire_keypad_probe(struct platform_device *pdev) } keypad->reg_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(keypad->reg_base)) { - dev_err(&pdev->dev, "failed to remap I/O memory\n"); + if (IS_ERR(keypad->reg_base)) return PTR_ERR(keypad->reg_base); - } keypad->input = input = devm_input_allocate_device(&pdev->dev); if (!input) { -- cgit v1.2.3 From ee89bd6bc73d1d14555418a2642172448052f1dd Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sun, 9 Jun 2013 11:46:43 +0200 Subject: lib: Move fonts from drivers/video/console/ to lib/fonts/ Several drivers need font support independent of CONFIG_VT, cfr. commit 9cbce8d7e1dae0744ca4f68d62aa7de18196b6f4, "console/font: Refactor font support code selection logic"). Hence move the fonts and their support logic from drivers/video/console/ to its own library directory lib/fonts/. This also allows to limit processing of drivers/video/console/Makefile to CONFIG_VT=y again. [Kevin Hilman : Update arch/arm/boot/compressed/Makefile] Signed-off-by: Geert Uytterhoeven --- Documentation/DocBook/device-drivers.tmpl | 4 +- arch/arm/boot/compressed/Makefile | 2 +- arch/m68k/kernel/asm-offsets.c | 2 +- arch/unicore32/boot/compressed/Makefile | 2 +- drivers/video/Makefile | 2 +- drivers/video/console/Kconfig | 114 - drivers/video/console/Makefile | 18 - drivers/video/console/font_10x18.c | 5146 ------------------------ drivers/video/console/font_6x11.c | 3352 ---------------- drivers/video/console/font_7x14.c | 4118 ------------------- drivers/video/console/font_8x16.c | 4633 ---------------------- drivers/video/console/font_8x8.c | 2583 ------------ drivers/video/console/font_acorn_8x8.c | 275 -- drivers/video/console/font_mini_4x6.c | 2158 ---------- drivers/video/console/font_pearl_8x8.c | 2587 ------------ drivers/video/console/font_sun12x22.c | 6165 ----------------------------- drivers/video/console/font_sun8x16.c | 275 -- drivers/video/console/fonts.c | 153 - lib/Kconfig | 2 + lib/Makefile | 2 + lib/fonts/Kconfig | 117 + lib/fonts/Makefile | 18 + lib/fonts/font_10x18.c | 5146 ++++++++++++++++++++++++ lib/fonts/font_6x11.c | 3352 ++++++++++++++++ lib/fonts/font_7x14.c | 4118 +++++++++++++++++++ lib/fonts/font_8x16.c | 4633 ++++++++++++++++++++++ lib/fonts/font_8x8.c | 2583 ++++++++++++ lib/fonts/font_acorn_8x8.c | 275 ++ lib/fonts/font_mini_4x6.c | 2158 ++++++++++ lib/fonts/font_pearl_8x8.c | 2587 ++++++++++++ lib/fonts/font_sun12x22.c | 6165 +++++++++++++++++++++++++++++ lib/fonts/font_sun8x16.c | 275 ++ lib/fonts/fonts.c | 153 + 33 files changed, 31590 insertions(+), 31583 deletions(-) delete mode 100644 drivers/video/console/font_10x18.c delete mode 100644 drivers/video/console/font_6x11.c delete mode 100644 drivers/video/console/font_7x14.c delete mode 100644 drivers/video/console/font_8x16.c delete mode 100644 drivers/video/console/font_8x8.c delete mode 100644 drivers/video/console/font_acorn_8x8.c delete mode 100644 drivers/video/console/font_mini_4x6.c delete mode 100644 drivers/video/console/font_pearl_8x8.c delete mode 100644 drivers/video/console/font_sun12x22.c delete mode 100644 drivers/video/console/font_sun8x16.c delete mode 100644 drivers/video/console/fonts.c create mode 100644 lib/fonts/Kconfig create mode 100644 lib/fonts/Makefile create mode 100644 lib/fonts/font_10x18.c create mode 100644 lib/fonts/font_6x11.c create mode 100644 lib/fonts/font_7x14.c create mode 100644 lib/fonts/font_8x16.c create mode 100644 lib/fonts/font_8x8.c create mode 100644 lib/fonts/font_acorn_8x8.c create mode 100644 lib/fonts/font_mini_4x6.c create mode 100644 lib/fonts/font_pearl_8x8.c create mode 100644 lib/fonts/font_sun12x22.c create mode 100644 lib/fonts/font_sun8x16.c create mode 100644 lib/fonts/fonts.c diff --git a/Documentation/DocBook/device-drivers.tmpl b/Documentation/DocBook/device-drivers.tmpl index c36892c072da..fca34192cf80 100644 --- a/Documentation/DocBook/device-drivers.tmpl +++ b/Documentation/DocBook/device-drivers.tmpl @@ -297,10 +297,10 @@ KAO --> Frame Buffer Fonts - Refer to the file drivers/video/console/fonts.c for more information. + Refer to the file lib/fonts/fonts.c for more information. diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile index 3580d57ea218..6d7b013a89c2 100644 --- a/arch/arm/boot/compressed/Makefile +++ b/arch/arm/boot/compressed/Makefile @@ -27,7 +27,7 @@ OBJS += misc.o decompress.o ifeq ($(CONFIG_DEBUG_UNCOMPRESS),y) OBJS += debug.o endif -FONTC = $(srctree)/drivers/video/console/font_acorn_8x8.c +FONTC = $(srctree)/lib/fonts/font_acorn_8x8.c # string library code (-Os is enforced to keep it much smaller) OBJS += string.o diff --git a/arch/m68k/kernel/asm-offsets.c b/arch/m68k/kernel/asm-offsets.c index a972b00cd77d..8b7b22846366 100644 --- a/arch/m68k/kernel/asm-offsets.c +++ b/arch/m68k/kernel/asm-offsets.c @@ -77,7 +77,7 @@ int main(void) DEFINE(BIR_SIZE, offsetof(struct bi_record, size)); DEFINE(BIR_DATA, offsetof(struct bi_record, data)); - /* offsets into font_desc (drivers/video/console/font.h) */ + /* offsets into the font_desc struct */ DEFINE(FONT_DESC_IDX, offsetof(struct font_desc, idx)); DEFINE(FONT_DESC_NAME, offsetof(struct font_desc, name)); DEFINE(FONT_DESC_WIDTH, offsetof(struct font_desc, width)); diff --git a/arch/unicore32/boot/compressed/Makefile b/arch/unicore32/boot/compressed/Makefile index 950a9afa38f8..96494fb646f7 100644 --- a/arch/unicore32/boot/compressed/Makefile +++ b/arch/unicore32/boot/compressed/Makefile @@ -17,7 +17,7 @@ OBJS := misc.o # font.c and font.o CFLAGS_font.o := -Dstatic= -$(obj)/font.c: $(srctree)/drivers/video/console/font_8x8.c +$(obj)/font.c: $(srctree)/lib/fonts/font_8x8.c $(call cmd,shipped) # piggy.S and piggy.o diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 8c8be7e15f9c..e8bae8dd4804 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -12,7 +12,7 @@ fb-y := fbmem.o fbmon.o fbcmap.o fbsysfs.o \ modedb.o fbcvt.o fb-objs := $(fb-y) -obj-y += console/ +obj-$(CONFIG_VT) += console/ obj-$(CONFIG_LOGO) += logo/ obj-y += backlight/ diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig index 8af6ad3f1323..8c30603e0a86 100644 --- a/drivers/video/console/Kconfig +++ b/drivers/video/console/Kconfig @@ -134,119 +134,5 @@ config STI_CONSOLE machines. Say Y here to build support for it into your kernel. The alternative is to use your primary serial port as a console. -config FONT_SUPPORT - tristate - -if FONT_SUPPORT - -config FONTS - bool "Select compiled-in fonts" - depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE - help - Say Y here if you would like to use fonts other than the default - your frame buffer console usually use. - - Note that the answer to this question won't directly affect the - kernel: saying N will just cause the configurator to skip all - the questions about foreign fonts. - - If unsure, say N (the default choices are safe). - -config FONT_8x8 - bool "VGA 8x8 font" if FONTS - depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE - default y if !SPARC && !FONTS - help - This is the "high resolution" font for the VGA frame buffer (the one - provided by the text console 80x50 (and higher) modes). - - Note that this is a poor quality font. The VGA 8x16 font is quite a - lot more readable. - - Given the resolution provided by the frame buffer device, answer N - here is safe. - -config FONT_8x16 - bool "VGA 8x16 font" if FONTS - default y if !SPARC && !FONTS - help - This is the "high resolution" font for the VGA frame buffer (the one - provided by the VGA text console 80x25 mode. - - If unsure, say Y. - -config FONT_6x11 - bool "Mac console 6x11 font (not supported by all drivers)" if FONTS - depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE - default y if !SPARC && !FONTS && MAC - help - Small console font with Macintosh-style high-half glyphs. Some Mac - framebuffer drivers don't support this one at all. - -config FONT_7x14 - bool "console 7x14 font (not supported by all drivers)" if FONTS - depends on FRAMEBUFFER_CONSOLE - help - Console font with characters just a bit smaller than the default. - If the standard 8x16 font is a little too big for you, say Y. - Otherwise, say N. - -config FONT_PEARL_8x8 - bool "Pearl (old m68k) console 8x8 font" if FONTS - depends on FRAMEBUFFER_CONSOLE - default y if !SPARC && !FONTS && AMIGA - help - Small console font with PC-style control-character and high-half - glyphs. - -config FONT_ACORN_8x8 - bool "Acorn console 8x8 font" if FONTS - depends on FRAMEBUFFER_CONSOLE - default y if !SPARC && !FONTS && ARM && ARCH_ACORN - help - Small console font with PC-style control characters and high-half - glyphs. - -config FONT_MINI_4x6 - bool "Mini 4x6 font" - depends on !SPARC && FONTS - -config FONT_SUN8x16 - bool "Sparc console 8x16 font" - depends on FRAMEBUFFER_CONSOLE && (!SPARC && FONTS || SPARC) - help - This is the high resolution console font for Sun machines. Say Y. - -config FONT_SUN12x22 - bool "Sparc console 12x22 font (not supported by all drivers)" - depends on FRAMEBUFFER_CONSOLE && (!SPARC && FONTS || SPARC) - help - This is the high resolution console font for Sun machines with very - big letters (like the letters used in the SPARC PROM). If the - standard font is unreadable for you, say Y, otherwise say N. - -config FONT_10x18 - bool "console 10x18 font (not supported by all drivers)" if FONTS - depends on FRAMEBUFFER_CONSOLE - help - This is a high resolution console font for machines with very - big letters. It fits between the sun 12x22 and the normal 8x16 font. - If other fonts are too big or too small for you, say Y, otherwise say N. - -config FONT_AUTOSELECT - def_bool y - depends on !FONT_8x8 - depends on !FONT_6x11 - depends on !FONT_7x14 - depends on !FONT_PEARL_8x8 - depends on !FONT_ACORN_8x8 - depends on !FONT_MINI_4x6 - depends on !FONT_SUN8x16 - depends on !FONT_SUN12x22 - depends on !FONT_10x18 - select FONT_8x16 - -endif # FONT_SUPPORT - endmenu diff --git a/drivers/video/console/Makefile b/drivers/video/console/Makefile index 3a11b635b4da..43bfa485db96 100644 --- a/drivers/video/console/Makefile +++ b/drivers/video/console/Makefile @@ -2,24 +2,6 @@ # 5 Aug 1999, James Simmons, # Rewritten to use lists instead of if-statements. -# Font handling -font-objs := fonts.o - -font-objs-$(CONFIG_FONT_SUN8x16) += font_sun8x16.o -font-objs-$(CONFIG_FONT_SUN12x22) += font_sun12x22.o -font-objs-$(CONFIG_FONT_8x8) += font_8x8.o -font-objs-$(CONFIG_FONT_8x16) += font_8x16.o -font-objs-$(CONFIG_FONT_6x11) += font_6x11.o -font-objs-$(CONFIG_FONT_7x14) += font_7x14.o -font-objs-$(CONFIG_FONT_10x18) += font_10x18.o -font-objs-$(CONFIG_FONT_PEARL_8x8) += font_pearl_8x8.o -font-objs-$(CONFIG_FONT_ACORN_8x8) += font_acorn_8x8.o -font-objs-$(CONFIG_FONT_MINI_4x6) += font_mini_4x6.o - -font-objs += $(font-objs-y) - -obj-$(CONFIG_FONT_SUPPORT) += font.o - obj-$(CONFIG_DUMMY_CONSOLE) += dummycon.o obj-$(CONFIG_SGI_NEWPORT_CONSOLE) += newport_con.o obj-$(CONFIG_STI_CONSOLE) += sticon.o sticore.o diff --git a/drivers/video/console/font_10x18.c b/drivers/video/console/font_10x18.c deleted file mode 100644 index 6be72bb218ee..000000000000 --- a/drivers/video/console/font_10x18.c +++ /dev/null @@ -1,5146 +0,0 @@ -/******************************** - * adapted from font_sun12x22.c * - * by Jurriaan Kalkman 06-2005 * - ********************************/ - -#include - -#define FONTDATAMAX 9216 - -static const unsigned char fontdata_10x18[FONTDATAMAX] = { - - /* 0 0x00 '^@' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 1 0x01 '^A' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x3f, 0x80, /* 0011111110 */ - 0x40, 0x40, /* 0100000001 */ - 0x5b, 0x40, /* 0101101101 */ - 0x40, 0x40, /* 0100000001 */ - 0x44, 0x40, /* 0100010001 */ - 0x44, 0x40, /* 0100010001 */ - 0x51, 0x40, /* 0101000101 */ - 0x4e, 0x40, /* 0100111001 */ - 0x40, 0x40, /* 0100000001 */ - 0x3f, 0x80, /* 0011111110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 2 0x02 '^B' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x3f, 0x80, /* 0011111110 */ - 0x7f, 0xc0, /* 0111111111 */ - 0x64, 0xc0, /* 0110010011 */ - 0x7f, 0xc0, /* 0111111111 */ - 0x7b, 0xc0, /* 0111101111 */ - 0x7b, 0xc0, /* 0111101111 */ - 0x6e, 0xc0, /* 0110111011 */ - 0x71, 0xc0, /* 0111000111 */ - 0x7f, 0xc0, /* 0111111111 */ - 0x3f, 0x80, /* 0011111110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 3 0x03 '^C' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x11, 0x00, /* 0001000100 */ - 0x3b, 0x80, /* 0011101110 */ - 0x7f, 0xc0, /* 0111111111 */ - 0x3f, 0x80, /* 0011111110 */ - 0x3f, 0x80, /* 0011111110 */ - 0x1f, 0x00, /* 0001111100 */ - 0x1f, 0x00, /* 0001111100 */ - 0x0e, 0x00, /* 0000111000 */ - 0x0e, 0x00, /* 0000111000 */ - 0x04, 0x00, /* 0000010000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 4 0x04 '^D' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x04, 0x00, /* 0000010000 */ - 0x0e, 0x00, /* 0000111000 */ - 0x0e, 0x00, /* 0000111000 */ - 0x1f, 0x00, /* 0001111100 */ - 0x1f, 0x00, /* 0001111100 */ - 0x3f, 0x80, /* 0011111110 */ - 0x1f, 0x00, /* 0001111100 */ - 0x1f, 0x00, /* 0001111100 */ - 0x0e, 0x00, /* 0000111000 */ - 0x0e, 0x00, /* 0000111000 */ - 0x04, 0x00, /* 0000010000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 5 0x05 '^E' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x1e, 0x00, /* 0001111000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x31, 0x80, /* 0011000110 */ - 0x7b, 0xc0, /* 0111101111 */ - 0x35, 0x80, /* 0011010110 */ - 0x04, 0x00, /* 0000010000 */ - 0x0e, 0x00, /* 0000111000 */ - 0x1f, 0x00, /* 0001111100 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 6 0x06 '^F' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x04, 0x00, /* 0000010000 */ - 0x0e, 0x00, /* 0000111000 */ - 0x1f, 0x00, /* 0001111100 */ - 0x1f, 0x00, /* 0001111100 */ - 0x3f, 0x80, /* 0011111110 */ - 0x7f, 0xc0, /* 0111111111 */ - 0x7f, 0xc0, /* 0111111111 */ - 0x35, 0x80, /* 0011010110 */ - 0x04, 0x00, /* 0000010000 */ - 0x0e, 0x00, /* 0000111000 */ - 0x1f, 0x00, /* 0001111100 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 7 0x07 '^G' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x1e, 0x00, /* 0001111000 */ - 0x1e, 0x00, /* 0001111000 */ - 0x3f, 0x00, /* 0011111100 */ - 0x3f, 0x00, /* 0011111100 */ - 0x1e, 0x00, /* 0001111000 */ - 0x1e, 0x00, /* 0001111000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 8 0x08 '^H' */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xf3, 0xc0, /* 1111001111 */ - 0xe1, 0xc0, /* 1110000111 */ - 0xe1, 0xc0, /* 1110000111 */ - 0xc0, 0xc0, /* 1100000011 */ - 0xc0, 0xc0, /* 1100000011 */ - 0xe1, 0xc0, /* 1110000111 */ - 0xe1, 0xc0, /* 1110000111 */ - 0xf3, 0xc0, /* 1111001111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - - /* 9 0x09 '^I' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x12, 0x00, /* 0001001000 */ - 0x12, 0x00, /* 0001001000 */ - 0x21, 0x00, /* 0010000100 */ - 0x21, 0x00, /* 0010000100 */ - 0x12, 0x00, /* 0001001000 */ - 0x12, 0x00, /* 0001001000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 10 0x0a '^J' */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xf3, 0xc0, /* 1111001111 */ - 0xed, 0xc0, /* 1110110111 */ - 0xed, 0xc0, /* 1110110111 */ - 0xde, 0xc0, /* 1101111011 */ - 0xde, 0xc0, /* 1101111011 */ - 0xed, 0xc0, /* 1110110111 */ - 0xed, 0xc0, /* 1110110111 */ - 0xf3, 0xc0, /* 1111001111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - - /* 11 0x0b '^K' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x0f, 0xc0, /* 0000111111 */ - 0x0f, 0xc0, /* 0000111111 */ - 0x03, 0xc0, /* 0000001111 */ - 0x06, 0xc0, /* 0000011011 */ - 0x0c, 0xc0, /* 0000110011 */ - 0x3c, 0x00, /* 0011110000 */ - 0x66, 0x00, /* 0110011000 */ - 0xc3, 0x00, /* 1100001100 */ - 0xc3, 0x00, /* 1100001100 */ - 0xc3, 0x00, /* 1100001100 */ - 0x66, 0x00, /* 0110011000 */ - 0x3c, 0x00, /* 0011110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 12 0x0c '^L' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x1e, 0x00, /* 0001111000 */ - 0x33, 0x00, /* 0011001100 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x33, 0x00, /* 0011001100 */ - 0x1e, 0x00, /* 0001111000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x3f, 0x00, /* 0011111100 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 13 0x0d '^M' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x0f, 0x80, /* 0000111110 */ - 0x08, 0x80, /* 0000100010 */ - 0x0f, 0x80, /* 0000111110 */ - 0x08, 0x00, /* 0000100000 */ - 0x08, 0x00, /* 0000100000 */ - 0x08, 0x00, /* 0000100000 */ - 0x08, 0x00, /* 0000100000 */ - 0x08, 0x00, /* 0000100000 */ - 0x08, 0x00, /* 0000100000 */ - 0x38, 0x00, /* 0011100000 */ - 0x78, 0x00, /* 0111100000 */ - 0x30, 0x00, /* 0011000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 14 0x0e '^N' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x1f, 0x80, /* 0001111110 */ - 0x10, 0x80, /* 0001000010 */ - 0x1f, 0x80, /* 0001111110 */ - 0x10, 0x80, /* 0001000010 */ - 0x10, 0x80, /* 0001000010 */ - 0x10, 0x80, /* 0001000010 */ - 0x10, 0x80, /* 0001000010 */ - 0x13, 0x80, /* 0001001110 */ - 0x17, 0x80, /* 0001011110 */ - 0x73, 0x00, /* 0111001100 */ - 0xf0, 0x00, /* 1111000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 15 0x0f '^O' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x04, 0x00, /* 0000010000 */ - 0x24, 0x80, /* 0010010010 */ - 0x15, 0x00, /* 0001010100 */ - 0x55, 0x40, /* 0101010101 */ - 0x3f, 0x80, /* 0011111110 */ - 0x0e, 0x00, /* 0000111000 */ - 0x00, 0x00, /* 0000000000 */ - 0x0e, 0x00, /* 0000111000 */ - 0x3f, 0x80, /* 0011111110 */ - 0x55, 0x40, /* 0101010101 */ - 0x15, 0x00, /* 0001010100 */ - 0x24, 0x80, /* 0010010010 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 16 0x10 '^P' */ - 0x00, 0x80, /* 0000000010 */ - 0x01, 0x80, /* 0000000110 */ - 0x03, 0x80, /* 0000001110 */ - 0x07, 0x80, /* 0000011110 */ - 0x0f, 0x80, /* 0000111110 */ - 0x1f, 0x80, /* 0001111110 */ - 0x3f, 0x80, /* 0011111110 */ - 0x7f, 0x80, /* 0111111110 */ - 0xff, 0x80, /* 1111111110 */ - 0x7f, 0x80, /* 0111111110 */ - 0x3f, 0x80, /* 0011111110 */ - 0x1f, 0x80, /* 0001111110 */ - 0x0f, 0x80, /* 0000111110 */ - 0x07, 0x80, /* 0000011110 */ - 0x03, 0x80, /* 0000001110 */ - 0x01, 0x80, /* 0000000110 */ - 0x00, 0x80, /* 0000000010 */ - 0x00, 0x00, /* 0000000000 */ - - /* 17 0x11 '^Q' */ - 0x40, 0x00, /* 0100000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x70, 0x00, /* 0111000000 */ - 0x78, 0x00, /* 0111100000 */ - 0x7c, 0x00, /* 0111110000 */ - 0x7e, 0x00, /* 0111111000 */ - 0x7f, 0x00, /* 0111111100 */ - 0x7f, 0x80, /* 0111111110 */ - 0x7f, 0xc0, /* 0111111111 */ - 0x7f, 0x80, /* 0111111110 */ - 0x7f, 0x00, /* 0111111100 */ - 0x7e, 0x00, /* 0111111000 */ - 0x7c, 0x00, /* 0111110000 */ - 0x78, 0x00, /* 0111100000 */ - 0x70, 0x00, /* 0111000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x40, 0x00, /* 0100000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 18 0x12 '^R' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x1e, 0x00, /* 0001111000 */ - 0x3f, 0x00, /* 0011111100 */ - 0x7f, 0x80, /* 0111111110 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x7f, 0x80, /* 0111111110 */ - 0x3f, 0x00, /* 0011111100 */ - 0x1e, 0x00, /* 0001111000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 19 0x13 '^S' */ - 0x00, 0x00, /* 0000000000 */ - 0x33, 0x00, /* 0011001100 */ - 0x33, 0x00, /* 0011001100 */ - 0x33, 0x00, /* 0011001100 */ - 0x33, 0x00, /* 0011001100 */ - 0x33, 0x00, /* 0011001100 */ - 0x33, 0x00, /* 0011001100 */ - 0x33, 0x00, /* 0011001100 */ - 0x33, 0x00, /* 0011001100 */ - 0x33, 0x00, /* 0011001100 */ - 0x33, 0x00, /* 0011001100 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x33, 0x00, /* 0011001100 */ - 0x33, 0x00, /* 0011001100 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 20 0x14 '^T' */ - 0x00, 0x00, /* 0000000000 */ - 0x1f, 0xc0, /* 0001111111 */ - 0x39, 0x80, /* 0011100110 */ - 0x79, 0x80, /* 0111100110 */ - 0x79, 0x80, /* 0111100110 */ - 0x79, 0x80, /* 0111100110 */ - 0x39, 0x80, /* 0011100110 */ - 0x19, 0x80, /* 0001100110 */ - 0x19, 0x80, /* 0001100110 */ - 0x19, 0x80, /* 0001100110 */ - 0x19, 0x80, /* 0001100110 */ - 0x19, 0x80, /* 0001100110 */ - 0x19, 0x80, /* 0001100110 */ - 0x39, 0xc0, /* 0011100111 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 21 0x15 '^U' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x3e, 0x00, /* 0011111000 */ - 0x63, 0x00, /* 0110001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x3e, 0x00, /* 0011111000 */ - 0x63, 0x00, /* 0110001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x3e, 0x00, /* 0011111000 */ - 0x03, 0x00, /* 0000001100 */ - 0x03, 0x00, /* 0000001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x3e, 0x00, /* 0011111000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 22 0x16 '^V' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x7f, 0x80, /* 0111111110 */ - 0x7f, 0x80, /* 0111111110 */ - 0x7f, 0x80, /* 0111111110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 23 0x17 '^W' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x1e, 0x00, /* 0001111000 */ - 0x3f, 0x00, /* 0011111100 */ - 0x7f, 0x80, /* 0111111110 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x7f, 0x80, /* 0111111110 */ - 0x3f, 0x00, /* 0011111100 */ - 0x1e, 0x00, /* 0001111000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x7f, 0x80, /* 0111111110 */ - 0x7f, 0x80, /* 0111111110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 24 0x18 '^X' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x1e, 0x00, /* 0001111000 */ - 0x3f, 0x00, /* 0011111100 */ - 0x7f, 0x80, /* 0111111110 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 25 0x19 '^Y' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x7f, 0x80, /* 0111111110 */ - 0x3f, 0x00, /* 0011111100 */ - 0x1e, 0x00, /* 0001111000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 26 0x1a '^Z' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x04, 0x00, /* 0000010000 */ - 0x06, 0x00, /* 0000011000 */ - 0x07, 0x00, /* 0000011100 */ - 0x7f, 0x80, /* 0111111110 */ - 0x7f, 0x80, /* 0111111110 */ - 0x07, 0x00, /* 0000011100 */ - 0x06, 0x00, /* 0000011000 */ - 0x04, 0x00, /* 0000010000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 27 0x1b '^[' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x08, 0x00, /* 0000100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x38, 0x00, /* 0011100000 */ - 0x7f, 0x80, /* 0111111110 */ - 0x7f, 0x80, /* 0111111110 */ - 0x38, 0x00, /* 0011100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x08, 0x00, /* 0000100000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 28 0x1c '^\' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x7f, 0x80, /* 0111111110 */ - 0x7f, 0x80, /* 0111111110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 29 0x1d '^]' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x12, 0x00, /* 0001001000 */ - 0x33, 0x00, /* 0011001100 */ - 0x73, 0x80, /* 0111001110 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0x73, 0x80, /* 0111001110 */ - 0x33, 0x00, /* 0011001100 */ - 0x12, 0x00, /* 0001001000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 30 0x1e '^^' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x04, 0x00, /* 0000010000 */ - 0x04, 0x00, /* 0000010000 */ - 0x0e, 0x00, /* 0000111000 */ - 0x0e, 0x00, /* 0000111000 */ - 0x1f, 0x00, /* 0001111100 */ - 0x1f, 0x00, /* 0001111100 */ - 0x3f, 0x80, /* 0011111110 */ - 0x3f, 0x80, /* 0011111110 */ - 0x7f, 0xc0, /* 0111111111 */ - 0x7f, 0xc0, /* 0111111111 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 31 0x1f '^_' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x7f, 0xc0, /* 0111111111 */ - 0x7f, 0xc0, /* 0111111111 */ - 0x3f, 0x80, /* 0011111110 */ - 0x3f, 0x80, /* 0011111110 */ - 0x1f, 0x00, /* 0001111100 */ - 0x1f, 0x00, /* 0001111100 */ - 0x0e, 0x00, /* 0000111000 */ - 0x0e, 0x00, /* 0000111000 */ - 0x04, 0x00, /* 0000010000 */ - 0x04, 0x00, /* 0000010000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 32 0x20 ' ' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 33 0x21 '!' */ - 0x00, 0x00, /* 0000000000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 34 0x22 '"' */ - 0x00, 0x00, /* 0000000000 */ - 0x63, 0x00, /* 0110001100 */ - 0xf7, 0x80, /* 1111011110 */ - 0xf7, 0x80, /* 1111011110 */ - 0x31, 0x80, /* 0011000110 */ - 0x31, 0x80, /* 0011000110 */ - 0x63, 0x00, /* 0110001100 */ - 0x42, 0x00, /* 0100001000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 35 0x23 '#' */ - 0x00, 0x00, /* 0000000000 */ - 0x33, 0x00, /* 0011001100 */ - 0x33, 0x00, /* 0011001100 */ - 0x7f, 0x80, /* 0111111110 */ - 0x7f, 0x80, /* 0111111110 */ - 0x33, 0x00, /* 0011001100 */ - 0x33, 0x00, /* 0011001100 */ - 0x33, 0x00, /* 0011001100 */ - 0x33, 0x00, /* 0011001100 */ - 0x7f, 0x80, /* 0111111110 */ - 0x7f, 0x80, /* 0111111110 */ - 0x33, 0x00, /* 0011001100 */ - 0x33, 0x00, /* 0011001100 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 36 0x24 '$' */ - 0x00, 0x00, /* 0000000000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x3e, 0x00, /* 0011111000 */ - 0x3f, 0x00, /* 0011111100 */ - 0x6f, 0x80, /* 0110111110 */ - 0x6d, 0x80, /* 0110110110 */ - 0x6c, 0x80, /* 0110110010 */ - 0x3c, 0x00, /* 0011110000 */ - 0x0f, 0x00, /* 0000111100 */ - 0x0d, 0x80, /* 0000110110 */ - 0x4d, 0x80, /* 0100110110 */ - 0x6d, 0x80, /* 0110110110 */ - 0x7f, 0x00, /* 0111111100 */ - 0x3e, 0x00, /* 0011111000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 37 0x25 '%' */ - 0x00, 0x00, /* 0000000000 */ - 0x31, 0x80, /* 0011000110 */ - 0x7b, 0x00, /* 0111101100 */ - 0x7b, 0x00, /* 0111101100 */ - 0x36, 0x00, /* 0011011000 */ - 0x06, 0x00, /* 0000011000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x18, 0x00, /* 0001100000 */ - 0x1b, 0x00, /* 0001101100 */ - 0x37, 0x80, /* 0011011110 */ - 0x37, 0x80, /* 0011011110 */ - 0x63, 0x00, /* 0110001100 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 38 0x26 '&' */ - 0x00, 0x00, /* 0000000000 */ - 0x07, 0x00, /* 0000011100 */ - 0x0f, 0x80, /* 0000111110 */ - 0x19, 0x80, /* 0001100110 */ - 0x19, 0x80, /* 0001100110 */ - 0x0f, 0x80, /* 0000111110 */ - 0x1e, 0x00, /* 0001111000 */ - 0x3e, 0x00, /* 0011111000 */ - 0x76, 0x00, /* 0111011000 */ - 0x66, 0x40, /* 0110011001 */ - 0x63, 0xc0, /* 0110001111 */ - 0x63, 0x80, /* 0110001110 */ - 0x63, 0x00, /* 0110001100 */ - 0x3f, 0x80, /* 0011111110 */ - 0x1c, 0xc0, /* 0001110011 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 39 0x27 ''' */ - 0x00, 0x00, /* 0000000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x78, 0x00, /* 0111100000 */ - 0x78, 0x00, /* 0111100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x30, 0x00, /* 0011000000 */ - 0x20, 0x00, /* 0010000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 40 0x28 '(' */ - 0x00, 0x00, /* 0000000000 */ - 0x03, 0x00, /* 0000001100 */ - 0x06, 0x00, /* 0000011000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x18, 0x00, /* 0001100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x06, 0x00, /* 0000011000 */ - 0x03, 0x00, /* 0000001100 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 41 0x29 ')' */ - 0x00, 0x00, /* 0000000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x18, 0x00, /* 0001100000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x06, 0x00, /* 0000011000 */ - 0x06, 0x00, /* 0000011000 */ - 0x06, 0x00, /* 0000011000 */ - 0x06, 0x00, /* 0000011000 */ - 0x06, 0x00, /* 0000011000 */ - 0x06, 0x00, /* 0000011000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x18, 0x00, /* 0001100000 */ - 0x30, 0x00, /* 0011000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 42 0x2a '*' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x4c, 0x80, /* 0100110010 */ - 0x6d, 0x80, /* 0110110110 */ - 0x3f, 0x00, /* 0011111100 */ - 0x7f, 0x80, /* 0111111110 */ - 0x3f, 0x00, /* 0011111100 */ - 0x6d, 0x80, /* 0110110110 */ - 0x4c, 0x80, /* 0100110010 */ - 0x0c, 0x00, /* 0000110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 43 0x2b '+' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x7f, 0x80, /* 0111111110 */ - 0x7f, 0x80, /* 0111111110 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 44 0x2c ',' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x78, 0x00, /* 0111100000 */ - 0x78, 0x00, /* 0111100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x30, 0x00, /* 0011000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x40, 0x00, /* 0100000000 */ - - /* 45 0x2d '-' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x7f, 0x80, /* 0111111110 */ - 0x7f, 0x80, /* 0111111110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 46 0x2e '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x18, 0x00, /* 0001100000 */ - 0x3c, 0x00, /* 0011110000 */ - 0x3c, 0x00, /* 0011110000 */ - 0x18, 0x00, /* 0001100000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 47 0x2f '/' */ - 0x00, 0x00, /* 0000000000 */ - 0x01, 0x80, /* 0000000110 */ - 0x01, 0x80, /* 0000000110 */ - 0x03, 0x00, /* 0000001100 */ - 0x03, 0x00, /* 0000001100 */ - 0x06, 0x00, /* 0000011000 */ - 0x06, 0x00, /* 0000011000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x18, 0x00, /* 0001100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 48 0x30 '0' */ - 0x00, 0x00, /* 0000000000 */ - 0x0e, 0x00, /* 0000111000 */ - 0x1f, 0x00, /* 0001111100 */ - 0x23, 0x00, /* 0010001100 */ - 0x61, 0x80, /* 0110000110 */ - 0x63, 0x80, /* 0110001110 */ - 0x65, 0x80, /* 0110010110 */ - 0x65, 0x80, /* 0110010110 */ - 0x69, 0x80, /* 0110100110 */ - 0x69, 0x80, /* 0110100110 */ - 0x71, 0x80, /* 0111000110 */ - 0x61, 0x00, /* 0110000100 */ - 0x31, 0x00, /* 0011000100 */ - 0x3e, 0x00, /* 0011111000 */ - 0x1c, 0x00, /* 0001110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 49 0x31 '1' */ - 0x00, 0x00, /* 0000000000 */ - 0x04, 0x00, /* 0000010000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x1c, 0x00, /* 0001110000 */ - 0x3c, 0x00, /* 0011110000 */ - 0x6c, 0x00, /* 0110110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x7f, 0x80, /* 0111111110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 50 0x32 '2' */ - 0x00, 0x00, /* 0000000000 */ - 0x1e, 0x00, /* 0001111000 */ - 0x3f, 0x00, /* 0011111100 */ - 0x63, 0x80, /* 0110001110 */ - 0x41, 0x80, /* 0100000110 */ - 0x01, 0x80, /* 0000000110 */ - 0x01, 0x80, /* 0000000110 */ - 0x03, 0x00, /* 0000001100 */ - 0x06, 0x00, /* 0000011000 */ - 0x06, 0x00, /* 0000011000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x18, 0x00, /* 0001100000 */ - 0x30, 0x80, /* 0011000010 */ - 0x7f, 0x80, /* 0111111110 */ - 0x7f, 0x80, /* 0111111110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 51 0x33 '3' */ - 0x00, 0x00, /* 0000000000 */ - 0x1c, 0x00, /* 0001110000 */ - 0x3e, 0x00, /* 0011111000 */ - 0x47, 0x00, /* 0100011100 */ - 0x03, 0x00, /* 0000001100 */ - 0x07, 0x00, /* 0000011100 */ - 0x06, 0x00, /* 0000011000 */ - 0x0e, 0x00, /* 0000111000 */ - 0x07, 0x00, /* 0000011100 */ - 0x03, 0x00, /* 0000001100 */ - 0x01, 0x80, /* 0000000110 */ - 0x41, 0x80, /* 0100000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x3f, 0x00, /* 0011111100 */ - 0x1e, 0x00, /* 0001111000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 52 0x34 '4' */ - 0x00, 0x00, /* 0000000000 */ - 0x06, 0x00, /* 0000011000 */ - 0x0e, 0x00, /* 0000111000 */ - 0x1e, 0x00, /* 0001111000 */ - 0x36, 0x00, /* 0011011000 */ - 0x36, 0x00, /* 0011011000 */ - 0x66, 0x00, /* 0110011000 */ - 0x66, 0x00, /* 0110011000 */ - 0xc6, 0x00, /* 1100011000 */ - 0xc6, 0x00, /* 1100011000 */ - 0xff, 0x80, /* 1111111110 */ - 0xff, 0x80, /* 1111111110 */ - 0x06, 0x00, /* 0000011000 */ - 0x06, 0x00, /* 0000011000 */ - 0x06, 0x00, /* 0000011000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 53 0x35 '5' */ - 0x00, 0x00, /* 0000000000 */ - 0x1f, 0x00, /* 0001111100 */ - 0x1f, 0x00, /* 0001111100 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x7e, 0x00, /* 0111111000 */ - 0x67, 0x00, /* 0110011100 */ - 0x03, 0x80, /* 0000001110 */ - 0x01, 0x80, /* 0000000110 */ - 0x01, 0x80, /* 0000000110 */ - 0x01, 0x80, /* 0000000110 */ - 0x41, 0x80, /* 0100000110 */ - 0x63, 0x00, /* 0110001100 */ - 0x3e, 0x00, /* 0011111000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 54 0x36 '6' */ - 0x00, 0x00, /* 0000000000 */ - 0x06, 0x00, /* 0000011000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x18, 0x00, /* 0001100000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x6e, 0x00, /* 0110111000 */ - 0x7f, 0x00, /* 0111111100 */ - 0x73, 0x80, /* 0111001110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x71, 0x00, /* 0111000100 */ - 0x3e, 0x00, /* 0011111000 */ - 0x1c, 0x00, /* 0001110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 55 0x37 '7' */ - 0x00, 0x00, /* 0000000000 */ - 0x1f, 0x80, /* 0001111110 */ - 0x3f, 0x80, /* 0011111110 */ - 0x61, 0x80, /* 0110000110 */ - 0x01, 0x80, /* 0000000110 */ - 0x03, 0x00, /* 0000001100 */ - 0x03, 0x00, /* 0000001100 */ - 0x06, 0x00, /* 0000011000 */ - 0x06, 0x00, /* 0000011000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x18, 0x00, /* 0001100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 56 0x38 '8' */ - 0x00, 0x00, /* 0000000000 */ - 0x1e, 0x00, /* 0001111000 */ - 0x23, 0x00, /* 0010001100 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x31, 0x00, /* 0011000100 */ - 0x1a, 0x00, /* 0001101000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x16, 0x00, /* 0001011000 */ - 0x23, 0x00, /* 0010001100 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x31, 0x00, /* 0011000100 */ - 0x1e, 0x00, /* 0001111000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 57 0x39 '9' */ - 0x00, 0x00, /* 0000000000 */ - 0x0e, 0x00, /* 0000111000 */ - 0x17, 0x00, /* 0001011100 */ - 0x23, 0x80, /* 0010001110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x73, 0x80, /* 0111001110 */ - 0x3d, 0x80, /* 0011110110 */ - 0x19, 0x80, /* 0001100110 */ - 0x01, 0x80, /* 0000000110 */ - 0x03, 0x00, /* 0000001100 */ - 0x03, 0x00, /* 0000001100 */ - 0x06, 0x00, /* 0000011000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x18, 0x00, /* 0001100000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 58 0x3a ':' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x18, 0x00, /* 0001100000 */ - 0x3c, 0x00, /* 0011110000 */ - 0x3c, 0x00, /* 0011110000 */ - 0x18, 0x00, /* 0001100000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x18, 0x00, /* 0001100000 */ - 0x3c, 0x00, /* 0011110000 */ - 0x3c, 0x00, /* 0011110000 */ - 0x18, 0x00, /* 0001100000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 59 0x3b ';' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x18, 0x00, /* 0001100000 */ - 0x3c, 0x00, /* 0011110000 */ - 0x3c, 0x00, /* 0011110000 */ - 0x18, 0x00, /* 0001100000 */ - 0x00, 0x00, /* 0000000000 */ - 0x18, 0x00, /* 0001100000 */ - 0x3c, 0x00, /* 0011110000 */ - 0x3c, 0x00, /* 0011110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x18, 0x00, /* 0001100000 */ - 0x30, 0x00, /* 0011000000 */ - 0x20, 0x00, /* 0010000000 */ - - /* 60 0x3c '<' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x03, 0x00, /* 0000001100 */ - 0x06, 0x00, /* 0000011000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x18, 0x00, /* 0001100000 */ - 0x30, 0x00, /* 0011000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x18, 0x00, /* 0001100000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x06, 0x00, /* 0000011000 */ - 0x03, 0x00, /* 0000001100 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 61 0x3d '=' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x7f, 0x80, /* 0111111110 */ - 0x7f, 0x80, /* 0111111110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x7f, 0x80, /* 0111111110 */ - 0x7f, 0x80, /* 0111111110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 62 0x3e '>' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x18, 0x00, /* 0001100000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x06, 0x00, /* 0000011000 */ - 0x03, 0x00, /* 0000001100 */ - 0x01, 0x80, /* 0000000110 */ - 0x01, 0x80, /* 0000000110 */ - 0x03, 0x00, /* 0000001100 */ - 0x06, 0x00, /* 0000011000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x18, 0x00, /* 0001100000 */ - 0x30, 0x00, /* 0011000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 63 0x3f '?' */ - 0x00, 0x00, /* 0000000000 */ - 0x0e, 0x00, /* 0000111000 */ - 0x1f, 0x00, /* 0001111100 */ - 0x3b, 0x80, /* 0011101110 */ - 0x21, 0x80, /* 0010000110 */ - 0x01, 0x80, /* 0000000110 */ - 0x03, 0x00, /* 0000001100 */ - 0x06, 0x00, /* 0000011000 */ - 0x06, 0x00, /* 0000011000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 64 0x40 '@' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x0e, 0x00, /* 0000111000 */ - 0x3f, 0x00, /* 0011111100 */ - 0x31, 0x80, /* 0011000110 */ - 0x65, 0x80, /* 0110010110 */ - 0x6d, 0x80, /* 0110110110 */ - 0x6d, 0x80, /* 0110110110 */ - 0x6d, 0x80, /* 0110110110 */ - 0x6d, 0x80, /* 0110110110 */ - 0x6f, 0x80, /* 0110111110 */ - 0x60, 0x00, /* 0110000000 */ - 0x31, 0x80, /* 0011000110 */ - 0x3f, 0x80, /* 0011111110 */ - 0x0f, 0x00, /* 0000111100 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 65 0x41 'A' */ - 0x00, 0x00, /* 0000000000 */ - 0x04, 0x00, /* 0000010000 */ - 0x04, 0x00, /* 0000010000 */ - 0x0e, 0x00, /* 0000111000 */ - 0x0e, 0x00, /* 0000111000 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x19, 0x80, /* 0001100110 */ - 0x31, 0x80, /* 0011000110 */ - 0x3f, 0x80, /* 0011111110 */ - 0x31, 0x80, /* 0011000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x60, 0xc0, /* 0110000011 */ - 0x60, 0xc0, /* 0110000011 */ - 0xf1, 0xc0, /* 1111000111 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 66 0x42 'B' */ - 0x00, 0x00, /* 0000000000 */ - 0xfc, 0x00, /* 1111110000 */ - 0x62, 0x00, /* 0110001000 */ - 0x63, 0x00, /* 0110001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x66, 0x00, /* 0110011000 */ - 0x7e, 0x00, /* 0111111000 */ - 0x63, 0x00, /* 0110001100 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x63, 0x00, /* 0110001100 */ - 0xfe, 0x00, /* 1111111000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 67 0x43 'C' */ - 0x00, 0x00, /* 0000000000 */ - 0x0f, 0x00, /* 0000111100 */ - 0x11, 0x80, /* 0001000110 */ - 0x20, 0x80, /* 0010000010 */ - 0x20, 0x00, /* 0010000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x20, 0x00, /* 0010000000 */ - 0x30, 0x80, /* 0011000010 */ - 0x19, 0x00, /* 0001100100 */ - 0x0e, 0x00, /* 0000111000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 68 0x44 'D' */ - 0x00, 0x00, /* 0000000000 */ - 0xfc, 0x00, /* 1111110000 */ - 0x67, 0x00, /* 0110011100 */ - 0x63, 0x00, /* 0110001100 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x00, /* 0110000100 */ - 0x66, 0x00, /* 0110011000 */ - 0xf8, 0x00, /* 1111100000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 69 0x45 'E' */ - 0x00, 0x00, /* 0000000000 */ - 0x7f, 0x80, /* 0111111110 */ - 0x30, 0x80, /* 0011000010 */ - 0x30, 0x80, /* 0011000010 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x31, 0x00, /* 0011000100 */ - 0x3f, 0x00, /* 0011111100 */ - 0x31, 0x00, /* 0011000100 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x80, /* 0011000010 */ - 0x30, 0x80, /* 0011000010 */ - 0x7f, 0x80, /* 0111111110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 70 0x46 'F' */ - 0x00, 0x00, /* 0000000000 */ - 0x7f, 0x80, /* 0111111110 */ - 0x30, 0x80, /* 0011000010 */ - 0x30, 0x80, /* 0011000010 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x31, 0x00, /* 0011000100 */ - 0x3f, 0x00, /* 0011111100 */ - 0x31, 0x00, /* 0011000100 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x78, 0x00, /* 0111100000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 71 0x47 'G' */ - 0x00, 0x00, /* 0000000000 */ - 0x0f, 0x00, /* 0000111100 */ - 0x11, 0x80, /* 0001000110 */ - 0x20, 0x80, /* 0010000010 */ - 0x20, 0x00, /* 0010000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x67, 0xc0, /* 0110011111 */ - 0x61, 0x80, /* 0110000110 */ - 0x21, 0x80, /* 0010000110 */ - 0x31, 0x80, /* 0011000110 */ - 0x19, 0x80, /* 0001100110 */ - 0x0e, 0x00, /* 0000111000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 72 0x48 'H' */ - 0x00, 0x00, /* 0000000000 */ - 0xf3, 0xc0, /* 1111001111 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x7f, 0x80, /* 0111111110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0xf3, 0xc0, /* 1111001111 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 73 0x49 'I' */ - 0x00, 0x00, /* 0000000000 */ - 0x3f, 0x00, /* 0011111100 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x3f, 0x00, /* 0011111100 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 74 0x4a 'J' */ - 0x00, 0x00, /* 0000000000 */ - 0x3f, 0x00, /* 0011111100 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x08, 0x00, /* 0000100000 */ - 0x70, 0x00, /* 0111000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 75 0x4b 'K' */ - 0x00, 0x00, /* 0000000000 */ - 0xf1, 0x80, /* 1111000110 */ - 0x63, 0x00, /* 0110001100 */ - 0x66, 0x00, /* 0110011000 */ - 0x6c, 0x00, /* 0110110000 */ - 0x78, 0x00, /* 0111100000 */ - 0x70, 0x00, /* 0111000000 */ - 0x70, 0x00, /* 0111000000 */ - 0x78, 0x00, /* 0111100000 */ - 0x78, 0x00, /* 0111100000 */ - 0x6c, 0x00, /* 0110110000 */ - 0x66, 0x00, /* 0110011000 */ - 0x63, 0x00, /* 0110001100 */ - 0x61, 0x80, /* 0110000110 */ - 0xf0, 0xc0, /* 1111000011 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 76 0x4c 'L' */ - 0x00, 0x00, /* 0000000000 */ - 0x78, 0x00, /* 0111100000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x80, /* 0011000010 */ - 0x30, 0x80, /* 0011000010 */ - 0x7f, 0x80, /* 0111111110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 77 0x4d 'M' */ - 0x00, 0x00, /* 0000000000 */ - 0xe0, 0xc0, /* 1110000011 */ - 0x61, 0x80, /* 0110000110 */ - 0x73, 0x80, /* 0111001110 */ - 0x73, 0x80, /* 0111001110 */ - 0x6d, 0x80, /* 0110110110 */ - 0x6d, 0x80, /* 0110110110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0xf3, 0xc0, /* 1111001111 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 78 0x4e 'N' */ - 0x00, 0x00, /* 0000000000 */ - 0xf3, 0xc0, /* 1111001111 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x71, 0x80, /* 0111000110 */ - 0x79, 0x80, /* 0111100110 */ - 0x79, 0x80, /* 0111100110 */ - 0x6d, 0x80, /* 0110110110 */ - 0x6d, 0x80, /* 0110110110 */ - 0x67, 0x80, /* 0110011110 */ - 0x67, 0x80, /* 0110011110 */ - 0x63, 0x80, /* 0110001110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0xf3, 0xc0, /* 1111001111 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 79 0x4f 'O' */ - 0x00, 0x00, /* 0000000000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x17, 0x00, /* 0001011100 */ - 0x23, 0x00, /* 0010001100 */ - 0x21, 0x80, /* 0010000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x21, 0x00, /* 0010000100 */ - 0x31, 0x00, /* 0011000100 */ - 0x1a, 0x00, /* 0001101000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 80 0x50 'P' */ - 0x00, 0x00, /* 0000000000 */ - 0xfe, 0x00, /* 1111111000 */ - 0x63, 0x00, /* 0110001100 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x63, 0x00, /* 0110001100 */ - 0x7e, 0x00, /* 0111111000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0xf0, 0x00, /* 1111000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 81 0x51 'Q' */ - 0x00, 0x00, /* 0000000000 */ - 0x0e, 0x00, /* 0000111000 */ - 0x13, 0x00, /* 0001001100 */ - 0x23, 0x00, /* 0010001100 */ - 0x21, 0x80, /* 0010000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x31, 0x80, /* 0011000110 */ - 0x3b, 0x00, /* 0011101100 */ - 0x1e, 0x00, /* 0001111000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x1e, 0x00, /* 0001111000 */ - 0x26, 0x00, /* 0010011000 */ - 0x03, 0x80, /* 0000001110 */ - 0x00, 0x00, /* 0000000000 */ - - /* 82 0x52 'R' */ - 0x00, 0x00, /* 0000000000 */ - 0xfe, 0x00, /* 1111111000 */ - 0x63, 0x00, /* 0110001100 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x00, /* 0110000100 */ - 0x7e, 0x00, /* 0111111000 */ - 0x78, 0x00, /* 0111100000 */ - 0x6c, 0x00, /* 0110110000 */ - 0x6e, 0x00, /* 0110111000 */ - 0x67, 0x00, /* 0110011100 */ - 0x63, 0x80, /* 0110001110 */ - 0x61, 0xc0, /* 0110000111 */ - 0xf0, 0xc0, /* 1111000011 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 83 0x53 'S' */ - 0x00, 0x00, /* 0000000000 */ - 0x1f, 0x00, /* 0001111100 */ - 0x31, 0x80, /* 0011000110 */ - 0x60, 0x80, /* 0110000010 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x18, 0x00, /* 0001100000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x06, 0x00, /* 0000011000 */ - 0x03, 0x00, /* 0000001100 */ - 0x01, 0x80, /* 0000000110 */ - 0x41, 0x80, /* 0100000110 */ - 0x63, 0x00, /* 0110001100 */ - 0x3e, 0x00, /* 0011111000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 84 0x54 'T' */ - 0x00, 0x00, /* 0000000000 */ - 0x7f, 0x80, /* 0111111110 */ - 0x4c, 0x80, /* 0100110010 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x3f, 0x00, /* 0011111100 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 85 0x55 'U' */ - 0x00, 0x00, /* 0000000000 */ - 0xf3, 0xc0, /* 1111001111 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x73, 0x00, /* 0111001100 */ - 0x3f, 0x00, /* 0011111100 */ - 0x1e, 0x00, /* 0001111000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 86 0x56 'V' */ - 0x00, 0x00, /* 0000000000 */ - 0xe1, 0xc0, /* 1110000111 */ - 0xc0, 0xc0, /* 1100000011 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x33, 0x00, /* 0011001100 */ - 0x33, 0x00, /* 0011001100 */ - 0x33, 0x00, /* 0011001100 */ - 0x12, 0x00, /* 0001001000 */ - 0x1e, 0x00, /* 0001111000 */ - 0x1e, 0x00, /* 0001111000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 87 0x57 'W' */ - 0x00, 0x00, /* 0000000000 */ - 0xe1, 0xc0, /* 1110000111 */ - 0xc0, 0xc0, /* 1100000011 */ - 0xc0, 0xc0, /* 1100000011 */ - 0xc0, 0xc0, /* 1100000011 */ - 0xe0, 0xc0, /* 1110000011 */ - 0x61, 0x80, /* 0110000110 */ - 0x6d, 0x80, /* 0110110110 */ - 0x6d, 0x80, /* 0110110110 */ - 0x7f, 0x80, /* 0111111110 */ - 0x77, 0x00, /* 0111011100 */ - 0x33, 0x00, /* 0011001100 */ - 0x33, 0x00, /* 0011001100 */ - 0x33, 0x00, /* 0011001100 */ - 0x33, 0x00, /* 0011001100 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 88 0x58 'X' */ - 0x00, 0x00, /* 0000000000 */ - 0xf7, 0x80, /* 1111011110 */ - 0x63, 0x00, /* 0110001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x36, 0x00, /* 0011011000 */ - 0x36, 0x00, /* 0011011000 */ - 0x36, 0x00, /* 0011011000 */ - 0x1c, 0x00, /* 0001110000 */ - 0x1c, 0x00, /* 0001110000 */ - 0x36, 0x00, /* 0011011000 */ - 0x36, 0x00, /* 0011011000 */ - 0x36, 0x00, /* 0011011000 */ - 0x63, 0x00, /* 0110001100 */ - 0x63, 0x00, /* 0110001100 */ - 0xf7, 0x80, /* 1111011110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 89 0x59 'Y' */ - 0x00, 0x00, /* 0000000000 */ - 0xf3, 0xc0, /* 1111001111 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x33, 0x00, /* 0011001100 */ - 0x33, 0x00, /* 0011001100 */ - 0x1e, 0x00, /* 0001111000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x1e, 0x00, /* 0001111000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 90 0x5a 'Z' */ - 0x00, 0x00, /* 0000000000 */ - 0x3f, 0x80, /* 0011111110 */ - 0x21, 0x80, /* 0010000110 */ - 0x01, 0x80, /* 0000000110 */ - 0x03, 0x00, /* 0000001100 */ - 0x03, 0x00, /* 0000001100 */ - 0x06, 0x00, /* 0000011000 */ - 0x06, 0x00, /* 0000011000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x18, 0x00, /* 0001100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x80, /* 0011000010 */ - 0x3f, 0x80, /* 0011111110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 91 0x5b '[' */ - 0x00, 0x00, /* 0000000000 */ - 0x1f, 0x00, /* 0001111100 */ - 0x1f, 0x00, /* 0001111100 */ - 0x18, 0x00, /* 0001100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x1f, 0x00, /* 0001111100 */ - 0x1f, 0x00, /* 0001111100 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 92 0x5c '\' */ - 0x00, 0x00, /* 0000000000 */ - 0xc0, 0x00, /* 1100000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x18, 0x00, /* 0001100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x06, 0x00, /* 0000011000 */ - 0x06, 0x00, /* 0000011000 */ - 0x03, 0x00, /* 0000001100 */ - 0x03, 0x00, /* 0000001100 */ - 0x01, 0x80, /* 0000000110 */ - 0x01, 0x80, /* 0000000110 */ - 0x00, 0xc0, /* 0000000011 */ - 0x00, 0x00, /* 0000000000 */ - - /* 93 0x5d ']' */ - 0x00, 0x00, /* 0000000000 */ - 0x3e, 0x00, /* 0011111000 */ - 0x3e, 0x00, /* 0011111000 */ - 0x06, 0x00, /* 0000011000 */ - 0x06, 0x00, /* 0000011000 */ - 0x06, 0x00, /* 0000011000 */ - 0x06, 0x00, /* 0000011000 */ - 0x06, 0x00, /* 0000011000 */ - 0x06, 0x00, /* 0000011000 */ - 0x06, 0x00, /* 0000011000 */ - 0x06, 0x00, /* 0000011000 */ - 0x06, 0x00, /* 0000011000 */ - 0x06, 0x00, /* 0000011000 */ - 0x06, 0x00, /* 0000011000 */ - 0x3e, 0x00, /* 0011111000 */ - 0x3e, 0x00, /* 0011111000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 94 0x5e '^' */ - 0x00, 0x00, /* 0000000000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x1e, 0x00, /* 0001111000 */ - 0x33, 0x00, /* 0011001100 */ - 0x61, 0x80, /* 0110000110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 95 0x5f '_' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0x00, 0x00, /* 0000000000 */ - - /* 96 0x60 '`' */ - 0x04, 0x00, /* 0000010000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x18, 0x00, /* 0001100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x1e, 0x00, /* 0001111000 */ - 0x1e, 0x00, /* 0001111000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 97 0x61 'a' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x1f, 0x00, /* 0001111100 */ - 0x31, 0x80, /* 0011000110 */ - 0x21, 0x80, /* 0010000110 */ - 0x07, 0x80, /* 0000011110 */ - 0x39, 0x80, /* 0011100110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x73, 0x80, /* 0111001110 */ - 0x3d, 0xc0, /* 0011110111 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 98 0x62 'b' */ - 0x20, 0x00, /* 0010000000 */ - 0x60, 0x00, /* 0110000000 */ - 0xe0, 0x00, /* 1110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x66, 0x00, /* 0110011000 */ - 0x6f, 0x00, /* 0110111100 */ - 0x73, 0x80, /* 0111001110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x71, 0x80, /* 0111000110 */ - 0x7b, 0x00, /* 0111101100 */ - 0x4e, 0x00, /* 0100111000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 99 0x63 'c' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x1e, 0x00, /* 0001111000 */ - 0x37, 0x00, /* 0011011100 */ - 0x23, 0x00, /* 0010001100 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x71, 0x00, /* 0111000100 */ - 0x33, 0x00, /* 0011001100 */ - 0x1e, 0x00, /* 0001111000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 100 0x64 'd' */ - 0x01, 0x80, /* 0000000110 */ - 0x03, 0x80, /* 0000001110 */ - 0x01, 0x80, /* 0000000110 */ - 0x01, 0x80, /* 0000000110 */ - 0x01, 0x80, /* 0000000110 */ - 0x0d, 0x80, /* 0000110110 */ - 0x37, 0x80, /* 0011011110 */ - 0x23, 0x80, /* 0010001110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x73, 0x80, /* 0111001110 */ - 0x35, 0x80, /* 0011010110 */ - 0x19, 0xc0, /* 0001100111 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 101 0x65 'e' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x33, 0x00, /* 0011001100 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x7f, 0x80, /* 0111111110 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x19, 0x80, /* 0001100110 */ - 0x0e, 0x00, /* 0000111000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 102 0x66 'f' */ - 0x07, 0x00, /* 0000011100 */ - 0x09, 0x80, /* 0000100110 */ - 0x09, 0x80, /* 0000100110 */ - 0x18, 0x00, /* 0001100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x7f, 0x00, /* 0111111100 */ - 0x18, 0x00, /* 0001100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x3c, 0x00, /* 0011110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 103 0x67 'g' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x1c, 0x80, /* 0001110010 */ - 0x37, 0x80, /* 0011011110 */ - 0x63, 0x00, /* 0110001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x36, 0x00, /* 0011011000 */ - 0x3c, 0x00, /* 0011110000 */ - 0x60, 0x00, /* 0110000000 */ - 0x7f, 0x00, /* 0111111100 */ - 0x3f, 0x80, /* 0011111110 */ - 0x21, 0x80, /* 0010000110 */ - 0x40, 0x80, /* 0100000010 */ - 0x7f, 0x00, /* 0111111100 */ - 0x3e, 0x00, /* 0011111000 */ - - /* 104 0x68 'h' */ - 0x10, 0x00, /* 0001000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x70, 0x00, /* 0111000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x37, 0x00, /* 0011011100 */ - 0x3b, 0x80, /* 0011101110 */ - 0x31, 0x80, /* 0011000110 */ - 0x31, 0x80, /* 0011000110 */ - 0x31, 0x80, /* 0011000110 */ - 0x31, 0x80, /* 0011000110 */ - 0x31, 0x80, /* 0011000110 */ - 0x31, 0x80, /* 0011000110 */ - 0x31, 0x80, /* 0011000110 */ - 0x7b, 0xc0, /* 0111101111 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 105 0x69 'i' */ - 0x00, 0x00, /* 0000000000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x3c, 0x00, /* 0011110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x3f, 0x00, /* 0011111100 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 106 0x6a 'j' */ - 0x00, 0x00, /* 0000000000 */ - 0x01, 0x80, /* 0000000110 */ - 0x01, 0x80, /* 0000000110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x07, 0x80, /* 0000011110 */ - 0x01, 0x80, /* 0000000110 */ - 0x01, 0x80, /* 0000000110 */ - 0x01, 0x80, /* 0000000110 */ - 0x01, 0x80, /* 0000000110 */ - 0x01, 0x80, /* 0000000110 */ - 0x01, 0x80, /* 0000000110 */ - 0x01, 0x80, /* 0000000110 */ - 0x41, 0x80, /* 0100000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x71, 0x80, /* 0111000110 */ - 0x3f, 0x00, /* 0011111100 */ - 0x1c, 0x00, /* 0001110000 */ - - /* 107 0x6b 'k' */ - 0x60, 0x00, /* 0110000000 */ - 0xe0, 0x00, /* 1110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x63, 0x80, /* 0110001110 */ - 0x66, 0x00, /* 0110011000 */ - 0x6c, 0x00, /* 0110110000 */ - 0x78, 0x00, /* 0111100000 */ - 0x70, 0x00, /* 0111000000 */ - 0x78, 0x00, /* 0111100000 */ - 0x6c, 0x00, /* 0110110000 */ - 0x6e, 0x00, /* 0110111000 */ - 0x67, 0x00, /* 0110011100 */ - 0xf3, 0x80, /* 1111001110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 108 0x6c 'l' */ - 0x3c, 0x00, /* 0011110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x3f, 0x00, /* 0011111100 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 109 0x6d 'm' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0xdb, 0x80, /* 1101101110 */ - 0x6d, 0x80, /* 0110110110 */ - 0x6d, 0x80, /* 0110110110 */ - 0x6d, 0x80, /* 0110110110 */ - 0x6d, 0x80, /* 0110110110 */ - 0x6d, 0x80, /* 0110110110 */ - 0x6d, 0x80, /* 0110110110 */ - 0x6d, 0x80, /* 0110110110 */ - 0x6d, 0x80, /* 0110110110 */ - 0xed, 0xc0, /* 1110110111 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 110 0x6e 'n' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x6f, 0x00, /* 0110111100 */ - 0x7b, 0x80, /* 0111101110 */ - 0x31, 0x80, /* 0011000110 */ - 0x31, 0x80, /* 0011000110 */ - 0x31, 0x80, /* 0011000110 */ - 0x31, 0x80, /* 0011000110 */ - 0x31, 0x80, /* 0011000110 */ - 0x31, 0x80, /* 0011000110 */ - 0x31, 0x80, /* 0011000110 */ - 0x7b, 0xc0, /* 0111101111 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 111 0x6f 'o' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x3c, 0x00, /* 0011110000 */ - 0x66, 0x00, /* 0110011000 */ - 0xc3, 0x00, /* 1100001100 */ - 0xc1, 0x80, /* 1100000110 */ - 0xc1, 0x80, /* 1100000110 */ - 0xc1, 0x80, /* 1100000110 */ - 0xc1, 0x80, /* 1100000110 */ - 0xe1, 0x80, /* 1110000110 */ - 0x73, 0x00, /* 0111001100 */ - 0x3c, 0x00, /* 0011110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 112 0x70 'p' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0xde, 0x00, /* 1101111000 */ - 0x76, 0x00, /* 0111011000 */ - 0x63, 0x00, /* 0110001100 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x71, 0x80, /* 0111000110 */ - 0x7b, 0x00, /* 0111101100 */ - 0x7e, 0x00, /* 0111111000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0xf0, 0x00, /* 1111000000 */ - - /* 113 0x71 'q' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x0e, 0xc0, /* 0000111011 */ - 0x1b, 0x80, /* 0001101110 */ - 0x33, 0x80, /* 0011001110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x71, 0x80, /* 0111000110 */ - 0x3b, 0x80, /* 0011101110 */ - 0x1f, 0x80, /* 0001111110 */ - 0x01, 0x80, /* 0000000110 */ - 0x01, 0x80, /* 0000000110 */ - 0x03, 0xc0, /* 0000001111 */ - - /* 114 0x72 'r' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x73, 0x00, /* 0111001100 */ - 0x35, 0x80, /* 0011010110 */ - 0x39, 0x80, /* 0011100110 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x78, 0x00, /* 0111100000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 115 0x73 's' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x3f, 0x00, /* 0011111100 */ - 0x63, 0x00, /* 0110001100 */ - 0x61, 0x00, /* 0110000100 */ - 0x70, 0x00, /* 0111000000 */ - 0x38, 0x00, /* 0011100000 */ - 0x0e, 0x00, /* 0000111000 */ - 0x07, 0x00, /* 0000011100 */ - 0x43, 0x00, /* 0100001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x7e, 0x00, /* 0111111000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 116 0x74 't' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x08, 0x00, /* 0000100000 */ - 0x08, 0x00, /* 0000100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x7f, 0x80, /* 0111111110 */ - 0x18, 0x00, /* 0001100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x1c, 0x80, /* 0001110010 */ - 0x0f, 0x00, /* 0000111100 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 117 0x75 'u' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0xf7, 0x80, /* 1111011110 */ - 0x63, 0x00, /* 0110001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x77, 0x00, /* 0111011100 */ - 0x3d, 0x80, /* 0011110110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 118 0x76 'v' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0xf1, 0xc0, /* 1111000111 */ - 0x60, 0xc0, /* 0110000011 */ - 0x31, 0x80, /* 0011000110 */ - 0x31, 0x80, /* 0011000110 */ - 0x19, 0x80, /* 0001100110 */ - 0x1b, 0x00, /* 0001101100 */ - 0x0f, 0x00, /* 0000111100 */ - 0x0f, 0x00, /* 0000111100 */ - 0x06, 0x00, /* 0000011000 */ - 0x06, 0x00, /* 0000011000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 119 0x77 'w' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0xe3, 0xc0, /* 1110001111 */ - 0xc1, 0x80, /* 1100000110 */ - 0xc1, 0x80, /* 1100000110 */ - 0xc1, 0x80, /* 1100000110 */ - 0xc1, 0x80, /* 1100000110 */ - 0x6b, 0x00, /* 0110101100 */ - 0x6b, 0x00, /* 0110101100 */ - 0x7e, 0x00, /* 0111111000 */ - 0x36, 0x00, /* 0011011000 */ - 0x36, 0x00, /* 0011011000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 120 0x78 'x' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0xf7, 0x80, /* 1111011110 */ - 0x63, 0x00, /* 0110001100 */ - 0x36, 0x00, /* 0011011000 */ - 0x36, 0x00, /* 0011011000 */ - 0x1c, 0x00, /* 0001110000 */ - 0x1c, 0x00, /* 0001110000 */ - 0x36, 0x00, /* 0011011000 */ - 0x66, 0x00, /* 0110011000 */ - 0x63, 0x00, /* 0110001100 */ - 0xf7, 0x80, /* 1111011110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 121 0x79 'y' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0xf3, 0xc0, /* 1111001111 */ - 0x61, 0x80, /* 0110000110 */ - 0x33, 0x00, /* 0011001100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1e, 0x00, /* 0001111000 */ - 0x0e, 0x00, /* 0000111000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x18, 0x00, /* 0001100000 */ - 0x78, 0x00, /* 0111100000 */ - 0x70, 0x00, /* 0111000000 */ - - /* 122 0x7a 'z' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x7f, 0x80, /* 0111111110 */ - 0x61, 0x80, /* 0110000110 */ - 0x43, 0x00, /* 0100001100 */ - 0x06, 0x00, /* 0000011000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x18, 0x00, /* 0001100000 */ - 0x30, 0x00, /* 0011000000 */ - 0x60, 0x80, /* 0110000010 */ - 0x61, 0x80, /* 0110000110 */ - 0x7f, 0x80, /* 0111111110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 123 0x7b '{' */ - 0x07, 0x00, /* 0000011100 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x18, 0x00, /* 0001100000 */ - 0x70, 0x00, /* 0111000000 */ - 0x18, 0x00, /* 0001100000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x07, 0x00, /* 0000011100 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 124 0x7c '|' */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 125 0x7d '}' */ - 0x38, 0x00, /* 0011100000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x06, 0x00, /* 0000011000 */ - 0x03, 0x80, /* 0000001110 */ - 0x06, 0x00, /* 0000011000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x38, 0x00, /* 0011100000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 126 0x7e '~' */ - 0x00, 0x00, /* 0000000000 */ - 0x18, 0x80, /* 0001100010 */ - 0x3d, 0x80, /* 0011110110 */ - 0x6f, 0x00, /* 0110111100 */ - 0x46, 0x00, /* 0100011000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 127 0x7f '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x12, 0x00, /* 0001001000 */ - 0x21, 0x00, /* 0010000100 */ - 0x40, 0x80, /* 0100000010 */ - 0x40, 0x80, /* 0100000010 */ - 0x40, 0x80, /* 0100000010 */ - 0x7f, 0x80, /* 0111111110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 128 0x80 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x1f, 0x00, /* 0001111100 */ - 0x21, 0x80, /* 0010000110 */ - 0x40, 0x80, /* 0100000010 */ - 0x40, 0x00, /* 0100000000 */ - 0x40, 0x00, /* 0100000000 */ - 0x40, 0x00, /* 0100000000 */ - 0x40, 0x00, /* 0100000000 */ - 0x40, 0x00, /* 0100000000 */ - 0x40, 0x00, /* 0100000000 */ - 0x60, 0x80, /* 0110000010 */ - 0x31, 0x00, /* 0011000100 */ - 0x1e, 0x00, /* 0001111000 */ - 0x08, 0x00, /* 0000100000 */ - 0x04, 0x00, /* 0000010000 */ - 0x02, 0x00, /* 0000001000 */ - 0x02, 0x00, /* 0000001000 */ - 0x1c, 0x00, /* 0001110000 */ - - /* 129 0x81 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x7b, 0x80, /* 0111101110 */ - 0x31, 0x00, /* 0011000100 */ - 0x31, 0x00, /* 0011000100 */ - 0x31, 0x00, /* 0011000100 */ - 0x31, 0x00, /* 0011000100 */ - 0x31, 0x00, /* 0011000100 */ - 0x31, 0x00, /* 0011000100 */ - 0x31, 0x00, /* 0011000100 */ - 0x3b, 0x00, /* 0011101100 */ - 0x1c, 0x80, /* 0001110010 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 130 0x82 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x01, 0x00, /* 0000000100 */ - 0x02, 0x00, /* 0000001000 */ - 0x04, 0x00, /* 0000010000 */ - 0x00, 0x00, /* 0000000000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x33, 0x00, /* 0011001100 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x7f, 0x80, /* 0111111110 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x19, 0x80, /* 0001100110 */ - 0x0e, 0x00, /* 0000111000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 131 0x83 '.' */ - 0x04, 0x00, /* 0000010000 */ - 0x0e, 0x00, /* 0000111000 */ - 0x1b, 0x00, /* 0001101100 */ - 0x31, 0x80, /* 0011000110 */ - 0x00, 0x00, /* 0000000000 */ - 0x1f, 0x00, /* 0001111100 */ - 0x31, 0x80, /* 0011000110 */ - 0x21, 0x80, /* 0010000110 */ - 0x07, 0x80, /* 0000011110 */ - 0x39, 0x80, /* 0011100110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x73, 0x80, /* 0111001110 */ - 0x3d, 0xc0, /* 0011110111 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 132 0x84 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x1f, 0x00, /* 0001111100 */ - 0x31, 0x80, /* 0011000110 */ - 0x21, 0x80, /* 0010000110 */ - 0x07, 0x80, /* 0000011110 */ - 0x39, 0x80, /* 0011100110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x73, 0x80, /* 0111001110 */ - 0x3d, 0xc0, /* 0011110111 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 133 0x85 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x18, 0x00, /* 0001100000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x06, 0x00, /* 0000011000 */ - 0x00, 0x00, /* 0000000000 */ - 0x1f, 0x00, /* 0001111100 */ - 0x31, 0x80, /* 0011000110 */ - 0x21, 0x80, /* 0010000110 */ - 0x07, 0x80, /* 0000011110 */ - 0x39, 0x80, /* 0011100110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x73, 0x80, /* 0111001110 */ - 0x3d, 0xc0, /* 0011110111 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 134 0x86 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x0e, 0x00, /* 0000111000 */ - 0x1b, 0x00, /* 0001101100 */ - 0x0e, 0x00, /* 0000111000 */ - 0x00, 0x00, /* 0000000000 */ - 0x1f, 0x00, /* 0001111100 */ - 0x31, 0x80, /* 0011000110 */ - 0x21, 0x80, /* 0010000110 */ - 0x07, 0x80, /* 0000011110 */ - 0x39, 0x80, /* 0011100110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x73, 0x80, /* 0111001110 */ - 0x3d, 0xc0, /* 0011110111 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 135 0x87 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x1f, 0x00, /* 0001111100 */ - 0x31, 0x80, /* 0011000110 */ - 0x20, 0x80, /* 0010000010 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x70, 0x80, /* 0111000010 */ - 0x30, 0x80, /* 0011000010 */ - 0x1f, 0x00, /* 0001111100 */ - 0x04, 0x00, /* 0000010000 */ - 0x02, 0x00, /* 0000001000 */ - 0x01, 0x00, /* 0000000100 */ - 0x0e, 0x00, /* 0000111000 */ - - /* 136 0x88 '.' */ - 0x04, 0x00, /* 0000010000 */ - 0x0e, 0x00, /* 0000111000 */ - 0x1b, 0x00, /* 0001101100 */ - 0x31, 0x80, /* 0011000110 */ - 0x00, 0x00, /* 0000000000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x33, 0x00, /* 0011001100 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x7f, 0x80, /* 0111111110 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x19, 0x80, /* 0001100110 */ - 0x0e, 0x00, /* 0000111000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 137 0x89 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x36, 0x00, /* 0011011000 */ - 0x36, 0x00, /* 0011011000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x33, 0x00, /* 0011001100 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x7f, 0x80, /* 0111111110 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x19, 0x80, /* 0001100110 */ - 0x0e, 0x00, /* 0000111000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 138 0x8a '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x18, 0x00, /* 0001100000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x33, 0x00, /* 0011001100 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x7f, 0x80, /* 0111111110 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x19, 0x80, /* 0001100110 */ - 0x0e, 0x00, /* 0000111000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 139 0x8b '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x36, 0x00, /* 0011011000 */ - 0x36, 0x00, /* 0011011000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x3c, 0x00, /* 0011110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x3f, 0x00, /* 0011111100 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 140 0x8c '.' */ - 0x08, 0x00, /* 0000100000 */ - 0x1c, 0x00, /* 0001110000 */ - 0x36, 0x00, /* 0011011000 */ - 0x63, 0x00, /* 0110001100 */ - 0x00, 0x00, /* 0000000000 */ - 0x3c, 0x00, /* 0011110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x3f, 0x00, /* 0011111100 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 141 0x8d '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x18, 0x00, /* 0001100000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x3c, 0x00, /* 0011110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x3f, 0x00, /* 0011111100 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 142 0x8e '.' */ - 0x31, 0x80, /* 0011000110 */ - 0x31, 0x80, /* 0011000110 */ - 0x04, 0x00, /* 0000010000 */ - 0x04, 0x00, /* 0000010000 */ - 0x0e, 0x00, /* 0000111000 */ - 0x0e, 0x00, /* 0000111000 */ - 0x0e, 0x00, /* 0000111000 */ - 0x19, 0x00, /* 0001100100 */ - 0x19, 0x00, /* 0001100100 */ - 0x3f, 0x00, /* 0011111100 */ - 0x31, 0x00, /* 0011000100 */ - 0x31, 0x00, /* 0011000100 */ - 0x60, 0x80, /* 0110000010 */ - 0x60, 0x80, /* 0110000010 */ - 0xf3, 0xc0, /* 1111001111 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 143 0x8f '.' */ - 0x04, 0x00, /* 0000010000 */ - 0x0a, 0x00, /* 0000101000 */ - 0x0a, 0x00, /* 0000101000 */ - 0x04, 0x00, /* 0000010000 */ - 0x0e, 0x00, /* 0000111000 */ - 0x0e, 0x00, /* 0000111000 */ - 0x0e, 0x00, /* 0000111000 */ - 0x19, 0x00, /* 0001100100 */ - 0x19, 0x00, /* 0001100100 */ - 0x3f, 0x00, /* 0011111100 */ - 0x31, 0x00, /* 0011000100 */ - 0x31, 0x00, /* 0011000100 */ - 0x60, 0x80, /* 0110000010 */ - 0x60, 0x80, /* 0110000010 */ - 0xf3, 0xc0, /* 1111001111 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 144 0x90 '.' */ - 0x03, 0x00, /* 0000001100 */ - 0x06, 0x00, /* 0000011000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x7f, 0x80, /* 0111111110 */ - 0x30, 0x80, /* 0011000010 */ - 0x30, 0x80, /* 0011000010 */ - 0x30, 0x00, /* 0011000000 */ - 0x31, 0x00, /* 0011000100 */ - 0x3f, 0x00, /* 0011111100 */ - 0x31, 0x00, /* 0011000100 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x80, /* 0011000010 */ - 0x30, 0x80, /* 0011000010 */ - 0x7f, 0x80, /* 0111111110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 145 0x91 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x3b, 0x80, /* 0011101110 */ - 0x6c, 0xc0, /* 0110110011 */ - 0x4c, 0xc0, /* 0100110011 */ - 0x0c, 0xc0, /* 0000110011 */ - 0x3f, 0xc0, /* 0011111111 */ - 0x6c, 0x00, /* 0110110000 */ - 0xcc, 0x00, /* 1100110000 */ - 0xcc, 0x00, /* 1100110000 */ - 0xee, 0xc0, /* 1110111011 */ - 0x7b, 0x80, /* 0111101110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 146 0x92 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x07, 0xc0, /* 0000011111 */ - 0x0e, 0x40, /* 0000111001 */ - 0x0e, 0x40, /* 0000111001 */ - 0x0e, 0x00, /* 0000111000 */ - 0x16, 0x00, /* 0001011000 */ - 0x16, 0x80, /* 0001011010 */ - 0x17, 0x80, /* 0001011110 */ - 0x16, 0x80, /* 0001011010 */ - 0x3e, 0x00, /* 0011111000 */ - 0x26, 0x00, /* 0010011000 */ - 0x26, 0x00, /* 0010011000 */ - 0x46, 0x40, /* 0100011001 */ - 0x46, 0x40, /* 0100011001 */ - 0xef, 0xc0, /* 1110111111 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 147 0x93 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x08, 0x00, /* 0000100000 */ - 0x1c, 0x00, /* 0001110000 */ - 0x36, 0x00, /* 0011011000 */ - 0x00, 0x00, /* 0000000000 */ - 0x3c, 0x00, /* 0011110000 */ - 0x66, 0x00, /* 0110011000 */ - 0xc3, 0x00, /* 1100001100 */ - 0xc1, 0x80, /* 1100000110 */ - 0xc1, 0x80, /* 1100000110 */ - 0xc1, 0x80, /* 1100000110 */ - 0xc1, 0x80, /* 1100000110 */ - 0xe1, 0x80, /* 1110000110 */ - 0x73, 0x00, /* 0111001100 */ - 0x3c, 0x00, /* 0011110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 148 0x94 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x33, 0x00, /* 0011001100 */ - 0x33, 0x00, /* 0011001100 */ - 0x00, 0x00, /* 0000000000 */ - 0x3c, 0x00, /* 0011110000 */ - 0x66, 0x00, /* 0110011000 */ - 0xc3, 0x00, /* 1100001100 */ - 0xc1, 0x80, /* 1100000110 */ - 0xc1, 0x80, /* 1100000110 */ - 0xc1, 0x80, /* 1100000110 */ - 0xc1, 0x80, /* 1100000110 */ - 0xe1, 0x80, /* 1110000110 */ - 0x73, 0x00, /* 0111001100 */ - 0x3c, 0x00, /* 0011110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 149 0x95 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x18, 0x00, /* 0001100000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x06, 0x00, /* 0000011000 */ - 0x00, 0x00, /* 0000000000 */ - 0x3c, 0x00, /* 0011110000 */ - 0x66, 0x00, /* 0110011000 */ - 0xc3, 0x00, /* 1100001100 */ - 0xc1, 0x80, /* 1100000110 */ - 0xc1, 0x80, /* 1100000110 */ - 0xc1, 0x80, /* 1100000110 */ - 0xc1, 0x80, /* 1100000110 */ - 0xe1, 0x80, /* 1110000110 */ - 0x73, 0x00, /* 0111001100 */ - 0x3c, 0x00, /* 0011110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 150 0x96 '.' */ - 0x08, 0x00, /* 0000100000 */ - 0x1c, 0x00, /* 0001110000 */ - 0x36, 0x00, /* 0011011000 */ - 0x63, 0x00, /* 0110001100 */ - 0x00, 0x00, /* 0000000000 */ - 0xf7, 0x80, /* 1111011110 */ - 0x63, 0x00, /* 0110001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x77, 0x00, /* 0111011100 */ - 0x3d, 0x80, /* 0011110110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 151 0x97 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x18, 0x00, /* 0001100000 */ - 0x00, 0x00, /* 0000000000 */ - 0xf7, 0x80, /* 1111011110 */ - 0x63, 0x00, /* 0110001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x77, 0x00, /* 0111011100 */ - 0x3d, 0x80, /* 0011110110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 152 0x98 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x33, 0x00, /* 0011001100 */ - 0x33, 0x00, /* 0011001100 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0xf3, 0xc0, /* 1111001111 */ - 0x61, 0x80, /* 0110000110 */ - 0x33, 0x00, /* 0011001100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1e, 0x00, /* 0001111000 */ - 0x0e, 0x00, /* 0000111000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x18, 0x00, /* 0001100000 */ - 0x78, 0x00, /* 0111100000 */ - 0x70, 0x00, /* 0111000000 */ - - /* 153 0x99 '.' */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x0c, 0x00, /* 0000110000 */ - 0x17, 0x00, /* 0001011100 */ - 0x23, 0x00, /* 0010001100 */ - 0x21, 0x80, /* 0010000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x21, 0x00, /* 0010000100 */ - 0x31, 0x00, /* 0011000100 */ - 0x1a, 0x00, /* 0001101000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 154 0x9a '.' */ - 0x33, 0x00, /* 0011001100 */ - 0x33, 0x00, /* 0011001100 */ - 0x00, 0x00, /* 0000000000 */ - 0xf1, 0xc0, /* 1111000111 */ - 0x60, 0x80, /* 0110000010 */ - 0x60, 0x80, /* 0110000010 */ - 0x60, 0x80, /* 0110000010 */ - 0x60, 0x80, /* 0110000010 */ - 0x60, 0x80, /* 0110000010 */ - 0x60, 0x80, /* 0110000010 */ - 0x60, 0x80, /* 0110000010 */ - 0x60, 0x80, /* 0110000010 */ - 0x71, 0x00, /* 0111000100 */ - 0x3f, 0x00, /* 0011111100 */ - 0x1e, 0x00, /* 0001111000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 155 0x9b '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x06, 0x00, /* 0000011000 */ - 0x06, 0x00, /* 0000011000 */ - 0x1f, 0x80, /* 0001111110 */ - 0x36, 0x80, /* 0011011010 */ - 0x26, 0x00, /* 0010011000 */ - 0x66, 0x00, /* 0110011000 */ - 0x66, 0x00, /* 0110011000 */ - 0x66, 0x00, /* 0110011000 */ - 0x66, 0x00, /* 0110011000 */ - 0x76, 0x00, /* 0111011000 */ - 0x36, 0x80, /* 0011011010 */ - 0x1f, 0x80, /* 0001111110 */ - 0x06, 0x00, /* 0000011000 */ - 0x06, 0x00, /* 0000011000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 156 0x9c '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x1e, 0x00, /* 0001111000 */ - 0x3b, 0x00, /* 0011101100 */ - 0x33, 0x00, /* 0011001100 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x7e, 0x00, /* 0111111000 */ - 0x7e, 0x00, /* 0111111000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x7c, 0x80, /* 0111110010 */ - 0x7f, 0x80, /* 0111111110 */ - 0x43, 0x00, /* 0100001100 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 157 0x9d '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x40, 0x80, /* 0100000010 */ - 0x40, 0x80, /* 0100000010 */ - 0x21, 0x00, /* 0010000100 */ - 0x33, 0x00, /* 0011001100 */ - 0x33, 0x00, /* 0011001100 */ - 0x1e, 0x00, /* 0001111000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x3f, 0x00, /* 0011111100 */ - 0x0c, 0x00, /* 0000110000 */ - 0x3f, 0x00, /* 0011111100 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 158 0x9e '.' */ - 0x00, 0x00, /* 0000000000 */ - 0xbf, 0x00, /* 1011111100 */ - 0x40, 0x80, /* 0100000010 */ - 0x40, 0x80, /* 0100000010 */ - 0x7f, 0x00, /* 0111111100 */ - 0x40, 0x00, /* 0100000000 */ - 0x48, 0x00, /* 0100100000 */ - 0x48, 0x00, /* 0100100000 */ - 0x5e, 0x00, /* 0101111000 */ - 0x48, 0x00, /* 0100100000 */ - 0x48, 0x00, /* 0100100000 */ - 0x48, 0x00, /* 0100100000 */ - 0x48, 0x80, /* 0100100010 */ - 0x47, 0x00, /* 0100011100 */ - 0xe0, 0x00, /* 1110000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 159 0x9f '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x03, 0x00, /* 0000001100 */ - 0x04, 0x80, /* 0000010010 */ - 0x08, 0x00, /* 0000100000 */ - 0x08, 0x00, /* 0000100000 */ - 0x08, 0x00, /* 0000100000 */ - 0x08, 0x00, /* 0000100000 */ - 0x09, 0x00, /* 0000100100 */ - 0x3e, 0x00, /* 0011111000 */ - 0x48, 0x00, /* 0100100000 */ - 0x08, 0x00, /* 0000100000 */ - 0x08, 0x00, /* 0000100000 */ - 0x08, 0x00, /* 0000100000 */ - 0x08, 0x00, /* 0000100000 */ - 0x08, 0x00, /* 0000100000 */ - 0x90, 0x00, /* 1001000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 160 0xa0 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x03, 0x00, /* 0000001100 */ - 0x06, 0x00, /* 0000011000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x1f, 0x00, /* 0001111100 */ - 0x31, 0x80, /* 0011000110 */ - 0x21, 0x80, /* 0010000110 */ - 0x07, 0x80, /* 0000011110 */ - 0x39, 0x80, /* 0011100110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x73, 0x80, /* 0111001110 */ - 0x3d, 0xc0, /* 0011110111 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 161 0xa1 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x03, 0x00, /* 0000001100 */ - 0x06, 0x00, /* 0000011000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x3c, 0x00, /* 0011110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x3f, 0x00, /* 0011111100 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 162 0xa2 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x06, 0x00, /* 0000011000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x18, 0x00, /* 0001100000 */ - 0x00, 0x00, /* 0000000000 */ - 0x3c, 0x00, /* 0011110000 */ - 0x66, 0x00, /* 0110011000 */ - 0xc3, 0x00, /* 1100001100 */ - 0xc1, 0x80, /* 1100000110 */ - 0xc1, 0x80, /* 1100000110 */ - 0xc1, 0x80, /* 1100000110 */ - 0xc1, 0x80, /* 1100000110 */ - 0xe1, 0x80, /* 1110000110 */ - 0x73, 0x00, /* 0111001100 */ - 0x3c, 0x00, /* 0011110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 163 0xa3 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x06, 0x00, /* 0000011000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x18, 0x00, /* 0001100000 */ - 0x00, 0x00, /* 0000000000 */ - 0xf7, 0x80, /* 1111011110 */ - 0x63, 0x00, /* 0110001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x77, 0x00, /* 0111011100 */ - 0x3d, 0x80, /* 0011110110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 164 0xa4 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x38, 0x80, /* 0011100010 */ - 0x7f, 0x80, /* 0111111110 */ - 0x47, 0x00, /* 0100011100 */ - 0x00, 0x00, /* 0000000000 */ - 0x6f, 0x00, /* 0110111100 */ - 0x7b, 0x80, /* 0111101110 */ - 0x31, 0x80, /* 0011000110 */ - 0x31, 0x80, /* 0011000110 */ - 0x31, 0x80, /* 0011000110 */ - 0x31, 0x80, /* 0011000110 */ - 0x31, 0x80, /* 0011000110 */ - 0x31, 0x80, /* 0011000110 */ - 0x31, 0x80, /* 0011000110 */ - 0x7b, 0xc0, /* 0111101111 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 165 0xa5 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x38, 0x80, /* 0011100010 */ - 0x7f, 0x80, /* 0111111110 */ - 0x47, 0x00, /* 0100011100 */ - 0x00, 0x00, /* 0000000000 */ - 0xe3, 0xc0, /* 1110001111 */ - 0x71, 0x80, /* 0111000110 */ - 0x79, 0x80, /* 0111100110 */ - 0x79, 0x80, /* 0111100110 */ - 0x6d, 0x80, /* 0110110110 */ - 0x6d, 0x80, /* 0110110110 */ - 0x67, 0x80, /* 0110011110 */ - 0x63, 0x80, /* 0110001110 */ - 0x61, 0x80, /* 0110000110 */ - 0xf0, 0xc0, /* 1111000011 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 166 0xa6 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x3e, 0x00, /* 0011111000 */ - 0x63, 0x00, /* 0110001100 */ - 0x03, 0x00, /* 0000001100 */ - 0x0f, 0x00, /* 0000111100 */ - 0x33, 0x00, /* 0011001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x67, 0x00, /* 0110011100 */ - 0x3b, 0x80, /* 0011101110 */ - 0x00, 0x00, /* 0000000000 */ - 0x7f, 0x80, /* 0111111110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 167 0xa7 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x0e, 0x00, /* 0000111000 */ - 0x33, 0x00, /* 0011001100 */ - 0x21, 0x80, /* 0010000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x00, /* 0110000100 */ - 0x33, 0x00, /* 0011001100 */ - 0x1c, 0x00, /* 0001110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x7f, 0x80, /* 0111111110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 168 0xa8 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x06, 0x00, /* 0000011000 */ - 0x06, 0x00, /* 0000011000 */ - 0x00, 0x00, /* 0000000000 */ - 0x06, 0x00, /* 0000011000 */ - 0x06, 0x00, /* 0000011000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x18, 0x00, /* 0001100000 */ - 0x30, 0x00, /* 0011000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x80, /* 0110000010 */ - 0x73, 0x80, /* 0111001110 */ - 0x3f, 0x00, /* 0011111100 */ - 0x1e, 0x00, /* 0001111000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 169 0xa9 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x7f, 0x80, /* 0111111110 */ - 0x7f, 0x80, /* 0111111110 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 170 0xaa '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x7f, 0x80, /* 0111111110 */ - 0x7f, 0x80, /* 0111111110 */ - 0x01, 0x80, /* 0000000110 */ - 0x01, 0x80, /* 0000000110 */ - 0x01, 0x80, /* 0000000110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 171 0xab '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x20, 0x00, /* 0010000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x20, 0x00, /* 0010000000 */ - 0x20, 0x80, /* 0010000010 */ - 0x21, 0x00, /* 0010000100 */ - 0x22, 0x00, /* 0010001000 */ - 0x74, 0x00, /* 0111010000 */ - 0x08, 0x00, /* 0000100000 */ - 0x17, 0x00, /* 0001011100 */ - 0x28, 0x80, /* 0010100010 */ - 0x43, 0x00, /* 0100001100 */ - 0x04, 0x00, /* 0000010000 */ - 0x08, 0x00, /* 0000100000 */ - 0x0f, 0x80, /* 0000111110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 172 0xac '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x20, 0x00, /* 0010000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x20, 0x00, /* 0010000000 */ - 0x20, 0x80, /* 0010000010 */ - 0x21, 0x00, /* 0010000100 */ - 0x22, 0x00, /* 0010001000 */ - 0x74, 0x00, /* 0111010000 */ - 0x09, 0x00, /* 0000100100 */ - 0x13, 0x00, /* 0001001100 */ - 0x25, 0x00, /* 0010010100 */ - 0x49, 0x00, /* 0100100100 */ - 0x1f, 0x80, /* 0001111110 */ - 0x01, 0x00, /* 0000000100 */ - 0x01, 0x00, /* 0000000100 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 173 0xad '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 174 0xae '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x0d, 0x80, /* 0000110110 */ - 0x1b, 0x00, /* 0001101100 */ - 0x36, 0x00, /* 0011011000 */ - 0x6c, 0x00, /* 0110110000 */ - 0xd8, 0x00, /* 1101100000 */ - 0x6c, 0x00, /* 0110110000 */ - 0x36, 0x00, /* 0011011000 */ - 0x1b, 0x00, /* 0001101100 */ - 0x0d, 0x80, /* 0000110110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 175 0xaf '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x6c, 0x00, /* 0110110000 */ - 0x36, 0x00, /* 0011011000 */ - 0x1b, 0x00, /* 0001101100 */ - 0x0d, 0x80, /* 0000110110 */ - 0x06, 0xc0, /* 0000011011 */ - 0x0d, 0x80, /* 0000110110 */ - 0x1b, 0x00, /* 0001101100 */ - 0x36, 0x00, /* 0011011000 */ - 0x6c, 0x00, /* 0110110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 176 0xb0 '.' */ - 0xc3, 0x00, /* 1100001100 */ - 0x41, 0x00, /* 0100000100 */ - 0x18, 0x40, /* 0001100001 */ - 0x10, 0x40, /* 0001000001 */ - 0xc3, 0x00, /* 1100001100 */ - 0x41, 0x00, /* 0100000100 */ - 0x18, 0x40, /* 0001100001 */ - 0x10, 0x40, /* 0001000001 */ - 0xc3, 0x00, /* 1100001100 */ - 0x41, 0x00, /* 0100000100 */ - 0x18, 0x40, /* 0001100001 */ - 0x10, 0x40, /* 0001000001 */ - 0xc3, 0x00, /* 1100001100 */ - 0x41, 0x00, /* 0100000100 */ - 0x18, 0x40, /* 0001100001 */ - 0x10, 0x40, /* 0001000001 */ - 0xc3, 0x00, /* 1100001100 */ - 0x41, 0x00, /* 0100000100 */ - - /* 177 0xb1 '.' */ - 0x11, 0x00, /* 0001000100 */ - 0xbb, 0x80, /* 1011101110 */ - 0x11, 0x00, /* 0001000100 */ - 0x44, 0x40, /* 0100010001 */ - 0xee, 0xc0, /* 1110111011 */ - 0x44, 0x40, /* 0100010001 */ - 0x11, 0x00, /* 0001000100 */ - 0xbb, 0x80, /* 1011101110 */ - 0x11, 0x00, /* 0001000100 */ - 0x44, 0x40, /* 0100010001 */ - 0xee, 0xc0, /* 1110111011 */ - 0x44, 0x40, /* 0100010001 */ - 0x11, 0x00, /* 0001000100 */ - 0xbb, 0x80, /* 1011101110 */ - 0x11, 0x00, /* 0001000100 */ - 0x44, 0x40, /* 0100010001 */ - 0xee, 0xc0, /* 1110111011 */ - 0x44, 0x40, /* 0100010001 */ - - /* 178 0xb2 '.' */ - 0x3c, 0xc0, /* 0011110011 */ - 0xbe, 0xc0, /* 1011111011 */ - 0xe7, 0x80, /* 1110011110 */ - 0xef, 0x80, /* 1110111110 */ - 0x3c, 0xc0, /* 0011110011 */ - 0xbe, 0xc0, /* 1011111011 */ - 0xe7, 0x80, /* 1110011110 */ - 0xef, 0x80, /* 1110111110 */ - 0x3c, 0xc0, /* 0011110011 */ - 0xbe, 0xc0, /* 1011111011 */ - 0xe7, 0x80, /* 1110011110 */ - 0xef, 0x80, /* 1110111110 */ - 0x3c, 0xc0, /* 0011110011 */ - 0xbe, 0xc0, /* 1011111011 */ - 0xe7, 0x80, /* 1110011110 */ - 0xef, 0x80, /* 1110111110 */ - 0x3c, 0xc0, /* 0011110011 */ - 0xbe, 0xc0, /* 1011111011 */ - - /* 179 0xb3 '.' */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - - /* 180 0xb4 '.' */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0xfc, 0x00, /* 1111110000 */ - 0xfc, 0x00, /* 1111110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - - /* 181 0xb5 '.' */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0xfc, 0x00, /* 1111110000 */ - 0xfc, 0x00, /* 1111110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0xfc, 0x00, /* 1111110000 */ - 0xfc, 0x00, /* 1111110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - - /* 182 0xb6 '.' */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0xfb, 0x00, /* 1111101100 */ - 0xfb, 0x00, /* 1111101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - - /* 183 0xb7 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0xff, 0x00, /* 1111111100 */ - 0xff, 0x00, /* 1111111100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - - /* 184 0xb8 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0xfc, 0x00, /* 1111110000 */ - 0xfc, 0x00, /* 1111110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0xfc, 0x00, /* 1111110000 */ - 0xfc, 0x00, /* 1111110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - - /* 185 0xb9 '.' */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0xfb, 0x00, /* 1111101100 */ - 0xfb, 0x00, /* 1111101100 */ - 0x03, 0x00, /* 0000001100 */ - 0xfb, 0x00, /* 1111101100 */ - 0xfb, 0x00, /* 1111101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - - /* 186 0xba '.' */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - - /* 187 0xbb '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0xff, 0x00, /* 1111111100 */ - 0xff, 0x00, /* 1111111100 */ - 0x03, 0x00, /* 0000001100 */ - 0xfb, 0x00, /* 1111101100 */ - 0xfb, 0x00, /* 1111101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - - /* 188 0xbc '.' */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0xfb, 0x00, /* 1111101100 */ - 0xfb, 0x00, /* 1111101100 */ - 0x03, 0x00, /* 0000001100 */ - 0xff, 0x00, /* 1111111100 */ - 0xff, 0x00, /* 1111111100 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 189 0xbd '.' */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0xff, 0x00, /* 1111111100 */ - 0xff, 0x00, /* 1111111100 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 190 0xbe '.' */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0xfc, 0x00, /* 1111110000 */ - 0xfc, 0x00, /* 1111110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0xfc, 0x00, /* 1111110000 */ - 0xfc, 0x00, /* 1111110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 191 0xbf '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0xfc, 0x00, /* 1111110000 */ - 0xfc, 0x00, /* 1111110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - - /* 192 0xc0 '.' */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0f, 0xc0, /* 0000111111 */ - 0x0f, 0xc0, /* 0000111111 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 193 0xc1 '.' */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 194 0xc2 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - - /* 195 0xc3 '.' */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0f, 0xc0, /* 0000111111 */ - 0x0f, 0xc0, /* 0000111111 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - - /* 196 0xc4 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 197 0xc5 '.' */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - - /* 198 0xc6 '.' */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0f, 0xc0, /* 0000111111 */ - 0x0f, 0xc0, /* 0000111111 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0f, 0xc0, /* 0000111111 */ - 0x0f, 0xc0, /* 0000111111 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - - /* 199 0xc7 '.' */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0xc0, /* 0001101111 */ - 0x1b, 0xc0, /* 0001101111 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - - /* 200 0xc8 '.' */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0xc0, /* 0001101111 */ - 0x1b, 0xc0, /* 0001101111 */ - 0x18, 0x00, /* 0001100000 */ - 0x1f, 0xc0, /* 0001111111 */ - 0x1f, 0xc0, /* 0001111111 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 201 0xc9 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x1f, 0xc0, /* 0001111111 */ - 0x1f, 0xc0, /* 0001111111 */ - 0x18, 0x00, /* 0001100000 */ - 0x1b, 0xc0, /* 0001101111 */ - 0x1b, 0xc0, /* 0001101111 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - - /* 202 0xca '.' */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0xfb, 0xc0, /* 1111101111 */ - 0xfb, 0xc0, /* 1111101111 */ - 0x00, 0x00, /* 0000000000 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 203 0xcb '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0x00, 0x00, /* 0000000000 */ - 0xfb, 0xc0, /* 1111101111 */ - 0xfb, 0xc0, /* 1111101111 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - - /* 204 0xcc '.' */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0xc0, /* 0001101111 */ - 0x1b, 0xc0, /* 0001101111 */ - 0x18, 0x00, /* 0001100000 */ - 0x1b, 0xc0, /* 0001101111 */ - 0x1b, 0xc0, /* 0001101111 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - - /* 205 0xcd '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0x00, 0x00, /* 0000000000 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 206 0xce '.' */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0xfb, 0xc0, /* 1111101111 */ - 0xfb, 0xc0, /* 1111101111 */ - 0x00, 0x00, /* 0000000000 */ - 0xfb, 0xc0, /* 1111101111 */ - 0xfb, 0xc0, /* 1111101111 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - - /* 207 0xcf '.' */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0x00, 0x00, /* 0000000000 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 208 0xd0 '.' */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 209 0xd1 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0x00, 0x00, /* 0000000000 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - - /* 210 0xd2 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - - /* 211 0xd3 '.' */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1f, 0xc0, /* 0001111111 */ - 0x1f, 0xc0, /* 0001111111 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 212 0xd4 '.' */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0f, 0xc0, /* 0000111111 */ - 0x0f, 0xc0, /* 0000111111 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0f, 0xc0, /* 0000111111 */ - 0x0f, 0xc0, /* 0000111111 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 213 0xd5 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x0f, 0xc0, /* 0000111111 */ - 0x0f, 0xc0, /* 0000111111 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0f, 0xc0, /* 0000111111 */ - 0x0f, 0xc0, /* 0000111111 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - - /* 214 0xd6 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x1f, 0xc0, /* 0001111111 */ - 0x1f, 0xc0, /* 0001111111 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - - /* 215 0xd7 '.' */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - 0x1b, 0x00, /* 0001101100 */ - - /* 216 0xd8 '.' */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0x0c, 0x00, /* 0000110000 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - - /* 217 0xd9 '.' */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0xfc, 0x00, /* 1111110000 */ - 0xfc, 0x00, /* 1111110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 218 0xda '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x0f, 0xc0, /* 0000111111 */ - 0x0f, 0xc0, /* 0000111111 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - - /* 219 0xdb '.' */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - - /* 220 0xdc '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - - /* 221 0xdd '.' */ - 0xf8, 0x00, /* 1111100000 */ - 0xf8, 0x00, /* 1111100000 */ - 0xf8, 0x00, /* 1111100000 */ - 0xf8, 0x00, /* 1111100000 */ - 0xf8, 0x00, /* 1111100000 */ - 0xf8, 0x00, /* 1111100000 */ - 0xf8, 0x00, /* 1111100000 */ - 0xf8, 0x00, /* 1111100000 */ - 0xf8, 0x00, /* 1111100000 */ - 0xf8, 0x00, /* 1111100000 */ - 0xf8, 0x00, /* 1111100000 */ - 0xf8, 0x00, /* 1111100000 */ - 0xf8, 0x00, /* 1111100000 */ - 0xf8, 0x00, /* 1111100000 */ - 0xf8, 0x00, /* 1111100000 */ - 0xf8, 0x00, /* 1111100000 */ - 0xf8, 0x00, /* 1111100000 */ - 0xf8, 0x00, /* 1111100000 */ - - /* 222 0xde '.' */ - 0x07, 0xc0, /* 0000011111 */ - 0x07, 0xc0, /* 0000011111 */ - 0x07, 0xc0, /* 0000011111 */ - 0x07, 0xc0, /* 0000011111 */ - 0x07, 0xc0, /* 0000011111 */ - 0x07, 0xc0, /* 0000011111 */ - 0x07, 0xc0, /* 0000011111 */ - 0x07, 0xc0, /* 0000011111 */ - 0x07, 0xc0, /* 0000011111 */ - 0x07, 0xc0, /* 0000011111 */ - 0x07, 0xc0, /* 0000011111 */ - 0x07, 0xc0, /* 0000011111 */ - 0x07, 0xc0, /* 0000011111 */ - 0x07, 0xc0, /* 0000011111 */ - 0x07, 0xc0, /* 0000011111 */ - 0x07, 0xc0, /* 0000011111 */ - 0x07, 0xc0, /* 0000011111 */ - 0x07, 0xc0, /* 0000011111 */ - - /* 223 0xdf '.' */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0xff, 0xc0, /* 1111111111 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 224 0xe0 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x1c, 0x80, /* 0001110010 */ - 0x35, 0x80, /* 0011010110 */ - 0x63, 0x00, /* 0110001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x63, 0x00, /* 0110001100 */ - 0x37, 0x80, /* 0011011110 */ - 0x1c, 0x80, /* 0001110010 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 225 0xe1 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x1e, 0x00, /* 0001111000 */ - 0x33, 0x00, /* 0011001100 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x63, 0x00, /* 0110001100 */ - 0x6f, 0x00, /* 0110111100 */ - 0x63, 0x00, /* 0110001100 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x63, 0x00, /* 0110001100 */ - 0x6e, 0x00, /* 0110111000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 226 0xe2 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x7f, 0x80, /* 0111111110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 227 0xe3 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x7f, 0x80, /* 0111111110 */ - 0x33, 0x00, /* 0011001100 */ - 0x33, 0x00, /* 0011001100 */ - 0x33, 0x00, /* 0011001100 */ - 0x33, 0x00, /* 0011001100 */ - 0x33, 0x00, /* 0011001100 */ - 0x33, 0x00, /* 0011001100 */ - 0x33, 0x00, /* 0011001100 */ - 0x33, 0x00, /* 0011001100 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 228 0xe4 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0xff, 0x80, /* 1111111110 */ - 0x60, 0x00, /* 0110000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x18, 0x00, /* 0001100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x18, 0x00, /* 0001100000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x80, /* 0011000010 */ - 0x61, 0x80, /* 0110000110 */ - 0xff, 0x80, /* 1111111110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 229 0xe5 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x1f, 0xc0, /* 0001111111 */ - 0x36, 0x00, /* 0011011000 */ - 0x63, 0x00, /* 0110001100 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x33, 0x00, /* 0011001100 */ - 0x3e, 0x00, /* 0011111000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 230 0xe6 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x73, 0x80, /* 0111001110 */ - 0x6d, 0x80, /* 0110110110 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0xc0, 0x00, /* 1100000000 */ - - /* 231 0xe7 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x01, 0x80, /* 0000000110 */ - 0x36, 0x40, /* 0011011001 */ - 0x5e, 0x00, /* 0101111000 */ - 0x8c, 0x00, /* 1000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 232 0xe8 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x7f, 0x80, /* 0111111110 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x1e, 0x00, /* 0001111000 */ - 0x33, 0x00, /* 0011001100 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x33, 0x00, /* 0011001100 */ - 0x1e, 0x00, /* 0001111000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x7f, 0x80, /* 0111111110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 233 0xe9 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x0e, 0x00, /* 0000111000 */ - 0x1f, 0x00, /* 0001111100 */ - 0x31, 0x80, /* 0011000110 */ - 0x31, 0x80, /* 0011000110 */ - 0x60, 0xc0, /* 0110000011 */ - 0x7f, 0xc0, /* 0111111111 */ - 0x7f, 0xc0, /* 0111111111 */ - 0x60, 0xc0, /* 0110000011 */ - 0x31, 0x80, /* 0011000110 */ - 0x31, 0x80, /* 0011000110 */ - 0x1f, 0x00, /* 0001111100 */ - 0x0e, 0x00, /* 0000111000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 234 0xea '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x1e, 0x00, /* 0001111000 */ - 0x33, 0x00, /* 0011001100 */ - 0x61, 0x80, /* 0110000110 */ - 0xc0, 0xc0, /* 1100000011 */ - 0xc0, 0xc0, /* 1100000011 */ - 0xc0, 0xc0, /* 1100000011 */ - 0x61, 0x80, /* 0110000110 */ - 0x33, 0x00, /* 0011001100 */ - 0x33, 0x00, /* 0011001100 */ - 0x33, 0x00, /* 0011001100 */ - 0x33, 0x00, /* 0011001100 */ - 0xf3, 0xc0, /* 1111001111 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 235 0xeb '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x07, 0x00, /* 0000011100 */ - 0x1f, 0x80, /* 0001111110 */ - 0x30, 0xc0, /* 0011000011 */ - 0x30, 0x00, /* 0011000000 */ - 0x18, 0x00, /* 0001100000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x3e, 0x00, /* 0011111000 */ - 0x66, 0x00, /* 0110011000 */ - 0xc3, 0x00, /* 1100001100 */ - 0xc3, 0x00, /* 1100001100 */ - 0xc3, 0x00, /* 1100001100 */ - 0x66, 0x00, /* 0110011000 */ - 0x3c, 0x00, /* 0011110000 */ - 0x18, 0x00, /* 0001100000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 236 0xec '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x33, 0x00, /* 0011001100 */ - 0x6d, 0x80, /* 0110110110 */ - 0xcc, 0xc0, /* 1100110011 */ - 0xcc, 0xc0, /* 1100110011 */ - 0xcc, 0xc0, /* 1100110011 */ - 0xcc, 0xc0, /* 1100110011 */ - 0x6d, 0x80, /* 0110110110 */ - 0x33, 0x00, /* 0011001100 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 237 0xed '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x01, 0x80, /* 0000000110 */ - 0x01, 0x80, /* 0000000110 */ - 0x03, 0x00, /* 0000001100 */ - 0x03, 0x00, /* 0000001100 */ - 0x37, 0x00, /* 0011011100 */ - 0x6d, 0x80, /* 0110110110 */ - 0xcc, 0xc0, /* 1100110011 */ - 0xcc, 0xc0, /* 1100110011 */ - 0xcc, 0xc0, /* 1100110011 */ - 0xcc, 0xc0, /* 1100110011 */ - 0x6d, 0x80, /* 0110110110 */ - 0x3b, 0x00, /* 0011101100 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x60, 0x00, /* 0110000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 238 0xee '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x03, 0x80, /* 0000001110 */ - 0x0e, 0x00, /* 0000111000 */ - 0x18, 0x00, /* 0001100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x3f, 0x80, /* 0011111110 */ - 0x3f, 0x80, /* 0011111110 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x18, 0x00, /* 0001100000 */ - 0x18, 0x00, /* 0001100000 */ - 0x0e, 0x00, /* 0000111000 */ - 0x03, 0x80, /* 0000001110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 239 0xef '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x1e, 0x00, /* 0001111000 */ - 0x33, 0x00, /* 0011001100 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x61, 0x80, /* 0110000110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 240 0xf0 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x7f, 0x80, /* 0111111110 */ - 0x7f, 0x80, /* 0111111110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x7f, 0x80, /* 0111111110 */ - 0x7f, 0x80, /* 0111111110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x7f, 0x80, /* 0111111110 */ - 0x7f, 0x80, /* 0111111110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 241 0xf1 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x7f, 0x80, /* 0111111110 */ - 0x7f, 0x80, /* 0111111110 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x7f, 0x80, /* 0111111110 */ - 0x7f, 0x80, /* 0111111110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 242 0xf2 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0xe0, 0x00, /* 1110000000 */ - 0x38, 0x00, /* 0011100000 */ - 0x0e, 0x00, /* 0000111000 */ - 0x03, 0x80, /* 0000001110 */ - 0x0e, 0x00, /* 0000111000 */ - 0x38, 0x00, /* 0011100000 */ - 0xe0, 0x00, /* 1110000000 */ - 0x00, 0x00, /* 0000000000 */ - 0xff, 0x00, /* 1111111100 */ - 0xff, 0x00, /* 1111111100 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 243 0xf3 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x03, 0x80, /* 0000001110 */ - 0x0e, 0x00, /* 0000111000 */ - 0x38, 0x00, /* 0011100000 */ - 0xe0, 0x00, /* 1110000000 */ - 0x38, 0x00, /* 0011100000 */ - 0x0e, 0x00, /* 0000111000 */ - 0x03, 0x80, /* 0000001110 */ - 0x00, 0x00, /* 0000000000 */ - 0xff, 0x80, /* 1111111110 */ - 0xff, 0x80, /* 1111111110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 244 0xf4 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x1e, 0x00, /* 0001111000 */ - 0x33, 0x00, /* 0011001100 */ - 0x33, 0x00, /* 0011001100 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x30, 0x00, /* 0011000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 245 0xf5 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x1e, 0x00, /* 0001111000 */ - 0x33, 0x00, /* 0011001100 */ - 0x33, 0x00, /* 0011001100 */ - 0x03, 0x00, /* 0000001100 */ - 0x03, 0x00, /* 0000001100 */ - 0x03, 0x00, /* 0000001100 */ - 0x03, 0x00, /* 0000001100 */ - 0x03, 0x00, /* 0000001100 */ - 0x03, 0x00, /* 0000001100 */ - 0x03, 0x00, /* 0000001100 */ - 0x03, 0x00, /* 0000001100 */ - 0x03, 0x00, /* 0000001100 */ - 0x03, 0x00, /* 0000001100 */ - 0x03, 0x00, /* 0000001100 */ - 0x03, 0x00, /* 0000001100 */ - 0x00, 0x00, /* 0000000000 */ - - /* 246 0xf6 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x7f, 0x80, /* 0111111110 */ - 0x7f, 0x80, /* 0111111110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 247 0xf7 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x38, 0x00, /* 0011100000 */ - 0x6c, 0x00, /* 0110110000 */ - 0x06, 0xc0, /* 0000011011 */ - 0x03, 0x80, /* 0000001110 */ - 0x38, 0x00, /* 0011100000 */ - 0x6c, 0x00, /* 0110110000 */ - 0x06, 0xc0, /* 0000011011 */ - 0x03, 0x80, /* 0000001110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 248 0xf8 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x1e, 0x00, /* 0001111000 */ - 0x33, 0x00, /* 0011001100 */ - 0x33, 0x00, /* 0011001100 */ - 0x1e, 0x00, /* 0001111000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 249 0xf9 '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x1e, 0x00, /* 0001111000 */ - 0x1e, 0x00, /* 0001111000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 250 0xfa '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 251 0xfb '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x0f, 0xc0, /* 0000111111 */ - 0x0f, 0xc0, /* 0000111111 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0xcc, 0x00, /* 1100110000 */ - 0x6c, 0x00, /* 0110110000 */ - 0x3c, 0x00, /* 0011110000 */ - 0x1c, 0x00, /* 0001110000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 252 0xfc '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x27, 0x00, /* 0010011100 */ - 0x7b, 0x00, /* 0111101100 */ - 0x31, 0x00, /* 0011000100 */ - 0x31, 0x00, /* 0011000100 */ - 0x31, 0x00, /* 0011000100 */ - 0x7b, 0x80, /* 0111101110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 253 0xfd '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x1e, 0x00, /* 0001111000 */ - 0x3f, 0x00, /* 0011111100 */ - 0x63, 0x00, /* 0110001100 */ - 0x43, 0x00, /* 0100001100 */ - 0x06, 0x00, /* 0000011000 */ - 0x0c, 0x00, /* 0000110000 */ - 0x18, 0x00, /* 0001100000 */ - 0x30, 0x80, /* 0011000010 */ - 0x7f, 0x80, /* 0111111110 */ - 0x7f, 0x80, /* 0111111110 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 254 0xfe '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x3f, 0x00, /* 0011111100 */ - 0x3f, 0x00, /* 0011111100 */ - 0x3f, 0x00, /* 0011111100 */ - 0x3f, 0x00, /* 0011111100 */ - 0x3f, 0x00, /* 0011111100 */ - 0x3f, 0x00, /* 0011111100 */ - 0x3f, 0x00, /* 0011111100 */ - 0x3f, 0x00, /* 0011111100 */ - 0x3f, 0x00, /* 0011111100 */ - 0x3f, 0x00, /* 0011111100 */ - 0x3f, 0x00, /* 0011111100 */ - 0x3f, 0x00, /* 0011111100 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - - /* 255 0xff '.' */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - 0x00, 0x00, /* 0000000000 */ - -}; - - -const struct font_desc font_10x18 = { - .idx = FONT10x18_IDX, - .name = "10x18", - .width = 10, - .height = 18, - .data = fontdata_10x18, -#ifdef __sparc__ - .pref = 5, -#else - .pref = -1, -#endif -}; diff --git a/drivers/video/console/font_6x11.c b/drivers/video/console/font_6x11.c deleted file mode 100644 index 46e86e67aa6a..000000000000 --- a/drivers/video/console/font_6x11.c +++ /dev/null @@ -1,3352 +0,0 @@ -/**********************************************/ -/* */ -/* Font file generated by rthelen */ -/* */ -/**********************************************/ - -#include - -#define FONTDATAMAX (11*256) - -static const unsigned char fontdata_6x11[FONTDATAMAX] = { - - /* 0 0x00 '^@' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 1 0x01 '^A' */ - 0x00, /* 00000000 */ - 0x78, /* 0 000 */ - 0x84, /* 0000 00 */ - 0xcc, /* 00 00 */ - 0x84, /* 0000 00 */ - 0xb4, /* 0 0 00 */ - 0x84, /* 0000 00 */ - 0x78, /* 0 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 2 0x02 '^B' */ - 0x00, /* 00000000 */ - 0x78, /* 0 000 */ - 0xfc, /* 00 */ - 0xb4, /* 0 0 00 */ - 0xfc, /* 00 */ - 0xcc, /* 00 00 */ - 0xfc, /* 00 */ - 0x78, /* 0 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 3 0x03 '^C' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x28, /* 00 0 000 */ - 0x7c, /* 0 00 */ - 0x7c, /* 0 00 */ - 0x38, /* 00 000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 4 0x04 '^D' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x38, /* 00 000 */ - 0x7c, /* 0 00 */ - 0x38, /* 00 000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 5 0x05 '^E' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x38, /* 00 000 */ - 0x6c, /* 0 0 00 */ - 0x6c, /* 0 0 00 */ - 0x10, /* 000 0000 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 6 0x06 '^F' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x38, /* 00 000 */ - 0x7c, /* 0 00 */ - 0x7c, /* 0 00 */ - 0x10, /* 000 0000 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 7 0x07 '^G' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x30, /* 00 0000 */ - 0x78, /* 0 000 */ - 0x30, /* 00 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 8 0x08 '^H' */ - 0xff, /* */ - 0xff, /* */ - 0xff, /* */ - 0xcf, /* 00 */ - 0x87, /* 0000 */ - 0xcf, /* 00 */ - 0xff, /* */ - 0xff, /* */ - 0xff, /* */ - 0xff, /* */ - 0xff, /* */ - - /* 9 0x09 '^I' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x30, /* 00 0000 */ - 0x48, /* 0 00 000 */ - 0x84, /* 0000 00 */ - 0x48, /* 0 00 000 */ - 0x30, /* 00 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 10 0x0a '^J' */ - 0xff, /* */ - 0xff, /* */ - 0xcf, /* 00 */ - 0xb7, /* 0 0 */ - 0x7b, /* 0 0 */ - 0xb7, /* 0 0 */ - 0xcf, /* 00 */ - 0xff, /* */ - 0xff, /* */ - 0xff, /* */ - 0xff, /* */ - - /* 11 0x0b '^K' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x14, /* 000 0 00 */ - 0x20, /* 00 00000 */ - 0x78, /* 0 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 12 0x0c '^L' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x10, /* 000 0000 */ - 0x7c, /* 0 00 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 13 0x0d '^M' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x24, /* 00 00 00 */ - 0x3c, /* 00 00 */ - 0x20, /* 00 00000 */ - 0x20, /* 00 00000 */ - 0xe0, /* 00000 */ - 0xc0, /* 000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 14 0x0e '^N' */ - 0x00, /* 00000000 */ - 0x7c, /* 0 00 */ - 0x44, /* 0 000 00 */ - 0x7c, /* 0 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0xcc, /* 00 00 */ - 0xcc, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 15 0x0f '^O' */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x54, /* 0 0 0 00 */ - 0x38, /* 00 000 */ - 0x6c, /* 0 0 00 */ - 0x38, /* 00 000 */ - 0x54, /* 0 0 0 00 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 16 0x10 '^P' */ - 0x00, /* 00000000 */ - 0x40, /* 0 000000 */ - 0x60, /* 0 00000 */ - 0x70, /* 0 0000 */ - 0x7c, /* 0 00 */ - 0x70, /* 0 0000 */ - 0x60, /* 0 00000 */ - 0x40, /* 0 000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 17 0x11 '^Q' */ - 0x00, /* 00000000 */ - 0x04, /* 00000 00 */ - 0x0c, /* 0000 00 */ - 0x1c, /* 000 00 */ - 0x7c, /* 0 00 */ - 0x1c, /* 000 00 */ - 0x0c, /* 0000 00 */ - 0x04, /* 00000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 18 0x12 '^R' */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x38, /* 00 000 */ - 0x54, /* 0 0 0 00 */ - 0x10, /* 000 0000 */ - 0x54, /* 0 0 0 00 */ - 0x38, /* 00 000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 19 0x13 '^S' */ - 0x00, /* 00000000 */ - 0x48, /* 0 00 000 */ - 0x48, /* 0 00 000 */ - 0x48, /* 0 00 000 */ - 0x48, /* 0 00 000 */ - 0x48, /* 0 00 000 */ - 0x00, /* 00000000 */ - 0x48, /* 0 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 20 0x14 '^T' */ - 0x3c, /* 00 00 */ - 0x54, /* 0 0 0 00 */ - 0x54, /* 0 0 0 00 */ - 0x54, /* 0 0 0 00 */ - 0x3c, /* 00 00 */ - 0x14, /* 000 0 00 */ - 0x14, /* 000 0 00 */ - 0x14, /* 000 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 21 0x15 '^U' */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x24, /* 00 00 00 */ - 0x50, /* 0 0 0000 */ - 0x48, /* 0 00 000 */ - 0x24, /* 00 00 00 */ - 0x14, /* 000 0 00 */ - 0x48, /* 0 00 000 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - - /* 22 0x16 '^V' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xf8, /* 000 */ - 0xf8, /* 000 */ - 0xf8, /* 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 23 0x17 '^W' */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x38, /* 00 000 */ - 0x54, /* 0 0 0 00 */ - 0x10, /* 000 0000 */ - 0x54, /* 0 0 0 00 */ - 0x38, /* 00 000 */ - 0x10, /* 000 0000 */ - 0x7c, /* 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 24 0x18 '^X' */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x38, /* 00 000 */ - 0x54, /* 0 0 0 00 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 25 0x19 '^Y' */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x54, /* 0 0 0 00 */ - 0x38, /* 00 000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 26 0x1a '^Z' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x08, /* 0000 000 */ - 0x7c, /* 0 00 */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 27 0x1b '^[' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x20, /* 00 00000 */ - 0x7c, /* 0 00 */ - 0x20, /* 00 00000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 28 0x1c '^\' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x78, /* 0 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 29 0x1d '^]' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x48, /* 0 00 000 */ - 0x84, /* 0000 00 */ - 0xfc, /* 00 */ - 0x84, /* 0000 00 */ - 0x48, /* 0 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 30 0x1e '^^' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x38, /* 00 000 */ - 0x38, /* 00 000 */ - 0x7c, /* 0 00 */ - 0x7c, /* 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 31 0x1f '^`' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 0 00 */ - 0x7c, /* 0 00 */ - 0x38, /* 00 000 */ - 0x38, /* 00 000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 32 0x20 ' ' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 33 0x21 '!' */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 34 0x22 '"' */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 35 0x23 '#' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x28, /* 00 0 000 */ - 0x7c, /* 0 00 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x7c, /* 0 00 */ - 0x28, /* 00 0 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 36 0x24 '$' */ - 0x10, /* 000 0000 */ - 0x38, /* 00 000 */ - 0x54, /* 0 0 0 00 */ - 0x50, /* 0 0 0000 */ - 0x38, /* 00 000 */ - 0x14, /* 000 0 00 */ - 0x54, /* 0 0 0 00 */ - 0x38, /* 00 000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 37 0x25 '%' */ - 0x00, /* 00000000 */ - 0x64, /* 0 00 00 */ - 0x64, /* 0 00 00 */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x20, /* 00 00000 */ - 0x4c, /* 0 00 00 */ - 0x4c, /* 0 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 38 0x26 '&' */ - 0x00, /* 00000000 */ - 0x30, /* 00 0000 */ - 0x48, /* 0 00 000 */ - 0x50, /* 0 0 0000 */ - 0x20, /* 00 00000 */ - 0x54, /* 0 0 0 00 */ - 0x48, /* 0 00 000 */ - 0x34, /* 00 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 39 0x27 ''' */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 40 0x28 '(' */ - 0x04, /* 00000 00 */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x08, /* 0000 000 */ - 0x04, /* 00000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 41 0x29 ')' */ - 0x20, /* 00 00000 */ - 0x10, /* 000 0000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x20, /* 00 00000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 42 0x2a '*' */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x54, /* 0 0 0 00 */ - 0x38, /* 00 000 */ - 0x54, /* 0 0 0 00 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 43 0x2b '+' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x7c, /* 0 00 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 44 0x2c ',' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x30, /* 00 0000 */ - 0x30, /* 00 0000 */ - 0x10, /* 000 0000 */ - 0x20, /* 00 00000 */ - 0x00, /* 00000000 */ - - /* 45 0x2d '-' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 46 0x2e '.' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 000 000 */ - 0x18, /* 000 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 47 0x2f '/' */ - 0x04, /* 00000 00 */ - 0x04, /* 00000 00 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x20, /* 00 00000 */ - 0x20, /* 00 00000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x00, /* 00000000 */ - - /* 48 0x30 '0' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x4c, /* 0 00 00 */ - 0x54, /* 0 0 0 00 */ - 0x64, /* 0 00 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 49 0x31 '1' */ - 0x00, /* 00000000 */ - 0x08, /* 0000 000 */ - 0x18, /* 000 000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x1c, /* 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 50 0x32 '2' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x04, /* 00000 00 */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x20, /* 00 00000 */ - 0x7c, /* 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 51 0x33 '3' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x04, /* 00000 00 */ - 0x18, /* 000 000 */ - 0x04, /* 00000 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 52 0x34 '4' */ - 0x00, /* 00000000 */ - 0x08, /* 0000 000 */ - 0x18, /* 000 000 */ - 0x28, /* 00 0 000 */ - 0x48, /* 0 00 000 */ - 0x7c, /* 0 00 */ - 0x08, /* 0000 000 */ - 0x1c, /* 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 53 0x35 '5' */ - 0x00, /* 00000000 */ - 0x7c, /* 0 00 */ - 0x40, /* 0 000000 */ - 0x78, /* 0 000 */ - 0x04, /* 00000 00 */ - 0x04, /* 00000 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 54 0x36 '6' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x40, /* 0 000000 */ - 0x78, /* 0 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 55 0x37 '7' */ - 0x00, /* 00000000 */ - 0x7c, /* 0 00 */ - 0x04, /* 00000 00 */ - 0x04, /* 00000 00 */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 56 0x38 '8' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 57 0x39 '9' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x3c, /* 00 00 */ - 0x04, /* 00000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 58 0x3a ':' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 000 000 */ - 0x18, /* 000 000 */ - 0x00, /* 00000000 */ - 0x18, /* 000 000 */ - 0x18, /* 000 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 59 0x3b ';' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x30, /* 00 0000 */ - 0x30, /* 00 0000 */ - 0x00, /* 00000000 */ - 0x30, /* 00 0000 */ - 0x30, /* 00 0000 */ - 0x10, /* 000 0000 */ - 0x20, /* 00 00000 */ - 0x00, /* 00000000 */ - - /* 60 0x3c '<' */ - 0x00, /* 00000000 */ - 0x04, /* 00000 00 */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x20, /* 00 00000 */ - 0x10, /* 000 0000 */ - 0x08, /* 0000 000 */ - 0x04, /* 00000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 61 0x3d '=' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 0 00 */ - 0x00, /* 00000000 */ - 0x7c, /* 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 62 0x3e '>' */ - 0x00, /* 00000000 */ - 0x20, /* 00 00000 */ - 0x10, /* 000 0000 */ - 0x08, /* 0000 000 */ - 0x04, /* 00000 00 */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x20, /* 00 00000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 63 0x3f '?' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x04, /* 00000 00 */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 64 0x40 '@' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x74, /* 0 0 00 */ - 0x54, /* 0 0 0 00 */ - 0x78, /* 0 000 */ - 0x40, /* 0 000000 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 65 0x41 'A' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x7c, /* 0 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 66 0x42 'B' */ - 0x00, /* 00000000 */ - 0x78, /* 0 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x78, /* 0 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x78, /* 0 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 67 0x43 'C' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 68 0x44 'D' */ - 0x00, /* 00000000 */ - 0x78, /* 0 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x78, /* 0 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 69 0x45 'E' */ - 0x00, /* 00000000 */ - 0x7c, /* 0 00 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x78, /* 0 000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x7c, /* 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 70 0x46 'F' */ - 0x00, /* 00000000 */ - 0x7c, /* 0 00 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x78, /* 0 000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 71 0x47 'G' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x40, /* 0 000000 */ - 0x4c, /* 0 00 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 72 0x48 'H' */ - 0x00, /* 00000000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x7c, /* 0 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 73 0x49 'I' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 74 0x4a 'J' */ - 0x00, /* 00000000 */ - 0x04, /* 00000 00 */ - 0x04, /* 00000 00 */ - 0x04, /* 00000 00 */ - 0x04, /* 00000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 75 0x4b 'K' */ - 0x00, /* 00000000 */ - 0x44, /* 0 000 00 */ - 0x48, /* 0 00 000 */ - 0x50, /* 0 0 0000 */ - 0x60, /* 0 00000 */ - 0x50, /* 0 0 0000 */ - 0x48, /* 0 00 000 */ - 0x44, /* 0 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 76 0x4c 'L' */ - 0x00, /* 00000000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x7c, /* 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 77 0x4d 'M' */ - 0x00, /* 00000000 */ - 0x44, /* 0 000 00 */ - 0x6c, /* 0 0 00 */ - 0x54, /* 0 0 0 00 */ - 0x54, /* 0 0 0 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 78 0x4e 'N' */ - 0x00, /* 00000000 */ - 0x44, /* 0 000 00 */ - 0x64, /* 0 00 00 */ - 0x54, /* 0 0 0 00 */ - 0x4c, /* 0 00 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 79 0x4f 'O' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 80 0x50 'P' */ - 0x00, /* 00000000 */ - 0x78, /* 0 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x78, /* 0 000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 81 0x51 'Q' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x54, /* 0 0 0 00 */ - 0x38, /* 00 000 */ - 0x04, /* 00000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 82 0x52 'R' */ - 0x00, /* 00000000 */ - 0x78, /* 0 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x78, /* 0 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 83 0x53 'S' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x40, /* 0 000000 */ - 0x38, /* 00 000 */ - 0x04, /* 00000 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 84 0x54 'T' */ - 0x00, /* 00000000 */ - 0x7c, /* 0 00 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 85 0x55 'U' */ - 0x00, /* 00000000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 86 0x56 'V' */ - 0x00, /* 00000000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x28, /* 00 0 000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 87 0x57 'W' */ - 0x00, /* 00000000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x54, /* 0 0 0 00 */ - 0x54, /* 0 0 0 00 */ - 0x6c, /* 0 0 00 */ - 0x44, /* 0 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 88 0x58 'X' */ - 0x00, /* 00000000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x28, /* 00 0 000 */ - 0x10, /* 000 0000 */ - 0x28, /* 00 0 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 89 0x59 'Y' */ - 0x00, /* 00000000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x28, /* 00 0 000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 90 0x5a 'Z' */ - 0x00, /* 00000000 */ - 0x7c, /* 0 00 */ - 0x04, /* 00000 00 */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x20, /* 00 00000 */ - 0x40, /* 0 000000 */ - 0x7c, /* 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 91 0x5b '[' */ - 0x0c, /* 0000 00 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x0c, /* 0000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 92 0x5c '\' */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x20, /* 00 00000 */ - 0x20, /* 00 00000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x04, /* 00000 00 */ - 0x04, /* 00000 00 */ - 0x00, /* 00000000 */ - - /* 93 0x5d ']' */ - 0x30, /* 00 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x30, /* 00 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 94 0x5e '^' */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x28, /* 00 0 000 */ - 0x44, /* 0 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 95 0x5f '_' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfc, /* 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 96 0x60 '`' */ - 0x20, /* 00 00000 */ - 0x10, /* 000 0000 */ - 0x08, /* 0000 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 97 0x61 'a' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x4c, /* 0 00 00 */ - 0x34, /* 00 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 98 0x62 'b' */ - 0x00, /* 00000000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x78, /* 0 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x78, /* 0 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 99 0x63 'c' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x40, /* 0 000000 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 100 0x64 'd' */ - 0x00, /* 00000000 */ - 0x04, /* 00000 00 */ - 0x04, /* 00000 00 */ - 0x3c, /* 00 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 101 0x65 'e' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x7c, /* 0 00 */ - 0x40, /* 0 000000 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 102 0x66 'f' */ - 0x00, /* 00000000 */ - 0x0c, /* 0000 00 */ - 0x10, /* 000 0000 */ - 0x38, /* 00 000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 103 0x67 'g' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x3c, /* 00 00 */ - 0x04, /* 00000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - - /* 104 0x68 'h' */ - 0x00, /* 00000000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x78, /* 0 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 105 0x69 'i' */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x30, /* 00 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 106 0x6a 'j' */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x30, /* 00 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x60, /* 0 00000 */ - 0x00, /* 00000000 */ - - /* 107 0x6b 'k' */ - 0x00, /* 00000000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x48, /* 0 00 000 */ - 0x50, /* 0 0 0000 */ - 0x70, /* 0 0000 */ - 0x48, /* 0 00 000 */ - 0x44, /* 0 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 108 0x6c 'l' */ - 0x00, /* 00000000 */ - 0x30, /* 00 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 109 0x6d 'm' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x78, /* 0 000 */ - 0x54, /* 0 0 0 00 */ - 0x54, /* 0 0 0 00 */ - 0x54, /* 0 0 0 00 */ - 0x54, /* 0 0 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 110 0x6e 'n' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x58, /* 0 0 000 */ - 0x64, /* 0 00 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 111 0x6f 'o' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 112 0x70 'p' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x78, /* 0 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x78, /* 0 000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x00, /* 00000000 */ - - /* 113 0x71 'q' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x3c, /* 00 00 */ - 0x04, /* 00000 00 */ - 0x04, /* 00000 00 */ - 0x00, /* 00000000 */ - - /* 114 0x72 'r' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x58, /* 0 0 000 */ - 0x64, /* 0 00 00 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 115 0x73 's' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x40, /* 0 000000 */ - 0x38, /* 00 000 */ - 0x04, /* 00000 00 */ - 0x78, /* 0 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 116 0x74 't' */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x38, /* 00 000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x0c, /* 0000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 117 0x75 'u' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x4c, /* 0 00 00 */ - 0x34, /* 00 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 118 0x76 'v' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x28, /* 00 0 000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 119 0x77 'w' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x54, /* 0 0 0 00 */ - 0x54, /* 0 0 0 00 */ - 0x54, /* 0 0 0 00 */ - 0x54, /* 0 0 0 00 */ - 0x28, /* 00 0 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 120 0x78 'x' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x44, /* 0 000 00 */ - 0x28, /* 00 0 000 */ - 0x10, /* 000 0000 */ - 0x28, /* 00 0 000 */ - 0x44, /* 0 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 121 0x79 'y' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x3c, /* 00 00 */ - 0x04, /* 00000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - - /* 122 0x7a 'z' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 0 00 */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x20, /* 00 00000 */ - 0x7c, /* 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 123 0x7b '{' */ - 0x04, /* 00000 00 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x04, /* 00000 00 */ - - /* 124 0x7c '|' */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 125 0x7d '}' */ - 0x20, /* 00 00000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x20, /* 00 00000 */ - - /* 126 0x7e '~' */ - 0x00, /* 00000000 */ - 0x34, /* 00 0 00 */ - 0x58, /* 0 0 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 127 0x7f '^?' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x28, /* 00 0 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x7c, /* 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 128 0x80 '\200' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x10, /* 000 0000 */ - 0x20, /* 00 00000 */ - 0x00, /* 00000000 */ - - /* 129 0x81 '\201' */ - 0x00, /* 00000000 */ - 0x28, /* 00 0 000 */ - 0x00, /* 00000000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x4c, /* 0 00 00 */ - 0x34, /* 00 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 130 0x82 '\202' */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x7c, /* 0 00 */ - 0x40, /* 0 000000 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 131 0x83 '\203' */ - 0x10, /* 000 0000 */ - 0x28, /* 00 0 000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x4c, /* 0 00 00 */ - 0x34, /* 00 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 132 0x84 '\204' */ - 0x00, /* 00000000 */ - 0x28, /* 00 0 000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x4c, /* 0 00 00 */ - 0x34, /* 00 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 133 0x85 '\205' */ - 0x10, /* 000 0000 */ - 0x08, /* 0000 000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x4c, /* 0 00 00 */ - 0x34, /* 00 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 134 0x86 '\206' */ - 0x18, /* 000 000 */ - 0x24, /* 00 00 00 */ - 0x18, /* 000 000 */ - 0x3c, /* 00 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x4c, /* 0 00 00 */ - 0x34, /* 00 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 135 0x87 '\207' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x3c, /* 00 00 */ - 0x10, /* 000 0000 */ - 0x20, /* 00 00000 */ - 0x00, /* 00000000 */ - - /* 136 0x88 '\210' */ - 0x10, /* 000 0000 */ - 0x28, /* 00 0 000 */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x7c, /* 0 00 */ - 0x40, /* 0 000000 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 137 0x89 '\211' */ - 0x00, /* 00000000 */ - 0x28, /* 00 0 000 */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x7c, /* 0 00 */ - 0x40, /* 0 000000 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 138 0x8a '\212' */ - 0x20, /* 00 00000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x7c, /* 0 00 */ - 0x40, /* 0 000000 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 139 0x8b '\213' */ - 0x00, /* 00000000 */ - 0x28, /* 00 0 000 */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 140 0x8c '\214' */ - 0x10, /* 000 0000 */ - 0x28, /* 00 0 000 */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 141 0x8d '\215' */ - 0x20, /* 00 00000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 142 0x8e '\216' */ - 0x84, /* 0000 00 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x7c, /* 0 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 143 0x8f '\217' */ - 0x58, /* 0 0 000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x7c, /* 0 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 144 0x90 '\220' */ - 0x10, /* 000 0000 */ - 0x7c, /* 0 00 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x78, /* 0 000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x7c, /* 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 145 0x91 '\221' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x54, /* 0 0 0 00 */ - 0x5c, /* 0 0 00 */ - 0x50, /* 0 0 0000 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 146 0x92 '\222' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x50, /* 0 0 0000 */ - 0x50, /* 0 0 0000 */ - 0x78, /* 0 000 */ - 0x50, /* 0 0 0000 */ - 0x50, /* 0 0 0000 */ - 0x5c, /* 0 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 147 0x93 '\223' */ - 0x10, /* 000 0000 */ - 0x28, /* 00 0 000 */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 148 0x94 '\224' */ - 0x00, /* 00000000 */ - 0x28, /* 00 0 000 */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 149 0x95 '\225' */ - 0x20, /* 00 00000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 150 0x96 '\226' */ - 0x10, /* 000 0000 */ - 0x28, /* 00 0 000 */ - 0x00, /* 00000000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x4c, /* 0 00 00 */ - 0x34, /* 00 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 151 0x97 '\227' */ - 0x20, /* 00 00000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x4c, /* 0 00 00 */ - 0x34, /* 00 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 152 0x98 '\230' */ - 0x00, /* 00000000 */ - 0x28, /* 00 0 000 */ - 0x00, /* 00000000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x3c, /* 00 00 */ - 0x04, /* 00000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - - /* 153 0x99 '\231' */ - 0x84, /* 0000 00 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 154 0x9a '\232' */ - 0x88, /* 000 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 155 0x9b '\233' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x38, /* 00 000 */ - 0x54, /* 0 0 0 00 */ - 0x50, /* 0 0 0000 */ - 0x54, /* 0 0 0 00 */ - 0x38, /* 00 000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 156 0x9c '\234' */ - 0x30, /* 00 0000 */ - 0x48, /* 0 00 000 */ - 0x40, /* 0 000000 */ - 0x70, /* 0 0000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x44, /* 0 000 00 */ - 0x78, /* 0 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 157 0x9d '\235' */ - 0x00, /* 00000000 */ - 0x44, /* 0 000 00 */ - 0x28, /* 00 0 000 */ - 0x7c, /* 0 00 */ - 0x10, /* 000 0000 */ - 0x7c, /* 0 00 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 158 0x9e '\236' */ - 0x00, /* 00000000 */ - 0x70, /* 0 0000 */ - 0x48, /* 0 00 000 */ - 0x70, /* 0 0000 */ - 0x48, /* 0 00 000 */ - 0x5c, /* 0 0 00 */ - 0x48, /* 0 00 000 */ - 0x44, /* 0 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 159 0x9f '\237' */ - 0x00, /* 00000000 */ - 0x0c, /* 0000 00 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x38, /* 00 000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x60, /* 0 00000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 160 0xa0 '\240' */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x4c, /* 0 00 00 */ - 0x34, /* 00 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 161 0xa1 '\241' */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 162 0xa2 '\242' */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 163 0xa3 '\243' */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x4c, /* 0 00 00 */ - 0x34, /* 00 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 164 0xa4 '\244' */ - 0x34, /* 00 0 00 */ - 0x58, /* 0 0 000 */ - 0x00, /* 00000000 */ - 0x58, /* 0 0 000 */ - 0x64, /* 0 00 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 165 0xa5 '\245' */ - 0x58, /* 0 0 000 */ - 0x44, /* 0 000 00 */ - 0x64, /* 0 00 00 */ - 0x54, /* 0 0 0 00 */ - 0x4c, /* 0 00 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 166 0xa6 '\246' */ - 0x00, /* 00000000 */ - 0x1c, /* 000 00 */ - 0x24, /* 00 00 00 */ - 0x24, /* 00 00 00 */ - 0x1c, /* 000 00 */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 167 0xa7 '\247' */ - 0x00, /* 00000000 */ - 0x18, /* 000 000 */ - 0x24, /* 00 00 00 */ - 0x24, /* 00 00 00 */ - 0x18, /* 000 000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 168 0xa8 '\250' */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x20, /* 00 00000 */ - 0x40, /* 0 000000 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 169 0xa9 '\251' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 0 00 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 170 0xaa '\252' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 0 00 */ - 0x04, /* 00000 00 */ - 0x04, /* 00000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 171 0xab '\253' */ - 0x20, /* 00 00000 */ - 0x60, /* 0 00000 */ - 0x24, /* 00 00 00 */ - 0x28, /* 00 0 000 */ - 0x10, /* 000 0000 */ - 0x28, /* 00 0 000 */ - 0x44, /* 0 000 00 */ - 0x08, /* 0000 000 */ - 0x1c, /* 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 172 0xac '\254' */ - 0x20, /* 00 00000 */ - 0x60, /* 0 00000 */ - 0x24, /* 00 00 00 */ - 0x28, /* 00 0 000 */ - 0x10, /* 000 0000 */ - 0x28, /* 00 0 000 */ - 0x58, /* 0 0 000 */ - 0x3c, /* 00 00 */ - 0x08, /* 0000 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 173 0xad '\255' */ - 0x00, /* 00000000 */ - 0x08, /* 0000 000 */ - 0x00, /* 00000000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 174 0xae '\256' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x24, /* 00 00 00 */ - 0x48, /* 0 00 000 */ - 0x48, /* 0 00 000 */ - 0x24, /* 00 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 175 0xaf '\257' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x48, /* 0 00 000 */ - 0x24, /* 00 00 00 */ - 0x24, /* 00 00 00 */ - 0x48, /* 0 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 176 0xb0 '\260' */ - 0x11, /* 000 000 */ - 0x44, /* 0 000 00 */ - 0x11, /* 000 000 */ - 0x44, /* 0 000 00 */ - 0x11, /* 000 000 */ - 0x44, /* 0 000 00 */ - 0x11, /* 000 000 */ - 0x44, /* 0 000 00 */ - 0x11, /* 000 000 */ - 0x44, /* 0 000 00 */ - 0x11, /* 000 000 */ - - /* 177 0xb1 '\261' */ - 0x55, /* 0 0 0 0 */ - 0xaa, /* 0 0 0 0 */ - 0x55, /* 0 0 0 0 */ - 0xaa, /* 0 0 0 0 */ - 0x55, /* 0 0 0 0 */ - 0xaa, /* 0 0 0 0 */ - 0x55, /* 0 0 0 0 */ - 0xaa, /* 0 0 0 0 */ - 0x55, /* 0 0 0 0 */ - 0xaa, /* 0 0 0 0 */ - 0x55, /* 0 0 0 0 */ - - /* 178 0xb2 '\262' */ - 0xdd, /* 0 0 */ - 0x77, /* 0 0 */ - 0xdd, /* 0 0 */ - 0x77, /* 0 0 */ - 0xdd, /* 0 0 */ - 0x77, /* 0 0 */ - 0xdd, /* 0 0 */ - 0x77, /* 0 0 */ - 0xdd, /* 0 0 */ - 0x77, /* 0 0 */ - 0xdd, /* 0 0 */ - - /* 179 0xb3 '\263' */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - - /* 180 0xb4 '\264' */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0xf0, /* 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - - /* 181 0xb5 '\265' */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0xf0, /* 0000 */ - 0x10, /* 000 0000 */ - 0xf0, /* 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - - /* 182 0xb6 '\266' */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0xe8, /* 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - - /* 183 0xb7 '\267' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xf8, /* 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - - /* 184 0xb8 '\270' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xf0, /* 0000 */ - 0x10, /* 000 0000 */ - 0xf0, /* 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - - /* 185 0xb9 '\271' */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0xe8, /* 0 000 */ - 0x08, /* 0000 000 */ - 0xe8, /* 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - - /* 186 0xba '\272' */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - - /* 187 0xbb '\273' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xf8, /* 000 */ - 0x08, /* 0000 000 */ - 0xe8, /* 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - - /* 188 0xbc '\274' */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0xe8, /* 0 000 */ - 0x08, /* 0000 000 */ - 0xf8, /* 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 189 0xbd '\275' */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0xf8, /* 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 190 0xbe '\276' */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0xf0, /* 0000 */ - 0x10, /* 000 0000 */ - 0xf0, /* 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 191 0xbf '\277' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xf0, /* 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - - /* 192 0xc0 '\300' */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x1c, /* 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 193 0xc1 '\301' */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0xfc, /* 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 194 0xc2 '\302' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfc, /* 00 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - - /* 195 0xc3 '\303' */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x1c, /* 000 00 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - - /* 196 0xc4 '\304' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfc, /* 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 197 0xc5 '\305' */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0xfc, /* 00 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - - /* 198 0xc6 '\306' */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x1c, /* 000 00 */ - 0x10, /* 000 0000 */ - 0x1c, /* 000 00 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - - /* 199 0xc7 '\307' */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x2c, /* 00 0 00 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - - /* 200 0xc8 '\310' */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x2c, /* 00 0 00 */ - 0x20, /* 00 00000 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 201 0xc9 '\311' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x20, /* 00 00000 */ - 0x2c, /* 00 0 00 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - - /* 202 0xca '\312' */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0xec, /* 0 00 */ - 0x00, /* 00000000 */ - 0xfc, /* 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 203 0xcb '\313' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfc, /* 00 */ - 0x00, /* 00000000 */ - 0xec, /* 0 00 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - - /* 204 0xcc '\314' */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x2c, /* 00 0 00 */ - 0x20, /* 00 00000 */ - 0x2c, /* 00 0 00 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - - /* 205 0xcd '\315' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfc, /* 00 */ - 0x00, /* 00000000 */ - 0xfc, /* 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 206 0xce '\316' */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0xec, /* 0 00 */ - 0x00, /* 00000000 */ - 0xec, /* 0 00 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - - /* 207 0xcf '\317' */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0xfc, /* 00 */ - 0x00, /* 00000000 */ - 0xfc, /* 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 208 0xd0 '\320' */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0xfc, /* 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 209 0xd1 '\321' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfc, /* 00 */ - 0x00, /* 00000000 */ - 0xfc, /* 00 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - - /* 210 0xd2 '\322' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfc, /* 00 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - - /* 211 0xd3 '\323' */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 212 0xd4 '\324' */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x1c, /* 000 00 */ - 0x10, /* 000 0000 */ - 0x1c, /* 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 213 0xd5 '\325' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x1c, /* 000 00 */ - 0x10, /* 000 0000 */ - 0x1c, /* 000 00 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - - /* 214 0xd6 '\326' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - - /* 215 0xd7 '\327' */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0xfc, /* 00 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - - /* 216 0xd8 '\330' */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0xfc, /* 00 */ - 0x10, /* 000 0000 */ - 0xfc, /* 00 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - - /* 217 0xd9 '\331' */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0xf0, /* 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 218 0xda '\332' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x1f, /* 000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - - /* 219 0xdb '\333' */ - 0xfc, /* 00 */ - 0xfc, /* 00 */ - 0xfc, /* 00 */ - 0xfc, /* 00 */ - 0xfc, /* 00 */ - 0xfc, /* 00 */ - 0xfc, /* 00 */ - 0xfc, /* 00 */ - 0xfc, /* 00 */ - 0xfc, /* 00 */ - 0xfc, /* 00 */ - - /* 220 0xdc '\334' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfc, /* 00 */ - 0xfc, /* 00 */ - 0xfc, /* 00 */ - 0xfc, /* 00 */ - 0xfc, /* 00 */ - 0xfc, /* 00 */ - - /* 221 0xdd '\335' */ - 0xe0, /* 00000 */ - 0xe0, /* 00000 */ - 0xe0, /* 00000 */ - 0xe0, /* 00000 */ - 0xe0, /* 00000 */ - 0xe0, /* 00000 */ - 0xe0, /* 00000 */ - 0xe0, /* 00000 */ - 0xe0, /* 00000 */ - 0xe0, /* 00000 */ - 0xe0, /* 00000 */ - - /* 222 0xde '\336' */ - 0x1c, /* 000 00 */ - 0x1c, /* 000 00 */ - 0x1c, /* 000 00 */ - 0x1c, /* 000 00 */ - 0x1c, /* 000 00 */ - 0x1c, /* 000 00 */ - 0x1c, /* 000 00 */ - 0x1c, /* 000 00 */ - 0x1c, /* 000 00 */ - 0x1c, /* 000 00 */ - 0x1c, /* 000 00 */ - - /* 223 0xdf '\337' */ - 0xfc, /* 00 */ - 0xfc, /* 00 */ - 0xfc, /* 00 */ - 0xfc, /* 00 */ - 0xfc, /* 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 224 0xe0 '\340' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x24, /* 00 00 00 */ - 0x58, /* 0 0 000 */ - 0x50, /* 0 0 0000 */ - 0x54, /* 0 0 0 00 */ - 0x2c, /* 00 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 225 0xe1 '\341' */ - 0x18, /* 000 000 */ - 0x24, /* 00 00 00 */ - 0x44, /* 0 000 00 */ - 0x48, /* 0 00 000 */ - 0x48, /* 0 00 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x58, /* 0 0 000 */ - 0x40, /* 0 000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 226 0xe2 '\342' */ - 0x00, /* 00000000 */ - 0x7c, /* 0 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 227 0xe3 '\343' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 0 00 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 228 0xe4 '\344' */ - 0x00, /* 00000000 */ - 0x7c, /* 0 00 */ - 0x24, /* 00 00 00 */ - 0x10, /* 000 0000 */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x24, /* 00 00 00 */ - 0x7c, /* 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 229 0xe5 '\345' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x48, /* 0 00 000 */ - 0x48, /* 0 00 000 */ - 0x48, /* 0 00 000 */ - 0x30, /* 00 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 230 0xe6 '\346' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x48, /* 0 00 000 */ - 0x48, /* 0 00 000 */ - 0x48, /* 0 00 000 */ - 0x48, /* 0 00 000 */ - 0x74, /* 0 0 00 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x00, /* 00000000 */ - - /* 231 0xe7 '\347' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x6c, /* 0 0 00 */ - 0x98, /* 00 000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 232 0xe8 '\350' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x10, /* 000 0000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x10, /* 000 0000 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 233 0xe9 '\351' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x4c, /* 0 00 00 */ - 0x54, /* 0 0 0 00 */ - 0x64, /* 0 00 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 234 0xea '\352' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x28, /* 00 0 000 */ - 0x6c, /* 0 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 235 0xeb '\353' */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x08, /* 0000 000 */ - 0x0c, /* 0000 00 */ - 0x14, /* 000 0 00 */ - 0x24, /* 00 00 00 */ - 0x24, /* 00 00 00 */ - 0x18, /* 000 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 236 0xec '\354' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x54, /* 0 0 0 00 */ - 0x54, /* 0 0 0 00 */ - 0x54, /* 0 0 0 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 237 0xed '\355' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x04, /* 00000 00 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x40, /* 0 000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 238 0xee '\356' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x78, /* 0 000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 239 0xef '\357' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 240 0xf0 '\360' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfc, /* 00 */ - 0x00, /* 00000000 */ - 0xfc, /* 00 */ - 0x00, /* 00000000 */ - 0xfc, /* 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 241 0xf1 '\361' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x7c, /* 0 00 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x7c, /* 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 242 0xf2 '\362' */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x08, /* 0000 000 */ - 0x04, /* 00000 00 */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x1c, /* 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 243 0xf3 '\363' */ - 0x00, /* 00000000 */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x20, /* 00 00000 */ - 0x10, /* 000 0000 */ - 0x08, /* 0000 000 */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 244 0xf4 '\364' */ - 0x00, /* 00000000 */ - 0x0c, /* 0000 00 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - - /* 245 0xf5 '\365' */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x60, /* 0 00000 */ - 0x00, /* 00000000 */ - - /* 246 0xf6 '\366' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x7c, /* 0 00 */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 247 0xf7 '\367' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x34, /* 00 0 00 */ - 0x48, /* 0 00 000 */ - 0x00, /* 00000000 */ - 0x34, /* 00 0 00 */ - 0x48, /* 0 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 248 0xf8 '\370' */ - 0x18, /* 000 000 */ - 0x24, /* 00 00 00 */ - 0x24, /* 00 00 00 */ - 0x18, /* 000 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 249 0xf9 '\371' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x38, /* 00 000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 250 0xfa '\372' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 251 0xfb '\373' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x0c, /* 0000 00 */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x50, /* 0 0 0000 */ - 0x20, /* 00 00000 */ - 0x20, /* 00 00000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 252 0xfc '\374' */ - 0x00, /* 00000000 */ - 0x50, /* 0 0 0000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 253 0xfd '\375' */ - 0x00, /* 00000000 */ - 0x70, /* 0 0000 */ - 0x08, /* 0000 000 */ - 0x20, /* 00 00000 */ - 0x78, /* 0 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 254 0xfe '\376' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x38, /* 00 000 */ - 0x38, /* 00 000 */ - 0x38, /* 00 000 */ - 0x38, /* 00 000 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 255 0xff '\377' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - -}; - - -const struct font_desc font_vga_6x11 = { - .idx = VGA6x11_IDX, - .name = "ProFont6x11", - .width = 6, - .height = 11, - .data = fontdata_6x11, - /* Try avoiding this font if possible unless on MAC */ - .pref = -2000, -}; diff --git a/drivers/video/console/font_7x14.c b/drivers/video/console/font_7x14.c deleted file mode 100644 index 3b7dbf9c060b..000000000000 --- a/drivers/video/console/font_7x14.c +++ /dev/null @@ -1,4118 +0,0 @@ -/**************************************/ -/* this file adapted from font_8x16.c */ -/* by Jurriaan Kalkman 05-2005 */ -/**************************************/ - -#include - -#define FONTDATAMAX 3584 - -static const unsigned char fontdata_7x14[FONTDATAMAX] = { - - /* 0 0x00 '^@' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 1 0x01 '^A' */ - 0x00, /* 0000000 */ - 0x7c, /* 0111110 */ - 0x82, /* 1000001 */ - 0xaa, /* 1010101 */ - 0x82, /* 1000001 */ - 0x82, /* 1000001 */ - 0xba, /* 1011101 */ - 0x92, /* 1001001 */ - 0x82, /* 1000001 */ - 0x7c, /* 0111110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 2 0x02 '^B' */ - 0x00, /* 0000000 */ - 0x7c, /* 0111110 */ - 0xfe, /* 1111111 */ - 0xd6, /* 1101011 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0xc6, /* 1100011 */ - 0xee, /* 1110111 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0x7c, /* 0111110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 3 0x03 '^C' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x6c, /* 0110110 */ - 0x7c, /* 0111110 */ - 0xfe, /* 1111111 */ - 0x7c, /* 0111110 */ - 0x38, /* 0011100 */ - 0x18, /* 0001100 */ - 0x10, /* 0001000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 4 0x04 '^D' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x10, /* 0001000 */ - 0x38, /* 0011100 */ - 0x7c, /* 0111110 */ - 0xfe, /* 1111111 */ - 0x7c, /* 0111110 */ - 0x38, /* 0011100 */ - 0x10, /* 0001000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 5 0x05 '^E' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x38, /* 0011100 */ - 0x38, /* 0011100 */ - 0x38, /* 0011100 */ - 0xee, /* 1110111 */ - 0xee, /* 1110111 */ - 0xee, /* 1110111 */ - 0x10, /* 0001000 */ - 0x10, /* 0001000 */ - 0x38, /* 0011100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 6 0x06 '^F' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x10, /* 0001000 */ - 0x38, /* 0011100 */ - 0x7c, /* 0111110 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0x7c, /* 0111110 */ - 0x10, /* 0001000 */ - 0x10, /* 0001000 */ - 0x38, /* 0011100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 7 0x07 '^G' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x18, /* 0001100 */ - 0x3c, /* 0011110 */ - 0x3c, /* 0011110 */ - 0x18, /* 0001100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 8 0x08 '^H' */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0xe6, /* 1110011 */ - 0xc2, /* 1100001 */ - 0xc2, /* 1100001 */ - 0xe6, /* 1110011 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - - /* 9 0x09 '^I' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x38, /* 0011100 */ - 0x6c, /* 0110110 */ - 0x44, /* 0100010 */ - 0x6c, /* 0110110 */ - 0x38, /* 0011100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 10 0x0a '^J' */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0xc6, /* 1100011 */ - 0x92, /* 1001001 */ - 0xba, /* 1011101 */ - 0x92, /* 1001001 */ - 0xc6, /* 1100011 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - - /* 11 0x0b '^K' */ - 0x00, /* 0000000 */ - 0x1e, /* 0001111 */ - 0x0e, /* 0000111 */ - 0x1a, /* 0001101 */ - 0x1a, /* 0001101 */ - 0x78, /* 0111100 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x78, /* 0111100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 12 0x0c '^L' */ - 0x00, /* 0000000 */ - 0x3c, /* 0011110 */ - 0x66, /* 0110011 */ - 0x66, /* 0110011 */ - 0x66, /* 0110011 */ - 0x66, /* 0110011 */ - 0x3c, /* 0011110 */ - 0x18, /* 0001100 */ - 0x7e, /* 0111111 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 13 0x0d '^M' */ - 0x00, /* 0000000 */ - 0x3e, /* 0011111 */ - 0x36, /* 0011011 */ - 0x3e, /* 0011111 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x70, /* 0111000 */ - 0xf0, /* 1111000 */ - 0xe0, /* 1110000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 14 0x0e '^N' */ - 0x00, /* 0000000 */ - 0x7e, /* 0111111 */ - 0x66, /* 0110011 */ - 0x7e, /* 0111111 */ - 0x66, /* 0110011 */ - 0x66, /* 0110011 */ - 0x66, /* 0110011 */ - 0x66, /* 0110011 */ - 0x6e, /* 0110111 */ - 0xee, /* 1110111 */ - 0xec, /* 1110110 */ - 0xc0, /* 1100000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 15 0x0f '^O' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x10, /* 0001000 */ - 0x10, /* 0001000 */ - 0xd6, /* 1101011 */ - 0x38, /* 0011100 */ - 0xee, /* 1110111 */ - 0x38, /* 0011100 */ - 0xd6, /* 1101011 */ - 0x10, /* 0001000 */ - 0x10, /* 0001000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 16 0x10 '^P' */ - 0x00, /* 0000000 */ - 0x80, /* 1000000 */ - 0xc0, /* 1100000 */ - 0xe0, /* 1110000 */ - 0xf0, /* 1111000 */ - 0xfc, /* 1111110 */ - 0xf0, /* 1111000 */ - 0xe0, /* 1110000 */ - 0xc0, /* 1100000 */ - 0x80, /* 1000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 17 0x11 '^Q' */ - 0x00, /* 0000000 */ - 0x04, /* 0000010 */ - 0x0c, /* 0000110 */ - 0x1c, /* 0001110 */ - 0x3c, /* 0011110 */ - 0xfc, /* 1111110 */ - 0x3c, /* 0011110 */ - 0x1c, /* 0001110 */ - 0x0c, /* 0000110 */ - 0x04, /* 0000010 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 18 0x12 '^R' */ - 0x00, /* 0000000 */ - 0x18, /* 0001100 */ - 0x3c, /* 0011110 */ - 0x7e, /* 0111111 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x7e, /* 0111111 */ - 0x3c, /* 0011110 */ - 0x18, /* 0001100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 19 0x13 '^S' */ - 0x00, /* 0000000 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x00, /* 0000000 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 20 0x14 '^T' */ - 0x00, /* 0000000 */ - 0x7e, /* 0111111 */ - 0xd4, /* 1101010 */ - 0xd4, /* 1101010 */ - 0xd4, /* 1101010 */ - 0x74, /* 0111010 */ - 0x14, /* 0001010 */ - 0x14, /* 0001010 */ - 0x14, /* 0001010 */ - 0x14, /* 0001010 */ - 0x16, /* 0001011 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 21 0x15 '^U' */ - 0x78, /* 0111100 */ - 0xcc, /* 1100110 */ - 0x60, /* 0110000 */ - 0x38, /* 0011100 */ - 0x6c, /* 0110110 */ - 0xc6, /* 1100011 */ - 0xc6, /* 1100011 */ - 0x6c, /* 0110110 */ - 0x38, /* 0011100 */ - 0x18, /* 0001100 */ - 0xcc, /* 1100110 */ - 0x78, /* 0111100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 22 0x16 '^V' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0xfc, /* 1111110 */ - 0xfc, /* 1111110 */ - 0xfc, /* 1111110 */ - 0xfc, /* 1111110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 23 0x17 '^W' */ - 0x00, /* 0000000 */ - 0x18, /* 0001100 */ - 0x3c, /* 0011110 */ - 0x7e, /* 0111111 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x7e, /* 0111111 */ - 0x3c, /* 0011110 */ - 0x18, /* 0001100 */ - 0x7e, /* 0111111 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 24 0x18 '^X' */ - 0x00, /* 0000000 */ - 0x18, /* 0001100 */ - 0x3c, /* 0011110 */ - 0x7e, /* 0111111 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 25 0x19 '^Y' */ - 0x00, /* 0000000 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x7e, /* 0111111 */ - 0x3c, /* 0011110 */ - 0x18, /* 0001100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 26 0x1a '^Z' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x30, /* 0011000 */ - 0x18, /* 0001100 */ - 0xfc, /* 1111110 */ - 0x18, /* 0001100 */ - 0x30, /* 0011000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 27 0x1b '^[' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x30, /* 0011000 */ - 0x60, /* 0110000 */ - 0xfc, /* 1111110 */ - 0x60, /* 0110000 */ - 0x30, /* 0011000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 28 0x1c '^\' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xfc, /* 1111110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 29 0x1d '^]' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x28, /* 0010100 */ - 0x6c, /* 0110110 */ - 0xfe, /* 1111111 */ - 0x6c, /* 0110110 */ - 0x28, /* 0010100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 30 0x1e '^^' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x78, /* 0111100 */ - 0x78, /* 0111100 */ - 0xfc, /* 1111110 */ - 0xfc, /* 1111110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 31 0x1f '^_' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0xfc, /* 1111110 */ - 0xfc, /* 1111110 */ - 0x78, /* 0111100 */ - 0x78, /* 0111100 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 32 0x20 ' ' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 33 0x21 '!' */ - 0x00, /* 0000000 */ - 0x18, /* 0001100 */ - 0x3c, /* 0011110 */ - 0x3c, /* 0011110 */ - 0x3c, /* 0011110 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x00, /* 0000000 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 34 0x22 '"' */ - 0x00, /* 0000000 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x28, /* 0010100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 35 0x23 '#' */ - 0x00, /* 0000000 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 36 0x24 '$' */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x78, /* 0111100 */ - 0xcc, /* 1100110 */ - 0xc4, /* 1100010 */ - 0xc0, /* 1100000 */ - 0x78, /* 0111100 */ - 0x0c, /* 0000110 */ - 0x8c, /* 1000110 */ - 0xcc, /* 1100110 */ - 0x78, /* 0111100 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x00, /* 0000000 */ - - /* 37 0x25 '%' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0xc0, /* 1100000 */ - 0xc4, /* 1100010 */ - 0x0c, /* 0000110 */ - 0x18, /* 0001100 */ - 0x30, /* 0011000 */ - 0x60, /* 0110000 */ - 0xcc, /* 1100110 */ - 0x8c, /* 1000110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 38 0x26 '&' */ - 0x00, /* 0000000 */ - 0x38, /* 0011100 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x38, /* 0011100 */ - 0x78, /* 0111100 */ - 0xde, /* 1101111 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xdc, /* 1101110 */ - 0x76, /* 0111011 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 39 0x27 ''' */ - 0x00, /* 0000000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x60, /* 0110000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 40 0x28 '(' */ - 0x00, /* 0000000 */ - 0x0c, /* 0000110 */ - 0x18, /* 0001100 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x18, /* 0001100 */ - 0x0c, /* 0000110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 41 0x29 ')' */ - 0x00, /* 0000000 */ - 0x30, /* 0011000 */ - 0x18, /* 0001100 */ - 0x0c, /* 0000110 */ - 0x0c, /* 0000110 */ - 0x0c, /* 0000110 */ - 0x0c, /* 0000110 */ - 0x0c, /* 0000110 */ - 0x0c, /* 0000110 */ - 0x18, /* 0001100 */ - 0x30, /* 0011000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 42 0x2a '*' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x6c, /* 0110110 */ - 0x38, /* 0011100 */ - 0xfe, /* 1111111 */ - 0x38, /* 0011100 */ - 0x6c, /* 0110110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 43 0x2b '+' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x10, /* 0001000 */ - 0x10, /* 0001000 */ - 0x7c, /* 0111110 */ - 0x10, /* 0001000 */ - 0x10, /* 0001000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 44 0x2c ',' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x30, /* 0011000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 45 0x2d '-' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0xfc, /* 1111110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 46 0x2e '.' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 47 0x2f '/' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x04, /* 0000010 */ - 0x0c, /* 0000110 */ - 0x18, /* 0001100 */ - 0x30, /* 0011000 */ - 0x60, /* 0110000 */ - 0xc0, /* 1100000 */ - 0x80, /* 1000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 48 0x30 '0' */ - 0x00, /* 0000000 */ - 0x30, /* 0011000 */ - 0x78, /* 0111100 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xdc, /* 1101110 */ - 0xec, /* 1110110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x78, /* 0111100 */ - 0x30, /* 0011000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 49 0x31 '1' */ - 0x00, /* 0000000 */ - 0x18, /* 0001100 */ - 0x38, /* 0011100 */ - 0x78, /* 0111100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x7c, /* 0111110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 50 0x32 '2' */ - 0x00, /* 0000000 */ - 0x78, /* 0111100 */ - 0xcc, /* 1100110 */ - 0x0c, /* 0000110 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x30, /* 0011000 */ - 0x60, /* 0110000 */ - 0xc0, /* 1100000 */ - 0xcc, /* 1100110 */ - 0xfc, /* 1111110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 51 0x33 '3' */ - 0x00, /* 0000000 */ - 0x78, /* 0111100 */ - 0xcc, /* 1100110 */ - 0x0c, /* 0000110 */ - 0x0c, /* 0000110 */ - 0x38, /* 0011100 */ - 0x0c, /* 0000110 */ - 0x0c, /* 0000110 */ - 0x0c, /* 0000110 */ - 0xcc, /* 1100110 */ - 0x78, /* 0111100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 52 0x34 '4' */ - 0x00, /* 0000000 */ - 0x0c, /* 0000110 */ - 0x1c, /* 0001110 */ - 0x3c, /* 0011110 */ - 0x6c, /* 0110110 */ - 0xcc, /* 1100110 */ - 0xfe, /* 1111111 */ - 0x0c, /* 0000110 */ - 0x0c, /* 0000110 */ - 0x0c, /* 0000110 */ - 0x0c, /* 0000110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 53 0x35 '5' */ - 0x00, /* 0000000 */ - 0xfc, /* 1111110 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xf8, /* 1111100 */ - 0x0c, /* 0000110 */ - 0x0c, /* 0000110 */ - 0x0c, /* 0000110 */ - 0xcc, /* 1100110 */ - 0x78, /* 0111100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 54 0x36 '6' */ - 0x00, /* 0000000 */ - 0x30, /* 0011000 */ - 0x60, /* 0110000 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xf8, /* 1111100 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x78, /* 0111100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 55 0x37 '7' */ - 0x00, /* 0000000 */ - 0xfc, /* 1111110 */ - 0xcc, /* 1100110 */ - 0x0c, /* 0000110 */ - 0x0c, /* 0000110 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 56 0x38 '8' */ - 0x00, /* 0000000 */ - 0x78, /* 0111100 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x78, /* 0111100 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x78, /* 0111100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 57 0x39 '9' */ - 0x00, /* 0000000 */ - 0x78, /* 0111100 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x7c, /* 0111110 */ - 0x0c, /* 0000110 */ - 0x0c, /* 0000110 */ - 0x0c, /* 0000110 */ - 0x18, /* 0001100 */ - 0x70, /* 0111000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 58 0x3a ':' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 59 0x3b ';' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x30, /* 0011000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 60 0x3c '<' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x04, /* 0000010 */ - 0x0c, /* 0000110 */ - 0x18, /* 0001100 */ - 0x30, /* 0011000 */ - 0x60, /* 0110000 */ - 0x30, /* 0011000 */ - 0x18, /* 0001100 */ - 0x0c, /* 0000110 */ - 0x04, /* 0000010 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 61 0x3d '=' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x7c, /* 0111110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x7c, /* 0111110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 62 0x3e '>' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x40, /* 0100000 */ - 0x60, /* 0110000 */ - 0x30, /* 0011000 */ - 0x18, /* 0001100 */ - 0x0c, /* 0000110 */ - 0x18, /* 0001100 */ - 0x30, /* 0011000 */ - 0x60, /* 0110000 */ - 0x40, /* 0100000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 63 0x3f '?' */ - 0x00, /* 0000000 */ - 0x78, /* 0111100 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x18, /* 0001100 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x00, /* 0000000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 64 0x40 '@' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x78, /* 0111100 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xdc, /* 1101110 */ - 0xdc, /* 1101110 */ - 0xd8, /* 1101100 */ - 0xc0, /* 1100000 */ - 0x78, /* 0111100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 65 0x41 'A' */ - 0x00, /* 0000000 */ - 0x30, /* 0011000 */ - 0x78, /* 0111100 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xfc, /* 1111110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 66 0x42 'B' */ - 0x00, /* 0000000 */ - 0xf8, /* 1111100 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x78, /* 0111100 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0xf8, /* 1111100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 67 0x43 'C' */ - 0x00, /* 0000000 */ - 0x38, /* 0011100 */ - 0x6c, /* 0110110 */ - 0xc4, /* 1100010 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xc4, /* 1100010 */ - 0x6c, /* 0110110 */ - 0x38, /* 0011100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 68 0x44 'D' */ - 0x00, /* 0000000 */ - 0xf0, /* 1111000 */ - 0xd8, /* 1101100 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xd8, /* 1101100 */ - 0xf0, /* 1111000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 69 0x45 'E' */ - 0x00, /* 0000000 */ - 0x7c, /* 0111110 */ - 0x6c, /* 0110110 */ - 0x64, /* 0110010 */ - 0x68, /* 0110100 */ - 0x78, /* 0111100 */ - 0x68, /* 0110100 */ - 0x60, /* 0110000 */ - 0x64, /* 0110010 */ - 0x6c, /* 0110110 */ - 0x7c, /* 0111110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 70 0x46 'F' */ - 0x00, /* 0000000 */ - 0x7c, /* 0111110 */ - 0x64, /* 0110010 */ - 0x60, /* 0110000 */ - 0x68, /* 0110100 */ - 0x78, /* 0111100 */ - 0x68, /* 0110100 */ - 0x60, /* 0110000 */ - 0x60, /* 0110000 */ - 0x60, /* 0110000 */ - 0x70, /* 0111000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 71 0x47 'G' */ - 0x00, /* 0000000 */ - 0x38, /* 0011100 */ - 0x6c, /* 0110110 */ - 0xc4, /* 1100010 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xdc, /* 1101110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x6c, /* 0110110 */ - 0x34, /* 0011010 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 72 0x48 'H' */ - 0x00, /* 0000000 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xfc, /* 1111110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 73 0x49 'I' */ - 0x00, /* 0000000 */ - 0x3c, /* 0011110 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x3c, /* 0011110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 74 0x4a 'J' */ - 0x00, /* 0000000 */ - 0x1c, /* 0001110 */ - 0x0c, /* 0000110 */ - 0x0c, /* 0000110 */ - 0x0c, /* 0000110 */ - 0x0c, /* 0000110 */ - 0x0c, /* 0000110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x78, /* 0111100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 75 0x4b 'K' */ - 0x00, /* 0000000 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xd8, /* 1101100 */ - 0xf0, /* 1111000 */ - 0xf0, /* 1111000 */ - 0xd8, /* 1101100 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 76 0x4c 'L' */ - 0x00, /* 0000000 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xc4, /* 1100010 */ - 0xcc, /* 1100110 */ - 0xfc, /* 1111110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 77 0x4d 'M' */ - 0x00, /* 0000000 */ - 0xc6, /* 1100011 */ - 0xee, /* 1110111 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0xd6, /* 1101011 */ - 0xc6, /* 1100011 */ - 0xc6, /* 1100011 */ - 0xc6, /* 1100011 */ - 0xc6, /* 1100011 */ - 0xc6, /* 1100011 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 78 0x4e 'N' */ - 0x00, /* 0000000 */ - 0xcc, /* 1100110 */ - 0xec, /* 1110110 */ - 0xec, /* 1110110 */ - 0xfc, /* 1111110 */ - 0xdc, /* 1101110 */ - 0xdc, /* 1101110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 79 0x4f 'O' */ - 0x00, /* 0000000 */ - 0x78, /* 0111100 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x78, /* 0111100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 80 0x50 'P' */ - 0x00, /* 0000000 */ - 0xf8, /* 1111100 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xf8, /* 1111100 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 81 0x51 'Q' */ - 0x00, /* 0000000 */ - 0x78, /* 0111100 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xdc, /* 1101110 */ - 0x78, /* 0111100 */ - 0x18, /* 0001100 */ - 0x1c, /* 0001110 */ - 0x00, /* 0000000 */ - - /* 82 0x52 'R' */ - 0x00, /* 0000000 */ - 0xf8, /* 1111100 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xf8, /* 1111100 */ - 0xd8, /* 1101100 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 83 0x53 'S' */ - 0x00, /* 0000000 */ - 0x7c, /* 0111110 */ - 0xc4, /* 1100010 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0x60, /* 0110000 */ - 0x38, /* 0011100 */ - 0x0c, /* 0000110 */ - 0x0c, /* 0000110 */ - 0x8c, /* 1000110 */ - 0xf8, /* 1111100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 84 0x54 'T' */ - 0x00, /* 0000000 */ - 0xfc, /* 1111110 */ - 0xfc, /* 1111110 */ - 0xb4, /* 1011010 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x78, /* 0111100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 85 0x55 'U' */ - 0x00, /* 0000000 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x78, /* 0111100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 86 0x56 'V' */ - 0x00, /* 0000000 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x78, /* 0111100 */ - 0x78, /* 0111100 */ - 0x30, /* 0011000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 87 0x57 'W' */ - 0x00, /* 0000000 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xfc, /* 1111110 */ - 0xfc, /* 1111110 */ - 0x48, /* 0100100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 88 0x58 'X' */ - 0x00, /* 0000000 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x78, /* 0111100 */ - 0x78, /* 0111100 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x78, /* 0111100 */ - 0x78, /* 0111100 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 89 0x59 'Y' */ - 0x00, /* 0000000 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x78, /* 0111100 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 90 0x5a 'Z' */ - 0x00, /* 0000000 */ - 0xfc, /* 1111110 */ - 0xcc, /* 1100110 */ - 0x8c, /* 1000110 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x30, /* 0011000 */ - 0x60, /* 0110000 */ - 0xc4, /* 1100010 */ - 0xcc, /* 1100110 */ - 0xfc, /* 1111110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 91 0x5b '[' */ - 0x00, /* 0000000 */ - 0x78, /* 0111100 */ - 0x60, /* 0110000 */ - 0x60, /* 0110000 */ - 0x60, /* 0110000 */ - 0x60, /* 0110000 */ - 0x60, /* 0110000 */ - 0x60, /* 0110000 */ - 0x60, /* 0110000 */ - 0x60, /* 0110000 */ - 0x78, /* 0111100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 92 0x5c '\' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x80, /* 1000000 */ - 0xc0, /* 1100000 */ - 0xe0, /* 1110000 */ - 0x70, /* 0111000 */ - 0x38, /* 0011100 */ - 0x1c, /* 0001110 */ - 0x0c, /* 0000110 */ - 0x04, /* 0000010 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 93 0x5d ']' */ - 0x00, /* 0000000 */ - 0x78, /* 0111100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x78, /* 0111100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 94 0x5e '^' */ - 0x10, /* 0001000 */ - 0x38, /* 0011100 */ - 0x6c, /* 0110110 */ - 0xc6, /* 1100011 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 95 0x5f '_' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0xfe, /* 1111111 */ - 0x00, /* 0000000 */ - - /* 96 0x60 '`' */ - 0x00, /* 0000000 */ - 0x60, /* 0110000 */ - 0x30, /* 0011000 */ - 0x18, /* 0001100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 97 0x61 'a' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x78, /* 0111100 */ - 0x0c, /* 0000110 */ - 0x7c, /* 0111110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x76, /* 0111011 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 98 0x62 'b' */ - 0x00, /* 0000000 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xf0, /* 1111000 */ - 0xd8, /* 1101100 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xf8, /* 1111100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 99 0x63 'c' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x78, /* 0111100 */ - 0xcc, /* 1100110 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xcc, /* 1100110 */ - 0x78, /* 0111100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 100 0x64 'd' */ - 0x00, /* 0000000 */ - 0x1c, /* 0001110 */ - 0x0c, /* 0000110 */ - 0x0c, /* 0000110 */ - 0x3c, /* 0011110 */ - 0x6c, /* 0110110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x76, /* 0111011 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 101 0x65 'e' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x78, /* 0111100 */ - 0xcc, /* 1100110 */ - 0xfc, /* 1111110 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xcc, /* 1100110 */ - 0x78, /* 0111100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 102 0x66 'f' */ - 0x00, /* 0000000 */ - 0x38, /* 0011100 */ - 0x6c, /* 0110110 */ - 0x64, /* 0110010 */ - 0x60, /* 0110000 */ - 0xf0, /* 1111000 */ - 0x60, /* 0110000 */ - 0x60, /* 0110000 */ - 0x60, /* 0110000 */ - 0x60, /* 0110000 */ - 0xf0, /* 1111000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 103 0x67 'g' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x76, /* 0111011 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x7c, /* 0111110 */ - 0x0c, /* 0000110 */ - 0xcc, /* 1100110 */ - 0x78, /* 0111100 */ - - /* 104 0x68 'h' */ - 0x00, /* 0000000 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xd8, /* 1101100 */ - 0xec, /* 1110110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 105 0x69 'i' */ - 0x00, /* 0000000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x00, /* 0000000 */ - 0x70, /* 0111000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x78, /* 0111100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 106 0x6a 'j' */ - 0x00, /* 0000000 */ - 0x0c, /* 0000110 */ - 0x0c, /* 0000110 */ - 0x00, /* 0000000 */ - 0x1c, /* 0001110 */ - 0x0c, /* 0000110 */ - 0x0c, /* 0000110 */ - 0x0c, /* 0000110 */ - 0x0c, /* 0000110 */ - 0x0c, /* 0000110 */ - 0x0c, /* 0000110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x78, /* 0111100 */ - - /* 107 0x6b 'k' */ - 0x00, /* 0000000 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xcc, /* 1100110 */ - 0xd8, /* 1101100 */ - 0xf0, /* 1111000 */ - 0xf0, /* 1111000 */ - 0xd8, /* 1101100 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 108 0x6c 'l' */ - 0x00, /* 0000000 */ - 0x70, /* 0111000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x78, /* 0111100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 109 0x6d 'm' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0xec, /* 1110110 */ - 0xfe, /* 1111111 */ - 0xd6, /* 1101011 */ - 0xd6, /* 1101011 */ - 0xd6, /* 1101011 */ - 0xd6, /* 1101011 */ - 0xd6, /* 1101011 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 110 0x6e 'n' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0xb8, /* 1011100 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 111 0x6f 'o' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x78, /* 0111100 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x78, /* 0111100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 112 0x70 'p' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0xb8, /* 1011100 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xf8, /* 1111100 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - - /* 113 0x71 'q' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x74, /* 0111010 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x7c, /* 0111110 */ - 0x0c, /* 0000110 */ - 0x0c, /* 0000110 */ - 0x0c, /* 0000110 */ - - /* 114 0x72 'r' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0xb8, /* 1011100 */ - 0xec, /* 1110110 */ - 0xcc, /* 1100110 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 115 0x73 's' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x78, /* 0111100 */ - 0xcc, /* 1100110 */ - 0x60, /* 0110000 */ - 0x30, /* 0011000 */ - 0x18, /* 0001100 */ - 0xcc, /* 1100110 */ - 0x78, /* 0111100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 116 0x74 't' */ - 0x00, /* 0000000 */ - 0x10, /* 0001000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0xfc, /* 1111110 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x36, /* 0011011 */ - 0x1c, /* 0001110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 117 0x75 'u' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x76, /* 0111011 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 118 0x76 'v' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x78, /* 0111100 */ - 0x30, /* 0011000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 119 0x77 'w' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0xc6, /* 1100011 */ - 0xc6, /* 1100011 */ - 0xd6, /* 1101011 */ - 0xd6, /* 1101011 */ - 0xd6, /* 1101011 */ - 0xfe, /* 1111111 */ - 0x6c, /* 0110110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 120 0x78 'x' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x78, /* 0111100 */ - 0x30, /* 0011000 */ - 0x78, /* 0111100 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 121 0x79 'y' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x7c, /* 0111110 */ - 0x0c, /* 0000110 */ - 0x18, /* 0001100 */ - 0xf0, /* 1111000 */ - - /* 122 0x7a 'z' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0xfc, /* 1111110 */ - 0xcc, /* 1100110 */ - 0x18, /* 0001100 */ - 0x30, /* 0011000 */ - 0x60, /* 0110000 */ - 0xcc, /* 1100110 */ - 0xfc, /* 1111110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 123 0x7b '{' */ - 0x00, /* 0000000 */ - 0x1c, /* 0001110 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0xe0, /* 1110000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x1c, /* 0001110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 124 0x7c '|' */ - 0x00, /* 0000000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 125 0x7d '}' */ - 0x00, /* 0000000 */ - 0x70, /* 0111000 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x0e, /* 0000111 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x70, /* 0111000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 126 0x7e '~' */ - 0x00, /* 0000000 */ - 0xec, /* 1110110 */ - 0xb8, /* 1011100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 127 0x7f '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x10, /* 0001000 */ - 0x38, /* 0011100 */ - 0x6c, /* 0110110 */ - 0xc6, /* 1100011 */ - 0xc6, /* 1100011 */ - 0xc6, /* 1100011 */ - 0xfe, /* 1111111 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 128 0x80 '' */ - 0x00, /* 0000000 */ - 0x38, /* 0011100 */ - 0x6c, /* 0110110 */ - 0xc4, /* 1100010 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xc4, /* 1100010 */ - 0x6c, /* 0110110 */ - 0x38, /* 0011100 */ - 0x18, /* 0001100 */ - 0x70, /* 0111000 */ - 0x00, /* 0000000 */ - - /* 129 0x81 '' */ - 0x00, /* 0000000 */ - 0xcc, /* 1100110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x76, /* 0111011 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 130 0x82 '' */ - 0x0c, /* 0000110 */ - 0x18, /* 0001100 */ - 0x30, /* 0011000 */ - 0x00, /* 0000000 */ - 0x78, /* 0111100 */ - 0xcc, /* 1100110 */ - 0xfc, /* 1111110 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xcc, /* 1100110 */ - 0x78, /* 0111100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 131 0x83 '' */ - 0x10, /* 0001000 */ - 0x38, /* 0011100 */ - 0x6c, /* 0110110 */ - 0x00, /* 0000000 */ - 0x78, /* 0111100 */ - 0x0c, /* 0000110 */ - 0x7c, /* 0111110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x76, /* 0111011 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 132 0x84 '' */ - 0x00, /* 0000000 */ - 0xcc, /* 1100110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x78, /* 0111100 */ - 0x0c, /* 0000110 */ - 0x7c, /* 0111110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x76, /* 0111011 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 133 0x85 '' */ - 0x60, /* 0110000 */ - 0x30, /* 0011000 */ - 0x18, /* 0001100 */ - 0x00, /* 0000000 */ - 0x78, /* 0111100 */ - 0x0c, /* 0000110 */ - 0x7c, /* 0111110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x76, /* 0111011 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 134 0x86 '' */ - 0x38, /* 0011100 */ - 0x6c, /* 0110110 */ - 0x38, /* 0011100 */ - 0x00, /* 0000000 */ - 0x78, /* 0111100 */ - 0x0c, /* 0000110 */ - 0x7c, /* 0111110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x76, /* 0111011 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 135 0x87 '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x78, /* 0111100 */ - 0xcc, /* 1100110 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xcc, /* 1100110 */ - 0x78, /* 0111100 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0xe0, /* 1110000 */ - - /* 136 0x88 '' */ - 0x10, /* 0001000 */ - 0x38, /* 0011100 */ - 0x6c, /* 0110110 */ - 0x00, /* 0000000 */ - 0x78, /* 0111100 */ - 0xcc, /* 1100110 */ - 0xfc, /* 1111110 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xcc, /* 1100110 */ - 0x78, /* 0111100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 137 0x89 '' */ - 0x00, /* 0000000 */ - 0xcc, /* 1100110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x78, /* 0111100 */ - 0xcc, /* 1100110 */ - 0xfc, /* 1111110 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xcc, /* 1100110 */ - 0x78, /* 0111100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 138 0x8a '' */ - 0xc0, /* 1100000 */ - 0x60, /* 0110000 */ - 0x30, /* 0011000 */ - 0x00, /* 0000000 */ - 0x78, /* 0111100 */ - 0xcc, /* 1100110 */ - 0xfc, /* 1111110 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xcc, /* 1100110 */ - 0x78, /* 0111100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 139 0x8b '' */ - 0x00, /* 0000000 */ - 0x6c, /* 0110110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x38, /* 0011100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x3c, /* 0011110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 140 0x8c '' */ - 0x30, /* 0011000 */ - 0x78, /* 0111100 */ - 0xcc, /* 1100110 */ - 0x00, /* 0000000 */ - 0x70, /* 0111000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x78, /* 0111100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 141 0x8d '' */ - 0xc0, /* 1100000 */ - 0x60, /* 0110000 */ - 0x30, /* 0011000 */ - 0x00, /* 0000000 */ - 0x70, /* 0111000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x78, /* 0111100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 142 0x8e '' */ - 0x00, /* 0000000 */ - 0xcc, /* 1100110 */ - 0x00, /* 0000000 */ - 0x30, /* 0011000 */ - 0x78, /* 0111100 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xfc, /* 1111110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 143 0x8f '' */ - 0x30, /* 0011000 */ - 0x48, /* 0100100 */ - 0x48, /* 0100100 */ - 0x30, /* 0011000 */ - 0x78, /* 0111100 */ - 0xcc, /* 1100110 */ - 0xfc, /* 1111110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 144 0x90 '' */ - 0x18, /* 0001100 */ - 0x30, /* 0011000 */ - 0xfc, /* 1111110 */ - 0xcc, /* 1100110 */ - 0xc4, /* 1100010 */ - 0xd0, /* 1101000 */ - 0xf0, /* 1111000 */ - 0xd0, /* 1101000 */ - 0xc4, /* 1100010 */ - 0xcc, /* 1100110 */ - 0xfc, /* 1111110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 145 0x91 '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0xec, /* 1110110 */ - 0x36, /* 0011011 */ - 0x36, /* 0011011 */ - 0x7e, /* 0111111 */ - 0xd8, /* 1101100 */ - 0xd8, /* 1101100 */ - 0x6e, /* 0110111 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 146 0x92 '' */ - 0x00, /* 0000000 */ - 0x3e, /* 0011111 */ - 0x6c, /* 0110110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xfe, /* 1111111 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xce, /* 1100111 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 147 0x93 '' */ - 0x10, /* 0001000 */ - 0x38, /* 0011100 */ - 0x6c, /* 0110110 */ - 0x00, /* 0000000 */ - 0x78, /* 0111100 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x78, /* 0111100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 148 0x94 '' */ - 0x00, /* 0000000 */ - 0xcc, /* 1100110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x78, /* 0111100 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x78, /* 0111100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 149 0x95 '' */ - 0xc0, /* 1100000 */ - 0x60, /* 0110000 */ - 0x30, /* 0011000 */ - 0x00, /* 0000000 */ - 0x78, /* 0111100 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x78, /* 0111100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 150 0x96 '' */ - 0x30, /* 0011000 */ - 0x78, /* 0111100 */ - 0xcc, /* 1100110 */ - 0x00, /* 0000000 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x76, /* 0111011 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 151 0x97 '' */ - 0x60, /* 0110000 */ - 0x30, /* 0011000 */ - 0x18, /* 0001100 */ - 0x00, /* 0000000 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x76, /* 0111011 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 152 0x98 '' */ - 0x00, /* 0000000 */ - 0xcc, /* 1100110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x7c, /* 0111110 */ - 0x0c, /* 0000110 */ - 0x18, /* 0001100 */ - 0x70, /* 0111000 */ - - /* 153 0x99 '' */ - 0xcc, /* 1100110 */ - 0x00, /* 0000000 */ - 0x78, /* 0111100 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x78, /* 0111100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 154 0x9a '' */ - 0xcc, /* 1100110 */ - 0x00, /* 0000000 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x78, /* 0111100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 155 0x9b '' */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x7c, /* 0111110 */ - 0xcc, /* 1100110 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xcc, /* 1100110 */ - 0x7c, /* 0111110 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 156 0x9c '' */ - 0x38, /* 0011100 */ - 0x6c, /* 0110110 */ - 0x64, /* 0110010 */ - 0x60, /* 0110000 */ - 0xf0, /* 1111000 */ - 0x60, /* 0110000 */ - 0x60, /* 0110000 */ - 0x60, /* 0110000 */ - 0x60, /* 0110000 */ - 0xe6, /* 1110011 */ - 0xfc, /* 1111110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 157 0x9d '' */ - 0x00, /* 0000000 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x78, /* 0111100 */ - 0x30, /* 0011000 */ - 0xfc, /* 1111110 */ - 0x30, /* 0011000 */ - 0xfc, /* 1111110 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 158 0x9e '' */ - 0xf8, /* 1111100 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xf8, /* 1111100 */ - 0xc4, /* 1100010 */ - 0xcc, /* 1100110 */ - 0xde, /* 1101111 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xc6, /* 1100011 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 159 0x9f '' */ - 0x1c, /* 0001110 */ - 0x36, /* 0011011 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0xfc, /* 1111110 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0xb0, /* 1011000 */ - 0xe0, /* 1110000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 160 0xa0 '' */ - 0x18, /* 0001100 */ - 0x30, /* 0011000 */ - 0x60, /* 0110000 */ - 0x00, /* 0000000 */ - 0x78, /* 0111100 */ - 0x0c, /* 0000110 */ - 0x7c, /* 0111110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x76, /* 0111011 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 161 0xa1 '' */ - 0x18, /* 0001100 */ - 0x30, /* 0011000 */ - 0x60, /* 0110000 */ - 0x00, /* 0000000 */ - 0x70, /* 0111000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x78, /* 0111100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 162 0xa2 '' */ - 0x18, /* 0001100 */ - 0x30, /* 0011000 */ - 0x60, /* 0110000 */ - 0x00, /* 0000000 */ - 0x78, /* 0111100 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x78, /* 0111100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 163 0xa3 '' */ - 0x18, /* 0001100 */ - 0x30, /* 0011000 */ - 0x60, /* 0110000 */ - 0x00, /* 0000000 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x76, /* 0111011 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 164 0xa4 '' */ - 0x00, /* 0000000 */ - 0x76, /* 0111011 */ - 0xdc, /* 1101110 */ - 0x00, /* 0000000 */ - 0xb8, /* 1011100 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 165 0xa5 '' */ - 0x76, /* 0111011 */ - 0xdc, /* 1101110 */ - 0x00, /* 0000000 */ - 0xcc, /* 1100110 */ - 0xec, /* 1110110 */ - 0xec, /* 1110110 */ - 0xfc, /* 1111110 */ - 0xdc, /* 1101110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 166 0xa6 '' */ - 0x00, /* 0000000 */ - 0x78, /* 0111100 */ - 0xd8, /* 1101100 */ - 0xd8, /* 1101100 */ - 0x7c, /* 0111110 */ - 0x00, /* 0000000 */ - 0xfc, /* 1111110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 167 0xa7 '' */ - 0x00, /* 0000000 */ - 0x70, /* 0111000 */ - 0xd8, /* 1101100 */ - 0xd8, /* 1101100 */ - 0x70, /* 0111000 */ - 0x00, /* 0000000 */ - 0xf8, /* 1111100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 168 0xa8 '' */ - 0x00, /* 0000000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x00, /* 0000000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x60, /* 0110000 */ - 0xc0, /* 1100000 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x78, /* 0111100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 169 0xa9 '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0xfc, /* 1111110 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 170 0xaa '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0xfc, /* 1111110 */ - 0x0c, /* 0000110 */ - 0x0c, /* 0000110 */ - 0x0c, /* 0000110 */ - 0x0c, /* 0000110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 171 0xab '' */ - 0x60, /* 0110000 */ - 0xe0, /* 1110000 */ - 0x62, /* 0110001 */ - 0x66, /* 0110011 */ - 0x6c, /* 0110110 */ - 0x18, /* 0001100 */ - 0x30, /* 0011000 */ - 0x60, /* 0110000 */ - 0xc0, /* 1100000 */ - 0xb8, /* 1011100 */ - 0x4c, /* 0100110 */ - 0x18, /* 0001100 */ - 0x30, /* 0011000 */ - 0x7c, /* 0111110 */ - - /* 172 0xac '' */ - 0x60, /* 0110000 */ - 0xe0, /* 1110000 */ - 0x62, /* 0110001 */ - 0x66, /* 0110011 */ - 0x6c, /* 0110110 */ - 0x18, /* 0001100 */ - 0x30, /* 0011000 */ - 0x6c, /* 0110110 */ - 0xdc, /* 1101110 */ - 0xb4, /* 1011010 */ - 0x7e, /* 0111111 */ - 0x0c, /* 0000110 */ - 0x0c, /* 0000110 */ - 0x00, /* 0000000 */ - - /* 173 0xad '' */ - 0x00, /* 0000000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x00, /* 0000000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x78, /* 0111100 */ - 0x78, /* 0111100 */ - 0x78, /* 0111100 */ - 0x30, /* 0011000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 174 0xae '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x36, /* 0011011 */ - 0x6c, /* 0110110 */ - 0xd8, /* 1101100 */ - 0x6c, /* 0110110 */ - 0x36, /* 0011011 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 175 0xaf '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0xd8, /* 1101100 */ - 0x6c, /* 0110110 */ - 0x36, /* 0011011 */ - 0x6c, /* 0110110 */ - 0xd8, /* 1101100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 176 0xb0 '' */ - 0x88, /* 1000100 */ - 0x22, /* 0010001 */ - 0x88, /* 1000100 */ - 0x22, /* 0010001 */ - 0x88, /* 1000100 */ - 0x22, /* 0010001 */ - 0x88, /* 1000100 */ - 0x22, /* 0010001 */ - 0x88, /* 1000100 */ - 0x22, /* 0010001 */ - 0x88, /* 1000100 */ - 0x22, /* 0010001 */ - 0x88, /* 1000100 */ - 0x22, /* 0010001 */ - - /* 177 0xb1 '' */ - 0x54, /* 0101010 */ - 0xaa, /* 1010101 */ - 0x54, /* 0101010 */ - 0xaa, /* 1010101 */ - 0x54, /* 0101010 */ - 0xaa, /* 1010101 */ - 0x54, /* 0101010 */ - 0xaa, /* 1010101 */ - 0x54, /* 0101010 */ - 0xaa, /* 1010101 */ - 0x54, /* 0101010 */ - 0xaa, /* 1010101 */ - 0x54, /* 0101010 */ - 0xaa, /* 1010101 */ - - /* 178 0xb2 '' */ - 0xee, /* 1110111 */ - 0xba, /* 1011101 */ - 0xee, /* 1110111 */ - 0xba, /* 1011101 */ - 0xee, /* 1110111 */ - 0xba, /* 1011101 */ - 0xee, /* 1110111 */ - 0xba, /* 1011101 */ - 0xee, /* 1110111 */ - 0xba, /* 1011101 */ - 0xee, /* 1110111 */ - 0xba, /* 1011101 */ - 0xee, /* 1110111 */ - 0xba, /* 1011101 */ - - /* 179 0xb3 '' */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - - /* 180 0xb4 '' */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0xf0, /* 1111000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - - /* 181 0xb5 '' */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0xf0, /* 1111000 */ - 0x30, /* 0011000 */ - 0xf0, /* 1111000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - - /* 182 0xb6 '' */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0xec, /* 1110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - - /* 183 0xb7 '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0xfc, /* 1111110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - - /* 184 0xb8 '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0xf0, /* 1111000 */ - 0x30, /* 0011000 */ - 0xf0, /* 1111000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - - /* 185 0xb9 '' */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0xec, /* 1110110 */ - 0x0c, /* 0000110 */ - 0xec, /* 1110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - - /* 186 0xba '' */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - - /* 187 0xbb '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0xfc, /* 1111110 */ - 0x0c, /* 0000110 */ - 0xec, /* 1110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - - /* 188 0xbc '' */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0xec, /* 1110110 */ - 0x0c, /* 0000110 */ - 0xfc, /* 1111110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 189 0xbd '' */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0xfc, /* 1111110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 190 0xbe '' */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0xf0, /* 1111000 */ - 0x30, /* 0011000 */ - 0xf0, /* 1111000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 191 0xbf '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0xf0, /* 1111000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - - /* 192 0xc0 '' */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x3e, /* 0011111 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 193 0xc1 '' */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0xfe, /* 1111111 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 194 0xc2 '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0xfe, /* 1111111 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - - /* 195 0xc3 '' */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x3e, /* 0011111 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - - /* 196 0xc4 '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0xfe, /* 1111111 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 197 0xc5 '' */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0xfe, /* 1111111 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - - /* 198 0xc6 '' */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x3e, /* 0011111 */ - 0x30, /* 0011000 */ - 0x3e, /* 0011111 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - - /* 199 0xc7 '' */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6e, /* 0110111 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - - /* 200 0xc8 '' */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6e, /* 0110111 */ - 0x60, /* 0110000 */ - 0x7e, /* 0111111 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 201 0xc9 '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x7e, /* 0111111 */ - 0x60, /* 0110000 */ - 0x6e, /* 0110111 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - - /* 202 0xca '' */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0xee, /* 1110111 */ - 0x00, /* 0000000 */ - 0xfe, /* 1111111 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 203 0xcb '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0xfe, /* 1111111 */ - 0x00, /* 0000000 */ - 0xee, /* 1110111 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - - /* 204 0xcc '' */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6e, /* 0110111 */ - 0x60, /* 0110000 */ - 0x6e, /* 0110111 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - - /* 205 0xcd '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0xfe, /* 1111111 */ - 0x00, /* 0000000 */ - 0xfe, /* 1111111 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 206 0xce '' */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0xee, /* 1110111 */ - 0x00, /* 0000000 */ - 0xee, /* 1110111 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - - /* 207 0xcf '' */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0xfe, /* 1111111 */ - 0x00, /* 0000000 */ - 0xfe, /* 1111111 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 208 0xd0 '' */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0xfe, /* 1111111 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 209 0xd1 '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0xfe, /* 1111111 */ - 0x00, /* 0000000 */ - 0xfe, /* 1111111 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - - /* 210 0xd2 '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0xfe, /* 1111111 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - - /* 211 0xd3 '' */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x7e, /* 0111111 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 212 0xd4 '' */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x3e, /* 0011111 */ - 0x30, /* 0011000 */ - 0x3e, /* 0011111 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 213 0xd5 '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x3e, /* 0011111 */ - 0x30, /* 0011000 */ - 0x3e, /* 0011111 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - - /* 214 0xd6 '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x7e, /* 0111111 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - - /* 215 0xd7 '' */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0xfe, /* 1111111 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - - /* 216 0xd8 '' */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0xfe, /* 1111111 */ - 0x30, /* 0011000 */ - 0xfe, /* 1111111 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - - /* 217 0xd9 '' */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0xf0, /* 1111000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 218 0xda '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x3e, /* 0011111 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - - /* 219 0xdb '' */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - - /* 220 0xdc '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - - /* 221 0xdd '' */ - 0xe0, /* 1110000 */ - 0xe0, /* 1110000 */ - 0xe0, /* 1110000 */ - 0xe0, /* 1110000 */ - 0xe0, /* 1110000 */ - 0xe0, /* 1110000 */ - 0xe0, /* 1110000 */ - 0xe0, /* 1110000 */ - 0xe0, /* 1110000 */ - 0xe0, /* 1110000 */ - 0xe0, /* 1110000 */ - 0xe0, /* 1110000 */ - 0xe0, /* 1110000 */ - 0xe0, /* 1110000 */ - - /* 222 0xde '' */ - 0x1e, /* 0001111 */ - 0x1e, /* 0001111 */ - 0x1e, /* 0001111 */ - 0x1e, /* 0001111 */ - 0x1e, /* 0001111 */ - 0x1e, /* 0001111 */ - 0x1e, /* 0001111 */ - 0x1e, /* 0001111 */ - 0x1e, /* 0001111 */ - 0x1e, /* 0001111 */ - 0x1e, /* 0001111 */ - 0x1e, /* 0001111 */ - 0x1e, /* 0001111 */ - 0x1e, /* 0001111 */ - - /* 223 0xdf '' */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 224 0xe0 '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x76, /* 0111011 */ - 0xdc, /* 1101110 */ - 0xd8, /* 1101100 */ - 0xd8, /* 1101100 */ - 0xd8, /* 1101100 */ - 0xdc, /* 1101110 */ - 0x76, /* 0111011 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 225 0xe1 '' */ - 0x00, /* 0000000 */ - 0x78, /* 0111100 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xd8, /* 1101100 */ - 0xcc, /* 1100110 */ - 0xc6, /* 1100011 */ - 0xc6, /* 1100011 */ - 0xc6, /* 1100011 */ - 0xcc, /* 1100110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 226 0xe2 '' */ - 0x00, /* 0000000 */ - 0xfc, /* 1111110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 227 0xe3 '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0xfe, /* 1111111 */ - 0xfe, /* 1111111 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 228 0xe4 '' */ - 0x00, /* 0000000 */ - 0xfc, /* 1111110 */ - 0xcc, /* 1100110 */ - 0x60, /* 0110000 */ - 0x30, /* 0011000 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x30, /* 0011000 */ - 0x60, /* 0110000 */ - 0xcc, /* 1100110 */ - 0xfc, /* 1111110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 229 0xe5 '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x7e, /* 0111111 */ - 0xd8, /* 1101100 */ - 0xd8, /* 1101100 */ - 0xd8, /* 1101100 */ - 0xd8, /* 1101100 */ - 0xd8, /* 1101100 */ - 0x70, /* 0111000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 230 0xe6 '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xf8, /* 1111100 */ - 0xc0, /* 1100000 */ - 0xc0, /* 1100000 */ - 0x80, /* 1000000 */ - - /* 231 0xe7 '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x76, /* 0111011 */ - 0xdc, /* 1101110 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 232 0xe8 '' */ - 0x00, /* 0000000 */ - 0xfc, /* 1111110 */ - 0x30, /* 0011000 */ - 0x78, /* 0111100 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x78, /* 0111100 */ - 0x30, /* 0011000 */ - 0xfc, /* 1111110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 233 0xe9 '' */ - 0x00, /* 0000000 */ - 0x38, /* 0011100 */ - 0x6c, /* 0110110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xfc, /* 1111110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x6c, /* 0110110 */ - 0x38, /* 0011100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 234 0xea '' */ - 0x00, /* 0000000 */ - 0x38, /* 0011100 */ - 0x6c, /* 0110110 */ - 0xc6, /* 1100011 */ - 0xc6, /* 1100011 */ - 0xc6, /* 1100011 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0xee, /* 1110111 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 235 0xeb '' */ - 0x00, /* 0000000 */ - 0x3c, /* 0011110 */ - 0x60, /* 0110000 */ - 0x30, /* 0011000 */ - 0x18, /* 0001100 */ - 0x7c, /* 0111110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x78, /* 0111100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 236 0xec '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x7c, /* 0111110 */ - 0xd6, /* 1101011 */ - 0xd6, /* 1101011 */ - 0xd6, /* 1101011 */ - 0x7c, /* 0111110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 237 0xed '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x06, /* 0000011 */ - 0x0c, /* 0000110 */ - 0x7c, /* 0111110 */ - 0xd6, /* 1101011 */ - 0xd6, /* 1101011 */ - 0xe6, /* 1110011 */ - 0x7c, /* 0111110 */ - 0x60, /* 0110000 */ - 0xc0, /* 1100000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 238 0xee '' */ - 0x00, /* 0000000 */ - 0x1c, /* 0001110 */ - 0x30, /* 0011000 */ - 0x60, /* 0110000 */ - 0x60, /* 0110000 */ - 0x7c, /* 0111110 */ - 0x60, /* 0110000 */ - 0x60, /* 0110000 */ - 0x60, /* 0110000 */ - 0x30, /* 0011000 */ - 0x1c, /* 0001110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 239 0xef '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x78, /* 0111100 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0xcc, /* 1100110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 240 0xf0 '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0xfc, /* 1111110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0xfc, /* 1111110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0xfc, /* 1111110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 241 0xf1 '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0xfc, /* 1111110 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0xfc, /* 1111110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 242 0xf2 '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x60, /* 0110000 */ - 0x30, /* 0011000 */ - 0x18, /* 0001100 */ - 0x0c, /* 0000110 */ - 0x18, /* 0001100 */ - 0x30, /* 0011000 */ - 0x60, /* 0110000 */ - 0x00, /* 0000000 */ - 0xfc, /* 1111110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 243 0xf3 '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x18, /* 0001100 */ - 0x30, /* 0011000 */ - 0x60, /* 0110000 */ - 0xc0, /* 1100000 */ - 0x60, /* 0110000 */ - 0x30, /* 0011000 */ - 0x18, /* 0001100 */ - 0x00, /* 0000000 */ - 0xfc, /* 1111110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 244 0xf4 '' */ - 0x00, /* 0000000 */ - 0x1c, /* 0001110 */ - 0x36, /* 0011011 */ - 0x36, /* 0011011 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - - /* 245 0xf5 '' */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0xd8, /* 1101100 */ - 0xd8, /* 1101100 */ - 0xd8, /* 1101100 */ - 0x70, /* 0111000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 246 0xf6 '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x30, /* 0011000 */ - 0x00, /* 0000000 */ - 0xfc, /* 1111110 */ - 0x00, /* 0000000 */ - 0x30, /* 0011000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 247 0xf7 '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x76, /* 0111011 */ - 0xdc, /* 1101110 */ - 0x00, /* 0000000 */ - 0x76, /* 0111011 */ - 0xdc, /* 1101110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 248 0xf8 '' */ - 0x38, /* 0011100 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x38, /* 0011100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 249 0xf9 '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x30, /* 0011000 */ - 0x30, /* 0011000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 250 0xfa '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x30, /* 0011000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 251 0xfb '' */ - 0x1e, /* 0001111 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0x18, /* 0001100 */ - 0xd8, /* 1101100 */ - 0xd8, /* 1101100 */ - 0xd8, /* 1101100 */ - 0x78, /* 0111100 */ - 0x38, /* 0011100 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 252 0xfc '' */ - 0xd8, /* 1101100 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x6c, /* 0110110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 253 0xfd '' */ - 0x78, /* 0111100 */ - 0xcc, /* 1100110 */ - 0x18, /* 0001100 */ - 0x30, /* 0011000 */ - 0x64, /* 0110010 */ - 0xfc, /* 1111110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 254 0xfe '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x7c, /* 0111110 */ - 0x7c, /* 0111110 */ - 0x7c, /* 0111110 */ - 0x7c, /* 0111110 */ - 0x7c, /* 0111110 */ - 0x7c, /* 0111110 */ - 0x7c, /* 0111110 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - - /* 255 0xff '' */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - 0x00, /* 0000000 */ - -}; - - -const struct font_desc font_7x14 = { - .idx = FONT7x14_IDX, - .name = "7x14", - .width = 7, - .height = 14, - .data = fontdata_7x14, - .pref = 0, -}; diff --git a/drivers/video/console/font_8x16.c b/drivers/video/console/font_8x16.c deleted file mode 100644 index 00a0c67a5c7d..000000000000 --- a/drivers/video/console/font_8x16.c +++ /dev/null @@ -1,4633 +0,0 @@ -/**********************************************/ -/* */ -/* Font file generated by cpi2fnt */ -/* */ -/**********************************************/ - -#include -#include - -#define FONTDATAMAX 4096 - -static const unsigned char fontdata_8x16[FONTDATAMAX] = { - - /* 0 0x00 '^@' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 1 0x01 '^A' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x81, /* 10000001 */ - 0xa5, /* 10100101 */ - 0x81, /* 10000001 */ - 0x81, /* 10000001 */ - 0xbd, /* 10111101 */ - 0x99, /* 10011001 */ - 0x81, /* 10000001 */ - 0x81, /* 10000001 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 2 0x02 '^B' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0xff, /* 11111111 */ - 0xdb, /* 11011011 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xc3, /* 11000011 */ - 0xe7, /* 11100111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 3 0x03 '^C' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x6c, /* 01101100 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0x7c, /* 01111100 */ - 0x38, /* 00111000 */ - 0x10, /* 00010000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 4 0x04 '^D' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x7c, /* 01111100 */ - 0xfe, /* 11111110 */ - 0x7c, /* 01111100 */ - 0x38, /* 00111000 */ - 0x10, /* 00010000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 5 0x05 '^E' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0xe7, /* 11100111 */ - 0xe7, /* 11100111 */ - 0xe7, /* 11100111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 6 0x06 '^F' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x7e, /* 01111110 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 7 0x07 '^G' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 8 0x08 '^H' */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xe7, /* 11100111 */ - 0xc3, /* 11000011 */ - 0xc3, /* 11000011 */ - 0xe7, /* 11100111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - - /* 9 0x09 '^I' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x42, /* 01000010 */ - 0x42, /* 01000010 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 10 0x0a '^J' */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xc3, /* 11000011 */ - 0x99, /* 10011001 */ - 0xbd, /* 10111101 */ - 0xbd, /* 10111101 */ - 0x99, /* 10011001 */ - 0xc3, /* 11000011 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - - /* 11 0x0b '^K' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x1e, /* 00011110 */ - 0x0e, /* 00001110 */ - 0x1a, /* 00011010 */ - 0x32, /* 00110010 */ - 0x78, /* 01111000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x78, /* 01111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 12 0x0c '^L' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 13 0x0d '^M' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3f, /* 00111111 */ - 0x33, /* 00110011 */ - 0x3f, /* 00111111 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x70, /* 01110000 */ - 0xf0, /* 11110000 */ - 0xe0, /* 11100000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 14 0x0e '^N' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7f, /* 01111111 */ - 0x63, /* 01100011 */ - 0x7f, /* 01111111 */ - 0x63, /* 01100011 */ - 0x63, /* 01100011 */ - 0x63, /* 01100011 */ - 0x63, /* 01100011 */ - 0x67, /* 01100111 */ - 0xe7, /* 11100111 */ - 0xe6, /* 11100110 */ - 0xc0, /* 11000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 15 0x0f '^O' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xdb, /* 11011011 */ - 0x3c, /* 00111100 */ - 0xe7, /* 11100111 */ - 0x3c, /* 00111100 */ - 0xdb, /* 11011011 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 16 0x10 '^P' */ - 0x00, /* 00000000 */ - 0x80, /* 10000000 */ - 0xc0, /* 11000000 */ - 0xe0, /* 11100000 */ - 0xf0, /* 11110000 */ - 0xf8, /* 11111000 */ - 0xfe, /* 11111110 */ - 0xf8, /* 11111000 */ - 0xf0, /* 11110000 */ - 0xe0, /* 11100000 */ - 0xc0, /* 11000000 */ - 0x80, /* 10000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 17 0x11 '^Q' */ - 0x00, /* 00000000 */ - 0x02, /* 00000010 */ - 0x06, /* 00000110 */ - 0x0e, /* 00001110 */ - 0x1e, /* 00011110 */ - 0x3e, /* 00111110 */ - 0xfe, /* 11111110 */ - 0x3e, /* 00111110 */ - 0x1e, /* 00011110 */ - 0x0e, /* 00001110 */ - 0x06, /* 00000110 */ - 0x02, /* 00000010 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 18 0x12 '^R' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 19 0x13 '^S' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 20 0x14 '^T' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7f, /* 01111111 */ - 0xdb, /* 11011011 */ - 0xdb, /* 11011011 */ - 0xdb, /* 11011011 */ - 0x7b, /* 01111011 */ - 0x1b, /* 00011011 */ - 0x1b, /* 00011011 */ - 0x1b, /* 00011011 */ - 0x1b, /* 00011011 */ - 0x1b, /* 00011011 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 21 0x15 '^U' */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0x60, /* 01100000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x0c, /* 00001100 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 22 0x16 '^V' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 23 0x17 '^W' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 24 0x18 '^X' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 25 0x19 '^Y' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 26 0x1a '^Z' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0xfe, /* 11111110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 27 0x1b '^[' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xfe, /* 11111110 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 28 0x1c '^\' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 29 0x1d '^]' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x28, /* 00101000 */ - 0x6c, /* 01101100 */ - 0xfe, /* 11111110 */ - 0x6c, /* 01101100 */ - 0x28, /* 00101000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 30 0x1e '^^' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x38, /* 00111000 */ - 0x7c, /* 01111100 */ - 0x7c, /* 01111100 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 31 0x1f '^_' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0x7c, /* 01111100 */ - 0x7c, /* 01111100 */ - 0x38, /* 00111000 */ - 0x38, /* 00111000 */ - 0x10, /* 00010000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 32 0x20 ' ' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 33 0x21 '!' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 34 0x22 '"' */ - 0x00, /* 00000000 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x24, /* 00100100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 35 0x23 '#' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0xfe, /* 11111110 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0xfe, /* 11111110 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 36 0x24 '$' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc2, /* 11000010 */ - 0xc0, /* 11000000 */ - 0x7c, /* 01111100 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x86, /* 10000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 37 0x25 '%' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc2, /* 11000010 */ - 0xc6, /* 11000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xc6, /* 11000110 */ - 0x86, /* 10000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 38 0x26 '&' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 39 0x27 ''' */ - 0x00, /* 00000000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 40 0x28 '(' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 41 0x29 ')' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 42 0x2a '*' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0xff, /* 11111111 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 43 0x2b '+' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 44 0x2c ',' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 45 0x2d '-' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 46 0x2e '.' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 47 0x2f '/' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x02, /* 00000010 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xc0, /* 11000000 */ - 0x80, /* 10000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 48 0x30 '0' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xd6, /* 11010110 */ - 0xd6, /* 11010110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 49 0x31 '1' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x38, /* 00111000 */ - 0x78, /* 01111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 50 0x32 '2' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 51 0x33 '3' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x3c, /* 00111100 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 52 0x34 '4' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x0c, /* 00001100 */ - 0x1c, /* 00011100 */ - 0x3c, /* 00111100 */ - 0x6c, /* 01101100 */ - 0xcc, /* 11001100 */ - 0xfe, /* 11111110 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x1e, /* 00011110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 53 0x35 '5' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xfc, /* 11111100 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 54 0x36 '6' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x60, /* 01100000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xfc, /* 11111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 55 0x37 '7' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 56 0x38 '8' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 57 0x39 '9' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7e, /* 01111110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x78, /* 01111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 58 0x3a ':' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 59 0x3b ';' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 60 0x3c '<' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x06, /* 00000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 61 0x3d '=' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 62 0x3e '>' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 63 0x3f '?' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 64 0x40 '@' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xde, /* 11011110 */ - 0xde, /* 11011110 */ - 0xde, /* 11011110 */ - 0xdc, /* 11011100 */ - 0xc0, /* 11000000 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 65 0x41 'A' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 66 0x42 'B' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfc, /* 11111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x7c, /* 01111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0xfc, /* 11111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 67 0x43 'C' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0xc2, /* 11000010 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc2, /* 11000010 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 68 0x44 'D' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xf8, /* 11111000 */ - 0x6c, /* 01101100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x6c, /* 01101100 */ - 0xf8, /* 11111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 69 0x45 'E' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x66, /* 01100110 */ - 0x62, /* 01100010 */ - 0x68, /* 01101000 */ - 0x78, /* 01111000 */ - 0x68, /* 01101000 */ - 0x60, /* 01100000 */ - 0x62, /* 01100010 */ - 0x66, /* 01100110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 70 0x46 'F' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x66, /* 01100110 */ - 0x62, /* 01100010 */ - 0x68, /* 01101000 */ - 0x78, /* 01111000 */ - 0x68, /* 01101000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0xf0, /* 11110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 71 0x47 'G' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0xc2, /* 11000010 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xde, /* 11011110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x66, /* 01100110 */ - 0x3a, /* 00111010 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 72 0x48 'H' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 73 0x49 'I' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 74 0x4a 'J' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x1e, /* 00011110 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x78, /* 01111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 75 0x4b 'K' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xe6, /* 11100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x6c, /* 01101100 */ - 0x78, /* 01111000 */ - 0x78, /* 01111000 */ - 0x6c, /* 01101100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0xe6, /* 11100110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 76 0x4c 'L' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xf0, /* 11110000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x62, /* 01100010 */ - 0x66, /* 01100110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 77 0x4d 'M' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xee, /* 11101110 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0xd6, /* 11010110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 78 0x4e 'N' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xe6, /* 11100110 */ - 0xf6, /* 11110110 */ - 0xfe, /* 11111110 */ - 0xde, /* 11011110 */ - 0xce, /* 11001110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 79 0x4f 'O' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 80 0x50 'P' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfc, /* 11111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x7c, /* 01111100 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0xf0, /* 11110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 81 0x51 'Q' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xd6, /* 11010110 */ - 0xde, /* 11011110 */ - 0x7c, /* 01111100 */ - 0x0c, /* 00001100 */ - 0x0e, /* 00001110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 82 0x52 'R' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfc, /* 11111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x7c, /* 01111100 */ - 0x6c, /* 01101100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0xe6, /* 11100110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 83 0x53 'S' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x60, /* 01100000 */ - 0x38, /* 00111000 */ - 0x0c, /* 00001100 */ - 0x06, /* 00000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 84 0x54 'T' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x7e, /* 01111110 */ - 0x5a, /* 01011010 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 85 0x55 'U' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 86 0x56 'V' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x10, /* 00010000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 87 0x57 'W' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xd6, /* 11010110 */ - 0xd6, /* 11010110 */ - 0xd6, /* 11010110 */ - 0xfe, /* 11111110 */ - 0xee, /* 11101110 */ - 0x6c, /* 01101100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 88 0x58 'X' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x7c, /* 01111100 */ - 0x38, /* 00111000 */ - 0x38, /* 00111000 */ - 0x7c, /* 01111100 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 89 0x59 'Y' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 90 0x5a 'Z' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0x86, /* 10000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xc2, /* 11000010 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 91 0x5b '[' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00111100 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 92 0x5c '\' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x80, /* 10000000 */ - 0xc0, /* 11000000 */ - 0xe0, /* 11100000 */ - 0x70, /* 01110000 */ - 0x38, /* 00111000 */ - 0x1c, /* 00011100 */ - 0x0e, /* 00001110 */ - 0x06, /* 00000110 */ - 0x02, /* 00000010 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 93 0x5d ']' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00111100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 94 0x5e '^' */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 95 0x5f '_' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 96 0x60 '`' */ - 0x00, /* 00000000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 97 0x61 'a' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 98 0x62 'b' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xe0, /* 11100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x78, /* 01111000 */ - 0x6c, /* 01101100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 99 0x63 'c' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 100 0x64 'd' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x1c, /* 00011100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x3c, /* 00111100 */ - 0x6c, /* 01101100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 101 0x65 'e' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 102 0x66 'f' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x1c, /* 00011100 */ - 0x36, /* 00110110 */ - 0x32, /* 00110010 */ - 0x30, /* 00110000 */ - 0x78, /* 01111000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x78, /* 01111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 103 0x67 'g' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x7c, /* 01111100 */ - 0x0c, /* 00001100 */ - 0xcc, /* 11001100 */ - 0x78, /* 01111000 */ - 0x00, /* 00000000 */ - - /* 104 0x68 'h' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xe0, /* 11100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x6c, /* 01101100 */ - 0x76, /* 01110110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0xe6, /* 11100110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 105 0x69 'i' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 106 0x6a 'j' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x00, /* 00000000 */ - 0x0e, /* 00001110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 107 0x6b 'k' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xe0, /* 11100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x66, /* 01100110 */ - 0x6c, /* 01101100 */ - 0x78, /* 01111000 */ - 0x78, /* 01111000 */ - 0x6c, /* 01101100 */ - 0x66, /* 01100110 */ - 0xe6, /* 11100110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 108 0x6c 'l' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 109 0x6d 'm' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xec, /* 11101100 */ - 0xfe, /* 11111110 */ - 0xd6, /* 11010110 */ - 0xd6, /* 11010110 */ - 0xd6, /* 11010110 */ - 0xd6, /* 11010110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 110 0x6e 'n' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xdc, /* 11011100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 111 0x6f 'o' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 112 0x70 'p' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xdc, /* 11011100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x7c, /* 01111100 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0xf0, /* 11110000 */ - 0x00, /* 00000000 */ - - /* 113 0x71 'q' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x7c, /* 01111100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x1e, /* 00011110 */ - 0x00, /* 00000000 */ - - /* 114 0x72 'r' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xdc, /* 11011100 */ - 0x76, /* 01110110 */ - 0x66, /* 01100110 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0xf0, /* 11110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 115 0x73 's' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0x60, /* 01100000 */ - 0x38, /* 00111000 */ - 0x0c, /* 00001100 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 116 0x74 't' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x10, /* 00010000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0xfc, /* 11111100 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x36, /* 00110110 */ - 0x1c, /* 00011100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 117 0x75 'u' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 118 0x76 'v' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 119 0x77 'w' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xd6, /* 11010110 */ - 0xd6, /* 11010110 */ - 0xd6, /* 11010110 */ - 0xfe, /* 11111110 */ - 0x6c, /* 01101100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 120 0x78 'x' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x38, /* 00111000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 121 0x79 'y' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7e, /* 01111110 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0xf8, /* 11111000 */ - 0x00, /* 00000000 */ - - /* 122 0x7a 'z' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0xcc, /* 11001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 123 0x7b '{' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x0e, /* 00001110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x70, /* 01110000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x0e, /* 00001110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 124 0x7c '|' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 125 0x7d '}' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x70, /* 01110000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x0e, /* 00001110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x70, /* 01110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 126 0x7e '~' */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 127 0x7f '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 128 0x80 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0xc2, /* 11000010 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc2, /* 11000010 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x70, /* 01110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 129 0x81 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xcc, /* 11001100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 130 0x82 '' */ - 0x00, /* 00000000 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 131 0x83 '' */ - 0x00, /* 00000000 */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x00, /* 00000000 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 132 0x84 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xcc, /* 11001100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 133 0x85 '' */ - 0x00, /* 00000000 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 134 0x86 '' */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 135 0x87 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x18, /* 00011000 */ - 0x70, /* 01110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 136 0x88 '' */ - 0x00, /* 00000000 */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 137 0x89 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 138 0x8a '' */ - 0x00, /* 00000000 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 139 0x8b '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 140 0x8c '' */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 141 0x8d '' */ - 0x00, /* 00000000 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 142 0x8e '' */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 143 0x8f '' */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 144 0x90 '' */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x66, /* 01100110 */ - 0x62, /* 01100010 */ - 0x68, /* 01101000 */ - 0x78, /* 01111000 */ - 0x68, /* 01101000 */ - 0x62, /* 01100010 */ - 0x66, /* 01100110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 145 0x91 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xec, /* 11101100 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x7e, /* 01111110 */ - 0xd8, /* 11011000 */ - 0xd8, /* 11011000 */ - 0x6e, /* 01101110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 146 0x92 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3e, /* 00111110 */ - 0x6c, /* 01101100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xfe, /* 11111110 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xce, /* 11001110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 147 0x93 '' */ - 0x00, /* 00000000 */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 148 0x94 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 149 0x95 '' */ - 0x00, /* 00000000 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 150 0x96 '' */ - 0x00, /* 00000000 */ - 0x30, /* 00110000 */ - 0x78, /* 01111000 */ - 0xcc, /* 11001100 */ - 0x00, /* 00000000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 151 0x97 '' */ - 0x00, /* 00000000 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 152 0x98 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7e, /* 01111110 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x78, /* 01111000 */ - 0x00, /* 00000000 */ - - /* 153 0x99 '' */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 154 0x9a '' */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 155 0x9b '' */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 156 0x9c '' */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x64, /* 01100100 */ - 0x60, /* 01100000 */ - 0xf0, /* 11110000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0xe6, /* 11100110 */ - 0xfc, /* 11111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 157 0x9d '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 158 0x9e '' */ - 0x00, /* 00000000 */ - 0xf8, /* 11111000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xf8, /* 11111000 */ - 0xc4, /* 11000100 */ - 0xcc, /* 11001100 */ - 0xde, /* 11011110 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 159 0x9f '' */ - 0x00, /* 00000000 */ - 0x0e, /* 00001110 */ - 0x1b, /* 00011011 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xd8, /* 11011000 */ - 0x70, /* 01110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 160 0xa0 '' */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0x00, /* 00000000 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 161 0xa1 '' */ - 0x00, /* 00000000 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 162 0xa2 '' */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 163 0xa3 '' */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0x00, /* 00000000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 164 0xa4 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x00, /* 00000000 */ - 0xdc, /* 11011100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 165 0xa5 '' */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xe6, /* 11100110 */ - 0xf6, /* 11110110 */ - 0xfe, /* 11111110 */ - 0xde, /* 11011110 */ - 0xce, /* 11001110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 166 0xa6 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00111100 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x3e, /* 00111110 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 167 0xa7 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 168 0xa8 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 169 0xa9 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 170 0xaa '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 171 0xab '' */ - 0x00, /* 00000000 */ - 0x60, /* 01100000 */ - 0xe0, /* 11100000 */ - 0x62, /* 01100010 */ - 0x66, /* 01100110 */ - 0x6c, /* 01101100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xdc, /* 11011100 */ - 0x86, /* 10000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x3e, /* 00111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 172 0xac '' */ - 0x00, /* 00000000 */ - 0x60, /* 01100000 */ - 0xe0, /* 11100000 */ - 0x62, /* 01100010 */ - 0x66, /* 01100110 */ - 0x6c, /* 01101100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x66, /* 01100110 */ - 0xce, /* 11001110 */ - 0x9a, /* 10011010 */ - 0x3f, /* 00111111 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 173 0xad '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 174 0xae '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x36, /* 00110110 */ - 0x6c, /* 01101100 */ - 0xd8, /* 11011000 */ - 0x6c, /* 01101100 */ - 0x36, /* 00110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 175 0xaf '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xd8, /* 11011000 */ - 0x6c, /* 01101100 */ - 0x36, /* 00110110 */ - 0x6c, /* 01101100 */ - 0xd8, /* 11011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 176 0xb0 '' */ - 0x11, /* 00010001 */ - 0x44, /* 01000100 */ - 0x11, /* 00010001 */ - 0x44, /* 01000100 */ - 0x11, /* 00010001 */ - 0x44, /* 01000100 */ - 0x11, /* 00010001 */ - 0x44, /* 01000100 */ - 0x11, /* 00010001 */ - 0x44, /* 01000100 */ - 0x11, /* 00010001 */ - 0x44, /* 01000100 */ - 0x11, /* 00010001 */ - 0x44, /* 01000100 */ - 0x11, /* 00010001 */ - 0x44, /* 01000100 */ - - /* 177 0xb1 '' */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - - /* 178 0xb2 '' */ - 0xdd, /* 11011101 */ - 0x77, /* 01110111 */ - 0xdd, /* 11011101 */ - 0x77, /* 01110111 */ - 0xdd, /* 11011101 */ - 0x77, /* 01110111 */ - 0xdd, /* 11011101 */ - 0x77, /* 01110111 */ - 0xdd, /* 11011101 */ - 0x77, /* 01110111 */ - 0xdd, /* 11011101 */ - 0x77, /* 01110111 */ - 0xdd, /* 11011101 */ - 0x77, /* 01110111 */ - 0xdd, /* 11011101 */ - 0x77, /* 01110111 */ - - /* 179 0xb3 '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 180 0xb4 '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 181 0xb5 '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 182 0xb6 '' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xf6, /* 11110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 183 0xb7 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 184 0xb8 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 185 0xb9 '' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xf6, /* 11110110 */ - 0x06, /* 00000110 */ - 0xf6, /* 11110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 186 0xba '' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 187 0xbb '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x06, /* 00000110 */ - 0xf6, /* 11110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 188 0xbc '' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xf6, /* 11110110 */ - 0x06, /* 00000110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 189 0xbd '' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 190 0xbe '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 191 0xbf '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 192 0xc0 '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 193 0xc1 '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 194 0xc2 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 195 0xc3 '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 196 0xc4 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 197 0xc5 '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xff, /* 11111111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 198 0xc6 '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 199 0xc7 '' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x37, /* 00110111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 200 0xc8 '' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x37, /* 00110111 */ - 0x30, /* 00110000 */ - 0x3f, /* 00111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 201 0xc9 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3f, /* 00111111 */ - 0x30, /* 00110000 */ - 0x37, /* 00110111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 202 0xca '' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xf7, /* 11110111 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 203 0xcb '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0xf7, /* 11110111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 204 0xcc '' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x37, /* 00110111 */ - 0x30, /* 00110000 */ - 0x37, /* 00110111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 205 0xcd '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 206 0xce '' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xf7, /* 11110111 */ - 0x00, /* 00000000 */ - 0xf7, /* 11110111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 207 0xcf '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 208 0xd0 '' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 209 0xd1 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 210 0xd2 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 211 0xd3 '' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x3f, /* 00111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 212 0xd4 '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 213 0xd5 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 214 0xd6 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3f, /* 00111111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 215 0xd7 '' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xff, /* 11111111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 216 0xd8 '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xff, /* 11111111 */ - 0x18, /* 00011000 */ - 0xff, /* 11111111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 217 0xd9 '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 218 0xda '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 219 0xdb '' */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - - /* 220 0xdc '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - - /* 221 0xdd '' */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - - /* 222 0xde '' */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - - /* 223 0xdf '' */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 224 0xe0 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0xd8, /* 11011000 */ - 0xd8, /* 11011000 */ - 0xd8, /* 11011000 */ - 0xdc, /* 11011100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 225 0xe1 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x78, /* 01111000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xd8, /* 11011000 */ - 0xcc, /* 11001100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xcc, /* 11001100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 226 0xe2 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 227 0xe3 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 228 0xe4 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 229 0xe5 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0xd8, /* 11011000 */ - 0xd8, /* 11011000 */ - 0xd8, /* 11011000 */ - 0xd8, /* 11011000 */ - 0xd8, /* 11011000 */ - 0x70, /* 01110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 230 0xe6 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x7c, /* 01111100 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0xc0, /* 11000000 */ - 0x00, /* 00000000 */ - - /* 231 0xe7 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 232 0xe8 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 233 0xe9 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 234 0xea '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0xee, /* 11101110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 235 0xeb '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x1e, /* 00011110 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x3e, /* 00111110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 236 0xec '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0xdb, /* 11011011 */ - 0xdb, /* 11011011 */ - 0xdb, /* 11011011 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 237 0xed '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x03, /* 00000011 */ - 0x06, /* 00000110 */ - 0x7e, /* 01111110 */ - 0xdb, /* 11011011 */ - 0xdb, /* 11011011 */ - 0xf3, /* 11110011 */ - 0x7e, /* 01111110 */ - 0x60, /* 01100000 */ - 0xc0, /* 11000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 238 0xee '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x1c, /* 00011100 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x7c, /* 01111100 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x1c, /* 00011100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 239 0xef '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 240 0xf0 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 241 0xf1 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 242 0xf2 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 243 0xf3 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 244 0xf4 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x0e, /* 00001110 */ - 0x1b, /* 00011011 */ - 0x1b, /* 00011011 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 245 0xf5 '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xd8, /* 11011000 */ - 0xd8, /* 11011000 */ - 0xd8, /* 11011000 */ - 0x70, /* 01110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 246 0xf6 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 247 0xf7 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 248 0xf8 '' */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 249 0xf9 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 250 0xfa '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 251 0xfb '' */ - 0x00, /* 00000000 */ - 0x0f, /* 00001111 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0xec, /* 11101100 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x3c, /* 00111100 */ - 0x1c, /* 00011100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 252 0xfc '' */ - 0x00, /* 00000000 */ - 0x6c, /* 01101100 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 253 0xfd '' */ - 0x00, /* 00000000 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x32, /* 00110010 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 254 0xfe '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x7e, /* 01111110 */ - 0x7e, /* 01111110 */ - 0x7e, /* 01111110 */ - 0x7e, /* 01111110 */ - 0x7e, /* 01111110 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 255 0xff '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - -}; - - -const struct font_desc font_vga_8x16 = { - .idx = VGA8x16_IDX, - .name = "VGA8x16", - .width = 8, - .height = 16, - .data = fontdata_8x16, - .pref = 0, -}; -EXPORT_SYMBOL(font_vga_8x16); diff --git a/drivers/video/console/font_8x8.c b/drivers/video/console/font_8x8.c deleted file mode 100644 index 9f56efe2cee7..000000000000 --- a/drivers/video/console/font_8x8.c +++ /dev/null @@ -1,2583 +0,0 @@ -/**********************************************/ -/* */ -/* Font file generated by cpi2fnt */ -/* */ -/**********************************************/ - -#include - -#define FONTDATAMAX 2048 - -static const unsigned char fontdata_8x8[FONTDATAMAX] = { - - /* 0 0x00 '^@' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 1 0x01 '^A' */ - 0x7e, /* 01111110 */ - 0x81, /* 10000001 */ - 0xa5, /* 10100101 */ - 0x81, /* 10000001 */ - 0xbd, /* 10111101 */ - 0x99, /* 10011001 */ - 0x81, /* 10000001 */ - 0x7e, /* 01111110 */ - - /* 2 0x02 '^B' */ - 0x7e, /* 01111110 */ - 0xff, /* 11111111 */ - 0xdb, /* 11011011 */ - 0xff, /* 11111111 */ - 0xc3, /* 11000011 */ - 0xe7, /* 11100111 */ - 0xff, /* 11111111 */ - 0x7e, /* 01111110 */ - - /* 3 0x03 '^C' */ - 0x6c, /* 01101100 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0x7c, /* 01111100 */ - 0x38, /* 00111000 */ - 0x10, /* 00010000 */ - 0x00, /* 00000000 */ - - /* 4 0x04 '^D' */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x7c, /* 01111100 */ - 0xfe, /* 11111110 */ - 0x7c, /* 01111100 */ - 0x38, /* 00111000 */ - 0x10, /* 00010000 */ - 0x00, /* 00000000 */ - - /* 5 0x05 '^E' */ - 0x38, /* 00111000 */ - 0x7c, /* 01111100 */ - 0x38, /* 00111000 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0xd6, /* 11010110 */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - - /* 6 0x06 '^F' */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x7c, /* 01111100 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0x7c, /* 01111100 */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - - /* 7 0x07 '^G' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 8 0x08 '^H' */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xe7, /* 11100111 */ - 0xc3, /* 11000011 */ - 0xc3, /* 11000011 */ - 0xe7, /* 11100111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - - /* 9 0x09 '^I' */ - 0x00, /* 00000000 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x42, /* 01000010 */ - 0x42, /* 01000010 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 10 0x0a '^J' */ - 0xff, /* 11111111 */ - 0xc3, /* 11000011 */ - 0x99, /* 10011001 */ - 0xbd, /* 10111101 */ - 0xbd, /* 10111101 */ - 0x99, /* 10011001 */ - 0xc3, /* 11000011 */ - 0xff, /* 11111111 */ - - /* 11 0x0b '^K' */ - 0x0f, /* 00001111 */ - 0x07, /* 00000111 */ - 0x0f, /* 00001111 */ - 0x7d, /* 01111101 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x78, /* 01111000 */ - - /* 12 0x0c '^L' */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - - /* 13 0x0d '^M' */ - 0x3f, /* 00111111 */ - 0x33, /* 00110011 */ - 0x3f, /* 00111111 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x70, /* 01110000 */ - 0xf0, /* 11110000 */ - 0xe0, /* 11100000 */ - - /* 14 0x0e '^N' */ - 0x7f, /* 01111111 */ - 0x63, /* 01100011 */ - 0x7f, /* 01111111 */ - 0x63, /* 01100011 */ - 0x63, /* 01100011 */ - 0x67, /* 01100111 */ - 0xe6, /* 11100110 */ - 0xc0, /* 11000000 */ - - /* 15 0x0f '^O' */ - 0x18, /* 00011000 */ - 0xdb, /* 11011011 */ - 0x3c, /* 00111100 */ - 0xe7, /* 11100111 */ - 0xe7, /* 11100111 */ - 0x3c, /* 00111100 */ - 0xdb, /* 11011011 */ - 0x18, /* 00011000 */ - - /* 16 0x10 '^P' */ - 0x80, /* 10000000 */ - 0xe0, /* 11100000 */ - 0xf8, /* 11111000 */ - 0xfe, /* 11111110 */ - 0xf8, /* 11111000 */ - 0xe0, /* 11100000 */ - 0x80, /* 10000000 */ - 0x00, /* 00000000 */ - - /* 17 0x11 '^Q' */ - 0x02, /* 00000010 */ - 0x0e, /* 00001110 */ - 0x3e, /* 00111110 */ - 0xfe, /* 11111110 */ - 0x3e, /* 00111110 */ - 0x0e, /* 00001110 */ - 0x02, /* 00000010 */ - 0x00, /* 00000000 */ - - /* 18 0x12 '^R' */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - - /* 19 0x13 '^S' */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - - /* 20 0x14 '^T' */ - 0x7f, /* 01111111 */ - 0xdb, /* 11011011 */ - 0xdb, /* 11011011 */ - 0x7b, /* 01111011 */ - 0x1b, /* 00011011 */ - 0x1b, /* 00011011 */ - 0x1b, /* 00011011 */ - 0x00, /* 00000000 */ - - /* 21 0x15 '^U' */ - 0x3e, /* 00111110 */ - 0x61, /* 01100001 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x86, /* 10000110 */ - 0x7c, /* 01111100 */ - - /* 22 0x16 '^V' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x7e, /* 01111110 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - - /* 23 0x17 '^W' */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0xff, /* 11111111 */ - - /* 24 0x18 '^X' */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 25 0x19 '^Y' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 26 0x1a '^Z' */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0xfe, /* 11111110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 27 0x1b '^[' */ - 0x00, /* 00000000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xfe, /* 11111110 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 28 0x1c '^\' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 29 0x1d '^]' */ - 0x00, /* 00000000 */ - 0x24, /* 00100100 */ - 0x66, /* 01100110 */ - 0xff, /* 11111111 */ - 0x66, /* 01100110 */ - 0x24, /* 00100100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 30 0x1e '^^' */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x7e, /* 01111110 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 31 0x1f '^_' */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0x7e, /* 01111110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 32 0x20 ' ' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 33 0x21 '!' */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 34 0x22 '"' */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x24, /* 00100100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 35 0x23 '#' */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0xfe, /* 11111110 */ - 0x6c, /* 01101100 */ - 0xfe, /* 11111110 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x00, /* 00000000 */ - - /* 36 0x24 '$' */ - 0x18, /* 00011000 */ - 0x3e, /* 00111110 */ - 0x60, /* 01100000 */ - 0x3c, /* 00111100 */ - 0x06, /* 00000110 */ - 0x7c, /* 01111100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 37 0x25 '%' */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xcc, /* 11001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x66, /* 01100110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 38 0x26 '&' */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 39 0x27 ''' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 40 0x28 '(' */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x00, /* 00000000 */ - - /* 41 0x29 ')' */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - - /* 42 0x2a '*' */ - 0x00, /* 00000000 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0xff, /* 11111111 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 43 0x2b '+' */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 44 0x2c ',' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - - /* 45 0x2d '-' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 46 0x2e '.' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 47 0x2f '/' */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xc0, /* 11000000 */ - 0x80, /* 10000000 */ - 0x00, /* 00000000 */ - - /* 48 0x30 '0' */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xd6, /* 11010110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - - /* 49 0x31 '1' */ - 0x18, /* 00011000 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - - /* 50 0x32 '2' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0x06, /* 00000110 */ - 0x1c, /* 00011100 */ - 0x30, /* 00110000 */ - 0x66, /* 01100110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - - /* 51 0x33 '3' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0x06, /* 00000110 */ - 0x3c, /* 00111100 */ - 0x06, /* 00000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 52 0x34 '4' */ - 0x1c, /* 00011100 */ - 0x3c, /* 00111100 */ - 0x6c, /* 01101100 */ - 0xcc, /* 11001100 */ - 0xfe, /* 11111110 */ - 0x0c, /* 00001100 */ - 0x1e, /* 00011110 */ - 0x00, /* 00000000 */ - - /* 53 0x35 '5' */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xfc, /* 11111100 */ - 0x06, /* 00000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 54 0x36 '6' */ - 0x38, /* 00111000 */ - 0x60, /* 01100000 */ - 0xc0, /* 11000000 */ - 0xfc, /* 11111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 55 0x37 '7' */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - - /* 56 0x38 '8' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 57 0x39 '9' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7e, /* 01111110 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x78, /* 01111000 */ - 0x00, /* 00000000 */ - - /* 58 0x3a ':' */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 59 0x3b ';' */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - - /* 60 0x3c '<' */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x06, /* 00000110 */ - 0x00, /* 00000000 */ - - /* 61 0x3d '=' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 62 0x3e '>' */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0x00, /* 00000000 */ - - /* 63 0x3f '?' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 64 0x40 '@' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xde, /* 11011110 */ - 0xde, /* 11011110 */ - 0xde, /* 11011110 */ - 0xc0, /* 11000000 */ - 0x78, /* 01111000 */ - 0x00, /* 00000000 */ - - /* 65 0x41 'A' */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 66 0x42 'B' */ - 0xfc, /* 11111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x7c, /* 01111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0xfc, /* 11111100 */ - 0x00, /* 00000000 */ - - /* 67 0x43 'C' */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 68 0x44 'D' */ - 0xf8, /* 11111000 */ - 0x6c, /* 01101100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x6c, /* 01101100 */ - 0xf8, /* 11111000 */ - 0x00, /* 00000000 */ - - /* 69 0x45 'E' */ - 0xfe, /* 11111110 */ - 0x62, /* 01100010 */ - 0x68, /* 01101000 */ - 0x78, /* 01111000 */ - 0x68, /* 01101000 */ - 0x62, /* 01100010 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - - /* 70 0x46 'F' */ - 0xfe, /* 11111110 */ - 0x62, /* 01100010 */ - 0x68, /* 01101000 */ - 0x78, /* 01111000 */ - 0x68, /* 01101000 */ - 0x60, /* 01100000 */ - 0xf0, /* 11110000 */ - 0x00, /* 00000000 */ - - /* 71 0x47 'G' */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xce, /* 11001110 */ - 0x66, /* 01100110 */ - 0x3a, /* 00111010 */ - 0x00, /* 00000000 */ - - /* 72 0x48 'H' */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 73 0x49 'I' */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 74 0x4a 'J' */ - 0x1e, /* 00011110 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x78, /* 01111000 */ - 0x00, /* 00000000 */ - - /* 75 0x4b 'K' */ - 0xe6, /* 11100110 */ - 0x66, /* 01100110 */ - 0x6c, /* 01101100 */ - 0x78, /* 01111000 */ - 0x6c, /* 01101100 */ - 0x66, /* 01100110 */ - 0xe6, /* 11100110 */ - 0x00, /* 00000000 */ - - /* 76 0x4c 'L' */ - 0xf0, /* 11110000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x62, /* 01100010 */ - 0x66, /* 01100110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - - /* 77 0x4d 'M' */ - 0xc6, /* 11000110 */ - 0xee, /* 11101110 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0xd6, /* 11010110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 78 0x4e 'N' */ - 0xc6, /* 11000110 */ - 0xe6, /* 11100110 */ - 0xf6, /* 11110110 */ - 0xde, /* 11011110 */ - 0xce, /* 11001110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 79 0x4f 'O' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 80 0x50 'P' */ - 0xfc, /* 11111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x7c, /* 01111100 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0xf0, /* 11110000 */ - 0x00, /* 00000000 */ - - /* 81 0x51 'Q' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xce, /* 11001110 */ - 0x7c, /* 01111100 */ - 0x0e, /* 00001110 */ - - /* 82 0x52 'R' */ - 0xfc, /* 11111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x7c, /* 01111100 */ - 0x6c, /* 01101100 */ - 0x66, /* 01100110 */ - 0xe6, /* 11100110 */ - 0x00, /* 00000000 */ - - /* 83 0x53 'S' */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 84 0x54 'T' */ - 0x7e, /* 01111110 */ - 0x7e, /* 01111110 */ - 0x5a, /* 01011010 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 85 0x55 'U' */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 86 0x56 'V' */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - - /* 87 0x57 'W' */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xd6, /* 11010110 */ - 0xd6, /* 11010110 */ - 0xfe, /* 11111110 */ - 0x6c, /* 01101100 */ - 0x00, /* 00000000 */ - - /* 88 0x58 'X' */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 89 0x59 'Y' */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 90 0x5a 'Z' */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0x8c, /* 10001100 */ - 0x18, /* 00011000 */ - 0x32, /* 00110010 */ - 0x66, /* 01100110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - - /* 91 0x5b '[' */ - 0x3c, /* 00111100 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 92 0x5c '\' */ - 0xc0, /* 11000000 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x06, /* 00000110 */ - 0x02, /* 00000010 */ - 0x00, /* 00000000 */ - - /* 93 0x5d ']' */ - 0x3c, /* 00111100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 94 0x5e '^' */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 95 0x5f '_' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - - /* 96 0x60 '`' */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 97 0x61 'a' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 98 0x62 'b' */ - 0xe0, /* 11100000 */ - 0x60, /* 01100000 */ - 0x7c, /* 01111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0xdc, /* 11011100 */ - 0x00, /* 00000000 */ - - /* 99 0x63 'c' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 100 0x64 'd' */ - 0x1c, /* 00011100 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 101 0x65 'e' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 102 0x66 'f' */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x60, /* 01100000 */ - 0xf8, /* 11111000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0xf0, /* 11110000 */ - 0x00, /* 00000000 */ - - /* 103 0x67 'g' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x7c, /* 01111100 */ - 0x0c, /* 00001100 */ - 0xf8, /* 11111000 */ - - /* 104 0x68 'h' */ - 0xe0, /* 11100000 */ - 0x60, /* 01100000 */ - 0x6c, /* 01101100 */ - 0x76, /* 01110110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0xe6, /* 11100110 */ - 0x00, /* 00000000 */ - - /* 105 0x69 'i' */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 106 0x6a 'j' */ - 0x06, /* 00000110 */ - 0x00, /* 00000000 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - - /* 107 0x6b 'k' */ - 0xe0, /* 11100000 */ - 0x60, /* 01100000 */ - 0x66, /* 01100110 */ - 0x6c, /* 01101100 */ - 0x78, /* 01111000 */ - 0x6c, /* 01101100 */ - 0xe6, /* 11100110 */ - 0x00, /* 00000000 */ - - /* 108 0x6c 'l' */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 109 0x6d 'm' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xec, /* 11101100 */ - 0xfe, /* 11111110 */ - 0xd6, /* 11010110 */ - 0xd6, /* 11010110 */ - 0xd6, /* 11010110 */ - 0x00, /* 00000000 */ - - /* 110 0x6e 'n' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xdc, /* 11011100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - - /* 111 0x6f 'o' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 112 0x70 'p' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xdc, /* 11011100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x7c, /* 01111100 */ - 0x60, /* 01100000 */ - 0xf0, /* 11110000 */ - - /* 113 0x71 'q' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x7c, /* 01111100 */ - 0x0c, /* 00001100 */ - 0x1e, /* 00011110 */ - - /* 114 0x72 'r' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xdc, /* 11011100 */ - 0x76, /* 01110110 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0xf0, /* 11110000 */ - 0x00, /* 00000000 */ - - /* 115 0x73 's' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0xc0, /* 11000000 */ - 0x7c, /* 01111100 */ - 0x06, /* 00000110 */ - 0xfc, /* 11111100 */ - 0x00, /* 00000000 */ - - /* 116 0x74 't' */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0xfc, /* 11111100 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x36, /* 00110110 */ - 0x1c, /* 00011100 */ - 0x00, /* 00000000 */ - - /* 117 0x75 'u' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 118 0x76 'v' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - - /* 119 0x77 'w' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xd6, /* 11010110 */ - 0xd6, /* 11010110 */ - 0xfe, /* 11111110 */ - 0x6c, /* 01101100 */ - 0x00, /* 00000000 */ - - /* 120 0x78 'x' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 121 0x79 'y' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7e, /* 01111110 */ - 0x06, /* 00000110 */ - 0xfc, /* 11111100 */ - - /* 122 0x7a 'z' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x4c, /* 01001100 */ - 0x18, /* 00011000 */ - 0x32, /* 00110010 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - - /* 123 0x7b '{' */ - 0x0e, /* 00001110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x70, /* 01110000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x0e, /* 00001110 */ - 0x00, /* 00000000 */ - - /* 124 0x7c '|' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 125 0x7d '}' */ - 0x70, /* 01110000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x0e, /* 00001110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x70, /* 01110000 */ - 0x00, /* 00000000 */ - - /* 126 0x7e '~' */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 127 0x7f '' */ - 0x00, /* 00000000 */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - - /* 128 0x80 '' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x0c, /* 00001100 */ - 0x78, /* 01111000 */ - - /* 129 0x81 '' */ - 0xcc, /* 11001100 */ - 0x00, /* 00000000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 130 0x82 '' */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 131 0x83 '' */ - 0x7c, /* 01111100 */ - 0x82, /* 10000010 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 132 0x84 '' */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 133 0x85 '' */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 134 0x86 '' */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 135 0x87 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0x7e, /* 01111110 */ - 0x0c, /* 00001100 */ - 0x38, /* 00111000 */ - - /* 136 0x88 '' */ - 0x7c, /* 01111100 */ - 0x82, /* 10000010 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 137 0x89 '' */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 138 0x8a '' */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 139 0x8b '' */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 140 0x8c '' */ - 0x7c, /* 01111100 */ - 0x82, /* 10000010 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 141 0x8d '' */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 142 0x8e '' */ - 0xc6, /* 11000110 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 143 0x8f '' */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 144 0x90 '' */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0xf8, /* 11111000 */ - 0xc0, /* 11000000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - - /* 145 0x91 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0xd8, /* 11011000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - - /* 146 0x92 '' */ - 0x3e, /* 00111110 */ - 0x6c, /* 01101100 */ - 0xcc, /* 11001100 */ - 0xfe, /* 11111110 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xce, /* 11001110 */ - 0x00, /* 00000000 */ - - /* 147 0x93 '' */ - 0x7c, /* 01111100 */ - 0x82, /* 10000010 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 148 0x94 '' */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 149 0x95 '' */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 150 0x96 '' */ - 0x78, /* 01111000 */ - 0x84, /* 10000100 */ - 0x00, /* 00000000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 151 0x97 '' */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 152 0x98 '' */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7e, /* 01111110 */ - 0x06, /* 00000110 */ - 0xfc, /* 11111100 */ - - /* 153 0x99 '' */ - 0xc6, /* 11000110 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - - /* 154 0x9a '' */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 155 0x9b '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 156 0x9c '' */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x64, /* 01100100 */ - 0xf0, /* 11110000 */ - 0x60, /* 01100000 */ - 0x66, /* 01100110 */ - 0xfc, /* 11111100 */ - 0x00, /* 00000000 */ - - /* 157 0x9d '' */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 158 0x9e '' */ - 0xf8, /* 11111000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xfa, /* 11111010 */ - 0xc6, /* 11000110 */ - 0xcf, /* 11001111 */ - 0xc6, /* 11000110 */ - 0xc7, /* 11000111 */ - - /* 159 0x9f '' */ - 0x0e, /* 00001110 */ - 0x1b, /* 00011011 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0xd8, /* 11011000 */ - 0x70, /* 01110000 */ - 0x00, /* 00000000 */ - - /* 160 0xa0 '' */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 161 0xa1 '' */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 162 0xa2 '' */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 163 0xa3 '' */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 164 0xa4 '' */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x00, /* 00000000 */ - 0xdc, /* 11011100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - - /* 165 0xa5 '' */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x00, /* 00000000 */ - 0xe6, /* 11100110 */ - 0xf6, /* 11110110 */ - 0xde, /* 11011110 */ - 0xce, /* 11001110 */ - 0x00, /* 00000000 */ - - /* 166 0xa6 '' */ - 0x3c, /* 00111100 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x3e, /* 00111110 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 167 0xa7 '' */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 168 0xa8 '' */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x63, /* 01100011 */ - 0x3e, /* 00111110 */ - 0x00, /* 00000000 */ - - /* 169 0xa9 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 170 0xaa '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 171 0xab '' */ - 0x63, /* 01100011 */ - 0xe6, /* 11100110 */ - 0x6c, /* 01101100 */ - 0x7e, /* 01111110 */ - 0x33, /* 00110011 */ - 0x66, /* 01100110 */ - 0xcc, /* 11001100 */ - 0x0f, /* 00001111 */ - - /* 172 0xac '' */ - 0x63, /* 01100011 */ - 0xe6, /* 11100110 */ - 0x6c, /* 01101100 */ - 0x7a, /* 01111010 */ - 0x36, /* 00110110 */ - 0x6a, /* 01101010 */ - 0xdf, /* 11011111 */ - 0x06, /* 00000110 */ - - /* 173 0xad '' */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 174 0xae '' */ - 0x00, /* 00000000 */ - 0x33, /* 00110011 */ - 0x66, /* 01100110 */ - 0xcc, /* 11001100 */ - 0x66, /* 01100110 */ - 0x33, /* 00110011 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 175 0xaf '' */ - 0x00, /* 00000000 */ - 0xcc, /* 11001100 */ - 0x66, /* 01100110 */ - 0x33, /* 00110011 */ - 0x66, /* 01100110 */ - 0xcc, /* 11001100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 176 0xb0 '' */ - 0x22, /* 00100010 */ - 0x88, /* 10001000 */ - 0x22, /* 00100010 */ - 0x88, /* 10001000 */ - 0x22, /* 00100010 */ - 0x88, /* 10001000 */ - 0x22, /* 00100010 */ - 0x88, /* 10001000 */ - - /* 177 0xb1 '' */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - - /* 178 0xb2 '' */ - 0x77, /* 01110111 */ - 0xdd, /* 11011101 */ - 0x77, /* 01110111 */ - 0xdd, /* 11011101 */ - 0x77, /* 01110111 */ - 0xdd, /* 11011101 */ - 0x77, /* 01110111 */ - 0xdd, /* 11011101 */ - - /* 179 0xb3 '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 180 0xb4 '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 181 0xb5 '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 182 0xb6 '' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xf6, /* 11110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 183 0xb7 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 184 0xb8 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 185 0xb9 '' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xf6, /* 11110110 */ - 0x06, /* 00000110 */ - 0xf6, /* 11110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 186 0xba '' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 187 0xbb '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x06, /* 00000110 */ - 0xf6, /* 11110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 188 0xbc '' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xf6, /* 11110110 */ - 0x06, /* 00000110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 189 0xbd '' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 190 0xbe '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 191 0xbf '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 192 0xc0 '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 193 0xc1 '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 194 0xc2 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 195 0xc3 '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 196 0xc4 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 197 0xc5 '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xff, /* 11111111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 198 0xc6 '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 199 0xc7 '' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x37, /* 00110111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 200 0xc8 '' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x37, /* 00110111 */ - 0x30, /* 00110000 */ - 0x3f, /* 00111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 201 0xc9 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3f, /* 00111111 */ - 0x30, /* 00110000 */ - 0x37, /* 00110111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 202 0xca '' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xf7, /* 11110111 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 203 0xcb '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0xf7, /* 11110111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 204 0xcc '' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x37, /* 00110111 */ - 0x30, /* 00110000 */ - 0x37, /* 00110111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 205 0xcd '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 206 0xce '' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xf7, /* 11110111 */ - 0x00, /* 00000000 */ - 0xf7, /* 11110111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 207 0xcf '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 208 0xd0 '' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 209 0xd1 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 210 0xd2 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 211 0xd3 '' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x3f, /* 00111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 212 0xd4 '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 213 0xd5 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 214 0xd6 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3f, /* 00111111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 215 0xd7 '' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xff, /* 11111111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 216 0xd8 '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xff, /* 11111111 */ - 0x18, /* 00011000 */ - 0xff, /* 11111111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 217 0xd9 '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 218 0xda '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 219 0xdb '' */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - - /* 220 0xdc '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - - /* 221 0xdd '' */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - - /* 222 0xde '' */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - - /* 223 0xdf '' */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 224 0xe0 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0xc8, /* 11001000 */ - 0xdc, /* 11011100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 225 0xe1 '' */ - 0x78, /* 01111000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xd8, /* 11011000 */ - 0xcc, /* 11001100 */ - 0xc6, /* 11000110 */ - 0xcc, /* 11001100 */ - 0x00, /* 00000000 */ - - /* 226 0xe2 '' */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0x00, /* 00000000 */ - - /* 227 0xe3 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x00, /* 00000000 */ - - /* 228 0xe4 '' */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - - /* 229 0xe5 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0xd8, /* 11011000 */ - 0xd8, /* 11011000 */ - 0xd8, /* 11011000 */ - 0x70, /* 01110000 */ - 0x00, /* 00000000 */ - - /* 230 0xe6 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x7c, /* 01111100 */ - 0xc0, /* 11000000 */ - - /* 231 0xe7 '' */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 232 0xe8 '' */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - - /* 233 0xe9 '' */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - - /* 234 0xea '' */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0xee, /* 11101110 */ - 0x00, /* 00000000 */ - - /* 235 0xeb '' */ - 0x0e, /* 00001110 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x3e, /* 00111110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 236 0xec '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0xdb, /* 11011011 */ - 0xdb, /* 11011011 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 237 0xed '' */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x7e, /* 01111110 */ - 0xdb, /* 11011011 */ - 0xdb, /* 11011011 */ - 0x7e, /* 01111110 */ - 0x60, /* 01100000 */ - 0xc0, /* 11000000 */ - - /* 238 0xee '' */ - 0x1e, /* 00011110 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0x7e, /* 01111110 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x1e, /* 00011110 */ - 0x00, /* 00000000 */ - - /* 239 0xef '' */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 240 0xf0 '' */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 241 0xf1 '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - - /* 242 0xf2 '' */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - - /* 243 0xf3 '' */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - - /* 244 0xf4 '' */ - 0x0e, /* 00001110 */ - 0x1b, /* 00011011 */ - 0x1b, /* 00011011 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 245 0xf5 '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xd8, /* 11011000 */ - 0xd8, /* 11011000 */ - 0x70, /* 01110000 */ - - /* 246 0xf6 '' */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 247 0xf7 '' */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 248 0xf8 '' */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 249 0xf9 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 250 0xfa '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 251 0xfb '' */ - 0x0f, /* 00001111 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0xec, /* 11101100 */ - 0x6c, /* 01101100 */ - 0x3c, /* 00111100 */ - 0x1c, /* 00011100 */ - - /* 252 0xfc '' */ - 0x6c, /* 01101100 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 253 0xfd '' */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 254 0xfe '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 255 0xff '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - -}; - - -const struct font_desc font_vga_8x8 = { - .idx = VGA8x8_IDX, - .name = "VGA8x8", - .width = 8, - .height = 8, - .data = fontdata_8x8, - .pref = 0, -}; diff --git a/drivers/video/console/font_acorn_8x8.c b/drivers/video/console/font_acorn_8x8.c deleted file mode 100644 index 639e31ae1100..000000000000 --- a/drivers/video/console/font_acorn_8x8.c +++ /dev/null @@ -1,275 +0,0 @@ -/* Acorn-like font definition, with PC graphics characters */ - -#include - -static const unsigned char acorndata_8x8[] = { -/* 00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ^@ */ -/* 01 */ 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e, /* ^A */ -/* 02 */ 0x7e, 0xff, 0xbd, 0xff, 0xc3, 0xe7, 0xff, 0x7e, /* ^B */ -/* 03 */ 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, /* ^C */ -/* 04 */ 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, /* ^D */ -/* 05 */ 0x00, 0x18, 0x3c, 0xe7, 0xe7, 0x3c, 0x18, 0x00, /* ^E */ -/* 06 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* 07 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* 08 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* 09 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* 0A */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* 0B */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* 0C */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* 0D */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* 0E */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* 0F */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* 10 */ 0x00, 0x60, 0x78, 0x7e, 0x7e, 0x78, 0x60, 0x00, /* |> */ -/* 11 */ 0x00, 0x06, 0x1e, 0x7e, 0x7e, 0x1e, 0x06, 0x00, /* <| */ -/* 12 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* 13 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* 14 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* 15 */ 0x3c, 0x60, 0x3c, 0x66, 0x3c, 0x06, 0x3c, 0x00, -/* 16 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* 17 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* 18 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* 19 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* 1A */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* 1B */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* 1C */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* 1D */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* 1E */ 0x00, 0x18, 0x18, 0x3c, 0x3c, 0x7e, 0x7e, 0x00, /* /\ */ -/* 1F */ 0x00, 0x7e, 0x7e, 0x3c, 0x3c, 0x18, 0x18, 0x00, /* \/ */ -/* 20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* */ -/* 21 */ 0x18, 0x3c, 0x3c, 0x18, 0x18, 0x00, 0x18, 0x00, /* ! */ -/* 22 */ 0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, /* " */ -/* 23 */ 0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00, /* # */ -/* 24 */ 0x0C, 0x3F, 0x68, 0x3E, 0x0B, 0x7E, 0x18, 0x00, /* $ */ -/* 25 */ 0x60, 0x66, 0x0C, 0x18, 0x30, 0x66, 0x06, 0x00, /* % */ -/* 26 */ 0x38, 0x6C, 0x6C, 0x38, 0x6D, 0x66, 0x3B, 0x00, /* & */ -/* 27 */ 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' */ -/* 28 */ 0x0C, 0x18, 0x30, 0x30, 0x30, 0x18, 0x0C, 0x00, /* ( */ -/* 29 */ 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x18, 0x30, 0x00, /* ) */ -/* 2A */ 0x00, 0x18, 0x7E, 0x3C, 0x7E, 0x18, 0x00, 0x00, /* * */ -/* 2B */ 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00, /* + */ -/* 2C */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, /* , */ -/* 2D */ 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, /* - */ -/* 2E */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, /* . */ -/* 2F */ 0x00, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x00, 0x00, /* / */ -/* 30 */ 0x3C, 0x66, 0x6E, 0x7E, 0x76, 0x66, 0x3C, 0x00, /* 0 */ -/* 31 */ 0x18, 0x38, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x00, /* 1 */ -/* 32 */ 0x3C, 0x66, 0x06, 0x0C, 0x18, 0x30, 0x7E, 0x00, /* 2 */ -/* 33 */ 0x3C, 0x66, 0x06, 0x1C, 0x06, 0x66, 0x3C, 0x00, /* 3 */ -/* 34 */ 0x0C, 0x1C, 0x3C, 0x6C, 0x7E, 0x0C, 0x0C, 0x00, /* 4 */ -/* 35 */ 0x7E, 0x60, 0x7C, 0x06, 0x06, 0x66, 0x3C, 0x00, /* 5 */ -/* 36 */ 0x1C, 0x30, 0x60, 0x7C, 0x66, 0x66, 0x3C, 0x00, /* 6 */ -/* 37 */ 0x7E, 0x06, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x00, /* 7 */ -/* 38 */ 0x3C, 0x66, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00, /* 8 */ -/* 39 */ 0x3C, 0x66, 0x66, 0x3E, 0x06, 0x0C, 0x38, 0x00, /* 9 */ -/* 3A */ 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, /* : */ -/* 3B */ 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x30, /* ; */ -/* 3C */ 0x0C, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0C, 0x00, /* < */ -/* 3D */ 0x00, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x00, 0x00, /* = */ -/* 3E */ 0x30, 0x18, 0x0C, 0x06, 0x0C, 0x18, 0x30, 0x00, /* > */ -/* 3F */ 0x3C, 0x66, 0x0C, 0x18, 0x18, 0x00, 0x18, 0x00, /* ? */ -/* 40 */ 0x3C, 0x66, 0x6E, 0x6A, 0x6E, 0x60, 0x3C, 0x00, /* @ */ -/* 41 */ 0x3C, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, /* A */ -/* 42 */ 0x7C, 0x66, 0x66, 0x7C, 0x66, 0x66, 0x7C, 0x00, /* B */ -/* 43 */ 0x3C, 0x66, 0x60, 0x60, 0x60, 0x66, 0x3C, 0x00, /* C */ -/* 44 */ 0x78, 0x6C, 0x66, 0x66, 0x66, 0x6C, 0x78, 0x00, /* D */ -/* 45 */ 0x7E, 0x60, 0x60, 0x7C, 0x60, 0x60, 0x7E, 0x00, /* E */ -/* 46 */ 0x7E, 0x60, 0x60, 0x7C, 0x60, 0x60, 0x60, 0x00, /* F */ -/* 47 */ 0x3C, 0x66, 0x60, 0x6E, 0x66, 0x66, 0x3C, 0x00, /* G */ -/* 48 */ 0x66, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, /* H */ -/* 49 */ 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x00, /* I */ -/* 4A */ 0x3E, 0x0C, 0x0C, 0x0C, 0x0C, 0x6C, 0x38, 0x00, /* J */ -/* 4B */ 0x66, 0x6C, 0x78, 0x70, 0x78, 0x6C, 0x66, 0x00, /* K */ -/* 4C */ 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7E, 0x00, /* L */ -/* 4D */ 0x63, 0x77, 0x7F, 0x6B, 0x6B, 0x63, 0x63, 0x00, /* M */ -/* 4E */ 0x66, 0x66, 0x76, 0x7E, 0x6E, 0x66, 0x66, 0x00, /* N */ -/* 4F */ 0x3C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, /* O */ -/* 50 */ 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x60, 0x00, /* P */ -/* 51 */ 0x3C, 0x66, 0x66, 0x66, 0x6A, 0x6C, 0x36, 0x00, /* Q */ -/* 52 */ 0x7C, 0x66, 0x66, 0x7C, 0x6C, 0x66, 0x66, 0x00, /* R */ -/* 53 */ 0x3C, 0x66, 0x60, 0x3C, 0x06, 0x66, 0x3C, 0x00, /* S */ -/* 54 */ 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, /* T */ -/* 55 */ 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, /* U */ -/* 56 */ 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, /* V */ -/* 57 */ 0x63, 0x63, 0x6B, 0x6B, 0x7F, 0x77, 0x63, 0x00, /* W */ -/* 58 */ 0x66, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x66, 0x00, /* X */ -/* 59 */ 0x66, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x18, 0x00, /* Y */ -/* 5A */ 0x7E, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x7E, 0x00, /* Z */ -/* 5B */ 0x7C, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7C, 0x00, /* [ */ -/* 5C */ 0x00, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x00, 0x00, /* \ */ -/* 5D */ 0x3E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x3E, 0x00, /* ] */ -/* 5E */ 0x3C, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ^ */ -/* 5F */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, /* _ */ -/* 60 */ 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ` */ -/* 61 */ 0x00, 0x00, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00, /* a */ -/* 62 */ 0x60, 0x60, 0x7C, 0x66, 0x66, 0x66, 0x7C, 0x00, /* b */ -/* 63 */ 0x00, 0x00, 0x3C, 0x66, 0x60, 0x66, 0x3C, 0x00, /* c */ -/* 64 */ 0x06, 0x06, 0x3E, 0x66, 0x66, 0x66, 0x3E, 0x00, /* d */ -/* 65 */ 0x00, 0x00, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00, /* e */ -/* 66 */ 0x1C, 0x30, 0x30, 0x7C, 0x30, 0x30, 0x30, 0x00, /* f */ -/* 67 */ 0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x3C, /* g */ -/* 68 */ 0x60, 0x60, 0x7C, 0x66, 0x66, 0x66, 0x66, 0x00, /* h */ -/* 69 */ 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x3C, 0x00, /* i */ -/* 6A */ 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x70, /* j */ -/* 6B */ 0x60, 0x60, 0x66, 0x6C, 0x78, 0x6C, 0x66, 0x00, /* k */ -/* 6C */ 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, /* l */ -/* 6D */ 0x00, 0x00, 0x36, 0x7F, 0x6B, 0x6B, 0x63, 0x00, /* m */ -/* 6E */ 0x00, 0x00, 0x7C, 0x66, 0x66, 0x66, 0x66, 0x00, /* n */ -/* 6F */ 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00, /* o */ -/* 70 */ 0x00, 0x00, 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x60, /* p */ -/* 71 */ 0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x07, /* q */ -/* 72 */ 0x00, 0x00, 0x6C, 0x76, 0x60, 0x60, 0x60, 0x00, /* r */ -/* 73 */ 0x00, 0x00, 0x3E, 0x60, 0x3C, 0x06, 0x7C, 0x00, /* s */ -/* 74 */ 0x30, 0x30, 0x7C, 0x30, 0x30, 0x30, 0x1C, 0x00, /* t */ -/* 75 */ 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3E, 0x00, /* u */ -/* 76 */ 0x00, 0x00, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, /* v */ -/* 77 */ 0x00, 0x00, 0x63, 0x6B, 0x6B, 0x7F, 0x36, 0x00, /* w */ -/* 78 */ 0x00, 0x00, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x00, /* x */ -/* 79 */ 0x00, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x06, 0x3C, /* y */ -/* 7A */ 0x00, 0x00, 0x7E, 0x0C, 0x18, 0x30, 0x7E, 0x00, /* z */ -/* 7B */ 0x0C, 0x18, 0x18, 0x70, 0x18, 0x18, 0x0C, 0x00, /* { */ -/* 7C */ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, /* | */ -/* 7D */ 0x30, 0x18, 0x18, 0x0E, 0x18, 0x18, 0x30, 0x00, /* } */ -/* 7E */ 0x31, 0x6B, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, /* ~ */ -/* 7F */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*  */ -/* 80 */ 0x3c, 0x66, 0x60, 0x60, 0x66, 0x3c, 0x30, 0x60, -/* 81 */ 0x66, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x00, -/* 82 */ 0x0c, 0x18, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00, -/* 83 */ 0x18, 0x66, 0x3c, 0x06, 0x3e, 0x66, 0x3e, 0x00, -/* 84 */ 0x66, 0x00, 0x3c, 0x06, 0x3e, 0x66, 0x3e, 0x00, -/* 85 */ 0x30, 0x18, 0x3c, 0x06, 0x3e, 0x66, 0x3e, 0x00, -/* 86 */ 0x3c, 0x66, 0x3c, 0x06, 0x3e, 0x66, 0x3e, 0x00, -/* 87 */ 0x00, 0x00, 0x3c, 0x66, 0x60, 0x66, 0x3c, 0x60, -/* 88 */ 0x3c, 0x66, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00, -/* 89 */ 0x66, 0x00, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00, -/* 8A */ 0x30, 0x18, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00, -/* 8B */ 0x66, 0x00, 0x00, 0x38, 0x18, 0x18, 0x3c, 0x00, -/* 8C */ 0x3c, 0x66, 0x00, 0x38, 0x18, 0x18, 0x3c, 0x00, -/* 8D */ 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x3c, 0x00, -/* 8E */ 0x66, 0x66, 0x00, 0x3c, 0x66, 0x7e, 0x66, 0x00, -/* 8F */ 0x18, 0x66, 0x00, 0x3c, 0x66, 0x7e, 0x66, 0x00, -/* 90 */ 0x0c, 0x18, 0x7e, 0x60, 0x7c, 0x60, 0x7e, 0x00, -/* 91 */ 0x00, 0x00, 0x3f, 0x0d, 0x3f, 0x6c, 0x3f, 0x00, -/* 92 */ 0x3f, 0x66, 0x66, 0x7f, 0x66, 0x66, 0x67, 0x00, -/* 93 */ 0x3c, 0x66, 0x00, 0x3c, 0x66, 0x66, 0x3c, 0x00, -/* 94 */ 0x66, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x3c, 0x00, -/* 95 */ 0x30, 0x18, 0x00, 0x3c, 0x66, 0x66, 0x3c, 0x00, -/* 96 */ 0x3c, 0x66, 0x00, 0x66, 0x66, 0x66, 0x3e, 0x00, -/* 97 */ 0x30, 0x18, 0x00, 0x66, 0x66, 0x66, 0x3e, 0x00, -/* 98 */ 0x66, 0x00, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x3c, -/* 99 */ 0x66, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x00, -/* 9A */ 0x66, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, -/* 9B */ 0x08, 0x3e, 0x6b, 0x68, 0x6b, 0x3e, 0x08, 0x00, -/* 9C */ 0x1c, 0x36, 0x30, 0x7c, 0x30, 0x30, 0x7e, 0x00, -/* 9D */ 0x66, 0x3c, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, -/* 9E */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* 9F */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* A0 */ 0x0c, 0x18, 0x3c, 0x06, 0x3e, 0x66, 0x3e, 0x00, -/* A1 */ 0x0c, 0x18, 0x00, 0x38, 0x18, 0x18, 0x3c, 0x00, -/* A2 */ 0x0c, 0x18, 0x00, 0x3c, 0x66, 0x66, 0x3c, 0x00, -/* A3 */ 0x0c, 0x18, 0x00, 0x66, 0x66, 0x66, 0x3e, 0x00, -/* A4 */ 0x36, 0x6c, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x00, -/* A5 */ 0x36, 0x6c, 0x00, 0x66, 0x76, 0x6e, 0x66, 0x00, -/* A6 */ 0x1c, 0x06, 0x1e, 0x36, 0x1e, 0x00, 0x3e, 0x00, -/* A7 */ 0x1c, 0x36, 0x36, 0x36, 0x1c, 0x00, 0x3e, 0x00, -/* A8 */ 0x18, 0x00, 0x18, 0x18, 0x30, 0x66, 0x3c, 0x00, -/* A9 */ 0x7e, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* AA */ 0x7e, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* AB */ 0x40, 0xc0, 0x40, 0x4f, 0x41, 0x0f, 0x08, 0x0f, -/* AC */ 0x40, 0xc0, 0x40, 0x48, 0x48, 0x0a, 0x0f, 0x02, -/* AD */ 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, -/* AE */ 0x00, 0x33, 0x66, 0xcc, 0xcc, 0x66, 0x33, 0x00, -/* AF */ 0x00, 0xcc, 0x66, 0x33, 0x33, 0x66, 0xcc, 0x00, -/* B0 */ 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, -/* B1 */ 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, -/* B2 */ 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, -/* B3 */ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -/* B4 */ 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, -/* B5 */ 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, -/* B6 */ 0x66, 0x66, 0x66, 0xe6, 0x66, 0x66, 0x66, 0x66, -/* B7 */ 0x00, 0x00, 0x00, 0xfe, 0x66, 0x66, 0x66, 0x66, -/* B8 */ 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, -/* B9 */ 0x66, 0x66, 0xe6, 0x06, 0xe6, 0x66, 0x66, 0x66, -/* BA */ 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, -/* BB */ 0x00, 0x00, 0xfe, 0x06, 0xe6, 0x66, 0x66, 0x66, -/* BC */ 0x66, 0x66, 0xe6, 0x06, 0xfe, 0x00, 0x00, 0x00, -/* BD */ 0x66, 0x66, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, -/* BE */ 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, -/* BF */ 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, -/* C0 */ 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, -/* C1 */ 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, -/* C2 */ 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, -/* C3 */ 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, -/* C4 */ 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, -/* C5 */ 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, -/* C6 */ 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, -/* C7 */ 0x66, 0x66, 0x66, 0x67, 0x66, 0x66, 0x66, 0x66, -/* C8 */ 0x66, 0x66, 0x67, 0x60, 0x7f, 0x00, 0x00, 0x00, -/* C9 */ 0x00, 0x00, 0x7f, 0x60, 0x67, 0x66, 0x66, 0x66, -/* CA */ 0x66, 0x66, 0xe7, 0x00, 0xff, 0x00, 0x00, 0x00, -/* CB */ 0x00, 0x00, 0xff, 0x00, 0xe7, 0x66, 0x66, 0x66, -/* CC */ 0x66, 0x66, 0x67, 0x60, 0x67, 0x66, 0x66, 0x66, -/* CD */ 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, -/* CE */ 0x66, 0x66, 0xe7, 0x00, 0xe7, 0x66, 0x66, 0x66, -/* CF */ 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, -/* D0 */ 0x66, 0x66, 0x66, 0xff, 0x00, 0x00, 0x00, 0x00, -/* D1 */ 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, -/* D2 */ 0x00, 0x00, 0x00, 0xff, 0x66, 0x66, 0x66, 0x66, -/* D3 */ 0x66, 0x66, 0x66, 0x7f, 0x00, 0x00, 0x00, 0x00, -/* D4 */ 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, -/* D5 */ 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, -/* D6 */ 0x00, 0x00, 0x00, 0x7f, 0x66, 0x66, 0x66, 0x66, -/* D7 */ 0x66, 0x66, 0x66, 0xff, 0x66, 0x66, 0x66, 0x66, -/* D8 */ 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, -/* D9 */ 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, -/* DA */ 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, -/* DB */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, -/* DC */ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, -/* DD */ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, -/* DE */ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, -/* DF */ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, -/* E0 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* E1 */ 0x3c, 0x66, 0x66, 0x6c, 0x66, 0x66, 0x6c, 0xc0, -/* E2 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* E3 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* E4 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* E5 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* E6 */ 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x3e, 0x60, -/* E7 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* E8 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* E9 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* EA */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* EB */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* EC */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* ED */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* EE */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* EF */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* F0 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* F1 */ 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x7e, 0x00, -/* F2 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* F3 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* F4 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* F5 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* F6 */ 0x00, 0x18, 0x00, 0xff, 0x00, 0x18, 0x00, 0x00, -/* F7 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* F8 */ 0x3c, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, -/* F9 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* FA */ 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, -/* FB */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* FC */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* FD */ 0x38, 0x04, 0x18, 0x20, 0x3c, 0x00, 0x00, 0x00, -/* FE */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, -/* FF */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -const struct font_desc font_acorn_8x8 = { - .idx = ACORN8x8_IDX, - .name = "Acorn8x8", - .width = 8, - .height = 8, - .data = acorndata_8x8, -#ifdef CONFIG_ARCH_ACORN - .pref = 20, -#else - .pref = 0, -#endif -}; diff --git a/drivers/video/console/font_mini_4x6.c b/drivers/video/console/font_mini_4x6.c deleted file mode 100644 index 838caa1cfef7..000000000000 --- a/drivers/video/console/font_mini_4x6.c +++ /dev/null @@ -1,2158 +0,0 @@ - -/* Hand composed "Minuscule" 4x6 font, with binary data generated using - * Perl stub. - * - * Use 'perl -x mini_4x6.c < mini_4x6.c > new_version.c' to regenerate - * binary data. - * - * Created by Kenneth Albanowski. - * No rights reserved, released to the public domain. - * - * Version 1.0 - */ - -/* - -#!/usr/bin/perl -pn - -s{((0x)?[0-9a-fA-F]+)(.*\[([\*\ ]{4})\])}{ - - ($num,$pat,$bits) = ($1,$3,$4); - - $bits =~ s/([^\s0])|(.)/ defined($1) + 0 /ge; - - $num = ord(pack("B8", $bits)); - $num |= $num >> 4; - $num = sprintf("0x%.2x", $num); - - #print "$num,$pat,$bits\n"; - - $num . $pat; -}ge; - -__END__; -*/ - -/* Note: binary data consists of one byte for each row of each character top - to bottom, character 0 to character 255, six bytes per character. Each - byte contains the same four character bits in both nybbles. - MSBit to LSBit = left to right. - */ - -#include - -#define FONTDATAMAX 1536 - -static const unsigned char fontdata_mini_4x6[FONTDATAMAX] = { - - /*{*/ - /* Char 0: ' ' */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 1: ' ' */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 2: ' ' */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 3: ' ' */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 4: ' ' */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 5: ' ' */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 6: ' ' */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 7: ' ' */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 8: ' ' */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 9: ' ' */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 10: '' */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 11: ' ' */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 12: ' ' */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 13: ' ' */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 14: ' ' */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 15: ' ' */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 16: ' ' */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 17: ' ' */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 18: ' ' */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 19: ' ' */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 20: ' ' */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 21: ' ' */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 22: ' ' */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 23: ' ' */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 24: ' ' */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 25: ' ' */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 26: ' ' */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 27: ' ' */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 28: ' ' */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 29: ' ' */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 30: ' ' */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 31: ' ' */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 32: ' ' */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 33: '!' */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x00, /*= [ ] */ - 0x44, /*= [ * ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 34: '"' */ - 0xaa, /*= [* * ] */ - 0xaa, /*= [* * ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 35: '#' */ - 0xaa, /*= [* * ] */ - 0xff, /*= [****] */ - 0xff, /*= [****] */ - 0xaa, /*= [* * ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 36: '$' */ - 0x44, /*= [ * ] */ - 0x66, /*= [ ** ] */ - 0xee, /*= [*** ] */ - 0xcc, /*= [** ] */ - 0x44, /*= [ * ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 37: '%' */ - 0xaa, /*= [* * ] */ - 0x22, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x88, /*= [* ] */ - 0xaa, /*= [* * ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 38: '&' */ - 0x66, /*= [ ** ] */ - 0x99, /*= [* *] */ - 0x66, /*= [ ** ] */ - 0xaa, /*= [* * ] */ - 0xdd, /*= [** *] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 39: ''' */ - 0x22, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 40: '(' */ - 0x22, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x22, /*= [ * ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 41: ')' */ - 0x44, /*= [ * ] */ - 0x22, /*= [ * ] */ - 0x22, /*= [ * ] */ - 0x22, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 42: '*' */ - 0x00, /*= [ ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 43: '+' */ - 0x00, /*= [ ] */ - 0x44, /*= [ * ] */ - 0xee, /*= [*** ] */ - 0x44, /*= [ * ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 44: ',' */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x44, /*= [ * ] */ - 0x88, /*= [* ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 45: '-' */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 46: '.' */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x44, /*= [ * ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 47: '/' */ - 0x00, /*= [ ] */ - 0x22, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x88, /*= [* ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 48: '0' */ - 0x44, /*= [ * ] */ - 0xaa, /*= [* * ] */ - 0xaa, /*= [* * ] */ - 0xaa, /*= [* * ] */ - 0x44, /*= [ * ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 49: '1' */ - 0x44, /*= [ * ] */ - 0xcc, /*= [** ] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 50: '2' */ - 0xcc, /*= [** ] */ - 0x22, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x88, /*= [* ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ - /* Char 51: '3' */ - 0xee, /*= [*** ] */ - 0x22, /*= [ * ] */ - 0x66, /*= [ ** ] */ - 0x22, /*= [ * ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 52: '4' */ - 0xaa, /*= [* * ] */ - 0xaa, /*= [* * ] */ - 0xee, /*= [*** ] */ - 0x22, /*= [ * ] */ - 0x22, /*= [ * ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 53: '5' */ - 0xee, /*= [*** ] */ - 0x88, /*= [* ] */ - 0xee, /*= [*** ] */ - 0x22, /*= [ * ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 54: '6' */ - 0xee, /*= [*** ] */ - 0x88, /*= [* ] */ - 0xee, /*= [*** ] */ - 0xaa, /*= [* * ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 55: '7' */ - 0xee, /*= [*** ] */ - 0x22, /*= [ * ] */ - 0x22, /*= [ * ] */ - 0x22, /*= [ * ] */ - 0x22, /*= [ * ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 56: '8' */ - 0xee, /*= [*** ] */ - 0xaa, /*= [* * ] */ - 0xee, /*= [*** ] */ - 0xaa, /*= [* * ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 57: '9' */ - 0xee, /*= [*** ] */ - 0xaa, /*= [* * ] */ - 0xee, /*= [*** ] */ - 0x22, /*= [ * ] */ - 0x22, /*= [ * ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 58: ':' */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x44, /*= [ * ] */ - 0x00, /*= [ ] */ - 0x44, /*= [ * ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 59: ';' */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x44, /*= [ * ] */ - 0x00, /*= [ ] */ - 0x44, /*= [ * ] */ - 0x88, /*= [* ] */ - /*}*/ - /*{*/ /* Char 60: '<' */ - 0x22, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x88, /*= [* ] */ - 0x44, /*= [ * ] */ - 0x22, /*= [ * ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 61: '=' */ - 0x00, /*= [ ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 62: '>' */ - 0x88, /*= [* ] */ - 0x44, /*= [ * ] */ - 0x22, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x88, /*= [* ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 63: '?' */ - 0xee, /*= [*** ] */ - 0x22, /*= [ * ] */ - 0x66, /*= [ ** ] */ - 0x00, /*= [ ] */ - 0x44, /*= [ * ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 64: '@' */ - 0x44, /*= [ * ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x88, /*= [* ] */ - 0x44, /*= [ * ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 65: 'A' */ - 0x44, /*= [ * ] */ - 0xaa, /*= [* * ] */ - 0xee, /*= [*** ] */ - 0xaa, /*= [* * ] */ - 0xaa, /*= [* * ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 66: 'B' */ - 0xcc, /*= [** ] */ - 0xaa, /*= [* * ] */ - 0xcc, /*= [** ] */ - 0xaa, /*= [* * ] */ - 0xcc, /*= [** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 67: 'C' */ - 0x66, /*= [ ** ] */ - 0x88, /*= [* ] */ - 0x88, /*= [* ] */ - 0x88, /*= [* ] */ - 0x66, /*= [ ** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 68: 'D' */ - 0xcc, /*= [** ] */ - 0xaa, /*= [* * ] */ - 0xaa, /*= [* * ] */ - 0xaa, /*= [* * ] */ - 0xcc, /*= [** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 69: 'E' */ - 0xee, /*= [*** ] */ - 0x88, /*= [* ] */ - 0xee, /*= [*** ] */ - 0x88, /*= [* ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 70: 'F' */ - 0xee, /*= [*** ] */ - 0x88, /*= [* ] */ - 0xee, /*= [*** ] */ - 0x88, /*= [* ] */ - 0x88, /*= [* ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 71: 'G' */ - 0x66, /*= [ ** ] */ - 0x88, /*= [* ] */ - 0xee, /*= [*** ] */ - 0xaa, /*= [* * ] */ - 0x66, /*= [ ** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 72: 'H' */ - 0xaa, /*= [* * ] */ - 0xaa, /*= [* * ] */ - 0xee, /*= [*** ] */ - 0xaa, /*= [* * ] */ - 0xaa, /*= [* * ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 73: 'I' */ - 0xee, /*= [*** ] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 74: 'J' */ - 0x22, /*= [ * ] */ - 0x22, /*= [ * ] */ - 0x22, /*= [ * ] */ - 0xaa, /*= [* * ] */ - 0x44, /*= [ * ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 75: 'K' */ - 0xaa, /*= [* * ] */ - 0xaa, /*= [* * ] */ - 0xcc, /*= [** ] */ - 0xaa, /*= [* * ] */ - 0xaa, /*= [* * ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 76: 'L' */ - 0x88, /*= [* ] */ - 0x88, /*= [* ] */ - 0x88, /*= [* ] */ - 0x88, /*= [* ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 77: 'M' */ - 0xaa, /*= [* * ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xaa, /*= [* * ] */ - 0xaa, /*= [* * ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 78: 'N' */ - 0xaa, /*= [* * ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xaa, /*= [* * ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 79: 'O' */ - 0x44, /*= [ * ] */ - 0xaa, /*= [* * ] */ - 0xaa, /*= [* * ] */ - 0xaa, /*= [* * ] */ - 0x44, /*= [ * ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 80: 'P' */ - 0xcc, /*= [** ] */ - 0xaa, /*= [* * ] */ - 0xcc, /*= [** ] */ - 0x88, /*= [* ] */ - 0x88, /*= [* ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 81: 'Q' */ - 0x44, /*= [ * ] */ - 0xaa, /*= [* * ] */ - 0xaa, /*= [* * ] */ - 0xee, /*= [*** ] */ - 0x66, /*= [ ** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 82: 'R' */ - 0xcc, /*= [** ] */ - 0xaa, /*= [* * ] */ - 0xee, /*= [*** ] */ - 0xcc, /*= [** ] */ - 0xaa, /*= [* * ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 83: 'S' */ - 0x66, /*= [ ** ] */ - 0x88, /*= [* ] */ - 0x44, /*= [ * ] */ - 0x22, /*= [ * ] */ - 0xcc, /*= [** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 84: 'T' */ - 0xee, /*= [*** ] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 85: 'U' */ - 0xaa, /*= [* * ] */ - 0xaa, /*= [* * ] */ - 0xaa, /*= [* * ] */ - 0xaa, /*= [* * ] */ - 0x66, /*= [ ** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 86: 'V' */ - 0xaa, /*= [* * ] */ - 0xaa, /*= [* * ] */ - 0xaa, /*= [* * ] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 87: 'W' */ - 0xaa, /*= [* * ] */ - 0xaa, /*= [* * ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xaa, /*= [* * ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 88: 'X' */ - 0xaa, /*= [* * ] */ - 0xaa, /*= [* * ] */ - 0x44, /*= [ * ] */ - 0xaa, /*= [* * ] */ - 0xaa, /*= [* * ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 89: 'Y' */ - 0xaa, /*= [* * ] */ - 0xaa, /*= [* * ] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 90: 'Z' */ - 0xee, /*= [*** ] */ - 0x22, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x88, /*= [* ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 91: '[' */ - 0x66, /*= [ ** ] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x66, /*= [ ** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 92: '\' */ - 0x00, /*= [ ] */ - 0x88, /*= [* ] */ - 0x44, /*= [ * ] */ - 0x22, /*= [ * ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 93: ']' */ - 0x66, /*= [ ** ] */ - 0x22, /*= [ * ] */ - 0x22, /*= [ * ] */ - 0x22, /*= [ * ] */ - 0x66, /*= [ ** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 94: '^' */ - 0x44, /*= [ * ] */ - 0xaa, /*= [* * ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 95: '_' */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0xff, /*= [****] */ - /*}*/ - /*{*/ /* Char 96: '`' */ - 0x88, /*= [* ] */ - 0x44, /*= [ * ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 97: 'a' */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x66, /*= [ ** ] */ - 0xaa, /*= [* * ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 98: 'b' */ - 0x88, /*= [* ] */ - 0x88, /*= [* ] */ - 0xcc, /*= [** ] */ - 0xaa, /*= [* * ] */ - 0xcc, /*= [** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 99: 'c' */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x66, /*= [ ** ] */ - 0x88, /*= [* ] */ - 0x66, /*= [ ** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 100: 'd' */ - 0x22, /*= [ * ] */ - 0x22, /*= [ * ] */ - 0x66, /*= [ ** ] */ - 0xaa, /*= [* * ] */ - 0x66, /*= [ ** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 101: 'e' */ - 0x00, /*= [ ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x88, /*= [* ] */ - 0x66, /*= [ ** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 102: 'f' */ - 0x22, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0xee, /*= [*** ] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 103: 'g' */ - 0x00, /*= [ ] */ - 0x66, /*= [ ** ] */ - 0xaa, /*= [* * ] */ - 0x66, /*= [ ** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 104: 'h' */ - 0x88, /*= [* ] */ - 0x88, /*= [* ] */ - 0xcc, /*= [** ] */ - 0xaa, /*= [* * ] */ - 0xaa, /*= [* * ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 105: 'i' */ - 0x44, /*= [ * ] */ - 0x00, /*= [ ] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 106: 'j' */ - 0x44, /*= [ * ] */ - 0x00, /*= [ ] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x88, /*= [* ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 107: 'k' */ - 0x00, /*= [ ] */ - 0x88, /*= [* ] */ - 0xaa, /*= [* * ] */ - 0xcc, /*= [** ] */ - 0xaa, /*= [* * ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 108: 'l' */ - 0x00, /*= [ ] */ - 0xcc, /*= [** ] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 109: 'm' */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xaa, /*= [* * ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 110: 'n' */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0xcc, /*= [** ] */ - 0xaa, /*= [* * ] */ - 0xaa, /*= [* * ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 111: 'o' */ - 0x00, /*= [ ] */ - 0x44, /*= [ * ] */ - 0xaa, /*= [* * ] */ - 0xaa, /*= [* * ] */ - 0x44, /*= [ * ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 112: 'p' */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0xcc, /*= [** ] */ - 0xaa, /*= [* * ] */ - 0xcc, /*= [** ] */ - 0x88, /*= [* ] */ - /*}*/ - /*{*/ /* Char 113: 'q' */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x66, /*= [ ** ] */ - 0xaa, /*= [* * ] */ - 0x66, /*= [ ** ] */ - 0x22, /*= [ * ] */ - /*}*/ - /*{*/ /* Char 114: 'r' */ - 0x00, /*= [ ] */ - 0xcc, /*= [** ] */ - 0xaa, /*= [* * ] */ - 0x88, /*= [* ] */ - 0x88, /*= [* ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 115: 's' */ - 0x00, /*= [ ] */ - 0x66, /*= [ ** ] */ - 0xcc, /*= [** ] */ - 0x22, /*= [ * ] */ - 0xcc, /*= [** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 116: 't' */ - 0x00, /*= [ ] */ - 0x44, /*= [ * ] */ - 0xee, /*= [*** ] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 117: 'u' */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0xaa, /*= [* * ] */ - 0xaa, /*= [* * ] */ - 0x66, /*= [ ** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 118: 'v' */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0xaa, /*= [* * ] */ - 0xee, /*= [*** ] */ - 0x44, /*= [ * ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 119: 'w' */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0xaa, /*= [* * ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 120: 'x' */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0xaa, /*= [* * ] */ - 0x44, /*= [ * ] */ - 0xaa, /*= [* * ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 121: 'y' */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0xaa, /*= [* * ] */ - 0xee, /*= [*** ] */ - 0x22, /*= [ * ] */ - 0xcc, /*= [** ] */ - /*}*/ - /*{*/ /* Char 122: 'z' */ - 0x00, /*= [ ] */ - 0xee, /*= [*** ] */ - 0x66, /*= [ ** ] */ - 0xcc, /*= [** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 123: '{' */ - 0x22, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0xcc, /*= [** ] */ - 0x44, /*= [ * ] */ - 0x22, /*= [ * ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 124: '|' */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 125: '}' */ - 0x88, /*= [* ] */ - 0x44, /*= [ * ] */ - 0x66, /*= [ ** ] */ - 0x44, /*= [ * ] */ - 0x88, /*= [* ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 126: '~' */ - 0x55, /*= [ * *] */ - 0xaa, /*= [* * ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 127: '' */ - 0x44, /*= [ * ] */ - 0xaa, /*= [* * ] */ - 0xaa, /*= [* * ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 128: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 129: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 130: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 131: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 132: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 133: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 134: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 135: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 136: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 137: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 138: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 139: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 140: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 141: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 142: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 143: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 144: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 145: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 146: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 147: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 148: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 149: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 150: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 151: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 152: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 153: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 154: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 155: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 156: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 157: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 158: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 159: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 160: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 161: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 162: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 163: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 164: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 165: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 166: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 167: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 168: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 169: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 170: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 171: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 172: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 173: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 174: */ - 0x00, /*= [ ] */ - 0x66, /*= [ ** ] */ - 0xcc, /*= [** ] */ - 0x66, /*= [ ** ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 175: */ - 0x00, /*= [ ] */ - 0xcc, /*= [** ] */ - 0x66, /*= [ ** ] */ - 0xcc, /*= [** ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 176: */ - 0x88, /*= [* ] */ - 0x22, /*= [ * ] */ - 0x88, /*= [* ] */ - 0x22, /*= [ * ] */ - 0x88, /*= [* ] */ - 0x22, /*= [ * ] */ - /*}*/ - /*{*/ /* Char 177: */ - 0xaa, /*= [* * ] */ - 0x55, /*= [ * *] */ - 0xaa, /*= [* * ] */ - 0x55, /*= [ * *] */ - 0xaa, /*= [* * ] */ - 0x55, /*= [ * *] */ - /*}*/ - /*{*/ /* Char 178: */ - 0xdd, /*= [** *] */ - 0xbb, /*= [* **] */ - 0xdd, /*= [** *] */ - 0xbb, /*= [* **] */ - 0xdd, /*= [** *] */ - 0xbb, /*= [* **] */ - /*}*/ - /*{*/ /* Char 179: */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - /*}*/ - /*{*/ /* Char 180: */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0xcc, /*= [** ] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - /*}*/ - /*{*/ /* Char 181: */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0xcc, /*= [** ] */ - 0xcc, /*= [** ] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - /*}*/ - /*{*/ /* Char 182: */ - 0x66, /*= [ ** ] */ - 0x66, /*= [ ** ] */ - 0xee, /*= [*** ] */ - 0x66, /*= [ ** ] */ - 0x66, /*= [ ** ] */ - 0x66, /*= [ ** ] */ - /*}*/ - /*{*/ /* Char 183: */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0xee, /*= [*** ] */ - 0x66, /*= [ ** ] */ - 0x66, /*= [ ** ] */ - 0x66, /*= [ ** ] */ - /*}*/ - /*{*/ /* Char 184: */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0xcc, /*= [** ] */ - 0xcc, /*= [** ] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - /*}*/ - /*{*/ /* Char 185: */ - 0x66, /*= [ ** ] */ - 0x66, /*= [ ** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x66, /*= [ ** ] */ - 0x66, /*= [ ** ] */ - /*}*/ - /*{*/ /* Char 186: */ - 0x66, /*= [ ** ] */ - 0x66, /*= [ ** ] */ - 0x66, /*= [ ** ] */ - 0x66, /*= [ ** ] */ - 0x66, /*= [ ** ] */ - 0x66, /*= [ ** ] */ - /*}*/ - /*{*/ /* Char 187: */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x66, /*= [ ** ] */ - 0x66, /*= [ ** ] */ - /*}*/ - /*{*/ /* Char 188: */ - 0x66, /*= [ ** ] */ - 0x66, /*= [ ** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 189: */ - 0x66, /*= [ ** ] */ - 0x66, /*= [ ** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 190: */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0xcc, /*= [** ] */ - 0xcc, /*= [** ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 191: */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0xcc, /*= [** ] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - /*}*/ - /*{*/ /* Char 192: */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x77, /*= [ ***] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 193: */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0xff, /*= [****] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 194: */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0xff, /*= [****] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - /*}*/ - /*{*/ /* Char 195: */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x77, /*= [ ***] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - /*}*/ - /*{*/ /* Char 196: */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0xff, /*= [****] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 197: */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0xff, /*= [****] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - /*}*/ - /*{*/ /* Char 198: */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x77, /*= [ ***] */ - 0x77, /*= [ ***] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - /*}*/ - /*{*/ /* Char 199: */ - 0x66, /*= [ ** ] */ - 0x66, /*= [ ** ] */ - 0x77, /*= [ ***] */ - 0x66, /*= [ ** ] */ - 0x66, /*= [ ** ] */ - 0x66, /*= [ ** ] */ - /*}*/ - /*{*/ /* Char 200: */ - 0x66, /*= [ ** ] */ - 0x66, /*= [ ** ] */ - 0x77, /*= [ ***] */ - 0x77, /*= [ ***] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 201: */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x77, /*= [ ***] */ - 0x77, /*= [ ***] */ - 0x66, /*= [ ** ] */ - 0x66, /*= [ ** ] */ - /*}*/ - /*{*/ /* Char 202: */ - 0x66, /*= [ ** ] */ - 0x66, /*= [ ** ] */ - 0xff, /*= [****] */ - 0xff, /*= [****] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 203: */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0xff, /*= [****] */ - 0xff, /*= [****] */ - 0x66, /*= [ ** ] */ - 0x66, /*= [ ** ] */ - /*}*/ - /*{*/ /* Char 204: */ - 0x66, /*= [ ** ] */ - 0x66, /*= [ ** ] */ - 0x77, /*= [ ***] */ - 0x77, /*= [ ***] */ - 0x66, /*= [ ** ] */ - 0x66, /*= [ ** ] */ - /*}*/ - /*{*/ /* Char 205: */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0xff, /*= [****] */ - 0xff, /*= [****] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 206: */ - 0x66, /*= [ ** ] */ - 0x66, /*= [ ** ] */ - 0xff, /*= [****] */ - 0xff, /*= [****] */ - 0x66, /*= [ ** ] */ - 0x66, /*= [ ** ] */ - /*}*/ - /*{*/ /* Char 207: */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0xff, /*= [****] */ - 0xff, /*= [****] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 208: */ - 0x66, /*= [ ** ] */ - 0x66, /*= [ ** ] */ - 0xff, /*= [****] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 209: */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0xff, /*= [****] */ - 0xff, /*= [****] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - /*}*/ - /*{*/ /* Char 210: */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0xff, /*= [****] */ - 0x66, /*= [ ** ] */ - 0x66, /*= [ ** ] */ - 0x66, /*= [ ** ] */ - /*}*/ - /*{*/ /* Char 211: */ - 0x66, /*= [ ** ] */ - 0x66, /*= [ ** ] */ - 0x77, /*= [ ***] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 212: */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x77, /*= [ ***] */ - 0x77, /*= [ ***] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 213: */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x77, /*= [ ***] */ - 0x77, /*= [ ***] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - /*}*/ - /*{*/ /* Char 214: */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x77, /*= [ ***] */ - 0x66, /*= [ ** ] */ - 0x66, /*= [ ** ] */ - 0x66, /*= [ ** ] */ - /*}*/ - /*{*/ /* Char 215: */ - 0x66, /*= [ ** ] */ - 0x66, /*= [ ** ] */ - 0xff, /*= [****] */ - 0x66, /*= [ ** ] */ - 0x66, /*= [ ** ] */ - 0x66, /*= [ ** ] */ - /*}*/ - /*{*/ /* Char 216: */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0xff, /*= [****] */ - 0xff, /*= [****] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - /*}*/ - /*{*/ /* Char 217: */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0xcc, /*= [** ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 218: */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x77, /*= [ ***] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - 0x44, /*= [ * ] */ - /*}*/ - /*{*/ /* Char 219: */ - 0xff, /*= [****] */ - 0xff, /*= [****] */ - 0xff, /*= [****] */ - 0xff, /*= [****] */ - 0xff, /*= [****] */ - 0xff, /*= [****] */ - /*}*/ - /*{*/ /* Char 220: */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0xff, /*= [****] */ - 0xff, /*= [****] */ - 0xff, /*= [****] */ - /*}*/ - /*{*/ /* Char 221: */ - 0xcc, /*= [** ] */ - 0xcc, /*= [** ] */ - 0xcc, /*= [** ] */ - 0xcc, /*= [** ] */ - 0xcc, /*= [** ] */ - 0xcc, /*= [** ] */ - /*}*/ - /*{*/ /* Char 222: */ - 0x33, /*= [ **] */ - 0x33, /*= [ **] */ - 0x33, /*= [ **] */ - 0x33, /*= [ **] */ - 0x33, /*= [ **] */ - 0x33, /*= [ **] */ - /*}*/ - /*{*/ /* Char 223: */ - 0xff, /*= [****] */ - 0xff, /*= [****] */ - 0xff, /*= [****] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 224: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 225: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 226: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 227: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 228: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 229: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 230: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 231: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 232: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 233: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 234: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 235: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 236: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 237: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 238: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 239: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 240: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 241: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 242: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 243: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 244: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 245: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 246: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 247: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 248: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 249: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 250: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 251: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 252: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 253: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 254: */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - 0x66, /*= [ ** ] */ - 0x66, /*= [ ** ] */ - 0x00, /*= [ ] */ - 0x00, /*= [ ] */ - /*}*/ - /*{*/ /* Char 255: */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0xee, /*= [*** ] */ - 0x00, /*= [ ] */ - /*}*/ -}; - -const struct font_desc font_mini_4x6 = { - .idx = MINI4x6_IDX, - .name = "MINI4x6", - .width = 4, - .height = 6, - .data = fontdata_mini_4x6, - .pref = 3, -}; - diff --git a/drivers/video/console/font_pearl_8x8.c b/drivers/video/console/font_pearl_8x8.c deleted file mode 100644 index dc6ad539ca4e..000000000000 --- a/drivers/video/console/font_pearl_8x8.c +++ /dev/null @@ -1,2587 +0,0 @@ -/**********************************************/ -/* */ -/* Font file generated by cpi2fnt */ -/* ------------------------------ */ -/* Combined with the alpha-numeric */ -/* portion of Greg Harp's old PEARL */ -/* font (from earlier versions of */ -/* linux-m86k) by John Shifflett */ -/* */ -/**********************************************/ - -#include - -#define FONTDATAMAX 2048 - -static const unsigned char fontdata_pearl8x8[FONTDATAMAX] = { - - /* 0 0x00 '^@' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 1 0x01 '^A' */ - 0x7e, /* 01111110 */ - 0x81, /* 10000001 */ - 0xa5, /* 10100101 */ - 0x81, /* 10000001 */ - 0xbd, /* 10111101 */ - 0x99, /* 10011001 */ - 0x81, /* 10000001 */ - 0x7e, /* 01111110 */ - - /* 2 0x02 '^B' */ - 0x7e, /* 01111110 */ - 0xff, /* 11111111 */ - 0xdb, /* 11011011 */ - 0xff, /* 11111111 */ - 0xc3, /* 11000011 */ - 0xe7, /* 11100111 */ - 0xff, /* 11111111 */ - 0x7e, /* 01111110 */ - - /* 3 0x03 '^C' */ - 0x6c, /* 01101100 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0x7c, /* 01111100 */ - 0x38, /* 00111000 */ - 0x10, /* 00010000 */ - 0x00, /* 00000000 */ - - /* 4 0x04 '^D' */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x7c, /* 01111100 */ - 0xfe, /* 11111110 */ - 0x7c, /* 01111100 */ - 0x38, /* 00111000 */ - 0x10, /* 00010000 */ - 0x00, /* 00000000 */ - - /* 5 0x05 '^E' */ - 0x38, /* 00111000 */ - 0x7c, /* 01111100 */ - 0x38, /* 00111000 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0xd6, /* 11010110 */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - - /* 6 0x06 '^F' */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x7c, /* 01111100 */ - 0xfe, /* 11111110 */ - 0xfe, /* 11111110 */ - 0x7c, /* 01111100 */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - - /* 7 0x07 '^G' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 8 0x08 '^H' */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xe7, /* 11100111 */ - 0xc3, /* 11000011 */ - 0xc3, /* 11000011 */ - 0xe7, /* 11100111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - - /* 9 0x09 '^I' */ - 0x00, /* 00000000 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x42, /* 01000010 */ - 0x42, /* 01000010 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 10 0x0a '^J' */ - 0xff, /* 11111111 */ - 0xc3, /* 11000011 */ - 0x99, /* 10011001 */ - 0xbd, /* 10111101 */ - 0xbd, /* 10111101 */ - 0x99, /* 10011001 */ - 0xc3, /* 11000011 */ - 0xff, /* 11111111 */ - - /* 11 0x0b '^K' */ - 0x0f, /* 00001111 */ - 0x07, /* 00000111 */ - 0x0f, /* 00001111 */ - 0x7d, /* 01111101 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x78, /* 01111000 */ - - /* 12 0x0c '^L' */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - - /* 13 0x0d '^M' */ - 0x3f, /* 00111111 */ - 0x33, /* 00110011 */ - 0x3f, /* 00111111 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x70, /* 01110000 */ - 0xf0, /* 11110000 */ - 0xe0, /* 11100000 */ - - /* 14 0x0e '^N' */ - 0x7f, /* 01111111 */ - 0x63, /* 01100011 */ - 0x7f, /* 01111111 */ - 0x63, /* 01100011 */ - 0x63, /* 01100011 */ - 0x67, /* 01100111 */ - 0xe6, /* 11100110 */ - 0xc0, /* 11000000 */ - - /* 15 0x0f '^O' */ - 0x18, /* 00011000 */ - 0xdb, /* 11011011 */ - 0x3c, /* 00111100 */ - 0xe7, /* 11100111 */ - 0xe7, /* 11100111 */ - 0x3c, /* 00111100 */ - 0xdb, /* 11011011 */ - 0x18, /* 00011000 */ - - /* 16 0x10 '^P' */ - 0x80, /* 10000000 */ - 0xe0, /* 11100000 */ - 0xf8, /* 11111000 */ - 0xfe, /* 11111110 */ - 0xf8, /* 11111000 */ - 0xe0, /* 11100000 */ - 0x80, /* 10000000 */ - 0x00, /* 00000000 */ - - /* 17 0x11 '^Q' */ - 0x02, /* 00000010 */ - 0x0e, /* 00001110 */ - 0x3e, /* 00111110 */ - 0xfe, /* 11111110 */ - 0x3e, /* 00111110 */ - 0x0e, /* 00001110 */ - 0x02, /* 00000010 */ - 0x00, /* 00000000 */ - - /* 18 0x12 '^R' */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - - /* 19 0x13 '^S' */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - - /* 20 0x14 '^T' */ - 0x7f, /* 01111111 */ - 0xdb, /* 11011011 */ - 0xdb, /* 11011011 */ - 0x7b, /* 01111011 */ - 0x1b, /* 00011011 */ - 0x1b, /* 00011011 */ - 0x1b, /* 00011011 */ - 0x00, /* 00000000 */ - - /* 21 0x15 '^U' */ - 0x3e, /* 00111110 */ - 0x61, /* 01100001 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x86, /* 10000110 */ - 0x7c, /* 01111100 */ - - /* 22 0x16 '^V' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x7e, /* 01111110 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - - /* 23 0x17 '^W' */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0xff, /* 11111111 */ - - /* 24 0x18 '^X' */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 25 0x19 '^Y' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 26 0x1a '^Z' */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0xfe, /* 11111110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 27 0x1b '^[' */ - 0x00, /* 00000000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xfe, /* 11111110 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 28 0x1c '^\' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 29 0x1d '^]' */ - 0x00, /* 00000000 */ - 0x24, /* 00100100 */ - 0x66, /* 01100110 */ - 0xff, /* 11111111 */ - 0x66, /* 01100110 */ - 0x24, /* 00100100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 30 0x1e '^^' */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x7e, /* 01111110 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 31 0x1f '^_' */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0x7e, /* 01111110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 32 0x20 ' ' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 33 0x21 '!' */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 34 0x22 '"' */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 35 0x23 '#' */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0xfe, /* 11111110 */ - 0x6c, /* 01101100 */ - 0xfe, /* 11111110 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x00, /* 00000000 */ - - /* 36 0x24 '$' */ - 0x18, /* 00011000 */ - 0x3e, /* 00111110 */ - 0x60, /* 01100000 */ - 0x3c, /* 00111100 */ - 0x06, /* 00000110 */ - 0x7c, /* 01111100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 37 0x25 '%' */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xcc, /* 11001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x66, /* 01100110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 38 0x26 '&' */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x68, /* 01101000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 39 0x27 ''' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 40 0x28 '(' */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x00, /* 00000000 */ - - /* 41 0x29 ')' */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - - /* 42 0x2a '*' */ - 0x00, /* 00000000 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0xff, /* 11111111 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 43 0x2b '+' */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 44 0x2c ',' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - - /* 45 0x2d '-' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 46 0x2e '.' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 47 0x2f '/' */ - 0x03, /* 00000011 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xc0, /* 11000000 */ - 0x00, /* 00000000 */ - - /* 48 0x30 '0' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xde, /* 11011110 */ - 0xfe, /* 11111110 */ - 0xf6, /* 11110110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 49 0x31 '1' */ - 0x18, /* 00011000 */ - 0x78, /* 01111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 50 0x32 '2' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - - /* 51 0x33 '3' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0x06, /* 00000110 */ - 0x1c, /* 00011100 */ - 0x06, /* 00000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 52 0x34 '4' */ - 0x1c, /* 00011100 */ - 0x3c, /* 00111100 */ - 0x6c, /* 01101100 */ - 0xcc, /* 11001100 */ - 0xfe, /* 11111110 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x00, /* 00000000 */ - - /* 53 0x35 '5' */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0xfc, /* 11111100 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 54 0x36 '6' */ - 0x38, /* 00111000 */ - 0x60, /* 01100000 */ - 0xc0, /* 11000000 */ - 0xfc, /* 11111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 55 0x37 '7' */ - 0xfe, /* 11111110 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x00, /* 00000000 */ - - /* 56 0x38 '8' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 57 0x39 '9' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7e, /* 01111110 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - - /* 58 0x3a ':' */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 59 0x3b ';' */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - - /* 60 0x3c '<' */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x00, /* 00000000 */ - - /* 61 0x3d '=' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 62 0x3e '>' */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - - /* 63 0x3f '?' */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 64 0x40 '@' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xde, /* 11011110 */ - 0xde, /* 11011110 */ - 0xde, /* 11011110 */ - 0xc0, /* 11000000 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 65 0x41 'A' */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 66 0x42 'B' */ - 0xfc, /* 11111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xfc, /* 11111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xfc, /* 11111100 */ - 0x00, /* 00000000 */ - - /* 67 0x43 'C' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 68 0x44 'D' */ - 0xfc, /* 11111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xfc, /* 11111100 */ - 0x00, /* 00000000 */ - - /* 69 0x45 'E' */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xf8, /* 11111000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - - /* 70 0x46 'F' */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xf8, /* 11111000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0x00, /* 00000000 */ - - /* 71 0x47 'G' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc0, /* 11000000 */ - 0xce, /* 11001110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 72 0x48 'H' */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 73 0x49 'I' */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - - /* 74 0x4a 'J' */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 75 0x4b 'K' */ - 0xc6, /* 11000110 */ - 0xcc, /* 11001100 */ - 0xd8, /* 11011000 */ - 0xf0, /* 11110000 */ - 0xd8, /* 11011000 */ - 0xcc, /* 11001100 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 76 0x4c 'L' */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - - /* 77 0x4d 'M' */ - 0x82, /* 10000010 */ - 0xc6, /* 11000110 */ - 0xee, /* 11101110 */ - 0xfe, /* 11111110 */ - 0xd6, /* 11010110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 78 0x4e 'N' */ - 0xc6, /* 11000110 */ - 0xe6, /* 11100110 */ - 0xf6, /* 11110110 */ - 0xde, /* 11011110 */ - 0xce, /* 11001110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 79 0x4f 'O' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 80 0x50 'P' */ - 0xfc, /* 11111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xfc, /* 11111100 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0x00, /* 00000000 */ - - /* 81 0x51 'Q' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xf6, /* 11110110 */ - 0xde, /* 11011110 */ - 0x7c, /* 01111100 */ - 0x06, /* 00000110 */ - - /* 82 0x52 'R' */ - 0xfc, /* 11111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xfc, /* 11111100 */ - 0xd8, /* 11011000 */ - 0xcc, /* 11001100 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 83 0x53 'S' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0x60, /* 01100000 */ - 0x38, /* 00111000 */ - 0x0c, /* 00001100 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 84 0x54 'T' */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 85 0x55 'U' */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 86 0x56 'V' */ - 0xc3, /* 11000011 */ - 0xc3, /* 11000011 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 87 0x57 'W' */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xd6, /* 11010110 */ - 0xfe, /* 11111110 */ - 0xee, /* 11101110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 88 0x58 'X' */ - 0xc3, /* 11000011 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0xc3, /* 11000011 */ - 0x00, /* 00000000 */ - - /* 89 0x59 'Y' */ - 0xc3, /* 11000011 */ - 0xc3, /* 11000011 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 90 0x5a 'Z' */ - 0xfe, /* 11111110 */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - - /* 91 0x5b '[' */ - 0x3c, /* 00111100 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 92 0x5c '\' */ - 0xc0, /* 11000000 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x06, /* 00000110 */ - 0x03, /* 00000011 */ - 0x00, /* 00000000 */ - - /* 93 0x5d ']' */ - 0x3c, /* 00111100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 94 0x5e '^' */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 95 0x5f '_' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - - /* 96 0x60 '`' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 97 0x61 'a' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0x06, /* 00000110 */ - 0x7e, /* 01111110 */ - 0xc6, /* 11000110 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - - /* 98 0x62 'b' */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xfc, /* 11111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xfc, /* 11111100 */ - 0x00, /* 00000000 */ - - /* 99 0x63 'c' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 100 0x64 'd' */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x7e, /* 01111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - - /* 101 0x65 'e' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 102 0x66 'f' */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x60, /* 01100000 */ - 0xf0, /* 11110000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x60, /* 01100000 */ - 0x00, /* 00000000 */ - - /* 103 0x67 'g' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7e, /* 01111110 */ - 0x06, /* 00000110 */ - 0x7c, /* 01111100 */ - - /* 104 0x68 'h' */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xfc, /* 11111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 105 0x69 'i' */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 106 0x6a 'j' */ - 0x06, /* 00000110 */ - 0x00, /* 00000000 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - - /* 107 0x6b 'k' */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xcc, /* 11001100 */ - 0xd8, /* 11011000 */ - 0xf0, /* 11110000 */ - 0xd8, /* 11011000 */ - 0xcc, /* 11001100 */ - 0x00, /* 00000000 */ - - /* 108 0x6c 'l' */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 109 0x6d 'm' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xec, /* 11101100 */ - 0xfe, /* 11111110 */ - 0xd6, /* 11010110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 110 0x6e 'n' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfc, /* 11111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 111 0x6f 'o' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 112 0x70 'p' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfc, /* 11111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xfc, /* 11111100 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - - /* 113 0x71 'q' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7e, /* 01111110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - - /* 114 0x72 'r' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xdc, /* 11011100 */ - 0xe6, /* 11100110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0x00, /* 00000000 */ - - /* 115 0x73 's' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0xc0, /* 11000000 */ - 0x7c, /* 01111100 */ - 0x06, /* 00000110 */ - 0xfc, /* 11111100 */ - 0x00, /* 00000000 */ - - /* 116 0x74 't' */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x7c, /* 01111100 */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x36, /* 00110110 */ - 0x1c, /* 00011100 */ - 0x00, /* 00000000 */ - - /* 117 0x75 'u' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 118 0x76 'v' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - - /* 119 0x77 'w' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xd6, /* 11010110 */ - 0xfe, /* 11111110 */ - 0x6c, /* 01101100 */ - 0x00, /* 00000000 */ - - /* 120 0x78 'x' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 121 0x79 'y' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xc3, /* 11000011 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - - /* 122 0x7a 'z' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x0c, /* 00001100 */ - 0x38, /* 00111000 */ - 0x60, /* 01100000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - - /* 123 0x7b '{' */ - 0x0e, /* 00001110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x70, /* 01110000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x0e, /* 00001110 */ - 0x00, /* 00000000 */ - - /* 124 0x7c '|' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 125 0x7d '}' */ - 0x70, /* 01110000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x0e, /* 00001110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x70, /* 01110000 */ - 0x00, /* 00000000 */ - - /* 126 0x7e '~' */ - 0x72, /* 01110010 */ - 0x9c, /* 10011100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 127 0x7f '' */ - 0x00, /* 00000000 */ - 0x10, /* 00010000 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - - /* 128 0x80 '' */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x0c, /* 00001100 */ - 0x78, /* 01111000 */ - - /* 129 0x81 '' */ - 0xcc, /* 11001100 */ - 0x00, /* 00000000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 130 0x82 '' */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 131 0x83 '' */ - 0x7c, /* 01111100 */ - 0x82, /* 10000010 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 132 0x84 '' */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 133 0x85 '' */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 134 0x86 '' */ - 0x30, /* 00110000 */ - 0x30, /* 00110000 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 135 0x87 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0x7e, /* 01111110 */ - 0x0c, /* 00001100 */ - 0x38, /* 00111000 */ - - /* 136 0x88 '' */ - 0x7c, /* 01111100 */ - 0x82, /* 10000010 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 137 0x89 '' */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 138 0x8a '' */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 139 0x8b '' */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 140 0x8c '' */ - 0x7c, /* 01111100 */ - 0x82, /* 10000010 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 141 0x8d '' */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 142 0x8e '' */ - 0xc6, /* 11000110 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 143 0x8f '' */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 144 0x90 '' */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0xf8, /* 11111000 */ - 0xc0, /* 11000000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - - /* 145 0x91 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0xd8, /* 11011000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - - /* 146 0x92 '' */ - 0x3e, /* 00111110 */ - 0x6c, /* 01101100 */ - 0xcc, /* 11001100 */ - 0xfe, /* 11111110 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xce, /* 11001110 */ - 0x00, /* 00000000 */ - - /* 147 0x93 '' */ - 0x7c, /* 01111100 */ - 0x82, /* 10000010 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 148 0x94 '' */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 149 0x95 '' */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 150 0x96 '' */ - 0x78, /* 01111000 */ - 0x84, /* 10000100 */ - 0x00, /* 00000000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 151 0x97 '' */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 152 0x98 '' */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7e, /* 01111110 */ - 0x06, /* 00000110 */ - 0xfc, /* 11111100 */ - - /* 153 0x99 '' */ - 0xc6, /* 11000110 */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - - /* 154 0x9a '' */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 155 0x9b '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 156 0x9c '' */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x64, /* 01100100 */ - 0xf0, /* 11110000 */ - 0x60, /* 01100000 */ - 0x66, /* 01100110 */ - 0xfc, /* 11111100 */ - 0x00, /* 00000000 */ - - /* 157 0x9d '' */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 158 0x9e '' */ - 0xf8, /* 11111000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xfa, /* 11111010 */ - 0xc6, /* 11000110 */ - 0xcf, /* 11001111 */ - 0xc6, /* 11000110 */ - 0xc7, /* 11000111 */ - - /* 159 0x9f '' */ - 0x0e, /* 00001110 */ - 0x1b, /* 00011011 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0xd8, /* 11011000 */ - 0x70, /* 01110000 */ - 0x00, /* 00000000 */ - - /* 160 0xa0 '' */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x7c, /* 01111100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 161 0xa1 '' */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x38, /* 00111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 162 0xa2 '' */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - - /* 163 0xa3 '' */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 164 0xa4 '' */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x00, /* 00000000 */ - 0xdc, /* 11011100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x00, /* 00000000 */ - - /* 165 0xa5 '' */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x00, /* 00000000 */ - 0xe6, /* 11100110 */ - 0xf6, /* 11110110 */ - 0xde, /* 11011110 */ - 0xce, /* 11001110 */ - 0x00, /* 00000000 */ - - /* 166 0xa6 '' */ - 0x3c, /* 00111100 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x3e, /* 00111110 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 167 0xa7 '' */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 168 0xa8 '' */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x63, /* 01100011 */ - 0x3e, /* 00111110 */ - 0x00, /* 00000000 */ - - /* 169 0xa9 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 170 0xaa '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x06, /* 00000110 */ - 0x06, /* 00000110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 171 0xab '' */ - 0x63, /* 01100011 */ - 0xe6, /* 11100110 */ - 0x6c, /* 01101100 */ - 0x7e, /* 01111110 */ - 0x33, /* 00110011 */ - 0x66, /* 01100110 */ - 0xcc, /* 11001100 */ - 0x0f, /* 00001111 */ - - /* 172 0xac '' */ - 0x63, /* 01100011 */ - 0xe6, /* 11100110 */ - 0x6c, /* 01101100 */ - 0x7a, /* 01111010 */ - 0x36, /* 00110110 */ - 0x6a, /* 01101010 */ - 0xdf, /* 11011111 */ - 0x06, /* 00000110 */ - - /* 173 0xad '' */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 174 0xae '' */ - 0x00, /* 00000000 */ - 0x33, /* 00110011 */ - 0x66, /* 01100110 */ - 0xcc, /* 11001100 */ - 0x66, /* 01100110 */ - 0x33, /* 00110011 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 175 0xaf '' */ - 0x00, /* 00000000 */ - 0xcc, /* 11001100 */ - 0x66, /* 01100110 */ - 0x33, /* 00110011 */ - 0x66, /* 01100110 */ - 0xcc, /* 11001100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 176 0xb0 '' */ - 0x22, /* 00100010 */ - 0x88, /* 10001000 */ - 0x22, /* 00100010 */ - 0x88, /* 10001000 */ - 0x22, /* 00100010 */ - 0x88, /* 10001000 */ - 0x22, /* 00100010 */ - 0x88, /* 10001000 */ - - /* 177 0xb1 '' */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - 0x55, /* 01010101 */ - 0xaa, /* 10101010 */ - - /* 178 0xb2 '' */ - 0x77, /* 01110111 */ - 0xdd, /* 11011101 */ - 0x77, /* 01110111 */ - 0xdd, /* 11011101 */ - 0x77, /* 01110111 */ - 0xdd, /* 11011101 */ - 0x77, /* 01110111 */ - 0xdd, /* 11011101 */ - - /* 179 0xb3 '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 180 0xb4 '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 181 0xb5 '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 182 0xb6 '' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xf6, /* 11110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 183 0xb7 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 184 0xb8 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 185 0xb9 '' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xf6, /* 11110110 */ - 0x06, /* 00000110 */ - 0xf6, /* 11110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 186 0xba '' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 187 0xbb '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x06, /* 00000110 */ - 0xf6, /* 11110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 188 0xbc '' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xf6, /* 11110110 */ - 0x06, /* 00000110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 189 0xbd '' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 190 0xbe '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 191 0xbf '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xf8, /* 11111000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 192 0xc0 '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 193 0xc1 '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 194 0xc2 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 195 0xc3 '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 196 0xc4 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 197 0xc5 '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xff, /* 11111111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 198 0xc6 '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 199 0xc7 '' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x37, /* 00110111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 200 0xc8 '' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x37, /* 00110111 */ - 0x30, /* 00110000 */ - 0x3f, /* 00111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 201 0xc9 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3f, /* 00111111 */ - 0x30, /* 00110000 */ - 0x37, /* 00110111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 202 0xca '' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xf7, /* 11110111 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 203 0xcb '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0xf7, /* 11110111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 204 0xcc '' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x37, /* 00110111 */ - 0x30, /* 00110000 */ - 0x37, /* 00110111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 205 0xcd '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 206 0xce '' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xf7, /* 11110111 */ - 0x00, /* 00000000 */ - 0xf7, /* 11110111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 207 0xcf '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 208 0xd0 '' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 209 0xd1 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 210 0xd2 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 211 0xd3 '' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x3f, /* 00111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 212 0xd4 '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 213 0xd5 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 214 0xd6 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3f, /* 00111111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 215 0xd7 '' */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0xff, /* 11111111 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - - /* 216 0xd8 '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xff, /* 11111111 */ - 0x18, /* 00011000 */ - 0xff, /* 11111111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 217 0xd9 '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xf8, /* 11111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 218 0xda '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x1f, /* 00011111 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 219 0xdb '' */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - - /* 220 0xdc '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - - /* 221 0xdd '' */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - 0xf0, /* 11110000 */ - - /* 222 0xde '' */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - 0x0f, /* 00001111 */ - - /* 223 0xdf '' */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0xff, /* 11111111 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 224 0xe0 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0xc8, /* 11001000 */ - 0xdc, /* 11011100 */ - 0x76, /* 01110110 */ - 0x00, /* 00000000 */ - - /* 225 0xe1 '' */ - 0x78, /* 01111000 */ - 0xcc, /* 11001100 */ - 0xcc, /* 11001100 */ - 0xd8, /* 11011000 */ - 0xcc, /* 11001100 */ - 0xc6, /* 11000110 */ - 0xcc, /* 11001100 */ - 0x00, /* 00000000 */ - - /* 226 0xe2 '' */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0xc0, /* 11000000 */ - 0x00, /* 00000000 */ - - /* 227 0xe3 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x00, /* 00000000 */ - - /* 228 0xe4 '' */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - - /* 229 0xe5 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0xd8, /* 11011000 */ - 0xd8, /* 11011000 */ - 0xd8, /* 11011000 */ - 0x70, /* 01110000 */ - 0x00, /* 00000000 */ - - /* 230 0xe6 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x7c, /* 01111100 */ - 0xc0, /* 11000000 */ - - /* 231 0xe7 '' */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - - /* 232 0xe8 '' */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x3c, /* 00111100 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - - /* 233 0xe9 '' */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xfe, /* 11111110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - - /* 234 0xea '' */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0xee, /* 11101110 */ - 0x00, /* 00000000 */ - - /* 235 0xeb '' */ - 0x0e, /* 00001110 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x3e, /* 00111110 */ - 0x66, /* 01100110 */ - 0x66, /* 01100110 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - - /* 236 0xec '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0xdb, /* 11011011 */ - 0xdb, /* 11011011 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 237 0xed '' */ - 0x06, /* 00000110 */ - 0x0c, /* 00001100 */ - 0x7e, /* 01111110 */ - 0xdb, /* 11011011 */ - 0xdb, /* 11011011 */ - 0x7e, /* 01111110 */ - 0x60, /* 01100000 */ - 0xc0, /* 11000000 */ - - /* 238 0xee '' */ - 0x1e, /* 00011110 */ - 0x30, /* 00110000 */ - 0x60, /* 01100000 */ - 0x7e, /* 01111110 */ - 0x60, /* 01100000 */ - 0x30, /* 00110000 */ - 0x1e, /* 00011110 */ - 0x00, /* 00000000 */ - - /* 239 0xef '' */ - 0x00, /* 00000000 */ - 0x7c, /* 01111100 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0xc6, /* 11000110 */ - 0x00, /* 00000000 */ - - /* 240 0xf0 '' */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0xfe, /* 11111110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 241 0xf1 '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x7e, /* 01111110 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - - /* 242 0xf2 '' */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - - /* 243 0xf3 '' */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x18, /* 00011000 */ - 0x0c, /* 00001100 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - - /* 244 0xf4 '' */ - 0x0e, /* 00001110 */ - 0x1b, /* 00011011 */ - 0x1b, /* 00011011 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - - /* 245 0xf5 '' */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0xd8, /* 11011000 */ - 0xd8, /* 11011000 */ - 0x70, /* 01110000 */ - - /* 246 0xf6 '' */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x7e, /* 01111110 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 247 0xf7 '' */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x00, /* 00000000 */ - 0x76, /* 01110110 */ - 0xdc, /* 11011100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 248 0xf8 '' */ - 0x38, /* 00111000 */ - 0x6c, /* 01101100 */ - 0x6c, /* 01101100 */ - 0x38, /* 00111000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 249 0xf9 '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 250 0xfa '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 00011000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 251 0xfb '' */ - 0x0f, /* 00001111 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0x0c, /* 00001100 */ - 0xec, /* 11101100 */ - 0x6c, /* 01101100 */ - 0x3c, /* 00111100 */ - 0x1c, /* 00011100 */ - - /* 252 0xfc '' */ - 0x6c, /* 01101100 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x36, /* 00110110 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 253 0xfd '' */ - 0x78, /* 01111000 */ - 0x0c, /* 00001100 */ - 0x18, /* 00011000 */ - 0x30, /* 00110000 */ - 0x7c, /* 01111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 254 0xfe '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x3c, /* 00111100 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 255 0xff '' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - -}; - -const struct font_desc font_pearl_8x8 = { - .idx = PEARL8x8_IDX, - .name = "PEARL8x8", - .width = 8, - .height = 8, - .data = fontdata_pearl8x8, - .pref = 2, -}; diff --git a/drivers/video/console/font_sun12x22.c b/drivers/video/console/font_sun12x22.c deleted file mode 100644 index d3643853c33a..000000000000 --- a/drivers/video/console/font_sun12x22.c +++ /dev/null @@ -1,6165 +0,0 @@ -#include - -#define FONTDATAMAX 11264 - -static const unsigned char fontdata_sun12x22[FONTDATAMAX] = { - - /* 0 0x00 '^@' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 1 0x01 '^A' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x1f, 0xc0, /* 000111111100 */ - 0x30, 0x60, /* 001100000110 */ - 0x65, 0x30, /* 011001010011 */ - 0x6d, 0xb0, /* 011011011011 */ - 0x60, 0x30, /* 011000000011 */ - 0x62, 0x30, /* 011000100011 */ - 0x62, 0x30, /* 011000100011 */ - 0x60, 0x30, /* 011000000011 */ - 0x6f, 0xb0, /* 011011111011 */ - 0x67, 0x30, /* 011001110011 */ - 0x30, 0x60, /* 001100000110 */ - 0x1f, 0xc0, /* 000111111100 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 2 0x02 '^B' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x1f, 0xc0, /* 000111111100 */ - 0x3f, 0xe0, /* 001111111110 */ - 0x7a, 0xf0, /* 011110101111 */ - 0x72, 0x70, /* 011100100111 */ - 0x7f, 0xf0, /* 011111111111 */ - 0x7d, 0xf0, /* 011111011111 */ - 0x7d, 0xf0, /* 011111011111 */ - 0x7f, 0xf0, /* 011111111111 */ - 0x70, 0x70, /* 011100000111 */ - 0x78, 0xf0, /* 011110001111 */ - 0x3f, 0xe0, /* 001111111110 */ - 0x1f, 0xc0, /* 000111111100 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 3 0x03 '^C' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x19, 0x80, /* 000110011000 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x1f, 0x80, /* 000111111000 */ - 0x1f, 0x80, /* 000111111000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x06, 0x00, /* 000001100000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 4 0x04 '^D' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x02, 0x00, /* 000000100000 */ - 0x07, 0x00, /* 000001110000 */ - 0x0f, 0x80, /* 000011111000 */ - 0x0f, 0x80, /* 000011111000 */ - 0x1f, 0xc0, /* 000111111100 */ - 0x1f, 0xc0, /* 000111111100 */ - 0x3f, 0xe0, /* 001111111110 */ - 0x1f, 0xc0, /* 000111111100 */ - 0x1f, 0xc0, /* 000111111100 */ - 0x0f, 0x80, /* 000011111000 */ - 0x0f, 0x80, /* 000011111000 */ - 0x07, 0x00, /* 000001110000 */ - 0x02, 0x00, /* 000000100000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 5 0x05 '^E' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x02, 0x00, /* 000000100000 */ - 0x07, 0x00, /* 000001110000 */ - 0x07, 0x00, /* 000001110000 */ - 0x02, 0x00, /* 000000100000 */ - 0x18, 0xc0, /* 000110001100 */ - 0x3d, 0xe0, /* 001111011110 */ - 0x3d, 0xe0, /* 001111011110 */ - 0x1a, 0xc0, /* 000110101100 */ - 0x02, 0x00, /* 000000100000 */ - 0x07, 0x00, /* 000001110000 */ - 0x0f, 0x80, /* 000011111000 */ - 0x1f, 0xc0, /* 000111111100 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 6 0x06 '^F' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x06, 0x00, /* 000001100000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x1f, 0x80, /* 000111111000 */ - 0x1f, 0x80, /* 000111111000 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x36, 0xc0, /* 001101101100 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x1f, 0x80, /* 000111111000 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 7 0x07 '^G' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x06, 0x00, /* 000001100000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x1f, 0x80, /* 000111111000 */ - 0x1f, 0x80, /* 000111111000 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x1f, 0x80, /* 000111111000 */ - 0x1f, 0x80, /* 000111111000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x06, 0x00, /* 000001100000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 8 0x08 '^H' */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xf9, 0xf0, /* 111110011111 */ - 0xf0, 0xf0, /* 111100001111 */ - 0xf0, 0xf0, /* 111100001111 */ - 0xe0, 0x70, /* 111000000111 */ - 0xe0, 0x70, /* 111000000111 */ - 0xc0, 0x30, /* 110000000011 */ - 0xc0, 0x30, /* 110000000011 */ - 0xe0, 0x70, /* 111000000111 */ - 0xe0, 0x70, /* 111000000111 */ - 0xf0, 0xf0, /* 111100001111 */ - 0xf0, 0xf0, /* 111100001111 */ - 0xf9, 0xf0, /* 111110011111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - - /* 9 0x09 '^I' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x06, 0x00, /* 000001100000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x19, 0x80, /* 000110011000 */ - 0x19, 0x80, /* 000110011000 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x19, 0x80, /* 000110011000 */ - 0x19, 0x80, /* 000110011000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x06, 0x00, /* 000001100000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 10 0x0a '^J' */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xf9, 0xf0, /* 111110011111 */ - 0xf0, 0xf0, /* 111100001111 */ - 0xf0, 0xf0, /* 111100001111 */ - 0xe6, 0x70, /* 111001100111 */ - 0xe6, 0x70, /* 111001100111 */ - 0xcf, 0x30, /* 110011110011 */ - 0xcf, 0x30, /* 110011110011 */ - 0xe6, 0x70, /* 111001100111 */ - 0xe6, 0x70, /* 111001100111 */ - 0xf0, 0xf0, /* 111100001111 */ - 0xf0, 0xf0, /* 111100001111 */ - 0xf9, 0xf0, /* 111110011111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - - /* 11 0x0b '^K' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0xe0, /* 000011111110 */ - 0x0f, 0xe0, /* 000011111110 */ - 0x01, 0xe0, /* 000000011110 */ - 0x03, 0x60, /* 000000110110 */ - 0x06, 0x60, /* 000001100110 */ - 0x1e, 0x00, /* 000111100000 */ - 0x33, 0x00, /* 001100110000 */ - 0x33, 0x00, /* 001100110000 */ - 0x61, 0x80, /* 011000011000 */ - 0x61, 0x80, /* 011000011000 */ - 0x33, 0x00, /* 001100110000 */ - 0x33, 0x00, /* 001100110000 */ - 0x1e, 0x00, /* 000111100000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 12 0x0c '^L' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x06, 0x00, /* 000001100000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x19, 0x80, /* 000110011000 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x19, 0x80, /* 000110011000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 13 0x0d '^M' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0xe0, /* 000011111110 */ - 0x0c, 0x60, /* 000011000110 */ - 0x0c, 0x60, /* 000011000110 */ - 0x0f, 0xe0, /* 000011111110 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x3c, 0x00, /* 001111000000 */ - 0x7c, 0x00, /* 011111000000 */ - 0x78, 0x00, /* 011110000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 14 0x0e '^N' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x1f, 0xe0, /* 000111111110 */ - 0x18, 0x60, /* 000110000110 */ - 0x18, 0x60, /* 000110000110 */ - 0x1f, 0xe0, /* 000111111110 */ - 0x18, 0x60, /* 000110000110 */ - 0x18, 0x60, /* 000110000110 */ - 0x18, 0x60, /* 000110000110 */ - 0x18, 0x60, /* 000110000110 */ - 0x18, 0x60, /* 000110000110 */ - 0x18, 0x60, /* 000110000110 */ - 0x19, 0xe0, /* 000110011110 */ - 0x1b, 0xe0, /* 000110111110 */ - 0x1b, 0xc0, /* 000110111100 */ - 0x79, 0x80, /* 011110011000 */ - 0xf8, 0x00, /* 111110000000 */ - 0xf0, 0x00, /* 111100000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 15 0x0f '^O' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x18, 0xc0, /* 000110001100 */ - 0x0d, 0x80, /* 000011011000 */ - 0x6d, 0xb0, /* 011011011011 */ - 0x3d, 0xe0, /* 001111011110 */ - 0x00, 0x00, /* 000000000000 */ - 0x3d, 0xe0, /* 001111011110 */ - 0x6d, 0xb0, /* 011011011011 */ - 0x0d, 0x80, /* 000011011000 */ - 0x18, 0xc0, /* 000110001100 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 16 0x10 '^P' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x20, /* 000000000010 */ - 0x00, 0x60, /* 000000000110 */ - 0x00, 0xe0, /* 000000001110 */ - 0x01, 0xe0, /* 000000011110 */ - 0x03, 0xe0, /* 000000111110 */ - 0x07, 0xe0, /* 000001111110 */ - 0x0f, 0xe0, /* 000011111110 */ - 0x1f, 0xe0, /* 000111111110 */ - 0x3f, 0xe0, /* 001111111110 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x3f, 0xe0, /* 001111111110 */ - 0x1f, 0xe0, /* 000111111110 */ - 0x0f, 0xe0, /* 000011111110 */ - 0x07, 0xe0, /* 000001111110 */ - 0x03, 0xe0, /* 000000111110 */ - 0x01, 0xe0, /* 000000011110 */ - 0x00, 0xe0, /* 000000001110 */ - 0x00, 0x60, /* 000000000110 */ - 0x00, 0x20, /* 000000000010 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 17 0x11 '^Q' */ - 0x00, 0x00, /* 000000000000 */ - 0x40, 0x00, /* 010000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x70, 0x00, /* 011100000000 */ - 0x78, 0x00, /* 011110000000 */ - 0x7c, 0x00, /* 011111000000 */ - 0x7e, 0x00, /* 011111100000 */ - 0x7f, 0x00, /* 011111110000 */ - 0x7f, 0x80, /* 011111111000 */ - 0x7f, 0xc0, /* 011111111100 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x7f, 0xc0, /* 011111111100 */ - 0x7f, 0x80, /* 011111111000 */ - 0x7f, 0x00, /* 011111110000 */ - 0x7e, 0x00, /* 011111100000 */ - 0x7c, 0x00, /* 011111000000 */ - 0x78, 0x00, /* 011110000000 */ - 0x70, 0x00, /* 011100000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x40, 0x00, /* 010000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 18 0x12 '^R' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x04, 0x00, /* 000001000000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x1f, 0x00, /* 000111110000 */ - 0x3f, 0x80, /* 001111111000 */ - 0x7f, 0xc0, /* 011111111100 */ - 0x0e, 0x00, /* 000011100000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x7f, 0xc0, /* 011111111100 */ - 0x3f, 0x80, /* 001111111000 */ - 0x1f, 0x00, /* 000111110000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x04, 0x00, /* 000001000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 19 0x13 '^S' */ - 0x00, 0x00, /* 000000000000 */ - 0x31, 0x80, /* 001100011000 */ - 0x31, 0x80, /* 001100011000 */ - 0x31, 0x80, /* 001100011000 */ - 0x31, 0x80, /* 001100011000 */ - 0x31, 0x80, /* 001100011000 */ - 0x31, 0x80, /* 001100011000 */ - 0x31, 0x80, /* 001100011000 */ - 0x31, 0x80, /* 001100011000 */ - 0x31, 0x80, /* 001100011000 */ - 0x31, 0x80, /* 001100011000 */ - 0x31, 0x80, /* 001100011000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x31, 0x80, /* 001100011000 */ - 0x31, 0x80, /* 001100011000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 20 0x14 '^T' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x1f, 0xf0, /* 000111111111 */ - 0x3c, 0xc0, /* 001111001100 */ - 0x7c, 0xc0, /* 011111001100 */ - 0x7c, 0xc0, /* 011111001100 */ - 0x7c, 0xc0, /* 011111001100 */ - 0x3c, 0xc0, /* 001111001100 */ - 0x1c, 0xc0, /* 000111001100 */ - 0x0c, 0xc0, /* 000011001100 */ - 0x0c, 0xc0, /* 000011001100 */ - 0x0c, 0xc0, /* 000011001100 */ - 0x0c, 0xc0, /* 000011001100 */ - 0x0c, 0xc0, /* 000011001100 */ - 0x0c, 0xc0, /* 000011001100 */ - 0x1c, 0xe0, /* 000111001110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 21 0x15 '^U' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x1f, 0x00, /* 000111110000 */ - 0x31, 0x80, /* 001100011000 */ - 0x31, 0x80, /* 001100011000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x1f, 0x00, /* 000111110000 */ - 0x31, 0x80, /* 001100011000 */ - 0x31, 0x80, /* 001100011000 */ - 0x1f, 0x00, /* 000111110000 */ - 0x01, 0x80, /* 000000011000 */ - 0x01, 0x80, /* 000000011000 */ - 0x31, 0x80, /* 001100011000 */ - 0x31, 0x80, /* 001100011000 */ - 0x1f, 0x00, /* 000111110000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 22 0x16 '^V' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 23 0x17 '^W' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x04, 0x00, /* 000001000000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x1f, 0x00, /* 000111110000 */ - 0x3f, 0x80, /* 001111111000 */ - 0x7f, 0xc0, /* 011111111100 */ - 0x0e, 0x00, /* 000011100000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x7f, 0xc0, /* 011111111100 */ - 0x3f, 0x80, /* 001111111000 */ - 0x1f, 0x00, /* 000111110000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x04, 0x00, /* 000001000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 24 0x18 '^X' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x04, 0x00, /* 000001000000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x1f, 0x00, /* 000111110000 */ - 0x3f, 0x80, /* 001111111000 */ - 0x7f, 0xc0, /* 011111111100 */ - 0x0e, 0x00, /* 000011100000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 25 0x19 '^Y' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x7f, 0xc0, /* 011111111100 */ - 0x3f, 0x80, /* 001111111000 */ - 0x1f, 0x00, /* 000111110000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x04, 0x00, /* 000001000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 26 0x1a '^Z' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x08, 0x00, /* 000010000000 */ - 0x18, 0x00, /* 000110000000 */ - 0x38, 0x00, /* 001110000000 */ - 0x7f, 0xe0, /* 011111111110 */ - 0xff, 0xe0, /* 111111111110 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x38, 0x00, /* 001110000000 */ - 0x18, 0x00, /* 000110000000 */ - 0x08, 0x00, /* 000010000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 27 0x1b '^[' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x01, 0x00, /* 000000010000 */ - 0x01, 0x80, /* 000000011000 */ - 0x01, 0xc0, /* 000000011100 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x7f, 0xf0, /* 011111111111 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x01, 0xc0, /* 000000011100 */ - 0x01, 0x80, /* 000000011000 */ - 0x01, 0x00, /* 000000010000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 28 0x1c '^\' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x3f, 0xe0, /* 001111111110 */ - 0x3f, 0xe0, /* 001111111110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 29 0x1d '^]' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x09, 0x00, /* 000010010000 */ - 0x19, 0x80, /* 000110011000 */ - 0x39, 0xc0, /* 001110011100 */ - 0x7f, 0xe0, /* 011111111110 */ - 0xff, 0xf0, /* 111111111111 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x39, 0xc0, /* 001110011100 */ - 0x19, 0x80, /* 000110011000 */ - 0x09, 0x00, /* 000010010000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 30 0x1e '^^' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x04, 0x00, /* 000001000000 */ - 0x04, 0x00, /* 000001000000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x1f, 0x00, /* 000111110000 */ - 0x1f, 0x00, /* 000111110000 */ - 0x3f, 0x80, /* 001111111000 */ - 0x3f, 0x80, /* 001111111000 */ - 0x7f, 0xc0, /* 011111111100 */ - 0x7f, 0xc0, /* 011111111100 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 31 0x1f '^_' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x7f, 0xc0, /* 011111111100 */ - 0x7f, 0xc0, /* 011111111100 */ - 0x3f, 0x80, /* 001111111000 */ - 0x3f, 0x80, /* 001111111000 */ - 0x1f, 0x00, /* 000111110000 */ - 0x1f, 0x00, /* 000111110000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x04, 0x00, /* 000001000000 */ - 0x04, 0x00, /* 000001000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 32 0x20 ' ' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 33 0x21 '!' */ - 0x00, 0x00, /* 000000000000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 34 0x22 '"' */ - 0x00, 0x00, /* 000000000000 */ - 0x19, 0x80, /* 000110011000 */ - 0x19, 0x80, /* 000110011000 */ - 0x19, 0x80, /* 000110011000 */ - 0x19, 0x80, /* 000110011000 */ - 0x19, 0x80, /* 000110011000 */ - 0x19, 0x80, /* 000110011000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 35 0x23 '#' */ - 0x00, 0x00, /* 000000000000 */ - 0x03, 0x30, /* 000000110011 */ - 0x03, 0x30, /* 000000110011 */ - 0x03, 0x30, /* 000000110011 */ - 0x06, 0x60, /* 000001100110 */ - 0x1f, 0xf0, /* 000111111111 */ - 0x1f, 0xf0, /* 000111111111 */ - 0x0c, 0xc0, /* 000011001100 */ - 0x0c, 0xc0, /* 000011001100 */ - 0x19, 0x80, /* 000110011000 */ - 0x19, 0x80, /* 000110011000 */ - 0x7f, 0xc0, /* 011111111100 */ - 0x7f, 0xc0, /* 011111111100 */ - 0x33, 0x00, /* 001100110000 */ - 0x66, 0x00, /* 011001100000 */ - 0x66, 0x00, /* 011001100000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 36 0x24 '$' */ - 0x00, 0x00, /* 000000000000 */ - 0x06, 0x00, /* 000001100000 */ - 0x1f, 0x80, /* 000111111000 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x66, 0xe0, /* 011001101110 */ - 0x66, 0x60, /* 011001100110 */ - 0x66, 0x00, /* 011001100000 */ - 0x3e, 0x00, /* 001111100000 */ - 0x1f, 0x80, /* 000111111000 */ - 0x07, 0xc0, /* 000001111100 */ - 0x06, 0x60, /* 000001100110 */ - 0x06, 0x60, /* 000001100110 */ - 0x66, 0x60, /* 011001100110 */ - 0x7f, 0xc0, /* 011111111100 */ - 0x3f, 0x80, /* 001111111000 */ - 0x06, 0x00, /* 000001100000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 37 0x25 '%' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x38, 0xc0, /* 001110001100 */ - 0x4c, 0xc0, /* 010011001100 */ - 0x45, 0x80, /* 010001011000 */ - 0x65, 0x80, /* 011001011000 */ - 0x3b, 0x00, /* 001110110000 */ - 0x03, 0x00, /* 000000110000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0d, 0xc0, /* 000011011100 */ - 0x1a, 0x60, /* 000110100110 */ - 0x1a, 0x20, /* 000110100010 */ - 0x33, 0x20, /* 001100110010 */ - 0x31, 0xc0, /* 001100011100 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 38 0x26 '&' */ - 0x00, 0x00, /* 000000000000 */ - 0x07, 0x00, /* 000001110000 */ - 0x0f, 0x80, /* 000011111000 */ - 0x18, 0xc0, /* 000110001100 */ - 0x18, 0xc0, /* 000110001100 */ - 0x18, 0xc0, /* 000110001100 */ - 0x0f, 0x80, /* 000011111000 */ - 0x1e, 0x00, /* 000111100000 */ - 0x3e, 0x00, /* 001111100000 */ - 0x77, 0x00, /* 011101110000 */ - 0x63, 0x60, /* 011000110110 */ - 0x61, 0xe0, /* 011000011110 */ - 0x61, 0xc0, /* 011000011100 */ - 0x61, 0x80, /* 011000011000 */ - 0x3f, 0xe0, /* 001111111110 */ - 0x1e, 0x60, /* 000111100110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 39 0x27 ''' */ - 0x00, 0x00, /* 000000000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x1e, 0x00, /* 000111100000 */ - 0x1e, 0x00, /* 000111100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x18, 0x00, /* 000110000000 */ - 0x10, 0x00, /* 000100000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 40 0x28 '(' */ - 0x00, 0x00, /* 000000000000 */ - 0x01, 0x80, /* 000000011000 */ - 0x03, 0x00, /* 000000110000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x03, 0x00, /* 000000110000 */ - 0x01, 0x80, /* 000000011000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 41 0x29 ')' */ - 0x00, 0x00, /* 000000000000 */ - 0x18, 0x00, /* 000110000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x03, 0x00, /* 000000110000 */ - 0x03, 0x00, /* 000000110000 */ - 0x03, 0x00, /* 000000110000 */ - 0x03, 0x00, /* 000000110000 */ - 0x03, 0x00, /* 000000110000 */ - 0x03, 0x00, /* 000000110000 */ - 0x03, 0x00, /* 000000110000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x18, 0x00, /* 000110000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 42 0x2a '*' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x06, 0x00, /* 000001100000 */ - 0x66, 0x60, /* 011001100110 */ - 0x76, 0xe0, /* 011101101110 */ - 0x19, 0x80, /* 000110011000 */ - 0x00, 0x00, /* 000000000000 */ - 0x19, 0x80, /* 000110011000 */ - 0x76, 0xe0, /* 011101101110 */ - 0x66, 0x60, /* 011001100110 */ - 0x06, 0x00, /* 000001100000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 43 0x2b '+' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 44 0x2c ',' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x1e, 0x00, /* 000111100000 */ - 0x1e, 0x00, /* 000111100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x18, 0x00, /* 000110000000 */ - 0x10, 0x00, /* 000100000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 45 0x2d '-' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 46 0x2e '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x1e, 0x00, /* 000111100000 */ - 0x1e, 0x00, /* 000111100000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 47 0x2f '/' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x60, /* 000000000110 */ - 0x00, 0xc0, /* 000000001100 */ - 0x00, 0xc0, /* 000000001100 */ - 0x01, 0x80, /* 000000011000 */ - 0x01, 0x80, /* 000000011000 */ - 0x03, 0x00, /* 000000110000 */ - 0x03, 0x00, /* 000000110000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x18, 0x00, /* 000110000000 */ - 0x18, 0x00, /* 000110000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 48 0x30 '0' */ - 0x00, 0x00, /* 000000000000 */ - 0x07, 0x00, /* 000001110000 */ - 0x0f, 0x80, /* 000011111000 */ - 0x11, 0x80, /* 000100011000 */ - 0x10, 0xc0, /* 000100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0x80, /* 001100001000 */ - 0x18, 0x80, /* 000110001000 */ - 0x1f, 0x00, /* 000111110000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 49 0x31 '1' */ - 0x00, 0x00, /* 000000000000 */ - 0x02, 0x00, /* 000000100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x1e, 0x00, /* 000111100000 */ - 0x36, 0x00, /* 001101100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 50 0x32 '2' */ - 0x00, 0x00, /* 000000000000 */ - 0x1f, 0x00, /* 000111110000 */ - 0x3f, 0x80, /* 001111111000 */ - 0x61, 0xc0, /* 011000011100 */ - 0x40, 0xc0, /* 010000001100 */ - 0x00, 0xc0, /* 000000001100 */ - 0x00, 0xc0, /* 000000001100 */ - 0x00, 0xc0, /* 000000001100 */ - 0x01, 0x80, /* 000000011000 */ - 0x03, 0x00, /* 000000110000 */ - 0x06, 0x00, /* 000001100000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x18, 0x00, /* 000110000000 */ - 0x30, 0x20, /* 001100000010 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 51 0x33 '3' */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0x80, /* 000011111000 */ - 0x1f, 0xc0, /* 000111111100 */ - 0x20, 0xe0, /* 001000001110 */ - 0x40, 0x60, /* 010000000110 */ - 0x00, 0x60, /* 000000000110 */ - 0x00, 0xe0, /* 000000001110 */ - 0x07, 0xc0, /* 000001111100 */ - 0x0f, 0xc0, /* 000011111100 */ - 0x00, 0xe0, /* 000000001110 */ - 0x00, 0x60, /* 000000000110 */ - 0x00, 0x60, /* 000000000110 */ - 0x40, 0x60, /* 010000000110 */ - 0x60, 0x40, /* 011000000100 */ - 0x3f, 0x80, /* 001111111000 */ - 0x1f, 0x00, /* 000111110000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 52 0x34 '4' */ - 0x00, 0x00, /* 000000000000 */ - 0x01, 0x80, /* 000000011000 */ - 0x03, 0x80, /* 000000111000 */ - 0x03, 0x80, /* 000000111000 */ - 0x05, 0x80, /* 000001011000 */ - 0x05, 0x80, /* 000001011000 */ - 0x09, 0x80, /* 000010011000 */ - 0x09, 0x80, /* 000010011000 */ - 0x11, 0x80, /* 000100011000 */ - 0x11, 0x80, /* 000100011000 */ - 0x21, 0x80, /* 001000011000 */ - 0x3f, 0xe0, /* 001111111110 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x01, 0x80, /* 000000011000 */ - 0x01, 0x80, /* 000000011000 */ - 0x01, 0x80, /* 000000011000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 53 0x35 '5' */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0xc0, /* 000011111100 */ - 0x0f, 0xc0, /* 000011111100 */ - 0x10, 0x00, /* 000100000000 */ - 0x10, 0x00, /* 000100000000 */ - 0x20, 0x00, /* 001000000000 */ - 0x3f, 0x80, /* 001111111000 */ - 0x31, 0xc0, /* 001100011100 */ - 0x00, 0xe0, /* 000000001110 */ - 0x00, 0x60, /* 000000000110 */ - 0x00, 0x60, /* 000000000110 */ - 0x00, 0x60, /* 000000000110 */ - 0x40, 0x60, /* 010000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x30, 0xc0, /* 001100001100 */ - 0x1f, 0x80, /* 000111111000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 54 0x36 '6' */ - 0x00, 0x00, /* 000000000000 */ - 0x07, 0x00, /* 000001110000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x18, 0x00, /* 000110000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x67, 0x80, /* 011001111000 */ - 0x6f, 0xc0, /* 011011111100 */ - 0x70, 0xe0, /* 011100001110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x70, 0x40, /* 011100000100 */ - 0x3f, 0x80, /* 001111111000 */ - 0x1f, 0x00, /* 000111110000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 55 0x37 '7' */ - 0x00, 0x00, /* 000000000000 */ - 0x1f, 0xe0, /* 000111111110 */ - 0x3f, 0xe0, /* 001111111110 */ - 0x60, 0x40, /* 011000000100 */ - 0x00, 0x40, /* 000000000100 */ - 0x00, 0xc0, /* 000000001100 */ - 0x00, 0x80, /* 000000001000 */ - 0x00, 0x80, /* 000000001000 */ - 0x01, 0x80, /* 000000011000 */ - 0x01, 0x00, /* 000000010000 */ - 0x01, 0x00, /* 000000010000 */ - 0x03, 0x00, /* 000000110000 */ - 0x02, 0x00, /* 000000100000 */ - 0x02, 0x00, /* 000000100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x04, 0x00, /* 000001000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 56 0x38 '8' */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x11, 0x80, /* 000100011000 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x18, 0x80, /* 000110001000 */ - 0x0d, 0x00, /* 000011010000 */ - 0x06, 0x00, /* 000001100000 */ - 0x0b, 0x00, /* 000010110000 */ - 0x11, 0x80, /* 000100011000 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x18, 0x80, /* 000110001000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 57 0x39 '9' */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0x80, /* 000011111000 */ - 0x11, 0xc0, /* 000100011100 */ - 0x20, 0xe0, /* 001000001110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x70, 0xe0, /* 011100001110 */ - 0x3f, 0x60, /* 001111110110 */ - 0x1e, 0x60, /* 000111100110 */ - 0x00, 0x60, /* 000000000110 */ - 0x00, 0xc0, /* 000000001100 */ - 0x00, 0xc0, /* 000000001100 */ - 0x01, 0x80, /* 000000011000 */ - 0x07, 0x00, /* 000001110000 */ - 0x3c, 0x00, /* 001111000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 58 0x3a ':' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x1e, 0x00, /* 000111100000 */ - 0x1e, 0x00, /* 000111100000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x1e, 0x00, /* 000111100000 */ - 0x1e, 0x00, /* 000111100000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 59 0x3b ';' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x1e, 0x00, /* 000111100000 */ - 0x1e, 0x00, /* 000111100000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x1e, 0x00, /* 000111100000 */ - 0x1e, 0x00, /* 000111100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x18, 0x00, /* 000110000000 */ - 0x10, 0x00, /* 000100000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 60 0x3c '<' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x60, /* 000000000110 */ - 0x01, 0xc0, /* 000000011100 */ - 0x07, 0x00, /* 000001110000 */ - 0x1c, 0x00, /* 000111000000 */ - 0x70, 0x00, /* 011100000000 */ - 0x70, 0x00, /* 011100000000 */ - 0x1c, 0x00, /* 000111000000 */ - 0x07, 0x00, /* 000001110000 */ - 0x01, 0xc0, /* 000000011100 */ - 0x00, 0x60, /* 000000000110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 61 0x3d '=' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 62 0x3e '>' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x38, 0x00, /* 001110000000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x03, 0x80, /* 000000111000 */ - 0x00, 0xe0, /* 000000001110 */ - 0x00, 0xe0, /* 000000001110 */ - 0x03, 0x80, /* 000000111000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x38, 0x00, /* 001110000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 63 0x3f '?' */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x1f, 0x80, /* 000111111000 */ - 0x39, 0xc0, /* 001110011100 */ - 0x20, 0xc0, /* 001000001100 */ - 0x00, 0xc0, /* 000000001100 */ - 0x00, 0xc0, /* 000000001100 */ - 0x01, 0x80, /* 000000011000 */ - 0x03, 0x00, /* 000000110000 */ - 0x06, 0x00, /* 000001100000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 64 0x40 '@' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0x80, /* 000011111000 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x30, 0x60, /* 001100000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x67, 0x20, /* 011001110010 */ - 0x6f, 0xa0, /* 011011111010 */ - 0x6c, 0xa0, /* 011011001010 */ - 0x6c, 0xa0, /* 011011001010 */ - 0x67, 0xe0, /* 011001111110 */ - 0x60, 0x00, /* 011000000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x3f, 0xe0, /* 001111111110 */ - 0x0f, 0xe0, /* 000011111110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 65 0x41 'A' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x0b, 0x00, /* 000010110000 */ - 0x0b, 0x00, /* 000010110000 */ - 0x09, 0x00, /* 000010010000 */ - 0x11, 0x80, /* 000100011000 */ - 0x11, 0x80, /* 000100011000 */ - 0x10, 0x80, /* 000100001000 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x20, 0xc0, /* 001000001100 */ - 0x20, 0x40, /* 001000000100 */ - 0x40, 0x60, /* 010000000110 */ - 0x40, 0x60, /* 010000000110 */ - 0xe0, 0xf0, /* 111000001111 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 66 0x42 'B' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0xff, 0x00, /* 111111110000 */ - 0x60, 0x80, /* 011000001000 */ - 0x60, 0xc0, /* 011000001100 */ - 0x60, 0xc0, /* 011000001100 */ - 0x60, 0xc0, /* 011000001100 */ - 0x61, 0x80, /* 011000011000 */ - 0x7f, 0x80, /* 011111111000 */ - 0x60, 0xc0, /* 011000001100 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0xc0, /* 011000001100 */ - 0xff, 0x80, /* 111111111000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 67 0x43 'C' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0xc0, /* 000011111100 */ - 0x10, 0x60, /* 000100000110 */ - 0x20, 0x20, /* 001000000010 */ - 0x20, 0x00, /* 001000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x20, 0x00, /* 001000000000 */ - 0x30, 0x20, /* 001100000010 */ - 0x18, 0x40, /* 000110000100 */ - 0x0f, 0x80, /* 000011111000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 68 0x44 'D' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0xff, 0x00, /* 111111110000 */ - 0x61, 0xc0, /* 011000011100 */ - 0x60, 0xc0, /* 011000001100 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x40, /* 011000000100 */ - 0x61, 0x80, /* 011000011000 */ - 0xfe, 0x00, /* 111111100000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 69 0x45 'E' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x7f, 0xc0, /* 011111111100 */ - 0x30, 0x40, /* 001100000100 */ - 0x30, 0x40, /* 001100000100 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x80, /* 001100001000 */ - 0x3f, 0x80, /* 001111111000 */ - 0x30, 0x80, /* 001100001000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x20, /* 001100000010 */ - 0x30, 0x20, /* 001100000010 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 70 0x46 'F' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x7f, 0xc0, /* 011111111100 */ - 0x30, 0x40, /* 001100000100 */ - 0x30, 0x40, /* 001100000100 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x80, /* 001100001000 */ - 0x3f, 0x80, /* 001111111000 */ - 0x30, 0x80, /* 001100001000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x78, 0x00, /* 011110000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 71 0x47 'G' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0xc0, /* 000011111100 */ - 0x10, 0x60, /* 000100000110 */ - 0x20, 0x20, /* 001000000010 */ - 0x20, 0x00, /* 001000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x61, 0xf0, /* 011000011111 */ - 0x60, 0x60, /* 011000000110 */ - 0x20, 0x60, /* 001000000110 */ - 0x30, 0x60, /* 001100000110 */ - 0x18, 0x60, /* 000110000110 */ - 0x0f, 0x80, /* 000011111000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 72 0x48 'H' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0xf0, 0xf0, /* 111100001111 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0xf0, 0xf0, /* 111100001111 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 73 0x49 'I' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x1f, 0x80, /* 000111111000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x1f, 0x80, /* 000111111000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 74 0x4a 'J' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x1f, 0x80, /* 000111111000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x04, 0x00, /* 000001000000 */ - 0x38, 0x00, /* 001110000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 75 0x4b 'K' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0xf0, 0xe0, /* 111100001110 */ - 0x61, 0x80, /* 011000011000 */ - 0x63, 0x00, /* 011000110000 */ - 0x66, 0x00, /* 011001100000 */ - 0x6c, 0x00, /* 011011000000 */ - 0x78, 0x00, /* 011110000000 */ - 0x78, 0x00, /* 011110000000 */ - 0x7c, 0x00, /* 011111000000 */ - 0x6e, 0x00, /* 011011100000 */ - 0x67, 0x00, /* 011001110000 */ - 0x63, 0x80, /* 011000111000 */ - 0x61, 0xc0, /* 011000011100 */ - 0x60, 0xe0, /* 011000001110 */ - 0xf0, 0x70, /* 111100000111 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 76 0x4c 'L' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x78, 0x00, /* 011110000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x20, /* 001100000010 */ - 0x30, 0x20, /* 001100000010 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 77 0x4d 'M' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0xe0, 0x70, /* 111000000111 */ - 0x60, 0xe0, /* 011000001110 */ - 0x70, 0xe0, /* 011100001110 */ - 0x70, 0xe0, /* 011100001110 */ - 0x70, 0xe0, /* 011100001110 */ - 0x59, 0x60, /* 010110010110 */ - 0x59, 0x60, /* 010110010110 */ - 0x59, 0x60, /* 010110010110 */ - 0x4d, 0x60, /* 010011010110 */ - 0x4e, 0x60, /* 010011100110 */ - 0x4e, 0x60, /* 010011100110 */ - 0x44, 0x60, /* 010001000110 */ - 0x44, 0x60, /* 010001000110 */ - 0xe4, 0xf0, /* 111001001111 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 78 0x4e 'N' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0xc0, 0x70, /* 110000000111 */ - 0x60, 0x20, /* 011000000010 */ - 0x70, 0x20, /* 011100000010 */ - 0x78, 0x20, /* 011110000010 */ - 0x58, 0x20, /* 010110000010 */ - 0x4c, 0x20, /* 010011000010 */ - 0x46, 0x20, /* 010001100010 */ - 0x47, 0x20, /* 010001110010 */ - 0x43, 0x20, /* 010000110010 */ - 0x41, 0xa0, /* 010000011010 */ - 0x40, 0xe0, /* 010000001110 */ - 0x40, 0xe0, /* 010000001110 */ - 0x40, 0x60, /* 010000000110 */ - 0xe0, 0x30, /* 111000000011 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 79 0x4f 'O' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x11, 0xc0, /* 000100011100 */ - 0x20, 0xc0, /* 001000001100 */ - 0x20, 0x60, /* 001000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x20, 0x40, /* 001000000100 */ - 0x30, 0x40, /* 001100000100 */ - 0x18, 0x80, /* 000110001000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 80 0x50 'P' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x7f, 0x80, /* 011111111000 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0x60, /* 001100000110 */ - 0x30, 0x60, /* 001100000110 */ - 0x30, 0x60, /* 001100000110 */ - 0x30, 0xc0, /* 001100001100 */ - 0x37, 0x80, /* 001101111000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x78, 0x00, /* 011110000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 81 0x51 'Q' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x11, 0xc0, /* 000100011100 */ - 0x20, 0xc0, /* 001000001100 */ - 0x20, 0x60, /* 001000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x30, 0x40, /* 001100000100 */ - 0x38, 0x40, /* 001110000100 */ - 0x1f, 0x80, /* 000111111000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x1f, 0x00, /* 000111110000 */ - 0x23, 0x90, /* 001000111001 */ - 0x01, 0xe0, /* 000000011110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 82 0x52 'R' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0xff, 0x00, /* 111111110000 */ - 0x61, 0x80, /* 011000011000 */ - 0x60, 0xc0, /* 011000001100 */ - 0x60, 0xc0, /* 011000001100 */ - 0x60, 0xc0, /* 011000001100 */ - 0x60, 0x80, /* 011000001000 */ - 0x7f, 0x00, /* 011111110000 */ - 0x7c, 0x00, /* 011111000000 */ - 0x6e, 0x00, /* 011011100000 */ - 0x67, 0x00, /* 011001110000 */ - 0x63, 0x80, /* 011000111000 */ - 0x61, 0xc0, /* 011000011100 */ - 0x60, 0xe0, /* 011000001110 */ - 0xf0, 0x70, /* 111100000111 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 83 0x53 'S' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x1f, 0xe0, /* 000111111110 */ - 0x30, 0x60, /* 001100000110 */ - 0x60, 0x20, /* 011000000010 */ - 0x60, 0x20, /* 011000000010 */ - 0x70, 0x00, /* 011100000000 */ - 0x3c, 0x00, /* 001111000000 */ - 0x1e, 0x00, /* 000111100000 */ - 0x07, 0x80, /* 000001111000 */ - 0x01, 0xc0, /* 000000011100 */ - 0x00, 0xe0, /* 000000001110 */ - 0x40, 0x60, /* 010000000110 */ - 0x40, 0x60, /* 010000000110 */ - 0x60, 0xc0, /* 011000001100 */ - 0x7f, 0x80, /* 011111111000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 84 0x54 'T' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x46, 0x20, /* 010001100010 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x1f, 0x80, /* 000111111000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 85 0x55 'U' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0xf0, 0x70, /* 111100000111 */ - 0x60, 0x20, /* 011000000010 */ - 0x60, 0x20, /* 011000000010 */ - 0x60, 0x20, /* 011000000010 */ - 0x60, 0x20, /* 011000000010 */ - 0x60, 0x20, /* 011000000010 */ - 0x60, 0x20, /* 011000000010 */ - 0x60, 0x20, /* 011000000010 */ - 0x60, 0x20, /* 011000000010 */ - 0x60, 0x20, /* 011000000010 */ - 0x60, 0x20, /* 011000000010 */ - 0x70, 0x40, /* 011100000100 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x1f, 0x80, /* 000111111000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 86 0x56 'V' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0xe0, 0xe0, /* 111000001110 */ - 0x60, 0x40, /* 011000000100 */ - 0x30, 0x80, /* 001100001000 */ - 0x30, 0x80, /* 001100001000 */ - 0x30, 0x80, /* 001100001000 */ - 0x19, 0x00, /* 000110010000 */ - 0x19, 0x00, /* 000110010000 */ - 0x19, 0x00, /* 000110010000 */ - 0x0a, 0x00, /* 000010100000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x04, 0x00, /* 000001000000 */ - 0x04, 0x00, /* 000001000000 */ - 0x04, 0x00, /* 000001000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 87 0x57 'W' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0xfe, 0xf0, /* 111111101111 */ - 0x66, 0x20, /* 011001100010 */ - 0x66, 0x20, /* 011001100010 */ - 0x66, 0x20, /* 011001100010 */ - 0x76, 0x20, /* 011101100010 */ - 0x77, 0x40, /* 011101110100 */ - 0x33, 0x40, /* 001100110100 */ - 0x37, 0x40, /* 001101110100 */ - 0x3b, 0xc0, /* 001110111100 */ - 0x3b, 0x80, /* 001110111000 */ - 0x19, 0x80, /* 000110011000 */ - 0x19, 0x80, /* 000110011000 */ - 0x19, 0x80, /* 000110011000 */ - 0x19, 0x80, /* 000110011000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 88 0x58 'X' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0xf0, 0x70, /* 111100000111 */ - 0x60, 0x20, /* 011000000010 */ - 0x30, 0x40, /* 001100000100 */ - 0x38, 0x80, /* 001110001000 */ - 0x18, 0x80, /* 000110001000 */ - 0x0d, 0x00, /* 000011010000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x0b, 0x00, /* 000010110000 */ - 0x11, 0x80, /* 000100011000 */ - 0x11, 0xc0, /* 000100011100 */ - 0x20, 0xc0, /* 001000001100 */ - 0x40, 0x60, /* 010000000110 */ - 0xe0, 0xf0, /* 111000001111 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 89 0x59 'Y' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0xf0, 0x70, /* 111100000111 */ - 0x60, 0x20, /* 011000000010 */ - 0x30, 0x40, /* 001100000100 */ - 0x18, 0x80, /* 000110001000 */ - 0x18, 0x80, /* 000110001000 */ - 0x0d, 0x00, /* 000011010000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 90 0x5a 'Z' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x3f, 0xe0, /* 001111111110 */ - 0x20, 0xc0, /* 001000001100 */ - 0x00, 0xc0, /* 000000001100 */ - 0x01, 0x80, /* 000000011000 */ - 0x01, 0x80, /* 000000011000 */ - 0x03, 0x00, /* 000000110000 */ - 0x03, 0x00, /* 000000110000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x18, 0x00, /* 000110000000 */ - 0x18, 0x20, /* 000110000010 */ - 0x3f, 0xe0, /* 001111111110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 91 0x5b '[' */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0x80, /* 000011111000 */ - 0x0f, 0x80, /* 000011111000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0f, 0x80, /* 000011111000 */ - 0x0f, 0x80, /* 000011111000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 92 0x5c '\' */ - 0x00, 0x00, /* 000000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x18, 0x00, /* 000110000000 */ - 0x18, 0x00, /* 000110000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x03, 0x00, /* 000000110000 */ - 0x03, 0x00, /* 000000110000 */ - 0x01, 0x80, /* 000000011000 */ - 0x01, 0x80, /* 000000011000 */ - 0x00, 0xc0, /* 000000001100 */ - 0x00, 0xc0, /* 000000001100 */ - 0x00, 0x60, /* 000000000110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 93 0x5d ']' */ - 0x00, 0x00, /* 000000000000 */ - 0x1f, 0x00, /* 000111110000 */ - 0x1f, 0x00, /* 000111110000 */ - 0x03, 0x00, /* 000000110000 */ - 0x03, 0x00, /* 000000110000 */ - 0x03, 0x00, /* 000000110000 */ - 0x03, 0x00, /* 000000110000 */ - 0x03, 0x00, /* 000000110000 */ - 0x03, 0x00, /* 000000110000 */ - 0x03, 0x00, /* 000000110000 */ - 0x03, 0x00, /* 000000110000 */ - 0x03, 0x00, /* 000000110000 */ - 0x03, 0x00, /* 000000110000 */ - 0x03, 0x00, /* 000000110000 */ - 0x1f, 0x00, /* 000111110000 */ - 0x1f, 0x00, /* 000111110000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 94 0x5e '^' */ - 0x00, 0x00, /* 000000000000 */ - 0x04, 0x00, /* 000001000000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x1b, 0x00, /* 000110110000 */ - 0x31, 0x80, /* 001100011000 */ - 0x60, 0xc0, /* 011000001100 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 95 0x5f '_' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 96 0x60 '`' */ - 0x00, 0x00, /* 000000000000 */ - 0x01, 0x00, /* 000000010000 */ - 0x03, 0x00, /* 000000110000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x07, 0x80, /* 000001111000 */ - 0x07, 0x80, /* 000001111000 */ - 0x03, 0x00, /* 000000110000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 97 0x61 'a' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0x80, /* 000011111000 */ - 0x18, 0xc0, /* 000110001100 */ - 0x10, 0xc0, /* 000100001100 */ - 0x03, 0xc0, /* 000000111100 */ - 0x1c, 0xc0, /* 000111001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x39, 0xc0, /* 001110011100 */ - 0x1e, 0xe0, /* 000111101110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 98 0x62 'b' */ - 0x00, 0x00, /* 000000000000 */ - 0x20, 0x00, /* 001000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0xe0, 0x00, /* 111000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x67, 0x80, /* 011001111000 */ - 0x6f, 0xc0, /* 011011111100 */ - 0x70, 0xe0, /* 011100001110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x70, 0x60, /* 011100000110 */ - 0x78, 0xc0, /* 011110001100 */ - 0x4f, 0x80, /* 010011111000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 99 0x63 'c' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x1f, 0x80, /* 000111111000 */ - 0x31, 0xc0, /* 001100011100 */ - 0x20, 0xc0, /* 001000001100 */ - 0x60, 0x00, /* 011000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x70, 0x40, /* 011100000100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x1f, 0x80, /* 000111111000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 100 0x64 'd' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x60, /* 000000000110 */ - 0x00, 0xe0, /* 000000001110 */ - 0x00, 0x60, /* 000000000110 */ - 0x00, 0x60, /* 000000000110 */ - 0x00, 0x60, /* 000000000110 */ - 0x0f, 0x60, /* 000011110110 */ - 0x31, 0xe0, /* 001100011110 */ - 0x20, 0xe0, /* 001000001110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x70, 0xe0, /* 011100001110 */ - 0x39, 0x60, /* 001110010110 */ - 0x1e, 0x70, /* 000111100111 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 101 0x65 'e' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x30, 0xc0, /* 001100001100 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x60, 0x00, /* 011000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x18, 0x60, /* 000110000110 */ - 0x0f, 0x80, /* 000011111000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 102 0x66 'f' */ - 0x00, 0x00, /* 000000000000 */ - 0x03, 0x80, /* 000000111000 */ - 0x04, 0xc0, /* 000001001100 */ - 0x04, 0xc0, /* 000001001100 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x3f, 0x80, /* 001111111000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x1e, 0x00, /* 000111100000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 103 0x67 'g' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x1f, 0x20, /* 000111110010 */ - 0x31, 0xe0, /* 001100011110 */ - 0x60, 0xc0, /* 011000001100 */ - 0x60, 0xc0, /* 011000001100 */ - 0x60, 0xc0, /* 011000001100 */ - 0x31, 0x80, /* 001100011000 */ - 0x3f, 0x00, /* 001111110000 */ - 0x60, 0x00, /* 011000000000 */ - 0x7f, 0xc0, /* 011111111100 */ - 0x3f, 0xe0, /* 001111111110 */ - 0x20, 0x60, /* 001000000110 */ - 0x40, 0x20, /* 010000000010 */ - 0x40, 0x20, /* 010000000010 */ - 0x7f, 0xc0, /* 011111111100 */ - 0x3f, 0x80, /* 001111111000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 104 0x68 'h' */ - 0x00, 0x00, /* 000000000000 */ - 0x10, 0x00, /* 000100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x70, 0x00, /* 011100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x37, 0x80, /* 001101111000 */ - 0x39, 0xc0, /* 001110011100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x79, 0xe0, /* 011110011110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 105 0x69 'i' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x1e, 0x00, /* 000111100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x1f, 0x80, /* 000111111000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 106 0x6a 'j' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0xc0, /* 000000001100 */ - 0x00, 0xc0, /* 000000001100 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x03, 0xc0, /* 000000111100 */ - 0x00, 0xc0, /* 000000001100 */ - 0x00, 0xc0, /* 000000001100 */ - 0x00, 0xc0, /* 000000001100 */ - 0x00, 0xc0, /* 000000001100 */ - 0x00, 0xc0, /* 000000001100 */ - 0x00, 0xc0, /* 000000001100 */ - 0x00, 0xc0, /* 000000001100 */ - 0x00, 0xc0, /* 000000001100 */ - 0x00, 0xc0, /* 000000001100 */ - 0x20, 0xc0, /* 001000001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x38, 0x80, /* 001110001000 */ - 0x1f, 0x00, /* 000111110000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 107 0x6b 'k' */ - 0x00, 0x00, /* 000000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0xe0, 0x00, /* 111000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x61, 0xc0, /* 011000011100 */ - 0x63, 0x00, /* 011000110000 */ - 0x66, 0x00, /* 011001100000 */ - 0x7c, 0x00, /* 011111000000 */ - 0x78, 0x00, /* 011110000000 */ - 0x7c, 0x00, /* 011111000000 */ - 0x6e, 0x00, /* 011011100000 */ - 0x67, 0x00, /* 011001110000 */ - 0x63, 0x80, /* 011000111000 */ - 0xf1, 0xe0, /* 111100011110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 108 0x6c 'l' */ - 0x00, 0x00, /* 000000000000 */ - 0x1e, 0x00, /* 000111100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x1f, 0x80, /* 000111111000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 109 0x6d 'm' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0xdd, 0xc0, /* 110111011100 */ - 0x6e, 0xe0, /* 011011101110 */ - 0x66, 0x60, /* 011001100110 */ - 0x66, 0x60, /* 011001100110 */ - 0x66, 0x60, /* 011001100110 */ - 0x66, 0x60, /* 011001100110 */ - 0x66, 0x60, /* 011001100110 */ - 0x66, 0x60, /* 011001100110 */ - 0x66, 0x60, /* 011001100110 */ - 0xef, 0x70, /* 111011110111 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 110 0x6e 'n' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x27, 0x80, /* 001001111000 */ - 0x79, 0xc0, /* 011110011100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x79, 0xe0, /* 011110011110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 111 0x6f 'o' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0x80, /* 000011111000 */ - 0x11, 0xc0, /* 000100011100 */ - 0x20, 0xe0, /* 001000001110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x70, 0x40, /* 011100000100 */ - 0x38, 0x80, /* 001110001000 */ - 0x1f, 0x00, /* 000111110000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 112 0x70 'p' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0xef, 0x80, /* 111011111000 */ - 0x71, 0xc0, /* 011100011100 */ - 0x60, 0xe0, /* 011000001110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x40, /* 011000000100 */ - 0x70, 0x80, /* 011100001000 */ - 0x7f, 0x00, /* 011111110000 */ - 0x60, 0x00, /* 011000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0xf0, 0x00, /* 111100000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 113 0x71 'q' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0x20, /* 000011110010 */ - 0x11, 0xe0, /* 000100011110 */ - 0x20, 0xe0, /* 001000001110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x70, 0x60, /* 011100000110 */ - 0x38, 0xe0, /* 001110001110 */ - 0x1f, 0xe0, /* 000111111110 */ - 0x00, 0x60, /* 000000000110 */ - 0x00, 0x60, /* 000000000110 */ - 0x00, 0x60, /* 000000000110 */ - 0x00, 0x60, /* 000000000110 */ - 0x00, 0xf0, /* 000000001111 */ - 0x00, 0x00, /* 000000000000 */ - - /* 114 0x72 'r' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x73, 0x80, /* 011100111000 */ - 0x34, 0xc0, /* 001101001100 */ - 0x38, 0xc0, /* 001110001100 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x78, 0x00, /* 011110000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 115 0x73 's' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x1f, 0xc0, /* 000111111100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0x40, /* 001100000100 */ - 0x38, 0x00, /* 001110000000 */ - 0x1e, 0x00, /* 000111100000 */ - 0x07, 0x80, /* 000001111000 */ - 0x01, 0xc0, /* 000000011100 */ - 0x20, 0xc0, /* 001000001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x3f, 0x80, /* 001111111000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 116 0x74 't' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x04, 0x00, /* 000001000000 */ - 0x04, 0x00, /* 000001000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x7f, 0xc0, /* 011111111100 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x20, /* 000011000010 */ - 0x0e, 0x40, /* 000011100100 */ - 0x07, 0x80, /* 000001111000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 117 0x75 'u' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x79, 0xe0, /* 011110011110 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x39, 0xc0, /* 001110011100 */ - 0x1e, 0x60, /* 000111100110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 118 0x76 'v' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0xf0, 0x70, /* 111100000111 */ - 0x60, 0x20, /* 011000000010 */ - 0x30, 0x40, /* 001100000100 */ - 0x30, 0x40, /* 001100000100 */ - 0x18, 0x80, /* 000110001000 */ - 0x18, 0x80, /* 000110001000 */ - 0x0d, 0x00, /* 000011010000 */ - 0x0d, 0x00, /* 000011010000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 119 0x77 'w' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0xff, 0x70, /* 111111110111 */ - 0x66, 0x20, /* 011001100010 */ - 0x66, 0x20, /* 011001100010 */ - 0x66, 0x20, /* 011001100010 */ - 0x37, 0x40, /* 001101110100 */ - 0x3b, 0x40, /* 001110110100 */ - 0x3b, 0x40, /* 001110110100 */ - 0x19, 0x80, /* 000110011000 */ - 0x19, 0x80, /* 000110011000 */ - 0x19, 0x80, /* 000110011000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 120 0x78 'x' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0xf8, 0xf0, /* 111110001111 */ - 0x70, 0x40, /* 011100000100 */ - 0x38, 0x80, /* 001110001000 */ - 0x1d, 0x00, /* 000111010000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x07, 0x00, /* 000001110000 */ - 0x0b, 0x80, /* 000010111000 */ - 0x11, 0xc0, /* 000100011100 */ - 0x20, 0xe0, /* 001000001110 */ - 0xf1, 0xf0, /* 111100011111 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 121 0x79 'y' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0xf0, 0xf0, /* 111100001111 */ - 0x60, 0x20, /* 011000000010 */ - 0x30, 0x40, /* 001100000100 */ - 0x30, 0x40, /* 001100000100 */ - 0x18, 0x80, /* 000110001000 */ - 0x18, 0x80, /* 000110001000 */ - 0x0d, 0x00, /* 000011010000 */ - 0x0d, 0x00, /* 000011010000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x04, 0x00, /* 000001000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x08, 0x00, /* 000010000000 */ - 0x78, 0x00, /* 011110000000 */ - 0x70, 0x00, /* 011100000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 122 0x7a 'z' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x60, 0xe0, /* 011000001110 */ - 0x41, 0xc0, /* 010000011100 */ - 0x03, 0x80, /* 000000111000 */ - 0x07, 0x00, /* 000001110000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x1c, 0x00, /* 000111000000 */ - 0x38, 0x20, /* 001110000010 */ - 0x70, 0x60, /* 011100000110 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 123 0x7b '{' */ - 0x00, 0x00, /* 000000000000 */ - 0x03, 0x80, /* 000000111000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x38, 0x00, /* 001110000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x03, 0x80, /* 000000111000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 124 0x7c '|' */ - 0x00, 0x00, /* 000000000000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 125 0x7d '}' */ - 0x00, 0x00, /* 000000000000 */ - 0x1c, 0x00, /* 000111000000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x03, 0x00, /* 000000110000 */ - 0x01, 0xc0, /* 000000011100 */ - 0x03, 0x00, /* 000000110000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x1c, 0x00, /* 000111000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 126 0x7e '~' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x1c, 0x20, /* 000111000010 */ - 0x3e, 0x60, /* 001111100110 */ - 0x67, 0xc0, /* 011001111100 */ - 0x43, 0x80, /* 010000111000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 127 0x7f '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0x00, 0x00, /* 000000000000 */ - - /* 128 0x80 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0xc0, /* 000011111100 */ - 0x10, 0x60, /* 000100000110 */ - 0x20, 0x20, /* 001000000010 */ - 0x20, 0x00, /* 001000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x20, 0x00, /* 001000000000 */ - 0x30, 0x20, /* 001100000010 */ - 0x18, 0x40, /* 000110000100 */ - 0x0f, 0x80, /* 000011111000 */ - 0x06, 0x00, /* 000001100000 */ - 0x03, 0x00, /* 000000110000 */ - 0x01, 0x80, /* 000000011000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 129 0x81 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x19, 0x80, /* 000110011000 */ - 0x19, 0x80, /* 000110011000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x79, 0xe0, /* 011110011110 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x39, 0xc0, /* 001110011100 */ - 0x1e, 0x60, /* 000111100110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 130 0x82 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x01, 0x80, /* 000000011000 */ - 0x03, 0x00, /* 000000110000 */ - 0x06, 0x00, /* 000001100000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x30, 0xc0, /* 001100001100 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x60, 0x00, /* 011000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x18, 0x60, /* 000110000110 */ - 0x0f, 0x80, /* 000011111000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 131 0x83 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x02, 0x00, /* 000000100000 */ - 0x07, 0x00, /* 000001110000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x18, 0xc0, /* 000110001100 */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0x80, /* 000011111000 */ - 0x18, 0xc0, /* 000110001100 */ - 0x10, 0xc0, /* 000100001100 */ - 0x03, 0xc0, /* 000000111100 */ - 0x1c, 0xc0, /* 000111001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x39, 0xc0, /* 001110011100 */ - 0x1e, 0xe0, /* 000111101110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 132 0x84 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x19, 0x80, /* 000110011000 */ - 0x19, 0x80, /* 000110011000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0x80, /* 000011111000 */ - 0x18, 0xc0, /* 000110001100 */ - 0x10, 0xc0, /* 000100001100 */ - 0x03, 0xc0, /* 000000111100 */ - 0x1c, 0xc0, /* 000111001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x39, 0xc0, /* 001110011100 */ - 0x1e, 0xe0, /* 000111101110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 133 0x85 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x06, 0x00, /* 000001100000 */ - 0x03, 0x00, /* 000000110000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0x80, /* 000011111000 */ - 0x18, 0xc0, /* 000110001100 */ - 0x10, 0xc0, /* 000100001100 */ - 0x03, 0xc0, /* 000000111100 */ - 0x1c, 0xc0, /* 000111001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x39, 0xc0, /* 001110011100 */ - 0x1e, 0xe0, /* 000111101110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 134 0x86 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x07, 0x00, /* 000001110000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x07, 0x00, /* 000001110000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0x80, /* 000011111000 */ - 0x18, 0xc0, /* 000110001100 */ - 0x10, 0xc0, /* 000100001100 */ - 0x03, 0xc0, /* 000000111100 */ - 0x1c, 0xc0, /* 000111001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x39, 0xc0, /* 001110011100 */ - 0x1e, 0xe0, /* 000111101110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 135 0x87 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x1f, 0x80, /* 000111111000 */ - 0x31, 0xc0, /* 001100011100 */ - 0x20, 0xc0, /* 001000001100 */ - 0x60, 0x00, /* 011000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x70, 0x40, /* 011100000100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x1f, 0x80, /* 000111111000 */ - 0x06, 0x00, /* 000001100000 */ - 0x03, 0x00, /* 000000110000 */ - 0x01, 0x80, /* 000000011000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 136 0x88 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x02, 0x00, /* 000000100000 */ - 0x07, 0x00, /* 000001110000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x18, 0xc0, /* 000110001100 */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x30, 0xc0, /* 001100001100 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x60, 0x00, /* 011000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x18, 0x60, /* 000110000110 */ - 0x0f, 0x80, /* 000011111000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 137 0x89 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x19, 0x80, /* 000110011000 */ - 0x19, 0x80, /* 000110011000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x30, 0xc0, /* 001100001100 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x60, 0x00, /* 011000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x18, 0x60, /* 000110000110 */ - 0x0f, 0x80, /* 000011111000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 138 0x8a '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x06, 0x00, /* 000001100000 */ - 0x03, 0x00, /* 000000110000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x30, 0xc0, /* 001100001100 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x60, 0x00, /* 011000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x18, 0x60, /* 000110000110 */ - 0x0f, 0x80, /* 000011111000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 139 0x8b '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x19, 0x80, /* 000110011000 */ - 0x19, 0x80, /* 000110011000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x1e, 0x00, /* 000111100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x1f, 0x80, /* 000111111000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 140 0x8c '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x04, 0x00, /* 000001000000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x1b, 0x00, /* 000110110000 */ - 0x31, 0x80, /* 001100011000 */ - 0x00, 0x00, /* 000000000000 */ - 0x1e, 0x00, /* 000111100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x1f, 0x80, /* 000111111000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 141 0x8d '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x18, 0x00, /* 000110000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x06, 0x00, /* 000001100000 */ - 0x00, 0x00, /* 000000000000 */ - 0x1e, 0x00, /* 000111100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x1f, 0x80, /* 000111111000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 142 0x8e '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x19, 0x80, /* 000110011000 */ - 0x19, 0x80, /* 000110011000 */ - 0x00, 0x00, /* 000000000000 */ - 0x04, 0x00, /* 000001000000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x0b, 0x00, /* 000010110000 */ - 0x0b, 0x00, /* 000010110000 */ - 0x19, 0x80, /* 000110011000 */ - 0x11, 0x80, /* 000100011000 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x20, 0xc0, /* 001000001100 */ - 0x60, 0x60, /* 011000000110 */ - 0x40, 0x60, /* 010000000110 */ - 0xe0, 0xf0, /* 111000001111 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 143 0x8f '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x19, 0x80, /* 000110011000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x04, 0x00, /* 000001000000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x0b, 0x00, /* 000010110000 */ - 0x0b, 0x00, /* 000010110000 */ - 0x19, 0x80, /* 000110011000 */ - 0x11, 0x80, /* 000100011000 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x20, 0xc0, /* 001000001100 */ - 0x60, 0x60, /* 011000000110 */ - 0x40, 0x60, /* 010000000110 */ - 0xe0, 0xf0, /* 111000001111 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 144 0x90 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x03, 0x00, /* 000000110000 */ - 0x06, 0x00, /* 000001100000 */ - 0x08, 0x00, /* 000010000000 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x30, 0x20, /* 001100000010 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x80, /* 001100001000 */ - 0x3f, 0x80, /* 001111111000 */ - 0x30, 0x80, /* 001100001000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x20, /* 001100000010 */ - 0x30, 0x20, /* 001100000010 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 145 0x91 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x3d, 0xe0, /* 001111011110 */ - 0x66, 0x30, /* 011001100011 */ - 0x46, 0x30, /* 010001100011 */ - 0x06, 0x30, /* 000001100011 */ - 0x3f, 0xf0, /* 001111111111 */ - 0x66, 0x00, /* 011001100000 */ - 0xc6, 0x00, /* 110001100000 */ - 0xc6, 0x00, /* 110001100000 */ - 0xe7, 0x30, /* 111001110011 */ - 0x7d, 0xe0, /* 011111011110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 146 0x92 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x03, 0xf0, /* 000000111111 */ - 0x07, 0x10, /* 000001110001 */ - 0x07, 0x10, /* 000001110001 */ - 0x0b, 0x00, /* 000010110000 */ - 0x0b, 0x00, /* 000010110000 */ - 0x0b, 0x20, /* 000010110010 */ - 0x13, 0xe0, /* 000100111110 */ - 0x13, 0x20, /* 000100110010 */ - 0x3f, 0x00, /* 001111110000 */ - 0x23, 0x00, /* 001000110000 */ - 0x23, 0x00, /* 001000110000 */ - 0x43, 0x10, /* 010000110001 */ - 0x43, 0x10, /* 010000110001 */ - 0xe7, 0xf0, /* 111001111111 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 147 0x93 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x02, 0x00, /* 000000100000 */ - 0x07, 0x00, /* 000001110000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x18, 0xc0, /* 000110001100 */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0x80, /* 000011111000 */ - 0x11, 0xc0, /* 000100011100 */ - 0x20, 0xe0, /* 001000001110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x70, 0x40, /* 011100000100 */ - 0x38, 0x80, /* 001110001000 */ - 0x1f, 0x00, /* 000111110000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 148 0x94 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x19, 0x80, /* 000110011000 */ - 0x19, 0x80, /* 000110011000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0x80, /* 000011111000 */ - 0x11, 0xc0, /* 000100011100 */ - 0x20, 0xe0, /* 001000001110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x70, 0x40, /* 011100000100 */ - 0x38, 0x80, /* 001110001000 */ - 0x1f, 0x00, /* 000111110000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 149 0x95 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x06, 0x00, /* 000001100000 */ - 0x03, 0x00, /* 000000110000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0x80, /* 000011111000 */ - 0x11, 0xc0, /* 000100011100 */ - 0x20, 0xe0, /* 001000001110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x70, 0x40, /* 011100000100 */ - 0x38, 0x80, /* 001110001000 */ - 0x1f, 0x00, /* 000111110000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 150 0x96 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x02, 0x00, /* 000000100000 */ - 0x07, 0x00, /* 000001110000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x18, 0xc0, /* 000110001100 */ - 0x00, 0x00, /* 000000000000 */ - 0x79, 0xe0, /* 011110011110 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x39, 0xc0, /* 001110011100 */ - 0x1e, 0x60, /* 000111100110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 151 0x97 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x18, 0x00, /* 000110000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x06, 0x00, /* 000001100000 */ - 0x00, 0x00, /* 000000000000 */ - 0x79, 0xe0, /* 011110011110 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x39, 0xc0, /* 001110011100 */ - 0x1e, 0x60, /* 000111100110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 152 0x98 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x19, 0x80, /* 000110011000 */ - 0x19, 0x80, /* 000110011000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0xf0, 0xf0, /* 111100001111 */ - 0x60, 0x20, /* 011000000010 */ - 0x30, 0x40, /* 001100000100 */ - 0x30, 0x40, /* 001100000100 */ - 0x18, 0x80, /* 000110001000 */ - 0x18, 0x80, /* 000110001000 */ - 0x0d, 0x00, /* 000011010000 */ - 0x0d, 0x00, /* 000011010000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x04, 0x00, /* 000001000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x08, 0x00, /* 000010000000 */ - 0x78, 0x00, /* 011110000000 */ - 0x70, 0x00, /* 011100000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 153 0x99 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x19, 0x80, /* 000110011000 */ - 0x19, 0x80, /* 000110011000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0x80, /* 000011111000 */ - 0x11, 0xc0, /* 000100011100 */ - 0x20, 0xc0, /* 001000001100 */ - 0x20, 0x60, /* 001000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x20, 0x40, /* 001000000100 */ - 0x30, 0x40, /* 001100000100 */ - 0x18, 0x80, /* 000110001000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 154 0x9a '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x19, 0x80, /* 000110011000 */ - 0x19, 0x80, /* 000110011000 */ - 0xe0, 0x30, /* 111000000011 */ - 0x60, 0x20, /* 011000000010 */ - 0x60, 0x20, /* 011000000010 */ - 0x60, 0x20, /* 011000000010 */ - 0x60, 0x20, /* 011000000010 */ - 0x60, 0x20, /* 011000000010 */ - 0x60, 0x20, /* 011000000010 */ - 0x60, 0x20, /* 011000000010 */ - 0x60, 0x20, /* 011000000010 */ - 0x60, 0x20, /* 011000000010 */ - 0x70, 0x40, /* 011100000100 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x1f, 0x80, /* 000111111000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 155 0x9b '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x1f, 0x80, /* 000111111000 */ - 0x36, 0xc0, /* 001101101100 */ - 0x26, 0xc0, /* 001001101100 */ - 0x66, 0x00, /* 011001100000 */ - 0x66, 0x00, /* 011001100000 */ - 0x66, 0x00, /* 011001100000 */ - 0x66, 0x00, /* 011001100000 */ - 0x76, 0x40, /* 011101100100 */ - 0x36, 0xc0, /* 001101101100 */ - 0x1f, 0x80, /* 000111111000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 156 0x9c '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0x80, /* 000011111000 */ - 0x1c, 0xc0, /* 000111001100 */ - 0x18, 0xc0, /* 000110001100 */ - 0x18, 0x00, /* 000110000000 */ - 0x18, 0x00, /* 000110000000 */ - 0x18, 0x00, /* 000110000000 */ - 0x7e, 0x00, /* 011111100000 */ - 0x7e, 0x00, /* 011111100000 */ - 0x18, 0x00, /* 000110000000 */ - 0x18, 0x00, /* 000110000000 */ - 0x18, 0x00, /* 000110000000 */ - 0x18, 0x00, /* 000110000000 */ - 0x3e, 0x20, /* 001111100010 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x61, 0xc0, /* 011000011100 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 157 0x9d '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x30, 0xc0, /* 001100001100 */ - 0x19, 0x80, /* 000110011000 */ - 0x19, 0x80, /* 000110011000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x06, 0x00, /* 000001100000 */ - 0x1f, 0x80, /* 000111111000 */ - 0x06, 0x00, /* 000001100000 */ - 0x1f, 0x80, /* 000111111000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 158 0x9e '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x7f, 0x80, /* 011111111000 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0x60, /* 001100000110 */ - 0x30, 0x60, /* 001100000110 */ - 0x30, 0x60, /* 001100000110 */ - 0x30, 0xc0, /* 001100001100 */ - 0x37, 0x80, /* 001101111000 */ - 0x30, 0x00, /* 001100000000 */ - 0x33, 0x00, /* 001100110000 */ - 0x37, 0x80, /* 001101111000 */ - 0x33, 0x00, /* 001100110000 */ - 0x33, 0x00, /* 001100110000 */ - 0x33, 0x30, /* 001100110011 */ - 0x31, 0xe0, /* 001100011110 */ - 0x78, 0xc0, /* 011110001100 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 159 0x9f '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0xc0, /* 000000001100 */ - 0x01, 0xe0, /* 000000011110 */ - 0x03, 0x30, /* 000000110011 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x3f, 0xe0, /* 001111111110 */ - 0x7f, 0xc0, /* 011111111100 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0xcc, 0x00, /* 110011000000 */ - 0x78, 0x00, /* 011110000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 160 0xa0 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x01, 0x80, /* 000000011000 */ - 0x03, 0x00, /* 000000110000 */ - 0x06, 0x00, /* 000001100000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0x80, /* 000011111000 */ - 0x18, 0xc0, /* 000110001100 */ - 0x10, 0xc0, /* 000100001100 */ - 0x03, 0xc0, /* 000000111100 */ - 0x1c, 0xc0, /* 000111001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x39, 0xc0, /* 001110011100 */ - 0x1e, 0xe0, /* 000111101110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 161 0xa1 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x01, 0x80, /* 000000011000 */ - 0x03, 0x00, /* 000000110000 */ - 0x06, 0x00, /* 000001100000 */ - 0x00, 0x00, /* 000000000000 */ - 0x1e, 0x00, /* 000111100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x1f, 0x80, /* 000111111000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 162 0xa2 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x01, 0x80, /* 000000011000 */ - 0x03, 0x00, /* 000000110000 */ - 0x06, 0x00, /* 000001100000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0x80, /* 000011111000 */ - 0x11, 0xc0, /* 000100011100 */ - 0x20, 0xe0, /* 001000001110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x70, 0x40, /* 011100000100 */ - 0x38, 0x80, /* 001110001000 */ - 0x1f, 0x00, /* 000111110000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 163 0xa3 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x01, 0x80, /* 000000011000 */ - 0x03, 0x00, /* 000000110000 */ - 0x06, 0x00, /* 000001100000 */ - 0x00, 0x00, /* 000000000000 */ - 0x79, 0xe0, /* 011110011110 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x39, 0xc0, /* 001110011100 */ - 0x1e, 0x60, /* 000111100110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 164 0xa4 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x1c, 0x40, /* 000111000100 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x23, 0x80, /* 001000111000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x27, 0x80, /* 001001111000 */ - 0x79, 0xc0, /* 011110011100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x79, 0xe0, /* 011110011110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 165 0xa5 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x1c, 0x40, /* 000111000100 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x23, 0x80, /* 001000111000 */ - 0xc0, 0x70, /* 110000000111 */ - 0x60, 0x20, /* 011000000010 */ - 0x70, 0x20, /* 011100000010 */ - 0x78, 0x20, /* 011110000010 */ - 0x5c, 0x20, /* 010111000010 */ - 0x4e, 0x20, /* 010011100010 */ - 0x47, 0x20, /* 010001110010 */ - 0x43, 0xa0, /* 010000111010 */ - 0x41, 0xe0, /* 010000011110 */ - 0x40, 0xe0, /* 010000001110 */ - 0x40, 0x60, /* 010000000110 */ - 0xe0, 0x30, /* 111000000011 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 166 0xa6 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x1f, 0x00, /* 000111110000 */ - 0x31, 0x80, /* 001100011000 */ - 0x01, 0x80, /* 000000011000 */ - 0x07, 0x80, /* 000001111000 */ - 0x19, 0x80, /* 000110011000 */ - 0x31, 0x80, /* 001100011000 */ - 0x31, 0x80, /* 001100011000 */ - 0x33, 0x80, /* 001100111000 */ - 0x1d, 0xc0, /* 000111011100 */ - 0x00, 0x00, /* 000000000000 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 167 0xa7 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x07, 0x00, /* 000001110000 */ - 0x19, 0x80, /* 000110011000 */ - 0x10, 0xc0, /* 000100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0x80, /* 001100001000 */ - 0x19, 0x80, /* 000110011000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x00, 0x00, /* 000000000000 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 168 0xa8 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x03, 0x00, /* 000000110000 */ - 0x03, 0x00, /* 000000110000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x03, 0x00, /* 000000110000 */ - 0x03, 0x00, /* 000000110000 */ - 0x06, 0x00, /* 000001100000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x18, 0x00, /* 000110000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x40, /* 001100000100 */ - 0x39, 0xc0, /* 001110011100 */ - 0x1f, 0x80, /* 000111111000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 169 0xa9 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 170 0xaa '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x00, 0xc0, /* 000000001100 */ - 0x00, 0xc0, /* 000000001100 */ - 0x00, 0xc0, /* 000000001100 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 171 0xab '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x10, 0x00, /* 000100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x10, 0x00, /* 000100000000 */ - 0x10, 0x40, /* 000100000100 */ - 0x10, 0x80, /* 000100001000 */ - 0x11, 0x00, /* 000100010000 */ - 0x3a, 0x00, /* 001110100000 */ - 0x05, 0xc0, /* 000001011100 */ - 0x0a, 0x20, /* 000010100010 */ - 0x10, 0x20, /* 000100000010 */ - 0x20, 0xc0, /* 001000001100 */ - 0x41, 0x00, /* 010000010000 */ - 0x02, 0x00, /* 000000100000 */ - 0x03, 0xe0, /* 000000111110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 172 0xac '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x10, 0x00, /* 000100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x10, 0x00, /* 000100000000 */ - 0x10, 0x40, /* 000100000100 */ - 0x10, 0x80, /* 000100001000 */ - 0x11, 0x00, /* 000100010000 */ - 0x3a, 0x40, /* 001110100100 */ - 0x04, 0xc0, /* 000001001100 */ - 0x09, 0x40, /* 000010010100 */ - 0x12, 0x40, /* 000100100100 */ - 0x24, 0x40, /* 001001000100 */ - 0x47, 0xe0, /* 010001111110 */ - 0x00, 0x40, /* 000000000100 */ - 0x00, 0x40, /* 000000000100 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 173 0xad '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 174 0xae '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x06, 0x60, /* 000001100110 */ - 0x0c, 0xc0, /* 000011001100 */ - 0x19, 0x80, /* 000110011000 */ - 0x33, 0x00, /* 001100110000 */ - 0x66, 0x00, /* 011001100000 */ - 0x33, 0x00, /* 001100110000 */ - 0x19, 0x80, /* 000110011000 */ - 0x0c, 0xc0, /* 000011001100 */ - 0x06, 0x60, /* 000001100110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 175 0xaf '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x66, 0x00, /* 011001100000 */ - 0x33, 0x00, /* 001100110000 */ - 0x19, 0x80, /* 000110011000 */ - 0x0c, 0xc0, /* 000011001100 */ - 0x06, 0x60, /* 000001100110 */ - 0x0c, 0xc0, /* 000011001100 */ - 0x19, 0x80, /* 000110011000 */ - 0x33, 0x00, /* 001100110000 */ - 0x66, 0x00, /* 011001100000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 176 0xb0 '.' */ - 0x0c, 0x30, /* 000011000011 */ - 0x08, 0x20, /* 000010000010 */ - 0x61, 0x80, /* 011000011000 */ - 0x20, 0x80, /* 001000001000 */ - 0x0c, 0x30, /* 000011000011 */ - 0x08, 0x20, /* 000010000010 */ - 0x61, 0x80, /* 011000011000 */ - 0x20, 0x80, /* 001000001000 */ - 0x0c, 0x30, /* 000011000011 */ - 0x08, 0x20, /* 000010000010 */ - 0x61, 0x80, /* 011000011000 */ - 0x20, 0x80, /* 001000001000 */ - 0x0c, 0x30, /* 000011000011 */ - 0x08, 0x20, /* 000010000010 */ - 0x61, 0x80, /* 011000011000 */ - 0x20, 0x80, /* 001000001000 */ - 0x0c, 0x30, /* 000011000011 */ - 0x08, 0x20, /* 000010000010 */ - 0x61, 0x80, /* 011000011000 */ - 0x20, 0x80, /* 001000001000 */ - 0x0c, 0x30, /* 000011000011 */ - 0x08, 0x20, /* 000010000010 */ - - /* 177 0xb1 '.' */ - 0x77, 0x70, /* 011101110111 */ - 0x22, 0x20, /* 001000100010 */ - 0x88, 0x80, /* 100010001000 */ - 0xdd, 0xd0, /* 110111011101 */ - 0x88, 0x80, /* 100010001000 */ - 0x22, 0x20, /* 001000100010 */ - 0x77, 0x70, /* 011101110111 */ - 0x22, 0x20, /* 001000100010 */ - 0x88, 0x80, /* 100010001000 */ - 0xdd, 0xd0, /* 110111011101 */ - 0x88, 0x80, /* 100010001000 */ - 0x22, 0x20, /* 001000100010 */ - 0x77, 0x70, /* 011101110111 */ - 0x22, 0x20, /* 001000100010 */ - 0x88, 0x80, /* 100010001000 */ - 0xdd, 0xd0, /* 110111011101 */ - 0x88, 0x80, /* 100010001000 */ - 0x22, 0x20, /* 001000100010 */ - 0x77, 0x70, /* 011101110111 */ - 0x22, 0x20, /* 001000100010 */ - 0x88, 0x80, /* 100010001000 */ - 0xdd, 0xd0, /* 110111011101 */ - - /* 178 0xb2 '.' */ - 0xf3, 0xc0, /* 111100111100 */ - 0xf7, 0xd0, /* 111101111101 */ - 0x9e, 0x70, /* 100111100111 */ - 0xdf, 0x70, /* 110111110111 */ - 0xf3, 0xc0, /* 111100111100 */ - 0xf7, 0xd0, /* 111101111101 */ - 0x9e, 0x70, /* 100111100111 */ - 0xdf, 0x70, /* 110111110111 */ - 0xf3, 0xc0, /* 111100111100 */ - 0xf7, 0xd0, /* 111101111101 */ - 0x9e, 0x70, /* 100111100111 */ - 0xdf, 0x70, /* 110111110111 */ - 0xf3, 0xc0, /* 111100111100 */ - 0xf7, 0xd0, /* 111101111101 */ - 0x9e, 0x70, /* 100111100111 */ - 0xdf, 0x70, /* 110111110111 */ - 0xf3, 0xc0, /* 111100111100 */ - 0xf7, 0xd0, /* 111101111101 */ - 0x9e, 0x70, /* 100111100111 */ - 0xdf, 0x70, /* 110111110111 */ - 0xf3, 0xc0, /* 111100111100 */ - 0xf7, 0xd0, /* 111101111101 */ - - /* 179 0xb3 '.' */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - - /* 180 0xb4 '.' */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0xfe, 0x00, /* 111111100000 */ - 0xfe, 0x00, /* 111111100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - - /* 181 0xb5 '.' */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0xfe, 0x00, /* 111111100000 */ - 0xfe, 0x00, /* 111111100000 */ - 0x06, 0x00, /* 000001100000 */ - 0xfe, 0x00, /* 111111100000 */ - 0xfe, 0x00, /* 111111100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - - /* 182 0xb6 '.' */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0xfd, 0x80, /* 111111011000 */ - 0xfd, 0x80, /* 111111011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - - /* 183 0xb7 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0xff, 0x80, /* 111111111000 */ - 0xff, 0x80, /* 111111111000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - - /* 184 0xb8 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0xfe, 0x00, /* 111111100000 */ - 0xfe, 0x00, /* 111111100000 */ - 0x06, 0x00, /* 000001100000 */ - 0xfe, 0x00, /* 111111100000 */ - 0xfe, 0x00, /* 111111100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - - /* 185 0xb9 '.' */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0xfd, 0x80, /* 111111011000 */ - 0xfd, 0x80, /* 111111011000 */ - 0x01, 0x80, /* 000000011000 */ - 0xfd, 0x80, /* 111111011000 */ - 0xfd, 0x80, /* 111111011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - - /* 186 0xba '.' */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - - /* 187 0xbb '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0xff, 0x80, /* 111111111000 */ - 0xff, 0x80, /* 111111111000 */ - 0x01, 0x80, /* 000000011000 */ - 0xfd, 0x80, /* 111111011000 */ - 0xfd, 0x80, /* 111111011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - - /* 188 0xbc '.' */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0xfd, 0x80, /* 111111011000 */ - 0xfd, 0x80, /* 111111011000 */ - 0x01, 0x80, /* 000000011000 */ - 0xff, 0x80, /* 111111111000 */ - 0xff, 0x80, /* 111111111000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 189 0xbd '.' */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0xff, 0x80, /* 111111111000 */ - 0xff, 0x80, /* 111111111000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 190 0xbe '.' */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0xfe, 0x00, /* 111111100000 */ - 0xfe, 0x00, /* 111111100000 */ - 0x06, 0x00, /* 000001100000 */ - 0xfe, 0x00, /* 111111100000 */ - 0xfe, 0x00, /* 111111100000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 191 0xbf '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0xfe, 0x00, /* 111111100000 */ - 0xfe, 0x00, /* 111111100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - - /* 192 0xc0 '.' */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x07, 0xf0, /* 000001111111 */ - 0x07, 0xf0, /* 000001111111 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 193 0xc1 '.' */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 194 0xc2 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - - /* 195 0xc3 '.' */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x07, 0xf0, /* 000001111111 */ - 0x07, 0xf0, /* 000001111111 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - - /* 196 0xc4 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 197 0xc5 '.' */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - - /* 198 0xc6 '.' */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x07, 0xf0, /* 000001111111 */ - 0x07, 0xf0, /* 000001111111 */ - 0x06, 0x00, /* 000001100000 */ - 0x07, 0xf0, /* 000001111111 */ - 0x07, 0xf0, /* 000001111111 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - - /* 199 0xc7 '.' */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0xf0, /* 000011011111 */ - 0x0d, 0xf0, /* 000011011111 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - - /* 200 0xc8 '.' */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0xf0, /* 000011011111 */ - 0x0d, 0xf0, /* 000011011111 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0f, 0xf0, /* 000011111111 */ - 0x0f, 0xf0, /* 000011111111 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 201 0xc9 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0xf0, /* 000011111111 */ - 0x0f, 0xf0, /* 000011111111 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0d, 0xf0, /* 000011011111 */ - 0x0d, 0xf0, /* 000011011111 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - - /* 202 0xca '.' */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0xfd, 0xf0, /* 111111011111 */ - 0xfd, 0xf0, /* 111111011111 */ - 0x00, 0x00, /* 000000000000 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 203 0xcb '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0x00, 0x00, /* 000000000000 */ - 0xfd, 0xf0, /* 111111011111 */ - 0xfd, 0xf0, /* 111111011111 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - - /* 204 0xcc '.' */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0xf0, /* 000011011111 */ - 0x0d, 0xf0, /* 000011011111 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0d, 0xf0, /* 000011011111 */ - 0x0d, 0xf0, /* 000011011111 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - - /* 205 0xcd '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0x00, 0x00, /* 000000000000 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 206 0xce '.' */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0xfd, 0xf0, /* 111111011111 */ - 0xfd, 0xf0, /* 111111011111 */ - 0x00, 0x00, /* 000000000000 */ - 0xfd, 0xf0, /* 111111011111 */ - 0xfd, 0xf0, /* 111111011111 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - - /* 207 0xcf '.' */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0x00, 0x00, /* 000000000000 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 208 0xd0 '.' */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 209 0xd1 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0x00, 0x00, /* 000000000000 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - - /* 210 0xd2 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - - /* 211 0xd3 '.' */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0f, 0xf0, /* 000011111111 */ - 0x0f, 0xf0, /* 000011111111 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 212 0xd4 '.' */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x07, 0xf0, /* 000001111111 */ - 0x07, 0xf0, /* 000001111111 */ - 0x06, 0x00, /* 000001100000 */ - 0x07, 0xf0, /* 000001111111 */ - 0x07, 0xf0, /* 000001111111 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 213 0xd5 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x07, 0xf0, /* 000001111111 */ - 0x07, 0xf0, /* 000001111111 */ - 0x06, 0x00, /* 000001100000 */ - 0x07, 0xf0, /* 000001111111 */ - 0x07, 0xf0, /* 000001111111 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - - /* 214 0xd6 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0xf0, /* 000011111111 */ - 0x0f, 0xf0, /* 000011111111 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - - /* 215 0xd7 '.' */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - 0x0d, 0x80, /* 000011011000 */ - - /* 216 0xd8 '.' */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0x06, 0x00, /* 000001100000 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - - /* 217 0xd9 '.' */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0xfe, 0x00, /* 111111100000 */ - 0xfe, 0x00, /* 111111100000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 218 0xda '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x07, 0xf0, /* 000001111111 */ - 0x07, 0xf0, /* 000001111111 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - - /* 219 0xdb '.' */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - - /* 220 0xdc '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - - /* 221 0xdd '.' */ - 0xfc, 0x00, /* 111111000000 */ - 0xfc, 0x00, /* 111111000000 */ - 0xfc, 0x00, /* 111111000000 */ - 0xfc, 0x00, /* 111111000000 */ - 0xfc, 0x00, /* 111111000000 */ - 0xfc, 0x00, /* 111111000000 */ - 0xfc, 0x00, /* 111111000000 */ - 0xfc, 0x00, /* 111111000000 */ - 0xfc, 0x00, /* 111111000000 */ - 0xfc, 0x00, /* 111111000000 */ - 0xfc, 0x00, /* 111111000000 */ - 0xfc, 0x00, /* 111111000000 */ - 0xfc, 0x00, /* 111111000000 */ - 0xfc, 0x00, /* 111111000000 */ - 0xfc, 0x00, /* 111111000000 */ - 0xfc, 0x00, /* 111111000000 */ - 0xfc, 0x00, /* 111111000000 */ - 0xfc, 0x00, /* 111111000000 */ - 0xfc, 0x00, /* 111111000000 */ - 0xfc, 0x00, /* 111111000000 */ - 0xfc, 0x00, /* 111111000000 */ - 0xfc, 0x00, /* 111111000000 */ - - /* 222 0xde '.' */ - 0x03, 0xf0, /* 000000111111 */ - 0x03, 0xf0, /* 000000111111 */ - 0x03, 0xf0, /* 000000111111 */ - 0x03, 0xf0, /* 000000111111 */ - 0x03, 0xf0, /* 000000111111 */ - 0x03, 0xf0, /* 000000111111 */ - 0x03, 0xf0, /* 000000111111 */ - 0x03, 0xf0, /* 000000111111 */ - 0x03, 0xf0, /* 000000111111 */ - 0x03, 0xf0, /* 000000111111 */ - 0x03, 0xf0, /* 000000111111 */ - 0x03, 0xf0, /* 000000111111 */ - 0x03, 0xf0, /* 000000111111 */ - 0x03, 0xf0, /* 000000111111 */ - 0x03, 0xf0, /* 000000111111 */ - 0x03, 0xf0, /* 000000111111 */ - 0x03, 0xf0, /* 000000111111 */ - 0x03, 0xf0, /* 000000111111 */ - 0x03, 0xf0, /* 000000111111 */ - 0x03, 0xf0, /* 000000111111 */ - 0x03, 0xf0, /* 000000111111 */ - 0x03, 0xf0, /* 000000111111 */ - - /* 223 0xdf '.' */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0xff, 0xf0, /* 111111111111 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 224 0xe0 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0x60, /* 000011110110 */ - 0x13, 0xe0, /* 000100111110 */ - 0x21, 0xc0, /* 001000011100 */ - 0x60, 0xc0, /* 011000001100 */ - 0x60, 0xc0, /* 011000001100 */ - 0x60, 0xc0, /* 011000001100 */ - 0x60, 0xc0, /* 011000001100 */ - 0x70, 0x80, /* 011100001000 */ - 0x39, 0xc0, /* 001110011100 */ - 0x1f, 0x60, /* 000111110110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 225 0xe1 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x19, 0x80, /* 000110011000 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x31, 0x80, /* 001100011000 */ - 0x37, 0x80, /* 001101111000 */ - 0x31, 0x80, /* 001100011000 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x31, 0x80, /* 001100011000 */ - 0x77, 0x00, /* 011101110000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 226 0xe2 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x3f, 0xe0, /* 001111111110 */ - 0x3f, 0xe0, /* 001111111110 */ - 0x30, 0x60, /* 001100000110 */ - 0x30, 0x60, /* 001100000110 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 227 0xe3 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x19, 0x80, /* 000110011000 */ - 0x19, 0x80, /* 000110011000 */ - 0x19, 0x80, /* 000110011000 */ - 0x19, 0x80, /* 000110011000 */ - 0x19, 0x80, /* 000110011000 */ - 0x19, 0x80, /* 000110011000 */ - 0x19, 0x80, /* 000110011000 */ - 0x19, 0x80, /* 000110011000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 228 0xe4 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x60, 0x60, /* 011000000110 */ - 0x30, 0x60, /* 001100000110 */ - 0x30, 0x00, /* 001100000000 */ - 0x18, 0x00, /* 000110000000 */ - 0x18, 0x00, /* 000110000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x18, 0x00, /* 000110000000 */ - 0x18, 0x00, /* 000110000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x60, /* 001100000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 229 0xe5 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x07, 0xe0, /* 000001111110 */ - 0x0f, 0xe0, /* 000011111110 */ - 0x13, 0x80, /* 000100111000 */ - 0x21, 0xc0, /* 001000011100 */ - 0x60, 0xc0, /* 011000001100 */ - 0x60, 0xc0, /* 011000001100 */ - 0x60, 0xc0, /* 011000001100 */ - 0x60, 0xc0, /* 011000001100 */ - 0x70, 0x80, /* 011100001000 */ - 0x39, 0x00, /* 001110010000 */ - 0x1e, 0x00, /* 000111100000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 230 0xe6 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x39, 0xc0, /* 001110011100 */ - 0x36, 0xe0, /* 001101101110 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 231 0xe7 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x19, 0x80, /* 000110011000 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x66, 0x60, /* 011001100110 */ - 0x66, 0x60, /* 011001100110 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 232 0xe8 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x19, 0x80, /* 000110011000 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x19, 0x80, /* 000110011000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 233 0xe9 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x1f, 0x80, /* 000111111000 */ - 0x30, 0xc0, /* 001100001100 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x30, 0xc0, /* 001100001100 */ - 0x1f, 0x80, /* 000111111000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 234 0xea '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x1f, 0x00, /* 000111110000 */ - 0x31, 0x80, /* 001100011000 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x19, 0x80, /* 000110011000 */ - 0x19, 0x80, /* 000110011000 */ - 0xd9, 0xb0, /* 110110011011 */ - 0x79, 0xe0, /* 011110011110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 235 0xeb '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x07, 0x80, /* 000001111000 */ - 0x0c, 0xc0, /* 000011001100 */ - 0x18, 0x60, /* 000110000110 */ - 0x18, 0x00, /* 000110000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x06, 0x00, /* 000001100000 */ - 0x03, 0x00, /* 000000110000 */ - 0x0f, 0x80, /* 000011111000 */ - 0x11, 0xc0, /* 000100011100 */ - 0x20, 0xe0, /* 001000001110 */ - 0x60, 0x60, /* 011000000110 */ - 0x60, 0x60, /* 011000000110 */ - 0x70, 0x40, /* 011100000100 */ - 0x38, 0x80, /* 001110001000 */ - 0x1f, 0x00, /* 000111110000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 236 0xec '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x39, 0xc0, /* 001110011100 */ - 0x6f, 0x60, /* 011011110110 */ - 0x66, 0x60, /* 011001100110 */ - 0xc6, 0x30, /* 110001100011 */ - 0xc6, 0x30, /* 110001100011 */ - 0x66, 0x60, /* 011001100110 */ - 0x6f, 0x60, /* 011011110110 */ - 0x39, 0xc0, /* 001110011100 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 237 0xed '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0xc0, /* 000000001100 */ - 0x00, 0xc0, /* 000000001100 */ - 0x01, 0x80, /* 000000011000 */ - 0x01, 0x80, /* 000000011000 */ - 0x3b, 0xc0, /* 001110111100 */ - 0x6f, 0x60, /* 011011110110 */ - 0x66, 0x60, /* 011001100110 */ - 0xc6, 0x30, /* 110001100011 */ - 0xc6, 0x30, /* 110001100011 */ - 0x66, 0x60, /* 011001100110 */ - 0x6f, 0x60, /* 011011110110 */ - 0x3d, 0xc0, /* 001111011100 */ - 0x18, 0x00, /* 000110000000 */ - 0x18, 0x00, /* 000110000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x30, 0x00, /* 001100000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 238 0xee '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x01, 0xc0, /* 000000011100 */ - 0x03, 0x00, /* 000000110000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x18, 0x00, /* 000110000000 */ - 0x1f, 0xc0, /* 000111111100 */ - 0x18, 0x00, /* 000110000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x03, 0x00, /* 000000110000 */ - 0x01, 0xc0, /* 000000011100 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 239 0xef '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x1f, 0x80, /* 000111111000 */ - 0x39, 0xc0, /* 001110011100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x30, 0xc0, /* 001100001100 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 240 0xf0 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 241 0xf1 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x00, 0x00, /* 000000000000 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 242 0xf2 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x38, 0x00, /* 001110000000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x03, 0x80, /* 000000111000 */ - 0x00, 0xe0, /* 000000001110 */ - 0x00, 0xe0, /* 000000001110 */ - 0x03, 0x80, /* 000000111000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x38, 0x00, /* 001110000000 */ - 0x60, 0x00, /* 011000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 243 0xf3 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x60, /* 000000000110 */ - 0x01, 0xc0, /* 000000011100 */ - 0x07, 0x00, /* 000001110000 */ - 0x1c, 0x00, /* 000111000000 */ - 0x70, 0x00, /* 011100000000 */ - 0x70, 0x00, /* 011100000000 */ - 0x1c, 0x00, /* 000111000000 */ - 0x07, 0x00, /* 000001110000 */ - 0x01, 0xc0, /* 000000011100 */ - 0x00, 0x60, /* 000000000110 */ - 0x00, 0x00, /* 000000000000 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 244 0xf4 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x03, 0x80, /* 000000111000 */ - 0x07, 0xc0, /* 000001111100 */ - 0x0c, 0x60, /* 000011000110 */ - 0x0c, 0x60, /* 000011000110 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x0c, 0x00, /* 000011000000 */ - - /* 245 0xf5 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x1c, 0x00, /* 000111000000 */ - 0x3e, 0x00, /* 001111100000 */ - 0x63, 0x00, /* 011000110000 */ - 0x63, 0x00, /* 011000110000 */ - 0x03, 0x00, /* 000000110000 */ - 0x03, 0x00, /* 000000110000 */ - 0x03, 0x00, /* 000000110000 */ - 0x03, 0x00, /* 000000110000 */ - 0x03, 0x00, /* 000000110000 */ - 0x03, 0x00, /* 000000110000 */ - 0x03, 0x00, /* 000000110000 */ - 0x03, 0x00, /* 000000110000 */ - 0x03, 0x00, /* 000000110000 */ - 0x03, 0x00, /* 000000110000 */ - 0x03, 0x00, /* 000000110000 */ - 0x03, 0x00, /* 000000110000 */ - 0x03, 0x00, /* 000000110000 */ - 0x03, 0x00, /* 000000110000 */ - 0x03, 0x00, /* 000000110000 */ - 0x03, 0x00, /* 000000110000 */ - 0x03, 0x00, /* 000000110000 */ - - /* 246 0xf6 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x7f, 0xe0, /* 011111111110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 247 0xf7 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x38, 0x00, /* 001110000000 */ - 0x6c, 0x00, /* 011011000000 */ - 0x06, 0x30, /* 000001100011 */ - 0x03, 0x60, /* 000000110110 */ - 0x39, 0xc0, /* 001110011100 */ - 0x6c, 0x00, /* 011011000000 */ - 0x06, 0x30, /* 000001100011 */ - 0x03, 0x60, /* 000000110110 */ - 0x01, 0xc0, /* 000000011100 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 248 0xf8 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x19, 0x80, /* 000110011000 */ - 0x19, 0x80, /* 000110011000 */ - 0x19, 0x80, /* 000110011000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 249 0xf9 '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x1c, 0x00, /* 000111000000 */ - 0x3e, 0x00, /* 001111100000 */ - 0x3e, 0x00, /* 001111100000 */ - 0x3e, 0x00, /* 001111100000 */ - 0x1c, 0x00, /* 000111000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 250 0xfa '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x18, 0x00, /* 000110000000 */ - 0x3c, 0x00, /* 001111000000 */ - 0x3c, 0x00, /* 001111000000 */ - 0x18, 0x00, /* 000110000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 251 0xfb '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x07, 0xe0, /* 000001111110 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x06, 0x00, /* 000001100000 */ - 0xc6, 0x00, /* 110001100000 */ - 0x66, 0x00, /* 011001100000 */ - 0x36, 0x00, /* 001101100000 */ - 0x1e, 0x00, /* 000111100000 */ - 0x0e, 0x00, /* 000011100000 */ - 0x06, 0x00, /* 000001100000 */ - 0x02, 0x00, /* 000000100000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 252 0xfc '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x13, 0x80, /* 000100111000 */ - 0x3d, 0xc0, /* 001111011100 */ - 0x18, 0xc0, /* 000110001100 */ - 0x18, 0xc0, /* 000110001100 */ - 0x18, 0xc0, /* 000110001100 */ - 0x18, 0xc0, /* 000110001100 */ - 0x3d, 0xe0, /* 001111011110 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 253 0xfd '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x0f, 0x00, /* 000011110000 */ - 0x1f, 0x80, /* 000111111000 */ - 0x31, 0x80, /* 001100011000 */ - 0x21, 0x80, /* 001000011000 */ - 0x03, 0x00, /* 000000110000 */ - 0x06, 0x00, /* 000001100000 */ - 0x0c, 0x00, /* 000011000000 */ - 0x18, 0x40, /* 000110000100 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 254 0xfe '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x3f, 0xc0, /* 001111111100 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - - /* 255 0xff '.' */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - 0x00, 0x00, /* 000000000000 */ - -}; - - -const struct font_desc font_sun_12x22 = { - .idx = SUN12x22_IDX, - .name = "SUN12x22", - .width = 12, - .height = 22, - .data = fontdata_sun12x22, -#ifdef __sparc__ - .pref = 5, -#else - .pref = -1, -#endif -}; diff --git a/drivers/video/console/font_sun8x16.c b/drivers/video/console/font_sun8x16.c deleted file mode 100644 index 268151325b83..000000000000 --- a/drivers/video/console/font_sun8x16.c +++ /dev/null @@ -1,275 +0,0 @@ -#include - -#define FONTDATAMAX 4096 - -static const unsigned char fontdata_sun8x16[FONTDATAMAX] = { -/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x7e,0x81,0xa5,0x81,0x81,0xbd,0x99,0x81,0x81,0x7e,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x7e,0xff,0xdb,0xff,0xff,0xc3,0xe7,0xff,0xff,0x7e,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0x6c,0xfe,0xfe,0xfe,0xfe,0x7c,0x38,0x10,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0x10,0x38,0x7c,0xfe,0x7c,0x38,0x10,0x00,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x18,0x3c,0x3c,0xe7,0xe7,0xe7,0x18,0x18,0x3c,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x18,0x3c,0x7e,0xff,0xff,0x7e,0x18,0x18,0x3c,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x3c,0x3c,0x18,0x00,0x00,0x00,0x00,0x00,0x00, -/* */ 0xff,0xff,0xff,0xff,0xff,0xff,0xe7,0xc3,0xc3,0xe7,0xff,0xff,0xff,0xff,0xff,0xff, -/* */ 0x00,0x00,0x00,0x00,0x00,0x3c,0x66,0x42,0x42,0x66,0x3c,0x00,0x00,0x00,0x00,0x00, -/* */ 0xff,0xff,0xff,0xff,0xff,0xc3,0x99,0xbd,0xbd,0x99,0xc3,0xff,0xff,0xff,0xff,0xff, -/* */ 0x00,0x00,0x1e,0x0e,0x1a,0x32,0x78,0xcc,0xcc,0xcc,0xcc,0x78,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x3c,0x66,0x66,0x66,0x66,0x3c,0x18,0x7e,0x18,0x18,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x3f,0x33,0x3f,0x30,0x30,0x30,0x30,0x70,0xf0,0xe0,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x7f,0x63,0x7f,0x63,0x63,0x63,0x63,0x67,0xe7,0xe6,0xc0,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x18,0x18,0xdb,0x3c,0xe7,0x3c,0xdb,0x18,0x18,0x00,0x00,0x00,0x00, -/* */ 0x00,0x80,0xc0,0xe0,0xf0,0xf8,0xfe,0xf8,0xf0,0xe0,0xc0,0x80,0x00,0x00,0x00,0x00, -/* */ 0x00,0x02,0x06,0x0e,0x1e,0x3e,0xfe,0x3e,0x1e,0x0e,0x06,0x02,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x18,0x3c,0x7e,0x18,0x18,0x18,0x7e,0x3c,0x18,0x00,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x66,0x66,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x7f,0xdb,0xdb,0xdb,0x7b,0x1b,0x1b,0x1b,0x1b,0x1b,0x00,0x00,0x00,0x00, -/* */ 0x00,0x7c,0xc6,0x60,0x38,0x6c,0xc6,0xc6,0x6c,0x38,0x0c,0xc6,0x7c,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfe,0xfe,0xfe,0xfe,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x18,0x3c,0x7e,0x18,0x18,0x18,0x7e,0x3c,0x18,0x7e,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x18,0x3c,0x7e,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x7e,0x3c,0x18,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0x00,0x18,0x0c,0xfe,0x0c,0x18,0x00,0x00,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0x00,0x30,0x60,0xfe,0x60,0x30,0x00,0x00,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0xc0,0xc0,0xfe,0x00,0x00,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0x00,0x24,0x66,0xff,0x66,0x24,0x00,0x00,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0x10,0x38,0x38,0x7c,0x7c,0xfe,0xfe,0x00,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0xfe,0xfe,0x7c,0x7c,0x38,0x38,0x10,0x00,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/*!*/ 0x00,0x00,0x18,0x3c,0x3c,0x3c,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00, -/*"*/ 0x00,0x66,0x66,0x66,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/*#*/ 0x00,0x00,0x00,0x6c,0x6c,0xfe,0x6c,0x6c,0x6c,0xfe,0x6c,0x6c,0x00,0x00,0x00,0x00, -/*$*/ 0x18,0x18,0x7c,0xc6,0xc2,0xc0,0x7c,0x06,0x06,0x86,0xc6,0x7c,0x18,0x18,0x00,0x00, -/*%*/ 0x00,0x00,0x00,0x00,0xc2,0xc6,0x0c,0x18,0x30,0x60,0xc6,0x86,0x00,0x00,0x00,0x00, -/*&*/ 0x00,0x00,0x38,0x6c,0x6c,0x38,0x76,0xdc,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00, -/*'*/ 0x00,0x30,0x30,0x30,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/*(*/ 0x00,0x00,0x0c,0x18,0x30,0x30,0x30,0x30,0x30,0x30,0x18,0x0c,0x00,0x00,0x00,0x00, -/*)*/ 0x00,0x00,0x30,0x18,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x18,0x30,0x00,0x00,0x00,0x00, -/***/ 0x00,0x00,0x00,0x00,0x00,0x66,0x3c,0xff,0x3c,0x66,0x00,0x00,0x00,0x00,0x00,0x00, -/*+*/ 0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x7e,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00, -/*,*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x18,0x30,0x00,0x00,0x00, -/*-*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/*.*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0x02,0x06,0x0c,0x18,0x30,0x60,0xc0,0x80,0x00,0x00,0x00,0x00, -/*0*/ 0x00,0x00,0x7c,0xc6,0xc6,0xce,0xde,0xf6,0xe6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, -/*1*/ 0x00,0x00,0x18,0x38,0x78,0x18,0x18,0x18,0x18,0x18,0x18,0x7e,0x00,0x00,0x00,0x00, -/*2*/ 0x00,0x00,0x7c,0xc6,0x06,0x0c,0x18,0x30,0x60,0xc0,0xc6,0xfe,0x00,0x00,0x00,0x00, -/*3*/ 0x00,0x00,0x7c,0xc6,0x06,0x06,0x3c,0x06,0x06,0x06,0xc6,0x7c,0x00,0x00,0x00,0x00, -/*4*/ 0x00,0x00,0x0c,0x1c,0x3c,0x6c,0xcc,0xfe,0x0c,0x0c,0x0c,0x1e,0x00,0x00,0x00,0x00, -/*5*/ 0x00,0x00,0xfe,0xc0,0xc0,0xc0,0xfc,0x06,0x06,0x06,0xc6,0x7c,0x00,0x00,0x00,0x00, -/*6*/ 0x00,0x00,0x38,0x60,0xc0,0xc0,0xfc,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, -/*7*/ 0x00,0x00,0xfe,0xc6,0x06,0x06,0x0c,0x18,0x30,0x30,0x30,0x30,0x00,0x00,0x00,0x00, -/*8*/ 0x00,0x00,0x7c,0xc6,0xc6,0xc6,0x7c,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, -/*9*/ 0x00,0x00,0x7c,0xc6,0xc6,0xc6,0x7e,0x06,0x06,0x06,0x0c,0x78,0x00,0x00,0x00,0x00, -/*:*/ 0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00, -/*;*/ 0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x30,0x00,0x00,0x00,0x00, -/*<*/ 0x00,0x00,0x00,0x06,0x0c,0x18,0x30,0x60,0x30,0x18,0x0c,0x06,0x00,0x00,0x00,0x00, -/*=*/ 0x00,0x00,0x00,0x00,0x00,0x7e,0x00,0x00,0x7e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/*>*/ 0x00,0x00,0x00,0x60,0x30,0x18,0x0c,0x06,0x0c,0x18,0x30,0x60,0x00,0x00,0x00,0x00, -/*?*/ 0x00,0x00,0x7c,0xc6,0xc6,0x0c,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00, -/*@*/ 0x00,0x00,0x7c,0xc6,0xc6,0xc6,0xde,0xde,0xde,0xdc,0xc0,0x7c,0x00,0x00,0x00,0x00, -/*A*/ 0x00,0x00,0x10,0x38,0x6c,0xc6,0xc6,0xfe,0xc6,0xc6,0xc6,0xc6,0x00,0x00,0x00,0x00, -/*B*/ 0x00,0x00,0xfc,0x66,0x66,0x66,0x7c,0x66,0x66,0x66,0x66,0xfc,0x00,0x00,0x00,0x00, -/*C*/ 0x00,0x00,0x3c,0x66,0xc2,0xc0,0xc0,0xc0,0xc0,0xc2,0x66,0x3c,0x00,0x00,0x00,0x00, -/*D*/ 0x00,0x00,0xf8,0x6c,0x66,0x66,0x66,0x66,0x66,0x66,0x6c,0xf8,0x00,0x00,0x00,0x00, -/*E*/ 0x00,0x00,0xfe,0x66,0x62,0x68,0x78,0x68,0x60,0x62,0x66,0xfe,0x00,0x00,0x00,0x00, -/*F*/ 0x00,0x00,0xfe,0x66,0x62,0x68,0x78,0x68,0x60,0x60,0x60,0xf0,0x00,0x00,0x00,0x00, -/*G*/ 0x00,0x00,0x3c,0x66,0xc2,0xc0,0xc0,0xde,0xc6,0xc6,0x66,0x3a,0x00,0x00,0x00,0x00, -/*H*/ 0x00,0x00,0xc6,0xc6,0xc6,0xc6,0xfe,0xc6,0xc6,0xc6,0xc6,0xc6,0x00,0x00,0x00,0x00, -/*I*/ 0x00,0x00,0x3c,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00, -/*J*/ 0x00,0x00,0x1e,0x0c,0x0c,0x0c,0x0c,0x0c,0xcc,0xcc,0xcc,0x78,0x00,0x00,0x00,0x00, -/*K*/ 0x00,0x00,0xe6,0x66,0x66,0x6c,0x78,0x78,0x6c,0x66,0x66,0xe6,0x00,0x00,0x00,0x00, -/*L*/ 0x00,0x00,0xf0,0x60,0x60,0x60,0x60,0x60,0x60,0x62,0x66,0xfe,0x00,0x00,0x00,0x00, -/*M*/ 0x00,0x00,0xc3,0xe7,0xff,0xff,0xdb,0xc3,0xc3,0xc3,0xc3,0xc3,0x00,0x00,0x00,0x00, -/*N*/ 0x00,0x00,0xc6,0xe6,0xf6,0xfe,0xde,0xce,0xc6,0xc6,0xc6,0xc6,0x00,0x00,0x00,0x00, -/*O*/ 0x00,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, -/*P*/ 0x00,0x00,0xfc,0x66,0x66,0x66,0x7c,0x60,0x60,0x60,0x60,0xf0,0x00,0x00,0x00,0x00, -/*Q*/ 0x00,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xd6,0xde,0x7c,0x0c,0x0e,0x00,0x00, -/*R*/ 0x00,0x00,0xfc,0x66,0x66,0x66,0x7c,0x6c,0x66,0x66,0x66,0xe6,0x00,0x00,0x00,0x00, -/*S*/ 0x00,0x00,0x7c,0xc6,0xc6,0x60,0x38,0x0c,0x06,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, -/*T*/ 0x00,0x00,0xff,0xdb,0x99,0x18,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00, -/*U*/ 0x00,0x00,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, -/*V*/ 0x00,0x00,0xc3,0xc3,0xc3,0xc3,0xc3,0xc3,0xc3,0x66,0x3c,0x18,0x00,0x00,0x00,0x00, -/*W*/ 0x00,0x00,0xc3,0xc3,0xc3,0xc3,0xc3,0xdb,0xdb,0xff,0x66,0x66,0x00,0x00,0x00,0x00, -/*X*/ 0x00,0x00,0xc3,0xc3,0x66,0x3c,0x18,0x18,0x3c,0x66,0xc3,0xc3,0x00,0x00,0x00,0x00, -/*Y*/ 0x00,0x00,0xc3,0xc3,0xc3,0x66,0x3c,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00, -/*Z*/ 0x00,0x00,0xff,0xc3,0x86,0x0c,0x18,0x30,0x60,0xc1,0xc3,0xff,0x00,0x00,0x00,0x00, -/*[*/ 0x00,0x00,0x3c,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x3c,0x00,0x00,0x00,0x00, -/*\*/ 0x00,0x00,0x00,0x80,0xc0,0xe0,0x70,0x38,0x1c,0x0e,0x06,0x02,0x00,0x00,0x00,0x00, -/*]*/ 0x00,0x00,0x3c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x3c,0x00,0x00,0x00,0x00, -/*^*/ 0x10,0x38,0x6c,0xc6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/*_*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00, -/*`*/ 0x30,0x30,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/*a*/ 0x00,0x00,0x00,0x00,0x00,0x78,0x0c,0x7c,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00, -/*b*/ 0x00,0x00,0xe0,0x60,0x60,0x78,0x6c,0x66,0x66,0x66,0x66,0x7c,0x00,0x00,0x00,0x00, -/*c*/ 0x00,0x00,0x00,0x00,0x00,0x7c,0xc6,0xc0,0xc0,0xc0,0xc6,0x7c,0x00,0x00,0x00,0x00, -/*d*/ 0x00,0x00,0x1c,0x0c,0x0c,0x3c,0x6c,0xcc,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00, -/*e*/ 0x00,0x00,0x00,0x00,0x00,0x7c,0xc6,0xfe,0xc0,0xc0,0xc6,0x7c,0x00,0x00,0x00,0x00, -/*f*/ 0x00,0x00,0x38,0x6c,0x64,0x60,0xf0,0x60,0x60,0x60,0x60,0xf0,0x00,0x00,0x00,0x00, -/*g*/ 0x00,0x00,0x00,0x00,0x00,0x76,0xcc,0xcc,0xcc,0xcc,0xcc,0x7c,0x0c,0xcc,0x78,0x00, -/*h*/ 0x00,0x00,0xe0,0x60,0x60,0x6c,0x76,0x66,0x66,0x66,0x66,0xe6,0x00,0x00,0x00,0x00, -/*i*/ 0x00,0x00,0x18,0x18,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00, -/*j*/ 0x00,0x00,0x06,0x06,0x00,0x0e,0x06,0x06,0x06,0x06,0x06,0x06,0x66,0x66,0x3c,0x00, -/*k*/ 0x00,0x00,0xe0,0x60,0x60,0x66,0x6c,0x78,0x78,0x6c,0x66,0xe6,0x00,0x00,0x00,0x00, -/*l*/ 0x00,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00, -/*m*/ 0x00,0x00,0x00,0x00,0x00,0xe6,0xff,0xdb,0xdb,0xdb,0xdb,0xdb,0x00,0x00,0x00,0x00, -/*n*/ 0x00,0x00,0x00,0x00,0x00,0xdc,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x00,0x00,0x00, -/*o*/ 0x00,0x00,0x00,0x00,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, -/*p*/ 0x00,0x00,0x00,0x00,0x00,0xdc,0x66,0x66,0x66,0x66,0x66,0x7c,0x60,0x60,0xf0,0x00, -/*q*/ 0x00,0x00,0x00,0x00,0x00,0x76,0xcc,0xcc,0xcc,0xcc,0xcc,0x7c,0x0c,0x0c,0x1e,0x00, -/*r*/ 0x00,0x00,0x00,0x00,0x00,0xdc,0x76,0x66,0x60,0x60,0x60,0xf0,0x00,0x00,0x00,0x00, -/*s*/ 0x00,0x00,0x00,0x00,0x00,0x7c,0xc6,0x60,0x38,0x0c,0xc6,0x7c,0x00,0x00,0x00,0x00, -/*t*/ 0x00,0x00,0x10,0x30,0x30,0xfc,0x30,0x30,0x30,0x30,0x36,0x1c,0x00,0x00,0x00,0x00, -/*u*/ 0x00,0x00,0x00,0x00,0x00,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00, -/*v*/ 0x00,0x00,0x00,0x00,0x00,0xc3,0xc3,0xc3,0xc3,0x66,0x3c,0x18,0x00,0x00,0x00,0x00, -/*w*/ 0x00,0x00,0x00,0x00,0x00,0xc3,0xc3,0xc3,0xdb,0xdb,0xff,0x66,0x00,0x00,0x00,0x00, -/*x*/ 0x00,0x00,0x00,0x00,0x00,0xc3,0x66,0x3c,0x18,0x3c,0x66,0xc3,0x00,0x00,0x00,0x00, -/*y*/ 0x00,0x00,0x00,0x00,0x00,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x7e,0x06,0x0c,0xf8,0x00, -/*z*/ 0x00,0x00,0x00,0x00,0x00,0xfe,0xcc,0x18,0x30,0x60,0xc6,0xfe,0x00,0x00,0x00,0x00, -/*{*/ 0x00,0x00,0x0e,0x18,0x18,0x18,0x70,0x18,0x18,0x18,0x18,0x0e,0x00,0x00,0x00,0x00, -/*|*/ 0x00,0x00,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00, -/*}*/ 0x00,0x00,0x70,0x18,0x18,0x18,0x0e,0x18,0x18,0x18,0x18,0x70,0x00,0x00,0x00,0x00, -/*~*/ 0x00,0x00,0x76,0xdc,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0x10,0x38,0x6c,0xc6,0xc6,0xc6,0xfe,0x00,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x3c,0x66,0xc2,0xc0,0xc0,0xc0,0xc2,0x66,0x3c,0x0c,0x06,0x7c,0x00,0x00, -/* */ 0x00,0x00,0xcc,0x00,0x00,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00, -/* */ 0x00,0x0c,0x18,0x30,0x00,0x7c,0xc6,0xfe,0xc0,0xc0,0xc6,0x7c,0x00,0x00,0x00,0x00, -/* */ 0x00,0x10,0x38,0x6c,0x00,0x78,0x0c,0x7c,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0xcc,0x00,0x00,0x78,0x0c,0x7c,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00, -/* */ 0x00,0x60,0x30,0x18,0x00,0x78,0x0c,0x7c,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00, -/* */ 0x00,0x38,0x6c,0x38,0x00,0x78,0x0c,0x7c,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0x3c,0x66,0x60,0x60,0x66,0x3c,0x0c,0x06,0x3c,0x00,0x00,0x00, -/* */ 0x00,0x10,0x38,0x6c,0x00,0x7c,0xc6,0xfe,0xc0,0xc0,0xc6,0x7c,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0xc6,0x00,0x00,0x7c,0xc6,0xfe,0xc0,0xc0,0xc6,0x7c,0x00,0x00,0x00,0x00, -/* */ 0x00,0x60,0x30,0x18,0x00,0x7c,0xc6,0xfe,0xc0,0xc0,0xc6,0x7c,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x66,0x00,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00, -/* */ 0x00,0x18,0x3c,0x66,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00, -/* */ 0x00,0x60,0x30,0x18,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00, -/* */ 0x00,0xc6,0x00,0x10,0x38,0x6c,0xc6,0xc6,0xfe,0xc6,0xc6,0xc6,0x00,0x00,0x00,0x00, -/* */ 0x38,0x6c,0x38,0x00,0x38,0x6c,0xc6,0xc6,0xfe,0xc6,0xc6,0xc6,0x00,0x00,0x00,0x00, -/* */ 0x18,0x30,0x60,0x00,0xfe,0x66,0x60,0x7c,0x60,0x60,0x66,0xfe,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0x00,0x6e,0x3b,0x1b,0x7e,0xd8,0xdc,0x77,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x3e,0x6c,0xcc,0xcc,0xfe,0xcc,0xcc,0xcc,0xcc,0xce,0x00,0x00,0x00,0x00, -/* */ 0x00,0x10,0x38,0x6c,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0xc6,0x00,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, -/* */ 0x00,0x60,0x30,0x18,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, -/* */ 0x00,0x30,0x78,0xcc,0x00,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00, -/* */ 0x00,0x60,0x30,0x18,0x00,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0xc6,0x00,0x00,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x7e,0x06,0x0c,0x78,0x00, -/* */ 0x00,0xc6,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, -/* */ 0x00,0xc6,0x00,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, -/* */ 0x00,0x18,0x18,0x7e,0xc3,0xc0,0xc0,0xc0,0xc3,0x7e,0x18,0x18,0x00,0x00,0x00,0x00, -/* */ 0x00,0x38,0x6c,0x64,0x60,0xf0,0x60,0x60,0x60,0x60,0xe6,0xfc,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0xc3,0x66,0x3c,0x18,0xff,0x18,0xff,0x18,0x18,0x18,0x00,0x00,0x00,0x00, -/* */ 0x00,0xfc,0x66,0x66,0x7c,0x62,0x66,0x6f,0x66,0x66,0x66,0xf3,0x00,0x00,0x00,0x00, -/* */ 0x00,0x0e,0x1b,0x18,0x18,0x18,0x7e,0x18,0x18,0x18,0x18,0x18,0xd8,0x70,0x00,0x00, -/* */ 0x00,0x18,0x30,0x60,0x00,0x78,0x0c,0x7c,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00, -/* */ 0x00,0x0c,0x18,0x30,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00, -/* */ 0x00,0x18,0x30,0x60,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, -/* */ 0x00,0x18,0x30,0x60,0x00,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x76,0xdc,0x00,0xdc,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x00,0x00,0x00, -/* */ 0x76,0xdc,0x00,0xc6,0xe6,0xf6,0xfe,0xde,0xce,0xc6,0xc6,0xc6,0x00,0x00,0x00,0x00, -/* */ 0x00,0x3c,0x6c,0x6c,0x3e,0x00,0x7e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/* */ 0x00,0x38,0x6c,0x6c,0x38,0x00,0x7c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x30,0x30,0x00,0x30,0x30,0x60,0xc0,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0xfe,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0xfe,0x06,0x06,0x06,0x06,0x00,0x00,0x00,0x00,0x00, -/* */ 0x00,0xc0,0xc0,0xc2,0xc6,0xcc,0x18,0x30,0x60,0xce,0x9b,0x06,0x0c,0x1f,0x00,0x00, -/* */ 0x00,0xc0,0xc0,0xc2,0xc6,0xcc,0x18,0x30,0x66,0xce,0x96,0x3e,0x06,0x06,0x00,0x00, -/* */ 0x00,0x00,0x18,0x18,0x00,0x18,0x18,0x18,0x3c,0x3c,0x3c,0x18,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0x00,0x36,0x6c,0xd8,0x6c,0x36,0x00,0x00,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0x00,0xd8,0x6c,0x36,0x6c,0xd8,0x00,0x00,0x00,0x00,0x00,0x00, -/* */ 0x11,0x44,0x11,0x44,0x11,0x44,0x11,0x44,0x11,0x44,0x11,0x44,0x11,0x44,0x11,0x44, -/* */ 0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa, -/* */ 0xdd,0x77,0xdd,0x77,0xdd,0x77,0xdd,0x77,0xdd,0x77,0xdd,0x77,0xdd,0x77,0xdd,0x77, -/* */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, -/* */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xf8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, -/* */ 0x18,0x18,0x18,0x18,0x18,0xf8,0x18,0xf8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, -/* */ 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xf6,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, -/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfe,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, -/* */ 0x00,0x00,0x00,0x00,0x00,0xf8,0x18,0xf8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, -/* */ 0x36,0x36,0x36,0x36,0x36,0xf6,0x06,0xf6,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, -/* */ 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, -/* */ 0x00,0x00,0x00,0x00,0x00,0xfe,0x06,0xf6,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, -/* */ 0x36,0x36,0x36,0x36,0x36,0xf6,0x06,0xfe,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/* */ 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xfe,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/* */ 0x18,0x18,0x18,0x18,0x18,0xf8,0x18,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, -/* */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/* */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, -/* */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1f,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, -/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/* */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xff,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, -/* */ 0x18,0x18,0x18,0x18,0x18,0x1f,0x18,0x1f,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, -/* */ 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x37,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, -/* */ 0x36,0x36,0x36,0x36,0x36,0x37,0x30,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0x00,0x3f,0x30,0x37,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, -/* */ 0x36,0x36,0x36,0x36,0x36,0xf7,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xf7,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, -/* */ 0x36,0x36,0x36,0x36,0x36,0x37,0x30,0x37,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, -/* */ 0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/* */ 0x36,0x36,0x36,0x36,0x36,0xf7,0x00,0xf7,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, -/* */ 0x18,0x18,0x18,0x18,0x18,0xff,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/* */ 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, -/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, -/* */ 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/* */ 0x18,0x18,0x18,0x18,0x18,0x1f,0x18,0x1f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0x00,0x1f,0x18,0x1f,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, -/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3f,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, -/* */ 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xff,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, -/* */ 0x18,0x18,0x18,0x18,0x18,0xff,0x18,0xff,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, -/* */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1f,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, -/* */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, -/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, -/* */ 0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0, -/* */ 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, -/* */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0x00,0x76,0xdc,0xd8,0xd8,0xd8,0xdc,0x76,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x78,0xcc,0xcc,0xcc,0xd8,0xcc,0xc6,0xc6,0xc6,0xcc,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0xfe,0xc6,0xc6,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0xfe,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0xfe,0xc6,0x60,0x30,0x18,0x30,0x60,0xc6,0xfe,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0x00,0x7e,0xd8,0xd8,0xd8,0xd8,0xd8,0x70,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0x66,0x66,0x66,0x66,0x66,0x7c,0x60,0x60,0xc0,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0x76,0xdc,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x7e,0x18,0x3c,0x66,0x66,0x66,0x3c,0x18,0x7e,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x38,0x6c,0xc6,0xc6,0xfe,0xc6,0xc6,0x6c,0x38,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x38,0x6c,0xc6,0xc6,0xc6,0x6c,0x6c,0x6c,0x6c,0xee,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x1e,0x30,0x18,0x0c,0x3e,0x66,0x66,0x66,0x66,0x3c,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0x00,0x7e,0xdb,0xdb,0xdb,0x7e,0x00,0x00,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x03,0x06,0x7e,0xdb,0xdb,0xf3,0x7e,0x60,0xc0,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x1c,0x30,0x60,0x60,0x7c,0x60,0x60,0x60,0x30,0x1c,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0xfe,0x00,0x00,0xfe,0x00,0x00,0xfe,0x00,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0x18,0x18,0x7e,0x18,0x18,0x00,0x00,0xff,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x30,0x18,0x0c,0x06,0x0c,0x18,0x30,0x00,0x7e,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x0c,0x18,0x30,0x60,0x30,0x18,0x0c,0x00,0x7e,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x0e,0x1b,0x1b,0x1b,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, -/* */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xd8,0xd8,0xd8,0x70,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x7e,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0x00,0x76,0xdc,0x00,0x76,0xdc,0x00,0x00,0x00,0x00,0x00,0x00, -/* */ 0x00,0x38,0x6c,0x6c,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/* */ 0x00,0x0f,0x0c,0x0c,0x0c,0x0c,0x0c,0xec,0x6c,0x6c,0x3c,0x1c,0x00,0x00,0x00,0x00, -/* */ 0x00,0xd8,0x6c,0x6c,0x6c,0x6c,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/* */ 0x00,0x70,0xd8,0x30,0x60,0xc8,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0x7c,0x7c,0x7c,0x7c,0x7c,0x7c,0x7c,0x00,0x00,0x00,0x00,0x00, -/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -}; - -const struct font_desc font_sun_8x16 = { - .idx = SUN8x16_IDX, - .name = "SUN8x16", - .width = 8, - .height = 16, - .data = fontdata_sun8x16, -#ifdef __sparc__ - .pref = 10, -#else - .pref = -1, -#endif -}; diff --git a/drivers/video/console/fonts.c b/drivers/video/console/fonts.c deleted file mode 100644 index d0c03fd70871..000000000000 --- a/drivers/video/console/fonts.c +++ /dev/null @@ -1,153 +0,0 @@ -/* - * linux/drivers/video/fonts.c -- `Soft' font definitions - * - * Created 1995 by Geert Uytterhoeven - * Rewritten 1998 by Martin Mares - * - * 2001 - Documented with DocBook - * - Brad Douglas - * - * 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 -#include -#include -#if defined(__mc68000__) -#include -#endif -#include - -#define NO_FONTS - -static const struct font_desc *fonts[] = { -#ifdef CONFIG_FONT_8x8 -#undef NO_FONTS - &font_vga_8x8, -#endif -#ifdef CONFIG_FONT_8x16 -#undef NO_FONTS - &font_vga_8x16, -#endif -#ifdef CONFIG_FONT_6x11 -#undef NO_FONTS - &font_vga_6x11, -#endif -#ifdef CONFIG_FONT_7x14 -#undef NO_FONTS - &font_7x14, -#endif -#ifdef CONFIG_FONT_SUN8x16 -#undef NO_FONTS - &font_sun_8x16, -#endif -#ifdef CONFIG_FONT_SUN12x22 -#undef NO_FONTS - &font_sun_12x22, -#endif -#ifdef CONFIG_FONT_10x18 -#undef NO_FONTS - &font_10x18, -#endif -#ifdef CONFIG_FONT_ACORN_8x8 -#undef NO_FONTS - &font_acorn_8x8, -#endif -#ifdef CONFIG_FONT_PEARL_8x8 -#undef NO_FONTS - &font_pearl_8x8, -#endif -#ifdef CONFIG_FONT_MINI_4x6 -#undef NO_FONTS - &font_mini_4x6, -#endif -}; - -#define num_fonts ARRAY_SIZE(fonts) - -#ifdef NO_FONTS -#error No fonts configured. -#endif - - -/** - * find_font - find a font - * @name: string name of a font - * - * Find a specified font with string name @name. - * - * Returns %NULL if no font found, or a pointer to the - * specified font. - * - */ - -const struct font_desc *find_font(const char *name) -{ - unsigned int i; - - for (i = 0; i < num_fonts; i++) - if (!strcmp(fonts[i]->name, name)) - return fonts[i]; - return NULL; -} - - -/** - * get_default_font - get default font - * @xres: screen size of X - * @yres: screen size of Y - * @font_w: bit array of supported widths (1 - 32) - * @font_h: bit array of supported heights (1 - 32) - * - * Get the default font for a specified screen size. - * Dimensions are in pixels. - * - * Returns %NULL if no font is found, or a pointer to the - * chosen font. - * - */ - -const struct font_desc *get_default_font(int xres, int yres, u32 font_w, - u32 font_h) -{ - int i, c, cc; - const struct font_desc *f, *g; - - g = NULL; - cc = -10000; - for(i=0; ipref; -#if defined(__mc68000__) -#ifdef CONFIG_FONT_PEARL_8x8 - if (MACH_IS_AMIGA && f->idx == PEARL8x8_IDX) - c = 100; -#endif -#ifdef CONFIG_FONT_6x11 - if (MACH_IS_MAC && xres < 640 && f->idx == VGA6x11_IDX) - c = 100; -#endif -#endif - if ((yres < 400) == (f->height <= 8)) - c += 1000; - - if ((font_w & (1 << (f->width - 1))) && - (font_h & (1 << (f->height - 1)))) - c += 1000; - - if (c > cc) { - cc = c; - g = f; - } - } - return g; -} - -EXPORT_SYMBOL(find_font); -EXPORT_SYMBOL(get_default_font); - -MODULE_AUTHOR("James Simmons "); -MODULE_DESCRIPTION("Console Fonts"); -MODULE_LICENSE("GPL"); diff --git a/lib/Kconfig b/lib/Kconfig index fe01d418b09a..0fe08ea3443d 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -407,4 +407,6 @@ config OID_REGISTRY config UCS2_STRING tristate +source "lib/fonts/Kconfig" + endmenu diff --git a/lib/Makefile b/lib/Makefile index c55a037a354e..9a51526aa36a 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -151,6 +151,8 @@ interval_tree_test-objs := interval_tree_test_main.o interval_tree.o obj-$(CONFIG_ASN1) += asn1_decoder.o +obj-$(CONFIG_FONT_SUPPORT) += fonts/ + hostprogs-y := gen_crc32table clean-files := crc32table.h diff --git a/lib/fonts/Kconfig b/lib/fonts/Kconfig new file mode 100644 index 000000000000..34fd931b54b5 --- /dev/null +++ b/lib/fonts/Kconfig @@ -0,0 +1,117 @@ +# +# Font configuration +# + +config FONT_SUPPORT + tristate + +if FONT_SUPPORT + +config FONTS + bool "Select compiled-in fonts" + depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE + help + Say Y here if you would like to use fonts other than the default + your frame buffer console usually use. + + Note that the answer to this question won't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about foreign fonts. + + If unsure, say N (the default choices are safe). + +config FONT_8x8 + bool "VGA 8x8 font" if FONTS + depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE + default y if !SPARC && !FONTS + help + This is the "high resolution" font for the VGA frame buffer (the one + provided by the text console 80x50 (and higher) modes). + + Note that this is a poor quality font. The VGA 8x16 font is quite a + lot more readable. + + Given the resolution provided by the frame buffer device, answer N + here is safe. + +config FONT_8x16 + bool "VGA 8x16 font" if FONTS + default y if !SPARC && !FONTS + help + This is the "high resolution" font for the VGA frame buffer (the one + provided by the VGA text console 80x25 mode. + + If unsure, say Y. + +config FONT_6x11 + bool "Mac console 6x11 font (not supported by all drivers)" if FONTS + depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE + default y if !SPARC && !FONTS && MAC + help + Small console font with Macintosh-style high-half glyphs. Some Mac + framebuffer drivers don't support this one at all. + +config FONT_7x14 + bool "console 7x14 font (not supported by all drivers)" if FONTS + depends on FRAMEBUFFER_CONSOLE + help + Console font with characters just a bit smaller than the default. + If the standard 8x16 font is a little too big for you, say Y. + Otherwise, say N. + +config FONT_PEARL_8x8 + bool "Pearl (old m68k) console 8x8 font" if FONTS + depends on FRAMEBUFFER_CONSOLE + default y if !SPARC && !FONTS && AMIGA + help + Small console font with PC-style control-character and high-half + glyphs. + +config FONT_ACORN_8x8 + bool "Acorn console 8x8 font" if FONTS + depends on FRAMEBUFFER_CONSOLE + default y if !SPARC && !FONTS && ARM && ARCH_ACORN + help + Small console font with PC-style control characters and high-half + glyphs. + +config FONT_MINI_4x6 + bool "Mini 4x6 font" + depends on !SPARC && FONTS + +config FONT_SUN8x16 + bool "Sparc console 8x16 font" + depends on FRAMEBUFFER_CONSOLE && (!SPARC && FONTS || SPARC) + help + This is the high resolution console font for Sun machines. Say Y. + +config FONT_SUN12x22 + bool "Sparc console 12x22 font (not supported by all drivers)" + depends on FRAMEBUFFER_CONSOLE && (!SPARC && FONTS || SPARC) + help + This is the high resolution console font for Sun machines with very + big letters (like the letters used in the SPARC PROM). If the + standard font is unreadable for you, say Y, otherwise say N. + +config FONT_10x18 + bool "console 10x18 font (not supported by all drivers)" if FONTS + depends on FRAMEBUFFER_CONSOLE + help + This is a high resolution console font for machines with very + big letters. It fits between the sun 12x22 and the normal 8x16 font. + If other fonts are too big or too small for you, say Y, otherwise say N. + +config FONT_AUTOSELECT + def_bool y + depends on !FONT_8x8 + depends on !FONT_6x11 + depends on !FONT_7x14 + depends on !FONT_PEARL_8x8 + depends on !FONT_ACORN_8x8 + depends on !FONT_MINI_4x6 + depends on !FONT_SUN8x16 + depends on !FONT_SUN12x22 + depends on !FONT_10x18 + select FONT_8x16 + +endif # FONT_SUPPORT diff --git a/lib/fonts/Makefile b/lib/fonts/Makefile new file mode 100644 index 000000000000..2761560f3f15 --- /dev/null +++ b/lib/fonts/Makefile @@ -0,0 +1,18 @@ +# Font handling + +font-objs := fonts.o + +font-objs-$(CONFIG_FONT_SUN8x16) += font_sun8x16.o +font-objs-$(CONFIG_FONT_SUN12x22) += font_sun12x22.o +font-objs-$(CONFIG_FONT_8x8) += font_8x8.o +font-objs-$(CONFIG_FONT_8x16) += font_8x16.o +font-objs-$(CONFIG_FONT_6x11) += font_6x11.o +font-objs-$(CONFIG_FONT_7x14) += font_7x14.o +font-objs-$(CONFIG_FONT_10x18) += font_10x18.o +font-objs-$(CONFIG_FONT_PEARL_8x8) += font_pearl_8x8.o +font-objs-$(CONFIG_FONT_ACORN_8x8) += font_acorn_8x8.o +font-objs-$(CONFIG_FONT_MINI_4x6) += font_mini_4x6.o + +font-objs += $(font-objs-y) + +obj-$(CONFIG_FONT_SUPPORT) += font.o diff --git a/lib/fonts/font_10x18.c b/lib/fonts/font_10x18.c new file mode 100644 index 000000000000..6be72bb218ee --- /dev/null +++ b/lib/fonts/font_10x18.c @@ -0,0 +1,5146 @@ +/******************************** + * adapted from font_sun12x22.c * + * by Jurriaan Kalkman 06-2005 * + ********************************/ + +#include + +#define FONTDATAMAX 9216 + +static const unsigned char fontdata_10x18[FONTDATAMAX] = { + + /* 0 0x00 '^@' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 1 0x01 '^A' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x3f, 0x80, /* 0011111110 */ + 0x40, 0x40, /* 0100000001 */ + 0x5b, 0x40, /* 0101101101 */ + 0x40, 0x40, /* 0100000001 */ + 0x44, 0x40, /* 0100010001 */ + 0x44, 0x40, /* 0100010001 */ + 0x51, 0x40, /* 0101000101 */ + 0x4e, 0x40, /* 0100111001 */ + 0x40, 0x40, /* 0100000001 */ + 0x3f, 0x80, /* 0011111110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 2 0x02 '^B' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x3f, 0x80, /* 0011111110 */ + 0x7f, 0xc0, /* 0111111111 */ + 0x64, 0xc0, /* 0110010011 */ + 0x7f, 0xc0, /* 0111111111 */ + 0x7b, 0xc0, /* 0111101111 */ + 0x7b, 0xc0, /* 0111101111 */ + 0x6e, 0xc0, /* 0110111011 */ + 0x71, 0xc0, /* 0111000111 */ + 0x7f, 0xc0, /* 0111111111 */ + 0x3f, 0x80, /* 0011111110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 3 0x03 '^C' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x11, 0x00, /* 0001000100 */ + 0x3b, 0x80, /* 0011101110 */ + 0x7f, 0xc0, /* 0111111111 */ + 0x3f, 0x80, /* 0011111110 */ + 0x3f, 0x80, /* 0011111110 */ + 0x1f, 0x00, /* 0001111100 */ + 0x1f, 0x00, /* 0001111100 */ + 0x0e, 0x00, /* 0000111000 */ + 0x0e, 0x00, /* 0000111000 */ + 0x04, 0x00, /* 0000010000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 4 0x04 '^D' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x04, 0x00, /* 0000010000 */ + 0x0e, 0x00, /* 0000111000 */ + 0x0e, 0x00, /* 0000111000 */ + 0x1f, 0x00, /* 0001111100 */ + 0x1f, 0x00, /* 0001111100 */ + 0x3f, 0x80, /* 0011111110 */ + 0x1f, 0x00, /* 0001111100 */ + 0x1f, 0x00, /* 0001111100 */ + 0x0e, 0x00, /* 0000111000 */ + 0x0e, 0x00, /* 0000111000 */ + 0x04, 0x00, /* 0000010000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 5 0x05 '^E' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x1e, 0x00, /* 0001111000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x31, 0x80, /* 0011000110 */ + 0x7b, 0xc0, /* 0111101111 */ + 0x35, 0x80, /* 0011010110 */ + 0x04, 0x00, /* 0000010000 */ + 0x0e, 0x00, /* 0000111000 */ + 0x1f, 0x00, /* 0001111100 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 6 0x06 '^F' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x04, 0x00, /* 0000010000 */ + 0x0e, 0x00, /* 0000111000 */ + 0x1f, 0x00, /* 0001111100 */ + 0x1f, 0x00, /* 0001111100 */ + 0x3f, 0x80, /* 0011111110 */ + 0x7f, 0xc0, /* 0111111111 */ + 0x7f, 0xc0, /* 0111111111 */ + 0x35, 0x80, /* 0011010110 */ + 0x04, 0x00, /* 0000010000 */ + 0x0e, 0x00, /* 0000111000 */ + 0x1f, 0x00, /* 0001111100 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 7 0x07 '^G' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x1e, 0x00, /* 0001111000 */ + 0x1e, 0x00, /* 0001111000 */ + 0x3f, 0x00, /* 0011111100 */ + 0x3f, 0x00, /* 0011111100 */ + 0x1e, 0x00, /* 0001111000 */ + 0x1e, 0x00, /* 0001111000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 8 0x08 '^H' */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xf3, 0xc0, /* 1111001111 */ + 0xe1, 0xc0, /* 1110000111 */ + 0xe1, 0xc0, /* 1110000111 */ + 0xc0, 0xc0, /* 1100000011 */ + 0xc0, 0xc0, /* 1100000011 */ + 0xe1, 0xc0, /* 1110000111 */ + 0xe1, 0xc0, /* 1110000111 */ + 0xf3, 0xc0, /* 1111001111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + + /* 9 0x09 '^I' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x12, 0x00, /* 0001001000 */ + 0x12, 0x00, /* 0001001000 */ + 0x21, 0x00, /* 0010000100 */ + 0x21, 0x00, /* 0010000100 */ + 0x12, 0x00, /* 0001001000 */ + 0x12, 0x00, /* 0001001000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 10 0x0a '^J' */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xf3, 0xc0, /* 1111001111 */ + 0xed, 0xc0, /* 1110110111 */ + 0xed, 0xc0, /* 1110110111 */ + 0xde, 0xc0, /* 1101111011 */ + 0xde, 0xc0, /* 1101111011 */ + 0xed, 0xc0, /* 1110110111 */ + 0xed, 0xc0, /* 1110110111 */ + 0xf3, 0xc0, /* 1111001111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + + /* 11 0x0b '^K' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x0f, 0xc0, /* 0000111111 */ + 0x0f, 0xc0, /* 0000111111 */ + 0x03, 0xc0, /* 0000001111 */ + 0x06, 0xc0, /* 0000011011 */ + 0x0c, 0xc0, /* 0000110011 */ + 0x3c, 0x00, /* 0011110000 */ + 0x66, 0x00, /* 0110011000 */ + 0xc3, 0x00, /* 1100001100 */ + 0xc3, 0x00, /* 1100001100 */ + 0xc3, 0x00, /* 1100001100 */ + 0x66, 0x00, /* 0110011000 */ + 0x3c, 0x00, /* 0011110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 12 0x0c '^L' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x1e, 0x00, /* 0001111000 */ + 0x33, 0x00, /* 0011001100 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x33, 0x00, /* 0011001100 */ + 0x1e, 0x00, /* 0001111000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x3f, 0x00, /* 0011111100 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 13 0x0d '^M' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x0f, 0x80, /* 0000111110 */ + 0x08, 0x80, /* 0000100010 */ + 0x0f, 0x80, /* 0000111110 */ + 0x08, 0x00, /* 0000100000 */ + 0x08, 0x00, /* 0000100000 */ + 0x08, 0x00, /* 0000100000 */ + 0x08, 0x00, /* 0000100000 */ + 0x08, 0x00, /* 0000100000 */ + 0x08, 0x00, /* 0000100000 */ + 0x38, 0x00, /* 0011100000 */ + 0x78, 0x00, /* 0111100000 */ + 0x30, 0x00, /* 0011000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 14 0x0e '^N' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x1f, 0x80, /* 0001111110 */ + 0x10, 0x80, /* 0001000010 */ + 0x1f, 0x80, /* 0001111110 */ + 0x10, 0x80, /* 0001000010 */ + 0x10, 0x80, /* 0001000010 */ + 0x10, 0x80, /* 0001000010 */ + 0x10, 0x80, /* 0001000010 */ + 0x13, 0x80, /* 0001001110 */ + 0x17, 0x80, /* 0001011110 */ + 0x73, 0x00, /* 0111001100 */ + 0xf0, 0x00, /* 1111000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 15 0x0f '^O' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x04, 0x00, /* 0000010000 */ + 0x24, 0x80, /* 0010010010 */ + 0x15, 0x00, /* 0001010100 */ + 0x55, 0x40, /* 0101010101 */ + 0x3f, 0x80, /* 0011111110 */ + 0x0e, 0x00, /* 0000111000 */ + 0x00, 0x00, /* 0000000000 */ + 0x0e, 0x00, /* 0000111000 */ + 0x3f, 0x80, /* 0011111110 */ + 0x55, 0x40, /* 0101010101 */ + 0x15, 0x00, /* 0001010100 */ + 0x24, 0x80, /* 0010010010 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 16 0x10 '^P' */ + 0x00, 0x80, /* 0000000010 */ + 0x01, 0x80, /* 0000000110 */ + 0x03, 0x80, /* 0000001110 */ + 0x07, 0x80, /* 0000011110 */ + 0x0f, 0x80, /* 0000111110 */ + 0x1f, 0x80, /* 0001111110 */ + 0x3f, 0x80, /* 0011111110 */ + 0x7f, 0x80, /* 0111111110 */ + 0xff, 0x80, /* 1111111110 */ + 0x7f, 0x80, /* 0111111110 */ + 0x3f, 0x80, /* 0011111110 */ + 0x1f, 0x80, /* 0001111110 */ + 0x0f, 0x80, /* 0000111110 */ + 0x07, 0x80, /* 0000011110 */ + 0x03, 0x80, /* 0000001110 */ + 0x01, 0x80, /* 0000000110 */ + 0x00, 0x80, /* 0000000010 */ + 0x00, 0x00, /* 0000000000 */ + + /* 17 0x11 '^Q' */ + 0x40, 0x00, /* 0100000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x70, 0x00, /* 0111000000 */ + 0x78, 0x00, /* 0111100000 */ + 0x7c, 0x00, /* 0111110000 */ + 0x7e, 0x00, /* 0111111000 */ + 0x7f, 0x00, /* 0111111100 */ + 0x7f, 0x80, /* 0111111110 */ + 0x7f, 0xc0, /* 0111111111 */ + 0x7f, 0x80, /* 0111111110 */ + 0x7f, 0x00, /* 0111111100 */ + 0x7e, 0x00, /* 0111111000 */ + 0x7c, 0x00, /* 0111110000 */ + 0x78, 0x00, /* 0111100000 */ + 0x70, 0x00, /* 0111000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x40, 0x00, /* 0100000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 18 0x12 '^R' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x1e, 0x00, /* 0001111000 */ + 0x3f, 0x00, /* 0011111100 */ + 0x7f, 0x80, /* 0111111110 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x7f, 0x80, /* 0111111110 */ + 0x3f, 0x00, /* 0011111100 */ + 0x1e, 0x00, /* 0001111000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 19 0x13 '^S' */ + 0x00, 0x00, /* 0000000000 */ + 0x33, 0x00, /* 0011001100 */ + 0x33, 0x00, /* 0011001100 */ + 0x33, 0x00, /* 0011001100 */ + 0x33, 0x00, /* 0011001100 */ + 0x33, 0x00, /* 0011001100 */ + 0x33, 0x00, /* 0011001100 */ + 0x33, 0x00, /* 0011001100 */ + 0x33, 0x00, /* 0011001100 */ + 0x33, 0x00, /* 0011001100 */ + 0x33, 0x00, /* 0011001100 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x33, 0x00, /* 0011001100 */ + 0x33, 0x00, /* 0011001100 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 20 0x14 '^T' */ + 0x00, 0x00, /* 0000000000 */ + 0x1f, 0xc0, /* 0001111111 */ + 0x39, 0x80, /* 0011100110 */ + 0x79, 0x80, /* 0111100110 */ + 0x79, 0x80, /* 0111100110 */ + 0x79, 0x80, /* 0111100110 */ + 0x39, 0x80, /* 0011100110 */ + 0x19, 0x80, /* 0001100110 */ + 0x19, 0x80, /* 0001100110 */ + 0x19, 0x80, /* 0001100110 */ + 0x19, 0x80, /* 0001100110 */ + 0x19, 0x80, /* 0001100110 */ + 0x19, 0x80, /* 0001100110 */ + 0x39, 0xc0, /* 0011100111 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 21 0x15 '^U' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x3e, 0x00, /* 0011111000 */ + 0x63, 0x00, /* 0110001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x3e, 0x00, /* 0011111000 */ + 0x63, 0x00, /* 0110001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x3e, 0x00, /* 0011111000 */ + 0x03, 0x00, /* 0000001100 */ + 0x03, 0x00, /* 0000001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x3e, 0x00, /* 0011111000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 22 0x16 '^V' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x7f, 0x80, /* 0111111110 */ + 0x7f, 0x80, /* 0111111110 */ + 0x7f, 0x80, /* 0111111110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 23 0x17 '^W' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x1e, 0x00, /* 0001111000 */ + 0x3f, 0x00, /* 0011111100 */ + 0x7f, 0x80, /* 0111111110 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x7f, 0x80, /* 0111111110 */ + 0x3f, 0x00, /* 0011111100 */ + 0x1e, 0x00, /* 0001111000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x7f, 0x80, /* 0111111110 */ + 0x7f, 0x80, /* 0111111110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 24 0x18 '^X' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x1e, 0x00, /* 0001111000 */ + 0x3f, 0x00, /* 0011111100 */ + 0x7f, 0x80, /* 0111111110 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 25 0x19 '^Y' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x7f, 0x80, /* 0111111110 */ + 0x3f, 0x00, /* 0011111100 */ + 0x1e, 0x00, /* 0001111000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 26 0x1a '^Z' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x04, 0x00, /* 0000010000 */ + 0x06, 0x00, /* 0000011000 */ + 0x07, 0x00, /* 0000011100 */ + 0x7f, 0x80, /* 0111111110 */ + 0x7f, 0x80, /* 0111111110 */ + 0x07, 0x00, /* 0000011100 */ + 0x06, 0x00, /* 0000011000 */ + 0x04, 0x00, /* 0000010000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 27 0x1b '^[' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x08, 0x00, /* 0000100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x38, 0x00, /* 0011100000 */ + 0x7f, 0x80, /* 0111111110 */ + 0x7f, 0x80, /* 0111111110 */ + 0x38, 0x00, /* 0011100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x08, 0x00, /* 0000100000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 28 0x1c '^\' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x7f, 0x80, /* 0111111110 */ + 0x7f, 0x80, /* 0111111110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 29 0x1d '^]' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x12, 0x00, /* 0001001000 */ + 0x33, 0x00, /* 0011001100 */ + 0x73, 0x80, /* 0111001110 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0x73, 0x80, /* 0111001110 */ + 0x33, 0x00, /* 0011001100 */ + 0x12, 0x00, /* 0001001000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 30 0x1e '^^' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x04, 0x00, /* 0000010000 */ + 0x04, 0x00, /* 0000010000 */ + 0x0e, 0x00, /* 0000111000 */ + 0x0e, 0x00, /* 0000111000 */ + 0x1f, 0x00, /* 0001111100 */ + 0x1f, 0x00, /* 0001111100 */ + 0x3f, 0x80, /* 0011111110 */ + 0x3f, 0x80, /* 0011111110 */ + 0x7f, 0xc0, /* 0111111111 */ + 0x7f, 0xc0, /* 0111111111 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 31 0x1f '^_' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x7f, 0xc0, /* 0111111111 */ + 0x7f, 0xc0, /* 0111111111 */ + 0x3f, 0x80, /* 0011111110 */ + 0x3f, 0x80, /* 0011111110 */ + 0x1f, 0x00, /* 0001111100 */ + 0x1f, 0x00, /* 0001111100 */ + 0x0e, 0x00, /* 0000111000 */ + 0x0e, 0x00, /* 0000111000 */ + 0x04, 0x00, /* 0000010000 */ + 0x04, 0x00, /* 0000010000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 32 0x20 ' ' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 33 0x21 '!' */ + 0x00, 0x00, /* 0000000000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 34 0x22 '"' */ + 0x00, 0x00, /* 0000000000 */ + 0x63, 0x00, /* 0110001100 */ + 0xf7, 0x80, /* 1111011110 */ + 0xf7, 0x80, /* 1111011110 */ + 0x31, 0x80, /* 0011000110 */ + 0x31, 0x80, /* 0011000110 */ + 0x63, 0x00, /* 0110001100 */ + 0x42, 0x00, /* 0100001000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 35 0x23 '#' */ + 0x00, 0x00, /* 0000000000 */ + 0x33, 0x00, /* 0011001100 */ + 0x33, 0x00, /* 0011001100 */ + 0x7f, 0x80, /* 0111111110 */ + 0x7f, 0x80, /* 0111111110 */ + 0x33, 0x00, /* 0011001100 */ + 0x33, 0x00, /* 0011001100 */ + 0x33, 0x00, /* 0011001100 */ + 0x33, 0x00, /* 0011001100 */ + 0x7f, 0x80, /* 0111111110 */ + 0x7f, 0x80, /* 0111111110 */ + 0x33, 0x00, /* 0011001100 */ + 0x33, 0x00, /* 0011001100 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 36 0x24 '$' */ + 0x00, 0x00, /* 0000000000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x3e, 0x00, /* 0011111000 */ + 0x3f, 0x00, /* 0011111100 */ + 0x6f, 0x80, /* 0110111110 */ + 0x6d, 0x80, /* 0110110110 */ + 0x6c, 0x80, /* 0110110010 */ + 0x3c, 0x00, /* 0011110000 */ + 0x0f, 0x00, /* 0000111100 */ + 0x0d, 0x80, /* 0000110110 */ + 0x4d, 0x80, /* 0100110110 */ + 0x6d, 0x80, /* 0110110110 */ + 0x7f, 0x00, /* 0111111100 */ + 0x3e, 0x00, /* 0011111000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 37 0x25 '%' */ + 0x00, 0x00, /* 0000000000 */ + 0x31, 0x80, /* 0011000110 */ + 0x7b, 0x00, /* 0111101100 */ + 0x7b, 0x00, /* 0111101100 */ + 0x36, 0x00, /* 0011011000 */ + 0x06, 0x00, /* 0000011000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x18, 0x00, /* 0001100000 */ + 0x1b, 0x00, /* 0001101100 */ + 0x37, 0x80, /* 0011011110 */ + 0x37, 0x80, /* 0011011110 */ + 0x63, 0x00, /* 0110001100 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 38 0x26 '&' */ + 0x00, 0x00, /* 0000000000 */ + 0x07, 0x00, /* 0000011100 */ + 0x0f, 0x80, /* 0000111110 */ + 0x19, 0x80, /* 0001100110 */ + 0x19, 0x80, /* 0001100110 */ + 0x0f, 0x80, /* 0000111110 */ + 0x1e, 0x00, /* 0001111000 */ + 0x3e, 0x00, /* 0011111000 */ + 0x76, 0x00, /* 0111011000 */ + 0x66, 0x40, /* 0110011001 */ + 0x63, 0xc0, /* 0110001111 */ + 0x63, 0x80, /* 0110001110 */ + 0x63, 0x00, /* 0110001100 */ + 0x3f, 0x80, /* 0011111110 */ + 0x1c, 0xc0, /* 0001110011 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 39 0x27 ''' */ + 0x00, 0x00, /* 0000000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x78, 0x00, /* 0111100000 */ + 0x78, 0x00, /* 0111100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x30, 0x00, /* 0011000000 */ + 0x20, 0x00, /* 0010000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 40 0x28 '(' */ + 0x00, 0x00, /* 0000000000 */ + 0x03, 0x00, /* 0000001100 */ + 0x06, 0x00, /* 0000011000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x18, 0x00, /* 0001100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x06, 0x00, /* 0000011000 */ + 0x03, 0x00, /* 0000001100 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 41 0x29 ')' */ + 0x00, 0x00, /* 0000000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x18, 0x00, /* 0001100000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x06, 0x00, /* 0000011000 */ + 0x06, 0x00, /* 0000011000 */ + 0x06, 0x00, /* 0000011000 */ + 0x06, 0x00, /* 0000011000 */ + 0x06, 0x00, /* 0000011000 */ + 0x06, 0x00, /* 0000011000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x18, 0x00, /* 0001100000 */ + 0x30, 0x00, /* 0011000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 42 0x2a '*' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x4c, 0x80, /* 0100110010 */ + 0x6d, 0x80, /* 0110110110 */ + 0x3f, 0x00, /* 0011111100 */ + 0x7f, 0x80, /* 0111111110 */ + 0x3f, 0x00, /* 0011111100 */ + 0x6d, 0x80, /* 0110110110 */ + 0x4c, 0x80, /* 0100110010 */ + 0x0c, 0x00, /* 0000110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 43 0x2b '+' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x7f, 0x80, /* 0111111110 */ + 0x7f, 0x80, /* 0111111110 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 44 0x2c ',' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x78, 0x00, /* 0111100000 */ + 0x78, 0x00, /* 0111100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x30, 0x00, /* 0011000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x40, 0x00, /* 0100000000 */ + + /* 45 0x2d '-' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x7f, 0x80, /* 0111111110 */ + 0x7f, 0x80, /* 0111111110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 46 0x2e '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x18, 0x00, /* 0001100000 */ + 0x3c, 0x00, /* 0011110000 */ + 0x3c, 0x00, /* 0011110000 */ + 0x18, 0x00, /* 0001100000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 47 0x2f '/' */ + 0x00, 0x00, /* 0000000000 */ + 0x01, 0x80, /* 0000000110 */ + 0x01, 0x80, /* 0000000110 */ + 0x03, 0x00, /* 0000001100 */ + 0x03, 0x00, /* 0000001100 */ + 0x06, 0x00, /* 0000011000 */ + 0x06, 0x00, /* 0000011000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x18, 0x00, /* 0001100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 48 0x30 '0' */ + 0x00, 0x00, /* 0000000000 */ + 0x0e, 0x00, /* 0000111000 */ + 0x1f, 0x00, /* 0001111100 */ + 0x23, 0x00, /* 0010001100 */ + 0x61, 0x80, /* 0110000110 */ + 0x63, 0x80, /* 0110001110 */ + 0x65, 0x80, /* 0110010110 */ + 0x65, 0x80, /* 0110010110 */ + 0x69, 0x80, /* 0110100110 */ + 0x69, 0x80, /* 0110100110 */ + 0x71, 0x80, /* 0111000110 */ + 0x61, 0x00, /* 0110000100 */ + 0x31, 0x00, /* 0011000100 */ + 0x3e, 0x00, /* 0011111000 */ + 0x1c, 0x00, /* 0001110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 49 0x31 '1' */ + 0x00, 0x00, /* 0000000000 */ + 0x04, 0x00, /* 0000010000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x1c, 0x00, /* 0001110000 */ + 0x3c, 0x00, /* 0011110000 */ + 0x6c, 0x00, /* 0110110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x7f, 0x80, /* 0111111110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 50 0x32 '2' */ + 0x00, 0x00, /* 0000000000 */ + 0x1e, 0x00, /* 0001111000 */ + 0x3f, 0x00, /* 0011111100 */ + 0x63, 0x80, /* 0110001110 */ + 0x41, 0x80, /* 0100000110 */ + 0x01, 0x80, /* 0000000110 */ + 0x01, 0x80, /* 0000000110 */ + 0x03, 0x00, /* 0000001100 */ + 0x06, 0x00, /* 0000011000 */ + 0x06, 0x00, /* 0000011000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x18, 0x00, /* 0001100000 */ + 0x30, 0x80, /* 0011000010 */ + 0x7f, 0x80, /* 0111111110 */ + 0x7f, 0x80, /* 0111111110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 51 0x33 '3' */ + 0x00, 0x00, /* 0000000000 */ + 0x1c, 0x00, /* 0001110000 */ + 0x3e, 0x00, /* 0011111000 */ + 0x47, 0x00, /* 0100011100 */ + 0x03, 0x00, /* 0000001100 */ + 0x07, 0x00, /* 0000011100 */ + 0x06, 0x00, /* 0000011000 */ + 0x0e, 0x00, /* 0000111000 */ + 0x07, 0x00, /* 0000011100 */ + 0x03, 0x00, /* 0000001100 */ + 0x01, 0x80, /* 0000000110 */ + 0x41, 0x80, /* 0100000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x3f, 0x00, /* 0011111100 */ + 0x1e, 0x00, /* 0001111000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 52 0x34 '4' */ + 0x00, 0x00, /* 0000000000 */ + 0x06, 0x00, /* 0000011000 */ + 0x0e, 0x00, /* 0000111000 */ + 0x1e, 0x00, /* 0001111000 */ + 0x36, 0x00, /* 0011011000 */ + 0x36, 0x00, /* 0011011000 */ + 0x66, 0x00, /* 0110011000 */ + 0x66, 0x00, /* 0110011000 */ + 0xc6, 0x00, /* 1100011000 */ + 0xc6, 0x00, /* 1100011000 */ + 0xff, 0x80, /* 1111111110 */ + 0xff, 0x80, /* 1111111110 */ + 0x06, 0x00, /* 0000011000 */ + 0x06, 0x00, /* 0000011000 */ + 0x06, 0x00, /* 0000011000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 53 0x35 '5' */ + 0x00, 0x00, /* 0000000000 */ + 0x1f, 0x00, /* 0001111100 */ + 0x1f, 0x00, /* 0001111100 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x7e, 0x00, /* 0111111000 */ + 0x67, 0x00, /* 0110011100 */ + 0x03, 0x80, /* 0000001110 */ + 0x01, 0x80, /* 0000000110 */ + 0x01, 0x80, /* 0000000110 */ + 0x01, 0x80, /* 0000000110 */ + 0x41, 0x80, /* 0100000110 */ + 0x63, 0x00, /* 0110001100 */ + 0x3e, 0x00, /* 0011111000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 54 0x36 '6' */ + 0x00, 0x00, /* 0000000000 */ + 0x06, 0x00, /* 0000011000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x18, 0x00, /* 0001100000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x6e, 0x00, /* 0110111000 */ + 0x7f, 0x00, /* 0111111100 */ + 0x73, 0x80, /* 0111001110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x71, 0x00, /* 0111000100 */ + 0x3e, 0x00, /* 0011111000 */ + 0x1c, 0x00, /* 0001110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 55 0x37 '7' */ + 0x00, 0x00, /* 0000000000 */ + 0x1f, 0x80, /* 0001111110 */ + 0x3f, 0x80, /* 0011111110 */ + 0x61, 0x80, /* 0110000110 */ + 0x01, 0x80, /* 0000000110 */ + 0x03, 0x00, /* 0000001100 */ + 0x03, 0x00, /* 0000001100 */ + 0x06, 0x00, /* 0000011000 */ + 0x06, 0x00, /* 0000011000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x18, 0x00, /* 0001100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 56 0x38 '8' */ + 0x00, 0x00, /* 0000000000 */ + 0x1e, 0x00, /* 0001111000 */ + 0x23, 0x00, /* 0010001100 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x31, 0x00, /* 0011000100 */ + 0x1a, 0x00, /* 0001101000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x16, 0x00, /* 0001011000 */ + 0x23, 0x00, /* 0010001100 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x31, 0x00, /* 0011000100 */ + 0x1e, 0x00, /* 0001111000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 57 0x39 '9' */ + 0x00, 0x00, /* 0000000000 */ + 0x0e, 0x00, /* 0000111000 */ + 0x17, 0x00, /* 0001011100 */ + 0x23, 0x80, /* 0010001110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x73, 0x80, /* 0111001110 */ + 0x3d, 0x80, /* 0011110110 */ + 0x19, 0x80, /* 0001100110 */ + 0x01, 0x80, /* 0000000110 */ + 0x03, 0x00, /* 0000001100 */ + 0x03, 0x00, /* 0000001100 */ + 0x06, 0x00, /* 0000011000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x18, 0x00, /* 0001100000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 58 0x3a ':' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x18, 0x00, /* 0001100000 */ + 0x3c, 0x00, /* 0011110000 */ + 0x3c, 0x00, /* 0011110000 */ + 0x18, 0x00, /* 0001100000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x18, 0x00, /* 0001100000 */ + 0x3c, 0x00, /* 0011110000 */ + 0x3c, 0x00, /* 0011110000 */ + 0x18, 0x00, /* 0001100000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 59 0x3b ';' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x18, 0x00, /* 0001100000 */ + 0x3c, 0x00, /* 0011110000 */ + 0x3c, 0x00, /* 0011110000 */ + 0x18, 0x00, /* 0001100000 */ + 0x00, 0x00, /* 0000000000 */ + 0x18, 0x00, /* 0001100000 */ + 0x3c, 0x00, /* 0011110000 */ + 0x3c, 0x00, /* 0011110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x18, 0x00, /* 0001100000 */ + 0x30, 0x00, /* 0011000000 */ + 0x20, 0x00, /* 0010000000 */ + + /* 60 0x3c '<' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x03, 0x00, /* 0000001100 */ + 0x06, 0x00, /* 0000011000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x18, 0x00, /* 0001100000 */ + 0x30, 0x00, /* 0011000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x18, 0x00, /* 0001100000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x06, 0x00, /* 0000011000 */ + 0x03, 0x00, /* 0000001100 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 61 0x3d '=' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x7f, 0x80, /* 0111111110 */ + 0x7f, 0x80, /* 0111111110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x7f, 0x80, /* 0111111110 */ + 0x7f, 0x80, /* 0111111110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 62 0x3e '>' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x18, 0x00, /* 0001100000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x06, 0x00, /* 0000011000 */ + 0x03, 0x00, /* 0000001100 */ + 0x01, 0x80, /* 0000000110 */ + 0x01, 0x80, /* 0000000110 */ + 0x03, 0x00, /* 0000001100 */ + 0x06, 0x00, /* 0000011000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x18, 0x00, /* 0001100000 */ + 0x30, 0x00, /* 0011000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 63 0x3f '?' */ + 0x00, 0x00, /* 0000000000 */ + 0x0e, 0x00, /* 0000111000 */ + 0x1f, 0x00, /* 0001111100 */ + 0x3b, 0x80, /* 0011101110 */ + 0x21, 0x80, /* 0010000110 */ + 0x01, 0x80, /* 0000000110 */ + 0x03, 0x00, /* 0000001100 */ + 0x06, 0x00, /* 0000011000 */ + 0x06, 0x00, /* 0000011000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 64 0x40 '@' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x0e, 0x00, /* 0000111000 */ + 0x3f, 0x00, /* 0011111100 */ + 0x31, 0x80, /* 0011000110 */ + 0x65, 0x80, /* 0110010110 */ + 0x6d, 0x80, /* 0110110110 */ + 0x6d, 0x80, /* 0110110110 */ + 0x6d, 0x80, /* 0110110110 */ + 0x6d, 0x80, /* 0110110110 */ + 0x6f, 0x80, /* 0110111110 */ + 0x60, 0x00, /* 0110000000 */ + 0x31, 0x80, /* 0011000110 */ + 0x3f, 0x80, /* 0011111110 */ + 0x0f, 0x00, /* 0000111100 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 65 0x41 'A' */ + 0x00, 0x00, /* 0000000000 */ + 0x04, 0x00, /* 0000010000 */ + 0x04, 0x00, /* 0000010000 */ + 0x0e, 0x00, /* 0000111000 */ + 0x0e, 0x00, /* 0000111000 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x19, 0x80, /* 0001100110 */ + 0x31, 0x80, /* 0011000110 */ + 0x3f, 0x80, /* 0011111110 */ + 0x31, 0x80, /* 0011000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x60, 0xc0, /* 0110000011 */ + 0x60, 0xc0, /* 0110000011 */ + 0xf1, 0xc0, /* 1111000111 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 66 0x42 'B' */ + 0x00, 0x00, /* 0000000000 */ + 0xfc, 0x00, /* 1111110000 */ + 0x62, 0x00, /* 0110001000 */ + 0x63, 0x00, /* 0110001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x66, 0x00, /* 0110011000 */ + 0x7e, 0x00, /* 0111111000 */ + 0x63, 0x00, /* 0110001100 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x63, 0x00, /* 0110001100 */ + 0xfe, 0x00, /* 1111111000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 67 0x43 'C' */ + 0x00, 0x00, /* 0000000000 */ + 0x0f, 0x00, /* 0000111100 */ + 0x11, 0x80, /* 0001000110 */ + 0x20, 0x80, /* 0010000010 */ + 0x20, 0x00, /* 0010000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x20, 0x00, /* 0010000000 */ + 0x30, 0x80, /* 0011000010 */ + 0x19, 0x00, /* 0001100100 */ + 0x0e, 0x00, /* 0000111000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 68 0x44 'D' */ + 0x00, 0x00, /* 0000000000 */ + 0xfc, 0x00, /* 1111110000 */ + 0x67, 0x00, /* 0110011100 */ + 0x63, 0x00, /* 0110001100 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x00, /* 0110000100 */ + 0x66, 0x00, /* 0110011000 */ + 0xf8, 0x00, /* 1111100000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 69 0x45 'E' */ + 0x00, 0x00, /* 0000000000 */ + 0x7f, 0x80, /* 0111111110 */ + 0x30, 0x80, /* 0011000010 */ + 0x30, 0x80, /* 0011000010 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x31, 0x00, /* 0011000100 */ + 0x3f, 0x00, /* 0011111100 */ + 0x31, 0x00, /* 0011000100 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x80, /* 0011000010 */ + 0x30, 0x80, /* 0011000010 */ + 0x7f, 0x80, /* 0111111110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 70 0x46 'F' */ + 0x00, 0x00, /* 0000000000 */ + 0x7f, 0x80, /* 0111111110 */ + 0x30, 0x80, /* 0011000010 */ + 0x30, 0x80, /* 0011000010 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x31, 0x00, /* 0011000100 */ + 0x3f, 0x00, /* 0011111100 */ + 0x31, 0x00, /* 0011000100 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x78, 0x00, /* 0111100000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 71 0x47 'G' */ + 0x00, 0x00, /* 0000000000 */ + 0x0f, 0x00, /* 0000111100 */ + 0x11, 0x80, /* 0001000110 */ + 0x20, 0x80, /* 0010000010 */ + 0x20, 0x00, /* 0010000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x67, 0xc0, /* 0110011111 */ + 0x61, 0x80, /* 0110000110 */ + 0x21, 0x80, /* 0010000110 */ + 0x31, 0x80, /* 0011000110 */ + 0x19, 0x80, /* 0001100110 */ + 0x0e, 0x00, /* 0000111000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 72 0x48 'H' */ + 0x00, 0x00, /* 0000000000 */ + 0xf3, 0xc0, /* 1111001111 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x7f, 0x80, /* 0111111110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0xf3, 0xc0, /* 1111001111 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 73 0x49 'I' */ + 0x00, 0x00, /* 0000000000 */ + 0x3f, 0x00, /* 0011111100 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x3f, 0x00, /* 0011111100 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 74 0x4a 'J' */ + 0x00, 0x00, /* 0000000000 */ + 0x3f, 0x00, /* 0011111100 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x08, 0x00, /* 0000100000 */ + 0x70, 0x00, /* 0111000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 75 0x4b 'K' */ + 0x00, 0x00, /* 0000000000 */ + 0xf1, 0x80, /* 1111000110 */ + 0x63, 0x00, /* 0110001100 */ + 0x66, 0x00, /* 0110011000 */ + 0x6c, 0x00, /* 0110110000 */ + 0x78, 0x00, /* 0111100000 */ + 0x70, 0x00, /* 0111000000 */ + 0x70, 0x00, /* 0111000000 */ + 0x78, 0x00, /* 0111100000 */ + 0x78, 0x00, /* 0111100000 */ + 0x6c, 0x00, /* 0110110000 */ + 0x66, 0x00, /* 0110011000 */ + 0x63, 0x00, /* 0110001100 */ + 0x61, 0x80, /* 0110000110 */ + 0xf0, 0xc0, /* 1111000011 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 76 0x4c 'L' */ + 0x00, 0x00, /* 0000000000 */ + 0x78, 0x00, /* 0111100000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x80, /* 0011000010 */ + 0x30, 0x80, /* 0011000010 */ + 0x7f, 0x80, /* 0111111110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 77 0x4d 'M' */ + 0x00, 0x00, /* 0000000000 */ + 0xe0, 0xc0, /* 1110000011 */ + 0x61, 0x80, /* 0110000110 */ + 0x73, 0x80, /* 0111001110 */ + 0x73, 0x80, /* 0111001110 */ + 0x6d, 0x80, /* 0110110110 */ + 0x6d, 0x80, /* 0110110110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0xf3, 0xc0, /* 1111001111 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 78 0x4e 'N' */ + 0x00, 0x00, /* 0000000000 */ + 0xf3, 0xc0, /* 1111001111 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x71, 0x80, /* 0111000110 */ + 0x79, 0x80, /* 0111100110 */ + 0x79, 0x80, /* 0111100110 */ + 0x6d, 0x80, /* 0110110110 */ + 0x6d, 0x80, /* 0110110110 */ + 0x67, 0x80, /* 0110011110 */ + 0x67, 0x80, /* 0110011110 */ + 0x63, 0x80, /* 0110001110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0xf3, 0xc0, /* 1111001111 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 79 0x4f 'O' */ + 0x00, 0x00, /* 0000000000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x17, 0x00, /* 0001011100 */ + 0x23, 0x00, /* 0010001100 */ + 0x21, 0x80, /* 0010000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x21, 0x00, /* 0010000100 */ + 0x31, 0x00, /* 0011000100 */ + 0x1a, 0x00, /* 0001101000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 80 0x50 'P' */ + 0x00, 0x00, /* 0000000000 */ + 0xfe, 0x00, /* 1111111000 */ + 0x63, 0x00, /* 0110001100 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x63, 0x00, /* 0110001100 */ + 0x7e, 0x00, /* 0111111000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0xf0, 0x00, /* 1111000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 81 0x51 'Q' */ + 0x00, 0x00, /* 0000000000 */ + 0x0e, 0x00, /* 0000111000 */ + 0x13, 0x00, /* 0001001100 */ + 0x23, 0x00, /* 0010001100 */ + 0x21, 0x80, /* 0010000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x31, 0x80, /* 0011000110 */ + 0x3b, 0x00, /* 0011101100 */ + 0x1e, 0x00, /* 0001111000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x1e, 0x00, /* 0001111000 */ + 0x26, 0x00, /* 0010011000 */ + 0x03, 0x80, /* 0000001110 */ + 0x00, 0x00, /* 0000000000 */ + + /* 82 0x52 'R' */ + 0x00, 0x00, /* 0000000000 */ + 0xfe, 0x00, /* 1111111000 */ + 0x63, 0x00, /* 0110001100 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x00, /* 0110000100 */ + 0x7e, 0x00, /* 0111111000 */ + 0x78, 0x00, /* 0111100000 */ + 0x6c, 0x00, /* 0110110000 */ + 0x6e, 0x00, /* 0110111000 */ + 0x67, 0x00, /* 0110011100 */ + 0x63, 0x80, /* 0110001110 */ + 0x61, 0xc0, /* 0110000111 */ + 0xf0, 0xc0, /* 1111000011 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 83 0x53 'S' */ + 0x00, 0x00, /* 0000000000 */ + 0x1f, 0x00, /* 0001111100 */ + 0x31, 0x80, /* 0011000110 */ + 0x60, 0x80, /* 0110000010 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x18, 0x00, /* 0001100000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x06, 0x00, /* 0000011000 */ + 0x03, 0x00, /* 0000001100 */ + 0x01, 0x80, /* 0000000110 */ + 0x41, 0x80, /* 0100000110 */ + 0x63, 0x00, /* 0110001100 */ + 0x3e, 0x00, /* 0011111000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 84 0x54 'T' */ + 0x00, 0x00, /* 0000000000 */ + 0x7f, 0x80, /* 0111111110 */ + 0x4c, 0x80, /* 0100110010 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x3f, 0x00, /* 0011111100 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 85 0x55 'U' */ + 0x00, 0x00, /* 0000000000 */ + 0xf3, 0xc0, /* 1111001111 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x73, 0x00, /* 0111001100 */ + 0x3f, 0x00, /* 0011111100 */ + 0x1e, 0x00, /* 0001111000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 86 0x56 'V' */ + 0x00, 0x00, /* 0000000000 */ + 0xe1, 0xc0, /* 1110000111 */ + 0xc0, 0xc0, /* 1100000011 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x33, 0x00, /* 0011001100 */ + 0x33, 0x00, /* 0011001100 */ + 0x33, 0x00, /* 0011001100 */ + 0x12, 0x00, /* 0001001000 */ + 0x1e, 0x00, /* 0001111000 */ + 0x1e, 0x00, /* 0001111000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 87 0x57 'W' */ + 0x00, 0x00, /* 0000000000 */ + 0xe1, 0xc0, /* 1110000111 */ + 0xc0, 0xc0, /* 1100000011 */ + 0xc0, 0xc0, /* 1100000011 */ + 0xc0, 0xc0, /* 1100000011 */ + 0xe0, 0xc0, /* 1110000011 */ + 0x61, 0x80, /* 0110000110 */ + 0x6d, 0x80, /* 0110110110 */ + 0x6d, 0x80, /* 0110110110 */ + 0x7f, 0x80, /* 0111111110 */ + 0x77, 0x00, /* 0111011100 */ + 0x33, 0x00, /* 0011001100 */ + 0x33, 0x00, /* 0011001100 */ + 0x33, 0x00, /* 0011001100 */ + 0x33, 0x00, /* 0011001100 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 88 0x58 'X' */ + 0x00, 0x00, /* 0000000000 */ + 0xf7, 0x80, /* 1111011110 */ + 0x63, 0x00, /* 0110001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x36, 0x00, /* 0011011000 */ + 0x36, 0x00, /* 0011011000 */ + 0x36, 0x00, /* 0011011000 */ + 0x1c, 0x00, /* 0001110000 */ + 0x1c, 0x00, /* 0001110000 */ + 0x36, 0x00, /* 0011011000 */ + 0x36, 0x00, /* 0011011000 */ + 0x36, 0x00, /* 0011011000 */ + 0x63, 0x00, /* 0110001100 */ + 0x63, 0x00, /* 0110001100 */ + 0xf7, 0x80, /* 1111011110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 89 0x59 'Y' */ + 0x00, 0x00, /* 0000000000 */ + 0xf3, 0xc0, /* 1111001111 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x33, 0x00, /* 0011001100 */ + 0x33, 0x00, /* 0011001100 */ + 0x1e, 0x00, /* 0001111000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x1e, 0x00, /* 0001111000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 90 0x5a 'Z' */ + 0x00, 0x00, /* 0000000000 */ + 0x3f, 0x80, /* 0011111110 */ + 0x21, 0x80, /* 0010000110 */ + 0x01, 0x80, /* 0000000110 */ + 0x03, 0x00, /* 0000001100 */ + 0x03, 0x00, /* 0000001100 */ + 0x06, 0x00, /* 0000011000 */ + 0x06, 0x00, /* 0000011000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x18, 0x00, /* 0001100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x80, /* 0011000010 */ + 0x3f, 0x80, /* 0011111110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 91 0x5b '[' */ + 0x00, 0x00, /* 0000000000 */ + 0x1f, 0x00, /* 0001111100 */ + 0x1f, 0x00, /* 0001111100 */ + 0x18, 0x00, /* 0001100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x1f, 0x00, /* 0001111100 */ + 0x1f, 0x00, /* 0001111100 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 92 0x5c '\' */ + 0x00, 0x00, /* 0000000000 */ + 0xc0, 0x00, /* 1100000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x18, 0x00, /* 0001100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x06, 0x00, /* 0000011000 */ + 0x06, 0x00, /* 0000011000 */ + 0x03, 0x00, /* 0000001100 */ + 0x03, 0x00, /* 0000001100 */ + 0x01, 0x80, /* 0000000110 */ + 0x01, 0x80, /* 0000000110 */ + 0x00, 0xc0, /* 0000000011 */ + 0x00, 0x00, /* 0000000000 */ + + /* 93 0x5d ']' */ + 0x00, 0x00, /* 0000000000 */ + 0x3e, 0x00, /* 0011111000 */ + 0x3e, 0x00, /* 0011111000 */ + 0x06, 0x00, /* 0000011000 */ + 0x06, 0x00, /* 0000011000 */ + 0x06, 0x00, /* 0000011000 */ + 0x06, 0x00, /* 0000011000 */ + 0x06, 0x00, /* 0000011000 */ + 0x06, 0x00, /* 0000011000 */ + 0x06, 0x00, /* 0000011000 */ + 0x06, 0x00, /* 0000011000 */ + 0x06, 0x00, /* 0000011000 */ + 0x06, 0x00, /* 0000011000 */ + 0x06, 0x00, /* 0000011000 */ + 0x3e, 0x00, /* 0011111000 */ + 0x3e, 0x00, /* 0011111000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 94 0x5e '^' */ + 0x00, 0x00, /* 0000000000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x1e, 0x00, /* 0001111000 */ + 0x33, 0x00, /* 0011001100 */ + 0x61, 0x80, /* 0110000110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 95 0x5f '_' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0x00, 0x00, /* 0000000000 */ + + /* 96 0x60 '`' */ + 0x04, 0x00, /* 0000010000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x18, 0x00, /* 0001100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x1e, 0x00, /* 0001111000 */ + 0x1e, 0x00, /* 0001111000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 97 0x61 'a' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x1f, 0x00, /* 0001111100 */ + 0x31, 0x80, /* 0011000110 */ + 0x21, 0x80, /* 0010000110 */ + 0x07, 0x80, /* 0000011110 */ + 0x39, 0x80, /* 0011100110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x73, 0x80, /* 0111001110 */ + 0x3d, 0xc0, /* 0011110111 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 98 0x62 'b' */ + 0x20, 0x00, /* 0010000000 */ + 0x60, 0x00, /* 0110000000 */ + 0xe0, 0x00, /* 1110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x66, 0x00, /* 0110011000 */ + 0x6f, 0x00, /* 0110111100 */ + 0x73, 0x80, /* 0111001110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x71, 0x80, /* 0111000110 */ + 0x7b, 0x00, /* 0111101100 */ + 0x4e, 0x00, /* 0100111000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 99 0x63 'c' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x1e, 0x00, /* 0001111000 */ + 0x37, 0x00, /* 0011011100 */ + 0x23, 0x00, /* 0010001100 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x71, 0x00, /* 0111000100 */ + 0x33, 0x00, /* 0011001100 */ + 0x1e, 0x00, /* 0001111000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 100 0x64 'd' */ + 0x01, 0x80, /* 0000000110 */ + 0x03, 0x80, /* 0000001110 */ + 0x01, 0x80, /* 0000000110 */ + 0x01, 0x80, /* 0000000110 */ + 0x01, 0x80, /* 0000000110 */ + 0x0d, 0x80, /* 0000110110 */ + 0x37, 0x80, /* 0011011110 */ + 0x23, 0x80, /* 0010001110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x73, 0x80, /* 0111001110 */ + 0x35, 0x80, /* 0011010110 */ + 0x19, 0xc0, /* 0001100111 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 101 0x65 'e' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x33, 0x00, /* 0011001100 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x7f, 0x80, /* 0111111110 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x19, 0x80, /* 0001100110 */ + 0x0e, 0x00, /* 0000111000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 102 0x66 'f' */ + 0x07, 0x00, /* 0000011100 */ + 0x09, 0x80, /* 0000100110 */ + 0x09, 0x80, /* 0000100110 */ + 0x18, 0x00, /* 0001100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x7f, 0x00, /* 0111111100 */ + 0x18, 0x00, /* 0001100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x3c, 0x00, /* 0011110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 103 0x67 'g' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x1c, 0x80, /* 0001110010 */ + 0x37, 0x80, /* 0011011110 */ + 0x63, 0x00, /* 0110001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x36, 0x00, /* 0011011000 */ + 0x3c, 0x00, /* 0011110000 */ + 0x60, 0x00, /* 0110000000 */ + 0x7f, 0x00, /* 0111111100 */ + 0x3f, 0x80, /* 0011111110 */ + 0x21, 0x80, /* 0010000110 */ + 0x40, 0x80, /* 0100000010 */ + 0x7f, 0x00, /* 0111111100 */ + 0x3e, 0x00, /* 0011111000 */ + + /* 104 0x68 'h' */ + 0x10, 0x00, /* 0001000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x70, 0x00, /* 0111000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x37, 0x00, /* 0011011100 */ + 0x3b, 0x80, /* 0011101110 */ + 0x31, 0x80, /* 0011000110 */ + 0x31, 0x80, /* 0011000110 */ + 0x31, 0x80, /* 0011000110 */ + 0x31, 0x80, /* 0011000110 */ + 0x31, 0x80, /* 0011000110 */ + 0x31, 0x80, /* 0011000110 */ + 0x31, 0x80, /* 0011000110 */ + 0x7b, 0xc0, /* 0111101111 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 105 0x69 'i' */ + 0x00, 0x00, /* 0000000000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x3c, 0x00, /* 0011110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x3f, 0x00, /* 0011111100 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 106 0x6a 'j' */ + 0x00, 0x00, /* 0000000000 */ + 0x01, 0x80, /* 0000000110 */ + 0x01, 0x80, /* 0000000110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x07, 0x80, /* 0000011110 */ + 0x01, 0x80, /* 0000000110 */ + 0x01, 0x80, /* 0000000110 */ + 0x01, 0x80, /* 0000000110 */ + 0x01, 0x80, /* 0000000110 */ + 0x01, 0x80, /* 0000000110 */ + 0x01, 0x80, /* 0000000110 */ + 0x01, 0x80, /* 0000000110 */ + 0x41, 0x80, /* 0100000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x71, 0x80, /* 0111000110 */ + 0x3f, 0x00, /* 0011111100 */ + 0x1c, 0x00, /* 0001110000 */ + + /* 107 0x6b 'k' */ + 0x60, 0x00, /* 0110000000 */ + 0xe0, 0x00, /* 1110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x63, 0x80, /* 0110001110 */ + 0x66, 0x00, /* 0110011000 */ + 0x6c, 0x00, /* 0110110000 */ + 0x78, 0x00, /* 0111100000 */ + 0x70, 0x00, /* 0111000000 */ + 0x78, 0x00, /* 0111100000 */ + 0x6c, 0x00, /* 0110110000 */ + 0x6e, 0x00, /* 0110111000 */ + 0x67, 0x00, /* 0110011100 */ + 0xf3, 0x80, /* 1111001110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 108 0x6c 'l' */ + 0x3c, 0x00, /* 0011110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x3f, 0x00, /* 0011111100 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 109 0x6d 'm' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0xdb, 0x80, /* 1101101110 */ + 0x6d, 0x80, /* 0110110110 */ + 0x6d, 0x80, /* 0110110110 */ + 0x6d, 0x80, /* 0110110110 */ + 0x6d, 0x80, /* 0110110110 */ + 0x6d, 0x80, /* 0110110110 */ + 0x6d, 0x80, /* 0110110110 */ + 0x6d, 0x80, /* 0110110110 */ + 0x6d, 0x80, /* 0110110110 */ + 0xed, 0xc0, /* 1110110111 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 110 0x6e 'n' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x6f, 0x00, /* 0110111100 */ + 0x7b, 0x80, /* 0111101110 */ + 0x31, 0x80, /* 0011000110 */ + 0x31, 0x80, /* 0011000110 */ + 0x31, 0x80, /* 0011000110 */ + 0x31, 0x80, /* 0011000110 */ + 0x31, 0x80, /* 0011000110 */ + 0x31, 0x80, /* 0011000110 */ + 0x31, 0x80, /* 0011000110 */ + 0x7b, 0xc0, /* 0111101111 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 111 0x6f 'o' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x3c, 0x00, /* 0011110000 */ + 0x66, 0x00, /* 0110011000 */ + 0xc3, 0x00, /* 1100001100 */ + 0xc1, 0x80, /* 1100000110 */ + 0xc1, 0x80, /* 1100000110 */ + 0xc1, 0x80, /* 1100000110 */ + 0xc1, 0x80, /* 1100000110 */ + 0xe1, 0x80, /* 1110000110 */ + 0x73, 0x00, /* 0111001100 */ + 0x3c, 0x00, /* 0011110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 112 0x70 'p' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0xde, 0x00, /* 1101111000 */ + 0x76, 0x00, /* 0111011000 */ + 0x63, 0x00, /* 0110001100 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x71, 0x80, /* 0111000110 */ + 0x7b, 0x00, /* 0111101100 */ + 0x7e, 0x00, /* 0111111000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0xf0, 0x00, /* 1111000000 */ + + /* 113 0x71 'q' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x0e, 0xc0, /* 0000111011 */ + 0x1b, 0x80, /* 0001101110 */ + 0x33, 0x80, /* 0011001110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x71, 0x80, /* 0111000110 */ + 0x3b, 0x80, /* 0011101110 */ + 0x1f, 0x80, /* 0001111110 */ + 0x01, 0x80, /* 0000000110 */ + 0x01, 0x80, /* 0000000110 */ + 0x03, 0xc0, /* 0000001111 */ + + /* 114 0x72 'r' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x73, 0x00, /* 0111001100 */ + 0x35, 0x80, /* 0011010110 */ + 0x39, 0x80, /* 0011100110 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x78, 0x00, /* 0111100000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 115 0x73 's' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x3f, 0x00, /* 0011111100 */ + 0x63, 0x00, /* 0110001100 */ + 0x61, 0x00, /* 0110000100 */ + 0x70, 0x00, /* 0111000000 */ + 0x38, 0x00, /* 0011100000 */ + 0x0e, 0x00, /* 0000111000 */ + 0x07, 0x00, /* 0000011100 */ + 0x43, 0x00, /* 0100001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x7e, 0x00, /* 0111111000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 116 0x74 't' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x08, 0x00, /* 0000100000 */ + 0x08, 0x00, /* 0000100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x7f, 0x80, /* 0111111110 */ + 0x18, 0x00, /* 0001100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x1c, 0x80, /* 0001110010 */ + 0x0f, 0x00, /* 0000111100 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 117 0x75 'u' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0xf7, 0x80, /* 1111011110 */ + 0x63, 0x00, /* 0110001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x77, 0x00, /* 0111011100 */ + 0x3d, 0x80, /* 0011110110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 118 0x76 'v' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0xf1, 0xc0, /* 1111000111 */ + 0x60, 0xc0, /* 0110000011 */ + 0x31, 0x80, /* 0011000110 */ + 0x31, 0x80, /* 0011000110 */ + 0x19, 0x80, /* 0001100110 */ + 0x1b, 0x00, /* 0001101100 */ + 0x0f, 0x00, /* 0000111100 */ + 0x0f, 0x00, /* 0000111100 */ + 0x06, 0x00, /* 0000011000 */ + 0x06, 0x00, /* 0000011000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 119 0x77 'w' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0xe3, 0xc0, /* 1110001111 */ + 0xc1, 0x80, /* 1100000110 */ + 0xc1, 0x80, /* 1100000110 */ + 0xc1, 0x80, /* 1100000110 */ + 0xc1, 0x80, /* 1100000110 */ + 0x6b, 0x00, /* 0110101100 */ + 0x6b, 0x00, /* 0110101100 */ + 0x7e, 0x00, /* 0111111000 */ + 0x36, 0x00, /* 0011011000 */ + 0x36, 0x00, /* 0011011000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 120 0x78 'x' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0xf7, 0x80, /* 1111011110 */ + 0x63, 0x00, /* 0110001100 */ + 0x36, 0x00, /* 0011011000 */ + 0x36, 0x00, /* 0011011000 */ + 0x1c, 0x00, /* 0001110000 */ + 0x1c, 0x00, /* 0001110000 */ + 0x36, 0x00, /* 0011011000 */ + 0x66, 0x00, /* 0110011000 */ + 0x63, 0x00, /* 0110001100 */ + 0xf7, 0x80, /* 1111011110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 121 0x79 'y' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0xf3, 0xc0, /* 1111001111 */ + 0x61, 0x80, /* 0110000110 */ + 0x33, 0x00, /* 0011001100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1e, 0x00, /* 0001111000 */ + 0x0e, 0x00, /* 0000111000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x18, 0x00, /* 0001100000 */ + 0x78, 0x00, /* 0111100000 */ + 0x70, 0x00, /* 0111000000 */ + + /* 122 0x7a 'z' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x7f, 0x80, /* 0111111110 */ + 0x61, 0x80, /* 0110000110 */ + 0x43, 0x00, /* 0100001100 */ + 0x06, 0x00, /* 0000011000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x18, 0x00, /* 0001100000 */ + 0x30, 0x00, /* 0011000000 */ + 0x60, 0x80, /* 0110000010 */ + 0x61, 0x80, /* 0110000110 */ + 0x7f, 0x80, /* 0111111110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 123 0x7b '{' */ + 0x07, 0x00, /* 0000011100 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x18, 0x00, /* 0001100000 */ + 0x70, 0x00, /* 0111000000 */ + 0x18, 0x00, /* 0001100000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x07, 0x00, /* 0000011100 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 124 0x7c '|' */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 125 0x7d '}' */ + 0x38, 0x00, /* 0011100000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x06, 0x00, /* 0000011000 */ + 0x03, 0x80, /* 0000001110 */ + 0x06, 0x00, /* 0000011000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x38, 0x00, /* 0011100000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 126 0x7e '~' */ + 0x00, 0x00, /* 0000000000 */ + 0x18, 0x80, /* 0001100010 */ + 0x3d, 0x80, /* 0011110110 */ + 0x6f, 0x00, /* 0110111100 */ + 0x46, 0x00, /* 0100011000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 127 0x7f '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x12, 0x00, /* 0001001000 */ + 0x21, 0x00, /* 0010000100 */ + 0x40, 0x80, /* 0100000010 */ + 0x40, 0x80, /* 0100000010 */ + 0x40, 0x80, /* 0100000010 */ + 0x7f, 0x80, /* 0111111110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 128 0x80 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x1f, 0x00, /* 0001111100 */ + 0x21, 0x80, /* 0010000110 */ + 0x40, 0x80, /* 0100000010 */ + 0x40, 0x00, /* 0100000000 */ + 0x40, 0x00, /* 0100000000 */ + 0x40, 0x00, /* 0100000000 */ + 0x40, 0x00, /* 0100000000 */ + 0x40, 0x00, /* 0100000000 */ + 0x40, 0x00, /* 0100000000 */ + 0x60, 0x80, /* 0110000010 */ + 0x31, 0x00, /* 0011000100 */ + 0x1e, 0x00, /* 0001111000 */ + 0x08, 0x00, /* 0000100000 */ + 0x04, 0x00, /* 0000010000 */ + 0x02, 0x00, /* 0000001000 */ + 0x02, 0x00, /* 0000001000 */ + 0x1c, 0x00, /* 0001110000 */ + + /* 129 0x81 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x7b, 0x80, /* 0111101110 */ + 0x31, 0x00, /* 0011000100 */ + 0x31, 0x00, /* 0011000100 */ + 0x31, 0x00, /* 0011000100 */ + 0x31, 0x00, /* 0011000100 */ + 0x31, 0x00, /* 0011000100 */ + 0x31, 0x00, /* 0011000100 */ + 0x31, 0x00, /* 0011000100 */ + 0x3b, 0x00, /* 0011101100 */ + 0x1c, 0x80, /* 0001110010 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 130 0x82 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x01, 0x00, /* 0000000100 */ + 0x02, 0x00, /* 0000001000 */ + 0x04, 0x00, /* 0000010000 */ + 0x00, 0x00, /* 0000000000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x33, 0x00, /* 0011001100 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x7f, 0x80, /* 0111111110 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x19, 0x80, /* 0001100110 */ + 0x0e, 0x00, /* 0000111000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 131 0x83 '.' */ + 0x04, 0x00, /* 0000010000 */ + 0x0e, 0x00, /* 0000111000 */ + 0x1b, 0x00, /* 0001101100 */ + 0x31, 0x80, /* 0011000110 */ + 0x00, 0x00, /* 0000000000 */ + 0x1f, 0x00, /* 0001111100 */ + 0x31, 0x80, /* 0011000110 */ + 0x21, 0x80, /* 0010000110 */ + 0x07, 0x80, /* 0000011110 */ + 0x39, 0x80, /* 0011100110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x73, 0x80, /* 0111001110 */ + 0x3d, 0xc0, /* 0011110111 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 132 0x84 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x1f, 0x00, /* 0001111100 */ + 0x31, 0x80, /* 0011000110 */ + 0x21, 0x80, /* 0010000110 */ + 0x07, 0x80, /* 0000011110 */ + 0x39, 0x80, /* 0011100110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x73, 0x80, /* 0111001110 */ + 0x3d, 0xc0, /* 0011110111 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 133 0x85 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x18, 0x00, /* 0001100000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x06, 0x00, /* 0000011000 */ + 0x00, 0x00, /* 0000000000 */ + 0x1f, 0x00, /* 0001111100 */ + 0x31, 0x80, /* 0011000110 */ + 0x21, 0x80, /* 0010000110 */ + 0x07, 0x80, /* 0000011110 */ + 0x39, 0x80, /* 0011100110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x73, 0x80, /* 0111001110 */ + 0x3d, 0xc0, /* 0011110111 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 134 0x86 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x0e, 0x00, /* 0000111000 */ + 0x1b, 0x00, /* 0001101100 */ + 0x0e, 0x00, /* 0000111000 */ + 0x00, 0x00, /* 0000000000 */ + 0x1f, 0x00, /* 0001111100 */ + 0x31, 0x80, /* 0011000110 */ + 0x21, 0x80, /* 0010000110 */ + 0x07, 0x80, /* 0000011110 */ + 0x39, 0x80, /* 0011100110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x73, 0x80, /* 0111001110 */ + 0x3d, 0xc0, /* 0011110111 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 135 0x87 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x1f, 0x00, /* 0001111100 */ + 0x31, 0x80, /* 0011000110 */ + 0x20, 0x80, /* 0010000010 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x70, 0x80, /* 0111000010 */ + 0x30, 0x80, /* 0011000010 */ + 0x1f, 0x00, /* 0001111100 */ + 0x04, 0x00, /* 0000010000 */ + 0x02, 0x00, /* 0000001000 */ + 0x01, 0x00, /* 0000000100 */ + 0x0e, 0x00, /* 0000111000 */ + + /* 136 0x88 '.' */ + 0x04, 0x00, /* 0000010000 */ + 0x0e, 0x00, /* 0000111000 */ + 0x1b, 0x00, /* 0001101100 */ + 0x31, 0x80, /* 0011000110 */ + 0x00, 0x00, /* 0000000000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x33, 0x00, /* 0011001100 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x7f, 0x80, /* 0111111110 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x19, 0x80, /* 0001100110 */ + 0x0e, 0x00, /* 0000111000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 137 0x89 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x36, 0x00, /* 0011011000 */ + 0x36, 0x00, /* 0011011000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x33, 0x00, /* 0011001100 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x7f, 0x80, /* 0111111110 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x19, 0x80, /* 0001100110 */ + 0x0e, 0x00, /* 0000111000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 138 0x8a '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x18, 0x00, /* 0001100000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x33, 0x00, /* 0011001100 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x7f, 0x80, /* 0111111110 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x19, 0x80, /* 0001100110 */ + 0x0e, 0x00, /* 0000111000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 139 0x8b '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x36, 0x00, /* 0011011000 */ + 0x36, 0x00, /* 0011011000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x3c, 0x00, /* 0011110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x3f, 0x00, /* 0011111100 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 140 0x8c '.' */ + 0x08, 0x00, /* 0000100000 */ + 0x1c, 0x00, /* 0001110000 */ + 0x36, 0x00, /* 0011011000 */ + 0x63, 0x00, /* 0110001100 */ + 0x00, 0x00, /* 0000000000 */ + 0x3c, 0x00, /* 0011110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x3f, 0x00, /* 0011111100 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 141 0x8d '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x18, 0x00, /* 0001100000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x3c, 0x00, /* 0011110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x3f, 0x00, /* 0011111100 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 142 0x8e '.' */ + 0x31, 0x80, /* 0011000110 */ + 0x31, 0x80, /* 0011000110 */ + 0x04, 0x00, /* 0000010000 */ + 0x04, 0x00, /* 0000010000 */ + 0x0e, 0x00, /* 0000111000 */ + 0x0e, 0x00, /* 0000111000 */ + 0x0e, 0x00, /* 0000111000 */ + 0x19, 0x00, /* 0001100100 */ + 0x19, 0x00, /* 0001100100 */ + 0x3f, 0x00, /* 0011111100 */ + 0x31, 0x00, /* 0011000100 */ + 0x31, 0x00, /* 0011000100 */ + 0x60, 0x80, /* 0110000010 */ + 0x60, 0x80, /* 0110000010 */ + 0xf3, 0xc0, /* 1111001111 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 143 0x8f '.' */ + 0x04, 0x00, /* 0000010000 */ + 0x0a, 0x00, /* 0000101000 */ + 0x0a, 0x00, /* 0000101000 */ + 0x04, 0x00, /* 0000010000 */ + 0x0e, 0x00, /* 0000111000 */ + 0x0e, 0x00, /* 0000111000 */ + 0x0e, 0x00, /* 0000111000 */ + 0x19, 0x00, /* 0001100100 */ + 0x19, 0x00, /* 0001100100 */ + 0x3f, 0x00, /* 0011111100 */ + 0x31, 0x00, /* 0011000100 */ + 0x31, 0x00, /* 0011000100 */ + 0x60, 0x80, /* 0110000010 */ + 0x60, 0x80, /* 0110000010 */ + 0xf3, 0xc0, /* 1111001111 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 144 0x90 '.' */ + 0x03, 0x00, /* 0000001100 */ + 0x06, 0x00, /* 0000011000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x7f, 0x80, /* 0111111110 */ + 0x30, 0x80, /* 0011000010 */ + 0x30, 0x80, /* 0011000010 */ + 0x30, 0x00, /* 0011000000 */ + 0x31, 0x00, /* 0011000100 */ + 0x3f, 0x00, /* 0011111100 */ + 0x31, 0x00, /* 0011000100 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x80, /* 0011000010 */ + 0x30, 0x80, /* 0011000010 */ + 0x7f, 0x80, /* 0111111110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 145 0x91 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x3b, 0x80, /* 0011101110 */ + 0x6c, 0xc0, /* 0110110011 */ + 0x4c, 0xc0, /* 0100110011 */ + 0x0c, 0xc0, /* 0000110011 */ + 0x3f, 0xc0, /* 0011111111 */ + 0x6c, 0x00, /* 0110110000 */ + 0xcc, 0x00, /* 1100110000 */ + 0xcc, 0x00, /* 1100110000 */ + 0xee, 0xc0, /* 1110111011 */ + 0x7b, 0x80, /* 0111101110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 146 0x92 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x07, 0xc0, /* 0000011111 */ + 0x0e, 0x40, /* 0000111001 */ + 0x0e, 0x40, /* 0000111001 */ + 0x0e, 0x00, /* 0000111000 */ + 0x16, 0x00, /* 0001011000 */ + 0x16, 0x80, /* 0001011010 */ + 0x17, 0x80, /* 0001011110 */ + 0x16, 0x80, /* 0001011010 */ + 0x3e, 0x00, /* 0011111000 */ + 0x26, 0x00, /* 0010011000 */ + 0x26, 0x00, /* 0010011000 */ + 0x46, 0x40, /* 0100011001 */ + 0x46, 0x40, /* 0100011001 */ + 0xef, 0xc0, /* 1110111111 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 147 0x93 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x08, 0x00, /* 0000100000 */ + 0x1c, 0x00, /* 0001110000 */ + 0x36, 0x00, /* 0011011000 */ + 0x00, 0x00, /* 0000000000 */ + 0x3c, 0x00, /* 0011110000 */ + 0x66, 0x00, /* 0110011000 */ + 0xc3, 0x00, /* 1100001100 */ + 0xc1, 0x80, /* 1100000110 */ + 0xc1, 0x80, /* 1100000110 */ + 0xc1, 0x80, /* 1100000110 */ + 0xc1, 0x80, /* 1100000110 */ + 0xe1, 0x80, /* 1110000110 */ + 0x73, 0x00, /* 0111001100 */ + 0x3c, 0x00, /* 0011110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 148 0x94 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x33, 0x00, /* 0011001100 */ + 0x33, 0x00, /* 0011001100 */ + 0x00, 0x00, /* 0000000000 */ + 0x3c, 0x00, /* 0011110000 */ + 0x66, 0x00, /* 0110011000 */ + 0xc3, 0x00, /* 1100001100 */ + 0xc1, 0x80, /* 1100000110 */ + 0xc1, 0x80, /* 1100000110 */ + 0xc1, 0x80, /* 1100000110 */ + 0xc1, 0x80, /* 1100000110 */ + 0xe1, 0x80, /* 1110000110 */ + 0x73, 0x00, /* 0111001100 */ + 0x3c, 0x00, /* 0011110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 149 0x95 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x18, 0x00, /* 0001100000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x06, 0x00, /* 0000011000 */ + 0x00, 0x00, /* 0000000000 */ + 0x3c, 0x00, /* 0011110000 */ + 0x66, 0x00, /* 0110011000 */ + 0xc3, 0x00, /* 1100001100 */ + 0xc1, 0x80, /* 1100000110 */ + 0xc1, 0x80, /* 1100000110 */ + 0xc1, 0x80, /* 1100000110 */ + 0xc1, 0x80, /* 1100000110 */ + 0xe1, 0x80, /* 1110000110 */ + 0x73, 0x00, /* 0111001100 */ + 0x3c, 0x00, /* 0011110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 150 0x96 '.' */ + 0x08, 0x00, /* 0000100000 */ + 0x1c, 0x00, /* 0001110000 */ + 0x36, 0x00, /* 0011011000 */ + 0x63, 0x00, /* 0110001100 */ + 0x00, 0x00, /* 0000000000 */ + 0xf7, 0x80, /* 1111011110 */ + 0x63, 0x00, /* 0110001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x77, 0x00, /* 0111011100 */ + 0x3d, 0x80, /* 0011110110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 151 0x97 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x18, 0x00, /* 0001100000 */ + 0x00, 0x00, /* 0000000000 */ + 0xf7, 0x80, /* 1111011110 */ + 0x63, 0x00, /* 0110001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x77, 0x00, /* 0111011100 */ + 0x3d, 0x80, /* 0011110110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 152 0x98 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x33, 0x00, /* 0011001100 */ + 0x33, 0x00, /* 0011001100 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0xf3, 0xc0, /* 1111001111 */ + 0x61, 0x80, /* 0110000110 */ + 0x33, 0x00, /* 0011001100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1e, 0x00, /* 0001111000 */ + 0x0e, 0x00, /* 0000111000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x18, 0x00, /* 0001100000 */ + 0x78, 0x00, /* 0111100000 */ + 0x70, 0x00, /* 0111000000 */ + + /* 153 0x99 '.' */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x0c, 0x00, /* 0000110000 */ + 0x17, 0x00, /* 0001011100 */ + 0x23, 0x00, /* 0010001100 */ + 0x21, 0x80, /* 0010000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x21, 0x00, /* 0010000100 */ + 0x31, 0x00, /* 0011000100 */ + 0x1a, 0x00, /* 0001101000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 154 0x9a '.' */ + 0x33, 0x00, /* 0011001100 */ + 0x33, 0x00, /* 0011001100 */ + 0x00, 0x00, /* 0000000000 */ + 0xf1, 0xc0, /* 1111000111 */ + 0x60, 0x80, /* 0110000010 */ + 0x60, 0x80, /* 0110000010 */ + 0x60, 0x80, /* 0110000010 */ + 0x60, 0x80, /* 0110000010 */ + 0x60, 0x80, /* 0110000010 */ + 0x60, 0x80, /* 0110000010 */ + 0x60, 0x80, /* 0110000010 */ + 0x60, 0x80, /* 0110000010 */ + 0x71, 0x00, /* 0111000100 */ + 0x3f, 0x00, /* 0011111100 */ + 0x1e, 0x00, /* 0001111000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 155 0x9b '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x06, 0x00, /* 0000011000 */ + 0x06, 0x00, /* 0000011000 */ + 0x1f, 0x80, /* 0001111110 */ + 0x36, 0x80, /* 0011011010 */ + 0x26, 0x00, /* 0010011000 */ + 0x66, 0x00, /* 0110011000 */ + 0x66, 0x00, /* 0110011000 */ + 0x66, 0x00, /* 0110011000 */ + 0x66, 0x00, /* 0110011000 */ + 0x76, 0x00, /* 0111011000 */ + 0x36, 0x80, /* 0011011010 */ + 0x1f, 0x80, /* 0001111110 */ + 0x06, 0x00, /* 0000011000 */ + 0x06, 0x00, /* 0000011000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 156 0x9c '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x1e, 0x00, /* 0001111000 */ + 0x3b, 0x00, /* 0011101100 */ + 0x33, 0x00, /* 0011001100 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x7e, 0x00, /* 0111111000 */ + 0x7e, 0x00, /* 0111111000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x7c, 0x80, /* 0111110010 */ + 0x7f, 0x80, /* 0111111110 */ + 0x43, 0x00, /* 0100001100 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 157 0x9d '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x40, 0x80, /* 0100000010 */ + 0x40, 0x80, /* 0100000010 */ + 0x21, 0x00, /* 0010000100 */ + 0x33, 0x00, /* 0011001100 */ + 0x33, 0x00, /* 0011001100 */ + 0x1e, 0x00, /* 0001111000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x3f, 0x00, /* 0011111100 */ + 0x0c, 0x00, /* 0000110000 */ + 0x3f, 0x00, /* 0011111100 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 158 0x9e '.' */ + 0x00, 0x00, /* 0000000000 */ + 0xbf, 0x00, /* 1011111100 */ + 0x40, 0x80, /* 0100000010 */ + 0x40, 0x80, /* 0100000010 */ + 0x7f, 0x00, /* 0111111100 */ + 0x40, 0x00, /* 0100000000 */ + 0x48, 0x00, /* 0100100000 */ + 0x48, 0x00, /* 0100100000 */ + 0x5e, 0x00, /* 0101111000 */ + 0x48, 0x00, /* 0100100000 */ + 0x48, 0x00, /* 0100100000 */ + 0x48, 0x00, /* 0100100000 */ + 0x48, 0x80, /* 0100100010 */ + 0x47, 0x00, /* 0100011100 */ + 0xe0, 0x00, /* 1110000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 159 0x9f '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x03, 0x00, /* 0000001100 */ + 0x04, 0x80, /* 0000010010 */ + 0x08, 0x00, /* 0000100000 */ + 0x08, 0x00, /* 0000100000 */ + 0x08, 0x00, /* 0000100000 */ + 0x08, 0x00, /* 0000100000 */ + 0x09, 0x00, /* 0000100100 */ + 0x3e, 0x00, /* 0011111000 */ + 0x48, 0x00, /* 0100100000 */ + 0x08, 0x00, /* 0000100000 */ + 0x08, 0x00, /* 0000100000 */ + 0x08, 0x00, /* 0000100000 */ + 0x08, 0x00, /* 0000100000 */ + 0x08, 0x00, /* 0000100000 */ + 0x90, 0x00, /* 1001000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 160 0xa0 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x03, 0x00, /* 0000001100 */ + 0x06, 0x00, /* 0000011000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x1f, 0x00, /* 0001111100 */ + 0x31, 0x80, /* 0011000110 */ + 0x21, 0x80, /* 0010000110 */ + 0x07, 0x80, /* 0000011110 */ + 0x39, 0x80, /* 0011100110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x73, 0x80, /* 0111001110 */ + 0x3d, 0xc0, /* 0011110111 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 161 0xa1 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x03, 0x00, /* 0000001100 */ + 0x06, 0x00, /* 0000011000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x3c, 0x00, /* 0011110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x3f, 0x00, /* 0011111100 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 162 0xa2 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x06, 0x00, /* 0000011000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x18, 0x00, /* 0001100000 */ + 0x00, 0x00, /* 0000000000 */ + 0x3c, 0x00, /* 0011110000 */ + 0x66, 0x00, /* 0110011000 */ + 0xc3, 0x00, /* 1100001100 */ + 0xc1, 0x80, /* 1100000110 */ + 0xc1, 0x80, /* 1100000110 */ + 0xc1, 0x80, /* 1100000110 */ + 0xc1, 0x80, /* 1100000110 */ + 0xe1, 0x80, /* 1110000110 */ + 0x73, 0x00, /* 0111001100 */ + 0x3c, 0x00, /* 0011110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 163 0xa3 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x06, 0x00, /* 0000011000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x18, 0x00, /* 0001100000 */ + 0x00, 0x00, /* 0000000000 */ + 0xf7, 0x80, /* 1111011110 */ + 0x63, 0x00, /* 0110001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x77, 0x00, /* 0111011100 */ + 0x3d, 0x80, /* 0011110110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 164 0xa4 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x38, 0x80, /* 0011100010 */ + 0x7f, 0x80, /* 0111111110 */ + 0x47, 0x00, /* 0100011100 */ + 0x00, 0x00, /* 0000000000 */ + 0x6f, 0x00, /* 0110111100 */ + 0x7b, 0x80, /* 0111101110 */ + 0x31, 0x80, /* 0011000110 */ + 0x31, 0x80, /* 0011000110 */ + 0x31, 0x80, /* 0011000110 */ + 0x31, 0x80, /* 0011000110 */ + 0x31, 0x80, /* 0011000110 */ + 0x31, 0x80, /* 0011000110 */ + 0x31, 0x80, /* 0011000110 */ + 0x7b, 0xc0, /* 0111101111 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 165 0xa5 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x38, 0x80, /* 0011100010 */ + 0x7f, 0x80, /* 0111111110 */ + 0x47, 0x00, /* 0100011100 */ + 0x00, 0x00, /* 0000000000 */ + 0xe3, 0xc0, /* 1110001111 */ + 0x71, 0x80, /* 0111000110 */ + 0x79, 0x80, /* 0111100110 */ + 0x79, 0x80, /* 0111100110 */ + 0x6d, 0x80, /* 0110110110 */ + 0x6d, 0x80, /* 0110110110 */ + 0x67, 0x80, /* 0110011110 */ + 0x63, 0x80, /* 0110001110 */ + 0x61, 0x80, /* 0110000110 */ + 0xf0, 0xc0, /* 1111000011 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 166 0xa6 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x3e, 0x00, /* 0011111000 */ + 0x63, 0x00, /* 0110001100 */ + 0x03, 0x00, /* 0000001100 */ + 0x0f, 0x00, /* 0000111100 */ + 0x33, 0x00, /* 0011001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x67, 0x00, /* 0110011100 */ + 0x3b, 0x80, /* 0011101110 */ + 0x00, 0x00, /* 0000000000 */ + 0x7f, 0x80, /* 0111111110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 167 0xa7 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x0e, 0x00, /* 0000111000 */ + 0x33, 0x00, /* 0011001100 */ + 0x21, 0x80, /* 0010000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x00, /* 0110000100 */ + 0x33, 0x00, /* 0011001100 */ + 0x1c, 0x00, /* 0001110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x7f, 0x80, /* 0111111110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 168 0xa8 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x06, 0x00, /* 0000011000 */ + 0x06, 0x00, /* 0000011000 */ + 0x00, 0x00, /* 0000000000 */ + 0x06, 0x00, /* 0000011000 */ + 0x06, 0x00, /* 0000011000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x18, 0x00, /* 0001100000 */ + 0x30, 0x00, /* 0011000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x80, /* 0110000010 */ + 0x73, 0x80, /* 0111001110 */ + 0x3f, 0x00, /* 0011111100 */ + 0x1e, 0x00, /* 0001111000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 169 0xa9 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x7f, 0x80, /* 0111111110 */ + 0x7f, 0x80, /* 0111111110 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 170 0xaa '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x7f, 0x80, /* 0111111110 */ + 0x7f, 0x80, /* 0111111110 */ + 0x01, 0x80, /* 0000000110 */ + 0x01, 0x80, /* 0000000110 */ + 0x01, 0x80, /* 0000000110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 171 0xab '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x20, 0x00, /* 0010000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x20, 0x00, /* 0010000000 */ + 0x20, 0x80, /* 0010000010 */ + 0x21, 0x00, /* 0010000100 */ + 0x22, 0x00, /* 0010001000 */ + 0x74, 0x00, /* 0111010000 */ + 0x08, 0x00, /* 0000100000 */ + 0x17, 0x00, /* 0001011100 */ + 0x28, 0x80, /* 0010100010 */ + 0x43, 0x00, /* 0100001100 */ + 0x04, 0x00, /* 0000010000 */ + 0x08, 0x00, /* 0000100000 */ + 0x0f, 0x80, /* 0000111110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 172 0xac '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x20, 0x00, /* 0010000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x20, 0x00, /* 0010000000 */ + 0x20, 0x80, /* 0010000010 */ + 0x21, 0x00, /* 0010000100 */ + 0x22, 0x00, /* 0010001000 */ + 0x74, 0x00, /* 0111010000 */ + 0x09, 0x00, /* 0000100100 */ + 0x13, 0x00, /* 0001001100 */ + 0x25, 0x00, /* 0010010100 */ + 0x49, 0x00, /* 0100100100 */ + 0x1f, 0x80, /* 0001111110 */ + 0x01, 0x00, /* 0000000100 */ + 0x01, 0x00, /* 0000000100 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 173 0xad '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 174 0xae '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x0d, 0x80, /* 0000110110 */ + 0x1b, 0x00, /* 0001101100 */ + 0x36, 0x00, /* 0011011000 */ + 0x6c, 0x00, /* 0110110000 */ + 0xd8, 0x00, /* 1101100000 */ + 0x6c, 0x00, /* 0110110000 */ + 0x36, 0x00, /* 0011011000 */ + 0x1b, 0x00, /* 0001101100 */ + 0x0d, 0x80, /* 0000110110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 175 0xaf '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x6c, 0x00, /* 0110110000 */ + 0x36, 0x00, /* 0011011000 */ + 0x1b, 0x00, /* 0001101100 */ + 0x0d, 0x80, /* 0000110110 */ + 0x06, 0xc0, /* 0000011011 */ + 0x0d, 0x80, /* 0000110110 */ + 0x1b, 0x00, /* 0001101100 */ + 0x36, 0x00, /* 0011011000 */ + 0x6c, 0x00, /* 0110110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 176 0xb0 '.' */ + 0xc3, 0x00, /* 1100001100 */ + 0x41, 0x00, /* 0100000100 */ + 0x18, 0x40, /* 0001100001 */ + 0x10, 0x40, /* 0001000001 */ + 0xc3, 0x00, /* 1100001100 */ + 0x41, 0x00, /* 0100000100 */ + 0x18, 0x40, /* 0001100001 */ + 0x10, 0x40, /* 0001000001 */ + 0xc3, 0x00, /* 1100001100 */ + 0x41, 0x00, /* 0100000100 */ + 0x18, 0x40, /* 0001100001 */ + 0x10, 0x40, /* 0001000001 */ + 0xc3, 0x00, /* 1100001100 */ + 0x41, 0x00, /* 0100000100 */ + 0x18, 0x40, /* 0001100001 */ + 0x10, 0x40, /* 0001000001 */ + 0xc3, 0x00, /* 1100001100 */ + 0x41, 0x00, /* 0100000100 */ + + /* 177 0xb1 '.' */ + 0x11, 0x00, /* 0001000100 */ + 0xbb, 0x80, /* 1011101110 */ + 0x11, 0x00, /* 0001000100 */ + 0x44, 0x40, /* 0100010001 */ + 0xee, 0xc0, /* 1110111011 */ + 0x44, 0x40, /* 0100010001 */ + 0x11, 0x00, /* 0001000100 */ + 0xbb, 0x80, /* 1011101110 */ + 0x11, 0x00, /* 0001000100 */ + 0x44, 0x40, /* 0100010001 */ + 0xee, 0xc0, /* 1110111011 */ + 0x44, 0x40, /* 0100010001 */ + 0x11, 0x00, /* 0001000100 */ + 0xbb, 0x80, /* 1011101110 */ + 0x11, 0x00, /* 0001000100 */ + 0x44, 0x40, /* 0100010001 */ + 0xee, 0xc0, /* 1110111011 */ + 0x44, 0x40, /* 0100010001 */ + + /* 178 0xb2 '.' */ + 0x3c, 0xc0, /* 0011110011 */ + 0xbe, 0xc0, /* 1011111011 */ + 0xe7, 0x80, /* 1110011110 */ + 0xef, 0x80, /* 1110111110 */ + 0x3c, 0xc0, /* 0011110011 */ + 0xbe, 0xc0, /* 1011111011 */ + 0xe7, 0x80, /* 1110011110 */ + 0xef, 0x80, /* 1110111110 */ + 0x3c, 0xc0, /* 0011110011 */ + 0xbe, 0xc0, /* 1011111011 */ + 0xe7, 0x80, /* 1110011110 */ + 0xef, 0x80, /* 1110111110 */ + 0x3c, 0xc0, /* 0011110011 */ + 0xbe, 0xc0, /* 1011111011 */ + 0xe7, 0x80, /* 1110011110 */ + 0xef, 0x80, /* 1110111110 */ + 0x3c, 0xc0, /* 0011110011 */ + 0xbe, 0xc0, /* 1011111011 */ + + /* 179 0xb3 '.' */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + + /* 180 0xb4 '.' */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0xfc, 0x00, /* 1111110000 */ + 0xfc, 0x00, /* 1111110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + + /* 181 0xb5 '.' */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0xfc, 0x00, /* 1111110000 */ + 0xfc, 0x00, /* 1111110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0xfc, 0x00, /* 1111110000 */ + 0xfc, 0x00, /* 1111110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + + /* 182 0xb6 '.' */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0xfb, 0x00, /* 1111101100 */ + 0xfb, 0x00, /* 1111101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + + /* 183 0xb7 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0xff, 0x00, /* 1111111100 */ + 0xff, 0x00, /* 1111111100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + + /* 184 0xb8 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0xfc, 0x00, /* 1111110000 */ + 0xfc, 0x00, /* 1111110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0xfc, 0x00, /* 1111110000 */ + 0xfc, 0x00, /* 1111110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + + /* 185 0xb9 '.' */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0xfb, 0x00, /* 1111101100 */ + 0xfb, 0x00, /* 1111101100 */ + 0x03, 0x00, /* 0000001100 */ + 0xfb, 0x00, /* 1111101100 */ + 0xfb, 0x00, /* 1111101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + + /* 186 0xba '.' */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + + /* 187 0xbb '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0xff, 0x00, /* 1111111100 */ + 0xff, 0x00, /* 1111111100 */ + 0x03, 0x00, /* 0000001100 */ + 0xfb, 0x00, /* 1111101100 */ + 0xfb, 0x00, /* 1111101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + + /* 188 0xbc '.' */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0xfb, 0x00, /* 1111101100 */ + 0xfb, 0x00, /* 1111101100 */ + 0x03, 0x00, /* 0000001100 */ + 0xff, 0x00, /* 1111111100 */ + 0xff, 0x00, /* 1111111100 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 189 0xbd '.' */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0xff, 0x00, /* 1111111100 */ + 0xff, 0x00, /* 1111111100 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 190 0xbe '.' */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0xfc, 0x00, /* 1111110000 */ + 0xfc, 0x00, /* 1111110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0xfc, 0x00, /* 1111110000 */ + 0xfc, 0x00, /* 1111110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 191 0xbf '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0xfc, 0x00, /* 1111110000 */ + 0xfc, 0x00, /* 1111110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + + /* 192 0xc0 '.' */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0f, 0xc0, /* 0000111111 */ + 0x0f, 0xc0, /* 0000111111 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 193 0xc1 '.' */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 194 0xc2 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + + /* 195 0xc3 '.' */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0f, 0xc0, /* 0000111111 */ + 0x0f, 0xc0, /* 0000111111 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + + /* 196 0xc4 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 197 0xc5 '.' */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + + /* 198 0xc6 '.' */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0f, 0xc0, /* 0000111111 */ + 0x0f, 0xc0, /* 0000111111 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0f, 0xc0, /* 0000111111 */ + 0x0f, 0xc0, /* 0000111111 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + + /* 199 0xc7 '.' */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0xc0, /* 0001101111 */ + 0x1b, 0xc0, /* 0001101111 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + + /* 200 0xc8 '.' */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0xc0, /* 0001101111 */ + 0x1b, 0xc0, /* 0001101111 */ + 0x18, 0x00, /* 0001100000 */ + 0x1f, 0xc0, /* 0001111111 */ + 0x1f, 0xc0, /* 0001111111 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 201 0xc9 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x1f, 0xc0, /* 0001111111 */ + 0x1f, 0xc0, /* 0001111111 */ + 0x18, 0x00, /* 0001100000 */ + 0x1b, 0xc0, /* 0001101111 */ + 0x1b, 0xc0, /* 0001101111 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + + /* 202 0xca '.' */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0xfb, 0xc0, /* 1111101111 */ + 0xfb, 0xc0, /* 1111101111 */ + 0x00, 0x00, /* 0000000000 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 203 0xcb '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0x00, 0x00, /* 0000000000 */ + 0xfb, 0xc0, /* 1111101111 */ + 0xfb, 0xc0, /* 1111101111 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + + /* 204 0xcc '.' */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0xc0, /* 0001101111 */ + 0x1b, 0xc0, /* 0001101111 */ + 0x18, 0x00, /* 0001100000 */ + 0x1b, 0xc0, /* 0001101111 */ + 0x1b, 0xc0, /* 0001101111 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + + /* 205 0xcd '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0x00, 0x00, /* 0000000000 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 206 0xce '.' */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0xfb, 0xc0, /* 1111101111 */ + 0xfb, 0xc0, /* 1111101111 */ + 0x00, 0x00, /* 0000000000 */ + 0xfb, 0xc0, /* 1111101111 */ + 0xfb, 0xc0, /* 1111101111 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + + /* 207 0xcf '.' */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0x00, 0x00, /* 0000000000 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 208 0xd0 '.' */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 209 0xd1 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0x00, 0x00, /* 0000000000 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + + /* 210 0xd2 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + + /* 211 0xd3 '.' */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1f, 0xc0, /* 0001111111 */ + 0x1f, 0xc0, /* 0001111111 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 212 0xd4 '.' */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0f, 0xc0, /* 0000111111 */ + 0x0f, 0xc0, /* 0000111111 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0f, 0xc0, /* 0000111111 */ + 0x0f, 0xc0, /* 0000111111 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 213 0xd5 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x0f, 0xc0, /* 0000111111 */ + 0x0f, 0xc0, /* 0000111111 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0f, 0xc0, /* 0000111111 */ + 0x0f, 0xc0, /* 0000111111 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + + /* 214 0xd6 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x1f, 0xc0, /* 0001111111 */ + 0x1f, 0xc0, /* 0001111111 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + + /* 215 0xd7 '.' */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + 0x1b, 0x00, /* 0001101100 */ + + /* 216 0xd8 '.' */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0x0c, 0x00, /* 0000110000 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + + /* 217 0xd9 '.' */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0xfc, 0x00, /* 1111110000 */ + 0xfc, 0x00, /* 1111110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 218 0xda '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x0f, 0xc0, /* 0000111111 */ + 0x0f, 0xc0, /* 0000111111 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + + /* 219 0xdb '.' */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + + /* 220 0xdc '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + + /* 221 0xdd '.' */ + 0xf8, 0x00, /* 1111100000 */ + 0xf8, 0x00, /* 1111100000 */ + 0xf8, 0x00, /* 1111100000 */ + 0xf8, 0x00, /* 1111100000 */ + 0xf8, 0x00, /* 1111100000 */ + 0xf8, 0x00, /* 1111100000 */ + 0xf8, 0x00, /* 1111100000 */ + 0xf8, 0x00, /* 1111100000 */ + 0xf8, 0x00, /* 1111100000 */ + 0xf8, 0x00, /* 1111100000 */ + 0xf8, 0x00, /* 1111100000 */ + 0xf8, 0x00, /* 1111100000 */ + 0xf8, 0x00, /* 1111100000 */ + 0xf8, 0x00, /* 1111100000 */ + 0xf8, 0x00, /* 1111100000 */ + 0xf8, 0x00, /* 1111100000 */ + 0xf8, 0x00, /* 1111100000 */ + 0xf8, 0x00, /* 1111100000 */ + + /* 222 0xde '.' */ + 0x07, 0xc0, /* 0000011111 */ + 0x07, 0xc0, /* 0000011111 */ + 0x07, 0xc0, /* 0000011111 */ + 0x07, 0xc0, /* 0000011111 */ + 0x07, 0xc0, /* 0000011111 */ + 0x07, 0xc0, /* 0000011111 */ + 0x07, 0xc0, /* 0000011111 */ + 0x07, 0xc0, /* 0000011111 */ + 0x07, 0xc0, /* 0000011111 */ + 0x07, 0xc0, /* 0000011111 */ + 0x07, 0xc0, /* 0000011111 */ + 0x07, 0xc0, /* 0000011111 */ + 0x07, 0xc0, /* 0000011111 */ + 0x07, 0xc0, /* 0000011111 */ + 0x07, 0xc0, /* 0000011111 */ + 0x07, 0xc0, /* 0000011111 */ + 0x07, 0xc0, /* 0000011111 */ + 0x07, 0xc0, /* 0000011111 */ + + /* 223 0xdf '.' */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0xff, 0xc0, /* 1111111111 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 224 0xe0 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x1c, 0x80, /* 0001110010 */ + 0x35, 0x80, /* 0011010110 */ + 0x63, 0x00, /* 0110001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x63, 0x00, /* 0110001100 */ + 0x37, 0x80, /* 0011011110 */ + 0x1c, 0x80, /* 0001110010 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 225 0xe1 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x1e, 0x00, /* 0001111000 */ + 0x33, 0x00, /* 0011001100 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x63, 0x00, /* 0110001100 */ + 0x6f, 0x00, /* 0110111100 */ + 0x63, 0x00, /* 0110001100 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x63, 0x00, /* 0110001100 */ + 0x6e, 0x00, /* 0110111000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 226 0xe2 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x7f, 0x80, /* 0111111110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 227 0xe3 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x7f, 0x80, /* 0111111110 */ + 0x33, 0x00, /* 0011001100 */ + 0x33, 0x00, /* 0011001100 */ + 0x33, 0x00, /* 0011001100 */ + 0x33, 0x00, /* 0011001100 */ + 0x33, 0x00, /* 0011001100 */ + 0x33, 0x00, /* 0011001100 */ + 0x33, 0x00, /* 0011001100 */ + 0x33, 0x00, /* 0011001100 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 228 0xe4 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0xff, 0x80, /* 1111111110 */ + 0x60, 0x00, /* 0110000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x18, 0x00, /* 0001100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x18, 0x00, /* 0001100000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x80, /* 0011000010 */ + 0x61, 0x80, /* 0110000110 */ + 0xff, 0x80, /* 1111111110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 229 0xe5 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x1f, 0xc0, /* 0001111111 */ + 0x36, 0x00, /* 0011011000 */ + 0x63, 0x00, /* 0110001100 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x33, 0x00, /* 0011001100 */ + 0x3e, 0x00, /* 0011111000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 230 0xe6 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x73, 0x80, /* 0111001110 */ + 0x6d, 0x80, /* 0110110110 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0xc0, 0x00, /* 1100000000 */ + + /* 231 0xe7 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x01, 0x80, /* 0000000110 */ + 0x36, 0x40, /* 0011011001 */ + 0x5e, 0x00, /* 0101111000 */ + 0x8c, 0x00, /* 1000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 232 0xe8 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x7f, 0x80, /* 0111111110 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x1e, 0x00, /* 0001111000 */ + 0x33, 0x00, /* 0011001100 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x33, 0x00, /* 0011001100 */ + 0x1e, 0x00, /* 0001111000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x7f, 0x80, /* 0111111110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 233 0xe9 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x0e, 0x00, /* 0000111000 */ + 0x1f, 0x00, /* 0001111100 */ + 0x31, 0x80, /* 0011000110 */ + 0x31, 0x80, /* 0011000110 */ + 0x60, 0xc0, /* 0110000011 */ + 0x7f, 0xc0, /* 0111111111 */ + 0x7f, 0xc0, /* 0111111111 */ + 0x60, 0xc0, /* 0110000011 */ + 0x31, 0x80, /* 0011000110 */ + 0x31, 0x80, /* 0011000110 */ + 0x1f, 0x00, /* 0001111100 */ + 0x0e, 0x00, /* 0000111000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 234 0xea '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x1e, 0x00, /* 0001111000 */ + 0x33, 0x00, /* 0011001100 */ + 0x61, 0x80, /* 0110000110 */ + 0xc0, 0xc0, /* 1100000011 */ + 0xc0, 0xc0, /* 1100000011 */ + 0xc0, 0xc0, /* 1100000011 */ + 0x61, 0x80, /* 0110000110 */ + 0x33, 0x00, /* 0011001100 */ + 0x33, 0x00, /* 0011001100 */ + 0x33, 0x00, /* 0011001100 */ + 0x33, 0x00, /* 0011001100 */ + 0xf3, 0xc0, /* 1111001111 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 235 0xeb '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x07, 0x00, /* 0000011100 */ + 0x1f, 0x80, /* 0001111110 */ + 0x30, 0xc0, /* 0011000011 */ + 0x30, 0x00, /* 0011000000 */ + 0x18, 0x00, /* 0001100000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x3e, 0x00, /* 0011111000 */ + 0x66, 0x00, /* 0110011000 */ + 0xc3, 0x00, /* 1100001100 */ + 0xc3, 0x00, /* 1100001100 */ + 0xc3, 0x00, /* 1100001100 */ + 0x66, 0x00, /* 0110011000 */ + 0x3c, 0x00, /* 0011110000 */ + 0x18, 0x00, /* 0001100000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 236 0xec '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x33, 0x00, /* 0011001100 */ + 0x6d, 0x80, /* 0110110110 */ + 0xcc, 0xc0, /* 1100110011 */ + 0xcc, 0xc0, /* 1100110011 */ + 0xcc, 0xc0, /* 1100110011 */ + 0xcc, 0xc0, /* 1100110011 */ + 0x6d, 0x80, /* 0110110110 */ + 0x33, 0x00, /* 0011001100 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 237 0xed '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x01, 0x80, /* 0000000110 */ + 0x01, 0x80, /* 0000000110 */ + 0x03, 0x00, /* 0000001100 */ + 0x03, 0x00, /* 0000001100 */ + 0x37, 0x00, /* 0011011100 */ + 0x6d, 0x80, /* 0110110110 */ + 0xcc, 0xc0, /* 1100110011 */ + 0xcc, 0xc0, /* 1100110011 */ + 0xcc, 0xc0, /* 1100110011 */ + 0xcc, 0xc0, /* 1100110011 */ + 0x6d, 0x80, /* 0110110110 */ + 0x3b, 0x00, /* 0011101100 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x60, 0x00, /* 0110000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 238 0xee '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x03, 0x80, /* 0000001110 */ + 0x0e, 0x00, /* 0000111000 */ + 0x18, 0x00, /* 0001100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x3f, 0x80, /* 0011111110 */ + 0x3f, 0x80, /* 0011111110 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x18, 0x00, /* 0001100000 */ + 0x18, 0x00, /* 0001100000 */ + 0x0e, 0x00, /* 0000111000 */ + 0x03, 0x80, /* 0000001110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 239 0xef '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x1e, 0x00, /* 0001111000 */ + 0x33, 0x00, /* 0011001100 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x61, 0x80, /* 0110000110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 240 0xf0 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x7f, 0x80, /* 0111111110 */ + 0x7f, 0x80, /* 0111111110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x7f, 0x80, /* 0111111110 */ + 0x7f, 0x80, /* 0111111110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x7f, 0x80, /* 0111111110 */ + 0x7f, 0x80, /* 0111111110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 241 0xf1 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x7f, 0x80, /* 0111111110 */ + 0x7f, 0x80, /* 0111111110 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x7f, 0x80, /* 0111111110 */ + 0x7f, 0x80, /* 0111111110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 242 0xf2 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0xe0, 0x00, /* 1110000000 */ + 0x38, 0x00, /* 0011100000 */ + 0x0e, 0x00, /* 0000111000 */ + 0x03, 0x80, /* 0000001110 */ + 0x0e, 0x00, /* 0000111000 */ + 0x38, 0x00, /* 0011100000 */ + 0xe0, 0x00, /* 1110000000 */ + 0x00, 0x00, /* 0000000000 */ + 0xff, 0x00, /* 1111111100 */ + 0xff, 0x00, /* 1111111100 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 243 0xf3 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x03, 0x80, /* 0000001110 */ + 0x0e, 0x00, /* 0000111000 */ + 0x38, 0x00, /* 0011100000 */ + 0xe0, 0x00, /* 1110000000 */ + 0x38, 0x00, /* 0011100000 */ + 0x0e, 0x00, /* 0000111000 */ + 0x03, 0x80, /* 0000001110 */ + 0x00, 0x00, /* 0000000000 */ + 0xff, 0x80, /* 1111111110 */ + 0xff, 0x80, /* 1111111110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 244 0xf4 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x1e, 0x00, /* 0001111000 */ + 0x33, 0x00, /* 0011001100 */ + 0x33, 0x00, /* 0011001100 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x30, 0x00, /* 0011000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 245 0xf5 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x1e, 0x00, /* 0001111000 */ + 0x33, 0x00, /* 0011001100 */ + 0x33, 0x00, /* 0011001100 */ + 0x03, 0x00, /* 0000001100 */ + 0x03, 0x00, /* 0000001100 */ + 0x03, 0x00, /* 0000001100 */ + 0x03, 0x00, /* 0000001100 */ + 0x03, 0x00, /* 0000001100 */ + 0x03, 0x00, /* 0000001100 */ + 0x03, 0x00, /* 0000001100 */ + 0x03, 0x00, /* 0000001100 */ + 0x03, 0x00, /* 0000001100 */ + 0x03, 0x00, /* 0000001100 */ + 0x03, 0x00, /* 0000001100 */ + 0x03, 0x00, /* 0000001100 */ + 0x00, 0x00, /* 0000000000 */ + + /* 246 0xf6 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x7f, 0x80, /* 0111111110 */ + 0x7f, 0x80, /* 0111111110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 247 0xf7 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x38, 0x00, /* 0011100000 */ + 0x6c, 0x00, /* 0110110000 */ + 0x06, 0xc0, /* 0000011011 */ + 0x03, 0x80, /* 0000001110 */ + 0x38, 0x00, /* 0011100000 */ + 0x6c, 0x00, /* 0110110000 */ + 0x06, 0xc0, /* 0000011011 */ + 0x03, 0x80, /* 0000001110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 248 0xf8 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x1e, 0x00, /* 0001111000 */ + 0x33, 0x00, /* 0011001100 */ + 0x33, 0x00, /* 0011001100 */ + 0x1e, 0x00, /* 0001111000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 249 0xf9 '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x1e, 0x00, /* 0001111000 */ + 0x1e, 0x00, /* 0001111000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 250 0xfa '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 251 0xfb '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x0f, 0xc0, /* 0000111111 */ + 0x0f, 0xc0, /* 0000111111 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0xcc, 0x00, /* 1100110000 */ + 0x6c, 0x00, /* 0110110000 */ + 0x3c, 0x00, /* 0011110000 */ + 0x1c, 0x00, /* 0001110000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 252 0xfc '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x27, 0x00, /* 0010011100 */ + 0x7b, 0x00, /* 0111101100 */ + 0x31, 0x00, /* 0011000100 */ + 0x31, 0x00, /* 0011000100 */ + 0x31, 0x00, /* 0011000100 */ + 0x7b, 0x80, /* 0111101110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 253 0xfd '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x1e, 0x00, /* 0001111000 */ + 0x3f, 0x00, /* 0011111100 */ + 0x63, 0x00, /* 0110001100 */ + 0x43, 0x00, /* 0100001100 */ + 0x06, 0x00, /* 0000011000 */ + 0x0c, 0x00, /* 0000110000 */ + 0x18, 0x00, /* 0001100000 */ + 0x30, 0x80, /* 0011000010 */ + 0x7f, 0x80, /* 0111111110 */ + 0x7f, 0x80, /* 0111111110 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 254 0xfe '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x3f, 0x00, /* 0011111100 */ + 0x3f, 0x00, /* 0011111100 */ + 0x3f, 0x00, /* 0011111100 */ + 0x3f, 0x00, /* 0011111100 */ + 0x3f, 0x00, /* 0011111100 */ + 0x3f, 0x00, /* 0011111100 */ + 0x3f, 0x00, /* 0011111100 */ + 0x3f, 0x00, /* 0011111100 */ + 0x3f, 0x00, /* 0011111100 */ + 0x3f, 0x00, /* 0011111100 */ + 0x3f, 0x00, /* 0011111100 */ + 0x3f, 0x00, /* 0011111100 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + + /* 255 0xff '.' */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + 0x00, 0x00, /* 0000000000 */ + +}; + + +const struct font_desc font_10x18 = { + .idx = FONT10x18_IDX, + .name = "10x18", + .width = 10, + .height = 18, + .data = fontdata_10x18, +#ifdef __sparc__ + .pref = 5, +#else + .pref = -1, +#endif +}; diff --git a/lib/fonts/font_6x11.c b/lib/fonts/font_6x11.c new file mode 100644 index 000000000000..46e86e67aa6a --- /dev/null +++ b/lib/fonts/font_6x11.c @@ -0,0 +1,3352 @@ +/**********************************************/ +/* */ +/* Font file generated by rthelen */ +/* */ +/**********************************************/ + +#include + +#define FONTDATAMAX (11*256) + +static const unsigned char fontdata_6x11[FONTDATAMAX] = { + + /* 0 0x00 '^@' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 1 0x01 '^A' */ + 0x00, /* 00000000 */ + 0x78, /* 0 000 */ + 0x84, /* 0000 00 */ + 0xcc, /* 00 00 */ + 0x84, /* 0000 00 */ + 0xb4, /* 0 0 00 */ + 0x84, /* 0000 00 */ + 0x78, /* 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 2 0x02 '^B' */ + 0x00, /* 00000000 */ + 0x78, /* 0 000 */ + 0xfc, /* 00 */ + 0xb4, /* 0 0 00 */ + 0xfc, /* 00 */ + 0xcc, /* 00 00 */ + 0xfc, /* 00 */ + 0x78, /* 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 3 0x03 '^C' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x28, /* 00 0 000 */ + 0x7c, /* 0 00 */ + 0x7c, /* 0 00 */ + 0x38, /* 00 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 4 0x04 '^D' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x7c, /* 0 00 */ + 0x38, /* 00 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 5 0x05 '^E' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x38, /* 00 000 */ + 0x6c, /* 0 0 00 */ + 0x6c, /* 0 0 00 */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 6 0x06 '^F' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x7c, /* 0 00 */ + 0x7c, /* 0 00 */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 7 0x07 '^G' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x30, /* 00 0000 */ + 0x78, /* 0 000 */ + 0x30, /* 00 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 8 0x08 '^H' */ + 0xff, /* */ + 0xff, /* */ + 0xff, /* */ + 0xcf, /* 00 */ + 0x87, /* 0000 */ + 0xcf, /* 00 */ + 0xff, /* */ + 0xff, /* */ + 0xff, /* */ + 0xff, /* */ + 0xff, /* */ + + /* 9 0x09 '^I' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x30, /* 00 0000 */ + 0x48, /* 0 00 000 */ + 0x84, /* 0000 00 */ + 0x48, /* 0 00 000 */ + 0x30, /* 00 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 10 0x0a '^J' */ + 0xff, /* */ + 0xff, /* */ + 0xcf, /* 00 */ + 0xb7, /* 0 0 */ + 0x7b, /* 0 0 */ + 0xb7, /* 0 0 */ + 0xcf, /* 00 */ + 0xff, /* */ + 0xff, /* */ + 0xff, /* */ + 0xff, /* */ + + /* 11 0x0b '^K' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x14, /* 000 0 00 */ + 0x20, /* 00 00000 */ + 0x78, /* 0 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 12 0x0c '^L' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x10, /* 000 0000 */ + 0x7c, /* 0 00 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 13 0x0d '^M' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x24, /* 00 00 00 */ + 0x3c, /* 00 00 */ + 0x20, /* 00 00000 */ + 0x20, /* 00 00000 */ + 0xe0, /* 00000 */ + 0xc0, /* 000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 14 0x0e '^N' */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x44, /* 0 000 00 */ + 0x7c, /* 0 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0xcc, /* 00 00 */ + 0xcc, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 15 0x0f '^O' */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x54, /* 0 0 0 00 */ + 0x38, /* 00 000 */ + 0x6c, /* 0 0 00 */ + 0x38, /* 00 000 */ + 0x54, /* 0 0 0 00 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 16 0x10 '^P' */ + 0x00, /* 00000000 */ + 0x40, /* 0 000000 */ + 0x60, /* 0 00000 */ + 0x70, /* 0 0000 */ + 0x7c, /* 0 00 */ + 0x70, /* 0 0000 */ + 0x60, /* 0 00000 */ + 0x40, /* 0 000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 17 0x11 '^Q' */ + 0x00, /* 00000000 */ + 0x04, /* 00000 00 */ + 0x0c, /* 0000 00 */ + 0x1c, /* 000 00 */ + 0x7c, /* 0 00 */ + 0x1c, /* 000 00 */ + 0x0c, /* 0000 00 */ + 0x04, /* 00000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 18 0x12 '^R' */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x54, /* 0 0 0 00 */ + 0x10, /* 000 0000 */ + 0x54, /* 0 0 0 00 */ + 0x38, /* 00 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 19 0x13 '^S' */ + 0x00, /* 00000000 */ + 0x48, /* 0 00 000 */ + 0x48, /* 0 00 000 */ + 0x48, /* 0 00 000 */ + 0x48, /* 0 00 000 */ + 0x48, /* 0 00 000 */ + 0x00, /* 00000000 */ + 0x48, /* 0 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 20 0x14 '^T' */ + 0x3c, /* 00 00 */ + 0x54, /* 0 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x3c, /* 00 00 */ + 0x14, /* 000 0 00 */ + 0x14, /* 000 0 00 */ + 0x14, /* 000 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 21 0x15 '^U' */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x24, /* 00 00 00 */ + 0x50, /* 0 0 0000 */ + 0x48, /* 0 00 000 */ + 0x24, /* 00 00 00 */ + 0x14, /* 000 0 00 */ + 0x48, /* 0 00 000 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + + /* 22 0x16 '^V' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xf8, /* 000 */ + 0xf8, /* 000 */ + 0xf8, /* 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 23 0x17 '^W' */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x54, /* 0 0 0 00 */ + 0x10, /* 000 0000 */ + 0x54, /* 0 0 0 00 */ + 0x38, /* 00 000 */ + 0x10, /* 000 0000 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 24 0x18 '^X' */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x54, /* 0 0 0 00 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 25 0x19 '^Y' */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x54, /* 0 0 0 00 */ + 0x38, /* 00 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 26 0x1a '^Z' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x7c, /* 0 00 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 27 0x1b '^[' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x7c, /* 0 00 */ + 0x20, /* 00 00000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 28 0x1c '^\' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x78, /* 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 29 0x1d '^]' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x48, /* 0 00 000 */ + 0x84, /* 0000 00 */ + 0xfc, /* 00 */ + 0x84, /* 0000 00 */ + 0x48, /* 0 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 30 0x1e '^^' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x38, /* 00 000 */ + 0x7c, /* 0 00 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 31 0x1f '^`' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x7c, /* 0 00 */ + 0x38, /* 00 000 */ + 0x38, /* 00 000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 32 0x20 ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 33 0x21 '!' */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 34 0x22 '"' */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 35 0x23 '#' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x28, /* 00 0 000 */ + 0x7c, /* 0 00 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x7c, /* 0 00 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 36 0x24 '$' */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x54, /* 0 0 0 00 */ + 0x50, /* 0 0 0000 */ + 0x38, /* 00 000 */ + 0x14, /* 000 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x38, /* 00 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 37 0x25 '%' */ + 0x00, /* 00000000 */ + 0x64, /* 0 00 00 */ + 0x64, /* 0 00 00 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x4c, /* 0 00 00 */ + 0x4c, /* 0 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 38 0x26 '&' */ + 0x00, /* 00000000 */ + 0x30, /* 00 0000 */ + 0x48, /* 0 00 000 */ + 0x50, /* 0 0 0000 */ + 0x20, /* 00 00000 */ + 0x54, /* 0 0 0 00 */ + 0x48, /* 0 00 000 */ + 0x34, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 39 0x27 ''' */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 40 0x28 '(' */ + 0x04, /* 00000 00 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x04, /* 00000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 41 0x29 ')' */ + 0x20, /* 00 00000 */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 42 0x2a '*' */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x54, /* 0 0 0 00 */ + 0x38, /* 00 000 */ + 0x54, /* 0 0 0 00 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 43 0x2b '+' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x7c, /* 0 00 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 44 0x2c ',' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x30, /* 00 0000 */ + 0x30, /* 00 0000 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x00, /* 00000000 */ + + /* 45 0x2d '-' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 46 0x2e '.' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 000 000 */ + 0x18, /* 000 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 47 0x2f '/' */ + 0x04, /* 00000 00 */ + 0x04, /* 00000 00 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x20, /* 00 00000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x00, /* 00000000 */ + + /* 48 0x30 '0' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x54, /* 0 0 0 00 */ + 0x64, /* 0 00 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 49 0x31 '1' */ + 0x00, /* 00000000 */ + 0x08, /* 0000 000 */ + 0x18, /* 000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x1c, /* 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 50 0x32 '2' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x04, /* 00000 00 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 51 0x33 '3' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x04, /* 00000 00 */ + 0x18, /* 000 000 */ + 0x04, /* 00000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 52 0x34 '4' */ + 0x00, /* 00000000 */ + 0x08, /* 0000 000 */ + 0x18, /* 000 000 */ + 0x28, /* 00 0 000 */ + 0x48, /* 0 00 000 */ + 0x7c, /* 0 00 */ + 0x08, /* 0000 000 */ + 0x1c, /* 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 53 0x35 '5' */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x40, /* 0 000000 */ + 0x78, /* 0 000 */ + 0x04, /* 00000 00 */ + 0x04, /* 00000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 54 0x36 '6' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x40, /* 0 000000 */ + 0x78, /* 0 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 55 0x37 '7' */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x04, /* 00000 00 */ + 0x04, /* 00000 00 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 56 0x38 '8' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 57 0x39 '9' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x3c, /* 00 00 */ + 0x04, /* 00000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 58 0x3a ':' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 000 000 */ + 0x18, /* 000 000 */ + 0x00, /* 00000000 */ + 0x18, /* 000 000 */ + 0x18, /* 000 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 59 0x3b ';' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x30, /* 00 0000 */ + 0x30, /* 00 0000 */ + 0x00, /* 00000000 */ + 0x30, /* 00 0000 */ + 0x30, /* 00 0000 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x00, /* 00000000 */ + + /* 60 0x3c '<' */ + 0x00, /* 00000000 */ + 0x04, /* 00000 00 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x04, /* 00000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 61 0x3d '=' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 62 0x3e '>' */ + 0x00, /* 00000000 */ + 0x20, /* 00 00000 */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x04, /* 00000 00 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 63 0x3f '?' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x04, /* 00000 00 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 64 0x40 '@' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x74, /* 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x78, /* 0 000 */ + 0x40, /* 0 000000 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 65 0x41 'A' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x7c, /* 0 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 66 0x42 'B' */ + 0x00, /* 00000000 */ + 0x78, /* 0 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x78, /* 0 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x78, /* 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 67 0x43 'C' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 68 0x44 'D' */ + 0x00, /* 00000000 */ + 0x78, /* 0 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x78, /* 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 69 0x45 'E' */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x78, /* 0 000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 70 0x46 'F' */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x78, /* 0 000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 71 0x47 'G' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x40, /* 0 000000 */ + 0x4c, /* 0 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 72 0x48 'H' */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x7c, /* 0 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 73 0x49 'I' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 74 0x4a 'J' */ + 0x00, /* 00000000 */ + 0x04, /* 00000 00 */ + 0x04, /* 00000 00 */ + 0x04, /* 00000 00 */ + 0x04, /* 00000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 75 0x4b 'K' */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x48, /* 0 00 000 */ + 0x50, /* 0 0 0000 */ + 0x60, /* 0 00000 */ + 0x50, /* 0 0 0000 */ + 0x48, /* 0 00 000 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 76 0x4c 'L' */ + 0x00, /* 00000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 77 0x4d 'M' */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x6c, /* 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 78 0x4e 'N' */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x64, /* 0 00 00 */ + 0x54, /* 0 0 0 00 */ + 0x4c, /* 0 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 79 0x4f 'O' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 80 0x50 'P' */ + 0x00, /* 00000000 */ + 0x78, /* 0 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x78, /* 0 000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 81 0x51 'Q' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x54, /* 0 0 0 00 */ + 0x38, /* 00 000 */ + 0x04, /* 00000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 82 0x52 'R' */ + 0x00, /* 00000000 */ + 0x78, /* 0 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x78, /* 0 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 83 0x53 'S' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x40, /* 0 000000 */ + 0x38, /* 00 000 */ + 0x04, /* 00000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 84 0x54 'T' */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 85 0x55 'U' */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 86 0x56 'V' */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x28, /* 00 0 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 87 0x57 'W' */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x54, /* 0 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x6c, /* 0 0 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 88 0x58 'X' */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x28, /* 00 0 000 */ + 0x10, /* 000 0000 */ + 0x28, /* 00 0 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 89 0x59 'Y' */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x28, /* 00 0 000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 90 0x5a 'Z' */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x04, /* 00000 00 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x40, /* 0 000000 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 91 0x5b '[' */ + 0x0c, /* 0000 00 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x0c, /* 0000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 92 0x5c '\' */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x20, /* 00 00000 */ + 0x20, /* 00 00000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x04, /* 00000 00 */ + 0x04, /* 00000 00 */ + 0x00, /* 00000000 */ + + /* 93 0x5d ']' */ + 0x30, /* 00 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x30, /* 00 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 94 0x5e '^' */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x28, /* 00 0 000 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 95 0x5f '_' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfc, /* 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 96 0x60 '`' */ + 0x20, /* 00 00000 */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 97 0x61 'a' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x34, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 98 0x62 'b' */ + 0x00, /* 00000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x78, /* 0 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x78, /* 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 99 0x63 'c' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x40, /* 0 000000 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 100 0x64 'd' */ + 0x00, /* 00000000 */ + 0x04, /* 00000 00 */ + 0x04, /* 00000 00 */ + 0x3c, /* 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 101 0x65 'e' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x7c, /* 0 00 */ + 0x40, /* 0 000000 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 102 0x66 'f' */ + 0x00, /* 00000000 */ + 0x0c, /* 0000 00 */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 103 0x67 'g' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x3c, /* 00 00 */ + 0x04, /* 00000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + + /* 104 0x68 'h' */ + 0x00, /* 00000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x78, /* 0 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 105 0x69 'i' */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x30, /* 00 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 106 0x6a 'j' */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x30, /* 00 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x60, /* 0 00000 */ + 0x00, /* 00000000 */ + + /* 107 0x6b 'k' */ + 0x00, /* 00000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x48, /* 0 00 000 */ + 0x50, /* 0 0 0000 */ + 0x70, /* 0 0000 */ + 0x48, /* 0 00 000 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 108 0x6c 'l' */ + 0x00, /* 00000000 */ + 0x30, /* 00 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 109 0x6d 'm' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x78, /* 0 000 */ + 0x54, /* 0 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 110 0x6e 'n' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x58, /* 0 0 000 */ + 0x64, /* 0 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 111 0x6f 'o' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 112 0x70 'p' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x78, /* 0 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x78, /* 0 000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x00, /* 00000000 */ + + /* 113 0x71 'q' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x3c, /* 00 00 */ + 0x04, /* 00000 00 */ + 0x04, /* 00000 00 */ + 0x00, /* 00000000 */ + + /* 114 0x72 'r' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x58, /* 0 0 000 */ + 0x64, /* 0 00 00 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 115 0x73 's' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x40, /* 0 000000 */ + 0x38, /* 00 000 */ + 0x04, /* 00000 00 */ + 0x78, /* 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 116 0x74 't' */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x0c, /* 0000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 117 0x75 'u' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x34, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 118 0x76 'v' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x28, /* 00 0 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 119 0x77 'w' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x54, /* 0 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 120 0x78 'x' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x28, /* 00 0 000 */ + 0x10, /* 000 0000 */ + 0x28, /* 00 0 000 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 121 0x79 'y' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x3c, /* 00 00 */ + 0x04, /* 00000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + + /* 122 0x7a 'z' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 123 0x7b '{' */ + 0x04, /* 00000 00 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x04, /* 00000 00 */ + + /* 124 0x7c '|' */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 125 0x7d '}' */ + 0x20, /* 00 00000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + + /* 126 0x7e '~' */ + 0x00, /* 00000000 */ + 0x34, /* 00 0 00 */ + 0x58, /* 0 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 127 0x7f '^?' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x28, /* 00 0 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 128 0x80 '\200' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x00, /* 00000000 */ + + /* 129 0x81 '\201' */ + 0x00, /* 00000000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x34, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 130 0x82 '\202' */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x7c, /* 0 00 */ + 0x40, /* 0 000000 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 131 0x83 '\203' */ + 0x10, /* 000 0000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x34, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 132 0x84 '\204' */ + 0x00, /* 00000000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x34, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 133 0x85 '\205' */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x34, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 134 0x86 '\206' */ + 0x18, /* 000 000 */ + 0x24, /* 00 00 00 */ + 0x18, /* 000 000 */ + 0x3c, /* 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x34, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 135 0x87 '\207' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x3c, /* 00 00 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x00, /* 00000000 */ + + /* 136 0x88 '\210' */ + 0x10, /* 000 0000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x7c, /* 0 00 */ + 0x40, /* 0 000000 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 137 0x89 '\211' */ + 0x00, /* 00000000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x7c, /* 0 00 */ + 0x40, /* 0 000000 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 138 0x8a '\212' */ + 0x20, /* 00 00000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x7c, /* 0 00 */ + 0x40, /* 0 000000 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 139 0x8b '\213' */ + 0x00, /* 00000000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 140 0x8c '\214' */ + 0x10, /* 000 0000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 141 0x8d '\215' */ + 0x20, /* 00 00000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 142 0x8e '\216' */ + 0x84, /* 0000 00 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x7c, /* 0 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 143 0x8f '\217' */ + 0x58, /* 0 0 000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x7c, /* 0 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 144 0x90 '\220' */ + 0x10, /* 000 0000 */ + 0x7c, /* 0 00 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x78, /* 0 000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 145 0x91 '\221' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x54, /* 0 0 0 00 */ + 0x5c, /* 0 0 00 */ + 0x50, /* 0 0 0000 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 146 0x92 '\222' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x50, /* 0 0 0000 */ + 0x50, /* 0 0 0000 */ + 0x78, /* 0 000 */ + 0x50, /* 0 0 0000 */ + 0x50, /* 0 0 0000 */ + 0x5c, /* 0 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 147 0x93 '\223' */ + 0x10, /* 000 0000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 148 0x94 '\224' */ + 0x00, /* 00000000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 149 0x95 '\225' */ + 0x20, /* 00 00000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 150 0x96 '\226' */ + 0x10, /* 000 0000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x34, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 151 0x97 '\227' */ + 0x20, /* 00 00000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x34, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 152 0x98 '\230' */ + 0x00, /* 00000000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x3c, /* 00 00 */ + 0x04, /* 00000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + + /* 153 0x99 '\231' */ + 0x84, /* 0000 00 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 154 0x9a '\232' */ + 0x88, /* 000 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 155 0x9b '\233' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x54, /* 0 0 0 00 */ + 0x50, /* 0 0 0000 */ + 0x54, /* 0 0 0 00 */ + 0x38, /* 00 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 156 0x9c '\234' */ + 0x30, /* 00 0000 */ + 0x48, /* 0 00 000 */ + 0x40, /* 0 000000 */ + 0x70, /* 0 0000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x44, /* 0 000 00 */ + 0x78, /* 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 157 0x9d '\235' */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x28, /* 00 0 000 */ + 0x7c, /* 0 00 */ + 0x10, /* 000 0000 */ + 0x7c, /* 0 00 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 158 0x9e '\236' */ + 0x00, /* 00000000 */ + 0x70, /* 0 0000 */ + 0x48, /* 0 00 000 */ + 0x70, /* 0 0000 */ + 0x48, /* 0 00 000 */ + 0x5c, /* 0 0 00 */ + 0x48, /* 0 00 000 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 159 0x9f '\237' */ + 0x00, /* 00000000 */ + 0x0c, /* 0000 00 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x60, /* 0 00000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 160 0xa0 '\240' */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x34, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 161 0xa1 '\241' */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 162 0xa2 '\242' */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 163 0xa3 '\243' */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x34, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 164 0xa4 '\244' */ + 0x34, /* 00 0 00 */ + 0x58, /* 0 0 000 */ + 0x00, /* 00000000 */ + 0x58, /* 0 0 000 */ + 0x64, /* 0 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 165 0xa5 '\245' */ + 0x58, /* 0 0 000 */ + 0x44, /* 0 000 00 */ + 0x64, /* 0 00 00 */ + 0x54, /* 0 0 0 00 */ + 0x4c, /* 0 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 166 0xa6 '\246' */ + 0x00, /* 00000000 */ + 0x1c, /* 000 00 */ + 0x24, /* 00 00 00 */ + 0x24, /* 00 00 00 */ + 0x1c, /* 000 00 */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 167 0xa7 '\247' */ + 0x00, /* 00000000 */ + 0x18, /* 000 000 */ + 0x24, /* 00 00 00 */ + 0x24, /* 00 00 00 */ + 0x18, /* 000 000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 168 0xa8 '\250' */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x40, /* 0 000000 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 169 0xa9 '\251' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 170 0xaa '\252' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x04, /* 00000 00 */ + 0x04, /* 00000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 171 0xab '\253' */ + 0x20, /* 00 00000 */ + 0x60, /* 0 00000 */ + 0x24, /* 00 00 00 */ + 0x28, /* 00 0 000 */ + 0x10, /* 000 0000 */ + 0x28, /* 00 0 000 */ + 0x44, /* 0 000 00 */ + 0x08, /* 0000 000 */ + 0x1c, /* 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 172 0xac '\254' */ + 0x20, /* 00 00000 */ + 0x60, /* 0 00000 */ + 0x24, /* 00 00 00 */ + 0x28, /* 00 0 000 */ + 0x10, /* 000 0000 */ + 0x28, /* 00 0 000 */ + 0x58, /* 0 0 000 */ + 0x3c, /* 00 00 */ + 0x08, /* 0000 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 173 0xad '\255' */ + 0x00, /* 00000000 */ + 0x08, /* 0000 000 */ + 0x00, /* 00000000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 174 0xae '\256' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x24, /* 00 00 00 */ + 0x48, /* 0 00 000 */ + 0x48, /* 0 00 000 */ + 0x24, /* 00 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 175 0xaf '\257' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x48, /* 0 00 000 */ + 0x24, /* 00 00 00 */ + 0x24, /* 00 00 00 */ + 0x48, /* 0 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 176 0xb0 '\260' */ + 0x11, /* 000 000 */ + 0x44, /* 0 000 00 */ + 0x11, /* 000 000 */ + 0x44, /* 0 000 00 */ + 0x11, /* 000 000 */ + 0x44, /* 0 000 00 */ + 0x11, /* 000 000 */ + 0x44, /* 0 000 00 */ + 0x11, /* 000 000 */ + 0x44, /* 0 000 00 */ + 0x11, /* 000 000 */ + + /* 177 0xb1 '\261' */ + 0x55, /* 0 0 0 0 */ + 0xaa, /* 0 0 0 0 */ + 0x55, /* 0 0 0 0 */ + 0xaa, /* 0 0 0 0 */ + 0x55, /* 0 0 0 0 */ + 0xaa, /* 0 0 0 0 */ + 0x55, /* 0 0 0 0 */ + 0xaa, /* 0 0 0 0 */ + 0x55, /* 0 0 0 0 */ + 0xaa, /* 0 0 0 0 */ + 0x55, /* 0 0 0 0 */ + + /* 178 0xb2 '\262' */ + 0xdd, /* 0 0 */ + 0x77, /* 0 0 */ + 0xdd, /* 0 0 */ + 0x77, /* 0 0 */ + 0xdd, /* 0 0 */ + 0x77, /* 0 0 */ + 0xdd, /* 0 0 */ + 0x77, /* 0 0 */ + 0xdd, /* 0 0 */ + 0x77, /* 0 0 */ + 0xdd, /* 0 0 */ + + /* 179 0xb3 '\263' */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + + /* 180 0xb4 '\264' */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0xf0, /* 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + + /* 181 0xb5 '\265' */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0xf0, /* 0000 */ + 0x10, /* 000 0000 */ + 0xf0, /* 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + + /* 182 0xb6 '\266' */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0xe8, /* 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + + /* 183 0xb7 '\267' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xf8, /* 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + + /* 184 0xb8 '\270' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xf0, /* 0000 */ + 0x10, /* 000 0000 */ + 0xf0, /* 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + + /* 185 0xb9 '\271' */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0xe8, /* 0 000 */ + 0x08, /* 0000 000 */ + 0xe8, /* 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + + /* 186 0xba '\272' */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + + /* 187 0xbb '\273' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xf8, /* 000 */ + 0x08, /* 0000 000 */ + 0xe8, /* 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + + /* 188 0xbc '\274' */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0xe8, /* 0 000 */ + 0x08, /* 0000 000 */ + 0xf8, /* 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 189 0xbd '\275' */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0xf8, /* 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 190 0xbe '\276' */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0xf0, /* 0000 */ + 0x10, /* 000 0000 */ + 0xf0, /* 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 191 0xbf '\277' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xf0, /* 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + + /* 192 0xc0 '\300' */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x1c, /* 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 193 0xc1 '\301' */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0xfc, /* 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 194 0xc2 '\302' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfc, /* 00 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + + /* 195 0xc3 '\303' */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x1c, /* 000 00 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + + /* 196 0xc4 '\304' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfc, /* 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 197 0xc5 '\305' */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0xfc, /* 00 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + + /* 198 0xc6 '\306' */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x1c, /* 000 00 */ + 0x10, /* 000 0000 */ + 0x1c, /* 000 00 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + + /* 199 0xc7 '\307' */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x2c, /* 00 0 00 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + + /* 200 0xc8 '\310' */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x2c, /* 00 0 00 */ + 0x20, /* 00 00000 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 201 0xc9 '\311' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x20, /* 00 00000 */ + 0x2c, /* 00 0 00 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + + /* 202 0xca '\312' */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0xec, /* 0 00 */ + 0x00, /* 00000000 */ + 0xfc, /* 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 203 0xcb '\313' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfc, /* 00 */ + 0x00, /* 00000000 */ + 0xec, /* 0 00 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + + /* 204 0xcc '\314' */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x2c, /* 00 0 00 */ + 0x20, /* 00 00000 */ + 0x2c, /* 00 0 00 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + + /* 205 0xcd '\315' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfc, /* 00 */ + 0x00, /* 00000000 */ + 0xfc, /* 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 206 0xce '\316' */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0xec, /* 0 00 */ + 0x00, /* 00000000 */ + 0xec, /* 0 00 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + + /* 207 0xcf '\317' */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0xfc, /* 00 */ + 0x00, /* 00000000 */ + 0xfc, /* 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 208 0xd0 '\320' */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0xfc, /* 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 209 0xd1 '\321' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfc, /* 00 */ + 0x00, /* 00000000 */ + 0xfc, /* 00 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + + /* 210 0xd2 '\322' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfc, /* 00 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + + /* 211 0xd3 '\323' */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 212 0xd4 '\324' */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x1c, /* 000 00 */ + 0x10, /* 000 0000 */ + 0x1c, /* 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 213 0xd5 '\325' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1c, /* 000 00 */ + 0x10, /* 000 0000 */ + 0x1c, /* 000 00 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + + /* 214 0xd6 '\326' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + + /* 215 0xd7 '\327' */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0xfc, /* 00 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + + /* 216 0xd8 '\330' */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0xfc, /* 00 */ + 0x10, /* 000 0000 */ + 0xfc, /* 00 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + + /* 217 0xd9 '\331' */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0xf0, /* 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 218 0xda '\332' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1f, /* 000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + + /* 219 0xdb '\333' */ + 0xfc, /* 00 */ + 0xfc, /* 00 */ + 0xfc, /* 00 */ + 0xfc, /* 00 */ + 0xfc, /* 00 */ + 0xfc, /* 00 */ + 0xfc, /* 00 */ + 0xfc, /* 00 */ + 0xfc, /* 00 */ + 0xfc, /* 00 */ + 0xfc, /* 00 */ + + /* 220 0xdc '\334' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfc, /* 00 */ + 0xfc, /* 00 */ + 0xfc, /* 00 */ + 0xfc, /* 00 */ + 0xfc, /* 00 */ + 0xfc, /* 00 */ + + /* 221 0xdd '\335' */ + 0xe0, /* 00000 */ + 0xe0, /* 00000 */ + 0xe0, /* 00000 */ + 0xe0, /* 00000 */ + 0xe0, /* 00000 */ + 0xe0, /* 00000 */ + 0xe0, /* 00000 */ + 0xe0, /* 00000 */ + 0xe0, /* 00000 */ + 0xe0, /* 00000 */ + 0xe0, /* 00000 */ + + /* 222 0xde '\336' */ + 0x1c, /* 000 00 */ + 0x1c, /* 000 00 */ + 0x1c, /* 000 00 */ + 0x1c, /* 000 00 */ + 0x1c, /* 000 00 */ + 0x1c, /* 000 00 */ + 0x1c, /* 000 00 */ + 0x1c, /* 000 00 */ + 0x1c, /* 000 00 */ + 0x1c, /* 000 00 */ + 0x1c, /* 000 00 */ + + /* 223 0xdf '\337' */ + 0xfc, /* 00 */ + 0xfc, /* 00 */ + 0xfc, /* 00 */ + 0xfc, /* 00 */ + 0xfc, /* 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 224 0xe0 '\340' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x24, /* 00 00 00 */ + 0x58, /* 0 0 000 */ + 0x50, /* 0 0 0000 */ + 0x54, /* 0 0 0 00 */ + 0x2c, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 225 0xe1 '\341' */ + 0x18, /* 000 000 */ + 0x24, /* 00 00 00 */ + 0x44, /* 0 000 00 */ + 0x48, /* 0 00 000 */ + 0x48, /* 0 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x58, /* 0 0 000 */ + 0x40, /* 0 000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 226 0xe2 '\342' */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 227 0xe3 '\343' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 228 0xe4 '\344' */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x24, /* 00 00 00 */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x24, /* 00 00 00 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 229 0xe5 '\345' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x48, /* 0 00 000 */ + 0x48, /* 0 00 000 */ + 0x48, /* 0 00 000 */ + 0x30, /* 00 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 230 0xe6 '\346' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x48, /* 0 00 000 */ + 0x48, /* 0 00 000 */ + 0x48, /* 0 00 000 */ + 0x48, /* 0 00 000 */ + 0x74, /* 0 0 00 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x00, /* 00000000 */ + + /* 231 0xe7 '\347' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x6c, /* 0 0 00 */ + 0x98, /* 00 000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 232 0xe8 '\350' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 233 0xe9 '\351' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x4c, /* 0 00 00 */ + 0x54, /* 0 0 0 00 */ + 0x64, /* 0 00 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 234 0xea '\352' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x28, /* 00 0 000 */ + 0x6c, /* 0 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 235 0xeb '\353' */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x0c, /* 0000 00 */ + 0x14, /* 000 0 00 */ + 0x24, /* 00 00 00 */ + 0x24, /* 00 00 00 */ + 0x18, /* 000 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 236 0xec '\354' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x54, /* 0 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 237 0xed '\355' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x04, /* 00000 00 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x40, /* 0 000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 238 0xee '\356' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x78, /* 0 000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 239 0xef '\357' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 240 0xf0 '\360' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfc, /* 00 */ + 0x00, /* 00000000 */ + 0xfc, /* 00 */ + 0x00, /* 00000000 */ + 0xfc, /* 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 241 0xf1 '\361' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x7c, /* 0 00 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 242 0xf2 '\362' */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x04, /* 00000 00 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x1c, /* 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 243 0xf3 '\363' */ + 0x00, /* 00000000 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 244 0xf4 '\364' */ + 0x00, /* 00000000 */ + 0x0c, /* 0000 00 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + + /* 245 0xf5 '\365' */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x60, /* 0 00000 */ + 0x00, /* 00000000 */ + + /* 246 0xf6 '\366' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 247 0xf7 '\367' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x34, /* 00 0 00 */ + 0x48, /* 0 00 000 */ + 0x00, /* 00000000 */ + 0x34, /* 00 0 00 */ + 0x48, /* 0 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 248 0xf8 '\370' */ + 0x18, /* 000 000 */ + 0x24, /* 00 00 00 */ + 0x24, /* 00 00 00 */ + 0x18, /* 000 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 249 0xf9 '\371' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 250 0xfa '\372' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 251 0xfb '\373' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x0c, /* 0000 00 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x50, /* 0 0 0000 */ + 0x20, /* 00 00000 */ + 0x20, /* 00 00000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 252 0xfc '\374' */ + 0x00, /* 00000000 */ + 0x50, /* 0 0 0000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 253 0xfd '\375' */ + 0x00, /* 00000000 */ + 0x70, /* 0 0000 */ + 0x08, /* 0000 000 */ + 0x20, /* 00 00000 */ + 0x78, /* 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 254 0xfe '\376' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x38, /* 00 000 */ + 0x38, /* 00 000 */ + 0x38, /* 00 000 */ + 0x38, /* 00 000 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 255 0xff '\377' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + +}; + + +const struct font_desc font_vga_6x11 = { + .idx = VGA6x11_IDX, + .name = "ProFont6x11", + .width = 6, + .height = 11, + .data = fontdata_6x11, + /* Try avoiding this font if possible unless on MAC */ + .pref = -2000, +}; diff --git a/lib/fonts/font_7x14.c b/lib/fonts/font_7x14.c new file mode 100644 index 000000000000..3b7dbf9c060b --- /dev/null +++ b/lib/fonts/font_7x14.c @@ -0,0 +1,4118 @@ +/**************************************/ +/* this file adapted from font_8x16.c */ +/* by Jurriaan Kalkman 05-2005 */ +/**************************************/ + +#include + +#define FONTDATAMAX 3584 + +static const unsigned char fontdata_7x14[FONTDATAMAX] = { + + /* 0 0x00 '^@' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 1 0x01 '^A' */ + 0x00, /* 0000000 */ + 0x7c, /* 0111110 */ + 0x82, /* 1000001 */ + 0xaa, /* 1010101 */ + 0x82, /* 1000001 */ + 0x82, /* 1000001 */ + 0xba, /* 1011101 */ + 0x92, /* 1001001 */ + 0x82, /* 1000001 */ + 0x7c, /* 0111110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 2 0x02 '^B' */ + 0x00, /* 0000000 */ + 0x7c, /* 0111110 */ + 0xfe, /* 1111111 */ + 0xd6, /* 1101011 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0xc6, /* 1100011 */ + 0xee, /* 1110111 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0x7c, /* 0111110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 3 0x03 '^C' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x6c, /* 0110110 */ + 0x7c, /* 0111110 */ + 0xfe, /* 1111111 */ + 0x7c, /* 0111110 */ + 0x38, /* 0011100 */ + 0x18, /* 0001100 */ + 0x10, /* 0001000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 4 0x04 '^D' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x10, /* 0001000 */ + 0x38, /* 0011100 */ + 0x7c, /* 0111110 */ + 0xfe, /* 1111111 */ + 0x7c, /* 0111110 */ + 0x38, /* 0011100 */ + 0x10, /* 0001000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 5 0x05 '^E' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x38, /* 0011100 */ + 0x38, /* 0011100 */ + 0x38, /* 0011100 */ + 0xee, /* 1110111 */ + 0xee, /* 1110111 */ + 0xee, /* 1110111 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x38, /* 0011100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 6 0x06 '^F' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x10, /* 0001000 */ + 0x38, /* 0011100 */ + 0x7c, /* 0111110 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0x7c, /* 0111110 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x38, /* 0011100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 7 0x07 '^G' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x3c, /* 0011110 */ + 0x3c, /* 0011110 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 8 0x08 '^H' */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0xe6, /* 1110011 */ + 0xc2, /* 1100001 */ + 0xc2, /* 1100001 */ + 0xe6, /* 1110011 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + + /* 9 0x09 '^I' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x38, /* 0011100 */ + 0x6c, /* 0110110 */ + 0x44, /* 0100010 */ + 0x6c, /* 0110110 */ + 0x38, /* 0011100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 10 0x0a '^J' */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0xc6, /* 1100011 */ + 0x92, /* 1001001 */ + 0xba, /* 1011101 */ + 0x92, /* 1001001 */ + 0xc6, /* 1100011 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + + /* 11 0x0b '^K' */ + 0x00, /* 0000000 */ + 0x1e, /* 0001111 */ + 0x0e, /* 0000111 */ + 0x1a, /* 0001101 */ + 0x1a, /* 0001101 */ + 0x78, /* 0111100 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x78, /* 0111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 12 0x0c '^L' */ + 0x00, /* 0000000 */ + 0x3c, /* 0011110 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x3c, /* 0011110 */ + 0x18, /* 0001100 */ + 0x7e, /* 0111111 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 13 0x0d '^M' */ + 0x00, /* 0000000 */ + 0x3e, /* 0011111 */ + 0x36, /* 0011011 */ + 0x3e, /* 0011111 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x70, /* 0111000 */ + 0xf0, /* 1111000 */ + 0xe0, /* 1110000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 14 0x0e '^N' */ + 0x00, /* 0000000 */ + 0x7e, /* 0111111 */ + 0x66, /* 0110011 */ + 0x7e, /* 0111111 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x66, /* 0110011 */ + 0x6e, /* 0110111 */ + 0xee, /* 1110111 */ + 0xec, /* 1110110 */ + 0xc0, /* 1100000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 15 0x0f '^O' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0xd6, /* 1101011 */ + 0x38, /* 0011100 */ + 0xee, /* 1110111 */ + 0x38, /* 0011100 */ + 0xd6, /* 1101011 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 16 0x10 '^P' */ + 0x00, /* 0000000 */ + 0x80, /* 1000000 */ + 0xc0, /* 1100000 */ + 0xe0, /* 1110000 */ + 0xf0, /* 1111000 */ + 0xfc, /* 1111110 */ + 0xf0, /* 1111000 */ + 0xe0, /* 1110000 */ + 0xc0, /* 1100000 */ + 0x80, /* 1000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 17 0x11 '^Q' */ + 0x00, /* 0000000 */ + 0x04, /* 0000010 */ + 0x0c, /* 0000110 */ + 0x1c, /* 0001110 */ + 0x3c, /* 0011110 */ + 0xfc, /* 1111110 */ + 0x3c, /* 0011110 */ + 0x1c, /* 0001110 */ + 0x0c, /* 0000110 */ + 0x04, /* 0000010 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 18 0x12 '^R' */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x3c, /* 0011110 */ + 0x7e, /* 0111111 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x7e, /* 0111111 */ + 0x3c, /* 0011110 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 19 0x13 '^S' */ + 0x00, /* 0000000 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x00, /* 0000000 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 20 0x14 '^T' */ + 0x00, /* 0000000 */ + 0x7e, /* 0111111 */ + 0xd4, /* 1101010 */ + 0xd4, /* 1101010 */ + 0xd4, /* 1101010 */ + 0x74, /* 0111010 */ + 0x14, /* 0001010 */ + 0x14, /* 0001010 */ + 0x14, /* 0001010 */ + 0x14, /* 0001010 */ + 0x16, /* 0001011 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 21 0x15 '^U' */ + 0x78, /* 0111100 */ + 0xcc, /* 1100110 */ + 0x60, /* 0110000 */ + 0x38, /* 0011100 */ + 0x6c, /* 0110110 */ + 0xc6, /* 1100011 */ + 0xc6, /* 1100011 */ + 0x6c, /* 0110110 */ + 0x38, /* 0011100 */ + 0x18, /* 0001100 */ + 0xcc, /* 1100110 */ + 0x78, /* 0111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 22 0x16 '^V' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xfc, /* 1111110 */ + 0xfc, /* 1111110 */ + 0xfc, /* 1111110 */ + 0xfc, /* 1111110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 23 0x17 '^W' */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x3c, /* 0011110 */ + 0x7e, /* 0111111 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x7e, /* 0111111 */ + 0x3c, /* 0011110 */ + 0x18, /* 0001100 */ + 0x7e, /* 0111111 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 24 0x18 '^X' */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x3c, /* 0011110 */ + 0x7e, /* 0111111 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 25 0x19 '^Y' */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x7e, /* 0111111 */ + 0x3c, /* 0011110 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 26 0x1a '^Z' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x30, /* 0011000 */ + 0x18, /* 0001100 */ + 0xfc, /* 1111110 */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 27 0x1b '^[' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x30, /* 0011000 */ + 0x60, /* 0110000 */ + 0xfc, /* 1111110 */ + 0x60, /* 0110000 */ + 0x30, /* 0011000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 28 0x1c '^\' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xfc, /* 1111110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 29 0x1d '^]' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x28, /* 0010100 */ + 0x6c, /* 0110110 */ + 0xfe, /* 1111111 */ + 0x6c, /* 0110110 */ + 0x28, /* 0010100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 30 0x1e '^^' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x78, /* 0111100 */ + 0x78, /* 0111100 */ + 0xfc, /* 1111110 */ + 0xfc, /* 1111110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 31 0x1f '^_' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xfc, /* 1111110 */ + 0xfc, /* 1111110 */ + 0x78, /* 0111100 */ + 0x78, /* 0111100 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 32 0x20 ' ' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 33 0x21 '!' */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x3c, /* 0011110 */ + 0x3c, /* 0011110 */ + 0x3c, /* 0011110 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 34 0x22 '"' */ + 0x00, /* 0000000 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x28, /* 0010100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 35 0x23 '#' */ + 0x00, /* 0000000 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 36 0x24 '$' */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x78, /* 0111100 */ + 0xcc, /* 1100110 */ + 0xc4, /* 1100010 */ + 0xc0, /* 1100000 */ + 0x78, /* 0111100 */ + 0x0c, /* 0000110 */ + 0x8c, /* 1000110 */ + 0xcc, /* 1100110 */ + 0x78, /* 0111100 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x00, /* 0000000 */ + + /* 37 0x25 '%' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xc0, /* 1100000 */ + 0xc4, /* 1100010 */ + 0x0c, /* 0000110 */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0x60, /* 0110000 */ + 0xcc, /* 1100110 */ + 0x8c, /* 1000110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 38 0x26 '&' */ + 0x00, /* 0000000 */ + 0x38, /* 0011100 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x38, /* 0011100 */ + 0x78, /* 0111100 */ + 0xde, /* 1101111 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xdc, /* 1101110 */ + 0x76, /* 0111011 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 39 0x27 ''' */ + 0x00, /* 0000000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x60, /* 0110000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 40 0x28 '(' */ + 0x00, /* 0000000 */ + 0x0c, /* 0000110 */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x18, /* 0001100 */ + 0x0c, /* 0000110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 41 0x29 ')' */ + 0x00, /* 0000000 */ + 0x30, /* 0011000 */ + 0x18, /* 0001100 */ + 0x0c, /* 0000110 */ + 0x0c, /* 0000110 */ + 0x0c, /* 0000110 */ + 0x0c, /* 0000110 */ + 0x0c, /* 0000110 */ + 0x0c, /* 0000110 */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 42 0x2a '*' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x6c, /* 0110110 */ + 0x38, /* 0011100 */ + 0xfe, /* 1111111 */ + 0x38, /* 0011100 */ + 0x6c, /* 0110110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 43 0x2b '+' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x7c, /* 0111110 */ + 0x10, /* 0001000 */ + 0x10, /* 0001000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 44 0x2c ',' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 45 0x2d '-' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xfc, /* 1111110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 46 0x2e '.' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 47 0x2f '/' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x04, /* 0000010 */ + 0x0c, /* 0000110 */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0x60, /* 0110000 */ + 0xc0, /* 1100000 */ + 0x80, /* 1000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 48 0x30 '0' */ + 0x00, /* 0000000 */ + 0x30, /* 0011000 */ + 0x78, /* 0111100 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xdc, /* 1101110 */ + 0xec, /* 1110110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x78, /* 0111100 */ + 0x30, /* 0011000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 49 0x31 '1' */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x38, /* 0011100 */ + 0x78, /* 0111100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x7c, /* 0111110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 50 0x32 '2' */ + 0x00, /* 0000000 */ + 0x78, /* 0111100 */ + 0xcc, /* 1100110 */ + 0x0c, /* 0000110 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0x60, /* 0110000 */ + 0xc0, /* 1100000 */ + 0xcc, /* 1100110 */ + 0xfc, /* 1111110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 51 0x33 '3' */ + 0x00, /* 0000000 */ + 0x78, /* 0111100 */ + 0xcc, /* 1100110 */ + 0x0c, /* 0000110 */ + 0x0c, /* 0000110 */ + 0x38, /* 0011100 */ + 0x0c, /* 0000110 */ + 0x0c, /* 0000110 */ + 0x0c, /* 0000110 */ + 0xcc, /* 1100110 */ + 0x78, /* 0111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 52 0x34 '4' */ + 0x00, /* 0000000 */ + 0x0c, /* 0000110 */ + 0x1c, /* 0001110 */ + 0x3c, /* 0011110 */ + 0x6c, /* 0110110 */ + 0xcc, /* 1100110 */ + 0xfe, /* 1111111 */ + 0x0c, /* 0000110 */ + 0x0c, /* 0000110 */ + 0x0c, /* 0000110 */ + 0x0c, /* 0000110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 53 0x35 '5' */ + 0x00, /* 0000000 */ + 0xfc, /* 1111110 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xf8, /* 1111100 */ + 0x0c, /* 0000110 */ + 0x0c, /* 0000110 */ + 0x0c, /* 0000110 */ + 0xcc, /* 1100110 */ + 0x78, /* 0111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 54 0x36 '6' */ + 0x00, /* 0000000 */ + 0x30, /* 0011000 */ + 0x60, /* 0110000 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xf8, /* 1111100 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x78, /* 0111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 55 0x37 '7' */ + 0x00, /* 0000000 */ + 0xfc, /* 1111110 */ + 0xcc, /* 1100110 */ + 0x0c, /* 0000110 */ + 0x0c, /* 0000110 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 56 0x38 '8' */ + 0x00, /* 0000000 */ + 0x78, /* 0111100 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x78, /* 0111100 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x78, /* 0111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 57 0x39 '9' */ + 0x00, /* 0000000 */ + 0x78, /* 0111100 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x7c, /* 0111110 */ + 0x0c, /* 0000110 */ + 0x0c, /* 0000110 */ + 0x0c, /* 0000110 */ + 0x18, /* 0001100 */ + 0x70, /* 0111000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 58 0x3a ':' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 59 0x3b ';' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 60 0x3c '<' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x04, /* 0000010 */ + 0x0c, /* 0000110 */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0x60, /* 0110000 */ + 0x30, /* 0011000 */ + 0x18, /* 0001100 */ + 0x0c, /* 0000110 */ + 0x04, /* 0000010 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 61 0x3d '=' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x7c, /* 0111110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x7c, /* 0111110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 62 0x3e '>' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x40, /* 0100000 */ + 0x60, /* 0110000 */ + 0x30, /* 0011000 */ + 0x18, /* 0001100 */ + 0x0c, /* 0000110 */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0x60, /* 0110000 */ + 0x40, /* 0100000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 63 0x3f '?' */ + 0x00, /* 0000000 */ + 0x78, /* 0111100 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x00, /* 0000000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 64 0x40 '@' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x78, /* 0111100 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xdc, /* 1101110 */ + 0xdc, /* 1101110 */ + 0xd8, /* 1101100 */ + 0xc0, /* 1100000 */ + 0x78, /* 0111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 65 0x41 'A' */ + 0x00, /* 0000000 */ + 0x30, /* 0011000 */ + 0x78, /* 0111100 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xfc, /* 1111110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 66 0x42 'B' */ + 0x00, /* 0000000 */ + 0xf8, /* 1111100 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x78, /* 0111100 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0xf8, /* 1111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 67 0x43 'C' */ + 0x00, /* 0000000 */ + 0x38, /* 0011100 */ + 0x6c, /* 0110110 */ + 0xc4, /* 1100010 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xc4, /* 1100010 */ + 0x6c, /* 0110110 */ + 0x38, /* 0011100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 68 0x44 'D' */ + 0x00, /* 0000000 */ + 0xf0, /* 1111000 */ + 0xd8, /* 1101100 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xd8, /* 1101100 */ + 0xf0, /* 1111000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 69 0x45 'E' */ + 0x00, /* 0000000 */ + 0x7c, /* 0111110 */ + 0x6c, /* 0110110 */ + 0x64, /* 0110010 */ + 0x68, /* 0110100 */ + 0x78, /* 0111100 */ + 0x68, /* 0110100 */ + 0x60, /* 0110000 */ + 0x64, /* 0110010 */ + 0x6c, /* 0110110 */ + 0x7c, /* 0111110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 70 0x46 'F' */ + 0x00, /* 0000000 */ + 0x7c, /* 0111110 */ + 0x64, /* 0110010 */ + 0x60, /* 0110000 */ + 0x68, /* 0110100 */ + 0x78, /* 0111100 */ + 0x68, /* 0110100 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x70, /* 0111000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 71 0x47 'G' */ + 0x00, /* 0000000 */ + 0x38, /* 0011100 */ + 0x6c, /* 0110110 */ + 0xc4, /* 1100010 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xdc, /* 1101110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x6c, /* 0110110 */ + 0x34, /* 0011010 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 72 0x48 'H' */ + 0x00, /* 0000000 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xfc, /* 1111110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 73 0x49 'I' */ + 0x00, /* 0000000 */ + 0x3c, /* 0011110 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x3c, /* 0011110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 74 0x4a 'J' */ + 0x00, /* 0000000 */ + 0x1c, /* 0001110 */ + 0x0c, /* 0000110 */ + 0x0c, /* 0000110 */ + 0x0c, /* 0000110 */ + 0x0c, /* 0000110 */ + 0x0c, /* 0000110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x78, /* 0111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 75 0x4b 'K' */ + 0x00, /* 0000000 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xd8, /* 1101100 */ + 0xf0, /* 1111000 */ + 0xf0, /* 1111000 */ + 0xd8, /* 1101100 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 76 0x4c 'L' */ + 0x00, /* 0000000 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xc4, /* 1100010 */ + 0xcc, /* 1100110 */ + 0xfc, /* 1111110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 77 0x4d 'M' */ + 0x00, /* 0000000 */ + 0xc6, /* 1100011 */ + 0xee, /* 1110111 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0xd6, /* 1101011 */ + 0xc6, /* 1100011 */ + 0xc6, /* 1100011 */ + 0xc6, /* 1100011 */ + 0xc6, /* 1100011 */ + 0xc6, /* 1100011 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 78 0x4e 'N' */ + 0x00, /* 0000000 */ + 0xcc, /* 1100110 */ + 0xec, /* 1110110 */ + 0xec, /* 1110110 */ + 0xfc, /* 1111110 */ + 0xdc, /* 1101110 */ + 0xdc, /* 1101110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 79 0x4f 'O' */ + 0x00, /* 0000000 */ + 0x78, /* 0111100 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x78, /* 0111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 80 0x50 'P' */ + 0x00, /* 0000000 */ + 0xf8, /* 1111100 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xf8, /* 1111100 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 81 0x51 'Q' */ + 0x00, /* 0000000 */ + 0x78, /* 0111100 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xdc, /* 1101110 */ + 0x78, /* 0111100 */ + 0x18, /* 0001100 */ + 0x1c, /* 0001110 */ + 0x00, /* 0000000 */ + + /* 82 0x52 'R' */ + 0x00, /* 0000000 */ + 0xf8, /* 1111100 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xf8, /* 1111100 */ + 0xd8, /* 1101100 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 83 0x53 'S' */ + 0x00, /* 0000000 */ + 0x7c, /* 0111110 */ + 0xc4, /* 1100010 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0x60, /* 0110000 */ + 0x38, /* 0011100 */ + 0x0c, /* 0000110 */ + 0x0c, /* 0000110 */ + 0x8c, /* 1000110 */ + 0xf8, /* 1111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 84 0x54 'T' */ + 0x00, /* 0000000 */ + 0xfc, /* 1111110 */ + 0xfc, /* 1111110 */ + 0xb4, /* 1011010 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x78, /* 0111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 85 0x55 'U' */ + 0x00, /* 0000000 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x78, /* 0111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 86 0x56 'V' */ + 0x00, /* 0000000 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x78, /* 0111100 */ + 0x78, /* 0111100 */ + 0x30, /* 0011000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 87 0x57 'W' */ + 0x00, /* 0000000 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xfc, /* 1111110 */ + 0xfc, /* 1111110 */ + 0x48, /* 0100100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 88 0x58 'X' */ + 0x00, /* 0000000 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x78, /* 0111100 */ + 0x78, /* 0111100 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x78, /* 0111100 */ + 0x78, /* 0111100 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 89 0x59 'Y' */ + 0x00, /* 0000000 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x78, /* 0111100 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 90 0x5a 'Z' */ + 0x00, /* 0000000 */ + 0xfc, /* 1111110 */ + 0xcc, /* 1100110 */ + 0x8c, /* 1000110 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0x60, /* 0110000 */ + 0xc4, /* 1100010 */ + 0xcc, /* 1100110 */ + 0xfc, /* 1111110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 91 0x5b '[' */ + 0x00, /* 0000000 */ + 0x78, /* 0111100 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x78, /* 0111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 92 0x5c '\' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x80, /* 1000000 */ + 0xc0, /* 1100000 */ + 0xe0, /* 1110000 */ + 0x70, /* 0111000 */ + 0x38, /* 0011100 */ + 0x1c, /* 0001110 */ + 0x0c, /* 0000110 */ + 0x04, /* 0000010 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 93 0x5d ']' */ + 0x00, /* 0000000 */ + 0x78, /* 0111100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x78, /* 0111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 94 0x5e '^' */ + 0x10, /* 0001000 */ + 0x38, /* 0011100 */ + 0x6c, /* 0110110 */ + 0xc6, /* 1100011 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 95 0x5f '_' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xfe, /* 1111111 */ + 0x00, /* 0000000 */ + + /* 96 0x60 '`' */ + 0x00, /* 0000000 */ + 0x60, /* 0110000 */ + 0x30, /* 0011000 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 97 0x61 'a' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x78, /* 0111100 */ + 0x0c, /* 0000110 */ + 0x7c, /* 0111110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x76, /* 0111011 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 98 0x62 'b' */ + 0x00, /* 0000000 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xf0, /* 1111000 */ + 0xd8, /* 1101100 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xf8, /* 1111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 99 0x63 'c' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x78, /* 0111100 */ + 0xcc, /* 1100110 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xcc, /* 1100110 */ + 0x78, /* 0111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 100 0x64 'd' */ + 0x00, /* 0000000 */ + 0x1c, /* 0001110 */ + 0x0c, /* 0000110 */ + 0x0c, /* 0000110 */ + 0x3c, /* 0011110 */ + 0x6c, /* 0110110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x76, /* 0111011 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 101 0x65 'e' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x78, /* 0111100 */ + 0xcc, /* 1100110 */ + 0xfc, /* 1111110 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xcc, /* 1100110 */ + 0x78, /* 0111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 102 0x66 'f' */ + 0x00, /* 0000000 */ + 0x38, /* 0011100 */ + 0x6c, /* 0110110 */ + 0x64, /* 0110010 */ + 0x60, /* 0110000 */ + 0xf0, /* 1111000 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0xf0, /* 1111000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 103 0x67 'g' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x76, /* 0111011 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x7c, /* 0111110 */ + 0x0c, /* 0000110 */ + 0xcc, /* 1100110 */ + 0x78, /* 0111100 */ + + /* 104 0x68 'h' */ + 0x00, /* 0000000 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xd8, /* 1101100 */ + 0xec, /* 1110110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 105 0x69 'i' */ + 0x00, /* 0000000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x00, /* 0000000 */ + 0x70, /* 0111000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x78, /* 0111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 106 0x6a 'j' */ + 0x00, /* 0000000 */ + 0x0c, /* 0000110 */ + 0x0c, /* 0000110 */ + 0x00, /* 0000000 */ + 0x1c, /* 0001110 */ + 0x0c, /* 0000110 */ + 0x0c, /* 0000110 */ + 0x0c, /* 0000110 */ + 0x0c, /* 0000110 */ + 0x0c, /* 0000110 */ + 0x0c, /* 0000110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x78, /* 0111100 */ + + /* 107 0x6b 'k' */ + 0x00, /* 0000000 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xcc, /* 1100110 */ + 0xd8, /* 1101100 */ + 0xf0, /* 1111000 */ + 0xf0, /* 1111000 */ + 0xd8, /* 1101100 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 108 0x6c 'l' */ + 0x00, /* 0000000 */ + 0x70, /* 0111000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x78, /* 0111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 109 0x6d 'm' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xec, /* 1110110 */ + 0xfe, /* 1111111 */ + 0xd6, /* 1101011 */ + 0xd6, /* 1101011 */ + 0xd6, /* 1101011 */ + 0xd6, /* 1101011 */ + 0xd6, /* 1101011 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 110 0x6e 'n' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xb8, /* 1011100 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 111 0x6f 'o' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x78, /* 0111100 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x78, /* 0111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 112 0x70 'p' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xb8, /* 1011100 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xf8, /* 1111100 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + + /* 113 0x71 'q' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x74, /* 0111010 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x7c, /* 0111110 */ + 0x0c, /* 0000110 */ + 0x0c, /* 0000110 */ + 0x0c, /* 0000110 */ + + /* 114 0x72 'r' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xb8, /* 1011100 */ + 0xec, /* 1110110 */ + 0xcc, /* 1100110 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 115 0x73 's' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x78, /* 0111100 */ + 0xcc, /* 1100110 */ + 0x60, /* 0110000 */ + 0x30, /* 0011000 */ + 0x18, /* 0001100 */ + 0xcc, /* 1100110 */ + 0x78, /* 0111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 116 0x74 't' */ + 0x00, /* 0000000 */ + 0x10, /* 0001000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0xfc, /* 1111110 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x36, /* 0011011 */ + 0x1c, /* 0001110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 117 0x75 'u' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x76, /* 0111011 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 118 0x76 'v' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x78, /* 0111100 */ + 0x30, /* 0011000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 119 0x77 'w' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xc6, /* 1100011 */ + 0xc6, /* 1100011 */ + 0xd6, /* 1101011 */ + 0xd6, /* 1101011 */ + 0xd6, /* 1101011 */ + 0xfe, /* 1111111 */ + 0x6c, /* 0110110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 120 0x78 'x' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x78, /* 0111100 */ + 0x30, /* 0011000 */ + 0x78, /* 0111100 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 121 0x79 'y' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x7c, /* 0111110 */ + 0x0c, /* 0000110 */ + 0x18, /* 0001100 */ + 0xf0, /* 1111000 */ + + /* 122 0x7a 'z' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xfc, /* 1111110 */ + 0xcc, /* 1100110 */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0x60, /* 0110000 */ + 0xcc, /* 1100110 */ + 0xfc, /* 1111110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 123 0x7b '{' */ + 0x00, /* 0000000 */ + 0x1c, /* 0001110 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0xe0, /* 1110000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x1c, /* 0001110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 124 0x7c '|' */ + 0x00, /* 0000000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 125 0x7d '}' */ + 0x00, /* 0000000 */ + 0x70, /* 0111000 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x0e, /* 0000111 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x70, /* 0111000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 126 0x7e '~' */ + 0x00, /* 0000000 */ + 0xec, /* 1110110 */ + 0xb8, /* 1011100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 127 0x7f '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x10, /* 0001000 */ + 0x38, /* 0011100 */ + 0x6c, /* 0110110 */ + 0xc6, /* 1100011 */ + 0xc6, /* 1100011 */ + 0xc6, /* 1100011 */ + 0xfe, /* 1111111 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 128 0x80 '' */ + 0x00, /* 0000000 */ + 0x38, /* 0011100 */ + 0x6c, /* 0110110 */ + 0xc4, /* 1100010 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xc4, /* 1100010 */ + 0x6c, /* 0110110 */ + 0x38, /* 0011100 */ + 0x18, /* 0001100 */ + 0x70, /* 0111000 */ + 0x00, /* 0000000 */ + + /* 129 0x81 '' */ + 0x00, /* 0000000 */ + 0xcc, /* 1100110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x76, /* 0111011 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 130 0x82 '' */ + 0x0c, /* 0000110 */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0x00, /* 0000000 */ + 0x78, /* 0111100 */ + 0xcc, /* 1100110 */ + 0xfc, /* 1111110 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xcc, /* 1100110 */ + 0x78, /* 0111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 131 0x83 '' */ + 0x10, /* 0001000 */ + 0x38, /* 0011100 */ + 0x6c, /* 0110110 */ + 0x00, /* 0000000 */ + 0x78, /* 0111100 */ + 0x0c, /* 0000110 */ + 0x7c, /* 0111110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x76, /* 0111011 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 132 0x84 '' */ + 0x00, /* 0000000 */ + 0xcc, /* 1100110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x78, /* 0111100 */ + 0x0c, /* 0000110 */ + 0x7c, /* 0111110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x76, /* 0111011 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 133 0x85 '' */ + 0x60, /* 0110000 */ + 0x30, /* 0011000 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + 0x78, /* 0111100 */ + 0x0c, /* 0000110 */ + 0x7c, /* 0111110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x76, /* 0111011 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 134 0x86 '' */ + 0x38, /* 0011100 */ + 0x6c, /* 0110110 */ + 0x38, /* 0011100 */ + 0x00, /* 0000000 */ + 0x78, /* 0111100 */ + 0x0c, /* 0000110 */ + 0x7c, /* 0111110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x76, /* 0111011 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 135 0x87 '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x78, /* 0111100 */ + 0xcc, /* 1100110 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xcc, /* 1100110 */ + 0x78, /* 0111100 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0xe0, /* 1110000 */ + + /* 136 0x88 '' */ + 0x10, /* 0001000 */ + 0x38, /* 0011100 */ + 0x6c, /* 0110110 */ + 0x00, /* 0000000 */ + 0x78, /* 0111100 */ + 0xcc, /* 1100110 */ + 0xfc, /* 1111110 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xcc, /* 1100110 */ + 0x78, /* 0111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 137 0x89 '' */ + 0x00, /* 0000000 */ + 0xcc, /* 1100110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x78, /* 0111100 */ + 0xcc, /* 1100110 */ + 0xfc, /* 1111110 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xcc, /* 1100110 */ + 0x78, /* 0111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 138 0x8a '' */ + 0xc0, /* 1100000 */ + 0x60, /* 0110000 */ + 0x30, /* 0011000 */ + 0x00, /* 0000000 */ + 0x78, /* 0111100 */ + 0xcc, /* 1100110 */ + 0xfc, /* 1111110 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xcc, /* 1100110 */ + 0x78, /* 0111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 139 0x8b '' */ + 0x00, /* 0000000 */ + 0x6c, /* 0110110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x38, /* 0011100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x3c, /* 0011110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 140 0x8c '' */ + 0x30, /* 0011000 */ + 0x78, /* 0111100 */ + 0xcc, /* 1100110 */ + 0x00, /* 0000000 */ + 0x70, /* 0111000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x78, /* 0111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 141 0x8d '' */ + 0xc0, /* 1100000 */ + 0x60, /* 0110000 */ + 0x30, /* 0011000 */ + 0x00, /* 0000000 */ + 0x70, /* 0111000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x78, /* 0111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 142 0x8e '' */ + 0x00, /* 0000000 */ + 0xcc, /* 1100110 */ + 0x00, /* 0000000 */ + 0x30, /* 0011000 */ + 0x78, /* 0111100 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xfc, /* 1111110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 143 0x8f '' */ + 0x30, /* 0011000 */ + 0x48, /* 0100100 */ + 0x48, /* 0100100 */ + 0x30, /* 0011000 */ + 0x78, /* 0111100 */ + 0xcc, /* 1100110 */ + 0xfc, /* 1111110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 144 0x90 '' */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0xfc, /* 1111110 */ + 0xcc, /* 1100110 */ + 0xc4, /* 1100010 */ + 0xd0, /* 1101000 */ + 0xf0, /* 1111000 */ + 0xd0, /* 1101000 */ + 0xc4, /* 1100010 */ + 0xcc, /* 1100110 */ + 0xfc, /* 1111110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 145 0x91 '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xec, /* 1110110 */ + 0x36, /* 0011011 */ + 0x36, /* 0011011 */ + 0x7e, /* 0111111 */ + 0xd8, /* 1101100 */ + 0xd8, /* 1101100 */ + 0x6e, /* 0110111 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 146 0x92 '' */ + 0x00, /* 0000000 */ + 0x3e, /* 0011111 */ + 0x6c, /* 0110110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xfe, /* 1111111 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xce, /* 1100111 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 147 0x93 '' */ + 0x10, /* 0001000 */ + 0x38, /* 0011100 */ + 0x6c, /* 0110110 */ + 0x00, /* 0000000 */ + 0x78, /* 0111100 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x78, /* 0111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 148 0x94 '' */ + 0x00, /* 0000000 */ + 0xcc, /* 1100110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x78, /* 0111100 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x78, /* 0111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 149 0x95 '' */ + 0xc0, /* 1100000 */ + 0x60, /* 0110000 */ + 0x30, /* 0011000 */ + 0x00, /* 0000000 */ + 0x78, /* 0111100 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x78, /* 0111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 150 0x96 '' */ + 0x30, /* 0011000 */ + 0x78, /* 0111100 */ + 0xcc, /* 1100110 */ + 0x00, /* 0000000 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x76, /* 0111011 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 151 0x97 '' */ + 0x60, /* 0110000 */ + 0x30, /* 0011000 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x76, /* 0111011 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 152 0x98 '' */ + 0x00, /* 0000000 */ + 0xcc, /* 1100110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x7c, /* 0111110 */ + 0x0c, /* 0000110 */ + 0x18, /* 0001100 */ + 0x70, /* 0111000 */ + + /* 153 0x99 '' */ + 0xcc, /* 1100110 */ + 0x00, /* 0000000 */ + 0x78, /* 0111100 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x78, /* 0111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 154 0x9a '' */ + 0xcc, /* 1100110 */ + 0x00, /* 0000000 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x78, /* 0111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 155 0x9b '' */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x7c, /* 0111110 */ + 0xcc, /* 1100110 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xcc, /* 1100110 */ + 0x7c, /* 0111110 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 156 0x9c '' */ + 0x38, /* 0011100 */ + 0x6c, /* 0110110 */ + 0x64, /* 0110010 */ + 0x60, /* 0110000 */ + 0xf0, /* 1111000 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0xe6, /* 1110011 */ + 0xfc, /* 1111110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 157 0x9d '' */ + 0x00, /* 0000000 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x78, /* 0111100 */ + 0x30, /* 0011000 */ + 0xfc, /* 1111110 */ + 0x30, /* 0011000 */ + 0xfc, /* 1111110 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 158 0x9e '' */ + 0xf8, /* 1111100 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xf8, /* 1111100 */ + 0xc4, /* 1100010 */ + 0xcc, /* 1100110 */ + 0xde, /* 1101111 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xc6, /* 1100011 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 159 0x9f '' */ + 0x1c, /* 0001110 */ + 0x36, /* 0011011 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0xfc, /* 1111110 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0xb0, /* 1011000 */ + 0xe0, /* 1110000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 160 0xa0 '' */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0x60, /* 0110000 */ + 0x00, /* 0000000 */ + 0x78, /* 0111100 */ + 0x0c, /* 0000110 */ + 0x7c, /* 0111110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x76, /* 0111011 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 161 0xa1 '' */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0x60, /* 0110000 */ + 0x00, /* 0000000 */ + 0x70, /* 0111000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x78, /* 0111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 162 0xa2 '' */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0x60, /* 0110000 */ + 0x00, /* 0000000 */ + 0x78, /* 0111100 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x78, /* 0111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 163 0xa3 '' */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0x60, /* 0110000 */ + 0x00, /* 0000000 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x76, /* 0111011 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 164 0xa4 '' */ + 0x00, /* 0000000 */ + 0x76, /* 0111011 */ + 0xdc, /* 1101110 */ + 0x00, /* 0000000 */ + 0xb8, /* 1011100 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 165 0xa5 '' */ + 0x76, /* 0111011 */ + 0xdc, /* 1101110 */ + 0x00, /* 0000000 */ + 0xcc, /* 1100110 */ + 0xec, /* 1110110 */ + 0xec, /* 1110110 */ + 0xfc, /* 1111110 */ + 0xdc, /* 1101110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 166 0xa6 '' */ + 0x00, /* 0000000 */ + 0x78, /* 0111100 */ + 0xd8, /* 1101100 */ + 0xd8, /* 1101100 */ + 0x7c, /* 0111110 */ + 0x00, /* 0000000 */ + 0xfc, /* 1111110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 167 0xa7 '' */ + 0x00, /* 0000000 */ + 0x70, /* 0111000 */ + 0xd8, /* 1101100 */ + 0xd8, /* 1101100 */ + 0x70, /* 0111000 */ + 0x00, /* 0000000 */ + 0xf8, /* 1111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 168 0xa8 '' */ + 0x00, /* 0000000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x00, /* 0000000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x60, /* 0110000 */ + 0xc0, /* 1100000 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x78, /* 0111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 169 0xa9 '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xfc, /* 1111110 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 170 0xaa '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xfc, /* 1111110 */ + 0x0c, /* 0000110 */ + 0x0c, /* 0000110 */ + 0x0c, /* 0000110 */ + 0x0c, /* 0000110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 171 0xab '' */ + 0x60, /* 0110000 */ + 0xe0, /* 1110000 */ + 0x62, /* 0110001 */ + 0x66, /* 0110011 */ + 0x6c, /* 0110110 */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0x60, /* 0110000 */ + 0xc0, /* 1100000 */ + 0xb8, /* 1011100 */ + 0x4c, /* 0100110 */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0x7c, /* 0111110 */ + + /* 172 0xac '' */ + 0x60, /* 0110000 */ + 0xe0, /* 1110000 */ + 0x62, /* 0110001 */ + 0x66, /* 0110011 */ + 0x6c, /* 0110110 */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0x6c, /* 0110110 */ + 0xdc, /* 1101110 */ + 0xb4, /* 1011010 */ + 0x7e, /* 0111111 */ + 0x0c, /* 0000110 */ + 0x0c, /* 0000110 */ + 0x00, /* 0000000 */ + + /* 173 0xad '' */ + 0x00, /* 0000000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x00, /* 0000000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x78, /* 0111100 */ + 0x78, /* 0111100 */ + 0x78, /* 0111100 */ + 0x30, /* 0011000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 174 0xae '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x36, /* 0011011 */ + 0x6c, /* 0110110 */ + 0xd8, /* 1101100 */ + 0x6c, /* 0110110 */ + 0x36, /* 0011011 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 175 0xaf '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xd8, /* 1101100 */ + 0x6c, /* 0110110 */ + 0x36, /* 0011011 */ + 0x6c, /* 0110110 */ + 0xd8, /* 1101100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 176 0xb0 '' */ + 0x88, /* 1000100 */ + 0x22, /* 0010001 */ + 0x88, /* 1000100 */ + 0x22, /* 0010001 */ + 0x88, /* 1000100 */ + 0x22, /* 0010001 */ + 0x88, /* 1000100 */ + 0x22, /* 0010001 */ + 0x88, /* 1000100 */ + 0x22, /* 0010001 */ + 0x88, /* 1000100 */ + 0x22, /* 0010001 */ + 0x88, /* 1000100 */ + 0x22, /* 0010001 */ + + /* 177 0xb1 '' */ + 0x54, /* 0101010 */ + 0xaa, /* 1010101 */ + 0x54, /* 0101010 */ + 0xaa, /* 1010101 */ + 0x54, /* 0101010 */ + 0xaa, /* 1010101 */ + 0x54, /* 0101010 */ + 0xaa, /* 1010101 */ + 0x54, /* 0101010 */ + 0xaa, /* 1010101 */ + 0x54, /* 0101010 */ + 0xaa, /* 1010101 */ + 0x54, /* 0101010 */ + 0xaa, /* 1010101 */ + + /* 178 0xb2 '' */ + 0xee, /* 1110111 */ + 0xba, /* 1011101 */ + 0xee, /* 1110111 */ + 0xba, /* 1011101 */ + 0xee, /* 1110111 */ + 0xba, /* 1011101 */ + 0xee, /* 1110111 */ + 0xba, /* 1011101 */ + 0xee, /* 1110111 */ + 0xba, /* 1011101 */ + 0xee, /* 1110111 */ + 0xba, /* 1011101 */ + 0xee, /* 1110111 */ + 0xba, /* 1011101 */ + + /* 179 0xb3 '' */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + + /* 180 0xb4 '' */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0xf0, /* 1111000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + + /* 181 0xb5 '' */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0xf0, /* 1111000 */ + 0x30, /* 0011000 */ + 0xf0, /* 1111000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + + /* 182 0xb6 '' */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0xec, /* 1110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + + /* 183 0xb7 '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xfc, /* 1111110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + + /* 184 0xb8 '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xf0, /* 1111000 */ + 0x30, /* 0011000 */ + 0xf0, /* 1111000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + + /* 185 0xb9 '' */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0xec, /* 1110110 */ + 0x0c, /* 0000110 */ + 0xec, /* 1110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + + /* 186 0xba '' */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + + /* 187 0xbb '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xfc, /* 1111110 */ + 0x0c, /* 0000110 */ + 0xec, /* 1110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + + /* 188 0xbc '' */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0xec, /* 1110110 */ + 0x0c, /* 0000110 */ + 0xfc, /* 1111110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 189 0xbd '' */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0xfc, /* 1111110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 190 0xbe '' */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0xf0, /* 1111000 */ + 0x30, /* 0011000 */ + 0xf0, /* 1111000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 191 0xbf '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xf0, /* 1111000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + + /* 192 0xc0 '' */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x3e, /* 0011111 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 193 0xc1 '' */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0xfe, /* 1111111 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 194 0xc2 '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xfe, /* 1111111 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + + /* 195 0xc3 '' */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x3e, /* 0011111 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + + /* 196 0xc4 '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xfe, /* 1111111 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 197 0xc5 '' */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0xfe, /* 1111111 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + + /* 198 0xc6 '' */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x3e, /* 0011111 */ + 0x30, /* 0011000 */ + 0x3e, /* 0011111 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + + /* 199 0xc7 '' */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6e, /* 0110111 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + + /* 200 0xc8 '' */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6e, /* 0110111 */ + 0x60, /* 0110000 */ + 0x7e, /* 0111111 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 201 0xc9 '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x7e, /* 0111111 */ + 0x60, /* 0110000 */ + 0x6e, /* 0110111 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + + /* 202 0xca '' */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0xee, /* 1110111 */ + 0x00, /* 0000000 */ + 0xfe, /* 1111111 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 203 0xcb '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xfe, /* 1111111 */ + 0x00, /* 0000000 */ + 0xee, /* 1110111 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + + /* 204 0xcc '' */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6e, /* 0110111 */ + 0x60, /* 0110000 */ + 0x6e, /* 0110111 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + + /* 205 0xcd '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xfe, /* 1111111 */ + 0x00, /* 0000000 */ + 0xfe, /* 1111111 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 206 0xce '' */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0xee, /* 1110111 */ + 0x00, /* 0000000 */ + 0xee, /* 1110111 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + + /* 207 0xcf '' */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0xfe, /* 1111111 */ + 0x00, /* 0000000 */ + 0xfe, /* 1111111 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 208 0xd0 '' */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0xfe, /* 1111111 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 209 0xd1 '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xfe, /* 1111111 */ + 0x00, /* 0000000 */ + 0xfe, /* 1111111 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + + /* 210 0xd2 '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xfe, /* 1111111 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + + /* 211 0xd3 '' */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x7e, /* 0111111 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 212 0xd4 '' */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x3e, /* 0011111 */ + 0x30, /* 0011000 */ + 0x3e, /* 0011111 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 213 0xd5 '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x3e, /* 0011111 */ + 0x30, /* 0011000 */ + 0x3e, /* 0011111 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + + /* 214 0xd6 '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x7e, /* 0111111 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + + /* 215 0xd7 '' */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0xfe, /* 1111111 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + + /* 216 0xd8 '' */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0xfe, /* 1111111 */ + 0x30, /* 0011000 */ + 0xfe, /* 1111111 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + + /* 217 0xd9 '' */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0xf0, /* 1111000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 218 0xda '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x3e, /* 0011111 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + + /* 219 0xdb '' */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + + /* 220 0xdc '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + + /* 221 0xdd '' */ + 0xe0, /* 1110000 */ + 0xe0, /* 1110000 */ + 0xe0, /* 1110000 */ + 0xe0, /* 1110000 */ + 0xe0, /* 1110000 */ + 0xe0, /* 1110000 */ + 0xe0, /* 1110000 */ + 0xe0, /* 1110000 */ + 0xe0, /* 1110000 */ + 0xe0, /* 1110000 */ + 0xe0, /* 1110000 */ + 0xe0, /* 1110000 */ + 0xe0, /* 1110000 */ + 0xe0, /* 1110000 */ + + /* 222 0xde '' */ + 0x1e, /* 0001111 */ + 0x1e, /* 0001111 */ + 0x1e, /* 0001111 */ + 0x1e, /* 0001111 */ + 0x1e, /* 0001111 */ + 0x1e, /* 0001111 */ + 0x1e, /* 0001111 */ + 0x1e, /* 0001111 */ + 0x1e, /* 0001111 */ + 0x1e, /* 0001111 */ + 0x1e, /* 0001111 */ + 0x1e, /* 0001111 */ + 0x1e, /* 0001111 */ + 0x1e, /* 0001111 */ + + /* 223 0xdf '' */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 224 0xe0 '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x76, /* 0111011 */ + 0xdc, /* 1101110 */ + 0xd8, /* 1101100 */ + 0xd8, /* 1101100 */ + 0xd8, /* 1101100 */ + 0xdc, /* 1101110 */ + 0x76, /* 0111011 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 225 0xe1 '' */ + 0x00, /* 0000000 */ + 0x78, /* 0111100 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xd8, /* 1101100 */ + 0xcc, /* 1100110 */ + 0xc6, /* 1100011 */ + 0xc6, /* 1100011 */ + 0xc6, /* 1100011 */ + 0xcc, /* 1100110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 226 0xe2 '' */ + 0x00, /* 0000000 */ + 0xfc, /* 1111110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 227 0xe3 '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xfe, /* 1111111 */ + 0xfe, /* 1111111 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 228 0xe4 '' */ + 0x00, /* 0000000 */ + 0xfc, /* 1111110 */ + 0xcc, /* 1100110 */ + 0x60, /* 0110000 */ + 0x30, /* 0011000 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0x60, /* 0110000 */ + 0xcc, /* 1100110 */ + 0xfc, /* 1111110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 229 0xe5 '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x7e, /* 0111111 */ + 0xd8, /* 1101100 */ + 0xd8, /* 1101100 */ + 0xd8, /* 1101100 */ + 0xd8, /* 1101100 */ + 0xd8, /* 1101100 */ + 0x70, /* 0111000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 230 0xe6 '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xf8, /* 1111100 */ + 0xc0, /* 1100000 */ + 0xc0, /* 1100000 */ + 0x80, /* 1000000 */ + + /* 231 0xe7 '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x76, /* 0111011 */ + 0xdc, /* 1101110 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 232 0xe8 '' */ + 0x00, /* 0000000 */ + 0xfc, /* 1111110 */ + 0x30, /* 0011000 */ + 0x78, /* 0111100 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x78, /* 0111100 */ + 0x30, /* 0011000 */ + 0xfc, /* 1111110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 233 0xe9 '' */ + 0x00, /* 0000000 */ + 0x38, /* 0011100 */ + 0x6c, /* 0110110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xfc, /* 1111110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x6c, /* 0110110 */ + 0x38, /* 0011100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 234 0xea '' */ + 0x00, /* 0000000 */ + 0x38, /* 0011100 */ + 0x6c, /* 0110110 */ + 0xc6, /* 1100011 */ + 0xc6, /* 1100011 */ + 0xc6, /* 1100011 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0xee, /* 1110111 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 235 0xeb '' */ + 0x00, /* 0000000 */ + 0x3c, /* 0011110 */ + 0x60, /* 0110000 */ + 0x30, /* 0011000 */ + 0x18, /* 0001100 */ + 0x7c, /* 0111110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x78, /* 0111100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 236 0xec '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x7c, /* 0111110 */ + 0xd6, /* 1101011 */ + 0xd6, /* 1101011 */ + 0xd6, /* 1101011 */ + 0x7c, /* 0111110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 237 0xed '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x06, /* 0000011 */ + 0x0c, /* 0000110 */ + 0x7c, /* 0111110 */ + 0xd6, /* 1101011 */ + 0xd6, /* 1101011 */ + 0xe6, /* 1110011 */ + 0x7c, /* 0111110 */ + 0x60, /* 0110000 */ + 0xc0, /* 1100000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 238 0xee '' */ + 0x00, /* 0000000 */ + 0x1c, /* 0001110 */ + 0x30, /* 0011000 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x7c, /* 0111110 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x60, /* 0110000 */ + 0x30, /* 0011000 */ + 0x1c, /* 0001110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 239 0xef '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x78, /* 0111100 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0xcc, /* 1100110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 240 0xf0 '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xfc, /* 1111110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xfc, /* 1111110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xfc, /* 1111110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 241 0xf1 '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0xfc, /* 1111110 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0xfc, /* 1111110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 242 0xf2 '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x60, /* 0110000 */ + 0x30, /* 0011000 */ + 0x18, /* 0001100 */ + 0x0c, /* 0000110 */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0x60, /* 0110000 */ + 0x00, /* 0000000 */ + 0xfc, /* 1111110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 243 0xf3 '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0x60, /* 0110000 */ + 0xc0, /* 1100000 */ + 0x60, /* 0110000 */ + 0x30, /* 0011000 */ + 0x18, /* 0001100 */ + 0x00, /* 0000000 */ + 0xfc, /* 1111110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 244 0xf4 '' */ + 0x00, /* 0000000 */ + 0x1c, /* 0001110 */ + 0x36, /* 0011011 */ + 0x36, /* 0011011 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + + /* 245 0xf5 '' */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0xd8, /* 1101100 */ + 0xd8, /* 1101100 */ + 0xd8, /* 1101100 */ + 0x70, /* 0111000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 246 0xf6 '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x30, /* 0011000 */ + 0x00, /* 0000000 */ + 0xfc, /* 1111110 */ + 0x00, /* 0000000 */ + 0x30, /* 0011000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 247 0xf7 '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x76, /* 0111011 */ + 0xdc, /* 1101110 */ + 0x00, /* 0000000 */ + 0x76, /* 0111011 */ + 0xdc, /* 1101110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 248 0xf8 '' */ + 0x38, /* 0011100 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x38, /* 0011100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 249 0xf9 '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x30, /* 0011000 */ + 0x30, /* 0011000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 250 0xfa '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x30, /* 0011000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 251 0xfb '' */ + 0x1e, /* 0001111 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0x18, /* 0001100 */ + 0xd8, /* 1101100 */ + 0xd8, /* 1101100 */ + 0xd8, /* 1101100 */ + 0x78, /* 0111100 */ + 0x38, /* 0011100 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 252 0xfc '' */ + 0xd8, /* 1101100 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x6c, /* 0110110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 253 0xfd '' */ + 0x78, /* 0111100 */ + 0xcc, /* 1100110 */ + 0x18, /* 0001100 */ + 0x30, /* 0011000 */ + 0x64, /* 0110010 */ + 0xfc, /* 1111110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 254 0xfe '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x7c, /* 0111110 */ + 0x7c, /* 0111110 */ + 0x7c, /* 0111110 */ + 0x7c, /* 0111110 */ + 0x7c, /* 0111110 */ + 0x7c, /* 0111110 */ + 0x7c, /* 0111110 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + + /* 255 0xff '' */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + 0x00, /* 0000000 */ + +}; + + +const struct font_desc font_7x14 = { + .idx = FONT7x14_IDX, + .name = "7x14", + .width = 7, + .height = 14, + .data = fontdata_7x14, + .pref = 0, +}; diff --git a/lib/fonts/font_8x16.c b/lib/fonts/font_8x16.c new file mode 100644 index 000000000000..00a0c67a5c7d --- /dev/null +++ b/lib/fonts/font_8x16.c @@ -0,0 +1,4633 @@ +/**********************************************/ +/* */ +/* Font file generated by cpi2fnt */ +/* */ +/**********************************************/ + +#include +#include + +#define FONTDATAMAX 4096 + +static const unsigned char fontdata_8x16[FONTDATAMAX] = { + + /* 0 0x00 '^@' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 1 0x01 '^A' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x81, /* 10000001 */ + 0xa5, /* 10100101 */ + 0x81, /* 10000001 */ + 0x81, /* 10000001 */ + 0xbd, /* 10111101 */ + 0x99, /* 10011001 */ + 0x81, /* 10000001 */ + 0x81, /* 10000001 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 2 0x02 '^B' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0xff, /* 11111111 */ + 0xdb, /* 11011011 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xc3, /* 11000011 */ + 0xe7, /* 11100111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 3 0x03 '^C' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x6c, /* 01101100 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0x7c, /* 01111100 */ + 0x38, /* 00111000 */ + 0x10, /* 00010000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 4 0x04 '^D' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x7c, /* 01111100 */ + 0xfe, /* 11111110 */ + 0x7c, /* 01111100 */ + 0x38, /* 00111000 */ + 0x10, /* 00010000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 5 0x05 '^E' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0xe7, /* 11100111 */ + 0xe7, /* 11100111 */ + 0xe7, /* 11100111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 6 0x06 '^F' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 7 0x07 '^G' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 8 0x08 '^H' */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xe7, /* 11100111 */ + 0xc3, /* 11000011 */ + 0xc3, /* 11000011 */ + 0xe7, /* 11100111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + + /* 9 0x09 '^I' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x42, /* 01000010 */ + 0x42, /* 01000010 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 10 0x0a '^J' */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xc3, /* 11000011 */ + 0x99, /* 10011001 */ + 0xbd, /* 10111101 */ + 0xbd, /* 10111101 */ + 0x99, /* 10011001 */ + 0xc3, /* 11000011 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + + /* 11 0x0b '^K' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1e, /* 00011110 */ + 0x0e, /* 00001110 */ + 0x1a, /* 00011010 */ + 0x32, /* 00110010 */ + 0x78, /* 01111000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 12 0x0c '^L' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 13 0x0d '^M' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3f, /* 00111111 */ + 0x33, /* 00110011 */ + 0x3f, /* 00111111 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x70, /* 01110000 */ + 0xf0, /* 11110000 */ + 0xe0, /* 11100000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 14 0x0e '^N' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7f, /* 01111111 */ + 0x63, /* 01100011 */ + 0x7f, /* 01111111 */ + 0x63, /* 01100011 */ + 0x63, /* 01100011 */ + 0x63, /* 01100011 */ + 0x63, /* 01100011 */ + 0x67, /* 01100111 */ + 0xe7, /* 11100111 */ + 0xe6, /* 11100110 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 15 0x0f '^O' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xdb, /* 11011011 */ + 0x3c, /* 00111100 */ + 0xe7, /* 11100111 */ + 0x3c, /* 00111100 */ + 0xdb, /* 11011011 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 16 0x10 '^P' */ + 0x00, /* 00000000 */ + 0x80, /* 10000000 */ + 0xc0, /* 11000000 */ + 0xe0, /* 11100000 */ + 0xf0, /* 11110000 */ + 0xf8, /* 11111000 */ + 0xfe, /* 11111110 */ + 0xf8, /* 11111000 */ + 0xf0, /* 11110000 */ + 0xe0, /* 11100000 */ + 0xc0, /* 11000000 */ + 0x80, /* 10000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 17 0x11 '^Q' */ + 0x00, /* 00000000 */ + 0x02, /* 00000010 */ + 0x06, /* 00000110 */ + 0x0e, /* 00001110 */ + 0x1e, /* 00011110 */ + 0x3e, /* 00111110 */ + 0xfe, /* 11111110 */ + 0x3e, /* 00111110 */ + 0x1e, /* 00011110 */ + 0x0e, /* 00001110 */ + 0x06, /* 00000110 */ + 0x02, /* 00000010 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 18 0x12 '^R' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 19 0x13 '^S' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 20 0x14 '^T' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7f, /* 01111111 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0x7b, /* 01111011 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 21 0x15 '^U' */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0x60, /* 01100000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x0c, /* 00001100 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 22 0x16 '^V' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 23 0x17 '^W' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 24 0x18 '^X' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 25 0x19 '^Y' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 26 0x1a '^Z' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0xfe, /* 11111110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 27 0x1b '^[' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xfe, /* 11111110 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 28 0x1c '^\' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 29 0x1d '^]' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x28, /* 00101000 */ + 0x6c, /* 01101100 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x28, /* 00101000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 30 0x1e '^^' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x38, /* 00111000 */ + 0x7c, /* 01111100 */ + 0x7c, /* 01111100 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 31 0x1f '^_' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0x7c, /* 01111100 */ + 0x7c, /* 01111100 */ + 0x38, /* 00111000 */ + 0x38, /* 00111000 */ + 0x10, /* 00010000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 32 0x20 ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 33 0x21 '!' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 34 0x22 '"' */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x24, /* 00100100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 35 0x23 '#' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 36 0x24 '$' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc2, /* 11000010 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x86, /* 10000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 37 0x25 '%' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc2, /* 11000010 */ + 0xc6, /* 11000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc6, /* 11000110 */ + 0x86, /* 10000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 38 0x26 '&' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 39 0x27 ''' */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 40 0x28 '(' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 41 0x29 ')' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 42 0x2a '*' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0xff, /* 11111111 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 43 0x2b '+' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 44 0x2c ',' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 45 0x2d '-' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 46 0x2e '.' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 47 0x2f '/' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x02, /* 00000010 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0x80, /* 10000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 48 0x30 '0' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 49 0x31 '1' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x38, /* 00111000 */ + 0x78, /* 01111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 50 0x32 '2' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 51 0x33 '3' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x3c, /* 00111100 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 52 0x34 '4' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x0c, /* 00001100 */ + 0x1c, /* 00011100 */ + 0x3c, /* 00111100 */ + 0x6c, /* 01101100 */ + 0xcc, /* 11001100 */ + 0xfe, /* 11111110 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x1e, /* 00011110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 53 0x35 '5' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xfc, /* 11111100 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 54 0x36 '6' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xfc, /* 11111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 55 0x37 '7' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 56 0x38 '8' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 57 0x39 '9' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7e, /* 01111110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 58 0x3a ':' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 59 0x3b ';' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 60 0x3c '<' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x06, /* 00000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 61 0x3d '=' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 62 0x3e '>' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 63 0x3f '?' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 64 0x40 '@' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xde, /* 11011110 */ + 0xde, /* 11011110 */ + 0xde, /* 11011110 */ + 0xdc, /* 11011100 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 65 0x41 'A' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 66 0x42 'B' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfc, /* 11111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0xfc, /* 11111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 67 0x43 'C' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0xc2, /* 11000010 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc2, /* 11000010 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 68 0x44 'D' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xf8, /* 11111000 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0xf8, /* 11111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 69 0x45 'E' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x66, /* 01100110 */ + 0x62, /* 01100010 */ + 0x68, /* 01101000 */ + 0x78, /* 01111000 */ + 0x68, /* 01101000 */ + 0x60, /* 01100000 */ + 0x62, /* 01100010 */ + 0x66, /* 01100110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 70 0x46 'F' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x66, /* 01100110 */ + 0x62, /* 01100010 */ + 0x68, /* 01101000 */ + 0x78, /* 01111000 */ + 0x68, /* 01101000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 71 0x47 'G' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0xc2, /* 11000010 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xde, /* 11011110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x66, /* 01100110 */ + 0x3a, /* 00111010 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 72 0x48 'H' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 73 0x49 'I' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 74 0x4a 'J' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1e, /* 00011110 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 75 0x4b 'K' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xe6, /* 11100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0x78, /* 01111000 */ + 0x78, /* 01111000 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 76 0x4c 'L' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xf0, /* 11110000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x62, /* 01100010 */ + 0x66, /* 01100110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 77 0x4d 'M' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xee, /* 11101110 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xd6, /* 11010110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 78 0x4e 'N' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xe6, /* 11100110 */ + 0xf6, /* 11110110 */ + 0xfe, /* 11111110 */ + 0xde, /* 11011110 */ + 0xce, /* 11001110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 79 0x4f 'O' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 80 0x50 'P' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfc, /* 11111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 81 0x51 'Q' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xd6, /* 11010110 */ + 0xde, /* 11011110 */ + 0x7c, /* 01111100 */ + 0x0c, /* 00001100 */ + 0x0e, /* 00001110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 82 0x52 'R' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfc, /* 11111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 83 0x53 'S' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x60, /* 01100000 */ + 0x38, /* 00111000 */ + 0x0c, /* 00001100 */ + 0x06, /* 00000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 84 0x54 'T' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x5a, /* 01011010 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 85 0x55 'U' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 86 0x56 'V' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x10, /* 00010000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 87 0x57 'W' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xfe, /* 11111110 */ + 0xee, /* 11101110 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 88 0x58 'X' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x7c, /* 01111100 */ + 0x38, /* 00111000 */ + 0x38, /* 00111000 */ + 0x7c, /* 01111100 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 89 0x59 'Y' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 90 0x5a 'Z' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0x86, /* 10000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc2, /* 11000010 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 91 0x5b '[' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 92 0x5c '\' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x80, /* 10000000 */ + 0xc0, /* 11000000 */ + 0xe0, /* 11100000 */ + 0x70, /* 01110000 */ + 0x38, /* 00111000 */ + 0x1c, /* 00011100 */ + 0x0e, /* 00001110 */ + 0x06, /* 00000110 */ + 0x02, /* 00000010 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 93 0x5d ']' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 94 0x5e '^' */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 95 0x5f '_' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 96 0x60 '`' */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 97 0x61 'a' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 98 0x62 'b' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xe0, /* 11100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x78, /* 01111000 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 99 0x63 'c' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 100 0x64 'd' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1c, /* 00011100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x3c, /* 00111100 */ + 0x6c, /* 01101100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 101 0x65 'e' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 102 0x66 'f' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1c, /* 00011100 */ + 0x36, /* 00110110 */ + 0x32, /* 00110010 */ + 0x30, /* 00110000 */ + 0x78, /* 01111000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 103 0x67 'g' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x7c, /* 01111100 */ + 0x0c, /* 00001100 */ + 0xcc, /* 11001100 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + + /* 104 0x68 'h' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xe0, /* 11100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x6c, /* 01101100 */ + 0x76, /* 01110110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 105 0x69 'i' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 106 0x6a 'j' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x00, /* 00000000 */ + 0x0e, /* 00001110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 107 0x6b 'k' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xe0, /* 11100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0x78, /* 01111000 */ + 0x78, /* 01111000 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 108 0x6c 'l' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 109 0x6d 'm' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xec, /* 11101100 */ + 0xfe, /* 11111110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 110 0x6e 'n' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 111 0x6f 'o' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 112 0x70 'p' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + + /* 113 0x71 'q' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x7c, /* 01111100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x1e, /* 00011110 */ + 0x00, /* 00000000 */ + + /* 114 0x72 'r' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x76, /* 01110110 */ + 0x66, /* 01100110 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 115 0x73 's' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0x60, /* 01100000 */ + 0x38, /* 00111000 */ + 0x0c, /* 00001100 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 116 0x74 't' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0xfc, /* 11111100 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x36, /* 00110110 */ + 0x1c, /* 00011100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 117 0x75 'u' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 118 0x76 'v' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 119 0x77 'w' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 120 0x78 'x' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x38, /* 00111000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 121 0x79 'y' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7e, /* 01111110 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0xf8, /* 11111000 */ + 0x00, /* 00000000 */ + + /* 122 0x7a 'z' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xcc, /* 11001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 123 0x7b '{' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x0e, /* 00001110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x70, /* 01110000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x0e, /* 00001110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 124 0x7c '|' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 125 0x7d '}' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x70, /* 01110000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x0e, /* 00001110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 126 0x7e '~' */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 127 0x7f '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 128 0x80 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0xc2, /* 11000010 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc2, /* 11000010 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 129 0x81 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 130 0x82 '' */ + 0x00, /* 00000000 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 131 0x83 '' */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 132 0x84 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 133 0x85 '' */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 134 0x86 '' */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 135 0x87 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x18, /* 00011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 136 0x88 '' */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 137 0x89 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 138 0x8a '' */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 139 0x8b '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 140 0x8c '' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 141 0x8d '' */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 142 0x8e '' */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 143 0x8f '' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 144 0x90 '' */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x66, /* 01100110 */ + 0x62, /* 01100010 */ + 0x68, /* 01101000 */ + 0x78, /* 01111000 */ + 0x68, /* 01101000 */ + 0x62, /* 01100010 */ + 0x66, /* 01100110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 145 0x91 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xec, /* 11101100 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x7e, /* 01111110 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0x6e, /* 01101110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 146 0x92 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3e, /* 00111110 */ + 0x6c, /* 01101100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xfe, /* 11111110 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xce, /* 11001110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 147 0x93 '' */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 148 0x94 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 149 0x95 '' */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 150 0x96 '' */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x78, /* 01111000 */ + 0xcc, /* 11001100 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 151 0x97 '' */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 152 0x98 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7e, /* 01111110 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + + /* 153 0x99 '' */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 154 0x9a '' */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 155 0x9b '' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 156 0x9c '' */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x64, /* 01100100 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xe6, /* 11100110 */ + 0xfc, /* 11111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 157 0x9d '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 158 0x9e '' */ + 0x00, /* 00000000 */ + 0xf8, /* 11111000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xf8, /* 11111000 */ + 0xc4, /* 11000100 */ + 0xcc, /* 11001100 */ + 0xde, /* 11011110 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 159 0x9f '' */ + 0x00, /* 00000000 */ + 0x0e, /* 00001110 */ + 0x1b, /* 00011011 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xd8, /* 11011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 160 0xa0 '' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 161 0xa1 '' */ + 0x00, /* 00000000 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 162 0xa2 '' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 163 0xa3 '' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 164 0xa4 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 165 0xa5 '' */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xe6, /* 11100110 */ + 0xf6, /* 11110110 */ + 0xfe, /* 11111110 */ + 0xde, /* 11011110 */ + 0xce, /* 11001110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 166 0xa6 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x3e, /* 00111110 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 167 0xa7 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 168 0xa8 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 169 0xa9 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 170 0xaa '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 171 0xab '' */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0xe0, /* 11100000 */ + 0x62, /* 01100010 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xdc, /* 11011100 */ + 0x86, /* 10000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x3e, /* 00111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 172 0xac '' */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0xe0, /* 11100000 */ + 0x62, /* 01100010 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x66, /* 01100110 */ + 0xce, /* 11001110 */ + 0x9a, /* 10011010 */ + 0x3f, /* 00111111 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 173 0xad '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 174 0xae '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x36, /* 00110110 */ + 0x6c, /* 01101100 */ + 0xd8, /* 11011000 */ + 0x6c, /* 01101100 */ + 0x36, /* 00110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 175 0xaf '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xd8, /* 11011000 */ + 0x6c, /* 01101100 */ + 0x36, /* 00110110 */ + 0x6c, /* 01101100 */ + 0xd8, /* 11011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 176 0xb0 '' */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + + /* 177 0xb1 '' */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + + /* 178 0xb2 '' */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + + /* 179 0xb3 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 180 0xb4 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 181 0xb5 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 182 0xb6 '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf6, /* 11110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 183 0xb7 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 184 0xb8 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 185 0xb9 '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf6, /* 11110110 */ + 0x06, /* 00000110 */ + 0xf6, /* 11110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 186 0xba '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 187 0xbb '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x06, /* 00000110 */ + 0xf6, /* 11110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 188 0xbc '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf6, /* 11110110 */ + 0x06, /* 00000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 189 0xbd '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 190 0xbe '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 191 0xbf '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 192 0xc0 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 193 0xc1 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 194 0xc2 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 195 0xc3 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 196 0xc4 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 197 0xc5 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 198 0xc6 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 199 0xc7 '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x37, /* 00110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 200 0xc8 '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x37, /* 00110111 */ + 0x30, /* 00110000 */ + 0x3f, /* 00111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 201 0xc9 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3f, /* 00111111 */ + 0x30, /* 00110000 */ + 0x37, /* 00110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 202 0xca '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf7, /* 11110111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 203 0xcb '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xf7, /* 11110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 204 0xcc '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x37, /* 00110111 */ + 0x30, /* 00110000 */ + 0x37, /* 00110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 205 0xcd '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 206 0xce '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf7, /* 11110111 */ + 0x00, /* 00000000 */ + 0xf7, /* 11110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 207 0xcf '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 208 0xd0 '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 209 0xd1 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 210 0xd2 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 211 0xd3 '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x3f, /* 00111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 212 0xd4 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 213 0xd5 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 214 0xd6 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3f, /* 00111111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 215 0xd7 '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xff, /* 11111111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 216 0xd8 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 217 0xd9 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 218 0xda '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 219 0xdb '' */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + + /* 220 0xdc '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + + /* 221 0xdd '' */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + + /* 222 0xde '' */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + + /* 223 0xdf '' */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 224 0xe0 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0xdc, /* 11011100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 225 0xe1 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xd8, /* 11011000 */ + 0xcc, /* 11001100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xcc, /* 11001100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 226 0xe2 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 227 0xe3 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 228 0xe4 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 229 0xe5 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 230 0xe6 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + + /* 231 0xe7 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 232 0xe8 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 233 0xe9 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 234 0xea '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0xee, /* 11101110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 235 0xeb '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1e, /* 00011110 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x3e, /* 00111110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 236 0xec '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 237 0xed '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x03, /* 00000011 */ + 0x06, /* 00000110 */ + 0x7e, /* 01111110 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0xf3, /* 11110011 */ + 0x7e, /* 01111110 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 238 0xee '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1c, /* 00011100 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x7c, /* 01111100 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x1c, /* 00011100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 239 0xef '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 240 0xf0 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 241 0xf1 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 242 0xf2 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 243 0xf3 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 244 0xf4 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x0e, /* 00001110 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 245 0xf5 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 246 0xf6 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 247 0xf7 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 248 0xf8 '' */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 249 0xf9 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 250 0xfa '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 251 0xfb '' */ + 0x00, /* 00000000 */ + 0x0f, /* 00001111 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0xec, /* 11101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x3c, /* 00111100 */ + 0x1c, /* 00011100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 252 0xfc '' */ + 0x00, /* 00000000 */ + 0x6c, /* 01101100 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 253 0xfd '' */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x32, /* 00110010 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 254 0xfe '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 255 0xff '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + +}; + + +const struct font_desc font_vga_8x16 = { + .idx = VGA8x16_IDX, + .name = "VGA8x16", + .width = 8, + .height = 16, + .data = fontdata_8x16, + .pref = 0, +}; +EXPORT_SYMBOL(font_vga_8x16); diff --git a/lib/fonts/font_8x8.c b/lib/fonts/font_8x8.c new file mode 100644 index 000000000000..9f56efe2cee7 --- /dev/null +++ b/lib/fonts/font_8x8.c @@ -0,0 +1,2583 @@ +/**********************************************/ +/* */ +/* Font file generated by cpi2fnt */ +/* */ +/**********************************************/ + +#include + +#define FONTDATAMAX 2048 + +static const unsigned char fontdata_8x8[FONTDATAMAX] = { + + /* 0 0x00 '^@' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 1 0x01 '^A' */ + 0x7e, /* 01111110 */ + 0x81, /* 10000001 */ + 0xa5, /* 10100101 */ + 0x81, /* 10000001 */ + 0xbd, /* 10111101 */ + 0x99, /* 10011001 */ + 0x81, /* 10000001 */ + 0x7e, /* 01111110 */ + + /* 2 0x02 '^B' */ + 0x7e, /* 01111110 */ + 0xff, /* 11111111 */ + 0xdb, /* 11011011 */ + 0xff, /* 11111111 */ + 0xc3, /* 11000011 */ + 0xe7, /* 11100111 */ + 0xff, /* 11111111 */ + 0x7e, /* 01111110 */ + + /* 3 0x03 '^C' */ + 0x6c, /* 01101100 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0x7c, /* 01111100 */ + 0x38, /* 00111000 */ + 0x10, /* 00010000 */ + 0x00, /* 00000000 */ + + /* 4 0x04 '^D' */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x7c, /* 01111100 */ + 0xfe, /* 11111110 */ + 0x7c, /* 01111100 */ + 0x38, /* 00111000 */ + 0x10, /* 00010000 */ + 0x00, /* 00000000 */ + + /* 5 0x05 '^E' */ + 0x38, /* 00111000 */ + 0x7c, /* 01111100 */ + 0x38, /* 00111000 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xd6, /* 11010110 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + + /* 6 0x06 '^F' */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x7c, /* 01111100 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0x7c, /* 01111100 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + + /* 7 0x07 '^G' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 8 0x08 '^H' */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xe7, /* 11100111 */ + 0xc3, /* 11000011 */ + 0xc3, /* 11000011 */ + 0xe7, /* 11100111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + + /* 9 0x09 '^I' */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x42, /* 01000010 */ + 0x42, /* 01000010 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 10 0x0a '^J' */ + 0xff, /* 11111111 */ + 0xc3, /* 11000011 */ + 0x99, /* 10011001 */ + 0xbd, /* 10111101 */ + 0xbd, /* 10111101 */ + 0x99, /* 10011001 */ + 0xc3, /* 11000011 */ + 0xff, /* 11111111 */ + + /* 11 0x0b '^K' */ + 0x0f, /* 00001111 */ + 0x07, /* 00000111 */ + 0x0f, /* 00001111 */ + 0x7d, /* 01111101 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x78, /* 01111000 */ + + /* 12 0x0c '^L' */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + + /* 13 0x0d '^M' */ + 0x3f, /* 00111111 */ + 0x33, /* 00110011 */ + 0x3f, /* 00111111 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x70, /* 01110000 */ + 0xf0, /* 11110000 */ + 0xe0, /* 11100000 */ + + /* 14 0x0e '^N' */ + 0x7f, /* 01111111 */ + 0x63, /* 01100011 */ + 0x7f, /* 01111111 */ + 0x63, /* 01100011 */ + 0x63, /* 01100011 */ + 0x67, /* 01100111 */ + 0xe6, /* 11100110 */ + 0xc0, /* 11000000 */ + + /* 15 0x0f '^O' */ + 0x18, /* 00011000 */ + 0xdb, /* 11011011 */ + 0x3c, /* 00111100 */ + 0xe7, /* 11100111 */ + 0xe7, /* 11100111 */ + 0x3c, /* 00111100 */ + 0xdb, /* 11011011 */ + 0x18, /* 00011000 */ + + /* 16 0x10 '^P' */ + 0x80, /* 10000000 */ + 0xe0, /* 11100000 */ + 0xf8, /* 11111000 */ + 0xfe, /* 11111110 */ + 0xf8, /* 11111000 */ + 0xe0, /* 11100000 */ + 0x80, /* 10000000 */ + 0x00, /* 00000000 */ + + /* 17 0x11 '^Q' */ + 0x02, /* 00000010 */ + 0x0e, /* 00001110 */ + 0x3e, /* 00111110 */ + 0xfe, /* 11111110 */ + 0x3e, /* 00111110 */ + 0x0e, /* 00001110 */ + 0x02, /* 00000010 */ + 0x00, /* 00000000 */ + + /* 18 0x12 '^R' */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + + /* 19 0x13 '^S' */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + + /* 20 0x14 '^T' */ + 0x7f, /* 01111111 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0x7b, /* 01111011 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x00, /* 00000000 */ + + /* 21 0x15 '^U' */ + 0x3e, /* 00111110 */ + 0x61, /* 01100001 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x86, /* 10000110 */ + 0x7c, /* 01111100 */ + + /* 22 0x16 '^V' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + + /* 23 0x17 '^W' */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + + /* 24 0x18 '^X' */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 25 0x19 '^Y' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 26 0x1a '^Z' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0xfe, /* 11111110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 27 0x1b '^[' */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xfe, /* 11111110 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 28 0x1c '^\' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 29 0x1d '^]' */ + 0x00, /* 00000000 */ + 0x24, /* 00100100 */ + 0x66, /* 01100110 */ + 0xff, /* 11111111 */ + 0x66, /* 01100110 */ + 0x24, /* 00100100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 30 0x1e '^^' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 31 0x1f '^_' */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0x7e, /* 01111110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 32 0x20 ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 33 0x21 '!' */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 34 0x22 '"' */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x24, /* 00100100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 35 0x23 '#' */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + + /* 36 0x24 '$' */ + 0x18, /* 00011000 */ + 0x3e, /* 00111110 */ + 0x60, /* 01100000 */ + 0x3c, /* 00111100 */ + 0x06, /* 00000110 */ + 0x7c, /* 01111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 37 0x25 '%' */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xcc, /* 11001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x66, /* 01100110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 38 0x26 '&' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 39 0x27 ''' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 40 0x28 '(' */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x00, /* 00000000 */ + + /* 41 0x29 ')' */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + + /* 42 0x2a '*' */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0xff, /* 11111111 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 43 0x2b '+' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 44 0x2c ',' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + + /* 45 0x2d '-' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 46 0x2e '.' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 47 0x2f '/' */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0x80, /* 10000000 */ + 0x00, /* 00000000 */ + + /* 48 0x30 '0' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xd6, /* 11010110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + + /* 49 0x31 '1' */ + 0x18, /* 00011000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + + /* 50 0x32 '2' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0x06, /* 00000110 */ + 0x1c, /* 00011100 */ + 0x30, /* 00110000 */ + 0x66, /* 01100110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + + /* 51 0x33 '3' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0x06, /* 00000110 */ + 0x3c, /* 00111100 */ + 0x06, /* 00000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 52 0x34 '4' */ + 0x1c, /* 00011100 */ + 0x3c, /* 00111100 */ + 0x6c, /* 01101100 */ + 0xcc, /* 11001100 */ + 0xfe, /* 11111110 */ + 0x0c, /* 00001100 */ + 0x1e, /* 00011110 */ + 0x00, /* 00000000 */ + + /* 53 0x35 '5' */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xfc, /* 11111100 */ + 0x06, /* 00000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 54 0x36 '6' */ + 0x38, /* 00111000 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0xfc, /* 11111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 55 0x37 '7' */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + + /* 56 0x38 '8' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 57 0x39 '9' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7e, /* 01111110 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + + /* 58 0x3a ':' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 59 0x3b ';' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + + /* 60 0x3c '<' */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x06, /* 00000110 */ + 0x00, /* 00000000 */ + + /* 61 0x3d '=' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 62 0x3e '>' */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x00, /* 00000000 */ + + /* 63 0x3f '?' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 64 0x40 '@' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xde, /* 11011110 */ + 0xde, /* 11011110 */ + 0xde, /* 11011110 */ + 0xc0, /* 11000000 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + + /* 65 0x41 'A' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 66 0x42 'B' */ + 0xfc, /* 11111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0xfc, /* 11111100 */ + 0x00, /* 00000000 */ + + /* 67 0x43 'C' */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 68 0x44 'D' */ + 0xf8, /* 11111000 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0xf8, /* 11111000 */ + 0x00, /* 00000000 */ + + /* 69 0x45 'E' */ + 0xfe, /* 11111110 */ + 0x62, /* 01100010 */ + 0x68, /* 01101000 */ + 0x78, /* 01111000 */ + 0x68, /* 01101000 */ + 0x62, /* 01100010 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + + /* 70 0x46 'F' */ + 0xfe, /* 11111110 */ + 0x62, /* 01100010 */ + 0x68, /* 01101000 */ + 0x78, /* 01111000 */ + 0x68, /* 01101000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + + /* 71 0x47 'G' */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xce, /* 11001110 */ + 0x66, /* 01100110 */ + 0x3a, /* 00111010 */ + 0x00, /* 00000000 */ + + /* 72 0x48 'H' */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 73 0x49 'I' */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 74 0x4a 'J' */ + 0x1e, /* 00011110 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + + /* 75 0x4b 'K' */ + 0xe6, /* 11100110 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0x78, /* 01111000 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + + /* 76 0x4c 'L' */ + 0xf0, /* 11110000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x62, /* 01100010 */ + 0x66, /* 01100110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + + /* 77 0x4d 'M' */ + 0xc6, /* 11000110 */ + 0xee, /* 11101110 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xd6, /* 11010110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 78 0x4e 'N' */ + 0xc6, /* 11000110 */ + 0xe6, /* 11100110 */ + 0xf6, /* 11110110 */ + 0xde, /* 11011110 */ + 0xce, /* 11001110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 79 0x4f 'O' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 80 0x50 'P' */ + 0xfc, /* 11111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + + /* 81 0x51 'Q' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xce, /* 11001110 */ + 0x7c, /* 01111100 */ + 0x0e, /* 00001110 */ + + /* 82 0x52 'R' */ + 0xfc, /* 11111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + + /* 83 0x53 'S' */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 84 0x54 'T' */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x5a, /* 01011010 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 85 0x55 'U' */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 86 0x56 'V' */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + + /* 87 0x57 'W' */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + + /* 88 0x58 'X' */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 89 0x59 'Y' */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 90 0x5a 'Z' */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0x8c, /* 10001100 */ + 0x18, /* 00011000 */ + 0x32, /* 00110010 */ + 0x66, /* 01100110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + + /* 91 0x5b '[' */ + 0x3c, /* 00111100 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 92 0x5c '\' */ + 0xc0, /* 11000000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x06, /* 00000110 */ + 0x02, /* 00000010 */ + 0x00, /* 00000000 */ + + /* 93 0x5d ']' */ + 0x3c, /* 00111100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 94 0x5e '^' */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 95 0x5f '_' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + + /* 96 0x60 '`' */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 97 0x61 'a' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 98 0x62 'b' */ + 0xe0, /* 11100000 */ + 0x60, /* 01100000 */ + 0x7c, /* 01111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + + /* 99 0x63 'c' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 100 0x64 'd' */ + 0x1c, /* 00011100 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 101 0x65 'e' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 102 0x66 'f' */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x60, /* 01100000 */ + 0xf8, /* 11111000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + + /* 103 0x67 'g' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x7c, /* 01111100 */ + 0x0c, /* 00001100 */ + 0xf8, /* 11111000 */ + + /* 104 0x68 'h' */ + 0xe0, /* 11100000 */ + 0x60, /* 01100000 */ + 0x6c, /* 01101100 */ + 0x76, /* 01110110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + + /* 105 0x69 'i' */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 106 0x6a 'j' */ + 0x06, /* 00000110 */ + 0x00, /* 00000000 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + + /* 107 0x6b 'k' */ + 0xe0, /* 11100000 */ + 0x60, /* 01100000 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0x78, /* 01111000 */ + 0x6c, /* 01101100 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + + /* 108 0x6c 'l' */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 109 0x6d 'm' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xec, /* 11101100 */ + 0xfe, /* 11111110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0x00, /* 00000000 */ + + /* 110 0x6e 'n' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + + /* 111 0x6f 'o' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 112 0x70 'p' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + + /* 113 0x71 'q' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x7c, /* 01111100 */ + 0x0c, /* 00001100 */ + 0x1e, /* 00011110 */ + + /* 114 0x72 'r' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x76, /* 01110110 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + + /* 115 0x73 's' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x06, /* 00000110 */ + 0xfc, /* 11111100 */ + 0x00, /* 00000000 */ + + /* 116 0x74 't' */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0xfc, /* 11111100 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x36, /* 00110110 */ + 0x1c, /* 00011100 */ + 0x00, /* 00000000 */ + + /* 117 0x75 'u' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 118 0x76 'v' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + + /* 119 0x77 'w' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + + /* 120 0x78 'x' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 121 0x79 'y' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7e, /* 01111110 */ + 0x06, /* 00000110 */ + 0xfc, /* 11111100 */ + + /* 122 0x7a 'z' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x4c, /* 01001100 */ + 0x18, /* 00011000 */ + 0x32, /* 00110010 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + + /* 123 0x7b '{' */ + 0x0e, /* 00001110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x70, /* 01110000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x0e, /* 00001110 */ + 0x00, /* 00000000 */ + + /* 124 0x7c '|' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 125 0x7d '}' */ + 0x70, /* 01110000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x0e, /* 00001110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + + /* 126 0x7e '~' */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 127 0x7f '' */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + + /* 128 0x80 '' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x0c, /* 00001100 */ + 0x78, /* 01111000 */ + + /* 129 0x81 '' */ + 0xcc, /* 11001100 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 130 0x82 '' */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 131 0x83 '' */ + 0x7c, /* 01111100 */ + 0x82, /* 10000010 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 132 0x84 '' */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 133 0x85 '' */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 134 0x86 '' */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 135 0x87 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0x7e, /* 01111110 */ + 0x0c, /* 00001100 */ + 0x38, /* 00111000 */ + + /* 136 0x88 '' */ + 0x7c, /* 01111100 */ + 0x82, /* 10000010 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 137 0x89 '' */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 138 0x8a '' */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 139 0x8b '' */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 140 0x8c '' */ + 0x7c, /* 01111100 */ + 0x82, /* 10000010 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 141 0x8d '' */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 142 0x8e '' */ + 0xc6, /* 11000110 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 143 0x8f '' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 144 0x90 '' */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xf8, /* 11111000 */ + 0xc0, /* 11000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + + /* 145 0x91 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0xd8, /* 11011000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + + /* 146 0x92 '' */ + 0x3e, /* 00111110 */ + 0x6c, /* 01101100 */ + 0xcc, /* 11001100 */ + 0xfe, /* 11111110 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xce, /* 11001110 */ + 0x00, /* 00000000 */ + + /* 147 0x93 '' */ + 0x7c, /* 01111100 */ + 0x82, /* 10000010 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 148 0x94 '' */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 149 0x95 '' */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 150 0x96 '' */ + 0x78, /* 01111000 */ + 0x84, /* 10000100 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 151 0x97 '' */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 152 0x98 '' */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7e, /* 01111110 */ + 0x06, /* 00000110 */ + 0xfc, /* 11111100 */ + + /* 153 0x99 '' */ + 0xc6, /* 11000110 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + + /* 154 0x9a '' */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 155 0x9b '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 156 0x9c '' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x64, /* 01100100 */ + 0xf0, /* 11110000 */ + 0x60, /* 01100000 */ + 0x66, /* 01100110 */ + 0xfc, /* 11111100 */ + 0x00, /* 00000000 */ + + /* 157 0x9d '' */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 158 0x9e '' */ + 0xf8, /* 11111000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xfa, /* 11111010 */ + 0xc6, /* 11000110 */ + 0xcf, /* 11001111 */ + 0xc6, /* 11000110 */ + 0xc7, /* 11000111 */ + + /* 159 0x9f '' */ + 0x0e, /* 00001110 */ + 0x1b, /* 00011011 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0xd8, /* 11011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + + /* 160 0xa0 '' */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 161 0xa1 '' */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 162 0xa2 '' */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 163 0xa3 '' */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 164 0xa4 '' */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + + /* 165 0xa5 '' */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0xe6, /* 11100110 */ + 0xf6, /* 11110110 */ + 0xde, /* 11011110 */ + 0xce, /* 11001110 */ + 0x00, /* 00000000 */ + + /* 166 0xa6 '' */ + 0x3c, /* 00111100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x3e, /* 00111110 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 167 0xa7 '' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 168 0xa8 '' */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x63, /* 01100011 */ + 0x3e, /* 00111110 */ + 0x00, /* 00000000 */ + + /* 169 0xa9 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 170 0xaa '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 171 0xab '' */ + 0x63, /* 01100011 */ + 0xe6, /* 11100110 */ + 0x6c, /* 01101100 */ + 0x7e, /* 01111110 */ + 0x33, /* 00110011 */ + 0x66, /* 01100110 */ + 0xcc, /* 11001100 */ + 0x0f, /* 00001111 */ + + /* 172 0xac '' */ + 0x63, /* 01100011 */ + 0xe6, /* 11100110 */ + 0x6c, /* 01101100 */ + 0x7a, /* 01111010 */ + 0x36, /* 00110110 */ + 0x6a, /* 01101010 */ + 0xdf, /* 11011111 */ + 0x06, /* 00000110 */ + + /* 173 0xad '' */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 174 0xae '' */ + 0x00, /* 00000000 */ + 0x33, /* 00110011 */ + 0x66, /* 01100110 */ + 0xcc, /* 11001100 */ + 0x66, /* 01100110 */ + 0x33, /* 00110011 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 175 0xaf '' */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0x66, /* 01100110 */ + 0x33, /* 00110011 */ + 0x66, /* 01100110 */ + 0xcc, /* 11001100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 176 0xb0 '' */ + 0x22, /* 00100010 */ + 0x88, /* 10001000 */ + 0x22, /* 00100010 */ + 0x88, /* 10001000 */ + 0x22, /* 00100010 */ + 0x88, /* 10001000 */ + 0x22, /* 00100010 */ + 0x88, /* 10001000 */ + + /* 177 0xb1 '' */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + + /* 178 0xb2 '' */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + + /* 179 0xb3 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 180 0xb4 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 181 0xb5 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 182 0xb6 '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf6, /* 11110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 183 0xb7 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 184 0xb8 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 185 0xb9 '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf6, /* 11110110 */ + 0x06, /* 00000110 */ + 0xf6, /* 11110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 186 0xba '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 187 0xbb '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x06, /* 00000110 */ + 0xf6, /* 11110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 188 0xbc '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf6, /* 11110110 */ + 0x06, /* 00000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 189 0xbd '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 190 0xbe '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 191 0xbf '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 192 0xc0 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 193 0xc1 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 194 0xc2 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 195 0xc3 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 196 0xc4 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 197 0xc5 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 198 0xc6 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 199 0xc7 '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x37, /* 00110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 200 0xc8 '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x37, /* 00110111 */ + 0x30, /* 00110000 */ + 0x3f, /* 00111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 201 0xc9 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3f, /* 00111111 */ + 0x30, /* 00110000 */ + 0x37, /* 00110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 202 0xca '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf7, /* 11110111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 203 0xcb '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xf7, /* 11110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 204 0xcc '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x37, /* 00110111 */ + 0x30, /* 00110000 */ + 0x37, /* 00110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 205 0xcd '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 206 0xce '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf7, /* 11110111 */ + 0x00, /* 00000000 */ + 0xf7, /* 11110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 207 0xcf '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 208 0xd0 '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 209 0xd1 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 210 0xd2 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 211 0xd3 '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x3f, /* 00111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 212 0xd4 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 213 0xd5 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 214 0xd6 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3f, /* 00111111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 215 0xd7 '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xff, /* 11111111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 216 0xd8 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 217 0xd9 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 218 0xda '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 219 0xdb '' */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + + /* 220 0xdc '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + + /* 221 0xdd '' */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + + /* 222 0xde '' */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + + /* 223 0xdf '' */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 224 0xe0 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0xc8, /* 11001000 */ + 0xdc, /* 11011100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 225 0xe1 '' */ + 0x78, /* 01111000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xd8, /* 11011000 */ + 0xcc, /* 11001100 */ + 0xc6, /* 11000110 */ + 0xcc, /* 11001100 */ + 0x00, /* 00000000 */ + + /* 226 0xe2 '' */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + + /* 227 0xe3 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + + /* 228 0xe4 '' */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + + /* 229 0xe5 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + + /* 230 0xe6 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0xc0, /* 11000000 */ + + /* 231 0xe7 '' */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 232 0xe8 '' */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + + /* 233 0xe9 '' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + + /* 234 0xea '' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0xee, /* 11101110 */ + 0x00, /* 00000000 */ + + /* 235 0xeb '' */ + 0x0e, /* 00001110 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x3e, /* 00111110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 236 0xec '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 237 0xed '' */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x7e, /* 01111110 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0x7e, /* 01111110 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + + /* 238 0xee '' */ + 0x1e, /* 00011110 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x7e, /* 01111110 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x1e, /* 00011110 */ + 0x00, /* 00000000 */ + + /* 239 0xef '' */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 240 0xf0 '' */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 241 0xf1 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + + /* 242 0xf2 '' */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + + /* 243 0xf3 '' */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + + /* 244 0xf4 '' */ + 0x0e, /* 00001110 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 245 0xf5 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0x70, /* 01110000 */ + + /* 246 0xf6 '' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 247 0xf7 '' */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 248 0xf8 '' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 249 0xf9 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 250 0xfa '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 251 0xfb '' */ + 0x0f, /* 00001111 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0xec, /* 11101100 */ + 0x6c, /* 01101100 */ + 0x3c, /* 00111100 */ + 0x1c, /* 00011100 */ + + /* 252 0xfc '' */ + 0x6c, /* 01101100 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 253 0xfd '' */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 254 0xfe '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 255 0xff '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + +}; + + +const struct font_desc font_vga_8x8 = { + .idx = VGA8x8_IDX, + .name = "VGA8x8", + .width = 8, + .height = 8, + .data = fontdata_8x8, + .pref = 0, +}; diff --git a/lib/fonts/font_acorn_8x8.c b/lib/fonts/font_acorn_8x8.c new file mode 100644 index 000000000000..639e31ae1100 --- /dev/null +++ b/lib/fonts/font_acorn_8x8.c @@ -0,0 +1,275 @@ +/* Acorn-like font definition, with PC graphics characters */ + +#include + +static const unsigned char acorndata_8x8[] = { +/* 00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ^@ */ +/* 01 */ 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e, /* ^A */ +/* 02 */ 0x7e, 0xff, 0xbd, 0xff, 0xc3, 0xe7, 0xff, 0x7e, /* ^B */ +/* 03 */ 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, /* ^C */ +/* 04 */ 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, /* ^D */ +/* 05 */ 0x00, 0x18, 0x3c, 0xe7, 0xe7, 0x3c, 0x18, 0x00, /* ^E */ +/* 06 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* 07 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* 08 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* 09 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* 0A */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* 0B */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* 0C */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* 0D */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* 0E */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* 0F */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* 10 */ 0x00, 0x60, 0x78, 0x7e, 0x7e, 0x78, 0x60, 0x00, /* |> */ +/* 11 */ 0x00, 0x06, 0x1e, 0x7e, 0x7e, 0x1e, 0x06, 0x00, /* <| */ +/* 12 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* 13 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* 14 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* 15 */ 0x3c, 0x60, 0x3c, 0x66, 0x3c, 0x06, 0x3c, 0x00, +/* 16 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* 17 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* 18 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* 19 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* 1A */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* 1B */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* 1C */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* 1D */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* 1E */ 0x00, 0x18, 0x18, 0x3c, 0x3c, 0x7e, 0x7e, 0x00, /* /\ */ +/* 1F */ 0x00, 0x7e, 0x7e, 0x3c, 0x3c, 0x18, 0x18, 0x00, /* \/ */ +/* 20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* */ +/* 21 */ 0x18, 0x3c, 0x3c, 0x18, 0x18, 0x00, 0x18, 0x00, /* ! */ +/* 22 */ 0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, /* " */ +/* 23 */ 0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00, /* # */ +/* 24 */ 0x0C, 0x3F, 0x68, 0x3E, 0x0B, 0x7E, 0x18, 0x00, /* $ */ +/* 25 */ 0x60, 0x66, 0x0C, 0x18, 0x30, 0x66, 0x06, 0x00, /* % */ +/* 26 */ 0x38, 0x6C, 0x6C, 0x38, 0x6D, 0x66, 0x3B, 0x00, /* & */ +/* 27 */ 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, /* ' */ +/* 28 */ 0x0C, 0x18, 0x30, 0x30, 0x30, 0x18, 0x0C, 0x00, /* ( */ +/* 29 */ 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x18, 0x30, 0x00, /* ) */ +/* 2A */ 0x00, 0x18, 0x7E, 0x3C, 0x7E, 0x18, 0x00, 0x00, /* * */ +/* 2B */ 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00, /* + */ +/* 2C */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, /* , */ +/* 2D */ 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, /* - */ +/* 2E */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, /* . */ +/* 2F */ 0x00, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x00, 0x00, /* / */ +/* 30 */ 0x3C, 0x66, 0x6E, 0x7E, 0x76, 0x66, 0x3C, 0x00, /* 0 */ +/* 31 */ 0x18, 0x38, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x00, /* 1 */ +/* 32 */ 0x3C, 0x66, 0x06, 0x0C, 0x18, 0x30, 0x7E, 0x00, /* 2 */ +/* 33 */ 0x3C, 0x66, 0x06, 0x1C, 0x06, 0x66, 0x3C, 0x00, /* 3 */ +/* 34 */ 0x0C, 0x1C, 0x3C, 0x6C, 0x7E, 0x0C, 0x0C, 0x00, /* 4 */ +/* 35 */ 0x7E, 0x60, 0x7C, 0x06, 0x06, 0x66, 0x3C, 0x00, /* 5 */ +/* 36 */ 0x1C, 0x30, 0x60, 0x7C, 0x66, 0x66, 0x3C, 0x00, /* 6 */ +/* 37 */ 0x7E, 0x06, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x00, /* 7 */ +/* 38 */ 0x3C, 0x66, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00, /* 8 */ +/* 39 */ 0x3C, 0x66, 0x66, 0x3E, 0x06, 0x0C, 0x38, 0x00, /* 9 */ +/* 3A */ 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, /* : */ +/* 3B */ 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x30, /* ; */ +/* 3C */ 0x0C, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0C, 0x00, /* < */ +/* 3D */ 0x00, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x00, 0x00, /* = */ +/* 3E */ 0x30, 0x18, 0x0C, 0x06, 0x0C, 0x18, 0x30, 0x00, /* > */ +/* 3F */ 0x3C, 0x66, 0x0C, 0x18, 0x18, 0x00, 0x18, 0x00, /* ? */ +/* 40 */ 0x3C, 0x66, 0x6E, 0x6A, 0x6E, 0x60, 0x3C, 0x00, /* @ */ +/* 41 */ 0x3C, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, /* A */ +/* 42 */ 0x7C, 0x66, 0x66, 0x7C, 0x66, 0x66, 0x7C, 0x00, /* B */ +/* 43 */ 0x3C, 0x66, 0x60, 0x60, 0x60, 0x66, 0x3C, 0x00, /* C */ +/* 44 */ 0x78, 0x6C, 0x66, 0x66, 0x66, 0x6C, 0x78, 0x00, /* D */ +/* 45 */ 0x7E, 0x60, 0x60, 0x7C, 0x60, 0x60, 0x7E, 0x00, /* E */ +/* 46 */ 0x7E, 0x60, 0x60, 0x7C, 0x60, 0x60, 0x60, 0x00, /* F */ +/* 47 */ 0x3C, 0x66, 0x60, 0x6E, 0x66, 0x66, 0x3C, 0x00, /* G */ +/* 48 */ 0x66, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, /* H */ +/* 49 */ 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x00, /* I */ +/* 4A */ 0x3E, 0x0C, 0x0C, 0x0C, 0x0C, 0x6C, 0x38, 0x00, /* J */ +/* 4B */ 0x66, 0x6C, 0x78, 0x70, 0x78, 0x6C, 0x66, 0x00, /* K */ +/* 4C */ 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7E, 0x00, /* L */ +/* 4D */ 0x63, 0x77, 0x7F, 0x6B, 0x6B, 0x63, 0x63, 0x00, /* M */ +/* 4E */ 0x66, 0x66, 0x76, 0x7E, 0x6E, 0x66, 0x66, 0x00, /* N */ +/* 4F */ 0x3C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, /* O */ +/* 50 */ 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x60, 0x00, /* P */ +/* 51 */ 0x3C, 0x66, 0x66, 0x66, 0x6A, 0x6C, 0x36, 0x00, /* Q */ +/* 52 */ 0x7C, 0x66, 0x66, 0x7C, 0x6C, 0x66, 0x66, 0x00, /* R */ +/* 53 */ 0x3C, 0x66, 0x60, 0x3C, 0x06, 0x66, 0x3C, 0x00, /* S */ +/* 54 */ 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, /* T */ +/* 55 */ 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, /* U */ +/* 56 */ 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, /* V */ +/* 57 */ 0x63, 0x63, 0x6B, 0x6B, 0x7F, 0x77, 0x63, 0x00, /* W */ +/* 58 */ 0x66, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x66, 0x00, /* X */ +/* 59 */ 0x66, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x18, 0x00, /* Y */ +/* 5A */ 0x7E, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x7E, 0x00, /* Z */ +/* 5B */ 0x7C, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7C, 0x00, /* [ */ +/* 5C */ 0x00, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x00, 0x00, /* \ */ +/* 5D */ 0x3E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x3E, 0x00, /* ] */ +/* 5E */ 0x3C, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ^ */ +/* 5F */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, /* _ */ +/* 60 */ 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ` */ +/* 61 */ 0x00, 0x00, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00, /* a */ +/* 62 */ 0x60, 0x60, 0x7C, 0x66, 0x66, 0x66, 0x7C, 0x00, /* b */ +/* 63 */ 0x00, 0x00, 0x3C, 0x66, 0x60, 0x66, 0x3C, 0x00, /* c */ +/* 64 */ 0x06, 0x06, 0x3E, 0x66, 0x66, 0x66, 0x3E, 0x00, /* d */ +/* 65 */ 0x00, 0x00, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00, /* e */ +/* 66 */ 0x1C, 0x30, 0x30, 0x7C, 0x30, 0x30, 0x30, 0x00, /* f */ +/* 67 */ 0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x3C, /* g */ +/* 68 */ 0x60, 0x60, 0x7C, 0x66, 0x66, 0x66, 0x66, 0x00, /* h */ +/* 69 */ 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x3C, 0x00, /* i */ +/* 6A */ 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x70, /* j */ +/* 6B */ 0x60, 0x60, 0x66, 0x6C, 0x78, 0x6C, 0x66, 0x00, /* k */ +/* 6C */ 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, /* l */ +/* 6D */ 0x00, 0x00, 0x36, 0x7F, 0x6B, 0x6B, 0x63, 0x00, /* m */ +/* 6E */ 0x00, 0x00, 0x7C, 0x66, 0x66, 0x66, 0x66, 0x00, /* n */ +/* 6F */ 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00, /* o */ +/* 70 */ 0x00, 0x00, 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x60, /* p */ +/* 71 */ 0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x07, /* q */ +/* 72 */ 0x00, 0x00, 0x6C, 0x76, 0x60, 0x60, 0x60, 0x00, /* r */ +/* 73 */ 0x00, 0x00, 0x3E, 0x60, 0x3C, 0x06, 0x7C, 0x00, /* s */ +/* 74 */ 0x30, 0x30, 0x7C, 0x30, 0x30, 0x30, 0x1C, 0x00, /* t */ +/* 75 */ 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3E, 0x00, /* u */ +/* 76 */ 0x00, 0x00, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, /* v */ +/* 77 */ 0x00, 0x00, 0x63, 0x6B, 0x6B, 0x7F, 0x36, 0x00, /* w */ +/* 78 */ 0x00, 0x00, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x00, /* x */ +/* 79 */ 0x00, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x06, 0x3C, /* y */ +/* 7A */ 0x00, 0x00, 0x7E, 0x0C, 0x18, 0x30, 0x7E, 0x00, /* z */ +/* 7B */ 0x0C, 0x18, 0x18, 0x70, 0x18, 0x18, 0x0C, 0x00, /* { */ +/* 7C */ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, /* | */ +/* 7D */ 0x30, 0x18, 0x18, 0x0E, 0x18, 0x18, 0x30, 0x00, /* } */ +/* 7E */ 0x31, 0x6B, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, /* ~ */ +/* 7F */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*  */ +/* 80 */ 0x3c, 0x66, 0x60, 0x60, 0x66, 0x3c, 0x30, 0x60, +/* 81 */ 0x66, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x00, +/* 82 */ 0x0c, 0x18, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00, +/* 83 */ 0x18, 0x66, 0x3c, 0x06, 0x3e, 0x66, 0x3e, 0x00, +/* 84 */ 0x66, 0x00, 0x3c, 0x06, 0x3e, 0x66, 0x3e, 0x00, +/* 85 */ 0x30, 0x18, 0x3c, 0x06, 0x3e, 0x66, 0x3e, 0x00, +/* 86 */ 0x3c, 0x66, 0x3c, 0x06, 0x3e, 0x66, 0x3e, 0x00, +/* 87 */ 0x00, 0x00, 0x3c, 0x66, 0x60, 0x66, 0x3c, 0x60, +/* 88 */ 0x3c, 0x66, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00, +/* 89 */ 0x66, 0x00, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00, +/* 8A */ 0x30, 0x18, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00, +/* 8B */ 0x66, 0x00, 0x00, 0x38, 0x18, 0x18, 0x3c, 0x00, +/* 8C */ 0x3c, 0x66, 0x00, 0x38, 0x18, 0x18, 0x3c, 0x00, +/* 8D */ 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x3c, 0x00, +/* 8E */ 0x66, 0x66, 0x00, 0x3c, 0x66, 0x7e, 0x66, 0x00, +/* 8F */ 0x18, 0x66, 0x00, 0x3c, 0x66, 0x7e, 0x66, 0x00, +/* 90 */ 0x0c, 0x18, 0x7e, 0x60, 0x7c, 0x60, 0x7e, 0x00, +/* 91 */ 0x00, 0x00, 0x3f, 0x0d, 0x3f, 0x6c, 0x3f, 0x00, +/* 92 */ 0x3f, 0x66, 0x66, 0x7f, 0x66, 0x66, 0x67, 0x00, +/* 93 */ 0x3c, 0x66, 0x00, 0x3c, 0x66, 0x66, 0x3c, 0x00, +/* 94 */ 0x66, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x3c, 0x00, +/* 95 */ 0x30, 0x18, 0x00, 0x3c, 0x66, 0x66, 0x3c, 0x00, +/* 96 */ 0x3c, 0x66, 0x00, 0x66, 0x66, 0x66, 0x3e, 0x00, +/* 97 */ 0x30, 0x18, 0x00, 0x66, 0x66, 0x66, 0x3e, 0x00, +/* 98 */ 0x66, 0x00, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x3c, +/* 99 */ 0x66, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x00, +/* 9A */ 0x66, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, +/* 9B */ 0x08, 0x3e, 0x6b, 0x68, 0x6b, 0x3e, 0x08, 0x00, +/* 9C */ 0x1c, 0x36, 0x30, 0x7c, 0x30, 0x30, 0x7e, 0x00, +/* 9D */ 0x66, 0x3c, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, +/* 9E */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* 9F */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* A0 */ 0x0c, 0x18, 0x3c, 0x06, 0x3e, 0x66, 0x3e, 0x00, +/* A1 */ 0x0c, 0x18, 0x00, 0x38, 0x18, 0x18, 0x3c, 0x00, +/* A2 */ 0x0c, 0x18, 0x00, 0x3c, 0x66, 0x66, 0x3c, 0x00, +/* A3 */ 0x0c, 0x18, 0x00, 0x66, 0x66, 0x66, 0x3e, 0x00, +/* A4 */ 0x36, 0x6c, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x00, +/* A5 */ 0x36, 0x6c, 0x00, 0x66, 0x76, 0x6e, 0x66, 0x00, +/* A6 */ 0x1c, 0x06, 0x1e, 0x36, 0x1e, 0x00, 0x3e, 0x00, +/* A7 */ 0x1c, 0x36, 0x36, 0x36, 0x1c, 0x00, 0x3e, 0x00, +/* A8 */ 0x18, 0x00, 0x18, 0x18, 0x30, 0x66, 0x3c, 0x00, +/* A9 */ 0x7e, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* AA */ 0x7e, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* AB */ 0x40, 0xc0, 0x40, 0x4f, 0x41, 0x0f, 0x08, 0x0f, +/* AC */ 0x40, 0xc0, 0x40, 0x48, 0x48, 0x0a, 0x0f, 0x02, +/* AD */ 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, +/* AE */ 0x00, 0x33, 0x66, 0xcc, 0xcc, 0x66, 0x33, 0x00, +/* AF */ 0x00, 0xcc, 0x66, 0x33, 0x33, 0x66, 0xcc, 0x00, +/* B0 */ 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, +/* B1 */ 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, +/* B2 */ 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, +/* B3 */ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +/* B4 */ 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, +/* B5 */ 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, +/* B6 */ 0x66, 0x66, 0x66, 0xe6, 0x66, 0x66, 0x66, 0x66, +/* B7 */ 0x00, 0x00, 0x00, 0xfe, 0x66, 0x66, 0x66, 0x66, +/* B8 */ 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, +/* B9 */ 0x66, 0x66, 0xe6, 0x06, 0xe6, 0x66, 0x66, 0x66, +/* BA */ 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, +/* BB */ 0x00, 0x00, 0xfe, 0x06, 0xe6, 0x66, 0x66, 0x66, +/* BC */ 0x66, 0x66, 0xe6, 0x06, 0xfe, 0x00, 0x00, 0x00, +/* BD */ 0x66, 0x66, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, +/* BE */ 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, +/* BF */ 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, +/* C0 */ 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, +/* C1 */ 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, +/* C2 */ 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, +/* C3 */ 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, +/* C4 */ 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, +/* C5 */ 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, +/* C6 */ 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, +/* C7 */ 0x66, 0x66, 0x66, 0x67, 0x66, 0x66, 0x66, 0x66, +/* C8 */ 0x66, 0x66, 0x67, 0x60, 0x7f, 0x00, 0x00, 0x00, +/* C9 */ 0x00, 0x00, 0x7f, 0x60, 0x67, 0x66, 0x66, 0x66, +/* CA */ 0x66, 0x66, 0xe7, 0x00, 0xff, 0x00, 0x00, 0x00, +/* CB */ 0x00, 0x00, 0xff, 0x00, 0xe7, 0x66, 0x66, 0x66, +/* CC */ 0x66, 0x66, 0x67, 0x60, 0x67, 0x66, 0x66, 0x66, +/* CD */ 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, +/* CE */ 0x66, 0x66, 0xe7, 0x00, 0xe7, 0x66, 0x66, 0x66, +/* CF */ 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, +/* D0 */ 0x66, 0x66, 0x66, 0xff, 0x00, 0x00, 0x00, 0x00, +/* D1 */ 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, +/* D2 */ 0x00, 0x00, 0x00, 0xff, 0x66, 0x66, 0x66, 0x66, +/* D3 */ 0x66, 0x66, 0x66, 0x7f, 0x00, 0x00, 0x00, 0x00, +/* D4 */ 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, +/* D5 */ 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, +/* D6 */ 0x00, 0x00, 0x00, 0x7f, 0x66, 0x66, 0x66, 0x66, +/* D7 */ 0x66, 0x66, 0x66, 0xff, 0x66, 0x66, 0x66, 0x66, +/* D8 */ 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, +/* D9 */ 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, +/* DA */ 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, +/* DB */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +/* DC */ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, +/* DD */ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, +/* DE */ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, +/* DF */ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, +/* E0 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* E1 */ 0x3c, 0x66, 0x66, 0x6c, 0x66, 0x66, 0x6c, 0xc0, +/* E2 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* E3 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* E4 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* E5 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* E6 */ 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x3e, 0x60, +/* E7 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* E8 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* E9 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* EA */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* EB */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* EC */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* ED */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* EE */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* EF */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* F0 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* F1 */ 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x7e, 0x00, +/* F2 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* F3 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* F4 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* F5 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* F6 */ 0x00, 0x18, 0x00, 0xff, 0x00, 0x18, 0x00, 0x00, +/* F7 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* F8 */ 0x3c, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, +/* F9 */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* FA */ 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, +/* FB */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* FC */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* FD */ 0x38, 0x04, 0x18, 0x20, 0x3c, 0x00, 0x00, 0x00, +/* FE */ 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, +/* FF */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +const struct font_desc font_acorn_8x8 = { + .idx = ACORN8x8_IDX, + .name = "Acorn8x8", + .width = 8, + .height = 8, + .data = acorndata_8x8, +#ifdef CONFIG_ARCH_ACORN + .pref = 20, +#else + .pref = 0, +#endif +}; diff --git a/lib/fonts/font_mini_4x6.c b/lib/fonts/font_mini_4x6.c new file mode 100644 index 000000000000..838caa1cfef7 --- /dev/null +++ b/lib/fonts/font_mini_4x6.c @@ -0,0 +1,2158 @@ + +/* Hand composed "Minuscule" 4x6 font, with binary data generated using + * Perl stub. + * + * Use 'perl -x mini_4x6.c < mini_4x6.c > new_version.c' to regenerate + * binary data. + * + * Created by Kenneth Albanowski. + * No rights reserved, released to the public domain. + * + * Version 1.0 + */ + +/* + +#!/usr/bin/perl -pn + +s{((0x)?[0-9a-fA-F]+)(.*\[([\*\ ]{4})\])}{ + + ($num,$pat,$bits) = ($1,$3,$4); + + $bits =~ s/([^\s0])|(.)/ defined($1) + 0 /ge; + + $num = ord(pack("B8", $bits)); + $num |= $num >> 4; + $num = sprintf("0x%.2x", $num); + + #print "$num,$pat,$bits\n"; + + $num . $pat; +}ge; + +__END__; +*/ + +/* Note: binary data consists of one byte for each row of each character top + to bottom, character 0 to character 255, six bytes per character. Each + byte contains the same four character bits in both nybbles. + MSBit to LSBit = left to right. + */ + +#include + +#define FONTDATAMAX 1536 + +static const unsigned char fontdata_mini_4x6[FONTDATAMAX] = { + + /*{*/ + /* Char 0: ' ' */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 1: ' ' */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 2: ' ' */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 3: ' ' */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 4: ' ' */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 5: ' ' */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 6: ' ' */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 7: ' ' */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 8: ' ' */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 9: ' ' */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 10: '' */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 11: ' ' */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 12: ' ' */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 13: ' ' */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 14: ' ' */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 15: ' ' */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 16: ' ' */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 17: ' ' */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 18: ' ' */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 19: ' ' */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 20: ' ' */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 21: ' ' */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 22: ' ' */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 23: ' ' */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 24: ' ' */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 25: ' ' */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 26: ' ' */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 27: ' ' */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 28: ' ' */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 29: ' ' */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 30: ' ' */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 31: ' ' */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 32: ' ' */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 33: '!' */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x00, /*= [ ] */ + 0x44, /*= [ * ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 34: '"' */ + 0xaa, /*= [* * ] */ + 0xaa, /*= [* * ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 35: '#' */ + 0xaa, /*= [* * ] */ + 0xff, /*= [****] */ + 0xff, /*= [****] */ + 0xaa, /*= [* * ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 36: '$' */ + 0x44, /*= [ * ] */ + 0x66, /*= [ ** ] */ + 0xee, /*= [*** ] */ + 0xcc, /*= [** ] */ + 0x44, /*= [ * ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 37: '%' */ + 0xaa, /*= [* * ] */ + 0x22, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x88, /*= [* ] */ + 0xaa, /*= [* * ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 38: '&' */ + 0x66, /*= [ ** ] */ + 0x99, /*= [* *] */ + 0x66, /*= [ ** ] */ + 0xaa, /*= [* * ] */ + 0xdd, /*= [** *] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 39: ''' */ + 0x22, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 40: '(' */ + 0x22, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x22, /*= [ * ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 41: ')' */ + 0x44, /*= [ * ] */ + 0x22, /*= [ * ] */ + 0x22, /*= [ * ] */ + 0x22, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 42: '*' */ + 0x00, /*= [ ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 43: '+' */ + 0x00, /*= [ ] */ + 0x44, /*= [ * ] */ + 0xee, /*= [*** ] */ + 0x44, /*= [ * ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 44: ',' */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x44, /*= [ * ] */ + 0x88, /*= [* ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 45: '-' */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 46: '.' */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x44, /*= [ * ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 47: '/' */ + 0x00, /*= [ ] */ + 0x22, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x88, /*= [* ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 48: '0' */ + 0x44, /*= [ * ] */ + 0xaa, /*= [* * ] */ + 0xaa, /*= [* * ] */ + 0xaa, /*= [* * ] */ + 0x44, /*= [ * ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 49: '1' */ + 0x44, /*= [ * ] */ + 0xcc, /*= [** ] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 50: '2' */ + 0xcc, /*= [** ] */ + 0x22, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x88, /*= [* ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ + /* Char 51: '3' */ + 0xee, /*= [*** ] */ + 0x22, /*= [ * ] */ + 0x66, /*= [ ** ] */ + 0x22, /*= [ * ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 52: '4' */ + 0xaa, /*= [* * ] */ + 0xaa, /*= [* * ] */ + 0xee, /*= [*** ] */ + 0x22, /*= [ * ] */ + 0x22, /*= [ * ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 53: '5' */ + 0xee, /*= [*** ] */ + 0x88, /*= [* ] */ + 0xee, /*= [*** ] */ + 0x22, /*= [ * ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 54: '6' */ + 0xee, /*= [*** ] */ + 0x88, /*= [* ] */ + 0xee, /*= [*** ] */ + 0xaa, /*= [* * ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 55: '7' */ + 0xee, /*= [*** ] */ + 0x22, /*= [ * ] */ + 0x22, /*= [ * ] */ + 0x22, /*= [ * ] */ + 0x22, /*= [ * ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 56: '8' */ + 0xee, /*= [*** ] */ + 0xaa, /*= [* * ] */ + 0xee, /*= [*** ] */ + 0xaa, /*= [* * ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 57: '9' */ + 0xee, /*= [*** ] */ + 0xaa, /*= [* * ] */ + 0xee, /*= [*** ] */ + 0x22, /*= [ * ] */ + 0x22, /*= [ * ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 58: ':' */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x44, /*= [ * ] */ + 0x00, /*= [ ] */ + 0x44, /*= [ * ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 59: ';' */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x44, /*= [ * ] */ + 0x00, /*= [ ] */ + 0x44, /*= [ * ] */ + 0x88, /*= [* ] */ + /*}*/ + /*{*/ /* Char 60: '<' */ + 0x22, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x88, /*= [* ] */ + 0x44, /*= [ * ] */ + 0x22, /*= [ * ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 61: '=' */ + 0x00, /*= [ ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 62: '>' */ + 0x88, /*= [* ] */ + 0x44, /*= [ * ] */ + 0x22, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x88, /*= [* ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 63: '?' */ + 0xee, /*= [*** ] */ + 0x22, /*= [ * ] */ + 0x66, /*= [ ** ] */ + 0x00, /*= [ ] */ + 0x44, /*= [ * ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 64: '@' */ + 0x44, /*= [ * ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x88, /*= [* ] */ + 0x44, /*= [ * ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 65: 'A' */ + 0x44, /*= [ * ] */ + 0xaa, /*= [* * ] */ + 0xee, /*= [*** ] */ + 0xaa, /*= [* * ] */ + 0xaa, /*= [* * ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 66: 'B' */ + 0xcc, /*= [** ] */ + 0xaa, /*= [* * ] */ + 0xcc, /*= [** ] */ + 0xaa, /*= [* * ] */ + 0xcc, /*= [** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 67: 'C' */ + 0x66, /*= [ ** ] */ + 0x88, /*= [* ] */ + 0x88, /*= [* ] */ + 0x88, /*= [* ] */ + 0x66, /*= [ ** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 68: 'D' */ + 0xcc, /*= [** ] */ + 0xaa, /*= [* * ] */ + 0xaa, /*= [* * ] */ + 0xaa, /*= [* * ] */ + 0xcc, /*= [** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 69: 'E' */ + 0xee, /*= [*** ] */ + 0x88, /*= [* ] */ + 0xee, /*= [*** ] */ + 0x88, /*= [* ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 70: 'F' */ + 0xee, /*= [*** ] */ + 0x88, /*= [* ] */ + 0xee, /*= [*** ] */ + 0x88, /*= [* ] */ + 0x88, /*= [* ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 71: 'G' */ + 0x66, /*= [ ** ] */ + 0x88, /*= [* ] */ + 0xee, /*= [*** ] */ + 0xaa, /*= [* * ] */ + 0x66, /*= [ ** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 72: 'H' */ + 0xaa, /*= [* * ] */ + 0xaa, /*= [* * ] */ + 0xee, /*= [*** ] */ + 0xaa, /*= [* * ] */ + 0xaa, /*= [* * ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 73: 'I' */ + 0xee, /*= [*** ] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 74: 'J' */ + 0x22, /*= [ * ] */ + 0x22, /*= [ * ] */ + 0x22, /*= [ * ] */ + 0xaa, /*= [* * ] */ + 0x44, /*= [ * ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 75: 'K' */ + 0xaa, /*= [* * ] */ + 0xaa, /*= [* * ] */ + 0xcc, /*= [** ] */ + 0xaa, /*= [* * ] */ + 0xaa, /*= [* * ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 76: 'L' */ + 0x88, /*= [* ] */ + 0x88, /*= [* ] */ + 0x88, /*= [* ] */ + 0x88, /*= [* ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 77: 'M' */ + 0xaa, /*= [* * ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xaa, /*= [* * ] */ + 0xaa, /*= [* * ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 78: 'N' */ + 0xaa, /*= [* * ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xaa, /*= [* * ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 79: 'O' */ + 0x44, /*= [ * ] */ + 0xaa, /*= [* * ] */ + 0xaa, /*= [* * ] */ + 0xaa, /*= [* * ] */ + 0x44, /*= [ * ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 80: 'P' */ + 0xcc, /*= [** ] */ + 0xaa, /*= [* * ] */ + 0xcc, /*= [** ] */ + 0x88, /*= [* ] */ + 0x88, /*= [* ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 81: 'Q' */ + 0x44, /*= [ * ] */ + 0xaa, /*= [* * ] */ + 0xaa, /*= [* * ] */ + 0xee, /*= [*** ] */ + 0x66, /*= [ ** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 82: 'R' */ + 0xcc, /*= [** ] */ + 0xaa, /*= [* * ] */ + 0xee, /*= [*** ] */ + 0xcc, /*= [** ] */ + 0xaa, /*= [* * ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 83: 'S' */ + 0x66, /*= [ ** ] */ + 0x88, /*= [* ] */ + 0x44, /*= [ * ] */ + 0x22, /*= [ * ] */ + 0xcc, /*= [** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 84: 'T' */ + 0xee, /*= [*** ] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 85: 'U' */ + 0xaa, /*= [* * ] */ + 0xaa, /*= [* * ] */ + 0xaa, /*= [* * ] */ + 0xaa, /*= [* * ] */ + 0x66, /*= [ ** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 86: 'V' */ + 0xaa, /*= [* * ] */ + 0xaa, /*= [* * ] */ + 0xaa, /*= [* * ] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 87: 'W' */ + 0xaa, /*= [* * ] */ + 0xaa, /*= [* * ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xaa, /*= [* * ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 88: 'X' */ + 0xaa, /*= [* * ] */ + 0xaa, /*= [* * ] */ + 0x44, /*= [ * ] */ + 0xaa, /*= [* * ] */ + 0xaa, /*= [* * ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 89: 'Y' */ + 0xaa, /*= [* * ] */ + 0xaa, /*= [* * ] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 90: 'Z' */ + 0xee, /*= [*** ] */ + 0x22, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x88, /*= [* ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 91: '[' */ + 0x66, /*= [ ** ] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x66, /*= [ ** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 92: '\' */ + 0x00, /*= [ ] */ + 0x88, /*= [* ] */ + 0x44, /*= [ * ] */ + 0x22, /*= [ * ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 93: ']' */ + 0x66, /*= [ ** ] */ + 0x22, /*= [ * ] */ + 0x22, /*= [ * ] */ + 0x22, /*= [ * ] */ + 0x66, /*= [ ** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 94: '^' */ + 0x44, /*= [ * ] */ + 0xaa, /*= [* * ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 95: '_' */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0xff, /*= [****] */ + /*}*/ + /*{*/ /* Char 96: '`' */ + 0x88, /*= [* ] */ + 0x44, /*= [ * ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 97: 'a' */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x66, /*= [ ** ] */ + 0xaa, /*= [* * ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 98: 'b' */ + 0x88, /*= [* ] */ + 0x88, /*= [* ] */ + 0xcc, /*= [** ] */ + 0xaa, /*= [* * ] */ + 0xcc, /*= [** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 99: 'c' */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x66, /*= [ ** ] */ + 0x88, /*= [* ] */ + 0x66, /*= [ ** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 100: 'd' */ + 0x22, /*= [ * ] */ + 0x22, /*= [ * ] */ + 0x66, /*= [ ** ] */ + 0xaa, /*= [* * ] */ + 0x66, /*= [ ** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 101: 'e' */ + 0x00, /*= [ ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x88, /*= [* ] */ + 0x66, /*= [ ** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 102: 'f' */ + 0x22, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0xee, /*= [*** ] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 103: 'g' */ + 0x00, /*= [ ] */ + 0x66, /*= [ ** ] */ + 0xaa, /*= [* * ] */ + 0x66, /*= [ ** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 104: 'h' */ + 0x88, /*= [* ] */ + 0x88, /*= [* ] */ + 0xcc, /*= [** ] */ + 0xaa, /*= [* * ] */ + 0xaa, /*= [* * ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 105: 'i' */ + 0x44, /*= [ * ] */ + 0x00, /*= [ ] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 106: 'j' */ + 0x44, /*= [ * ] */ + 0x00, /*= [ ] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x88, /*= [* ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 107: 'k' */ + 0x00, /*= [ ] */ + 0x88, /*= [* ] */ + 0xaa, /*= [* * ] */ + 0xcc, /*= [** ] */ + 0xaa, /*= [* * ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 108: 'l' */ + 0x00, /*= [ ] */ + 0xcc, /*= [** ] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 109: 'm' */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xaa, /*= [* * ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 110: 'n' */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0xcc, /*= [** ] */ + 0xaa, /*= [* * ] */ + 0xaa, /*= [* * ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 111: 'o' */ + 0x00, /*= [ ] */ + 0x44, /*= [ * ] */ + 0xaa, /*= [* * ] */ + 0xaa, /*= [* * ] */ + 0x44, /*= [ * ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 112: 'p' */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0xcc, /*= [** ] */ + 0xaa, /*= [* * ] */ + 0xcc, /*= [** ] */ + 0x88, /*= [* ] */ + /*}*/ + /*{*/ /* Char 113: 'q' */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x66, /*= [ ** ] */ + 0xaa, /*= [* * ] */ + 0x66, /*= [ ** ] */ + 0x22, /*= [ * ] */ + /*}*/ + /*{*/ /* Char 114: 'r' */ + 0x00, /*= [ ] */ + 0xcc, /*= [** ] */ + 0xaa, /*= [* * ] */ + 0x88, /*= [* ] */ + 0x88, /*= [* ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 115: 's' */ + 0x00, /*= [ ] */ + 0x66, /*= [ ** ] */ + 0xcc, /*= [** ] */ + 0x22, /*= [ * ] */ + 0xcc, /*= [** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 116: 't' */ + 0x00, /*= [ ] */ + 0x44, /*= [ * ] */ + 0xee, /*= [*** ] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 117: 'u' */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0xaa, /*= [* * ] */ + 0xaa, /*= [* * ] */ + 0x66, /*= [ ** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 118: 'v' */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0xaa, /*= [* * ] */ + 0xee, /*= [*** ] */ + 0x44, /*= [ * ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 119: 'w' */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0xaa, /*= [* * ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 120: 'x' */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0xaa, /*= [* * ] */ + 0x44, /*= [ * ] */ + 0xaa, /*= [* * ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 121: 'y' */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0xaa, /*= [* * ] */ + 0xee, /*= [*** ] */ + 0x22, /*= [ * ] */ + 0xcc, /*= [** ] */ + /*}*/ + /*{*/ /* Char 122: 'z' */ + 0x00, /*= [ ] */ + 0xee, /*= [*** ] */ + 0x66, /*= [ ** ] */ + 0xcc, /*= [** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 123: '{' */ + 0x22, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0xcc, /*= [** ] */ + 0x44, /*= [ * ] */ + 0x22, /*= [ * ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 124: '|' */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 125: '}' */ + 0x88, /*= [* ] */ + 0x44, /*= [ * ] */ + 0x66, /*= [ ** ] */ + 0x44, /*= [ * ] */ + 0x88, /*= [* ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 126: '~' */ + 0x55, /*= [ * *] */ + 0xaa, /*= [* * ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 127: '' */ + 0x44, /*= [ * ] */ + 0xaa, /*= [* * ] */ + 0xaa, /*= [* * ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 128: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 129: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 130: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 131: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 132: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 133: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 134: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 135: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 136: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 137: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 138: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 139: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 140: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 141: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 142: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 143: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 144: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 145: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 146: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 147: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 148: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 149: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 150: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 151: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 152: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 153: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 154: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 155: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 156: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 157: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 158: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 159: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 160: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 161: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 162: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 163: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 164: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 165: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 166: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 167: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 168: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 169: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 170: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 171: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 172: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 173: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 174: */ + 0x00, /*= [ ] */ + 0x66, /*= [ ** ] */ + 0xcc, /*= [** ] */ + 0x66, /*= [ ** ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 175: */ + 0x00, /*= [ ] */ + 0xcc, /*= [** ] */ + 0x66, /*= [ ** ] */ + 0xcc, /*= [** ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 176: */ + 0x88, /*= [* ] */ + 0x22, /*= [ * ] */ + 0x88, /*= [* ] */ + 0x22, /*= [ * ] */ + 0x88, /*= [* ] */ + 0x22, /*= [ * ] */ + /*}*/ + /*{*/ /* Char 177: */ + 0xaa, /*= [* * ] */ + 0x55, /*= [ * *] */ + 0xaa, /*= [* * ] */ + 0x55, /*= [ * *] */ + 0xaa, /*= [* * ] */ + 0x55, /*= [ * *] */ + /*}*/ + /*{*/ /* Char 178: */ + 0xdd, /*= [** *] */ + 0xbb, /*= [* **] */ + 0xdd, /*= [** *] */ + 0xbb, /*= [* **] */ + 0xdd, /*= [** *] */ + 0xbb, /*= [* **] */ + /*}*/ + /*{*/ /* Char 179: */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + /*}*/ + /*{*/ /* Char 180: */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0xcc, /*= [** ] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + /*}*/ + /*{*/ /* Char 181: */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0xcc, /*= [** ] */ + 0xcc, /*= [** ] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + /*}*/ + /*{*/ /* Char 182: */ + 0x66, /*= [ ** ] */ + 0x66, /*= [ ** ] */ + 0xee, /*= [*** ] */ + 0x66, /*= [ ** ] */ + 0x66, /*= [ ** ] */ + 0x66, /*= [ ** ] */ + /*}*/ + /*{*/ /* Char 183: */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0xee, /*= [*** ] */ + 0x66, /*= [ ** ] */ + 0x66, /*= [ ** ] */ + 0x66, /*= [ ** ] */ + /*}*/ + /*{*/ /* Char 184: */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0xcc, /*= [** ] */ + 0xcc, /*= [** ] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + /*}*/ + /*{*/ /* Char 185: */ + 0x66, /*= [ ** ] */ + 0x66, /*= [ ** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x66, /*= [ ** ] */ + 0x66, /*= [ ** ] */ + /*}*/ + /*{*/ /* Char 186: */ + 0x66, /*= [ ** ] */ + 0x66, /*= [ ** ] */ + 0x66, /*= [ ** ] */ + 0x66, /*= [ ** ] */ + 0x66, /*= [ ** ] */ + 0x66, /*= [ ** ] */ + /*}*/ + /*{*/ /* Char 187: */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x66, /*= [ ** ] */ + 0x66, /*= [ ** ] */ + /*}*/ + /*{*/ /* Char 188: */ + 0x66, /*= [ ** ] */ + 0x66, /*= [ ** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 189: */ + 0x66, /*= [ ** ] */ + 0x66, /*= [ ** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 190: */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0xcc, /*= [** ] */ + 0xcc, /*= [** ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 191: */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0xcc, /*= [** ] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + /*}*/ + /*{*/ /* Char 192: */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x77, /*= [ ***] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 193: */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0xff, /*= [****] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 194: */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0xff, /*= [****] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + /*}*/ + /*{*/ /* Char 195: */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x77, /*= [ ***] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + /*}*/ + /*{*/ /* Char 196: */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0xff, /*= [****] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 197: */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0xff, /*= [****] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + /*}*/ + /*{*/ /* Char 198: */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x77, /*= [ ***] */ + 0x77, /*= [ ***] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + /*}*/ + /*{*/ /* Char 199: */ + 0x66, /*= [ ** ] */ + 0x66, /*= [ ** ] */ + 0x77, /*= [ ***] */ + 0x66, /*= [ ** ] */ + 0x66, /*= [ ** ] */ + 0x66, /*= [ ** ] */ + /*}*/ + /*{*/ /* Char 200: */ + 0x66, /*= [ ** ] */ + 0x66, /*= [ ** ] */ + 0x77, /*= [ ***] */ + 0x77, /*= [ ***] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 201: */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x77, /*= [ ***] */ + 0x77, /*= [ ***] */ + 0x66, /*= [ ** ] */ + 0x66, /*= [ ** ] */ + /*}*/ + /*{*/ /* Char 202: */ + 0x66, /*= [ ** ] */ + 0x66, /*= [ ** ] */ + 0xff, /*= [****] */ + 0xff, /*= [****] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 203: */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0xff, /*= [****] */ + 0xff, /*= [****] */ + 0x66, /*= [ ** ] */ + 0x66, /*= [ ** ] */ + /*}*/ + /*{*/ /* Char 204: */ + 0x66, /*= [ ** ] */ + 0x66, /*= [ ** ] */ + 0x77, /*= [ ***] */ + 0x77, /*= [ ***] */ + 0x66, /*= [ ** ] */ + 0x66, /*= [ ** ] */ + /*}*/ + /*{*/ /* Char 205: */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0xff, /*= [****] */ + 0xff, /*= [****] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 206: */ + 0x66, /*= [ ** ] */ + 0x66, /*= [ ** ] */ + 0xff, /*= [****] */ + 0xff, /*= [****] */ + 0x66, /*= [ ** ] */ + 0x66, /*= [ ** ] */ + /*}*/ + /*{*/ /* Char 207: */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0xff, /*= [****] */ + 0xff, /*= [****] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 208: */ + 0x66, /*= [ ** ] */ + 0x66, /*= [ ** ] */ + 0xff, /*= [****] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 209: */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0xff, /*= [****] */ + 0xff, /*= [****] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + /*}*/ + /*{*/ /* Char 210: */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0xff, /*= [****] */ + 0x66, /*= [ ** ] */ + 0x66, /*= [ ** ] */ + 0x66, /*= [ ** ] */ + /*}*/ + /*{*/ /* Char 211: */ + 0x66, /*= [ ** ] */ + 0x66, /*= [ ** ] */ + 0x77, /*= [ ***] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 212: */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x77, /*= [ ***] */ + 0x77, /*= [ ***] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 213: */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x77, /*= [ ***] */ + 0x77, /*= [ ***] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + /*}*/ + /*{*/ /* Char 214: */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x77, /*= [ ***] */ + 0x66, /*= [ ** ] */ + 0x66, /*= [ ** ] */ + 0x66, /*= [ ** ] */ + /*}*/ + /*{*/ /* Char 215: */ + 0x66, /*= [ ** ] */ + 0x66, /*= [ ** ] */ + 0xff, /*= [****] */ + 0x66, /*= [ ** ] */ + 0x66, /*= [ ** ] */ + 0x66, /*= [ ** ] */ + /*}*/ + /*{*/ /* Char 216: */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0xff, /*= [****] */ + 0xff, /*= [****] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + /*}*/ + /*{*/ /* Char 217: */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0xcc, /*= [** ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 218: */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x77, /*= [ ***] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + 0x44, /*= [ * ] */ + /*}*/ + /*{*/ /* Char 219: */ + 0xff, /*= [****] */ + 0xff, /*= [****] */ + 0xff, /*= [****] */ + 0xff, /*= [****] */ + 0xff, /*= [****] */ + 0xff, /*= [****] */ + /*}*/ + /*{*/ /* Char 220: */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0xff, /*= [****] */ + 0xff, /*= [****] */ + 0xff, /*= [****] */ + /*}*/ + /*{*/ /* Char 221: */ + 0xcc, /*= [** ] */ + 0xcc, /*= [** ] */ + 0xcc, /*= [** ] */ + 0xcc, /*= [** ] */ + 0xcc, /*= [** ] */ + 0xcc, /*= [** ] */ + /*}*/ + /*{*/ /* Char 222: */ + 0x33, /*= [ **] */ + 0x33, /*= [ **] */ + 0x33, /*= [ **] */ + 0x33, /*= [ **] */ + 0x33, /*= [ **] */ + 0x33, /*= [ **] */ + /*}*/ + /*{*/ /* Char 223: */ + 0xff, /*= [****] */ + 0xff, /*= [****] */ + 0xff, /*= [****] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 224: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 225: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 226: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 227: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 228: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 229: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 230: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 231: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 232: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 233: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 234: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 235: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 236: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 237: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 238: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 239: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 240: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 241: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 242: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 243: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 244: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 245: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 246: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 247: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 248: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 249: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 250: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 251: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 252: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 253: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 254: */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + 0x66, /*= [ ** ] */ + 0x66, /*= [ ** ] */ + 0x00, /*= [ ] */ + 0x00, /*= [ ] */ + /*}*/ + /*{*/ /* Char 255: */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0xee, /*= [*** ] */ + 0x00, /*= [ ] */ + /*}*/ +}; + +const struct font_desc font_mini_4x6 = { + .idx = MINI4x6_IDX, + .name = "MINI4x6", + .width = 4, + .height = 6, + .data = fontdata_mini_4x6, + .pref = 3, +}; + diff --git a/lib/fonts/font_pearl_8x8.c b/lib/fonts/font_pearl_8x8.c new file mode 100644 index 000000000000..dc6ad539ca4e --- /dev/null +++ b/lib/fonts/font_pearl_8x8.c @@ -0,0 +1,2587 @@ +/**********************************************/ +/* */ +/* Font file generated by cpi2fnt */ +/* ------------------------------ */ +/* Combined with the alpha-numeric */ +/* portion of Greg Harp's old PEARL */ +/* font (from earlier versions of */ +/* linux-m86k) by John Shifflett */ +/* */ +/**********************************************/ + +#include + +#define FONTDATAMAX 2048 + +static const unsigned char fontdata_pearl8x8[FONTDATAMAX] = { + + /* 0 0x00 '^@' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 1 0x01 '^A' */ + 0x7e, /* 01111110 */ + 0x81, /* 10000001 */ + 0xa5, /* 10100101 */ + 0x81, /* 10000001 */ + 0xbd, /* 10111101 */ + 0x99, /* 10011001 */ + 0x81, /* 10000001 */ + 0x7e, /* 01111110 */ + + /* 2 0x02 '^B' */ + 0x7e, /* 01111110 */ + 0xff, /* 11111111 */ + 0xdb, /* 11011011 */ + 0xff, /* 11111111 */ + 0xc3, /* 11000011 */ + 0xe7, /* 11100111 */ + 0xff, /* 11111111 */ + 0x7e, /* 01111110 */ + + /* 3 0x03 '^C' */ + 0x6c, /* 01101100 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0x7c, /* 01111100 */ + 0x38, /* 00111000 */ + 0x10, /* 00010000 */ + 0x00, /* 00000000 */ + + /* 4 0x04 '^D' */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x7c, /* 01111100 */ + 0xfe, /* 11111110 */ + 0x7c, /* 01111100 */ + 0x38, /* 00111000 */ + 0x10, /* 00010000 */ + 0x00, /* 00000000 */ + + /* 5 0x05 '^E' */ + 0x38, /* 00111000 */ + 0x7c, /* 01111100 */ + 0x38, /* 00111000 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xd6, /* 11010110 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + + /* 6 0x06 '^F' */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x7c, /* 01111100 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0x7c, /* 01111100 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + + /* 7 0x07 '^G' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 8 0x08 '^H' */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xe7, /* 11100111 */ + 0xc3, /* 11000011 */ + 0xc3, /* 11000011 */ + 0xe7, /* 11100111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + + /* 9 0x09 '^I' */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x42, /* 01000010 */ + 0x42, /* 01000010 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 10 0x0a '^J' */ + 0xff, /* 11111111 */ + 0xc3, /* 11000011 */ + 0x99, /* 10011001 */ + 0xbd, /* 10111101 */ + 0xbd, /* 10111101 */ + 0x99, /* 10011001 */ + 0xc3, /* 11000011 */ + 0xff, /* 11111111 */ + + /* 11 0x0b '^K' */ + 0x0f, /* 00001111 */ + 0x07, /* 00000111 */ + 0x0f, /* 00001111 */ + 0x7d, /* 01111101 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x78, /* 01111000 */ + + /* 12 0x0c '^L' */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + + /* 13 0x0d '^M' */ + 0x3f, /* 00111111 */ + 0x33, /* 00110011 */ + 0x3f, /* 00111111 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x70, /* 01110000 */ + 0xf0, /* 11110000 */ + 0xe0, /* 11100000 */ + + /* 14 0x0e '^N' */ + 0x7f, /* 01111111 */ + 0x63, /* 01100011 */ + 0x7f, /* 01111111 */ + 0x63, /* 01100011 */ + 0x63, /* 01100011 */ + 0x67, /* 01100111 */ + 0xe6, /* 11100110 */ + 0xc0, /* 11000000 */ + + /* 15 0x0f '^O' */ + 0x18, /* 00011000 */ + 0xdb, /* 11011011 */ + 0x3c, /* 00111100 */ + 0xe7, /* 11100111 */ + 0xe7, /* 11100111 */ + 0x3c, /* 00111100 */ + 0xdb, /* 11011011 */ + 0x18, /* 00011000 */ + + /* 16 0x10 '^P' */ + 0x80, /* 10000000 */ + 0xe0, /* 11100000 */ + 0xf8, /* 11111000 */ + 0xfe, /* 11111110 */ + 0xf8, /* 11111000 */ + 0xe0, /* 11100000 */ + 0x80, /* 10000000 */ + 0x00, /* 00000000 */ + + /* 17 0x11 '^Q' */ + 0x02, /* 00000010 */ + 0x0e, /* 00001110 */ + 0x3e, /* 00111110 */ + 0xfe, /* 11111110 */ + 0x3e, /* 00111110 */ + 0x0e, /* 00001110 */ + 0x02, /* 00000010 */ + 0x00, /* 00000000 */ + + /* 18 0x12 '^R' */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + + /* 19 0x13 '^S' */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + + /* 20 0x14 '^T' */ + 0x7f, /* 01111111 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0x7b, /* 01111011 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x00, /* 00000000 */ + + /* 21 0x15 '^U' */ + 0x3e, /* 00111110 */ + 0x61, /* 01100001 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x86, /* 10000110 */ + 0x7c, /* 01111100 */ + + /* 22 0x16 '^V' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + + /* 23 0x17 '^W' */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + + /* 24 0x18 '^X' */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 25 0x19 '^Y' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 26 0x1a '^Z' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0xfe, /* 11111110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 27 0x1b '^[' */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xfe, /* 11111110 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 28 0x1c '^\' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 29 0x1d '^]' */ + 0x00, /* 00000000 */ + 0x24, /* 00100100 */ + 0x66, /* 01100110 */ + 0xff, /* 11111111 */ + 0x66, /* 01100110 */ + 0x24, /* 00100100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 30 0x1e '^^' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 31 0x1f '^_' */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0x7e, /* 01111110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 32 0x20 ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 33 0x21 '!' */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 34 0x22 '"' */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 35 0x23 '#' */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + + /* 36 0x24 '$' */ + 0x18, /* 00011000 */ + 0x3e, /* 00111110 */ + 0x60, /* 01100000 */ + 0x3c, /* 00111100 */ + 0x06, /* 00000110 */ + 0x7c, /* 01111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 37 0x25 '%' */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xcc, /* 11001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x66, /* 01100110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 38 0x26 '&' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x68, /* 01101000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 39 0x27 ''' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 40 0x28 '(' */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x00, /* 00000000 */ + + /* 41 0x29 ')' */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + + /* 42 0x2a '*' */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0xff, /* 11111111 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 43 0x2b '+' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 44 0x2c ',' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + + /* 45 0x2d '-' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 46 0x2e '.' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 47 0x2f '/' */ + 0x03, /* 00000011 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + + /* 48 0x30 '0' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xde, /* 11011110 */ + 0xfe, /* 11111110 */ + 0xf6, /* 11110110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 49 0x31 '1' */ + 0x18, /* 00011000 */ + 0x78, /* 01111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 50 0x32 '2' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + + /* 51 0x33 '3' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0x06, /* 00000110 */ + 0x1c, /* 00011100 */ + 0x06, /* 00000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 52 0x34 '4' */ + 0x1c, /* 00011100 */ + 0x3c, /* 00111100 */ + 0x6c, /* 01101100 */ + 0xcc, /* 11001100 */ + 0xfe, /* 11111110 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x00, /* 00000000 */ + + /* 53 0x35 '5' */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xfc, /* 11111100 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 54 0x36 '6' */ + 0x38, /* 00111000 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0xfc, /* 11111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 55 0x37 '7' */ + 0xfe, /* 11111110 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x00, /* 00000000 */ + + /* 56 0x38 '8' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 57 0x39 '9' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7e, /* 01111110 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + + /* 58 0x3a ':' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 59 0x3b ';' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + + /* 60 0x3c '<' */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x00, /* 00000000 */ + + /* 61 0x3d '=' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 62 0x3e '>' */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + + /* 63 0x3f '?' */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 64 0x40 '@' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xde, /* 11011110 */ + 0xde, /* 11011110 */ + 0xde, /* 11011110 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 65 0x41 'A' */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 66 0x42 'B' */ + 0xfc, /* 11111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfc, /* 11111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfc, /* 11111100 */ + 0x00, /* 00000000 */ + + /* 67 0x43 'C' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 68 0x44 'D' */ + 0xfc, /* 11111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfc, /* 11111100 */ + 0x00, /* 00000000 */ + + /* 69 0x45 'E' */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xf8, /* 11111000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + + /* 70 0x46 'F' */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xf8, /* 11111000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + + /* 71 0x47 'G' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0xce, /* 11001110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 72 0x48 'H' */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 73 0x49 'I' */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + + /* 74 0x4a 'J' */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 75 0x4b 'K' */ + 0xc6, /* 11000110 */ + 0xcc, /* 11001100 */ + 0xd8, /* 11011000 */ + 0xf0, /* 11110000 */ + 0xd8, /* 11011000 */ + 0xcc, /* 11001100 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 76 0x4c 'L' */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + + /* 77 0x4d 'M' */ + 0x82, /* 10000010 */ + 0xc6, /* 11000110 */ + 0xee, /* 11101110 */ + 0xfe, /* 11111110 */ + 0xd6, /* 11010110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 78 0x4e 'N' */ + 0xc6, /* 11000110 */ + 0xe6, /* 11100110 */ + 0xf6, /* 11110110 */ + 0xde, /* 11011110 */ + 0xce, /* 11001110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 79 0x4f 'O' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 80 0x50 'P' */ + 0xfc, /* 11111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfc, /* 11111100 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + + /* 81 0x51 'Q' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xf6, /* 11110110 */ + 0xde, /* 11011110 */ + 0x7c, /* 01111100 */ + 0x06, /* 00000110 */ + + /* 82 0x52 'R' */ + 0xfc, /* 11111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfc, /* 11111100 */ + 0xd8, /* 11011000 */ + 0xcc, /* 11001100 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 83 0x53 'S' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0x60, /* 01100000 */ + 0x38, /* 00111000 */ + 0x0c, /* 00001100 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 84 0x54 'T' */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 85 0x55 'U' */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 86 0x56 'V' */ + 0xc3, /* 11000011 */ + 0xc3, /* 11000011 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 87 0x57 'W' */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xd6, /* 11010110 */ + 0xfe, /* 11111110 */ + 0xee, /* 11101110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 88 0x58 'X' */ + 0xc3, /* 11000011 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0xc3, /* 11000011 */ + 0x00, /* 00000000 */ + + /* 89 0x59 'Y' */ + 0xc3, /* 11000011 */ + 0xc3, /* 11000011 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 90 0x5a 'Z' */ + 0xfe, /* 11111110 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + + /* 91 0x5b '[' */ + 0x3c, /* 00111100 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 92 0x5c '\' */ + 0xc0, /* 11000000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x06, /* 00000110 */ + 0x03, /* 00000011 */ + 0x00, /* 00000000 */ + + /* 93 0x5d ']' */ + 0x3c, /* 00111100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 94 0x5e '^' */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 95 0x5f '_' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + + /* 96 0x60 '`' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 97 0x61 'a' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0x06, /* 00000110 */ + 0x7e, /* 01111110 */ + 0xc6, /* 11000110 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + + /* 98 0x62 'b' */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xfc, /* 11111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfc, /* 11111100 */ + 0x00, /* 00000000 */ + + /* 99 0x63 'c' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 100 0x64 'd' */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x7e, /* 01111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + + /* 101 0x65 'e' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 102 0x66 'f' */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x00, /* 00000000 */ + + /* 103 0x67 'g' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7e, /* 01111110 */ + 0x06, /* 00000110 */ + 0x7c, /* 01111100 */ + + /* 104 0x68 'h' */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xfc, /* 11111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 105 0x69 'i' */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 106 0x6a 'j' */ + 0x06, /* 00000110 */ + 0x00, /* 00000000 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + + /* 107 0x6b 'k' */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xcc, /* 11001100 */ + 0xd8, /* 11011000 */ + 0xf0, /* 11110000 */ + 0xd8, /* 11011000 */ + 0xcc, /* 11001100 */ + 0x00, /* 00000000 */ + + /* 108 0x6c 'l' */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 109 0x6d 'm' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xec, /* 11101100 */ + 0xfe, /* 11111110 */ + 0xd6, /* 11010110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 110 0x6e 'n' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfc, /* 11111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 111 0x6f 'o' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 112 0x70 'p' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfc, /* 11111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfc, /* 11111100 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + + /* 113 0x71 'q' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7e, /* 01111110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + + /* 114 0x72 'r' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0xe6, /* 11100110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + + /* 115 0x73 's' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x06, /* 00000110 */ + 0xfc, /* 11111100 */ + 0x00, /* 00000000 */ + + /* 116 0x74 't' */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x7c, /* 01111100 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x36, /* 00110110 */ + 0x1c, /* 00011100 */ + 0x00, /* 00000000 */ + + /* 117 0x75 'u' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 118 0x76 'v' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + + /* 119 0x77 'w' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xd6, /* 11010110 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + + /* 120 0x78 'x' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 121 0x79 'y' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc3, /* 11000011 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + + /* 122 0x7a 'z' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x0c, /* 00001100 */ + 0x38, /* 00111000 */ + 0x60, /* 01100000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + + /* 123 0x7b '{' */ + 0x0e, /* 00001110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x70, /* 01110000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x0e, /* 00001110 */ + 0x00, /* 00000000 */ + + /* 124 0x7c '|' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 125 0x7d '}' */ + 0x70, /* 01110000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x0e, /* 00001110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + + /* 126 0x7e '~' */ + 0x72, /* 01110010 */ + 0x9c, /* 10011100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 127 0x7f '' */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + + /* 128 0x80 '' */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x0c, /* 00001100 */ + 0x78, /* 01111000 */ + + /* 129 0x81 '' */ + 0xcc, /* 11001100 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 130 0x82 '' */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 131 0x83 '' */ + 0x7c, /* 01111100 */ + 0x82, /* 10000010 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 132 0x84 '' */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 133 0x85 '' */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 134 0x86 '' */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 135 0x87 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0x7e, /* 01111110 */ + 0x0c, /* 00001100 */ + 0x38, /* 00111000 */ + + /* 136 0x88 '' */ + 0x7c, /* 01111100 */ + 0x82, /* 10000010 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 137 0x89 '' */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 138 0x8a '' */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 139 0x8b '' */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 140 0x8c '' */ + 0x7c, /* 01111100 */ + 0x82, /* 10000010 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 141 0x8d '' */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 142 0x8e '' */ + 0xc6, /* 11000110 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 143 0x8f '' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 144 0x90 '' */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xf8, /* 11111000 */ + 0xc0, /* 11000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + + /* 145 0x91 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0xd8, /* 11011000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + + /* 146 0x92 '' */ + 0x3e, /* 00111110 */ + 0x6c, /* 01101100 */ + 0xcc, /* 11001100 */ + 0xfe, /* 11111110 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xce, /* 11001110 */ + 0x00, /* 00000000 */ + + /* 147 0x93 '' */ + 0x7c, /* 01111100 */ + 0x82, /* 10000010 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 148 0x94 '' */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 149 0x95 '' */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 150 0x96 '' */ + 0x78, /* 01111000 */ + 0x84, /* 10000100 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 151 0x97 '' */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 152 0x98 '' */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7e, /* 01111110 */ + 0x06, /* 00000110 */ + 0xfc, /* 11111100 */ + + /* 153 0x99 '' */ + 0xc6, /* 11000110 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + + /* 154 0x9a '' */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 155 0x9b '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 156 0x9c '' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x64, /* 01100100 */ + 0xf0, /* 11110000 */ + 0x60, /* 01100000 */ + 0x66, /* 01100110 */ + 0xfc, /* 11111100 */ + 0x00, /* 00000000 */ + + /* 157 0x9d '' */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 158 0x9e '' */ + 0xf8, /* 11111000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xfa, /* 11111010 */ + 0xc6, /* 11000110 */ + 0xcf, /* 11001111 */ + 0xc6, /* 11000110 */ + 0xc7, /* 11000111 */ + + /* 159 0x9f '' */ + 0x0e, /* 00001110 */ + 0x1b, /* 00011011 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0xd8, /* 11011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + + /* 160 0xa0 '' */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 161 0xa1 '' */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 162 0xa2 '' */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + + /* 163 0xa3 '' */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 164 0xa4 '' */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + + /* 165 0xa5 '' */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0xe6, /* 11100110 */ + 0xf6, /* 11110110 */ + 0xde, /* 11011110 */ + 0xce, /* 11001110 */ + 0x00, /* 00000000 */ + + /* 166 0xa6 '' */ + 0x3c, /* 00111100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x3e, /* 00111110 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 167 0xa7 '' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 168 0xa8 '' */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x63, /* 01100011 */ + 0x3e, /* 00111110 */ + 0x00, /* 00000000 */ + + /* 169 0xa9 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 170 0xaa '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 171 0xab '' */ + 0x63, /* 01100011 */ + 0xe6, /* 11100110 */ + 0x6c, /* 01101100 */ + 0x7e, /* 01111110 */ + 0x33, /* 00110011 */ + 0x66, /* 01100110 */ + 0xcc, /* 11001100 */ + 0x0f, /* 00001111 */ + + /* 172 0xac '' */ + 0x63, /* 01100011 */ + 0xe6, /* 11100110 */ + 0x6c, /* 01101100 */ + 0x7a, /* 01111010 */ + 0x36, /* 00110110 */ + 0x6a, /* 01101010 */ + 0xdf, /* 11011111 */ + 0x06, /* 00000110 */ + + /* 173 0xad '' */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 174 0xae '' */ + 0x00, /* 00000000 */ + 0x33, /* 00110011 */ + 0x66, /* 01100110 */ + 0xcc, /* 11001100 */ + 0x66, /* 01100110 */ + 0x33, /* 00110011 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 175 0xaf '' */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0x66, /* 01100110 */ + 0x33, /* 00110011 */ + 0x66, /* 01100110 */ + 0xcc, /* 11001100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 176 0xb0 '' */ + 0x22, /* 00100010 */ + 0x88, /* 10001000 */ + 0x22, /* 00100010 */ + 0x88, /* 10001000 */ + 0x22, /* 00100010 */ + 0x88, /* 10001000 */ + 0x22, /* 00100010 */ + 0x88, /* 10001000 */ + + /* 177 0xb1 '' */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + + /* 178 0xb2 '' */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + + /* 179 0xb3 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 180 0xb4 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 181 0xb5 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 182 0xb6 '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf6, /* 11110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 183 0xb7 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 184 0xb8 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 185 0xb9 '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf6, /* 11110110 */ + 0x06, /* 00000110 */ + 0xf6, /* 11110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 186 0xba '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 187 0xbb '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x06, /* 00000110 */ + 0xf6, /* 11110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 188 0xbc '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf6, /* 11110110 */ + 0x06, /* 00000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 189 0xbd '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 190 0xbe '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 191 0xbf '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 192 0xc0 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 193 0xc1 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 194 0xc2 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 195 0xc3 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 196 0xc4 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 197 0xc5 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 198 0xc6 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 199 0xc7 '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x37, /* 00110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 200 0xc8 '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x37, /* 00110111 */ + 0x30, /* 00110000 */ + 0x3f, /* 00111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 201 0xc9 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3f, /* 00111111 */ + 0x30, /* 00110000 */ + 0x37, /* 00110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 202 0xca '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf7, /* 11110111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 203 0xcb '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xf7, /* 11110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 204 0xcc '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x37, /* 00110111 */ + 0x30, /* 00110000 */ + 0x37, /* 00110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 205 0xcd '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 206 0xce '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf7, /* 11110111 */ + 0x00, /* 00000000 */ + 0xf7, /* 11110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 207 0xcf '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 208 0xd0 '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 209 0xd1 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 210 0xd2 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 211 0xd3 '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x3f, /* 00111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 212 0xd4 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 213 0xd5 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 214 0xd6 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3f, /* 00111111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 215 0xd7 '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xff, /* 11111111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 216 0xd8 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 217 0xd9 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 218 0xda '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 219 0xdb '' */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + + /* 220 0xdc '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + + /* 221 0xdd '' */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + + /* 222 0xde '' */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + + /* 223 0xdf '' */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 224 0xe0 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0xc8, /* 11001000 */ + 0xdc, /* 11011100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + + /* 225 0xe1 '' */ + 0x78, /* 01111000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xd8, /* 11011000 */ + 0xcc, /* 11001100 */ + 0xc6, /* 11000110 */ + 0xcc, /* 11001100 */ + 0x00, /* 00000000 */ + + /* 226 0xe2 '' */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + + /* 227 0xe3 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + + /* 228 0xe4 '' */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + + /* 229 0xe5 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + + /* 230 0xe6 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0xc0, /* 11000000 */ + + /* 231 0xe7 '' */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + + /* 232 0xe8 '' */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + + /* 233 0xe9 '' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + + /* 234 0xea '' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0xee, /* 11101110 */ + 0x00, /* 00000000 */ + + /* 235 0xeb '' */ + 0x0e, /* 00001110 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x3e, /* 00111110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 236 0xec '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 237 0xed '' */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x7e, /* 01111110 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0x7e, /* 01111110 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + + /* 238 0xee '' */ + 0x1e, /* 00011110 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x7e, /* 01111110 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x1e, /* 00011110 */ + 0x00, /* 00000000 */ + + /* 239 0xef '' */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + + /* 240 0xf0 '' */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 241 0xf1 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + + /* 242 0xf2 '' */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + + /* 243 0xf3 '' */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + + /* 244 0xf4 '' */ + 0x0e, /* 00001110 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 245 0xf5 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0x70, /* 01110000 */ + + /* 246 0xf6 '' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 247 0xf7 '' */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 248 0xf8 '' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 249 0xf9 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 250 0xfa '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 251 0xfb '' */ + 0x0f, /* 00001111 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0xec, /* 11101100 */ + 0x6c, /* 01101100 */ + 0x3c, /* 00111100 */ + 0x1c, /* 00011100 */ + + /* 252 0xfc '' */ + 0x6c, /* 01101100 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 253 0xfd '' */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 254 0xfe '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 255 0xff '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + +}; + +const struct font_desc font_pearl_8x8 = { + .idx = PEARL8x8_IDX, + .name = "PEARL8x8", + .width = 8, + .height = 8, + .data = fontdata_pearl8x8, + .pref = 2, +}; diff --git a/lib/fonts/font_sun12x22.c b/lib/fonts/font_sun12x22.c new file mode 100644 index 000000000000..d3643853c33a --- /dev/null +++ b/lib/fonts/font_sun12x22.c @@ -0,0 +1,6165 @@ +#include + +#define FONTDATAMAX 11264 + +static const unsigned char fontdata_sun12x22[FONTDATAMAX] = { + + /* 0 0x00 '^@' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 1 0x01 '^A' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x1f, 0xc0, /* 000111111100 */ + 0x30, 0x60, /* 001100000110 */ + 0x65, 0x30, /* 011001010011 */ + 0x6d, 0xb0, /* 011011011011 */ + 0x60, 0x30, /* 011000000011 */ + 0x62, 0x30, /* 011000100011 */ + 0x62, 0x30, /* 011000100011 */ + 0x60, 0x30, /* 011000000011 */ + 0x6f, 0xb0, /* 011011111011 */ + 0x67, 0x30, /* 011001110011 */ + 0x30, 0x60, /* 001100000110 */ + 0x1f, 0xc0, /* 000111111100 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 2 0x02 '^B' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x1f, 0xc0, /* 000111111100 */ + 0x3f, 0xe0, /* 001111111110 */ + 0x7a, 0xf0, /* 011110101111 */ + 0x72, 0x70, /* 011100100111 */ + 0x7f, 0xf0, /* 011111111111 */ + 0x7d, 0xf0, /* 011111011111 */ + 0x7d, 0xf0, /* 011111011111 */ + 0x7f, 0xf0, /* 011111111111 */ + 0x70, 0x70, /* 011100000111 */ + 0x78, 0xf0, /* 011110001111 */ + 0x3f, 0xe0, /* 001111111110 */ + 0x1f, 0xc0, /* 000111111100 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 3 0x03 '^C' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x19, 0x80, /* 000110011000 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x1f, 0x80, /* 000111111000 */ + 0x1f, 0x80, /* 000111111000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x06, 0x00, /* 000001100000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 4 0x04 '^D' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x02, 0x00, /* 000000100000 */ + 0x07, 0x00, /* 000001110000 */ + 0x0f, 0x80, /* 000011111000 */ + 0x0f, 0x80, /* 000011111000 */ + 0x1f, 0xc0, /* 000111111100 */ + 0x1f, 0xc0, /* 000111111100 */ + 0x3f, 0xe0, /* 001111111110 */ + 0x1f, 0xc0, /* 000111111100 */ + 0x1f, 0xc0, /* 000111111100 */ + 0x0f, 0x80, /* 000011111000 */ + 0x0f, 0x80, /* 000011111000 */ + 0x07, 0x00, /* 000001110000 */ + 0x02, 0x00, /* 000000100000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 5 0x05 '^E' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x02, 0x00, /* 000000100000 */ + 0x07, 0x00, /* 000001110000 */ + 0x07, 0x00, /* 000001110000 */ + 0x02, 0x00, /* 000000100000 */ + 0x18, 0xc0, /* 000110001100 */ + 0x3d, 0xe0, /* 001111011110 */ + 0x3d, 0xe0, /* 001111011110 */ + 0x1a, 0xc0, /* 000110101100 */ + 0x02, 0x00, /* 000000100000 */ + 0x07, 0x00, /* 000001110000 */ + 0x0f, 0x80, /* 000011111000 */ + 0x1f, 0xc0, /* 000111111100 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 6 0x06 '^F' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x06, 0x00, /* 000001100000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x1f, 0x80, /* 000111111000 */ + 0x1f, 0x80, /* 000111111000 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x36, 0xc0, /* 001101101100 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x1f, 0x80, /* 000111111000 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 7 0x07 '^G' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x06, 0x00, /* 000001100000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x1f, 0x80, /* 000111111000 */ + 0x1f, 0x80, /* 000111111000 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x1f, 0x80, /* 000111111000 */ + 0x1f, 0x80, /* 000111111000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x06, 0x00, /* 000001100000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 8 0x08 '^H' */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xf9, 0xf0, /* 111110011111 */ + 0xf0, 0xf0, /* 111100001111 */ + 0xf0, 0xf0, /* 111100001111 */ + 0xe0, 0x70, /* 111000000111 */ + 0xe0, 0x70, /* 111000000111 */ + 0xc0, 0x30, /* 110000000011 */ + 0xc0, 0x30, /* 110000000011 */ + 0xe0, 0x70, /* 111000000111 */ + 0xe0, 0x70, /* 111000000111 */ + 0xf0, 0xf0, /* 111100001111 */ + 0xf0, 0xf0, /* 111100001111 */ + 0xf9, 0xf0, /* 111110011111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + + /* 9 0x09 '^I' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x06, 0x00, /* 000001100000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x19, 0x80, /* 000110011000 */ + 0x19, 0x80, /* 000110011000 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x19, 0x80, /* 000110011000 */ + 0x19, 0x80, /* 000110011000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x06, 0x00, /* 000001100000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 10 0x0a '^J' */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xf9, 0xf0, /* 111110011111 */ + 0xf0, 0xf0, /* 111100001111 */ + 0xf0, 0xf0, /* 111100001111 */ + 0xe6, 0x70, /* 111001100111 */ + 0xe6, 0x70, /* 111001100111 */ + 0xcf, 0x30, /* 110011110011 */ + 0xcf, 0x30, /* 110011110011 */ + 0xe6, 0x70, /* 111001100111 */ + 0xe6, 0x70, /* 111001100111 */ + 0xf0, 0xf0, /* 111100001111 */ + 0xf0, 0xf0, /* 111100001111 */ + 0xf9, 0xf0, /* 111110011111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + + /* 11 0x0b '^K' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0xe0, /* 000011111110 */ + 0x0f, 0xe0, /* 000011111110 */ + 0x01, 0xe0, /* 000000011110 */ + 0x03, 0x60, /* 000000110110 */ + 0x06, 0x60, /* 000001100110 */ + 0x1e, 0x00, /* 000111100000 */ + 0x33, 0x00, /* 001100110000 */ + 0x33, 0x00, /* 001100110000 */ + 0x61, 0x80, /* 011000011000 */ + 0x61, 0x80, /* 011000011000 */ + 0x33, 0x00, /* 001100110000 */ + 0x33, 0x00, /* 001100110000 */ + 0x1e, 0x00, /* 000111100000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 12 0x0c '^L' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x06, 0x00, /* 000001100000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x19, 0x80, /* 000110011000 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x19, 0x80, /* 000110011000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 13 0x0d '^M' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0xe0, /* 000011111110 */ + 0x0c, 0x60, /* 000011000110 */ + 0x0c, 0x60, /* 000011000110 */ + 0x0f, 0xe0, /* 000011111110 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x3c, 0x00, /* 001111000000 */ + 0x7c, 0x00, /* 011111000000 */ + 0x78, 0x00, /* 011110000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 14 0x0e '^N' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x1f, 0xe0, /* 000111111110 */ + 0x18, 0x60, /* 000110000110 */ + 0x18, 0x60, /* 000110000110 */ + 0x1f, 0xe0, /* 000111111110 */ + 0x18, 0x60, /* 000110000110 */ + 0x18, 0x60, /* 000110000110 */ + 0x18, 0x60, /* 000110000110 */ + 0x18, 0x60, /* 000110000110 */ + 0x18, 0x60, /* 000110000110 */ + 0x18, 0x60, /* 000110000110 */ + 0x19, 0xe0, /* 000110011110 */ + 0x1b, 0xe0, /* 000110111110 */ + 0x1b, 0xc0, /* 000110111100 */ + 0x79, 0x80, /* 011110011000 */ + 0xf8, 0x00, /* 111110000000 */ + 0xf0, 0x00, /* 111100000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 15 0x0f '^O' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x18, 0xc0, /* 000110001100 */ + 0x0d, 0x80, /* 000011011000 */ + 0x6d, 0xb0, /* 011011011011 */ + 0x3d, 0xe0, /* 001111011110 */ + 0x00, 0x00, /* 000000000000 */ + 0x3d, 0xe0, /* 001111011110 */ + 0x6d, 0xb0, /* 011011011011 */ + 0x0d, 0x80, /* 000011011000 */ + 0x18, 0xc0, /* 000110001100 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 16 0x10 '^P' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x20, /* 000000000010 */ + 0x00, 0x60, /* 000000000110 */ + 0x00, 0xe0, /* 000000001110 */ + 0x01, 0xe0, /* 000000011110 */ + 0x03, 0xe0, /* 000000111110 */ + 0x07, 0xe0, /* 000001111110 */ + 0x0f, 0xe0, /* 000011111110 */ + 0x1f, 0xe0, /* 000111111110 */ + 0x3f, 0xe0, /* 001111111110 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x3f, 0xe0, /* 001111111110 */ + 0x1f, 0xe0, /* 000111111110 */ + 0x0f, 0xe0, /* 000011111110 */ + 0x07, 0xe0, /* 000001111110 */ + 0x03, 0xe0, /* 000000111110 */ + 0x01, 0xe0, /* 000000011110 */ + 0x00, 0xe0, /* 000000001110 */ + 0x00, 0x60, /* 000000000110 */ + 0x00, 0x20, /* 000000000010 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 17 0x11 '^Q' */ + 0x00, 0x00, /* 000000000000 */ + 0x40, 0x00, /* 010000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x70, 0x00, /* 011100000000 */ + 0x78, 0x00, /* 011110000000 */ + 0x7c, 0x00, /* 011111000000 */ + 0x7e, 0x00, /* 011111100000 */ + 0x7f, 0x00, /* 011111110000 */ + 0x7f, 0x80, /* 011111111000 */ + 0x7f, 0xc0, /* 011111111100 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x7f, 0xc0, /* 011111111100 */ + 0x7f, 0x80, /* 011111111000 */ + 0x7f, 0x00, /* 011111110000 */ + 0x7e, 0x00, /* 011111100000 */ + 0x7c, 0x00, /* 011111000000 */ + 0x78, 0x00, /* 011110000000 */ + 0x70, 0x00, /* 011100000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x40, 0x00, /* 010000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 18 0x12 '^R' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x04, 0x00, /* 000001000000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x1f, 0x00, /* 000111110000 */ + 0x3f, 0x80, /* 001111111000 */ + 0x7f, 0xc0, /* 011111111100 */ + 0x0e, 0x00, /* 000011100000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x7f, 0xc0, /* 011111111100 */ + 0x3f, 0x80, /* 001111111000 */ + 0x1f, 0x00, /* 000111110000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x04, 0x00, /* 000001000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 19 0x13 '^S' */ + 0x00, 0x00, /* 000000000000 */ + 0x31, 0x80, /* 001100011000 */ + 0x31, 0x80, /* 001100011000 */ + 0x31, 0x80, /* 001100011000 */ + 0x31, 0x80, /* 001100011000 */ + 0x31, 0x80, /* 001100011000 */ + 0x31, 0x80, /* 001100011000 */ + 0x31, 0x80, /* 001100011000 */ + 0x31, 0x80, /* 001100011000 */ + 0x31, 0x80, /* 001100011000 */ + 0x31, 0x80, /* 001100011000 */ + 0x31, 0x80, /* 001100011000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x31, 0x80, /* 001100011000 */ + 0x31, 0x80, /* 001100011000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 20 0x14 '^T' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x1f, 0xf0, /* 000111111111 */ + 0x3c, 0xc0, /* 001111001100 */ + 0x7c, 0xc0, /* 011111001100 */ + 0x7c, 0xc0, /* 011111001100 */ + 0x7c, 0xc0, /* 011111001100 */ + 0x3c, 0xc0, /* 001111001100 */ + 0x1c, 0xc0, /* 000111001100 */ + 0x0c, 0xc0, /* 000011001100 */ + 0x0c, 0xc0, /* 000011001100 */ + 0x0c, 0xc0, /* 000011001100 */ + 0x0c, 0xc0, /* 000011001100 */ + 0x0c, 0xc0, /* 000011001100 */ + 0x0c, 0xc0, /* 000011001100 */ + 0x1c, 0xe0, /* 000111001110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 21 0x15 '^U' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x1f, 0x00, /* 000111110000 */ + 0x31, 0x80, /* 001100011000 */ + 0x31, 0x80, /* 001100011000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x1f, 0x00, /* 000111110000 */ + 0x31, 0x80, /* 001100011000 */ + 0x31, 0x80, /* 001100011000 */ + 0x1f, 0x00, /* 000111110000 */ + 0x01, 0x80, /* 000000011000 */ + 0x01, 0x80, /* 000000011000 */ + 0x31, 0x80, /* 001100011000 */ + 0x31, 0x80, /* 001100011000 */ + 0x1f, 0x00, /* 000111110000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 22 0x16 '^V' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 23 0x17 '^W' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x04, 0x00, /* 000001000000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x1f, 0x00, /* 000111110000 */ + 0x3f, 0x80, /* 001111111000 */ + 0x7f, 0xc0, /* 011111111100 */ + 0x0e, 0x00, /* 000011100000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x7f, 0xc0, /* 011111111100 */ + 0x3f, 0x80, /* 001111111000 */ + 0x1f, 0x00, /* 000111110000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x04, 0x00, /* 000001000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 24 0x18 '^X' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x04, 0x00, /* 000001000000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x1f, 0x00, /* 000111110000 */ + 0x3f, 0x80, /* 001111111000 */ + 0x7f, 0xc0, /* 011111111100 */ + 0x0e, 0x00, /* 000011100000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 25 0x19 '^Y' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x7f, 0xc0, /* 011111111100 */ + 0x3f, 0x80, /* 001111111000 */ + 0x1f, 0x00, /* 000111110000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x04, 0x00, /* 000001000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 26 0x1a '^Z' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x08, 0x00, /* 000010000000 */ + 0x18, 0x00, /* 000110000000 */ + 0x38, 0x00, /* 001110000000 */ + 0x7f, 0xe0, /* 011111111110 */ + 0xff, 0xe0, /* 111111111110 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x38, 0x00, /* 001110000000 */ + 0x18, 0x00, /* 000110000000 */ + 0x08, 0x00, /* 000010000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 27 0x1b '^[' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x01, 0x00, /* 000000010000 */ + 0x01, 0x80, /* 000000011000 */ + 0x01, 0xc0, /* 000000011100 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x7f, 0xf0, /* 011111111111 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x01, 0xc0, /* 000000011100 */ + 0x01, 0x80, /* 000000011000 */ + 0x01, 0x00, /* 000000010000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 28 0x1c '^\' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x3f, 0xe0, /* 001111111110 */ + 0x3f, 0xe0, /* 001111111110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 29 0x1d '^]' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x09, 0x00, /* 000010010000 */ + 0x19, 0x80, /* 000110011000 */ + 0x39, 0xc0, /* 001110011100 */ + 0x7f, 0xe0, /* 011111111110 */ + 0xff, 0xf0, /* 111111111111 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x39, 0xc0, /* 001110011100 */ + 0x19, 0x80, /* 000110011000 */ + 0x09, 0x00, /* 000010010000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 30 0x1e '^^' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x04, 0x00, /* 000001000000 */ + 0x04, 0x00, /* 000001000000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x1f, 0x00, /* 000111110000 */ + 0x1f, 0x00, /* 000111110000 */ + 0x3f, 0x80, /* 001111111000 */ + 0x3f, 0x80, /* 001111111000 */ + 0x7f, 0xc0, /* 011111111100 */ + 0x7f, 0xc0, /* 011111111100 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 31 0x1f '^_' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x7f, 0xc0, /* 011111111100 */ + 0x7f, 0xc0, /* 011111111100 */ + 0x3f, 0x80, /* 001111111000 */ + 0x3f, 0x80, /* 001111111000 */ + 0x1f, 0x00, /* 000111110000 */ + 0x1f, 0x00, /* 000111110000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x04, 0x00, /* 000001000000 */ + 0x04, 0x00, /* 000001000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 32 0x20 ' ' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 33 0x21 '!' */ + 0x00, 0x00, /* 000000000000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 34 0x22 '"' */ + 0x00, 0x00, /* 000000000000 */ + 0x19, 0x80, /* 000110011000 */ + 0x19, 0x80, /* 000110011000 */ + 0x19, 0x80, /* 000110011000 */ + 0x19, 0x80, /* 000110011000 */ + 0x19, 0x80, /* 000110011000 */ + 0x19, 0x80, /* 000110011000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 35 0x23 '#' */ + 0x00, 0x00, /* 000000000000 */ + 0x03, 0x30, /* 000000110011 */ + 0x03, 0x30, /* 000000110011 */ + 0x03, 0x30, /* 000000110011 */ + 0x06, 0x60, /* 000001100110 */ + 0x1f, 0xf0, /* 000111111111 */ + 0x1f, 0xf0, /* 000111111111 */ + 0x0c, 0xc0, /* 000011001100 */ + 0x0c, 0xc0, /* 000011001100 */ + 0x19, 0x80, /* 000110011000 */ + 0x19, 0x80, /* 000110011000 */ + 0x7f, 0xc0, /* 011111111100 */ + 0x7f, 0xc0, /* 011111111100 */ + 0x33, 0x00, /* 001100110000 */ + 0x66, 0x00, /* 011001100000 */ + 0x66, 0x00, /* 011001100000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 36 0x24 '$' */ + 0x00, 0x00, /* 000000000000 */ + 0x06, 0x00, /* 000001100000 */ + 0x1f, 0x80, /* 000111111000 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x66, 0xe0, /* 011001101110 */ + 0x66, 0x60, /* 011001100110 */ + 0x66, 0x00, /* 011001100000 */ + 0x3e, 0x00, /* 001111100000 */ + 0x1f, 0x80, /* 000111111000 */ + 0x07, 0xc0, /* 000001111100 */ + 0x06, 0x60, /* 000001100110 */ + 0x06, 0x60, /* 000001100110 */ + 0x66, 0x60, /* 011001100110 */ + 0x7f, 0xc0, /* 011111111100 */ + 0x3f, 0x80, /* 001111111000 */ + 0x06, 0x00, /* 000001100000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 37 0x25 '%' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x38, 0xc0, /* 001110001100 */ + 0x4c, 0xc0, /* 010011001100 */ + 0x45, 0x80, /* 010001011000 */ + 0x65, 0x80, /* 011001011000 */ + 0x3b, 0x00, /* 001110110000 */ + 0x03, 0x00, /* 000000110000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0d, 0xc0, /* 000011011100 */ + 0x1a, 0x60, /* 000110100110 */ + 0x1a, 0x20, /* 000110100010 */ + 0x33, 0x20, /* 001100110010 */ + 0x31, 0xc0, /* 001100011100 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 38 0x26 '&' */ + 0x00, 0x00, /* 000000000000 */ + 0x07, 0x00, /* 000001110000 */ + 0x0f, 0x80, /* 000011111000 */ + 0x18, 0xc0, /* 000110001100 */ + 0x18, 0xc0, /* 000110001100 */ + 0x18, 0xc0, /* 000110001100 */ + 0x0f, 0x80, /* 000011111000 */ + 0x1e, 0x00, /* 000111100000 */ + 0x3e, 0x00, /* 001111100000 */ + 0x77, 0x00, /* 011101110000 */ + 0x63, 0x60, /* 011000110110 */ + 0x61, 0xe0, /* 011000011110 */ + 0x61, 0xc0, /* 011000011100 */ + 0x61, 0x80, /* 011000011000 */ + 0x3f, 0xe0, /* 001111111110 */ + 0x1e, 0x60, /* 000111100110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 39 0x27 ''' */ + 0x00, 0x00, /* 000000000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x1e, 0x00, /* 000111100000 */ + 0x1e, 0x00, /* 000111100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x18, 0x00, /* 000110000000 */ + 0x10, 0x00, /* 000100000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 40 0x28 '(' */ + 0x00, 0x00, /* 000000000000 */ + 0x01, 0x80, /* 000000011000 */ + 0x03, 0x00, /* 000000110000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x03, 0x00, /* 000000110000 */ + 0x01, 0x80, /* 000000011000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 41 0x29 ')' */ + 0x00, 0x00, /* 000000000000 */ + 0x18, 0x00, /* 000110000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x03, 0x00, /* 000000110000 */ + 0x03, 0x00, /* 000000110000 */ + 0x03, 0x00, /* 000000110000 */ + 0x03, 0x00, /* 000000110000 */ + 0x03, 0x00, /* 000000110000 */ + 0x03, 0x00, /* 000000110000 */ + 0x03, 0x00, /* 000000110000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x18, 0x00, /* 000110000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 42 0x2a '*' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x06, 0x00, /* 000001100000 */ + 0x66, 0x60, /* 011001100110 */ + 0x76, 0xe0, /* 011101101110 */ + 0x19, 0x80, /* 000110011000 */ + 0x00, 0x00, /* 000000000000 */ + 0x19, 0x80, /* 000110011000 */ + 0x76, 0xe0, /* 011101101110 */ + 0x66, 0x60, /* 011001100110 */ + 0x06, 0x00, /* 000001100000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 43 0x2b '+' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 44 0x2c ',' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x1e, 0x00, /* 000111100000 */ + 0x1e, 0x00, /* 000111100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x18, 0x00, /* 000110000000 */ + 0x10, 0x00, /* 000100000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 45 0x2d '-' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 46 0x2e '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x1e, 0x00, /* 000111100000 */ + 0x1e, 0x00, /* 000111100000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 47 0x2f '/' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x60, /* 000000000110 */ + 0x00, 0xc0, /* 000000001100 */ + 0x00, 0xc0, /* 000000001100 */ + 0x01, 0x80, /* 000000011000 */ + 0x01, 0x80, /* 000000011000 */ + 0x03, 0x00, /* 000000110000 */ + 0x03, 0x00, /* 000000110000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x18, 0x00, /* 000110000000 */ + 0x18, 0x00, /* 000110000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 48 0x30 '0' */ + 0x00, 0x00, /* 000000000000 */ + 0x07, 0x00, /* 000001110000 */ + 0x0f, 0x80, /* 000011111000 */ + 0x11, 0x80, /* 000100011000 */ + 0x10, 0xc0, /* 000100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0x80, /* 001100001000 */ + 0x18, 0x80, /* 000110001000 */ + 0x1f, 0x00, /* 000111110000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 49 0x31 '1' */ + 0x00, 0x00, /* 000000000000 */ + 0x02, 0x00, /* 000000100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x1e, 0x00, /* 000111100000 */ + 0x36, 0x00, /* 001101100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 50 0x32 '2' */ + 0x00, 0x00, /* 000000000000 */ + 0x1f, 0x00, /* 000111110000 */ + 0x3f, 0x80, /* 001111111000 */ + 0x61, 0xc0, /* 011000011100 */ + 0x40, 0xc0, /* 010000001100 */ + 0x00, 0xc0, /* 000000001100 */ + 0x00, 0xc0, /* 000000001100 */ + 0x00, 0xc0, /* 000000001100 */ + 0x01, 0x80, /* 000000011000 */ + 0x03, 0x00, /* 000000110000 */ + 0x06, 0x00, /* 000001100000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x18, 0x00, /* 000110000000 */ + 0x30, 0x20, /* 001100000010 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 51 0x33 '3' */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0x80, /* 000011111000 */ + 0x1f, 0xc0, /* 000111111100 */ + 0x20, 0xe0, /* 001000001110 */ + 0x40, 0x60, /* 010000000110 */ + 0x00, 0x60, /* 000000000110 */ + 0x00, 0xe0, /* 000000001110 */ + 0x07, 0xc0, /* 000001111100 */ + 0x0f, 0xc0, /* 000011111100 */ + 0x00, 0xe0, /* 000000001110 */ + 0x00, 0x60, /* 000000000110 */ + 0x00, 0x60, /* 000000000110 */ + 0x40, 0x60, /* 010000000110 */ + 0x60, 0x40, /* 011000000100 */ + 0x3f, 0x80, /* 001111111000 */ + 0x1f, 0x00, /* 000111110000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 52 0x34 '4' */ + 0x00, 0x00, /* 000000000000 */ + 0x01, 0x80, /* 000000011000 */ + 0x03, 0x80, /* 000000111000 */ + 0x03, 0x80, /* 000000111000 */ + 0x05, 0x80, /* 000001011000 */ + 0x05, 0x80, /* 000001011000 */ + 0x09, 0x80, /* 000010011000 */ + 0x09, 0x80, /* 000010011000 */ + 0x11, 0x80, /* 000100011000 */ + 0x11, 0x80, /* 000100011000 */ + 0x21, 0x80, /* 001000011000 */ + 0x3f, 0xe0, /* 001111111110 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x01, 0x80, /* 000000011000 */ + 0x01, 0x80, /* 000000011000 */ + 0x01, 0x80, /* 000000011000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 53 0x35 '5' */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0xc0, /* 000011111100 */ + 0x0f, 0xc0, /* 000011111100 */ + 0x10, 0x00, /* 000100000000 */ + 0x10, 0x00, /* 000100000000 */ + 0x20, 0x00, /* 001000000000 */ + 0x3f, 0x80, /* 001111111000 */ + 0x31, 0xc0, /* 001100011100 */ + 0x00, 0xe0, /* 000000001110 */ + 0x00, 0x60, /* 000000000110 */ + 0x00, 0x60, /* 000000000110 */ + 0x00, 0x60, /* 000000000110 */ + 0x40, 0x60, /* 010000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x30, 0xc0, /* 001100001100 */ + 0x1f, 0x80, /* 000111111000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 54 0x36 '6' */ + 0x00, 0x00, /* 000000000000 */ + 0x07, 0x00, /* 000001110000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x18, 0x00, /* 000110000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x67, 0x80, /* 011001111000 */ + 0x6f, 0xc0, /* 011011111100 */ + 0x70, 0xe0, /* 011100001110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x70, 0x40, /* 011100000100 */ + 0x3f, 0x80, /* 001111111000 */ + 0x1f, 0x00, /* 000111110000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 55 0x37 '7' */ + 0x00, 0x00, /* 000000000000 */ + 0x1f, 0xe0, /* 000111111110 */ + 0x3f, 0xe0, /* 001111111110 */ + 0x60, 0x40, /* 011000000100 */ + 0x00, 0x40, /* 000000000100 */ + 0x00, 0xc0, /* 000000001100 */ + 0x00, 0x80, /* 000000001000 */ + 0x00, 0x80, /* 000000001000 */ + 0x01, 0x80, /* 000000011000 */ + 0x01, 0x00, /* 000000010000 */ + 0x01, 0x00, /* 000000010000 */ + 0x03, 0x00, /* 000000110000 */ + 0x02, 0x00, /* 000000100000 */ + 0x02, 0x00, /* 000000100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x04, 0x00, /* 000001000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 56 0x38 '8' */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x11, 0x80, /* 000100011000 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x18, 0x80, /* 000110001000 */ + 0x0d, 0x00, /* 000011010000 */ + 0x06, 0x00, /* 000001100000 */ + 0x0b, 0x00, /* 000010110000 */ + 0x11, 0x80, /* 000100011000 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x18, 0x80, /* 000110001000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 57 0x39 '9' */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0x80, /* 000011111000 */ + 0x11, 0xc0, /* 000100011100 */ + 0x20, 0xe0, /* 001000001110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x70, 0xe0, /* 011100001110 */ + 0x3f, 0x60, /* 001111110110 */ + 0x1e, 0x60, /* 000111100110 */ + 0x00, 0x60, /* 000000000110 */ + 0x00, 0xc0, /* 000000001100 */ + 0x00, 0xc0, /* 000000001100 */ + 0x01, 0x80, /* 000000011000 */ + 0x07, 0x00, /* 000001110000 */ + 0x3c, 0x00, /* 001111000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 58 0x3a ':' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x1e, 0x00, /* 000111100000 */ + 0x1e, 0x00, /* 000111100000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x1e, 0x00, /* 000111100000 */ + 0x1e, 0x00, /* 000111100000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 59 0x3b ';' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x1e, 0x00, /* 000111100000 */ + 0x1e, 0x00, /* 000111100000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x1e, 0x00, /* 000111100000 */ + 0x1e, 0x00, /* 000111100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x18, 0x00, /* 000110000000 */ + 0x10, 0x00, /* 000100000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 60 0x3c '<' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x60, /* 000000000110 */ + 0x01, 0xc0, /* 000000011100 */ + 0x07, 0x00, /* 000001110000 */ + 0x1c, 0x00, /* 000111000000 */ + 0x70, 0x00, /* 011100000000 */ + 0x70, 0x00, /* 011100000000 */ + 0x1c, 0x00, /* 000111000000 */ + 0x07, 0x00, /* 000001110000 */ + 0x01, 0xc0, /* 000000011100 */ + 0x00, 0x60, /* 000000000110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 61 0x3d '=' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 62 0x3e '>' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x38, 0x00, /* 001110000000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x03, 0x80, /* 000000111000 */ + 0x00, 0xe0, /* 000000001110 */ + 0x00, 0xe0, /* 000000001110 */ + 0x03, 0x80, /* 000000111000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x38, 0x00, /* 001110000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 63 0x3f '?' */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x1f, 0x80, /* 000111111000 */ + 0x39, 0xc0, /* 001110011100 */ + 0x20, 0xc0, /* 001000001100 */ + 0x00, 0xc0, /* 000000001100 */ + 0x00, 0xc0, /* 000000001100 */ + 0x01, 0x80, /* 000000011000 */ + 0x03, 0x00, /* 000000110000 */ + 0x06, 0x00, /* 000001100000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 64 0x40 '@' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0x80, /* 000011111000 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x30, 0x60, /* 001100000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x67, 0x20, /* 011001110010 */ + 0x6f, 0xa0, /* 011011111010 */ + 0x6c, 0xa0, /* 011011001010 */ + 0x6c, 0xa0, /* 011011001010 */ + 0x67, 0xe0, /* 011001111110 */ + 0x60, 0x00, /* 011000000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x3f, 0xe0, /* 001111111110 */ + 0x0f, 0xe0, /* 000011111110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 65 0x41 'A' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x0b, 0x00, /* 000010110000 */ + 0x0b, 0x00, /* 000010110000 */ + 0x09, 0x00, /* 000010010000 */ + 0x11, 0x80, /* 000100011000 */ + 0x11, 0x80, /* 000100011000 */ + 0x10, 0x80, /* 000100001000 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x20, 0xc0, /* 001000001100 */ + 0x20, 0x40, /* 001000000100 */ + 0x40, 0x60, /* 010000000110 */ + 0x40, 0x60, /* 010000000110 */ + 0xe0, 0xf0, /* 111000001111 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 66 0x42 'B' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0xff, 0x00, /* 111111110000 */ + 0x60, 0x80, /* 011000001000 */ + 0x60, 0xc0, /* 011000001100 */ + 0x60, 0xc0, /* 011000001100 */ + 0x60, 0xc0, /* 011000001100 */ + 0x61, 0x80, /* 011000011000 */ + 0x7f, 0x80, /* 011111111000 */ + 0x60, 0xc0, /* 011000001100 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0xc0, /* 011000001100 */ + 0xff, 0x80, /* 111111111000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 67 0x43 'C' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0xc0, /* 000011111100 */ + 0x10, 0x60, /* 000100000110 */ + 0x20, 0x20, /* 001000000010 */ + 0x20, 0x00, /* 001000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x20, 0x00, /* 001000000000 */ + 0x30, 0x20, /* 001100000010 */ + 0x18, 0x40, /* 000110000100 */ + 0x0f, 0x80, /* 000011111000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 68 0x44 'D' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0xff, 0x00, /* 111111110000 */ + 0x61, 0xc0, /* 011000011100 */ + 0x60, 0xc0, /* 011000001100 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x40, /* 011000000100 */ + 0x61, 0x80, /* 011000011000 */ + 0xfe, 0x00, /* 111111100000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 69 0x45 'E' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x7f, 0xc0, /* 011111111100 */ + 0x30, 0x40, /* 001100000100 */ + 0x30, 0x40, /* 001100000100 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x80, /* 001100001000 */ + 0x3f, 0x80, /* 001111111000 */ + 0x30, 0x80, /* 001100001000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x20, /* 001100000010 */ + 0x30, 0x20, /* 001100000010 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 70 0x46 'F' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x7f, 0xc0, /* 011111111100 */ + 0x30, 0x40, /* 001100000100 */ + 0x30, 0x40, /* 001100000100 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x80, /* 001100001000 */ + 0x3f, 0x80, /* 001111111000 */ + 0x30, 0x80, /* 001100001000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x78, 0x00, /* 011110000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 71 0x47 'G' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0xc0, /* 000011111100 */ + 0x10, 0x60, /* 000100000110 */ + 0x20, 0x20, /* 001000000010 */ + 0x20, 0x00, /* 001000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x61, 0xf0, /* 011000011111 */ + 0x60, 0x60, /* 011000000110 */ + 0x20, 0x60, /* 001000000110 */ + 0x30, 0x60, /* 001100000110 */ + 0x18, 0x60, /* 000110000110 */ + 0x0f, 0x80, /* 000011111000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 72 0x48 'H' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0xf0, 0xf0, /* 111100001111 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0xf0, 0xf0, /* 111100001111 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 73 0x49 'I' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x1f, 0x80, /* 000111111000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x1f, 0x80, /* 000111111000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 74 0x4a 'J' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x1f, 0x80, /* 000111111000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x04, 0x00, /* 000001000000 */ + 0x38, 0x00, /* 001110000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 75 0x4b 'K' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0xf0, 0xe0, /* 111100001110 */ + 0x61, 0x80, /* 011000011000 */ + 0x63, 0x00, /* 011000110000 */ + 0x66, 0x00, /* 011001100000 */ + 0x6c, 0x00, /* 011011000000 */ + 0x78, 0x00, /* 011110000000 */ + 0x78, 0x00, /* 011110000000 */ + 0x7c, 0x00, /* 011111000000 */ + 0x6e, 0x00, /* 011011100000 */ + 0x67, 0x00, /* 011001110000 */ + 0x63, 0x80, /* 011000111000 */ + 0x61, 0xc0, /* 011000011100 */ + 0x60, 0xe0, /* 011000001110 */ + 0xf0, 0x70, /* 111100000111 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 76 0x4c 'L' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x78, 0x00, /* 011110000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x20, /* 001100000010 */ + 0x30, 0x20, /* 001100000010 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 77 0x4d 'M' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0xe0, 0x70, /* 111000000111 */ + 0x60, 0xe0, /* 011000001110 */ + 0x70, 0xe0, /* 011100001110 */ + 0x70, 0xe0, /* 011100001110 */ + 0x70, 0xe0, /* 011100001110 */ + 0x59, 0x60, /* 010110010110 */ + 0x59, 0x60, /* 010110010110 */ + 0x59, 0x60, /* 010110010110 */ + 0x4d, 0x60, /* 010011010110 */ + 0x4e, 0x60, /* 010011100110 */ + 0x4e, 0x60, /* 010011100110 */ + 0x44, 0x60, /* 010001000110 */ + 0x44, 0x60, /* 010001000110 */ + 0xe4, 0xf0, /* 111001001111 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 78 0x4e 'N' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0xc0, 0x70, /* 110000000111 */ + 0x60, 0x20, /* 011000000010 */ + 0x70, 0x20, /* 011100000010 */ + 0x78, 0x20, /* 011110000010 */ + 0x58, 0x20, /* 010110000010 */ + 0x4c, 0x20, /* 010011000010 */ + 0x46, 0x20, /* 010001100010 */ + 0x47, 0x20, /* 010001110010 */ + 0x43, 0x20, /* 010000110010 */ + 0x41, 0xa0, /* 010000011010 */ + 0x40, 0xe0, /* 010000001110 */ + 0x40, 0xe0, /* 010000001110 */ + 0x40, 0x60, /* 010000000110 */ + 0xe0, 0x30, /* 111000000011 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 79 0x4f 'O' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x11, 0xc0, /* 000100011100 */ + 0x20, 0xc0, /* 001000001100 */ + 0x20, 0x60, /* 001000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x20, 0x40, /* 001000000100 */ + 0x30, 0x40, /* 001100000100 */ + 0x18, 0x80, /* 000110001000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 80 0x50 'P' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x7f, 0x80, /* 011111111000 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0x60, /* 001100000110 */ + 0x30, 0x60, /* 001100000110 */ + 0x30, 0x60, /* 001100000110 */ + 0x30, 0xc0, /* 001100001100 */ + 0x37, 0x80, /* 001101111000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x78, 0x00, /* 011110000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 81 0x51 'Q' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x11, 0xc0, /* 000100011100 */ + 0x20, 0xc0, /* 001000001100 */ + 0x20, 0x60, /* 001000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x30, 0x40, /* 001100000100 */ + 0x38, 0x40, /* 001110000100 */ + 0x1f, 0x80, /* 000111111000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x1f, 0x00, /* 000111110000 */ + 0x23, 0x90, /* 001000111001 */ + 0x01, 0xe0, /* 000000011110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 82 0x52 'R' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0xff, 0x00, /* 111111110000 */ + 0x61, 0x80, /* 011000011000 */ + 0x60, 0xc0, /* 011000001100 */ + 0x60, 0xc0, /* 011000001100 */ + 0x60, 0xc0, /* 011000001100 */ + 0x60, 0x80, /* 011000001000 */ + 0x7f, 0x00, /* 011111110000 */ + 0x7c, 0x00, /* 011111000000 */ + 0x6e, 0x00, /* 011011100000 */ + 0x67, 0x00, /* 011001110000 */ + 0x63, 0x80, /* 011000111000 */ + 0x61, 0xc0, /* 011000011100 */ + 0x60, 0xe0, /* 011000001110 */ + 0xf0, 0x70, /* 111100000111 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 83 0x53 'S' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x1f, 0xe0, /* 000111111110 */ + 0x30, 0x60, /* 001100000110 */ + 0x60, 0x20, /* 011000000010 */ + 0x60, 0x20, /* 011000000010 */ + 0x70, 0x00, /* 011100000000 */ + 0x3c, 0x00, /* 001111000000 */ + 0x1e, 0x00, /* 000111100000 */ + 0x07, 0x80, /* 000001111000 */ + 0x01, 0xc0, /* 000000011100 */ + 0x00, 0xe0, /* 000000001110 */ + 0x40, 0x60, /* 010000000110 */ + 0x40, 0x60, /* 010000000110 */ + 0x60, 0xc0, /* 011000001100 */ + 0x7f, 0x80, /* 011111111000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 84 0x54 'T' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x46, 0x20, /* 010001100010 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x1f, 0x80, /* 000111111000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 85 0x55 'U' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0xf0, 0x70, /* 111100000111 */ + 0x60, 0x20, /* 011000000010 */ + 0x60, 0x20, /* 011000000010 */ + 0x60, 0x20, /* 011000000010 */ + 0x60, 0x20, /* 011000000010 */ + 0x60, 0x20, /* 011000000010 */ + 0x60, 0x20, /* 011000000010 */ + 0x60, 0x20, /* 011000000010 */ + 0x60, 0x20, /* 011000000010 */ + 0x60, 0x20, /* 011000000010 */ + 0x60, 0x20, /* 011000000010 */ + 0x70, 0x40, /* 011100000100 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x1f, 0x80, /* 000111111000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 86 0x56 'V' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0xe0, 0xe0, /* 111000001110 */ + 0x60, 0x40, /* 011000000100 */ + 0x30, 0x80, /* 001100001000 */ + 0x30, 0x80, /* 001100001000 */ + 0x30, 0x80, /* 001100001000 */ + 0x19, 0x00, /* 000110010000 */ + 0x19, 0x00, /* 000110010000 */ + 0x19, 0x00, /* 000110010000 */ + 0x0a, 0x00, /* 000010100000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x04, 0x00, /* 000001000000 */ + 0x04, 0x00, /* 000001000000 */ + 0x04, 0x00, /* 000001000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 87 0x57 'W' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0xfe, 0xf0, /* 111111101111 */ + 0x66, 0x20, /* 011001100010 */ + 0x66, 0x20, /* 011001100010 */ + 0x66, 0x20, /* 011001100010 */ + 0x76, 0x20, /* 011101100010 */ + 0x77, 0x40, /* 011101110100 */ + 0x33, 0x40, /* 001100110100 */ + 0x37, 0x40, /* 001101110100 */ + 0x3b, 0xc0, /* 001110111100 */ + 0x3b, 0x80, /* 001110111000 */ + 0x19, 0x80, /* 000110011000 */ + 0x19, 0x80, /* 000110011000 */ + 0x19, 0x80, /* 000110011000 */ + 0x19, 0x80, /* 000110011000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 88 0x58 'X' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0xf0, 0x70, /* 111100000111 */ + 0x60, 0x20, /* 011000000010 */ + 0x30, 0x40, /* 001100000100 */ + 0x38, 0x80, /* 001110001000 */ + 0x18, 0x80, /* 000110001000 */ + 0x0d, 0x00, /* 000011010000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x0b, 0x00, /* 000010110000 */ + 0x11, 0x80, /* 000100011000 */ + 0x11, 0xc0, /* 000100011100 */ + 0x20, 0xc0, /* 001000001100 */ + 0x40, 0x60, /* 010000000110 */ + 0xe0, 0xf0, /* 111000001111 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 89 0x59 'Y' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0xf0, 0x70, /* 111100000111 */ + 0x60, 0x20, /* 011000000010 */ + 0x30, 0x40, /* 001100000100 */ + 0x18, 0x80, /* 000110001000 */ + 0x18, 0x80, /* 000110001000 */ + 0x0d, 0x00, /* 000011010000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 90 0x5a 'Z' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x3f, 0xe0, /* 001111111110 */ + 0x20, 0xc0, /* 001000001100 */ + 0x00, 0xc0, /* 000000001100 */ + 0x01, 0x80, /* 000000011000 */ + 0x01, 0x80, /* 000000011000 */ + 0x03, 0x00, /* 000000110000 */ + 0x03, 0x00, /* 000000110000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x18, 0x00, /* 000110000000 */ + 0x18, 0x20, /* 000110000010 */ + 0x3f, 0xe0, /* 001111111110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 91 0x5b '[' */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0x80, /* 000011111000 */ + 0x0f, 0x80, /* 000011111000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0f, 0x80, /* 000011111000 */ + 0x0f, 0x80, /* 000011111000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 92 0x5c '\' */ + 0x00, 0x00, /* 000000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x18, 0x00, /* 000110000000 */ + 0x18, 0x00, /* 000110000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x03, 0x00, /* 000000110000 */ + 0x03, 0x00, /* 000000110000 */ + 0x01, 0x80, /* 000000011000 */ + 0x01, 0x80, /* 000000011000 */ + 0x00, 0xc0, /* 000000001100 */ + 0x00, 0xc0, /* 000000001100 */ + 0x00, 0x60, /* 000000000110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 93 0x5d ']' */ + 0x00, 0x00, /* 000000000000 */ + 0x1f, 0x00, /* 000111110000 */ + 0x1f, 0x00, /* 000111110000 */ + 0x03, 0x00, /* 000000110000 */ + 0x03, 0x00, /* 000000110000 */ + 0x03, 0x00, /* 000000110000 */ + 0x03, 0x00, /* 000000110000 */ + 0x03, 0x00, /* 000000110000 */ + 0x03, 0x00, /* 000000110000 */ + 0x03, 0x00, /* 000000110000 */ + 0x03, 0x00, /* 000000110000 */ + 0x03, 0x00, /* 000000110000 */ + 0x03, 0x00, /* 000000110000 */ + 0x03, 0x00, /* 000000110000 */ + 0x1f, 0x00, /* 000111110000 */ + 0x1f, 0x00, /* 000111110000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 94 0x5e '^' */ + 0x00, 0x00, /* 000000000000 */ + 0x04, 0x00, /* 000001000000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x1b, 0x00, /* 000110110000 */ + 0x31, 0x80, /* 001100011000 */ + 0x60, 0xc0, /* 011000001100 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 95 0x5f '_' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 96 0x60 '`' */ + 0x00, 0x00, /* 000000000000 */ + 0x01, 0x00, /* 000000010000 */ + 0x03, 0x00, /* 000000110000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x07, 0x80, /* 000001111000 */ + 0x07, 0x80, /* 000001111000 */ + 0x03, 0x00, /* 000000110000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 97 0x61 'a' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0x80, /* 000011111000 */ + 0x18, 0xc0, /* 000110001100 */ + 0x10, 0xc0, /* 000100001100 */ + 0x03, 0xc0, /* 000000111100 */ + 0x1c, 0xc0, /* 000111001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x39, 0xc0, /* 001110011100 */ + 0x1e, 0xe0, /* 000111101110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 98 0x62 'b' */ + 0x00, 0x00, /* 000000000000 */ + 0x20, 0x00, /* 001000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0xe0, 0x00, /* 111000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x67, 0x80, /* 011001111000 */ + 0x6f, 0xc0, /* 011011111100 */ + 0x70, 0xe0, /* 011100001110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x70, 0x60, /* 011100000110 */ + 0x78, 0xc0, /* 011110001100 */ + 0x4f, 0x80, /* 010011111000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 99 0x63 'c' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x1f, 0x80, /* 000111111000 */ + 0x31, 0xc0, /* 001100011100 */ + 0x20, 0xc0, /* 001000001100 */ + 0x60, 0x00, /* 011000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x70, 0x40, /* 011100000100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x1f, 0x80, /* 000111111000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 100 0x64 'd' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x60, /* 000000000110 */ + 0x00, 0xe0, /* 000000001110 */ + 0x00, 0x60, /* 000000000110 */ + 0x00, 0x60, /* 000000000110 */ + 0x00, 0x60, /* 000000000110 */ + 0x0f, 0x60, /* 000011110110 */ + 0x31, 0xe0, /* 001100011110 */ + 0x20, 0xe0, /* 001000001110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x70, 0xe0, /* 011100001110 */ + 0x39, 0x60, /* 001110010110 */ + 0x1e, 0x70, /* 000111100111 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 101 0x65 'e' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x30, 0xc0, /* 001100001100 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x60, 0x00, /* 011000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x18, 0x60, /* 000110000110 */ + 0x0f, 0x80, /* 000011111000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 102 0x66 'f' */ + 0x00, 0x00, /* 000000000000 */ + 0x03, 0x80, /* 000000111000 */ + 0x04, 0xc0, /* 000001001100 */ + 0x04, 0xc0, /* 000001001100 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x3f, 0x80, /* 001111111000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x1e, 0x00, /* 000111100000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 103 0x67 'g' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x1f, 0x20, /* 000111110010 */ + 0x31, 0xe0, /* 001100011110 */ + 0x60, 0xc0, /* 011000001100 */ + 0x60, 0xc0, /* 011000001100 */ + 0x60, 0xc0, /* 011000001100 */ + 0x31, 0x80, /* 001100011000 */ + 0x3f, 0x00, /* 001111110000 */ + 0x60, 0x00, /* 011000000000 */ + 0x7f, 0xc0, /* 011111111100 */ + 0x3f, 0xe0, /* 001111111110 */ + 0x20, 0x60, /* 001000000110 */ + 0x40, 0x20, /* 010000000010 */ + 0x40, 0x20, /* 010000000010 */ + 0x7f, 0xc0, /* 011111111100 */ + 0x3f, 0x80, /* 001111111000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 104 0x68 'h' */ + 0x00, 0x00, /* 000000000000 */ + 0x10, 0x00, /* 000100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x70, 0x00, /* 011100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x37, 0x80, /* 001101111000 */ + 0x39, 0xc0, /* 001110011100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x79, 0xe0, /* 011110011110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 105 0x69 'i' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x1e, 0x00, /* 000111100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x1f, 0x80, /* 000111111000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 106 0x6a 'j' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0xc0, /* 000000001100 */ + 0x00, 0xc0, /* 000000001100 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x03, 0xc0, /* 000000111100 */ + 0x00, 0xc0, /* 000000001100 */ + 0x00, 0xc0, /* 000000001100 */ + 0x00, 0xc0, /* 000000001100 */ + 0x00, 0xc0, /* 000000001100 */ + 0x00, 0xc0, /* 000000001100 */ + 0x00, 0xc0, /* 000000001100 */ + 0x00, 0xc0, /* 000000001100 */ + 0x00, 0xc0, /* 000000001100 */ + 0x00, 0xc0, /* 000000001100 */ + 0x20, 0xc0, /* 001000001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x38, 0x80, /* 001110001000 */ + 0x1f, 0x00, /* 000111110000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 107 0x6b 'k' */ + 0x00, 0x00, /* 000000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0xe0, 0x00, /* 111000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x61, 0xc0, /* 011000011100 */ + 0x63, 0x00, /* 011000110000 */ + 0x66, 0x00, /* 011001100000 */ + 0x7c, 0x00, /* 011111000000 */ + 0x78, 0x00, /* 011110000000 */ + 0x7c, 0x00, /* 011111000000 */ + 0x6e, 0x00, /* 011011100000 */ + 0x67, 0x00, /* 011001110000 */ + 0x63, 0x80, /* 011000111000 */ + 0xf1, 0xe0, /* 111100011110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 108 0x6c 'l' */ + 0x00, 0x00, /* 000000000000 */ + 0x1e, 0x00, /* 000111100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x1f, 0x80, /* 000111111000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 109 0x6d 'm' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0xdd, 0xc0, /* 110111011100 */ + 0x6e, 0xe0, /* 011011101110 */ + 0x66, 0x60, /* 011001100110 */ + 0x66, 0x60, /* 011001100110 */ + 0x66, 0x60, /* 011001100110 */ + 0x66, 0x60, /* 011001100110 */ + 0x66, 0x60, /* 011001100110 */ + 0x66, 0x60, /* 011001100110 */ + 0x66, 0x60, /* 011001100110 */ + 0xef, 0x70, /* 111011110111 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 110 0x6e 'n' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x27, 0x80, /* 001001111000 */ + 0x79, 0xc0, /* 011110011100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x79, 0xe0, /* 011110011110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 111 0x6f 'o' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0x80, /* 000011111000 */ + 0x11, 0xc0, /* 000100011100 */ + 0x20, 0xe0, /* 001000001110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x70, 0x40, /* 011100000100 */ + 0x38, 0x80, /* 001110001000 */ + 0x1f, 0x00, /* 000111110000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 112 0x70 'p' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0xef, 0x80, /* 111011111000 */ + 0x71, 0xc0, /* 011100011100 */ + 0x60, 0xe0, /* 011000001110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x40, /* 011000000100 */ + 0x70, 0x80, /* 011100001000 */ + 0x7f, 0x00, /* 011111110000 */ + 0x60, 0x00, /* 011000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0xf0, 0x00, /* 111100000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 113 0x71 'q' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0x20, /* 000011110010 */ + 0x11, 0xe0, /* 000100011110 */ + 0x20, 0xe0, /* 001000001110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x70, 0x60, /* 011100000110 */ + 0x38, 0xe0, /* 001110001110 */ + 0x1f, 0xe0, /* 000111111110 */ + 0x00, 0x60, /* 000000000110 */ + 0x00, 0x60, /* 000000000110 */ + 0x00, 0x60, /* 000000000110 */ + 0x00, 0x60, /* 000000000110 */ + 0x00, 0xf0, /* 000000001111 */ + 0x00, 0x00, /* 000000000000 */ + + /* 114 0x72 'r' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x73, 0x80, /* 011100111000 */ + 0x34, 0xc0, /* 001101001100 */ + 0x38, 0xc0, /* 001110001100 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x78, 0x00, /* 011110000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 115 0x73 's' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x1f, 0xc0, /* 000111111100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0x40, /* 001100000100 */ + 0x38, 0x00, /* 001110000000 */ + 0x1e, 0x00, /* 000111100000 */ + 0x07, 0x80, /* 000001111000 */ + 0x01, 0xc0, /* 000000011100 */ + 0x20, 0xc0, /* 001000001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x3f, 0x80, /* 001111111000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 116 0x74 't' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x04, 0x00, /* 000001000000 */ + 0x04, 0x00, /* 000001000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x7f, 0xc0, /* 011111111100 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x20, /* 000011000010 */ + 0x0e, 0x40, /* 000011100100 */ + 0x07, 0x80, /* 000001111000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 117 0x75 'u' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x79, 0xe0, /* 011110011110 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x39, 0xc0, /* 001110011100 */ + 0x1e, 0x60, /* 000111100110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 118 0x76 'v' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0xf0, 0x70, /* 111100000111 */ + 0x60, 0x20, /* 011000000010 */ + 0x30, 0x40, /* 001100000100 */ + 0x30, 0x40, /* 001100000100 */ + 0x18, 0x80, /* 000110001000 */ + 0x18, 0x80, /* 000110001000 */ + 0x0d, 0x00, /* 000011010000 */ + 0x0d, 0x00, /* 000011010000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 119 0x77 'w' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0xff, 0x70, /* 111111110111 */ + 0x66, 0x20, /* 011001100010 */ + 0x66, 0x20, /* 011001100010 */ + 0x66, 0x20, /* 011001100010 */ + 0x37, 0x40, /* 001101110100 */ + 0x3b, 0x40, /* 001110110100 */ + 0x3b, 0x40, /* 001110110100 */ + 0x19, 0x80, /* 000110011000 */ + 0x19, 0x80, /* 000110011000 */ + 0x19, 0x80, /* 000110011000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 120 0x78 'x' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0xf8, 0xf0, /* 111110001111 */ + 0x70, 0x40, /* 011100000100 */ + 0x38, 0x80, /* 001110001000 */ + 0x1d, 0x00, /* 000111010000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x07, 0x00, /* 000001110000 */ + 0x0b, 0x80, /* 000010111000 */ + 0x11, 0xc0, /* 000100011100 */ + 0x20, 0xe0, /* 001000001110 */ + 0xf1, 0xf0, /* 111100011111 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 121 0x79 'y' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0xf0, 0xf0, /* 111100001111 */ + 0x60, 0x20, /* 011000000010 */ + 0x30, 0x40, /* 001100000100 */ + 0x30, 0x40, /* 001100000100 */ + 0x18, 0x80, /* 000110001000 */ + 0x18, 0x80, /* 000110001000 */ + 0x0d, 0x00, /* 000011010000 */ + 0x0d, 0x00, /* 000011010000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x04, 0x00, /* 000001000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x08, 0x00, /* 000010000000 */ + 0x78, 0x00, /* 011110000000 */ + 0x70, 0x00, /* 011100000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 122 0x7a 'z' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x60, 0xe0, /* 011000001110 */ + 0x41, 0xc0, /* 010000011100 */ + 0x03, 0x80, /* 000000111000 */ + 0x07, 0x00, /* 000001110000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x1c, 0x00, /* 000111000000 */ + 0x38, 0x20, /* 001110000010 */ + 0x70, 0x60, /* 011100000110 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 123 0x7b '{' */ + 0x00, 0x00, /* 000000000000 */ + 0x03, 0x80, /* 000000111000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x38, 0x00, /* 001110000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x03, 0x80, /* 000000111000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 124 0x7c '|' */ + 0x00, 0x00, /* 000000000000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 125 0x7d '}' */ + 0x00, 0x00, /* 000000000000 */ + 0x1c, 0x00, /* 000111000000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x03, 0x00, /* 000000110000 */ + 0x01, 0xc0, /* 000000011100 */ + 0x03, 0x00, /* 000000110000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x1c, 0x00, /* 000111000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 126 0x7e '~' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x1c, 0x20, /* 000111000010 */ + 0x3e, 0x60, /* 001111100110 */ + 0x67, 0xc0, /* 011001111100 */ + 0x43, 0x80, /* 010000111000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 127 0x7f '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0x00, 0x00, /* 000000000000 */ + + /* 128 0x80 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0xc0, /* 000011111100 */ + 0x10, 0x60, /* 000100000110 */ + 0x20, 0x20, /* 001000000010 */ + 0x20, 0x00, /* 001000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x20, 0x00, /* 001000000000 */ + 0x30, 0x20, /* 001100000010 */ + 0x18, 0x40, /* 000110000100 */ + 0x0f, 0x80, /* 000011111000 */ + 0x06, 0x00, /* 000001100000 */ + 0x03, 0x00, /* 000000110000 */ + 0x01, 0x80, /* 000000011000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 129 0x81 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x19, 0x80, /* 000110011000 */ + 0x19, 0x80, /* 000110011000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x79, 0xe0, /* 011110011110 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x39, 0xc0, /* 001110011100 */ + 0x1e, 0x60, /* 000111100110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 130 0x82 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x01, 0x80, /* 000000011000 */ + 0x03, 0x00, /* 000000110000 */ + 0x06, 0x00, /* 000001100000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x30, 0xc0, /* 001100001100 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x60, 0x00, /* 011000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x18, 0x60, /* 000110000110 */ + 0x0f, 0x80, /* 000011111000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 131 0x83 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x02, 0x00, /* 000000100000 */ + 0x07, 0x00, /* 000001110000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x18, 0xc0, /* 000110001100 */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0x80, /* 000011111000 */ + 0x18, 0xc0, /* 000110001100 */ + 0x10, 0xc0, /* 000100001100 */ + 0x03, 0xc0, /* 000000111100 */ + 0x1c, 0xc0, /* 000111001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x39, 0xc0, /* 001110011100 */ + 0x1e, 0xe0, /* 000111101110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 132 0x84 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x19, 0x80, /* 000110011000 */ + 0x19, 0x80, /* 000110011000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0x80, /* 000011111000 */ + 0x18, 0xc0, /* 000110001100 */ + 0x10, 0xc0, /* 000100001100 */ + 0x03, 0xc0, /* 000000111100 */ + 0x1c, 0xc0, /* 000111001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x39, 0xc0, /* 001110011100 */ + 0x1e, 0xe0, /* 000111101110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 133 0x85 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x06, 0x00, /* 000001100000 */ + 0x03, 0x00, /* 000000110000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0x80, /* 000011111000 */ + 0x18, 0xc0, /* 000110001100 */ + 0x10, 0xc0, /* 000100001100 */ + 0x03, 0xc0, /* 000000111100 */ + 0x1c, 0xc0, /* 000111001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x39, 0xc0, /* 001110011100 */ + 0x1e, 0xe0, /* 000111101110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 134 0x86 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x07, 0x00, /* 000001110000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x07, 0x00, /* 000001110000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0x80, /* 000011111000 */ + 0x18, 0xc0, /* 000110001100 */ + 0x10, 0xc0, /* 000100001100 */ + 0x03, 0xc0, /* 000000111100 */ + 0x1c, 0xc0, /* 000111001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x39, 0xc0, /* 001110011100 */ + 0x1e, 0xe0, /* 000111101110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 135 0x87 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x1f, 0x80, /* 000111111000 */ + 0x31, 0xc0, /* 001100011100 */ + 0x20, 0xc0, /* 001000001100 */ + 0x60, 0x00, /* 011000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x70, 0x40, /* 011100000100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x1f, 0x80, /* 000111111000 */ + 0x06, 0x00, /* 000001100000 */ + 0x03, 0x00, /* 000000110000 */ + 0x01, 0x80, /* 000000011000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 136 0x88 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x02, 0x00, /* 000000100000 */ + 0x07, 0x00, /* 000001110000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x18, 0xc0, /* 000110001100 */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x30, 0xc0, /* 001100001100 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x60, 0x00, /* 011000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x18, 0x60, /* 000110000110 */ + 0x0f, 0x80, /* 000011111000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 137 0x89 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x19, 0x80, /* 000110011000 */ + 0x19, 0x80, /* 000110011000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x30, 0xc0, /* 001100001100 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x60, 0x00, /* 011000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x18, 0x60, /* 000110000110 */ + 0x0f, 0x80, /* 000011111000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 138 0x8a '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x06, 0x00, /* 000001100000 */ + 0x03, 0x00, /* 000000110000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x30, 0xc0, /* 001100001100 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x60, 0x00, /* 011000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x18, 0x60, /* 000110000110 */ + 0x0f, 0x80, /* 000011111000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 139 0x8b '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x19, 0x80, /* 000110011000 */ + 0x19, 0x80, /* 000110011000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x1e, 0x00, /* 000111100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x1f, 0x80, /* 000111111000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 140 0x8c '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x04, 0x00, /* 000001000000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x1b, 0x00, /* 000110110000 */ + 0x31, 0x80, /* 001100011000 */ + 0x00, 0x00, /* 000000000000 */ + 0x1e, 0x00, /* 000111100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x1f, 0x80, /* 000111111000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 141 0x8d '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x18, 0x00, /* 000110000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x06, 0x00, /* 000001100000 */ + 0x00, 0x00, /* 000000000000 */ + 0x1e, 0x00, /* 000111100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x1f, 0x80, /* 000111111000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 142 0x8e '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x19, 0x80, /* 000110011000 */ + 0x19, 0x80, /* 000110011000 */ + 0x00, 0x00, /* 000000000000 */ + 0x04, 0x00, /* 000001000000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x0b, 0x00, /* 000010110000 */ + 0x0b, 0x00, /* 000010110000 */ + 0x19, 0x80, /* 000110011000 */ + 0x11, 0x80, /* 000100011000 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x20, 0xc0, /* 001000001100 */ + 0x60, 0x60, /* 011000000110 */ + 0x40, 0x60, /* 010000000110 */ + 0xe0, 0xf0, /* 111000001111 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 143 0x8f '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x19, 0x80, /* 000110011000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x04, 0x00, /* 000001000000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x0b, 0x00, /* 000010110000 */ + 0x0b, 0x00, /* 000010110000 */ + 0x19, 0x80, /* 000110011000 */ + 0x11, 0x80, /* 000100011000 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x20, 0xc0, /* 001000001100 */ + 0x60, 0x60, /* 011000000110 */ + 0x40, 0x60, /* 010000000110 */ + 0xe0, 0xf0, /* 111000001111 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 144 0x90 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x03, 0x00, /* 000000110000 */ + 0x06, 0x00, /* 000001100000 */ + 0x08, 0x00, /* 000010000000 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x30, 0x20, /* 001100000010 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x80, /* 001100001000 */ + 0x3f, 0x80, /* 001111111000 */ + 0x30, 0x80, /* 001100001000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x20, /* 001100000010 */ + 0x30, 0x20, /* 001100000010 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 145 0x91 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x3d, 0xe0, /* 001111011110 */ + 0x66, 0x30, /* 011001100011 */ + 0x46, 0x30, /* 010001100011 */ + 0x06, 0x30, /* 000001100011 */ + 0x3f, 0xf0, /* 001111111111 */ + 0x66, 0x00, /* 011001100000 */ + 0xc6, 0x00, /* 110001100000 */ + 0xc6, 0x00, /* 110001100000 */ + 0xe7, 0x30, /* 111001110011 */ + 0x7d, 0xe0, /* 011111011110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 146 0x92 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x03, 0xf0, /* 000000111111 */ + 0x07, 0x10, /* 000001110001 */ + 0x07, 0x10, /* 000001110001 */ + 0x0b, 0x00, /* 000010110000 */ + 0x0b, 0x00, /* 000010110000 */ + 0x0b, 0x20, /* 000010110010 */ + 0x13, 0xe0, /* 000100111110 */ + 0x13, 0x20, /* 000100110010 */ + 0x3f, 0x00, /* 001111110000 */ + 0x23, 0x00, /* 001000110000 */ + 0x23, 0x00, /* 001000110000 */ + 0x43, 0x10, /* 010000110001 */ + 0x43, 0x10, /* 010000110001 */ + 0xe7, 0xf0, /* 111001111111 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 147 0x93 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x02, 0x00, /* 000000100000 */ + 0x07, 0x00, /* 000001110000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x18, 0xc0, /* 000110001100 */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0x80, /* 000011111000 */ + 0x11, 0xc0, /* 000100011100 */ + 0x20, 0xe0, /* 001000001110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x70, 0x40, /* 011100000100 */ + 0x38, 0x80, /* 001110001000 */ + 0x1f, 0x00, /* 000111110000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 148 0x94 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x19, 0x80, /* 000110011000 */ + 0x19, 0x80, /* 000110011000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0x80, /* 000011111000 */ + 0x11, 0xc0, /* 000100011100 */ + 0x20, 0xe0, /* 001000001110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x70, 0x40, /* 011100000100 */ + 0x38, 0x80, /* 001110001000 */ + 0x1f, 0x00, /* 000111110000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 149 0x95 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x06, 0x00, /* 000001100000 */ + 0x03, 0x00, /* 000000110000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0x80, /* 000011111000 */ + 0x11, 0xc0, /* 000100011100 */ + 0x20, 0xe0, /* 001000001110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x70, 0x40, /* 011100000100 */ + 0x38, 0x80, /* 001110001000 */ + 0x1f, 0x00, /* 000111110000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 150 0x96 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x02, 0x00, /* 000000100000 */ + 0x07, 0x00, /* 000001110000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x18, 0xc0, /* 000110001100 */ + 0x00, 0x00, /* 000000000000 */ + 0x79, 0xe0, /* 011110011110 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x39, 0xc0, /* 001110011100 */ + 0x1e, 0x60, /* 000111100110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 151 0x97 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x18, 0x00, /* 000110000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x06, 0x00, /* 000001100000 */ + 0x00, 0x00, /* 000000000000 */ + 0x79, 0xe0, /* 011110011110 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x39, 0xc0, /* 001110011100 */ + 0x1e, 0x60, /* 000111100110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 152 0x98 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x19, 0x80, /* 000110011000 */ + 0x19, 0x80, /* 000110011000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0xf0, 0xf0, /* 111100001111 */ + 0x60, 0x20, /* 011000000010 */ + 0x30, 0x40, /* 001100000100 */ + 0x30, 0x40, /* 001100000100 */ + 0x18, 0x80, /* 000110001000 */ + 0x18, 0x80, /* 000110001000 */ + 0x0d, 0x00, /* 000011010000 */ + 0x0d, 0x00, /* 000011010000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x04, 0x00, /* 000001000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x08, 0x00, /* 000010000000 */ + 0x78, 0x00, /* 011110000000 */ + 0x70, 0x00, /* 011100000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 153 0x99 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x19, 0x80, /* 000110011000 */ + 0x19, 0x80, /* 000110011000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0x80, /* 000011111000 */ + 0x11, 0xc0, /* 000100011100 */ + 0x20, 0xc0, /* 001000001100 */ + 0x20, 0x60, /* 001000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x20, 0x40, /* 001000000100 */ + 0x30, 0x40, /* 001100000100 */ + 0x18, 0x80, /* 000110001000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 154 0x9a '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x19, 0x80, /* 000110011000 */ + 0x19, 0x80, /* 000110011000 */ + 0xe0, 0x30, /* 111000000011 */ + 0x60, 0x20, /* 011000000010 */ + 0x60, 0x20, /* 011000000010 */ + 0x60, 0x20, /* 011000000010 */ + 0x60, 0x20, /* 011000000010 */ + 0x60, 0x20, /* 011000000010 */ + 0x60, 0x20, /* 011000000010 */ + 0x60, 0x20, /* 011000000010 */ + 0x60, 0x20, /* 011000000010 */ + 0x60, 0x20, /* 011000000010 */ + 0x70, 0x40, /* 011100000100 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x1f, 0x80, /* 000111111000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 155 0x9b '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x1f, 0x80, /* 000111111000 */ + 0x36, 0xc0, /* 001101101100 */ + 0x26, 0xc0, /* 001001101100 */ + 0x66, 0x00, /* 011001100000 */ + 0x66, 0x00, /* 011001100000 */ + 0x66, 0x00, /* 011001100000 */ + 0x66, 0x00, /* 011001100000 */ + 0x76, 0x40, /* 011101100100 */ + 0x36, 0xc0, /* 001101101100 */ + 0x1f, 0x80, /* 000111111000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 156 0x9c '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0x80, /* 000011111000 */ + 0x1c, 0xc0, /* 000111001100 */ + 0x18, 0xc0, /* 000110001100 */ + 0x18, 0x00, /* 000110000000 */ + 0x18, 0x00, /* 000110000000 */ + 0x18, 0x00, /* 000110000000 */ + 0x7e, 0x00, /* 011111100000 */ + 0x7e, 0x00, /* 011111100000 */ + 0x18, 0x00, /* 000110000000 */ + 0x18, 0x00, /* 000110000000 */ + 0x18, 0x00, /* 000110000000 */ + 0x18, 0x00, /* 000110000000 */ + 0x3e, 0x20, /* 001111100010 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x61, 0xc0, /* 011000011100 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 157 0x9d '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x30, 0xc0, /* 001100001100 */ + 0x19, 0x80, /* 000110011000 */ + 0x19, 0x80, /* 000110011000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x06, 0x00, /* 000001100000 */ + 0x1f, 0x80, /* 000111111000 */ + 0x06, 0x00, /* 000001100000 */ + 0x1f, 0x80, /* 000111111000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 158 0x9e '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x7f, 0x80, /* 011111111000 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0x60, /* 001100000110 */ + 0x30, 0x60, /* 001100000110 */ + 0x30, 0x60, /* 001100000110 */ + 0x30, 0xc0, /* 001100001100 */ + 0x37, 0x80, /* 001101111000 */ + 0x30, 0x00, /* 001100000000 */ + 0x33, 0x00, /* 001100110000 */ + 0x37, 0x80, /* 001101111000 */ + 0x33, 0x00, /* 001100110000 */ + 0x33, 0x00, /* 001100110000 */ + 0x33, 0x30, /* 001100110011 */ + 0x31, 0xe0, /* 001100011110 */ + 0x78, 0xc0, /* 011110001100 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 159 0x9f '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0xc0, /* 000000001100 */ + 0x01, 0xe0, /* 000000011110 */ + 0x03, 0x30, /* 000000110011 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x3f, 0xe0, /* 001111111110 */ + 0x7f, 0xc0, /* 011111111100 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0xcc, 0x00, /* 110011000000 */ + 0x78, 0x00, /* 011110000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 160 0xa0 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x01, 0x80, /* 000000011000 */ + 0x03, 0x00, /* 000000110000 */ + 0x06, 0x00, /* 000001100000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0x80, /* 000011111000 */ + 0x18, 0xc0, /* 000110001100 */ + 0x10, 0xc0, /* 000100001100 */ + 0x03, 0xc0, /* 000000111100 */ + 0x1c, 0xc0, /* 000111001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x39, 0xc0, /* 001110011100 */ + 0x1e, 0xe0, /* 000111101110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 161 0xa1 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x01, 0x80, /* 000000011000 */ + 0x03, 0x00, /* 000000110000 */ + 0x06, 0x00, /* 000001100000 */ + 0x00, 0x00, /* 000000000000 */ + 0x1e, 0x00, /* 000111100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x1f, 0x80, /* 000111111000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 162 0xa2 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x01, 0x80, /* 000000011000 */ + 0x03, 0x00, /* 000000110000 */ + 0x06, 0x00, /* 000001100000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0x80, /* 000011111000 */ + 0x11, 0xc0, /* 000100011100 */ + 0x20, 0xe0, /* 001000001110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x70, 0x40, /* 011100000100 */ + 0x38, 0x80, /* 001110001000 */ + 0x1f, 0x00, /* 000111110000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 163 0xa3 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x01, 0x80, /* 000000011000 */ + 0x03, 0x00, /* 000000110000 */ + 0x06, 0x00, /* 000001100000 */ + 0x00, 0x00, /* 000000000000 */ + 0x79, 0xe0, /* 011110011110 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x39, 0xc0, /* 001110011100 */ + 0x1e, 0x60, /* 000111100110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 164 0xa4 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x1c, 0x40, /* 000111000100 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x23, 0x80, /* 001000111000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x27, 0x80, /* 001001111000 */ + 0x79, 0xc0, /* 011110011100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x79, 0xe0, /* 011110011110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 165 0xa5 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x1c, 0x40, /* 000111000100 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x23, 0x80, /* 001000111000 */ + 0xc0, 0x70, /* 110000000111 */ + 0x60, 0x20, /* 011000000010 */ + 0x70, 0x20, /* 011100000010 */ + 0x78, 0x20, /* 011110000010 */ + 0x5c, 0x20, /* 010111000010 */ + 0x4e, 0x20, /* 010011100010 */ + 0x47, 0x20, /* 010001110010 */ + 0x43, 0xa0, /* 010000111010 */ + 0x41, 0xe0, /* 010000011110 */ + 0x40, 0xe0, /* 010000001110 */ + 0x40, 0x60, /* 010000000110 */ + 0xe0, 0x30, /* 111000000011 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 166 0xa6 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x1f, 0x00, /* 000111110000 */ + 0x31, 0x80, /* 001100011000 */ + 0x01, 0x80, /* 000000011000 */ + 0x07, 0x80, /* 000001111000 */ + 0x19, 0x80, /* 000110011000 */ + 0x31, 0x80, /* 001100011000 */ + 0x31, 0x80, /* 001100011000 */ + 0x33, 0x80, /* 001100111000 */ + 0x1d, 0xc0, /* 000111011100 */ + 0x00, 0x00, /* 000000000000 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 167 0xa7 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x07, 0x00, /* 000001110000 */ + 0x19, 0x80, /* 000110011000 */ + 0x10, 0xc0, /* 000100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0x80, /* 001100001000 */ + 0x19, 0x80, /* 000110011000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x00, 0x00, /* 000000000000 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 168 0xa8 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x03, 0x00, /* 000000110000 */ + 0x03, 0x00, /* 000000110000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x03, 0x00, /* 000000110000 */ + 0x03, 0x00, /* 000000110000 */ + 0x06, 0x00, /* 000001100000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x18, 0x00, /* 000110000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x40, /* 001100000100 */ + 0x39, 0xc0, /* 001110011100 */ + 0x1f, 0x80, /* 000111111000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 169 0xa9 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 170 0xaa '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x00, 0xc0, /* 000000001100 */ + 0x00, 0xc0, /* 000000001100 */ + 0x00, 0xc0, /* 000000001100 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 171 0xab '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x10, 0x00, /* 000100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x10, 0x00, /* 000100000000 */ + 0x10, 0x40, /* 000100000100 */ + 0x10, 0x80, /* 000100001000 */ + 0x11, 0x00, /* 000100010000 */ + 0x3a, 0x00, /* 001110100000 */ + 0x05, 0xc0, /* 000001011100 */ + 0x0a, 0x20, /* 000010100010 */ + 0x10, 0x20, /* 000100000010 */ + 0x20, 0xc0, /* 001000001100 */ + 0x41, 0x00, /* 010000010000 */ + 0x02, 0x00, /* 000000100000 */ + 0x03, 0xe0, /* 000000111110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 172 0xac '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x10, 0x00, /* 000100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x10, 0x00, /* 000100000000 */ + 0x10, 0x40, /* 000100000100 */ + 0x10, 0x80, /* 000100001000 */ + 0x11, 0x00, /* 000100010000 */ + 0x3a, 0x40, /* 001110100100 */ + 0x04, 0xc0, /* 000001001100 */ + 0x09, 0x40, /* 000010010100 */ + 0x12, 0x40, /* 000100100100 */ + 0x24, 0x40, /* 001001000100 */ + 0x47, 0xe0, /* 010001111110 */ + 0x00, 0x40, /* 000000000100 */ + 0x00, 0x40, /* 000000000100 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 173 0xad '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 174 0xae '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x06, 0x60, /* 000001100110 */ + 0x0c, 0xc0, /* 000011001100 */ + 0x19, 0x80, /* 000110011000 */ + 0x33, 0x00, /* 001100110000 */ + 0x66, 0x00, /* 011001100000 */ + 0x33, 0x00, /* 001100110000 */ + 0x19, 0x80, /* 000110011000 */ + 0x0c, 0xc0, /* 000011001100 */ + 0x06, 0x60, /* 000001100110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 175 0xaf '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x66, 0x00, /* 011001100000 */ + 0x33, 0x00, /* 001100110000 */ + 0x19, 0x80, /* 000110011000 */ + 0x0c, 0xc0, /* 000011001100 */ + 0x06, 0x60, /* 000001100110 */ + 0x0c, 0xc0, /* 000011001100 */ + 0x19, 0x80, /* 000110011000 */ + 0x33, 0x00, /* 001100110000 */ + 0x66, 0x00, /* 011001100000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 176 0xb0 '.' */ + 0x0c, 0x30, /* 000011000011 */ + 0x08, 0x20, /* 000010000010 */ + 0x61, 0x80, /* 011000011000 */ + 0x20, 0x80, /* 001000001000 */ + 0x0c, 0x30, /* 000011000011 */ + 0x08, 0x20, /* 000010000010 */ + 0x61, 0x80, /* 011000011000 */ + 0x20, 0x80, /* 001000001000 */ + 0x0c, 0x30, /* 000011000011 */ + 0x08, 0x20, /* 000010000010 */ + 0x61, 0x80, /* 011000011000 */ + 0x20, 0x80, /* 001000001000 */ + 0x0c, 0x30, /* 000011000011 */ + 0x08, 0x20, /* 000010000010 */ + 0x61, 0x80, /* 011000011000 */ + 0x20, 0x80, /* 001000001000 */ + 0x0c, 0x30, /* 000011000011 */ + 0x08, 0x20, /* 000010000010 */ + 0x61, 0x80, /* 011000011000 */ + 0x20, 0x80, /* 001000001000 */ + 0x0c, 0x30, /* 000011000011 */ + 0x08, 0x20, /* 000010000010 */ + + /* 177 0xb1 '.' */ + 0x77, 0x70, /* 011101110111 */ + 0x22, 0x20, /* 001000100010 */ + 0x88, 0x80, /* 100010001000 */ + 0xdd, 0xd0, /* 110111011101 */ + 0x88, 0x80, /* 100010001000 */ + 0x22, 0x20, /* 001000100010 */ + 0x77, 0x70, /* 011101110111 */ + 0x22, 0x20, /* 001000100010 */ + 0x88, 0x80, /* 100010001000 */ + 0xdd, 0xd0, /* 110111011101 */ + 0x88, 0x80, /* 100010001000 */ + 0x22, 0x20, /* 001000100010 */ + 0x77, 0x70, /* 011101110111 */ + 0x22, 0x20, /* 001000100010 */ + 0x88, 0x80, /* 100010001000 */ + 0xdd, 0xd0, /* 110111011101 */ + 0x88, 0x80, /* 100010001000 */ + 0x22, 0x20, /* 001000100010 */ + 0x77, 0x70, /* 011101110111 */ + 0x22, 0x20, /* 001000100010 */ + 0x88, 0x80, /* 100010001000 */ + 0xdd, 0xd0, /* 110111011101 */ + + /* 178 0xb2 '.' */ + 0xf3, 0xc0, /* 111100111100 */ + 0xf7, 0xd0, /* 111101111101 */ + 0x9e, 0x70, /* 100111100111 */ + 0xdf, 0x70, /* 110111110111 */ + 0xf3, 0xc0, /* 111100111100 */ + 0xf7, 0xd0, /* 111101111101 */ + 0x9e, 0x70, /* 100111100111 */ + 0xdf, 0x70, /* 110111110111 */ + 0xf3, 0xc0, /* 111100111100 */ + 0xf7, 0xd0, /* 111101111101 */ + 0x9e, 0x70, /* 100111100111 */ + 0xdf, 0x70, /* 110111110111 */ + 0xf3, 0xc0, /* 111100111100 */ + 0xf7, 0xd0, /* 111101111101 */ + 0x9e, 0x70, /* 100111100111 */ + 0xdf, 0x70, /* 110111110111 */ + 0xf3, 0xc0, /* 111100111100 */ + 0xf7, 0xd0, /* 111101111101 */ + 0x9e, 0x70, /* 100111100111 */ + 0xdf, 0x70, /* 110111110111 */ + 0xf3, 0xc0, /* 111100111100 */ + 0xf7, 0xd0, /* 111101111101 */ + + /* 179 0xb3 '.' */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + + /* 180 0xb4 '.' */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0xfe, 0x00, /* 111111100000 */ + 0xfe, 0x00, /* 111111100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + + /* 181 0xb5 '.' */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0xfe, 0x00, /* 111111100000 */ + 0xfe, 0x00, /* 111111100000 */ + 0x06, 0x00, /* 000001100000 */ + 0xfe, 0x00, /* 111111100000 */ + 0xfe, 0x00, /* 111111100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + + /* 182 0xb6 '.' */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0xfd, 0x80, /* 111111011000 */ + 0xfd, 0x80, /* 111111011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + + /* 183 0xb7 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0xff, 0x80, /* 111111111000 */ + 0xff, 0x80, /* 111111111000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + + /* 184 0xb8 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0xfe, 0x00, /* 111111100000 */ + 0xfe, 0x00, /* 111111100000 */ + 0x06, 0x00, /* 000001100000 */ + 0xfe, 0x00, /* 111111100000 */ + 0xfe, 0x00, /* 111111100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + + /* 185 0xb9 '.' */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0xfd, 0x80, /* 111111011000 */ + 0xfd, 0x80, /* 111111011000 */ + 0x01, 0x80, /* 000000011000 */ + 0xfd, 0x80, /* 111111011000 */ + 0xfd, 0x80, /* 111111011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + + /* 186 0xba '.' */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + + /* 187 0xbb '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0xff, 0x80, /* 111111111000 */ + 0xff, 0x80, /* 111111111000 */ + 0x01, 0x80, /* 000000011000 */ + 0xfd, 0x80, /* 111111011000 */ + 0xfd, 0x80, /* 111111011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + + /* 188 0xbc '.' */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0xfd, 0x80, /* 111111011000 */ + 0xfd, 0x80, /* 111111011000 */ + 0x01, 0x80, /* 000000011000 */ + 0xff, 0x80, /* 111111111000 */ + 0xff, 0x80, /* 111111111000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 189 0xbd '.' */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0xff, 0x80, /* 111111111000 */ + 0xff, 0x80, /* 111111111000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 190 0xbe '.' */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0xfe, 0x00, /* 111111100000 */ + 0xfe, 0x00, /* 111111100000 */ + 0x06, 0x00, /* 000001100000 */ + 0xfe, 0x00, /* 111111100000 */ + 0xfe, 0x00, /* 111111100000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 191 0xbf '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0xfe, 0x00, /* 111111100000 */ + 0xfe, 0x00, /* 111111100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + + /* 192 0xc0 '.' */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x07, 0xf0, /* 000001111111 */ + 0x07, 0xf0, /* 000001111111 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 193 0xc1 '.' */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 194 0xc2 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + + /* 195 0xc3 '.' */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x07, 0xf0, /* 000001111111 */ + 0x07, 0xf0, /* 000001111111 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + + /* 196 0xc4 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 197 0xc5 '.' */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + + /* 198 0xc6 '.' */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x07, 0xf0, /* 000001111111 */ + 0x07, 0xf0, /* 000001111111 */ + 0x06, 0x00, /* 000001100000 */ + 0x07, 0xf0, /* 000001111111 */ + 0x07, 0xf0, /* 000001111111 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + + /* 199 0xc7 '.' */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0xf0, /* 000011011111 */ + 0x0d, 0xf0, /* 000011011111 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + + /* 200 0xc8 '.' */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0xf0, /* 000011011111 */ + 0x0d, 0xf0, /* 000011011111 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0f, 0xf0, /* 000011111111 */ + 0x0f, 0xf0, /* 000011111111 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 201 0xc9 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0xf0, /* 000011111111 */ + 0x0f, 0xf0, /* 000011111111 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0d, 0xf0, /* 000011011111 */ + 0x0d, 0xf0, /* 000011011111 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + + /* 202 0xca '.' */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0xfd, 0xf0, /* 111111011111 */ + 0xfd, 0xf0, /* 111111011111 */ + 0x00, 0x00, /* 000000000000 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 203 0xcb '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0x00, 0x00, /* 000000000000 */ + 0xfd, 0xf0, /* 111111011111 */ + 0xfd, 0xf0, /* 111111011111 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + + /* 204 0xcc '.' */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0xf0, /* 000011011111 */ + 0x0d, 0xf0, /* 000011011111 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0d, 0xf0, /* 000011011111 */ + 0x0d, 0xf0, /* 000011011111 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + + /* 205 0xcd '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0x00, 0x00, /* 000000000000 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 206 0xce '.' */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0xfd, 0xf0, /* 111111011111 */ + 0xfd, 0xf0, /* 111111011111 */ + 0x00, 0x00, /* 000000000000 */ + 0xfd, 0xf0, /* 111111011111 */ + 0xfd, 0xf0, /* 111111011111 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + + /* 207 0xcf '.' */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0x00, 0x00, /* 000000000000 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 208 0xd0 '.' */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 209 0xd1 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0x00, 0x00, /* 000000000000 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + + /* 210 0xd2 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + + /* 211 0xd3 '.' */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0f, 0xf0, /* 000011111111 */ + 0x0f, 0xf0, /* 000011111111 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 212 0xd4 '.' */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x07, 0xf0, /* 000001111111 */ + 0x07, 0xf0, /* 000001111111 */ + 0x06, 0x00, /* 000001100000 */ + 0x07, 0xf0, /* 000001111111 */ + 0x07, 0xf0, /* 000001111111 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 213 0xd5 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x07, 0xf0, /* 000001111111 */ + 0x07, 0xf0, /* 000001111111 */ + 0x06, 0x00, /* 000001100000 */ + 0x07, 0xf0, /* 000001111111 */ + 0x07, 0xf0, /* 000001111111 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + + /* 214 0xd6 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0xf0, /* 000011111111 */ + 0x0f, 0xf0, /* 000011111111 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + + /* 215 0xd7 '.' */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + 0x0d, 0x80, /* 000011011000 */ + + /* 216 0xd8 '.' */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0x06, 0x00, /* 000001100000 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + + /* 217 0xd9 '.' */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0xfe, 0x00, /* 111111100000 */ + 0xfe, 0x00, /* 111111100000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 218 0xda '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x07, 0xf0, /* 000001111111 */ + 0x07, 0xf0, /* 000001111111 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + + /* 219 0xdb '.' */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + + /* 220 0xdc '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + + /* 221 0xdd '.' */ + 0xfc, 0x00, /* 111111000000 */ + 0xfc, 0x00, /* 111111000000 */ + 0xfc, 0x00, /* 111111000000 */ + 0xfc, 0x00, /* 111111000000 */ + 0xfc, 0x00, /* 111111000000 */ + 0xfc, 0x00, /* 111111000000 */ + 0xfc, 0x00, /* 111111000000 */ + 0xfc, 0x00, /* 111111000000 */ + 0xfc, 0x00, /* 111111000000 */ + 0xfc, 0x00, /* 111111000000 */ + 0xfc, 0x00, /* 111111000000 */ + 0xfc, 0x00, /* 111111000000 */ + 0xfc, 0x00, /* 111111000000 */ + 0xfc, 0x00, /* 111111000000 */ + 0xfc, 0x00, /* 111111000000 */ + 0xfc, 0x00, /* 111111000000 */ + 0xfc, 0x00, /* 111111000000 */ + 0xfc, 0x00, /* 111111000000 */ + 0xfc, 0x00, /* 111111000000 */ + 0xfc, 0x00, /* 111111000000 */ + 0xfc, 0x00, /* 111111000000 */ + 0xfc, 0x00, /* 111111000000 */ + + /* 222 0xde '.' */ + 0x03, 0xf0, /* 000000111111 */ + 0x03, 0xf0, /* 000000111111 */ + 0x03, 0xf0, /* 000000111111 */ + 0x03, 0xf0, /* 000000111111 */ + 0x03, 0xf0, /* 000000111111 */ + 0x03, 0xf0, /* 000000111111 */ + 0x03, 0xf0, /* 000000111111 */ + 0x03, 0xf0, /* 000000111111 */ + 0x03, 0xf0, /* 000000111111 */ + 0x03, 0xf0, /* 000000111111 */ + 0x03, 0xf0, /* 000000111111 */ + 0x03, 0xf0, /* 000000111111 */ + 0x03, 0xf0, /* 000000111111 */ + 0x03, 0xf0, /* 000000111111 */ + 0x03, 0xf0, /* 000000111111 */ + 0x03, 0xf0, /* 000000111111 */ + 0x03, 0xf0, /* 000000111111 */ + 0x03, 0xf0, /* 000000111111 */ + 0x03, 0xf0, /* 000000111111 */ + 0x03, 0xf0, /* 000000111111 */ + 0x03, 0xf0, /* 000000111111 */ + 0x03, 0xf0, /* 000000111111 */ + + /* 223 0xdf '.' */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0xff, 0xf0, /* 111111111111 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 224 0xe0 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0x60, /* 000011110110 */ + 0x13, 0xe0, /* 000100111110 */ + 0x21, 0xc0, /* 001000011100 */ + 0x60, 0xc0, /* 011000001100 */ + 0x60, 0xc0, /* 011000001100 */ + 0x60, 0xc0, /* 011000001100 */ + 0x60, 0xc0, /* 011000001100 */ + 0x70, 0x80, /* 011100001000 */ + 0x39, 0xc0, /* 001110011100 */ + 0x1f, 0x60, /* 000111110110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 225 0xe1 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x19, 0x80, /* 000110011000 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x31, 0x80, /* 001100011000 */ + 0x37, 0x80, /* 001101111000 */ + 0x31, 0x80, /* 001100011000 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x31, 0x80, /* 001100011000 */ + 0x77, 0x00, /* 011101110000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 226 0xe2 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x3f, 0xe0, /* 001111111110 */ + 0x3f, 0xe0, /* 001111111110 */ + 0x30, 0x60, /* 001100000110 */ + 0x30, 0x60, /* 001100000110 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 227 0xe3 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x19, 0x80, /* 000110011000 */ + 0x19, 0x80, /* 000110011000 */ + 0x19, 0x80, /* 000110011000 */ + 0x19, 0x80, /* 000110011000 */ + 0x19, 0x80, /* 000110011000 */ + 0x19, 0x80, /* 000110011000 */ + 0x19, 0x80, /* 000110011000 */ + 0x19, 0x80, /* 000110011000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 228 0xe4 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x60, 0x60, /* 011000000110 */ + 0x30, 0x60, /* 001100000110 */ + 0x30, 0x00, /* 001100000000 */ + 0x18, 0x00, /* 000110000000 */ + 0x18, 0x00, /* 000110000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x18, 0x00, /* 000110000000 */ + 0x18, 0x00, /* 000110000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x60, /* 001100000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 229 0xe5 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x07, 0xe0, /* 000001111110 */ + 0x0f, 0xe0, /* 000011111110 */ + 0x13, 0x80, /* 000100111000 */ + 0x21, 0xc0, /* 001000011100 */ + 0x60, 0xc0, /* 011000001100 */ + 0x60, 0xc0, /* 011000001100 */ + 0x60, 0xc0, /* 011000001100 */ + 0x60, 0xc0, /* 011000001100 */ + 0x70, 0x80, /* 011100001000 */ + 0x39, 0x00, /* 001110010000 */ + 0x1e, 0x00, /* 000111100000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 230 0xe6 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x39, 0xc0, /* 001110011100 */ + 0x36, 0xe0, /* 001101101110 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 231 0xe7 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x19, 0x80, /* 000110011000 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x66, 0x60, /* 011001100110 */ + 0x66, 0x60, /* 011001100110 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 232 0xe8 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x19, 0x80, /* 000110011000 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x19, 0x80, /* 000110011000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 233 0xe9 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x1f, 0x80, /* 000111111000 */ + 0x30, 0xc0, /* 001100001100 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x30, 0xc0, /* 001100001100 */ + 0x1f, 0x80, /* 000111111000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 234 0xea '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x1f, 0x00, /* 000111110000 */ + 0x31, 0x80, /* 001100011000 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x19, 0x80, /* 000110011000 */ + 0x19, 0x80, /* 000110011000 */ + 0xd9, 0xb0, /* 110110011011 */ + 0x79, 0xe0, /* 011110011110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 235 0xeb '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x07, 0x80, /* 000001111000 */ + 0x0c, 0xc0, /* 000011001100 */ + 0x18, 0x60, /* 000110000110 */ + 0x18, 0x00, /* 000110000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x06, 0x00, /* 000001100000 */ + 0x03, 0x00, /* 000000110000 */ + 0x0f, 0x80, /* 000011111000 */ + 0x11, 0xc0, /* 000100011100 */ + 0x20, 0xe0, /* 001000001110 */ + 0x60, 0x60, /* 011000000110 */ + 0x60, 0x60, /* 011000000110 */ + 0x70, 0x40, /* 011100000100 */ + 0x38, 0x80, /* 001110001000 */ + 0x1f, 0x00, /* 000111110000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 236 0xec '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x39, 0xc0, /* 001110011100 */ + 0x6f, 0x60, /* 011011110110 */ + 0x66, 0x60, /* 011001100110 */ + 0xc6, 0x30, /* 110001100011 */ + 0xc6, 0x30, /* 110001100011 */ + 0x66, 0x60, /* 011001100110 */ + 0x6f, 0x60, /* 011011110110 */ + 0x39, 0xc0, /* 001110011100 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 237 0xed '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0xc0, /* 000000001100 */ + 0x00, 0xc0, /* 000000001100 */ + 0x01, 0x80, /* 000000011000 */ + 0x01, 0x80, /* 000000011000 */ + 0x3b, 0xc0, /* 001110111100 */ + 0x6f, 0x60, /* 011011110110 */ + 0x66, 0x60, /* 011001100110 */ + 0xc6, 0x30, /* 110001100011 */ + 0xc6, 0x30, /* 110001100011 */ + 0x66, 0x60, /* 011001100110 */ + 0x6f, 0x60, /* 011011110110 */ + 0x3d, 0xc0, /* 001111011100 */ + 0x18, 0x00, /* 000110000000 */ + 0x18, 0x00, /* 000110000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x30, 0x00, /* 001100000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 238 0xee '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x01, 0xc0, /* 000000011100 */ + 0x03, 0x00, /* 000000110000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x18, 0x00, /* 000110000000 */ + 0x1f, 0xc0, /* 000111111100 */ + 0x18, 0x00, /* 000110000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x03, 0x00, /* 000000110000 */ + 0x01, 0xc0, /* 000000011100 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 239 0xef '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x1f, 0x80, /* 000111111000 */ + 0x39, 0xc0, /* 001110011100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x30, 0xc0, /* 001100001100 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 240 0xf0 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 241 0xf1 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x00, 0x00, /* 000000000000 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 242 0xf2 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x38, 0x00, /* 001110000000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x03, 0x80, /* 000000111000 */ + 0x00, 0xe0, /* 000000001110 */ + 0x00, 0xe0, /* 000000001110 */ + 0x03, 0x80, /* 000000111000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x38, 0x00, /* 001110000000 */ + 0x60, 0x00, /* 011000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 243 0xf3 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x60, /* 000000000110 */ + 0x01, 0xc0, /* 000000011100 */ + 0x07, 0x00, /* 000001110000 */ + 0x1c, 0x00, /* 000111000000 */ + 0x70, 0x00, /* 011100000000 */ + 0x70, 0x00, /* 011100000000 */ + 0x1c, 0x00, /* 000111000000 */ + 0x07, 0x00, /* 000001110000 */ + 0x01, 0xc0, /* 000000011100 */ + 0x00, 0x60, /* 000000000110 */ + 0x00, 0x00, /* 000000000000 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 244 0xf4 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x03, 0x80, /* 000000111000 */ + 0x07, 0xc0, /* 000001111100 */ + 0x0c, 0x60, /* 000011000110 */ + 0x0c, 0x60, /* 000011000110 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x0c, 0x00, /* 000011000000 */ + + /* 245 0xf5 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x1c, 0x00, /* 000111000000 */ + 0x3e, 0x00, /* 001111100000 */ + 0x63, 0x00, /* 011000110000 */ + 0x63, 0x00, /* 011000110000 */ + 0x03, 0x00, /* 000000110000 */ + 0x03, 0x00, /* 000000110000 */ + 0x03, 0x00, /* 000000110000 */ + 0x03, 0x00, /* 000000110000 */ + 0x03, 0x00, /* 000000110000 */ + 0x03, 0x00, /* 000000110000 */ + 0x03, 0x00, /* 000000110000 */ + 0x03, 0x00, /* 000000110000 */ + 0x03, 0x00, /* 000000110000 */ + 0x03, 0x00, /* 000000110000 */ + 0x03, 0x00, /* 000000110000 */ + 0x03, 0x00, /* 000000110000 */ + 0x03, 0x00, /* 000000110000 */ + 0x03, 0x00, /* 000000110000 */ + 0x03, 0x00, /* 000000110000 */ + 0x03, 0x00, /* 000000110000 */ + 0x03, 0x00, /* 000000110000 */ + + /* 246 0xf6 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x7f, 0xe0, /* 011111111110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 247 0xf7 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x38, 0x00, /* 001110000000 */ + 0x6c, 0x00, /* 011011000000 */ + 0x06, 0x30, /* 000001100011 */ + 0x03, 0x60, /* 000000110110 */ + 0x39, 0xc0, /* 001110011100 */ + 0x6c, 0x00, /* 011011000000 */ + 0x06, 0x30, /* 000001100011 */ + 0x03, 0x60, /* 000000110110 */ + 0x01, 0xc0, /* 000000011100 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 248 0xf8 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x19, 0x80, /* 000110011000 */ + 0x19, 0x80, /* 000110011000 */ + 0x19, 0x80, /* 000110011000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 249 0xf9 '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x1c, 0x00, /* 000111000000 */ + 0x3e, 0x00, /* 001111100000 */ + 0x3e, 0x00, /* 001111100000 */ + 0x3e, 0x00, /* 001111100000 */ + 0x1c, 0x00, /* 000111000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 250 0xfa '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x18, 0x00, /* 000110000000 */ + 0x3c, 0x00, /* 001111000000 */ + 0x3c, 0x00, /* 001111000000 */ + 0x18, 0x00, /* 000110000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 251 0xfb '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x07, 0xe0, /* 000001111110 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x06, 0x00, /* 000001100000 */ + 0xc6, 0x00, /* 110001100000 */ + 0x66, 0x00, /* 011001100000 */ + 0x36, 0x00, /* 001101100000 */ + 0x1e, 0x00, /* 000111100000 */ + 0x0e, 0x00, /* 000011100000 */ + 0x06, 0x00, /* 000001100000 */ + 0x02, 0x00, /* 000000100000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 252 0xfc '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x13, 0x80, /* 000100111000 */ + 0x3d, 0xc0, /* 001111011100 */ + 0x18, 0xc0, /* 000110001100 */ + 0x18, 0xc0, /* 000110001100 */ + 0x18, 0xc0, /* 000110001100 */ + 0x18, 0xc0, /* 000110001100 */ + 0x3d, 0xe0, /* 001111011110 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 253 0xfd '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x0f, 0x00, /* 000011110000 */ + 0x1f, 0x80, /* 000111111000 */ + 0x31, 0x80, /* 001100011000 */ + 0x21, 0x80, /* 001000011000 */ + 0x03, 0x00, /* 000000110000 */ + 0x06, 0x00, /* 000001100000 */ + 0x0c, 0x00, /* 000011000000 */ + 0x18, 0x40, /* 000110000100 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 254 0xfe '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x3f, 0xc0, /* 001111111100 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + + /* 255 0xff '.' */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + 0x00, 0x00, /* 000000000000 */ + +}; + + +const struct font_desc font_sun_12x22 = { + .idx = SUN12x22_IDX, + .name = "SUN12x22", + .width = 12, + .height = 22, + .data = fontdata_sun12x22, +#ifdef __sparc__ + .pref = 5, +#else + .pref = -1, +#endif +}; diff --git a/lib/fonts/font_sun8x16.c b/lib/fonts/font_sun8x16.c new file mode 100644 index 000000000000..268151325b83 --- /dev/null +++ b/lib/fonts/font_sun8x16.c @@ -0,0 +1,275 @@ +#include + +#define FONTDATAMAX 4096 + +static const unsigned char fontdata_sun8x16[FONTDATAMAX] = { +/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x7e,0x81,0xa5,0x81,0x81,0xbd,0x99,0x81,0x81,0x7e,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x7e,0xff,0xdb,0xff,0xff,0xc3,0xe7,0xff,0xff,0x7e,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x6c,0xfe,0xfe,0xfe,0xfe,0x7c,0x38,0x10,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x10,0x38,0x7c,0xfe,0x7c,0x38,0x10,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x18,0x3c,0x3c,0xe7,0xe7,0xe7,0x18,0x18,0x3c,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x18,0x3c,0x7e,0xff,0xff,0x7e,0x18,0x18,0x3c,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x3c,0x3c,0x18,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0xff,0xff,0xff,0xff,0xff,0xff,0xe7,0xc3,0xc3,0xe7,0xff,0xff,0xff,0xff,0xff,0xff, +/* */ 0x00,0x00,0x00,0x00,0x00,0x3c,0x66,0x42,0x42,0x66,0x3c,0x00,0x00,0x00,0x00,0x00, +/* */ 0xff,0xff,0xff,0xff,0xff,0xc3,0x99,0xbd,0xbd,0x99,0xc3,0xff,0xff,0xff,0xff,0xff, +/* */ 0x00,0x00,0x1e,0x0e,0x1a,0x32,0x78,0xcc,0xcc,0xcc,0xcc,0x78,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x3c,0x66,0x66,0x66,0x66,0x3c,0x18,0x7e,0x18,0x18,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x3f,0x33,0x3f,0x30,0x30,0x30,0x30,0x70,0xf0,0xe0,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x7f,0x63,0x7f,0x63,0x63,0x63,0x63,0x67,0xe7,0xe6,0xc0,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x18,0x18,0xdb,0x3c,0xe7,0x3c,0xdb,0x18,0x18,0x00,0x00,0x00,0x00, +/* */ 0x00,0x80,0xc0,0xe0,0xf0,0xf8,0xfe,0xf8,0xf0,0xe0,0xc0,0x80,0x00,0x00,0x00,0x00, +/* */ 0x00,0x02,0x06,0x0e,0x1e,0x3e,0xfe,0x3e,0x1e,0x0e,0x06,0x02,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x18,0x3c,0x7e,0x18,0x18,0x18,0x7e,0x3c,0x18,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x66,0x66,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x7f,0xdb,0xdb,0xdb,0x7b,0x1b,0x1b,0x1b,0x1b,0x1b,0x00,0x00,0x00,0x00, +/* */ 0x00,0x7c,0xc6,0x60,0x38,0x6c,0xc6,0xc6,0x6c,0x38,0x0c,0xc6,0x7c,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfe,0xfe,0xfe,0xfe,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x18,0x3c,0x7e,0x18,0x18,0x18,0x7e,0x3c,0x18,0x7e,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x18,0x3c,0x7e,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x7e,0x3c,0x18,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x18,0x0c,0xfe,0x0c,0x18,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x30,0x60,0xfe,0x60,0x30,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0xc0,0xc0,0xfe,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x24,0x66,0xff,0x66,0x24,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x10,0x38,0x38,0x7c,0x7c,0xfe,0xfe,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0xfe,0xfe,0x7c,0x7c,0x38,0x38,0x10,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*!*/ 0x00,0x00,0x18,0x3c,0x3c,0x3c,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00, +/*"*/ 0x00,0x66,0x66,0x66,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*#*/ 0x00,0x00,0x00,0x6c,0x6c,0xfe,0x6c,0x6c,0x6c,0xfe,0x6c,0x6c,0x00,0x00,0x00,0x00, +/*$*/ 0x18,0x18,0x7c,0xc6,0xc2,0xc0,0x7c,0x06,0x06,0x86,0xc6,0x7c,0x18,0x18,0x00,0x00, +/*%*/ 0x00,0x00,0x00,0x00,0xc2,0xc6,0x0c,0x18,0x30,0x60,0xc6,0x86,0x00,0x00,0x00,0x00, +/*&*/ 0x00,0x00,0x38,0x6c,0x6c,0x38,0x76,0xdc,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00, +/*'*/ 0x00,0x30,0x30,0x30,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*(*/ 0x00,0x00,0x0c,0x18,0x30,0x30,0x30,0x30,0x30,0x30,0x18,0x0c,0x00,0x00,0x00,0x00, +/*)*/ 0x00,0x00,0x30,0x18,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x18,0x30,0x00,0x00,0x00,0x00, +/***/ 0x00,0x00,0x00,0x00,0x00,0x66,0x3c,0xff,0x3c,0x66,0x00,0x00,0x00,0x00,0x00,0x00, +/*+*/ 0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x7e,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00, +/*,*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x18,0x30,0x00,0x00,0x00, +/*-*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*.*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x02,0x06,0x0c,0x18,0x30,0x60,0xc0,0x80,0x00,0x00,0x00,0x00, +/*0*/ 0x00,0x00,0x7c,0xc6,0xc6,0xce,0xde,0xf6,0xe6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, +/*1*/ 0x00,0x00,0x18,0x38,0x78,0x18,0x18,0x18,0x18,0x18,0x18,0x7e,0x00,0x00,0x00,0x00, +/*2*/ 0x00,0x00,0x7c,0xc6,0x06,0x0c,0x18,0x30,0x60,0xc0,0xc6,0xfe,0x00,0x00,0x00,0x00, +/*3*/ 0x00,0x00,0x7c,0xc6,0x06,0x06,0x3c,0x06,0x06,0x06,0xc6,0x7c,0x00,0x00,0x00,0x00, +/*4*/ 0x00,0x00,0x0c,0x1c,0x3c,0x6c,0xcc,0xfe,0x0c,0x0c,0x0c,0x1e,0x00,0x00,0x00,0x00, +/*5*/ 0x00,0x00,0xfe,0xc0,0xc0,0xc0,0xfc,0x06,0x06,0x06,0xc6,0x7c,0x00,0x00,0x00,0x00, +/*6*/ 0x00,0x00,0x38,0x60,0xc0,0xc0,0xfc,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, +/*7*/ 0x00,0x00,0xfe,0xc6,0x06,0x06,0x0c,0x18,0x30,0x30,0x30,0x30,0x00,0x00,0x00,0x00, +/*8*/ 0x00,0x00,0x7c,0xc6,0xc6,0xc6,0x7c,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, +/*9*/ 0x00,0x00,0x7c,0xc6,0xc6,0xc6,0x7e,0x06,0x06,0x06,0x0c,0x78,0x00,0x00,0x00,0x00, +/*:*/ 0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00, +/*;*/ 0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x30,0x00,0x00,0x00,0x00, +/*<*/ 0x00,0x00,0x00,0x06,0x0c,0x18,0x30,0x60,0x30,0x18,0x0c,0x06,0x00,0x00,0x00,0x00, +/*=*/ 0x00,0x00,0x00,0x00,0x00,0x7e,0x00,0x00,0x7e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*>*/ 0x00,0x00,0x00,0x60,0x30,0x18,0x0c,0x06,0x0c,0x18,0x30,0x60,0x00,0x00,0x00,0x00, +/*?*/ 0x00,0x00,0x7c,0xc6,0xc6,0x0c,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00, +/*@*/ 0x00,0x00,0x7c,0xc6,0xc6,0xc6,0xde,0xde,0xde,0xdc,0xc0,0x7c,0x00,0x00,0x00,0x00, +/*A*/ 0x00,0x00,0x10,0x38,0x6c,0xc6,0xc6,0xfe,0xc6,0xc6,0xc6,0xc6,0x00,0x00,0x00,0x00, +/*B*/ 0x00,0x00,0xfc,0x66,0x66,0x66,0x7c,0x66,0x66,0x66,0x66,0xfc,0x00,0x00,0x00,0x00, +/*C*/ 0x00,0x00,0x3c,0x66,0xc2,0xc0,0xc0,0xc0,0xc0,0xc2,0x66,0x3c,0x00,0x00,0x00,0x00, +/*D*/ 0x00,0x00,0xf8,0x6c,0x66,0x66,0x66,0x66,0x66,0x66,0x6c,0xf8,0x00,0x00,0x00,0x00, +/*E*/ 0x00,0x00,0xfe,0x66,0x62,0x68,0x78,0x68,0x60,0x62,0x66,0xfe,0x00,0x00,0x00,0x00, +/*F*/ 0x00,0x00,0xfe,0x66,0x62,0x68,0x78,0x68,0x60,0x60,0x60,0xf0,0x00,0x00,0x00,0x00, +/*G*/ 0x00,0x00,0x3c,0x66,0xc2,0xc0,0xc0,0xde,0xc6,0xc6,0x66,0x3a,0x00,0x00,0x00,0x00, +/*H*/ 0x00,0x00,0xc6,0xc6,0xc6,0xc6,0xfe,0xc6,0xc6,0xc6,0xc6,0xc6,0x00,0x00,0x00,0x00, +/*I*/ 0x00,0x00,0x3c,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00, +/*J*/ 0x00,0x00,0x1e,0x0c,0x0c,0x0c,0x0c,0x0c,0xcc,0xcc,0xcc,0x78,0x00,0x00,0x00,0x00, +/*K*/ 0x00,0x00,0xe6,0x66,0x66,0x6c,0x78,0x78,0x6c,0x66,0x66,0xe6,0x00,0x00,0x00,0x00, +/*L*/ 0x00,0x00,0xf0,0x60,0x60,0x60,0x60,0x60,0x60,0x62,0x66,0xfe,0x00,0x00,0x00,0x00, +/*M*/ 0x00,0x00,0xc3,0xe7,0xff,0xff,0xdb,0xc3,0xc3,0xc3,0xc3,0xc3,0x00,0x00,0x00,0x00, +/*N*/ 0x00,0x00,0xc6,0xe6,0xf6,0xfe,0xde,0xce,0xc6,0xc6,0xc6,0xc6,0x00,0x00,0x00,0x00, +/*O*/ 0x00,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, +/*P*/ 0x00,0x00,0xfc,0x66,0x66,0x66,0x7c,0x60,0x60,0x60,0x60,0xf0,0x00,0x00,0x00,0x00, +/*Q*/ 0x00,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xd6,0xde,0x7c,0x0c,0x0e,0x00,0x00, +/*R*/ 0x00,0x00,0xfc,0x66,0x66,0x66,0x7c,0x6c,0x66,0x66,0x66,0xe6,0x00,0x00,0x00,0x00, +/*S*/ 0x00,0x00,0x7c,0xc6,0xc6,0x60,0x38,0x0c,0x06,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, +/*T*/ 0x00,0x00,0xff,0xdb,0x99,0x18,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00, +/*U*/ 0x00,0x00,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, +/*V*/ 0x00,0x00,0xc3,0xc3,0xc3,0xc3,0xc3,0xc3,0xc3,0x66,0x3c,0x18,0x00,0x00,0x00,0x00, +/*W*/ 0x00,0x00,0xc3,0xc3,0xc3,0xc3,0xc3,0xdb,0xdb,0xff,0x66,0x66,0x00,0x00,0x00,0x00, +/*X*/ 0x00,0x00,0xc3,0xc3,0x66,0x3c,0x18,0x18,0x3c,0x66,0xc3,0xc3,0x00,0x00,0x00,0x00, +/*Y*/ 0x00,0x00,0xc3,0xc3,0xc3,0x66,0x3c,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00, +/*Z*/ 0x00,0x00,0xff,0xc3,0x86,0x0c,0x18,0x30,0x60,0xc1,0xc3,0xff,0x00,0x00,0x00,0x00, +/*[*/ 0x00,0x00,0x3c,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x3c,0x00,0x00,0x00,0x00, +/*\*/ 0x00,0x00,0x00,0x80,0xc0,0xe0,0x70,0x38,0x1c,0x0e,0x06,0x02,0x00,0x00,0x00,0x00, +/*]*/ 0x00,0x00,0x3c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x3c,0x00,0x00,0x00,0x00, +/*^*/ 0x10,0x38,0x6c,0xc6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*_*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00, +/*`*/ 0x30,0x30,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/*a*/ 0x00,0x00,0x00,0x00,0x00,0x78,0x0c,0x7c,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00, +/*b*/ 0x00,0x00,0xe0,0x60,0x60,0x78,0x6c,0x66,0x66,0x66,0x66,0x7c,0x00,0x00,0x00,0x00, +/*c*/ 0x00,0x00,0x00,0x00,0x00,0x7c,0xc6,0xc0,0xc0,0xc0,0xc6,0x7c,0x00,0x00,0x00,0x00, +/*d*/ 0x00,0x00,0x1c,0x0c,0x0c,0x3c,0x6c,0xcc,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00, +/*e*/ 0x00,0x00,0x00,0x00,0x00,0x7c,0xc6,0xfe,0xc0,0xc0,0xc6,0x7c,0x00,0x00,0x00,0x00, +/*f*/ 0x00,0x00,0x38,0x6c,0x64,0x60,0xf0,0x60,0x60,0x60,0x60,0xf0,0x00,0x00,0x00,0x00, +/*g*/ 0x00,0x00,0x00,0x00,0x00,0x76,0xcc,0xcc,0xcc,0xcc,0xcc,0x7c,0x0c,0xcc,0x78,0x00, +/*h*/ 0x00,0x00,0xe0,0x60,0x60,0x6c,0x76,0x66,0x66,0x66,0x66,0xe6,0x00,0x00,0x00,0x00, +/*i*/ 0x00,0x00,0x18,0x18,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00, +/*j*/ 0x00,0x00,0x06,0x06,0x00,0x0e,0x06,0x06,0x06,0x06,0x06,0x06,0x66,0x66,0x3c,0x00, +/*k*/ 0x00,0x00,0xe0,0x60,0x60,0x66,0x6c,0x78,0x78,0x6c,0x66,0xe6,0x00,0x00,0x00,0x00, +/*l*/ 0x00,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00, +/*m*/ 0x00,0x00,0x00,0x00,0x00,0xe6,0xff,0xdb,0xdb,0xdb,0xdb,0xdb,0x00,0x00,0x00,0x00, +/*n*/ 0x00,0x00,0x00,0x00,0x00,0xdc,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x00,0x00,0x00, +/*o*/ 0x00,0x00,0x00,0x00,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, +/*p*/ 0x00,0x00,0x00,0x00,0x00,0xdc,0x66,0x66,0x66,0x66,0x66,0x7c,0x60,0x60,0xf0,0x00, +/*q*/ 0x00,0x00,0x00,0x00,0x00,0x76,0xcc,0xcc,0xcc,0xcc,0xcc,0x7c,0x0c,0x0c,0x1e,0x00, +/*r*/ 0x00,0x00,0x00,0x00,0x00,0xdc,0x76,0x66,0x60,0x60,0x60,0xf0,0x00,0x00,0x00,0x00, +/*s*/ 0x00,0x00,0x00,0x00,0x00,0x7c,0xc6,0x60,0x38,0x0c,0xc6,0x7c,0x00,0x00,0x00,0x00, +/*t*/ 0x00,0x00,0x10,0x30,0x30,0xfc,0x30,0x30,0x30,0x30,0x36,0x1c,0x00,0x00,0x00,0x00, +/*u*/ 0x00,0x00,0x00,0x00,0x00,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00, +/*v*/ 0x00,0x00,0x00,0x00,0x00,0xc3,0xc3,0xc3,0xc3,0x66,0x3c,0x18,0x00,0x00,0x00,0x00, +/*w*/ 0x00,0x00,0x00,0x00,0x00,0xc3,0xc3,0xc3,0xdb,0xdb,0xff,0x66,0x00,0x00,0x00,0x00, +/*x*/ 0x00,0x00,0x00,0x00,0x00,0xc3,0x66,0x3c,0x18,0x3c,0x66,0xc3,0x00,0x00,0x00,0x00, +/*y*/ 0x00,0x00,0x00,0x00,0x00,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x7e,0x06,0x0c,0xf8,0x00, +/*z*/ 0x00,0x00,0x00,0x00,0x00,0xfe,0xcc,0x18,0x30,0x60,0xc6,0xfe,0x00,0x00,0x00,0x00, +/*{*/ 0x00,0x00,0x0e,0x18,0x18,0x18,0x70,0x18,0x18,0x18,0x18,0x0e,0x00,0x00,0x00,0x00, +/*|*/ 0x00,0x00,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00, +/*}*/ 0x00,0x00,0x70,0x18,0x18,0x18,0x0e,0x18,0x18,0x18,0x18,0x70,0x00,0x00,0x00,0x00, +/*~*/ 0x00,0x00,0x76,0xdc,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x10,0x38,0x6c,0xc6,0xc6,0xc6,0xfe,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x3c,0x66,0xc2,0xc0,0xc0,0xc0,0xc2,0x66,0x3c,0x0c,0x06,0x7c,0x00,0x00, +/* */ 0x00,0x00,0xcc,0x00,0x00,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00, +/* */ 0x00,0x0c,0x18,0x30,0x00,0x7c,0xc6,0xfe,0xc0,0xc0,0xc6,0x7c,0x00,0x00,0x00,0x00, +/* */ 0x00,0x10,0x38,0x6c,0x00,0x78,0x0c,0x7c,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0xcc,0x00,0x00,0x78,0x0c,0x7c,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00, +/* */ 0x00,0x60,0x30,0x18,0x00,0x78,0x0c,0x7c,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00, +/* */ 0x00,0x38,0x6c,0x38,0x00,0x78,0x0c,0x7c,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x3c,0x66,0x60,0x60,0x66,0x3c,0x0c,0x06,0x3c,0x00,0x00,0x00, +/* */ 0x00,0x10,0x38,0x6c,0x00,0x7c,0xc6,0xfe,0xc0,0xc0,0xc6,0x7c,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0xc6,0x00,0x00,0x7c,0xc6,0xfe,0xc0,0xc0,0xc6,0x7c,0x00,0x00,0x00,0x00, +/* */ 0x00,0x60,0x30,0x18,0x00,0x7c,0xc6,0xfe,0xc0,0xc0,0xc6,0x7c,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x66,0x00,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00, +/* */ 0x00,0x18,0x3c,0x66,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00, +/* */ 0x00,0x60,0x30,0x18,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00, +/* */ 0x00,0xc6,0x00,0x10,0x38,0x6c,0xc6,0xc6,0xfe,0xc6,0xc6,0xc6,0x00,0x00,0x00,0x00, +/* */ 0x38,0x6c,0x38,0x00,0x38,0x6c,0xc6,0xc6,0xfe,0xc6,0xc6,0xc6,0x00,0x00,0x00,0x00, +/* */ 0x18,0x30,0x60,0x00,0xfe,0x66,0x60,0x7c,0x60,0x60,0x66,0xfe,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x6e,0x3b,0x1b,0x7e,0xd8,0xdc,0x77,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x3e,0x6c,0xcc,0xcc,0xfe,0xcc,0xcc,0xcc,0xcc,0xce,0x00,0x00,0x00,0x00, +/* */ 0x00,0x10,0x38,0x6c,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0xc6,0x00,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, +/* */ 0x00,0x60,0x30,0x18,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, +/* */ 0x00,0x30,0x78,0xcc,0x00,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00, +/* */ 0x00,0x60,0x30,0x18,0x00,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0xc6,0x00,0x00,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x7e,0x06,0x0c,0x78,0x00, +/* */ 0x00,0xc6,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, +/* */ 0x00,0xc6,0x00,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, +/* */ 0x00,0x18,0x18,0x7e,0xc3,0xc0,0xc0,0xc0,0xc3,0x7e,0x18,0x18,0x00,0x00,0x00,0x00, +/* */ 0x00,0x38,0x6c,0x64,0x60,0xf0,0x60,0x60,0x60,0x60,0xe6,0xfc,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0xc3,0x66,0x3c,0x18,0xff,0x18,0xff,0x18,0x18,0x18,0x00,0x00,0x00,0x00, +/* */ 0x00,0xfc,0x66,0x66,0x7c,0x62,0x66,0x6f,0x66,0x66,0x66,0xf3,0x00,0x00,0x00,0x00, +/* */ 0x00,0x0e,0x1b,0x18,0x18,0x18,0x7e,0x18,0x18,0x18,0x18,0x18,0xd8,0x70,0x00,0x00, +/* */ 0x00,0x18,0x30,0x60,0x00,0x78,0x0c,0x7c,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00, +/* */ 0x00,0x0c,0x18,0x30,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00, +/* */ 0x00,0x18,0x30,0x60,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, +/* */ 0x00,0x18,0x30,0x60,0x00,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x76,0xdc,0x00,0xdc,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x00,0x00,0x00, +/* */ 0x76,0xdc,0x00,0xc6,0xe6,0xf6,0xfe,0xde,0xce,0xc6,0xc6,0xc6,0x00,0x00,0x00,0x00, +/* */ 0x00,0x3c,0x6c,0x6c,0x3e,0x00,0x7e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x38,0x6c,0x6c,0x38,0x00,0x7c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x30,0x30,0x00,0x30,0x30,0x60,0xc0,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0xfe,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0xfe,0x06,0x06,0x06,0x06,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0xc0,0xc0,0xc2,0xc6,0xcc,0x18,0x30,0x60,0xce,0x9b,0x06,0x0c,0x1f,0x00,0x00, +/* */ 0x00,0xc0,0xc0,0xc2,0xc6,0xcc,0x18,0x30,0x66,0xce,0x96,0x3e,0x06,0x06,0x00,0x00, +/* */ 0x00,0x00,0x18,0x18,0x00,0x18,0x18,0x18,0x3c,0x3c,0x3c,0x18,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x36,0x6c,0xd8,0x6c,0x36,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0xd8,0x6c,0x36,0x6c,0xd8,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x11,0x44,0x11,0x44,0x11,0x44,0x11,0x44,0x11,0x44,0x11,0x44,0x11,0x44,0x11,0x44, +/* */ 0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa, +/* */ 0xdd,0x77,0xdd,0x77,0xdd,0x77,0xdd,0x77,0xdd,0x77,0xdd,0x77,0xdd,0x77,0xdd,0x77, +/* */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xf8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* */ 0x18,0x18,0x18,0x18,0x18,0xf8,0x18,0xf8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* */ 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xf6,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfe,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +/* */ 0x00,0x00,0x00,0x00,0x00,0xf8,0x18,0xf8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* */ 0x36,0x36,0x36,0x36,0x36,0xf6,0x06,0xf6,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +/* */ 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +/* */ 0x00,0x00,0x00,0x00,0x00,0xfe,0x06,0xf6,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +/* */ 0x36,0x36,0x36,0x36,0x36,0xf6,0x06,0xfe,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xfe,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x18,0x18,0x18,0x18,0x18,0xf8,0x18,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1f,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xff,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* */ 0x18,0x18,0x18,0x18,0x18,0x1f,0x18,0x1f,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* */ 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x37,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +/* */ 0x36,0x36,0x36,0x36,0x36,0x37,0x30,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x3f,0x30,0x37,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +/* */ 0x36,0x36,0x36,0x36,0x36,0xf7,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xf7,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +/* */ 0x36,0x36,0x36,0x36,0x36,0x37,0x30,0x37,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +/* */ 0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x36,0x36,0x36,0x36,0x36,0xf7,0x00,0xf7,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +/* */ 0x18,0x18,0x18,0x18,0x18,0xff,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +/* */ 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x18,0x18,0x18,0x18,0x18,0x1f,0x18,0x1f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x1f,0x18,0x1f,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3f,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +/* */ 0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xff,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36, +/* */ 0x18,0x18,0x18,0x18,0x18,0xff,0x18,0xff,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1f,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +/* */ 0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0, +/* */ 0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, +/* */ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x76,0xdc,0xd8,0xd8,0xd8,0xdc,0x76,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x78,0xcc,0xcc,0xcc,0xd8,0xcc,0xc6,0xc6,0xc6,0xcc,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0xfe,0xc6,0xc6,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0xfe,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0xfe,0xc6,0x60,0x30,0x18,0x30,0x60,0xc6,0xfe,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x7e,0xd8,0xd8,0xd8,0xd8,0xd8,0x70,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x66,0x66,0x66,0x66,0x66,0x7c,0x60,0x60,0xc0,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x76,0xdc,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x7e,0x18,0x3c,0x66,0x66,0x66,0x3c,0x18,0x7e,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x38,0x6c,0xc6,0xc6,0xfe,0xc6,0xc6,0x6c,0x38,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x38,0x6c,0xc6,0xc6,0xc6,0x6c,0x6c,0x6c,0x6c,0xee,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x1e,0x30,0x18,0x0c,0x3e,0x66,0x66,0x66,0x66,0x3c,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x7e,0xdb,0xdb,0xdb,0x7e,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x03,0x06,0x7e,0xdb,0xdb,0xf3,0x7e,0x60,0xc0,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x1c,0x30,0x60,0x60,0x7c,0x60,0x60,0x60,0x30,0x1c,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0xfe,0x00,0x00,0xfe,0x00,0x00,0xfe,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x18,0x18,0x7e,0x18,0x18,0x00,0x00,0xff,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x30,0x18,0x0c,0x06,0x0c,0x18,0x30,0x00,0x7e,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x0c,0x18,0x30,0x60,0x30,0x18,0x0c,0x00,0x7e,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x0e,0x1b,0x1b,0x1b,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, +/* */ 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xd8,0xd8,0xd8,0x70,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x7e,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x76,0xdc,0x00,0x76,0xdc,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x38,0x6c,0x6c,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x0f,0x0c,0x0c,0x0c,0x0c,0x0c,0xec,0x6c,0x6c,0x3c,0x1c,0x00,0x00,0x00,0x00, +/* */ 0x00,0xd8,0x6c,0x6c,0x6c,0x6c,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x70,0xd8,0x30,0x60,0xc8,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x7c,0x7c,0x7c,0x7c,0x7c,0x7c,0x7c,0x00,0x00,0x00,0x00,0x00, +/* */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; + +const struct font_desc font_sun_8x16 = { + .idx = SUN8x16_IDX, + .name = "SUN8x16", + .width = 8, + .height = 16, + .data = fontdata_sun8x16, +#ifdef __sparc__ + .pref = 10, +#else + .pref = -1, +#endif +}; diff --git a/lib/fonts/fonts.c b/lib/fonts/fonts.c new file mode 100644 index 000000000000..f947189efe6d --- /dev/null +++ b/lib/fonts/fonts.c @@ -0,0 +1,153 @@ +/* + * `Soft' font definitions + * + * Created 1995 by Geert Uytterhoeven + * Rewritten 1998 by Martin Mares + * + * 2001 - Documented with DocBook + * - Brad Douglas + * + * 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 +#include +#include +#if defined(__mc68000__) +#include +#endif +#include + +#define NO_FONTS + +static const struct font_desc *fonts[] = { +#ifdef CONFIG_FONT_8x8 +#undef NO_FONTS + &font_vga_8x8, +#endif +#ifdef CONFIG_FONT_8x16 +#undef NO_FONTS + &font_vga_8x16, +#endif +#ifdef CONFIG_FONT_6x11 +#undef NO_FONTS + &font_vga_6x11, +#endif +#ifdef CONFIG_FONT_7x14 +#undef NO_FONTS + &font_7x14, +#endif +#ifdef CONFIG_FONT_SUN8x16 +#undef NO_FONTS + &font_sun_8x16, +#endif +#ifdef CONFIG_FONT_SUN12x22 +#undef NO_FONTS + &font_sun_12x22, +#endif +#ifdef CONFIG_FONT_10x18 +#undef NO_FONTS + &font_10x18, +#endif +#ifdef CONFIG_FONT_ACORN_8x8 +#undef NO_FONTS + &font_acorn_8x8, +#endif +#ifdef CONFIG_FONT_PEARL_8x8 +#undef NO_FONTS + &font_pearl_8x8, +#endif +#ifdef CONFIG_FONT_MINI_4x6 +#undef NO_FONTS + &font_mini_4x6, +#endif +}; + +#define num_fonts ARRAY_SIZE(fonts) + +#ifdef NO_FONTS +#error No fonts configured. +#endif + + +/** + * find_font - find a font + * @name: string name of a font + * + * Find a specified font with string name @name. + * + * Returns %NULL if no font found, or a pointer to the + * specified font. + * + */ + +const struct font_desc *find_font(const char *name) +{ + unsigned int i; + + for (i = 0; i < num_fonts; i++) + if (!strcmp(fonts[i]->name, name)) + return fonts[i]; + return NULL; +} + + +/** + * get_default_font - get default font + * @xres: screen size of X + * @yres: screen size of Y + * @font_w: bit array of supported widths (1 - 32) + * @font_h: bit array of supported heights (1 - 32) + * + * Get the default font for a specified screen size. + * Dimensions are in pixels. + * + * Returns %NULL if no font is found, or a pointer to the + * chosen font. + * + */ + +const struct font_desc *get_default_font(int xres, int yres, u32 font_w, + u32 font_h) +{ + int i, c, cc; + const struct font_desc *f, *g; + + g = NULL; + cc = -10000; + for(i=0; ipref; +#if defined(__mc68000__) +#ifdef CONFIG_FONT_PEARL_8x8 + if (MACH_IS_AMIGA && f->idx == PEARL8x8_IDX) + c = 100; +#endif +#ifdef CONFIG_FONT_6x11 + if (MACH_IS_MAC && xres < 640 && f->idx == VGA6x11_IDX) + c = 100; +#endif +#endif + if ((yres < 400) == (f->height <= 8)) + c += 1000; + + if ((font_w & (1 << (f->width - 1))) && + (font_h & (1 << (f->height - 1)))) + c += 1000; + + if (c > cc) { + cc = c; + g = f; + } + } + return g; +} + +EXPORT_SYMBOL(find_font); +EXPORT_SYMBOL(get_default_font); + +MODULE_AUTHOR("James Simmons "); +MODULE_DESCRIPTION("Console Fonts"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From d0e63ed9411fb9fe95dac1690deb01f9ccb1701d Mon Sep 17 00:00:00 2001 From: Alexey Brodkin Date: Wed, 26 Jun 2013 14:02:18 +0400 Subject: ARC: [plat-arcfpga] Enable arc_emac for ARCAngle4 Board * Add arc_emac to DeviceTree DT description "Documentation/devicetree/bindings/net/arc_emac.txt". * Update defconfig correspondingly [vgupta: tweaked changelog] Signed-off-by: Alexey Brodkin Signed-off-by: Vineet Gupta --- arch/arc/boot/dts/angel4.dts | 16 ++++++++++++++++ arch/arc/configs/fpga_defconfig | 3 +++ arch/arc/plat-arcfpga/include/plat/irq.h | 2 -- arch/arc/plat-arcfpga/include/plat/memmap.h | 2 -- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/arch/arc/boot/dts/angel4.dts b/arch/arc/boot/dts/angel4.dts index bae4f936cb03..4fb2d6f655bd 100644 --- a/arch/arc/boot/dts/angel4.dts +++ b/arch/arc/boot/dts/angel4.dts @@ -51,5 +51,21 @@ current-speed = <115200>; status = "okay"; }; + + ethernet@c0fc2000 { + compatible = "snps,arc-emac"; + reg = <0xc0fc2000 0x3c>; + interrupts = <6>; + mac-address = [ 00 11 22 33 44 55 ]; + clock-frequency = <80000000>; + max-speed = <100>; + phy = <&phy0>; + + #address-cells = <1>; + #size-cells = <0>; + phy0: ethernet-phy@0 { + reg = <1>; + }; + }; }; }; diff --git a/arch/arc/configs/fpga_defconfig b/arch/arc/configs/fpga_defconfig index 95350be6ef6f..2c8ee30e2756 100644 --- a/arch/arc/configs/fpga_defconfig +++ b/arch/arc/configs/fpga_defconfig @@ -38,6 +38,9 @@ CONFIG_INET=y # CONFIG_PREVENT_FIRMWARE_BUILD is not set # CONFIG_FIRMWARE_IN_KERNEL is not set # CONFIG_BLK_DEV is not set +CONFIG_NETDEVICES=y +CONFIG_ARC_EMAC=y +CONFIG_LXT_PHY=y # CONFIG_INPUT_MOUSEDEV_PSAUX is not set # CONFIG_INPUT_KEYBOARD is not set # CONFIG_INPUT_MOUSE is not set diff --git a/arch/arc/plat-arcfpga/include/plat/irq.h b/arch/arc/plat-arcfpga/include/plat/irq.h index 41e335670f60..6adbc53c3a5b 100644 --- a/arch/arc/plat-arcfpga/include/plat/irq.h +++ b/arch/arc/plat-arcfpga/include/plat/irq.h @@ -16,8 +16,6 @@ #define UART1_IRQ 10 #define UART2_IRQ 11 -#define VMAC_IRQ 6 - #define IDE_IRQ 13 #define PCI_IRQ 14 #define PS2_IRQ 15 diff --git a/arch/arc/plat-arcfpga/include/plat/memmap.h b/arch/arc/plat-arcfpga/include/plat/memmap.h index 1663f3388085..5c78e6135a1f 100644 --- a/arch/arc/plat-arcfpga/include/plat/memmap.h +++ b/arch/arc/plat-arcfpga/include/plat/memmap.h @@ -15,8 +15,6 @@ #define UART0_BASE 0xC0FC1000 #define UART1_BASE 0xC0FC1100 -#define VMAC_REG_BASEADDR 0xC0FC2000 - #define IDE_CONTROLLER_BASE 0xC0FC9000 #define AHB_PCI_HOST_BRG_BASE 0xC0FD0000 -- cgit v1.2.3 From 723e2b801d803035ec7a7c0fe162a6c9fc118164 Mon Sep 17 00:00:00 2001 From: Christian Ruppert Date: Wed, 26 Jun 2013 16:01:28 +0200 Subject: ARC: [TB10x] Updates for irqchip driver Device tree and Kconfig updates for irqchip driver. Signed-off-by: Christian Ruppert Signed-off-by: Vineet Gupta --- arch/arc/boot/dts/abilis_tb100.dtsi | 32 ++++++++++++++------------------ arch/arc/boot/dts/abilis_tb101.dtsi | 32 ++++++++++++++------------------ arch/arc/boot/dts/abilis_tb10x.dtsi | 32 ++++++++++++++------------------ arch/arc/plat-tb10x/Kconfig | 1 + 4 files changed, 43 insertions(+), 54 deletions(-) diff --git a/arch/arc/boot/dts/abilis_tb100.dtsi b/arch/arc/boot/dts/abilis_tb100.dtsi index 941ad118a7e7..d9f8249aa66e 100644 --- a/arch/arc/boot/dts/abilis_tb100.dtsi +++ b/arch/arc/boot/dts/abilis_tb100.dtsi @@ -21,10 +21,6 @@ /include/ "abilis_tb10x.dtsi" -/* interrupt specifiers - * -------------------- - * 0: rising, 1: low, 2: high, 3: falling, - */ / { clock-frequency = <500000000>; /* 500 MHZ */ @@ -173,7 +169,7 @@ interrupt-controller; #interrupt-cells = <1>; interrupt-parent = <&tb10x_ictl>; - interrupts = <27 1>; + interrupts = <27 2>; reg = <0xFF140000 0x1000>; gpio-controller; #gpio-cells = <1>; @@ -185,7 +181,7 @@ interrupt-controller; #interrupt-cells = <1>; interrupt-parent = <&tb10x_ictl>; - interrupts = <27 1>; + interrupts = <27 2>; reg = <0xFF141000 0x1000>; gpio-controller; #gpio-cells = <1>; @@ -197,7 +193,7 @@ interrupt-controller; #interrupt-cells = <1>; interrupt-parent = <&tb10x_ictl>; - interrupts = <27 1>; + interrupts = <27 2>; reg = <0xFF142000 0x1000>; gpio-controller; #gpio-cells = <1>; @@ -209,7 +205,7 @@ interrupt-controller; #interrupt-cells = <1>; interrupt-parent = <&tb10x_ictl>; - interrupts = <27 1>; + interrupts = <27 2>; reg = <0xFF143000 0x1000>; gpio-controller; #gpio-cells = <1>; @@ -221,7 +217,7 @@ interrupt-controller; #interrupt-cells = <1>; interrupt-parent = <&tb10x_ictl>; - interrupts = <27 1>; + interrupts = <27 2>; reg = <0xFF144000 0x1000>; gpio-controller; #gpio-cells = <1>; @@ -233,7 +229,7 @@ interrupt-controller; #interrupt-cells = <1>; interrupt-parent = <&tb10x_ictl>; - interrupts = <27 1>; + interrupts = <27 2>; reg = <0xFF145000 0x1000>; gpio-controller; #gpio-cells = <1>; @@ -245,7 +241,7 @@ interrupt-controller; #interrupt-cells = <1>; interrupt-parent = <&tb10x_ictl>; - interrupts = <27 1>; + interrupts = <27 2>; reg = <0xFF146000 0x1000>; gpio-controller; #gpio-cells = <1>; @@ -257,7 +253,7 @@ interrupt-controller; #interrupt-cells = <1>; interrupt-parent = <&tb10x_ictl>; - interrupts = <27 1>; + interrupts = <27 2>; reg = <0xFF147000 0x1000>; gpio-controller; #gpio-cells = <1>; @@ -269,7 +265,7 @@ interrupt-controller; #interrupt-cells = <1>; interrupt-parent = <&tb10x_ictl>; - interrupts = <27 1>; + interrupts = <27 2>; reg = <0xFF148000 0x1000>; gpio-controller; #gpio-cells = <1>; @@ -281,7 +277,7 @@ interrupt-controller; #interrupt-cells = <1>; interrupt-parent = <&tb10x_ictl>; - interrupts = <27 1>; + interrupts = <27 2>; reg = <0xFF149000 0x1000>; gpio-controller; #gpio-cells = <1>; @@ -293,7 +289,7 @@ interrupt-controller; #interrupt-cells = <1>; interrupt-parent = <&tb10x_ictl>; - interrupts = <27 1>; + interrupts = <27 2>; reg = <0xFF14A000 0x1000>; gpio-controller; #gpio-cells = <1>; @@ -305,7 +301,7 @@ interrupt-controller; #interrupt-cells = <1>; interrupt-parent = <&tb10x_ictl>; - interrupts = <27 1>; + interrupts = <27 2>; reg = <0xFF14B000 0x1000>; gpio-controller; #gpio-cells = <1>; @@ -317,7 +313,7 @@ interrupt-controller; #interrupt-cells = <1>; interrupt-parent = <&tb10x_ictl>; - interrupts = <27 1>; + interrupts = <27 2>; reg = <0xFF14C000 0x1000>; gpio-controller; #gpio-cells = <1>; @@ -329,7 +325,7 @@ interrupt-controller; #interrupt-cells = <1>; interrupt-parent = <&tb10x_ictl>; - interrupts = <27 1>; + interrupts = <27 2>; reg = <0xFF14D000 0x1000>; gpio-controller; #gpio-cells = <1>; diff --git a/arch/arc/boot/dts/abilis_tb101.dtsi b/arch/arc/boot/dts/abilis_tb101.dtsi index fd25c212049f..da8ca7941e67 100644 --- a/arch/arc/boot/dts/abilis_tb101.dtsi +++ b/arch/arc/boot/dts/abilis_tb101.dtsi @@ -21,10 +21,6 @@ /include/ "abilis_tb10x.dtsi" -/* interrupt specifiers - * -------------------- - * 0: rising, 1: low, 2: high, 3: falling, - */ / { clock-frequency = <500000000>; /* 500 MHZ */ @@ -182,7 +178,7 @@ interrupt-controller; #interrupt-cells = <1>; interrupt-parent = <&tb10x_ictl>; - interrupts = <27 1>; + interrupts = <27 2>; reg = <0xFF140000 0x1000>; gpio-controller; #gpio-cells = <1>; @@ -194,7 +190,7 @@ interrupt-controller; #interrupt-cells = <1>; interrupt-parent = <&tb10x_ictl>; - interrupts = <27 1>; + interrupts = <27 2>; reg = <0xFF141000 0x1000>; gpio-controller; #gpio-cells = <1>; @@ -206,7 +202,7 @@ interrupt-controller; #interrupt-cells = <1>; interrupt-parent = <&tb10x_ictl>; - interrupts = <27 1>; + interrupts = <27 2>; reg = <0xFF142000 0x1000>; gpio-controller; #gpio-cells = <1>; @@ -218,7 +214,7 @@ interrupt-controller; #interrupt-cells = <1>; interrupt-parent = <&tb10x_ictl>; - interrupts = <27 1>; + interrupts = <27 2>; reg = <0xFF143000 0x1000>; gpio-controller; #gpio-cells = <1>; @@ -230,7 +226,7 @@ interrupt-controller; #interrupt-cells = <1>; interrupt-parent = <&tb10x_ictl>; - interrupts = <27 1>; + interrupts = <27 2>; reg = <0xFF144000 0x1000>; gpio-controller; #gpio-cells = <1>; @@ -242,7 +238,7 @@ interrupt-controller; #interrupt-cells = <1>; interrupt-parent = <&tb10x_ictl>; - interrupts = <27 1>; + interrupts = <27 2>; reg = <0xFF145000 0x1000>; gpio-controller; #gpio-cells = <1>; @@ -254,7 +250,7 @@ interrupt-controller; #interrupt-cells = <1>; interrupt-parent = <&tb10x_ictl>; - interrupts = <27 1>; + interrupts = <27 2>; reg = <0xFF146000 0x1000>; gpio-controller; #gpio-cells = <1>; @@ -266,7 +262,7 @@ interrupt-controller; #interrupt-cells = <1>; interrupt-parent = <&tb10x_ictl>; - interrupts = <27 1>; + interrupts = <27 2>; reg = <0xFF147000 0x1000>; gpio-controller; #gpio-cells = <1>; @@ -278,7 +274,7 @@ interrupt-controller; #interrupt-cells = <1>; interrupt-parent = <&tb10x_ictl>; - interrupts = <27 1>; + interrupts = <27 2>; reg = <0xFF148000 0x1000>; gpio-controller; #gpio-cells = <1>; @@ -290,7 +286,7 @@ interrupt-controller; #interrupt-cells = <1>; interrupt-parent = <&tb10x_ictl>; - interrupts = <27 1>; + interrupts = <27 2>; reg = <0xFF149000 0x1000>; gpio-controller; #gpio-cells = <1>; @@ -302,7 +298,7 @@ interrupt-controller; #interrupt-cells = <1>; interrupt-parent = <&tb10x_ictl>; - interrupts = <27 1>; + interrupts = <27 2>; reg = <0xFF14A000 0x1000>; gpio-controller; #gpio-cells = <1>; @@ -314,7 +310,7 @@ interrupt-controller; #interrupt-cells = <1>; interrupt-parent = <&tb10x_ictl>; - interrupts = <27 1>; + interrupts = <27 2>; reg = <0xFF14B000 0x1000>; gpio-controller; #gpio-cells = <1>; @@ -326,7 +322,7 @@ interrupt-controller; #interrupt-cells = <1>; interrupt-parent = <&tb10x_ictl>; - interrupts = <27 1>; + interrupts = <27 2>; reg = <0xFF14C000 0x1000>; gpio-controller; #gpio-cells = <1>; @@ -338,7 +334,7 @@ interrupt-controller; #interrupt-cells = <1>; interrupt-parent = <&tb10x_ictl>; - interrupts = <27 1>; + interrupts = <27 2>; reg = <0xFF14D000 0x1000>; gpio-controller; #gpio-cells = <1>; diff --git a/arch/arc/boot/dts/abilis_tb10x.dtsi b/arch/arc/boot/dts/abilis_tb10x.dtsi index b97e3051ba4b..edf56f4749e1 100644 --- a/arch/arc/boot/dts/abilis_tb10x.dtsi +++ b/arch/arc/boot/dts/abilis_tb10x.dtsi @@ -19,10 +19,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* interrupt specifiers - * -------------------- - * 0: rising, 1: low, 2: high, 3: falling, - */ / { compatible = "abilis,arc-tb10x"; @@ -78,7 +74,7 @@ #interrupt-cells = <1>; }; tb10x_ictl: pic@fe002000 { - compatible = "abilis,tb10x_ictl"; + compatible = "abilis,tb10x-ictl"; reg = <0xFE002000 0x20>; interrupt-controller; #interrupt-cells = <2>; @@ -91,7 +87,7 @@ compatible = "snps,dw-apb-uart"; reg = <0xFF100000 0x100>; clock-frequency = <166666666>; - interrupts = <25 1>; + interrupts = <25 8>; reg-shift = <2>; reg-io-width = <4>; interrupt-parent = <&tb10x_ictl>; @@ -100,7 +96,7 @@ compatible = "snps,dwmac-3.70a","snps,dwmac"; reg = <0xFE100000 0x1058>; interrupt-parent = <&tb10x_ictl>; - interrupts = <6 1>; + interrupts = <6 8>; interrupt-names = "macirq"; clocks = <&ahb_clk>; clock-names = "stmmaceth"; @@ -109,7 +105,7 @@ compatible = "snps,dma-spear1340"; reg = <0xFE000000 0x400>; interrupt-parent = <&tb10x_ictl>; - interrupts = <14 1>; + interrupts = <14 8>; dma-channels = <6>; dma-requests = <0>; dma-masters = <1>; @@ -128,7 +124,7 @@ compatible = "snps,designware-i2c"; reg = <0xFF120000 0x1000>; interrupt-parent = <&tb10x_ictl>; - interrupts = <12 1>; + interrupts = <12 8>; clocks = <&ahb_clk>; }; i2c1: i2c@FF121000 { @@ -137,7 +133,7 @@ compatible = "snps,designware-i2c"; reg = <0xFF121000 0x1000>; interrupt-parent = <&tb10x_ictl>; - interrupts = <12 1>; + interrupts = <12 8>; clocks = <&ahb_clk>; }; i2c2: i2c@FF122000 { @@ -146,7 +142,7 @@ compatible = "snps,designware-i2c"; reg = <0xFF122000 0x1000>; interrupt-parent = <&tb10x_ictl>; - interrupts = <12 1>; + interrupts = <12 8>; clocks = <&ahb_clk>; }; i2c3: i2c@FF123000 { @@ -155,7 +151,7 @@ compatible = "snps,designware-i2c"; reg = <0xFF123000 0x1000>; interrupt-parent = <&tb10x_ictl>; - interrupts = <12 1>; + interrupts = <12 8>; clocks = <&ahb_clk>; }; i2c4: i2c@FF124000 { @@ -164,7 +160,7 @@ compatible = "snps,designware-i2c"; reg = <0xFF124000 0x1000>; interrupt-parent = <&tb10x_ictl>; - interrupts = <12 1>; + interrupts = <12 8>; clocks = <&ahb_clk>; }; @@ -176,7 +172,7 @@ num-cs = <1>; reg = <0xFE010000 0x20>; interrupt-parent = <&tb10x_ictl>; - interrupts = <26 1>; + interrupts = <26 8>; clocks = <&ahb_clk>; }; spi1: spi@0xFE011000 { @@ -187,7 +183,7 @@ num-cs = <2>; reg = <0xFE011000 0x20>; interrupt-parent = <&tb10x_ictl>; - interrupts = <10 1>; + interrupts = <10 8>; clocks = <&ahb_clk>; }; @@ -195,7 +191,7 @@ compatible = "abilis,tb100-tsm"; reg = <0xff316000 0x400>; interrupt-parent = <&tb10x_ictl>; - interrupts = <17 1>; + interrupts = <17 8>; output-clkdiv = <4>; global-packet-delay = <0x21>; port-packet-delay = <0>; @@ -213,7 +209,7 @@ "cpuctrl", "a6it_int_force"; interrupt-parent = <&tb10x_ictl>; - interrupts = <20 1>, <19 1>; + interrupts = <20 2>, <19 2>; interrupt-names = "cmd_irq", "event_irq"; }; tb10x_mdsc0: tb10x-mdscr@FF300000 { @@ -239,7 +235,7 @@ compatible = "abilis,tb100-wfb"; reg = <0xff319000 0x1000>; interrupt-parent = <&tb10x_ictl>; - interrupts = <16 1>; + interrupts = <16 8>; }; }; }; diff --git a/arch/arc/plat-tb10x/Kconfig b/arch/arc/plat-tb10x/Kconfig index 1d3452100f1f..1ab386bb5da8 100644 --- a/arch/arc/plat-tb10x/Kconfig +++ b/arch/arc/plat-tb10x/Kconfig @@ -22,6 +22,7 @@ menuconfig ARC_PLAT_TB10X select PINCTRL select PINMUX select ARCH_REQUIRE_GPIOLIB + select TB10X_IRQC help Support for platforms based on the TB10x home media gateway SOC by Abilis Systems. TB10x is based on the ARC700 CPU architecture. -- cgit v1.2.3 From 6c50e9147ff03996417e24a11e31831d245b52f0 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 28 Jun 2013 10:57:26 +0100 Subject: ASoC: mfld: Remove unused variable Reported-by: Fengguang Wu Signed-off-by: Mark Brown --- sound/soc/mid-x86/mfld_machine.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/mid-x86/mfld_machine.c b/sound/soc/mid-x86/mfld_machine.c index aec29a805354..ee363845759e 100644 --- a/sound/soc/mid-x86/mfld_machine.c +++ b/sound/soc/mid-x86/mfld_machine.c @@ -412,8 +412,6 @@ static int snd_mfld_mc_probe(struct platform_device *pdev) static int snd_mfld_mc_remove(struct platform_device *pdev) { - struct mfld_mc_private *mc_drv_ctx = platform_get_drvdata(pdev); - pr_debug("snd_mfld_mc_remove called\n"); snd_soc_unregister_card(&snd_soc_card_mfld); return 0; -- cgit v1.2.3 From ad60d502fb8aaa3c1e011f4d72b8228f553d87a8 Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Fri, 28 Jun 2013 12:03:01 +0200 Subject: ALSA: hda - Add support for ALC5505 DSP power-save mode This patch adds the power-saving control for ALC5505 DSP on some Realtek codecs. Signed-off-by: Kailang Yang Tested-by: Mengdong Lin Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 98 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index ae121113f223..eeb6ecc31366 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -115,6 +115,7 @@ struct alc_spec { int init_amp; int codec_variant; /* flag for other variants */ + bool has_alc5505_dsp; /* for PLL fix */ hda_nid_t pll_nid; @@ -2580,7 +2581,96 @@ static void alc269_shutup(struct hda_codec *codec) } } +static void alc5505_coef_set(struct hda_codec *codec, unsigned int index_reg, + unsigned int val) +{ + snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_COEF_INDEX, index_reg >> 1); + snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_PROC_COEF, val & 0xffff); /* LSB */ + snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_PROC_COEF, val >> 16); /* MSB */ +} + +static int alc5505_coef_get(struct hda_codec *codec, unsigned int index_reg) +{ + unsigned int val; + + snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_COEF_INDEX, index_reg >> 1); + val = snd_hda_codec_read(codec, 0x51, 0, AC_VERB_GET_PROC_COEF, 0) + & 0xffff; + val |= snd_hda_codec_read(codec, 0x51, 0, AC_VERB_GET_PROC_COEF, 0) + << 16; + return val; +} + +static void alc5505_dsp_halt(struct hda_codec *codec) +{ + unsigned int val; + + alc5505_coef_set(codec, 0x3000, 0x000c); /* DSP CPU stop */ + alc5505_coef_set(codec, 0x880c, 0x0008); /* DDR enter self refresh */ + alc5505_coef_set(codec, 0x61c0, 0x11110080); /* Clock control for PLL and CPU */ + alc5505_coef_set(codec, 0x6230, 0xfc0d4011); /* Disable Input OP */ + alc5505_coef_set(codec, 0x61b4, 0x040a2b03); /* Stop PLL2 */ + alc5505_coef_set(codec, 0x61b0, 0x00005b17); /* Stop PLL1 */ + alc5505_coef_set(codec, 0x61b8, 0x04133303); /* Stop PLL3 */ + val = alc5505_coef_get(codec, 0x6220); + alc5505_coef_set(codec, 0x6220, (val | 0x3000)); /* switch Ringbuffer clock to DBUS clock */ +} + +static void alc5505_dsp_back_from_halt(struct hda_codec *codec) +{ + alc5505_coef_set(codec, 0x61b8, 0x04133302); + alc5505_coef_set(codec, 0x61b0, 0x00005b16); + alc5505_coef_set(codec, 0x61b4, 0x040a2b02); + alc5505_coef_set(codec, 0x6230, 0xf80d4011); + alc5505_coef_set(codec, 0x6220, 0x2002010f); + alc5505_coef_set(codec, 0x880c, 0x00000004); +} + +static void alc5505_dsp_init(struct hda_codec *codec) +{ + unsigned int val; + + alc5505_dsp_halt(codec); + alc5505_dsp_back_from_halt(codec); + alc5505_coef_set(codec, 0x61b0, 0x5b14); /* PLL1 control */ + alc5505_coef_set(codec, 0x61b0, 0x5b16); + alc5505_coef_set(codec, 0x61b4, 0x04132b00); /* PLL2 control */ + alc5505_coef_set(codec, 0x61b4, 0x04132b02); + alc5505_coef_set(codec, 0x61b8, 0x041f3300); /* PLL3 control*/ + alc5505_coef_set(codec, 0x61b8, 0x041f3302); + snd_hda_codec_write(codec, 0x51, 0, AC_VERB_SET_CODEC_RESET, 0); /* Function reset */ + alc5505_coef_set(codec, 0x61b8, 0x041b3302); + alc5505_coef_set(codec, 0x61b8, 0x04173302); + alc5505_coef_set(codec, 0x61b8, 0x04163302); + alc5505_coef_set(codec, 0x8800, 0x348b328b); /* DRAM control */ + alc5505_coef_set(codec, 0x8808, 0x00020022); /* DRAM control */ + alc5505_coef_set(codec, 0x8818, 0x00000400); /* DRAM control */ + + val = alc5505_coef_get(codec, 0x6200) >> 16; /* Read revision ID */ + if (val <= 3) + alc5505_coef_set(codec, 0x6220, 0x2002010f); /* I/O PAD Configuration */ + else + alc5505_coef_set(codec, 0x6220, 0x6002018f); + + alc5505_coef_set(codec, 0x61ac, 0x055525f0); /**/ + alc5505_coef_set(codec, 0x61c0, 0x12230080); /* Clock control */ + alc5505_coef_set(codec, 0x61b4, 0x040e2b02); /* PLL2 control */ + alc5505_coef_set(codec, 0x61bc, 0x010234f8); /* OSC Control */ + alc5505_coef_set(codec, 0x880c, 0x00000004); /* DRAM Function control */ + alc5505_coef_set(codec, 0x880c, 0x00000003); + alc5505_coef_set(codec, 0x880c, 0x00000010); +} + #ifdef CONFIG_PM +static int alc269_suspend(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + if (spec->has_alc5505_dsp) + alc5505_dsp_halt(codec); + return alc_suspend(codec); +} + static int alc269_resume(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -2605,6 +2695,8 @@ static int alc269_resume(struct hda_codec *codec) snd_hda_codec_resume_cache(codec); alc_inv_dmic_sync(codec, true); hda_call_check_power_status(codec, 0x01); + if (spec->has_alc5505_dsp) + alc5505_dsp_back_from_halt(codec); return 0; } #endif /* CONFIG_PM */ @@ -3730,6 +3822,11 @@ static int patch_alc269(struct hda_codec *codec) break; } + if (snd_hda_codec_read(codec, 0x51, 0, AC_VERB_PARAMETERS, 0) == 0x10ec5505) { + spec->has_alc5505_dsp = true; + spec->init_hook = alc5505_dsp_init; + } + /* automatic parse from the BIOS config */ err = alc269_parse_auto_config(codec); if (err < 0) @@ -3740,6 +3837,7 @@ static int patch_alc269(struct hda_codec *codec) codec->patch_ops = alc_patch_ops; #ifdef CONFIG_PM + codec->patch_ops.suspend = alc269_suspend; codec->patch_ops.resume = alc269_resume; #endif spec->shutup = alc269_shutup; -- cgit v1.2.3 From 6c29d68a82ec68db18241b818d03e7864c052be9 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Fri, 28 Jun 2013 08:53:34 +0200 Subject: ALSA: hda - Yet another Dell headset mic quirk This quirk is needed for the headset mic to work on this Dell machine. BugLink: https://bugs.launchpad.net/bugs/1195597 Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index eeb6ecc31366..065718f0f702 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3601,6 +3601,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1028, 0x0606, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0608, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0609, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x0613, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2), SND_PCI_QUIRK(0x103c, 0x18e6, "HP", ALC269_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x1973, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1), -- cgit v1.2.3 From cd6fb6793a33e2b02af6e05a8d3f735f7c88a943 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Fri, 28 Jun 2013 11:09:56 +0200 Subject: ALSA: hda - Guess what, it's two more Dell headset mic quirks Add two more machines that need quirks for headset mics to work. Tested-by: Shawn Wang BugLink: https://bugs.launchpad.net/bugs/1195636 Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 065718f0f702..7d6a9f5d2b06 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3598,6 +3598,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1028, 0x05f5, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x05f6, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x05f8, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x05f9, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x05fb, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0606, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0608, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0609, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), -- cgit v1.2.3 From 0c055b3413868227f2e85701c4e6938c9581f0e2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 28 Jun 2013 11:51:32 +0200 Subject: ALSA: hda - Fix the max length of control name in generic parser add_control_with_pfx() in hda_generic.c assumes a shorter name string for the control element, and this resulted in the truncation of the long but valid string like "Headphone Surround Switch" in the middle. This patch aligns the max size to the actual limit of snd_ctl_elem_id, 44. Cc: [v3.9+] Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_generic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 1485d871d628..6460fc519d36 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -854,7 +854,7 @@ static int add_control_with_pfx(struct hda_gen_spec *spec, int type, const char *pfx, const char *dir, const char *sfx, int cidx, unsigned long val) { - char name[32]; + char name[44]; snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx); if (!add_control(spec, type, name, cidx, val)) return -ENOMEM; -- cgit v1.2.3 From 975cc02a904ae385721f1bdb65eb1bcf707dfaf1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 28 Jun 2013 11:56:49 +0200 Subject: ALSA: Replace the magic number 44 with const The char arrays with size 44 are for the name string of snd_ctl_elem_id. Define the constant and replace the raw numbers with it for clarifying better. Signed-off-by: Takashi Iwai --- include/uapi/sound/asound.h | 2 ++ sound/pci/ac97/ac97_codec.c | 2 +- sound/pci/asihpi/asihpi.c | 2 +- sound/pci/hda/hda_generic.c | 12 ++++++------ sound/pci/hda/hda_jack.c | 2 +- sound/pci/hda/patch_ca0132.c | 8 ++++---- 6 files changed, 15 insertions(+), 13 deletions(-) diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index e3983d508272..041203f20f6d 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -817,6 +817,8 @@ typedef int __bitwise snd_ctl_elem_iface_t; #define SNDRV_CTL_POWER_D3hot (SNDRV_CTL_POWER_D3|0x0000) /* Off, with power */ #define SNDRV_CTL_POWER_D3cold (SNDRV_CTL_POWER_D3|0x0001) /* Off, without power */ +#define SNDRV_CTL_ELEM_ID_NAME_MAXLEN 44 + struct snd_ctl_elem_id { unsigned int numid; /* numeric identifier, zero = invalid */ snd_ctl_elem_iface_t iface; /* interface identifier */ diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index d37c683cfd7a..445ca481d8d3 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -1296,7 +1296,7 @@ static int snd_ac97_cmix_new_stereo(struct snd_card *card, const char *pfx, struct snd_ac97 *ac97) { int err; - char name[44]; + char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; unsigned char lo_max, hi_max; if (! snd_ac97_valid_reg(ac97, reg)) diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index fbc17203613c..185d54a5cb1a 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -1278,7 +1278,7 @@ struct hpi_control { u16 dst_node_type; u16 dst_node_index; u16 band; - char name[44]; /* copied to snd_ctl_elem_id.name[44]; */ + char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; /* copied to snd_ctl_elem_id.name[44]; */ }; static const char * const asihpi_tuner_band_names[] = { diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 6460fc519d36..8e77cbbad871 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -854,7 +854,7 @@ static int add_control_with_pfx(struct hda_gen_spec *spec, int type, const char *pfx, const char *dir, const char *sfx, int cidx, unsigned long val) { - char name[44]; + char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx); if (!add_control(spec, type, name, cidx, val)) return -ENOMEM; @@ -1931,7 +1931,7 @@ static int create_extra_outs(struct hda_codec *codec, int num_pins, for (i = 0; i < num_pins; i++) { const char *name; - char tmp[44]; + char tmp[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; int err, idx = 0; if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker")) @@ -2484,7 +2484,7 @@ static int create_out_jack_modes(struct hda_codec *codec, int num_pins, } if (get_out_jack_num_items(codec, pin) > 1) { struct snd_kcontrol_new *knew; - char name[44]; + char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; get_jack_mode_name(codec, pin, name, sizeof(name)); knew = snd_hda_gen_add_kctl(spec, name, &out_jack_mode_enum); @@ -2616,7 +2616,7 @@ static int create_in_jack_mode(struct hda_codec *codec, hda_nid_t pin) { struct hda_gen_spec *spec = codec->spec; struct snd_kcontrol_new *knew; - char name[44]; + char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; unsigned int defcfg; if (pin == spec->hp_mic_pin) @@ -3316,7 +3316,7 @@ static int add_single_cap_ctl(struct hda_codec *codec, const char *label, bool inv_dmic) { struct hda_gen_spec *spec = codec->spec; - char tmpname[44]; + char tmpname[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; int type = is_switch ? HDA_CTL_WIDGET_MUTE : HDA_CTL_WIDGET_VOL; const char *sfx = is_switch ? "Switch" : "Volume"; unsigned int chs = inv_dmic ? 1 : 3; @@ -3578,7 +3578,7 @@ static int parse_mic_boost(struct hda_codec *codec) struct nid_path *path; unsigned int val; int idx; - char boost_label[44]; + char boost_label[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; idx = imux->items[i].index; if (idx >= imux->num_items) diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index 9e0a95288f46..3fd2973183e2 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -398,7 +398,7 @@ static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid, const char *base_name) { unsigned int def_conf, conn; - char name[44]; + char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; int idx, err; bool phantom_jack; diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 90ff7a3f72df..6e9876f27d95 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -139,7 +139,7 @@ enum { #define DSP_SPEAKER_OUT_LATENCY 7 struct ct_effect { - char name[44]; + char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; hda_nid_t nid; int mid; /*effect module ID*/ int reqs[EFFECT_VALS_MAX_COUNT]; /*effect module request*/ @@ -270,7 +270,7 @@ enum { }; struct ct_tuning_ctl { - char name[44]; + char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; hda_nid_t parent_nid; hda_nid_t nid; int mid; /*effect module ID*/ @@ -3103,7 +3103,7 @@ static int add_tuning_control(struct hda_codec *codec, hda_nid_t pnid, hda_nid_t nid, const char *name, int dir) { - char namestr[44]; + char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; int type = dir ? HDA_INPUT : HDA_OUTPUT; struct snd_kcontrol_new knew = HDA_CODEC_VOLUME_MONO(namestr, nid, 1, 0, type); @@ -3935,7 +3935,7 @@ static int ca0132_volume_tlv(struct snd_kcontrol *kcontrol, int op_flag, static int add_fx_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx, int dir) { - char namestr[44]; + char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; int type = dir ? HDA_INPUT : HDA_OUTPUT; struct snd_kcontrol_new knew = CA0132_CODEC_MUTE_MONO(namestr, nid, 1, type); -- cgit v1.2.3 From d482e5fa299c2cfbb4700143dd766273730e2357 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Fri, 28 Jun 2013 20:31:34 +1000 Subject: Revert "drm: kms_helper: don't lose hotplug event" This reverts commit 160954b7bca43da7cd3cfbce310e6df919a8216e. This was rearming the workqueue with a 0 timeout, causing a WARN_ON, and possible loop. Daniel writes: "I've looked a bit into this and I think we need to have a separate work struct for recovering these lost hotplug events since the continuous self-rearming case is a real risk (e.g. if a connector flip-flops all the time). At least I don't see a sane way to block out re-arming with the current code in a simple way. So reverting the offender seems like the right thing and I'll go back to the drawing board for 3.12." Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_crtc_helper.c | 32 +------------------------------- include/drm/drm_crtc.h | 1 - 2 files changed, 1 insertion(+), 32 deletions(-) diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index f6829ba58e86..738a4294d820 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -122,7 +122,6 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector, int count = 0; int mode_flags = 0; bool verbose_prune = true; - enum drm_connector_status old_status; DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id, drm_get_connector_name(connector)); @@ -138,32 +137,7 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector, if (connector->funcs->force) connector->funcs->force(connector); } else { - old_status = connector->status; - connector->status = connector->funcs->detect(connector, true); - - /* - * Normally either the driver's hpd code or the poll loop should - * pick up any changes and fire the hotplug event. But if - * userspace sneaks in a probe, we might miss a change. Hence - * check here, and if anything changed start the hotplug code. - */ - if (old_status != connector->status) { - DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %d to %d\n", - connector->base.id, - drm_get_connector_name(connector), - old_status, connector->status); - - /* - * The hotplug event code might call into the fb - * helpers, and so expects that we do not hold any - * locks. Fire up the poll struct instead, it will - * disable itself again. - */ - dev->mode_config.delayed_event = true; - schedule_delayed_work(&dev->mode_config.output_poll_work, - 0); - } } /* Re-enable polling in case the global poll config changed. */ @@ -1011,11 +985,7 @@ static void output_poll_execute(struct work_struct *work) struct drm_device *dev = container_of(delayed_work, struct drm_device, mode_config.output_poll_work); struct drm_connector *connector; enum drm_connector_status old_status; - bool repoll = false, changed; - - /* Pick up any changes detected by the probe functions. */ - changed = dev->mode_config.delayed_event; - dev->mode_config.delayed_event = false; + bool repoll = false, changed = false; if (!drm_kms_helper_poll) return; diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 663c3ab47752..fa12a2fa4293 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -811,7 +811,6 @@ struct drm_mode_config { /* output poll support */ bool poll_enabled; bool poll_running; - bool delayed_event; struct delayed_work output_poll_work; /* pointers to standard properties */ -- cgit v1.2.3 From 9b5c7a5a977a330ffaf83c4d383ba247c74c800f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 27 Jun 2013 14:01:02 +0200 Subject: ACPI / PM: Fix possible NULL pointer deref in acpi_pm_device_sleep_state() After commit fa1675b (ACPI / PM: Rework and clean up acpi_dev_pm_get_state()) a NULL pointer dereference will take place if NULL is passed to acpi_pm_device_sleep_state() as the second argument. Fix that by avoiding to use the pointer that may be NULL until it's necessary to store a return value at the location pointed to by it (if not NULL). Reported-and-tested-by: Aaron Lu Signed-off-by: Rafael J. Wysocki --- drivers/acpi/device_pm.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index fd363b57a596..4c56dc830ebc 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -521,7 +521,7 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p, int d_max_in) { acpi_handle handle = DEVICE_ACPI_HANDLE(dev); struct acpi_device *adev; - int ret, d_max; + int ret, d_min, d_max; if (d_max_in < ACPI_STATE_D0 || d_max_in > ACPI_STATE_D3_COLD) return -EINVAL; @@ -540,19 +540,23 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p, int d_max_in) } ret = acpi_dev_pm_get_state(dev, adev, acpi_target_system_state(), - d_min_p, &d_max); + &d_min, &d_max); if (ret) return ret; - if (d_max_in < *d_min_p) + if (d_max_in < d_min) return -EINVAL; if (d_max > d_max_in) { - for (d_max = d_max_in; d_max > *d_min_p; d_max--) { + for (d_max = d_max_in; d_max > d_min; d_max--) { if (adev->power.states[d_max].flags.valid) break; } } + + if (d_min_p) + *d_min_p = d_min; + return d_max; } EXPORT_SYMBOL(acpi_pm_device_sleep_state); -- cgit v1.2.3 From 9f24dc877093744c0db323cc3d8a9c82aa2af8a5 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 27 Jun 2013 21:59:10 +0200 Subject: ASoC: tas5086: fix TAS5086_CLOCK_CONTROL register size The TAS5086_CLOCK_CONTROL also has a size of 1 byte. Signed-off-by: Daniel Mack Signed-off-by: Mark Brown --- sound/soc/codecs/tas5086.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/tas5086.c b/sound/soc/codecs/tas5086.c index 72067f79633e..8bbdf25530ca 100644 --- a/sound/soc/codecs/tas5086.c +++ b/sound/soc/codecs/tas5086.c @@ -130,7 +130,7 @@ static const struct reg_default tas5086_reg_defaults[] = { static int tas5086_register_size(struct device *dev, unsigned int reg) { switch (reg) { - case TAS5086_DEV_ID ... TAS5086_BKNDERR: + case TAS5086_CLOCK_CONTROL ... TAS5086_BKNDERR: return 1; case TAS5086_INPUT_MUX: case TAS5086_PWM_OUTPUT_MUX: -- cgit v1.2.3 From a975873a9acc0788c1aee5ca183deb420b5c00e5 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 27 Jun 2013 21:59:11 +0200 Subject: ASoC: tas5086: fix Mid-Z implementation It turns out that the TAS5086 doesn't like channel start parts to be empty, and if all channels are configured to Mid-Z, part 1 has to be used. Signed-off-by: Daniel Mack Signed-off-by: Mark Brown --- sound/soc/codecs/tas5086.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/sound/soc/codecs/tas5086.c b/sound/soc/codecs/tas5086.c index 8bbdf25530ca..6d31d88f7204 100644 --- a/sound/soc/codecs/tas5086.c +++ b/sound/soc/codecs/tas5086.c @@ -721,7 +721,7 @@ static int tas5086_probe(struct snd_soc_codec *codec) { struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec); int charge_period = 1300000; /* hardware default is 1300 ms */ - u8 pwm_start = TAS5086_PWM_START_CHANNEL_MASK; + u8 pwm_start_mid_z = 0; int i, ret; if (of_match_device(of_match_ptr(tas5086_dt_ids), codec->dev)) { @@ -735,16 +735,19 @@ static int tas5086_probe(struct snd_soc_codec *codec) "ti,mid-z-channel-%d", i + 1); if (of_get_property(of_node, name, NULL) != NULL) - pwm_start &= ~(1 << i); + pwm_start_mid_z |= 1 << i; } } /* - * Configure 'part 2' of the PWM starts to always use MID-Z, and tell - * all configured mid-z channels to start start under 'part 2'. + * If any of the channels is configured to start in Mid-Z mode, + * configure 'part 1' of the PWM starts to use Mid-Z, and tell + * all configured mid-z channels to start start under 'part 1'. */ - regmap_write(priv->regmap, TAS5086_PWM_START, - TAS5086_PWM_START_MIDZ_FOR_START_2 | pwm_start); + if (pwm_start_mid_z) + regmap_write(priv->regmap, TAS5086_PWM_START, + TAS5086_PWM_START_MIDZ_FOR_START_1 | + pwm_start_mid_z); /* lookup and set split-capacitor charge period */ if (charge_period == 0) { -- cgit v1.2.3 From 066624c6a1733a72a67f1d06d35a2153e7d9082b Mon Sep 17 00:00:00 2001 From: Przemek Rudy Date: Thu, 27 Jun 2013 23:52:33 +0200 Subject: ALSA: usb-audio: Add Audio Advantage Micro II This patch is adding extensive support (beside standard usb audio class) for Audio Advantage Micro II usb sound card. Features included: - Access to AES bits (so now sending the IEC61937 compliant stream is possible). - Mixer SPDIF control added to turn on/off the optical transmitter. Signed-off-by: Przemek Rudy Signed-off-by: Takashi Iwai --- sound/usb/mixer_quirks.c | 212 +++++++++++++++++++++++++++++++++++++++++++++++ sound/usb/quirks-table.h | 12 +++ 2 files changed, 224 insertions(+) diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index ebe91440a068..d42a584cf829 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -9,6 +9,8 @@ * Alan Cox (alan@lxorguk.ukuu.org.uk) * Thomas Sailer (sailer@ife.ee.ethz.ch) * + * Audio Advantage Micro II support added by: + * Przemek Rudy (prudy1@o2.pl) * * 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 @@ -30,6 +32,7 @@ #include #include +#include #include #include #include @@ -1315,6 +1318,211 @@ static struct std_mono_table ebox44_table[] = { {} }; +/* Audio Advantage Micro II findings: + * + * Mapping spdif AES bits to vendor register.bit: + * AES0: [0 0 0 0 2.3 2.2 2.1 2.0] - default 0x00 + * AES1: [3.3 3.2.3.1.3.0 2.7 2.6 2.5 2.4] - default: 0x01 + * AES2: [0 0 0 0 0 0 0 0] + * AES3: [0 0 0 0 0 0 x 0] - 'x' bit is set basing on standard usb request + * (UAC_EP_CS_ATTR_SAMPLE_RATE) for Audio Devices + * + * power on values: + * r2: 0x10 + * r3: 0x20 (b7 is zeroed just before playback (except IEC61937) and set + * just after it to 0xa0, presumably it disables/mutes some analog + * parts when there is no audio.) + * r9: 0x28 + * + * Optical transmitter on/off: + * vendor register.bit: 9.1 + * 0 - on (0x28 register value) + * 1 - off (0x2a register value) + * + */ +static int snd_microii_spdif_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); + int err; + struct usb_interface *iface; + struct usb_host_interface *alts; + unsigned int ep; + unsigned char data[3]; + int rate; + + ucontrol->value.iec958.status[0] = kcontrol->private_value & 0xff; + ucontrol->value.iec958.status[1] = (kcontrol->private_value >> 8) & 0xff; + ucontrol->value.iec958.status[2] = 0x00; + + /* use known values for that card: interface#1 altsetting#1 */ + iface = usb_ifnum_to_if(mixer->chip->dev, 1); + alts = &iface->altsetting[1]; + ep = get_endpoint(alts, 0)->bEndpointAddress; + + err = snd_usb_ctl_msg(mixer->chip->dev, + usb_rcvctrlpipe(mixer->chip->dev, 0), + UAC_GET_CUR, + USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN, + UAC_EP_CS_ATTR_SAMPLE_RATE << 8, + ep, + data, + sizeof(data)); + if (err < 0) + goto end; + + rate = data[0] | (data[1] << 8) | (data[2] << 16); + ucontrol->value.iec958.status[3] = (rate == 48000) ? + IEC958_AES3_CON_FS_48000 : IEC958_AES3_CON_FS_44100; + + err = 0; +end: + return err; +} + +static int snd_microii_spdif_default_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); + int err; + u8 reg; + unsigned long priv_backup = kcontrol->private_value; + + reg = ((ucontrol->value.iec958.status[1] & 0x0f) << 4) | + (ucontrol->value.iec958.status[0] & 0x0f); + err = snd_usb_ctl_msg(mixer->chip->dev, + usb_sndctrlpipe(mixer->chip->dev, 0), + UAC_SET_CUR, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, + reg, + 2, + NULL, + 0); + if (err < 0) + goto end; + + kcontrol->private_value &= 0xfffff0f0; + kcontrol->private_value |= (ucontrol->value.iec958.status[1] & 0x0f) << 8; + kcontrol->private_value |= (ucontrol->value.iec958.status[0] & 0x0f); + + reg = (ucontrol->value.iec958.status[0] & IEC958_AES0_NONAUDIO) ? + 0xa0 : 0x20; + reg |= (ucontrol->value.iec958.status[1] >> 4) & 0x0f; + err = snd_usb_ctl_msg(mixer->chip->dev, + usb_sndctrlpipe(mixer->chip->dev, 0), + UAC_SET_CUR, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, + reg, + 3, + NULL, + 0); + if (err < 0) + goto end; + + kcontrol->private_value &= 0xffff0fff; + kcontrol->private_value |= (ucontrol->value.iec958.status[1] & 0xf0) << 8; + + /* The frequency bits in AES3 cannot be set via register access. */ + + /* Silently ignore any bits from the request that cannot be set. */ + + err = (priv_backup != kcontrol->private_value); +end: + return err; +} + +static int snd_microii_spdif_mask_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.iec958.status[0] = 0x0f; + ucontrol->value.iec958.status[1] = 0xff; + ucontrol->value.iec958.status[2] = 0x00; + ucontrol->value.iec958.status[3] = 0x00; + + return 0; +} + +static int snd_microii_spdif_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = !(kcontrol->private_value & 0x02); + + return 0; +} + +static int snd_microii_spdif_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); + int err; + u8 reg = ucontrol->value.integer.value[0] ? 0x28 : 0x2a; + + err = snd_usb_ctl_msg(mixer->chip->dev, + usb_sndctrlpipe(mixer->chip->dev, 0), + UAC_SET_CUR, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, + reg, + 9, + NULL, + 0); + + if (!err) { + err = (reg != (kcontrol->private_value & 0x0ff)); + if (err) + kcontrol->private_value = reg; + } + + return err; +} + +static struct snd_kcontrol_new snd_microii_mixer_spdif[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), + .info = snd_microii_spdif_info, + .get = snd_microii_spdif_default_get, + .put = snd_microii_spdif_default_put, + .private_value = 0x00000100UL,/* reset value */ + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK), + .info = snd_microii_spdif_info, + .get = snd_microii_spdif_mask_get, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, SWITCH), + .info = snd_ctl_boolean_mono_info, + .get = snd_microii_spdif_switch_get, + .put = snd_microii_spdif_switch_put, + .private_value = 0x00000028UL,/* reset value */ + } +}; + +static int snd_microii_controls_create(struct usb_mixer_interface *mixer) +{ + int err, i; + + for (i = 0; i < ARRAY_SIZE(snd_microii_mixer_spdif); ++i) { + err = snd_ctl_add(mixer->chip->card, + snd_ctl_new1(&snd_microii_mixer_spdif[i], mixer)); + if (err < 0) + return err; + } + + return err; +} + int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) { int err = 0; @@ -1353,6 +1561,10 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) err = snd_xonar_u1_controls_create(mixer); break; + case USB_ID(0x0d8c, 0x0103): /* Audio Advantage Micro II */ + err = snd_microii_controls_create(mixer); + break; + case USB_ID(0x17cc, 0x1011): /* Traktor Audio 6 */ err = snd_nativeinstruments_create_mixer(mixer, snd_nativeinstruments_ta6_mixers, diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 9c636c2d99f6..f5f0595ef9c7 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -3119,4 +3119,16 @@ YAMAHA_DEVICE(0x7010, "UB99"), } }, +{ + /* + * The original product_name is "USB Sound Device", however this name + * is also used by the CM106 based cards, so make it unique. + */ + USB_DEVICE(0x0d8c, 0x0103), + .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { + .product_name = "Audio Advantage MicroII", + .ifnum = QUIRK_NO_INTERFACE + } +}, + #undef USB_DEVICE_VENDOR_SPEC -- cgit v1.2.3 From 1586d80cbf25d5f82539fd243924763738803745 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 27 May 2013 15:00:43 +0900 Subject: drm/exynos: fix checks for valid mixer window Valid values for mixer window are from 0 to MIXER_WIN_NR-1 inclusive. Arrays in structures (e.g. mixer_context.win_data) have size of MIXER_WIN_NR so checks for wrong mixer window must be greater-equal. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Hyunhee Kim Signed-off-by: Seung-Woo Kim Signed-off-by: Inki Dae --- drivers/gpu/drm/exynos/exynos_drm_hdmi.c | 4 ++-- drivers/gpu/drm/exynos/exynos_mixer.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c index 437fb947e46d..b9b27265d0b1 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c @@ -344,7 +344,7 @@ static void drm_mixer_commit(struct device *subdrv_dev, int zpos) DRM_DEBUG_KMS("%s\n", __FILE__); - if (win < 0 || win > MIXER_WIN_NR) { + if (win < 0 || win >= MIXER_WIN_NR) { DRM_ERROR("mixer window[%d] is wrong\n", win); return; } @@ -362,7 +362,7 @@ static void drm_mixer_disable(struct device *subdrv_dev, int zpos) DRM_DEBUG_KMS("%s\n", __FILE__); - if (win < 0 || win > MIXER_WIN_NR) { + if (win < 0 || win >= MIXER_WIN_NR) { DRM_ERROR("mixer window[%d] is wrong\n", win); return; } diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index 7c197d3820c5..3658e0743487 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -742,7 +742,7 @@ static void mixer_win_mode_set(void *ctx, if (win == DEFAULT_ZPOS) win = MIXER_DEFAULT_WIN; - if (win < 0 || win > MIXER_WIN_NR) { + if (win < 0 || win >= MIXER_WIN_NR) { DRM_ERROR("mixer window[%d] is wrong\n", win); return; } -- cgit v1.2.3 From 37b006e88e090392cdb2787ef344193702c1d75b Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 27 May 2013 11:56:26 +0200 Subject: drm/exynos: fix tests for valid FIMD window number Valid values for FIMD windows are from 0 to WINDOWS_NR-1 inclusive (5 windows in total). The WINDOWS_NR is also a size of fimd_context.win_data array. However, early-return tests for wrong values of windows accepted a value of WINDOWS_NR which is out of bound for fimd_context.win_data. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Inki Dae --- drivers/gpu/drm/exynos/exynos_drm_fimd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index 97c61dbffd82..279c3f8c3d4e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -381,7 +381,7 @@ static void fimd_win_mode_set(struct device *dev, if (win == DEFAULT_ZPOS) win = ctx->default_win; - if (win < 0 || win > WINDOWS_NR) + if (win < 0 || win >= WINDOWS_NR) return; offset = overlay->fb_x * (overlay->bpp >> 3); @@ -506,7 +506,7 @@ static void fimd_win_commit(struct device *dev, int zpos) if (win == DEFAULT_ZPOS) win = ctx->default_win; - if (win < 0 || win > WINDOWS_NR) + if (win < 0 || win >= WINDOWS_NR) return; win_data = &ctx->win_data[win]; @@ -622,7 +622,7 @@ static void fimd_win_disable(struct device *dev, int zpos) if (win == DEFAULT_ZPOS) win = ctx->default_win; - if (win < 0 || win > WINDOWS_NR) + if (win < 0 || win >= WINDOWS_NR) return; win_data = &ctx->win_data[win]; -- cgit v1.2.3 From a7f98d6a929ff1cd8378522372f36faf8b9f9a37 Mon Sep 17 00:00:00 2001 From: Inki Dae Date: Tue, 28 May 2013 16:01:21 +0900 Subject: drm/exynos: fix WINDOWS_NR checking to vidi driver This patch just checks if win_data array range is valid or not correctly. Signed-off-by: Inki Dae Signed-off-by: Kyungmin Park --- drivers/gpu/drm/exynos/exynos_drm_vidi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c index 24376c194a5e..11a016d49a44 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c @@ -282,7 +282,7 @@ static void vidi_win_mode_set(struct device *dev, if (win == DEFAULT_ZPOS) win = ctx->default_win; - if (win < 0 || win > WINDOWS_NR) + if (win < 0 || win >= WINDOWS_NR) return; offset = overlay->fb_x * (overlay->bpp >> 3); @@ -332,7 +332,7 @@ static void vidi_win_commit(struct device *dev, int zpos) if (win == DEFAULT_ZPOS) win = ctx->default_win; - if (win < 0 || win > WINDOWS_NR) + if (win < 0 || win >= WINDOWS_NR) return; win_data = &ctx->win_data[win]; @@ -356,7 +356,7 @@ static void vidi_win_disable(struct device *dev, int zpos) if (win == DEFAULT_ZPOS) win = ctx->default_win; - if (win < 0 || win > WINDOWS_NR) + if (win < 0 || win >= WINDOWS_NR) return; win_data = &ctx->win_data[win]; -- cgit v1.2.3 From ad07945a857858598fd5e3b24d226a3b501d5375 Mon Sep 17 00:00:00 2001 From: Seung-Woo Kim Date: Wed, 5 Jun 2013 14:34:38 +0900 Subject: drm/exynos: remove ignoring return value warning in hdmi The definition of regulator_bulk_enable is fixed with __must_check and this causes following build warning. warning: ignoring return value of 'regulator_bulk_enable', declared with attribute warn_unused_result This patch fixes to check return value of the function. Signed-off-by: Seung-Woo Kim Signed-off-by: Inki Dae --- drivers/gpu/drm/exynos/exynos_hdmi.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index fd1426dca882..4dfe82974ce7 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -1699,7 +1699,9 @@ static void hdmi_poweron(struct hdmi_context *hdata) mutex_unlock(&hdata->hdmi_mutex); - regulator_bulk_enable(res->regul_count, res->regul_bulk); + if (regulator_bulk_enable(res->regul_count, res->regul_bulk)) + DRM_DEBUG_KMS("failed to enable regulator bulk\n"); + clk_enable(res->hdmiphy); clk_enable(res->hdmi); clk_enable(res->sclk_hdmi); -- cgit v1.2.3 From e436b09dc5fa33d36f4906f4556c4f543afd4b65 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 5 Jun 2013 16:00:23 +0900 Subject: drm/exynos: Remove redundant use of of_match_ptr macro 'mixer_match_types' is always compiled in. Hence of_match_ptr is not necessary. Signed-off-by: Sachin Kamat Signed-off-by: Inki Dae --- drivers/gpu/drm/exynos/exynos_mixer.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index 3658e0743487..de5c735c8223 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -1186,8 +1186,7 @@ static int mixer_probe(struct platform_device *pdev) if (dev->of_node) { const struct of_device_id *match; - match = of_match_node(of_match_ptr(mixer_match_types), - dev->of_node); + match = of_match_node(mixer_match_types, dev->of_node); drv = (struct mixer_drv_data *)match->data; } else { drv = (struct mixer_drv_data *) -- cgit v1.2.3 From 7fd65df155ebcd3684537c1e8f738b98ae595e28 Mon Sep 17 00:00:00 2001 From: Inki Dae Date: Sun, 12 May 2013 16:09:33 +0900 Subject: drm/exynos: do not use mode_set_base function directly This patch adds exynos_drm_crtc_mode_set_commit function to update mode data and it makes page flip call this function instead of calling exynos_drm_crtc_mode_set_base function directly. exynos_drm_crtc_mode_set_base function is called by drm subsystem as a callback so we don't have to call this function directly. Signed-off-by: Inki Dae Signed-off-by: Kyungmin Park --- drivers/gpu/drm/exynos/exynos_drm_crtc.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index 073c10a35b2a..a143605884a4 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -139,7 +139,7 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, return 0; } -static int exynos_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, +static int exynos_drm_crtc_mode_set_commit(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb) { struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); @@ -169,6 +169,12 @@ static int exynos_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, return 0; } +static int exynos_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, + struct drm_framebuffer *old_fb) +{ + return exynos_drm_crtc_mode_set_commit(crtc, x, y, old_fb); +} + static void exynos_drm_crtc_disable(struct drm_crtc *crtc) { struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); @@ -230,7 +236,7 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc, spin_unlock_irq(&dev->event_lock); crtc->fb = fb; - ret = exynos_drm_crtc_mode_set_base(crtc, crtc->x, crtc->y, + ret = exynos_drm_crtc_mode_set_commit(crtc, crtc->x, crtc->y, NULL); if (ret) { crtc->fb = old_fb; -- cgit v1.2.3 From 188734653ca7eea1b01b214565b1990caf6eb84e Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Wed, 1 May 2013 21:02:26 +0200 Subject: drm/exynos: fimd: Hold pointer to driver data in context struct This patch adds pointer to driver data to fimd_context structure, to remove the need to call drm_fimd_get_driver_data() each time access to driver data is necessary. Signed-off-by: Tomasz Figa Acked-by: Joonyoung Shim Signed-off-by: Inki Dae --- drivers/gpu/drm/exynos/exynos_drm_fimd.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index 279c3f8c3d4e..de3367078cf8 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -107,6 +107,7 @@ struct fimd_context { atomic_t wait_vsync_event; struct exynos_drm_panel_info *panel; + struct fimd_driver_data *driver_data; }; #ifdef CONFIG_OF @@ -239,10 +240,9 @@ static void fimd_commit(struct device *dev) struct exynos_drm_panel_info *panel = ctx->panel; struct fb_videomode *timing = &panel->timing; struct fimd_driver_data *driver_data; - struct platform_device *pdev = to_platform_device(dev); u32 val; - driver_data = drm_fimd_get_driver_data(pdev); + driver_data = ctx->driver_data; if (ctx->suspended) return; @@ -949,6 +949,7 @@ static int fimd_probe(struct platform_device *pdev) return ret; } + ctx->driver_data = drm_fimd_get_driver_data(pdev); ctx->vidcon0 = pdata->vidcon0; ctx->vidcon1 = pdata->vidcon1; ctx->default_win = pdata->default_win; -- cgit v1.2.3 From de7af1004bc74522d31675fdabdff432d2ddd986 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Wed, 1 May 2013 21:02:27 +0200 Subject: drm/exynos: fimd: Add support for FIMD versions without SHADOWCON register Some platforms that can be supported with this driver have PRTCON register instead of SHADOWCON, which requires slightly different handling. This patch factors out all register shadow control code from the driver and adds a function to control register shadowing appropriately, depending on driver data. Signed-off-by: Tomasz Figa Acked-by: Joonyoung Shim Signed-off-by: Inki Dae --- drivers/gpu/drm/exynos/exynos_drm_fimd.c | 69 +++++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index de3367078cf8..015a3be0b06e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -63,14 +63,18 @@ struct fimd_driver_data { unsigned int timing_base; + + unsigned int has_shadowcon:1; }; static struct fimd_driver_data exynos4_fimd_driver_data = { .timing_base = 0x0, + .has_shadowcon = 1, }; static struct fimd_driver_data exynos5_fimd_driver_data = { .timing_base = 0x20000, + .has_shadowcon = 1, }; struct fimd_win_data { @@ -489,6 +493,33 @@ static void fimd_win_set_colkey(struct device *dev, unsigned int win) writel(keycon1, ctx->regs + WKEYCON1_BASE(win)); } +/** + * shadow_protect_win() - disable updating values from shadow registers at vsync + * + * @win: window to protect registers for + * @protect: 1 to protect (disable updates) + */ +static void fimd_shadow_protect_win(struct fimd_context *ctx, + int win, bool protect) +{ + u32 reg, bits, val; + + if (ctx->driver_data->has_shadowcon) { + reg = SHADOWCON; + bits = SHADOWCON_WINx_PROTECT(win); + } else { + reg = PRTCON; + bits = PRTCON_PROTECT; + } + + val = readl(ctx->regs + reg); + if (protect) + val |= bits; + else + val &= ~bits; + writel(val, ctx->regs + reg); +} + static void fimd_win_commit(struct device *dev, int zpos) { struct fimd_context *ctx = get_fimd_context(dev); @@ -512,7 +543,7 @@ static void fimd_win_commit(struct device *dev, int zpos) win_data = &ctx->win_data[win]; /* - * SHADOWCON register is used for enabling timing. + * SHADOWCON/PRTCON register is used for enabling timing. * * for example, once only width value of a register is set, * if the dma is started then fimd hardware could malfunction so @@ -522,9 +553,7 @@ static void fimd_win_commit(struct device *dev, int zpos) */ /* protect windows */ - val = readl(ctx->regs + SHADOWCON); - val |= SHADOWCON_WINx_PROTECT(win); - writel(val, ctx->regs + SHADOWCON); + fimd_shadow_protect_win(ctx, win, true); /* buffer start address */ val = (unsigned long)win_data->dma_addr; @@ -602,10 +631,13 @@ static void fimd_win_commit(struct device *dev, int zpos) writel(val, ctx->regs + WINCON(win)); /* Enable DMA channel and unprotect windows */ - val = readl(ctx->regs + SHADOWCON); - val |= SHADOWCON_CHx_ENABLE(win); - val &= ~SHADOWCON_WINx_PROTECT(win); - writel(val, ctx->regs + SHADOWCON); + fimd_shadow_protect_win(ctx, win, false); + + if (ctx->driver_data->has_shadowcon) { + val = readl(ctx->regs + SHADOWCON); + val |= SHADOWCON_CHx_ENABLE(win); + writel(val, ctx->regs + SHADOWCON); + } win_data->enabled = true; } @@ -634,9 +666,7 @@ static void fimd_win_disable(struct device *dev, int zpos) } /* protect windows */ - val = readl(ctx->regs + SHADOWCON); - val |= SHADOWCON_WINx_PROTECT(win); - writel(val, ctx->regs + SHADOWCON); + fimd_shadow_protect_win(ctx, win, true); /* wincon */ val = readl(ctx->regs + WINCON(win)); @@ -644,10 +674,13 @@ static void fimd_win_disable(struct device *dev, int zpos) writel(val, ctx->regs + WINCON(win)); /* unprotect windows */ - val = readl(ctx->regs + SHADOWCON); - val &= ~SHADOWCON_CHx_ENABLE(win); - val &= ~SHADOWCON_WINx_PROTECT(win); - writel(val, ctx->regs + SHADOWCON); + if (ctx->driver_data->has_shadowcon) { + val = readl(ctx->regs + SHADOWCON); + val &= ~SHADOWCON_CHx_ENABLE(win); + writel(val, ctx->regs + SHADOWCON); + } + + fimd_shadow_protect_win(ctx, win, false); win_data->enabled = false; } @@ -777,8 +810,6 @@ static int fimd_calc_clkdiv(struct fimd_context *ctx, static void fimd_clear_win(struct fimd_context *ctx, int win) { - u32 val; - DRM_DEBUG_KMS("%s\n", __FILE__); writel(0, ctx->regs + WINCON(win)); @@ -789,9 +820,7 @@ static void fimd_clear_win(struct fimd_context *ctx, int win) if (win == 1 || win == 2) writel(0, ctx->regs + VIDOSD_D(win)); - val = readl(ctx->regs + SHADOWCON); - val &= ~SHADOWCON_WINx_PROTECT(win); - writel(val, ctx->regs + SHADOWCON); + fimd_shadow_protect_win(ctx, win, false); } static int fimd_clock(struct fimd_context *ctx, bool enable) -- cgit v1.2.3 From 411d9ed4486a4e40bded42b2e026ba3ce866891f Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Wed, 1 May 2013 21:02:28 +0200 Subject: drm/exynos: fimd: Add support for FIMD variants with clock selection Some platforms that can be supported this driver has additional clock source selection bits in VIDCON0 register that allows to select which clock should be used to drive the pixel clock: bus clock or special clock. Since this driver assumes that special clock always drives the pixel clock, this patch sets the selection bitfield to use the special clock. Signed-off-by: Tomasz Figa Acked-by: Joonyoung Shim Signed-off-by: Inki Dae --- drivers/gpu/drm/exynos/exynos_drm_fimd.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index 015a3be0b06e..7681a8afa6ed 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -65,6 +65,7 @@ struct fimd_driver_data { unsigned int timing_base; unsigned int has_shadowcon:1; + unsigned int has_clksel:1; }; static struct fimd_driver_data exynos4_fimd_driver_data = { @@ -278,6 +279,11 @@ static void fimd_commit(struct device *dev) val = ctx->vidcon0; val &= ~(VIDCON0_CLKVAL_F_MASK | VIDCON0_CLKDIR); + if (ctx->driver_data->has_clksel) { + val &= ~VIDCON0_CLKSEL_MASK; + val |= VIDCON0_CLKSEL_LCD; + } + if (ctx->clkdiv > 1) val |= VIDCON0_CLKVAL_F(ctx->clkdiv - 1) | VIDCON0_CLKDIR; else -- cgit v1.2.3 From 725ddead50b225997406613f3323ba1df8ed5433 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Wed, 1 May 2013 21:02:29 +0200 Subject: drm/exynos: fimd: Add support for S3C64xx SoCs The FIMD block present on S3C6400/S3C6410 SoCs is compatible with this driver, so it can be supported by it as well. This patch adds appropriate device IDs and driver data to enable this driver for S3C64xx SoCs. Signed-off-by: Tomasz Figa Acked-by: Joonyoung Shim Signed-off-by: Inki Dae --- drivers/gpu/drm/exynos/exynos_drm_fimd.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index 7681a8afa6ed..3194107f7025 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -68,6 +68,11 @@ struct fimd_driver_data { unsigned int has_clksel:1; }; +static struct fimd_driver_data s3c64xx_fimd_driver_data = { + .timing_base = 0x0, + .has_clksel = 1, +}; + static struct fimd_driver_data exynos4_fimd_driver_data = { .timing_base = 0x0, .has_shadowcon = 1, @@ -117,6 +122,8 @@ struct fimd_context { #ifdef CONFIG_OF static const struct of_device_id fimd_driver_dt_match[] = { + { .compatible = "samsung,s3c6400-fimd", + .data = &s3c64xx_fimd_driver_data }, { .compatible = "samsung,exynos4210-fimd", .data = &exynos4_fimd_driver_data }, { .compatible = "samsung,exynos5250-fimd", @@ -1108,6 +1115,9 @@ static int fimd_runtime_resume(struct device *dev) static struct platform_device_id fimd_driver_ids[] = { { + .name = "s3c64xx-fb", + .driver_data = (unsigned long)&s3c64xx_fimd_driver_data, + }, { .name = "exynos4-fb", .driver_data = (unsigned long)&exynos4_fimd_driver_data, }, { -- cgit v1.2.3 From 16844fb1e612e44cdda7043238230b12bdb68437 Mon Sep 17 00:00:00 2001 From: Rahul Sharma Date: Mon, 10 Jun 2013 14:50:00 +0530 Subject: drm/exynos: hdmi: use drm_display_mode to check the supported modes This patch renames check_timing to check_mode and removes the unnecessary conversion of drm_display_mode to/from fb_videomode in the hdmi driver. v4: 1) Changed the commit message to add information related to renaming the callbacks to check_mode. 2) Changed debug message to print 1/0 for interlace mode. v3: 1) Replaced check_timing callbacks with check_mode. 2) Change the type of second parameter of check_mode callback from void pointer paramenter to struct drm_display_mode pointer. v2: 1) Removed convert_to_video_timing(). 2) Corrected DRM_DEBUG_KMS to print the resolution properly. Signed-off-by: Rahul Sharma Signed-off-by: Inki Dae --- drivers/gpu/drm/exynos/exynos_drm_connector.c | 38 ++------------------------- drivers/gpu/drm/exynos/exynos_drm_drv.h | 4 +-- drivers/gpu/drm/exynos/exynos_drm_fimd.c | 4 +-- drivers/gpu/drm/exynos/exynos_drm_hdmi.c | 17 ++++++------ drivers/gpu/drm/exynos/exynos_drm_hdmi.h | 6 ++--- drivers/gpu/drm/exynos/exynos_drm_vidi.c | 4 +-- drivers/gpu/drm/exynos/exynos_hdmi.c | 29 ++++++++++---------- drivers/gpu/drm/exynos/exynos_mixer.c | 15 +++++------ 8 files changed, 41 insertions(+), 76 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos_drm_connector.c b/drivers/gpu/drm/exynos/exynos_drm_connector.c index 8bcc13ac9f73..ab3c6d41f5d9 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_connector.c +++ b/drivers/gpu/drm/exynos/exynos_drm_connector.c @@ -58,37 +58,6 @@ convert_to_display_mode(struct drm_display_mode *mode, mode->flags |= DRM_MODE_FLAG_DBLSCAN; } -/* convert drm_display_mode to exynos_video_timings */ -static inline void -convert_to_video_timing(struct fb_videomode *timing, - struct drm_display_mode *mode) -{ - DRM_DEBUG_KMS("%s\n", __FILE__); - - memset(timing, 0, sizeof(*timing)); - - timing->pixclock = mode->clock * 1000; - timing->refresh = drm_mode_vrefresh(mode); - - timing->xres = mode->hdisplay; - timing->right_margin = mode->hsync_start - mode->hdisplay; - timing->hsync_len = mode->hsync_end - mode->hsync_start; - timing->left_margin = mode->htotal - mode->hsync_end; - - timing->yres = mode->vdisplay; - timing->lower_margin = mode->vsync_start - mode->vdisplay; - timing->vsync_len = mode->vsync_end - mode->vsync_start; - timing->upper_margin = mode->vtotal - mode->vsync_end; - - if (mode->flags & DRM_MODE_FLAG_INTERLACE) - timing->vmode = FB_VMODE_INTERLACED; - else - timing->vmode = FB_VMODE_NONINTERLACED; - - if (mode->flags & DRM_MODE_FLAG_DBLSCAN) - timing->vmode |= FB_VMODE_DOUBLE; -} - static int exynos_drm_connector_get_modes(struct drm_connector *connector) { struct exynos_drm_connector *exynos_connector = @@ -168,15 +137,12 @@ static int exynos_drm_connector_mode_valid(struct drm_connector *connector, to_exynos_connector(connector); struct exynos_drm_manager *manager = exynos_connector->manager; struct exynos_drm_display_ops *display_ops = manager->display_ops; - struct fb_videomode timing; int ret = MODE_BAD; DRM_DEBUG_KMS("%s\n", __FILE__); - convert_to_video_timing(&timing, mode); - - if (display_ops && display_ops->check_timing) - if (!display_ops->check_timing(manager->dev, (void *)&timing)) + if (display_ops && display_ops->check_mode) + if (!display_ops->check_mode(manager->dev, mode)) ret = MODE_OK; return ret; diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index 680a7c1b9dea..eaa19668bf00 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -142,7 +142,7 @@ struct exynos_drm_overlay { * @is_connected: check for that display is connected or not. * @get_edid: get edid modes from display driver. * @get_panel: get panel object from display driver. - * @check_timing: check if timing is valid or not. + * @check_mode: check if mode is valid or not. * @power_on: display device on or off. */ struct exynos_drm_display_ops { @@ -151,7 +151,7 @@ struct exynos_drm_display_ops { struct edid *(*get_edid)(struct device *dev, struct drm_connector *connector); void *(*get_panel)(struct device *dev); - int (*check_timing)(struct device *dev, void *timing); + int (*check_mode)(struct device *dev, struct drm_display_mode *mode); int (*power_on)(struct device *dev, int mode); }; diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index 3194107f7025..0939e4652706 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -166,7 +166,7 @@ static void *fimd_get_panel(struct device *dev) return ctx->panel; } -static int fimd_check_timing(struct device *dev, void *timing) +static int fimd_check_mode(struct device *dev, struct drm_display_mode *mode) { DRM_DEBUG_KMS("%s\n", __FILE__); @@ -188,7 +188,7 @@ static struct exynos_drm_display_ops fimd_display_ops = { .type = EXYNOS_DISPLAY_TYPE_LCD, .is_connected = fimd_display_is_connected, .get_panel = fimd_get_panel, - .check_timing = fimd_check_timing, + .check_mode = fimd_check_mode, .power_on = fimd_display_power_on, }; diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c index b9b27265d0b1..59acdc0017a7 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c @@ -127,7 +127,8 @@ static struct edid *drm_hdmi_get_edid(struct device *dev, return NULL; } -static int drm_hdmi_check_timing(struct device *dev, void *timing) +static int drm_hdmi_check_mode(struct device *dev, + struct drm_display_mode *mode) { struct drm_hdmi_context *ctx = to_context(dev); int ret = 0; @@ -139,14 +140,14 @@ static int drm_hdmi_check_timing(struct device *dev, void *timing) * If any of the two fails, return mode as BAD. */ - if (mixer_ops && mixer_ops->check_timing) - ret = mixer_ops->check_timing(ctx->mixer_ctx->ctx, timing); + if (mixer_ops && mixer_ops->check_mode) + ret = mixer_ops->check_mode(ctx->mixer_ctx->ctx, mode); if (ret) return ret; - if (hdmi_ops && hdmi_ops->check_timing) - return hdmi_ops->check_timing(ctx->hdmi_ctx->ctx, timing); + if (hdmi_ops && hdmi_ops->check_mode) + return hdmi_ops->check_mode(ctx->hdmi_ctx->ctx, mode); return 0; } @@ -167,7 +168,7 @@ static struct exynos_drm_display_ops drm_hdmi_display_ops = { .type = EXYNOS_DISPLAY_TYPE_HDMI, .is_connected = drm_hdmi_is_connected, .get_edid = drm_hdmi_get_edid, - .check_timing = drm_hdmi_check_timing, + .check_mode = drm_hdmi_check_mode, .power_on = drm_hdmi_power_on, }; @@ -218,7 +219,7 @@ static void drm_hdmi_mode_fixup(struct device *subdrv_dev, drm_mode_set_crtcinfo(adjusted_mode, 0); - mode_ok = drm_hdmi_check_timing(subdrv_dev, adjusted_mode); + mode_ok = drm_hdmi_check_mode(subdrv_dev, adjusted_mode); /* just return if user desired mode exists. */ if (mode_ok == 0) @@ -229,7 +230,7 @@ static void drm_hdmi_mode_fixup(struct device *subdrv_dev, * to adjusted_mode. */ list_for_each_entry(m, &connector->modes, head) { - mode_ok = drm_hdmi_check_timing(subdrv_dev, m); + mode_ok = drm_hdmi_check_mode(subdrv_dev, m); if (mode_ok == 0) { struct drm_mode_object base; diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.h b/drivers/gpu/drm/exynos/exynos_drm_hdmi.h index 6b709440df4c..724cab181976 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.h +++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.h @@ -32,11 +32,11 @@ struct exynos_hdmi_ops { bool (*is_connected)(void *ctx); struct edid *(*get_edid)(void *ctx, struct drm_connector *connector); - int (*check_timing)(void *ctx, struct fb_videomode *timing); + int (*check_mode)(void *ctx, struct drm_display_mode *mode); int (*power_on)(void *ctx, int mode); /* manager */ - void (*mode_set)(void *ctx, void *mode); + void (*mode_set)(void *ctx, struct drm_display_mode *mode); void (*get_max_resol)(void *ctx, unsigned int *width, unsigned int *height); void (*commit)(void *ctx); @@ -57,7 +57,7 @@ struct exynos_mixer_ops { void (*win_disable)(void *ctx, int zpos); /* display */ - int (*check_timing)(void *ctx, struct fb_videomode *timing); + int (*check_mode)(void *ctx, struct drm_display_mode *mode); }; void exynos_hdmi_drv_attach(struct exynos_drm_hdmi_context *ctx); diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c index 11a016d49a44..294ba35efa5b 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c @@ -135,7 +135,7 @@ static void *vidi_get_panel(struct device *dev) return NULL; } -static int vidi_check_timing(struct device *dev, void *timing) +static int vidi_check_mode(struct device *dev, struct drm_display_mode *mode) { DRM_DEBUG_KMS("%s\n", __FILE__); @@ -158,7 +158,7 @@ static struct exynos_drm_display_ops vidi_display_ops = { .is_connected = vidi_display_is_connected, .get_edid = vidi_get_edid, .get_panel = vidi_get_panel, - .check_timing = vidi_check_timing, + .check_mode = vidi_check_mode, .power_on = vidi_display_power_on, }; diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 4dfe82974ce7..04255feaeaed 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -796,18 +796,17 @@ static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock) return -EINVAL; } -static int hdmi_check_timing(void *ctx, struct fb_videomode *timing) +static int hdmi_check_mode(void *ctx, struct drm_display_mode *mode) { struct hdmi_context *hdata = ctx; int ret; - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - - DRM_DEBUG_KMS("[%d]x[%d] [%d]Hz [%x]\n", timing->xres, - timing->yres, timing->refresh, - timing->vmode); + DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n", + mode->hdisplay, mode->vdisplay, mode->vrefresh, + (mode->flags & DRM_MODE_FLAG_INTERLACE) ? true : + false, mode->clock * 1000); - ret = hdmi_find_phy_conf(hdata, timing->pixclock); + ret = hdmi_find_phy_conf(hdata, mode->clock * 1000); if (ret < 0) return ret; return 0; @@ -1042,7 +1041,7 @@ static void hdmi_conf_init(struct hdmi_context *hdata) } } -static void hdmi_v13_timing_apply(struct hdmi_context *hdata) +static void hdmi_v13_mode_apply(struct hdmi_context *hdata) { const struct hdmi_tg_regs *tg = &hdata->mode_conf.conf.v13_conf.tg; const struct hdmi_v13_core_regs *core = @@ -1131,7 +1130,7 @@ static void hdmi_v13_timing_apply(struct hdmi_context *hdata) hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN); } -static void hdmi_v14_timing_apply(struct hdmi_context *hdata) +static void hdmi_v14_mode_apply(struct hdmi_context *hdata) { const struct hdmi_tg_regs *tg = &hdata->mode_conf.conf.v14_conf.tg; const struct hdmi_v14_core_regs *core = @@ -1298,12 +1297,12 @@ static void hdmi_v14_timing_apply(struct hdmi_context *hdata) hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN); } -static void hdmi_timing_apply(struct hdmi_context *hdata) +static void hdmi_mode_apply(struct hdmi_context *hdata) { if (hdata->type == HDMI_TYPE13) - hdmi_v13_timing_apply(hdata); + hdmi_v13_mode_apply(hdata); else - hdmi_v14_timing_apply(hdata); + hdmi_v14_mode_apply(hdata); } static void hdmiphy_conf_reset(struct hdmi_context *hdata) @@ -1423,7 +1422,7 @@ static void hdmi_conf_apply(struct hdmi_context *hdata) hdmi_audio_init(hdata); /* setting core registers */ - hdmi_timing_apply(hdata); + hdmi_mode_apply(hdata); hdmi_audio_control(hdata, true); hdmi_regs_dump(hdata, "start"); @@ -1642,7 +1641,7 @@ static void hdmi_v14_mode_set(struct hdmi_context *hdata, hdmi_set_reg(tg->tg_3d, 1, 0x0); } -static void hdmi_mode_set(void *ctx, void *mode) +static void hdmi_mode_set(void *ctx, struct drm_display_mode *mode) { struct hdmi_context *hdata = ctx; struct drm_display_mode *m = mode; @@ -1767,7 +1766,7 @@ static struct exynos_hdmi_ops hdmi_ops = { /* display */ .is_connected = hdmi_is_connected, .get_edid = hdmi_get_edid, - .check_timing = hdmi_check_timing, + .check_mode = hdmi_check_mode, /* manager */ .mode_set = hdmi_mode_set, diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index de5c735c8223..b0882b31353e 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -820,17 +820,16 @@ static void mixer_win_disable(void *ctx, int win) mixer_ctx->win_data[win].enabled = false; } -static int mixer_check_timing(void *ctx, struct fb_videomode *timing) +static int mixer_check_mode(void *ctx, struct drm_display_mode *mode) { u32 w, h; - w = timing->xres; - h = timing->yres; + w = mode->hdisplay; + h = mode->vdisplay; - DRM_DEBUG_KMS("%s : xres=%d, yres=%d, refresh=%d, intl=%d\n", - __func__, timing->xres, timing->yres, - timing->refresh, (timing->vmode & - FB_VMODE_INTERLACED) ? true : false); + DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d\n", + mode->hdisplay, mode->vdisplay, mode->vrefresh, + (mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0); if ((w >= 464 && w <= 720 && h >= 261 && h <= 576) || (w >= 1024 && w <= 1280 && h >= 576 && h <= 720) || @@ -978,7 +977,7 @@ static struct exynos_mixer_ops mixer_ops = { .win_disable = mixer_win_disable, /* display */ - .check_timing = mixer_check_timing, + .check_mode = mixer_check_mode, }; static irqreturn_t mixer_irq_handler(int irq, void *arg) -- cgit v1.2.3 From bca34c9a40e503e9bc6bafa45819dd55c2fd3e20 Mon Sep 17 00:00:00 2001 From: YoungJun Cho Date: Wed, 12 Jun 2013 10:40:52 +0900 Subject: drm/exynos: Remove tracking log functions This patch removes tracking log functions which were used to debug in the early development stage and are not so important as were. So remove them for code clean up. Signed-off-by: YoungJun Cho Signed-off-by: Seung-Woo Kim Signed-off-by: Inki Dae --- drivers/gpu/drm/exynos/exynos_drm_buf.c | 7 ---- drivers/gpu/drm/exynos/exynos_drm_connector.c | 13 ------- drivers/gpu/drm/exynos/exynos_drm_core.c | 12 ------- drivers/gpu/drm/exynos/exynos_drm_crtc.c | 28 --------------- drivers/gpu/drm/exynos/exynos_drm_dmabuf.c | 6 ---- drivers/gpu/drm/exynos/exynos_drm_drv.c | 20 ----------- drivers/gpu/drm/exynos/exynos_drm_encoder.c | 28 +-------------- drivers/gpu/drm/exynos/exynos_drm_fb.c | 10 ------ drivers/gpu/drm/exynos/exynos_drm_fbdev.c | 8 ----- drivers/gpu/drm/exynos/exynos_drm_fimc.c | 16 --------- drivers/gpu/drm/exynos/exynos_drm_fimd.c | 46 +----------------------- drivers/gpu/drm/exynos/exynos_drm_gem.c | 23 ------------ drivers/gpu/drm/exynos/exynos_drm_gsc.c | 12 +------ drivers/gpu/drm/exynos/exynos_drm_hdmi.c | 42 ---------------------- drivers/gpu/drm/exynos/exynos_drm_ipp.c | 52 --------------------------- drivers/gpu/drm/exynos/exynos_drm_plane.c | 16 --------- drivers/gpu/drm/exynos/exynos_drm_rotator.c | 12 ------- drivers/gpu/drm/exynos/exynos_drm_vidi.c | 40 +-------------------- drivers/gpu/drm/exynos/exynos_hdmi.c | 30 ---------------- drivers/gpu/drm/exynos/exynos_mixer.c | 20 ----------- 20 files changed, 4 insertions(+), 437 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos_drm_buf.c b/drivers/gpu/drm/exynos/exynos_drm_buf.c index 57affae9568b..22865baa03b1 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_buf.c +++ b/drivers/gpu/drm/exynos/exynos_drm_buf.c @@ -24,8 +24,6 @@ static int lowlevel_buffer_allocate(struct drm_device *dev, enum dma_attr attr; unsigned int nr_pages; - DRM_DEBUG_KMS("%s\n", __FILE__); - if (buf->dma_addr) { DRM_DEBUG_KMS("already allocated.\n"); return 0; @@ -119,8 +117,6 @@ err_free_attrs: static void lowlevel_buffer_deallocate(struct drm_device *dev, unsigned int flags, struct exynos_drm_gem_buf *buf) { - DRM_DEBUG_KMS("%s.\n", __FILE__); - if (!buf->dma_addr) { DRM_DEBUG_KMS("dma_addr is invalid.\n"); return; @@ -151,7 +147,6 @@ struct exynos_drm_gem_buf *exynos_drm_init_buf(struct drm_device *dev, { struct exynos_drm_gem_buf *buffer; - DRM_DEBUG_KMS("%s.\n", __FILE__); DRM_DEBUG_KMS("desired size = 0x%x\n", size); buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); @@ -167,8 +162,6 @@ struct exynos_drm_gem_buf *exynos_drm_init_buf(struct drm_device *dev, void exynos_drm_fini_buf(struct drm_device *dev, struct exynos_drm_gem_buf *buffer) { - DRM_DEBUG_KMS("%s.\n", __FILE__); - if (!buffer) { DRM_DEBUG_KMS("buffer is null.\n"); return; diff --git a/drivers/gpu/drm/exynos/exynos_drm_connector.c b/drivers/gpu/drm/exynos/exynos_drm_connector.c index ab3c6d41f5d9..02a8bc5226ca 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_connector.c +++ b/drivers/gpu/drm/exynos/exynos_drm_connector.c @@ -34,7 +34,6 @@ convert_to_display_mode(struct drm_display_mode *mode, struct exynos_drm_panel_info *panel) { struct fb_videomode *timing = &panel->timing; - DRM_DEBUG_KMS("%s\n", __FILE__); mode->clock = timing->pixclock / 1000; mode->vrefresh = timing->refresh; @@ -68,8 +67,6 @@ static int exynos_drm_connector_get_modes(struct drm_connector *connector) unsigned int count = 0; int ret; - DRM_DEBUG_KMS("%s\n", __FILE__); - if (!display_ops) { DRM_DEBUG_KMS("display_ops is null.\n"); return 0; @@ -156,8 +153,6 @@ struct drm_encoder *exynos_drm_best_encoder(struct drm_connector *connector) struct drm_mode_object *obj; struct drm_encoder *encoder; - DRM_DEBUG_KMS("%s\n", __FILE__); - obj = drm_mode_object_find(dev, exynos_connector->encoder_id, DRM_MODE_OBJECT_ENCODER); if (!obj) { @@ -200,8 +195,6 @@ void exynos_drm_display_power(struct drm_connector *connector, int mode) static void exynos_drm_connector_dpms(struct drm_connector *connector, int mode) { - DRM_DEBUG_KMS("%s\n", __FILE__); - /* * in case that drm_crtc_helper_set_mode() is called, * encoder/crtc->funcs->dpms() will be just returned @@ -248,8 +241,6 @@ exynos_drm_connector_detect(struct drm_connector *connector, bool force) manager->display_ops; enum drm_connector_status status = connector_status_disconnected; - DRM_DEBUG_KMS("%s\n", __FILE__); - if (display_ops && display_ops->is_connected) { if (display_ops->is_connected(manager->dev)) status = connector_status_connected; @@ -265,8 +256,6 @@ static void exynos_drm_connector_destroy(struct drm_connector *connector) struct exynos_drm_connector *exynos_connector = to_exynos_connector(connector); - DRM_DEBUG_KMS("%s\n", __FILE__); - drm_sysfs_connector_remove(connector); drm_connector_cleanup(connector); kfree(exynos_connector); @@ -288,8 +277,6 @@ struct drm_connector *exynos_drm_connector_create(struct drm_device *dev, int type; int err; - DRM_DEBUG_KMS("%s\n", __FILE__); - exynos_connector = kzalloc(sizeof(*exynos_connector), GFP_KERNEL); if (!exynos_connector) { DRM_ERROR("failed to allocate connector\n"); diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c index 4667c9f67acd..1bef6dc77478 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_core.c +++ b/drivers/gpu/drm/exynos/exynos_drm_core.c @@ -27,8 +27,6 @@ static int exynos_drm_create_enc_conn(struct drm_device *dev, struct drm_connector *connector; int ret; - DRM_DEBUG_DRIVER("%s\n", __FILE__); - subdrv->manager->dev = subdrv->dev; /* create and initialize a encoder for this sub driver. */ @@ -102,8 +100,6 @@ static int exynos_drm_subdrv_probe(struct drm_device *dev, static void exynos_drm_subdrv_remove(struct drm_device *dev, struct exynos_drm_subdrv *subdrv) { - DRM_DEBUG_DRIVER("%s\n", __FILE__); - if (subdrv->remove) subdrv->remove(dev, subdrv->dev); } @@ -114,8 +110,6 @@ int exynos_drm_device_register(struct drm_device *dev) unsigned int fine_cnt = 0; int err; - DRM_DEBUG_DRIVER("%s\n", __FILE__); - if (!dev) return -EINVAL; @@ -158,8 +152,6 @@ int exynos_drm_device_unregister(struct drm_device *dev) { struct exynos_drm_subdrv *subdrv; - DRM_DEBUG_DRIVER("%s\n", __FILE__); - if (!dev) { WARN(1, "Unexpected drm device unregister!\n"); return -EINVAL; @@ -176,8 +168,6 @@ EXPORT_SYMBOL_GPL(exynos_drm_device_unregister); int exynos_drm_subdrv_register(struct exynos_drm_subdrv *subdrv) { - DRM_DEBUG_DRIVER("%s\n", __FILE__); - if (!subdrv) return -EINVAL; @@ -189,8 +179,6 @@ EXPORT_SYMBOL_GPL(exynos_drm_subdrv_register); int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *subdrv) { - DRM_DEBUG_DRIVER("%s\n", __FILE__); - if (!subdrv) return -EINVAL; diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index a143605884a4..9a35d171a6d3 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -76,8 +76,6 @@ static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode) static void exynos_drm_crtc_prepare(struct drm_crtc *crtc) { - DRM_DEBUG_KMS("%s\n", __FILE__); - /* drm framework doesn't check NULL. */ } @@ -85,8 +83,6 @@ static void exynos_drm_crtc_commit(struct drm_crtc *crtc) { struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); - DRM_DEBUG_KMS("%s\n", __FILE__); - exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON); exynos_plane_commit(exynos_crtc->plane); exynos_plane_dpms(exynos_crtc->plane, DRM_MODE_DPMS_ON); @@ -97,8 +93,6 @@ exynos_drm_crtc_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { - DRM_DEBUG_KMS("%s\n", __FILE__); - /* drm framework doesn't check NULL */ return true; } @@ -115,8 +109,6 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, int pipe = exynos_crtc->pipe; int ret; - DRM_DEBUG_KMS("%s\n", __FILE__); - /* * copy the mode data adjusted by mode_fixup() into crtc->mode * so that hardware can be seet to proper mode. @@ -148,8 +140,6 @@ static int exynos_drm_crtc_mode_set_commit(struct drm_crtc *crtc, int x, int y, unsigned int crtc_h; int ret; - DRM_DEBUG_KMS("%s\n", __FILE__); - /* when framebuffer changing is requested, crtc's dpms should be on */ if (exynos_crtc->dpms > DRM_MODE_DPMS_ON) { DRM_ERROR("failed framebuffer changing request.\n"); @@ -179,8 +169,6 @@ static void exynos_drm_crtc_disable(struct drm_crtc *crtc) { struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); - DRM_DEBUG_KMS("%s\n", __FILE__); - exynos_plane_dpms(exynos_crtc->plane, DRM_MODE_DPMS_OFF); exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); } @@ -205,8 +193,6 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *old_fb = crtc->fb; int ret = -EINVAL; - DRM_DEBUG_KMS("%s\n", __FILE__); - /* when the page flip is requested, crtc's dpms should be on */ if (exynos_crtc->dpms > DRM_MODE_DPMS_ON) { DRM_ERROR("failed page flip request.\n"); @@ -259,8 +245,6 @@ static void exynos_drm_crtc_destroy(struct drm_crtc *crtc) struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); struct exynos_drm_private *private = crtc->dev->dev_private; - DRM_DEBUG_KMS("%s\n", __FILE__); - private->crtc[exynos_crtc->pipe] = NULL; drm_crtc_cleanup(crtc); @@ -275,8 +259,6 @@ static int exynos_drm_crtc_set_property(struct drm_crtc *crtc, struct exynos_drm_private *dev_priv = dev->dev_private; struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); - DRM_DEBUG_KMS("%s\n", __func__); - if (property == dev_priv->crtc_mode_property) { enum exynos_crtc_mode mode = val; @@ -321,8 +303,6 @@ static void exynos_drm_crtc_attach_mode_property(struct drm_crtc *crtc) struct exynos_drm_private *dev_priv = dev->dev_private; struct drm_property *prop; - DRM_DEBUG_KMS("%s\n", __func__); - prop = dev_priv->crtc_mode_property; if (!prop) { prop = drm_property_create_enum(dev, 0, "mode", mode_names, @@ -342,8 +322,6 @@ int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr) struct exynos_drm_private *private = dev->dev_private; struct drm_crtc *crtc; - DRM_DEBUG_KMS("%s\n", __FILE__); - exynos_crtc = kzalloc(sizeof(*exynos_crtc), GFP_KERNEL); if (!exynos_crtc) { DRM_ERROR("failed to allocate exynos crtc\n"); @@ -378,8 +356,6 @@ int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc) struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(private->crtc[crtc]); - DRM_DEBUG_KMS("%s\n", __FILE__); - if (exynos_crtc->dpms != DRM_MODE_DPMS_ON) return -EPERM; @@ -395,8 +371,6 @@ void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc) struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(private->crtc[crtc]); - DRM_DEBUG_KMS("%s\n", __FILE__); - if (exynos_crtc->dpms != DRM_MODE_DPMS_ON) return; @@ -412,8 +386,6 @@ void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int crtc) struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(drm_crtc); unsigned long flags; - DRM_DEBUG_KMS("%s\n", __FILE__); - spin_lock_irqsave(&dev->event_lock, flags); list_for_each_entry_safe(e, t, &dev_priv->pageflip_event_list, diff --git a/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c b/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c index ff7f2a886a34..a0f997e0cbdf 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c @@ -71,8 +71,6 @@ static struct sg_table * unsigned int i; int nents, ret; - DRM_DEBUG_PRIME("%s\n", __FILE__); - /* just return current sgt if already requested. */ if (exynos_attach->dir == dir && exynos_attach->is_mapped) return &exynos_attach->sgt; @@ -133,8 +131,6 @@ static void exynos_dmabuf_release(struct dma_buf *dmabuf) { struct exynos_drm_gem_obj *exynos_gem_obj = dmabuf->priv; - DRM_DEBUG_PRIME("%s\n", __FILE__); - /* * exynos_dmabuf_release() call means that file object's * f_count is 0 and it calls drm_gem_object_handle_unreference() @@ -219,8 +215,6 @@ struct drm_gem_object *exynos_dmabuf_prime_import(struct drm_device *drm_dev, struct exynos_drm_gem_buf *buffer; int ret; - DRM_DEBUG_PRIME("%s\n", __FILE__); - /* is this one of own objects? */ if (dma_buf->ops == &exynos_dmabuf_ops) { struct drm_gem_object *obj; diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index ba6d995e4375..276237348d1e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -46,8 +46,6 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags) int ret; int nr; - DRM_DEBUG_DRIVER("%s\n", __FILE__); - private = kzalloc(sizeof(struct exynos_drm_private), GFP_KERNEL); if (!private) { DRM_ERROR("failed to allocate private\n"); @@ -140,8 +138,6 @@ err_crtc: static int exynos_drm_unload(struct drm_device *dev) { - DRM_DEBUG_DRIVER("%s\n", __FILE__); - exynos_drm_fbdev_fini(dev); exynos_drm_device_unregister(dev); drm_vblank_cleanup(dev); @@ -160,8 +156,6 @@ static int exynos_drm_open(struct drm_device *dev, struct drm_file *file) { struct drm_exynos_file_private *file_priv; - DRM_DEBUG_DRIVER("%s\n", __FILE__); - file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL); if (!file_priv) return -ENOMEM; @@ -178,8 +172,6 @@ static void exynos_drm_preclose(struct drm_device *dev, struct drm_pending_vblank_event *e, *t; unsigned long flags; - DRM_DEBUG_DRIVER("%s\n", __FILE__); - /* release events of current file */ spin_lock_irqsave(&dev->event_lock, flags); list_for_each_entry_safe(e, t, &private->pageflip_event_list, @@ -196,8 +188,6 @@ static void exynos_drm_preclose(struct drm_device *dev, static void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file) { - DRM_DEBUG_DRIVER("%s\n", __FILE__); - if (!file->driver_priv) return; @@ -207,8 +197,6 @@ static void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file) static void exynos_drm_lastclose(struct drm_device *dev) { - DRM_DEBUG_DRIVER("%s\n", __FILE__); - exynos_drm_fbdev_restore_mode(dev); } @@ -292,8 +280,6 @@ static struct drm_driver exynos_drm_driver = { static int exynos_drm_platform_probe(struct platform_device *pdev) { - DRM_DEBUG_DRIVER("%s\n", __FILE__); - pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); exynos_drm_driver.num_ioctls = DRM_ARRAY_SIZE(exynos_ioctls); @@ -302,8 +288,6 @@ static int exynos_drm_platform_probe(struct platform_device *pdev) static int exynos_drm_platform_remove(struct platform_device *pdev) { - DRM_DEBUG_DRIVER("%s\n", __FILE__); - drm_platform_exit(&exynos_drm_driver, pdev); return 0; @@ -322,8 +306,6 @@ static int __init exynos_drm_init(void) { int ret; - DRM_DEBUG_DRIVER("%s\n", __FILE__); - #ifdef CONFIG_DRM_EXYNOS_FIMD ret = platform_driver_register(&fimd_driver); if (ret < 0) @@ -455,8 +437,6 @@ out_fimd: static void __exit exynos_drm_exit(void) { - DRM_DEBUG_DRIVER("%s\n", __FILE__); - platform_device_unregister(exynos_drm_pdev); platform_driver_unregister(&exynos_drm_platform_driver); diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.c b/drivers/gpu/drm/exynos/exynos_drm_encoder.c index c63721f64aec..a99a033793bc 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_encoder.c +++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.c @@ -61,7 +61,7 @@ static void exynos_drm_encoder_dpms(struct drm_encoder *encoder, int mode) struct exynos_drm_manager_ops *manager_ops = manager->ops; struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); - DRM_DEBUG_KMS("%s, encoder dpms: %d\n", __FILE__, mode); + DRM_DEBUG_KMS("encoder dpms: %d\n", mode); if (exynos_encoder->dpms == mode) { DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n"); @@ -104,8 +104,6 @@ exynos_drm_encoder_mode_fixup(struct drm_encoder *encoder, struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder); struct exynos_drm_manager_ops *manager_ops = manager->ops; - DRM_DEBUG_KMS("%s\n", __FILE__); - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { if (connector->encoder == encoder) if (manager_ops && manager_ops->mode_fixup) @@ -155,8 +153,6 @@ static void exynos_drm_encoder_mode_set(struct drm_encoder *encoder, struct exynos_drm_manager *manager; struct exynos_drm_manager_ops *manager_ops; - DRM_DEBUG_KMS("%s\n", __FILE__); - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { if (connector->encoder == encoder) { struct exynos_drm_encoder *exynos_encoder; @@ -189,8 +185,6 @@ static void exynos_drm_encoder_mode_set(struct drm_encoder *encoder, static void exynos_drm_encoder_prepare(struct drm_encoder *encoder) { - DRM_DEBUG_KMS("%s\n", __FILE__); - /* drm framework doesn't check NULL. */ } @@ -200,8 +194,6 @@ static void exynos_drm_encoder_commit(struct drm_encoder *encoder) struct exynos_drm_manager *manager = exynos_encoder->manager; struct exynos_drm_manager_ops *manager_ops = manager->ops; - DRM_DEBUG_KMS("%s\n", __FILE__); - if (manager_ops && manager_ops->commit) manager_ops->commit(manager->dev); @@ -274,8 +266,6 @@ static void exynos_drm_encoder_destroy(struct drm_encoder *encoder) struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder); - DRM_DEBUG_KMS("%s\n", __FILE__); - exynos_encoder->manager->pipe = -1; drm_encoder_cleanup(encoder); @@ -315,8 +305,6 @@ void exynos_drm_encoder_setup(struct drm_device *dev) { struct drm_encoder *encoder; - DRM_DEBUG_KMS("%s\n", __FILE__); - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) encoder->possible_clones = exynos_drm_encoder_clones(encoder); } @@ -329,8 +317,6 @@ exynos_drm_encoder_create(struct drm_device *dev, struct drm_encoder *encoder; struct exynos_drm_encoder *exynos_encoder; - DRM_DEBUG_KMS("%s\n", __FILE__); - if (!manager || !possible_crtcs) return NULL; @@ -427,8 +413,6 @@ void exynos_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data) struct exynos_drm_manager_ops *manager_ops = manager->ops; int mode = *(int *)data; - DRM_DEBUG_KMS("%s\n", __FILE__); - if (manager_ops && manager_ops->dpms) manager_ops->dpms(manager->dev, mode); @@ -449,8 +433,6 @@ void exynos_drm_encoder_crtc_pipe(struct drm_encoder *encoder, void *data) to_exynos_encoder(encoder)->manager; int pipe = *(int *)data; - DRM_DEBUG_KMS("%s\n", __FILE__); - /* * when crtc is detached from encoder, this pipe is used * to select manager operation @@ -465,8 +447,6 @@ void exynos_drm_encoder_plane_mode_set(struct drm_encoder *encoder, void *data) struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; struct exynos_drm_overlay *overlay = data; - DRM_DEBUG_KMS("%s\n", __FILE__); - if (overlay_ops && overlay_ops->mode_set) overlay_ops->mode_set(manager->dev, overlay); } @@ -478,8 +458,6 @@ void exynos_drm_encoder_plane_commit(struct drm_encoder *encoder, void *data) struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; int zpos = DEFAULT_ZPOS; - DRM_DEBUG_KMS("%s\n", __FILE__); - if (data) zpos = *(int *)data; @@ -494,8 +472,6 @@ void exynos_drm_encoder_plane_enable(struct drm_encoder *encoder, void *data) struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; int zpos = DEFAULT_ZPOS; - DRM_DEBUG_KMS("%s\n", __FILE__); - if (data) zpos = *(int *)data; @@ -510,8 +486,6 @@ void exynos_drm_encoder_plane_disable(struct drm_encoder *encoder, void *data) struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; int zpos = DEFAULT_ZPOS; - DRM_DEBUG_KMS("%s\n", __FILE__); - if (data) zpos = *(int *)data; diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c index 0e04f4ea441f..c2d149f0408a 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fb.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c @@ -70,8 +70,6 @@ static void exynos_drm_fb_destroy(struct drm_framebuffer *fb) struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb); unsigned int i; - DRM_DEBUG_KMS("%s\n", __FILE__); - /* make sure that overlay data are updated before relesing fb. */ exynos_drm_encoder_complete_scanout(fb); @@ -97,8 +95,6 @@ static int exynos_drm_fb_create_handle(struct drm_framebuffer *fb, { struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb); - DRM_DEBUG_KMS("%s\n", __FILE__); - /* This fb should have only one gem object. */ if (WARN_ON(exynos_fb->buf_cnt != 1)) return -EINVAL; @@ -112,8 +108,6 @@ static int exynos_drm_fb_dirty(struct drm_framebuffer *fb, unsigned color, struct drm_clip_rect *clips, unsigned num_clips) { - DRM_DEBUG_KMS("%s\n", __FILE__); - /* TODO */ return 0; @@ -225,8 +219,6 @@ exynos_user_fb_create(struct drm_device *dev, struct drm_file *file_priv, struct exynos_drm_fb *exynos_fb; int i, ret; - DRM_DEBUG_KMS("%s\n", __FILE__); - exynos_fb = kzalloc(sizeof(*exynos_fb), GFP_KERNEL); if (!exynos_fb) { DRM_ERROR("failed to allocate exynos drm framebuffer\n"); @@ -293,8 +285,6 @@ struct exynos_drm_gem_buf *exynos_drm_fb_buffer(struct drm_framebuffer *fb, struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb); struct exynos_drm_gem_buf *buffer; - DRM_DEBUG_KMS("%s\n", __FILE__); - if (index >= MAX_FB_BUFFER) return NULL; diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index 8f007aaeffc3..8e60bd61137f 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -43,8 +43,6 @@ static int exynos_drm_fb_mmap(struct fb_info *info, unsigned long vm_size; int ret; - DRM_DEBUG_KMS("%s\n", __func__); - vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP; vm_size = vma->vm_end - vma->vm_start; @@ -84,8 +82,6 @@ static int exynos_drm_fbdev_update(struct drm_fb_helper *helper, unsigned int size = fb->width * fb->height * (fb->bits_per_pixel >> 3); unsigned long offset; - DRM_DEBUG_KMS("%s\n", __FILE__); - drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth); drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height); @@ -148,8 +144,6 @@ static int exynos_drm_fbdev_create(struct drm_fb_helper *helper, unsigned long size; int ret; - DRM_DEBUG_KMS("%s\n", __FILE__); - DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d\n", sizes->surface_width, sizes->surface_height, sizes->surface_bpp); @@ -238,8 +232,6 @@ int exynos_drm_fbdev_init(struct drm_device *dev) unsigned int num_crtc; int ret; - DRM_DEBUG_KMS("%s\n", __FILE__); - if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector) return 0; diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimc.c b/drivers/gpu/drm/exynos/exynos_drm_fimc.c index 4a1616a18ab7..7b5f2e8f0861 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimc.c @@ -175,8 +175,6 @@ static void fimc_sw_reset(struct fimc_context *ctx) { u32 cfg; - DRM_DEBUG_KMS("%s\n", __func__); - /* stop dma operation */ cfg = fimc_read(EXYNOS_CISTATUS); if (EXYNOS_CISTATUS_GET_ENVID_STATUS(cfg)) { @@ -210,8 +208,6 @@ static void fimc_sw_reset(struct fimc_context *ctx) static int fimc_set_camblk_fimd0_wb(struct fimc_context *ctx) { - DRM_DEBUG_KMS("%s\n", __func__); - return regmap_update_bits(ctx->sysreg, SYSREG_CAMERA_BLK, SYSREG_FIMD0WB_DEST_MASK, ctx->id << SYSREG_FIMD0WB_DEST_SHIFT); @@ -319,8 +315,6 @@ static void fimc_clear_irq(struct fimc_context *ctx) { u32 cfg; - DRM_DEBUG_KMS("%s\n", __func__); - cfg = fimc_read(EXYNOS_CIGCTRL); cfg |= EXYNOS_CIGCTRL_IRQ_CLR; fimc_write(cfg, EXYNOS_CIGCTRL); @@ -380,8 +374,6 @@ static int fimc_get_buf_id(struct fimc_context *ctx) u32 cfg; int frame_cnt, buf_id; - DRM_DEBUG_KMS("%s\n", __func__); - cfg = fimc_read(EXYNOS_CISTATUS2); frame_cnt = EXYNOS_CISTATUS2_GET_FRAMECOUNT_BEFORE(cfg); @@ -1357,8 +1349,6 @@ static int fimc_init_prop_list(struct exynos_drm_ippdrv *ippdrv) { struct drm_exynos_ipp_prop_list *prop_list; - DRM_DEBUG_KMS("%s\n", __func__); - prop_list = devm_kzalloc(ippdrv->dev, sizeof(*prop_list), GFP_KERNEL); if (!prop_list) { DRM_ERROR("failed to alloc property list.\n"); @@ -1419,8 +1409,6 @@ static int fimc_ippdrv_check_property(struct device *dev, bool swap; int i; - DRM_DEBUG_KMS("%s\n", __func__); - for_each_ipp_ops(i) { if ((i == EXYNOS_DRM_OPS_SRC) && (property->cmd == IPP_CMD_WB)) @@ -1526,8 +1514,6 @@ static void fimc_clear_addr(struct fimc_context *ctx) { int i; - DRM_DEBUG_KMS("%s:\n", __func__); - for (i = 0; i < FIMC_MAX_SRC; i++) { fimc_write(0, EXYNOS_CIIYSA(i)); fimc_write(0, EXYNOS_CIICBSA(i)); @@ -1545,8 +1531,6 @@ static int fimc_ippdrv_reset(struct device *dev) { struct fimc_context *ctx = get_fimc_context(dev); - DRM_DEBUG_KMS("%s\n", __func__); - /* reset h/w block */ fimc_sw_reset(ctx); diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index 0939e4652706..3e106beca5b6 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -150,8 +150,6 @@ static inline struct fimd_driver_data *drm_fimd_get_driver_data( static bool fimd_display_is_connected(struct device *dev) { - DRM_DEBUG_KMS("%s\n", __FILE__); - /* TODO. */ return true; @@ -161,15 +159,11 @@ static void *fimd_get_panel(struct device *dev) { struct fimd_context *ctx = get_fimd_context(dev); - DRM_DEBUG_KMS("%s\n", __FILE__); - return ctx->panel; } static int fimd_check_mode(struct device *dev, struct drm_display_mode *mode) { - DRM_DEBUG_KMS("%s\n", __FILE__); - /* TODO. */ return 0; @@ -177,8 +171,6 @@ static int fimd_check_mode(struct device *dev, struct drm_display_mode *mode) static int fimd_display_power_on(struct device *dev, int mode) { - DRM_DEBUG_KMS("%s\n", __FILE__); - /* TODO */ return 0; @@ -196,7 +188,7 @@ static void fimd_dpms(struct device *subdrv_dev, int mode) { struct fimd_context *ctx = get_fimd_context(subdrv_dev); - DRM_DEBUG_KMS("%s, %d\n", __FILE__, mode); + DRM_DEBUG_KMS("%d\n", mode); mutex_lock(&ctx->lock); @@ -234,8 +226,6 @@ static void fimd_apply(struct device *subdrv_dev) struct fimd_win_data *win_data; int i; - DRM_DEBUG_KMS("%s\n", __FILE__); - for (i = 0; i < WINDOWS_NR; i++) { win_data = &ctx->win_data[i]; if (win_data->enabled && (ovl_ops && ovl_ops->commit)) @@ -258,8 +248,6 @@ static void fimd_commit(struct device *dev) if (ctx->suspended) return; - DRM_DEBUG_KMS("%s\n", __FILE__); - /* setup polarity values from machine code. */ writel(ctx->vidcon1, ctx->regs + driver_data->timing_base + VIDCON1); @@ -309,8 +297,6 @@ static int fimd_enable_vblank(struct device *dev) struct fimd_context *ctx = get_fimd_context(dev); u32 val; - DRM_DEBUG_KMS("%s\n", __FILE__); - if (ctx->suspended) return -EPERM; @@ -336,8 +322,6 @@ static void fimd_disable_vblank(struct device *dev) struct fimd_context *ctx = get_fimd_context(dev); u32 val; - DRM_DEBUG_KMS("%s\n", __FILE__); - if (ctx->suspended) return; @@ -387,8 +371,6 @@ static void fimd_win_mode_set(struct device *dev, int win; unsigned long offset; - DRM_DEBUG_KMS("%s\n", __FILE__); - if (!overlay) { dev_err(dev, "overlay is NULL\n"); return; @@ -435,8 +417,6 @@ static void fimd_win_set_pixfmt(struct device *dev, unsigned int win) struct fimd_win_data *win_data = &ctx->win_data[win]; unsigned long val; - DRM_DEBUG_KMS("%s\n", __FILE__); - val = WINCONx_ENWIN; switch (win_data->bpp) { @@ -495,8 +475,6 @@ static void fimd_win_set_colkey(struct device *dev, unsigned int win) struct fimd_context *ctx = get_fimd_context(dev); unsigned int keycon0 = 0, keycon1 = 0; - DRM_DEBUG_KMS("%s\n", __FILE__); - keycon0 = ~(WxKEYCON0_KEYBL_EN | WxKEYCON0_KEYEN_F | WxKEYCON0_DIRCON) | WxKEYCON0_COMPKEY(0); @@ -542,8 +520,6 @@ static void fimd_win_commit(struct device *dev, int zpos) unsigned int last_x; unsigned int last_y; - DRM_DEBUG_KMS("%s\n", __FILE__); - if (ctx->suspended) return; @@ -662,8 +638,6 @@ static void fimd_win_disable(struct device *dev, int zpos) int win = zpos; u32 val; - DRM_DEBUG_KMS("%s\n", __FILE__); - if (win == DEFAULT_ZPOS) win = ctx->default_win; @@ -743,8 +717,6 @@ out: static int fimd_subdrv_probe(struct drm_device *drm_dev, struct device *dev) { - DRM_DEBUG_KMS("%s\n", __FILE__); - /* * enable drm irq mode. * - with irq_enabled = 1, we can use the vblank feature. @@ -771,8 +743,6 @@ static int fimd_subdrv_probe(struct drm_device *drm_dev, struct device *dev) static void fimd_subdrv_remove(struct drm_device *drm_dev, struct device *dev) { - DRM_DEBUG_KMS("%s\n", __FILE__); - /* detach this sub driver from iommu mapping if supported. */ if (is_drm_iommu_supported(drm_dev)) drm_iommu_detach_device(drm_dev, dev); @@ -787,8 +757,6 @@ static int fimd_calc_clkdiv(struct fimd_context *ctx, u32 best_framerate = 0; u32 framerate; - DRM_DEBUG_KMS("%s\n", __FILE__); - retrace = timing->left_margin + timing->hsync_len + timing->right_margin + timing->xres; retrace *= timing->upper_margin + timing->vsync_len + @@ -823,8 +791,6 @@ static int fimd_calc_clkdiv(struct fimd_context *ctx, static void fimd_clear_win(struct fimd_context *ctx, int win) { - DRM_DEBUG_KMS("%s\n", __FILE__); - writel(0, ctx->regs + WINCON(win)); writel(0, ctx->regs + VIDOSD_A(win)); writel(0, ctx->regs + VIDOSD_B(win)); @@ -838,8 +804,6 @@ static void fimd_clear_win(struct fimd_context *ctx, int win) static int fimd_clock(struct fimd_context *ctx, bool enable) { - DRM_DEBUG_KMS("%s\n", __FILE__); - if (enable) { int ret; @@ -925,8 +889,6 @@ static int fimd_probe(struct platform_device *pdev) int win; int ret = -EINVAL; - DRM_DEBUG_KMS("%s\n", __FILE__); - if (dev->of_node) { pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) { @@ -1032,8 +994,6 @@ static int fimd_remove(struct platform_device *pdev) struct device *dev = &pdev->dev; struct fimd_context *ctx = platform_get_drvdata(pdev); - DRM_DEBUG_KMS("%s\n", __FILE__); - exynos_drm_subdrv_unregister(&ctx->subdrv); if (ctx->suspended) @@ -1098,8 +1058,6 @@ static int fimd_runtime_suspend(struct device *dev) { struct fimd_context *ctx = get_fimd_context(dev); - DRM_DEBUG_KMS("%s\n", __FILE__); - return fimd_activate(ctx, false); } @@ -1107,8 +1065,6 @@ static int fimd_runtime_resume(struct device *dev) { struct fimd_context *ctx = get_fimd_context(dev); - DRM_DEBUG_KMS("%s\n", __FILE__); - return fimd_activate(ctx, true); } #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c index cf4543ffa079..5af14782fcfe 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c @@ -132,8 +132,6 @@ void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj) struct drm_gem_object *obj; struct exynos_drm_gem_buf *buf; - DRM_DEBUG_KMS("%s\n", __FILE__); - obj = &exynos_gem_obj->base; buf = exynos_gem_obj->buffer; @@ -227,7 +225,6 @@ struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev, } size = roundup_gem_size(size, flags); - DRM_DEBUG_KMS("%s\n", __FILE__); ret = check_gem_flags(flags); if (ret) @@ -268,8 +265,6 @@ int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data, struct exynos_drm_gem_obj *exynos_gem_obj; int ret; - DRM_DEBUG_KMS("%s\n", __FILE__); - exynos_gem_obj = exynos_drm_gem_create(dev, args->flags, args->size); if (IS_ERR(exynos_gem_obj)) return PTR_ERR(exynos_gem_obj); @@ -331,8 +326,6 @@ int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data, { struct drm_exynos_gem_map_off *args = data; - DRM_DEBUG_KMS("%s\n", __FILE__); - DRM_DEBUG_KMS("handle = 0x%x, offset = 0x%lx\n", args->handle, (unsigned long)args->offset); @@ -371,8 +364,6 @@ static int exynos_drm_gem_mmap_buffer(struct file *filp, unsigned long vm_size; int ret; - DRM_DEBUG_KMS("%s\n", __FILE__); - vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP; vma->vm_private_data = obj; vma->vm_ops = drm_dev->driver->gem_vm_ops; @@ -431,8 +422,6 @@ int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data, struct drm_gem_object *obj; unsigned int addr; - DRM_DEBUG_KMS("%s\n", __FILE__); - if (!(dev->driver->driver_features & DRIVER_GEM)) { DRM_ERROR("does not support GEM.\n"); return -ENODEV; @@ -643,8 +632,6 @@ void exynos_gem_unmap_sgt_from_dma(struct drm_device *drm_dev, int exynos_drm_gem_init_object(struct drm_gem_object *obj) { - DRM_DEBUG_KMS("%s\n", __FILE__); - return 0; } @@ -653,8 +640,6 @@ void exynos_drm_gem_free_object(struct drm_gem_object *obj) struct exynos_drm_gem_obj *exynos_gem_obj; struct exynos_drm_gem_buf *buf; - DRM_DEBUG_KMS("%s\n", __FILE__); - exynos_gem_obj = to_exynos_gem_obj(obj); buf = exynos_gem_obj->buffer; @@ -671,8 +656,6 @@ int exynos_drm_gem_dumb_create(struct drm_file *file_priv, struct exynos_drm_gem_obj *exynos_gem_obj; int ret; - DRM_DEBUG_KMS("%s\n", __FILE__); - /* * alocate memory to be used for framebuffer. * - this callback would be called by user application @@ -704,8 +687,6 @@ int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv, struct drm_gem_object *obj; int ret = 0; - DRM_DEBUG_KMS("%s\n", __FILE__); - mutex_lock(&dev->struct_mutex); /* @@ -743,8 +724,6 @@ int exynos_drm_gem_dumb_destroy(struct drm_file *file_priv, { int ret; - DRM_DEBUG_KMS("%s\n", __FILE__); - /* * obj->refcount and obj->handle_count are decreased and * if both them are 0 then exynos_drm_gem_free_object() @@ -788,8 +767,6 @@ int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) struct drm_gem_object *obj; int ret; - DRM_DEBUG_KMS("%s\n", __FILE__); - /* set vm_area_struct. */ ret = drm_gem_mmap(filp, vma); if (ret < 0) { diff --git a/drivers/gpu/drm/exynos/exynos_drm_gsc.c b/drivers/gpu/drm/exynos/exynos_drm_gsc.c index 762f40d548b7..398a0b31ddd0 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gsc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gsc.c @@ -400,8 +400,6 @@ static int gsc_sw_reset(struct gsc_context *ctx) u32 cfg; int count = GSC_RESET_TIMEOUT; - DRM_DEBUG_KMS("%s\n", __func__); - /* s/w reset */ cfg = (GSC_SW_RESET_SRESET); gsc_write(cfg, GSC_SW_RESET); @@ -441,8 +439,6 @@ static void gsc_set_gscblk_fimd_wb(struct gsc_context *ctx, bool enable) { u32 gscblk_cfg; - DRM_DEBUG_KMS("%s\n", __func__); - gscblk_cfg = readl(SYSREG_GSCBLK_CFG1); if (enable) @@ -1350,8 +1346,6 @@ static int gsc_init_prop_list(struct exynos_drm_ippdrv *ippdrv) { struct drm_exynos_ipp_prop_list *prop_list; - DRM_DEBUG_KMS("%s\n", __func__); - prop_list = devm_kzalloc(ippdrv->dev, sizeof(*prop_list), GFP_KERNEL); if (!prop_list) { DRM_ERROR("failed to alloc property list.\n"); @@ -1411,8 +1405,6 @@ static int gsc_ippdrv_check_property(struct device *dev, bool swap; int i; - DRM_DEBUG_KMS("%s\n", __func__); - for_each_ipp_ops(i) { if ((i == EXYNOS_DRM_OPS_SRC) && (property->cmd == IPP_CMD_WB)) @@ -1521,8 +1513,6 @@ static int gsc_ippdrv_reset(struct device *dev) struct gsc_scaler *sc = &ctx->sc; int ret; - DRM_DEBUG_KMS("%s\n", __func__); - /* reset h/w block */ ret = gsc_sw_reset(ctx); if (ret < 0) { @@ -1807,7 +1797,7 @@ static int gsc_runtime_resume(struct device *dev) { struct gsc_context *ctx = get_gsc_context(dev); - DRM_DEBUG_KMS("%s:id[%d]\n", __FILE__, ctx->id); + DRM_DEBUG_KMS("id[%d]\n", ctx->id); return gsc_clk_ctrl(ctx, true); } diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c index 59acdc0017a7..aaa550d622f0 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c @@ -88,16 +88,12 @@ void exynos_mixer_drv_attach(struct exynos_drm_hdmi_context *ctx) void exynos_hdmi_ops_register(struct exynos_hdmi_ops *ops) { - DRM_DEBUG_KMS("%s\n", __FILE__); - if (ops) hdmi_ops = ops; } void exynos_mixer_ops_register(struct exynos_mixer_ops *ops) { - DRM_DEBUG_KMS("%s\n", __FILE__); - if (ops) mixer_ops = ops; } @@ -106,8 +102,6 @@ static bool drm_hdmi_is_connected(struct device *dev) { struct drm_hdmi_context *ctx = to_context(dev); - DRM_DEBUG_KMS("%s\n", __FILE__); - if (hdmi_ops && hdmi_ops->is_connected) return hdmi_ops->is_connected(ctx->hdmi_ctx->ctx); @@ -119,8 +113,6 @@ static struct edid *drm_hdmi_get_edid(struct device *dev, { struct drm_hdmi_context *ctx = to_context(dev); - DRM_DEBUG_KMS("%s\n", __FILE__); - if (hdmi_ops && hdmi_ops->get_edid) return hdmi_ops->get_edid(ctx->hdmi_ctx->ctx, connector); @@ -133,8 +125,6 @@ static int drm_hdmi_check_mode(struct device *dev, struct drm_hdmi_context *ctx = to_context(dev); int ret = 0; - DRM_DEBUG_KMS("%s\n", __FILE__); - /* * Both, mixer and hdmi should be able to handle the requested mode. * If any of the two fails, return mode as BAD. @@ -156,8 +146,6 @@ static int drm_hdmi_power_on(struct device *dev, int mode) { struct drm_hdmi_context *ctx = to_context(dev); - DRM_DEBUG_KMS("%s\n", __FILE__); - if (hdmi_ops && hdmi_ops->power_on) return hdmi_ops->power_on(ctx->hdmi_ctx->ctx, mode); @@ -178,8 +166,6 @@ static int drm_hdmi_enable_vblank(struct device *subdrv_dev) struct exynos_drm_subdrv *subdrv = &ctx->subdrv; struct exynos_drm_manager *manager = subdrv->manager; - DRM_DEBUG_KMS("%s\n", __FILE__); - if (mixer_ops && mixer_ops->enable_vblank) return mixer_ops->enable_vblank(ctx->mixer_ctx->ctx, manager->pipe); @@ -191,8 +177,6 @@ static void drm_hdmi_disable_vblank(struct device *subdrv_dev) { struct drm_hdmi_context *ctx = to_context(subdrv_dev); - DRM_DEBUG_KMS("%s\n", __FILE__); - if (mixer_ops && mixer_ops->disable_vblank) return mixer_ops->disable_vblank(ctx->mixer_ctx->ctx); } @@ -201,8 +185,6 @@ static void drm_hdmi_wait_for_vblank(struct device *subdrv_dev) { struct drm_hdmi_context *ctx = to_context(subdrv_dev); - DRM_DEBUG_KMS("%s\n", __FILE__); - if (mixer_ops && mixer_ops->wait_for_vblank) mixer_ops->wait_for_vblank(ctx->mixer_ctx->ctx); } @@ -215,8 +197,6 @@ static void drm_hdmi_mode_fixup(struct device *subdrv_dev, struct drm_display_mode *m; int mode_ok; - DRM_DEBUG_KMS("%s\n", __FILE__); - drm_mode_set_crtcinfo(adjusted_mode, 0); mode_ok = drm_hdmi_check_mode(subdrv_dev, adjusted_mode); @@ -257,8 +237,6 @@ static void drm_hdmi_mode_set(struct device *subdrv_dev, void *mode) { struct drm_hdmi_context *ctx = to_context(subdrv_dev); - DRM_DEBUG_KMS("%s\n", __FILE__); - if (hdmi_ops && hdmi_ops->mode_set) hdmi_ops->mode_set(ctx->hdmi_ctx->ctx, mode); } @@ -268,8 +246,6 @@ static void drm_hdmi_get_max_resol(struct device *subdrv_dev, { struct drm_hdmi_context *ctx = to_context(subdrv_dev); - DRM_DEBUG_KMS("%s\n", __FILE__); - if (hdmi_ops && hdmi_ops->get_max_resol) hdmi_ops->get_max_resol(ctx->hdmi_ctx->ctx, width, height); } @@ -278,8 +254,6 @@ static void drm_hdmi_commit(struct device *subdrv_dev) { struct drm_hdmi_context *ctx = to_context(subdrv_dev); - DRM_DEBUG_KMS("%s\n", __FILE__); - if (hdmi_ops && hdmi_ops->commit) hdmi_ops->commit(ctx->hdmi_ctx->ctx); } @@ -288,8 +262,6 @@ static void drm_hdmi_dpms(struct device *subdrv_dev, int mode) { struct drm_hdmi_context *ctx = to_context(subdrv_dev); - DRM_DEBUG_KMS("%s\n", __FILE__); - if (mixer_ops && mixer_ops->dpms) mixer_ops->dpms(ctx->mixer_ctx->ctx, mode); @@ -302,8 +274,6 @@ static void drm_hdmi_apply(struct device *subdrv_dev) struct drm_hdmi_context *ctx = to_context(subdrv_dev); int i; - DRM_DEBUG_KMS("%s\n", __FILE__); - for (i = 0; i < MIXER_WIN_NR; i++) { if (!ctx->enabled[i]) continue; @@ -332,8 +302,6 @@ static void drm_mixer_mode_set(struct device *subdrv_dev, { struct drm_hdmi_context *ctx = to_context(subdrv_dev); - DRM_DEBUG_KMS("%s\n", __FILE__); - if (mixer_ops && mixer_ops->win_mode_set) mixer_ops->win_mode_set(ctx->mixer_ctx->ctx, overlay); } @@ -343,8 +311,6 @@ static void drm_mixer_commit(struct device *subdrv_dev, int zpos) struct drm_hdmi_context *ctx = to_context(subdrv_dev); int win = (zpos == DEFAULT_ZPOS) ? MIXER_DEFAULT_WIN : zpos; - DRM_DEBUG_KMS("%s\n", __FILE__); - if (win < 0 || win >= MIXER_WIN_NR) { DRM_ERROR("mixer window[%d] is wrong\n", win); return; @@ -361,8 +327,6 @@ static void drm_mixer_disable(struct device *subdrv_dev, int zpos) struct drm_hdmi_context *ctx = to_context(subdrv_dev); int win = (zpos == DEFAULT_ZPOS) ? MIXER_DEFAULT_WIN : zpos; - DRM_DEBUG_KMS("%s\n", __FILE__); - if (win < 0 || win >= MIXER_WIN_NR) { DRM_ERROR("mixer window[%d] is wrong\n", win); return; @@ -393,8 +357,6 @@ static int hdmi_subdrv_probe(struct drm_device *drm_dev, struct exynos_drm_subdrv *subdrv = to_subdrv(dev); struct drm_hdmi_context *ctx; - DRM_DEBUG_KMS("%s\n", __FILE__); - if (!hdmi_ctx) { DRM_ERROR("hdmi context not initialized.\n"); return -EFAULT; @@ -441,8 +403,6 @@ static int exynos_drm_hdmi_probe(struct platform_device *pdev) struct exynos_drm_subdrv *subdrv; struct drm_hdmi_context *ctx; - DRM_DEBUG_KMS("%s\n", __FILE__); - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) { DRM_LOG_KMS("failed to alloc common hdmi context.\n"); @@ -467,8 +427,6 @@ static int exynos_drm_hdmi_remove(struct platform_device *pdev) { struct drm_hdmi_context *ctx = platform_get_drvdata(pdev); - DRM_DEBUG_KMS("%s\n", __FILE__); - exynos_drm_subdrv_unregister(&ctx->subdrv); return 0; diff --git a/drivers/gpu/drm/exynos/exynos_drm_ipp.c b/drivers/gpu/drm/exynos/exynos_drm_ipp.c index be1e88463466..32d174ff1c50 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_ipp.c +++ b/drivers/gpu/drm/exynos/exynos_drm_ipp.c @@ -131,8 +131,6 @@ void exynos_platform_device_ipp_unregister(void) int exynos_drm_ippdrv_register(struct exynos_drm_ippdrv *ippdrv) { - DRM_DEBUG_KMS("%s\n", __func__); - if (!ippdrv) return -EINVAL; @@ -145,8 +143,6 @@ int exynos_drm_ippdrv_register(struct exynos_drm_ippdrv *ippdrv) int exynos_drm_ippdrv_unregister(struct exynos_drm_ippdrv *ippdrv) { - DRM_DEBUG_KMS("%s\n", __func__); - if (!ippdrv) return -EINVAL; @@ -162,8 +158,6 @@ static int ipp_create_id(struct idr *id_idr, struct mutex *lock, void *obj, { int ret; - DRM_DEBUG_KMS("%s\n", __func__); - /* do the allocation under our mutexlock */ mutex_lock(lock); ret = idr_alloc(id_idr, obj, 1, 0, GFP_KERNEL); @@ -320,8 +314,6 @@ int exynos_drm_ipp_get_property(struct drm_device *drm_dev, void *data, struct exynos_drm_ippdrv *ippdrv; int count = 0; - DRM_DEBUG_KMS("%s\n", __func__); - if (!ctx) { DRM_ERROR("invalid context.\n"); return -EINVAL; @@ -418,8 +410,6 @@ static struct drm_exynos_ipp_cmd_work *ipp_create_cmd_work(void) { struct drm_exynos_ipp_cmd_work *cmd_work; - DRM_DEBUG_KMS("%s\n", __func__); - cmd_work = kzalloc(sizeof(*cmd_work), GFP_KERNEL); if (!cmd_work) { DRM_ERROR("failed to alloc cmd_work.\n"); @@ -435,8 +425,6 @@ static struct drm_exynos_ipp_event_work *ipp_create_event_work(void) { struct drm_exynos_ipp_event_work *event_work; - DRM_DEBUG_KMS("%s\n", __func__); - event_work = kzalloc(sizeof(*event_work), GFP_KERNEL); if (!event_work) { DRM_ERROR("failed to alloc event_work.\n"); @@ -460,8 +448,6 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data, struct drm_exynos_ipp_cmd_node *c_node; int ret, i; - DRM_DEBUG_KMS("%s\n", __func__); - if (!ctx) { DRM_ERROR("invalid context.\n"); return -EINVAL; @@ -569,8 +555,6 @@ err_clear: static void ipp_clean_cmd_node(struct drm_exynos_ipp_cmd_node *c_node) { - DRM_DEBUG_KMS("%s\n", __func__); - /* delete list */ list_del(&c_node->list); @@ -593,8 +577,6 @@ static int ipp_check_mem_list(struct drm_exynos_ipp_cmd_node *c_node) struct list_head *head; int ret, i, count[EXYNOS_DRM_OPS_MAX] = { 0, }; - DRM_DEBUG_KMS("%s\n", __func__); - mutex_lock(&c_node->mem_lock); for_each_ipp_ops(i) { @@ -714,8 +696,6 @@ static struct drm_exynos_ipp_mem_node void *addr; int i; - DRM_DEBUG_KMS("%s\n", __func__); - mutex_lock(&c_node->mem_lock); m_node = kzalloc(sizeof(*m_node), GFP_KERNEL); @@ -857,8 +837,6 @@ static void ipp_put_event(struct drm_exynos_ipp_cmd_node *c_node, struct drm_exynos_ipp_send_event *e, *te; int count = 0; - DRM_DEBUG_KMS("%s\n", __func__); - if (list_empty(&c_node->event_list)) { DRM_DEBUG_KMS("%s:event_list is empty.\n", __func__); return; @@ -912,8 +890,6 @@ static int ipp_queue_buf_with_run(struct device *dev, struct exynos_drm_ipp_ops *ops; int ret; - DRM_DEBUG_KMS("%s\n", __func__); - ippdrv = ipp_find_drv_by_handle(qbuf->prop_id); if (IS_ERR(ippdrv)) { DRM_ERROR("failed to get ipp driver.\n"); @@ -964,8 +940,6 @@ static void ipp_clean_queue_buf(struct drm_device *drm_dev, { struct drm_exynos_ipp_mem_node *m_node, *tm_node; - DRM_DEBUG_KMS("%s\n", __func__); - if (!list_empty(&c_node->mem_list[qbuf->ops_id])) { /* delete list */ list_for_each_entry_safe(m_node, tm_node, @@ -989,8 +963,6 @@ int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev, void *data, struct drm_exynos_ipp_mem_node *m_node; int ret; - DRM_DEBUG_KMS("%s\n", __func__); - if (!qbuf) { DRM_ERROR("invalid buf parameter.\n"); return -EINVAL; @@ -1075,8 +1047,6 @@ err_clean_node: static bool exynos_drm_ipp_check_valid(struct device *dev, enum drm_exynos_ipp_ctrl ctrl, enum drm_exynos_ipp_state state) { - DRM_DEBUG_KMS("%s\n", __func__); - if (ctrl != IPP_CTRL_PLAY) { if (pm_runtime_suspended(dev)) { DRM_ERROR("pm:runtime_suspended.\n"); @@ -1126,8 +1096,6 @@ int exynos_drm_ipp_cmd_ctrl(struct drm_device *drm_dev, void *data, struct drm_exynos_ipp_cmd_work *cmd_work; struct drm_exynos_ipp_cmd_node *c_node; - DRM_DEBUG_KMS("%s\n", __func__); - if (!ctx) { DRM_ERROR("invalid context.\n"); return -EINVAL; @@ -1491,8 +1459,6 @@ void ipp_sched_cmd(struct work_struct *work) struct drm_exynos_ipp_property *property; int ret; - DRM_DEBUG_KMS("%s\n", __func__); - ippdrv = cmd_work->ippdrv; if (!ippdrv) { DRM_ERROR("invalid ippdrv list.\n"); @@ -1759,8 +1725,6 @@ static int ipp_subdrv_probe(struct drm_device *drm_dev, struct device *dev) struct exynos_drm_ippdrv *ippdrv; int ret, count = 0; - DRM_DEBUG_KMS("%s\n", __func__); - /* get ipp driver entry */ list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) { ippdrv->drm_dev = drm_dev; @@ -1816,8 +1780,6 @@ static void ipp_subdrv_remove(struct drm_device *drm_dev, struct device *dev) { struct exynos_drm_ippdrv *ippdrv; - DRM_DEBUG_KMS("%s\n", __func__); - /* get ipp driver entry */ list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) { if (is_drm_iommu_supported(drm_dev)) @@ -1834,8 +1796,6 @@ static int ipp_subdrv_open(struct drm_device *drm_dev, struct device *dev, struct drm_exynos_file_private *file_priv = file->driver_priv; struct exynos_drm_ipp_private *priv; - DRM_DEBUG_KMS("%s\n", __func__); - priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) { DRM_ERROR("failed to allocate priv.\n"); @@ -1913,8 +1873,6 @@ static int ipp_probe(struct platform_device *pdev) if (!ctx) return -ENOMEM; - DRM_DEBUG_KMS("%s\n", __func__); - mutex_init(&ctx->ipp_lock); mutex_init(&ctx->prop_lock); @@ -1978,8 +1936,6 @@ static int ipp_remove(struct platform_device *pdev) { struct ipp_context *ctx = platform_get_drvdata(pdev); - DRM_DEBUG_KMS("%s\n", __func__); - /* unregister sub driver */ exynos_drm_subdrv_unregister(&ctx->subdrv); @@ -2009,8 +1965,6 @@ static int ipp_suspend(struct device *dev) { struct ipp_context *ctx = get_ipp_context(dev); - DRM_DEBUG_KMS("%s\n", __func__); - if (pm_runtime_suspended(dev)) return 0; @@ -2021,8 +1975,6 @@ static int ipp_resume(struct device *dev) { struct ipp_context *ctx = get_ipp_context(dev); - DRM_DEBUG_KMS("%s\n", __func__); - if (!pm_runtime_suspended(dev)) return ipp_power_ctrl(ctx, true); @@ -2035,8 +1987,6 @@ static int ipp_runtime_suspend(struct device *dev) { struct ipp_context *ctx = get_ipp_context(dev); - DRM_DEBUG_KMS("%s\n", __func__); - return ipp_power_ctrl(ctx, false); } @@ -2044,8 +1994,6 @@ static int ipp_runtime_resume(struct device *dev) { struct ipp_context *ctx = get_ipp_context(dev); - DRM_DEBUG_KMS("%s\n", __func__); - return ipp_power_ctrl(ctx, true); } #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c index 83efc662d65a..6ee55e68e0a2 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_plane.c +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c @@ -81,8 +81,6 @@ int exynos_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc, int nr; int i; - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - nr = exynos_drm_fb_get_buf_cnt(fb); for (i = 0; i < nr; i++) { struct exynos_drm_gem_buf *buffer = exynos_drm_fb_buffer(fb, i); @@ -159,8 +157,6 @@ void exynos_plane_dpms(struct drm_plane *plane, int mode) struct exynos_plane *exynos_plane = to_exynos_plane(plane); struct exynos_drm_overlay *overlay = &exynos_plane->overlay; - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - if (mode == DRM_MODE_DPMS_ON) { if (exynos_plane->enabled) return; @@ -189,8 +185,6 @@ exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, { int ret; - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - ret = exynos_plane_mode_set(plane, crtc, fb, crtc_x, crtc_y, crtc_w, crtc_h, src_x >> 16, src_y >> 16, src_w >> 16, src_h >> 16); @@ -207,8 +201,6 @@ exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, static int exynos_disable_plane(struct drm_plane *plane) { - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - exynos_plane_dpms(plane, DRM_MODE_DPMS_OFF); return 0; @@ -218,8 +210,6 @@ static void exynos_plane_destroy(struct drm_plane *plane) { struct exynos_plane *exynos_plane = to_exynos_plane(plane); - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - exynos_disable_plane(plane); drm_plane_cleanup(plane); kfree(exynos_plane); @@ -233,8 +223,6 @@ static int exynos_plane_set_property(struct drm_plane *plane, struct exynos_plane *exynos_plane = to_exynos_plane(plane); struct exynos_drm_private *dev_priv = dev->dev_private; - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - if (property == dev_priv->plane_zpos_property) { exynos_plane->overlay.zpos = val; return 0; @@ -256,8 +244,6 @@ static void exynos_plane_attach_zpos_property(struct drm_plane *plane) struct exynos_drm_private *dev_priv = dev->dev_private; struct drm_property *prop; - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - prop = dev_priv->plane_zpos_property; if (!prop) { prop = drm_property_create_range(dev, 0, "zpos", 0, @@ -277,8 +263,6 @@ struct drm_plane *exynos_plane_init(struct drm_device *dev, struct exynos_plane *exynos_plane; int err; - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - exynos_plane = kzalloc(sizeof(struct exynos_plane), GFP_KERNEL); if (!exynos_plane) { DRM_ERROR("failed to allocate plane\n"); diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.c b/drivers/gpu/drm/exynos/exynos_drm_rotator.c index 9b6c70964d71..b811e5c24f94 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_rotator.c +++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.c @@ -471,8 +471,6 @@ static int rotator_init_prop_list(struct exynos_drm_ippdrv *ippdrv) { struct drm_exynos_ipp_prop_list *prop_list; - DRM_DEBUG_KMS("%s\n", __func__); - prop_list = devm_kzalloc(ippdrv->dev, sizeof(*prop_list), GFP_KERNEL); if (!prop_list) { DRM_ERROR("failed to alloc property list.\n"); @@ -752,8 +750,6 @@ static struct platform_device_id rotator_driver_ids[] = { static int rotator_clk_crtl(struct rot_context *rot, bool enable) { - DRM_DEBUG_KMS("%s\n", __func__); - if (enable) { clk_enable(rot->clock); rot->suspended = false; @@ -771,8 +767,6 @@ static int rotator_suspend(struct device *dev) { struct rot_context *rot = dev_get_drvdata(dev); - DRM_DEBUG_KMS("%s\n", __func__); - if (pm_runtime_suspended(dev)) return 0; @@ -783,8 +777,6 @@ static int rotator_resume(struct device *dev) { struct rot_context *rot = dev_get_drvdata(dev); - DRM_DEBUG_KMS("%s\n", __func__); - if (!pm_runtime_suspended(dev)) return rotator_clk_crtl(rot, true); @@ -797,8 +789,6 @@ static int rotator_runtime_suspend(struct device *dev) { struct rot_context *rot = dev_get_drvdata(dev); - DRM_DEBUG_KMS("%s\n", __func__); - return rotator_clk_crtl(rot, false); } @@ -806,8 +796,6 @@ static int rotator_runtime_resume(struct device *dev) { struct rot_context *rot = dev_get_drvdata(dev); - DRM_DEBUG_KMS("%s\n", __func__); - return rotator_clk_crtl(rot, true); } #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c index 294ba35efa5b..784bbce0741a 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c @@ -89,8 +89,6 @@ static bool vidi_display_is_connected(struct device *dev) { struct vidi_context *ctx = get_vidi_context(dev); - DRM_DEBUG_KMS("%s\n", __FILE__); - /* * connection request would come from user side * to do hotplug through specific ioctl. @@ -105,8 +103,6 @@ static struct edid *vidi_get_edid(struct device *dev, struct edid *edid; int edid_len; - DRM_DEBUG_KMS("%s\n", __FILE__); - /* * the edid data comes from user side and it would be set * to ctx->raw_edid through specific ioctl. @@ -128,8 +124,6 @@ static struct edid *vidi_get_edid(struct device *dev, static void *vidi_get_panel(struct device *dev) { - DRM_DEBUG_KMS("%s\n", __FILE__); - /* TODO. */ return NULL; @@ -137,8 +131,6 @@ static void *vidi_get_panel(struct device *dev) static int vidi_check_mode(struct device *dev, struct drm_display_mode *mode) { - DRM_DEBUG_KMS("%s\n", __FILE__); - /* TODO. */ return 0; @@ -146,8 +138,6 @@ static int vidi_check_mode(struct device *dev, struct drm_display_mode *mode) static int vidi_display_power_on(struct device *dev, int mode) { - DRM_DEBUG_KMS("%s\n", __FILE__); - /* TODO */ return 0; @@ -166,7 +156,7 @@ static void vidi_dpms(struct device *subdrv_dev, int mode) { struct vidi_context *ctx = get_vidi_context(subdrv_dev); - DRM_DEBUG_KMS("%s, %d\n", __FILE__, mode); + DRM_DEBUG_KMS("%d\n", mode); mutex_lock(&ctx->lock); @@ -196,8 +186,6 @@ static void vidi_apply(struct device *subdrv_dev) struct vidi_win_data *win_data; int i; - DRM_DEBUG_KMS("%s\n", __FILE__); - for (i = 0; i < WINDOWS_NR; i++) { win_data = &ctx->win_data[i]; if (win_data->enabled && (ovl_ops && ovl_ops->commit)) @@ -212,8 +200,6 @@ static void vidi_commit(struct device *dev) { struct vidi_context *ctx = get_vidi_context(dev); - DRM_DEBUG_KMS("%s\n", __FILE__); - if (ctx->suspended) return; } @@ -222,8 +208,6 @@ static int vidi_enable_vblank(struct device *dev) { struct vidi_context *ctx = get_vidi_context(dev); - DRM_DEBUG_KMS("%s\n", __FILE__); - if (ctx->suspended) return -EPERM; @@ -246,8 +230,6 @@ static void vidi_disable_vblank(struct device *dev) { struct vidi_context *ctx = get_vidi_context(dev); - DRM_DEBUG_KMS("%s\n", __FILE__); - if (ctx->suspended) return; @@ -271,8 +253,6 @@ static void vidi_win_mode_set(struct device *dev, int win; unsigned long offset; - DRM_DEBUG_KMS("%s\n", __FILE__); - if (!overlay) { dev_err(dev, "overlay is NULL\n"); return; @@ -324,8 +304,6 @@ static void vidi_win_commit(struct device *dev, int zpos) struct vidi_win_data *win_data; int win = zpos; - DRM_DEBUG_KMS("%s\n", __FILE__); - if (ctx->suspended) return; @@ -351,8 +329,6 @@ static void vidi_win_disable(struct device *dev, int zpos) struct vidi_win_data *win_data; int win = zpos; - DRM_DEBUG_KMS("%s\n", __FILE__); - if (win == DEFAULT_ZPOS) win = ctx->default_win; @@ -407,8 +383,6 @@ static void vidi_fake_vblank_handler(struct work_struct *work) static int vidi_subdrv_probe(struct drm_device *drm_dev, struct device *dev) { - DRM_DEBUG_KMS("%s\n", __FILE__); - /* * enable drm irq mode. * - with irq_enabled = 1, we can use the vblank feature. @@ -431,8 +405,6 @@ static int vidi_subdrv_probe(struct drm_device *drm_dev, struct device *dev) static void vidi_subdrv_remove(struct drm_device *drm_dev, struct device *dev) { - DRM_DEBUG_KMS("%s\n", __FILE__); - /* TODO. */ } @@ -441,8 +413,6 @@ static int vidi_power_on(struct vidi_context *ctx, bool enable) struct exynos_drm_subdrv *subdrv = &ctx->subdrv; struct device *dev = subdrv->dev; - DRM_DEBUG_KMS("%s\n", __FILE__); - if (enable != false && enable != true) return -EINVAL; @@ -483,8 +453,6 @@ static int vidi_store_connection(struct device *dev, struct vidi_context *ctx = get_vidi_context(dev); int ret; - DRM_DEBUG_KMS("%s\n", __FILE__); - ret = kstrtoint(buf, 0, &ctx->connected); if (ret) return ret; @@ -522,8 +490,6 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, void *data, struct drm_exynos_vidi_connection *vidi = data; int edid_len; - DRM_DEBUG_KMS("%s\n", __FILE__); - if (!vidi) { DRM_DEBUG_KMS("user data for vidi is null.\n"); return -EINVAL; @@ -592,8 +558,6 @@ static int vidi_probe(struct platform_device *pdev) struct exynos_drm_subdrv *subdrv; int ret; - DRM_DEBUG_KMS("%s\n", __FILE__); - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; @@ -625,8 +589,6 @@ static int vidi_remove(struct platform_device *pdev) { struct vidi_context *ctx = platform_get_drvdata(pdev); - DRM_DEBUG_KMS("%s\n", __FILE__); - exynos_drm_subdrv_unregister(&ctx->subdrv); if (ctx->raw_edid != (struct edid *)fake_edid_info) { diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 04255feaeaed..67692a362c6e 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -689,8 +689,6 @@ static void hdmi_reg_infoframe(struct hdmi_context *hdata, u32 mod; u32 vic; - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - mod = hdmi_reg_read(hdata, HDMI_MODE_SEL); if (hdata->dvi_mode) { hdmi_reg_writeb(hdata, HDMI_VSI_CON, @@ -755,8 +753,6 @@ static struct edid *hdmi_get_edid(void *ctx, struct drm_connector *connector) struct edid *raw_edid; struct hdmi_context *hdata = ctx; - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - if (!hdata->ddc_port) return ERR_PTR(-ENODEV); @@ -777,8 +773,6 @@ static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock) const struct hdmiphy_config *confs; int count, i; - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - if (hdata->type == HDMI_TYPE13) { confs = hdmiphy_v13_configs; count = ARRAY_SIZE(hdmiphy_v13_configs); @@ -1335,8 +1329,6 @@ static void hdmiphy_conf_reset(struct hdmi_context *hdata) static void hdmiphy_poweron(struct hdmi_context *hdata) { - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - if (hdata->type == HDMI_TYPE14) hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, 0, HDMI_PHY_POWER_OFF_EN); @@ -1344,8 +1336,6 @@ static void hdmiphy_poweron(struct hdmi_context *hdata) static void hdmiphy_poweroff(struct hdmi_context *hdata) { - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - if (hdata->type == HDMI_TYPE14) hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, ~0, HDMI_PHY_POWER_OFF_EN); @@ -1409,8 +1399,6 @@ static void hdmiphy_conf_apply(struct hdmi_context *hdata) static void hdmi_conf_apply(struct hdmi_context *hdata) { - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - hdmiphy_conf_reset(hdata); hdmiphy_conf_apply(hdata); @@ -1660,8 +1648,6 @@ static void hdmi_mode_set(void *ctx, struct drm_display_mode *mode) static void hdmi_get_max_resol(void *ctx, unsigned int *width, unsigned int *height) { - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - *width = MAX_WIDTH; *height = MAX_HEIGHT; } @@ -1670,8 +1656,6 @@ static void hdmi_commit(void *ctx) { struct hdmi_context *hdata = ctx; - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - mutex_lock(&hdata->hdmi_mutex); if (!hdata->powered) { mutex_unlock(&hdata->hdmi_mutex); @@ -1686,8 +1670,6 @@ static void hdmi_poweron(struct hdmi_context *hdata) { struct hdmi_resources *res = &hdata->res; - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - mutex_lock(&hdata->hdmi_mutex); if (hdata->powered) { mutex_unlock(&hdata->hdmi_mutex); @@ -1712,8 +1694,6 @@ static void hdmi_poweroff(struct hdmi_context *hdata) { struct hdmi_resources *res = &hdata->res; - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - mutex_lock(&hdata->hdmi_mutex); if (!hdata->powered) goto out; @@ -1945,8 +1925,6 @@ static int hdmi_probe(struct platform_device *pdev) struct resource *res; int ret; - DRM_DEBUG_KMS("[%d]\n", __LINE__); - if (dev->of_node) { pdata = drm_hdmi_dt_parse_pdata(dev); if (IS_ERR(pdata)) { @@ -2072,8 +2050,6 @@ static int hdmi_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - pm_runtime_disable(dev); /* hdmiphy i2c driver */ @@ -2090,8 +2066,6 @@ static int hdmi_suspend(struct device *dev) struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev); struct hdmi_context *hdata = ctx->ctx; - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - disable_irq(hdata->irq); hdata->hpd = false; @@ -2113,8 +2087,6 @@ static int hdmi_resume(struct device *dev) struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev); struct hdmi_context *hdata = ctx->ctx; - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - hdata->hpd = gpio_get_value(hdata->hpd_gpio); enable_irq(hdata->irq); @@ -2135,7 +2107,6 @@ static int hdmi_runtime_suspend(struct device *dev) { struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev); struct hdmi_context *hdata = ctx->ctx; - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); hdmi_poweroff(hdata); @@ -2146,7 +2117,6 @@ static int hdmi_runtime_resume(struct device *dev) { struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev); struct hdmi_context *hdata = ctx->ctx; - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); hdmi_poweron(hdata); diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index b0882b31353e..e84f9e9fcd74 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -696,8 +696,6 @@ static int mixer_enable_vblank(void *ctx, int pipe) struct mixer_context *mixer_ctx = ctx; struct mixer_resources *res = &mixer_ctx->mixer_res; - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - mixer_ctx->pipe = pipe; /* enable vsync interrupt */ @@ -712,8 +710,6 @@ static void mixer_disable_vblank(void *ctx) struct mixer_context *mixer_ctx = ctx; struct mixer_resources *res = &mixer_ctx->mixer_res; - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - /* disable vsync interrupt */ mixer_reg_writemask(res, MXR_INT_EN, 0, MXR_INT_EN_VSYNC); } @@ -725,8 +721,6 @@ static void mixer_win_mode_set(void *ctx, struct hdmi_win_data *win_data; int win; - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - if (!overlay) { DRM_ERROR("overlay is NULL\n"); return; @@ -890,8 +884,6 @@ static void mixer_poweron(struct mixer_context *ctx) { struct mixer_resources *res = &ctx->mixer_res; - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - mutex_lock(&ctx->mixer_mutex); if (ctx->powered) { mutex_unlock(&ctx->mixer_mutex); @@ -916,8 +908,6 @@ static void mixer_poweroff(struct mixer_context *ctx) { struct mixer_resources *res = &ctx->mixer_res; - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - mutex_lock(&ctx->mixer_mutex); if (!ctx->powered) goto out; @@ -944,8 +934,6 @@ static void mixer_dpms(void *ctx, int mode) { struct mixer_context *mixer_ctx = ctx; - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - switch (mode) { case DRM_MODE_DPMS_ON: if (pm_runtime_suspended(mixer_ctx->dev)) @@ -1249,8 +1237,6 @@ static int mixer_suspend(struct device *dev) struct exynos_drm_hdmi_context *drm_hdmi_ctx = get_mixer_context(dev); struct mixer_context *ctx = drm_hdmi_ctx->ctx; - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - if (pm_runtime_suspended(dev)) { DRM_DEBUG_KMS("%s : Already suspended\n", __func__); return 0; @@ -1266,8 +1252,6 @@ static int mixer_resume(struct device *dev) struct exynos_drm_hdmi_context *drm_hdmi_ctx = get_mixer_context(dev); struct mixer_context *ctx = drm_hdmi_ctx->ctx; - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - if (!pm_runtime_suspended(dev)) { DRM_DEBUG_KMS("%s : Already resumed\n", __func__); return 0; @@ -1285,8 +1269,6 @@ static int mixer_runtime_suspend(struct device *dev) struct exynos_drm_hdmi_context *drm_hdmi_ctx = get_mixer_context(dev); struct mixer_context *ctx = drm_hdmi_ctx->ctx; - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - mixer_poweroff(ctx); return 0; @@ -1297,8 +1279,6 @@ static int mixer_runtime_resume(struct device *dev) struct exynos_drm_hdmi_context *drm_hdmi_ctx = get_mixer_context(dev); struct mixer_context *ctx = drm_hdmi_ctx->ctx; - DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__); - mixer_poweron(ctx); return 0; -- cgit v1.2.3 From cbc4c33d14ceefe99372065cb5733101401c08a2 Mon Sep 17 00:00:00 2001 From: YoungJun Cho Date: Wed, 12 Jun 2013 10:44:40 +0900 Subject: drm/exynos: Clean up logs for DRM_ERROR / DRM_DEBUG_KMS This patch cleans up logs for DRM_ERROR / DRM_DEBUG_KMS to avoid logging duplicated function name because the macros already contain __func__. Signed-off-by: YoungJun Cho Signed-off-by: Seung-Woo Kim Signed-off-by: Inki Dae --- drivers/gpu/drm/exynos/exynos_drm_fimc.c | 113 ++++++++++----------- drivers/gpu/drm/exynos/exynos_drm_gsc.c | 90 ++++++++--------- drivers/gpu/drm/exynos/exynos_drm_ipp.c | 149 +++++++++++++--------------- drivers/gpu/drm/exynos/exynos_drm_rotator.c | 41 ++++---- drivers/gpu/drm/exynos/exynos_hdmi.c | 10 +- drivers/gpu/drm/exynos/exynos_mixer.c | 8 +- 6 files changed, 187 insertions(+), 224 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimc.c b/drivers/gpu/drm/exynos/exynos_drm_fimc.c index 7b5f2e8f0861..61b094f689a7 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimc.c @@ -217,7 +217,7 @@ static void fimc_set_type_ctrl(struct fimc_context *ctx, enum fimc_wb wb) { u32 cfg; - DRM_DEBUG_KMS("%s:wb[%d]\n", __func__, wb); + DRM_DEBUG_KMS("wb[%d]\n", wb); cfg = fimc_read(EXYNOS_CIGCTRL); cfg &= ~(EXYNOS_CIGCTRL_TESTPATTERN_MASK | @@ -253,10 +253,10 @@ static void fimc_set_polarity(struct fimc_context *ctx, { u32 cfg; - DRM_DEBUG_KMS("%s:inv_pclk[%d]inv_vsync[%d]\n", - __func__, pol->inv_pclk, pol->inv_vsync); - DRM_DEBUG_KMS("%s:inv_href[%d]inv_hsync[%d]\n", - __func__, pol->inv_href, pol->inv_hsync); + DRM_DEBUG_KMS("inv_pclk[%d]inv_vsync[%d]\n", + pol->inv_pclk, pol->inv_vsync); + DRM_DEBUG_KMS("inv_href[%d]inv_hsync[%d]\n", + pol->inv_href, pol->inv_hsync); cfg = fimc_read(EXYNOS_CIGCTRL); cfg &= ~(EXYNOS_CIGCTRL_INVPOLPCLK | EXYNOS_CIGCTRL_INVPOLVSYNC | @@ -278,7 +278,7 @@ static void fimc_handle_jpeg(struct fimc_context *ctx, bool enable) { u32 cfg; - DRM_DEBUG_KMS("%s:enable[%d]\n", __func__, enable); + DRM_DEBUG_KMS("enable[%d]\n", enable); cfg = fimc_read(EXYNOS_CIGCTRL); if (enable) @@ -294,7 +294,7 @@ static void fimc_handle_irq(struct fimc_context *ctx, bool enable, { u32 cfg; - DRM_DEBUG_KMS("%s:enable[%d]overflow[%d]level[%d]\n", __func__, + DRM_DEBUG_KMS("enable[%d]overflow[%d]level[%d]\n", enable, overflow, level); cfg = fimc_read(EXYNOS_CIGCTRL); @@ -329,7 +329,7 @@ static bool fimc_check_ovf(struct fimc_context *ctx) flag = EXYNOS_CISTATUS_OVFIY | EXYNOS_CISTATUS_OVFICB | EXYNOS_CISTATUS_OVFICR; - DRM_DEBUG_KMS("%s:flag[0x%x]\n", __func__, flag); + DRM_DEBUG_KMS("flag[0x%x]\n", flag); if (status & flag) { cfg = fimc_read(EXYNOS_CIWDOFST); @@ -358,7 +358,7 @@ static bool fimc_check_frame_end(struct fimc_context *ctx) cfg = fimc_read(EXYNOS_CISTATUS); - DRM_DEBUG_KMS("%s:cfg[0x%x]\n", __func__, cfg); + DRM_DEBUG_KMS("cfg[0x%x]\n", cfg); if (!(cfg & EXYNOS_CISTATUS_FRAMEEND)) return false; @@ -380,7 +380,7 @@ static int fimc_get_buf_id(struct fimc_context *ctx) if (frame_cnt == 0) frame_cnt = EXYNOS_CISTATUS2_GET_FRAMECOUNT_PRESENT(cfg); - DRM_DEBUG_KMS("%s:present[%d]before[%d]\n", __func__, + DRM_DEBUG_KMS("present[%d]before[%d]\n", EXYNOS_CISTATUS2_GET_FRAMECOUNT_PRESENT(cfg), EXYNOS_CISTATUS2_GET_FRAMECOUNT_BEFORE(cfg)); @@ -390,7 +390,7 @@ static int fimc_get_buf_id(struct fimc_context *ctx) } buf_id = frame_cnt - 1; - DRM_DEBUG_KMS("%s:buf_id[%d]\n", __func__, buf_id); + DRM_DEBUG_KMS("buf_id[%d]\n", buf_id); return buf_id; } @@ -399,7 +399,7 @@ static void fimc_handle_lastend(struct fimc_context *ctx, bool enable) { u32 cfg; - DRM_DEBUG_KMS("%s:enable[%d]\n", __func__, enable); + DRM_DEBUG_KMS("enable[%d]\n", enable); cfg = fimc_read(EXYNOS_CIOCTRL); if (enable) @@ -416,7 +416,7 @@ static int fimc_src_set_fmt_order(struct fimc_context *ctx, u32 fmt) struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; u32 cfg; - DRM_DEBUG_KMS("%s:fmt[0x%x]\n", __func__, fmt); + DRM_DEBUG_KMS("fmt[0x%x]\n", fmt); /* RGB */ cfg = fimc_read(EXYNOS_CISCCTRL); @@ -489,7 +489,7 @@ static int fimc_src_set_fmt(struct device *dev, u32 fmt) struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; u32 cfg; - DRM_DEBUG_KMS("%s:fmt[0x%x]\n", __func__, fmt); + DRM_DEBUG_KMS("fmt[0x%x]\n", fmt); cfg = fimc_read(EXYNOS_MSCTRL); cfg &= ~EXYNOS_MSCTRL_INFORMAT_RGB; @@ -549,8 +549,7 @@ static int fimc_src_set_transf(struct device *dev, struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; u32 cfg1, cfg2; - DRM_DEBUG_KMS("%s:degree[%d]flip[0x%x]\n", __func__, - degree, flip); + DRM_DEBUG_KMS("degree[%d]flip[0x%x]\n", degree, flip); cfg1 = fimc_read(EXYNOS_MSCTRL); cfg1 &= ~(EXYNOS_MSCTRL_FLIP_X_MIRROR | @@ -613,10 +612,9 @@ static int fimc_set_window(struct fimc_context *ctx, v1 = pos->y; v2 = sz->vsize - pos->h - pos->y; - DRM_DEBUG_KMS("%s:x[%d]y[%d]w[%d]h[%d]hsize[%d]vsize[%d]\n", - __func__, pos->x, pos->y, pos->w, pos->h, sz->hsize, sz->vsize); - DRM_DEBUG_KMS("%s:h1[%d]h2[%d]v1[%d]v2[%d]\n", __func__, - h1, h2, v1, v2); + DRM_DEBUG_KMS("x[%d]y[%d]w[%d]h[%d]hsize[%d]vsize[%d]\n", + pos->x, pos->y, pos->w, pos->h, sz->hsize, sz->vsize); + DRM_DEBUG_KMS("h1[%d]h2[%d]v1[%d]v2[%d]\n", h1, h2, v1, v2); /* * set window offset 1, 2 size @@ -645,8 +643,8 @@ static int fimc_src_set_size(struct device *dev, int swap, struct drm_exynos_sz img_sz = *sz; u32 cfg; - DRM_DEBUG_KMS("%s:swap[%d]hsize[%d]vsize[%d]\n", - __func__, swap, sz->hsize, sz->vsize); + DRM_DEBUG_KMS("swap[%d]hsize[%d]vsize[%d]\n", + swap, sz->hsize, sz->vsize); /* original size */ cfg = (EXYNOS_ORGISIZE_HORIZONTAL(img_sz.hsize) | @@ -654,8 +652,7 @@ static int fimc_src_set_size(struct device *dev, int swap, fimc_write(cfg, EXYNOS_ORGISIZE); - DRM_DEBUG_KMS("%s:x[%d]y[%d]w[%d]h[%d]\n", __func__, - pos->x, pos->y, pos->w, pos->h); + DRM_DEBUG_KMS("x[%d]y[%d]w[%d]h[%d]\n", pos->x, pos->y, pos->w, pos->h); if (swap) { img_pos.w = pos->h; @@ -712,7 +709,7 @@ static int fimc_src_set_addr(struct device *dev, property = &c_node->property; - DRM_DEBUG_KMS("%s:prop_id[%d]buf_id[%d]buf_type[%d]\n", __func__, + DRM_DEBUG_KMS("prop_id[%d]buf_id[%d]buf_type[%d]\n", property->prop_id, buf_id, buf_type); if (buf_id > FIMC_MAX_SRC) { @@ -764,7 +761,7 @@ static int fimc_dst_set_fmt_order(struct fimc_context *ctx, u32 fmt) struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; u32 cfg; - DRM_DEBUG_KMS("%s:fmt[0x%x]\n", __func__, fmt); + DRM_DEBUG_KMS("fmt[0x%x]\n", fmt); /* RGB */ cfg = fimc_read(EXYNOS_CISCCTRL); @@ -843,7 +840,7 @@ static int fimc_dst_set_fmt(struct device *dev, u32 fmt) struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; u32 cfg; - DRM_DEBUG_KMS("%s:fmt[0x%x]\n", __func__, fmt); + DRM_DEBUG_KMS("fmt[0x%x]\n", fmt); cfg = fimc_read(EXYNOS_CIEXTEN); @@ -911,8 +908,7 @@ static int fimc_dst_set_transf(struct device *dev, struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; u32 cfg; - DRM_DEBUG_KMS("%s:degree[%d]flip[0x%x]\n", __func__, - degree, flip); + DRM_DEBUG_KMS("degree[%d]flip[0x%x]\n", degree, flip); cfg = fimc_read(EXYNOS_CITRGFMT); cfg &= ~EXYNOS_CITRGFMT_FLIP_MASK; @@ -962,7 +958,7 @@ static int fimc_dst_set_transf(struct device *dev, static int fimc_get_ratio_shift(u32 src, u32 dst, u32 *ratio, u32 *shift) { - DRM_DEBUG_KMS("%s:src[%d]dst[%d]\n", __func__, src, dst); + DRM_DEBUG_KMS("src[%d]dst[%d]\n", src, dst); if (src >= dst * 64) { DRM_ERROR("failed to make ratio and shift.\n"); @@ -1031,20 +1027,20 @@ static int fimc_set_prescaler(struct fimc_context *ctx, struct fimc_scaler *sc, pre_dst_width = src_w / pre_hratio; pre_dst_height = src_h / pre_vratio; - DRM_DEBUG_KMS("%s:pre_dst_width[%d]pre_dst_height[%d]\n", __func__, + DRM_DEBUG_KMS("pre_dst_width[%d]pre_dst_height[%d]\n", pre_dst_width, pre_dst_height); - DRM_DEBUG_KMS("%s:pre_hratio[%d]hfactor[%d]pre_vratio[%d]vfactor[%d]\n", - __func__, pre_hratio, hfactor, pre_vratio, vfactor); + DRM_DEBUG_KMS("pre_hratio[%d]hfactor[%d]pre_vratio[%d]vfactor[%d]\n", + pre_hratio, hfactor, pre_vratio, vfactor); sc->hratio = (src_w << 14) / (dst_w << hfactor); sc->vratio = (src_h << 14) / (dst_h << vfactor); sc->up_h = (dst_w >= src_w) ? true : false; sc->up_v = (dst_h >= src_h) ? true : false; - DRM_DEBUG_KMS("%s:hratio[%d]vratio[%d]up_h[%d]up_v[%d]\n", - __func__, sc->hratio, sc->vratio, sc->up_h, sc->up_v); + DRM_DEBUG_KMS("hratio[%d]vratio[%d]up_h[%d]up_v[%d]\n", + sc->hratio, sc->vratio, sc->up_h, sc->up_v); shfactor = FIMC_SHFACTOR - (hfactor + vfactor); - DRM_DEBUG_KMS("%s:shfactor[%d]\n", __func__, shfactor); + DRM_DEBUG_KMS("shfactor[%d]\n", shfactor); cfg = (EXYNOS_CISCPRERATIO_SHFACTOR(shfactor) | EXYNOS_CISCPRERATIO_PREHORRATIO(pre_hratio) | @@ -1062,10 +1058,10 @@ static void fimc_set_scaler(struct fimc_context *ctx, struct fimc_scaler *sc) { u32 cfg, cfg_ext; - DRM_DEBUG_KMS("%s:range[%d]bypass[%d]up_h[%d]up_v[%d]\n", - __func__, sc->range, sc->bypass, sc->up_h, sc->up_v); - DRM_DEBUG_KMS("%s:hratio[%d]vratio[%d]\n", - __func__, sc->hratio, sc->vratio); + DRM_DEBUG_KMS("range[%d]bypass[%d]up_h[%d]up_v[%d]\n", + sc->range, sc->bypass, sc->up_h, sc->up_v); + DRM_DEBUG_KMS("hratio[%d]vratio[%d]\n", + sc->hratio, sc->vratio); cfg = fimc_read(EXYNOS_CISCCTRL); cfg &= ~(EXYNOS_CISCCTRL_SCALERBYPASS | @@ -1105,8 +1101,8 @@ static int fimc_dst_set_size(struct device *dev, int swap, struct drm_exynos_sz img_sz = *sz; u32 cfg; - DRM_DEBUG_KMS("%s:swap[%d]hsize[%d]vsize[%d]\n", - __func__, swap, sz->hsize, sz->vsize); + DRM_DEBUG_KMS("swap[%d]hsize[%d]vsize[%d]\n", + swap, sz->hsize, sz->vsize); /* original size */ cfg = (EXYNOS_ORGOSIZE_HORIZONTAL(img_sz.hsize) | @@ -1114,8 +1110,7 @@ static int fimc_dst_set_size(struct device *dev, int swap, fimc_write(cfg, EXYNOS_ORGOSIZE); - DRM_DEBUG_KMS("%s:x[%d]y[%d]w[%d]h[%d]\n", - __func__, pos->x, pos->y, pos->w, pos->h); + DRM_DEBUG_KMS("x[%d]y[%d]w[%d]h[%d]\n", pos->x, pos->y, pos->w, pos->h); /* CSC ITU */ cfg = fimc_read(EXYNOS_CIGCTRL); @@ -1172,7 +1167,7 @@ static int fimc_dst_get_buf_seq(struct fimc_context *ctx) if (cfg & (mask << i)) buf_num++; - DRM_DEBUG_KMS("%s:buf_num[%d]\n", __func__, buf_num); + DRM_DEBUG_KMS("buf_num[%d]\n", buf_num); return buf_num; } @@ -1186,8 +1181,7 @@ static int fimc_dst_set_buf_seq(struct fimc_context *ctx, u32 buf_id, u32 mask = 0x00000001 << buf_id; int ret = 0; - DRM_DEBUG_KMS("%s:buf_id[%d]buf_type[%d]\n", __func__, - buf_id, buf_type); + DRM_DEBUG_KMS("buf_id[%d]buf_type[%d]\n", buf_id, buf_type); mutex_lock(&ctx->lock); @@ -1244,7 +1238,7 @@ static int fimc_dst_set_addr(struct device *dev, property = &c_node->property; - DRM_DEBUG_KMS("%s:prop_id[%d]buf_id[%d]buf_type[%d]\n", __func__, + DRM_DEBUG_KMS("prop_id[%d]buf_id[%d]buf_type[%d]\n", property->prop_id, buf_id, buf_type); if (buf_id > FIMC_MAX_DST) { @@ -1294,7 +1288,7 @@ static struct exynos_drm_ipp_ops fimc_dst_ops = { static int fimc_clk_ctrl(struct fimc_context *ctx, bool enable) { - DRM_DEBUG_KMS("%s:enable[%d]\n", __func__, enable); + DRM_DEBUG_KMS("enable[%d]\n", enable); if (enable) { clk_prepare_enable(ctx->clocks[FIMC_CLK_GATE]); @@ -1318,7 +1312,7 @@ static irqreturn_t fimc_irq_handler(int irq, void *dev_id) c_node->event_work; int buf_id; - DRM_DEBUG_KMS("%s:fimc id[%d]\n", __func__, ctx->id); + DRM_DEBUG_KMS("fimc id[%d]\n", ctx->id); fimc_clear_irq(ctx); if (fimc_check_ovf(ctx)) @@ -1331,7 +1325,7 @@ static irqreturn_t fimc_irq_handler(int irq, void *dev_id) if (buf_id < 0) return IRQ_HANDLED; - DRM_DEBUG_KMS("%s:buf_id[%d]\n", __func__, buf_id); + DRM_DEBUG_KMS("buf_id[%d]\n", buf_id); if (fimc_dst_set_buf_seq(ctx, buf_id, IPP_BUF_DEQUEUE) < 0) { DRM_ERROR("failed to dequeue.\n"); @@ -1392,7 +1386,7 @@ static inline bool fimc_check_drm_flip(enum drm_exynos_flip flip) case EXYNOS_DRM_FLIP_BOTH: return true; default: - DRM_DEBUG_KMS("%s:invalid flip\n", __func__); + DRM_DEBUG_KMS("invalid flip\n"); return false; } } @@ -1554,7 +1548,7 @@ static int fimc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd) int ret, i; u32 cfg0, cfg1; - DRM_DEBUG_KMS("%s:cmd[%d]\n", __func__, cmd); + DRM_DEBUG_KMS("cmd[%d]\n", cmd); if (!c_node) { DRM_ERROR("failed to get c_node.\n"); @@ -1663,7 +1657,7 @@ static void fimc_ippdrv_stop(struct device *dev, enum drm_exynos_ipp_cmd cmd) struct drm_exynos_ipp_set_wb set_wb = {0, 0}; u32 cfg; - DRM_DEBUG_KMS("%s:cmd[%d]\n", __func__, cmd); + DRM_DEBUG_KMS("cmd[%d]\n", cmd); switch (cmd) { case IPP_CMD_M2M: @@ -1853,8 +1847,7 @@ static int fimc_probe(struct platform_device *pdev) goto err_put_clk; } - DRM_DEBUG_KMS("%s:id[%d]ippdrv[0x%x]\n", __func__, ctx->id, - (int)ippdrv); + DRM_DEBUG_KMS("id[%d]ippdrv[0x%x]\n", ctx->id, (int)ippdrv); mutex_init(&ctx->lock); platform_set_drvdata(pdev, ctx); @@ -1901,7 +1894,7 @@ static int fimc_suspend(struct device *dev) { struct fimc_context *ctx = get_fimc_context(dev); - DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id); + DRM_DEBUG_KMS("id[%d]\n", ctx->id); if (pm_runtime_suspended(dev)) return 0; @@ -1913,7 +1906,7 @@ static int fimc_resume(struct device *dev) { struct fimc_context *ctx = get_fimc_context(dev); - DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id); + DRM_DEBUG_KMS("id[%d]\n", ctx->id); if (!pm_runtime_suspended(dev)) return fimc_clk_ctrl(ctx, true); @@ -1927,7 +1920,7 @@ static int fimc_runtime_suspend(struct device *dev) { struct fimc_context *ctx = get_fimc_context(dev); - DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id); + DRM_DEBUG_KMS("id[%d]\n", ctx->id); return fimc_clk_ctrl(ctx, false); } @@ -1936,7 +1929,7 @@ static int fimc_runtime_resume(struct device *dev) { struct fimc_context *ctx = get_fimc_context(dev); - DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id); + DRM_DEBUG_KMS("id[%d]\n", ctx->id); return fimc_clk_ctrl(ctx, true); } diff --git a/drivers/gpu/drm/exynos/exynos_drm_gsc.c b/drivers/gpu/drm/exynos/exynos_drm_gsc.c index 398a0b31ddd0..472e3b25e7f2 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gsc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gsc.c @@ -456,7 +456,7 @@ static void gsc_handle_irq(struct gsc_context *ctx, bool enable, { u32 cfg; - DRM_DEBUG_KMS("%s:enable[%d]overflow[%d]level[%d]\n", __func__, + DRM_DEBUG_KMS("enable[%d]overflow[%d]level[%d]\n", enable, overflow, done); cfg = gsc_read(GSC_IRQ); @@ -487,7 +487,7 @@ static int gsc_src_set_fmt(struct device *dev, u32 fmt) struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; u32 cfg; - DRM_DEBUG_KMS("%s:fmt[0x%x]\n", __func__, fmt); + DRM_DEBUG_KMS("fmt[0x%x]\n", fmt); cfg = gsc_read(GSC_IN_CON); cfg &= ~(GSC_IN_RGB_TYPE_MASK | GSC_IN_YUV422_1P_ORDER_MASK | @@ -563,8 +563,7 @@ static int gsc_src_set_transf(struct device *dev, struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; u32 cfg; - DRM_DEBUG_KMS("%s:degree[%d]flip[0x%x]\n", __func__, - degree, flip); + DRM_DEBUG_KMS("degree[%d]flip[0x%x]\n", degree, flip); cfg = gsc_read(GSC_IN_CON); cfg &= ~GSC_IN_ROT_MASK; @@ -612,8 +611,8 @@ static int gsc_src_set_size(struct device *dev, int swap, struct gsc_scaler *sc = &ctx->sc; u32 cfg; - DRM_DEBUG_KMS("%s:swap[%d]x[%d]y[%d]w[%d]h[%d]\n", - __func__, swap, pos->x, pos->y, pos->w, pos->h); + DRM_DEBUG_KMS("swap[%d]x[%d]y[%d]w[%d]h[%d]\n", + swap, pos->x, pos->y, pos->w, pos->h); if (swap) { img_pos.w = pos->h; @@ -630,8 +629,7 @@ static int gsc_src_set_size(struct device *dev, int swap, GSC_CROPPED_HEIGHT(img_pos.h)); gsc_write(cfg, GSC_CROPPED_SIZE); - DRM_DEBUG_KMS("%s:hsize[%d]vsize[%d]\n", - __func__, sz->hsize, sz->vsize); + DRM_DEBUG_KMS("hsize[%d]vsize[%d]\n", sz->hsize, sz->vsize); /* original size */ cfg = gsc_read(GSC_SRCIMG_SIZE); @@ -646,8 +644,7 @@ static int gsc_src_set_size(struct device *dev, int swap, cfg = gsc_read(GSC_IN_CON); cfg &= ~GSC_IN_RGB_TYPE_MASK; - DRM_DEBUG_KMS("%s:width[%d]range[%d]\n", - __func__, pos->w, sc->range); + DRM_DEBUG_KMS("width[%d]range[%d]\n", pos->w, sc->range); if (pos->w >= GSC_WIDTH_ITU_709) if (sc->range) @@ -673,8 +670,7 @@ static int gsc_src_set_buf_seq(struct gsc_context *ctx, u32 buf_id, u32 cfg; u32 mask = 0x00000001 << buf_id; - DRM_DEBUG_KMS("%s:buf_id[%d]buf_type[%d]\n", __func__, - buf_id, buf_type); + DRM_DEBUG_KMS("buf_id[%d]buf_type[%d]\n", buf_id, buf_type); /* mask register set */ cfg = gsc_read(GSC_IN_BASE_ADDR_Y_MASK); @@ -717,7 +713,7 @@ static int gsc_src_set_addr(struct device *dev, property = &c_node->property; - DRM_DEBUG_KMS("%s:prop_id[%d]buf_id[%d]buf_type[%d]\n", __func__, + DRM_DEBUG_KMS("prop_id[%d]buf_id[%d]buf_type[%d]\n", property->prop_id, buf_id, buf_type); if (buf_id > GSC_MAX_SRC) { @@ -761,7 +757,7 @@ static int gsc_dst_set_fmt(struct device *dev, u32 fmt) struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; u32 cfg; - DRM_DEBUG_KMS("%s:fmt[0x%x]\n", __func__, fmt); + DRM_DEBUG_KMS("fmt[0x%x]\n", fmt); cfg = gsc_read(GSC_OUT_CON); cfg &= ~(GSC_OUT_RGB_TYPE_MASK | GSC_OUT_YUV422_1P_ORDER_MASK | @@ -834,8 +830,7 @@ static int gsc_dst_set_transf(struct device *dev, struct exynos_drm_ippdrv *ippdrv = &ctx->ippdrv; u32 cfg; - DRM_DEBUG_KMS("%s:degree[%d]flip[0x%x]\n", __func__, - degree, flip); + DRM_DEBUG_KMS("degree[%d]flip[0x%x]\n", degree, flip); cfg = gsc_read(GSC_IN_CON); cfg &= ~GSC_IN_ROT_MASK; @@ -877,7 +872,7 @@ static int gsc_dst_set_transf(struct device *dev, static int gsc_get_ratio_shift(u32 src, u32 dst, u32 *ratio) { - DRM_DEBUG_KMS("%s:src[%d]dst[%d]\n", __func__, src, dst); + DRM_DEBUG_KMS("src[%d]dst[%d]\n", src, dst); if (src >= dst * 8) { DRM_ERROR("failed to make ratio and shift.\n"); @@ -940,20 +935,19 @@ static int gsc_set_prescaler(struct gsc_context *ctx, struct gsc_scaler *sc, return ret; } - DRM_DEBUG_KMS("%s:pre_hratio[%d]pre_vratio[%d]\n", - __func__, sc->pre_hratio, sc->pre_vratio); + DRM_DEBUG_KMS("pre_hratio[%d]pre_vratio[%d]\n", + sc->pre_hratio, sc->pre_vratio); sc->main_hratio = (src_w << 16) / dst_w; sc->main_vratio = (src_h << 16) / dst_h; - DRM_DEBUG_KMS("%s:main_hratio[%ld]main_vratio[%ld]\n", - __func__, sc->main_hratio, sc->main_vratio); + DRM_DEBUG_KMS("main_hratio[%ld]main_vratio[%ld]\n", + sc->main_hratio, sc->main_vratio); gsc_get_prescaler_shfactor(sc->pre_hratio, sc->pre_vratio, &sc->pre_shfactor); - DRM_DEBUG_KMS("%s:pre_shfactor[%d]\n", __func__, - sc->pre_shfactor); + DRM_DEBUG_KMS("pre_shfactor[%d]\n", sc->pre_shfactor); cfg = (GSC_PRESC_SHFACTOR(sc->pre_shfactor) | GSC_PRESC_H_RATIO(sc->pre_hratio) | @@ -1019,8 +1013,8 @@ static void gsc_set_scaler(struct gsc_context *ctx, struct gsc_scaler *sc) { u32 cfg; - DRM_DEBUG_KMS("%s:main_hratio[%ld]main_vratio[%ld]\n", - __func__, sc->main_hratio, sc->main_vratio); + DRM_DEBUG_KMS("main_hratio[%ld]main_vratio[%ld]\n", + sc->main_hratio, sc->main_vratio); gsc_set_h_coef(ctx, sc->main_hratio); cfg = GSC_MAIN_H_RATIO_VALUE(sc->main_hratio); @@ -1039,8 +1033,8 @@ static int gsc_dst_set_size(struct device *dev, int swap, struct gsc_scaler *sc = &ctx->sc; u32 cfg; - DRM_DEBUG_KMS("%s:swap[%d]x[%d]y[%d]w[%d]h[%d]\n", - __func__, swap, pos->x, pos->y, pos->w, pos->h); + DRM_DEBUG_KMS("swap[%d]x[%d]y[%d]w[%d]h[%d]\n", + swap, pos->x, pos->y, pos->w, pos->h); if (swap) { img_pos.w = pos->h; @@ -1056,8 +1050,7 @@ static int gsc_dst_set_size(struct device *dev, int swap, cfg = (GSC_SCALED_WIDTH(img_pos.w) | GSC_SCALED_HEIGHT(img_pos.h)); gsc_write(cfg, GSC_SCALED_SIZE); - DRM_DEBUG_KMS("%s:hsize[%d]vsize[%d]\n", - __func__, sz->hsize, sz->vsize); + DRM_DEBUG_KMS("hsize[%d]vsize[%d]\n", sz->hsize, sz->vsize); /* original size */ cfg = gsc_read(GSC_DSTIMG_SIZE); @@ -1070,8 +1063,7 @@ static int gsc_dst_set_size(struct device *dev, int swap, cfg = gsc_read(GSC_OUT_CON); cfg &= ~GSC_OUT_RGB_TYPE_MASK; - DRM_DEBUG_KMS("%s:width[%d]range[%d]\n", - __func__, pos->w, sc->range); + DRM_DEBUG_KMS("width[%d]range[%d]\n", pos->w, sc->range); if (pos->w >= GSC_WIDTH_ITU_709) if (sc->range) @@ -1100,7 +1092,7 @@ static int gsc_dst_get_buf_seq(struct gsc_context *ctx) if (cfg & (mask << i)) buf_num--; - DRM_DEBUG_KMS("%s:buf_num[%d]\n", __func__, buf_num); + DRM_DEBUG_KMS("buf_num[%d]\n", buf_num); return buf_num; } @@ -1114,8 +1106,7 @@ static int gsc_dst_set_buf_seq(struct gsc_context *ctx, u32 buf_id, u32 mask = 0x00000001 << buf_id; int ret = 0; - DRM_DEBUG_KMS("%s:buf_id[%d]buf_type[%d]\n", __func__, - buf_id, buf_type); + DRM_DEBUG_KMS("buf_id[%d]buf_type[%d]\n", buf_id, buf_type); mutex_lock(&ctx->lock); @@ -1173,7 +1164,7 @@ static int gsc_dst_set_addr(struct device *dev, property = &c_node->property; - DRM_DEBUG_KMS("%s:prop_id[%d]buf_id[%d]buf_type[%d]\n", __func__, + DRM_DEBUG_KMS("prop_id[%d]buf_id[%d]buf_type[%d]\n", property->prop_id, buf_id, buf_type); if (buf_id > GSC_MAX_DST) { @@ -1213,7 +1204,7 @@ static struct exynos_drm_ipp_ops gsc_dst_ops = { static int gsc_clk_ctrl(struct gsc_context *ctx, bool enable) { - DRM_DEBUG_KMS("%s:enable[%d]\n", __func__, enable); + DRM_DEBUG_KMS("enable[%d]\n", enable); if (enable) { clk_enable(ctx->gsc_clk); @@ -1232,7 +1223,7 @@ static int gsc_get_src_buf_index(struct gsc_context *ctx) u32 buf_id = GSC_MAX_SRC; int ret; - DRM_DEBUG_KMS("%s:gsc id[%d]\n", __func__, ctx->id); + DRM_DEBUG_KMS("gsc id[%d]\n", ctx->id); cfg = gsc_read(GSC_IN_BASE_ADDR_Y_MASK); curr_index = GSC_IN_CURR_GET_INDEX(cfg); @@ -1255,7 +1246,7 @@ static int gsc_get_src_buf_index(struct gsc_context *ctx) return ret; } - DRM_DEBUG_KMS("%s:cfg[0x%x]curr_index[%d]buf_id[%d]\n", __func__, cfg, + DRM_DEBUG_KMS("cfg[0x%x]curr_index[%d]buf_id[%d]\n", cfg, curr_index, buf_id); return buf_id; @@ -1267,7 +1258,7 @@ static int gsc_get_dst_buf_index(struct gsc_context *ctx) u32 buf_id = GSC_MAX_DST; int ret; - DRM_DEBUG_KMS("%s:gsc id[%d]\n", __func__, ctx->id); + DRM_DEBUG_KMS("gsc id[%d]\n", ctx->id); cfg = gsc_read(GSC_OUT_BASE_ADDR_Y_MASK); curr_index = GSC_OUT_CURR_GET_INDEX(cfg); @@ -1290,7 +1281,7 @@ static int gsc_get_dst_buf_index(struct gsc_context *ctx) return ret; } - DRM_DEBUG_KMS("%s:cfg[0x%x]curr_index[%d]buf_id[%d]\n", __func__, cfg, + DRM_DEBUG_KMS("cfg[0x%x]curr_index[%d]buf_id[%d]\n", cfg, curr_index, buf_id); return buf_id; @@ -1306,7 +1297,7 @@ static irqreturn_t gsc_irq_handler(int irq, void *dev_id) u32 status; int buf_id[EXYNOS_DRM_OPS_MAX]; - DRM_DEBUG_KMS("%s:gsc id[%d]\n", __func__, ctx->id); + DRM_DEBUG_KMS("gsc id[%d]\n", ctx->id); status = gsc_read(GSC_IRQ); if (status & GSC_IRQ_STATUS_OR_IRQ) { @@ -1327,7 +1318,7 @@ static irqreturn_t gsc_irq_handler(int irq, void *dev_id) if (buf_id[EXYNOS_DRM_OPS_DST] < 0) return IRQ_HANDLED; - DRM_DEBUG_KMS("%s:buf_id_src[%d]buf_id_dst[%d]\n", __func__, + DRM_DEBUG_KMS("buf_id_src[%d]buf_id_dst[%d]\n", buf_id[EXYNOS_DRM_OPS_SRC], buf_id[EXYNOS_DRM_OPS_DST]); event_work->ippdrv = ippdrv; @@ -1388,7 +1379,7 @@ static inline bool gsc_check_drm_flip(enum drm_exynos_flip flip) case EXYNOS_DRM_FLIP_BOTH: return true; default: - DRM_DEBUG_KMS("%s:invalid flip\n", __func__); + DRM_DEBUG_KMS("invalid flip\n"); return false; } } @@ -1539,7 +1530,7 @@ static int gsc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd) u32 cfg; int ret, i; - DRM_DEBUG_KMS("%s:cmd[%d]\n", __func__, cmd); + DRM_DEBUG_KMS("cmd[%d]\n", cmd); if (!c_node) { DRM_ERROR("failed to get c_node.\n"); @@ -1633,7 +1624,7 @@ static void gsc_ippdrv_stop(struct device *dev, enum drm_exynos_ipp_cmd cmd) struct drm_exynos_ipp_set_wb set_wb = {0, 0}; u32 cfg; - DRM_DEBUG_KMS("%s:cmd[%d]\n", __func__, cmd); + DRM_DEBUG_KMS("cmd[%d]\n", cmd); switch (cmd) { case IPP_CMD_M2M: @@ -1718,8 +1709,7 @@ static int gsc_probe(struct platform_device *pdev) return ret; } - DRM_DEBUG_KMS("%s:id[%d]ippdrv[0x%x]\n", __func__, ctx->id, - (int)ippdrv); + DRM_DEBUG_KMS("id[%d]ippdrv[0x%x]\n", ctx->id, (int)ippdrv); mutex_init(&ctx->lock); platform_set_drvdata(pdev, ctx); @@ -1762,7 +1752,7 @@ static int gsc_suspend(struct device *dev) { struct gsc_context *ctx = get_gsc_context(dev); - DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id); + DRM_DEBUG_KMS("id[%d]\n", ctx->id); if (pm_runtime_suspended(dev)) return 0; @@ -1774,7 +1764,7 @@ static int gsc_resume(struct device *dev) { struct gsc_context *ctx = get_gsc_context(dev); - DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id); + DRM_DEBUG_KMS("id[%d]\n", ctx->id); if (!pm_runtime_suspended(dev)) return gsc_clk_ctrl(ctx, true); @@ -1788,7 +1778,7 @@ static int gsc_runtime_suspend(struct device *dev) { struct gsc_context *ctx = get_gsc_context(dev); - DRM_DEBUG_KMS("%s:id[%d]\n", __func__, ctx->id); + DRM_DEBUG_KMS("id[%d]\n", ctx->id); return gsc_clk_ctrl(ctx, false); } diff --git a/drivers/gpu/drm/exynos/exynos_drm_ipp.c b/drivers/gpu/drm/exynos/exynos_drm_ipp.c index 32d174ff1c50..01cb9a03aac0 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_ipp.c +++ b/drivers/gpu/drm/exynos/exynos_drm_ipp.c @@ -173,7 +173,7 @@ static void *ipp_find_obj(struct idr *id_idr, struct mutex *lock, u32 id) { void *obj; - DRM_DEBUG_KMS("%s:id[%d]\n", __func__, id); + DRM_DEBUG_KMS("id[%d]\n", id); mutex_lock(lock); @@ -210,7 +210,7 @@ static struct exynos_drm_ippdrv *ipp_find_driver(struct ipp_context *ctx, struct exynos_drm_ippdrv *ippdrv; u32 ipp_id = property->ipp_id; - DRM_DEBUG_KMS("%s:ipp_id[%d]\n", __func__, ipp_id); + DRM_DEBUG_KMS("ipp_id[%d]\n", ipp_id); if (ipp_id) { /* find ipp driver using idr */ @@ -251,14 +251,13 @@ static struct exynos_drm_ippdrv *ipp_find_driver(struct ipp_context *ctx, */ list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) { if (ipp_check_dedicated(ippdrv, property->cmd)) { - DRM_DEBUG_KMS("%s:used device.\n", __func__); + DRM_DEBUG_KMS("used device.\n"); continue; } if (ippdrv->check_property && ippdrv->check_property(ippdrv->dev, property)) { - DRM_DEBUG_KMS("%s:not support property.\n", - __func__); + DRM_DEBUG_KMS("not support property.\n"); continue; } @@ -277,10 +276,10 @@ static struct exynos_drm_ippdrv *ipp_find_drv_by_handle(u32 prop_id) struct drm_exynos_ipp_cmd_node *c_node; int count = 0; - DRM_DEBUG_KMS("%s:prop_id[%d]\n", __func__, prop_id); + DRM_DEBUG_KMS("prop_id[%d]\n", prop_id); if (list_empty(&exynos_drm_ippdrv_list)) { - DRM_DEBUG_KMS("%s:ippdrv_list is empty.\n", __func__); + DRM_DEBUG_KMS("ippdrv_list is empty.\n"); return ERR_PTR(-ENODEV); } @@ -290,8 +289,7 @@ static struct exynos_drm_ippdrv *ipp_find_drv_by_handle(u32 prop_id) * e.g PAUSE state, queue buf, command contro. */ list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) { - DRM_DEBUG_KMS("%s:count[%d]ippdrv[0x%x]\n", __func__, - count++, (int)ippdrv); + DRM_DEBUG_KMS("count[%d]ippdrv[0x%x]\n", count++, (int)ippdrv); if (!list_empty(&ippdrv->cmd_list)) { list_for_each_entry(c_node, &ippdrv->cmd_list, list) @@ -324,7 +322,7 @@ int exynos_drm_ipp_get_property(struct drm_device *drm_dev, void *data, return -EINVAL; } - DRM_DEBUG_KMS("%s:ipp_id[%d]\n", __func__, prop_list->ipp_id); + DRM_DEBUG_KMS("ipp_id[%d]\n", prop_list->ipp_id); if (!prop_list->ipp_id) { list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) @@ -363,11 +361,11 @@ static void ipp_print_property(struct drm_exynos_ipp_property *property, struct drm_exynos_pos *pos = &config->pos; struct drm_exynos_sz *sz = &config->sz; - DRM_DEBUG_KMS("%s:prop_id[%d]ops[%s]fmt[0x%x]\n", - __func__, property->prop_id, idx ? "dst" : "src", config->fmt); + DRM_DEBUG_KMS("prop_id[%d]ops[%s]fmt[0x%x]\n", + property->prop_id, idx ? "dst" : "src", config->fmt); - DRM_DEBUG_KMS("%s:pos[%d %d %d %d]sz[%d %d]f[%d]r[%d]\n", - __func__, pos->x, pos->y, pos->w, pos->h, + DRM_DEBUG_KMS("pos[%d %d %d %d]sz[%d %d]f[%d]r[%d]\n", + pos->x, pos->y, pos->w, pos->h, sz->hsize, sz->vsize, config->flip, config->degree); } @@ -377,7 +375,7 @@ static int ipp_find_and_set_property(struct drm_exynos_ipp_property *property) struct drm_exynos_ipp_cmd_node *c_node; u32 prop_id = property->prop_id; - DRM_DEBUG_KMS("%s:prop_id[%d]\n", __func__, prop_id); + DRM_DEBUG_KMS("prop_id[%d]\n", prop_id); ippdrv = ipp_find_drv_by_handle(prop_id); if (IS_ERR(ippdrv)) { @@ -393,8 +391,8 @@ static int ipp_find_and_set_property(struct drm_exynos_ipp_property *property) list_for_each_entry(c_node, &ippdrv->cmd_list, list) { if ((c_node->property.prop_id == prop_id) && (c_node->state == IPP_STATE_STOP)) { - DRM_DEBUG_KMS("%s:found cmd[%d]ippdrv[0x%x]\n", - __func__, property->cmd, (int)ippdrv); + DRM_DEBUG_KMS("found cmd[%d]ippdrv[0x%x]\n", + property->cmd, (int)ippdrv); c_node->property = *property; return 0; @@ -472,7 +470,7 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data, * instead of allocation. */ if (property->prop_id) { - DRM_DEBUG_KMS("%s:prop_id[%d]\n", __func__, property->prop_id); + DRM_DEBUG_KMS("prop_id[%d]\n", property->prop_id); return ipp_find_and_set_property(property); } @@ -498,8 +496,8 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data, goto err_clear; } - DRM_DEBUG_KMS("%s:created prop_id[%d]cmd[%d]ippdrv[0x%x]\n", - __func__, property->prop_id, property->cmd, (int)ippdrv); + DRM_DEBUG_KMS("created prop_id[%d]cmd[%d]ippdrv[0x%x]\n", + property->prop_id, property->cmd, (int)ippdrv); /* stored property information and ippdrv in private data */ c_node->priv = priv; @@ -584,20 +582,19 @@ static int ipp_check_mem_list(struct drm_exynos_ipp_cmd_node *c_node) head = &c_node->mem_list[i]; if (list_empty(head)) { - DRM_DEBUG_KMS("%s:%s memory empty.\n", __func__, - i ? "dst" : "src"); + DRM_DEBUG_KMS("%s memory empty.\n", i ? "dst" : "src"); continue; } /* find memory node entry */ list_for_each_entry(m_node, head, list) { - DRM_DEBUG_KMS("%s:%s,count[%d]m_node[0x%x]\n", __func__, + DRM_DEBUG_KMS("%s,count[%d]m_node[0x%x]\n", i ? "dst" : "src", count[i], (int)m_node); count[i]++; } } - DRM_DEBUG_KMS("%s:min[%d]max[%d]\n", __func__, + DRM_DEBUG_KMS("min[%d]max[%d]\n", min(count[EXYNOS_DRM_OPS_SRC], count[EXYNOS_DRM_OPS_DST]), max(count[EXYNOS_DRM_OPS_SRC], count[EXYNOS_DRM_OPS_DST])); @@ -626,15 +623,14 @@ static struct drm_exynos_ipp_mem_node struct list_head *head; int count = 0; - DRM_DEBUG_KMS("%s:buf_id[%d]\n", __func__, qbuf->buf_id); + DRM_DEBUG_KMS("buf_id[%d]\n", qbuf->buf_id); /* source/destination memory list */ head = &c_node->mem_list[qbuf->ops_id]; /* find memory node from memory list */ list_for_each_entry(m_node, head, list) { - DRM_DEBUG_KMS("%s:count[%d]m_node[0x%x]\n", - __func__, count++, (int)m_node); + DRM_DEBUG_KMS("count[%d]m_node[0x%x]\n", count++, (int)m_node); /* compare buffer id */ if (m_node->buf_id == qbuf->buf_id) @@ -651,7 +647,7 @@ static int ipp_set_mem_node(struct exynos_drm_ippdrv *ippdrv, struct exynos_drm_ipp_ops *ops = NULL; int ret = 0; - DRM_DEBUG_KMS("%s:node[0x%x]\n", __func__, (int)m_node); + DRM_DEBUG_KMS("node[0x%x]\n", (int)m_node); if (!m_node) { DRM_ERROR("invalid queue node.\n"); @@ -660,7 +656,7 @@ static int ipp_set_mem_node(struct exynos_drm_ippdrv *ippdrv, mutex_lock(&c_node->mem_lock); - DRM_DEBUG_KMS("%s:ops_id[%d]\n", __func__, m_node->ops_id); + DRM_DEBUG_KMS("ops_id[%d]\n", m_node->ops_id); /* get operations callback */ ops = ippdrv->ops[m_node->ops_id]; @@ -712,14 +708,11 @@ static struct drm_exynos_ipp_mem_node m_node->prop_id = qbuf->prop_id; m_node->buf_id = qbuf->buf_id; - DRM_DEBUG_KMS("%s:m_node[0x%x]ops_id[%d]\n", __func__, - (int)m_node, qbuf->ops_id); - DRM_DEBUG_KMS("%s:prop_id[%d]buf_id[%d]\n", __func__, - qbuf->prop_id, m_node->buf_id); + DRM_DEBUG_KMS("m_node[0x%x]ops_id[%d]\n", (int)m_node, qbuf->ops_id); + DRM_DEBUG_KMS("prop_id[%d]buf_id[%d]\n", qbuf->prop_id, m_node->buf_id); for_each_ipp_planar(i) { - DRM_DEBUG_KMS("%s:i[%d]handle[0x%x]\n", __func__, - i, qbuf->handle[i]); + DRM_DEBUG_KMS("i[%d]handle[0x%x]\n", i, qbuf->handle[i]); /* get dma address by handle */ if (qbuf->handle[i]) { @@ -732,9 +725,8 @@ static struct drm_exynos_ipp_mem_node buf_info.handles[i] = qbuf->handle[i]; buf_info.base[i] = *(dma_addr_t *) addr; - DRM_DEBUG_KMS("%s:i[%d]base[0x%x]hd[0x%x]\n", - __func__, i, buf_info.base[i], - (int)buf_info.handles[i]); + DRM_DEBUG_KMS("i[%d]base[0x%x]hd[0x%x]\n", + i, buf_info.base[i], (int)buf_info.handles[i]); } } @@ -758,7 +750,7 @@ static int ipp_put_mem_node(struct drm_device *drm_dev, { int i; - DRM_DEBUG_KMS("%s:node[0x%x]\n", __func__, (int)m_node); + DRM_DEBUG_KMS("node[0x%x]\n", (int)m_node); if (!m_node) { DRM_ERROR("invalid dequeue node.\n"); @@ -772,7 +764,7 @@ static int ipp_put_mem_node(struct drm_device *drm_dev, mutex_lock(&c_node->mem_lock); - DRM_DEBUG_KMS("%s:ops_id[%d]\n", __func__, m_node->ops_id); + DRM_DEBUG_KMS("ops_id[%d]\n", m_node->ops_id); /* put gem buffer */ for_each_ipp_planar(i) { @@ -804,8 +796,7 @@ static int ipp_get_event(struct drm_device *drm_dev, struct drm_exynos_ipp_send_event *e; unsigned long flags; - DRM_DEBUG_KMS("%s:ops_id[%d]buf_id[%d]\n", __func__, - qbuf->ops_id, qbuf->buf_id); + DRM_DEBUG_KMS("ops_id[%d]buf_id[%d]\n", qbuf->ops_id, qbuf->buf_id); e = kzalloc(sizeof(*e), GFP_KERNEL); @@ -838,13 +829,12 @@ static void ipp_put_event(struct drm_exynos_ipp_cmd_node *c_node, int count = 0; if (list_empty(&c_node->event_list)) { - DRM_DEBUG_KMS("%s:event_list is empty.\n", __func__); + DRM_DEBUG_KMS("event_list is empty.\n"); return; } list_for_each_entry_safe(e, te, &c_node->event_list, base.link) { - DRM_DEBUG_KMS("%s:count[%d]e[0x%x]\n", - __func__, count++, (int)e); + DRM_DEBUG_KMS("count[%d]e[0x%x]\n", count++, (int)e); /* * quf == NULL condition means all event deletion. @@ -905,12 +895,12 @@ static int ipp_queue_buf_with_run(struct device *dev, property = &c_node->property; if (c_node->state != IPP_STATE_START) { - DRM_DEBUG_KMS("%s:bypass for invalid state.\n" , __func__); + DRM_DEBUG_KMS("bypass for invalid state.\n"); return 0; } if (!ipp_check_mem_list(c_node)) { - DRM_DEBUG_KMS("%s:empty memory.\n", __func__); + DRM_DEBUG_KMS("empty memory.\n"); return 0; } @@ -973,8 +963,8 @@ int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev, void *data, return -EINVAL; } - DRM_DEBUG_KMS("%s:prop_id[%d]ops_id[%s]buf_id[%d]buf_type[%d]\n", - __func__, qbuf->prop_id, qbuf->ops_id ? "dst" : "src", + DRM_DEBUG_KMS("prop_id[%d]ops_id[%s]buf_id[%d]buf_type[%d]\n", + qbuf->prop_id, qbuf->ops_id ? "dst" : "src", qbuf->buf_id, qbuf->buf_type); /* find command node */ @@ -1106,7 +1096,7 @@ int exynos_drm_ipp_cmd_ctrl(struct drm_device *drm_dev, void *data, return -EINVAL; } - DRM_DEBUG_KMS("%s:ctrl[%d]prop_id[%d]\n", __func__, + DRM_DEBUG_KMS("ctrl[%d]prop_id[%d]\n", cmd_ctrl->ctrl, cmd_ctrl->prop_id); ippdrv = ipp_find_drv_by_handle(cmd_ctrl->prop_id); @@ -1181,7 +1171,7 @@ int exynos_drm_ipp_cmd_ctrl(struct drm_device *drm_dev, void *data, return -EINVAL; } - DRM_DEBUG_KMS("%s:done ctrl[%d]prop_id[%d]\n", __func__, + DRM_DEBUG_KMS("done ctrl[%d]prop_id[%d]\n", cmd_ctrl->ctrl, cmd_ctrl->prop_id); return 0; @@ -1217,7 +1207,7 @@ static int ipp_set_property(struct exynos_drm_ippdrv *ippdrv, return -EINVAL; } - DRM_DEBUG_KMS("%s:prop_id[%d]\n", __func__, property->prop_id); + DRM_DEBUG_KMS("prop_id[%d]\n", property->prop_id); /* reset h/w block */ if (ippdrv->reset && @@ -1278,13 +1268,13 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv, struct list_head *head; int ret, i; - DRM_DEBUG_KMS("%s:prop_id[%d]\n", __func__, property->prop_id); + DRM_DEBUG_KMS("prop_id[%d]\n", property->prop_id); /* store command info in ippdrv */ ippdrv->c_node = c_node; if (!ipp_check_mem_list(c_node)) { - DRM_DEBUG_KMS("%s:empty memory.\n", __func__); + DRM_DEBUG_KMS("empty memory.\n"); return -ENOMEM; } @@ -1311,8 +1301,7 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv, return ret; } - DRM_DEBUG_KMS("%s:m_node[0x%x]\n", - __func__, (int)m_node); + DRM_DEBUG_KMS("m_node[0x%x]\n", (int)m_node); ret = ipp_set_mem_node(ippdrv, c_node, m_node); if (ret) { @@ -1350,7 +1339,7 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv, return -EINVAL; } - DRM_DEBUG_KMS("%s:cmd[%d]\n", __func__, property->cmd); + DRM_DEBUG_KMS("cmd[%d]\n", property->cmd); /* start operations */ if (ippdrv->start) { @@ -1373,7 +1362,7 @@ static int ipp_stop_property(struct drm_device *drm_dev, struct list_head *head; int ret = 0, i; - DRM_DEBUG_KMS("%s:prop_id[%d]\n", __func__, property->prop_id); + DRM_DEBUG_KMS("prop_id[%d]\n", property->prop_id); /* put event */ ipp_put_event(c_node, NULL); @@ -1386,8 +1375,7 @@ static int ipp_stop_property(struct drm_device *drm_dev, head = &c_node->mem_list[i]; if (list_empty(head)) { - DRM_DEBUG_KMS("%s:mem_list is empty.\n", - __func__); + DRM_DEBUG_KMS("mem_list is empty.\n"); break; } @@ -1407,7 +1395,7 @@ static int ipp_stop_property(struct drm_device *drm_dev, head = &c_node->mem_list[EXYNOS_DRM_OPS_DST]; if (list_empty(head)) { - DRM_DEBUG_KMS("%s:mem_list is empty.\n", __func__); + DRM_DEBUG_KMS("mem_list is empty.\n"); break; } @@ -1424,7 +1412,7 @@ static int ipp_stop_property(struct drm_device *drm_dev, head = &c_node->mem_list[EXYNOS_DRM_OPS_SRC]; if (list_empty(head)) { - DRM_DEBUG_KMS("%s:mem_list is empty.\n", __func__); + DRM_DEBUG_KMS("mem_list is empty.\n"); break; } @@ -1516,7 +1504,7 @@ void ipp_sched_cmd(struct work_struct *work) break; } - DRM_DEBUG_KMS("%s:ctrl[%d] done.\n", __func__, cmd_work->ctrl); + DRM_DEBUG_KMS("ctrl[%d] done.\n", cmd_work->ctrl); err_unlock: mutex_unlock(&c_node->cmd_lock); @@ -1537,8 +1525,7 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv, int ret, i; for_each_ipp_ops(i) - DRM_DEBUG_KMS("%s:%s buf_id[%d]\n", __func__, - i ? "dst" : "src", buf_id[i]); + DRM_DEBUG_KMS("%s buf_id[%d]\n", i ? "dst" : "src", buf_id[i]); if (!drm_dev) { DRM_ERROR("failed to get drm_dev.\n"); @@ -1551,12 +1538,12 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv, } if (list_empty(&c_node->event_list)) { - DRM_DEBUG_KMS("%s:event list is empty.\n", __func__); + DRM_DEBUG_KMS("event list is empty.\n"); return 0; } if (!ipp_check_mem_list(c_node)) { - DRM_DEBUG_KMS("%s:empty memory.\n", __func__); + DRM_DEBUG_KMS("empty memory.\n"); return 0; } @@ -1575,7 +1562,7 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv, } tbuf_id[i] = m_node->buf_id; - DRM_DEBUG_KMS("%s:%s buf_id[%d]\n", __func__, + DRM_DEBUG_KMS("%s buf_id[%d]\n", i ? "dst" : "src", tbuf_id[i]); ret = ipp_put_mem_node(drm_dev, c_node, m_node); @@ -1643,8 +1630,7 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv, } do_gettimeofday(&now); - DRM_DEBUG_KMS("%s:tv_sec[%ld]tv_usec[%ld]\n" - , __func__, now.tv_sec, now.tv_usec); + DRM_DEBUG_KMS("tv_sec[%ld]tv_usec[%ld]\n", now.tv_sec, now.tv_usec); e->event.tv_sec = now.tv_sec; e->event.tv_usec = now.tv_usec; e->event.prop_id = property->prop_id; @@ -1658,7 +1644,7 @@ static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv, wake_up_interruptible(&e->base.file_priv->event_wait); spin_unlock_irqrestore(&drm_dev->event_lock, flags); - DRM_DEBUG_KMS("%s:done cmd[%d]prop_id[%d]buf_id[%d]\n", __func__, + DRM_DEBUG_KMS("done cmd[%d]prop_id[%d]buf_id[%d]\n", property->cmd, property->prop_id, tbuf_id[EXYNOS_DRM_OPS_DST]); return 0; @@ -1677,8 +1663,7 @@ void ipp_sched_event(struct work_struct *work) return; } - DRM_DEBUG_KMS("%s:buf_id[%d]\n", __func__, - event_work->buf_id[EXYNOS_DRM_OPS_DST]); + DRM_DEBUG_KMS("buf_id[%d]\n", event_work->buf_id[EXYNOS_DRM_OPS_DST]); ippdrv = event_work->ippdrv; if (!ippdrv) { @@ -1699,8 +1684,8 @@ void ipp_sched_event(struct work_struct *work) * or going out operations. */ if (c_node->state != IPP_STATE_START) { - DRM_DEBUG_KMS("%s:bypass state[%d]prop_id[%d]\n", - __func__, c_node->state, c_node->property.prop_id); + DRM_DEBUG_KMS("bypass state[%d]prop_id[%d]\n", + c_node->state, c_node->property.prop_id); goto err_completion; } @@ -1736,7 +1721,7 @@ static int ipp_subdrv_probe(struct drm_device *drm_dev, struct device *dev) goto err_idr; } - DRM_DEBUG_KMS("%s:count[%d]ippdrv[0x%x]ipp_id[%d]\n", __func__, + DRM_DEBUG_KMS("count[%d]ippdrv[0x%x]ipp_id[%d]\n", count++, (int)ippdrv, ippdrv->ipp_id); if (ippdrv->ipp_id == 0) { @@ -1806,7 +1791,7 @@ static int ipp_subdrv_open(struct drm_device *drm_dev, struct device *dev, INIT_LIST_HEAD(&priv->event_list); - DRM_DEBUG_KMS("%s:done priv[0x%x]\n", __func__, (int)priv); + DRM_DEBUG_KMS("done priv[0x%x]\n", (int)priv); return 0; } @@ -1820,10 +1805,10 @@ static void ipp_subdrv_close(struct drm_device *drm_dev, struct device *dev, struct drm_exynos_ipp_cmd_node *c_node, *tc_node; int count = 0; - DRM_DEBUG_KMS("%s:for priv[0x%x]\n", __func__, (int)priv); + DRM_DEBUG_KMS("for priv[0x%x]\n", (int)priv); if (list_empty(&exynos_drm_ippdrv_list)) { - DRM_DEBUG_KMS("%s:ippdrv_list is empty.\n", __func__); + DRM_DEBUG_KMS("ippdrv_list is empty.\n"); goto err_clear; } @@ -1833,8 +1818,8 @@ static void ipp_subdrv_close(struct drm_device *drm_dev, struct device *dev, list_for_each_entry_safe(c_node, tc_node, &ippdrv->cmd_list, list) { - DRM_DEBUG_KMS("%s:count[%d]ippdrv[0x%x]\n", - __func__, count++, (int)ippdrv); + DRM_DEBUG_KMS("count[%d]ippdrv[0x%x]\n", + count++, (int)ippdrv); if (c_node->priv == priv) { /* @@ -1955,7 +1940,7 @@ static int ipp_remove(struct platform_device *pdev) static int ipp_power_ctrl(struct ipp_context *ctx, bool enable) { - DRM_DEBUG_KMS("%s:enable[%d]\n", __func__, enable); + DRM_DEBUG_KMS("enable[%d]\n", enable); return 0; } diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.c b/drivers/gpu/drm/exynos/exynos_drm_rotator.c index b811e5c24f94..427640aa5148 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_rotator.c +++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.c @@ -244,7 +244,7 @@ static int rotator_src_set_size(struct device *dev, int swap, /* Get format */ fmt = rotator_reg_get_fmt(rot); if (!rotator_check_reg_fmt(fmt)) { - DRM_ERROR("%s:invalid format.\n", __func__); + DRM_ERROR("invalid format.\n"); return -EINVAL; } @@ -287,7 +287,7 @@ static int rotator_src_set_addr(struct device *dev, /* Get format */ fmt = rotator_reg_get_fmt(rot); if (!rotator_check_reg_fmt(fmt)) { - DRM_ERROR("%s:invalid format.\n", __func__); + DRM_ERROR("invalid format.\n"); return -EINVAL; } @@ -381,7 +381,7 @@ static int rotator_dst_set_size(struct device *dev, int swap, /* Get format */ fmt = rotator_reg_get_fmt(rot); if (!rotator_check_reg_fmt(fmt)) { - DRM_ERROR("%s:invalid format.\n", __func__); + DRM_ERROR("invalid format.\n"); return -EINVAL; } @@ -422,7 +422,7 @@ static int rotator_dst_set_addr(struct device *dev, /* Get format */ fmt = rotator_reg_get_fmt(rot); if (!rotator_check_reg_fmt(fmt)) { - DRM_ERROR("%s:invalid format.\n", __func__); + DRM_ERROR("invalid format.\n"); return -EINVAL; } @@ -500,7 +500,7 @@ static inline bool rotator_check_drm_fmt(u32 fmt) case DRM_FORMAT_NV12: return true; default: - DRM_DEBUG_KMS("%s:not support format\n", __func__); + DRM_DEBUG_KMS("not support format\n"); return false; } } @@ -514,7 +514,7 @@ static inline bool rotator_check_drm_flip(enum drm_exynos_flip flip) case EXYNOS_DRM_FLIP_BOTH: return true; default: - DRM_DEBUG_KMS("%s:invalid flip\n", __func__); + DRM_DEBUG_KMS("invalid flip\n"); return false; } } @@ -534,19 +534,18 @@ static int rotator_ippdrv_check_property(struct device *dev, /* Check format configuration */ if (src_config->fmt != dst_config->fmt) { - DRM_DEBUG_KMS("%s:not support csc feature\n", __func__); + DRM_DEBUG_KMS("not support csc feature\n"); return -EINVAL; } if (!rotator_check_drm_fmt(dst_config->fmt)) { - DRM_DEBUG_KMS("%s:invalid format\n", __func__); + DRM_DEBUG_KMS("invalid format\n"); return -EINVAL; } /* Check transform configuration */ if (src_config->degree != EXYNOS_DRM_DEGREE_0) { - DRM_DEBUG_KMS("%s:not support source-side rotation\n", - __func__); + DRM_DEBUG_KMS("not support source-side rotation\n"); return -EINVAL; } @@ -559,51 +558,47 @@ static int rotator_ippdrv_check_property(struct device *dev, /* No problem */ break; default: - DRM_DEBUG_KMS("%s:invalid degree\n", __func__); + DRM_DEBUG_KMS("invalid degree\n"); return -EINVAL; } if (src_config->flip != EXYNOS_DRM_FLIP_NONE) { - DRM_DEBUG_KMS("%s:not support source-side flip\n", __func__); + DRM_DEBUG_KMS("not support source-side flip\n"); return -EINVAL; } if (!rotator_check_drm_flip(dst_config->flip)) { - DRM_DEBUG_KMS("%s:invalid flip\n", __func__); + DRM_DEBUG_KMS("invalid flip\n"); return -EINVAL; } /* Check size configuration */ if ((src_pos->x + src_pos->w > src_sz->hsize) || (src_pos->y + src_pos->h > src_sz->vsize)) { - DRM_DEBUG_KMS("%s:out of source buffer bound\n", __func__); + DRM_DEBUG_KMS("out of source buffer bound\n"); return -EINVAL; } if (swap) { if ((dst_pos->x + dst_pos->h > dst_sz->vsize) || (dst_pos->y + dst_pos->w > dst_sz->hsize)) { - DRM_DEBUG_KMS("%s:out of destination buffer bound\n", - __func__); + DRM_DEBUG_KMS("out of destination buffer bound\n"); return -EINVAL; } if ((src_pos->w != dst_pos->h) || (src_pos->h != dst_pos->w)) { - DRM_DEBUG_KMS("%s:not support scale feature\n", - __func__); + DRM_DEBUG_KMS("not support scale feature\n"); return -EINVAL; } } else { if ((dst_pos->x + dst_pos->w > dst_sz->hsize) || (dst_pos->y + dst_pos->h > dst_sz->vsize)) { - DRM_DEBUG_KMS("%s:out of destination buffer bound\n", - __func__); + DRM_DEBUG_KMS("out of destination buffer bound\n"); return -EINVAL; } if ((src_pos->w != dst_pos->w) || (src_pos->h != dst_pos->h)) { - DRM_DEBUG_KMS("%s:not support scale feature\n", - __func__); + DRM_DEBUG_KMS("not support scale feature\n"); return -EINVAL; } } @@ -691,7 +686,7 @@ static int rotator_probe(struct platform_device *pdev) goto err_ippdrv_register; } - DRM_DEBUG_KMS("%s:ippdrv[0x%x]\n", __func__, (int)ippdrv); + DRM_DEBUG_KMS("ippdrv[0x%x]\n", (int)ippdrv); platform_set_drvdata(pdev, rot); diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 67692a362c6e..65bb9841317b 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -1634,8 +1634,8 @@ static void hdmi_mode_set(void *ctx, struct drm_display_mode *mode) struct hdmi_context *hdata = ctx; struct drm_display_mode *m = mode; - DRM_DEBUG_KMS("[%s]: xres=%d, yres=%d, refresh=%d, intl=%s\n", - __func__, m->hdisplay, m->vdisplay, + DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%s\n", + m->hdisplay, m->vdisplay, m->vrefresh, (m->flags & DRM_MODE_FLAG_INTERLACE) ? "INTERLACED" : "PROGERESSIVE"); @@ -1723,7 +1723,7 @@ static void hdmi_dpms(void *ctx, int mode) { struct hdmi_context *hdata = ctx; - DRM_DEBUG_KMS("[%d] %s mode %d\n", __LINE__, __func__, mode); + DRM_DEBUG_KMS("mode %d\n", mode); switch (mode) { case DRM_MODE_DPMS_ON: @@ -2073,7 +2073,7 @@ static int hdmi_suspend(struct device *dev) drm_helper_hpd_irq_event(ctx->drm_dev); if (pm_runtime_suspended(dev)) { - DRM_DEBUG_KMS("%s : Already suspended\n", __func__); + DRM_DEBUG_KMS("Already suspended\n"); return 0; } @@ -2092,7 +2092,7 @@ static int hdmi_resume(struct device *dev) enable_irq(hdata->irq); if (!pm_runtime_suspended(dev)) { - DRM_DEBUG_KMS("%s : Already resumed\n", __func__); + DRM_DEBUG_KMS("Already resumed\n"); return 0; } diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index e84f9e9fcd74..1f4cec12e326 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -770,7 +770,7 @@ static void mixer_win_commit(void *ctx, int win) { struct mixer_context *mixer_ctx = ctx; - DRM_DEBUG_KMS("[%d] %s, win: %d\n", __LINE__, __func__, win); + DRM_DEBUG_KMS("win: %d\n", win); mutex_lock(&mixer_ctx->mixer_mutex); if (!mixer_ctx->powered) { @@ -793,7 +793,7 @@ static void mixer_win_disable(void *ctx, int win) struct mixer_resources *res = &mixer_ctx->mixer_res; unsigned long flags; - DRM_DEBUG_KMS("[%d] %s, win: %d\n", __LINE__, __func__, win); + DRM_DEBUG_KMS("win: %d\n", win); mutex_lock(&mixer_ctx->mixer_mutex); if (!mixer_ctx->powered) { @@ -1238,7 +1238,7 @@ static int mixer_suspend(struct device *dev) struct mixer_context *ctx = drm_hdmi_ctx->ctx; if (pm_runtime_suspended(dev)) { - DRM_DEBUG_KMS("%s : Already suspended\n", __func__); + DRM_DEBUG_KMS("Already suspended\n"); return 0; } @@ -1253,7 +1253,7 @@ static int mixer_resume(struct device *dev) struct mixer_context *ctx = drm_hdmi_ctx->ctx; if (!pm_runtime_suspended(dev)) { - DRM_DEBUG_KMS("%s : Already resumed\n", __func__); + DRM_DEBUG_KMS("Already resumed\n"); return 0; } -- cgit v1.2.3 From 0bfb1f8bcbf298854276472e20ee66312aee5029 Mon Sep 17 00:00:00 2001 From: Sean Paul Date: Tue, 11 Jun 2013 12:24:02 +0530 Subject: drm/exynos: Prepare/Unprepare HDMI subsystem clocks Change the clk_enable/clk_disable calls in mixer and hdmi drivers into clk_prepare_enable/clk_disable_unprepare, respectively. Signed-off-by: Sean Paul Signed-off-by: Rahul Sharma Signed-off-by: Inki Dae --- drivers/gpu/drm/exynos/exynos_hdmi.c | 24 ++++++++++++------------ drivers/gpu/drm/exynos/exynos_mixer.c | 12 ++++++------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 65bb9841317b..83bd496ca6a3 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -1111,9 +1111,9 @@ static void hdmi_v13_mode_apply(struct hdmi_context *hdata) hdmi_regs_dump(hdata, "timing apply"); } - clk_disable(hdata->res.sclk_hdmi); + clk_disable_unprepare(hdata->res.sclk_hdmi); clk_set_parent(hdata->res.sclk_hdmi, hdata->res.sclk_hdmiphy); - clk_enable(hdata->res.sclk_hdmi); + clk_prepare_enable(hdata->res.sclk_hdmi); /* enable HDMI and timing generator */ hdmi_reg_writemask(hdata, HDMI_CON_0, ~0, HDMI_EN); @@ -1278,9 +1278,9 @@ static void hdmi_v14_mode_apply(struct hdmi_context *hdata) hdmi_regs_dump(hdata, "timing apply"); } - clk_disable(hdata->res.sclk_hdmi); + clk_disable_unprepare(hdata->res.sclk_hdmi); clk_set_parent(hdata->res.sclk_hdmi, hdata->res.sclk_hdmiphy); - clk_enable(hdata->res.sclk_hdmi); + clk_prepare_enable(hdata->res.sclk_hdmi); /* enable HDMI and timing generator */ hdmi_reg_writemask(hdata, HDMI_CON_0, ~0, HDMI_EN); @@ -1304,9 +1304,9 @@ static void hdmiphy_conf_reset(struct hdmi_context *hdata) u8 buffer[2]; u32 reg; - clk_disable(hdata->res.sclk_hdmi); + clk_disable_unprepare(hdata->res.sclk_hdmi); clk_set_parent(hdata->res.sclk_hdmi, hdata->res.sclk_pixel); - clk_enable(hdata->res.sclk_hdmi); + clk_prepare_enable(hdata->res.sclk_hdmi); /* operation mode */ buffer[0] = 0x1f; @@ -1683,9 +1683,9 @@ static void hdmi_poweron(struct hdmi_context *hdata) if (regulator_bulk_enable(res->regul_count, res->regul_bulk)) DRM_DEBUG_KMS("failed to enable regulator bulk\n"); - clk_enable(res->hdmiphy); - clk_enable(res->hdmi); - clk_enable(res->sclk_hdmi); + clk_prepare_enable(res->hdmiphy); + clk_prepare_enable(res->hdmi); + clk_prepare_enable(res->sclk_hdmi); hdmiphy_poweron(hdata); } @@ -1706,9 +1706,9 @@ static void hdmi_poweroff(struct hdmi_context *hdata) hdmiphy_conf_reset(hdata); hdmiphy_poweroff(hdata); - clk_disable(res->sclk_hdmi); - clk_disable(res->hdmi); - clk_disable(res->hdmiphy); + clk_disable_unprepare(res->sclk_hdmi); + clk_disable_unprepare(res->hdmi); + clk_disable_unprepare(res->hdmiphy); regulator_bulk_disable(res->regul_count, res->regul_bulk); mutex_lock(&hdata->hdmi_mutex); diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index 1f4cec12e326..f36f878f1e3a 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -892,10 +892,10 @@ static void mixer_poweron(struct mixer_context *ctx) ctx->powered = true; mutex_unlock(&ctx->mixer_mutex); - clk_enable(res->mixer); + clk_prepare_enable(res->mixer); if (ctx->vp_enabled) { - clk_enable(res->vp); - clk_enable(res->sclk_mixer); + clk_prepare_enable(res->vp); + clk_prepare_enable(res->sclk_mixer); } mixer_reg_write(res, MXR_INT_EN, ctx->int_en); @@ -917,10 +917,10 @@ static void mixer_poweroff(struct mixer_context *ctx) ctx->int_en = mixer_reg_read(res, MXR_INT_EN); - clk_disable(res->mixer); + clk_disable_unprepare(res->mixer); if (ctx->vp_enabled) { - clk_disable(res->vp); - clk_disable(res->sclk_mixer); + clk_disable_unprepare(res->vp); + clk_disable_unprepare(res->sclk_mixer); } mutex_lock(&ctx->mixer_mutex); -- cgit v1.2.3 From 59956d35a8618235ea715280b49447bb36f2c975 Mon Sep 17 00:00:00 2001 From: Rahul Sharma Date: Tue, 11 Jun 2013 12:24:03 +0530 Subject: drm/exynos: add mout_hdmi clock in hdmi driver to change parent HDMI driver needs to configure the mout_hdmi mux clock to change the parent between sclk_hdmiphy and sclk_pixel. Signed-off-by: Rahul Sharma Signed-off-by: Inki Dae --- drivers/gpu/drm/exynos/exynos_hdmi.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 83bd496ca6a3..ce13a23a8274 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -83,6 +83,7 @@ struct hdmi_resources { struct clk *sclk_pixel; struct clk *sclk_hdmiphy; struct clk *hdmiphy; + struct clk *mout_hdmi; struct regulator_bulk_data *regul_bulk; int regul_count; }; @@ -1112,7 +1113,7 @@ static void hdmi_v13_mode_apply(struct hdmi_context *hdata) } clk_disable_unprepare(hdata->res.sclk_hdmi); - clk_set_parent(hdata->res.sclk_hdmi, hdata->res.sclk_hdmiphy); + clk_set_parent(hdata->res.mout_hdmi, hdata->res.sclk_hdmiphy); clk_prepare_enable(hdata->res.sclk_hdmi); /* enable HDMI and timing generator */ @@ -1279,7 +1280,7 @@ static void hdmi_v14_mode_apply(struct hdmi_context *hdata) } clk_disable_unprepare(hdata->res.sclk_hdmi); - clk_set_parent(hdata->res.sclk_hdmi, hdata->res.sclk_hdmiphy); + clk_set_parent(hdata->res.mout_hdmi, hdata->res.sclk_hdmiphy); clk_prepare_enable(hdata->res.sclk_hdmi); /* enable HDMI and timing generator */ @@ -1305,7 +1306,7 @@ static void hdmiphy_conf_reset(struct hdmi_context *hdata) u32 reg; clk_disable_unprepare(hdata->res.sclk_hdmi); - clk_set_parent(hdata->res.sclk_hdmi, hdata->res.sclk_pixel); + clk_set_parent(hdata->res.mout_hdmi, hdata->res.sclk_pixel); clk_prepare_enable(hdata->res.sclk_hdmi); /* operation mode */ @@ -1812,8 +1813,13 @@ static int hdmi_resources_init(struct hdmi_context *hdata) DRM_ERROR("failed to get clock 'hdmiphy'\n"); goto fail; } + res->mout_hdmi = devm_clk_get(dev, "mout_hdmi"); + if (IS_ERR(res->mout_hdmi)) { + DRM_ERROR("failed to get clock 'mout_hdmi'\n"); + goto fail; + } - clk_set_parent(res->sclk_hdmi, res->sclk_pixel); + clk_set_parent(res->mout_hdmi, res->sclk_pixel); res->regul_bulk = devm_kzalloc(dev, ARRAY_SIZE(supply) * sizeof(res->regul_bulk[0]), GFP_KERNEL); -- cgit v1.2.3 From 2fa7b74c7b39b66c378be151a854e0b8a705765c Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 11 Jun 2013 12:26:51 +0530 Subject: drm/exynos: exynos_drm_ipp: Remove redundant break statement 'break' after goto statement is redundant. Silences the following message: drivers/gpu/drm/exynos/exynos_drm_ipp.c:1067 exynos_drm_ipp_check_valid() info: ignoring unreachable code. Signed-off-by: Sachin Kamat Signed-off-by: Inki Dae --- drivers/gpu/drm/exynos/exynos_drm_ipp.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpu/drm/exynos/exynos_drm_ipp.c b/drivers/gpu/drm/exynos/exynos_drm_ipp.c index 01cb9a03aac0..b1ef8e7ff9c9 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_ipp.c +++ b/drivers/gpu/drm/exynos/exynos_drm_ipp.c @@ -1064,7 +1064,6 @@ static bool exynos_drm_ipp_check_valid(struct device *dev, default: DRM_ERROR("invalid state.\n"); goto err_status; - break; } return true; -- cgit v1.2.3 From 23f340e0314eab461d33ae91250f9b47af23918f Mon Sep 17 00:00:00 2001 From: Inki Dae Date: Fri, 14 Jun 2013 17:54:27 +0900 Subject: drm/exynos: make sure to handle an error case to vm_mmap call vm_mmap function returns unsigned long so addr type should be unsigned long. a pointer or address variable is required to use unsigned long or uint64_t type for 64bits address support. So this patch makes sure that addr has unsigned long type and also exynos_drm_gem_mmap_ioctl returns correct error type. Signed-off-by: Inki Dae Signed-off-by: Kyungmin Park --- drivers/gpu/drm/exynos/exynos_drm_gem.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c index 5af14782fcfe..c3f15e7646d5 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c @@ -420,7 +420,7 @@ int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data, { struct drm_exynos_gem_mmap *args = data; struct drm_gem_object *obj; - unsigned int addr; + unsigned long addr; if (!(dev->driver->driver_features & DRIVER_GEM)) { DRM_ERROR("does not support GEM.\n"); @@ -462,14 +462,14 @@ int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data, drm_gem_object_unreference(obj); - if (IS_ERR((void *)addr)) { + if (IS_ERR_VALUE(addr)) { /* check filp->f_op, filp->private_data are restored */ if (file_priv->filp->f_op == &exynos_drm_gem_fops) { file_priv->filp->f_op = fops_get(dev->driver->fops); file_priv->filp->private_data = file_priv; } mutex_unlock(&dev->struct_mutex); - return PTR_ERR((void *)addr); + return (int)addr; } mutex_unlock(&dev->struct_mutex); -- cgit v1.2.3 From 5f916e289894e97f3b4c3a91a44debf9a47d1b85 Mon Sep 17 00:00:00 2001 From: Rahul Sharma Date: Tue, 11 Jun 2013 19:41:29 +0530 Subject: drm/exynos: use of_get_named_gpio to get hdmi hpd gpio Cleanup by removing flags variable from drm_hdmi_dt_parse_pdata which is not used anywhere. Swtiching to of_get_named_gpio instead of of_get_named_gpio_flags solved this. Signed-off-by: Rahul Sharma Acked-by: Seung-Woo Kim Signed-off-by: Inki Dae --- drivers/gpu/drm/exynos/exynos_hdmi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index ce13a23a8274..743059fed39b 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -1864,7 +1864,6 @@ static struct s5p_hdmi_platform_data *drm_hdmi_dt_parse_pdata { struct device_node *np = dev->of_node; struct s5p_hdmi_platform_data *pd; - enum of_gpio_flags flags; u32 value; pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); @@ -1878,7 +1877,7 @@ static struct s5p_hdmi_platform_data *drm_hdmi_dt_parse_pdata goto err_data; } - pd->hpd_gpio = of_get_named_gpio_flags(np, "hpd-gpio", 0, &flags); + pd->hpd_gpio = of_get_named_gpio(np, "hpd-gpio", 0); return pd; -- cgit v1.2.3 From 1482995c707631f2e99825bfc9b621debd264d31 Mon Sep 17 00:00:00 2001 From: Rahul Sharma Date: Tue, 18 Jun 2013 18:19:37 +0530 Subject: drm/exynos: fix interlace resolutions for exynos5420 Modified code for calculating hdmi IP register values from drm timing values. The modification is based on the inputs from hw team and specifically proposed for 1440x576i and 1440x480i. But same changes holds good for other interlaced resolutions also. Signed-off-by: Rahul Sharma Acked-by: Seung-Woo Kim Signed-off-by: Inki Dae --- drivers/gpu/drm/exynos/exynos_hdmi.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 743059fed39b..b565d1e63b3b 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -1557,8 +1557,7 @@ static void hdmi_v14_mode_set(struct hdmi_context *hdata, (m->vsync_start - m->vdisplay) / 2); hdmi_set_reg(core->v2_blank, 2, m->vtotal / 2); hdmi_set_reg(core->v1_blank, 2, (m->vtotal - m->vdisplay) / 2); - hdmi_set_reg(core->v_blank_f0, 2, (m->vtotal + - ((m->vsync_end - m->vsync_start) * 4) + 5) / 2); + hdmi_set_reg(core->v_blank_f0, 2, m->vtotal - m->vdisplay / 2); hdmi_set_reg(core->v_blank_f1, 2, m->vtotal); hdmi_set_reg(core->v_sync_line_aft_2, 2, (m->vtotal / 2) + 7); hdmi_set_reg(core->v_sync_line_aft_1, 2, (m->vtotal / 2) + 2); @@ -1568,7 +1567,10 @@ static void hdmi_v14_mode_set(struct hdmi_context *hdata, (m->htotal / 2) + (m->hsync_start - m->hdisplay)); hdmi_set_reg(tg->vact_st, 2, (m->vtotal - m->vdisplay) / 2); hdmi_set_reg(tg->vact_sz, 2, m->vdisplay / 2); - hdmi_set_reg(tg->vact_st2, 2, 0x249);/* Reset value + 1*/ + hdmi_set_reg(tg->vact_st2, 2, m->vtotal - m->vdisplay / 2); + hdmi_set_reg(tg->vsync2, 2, (m->vtotal / 2) + 1); + hdmi_set_reg(tg->vsync_bot_hdmi, 2, (m->vtotal / 2) + 1); + hdmi_set_reg(tg->field_bot_hdmi, 2, (m->vtotal / 2) + 1); hdmi_set_reg(tg->vact_st3, 2, 0x0); hdmi_set_reg(tg->vact_st4, 2, 0x0); } else { @@ -1590,6 +1592,9 @@ static void hdmi_v14_mode_set(struct hdmi_context *hdata, hdmi_set_reg(tg->vact_st2, 2, 0x248); /* Reset value */ hdmi_set_reg(tg->vact_st3, 2, 0x47b); /* Reset value */ hdmi_set_reg(tg->vact_st4, 2, 0x6ae); /* Reset value */ + hdmi_set_reg(tg->vsync2, 2, 0x233); /* Reset value */ + hdmi_set_reg(tg->vsync_bot_hdmi, 2, 0x233); /* Reset value */ + hdmi_set_reg(tg->field_bot_hdmi, 2, 0x233); /* Reset value */ } /* Following values & calculations are same irrespective of mode type */ @@ -1621,12 +1626,9 @@ static void hdmi_v14_mode_set(struct hdmi_context *hdata, hdmi_set_reg(tg->hact_sz, 2, m->hdisplay); hdmi_set_reg(tg->v_fsz, 2, m->vtotal); hdmi_set_reg(tg->vsync, 2, 0x1); - hdmi_set_reg(tg->vsync2, 2, 0x233); /* Reset value */ hdmi_set_reg(tg->field_chg, 2, 0x233); /* Reset value */ hdmi_set_reg(tg->vsync_top_hdmi, 2, 0x1); /* Reset value */ - hdmi_set_reg(tg->vsync_bot_hdmi, 2, 0x233); /* Reset value */ hdmi_set_reg(tg->field_top_hdmi, 2, 0x1); /* Reset value */ - hdmi_set_reg(tg->field_bot_hdmi, 2, 0x233); /* Reset value */ hdmi_set_reg(tg->tg_3d, 1, 0x0); } -- cgit v1.2.3 From cc57caf0cfe74e536910f587a369af4a8550a4ee Mon Sep 17 00:00:00 2001 From: Rahul Sharma Date: Wed, 19 Jun 2013 18:21:07 +0530 Subject: drm/exynos: add new compatible strings for hdmi subsystem This patch adds new combatible strings for hdmi, mixer, ddc and hdmiphy. It follows the convention of using compatible string which represent the SoC in which the IP was added for the first time. Drivers continue to support the previous compatible strings but further addition of these compatible strings in device tree is deprecated. Signed-off-by: Rahul Sharma Reviewed-by: Tomasz Figa Signed-off-by: Inki Dae --- Documentation/devicetree/bindings/video/exynos_hdmi.txt | 7 +++++-- Documentation/devicetree/bindings/video/exynos_hdmiddc.txt | 7 +++++-- Documentation/devicetree/bindings/video/exynos_hdmiphy.txt | 7 +++++-- Documentation/devicetree/bindings/video/exynos_mixer.txt | 8 ++++++-- drivers/gpu/drm/exynos/exynos_ddc.c | 2 ++ drivers/gpu/drm/exynos/exynos_hdmi.c | 3 +++ drivers/gpu/drm/exynos/exynos_hdmiphy.c | 4 ++++ drivers/gpu/drm/exynos/exynos_mixer.c | 13 ++++++++----- 8 files changed, 38 insertions(+), 13 deletions(-) diff --git a/Documentation/devicetree/bindings/video/exynos_hdmi.txt b/Documentation/devicetree/bindings/video/exynos_hdmi.txt index 589edee37394..c71d0f0b750a 100644 --- a/Documentation/devicetree/bindings/video/exynos_hdmi.txt +++ b/Documentation/devicetree/bindings/video/exynos_hdmi.txt @@ -1,7 +1,10 @@ Device-Tree bindings for drm hdmi driver Required properties: -- compatible: value should be "samsung,exynos5-hdmi". +- compatible: value should be one among the following: + 1) "samsung,exynos5-hdmi" + 2) "samsung,exynos4210-hdmi" + 3) "samsung,exynos4212-hdmi" - reg: physical base address of the hdmi and length of memory mapped region. - interrupts: interrupt number to the cpu. @@ -15,7 +18,7 @@ Required properties: Example: hdmi { - compatible = "samsung,exynos5-hdmi"; + compatible = "samsung,exynos4212-hdmi"; reg = <0x14530000 0x100000>; interrupts = <0 95 0>; hpd-gpio = <&gpx3 7 0xf 1 3>; diff --git a/Documentation/devicetree/bindings/video/exynos_hdmiddc.txt b/Documentation/devicetree/bindings/video/exynos_hdmiddc.txt index fa166d945809..41eee971562b 100644 --- a/Documentation/devicetree/bindings/video/exynos_hdmiddc.txt +++ b/Documentation/devicetree/bindings/video/exynos_hdmiddc.txt @@ -1,12 +1,15 @@ Device-Tree bindings for hdmiddc driver Required properties: -- compatible: value should be "samsung,exynos5-hdmiddc". +- compatible: value should be one of the following + 1) "samsung,exynos5-hdmiddc" + 2) "samsung,exynos4210-hdmiddc" + - reg: I2C address of the hdmiddc device. Example: hdmiddc { - compatible = "samsung,exynos5-hdmiddc"; + compatible = "samsung,exynos4210-hdmiddc"; reg = <0x50>; }; diff --git a/Documentation/devicetree/bindings/video/exynos_hdmiphy.txt b/Documentation/devicetree/bindings/video/exynos_hdmiphy.txt index 858f4f9b902f..162f641f7639 100644 --- a/Documentation/devicetree/bindings/video/exynos_hdmiphy.txt +++ b/Documentation/devicetree/bindings/video/exynos_hdmiphy.txt @@ -1,12 +1,15 @@ Device-Tree bindings for hdmiphy driver Required properties: -- compatible: value should be "samsung,exynos5-hdmiphy". +- compatible: value should be one of the following: + 1) "samsung,exynos5-hdmiphy" + 2) "samsung,exynos4210-hdmiphy". + 3) "samsung,exynos4212-hdmiphy". - reg: I2C address of the hdmiphy device. Example: hdmiphy { - compatible = "samsung,exynos5-hdmiphy"; + compatible = "samsung,exynos4210-hdmiphy"; reg = <0x38>; }; diff --git a/Documentation/devicetree/bindings/video/exynos_mixer.txt b/Documentation/devicetree/bindings/video/exynos_mixer.txt index 9b2ea0343566..9131b99cd8ff 100644 --- a/Documentation/devicetree/bindings/video/exynos_mixer.txt +++ b/Documentation/devicetree/bindings/video/exynos_mixer.txt @@ -1,7 +1,11 @@ Device-Tree bindings for mixer driver Required properties: -- compatible: value should be "samsung,exynos5-mixer". +- compatible: value should be one of the following: + 1) "samsung,exynos5-mixer" + 2) "samsung,exynos4210-mixer" + 3) "samsung,exynos5250-mixer" + - reg: physical base address of the mixer and length of memory mapped region. - interrupts: interrupt number to the cpu. @@ -9,7 +13,7 @@ Required properties: Example: mixer { - compatible = "samsung,exynos5-mixer"; + compatible = "samsung,exynos5250-mixer"; reg = <0x14450000 0x10000>; interrupts = <0 94 0>; }; diff --git a/drivers/gpu/drm/exynos/exynos_ddc.c b/drivers/gpu/drm/exynos/exynos_ddc.c index 4e9b5ba8edff..95c75edef01a 100644 --- a/drivers/gpu/drm/exynos/exynos_ddc.c +++ b/drivers/gpu/drm/exynos/exynos_ddc.c @@ -52,6 +52,8 @@ static struct i2c_device_id ddc_idtable[] = { static struct of_device_id hdmiddc_match_types[] = { { .compatible = "samsung,exynos5-hdmiddc", + }, { + .compatible = "samsung,exynos4210-hdmiddc", }, { /* end node */ } diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index b565d1e63b3b..62ef5971ac3c 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -1917,6 +1917,9 @@ static struct of_device_id hdmi_match_types[] = { { .compatible = "samsung,exynos5-hdmi", .data = (void *)HDMI_TYPE14, + }, { + .compatible = "samsung,exynos4212-hdmi", + .data = (void *)HDMI_TYPE14, }, { /* end node */ } diff --git a/drivers/gpu/drm/exynos/exynos_hdmiphy.c b/drivers/gpu/drm/exynos/exynos_hdmiphy.c index ea49d132ecf6..ef04255076c7 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmiphy.c +++ b/drivers/gpu/drm/exynos/exynos_hdmiphy.c @@ -50,6 +50,10 @@ static const struct i2c_device_id hdmiphy_id[] = { static struct of_device_id hdmiphy_match_types[] = { { .compatible = "samsung,exynos5-hdmiphy", + }, { + .compatible = "samsung,exynos4210-hdmiphy", + }, { + .compatible = "samsung,exynos4212-hdmiphy", }, { /* end node */ } diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index f36f878f1e3a..62255011c9d6 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -1115,12 +1115,12 @@ static int vp_resources_init(struct exynos_drm_hdmi_context *ctx, return 0; } -static struct mixer_drv_data exynos5_mxr_drv_data = { +static struct mixer_drv_data exynos5250_mxr_drv_data = { .version = MXR_VER_16_0_33_0, .is_vp_enabled = 0, }; -static struct mixer_drv_data exynos4_mxr_drv_data = { +static struct mixer_drv_data exynos4210_mxr_drv_data = { .version = MXR_VER_0_0_0_16, .is_vp_enabled = 1, }; @@ -1128,10 +1128,10 @@ static struct mixer_drv_data exynos4_mxr_drv_data = { static struct platform_device_id mixer_driver_types[] = { { .name = "s5p-mixer", - .driver_data = (unsigned long)&exynos4_mxr_drv_data, + .driver_data = (unsigned long)&exynos4210_mxr_drv_data, }, { .name = "exynos5-mixer", - .driver_data = (unsigned long)&exynos5_mxr_drv_data, + .driver_data = (unsigned long)&exynos5250_mxr_drv_data, }, { /* end node */ } @@ -1140,7 +1140,10 @@ static struct platform_device_id mixer_driver_types[] = { static struct of_device_id mixer_match_types[] = { { .compatible = "samsung,exynos5-mixer", - .data = &exynos5_mxr_drv_data, + .data = &exynos5250_mxr_drv_data, + }, { + .compatible = "samsung,exynos5250-mixer", + .data = &exynos5250_mxr_drv_data, }, { /* end node */ } -- cgit v1.2.3 From def5e095719dbc808c856dd5c64749b867b3984a Mon Sep 17 00:00:00 2001 From: Rahul Sharma Date: Wed, 19 Jun 2013 18:21:08 +0530 Subject: drm/exynos: add support for exynos5420 mixer Add support for exynos5420 mixer IP in the drm mixer driver. Signed-off-by: Rahul Sharma Acked-by: Seung-Woo Kim Reviewed-by: Tomasz Figa Signed-off-by: Inki Dae --- .../devicetree/bindings/video/exynos_mixer.txt | 1 + drivers/gpu/drm/exynos/exynos_mixer.c | 49 ++++++++++++++++------ drivers/gpu/drm/exynos/regs-mixer.h | 7 ++++ 3 files changed, 45 insertions(+), 12 deletions(-) diff --git a/Documentation/devicetree/bindings/video/exynos_mixer.txt b/Documentation/devicetree/bindings/video/exynos_mixer.txt index 9131b99cd8ff..3334b0a8e343 100644 --- a/Documentation/devicetree/bindings/video/exynos_mixer.txt +++ b/Documentation/devicetree/bindings/video/exynos_mixer.txt @@ -5,6 +5,7 @@ Required properties: 1) "samsung,exynos5-mixer" 2) "samsung,exynos4210-mixer" 3) "samsung,exynos5250-mixer" + 4) "samsung,exynos5420-mixer" - reg: physical base address of the mixer and length of memory mapped region. diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index 62255011c9d6..b1280b43931c 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -78,6 +78,7 @@ struct mixer_resources { enum mixer_version_id { MXR_VER_0_0_0_16, MXR_VER_16_0_33_0, + MXR_VER_128_0_0_184, }; struct mixer_context { @@ -283,17 +284,19 @@ static void mixer_cfg_scan(struct mixer_context *ctx, unsigned int height) val = (ctx->interlace ? MXR_CFG_SCAN_INTERLACE : MXR_CFG_SCAN_PROGRASSIVE); - /* choosing between porper HD and SD mode */ - if (height <= 480) - val |= MXR_CFG_SCAN_NTSC | MXR_CFG_SCAN_SD; - else if (height <= 576) - val |= MXR_CFG_SCAN_PAL | MXR_CFG_SCAN_SD; - else if (height <= 720) - val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD; - else if (height <= 1080) - val |= MXR_CFG_SCAN_HD_1080 | MXR_CFG_SCAN_HD; - else - val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD; + if (ctx->mxr_ver != MXR_VER_128_0_0_184) { + /* choosing between proper HD and SD mode */ + if (height <= 480) + val |= MXR_CFG_SCAN_NTSC | MXR_CFG_SCAN_SD; + else if (height <= 576) + val |= MXR_CFG_SCAN_PAL | MXR_CFG_SCAN_SD; + else if (height <= 720) + val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD; + else if (height <= 1080) + val |= MXR_CFG_SCAN_HD_1080 | MXR_CFG_SCAN_HD; + else + val |= MXR_CFG_SCAN_HD_720 | MXR_CFG_SCAN_HD; + } mixer_reg_writemask(res, MXR_CFG, val, MXR_CFG_SCAN_MASK); } @@ -557,6 +560,14 @@ static void mixer_graph_buffer(struct mixer_context *ctx, int win) /* setup geometry */ mixer_reg_write(res, MXR_GRAPHIC_SPAN(win), win_data->fb_width); + /* setup display size */ + if (ctx->mxr_ver == MXR_VER_128_0_0_184 && + win == MIXER_DEFAULT_WIN) { + val = MXR_MXR_RES_HEIGHT(win_data->fb_height); + val |= MXR_MXR_RES_WIDTH(win_data->fb_width); + mixer_reg_write(res, MXR_RESOLUTION, val); + } + val = MXR_GRP_WH_WIDTH(win_data->crtc_width); val |= MXR_GRP_WH_HEIGHT(win_data->crtc_height); val |= MXR_GRP_WH_H_SCALE(x_ratio); @@ -581,7 +592,8 @@ static void mixer_graph_buffer(struct mixer_context *ctx, int win) mixer_cfg_layer(ctx, win, true); /* layer update mandatory for mixer 16.0.33.0 */ - if (ctx->mxr_ver == MXR_VER_16_0_33_0) + if (ctx->mxr_ver == MXR_VER_16_0_33_0 || + ctx->mxr_ver == MXR_VER_128_0_0_184) mixer_layer_update(ctx); mixer_run(ctx); @@ -816,6 +828,7 @@ static void mixer_win_disable(void *ctx, int win) static int mixer_check_mode(void *ctx, struct drm_display_mode *mode) { + struct mixer_context *mixer_ctx = ctx; u32 w, h; w = mode->hdisplay; @@ -825,6 +838,10 @@ static int mixer_check_mode(void *ctx, struct drm_display_mode *mode) mode->hdisplay, mode->vdisplay, mode->vrefresh, (mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0); + if (mixer_ctx->mxr_ver == MXR_VER_0_0_0_16 || + mixer_ctx->mxr_ver == MXR_VER_128_0_0_184) + return 0; + if ((w >= 464 && w <= 720 && h >= 261 && h <= 576) || (w >= 1024 && w <= 1280 && h >= 576 && h <= 720) || (w >= 1664 && w <= 1920 && h >= 936 && h <= 1080)) @@ -1115,6 +1132,11 @@ static int vp_resources_init(struct exynos_drm_hdmi_context *ctx, return 0; } +static struct mixer_drv_data exynos5420_mxr_drv_data = { + .version = MXR_VER_128_0_0_184, + .is_vp_enabled = 0, +}; + static struct mixer_drv_data exynos5250_mxr_drv_data = { .version = MXR_VER_16_0_33_0, .is_vp_enabled = 0, @@ -1144,6 +1166,9 @@ static struct of_device_id mixer_match_types[] = { }, { .compatible = "samsung,exynos5250-mixer", .data = &exynos5250_mxr_drv_data, + }, { + .compatible = "samsung,exynos5420-mixer", + .data = &exynos5420_mxr_drv_data, }, { /* end node */ } diff --git a/drivers/gpu/drm/exynos/regs-mixer.h b/drivers/gpu/drm/exynos/regs-mixer.h index 5d8dbc0301e6..4537026bc385 100644 --- a/drivers/gpu/drm/exynos/regs-mixer.h +++ b/drivers/gpu/drm/exynos/regs-mixer.h @@ -44,6 +44,9 @@ #define MXR_CM_COEFF_Y 0x0080 #define MXR_CM_COEFF_CB 0x0084 #define MXR_CM_COEFF_CR 0x0088 +#define MXR_MO 0x0304 +#define MXR_RESOLUTION 0x0310 + #define MXR_GRAPHIC0_BASE_S 0x2024 #define MXR_GRAPHIC1_BASE_S 0x2044 @@ -119,6 +122,10 @@ #define MXR_GRP_WH_WIDTH(x) MXR_MASK_VAL(x, 26, 16) #define MXR_GRP_WH_HEIGHT(x) MXR_MASK_VAL(x, 10, 0) +/* bits for MXR_RESOLUTION */ +#define MXR_MXR_RES_HEIGHT(x) MXR_MASK_VAL(x, 26, 16) +#define MXR_MXR_RES_WIDTH(x) MXR_MASK_VAL(x, 10, 0) + /* bits for MXR_GRAPHICn_SXY */ #define MXR_GRP_SXY_SX(x) MXR_MASK_VAL(x, 26, 16) #define MXR_GRP_SXY_SY(x) MXR_MASK_VAL(x, 10, 0) -- cgit v1.2.3 From 0d1fc829791d6a4f00528d65b8c88b9b9bd0b862 Mon Sep 17 00:00:00 2001 From: Rahul Sharma Date: Wed, 19 Jun 2013 18:21:09 +0530 Subject: ARM: dts: change compatible strings for EXYNOS5250 hdmi subsystem This patch renames the combatible strings for hdmi, mixer, ddc and hdmiphy. It follows the convention of using compatible string which represent the SoC in which the IP was added for the first time. Signed-off-by: Rahul Sharma Acked-by: Kukjin Kim Reviewed-by: Tomasz Figa Signed-off-by: Inki Dae --- arch/arm/boot/dts/cros5250-common.dtsi | 4 ++-- arch/arm/boot/dts/exynos5250-smdk5250.dts | 4 ++-- arch/arm/boot/dts/exynos5250.dtsi | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/arm/boot/dts/cros5250-common.dtsi b/arch/arm/boot/dts/cros5250-common.dtsi index 3f0239ec1bc5..dc259e8b8a73 100644 --- a/arch/arm/boot/dts/cros5250-common.dtsi +++ b/arch/arm/boot/dts/cros5250-common.dtsi @@ -190,7 +190,7 @@ samsung,i2c-max-bus-freq = <66000>; hdmiddc@50 { - compatible = "samsung,exynos5-hdmiddc"; + compatible = "samsung,exynos4210-hdmiddc"; reg = <0x50>; }; }; @@ -224,7 +224,7 @@ samsung,i2c-max-bus-freq = <378000>; hdmiphy@38 { - compatible = "samsung,exynos5-hdmiphy"; + compatible = "samsung,exynos4212-hdmiphy"; reg = <0x38>; }; }; diff --git a/arch/arm/boot/dts/exynos5250-smdk5250.dts b/arch/arm/boot/dts/exynos5250-smdk5250.dts index 3e0c792e2767..f320d7cb4174 100644 --- a/arch/arm/boot/dts/exynos5250-smdk5250.dts +++ b/arch/arm/boot/dts/exynos5250-smdk5250.dts @@ -72,7 +72,7 @@ samsung,i2c-max-bus-freq = <66000>; hdmiddc@50 { - compatible = "samsung,exynos5-hdmiddc"; + compatible = "samsung,exynos4210-hdmiddc"; reg = <0x50>; }; }; @@ -102,7 +102,7 @@ samsung,i2c-max-bus-freq = <66000>; hdmiphy@38 { - compatible = "samsung,exynos5-hdmiphy"; + compatible = "samsung,exynos4212-hdmiphy"; reg = <0x38>; }; }; diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi index fc9fb3d526e2..8b815c945949 100644 --- a/arch/arm/boot/dts/exynos5250.dtsi +++ b/arch/arm/boot/dts/exynos5250.dtsi @@ -601,7 +601,7 @@ }; hdmi { - compatible = "samsung,exynos5-hdmi"; + compatible = "samsung,exynos4212-hdmi"; reg = <0x14530000 0x70000>; interrupts = <0 95 0>; clocks = <&clock 333>, <&clock 136>, <&clock 137>, @@ -611,7 +611,7 @@ }; mixer { - compatible = "samsung,exynos5-mixer"; + compatible = "samsung,exynos5250-mixer"; reg = <0x14450000 0x10000>; interrupts = <0 94 0>; }; -- cgit v1.2.3 From acd8db100ed5220fe8043f91cdc20155325542a9 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Wed, 12 Jun 2013 17:27:23 -0300 Subject: drm/i915: don't check encoder at DP connector destroy() By the time we call intel_dp_destroy (which destroys the connector) the encoder may have been destroyed already, so if we use it we may be reading some free memory. That happens in drm_mode_config_cleanup() and also inside intel_dp_init_connector() when we detect a ghost eDP. I also hope this may solve some random memory bugs. Reported by kmemcheck. Signed-off-by: Paulo Zanoni Reviewed-by: Zoltan Nyul Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 98686005dcf6..6975d4bdad6b 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2683,13 +2683,14 @@ done: static void intel_dp_destroy(struct drm_connector *connector) { - struct intel_dp *intel_dp = intel_attached_dp(connector); struct intel_connector *intel_connector = to_intel_connector(connector); if (!IS_ERR_OR_NULL(intel_connector->edid)) kfree(intel_connector->edid); - if (is_edp(intel_dp)) + /* Can't call is_edp() since the encoder may have been destroyed + * already. */ + if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) intel_panel_fini(&intel_connector->panel); drm_sysfs_connector_remove(connector); -- cgit v1.2.3 From ed92f0b239ac971edc509169ae3d6955fbe0a188 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Wed, 12 Jun 2013 17:27:24 -0300 Subject: drm/i915: extract intel_edp_init_connector Because intel_dp_init_connector is too big for my poor little brain. No functional changes. Signed-off-by: Paulo Zanoni Reviewed-by: Zoltan Nyul Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 151 ++++++++++++++++++++++------------------ 1 file changed, 82 insertions(+), 69 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 6975d4bdad6b..30514e58269b 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2955,6 +2955,86 @@ intel_dp_init_panel_power_sequencer_registers(struct drm_device *dev, I915_READ(pp_div_reg)); } +static bool intel_edp_init_connector(struct intel_dp *intel_dp, + struct intel_connector *intel_connector) +{ + struct drm_connector *connector = &intel_connector->base; + struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); + struct drm_device *dev = intel_dig_port->base.base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_display_mode *fixed_mode = NULL; + struct edp_power_seq power_seq = { 0 }; + bool has_dpcd; + struct drm_display_mode *scan; + struct edid *edid; + + if (!is_edp(intel_dp)) + return true; + + intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq); + + /* Cache DPCD and EDID for edp. */ + ironlake_edp_panel_vdd_on(intel_dp); + has_dpcd = intel_dp_get_dpcd(intel_dp); + ironlake_edp_panel_vdd_off(intel_dp, false); + + if (has_dpcd) { + if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11) + dev_priv->no_aux_handshake = + intel_dp->dpcd[DP_MAX_DOWNSPREAD] & + DP_NO_AUX_HANDSHAKE_LINK_TRAINING; + } else { + /* if this fails, presume the device is a ghost */ + DRM_INFO("failed to retrieve link info, disabling eDP\n"); + intel_dp_encoder_destroy(&intel_dig_port->base.base); + intel_dp_destroy(connector); + return false; + } + + /* We now know it's not a ghost, init power sequence regs. */ + intel_dp_init_panel_power_sequencer_registers(dev, intel_dp, + &power_seq); + + ironlake_edp_panel_vdd_on(intel_dp); + edid = drm_get_edid(connector, &intel_dp->adapter); + if (edid) { + if (drm_add_edid_modes(connector, edid)) { + drm_mode_connector_update_edid_property(connector, + edid); + drm_edid_to_eld(connector, edid); + } else { + kfree(edid); + edid = ERR_PTR(-EINVAL); + } + } else { + edid = ERR_PTR(-ENOENT); + } + intel_connector->edid = edid; + + /* prefer fixed mode from EDID if available */ + list_for_each_entry(scan, &connector->probed_modes, head) { + if ((scan->type & DRM_MODE_TYPE_PREFERRED)) { + fixed_mode = drm_mode_duplicate(dev, scan); + break; + } + } + + /* fallback to VBT if available for eDP */ + if (!fixed_mode && dev_priv->vbt.lfp_lvds_vbt_mode) { + fixed_mode = drm_mode_duplicate(dev, + dev_priv->vbt.lfp_lvds_vbt_mode); + if (fixed_mode) + fixed_mode->type |= DRM_MODE_TYPE_PREFERRED; + } + + ironlake_edp_panel_vdd_off(intel_dp, false); + + intel_panel_init(&intel_connector->panel, fixed_mode); + intel_panel_setup_backlight(connector); + + return true; +} + void intel_dp_init_connector(struct intel_digital_port *intel_dig_port, struct intel_connector *intel_connector) @@ -2964,8 +3044,6 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, struct intel_encoder *intel_encoder = &intel_dig_port->base; struct drm_device *dev = intel_encoder->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct drm_display_mode *fixed_mode = NULL; - struct edp_power_seq power_seq = { 0 }; enum port port = intel_dig_port->port; const char *name = NULL; int type; @@ -3066,75 +3144,10 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, BUG(); } - if (is_edp(intel_dp)) - intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq); - intel_dp_i2c_init(intel_dp, intel_connector, name); - /* Cache DPCD and EDID for edp. */ - if (is_edp(intel_dp)) { - bool ret; - struct drm_display_mode *scan; - struct edid *edid; - - ironlake_edp_panel_vdd_on(intel_dp); - ret = intel_dp_get_dpcd(intel_dp); - ironlake_edp_panel_vdd_off(intel_dp, false); - - if (ret) { - if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11) - dev_priv->no_aux_handshake = - intel_dp->dpcd[DP_MAX_DOWNSPREAD] & - DP_NO_AUX_HANDSHAKE_LINK_TRAINING; - } else { - /* if this fails, presume the device is a ghost */ - DRM_INFO("failed to retrieve link info, disabling eDP\n"); - intel_dp_encoder_destroy(&intel_encoder->base); - intel_dp_destroy(connector); - return; - } - - /* We now know it's not a ghost, init power sequence regs. */ - intel_dp_init_panel_power_sequencer_registers(dev, intel_dp, - &power_seq); - - ironlake_edp_panel_vdd_on(intel_dp); - edid = drm_get_edid(connector, &intel_dp->adapter); - if (edid) { - if (drm_add_edid_modes(connector, edid)) { - drm_mode_connector_update_edid_property(connector, edid); - drm_edid_to_eld(connector, edid); - } else { - kfree(edid); - edid = ERR_PTR(-EINVAL); - } - } else { - edid = ERR_PTR(-ENOENT); - } - intel_connector->edid = edid; - - /* prefer fixed mode from EDID if available */ - list_for_each_entry(scan, &connector->probed_modes, head) { - if ((scan->type & DRM_MODE_TYPE_PREFERRED)) { - fixed_mode = drm_mode_duplicate(dev, scan); - break; - } - } - - /* fallback to VBT if available for eDP */ - if (!fixed_mode && dev_priv->vbt.lfp_lvds_vbt_mode) { - fixed_mode = drm_mode_duplicate(dev, dev_priv->vbt.lfp_lvds_vbt_mode); - if (fixed_mode) - fixed_mode->type |= DRM_MODE_TYPE_PREFERRED; - } - - ironlake_edp_panel_vdd_off(intel_dp, false); - } - - if (is_edp(intel_dp)) { - intel_panel_init(&intel_connector->panel, fixed_mode); - intel_panel_setup_backlight(connector); - } + if (!intel_edp_init_connector(intel_dp, intel_connector)) + return; intel_dp_add_properties(intel_dp, connector); -- cgit v1.2.3 From 16c255335b0ec39b4e5e976f4b260978aeed5a68 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Wed, 12 Jun 2013 17:27:25 -0300 Subject: drm/i915: propagate errors from intel_dp_init_connector In case we detect a "ghost eDP", intel_edp_init_connector frees both the connector and encoder and then returns. On Haswell, intel_ddi_init then tries to use the freed encoder on the HDMI initialization path since the following commit: commit 21a8e6a4853b2ed39fa4c5188a710f2cf1b92026 Author: Daniel Vetter Date: Wed Apr 10 23:28:35 2013 +0200 drm/i915: don't setup hdmi for port D edp in ddi_init So now on intel_ddi_init we check for the "ghost eDP" case and return without trying to initialize HDMI. This way we won't try to read the freed "intel_encoder" struct in the next "if" statement. Signed-off-by: Paulo Zanoni Reviewed-by: Zoltan Nyul Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ddi.c | 3 ++- drivers/gpu/drm/i915/intel_dp.c | 6 ++++-- drivers/gpu/drm/i915/intel_drv.h | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 224ce25129ce..0f835d15c18b 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -1356,7 +1356,8 @@ void intel_ddi_init(struct drm_device *dev, enum port port) intel_encoder->cloneable = false; intel_encoder->hot_plug = intel_ddi_hot_plug; - intel_dp_init_connector(intel_dig_port, dp_connector); + if (!intel_dp_init_connector(intel_dig_port, dp_connector)) + return; if (intel_encoder->type != INTEL_OUTPUT_EDP) { hdmi_connector = kzalloc(sizeof(struct intel_connector), diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 30514e58269b..1a429cf55291 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -3035,7 +3035,7 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp, return true; } -void +bool intel_dp_init_connector(struct intel_digital_port *intel_dig_port, struct intel_connector *intel_connector) { @@ -3147,7 +3147,7 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, intel_dp_i2c_init(intel_dp, intel_connector, name); if (!intel_edp_init_connector(intel_dp, intel_connector)) - return; + return false; intel_dp_add_properties(intel_dp, connector); @@ -3159,6 +3159,8 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, u32 temp = I915_READ(PEG_BAND_GAP_DATA); I915_WRITE(PEG_BAND_GAP_DATA, (temp & ~0xf) | 0xd); } + + return true; } void diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index ffe9d35b37b4..9ddbe3b49fef 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -586,7 +586,7 @@ extern void intel_lvds_init(struct drm_device *dev); extern bool intel_is_dual_link_lvds(struct drm_device *dev); extern void intel_dp_init(struct drm_device *dev, int output_reg, enum port port); -extern void intel_dp_init_connector(struct intel_digital_port *intel_dig_port, +extern bool intel_dp_init_connector(struct intel_digital_port *intel_dig_port, struct intel_connector *intel_connector); extern void intel_dp_init_link_config(struct intel_dp *intel_dp); extern void intel_dp_start_link_train(struct intel_dp *intel_dp); -- cgit v1.2.3 From b2f246a8998ccf9e00477c8829a62139804e9857 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Wed, 12 Jun 2013 17:27:26 -0300 Subject: drm/i915: fix the "ghost eDP" connector unwind path Because calling intel_dp_destroy inside intel_edp_init_connector is just wrong. This is the initialization path, so we should properly unwind all the initialization through the whole caller stack. On the intel_dp_destroy function we do the following: 1 - Free edid if it exists 2 - Call intel_panel_fini in case it's eDP 3 - Call drm_sysfs_connector_remove 4 - Call drm_connector_cleanup 5 - Free the connector And here is how we unwind each specific step: 1 - No need as we still didn't assign anything 2 - No need as we still didn't call intel_panel_init 3 - Call it in the same function that called drm_sysfs_connector_add 4 - Call it in the same function that called drm_connector_init 5 - Free it in the same function that allocated it Signed-off-by: Paulo Zanoni Reviewed-by: Zoltan Nyul Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ddi.c | 4 +++- drivers/gpu/drm/i915/intel_dp.c | 9 ++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 0f835d15c18b..aed363cabe07 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -1356,8 +1356,10 @@ void intel_ddi_init(struct drm_device *dev, enum port port) intel_encoder->cloneable = false; intel_encoder->hot_plug = intel_ddi_hot_plug; - if (!intel_dp_init_connector(intel_dig_port, dp_connector)) + if (!intel_dp_init_connector(intel_dig_port, dp_connector)) { + kfree(dp_connector); return; + } if (intel_encoder->type != INTEL_OUTPUT_EDP) { hdmi_connector = kzalloc(sizeof(struct intel_connector), diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 1a429cf55291..5ba0a612d463 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2987,7 +2987,6 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp, /* if this fails, presume the device is a ghost */ DRM_INFO("failed to retrieve link info, disabling eDP\n"); intel_dp_encoder_destroy(&intel_dig_port->base.base); - intel_dp_destroy(connector); return false; } @@ -3146,8 +3145,11 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, intel_dp_i2c_init(intel_dp, intel_connector, name); - if (!intel_edp_init_connector(intel_dp, intel_connector)) + if (!intel_edp_init_connector(intel_dp, intel_connector)) { + drm_sysfs_connector_remove(connector); + drm_connector_cleanup(connector); return false; + } intel_dp_add_properties(intel_dp, connector); @@ -3206,5 +3208,6 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port) intel_encoder->cloneable = false; intel_encoder->hot_plug = intel_dp_hot_plug; - intel_dp_init_connector(intel_dig_port, intel_connector); + if (!intel_dp_init_connector(intel_dig_port, intel_connector)) + kfree(intel_connector); } -- cgit v1.2.3 From 15b1d171d87e86366266255462e6b11d21b61c1c Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Wed, 12 Jun 2013 17:27:27 -0300 Subject: drm/i915: fix the "ghost eDP" encoder unwind path Because calling intel_dp_encoder_destroy inside intel_edp_init_connector is just wrong. This is the initialization path, so we should properly unwind all the initialization through the whole caller stack. On the intel_dp_encoder_destroy function we do the following: 1 - Call i2c_del_adapter 2 - Call drm_encoder_cleanup 3 - If edp: 3.1 - Cancel panel_vdd_work 3.2 - Call ironlake_panel_vdd_of_sync 4 - Free the encoder And here is how we unwind each specific step: 1 - We have intel_dp_init_connector -> intel_dp_i2c_init -> i2c_dp_aux_add_bus -> i2c_add_adapter, so we call i2c_del_dapter at intel_dp_init_connector 2 - Call it in the same function that called drm_encoder_init 3 - Call it in the same function that called INIT_DELAYED_WORK 4 - Free it in the same function that allocated it Signed-off-by: Paulo Zanoni Reviewed-by: Zoltan Nyul Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ddi.c | 2 ++ drivers/gpu/drm/i915/intel_dp.c | 13 +++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index aed363cabe07..324211ac9c55 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -1357,6 +1357,8 @@ void intel_ddi_init(struct drm_device *dev, enum port port) intel_encoder->hot_plug = intel_ddi_hot_plug; if (!intel_dp_init_connector(intel_dig_port, dp_connector)) { + drm_encoder_cleanup(encoder); + kfree(intel_dig_port); kfree(dp_connector); return; } diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 5ba0a612d463..ed1d34680125 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2986,7 +2986,6 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp, } else { /* if this fails, presume the device is a ghost */ DRM_INFO("failed to retrieve link info, disabling eDP\n"); - intel_dp_encoder_destroy(&intel_dig_port->base.base); return false; } @@ -3146,6 +3145,13 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, intel_dp_i2c_init(intel_dp, intel_connector, name); if (!intel_edp_init_connector(intel_dp, intel_connector)) { + i2c_del_adapter(&intel_dp->adapter); + if (is_edp(intel_dp)) { + cancel_delayed_work_sync(&intel_dp->panel_vdd_work); + mutex_lock(&dev->mode_config.mutex); + ironlake_panel_vdd_off_sync(intel_dp); + mutex_unlock(&dev->mode_config.mutex); + } drm_sysfs_connector_remove(connector); drm_connector_cleanup(connector); return false; @@ -3208,6 +3214,9 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port) intel_encoder->cloneable = false; intel_encoder->hot_plug = intel_dp_hot_plug; - if (!intel_dp_init_connector(intel_dig_port, intel_connector)) + if (!intel_dp_init_connector(intel_dig_port, intel_connector)) { + drm_encoder_cleanup(encoder); + kfree(intel_dig_port); kfree(intel_connector); + } } -- cgit v1.2.3 From b2a1475561d59e8d182ba8cc4b7e78b662a3f533 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Wed, 12 Jun 2013 17:27:28 -0300 Subject: drm/i915: check the return value of intel_dp_i2c_init We've been ignoring this return value, so print a nice backtrace in case it's not what we expected. Signed-off-by: Paulo Zanoni Reviewed-by: Zoltan Nyul Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index ed1d34680125..591502c6f34c 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -3044,7 +3044,7 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, struct drm_i915_private *dev_priv = dev->dev_private; enum port port = intel_dig_port->port; const char *name = NULL; - int type; + int type, error; /* Preserve the current hw state. */ intel_dp->DP = I915_READ(intel_dp->output_reg); @@ -3142,7 +3142,9 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, BUG(); } - intel_dp_i2c_init(intel_dp, intel_connector, name); + error = intel_dp_i2c_init(intel_dp, intel_connector, name); + WARN(error, "intel_dp_i2c_init failed with error %d for port %c\n", + error, port_name(port)); if (!intel_edp_init_connector(intel_dp, intel_connector)) { i2c_del_adapter(&intel_dp->adapter); -- cgit v1.2.3 From 83a35e360433b58791bc9c4e288cace466d62e3a Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 28 Jun 2013 11:27:31 +0200 Subject: treewide: relase -> release Signed-off-by: Geert Uytterhoeven Signed-off-by: Jiri Kosina --- Makefile | 2 +- drivers/input/misc/da9055_onkey.c | 2 +- drivers/media/dvb-frontends/stv0367.c | 2 +- drivers/net/ethernet/intel/igb/e1000_phy.c | 2 +- drivers/s390/char/vmur.c | 2 +- drivers/scsi/libfc/fc_fcp.c | 2 +- mm/mmu_notifier.c | 2 +- sound/pci/hda/hda_codec.c | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 73e20dba55c1..75789ec41f53 100644 --- a/Makefile +++ b/Makefile @@ -794,7 +794,7 @@ PHONY += $(vmlinux-dirs) $(vmlinux-dirs): prepare scripts $(Q)$(MAKE) $(build)=$@ -# Store (new) KERNELRELASE string in include/config/kernel.release +# Store (new) KERNELRELEASE string in include/config/kernel.release include/config/kernel.release: include/config/auto.conf FORCE $(Q)rm -f $@ $(Q)echo "$(KERNELVERSION)$$($(CONFIG_SHELL) $(srctree)/scripts/setlocalversion $(srctree))" > $@ diff --git a/drivers/input/misc/da9055_onkey.c b/drivers/input/misc/da9055_onkey.c index ee6ae3a00174..a0af8b2506ce 100644 --- a/drivers/input/misc/da9055_onkey.c +++ b/drivers/input/misc/da9055_onkey.c @@ -36,7 +36,7 @@ static void da9055_onkey_query(struct da9055_onkey *onkey) } else { key_stat &= DA9055_NOKEY_STS; /* - * Onkey status bit is cleared when onkey button is relased. + * Onkey status bit is cleared when onkey button is released. */ if (!key_stat) { input_report_key(onkey->input, KEY_POWER, 0); diff --git a/drivers/media/dvb-frontends/stv0367.c b/drivers/media/dvb-frontends/stv0367.c index 0c8e45949b11..7b6dba3ce55e 100644 --- a/drivers/media/dvb-frontends/stv0367.c +++ b/drivers/media/dvb-frontends/stv0367.c @@ -2919,7 +2919,7 @@ enum stv0367_cab_signal_type stv0367cab_algo(struct stv0367_state *state, if (tuner_lock == 0) return FE_367CAB_NOTUNER; #endif - /* Relase the TRL to start demodulator acquisition */ + /* Release the TRL to start demodulator acquisition */ /* Wait for QAM lock */ LockTime = 0; stv0367_writereg(state, R367CAB_CTRL_1, 0x00); diff --git a/drivers/net/ethernet/intel/igb/e1000_phy.c b/drivers/net/ethernet/intel/igb/e1000_phy.c index 115b0da6e013..9979ebcf2a0c 100644 --- a/drivers/net/ethernet/intel/igb/e1000_phy.c +++ b/drivers/net/ethernet/intel/igb/e1000_phy.c @@ -2014,7 +2014,7 @@ out: * Verify the reset block is not blocking us from resetting. Acquire * semaphore (if necessary) and read/set/write the device control reset * bit in the PHY. Wait the appropriate delay time for the device to - * reset and relase the semaphore (if necessary). + * reset and release the semaphore (if necessary). **/ s32 igb_phy_hw_reset(struct e1000_hw *hw) { diff --git a/drivers/s390/char/vmur.c b/drivers/s390/char/vmur.c index c180e3135b3b..64c467998a90 100644 --- a/drivers/s390/char/vmur.c +++ b/drivers/s390/char/vmur.c @@ -89,7 +89,7 @@ static DEFINE_MUTEX(vmur_mutex); * urd references: * - ur_probe gets a urd reference, ur_remove drops the reference * dev_get_drvdata(&cdev->dev) - * - ur_open gets a urd reference, ur_relase drops the reference + * - ur_open gets a urd reference, ur_release drops the reference * (urf->urd) * * cdev references: diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c index 09c81b2f2169..5fd0f1fbe586 100644 --- a/drivers/scsi/libfc/fc_fcp.c +++ b/drivers/scsi/libfc/fc_fcp.c @@ -2043,7 +2043,7 @@ int fc_eh_abort(struct scsi_cmnd *sc_cmd) spin_unlock_irqrestore(&si->scsi_queue_lock, flags); return SUCCESS; } - /* grab a ref so the fsp and sc_cmd cannot be relased from under us */ + /* grab a ref so the fsp and sc_cmd cannot be released from under us */ fc_fcp_pkt_hold(fsp); spin_unlock_irqrestore(&si->scsi_queue_lock, flags); diff --git a/mm/mmu_notifier.c b/mm/mmu_notifier.c index 6725ff183374..93e6089cb456 100644 --- a/mm/mmu_notifier.c +++ b/mm/mmu_notifier.c @@ -315,7 +315,7 @@ void mmu_notifier_unregister(struct mmu_notifier *mn, struct mm_struct *mm) /* * Wait for any running method to finish, of course including - * ->release if it was run by mmu_notifier_relase instead of us. + * ->release if it was run by mmu_notifier_release instead of us. */ synchronize_srcu(&srcu); diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 55108b5fb291..a9676ceafe00 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -2523,7 +2523,7 @@ int snd_hda_codec_reset(struct hda_codec *codec) flush_workqueue(bus->workq); #endif snd_hda_ctls_clear(codec); - /* relase PCMs */ + /* release PCMs */ for (i = 0; i < codec->num_pcms; i++) { if (codec->pcm_info[i].pcm) { snd_device_free(card, codec->pcm_info[i].pcm); -- cgit v1.2.3 From 836bfa0d2930b7ec236aa488f01678c92eb5f0de Mon Sep 17 00:00:00 2001 From: YoungJun Cho Date: Mon, 17 Jun 2013 13:18:52 +0900 Subject: ARM: dma-mapping: Get pages if the cpu_addr is out of atomic_pool In __iommu_get_pages(), the cpu_addr is checked wheather in atomic_pool range or not. So if the cpu_addr is in atomic_pool range, it does not need to check twice. Signed-off-by: YoungJun Cho Signed-off-by: Kyungmin Park Signed-off-by: Marek Szyprowski --- arch/arm/mm/dma-mapping.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index ef3e0f3aac96..1d158c223ebb 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -1372,16 +1372,17 @@ static int arm_iommu_mmap_attrs(struct device *dev, struct vm_area_struct *vma, void arm_iommu_free_attrs(struct device *dev, size_t size, void *cpu_addr, dma_addr_t handle, struct dma_attrs *attrs) { - struct page **pages = __iommu_get_pages(cpu_addr, attrs); + struct page **pages; size = PAGE_ALIGN(size); - if (!pages) { - WARN(1, "trying to free invalid coherent area: %p\n", cpu_addr); + if (__in_atomic_pool(cpu_addr, size)) { + __iommu_free_atomic(dev, cpu_addr, handle, size); return; } - if (__in_atomic_pool(cpu_addr, size)) { - __iommu_free_atomic(dev, cpu_addr, handle, size); + pages = __iommu_get_pages(cpu_addr, attrs); + if (!pages) { + WARN(1, "trying to free invalid coherent area: %p\n", cpu_addr); return; } -- cgit v1.2.3 From 13987d68bcf476a93fa864ea4e58755f1bc4bd30 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Mon, 10 Jun 2013 19:34:39 +0100 Subject: ARM: dma-mapping: convert DMA direction into IOMMU protection attributes IOMMU mappings take a prot parameter, identifying the protection bits to enforce on the newly created mapping (READ or WRITE). The ARM dma-mapping framework currently just passes 0 as the prot argument, resulting in faulting mappings. This patch infers the protection attributes based on the direction of the DMA transfer. Cc: Marek Szyprowski Signed-off-by: Will Deacon Signed-off-by: Marek Szyprowski --- arch/arm/mm/dma-mapping.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index 1d158c223ebb..282aacd6f570 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -1637,13 +1637,27 @@ static dma_addr_t arm_coherent_iommu_map_page(struct device *dev, struct page *p { struct dma_iommu_mapping *mapping = dev->archdata.mapping; dma_addr_t dma_addr; - int ret, len = PAGE_ALIGN(size + offset); + int ret, prot, len = PAGE_ALIGN(size + offset); dma_addr = __alloc_iova(mapping, len); if (dma_addr == DMA_ERROR_CODE) return dma_addr; - ret = iommu_map(mapping->domain, dma_addr, page_to_phys(page), len, 0); + switch (dir) { + case DMA_BIDIRECTIONAL: + prot = IOMMU_READ | IOMMU_WRITE; + break; + case DMA_TO_DEVICE: + prot = IOMMU_READ; + break; + case DMA_FROM_DEVICE: + prot = IOMMU_WRITE; + break; + default: + prot = 0; + } + + ret = iommu_map(mapping->domain, dma_addr, page_to_phys(page), len, prot); if (ret < 0) goto fail; -- cgit v1.2.3 From 9e4b259d4fbf8a9c822117e734356e94b7f30927 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Mon, 10 Jun 2013 19:34:40 +0100 Subject: ARM: dma-mapping: NULLify dev->archdata.mapping pointer on detach The current code only clobbers a local variable, so the device is left with a stale mapping pointer. Cc: Hiroshi Doyu Signed-off-by: Will Deacon Acked-by: Hiroshi Doyu Signed-off-by: Marek Szyprowski --- arch/arm/mm/dma-mapping.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index 282aacd6f570..26a5833c9f29 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -1922,7 +1922,7 @@ void arm_iommu_detach_device(struct device *dev) iommu_detach_device(mapping->domain, dev); kref_put(&mapping->kref, release_iommu_mapping); - mapping = NULL; + dev->archdata.mapping = NULL; set_dma_ops(dev, NULL); pr_debug("Detached IOMMU controller from %s device.\n", dev_name(dev)); -- cgit v1.2.3 From 63c181922f25371b7d24ef63d4e15887ff23a088 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Wed, 15 May 2013 12:09:49 +0800 Subject: ARM: DMA-mapping: mark all !DMA_TO_DEVICE pages in unmapping as clean It is common for one sg to include many pages, so mark all these pages as clean to avoid unnecessary flushing on them in set_pte_at() or update_mmu_cache(). The patch might improve loading performance of applciation code a bit. On the below test code to read file(~1GByte size) from usb mass storage disk to buffer created with mmap(PROT_READ | PROT_EXEC) on Pandaboard, average ~1% improvement can be observed with the patch on 10 times test. unsigned int sum = 0; static unsigned long tv_diff(struct timeval *tv1, struct timeval *tv2) { return (tv2->tv_sec - tv1->tv_sec) * 1000000 + (tv2->tv_usec - tv1->tv_usec); } int main(int argc, char *argv[]) { char *mbuffer; int fd; int i; unsigned long page_size, size; struct stat stat; struct timeval t1, t2; page_size = getpagesize(); fd = open(argv[1], O_RDONLY); assert(fd >= 0); fstat(fd, &stat); size = stat.st_size; printf("%s: file %s, file size %lu, page size %lu\n", argv[0], read_filename, size, page_size); gettimeofday(&t1, NULL); mbuffer = mmap(NULL, size, PROT_READ | PROT_EXEC, MAP_SHARED, fd, 0); for (i = 0 ; i < size ; i += page_size) sum += mbuffer[i]; munmap(mbuffer, page_size); gettimeofday(&t2, NULL); printf("\tread mmaped time: %luus\n", tv_diff(&t1, &t2)); close(fd); } Acked-by: Nicolas Pitre Cc: Catalin Marinas Cc: Marek Szyprowski Cc: Russell King Signed-off-by: Ming Lei Signed-off-by: Marek Szyprowski --- arch/arm/mm/dma-mapping.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index 26a5833c9f29..97926b1277b2 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -880,10 +880,24 @@ static void __dma_page_dev_to_cpu(struct page *page, unsigned long off, dma_cache_maint_page(page, off, size, dir, dmac_unmap_area); /* - * Mark the D-cache clean for this page to avoid extra flushing. + * Mark the D-cache clean for these pages to avoid extra flushing. */ - if (dir != DMA_TO_DEVICE && off == 0 && size >= PAGE_SIZE) - set_bit(PG_dcache_clean, &page->flags); + if (dir != DMA_TO_DEVICE && size >= PAGE_SIZE) { + unsigned long pfn; + size_t left = size; + + pfn = page_to_pfn(page) + off / PAGE_SIZE; + off %= PAGE_SIZE; + if (off) { + pfn++; + left -= PAGE_SIZE - off; + } + while (left >= PAGE_SIZE) { + page = pfn_to_page(pfn++); + set_bit(PG_dcache_clean, &page->flags); + left -= PAGE_SIZE; + } + } } /** -- cgit v1.2.3 From 5b91a98c61abe914e6a80d7dc15e435c47ea0004 Mon Sep 17 00:00:00 2001 From: Richard Zhao Date: Thu, 20 Jun 2013 20:31:00 +0800 Subject: ARM: dma: Drop __GFP_COMP for iommu dma memory allocations __iommu_alloc_buffer wants to split pages after allocation in order to reduce the memory footprint. This does not work well with __GFP_COMP pages, so drop this flag before allocation One failure example is snd_malloc_dev_pages call dma_alloc_coherent with __GFP_COMP. Signed-off-by: Richard Zhao Signed-off-by: Marek Szyprowski --- arch/arm/mm/dma-mapping.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index 97926b1277b2..22c7d7eb35a0 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -1328,6 +1328,15 @@ static void *arm_iommu_alloc_attrs(struct device *dev, size_t size, if (gfp & GFP_ATOMIC) return __iommu_alloc_atomic(dev, size, handle); + /* + * Following is a work-around (a.k.a. hack) to prevent pages + * with __GFP_COMP being passed to split_page() which cannot + * handle them. The real problem is that this flag probably + * should be 0 on ARM as it is not supported on this + * platform; see CONFIG_HUGETLBFS. + */ + gfp &= ~(__GFP_COMP); + pages = __iommu_alloc_buffer(dev, size, gfp, attrs); if (!pages) return NULL; -- cgit v1.2.3 From a50d9a4d9ad3c71341788099d733e4151b8a511b Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 27 Jun 2013 10:07:19 -0700 Subject: hwmon: (ds1621) Fix temperature rounding operations Commit "hwmon: (ds1621) Add ds1721 chip support" broke rounding for chips or configurations with less than 12 bit resolution. Tested-by: Jean Delvare Acked-by: Jean Delvare Signed-off-by: Guenter Roeck --- drivers/hwmon/ds1621.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/hwmon/ds1621.c b/drivers/hwmon/ds1621.c index 591758bb629f..a26ba7a17c2b 100644 --- a/drivers/hwmon/ds1621.c +++ b/drivers/hwmon/ds1621.c @@ -128,6 +128,8 @@ struct ds1621_data { u16 temp[3]; /* Register values, word */ u8 conf; /* Register encoding, combined */ + u8 zbits; /* Resolution encoded as number of + * zero bits */ u16 update_interval; /* Conversion rate in milliseconds */ }; @@ -139,16 +141,14 @@ static inline int DS1621_TEMP_FROM_REG(u16 reg) /* * TEMP: 0.001C/bit (-55C to +125C) * REG: - * - 1621, 1625: 0.5C/bit - * - 1631, 1721, 1731: 0.0625C/bit - * Assume highest resolution and let the bits fall where they may.. + * - 1621, 1625: 0.5C/bit, 7 zero-bits + * - 1631, 1721, 1731: 0.0625C/bit, 4 zero-bits */ -static inline u16 DS1621_TEMP_TO_REG(long temp) +static inline u16 DS1621_TEMP_TO_REG(long temp, u8 zbits) { - int ntemp = clamp_val(temp, DS1621_TEMP_MIN, DS1621_TEMP_MAX); - ntemp += (ntemp < 0 ? -31 : 31); - ntemp = DIV_ROUND_CLOSEST(ntemp * 10, 625) << 4; - return (u16)ntemp; + temp = clamp_val(temp, DS1621_TEMP_MIN, DS1621_TEMP_MAX); + temp = DIV_ROUND_CLOSEST(temp * (1 << (8 - zbits)), 1000) << zbits; + return temp; } static void ds1621_init_client(struct i2c_client *client) @@ -172,6 +172,7 @@ static void ds1621_init_client(struct i2c_client *client) switch (data->kind) { case ds1625: data->update_interval = DS1625_CONVERSION_MAX; + data->zbits = 7; sreg = DS1621_COM_START; break; case ds1631: @@ -180,10 +181,12 @@ static void ds1621_init_client(struct i2c_client *client) resol = (new_conf & DS1621_REG_CONFIG_RESOL) >> DS1621_REG_CONFIG_RESOL_SHIFT; data->update_interval = ds1721_convrates[resol]; + data->zbits = 7 - resol; sreg = DS1721_COM_START; break; default: data->update_interval = DS1621_CONVERSION_MAX; + data->zbits = 7; sreg = DS1621_COM_START; break; } @@ -254,7 +257,7 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *da, return err; mutex_lock(&data->update_lock); - data->temp[attr->index] = DS1621_TEMP_TO_REG(val); + data->temp[attr->index] = DS1621_TEMP_TO_REG(val, data->zbits); i2c_smbus_write_word_swapped(client, DS1621_REG_TEMP[attr->index], data->temp[attr->index]); mutex_unlock(&data->update_lock); -- cgit v1.2.3 From 5c75acdcae1be608b27414334d7e1febef7dbd99 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Fri, 28 Jun 2013 19:30:40 +0800 Subject: pinctrl: st: fix return value check In case of error, the function pinctrl_register() returns NULL not ERR_PTR(). The IS_ERR() test in the return value check should be replaced with NULL test. The function syscon_regmap_lookup_by_phandle() returns ERR_PTR() and never returns NULL. The NULL test in the return value check should be replaced with IS_ERR(). Signed-off-by: Wei Yongjun Acked-by: Srinivas Kandagatla Signed-off-by: Mark Brown --- drivers/pinctrl/pinctrl-st.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pinctrl/pinctrl-st.c b/drivers/pinctrl/pinctrl-st.c index 7effedfd4761..de8c62664fa8 100644 --- a/drivers/pinctrl/pinctrl-st.c +++ b/drivers/pinctrl/pinctrl-st.c @@ -1299,9 +1299,9 @@ static int st_pctl_probe_dt(struct platform_device *pdev, return -ENOMEM; info->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); - if (!info->regmap) { + if (IS_ERR(info->regmap)) { dev_err(info->dev, "No syscfg phandle specified\n"); - return -ENOMEM; + return PTR_ERR(info->regmap); } info->data = of_match_node(st_pctl_of_match, np)->data; @@ -1376,9 +1376,9 @@ static int st_pctl_probe(struct platform_device *pdev) pctl_desc->name = dev_name(&pdev->dev); info->pctl = pinctrl_register(pctl_desc, &pdev->dev, info); - if (IS_ERR(info->pctl)) { + if (!info->pctl) { dev_err(&pdev->dev, "Failed pinctrl registration\n"); - return PTR_ERR(info->pctl); + return -EINVAL; } for (i = 0; i < info->nbanks; i++) -- cgit v1.2.3 From a5faeaf9109578e65e1a32e2a3e76c8b47e7dcb6 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Fri, 28 Jun 2013 16:04:02 +0200 Subject: writeback: Fix periodic writeback after fs mount Code in blkdev.c moves a device inode to default_backing_dev_info when the last reference to the device is put and moves the device inode back to its bdi when the first reference is acquired. This includes moving to wb.b_dirty list if the device inode is dirty. The code however doesn't setup timer to wake corresponding flusher thread and while wb.b_dirty list is non-empty __mark_inode_dirty() will not set it up either. Thus periodic writeback is effectively disabled until a sync(2) call which can lead to unexpected data loss in case of crash or power failure. Fix the problem by setting up a timer for periodic writeback in case we add the first dirty inode to wb.b_dirty list in bdev_inode_switch_bdi(). Reported-by: Bert De Jonghe CC: stable@vger.kernel.org # >= 3.0 Signed-off-by: Jan Kara Signed-off-by: Jens Axboe --- fs/block_dev.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/fs/block_dev.c b/fs/block_dev.c index 2091db8cdd78..85f5c85ec91c 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -58,17 +58,24 @@ static void bdev_inode_switch_bdi(struct inode *inode, struct backing_dev_info *dst) { struct backing_dev_info *old = inode->i_data.backing_dev_info; + bool wakeup_bdi = false; if (unlikely(dst == old)) /* deadlock avoidance */ return; bdi_lock_two(&old->wb, &dst->wb); spin_lock(&inode->i_lock); inode->i_data.backing_dev_info = dst; - if (inode->i_state & I_DIRTY) + if (inode->i_state & I_DIRTY) { + if (bdi_cap_writeback_dirty(dst) && !wb_has_dirty_io(&dst->wb)) + wakeup_bdi = true; list_move(&inode->i_wb_list, &dst->wb.b_dirty); + } spin_unlock(&inode->i_lock); spin_unlock(&old->wb.list_lock); spin_unlock(&dst->wb.list_lock); + + if (wakeup_bdi) + bdi_wakeup_thread_delayed(dst); } /* Kill _all_ buffers and pagecache , dirty or not.. */ -- cgit v1.2.3 From 9e04d3804d3ac97d8c03a41d78d0f0674b5d01e1 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Tue, 21 May 2013 20:43:50 +0200 Subject: timer: Fix jiffies wrap behavior of round_jiffies_common() Direct compare of jiffies related values does not work in the wrap around case. Replace it with time_is_after_jiffies(). Signed-off-by: Bart Van Assche Cc: Arjan van de Ven Cc: Stephen Rothwell Link: http://lkml.kernel.org/r/519BC066.5080600@acm.org Cc: stable@vger.kernel.org Signed-off-by: Thomas Gleixner --- kernel/timer.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/kernel/timer.c b/kernel/timer.c index 15ffdb3f1948..15bc1b41021d 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -149,9 +149,11 @@ static unsigned long round_jiffies_common(unsigned long j, int cpu, /* now that we have rounded, subtract the extra skew again */ j -= cpu * 3; - if (j <= jiffies) /* rounding ate our timeout entirely; */ - return original; - return j; + /* + * Make sure j is still in the future. Otherwise return the + * unmodified value. + */ + return time_is_after_jiffies(j) ? j : original; } /** -- cgit v1.2.3 From 65e053a7038c608f89272d4fb1f899890c579f0a Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Thu, 27 Jun 2013 14:28:38 +0100 Subject: xen: ifdef CONFIG_HIBERNATE_CALLBACKS xen_*_suspend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit xen_hvm_post_suspend, xen_pre_suspend, xen_post_suspend are only used if CONFIG_HIBERNATE_CALLBACKS is defined, resulting in: drivers/xen/manage.c:46:13: warning: ‘xen_hvm_post_suspend’ defined but not used [-Wunused-function] drivers/xen/manage.c:52:13: warning: ‘xen_pre_suspend’ defined but not used [-Wunused-function] drivers/xen/manage.c:59:13: warning: ‘xen_post_suspend’ defined but not used [-Wunused-function] If the kernel config is missing CONFIG_HIBERNATE_CALLBACKS. Simply ifdef CONFIG_HIBERNATE_CALLBACKS the three functions. Signed-off-by: Stefano Stabellini Signed-off-by: Konrad Rzeszutek Wilk --- drivers/xen/manage.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/xen/manage.c b/drivers/xen/manage.c index 412b96cc5305..b1005d4a953d 100644 --- a/drivers/xen/manage.c +++ b/drivers/xen/manage.c @@ -43,6 +43,7 @@ struct suspend_info { void (*post)(int cancelled); }; +#ifdef CONFIG_HIBERNATE_CALLBACKS static void xen_hvm_post_suspend(int cancelled) { xen_arch_hvm_post_suspend(cancelled); @@ -63,7 +64,6 @@ static void xen_post_suspend(int cancelled) xen_mm_unpin_all(); } -#ifdef CONFIG_HIBERNATE_CALLBACKS static int xen_suspend(void *data) { struct suspend_info *si = data; -- cgit v1.2.3 From 283c0972d53769ee44750cad4c27e3f5fa26ec1f Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Fri, 28 Jun 2013 03:21:41 -0700 Subject: xen: Convert printks to pr_ Convert printks to pr_ (excludes printk(KERN_DEBUG...) to be more consistent throughout the xen subsystem. Add pr_fmt with KBUILD_MODNAME or "xen:" KBUILD_MODNAME Coalesce formats and add missing word spaces Add missing newlines Align arguments and reflow to 80 columns Remove DRV_NAME from formats as pr_fmt adds the same content This does change some of the prefixes of these messages but it also does make them more consistent. Signed-off-by: Joe Perches Signed-off-by: Konrad Rzeszutek Wilk --- drivers/xen/balloon.c | 6 +++-- drivers/xen/cpu_hotplug.c | 6 +++-- drivers/xen/events.c | 23 +++++++++--------- drivers/xen/evtchn.c | 6 +++-- drivers/xen/gntalloc.c | 6 +++-- drivers/xen/gntdev.c | 8 ++++--- drivers/xen/grant-table.c | 17 +++++++------- drivers/xen/manage.c | 23 +++++++++--------- drivers/xen/mcelog.c | 36 +++++++++++------------------ drivers/xen/pcpu.c | 12 +++++----- drivers/xen/privcmd.c | 4 +++- drivers/xen/swiotlb-xen.c | 12 ++++++---- drivers/xen/tmem.c | 10 ++++---- drivers/xen/xen-acpi-cpuhotplug.c | 2 ++ drivers/xen/xen-acpi-memhotplug.c | 2 ++ drivers/xen/xen-acpi-pad.c | 2 ++ drivers/xen/xen-acpi-processor.c | 25 ++++++++++---------- drivers/xen/xen-balloon.c | 6 +++-- drivers/xen/xen-pciback/conf_space_header.c | 16 ++++++------- drivers/xen/xen-pciback/pci_stub.c | 25 +++++++++----------- drivers/xen/xen-pciback/pciback_ops.c | 9 +++++--- drivers/xen/xen-pciback/vpci.c | 10 ++++---- drivers/xen/xen-pciback/xenbus.c | 8 ++++--- drivers/xen/xen-selfballoon.c | 11 ++++----- drivers/xen/xenbus/xenbus_comms.c | 13 ++++++----- drivers/xen/xenbus/xenbus_dev_backend.c | 4 +++- drivers/xen/xenbus/xenbus_dev_frontend.c | 4 +++- drivers/xen/xenbus/xenbus_probe.c | 30 +++++++++++------------- drivers/xen/xenbus/xenbus_probe_backend.c | 8 ++++--- drivers/xen/xenbus/xenbus_probe_frontend.c | 35 ++++++++++++++-------------- drivers/xen/xenbus/xenbus_xs.c | 22 ++++++++---------- drivers/xen/xencomm.c | 2 ++ drivers/xen/xenfs/super.c | 4 +++- include/xen/hvm.h | 4 ++-- 34 files changed, 215 insertions(+), 196 deletions(-) diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c index 930fb6817901..e33615335aa0 100644 --- a/drivers/xen/balloon.c +++ b/drivers/xen/balloon.c @@ -36,6 +36,8 @@ * IN THE SOFTWARE. */ +#define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt + #include #include #include @@ -242,7 +244,7 @@ static enum bp_state reserve_additional_memory(long credit) rc = add_memory(nid, hotplug_start_paddr, balloon_hotplug << PAGE_SHIFT); if (rc) { - pr_info("xen_balloon: %s: add_memory() failed: %i\n", __func__, rc); + pr_info("%s: add_memory() failed: %i\n", __func__, rc); return BP_EAGAIN; } @@ -591,7 +593,7 @@ static int __init balloon_init(void) if (!xen_domain()) return -ENODEV; - pr_info("xen/balloon: Initialising balloon driver.\n"); + pr_info("Initialising balloon driver\n"); balloon_stats.current_pages = xen_pv_domain() ? min(xen_start_info->nr_pages - xen_released_pages, max_pfn) diff --git a/drivers/xen/cpu_hotplug.c b/drivers/xen/cpu_hotplug.c index 084041d42c9a..cc6513a176b0 100644 --- a/drivers/xen/cpu_hotplug.c +++ b/drivers/xen/cpu_hotplug.c @@ -1,3 +1,5 @@ +#define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt + #include #include @@ -31,7 +33,7 @@ static int vcpu_online(unsigned int cpu) err = xenbus_scanf(XBT_NIL, dir, "availability", "%15s", state); if (err != 1) { if (!xen_initial_domain()) - printk(KERN_ERR "XENBUS: Unable to read cpu state\n"); + pr_err("Unable to read cpu state\n"); return err; } @@ -40,7 +42,7 @@ static int vcpu_online(unsigned int cpu) else if (strcmp(state, "offline") == 0) return 0; - printk(KERN_ERR "XENBUS: unknown state(%s) on CPU%d\n", state, cpu); + pr_err("unknown state(%s) on CPU%d\n", state, cpu); return -EINVAL; } static void vcpu_hotplug(unsigned int cpu) diff --git a/drivers/xen/events.c b/drivers/xen/events.c index 6a6bbe4ede92..a58ac435a9a4 100644 --- a/drivers/xen/events.c +++ b/drivers/xen/events.c @@ -21,6 +21,8 @@ * Jeremy Fitzhardinge , XenSource Inc, 2007 */ +#define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt + #include #include #include @@ -600,8 +602,7 @@ static unsigned int __startup_pirq(unsigned int irq) rc = HYPERVISOR_event_channel_op(EVTCHNOP_bind_pirq, &bind_pirq); if (rc != 0) { if (!probing_irq(irq)) - printk(KERN_INFO "Failed to obtain physical IRQ %d\n", - irq); + pr_info("Failed to obtain physical IRQ %d\n", irq); return 0; } evtchn = bind_pirq.port; @@ -693,8 +694,8 @@ int xen_bind_pirq_gsi_to_irq(unsigned gsi, irq = xen_irq_from_gsi(gsi); if (irq != -1) { - printk(KERN_INFO "xen_map_pirq_gsi: returning irq %d for gsi %u\n", - irq, gsi); + pr_info("%s: returning irq %d for gsi %u\n", + __func__, irq, gsi); goto out; } @@ -812,10 +813,10 @@ int xen_destroy_irq(int irq) * (free_domain_pirqs). */ if ((rc == -ESRCH && info->u.pirq.domid != DOMID_SELF)) - printk(KERN_INFO "domain %d does not have %d anymore\n", + pr_info("domain %d does not have %d anymore\n", info->u.pirq.domid, info->u.pirq.pirq); else if (rc) { - printk(KERN_WARNING "unmap irq failed %d\n", rc); + pr_warn("unmap irq failed %d\n", rc); goto out; } } @@ -1621,8 +1622,8 @@ static void restore_pirqs(void) rc = HYPERVISOR_physdev_op(PHYSDEVOP_map_pirq, &map_irq); if (rc) { - printk(KERN_WARNING "xen map irq failed gsi=%d irq=%d pirq=%d rc=%d\n", - gsi, irq, pirq, rc); + pr_warn("xen map irq failed gsi=%d irq=%d pirq=%d rc=%d\n", + gsi, irq, pirq, rc); xen_free_irq(irq); continue; } @@ -1844,13 +1845,11 @@ void xen_callback_vector(void) callback_via = HVM_CALLBACK_VECTOR(HYPERVISOR_CALLBACK_VECTOR); rc = xen_set_callback_via(callback_via); if (rc) { - printk(KERN_ERR "Request for Xen HVM callback vector" - " failed.\n"); + pr_err("Request for Xen HVM callback vector failed\n"); xen_have_vector_callback = 0; return; } - printk(KERN_INFO "Xen HVM callback vector for event delivery is " - "enabled\n"); + pr_info("Xen HVM callback vector for event delivery is enabled\n"); /* in the restore case the vector has already been allocated */ if (!test_bit(HYPERVISOR_CALLBACK_VECTOR, used_vectors)) alloc_intr_gate(HYPERVISOR_CALLBACK_VECTOR, diff --git a/drivers/xen/evtchn.c b/drivers/xen/evtchn.c index 45c8efaa6b3e..8feecf01d55c 100644 --- a/drivers/xen/evtchn.c +++ b/drivers/xen/evtchn.c @@ -31,6 +31,8 @@ * IN THE SOFTWARE. */ +#define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt + #include #include #include @@ -547,11 +549,11 @@ static int __init evtchn_init(void) /* Create '/dev/xen/evtchn'. */ err = misc_register(&evtchn_miscdev); if (err != 0) { - printk(KERN_ERR "Could not register /dev/xen/evtchn\n"); + pr_err("Could not register /dev/xen/evtchn\n"); return err; } - printk(KERN_INFO "Event-channel device installed.\n"); + pr_info("Event-channel device installed\n"); return 0; } diff --git a/drivers/xen/gntalloc.c b/drivers/xen/gntalloc.c index 4097987b330e..787d17945418 100644 --- a/drivers/xen/gntalloc.c +++ b/drivers/xen/gntalloc.c @@ -48,6 +48,8 @@ * grant operation. */ +#define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt + #include #include #include @@ -507,7 +509,7 @@ static int gntalloc_mmap(struct file *filp, struct vm_area_struct *vma) int rv, i; if (!(vma->vm_flags & VM_SHARED)) { - printk(KERN_ERR "%s: Mapping must be shared.\n", __func__); + pr_err("%s: Mapping must be shared\n", __func__); return -EINVAL; } @@ -584,7 +586,7 @@ static int __init gntalloc_init(void) err = misc_register(&gntalloc_miscdev); if (err != 0) { - printk(KERN_ERR "Could not register misc gntalloc device\n"); + pr_err("Could not register misc gntalloc device\n"); return err; } diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c index 3c8803feba26..eab5427c75f5 100644 --- a/drivers/xen/gntdev.c +++ b/drivers/xen/gntdev.c @@ -19,6 +19,8 @@ #undef DEBUG +#define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt + #include #include #include @@ -760,7 +762,7 @@ static int gntdev_mmap(struct file *flip, struct vm_area_struct *vma) if (use_ptemod && map->vma) goto unlock_out; if (use_ptemod && priv->mm != vma->vm_mm) { - printk(KERN_WARNING "Huh? Other mm?\n"); + pr_warn("Huh? Other mm?\n"); goto unlock_out; } @@ -795,7 +797,7 @@ static int gntdev_mmap(struct file *flip, struct vm_area_struct *vma) vma->vm_end - vma->vm_start, find_grant_ptes, map); if (err) { - printk(KERN_WARNING "find_grant_ptes() failure.\n"); + pr_warn("find_grant_ptes() failure.\n"); goto out_put_map; } } @@ -855,7 +857,7 @@ static int __init gntdev_init(void) err = misc_register(&gntdev_miscdev); if (err != 0) { - printk(KERN_ERR "Could not register gntdev device\n"); + pr_err("Could not register gntdev device\n"); return err; } return 0; diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c index 04c1b2d9b775..04cdeb8e3719 100644 --- a/drivers/xen/grant-table.c +++ b/drivers/xen/grant-table.c @@ -31,6 +31,8 @@ * IN THE SOFTWARE. */ +#define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt + #include #include #include @@ -508,8 +510,7 @@ static void gnttab_handle_deferred(unsigned long unused) entry = NULL; } else { if (!--entry->warn_delay) - pr_info("g.e. %#x still pending\n", - entry->ref); + pr_info("g.e. %#x still pending\n", entry->ref); if (!first) first = entry; } @@ -838,7 +839,7 @@ gnttab_retry_eagain_gop(unsigned int cmd, void *gop, int16_t *status, } while ((*status == GNTST_eagain) && (delay < MAX_DELAY)); if (delay >= MAX_DELAY) { - printk(KERN_ERR "%s: %s eagain grant\n", func, current->comm); + pr_err("%s: %s eagain grant\n", func, current->comm); *status = GNTST_bad_page; } } @@ -1048,8 +1049,8 @@ static int gnttab_map(unsigned int start_idx, unsigned int end_idx) xatp.gpfn = (xen_hvm_resume_frames >> PAGE_SHIFT) + i; rc = HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp); if (rc != 0) { - printk(KERN_WARNING - "grant table add_to_physmap failed, err=%d\n", rc); + pr_warn("grant table add_to_physmap failed, err=%d\n", + rc); break; } } while (i-- > start_idx); @@ -1131,8 +1132,7 @@ static void gnttab_request_version(void) grefs_per_grant_frame = PAGE_SIZE / sizeof(struct grant_entry_v1); gnttab_interface = &gnttab_v1_ops; } - printk(KERN_INFO "Grant tables using version %d layout.\n", - grant_table_version); + pr_info("Grant tables using version %d layout\n", grant_table_version); } static int gnttab_setup(void) @@ -1150,8 +1150,7 @@ static int gnttab_setup(void) gnttab_shared.addr = xen_remap(xen_hvm_resume_frames, PAGE_SIZE * max_nr_gframes); if (gnttab_shared.addr == NULL) { - printk(KERN_WARNING - "Failed to ioremap gnttab share frames!"); + pr_warn("Failed to ioremap gnttab share frames!\n"); return -ENOMEM; } } diff --git a/drivers/xen/manage.c b/drivers/xen/manage.c index b1005d4a953d..9385b4524547 100644 --- a/drivers/xen/manage.c +++ b/drivers/xen/manage.c @@ -1,6 +1,9 @@ /* * Handle extern requests for shutdown, reboot and sysrq */ + +#define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt + #include #include #include @@ -73,8 +76,7 @@ static int xen_suspend(void *data) err = syscore_suspend(); if (err) { - printk(KERN_ERR "xen_suspend: system core suspend failed: %d\n", - err); + pr_err("%s: system core suspend failed: %d\n", __func__, err); return err; } @@ -115,14 +117,14 @@ static void do_suspend(void) during suspend. */ err = freeze_processes(); if (err) { - printk(KERN_ERR "xen suspend: freeze failed %d\n", err); + pr_err("%s: freeze failed %d\n", __func__, err); goto out; } #endif err = dpm_suspend_start(PMSG_FREEZE); if (err) { - printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err); + pr_err("%s: dpm_suspend_start %d\n", __func__, err); goto out_thaw; } @@ -131,7 +133,7 @@ static void do_suspend(void) err = dpm_suspend_end(PMSG_FREEZE); if (err) { - printk(KERN_ERR "dpm_suspend_end failed: %d\n", err); + pr_err("dpm_suspend_end failed: %d\n", err); si.cancelled = 0; goto out_resume; } @@ -153,7 +155,7 @@ static void do_suspend(void) dpm_resume_start(si.cancelled ? PMSG_THAW : PMSG_RESTORE); if (err) { - printk(KERN_ERR "failed to start xen_suspend: %d\n", err); + pr_err("failed to start xen_suspend: %d\n", err); si.cancelled = 1; } @@ -245,7 +247,7 @@ static void shutdown_handler(struct xenbus_watch *watch, if (handler->cb) { handler->cb(); } else { - printk(KERN_INFO "Ignoring shutdown request: %s\n", str); + pr_info("Ignoring shutdown request: %s\n", str); shutting_down = SHUTDOWN_INVALID; } @@ -265,8 +267,7 @@ static void sysrq_handler(struct xenbus_watch *watch, const char **vec, if (err) return; if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) { - printk(KERN_ERR "Unable to read sysrq code in " - "control/sysrq\n"); + pr_err("Unable to read sysrq code in control/sysrq\n"); xenbus_transaction_end(xbt, 1); return; } @@ -299,14 +300,14 @@ static int setup_shutdown_watcher(void) err = register_xenbus_watch(&shutdown_watch); if (err) { - printk(KERN_ERR "Failed to set shutdown watcher\n"); + pr_err("Failed to set shutdown watcher\n"); return err; } #ifdef CONFIG_MAGIC_SYSRQ err = register_xenbus_watch(&sysrq_watch); if (err) { - printk(KERN_ERR "Failed to set sysrq watcher\n"); + pr_err("Failed to set sysrq watcher\n"); return err; } #endif diff --git a/drivers/xen/mcelog.c b/drivers/xen/mcelog.c index 8feee08bcb43..6ab6a79c38a5 100644 --- a/drivers/xen/mcelog.c +++ b/drivers/xen/mcelog.c @@ -32,6 +32,8 @@ * IN THE SOFTWARE. */ +#define pr_fmt(fmt) "xen_mcelog: " fmt + #include #include #include @@ -51,8 +53,6 @@ #include #include -#define XEN_MCELOG "xen_mcelog: " - static struct mc_info g_mi; static struct mcinfo_logical_cpu *g_physinfo; static uint32_t ncpus; @@ -227,7 +227,7 @@ static int convert_log(struct mc_info *mi) mic = NULL; x86_mcinfo_lookup(&mic, mi, MC_TYPE_GLOBAL); if (unlikely(!mic)) { - pr_warning(XEN_MCELOG "Failed to find global error info\n"); + pr_warn("Failed to find global error info\n"); return -ENODEV; } @@ -241,8 +241,7 @@ static int convert_log(struct mc_info *mi) if (g_physinfo[i].mc_apicid == m.apicid) break; if (unlikely(i == ncpus)) { - pr_warning(XEN_MCELOG "Failed to match cpu with apicid %d\n", - m.apicid); + pr_warn("Failed to match cpu with apicid %d\n", m.apicid); return -ENODEV; } @@ -254,7 +253,7 @@ static int convert_log(struct mc_info *mi) mic = NULL; x86_mcinfo_lookup(&mic, mi, MC_TYPE_BANK); if (unlikely(!mic)) { - pr_warning(XEN_MCELOG "Fail to find bank error info\n"); + pr_warn("Fail to find bank error info\n"); return -ENODEV; } @@ -295,9 +294,8 @@ static int mc_queue_handle(uint32_t flags) mc_op.u.mc_fetch.flags = flags; ret = HYPERVISOR_mca(&mc_op); if (ret) { - pr_err(XEN_MCELOG "Failed to fetch %s error log\n", - (flags == XEN_MC_URGENT) ? - "urgnet" : "nonurgent"); + pr_err("Failed to fetch %surgent error log\n", + flags == XEN_MC_URGENT ? "" : "non"); break; } @@ -307,15 +305,12 @@ static int mc_queue_handle(uint32_t flags) else { ret = convert_log(&g_mi); if (ret) - pr_warning(XEN_MCELOG - "Failed to convert this error log, " - "continue acking it anyway\n"); + pr_warn("Failed to convert this error log, continue acking it anyway\n"); mc_op.u.mc_fetch.flags = flags | XEN_MC_ACK; ret = HYPERVISOR_mca(&mc_op); if (ret) { - pr_err(XEN_MCELOG - "Failed to ack previous error log\n"); + pr_err("Failed to ack previous error log\n"); break; } } @@ -334,15 +329,12 @@ static void xen_mce_work_fn(struct work_struct *work) /* urgent mc_info */ err = mc_queue_handle(XEN_MC_URGENT); if (err) - pr_err(XEN_MCELOG - "Failed to handle urgent mc_info queue, " - "continue handling nonurgent mc_info queue anyway.\n"); + pr_err("Failed to handle urgent mc_info queue, continue handling nonurgent mc_info queue anyway\n"); /* nonurgent mc_info */ err = mc_queue_handle(XEN_MC_NONURGENT); if (err) - pr_err(XEN_MCELOG - "Failed to handle nonurgent mc_info queue.\n"); + pr_err("Failed to handle nonurgent mc_info queue\n"); /* wake processes polling /dev/mcelog */ wake_up_interruptible(&xen_mce_chrdev_wait); @@ -370,7 +362,7 @@ static int bind_virq_for_mce(void) set_xen_guest_handle(mc_op.u.mc_physcpuinfo.info, g_physinfo); ret = HYPERVISOR_mca(&mc_op); if (ret) { - pr_err(XEN_MCELOG "Failed to get CPU numbers\n"); + pr_err("Failed to get CPU numbers\n"); return ret; } @@ -383,7 +375,7 @@ static int bind_virq_for_mce(void) set_xen_guest_handle(mc_op.u.mc_physcpuinfo.info, g_physinfo); ret = HYPERVISOR_mca(&mc_op); if (ret) { - pr_err(XEN_MCELOG "Failed to get CPU info\n"); + pr_err("Failed to get CPU info\n"); kfree(g_physinfo); return ret; } @@ -391,7 +383,7 @@ static int bind_virq_for_mce(void) ret = bind_virq_to_irqhandler(VIRQ_MCA, 0, xen_mce_interrupt, 0, "mce", NULL); if (ret < 0) { - pr_err(XEN_MCELOG "Failed to bind virq\n"); + pr_err("Failed to bind virq\n"); kfree(g_physinfo); return ret; } diff --git a/drivers/xen/pcpu.c b/drivers/xen/pcpu.c index 6536d5ab1697..79e1dff7ed4f 100644 --- a/drivers/xen/pcpu.c +++ b/drivers/xen/pcpu.c @@ -31,6 +31,8 @@ * IN THE SOFTWARE. */ +#define pr_fmt(fmt) "xen_cpu: " fmt + #include #include #include @@ -44,7 +46,6 @@ #include #include -#define XEN_PCPU "xen_cpu: " /* * @cpu_id: Xen physical cpu logic number @@ -242,8 +243,7 @@ static struct pcpu *create_and_register_pcpu(struct xenpf_pcpuinfo *info) err = register_pcpu(pcpu); if (err) { - pr_warning(XEN_PCPU "Failed to register pcpu%u\n", - info->xen_cpuid); + pr_warn("Failed to register pcpu%u\n", info->xen_cpuid); return ERR_PTR(-ENOENT); } @@ -378,19 +378,19 @@ static int __init xen_pcpu_init(void) xen_pcpu_interrupt, 0, "xen-pcpu", NULL); if (irq < 0) { - pr_warning(XEN_PCPU "Failed to bind pcpu virq\n"); + pr_warn("Failed to bind pcpu virq\n"); return irq; } ret = subsys_system_register(&xen_pcpu_subsys, NULL); if (ret) { - pr_warning(XEN_PCPU "Failed to register pcpu subsys\n"); + pr_warn("Failed to register pcpu subsys\n"); goto err1; } ret = xen_sync_pcpus(); if (ret) { - pr_warning(XEN_PCPU "Failed to sync pcpu info\n"); + pr_warn("Failed to sync pcpu info\n"); goto err2; } diff --git a/drivers/xen/privcmd.c b/drivers/xen/privcmd.c index 2cfc24d76fc5..f8e5dd701ecb 100644 --- a/drivers/xen/privcmd.c +++ b/drivers/xen/privcmd.c @@ -6,6 +6,8 @@ * Copyright (c) 2002-2004, K A Fraser, B Dragovic */ +#define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt + #include #include #include @@ -565,7 +567,7 @@ static int __init privcmd_init(void) err = misc_register(&privcmd_dev); if (err != 0) { - printk(KERN_ERR "Could not register Xen privcmd device\n"); + pr_err("Could not register Xen privcmd device\n"); return err; } return 0; diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c index 1d94316f0ea4..aadffcf7db9b 100644 --- a/drivers/xen/swiotlb-xen.c +++ b/drivers/xen/swiotlb-xen.c @@ -33,6 +33,8 @@ * */ +#define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt + #include #include #include @@ -202,8 +204,8 @@ retry: order--; } if (order != get_order(bytes)) { - pr_warn("Warning: only able to allocate %ld MB " - "for software IO TLB\n", (PAGE_SIZE << order) >> 20); + pr_warn("Warning: only able to allocate %ld MB for software IO TLB\n", + (PAGE_SIZE << order) >> 20); xen_io_tlb_nslabs = SLABS_PER_PAGE << order; bytes = xen_io_tlb_nslabs << IO_TLB_SHIFT; } @@ -242,11 +244,11 @@ error: if (repeat--) { xen_io_tlb_nslabs = max(1024UL, /* Min is 2MB */ (xen_io_tlb_nslabs >> 1)); - printk(KERN_INFO "Xen-SWIOTLB: Lowering to %luMB\n", - (xen_io_tlb_nslabs << IO_TLB_SHIFT) >> 20); + pr_info("Lowering to %luMB\n", + (xen_io_tlb_nslabs << IO_TLB_SHIFT) >> 20); goto retry; } - pr_err("%s (rc:%d)", xen_swiotlb_error(m_ret), rc); + pr_err("%s (rc:%d)\n", xen_swiotlb_error(m_ret), rc); if (early) panic("%s (rc:%d)", xen_swiotlb_error(m_ret), rc); else diff --git a/drivers/xen/tmem.c b/drivers/xen/tmem.c index cc072c66c766..fd823877b947 100644 --- a/drivers/xen/tmem.c +++ b/drivers/xen/tmem.c @@ -5,6 +5,8 @@ * Author: Dan Magenheimer */ +#define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt + #include #include #include @@ -388,8 +390,8 @@ static int xen_tmem_init(void) return PTR_ERR(old_ops); s = " (WARNING: frontswap_ops overridden)"; } - printk(KERN_INFO "frontswap enabled, RAM provided by " - "Xen Transcendent Memory%s\n", s); + pr_info("frontswap enabled, RAM provided by Xen Transcendent Memory%s\n", + s); } #endif #ifdef CONFIG_CLEANCACHE @@ -400,8 +402,8 @@ static int xen_tmem_init(void) cleancache_register_ops(&tmem_cleancache_ops); if (old_ops) s = " (WARNING: cleancache_ops overridden)"; - printk(KERN_INFO "cleancache enabled, RAM provided by " - "Xen Transcendent Memory%s\n", s); + pr_info("cleancache enabled, RAM provided by Xen Transcendent Memory%s\n", + s); } #endif #ifdef CONFIG_XEN_SELFBALLOONING diff --git a/drivers/xen/xen-acpi-cpuhotplug.c b/drivers/xen/xen-acpi-cpuhotplug.c index 18c742bec91b..0caf4863be8c 100644 --- a/drivers/xen/xen-acpi-cpuhotplug.c +++ b/drivers/xen/xen-acpi-cpuhotplug.c @@ -15,6 +15,8 @@ * details. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include diff --git a/drivers/xen/xen-acpi-memhotplug.c b/drivers/xen/xen-acpi-memhotplug.c index faef5b396051..9083f1e474f8 100644 --- a/drivers/xen/xen-acpi-memhotplug.c +++ b/drivers/xen/xen-acpi-memhotplug.c @@ -15,6 +15,8 @@ * details. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include diff --git a/drivers/xen/xen-acpi-pad.c b/drivers/xen/xen-acpi-pad.c index c763479ed85e..59708fdd068b 100644 --- a/drivers/xen/xen-acpi-pad.c +++ b/drivers/xen/xen-acpi-pad.c @@ -14,6 +14,8 @@ * more details. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include diff --git a/drivers/xen/xen-acpi-processor.c b/drivers/xen/xen-acpi-processor.c index 8abd7d579037..13bc6c31c060 100644 --- a/drivers/xen/xen-acpi-processor.c +++ b/drivers/xen/xen-acpi-processor.c @@ -17,6 +17,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -34,8 +36,6 @@ #include #include -#define DRV_NAME "xen-acpi-processor: " - static int no_hypercall; MODULE_PARM_DESC(off, "Inhibit the hypercall."); module_param_named(off, no_hypercall, int, 0400); @@ -104,7 +104,7 @@ static int push_cxx_to_hypervisor(struct acpi_processor *_pr) set_xen_guest_handle(dst_cx->dp, NULL); } if (!ok) { - pr_debug(DRV_NAME "No _Cx for ACPI CPU %u\n", _pr->acpi_id); + pr_debug("No _Cx for ACPI CPU %u\n", _pr->acpi_id); kfree(dst_cx_states); return -EINVAL; } @@ -133,7 +133,7 @@ static int push_cxx_to_hypervisor(struct acpi_processor *_pr) /* EINVAL means the ACPI ID is incorrect - meaning the ACPI * table is referencing a non-existing CPU - which can happen * with broken ACPI tables. */ - pr_err(DRV_NAME "(CX): Hypervisor error (%d) for ACPI CPU%u\n", + pr_err("(CX): Hypervisor error (%d) for ACPI CPU%u\n", ret, _pr->acpi_id); kfree(dst_cx_states); @@ -239,7 +239,7 @@ static int push_pxx_to_hypervisor(struct acpi_processor *_pr) dst_perf->flags |= XEN_PX_PSD; if (dst_perf->flags != (XEN_PX_PSD | XEN_PX_PSS | XEN_PX_PCT | XEN_PX_PPC)) { - pr_warn(DRV_NAME "ACPI CPU%u missing some P-state data (%x), skipping.\n", + pr_warn("ACPI CPU%u missing some P-state data (%x), skipping\n", _pr->acpi_id, dst_perf->flags); ret = -ENODEV; goto err_free; @@ -265,8 +265,8 @@ static int push_pxx_to_hypervisor(struct acpi_processor *_pr) /* EINVAL means the ACPI ID is incorrect - meaning the ACPI * table is referencing a non-existing CPU - which can happen * with broken ACPI tables. */ - pr_warn(DRV_NAME "(_PXX): Hypervisor error (%d) for ACPI CPU%u\n", - ret, _pr->acpi_id); + pr_warn("(_PXX): Hypervisor error (%d) for ACPI CPU%u\n", + ret, _pr->acpi_id); err_free: if (!IS_ERR_OR_NULL(dst_states)) kfree(dst_states); @@ -318,7 +318,7 @@ static unsigned int __init get_max_acpi_id(void) max_acpi_id = max(info->acpi_id, max_acpi_id); } max_acpi_id *= 2; /* Slack for CPU hotplug support. */ - pr_debug(DRV_NAME "Max ACPI ID: %u\n", max_acpi_id); + pr_debug("Max ACPI ID: %u\n", max_acpi_id); return max_acpi_id; } /* @@ -365,15 +365,14 @@ read_acpi_id(acpi_handle handle, u32 lvl, void *context, void **rv) /* There are more ACPI Processor objects than in x2APIC or MADT. * This can happen with incorrect ACPI SSDT declerations. */ if (acpi_id > nr_acpi_bits) { - pr_debug(DRV_NAME "We only have %u, trying to set %u\n", + pr_debug("We only have %u, trying to set %u\n", nr_acpi_bits, acpi_id); return AE_OK; } /* OK, There is a ACPI Processor object */ __set_bit(acpi_id, acpi_id_present); - pr_debug(DRV_NAME "ACPI CPU%u w/ PBLK:0x%lx\n", acpi_id, - (unsigned long)pblk); + pr_debug("ACPI CPU%u w/ PBLK:0x%lx\n", acpi_id, (unsigned long)pblk); status = acpi_evaluate_object(handle, "_CST", NULL, &buffer); if (ACPI_FAILURE(status)) { @@ -476,7 +475,7 @@ static int xen_upload_processor_pm_data(void) unsigned int i; int rc = 0; - pr_info(DRV_NAME "Uploading Xen processor PM info\n"); + pr_info("Uploading Xen processor PM info\n"); for_each_possible_cpu(i) { struct acpi_processor *_pr; @@ -523,7 +522,7 @@ static int __init xen_acpi_processor_init(void) acpi_perf_data = alloc_percpu(struct acpi_processor_performance); if (!acpi_perf_data) { - pr_debug(DRV_NAME "Memory allocation error for acpi_perf_data.\n"); + pr_debug("Memory allocation error for acpi_perf_data\n"); kfree(acpi_ids_done); return -ENOMEM; } diff --git a/drivers/xen/xen-balloon.c b/drivers/xen/xen-balloon.c index 8f37e23f6d13..e555845d61fa 100644 --- a/drivers/xen/xen-balloon.c +++ b/drivers/xen/xen-balloon.c @@ -30,6 +30,8 @@ * IN THE SOFTWARE. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -81,7 +83,7 @@ static int balloon_init_watcher(struct notifier_block *notifier, err = register_xenbus_watch(&target_watch); if (err) - printk(KERN_ERR "Failed to set balloon watcher\n"); + pr_err("Failed to set balloon watcher\n"); return NOTIFY_DONE; } @@ -95,7 +97,7 @@ static int __init balloon_init(void) if (!xen_domain()) return -ENODEV; - pr_info("xen-balloon: Initialising balloon driver.\n"); + pr_info("Initialising balloon driver\n"); register_balloon(&balloon_dev); diff --git a/drivers/xen/xen-pciback/conf_space_header.c b/drivers/xen/xen-pciback/conf_space_header.c index 3daf862d739d..c5ee82587e8c 100644 --- a/drivers/xen/xen-pciback/conf_space_header.c +++ b/drivers/xen/xen-pciback/conf_space_header.c @@ -4,6 +4,8 @@ * Author: Ryan Wilson */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include "pciback.h" @@ -75,10 +77,8 @@ static int command_write(struct pci_dev *dev, int offset, u16 value, void *data) pci_name(dev)); err = pci_set_mwi(dev); if (err) { - printk(KERN_WARNING - DRV_NAME ": %s: cannot enable " - "memory-write-invalidate (%d)\n", - pci_name(dev), err); + pr_warn("%s: cannot enable memory-write-invalidate (%d)\n", + pci_name(dev), err); value &= ~PCI_COMMAND_INVALIDATE; } } @@ -91,7 +91,7 @@ static int rom_write(struct pci_dev *dev, int offset, u32 value, void *data) struct pci_bar_info *bar = data; if (unlikely(!bar)) { - printk(KERN_WARNING DRV_NAME ": driver data not found for %s\n", + pr_warn(DRV_NAME ": driver data not found for %s\n", pci_name(dev)); return XEN_PCI_ERR_op_failed; } @@ -125,7 +125,7 @@ static int bar_write(struct pci_dev *dev, int offset, u32 value, void *data) struct pci_bar_info *bar = data; if (unlikely(!bar)) { - printk(KERN_WARNING DRV_NAME ": driver data not found for %s\n", + pr_warn(DRV_NAME ": driver data not found for %s\n", pci_name(dev)); return XEN_PCI_ERR_op_failed; } @@ -153,7 +153,7 @@ static int bar_read(struct pci_dev *dev, int offset, u32 * value, void *data) struct pci_bar_info *bar = data; if (unlikely(!bar)) { - printk(KERN_WARNING DRV_NAME ": driver data not found for %s\n", + pr_warn(DRV_NAME ": driver data not found for %s\n", pci_name(dev)); return XEN_PCI_ERR_op_failed; } @@ -375,7 +375,7 @@ int xen_pcibk_config_header_add_fields(struct pci_dev *dev) default: err = -EINVAL; - printk(KERN_ERR DRV_NAME ": %s: Unsupported header type %d!\n", + pr_err("%s: Unsupported header type %d!\n", pci_name(dev), dev->hdr_type); break; } diff --git a/drivers/xen/xen-pciback/pci_stub.c b/drivers/xen/xen-pciback/pci_stub.c index 002089918260..62fcd485f0a7 100644 --- a/drivers/xen/xen-pciback/pci_stub.c +++ b/drivers/xen/xen-pciback/pci_stub.c @@ -4,6 +4,9 @@ * Ryan Wilson * Chris Bookholt */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -425,8 +428,6 @@ static int __init pcistub_init_devices_late(void) unsigned long flags; int err = 0; - pr_debug(DRV_NAME ": pcistub_init_devices_late\n"); - spin_lock_irqsave(&pcistub_devices_lock, flags); while (!list_empty(&seized_devices)) { @@ -544,15 +545,11 @@ static void pcistub_remove(struct pci_dev *dev) found_psdev->pdev); if (found_psdev->pdev) { - printk(KERN_WARNING DRV_NAME ": ****** removing device " - "%s while still in-use! ******\n", + pr_warn("****** removing device %s while still in-use! ******\n", pci_name(found_psdev->dev)); - printk(KERN_WARNING DRV_NAME ": ****** driver domain may" - " still access this device's i/o resources!\n"); - printk(KERN_WARNING DRV_NAME ": ****** shutdown driver " - "domain before binding device\n"); - printk(KERN_WARNING DRV_NAME ": ****** to other drivers " - "or domains\n"); + pr_warn("****** driver domain may still access this device's i/o resources!\n"); + pr_warn("****** shutdown driver domain before binding device\n"); + pr_warn("****** to other drivers or domains\n"); xen_pcibk_release_pci_dev(found_psdev->pdev, found_psdev->dev); @@ -1018,7 +1015,7 @@ static int pcistub_device_id_add(int domain, int bus, int slot, int func) pci_dev_id->bus = bus; pci_dev_id->devfn = devfn; - pr_debug(DRV_NAME ": wants to seize %04x:%02x:%02x.%d\n", + pr_debug("wants to seize %04x:%02x:%02x.%d\n", domain, bus, slot, func); spin_lock_irqsave(&device_ids_lock, flags); @@ -1048,8 +1045,8 @@ static int pcistub_device_id_remove(int domain, int bus, int slot, int func) err = 0; - pr_debug(DRV_NAME ": removed %04x:%02x:%02x.%d from " - "seize list\n", domain, bus, slot, func); + pr_debug("removed %04x:%02x:%02x.%d from seize list\n", + domain, bus, slot, func); } } spin_unlock_irqrestore(&device_ids_lock, flags); @@ -1474,7 +1471,7 @@ out: return err; parse_error: - printk(KERN_ERR DRV_NAME ": Error parsing pci_devs_to_hide at \"%s\"\n", + pr_err("Error parsing pci_devs_to_hide at \"%s\"\n", pci_devs_to_hide + pos); return -EINVAL; } diff --git a/drivers/xen/xen-pciback/pciback_ops.c b/drivers/xen/xen-pciback/pciback_ops.c index b98cf0c35725..64eb0cd8b8af 100644 --- a/drivers/xen/xen-pciback/pciback_ops.c +++ b/drivers/xen/xen-pciback/pciback_ops.c @@ -3,6 +3,9 @@ * * Author: Ryan Wilson */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -144,7 +147,7 @@ int xen_pcibk_enable_msi(struct xen_pcibk_device *pdev, status = pci_enable_msi(dev); if (status) { - pr_warn_ratelimited(DRV_NAME ": %s: error enabling MSI for guest %u: err %d\n", + pr_warn_ratelimited("%s: error enabling MSI for guest %u: err %d\n", pci_name(dev), pdev->xdev->otherend_id, status); op->value = 0; @@ -225,7 +228,7 @@ int xen_pcibk_enable_msix(struct xen_pcibk_device *pdev, op->msix_entries[i].vector); } } else - pr_warn_ratelimited(DRV_NAME ": %s: error enabling MSI-X for guest %u: err %d!\n", + pr_warn_ratelimited("%s: error enabling MSI-X for guest %u: err %d!\n", pci_name(dev), pdev->xdev->otherend_id, result); kfree(entries); @@ -372,7 +375,7 @@ static irqreturn_t xen_pcibk_guest_interrupt(int irq, void *dev_id) dev_data->handled++; if ((dev_data->handled % 1000) == 0) { if (xen_test_irq_shared(irq)) { - printk(KERN_INFO "%s IRQ line is not shared " + pr_info("%s IRQ line is not shared " "with other domains. Turning ISR off\n", dev_data->irq_name); dev_data->ack_intr = 0; diff --git a/drivers/xen/xen-pciback/vpci.c b/drivers/xen/xen-pciback/vpci.c index 0f478ac483cd..3165ce361b00 100644 --- a/drivers/xen/xen-pciback/vpci.c +++ b/drivers/xen/xen-pciback/vpci.c @@ -5,6 +5,8 @@ * Author: Ryan Wilson */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -102,8 +104,7 @@ static int __xen_pcibk_add_pci_dev(struct xen_pcibk_device *pdev, struct pci_dev_entry, list); if (match_slot(dev, t->dev)) { - pr_info(DRV_NAME ": vpci: %s: " - "assign to virtual slot %d func %d\n", + pr_info("vpci: %s: assign to virtual slot %d func %d\n", pci_name(dev), slot, PCI_FUNC(dev->devfn)); list_add_tail(&dev_entry->list, @@ -117,9 +118,8 @@ static int __xen_pcibk_add_pci_dev(struct xen_pcibk_device *pdev, /* Assign to a new slot on the virtual PCI bus */ for (slot = 0; slot < PCI_SLOT_MAX; slot++) { if (list_empty(&vpci_dev->dev_list[slot])) { - printk(KERN_INFO DRV_NAME - ": vpci: %s: assign to virtual slot %d\n", - pci_name(dev), slot); + pr_info("vpci: %s: assign to virtual slot %d\n", + pci_name(dev), slot); list_add_tail(&dev_entry->list, &vpci_dev->dev_list[slot]); func = dev->is_virtfn ? 0 : PCI_FUNC(dev->devfn); diff --git a/drivers/xen/xen-pciback/xenbus.c b/drivers/xen/xen-pciback/xenbus.c index 64b11f99eacc..a9ed867afaba 100644 --- a/drivers/xen/xen-pciback/xenbus.c +++ b/drivers/xen/xen-pciback/xenbus.c @@ -3,6 +3,9 @@ * * Author: Ryan Wilson */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -723,14 +726,13 @@ int __init xen_pcibk_xenbus_register(void) { xen_pcibk_wq = create_workqueue("xen_pciback_workqueue"); if (!xen_pcibk_wq) { - printk(KERN_ERR "%s: create" - "xen_pciback_workqueue failed\n", __func__); + pr_err("%s: create xen_pciback_workqueue failed\n", __func__); return -EFAULT; } xen_pcibk_backend = &xen_pcibk_vpci_backend; if (passthrough) xen_pcibk_backend = &xen_pcibk_passthrough_backend; - pr_info(DRV_NAME ": backend is %s\n", xen_pcibk_backend->name); + pr_info("backend is %s\n", xen_pcibk_backend->name); return xenbus_register_backend(&xen_pcibk_driver); } diff --git a/drivers/xen/xen-selfballoon.c b/drivers/xen/xen-selfballoon.c index f70984a892aa..02817a85f877 100644 --- a/drivers/xen/xen-selfballoon.c +++ b/drivers/xen/xen-selfballoon.c @@ -64,6 +64,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -510,22 +512,19 @@ int xen_selfballoon_init(bool use_selfballooning, bool use_frontswap_selfshrink) return -ENODEV; if (xen_initial_domain()) { - pr_info("xen/balloon: Xen selfballooning driver " - "disabled for domain0.\n"); + pr_info("Xen selfballooning driver disabled for domain0\n"); return -ENODEV; } xen_selfballooning_enabled = tmem_enabled && use_selfballooning; if (xen_selfballooning_enabled) { - pr_info("xen/balloon: Initializing Xen " - "selfballooning driver.\n"); + pr_info("Initializing Xen selfballooning driver\n"); enable = true; } #ifdef CONFIG_FRONTSWAP frontswap_selfshrinking = tmem_enabled && use_frontswap_selfshrink; if (frontswap_selfshrinking) { - pr_info("xen/balloon: Initializing frontswap " - "selfshrinking driver.\n"); + pr_info("Initializing frontswap selfshrinking driver\n"); enable = true; } #endif diff --git a/drivers/xen/xenbus/xenbus_comms.c b/drivers/xen/xenbus/xenbus_comms.c index c5aa55c5d371..fdb0f339d0a7 100644 --- a/drivers/xen/xenbus/xenbus_comms.c +++ b/drivers/xen/xenbus/xenbus_comms.c @@ -30,6 +30,8 @@ * IN THE SOFTWARE. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -205,13 +207,12 @@ int xb_init_comms(void) struct xenstore_domain_interface *intf = xen_store_interface; if (intf->req_prod != intf->req_cons) - printk(KERN_ERR "XENBUS request ring is not quiescent " - "(%08x:%08x)!\n", intf->req_cons, intf->req_prod); + pr_err("request ring is not quiescent (%08x:%08x)!\n", + intf->req_cons, intf->req_prod); if (intf->rsp_prod != intf->rsp_cons) { - printk(KERN_WARNING "XENBUS response ring is not quiescent " - "(%08x:%08x): fixing up\n", - intf->rsp_cons, intf->rsp_prod); + pr_warn("response ring is not quiescent (%08x:%08x): fixing up\n", + intf->rsp_cons, intf->rsp_prod); /* breaks kdump */ if (!reset_devices) intf->rsp_cons = intf->rsp_prod; @@ -225,7 +226,7 @@ int xb_init_comms(void) err = bind_evtchn_to_irqhandler(xen_store_evtchn, wake_waiting, 0, "xenbus", &xb_waitq); if (err < 0) { - printk(KERN_ERR "XENBUS request irq failed %i\n", err); + pr_err("request irq failed %i\n", err); return err; } diff --git a/drivers/xen/xenbus/xenbus_dev_backend.c b/drivers/xen/xenbus/xenbus_dev_backend.c index a6f42fc01407..b17707ee07d4 100644 --- a/drivers/xen/xenbus/xenbus_dev_backend.c +++ b/drivers/xen/xenbus/xenbus_dev_backend.c @@ -1,3 +1,5 @@ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -127,7 +129,7 @@ static int __init xenbus_backend_init(void) err = misc_register(&xenbus_backend_dev); if (err) - printk(KERN_ERR "Could not register xenbus backend device\n"); + pr_err("Could not register xenbus backend device\n"); return err; } diff --git a/drivers/xen/xenbus/xenbus_dev_frontend.c b/drivers/xen/xenbus/xenbus_dev_frontend.c index ac727028e658..85534ea63555 100644 --- a/drivers/xen/xenbus/xenbus_dev_frontend.c +++ b/drivers/xen/xenbus/xenbus_dev_frontend.c @@ -35,6 +35,8 @@ * Turned xenfs into a loadable module. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -616,7 +618,7 @@ static int __init xenbus_init(void) err = misc_register(&xenbus_dev); if (err) - printk(KERN_ERR "Could not register xenbus frontend device\n"); + pr_err("Could not register xenbus frontend device\n"); return err; } diff --git a/drivers/xen/xenbus/xenbus_probe.c b/drivers/xen/xenbus/xenbus_probe.c index 56cfaaa9d006..cb1afc00c96d 100644 --- a/drivers/xen/xenbus/xenbus_probe.c +++ b/drivers/xen/xenbus/xenbus_probe.c @@ -30,6 +30,8 @@ * IN THE SOFTWARE. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define DPRINTK(fmt, args...) \ pr_debug("xenbus_probe (%s:%d) " fmt ".\n", \ __func__, __LINE__, ##args) @@ -280,15 +282,15 @@ void xenbus_dev_shutdown(struct device *_dev) get_device(&dev->dev); if (dev->state != XenbusStateConnected) { - printk(KERN_INFO "%s: %s: %s != Connected, skipping\n", __func__, - dev->nodename, xenbus_strstate(dev->state)); + pr_info("%s: %s: %s != Connected, skipping\n", + __func__, dev->nodename, xenbus_strstate(dev->state)); goto out; } xenbus_switch_state(dev, XenbusStateClosing); timeout = wait_for_completion_timeout(&dev->down, timeout); if (!timeout) - printk(KERN_INFO "%s: %s timeout closing device\n", - __func__, dev->nodename); + pr_info("%s: %s timeout closing device\n", + __func__, dev->nodename); out: put_device(&dev->dev); } @@ -579,8 +581,7 @@ int xenbus_dev_suspend(struct device *dev) if (drv->suspend) err = drv->suspend(xdev); if (err) - printk(KERN_WARNING - "xenbus: suspend %s failed: %i\n", dev_name(dev), err); + pr_warn("suspend %s failed: %i\n", dev_name(dev), err); return 0; } EXPORT_SYMBOL_GPL(xenbus_dev_suspend); @@ -599,9 +600,8 @@ int xenbus_dev_resume(struct device *dev) drv = to_xenbus_driver(dev->driver); err = talk_to_otherend(xdev); if (err) { - printk(KERN_WARNING - "xenbus: resume (talk_to_otherend) %s failed: %i\n", - dev_name(dev), err); + pr_warn("resume (talk_to_otherend) %s failed: %i\n", + dev_name(dev), err); return err; } @@ -610,18 +610,15 @@ int xenbus_dev_resume(struct device *dev) if (drv->resume) { err = drv->resume(xdev); if (err) { - printk(KERN_WARNING - "xenbus: resume %s failed: %i\n", - dev_name(dev), err); + pr_warn("resume %s failed: %i\n", dev_name(dev), err); return err; } } err = watch_otherend(xdev); if (err) { - printk(KERN_WARNING - "xenbus_probe: resume (watch_otherend) %s failed: " - "%d.\n", dev_name(dev), err); + pr_warn("resume (watch_otherend) %s failed: %d.\n", + dev_name(dev), err); return err; } @@ -776,8 +773,7 @@ static int __init xenbus_init(void) /* Initialize the interface to xenstore. */ err = xs_init(); if (err) { - printk(KERN_WARNING - "XENBUS: Error initializing xenstore comms: %i\n", err); + pr_warn("Error initializing xenstore comms: %i\n", err); goto out_error; } diff --git a/drivers/xen/xenbus/xenbus_probe_backend.c b/drivers/xen/xenbus/xenbus_probe_backend.c index 257be37d9091..998bbbab816b 100644 --- a/drivers/xen/xenbus/xenbus_probe_backend.c +++ b/drivers/xen/xenbus/xenbus_probe_backend.c @@ -31,9 +31,11 @@ * IN THE SOFTWARE. */ -#define DPRINTK(fmt, args...) \ - pr_debug("xenbus_probe (%s:%d) " fmt ".\n", \ - __func__, __LINE__, ##args) +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define DPRINTK(fmt, ...) \ + pr_debug("(%s:%d) " fmt "\n", \ + __func__, __LINE__, ##__VA_ARGS__) #include #include diff --git a/drivers/xen/xenbus/xenbus_probe_frontend.c b/drivers/xen/xenbus/xenbus_probe_frontend.c index a7e25073de19..6ed8a9df4472 100644 --- a/drivers/xen/xenbus/xenbus_probe_frontend.c +++ b/drivers/xen/xenbus/xenbus_probe_frontend.c @@ -1,6 +1,8 @@ -#define DPRINTK(fmt, args...) \ - pr_debug("xenbus_probe (%s:%d) " fmt ".\n", \ - __func__, __LINE__, ##args) +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define DPRINTK(fmt, ...) \ + pr_debug("(%s:%d) " fmt "\n", \ + __func__, __LINE__, ##__VA_ARGS__) #include #include @@ -36,13 +38,13 @@ static int frontend_bus_id(char bus_id[XEN_BUS_ID_SIZE], const char *nodename) { nodename = strchr(nodename, '/'); if (!nodename || strlen(nodename + 1) >= XEN_BUS_ID_SIZE) { - printk(KERN_WARNING "XENBUS: bad frontend %s\n", nodename); + pr_warn("bad frontend %s\n", nodename); return -EINVAL; } strlcpy(bus_id, nodename + 1, XEN_BUS_ID_SIZE); if (!strchr(bus_id, '/')) { - printk(KERN_WARNING "XENBUS: bus_id %s no slash\n", bus_id); + pr_warn("bus_id %s no slash\n", bus_id); return -EINVAL; } *strchr(bus_id, '/') = '-'; @@ -234,15 +236,13 @@ static int print_device_status(struct device *dev, void *data) if (!dev->driver) { /* Information only: is this too noisy? */ - printk(KERN_INFO "XENBUS: Device with no driver: %s\n", - xendev->nodename); + pr_info("Device with no driver: %s\n", xendev->nodename); } else if (xendev->state < XenbusStateConnected) { enum xenbus_state rstate = XenbusStateUnknown; if (xendev->otherend) rstate = xenbus_read_driver_state(xendev->otherend); - printk(KERN_WARNING "XENBUS: Timeout connecting " - "to device: %s (local state %d, remote state %d)\n", - xendev->nodename, xendev->state, rstate); + pr_warn("Timeout connecting to device: %s (local state %d, remote state %d)\n", + xendev->nodename, xendev->state, rstate); } return 0; @@ -256,12 +256,13 @@ static bool wait_loop(unsigned long start, unsigned int max_delay, { if (time_after(jiffies, start + (*seconds_waited+5)*HZ)) { if (!*seconds_waited) - printk(KERN_WARNING "XENBUS: Waiting for " - "devices to initialise: "); + pr_warn("Waiting for devices to initialise: "); *seconds_waited += 5; - printk("%us...", max_delay - *seconds_waited); - if (*seconds_waited == max_delay) + pr_cont("%us...", max_delay - *seconds_waited); + if (*seconds_waited == max_delay) { + pr_cont("\n"); return true; + } } schedule_timeout_interruptible(HZ/10); @@ -342,7 +343,7 @@ static void xenbus_reset_wait_for_backend(char *be, int expected) timeout = wait_event_interruptible_timeout(backend_state_wq, backend_state == expected, 5 * HZ); if (timeout <= 0) - printk(KERN_INFO "XENBUS: backend %s timed out.\n", be); + pr_info("backend %s timed out\n", be); } /* @@ -365,7 +366,7 @@ static void xenbus_reset_frontend(char *fe, char *be, int be_state) be_watch.callback = xenbus_reset_backend_state_changed; backend_state = XenbusStateUnknown; - printk(KERN_INFO "XENBUS: triggering reconnect on %s\n", be); + pr_info("triggering reconnect on %s\n", be); register_xenbus_watch(&be_watch); /* fall through to forward backend to state XenbusStateInitialising */ @@ -384,7 +385,7 @@ static void xenbus_reset_frontend(char *fe, char *be, int be_state) } unregister_xenbus_watch(&be_watch); - printk(KERN_INFO "XENBUS: reconnect done on %s\n", be); + pr_info("reconnect done on %s\n", be); kfree(be_watch.node); } diff --git a/drivers/xen/xenbus/xenbus_xs.c b/drivers/xen/xenbus/xenbus_xs.c index 88e677b0de74..b6d5fff43d16 100644 --- a/drivers/xen/xenbus/xenbus_xs.c +++ b/drivers/xen/xenbus/xenbus_xs.c @@ -31,6 +31,8 @@ * IN THE SOFTWARE. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -129,9 +131,8 @@ static int get_error(const char *errorstring) for (i = 0; strcmp(errorstring, xsd_errors[i].errstring) != 0; i++) { if (i == ARRAY_SIZE(xsd_errors) - 1) { - printk(KERN_WARNING - "XENBUS xen store gave: unknown error %s", - errorstring); + pr_warn("xen store gave: unknown error %s\n", + errorstring); return EINVAL; } } @@ -272,10 +273,8 @@ static void *xs_talkv(struct xenbus_transaction t, } if (msg.type != type) { - if (printk_ratelimit()) - printk(KERN_WARNING - "XENBUS unexpected type [%d], expected [%d]\n", - msg.type, type); + pr_warn_ratelimited("unexpected type [%d], expected [%d]\n", + msg.type, type); kfree(ret); return ERR_PTR(-EINVAL); } @@ -655,7 +654,7 @@ static void xs_reset_watches(void) err = xs_error(xs_single(XBT_NIL, XS_RESET_WATCHES, "", NULL)); if (err && err != -EEXIST) - printk(KERN_WARNING "xs_reset_watches failed: %d\n", err); + pr_warn("xs_reset_watches failed: %d\n", err); } /* Register callback to watch this node. */ @@ -705,9 +704,7 @@ void unregister_xenbus_watch(struct xenbus_watch *watch) err = xs_unwatch(watch->node, token); if (err) - printk(KERN_WARNING - "XENBUS Failed to release watch %s: %i\n", - watch->node, err); + pr_warn("Failed to release watch %s: %i\n", watch->node, err); up_read(&xs_state.watch_mutex); @@ -901,8 +898,7 @@ static int xenbus_thread(void *unused) for (;;) { err = process_msg(); if (err) - printk(KERN_WARNING "XENBUS error %d while reading " - "message\n", err); + pr_warn("error %d while reading message\n", err); if (kthread_should_stop()) break; } diff --git a/drivers/xen/xencomm.c b/drivers/xen/xencomm.c index b91f8ff50d05..4793fc594549 100644 --- a/drivers/xen/xencomm.c +++ b/drivers/xen/xencomm.c @@ -18,6 +18,8 @@ * Authors: Hollis Blanchard */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include diff --git a/drivers/xen/xenfs/super.c b/drivers/xen/xenfs/super.c index 71679875f056..06092e0fe8ce 100644 --- a/drivers/xen/xenfs/super.c +++ b/drivers/xen/xenfs/super.c @@ -7,6 +7,8 @@ * Turned xenfs into a loadable module. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -82,7 +84,7 @@ static int __init xenfs_init(void) if (xen_domain()) return register_filesystem(&xenfs_type); - printk(KERN_INFO "XENFS: not registering filesystem on non-xen platform\n"); + pr_info("not registering filesystem on non-xen platform\n"); return 0; } diff --git a/include/xen/hvm.h b/include/xen/hvm.h index 13e43e41637d..63917a8de3b0 100644 --- a/include/xen/hvm.h +++ b/include/xen/hvm.h @@ -44,8 +44,8 @@ static inline int hvm_get_parameter(int idx, uint64_t *value) xhv.index = idx; r = HYPERVISOR_hvm_op(HVMOP_get_param, &xhv); if (r < 0) { - printk(KERN_ERR "Cannot get hvm parameter %s (%d): %d!\n", - param_name(idx), idx, r); + pr_err("Cannot get hvm parameter %s (%d): %d!\n", + param_name(idx), idx, r); return r; } *value = xhv.value; -- cgit v1.2.3 From 0b0c002c340e78173789f8afaa508070d838cf3d Mon Sep 17 00:00:00 2001 From: Laszlo Ersek Date: Tue, 18 Oct 2011 22:42:59 +0200 Subject: xen/time: remove blocked time accounting from xen "clockchip" ... because the "clock_event_device framework" already accounts for idle time through the "event_handler" function pointer in xen_timer_interrupt(). The patch is intended as the completion of [1]. It should fix the double idle times seen in PV guests' /proc/stat [2]. It should be orthogonal to stolen time accounting (the removed code seems to be isolated). The approach may be completely misguided. [1] https://lkml.org/lkml/2011/10/6/10 [2] http://lists.xensource.com/archives/html/xen-devel/2010-08/msg01068.html John took the time to retest this patch on top of v3.10 and reported: "idle time is correctly incremented for pv and hvm for the normal case, nohz=off and nohz=idle." so lets put this patch in. CC: stable@vger.kernel.org Signed-off-by: Laszlo Ersek Signed-off-by: John Haxby Signed-off-by: Konrad Rzeszutek Wilk --- arch/x86/xen/time.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/arch/x86/xen/time.c b/arch/x86/xen/time.c index aec0b14b6d76..a690868be837 100644 --- a/arch/x86/xen/time.c +++ b/arch/x86/xen/time.c @@ -37,9 +37,8 @@ static DEFINE_PER_CPU(struct vcpu_runstate_info, xen_runstate); /* snapshots of runstate info */ static DEFINE_PER_CPU(struct vcpu_runstate_info, xen_runstate_snapshot); -/* unused ns of stolen and blocked time */ +/* unused ns of stolen time */ static DEFINE_PER_CPU(u64, xen_residual_stolen); -static DEFINE_PER_CPU(u64, xen_residual_blocked); /* return an consistent snapshot of 64-bit time/counter value */ static u64 get64(const u64 *p) @@ -116,7 +115,7 @@ static void do_stolen_accounting(void) { struct vcpu_runstate_info state; struct vcpu_runstate_info *snap; - s64 blocked, runnable, offline, stolen; + s64 runnable, offline, stolen; cputime_t ticks; get_runstate_snapshot(&state); @@ -126,7 +125,6 @@ static void do_stolen_accounting(void) snap = &__get_cpu_var(xen_runstate_snapshot); /* work out how much time the VCPU has not been runn*ing* */ - blocked = state.time[RUNSTATE_blocked] - snap->time[RUNSTATE_blocked]; runnable = state.time[RUNSTATE_runnable] - snap->time[RUNSTATE_runnable]; offline = state.time[RUNSTATE_offline] - snap->time[RUNSTATE_offline]; @@ -142,17 +140,6 @@ static void do_stolen_accounting(void) ticks = iter_div_u64_rem(stolen, NS_PER_TICK, &stolen); __this_cpu_write(xen_residual_stolen, stolen); account_steal_ticks(ticks); - - /* Add the appropriate number of ticks of blocked time, - including any left-overs from last time. */ - blocked += __this_cpu_read(xen_residual_blocked); - - if (blocked < 0) - blocked = 0; - - ticks = iter_div_u64_rem(blocked, NS_PER_TICK, &blocked); - __this_cpu_write(xen_residual_blocked, blocked); - account_idle_ticks(ticks); } /* Get the TSC speed from Xen */ -- cgit v1.2.3 From dc037ad7d24f3711e431a45c053b5d425995e9e4 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Thu, 27 Jun 2013 16:04:59 +1000 Subject: xfs: implement inode change count For CRC enabled filesystems, add support for the monotonic inode version change counter that is needed by protocols like NFSv4 for determining if the inode has changed in any way at all between two unrelated operations on the inode. This bumps the change count the first time an inode is dirtied in a transaction. Since all modifications to the inode are logged, this will catch all changes that are made to the inode, including timestamp updates that occur during data writes. Signed-off-by: Dave Chinner Reviewed-by: Mark Tinguely Reviewed-by: Chandra Seetharaman Signed-off-by: Ben Myers --- fs/xfs/xfs_super.c | 4 ++++ fs/xfs/xfs_trans_inode.c | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c index 30ef68f8a390..e5e8b5ee8bfa 100644 --- a/fs/xfs/xfs_super.c +++ b/fs/xfs/xfs_super.c @@ -1477,6 +1477,10 @@ xfs_fs_fill_super( sb->s_time_gran = 1; set_posix_acl_flag(sb); + /* version 5 superblocks support inode version counters. */ + if (XFS_SB_VERSION_NUM(&mp->m_sb) == XFS_SB_VERSION_5) + sb->s_flags |= MS_I_VERSION; + error = xfs_mountfs(mp); if (error) goto out_filestream_unmount; diff --git a/fs/xfs/xfs_trans_inode.c b/fs/xfs/xfs_trans_inode.c index ac6d567704db..53dfe46f3680 100644 --- a/fs/xfs/xfs_trans_inode.c +++ b/fs/xfs/xfs_trans_inode.c @@ -112,6 +112,17 @@ xfs_trans_log_inode( ASSERT(ip->i_itemp != NULL); ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); + /* + * First time we log the inode in a transaction, bump the inode change + * counter if it is configured for this to occur. + */ + if (!(ip->i_itemp->ili_item.li_desc->lid_flags & XFS_LID_DIRTY) && + IS_I_VERSION(VFS_I(ip))) { + inode_inc_iversion(VFS_I(ip)); + ip->i_d.di_changecount = VFS_I(ip)->i_version; + flags |= XFS_ILOG_CORE; + } + tp->t_flags |= XFS_TRANS_DIRTY; ip->i_itemp->ili_item.li_desc->lid_flags |= XFS_LID_DIRTY; -- cgit v1.2.3 From 9cad19d2cb57a2c32887a303b516d74254aa4b1c Mon Sep 17 00:00:00 2001 From: Chandra Seetharaman Date: Thu, 27 Jun 2013 17:25:04 -0500 Subject: xfs: Define a new function xfs_is_quota_inode() In preparation for combined pquota/gquota support, define a new function to check if the given inode is a quota inode. Signed-off-by: Chandra Seetharaman Reviewed-by: Ben Myers Signed-off-by: Ben Myers --- fs/xfs/xfs_itable.c | 2 +- fs/xfs/xfs_qm.c | 8 +++----- fs/xfs/xfs_sb.h | 6 ++++++ fs/xfs/xfs_trans_dquot.c | 6 ++---- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/fs/xfs/xfs_itable.c b/fs/xfs/xfs_itable.c index 06d004d85bf4..bc92c5306a17 100644 --- a/fs/xfs/xfs_itable.c +++ b/fs/xfs/xfs_itable.c @@ -43,7 +43,7 @@ xfs_internal_inum( { return (ino == mp->m_sb.sb_rbmino || ino == mp->m_sb.sb_rsumino || (xfs_sb_version_hasquota(&mp->m_sb) && - (ino == mp->m_sb.sb_uquotino || ino == mp->m_sb.sb_gquotino))); + xfs_is_quota_inode(&mp->m_sb, ino))); } /* diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index b75c9bb6e71e..28a5e8ae3829 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -489,8 +489,7 @@ xfs_qm_need_dqattach( return false; if (!XFS_NOT_DQATTACHED(mp, ip)) return false; - if (ip->i_ino == mp->m_sb.sb_uquotino || - ip->i_ino == mp->m_sb.sb_gquotino) + if (xfs_is_quota_inode(&mp->m_sb, ip->i_ino)) return false; return true; } @@ -606,8 +605,7 @@ xfs_qm_dqdetach( trace_xfs_dquot_dqdetach(ip); - ASSERT(ip->i_ino != ip->i_mount->m_sb.sb_uquotino); - ASSERT(ip->i_ino != ip->i_mount->m_sb.sb_gquotino); + ASSERT(!xfs_is_quota_inode(&ip->i_mount->m_sb, ip->i_ino)); if (ip->i_udquot) { xfs_qm_dqrele(ip->i_udquot); ip->i_udquot = NULL; @@ -1152,7 +1150,7 @@ xfs_qm_dqusage_adjust( * rootino must have its resources accounted for, not so with the quota * inodes. */ - if (ino == mp->m_sb.sb_uquotino || ino == mp->m_sb.sb_gquotino) { + if (xfs_is_quota_inode(&mp->m_sb, ino)) { *res = BULKSTAT_RV_NOTHING; return XFS_ERROR(EINVAL); } diff --git a/fs/xfs/xfs_sb.h b/fs/xfs/xfs_sb.h index 2de58a85833c..78f9e70b80c7 100644 --- a/fs/xfs/xfs_sb.h +++ b/fs/xfs/xfs_sb.h @@ -618,6 +618,12 @@ xfs_sb_has_incompat_log_feature( return (sbp->sb_features_log_incompat & feature) != 0; } +static inline bool +xfs_is_quota_inode(struct xfs_sb *sbp, xfs_ino_t ino) +{ + return (ino == sbp->sb_uquotino || ino == sbp->sb_gquotino); +} + /* * end of superblock version macros */ diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c index fec75d023703..3fa369c75a4d 100644 --- a/fs/xfs/xfs_trans_dquot.c +++ b/fs/xfs/xfs_trans_dquot.c @@ -157,8 +157,7 @@ xfs_trans_mod_dquot_byino( if (!XFS_IS_QUOTA_RUNNING(mp) || !XFS_IS_QUOTA_ON(mp) || - ip->i_ino == mp->m_sb.sb_uquotino || - ip->i_ino == mp->m_sb.sb_gquotino) + xfs_is_quota_inode(&mp->m_sb, ip->i_ino)) return; if (tp->t_dqinfo == NULL) @@ -816,8 +815,7 @@ xfs_trans_reserve_quota_nblks( if (XFS_IS_PQUOTA_ON(mp)) flags |= XFS_QMOPT_ENOSPC; - ASSERT(ip->i_ino != mp->m_sb.sb_uquotino); - ASSERT(ip->i_ino != mp->m_sb.sb_gquotino); + ASSERT(!xfs_is_quota_inode(&mp->m_sb, ip->i_ino)); ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); ASSERT((flags & ~(XFS_QMOPT_FORCE_RES | XFS_QMOPT_ENOSPC)) == -- cgit v1.2.3 From 329e0875286984df9053d410df83f839f85bea6e Mon Sep 17 00:00:00 2001 From: Chandra Seetharaman Date: Thu, 27 Jun 2013 17:25:05 -0500 Subject: xfs: Replace macro XFS_DQUOT_TREE with a function In preparation for combined pquota/gquota support, for the sake of readability, change the macro to an inline function. Signed-off-by: Chandra Seetharaman Reviewed-by: Ben Myers Signed-off-by: Ben Myers --- fs/xfs/xfs_dquot.c | 2 +- fs/xfs/xfs_qm.c | 6 +++--- fs/xfs/xfs_qm.h | 22 ++++++++++++++++------ 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index 044e97a33c8d..09af322653a2 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -804,7 +804,7 @@ xfs_qm_dqget( xfs_dquot_t **O_dqpp) /* OUT : locked incore dquot */ { struct xfs_quotainfo *qi = mp->m_quotainfo; - struct radix_tree_root *tree = XFS_DQUOT_TREE(qi, type); + struct radix_tree_root *tree = xfs_dquot_tree(qi, type); struct xfs_dquot *dqp; int error; diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index 28a5e8ae3829..8e707d33e106 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -70,7 +70,7 @@ xfs_qm_dquot_walk( void *data) { struct xfs_quotainfo *qi = mp->m_quotainfo; - struct radix_tree_root *tree = XFS_DQUOT_TREE(qi, type); + struct radix_tree_root *tree = xfs_dquot_tree(qi, type); uint32_t next_index; int last_error = 0; int skipped; @@ -189,7 +189,7 @@ xfs_qm_dqpurge( xfs_dqfunlock(dqp); xfs_dqunlock(dqp); - radix_tree_delete(XFS_DQUOT_TREE(qi, dqp->q_core.d_flags), + radix_tree_delete(xfs_dquot_tree(qi, dqp->q_core.d_flags), be32_to_cpu(dqp->q_core.d_id)); qi->qi_dquots--; @@ -1471,7 +1471,7 @@ xfs_qm_dqfree_one( struct xfs_quotainfo *qi = mp->m_quotainfo; mutex_lock(&qi->qi_tree_lock); - radix_tree_delete(XFS_DQUOT_TREE(qi, dqp->q_core.d_flags), + radix_tree_delete(xfs_dquot_tree(qi, dqp->q_core.d_flags), be32_to_cpu(dqp->q_core.d_id)); qi->qi_dquots--; diff --git a/fs/xfs/xfs_qm.h b/fs/xfs/xfs_qm.h index 5d16a6e6900f..96568c270670 100644 --- a/fs/xfs/xfs_qm.h +++ b/fs/xfs/xfs_qm.h @@ -69,12 +69,22 @@ typedef struct xfs_quotainfo { struct shrinker qi_shrinker; } xfs_quotainfo_t; -#define XFS_DQUOT_TREE(qi, type) \ - ((type & XFS_DQ_USER) ? \ - &((qi)->qi_uquota_tree) : \ - &((qi)->qi_gquota_tree)) - - +static inline struct radix_tree_root * +xfs_dquot_tree( + struct xfs_quotainfo *qi, + int type) +{ + switch (type) { + case XFS_DQ_USER: + return &qi->qi_uquota_tree; + case XFS_DQ_GROUP: + case XFS_DQ_PROJ: + return &qi->qi_gquota_tree; + default: + ASSERT(0); + } + return NULL; +} extern int xfs_qm_calc_dquots_per_chunk(struct xfs_mount *mp, unsigned int nbblks); extern void xfs_trans_mod_dquot(xfs_trans_t *, xfs_dquot_t *, uint, long); -- cgit v1.2.3 From 995961c4510460d9eef9b5ae46789aa2315545fe Mon Sep 17 00:00:00 2001 From: Chandra Seetharaman Date: Thu, 27 Jun 2013 17:25:06 -0500 Subject: xfs: Replace macro XFS_DQ_TO_QIP with a function In preparation for combined pquota/gquota support, for the sake of readability, change the macro to an inline function. Signed-off-by: Chandra Seetharaman Reviewed-by: Ben Myers Signed-off-by: Ben Myers --- fs/xfs/xfs_dquot.c | 2 +- fs/xfs/xfs_dquot.h | 4 ---- fs/xfs/xfs_qm.h | 16 ++++++++++++++++ 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index 09af322653a2..7d184dead99a 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -573,7 +573,7 @@ xfs_qm_dqtobp( xfs_bmbt_irec_t map; int nmaps = 1, error; xfs_buf_t *bp; - xfs_inode_t *quotip = XFS_DQ_TO_QIP(dqp); + struct xfs_inode *quotip = xfs_dq_to_quota_inode(dqp); xfs_mount_t *mp = dqp->q_mount; xfs_dqid_t id = be32_to_cpu(dqp->q_core.d_id); xfs_trans_t *tp = (tpp ? *tpp : NULL); diff --git a/fs/xfs/xfs_dquot.h b/fs/xfs/xfs_dquot.h index 4f0ebfc43cc9..b596626249b8 100644 --- a/fs/xfs/xfs_dquot.h +++ b/fs/xfs/xfs_dquot.h @@ -143,10 +143,6 @@ static inline xfs_dquot_t *xfs_inode_dquot(struct xfs_inode *ip, int type) #define XFS_QM_ISUDQ(dqp) ((dqp)->dq_flags & XFS_DQ_USER) #define XFS_QM_ISPDQ(dqp) ((dqp)->dq_flags & XFS_DQ_PROJ) #define XFS_QM_ISGDQ(dqp) ((dqp)->dq_flags & XFS_DQ_GROUP) -#define XFS_DQ_TO_QINF(dqp) ((dqp)->q_mount->m_quotainfo) -#define XFS_DQ_TO_QIP(dqp) (XFS_QM_ISUDQ(dqp) ? \ - XFS_DQ_TO_QINF(dqp)->qi_uquotaip : \ - XFS_DQ_TO_QINF(dqp)->qi_gquotaip) extern int xfs_qm_dqread(struct xfs_mount *, xfs_dqid_t, uint, uint, struct xfs_dquot **); diff --git a/fs/xfs/xfs_qm.h b/fs/xfs/xfs_qm.h index 96568c270670..051e43a5e20b 100644 --- a/fs/xfs/xfs_qm.h +++ b/fs/xfs/xfs_qm.h @@ -85,6 +85,22 @@ xfs_dquot_tree( } return NULL; } + +static inline struct xfs_inode * +xfs_dq_to_quota_inode(struct xfs_dquot *dqp) +{ + switch (dqp->dq_flags & XFS_DQ_ALLTYPES) { + case XFS_DQ_USER: + return dqp->q_mount->m_quotainfo->qi_uquotaip; + case XFS_DQ_GROUP: + case XFS_DQ_PROJ: + return dqp->q_mount->m_quotainfo->qi_gquotaip; + default: + ASSERT(0); + } + return NULL; +} + extern int xfs_qm_calc_dquots_per_chunk(struct xfs_mount *mp, unsigned int nbblks); extern void xfs_trans_mod_dquot(xfs_trans_t *, xfs_dquot_t *, uint, long); -- cgit v1.2.3 From 113a56835d938d5cf9b4599053da7afb80d6f710 Mon Sep 17 00:00:00 2001 From: Chandra Seetharaman Date: Thu, 27 Jun 2013 17:25:07 -0500 Subject: xfs: Code cleanup and removal of some typedef usage In preparation for combined pquota/gquota support, for the sake of readability, do some code cleanup surrounding the affected code. Signed-off-by: Chandra Seetharaman Reviewed-by: Ben Myers Signed-off-by: Ben Myers --- fs/xfs/xfs_dquot.c | 12 ++-- fs/xfs/xfs_icache.c | 3 +- fs/xfs/xfs_qm.c | 152 +++++++++++++++++++++++++---------------------- fs/xfs/xfs_qm.h | 37 ++++++------ fs/xfs/xfs_qm_syscalls.c | 8 +-- fs/xfs/xfs_symlink.c | 3 +- fs/xfs/xfs_trans_dquot.c | 46 +++++++------- 7 files changed, 135 insertions(+), 126 deletions(-) diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index 7d184dead99a..f01012de06d0 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -570,13 +570,13 @@ xfs_qm_dqtobp( xfs_buf_t **O_bpp, uint flags) { - xfs_bmbt_irec_t map; - int nmaps = 1, error; - xfs_buf_t *bp; + struct xfs_bmbt_irec map; + int nmaps = 1, error; + struct xfs_buf *bp; struct xfs_inode *quotip = xfs_dq_to_quota_inode(dqp); - xfs_mount_t *mp = dqp->q_mount; - xfs_dqid_t id = be32_to_cpu(dqp->q_core.d_id); - xfs_trans_t *tp = (tpp ? *tpp : NULL); + struct xfs_mount *mp = dqp->q_mount; + xfs_dqid_t id = be32_to_cpu(dqp->q_core.d_id); + struct xfs_trans *tp = (tpp ? *tpp : NULL); dqp->q_fileoffset = (xfs_fileoff_t)id / mp->m_quotainfo->qi_dqperchunk; diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c index 96e344e3e927..9560dc1f15a9 100644 --- a/fs/xfs/xfs_icache.c +++ b/fs/xfs/xfs_icache.c @@ -335,7 +335,8 @@ xfs_iget_cache_miss( iflags = XFS_INEW; if (flags & XFS_IGET_DONTCACHE) iflags |= XFS_IDONTCACHE; - ip->i_udquot = ip->i_gdquot = NULL; + ip->i_udquot = NULL; + ip->i_gdquot = NULL; xfs_iflags_set(ip, iflags); /* insert the new inode */ diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index 8e707d33e106..cf09aa845d8c 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -1260,19 +1260,20 @@ int xfs_qm_quotacheck( xfs_mount_t *mp) { - int done, count, error, error2; - xfs_ino_t lastino; - size_t structsz; - xfs_inode_t *uip, *gip; - uint flags; - LIST_HEAD (buffer_list); + int done, count, error, error2; + xfs_ino_t lastino; + size_t structsz; + uint flags; + LIST_HEAD (buffer_list); + struct xfs_inode *uip = mp->m_quotainfo->qi_uquotaip; + struct xfs_inode *gip = mp->m_quotainfo->qi_gquotaip; count = INT_MAX; structsz = 1; lastino = 0; flags = 0; - ASSERT(mp->m_quotainfo->qi_uquotaip || mp->m_quotainfo->qi_gquotaip); + ASSERT(uip || gip); ASSERT(XFS_IS_QUOTA_RUNNING(mp)); xfs_notice(mp, "Quotacheck needed: Please wait."); @@ -1282,7 +1283,6 @@ xfs_qm_quotacheck( * their counters to zero. We need a clean slate. * We don't log our changes till later. */ - uip = mp->m_quotainfo->qi_uquotaip; if (uip) { error = xfs_qm_dqiterate(mp, uip, XFS_QMOPT_UQUOTA, &buffer_list); @@ -1291,7 +1291,6 @@ xfs_qm_quotacheck( flags |= XFS_UQUOTA_CHKD; } - gip = mp->m_quotainfo->qi_gquotaip; if (gip) { error = xfs_qm_dqiterate(mp, gip, XFS_IS_GQUOTA_ON(mp) ? XFS_QMOPT_GQUOTA : XFS_QMOPT_PQUOTA, @@ -1393,15 +1392,13 @@ STATIC int xfs_qm_init_quotainos( xfs_mount_t *mp) { - xfs_inode_t *uip, *gip; - int error; - __int64_t sbflags; - uint flags; + struct xfs_inode *uip = NULL; + struct xfs_inode *gip = NULL; + int error; + __int64_t sbflags = 0; + uint flags = 0; ASSERT(mp->m_quotainfo); - uip = gip = NULL; - sbflags = 0; - flags = 0; /* * Get the uquota and gquota inodes @@ -1410,19 +1407,18 @@ xfs_qm_init_quotainos( if (XFS_IS_UQUOTA_ON(mp) && mp->m_sb.sb_uquotino != NULLFSINO) { ASSERT(mp->m_sb.sb_uquotino > 0); - if ((error = xfs_iget(mp, NULL, mp->m_sb.sb_uquotino, - 0, 0, &uip))) + error = xfs_iget(mp, NULL, mp->m_sb.sb_uquotino, + 0, 0, &uip); + if (error) return XFS_ERROR(error); } if (XFS_IS_OQUOTA_ON(mp) && mp->m_sb.sb_gquotino != NULLFSINO) { ASSERT(mp->m_sb.sb_gquotino > 0); - if ((error = xfs_iget(mp, NULL, mp->m_sb.sb_gquotino, - 0, 0, &gip))) { - if (uip) - IRELE(uip); - return XFS_ERROR(error); - } + error = xfs_iget(mp, NULL, mp->m_sb.sb_gquotino, + 0, 0, &gip); + if (error) + goto error_rele; } } else { flags |= XFS_QMOPT_SBVERSION; @@ -1437,10 +1433,11 @@ xfs_qm_init_quotainos( * temporarily switch to read-write to do this. */ if (XFS_IS_UQUOTA_ON(mp) && uip == NULL) { - if ((error = xfs_qm_qino_alloc(mp, &uip, + error = xfs_qm_qino_alloc(mp, &uip, sbflags | XFS_SB_UQUOTINO, - flags | XFS_QMOPT_UQUOTA))) - return XFS_ERROR(error); + flags | XFS_QMOPT_UQUOTA); + if (error) + goto error_rele; flags &= ~XFS_QMOPT_SBVERSION; } @@ -1449,18 +1446,21 @@ xfs_qm_init_quotainos( XFS_QMOPT_GQUOTA : XFS_QMOPT_PQUOTA); error = xfs_qm_qino_alloc(mp, &gip, sbflags | XFS_SB_GQUOTINO, flags); - if (error) { - if (uip) - IRELE(uip); - - return XFS_ERROR(error); - } + if (error) + goto error_rele; } mp->m_quotainfo->qi_uquotaip = uip; mp->m_quotainfo->qi_gquotaip = gip; return 0; + +error_rele: + if (uip) + IRELE(uip); + if (gip) + IRELE(gip); + return XFS_ERROR(error); } STATIC void @@ -1657,7 +1657,8 @@ xfs_qm_vop_dqalloc( struct xfs_dquot **O_gdqpp) { struct xfs_mount *mp = ip->i_mount; - struct xfs_dquot *uq, *gq; + struct xfs_dquot *uq = NULL; + struct xfs_dquot *gq = NULL; int error; uint lockflags; @@ -1682,7 +1683,6 @@ xfs_qm_vop_dqalloc( } } - uq = gq = NULL; if ((flags & XFS_QMOPT_UQUOTA) && XFS_IS_UQUOTA_ON(mp)) { if (ip->i_d.di_uid != uid) { /* @@ -1695,11 +1695,12 @@ xfs_qm_vop_dqalloc( * holding ilock. */ xfs_iunlock(ip, lockflags); - if ((error = xfs_qm_dqget(mp, NULL, (xfs_dqid_t) uid, + error = xfs_qm_dqget(mp, NULL, (xfs_dqid_t) uid, XFS_DQ_USER, XFS_QMOPT_DQALLOC | XFS_QMOPT_DOWARN, - &uq))) { + &uq); + if (error) { ASSERT(error != ENOENT); return error; } @@ -1721,15 +1722,14 @@ xfs_qm_vop_dqalloc( if ((flags & XFS_QMOPT_GQUOTA) && XFS_IS_GQUOTA_ON(mp)) { if (ip->i_d.di_gid != gid) { xfs_iunlock(ip, lockflags); - if ((error = xfs_qm_dqget(mp, NULL, (xfs_dqid_t)gid, + error = xfs_qm_dqget(mp, NULL, (xfs_dqid_t)gid, XFS_DQ_GROUP, XFS_QMOPT_DQALLOC | XFS_QMOPT_DOWARN, - &gq))) { - if (uq) - xfs_qm_dqrele(uq); + &gq); + if (error) { ASSERT(error != ENOENT); - return error; + goto error_rele; } xfs_dqunlock(gq); lockflags = XFS_ILOCK_SHARED; @@ -1741,15 +1741,14 @@ xfs_qm_vop_dqalloc( } else if ((flags & XFS_QMOPT_PQUOTA) && XFS_IS_PQUOTA_ON(mp)) { if (xfs_get_projid(ip) != prid) { xfs_iunlock(ip, lockflags); - if ((error = xfs_qm_dqget(mp, NULL, (xfs_dqid_t)prid, + error = xfs_qm_dqget(mp, NULL, (xfs_dqid_t)prid, XFS_DQ_PROJ, XFS_QMOPT_DQALLOC | XFS_QMOPT_DOWARN, - &gq))) { - if (uq) - xfs_qm_dqrele(uq); + &gq); + if (error) { ASSERT(error != ENOENT); - return (error); + goto error_rele; } xfs_dqunlock(gq); lockflags = XFS_ILOCK_SHARED; @@ -1772,6 +1771,11 @@ xfs_qm_vop_dqalloc( else if (gq) xfs_qm_dqrele(gq); return 0; + +error_rele: + if (uq) + xfs_qm_dqrele(uq); + return error; } /* @@ -1819,29 +1823,31 @@ xfs_qm_vop_chown( */ int xfs_qm_vop_chown_reserve( - xfs_trans_t *tp, - xfs_inode_t *ip, - xfs_dquot_t *udqp, - xfs_dquot_t *gdqp, - uint flags) + struct xfs_trans *tp, + struct xfs_inode *ip, + struct xfs_dquot *udqp, + struct xfs_dquot *gdqp, + uint flags) { - xfs_mount_t *mp = ip->i_mount; - uint delblks, blkflags, prjflags = 0; - xfs_dquot_t *unresudq, *unresgdq, *delblksudq, *delblksgdq; - int error; + struct xfs_mount *mp = ip->i_mount; + uint delblks, blkflags, prjflags = 0; + struct xfs_dquot *udq_unres = NULL; + struct xfs_dquot *gdq_unres = NULL; + struct xfs_dquot *udq_delblks = NULL; + struct xfs_dquot *gdq_delblks = NULL; + int error; ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL|XFS_ILOCK_SHARED)); ASSERT(XFS_IS_QUOTA_RUNNING(mp)); delblks = ip->i_delayed_blks; - delblksudq = delblksgdq = unresudq = unresgdq = NULL; blkflags = XFS_IS_REALTIME_INODE(ip) ? XFS_QMOPT_RES_RTBLKS : XFS_QMOPT_RES_REGBLKS; if (XFS_IS_UQUOTA_ON(mp) && udqp && ip->i_d.di_uid != (uid_t)be32_to_cpu(udqp->q_core.d_id)) { - delblksudq = udqp; + udq_delblks = udqp; /* * If there are delayed allocation blocks, then we have to * unreserve those from the old dquot, and add them to the @@ -1849,7 +1855,7 @@ xfs_qm_vop_chown_reserve( */ if (delblks) { ASSERT(ip->i_udquot); - unresudq = ip->i_udquot; + udq_unres = ip->i_udquot; } } if (XFS_IS_OQUOTA_ON(ip->i_mount) && gdqp) { @@ -1860,18 +1866,19 @@ xfs_qm_vop_chown_reserve( if (prjflags || (XFS_IS_GQUOTA_ON(ip->i_mount) && ip->i_d.di_gid != be32_to_cpu(gdqp->q_core.d_id))) { - delblksgdq = gdqp; + gdq_delblks = gdqp; if (delblks) { ASSERT(ip->i_gdquot); - unresgdq = ip->i_gdquot; + gdq_unres = ip->i_gdquot; } } } - if ((error = xfs_trans_reserve_quota_bydquots(tp, ip->i_mount, - delblksudq, delblksgdq, ip->i_d.di_nblocks, 1, - flags | blkflags | prjflags))) - return (error); + error = xfs_trans_reserve_quota_bydquots(tp, ip->i_mount, + udq_delblks, gdq_delblks, ip->i_d.di_nblocks, 1, + flags | blkflags | prjflags); + if (error) + return error; /* * Do the delayed blks reservations/unreservations now. Since, these @@ -1883,14 +1890,15 @@ xfs_qm_vop_chown_reserve( /* * Do the reservations first. Unreservation can't fail. */ - ASSERT(delblksudq || delblksgdq); - ASSERT(unresudq || unresgdq); - if ((error = xfs_trans_reserve_quota_bydquots(NULL, ip->i_mount, - delblksudq, delblksgdq, (xfs_qcnt_t)delblks, 0, - flags | blkflags | prjflags))) - return (error); + ASSERT(udq_delblks || gdq_delblks); + ASSERT(udq_unres || gdq_unres); + error = xfs_trans_reserve_quota_bydquots(NULL, ip->i_mount, + udq_delblks, gdq_delblks, (xfs_qcnt_t)delblks, 0, + flags | blkflags | prjflags); + if (error) + return error; xfs_trans_reserve_quota_bydquots(NULL, ip->i_mount, - unresudq, unresgdq, -((xfs_qcnt_t)delblks), 0, + udq_unres, gdq_unres, -((xfs_qcnt_t)delblks), 0, blkflags); } diff --git a/fs/xfs/xfs_qm.h b/fs/xfs/xfs_qm.h index 051e43a5e20b..978bbb1a9e77 100644 --- a/fs/xfs/xfs_qm.h +++ b/fs/xfs/xfs_qm.h @@ -103,11 +103,13 @@ xfs_dq_to_quota_inode(struct xfs_dquot *dqp) extern int xfs_qm_calc_dquots_per_chunk(struct xfs_mount *mp, unsigned int nbblks); -extern void xfs_trans_mod_dquot(xfs_trans_t *, xfs_dquot_t *, uint, long); -extern int xfs_trans_reserve_quota_bydquots(xfs_trans_t *, xfs_mount_t *, - xfs_dquot_t *, xfs_dquot_t *, long, long, uint); -extern void xfs_trans_dqjoin(xfs_trans_t *, xfs_dquot_t *); -extern void xfs_trans_log_dquot(xfs_trans_t *, xfs_dquot_t *); +extern void xfs_trans_mod_dquot(struct xfs_trans *, + struct xfs_dquot *, uint, long); +extern int xfs_trans_reserve_quota_bydquots(struct xfs_trans *, + struct xfs_mount *, struct xfs_dquot *, + struct xfs_dquot *, long, long, uint); +extern void xfs_trans_dqjoin(struct xfs_trans *, struct xfs_dquot *); +extern void xfs_trans_log_dquot(struct xfs_trans *, struct xfs_dquot *); /* * We keep the usr and grp dquots separately so that locking will be easier @@ -132,22 +134,23 @@ typedef struct xfs_dquot_acct { #define XFS_QM_IWARNLIMIT 5 #define XFS_QM_RTBWARNLIMIT 5 -extern void xfs_qm_destroy_quotainfo(xfs_mount_t *); -extern int xfs_qm_quotacheck(xfs_mount_t *); -extern int xfs_qm_write_sb_changes(xfs_mount_t *, __int64_t); +extern void xfs_qm_destroy_quotainfo(struct xfs_mount *); +extern int xfs_qm_quotacheck(struct xfs_mount *); +extern int xfs_qm_write_sb_changes(struct xfs_mount *, __int64_t); /* dquot stuff */ -extern void xfs_qm_dqpurge_all(xfs_mount_t *, uint); -extern void xfs_qm_dqrele_all_inodes(xfs_mount_t *, uint); +extern void xfs_qm_dqpurge_all(struct xfs_mount *, uint); +extern void xfs_qm_dqrele_all_inodes(struct xfs_mount *, uint); /* quota ops */ -extern int xfs_qm_scall_trunc_qfiles(xfs_mount_t *, uint); -extern int xfs_qm_scall_getquota(xfs_mount_t *, xfs_dqid_t, uint, - fs_disk_quota_t *); +extern int xfs_qm_scall_trunc_qfiles(struct xfs_mount *, uint); +extern int xfs_qm_scall_getquota(struct xfs_mount *, xfs_dqid_t, + uint, struct fs_disk_quota *); extern int xfs_qm_scall_setqlim(struct xfs_mount *, xfs_dqid_t, uint, - fs_disk_quota_t *); -extern int xfs_qm_scall_getqstat(xfs_mount_t *, fs_quota_stat_t *); -extern int xfs_qm_scall_quotaon(xfs_mount_t *, uint); -extern int xfs_qm_scall_quotaoff(xfs_mount_t *, uint); + struct fs_disk_quota *); +extern int xfs_qm_scall_getqstat(struct xfs_mount *, + struct fs_quota_stat *); +extern int xfs_qm_scall_quotaon(struct xfs_mount *, uint); +extern int xfs_qm_scall_quotaoff(struct xfs_mount *, uint); #endif /* __XFS_QM_H__ */ diff --git a/fs/xfs/xfs_qm_syscalls.c b/fs/xfs/xfs_qm_syscalls.c index 6cdf6ffc36a1..b03b2abd1e13 100644 --- a/fs/xfs/xfs_qm_syscalls.c +++ b/fs/xfs/xfs_qm_syscalls.c @@ -407,11 +407,11 @@ xfs_qm_scall_getqstat( struct fs_quota_stat *out) { struct xfs_quotainfo *q = mp->m_quotainfo; - struct xfs_inode *uip, *gip; - bool tempuqip, tempgqip; + struct xfs_inode *uip = NULL; + struct xfs_inode *gip = NULL; + bool tempuqip = false; + bool tempgqip = false; - uip = gip = NULL; - tempuqip = tempgqip = false; memset(out, 0, sizeof(fs_quota_stat_t)); out->qs_version = FS_QSTAT_VERSION; diff --git a/fs/xfs/xfs_symlink.c b/fs/xfs/xfs_symlink.c index 738c04be2019..e830fb56e27f 100644 --- a/fs/xfs/xfs_symlink.c +++ b/fs/xfs/xfs_symlink.c @@ -358,7 +358,8 @@ xfs_symlink( int n; xfs_buf_t *bp; prid_t prid; - struct xfs_dquot *udqp, *gdqp; + struct xfs_dquot *udqp = NULL; + struct xfs_dquot *gdqp = NULL; uint resblks; *ipp = NULL; diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c index 3fa369c75a4d..45b3e2d0773a 100644 --- a/fs/xfs/xfs_trans_dquot.c +++ b/fs/xfs/xfs_trans_dquot.c @@ -169,13 +169,13 @@ xfs_trans_mod_dquot_byino( (void) xfs_trans_mod_dquot(tp, ip->i_gdquot, field, delta); } -STATIC xfs_dqtrx_t * +STATIC struct xfs_dqtrx * xfs_trans_get_dqtrx( - xfs_trans_t *tp, - xfs_dquot_t *dqp) + struct xfs_trans *tp, + struct xfs_dquot *dqp) { - int i; - xfs_dqtrx_t *qa; + int i; + struct xfs_dqtrx *qa; qa = XFS_QM_ISUDQ(dqp) ? tp->t_dqinfo->dqa_usrdquots : tp->t_dqinfo->dqa_grpdquots; @@ -747,15 +747,15 @@ error_return: */ int xfs_trans_reserve_quota_bydquots( - xfs_trans_t *tp, - xfs_mount_t *mp, - xfs_dquot_t *udqp, - xfs_dquot_t *gdqp, - long nblks, - long ninos, - uint flags) + struct xfs_trans *tp, + struct xfs_mount *mp, + struct xfs_dquot *udqp, + struct xfs_dquot *gdqp, + long nblks, + long ninos, + uint flags) { - int resvd = 0, error; + int error; if (!XFS_IS_QUOTA_RUNNING(mp) || !XFS_IS_QUOTA_ON(mp)) return 0; @@ -770,28 +770,24 @@ xfs_trans_reserve_quota_bydquots( (flags & ~XFS_QMOPT_ENOSPC)); if (error) return error; - resvd = 1; } if (gdqp) { error = xfs_trans_dqresv(tp, mp, gdqp, nblks, ninos, flags); - if (error) { - /* - * can't do it, so backout previous reservation - */ - if (resvd) { - flags |= XFS_QMOPT_FORCE_RES; - xfs_trans_dqresv(tp, mp, udqp, - -nblks, -ninos, flags); - } - return error; - } + if (error) + goto unwind_usr; } /* * Didn't change anything critical, so, no need to log */ return 0; + +unwind_usr: + flags |= XFS_QMOPT_FORCE_RES; + if (udqp) + xfs_trans_dqresv(tp, mp, udqp, -nblks, -ninos, flags); + return error; } -- cgit v1.2.3 From 0e6436d99e730f9384f3c11d24a5efb788885fc6 Mon Sep 17 00:00:00 2001 From: Chandra Seetharaman Date: Thu, 27 Jun 2013 17:25:09 -0500 Subject: xfs: Change xfs_dquot_acct to be a 2-dimensional array In preparation for combined pquota/gquota support, for the sake of readability, change xfs_dquot_acct to be a 2-dimensional array. Signed-off-by: Chandra Seetharaman Reviewed-by: Ben Myers Signed-off-by: Ben Myers --- fs/xfs/xfs_qm.h | 12 ++++++++---- fs/xfs/xfs_trans_dquot.c | 31 ++++++++++++------------------- 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/fs/xfs/xfs_qm.h b/fs/xfs/xfs_qm.h index 978bbb1a9e77..bdb4f8b95207 100644 --- a/fs/xfs/xfs_qm.h +++ b/fs/xfs/xfs_qm.h @@ -116,11 +116,15 @@ extern void xfs_trans_log_dquot(struct xfs_trans *, struct xfs_dquot *); * to do at commit time. All transactions that we know of at this point * affect no more than two dquots of one type. Hence, the TRANS_MAXDQS value. */ +enum { + XFS_QM_TRANS_USR = 0, + XFS_QM_TRANS_GRP, + XFS_QM_TRANS_DQTYPES +}; #define XFS_QM_TRANS_MAXDQS 2 -typedef struct xfs_dquot_acct { - xfs_dqtrx_t dqa_usrdquots[XFS_QM_TRANS_MAXDQS]; - xfs_dqtrx_t dqa_grpdquots[XFS_QM_TRANS_MAXDQS]; -} xfs_dquot_acct_t; +struct xfs_dquot_acct { + struct xfs_dqtrx dqs[XFS_QM_TRANS_DQTYPES][XFS_QM_TRANS_MAXDQS]; +}; /* * Users are allowed to have a usage exceeding their softlimit for diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c index 45b3e2d0773a..7ea7485f9fb8 100644 --- a/fs/xfs/xfs_trans_dquot.c +++ b/fs/xfs/xfs_trans_dquot.c @@ -103,8 +103,6 @@ xfs_trans_dup_dqinfo( return; xfs_trans_alloc_dqinfo(ntp); - oqa = otp->t_dqinfo->dqa_usrdquots; - nqa = ntp->t_dqinfo->dqa_usrdquots; /* * Because the quota blk reservation is carried forward, @@ -113,7 +111,9 @@ xfs_trans_dup_dqinfo( if(otp->t_flags & XFS_TRANS_DQ_DIRTY) ntp->t_flags |= XFS_TRANS_DQ_DIRTY; - for (j = 0; j < 2; j++) { + for (j = 0; j < XFS_QM_TRANS_DQTYPES; j++) { + oqa = otp->t_dqinfo->dqs[j]; + nqa = ntp->t_dqinfo->dqs[j]; for (i = 0; i < XFS_QM_TRANS_MAXDQS; i++) { if (oqa[i].qt_dquot == NULL) break; @@ -138,8 +138,6 @@ xfs_trans_dup_dqinfo( oq->qt_ino_res = oq->qt_ino_res_used; } - oqa = otp->t_dqinfo->dqa_grpdquots; - nqa = ntp->t_dqinfo->dqa_grpdquots; } } @@ -177,8 +175,10 @@ xfs_trans_get_dqtrx( int i; struct xfs_dqtrx *qa; - qa = XFS_QM_ISUDQ(dqp) ? - tp->t_dqinfo->dqa_usrdquots : tp->t_dqinfo->dqa_grpdquots; + if (XFS_QM_ISUDQ(dqp)) + qa = tp->t_dqinfo->dqs[XFS_QM_TRANS_USR]; + else + qa = tp->t_dqinfo->dqs[XFS_QM_TRANS_GRP]; for (i = 0; i < XFS_QM_TRANS_MAXDQS; i++) { if (qa[i].qt_dquot == NULL || @@ -338,12 +338,10 @@ xfs_trans_apply_dquot_deltas( return; ASSERT(tp->t_dqinfo); - qa = tp->t_dqinfo->dqa_usrdquots; - for (j = 0; j < 2; j++) { - if (qa[0].qt_dquot == NULL) { - qa = tp->t_dqinfo->dqa_grpdquots; + for (j = 0; j < XFS_QM_TRANS_DQTYPES; j++) { + qa = tp->t_dqinfo->dqs[j]; + if (qa[0].qt_dquot == NULL) continue; - } /* * Lock all of the dquots and join them to the transaction. @@ -494,10 +492,6 @@ xfs_trans_apply_dquot_deltas( ASSERT(dqp->q_res_rtbcount >= be64_to_cpu(dqp->q_core.d_rtbcount)); } - /* - * Do the group quotas next - */ - qa = tp->t_dqinfo->dqa_grpdquots; } } @@ -520,9 +514,9 @@ xfs_trans_unreserve_and_mod_dquots( if (!tp->t_dqinfo || !(tp->t_flags & XFS_TRANS_DQ_DIRTY)) return; - qa = tp->t_dqinfo->dqa_usrdquots; + for (j = 0; j < XFS_QM_TRANS_DQTYPES; j++) { + qa = tp->t_dqinfo->dqs[j]; - for (j = 0; j < 2; j++) { for (i = 0; i < XFS_QM_TRANS_MAXDQS; i++) { qtrx = &qa[i]; /* @@ -564,7 +558,6 @@ xfs_trans_unreserve_and_mod_dquots( xfs_dqunlock(dqp); } - qa = tp->t_dqinfo->dqa_grpdquots; } } -- cgit v1.2.3 From ffa57b9e53cf7ace3e63e5fa9f91ffb31e0aacf6 Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Wed, 26 Jun 2013 14:09:46 -0400 Subject: NFS: Improve legacy idmapping fallback Fallback should happen only when the request_key() call fails, because this indicates that there was a problem running the nfsidmap program. We shouldn't call the legacy code if the error was elsewhere. Signed-off-by: Bryan Schumaker Signed-off-by: Trond Myklebust --- fs/nfs/idmap.c | 56 ++++++++++++++++++++++++++------------------------------ 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c index c516da5873fd..c2c4163d5683 100644 --- a/fs/nfs/idmap.c +++ b/fs/nfs/idmap.c @@ -262,29 +262,42 @@ static ssize_t nfs_idmap_get_desc(const char *name, size_t namelen, return desclen; } -static ssize_t nfs_idmap_request_key(struct key_type *key_type, - const char *name, size_t namelen, - const char *type, void *data, - size_t data_size, struct idmap *idmap) +static struct key *nfs_idmap_request_key(const char *name, size_t namelen, + const char *type, struct idmap *idmap) { - const struct cred *saved_cred; - struct key *rkey; char *desc; - struct user_key_payload *payload; + struct key *rkey; ssize_t ret; ret = nfs_idmap_get_desc(name, namelen, type, strlen(type), &desc); if (ret <= 0) - goto out; + return ERR_PTR(ret); + + rkey = request_key(&key_type_id_resolver, desc, ""); + if (IS_ERR(rkey)) { + mutex_lock(&idmap->idmap_mutex); + rkey = request_key_with_auxdata(&key_type_id_resolver_legacy, + desc, "", 0, idmap); + mutex_unlock(&idmap->idmap_mutex); + } + + kfree(desc); + return rkey; +} + +static ssize_t nfs_idmap_get_key(const char *name, size_t namelen, + const char *type, void *data, + size_t data_size, struct idmap *idmap) +{ + const struct cred *saved_cred; + struct key *rkey; + struct user_key_payload *payload; + ssize_t ret; saved_cred = override_creds(id_resolver_cache); - if (idmap) - rkey = request_key_with_auxdata(key_type, desc, "", 0, idmap); - else - rkey = request_key(&key_type_id_resolver, desc, ""); + rkey = nfs_idmap_request_key(name, namelen, type, idmap); revert_creds(saved_cred); - kfree(desc); if (IS_ERR(rkey)) { ret = PTR_ERR(rkey); goto out; @@ -316,23 +329,6 @@ out: return ret; } -static ssize_t nfs_idmap_get_key(const char *name, size_t namelen, - const char *type, void *data, - size_t data_size, struct idmap *idmap) -{ - ssize_t ret = nfs_idmap_request_key(&key_type_id_resolver, - name, namelen, type, data, - data_size, NULL); - if (ret < 0) { - mutex_lock(&idmap->idmap_mutex); - ret = nfs_idmap_request_key(&key_type_id_resolver_legacy, - name, namelen, type, data, - data_size, idmap); - mutex_unlock(&idmap->idmap_mutex); - } - return ret; -} - /* ID -> Name */ static ssize_t nfs_idmap_lookup_name(__u32 id, const char *type, char *buf, size_t buflen, struct idmap *idmap) -- cgit v1.2.3 From a6b3f7614ca690e49e934c291f707b0c19312194 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Fri, 28 Jun 2013 21:32:27 +0200 Subject: block: Reserve only one queue tag for sync IO if only 3 tags are available In case a device has three tags available we still reserve two of them for sync IO. That leaves only a single tag for async IO such as writeback from flusher thread which results in poor performance. Allow async IO to consume two tags in case queue has three tag availabe to get a decent async write performance. This patch improves streaming write performance on a machine with such disk from ~21 MB/s to ~52 MB/s. Also postmark throughput in presence of streaming writer improves from 8 to 12 transactions per second so sync IO doesn't seem to be harmed in presence of heavy async writer. Signed-off-by: Jan Kara Signed-off-by: Jens Axboe --- block/blk-tag.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/block/blk-tag.c b/block/blk-tag.c index cc345e1d8d4e..3f33d8672268 100644 --- a/block/blk-tag.c +++ b/block/blk-tag.c @@ -348,9 +348,16 @@ int blk_queue_start_tag(struct request_queue *q, struct request *rq) */ max_depth = bqt->max_depth; if (!rq_is_sync(rq) && max_depth > 1) { - max_depth -= 2; - if (!max_depth) + switch (max_depth) { + case 2: max_depth = 1; + break; + case 3: + max_depth = 2; + break; + default: + max_depth -= 2; + } if (q->in_flight[BLK_RW_ASYNC] > max_depth) return 1; } -- cgit v1.2.3 From f1c097be2b35dcfc06c1c591194d78058dd67284 Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Tue, 25 Jun 2013 19:02:53 -0400 Subject: NFSv4.1 Fix gdia_maxcount calculation to fit in ca_maxresponsesize The GETDEVICEINFO gdia_maxcount represents all of the data being returned within the GETDEVICEINFO4resok structure and includes the XDR overhead. The CREATE_SESSION ca_maxresponsesize is the maximum reply and includes the RPC headers (including security flavor credentials and verifiers). Split out the struct pnfs_device field maxcount which is the gdia_maxcount from the pglen field which is the reply (the total) buffer length. Signed-off-by: Andy Adamson Signed-off-by: Trond Myklebust --- fs/nfs/internal.h | 1 + fs/nfs/nfs4filelayoutdev.c | 1 + fs/nfs/nfs4xdr.c | 8 +++++++- fs/nfs/pnfs.h | 3 ++- 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 91e59a39fc08..4bd53f4e0fd2 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -255,6 +255,7 @@ extern int nfs4_decode_dirent(struct xdr_stream *, #ifdef CONFIG_NFS_V4_1 extern const u32 nfs41_maxread_overhead; extern const u32 nfs41_maxwrite_overhead; +extern const u32 nfs41_maxgetdevinfo_overhead; #endif /* nfs4proc.c */ diff --git a/fs/nfs/nfs4filelayoutdev.c b/fs/nfs/nfs4filelayoutdev.c index 0493dbd129c5..95604f64cab8 100644 --- a/fs/nfs/nfs4filelayoutdev.c +++ b/fs/nfs/nfs4filelayoutdev.c @@ -711,6 +711,7 @@ filelayout_get_device_info(struct inode *inode, pdev->pgbase = 0; pdev->pglen = max_resp_sz; pdev->mincount = 0; + pdev->maxcount = max_resp_sz - nfs41_maxgetdevinfo_overhead; rc = nfs4_proc_getdeviceinfo(server, pdev, cred); dprintk("%s getdevice info returns %d\n", __func__, rc); diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 4be8d135ed61..27cc76d88f9a 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -853,6 +853,12 @@ const u32 nfs41_maxread_overhead = ((RPC_MAX_HEADER_WITH_AUTH + decode_sequence_maxsz + decode_putfh_maxsz) * XDR_UNIT); + +const u32 nfs41_maxgetdevinfo_overhead = ((RPC_MAX_REPHEADER_WITH_AUTH + + compound_decode_hdr_maxsz + + decode_sequence_maxsz) * + XDR_UNIT); +EXPORT_SYMBOL_GPL(nfs41_maxgetdevinfo_overhead); #endif /* CONFIG_NFS_V4_1 */ static const umode_t nfs_type2fmt[] = { @@ -1889,7 +1895,7 @@ encode_getdeviceinfo(struct xdr_stream *xdr, p = xdr_encode_opaque_fixed(p, args->pdev->dev_id.data, NFS4_DEVICEID4_SIZE); *p++ = cpu_to_be32(args->pdev->layout_type); - *p++ = cpu_to_be32(args->pdev->pglen); /* gdia_maxcount */ + *p++ = cpu_to_be32(args->pdev->maxcount); /* gdia_maxcount */ *p++ = cpu_to_be32(0); /* bitmap length 0 */ } diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h index 1441dff9b0e7..a4f41810a7f4 100644 --- a/fs/nfs/pnfs.h +++ b/fs/nfs/pnfs.h @@ -149,9 +149,10 @@ struct pnfs_device { struct nfs4_deviceid dev_id; unsigned int layout_type; unsigned int mincount; + unsigned int maxcount; /* gdia_maxcount */ struct page **pages; unsigned int pgbase; - unsigned int pglen; + unsigned int pglen; /* reply buffer length */ }; #define NFS4_PNFS_GETDEVLIST_MAXNUM 16 -- cgit v1.2.3 From 968fe252437e5896e28271b7e7200e63c5c1d196 Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Tue, 25 Jun 2013 19:02:54 -0400 Subject: NFSv4.1 use pnfs_device maxcount for the blocklayout gdia_maxcount Signed-off-by: Andy Adamson Signed-off-by: Trond Myklebust --- fs/nfs/blocklayout/blocklayout.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/nfs/blocklayout/blocklayout.c b/fs/nfs/blocklayout/blocklayout.c index 1e5fdd3506e2..e242bbf72972 100644 --- a/fs/nfs/blocklayout/blocklayout.c +++ b/fs/nfs/blocklayout/blocklayout.c @@ -1089,6 +1089,7 @@ nfs4_blk_get_deviceinfo(struct nfs_server *server, const struct nfs_fh *fh, dev->pgbase = 0; dev->pglen = PAGE_SIZE * max_pages; dev->mincount = 0; + dev->maxcount = max_resp_sz - nfs41_maxgetdevinfo_overhead; dprintk("%s: dev_id: %s\n", __func__, dev->dev_id.data); rc = nfs4_proc_getdeviceinfo(server, dev, NULL); -- cgit v1.2.3 From 52fcac988ae6d5a902e9c1d79fc11ba5ec9361e7 Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Tue, 25 Jun 2013 19:02:55 -0400 Subject: NFSv4.1 use pnfs_device maxcount for the objectlayout gdia_maxcount Signed-off-by: Andy Adamson Signed-off-by: Trond Myklebust --- fs/nfs/objlayout/objlayout.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/nfs/objlayout/objlayout.c b/fs/nfs/objlayout/objlayout.c index 198990852d50..e4f9cbfec67b 100644 --- a/fs/nfs/objlayout/objlayout.c +++ b/fs/nfs/objlayout/objlayout.c @@ -613,6 +613,7 @@ int objlayout_get_deviceinfo(struct pnfs_layout_hdr *pnfslay, pd.pgbase = 0; pd.pglen = PAGE_SIZE; pd.mincount = 0; + pd.maxcount = PAGE_SIZE; err = nfs4_proc_getdeviceinfo(NFS_SERVER(pnfslay->plh_inode), &pd, pnfslay->plh_lc_cred); -- cgit v1.2.3 From 384816051ca9125cd54750e59c780c2a2655fa4f Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Mon, 24 Jun 2013 11:52:38 +0400 Subject: SUNRPC: fix races on PipeFS MOUNT notifications Below are races, when RPC client can be created without PiepFS dentries CPU#0 CPU#1 ----------------------------- ----------------------------- rpc_new_client rpc_fill_super rpc_setup_pipedir mutex_lock(&sn->pipefs_sb_lock) rpc_get_sb_net == NULL (no per-net PipeFS superblock) sn->pipefs_sb = sb; notifier_call_chain(MOUNT) (client is not in the list) rpc_register_client (client without pipes dentries) To fix this patch: 1) makes PipeFS mount notification call with pipefs_sb_lock being held. 2) releases pipefs_sb_lock on new SUNRPC client creation only after registration. Signed-off-by: Stanislav Kinsbursky Cc: stable@vger.kernel.org Signed-off-by: Trond Myklebust --- net/sunrpc/clnt.c | 26 +++++++++++++++----------- net/sunrpc/rpc_pipe.c | 3 +++ 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 5a750b9c3640..b827a4b91975 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -157,20 +157,15 @@ static struct dentry *rpc_setup_pipedir_sb(struct super_block *sb, } static int -rpc_setup_pipedir(struct rpc_clnt *clnt, const char *dir_name) +rpc_setup_pipedir(struct rpc_clnt *clnt, const char *dir_name, + struct super_block *pipefs_sb) { - struct net *net = rpc_net_ns(clnt); - struct super_block *pipefs_sb; struct dentry *dentry; clnt->cl_dentry = NULL; if (dir_name == NULL) return 0; - pipefs_sb = rpc_get_sb_net(net); - if (!pipefs_sb) - return 0; dentry = rpc_setup_pipedir_sb(pipefs_sb, clnt, dir_name); - rpc_put_sb_net(net); if (IS_ERR(dentry)) return PTR_ERR(dentry); clnt->cl_dentry = dentry; @@ -296,6 +291,7 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru struct rpc_clnt *clnt = NULL; struct rpc_auth *auth; int err; + struct super_block *pipefs_sb; /* sanity check the name before trying to print it */ dprintk("RPC: creating %s client for %s (xprt %p)\n", @@ -354,9 +350,12 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru atomic_set(&clnt->cl_count, 1); - err = rpc_setup_pipedir(clnt, program->pipe_dir_name); - if (err < 0) - goto out_no_path; + pipefs_sb = rpc_get_sb_net(rpc_net_ns(clnt)); + if (pipefs_sb) { + err = rpc_setup_pipedir(clnt, program->pipe_dir_name, pipefs_sb); + if (err) + goto out_no_path; + } auth = rpcauth_create(args->authflavor, clnt); if (IS_ERR(auth)) { @@ -369,11 +368,16 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru /* save the nodename */ rpc_clnt_set_nodename(clnt, utsname()->nodename); rpc_register_client(clnt); + if (pipefs_sb) + rpc_put_sb_net(rpc_net_ns(clnt)); return clnt; out_no_auth: - rpc_clnt_remove_pipedir(clnt); + if (pipefs_sb) + __rpc_clnt_remove_pipedir(clnt); out_no_path: + if (pipefs_sb) + rpc_put_sb_net(rpc_net_ns(clnt)); kfree(clnt->cl_principal); out_no_principal: rpc_free_iostats(clnt->cl_metrics); diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index a816b3a69059..e02823bdfe98 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -1127,6 +1127,7 @@ rpc_fill_super(struct super_block *sb, void *data, int silent) return -ENOMEM; dprintk("RPC: sending pipefs MOUNT notification for net %p%s\n", net, NET_NAME(net)); + mutex_lock(&sn->pipefs_sb_lock); sn->pipefs_sb = sb; err = blocking_notifier_call_chain(&rpc_pipefs_notifier_list, RPC_PIPEFS_MOUNT, @@ -1134,6 +1135,7 @@ rpc_fill_super(struct super_block *sb, void *data, int silent) if (err) goto err_depopulate; sb->s_fs_info = get_net(net); + mutex_unlock(&sn->pipefs_sb_lock); return 0; err_depopulate: @@ -1142,6 +1144,7 @@ err_depopulate: sb); sn->pipefs_sb = NULL; __rpc_depopulate(root, files, RPCAUTH_lockd, RPCAUTH_RootEOF); + mutex_unlock(&sn->pipefs_sb_lock); return err; } -- cgit v1.2.3 From adb6fa7ffe9031857ec14b8aab75c9ab65556cbc Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Wed, 26 Jun 2013 10:15:14 +0400 Subject: SUNRPC: fix races on PipeFS UMOUNT notifications CPU#0 CPU#1 ----------------------------- ----------------------------- rpc_kill_sb sn->pipefs_sb = NULL rpc_release_client (UMOUNT_EVENT) rpc_free_auth rpc_pipefs_event rpc_get_client_for_event !atomic_inc_not_zero(cl_count) atomic_inc(cl_count) rpc_free_client rpc_clnt_remove_pipedir To fix this, this patch does the following: 1) Calls RPC_PIPEFS_UMOUNT notification with sn->pipefs_sb_lock being held. 2) Removes SUNRPC client from the list AFTER pipes destroying. 3) Doesn't hold RPC client on notification: if client in the list, then it can't be destroyed while sn->pipefs_sb_lock in hold by notification caller. Signed-off-by: Stanislav Kinsbursky Cc: stable@vger.kernel.org Signed-off-by: Trond Myklebust --- net/sunrpc/clnt.c | 5 +---- net/sunrpc/rpc_pipe.c | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index b827a4b91975..41f180c5a498 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -236,8 +236,6 @@ static struct rpc_clnt *rpc_get_client_for_event(struct net *net, int event) continue; if (rpc_clnt_skip_event(clnt, event)) continue; - if (atomic_inc_not_zero(&clnt->cl_count) == 0) - continue; spin_unlock(&sn->rpc_client_lock); return clnt; } @@ -254,7 +252,6 @@ static int rpc_pipefs_event(struct notifier_block *nb, unsigned long event, while ((clnt = rpc_get_client_for_event(sb->s_fs_info, event))) { error = __rpc_pipefs_event(clnt, event, sb); - rpc_release_client(clnt); if (error) break; } @@ -641,8 +638,8 @@ rpc_free_client(struct rpc_clnt *clnt) rcu_dereference(clnt->cl_xprt)->servername); if (clnt->cl_parent != clnt) rpc_release_client(clnt->cl_parent); - rpc_unregister_client(clnt); rpc_clnt_remove_pipedir(clnt); + rpc_unregister_client(clnt); rpc_free_iostats(clnt->cl_metrics); kfree(clnt->cl_principal); clnt->cl_metrics = NULL; diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index e02823bdfe98..4679df5a6d50 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -1166,12 +1166,12 @@ static void rpc_kill_sb(struct super_block *sb) goto out; } sn->pipefs_sb = NULL; - mutex_unlock(&sn->pipefs_sb_lock); dprintk("RPC: sending pipefs UMOUNT notification for net %p%s\n", net, NET_NAME(net)); blocking_notifier_call_chain(&rpc_pipefs_notifier_list, RPC_PIPEFS_UMOUNT, sb); + mutex_unlock(&sn->pipefs_sb_lock); put_net(net); out: kill_litter_super(sb); -- cgit v1.2.3 From e73f4cc051199799aee4320f300f28ffb82f3eb1 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Mon, 24 Jun 2013 11:52:52 +0400 Subject: SUNRPC: split client creation routine into setup and registration This helper moves all "registration" code to the new rpc_client_register() helper. This helper will be used later in the series to synchronize against PipeFS MOUNT/UMOUNT events. Signed-off-by: Stanislav Kinsbursky Signed-off-by: Trond Myklebust --- net/sunrpc/clnt.c | 64 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 25 deletions(-) diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 41f180c5a498..b4f17117b779 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -281,14 +281,47 @@ static void rpc_clnt_set_nodename(struct rpc_clnt *clnt, const char *nodename) memcpy(clnt->cl_nodename, nodename, clnt->cl_nodelen); } +static int rpc_client_register(const struct rpc_create_args *args, + struct rpc_clnt *clnt) +{ + const struct rpc_program *program = args->program; + struct rpc_auth *auth; + struct net *net = rpc_net_ns(clnt); + struct super_block *pipefs_sb; + int err = 0; + + pipefs_sb = rpc_get_sb_net(net); + if (pipefs_sb) { + err = rpc_setup_pipedir(clnt, program->pipe_dir_name, pipefs_sb); + if (err) + goto out; + } + + auth = rpcauth_create(args->authflavor, clnt); + if (IS_ERR(auth)) { + dprintk("RPC: Couldn't create auth handle (flavor %u)\n", + args->authflavor); + err = PTR_ERR(auth); + goto err_auth; + } + + rpc_register_client(clnt); +out: + if (pipefs_sb) + rpc_put_sb_net(net); + return err; + +err_auth: + __rpc_clnt_remove_pipedir(clnt); + goto out; +} + static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, struct rpc_xprt *xprt) { const struct rpc_program *program = args->program; const struct rpc_version *version; struct rpc_clnt *clnt = NULL; - struct rpc_auth *auth; int err; - struct super_block *pipefs_sb; /* sanity check the name before trying to print it */ dprintk("RPC: creating %s client for %s (xprt %p)\n", @@ -347,34 +380,15 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru atomic_set(&clnt->cl_count, 1); - pipefs_sb = rpc_get_sb_net(rpc_net_ns(clnt)); - if (pipefs_sb) { - err = rpc_setup_pipedir(clnt, program->pipe_dir_name, pipefs_sb); - if (err) - goto out_no_path; - } - - auth = rpcauth_create(args->authflavor, clnt); - if (IS_ERR(auth)) { - dprintk("RPC: Couldn't create auth handle (flavor %u)\n", - args->authflavor); - err = PTR_ERR(auth); - goto out_no_auth; - } - /* save the nodename */ rpc_clnt_set_nodename(clnt, utsname()->nodename); - rpc_register_client(clnt); - if (pipefs_sb) - rpc_put_sb_net(rpc_net_ns(clnt)); + + err = rpc_client_register(args, clnt); + if (err) + goto out_no_path; return clnt; -out_no_auth: - if (pipefs_sb) - __rpc_clnt_remove_pipedir(clnt); out_no_path: - if (pipefs_sb) - rpc_put_sb_net(rpc_net_ns(clnt)); kfree(clnt->cl_principal); out_no_principal: rpc_free_iostats(clnt->cl_metrics); -- cgit v1.2.3 From 4f6bb246f69443549fbbd0f2abaf863243cb35e9 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Mon, 24 Jun 2013 11:52:59 +0400 Subject: SUNRPC: PipeFS MOUNT notification optimization for dying clients Not need to create pipes for dying client. So just skip them. Note: we can safely dereference the client structure, because notification caller is holding sn->pipefs_sb_lock. Signed-off-by: Stanislav Kinsbursky Cc: stable@vger.kernel.org Signed-off-by: Trond Myklebust --- net/sunrpc/clnt.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index b4f17117b779..f0339ae9bf37 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -177,6 +177,8 @@ static inline int rpc_clnt_skip_event(struct rpc_clnt *clnt, unsigned long event if (((event == RPC_PIPEFS_MOUNT) && clnt->cl_dentry) || ((event == RPC_PIPEFS_UMOUNT) && !clnt->cl_dentry)) return 1; + if ((event == RPC_PIPEFS_MOUNT) && atomic_read(&clnt->cl_count) == 0) + return 1; return 0; } -- cgit v1.2.3 From d17540c61bc7b0c7be45d0d2b6244ddbc9ba6aee Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Thu, 27 Jun 2013 15:54:38 -0400 Subject: nfs: refactor "need_mount" code out of nfs_try_mount This looks like pointless refactoring for now, but we'll flesh out the need_mount case a little more in a later patch. Cc: Chuck Lever Signed-off-by: Jeff Layton Signed-off-by: Trond Myklebust --- fs/nfs/super.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 2d7525fbcf25..afeee810f453 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -1759,21 +1759,29 @@ static int nfs_request_mount(struct nfs_parsed_mount_data *args, return nfs_select_flavor(args, &request); } +static struct nfs_server *nfs_try_mount_request(struct nfs_mount_info *mount_info, + struct nfs_subversion *nfs_mod) +{ + int status; + + status = nfs_request_mount(mount_info->parsed, mount_info->mntfh); + if (status) + return ERR_PTR(status); + + return nfs_mod->rpc_ops->create_server(mount_info, nfs_mod); +} + struct dentry *nfs_try_mount(int flags, const char *dev_name, struct nfs_mount_info *mount_info, struct nfs_subversion *nfs_mod) { - int status; struct nfs_server *server; - if (mount_info->parsed->need_mount) { - status = nfs_request_mount(mount_info->parsed, mount_info->mntfh); - if (status) - return ERR_PTR(status); - } + if (mount_info->parsed->need_mount) + server = nfs_try_mount_request(mount_info, nfs_mod); + else + server = nfs_mod->rpc_ops->create_server(mount_info, nfs_mod); - /* Get a volume representation */ - server = nfs_mod->rpc_ops->create_server(mount_info, nfs_mod); if (IS_ERR(server)) return ERR_CAST(server); -- cgit v1.2.3 From 294ae81d4f89c76b7c5bbad7b14a43c02f6d738d Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Thu, 27 Jun 2013 15:54:39 -0400 Subject: nfs: move server_authlist into nfs_try_mount_request In a later patch we're going to want to cycle over this list and attempt to call ->create_server for each different flavor until one succeeds. Move the list allocation to the stack of nfs_try_mount_request() and pass a pointer to it and its length to nfs_request_mount(). Cc: Chuck Lever Signed-off-by: Jeff Layton Signed-off-by: Trond Myklebust --- fs/nfs/super.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/fs/nfs/super.c b/fs/nfs/super.c index afeee810f453..a0949f5c00ba 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -1701,10 +1701,10 @@ out_err: * corresponding to the provided path. */ static int nfs_request_mount(struct nfs_parsed_mount_data *args, - struct nfs_fh *root_fh) + struct nfs_fh *root_fh, + rpc_authflavor_t *server_authlist, + unsigned int *server_authlist_len) { - rpc_authflavor_t server_authlist[NFS_MAX_SECFLAVORS]; - unsigned int server_authlist_len = ARRAY_SIZE(server_authlist); struct nfs_mount_request request = { .sap = (struct sockaddr *) &args->mount_server.address, @@ -1712,7 +1712,7 @@ static int nfs_request_mount(struct nfs_parsed_mount_data *args, .protocol = args->mount_server.protocol, .fh = root_fh, .noresvport = args->flags & NFS_MOUNT_NORESVPORT, - .auth_flav_len = &server_authlist_len, + .auth_flav_len = server_authlist_len, .auth_flavs = server_authlist, .net = args->net, }; @@ -1763,8 +1763,12 @@ static struct nfs_server *nfs_try_mount_request(struct nfs_mount_info *mount_inf struct nfs_subversion *nfs_mod) { int status; + struct nfs_parsed_mount_data *args = mount_info->parsed; + rpc_authflavor_t authlist[NFS_MAX_SECFLAVORS]; + unsigned int authlist_len = ARRAY_SIZE(authlist); - status = nfs_request_mount(mount_info->parsed, mount_info->mntfh); + status = nfs_request_mount(args, mount_info->mntfh, authlist, + &authlist_len); if (status) return ERR_PTR(status); -- cgit v1.2.3 From fb9b02fda06798cfca975c8b7fae3e6188a30572 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Thu, 27 Jun 2013 15:54:40 -0400 Subject: nfs: have nfs_mount fake up a auth_flavs list when the server didn't provide it Instead of handling this as a special case in the auth-selection code, we can simply fake up an auth_flavs list when the server doesn't provide it. Signed-off-by: Jeff Layton Signed-off-by: Trond Myklebust --- fs/nfs/mount_clnt.c | 14 +++++++++++++- fs/nfs/super.c | 13 ------------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c index 91a6faf811ac..99a45283b9ee 100644 --- a/fs/nfs/mount_clnt.c +++ b/fs/nfs/mount_clnt.c @@ -139,7 +139,10 @@ struct mnt_fhstatus { * nfs_mount - Obtain an NFS file handle for the given host and path * @info: pointer to mount request arguments * - * Uses default timeout parameters specified by underlying transport. + * Uses default timeout parameters specified by underlying transport. On + * successful return, the auth_flavs list and auth_flav_len will be populated + * with the list from the server or a faked-up list if the server didn't + * provide one. */ int nfs_mount(struct nfs_mount_request *info) { @@ -195,6 +198,15 @@ int nfs_mount(struct nfs_mount_request *info) dprintk("NFS: MNT request succeeded\n"); status = 0; + /* + * If the server didn't provide a flavor list, allow the + * client to try any flavor. + */ + if (info->version != NFS_MNT3_VERSION || *info->auth_flav_len == 0) { + dprintk("NFS: Faking up auth_flavs list\n"); + info->auth_flavs[0] = RPC_AUTH_NULL; + *info->auth_flav_len = 1; + } out: return status; diff --git a/fs/nfs/super.c b/fs/nfs/super.c index a0949f5c00ba..ceb60c78bfc0 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -1619,19 +1619,6 @@ static int nfs_select_flavor(struct nfs_parsed_mount_data *args, unsigned int i, count = *(request->auth_flav_len); rpc_authflavor_t flavor; - /* - * The NFSv2 MNT operation does not return a flavor list. - */ - if (args->mount_server.version != NFS_MNT3_VERSION) - goto out_default; - - /* - * Certain releases of Linux's mountd return an empty - * flavor list in some cases. - */ - if (count == 0) - goto out_default; - /* * If the sec= mount option is used, the specified flavor or AUTH_NULL * must be in the list returned by the server. -- cgit v1.2.3 From 9111c95b077a81573fb27df3ba8255d0a3a9ebdf Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Thu, 27 Jun 2013 15:54:41 -0400 Subject: nfs: have NFSv3 try server-specified auth flavors in turn The current scheme is to try and pick the auth flavor that the server prefers. In some cases though, we may find that we're not actually able to use that auth flavor later. For instance, the server may prefer an AUTH_GSS flavor, but we may not be able to get GSSAPI creds. The current code just gives up at that point. Change it instead to try the ->create_server call using each of the different authflavors in the server's list if one was not specified at mount time. Once we have a successful ->create_server call, return the result. Only give up and return error if all attempts fail. Cc: Chuck Lever Signed-off-by: Jeff Layton Signed-off-by: Trond Myklebust --- fs/nfs/super.c | 126 +++++++++++++++++++++++++++++++-------------------------- 1 file changed, 69 insertions(+), 57 deletions(-) diff --git a/fs/nfs/super.c b/fs/nfs/super.c index ceb60c78bfc0..8d51101771fc 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -1608,16 +1608,13 @@ out_security_failure: } /* - * Select a security flavor for this mount. The selected flavor - * is planted in args->auth_flavors[0]. - * - * Returns 0 on success, -EACCES on failure. + * Ensure that the specified authtype in args->auth_flavors[0] is supported by + * the server. Returns 0 if it's ok, and -EACCES if not. */ -static int nfs_select_flavor(struct nfs_parsed_mount_data *args, - struct nfs_mount_request *request) +static int nfs_verify_authflavor(struct nfs_parsed_mount_data *args, + rpc_authflavor_t *server_authlist, unsigned int count) { - unsigned int i, count = *(request->auth_flav_len); - rpc_authflavor_t flavor; + unsigned int i; /* * If the sec= mount option is used, the specified flavor or AUTH_NULL @@ -1627,60 +1624,19 @@ static int nfs_select_flavor(struct nfs_parsed_mount_data *args, * means that the server will ignore the rpc creds, so any flavor * can be used. */ - if (args->auth_flavors[0] != RPC_AUTH_MAXFLAVOR) { - for (i = 0; i < count; i++) { - if (args->auth_flavors[0] == request->auth_flavs[i] || - request->auth_flavs[i] == RPC_AUTH_NULL) - goto out; - } - dfprintk(MOUNT, "NFS: auth flavor %d not supported by server\n", - args->auth_flavors[0]); - goto out_err; - } - - /* - * RFC 2623, section 2.7 suggests we SHOULD prefer the - * flavor listed first. However, some servers list - * AUTH_NULL first. Avoid ever choosing AUTH_NULL. - */ for (i = 0; i < count; i++) { - struct rpcsec_gss_info info; - - flavor = request->auth_flavs[i]; - switch (flavor) { - case RPC_AUTH_UNIX: - goto out_set; - case RPC_AUTH_NULL: - continue; - default: - if (rpcauth_get_gssinfo(flavor, &info) == 0) - goto out_set; - } + if (args->auth_flavors[0] == server_authlist[i] || + server_authlist[i] == RPC_AUTH_NULL) + goto out; } - /* - * As a last chance, see if the server list contains AUTH_NULL - - * if it does, use the default flavor. - */ - for (i = 0; i < count; i++) { - if (request->auth_flavs[i] == RPC_AUTH_NULL) - goto out_default; - } - - dfprintk(MOUNT, "NFS: no auth flavors in common with server\n"); - goto out_err; + dfprintk(MOUNT, "NFS: auth flavor %u not supported by server\n", + args->auth_flavors[0]); + return -EACCES; -out_default: - /* use default if flavor not already set */ - flavor = (args->auth_flavors[0] == RPC_AUTH_MAXFLAVOR) ? - RPC_AUTH_UNIX : args->auth_flavors[0]; -out_set: - args->auth_flavors[0] = flavor; out: - dfprintk(MOUNT, "NFS: using auth flavor %d\n", args->auth_flavors[0]); + dfprintk(MOUNT, "NFS: using auth flavor %u\n", args->auth_flavors[0]); return 0; -out_err: - return -EACCES; } /* @@ -1743,13 +1699,17 @@ static int nfs_request_mount(struct nfs_parsed_mount_data *args, return status; } - return nfs_select_flavor(args, &request); + return 0; } static struct nfs_server *nfs_try_mount_request(struct nfs_mount_info *mount_info, struct nfs_subversion *nfs_mod) { int status; + unsigned int i; + bool tried_auth_unix = false; + bool auth_null_in_list = false; + struct nfs_server *server = ERR_PTR(-EACCES); struct nfs_parsed_mount_data *args = mount_info->parsed; rpc_authflavor_t authlist[NFS_MAX_SECFLAVORS]; unsigned int authlist_len = ARRAY_SIZE(authlist); @@ -1759,6 +1719,58 @@ static struct nfs_server *nfs_try_mount_request(struct nfs_mount_info *mount_inf if (status) return ERR_PTR(status); + /* + * Was a sec= authflavor specified in the options? First, verify + * whether the server supports it, and then just try to use it if so. + */ + if (args->auth_flavors[0] != RPC_AUTH_MAXFLAVOR) { + status = nfs_verify_authflavor(args, authlist, authlist_len); + dfprintk(MOUNT, "NFS: using auth flavor %u\n", args->auth_flavors[0]); + if (status) + return ERR_PTR(status); + return nfs_mod->rpc_ops->create_server(mount_info, nfs_mod); + } + + /* + * No sec= option was provided. RFC 2623, section 2.7 suggests we + * SHOULD prefer the flavor listed first. However, some servers list + * AUTH_NULL first. Avoid ever choosing AUTH_NULL. + */ + for (i = 0; i < authlist_len; ++i) { + rpc_authflavor_t flavor; + struct rpcsec_gss_info info; + + flavor = authlist[i]; + switch (flavor) { + case RPC_AUTH_UNIX: + tried_auth_unix = true; + break; + case RPC_AUTH_NULL: + auth_null_in_list = true; + continue; + default: + if (rpcauth_get_gssinfo(flavor, &info) != 0) + continue; + /* Fallthrough */ + } + dfprintk(MOUNT, "NFS: attempting to use auth flavor %u\n", flavor); + args->auth_flavors[0] = flavor; + server = nfs_mod->rpc_ops->create_server(mount_info, nfs_mod); + if (!IS_ERR(server)) + return server; + } + + /* + * Nothing we tried so far worked. At this point, give up if we've + * already tried AUTH_UNIX or if the server's list doesn't contain + * AUTH_NULL + */ + if (tried_auth_unix || !auth_null_in_list) + return server; + + /* Last chance! Try AUTH_UNIX */ + dfprintk(MOUNT, "NFS: attempting to use auth flavor %u\n", RPC_AUTH_UNIX); + args->auth_flavors[0] = RPC_AUTH_UNIX; return nfs_mod->rpc_ops->create_server(mount_info, nfs_mod); } -- cgit v1.2.3 From 18aad3d552c73adf2652a34baf0fe766058018e4 Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 26 Jun 2013 12:21:49 -0400 Subject: NFSv4.1 Refactor nfs4_init_session and nfs4_init_channel_attrs nfs4_init_session was originally written to be called prior to nfs4_init_channel_attrs, setting the session target_max response and request sizes that nfs4_init_channel_attrs would pay attention to. In the current code flow, nfs4_init_session, just like nfs4_init_ds_session for the data server case, is called after the session is all negotiated, and is actually used in a RECLAIM COMPLETE call to the server. Remove the un-needed fc_target_max response and request fields from nfs4_session and just set the max_resp_sz and max_rqst_sz in nfs4_init_channel_attrs. Signed-off-by: Andy Adamson Signed-off-by: Trond Myklebust --- fs/nfs/nfs4client.c | 2 +- fs/nfs/nfs4proc.c | 17 +++++++---------- fs/nfs/nfs4session.c | 40 ++-------------------------------------- fs/nfs/nfs4session.h | 7 ++----- 4 files changed, 12 insertions(+), 54 deletions(-) diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c index 4cbad5d6b276..daecaa94d7ad 100644 --- a/fs/nfs/nfs4client.c +++ b/fs/nfs/nfs4client.c @@ -730,7 +730,7 @@ static int nfs4_server_common_setup(struct nfs_server *server, return -ENOMEM; /* We must ensure the session is initialised first */ - error = nfs4_init_session(server); + error = nfs4_init_session(server->nfs_client); if (error < 0) goto out; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 83e0e1de7ecf..6d46f966d169 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -5792,17 +5792,14 @@ int nfs4_proc_get_lease_time(struct nfs_client *clp, struct nfs_fsinfo *fsinfo) */ static void nfs4_init_channel_attrs(struct nfs41_create_session_args *args) { - struct nfs4_session *session = args->client->cl_session; - unsigned int mxrqst_sz = session->fc_target_max_rqst_sz, - mxresp_sz = session->fc_target_max_resp_sz; - - if (mxrqst_sz == 0) - mxrqst_sz = NFS_MAX_FILE_IO_SIZE; - if (mxresp_sz == 0) - mxresp_sz = NFS_MAX_FILE_IO_SIZE; + unsigned int max_rqst_sz, max_resp_sz; + + max_rqst_sz = NFS_MAX_FILE_IO_SIZE + nfs41_maxwrite_overhead; + max_resp_sz = NFS_MAX_FILE_IO_SIZE + nfs41_maxread_overhead; + /* Fore channel attributes */ - args->fc_attrs.max_rqst_sz = mxrqst_sz; - args->fc_attrs.max_resp_sz = mxresp_sz; + args->fc_attrs.max_rqst_sz = max_rqst_sz; + args->fc_attrs.max_resp_sz = max_resp_sz; args->fc_attrs.max_ops = NFS4_MAX_OPS; args->fc_attrs.max_reqs = max_session_slots; diff --git a/fs/nfs/nfs4session.c b/fs/nfs/nfs4session.c index c4e225e4a9af..36e21cb29d65 100644 --- a/fs/nfs/nfs4session.c +++ b/fs/nfs/nfs4session.c @@ -478,48 +478,12 @@ static int nfs41_check_session_ready(struct nfs_client *clp) return 0; } -int nfs4_init_session(struct nfs_server *server) +int nfs4_init_session(struct nfs_client *clp) { - struct nfs_client *clp = server->nfs_client; - struct nfs4_session *session; - unsigned int target_max_rqst_sz = NFS_MAX_FILE_IO_SIZE; - unsigned int target_max_resp_sz = NFS_MAX_FILE_IO_SIZE; - if (!nfs4_has_session(clp)) return 0; - if (server->rsize != 0) - target_max_resp_sz = server->rsize; - target_max_resp_sz += nfs41_maxread_overhead; - - if (server->wsize != 0) - target_max_rqst_sz = server->wsize; - target_max_rqst_sz += nfs41_maxwrite_overhead; - - session = clp->cl_session; - spin_lock(&clp->cl_lock); - if (test_and_clear_bit(NFS4_SESSION_INITING, &session->session_state)) { - /* Initialise targets and channel attributes */ - session->fc_target_max_rqst_sz = target_max_rqst_sz; - session->fc_attrs.max_rqst_sz = target_max_rqst_sz; - session->fc_target_max_resp_sz = target_max_resp_sz; - session->fc_attrs.max_resp_sz = target_max_resp_sz; - } else { - /* Just adjust the targets */ - if (target_max_rqst_sz > session->fc_target_max_rqst_sz) { - session->fc_target_max_rqst_sz = target_max_rqst_sz; - set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state); - } - if (target_max_resp_sz > session->fc_target_max_resp_sz) { - session->fc_target_max_resp_sz = target_max_resp_sz; - set_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state); - } - } - spin_unlock(&clp->cl_lock); - - if (test_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state)) - nfs4_schedule_lease_recovery(clp); - + clear_bit(NFS4_SESSION_INITING, &clp->cl_session->session_state); return nfs41_check_session_ready(clp); } diff --git a/fs/nfs/nfs4session.h b/fs/nfs/nfs4session.h index ff7d9f0f8a65..3a153d82b90c 100644 --- a/fs/nfs/nfs4session.h +++ b/fs/nfs/nfs4session.h @@ -66,9 +66,6 @@ struct nfs4_session { struct nfs4_channel_attrs bc_attrs; struct nfs4_slot_table bc_slot_table; struct nfs_client *clp; - /* Create session arguments */ - unsigned int fc_target_max_rqst_sz; - unsigned int fc_target_max_resp_sz; }; enum nfs4_session_state { @@ -89,7 +86,7 @@ extern int nfs4_setup_session_slot_tables(struct nfs4_session *ses); extern struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp); extern void nfs4_destroy_session(struct nfs4_session *session); -extern int nfs4_init_session(struct nfs_server *server); +extern int nfs4_init_session(struct nfs_client *clp); extern int nfs4_init_ds_session(struct nfs_client *, unsigned long); extern void nfs4_slot_tbl_drain_complete(struct nfs4_slot_table *tbl); @@ -122,7 +119,7 @@ static inline int nfs4_has_persistent_session(const struct nfs_client *clp) #else /* defined(CONFIG_NFS_V4_1) */ -static inline int nfs4_init_session(struct nfs_server *server) +static inline int nfs4_init_session(struct nfs_client *clp) { return 0; } -- cgit v1.2.3 From b873a27538dff59e77c15eaf23bdf7e6be7d36e9 Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Wed, 26 Jun 2013 22:39:26 +0530 Subject: [SCSI] ufs: wrap the i/o access operations Simplify operations with hiding mmio_base. Signed-off-by: Seungwon Jeon Tested-by: Maya Erez Signed-off-by: Santosh Y Signed-off-by: James Bottomley --- drivers/scsi/ufs/ufshcd.c | 105 +++++++++++++++++++--------------------------- drivers/scsi/ufs/ufshcd.h | 7 +++- 2 files changed, 50 insertions(+), 62 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index c32a478df81b..871c2f0568ae 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -71,7 +71,7 @@ enum { */ static inline u32 ufshcd_get_ufs_version(struct ufs_hba *hba) { - return readl(hba->mmio_base + REG_UFS_VERSION); + return ufshcd_readl(hba, REG_UFS_VERSION); } /** @@ -130,8 +130,7 @@ static inline int ufshcd_get_tm_free_slot(struct ufs_hba *hba) */ static inline void ufshcd_utrl_clear(struct ufs_hba *hba, u32 pos) { - writel(~(1 << pos), - (hba->mmio_base + REG_UTP_TRANSFER_REQ_LIST_CLEAR)); + ufshcd_writel(hba, ~(1 << pos), REG_UTP_TRANSFER_REQ_LIST_CLEAR); } /** @@ -165,7 +164,7 @@ static inline int ufshcd_get_lists_status(u32 reg) */ static inline int ufshcd_get_uic_cmd_result(struct ufs_hba *hba) { - return readl(hba->mmio_base + REG_UIC_COMMAND_ARG_2) & + return ufshcd_readl(hba, REG_UIC_COMMAND_ARG_2) & MASK_UIC_COMMAND_RESULT; } @@ -243,18 +242,15 @@ ufshcd_config_int_aggr(struct ufs_hba *hba, int option) { switch (option) { case INT_AGGR_RESET: - writel((INT_AGGR_ENABLE | - INT_AGGR_COUNTER_AND_TIMER_RESET), - (hba->mmio_base + - REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL)); + ufshcd_writel(hba, INT_AGGR_ENABLE | + INT_AGGR_COUNTER_AND_TIMER_RESET, + REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL); break; case INT_AGGR_CONFIG: - writel((INT_AGGR_ENABLE | - INT_AGGR_PARAM_WRITE | - INT_AGGR_COUNTER_THRESHOLD_VALUE | - INT_AGGR_TIMEOUT_VALUE), - (hba->mmio_base + - REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL)); + ufshcd_writel(hba, INT_AGGR_ENABLE | INT_AGGR_PARAM_WRITE | + INT_AGGR_COUNTER_THRESHOLD_VALUE | + INT_AGGR_TIMEOUT_VALUE, + REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL); break; } } @@ -267,12 +263,10 @@ ufshcd_config_int_aggr(struct ufs_hba *hba, int option) */ static void ufshcd_enable_run_stop_reg(struct ufs_hba *hba) { - writel(UTP_TASK_REQ_LIST_RUN_STOP_BIT, - (hba->mmio_base + - REG_UTP_TASK_REQ_LIST_RUN_STOP)); - writel(UTP_TRANSFER_REQ_LIST_RUN_STOP_BIT, - (hba->mmio_base + - REG_UTP_TRANSFER_REQ_LIST_RUN_STOP)); + ufshcd_writel(hba, UTP_TASK_REQ_LIST_RUN_STOP_BIT, + REG_UTP_TASK_REQ_LIST_RUN_STOP); + ufshcd_writel(hba, UTP_TRANSFER_REQ_LIST_RUN_STOP_BIT, + REG_UTP_TRANSFER_REQ_LIST_RUN_STOP); } /** @@ -281,7 +275,7 @@ static void ufshcd_enable_run_stop_reg(struct ufs_hba *hba) */ static inline void ufshcd_hba_start(struct ufs_hba *hba) { - writel(CONTROLLER_ENABLE , (hba->mmio_base + REG_CONTROLLER_ENABLE)); + ufshcd_writel(hba, CONTROLLER_ENABLE, REG_CONTROLLER_ENABLE); } /** @@ -292,7 +286,7 @@ static inline void ufshcd_hba_start(struct ufs_hba *hba) */ static inline int ufshcd_is_hba_active(struct ufs_hba *hba) { - return (readl(hba->mmio_base + REG_CONTROLLER_ENABLE) & 0x1) ? 0 : 1; + return (ufshcd_readl(hba, REG_CONTROLLER_ENABLE) & 0x1) ? 0 : 1; } /** @@ -304,8 +298,7 @@ static inline void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag) { __set_bit(task_tag, &hba->outstanding_reqs); - writel((1 << task_tag), - (hba->mmio_base + REG_UTP_TRANSFER_REQ_DOOR_BELL)); + ufshcd_writel(hba, 1 << task_tag, REG_UTP_TRANSFER_REQ_DOOR_BELL); } /** @@ -329,8 +322,7 @@ static inline void ufshcd_copy_sense_data(struct ufshcd_lrb *lrbp) */ static inline void ufshcd_hba_capabilities(struct ufs_hba *hba) { - hba->capabilities = - readl(hba->mmio_base + REG_CONTROLLER_CAPABILITIES); + hba->capabilities = ufshcd_readl(hba, REG_CONTROLLER_CAPABILITIES); /* nutrs and nutmrs are 0 based values */ hba->nutrs = (hba->capabilities & MASK_TRANSFER_REQUESTS_SLOTS) + 1; @@ -347,16 +339,13 @@ static inline void ufshcd_send_uic_command(struct ufs_hba *hba, struct uic_command *uic_cmnd) { /* Write Args */ - writel(uic_cmnd->argument1, - (hba->mmio_base + REG_UIC_COMMAND_ARG_1)); - writel(uic_cmnd->argument2, - (hba->mmio_base + REG_UIC_COMMAND_ARG_2)); - writel(uic_cmnd->argument3, - (hba->mmio_base + REG_UIC_COMMAND_ARG_3)); + ufshcd_writel(hba, uic_cmnd->argument1, REG_UIC_COMMAND_ARG_1); + ufshcd_writel(hba, uic_cmnd->argument2, REG_UIC_COMMAND_ARG_2); + ufshcd_writel(hba, uic_cmnd->argument3, REG_UIC_COMMAND_ARG_3); /* Write UIC Cmd */ - writel((uic_cmnd->command & COMMAND_OPCODE_MASK), - (hba->mmio_base + REG_UIC_COMMAND)); + ufshcd_writel(hba, uic_cmnd->command & COMMAND_OPCODE_MASK, + REG_UIC_COMMAND); } /** @@ -408,16 +397,15 @@ static void ufshcd_int_config(struct ufs_hba *hba, u32 option) { switch (option) { case UFSHCD_INT_ENABLE: - writel(hba->int_enable_mask, - (hba->mmio_base + REG_INTERRUPT_ENABLE)); + ufshcd_writel(hba, hba->int_enable_mask, REG_INTERRUPT_ENABLE); break; case UFSHCD_INT_DISABLE: if (hba->ufs_version == UFSHCI_VERSION_10) - writel(INTERRUPT_DISABLE_MASK_10, - (hba->mmio_base + REG_INTERRUPT_ENABLE)); + ufshcd_writel(hba, INTERRUPT_DISABLE_MASK_10, + REG_INTERRUPT_ENABLE); else - writel(INTERRUPT_DISABLE_MASK_11, - (hba->mmio_base + REG_INTERRUPT_ENABLE)); + ufshcd_writel(hba, INTERRUPT_DISABLE_MASK_11, + REG_INTERRUPT_ENABLE); break; } } @@ -703,7 +691,7 @@ static int ufshcd_dme_link_startup(struct ufs_hba *hba) unsigned long flags; /* check if controller is ready to accept UIC commands */ - if (((readl(hba->mmio_base + REG_CONTROLLER_STATUS)) & + if ((ufshcd_readl(hba, REG_CONTROLLER_STATUS) & UIC_COMMAND_READY) == 0x0) { dev_err(hba->dev, "Controller not ready" @@ -748,7 +736,7 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba) u32 reg; /* check if device present */ - reg = readl((hba->mmio_base + REG_CONTROLLER_STATUS)); + reg = ufshcd_readl(hba, REG_CONTROLLER_STATUS); if (!ufshcd_is_device_present(reg)) { dev_err(hba->dev, "cc: Device not present\n"); err = -ENXIO; @@ -870,14 +858,14 @@ static int ufshcd_initialize_hba(struct ufs_hba *hba) return -EIO; /* Configure UTRL and UTMRL base address registers */ - writel(lower_32_bits(hba->utrdl_dma_addr), - (hba->mmio_base + REG_UTP_TRANSFER_REQ_LIST_BASE_L)); - writel(upper_32_bits(hba->utrdl_dma_addr), - (hba->mmio_base + REG_UTP_TRANSFER_REQ_LIST_BASE_H)); - writel(lower_32_bits(hba->utmrdl_dma_addr), - (hba->mmio_base + REG_UTP_TASK_REQ_LIST_BASE_L)); - writel(upper_32_bits(hba->utmrdl_dma_addr), - (hba->mmio_base + REG_UTP_TASK_REQ_LIST_BASE_H)); + ufshcd_writel(hba, lower_32_bits(hba->utrdl_dma_addr), + REG_UTP_TRANSFER_REQ_LIST_BASE_L); + ufshcd_writel(hba, upper_32_bits(hba->utrdl_dma_addr), + REG_UTP_TRANSFER_REQ_LIST_BASE_H); + ufshcd_writel(hba, lower_32_bits(hba->utmrdl_dma_addr), + REG_UTP_TASK_REQ_LIST_BASE_L); + ufshcd_writel(hba, upper_32_bits(hba->utmrdl_dma_addr), + REG_UTP_TASK_REQ_LIST_BASE_H); /* Initialize unipro link startup procedure */ return ufshcd_dme_link_startup(hba); @@ -1169,8 +1157,7 @@ static void ufshcd_transfer_req_compl(struct ufs_hba *hba) int index; lrb = hba->lrb; - tr_doorbell = - readl(hba->mmio_base + REG_UTP_TRANSFER_REQ_DOOR_BELL); + tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL); completed_reqs = tr_doorbell ^ hba->outstanding_reqs; for (index = 0; index < hba->nutrs; index++) { @@ -1244,9 +1231,7 @@ static void ufshcd_err_handler(struct ufs_hba *hba) goto fatal_eh; if (hba->errors & UIC_ERROR) { - - reg = readl(hba->mmio_base + - REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER); + reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER); if (reg & UIC_DATA_LINK_LAYER_ERROR_PA_INIT) goto fatal_eh; } @@ -1264,7 +1249,7 @@ static void ufshcd_tmc_handler(struct ufs_hba *hba) { u32 tm_doorbell; - tm_doorbell = readl(hba->mmio_base + REG_UTP_TASK_REQ_DOOR_BELL); + tm_doorbell = ufshcd_readl(hba, REG_UTP_TASK_REQ_DOOR_BELL); hba->tm_condition = tm_doorbell ^ hba->outstanding_tasks; wake_up_interruptible(&hba->ufshcd_tm_wait_queue); } @@ -1305,15 +1290,14 @@ static irqreturn_t ufshcd_intr(int irq, void *__hba) struct ufs_hba *hba = __hba; spin_lock(hba->host->host_lock); - intr_status = readl(hba->mmio_base + REG_INTERRUPT_STATUS); + intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS); if (intr_status) { ufshcd_sl_intr(hba, intr_status); /* If UFSHCI 1.0 then clear interrupt status register */ if (hba->ufs_version == UFSHCI_VERSION_10) - writel(intr_status, - (hba->mmio_base + REG_INTERRUPT_STATUS)); + ufshcd_writel(hba, intr_status, REG_INTERRUPT_STATUS); retval = IRQ_HANDLED; } spin_unlock(hba->host->host_lock); @@ -1378,8 +1362,7 @@ ufshcd_issue_tm_cmd(struct ufs_hba *hba, /* send command to the controller */ __set_bit(free_slot, &hba->outstanding_tasks); - writel((1 << free_slot), - (hba->mmio_base + REG_UTP_TASK_REQ_DOOR_BELL)); + ufshcd_writel(hba, 1 << free_slot, REG_UTP_TASK_REQ_DOOR_BELL); spin_unlock_irqrestore(host->host_lock, flags); diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 6b99a42f5819..807dd2d740d4 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -186,6 +186,11 @@ struct ufs_hba { u32 errors; }; +#define ufshcd_writel(hba, val, reg) \ + writel((val), (hba)->mmio_base + (reg)) +#define ufshcd_readl(hba, reg) \ + readl((hba)->mmio_base + (reg)) + int ufshcd_init(struct device *, struct ufs_hba ** , void __iomem * , unsigned int); void ufshcd_remove(struct ufs_hba *); @@ -196,7 +201,7 @@ void ufshcd_remove(struct ufs_hba *); */ static inline void ufshcd_hba_stop(struct ufs_hba *hba) { - writel(CONTROLLER_DISABLE, (hba->mmio_base + REG_CONTROLLER_ENABLE)); + ufshcd_writel(hba, CONTROLLER_DISABLE, REG_CONTROLLER_ENABLE); } #endif /* End of Header */ -- cgit v1.2.3 From 2fbd009b40967fc54b7eb3580372736862291a06 Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Wed, 26 Jun 2013 22:39:27 +0530 Subject: [SCSI] ufs: amend interrupt configuration It makes interrupt setting more flexible especially for disabling. And wrong bit mask is fixed for ver 1.0. [17:16] is added for mask. Signed-off-by: Seungwon Jeon Tested-by: Maya Erez Signed-off-by: Santosh Y Signed-off-by: James Bottomley --- drivers/scsi/ufs/ufshcd.c | 84 +++++++++++++++++++++++++++++++++-------------- drivers/scsi/ufs/ufshcd.h | 4 +-- drivers/scsi/ufs/ufshci.h | 5 +-- 3 files changed, 64 insertions(+), 29 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 871c2f0568ae..1f1e08518555 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -35,6 +35,10 @@ #include "ufshcd.h" +#define UFSHCD_ENABLE_INTRS (UTP_TRANSFER_REQ_COMPL |\ + UTP_TASK_REQ_COMPL |\ + UFSHCD_ERROR_MASK) + enum { UFSHCD_MAX_CHANNEL = 0, UFSHCD_MAX_ID = 1, @@ -63,6 +67,20 @@ enum { INT_AGGR_CONFIG, }; +/** + * ufshcd_get_intr_mask - Get the interrupt bit mask + * @hba - Pointer to adapter instance + * + * Returns interrupt bit mask per version + */ +static inline u32 ufshcd_get_intr_mask(struct ufs_hba *hba) +{ + if (hba->ufs_version == UFSHCI_VERSION_10) + return INTERRUPT_MASK_ALL_VER_10; + else + return INTERRUPT_MASK_ALL_VER_11; +} + /** * ufshcd_get_ufs_version - Get the UFS version supported by the HBA * @hba - Pointer to adapter instance @@ -389,25 +407,45 @@ static int ufshcd_map_sg(struct ufshcd_lrb *lrbp) } /** - * ufshcd_int_config - enable/disable interrupts + * ufshcd_enable_intr - enable interrupts * @hba: per adapter instance - * @option: interrupt option + * @intrs: interrupt bits */ -static void ufshcd_int_config(struct ufs_hba *hba, u32 option) +static void ufshcd_enable_intr(struct ufs_hba *hba, u32 intrs) { - switch (option) { - case UFSHCD_INT_ENABLE: - ufshcd_writel(hba, hba->int_enable_mask, REG_INTERRUPT_ENABLE); - break; - case UFSHCD_INT_DISABLE: - if (hba->ufs_version == UFSHCI_VERSION_10) - ufshcd_writel(hba, INTERRUPT_DISABLE_MASK_10, - REG_INTERRUPT_ENABLE); - else - ufshcd_writel(hba, INTERRUPT_DISABLE_MASK_11, - REG_INTERRUPT_ENABLE); - break; + u32 set = ufshcd_readl(hba, REG_INTERRUPT_ENABLE); + + if (hba->ufs_version == UFSHCI_VERSION_10) { + u32 rw; + rw = set & INTERRUPT_MASK_RW_VER_10; + set = rw | ((set ^ intrs) & intrs); + } else { + set |= intrs; + } + + ufshcd_writel(hba, set, REG_INTERRUPT_ENABLE); +} + +/** + * ufshcd_disable_intr - disable interrupts + * @hba: per adapter instance + * @intrs: interrupt bits + */ +static void ufshcd_disable_intr(struct ufs_hba *hba, u32 intrs) +{ + u32 set = ufshcd_readl(hba, REG_INTERRUPT_ENABLE); + + if (hba->ufs_version == UFSHCI_VERSION_10) { + u32 rw; + rw = (set & INTERRUPT_MASK_RW_VER_10) & + ~(intrs & INTERRUPT_MASK_RW_VER_10); + set = rw | ((set & intrs) & ~INTERRUPT_MASK_RW_VER_10); + + } else { + set &= ~intrs; } + + ufshcd_writel(hba, set, REG_INTERRUPT_ENABLE); } /** @@ -709,8 +747,7 @@ static int ufshcd_dme_link_startup(struct ufs_hba *hba) uic_cmd->argument3 = 0; /* enable UIC related interrupts */ - hba->int_enable_mask |= UIC_COMMAND_COMPL; - ufshcd_int_config(hba, UFSHCD_INT_ENABLE); + ufshcd_enable_intr(hba, UIC_COMMAND_COMPL); /* sending UIC commands to controller */ ufshcd_send_uic_command(hba, uic_cmd); @@ -757,13 +794,7 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba) } /* Enable required interrupts */ - hba->int_enable_mask |= (UTP_TRANSFER_REQ_COMPL | - UIC_ERROR | - UTP_TASK_REQ_COMPL | - DEVICE_FATAL_ERROR | - CONTROLLER_FATAL_ERROR | - SYSTEM_BUS_FATAL_ERROR); - ufshcd_int_config(hba, UFSHCD_INT_ENABLE); + ufshcd_enable_intr(hba, UFSHCD_ENABLE_INTRS); /* Configure interrupt aggregation */ ufshcd_config_int_aggr(hba, INT_AGGR_CONFIG); @@ -1570,7 +1601,7 @@ static void ufshcd_hba_free(struct ufs_hba *hba) void ufshcd_remove(struct ufs_hba *hba) { /* disable interrupts */ - ufshcd_int_config(hba, UFSHCD_INT_DISABLE); + ufshcd_disable_intr(hba, hba->intr_mask); ufshcd_hba_stop(hba); ufshcd_hba_free(hba); @@ -1628,6 +1659,9 @@ int ufshcd_init(struct device *dev, struct ufs_hba **hba_handle, /* Get UFS version supported by the controller */ hba->ufs_version = ufshcd_get_ufs_version(hba); + /* Get Interrupt bit mask per version */ + hba->intr_mask = ufshcd_get_intr_mask(hba); + /* Allocate memory for host memory space */ err = ufshcd_memory_alloc(hba); if (err) { diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 807dd2d740d4..4213600b89d6 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -139,7 +139,7 @@ struct ufshcd_lrb { * @ufshcd_tm_wait_queue: wait queue for task management * @tm_condition: condition variable for task management * @ufshcd_state: UFSHCD states - * @int_enable_mask: Interrupt Mask Bits + * @intr_mask: Interrupt Mask Bits * @uic_workq: Work queue for UIC completion handling * @feh_workq: Work queue for fatal controller error handling * @errors: HBA errors @@ -176,7 +176,7 @@ struct ufs_hba { unsigned long tm_condition; u32 ufshcd_state; - u32 int_enable_mask; + u32 intr_mask; /* Work Queues */ struct work_struct uic_workq; diff --git a/drivers/scsi/ufs/ufshci.h b/drivers/scsi/ufs/ufshci.h index 0c164847a3ef..d5c5f1482d7d 100644 --- a/drivers/scsi/ufs/ufshci.h +++ b/drivers/scsi/ufs/ufshci.h @@ -232,10 +232,11 @@ enum { /* Interrupt disable masks */ enum { /* Interrupt disable mask for UFSHCI v1.0 */ - INTERRUPT_DISABLE_MASK_10 = 0xFFFF, + INTERRUPT_MASK_ALL_VER_10 = 0x30FFF, + INTERRUPT_MASK_RW_VER_10 = 0x30000, /* Interrupt disable mask for UFSHCI v1.1 */ - INTERRUPT_DISABLE_MASK_11 = 0x0, + INTERRUPT_MASK_ALL_VER_11 = 0x31FFF, }; /* -- cgit v1.2.3 From f112bb48994e56868870a080773c392f774fa9a2 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 25 Jun 2013 12:23:27 -0400 Subject: NFS: Set NFS_CS_MIGRATION for NFSv4 mounts NFS_CS_MIGRATION makes sense only for NFSv4 mounts. Introduced by commit 89652617 (NFS: Introduce "migration" mount option) Fri Sep 14 17:24:11 2012. Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 2 -- fs/nfs/nfs4client.c | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/nfs/client.c b/fs/nfs/client.c index c513b0cc835f..dbb65fb9e82d 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -753,8 +753,6 @@ static int nfs_init_server(struct nfs_server *server, data->timeo, data->retrans); if (data->flags & NFS_MOUNT_NORESVPORT) set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags); - if (server->options & NFS_OPTION_MIGRATION) - set_bit(NFS_CS_MIGRATION, &cl_init.init_flags); /* Allocate or find a client reference we can use */ clp = nfs_get_client(&cl_init, &timeparms, NULL, RPC_AUTH_UNIX); diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c index daecaa94d7ad..0054e4bd6b55 100644 --- a/fs/nfs/nfs4client.c +++ b/fs/nfs/nfs4client.c @@ -626,6 +626,8 @@ static int nfs4_set_client(struct nfs_server *server, if (server->flags & NFS_MOUNT_NORESVPORT) set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags); + if (server->options & NFS_OPTION_MIGRATION) + set_bit(NFS_CS_MIGRATION, &cl_init.init_flags); /* Allocate or find a client reference we can use */ clp = nfs_get_client(&cl_init, timeparms, ip_addr, authflavour); -- cgit v1.2.3 From 261ea452037471b7cab6a3913d308acba54f7933 Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Wed, 26 Jun 2013 22:39:28 +0530 Subject: [SCSI] ufs: remove version check before IS reg clear There is no need to check the version to clear the interrupt status. And the order is changed prior to actual handling. Signed-off-by: Seungwon Jeon Tested-by: Maya Erez Signed-off-by: Santosh Y Signed-off-by: James Bottomley --- drivers/scsi/ufs/ufshcd.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 1f1e08518555..2e0248302e8d 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -1324,11 +1324,8 @@ static irqreturn_t ufshcd_intr(int irq, void *__hba) intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS); if (intr_status) { + ufshcd_writel(hba, intr_status, REG_INTERRUPT_STATUS); ufshcd_sl_intr(hba, intr_status); - - /* If UFSHCI 1.0 then clear interrupt status register */ - if (hba->ufs_version == UFSHCI_VERSION_10) - ufshcd_writel(hba, intr_status, REG_INTERRUPT_STATUS); retval = IRQ_HANDLED; } spin_unlock(hba->host->host_lock); -- cgit v1.2.3 From 6ccf44fe4cd7c45a33f571788890a299d8bca448 Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Wed, 26 Jun 2013 22:39:29 +0530 Subject: [SCSI] ufs: rework link start-up process Link start-up requires long time with multiphase handshakes between UFS host and device. This affects driver's probe time. This patch let link start-up run asynchronously. Link start-up will be executed at the end of prove separately. Along with this change, the following is worked. Defined completion time of uic command to avoid a permanent wait. Added mutex to guarantee of uic command at a time. Adapted some sequence of controller initialization after link statup according to HCI standard. Signed-off-by: Seungwon Jeon Signed-off-by: Sujit Reddy Thumma Tested-by: Maya Erez Signed-off-by: Santosh Y Signed-off-by: James Bottomley --- drivers/scsi/ufs/ufshcd.c | 295 +++++++++++++++++++++++++++++++--------------- drivers/scsi/ufs/ufshcd.h | 10 +- 2 files changed, 208 insertions(+), 97 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 2e0248302e8d..48a76459a5c9 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -33,11 +33,15 @@ * this program. */ +#include + #include "ufshcd.h" #define UFSHCD_ENABLE_INTRS (UTP_TRANSFER_REQ_COMPL |\ UTP_TASK_REQ_COMPL |\ UFSHCD_ERROR_MASK) +/* UIC command timeout, unit: ms */ +#define UIC_CMD_TIMEOUT 500 enum { UFSHCD_MAX_CHANNEL = 0, @@ -349,23 +353,121 @@ static inline void ufshcd_hba_capabilities(struct ufs_hba *hba) } /** - * ufshcd_send_uic_command - Send UIC commands to unipro layers + * ufshcd_ready_for_uic_cmd - Check if controller is ready + * to accept UIC commands * @hba: per adapter instance - * @uic_command: UIC command + * Return true on success, else false + */ +static inline bool ufshcd_ready_for_uic_cmd(struct ufs_hba *hba) +{ + if (ufshcd_readl(hba, REG_CONTROLLER_STATUS) & UIC_COMMAND_READY) + return true; + else + return false; +} + +/** + * ufshcd_dispatch_uic_cmd - Dispatch UIC commands to unipro layers + * @hba: per adapter instance + * @uic_cmd: UIC command + * + * Mutex must be held. */ static inline void -ufshcd_send_uic_command(struct ufs_hba *hba, struct uic_command *uic_cmnd) +ufshcd_dispatch_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd) { + WARN_ON(hba->active_uic_cmd); + + hba->active_uic_cmd = uic_cmd; + /* Write Args */ - ufshcd_writel(hba, uic_cmnd->argument1, REG_UIC_COMMAND_ARG_1); - ufshcd_writel(hba, uic_cmnd->argument2, REG_UIC_COMMAND_ARG_2); - ufshcd_writel(hba, uic_cmnd->argument3, REG_UIC_COMMAND_ARG_3); + ufshcd_writel(hba, uic_cmd->argument1, REG_UIC_COMMAND_ARG_1); + ufshcd_writel(hba, uic_cmd->argument2, REG_UIC_COMMAND_ARG_2); + ufshcd_writel(hba, uic_cmd->argument3, REG_UIC_COMMAND_ARG_3); /* Write UIC Cmd */ - ufshcd_writel(hba, uic_cmnd->command & COMMAND_OPCODE_MASK, + ufshcd_writel(hba, uic_cmd->command & COMMAND_OPCODE_MASK, REG_UIC_COMMAND); } +/** + * ufshcd_wait_for_uic_cmd - Wait complectioin of UIC command + * @hba: per adapter instance + * @uic_command: UIC command + * + * Must be called with mutex held. + * Returns 0 only if success. + */ +static int +ufshcd_wait_for_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd) +{ + int ret; + unsigned long flags; + + if (wait_for_completion_timeout(&uic_cmd->done, + msecs_to_jiffies(UIC_CMD_TIMEOUT))) + ret = uic_cmd->argument2 & MASK_UIC_COMMAND_RESULT; + else + ret = -ETIMEDOUT; + + spin_lock_irqsave(hba->host->host_lock, flags); + hba->active_uic_cmd = NULL; + spin_unlock_irqrestore(hba->host->host_lock, flags); + + return ret; +} + +/** + * __ufshcd_send_uic_cmd - Send UIC commands and retrieve the result + * @hba: per adapter instance + * @uic_cmd: UIC command + * + * Identical to ufshcd_send_uic_cmd() expect mutex. Must be called + * with mutex held. + * Returns 0 only if success. + */ +static int +__ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd) +{ + int ret; + unsigned long flags; + + if (!ufshcd_ready_for_uic_cmd(hba)) { + dev_err(hba->dev, + "Controller not ready to accept UIC commands\n"); + return -EIO; + } + + init_completion(&uic_cmd->done); + + spin_lock_irqsave(hba->host->host_lock, flags); + ufshcd_dispatch_uic_cmd(hba, uic_cmd); + spin_unlock_irqrestore(hba->host->host_lock, flags); + + ret = ufshcd_wait_for_uic_cmd(hba, uic_cmd); + + return ret; +} + +/** + * ufshcd_send_uic_cmd - Send UIC commands and retrieve the result + * @hba: per adapter instance + * @uic_cmd: UIC command + * + * Returns 0 only if success. + */ +static int +ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd) +{ + int ret; + + mutex_lock(&hba->uic_cmd_mutex); + ret = __ufshcd_send_uic_cmd(hba, uic_cmd); + mutex_unlock(&hba->uic_cmd_mutex); + + return ret; +} + /** * ufshcd_map_sg - Map scatter-gather list to prdt * @lrbp - pointer to local reference block @@ -725,34 +827,16 @@ static void ufshcd_host_memory_configure(struct ufs_hba *hba) */ static int ufshcd_dme_link_startup(struct ufs_hba *hba) { - struct uic_command *uic_cmd; - unsigned long flags; + struct uic_command uic_cmd = {0}; + int ret; - /* check if controller is ready to accept UIC commands */ - if ((ufshcd_readl(hba, REG_CONTROLLER_STATUS) & - UIC_COMMAND_READY) == 0x0) { - dev_err(hba->dev, - "Controller not ready" - " to accept UIC commands\n"); - return -EIO; - } + uic_cmd.command = UIC_CMD_DME_LINK_STARTUP; - spin_lock_irqsave(hba->host->host_lock, flags); - - /* form UIC command */ - uic_cmd = &hba->active_uic_cmd; - uic_cmd->command = UIC_CMD_DME_LINK_STARTUP; - uic_cmd->argument1 = 0; - uic_cmd->argument2 = 0; - uic_cmd->argument3 = 0; - - /* enable UIC related interrupts */ - ufshcd_enable_intr(hba, UIC_COMMAND_COMPL); - - /* sending UIC commands to controller */ - ufshcd_send_uic_command(hba, uic_cmd); - spin_unlock_irqrestore(hba->host->host_lock, flags); - return 0; + ret = ufshcd_send_uic_cmd(hba, &uic_cmd); + if (ret) + dev_err(hba->dev, + "dme-link-startup: error code %d\n", ret); + return ret; } /** @@ -761,9 +845,10 @@ static int ufshcd_dme_link_startup(struct ufs_hba *hba) * * To bring UFS host controller to operational state, * 1. Check if device is present - * 2. Configure run-stop-registers - * 3. Enable required interrupts - * 4. Configure interrupt aggregation + * 2. Enable required interrupts + * 3. Configure interrupt aggregation + * 4. Program UTRL and UTMRL base addres + * 5. Configure run-stop-registers * * Returns 0 on success, non-zero value on failure */ @@ -780,6 +865,22 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba) goto out; } + /* Enable required interrupts */ + ufshcd_enable_intr(hba, UFSHCD_ENABLE_INTRS); + + /* Configure interrupt aggregation */ + ufshcd_config_int_aggr(hba, INT_AGGR_CONFIG); + + /* Configure UTRL and UTMRL base address registers */ + ufshcd_writel(hba, lower_32_bits(hba->utrdl_dma_addr), + REG_UTP_TRANSFER_REQ_LIST_BASE_L); + ufshcd_writel(hba, upper_32_bits(hba->utrdl_dma_addr), + REG_UTP_TRANSFER_REQ_LIST_BASE_H); + ufshcd_writel(hba, lower_32_bits(hba->utmrdl_dma_addr), + REG_UTP_TASK_REQ_LIST_BASE_L); + ufshcd_writel(hba, upper_32_bits(hba->utmrdl_dma_addr), + REG_UTP_TASK_REQ_LIST_BASE_H); + /* * UCRDY, UTMRLDY and UTRLRDY bits must be 1 * DEI, HEI bits must be 0 @@ -793,17 +894,11 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba) goto out; } - /* Enable required interrupts */ - ufshcd_enable_intr(hba, UFSHCD_ENABLE_INTRS); - - /* Configure interrupt aggregation */ - ufshcd_config_int_aggr(hba, INT_AGGR_CONFIG); - if (hba->ufshcd_state == UFSHCD_STATE_RESET) scsi_unblock_requests(hba->host); hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL; - scsi_scan_host(hba->host); + out: return err; } @@ -872,34 +967,28 @@ static int ufshcd_hba_enable(struct ufs_hba *hba) } /** - * ufshcd_initialize_hba - start the initialization process + * ufshcd_link_startup - Initialize unipro link startup * @hba: per adapter instance * - * 1. Enable the controller via ufshcd_hba_enable. - * 2. Program the Transfer Request List Address with the starting address of - * UTRDL. - * 3. Program the Task Management Request List Address with starting address - * of UTMRDL. - * - * Returns 0 on success, non-zero value on failure. + * Returns 0 for success, non-zero in case of failure */ -static int ufshcd_initialize_hba(struct ufs_hba *hba) +static int ufshcd_link_startup(struct ufs_hba *hba) { - if (ufshcd_hba_enable(hba)) - return -EIO; + int ret; - /* Configure UTRL and UTMRL base address registers */ - ufshcd_writel(hba, lower_32_bits(hba->utrdl_dma_addr), - REG_UTP_TRANSFER_REQ_LIST_BASE_L); - ufshcd_writel(hba, upper_32_bits(hba->utrdl_dma_addr), - REG_UTP_TRANSFER_REQ_LIST_BASE_H); - ufshcd_writel(hba, lower_32_bits(hba->utmrdl_dma_addr), - REG_UTP_TASK_REQ_LIST_BASE_L); - ufshcd_writel(hba, upper_32_bits(hba->utmrdl_dma_addr), - REG_UTP_TASK_REQ_LIST_BASE_H); + /* enable UIC related interrupts */ + ufshcd_enable_intr(hba, UIC_COMMAND_COMPL); + + ret = ufshcd_dme_link_startup(hba); + if (ret) + goto out; + + ret = ufshcd_make_hba_operational(hba); - /* Initialize unipro link startup procedure */ - return ufshcd_dme_link_startup(hba); +out: + if (ret) + dev_err(hba->dev, "link startup failed %d\n", ret); + return ret; } /** @@ -939,12 +1028,19 @@ static int ufshcd_do_reset(struct ufs_hba *hba) hba->outstanding_reqs = 0; hba->outstanding_tasks = 0; - /* start the initialization process */ - if (ufshcd_initialize_hba(hba)) { + /* Host controller enable */ + if (ufshcd_hba_enable(hba)) { dev_err(hba->dev, "Reset: Controller initialization failed\n"); return FAILED; } + + if (ufshcd_link_startup(hba)) { + dev_err(hba->dev, + "Reset: Link start-up failed\n"); + return FAILED; + } + return SUCCESS; } @@ -1175,6 +1271,19 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) return result; } +/** + * ufshcd_uic_cmd_compl - handle completion of uic command + * @hba: per adapter instance + */ +static void ufshcd_uic_cmd_compl(struct ufs_hba *hba) +{ + if (hba->active_uic_cmd) { + hba->active_uic_cmd->argument2 |= + ufshcd_get_uic_cmd_result(hba); + complete(&hba->active_uic_cmd->done); + } +} + /** * ufshcd_transfer_req_compl - handle SCSI and query command completion * @hba: per adapter instance @@ -1214,28 +1323,6 @@ static void ufshcd_transfer_req_compl(struct ufs_hba *hba) ufshcd_config_int_aggr(hba, INT_AGGR_RESET); } -/** - * ufshcd_uic_cc_handler - handle UIC command completion - * @work: pointer to a work queue structure - * - * Returns 0 on success, non-zero value on failure - */ -static void ufshcd_uic_cc_handler (struct work_struct *work) -{ - struct ufs_hba *hba; - - hba = container_of(work, struct ufs_hba, uic_workq); - - if ((hba->active_uic_cmd.command == UIC_CMD_DME_LINK_STARTUP) && - !(ufshcd_get_uic_cmd_result(hba))) { - - if (ufshcd_make_hba_operational(hba)) - dev_err(hba->dev, - "cc: hba not operational state\n"); - return; - } -} - /** * ufshcd_fatal_err_handler - handle fatal errors * @hba: per adapter instance @@ -1297,7 +1384,7 @@ static void ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status) ufshcd_err_handler(hba); if (intr_status & UIC_COMMAND_COMPL) - schedule_work(&hba->uic_workq); + ufshcd_uic_cmd_compl(hba); if (intr_status & UTP_TASK_REQ_COMPL) ufshcd_tmc_handler(hba); @@ -1520,6 +1607,21 @@ out: return err; } +/** + * ufshcd_async_scan - asynchronous execution for link startup + * @data: data pointer to pass to this function + * @cookie: cookie data + */ +static void ufshcd_async_scan(void *data, async_cookie_t cookie) +{ + struct ufs_hba *hba = (struct ufs_hba *)data; + int ret; + + ret = ufshcd_link_startup(hba); + if (!ret) + scsi_scan_host(hba->host); +} + static struct scsi_host_template ufshcd_driver_template = { .module = THIS_MODULE, .name = UFSHCD, @@ -1681,9 +1783,11 @@ int ufshcd_init(struct device *dev, struct ufs_hba **hba_handle, init_waitqueue_head(&hba->ufshcd_tm_wait_queue); /* Initialize work queues */ - INIT_WORK(&hba->uic_workq, ufshcd_uic_cc_handler); INIT_WORK(&hba->feh_workq, ufshcd_fatal_err_handler); + /* Initialize UIC command mutex */ + mutex_init(&hba->uic_cmd_mutex); + /* IRQ registration */ err = request_irq(irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba); if (err) { @@ -1704,14 +1808,17 @@ int ufshcd_init(struct device *dev, struct ufs_hba **hba_handle, goto out_free_irq; } - /* Initialization routine */ - err = ufshcd_initialize_hba(hba); + /* Host controller enable */ + err = ufshcd_hba_enable(hba); if (err) { - dev_err(hba->dev, "Initialization failed\n"); + dev_err(hba->dev, "Host controller enable failed\n"); goto out_remove_scsi_host; } + *hba_handle = hba; + async_schedule(ufshcd_async_scan, hba); + return 0; out_remove_scsi_host: diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 4213600b89d6..49590ee07acc 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -51,6 +51,7 @@ #include #include #include +#include #include #include @@ -75,6 +76,7 @@ * @argument3: UIC command argument 3 * @cmd_active: Indicate if UIC command is outstanding * @result: UIC command result + * @done: UIC command completion */ struct uic_command { u32 command; @@ -83,6 +85,7 @@ struct uic_command { u32 argument3; int cmd_active; int result; + struct completion done; }; /** @@ -136,11 +139,11 @@ struct ufshcd_lrb { * @ufs_version: UFS Version to which controller complies * @irq: Irq number of the controller * @active_uic_cmd: handle of active UIC command + * @uic_cmd_mutex: mutex for uic command * @ufshcd_tm_wait_queue: wait queue for task management * @tm_condition: condition variable for task management * @ufshcd_state: UFSHCD states * @intr_mask: Interrupt Mask Bits - * @uic_workq: Work queue for UIC completion handling * @feh_workq: Work queue for fatal controller error handling * @errors: HBA errors */ @@ -171,7 +174,9 @@ struct ufs_hba { u32 ufs_version; unsigned int irq; - struct uic_command active_uic_cmd; + struct uic_command *active_uic_cmd; + struct mutex uic_cmd_mutex; + wait_queue_head_t ufshcd_tm_wait_queue; unsigned long tm_condition; @@ -179,7 +184,6 @@ struct ufs_hba { u32 intr_mask; /* Work Queues */ - struct work_struct uic_workq; struct work_struct feh_workq; /* HBA Errors */ -- cgit v1.2.3 From 3ca316c582ddf11944806a27e460e7bd8f61a968 Mon Sep 17 00:00:00 2001 From: Sujit Reddy Thumma Date: Wed, 26 Jun 2013 22:39:30 +0530 Subject: [SCSI] ufs: Fix the response UPIU length setting The response UPIU length should be in DWORD and not in bytes. Signed-off-by: Maya Erez Signed-off-by: Sujit Reddy Thumma Signed-off-by: Santosh Y Signed-off-by: James Bottomley --- drivers/scsi/ufs/ufshcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 48a76459a5c9..2230f1412d88 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -802,7 +802,7 @@ static void ufshcd_host_memory_configure(struct ufs_hba *hba) utrdlp[i].prd_table_offset = cpu_to_le16((prdt_offset >> 2)); utrdlp[i].response_upiu_length = - cpu_to_le16(ALIGNED_UPIU_SIZE); + cpu_to_le16(ALIGNED_UPIU_SIZE >> 2); hba->lrb[i].utr_descriptor_ptr = (utrdlp + i); hba->lrb[i].ucd_cmd_ptr = -- cgit v1.2.3 From 2953f850c3b80bdca004967c83733365d8aa0aa2 Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Thu, 27 Jun 2013 13:31:54 +0900 Subject: [SCSI] ufs: use devres functions for ufshcd This patch replaces normal calls for resource allocation with devm_*() derivative functions. It makes resource freeing simpler. Signed-off-by: Seungwon Jeon Signed-off-by: Santosh Y Signed-off-by: James Bottomley --- drivers/scsi/ufs/ufshcd-pci.c | 1 - drivers/scsi/ufs/ufshcd-pltfrm.c | 72 ++++++++++----------------------- drivers/scsi/ufs/ufshcd.c | 86 +++++++++------------------------------- 3 files changed, 39 insertions(+), 120 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd-pci.c b/drivers/scsi/ufs/ufshcd-pci.c index 5cb1d75f5868..48be39a6f6d7 100644 --- a/drivers/scsi/ufs/ufshcd-pci.c +++ b/drivers/scsi/ufs/ufshcd-pci.c @@ -92,7 +92,6 @@ static void ufshcd_pci_remove(struct pci_dev *pdev) struct ufs_hba *hba = pci_get_drvdata(pdev); disable_irq(pdev->irq); - free_irq(pdev->irq, hba); ufshcd_remove(hba); pci_release_regions(pdev); pci_set_drvdata(pdev, NULL); diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c index 3db2ee1a38bd..0e48827c86bd 100644 --- a/drivers/scsi/ufs/ufshcd-pltfrm.c +++ b/drivers/scsi/ufs/ufshcd-pltfrm.c @@ -33,9 +33,10 @@ * this program. */ -#include "ufshcd.h" #include +#include "ufshcd.h" + #ifdef CONFIG_PM /** * ufshcd_pltfrm_suspend - suspend power management function @@ -97,62 +98,45 @@ static int ufshcd_pltfrm_probe(struct platform_device *pdev) struct ufs_hba *hba; void __iomem *mmio_base; struct resource *mem_res; - struct resource *irq_res; - resource_size_t mem_size; - int err; + int irq, err; struct device *dev = &pdev->dev; mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mem_res) { - dev_err(&pdev->dev, - "Memory resource not available\n"); + dev_err(dev, "Memory resource not available\n"); err = -ENODEV; - goto out_error; + goto out; } - mem_size = resource_size(mem_res); - if (!request_mem_region(mem_res->start, mem_size, "ufshcd")) { - dev_err(&pdev->dev, - "Cannot reserve the memory resource\n"); - err = -EBUSY; - goto out_error; + mmio_base = devm_ioremap_resource(dev, mem_res); + if (IS_ERR(mmio_base)) { + dev_err(dev, "memory map failed\n"); + err = PTR_ERR(mmio_base); + goto out; } - mmio_base = ioremap_nocache(mem_res->start, mem_size); - if (!mmio_base) { - dev_err(&pdev->dev, "memory map failed\n"); - err = -ENOMEM; - goto out_release_regions; - } - - irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!irq_res) { - dev_err(&pdev->dev, "IRQ resource not available\n"); + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev, "IRQ resource not available\n"); err = -ENODEV; - goto out_iounmap; + goto out; } err = dma_set_coherent_mask(dev, dev->coherent_dma_mask); if (err) { - dev_err(&pdev->dev, "set dma mask failed\n"); - goto out_iounmap; + dev_err(dev, "set dma mask failed\n"); + goto out; } - err = ufshcd_init(&pdev->dev, &hba, mmio_base, irq_res->start); + err = ufshcd_init(dev, &hba, mmio_base, irq); if (err) { - dev_err(&pdev->dev, "Intialization failed\n"); - goto out_iounmap; + dev_err(dev, "Intialization failed\n"); + goto out; } platform_set_drvdata(pdev, hba); - return 0; - -out_iounmap: - iounmap(mmio_base); -out_release_regions: - release_mem_region(mem_res->start, mem_size); -out_error: +out: return err; } @@ -164,26 +148,10 @@ out_error: */ static int ufshcd_pltfrm_remove(struct platform_device *pdev) { - struct resource *mem_res; - resource_size_t mem_size; struct ufs_hba *hba = platform_get_drvdata(pdev); disable_irq(hba->irq); - - /* Some buggy controllers raise interrupt after - * the resources are removed. So first we unregister the - * irq handler and then the resources used by driver - */ - - free_irq(hba->irq, hba); ufshcd_remove(hba); - mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem_res) - dev_err(&pdev->dev, "ufshcd: Memory resource not available\n"); - else { - mem_size = resource_size(mem_res); - release_mem_region(mem_res->start, mem_size); - } return 0; } diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 2230f1412d88..aba461f0f11c 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -190,38 +190,6 @@ static inline int ufshcd_get_uic_cmd_result(struct ufs_hba *hba) MASK_UIC_COMMAND_RESULT; } -/** - * ufshcd_free_hba_memory - Free allocated memory for LRB, request - * and task lists - * @hba: Pointer to adapter instance - */ -static inline void ufshcd_free_hba_memory(struct ufs_hba *hba) -{ - size_t utmrdl_size, utrdl_size, ucdl_size; - - kfree(hba->lrb); - - if (hba->utmrdl_base_addr) { - utmrdl_size = sizeof(struct utp_task_req_desc) * hba->nutmrs; - dma_free_coherent(hba->dev, utmrdl_size, - hba->utmrdl_base_addr, hba->utmrdl_dma_addr); - } - - if (hba->utrdl_base_addr) { - utrdl_size = - (sizeof(struct utp_transfer_req_desc) * hba->nutrs); - dma_free_coherent(hba->dev, utrdl_size, - hba->utrdl_base_addr, hba->utrdl_dma_addr); - } - - if (hba->ucdl_base_addr) { - ucdl_size = - (sizeof(struct utp_transfer_cmd_desc) * hba->nutrs); - dma_free_coherent(hba->dev, ucdl_size, - hba->ucdl_base_addr, hba->ucdl_dma_addr); - } -} - /** * ufshcd_is_valid_req_rsp - checks if controller TR response is valid * @ucd_rsp_ptr: pointer to response UPIU @@ -690,10 +658,10 @@ static int ufshcd_memory_alloc(struct ufs_hba *hba) /* Allocate memory for UTP command descriptors */ ucdl_size = (sizeof(struct utp_transfer_cmd_desc) * hba->nutrs); - hba->ucdl_base_addr = dma_alloc_coherent(hba->dev, - ucdl_size, - &hba->ucdl_dma_addr, - GFP_KERNEL); + hba->ucdl_base_addr = dmam_alloc_coherent(hba->dev, + ucdl_size, + &hba->ucdl_dma_addr, + GFP_KERNEL); /* * UFSHCI requires UTP command descriptor to be 128 byte aligned. @@ -713,10 +681,10 @@ static int ufshcd_memory_alloc(struct ufs_hba *hba) * UFSHCI requires 1024 byte alignment of UTRD */ utrdl_size = (sizeof(struct utp_transfer_req_desc) * hba->nutrs); - hba->utrdl_base_addr = dma_alloc_coherent(hba->dev, - utrdl_size, - &hba->utrdl_dma_addr, - GFP_KERNEL); + hba->utrdl_base_addr = dmam_alloc_coherent(hba->dev, + utrdl_size, + &hba->utrdl_dma_addr, + GFP_KERNEL); if (!hba->utrdl_base_addr || WARN_ON(hba->utrdl_dma_addr & (PAGE_SIZE - 1))) { dev_err(hba->dev, @@ -729,10 +697,10 @@ static int ufshcd_memory_alloc(struct ufs_hba *hba) * UFSHCI requires 1024 byte alignment of UTMRD */ utmrdl_size = sizeof(struct utp_task_req_desc) * hba->nutmrs; - hba->utmrdl_base_addr = dma_alloc_coherent(hba->dev, - utmrdl_size, - &hba->utmrdl_dma_addr, - GFP_KERNEL); + hba->utmrdl_base_addr = dmam_alloc_coherent(hba->dev, + utmrdl_size, + &hba->utmrdl_dma_addr, + GFP_KERNEL); if (!hba->utmrdl_base_addr || WARN_ON(hba->utmrdl_dma_addr & (PAGE_SIZE - 1))) { dev_err(hba->dev, @@ -741,14 +709,15 @@ static int ufshcd_memory_alloc(struct ufs_hba *hba) } /* Allocate memory for local reference block */ - hba->lrb = kcalloc(hba->nutrs, sizeof(struct ufshcd_lrb), GFP_KERNEL); + hba->lrb = devm_kzalloc(hba->dev, + hba->nutrs * sizeof(struct ufshcd_lrb), + GFP_KERNEL); if (!hba->lrb) { dev_err(hba->dev, "LRB Memory allocation failed\n"); goto out; } return 0; out: - ufshcd_free_hba_memory(hba); return -ENOMEM; } @@ -1681,17 +1650,6 @@ int ufshcd_resume(struct ufs_hba *hba) } EXPORT_SYMBOL_GPL(ufshcd_resume); -/** - * ufshcd_hba_free - free allocated memory for - * host memory space data structures - * @hba: per adapter instance - */ -static void ufshcd_hba_free(struct ufs_hba *hba) -{ - iounmap(hba->mmio_base); - ufshcd_free_hba_memory(hba); -} - /** * ufshcd_remove - de-allocate SCSI host and host memory space * data structure memory @@ -1701,9 +1659,7 @@ void ufshcd_remove(struct ufs_hba *hba) { /* disable interrupts */ ufshcd_disable_intr(hba, hba->intr_mask); - ufshcd_hba_stop(hba); - ufshcd_hba_free(hba); scsi_remove_host(hba->host); scsi_host_put(hba->host); @@ -1789,23 +1745,23 @@ int ufshcd_init(struct device *dev, struct ufs_hba **hba_handle, mutex_init(&hba->uic_cmd_mutex); /* IRQ registration */ - err = request_irq(irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba); + err = devm_request_irq(dev, irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba); if (err) { dev_err(hba->dev, "request irq failed\n"); - goto out_lrb_free; + goto out_disable; } /* Enable SCSI tag mapping */ err = scsi_init_shared_tag_map(host, host->can_queue); if (err) { dev_err(hba->dev, "init shared queue failed\n"); - goto out_free_irq; + goto out_disable; } err = scsi_add_host(host, hba->dev); if (err) { dev_err(hba->dev, "scsi_add_host failed\n"); - goto out_free_irq; + goto out_disable; } /* Host controller enable */ @@ -1823,10 +1779,6 @@ int ufshcd_init(struct device *dev, struct ufs_hba **hba_handle, out_remove_scsi_host: scsi_remove_host(hba->host); -out_free_irq: - free_irq(irq, hba); -out_lrb_free: - ufshcd_free_hba_memory(hba); out_disable: scsi_host_put(host); out_error: -- cgit v1.2.3 From 2e2930a3449f1fe3806e26c4391a71097cae43f3 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Wed, 26 Jun 2013 22:39:32 +0530 Subject: [SCSI] ufshcd-pltfrm: add missing empty slot in ufs_of_match[] of_match_table member in struct device_driver must be terminated by empty slot as a sentinel. Signed-off-by: Akinobu Mita Signed-off-by: Santosh Y Signed-off-by: James Bottomley --- drivers/scsi/ufs/ufshcd-pltfrm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c index 0e48827c86bd..6829a160869f 100644 --- a/drivers/scsi/ufs/ufshcd-pltfrm.c +++ b/drivers/scsi/ufs/ufshcd-pltfrm.c @@ -157,6 +157,7 @@ static int ufshcd_pltfrm_remove(struct platform_device *pdev) static const struct of_device_id ufs_of_match[] = { { .compatible = "jedec,ufs-1.1"}, + {}, }; static const struct dev_pm_ops ufshcd_dev_pm_ops = { -- cgit v1.2.3 From cf9f4b59c52bd12c850c20435eef9f196976f541 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Wed, 26 Jun 2013 22:39:33 +0530 Subject: [SCSI] ufs: fix register address in UIC error interrupt handling In UIC error interrupt handling, it checks if UIC data link layer error code indicates PA_INIT_ERROR in order to determine whether a fatal error handling is needed or not. But the code tries to read UIC data link layer error code from wrong REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER, it should be REG_UIC_ERROR_CODE_DATA_LINK_LAYER. Signed-off-by: Akinobu Mita Signed-off-by: Santosh Y Signed-off-by: James Bottomley --- drivers/scsi/ufs/ufshcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index aba461f0f11c..b743bd6fce6b 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -1318,7 +1318,7 @@ static void ufshcd_err_handler(struct ufs_hba *hba) goto fatal_eh; if (hba->errors & UIC_ERROR) { - reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER); + reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_DATA_LINK_LAYER); if (reg & UIC_DATA_LINK_LAYER_ERROR_PA_INIT) goto fatal_eh; } -- cgit v1.2.3 From aca898f5d7ddcd0e7cc358a613338b009a88af80 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Wed, 26 Jun 2013 22:39:34 +0530 Subject: [SCSI] ufshcd-pltfrm: remove unnecessary dma_set_coherent_mask() call Changing the device coherent dma mask to the value that currently set has no effect. Signed-off-by: Akinobu Mita Signed-off-by: Santosh Y Signed-off-by: James Bottomley --- drivers/scsi/ufs/ufshcd-pltfrm.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c index 6829a160869f..c42db40d4e51 100644 --- a/drivers/scsi/ufs/ufshcd-pltfrm.c +++ b/drivers/scsi/ufs/ufshcd-pltfrm.c @@ -122,12 +122,6 @@ static int ufshcd_pltfrm_probe(struct platform_device *pdev) goto out; } - err = dma_set_coherent_mask(dev, dev->coherent_dma_mask); - if (err) { - dev_err(dev, "set dma mask failed\n"); - goto out; - } - err = ufshcd_init(dev, &hba, mmio_base, irq); if (err) { dev_err(dev, "Intialization failed\n"); -- cgit v1.2.3 From a3fda7dd5179989dd0ead820dcebd13f956ddec1 Mon Sep 17 00:00:00 2001 From: James Georgas Date: Wed, 26 Jun 2013 12:03:19 -0600 Subject: [SCSI] megaraid: minor cut and paste error fixed. This looks like a cut and paste typo to me. Both of the megasas_read_fw_status_reg_* functions involved are identical though, so there was no bad behaviour. I changed it for consistency and clarity. Signed-off-by: James Georgas Acked-by: Sumit Saxena Signed-off-by: James Bottomley --- drivers/scsi/megaraid/megaraid_sas_base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index 4c4abe190c77..6002d363c637 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -596,7 +596,7 @@ megasas_clear_intr_skinny(struct megasas_register_set __iomem *regs) /* * Check if it is our interrupt */ - if ((megasas_read_fw_status_reg_gen2(regs) & MFI_STATE_MASK) == + if ((megasas_read_fw_status_reg_skinny(regs) & MFI_STATE_MASK) == MFI_STATE_FAULT) { mfiStatus = MFI_INTR_FLAG_FIRMWARE_STATE_CHANGE; } else -- cgit v1.2.3 From 7c4c3a0f18ba57ea2a2985034532303d2929902a Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Thu, 27 Jun 2013 11:35:44 +0100 Subject: hrtimers: Support resuming with two or more CPUs online (but stopped) hrtimers_resume() only reprograms the timers for the current CPU as it assumes that all other CPUs are offline at this point in the resume process. If other CPUs are online then their timers will not be corrected and they may fire at the wrong time. When running as a Xen guest, this assumption is not true. Non-boot CPUs are only stopped with IRQs disabled instead of offlining them. This is a performance optimization as disabling the CPUs would add an unacceptable amount of additional downtime during a live migration (> 200 ms for a 4 VCPU guest). hrtimers_resume() cannot call on_each_cpu(retrigger_next_event,...) as the other CPUs will be stopped with IRQs disabled. Instead, defer the call to the next softirq. [ tglx: Separated the xen change out ] Signed-off-by: David Vrabel Cc: Konrad Rzeszutek Wilk Cc: John Stultz Cc: Link: http://lkml.kernel.org/r/1372329348-20841-2-git-send-email-david.vrabel@citrix.com Signed-off-by: Thomas Gleixner --- kernel/hrtimer.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index fd4b13b131f8..e86827e94c9a 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -773,15 +773,24 @@ void clock_was_set(void) /* * During resume we might have to reprogram the high resolution timer - * interrupt (on the local CPU): + * interrupt on all online CPUs. However, all other CPUs will be + * stopped with IRQs interrupts disabled so the clock_was_set() call + * must be deferred to the softirq. + * + * The one-shot timer has already been programmed to fire immediately + * (see tick_resume_oneshot()) and this interrupt will trigger the + * softirq to run early enough to correctly reprogram the timers on + * all CPUs. */ void hrtimers_resume(void) { + struct hrtimer_cpu_base *cpu_base = &__get_cpu_var(hrtimer_bases); + WARN_ONCE(!irqs_disabled(), KERN_INFO "hrtimers_resume() called with IRQs enabled!"); - retrigger_next_event(NULL); - timerfd_clock_was_set(); + cpu_base->clock_was_set = 1; + __raise_softirq_irqoff(HRTIMER_SOFTIRQ); } static inline void timer_stats_hrtimer_set_start_info(struct hrtimer *timer) -- cgit v1.2.3 From 0eb071651474952c8b6daecd36b378e2d01be22c Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Thu, 27 Jun 2013 11:35:44 +0100 Subject: xen: Remove clock_was_set() call in the resume path commit 359cdd3f866(xen: maintain clock offset over save/restore) added a clock_was_set() call into the xen resume code to propagate the system time changes. With the modified hrtimer resume code, which makes sure that all cpus are notified this call is not longer necessary. [ tglx: Separated it from the hrtimer change ] Signed-off-by: David Vrabel Cc: Konrad Rzeszutek Wilk Cc: John Stultz Cc: Link: http://lkml.kernel.org/r/1372329348-20841-2-git-send-email-david.vrabel@citrix.com Signed-off-by: Thomas Gleixner --- drivers/xen/manage.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/xen/manage.c b/drivers/xen/manage.c index 412b96cc5305..421da856135d 100644 --- a/drivers/xen/manage.c +++ b/drivers/xen/manage.c @@ -166,9 +166,6 @@ out_resume: dpm_resume_end(si.cancelled ? PMSG_THAW : PMSG_RESTORE); - /* Make sure timer events get retriggered on all CPUs */ - clock_was_set(); - out_thaw: #ifdef CONFIG_PREEMPT thaw_processes(); -- cgit v1.2.3 From 04397fe94ad65289884b9862b6a0c722ececaadf Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Thu, 27 Jun 2013 11:35:45 +0100 Subject: timekeeping: Pass flags instead of multiple bools to timekeeping_update() Instead of passing multiple bools to timekeeping_updated(), define flags and use a single 'action' parameter. It is then more obvious what each timekeeping_update() call does. Signed-off-by: David Vrabel Cc: Konrad Rzeszutek Wilk Cc: John Stultz Cc: Link: http://lkml.kernel.org/r/1372329348-20841-3-git-send-email-david.vrabel@citrix.com Signed-off-by: Thomas Gleixner --- kernel/time/timekeeping.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 838fc0777b68..d8b23a929e66 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -27,6 +27,9 @@ #include "ntp_internal.h" #include "timekeeping_internal.h" +#define TK_CLEAR_NTP (1 << 0) +#define TK_MIRROR (1 << 1) + static struct timekeeper timekeeper; static DEFINE_RAW_SPINLOCK(timekeeper_lock); static seqcount_t timekeeper_seq; @@ -242,16 +245,16 @@ int pvclock_gtod_unregister_notifier(struct notifier_block *nb) EXPORT_SYMBOL_GPL(pvclock_gtod_unregister_notifier); /* must hold timekeeper_lock */ -static void timekeeping_update(struct timekeeper *tk, bool clearntp, bool mirror) +static void timekeeping_update(struct timekeeper *tk, unsigned int action) { - if (clearntp) { + if (action & TK_CLEAR_NTP) { tk->ntp_error = 0; ntp_clear(); } update_vsyscall(tk); update_pvclock_gtod(tk); - if (mirror) + if (action & TK_MIRROR) memcpy(&shadow_timekeeper, &timekeeper, sizeof(timekeeper)); } @@ -509,7 +512,7 @@ int do_settimeofday(const struct timespec *tv) tk_set_xtime(tk, tv); - timekeeping_update(tk, true, true); + timekeeping_update(tk, TK_CLEAR_NTP | TK_MIRROR); write_seqcount_end(&timekeeper_seq); raw_spin_unlock_irqrestore(&timekeeper_lock, flags); @@ -553,7 +556,7 @@ int timekeeping_inject_offset(struct timespec *ts) tk_set_wall_to_mono(tk, timespec_sub(tk->wall_to_monotonic, *ts)); error: /* even if we error out, we forwarded the time, so call update */ - timekeeping_update(tk, true, true); + timekeeping_update(tk, TK_CLEAR_NTP | TK_MIRROR); write_seqcount_end(&timekeeper_seq); raw_spin_unlock_irqrestore(&timekeeper_lock, flags); @@ -643,7 +646,7 @@ static int change_clocksource(void *data) module_put(new->owner); } } - timekeeping_update(tk, true, true); + timekeeping_update(tk, TK_CLEAR_NTP | TK_MIRROR); write_seqcount_end(&timekeeper_seq); raw_spin_unlock_irqrestore(&timekeeper_lock, flags); @@ -884,7 +887,7 @@ void timekeeping_inject_sleeptime(struct timespec *delta) __timekeeping_inject_sleeptime(tk, delta); - timekeeping_update(tk, true, true); + timekeeping_update(tk, TK_CLEAR_NTP | TK_MIRROR); write_seqcount_end(&timekeeper_seq); raw_spin_unlock_irqrestore(&timekeeper_lock, flags); @@ -966,7 +969,7 @@ static void timekeeping_resume(void) tk->cycle_last = clock->cycle_last = cycle_now; tk->ntp_error = 0; timekeeping_suspended = 0; - timekeeping_update(tk, false, true); + timekeeping_update(tk, TK_MIRROR); write_seqcount_end(&timekeeper_seq); raw_spin_unlock_irqrestore(&timekeeper_lock, flags); @@ -1419,7 +1422,7 @@ static void update_wall_time(void) * updating. */ memcpy(real_tk, tk, sizeof(*tk)); - timekeeping_update(real_tk, false, false); + timekeeping_update(real_tk, 0); write_seqcount_end(&timekeeper_seq); out: raw_spin_unlock_irqrestore(&timekeeper_lock, flags); -- cgit v1.2.3 From 780427f0e113b4c77dfff4d258c05a902cdb0eb9 Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Thu, 27 Jun 2013 11:35:46 +0100 Subject: timekeeping: Indicate that clock was set in the pvclock gtod notifier If the clock was set (stepped), set the action parameter to functions in the pvclock gtod notifier chain to non-zero. This allows the callee to only do work if the clock was stepped. This will be used on Xen as the synchronization of the Xen wallclock to the control domain's (dom0) system time will be done with this notifier and updating on every timer tick is unnecessary and too expensive. Signed-off-by: David Vrabel Cc: Konrad Rzeszutek Wilk Cc: John Stultz Cc: Link: http://lkml.kernel.org/r/1372329348-20841-4-git-send-email-david.vrabel@citrix.com Signed-off-by: Thomas Gleixner --- include/linux/pvclock_gtod.h | 7 +++++++ kernel/time/timekeeping.c | 30 ++++++++++++++++++------------ 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/include/linux/pvclock_gtod.h b/include/linux/pvclock_gtod.h index 0ca75825b60d..a71d2dbd3610 100644 --- a/include/linux/pvclock_gtod.h +++ b/include/linux/pvclock_gtod.h @@ -3,6 +3,13 @@ #include +/* + * The pvclock gtod notifier is called when the system time is updated + * and is used to keep guest time synchronized with host time. + * + * The 'action' parameter in the notifier function is false (0), or + * true (non-zero) if system time was stepped. + */ extern int pvclock_gtod_register_notifier(struct notifier_block *nb); extern int pvclock_gtod_unregister_notifier(struct notifier_block *nb); diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index d8b23a929e66..846d0a1f235e 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -29,6 +29,7 @@ #define TK_CLEAR_NTP (1 << 0) #define TK_MIRROR (1 << 1) +#define TK_CLOCK_WAS_SET (1 << 2) static struct timekeeper timekeeper; static DEFINE_RAW_SPINLOCK(timekeeper_lock); @@ -204,9 +205,9 @@ static inline s64 timekeeping_get_ns_raw(struct timekeeper *tk) static RAW_NOTIFIER_HEAD(pvclock_gtod_chain); -static void update_pvclock_gtod(struct timekeeper *tk) +static void update_pvclock_gtod(struct timekeeper *tk, bool was_set) { - raw_notifier_call_chain(&pvclock_gtod_chain, 0, tk); + raw_notifier_call_chain(&pvclock_gtod_chain, was_set, tk); } /** @@ -220,7 +221,7 @@ int pvclock_gtod_register_notifier(struct notifier_block *nb) raw_spin_lock_irqsave(&timekeeper_lock, flags); ret = raw_notifier_chain_register(&pvclock_gtod_chain, nb); - update_pvclock_gtod(tk); + update_pvclock_gtod(tk, true); raw_spin_unlock_irqrestore(&timekeeper_lock, flags); return ret; @@ -252,7 +253,7 @@ static void timekeeping_update(struct timekeeper *tk, unsigned int action) ntp_clear(); } update_vsyscall(tk); - update_pvclock_gtod(tk); + update_pvclock_gtod(tk, action & TK_CLOCK_WAS_SET); if (action & TK_MIRROR) memcpy(&shadow_timekeeper, &timekeeper, sizeof(timekeeper)); @@ -512,7 +513,7 @@ int do_settimeofday(const struct timespec *tv) tk_set_xtime(tk, tv); - timekeeping_update(tk, TK_CLEAR_NTP | TK_MIRROR); + timekeeping_update(tk, TK_CLEAR_NTP | TK_MIRROR | TK_CLOCK_WAS_SET); write_seqcount_end(&timekeeper_seq); raw_spin_unlock_irqrestore(&timekeeper_lock, flags); @@ -556,7 +557,7 @@ int timekeeping_inject_offset(struct timespec *ts) tk_set_wall_to_mono(tk, timespec_sub(tk->wall_to_monotonic, *ts)); error: /* even if we error out, we forwarded the time, so call update */ - timekeeping_update(tk, TK_CLEAR_NTP | TK_MIRROR); + timekeeping_update(tk, TK_CLEAR_NTP | TK_MIRROR | TK_CLOCK_WAS_SET); write_seqcount_end(&timekeeper_seq); raw_spin_unlock_irqrestore(&timekeeper_lock, flags); @@ -646,7 +647,7 @@ static int change_clocksource(void *data) module_put(new->owner); } } - timekeeping_update(tk, TK_CLEAR_NTP | TK_MIRROR); + timekeeping_update(tk, TK_CLEAR_NTP | TK_MIRROR | TK_CLOCK_WAS_SET); write_seqcount_end(&timekeeper_seq); raw_spin_unlock_irqrestore(&timekeeper_lock, flags); @@ -887,7 +888,7 @@ void timekeeping_inject_sleeptime(struct timespec *delta) __timekeeping_inject_sleeptime(tk, delta); - timekeeping_update(tk, TK_CLEAR_NTP | TK_MIRROR); + timekeeping_update(tk, TK_CLEAR_NTP | TK_MIRROR | TK_CLOCK_WAS_SET); write_seqcount_end(&timekeeper_seq); raw_spin_unlock_irqrestore(&timekeeper_lock, flags); @@ -969,7 +970,7 @@ static void timekeeping_resume(void) tk->cycle_last = clock->cycle_last = cycle_now; tk->ntp_error = 0; timekeeping_suspended = 0; - timekeeping_update(tk, TK_MIRROR); + timekeeping_update(tk, TK_MIRROR | TK_CLOCK_WAS_SET); write_seqcount_end(&timekeeper_seq); raw_spin_unlock_irqrestore(&timekeeper_lock, flags); @@ -1243,9 +1244,10 @@ out_adjust: * It also calls into the NTP code to handle leapsecond processing. * */ -static inline void accumulate_nsecs_to_secs(struct timekeeper *tk) +static inline unsigned int accumulate_nsecs_to_secs(struct timekeeper *tk) { u64 nsecps = (u64)NSEC_PER_SEC << tk->shift; + unsigned int action = 0; while (tk->xtime_nsec >= nsecps) { int leap; @@ -1268,8 +1270,10 @@ static inline void accumulate_nsecs_to_secs(struct timekeeper *tk) __timekeeping_set_tai_offset(tk, tk->tai_offset - leap); clock_was_set_delayed(); + action = TK_CLOCK_WAS_SET; } } + return action; } /** @@ -1354,6 +1358,7 @@ static void update_wall_time(void) struct timekeeper *tk = &shadow_timekeeper; cycle_t offset; int shift = 0, maxshift; + unsigned int action; unsigned long flags; raw_spin_lock_irqsave(&timekeeper_lock, flags); @@ -1406,7 +1411,7 @@ static void update_wall_time(void) * Finally, make sure that after the rounding * xtime_nsec isn't larger than NSEC_PER_SEC */ - accumulate_nsecs_to_secs(tk); + action = accumulate_nsecs_to_secs(tk); write_seqcount_begin(&timekeeper_seq); /* Update clock->cycle_last with the new value */ @@ -1422,7 +1427,7 @@ static void update_wall_time(void) * updating. */ memcpy(real_tk, tk, sizeof(*tk)); - timekeeping_update(real_tk, 0); + timekeeping_update(real_tk, action); write_seqcount_end(&timekeeper_seq); out: raw_spin_unlock_irqrestore(&timekeeper_lock, flags); @@ -1684,6 +1689,7 @@ int do_adjtimex(struct timex *txc) if (tai != orig_tai) { __timekeeping_set_tai_offset(tk, tai); + update_pvclock_gtod(tk, true); clock_was_set_delayed(); } write_seqcount_end(&timekeeper_seq); -- cgit v1.2.3 From 5584880e44e49c587059801faa2a9f7d22619c48 Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Thu, 27 Jun 2013 11:35:47 +0100 Subject: x86: xen: Sync the wallclock when the system time is set Currently the Xen wallclock is only updated every 11 minutes if NTP is synchronized to its clock source (using the sync_cmos_clock() work). If a guest is started before NTP is synchronized it may see an incorrect wallclock time. Use the pvclock_gtod notifier chain to receive a notification when the system time has changed and update the wallclock to match. This chain is called on every timer tick and we want to avoid an extra (expensive) hypercall on every tick. Because dom0 has historically never provided a very accurate wallclock and guests do not expect one, we can do this simply: the wallclock is only updated if the clock was set. Signed-off-by: David Vrabel Cc: Konrad Rzeszutek Wilk Cc: John Stultz Cc: Link: http://lkml.kernel.org/r/1372329348-20841-5-git-send-email-david.vrabel@citrix.com Signed-off-by: Thomas Gleixner --- arch/x86/xen/time.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/arch/x86/xen/time.c b/arch/x86/xen/time.c index a1947ac2da82..3364850d23e6 100644 --- a/arch/x86/xen/time.c +++ b/arch/x86/xen/time.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -212,6 +213,30 @@ static int xen_set_wallclock(const struct timespec *now) return HYPERVISOR_dom0_op(&op); } +static int xen_pvclock_gtod_notify(struct notifier_block *nb, unsigned long was_set, + void *priv) +{ + struct timespec now; + struct xen_platform_op op; + + if (!was_set) + return NOTIFY_OK; + + now = __current_kernel_time(); + + op.cmd = XENPF_settime; + op.u.settime.secs = now.tv_sec; + op.u.settime.nsecs = now.tv_nsec; + op.u.settime.system_time = xen_clocksource_read(); + + (void)HYPERVISOR_dom0_op(&op); + return NOTIFY_OK; +} + +static struct notifier_block xen_pvclock_gtod_notifier = { + .notifier_call = xen_pvclock_gtod_notify, +}; + static struct clocksource xen_clocksource __read_mostly = { .name = "xen", .rating = 400, @@ -473,6 +498,9 @@ static void __init xen_time_init(void) xen_setup_runstate_info(cpu); xen_setup_timer(cpu); xen_setup_cpu_clockevents(); + + if (xen_initial_domain()) + pvclock_gtod_register_notifier(&xen_pvclock_gtod_notifier); } void __init xen_init_time_ops(void) -- cgit v1.2.3 From 47433b8c9d7480a3eebd99df38e857ce85a37cee Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Thu, 27 Jun 2013 11:35:48 +0100 Subject: x86: xen: Sync the CMOS RTC as well as the Xen wallclock Adjustments to Xen's persistent clock via update_persistent_clock() don't actually persist, as the Xen wallclock is a software only clock and modifications to it do not modify the underlying CMOS RTC. The x86_platform.set_wallclock hook is there to keep the hardware RTC synchronized. On a guest this is pointless. On Dom0 we can use the native implementaion which actually updates the hardware RTC, but we still need to keep the software emulation of RTC for the guests up to date. The subscription to the pvclock_notifier allows us to emulate this easily. The notifier is called at every tick and when the clock was set. Right now we only use that notifier when the clock was set, but due to the fact that it is called periodically from the timekeeping update code, we can utilize it to emulate the NTP driven drift compensation of update_persistant_clock() for the Xen wall (software) clock. Add a 11 minutes periodic update to the pvclock_gtod notifier callback to achieve that. The static variable 'next' which maintains that 11 minutes update cycle is protected by the core code serialization so there is no need to add a Xen specific serialization mechanism. [ tglx: Massaged changelog and added a few comments ] Signed-off-by: David Vrabel Cc: Konrad Rzeszutek Wilk Cc: John Stultz Cc: Link: http://lkml.kernel.org/r/1372329348-20841-6-git-send-email-david.vrabel@citrix.com Signed-off-by: Thomas Gleixner --- arch/x86/xen/time.c | 45 ++++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/arch/x86/xen/time.c b/arch/x86/xen/time.c index 3364850d23e6..7a5671b4fec6 100644 --- a/arch/x86/xen/time.c +++ b/arch/x86/xen/time.c @@ -199,37 +199,42 @@ static void xen_get_wallclock(struct timespec *now) static int xen_set_wallclock(const struct timespec *now) { - struct xen_platform_op op; - - /* do nothing for domU */ - if (!xen_initial_domain()) - return -1; - - op.cmd = XENPF_settime; - op.u.settime.secs = now->tv_sec; - op.u.settime.nsecs = now->tv_nsec; - op.u.settime.system_time = xen_clocksource_read(); - - return HYPERVISOR_dom0_op(&op); + return -1; } -static int xen_pvclock_gtod_notify(struct notifier_block *nb, unsigned long was_set, - void *priv) +static int xen_pvclock_gtod_notify(struct notifier_block *nb, + unsigned long was_set, void *priv) { - struct timespec now; - struct xen_platform_op op; + /* Protected by the calling core code serialization */ + static struct timespec next_sync; - if (!was_set) - return NOTIFY_OK; + struct xen_platform_op op; + struct timespec now; now = __current_kernel_time(); + /* + * We only take the expensive HV call when the clock was set + * or when the 11 minutes RTC synchronization time elapsed. + */ + if (!was_set && timespec_compare(&now, &next_sync) < 0) + return NOTIFY_OK; + op.cmd = XENPF_settime; op.u.settime.secs = now.tv_sec; op.u.settime.nsecs = now.tv_nsec; op.u.settime.system_time = xen_clocksource_read(); (void)HYPERVISOR_dom0_op(&op); + + /* + * Move the next drift compensation time 11 minutes + * ahead. That's emulating the sync_cmos_clock() update for + * the hardware RTC. + */ + next_sync = now; + next_sync.tv_sec += 11 * 60; + return NOTIFY_OK; } @@ -513,7 +518,9 @@ void __init xen_init_time_ops(void) x86_platform.calibrate_tsc = xen_tsc_khz; x86_platform.get_wallclock = xen_get_wallclock; - x86_platform.set_wallclock = xen_set_wallclock; + /* Dom0 uses the native method to set the hardware RTC. */ + if (!xen_initial_domain()) + x86_platform.set_wallclock = xen_set_wallclock; } #ifdef CONFIG_XEN_PVHVM -- cgit v1.2.3 From 8e48b1a8ed58595c40f2748c0f2da55b04da2dd6 Mon Sep 17 00:00:00 2001 From: Lenny Szubowicz Date: Fri, 28 Jun 2013 17:11:33 -0400 Subject: pstore: Return unique error if backend registration excluded by kernel param This is patch 1/3 of a patch set that avoids what misleadingly appears to be a error during boot: ERST: Could not register with persistent store This message is displayed if the system has a valid ACPI ERST table and the pstore.backend kernel parameter has been used to disable use of ERST by pstore. But this same message is used for errors that preclude registration. As part of fixing this, return a unique error status from pstore_register if the pstore.backend kernel parameter selects a specific facility other than the requesting facility and check for this condition before any others. This allows the caller to distinquish this benign case from the other failure cases. Also, print an informational console message about which facility successfully registered as the pstore backend. Since there are various kernel parameters, config build options, and boot-time errors that can influence which facility registers with pstore, it's useful to have a positive indication. Signed-off-by: Lenny Szubowicz Reported-by: Naotaka Hamaguchi Signed-off-by: Tony Luck --- fs/pstore/platform.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c index 86d1038b5a12..b7ffe2bcd9c4 100644 --- a/fs/pstore/platform.c +++ b/fs/pstore/platform.c @@ -239,17 +239,15 @@ int pstore_register(struct pstore_info *psi) { struct module *owner = psi->owner; + if (backend && strcmp(backend, psi->name)) + return -EPERM; + spin_lock(&pstore_lock); if (psinfo) { spin_unlock(&pstore_lock); return -EBUSY; } - if (backend && strcmp(backend, psi->name)) { - spin_unlock(&pstore_lock); - return -EINVAL; - } - if (!psi->write) psi->write = pstore_write_compat; psinfo = psi; @@ -274,6 +272,9 @@ int pstore_register(struct pstore_info *psi) add_timer(&pstore_timer); } + pr_info("pstore: Registered %s as persistent store backend\n", + psi->name); + return 0; } EXPORT_SYMBOL_GPL(pstore_register); -- cgit v1.2.3 From 74fd6c6f84b6d3e57bacb06161451c29949fbe51 Mon Sep 17 00:00:00 2001 From: Lenny Szubowicz Date: Fri, 28 Jun 2013 16:14:10 -0400 Subject: acpi: Eliminate console msg if pstore.backend excludes ERST This is patch 2/3 of a patch set that avoids what misleadingly appears to be a error during boot: ERST: Could not register with persistent store This message is displayed if the system has a valid ACPI ERST table and the pstore.backend kernel parameter has been used to disable use of ERST by pstore. But this same message is used for errors that preclude registration. In erst_init don't complain if the setting of kernel parameter pstore.backend precludes use of ACPI ERST for pstore. Routine pstore_register will inform about the facility that does register. Also, don't leave a dangling pointer to deallocated mem for the pstore buffer when registration fails. Signed-off-by: Lenny Szubowicz Reported-by: Naotaka Hamaguchi Signed-off-by: Tony Luck --- drivers/acpi/apei/erst.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c index 6d894bfd8b8f..f7b3b39e94fc 100644 --- a/drivers/acpi/apei/erst.c +++ b/drivers/acpi/apei/erst.c @@ -1180,20 +1180,28 @@ static int __init erst_init(void) if (!erst_erange.vaddr) goto err_release_erange; + pr_info(ERST_PFX + "Error Record Serialization Table (ERST) support is initialized.\n"); + buf = kmalloc(erst_erange.size, GFP_KERNEL); spin_lock_init(&erst_info.buf_lock); if (buf) { erst_info.buf = buf + sizeof(struct cper_pstore_record); erst_info.bufsize = erst_erange.size - sizeof(struct cper_pstore_record); - if (pstore_register(&erst_info)) { - pr_info(ERST_PFX "Could not register with persistent store\n"); + rc = pstore_register(&erst_info); + if (rc) { + if (rc != -EPERM) + pr_info(ERST_PFX + "Could not register with persistent store\n"); + erst_info.buf = NULL; + erst_info.bufsize = 0; kfree(buf); } - } - - pr_info(ERST_PFX - "Error Record Serialization Table (ERST) support is initialized.\n"); + } else + pr_err(ERST_PFX + "Failed to allocate %lld bytes for persistent store error log\n", + erst_erange.size); return 0; -- cgit v1.2.3 From 0d838347f1325cebfe8b9341a4b4c1f407022231 Mon Sep 17 00:00:00 2001 From: Lenny Szubowicz Date: Fri, 28 Jun 2013 16:14:11 -0400 Subject: efivars: If pstore_register fails, free unneeded pstore buffer This is patch 3/3 of a patch set that cleans up pstore_register failure paths. If efivars fails to register with pstore, there is no point to keeping the 4 KB buffer around. It's only used by the pstore read/write routines. Signed-off-by: Lenny Szubowicz Reported-by: Naotaka Hamaguchi Signed-off-by: Tony Luck --- drivers/firmware/efi/efi-pstore.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/efi/efi-pstore.c b/drivers/firmware/efi/efi-pstore.c index c692bb662178..91864ad200ff 100644 --- a/drivers/firmware/efi/efi-pstore.c +++ b/drivers/firmware/efi/efi-pstore.c @@ -235,7 +235,11 @@ static __init int efivars_pstore_init(void) efi_pstore_info.bufsize = 1024; spin_lock_init(&efi_pstore_info.buf_lock); - pstore_register(&efi_pstore_info); + if (pstore_register(&efi_pstore_info)) { + kfree(efi_pstore_info.buf); + efi_pstore_info.buf = NULL; + efi_pstore_info.bufsize = 0; + } return 0; } -- cgit v1.2.3 From 83e782e1a1cc0159888e58e14dfc8f3289663338 Mon Sep 17 00:00:00 2001 From: Chandra Seetharaman Date: Thu, 27 Jun 2013 17:25:10 -0500 Subject: xfs: Remove incore use of XFS_OQUOTA_ENFD and XFS_OQUOTA_CHKD Remove all incore use of XFS_OQUOTA_ENFD and XFS_OQUOTA_CHKD. Instead, start using XFS_GQUOTA_.* XFS_PQUOTA_.* counterparts for GQUOTA and PQUOTA respectively. On-disk copy still uses XFS_OQUOTA_ENFD and XFS_OQUOTA_CHKD. Read and write of the superblock does the conversion from *OQUOTA* to *[PG]QUOTA*. Signed-off-by: Chandra Seetharaman Reviewed-by: Ben Myers Signed-off-by: Ben Myers --- fs/xfs/xfs_mount.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/xfs_qm.c | 9 ++++++--- fs/xfs/xfs_qm_syscalls.c | 43 +++++++++++++++++++++------------------- fs/xfs/xfs_quota.h | 47 +++++++++++++++++++++++++++----------------- fs/xfs/xfs_quotaops.c | 6 ++++-- fs/xfs/xfs_super.c | 16 +++++++-------- fs/xfs/xfs_trans_dquot.c | 4 ++-- 7 files changed, 123 insertions(+), 53 deletions(-) diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index 2978bb4cc72a..2b0ba3581656 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -336,6 +336,14 @@ xfs_mount_validate_sb( return XFS_ERROR(EWRONGFS); } + if ((sbp->sb_qflags & (XFS_OQUOTA_ENFD | XFS_OQUOTA_CHKD)) && + (sbp->sb_qflags & (XFS_PQUOTA_ENFD | XFS_GQUOTA_ENFD | + XFS_PQUOTA_CHKD | XFS_GQUOTA_CHKD))) { + xfs_notice(mp, +"Super block has XFS_OQUOTA bits along with XFS_PQUOTA and/or XFS_GQUOTA bits.\n"); + return XFS_ERROR(EFSCORRUPTED); + } + /* * Version 5 superblock feature mask validation. Reject combinations the * kernel cannot support up front before checking anything else. For @@ -561,6 +569,18 @@ out_unwind: return error; } +static void +xfs_sb_quota_from_disk(struct xfs_sb *sbp) +{ + if (sbp->sb_qflags & XFS_OQUOTA_ENFD) + sbp->sb_qflags |= (sbp->sb_qflags & XFS_PQUOTA_ACCT) ? + XFS_PQUOTA_ENFD : XFS_GQUOTA_ENFD; + if (sbp->sb_qflags & XFS_OQUOTA_CHKD) + sbp->sb_qflags |= (sbp->sb_qflags & XFS_PQUOTA_ACCT) ? + XFS_PQUOTA_CHKD : XFS_GQUOTA_CHKD; + sbp->sb_qflags &= ~(XFS_OQUOTA_ENFD | XFS_OQUOTA_CHKD); +} + void xfs_sb_from_disk( struct xfs_sb *to, @@ -622,6 +642,35 @@ xfs_sb_from_disk( to->sb_lsn = be64_to_cpu(from->sb_lsn); } +static inline void +xfs_sb_quota_to_disk( + xfs_dsb_t *to, + xfs_sb_t *from, + __int64_t *fields) +{ + __uint16_t qflags = from->sb_qflags; + + if (*fields & XFS_SB_QFLAGS) { + /* + * The in-core version of sb_qflags do not have + * XFS_OQUOTA_* flags, whereas the on-disk version + * does. So, convert incore XFS_{PG}QUOTA_* flags + * to on-disk XFS_OQUOTA_* flags. + */ + qflags &= ~(XFS_PQUOTA_ENFD | XFS_PQUOTA_CHKD | + XFS_GQUOTA_ENFD | XFS_GQUOTA_CHKD); + + if (from->sb_qflags & + (XFS_PQUOTA_ENFD | XFS_GQUOTA_ENFD)) + qflags |= XFS_OQUOTA_ENFD; + if (from->sb_qflags & + (XFS_PQUOTA_CHKD | XFS_GQUOTA_CHKD)) + qflags |= XFS_OQUOTA_CHKD; + to->sb_qflags = cpu_to_be16(qflags); + *fields &= ~XFS_SB_QFLAGS; + } +} + /* * Copy in core superblock to ondisk one. * @@ -643,6 +692,7 @@ xfs_sb_to_disk( if (!fields) return; + xfs_sb_quota_to_disk(to, from, &fields); while (fields) { f = (xfs_sb_field_t)xfs_lowbit64((__uint64_t)fields); first = xfs_sb_info[f].offset; @@ -835,6 +885,7 @@ reread: */ xfs_sb_from_disk(&mp->m_sb, XFS_BUF_TO_SBP(bp)); + xfs_sb_quota_from_disk(&mp->m_sb); /* * We must be able to do sector-sized and sector-aligned IO. */ diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index cf09aa845d8c..7a3e007b49f4 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -299,8 +299,10 @@ xfs_qm_mount_quotas( */ if (!XFS_IS_UQUOTA_ON(mp)) mp->m_qflags &= ~XFS_UQUOTA_CHKD; - if (!(XFS_IS_GQUOTA_ON(mp) || XFS_IS_PQUOTA_ON(mp))) - mp->m_qflags &= ~XFS_OQUOTA_CHKD; + if (!XFS_IS_GQUOTA_ON(mp)) + mp->m_qflags &= ~XFS_GQUOTA_CHKD; + if (!XFS_IS_PQUOTA_ON(mp)) + mp->m_qflags &= ~XFS_PQUOTA_CHKD; write_changes: /* @@ -1297,7 +1299,8 @@ xfs_qm_quotacheck( &buffer_list); if (error) goto error_return; - flags |= XFS_OQUOTA_CHKD; + flags |= XFS_IS_GQUOTA_ON(mp) ? + XFS_GQUOTA_CHKD : XFS_PQUOTA_CHKD; } do { diff --git a/fs/xfs/xfs_qm_syscalls.c b/fs/xfs/xfs_qm_syscalls.c index b03b2abd1e13..a08801ae24e2 100644 --- a/fs/xfs/xfs_qm_syscalls.c +++ b/fs/xfs/xfs_qm_syscalls.c @@ -117,11 +117,11 @@ xfs_qm_scall_quotaoff( } if (flags & XFS_GQUOTA_ACCT) { dqtype |= XFS_QMOPT_GQUOTA; - flags |= (XFS_OQUOTA_CHKD | XFS_OQUOTA_ENFD); + flags |= (XFS_GQUOTA_CHKD | XFS_GQUOTA_ENFD); inactivate_flags |= XFS_GQUOTA_ACTIVE; } else if (flags & XFS_PQUOTA_ACCT) { dqtype |= XFS_QMOPT_PQUOTA; - flags |= (XFS_OQUOTA_CHKD | XFS_OQUOTA_ENFD); + flags |= (XFS_PQUOTA_CHKD | XFS_PQUOTA_ENFD); inactivate_flags |= XFS_PQUOTA_ACTIVE; } @@ -335,14 +335,14 @@ xfs_qm_scall_quotaon( * quota acct on ondisk without m_qflags' knowing. */ if (((flags & XFS_UQUOTA_ACCT) == 0 && - (mp->m_sb.sb_qflags & XFS_UQUOTA_ACCT) == 0 && - (flags & XFS_UQUOTA_ENFD)) - || + (mp->m_sb.sb_qflags & XFS_UQUOTA_ACCT) == 0 && + (flags & XFS_UQUOTA_ENFD)) || + ((flags & XFS_GQUOTA_ACCT) == 0 && + (mp->m_sb.sb_qflags & XFS_GQUOTA_ACCT) == 0 && + (flags & XFS_GQUOTA_ENFD)) || ((flags & XFS_PQUOTA_ACCT) == 0 && - (mp->m_sb.sb_qflags & XFS_PQUOTA_ACCT) == 0 && - (flags & XFS_GQUOTA_ACCT) == 0 && - (mp->m_sb.sb_qflags & XFS_GQUOTA_ACCT) == 0 && - (flags & XFS_OQUOTA_ENFD))) { + (mp->m_sb.sb_qflags & XFS_PQUOTA_ACCT) == 0 && + (flags & XFS_PQUOTA_ENFD))) { xfs_debug(mp, "%s: Can't enforce without acct, flags=%x sbflags=%x\n", __func__, flags, mp->m_sb.sb_qflags); @@ -776,9 +776,12 @@ xfs_qm_scall_getquota( * gets turned off. No need to confuse the user level code, * so return zeroes in that case. */ - if ((!XFS_IS_UQUOTA_ENFORCED(mp) && dqp->q_core.d_flags == XFS_DQ_USER) || - (!XFS_IS_OQUOTA_ENFORCED(mp) && - (dqp->q_core.d_flags & (XFS_DQ_PROJ | XFS_DQ_GROUP)))) { + if ((!XFS_IS_UQUOTA_ENFORCED(mp) && + dqp->q_core.d_flags == XFS_DQ_USER) || + (!XFS_IS_GQUOTA_ENFORCED(mp) && + dqp->q_core.d_flags == XFS_DQ_GROUP) || + (!XFS_IS_PQUOTA_ENFORCED(mp) && + dqp->q_core.d_flags == XFS_DQ_PROJ)) { dst->d_btimer = 0; dst->d_itimer = 0; dst->d_rtbtimer = 0; @@ -786,8 +789,8 @@ xfs_qm_scall_getquota( #ifdef DEBUG if (((XFS_IS_UQUOTA_ENFORCED(mp) && dst->d_flags == FS_USER_QUOTA) || - (XFS_IS_OQUOTA_ENFORCED(mp) && - (dst->d_flags & (FS_PROJ_QUOTA | FS_GROUP_QUOTA)))) && + (XFS_IS_GQUOTA_ENFORCED(mp) && dst->d_flags == FS_GROUP_QUOTA) || + (XFS_IS_PQUOTA_ENFORCED(mp) && dst->d_flags == FS_PROJ_QUOTA)) && dst->d_id != 0) { if ((dst->d_bcount > dst->d_blk_softlimit) && (dst->d_blk_softlimit > 0)) { @@ -833,16 +836,16 @@ xfs_qm_export_flags( uflags = 0; if (flags & XFS_UQUOTA_ACCT) uflags |= FS_QUOTA_UDQ_ACCT; - if (flags & XFS_PQUOTA_ACCT) - uflags |= FS_QUOTA_PDQ_ACCT; if (flags & XFS_GQUOTA_ACCT) uflags |= FS_QUOTA_GDQ_ACCT; + if (flags & XFS_PQUOTA_ACCT) + uflags |= FS_QUOTA_PDQ_ACCT; if (flags & XFS_UQUOTA_ENFD) uflags |= FS_QUOTA_UDQ_ENFD; - if (flags & (XFS_OQUOTA_ENFD)) { - uflags |= (flags & XFS_GQUOTA_ACCT) ? - FS_QUOTA_GDQ_ENFD : FS_QUOTA_PDQ_ENFD; - } + if (flags & XFS_GQUOTA_ENFD) + uflags |= FS_QUOTA_GDQ_ENFD; + if (flags & XFS_PQUOTA_ENFD) + uflags |= FS_QUOTA_PDQ_ENFD; return (uflags); } diff --git a/fs/xfs/xfs_quota.h b/fs/xfs/xfs_quota.h index c38068f26c55..c3483bab9cde 100644 --- a/fs/xfs/xfs_quota.h +++ b/fs/xfs/xfs_quota.h @@ -160,31 +160,43 @@ typedef struct xfs_qoff_logformat { #define XFS_OQUOTA_CHKD 0x0020 /* quotacheck run on other (grp/prj) quotas */ #define XFS_GQUOTA_ACCT 0x0040 /* group quota accounting ON */ +/* + * Conversion to and from the combined OQUOTA flag (if necessary) + * is done only in xfs_sb_qflags_to_disk() and xfs_sb_qflags_from_disk() + */ +#define XFS_GQUOTA_ENFD 0x0080 /* group quota limits enforced */ +#define XFS_GQUOTA_CHKD 0x0100 /* quotacheck run on group quotas */ +#define XFS_PQUOTA_ENFD 0x0200 /* project quota limits enforced */ +#define XFS_PQUOTA_CHKD 0x0400 /* quotacheck run on project quotas */ + /* * Quota Accounting/Enforcement flags */ #define XFS_ALL_QUOTA_ACCT \ (XFS_UQUOTA_ACCT | XFS_GQUOTA_ACCT | XFS_PQUOTA_ACCT) -#define XFS_ALL_QUOTA_ENFD (XFS_UQUOTA_ENFD | XFS_OQUOTA_ENFD) -#define XFS_ALL_QUOTA_CHKD (XFS_UQUOTA_CHKD | XFS_OQUOTA_CHKD) +#define XFS_ALL_QUOTA_ENFD \ + (XFS_UQUOTA_ENFD | XFS_GQUOTA_ENFD | XFS_PQUOTA_ENFD) +#define XFS_ALL_QUOTA_CHKD \ + (XFS_UQUOTA_CHKD | XFS_GQUOTA_CHKD | XFS_PQUOTA_CHKD) #define XFS_IS_QUOTA_RUNNING(mp) ((mp)->m_qflags & XFS_ALL_QUOTA_ACCT) #define XFS_IS_UQUOTA_RUNNING(mp) ((mp)->m_qflags & XFS_UQUOTA_ACCT) #define XFS_IS_PQUOTA_RUNNING(mp) ((mp)->m_qflags & XFS_PQUOTA_ACCT) #define XFS_IS_GQUOTA_RUNNING(mp) ((mp)->m_qflags & XFS_GQUOTA_ACCT) #define XFS_IS_UQUOTA_ENFORCED(mp) ((mp)->m_qflags & XFS_UQUOTA_ENFD) -#define XFS_IS_OQUOTA_ENFORCED(mp) ((mp)->m_qflags & XFS_OQUOTA_ENFD) +#define XFS_IS_GQUOTA_ENFORCED(mp) ((mp)->m_qflags & XFS_GQUOTA_ENFD) +#define XFS_IS_PQUOTA_ENFORCED(mp) ((mp)->m_qflags & XFS_PQUOTA_ENFD) /* * Incore only flags for quotaoff - these bits get cleared when quota(s) * are in the process of getting turned off. These flags are in m_qflags but * never in sb_qflags. */ -#define XFS_UQUOTA_ACTIVE 0x0100 /* uquotas are being turned off */ -#define XFS_PQUOTA_ACTIVE 0x0200 /* pquotas are being turned off */ -#define XFS_GQUOTA_ACTIVE 0x0400 /* gquotas are being turned off */ +#define XFS_UQUOTA_ACTIVE 0x1000 /* uquotas are being turned off */ +#define XFS_GQUOTA_ACTIVE 0x2000 /* gquotas are being turned off */ +#define XFS_PQUOTA_ACTIVE 0x4000 /* pquotas are being turned off */ #define XFS_ALL_QUOTA_ACTIVE \ - (XFS_UQUOTA_ACTIVE | XFS_PQUOTA_ACTIVE | XFS_GQUOTA_ACTIVE) + (XFS_UQUOTA_ACTIVE | XFS_GQUOTA_ACTIVE | XFS_PQUOTA_ACTIVE) /* * Checking XFS_IS_*QUOTA_ON() while holding any inode lock guarantees @@ -268,24 +280,23 @@ typedef struct xfs_qoff_logformat { ((XFS_IS_UQUOTA_ON(mp) && \ (mp->m_sb.sb_qflags & XFS_UQUOTA_CHKD) == 0) || \ (XFS_IS_GQUOTA_ON(mp) && \ - ((mp->m_sb.sb_qflags & XFS_OQUOTA_CHKD) == 0 || \ - (mp->m_sb.sb_qflags & XFS_PQUOTA_ACCT))) || \ + (mp->m_sb.sb_qflags & XFS_GQUOTA_CHKD) == 0) || \ (XFS_IS_PQUOTA_ON(mp) && \ - ((mp->m_sb.sb_qflags & XFS_OQUOTA_CHKD) == 0 || \ - (mp->m_sb.sb_qflags & XFS_GQUOTA_ACCT)))) + (mp->m_sb.sb_qflags & XFS_PQUOTA_CHKD) == 0)) #define XFS_MOUNT_QUOTA_SET1 (XFS_UQUOTA_ACCT|XFS_UQUOTA_ENFD|\ - XFS_UQUOTA_CHKD|XFS_PQUOTA_ACCT|\ - XFS_OQUOTA_ENFD|XFS_OQUOTA_CHKD) + XFS_UQUOTA_CHKD|XFS_GQUOTA_ACCT|\ + XFS_GQUOTA_ENFD|XFS_GQUOTA_CHKD) #define XFS_MOUNT_QUOTA_SET2 (XFS_UQUOTA_ACCT|XFS_UQUOTA_ENFD|\ - XFS_UQUOTA_CHKD|XFS_GQUOTA_ACCT|\ - XFS_OQUOTA_ENFD|XFS_OQUOTA_CHKD) + XFS_UQUOTA_CHKD|XFS_PQUOTA_ACCT|\ + XFS_PQUOTA_ENFD|XFS_PQUOTA_CHKD) #define XFS_MOUNT_QUOTA_ALL (XFS_UQUOTA_ACCT|XFS_UQUOTA_ENFD|\ - XFS_UQUOTA_CHKD|XFS_PQUOTA_ACCT|\ - XFS_OQUOTA_ENFD|XFS_OQUOTA_CHKD|\ - XFS_GQUOTA_ACCT) + XFS_UQUOTA_CHKD|XFS_GQUOTA_ACCT|\ + XFS_GQUOTA_ENFD|XFS_GQUOTA_CHKD|\ + XFS_PQUOTA_ACCT|XFS_PQUOTA_ENFD|\ + XFS_PQUOTA_CHKD) /* diff --git a/fs/xfs/xfs_quotaops.c b/fs/xfs/xfs_quotaops.c index 71926d630527..20e30f93b0c7 100644 --- a/fs/xfs/xfs_quotaops.c +++ b/fs/xfs/xfs_quotaops.c @@ -75,8 +75,10 @@ xfs_fs_set_xstate( flags |= XFS_GQUOTA_ACCT; if (uflags & FS_QUOTA_UDQ_ENFD) flags |= XFS_UQUOTA_ENFD; - if (uflags & (FS_QUOTA_PDQ_ENFD|FS_QUOTA_GDQ_ENFD)) - flags |= XFS_OQUOTA_ENFD; + if (uflags & FS_QUOTA_GDQ_ENFD) + flags |= XFS_GQUOTA_ENFD; + if (uflags & FS_QUOTA_PDQ_ENFD) + flags |= XFS_PQUOTA_ENFD; switch (op) { case Q_XQUOTAON: diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c index e5e8b5ee8bfa..1d68ffcdeaa7 100644 --- a/fs/xfs/xfs_super.c +++ b/fs/xfs/xfs_super.c @@ -360,17 +360,17 @@ xfs_parseargs( } else if (!strcmp(this_char, MNTOPT_PQUOTA) || !strcmp(this_char, MNTOPT_PRJQUOTA)) { mp->m_qflags |= (XFS_PQUOTA_ACCT | XFS_PQUOTA_ACTIVE | - XFS_OQUOTA_ENFD); + XFS_PQUOTA_ENFD); } else if (!strcmp(this_char, MNTOPT_PQUOTANOENF)) { mp->m_qflags |= (XFS_PQUOTA_ACCT | XFS_PQUOTA_ACTIVE); - mp->m_qflags &= ~XFS_OQUOTA_ENFD; + mp->m_qflags &= ~XFS_PQUOTA_ENFD; } else if (!strcmp(this_char, MNTOPT_GQUOTA) || !strcmp(this_char, MNTOPT_GRPQUOTA)) { mp->m_qflags |= (XFS_GQUOTA_ACCT | XFS_GQUOTA_ACTIVE | - XFS_OQUOTA_ENFD); + XFS_GQUOTA_ENFD); } else if (!strcmp(this_char, MNTOPT_GQUOTANOENF)) { mp->m_qflags |= (XFS_GQUOTA_ACCT | XFS_GQUOTA_ACTIVE); - mp->m_qflags &= ~XFS_OQUOTA_ENFD; + mp->m_qflags &= ~XFS_GQUOTA_ENFD; } else if (!strcmp(this_char, MNTOPT_DELAYLOG)) { xfs_warn(mp, "delaylog is the default now, option is deprecated."); @@ -559,12 +559,12 @@ xfs_showargs( /* Either project or group quotas can be active, not both */ if (mp->m_qflags & XFS_PQUOTA_ACCT) { - if (mp->m_qflags & XFS_OQUOTA_ENFD) + if (mp->m_qflags & XFS_PQUOTA_ENFD) seq_puts(m, "," MNTOPT_PRJQUOTA); else seq_puts(m, "," MNTOPT_PQUOTANOENF); } else if (mp->m_qflags & XFS_GQUOTA_ACCT) { - if (mp->m_qflags & XFS_OQUOTA_ENFD) + if (mp->m_qflags & XFS_GQUOTA_ENFD) seq_puts(m, "," MNTOPT_GRPQUOTA); else seq_puts(m, "," MNTOPT_GQUOTANOENF); @@ -1132,8 +1132,8 @@ xfs_fs_statfs( spin_unlock(&mp->m_sb_lock); if ((ip->i_d.di_flags & XFS_DIFLAG_PROJINHERIT) && - ((mp->m_qflags & (XFS_PQUOTA_ACCT|XFS_OQUOTA_ENFD))) == - (XFS_PQUOTA_ACCT|XFS_OQUOTA_ENFD)) + ((mp->m_qflags & (XFS_PQUOTA_ACCT|XFS_PQUOTA_ENFD))) == + (XFS_PQUOTA_ACCT|XFS_PQUOTA_ENFD)) xfs_qm_statvfs(ip, statp); return 0; } diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c index 7ea7485f9fb8..3ba64d540168 100644 --- a/fs/xfs/xfs_trans_dquot.c +++ b/fs/xfs/xfs_trans_dquot.c @@ -632,8 +632,8 @@ xfs_trans_dqresv( if ((flags & XFS_QMOPT_FORCE_RES) == 0 && dqp->q_core.d_id && ((XFS_IS_UQUOTA_ENFORCED(dqp->q_mount) && XFS_QM_ISUDQ(dqp)) || - (XFS_IS_OQUOTA_ENFORCED(dqp->q_mount) && - (XFS_QM_ISPDQ(dqp) || XFS_QM_ISGDQ(dqp))))) { + (XFS_IS_GQUOTA_ENFORCED(dqp->q_mount) && XFS_QM_ISGDQ(dqp)) || + (XFS_IS_PQUOTA_ENFORCED(dqp->q_mount) && XFS_QM_ISPDQ(dqp)))) { if (nblks > 0) { /* * dquot is locked already. See if we'd go over the -- cgit v1.2.3 From b50df95c8f0703c95625181d2eaf53855c5ebee5 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Fri, 28 Jun 2013 18:17:22 -0700 Subject: power_supply: Move of_node out of the #ifdef CONFIG_OF Similar to linux/device.h, move of_node struct member out of the #ifdef CONFIG_OF so that the drivers won't have to mess with #ifdefs in .c files. Signed-off-by: Anton Vorontsov --- include/linux/power_supply.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 3828cefb4f65..804b90643a85 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -162,6 +162,8 @@ union power_supply_propval { const char *strval; }; +struct device_node; + struct power_supply { const char *name; enum power_supply_type type; @@ -173,9 +175,7 @@ struct power_supply { char **supplied_from; size_t num_supplies; -#ifdef CONFIG_OF struct device_node *of_node; -#endif int (*get_property)(struct power_supply *psy, enum power_supply_property psp, -- cgit v1.2.3 From 2054d6e90e26b01ac0f125a42fecba63f226e55e Mon Sep 17 00:00:00 2001 From: Rhyland Klein Date: Mon, 10 Jun 2013 17:26:39 -0400 Subject: power_supply: Add of_node_put to fix refcount of_parse_phandle increments the refcount for a dt node before returning it. Add of_node_put where needed to properly decrement the refcount when we are done using a given node. Signed-off-by: Rhyland Klein Signed-off-by: Anton Vorontsov --- drivers/power/power_supply_core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index 1c517c34e4be..3b2d5df45e7a 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -109,8 +109,10 @@ static int __power_supply_populate_supplied_from(struct device *dev, psy->name, epsy->name); psy->supplied_from[i-1] = (char *)epsy->name; psy->num_supplies++; + of_node_put(np); break; } + of_node_put(np); } while (np); return 0; @@ -193,8 +195,10 @@ static int power_supply_check_supplies(struct power_supply *psy) ret = power_supply_find_supply_from_node(np); if (ret) { dev_dbg(psy->dev, "Failed to find supply, defer!\n"); + of_node_put(np); return -EPROBE_DEFER; } + of_node_put(np); } while (np); /* All supplies found, allocate char ** array for filling */ -- cgit v1.2.3 From 89a22b1a9850028bd216457397321db5a5ee18f5 Mon Sep 17 00:00:00 2001 From: Rhyland Klein Date: Mon, 10 Jun 2013 17:26:40 -0400 Subject: sbs-battery: Add dt to power_supply struct By passing in the dt node of this device, we enable the logic for linking power_supplies together from dt. This is specified by adding a "power-supplies" property with a phandle to the charger for a given supply. Enable this logic now for the sbs-battery driver. Signed-off-by: Rhyland Klein Signed-off-by: Anton Vorontsov --- drivers/power/sbs-battery.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/power/sbs-battery.c b/drivers/power/sbs-battery.c index c8c78a74e75a..b5f2a76b6cdf 100644 --- a/drivers/power/sbs-battery.c +++ b/drivers/power/sbs-battery.c @@ -704,6 +704,7 @@ static int sbs_probe(struct i2c_client *client, chip->power_supply.properties = sbs_properties; chip->power_supply.num_properties = ARRAY_SIZE(sbs_properties); chip->power_supply.get_property = sbs_get_property; + chip->power_supply.of_node = client->dev.of_node; /* ignore first notification of external change, it is generated * from the power_supply_register call back */ -- cgit v1.2.3 From 605860bc2495cd584fd639f00806591af0b1fefa Mon Sep 17 00:00:00 2001 From: Rhyland Klein Date: Mon, 10 Jun 2013 17:26:41 -0400 Subject: tps65090-charger: Add dt node to power_supply Passing in the dt node for this charger enables the logic in the core to lookup this device, to see if it is supplying another power_supply, through dt. Signed-off-by: Rhyland Klein Signed-off-by: Anton Vorontsov --- drivers/power/tps65090-charger.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/power/tps65090-charger.c b/drivers/power/tps65090-charger.c index e628f9813e08..bdd7b9b2546a 100644 --- a/drivers/power/tps65090-charger.c +++ b/drivers/power/tps65090-charger.c @@ -255,6 +255,7 @@ static int tps65090_charger_probe(struct platform_device *pdev) cdata->ac.num_properties = ARRAY_SIZE(tps65090_ac_props); cdata->ac.supplied_to = pdata->supplied_to; cdata->ac.num_supplicants = pdata->num_supplicants; + cdata->ac.of_node = pdev->dev.of_node; ret = power_supply_register(&pdev->dev, &cdata->ac); if (ret) { -- cgit v1.2.3 From 3cc9d26966ea02f71258ab0553ebd3388f40533a Mon Sep 17 00:00:00 2001 From: Jonghwa Lee Date: Tue, 25 Jun 2013 14:02:49 +0900 Subject: charger-manager: Fix a bug when it unregisters notifier block of extcon This patch prevents NULL pointer error cauesed by unregistering unregistered exton notifier block. At the probing time of charger manager, it tries to remove extcon notifier block when it fails to initialize them. It has to be applied for only registered one. Otherwise, it'd make kernel panic. To make it work right, it checks extcon_specific_cable_nb's extcon_dev node. If extcon cable notifier block was registered successfully, it has proper extcon_dev pointer if not so it has NULL pointer. Signed-off-by: Jonghwa Lee Signed-off-by: Myungjoo Ham Acked-by: Chanwoo Choi Signed-off-by: Anton Vorontsov --- drivers/power/charger-manager.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c index ba420292628e..8ad9698b55a9 100644 --- a/drivers/power/charger-manager.c +++ b/drivers/power/charger-manager.c @@ -1666,7 +1666,9 @@ err_reg_extcon: charger = &desc->charger_regulators[i]; for (j = 0; j < charger->num_cables; j++) { struct charger_cable *cable = &charger->cables[j]; - extcon_unregister_interest(&cable->extcon_dev); + /* Remove notifier block if only edev exists */ + if (cable->extcon_dev.edev) + extcon_unregister_interest(&cable->extcon_dev); } regulator_put(desc->charger_regulators[i].consumer); -- cgit v1.2.3 From 5a6c2208455f25b3e6f939adc2da59aa00d4806e Mon Sep 17 00:00:00 2001 From: Jonghwa Lee Date: Tue, 25 Jun 2013 14:18:14 +0900 Subject: charger-manager: Fix regulator_get() return check This patch fixes return value checking of regulator_get() in charger-manager driver. The API, regulator_get(), returns ERR_PTR() when it fails to get regulator with given name, not NULL. Signed-off-by: Jonghwa Lee Signed-off-by: Myungjoo Ham Acked-by: Chanwoo Choi Signed-off-by: Anton Vorontsov --- drivers/power/charger-manager.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c index 8ad9698b55a9..e30e847600bb 100644 --- a/drivers/power/charger-manager.c +++ b/drivers/power/charger-manager.c @@ -1239,11 +1239,10 @@ static int charger_manager_register_extcon(struct charger_manager *cm) charger->consumer = regulator_get(cm->dev, charger->regulator_name); - if (charger->consumer == NULL) { + if (IS_ERR(charger->consumer)) { dev_err(cm->dev, "Cannot find charger(%s)\n", charger->regulator_name); - ret = -EINVAL; - goto err; + return PTR_ERR(charger->consumer); } charger->cm = cm; -- cgit v1.2.3 From 1ec047eb4751e331bc61cff0e98f0db67db8b8dc Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Thu, 27 Jun 2013 00:06:56 +0200 Subject: ipv6: introduce per-interface counter for dad-completed ipv6 addresses To reduce the number of unnecessary router solicitations, MLDv2 and IGMPv3 messages we need to track the number of valid (as in non-optimistic, no-dad-failed and non-tentative) link-local addresses. Therefore, this patch implements a valid_ll_addr_cnt in struct inet6_dev. We now only emit router solicitations if the first link-local address finishes duplicate address detection. The changes for MLDv2 and IGMPv3 are in a follow-up patch. While there, also simplify one if statement(one minor nit I made in one of my previous patches): if (!...) do(); else return; <> if (...) return; do(); Cc: Flavio Leitner Cc: YOSHIFUJI Hideaki Cc: David Stevens Suggested-by: David Stevens Signed-off-by: Hannes Frederic Sowa Acked-by: Flavio Leitner Signed-off-by: David S. Miller --- include/net/if_inet6.h | 1 + net/ipv6/addrconf.c | 39 +++++++++++++++++++++++++++++++-------- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/include/net/if_inet6.h b/include/net/if_inet6.h index e4c5a2d2ba34..1628b8f5fb26 100644 --- a/include/net/if_inet6.h +++ b/include/net/if_inet6.h @@ -166,6 +166,7 @@ struct inet6_dev { struct net_device *dev; struct list_head addr_list; + int valid_ll_addr_cnt; struct ifmcaddr6 *mc_list; struct ifmcaddr6 *mc_tomb; diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 4e4cc1fc26d1..20d92ff2d690 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -3277,6 +3277,7 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp) { struct net_device *dev = ifp->idev->dev; struct in6_addr lladdr; + bool send_rs; addrconf_del_dad_timer(ifp); @@ -3290,20 +3291,25 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp) router advertisements, start sending router solicitations. */ - if (ipv6_accept_ra(ifp->idev) && - ifp->idev->cnf.rtr_solicits > 0 && - (dev->flags&IFF_LOOPBACK) == 0 && - (ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL)) { + read_lock_bh(&ifp->idev->lock); + spin_lock(&ifp->lock); + send_rs = ipv6_accept_ra(ifp->idev) && + ifp->idev->cnf.rtr_solicits > 0 && + (dev->flags&IFF_LOOPBACK) == 0 && + ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL && + ifp->idev->valid_ll_addr_cnt == 1; + spin_unlock(&ifp->lock); + read_unlock_bh(&ifp->idev->lock); + + if (send_rs) { /* * If a host as already performed a random delay * [...] as part of DAD [...] there is no need * to delay again before sending the first RS */ - if (!ipv6_get_lladdr(dev, &lladdr, IFA_F_TENTATIVE)) - ndisc_send_rs(dev, &lladdr, - &in6addr_linklocal_allrouters); - else + if (ipv6_get_lladdr(dev, &lladdr, IFA_F_TENTATIVE)) return; + ndisc_send_rs(dev, &lladdr, &in6addr_linklocal_allrouters); write_lock_bh(&ifp->idev->lock); spin_lock(&ifp->lock); @@ -4576,6 +4582,19 @@ errout: rtnl_set_sk_err(net, RTNLGRP_IPV6_PREFIX, err); } +static void update_valid_ll_addr_cnt(struct inet6_ifaddr *ifp, int count) +{ + write_lock_bh(&ifp->idev->lock); + spin_lock(&ifp->lock); + if (((ifp->flags & (IFA_F_PERMANENT|IFA_F_TENTATIVE|IFA_F_OPTIMISTIC| + IFA_F_DADFAILED)) == IFA_F_PERMANENT) && + (ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL)) + ifp->idev->valid_ll_addr_cnt += count; + WARN_ON(ifp->idev->valid_ll_addr_cnt < 0); + spin_unlock(&ifp->lock); + write_unlock_bh(&ifp->idev->lock); +} + static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp) { struct net *net = dev_net(ifp->idev->dev); @@ -4584,6 +4603,8 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp) switch (event) { case RTM_NEWADDR: + update_valid_ll_addr_cnt(ifp, 1); + /* * If the address was optimistic * we inserted the route at the start of @@ -4599,6 +4620,8 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp) ifp->idev->dev, 0, 0); break; case RTM_DELADDR: + update_valid_ll_addr_cnt(ifp, -1); + if (ifp->idev->cnf.forwarding) addrconf_leave_anycast(ifp); addrconf_leave_solict(ifp->idev, &ifp->addr); -- cgit v1.2.3 From b173ee488dcc545e77ed482158a2f0d06d7a5860 Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Thu, 27 Jun 2013 00:07:01 +0200 Subject: ipv6: resend MLD report if a link-local address completes DAD RFC3590/RFC3810 specifies we should resend MLD reports as soon as a valid link-local address is available. We now use the valid_ll_addr_cnt to check if it is necessary to resend a new report. Changes since Flavio Leitner's version: a) adapt for valid_ll_addr_cnt b) resend first reports directly in the path and just arm the timer for mc_qrv-1 resends. Reported-by: Flavio Leitner Cc: Hideaki YOSHIFUJI Cc: David Stevens Signed-off-by: Hannes Frederic Sowa Signed-off-by: Flavio Leitner Signed-off-by: David S. Miller --- include/net/addrconf.h | 1 + include/net/if_inet6.h | 2 ++ net/ipv6/addrconf.c | 17 ++++++++++++----- net/ipv6/mcast.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 67 insertions(+), 5 deletions(-) diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 21f702704f24..f68eaf574d7e 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -155,6 +155,7 @@ extern bool ipv6_chk_mcast_addr(struct net_device *dev, const struct in6_addr *group, const struct in6_addr *src_addr); +extern void ipv6_mc_dad_complete(struct inet6_dev *idev); /* * identify MLD packets for MLD filter exceptions */ diff --git a/include/net/if_inet6.h b/include/net/if_inet6.h index 1628b8f5fb26..736b5fb95474 100644 --- a/include/net/if_inet6.h +++ b/include/net/if_inet6.h @@ -174,10 +174,12 @@ struct inet6_dev { unsigned char mc_qrv; unsigned char mc_gq_running; unsigned char mc_ifc_count; + unsigned char mc_dad_count; unsigned long mc_v1_seen; unsigned long mc_maxdelay; struct timer_list mc_gq_timer; /* general query timer */ struct timer_list mc_ifc_timer; /* interface change timer */ + struct timer_list mc_dad_timer; /* dad complete mc timer */ struct ifacaddr6 *ac_list; rwlock_t lock; diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 20d92ff2d690..12dd2fec045c 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -3277,7 +3277,7 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp) { struct net_device *dev = ifp->idev->dev; struct in6_addr lladdr; - bool send_rs; + bool send_rs, send_mld; addrconf_del_dad_timer(ifp); @@ -3293,14 +3293,21 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp) read_lock_bh(&ifp->idev->lock); spin_lock(&ifp->lock); - send_rs = ipv6_accept_ra(ifp->idev) && + send_mld = ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL && + ifp->idev->valid_ll_addr_cnt == 1; + send_rs = send_mld && + ipv6_accept_ra(ifp->idev) && ifp->idev->cnf.rtr_solicits > 0 && - (dev->flags&IFF_LOOPBACK) == 0 && - ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL && - ifp->idev->valid_ll_addr_cnt == 1; + (dev->flags&IFF_LOOPBACK) == 0; spin_unlock(&ifp->lock); read_unlock_bh(&ifp->idev->lock); + /* While dad is in progress mld report's source address is in6_addrany. + * Resend with proper ll now. + */ + if (send_mld) + ipv6_mc_dad_complete(ifp->idev); + if (send_rs) { /* * If a host as already performed a random delay diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 72c8bfe06bb4..502c877cbf10 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -999,6 +999,14 @@ static void mld_ifc_start_timer(struct inet6_dev *idev, int delay) in6_dev_hold(idev); } +static void mld_dad_start_timer(struct inet6_dev *idev, int delay) +{ + int tv = net_random() % delay; + + if (!mod_timer(&idev->mc_dad_timer, jiffies+tv+2)) + in6_dev_hold(idev); +} + /* * IGMP handling (alias multicast ICMPv6 messages) */ @@ -1815,6 +1823,46 @@ err_out: goto out; } +static void mld_resend_report(struct inet6_dev *idev) +{ + if (MLD_V1_SEEN(idev)) { + struct ifmcaddr6 *mcaddr; + read_lock_bh(&idev->lock); + for (mcaddr = idev->mc_list; mcaddr; mcaddr = mcaddr->next) { + if (!(mcaddr->mca_flags & MAF_NOREPORT)) + igmp6_send(&mcaddr->mca_addr, idev->dev, + ICMPV6_MGM_REPORT); + } + read_unlock_bh(&idev->lock); + } else { + mld_send_report(idev, NULL); + } +} + +void ipv6_mc_dad_complete(struct inet6_dev *idev) +{ + idev->mc_dad_count = idev->mc_qrv; + if (idev->mc_dad_count) { + mld_resend_report(idev); + idev->mc_dad_count--; + if (idev->mc_dad_count) + mld_dad_start_timer(idev, idev->mc_maxdelay); + } +} + +static void mld_dad_timer_expire(unsigned long data) +{ + struct inet6_dev *idev = (struct inet6_dev *)data; + + mld_resend_report(idev); + if (idev->mc_dad_count) { + idev->mc_dad_count--; + if (idev->mc_dad_count) + mld_dad_start_timer(idev, idev->mc_maxdelay); + } + __in6_dev_put(idev); +} + static int ip6_mc_del1_src(struct ifmcaddr6 *pmc, int sfmode, const struct in6_addr *psfsrc) { @@ -2232,6 +2280,8 @@ void ipv6_mc_down(struct inet6_dev *idev) idev->mc_gq_running = 0; if (del_timer(&idev->mc_gq_timer)) __in6_dev_put(idev); + if (del_timer(&idev->mc_dad_timer)) + __in6_dev_put(idev); for (i = idev->mc_list; i; i=i->next) igmp6_group_dropped(i); @@ -2268,6 +2318,8 @@ void ipv6_mc_init_dev(struct inet6_dev *idev) idev->mc_ifc_count = 0; setup_timer(&idev->mc_ifc_timer, mld_ifc_timer_expire, (unsigned long)idev); + setup_timer(&idev->mc_dad_timer, mld_dad_timer_expire, + (unsigned long)idev); idev->mc_qrv = MLD_QRV_DEFAULT; idev->mc_maxdelay = IGMP6_UNSOLICITED_IVAL; idev->mc_v1_seen = 0; -- cgit v1.2.3 From 2ffae99d1fac272952b5a395759823717760ce37 Mon Sep 17 00:00:00 2001 From: Timo Teräs Date: Thu, 27 Jun 2013 10:27:05 +0300 Subject: ipv4: use next hop exceptions also for input routes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit d2d68ba9 (ipv4: Cache input routes in fib_info nexthops) assmued that "locally destined, and routed packets, never trigger PMTU events or redirects that will be processed by us". However, it seems that tunnel devices do trigger PMTU events in certain cases. At least ip_gre, ip6_gre, sit, and ipip do use the inner flow's skb_dst(skb)->ops->update_pmtu to propage mtu information from the outer flows. These can cause the inner flow mtu to be decreased. If next hop exceptions are not consulted for pmtu, IP fragmentation will not be done properly for these routes. It also seems that we really need to have the PMTU information always for netfilter TCPMSS clamp-to-pmtu feature to work properly. So for the time being, cache separate copies of input routes for each next hop exception. Signed-off-by: Timo Teräs Reviewed-by: Julian Anastasov Signed-off-by: David S. Miller --- include/net/ip_fib.h | 3 ++- net/ipv4/fib_semantics.c | 3 ++- net/ipv4/route.c | 65 +++++++++++++++++++++++++++++++++++++----------- 3 files changed, 54 insertions(+), 17 deletions(-) diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index 44424e9dab2a..aac85534d7b5 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -56,7 +56,8 @@ struct fib_nh_exception { u32 fnhe_pmtu; __be32 fnhe_gw; unsigned long fnhe_expires; - struct rtable __rcu *fnhe_rth; + struct rtable __rcu *fnhe_rth_input; + struct rtable __rcu *fnhe_rth_output; unsigned long fnhe_stamp; }; diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 8f6cb7a87cd6..d5dbca5ecf62 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -169,7 +169,8 @@ static void free_nh_exceptions(struct fib_nh *nh) next = rcu_dereference_protected(fnhe->fnhe_next, 1); - rt_fibinfo_free(&fnhe->fnhe_rth); + rt_fibinfo_free(&fnhe->fnhe_rth_input); + rt_fibinfo_free(&fnhe->fnhe_rth_output); kfree(fnhe); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index f3fa42eac461..a9a54a236832 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -565,10 +565,25 @@ static inline void rt_free(struct rtable *rt) static DEFINE_SPINLOCK(fnhe_lock); +static void fnhe_flush_routes(struct fib_nh_exception *fnhe) +{ + struct rtable *rt; + + rt = rcu_dereference(fnhe->fnhe_rth_input); + if (rt) { + RCU_INIT_POINTER(fnhe->fnhe_rth_input, NULL); + rt_free(rt); + } + rt = rcu_dereference(fnhe->fnhe_rth_output); + if (rt) { + RCU_INIT_POINTER(fnhe->fnhe_rth_output, NULL); + rt_free(rt); + } +} + static struct fib_nh_exception *fnhe_oldest(struct fnhe_hash_bucket *hash) { struct fib_nh_exception *fnhe, *oldest; - struct rtable *orig; oldest = rcu_dereference(hash->chain); for (fnhe = rcu_dereference(oldest->fnhe_next); fnhe; @@ -576,11 +591,7 @@ static struct fib_nh_exception *fnhe_oldest(struct fnhe_hash_bucket *hash) if (time_before(fnhe->fnhe_stamp, oldest->fnhe_stamp)) oldest = fnhe; } - orig = rcu_dereference(oldest->fnhe_rth); - if (orig) { - RCU_INIT_POINTER(oldest->fnhe_rth, NULL); - rt_free(orig); - } + fnhe_flush_routes(oldest); return oldest; } @@ -644,7 +655,10 @@ static void update_or_create_fnhe(struct fib_nh *nh, __be32 daddr, __be32 gw, fnhe->fnhe_expires = max(1UL, expires); } /* Update all cached dsts too */ - rt = rcu_dereference(fnhe->fnhe_rth); + rt = rcu_dereference(fnhe->fnhe_rth_input); + if (rt) + fill_route_from_fnhe(rt, fnhe); + rt = rcu_dereference(fnhe->fnhe_rth_output); if (rt) fill_route_from_fnhe(rt, fnhe); } else { @@ -668,6 +682,10 @@ static void update_or_create_fnhe(struct fib_nh *nh, __be32 daddr, __be32 gw, * stale, so anyone caching it rechecks if this exception * applies to them. */ + rt = rcu_dereference(nh->nh_rth_input); + if (rt) + rt->dst.obsolete = DST_OBSOLETE_KILL; + for_each_possible_cpu(i) { struct rtable __rcu **prt; prt = per_cpu_ptr(nh->nh_pcpu_rth_output, i); @@ -1242,25 +1260,36 @@ static bool rt_bind_exception(struct rtable *rt, struct fib_nh_exception *fnhe, spin_lock_bh(&fnhe_lock); if (daddr == fnhe->fnhe_daddr) { + struct rtable __rcu **porig; + struct rtable *orig; int genid = fnhe_genid(dev_net(rt->dst.dev)); - struct rtable *orig = rcu_dereference(fnhe->fnhe_rth); + + if (rt_is_input_route(rt)) + porig = &fnhe->fnhe_rth_input; + else + porig = &fnhe->fnhe_rth_output; + orig = rcu_dereference(*porig); if (fnhe->fnhe_genid != genid) { fnhe->fnhe_genid = genid; fnhe->fnhe_gw = 0; fnhe->fnhe_pmtu = 0; fnhe->fnhe_expires = 0; + fnhe_flush_routes(fnhe); + orig = NULL; } fill_route_from_fnhe(rt, fnhe); if (!rt->rt_gateway) rt->rt_gateway = daddr; - rcu_assign_pointer(fnhe->fnhe_rth, rt); - if (orig) - rt_free(orig); + if (!(rt->dst.flags & DST_NOCACHE)) { + rcu_assign_pointer(*porig, rt); + if (orig) + rt_free(orig); + ret = true; + } fnhe->fnhe_stamp = jiffies; - ret = true; } spin_unlock_bh(&fnhe_lock); @@ -1492,6 +1521,7 @@ static int __mkroute_input(struct sk_buff *skb, struct in_device *in_dev, __be32 daddr, __be32 saddr, u32 tos) { + struct fib_nh_exception *fnhe; struct rtable *rth; int err; struct in_device *out_dev; @@ -1538,8 +1568,13 @@ static int __mkroute_input(struct sk_buff *skb, } } + fnhe = find_exception(&FIB_RES_NH(*res), daddr); if (do_cache) { - rth = rcu_dereference(FIB_RES_NH(*res).nh_rth_input); + if (fnhe != NULL) + rth = rcu_dereference(fnhe->fnhe_rth_input); + else + rth = rcu_dereference(FIB_RES_NH(*res).nh_rth_input); + if (rt_cache_valid(rth)) { skb_dst_set_noref(skb, &rth->dst); goto out; @@ -1567,7 +1602,7 @@ static int __mkroute_input(struct sk_buff *skb, rth->dst.input = ip_forward; rth->dst.output = ip_output; - rt_set_nexthop(rth, daddr, res, NULL, res->fi, res->type, itag); + rt_set_nexthop(rth, daddr, res, fnhe, res->fi, res->type, itag); skb_dst_set(skb, &rth->dst); out: err = 0; @@ -1882,7 +1917,7 @@ static struct rtable *__mkroute_output(const struct fib_result *res, fnhe = find_exception(nh, fl4->daddr); if (fnhe) - prth = &fnhe->fnhe_rth; + prth = &fnhe->fnhe_rth_output; else { if (unlikely(fl4->flowi4_flags & FLOWI_FLAG_KNOWN_NH && -- cgit v1.2.3 From 803dee958390bd3e9e42a31dd8bdc4d1a8fa319f Mon Sep 17 00:00:00 2001 From: David Chang Date: Thu, 27 Jun 2013 17:16:42 +0800 Subject: usbnet: ax88179_178a: Correct a typo in description Correct a typo in description of driver_info, it should be Gigabit Signed-off-by: David Chang Signed-off-by: David S. Miller --- drivers/net/usb/ax88179_178a.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c index bd8758fa38c1..7ce243bf7088 100644 --- a/drivers/net/usb/ax88179_178a.c +++ b/drivers/net/usb/ax88179_178a.c @@ -1371,7 +1371,7 @@ static int ax88179_stop(struct usbnet *dev) } static const struct driver_info ax88179_info = { - .description = "ASIX AX88179 USB 3.0 Gigibit Ethernet", + .description = "ASIX AX88179 USB 3.0 Gigabit Ethernet", .bind = ax88179_bind, .unbind = ax88179_unbind, .status = ax88179_status, @@ -1384,7 +1384,7 @@ static const struct driver_info ax88179_info = { }; static const struct driver_info ax88178a_info = { - .description = "ASIX AX88178A USB 2.0 Gigibit Ethernet", + .description = "ASIX AX88178A USB 2.0 Gigabit Ethernet", .bind = ax88179_bind, .unbind = ax88179_unbind, .status = ax88179_status, -- cgit v1.2.3 From b8553b89edf73122eba4419cc846a31ebf7bc894 Mon Sep 17 00:00:00 2001 From: David Chang Date: Thu, 27 Jun 2013 17:16:43 +0800 Subject: usbnet: ax88179_178a: add .reset_resume hook I tested with the AX88179 usb dongle, if without .reset_resume hook, after S3/S4 resume you have to enable network interface or reload the dirver module manually otherwise the network interface can not work. Signed-off-by: David Chang Signed-off-by: David S. Miller --- drivers/net/usb/ax88179_178a.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c index 7ce243bf7088..1e3c302d94fe 100644 --- a/drivers/net/usb/ax88179_178a.c +++ b/drivers/net/usb/ax88179_178a.c @@ -1433,6 +1433,7 @@ static struct usb_driver ax88179_178a_driver = { .probe = usbnet_probe, .suspend = ax88179_suspend, .resume = ax88179_resume, + .reset_resume = ax88179_resume, .disconnect = usbnet_disconnect, .supports_autosuspend = 1, .disable_hub_initiated_lpm = 1, -- cgit v1.2.3 From bd79680956573dd70bfa207af6b143aa94980b96 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 28 Jun 2013 14:02:52 +0300 Subject: pch_gbe: remove inline keyword for exported functions There is no much sense to mark functions inline that are going to be used in the other compile modules. Signed-off-by: Andy Shevchenko Signed-off-by: David S. Miller --- drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_api.c | 21 +++++++++++---------- .../net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c | 2 +- drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_phy.c | 3 +-- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_api.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_api.c index 5ae03e815ee9..82e72b21a8fb 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_api.c +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_api.c @@ -19,6 +19,7 @@ */ #include "pch_gbe.h" #include "pch_gbe_phy.h" +#include "pch_gbe_api.h" /* bus type values */ #define pch_gbe_bus_type_unknown 0 @@ -112,7 +113,7 @@ static void pch_gbe_plat_init_function_pointers(struct pch_gbe_hw *hw) * 0: Successfully * ENOSYS: Function is not registered */ -inline s32 pch_gbe_hal_setup_init_funcs(struct pch_gbe_hw *hw) +s32 pch_gbe_hal_setup_init_funcs(struct pch_gbe_hw *hw) { if (!hw->reg) { pr_err("ERROR: Registers not mapped\n"); @@ -126,7 +127,7 @@ inline s32 pch_gbe_hal_setup_init_funcs(struct pch_gbe_hw *hw) * pch_gbe_hal_get_bus_info - Obtain bus information for adapter * @hw: Pointer to the HW structure */ -inline void pch_gbe_hal_get_bus_info(struct pch_gbe_hw *hw) +void pch_gbe_hal_get_bus_info(struct pch_gbe_hw *hw) { if (!hw->func->get_bus_info) pr_err("ERROR: configuration\n"); @@ -141,7 +142,7 @@ inline void pch_gbe_hal_get_bus_info(struct pch_gbe_hw *hw) * 0: Successfully * ENOSYS: Function is not registered */ -inline s32 pch_gbe_hal_init_hw(struct pch_gbe_hw *hw) +s32 pch_gbe_hal_init_hw(struct pch_gbe_hw *hw) { if (!hw->func->init_hw) { pr_err("ERROR: configuration\n"); @@ -159,7 +160,7 @@ inline s32 pch_gbe_hal_init_hw(struct pch_gbe_hw *hw) * 0: Successfully * Negative value: Failed */ -inline s32 pch_gbe_hal_read_phy_reg(struct pch_gbe_hw *hw, u32 offset, +s32 pch_gbe_hal_read_phy_reg(struct pch_gbe_hw *hw, u32 offset, u16 *data) { if (!hw->func->read_phy_reg) @@ -176,7 +177,7 @@ inline s32 pch_gbe_hal_read_phy_reg(struct pch_gbe_hw *hw, u32 offset, * 0: Successfully * Negative value: Failed */ -inline s32 pch_gbe_hal_write_phy_reg(struct pch_gbe_hw *hw, u32 offset, +s32 pch_gbe_hal_write_phy_reg(struct pch_gbe_hw *hw, u32 offset, u16 data) { if (!hw->func->write_phy_reg) @@ -188,7 +189,7 @@ inline s32 pch_gbe_hal_write_phy_reg(struct pch_gbe_hw *hw, u32 offset, * pch_gbe_hal_phy_hw_reset - Hard PHY reset * @hw: Pointer to the HW structure */ -inline void pch_gbe_hal_phy_hw_reset(struct pch_gbe_hw *hw) +void pch_gbe_hal_phy_hw_reset(struct pch_gbe_hw *hw) { if (!hw->func->reset_phy) pr_err("ERROR: configuration\n"); @@ -200,7 +201,7 @@ inline void pch_gbe_hal_phy_hw_reset(struct pch_gbe_hw *hw) * pch_gbe_hal_phy_sw_reset - Soft PHY reset * @hw: Pointer to the HW structure */ -inline void pch_gbe_hal_phy_sw_reset(struct pch_gbe_hw *hw) +void pch_gbe_hal_phy_sw_reset(struct pch_gbe_hw *hw) { if (!hw->func->sw_reset_phy) pr_err("ERROR: configuration\n"); @@ -215,7 +216,7 @@ inline void pch_gbe_hal_phy_sw_reset(struct pch_gbe_hw *hw) * 0: Successfully * ENOSYS: Function is not registered */ -inline s32 pch_gbe_hal_read_mac_addr(struct pch_gbe_hw *hw) +s32 pch_gbe_hal_read_mac_addr(struct pch_gbe_hw *hw) { if (!hw->func->read_mac_addr) { pr_err("ERROR: configuration\n"); @@ -228,7 +229,7 @@ inline s32 pch_gbe_hal_read_mac_addr(struct pch_gbe_hw *hw) * pch_gbe_hal_power_up_phy - Power up PHY * @hw: Pointer to the HW structure */ -inline void pch_gbe_hal_power_up_phy(struct pch_gbe_hw *hw) +void pch_gbe_hal_power_up_phy(struct pch_gbe_hw *hw) { if (hw->func->power_up_phy) hw->func->power_up_phy(hw); @@ -238,7 +239,7 @@ inline void pch_gbe_hal_power_up_phy(struct pch_gbe_hw *hw) * pch_gbe_hal_power_down_phy - Power down PHY * @hw: Pointer to the HW structure */ -inline void pch_gbe_hal_power_down_phy(struct pch_gbe_hw *hw) +void pch_gbe_hal_power_down_phy(struct pch_gbe_hw *hw) { if (hw->func->power_down_phy) hw->func->power_down_phy(hw); diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c index 0c1c65a9ce5e..16d5ffac1981 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c @@ -287,7 +287,7 @@ static int hwtstamp_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0; } -inline void pch_gbe_mac_load_mac_addr(struct pch_gbe_hw *hw) +static inline void pch_gbe_mac_load_mac_addr(struct pch_gbe_hw *hw) { iowrite32(0x01, &hw->reg->MAC_ADDR_LOAD); } diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_phy.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_phy.c index 28bb9603d736..b97c6575e018 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_phy.c +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_phy.c @@ -235,7 +235,7 @@ void pch_gbe_phy_power_down(struct pch_gbe_hw *hw) * pch_gbe_phy_set_rgmii - RGMII interface setting * @hw: Pointer to the HW structure */ -inline void pch_gbe_phy_set_rgmii(struct pch_gbe_hw *hw) +void pch_gbe_phy_set_rgmii(struct pch_gbe_hw *hw) { pch_gbe_phy_sw_reset(hw); } @@ -270,5 +270,4 @@ void pch_gbe_phy_init_setting(struct pch_gbe_hw *hw) pch_gbe_phy_read_reg_miic(hw, PHY_PHYSP_CONTROL, &mii_reg); mii_reg |= PHYSP_CTRL_ASSERT_CRS_TX; pch_gbe_phy_write_reg_miic(hw, PHY_PHYSP_CONTROL, mii_reg); - } -- cgit v1.2.3 From 453ca931f515161902dbb325d7f39a992c3059ce Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 28 Jun 2013 14:02:53 +0300 Subject: pch_gbe: convert pr_* to netdev_* We may use nice macros to prefix our messages with proper device name. Signed-off-by: Andy Shevchenko Signed-off-by: David S. Miller --- drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe.h | 2 + .../net/ethernet/oki-semi/pch_gbe/pch_gbe_api.c | 49 ++-- .../ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c | 2 +- .../net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c | 275 ++++++++++++--------- .../net/ethernet/oki-semi/pch_gbe/pch_gbe_param.c | 63 +++-- .../net/ethernet/oki-semi/pch_gbe/pch_gbe_phy.c | 23 +- 6 files changed, 250 insertions(+), 164 deletions(-) diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe.h b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe.h index 7fb7e178c74e..7779036690cc 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe.h +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe.h @@ -633,6 +633,8 @@ struct pch_gbe_adapter { struct pci_dev *ptp_pdev; }; +#define pch_gbe_hw_to_adapter(hw) container_of(hw, struct pch_gbe_adapter, hw) + extern const char pch_driver_version[]; /* pch_gbe_main.c */ diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_api.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_api.c index 82e72b21a8fb..ff3ad70935a6 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_api.c +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_api.c @@ -71,7 +71,9 @@ static s32 pch_gbe_plat_init_hw(struct pch_gbe_hw *hw) ret_val = pch_gbe_phy_get_id(hw); if (ret_val) { - pr_err("pch_gbe_phy_get_id error\n"); + struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); + + netdev_err(adapter->netdev, "pch_gbe_phy_get_id error\n"); return ret_val; } pch_gbe_phy_init_setting(hw); @@ -116,7 +118,9 @@ static void pch_gbe_plat_init_function_pointers(struct pch_gbe_hw *hw) s32 pch_gbe_hal_setup_init_funcs(struct pch_gbe_hw *hw) { if (!hw->reg) { - pr_err("ERROR: Registers not mapped\n"); + struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); + + netdev_err(adapter->netdev, "ERROR: Registers not mapped\n"); return -ENOSYS; } pch_gbe_plat_init_function_pointers(hw); @@ -129,10 +133,13 @@ s32 pch_gbe_hal_setup_init_funcs(struct pch_gbe_hw *hw) */ void pch_gbe_hal_get_bus_info(struct pch_gbe_hw *hw) { - if (!hw->func->get_bus_info) - pr_err("ERROR: configuration\n"); - else - hw->func->get_bus_info(hw); + if (!hw->func->get_bus_info) { + struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); + + netdev_err(adapter->netdev, "ERROR: configuration\n"); + return; + } + hw->func->get_bus_info(hw); } /** @@ -145,7 +152,9 @@ void pch_gbe_hal_get_bus_info(struct pch_gbe_hw *hw) s32 pch_gbe_hal_init_hw(struct pch_gbe_hw *hw) { if (!hw->func->init_hw) { - pr_err("ERROR: configuration\n"); + struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); + + netdev_err(adapter->netdev, "ERROR: configuration\n"); return -ENOSYS; } return hw->func->init_hw(hw); @@ -191,10 +200,13 @@ s32 pch_gbe_hal_write_phy_reg(struct pch_gbe_hw *hw, u32 offset, */ void pch_gbe_hal_phy_hw_reset(struct pch_gbe_hw *hw) { - if (!hw->func->reset_phy) - pr_err("ERROR: configuration\n"); - else - hw->func->reset_phy(hw); + if (!hw->func->reset_phy) { + struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); + + netdev_err(adapter->netdev, "ERROR: configuration\n"); + return; + } + hw->func->reset_phy(hw); } /** @@ -203,10 +215,13 @@ void pch_gbe_hal_phy_hw_reset(struct pch_gbe_hw *hw) */ void pch_gbe_hal_phy_sw_reset(struct pch_gbe_hw *hw) { - if (!hw->func->sw_reset_phy) - pr_err("ERROR: configuration\n"); - else - hw->func->sw_reset_phy(hw); + if (!hw->func->sw_reset_phy) { + struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); + + netdev_err(adapter->netdev, "ERROR: configuration\n"); + return; + } + hw->func->sw_reset_phy(hw); } /** @@ -219,7 +234,9 @@ void pch_gbe_hal_phy_sw_reset(struct pch_gbe_hw *hw) s32 pch_gbe_hal_read_mac_addr(struct pch_gbe_hw *hw) { if (!hw->func->read_mac_addr) { - pr_err("ERROR: configuration\n"); + struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); + + netdev_err(adapter->netdev, "ERROR: configuration\n"); return -ENOSYS; } return hw->func->read_mac_addr(hw); diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c index 24b787be6062..1129db0cdf82 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c @@ -122,7 +122,7 @@ static int pch_gbe_set_settings(struct net_device *netdev, } ret = mii_ethtool_sset(&adapter->mii, ecmd); if (ret) { - pr_err("Error: mii_ethtool_sset\n"); + netdev_err(netdev, "Error: mii_ethtool_sset\n"); return ret; } hw->mac.link_speed = speed; diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c index 16d5ffac1981..97db56393197 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c @@ -300,6 +300,7 @@ static inline void pch_gbe_mac_load_mac_addr(struct pch_gbe_hw *hw) */ s32 pch_gbe_mac_read_mac_addr(struct pch_gbe_hw *hw) { + struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); u32 adr1a, adr1b; adr1a = ioread32(&hw->reg->mac_adr[0].high); @@ -312,7 +313,7 @@ s32 pch_gbe_mac_read_mac_addr(struct pch_gbe_hw *hw) hw->mac.addr[4] = (u8)(adr1b & 0xFF); hw->mac.addr[5] = (u8)((adr1b >> 8) & 0xFF); - pr_debug("hw->mac.addr : %pM\n", hw->mac.addr); + netdev_dbg(adapter->netdev, "hw->mac.addr : %pM\n", hw->mac.addr); return 0; } @@ -324,6 +325,7 @@ s32 pch_gbe_mac_read_mac_addr(struct pch_gbe_hw *hw) static void pch_gbe_wait_clr_bit(void *reg, u32 bit) { u32 tmp; + /* wait busy */ tmp = 1000; while ((ioread32(reg) & bit) && --tmp) @@ -340,9 +342,10 @@ static void pch_gbe_wait_clr_bit(void *reg, u32 bit) */ static void pch_gbe_mac_mar_set(struct pch_gbe_hw *hw, u8 * addr, u32 index) { + struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); u32 mar_low, mar_high, adrmask; - pr_debug("index : 0x%x\n", index); + netdev_dbg(adapter->netdev, "index : 0x%x\n", index); /* * HW expects these in little endian so we reverse the byte order @@ -468,10 +471,11 @@ static void pch_gbe_mac_mc_addr_list_update(struct pch_gbe_hw *hw, */ s32 pch_gbe_mac_force_mac_fc(struct pch_gbe_hw *hw) { + struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); struct pch_gbe_mac_info *mac = &hw->mac; u32 rx_fctrl; - pr_debug("mac->fc = %u\n", mac->fc); + netdev_dbg(adapter->netdev, "mac->fc = %u\n", mac->fc); rx_fctrl = ioread32(&hw->reg->RX_FCTRL); @@ -493,14 +497,16 @@ s32 pch_gbe_mac_force_mac_fc(struct pch_gbe_hw *hw) mac->tx_fc_enable = true; break; default: - pr_err("Flow control param set incorrectly\n"); + netdev_err(adapter->netdev, + "Flow control param set incorrectly\n"); return -EINVAL; } if (mac->link_duplex == DUPLEX_HALF) rx_fctrl &= ~PCH_GBE_FL_CTRL_EN; iowrite32(rx_fctrl, &hw->reg->RX_FCTRL); - pr_debug("RX_FCTRL reg : 0x%08x mac->tx_fc_enable : %d\n", - ioread32(&hw->reg->RX_FCTRL), mac->tx_fc_enable); + netdev_dbg(adapter->netdev, + "RX_FCTRL reg : 0x%08x mac->tx_fc_enable : %d\n", + ioread32(&hw->reg->RX_FCTRL), mac->tx_fc_enable); return 0; } @@ -511,10 +517,11 @@ s32 pch_gbe_mac_force_mac_fc(struct pch_gbe_hw *hw) */ static void pch_gbe_mac_set_wol_event(struct pch_gbe_hw *hw, u32 wu_evt) { + struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); u32 addr_mask; - pr_debug("wu_evt : 0x%08x ADDR_MASK reg : 0x%08x\n", - wu_evt, ioread32(&hw->reg->ADDR_MASK)); + netdev_dbg(adapter->netdev, "wu_evt : 0x%08x ADDR_MASK reg : 0x%08x\n", + wu_evt, ioread32(&hw->reg->ADDR_MASK)); if (wu_evt) { /* Set Wake-On-Lan address mask */ @@ -546,6 +553,7 @@ static void pch_gbe_mac_set_wol_event(struct pch_gbe_hw *hw, u32 wu_evt) u16 pch_gbe_mac_ctrl_miim(struct pch_gbe_hw *hw, u32 addr, u32 dir, u32 reg, u16 data) { + struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); u32 data_out = 0; unsigned int i; unsigned long flags; @@ -558,7 +566,7 @@ u16 pch_gbe_mac_ctrl_miim(struct pch_gbe_hw *hw, u32 addr, u32 dir, u32 reg, udelay(20); } if (i == 0) { - pr_err("pch-gbe.miim won't go Ready\n"); + netdev_err(adapter->netdev, "pch-gbe.miim won't go Ready\n"); spin_unlock_irqrestore(&hw->miim_lock, flags); return 0; /* No way to indicate timeout error */ } @@ -573,9 +581,9 @@ u16 pch_gbe_mac_ctrl_miim(struct pch_gbe_hw *hw, u32 addr, u32 dir, u32 reg, } spin_unlock_irqrestore(&hw->miim_lock, flags); - pr_debug("PHY %s: reg=%d, data=0x%04X\n", - dir == PCH_GBE_MIIM_OPER_READ ? "READ" : "WRITE", reg, - dir == PCH_GBE_MIIM_OPER_READ ? data_out : data); + netdev_dbg(adapter->netdev, "PHY %s: reg=%d, data=0x%04X\n", + dir == PCH_GBE_MIIM_OPER_READ ? "READ" : "WRITE", reg, + dir == PCH_GBE_MIIM_OPER_READ ? data_out : data); return (u16) data_out; } @@ -585,6 +593,7 @@ u16 pch_gbe_mac_ctrl_miim(struct pch_gbe_hw *hw, u32 addr, u32 dir, u32 reg, */ static void pch_gbe_mac_set_pause_packet(struct pch_gbe_hw *hw) { + struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); unsigned long tmp2, tmp3; /* Set Pause packet */ @@ -606,10 +615,13 @@ static void pch_gbe_mac_set_pause_packet(struct pch_gbe_hw *hw) /* Transmit Pause Packet */ iowrite32(PCH_GBE_PS_PKT_RQ, &hw->reg->PAUSE_REQ); - pr_debug("PAUSE_PKT1-5 reg : 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", - ioread32(&hw->reg->PAUSE_PKT1), ioread32(&hw->reg->PAUSE_PKT2), - ioread32(&hw->reg->PAUSE_PKT3), ioread32(&hw->reg->PAUSE_PKT4), - ioread32(&hw->reg->PAUSE_PKT5)); + netdev_dbg(adapter->netdev, + "PAUSE_PKT1-5 reg : 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", + ioread32(&hw->reg->PAUSE_PKT1), + ioread32(&hw->reg->PAUSE_PKT2), + ioread32(&hw->reg->PAUSE_PKT3), + ioread32(&hw->reg->PAUSE_PKT4), + ioread32(&hw->reg->PAUSE_PKT5)); return; } @@ -669,7 +681,7 @@ static int pch_gbe_init_phy(struct pch_gbe_adapter *adapter) break; } adapter->hw.phy.addr = adapter->mii.phy_id; - pr_debug("phy_addr = %d\n", adapter->mii.phy_id); + netdev_dbg(netdev, "phy_addr = %d\n", adapter->mii.phy_id); if (addr == 32) return -EAGAIN; /* Selected the phy and isolate the rest */ @@ -758,13 +770,15 @@ void pch_gbe_reinit_locked(struct pch_gbe_adapter *adapter) */ void pch_gbe_reset(struct pch_gbe_adapter *adapter) { + struct net_device *netdev = adapter->netdev; + pch_gbe_mac_reset_hw(&adapter->hw); /* reprogram multicast address register after reset */ - pch_gbe_set_multi(adapter->netdev); + pch_gbe_set_multi(netdev); /* Setup the receive address. */ pch_gbe_mac_init_rx_addrs(&adapter->hw, PCH_GBE_MAR_ENTRIES); if (pch_gbe_hal_init_hw(&adapter->hw)) - pr_err("Hardware Error\n"); + netdev_err(netdev, "Hardware Error\n"); } /** @@ -778,7 +792,7 @@ static void pch_gbe_free_irq(struct pch_gbe_adapter *adapter) free_irq(adapter->pdev->irq, netdev); if (adapter->have_msi) { pci_disable_msi(adapter->pdev); - pr_debug("call pci_disable_msi\n"); + netdev_dbg(netdev, "call pci_disable_msi\n"); } } @@ -795,7 +809,8 @@ static void pch_gbe_irq_disable(struct pch_gbe_adapter *adapter) ioread32(&hw->reg->INT_ST); synchronize_irq(adapter->pdev->irq); - pr_debug("INT_EN reg : 0x%08x\n", ioread32(&hw->reg->INT_EN)); + netdev_dbg(adapter->netdev, "INT_EN reg : 0x%08x\n", + ioread32(&hw->reg->INT_EN)); } /** @@ -809,7 +824,8 @@ static void pch_gbe_irq_enable(struct pch_gbe_adapter *adapter) if (likely(atomic_dec_and_test(&adapter->irq_sem))) iowrite32(PCH_GBE_INT_ENABLE_MASK, &hw->reg->INT_EN); ioread32(&hw->reg->INT_ST); - pr_debug("INT_EN reg : 0x%08x\n", ioread32(&hw->reg->INT_EN)); + netdev_dbg(adapter->netdev, "INT_EN reg : 0x%08x\n", + ioread32(&hw->reg->INT_EN)); } @@ -846,9 +862,9 @@ static void pch_gbe_configure_tx(struct pch_gbe_adapter *adapter) struct pch_gbe_hw *hw = &adapter->hw; u32 tdba, tdlen, dctrl; - pr_debug("dma addr = 0x%08llx size = 0x%08x\n", - (unsigned long long)adapter->tx_ring->dma, - adapter->tx_ring->size); + netdev_dbg(adapter->netdev, "dma addr = 0x%08llx size = 0x%08x\n", + (unsigned long long)adapter->tx_ring->dma, + adapter->tx_ring->size); /* Setup the HW Tx Head and Tail descriptor pointers */ tdba = adapter->tx_ring->dma; @@ -894,9 +910,9 @@ static void pch_gbe_configure_rx(struct pch_gbe_adapter *adapter) struct pch_gbe_hw *hw = &adapter->hw; u32 rdba, rdlen, rxdma; - pr_debug("dma adr = 0x%08llx size = 0x%08x\n", - (unsigned long long)adapter->rx_ring->dma, - adapter->rx_ring->size); + netdev_dbg(adapter->netdev, "dma adr = 0x%08llx size = 0x%08x\n", + (unsigned long long)adapter->rx_ring->dma, + adapter->rx_ring->size); pch_gbe_mac_force_mac_fc(hw); @@ -907,9 +923,10 @@ static void pch_gbe_configure_rx(struct pch_gbe_adapter *adapter) rxdma &= ~PCH_GBE_RX_DMA_EN; iowrite32(rxdma, &hw->reg->DMA_CTRL); - pr_debug("MAC_RX_EN reg = 0x%08x DMA_CTRL reg = 0x%08x\n", - ioread32(&hw->reg->MAC_RX_EN), - ioread32(&hw->reg->DMA_CTRL)); + netdev_dbg(adapter->netdev, + "MAC_RX_EN reg = 0x%08x DMA_CTRL reg = 0x%08x\n", + ioread32(&hw->reg->MAC_RX_EN), + ioread32(&hw->reg->DMA_CTRL)); /* Setup the HW Rx Head and Tail Descriptor Pointers and * the Base and Length of the Rx Descriptor Ring */ @@ -977,7 +994,8 @@ static void pch_gbe_clean_tx_ring(struct pch_gbe_adapter *adapter, buffer_info = &tx_ring->buffer_info[i]; pch_gbe_unmap_and_free_tx_resource(adapter, buffer_info); } - pr_debug("call pch_gbe_unmap_and_free_tx_resource() %d count\n", i); + netdev_dbg(adapter->netdev, + "call pch_gbe_unmap_and_free_tx_resource() %d count\n", i); size = (unsigned long)sizeof(struct pch_gbe_buffer) * tx_ring->count; memset(tx_ring->buffer_info, 0, size); @@ -1009,7 +1027,8 @@ pch_gbe_clean_rx_ring(struct pch_gbe_adapter *adapter, buffer_info = &rx_ring->buffer_info[i]; pch_gbe_unmap_and_free_rx_resource(adapter, buffer_info); } - pr_debug("call pch_gbe_unmap_and_free_rx_resource() %d count\n", i); + netdev_dbg(adapter->netdev, + "call pch_gbe_unmap_and_free_rx_resource() %d count\n", i); size = (unsigned long)sizeof(struct pch_gbe_buffer) * rx_ring->count; memset(rx_ring->buffer_info, 0, size); @@ -1087,7 +1106,7 @@ static void pch_gbe_watchdog(unsigned long data) struct net_device *netdev = adapter->netdev; struct pch_gbe_hw *hw = &adapter->hw; - pr_debug("right now = %ld\n", jiffies); + netdev_dbg(netdev, "right now = %ld\n", jiffies); pch_gbe_update_stats(adapter); if ((mii_link_ok(&adapter->mii)) && (!netif_carrier_ok(netdev))) { @@ -1095,7 +1114,7 @@ static void pch_gbe_watchdog(unsigned long data) netdev->tx_queue_len = adapter->tx_queue_len; /* mii library handles link maintenance tasks */ if (mii_ethtool_gset(&adapter->mii, &cmd)) { - pr_err("ethtool get setting Error\n"); + netdev_err(netdev, "ethtool get setting Error\n"); mod_timer(&adapter->watchdog_timer, round_jiffies(jiffies + PCH_GBE_WATCHDOG_PERIOD)); @@ -1213,7 +1232,7 @@ static void pch_gbe_tx_queue(struct pch_gbe_adapter *adapter, buffer_info->length, DMA_TO_DEVICE); if (dma_mapping_error(&adapter->pdev->dev, buffer_info->dma)) { - pr_err("TX DMA map failed\n"); + netdev_err(adapter->netdev, "TX DMA map failed\n"); buffer_info->dma = 0; buffer_info->time_stamp = 0; tx_ring->next_to_use = ring_num; @@ -1333,13 +1352,13 @@ static irqreturn_t pch_gbe_intr(int irq, void *data) /* When request status is no interruption factor */ if (unlikely(!int_st)) return IRQ_NONE; /* Not our interrupt. End processing. */ - pr_debug("%s occur int_st = 0x%08x\n", __func__, int_st); + netdev_dbg(netdev, "%s occur int_st = 0x%08x\n", __func__, int_st); if (int_st & PCH_GBE_INT_RX_FRAME_ERR) adapter->stats.intr_rx_frame_err_count++; if (int_st & PCH_GBE_INT_RX_FIFO_ERR) if (!adapter->rx_stop_flag) { adapter->stats.intr_rx_fifo_err_count++; - pr_debug("Rx fifo over run\n"); + netdev_dbg(netdev, "Rx fifo over run\n"); adapter->rx_stop_flag = true; int_en = ioread32(&hw->reg->INT_EN); iowrite32((int_en & ~PCH_GBE_INT_RX_FIFO_ERR), @@ -1359,7 +1378,7 @@ static irqreturn_t pch_gbe_intr(int irq, void *data) /* When Rx descriptor is empty */ if ((int_st & PCH_GBE_INT_RX_DSC_EMP)) { adapter->stats.intr_rx_dsc_empty_count++; - pr_debug("Rx descriptor is empty\n"); + netdev_dbg(netdev, "Rx descriptor is empty\n"); int_en = ioread32(&hw->reg->INT_EN); iowrite32((int_en & ~PCH_GBE_INT_RX_DSC_EMP), &hw->reg->INT_EN); if (hw->mac.tx_fc_enable) { @@ -1382,8 +1401,8 @@ static irqreturn_t pch_gbe_intr(int irq, void *data) __napi_schedule(&adapter->napi); } } - pr_debug("return = 0x%08x INT_EN reg = 0x%08x\n", - IRQ_HANDLED, ioread32(&hw->reg->INT_EN)); + netdev_dbg(netdev, "return = 0x%08x INT_EN reg = 0x%08x\n", + IRQ_HANDLED, ioread32(&hw->reg->INT_EN)); return IRQ_HANDLED; } @@ -1437,9 +1456,10 @@ pch_gbe_alloc_rx_buffers(struct pch_gbe_adapter *adapter, rx_desc->buffer_addr = (buffer_info->dma); rx_desc->gbec_status = DSC_INIT16; - pr_debug("i = %d buffer_info->dma = 0x08%llx buffer_info->length = 0x%x\n", - i, (unsigned long long)buffer_info->dma, - buffer_info->length); + netdev_dbg(netdev, + "i = %d buffer_info->dma = 0x08%llx buffer_info->length = 0x%x\n", + i, (unsigned long long)buffer_info->dma, + buffer_info->length); if (unlikely(++i == rx_ring->count)) i = 0; @@ -1531,12 +1551,13 @@ pch_gbe_clean_tx(struct pch_gbe_adapter *adapter, bool cleaned = false; int unused, thresh; - pr_debug("next_to_clean : %d\n", tx_ring->next_to_clean); + netdev_dbg(adapter->netdev, "next_to_clean : %d\n", + tx_ring->next_to_clean); i = tx_ring->next_to_clean; tx_desc = PCH_GBE_TX_DESC(*tx_ring, i); - pr_debug("gbec_status:0x%04x dma_status:0x%04x\n", - tx_desc->gbec_status, tx_desc->dma_status); + netdev_dbg(adapter->netdev, "gbec_status:0x%04x dma_status:0x%04x\n", + tx_desc->gbec_status, tx_desc->dma_status); unused = PCH_GBE_DESC_UNUSED(tx_ring); thresh = tx_ring->count - PCH_GBE_TX_WEIGHT; @@ -1544,8 +1565,10 @@ pch_gbe_clean_tx(struct pch_gbe_adapter *adapter, { /* current marked clean, tx queue filling up, do extra clean */ int j, k; if (unused < 8) { /* tx queue nearly full */ - pr_debug("clean_tx: transmit queue warning (%x,%x) unused=%d\n", - tx_ring->next_to_clean,tx_ring->next_to_use,unused); + netdev_dbg(adapter->netdev, + "clean_tx: transmit queue warning (%x,%x) unused=%d\n", + tx_ring->next_to_clean, tx_ring->next_to_use, + unused); } /* current marked clean, scan for more that need cleaning. */ @@ -1557,49 +1580,56 @@ pch_gbe_clean_tx(struct pch_gbe_adapter *adapter, if (++k >= tx_ring->count) k = 0; /*increment, wrap*/ } if (j < PCH_GBE_TX_WEIGHT) { - pr_debug("clean_tx: unused=%d loops=%d found tx_desc[%x,%x:%x].gbec_status=%04x\n", - unused,j, i,k, tx_ring->next_to_use, tx_desc->gbec_status); + netdev_dbg(adapter->netdev, + "clean_tx: unused=%d loops=%d found tx_desc[%x,%x:%x].gbec_status=%04x\n", + unused, j, i, k, tx_ring->next_to_use, + tx_desc->gbec_status); i = k; /*found one to clean, usu gbec_status==2000.*/ } } while ((tx_desc->gbec_status & DSC_INIT16) == 0x0000) { - pr_debug("gbec_status:0x%04x\n", tx_desc->gbec_status); + netdev_dbg(adapter->netdev, "gbec_status:0x%04x\n", + tx_desc->gbec_status); buffer_info = &tx_ring->buffer_info[i]; skb = buffer_info->skb; cleaned = true; if ((tx_desc->gbec_status & PCH_GBE_TXD_GMAC_STAT_ABT)) { adapter->stats.tx_aborted_errors++; - pr_err("Transfer Abort Error\n"); + netdev_err(adapter->netdev, "Transfer Abort Error\n"); } else if ((tx_desc->gbec_status & PCH_GBE_TXD_GMAC_STAT_CRSER) ) { adapter->stats.tx_carrier_errors++; - pr_err("Transfer Carrier Sense Error\n"); + netdev_err(adapter->netdev, + "Transfer Carrier Sense Error\n"); } else if ((tx_desc->gbec_status & PCH_GBE_TXD_GMAC_STAT_EXCOL) ) { adapter->stats.tx_aborted_errors++; - pr_err("Transfer Collision Abort Error\n"); + netdev_err(adapter->netdev, + "Transfer Collision Abort Error\n"); } else if ((tx_desc->gbec_status & (PCH_GBE_TXD_GMAC_STAT_SNGCOL | PCH_GBE_TXD_GMAC_STAT_MLTCOL))) { adapter->stats.collisions++; adapter->stats.tx_packets++; adapter->stats.tx_bytes += skb->len; - pr_debug("Transfer Collision\n"); + netdev_dbg(adapter->netdev, "Transfer Collision\n"); } else if ((tx_desc->gbec_status & PCH_GBE_TXD_GMAC_STAT_CMPLT) ) { adapter->stats.tx_packets++; adapter->stats.tx_bytes += skb->len; } if (buffer_info->mapped) { - pr_debug("unmap buffer_info->dma : %d\n", i); + netdev_dbg(adapter->netdev, + "unmap buffer_info->dma : %d\n", i); dma_unmap_single(&adapter->pdev->dev, buffer_info->dma, buffer_info->length, DMA_TO_DEVICE); buffer_info->mapped = false; } if (buffer_info->skb) { - pr_debug("trim buffer_info->skb : %d\n", i); + netdev_dbg(adapter->netdev, + "trim buffer_info->skb : %d\n", i); skb_trim(buffer_info->skb, 0); } tx_desc->gbec_status = DSC_INIT16; @@ -1613,8 +1643,9 @@ pch_gbe_clean_tx(struct pch_gbe_adapter *adapter, break; } } - pr_debug("called pch_gbe_unmap_and_free_tx_resource() %d count\n", - cleaned_count); + netdev_dbg(adapter->netdev, + "called pch_gbe_unmap_and_free_tx_resource() %d count\n", + cleaned_count); if (cleaned_count > 0) { /*skip this if nothing cleaned*/ /* Recover from running out of Tx resources in xmit_frame */ spin_lock(&tx_ring->tx_lock); @@ -1622,12 +1653,13 @@ pch_gbe_clean_tx(struct pch_gbe_adapter *adapter, { netif_wake_queue(adapter->netdev); adapter->stats.tx_restart_count++; - pr_debug("Tx wake queue\n"); + netdev_dbg(adapter->netdev, "Tx wake queue\n"); } tx_ring->next_to_clean = i; - pr_debug("next_to_clean : %d\n", tx_ring->next_to_clean); + netdev_dbg(adapter->netdev, "next_to_clean : %d\n", + tx_ring->next_to_clean); spin_unlock(&tx_ring->tx_lock); } return cleaned; @@ -1684,22 +1716,22 @@ pch_gbe_clean_rx(struct pch_gbe_adapter *adapter, buffer_info->length, DMA_FROM_DEVICE); buffer_info->mapped = false; - pr_debug("RxDecNo = 0x%04x Status[DMA:0x%02x GBE:0x%04x " - "TCP:0x%08x] BufInf = 0x%p\n", - i, dma_status, gbec_status, tcp_ip_status, - buffer_info); + netdev_dbg(netdev, + "RxDecNo = 0x%04x Status[DMA:0x%02x GBE:0x%04x TCP:0x%08x] BufInf = 0x%p\n", + i, dma_status, gbec_status, tcp_ip_status, + buffer_info); /* Error check */ if (unlikely(gbec_status & PCH_GBE_RXD_GMAC_STAT_NOTOCTAL)) { adapter->stats.rx_frame_errors++; - pr_err("Receive Not Octal Error\n"); + netdev_err(netdev, "Receive Not Octal Error\n"); } else if (unlikely(gbec_status & PCH_GBE_RXD_GMAC_STAT_NBLERR)) { adapter->stats.rx_frame_errors++; - pr_err("Receive Nibble Error\n"); + netdev_err(netdev, "Receive Nibble Error\n"); } else if (unlikely(gbec_status & PCH_GBE_RXD_GMAC_STAT_CRCERR)) { adapter->stats.rx_crc_errors++; - pr_err("Receive CRC Error\n"); + netdev_err(netdev, "Receive CRC Error\n"); } else { /* get receive length */ /* length convert[-3], length includes FCS length */ @@ -1730,8 +1762,9 @@ pch_gbe_clean_rx(struct pch_gbe_adapter *adapter, napi_gro_receive(&adapter->napi, skb); (*work_done)++; - pr_debug("Receive skb->ip_summed: %d length: %d\n", - skb->ip_summed, length); + netdev_dbg(netdev, + "Receive skb->ip_summed: %d length: %d\n", + skb->ip_summed, length); } /* return some buffers to hardware, one at a time is too slow */ if (unlikely(cleaned_count >= PCH_GBE_RX_BUFFER_WRITE)) { @@ -1787,10 +1820,10 @@ int pch_gbe_setup_tx_resources(struct pch_gbe_adapter *adapter, tx_desc = PCH_GBE_TX_DESC(*tx_ring, desNo); tx_desc->gbec_status = DSC_INIT16; } - pr_debug("tx_ring->desc = 0x%p tx_ring->dma = 0x%08llx\n" - "next_to_clean = 0x%08x next_to_use = 0x%08x\n", - tx_ring->desc, (unsigned long long)tx_ring->dma, - tx_ring->next_to_clean, tx_ring->next_to_use); + netdev_dbg(adapter->netdev, + "tx_ring->desc = 0x%p tx_ring->dma = 0x%08llx next_to_clean = 0x%08x next_to_use = 0x%08x\n", + tx_ring->desc, (unsigned long long)tx_ring->dma, + tx_ring->next_to_clean, tx_ring->next_to_use); return 0; } @@ -1829,10 +1862,10 @@ int pch_gbe_setup_rx_resources(struct pch_gbe_adapter *adapter, rx_desc = PCH_GBE_RX_DESC(*rx_ring, desNo); rx_desc->gbec_status = DSC_INIT16; } - pr_debug("rx_ring->desc = 0x%p rx_ring->dma = 0x%08llx " - "next_to_clean = 0x%08x next_to_use = 0x%08x\n", - rx_ring->desc, (unsigned long long)rx_ring->dma, - rx_ring->next_to_clean, rx_ring->next_to_use); + netdev_dbg(adapter->netdev, + "rx_ring->desc = 0x%p rx_ring->dma = 0x%08llx next_to_clean = 0x%08x next_to_use = 0x%08x\n", + rx_ring->desc, (unsigned long long)rx_ring->dma, + rx_ring->next_to_clean, rx_ring->next_to_use); return 0; } @@ -1886,9 +1919,9 @@ static int pch_gbe_request_irq(struct pch_gbe_adapter *adapter) flags = IRQF_SHARED; adapter->have_msi = false; err = pci_enable_msi(adapter->pdev); - pr_debug("call pci_enable_msi\n"); + netdev_dbg(netdev, "call pci_enable_msi\n"); if (err) { - pr_debug("call pci_enable_msi - Error: %d\n", err); + netdev_dbg(netdev, "call pci_enable_msi - Error: %d\n", err); } else { flags = 0; adapter->have_msi = true; @@ -1896,9 +1929,11 @@ static int pch_gbe_request_irq(struct pch_gbe_adapter *adapter) err = request_irq(adapter->pdev->irq, &pch_gbe_intr, flags, netdev->name, netdev); if (err) - pr_err("Unable to allocate interrupt Error: %d\n", err); - pr_debug("adapter->have_msi : %d flags : 0x%04x return : 0x%04x\n", - adapter->have_msi, flags, err); + netdev_err(netdev, "Unable to allocate interrupt Error: %d\n", + err); + netdev_dbg(netdev, + "adapter->have_msi : %d flags : 0x%04x return : 0x%04x\n", + adapter->have_msi, flags, err); return err; } @@ -1919,7 +1954,7 @@ int pch_gbe_up(struct pch_gbe_adapter *adapter) /* Ensure we have a valid MAC */ if (!is_valid_ether_addr(adapter->hw.mac.addr)) { - pr_err("Error: Invalid MAC address\n"); + netdev_err(netdev, "Error: Invalid MAC address\n"); goto out; } @@ -1933,12 +1968,14 @@ int pch_gbe_up(struct pch_gbe_adapter *adapter) err = pch_gbe_request_irq(adapter); if (err) { - pr_err("Error: can't bring device up - irq request failed\n"); + netdev_err(netdev, + "Error: can't bring device up - irq request failed\n"); goto out; } err = pch_gbe_alloc_rx_buffers_pool(adapter, rx_ring, rx_ring->count); if (err) { - pr_err("Error: can't bring device up - alloc rx buffers pool failed\n"); + netdev_err(netdev, + "Error: can't bring device up - alloc rx buffers pool failed\n"); goto freeirq; } pch_gbe_alloc_tx_buffers(adapter, tx_ring); @@ -2015,11 +2052,11 @@ static int pch_gbe_sw_init(struct pch_gbe_adapter *adapter) /* Initialize the hardware-specific values */ if (pch_gbe_hal_setup_init_funcs(hw)) { - pr_err("Hardware Initialization Failure\n"); + netdev_err(netdev, "Hardware Initialization Failure\n"); return -EIO; } if (pch_gbe_alloc_queues(adapter)) { - pr_err("Unable to allocate memory for queues\n"); + netdev_err(netdev, "Unable to allocate memory for queues\n"); return -ENOMEM; } spin_lock_init(&adapter->hw.miim_lock); @@ -2030,9 +2067,10 @@ static int pch_gbe_sw_init(struct pch_gbe_adapter *adapter) pch_gbe_init_stats(adapter); - pr_debug("rx_buffer_len : %d mac.min_frame_size : %d mac.max_frame_size : %d\n", - (u32) adapter->rx_buffer_len, - hw->mac.min_frame_size, hw->mac.max_frame_size); + netdev_dbg(netdev, + "rx_buffer_len : %d mac.min_frame_size : %d mac.max_frame_size : %d\n", + (u32) adapter->rx_buffer_len, + hw->mac.min_frame_size, hw->mac.max_frame_size); return 0; } @@ -2061,7 +2099,7 @@ static int pch_gbe_open(struct net_device *netdev) err = pch_gbe_up(adapter); if (err) goto err_up; - pr_debug("Success End\n"); + netdev_dbg(netdev, "Success End\n"); return 0; err_up: @@ -2072,7 +2110,7 @@ err_setup_rx: pch_gbe_free_tx_resources(adapter, adapter->tx_ring); err_setup_tx: pch_gbe_reset(adapter); - pr_err("Error End\n"); + netdev_err(netdev, "Error End\n"); return err; } @@ -2116,8 +2154,9 @@ static int pch_gbe_xmit_frame(struct sk_buff *skb, struct net_device *netdev) if (unlikely(!PCH_GBE_DESC_UNUSED(tx_ring))) { netif_stop_queue(netdev); spin_unlock_irqrestore(&tx_ring->tx_lock, flags); - pr_debug("Return : BUSY next_to use : 0x%08x next_to clean : 0x%08x\n", - tx_ring->next_to_use, tx_ring->next_to_clean); + netdev_dbg(netdev, + "Return : BUSY next_to use : 0x%08x next_to clean : 0x%08x\n", + tx_ring->next_to_use, tx_ring->next_to_clean); return NETDEV_TX_BUSY; } @@ -2152,7 +2191,7 @@ static void pch_gbe_set_multi(struct net_device *netdev) int i; int mc_count; - pr_debug("netdev->flags : 0x%08x\n", netdev->flags); + netdev_dbg(netdev, "netdev->flags : 0x%08x\n", netdev->flags); /* Check for Promiscuous and All Multicast modes */ rctl = ioread32(&hw->reg->RX_MODE); @@ -2192,7 +2231,8 @@ static void pch_gbe_set_multi(struct net_device *netdev) PCH_GBE_MAR_ENTRIES); kfree(mta_list); - pr_debug("RX_MODE reg(check bit31,30 ADD,MLT) : 0x%08x netdev->mc_count : 0x%08x\n", + netdev_dbg(netdev, + "RX_MODE reg(check bit31,30 ADD,MLT) : 0x%08x netdev->mc_count : 0x%08x\n", ioread32(&hw->reg->RX_MODE), mc_count); } @@ -2218,12 +2258,12 @@ static int pch_gbe_set_mac(struct net_device *netdev, void *addr) pch_gbe_mac_mar_set(&adapter->hw, adapter->hw.mac.addr, 0); ret_val = 0; } - pr_debug("ret_val : 0x%08x\n", ret_val); - pr_debug("dev_addr : %pM\n", netdev->dev_addr); - pr_debug("mac_addr : %pM\n", adapter->hw.mac.addr); - pr_debug("MAC_ADR1AB reg : 0x%08x 0x%08x\n", - ioread32(&adapter->hw.reg->mac_adr[0].high), - ioread32(&adapter->hw.reg->mac_adr[0].low)); + netdev_dbg(netdev, "ret_val : 0x%08x\n", ret_val); + netdev_dbg(netdev, "dev_addr : %pM\n", netdev->dev_addr); + netdev_dbg(netdev, "mac_addr : %pM\n", adapter->hw.mac.addr); + netdev_dbg(netdev, "MAC_ADR1AB reg : 0x%08x 0x%08x\n", + ioread32(&adapter->hw.reg->mac_adr[0].high), + ioread32(&adapter->hw.reg->mac_adr[0].low)); return ret_val; } @@ -2245,7 +2285,7 @@ static int pch_gbe_change_mtu(struct net_device *netdev, int new_mtu) max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN; if ((max_frame < ETH_ZLEN + ETH_FCS_LEN) || (max_frame > PCH_GBE_MAX_JUMBO_FRAME_SIZE)) { - pr_err("Invalid MTU setting\n"); + netdev_err(netdev, "Invalid MTU setting\n"); return -EINVAL; } if (max_frame <= PCH_GBE_FRAME_SIZE_2048) @@ -2274,9 +2314,10 @@ static int pch_gbe_change_mtu(struct net_device *netdev, int new_mtu) adapter->hw.mac.max_frame_size = max_frame; } - pr_debug("max_frame : %d rx_buffer_len : %d mtu : %d max_frame_size : %d\n", - max_frame, (u32) adapter->rx_buffer_len, netdev->mtu, - adapter->hw.mac.max_frame_size); + netdev_dbg(netdev, + "max_frame : %d rx_buffer_len : %d mtu : %d max_frame_size : %d\n", + max_frame, (u32) adapter->rx_buffer_len, netdev->mtu, + adapter->hw.mac.max_frame_size); return 0; } @@ -2317,7 +2358,7 @@ static int pch_gbe_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) { struct pch_gbe_adapter *adapter = netdev_priv(netdev); - pr_debug("cmd : 0x%04x\n", cmd); + netdev_dbg(netdev, "cmd : 0x%04x\n", cmd); if (cmd == SIOCSHWTSTAMP) return hwtstamp_ioctl(netdev, ifr, cmd); @@ -2354,7 +2395,7 @@ static int pch_gbe_napi_poll(struct napi_struct *napi, int budget) bool poll_end_flag = false; bool cleaned = false; - pr_debug("budget : %d\n", budget); + netdev_dbg(adapter->netdev, "budget : %d\n", budget); pch_gbe_clean_rx(adapter, adapter->rx_ring, &work_done, budget); cleaned = pch_gbe_clean_tx(adapter, adapter->tx_ring); @@ -2377,8 +2418,9 @@ static int pch_gbe_napi_poll(struct napi_struct *napi, int budget) pch_gbe_enable_dma_rx(&adapter->hw); } - pr_debug("poll_end_flag : %d work_done : %d budget : %d\n", - poll_end_flag, work_done, budget); + netdev_dbg(adapter->netdev, + "poll_end_flag : %d work_done : %d budget : %d\n", + poll_end_flag, work_done, budget); return work_done; } @@ -2435,7 +2477,7 @@ static pci_ers_result_t pch_gbe_io_slot_reset(struct pci_dev *pdev) struct pch_gbe_hw *hw = &adapter->hw; if (pci_enable_device(pdev)) { - pr_err("Cannot re-enable PCI device after reset\n"); + netdev_err(netdev, "Cannot re-enable PCI device after reset\n"); return PCI_ERS_RESULT_DISCONNECT; } pci_set_master(pdev); @@ -2455,7 +2497,8 @@ static void pch_gbe_io_resume(struct pci_dev *pdev) if (netif_running(netdev)) { if (pch_gbe_up(adapter)) { - pr_debug("can't bring device back up after reset\n"); + netdev_dbg(netdev, + "can't bring device back up after reset\n"); return; } } @@ -2509,7 +2552,7 @@ static int pch_gbe_resume(struct device *device) err = pci_enable_device(pdev); if (err) { - pr_err("Cannot enable PCI device from suspend\n"); + netdev_err(netdev, "Cannot enable PCI device from suspend\n"); return err; } pci_set_master(pdev); @@ -2609,7 +2652,7 @@ static int pch_gbe_probe(struct pci_dev *pdev, adapter->ptp_pdev = pci_get_bus_and_slot(adapter->pdev->bus->number, PCI_DEVFN(12, 4)); if (ptp_filter_init(ptp_filter, ARRAY_SIZE(ptp_filter))) { - pr_err("Bad ptp filter\n"); + dev_err(&pdev->dev, "Bad ptp filter\n"); return -EINVAL; } diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_param.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_param.c index 8653c3b81f84..cf7c9b3a255b 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_param.c +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_param.c @@ -237,16 +237,17 @@ static int pch_gbe_validate_option(int *value, case enable_option: switch (*value) { case OPTION_ENABLED: - pr_debug("%s Enabled\n", opt->name); + netdev_dbg(adapter->netdev, "%s Enabled\n", opt->name); return 0; case OPTION_DISABLED: - pr_debug("%s Disabled\n", opt->name); + netdev_dbg(adapter->netdev, "%s Disabled\n", opt->name); return 0; } break; case range_option: if (*value >= opt->arg.r.min && *value <= opt->arg.r.max) { - pr_debug("%s set to %i\n", opt->name, *value); + netdev_dbg(adapter->netdev, "%s set to %i\n", + opt->name, *value); return 0; } break; @@ -258,7 +259,8 @@ static int pch_gbe_validate_option(int *value, ent = &opt->arg.l.p[i]; if (*value == ent->i) { if (ent->str[0] != '\0') - pr_debug("%s\n", ent->str); + netdev_dbg(adapter->netdev, "%s\n", + ent->str); return 0; } } @@ -268,8 +270,8 @@ static int pch_gbe_validate_option(int *value, BUG(); } - pr_debug("Invalid %s value specified (%i) %s\n", - opt->name, *value, opt->err); + netdev_dbg(adapter->netdev, "Invalid %s value specified (%i) %s\n", + opt->name, *value, opt->err); *value = opt->def; return -1; } @@ -318,7 +320,8 @@ static void pch_gbe_check_copper_options(struct pch_gbe_adapter *adapter) .p = an_list} } }; if (speed || dplx) { - pr_debug("AutoNeg specified along with Speed or Duplex, AutoNeg parameter ignored\n"); + netdev_dbg(adapter->netdev, + "AutoNeg specified along with Speed or Duplex, AutoNeg parameter ignored\n"); hw->phy.autoneg_advertised = opt.def; } else { int tmp = AutoNeg; @@ -332,13 +335,16 @@ static void pch_gbe_check_copper_options(struct pch_gbe_adapter *adapter) case 0: hw->mac.autoneg = hw->mac.fc_autoneg = 1; if ((speed || dplx)) - pr_debug("Speed and duplex autonegotiation enabled\n"); + netdev_dbg(adapter->netdev, + "Speed and duplex autonegotiation enabled\n"); hw->mac.link_speed = SPEED_10; hw->mac.link_duplex = DUPLEX_HALF; break; case HALF_DUPLEX: - pr_debug("Half Duplex specified without Speed\n"); - pr_debug("Using Autonegotiation at Half Duplex only\n"); + netdev_dbg(adapter->netdev, + "Half Duplex specified without Speed\n"); + netdev_dbg(adapter->netdev, + "Using Autonegotiation at Half Duplex only\n"); hw->mac.autoneg = hw->mac.fc_autoneg = 1; hw->phy.autoneg_advertised = PHY_ADVERTISE_10_HALF | PHY_ADVERTISE_100_HALF; @@ -346,8 +352,10 @@ static void pch_gbe_check_copper_options(struct pch_gbe_adapter *adapter) hw->mac.link_duplex = DUPLEX_HALF; break; case FULL_DUPLEX: - pr_debug("Full Duplex specified without Speed\n"); - pr_debug("Using Autonegotiation at Full Duplex only\n"); + netdev_dbg(adapter->netdev, + "Full Duplex specified without Speed\n"); + netdev_dbg(adapter->netdev, + "Using Autonegotiation at Full Duplex only\n"); hw->mac.autoneg = hw->mac.fc_autoneg = 1; hw->phy.autoneg_advertised = PHY_ADVERTISE_10_FULL | PHY_ADVERTISE_100_FULL | @@ -356,8 +364,10 @@ static void pch_gbe_check_copper_options(struct pch_gbe_adapter *adapter) hw->mac.link_duplex = DUPLEX_FULL; break; case SPEED_10: - pr_debug("10 Mbps Speed specified without Duplex\n"); - pr_debug("Using Autonegotiation at 10 Mbps only\n"); + netdev_dbg(adapter->netdev, + "10 Mbps Speed specified without Duplex\n"); + netdev_dbg(adapter->netdev, + "Using Autonegotiation at 10 Mbps only\n"); hw->mac.autoneg = hw->mac.fc_autoneg = 1; hw->phy.autoneg_advertised = PHY_ADVERTISE_10_HALF | PHY_ADVERTISE_10_FULL; @@ -365,22 +375,24 @@ static void pch_gbe_check_copper_options(struct pch_gbe_adapter *adapter) hw->mac.link_duplex = DUPLEX_HALF; break; case SPEED_10 + HALF_DUPLEX: - pr_debug("Forcing to 10 Mbps Half Duplex\n"); + netdev_dbg(adapter->netdev, "Forcing to 10 Mbps Half Duplex\n"); hw->mac.autoneg = hw->mac.fc_autoneg = 0; hw->phy.autoneg_advertised = 0; hw->mac.link_speed = SPEED_10; hw->mac.link_duplex = DUPLEX_HALF; break; case SPEED_10 + FULL_DUPLEX: - pr_debug("Forcing to 10 Mbps Full Duplex\n"); + netdev_dbg(adapter->netdev, "Forcing to 10 Mbps Full Duplex\n"); hw->mac.autoneg = hw->mac.fc_autoneg = 0; hw->phy.autoneg_advertised = 0; hw->mac.link_speed = SPEED_10; hw->mac.link_duplex = DUPLEX_FULL; break; case SPEED_100: - pr_debug("100 Mbps Speed specified without Duplex\n"); - pr_debug("Using Autonegotiation at 100 Mbps only\n"); + netdev_dbg(adapter->netdev, + "100 Mbps Speed specified without Duplex\n"); + netdev_dbg(adapter->netdev, + "Using Autonegotiation at 100 Mbps only\n"); hw->mac.autoneg = hw->mac.fc_autoneg = 1; hw->phy.autoneg_advertised = PHY_ADVERTISE_100_HALF | PHY_ADVERTISE_100_FULL; @@ -388,28 +400,33 @@ static void pch_gbe_check_copper_options(struct pch_gbe_adapter *adapter) hw->mac.link_duplex = DUPLEX_HALF; break; case SPEED_100 + HALF_DUPLEX: - pr_debug("Forcing to 100 Mbps Half Duplex\n"); + netdev_dbg(adapter->netdev, + "Forcing to 100 Mbps Half Duplex\n"); hw->mac.autoneg = hw->mac.fc_autoneg = 0; hw->phy.autoneg_advertised = 0; hw->mac.link_speed = SPEED_100; hw->mac.link_duplex = DUPLEX_HALF; break; case SPEED_100 + FULL_DUPLEX: - pr_debug("Forcing to 100 Mbps Full Duplex\n"); + netdev_dbg(adapter->netdev, + "Forcing to 100 Mbps Full Duplex\n"); hw->mac.autoneg = hw->mac.fc_autoneg = 0; hw->phy.autoneg_advertised = 0; hw->mac.link_speed = SPEED_100; hw->mac.link_duplex = DUPLEX_FULL; break; case SPEED_1000: - pr_debug("1000 Mbps Speed specified without Duplex\n"); + netdev_dbg(adapter->netdev, + "1000 Mbps Speed specified without Duplex\n"); goto full_duplex_only; case SPEED_1000 + HALF_DUPLEX: - pr_debug("Half Duplex is not supported at 1000 Mbps\n"); + netdev_dbg(adapter->netdev, + "Half Duplex is not supported at 1000 Mbps\n"); /* fall through */ case SPEED_1000 + FULL_DUPLEX: full_duplex_only: - pr_debug("Using Autonegotiation at 1000 Mbps Full Duplex only\n"); + netdev_dbg(adapter->netdev, + "Using Autonegotiation at 1000 Mbps Full Duplex only\n"); hw->mac.autoneg = hw->mac.fc_autoneg = 1; hw->phy.autoneg_advertised = PHY_ADVERTISE_1000_FULL; hw->mac.link_speed = SPEED_1000; diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_phy.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_phy.c index b97c6575e018..da079073a6c6 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_phy.c +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_phy.c @@ -97,6 +97,7 @@ */ s32 pch_gbe_phy_get_id(struct pch_gbe_hw *hw) { + struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); struct pch_gbe_phy_info *phy = &hw->phy; s32 ret; u16 phy_id1; @@ -115,8 +116,9 @@ s32 pch_gbe_phy_get_id(struct pch_gbe_hw *hw) phy->id = (u32)phy_id1; phy->id = ((phy->id << 6) | ((phy_id2 & 0xFC00) >> 10)); phy->revision = (u32) (phy_id2 & 0x000F); - pr_debug("phy->id : 0x%08x phy->revision : 0x%08x\n", - phy->id, phy->revision); + netdev_dbg(adapter->netdev, + "phy->id : 0x%08x phy->revision : 0x%08x\n", + phy->id, phy->revision); return 0; } @@ -134,7 +136,10 @@ s32 pch_gbe_phy_read_reg_miic(struct pch_gbe_hw *hw, u32 offset, u16 *data) struct pch_gbe_phy_info *phy = &hw->phy; if (offset > PHY_MAX_REG_ADDRESS) { - pr_err("PHY Address %d is out of range\n", offset); + struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); + + netdev_err(adapter->netdev, "PHY Address %d is out of range\n", + offset); return -EINVAL; } *data = pch_gbe_mac_ctrl_miim(hw, phy->addr, PCH_GBE_HAL_MIIM_READ, @@ -156,7 +161,10 @@ s32 pch_gbe_phy_write_reg_miic(struct pch_gbe_hw *hw, u32 offset, u16 data) struct pch_gbe_phy_info *phy = &hw->phy; if (offset > PHY_MAX_REG_ADDRESS) { - pr_err("PHY Address %d is out of range\n", offset); + struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); + + netdev_err(adapter->netdev, "PHY Address %d is out of range\n", + offset); return -EINVAL; } pch_gbe_mac_ctrl_miim(hw, phy->addr, PCH_GBE_HAL_MIIM_WRITE, @@ -246,15 +254,14 @@ void pch_gbe_phy_set_rgmii(struct pch_gbe_hw *hw) */ void pch_gbe_phy_init_setting(struct pch_gbe_hw *hw) { - struct pch_gbe_adapter *adapter; + struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); struct ethtool_cmd cmd = { .cmd = ETHTOOL_GSET }; int ret; u16 mii_reg; - adapter = container_of(hw, struct pch_gbe_adapter, hw); ret = mii_ethtool_gset(&adapter->mii, &cmd); if (ret) - pr_err("Error: mii_ethtool_gset\n"); + netdev_err(adapter->netdev, "Error: mii_ethtool_gset\n"); ethtool_cmd_speed_set(&cmd, hw->mac.link_speed); cmd.duplex = hw->mac.link_duplex; @@ -263,7 +270,7 @@ void pch_gbe_phy_init_setting(struct pch_gbe_hw *hw) pch_gbe_phy_write_reg_miic(hw, MII_BMCR, BMCR_RESET); ret = mii_ethtool_sset(&adapter->mii, &cmd); if (ret) - pr_err("Error: mii_ethtool_sset\n"); + netdev_err(adapter->netdev, "Error: mii_ethtool_sset\n"); pch_gbe_phy_sw_reset(hw); -- cgit v1.2.3 From 29cc436cb90da4cabf404d8ceedc762fc7387b15 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 28 Jun 2013 14:02:54 +0300 Subject: pch_gbe: use managed functions pcim_* and devm_* This makes the error handling much more simpler than open-coding everything and in addition makes the probe function smaller an tidier. Signed-off-by: Andy Shevchenko Signed-off-by: David S. Miller --- .../net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c | 50 +++++++--------------- 1 file changed, 15 insertions(+), 35 deletions(-) diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c index 97db56393197..ab1039a95bf9 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c @@ -636,15 +636,15 @@ static void pch_gbe_mac_set_pause_packet(struct pch_gbe_hw *hw) */ static int pch_gbe_alloc_queues(struct pch_gbe_adapter *adapter) { - adapter->tx_ring = kzalloc(sizeof(*adapter->tx_ring), GFP_KERNEL); + adapter->tx_ring = devm_kzalloc(&adapter->pdev->dev, + sizeof(*adapter->tx_ring), GFP_KERNEL); if (!adapter->tx_ring) return -ENOMEM; - adapter->rx_ring = kzalloc(sizeof(*adapter->rx_ring), GFP_KERNEL); - if (!adapter->rx_ring) { - kfree(adapter->tx_ring); + adapter->rx_ring = devm_kzalloc(&adapter->pdev->dev, + sizeof(*adapter->rx_ring), GFP_KERNEL); + if (!adapter->rx_ring) return -ENOMEM; - } return 0; } @@ -2588,13 +2588,7 @@ static void pch_gbe_remove(struct pci_dev *pdev) pch_gbe_hal_phy_hw_reset(&adapter->hw); - kfree(adapter->tx_ring); - kfree(adapter->rx_ring); - - iounmap(adapter->hw.reg); - pci_release_regions(pdev); free_netdev(netdev); - pci_disable_device(pdev); } static int pch_gbe_probe(struct pci_dev *pdev, @@ -2604,7 +2598,7 @@ static int pch_gbe_probe(struct pci_dev *pdev, struct pch_gbe_adapter *adapter; int ret; - ret = pci_enable_device(pdev); + ret = pcim_enable_device(pdev); if (ret) return ret; @@ -2617,24 +2611,22 @@ static int pch_gbe_probe(struct pci_dev *pdev, if (ret) { dev_err(&pdev->dev, "ERR: No usable DMA " "configuration, aborting\n"); - goto err_disable_device; + return ret; } } } - ret = pci_request_regions(pdev, KBUILD_MODNAME); + ret = pcim_iomap_regions(pdev, 1 << PCH_GBE_PCI_BAR, pci_name(pdev)); if (ret) { dev_err(&pdev->dev, "ERR: Can't reserve PCI I/O and memory resources\n"); - goto err_disable_device; + return ret; } pci_set_master(pdev); netdev = alloc_etherdev((int)sizeof(struct pch_gbe_adapter)); - if (!netdev) { - ret = -ENOMEM; - goto err_release_pci; - } + if (!netdev) + return -ENOMEM; SET_NETDEV_DEV(netdev, &pdev->dev); pci_set_drvdata(pdev, netdev); @@ -2642,18 +2634,14 @@ static int pch_gbe_probe(struct pci_dev *pdev, adapter->netdev = netdev; adapter->pdev = pdev; adapter->hw.back = adapter; - adapter->hw.reg = pci_iomap(pdev, PCH_GBE_PCI_BAR, 0); - if (!adapter->hw.reg) { - ret = -EIO; - dev_err(&pdev->dev, "Can't ioremap\n"); - goto err_free_netdev; - } + adapter->hw.reg = pcim_iomap_table(pdev)[PCH_GBE_PCI_BAR]; adapter->ptp_pdev = pci_get_bus_and_slot(adapter->pdev->bus->number, PCI_DEVFN(12, 4)); if (ptp_filter_init(ptp_filter, ARRAY_SIZE(ptp_filter))) { dev_err(&pdev->dev, "Bad ptp filter\n"); - return -EINVAL; + ret = -EINVAL; + goto err_free_netdev; } netdev->netdev_ops = &pch_gbe_netdev_ops; @@ -2671,7 +2659,7 @@ static int pch_gbe_probe(struct pci_dev *pdev, /* setup the private structure */ ret = pch_gbe_sw_init(adapter); if (ret) - goto err_iounmap; + goto err_free_netdev; /* Initialize PHY */ ret = pch_gbe_init_phy(adapter); @@ -2727,16 +2715,8 @@ static int pch_gbe_probe(struct pci_dev *pdev, err_free_adapter: pch_gbe_hal_phy_hw_reset(&adapter->hw); - kfree(adapter->tx_ring); - kfree(adapter->rx_ring); -err_iounmap: - iounmap(adapter->hw.reg); err_free_netdev: free_netdev(netdev); -err_release_pci: - pci_release_regions(pdev); -err_disable_device: - pci_disable_device(pdev); return ret; } -- cgit v1.2.3 From cf6122be45de68bc773f64e4266db76129ea556f Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 27 Jun 2013 11:40:47 +0200 Subject: drivers: net: cpsw: add newline after MACID log Cosmetic patch to add a newline after logging the device's MACID. Signed-off-by: Daniel Mack Acked-by: Mugunthan V N Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpsw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 101b037a7dce..2c3657adc7cb 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -1700,10 +1700,10 @@ static int cpsw_probe(struct platform_device *pdev) if (is_valid_ether_addr(data->slave_data[0].mac_addr)) { memcpy(priv->mac_addr, data->slave_data[0].mac_addr, ETH_ALEN); - pr_info("Detected MACID = %pM", priv->mac_addr); + pr_info("Detected MACID = %pM\n", priv->mac_addr); } else { eth_random_addr(priv->mac_addr); - pr_info("Random MACID = %pM", priv->mac_addr); + pr_info("Random MACID = %pM\n", priv->mac_addr); } memcpy(ndev->dev_addr, priv->mac_addr, ETH_ALEN); -- cgit v1.2.3 From 3b233fe0435518169af03027aedc83d42eb28ac4 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Thu, 27 Jun 2013 13:44:26 +0200 Subject: nlmon: fix comparison in nlmon_is_valid_mtu This patch fixes the following warning introduced in e4fc408e0e99 ("packet: nlmon: virtual netlink monitoring device for packet sockets") reported by Dan Carpenter: warning: "drivers/net/nlmon.c:31 nlmon_is_valid_mtu() warn: always true condition '(new_mtu <= ((~0 >> 1))) => (s32min-s32max <= s32max)'" Thus, we should simply remove the test against INT_MAX. Next to that we also need to explicitly cast the sizeof() case as the comparison is type promoted to unsigned long so negative values are then valid instead of invalid. While at it, this also adds a comment about Netlink and MTUs. Reported-by: Dan Carpenter Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller --- drivers/net/nlmon.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/nlmon.c b/drivers/net/nlmon.c index dc364be6e61e..a0baf56f5826 100644 --- a/drivers/net/nlmon.c +++ b/drivers/net/nlmon.c @@ -28,7 +28,11 @@ static netdev_tx_t nlmon_xmit(struct sk_buff *skb, struct net_device *dev) static int nlmon_is_valid_mtu(int new_mtu) { - return new_mtu >= sizeof(struct nlmsghdr) && new_mtu <= INT_MAX; + /* Note that in netlink we do not really have an upper limit. On + * default, we use NLMSG_GOODSIZE. Here at least we should make + * sure that it's at least the header size. + */ + return new_mtu >= (int) sizeof(struct nlmsghdr); } static int nlmon_change_mtu(struct net_device *dev, int new_mtu) -- cgit v1.2.3 From 7ac0febb81259fb9e0c447e0b90d0f05f409a02b Mon Sep 17 00:00:00 2001 From: David Disseldorp Date: Fri, 28 Jun 2013 11:47:33 +0200 Subject: cifs: fill TRANS2_QUERY_FILE_INFO ByteCount fields Currently the trans2 ByteCount field is incorrectly left zero in TRANS2_QUERY_FILE_INFO info_level=SMB_QUERY_FILE_ALL_INFO and info_level=SMB_QUERY_FILE_UNIX_BASIC requests. The field should properly reflect the FID, information_level and padding bytes carried in these requests. Leaving this field zero causes such requests to fail against Novell CIFS servers. Other SMB servers (e.g. Samba) use the parameter count fields for data length calculations instead, so do not suffer the same fate. Signed-off-by: David Disseldorp Acked-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifssmb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index bc7dfa857384..a89c4cb4e6cf 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -3930,6 +3930,7 @@ QFileInfoRetry: pSMB->Pad = 0; pSMB->Fid = netfid; inc_rfc1001_len(pSMB, byte_count); + pSMB->t2.ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); @@ -4098,6 +4099,7 @@ UnixQFileInfoRetry: pSMB->Pad = 0; pSMB->Fid = netfid; inc_rfc1001_len(pSMB, byte_count); + pSMB->t2.ByteCount = cpu_to_le16(byte_count); rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); -- cgit v1.2.3 From 1ca01512a2a95aa061c3fc24b7c5d7fad9f606bf Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Thu, 27 Jun 2013 20:53:42 +0800 Subject: net/trivial: replace numeric with standard PM state macros Use standard PM state macros PCI_Dx instead of numeric 0/1/2.. Signed-off-by: Yijing Wang Cc: netdev@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: David S. Miller --- drivers/net/ethernet/8390/ne2k-pci.c | 2 +- drivers/net/ethernet/emulex/benet/be_main.c | 4 ++-- drivers/net/ethernet/intel/e100.c | 4 ++-- drivers/net/ethernet/myricom/myri10ge/myri10ge.c | 2 +- drivers/net/ethernet/realtek/8139cp.c | 2 +- drivers/net/ethernet/ti/tlan.c | 2 +- drivers/net/ethernet/via/via-velocity.c | 2 +- drivers/net/wireless/orinoco/orinoco_pci.h | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/8390/ne2k-pci.c b/drivers/net/ethernet/8390/ne2k-pci.c index 587a885de259..92201080e07a 100644 --- a/drivers/net/ethernet/8390/ne2k-pci.c +++ b/drivers/net/ethernet/8390/ne2k-pci.c @@ -676,7 +676,7 @@ static int ne2k_pci_resume (struct pci_dev *pdev) struct net_device *dev = pci_get_drvdata (pdev); int rc; - pci_set_power_state(pdev, 0); + pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); rc = pci_enable_device(pdev); diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index cd69ac79f565..2df48bb0f1ca 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -4364,7 +4364,7 @@ static int be_resume(struct pci_dev *pdev) if (status) return status; - pci_set_power_state(pdev, 0); + pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); /* tell fw we're ready to fire cmds */ @@ -4460,7 +4460,7 @@ static pci_ers_result_t be_eeh_reset(struct pci_dev *pdev) return PCI_ERS_RESULT_DISCONNECT; pci_set_master(pdev); - pci_set_power_state(pdev, 0); + pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); /* Check if card is ok and fw is ready */ diff --git a/drivers/net/ethernet/intel/e100.c b/drivers/net/ethernet/intel/e100.c index d2bea3f07c73..5115ae76a5d1 100644 --- a/drivers/net/ethernet/intel/e100.c +++ b/drivers/net/ethernet/intel/e100.c @@ -3069,7 +3069,7 @@ static int e100_resume(struct pci_dev *pdev) pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); /* ack any pending wake events, disable PME */ - pci_enable_wake(pdev, 0, 0); + pci_enable_wake(pdev, PCI_D0, 0); /* disable reverse auto-negotiation */ if (nic->phy == phy_82552_v) { @@ -3160,7 +3160,7 @@ static void e100_io_resume(struct pci_dev *pdev) struct nic *nic = netdev_priv(netdev); /* ack any pending wake events, disable PME */ - pci_enable_wake(pdev, 0, 0); + pci_enable_wake(pdev, PCI_D0, 0); netif_device_attach(netdev); if (netif_running(netdev)) { diff --git a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c index 7be9788ed0f6..967bae8b85c5 100644 --- a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c +++ b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c @@ -3299,7 +3299,7 @@ static int myri10ge_resume(struct pci_dev *pdev) if (mgp == NULL) return -EINVAL; netdev = mgp->dev; - pci_set_power_state(pdev, 0); /* zeros conf space as a side effect */ + pci_set_power_state(pdev, PCI_D0); /* zeros conf space as a side effect */ msleep(5); /* give card time to respond */ pci_read_config_word(mgp->pdev, PCI_VENDOR_ID, &vendor); if (vendor == 0xffff) { diff --git a/drivers/net/ethernet/realtek/8139cp.c b/drivers/net/ethernet/realtek/8139cp.c index 03523459c406..e6acb9fa5767 100644 --- a/drivers/net/ethernet/realtek/8139cp.c +++ b/drivers/net/ethernet/realtek/8139cp.c @@ -1817,7 +1817,7 @@ static int cp_set_eeprom(struct net_device *dev, /* Put the board into D3cold state and wait for WakeUp signal */ static void cp_set_d3_state (struct cp_private *cp) { - pci_enable_wake (cp->pdev, 0, 1); /* Enable PME# generation */ + pci_enable_wake(cp->pdev, PCI_D0, 1); /* Enable PME# generation */ pci_set_power_state (cp->pdev, PCI_D3hot); } diff --git a/drivers/net/ethernet/ti/tlan.c b/drivers/net/ethernet/ti/tlan.c index 59abfbcd0d55..591437e59b90 100644 --- a/drivers/net/ethernet/ti/tlan.c +++ b/drivers/net/ethernet/ti/tlan.c @@ -372,7 +372,7 @@ static int tlan_resume(struct pci_dev *pdev) pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); - pci_enable_wake(pdev, 0, 0); + pci_enable_wake(pdev, PCI_D0, 0); netif_device_attach(dev); if (netif_running(dev)) diff --git a/drivers/net/ethernet/via/via-velocity.c b/drivers/net/ethernet/via/via-velocity.c index 76919948b4ee..1d6dc41f755d 100644 --- a/drivers/net/ethernet/via/via-velocity.c +++ b/drivers/net/ethernet/via/via-velocity.c @@ -3211,7 +3211,7 @@ static int velocity_resume(struct device *dev) velocity_set_power_state(vptr, PCI_D0); if (vptr->pdev) { - pci_enable_wake(vptr->pdev, 0, 0); + pci_enable_wake(vptr->pdev, PCI_D0, 0); pci_restore_state(vptr->pdev); } diff --git a/drivers/net/wireless/orinoco/orinoco_pci.h b/drivers/net/wireless/orinoco/orinoco_pci.h index ea7231af40a8..43f5b9f5a0b0 100644 --- a/drivers/net/wireless/orinoco/orinoco_pci.h +++ b/drivers/net/wireless/orinoco/orinoco_pci.h @@ -38,7 +38,7 @@ static int orinoco_pci_resume(struct pci_dev *pdev) struct net_device *dev = priv->ndev; int err; - pci_set_power_state(pdev, 0); + pci_set_power_state(pdev, PCI_D0); err = pci_enable_device(pdev); if (err) { printk(KERN_ERR "%s: pci_enable_device failed on resume\n", -- cgit v1.2.3 From d36a21da415b8e6545ae8b4eb6b23eea2ce001c8 Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Thu, 27 Jun 2013 21:00:11 +0800 Subject: ssb/trivial: replace numeric with standard PM state macros Use standard PM state macros PCI_Dx instead of numeric 0/1/2.. Signed-off-by: Yijing Wang Cc: netdev@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: David S. Miller --- drivers/ssb/pcihost_wrapper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ssb/pcihost_wrapper.c b/drivers/ssb/pcihost_wrapper.c index 32ed1fa4a82e..69161bbc4d0b 100644 --- a/drivers/ssb/pcihost_wrapper.c +++ b/drivers/ssb/pcihost_wrapper.c @@ -38,7 +38,7 @@ static int ssb_pcihost_resume(struct pci_dev *dev) struct ssb_bus *ssb = pci_get_drvdata(dev); int err; - pci_set_power_state(dev, 0); + pci_set_power_state(dev, PCI_D0); err = pci_enable_device(dev); if (err) return err; -- cgit v1.2.3 From 0888c321de70d93f8e15614073af6c3ef56088b1 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 5 Jun 2013 14:09:55 -0400 Subject: pcm_native: switch to fdget()/fdput() Signed-off-by: Al Viro --- sound/core/pcm_native.c | 40 +++++++++++++++------------------------- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index f92818155958..a68d4c6d702c 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -1589,29 +1589,16 @@ static int snd_pcm_drop(struct snd_pcm_substream *substream) } -/* WARNING: Don't forget to fput back the file */ -static struct file *snd_pcm_file_fd(int fd, int *fput_needed) +static bool is_pcm_file(struct file *file) { - struct file *file; - struct inode *inode; + struct inode *inode = file_inode(file); unsigned int minor; - file = fget_light(fd, fput_needed); - if (!file) - return NULL; - inode = file_inode(file); - if (!S_ISCHR(inode->i_mode) || - imajor(inode) != snd_major) { - fput_light(file, *fput_needed); - return NULL; - } + if (!S_ISCHR(inode->i_mode) || imajor(inode) != snd_major) + return false; minor = iminor(inode); - if (!snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_PLAYBACK) && - !snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_CAPTURE)) { - fput_light(file, *fput_needed); - return NULL; - } - return file; + return snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_PLAYBACK) || + snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_CAPTURE); } /* @@ -1620,16 +1607,18 @@ static struct file *snd_pcm_file_fd(int fd, int *fput_needed) static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) { int res = 0; - struct file *file; struct snd_pcm_file *pcm_file; struct snd_pcm_substream *substream1; struct snd_pcm_group *group; - int fput_needed; + struct fd f = fdget(fd); - file = snd_pcm_file_fd(fd, &fput_needed); - if (!file) + if (!f.file) return -EBADFD; - pcm_file = file->private_data; + if (!is_pcm_file(f.file)) { + res = -EBADFD; + goto _badf; + } + pcm_file = f.file->private_data; substream1 = pcm_file->substream; group = kmalloc(sizeof(*group), GFP_KERNEL); if (!group) { @@ -1663,8 +1652,9 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) up_write(&snd_pcm_link_rwsem); _nolock: snd_card_unref(substream1->pcm->card); - fput_light(file, fput_needed); kfree(group); + _badf: + fdput(f); return res; } -- cgit v1.2.3 From bc77daa783afcc56004d4ed3582983b234e01872 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 6 Jun 2013 09:12:33 -0400 Subject: do_last(): fix missing checks for LAST_BIND case /proc/self/cwd with O_CREAT should fail with EISDIR. /proc/self/exe, OTOH, should fail with ENOTDIR when opened with O_DIRECTORY. Signed-off-by: Al Viro --- fs/namei.c | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 9ed9361223c0..1bc7b7582a66 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2690,28 +2690,10 @@ static int do_last(struct nameidata *nd, struct path *path, nd->flags &= ~LOOKUP_PARENT; nd->flags |= op->intent; - switch (nd->last_type) { - case LAST_DOTDOT: - case LAST_DOT: + if (nd->last_type != LAST_NORM) { error = handle_dots(nd, nd->last_type); if (error) return error; - /* fallthrough */ - case LAST_ROOT: - error = complete_walk(nd); - if (error) - return error; - audit_inode(name, nd->path.dentry, 0); - if (open_flag & O_CREAT) { - error = -EISDIR; - goto out; - } - goto finish_open; - case LAST_BIND: - error = complete_walk(nd); - if (error) - return error; - audit_inode(name, dir, 0); goto finish_open; } @@ -2841,19 +2823,19 @@ finish_lookup: } nd->inode = inode; /* Why this, you ask? _Now_ we might have grown LOOKUP_JUMPED... */ +finish_open: error = complete_walk(nd); if (error) { path_put(&save_parent); return error; } + audit_inode(name, nd->path.dentry, 0); error = -EISDIR; if ((open_flag & O_CREAT) && S_ISDIR(nd->inode->i_mode)) goto out; error = -ENOTDIR; if ((nd->flags & LOOKUP_DIRECTORY) && !can_lookup(nd->inode)) goto out; - audit_inode(name, nd->path.dentry, 0); -finish_open: if (!S_ISREG(nd->inode->i_mode)) will_truncate = false; -- cgit v1.2.3 From 500368f7fbdd888038d3dda579823e9515f63a3b Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 23 May 2013 20:07:11 -0400 Subject: lift file_*_write out of do_splice_from() Signed-off-by: Al Viro --- fs/splice.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/fs/splice.c b/fs/splice.c index 9eca476227d5..fed5134f761c 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -1115,10 +1115,7 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, else splice_write = default_file_splice_write; - file_start_write(out); - ret = splice_write(pipe, out, ppos, len, flags); - file_end_write(out); - return ret; + return splice_write(pipe, out, ppos, len, flags); } /* @@ -1306,9 +1303,11 @@ long do_splice_direct(struct file *in, loff_t *ppos, struct file *out, }; long ret; + file_start_write(out); ret = splice_direct_to_actor(in, &sd, direct_splice_actor); if (ret > 0) *ppos = sd.pos; + file_end_write(out); return ret; } @@ -1361,7 +1360,9 @@ static long do_splice(struct file *in, loff_t __user *off_in, offset = out->f_pos; } + file_start_write(out); ret = do_splice_from(ipipe, out, &offset, len, flags); + file_end_write(out); if (!off_out) out->f_pos = offset; -- cgit v1.2.3 From 50cd2c577668a170750b15f9a88f022f681ce3c7 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 23 May 2013 20:10:34 -0400 Subject: lift file_*_write out of do_splice_direct() Signed-off-by: Al Viro --- fs/read_write.c | 2 ++ fs/splice.c | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/read_write.c b/fs/read_write.c index 2cefa417be34..abca7437d192 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -1129,7 +1129,9 @@ static ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos, if (in.file->f_flags & O_NONBLOCK) fl = SPLICE_F_NONBLOCK; #endif + file_start_write(out.file); retval = do_splice_direct(in.file, &pos, out.file, &out_pos, count, fl); + file_end_write(out.file); if (retval > 0) { add_rchar(current, retval); diff --git a/fs/splice.c b/fs/splice.c index fed5134f761c..78b3d12046e7 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -1303,11 +1303,9 @@ long do_splice_direct(struct file *in, loff_t *ppos, struct file *out, }; long ret; - file_start_write(out); ret = splice_direct_to_actor(in, &sd, direct_splice_actor); if (ret > 0) *ppos = sd.pos; - file_end_write(out); return ret; } -- cgit v1.2.3 From f9652e10c12b43d9bb957269745cf2fa5682fa92 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 11 Jun 2013 08:23:01 +0400 Subject: allow build_open_flags() to return an error Signed-off-by: Al Viro --- fs/exec.c | 10 ++++++---- fs/internal.h | 5 +++-- fs/namei.c | 8 ++++---- fs/open.c | 49 ++++++++++++++++++++++++++++--------------------- 4 files changed, 41 insertions(+), 31 deletions(-) diff --git a/fs/exec.c b/fs/exec.c index 643019585574..0f6c96c57b2f 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -110,13 +110,14 @@ SYSCALL_DEFINE1(uselib, const char __user *, library) static const struct open_flags uselib_flags = { .open_flag = O_LARGEFILE | O_RDONLY | __FMODE_EXEC, .acc_mode = MAY_READ | MAY_EXEC | MAY_OPEN, - .intent = LOOKUP_OPEN + .intent = LOOKUP_OPEN, + .lookup_flags = LOOKUP_FOLLOW, }; if (IS_ERR(tmp)) goto out; - file = do_filp_open(AT_FDCWD, tmp, &uselib_flags, LOOKUP_FOLLOW); + file = do_filp_open(AT_FDCWD, tmp, &uselib_flags); putname(tmp); error = PTR_ERR(file); if (IS_ERR(file)) @@ -756,10 +757,11 @@ struct file *open_exec(const char *name) static const struct open_flags open_exec_flags = { .open_flag = O_LARGEFILE | O_RDONLY | __FMODE_EXEC, .acc_mode = MAY_EXEC | MAY_OPEN, - .intent = LOOKUP_OPEN + .intent = LOOKUP_OPEN, + .lookup_flags = LOOKUP_FOLLOW, }; - file = do_filp_open(AT_FDCWD, &tmp, &open_exec_flags, LOOKUP_FOLLOW); + file = do_filp_open(AT_FDCWD, &tmp, &open_exec_flags); if (IS_ERR(file)) goto out; diff --git a/fs/internal.h b/fs/internal.h index 68121584ae37..f6ad34362823 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -96,11 +96,12 @@ struct open_flags { umode_t mode; int acc_mode; int intent; + int lookup_flags; }; extern struct file *do_filp_open(int dfd, struct filename *pathname, - const struct open_flags *op, int flags); + const struct open_flags *op); extern struct file *do_file_open_root(struct dentry *, struct vfsmount *, - const char *, const struct open_flags *, int lookup_flags); + const char *, const struct open_flags *); extern long do_handle_open(int mountdirfd, struct file_handle __user *ufh, int open_flag); diff --git a/fs/namei.c b/fs/namei.c index 1bc7b7582a66..402eda351d07 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2969,9 +2969,10 @@ out: } struct file *do_filp_open(int dfd, struct filename *pathname, - const struct open_flags *op, int flags) + const struct open_flags *op) { struct nameidata nd; + int flags = op->lookup_flags; struct file *filp; filp = path_openat(dfd, pathname, &nd, op, flags | LOOKUP_RCU); @@ -2983,17 +2984,16 @@ struct file *do_filp_open(int dfd, struct filename *pathname, } struct file *do_file_open_root(struct dentry *dentry, struct vfsmount *mnt, - const char *name, const struct open_flags *op, int flags) + const char *name, const struct open_flags *op) { struct nameidata nd; struct file *file; struct filename filename = { .name = name }; + int flags = op->lookup_flags | LOOKUP_ROOT; nd.root.mnt = mnt; nd.root.dentry = dentry; - flags |= LOOKUP_ROOT; - if (dentry->d_inode->i_op->follow_link && op->intent & LOOKUP_OPEN) return ERR_PTR(-ELOOP); diff --git a/fs/open.c b/fs/open.c index 8c741002f947..5a40a4a51757 100644 --- a/fs/open.c +++ b/fs/open.c @@ -876,7 +876,8 @@ static inline int build_open_flags(int flags, umode_t mode, struct open_flags *o lookup_flags |= LOOKUP_DIRECTORY; if (!(flags & O_NOFOLLOW)) lookup_flags |= LOOKUP_FOLLOW; - return lookup_flags; + op->lookup_flags = lookup_flags; + return 0; } /** @@ -893,8 +894,8 @@ static inline int build_open_flags(int flags, umode_t mode, struct open_flags *o struct file *file_open_name(struct filename *name, int flags, umode_t mode) { struct open_flags op; - int lookup = build_open_flags(flags, mode, &op); - return do_filp_open(AT_FDCWD, name, &op, lookup); + int err = build_open_flags(flags, mode, &op); + return err ? ERR_PTR(err) : do_filp_open(AT_FDCWD, name, &op); } /** @@ -919,37 +920,43 @@ struct file *file_open_root(struct dentry *dentry, struct vfsmount *mnt, const char *filename, int flags) { struct open_flags op; - int lookup = build_open_flags(flags, 0, &op); + int err = build_open_flags(flags, 0, &op); + if (err) + return ERR_PTR(err); if (flags & O_CREAT) return ERR_PTR(-EINVAL); if (!filename && (flags & O_DIRECTORY)) if (!dentry->d_inode->i_op->lookup) return ERR_PTR(-ENOTDIR); - return do_file_open_root(dentry, mnt, filename, &op, lookup); + return do_file_open_root(dentry, mnt, filename, &op); } EXPORT_SYMBOL(file_open_root); long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode) { struct open_flags op; - int lookup = build_open_flags(flags, mode, &op); - struct filename *tmp = getname(filename); - int fd = PTR_ERR(tmp); - - if (!IS_ERR(tmp)) { - fd = get_unused_fd_flags(flags); - if (fd >= 0) { - struct file *f = do_filp_open(dfd, tmp, &op, lookup); - if (IS_ERR(f)) { - put_unused_fd(fd); - fd = PTR_ERR(f); - } else { - fsnotify_open(f); - fd_install(fd, f); - } + int fd = build_open_flags(flags, mode, &op); + struct filename *tmp; + + if (fd) + return fd; + + tmp = getname(filename); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + + fd = get_unused_fd_flags(flags); + if (fd >= 0) { + struct file *f = do_filp_open(dfd, tmp, &op); + if (IS_ERR(f)) { + put_unused_fd(fd); + fd = PTR_ERR(f); + } else { + fsnotify_open(f); + fd_install(fd, f); } - putname(tmp); } + putname(tmp); return fd; } -- cgit v1.2.3 From 60545d0d4610b02e55f65d141c95b18ccf855b6e Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 7 Jun 2013 01:20:27 -0400 Subject: [O_TMPFILE] it's still short a few helpers, but infrastructure should be OK now... Signed-off-by: Al Viro --- arch/alpha/include/uapi/asm/fcntl.h | 1 + arch/parisc/include/uapi/asm/fcntl.h | 1 + arch/sparc/include/uapi/asm/fcntl.h | 1 + fs/dcache.c | 16 ++++++++++ fs/ext2/namei.c | 24 +++++++++++++++ fs/minix/namei.c | 13 ++++++++ fs/namei.c | 60 ++++++++++++++++++++++++++++++++++++ fs/open.c | 14 ++++++--- include/linux/dcache.h | 2 ++ include/linux/fs.h | 1 + include/uapi/asm-generic/fcntl.h | 4 +++ mm/shmem.c | 32 +++++++++++++++++++ 12 files changed, 164 insertions(+), 5 deletions(-) diff --git a/arch/alpha/include/uapi/asm/fcntl.h b/arch/alpha/include/uapi/asm/fcntl.h index 6d9e805f18a7..dfdadb0b4bef 100644 --- a/arch/alpha/include/uapi/asm/fcntl.h +++ b/arch/alpha/include/uapi/asm/fcntl.h @@ -32,6 +32,7 @@ #define O_SYNC (__O_SYNC|O_DSYNC) #define O_PATH 040000000 +#define O_TMPFILE 0100000000 #define F_GETLK 7 #define F_SETLK 8 diff --git a/arch/parisc/include/uapi/asm/fcntl.h b/arch/parisc/include/uapi/asm/fcntl.h index 0304b92ccfea..cc61c475f277 100644 --- a/arch/parisc/include/uapi/asm/fcntl.h +++ b/arch/parisc/include/uapi/asm/fcntl.h @@ -20,6 +20,7 @@ #define O_INVISIBLE 004000000 /* invisible I/O, for DMAPI/XDSM */ #define O_PATH 020000000 +#define O_TMPFILE 040000000 #define F_GETLK64 8 #define F_SETLK64 9 diff --git a/arch/sparc/include/uapi/asm/fcntl.h b/arch/sparc/include/uapi/asm/fcntl.h index d0b83f66f356..d73e5e008b0d 100644 --- a/arch/sparc/include/uapi/asm/fcntl.h +++ b/arch/sparc/include/uapi/asm/fcntl.h @@ -35,6 +35,7 @@ #define O_SYNC (__O_SYNC|O_DSYNC) #define O_PATH 0x1000000 +#define O_TMPFILE 0x2000000 #define F_GETOWN 5 /* for sockets. */ #define F_SETOWN 6 /* for sockets. */ diff --git a/fs/dcache.c b/fs/dcache.c index f09b9085f7d8..b7f049c31526 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -2968,6 +2968,22 @@ rename_retry: goto again; } +void d_tmpfile(struct dentry *dentry, struct inode *inode) +{ + inode_dec_link_count(inode); + BUG_ON(dentry->d_name.name != dentry->d_iname || + !hlist_unhashed(&dentry->d_alias) || + !d_unlinked(dentry)); + spin_lock(&dentry->d_parent->d_lock); + spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); + dentry->d_name.len = sprintf(dentry->d_iname, "#%llu", + (unsigned long long)inode->i_ino); + spin_unlock(&dentry->d_lock); + spin_unlock(&dentry->d_parent->d_lock); + d_instantiate(dentry, inode); +} +EXPORT_SYMBOL(d_tmpfile); + /** * find_inode_number - check for dentry with name * @dir: directory to check diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index 73b0d9519836..256dd5f4c1c4 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -119,6 +119,29 @@ static int ext2_create (struct inode * dir, struct dentry * dentry, umode_t mode return ext2_add_nondir(dentry, inode); } +static int ext2_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) +{ + struct inode *inode = ext2_new_inode(dir, mode, NULL); + if (IS_ERR(inode)) + return PTR_ERR(inode); + + inode->i_op = &ext2_file_inode_operations; + if (ext2_use_xip(inode->i_sb)) { + inode->i_mapping->a_ops = &ext2_aops_xip; + inode->i_fop = &ext2_xip_file_operations; + } else if (test_opt(inode->i_sb, NOBH)) { + inode->i_mapping->a_ops = &ext2_nobh_aops; + inode->i_fop = &ext2_file_operations; + } else { + inode->i_mapping->a_ops = &ext2_aops; + inode->i_fop = &ext2_file_operations; + } + mark_inode_dirty(inode); + d_tmpfile(dentry, inode); + unlock_new_inode(inode); + return 0; +} + static int ext2_mknod (struct inode * dir, struct dentry *dentry, umode_t mode, dev_t rdev) { struct inode * inode; @@ -398,6 +421,7 @@ const struct inode_operations ext2_dir_inode_operations = { #endif .setattr = ext2_setattr, .get_acl = ext2_get_acl, + .tmpfile = ext2_tmpfile, }; const struct inode_operations ext2_special_inode_operations = { diff --git a/fs/minix/namei.c b/fs/minix/namei.c index 0db73d9dd668..cd950e2331b6 100644 --- a/fs/minix/namei.c +++ b/fs/minix/namei.c @@ -54,6 +54,18 @@ static int minix_mknod(struct inode * dir, struct dentry *dentry, umode_t mode, return error; } +static int minix_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) +{ + int error; + struct inode *inode = minix_new_inode(dir, mode, &error); + if (inode) { + minix_set_inode(inode, 0); + mark_inode_dirty(inode); + d_tmpfile(dentry, inode); + } + return error; +} + static int minix_create(struct inode *dir, struct dentry *dentry, umode_t mode, bool excl) { @@ -254,4 +266,5 @@ const struct inode_operations minix_dir_inode_operations = { .mknod = minix_mknod, .rename = minix_rename, .getattr = minix_getattr, + .tmpfile = minix_tmpfile, }; diff --git a/fs/namei.c b/fs/namei.c index 402eda351d07..778e253e3d48 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2902,6 +2902,61 @@ stale_open: goto retry_lookup; } +static int do_tmpfile(int dfd, struct filename *pathname, + struct nameidata *nd, int flags, + const struct open_flags *op, + struct file *file, int *opened) +{ + static const struct qstr name = QSTR_INIT("/", 1); + struct dentry *dentry, *child; + struct inode *dir; + int error = path_lookupat(dfd, pathname->name, + flags | LOOKUP_DIRECTORY, nd); + if (unlikely(error)) + return error; + error = mnt_want_write(nd->path.mnt); + if (unlikely(error)) + goto out; + /* we want directory to be writable */ + error = inode_permission(nd->inode, MAY_WRITE | MAY_EXEC); + if (error) + goto out2; + dentry = nd->path.dentry; + dir = dentry->d_inode; + if (!dir->i_op->tmpfile) { + error = -EOPNOTSUPP; + goto out2; + } + child = d_alloc(dentry, &name); + if (unlikely(!child)) { + error = -ENOMEM; + goto out2; + } + nd->flags &= ~LOOKUP_DIRECTORY; + nd->flags |= op->intent; + dput(nd->path.dentry); + nd->path.dentry = child; + error = dir->i_op->tmpfile(dir, nd->path.dentry, op->mode); + if (error) + goto out2; + audit_inode(pathname, nd->path.dentry, 0); + error = may_open(&nd->path, op->acc_mode, op->open_flag); + if (error) + goto out2; + file->f_path.mnt = nd->path.mnt; + error = finish_open(file, nd->path.dentry, NULL, opened); + if (error) + goto out2; + error = open_check_o_direct(file); + if (error) + fput(file); +out2: + mnt_drop_write(nd->path.mnt); +out: + path_put(&nd->path); + return error; +} + static struct file *path_openat(int dfd, struct filename *pathname, struct nameidata *nd, const struct open_flags *op, int flags) { @@ -2917,6 +2972,11 @@ static struct file *path_openat(int dfd, struct filename *pathname, file->f_flags = op->open_flag; + if (unlikely(file->f_flags & O_TMPFILE)) { + error = do_tmpfile(dfd, pathname, nd, flags, op, file, &opened); + goto out; + } + error = path_init(dfd, pathname->name, flags | LOOKUP_PARENT, nd, &base); if (unlikely(error)) goto out; diff --git a/fs/open.c b/fs/open.c index 5a40a4a51757..fca72c4d3f17 100644 --- a/fs/open.c +++ b/fs/open.c @@ -840,11 +840,15 @@ static inline int build_open_flags(int flags, umode_t mode, struct open_flags *o if (flags & __O_SYNC) flags |= O_DSYNC; - /* - * If we have O_PATH in the open flag. Then we - * cannot have anything other than the below set of flags - */ - if (flags & O_PATH) { + if (flags & O_TMPFILE) { + if (!(flags & O_CREAT)) + return -EINVAL; + acc_mode = MAY_OPEN | ACC_MODE(flags); + } else if (flags & O_PATH) { + /* + * If we have O_PATH in the open flag. Then we + * cannot have anything other than the below set of flags + */ flags &= O_DIRECTORY | O_NOFOLLOW | O_PATH; acc_mode = 0; } else { diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 1a6bb81f0fe5..86da7595ba31 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -246,6 +246,8 @@ extern struct dentry * d_make_root(struct inode *); /* - the ramfs-type tree */ extern void d_genocide(struct dentry *); +extern void d_tmpfile(struct dentry *, struct inode *); + extern struct dentry *d_find_alias(struct inode *); extern void d_prune_aliases(struct inode *); diff --git a/include/linux/fs.h b/include/linux/fs.h index 7c30e3a62baf..dd6615f0fd13 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1580,6 +1580,7 @@ struct inode_operations { int (*atomic_open)(struct inode *, struct dentry *, struct file *, unsigned open_flag, umode_t create_mode, int *opened); + int (*tmpfile) (struct inode *, struct dentry *, umode_t); } ____cacheline_aligned; ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector, diff --git a/include/uapi/asm-generic/fcntl.h b/include/uapi/asm-generic/fcntl.h index a48937d4a5ea..06632beaa6d5 100644 --- a/include/uapi/asm-generic/fcntl.h +++ b/include/uapi/asm-generic/fcntl.h @@ -84,6 +84,10 @@ #define O_PATH 010000000 #endif +#ifndef O_TMPFILE +#define O_TMPFILE 020000000 +#endif + #ifndef O_NDELAY #define O_NDELAY O_NONBLOCK #endif diff --git a/mm/shmem.c b/mm/shmem.c index 5e6a8422658b..f887358dabc5 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -1965,6 +1965,37 @@ shmem_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev) return error; } +static int +shmem_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) +{ + struct inode *inode; + int error = -ENOSPC; + + inode = shmem_get_inode(dir->i_sb, dir, mode, 0, VM_NORESERVE); + if (inode) { + error = security_inode_init_security(inode, dir, + NULL, + shmem_initxattrs, NULL); + if (error) { + if (error != -EOPNOTSUPP) { + iput(inode); + return error; + } + } +#ifdef CONFIG_TMPFS_POSIX_ACL + error = generic_acl_init(inode, dir); + if (error) { + iput(inode); + return error; + } +#else + error = 0; +#endif + d_tmpfile(dentry, inode); + } + return error; +} + static int shmem_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) { int error; @@ -2723,6 +2754,7 @@ static const struct inode_operations shmem_dir_inode_operations = { .rmdir = shmem_rmdir, .mknod = shmem_mknod, .rename = shmem_rename, + .tmpfile = shmem_tmpfile, #endif #ifdef CONFIG_TMPFS_XATTR .setxattr = shmem_setxattr, -- cgit v1.2.3 From f4e0c30c191f87851c4a53454abb55ee276f4a7e Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 11 Jun 2013 08:34:36 +0400 Subject: allow the temp files created by open() to be linked to O_TMPFILE | O_CREAT => linkat() with AT_SYMLINK_FOLLOW and /proc/self/fd/ as oldpath (i.e. flink()) will create a link O_TMPFILE | O_CREAT | O_EXCL => ENOENT on attempt to link those guys Signed-off-by: Al Viro --- fs/inode.c | 4 +++- fs/namei.c | 16 ++++++++++++++-- include/linux/fs.h | 1 + 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/fs/inode.c b/fs/inode.c index 00d5fc3b86e1..d6dfb09c8280 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -333,8 +333,10 @@ EXPORT_SYMBOL(set_nlink); */ void inc_nlink(struct inode *inode) { - if (WARN_ON(inode->i_nlink == 0)) + if (unlikely(inode->i_nlink == 0)) { + WARN_ON(!(inode->i_state & I_LINKABLE)); atomic_long_dec(&inode->i_sb->s_remove_count); + } inode->__i_nlink++; } diff --git a/fs/namei.c b/fs/namei.c index 778e253e3d48..66998b06d822 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2948,8 +2948,14 @@ static int do_tmpfile(int dfd, struct filename *pathname, if (error) goto out2; error = open_check_o_direct(file); - if (error) + if (error) { fput(file); + } else if (!(op->open_flag & O_EXCL)) { + struct inode *inode = file_inode(file); + spin_lock(&inode->i_lock); + inode->i_state |= I_LINKABLE; + spin_unlock(&inode->i_lock); + } out2: mnt_drop_write(nd->path.mnt); out: @@ -3628,12 +3634,18 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de mutex_lock(&inode->i_mutex); /* Make sure we don't allow creating hardlink to an unlinked file */ - if (inode->i_nlink == 0) + if (inode->i_nlink == 0 && !(inode->i_state & I_LINKABLE)) error = -ENOENT; else if (max_links && inode->i_nlink >= max_links) error = -EMLINK; else error = dir->i_op->link(old_dentry, dir, new_dentry); + + if (!error && (inode->i_state & I_LINKABLE)) { + spin_lock(&inode->i_lock); + inode->i_state &= ~I_LINKABLE; + spin_unlock(&inode->i_lock); + } mutex_unlock(&inode->i_mutex); if (!error) fsnotify_link(dir, inode, new_dentry); diff --git a/include/linux/fs.h b/include/linux/fs.h index dd6615f0fd13..ab11c44b0697 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1744,6 +1744,7 @@ struct super_operations { #define I_REFERENCED (1 << 8) #define __I_DIO_WAKEUP 9 #define I_DIO_WAKEUP (1 << I_DIO_WAKEUP) +#define I_LINKABLE (1 << 10) #define I_DIRTY (I_DIRTY_SYNC | I_DIRTY_DATASYNC | I_DIRTY_PAGES) -- cgit v1.2.3 From e6bbef95429374fd3cac81c36b5894f55b2612dc Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 11 Jun 2013 12:52:02 +0400 Subject: ext3 ->tmpfile() support In this case we do need a bit more than usual, due to orphan list handling. Signed-off-by: Al Viro --- fs/ext3/namei.c | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c index 692de13e3596..7523c61f796c 100644 --- a/fs/ext3/namei.c +++ b/fs/ext3/namei.c @@ -1762,6 +1762,45 @@ retry: return err; } +static int ext3_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) +{ + handle_t *handle; + struct inode *inode; + int err, retries = 0; + + dquot_initialize(dir); + +retry: + handle = ext3_journal_start(dir, EXT3_MAXQUOTAS_INIT_BLOCKS(dir->i_sb) + + 4 + EXT3_XATTR_TRANS_BLOCKS); + + if (IS_ERR(handle)) + return PTR_ERR(handle); + + inode = ext3_new_inode (handle, dir, NULL, mode); + err = PTR_ERR(inode); + if (!IS_ERR(inode)) { + inode->i_op = &ext3_file_inode_operations; + inode->i_fop = &ext3_file_operations; + ext3_set_aops(inode); + err = ext3_orphan_add(handle, inode); + if (err) + goto err_drop_inode; + mark_inode_dirty(inode); + d_tmpfile(dentry, inode); + unlock_new_inode(inode); + } + ext3_journal_stop(handle); + if (err == -ENOSPC && ext3_should_retry_alloc(dir->i_sb, &retries)) + goto retry; + return err; +err_drop_inode: + ext3_journal_stop(handle); + unlock_new_inode(inode); + iput(inode); + return err; +} + static int ext3_mkdir(struct inode * dir, struct dentry * dentry, umode_t mode) { handle_t *handle; @@ -2303,7 +2342,7 @@ static int ext3_link (struct dentry * old_dentry, retry: handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS(dir->i_sb) + - EXT3_INDEX_EXTRA_TRANS_BLOCKS); + EXT3_INDEX_EXTRA_TRANS_BLOCKS + 1); if (IS_ERR(handle)) return PTR_ERR(handle); @@ -2317,6 +2356,11 @@ retry: err = ext3_add_entry(handle, dentry, inode); if (!err) { ext3_mark_inode_dirty(handle, inode); + /* this can happen only for tmpfile being + * linked the first time + */ + if (inode->i_nlink == 1) + ext3_orphan_del(handle, inode); d_instantiate(dentry, inode); } else { drop_nlink(inode); @@ -2519,6 +2563,7 @@ const struct inode_operations ext3_dir_inode_operations = { .mkdir = ext3_mkdir, .rmdir = ext3_rmdir, .mknod = ext3_mknod, + .tmpfile = ext3_tmpfile, .rename = ext3_rename, .setattr = ext3_setattr, #ifdef CONFIG_EXT3_FS_XATTR -- cgit v1.2.3 From 656d09df8f73b6f2ae0c7205c6eb79f1642353f8 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 12 Jun 2013 09:35:33 +0400 Subject: udf: provide ->tmpfile() Signed-off-by: Al Viro --- fs/udf/namei.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/fs/udf/namei.c b/fs/udf/namei.c index 102c072c6bbf..5f6fc17d6bc5 100644 --- a/fs/udf/namei.c +++ b/fs/udf/namei.c @@ -594,6 +594,29 @@ static int udf_create(struct inode *dir, struct dentry *dentry, umode_t mode, return 0; } +static int udf_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) +{ + struct inode *inode; + struct udf_inode_info *iinfo; + int err; + + inode = udf_new_inode(dir, mode, &err); + if (!inode) + return err; + + iinfo = UDF_I(inode); + if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) + inode->i_data.a_ops = &udf_adinicb_aops; + else + inode->i_data.a_ops = &udf_aops; + inode->i_op = &udf_file_inode_operations; + inode->i_fop = &udf_file_operations; + mark_inode_dirty(inode); + + d_tmpfile(dentry, inode); + return 0; +} + static int udf_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t rdev) { @@ -1311,6 +1334,7 @@ const struct inode_operations udf_dir_inode_operations = { .rmdir = udf_rmdir, .mknod = udf_mknod, .rename = udf_rename, + .tmpfile = udf_tmpfile, }; const struct inode_operations udf_symlink_inode_operations = { .readlink = generic_readlink, -- cgit v1.2.3 From c77cecee52e9b599da1f8ffd9170d4374c99a345 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 13 Jun 2013 23:37:49 +0100 Subject: Replace a bunch of file->dentry->d_inode refs with file_inode() Replace a bunch of file->dentry->d_inode refs with file_inode(). In __fput(), use file->f_inode instead so as not to be affected by any tricks that file_inode() might grow. Signed-off-by: David Howells Signed-off-by: Al Viro --- fs/file_table.c | 2 +- include/linux/fsnotify.h | 8 ++++---- security/integrity/ima/ima_main.c | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/fs/file_table.c b/fs/file_table.c index 485dc0eddd67..08e719b884ca 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -227,7 +227,7 @@ static void __fput(struct file *file) { struct dentry *dentry = file->f_path.dentry; struct vfsmount *mnt = file->f_path.mnt; - struct inode *inode = dentry->d_inode; + struct inode *inode = file->f_inode; might_sleep(); diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h index a78680a92dba..1c804b057fb1 100644 --- a/include/linux/fsnotify.h +++ b/include/linux/fsnotify.h @@ -38,7 +38,7 @@ static inline int fsnotify_parent(struct path *path, struct dentry *dentry, __u3 static inline int fsnotify_perm(struct file *file, int mask) { struct path *path = &file->f_path; - struct inode *inode = path->dentry->d_inode; + struct inode *inode = file_inode(file); __u32 fsnotify_mask = 0; int ret; @@ -192,7 +192,7 @@ static inline void fsnotify_mkdir(struct inode *inode, struct dentry *dentry) static inline void fsnotify_access(struct file *file) { struct path *path = &file->f_path; - struct inode *inode = path->dentry->d_inode; + struct inode *inode = file_inode(file); __u32 mask = FS_ACCESS; if (S_ISDIR(inode->i_mode)) @@ -210,7 +210,7 @@ static inline void fsnotify_access(struct file *file) static inline void fsnotify_modify(struct file *file) { struct path *path = &file->f_path; - struct inode *inode = path->dentry->d_inode; + struct inode *inode = file_inode(file); __u32 mask = FS_MODIFY; if (S_ISDIR(inode->i_mode)) @@ -228,7 +228,7 @@ static inline void fsnotify_modify(struct file *file) static inline void fsnotify_open(struct file *file) { struct path *path = &file->f_path; - struct inode *inode = path->dentry->d_inode; + struct inode *inode = file_inode(file); __u32 mask = FS_OPEN; if (S_ISDIR(inode->i_mode)) diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 6c491a63128e..e9508d5bbfcf 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -57,7 +57,7 @@ __setup("ima_hash=", hash_setup); static void ima_rdwr_violation_check(struct file *file) { struct dentry *dentry = file->f_path.dentry; - struct inode *inode = dentry->d_inode; + struct inode *inode = file_inode(file); fmode_t mode = file->f_mode; int must_measure; bool send_tomtou = false, send_writers = false; -- cgit v1.2.3 From 13f8e9810bff12d01807b6f92329111f45218235 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 13 Jun 2013 23:37:55 +0100 Subject: SELinux: Institute file_path_has_perm() Create a file_path_has_perm() function that is like path_has_perm() but instead takes a file struct that is the source of both the path and the inode (rather than getting the inode from the dentry in the path). This is then used where appropriate. This will be useful for situations like unionmount where it will be possible to have an apparently-negative dentry (eg. a fallthrough) that is open with the file struct pointing to an inode on the lower fs. Signed-off-by: David Howells Signed-off-by: Al Viro --- security/selinux/hooks.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 5c6f2cd2d095..db1fca990a24 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1547,6 +1547,18 @@ static inline int path_has_perm(const struct cred *cred, return inode_has_perm(cred, inode, av, &ad, 0); } +/* Same as path_has_perm, but uses the inode from the file struct. */ +static inline int file_path_has_perm(const struct cred *cred, + struct file *file, + u32 av) +{ + struct common_audit_data ad; + + ad.type = LSM_AUDIT_DATA_PATH; + ad.u.path = file->f_path; + return inode_has_perm(cred, file_inode(file), av, &ad, 0); +} + /* Check whether a task can use an open file descriptor to access an inode in a given way. Check access to the descriptor itself, and then use dentry_has_perm to @@ -2141,14 +2153,14 @@ static inline void flush_unauthorized_files(const struct cred *cred, struct tty_file_private *file_priv; /* Revalidate access to controlling tty. - Use path_has_perm on the tty path directly rather - than using file_has_perm, as this particular open - file may belong to another process and we are only - interested in the inode-based check here. */ + Use file_path_has_perm on the tty path directly + rather than using file_has_perm, as this particular + open file may belong to another process and we are + only interested in the inode-based check here. */ file_priv = list_first_entry(&tty->tty_files, struct tty_file_private, list); file = file_priv->file; - if (path_has_perm(cred, &file->f_path, FILE__READ | FILE__WRITE)) + if (file_path_has_perm(cred, file, FILE__READ | FILE__WRITE)) drop_tty = 1; } spin_unlock(&tty_files_lock); @@ -3259,7 +3271,7 @@ static int selinux_file_open(struct file *file, const struct cred *cred) * new inode label or new policy. * This check is not redundant - do not remove. */ - return path_has_perm(cred, &file->f_path, open_file_to_av(file)); + return file_path_has_perm(cred, file, open_file_to_av(file)); } /* task security operations */ -- cgit v1.2.3 From 5faf153ebf6128f02ad6ffa2e8bbc9d823ef762c Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 15 Jun 2013 05:49:36 +0400 Subject: don't call file_pos_write() if vfs_{read,write}{,v}() fails Signed-off-by: Al Viro --- fs/read_write.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/fs/read_write.c b/fs/read_write.c index abca7437d192..f646c8b565b9 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -477,7 +477,8 @@ SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count) if (f.file) { loff_t pos = file_pos_read(f.file); ret = vfs_read(f.file, buf, count, &pos); - file_pos_write(f.file, pos); + if (ret >= 0) + file_pos_write(f.file, pos); fdput(f); } return ret; @@ -492,7 +493,8 @@ SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf, if (f.file) { loff_t pos = file_pos_read(f.file); ret = vfs_write(f.file, buf, count, &pos); - file_pos_write(f.file, pos); + if (ret >= 0) + file_pos_write(f.file, pos); fdput(f); } @@ -780,7 +782,8 @@ SYSCALL_DEFINE3(readv, unsigned long, fd, const struct iovec __user *, vec, if (f.file) { loff_t pos = file_pos_read(f.file); ret = vfs_readv(f.file, vec, vlen, &pos); - file_pos_write(f.file, pos); + if (ret >= 0) + file_pos_write(f.file, pos); fdput(f); } @@ -799,7 +802,8 @@ SYSCALL_DEFINE3(writev, unsigned long, fd, const struct iovec __user *, vec, if (f.file) { loff_t pos = file_pos_read(f.file); ret = vfs_writev(f.file, vec, vlen, &pos); - file_pos_write(f.file, pos); + if (ret >= 0) + file_pos_write(f.file, pos); fdput(f); } @@ -959,7 +963,8 @@ COMPAT_SYSCALL_DEFINE3(readv, unsigned long, fd, return -EBADF; pos = f.file->f_pos; ret = compat_readv(f.file, vec, vlen, &pos); - f.file->f_pos = pos; + if (ret >= 0) + f.file->f_pos = pos; fdput(f); return ret; } @@ -1025,7 +1030,8 @@ COMPAT_SYSCALL_DEFINE3(writev, unsigned long, fd, return -EBADF; pos = f.file->f_pos; ret = compat_writev(f.file, vec, vlen, &pos); - f.file->f_pos = pos; + if (ret >= 0) + f.file->f_pos = pos; fdput(f); return ret; } -- cgit v1.2.3 From 338b2f57499f37c18887182b48a499efb8a6b68f Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 15 Jun 2013 05:53:23 +0400 Subject: ncpfs: don't bother with EBUSY on removal of busy directories Signed-off-by: Al Viro --- fs/ncpfs/dir.c | 11 ----------- fs/ncpfs/inode.c | 4 ++++ 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c index 0e7f00298213..e5d488530580 100644 --- a/fs/ncpfs/dir.c +++ b/fs/ncpfs/dir.c @@ -1123,17 +1123,6 @@ static int ncp_rename(struct inode *old_dir, struct dentry *old_dentry, old_dentry->d_parent->d_name.name, old_dentry->d_name.name, new_dentry->d_parent->d_name.name, new_dentry->d_name.name); - if (new_dentry->d_inode && S_ISDIR(new_dentry->d_inode->i_mode)) { - /* - * fail with EBUSY if there are still references to this - * directory. - */ - dentry_unhash(new_dentry); - error = -EBUSY; - if (!d_unhashed(new_dentry)) - goto out; - } - ncp_age_dentry(server, old_dentry); ncp_age_dentry(server, new_dentry); diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c index 26910c8154da..0765ad12c382 100644 --- a/fs/ncpfs/inode.c +++ b/fs/ncpfs/inode.c @@ -891,6 +891,10 @@ int ncp_notify_change(struct dentry *dentry, struct iattr *attr) if (!server) /* How this could happen? */ goto out; + result = -EPERM; + if (IS_DEADDIR(dentry->d_inode)) + goto out; + /* ageing the dentry to force validation */ ncp_age_dentry(server, dentry); -- cgit v1.2.3 From 147ce69974cc1b44defa2d3d9c202ee83e2f2f3b Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 15 Jun 2013 10:26:35 +0400 Subject: proc_fill_cache(): kill pointless check we'd just checked that child->d_inode is non-NULL, for fuck sake! Signed-off-by: Al Viro --- fs/proc/base.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/fs/proc/base.c b/fs/proc/base.c index 0016350ad95e..306419c19681 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -1710,10 +1710,8 @@ bool proc_fill_cache(struct file *file, struct dir_context *ctx, if (!child || IS_ERR(child) || !child->d_inode) goto end_instantiate; inode = child->d_inode; - if (inode) { - ino = inode->i_ino; - type = inode->i_mode >> 12; - } + ino = inode->i_ino; + type = inode->i_mode >> 12; dput(child); end_instantiate: if (!ino) -- cgit v1.2.3 From db963164873f04d7fbdd418b4d023e2b3dee2945 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 15 Jun 2013 10:45:10 +0400 Subject: proc_pid_readdir(): stop wanking with proc_fill_cache() for /proc/self Signed-off-by: Al Viro --- fs/proc/base.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/proc/base.c b/fs/proc/base.c index 306419c19681..40d515db91c9 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -2882,21 +2882,21 @@ retry: int proc_pid_readdir(struct file *file, struct dir_context *ctx) { struct tgid_iter iter; - struct pid_namespace *ns; + struct pid_namespace *ns = file->f_dentry->d_sb->s_fs_info; loff_t pos = ctx->pos; if (pos >= PID_MAX_LIMIT + TGID_OFFSET) return 0; if (pos == TGID_OFFSET - 1) { - if (!proc_fill_cache(file, ctx, "self", 4, NULL, NULL, NULL)) + struct inode *inode = ns->proc_self->d_inode; + if (!dir_emit(ctx, "self", 4, inode->i_ino, DT_LNK)) return 0; iter.tgid = 0; } else { iter.tgid = pos - TGID_OFFSET; } iter.task = NULL; - ns = file->f_dentry->d_sb->s_fs_info; for (iter = next_tgid(ns, iter); iter.task; iter.tgid += 1, iter = next_tgid(ns, iter)) { -- cgit v1.2.3 From c52a47ace7ef58cbe06e6b48190fee7073dceccc Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 15 Jun 2013 11:15:20 +0400 Subject: proc_fill_cache(): just make instantiate_t return int all instances always return ERR_PTR(-E...) or NULL, anyway Signed-off-by: Al Viro --- fs/proc/base.c | 59 +++++++++++++++++++++++++--------------------------- fs/proc/fd.c | 18 +++++++--------- fs/proc/internal.h | 2 +- fs/proc/namespaces.c | 13 ++++++------ 4 files changed, 43 insertions(+), 49 deletions(-) diff --git a/fs/proc/base.c b/fs/proc/base.c index 40d515db91c9..ac1f7a823204 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -1700,7 +1700,7 @@ bool proc_fill_cache(struct file *file, struct dir_context *ctx, struct dentry *new; new = d_alloc(dir, &qname); if (new) { - child = instantiate(dir->d_inode, new, task, ptr); + child = ERR_PTR(instantiate(dir->d_inode, new, task, ptr)); if (child) dput(new); else @@ -1844,7 +1844,7 @@ struct map_files_info { unsigned char name[4*sizeof(long)+2]; /* max: %lx-%lx\0 */ }; -static struct dentry * +static int proc_map_files_instantiate(struct inode *dir, struct dentry *dentry, struct task_struct *task, const void *ptr) { @@ -1854,7 +1854,7 @@ proc_map_files_instantiate(struct inode *dir, struct dentry *dentry, inode = proc_pid_make_inode(dir->i_sb, task); if (!inode) - return ERR_PTR(-ENOENT); + return -ENOENT; ei = PROC_I(inode); ei->op.proc_get_link = proc_map_files_get_link; @@ -1871,7 +1871,7 @@ proc_map_files_instantiate(struct inode *dir, struct dentry *dentry, d_set_d_op(dentry, &tid_map_files_dentry_operations); d_add(dentry, inode); - return NULL; + return 0; } static struct dentry *proc_map_files_lookup(struct inode *dir, @@ -1880,23 +1880,23 @@ static struct dentry *proc_map_files_lookup(struct inode *dir, unsigned long vm_start, vm_end; struct vm_area_struct *vma; struct task_struct *task; - struct dentry *result; + int result; struct mm_struct *mm; - result = ERR_PTR(-EPERM); + result = -EPERM; if (!capable(CAP_SYS_ADMIN)) goto out; - result = ERR_PTR(-ENOENT); + result = -ENOENT; task = get_proc_task(dir); if (!task) goto out; - result = ERR_PTR(-EACCES); + result = -EACCES; if (!ptrace_may_access(task, PTRACE_MODE_READ)) goto out_put_task; - result = ERR_PTR(-ENOENT); + result = -ENOENT; if (dname_to_vma_addr(dentry, &vm_start, &vm_end)) goto out_put_task; @@ -1919,7 +1919,7 @@ out_no_vma: out_put_task: put_task_struct(task); out: - return result; + return ERR_PTR(result); } static const struct inode_operations proc_map_files_inode_operations = { @@ -2133,13 +2133,12 @@ static const struct file_operations proc_timers_operations = { }; #endif /* CONFIG_CHECKPOINT_RESTORE */ -static struct dentry *proc_pident_instantiate(struct inode *dir, +static int proc_pident_instantiate(struct inode *dir, struct dentry *dentry, struct task_struct *task, const void *ptr) { const struct pid_entry *p = ptr; struct inode *inode; struct proc_inode *ei; - struct dentry *error = ERR_PTR(-ENOENT); inode = proc_pid_make_inode(dir->i_sb, task); if (!inode) @@ -2158,9 +2157,9 @@ static struct dentry *proc_pident_instantiate(struct inode *dir, d_add(dentry, inode); /* Close the race of the process dying before we return the dentry */ if (pid_revalidate(dentry, 0)) - error = NULL; + return 0; out: - return error; + return -ENOENT; } static struct dentry *proc_pident_lookup(struct inode *dir, @@ -2168,11 +2167,11 @@ static struct dentry *proc_pident_lookup(struct inode *dir, const struct pid_entry *ents, unsigned int nents) { - struct dentry *error; + int error; struct task_struct *task = get_proc_task(dir); const struct pid_entry *p, *last; - error = ERR_PTR(-ENOENT); + error = -ENOENT; if (!task) goto out_no_task; @@ -2195,7 +2194,7 @@ static struct dentry *proc_pident_lookup(struct inode *dir, out: put_task_struct(task); out_no_task: - return error; + return ERR_PTR(error); } static int proc_pident_readdir(struct file *file, struct dir_context *ctx, @@ -2778,11 +2777,10 @@ void proc_flush_task(struct task_struct *task) } } -static struct dentry *proc_pid_instantiate(struct inode *dir, - struct dentry * dentry, - struct task_struct *task, const void *ptr) +static int proc_pid_instantiate(struct inode *dir, + struct dentry * dentry, + struct task_struct *task, const void *ptr) { - struct dentry *error = ERR_PTR(-ENOENT); struct inode *inode; inode = proc_pid_make_inode(dir->i_sb, task); @@ -2802,14 +2800,14 @@ static struct dentry *proc_pid_instantiate(struct inode *dir, d_add(dentry, inode); /* Close the race of the process dying before we return the dentry */ if (pid_revalidate(dentry, 0)) - error = NULL; + return 0; out: - return error; + return -ENOENT; } struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry, unsigned int flags) { - struct dentry *result = NULL; + int result = 0; struct task_struct *task; unsigned tgid; struct pid_namespace *ns; @@ -2830,7 +2828,7 @@ struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry, unsign result = proc_pid_instantiate(dir, dentry, task, NULL); put_task_struct(task); out: - return result; + return ERR_PTR(result); } /* @@ -3025,10 +3023,9 @@ static const struct inode_operations proc_tid_base_inode_operations = { .setattr = proc_setattr, }; -static struct dentry *proc_task_instantiate(struct inode *dir, +static int proc_task_instantiate(struct inode *dir, struct dentry *dentry, struct task_struct *task, const void *ptr) { - struct dentry *error = ERR_PTR(-ENOENT); struct inode *inode; inode = proc_pid_make_inode(dir->i_sb, task); @@ -3047,14 +3044,14 @@ static struct dentry *proc_task_instantiate(struct inode *dir, d_add(dentry, inode); /* Close the race of the process dying before we return the dentry */ if (pid_revalidate(dentry, 0)) - error = NULL; + return 0; out: - return error; + return -ENOENT; } static struct dentry *proc_task_lookup(struct inode *dir, struct dentry * dentry, unsigned int flags) { - struct dentry *result = ERR_PTR(-ENOENT); + int result = -ENOENT; struct task_struct *task; struct task_struct *leader = get_proc_task(dir); unsigned tid; @@ -3084,7 +3081,7 @@ out_drop_task: out: put_task_struct(leader); out_no_task: - return result; + return ERR_PTR(result); } /* diff --git a/fs/proc/fd.c b/fs/proc/fd.c index 1441f143c43b..75f2890abbd8 100644 --- a/fs/proc/fd.c +++ b/fs/proc/fd.c @@ -167,11 +167,10 @@ static int proc_fd_link(struct dentry *dentry, struct path *path) return ret; } -static struct dentry * +static int proc_fd_instantiate(struct inode *dir, struct dentry *dentry, struct task_struct *task, const void *ptr) { - struct dentry *error = ERR_PTR(-ENOENT); unsigned fd = (unsigned long)ptr; struct proc_inode *ei; struct inode *inode; @@ -194,9 +193,9 @@ proc_fd_instantiate(struct inode *dir, struct dentry *dentry, /* Close the race of the process dying before we return the dentry */ if (tid_fd_revalidate(dentry, 0)) - error = NULL; + return 0; out: - return error; + return -ENOENT; } static struct dentry *proc_lookupfd_common(struct inode *dir, @@ -204,7 +203,7 @@ static struct dentry *proc_lookupfd_common(struct inode *dir, instantiate_t instantiate) { struct task_struct *task = get_proc_task(dir); - struct dentry *result = ERR_PTR(-ENOENT); + int result = -ENOENT; unsigned fd = name_to_int(dentry); if (!task) @@ -216,7 +215,7 @@ static struct dentry *proc_lookupfd_common(struct inode *dir, out: put_task_struct(task); out_no_task: - return result; + return ERR_PTR(result); } static int proc_readfd_common(struct file *file, struct dir_context *ctx, @@ -300,11 +299,10 @@ const struct inode_operations proc_fd_inode_operations = { .setattr = proc_setattr, }; -static struct dentry * +static int proc_fdinfo_instantiate(struct inode *dir, struct dentry *dentry, struct task_struct *task, const void *ptr) { - struct dentry *error = ERR_PTR(-ENOENT); unsigned fd = (unsigned long)ptr; struct proc_inode *ei; struct inode *inode; @@ -324,9 +322,9 @@ proc_fdinfo_instantiate(struct inode *dir, struct dentry *dentry, /* Close the race of the process dying before we return the dentry */ if (tid_fd_revalidate(dentry, 0)) - error = NULL; + return 0; out: - return error; + return -ENOENT; } static struct dentry * diff --git a/fs/proc/internal.h b/fs/proc/internal.h index 4eae2e149f31..651d09a11dde 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h @@ -170,7 +170,7 @@ extern struct dentry *proc_pid_lookup(struct inode *, struct dentry *, unsigned extern loff_t mem_lseek(struct file *, loff_t, int); /* Lookups */ -typedef struct dentry *instantiate_t(struct inode *, struct dentry *, +typedef int instantiate_t(struct inode *, struct dentry *, struct task_struct *, const void *); extern bool proc_fill_cache(struct file *, struct dir_context *, const char *, int, instantiate_t, struct task_struct *, const void *); diff --git a/fs/proc/namespaces.c b/fs/proc/namespaces.c index f6abbbbfad8a..49a7fff2e83a 100644 --- a/fs/proc/namespaces.c +++ b/fs/proc/namespaces.c @@ -187,13 +187,12 @@ static const struct inode_operations proc_ns_link_inode_operations = { .setattr = proc_setattr, }; -static struct dentry *proc_ns_instantiate(struct inode *dir, +static int proc_ns_instantiate(struct inode *dir, struct dentry *dentry, struct task_struct *task, const void *ptr) { const struct proc_ns_operations *ns_ops = ptr; struct inode *inode; struct proc_inode *ei; - struct dentry *error = ERR_PTR(-ENOENT); inode = proc_pid_make_inode(dir->i_sb, task); if (!inode) @@ -208,9 +207,9 @@ static struct dentry *proc_ns_instantiate(struct inode *dir, d_add(dentry, inode); /* Close the race of the process dying before we return the dentry */ if (pid_revalidate(dentry, 0)) - error = NULL; + return 0; out: - return error; + return -ENOENT; } static int proc_ns_dir_readdir(struct file *file, struct dir_context *ctx) @@ -248,12 +247,12 @@ const struct file_operations proc_ns_dir_operations = { static struct dentry *proc_ns_dir_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) { - struct dentry *error; + int error; struct task_struct *task = get_proc_task(dir); const struct proc_ns_operations **entry, **last; unsigned int len = dentry->d_name.len; - error = ERR_PTR(-ENOENT); + error = -ENOENT; if (!task) goto out_no_task; @@ -272,7 +271,7 @@ static struct dentry *proc_ns_dir_lookup(struct inode *dir, out: put_task_struct(task); out_no_task: - return error; + return ERR_PTR(error); } const struct inode_operations proc_ns_dir_inode_operations = { -- cgit v1.2.3 From 1df98b8bbccab87f9da27b4661bf528212ffd5a2 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 15 Jun 2013 11:33:10 +0400 Subject: proc_fill_cache(): clean up, get rid of pointless find_inode_number() use Signed-off-by: Al Viro --- fs/proc/base.c | 36 +++++++++++++----------------------- 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/fs/proc/base.c b/fs/proc/base.c index ac1f7a823204..1485e38daaa3 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -1686,39 +1686,29 @@ bool proc_fill_cache(struct file *file, struct dir_context *ctx, instantiate_t instantiate, struct task_struct *task, const void *ptr) { struct dentry *child, *dir = file->f_path.dentry; + struct qstr qname = QSTR_INIT(name, len); struct inode *inode; - struct qstr qname; - ino_t ino = 0; - unsigned type = DT_UNKNOWN; + unsigned type; + ino_t ino; - qname.name = name; - qname.len = len; - qname.hash = full_name_hash(name, len); - - child = d_lookup(dir, &qname); + child = d_hash_and_lookup(dir, &qname); if (!child) { - struct dentry *new; - new = d_alloc(dir, &qname); - if (new) { - child = ERR_PTR(instantiate(dir->d_inode, new, task, ptr)); - if (child) - dput(new); - else - child = new; + child = d_alloc(dir, &qname); + if (!child) + goto end_instantiate; + if (instantiate(dir->d_inode, child, task, ptr) < 0) { + dput(child); + goto end_instantiate; } } - if (!child || IS_ERR(child) || !child->d_inode) - goto end_instantiate; inode = child->d_inode; ino = inode->i_ino; type = inode->i_mode >> 12; dput(child); -end_instantiate: - if (!ino) - ino = find_inode_number(dir, &qname); - if (!ino) - ino = 1; return dir_emit(ctx, name, len, ino, type); + +end_instantiate: + return dir_emit(ctx, name, len, 1, DT_UNKNOWN); } #ifdef CONFIG_CHECKPOINT_RESTORE -- cgit v1.2.3 From 6b5e1223d9c3fae19b1a2acd789d9dab97de67c4 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 19 Jun 2013 13:21:03 +0400 Subject: coda: don't bother with find_inode_number() the fallback it's using for dcache misses is actually the same value we would've used for inumber anyway. Signed-off-by: Al Viro --- fs/coda/dir.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/fs/coda/dir.c b/fs/coda/dir.c index 87e0ee9f4465..14a14808320c 100644 --- a/fs/coda/dir.c +++ b/fs/coda/dir.c @@ -487,13 +487,7 @@ static int coda_venus_readdir(struct file *coda_file, struct dir_context *ctx) /* skip null entries */ if (vdir->d_fileno && name.len) { - /* try to look up this entry in the dcache, that way - * userspace doesn't have to worry about breaking - * getcwd by having mismatched inode numbers for - * internal volume mountpoints. */ - ino = find_inode_number(de, &name); - if (!ino) ino = vdir->d_fileno; - + ino = vdir->d_fileno; type = CDT2DT(vdir->d_type); if (!dir_emit(ctx, name.name, name.len, ino, type)) break; -- cgit v1.2.3 From 0b3fca1fd1499f0f5a7486d494f96538f2b7e5b9 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 15 Jun 2013 11:37:47 +0400 Subject: kill find_inode_number() the only remaining caller (in ncpfs) is guaranteed to return 0 - we only hit it if we'd just checked that there's no dentry with such name. Signed-off-by: Al Viro --- fs/dcache.c | 29 ----------------------------- fs/ncpfs/dir.c | 2 -- include/linux/fs.h | 1 - 3 files changed, 32 deletions(-) diff --git a/fs/dcache.c b/fs/dcache.c index b7f049c31526..b692c7e097c5 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -2984,35 +2984,6 @@ void d_tmpfile(struct dentry *dentry, struct inode *inode) } EXPORT_SYMBOL(d_tmpfile); -/** - * find_inode_number - check for dentry with name - * @dir: directory to check - * @name: Name to find. - * - * Check whether a dentry already exists for the given name, - * and return the inode number if it has an inode. Otherwise - * 0 is returned. - * - * This routine is used to post-process directory listings for - * filesystems using synthetic inode numbers, and is necessary - * to keep getcwd() working. - */ - -ino_t find_inode_number(struct dentry *dir, struct qstr *name) -{ - struct dentry * dentry; - ino_t ino = 0; - - dentry = d_hash_and_lookup(dir, name); - if (!IS_ERR_OR_NULL(dentry)) { - if (dentry->d_inode) - ino = dentry->d_inode->i_ino; - dput(dentry); - } - return ino; -} -EXPORT_SYMBOL(find_inode_number); - static __initdata unsigned long dhash_entries; static int __init set_dhash_entries(char *str) { diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c index e5d488530580..3bc105d36f10 100644 --- a/fs/ncpfs/dir.c +++ b/fs/ncpfs/dir.c @@ -659,8 +659,6 @@ end_advance: if (!valid) ctl.valid = 0; if (!ctl.filled && (ctl.fpos == ctx->pos)) { - if (!ino) - ino = find_inode_number(dentry, &qname); if (!ino) ino = iunique(dir->i_sb, 2); ctl.filled = !dir_emit(ctx, qname.name, qname.len, diff --git a/include/linux/fs.h b/include/linux/fs.h index ab11c44b0697..1db01c13ddce 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2311,7 +2311,6 @@ extern struct file * open_exec(const char *); /* fs/dcache.c -- generic fs support functions */ extern int is_subdir(struct dentry *, struct dentry *); extern int path_is_under(struct path *, struct path *); -extern ino_t find_inode_number(struct dentry *, struct qstr *); #include -- cgit v1.2.3 From e77e43003382e46c673c9f82b5f2df8058d3c527 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 16 Jun 2013 17:25:12 +0400 Subject: more open-coded file_inode() calls Signed-off-by: Al Viro --- arch/arc/kernel/troubleshoot.c | 2 +- drivers/staging/android/logger.c | 2 +- net/sunrpc/auth_gss/svcauth_gss.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arc/kernel/troubleshoot.c b/arch/arc/kernel/troubleshoot.c index 11c301b81c92..a03528ecd276 100644 --- a/arch/arc/kernel/troubleshoot.c +++ b/arch/arc/kernel/troubleshoot.c @@ -101,7 +101,7 @@ static void show_faulting_vma(unsigned long address, char *buf) if (file) { struct path *path = &file->f_path; nm = d_path(path, buf, PAGE_SIZE - 1); - inode = vma->vm_file->f_path.dentry->d_inode; + inode = file_inode(vma->vm_file); dev = inode->i_sb->s_dev; ino = inode->i_ino; } diff --git a/drivers/staging/android/logger.c b/drivers/staging/android/logger.c index 9bd874789ce5..080abf2faf97 100644 --- a/drivers/staging/android/logger.c +++ b/drivers/staging/android/logger.c @@ -696,7 +696,7 @@ static long logger_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ret = -EBADF; break; } - if (!(in_egroup_p(file->f_dentry->d_inode->i_gid) || + if (!(in_egroup_p(file_inode(file)->i_gid) || capable(CAP_SYSLOG))) { ret = -EPERM; break; diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index 29b4ba93ab3c..b05ace4c5f12 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -1330,7 +1330,7 @@ static int wait_for_gss_proxy(struct net *net, struct file *file) static ssize_t write_gssp(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - struct net *net = PDE_DATA(file->f_path.dentry->d_inode); + struct net *net = PDE_DATA(file_inode(file)); char tbuf[20]; unsigned long i; int res; @@ -1358,7 +1358,7 @@ static ssize_t write_gssp(struct file *file, const char __user *buf, static ssize_t read_gssp(struct file *file, char __user *buf, size_t count, loff_t *ppos) { - struct net *net = PDE_DATA(file->f_path.dentry->d_inode); + struct net *net = PDE_DATA(file_inode(file)); unsigned long p = *ppos; char tbuf[10]; size_t len; -- cgit v1.2.3 From a44f52096c0b35214953124754dc034a889dc3b0 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 16 Jun 2013 19:05:07 +0400 Subject: comedi: quit wanking with FASYNC in ->release() Signed-off-by: Al Viro --- drivers/staging/comedi/comedi_fops.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c index 924c54c9c31f..a9754e84eb8a 100644 --- a/drivers/staging/comedi/comedi_fops.c +++ b/drivers/staging/comedi/comedi_fops.c @@ -2317,9 +2317,6 @@ static int comedi_close(struct inode *inode, struct file *file) mutex_unlock(&dev->mutex); - if (file->f_flags & FASYNC) - comedi_fasync(-1, file, 0); - return 0; } -- cgit v1.2.3 From 3058dca6945f12f1777eb40d43f4c97351a9c175 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 16 Jun 2013 19:08:36 +0400 Subject: fanotify: quit wanking with FASYNC in ->release() ... especially since there's no way to get that sucker on the list fsnotify_fasync() works with - the only thing adding to it is fsnotify_fasync() itself and it's never called for fanotify files while they are opened. Signed-off-by: Al Viro --- fs/notify/fanotify/fanotify_user.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 6c80083a984f..1ea52f7c031f 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -399,9 +399,6 @@ static int fanotify_release(struct inode *ignored, struct file *file) wake_up(&group->fanotify_data.access_waitq); #endif - if (file->f_flags & FASYNC) - fsnotify_fasync(-1, file, 0); - /* matches the fanotify_init->fsnotify_alloc_group */ fsnotify_destroy_group(group); -- cgit v1.2.3 From 6d0379ec49d99530ae6e25ee5dcf2495caee0101 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 16 Jun 2013 19:32:35 +0400 Subject: btrfs: more open-coded file_inode() Signed-off-by: Al Viro --- fs/btrfs/ioctl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 0f81d67cdc8d..cd7e96c73cb7 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -3881,7 +3881,7 @@ drop_write: static long btrfs_ioctl_quota_rescan(struct file *file, void __user *arg) { - struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root; + struct btrfs_root *root = BTRFS_I(file_inode(file))->root; struct btrfs_ioctl_quota_rescan_args *qsa; int ret; @@ -3914,7 +3914,7 @@ drop_write: static long btrfs_ioctl_quota_rescan_status(struct file *file, void __user *arg) { - struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root; + struct btrfs_root *root = BTRFS_I(file_inode(file))->root; struct btrfs_ioctl_quota_rescan_args *qsa; int ret = 0; @@ -4020,7 +4020,7 @@ out: static int btrfs_ioctl_get_fslabel(struct file *file, void __user *arg) { - struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root; + struct btrfs_root *root = BTRFS_I(file_inode(file))->root; const char *label = root->fs_info->super_copy->label; size_t len = strnlen(label, BTRFS_LABEL_SIZE); int ret; @@ -4039,7 +4039,7 @@ static int btrfs_ioctl_get_fslabel(struct file *file, void __user *arg) static int btrfs_ioctl_set_fslabel(struct file *file, void __user *arg) { - struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root; + struct btrfs_root *root = BTRFS_I(file_inode(file))->root; struct btrfs_super_block *super_block = root->fs_info->super_copy; struct btrfs_trans_handle *trans; char label[BTRFS_LABEL_SIZE]; -- cgit v1.2.3 From cb5e05d1a6786dfd393cf4825d626817df01c2c3 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 16 Jun 2013 20:05:23 +0400 Subject: fuse: another open-coded file_inode() Signed-off-by: Al Viro --- fs/fuse/file.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index e570081f9f76..bfb20a8642c3 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -548,8 +548,7 @@ static void fuse_aio_complete(struct fuse_io_priv *io, int err, ssize_t pos) res = io->bytes < 0 ? io->size : io->bytes; if (!is_sync_kiocb(io->iocb)) { - struct path *path = &io->iocb->ki_filp->f_path; - struct inode *inode = path->dentry->d_inode; + struct inode *inode = file_inode(io->iocb->ki_filp); struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_inode *fi = get_fuse_inode(inode); -- cgit v1.2.3 From 0747fdb2bd59d9404ae2345cbddd7d837c5c4648 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 16 Jun 2013 20:05:38 +0400 Subject: ecryptfs: switch ecryptfs_decode_and_decrypt_filename() from dentry to sb Signed-off-by: Al Viro --- fs/ecryptfs/crypto.c | 5 ++--- fs/ecryptfs/ecryptfs_kernel.h | 2 +- fs/ecryptfs/file.c | 9 ++++----- fs/ecryptfs/inode.c | 2 +- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index f71ec125290d..cfa109a4d5a2 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -2243,12 +2243,11 @@ out: */ int ecryptfs_decode_and_decrypt_filename(char **plaintext_name, size_t *plaintext_name_size, - struct dentry *ecryptfs_dir_dentry, + struct super_block *sb, const char *name, size_t name_size) { struct ecryptfs_mount_crypt_stat *mount_crypt_stat = - &ecryptfs_superblock_to_private( - ecryptfs_dir_dentry->d_sb)->mount_crypt_stat; + &ecryptfs_superblock_to_private(sb)->mount_crypt_stat; char *decoded_name; size_t decoded_name_size; size_t packet_size; diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h index f622a733f7ad..df19d34a033b 100644 --- a/fs/ecryptfs/ecryptfs_kernel.h +++ b/fs/ecryptfs/ecryptfs_kernel.h @@ -575,7 +575,7 @@ int ecryptfs_initialize_file(struct dentry *ecryptfs_dentry, struct inode *ecryptfs_inode); int ecryptfs_decode_and_decrypt_filename(char **decrypted_name, size_t *decrypted_name_size, - struct dentry *ecryptfs_dentry, + struct super_block *sb, const char *name, size_t name_size); int ecryptfs_fill_zeros(struct file *file, loff_t new_length); int ecryptfs_encrypt_and_encode_filename( diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c index 9aa05e08060b..24f1105fda3a 100644 --- a/fs/ecryptfs/file.c +++ b/fs/ecryptfs/file.c @@ -70,7 +70,7 @@ static ssize_t ecryptfs_read_update_atime(struct kiocb *iocb, struct ecryptfs_getdents_callback { struct dir_context ctx; struct dir_context *caller; - struct dentry *dentry; + struct super_block *sb; int filldir_called; int entries_written; }; @@ -88,7 +88,7 @@ ecryptfs_filldir(void *dirent, const char *lower_name, int lower_namelen, buf->filldir_called++; rc = ecryptfs_decode_and_decrypt_filename(&name, &name_size, - buf->dentry, lower_name, + buf->sb, lower_name, lower_namelen); if (rc) { printk(KERN_ERR "%s: Error attempting to decode and decrypt " @@ -114,15 +114,14 @@ static int ecryptfs_readdir(struct file *file, struct dir_context *ctx) { int rc; struct file *lower_file; - struct inode *inode; + struct inode *inode = file_inode(file); struct ecryptfs_getdents_callback buf = { .ctx.actor = ecryptfs_filldir, .caller = ctx, - .dentry = file->f_path.dentry + .sb = inode->i_sb, }; lower_file = ecryptfs_file_to_lower(file); lower_file->f_pos = ctx->pos; - inode = file_inode(file); rc = iterate_dir(lower_file, &buf.ctx); ctx->pos = buf.ctx.pos; if (rc < 0) diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index 5eab400e2590..a2f2bb2c256d 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -679,7 +679,7 @@ static int ecryptfs_readlink_lower(struct dentry *dentry, char **buf, set_fs(old_fs); if (rc < 0) goto out; - rc = ecryptfs_decode_and_decrypt_filename(buf, bufsiz, dentry, + rc = ecryptfs_decode_and_decrypt_filename(buf, bufsiz, dentry->d_sb, lower_buf, rc); out: kfree(lower_buf); -- cgit v1.2.3 From 1bf9d14dff4a2c4de6152c6f751bdaf6896b68bb Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 16 Jun 2013 20:27:42 +0400 Subject: new helper: fixed_size_llseek() Signed-off-by: Al Viro --- fs/read_write.c | 20 ++++++++++++++++++++ include/linux/fs.h | 2 ++ 2 files changed, 22 insertions(+) diff --git a/fs/read_write.c b/fs/read_write.c index f646c8b565b9..782dfc3acebc 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -144,6 +144,26 @@ loff_t generic_file_llseek(struct file *file, loff_t offset, int whence) } EXPORT_SYMBOL(generic_file_llseek); +/** + * fixed_size_llseek - llseek implementation for fixed-sized devices + * @file: file structure to seek on + * @offset: file offset to seek to + * @whence: type of seek + * @size: size of the file + * + */ +loff_t fixed_size_llseek(struct file *file, loff_t offset, int whence, loff_t size) +{ + switch (whence) { + case SEEK_SET: case SEEK_CUR: case SEEK_END: + return generic_file_llseek_size(file, offset, whence, + size, size); + default: + return -EINVAL; + } +} +EXPORT_SYMBOL(fixed_size_llseek); + /** * noop_llseek - No Operation Performed llseek implementation * @file: file structure to seek on diff --git a/include/linux/fs.h b/include/linux/fs.h index 1db01c13ddce..803b7fa2520a 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2428,6 +2428,8 @@ extern loff_t no_llseek(struct file *file, loff_t offset, int whence); extern loff_t generic_file_llseek(struct file *file, loff_t offset, int whence); extern loff_t generic_file_llseek_size(struct file *file, loff_t offset, int whence, loff_t maxsize, loff_t eof); +extern loff_t fixed_size_llseek(struct file *file, loff_t offset, + int whence, loff_t size); extern int generic_file_open(struct inode * inode, struct file * filp); extern int nonseekable_open(struct inode * inode, struct file * filp); -- cgit v1.2.3 From b959957f9428b05cd5e138beebee04897bfd673d Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 16 Jun 2013 20:27:42 +0400 Subject: mtdchar: switch to fixed_size_llseek() Signed-off-by: Al Viro --- drivers/mtd/mtdchar.c | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index c719879284bd..684bfa39e4ee 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -55,25 +55,7 @@ struct mtd_file_info { static loff_t mtdchar_lseek(struct file *file, loff_t offset, int orig) { struct mtd_file_info *mfi = file->private_data; - struct mtd_info *mtd = mfi->mtd; - - switch (orig) { - case SEEK_SET: - break; - case SEEK_CUR: - offset += file->f_pos; - break; - case SEEK_END: - offset += mtd->size; - break; - default: - return -EINVAL; - } - - if (offset >= 0 && offset <= mtd->size) - return file->f_pos = offset; - - return -EINVAL; + return fixed_size_llseek(file, offset, orig, mfi->mtd->size); } static int count; -- cgit v1.2.3 From 3be1f2b81f6f9b333fe6fb5ac49c4a419786e859 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 22 Jun 2013 12:10:22 +0400 Subject: zorro: switch to fixed_size_llseek() Signed-off-by: Al Viro --- drivers/zorro/proc.c | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/drivers/zorro/proc.c b/drivers/zorro/proc.c index 1c15ee7456b6..ea1ce822a8e0 100644 --- a/drivers/zorro/proc.c +++ b/drivers/zorro/proc.c @@ -21,27 +21,7 @@ static loff_t proc_bus_zorro_lseek(struct file *file, loff_t off, int whence) { - loff_t new = -1; - struct inode *inode = file_inode(file); - - mutex_lock(&inode->i_mutex); - switch (whence) { - case 0: - new = off; - break; - case 1: - new = file->f_pos + off; - break; - case 2: - new = sizeof(struct ConfigDev) + off; - break; - } - if (new < 0 || new > sizeof(struct ConfigDev)) - new = -EINVAL; - else - file->f_pos = new; - mutex_unlock(&inode->i_mutex); - return new; + return fixed_size_llseek(file, off, whence, sizeof(struct ConfigDev)); } static ssize_t -- cgit v1.2.3 From c0caa07b6ab714caaea7beaa5a2e3cbf541f0831 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 17 Jun 2013 15:23:54 +0400 Subject: bna: switch to fixed_size_llseek() Signed-off-by: Al Viro --- drivers/net/ethernet/brocade/bna/bnad_debugfs.c | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/drivers/net/ethernet/brocade/bna/bnad_debugfs.c b/drivers/net/ethernet/brocade/bna/bnad_debugfs.c index 94d957d203a6..7d6aa8c87df8 100644 --- a/drivers/net/ethernet/brocade/bna/bnad_debugfs.c +++ b/drivers/net/ethernet/brocade/bna/bnad_debugfs.c @@ -230,32 +230,12 @@ bnad_debugfs_open_drvinfo(struct inode *inode, struct file *file) static loff_t bnad_debugfs_lseek(struct file *file, loff_t offset, int orig) { - loff_t pos = file->f_pos; struct bnad_debug_info *debug = file->private_data; if (!debug) return -EINVAL; - switch (orig) { - case 0: - file->f_pos = offset; - break; - case 1: - file->f_pos += offset; - break; - case 2: - file->f_pos = debug->buffer_len + offset; - break; - default: - return -EINVAL; - } - - if (file->f_pos < 0 || file->f_pos > debug->buffer_len) { - file->f_pos = pos; - return -EINVAL; - } - - return file->f_pos; + return fixed_size_llseek(file, offset, orig, debug->buffer_len); } static ssize_t -- cgit v1.2.3 From 747977976b8caf98b63559f200a5e94189059dc0 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 17 Jun 2013 15:27:47 +0400 Subject: eisa_eeprom: switch to fixed_size_llseek() Signed-off-by: Al Viro --- drivers/parisc/eisa_eeprom.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/drivers/parisc/eisa_eeprom.c b/drivers/parisc/eisa_eeprom.c index af212c6a6158..783906fe659a 100644 --- a/drivers/parisc/eisa_eeprom.c +++ b/drivers/parisc/eisa_eeprom.c @@ -31,20 +31,9 @@ #define EISA_EEPROM_MINOR 241 -static loff_t eisa_eeprom_llseek(struct file *file, loff_t offset, int origin ) +static loff_t eisa_eeprom_llseek(struct file *file, loff_t offset, int origin) { - switch (origin) { - case 0: - /* nothing to do */ - break; - case 1: - offset += file->f_pos; - break; - case 2: - offset += HPEE_MAX_LENGTH; - break; - } - return (offset >= 0 && offset < HPEE_MAX_LENGTH) ? (file->f_pos = offset) : -EINVAL; + return fixed_size_llseek(file, offset, origin, HPEE_MAX_LENGTH); } static ssize_t eisa_eeprom_read(struct file * file, -- cgit v1.2.3 From 65004276fc45a37c133f09a86712e6f2daede342 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 17 Jun 2013 15:31:22 +0400 Subject: vc: switch to fixed_size_llseek() Signed-off-by: Al Viro --- drivers/tty/vt/vc_screen.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/drivers/tty/vt/vc_screen.c b/drivers/tty/vt/vc_screen.c index d7799deacb21..14a2b5f11bca 100644 --- a/drivers/tty/vt/vc_screen.c +++ b/drivers/tty/vt/vc_screen.c @@ -188,22 +188,7 @@ static loff_t vcs_lseek(struct file *file, loff_t offset, int orig) console_unlock(); if (size < 0) return size; - switch (orig) { - default: - return -EINVAL; - case 2: - offset += size; - break; - case 1: - offset += file->f_pos; - case 0: - break; - } - if (offset < 0 || offset > size) { - return -EINVAL; - } - file->f_pos = offset; - return file->f_pos; + return fixed_size_llseek(file, offset, orig, size); } -- cgit v1.2.3 From eb5881d37fe2c8c43c92bb7f2c87fba90a2103f2 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 17 Jun 2013 17:44:23 +0400 Subject: fnic: switch to fixed_size_llseek() Signed-off-by: Al Viro --- drivers/scsi/fnic/fnic_debugfs.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/drivers/scsi/fnic/fnic_debugfs.c b/drivers/scsi/fnic/fnic_debugfs.c index 85e1ffd0e5c5..cbcb0121c84d 100644 --- a/drivers/scsi/fnic/fnic_debugfs.c +++ b/drivers/scsi/fnic/fnic_debugfs.c @@ -164,20 +164,8 @@ static loff_t fnic_trace_debugfs_lseek(struct file *file, int howto) { fnic_dbgfs_t *fnic_dbg_prt = file->private_data; - loff_t pos = -1; - - switch (howto) { - case 0: - pos = offset; - break; - case 1: - pos = file->f_pos + offset; - break; - case 2: - pos = fnic_dbg_prt->buffer_len + offset; - } - return (pos < 0 || pos > fnic_dbg_prt->buffer_len) ? - -EINVAL : (file->f_pos = pos); + return fixed_size_llseek(file, offset, howto, + fnic_dbg_prt->buffer_len); } /* -- cgit v1.2.3 From c04eba722f9b17cf07dd0e40bf497d5756cd8fe6 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 17 Jun 2013 17:45:46 +0400 Subject: bfa: switch to fixed_size_llseek() Signed-off-by: Al Viro --- drivers/scsi/bfa/bfad_debugfs.c | 28 +++------------------------- 1 file changed, 3 insertions(+), 25 deletions(-) diff --git a/drivers/scsi/bfa/bfad_debugfs.c b/drivers/scsi/bfa/bfad_debugfs.c index b63d534192e3..8e83d0474fe7 100644 --- a/drivers/scsi/bfa/bfad_debugfs.c +++ b/drivers/scsi/bfa/bfad_debugfs.c @@ -173,31 +173,9 @@ bfad_debugfs_open_reg(struct inode *inode, struct file *file) static loff_t bfad_debugfs_lseek(struct file *file, loff_t offset, int orig) { - struct bfad_debug_info *debug; - loff_t pos = file->f_pos; - - debug = file->private_data; - - switch (orig) { - case 0: - file->f_pos = offset; - break; - case 1: - file->f_pos += offset; - break; - case 2: - file->f_pos = debug->buffer_len + offset; - break; - default: - return -EINVAL; - } - - if (file->f_pos < 0 || file->f_pos > debug->buffer_len) { - file->f_pos = pos; - return -EINVAL; - } - - return file->f_pos; + struct bfad_debug_info *debug = file->private_data; + return fixed_size_llseek(file, offset, orig, + debug->buffer_len); } static ssize_t -- cgit v1.2.3 From 05c8aaa960f1c62edaba968f762c195bc9b251dd Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 17 Jun 2013 15:26:19 +0400 Subject: wlcore: use *ppos, not file->f_pos Signed-off-by: Al Viro --- drivers/net/wireless/ti/wlcore/debugfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c index c3e1f79c7856..e17630c2a849 100644 --- a/drivers/net/wireless/ti/wlcore/debugfs.c +++ b/drivers/net/wireless/ti/wlcore/debugfs.c @@ -1056,7 +1056,7 @@ static ssize_t dev_mem_read(struct file *file, return -EINVAL; memset(&part, 0, sizeof(part)); - part.mem.start = file->f_pos; + part.mem.start = *ppos; part.mem.size = bytes; buf = kmalloc(bytes, GFP_KERNEL); @@ -1137,7 +1137,7 @@ static ssize_t dev_mem_write(struct file *file, const char __user *user_buf, return -EINVAL; memset(&part, 0, sizeof(part)); - part.mem.start = file->f_pos; + part.mem.start = *ppos; part.mem.size = bytes; buf = kmalloc(bytes, GFP_KERNEL); -- cgit v1.2.3 From ee32465249bee53714c0ca7be38da83008f9f8a6 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 17 Jun 2013 10:05:35 +0400 Subject: ps3flash: switch to generic_file_llseek_size() Signed-off-by: Al Viro --- drivers/char/ps3flash.c | 28 ++-------------------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/drivers/char/ps3flash.c b/drivers/char/ps3flash.c index 8cafa9ccd43f..0b311fa277ef 100644 --- a/drivers/char/ps3flash.c +++ b/drivers/char/ps3flash.c @@ -98,32 +98,8 @@ static int ps3flash_fetch(struct ps3_storage_device *dev, u64 start_sector) static loff_t ps3flash_llseek(struct file *file, loff_t offset, int origin) { struct ps3_storage_device *dev = ps3flash_dev; - loff_t res; - - mutex_lock(&file->f_mapping->host->i_mutex); - switch (origin) { - case 0: - break; - case 1: - offset += file->f_pos; - break; - case 2: - offset += dev->regions[dev->region_idx].size*dev->blk_size; - break; - default: - offset = -1; - } - if (offset < 0) { - res = -EINVAL; - goto out; - } - - file->f_pos = offset; - res = file->f_pos; - -out: - mutex_unlock(&file->f_mapping->host->i_mutex); - return res; + return generic_file_llseek_size(file, offset, origin, MAX_LFS_FILESIZE, + dev->regions[dev->region_idx].size*dev->blk_size); } static ssize_t ps3flash_read(char __user *userbuf, void *kernelbuf, -- cgit v1.2.3 From 68d70d03f8f5bd10a0e7337210b13f536fd4aeb9 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 19 Jun 2013 15:26:04 +0400 Subject: constify rw_verify_area() Signed-off-by: Al Viro --- fs/aio.c | 2 ++ fs/internal.h | 1 + fs/read_write.c | 2 +- include/linux/fs.h | 1 - 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/aio.c b/fs/aio.c index 2bbcacf74d0c..a8ecc8313fb0 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -39,6 +39,8 @@ #include #include +#include "internal.h" + #define AIO_RING_MAGIC 0xa10a10a1 #define AIO_RING_COMPAT_FEATURES 1 #define AIO_RING_INCOMPAT_FEATURES 0 diff --git a/fs/internal.h b/fs/internal.h index f6ad34362823..7c5f01cf619d 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -131,6 +131,7 @@ extern struct dentry *__d_alloc(struct super_block *, const struct qstr *); * read_write.c */ extern ssize_t __kernel_write(struct file *, const char *, size_t, loff_t *); +extern int rw_verify_area(int, struct file *, const loff_t *, size_t); /* * splice.c diff --git a/fs/read_write.c b/fs/read_write.c index 782dfc3acebc..fd72b592aa1b 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -316,7 +316,7 @@ out_putf: * them to something that fits in "int" so that others * won't have to do range checks all the time. */ -int rw_verify_area(int read_write, struct file *file, loff_t *ppos, size_t count) +int rw_verify_area(int read_write, struct file *file, const loff_t *ppos, size_t count) { struct inode *inode; loff_t pos; diff --git a/include/linux/fs.h b/include/linux/fs.h index 803b7fa2520a..68f10204ab29 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1898,7 +1898,6 @@ extern int current_umask(void); extern struct kobject *fs_kobj; #define MAX_RW_COUNT (INT_MAX & PAGE_CACHE_MASK) -extern int rw_verify_area(int, struct file *, loff_t *, size_t); #define FLOCK_VERIFY_READ 1 #define FLOCK_VERIFY_WRITE 2 -- cgit v1.2.3 From 18c67cb9f0d2ac1e5660899c852f657ba84ddd2e Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 19 Jun 2013 15:41:54 +0400 Subject: splice: lift checks from do_splice_from() into callers Signed-off-by: Al Viro --- fs/splice.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/fs/splice.c b/fs/splice.c index 78b3d12046e7..cc53bd04be8f 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -1098,17 +1098,6 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, { ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); - int ret; - - if (unlikely(!(out->f_mode & FMODE_WRITE))) - return -EBADF; - - if (unlikely(out->f_flags & O_APPEND)) - return -EINVAL; - - ret = rw_verify_area(WRITE, out, ppos, len); - if (unlikely(ret < 0)) - return ret; if (out->f_op && out->f_op->splice_write) splice_write = out->f_op->splice_write; @@ -1303,6 +1292,16 @@ long do_splice_direct(struct file *in, loff_t *ppos, struct file *out, }; long ret; + if (unlikely(!(out->f_mode & FMODE_WRITE))) + return -EBADF; + + if (unlikely(out->f_flags & O_APPEND)) + return -EINVAL; + + ret = rw_verify_area(WRITE, out, opos, len); + if (unlikely(ret < 0)) + return ret; + ret = splice_direct_to_actor(in, &sd, direct_splice_actor); if (ret > 0) *ppos = sd.pos; @@ -1358,6 +1357,16 @@ static long do_splice(struct file *in, loff_t __user *off_in, offset = out->f_pos; } + if (unlikely(!(out->f_mode & FMODE_WRITE))) + return -EBADF; + + if (unlikely(out->f_flags & O_APPEND)) + return -EINVAL; + + ret = rw_verify_area(WRITE, out, &offset, len); + if (unlikely(ret < 0)) + return ret; + file_start_write(out); ret = do_splice_from(ipipe, out, &offset, len, flags); file_end_write(out); -- cgit v1.2.3 From 642b704cd7a29be0b8900971eb525086c1c995b7 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 19 Jun 2013 10:08:10 +1000 Subject: minix: bug widening a binary "not" operation "chunk_size" is an unsigned int and "pos" is an unsigned long. The "& ~(chunk_size-1)" operation clears the high 32 bits unintentionally. The ALIGN() macro does the correct thing. Signed-off-by: Dan Carpenter Cc: Al Viro Signed-off-by: Andrew Morton --- fs/minix/dir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/minix/dir.c b/fs/minix/dir.c index 08c442902fcd..dfaf6fa9b7b5 100644 --- a/fs/minix/dir.c +++ b/fs/minix/dir.c @@ -93,7 +93,7 @@ static int minix_readdir(struct file *file, struct dir_context *ctx) unsigned offset; unsigned long n; - ctx->pos = pos = (pos + chunk_size-1) & ~(chunk_size-1); + ctx->pos = pos = ALIGN(pos, chunk_size); if (pos >= inode->i_size) return 0; -- cgit v1.2.3 From da53be12bbb4fabbe2e9f6f908de0cf478b5161d Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 21 May 2013 15:22:44 -0700 Subject: Don't pass inode to ->d_hash() and ->d_compare() Instances either don't look at it at all (the majority of cases) or only want it to find the superblock (which can be had as dentry->d_sb). A few cases that want more are actually safe with dentry->d_inode - the only precaution needed is the check that it hadn't been replaced with NULL by rmdir() or by overwriting rename(), which case should be simply treated as cache miss. Signed-off-by: Linus Torvalds Signed-off-by: Al Viro --- Documentation/filesystems/Locking | 6 ++--- Documentation/filesystems/vfs.txt | 19 +++++++--------- fs/adfs/dir.c | 6 ++--- fs/affs/namei.c | 26 +++++++-------------- fs/cifs/dir.c | 9 +++----- fs/dcache.c | 27 ++++++++-------------- fs/efivarfs/super.c | 9 ++++---- fs/fat/namei_msdos.c | 6 ++--- fs/fat/namei_vfat.c | 12 ++++------ fs/gfs2/dentry.c | 3 +-- fs/hfs/hfs_fs.h | 7 ++---- fs/hfs/string.c | 6 ++--- fs/hfsplus/hfsplus_fs.h | 7 ++---- fs/hfsplus/unicode.c | 7 ++---- fs/hpfs/dentry.c | 7 ++---- fs/isofs/inode.c | 48 +++++++++++++-------------------------- fs/isofs/namei.c | 3 +-- fs/jfs/namei.c | 7 ++---- fs/namei.c | 7 +++--- fs/ncpfs/dir.c | 32 +++++++++++++++++++------- fs/proc/proc_sysctl.c | 7 +++--- fs/sysv/namei.c | 3 +-- include/linux/dcache.h | 9 +++----- 23 files changed, 108 insertions(+), 165 deletions(-) diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index bdd82b2339d9..f94a362f408e 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -11,10 +11,8 @@ be able to use diff(1). prototypes: int (*d_revalidate)(struct dentry *, unsigned int); int (*d_weak_revalidate)(struct dentry *, unsigned int); - int (*d_hash)(const struct dentry *, const struct inode *, - struct qstr *); - int (*d_compare)(const struct dentry *, const struct inode *, - const struct dentry *, const struct inode *, + int (*d_hash)(const struct dentry *, struct qstr *); + int (*d_compare)(const struct dentry *, const struct dentry *, unsigned int, const char *, const struct qstr *); int (*d_delete)(struct dentry *); void (*d_release)(struct dentry *); diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index 4a35f6614a66..51ba44e3fc40 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt @@ -901,10 +901,8 @@ defined: struct dentry_operations { int (*d_revalidate)(struct dentry *, unsigned int); int (*d_weak_revalidate)(struct dentry *, unsigned int); - int (*d_hash)(const struct dentry *, const struct inode *, - struct qstr *); - int (*d_compare)(const struct dentry *, const struct inode *, - const struct dentry *, const struct inode *, + int (*d_hash)(const struct dentry *, struct qstr *); + int (*d_compare)(const struct dentry *, const struct dentry *, unsigned int, const char *, const struct qstr *); int (*d_delete)(const struct dentry *); void (*d_release)(struct dentry *); @@ -949,25 +947,24 @@ struct dentry_operations { d_hash: called when the VFS adds a dentry to the hash table. The first dentry passed to d_hash is the parent directory that the name is - to be hashed into. The inode is the dentry's inode. + to be hashed into. Same locking and synchronisation rules as d_compare regarding what is safe to dereference etc. d_compare: called to compare a dentry name with a given name. The first dentry is the parent of the dentry to be compared, the second is - the parent's inode, then the dentry and inode (may be NULL) of the - child dentry. len and name string are properties of the dentry to be - compared. qstr is the name to compare it with. + the child dentry. len and name string are properties of the dentry + to be compared. qstr is the name to compare it with. Must be constant and idempotent, and should not take locks if - possible, and should not or store into the dentry or inodes. - Should not dereference pointers outside the dentry or inodes without + possible, and should not or store into the dentry. + Should not dereference pointers outside the dentry without lots of care (eg. d_parent, d_inode, d_name should not be used). However, our vfsmount is pinned, and RCU held, so the dentries and inodes won't disappear, neither will our sb or filesystem module. - ->i_sb and ->d_sb may be used. + ->d_sb may be used. It is a tricky calling convention because it needs to be called under "rcu-walk", ie. without any locks or references on things. diff --git a/fs/adfs/dir.c b/fs/adfs/dir.c index ade28bb058e3..0d138c0de293 100644 --- a/fs/adfs/dir.c +++ b/fs/adfs/dir.c @@ -191,8 +191,7 @@ const struct file_operations adfs_dir_operations = { }; static int -adfs_hash(const struct dentry *parent, const struct inode *inode, - struct qstr *qstr) +adfs_hash(const struct dentry *parent, struct qstr *qstr) { const unsigned int name_len = ADFS_SB(parent->d_sb)->s_namelen; const unsigned char *name; @@ -228,8 +227,7 @@ adfs_hash(const struct dentry *parent, const struct inode *inode, * requirements of the underlying filesystem. */ static int -adfs_compare(const struct dentry *parent, const struct inode *pinode, - const struct dentry *dentry, const struct inode *inode, +adfs_compare(const struct dentry *parent, const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { int i; diff --git a/fs/affs/namei.c b/fs/affs/namei.c index ff65884a7839..c36cbb4537a2 100644 --- a/fs/affs/namei.c +++ b/fs/affs/namei.c @@ -13,18 +13,12 @@ typedef int (*toupper_t)(int); static int affs_toupper(int ch); -static int affs_hash_dentry(const struct dentry *, - const struct inode *, struct qstr *); -static int affs_compare_dentry(const struct dentry *parent, - const struct inode *pinode, - const struct dentry *dentry, const struct inode *inode, +static int affs_hash_dentry(const struct dentry *, struct qstr *); +static int affs_compare_dentry(const struct dentry *parent, const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name); static int affs_intl_toupper(int ch); -static int affs_intl_hash_dentry(const struct dentry *, - const struct inode *, struct qstr *); -static int affs_intl_compare_dentry(const struct dentry *parent, - const struct inode *pinode, - const struct dentry *dentry, const struct inode *inode, +static int affs_intl_hash_dentry(const struct dentry *, struct qstr *); +static int affs_intl_compare_dentry(const struct dentry *parent, const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name); const struct dentry_operations affs_dentry_operations = { @@ -86,14 +80,12 @@ __affs_hash_dentry(struct qstr *qstr, toupper_t toupper) } static int -affs_hash_dentry(const struct dentry *dentry, const struct inode *inode, - struct qstr *qstr) +affs_hash_dentry(const struct dentry *dentry, struct qstr *qstr) { return __affs_hash_dentry(qstr, affs_toupper); } static int -affs_intl_hash_dentry(const struct dentry *dentry, const struct inode *inode, - struct qstr *qstr) +affs_intl_hash_dentry(const struct dentry *dentry, struct qstr *qstr) { return __affs_hash_dentry(qstr, affs_intl_toupper); } @@ -131,15 +123,13 @@ static inline int __affs_compare_dentry(unsigned int len, } static int -affs_compare_dentry(const struct dentry *parent, const struct inode *pinode, - const struct dentry *dentry, const struct inode *inode, +affs_compare_dentry(const struct dentry *parent, const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { return __affs_compare_dentry(len, str, name, affs_toupper); } static int -affs_intl_compare_dentry(const struct dentry *parent,const struct inode *pinode, - const struct dentry *dentry, const struct inode *inode, +affs_intl_compare_dentry(const struct dentry *parent, const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { return __affs_compare_dentry(len, str, name, affs_intl_toupper); diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 5699b5036ed8..5175aebf6737 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -822,8 +822,7 @@ const struct dentry_operations cifs_dentry_ops = { /* d_delete: cifs_d_delete, */ /* not needed except for debugging */ }; -static int cifs_ci_hash(const struct dentry *dentry, const struct inode *inode, - struct qstr *q) +static int cifs_ci_hash(const struct dentry *dentry, struct qstr *q) { struct nls_table *codepage = CIFS_SB(dentry->d_sb)->local_nls; unsigned long hash; @@ -838,12 +837,10 @@ static int cifs_ci_hash(const struct dentry *dentry, const struct inode *inode, return 0; } -static int cifs_ci_compare(const struct dentry *parent, - const struct inode *pinode, - const struct dentry *dentry, const struct inode *inode, +static int cifs_ci_compare(const struct dentry *parent, const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { - struct nls_table *codepage = CIFS_SB(pinode->i_sb)->local_nls; + struct nls_table *codepage = CIFS_SB(parent->d_sb)->local_nls; if ((name->len == len) && (nls_strnicmp(codepage, name->name, str, len) == 0)) diff --git a/fs/dcache.c b/fs/dcache.c index b692c7e097c5..3199fe6863a8 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1723,7 +1723,7 @@ EXPORT_SYMBOL(d_add_ci); * Do the slow-case of the dentry name compare. * * Unlike the dentry_cmp() function, we need to atomically - * load the name, length and inode information, so that the + * load the name and length information, so that the * filesystem can rely on them, and can use the 'name' and * 'len' information without worrying about walking off the * end of memory etc. @@ -1741,22 +1741,18 @@ enum slow_d_compare { static noinline enum slow_d_compare slow_dentry_cmp( const struct dentry *parent, - struct inode *inode, struct dentry *dentry, unsigned int seq, const struct qstr *name) { int tlen = dentry->d_name.len; const char *tname = dentry->d_name.name; - struct inode *i = dentry->d_inode; if (read_seqcount_retry(&dentry->d_seq, seq)) { cpu_relax(); return D_COMP_SEQRETRY; } - if (parent->d_op->d_compare(parent, inode, - dentry, i, - tlen, tname, name)) + if (parent->d_op->d_compare(parent, dentry, tlen, tname, name)) return D_COMP_NOMATCH; return D_COMP_OK; } @@ -1766,7 +1762,6 @@ static noinline enum slow_d_compare slow_dentry_cmp( * @parent: parent dentry * @name: qstr of name we wish to find * @seqp: returns d_seq value at the point where the dentry was found - * @inode: returns dentry->d_inode when the inode was found valid. * Returns: dentry, or NULL * * __d_lookup_rcu is the dcache lookup function for rcu-walk name @@ -1793,7 +1788,7 @@ static noinline enum slow_d_compare slow_dentry_cmp( */ struct dentry *__d_lookup_rcu(const struct dentry *parent, const struct qstr *name, - unsigned *seqp, struct inode *inode) + unsigned *seqp) { u64 hashlen = name->hash_len; const unsigned char *str = name->name; @@ -1827,11 +1822,10 @@ struct dentry *__d_lookup_rcu(const struct dentry *parent, seqretry: /* * The dentry sequence count protects us from concurrent - * renames, and thus protects inode, parent and name fields. + * renames, and thus protects parent and name fields. * * The caller must perform a seqcount check in order - * to do anything useful with the returned dentry, - * including using the 'd_inode' pointer. + * to do anything useful with the returned dentry. * * NOTE! We do a "raw" seqcount_begin here. That means that * we don't wait for the sequence count to stabilize if it @@ -1845,12 +1839,12 @@ seqretry: continue; if (d_unhashed(dentry)) continue; - *seqp = seq; if (unlikely(parent->d_flags & DCACHE_OP_COMPARE)) { if (dentry->d_name.hash != hashlen_hash(hashlen)) continue; - switch (slow_dentry_cmp(parent, inode, dentry, seq, name)) { + *seqp = seq; + switch (slow_dentry_cmp(parent, dentry, seq, name)) { case D_COMP_OK: return dentry; case D_COMP_NOMATCH: @@ -1862,6 +1856,7 @@ seqretry: if (dentry->d_name.hash_len != hashlen) continue; + *seqp = seq; if (!dentry_cmp(dentry, str, hashlen_len(hashlen))) return dentry; } @@ -1959,9 +1954,7 @@ struct dentry *__d_lookup(const struct dentry *parent, const struct qstr *name) if (parent->d_flags & DCACHE_OP_COMPARE) { int tlen = dentry->d_name.len; const char *tname = dentry->d_name.name; - if (parent->d_op->d_compare(parent, parent->d_inode, - dentry, dentry->d_inode, - tlen, tname, name)) + if (parent->d_op->d_compare(parent, dentry, tlen, tname, name)) goto next; } else { if (dentry->d_name.len != len) @@ -1998,7 +1991,7 @@ struct dentry *d_hash_and_lookup(struct dentry *dir, struct qstr *name) */ name->hash = full_name_hash(name->name, name->len); if (dir->d_flags & DCACHE_OP_HASH) { - int err = dir->d_op->d_hash(dir, dir->d_inode, name); + int err = dir->d_op->d_hash(dir, name); if (unlikely(err < 0)) return ERR_PTR(err); } diff --git a/fs/efivarfs/super.c b/fs/efivarfs/super.c index 141aee31884f..a8766b880c07 100644 --- a/fs/efivarfs/super.c +++ b/fs/efivarfs/super.c @@ -45,8 +45,8 @@ static struct super_block *efivarfs_sb; * So we need to perform a case-sensitive match on part 1 and a * case-insensitive match on part 2. */ -static int efivarfs_d_compare(const struct dentry *parent, const struct inode *pinode, - const struct dentry *dentry, const struct inode *inode, +static int efivarfs_d_compare(const struct dentry *parent, + const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { @@ -63,8 +63,7 @@ static int efivarfs_d_compare(const struct dentry *parent, const struct inode *p return strncasecmp(name->name + guid, str + guid, EFI_VARIABLE_GUID_LEN); } -static int efivarfs_d_hash(const struct dentry *dentry, - const struct inode *inode, struct qstr *qstr) +static int efivarfs_d_hash(const struct dentry *dentry, struct qstr *qstr) { unsigned long hash = init_name_hash(); const unsigned char *s = qstr->name; @@ -108,7 +107,7 @@ static struct dentry *efivarfs_alloc_dentry(struct dentry *parent, char *name) q.name = name; q.len = strlen(name); - err = efivarfs_d_hash(NULL, NULL, &q); + err = efivarfs_d_hash(NULL, &q); if (err) return ERR_PTR(err); diff --git a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c index 081b759cff83..a783b0e1272a 100644 --- a/fs/fat/namei_msdos.c +++ b/fs/fat/namei_msdos.c @@ -148,8 +148,7 @@ static int msdos_find(struct inode *dir, const unsigned char *name, int len, * that the existing dentry can be used. The msdos fs routines will * return ENOENT or EINVAL as appropriate. */ -static int msdos_hash(const struct dentry *dentry, const struct inode *inode, - struct qstr *qstr) +static int msdos_hash(const struct dentry *dentry, struct qstr *qstr) { struct fat_mount_options *options = &MSDOS_SB(dentry->d_sb)->options; unsigned char msdos_name[MSDOS_NAME]; @@ -165,8 +164,7 @@ static int msdos_hash(const struct dentry *dentry, const struct inode *inode, * Compare two msdos names. If either of the names are invalid, * we fall back to doing the standard name comparison. */ -static int msdos_cmp(const struct dentry *parent, const struct inode *pinode, - const struct dentry *dentry, const struct inode *inode, +static int msdos_cmp(const struct dentry *parent, const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { struct fat_mount_options *options = &MSDOS_SB(parent->d_sb)->options; diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c index 2da952036a3d..6df8d3d885e5 100644 --- a/fs/fat/namei_vfat.c +++ b/fs/fat/namei_vfat.c @@ -107,8 +107,7 @@ static unsigned int vfat_striptail_len(const struct qstr *qstr) * that the existing dentry can be used. The vfat fs routines will * return ENOENT or EINVAL as appropriate. */ -static int vfat_hash(const struct dentry *dentry, const struct inode *inode, - struct qstr *qstr) +static int vfat_hash(const struct dentry *dentry, struct qstr *qstr) { qstr->hash = full_name_hash(qstr->name, vfat_striptail_len(qstr)); return 0; @@ -120,8 +119,7 @@ static int vfat_hash(const struct dentry *dentry, const struct inode *inode, * that the existing dentry can be used. The vfat fs routines will * return ENOENT or EINVAL as appropriate. */ -static int vfat_hashi(const struct dentry *dentry, const struct inode *inode, - struct qstr *qstr) +static int vfat_hashi(const struct dentry *dentry, struct qstr *qstr) { struct nls_table *t = MSDOS_SB(dentry->d_sb)->nls_io; const unsigned char *name; @@ -142,8 +140,7 @@ static int vfat_hashi(const struct dentry *dentry, const struct inode *inode, /* * Case insensitive compare of two vfat names. */ -static int vfat_cmpi(const struct dentry *parent, const struct inode *pinode, - const struct dentry *dentry, const struct inode *inode, +static int vfat_cmpi(const struct dentry *parent, const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { struct nls_table *t = MSDOS_SB(parent->d_sb)->nls_io; @@ -162,8 +159,7 @@ static int vfat_cmpi(const struct dentry *parent, const struct inode *pinode, /* * Case sensitive compare of two vfat names. */ -static int vfat_cmp(const struct dentry *parent, const struct inode *pinode, - const struct dentry *dentry, const struct inode *inode, +static int vfat_cmp(const struct dentry *parent, const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { unsigned int alen, blen; diff --git a/fs/gfs2/dentry.c b/fs/gfs2/dentry.c index 4fddb3c22d25..f2448ab2aac5 100644 --- a/fs/gfs2/dentry.c +++ b/fs/gfs2/dentry.c @@ -109,8 +109,7 @@ fail: return 0; } -static int gfs2_dhash(const struct dentry *dentry, const struct inode *inode, - struct qstr *str) +static int gfs2_dhash(const struct dentry *dentry, struct qstr *str) { str->hash = gfs2_disk_hash(str->name, str->len); return 0; diff --git a/fs/hfs/hfs_fs.h b/fs/hfs/hfs_fs.h index a73b11839a41..0524cda47a6e 100644 --- a/fs/hfs/hfs_fs.h +++ b/fs/hfs/hfs_fs.h @@ -229,13 +229,10 @@ extern int hfs_part_find(struct super_block *, sector_t *, sector_t *); /* string.c */ extern const struct dentry_operations hfs_dentry_operations; -extern int hfs_hash_dentry(const struct dentry *, const struct inode *, - struct qstr *); +extern int hfs_hash_dentry(const struct dentry *, struct qstr *); extern int hfs_strcmp(const unsigned char *, unsigned int, const unsigned char *, unsigned int); -extern int hfs_compare_dentry(const struct dentry *parent, - const struct inode *pinode, - const struct dentry *dentry, const struct inode *inode, +extern int hfs_compare_dentry(const struct dentry *parent, const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name); /* trans.c */ diff --git a/fs/hfs/string.c b/fs/hfs/string.c index 495a976a3cc9..85b610c3909f 100644 --- a/fs/hfs/string.c +++ b/fs/hfs/string.c @@ -51,8 +51,7 @@ static unsigned char caseorder[256] = { /* * Hash a string to an integer in a case-independent way */ -int hfs_hash_dentry(const struct dentry *dentry, const struct inode *inode, - struct qstr *this) +int hfs_hash_dentry(const struct dentry *dentry, struct qstr *this) { const unsigned char *name = this->name; unsigned int hash, len = this->len; @@ -93,8 +92,7 @@ int hfs_strcmp(const unsigned char *s1, unsigned int len1, * Test for equality of two strings in the HFS filename character ordering. * return 1 on failure and 0 on success */ -int hfs_compare_dentry(const struct dentry *parent, const struct inode *pinode, - const struct dentry *dentry, const struct inode *inode, +int hfs_compare_dentry(const struct dentry *parent, const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { const unsigned char *n1, *n2; diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h index 60b0a3388b26..ede79317cfb8 100644 --- a/fs/hfsplus/hfsplus_fs.h +++ b/fs/hfsplus/hfsplus_fs.h @@ -495,11 +495,8 @@ int hfsplus_uni2asc(struct super_block *, const struct hfsplus_unistr *, char *, int *); int hfsplus_asc2uni(struct super_block *, struct hfsplus_unistr *, int, const char *, int); -int hfsplus_hash_dentry(const struct dentry *dentry, - const struct inode *inode, struct qstr *str); -int hfsplus_compare_dentry(const struct dentry *parent, - const struct inode *pinode, - const struct dentry *dentry, const struct inode *inode, +int hfsplus_hash_dentry(const struct dentry *dentry, struct qstr *str); +int hfsplus_compare_dentry(const struct dentry *parent, const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name); /* wrapper.c */ diff --git a/fs/hfsplus/unicode.c b/fs/hfsplus/unicode.c index 2c2e47dcfdd8..e8ef121a4d8b 100644 --- a/fs/hfsplus/unicode.c +++ b/fs/hfsplus/unicode.c @@ -334,8 +334,7 @@ int hfsplus_asc2uni(struct super_block *sb, * Composed unicode characters are decomposed and case-folding is performed * if the appropriate bits are (un)set on the superblock. */ -int hfsplus_hash_dentry(const struct dentry *dentry, const struct inode *inode, - struct qstr *str) +int hfsplus_hash_dentry(const struct dentry *dentry, struct qstr *str) { struct super_block *sb = dentry->d_sb; const char *astr; @@ -386,9 +385,7 @@ int hfsplus_hash_dentry(const struct dentry *dentry, const struct inode *inode, * Composed unicode characters are decomposed and case-folding is performed * if the appropriate bits are (un)set on the superblock. */ -int hfsplus_compare_dentry(const struct dentry *parent, - const struct inode *pinode, - const struct dentry *dentry, const struct inode *inode, +int hfsplus_compare_dentry(const struct dentry *parent, const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { struct super_block *sb = parent->d_sb; diff --git a/fs/hpfs/dentry.c b/fs/hpfs/dentry.c index 05d4816e4e77..fa27980f2229 100644 --- a/fs/hpfs/dentry.c +++ b/fs/hpfs/dentry.c @@ -12,8 +12,7 @@ * Note: the dentry argument is the parent dentry. */ -static int hpfs_hash_dentry(const struct dentry *dentry, const struct inode *inode, - struct qstr *qstr) +static int hpfs_hash_dentry(const struct dentry *dentry, struct qstr *qstr) { unsigned long hash; int i; @@ -35,9 +34,7 @@ static int hpfs_hash_dentry(const struct dentry *dentry, const struct inode *ino return 0; } -static int hpfs_compare_dentry(const struct dentry *parent, - const struct inode *pinode, - const struct dentry *dentry, const struct inode *inode, +static int hpfs_compare_dentry(const struct dentry *parent, const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { unsigned al = len; diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index d9b8aebdeb22..c348d6d88624 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -28,31 +28,23 @@ #define BEQUIET -static int isofs_hashi(const struct dentry *parent, const struct inode *inode, - struct qstr *qstr); -static int isofs_hash(const struct dentry *parent, const struct inode *inode, - struct qstr *qstr); +static int isofs_hashi(const struct dentry *parent, struct qstr *qstr); +static int isofs_hash(const struct dentry *parent, struct qstr *qstr); static int isofs_dentry_cmpi(const struct dentry *parent, - const struct inode *pinode, - const struct dentry *dentry, const struct inode *inode, + const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name); static int isofs_dentry_cmp(const struct dentry *parent, - const struct inode *pinode, - const struct dentry *dentry, const struct inode *inode, + const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name); #ifdef CONFIG_JOLIET -static int isofs_hashi_ms(const struct dentry *parent, const struct inode *inode, - struct qstr *qstr); -static int isofs_hash_ms(const struct dentry *parent, const struct inode *inode, - struct qstr *qstr); +static int isofs_hashi_ms(const struct dentry *parent, struct qstr *qstr); +static int isofs_hash_ms(const struct dentry *parent, struct qstr *qstr); static int isofs_dentry_cmpi_ms(const struct dentry *parent, - const struct inode *pinode, - const struct dentry *dentry, const struct inode *inode, + const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name); static int isofs_dentry_cmp_ms(const struct dentry *parent, - const struct inode *pinode, - const struct dentry *dentry, const struct inode *inode, + const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name); #endif @@ -265,30 +257,26 @@ static int isofs_dentry_cmp_common( } static int -isofs_hash(const struct dentry *dentry, const struct inode *inode, - struct qstr *qstr) +isofs_hash(const struct dentry *dentry, struct qstr *qstr) { return isofs_hash_common(dentry, qstr, 0); } static int -isofs_hashi(const struct dentry *dentry, const struct inode *inode, - struct qstr *qstr) +isofs_hashi(const struct dentry *dentry, struct qstr *qstr) { return isofs_hashi_common(dentry, qstr, 0); } static int -isofs_dentry_cmp(const struct dentry *parent, const struct inode *pinode, - const struct dentry *dentry, const struct inode *inode, +isofs_dentry_cmp(const struct dentry *parent, const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { return isofs_dentry_cmp_common(len, str, name, 0, 0); } static int -isofs_dentry_cmpi(const struct dentry *parent, const struct inode *pinode, - const struct dentry *dentry, const struct inode *inode, +isofs_dentry_cmpi(const struct dentry *parent, const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { return isofs_dentry_cmp_common(len, str, name, 0, 1); @@ -296,30 +284,26 @@ isofs_dentry_cmpi(const struct dentry *parent, const struct inode *pinode, #ifdef CONFIG_JOLIET static int -isofs_hash_ms(const struct dentry *dentry, const struct inode *inode, - struct qstr *qstr) +isofs_hash_ms(const struct dentry *dentry, struct qstr *qstr) { return isofs_hash_common(dentry, qstr, 1); } static int -isofs_hashi_ms(const struct dentry *dentry, const struct inode *inode, - struct qstr *qstr) +isofs_hashi_ms(const struct dentry *dentry, struct qstr *qstr) { return isofs_hashi_common(dentry, qstr, 1); } static int -isofs_dentry_cmp_ms(const struct dentry *parent, const struct inode *pinode, - const struct dentry *dentry, const struct inode *inode, +isofs_dentry_cmp_ms(const struct dentry *parent, const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { return isofs_dentry_cmp_common(len, str, name, 1, 0); } static int -isofs_dentry_cmpi_ms(const struct dentry *parent, const struct inode *pinode, - const struct dentry *dentry, const struct inode *inode, +isofs_dentry_cmpi_ms(const struct dentry *parent, const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { return isofs_dentry_cmp_common(len, str, name, 1, 1); diff --git a/fs/isofs/namei.c b/fs/isofs/namei.c index c167028844ed..95295640d9c8 100644 --- a/fs/isofs/namei.c +++ b/fs/isofs/namei.c @@ -37,8 +37,7 @@ isofs_cmp(struct dentry *dentry, const char *compare, int dlen) qstr.name = compare; qstr.len = dlen; - return dentry->d_op->d_compare(NULL, NULL, NULL, NULL, - dentry->d_name.len, dentry->d_name.name, &qstr); + return dentry->d_op->d_compare(NULL, NULL, dentry->d_name.len, dentry->d_name.name, &qstr); } /* diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c index 89186b7b9002..8b19027291d6 100644 --- a/fs/jfs/namei.c +++ b/fs/jfs/namei.c @@ -1538,8 +1538,7 @@ const struct file_operations jfs_dir_operations = { .llseek = generic_file_llseek, }; -static int jfs_ci_hash(const struct dentry *dir, const struct inode *inode, - struct qstr *this) +static int jfs_ci_hash(const struct dentry *dir, struct qstr *this) { unsigned long hash; int i; @@ -1552,9 +1551,7 @@ static int jfs_ci_hash(const struct dentry *dir, const struct inode *inode, return 0; } -static int jfs_ci_compare(const struct dentry *parent, - const struct inode *pinode, - const struct dentry *dentry, const struct inode *inode, +static int jfs_ci_compare(const struct dentry *parent, const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { int i, result = 1; diff --git a/fs/namei.c b/fs/namei.c index 66998b06d822..b2beee7a733f 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1352,7 +1352,7 @@ static int lookup_fast(struct nameidata *nd, */ if (nd->flags & LOOKUP_RCU) { unsigned seq; - dentry = __d_lookup_rcu(parent, &nd->last, &seq, nd->inode); + dentry = __d_lookup_rcu(parent, &nd->last, &seq); if (!dentry) goto unlazy; @@ -1787,8 +1787,7 @@ static int link_path_walk(const char *name, struct nameidata *nd) struct dentry *parent = nd->path.dentry; nd->flags &= ~LOOKUP_JUMPED; if (unlikely(parent->d_flags & DCACHE_OP_HASH)) { - err = parent->d_op->d_hash(parent, nd->inode, - &this); + err = parent->d_op->d_hash(parent, &this); if (err < 0) break; } @@ -2121,7 +2120,7 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len) * to use its own hash.. */ if (base->d_flags & DCACHE_OP_HASH) { - int err = base->d_op->d_hash(base, base->d_inode, &this); + int err = base->d_op->d_hash(base, &this); if (err < 0) return ERR_PTR(err); } diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c index 3bc105d36f10..3be047474bfc 100644 --- a/fs/ncpfs/dir.c +++ b/fs/ncpfs/dir.c @@ -73,10 +73,8 @@ const struct inode_operations ncp_dir_inode_operations = * Dentry operations routines */ static int ncp_lookup_validate(struct dentry *, unsigned int); -static int ncp_hash_dentry(const struct dentry *, const struct inode *, - struct qstr *); -static int ncp_compare_dentry(const struct dentry *, const struct inode *, - const struct dentry *, const struct inode *, +static int ncp_hash_dentry(const struct dentry *, struct qstr *); +static int ncp_compare_dentry(const struct dentry *, const struct dentry *, unsigned int, const char *, const struct qstr *); static int ncp_delete_dentry(const struct dentry *); @@ -119,11 +117,19 @@ static inline int ncp_case_sensitive(const struct inode *i) /* * Note: leave the hash unchanged if the directory * is case-sensitive. + * + * Accessing the parent inode can be racy under RCU pathwalking. + * Use ACCESS_ONCE() to make sure we use _one_ particular inode, + * the callers will handle races. */ static int -ncp_hash_dentry(const struct dentry *dentry, const struct inode *inode, - struct qstr *this) +ncp_hash_dentry(const struct dentry *dentry, struct qstr *this) { + struct inode *inode = ACCESS_ONCE(dentry->d_inode); + + if (!inode) + return 0; + if (!ncp_case_sensitive(inode)) { struct super_block *sb = dentry->d_sb; struct nls_table *t; @@ -140,14 +146,24 @@ ncp_hash_dentry(const struct dentry *dentry, const struct inode *inode, return 0; } +/* + * Accessing the parent inode can be racy under RCU pathwalking. + * Use ACCESS_ONCE() to make sure we use _one_ particular inode, + * the callers will handle races. + */ static int -ncp_compare_dentry(const struct dentry *parent, const struct inode *pinode, - const struct dentry *dentry, const struct inode *inode, +ncp_compare_dentry(const struct dentry *parent, const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { + struct inode *pinode; + if (len != name->len) return 1; + pinode = ACCESS_ONCE(parent->d_inode); + if (!pinode) + return 1; + if (ncp_case_sensitive(pinode)) return strncmp(str, name->name, len); diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index f3a570e7c257..71290463a1d3 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -796,15 +796,16 @@ static int sysctl_is_seen(struct ctl_table_header *p) return res; } -static int proc_sys_compare(const struct dentry *parent, - const struct inode *pinode, - const struct dentry *dentry, const struct inode *inode, +static int proc_sys_compare(const struct dentry *parent, const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { struct ctl_table_header *head; + struct inode *inode; + /* Although proc doesn't have negative dentries, rcu-walk means * that inode here can be NULL */ /* AV: can it, indeed? */ + inode = ACCESS_ONCE(dentry->d_inode); if (!inode) return 1; if (name->len != len) diff --git a/fs/sysv/namei.c b/fs/sysv/namei.c index 1c0d5f264767..731b2bbcaab3 100644 --- a/fs/sysv/namei.c +++ b/fs/sysv/namei.c @@ -27,8 +27,7 @@ static int add_nondir(struct dentry *dentry, struct inode *inode) return err; } -static int sysv_hash(const struct dentry *dentry, const struct inode *inode, - struct qstr *qstr) +static int sysv_hash(const struct dentry *dentry, struct qstr *qstr) { /* Truncate the name in place, avoids having to define a compare function. */ diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 86da7595ba31..f42dbe145479 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -146,10 +146,8 @@ enum dentry_d_lock_class struct dentry_operations { int (*d_revalidate)(struct dentry *, unsigned int); int (*d_weak_revalidate)(struct dentry *, unsigned int); - int (*d_hash)(const struct dentry *, const struct inode *, - struct qstr *); - int (*d_compare)(const struct dentry *, const struct inode *, - const struct dentry *, const struct inode *, + int (*d_hash)(const struct dentry *, struct qstr *); + int (*d_compare)(const struct dentry *, const struct dentry *, unsigned int, const char *, const struct qstr *); int (*d_delete)(const struct dentry *); void (*d_release)(struct dentry *); @@ -302,8 +300,7 @@ extern struct dentry *d_lookup(const struct dentry *, const struct qstr *); extern struct dentry *d_hash_and_lookup(struct dentry *, struct qstr *); extern struct dentry *__d_lookup(const struct dentry *, const struct qstr *); extern struct dentry *__d_lookup_rcu(const struct dentry *parent, - const struct qstr *name, - unsigned *seq, struct inode *inode); + const struct qstr *name, unsigned *seq); /** * __d_rcu_to_refcount - take a refcount on dentry if sequence check is ok -- cgit v1.2.3 From f891a29f46553a384edbaa0f6ac446b1d03bccac Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 21 Jun 2013 08:58:09 -0400 Subject: locks: drop the unused filp argument to posix_unblock_lock Signed-off-by: Jeff Layton Signed-off-by: Al Viro --- fs/lockd/svclock.c | 2 +- fs/locks.c | 4 +--- include/linux/fs.h | 5 ++--- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index e703318c41df..a469098682c4 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -276,7 +276,7 @@ static int nlmsvc_unlink_block(struct nlm_block *block) dprintk("lockd: unlinking block %p...\n", block); /* Remove block from list */ - status = posix_unblock_lock(block->b_file->f_file, &block->b_call->a_args.lock.fl); + status = posix_unblock_lock(&block->b_call->a_args.lock.fl); nlmsvc_remove_block(block); return status; } diff --git a/fs/locks.c b/fs/locks.c index cb424a4fed71..72fb2b722211 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -2099,13 +2099,12 @@ void locks_remove_flock(struct file *filp) /** * posix_unblock_lock - stop waiting for a file lock - * @filp: how the file was opened * @waiter: the lock which was waiting * * lockd needs to block waiting for locks. */ int -posix_unblock_lock(struct file *filp, struct file_lock *waiter) +posix_unblock_lock(struct file_lock *waiter) { int status = 0; @@ -2117,7 +2116,6 @@ posix_unblock_lock(struct file *filp, struct file_lock *waiter) unlock_flocks(); return status; } - EXPORT_SYMBOL(posix_unblock_lock); /** diff --git a/include/linux/fs.h b/include/linux/fs.h index 68f10204ab29..172303655702 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -994,7 +994,7 @@ extern void locks_release_private(struct file_lock *); extern void posix_test_lock(struct file *, struct file_lock *); extern int posix_lock_file(struct file *, struct file_lock *, struct file_lock *); extern int posix_lock_file_wait(struct file *, struct file_lock *); -extern int posix_unblock_lock(struct file *, struct file_lock *); +extern int posix_unblock_lock(struct file_lock *); extern int vfs_test_lock(struct file *, struct file_lock *); extern int vfs_lock_file(struct file *, unsigned int, struct file_lock *, struct file_lock *); extern int vfs_cancel_lock(struct file *filp, struct file_lock *fl); @@ -1084,8 +1084,7 @@ static inline int posix_lock_file_wait(struct file *filp, struct file_lock *fl) return -ENOLCK; } -static inline int posix_unblock_lock(struct file *filp, - struct file_lock *waiter) +static inline int posix_unblock_lock(struct file_lock *waiter) { return -ENOENT; } -- cgit v1.2.3 From 1a9e64a7118c5ad13dd5119da18375a5bd45b330 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 21 Jun 2013 08:58:10 -0400 Subject: cifs: use posix_unblock_lock instead of locks_delete_block commit 66189be74 (CIFS: Fix VFS lock usage for oplocked files) exported the locks_delete_block symbol. There's already an exported helper function that provides this capability however, so make cifs use that instead and turn locks_delete_block back into a static function. Note that if fl->fl_next == NULL then this lock has already been through locks_delete_block(), so we should be OK to ignore an ENOENT error here and simply not retry the lock. Cc: Pavel Shilovsky Signed-off-by: Jeff Layton Acked-by: J. Bruce Fields Signed-off-by: Al Viro --- fs/cifs/file.c | 2 +- fs/locks.c | 3 +-- include/linux/fs.h | 5 ----- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 48b29d24c9f4..1686e4085646 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -999,7 +999,7 @@ try_again: rc = wait_event_interruptible(flock->fl_wait, !flock->fl_next); if (!rc) goto try_again; - locks_delete_block(flock); + posix_unblock_lock(flock); } return rc; } diff --git a/fs/locks.c b/fs/locks.c index 72fb2b722211..d732e2226f17 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -496,13 +496,12 @@ static void __locks_delete_block(struct file_lock *waiter) /* */ -void locks_delete_block(struct file_lock *waiter) +static void locks_delete_block(struct file_lock *waiter) { lock_flocks(); __locks_delete_block(waiter); unlock_flocks(); } -EXPORT_SYMBOL(locks_delete_block); /* Insert waiter into blocker's block list. * We use a circular list so that processes can be easily woken up in diff --git a/include/linux/fs.h b/include/linux/fs.h index 172303655702..6cfc9a29a783 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1006,7 +1006,6 @@ extern int vfs_setlease(struct file *, long, struct file_lock **); extern int lease_modify(struct file_lock **, int); extern int lock_may_read(struct inode *, loff_t start, unsigned long count); extern int lock_may_write(struct inode *, loff_t start, unsigned long count); -extern void locks_delete_block(struct file_lock *waiter); extern void lock_flocks(void); extern void unlock_flocks(void); #else /* !CONFIG_FILE_LOCKING */ @@ -1150,10 +1149,6 @@ static inline int lock_may_write(struct inode *inode, loff_t start, return 1; } -static inline void locks_delete_block(struct file_lock *waiter) -{ -} - static inline void lock_flocks(void) { } -- cgit v1.2.3 From d4f22d19dffed6d9b50de4123f66b91875464435 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 21 Jun 2013 08:58:11 -0400 Subject: locks: make generic_add_lease and generic_delete_lease static Signed-off-by: Jeff Layton Acked-by: J. Bruce Fields Signed-off-by: Al Viro --- fs/locks.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/locks.c b/fs/locks.c index d732e2226f17..804bb9e01a65 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -1337,7 +1337,7 @@ int fcntl_getlease(struct file *filp) return type; } -int generic_add_lease(struct file *filp, long arg, struct file_lock **flp) +static int generic_add_lease(struct file *filp, long arg, struct file_lock **flp) { struct file_lock *fl, **before, **my_before = NULL, *lease; struct dentry *dentry = filp->f_path.dentry; @@ -1402,7 +1402,7 @@ out: return error; } -int generic_delete_lease(struct file *filp, struct file_lock **flp) +static int generic_delete_lease(struct file *filp, struct file_lock **flp) { struct file_lock *fl, **before; struct dentry *dentry = filp->f_path.dentry; -- cgit v1.2.3 From 1cb360125966cb6cb594e414ea80a0154617b846 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 21 Jun 2013 08:58:12 -0400 Subject: locks: comment cleanups and clarifications Signed-off-by: Jeff Layton Signed-off-by: Al Viro --- fs/locks.c | 21 +++++++++++++-------- include/linux/fs.h | 18 ++++++++++++++++++ 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/fs/locks.c b/fs/locks.c index 804bb9e01a65..ddeab49fe2be 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -518,9 +518,10 @@ static void locks_insert_block(struct file_lock *blocker, list_add(&waiter->fl_link, &blocked_list); } -/* Wake up processes blocked waiting for blocker. - * If told to wait then schedule the processes until the block list - * is empty, otherwise empty the block list ourselves. +/* + * Wake up processes blocked waiting for blocker. + * + * Must be called with the file_lock_lock held! */ static void locks_wake_up_blocks(struct file_lock *blocker) { @@ -806,6 +807,11 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str } lock_flocks(); + /* + * New lock request. Walk all POSIX locks and look for conflicts. If + * there are any, either return error or put the request on the + * blocker's list of waiters and the global blocked_list. + */ if (request->fl_type != F_UNLCK) { for_each_lock(inode, before) { fl = *before; @@ -844,7 +850,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str before = &fl->fl_next; } - /* Process locks with this owner. */ + /* Process locks with this owner. */ while ((fl = *before) && posix_same_owner(request, fl)) { /* Detect adjacent or overlapping regions (if same lock type) */ @@ -930,10 +936,9 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str } /* - * The above code only modifies existing locks in case of - * merging or replacing. If new lock(s) need to be inserted - * all modifications are done bellow this, so it's safe yet to - * bail out. + * The above code only modifies existing locks in case of merging or + * replacing. If new lock(s) need to be inserted all modifications are + * done below this, so it's safe yet to bail out. */ error = -ENOLCK; /* "no luck" */ if (right && left == right && !new_fl2) diff --git a/include/linux/fs.h b/include/linux/fs.h index 6cfc9a29a783..ed9fdaaf3223 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -926,6 +926,24 @@ int locks_in_grace(struct net *); /* that will die - we need it for nfs_lock_info */ #include +/* + * struct file_lock represents a generic "file lock". It's used to represent + * POSIX byte range locks, BSD (flock) locks, and leases. It's important to + * note that the same struct is used to represent both a request for a lock and + * the lock itself, but the same object is never used for both. + * + * FIXME: should we create a separate "struct lock_request" to help distinguish + * these two uses? + * + * The i_flock list is ordered by: + * + * 1) lock type -- FL_LEASEs first, then FL_FLOCK, and finally FL_POSIX + * 2) lock owner + * 3) lock range start + * 4) lock range end + * + * Obviously, the last two criteria only matter for POSIX locks. + */ struct file_lock { struct file_lock *fl_next; /* singly linked list for this inode */ struct list_head fl_link; /* doubly linked list of all locks */ -- cgit v1.2.3 From b9746ef80fa6963998866c1b6269c9cf3e9ea188 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 21 Jun 2013 08:58:13 -0400 Subject: locks: make "added" in __posix_lock_file a bool Signed-off-by: Jeff Layton Acked-by: J. Bruce Fields Signed-off-by: Al Viro --- fs/locks.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/fs/locks.c b/fs/locks.c index ddeab49fe2be..1d6cb28816be 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -791,7 +791,8 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str struct file_lock *left = NULL; struct file_lock *right = NULL; struct file_lock **before; - int error, added = 0; + int error; + bool added = false; /* * We may need two file_lock structures for this operation, @@ -885,7 +886,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str continue; } request = fl; - added = 1; + added = true; } else { /* Processing for different lock types is a bit @@ -896,7 +897,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str if (fl->fl_start > request->fl_end) break; if (request->fl_type == F_UNLCK) - added = 1; + added = true; if (fl->fl_start < request->fl_start) left = fl; /* If the next lock in the list has a higher end @@ -926,7 +927,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str locks_release_private(fl); locks_copy_private(fl, request); request = fl; - added = 1; + added = true; } } /* Go on to next lock. -- cgit v1.2.3 From 889746917193ab3007a779d65231510715b20fb6 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 21 Jun 2013 08:58:14 -0400 Subject: locks: encapsulate the fl_link list handling Move the fl_link list handling routines into a separate set of helpers. Also ensure that locks and requests are always put on global lists last (after fully initializing them) and are taken off before unintializing them. Signed-off-by: Jeff Layton Signed-off-by: Al Viro --- fs/locks.c | 45 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/fs/locks.c b/fs/locks.c index 1d6cb28816be..89d898bce166 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -153,13 +153,15 @@ int lease_break_time = 45; #define for_each_lock(inode, lockp) \ for (lockp = &inode->i_flock; *lockp != NULL; lockp = &(*lockp)->fl_next) +/* The global file_lock_list is only used for displaying /proc/locks. */ static LIST_HEAD(file_lock_list); + +/* The blocked_list is used to find POSIX lock loops for deadlock detection. */ static LIST_HEAD(blocked_list); + +/* Protects the two list heads above, plus the inode->i_flock list */ static DEFINE_SPINLOCK(file_lock_lock); -/* - * Protects the two list heads above, plus the inode->i_flock list - */ void lock_flocks(void) { spin_lock(&file_lock_lock); @@ -484,13 +486,37 @@ static int posix_same_owner(struct file_lock *fl1, struct file_lock *fl2) return fl1->fl_owner == fl2->fl_owner; } +static inline void +locks_insert_global_locks(struct file_lock *fl) +{ + list_add_tail(&fl->fl_link, &file_lock_list); +} + +static inline void +locks_delete_global_locks(struct file_lock *fl) +{ + list_del_init(&fl->fl_link); +} + +static inline void +locks_insert_global_blocked(struct file_lock *waiter) +{ + list_add(&waiter->fl_link, &blocked_list); +} + +static inline void +locks_delete_global_blocked(struct file_lock *waiter) +{ + list_del_init(&waiter->fl_link); +} + /* Remove waiter from blocker's block list. * When blocker ends up pointing to itself then the list is empty. */ static void __locks_delete_block(struct file_lock *waiter) { + locks_delete_global_blocked(waiter); list_del_init(&waiter->fl_block); - list_del_init(&waiter->fl_link); waiter->fl_next = NULL; } @@ -512,10 +538,10 @@ static void locks_insert_block(struct file_lock *blocker, struct file_lock *waiter) { BUG_ON(!list_empty(&waiter->fl_block)); - list_add_tail(&waiter->fl_block, &blocker->fl_block); waiter->fl_next = blocker; + list_add_tail(&waiter->fl_block, &blocker->fl_block); if (IS_POSIX(blocker)) - list_add(&waiter->fl_link, &blocked_list); + locks_insert_global_blocked(request); } /* @@ -543,13 +569,13 @@ static void locks_wake_up_blocks(struct file_lock *blocker) */ static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl) { - list_add(&fl->fl_link, &file_lock_list); - fl->fl_nspid = get_pid(task_tgid(current)); /* insert into file's list */ fl->fl_next = *pos; *pos = fl; + + locks_insert_global_locks(fl); } /* @@ -562,9 +588,10 @@ static void locks_delete_lock(struct file_lock **thisfl_p) { struct file_lock *fl = *thisfl_p; + locks_delete_global_locks(fl); + *thisfl_p = fl->fl_next; fl->fl_next = NULL; - list_del_init(&fl->fl_link); if (fl->fl_nspid) { put_pid(fl->fl_nspid); -- cgit v1.2.3 From 1c8c601a8c0dc59fe64907dcd9d512a3d181ddc7 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 21 Jun 2013 08:58:15 -0400 Subject: locks: protect most of the file_lock handling with i_lock Having a global lock that protects all of this code is a clear scalability problem. Instead of doing that, move most of the code to be protected by the i_lock instead. The exceptions are the global lists that the ->fl_link sits on, and the ->fl_block list. ->fl_link is what connects these structures to the global lists, so we must ensure that we hold those locks when iterating over or updating these lists. Furthermore, sound deadlock detection requires that we hold the blocked_list state steady while checking for loops. We also must ensure that the search and update to the list are atomic. For the checking and insertion side of the blocked_list, push the acquisition of the global lock into __posix_lock_file and ensure that checking and update of the blocked_list is done without dropping the lock in between. On the removal side, when waking up blocked lock waiters, take the global lock before walking the blocked list and dequeue the waiters from the global list prior to removal from the fl_block list. With this, deadlock detection should be race free while we minimize excessive file_lock_lock thrashing. Finally, in order to avoid a lock inversion problem when handling /proc/locks output we must ensure that manipulations of the fl_block list are also protected by the file_lock_lock. Signed-off-by: Jeff Layton Signed-off-by: Al Viro --- Documentation/filesystems/Locking | 21 +++-- fs/afs/flock.c | 7 +- fs/ceph/locks.c | 2 +- fs/ceph/mds_client.c | 8 +- fs/cifs/cifsfs.c | 2 +- fs/cifs/file.c | 13 +-- fs/gfs2/file.c | 2 +- fs/lockd/svcsubs.c | 12 +-- fs/locks.c | 164 ++++++++++++++++++++++++-------------- fs/nfs/delegation.c | 10 +-- fs/nfs/nfs4state.c | 8 +- fs/nfsd/nfs4state.c | 8 +- include/linux/fs.h | 11 --- 13 files changed, 155 insertions(+), 113 deletions(-) diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index f94a362f408e..c2963a74fbc3 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -342,7 +342,7 @@ prototypes: locking rules: - file_lock_lock may block + inode->i_lock may block fl_copy_lock: yes no fl_release_private: maybe no @@ -355,12 +355,19 @@ prototypes: int (*lm_change)(struct file_lock **, int); locking rules: - file_lock_lock may block -lm_compare_owner: yes no -lm_notify: yes no -lm_grant: no no -lm_break: yes no -lm_change yes no + + inode->i_lock file_lock_lock may block +lm_compare_owner: yes[1] maybe no +lm_notify: yes yes no +lm_grant: no no no +lm_break: yes no no +lm_change yes no no + +[1]: ->lm_compare_owner is generally called with *an* inode->i_lock held. It +may not be the i_lock of the inode for either file_lock being compared! This is +the case with deadlock detection, since the code has to chase down the owners +of locks that may be entirely unrelated to the one on which the lock is being +acquired. When doing a search for deadlocks, the file_lock_lock is also held. --------------------------- buffer_head ----------------------------------- prototypes: diff --git a/fs/afs/flock.c b/fs/afs/flock.c index 2497bf306c70..a8cf2cff836c 100644 --- a/fs/afs/flock.c +++ b/fs/afs/flock.c @@ -252,7 +252,8 @@ static void afs_defer_unlock(struct afs_vnode *vnode, struct key *key) */ static int afs_do_setlk(struct file *file, struct file_lock *fl) { - struct afs_vnode *vnode = AFS_FS_I(file->f_mapping->host); + struct inode *inode = file_inode(file); + struct afs_vnode *vnode = AFS_FS_I(inode); afs_lock_type_t type; struct key *key = file->private_data; int ret; @@ -273,7 +274,7 @@ static int afs_do_setlk(struct file *file, struct file_lock *fl) type = (fl->fl_type == F_RDLCK) ? AFS_LOCK_READ : AFS_LOCK_WRITE; - lock_flocks(); + spin_lock(&inode->i_lock); /* make sure we've got a callback on this file and that our view of the * data version is up to date */ @@ -420,7 +421,7 @@ given_lock: afs_vnode_fetch_status(vnode, NULL, key); error: - unlock_flocks(); + spin_unlock(&inode->i_lock); _leave(" = %d", ret); return ret; diff --git a/fs/ceph/locks.c b/fs/ceph/locks.c index ebbf680378e2..690f73f42425 100644 --- a/fs/ceph/locks.c +++ b/fs/ceph/locks.c @@ -192,7 +192,7 @@ void ceph_count_locks(struct inode *inode, int *fcntl_count, int *flock_count) /** * Encode the flock and fcntl locks for the given inode into the ceph_filelock - * array. Must be called with lock_flocks() already held. + * array. Must be called with inode->i_lock already held. * If we encounter more of a specific lock type than expected, return -ENOSPC. */ int ceph_encode_locks_to_buffer(struct inode *inode, diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 4d2920304be8..74fd2898b2ab 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -2481,20 +2481,20 @@ static int encode_caps_cb(struct inode *inode, struct ceph_cap *cap, struct ceph_filelock *flocks; encode_again: - lock_flocks(); + spin_lock(&inode->i_lock); ceph_count_locks(inode, &num_fcntl_locks, &num_flock_locks); - unlock_flocks(); + spin_unlock(&inode->i_lock); flocks = kmalloc((num_fcntl_locks+num_flock_locks) * sizeof(struct ceph_filelock), GFP_NOFS); if (!flocks) { err = -ENOMEM; goto out_free; } - lock_flocks(); + spin_lock(&inode->i_lock); err = ceph_encode_locks_to_buffer(inode, flocks, num_fcntl_locks, num_flock_locks); - unlock_flocks(); + spin_unlock(&inode->i_lock); if (err) { kfree(flocks); if (err == -ENOSPC) diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 540c1ccfcdb2..a445e71746fa 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -765,7 +765,7 @@ static loff_t cifs_llseek(struct file *file, loff_t offset, int whence) static int cifs_setlease(struct file *file, long arg, struct file_lock **lease) { - /* note that this is called by vfs setlease with lock_flocks held + /* note that this is called by vfs setlease with i_lock held to protect *lease from going away */ struct inode *inode = file_inode(file); struct cifsFileInfo *cfile = file->private_data; diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 1686e4085646..0630710a9c3f 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1092,6 +1092,7 @@ struct lock_to_push { static int cifs_push_posix_locks(struct cifsFileInfo *cfile) { + struct inode *inode = cfile->dentry->d_inode; struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); struct file_lock *flock, **before; unsigned int count = 0, i = 0; @@ -1102,12 +1103,12 @@ cifs_push_posix_locks(struct cifsFileInfo *cfile) xid = get_xid(); - lock_flocks(); - cifs_for_each_lock(cfile->dentry->d_inode, before) { + spin_lock(&inode->i_lock); + cifs_for_each_lock(inode, before) { if ((*before)->fl_flags & FL_POSIX) count++; } - unlock_flocks(); + spin_unlock(&inode->i_lock); INIT_LIST_HEAD(&locks_to_send); @@ -1126,8 +1127,8 @@ cifs_push_posix_locks(struct cifsFileInfo *cfile) } el = locks_to_send.next; - lock_flocks(); - cifs_for_each_lock(cfile->dentry->d_inode, before) { + spin_lock(&inode->i_lock); + cifs_for_each_lock(inode, before) { flock = *before; if ((flock->fl_flags & FL_POSIX) == 0) continue; @@ -1152,7 +1153,7 @@ cifs_push_posix_locks(struct cifsFileInfo *cfile) lck->offset = flock->fl_start; el = el->next; } - unlock_flocks(); + spin_unlock(&inode->i_lock); list_for_each_entry_safe(lck, tmp, &locks_to_send, llist) { int stored_rc; diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index b3333371aebb..cebfd404c1d9 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c @@ -889,7 +889,7 @@ out_uninit: * cluster; until we do, disable leases (by just returning -EINVAL), * unless the administrator has requested purely local locking. * - * Locking: called under lock_flocks + * Locking: called under i_lock * * Returns: errno */ diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c index 97e87415b145..dc5c75930f0f 100644 --- a/fs/lockd/svcsubs.c +++ b/fs/lockd/svcsubs.c @@ -169,7 +169,7 @@ nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file, again: file->f_locks = 0; - lock_flocks(); /* protects i_flock list */ + spin_lock(&inode->i_lock); for (fl = inode->i_flock; fl; fl = fl->fl_next) { if (fl->fl_lmops != &nlmsvc_lock_operations) continue; @@ -181,7 +181,7 @@ again: if (match(lockhost, host)) { struct file_lock lock = *fl; - unlock_flocks(); + spin_unlock(&inode->i_lock); lock.fl_type = F_UNLCK; lock.fl_start = 0; lock.fl_end = OFFSET_MAX; @@ -193,7 +193,7 @@ again: goto again; } } - unlock_flocks(); + spin_unlock(&inode->i_lock); return 0; } @@ -228,14 +228,14 @@ nlm_file_inuse(struct nlm_file *file) if (file->f_count || !list_empty(&file->f_blocks) || file->f_shares) return 1; - lock_flocks(); + spin_lock(&inode->i_lock); for (fl = inode->i_flock; fl; fl = fl->fl_next) { if (fl->fl_lmops == &nlmsvc_lock_operations) { - unlock_flocks(); + spin_unlock(&inode->i_lock); return 1; } } - unlock_flocks(); + spin_unlock(&inode->i_lock); file->f_locks = 0; return 0; } diff --git a/fs/locks.c b/fs/locks.c index 89d898bce166..ce302d43822b 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -153,27 +153,37 @@ int lease_break_time = 45; #define for_each_lock(inode, lockp) \ for (lockp = &inode->i_flock; *lockp != NULL; lockp = &(*lockp)->fl_next) -/* The global file_lock_list is only used for displaying /proc/locks. */ +/* + * The global file_lock_list is only used for displaying /proc/locks. Protected + * by the file_lock_lock. + */ static LIST_HEAD(file_lock_list); -/* The blocked_list is used to find POSIX lock loops for deadlock detection. */ +/* + * The blocked_list is used to find POSIX lock loops for deadlock detection. + * Protected by file_lock_lock. + */ static LIST_HEAD(blocked_list); -/* Protects the two list heads above, plus the inode->i_flock list */ +/* + * This lock protects the blocked_list, and the file_lock_list. Generally, if + * you're accessing one of those lists, you want to be holding this lock. + * + * In addition, it also protects the fl->fl_block list, and the fl->fl_next + * pointer for file_lock structures that are acting as lock requests (in + * contrast to those that are acting as records of acquired locks). + * + * Note that when we acquire this lock in order to change the above fields, + * we often hold the i_lock as well. In certain cases, when reading the fields + * protected by this lock, we can skip acquiring it iff we already hold the + * i_lock. + * + * In particular, adding an entry to the fl_block list requires that you hold + * both the i_lock and the blocked_lock_lock (acquired in that order). Deleting + * an entry from the list however only requires the file_lock_lock. + */ static DEFINE_SPINLOCK(file_lock_lock); -void lock_flocks(void) -{ - spin_lock(&file_lock_lock); -} -EXPORT_SYMBOL_GPL(lock_flocks); - -void unlock_flocks(void) -{ - spin_unlock(&file_lock_lock); -} -EXPORT_SYMBOL_GPL(unlock_flocks); - static struct kmem_cache *filelock_cache __read_mostly; static void locks_init_lock_heads(struct file_lock *fl) @@ -489,13 +499,17 @@ static int posix_same_owner(struct file_lock *fl1, struct file_lock *fl2) static inline void locks_insert_global_locks(struct file_lock *fl) { + spin_lock(&file_lock_lock); list_add_tail(&fl->fl_link, &file_lock_list); + spin_unlock(&file_lock_lock); } static inline void locks_delete_global_locks(struct file_lock *fl) { + spin_lock(&file_lock_lock); list_del_init(&fl->fl_link); + spin_unlock(&file_lock_lock); } static inline void @@ -512,6 +526,8 @@ locks_delete_global_blocked(struct file_lock *waiter) /* Remove waiter from blocker's block list. * When blocker ends up pointing to itself then the list is empty. + * + * Must be called with file_lock_lock held. */ static void __locks_delete_block(struct file_lock *waiter) { @@ -520,37 +536,47 @@ static void __locks_delete_block(struct file_lock *waiter) waiter->fl_next = NULL; } -/* - */ static void locks_delete_block(struct file_lock *waiter) { - lock_flocks(); + spin_lock(&file_lock_lock); __locks_delete_block(waiter); - unlock_flocks(); + spin_unlock(&file_lock_lock); } /* Insert waiter into blocker's block list. * We use a circular list so that processes can be easily woken up in * the order they blocked. The documentation doesn't require this but * it seems like the reasonable thing to do. + * + * Must be called with file_lock_lock held! */ -static void locks_insert_block(struct file_lock *blocker, - struct file_lock *waiter) +static void __locks_insert_block(struct file_lock *blocker, + struct file_lock *waiter) { BUG_ON(!list_empty(&waiter->fl_block)); waiter->fl_next = blocker; list_add_tail(&waiter->fl_block, &blocker->fl_block); if (IS_POSIX(blocker)) - locks_insert_global_blocked(request); + locks_insert_global_blocked(waiter); +} + +/* Must be called with i_lock held. */ +static void locks_insert_block(struct file_lock *blocker, + struct file_lock *waiter) +{ + spin_lock(&file_lock_lock); + __locks_insert_block(blocker, waiter); + spin_unlock(&file_lock_lock); } /* * Wake up processes blocked waiting for blocker. * - * Must be called with the file_lock_lock held! + * Must be called with the inode->i_lock held! */ static void locks_wake_up_blocks(struct file_lock *blocker) { + spin_lock(&file_lock_lock); while (!list_empty(&blocker->fl_block)) { struct file_lock *waiter; @@ -562,10 +588,13 @@ static void locks_wake_up_blocks(struct file_lock *blocker) else wake_up(&waiter->fl_wait); } + spin_unlock(&file_lock_lock); } /* Insert file lock fl into an inode's lock list at the position indicated * by pos. At the same time add the lock to the global file lock list. + * + * Must be called with the i_lock held! */ static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl) { @@ -583,6 +612,8 @@ static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl) * Wake up processes that are blocked waiting for this lock, * notify the FS that the lock has been cleared and * finally free the lock. + * + * Must be called with the i_lock held! */ static void locks_delete_lock(struct file_lock **thisfl_p) { @@ -652,8 +683,9 @@ void posix_test_lock(struct file *filp, struct file_lock *fl) { struct file_lock *cfl; + struct inode *inode = file_inode(filp); - lock_flocks(); + spin_lock(&inode->i_lock); for (cfl = file_inode(filp)->i_flock; cfl; cfl = cfl->fl_next) { if (!IS_POSIX(cfl)) continue; @@ -666,7 +698,7 @@ posix_test_lock(struct file *filp, struct file_lock *fl) fl->fl_pid = pid_vnr(cfl->fl_nspid); } else fl->fl_type = F_UNLCK; - unlock_flocks(); + spin_unlock(&inode->i_lock); return; } EXPORT_SYMBOL(posix_test_lock); @@ -710,6 +742,7 @@ static struct file_lock *what_owner_is_waiting_for(struct file_lock *block_fl) return NULL; } +/* Must be called with the file_lock_lock held! */ static int posix_locks_deadlock(struct file_lock *caller_fl, struct file_lock *block_fl) { @@ -745,7 +778,7 @@ static int flock_lock_file(struct file *filp, struct file_lock *request) return -ENOMEM; } - lock_flocks(); + spin_lock(&inode->i_lock); if (request->fl_flags & FL_ACCESS) goto find_conflict; @@ -775,9 +808,9 @@ static int flock_lock_file(struct file *filp, struct file_lock *request) * give it the opportunity to lock the file. */ if (found) { - unlock_flocks(); + spin_unlock(&inode->i_lock); cond_resched(); - lock_flocks(); + spin_lock(&inode->i_lock); } find_conflict: @@ -804,7 +837,7 @@ find_conflict: error = 0; out: - unlock_flocks(); + spin_unlock(&inode->i_lock); if (new_fl) locks_free_lock(new_fl); return error; @@ -834,7 +867,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str new_fl2 = locks_alloc_lock(); } - lock_flocks(); + spin_lock(&inode->i_lock); /* * New lock request. Walk all POSIX locks and look for conflicts. If * there are any, either return error or put the request on the @@ -852,11 +885,17 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str error = -EAGAIN; if (!(request->fl_flags & FL_SLEEP)) goto out; + /* + * Deadlock detection and insertion into the blocked + * locks list must be done while holding the same lock! + */ error = -EDEADLK; - if (posix_locks_deadlock(request, fl)) - goto out; - error = FILE_LOCK_DEFERRED; - locks_insert_block(fl, request); + spin_lock(&file_lock_lock); + if (likely(!posix_locks_deadlock(request, fl))) { + error = FILE_LOCK_DEFERRED; + __locks_insert_block(fl, request); + } + spin_unlock(&file_lock_lock); goto out; } } @@ -1006,7 +1045,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str locks_wake_up_blocks(left); } out: - unlock_flocks(); + spin_unlock(&inode->i_lock); /* * Free any unused locks. */ @@ -1081,14 +1120,14 @@ int locks_mandatory_locked(struct inode *inode) /* * Search the lock list for this inode for any POSIX locks. */ - lock_flocks(); + spin_lock(&inode->i_lock); for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { if (!IS_POSIX(fl)) continue; if (fl->fl_owner != owner) break; } - unlock_flocks(); + spin_unlock(&inode->i_lock); return fl ? -EAGAIN : 0; } @@ -1231,7 +1270,7 @@ int __break_lease(struct inode *inode, unsigned int mode) if (IS_ERR(new_fl)) return PTR_ERR(new_fl); - lock_flocks(); + spin_lock(&inode->i_lock); time_out_leases(inode); @@ -1281,11 +1320,11 @@ restart: break_time++; } locks_insert_block(flock, new_fl); - unlock_flocks(); + spin_unlock(&inode->i_lock); error = wait_event_interruptible_timeout(new_fl->fl_wait, !new_fl->fl_next, break_time); - lock_flocks(); - __locks_delete_block(new_fl); + spin_lock(&inode->i_lock); + locks_delete_block(new_fl); if (error >= 0) { if (error == 0) time_out_leases(inode); @@ -1302,7 +1341,7 @@ restart: } out: - unlock_flocks(); + spin_unlock(&inode->i_lock); locks_free_lock(new_fl); return error; } @@ -1355,9 +1394,10 @@ EXPORT_SYMBOL(lease_get_mtime); int fcntl_getlease(struct file *filp) { struct file_lock *fl; + struct inode *inode = file_inode(filp); int type = F_UNLCK; - lock_flocks(); + spin_lock(&inode->i_lock); time_out_leases(file_inode(filp)); for (fl = file_inode(filp)->i_flock; fl && IS_LEASE(fl); fl = fl->fl_next) { @@ -1366,7 +1406,7 @@ int fcntl_getlease(struct file *filp) break; } } - unlock_flocks(); + spin_unlock(&inode->i_lock); return type; } @@ -1460,7 +1500,7 @@ static int generic_delete_lease(struct file *filp, struct file_lock **flp) * The (input) flp->fl_lmops->lm_break function is required * by break_lease(). * - * Called with file_lock_lock held. + * Called with inode->i_lock held. */ int generic_setlease(struct file *filp, long arg, struct file_lock **flp) { @@ -1529,11 +1569,12 @@ static int __vfs_setlease(struct file *filp, long arg, struct file_lock **lease) int vfs_setlease(struct file *filp, long arg, struct file_lock **lease) { + struct inode *inode = file_inode(filp); int error; - lock_flocks(); + spin_lock(&inode->i_lock); error = __vfs_setlease(filp, arg, lease); - unlock_flocks(); + spin_unlock(&inode->i_lock); return error; } @@ -1551,6 +1592,7 @@ static int do_fcntl_delete_lease(struct file *filp) static int do_fcntl_add_lease(unsigned int fd, struct file *filp, long arg) { struct file_lock *fl, *ret; + struct inode *inode = file_inode(filp); struct fasync_struct *new; int error; @@ -1564,10 +1606,10 @@ static int do_fcntl_add_lease(unsigned int fd, struct file *filp, long arg) return -ENOMEM; } ret = fl; - lock_flocks(); + spin_lock(&inode->i_lock); error = __vfs_setlease(filp, arg, &ret); if (error) { - unlock_flocks(); + spin_unlock(&inode->i_lock); locks_free_lock(fl); goto out_free_fasync; } @@ -1584,7 +1626,7 @@ static int do_fcntl_add_lease(unsigned int fd, struct file *filp, long arg) new = NULL; error = __f_setown(filp, task_pid(current), PIDTYPE_PID, 0); - unlock_flocks(); + spin_unlock(&inode->i_lock); out_free_fasync: if (new) @@ -2108,7 +2150,7 @@ void locks_remove_flock(struct file *filp) fl.fl_ops->fl_release_private(&fl); } - lock_flocks(); + spin_lock(&inode->i_lock); before = &inode->i_flock; while ((fl = *before) != NULL) { @@ -2126,7 +2168,7 @@ void locks_remove_flock(struct file *filp) } before = &fl->fl_next; } - unlock_flocks(); + spin_unlock(&inode->i_lock); } /** @@ -2140,12 +2182,12 @@ posix_unblock_lock(struct file_lock *waiter) { int status = 0; - lock_flocks(); + spin_lock(&file_lock_lock); if (waiter->fl_next) __locks_delete_block(waiter); else status = -ENOENT; - unlock_flocks(); + spin_unlock(&file_lock_lock); return status; } EXPORT_SYMBOL(posix_unblock_lock); @@ -2259,7 +2301,7 @@ static void *locks_start(struct seq_file *f, loff_t *pos) { loff_t *p = f->private; - lock_flocks(); + spin_lock(&file_lock_lock); *p = (*pos + 1); return seq_list_start(&file_lock_list, *pos); } @@ -2273,7 +2315,7 @@ static void *locks_next(struct seq_file *f, void *v, loff_t *pos) static void locks_stop(struct seq_file *f, void *v) { - unlock_flocks(); + spin_unlock(&file_lock_lock); } static const struct seq_operations locks_seq_operations = { @@ -2320,7 +2362,8 @@ int lock_may_read(struct inode *inode, loff_t start, unsigned long len) { struct file_lock *fl; int result = 1; - lock_flocks(); + + spin_lock(&inode->i_lock); for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { if (IS_POSIX(fl)) { if (fl->fl_type == F_RDLCK) @@ -2337,7 +2380,7 @@ int lock_may_read(struct inode *inode, loff_t start, unsigned long len) result = 0; break; } - unlock_flocks(); + spin_unlock(&inode->i_lock); return result; } @@ -2360,7 +2403,8 @@ int lock_may_write(struct inode *inode, loff_t start, unsigned long len) { struct file_lock *fl; int result = 1; - lock_flocks(); + + spin_lock(&inode->i_lock); for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { if (IS_POSIX(fl)) { if ((fl->fl_end < start) || (fl->fl_start > (start + len))) @@ -2375,7 +2419,7 @@ int lock_may_write(struct inode *inode, loff_t start, unsigned long len) result = 0; break; } - unlock_flocks(); + spin_unlock(&inode->i_lock); return result; } diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 57db3244f4d9..7ec4814e298d 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -73,20 +73,20 @@ static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_ if (inode->i_flock == NULL) goto out; - /* Protect inode->i_flock using the file locks lock */ - lock_flocks(); + /* Protect inode->i_flock using the i_lock */ + spin_lock(&inode->i_lock); for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK))) continue; if (nfs_file_open_context(fl->fl_file) != ctx) continue; - unlock_flocks(); + spin_unlock(&inode->i_lock); status = nfs4_lock_delegation_recall(fl, state, stateid); if (status < 0) goto out; - lock_flocks(); + spin_lock(&inode->i_lock); } - unlock_flocks(); + spin_unlock(&inode->i_lock); out: return status; } diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 1fab140764c4..ff10b4aa534c 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -1373,13 +1373,13 @@ static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_ /* Guard against delegation returns and new lock/unlock calls */ down_write(&nfsi->rwsem); /* Protect inode->i_flock using the BKL */ - lock_flocks(); + spin_lock(&inode->i_lock); for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK))) continue; if (nfs_file_open_context(fl->fl_file)->state != state) continue; - unlock_flocks(); + spin_unlock(&inode->i_lock); status = ops->recover_lock(state, fl); switch (status) { case 0: @@ -1406,9 +1406,9 @@ static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_ /* kill_proc(fl->fl_pid, SIGLOST, 1); */ status = 0; } - lock_flocks(); + spin_lock(&inode->i_lock); } - unlock_flocks(); + spin_unlock(&inode->i_lock); out: up_write(&nfsi->rwsem); return status; diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 316ec843dec2..f17051838b41 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2645,13 +2645,13 @@ static void nfsd_break_one_deleg(struct nfs4_delegation *dp) list_add_tail(&dp->dl_recall_lru, &nn->del_recall_lru); - /* only place dl_time is set. protected by lock_flocks*/ + /* Only place dl_time is set; protected by i_lock: */ dp->dl_time = get_seconds(); nfsd4_cb_recall(dp); } -/* Called from break_lease() with lock_flocks() held. */ +/* Called from break_lease() with i_lock held. */ static void nfsd_break_deleg_cb(struct file_lock *fl) { struct nfs4_file *fp = (struct nfs4_file *)fl->fl_owner; @@ -4520,7 +4520,7 @@ check_for_locks(struct nfs4_file *filp, struct nfs4_lockowner *lowner) struct inode *inode = filp->fi_inode; int status = 0; - lock_flocks(); + spin_lock(&inode->i_lock); for (flpp = &inode->i_flock; *flpp != NULL; flpp = &(*flpp)->fl_next) { if ((*flpp)->fl_owner == (fl_owner_t)lowner) { status = 1; @@ -4528,7 +4528,7 @@ check_for_locks(struct nfs4_file *filp, struct nfs4_lockowner *lowner) } } out: - unlock_flocks(); + spin_unlock(&inode->i_lock); return status; } diff --git a/include/linux/fs.h b/include/linux/fs.h index ed9fdaaf3223..24fe998795e1 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1024,8 +1024,6 @@ extern int vfs_setlease(struct file *, long, struct file_lock **); extern int lease_modify(struct file_lock **, int); extern int lock_may_read(struct inode *, loff_t start, unsigned long count); extern int lock_may_write(struct inode *, loff_t start, unsigned long count); -extern void lock_flocks(void); -extern void unlock_flocks(void); #else /* !CONFIG_FILE_LOCKING */ static inline int fcntl_getlk(struct file *file, struct flock __user *user) { @@ -1166,15 +1164,6 @@ static inline int lock_may_write(struct inode *inode, loff_t start, { return 1; } - -static inline void lock_flocks(void) -{ -} - -static inline void unlock_flocks(void) -{ -} - #endif /* !CONFIG_FILE_LOCKING */ -- cgit v1.2.3 From 4e8c765d384e549f9b542ea0bd42e2aa227e1404 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 21 Jun 2013 08:58:16 -0400 Subject: locks: avoid taking global lock if possible when waking up blocked waiters Since we always hold the i_lock when inserting a new waiter onto the fl_block list, we can avoid taking the global lock at all if we find that it's empty when we go to wake up blocked waiters. Signed-off-by: Jeff Layton Signed-off-by: Al Viro --- fs/locks.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/fs/locks.c b/fs/locks.c index ce302d43822b..84e269fc4c69 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -548,7 +548,10 @@ static void locks_delete_block(struct file_lock *waiter) * the order they blocked. The documentation doesn't require this but * it seems like the reasonable thing to do. * - * Must be called with file_lock_lock held! + * Must be called with both the i_lock and file_lock_lock held. The fl_block + * list itself is protected by the file_lock_list, but by ensuring that the + * i_lock is also held on insertions we can avoid taking the file_lock_lock + * in some cases when we see that the fl_block list is empty. */ static void __locks_insert_block(struct file_lock *blocker, struct file_lock *waiter) @@ -576,6 +579,16 @@ static void locks_insert_block(struct file_lock *blocker, */ static void locks_wake_up_blocks(struct file_lock *blocker) { + /* + * Avoid taking global lock if list is empty. This is safe since new + * blocked requests are only added to the list under the i_lock, and + * the i_lock is always held here. Note that removal from the fl_block + * list does not require the i_lock, so we must recheck list_empty() + * after acquiring the file_lock_lock. + */ + if (list_empty(&blocker->fl_block)) + return; + spin_lock(&file_lock_lock); while (!list_empty(&blocker->fl_block)) { struct file_lock *waiter; -- cgit v1.2.3 From 139ca04ee572fea6c0c105e88aba3a534efcd7c4 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 21 Jun 2013 08:58:17 -0400 Subject: locks: convert fl_link to a hlist_node Testing has shown that iterating over the blocked_list for deadlock detection turns out to be a bottleneck. In order to alleviate that, begin the process of turning it into a hashtable. We start by turning the fl_link into a hlist_node and the global lists into hlists. A later patch will do the conversion of the blocked_list to a hashtable. Signed-off-by: Jeff Layton Acked-by: J. Bruce Fields Signed-off-by: Al Viro --- fs/locks.c | 24 ++++++++++++------------ include/linux/fs.h | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/fs/locks.c b/fs/locks.c index 84e269fc4c69..941b7146b6be 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -157,13 +157,13 @@ int lease_break_time = 45; * The global file_lock_list is only used for displaying /proc/locks. Protected * by the file_lock_lock. */ -static LIST_HEAD(file_lock_list); +static HLIST_HEAD(file_lock_list); /* * The blocked_list is used to find POSIX lock loops for deadlock detection. * Protected by file_lock_lock. */ -static LIST_HEAD(blocked_list); +static HLIST_HEAD(blocked_list); /* * This lock protects the blocked_list, and the file_lock_list. Generally, if @@ -188,7 +188,7 @@ static struct kmem_cache *filelock_cache __read_mostly; static void locks_init_lock_heads(struct file_lock *fl) { - INIT_LIST_HEAD(&fl->fl_link); + INIT_HLIST_NODE(&fl->fl_link); INIT_LIST_HEAD(&fl->fl_block); init_waitqueue_head(&fl->fl_wait); } @@ -222,7 +222,7 @@ void locks_free_lock(struct file_lock *fl) { BUG_ON(waitqueue_active(&fl->fl_wait)); BUG_ON(!list_empty(&fl->fl_block)); - BUG_ON(!list_empty(&fl->fl_link)); + BUG_ON(!hlist_unhashed(&fl->fl_link)); locks_release_private(fl); kmem_cache_free(filelock_cache, fl); @@ -500,7 +500,7 @@ static inline void locks_insert_global_locks(struct file_lock *fl) { spin_lock(&file_lock_lock); - list_add_tail(&fl->fl_link, &file_lock_list); + hlist_add_head(&fl->fl_link, &file_lock_list); spin_unlock(&file_lock_lock); } @@ -508,20 +508,20 @@ static inline void locks_delete_global_locks(struct file_lock *fl) { spin_lock(&file_lock_lock); - list_del_init(&fl->fl_link); + hlist_del_init(&fl->fl_link); spin_unlock(&file_lock_lock); } static inline void locks_insert_global_blocked(struct file_lock *waiter) { - list_add(&waiter->fl_link, &blocked_list); + hlist_add_head(&waiter->fl_link, &blocked_list); } static inline void locks_delete_global_blocked(struct file_lock *waiter) { - list_del_init(&waiter->fl_link); + hlist_del_init(&waiter->fl_link); } /* Remove waiter from blocker's block list. @@ -748,7 +748,7 @@ static struct file_lock *what_owner_is_waiting_for(struct file_lock *block_fl) { struct file_lock *fl; - list_for_each_entry(fl, &blocked_list, fl_link) { + hlist_for_each_entry(fl, &blocked_list, fl_link) { if (posix_same_owner(fl, block_fl)) return fl->fl_next; } @@ -2300,7 +2300,7 @@ static int locks_show(struct seq_file *f, void *v) { struct file_lock *fl, *bfl; - fl = list_entry(v, struct file_lock, fl_link); + fl = hlist_entry(v, struct file_lock, fl_link); lock_get_status(f, fl, *((loff_t *)f->private), ""); @@ -2316,14 +2316,14 @@ static void *locks_start(struct seq_file *f, loff_t *pos) spin_lock(&file_lock_lock); *p = (*pos + 1); - return seq_list_start(&file_lock_list, *pos); + return seq_hlist_start(&file_lock_list, *pos); } static void *locks_next(struct seq_file *f, void *v, loff_t *pos) { loff_t *p = f->private; ++*p; - return seq_list_next(v, &file_lock_list, pos); + return seq_hlist_next(v, &file_lock_list, pos); } static void locks_stop(struct seq_file *f, void *v) diff --git a/include/linux/fs.h b/include/linux/fs.h index 24fe998795e1..fab064a3b65f 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -946,7 +946,7 @@ int locks_in_grace(struct net *); */ struct file_lock { struct file_lock *fl_next; /* singly linked list for this inode */ - struct list_head fl_link; /* doubly linked list of all locks */ + struct hlist_node fl_link; /* node in global lists */ struct list_head fl_block; /* circular list of blocked processes */ fl_owner_t fl_owner; unsigned int fl_flags; -- cgit v1.2.3 From 48f74186546cd5929397856eab209ebcb5692d11 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 21 Jun 2013 08:58:18 -0400 Subject: locks: turn the blocked_list into a hashtable Break up the blocked_list into a hashtable, using the fl_owner as a key. This speeds up searching the hash chains, which is especially significant for deadlock detection. Note that the initial implementation assumes that hashing on fl_owner is sufficient. In most cases it should be, with the notable exception being server-side lockd, which compares ownership using a tuple of the nlm_host and the pid sent in the lock request. So, this may degrade to a single hash bucket when you only have a single NFS client. That will be addressed in a later patch. The careful observer may note that this patch leaves the file_lock_list alone. There's much less of a case for turning the file_lock_list into a hashtable. The only user of that list is the code that generates /proc/locks, and it always walks the entire list. Signed-off-by: Jeff Layton Acked-by: J. Bruce Fields Signed-off-by: Al Viro --- fs/locks.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/fs/locks.c b/fs/locks.c index 941b7146b6be..71d847cbbb63 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -126,6 +126,7 @@ #include #include #include +#include #include @@ -160,13 +161,21 @@ int lease_break_time = 45; static HLIST_HEAD(file_lock_list); /* - * The blocked_list is used to find POSIX lock loops for deadlock detection. - * Protected by file_lock_lock. + * The blocked_hash is used to find POSIX lock loops for deadlock detection. + * It is protected by file_lock_lock. + * + * We hash locks by lockowner in order to optimize searching for the lock a + * particular lockowner is waiting on. + * + * FIXME: make this value scale via some heuristic? We generally will want more + * buckets when we have more lockowners holding locks, but that's a little + * difficult to determine without knowing what the workload will look like. */ -static HLIST_HEAD(blocked_list); +#define BLOCKED_HASH_BITS 7 +static DEFINE_HASHTABLE(blocked_hash, BLOCKED_HASH_BITS); /* - * This lock protects the blocked_list, and the file_lock_list. Generally, if + * This lock protects the blocked_hash and the file_lock_list. Generally, if * you're accessing one of those lists, you want to be holding this lock. * * In addition, it also protects the fl->fl_block list, and the fl->fl_next @@ -515,13 +524,13 @@ locks_delete_global_locks(struct file_lock *fl) static inline void locks_insert_global_blocked(struct file_lock *waiter) { - hlist_add_head(&waiter->fl_link, &blocked_list); + hash_add(blocked_hash, &waiter->fl_link, (unsigned long)waiter->fl_owner); } static inline void locks_delete_global_blocked(struct file_lock *waiter) { - hlist_del_init(&waiter->fl_link); + hash_del(&waiter->fl_link); } /* Remove waiter from blocker's block list. @@ -748,7 +757,7 @@ static struct file_lock *what_owner_is_waiting_for(struct file_lock *block_fl) { struct file_lock *fl; - hlist_for_each_entry(fl, &blocked_list, fl_link) { + hash_for_each_possible(blocked_hash, fl, fl_link, (unsigned long)block_fl->fl_owner) { if (posix_same_owner(fl, block_fl)) return fl->fl_next; } @@ -884,7 +893,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str /* * New lock request. Walk all POSIX locks and look for conflicts. If * there are any, either return error or put the request on the - * blocker's list of waiters and the global blocked_list. + * blocker's list of waiters and the global blocked_hash. */ if (request->fl_type != F_UNLCK) { for_each_lock(inode, before) { -- cgit v1.2.3 From 3999e49364193f7dbbba66e2be655fe91ba1fced Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 21 Jun 2013 08:58:19 -0400 Subject: locks: add a new "lm_owner_key" lock operation Currently, the hashing that the locking code uses to add these values to the blocked_hash is simply calculated using fl_owner field. That's valid in most cases except for server-side lockd, which validates the owner of a lock based on fl_owner and fl_pid. In the case where you have a small number of NFS clients doing a lot of locking between different processes, you could end up with all the blocked requests sitting in a very small number of hash buckets. Add a new lm_owner_key operation to the lock_manager_operations that will generate an unsigned long to use as the key in the hashtable. That function is only implemented for server-side lockd, and simply XORs the fl_owner and fl_pid. Signed-off-by: Jeff Layton Acked-by: J. Bruce Fields Signed-off-by: Al Viro --- Documentation/filesystems/Locking | 16 +++++++++++----- fs/lockd/svclock.c | 12 ++++++++++++ fs/locks.c | 12 ++++++++++-- include/linux/fs.h | 1 + 4 files changed, 34 insertions(+), 7 deletions(-) diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index c2963a74fbc3..2db7c9e492e9 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -349,6 +349,7 @@ fl_release_private: maybe no ----------------------- lock_manager_operations --------------------------- prototypes: int (*lm_compare_owner)(struct file_lock *, struct file_lock *); + unsigned long (*lm_owner_key)(struct file_lock *); void (*lm_notify)(struct file_lock *); /* unblock callback */ int (*lm_grant)(struct file_lock *, struct file_lock *, int); void (*lm_break)(struct file_lock *); /* break_lease callback */ @@ -358,16 +359,21 @@ locking rules: inode->i_lock file_lock_lock may block lm_compare_owner: yes[1] maybe no +lm_owner_key yes[1] yes no lm_notify: yes yes no lm_grant: no no no lm_break: yes no no lm_change yes no no -[1]: ->lm_compare_owner is generally called with *an* inode->i_lock held. It -may not be the i_lock of the inode for either file_lock being compared! This is -the case with deadlock detection, since the code has to chase down the owners -of locks that may be entirely unrelated to the one on which the lock is being -acquired. When doing a search for deadlocks, the file_lock_lock is also held. +[1]: ->lm_compare_owner and ->lm_owner_key are generally called with +*an* inode->i_lock held. It may not be the i_lock of the inode +associated with either file_lock argument! This is the case with deadlock +detection, since the code has to chase down the owners of locks that may +be entirely unrelated to the one on which the lock is being acquired. +For deadlock detection however, the file_lock_lock is also held. The +fact that these locks are held ensures that the file_locks do not +disappear out from under you while doing the comparison or generating an +owner key. --------------------------- buffer_head ----------------------------------- prototypes: diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index a469098682c4..067778b0ccc9 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -744,8 +744,20 @@ static int nlmsvc_same_owner(struct file_lock *fl1, struct file_lock *fl2) return fl1->fl_owner == fl2->fl_owner && fl1->fl_pid == fl2->fl_pid; } +/* + * Since NLM uses two "keys" for tracking locks, we need to hash them down + * to one for the blocked_hash. Here, we're just xor'ing the host address + * with the pid in order to create a key value for picking a hash bucket. + */ +static unsigned long +nlmsvc_owner_key(struct file_lock *fl) +{ + return (unsigned long)fl->fl_owner ^ (unsigned long)fl->fl_pid; +} + const struct lock_manager_operations nlmsvc_lock_operations = { .lm_compare_owner = nlmsvc_same_owner, + .lm_owner_key = nlmsvc_owner_key, .lm_notify = nlmsvc_notify_blocked, .lm_grant = nlmsvc_grant_deferred, }; diff --git a/fs/locks.c b/fs/locks.c index 71d847cbbb63..6242e0b1c69c 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -521,10 +521,18 @@ locks_delete_global_locks(struct file_lock *fl) spin_unlock(&file_lock_lock); } +static unsigned long +posix_owner_key(struct file_lock *fl) +{ + if (fl->fl_lmops && fl->fl_lmops->lm_owner_key) + return fl->fl_lmops->lm_owner_key(fl); + return (unsigned long)fl->fl_owner; +} + static inline void locks_insert_global_blocked(struct file_lock *waiter) { - hash_add(blocked_hash, &waiter->fl_link, (unsigned long)waiter->fl_owner); + hash_add(blocked_hash, &waiter->fl_link, posix_owner_key(waiter)); } static inline void @@ -757,7 +765,7 @@ static struct file_lock *what_owner_is_waiting_for(struct file_lock *block_fl) { struct file_lock *fl; - hash_for_each_possible(blocked_hash, fl, fl_link, (unsigned long)block_fl->fl_owner) { + hash_for_each_possible(blocked_hash, fl, fl_link, posix_owner_key(block_fl)) { if (posix_same_owner(fl, block_fl)) return fl->fl_next; } diff --git a/include/linux/fs.h b/include/linux/fs.h index fab064a3b65f..a137a73fc1fe 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -908,6 +908,7 @@ struct file_lock_operations { struct lock_manager_operations { int (*lm_compare_owner)(struct file_lock *, struct file_lock *); + unsigned long (*lm_owner_key)(struct file_lock *); void (*lm_notify)(struct file_lock *); /* unblock callback */ int (*lm_grant)(struct file_lock *, struct file_lock *, int); void (*lm_break)(struct file_lock *); -- cgit v1.2.3 From 7b2296afb392bc21a50f42e7c7f4b19d3fea8c6d Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 21 Jun 2013 08:58:20 -0400 Subject: locks: give the blocked_hash its own spinlock There's no reason we have to protect the blocked_hash and file_lock_list with the same spinlock. With the tests I have, breaking it in two gives a barely measurable performance benefit, but it seems reasonable to make this locking as granular as possible. Signed-off-by: Jeff Layton Signed-off-by: Al Viro --- Documentation/filesystems/Locking | 16 +++++++-------- fs/locks.c | 41 +++++++++++++++++++++------------------ 2 files changed, 30 insertions(+), 27 deletions(-) diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index 2db7c9e492e9..7d9ca7a83fcc 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -357,20 +357,20 @@ prototypes: locking rules: - inode->i_lock file_lock_lock may block -lm_compare_owner: yes[1] maybe no -lm_owner_key yes[1] yes no -lm_notify: yes yes no -lm_grant: no no no -lm_break: yes no no -lm_change yes no no + inode->i_lock blocked_lock_lock may block +lm_compare_owner: yes[1] maybe no +lm_owner_key yes[1] yes no +lm_notify: yes yes no +lm_grant: no no no +lm_break: yes no no +lm_change yes no no [1]: ->lm_compare_owner and ->lm_owner_key are generally called with *an* inode->i_lock held. It may not be the i_lock of the inode associated with either file_lock argument! This is the case with deadlock detection, since the code has to chase down the owners of locks that may be entirely unrelated to the one on which the lock is being acquired. -For deadlock detection however, the file_lock_lock is also held. The +For deadlock detection however, the blocked_lock_lock is also held. The fact that these locks are held ensures that the file_locks do not disappear out from under you while doing the comparison or generating an owner key. diff --git a/fs/locks.c b/fs/locks.c index 6242e0b1c69c..04e2c1fdb157 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -159,10 +159,11 @@ int lease_break_time = 45; * by the file_lock_lock. */ static HLIST_HEAD(file_lock_list); +static DEFINE_SPINLOCK(file_lock_lock); /* * The blocked_hash is used to find POSIX lock loops for deadlock detection. - * It is protected by file_lock_lock. + * It is protected by blocked_lock_lock. * * We hash locks by lockowner in order to optimize searching for the lock a * particular lockowner is waiting on. @@ -175,8 +176,8 @@ static HLIST_HEAD(file_lock_list); static DEFINE_HASHTABLE(blocked_hash, BLOCKED_HASH_BITS); /* - * This lock protects the blocked_hash and the file_lock_list. Generally, if - * you're accessing one of those lists, you want to be holding this lock. + * This lock protects the blocked_hash. Generally, if you're accessing it, you + * want to be holding this lock. * * In addition, it also protects the fl->fl_block list, and the fl->fl_next * pointer for file_lock structures that are acting as lock requests (in @@ -191,7 +192,7 @@ static DEFINE_HASHTABLE(blocked_hash, BLOCKED_HASH_BITS); * both the i_lock and the blocked_lock_lock (acquired in that order). Deleting * an entry from the list however only requires the file_lock_lock. */ -static DEFINE_SPINLOCK(file_lock_lock); +static DEFINE_SPINLOCK(blocked_lock_lock); static struct kmem_cache *filelock_cache __read_mostly; @@ -544,7 +545,7 @@ locks_delete_global_blocked(struct file_lock *waiter) /* Remove waiter from blocker's block list. * When blocker ends up pointing to itself then the list is empty. * - * Must be called with file_lock_lock held. + * Must be called with blocked_lock_lock held. */ static void __locks_delete_block(struct file_lock *waiter) { @@ -555,9 +556,9 @@ static void __locks_delete_block(struct file_lock *waiter) static void locks_delete_block(struct file_lock *waiter) { - spin_lock(&file_lock_lock); + spin_lock(&blocked_lock_lock); __locks_delete_block(waiter); - spin_unlock(&file_lock_lock); + spin_unlock(&blocked_lock_lock); } /* Insert waiter into blocker's block list. @@ -565,9 +566,9 @@ static void locks_delete_block(struct file_lock *waiter) * the order they blocked. The documentation doesn't require this but * it seems like the reasonable thing to do. * - * Must be called with both the i_lock and file_lock_lock held. The fl_block + * Must be called with both the i_lock and blocked_lock_lock held. The fl_block * list itself is protected by the file_lock_list, but by ensuring that the - * i_lock is also held on insertions we can avoid taking the file_lock_lock + * i_lock is also held on insertions we can avoid taking the blocked_lock_lock * in some cases when we see that the fl_block list is empty. */ static void __locks_insert_block(struct file_lock *blocker, @@ -584,9 +585,9 @@ static void __locks_insert_block(struct file_lock *blocker, static void locks_insert_block(struct file_lock *blocker, struct file_lock *waiter) { - spin_lock(&file_lock_lock); + spin_lock(&blocked_lock_lock); __locks_insert_block(blocker, waiter); - spin_unlock(&file_lock_lock); + spin_unlock(&blocked_lock_lock); } /* @@ -601,12 +602,12 @@ static void locks_wake_up_blocks(struct file_lock *blocker) * blocked requests are only added to the list under the i_lock, and * the i_lock is always held here. Note that removal from the fl_block * list does not require the i_lock, so we must recheck list_empty() - * after acquiring the file_lock_lock. + * after acquiring the blocked_lock_lock. */ if (list_empty(&blocker->fl_block)) return; - spin_lock(&file_lock_lock); + spin_lock(&blocked_lock_lock); while (!list_empty(&blocker->fl_block)) { struct file_lock *waiter; @@ -618,7 +619,7 @@ static void locks_wake_up_blocks(struct file_lock *blocker) else wake_up(&waiter->fl_wait); } - spin_unlock(&file_lock_lock); + spin_unlock(&blocked_lock_lock); } /* Insert file lock fl into an inode's lock list at the position indicated @@ -772,7 +773,7 @@ static struct file_lock *what_owner_is_waiting_for(struct file_lock *block_fl) return NULL; } -/* Must be called with the file_lock_lock held! */ +/* Must be called with the blocked_lock_lock held! */ static int posix_locks_deadlock(struct file_lock *caller_fl, struct file_lock *block_fl) { @@ -920,12 +921,12 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str * locks list must be done while holding the same lock! */ error = -EDEADLK; - spin_lock(&file_lock_lock); + spin_lock(&blocked_lock_lock); if (likely(!posix_locks_deadlock(request, fl))) { error = FILE_LOCK_DEFERRED; __locks_insert_block(fl, request); } - spin_unlock(&file_lock_lock); + spin_unlock(&blocked_lock_lock); goto out; } } @@ -2212,12 +2213,12 @@ posix_unblock_lock(struct file_lock *waiter) { int status = 0; - spin_lock(&file_lock_lock); + spin_lock(&blocked_lock_lock); if (waiter->fl_next) __locks_delete_block(waiter); else status = -ENOENT; - spin_unlock(&file_lock_lock); + spin_unlock(&blocked_lock_lock); return status; } EXPORT_SYMBOL(posix_unblock_lock); @@ -2332,6 +2333,7 @@ static void *locks_start(struct seq_file *f, loff_t *pos) loff_t *p = f->private; spin_lock(&file_lock_lock); + spin_lock(&blocked_lock_lock); *p = (*pos + 1); return seq_hlist_start(&file_lock_list, *pos); } @@ -2345,6 +2347,7 @@ static void *locks_next(struct seq_file *f, void *v, loff_t *pos) static void locks_stop(struct seq_file *f, void *v) { + spin_unlock(&blocked_lock_lock); spin_unlock(&file_lock_lock); } -- cgit v1.2.3 From 7233c774080820b611f85a0a057ff4b43e9276f2 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 23 Jun 2013 12:08:05 +0400 Subject: lpfc: switch to fixed_size_llseek() Signed-off-by: Al Viro --- drivers/scsi/lpfc/lpfc_debugfs.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c index f525ecb7a9c6..60084e6ad2f2 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.c +++ b/drivers/scsi/lpfc/lpfc_debugfs.c @@ -1165,22 +1165,8 @@ out: static loff_t lpfc_debugfs_lseek(struct file *file, loff_t off, int whence) { - struct lpfc_debug *debug; - loff_t pos = -1; - - debug = file->private_data; - - switch (whence) { - case 0: - pos = off; - break; - case 1: - pos = file->f_pos + off; - break; - case 2: - pos = debug->len + off; - } - return (pos < 0 || pos > debug->len) ? -EINVAL : (file->f_pos = pos); + struct lpfc_debug *debug = file->private_data; + return fixed_size_llseek(file, off, whence, debug->len); } /** -- cgit v1.2.3 From c09ed2a6fafe0d4e506b9f2c36a47cf187509885 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 23 Jun 2013 12:09:11 +0400 Subject: isapnp: switch to fixed_size_llseek() Signed-off-by: Al Viro --- drivers/pnp/isapnp/proc.c | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/drivers/pnp/isapnp/proc.c b/drivers/pnp/isapnp/proc.c index 2365ef37ae24..5edee645d890 100644 --- a/drivers/pnp/isapnp/proc.c +++ b/drivers/pnp/isapnp/proc.c @@ -29,27 +29,7 @@ static struct proc_dir_entry *isapnp_proc_bus_dir = NULL; static loff_t isapnp_proc_bus_lseek(struct file *file, loff_t off, int whence) { - loff_t new = -1; - struct inode *inode = file_inode(file); - - mutex_lock(&inode->i_mutex); - switch (whence) { - case 0: - new = off; - break; - case 1: - new = file->f_pos + off; - break; - case 2: - new = 256 + off; - break; - } - if (new < 0 || new > 256) - new = -EINVAL; - else - file->f_pos = new; - mutex_unlock(&inode->i_mutex); - return new; + return fixed_size_llseek(file, off, whence, 256); } static ssize_t isapnp_proc_bus_read(struct file *file, char __user * buf, -- cgit v1.2.3 From 54de90d686eacd4a761f7ce65c54f1eb3a159429 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 23 Jun 2013 12:29:00 +0400 Subject: pci/proc: switch to fixed_size_llseek() Signed-off-by: Al Viro --- drivers/pci/proc.c | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c index 08126087ec31..cdc7836d7e3d 100644 --- a/drivers/pci/proc.c +++ b/drivers/pci/proc.c @@ -20,27 +20,8 @@ static int proc_initialized; /* = 0 */ static loff_t proc_bus_pci_lseek(struct file *file, loff_t off, int whence) { - loff_t new = -1; - struct inode *inode = file_inode(file); - - mutex_lock(&inode->i_mutex); - switch (whence) { - case 0: - new = off; - break; - case 1: - new = file->f_pos + off; - break; - case 2: - new = inode->i_size + off; - break; - } - if (new < 0 || new > inode->i_size) - new = -EINVAL; - else - file->f_pos = new; - mutex_unlock(&inode->i_mutex); - return new; + struct pci_dev *dev = PDE_DATA(file_inode(file)); + return fixed_size_llseek(file, off, whence, dev->cfg_size); } static ssize_t -- cgit v1.2.3 From 4a1f2f386b0011782099b4393ee74370916b6aef Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 23 Jun 2013 12:30:24 +0400 Subject: ubi/cdev: switch to fixed_size_llseek() Signed-off-by: Al Viro --- drivers/mtd/ubi/cdev.c | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index 4f02848bb2bc..8ca49f2043e4 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -155,7 +155,6 @@ static loff_t vol_cdev_llseek(struct file *file, loff_t offset, int origin) { struct ubi_volume_desc *desc = file->private_data; struct ubi_volume *vol = desc->vol; - loff_t new_offset; if (vol->updating) { /* Update is in progress, seeking is prohibited */ @@ -163,30 +162,7 @@ static loff_t vol_cdev_llseek(struct file *file, loff_t offset, int origin) return -EBUSY; } - switch (origin) { - case 0: /* SEEK_SET */ - new_offset = offset; - break; - case 1: /* SEEK_CUR */ - new_offset = file->f_pos + offset; - break; - case 2: /* SEEK_END */ - new_offset = vol->used_bytes + offset; - break; - default: - return -EINVAL; - } - - if (new_offset < 0 || new_offset > vol->used_bytes) { - ubi_err("bad seek %lld", new_offset); - return -EINVAL; - } - - dbg_gen("seek volume %d, offset %lld, origin %d, new offset %lld", - vol->vol_id, offset, origin, new_offset); - - file->f_pos = new_offset; - return new_offset; + return fixed_size_llseek(file, offset, origin, vol->used_bytes); } static int vol_cdev_fsync(struct file *file, loff_t start, loff_t end, -- cgit v1.2.3 From b33159b7d249afae386b91025ef4ea1d7793fc1b Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 23 Jun 2013 12:35:26 +0400 Subject: proc_powerpc: switch to fixed_size_llseek() Signed-off-by: Al Viro --- arch/powerpc/kernel/proc_powerpc.c | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/arch/powerpc/kernel/proc_powerpc.c b/arch/powerpc/kernel/proc_powerpc.c index feb8580fdc84..c30612aad68e 100644 --- a/arch/powerpc/kernel/proc_powerpc.c +++ b/arch/powerpc/kernel/proc_powerpc.c @@ -29,25 +29,9 @@ #ifdef CONFIG_PPC64 -static loff_t page_map_seek( struct file *file, loff_t off, int whence) +static loff_t page_map_seek(struct file *file, loff_t off, int whence) { - loff_t new; - switch(whence) { - case 0: - new = off; - break; - case 1: - new = file->f_pos + off; - break; - case 2: - new = PAGE_SIZE + off; - break; - default: - return -EINVAL; - } - if ( new < 0 || new > PAGE_SIZE ) - return -EINVAL; - return (file->f_pos = new); + return fixed_size_llseek(file, off, whence, PAGE_SIZE); } static ssize_t page_map_read( struct file *file, char __user *buf, size_t nbytes, -- cgit v1.2.3 From 914961aac077b5aebb4ae669d7c42d231826970b Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 23 Jun 2013 12:43:48 +0400 Subject: tile-srom: switch to fixed_size_llseek() Signed-off-by: Al Viro --- drivers/char/tile-srom.c | 28 +++------------------------- 1 file changed, 3 insertions(+), 25 deletions(-) diff --git a/drivers/char/tile-srom.c b/drivers/char/tile-srom.c index 2e2036e940fc..7faeb1cde97d 100644 --- a/drivers/char/tile-srom.c +++ b/drivers/char/tile-srom.c @@ -273,32 +273,10 @@ static ssize_t srom_write(struct file *filp, const char __user *buf, } /* Provide our own implementation so we can use srom->total_size. */ -loff_t srom_llseek(struct file *filp, loff_t offset, int origin) +loff_t srom_llseek(struct file *file, loff_t offset, int origin) { - struct srom_dev *srom = filp->private_data; - - if (mutex_lock_interruptible(&srom->lock)) - return -ERESTARTSYS; - - switch (origin) { - case SEEK_END: - offset += srom->total_size; - break; - case SEEK_CUR: - offset += filp->f_pos; - break; - } - - if (offset < 0 || offset > srom->total_size) { - offset = -EINVAL; - } else { - filp->f_pos = offset; - filp->f_version = 0; - } - - mutex_unlock(&srom->lock); - - return offset; + struct srom_dev *srom = file->private_data; + return fixed_size_llseek(file, offset, origin, srom->total_size); } static ssize_t total_show(struct device *dev, -- cgit v1.2.3 From d2c40f789f6c98c50bf01cf84ef46415fb7a2776 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 23 Jun 2013 12:46:49 +0400 Subject: cpqphp_sysfs: switch to fixed_size_llseek() Signed-off-by: Al Viro --- drivers/pci/hotplug/cpqphp_sysfs.c | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/drivers/pci/hotplug/cpqphp_sysfs.c b/drivers/pci/hotplug/cpqphp_sysfs.c index 4cb30447a486..17c1f36315d1 100644 --- a/drivers/pci/hotplug/cpqphp_sysfs.c +++ b/drivers/pci/hotplug/cpqphp_sysfs.c @@ -167,26 +167,8 @@ exit: static loff_t lseek(struct file *file, loff_t off, int whence) { - struct ctrl_dbg *dbg; - loff_t new = -1; - - mutex_lock(&cpqphp_mutex); - dbg = file->private_data; - - switch (whence) { - case 0: - new = off; - break; - case 1: - new = file->f_pos + off; - break; - } - if (new < 0 || new > dbg->size) { - mutex_unlock(&cpqphp_mutex); - return -EINVAL; - } - mutex_unlock(&cpqphp_mutex); - return (file->f_pos = new); + struct ctrl_dbg *dbg = file->private_data; + return fixed_size_llseek(file, off, whence, dbg->size); } static ssize_t read(struct file *file, char __user *buf, -- cgit v1.2.3 From 5d48f3a2de568ba2c9d06ecbdf995e0702748f22 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 23 Jun 2013 21:34:45 +0400 Subject: block_dev: switch to fixed_size_llseek() Signed-off-by: Al Viro --- fs/block_dev.c | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/fs/block_dev.c b/fs/block_dev.c index 2091db8cdd78..431b6a04ebfd 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -325,31 +325,10 @@ static int blkdev_write_end(struct file *file, struct address_space *mapping, static loff_t block_llseek(struct file *file, loff_t offset, int whence) { struct inode *bd_inode = file->f_mapping->host; - loff_t size; loff_t retval; mutex_lock(&bd_inode->i_mutex); - size = i_size_read(bd_inode); - - retval = -EINVAL; - switch (whence) { - case SEEK_END: - offset += size; - break; - case SEEK_CUR: - offset += file->f_pos; - case SEEK_SET: - break; - default: - goto out; - } - if (offset >= 0 && offset <= size) { - if (offset != file->f_pos) { - file->f_pos = offset; - } - retval = offset; - } -out: + retval = fixed_size_llseek(file, offset, whence, i_size_read(bd_inode)); mutex_unlock(&bd_inode->i_mutex); return retval; } -- cgit v1.2.3 From 2142914e3eb1168978e842f65cfd182be7582861 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 23 Jun 2013 22:45:30 +0400 Subject: lseek_execute() doesn't need an inode passed to it Signed-off-by: Al Viro --- fs/read_write.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/fs/read_write.c b/fs/read_write.c index fd72b592aa1b..37d16e82b575 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -41,8 +41,7 @@ static inline int unsigned_offsets(struct file *file) return file->f_mode & FMODE_UNSIGNED_OFFSET; } -static loff_t lseek_execute(struct file *file, struct inode *inode, - loff_t offset, loff_t maxsize) +static loff_t lseek_execute(struct file *file, loff_t offset, loff_t maxsize) { if (offset < 0 && !unsigned_offsets(file)) return -EINVAL; @@ -76,8 +75,6 @@ loff_t generic_file_llseek_size(struct file *file, loff_t offset, int whence, loff_t maxsize, loff_t eof) { - struct inode *inode = file->f_mapping->host; - switch (whence) { case SEEK_END: offset += eof; @@ -97,8 +94,7 @@ generic_file_llseek_size(struct file *file, loff_t offset, int whence, * like SEEK_SET. */ spin_lock(&file->f_lock); - offset = lseek_execute(file, inode, file->f_pos + offset, - maxsize); + offset = lseek_execute(file, file->f_pos + offset, maxsize); spin_unlock(&file->f_lock); return offset; case SEEK_DATA: @@ -120,7 +116,7 @@ generic_file_llseek_size(struct file *file, loff_t offset, int whence, break; } - return lseek_execute(file, inode, offset, maxsize); + return lseek_execute(file, offset, maxsize); } EXPORT_SYMBOL(generic_file_llseek_size); -- cgit v1.2.3 From 128521f6017d36d7c01449d4c97b37dc10c93387 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 28 Jun 2013 21:53:24 +0200 Subject: ASoC: tegra20-ac97: Remove duplicate error message devm_ioremap_resource() already outputs an error message when any of the operations it performs fails, so the duplicate in the caller can be removed. Signed-off-by: Thierry Reding Signed-off-by: Mark Brown --- sound/soc/tegra/tegra20_ac97.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sound/soc/tegra/tegra20_ac97.c b/sound/soc/tegra/tegra20_ac97.c index f52eab6d2231..6c1255b44535 100644 --- a/sound/soc/tegra/tegra20_ac97.c +++ b/sound/soc/tegra/tegra20_ac97.c @@ -343,7 +343,6 @@ static int tegra20_ac97_platform_probe(struct platform_device *pdev) regs = devm_ioremap_resource(&pdev->dev, mem); if (IS_ERR(regs)) { ret = PTR_ERR(regs); - dev_err(&pdev->dev, "ioremap failed: %d\n", ret); goto err_clk_put; } -- cgit v1.2.3 From 8a08f4c4f24b4dfe7ff08542868e2b434c96221f Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 28 Jun 2013 21:53:25 +0200 Subject: ASoC: tegra20-ac97: Remove unused variable With the conversion to devm_ioremap_resource() the memregion variable is no longer used so it can be dropped. Signed-off-by: Thierry Reding Signed-off-by: Mark Brown --- sound/soc/tegra/tegra20_ac97.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/tegra/tegra20_ac97.c b/sound/soc/tegra/tegra20_ac97.c index 6c1255b44535..e58233f7df61 100644 --- a/sound/soc/tegra/tegra20_ac97.c +++ b/sound/soc/tegra/tegra20_ac97.c @@ -312,7 +312,7 @@ static const struct regmap_config tegra20_ac97_regmap_config = { static int tegra20_ac97_platform_probe(struct platform_device *pdev) { struct tegra20_ac97 *ac97; - struct resource *mem, *memregion; + struct resource *mem; u32 of_dma[2]; void __iomem *regs; int ret = 0; -- cgit v1.2.3 From 81793bab5d7cc0d0962dae47dab3a63ae4bd5265 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 24 Jun 2013 22:03:50 +0100 Subject: ARM: 7774/1: Fix dtb dependency to use order-only prerequisites The %.dtb dependency is specified to depend on the PHONY "scripts". That means that it'll build every time even if the underlying dtb file hasn't been touched. Use an order-only prerequisites to fix this. Also mark "dtbs" as PHONY for correctness. This was broken in (70b0476 ARM: 7513/1: Make sure dtc is built before running it). Reported-by: Mike Frysinger Signed-off-by: Doug Anderson Acked-by: Olof Johansson Reviewed-by: David Brown Signed-off-by: Russell King --- arch/arm/Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 47374085befd..10c08092378f 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -289,9 +289,10 @@ zImage Image xipImage bootpImage uImage: vmlinux zinstall uinstall install: vmlinux $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $@ -%.dtb: scripts +%.dtb: | scripts $(Q)$(MAKE) $(build)=$(boot)/dts MACHINE=$(MACHINE) $(boot)/dts/$@ +PHONY += dtbs dtbs: scripts $(Q)$(MAKE) $(build)=$(boot)/dts MACHINE=$(MACHINE) dtbs -- cgit v1.2.3 From 1b21376a737aeaa82320ae014b8e1c2c53cfc479 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Thu, 27 Jun 2013 04:32:06 +0100 Subject: ARM: 7777/1: Avoid extra calls to the C compiler Starting up the C compiler can be a slow operation on some systems. Though these calls don't individually take a lot of time, they add up. Rearrange the ARM Makefile a bit to avoid extra calls to the compiler when they can be easily avoided. When running with the Chrome OS ARM cross compiler "armv7a-cros-linux-gnueabi-", this shaved .55 seconds (from 5.31 seconds to 4.76 seconds) off an incremental build of the kernel: time make -j32 ARCH=arm CROSS_COMPILE=armv7a-cros-linux-gnueabi- Thanks to Mike Frysinger for the clean trick to make this work. Signed-off-by: Doug Anderson Acked-by: Mike Frysinger Signed-off-by: Russell King --- arch/arm/Makefile | 56 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 10c08092378f..20559a089442 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -59,37 +59,43 @@ comma = , # Note that GCC does not numerically define an architecture version # macro, but instead defines a whole series of macros which makes # testing for a specific architecture or later rather impossible. -arch-$(CONFIG_CPU_32v7) :=-D__LINUX_ARM_ARCH__=7 $(call cc-option,-march=armv7-a,-march=armv5t -Wa$(comma)-march=armv7-a) -arch-$(CONFIG_CPU_32v6) :=-D__LINUX_ARM_ARCH__=6 $(call cc-option,-march=armv6,-march=armv5t -Wa$(comma)-march=armv6) +arch-$(CONFIG_CPU_32v7) =-D__LINUX_ARM_ARCH__=7 $(call cc-option,-march=armv7-a,-march=armv5t -Wa$(comma)-march=armv7-a) +arch-$(CONFIG_CPU_32v6) =-D__LINUX_ARM_ARCH__=6 $(call cc-option,-march=armv6,-march=armv5t -Wa$(comma)-march=armv6) # Only override the compiler option if ARMv6. The ARMv6K extensions are # always available in ARMv7 ifeq ($(CONFIG_CPU_32v6),y) -arch-$(CONFIG_CPU_32v6K) :=-D__LINUX_ARM_ARCH__=6 $(call cc-option,-march=armv6k,-march=armv5t -Wa$(comma)-march=armv6k) +arch-$(CONFIG_CPU_32v6K) =-D__LINUX_ARM_ARCH__=6 $(call cc-option,-march=armv6k,-march=armv5t -Wa$(comma)-march=armv6k) endif -arch-$(CONFIG_CPU_32v5) :=-D__LINUX_ARM_ARCH__=5 $(call cc-option,-march=armv5te,-march=armv4t) -arch-$(CONFIG_CPU_32v4T) :=-D__LINUX_ARM_ARCH__=4 -march=armv4t -arch-$(CONFIG_CPU_32v4) :=-D__LINUX_ARM_ARCH__=4 -march=armv4 -arch-$(CONFIG_CPU_32v3) :=-D__LINUX_ARM_ARCH__=3 -march=armv3 +arch-$(CONFIG_CPU_32v5) =-D__LINUX_ARM_ARCH__=5 $(call cc-option,-march=armv5te,-march=armv4t) +arch-$(CONFIG_CPU_32v4T) =-D__LINUX_ARM_ARCH__=4 -march=armv4t +arch-$(CONFIG_CPU_32v4) =-D__LINUX_ARM_ARCH__=4 -march=armv4 +arch-$(CONFIG_CPU_32v3) =-D__LINUX_ARM_ARCH__=3 -march=armv3 + +# Evaluate arch cc-option calls now +arch-y := $(arch-y) # This selects how we optimise for the processor. -tune-$(CONFIG_CPU_ARM7TDMI) :=-mtune=arm7tdmi -tune-$(CONFIG_CPU_ARM720T) :=-mtune=arm7tdmi -tune-$(CONFIG_CPU_ARM740T) :=-mtune=arm7tdmi -tune-$(CONFIG_CPU_ARM9TDMI) :=-mtune=arm9tdmi -tune-$(CONFIG_CPU_ARM940T) :=-mtune=arm9tdmi -tune-$(CONFIG_CPU_ARM946E) :=$(call cc-option,-mtune=arm9e,-mtune=arm9tdmi) -tune-$(CONFIG_CPU_ARM920T) :=-mtune=arm9tdmi -tune-$(CONFIG_CPU_ARM922T) :=-mtune=arm9tdmi -tune-$(CONFIG_CPU_ARM925T) :=-mtune=arm9tdmi -tune-$(CONFIG_CPU_ARM926T) :=-mtune=arm9tdmi -tune-$(CONFIG_CPU_FA526) :=-mtune=arm9tdmi -tune-$(CONFIG_CPU_SA110) :=-mtune=strongarm110 -tune-$(CONFIG_CPU_SA1100) :=-mtune=strongarm1100 -tune-$(CONFIG_CPU_XSCALE) :=$(call cc-option,-mtune=xscale,-mtune=strongarm110) -Wa,-mcpu=xscale -tune-$(CONFIG_CPU_XSC3) :=$(call cc-option,-mtune=xscale,-mtune=strongarm110) -Wa,-mcpu=xscale -tune-$(CONFIG_CPU_FEROCEON) :=$(call cc-option,-mtune=marvell-f,-mtune=xscale) -tune-$(CONFIG_CPU_V6) :=$(call cc-option,-mtune=arm1136j-s,-mtune=strongarm) -tune-$(CONFIG_CPU_V6K) :=$(call cc-option,-mtune=arm1136j-s,-mtune=strongarm) +tune-$(CONFIG_CPU_ARM7TDMI) =-mtune=arm7tdmi +tune-$(CONFIG_CPU_ARM720T) =-mtune=arm7tdmi +tune-$(CONFIG_CPU_ARM740T) =-mtune=arm7tdmi +tune-$(CONFIG_CPU_ARM9TDMI) =-mtune=arm9tdmi +tune-$(CONFIG_CPU_ARM940T) =-mtune=arm9tdmi +tune-$(CONFIG_CPU_ARM946E) =$(call cc-option,-mtune=arm9e,-mtune=arm9tdmi) +tune-$(CONFIG_CPU_ARM920T) =-mtune=arm9tdmi +tune-$(CONFIG_CPU_ARM922T) =-mtune=arm9tdmi +tune-$(CONFIG_CPU_ARM925T) =-mtune=arm9tdmi +tune-$(CONFIG_CPU_ARM926T) =-mtune=arm9tdmi +tune-$(CONFIG_CPU_FA526) =-mtune=arm9tdmi +tune-$(CONFIG_CPU_SA110) =-mtune=strongarm110 +tune-$(CONFIG_CPU_SA1100) =-mtune=strongarm1100 +tune-$(CONFIG_CPU_XSCALE) =$(call cc-option,-mtune=xscale,-mtune=strongarm110) -Wa,-mcpu=xscale +tune-$(CONFIG_CPU_XSC3) =$(call cc-option,-mtune=xscale,-mtune=strongarm110) -Wa,-mcpu=xscale +tune-$(CONFIG_CPU_FEROCEON) =$(call cc-option,-mtune=marvell-f,-mtune=xscale) +tune-$(CONFIG_CPU_V6) =$(call cc-option,-mtune=arm1136j-s,-mtune=strongarm) +tune-$(CONFIG_CPU_V6K) =$(call cc-option,-mtune=arm1136j-s,-mtune=strongarm) + +# Evaluate tune cc-option calls now +tune-y := $(tune-y) ifeq ($(CONFIG_AEABI),y) CFLAGS_ABI :=-mabi=aapcs-linux -mno-thumb-interwork -- cgit v1.2.3 From 809e660f438fc5a69bf57630a85bcd8112263f37 Mon Sep 17 00:00:00 2001 From: Steven Capper Date: Tue, 25 Jun 2013 08:45:51 +0100 Subject: ARM: 7775/1: mm: Remove do_sect_fault from LPAE code For LPAE, do_sect_fault used to be invoked as the second level access flag handler. When transparent huge pages were introduced for LPAE, do_page_fault was used instead. Unfortunately, do_sect_fault remains defined but not used for LPAE code resulting in a compile warning. This patch surrounds do_sect_fault with #ifndef CONFIG_ARM_LPAE to fix this warning. Signed-off-by: Steve Capper Acked-by: Arnd Bergmann Signed-off-by: Russell King --- arch/arm/mm/fault.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c index 5dbf13f954f6..c97f7940cb95 100644 --- a/arch/arm/mm/fault.c +++ b/arch/arm/mm/fault.c @@ -491,12 +491,14 @@ do_translation_fault(unsigned long addr, unsigned int fsr, * Some section permission faults need to be handled gracefully. * They can happen due to a __{get,put}_user during an oops. */ +#ifndef CONFIG_ARM_LPAE static int do_sect_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) { do_bad_area(addr, fsr, regs); return 0; } +#endif /* CONFIG_ARM_LPAE */ /* * This abort handler always returns "fault". -- cgit v1.2.3 From 7685e0165b36ae034dd5e67b7fbbee7e74604f38 Mon Sep 17 00:00:00 2001 From: Kevin Hilman Date: Fri, 28 Jun 2013 11:17:48 -0700 Subject: ASoC: pxa2xx: fixup multi-platform AC'97 build failures commit b047e1cc (ASoC: ac97: Support multi-platform AC'97) introduced some build failures for the pxa2xx-ac97 support, fix them. Cc: Mark Brown Signed-off-by: Kevin Hilman Signed-off-by: Mark Brown --- sound/soc/pxa/pxa2xx-ac97.c | 2 ++ sound/soc/pxa/pxa2xx-ac97.h | 3 --- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index a3c22ba25f08..1475515712e6 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c @@ -238,6 +238,8 @@ static const struct snd_soc_component_driver pxa_ac97_component = { static int pxa2xx_ac97_dev_probe(struct platform_device *pdev) { + int ret; + if (pdev->id != -1) { dev_err(&pdev->dev, "PXA2xx has only one AC97 port.\n"); return -ENXIO; diff --git a/sound/soc/pxa/pxa2xx-ac97.h b/sound/soc/pxa/pxa2xx-ac97.h index eda891e6f31b..a49c21ba3842 100644 --- a/sound/soc/pxa/pxa2xx-ac97.h +++ b/sound/soc/pxa/pxa2xx-ac97.h @@ -14,7 +14,4 @@ #define PXA2XX_DAI_AC97_AUX 1 #define PXA2XX_DAI_AC97_MIC 2 -/* platform data */ -extern struct snd_ac97_bus_ops pxa2xx_ac97_ops; - #endif -- cgit v1.2.3 From f74b5e253a062004c1d30177f9889501423e403d Mon Sep 17 00:00:00 2001 From: Kevin Hilman Date: Fri, 28 Jun 2013 11:17:49 -0700 Subject: ASoC: ac97: fixup multi-platform AC'97 module build failure commit b047e1cc (ASoC: ac97: Support multi-platform AC'97) introduced some build failures for modules wanting to access the generic soc_ac97_ops. For example: ERROR: "soc_ac97_ops" [sound/soc/codecs/snd-soc-wm9712.ko] undefined! To fix, export soc_ac97_ops to modules. Cc: Mark Brown Signed-off-by: Kevin Hilman Signed-off-by: Mark Brown --- sound/soc/soc-core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 562d72e04e6e..0a8a5f50b466 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2080,6 +2080,7 @@ int snd_soc_new_ac97_codec(struct snd_soc_codec *codec, EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec); struct snd_ac97_bus_ops *soc_ac97_ops; +EXPORT_SYMBOL_GPL(soc_ac97_ops); int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops) { -- cgit v1.2.3 From b57caaaed2bd127fe656e6c145970ed6a05c0125 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 28 Jun 2013 12:56:51 -0700 Subject: kconfig: allow "hex" and "range" to support longs The parsing routines for Kconfig files use strtol(), but store and render values as int. Switch types and formating to long to support a wider range of values. For example, 0x80000000 wasn't representable. Signed-off-by: Kees Cook Tested-by: "Yann E. MORIN" Reviewed-by: "Yann E. MORIN" Signed-off-by: "Yann E. MORIN" --- scripts/kconfig/symbol.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c index 387d55483882..d550300ec00c 100644 --- a/scripts/kconfig/symbol.c +++ b/scripts/kconfig/symbol.c @@ -136,7 +136,7 @@ static struct property *sym_get_range_prop(struct symbol *sym) return NULL; } -static int sym_get_range_val(struct symbol *sym, int base) +static long sym_get_range_val(struct symbol *sym, int base) { sym_calc_value(sym); switch (sym->type) { @@ -155,7 +155,7 @@ static int sym_get_range_val(struct symbol *sym, int base) static void sym_validate_range(struct symbol *sym) { struct property *prop; - int base, val, val2; + long base, val, val2; char str[64]; switch (sym->type) { @@ -179,9 +179,9 @@ static void sym_validate_range(struct symbol *sym) return; } if (sym->type == S_INT) - sprintf(str, "%d", val2); + sprintf(str, "%ld", val2); else - sprintf(str, "0x%x", val2); + sprintf(str, "0x%lx", val2); sym->curr.val = strdup(str); } @@ -594,7 +594,7 @@ bool sym_string_valid(struct symbol *sym, const char *str) bool sym_string_within_range(struct symbol *sym, const char *str) { struct property *prop; - int val; + long val; switch (sym->type) { case S_STRING: -- cgit v1.2.3 From 008aebde9be37e7e1248332b1983976e354327ea Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Sat, 29 Jun 2013 13:16:59 +0200 Subject: bonding: combine pr_debugs in bond_set_dev_addr into one Combine the multiple pr_debugs in bond_set_dev_addr into one pr_debug. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index ec44580b076f..742c193881fa 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -843,9 +843,8 @@ static void bond_hw_addr_swap(struct bonding *bond, struct slave *new_active, static void bond_set_dev_addr(struct net_device *bond_dev, struct net_device *slave_dev) { - pr_debug("bond_dev=%p\n", bond_dev); - pr_debug("slave_dev=%p\n", slave_dev); - pr_debug("slave_dev->addr_len=%d\n", slave_dev->addr_len); + pr_debug("bond_dev=%p slave_dev=%p slave_dev->addr_len=%d\n", + bond_dev, slave_dev, slave_dev->addr_len); memcpy(bond_dev->dev_addr, slave_dev->dev_addr, slave_dev->addr_len); bond_dev->addr_assign_type = NET_ADDR_STOLEN; call_netdevice_notifiers(NETDEV_CHANGEADDR, bond_dev); -- cgit v1.2.3 From c7ba8287cd11f2fc9e2feee9e1fac34b7293658f Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sat, 29 Jun 2013 14:06:10 -0700 Subject: cgroup: CGRP_ROOT_SUBSYS_BOUND should also be ignored when mounting an existing hierarchy 0ce6cba357 ("cgroup: CGRP_ROOT_SUBSYS_BOUND should be ignored when comparing mount options") only updated the remount path but CGRP_ROOT_SUBSYS_BOUND should also be ignored when comparing options while mounting an existing hierarchy. As option mismatch triggers a warning but doesn't fail the mount without sane_behavior, this only triggers a spurious warning message. Fix it by only comparing CGRP_ROOT_OPTION_MASK bits when comparing new and existing root options. Signed-off-by: Tejun Heo --- kernel/cgroup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 5a2fcf5bcc4a..e5583d10a325 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -1703,7 +1703,7 @@ static struct dentry *cgroup_mount(struct file_system_type *fs_type, */ cgroup_free_root(opts.new_root); - if (root->flags != opts.flags) { + if ((root->flags ^ opts.flags) & CGRP_ROOT_OPTION_MASK) { if ((root->flags | opts.flags) & CGRP_ROOT_SANE_BEHAVIOR) { pr_err("cgroup: sane_behavior: new mount options should match the existing superblock\n"); ret = -EINVAL; -- cgit v1.2.3 From 038f0babc98a16211959010d7cd48b4a14f108cc Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Thu, 27 Jun 2013 20:58:55 +0800 Subject: Gpio/trivial: replace numeric with standard PM state macros Use standard PM state macros PCI_Dx instead of numeric 0/1/2.. Signed-off-by: Yijing Wang Cc: Michael Buesch Cc: Grant Likely Cc: Linus Walleij Cc: Jiri Kosina Cc: linux-kernel@vger.kernel.org Signed-off-by: Linus Walleij --- drivers/gpio/gpio-bt8xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-bt8xx.c b/drivers/gpio/gpio-bt8xx.c index 7d9d7cb35f28..8369e71ebe4f 100644 --- a/drivers/gpio/gpio-bt8xx.c +++ b/drivers/gpio/gpio-bt8xx.c @@ -286,7 +286,7 @@ static int bt8xxgpio_resume(struct pci_dev *pdev) unsigned long flags; int err; - pci_set_power_state(pdev, 0); + pci_set_power_state(pdev, PCI_D0); err = pci_enable_device(pdev); if (err) return err; -- cgit v1.2.3 From 5f17ce8b954a2ff93f0460a2ae35d56285697d0f Mon Sep 17 00:00:00 2001 From: Tiejun Chen Date: Mon, 13 May 2013 10:00:45 +0800 Subject: KVM: PPC: Guard doorbell exception with CONFIG_PPC_DOORBELL Availablity of the doorbell_exception function is guarded by CONFIG_PPC_DOORBELL. Use the same define to guard our caller of it. Signed-off-by: Tiejun Chen [agraf: improve patch description] Signed-off-by: Alexander Graf --- arch/powerpc/kvm/booke.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c index 1020119226db..62d4ece03bcd 100644 --- a/arch/powerpc/kvm/booke.c +++ b/arch/powerpc/kvm/booke.c @@ -795,7 +795,7 @@ static void kvmppc_restart_interrupt(struct kvm_vcpu *vcpu, kvmppc_fill_pt_regs(®s); timer_interrupt(®s); break; -#if defined(CONFIG_PPC_FSL_BOOK3E) || defined(CONFIG_PPC_BOOK3E_64) +#if defined(CONFIG_PPC_DOORBELL) case BOOKE_INTERRUPT_DOORBELL: kvmppc_fill_pt_regs(®s); doorbell_exception(®s); -- cgit v1.2.3 From 8ed7b7e9d2a4e208d3c94af3ddbc628317b30409 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sat, 22 Jun 2013 17:13:32 +1000 Subject: KVM: PPC: Book3S PR: Fix proto-VSID calculations This makes sure the calculation of the proto-VSIDs used by PR KVM is done with 64-bit arithmetic. Since vcpu3s->context_id[] is int, when we do vcpu3s->context_id[0] << ESID_BITS the shift will be done with 32-bit instructions, possibly leading to significant bits getting lost, as the context id can be up to 524283 and ESID_BITS is 18. To fix this we cast the context id to u64 before shifting. Signed-off-by: Paul Mackerras Signed-off-by: Alexander Graf --- arch/powerpc/kvm/book3s_64_mmu_host.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/kvm/book3s_64_mmu_host.c b/arch/powerpc/kvm/book3s_64_mmu_host.c index 3a9a1aceb14f..2c6e7ee8be34 100644 --- a/arch/powerpc/kvm/book3s_64_mmu_host.c +++ b/arch/powerpc/kvm/book3s_64_mmu_host.c @@ -325,9 +325,9 @@ int kvmppc_mmu_init(struct kvm_vcpu *vcpu) return -1; vcpu3s->context_id[0] = err; - vcpu3s->proto_vsid_max = ((vcpu3s->context_id[0] + 1) + vcpu3s->proto_vsid_max = ((u64)(vcpu3s->context_id[0] + 1) << ESID_BITS) - 1; - vcpu3s->proto_vsid_first = vcpu3s->context_id[0] << ESID_BITS; + vcpu3s->proto_vsid_first = (u64)vcpu3s->context_id[0] << ESID_BITS; vcpu3s->proto_vsid_next = vcpu3s->proto_vsid_first; kvmppc_mmu_hpte_init(vcpu); -- cgit v1.2.3 From bc1bc4e3929d7a2e4078a9e48fe1e0bb853de1e6 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sat, 22 Jun 2013 17:14:11 +1000 Subject: KVM: PPC: Book3S PR: Fix invalidation of SLB entry 0 on guest entry On entering a PR KVM guest, we invalidate the whole SLB before loading up the guest entries. We do this using an slbia instruction, which invalidates all entries except entry 0, followed by an slbie to invalidate entry 0. However, the slbie turns out to be ineffective in some circumstances (specifically when the host linear mapping uses 64k pages) because of errors in computing the parameter to the slbie. The result is that the guest kernel hangs very early in boot because it takes a DSI the first time it tries to access kernel data using a linear mapping address in real mode. Currently we construct bits 36 - 43 (big-endian numbering) of the slbie parameter by taking bits 56 - 63 of the SLB VSID doubleword. These bits for the tlbie are C (class, 1 bit), B (segment size, 2 bits) and 5 reserved bits. For the SLB VSID doubleword these are C (class, 1 bit), reserved (1 bit), LP (large page size, 2 bits), and 4 reserved bits. Thus we are not setting the B field correctly, and when LP = 01 as it is for 64k pages, we are setting a reserved bit. Rather than add more instructions to calculate the slbie parameter correctly, this takes a simpler approach, which is to set entry 0 to zeroes explicitly. Normally slbmte should not be used to invalidate an entry, since it doesn't invalidate the ERATs, but it is OK to use it to invalidate an entry if it is immediately followed by slbia, which does invalidate the ERATs. (This has been confirmed with the Power architects.) This approach takes fewer instructions and will work whatever the contents of entry 0. Signed-off-by: Paul Mackerras Signed-off-by: Alexander Graf --- arch/powerpc/kvm/book3s_64_slb.S | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/arch/powerpc/kvm/book3s_64_slb.S b/arch/powerpc/kvm/book3s_64_slb.S index 56b983e7b738..4f0caecc0f9d 100644 --- a/arch/powerpc/kvm/book3s_64_slb.S +++ b/arch/powerpc/kvm/book3s_64_slb.S @@ -66,10 +66,6 @@ slb_exit_skip_ ## num: ld r12, PACA_SLBSHADOWPTR(r13) - /* Save off the first entry so we can slbie it later */ - ld r10, SHADOW_SLB_ESID(0)(r12) - ld r11, SHADOW_SLB_VSID(0)(r12) - /* Remove bolted entries */ UNBOLT_SLB_ENTRY(0) UNBOLT_SLB_ENTRY(1) @@ -81,15 +77,10 @@ slb_exit_skip_ ## num: /* Flush SLB */ + li r10, 0 + slbmte r10, r10 slbia - /* r0 = esid & ESID_MASK */ - rldicr r10, r10, 0, 35 - /* r0 |= CLASS_BIT(VSID) */ - rldic r12, r11, 56 - 36, 36 - or r10, r10, r12 - slbie r10 - /* Fill SLB with our shadow */ lbz r12, SVCPU_SLB_MAX(r3) -- cgit v1.2.3 From 6ed1485f65f0eb17aa7b649d5abe0b011b92f718 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sat, 22 Jun 2013 17:14:48 +1000 Subject: KVM: PPC: Book3S PR: Don't keep scanning HPTEG after we find a match The loop in kvmppc_mmu_book3s_64_xlate() that looks up a translation in the guest hashed page table (HPT) keeps going if it finds an HPTE that matches but doesn't allow access. This is incorrect; it is different from what the hardware does, and there should never be more than one matching HPTE anyway. This fixes it to stop when any matching HPTE is found. Signed-off-by: Paul Mackerras Signed-off-by: Alexander Graf --- arch/powerpc/kvm/book3s_64_mmu.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/arch/powerpc/kvm/book3s_64_mmu.c b/arch/powerpc/kvm/book3s_64_mmu.c index b871721c0050..2e93bb50a71c 100644 --- a/arch/powerpc/kvm/book3s_64_mmu.c +++ b/arch/powerpc/kvm/book3s_64_mmu.c @@ -167,7 +167,6 @@ static int kvmppc_mmu_book3s_64_xlate(struct kvm_vcpu *vcpu, gva_t eaddr, int i; u8 key = 0; bool found = false; - bool perm_err = false; int second = 0; ulong mp_ea = vcpu->arch.magic_page_ea; @@ -248,11 +247,6 @@ do_second: break; } - if (!gpte->may_read) { - perm_err = true; - continue; - } - dprintk("KVM MMU: Translated 0x%lx [0x%llx] -> 0x%llx " "-> 0x%lx\n", eaddr, avpn, gpte->vpage, gpte->raddr); @@ -281,6 +275,8 @@ do_second: if (pteg[i+1] != oldr) copy_to_user((void __user *)ptegp, pteg, sizeof(pteg)); + if (!gpte->may_read) + return -EPERM; return 0; } else { dprintk("KVM MMU: No PTE found (ea=0x%lx sdr1=0x%llx " @@ -296,13 +292,7 @@ do_second: } } - no_page_found: - - - if (perm_err) - return -EPERM; - return -ENOENT; no_seg_found: -- cgit v1.2.3 From 0f296829b5a59d5a157699cbb23672ccfdd8df4c Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sat, 22 Jun 2013 17:16:32 +1000 Subject: KVM: PPC: Book3S PR: Allow guest to use 1TB segments With this, the guest can use 1TB segments as well as 256MB segments. Since we now have the situation where a single emulated guest segment could correspond to multiple shadow segments (as the shadow segments are still 256MB segments), this adds a new kvmppc_mmu_flush_segment() to scan for all shadow segments that need to be removed. This restructures the guest HPT (hashed page table) lookup code to use the correct hashing and matching functions for HPTEs within a 1TB segment. We use the standard hpt_hash() function instead of open-coding the hash calculation, and we use HPTE_V_COMPARE() with an AVPN value that has the B (segment size) field included. The calculation of avpn is done a little earlier since it doesn't change in the loop starting at the do_second label. The computation in kvmppc_mmu_book3s_64_esid_to_vsid() changes so that it returns a 256MB VSID even if the guest SLB entry is a 1TB entry. This is because the users of this function are creating 256MB SLB entries. We set a new VSID_1T flag so that entries created from 1T segments don't collide with entries from 256MB segments. Signed-off-by: Paul Mackerras Signed-off-by: Alexander Graf --- arch/powerpc/include/asm/kvm_book3s.h | 6 ++-- arch/powerpc/kvm/book3s_64_mmu.c | 60 +++++++++++++++++++++++++---------- arch/powerpc/kvm/book3s_64_mmu_host.c | 17 ++++++++++ arch/powerpc/kvm/book3s_pr.c | 3 +- 4 files changed, 66 insertions(+), 20 deletions(-) diff --git a/arch/powerpc/include/asm/kvm_book3s.h b/arch/powerpc/include/asm/kvm_book3s.h index 349ed85c7d61..08891d07aeb6 100644 --- a/arch/powerpc/include/asm/kvm_book3s.h +++ b/arch/powerpc/include/asm/kvm_book3s.h @@ -107,8 +107,9 @@ struct kvmppc_vcpu_book3s { #define CONTEXT_GUEST 1 #define CONTEXT_GUEST_END 2 -#define VSID_REAL 0x1fffffffffc00000ULL -#define VSID_BAT 0x1fffffffffb00000ULL +#define VSID_REAL 0x0fffffffffc00000ULL +#define VSID_BAT 0x0fffffffffb00000ULL +#define VSID_1T 0x1000000000000000ULL #define VSID_REAL_DR 0x2000000000000000ULL #define VSID_REAL_IR 0x4000000000000000ULL #define VSID_PR 0x8000000000000000ULL @@ -123,6 +124,7 @@ extern void kvmppc_mmu_book3s_32_init(struct kvm_vcpu *vcpu); extern void kvmppc_mmu_book3s_hv_init(struct kvm_vcpu *vcpu); extern int kvmppc_mmu_map_page(struct kvm_vcpu *vcpu, struct kvmppc_pte *pte); extern int kvmppc_mmu_map_segment(struct kvm_vcpu *vcpu, ulong eaddr); +extern void kvmppc_mmu_flush_segment(struct kvm_vcpu *vcpu, ulong eaddr, ulong seg_size); extern void kvmppc_mmu_flush_segments(struct kvm_vcpu *vcpu); extern int kvmppc_book3s_hv_page_fault(struct kvm_run *run, struct kvm_vcpu *vcpu, unsigned long addr, diff --git a/arch/powerpc/kvm/book3s_64_mmu.c b/arch/powerpc/kvm/book3s_64_mmu.c index 2e93bb50a71c..ee435ba6b92a 100644 --- a/arch/powerpc/kvm/book3s_64_mmu.c +++ b/arch/powerpc/kvm/book3s_64_mmu.c @@ -26,6 +26,7 @@ #include #include #include +#include /* #define DEBUG_MMU */ @@ -76,6 +77,24 @@ static struct kvmppc_slb *kvmppc_mmu_book3s_64_find_slbe( return NULL; } +static int kvmppc_slb_sid_shift(struct kvmppc_slb *slbe) +{ + return slbe->tb ? SID_SHIFT_1T : SID_SHIFT; +} + +static u64 kvmppc_slb_offset_mask(struct kvmppc_slb *slbe) +{ + return (1ul << kvmppc_slb_sid_shift(slbe)) - 1; +} + +static u64 kvmppc_slb_calc_vpn(struct kvmppc_slb *slb, gva_t eaddr) +{ + eaddr &= kvmppc_slb_offset_mask(slb); + + return (eaddr >> VPN_SHIFT) | + ((slb->vsid) << (kvmppc_slb_sid_shift(slb) - VPN_SHIFT)); +} + static u64 kvmppc_mmu_book3s_64_ea_to_vp(struct kvm_vcpu *vcpu, gva_t eaddr, bool data) { @@ -85,11 +104,7 @@ static u64 kvmppc_mmu_book3s_64_ea_to_vp(struct kvm_vcpu *vcpu, gva_t eaddr, if (!slb) return 0; - if (slb->tb) - return (((u64)eaddr >> 12) & 0xfffffff) | - (((u64)slb->vsid) << 28); - - return (((u64)eaddr >> 12) & 0xffff) | (((u64)slb->vsid) << 16); + return kvmppc_slb_calc_vpn(slb, eaddr); } static int kvmppc_mmu_book3s_64_get_pagesize(struct kvmppc_slb *slbe) @@ -100,7 +115,8 @@ static int kvmppc_mmu_book3s_64_get_pagesize(struct kvmppc_slb *slbe) static u32 kvmppc_mmu_book3s_64_get_page(struct kvmppc_slb *slbe, gva_t eaddr) { int p = kvmppc_mmu_book3s_64_get_pagesize(slbe); - return ((eaddr & 0xfffffff) >> p); + + return ((eaddr & kvmppc_slb_offset_mask(slbe)) >> p); } static hva_t kvmppc_mmu_book3s_64_get_pteg( @@ -109,13 +125,15 @@ static hva_t kvmppc_mmu_book3s_64_get_pteg( bool second) { u64 hash, pteg, htabsize; - u32 page; + u32 ssize; hva_t r; + u64 vpn; - page = kvmppc_mmu_book3s_64_get_page(slbe, eaddr); htabsize = ((1 << ((vcpu_book3s->sdr1 & 0x1f) + 11)) - 1); - hash = slbe->vsid ^ page; + vpn = kvmppc_slb_calc_vpn(slbe, eaddr); + ssize = slbe->tb ? MMU_SEGSIZE_1T : MMU_SEGSIZE_256M; + hash = hpt_hash(vpn, kvmppc_mmu_book3s_64_get_pagesize(slbe), ssize); if (second) hash = ~hash; hash &= ((1ULL << 39ULL) - 1ULL); @@ -146,7 +164,7 @@ static u64 kvmppc_mmu_book3s_64_get_avpn(struct kvmppc_slb *slbe, gva_t eaddr) u64 avpn; avpn = kvmppc_mmu_book3s_64_get_page(slbe, eaddr); - avpn |= slbe->vsid << (28 - p); + avpn |= slbe->vsid << (kvmppc_slb_sid_shift(slbe) - p); if (p < 24) avpn >>= ((80 - p) - 56) - 8; @@ -189,13 +207,15 @@ static int kvmppc_mmu_book3s_64_xlate(struct kvm_vcpu *vcpu, gva_t eaddr, if (!slbe) goto no_seg_found; + avpn = kvmppc_mmu_book3s_64_get_avpn(slbe, eaddr); + if (slbe->tb) + avpn |= SLB_VSID_B_1T; + do_second: ptegp = kvmppc_mmu_book3s_64_get_pteg(vcpu_book3s, slbe, eaddr, second); if (kvm_is_error_hva(ptegp)) goto no_page_found; - avpn = kvmppc_mmu_book3s_64_get_avpn(slbe, eaddr); - if(copy_from_user(pteg, (void __user *)ptegp, sizeof(pteg))) { printk(KERN_ERR "KVM can't copy data from 0x%lx!\n", ptegp); goto no_page_found; @@ -218,7 +238,7 @@ do_second: continue; /* AVPN compare */ - if (HPTE_V_AVPN_VAL(avpn) == HPTE_V_AVPN_VAL(v)) { + if (HPTE_V_COMPARE(avpn, v)) { u8 pp = (r & HPTE_R_PP) | key; int eaddr_mask = 0xFFF; @@ -324,7 +344,7 @@ static void kvmppc_mmu_book3s_64_slbmte(struct kvm_vcpu *vcpu, u64 rs, u64 rb) slbe->large = (rs & SLB_VSID_L) ? 1 : 0; slbe->tb = (rs & SLB_VSID_B_1T) ? 1 : 0; slbe->esid = slbe->tb ? esid_1t : esid; - slbe->vsid = rs >> 12; + slbe->vsid = (rs & ~SLB_VSID_B) >> (kvmppc_slb_sid_shift(slbe) - 16); slbe->valid = (rb & SLB_ESID_V) ? 1 : 0; slbe->Ks = (rs & SLB_VSID_KS) ? 1 : 0; slbe->Kp = (rs & SLB_VSID_KP) ? 1 : 0; @@ -365,6 +385,7 @@ static u64 kvmppc_mmu_book3s_64_slbmfev(struct kvm_vcpu *vcpu, u64 slb_nr) static void kvmppc_mmu_book3s_64_slbie(struct kvm_vcpu *vcpu, u64 ea) { struct kvmppc_slb *slbe; + u64 seg_size; dprintk("KVM MMU: slbie(0x%llx)\n", ea); @@ -377,7 +398,8 @@ static void kvmppc_mmu_book3s_64_slbie(struct kvm_vcpu *vcpu, u64 ea) slbe->valid = false; - kvmppc_mmu_map_segment(vcpu, ea); + seg_size = 1ull << kvmppc_slb_sid_shift(slbe); + kvmppc_mmu_flush_segment(vcpu, ea & ~(seg_size - 1), seg_size); } static void kvmppc_mmu_book3s_64_slbia(struct kvm_vcpu *vcpu) @@ -457,8 +479,14 @@ static int kvmppc_mmu_book3s_64_esid_to_vsid(struct kvm_vcpu *vcpu, ulong esid, if (vcpu->arch.shared->msr & (MSR_DR|MSR_IR)) { slb = kvmppc_mmu_book3s_64_find_slbe(vcpu, ea); - if (slb) + if (slb) { gvsid = slb->vsid; + if (slb->tb) { + gvsid <<= SID_SHIFT_1T - SID_SHIFT; + gvsid |= esid & ((1ul << (SID_SHIFT_1T - SID_SHIFT)) - 1); + gvsid |= VSID_1T; + } + } } switch (vcpu->arch.shared->msr & (MSR_DR|MSR_IR)) { diff --git a/arch/powerpc/kvm/book3s_64_mmu_host.c b/arch/powerpc/kvm/book3s_64_mmu_host.c index 2c6e7ee8be34..b350d9494b26 100644 --- a/arch/powerpc/kvm/book3s_64_mmu_host.c +++ b/arch/powerpc/kvm/book3s_64_mmu_host.c @@ -301,6 +301,23 @@ out: return r; } +void kvmppc_mmu_flush_segment(struct kvm_vcpu *vcpu, ulong ea, ulong seg_size) +{ + struct kvmppc_book3s_shadow_vcpu *svcpu = svcpu_get(vcpu); + ulong seg_mask = -seg_size; + int i; + + for (i = 1; i < svcpu->slb_max; i++) { + if ((svcpu->slb[i].esid & SLB_ESID_V) && + (svcpu->slb[i].esid & seg_mask) == ea) { + /* Invalidate this entry */ + svcpu->slb[i].esid = 0; + } + } + + svcpu_put(svcpu); +} + void kvmppc_mmu_flush_segments(struct kvm_vcpu *vcpu) { struct kvmppc_book3s_shadow_vcpu *svcpu = svcpu_get(vcpu); diff --git a/arch/powerpc/kvm/book3s_pr.c b/arch/powerpc/kvm/book3s_pr.c index bdc40b8e77d9..19498a567a81 100644 --- a/arch/powerpc/kvm/book3s_pr.c +++ b/arch/powerpc/kvm/book3s_pr.c @@ -1239,8 +1239,7 @@ out: #ifdef CONFIG_PPC64 int kvm_vm_ioctl_get_smmu_info(struct kvm *kvm, struct kvm_ppc_smmu_info *info) { - /* No flags */ - info->flags = 0; + info->flags = KVM_PPC_1T_SEGMENTS; /* SLB is always 64 entries */ info->slb_size = 64; -- cgit v1.2.3 From 681562cd56f5336cbdf6dab0c4b2f6ef16ea89ed Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sat, 22 Jun 2013 17:15:24 +1000 Subject: KVM: PPC: Book3S PR: Invalidate SLB entries properly At present, if the guest creates a valid SLB (segment lookaside buffer) entry with the slbmte instruction, then invalidates it with the slbie instruction, then reads the entry with the slbmfee/slbmfev instructions, the result of the slbmfee will have the valid bit set, even though the entry is not actually considered valid by the host. This is confusing, if not worse. This fixes it by zeroing out the orige and origv fields of the SLB entry structure when the entry is invalidated. Signed-off-by: Paul Mackerras Signed-off-by: Alexander Graf --- arch/powerpc/kvm/book3s_64_mmu.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/kvm/book3s_64_mmu.c b/arch/powerpc/kvm/book3s_64_mmu.c index ee435ba6b92a..739bfbadb85e 100644 --- a/arch/powerpc/kvm/book3s_64_mmu.c +++ b/arch/powerpc/kvm/book3s_64_mmu.c @@ -397,6 +397,8 @@ static void kvmppc_mmu_book3s_64_slbie(struct kvm_vcpu *vcpu, u64 ea) dprintk("KVM MMU: slbie(0x%llx, 0x%llx)\n", ea, slbe->esid); slbe->valid = false; + slbe->orige = 0; + slbe->origv = 0; seg_size = 1ull << kvmppc_slb_sid_shift(slbe); kvmppc_mmu_flush_segment(vcpu, ea & ~(seg_size - 1), seg_size); @@ -408,8 +410,11 @@ static void kvmppc_mmu_book3s_64_slbia(struct kvm_vcpu *vcpu) dprintk("KVM MMU: slbia()\n"); - for (i = 1; i < vcpu->arch.slb_nr; i++) + for (i = 1; i < vcpu->arch.slb_nr; i++) { vcpu->arch.slb[i].valid = false; + vcpu->arch.slb[i].orige = 0; + vcpu->arch.slb[i].origv = 0; + } if (vcpu->arch.shared->msr & MSR_IR) { kvmppc_mmu_flush_segments(vcpu); -- cgit v1.2.3 From a3ff5fbc94a829680d4aa005cd17add1c1a1fb5b Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 27 Jun 2013 01:07:15 +0200 Subject: KVM: PPC: Ignore PIR writes While technically it's legal to write to PIR and have the identifier changed, we don't implement logic to do so because we simply expose vcpu_id to the guest. So instead, let's ignore writes to PIR. This ensures that we don't inject faults into the guest for something the guest is allowed to do. While at it, we cross our fingers hoping that it also doesn't mind that we broke its PIR read values. Signed-off-by: Alexander Graf --- arch/powerpc/kvm/emulate.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/powerpc/kvm/emulate.c b/arch/powerpc/kvm/emulate.c index 631a2650e4e4..2c52ada30775 100644 --- a/arch/powerpc/kvm/emulate.c +++ b/arch/powerpc/kvm/emulate.c @@ -169,6 +169,9 @@ static int kvmppc_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, int rs) vcpu->arch.shared->sprg3 = spr_val; break; + /* PIR can legally be written, but we ignore it */ + case SPRN_PIR: break; + default: emulated = kvmppc_core_emulate_mtspr(vcpu, sprn, spr_val); -- cgit v1.2.3 From e1b85c17bf3e4f2ecbf9ec824c4048a06078100b Mon Sep 17 00:00:00 2001 From: Sebastien Bessiere Date: Fri, 14 Jun 2013 17:57:03 +0200 Subject: trivial: powerpc: Fix typo in ioei_interrupt() description Signed-off-by: Sebastien Bessiere Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/pseries/io_event_irq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/platforms/pseries/io_event_irq.c b/arch/powerpc/platforms/pseries/io_event_irq.c index ef9d9d84c7d5..5ea88d1541f7 100644 --- a/arch/powerpc/platforms/pseries/io_event_irq.c +++ b/arch/powerpc/platforms/pseries/io_event_irq.c @@ -115,7 +115,7 @@ static struct pseries_io_event * ioei_find_event(struct rtas_error_log *elog) * by scope or event type alone. For example, Torrent ISR route change * event is reported with scope 0x00 (Not Applicatable) rather than * 0x3B (Torrent-hub). It is better to let the clients to identify - * who owns the the event. + * who owns the event. */ static irqreturn_t ioei_interrupt(int irq, void *dev_id) -- cgit v1.2.3 From 80aa0fb4940bf8ee52bcb574d74459a7aea45621 Mon Sep 17 00:00:00 2001 From: James Yang Date: Tue, 25 Jun 2013 11:41:05 -0500 Subject: powerpc: Fix string instr. emulation for 32-bit processes on ppc64 String instruction emulation would erroneously result in a segfault if the upper bits of the EA are set and is so high that it fails access check. Truncate the EA to 32 bits if the process is 32-bit. Signed-off-by: James Yang Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/traps.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index 071f6e040eb2..300daf3e7ab0 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -866,6 +866,10 @@ static int emulate_string_inst(struct pt_regs *regs, u32 instword) u8 val; u32 shift = 8 * (3 - (pos & 0x3)); + /* if process is 32-bit, clear upper 32 bits of EA */ + if ((regs->msr & MSR_64BIT) == 0) + EA &= 0xFFFFFFFF; + switch ((instword & PPC_INST_STRING_MASK)) { case PPC_INST_LSWX: case PPC_INST_LSWI: -- cgit v1.2.3 From 090b9284d72561644d17a6e568d29c9e472c9865 Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Fri, 28 Jun 2013 18:17:09 +1000 Subject: powerpc/tm: Clear MSR RI in non-recoverable TM code When we treclaim and trecheckpoint there's an unavoidable period when r1 will not be a valid kernel stack pointer. This patch clears the MSR recoverable interrupt (RI) bit over these regions to indicate we have an invalid kernel stack pointer. For treclaim, the region over which we clear MSR RI is larger than required to avoid the need for an extra costly mtmsrd. Thanks to Paulus for suggesting this change. Signed-off-by: Michael Neuling Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/tm.S | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/kernel/tm.S b/arch/powerpc/kernel/tm.S index 2da67e7a16d5..51be8fb24803 100644 --- a/arch/powerpc/kernel/tm.S +++ b/arch/powerpc/kernel/tm.S @@ -112,9 +112,18 @@ _GLOBAL(tm_reclaim) std r3, STACK_PARAM(0)(r1) SAVE_NVGPRS(r1) + /* We need to setup MSR for VSX register save instructions. Here we + * also clear the MSR RI since when we do the treclaim, we won't have a + * valid kernel pointer for a while. We clear RI here as it avoids + * adding another mtmsr closer to the treclaim. This makes the region + * maked as non-recoverable wider than it needs to be but it saves on + * inserting another mtmsrd later. + */ mfmsr r14 mr r15, r14 ori r15, r15, MSR_FP + li r16, MSR_RI + andc r15, r15, r16 oris r15, r15, MSR_VEC@h #ifdef CONFIG_VSX BEGIN_FTR_SECTION @@ -349,9 +358,10 @@ restore_gprs: mtcr r5 mtxer r6 - /* MSR and flags: We don't change CRs, and we don't need to alter - * MSR. + /* Clear the MSR RI since we are about to change R1. EE is already off */ + li r4, 0 + mtmsrd r4, 1 REST_4GPRS(0, r7) /* GPR0-3 */ REST_GPR(4, r7) /* GPR4-6 */ @@ -377,6 +387,10 @@ restore_gprs: GET_PACA(r13) GET_SCRATCH0(r1) + /* R1 is restored, so we are recoverable again. EE is still off */ + li r4, MSR_RI + mtmsrd r4, 1 + REST_NVGPRS(r1) addi r1, r1, TM_FRAME_SIZE -- cgit v1.2.3 From 89970d28cb3fd1c678f769d62d077c10ec5340e1 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sun, 30 Jun 2013 11:07:13 +0300 Subject: remoteproc: fix error return code in rproc_fw_boot() Set 'ret' to -EINVAL when needed, so a sensible return value is returned on errors. Signed-off-by: Wei Yongjun [fix additional instances of this bug as well, update commit log] Signed-off-by: Ohad Ben-Cohen --- drivers/remoteproc/remoteproc_core.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index 022dc635d01e..7e33536b5c2b 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -815,18 +815,17 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw) } rproc->bootaddr = rproc_get_boot_addr(rproc, fw); + ret = -EINVAL; /* look for the resource table */ table = rproc_find_rsc_table(rproc, fw, &tablesz); if (!table) { - ret = -EINVAL; goto clean_up; } /* Verify that resource table in loaded fw is unchanged */ if (rproc->table_csum != crc32(0, table, tablesz)) { dev_err(dev, "resource checksum failed, fw changed?\n"); - ret = -EINVAL; goto clean_up; } @@ -852,8 +851,10 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw) * copy this information to device memory. */ loaded_table = rproc_find_loaded_rsc_table(rproc, fw); - if (!loaded_table) + if (!loaded_table) { + ret = -EINVAL; goto clean_up; + } memcpy(loaded_table, rproc->cached_table, tablesz); -- cgit v1.2.3 From 57971159208757a37d44356764911703468a7ca3 Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Sun, 30 Jun 2013 11:33:05 +0300 Subject: remoteproc: fix checkpatch errors in remoteproc code This patch fixes relevant checkpatch errors and warnings in the remoteproc source files. Signed-off-by: Suman Anna [drop 80-char-lines checkpatch fixes and update commit log accordingly] Signed-off-by: Ohad Ben-Cohen --- drivers/remoteproc/remoteproc_debugfs.c | 3 +-- drivers/remoteproc/remoteproc_internal.h | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/remoteproc/remoteproc_debugfs.c b/drivers/remoteproc/remoteproc_debugfs.c index 157a57309601..9d30809bb407 100644 --- a/drivers/remoteproc/remoteproc_debugfs.c +++ b/drivers/remoteproc/remoteproc_debugfs.c @@ -248,6 +248,5 @@ void __init rproc_init_debugfs(void) void __exit rproc_exit_debugfs(void) { - if (rproc_dbg) - debugfs_remove(rproc_dbg); + debugfs_remove(rproc_dbg); } diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h index 157e762c1571..70701a50ddfa 100644 --- a/drivers/remoteproc/remoteproc_internal.h +++ b/drivers/remoteproc/remoteproc_internal.h @@ -107,12 +107,12 @@ struct resource_table *rproc_find_rsc_table(struct rproc *rproc, static inline struct resource_table *rproc_find_loaded_rsc_table(struct rproc *rproc, - const struct firmware *fw) + const struct firmware *fw) { if (rproc->fw_ops->find_loaded_rsc_table) return rproc->fw_ops->find_loaded_rsc_table(rproc, fw); - return NULL; + return NULL; } extern const struct rproc_fw_ops rproc_elf_fw_ops; -- cgit v1.2.3 From 539fde59ebc615bcb9af373af8947e866dc072c7 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 30 Jun 2013 08:58:57 +0800 Subject: pinctrl: st: Remove unnecessary use of of_match_ptr macro This is a DT only driver and st_pctl_of_match is always compiled in. Hence of_match_ptr is unnecessary. Signed-off-by: Axel Lin Acked-by: Linus Walleij Signed-off-by: Mark Brown --- drivers/pinctrl/pinctrl-st.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/pinctrl-st.c b/drivers/pinctrl/pinctrl-st.c index de8c62664fa8..04d4506ae18d 100644 --- a/drivers/pinctrl/pinctrl-st.c +++ b/drivers/pinctrl/pinctrl-st.c @@ -1391,7 +1391,7 @@ static struct platform_driver st_pctl_driver = { .driver = { .name = "st-pinctrl", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(st_pctl_of_match), + .of_match_table = st_pctl_of_match, }, .probe = st_pctl_probe, }; -- cgit v1.2.3 From ee441140e7676766b0ce8b9e9a259066bb54c149 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 27 Jun 2013 22:00:04 +0200 Subject: ASoC: adau1701: more direct regmap usage Replace calls to snd_soc_update_bits() with regmap_update_bits(). Signed-off-by: Daniel Mack Acked-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/codecs/adau1701.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c index 0e250f118c0e..4cd4dd10fa24 100644 --- a/sound/soc/codecs/adau1701.c +++ b/sound/soc/codecs/adau1701.c @@ -334,7 +334,7 @@ static int adau1701_set_capture_pcm_format(struct snd_soc_codec *codec, mask |= ADAU1701_SEROCTL_MSB_DEALY_MASK; } - snd_soc_update_bits(codec, ADAU1701_SEROCTL, mask, val); + regmap_update_bits(adau1701->regmap, ADAU1701_SEROCTL, mask, val); return 0; } @@ -362,7 +362,7 @@ static int adau1701_set_playback_pcm_format(struct snd_soc_codec *codec, return -EINVAL; } - snd_soc_update_bits(codec, ADAU1701_SERICTL, + regmap_update_bits(adau1701->regmap, ADAU1701_SERICTL, ADAU1701_SERICTL_MODE_MASK, val); return 0; @@ -403,7 +403,7 @@ static int adau1701_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - snd_soc_update_bits(codec, ADAU1701_DSPCTRL, + regmap_update_bits(adau1701->regmap, ADAU1701_DSPCTRL, ADAU1701_DSPCTRL_SR_MASK, val); format = params_format(params); @@ -490,6 +490,7 @@ static int adau1701_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { unsigned int mask = ADAU1701_AUXNPOW_VBPD | ADAU1701_AUXNPOW_VRPD; + struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec); switch (level) { case SND_SOC_BIAS_ON: @@ -498,11 +499,13 @@ static int adau1701_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: /* Enable VREF and VREF buffer */ - snd_soc_update_bits(codec, ADAU1701_AUXNPOW, mask, 0x00); + regmap_update_bits(adau1701->regmap, + ADAU1701_AUXNPOW, mask, 0x00); break; case SND_SOC_BIAS_OFF: /* Disable VREF and VREF buffer */ - snd_soc_update_bits(codec, ADAU1701_AUXNPOW, mask, mask); + regmap_update_bits(adau1701->regmap, + ADAU1701_AUXNPOW, mask, mask); break; } @@ -514,6 +517,7 @@ static int adau1701_digital_mute(struct snd_soc_dai *dai, int mute) { struct snd_soc_codec *codec = dai->codec; unsigned int mask = ADAU1701_DSPCTRL_DAM; + struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec); unsigned int val; if (mute) @@ -521,7 +525,7 @@ static int adau1701_digital_mute(struct snd_soc_dai *dai, int mute) else val = mask; - snd_soc_update_bits(codec, ADAU1701_DSPCTRL, mask, val); + regmap_update_bits(adau1701->regmap, ADAU1701_DSPCTRL, mask, val); return 0; } @@ -543,7 +547,8 @@ static int adau1701_set_sysclk(struct snd_soc_codec *codec, int clk_id, return -EINVAL; } - snd_soc_update_bits(codec, ADAU1701_OSCIPOW, ADAU1701_OSCIPOW_OPD, val); + regmap_update_bits(adau1701->regmap, ADAU1701_OSCIPOW, + ADAU1701_OSCIPOW_OPD, val); adau1701->sysclk = freq; return 0; -- cgit v1.2.3 From cef929ec4e80fcfe249c800408a5f9d72ebd5933 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 27 Jun 2013 22:00:05 +0200 Subject: ASoC: adau1701: remove control_data assignment codec->control_data has to be left unset to make the ASoC core access the regmap properly. That bug slipped in during a rebase session of the driver refactoring. Signed-off-by: Daniel Mack Acked-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/codecs/adau1701.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c index 4cd4dd10fa24..d1124a5b3471 100644 --- a/sound/soc/codecs/adau1701.c +++ b/sound/soc/codecs/adau1701.c @@ -600,8 +600,6 @@ static int adau1701_probe(struct snd_soc_codec *codec) unsigned int val; struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec); - codec->control_data = to_i2c_client(codec->dev); - /* * Let the pll_clkdiv variable default to something that won't happen * at runtime. That way, we can postpone the firmware download from -- cgit v1.2.3 From 496e4ae7dc944faa1721bfda7e9d834d5611a874 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sat, 29 Jun 2013 14:15:47 +0200 Subject: netfilter: nf_queue: add NFQA_SKB_CSUM_NOTVERIFIED info flag The common case is that TCP/IP checksums have already been verified, e.g. by hardware (rx checksum offload), or conntrack. Userspace can use this flag to determine when the checksum has not been validated yet. If the flag is set, this doesn't necessarily mean that the packet has an invalid checksum, e.g. if NIC doesn't support rx checksum. Userspace that sucessfully enabled NFQA_CFG_F_GSO queue feature flag can infer that IP/TCP checksum has already been validated if either the SKB_INFO attribute is not present or the NFQA_SKB_CSUM_NOTVERIFIED flag is unset. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/uapi/linux/netfilter/nfnetlink_queue.h | 2 ++ net/netfilter/nfnetlink_queue_core.c | 16 ++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/include/uapi/linux/netfilter/nfnetlink_queue.h b/include/uapi/linux/netfilter/nfnetlink_queue.h index a2308ae5a73d..3a9b92147339 100644 --- a/include/uapi/linux/netfilter/nfnetlink_queue.h +++ b/include/uapi/linux/netfilter/nfnetlink_queue.h @@ -105,5 +105,7 @@ enum nfqnl_attr_config { #define NFQA_SKB_CSUMNOTREADY (1 << 0) /* packet is GSO (i.e., exceeds device mtu) */ #define NFQA_SKB_GSO (1 << 1) +/* csum not validated (incoming device doesn't support hw checksum, etc.) */ +#define NFQA_SKB_CSUM_NOTVERIFIED (1 << 2) #endif /* _NFNETLINK_QUEUE_H */ diff --git a/net/netfilter/nfnetlink_queue_core.c b/net/netfilter/nfnetlink_queue_core.c index 299a48ae5dc9..971ea145ab3e 100644 --- a/net/netfilter/nfnetlink_queue_core.c +++ b/net/netfilter/nfnetlink_queue_core.c @@ -280,12 +280,17 @@ nfqnl_zcopy(struct sk_buff *to, const struct sk_buff *from, int len, int hlen) skb_shinfo(to)->nr_frags = j; } -static int nfqnl_put_packet_info(struct sk_buff *nlskb, struct sk_buff *packet) +static int +nfqnl_put_packet_info(struct sk_buff *nlskb, struct sk_buff *packet, + bool csum_verify) { __u32 flags = 0; if (packet->ip_summed == CHECKSUM_PARTIAL) flags = NFQA_SKB_CSUMNOTREADY; + else if (csum_verify) + flags = NFQA_SKB_CSUM_NOTVERIFIED; + if (skb_is_gso(packet)) flags |= NFQA_SKB_GSO; @@ -310,6 +315,7 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue, struct net_device *outdev; struct nf_conn *ct = NULL; enum ip_conntrack_info uninitialized_var(ctinfo); + bool csum_verify; size = nlmsg_total_size(sizeof(struct nfgenmsg)) + nla_total_size(sizeof(struct nfqnl_msg_packet_hdr)) @@ -327,6 +333,12 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue, if (entskb->tstamp.tv64) size += nla_total_size(sizeof(struct nfqnl_msg_packet_timestamp)); + if (entry->hook <= NF_INET_FORWARD || + (entry->hook == NF_INET_POST_ROUTING && entskb->sk == NULL)) + csum_verify = !skb_csum_unnecessary(entskb); + else + csum_verify = false; + outdev = entry->outdev; switch ((enum nfqnl_config_mode)ACCESS_ONCE(queue->copy_mode)) { @@ -476,7 +488,7 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue, nla_put_be32(skb, NFQA_CAP_LEN, htonl(cap_len))) goto nla_put_failure; - if (nfqnl_put_packet_info(skb, entskb)) + if (nfqnl_put_packet_info(skb, entskb, csum_verify)) goto nla_put_failure; if (data_len) { -- cgit v1.2.3 From 443c6ae253e96db9a5800a28d7c61131e81c2dee Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Mon, 24 Jun 2013 14:39:52 +0200 Subject: mfd: max8998: Add irq domain support This patch adds irq domain support for max8998 interrupts. To keep both non-DT and DT worlds happy, simple domain is used, which is linear when no explicit IRQ base is specified and legacy, with static mapping, otherwise. Signed-off-by: Tomasz Figa Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 1 + drivers/mfd/max8998-irq.c | 65 +++++++++++++++++++++++-------------- drivers/rtc/rtc-max8998.c | 12 ++++++- include/linux/mfd/max8998-private.h | 5 ++- include/linux/mfd/max8998.h | 2 +- 5 files changed, 57 insertions(+), 28 deletions(-) diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 3bb2932eafb1..aecd6ddcbbbf 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -363,6 +363,7 @@ config MFD_MAX8998 bool "Maxim Semiconductor MAX8998/National LP3974 PMIC Support" depends on I2C=y && GENERIC_HARDIRQS select MFD_CORE + select IRQ_DOMAIN help Say yes here to support for Maxim Semiconductor MAX8998 and National Semiconductor LP3974. This is a Power Management IC. diff --git a/drivers/mfd/max8998-irq.c b/drivers/mfd/max8998-irq.c index 5919710dc9ed..c469477eb778 100644 --- a/drivers/mfd/max8998-irq.c +++ b/drivers/mfd/max8998-irq.c @@ -14,6 +14,7 @@ #include #include #include +#include #include struct max8998_irq_data { @@ -99,7 +100,8 @@ static struct max8998_irq_data max8998_irqs[] = { static inline struct max8998_irq_data * irq_to_max8998_irq(struct max8998_dev *max8998, int irq) { - return &max8998_irqs[irq - max8998->irq_base]; + struct irq_data *data = irq_get_irq_data(irq); + return &max8998_irqs[data->hwirq]; } static void max8998_irq_lock(struct irq_data *data) @@ -176,8 +178,14 @@ static irqreturn_t max8998_irq_thread(int irq, void *data) /* Report */ for (i = 0; i < MAX8998_IRQ_NR; i++) { - if (irq_reg[max8998_irqs[i].reg - 1] & max8998_irqs[i].mask) - handle_nested_irq(max8998->irq_base + i); + if (irq_reg[max8998_irqs[i].reg - 1] & max8998_irqs[i].mask) { + irq = irq_find_mapping(max8998->irq_domain, i); + if (WARN_ON(!irq)) { + disable_irq_nosync(max8998->irq); + return IRQ_NONE; + } + handle_nested_irq(irq); + } } return IRQ_HANDLED; @@ -185,27 +193,40 @@ static irqreturn_t max8998_irq_thread(int irq, void *data) int max8998_irq_resume(struct max8998_dev *max8998) { - if (max8998->irq && max8998->irq_base) - max8998_irq_thread(max8998->irq_base, max8998); + if (max8998->irq && max8998->irq_domain) + max8998_irq_thread(max8998->irq, max8998); + return 0; +} + +static int max8998_irq_domain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hw) +{ + struct max8997_dev *max8998 = d->host_data; + + irq_set_chip_data(irq, max8998); + irq_set_chip_and_handler(irq, &max8998_irq_chip, handle_edge_irq); + irq_set_nested_thread(irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID); +#else + irq_set_noprobe(irq); +#endif return 0; } +static struct irq_domain_ops max8998_irq_domain_ops = { + .map = max8998_irq_domain_map, +}; + int max8998_irq_init(struct max8998_dev *max8998) { int i; - int cur_irq; int ret; + struct irq_domain *domain; if (!max8998->irq) { dev_warn(max8998->dev, "No interrupt specified, no interrupts\n"); - max8998->irq_base = 0; - return 0; - } - - if (!max8998->irq_base) { - dev_err(max8998->dev, - "No interrupt base specified, no interrupts\n"); return 0; } @@ -221,19 +242,13 @@ int max8998_irq_init(struct max8998_dev *max8998) max8998_write_reg(max8998->i2c, MAX8998_REG_STATUSM1, 0xff); max8998_write_reg(max8998->i2c, MAX8998_REG_STATUSM2, 0xff); - /* register with genirq */ - for (i = 0; i < MAX8998_IRQ_NR; i++) { - cur_irq = i + max8998->irq_base; - irq_set_chip_data(cur_irq, max8998); - irq_set_chip_and_handler(cur_irq, &max8998_irq_chip, - handle_edge_irq); - irq_set_nested_thread(cur_irq, 1); -#ifdef CONFIG_ARM - set_irq_flags(cur_irq, IRQF_VALID); -#else - irq_set_noprobe(cur_irq); -#endif + domain = irq_domain_add_simple(NULL, MAX8998_IRQ_NR, + max8998->irq_base, &max8998_irq_domain_ops, max8998); + if (!domain) { + dev_err(max8998->dev, "could not create irq domain\n"); + return -ENODEV; } + max8998->irq_domain = domain; ret = request_threaded_irq(max8998->irq, NULL, max8998_irq_thread, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, diff --git a/drivers/rtc/rtc-max8998.c b/drivers/rtc/rtc-max8998.c index d5af7baa48b5..46f23014759b 100644 --- a/drivers/rtc/rtc-max8998.c +++ b/drivers/rtc/rtc-max8998.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -264,7 +265,6 @@ static int max8998_rtc_probe(struct platform_device *pdev) info->dev = &pdev->dev; info->max8998 = max8998; info->rtc = max8998->rtc; - info->irq = max8998->irq_base + MAX8998_IRQ_ALARM0; platform_set_drvdata(pdev, info); @@ -277,6 +277,15 @@ static int max8998_rtc_probe(struct platform_device *pdev) goto out_rtc; } + if (!max8998->irq_domain) + goto no_irq; + + info->irq = irq_create_mapping(max8998->irq_domain, MAX8998_IRQ_ALARM0); + if (!info->irq) { + dev_warn(&pdev->dev, "Failed to map alarm IRQ\n"); + goto no_irq; + } + ret = devm_request_threaded_irq(&pdev->dev, info->irq, NULL, max8998_rtc_alarm_irq, 0, "rtc-alarm0", info); @@ -284,6 +293,7 @@ static int max8998_rtc_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n", info->irq, ret); +no_irq: dev_info(&pdev->dev, "RTC CHIP NAME: %s\n", pdev->id_entry->name); if (pdata && pdata->rtc_delay) { info->lp3974_bug_workaround = true; diff --git a/include/linux/mfd/max8998-private.h b/include/linux/mfd/max8998-private.h index effa5d3b96ae..bfb48b6fcc74 100644 --- a/include/linux/mfd/max8998-private.h +++ b/include/linux/mfd/max8998-private.h @@ -132,6 +132,8 @@ enum { #define MAX8998_ENRAMP (1 << 4) +struct irq_domain; + /** * struct max8998_dev - max8998 master device for sub-drivers * @dev: master device of the chip (can be used to access platform data) @@ -153,7 +155,8 @@ struct max8998_dev { struct mutex iolock; struct mutex irqlock; - int irq_base; + unsigned int irq_base; + struct irq_domain *irq_domain; int irq; int ono; u8 irq_masks_cur[MAX8998_NUM_IRQ_REGS]; diff --git a/include/linux/mfd/max8998.h b/include/linux/mfd/max8998.h index 6823548d0c0a..75471183b87a 100644 --- a/include/linux/mfd/max8998.h +++ b/include/linux/mfd/max8998.h @@ -100,7 +100,7 @@ struct max8998_regulator_data { struct max8998_platform_data { struct max8998_regulator_data *regulators; int num_regulators; - int irq_base; + unsigned int irq_base; int ono; bool buck_voltage_lock; int buck1_voltage1; -- cgit v1.2.3 From 4280e0b42bd590316a048d66ea356e78c5d0464e Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Mon, 24 Jun 2013 14:39:53 +0200 Subject: regulator: max8998: Use arrays for specifying voltages in platform data This patch modifies the platform data of max8998 to use arrays for specifying predefined voltages of buck1 and buck2 instead of separate field for each voltage. This allows to simplify the code a bit and will help in adding support for Device Tree, which will be introduced in further patch. Signed-off-by: Tomasz Figa Acked-by: Mark Brown Signed-off-by: Samuel Ortiz --- arch/arm/mach-exynos/mach-universal_c210.c | 8 +-- arch/arm/mach-s5pv210/mach-aquila.c | 8 +-- arch/arm/mach-s5pv210/mach-goni.c | 8 +-- drivers/regulator/max8998.c | 96 +++++++++--------------------- include/linux/mfd/max8998.h | 16 ++--- 5 files changed, 39 insertions(+), 97 deletions(-) diff --git a/arch/arm/mach-exynos/mach-universal_c210.c b/arch/arm/mach-exynos/mach-universal_c210.c index 74ddb2b55614..f912444cae33 100644 --- a/arch/arm/mach-exynos/mach-universal_c210.c +++ b/arch/arm/mach-exynos/mach-universal_c210.c @@ -540,15 +540,11 @@ static struct max8998_regulator_data lp3974_regulators[] = { static struct max8998_platform_data universal_lp3974_pdata = { .num_regulators = ARRAY_SIZE(lp3974_regulators), .regulators = lp3974_regulators, - .buck1_voltage1 = 1100000, /* INT */ - .buck1_voltage2 = 1000000, - .buck1_voltage3 = 1100000, - .buck1_voltage4 = 1000000, + .buck1_voltage = { 1100000, 1000000, 1100000, 1000000 }, .buck1_set1 = EXYNOS4_GPX0(5), .buck1_set2 = EXYNOS4_GPX0(6), - .buck2_voltage1 = 1200000, /* G3D */ - .buck2_voltage2 = 1100000, .buck1_default_idx = 0, + .buck2_voltage = { 1200000, 1100000 }, .buck2_set3 = EXYNOS4_GPE2(0), .buck2_default_idx = 0, .wakeup = true, diff --git a/arch/arm/mach-s5pv210/mach-aquila.c b/arch/arm/mach-s5pv210/mach-aquila.c index ed2b85485b9d..ad40ab0f5dbd 100644 --- a/arch/arm/mach-s5pv210/mach-aquila.c +++ b/arch/arm/mach-s5pv210/mach-aquila.c @@ -377,12 +377,8 @@ static struct max8998_platform_data aquila_max8998_pdata = { .buck1_set1 = S5PV210_GPH0(3), .buck1_set2 = S5PV210_GPH0(4), .buck2_set3 = S5PV210_GPH0(5), - .buck1_voltage1 = 1200000, - .buck1_voltage2 = 1200000, - .buck1_voltage3 = 1200000, - .buck1_voltage4 = 1200000, - .buck2_voltage1 = 1200000, - .buck2_voltage2 = 1200000, + .buck1_voltage = { 1200000, 1200000, 1200000, 1200000 }, + .buck2_voltage = { 1200000, 1200000 }, }; #endif diff --git a/arch/arm/mach-s5pv210/mach-goni.c b/arch/arm/mach-s5pv210/mach-goni.c index 30b24ad84f49..e5cd9fbf19e9 100644 --- a/arch/arm/mach-s5pv210/mach-goni.c +++ b/arch/arm/mach-s5pv210/mach-goni.c @@ -580,12 +580,8 @@ static struct max8998_platform_data goni_max8998_pdata = { .buck1_set1 = S5PV210_GPH0(3), .buck1_set2 = S5PV210_GPH0(4), .buck2_set3 = S5PV210_GPH0(5), - .buck1_voltage1 = 1200000, - .buck1_voltage2 = 1200000, - .buck1_voltage3 = 1200000, - .buck1_voltage4 = 1200000, - .buck2_voltage1 = 1200000, - .buck2_voltage2 = 1200000, + .buck1_voltage = { 1200000, 1200000, 1200000, 1200000 }, + .buck2_voltage = { 1200000, 1200000 }, }; #endif diff --git a/drivers/regulator/max8998.c b/drivers/regulator/max8998.c index a57a1b15cdba..8c45b93b7334 100644 --- a/drivers/regulator/max8998.c +++ b/drivers/regulator/max8998.c @@ -630,6 +630,7 @@ static int max8998_pmic_probe(struct platform_device *pdev) struct max8998_data *max8998; struct i2c_client *i2c; int i, ret, size; + unsigned int v; if (!pdata) { dev_err(pdev->dev.parent, "No platform init data supplied\n"); @@ -688,53 +689,21 @@ static int max8998_pmic_probe(struct platform_device *pdev) gpio_request(pdata->buck1_set2, "MAX8998 BUCK1_SET2"); gpio_direction_output(pdata->buck1_set2, (max8998->buck1_idx >> 1) & 0x1); - /* Set predefined value for BUCK1 register 1 */ - i = 0; - while (buck12_voltage_map_desc.min + - buck12_voltage_map_desc.step*i - < pdata->buck1_voltage1) - i++; - max8998->buck1_vol[0] = i; - ret = max8998_write_reg(i2c, MAX8998_REG_BUCK1_VOLTAGE1, i); - if (ret) - goto err_out; - - /* Set predefined value for BUCK1 register 2 */ - i = 0; - while (buck12_voltage_map_desc.min + - buck12_voltage_map_desc.step*i - < pdata->buck1_voltage2) - i++; - - max8998->buck1_vol[1] = i; - ret = max8998_write_reg(i2c, MAX8998_REG_BUCK1_VOLTAGE2, i); - if (ret) - goto err_out; - - /* Set predefined value for BUCK1 register 3 */ - i = 0; - while (buck12_voltage_map_desc.min + - buck12_voltage_map_desc.step*i - < pdata->buck1_voltage3) - i++; - - max8998->buck1_vol[2] = i; - ret = max8998_write_reg(i2c, MAX8998_REG_BUCK1_VOLTAGE3, i); - if (ret) - goto err_out; - - /* Set predefined value for BUCK1 register 4 */ - i = 0; - while (buck12_voltage_map_desc.min + - buck12_voltage_map_desc.step*i - < pdata->buck1_voltage4) - i++; - - max8998->buck1_vol[3] = i; - ret = max8998_write_reg(i2c, MAX8998_REG_BUCK1_VOLTAGE4, i); - if (ret) - goto err_out; + /* Set predefined values for BUCK1 registers */ + for (v = 0; v < ARRAY_SIZE(pdata->buck1_voltage); ++v) { + i = 0; + while (buck12_voltage_map_desc.min + + buck12_voltage_map_desc.step*i + < pdata->buck1_voltage[v]) + i++; + + max8998->buck1_vol[v] = i; + ret = max8998_write_reg(i2c, + MAX8998_REG_BUCK1_VOLTAGE1 + v, i); + if (ret) + goto err_out; + } } if (gpio_is_valid(pdata->buck2_set3)) { @@ -750,27 +719,20 @@ static int max8998_pmic_probe(struct platform_device *pdev) gpio_direction_output(pdata->buck2_set3, max8998->buck2_idx & 0x1); - /* BUCK2 register 1 */ - i = 0; - while (buck12_voltage_map_desc.min + - buck12_voltage_map_desc.step*i - < pdata->buck2_voltage1) - i++; - max8998->buck2_vol[0] = i; - ret = max8998_write_reg(i2c, MAX8998_REG_BUCK2_VOLTAGE1, i); - if (ret) - goto err_out; - - /* BUCK2 register 2 */ - i = 0; - while (buck12_voltage_map_desc.min + - buck12_voltage_map_desc.step*i - < pdata->buck2_voltage2) - i++; - max8998->buck2_vol[1] = i; - ret = max8998_write_reg(i2c, MAX8998_REG_BUCK2_VOLTAGE2, i); - if (ret) - goto err_out; + /* Set predefined values for BUCK2 registers */ + for (v = 0; v < ARRAY_SIZE(pdata->buck2_voltage); ++v) { + i = 0; + while (buck12_voltage_map_desc.min + + buck12_voltage_map_desc.step*i + < pdata->buck2_voltage[v]) + i++; + + max8998->buck2_vol[v] = i; + ret = max8998_write_reg(i2c, + MAX8998_REG_BUCK2_VOLTAGE1 + v, i); + if (ret) + goto err_out; + } } for (i = 0; i < pdata->num_regulators; i++) { diff --git a/include/linux/mfd/max8998.h b/include/linux/mfd/max8998.h index 75471183b87a..ca56bb03bc69 100644 --- a/include/linux/mfd/max8998.h +++ b/include/linux/mfd/max8998.h @@ -73,12 +73,8 @@ struct max8998_regulator_data { * @buck_voltage_lock: Do NOT change the values of the following six * registers set by buck?_voltage?. The voltage of BUCK1/2 cannot * be other than the preset values. - * @buck1_voltage1: BUCK1 DVS mode 1 voltage register - * @buck1_voltage2: BUCK1 DVS mode 2 voltage register - * @buck1_voltage3: BUCK1 DVS mode 3 voltage register - * @buck1_voltage4: BUCK1 DVS mode 4 voltage register - * @buck2_voltage1: BUCK2 DVS mode 1 voltage register - * @buck2_voltage2: BUCK2 DVS mode 2 voltage register + * @buck1_voltage: BUCK1 DVS mode 1 voltage registers + * @buck2_voltage: BUCK2 DVS mode 2 voltage registers * @buck1_set1: BUCK1 gpio pin 1 to set output voltage * @buck1_set2: BUCK1 gpio pin 2 to set output voltage * @buck1_default_idx: Default for BUCK1 gpio pin 1, 2 @@ -103,12 +99,8 @@ struct max8998_platform_data { unsigned int irq_base; int ono; bool buck_voltage_lock; - int buck1_voltage1; - int buck1_voltage2; - int buck1_voltage3; - int buck1_voltage4; - int buck2_voltage1; - int buck2_voltage2; + int buck1_voltage[4]; + int buck2_voltage[2]; int buck1_set1; int buck1_set2; int buck1_default_idx; -- cgit v1.2.3 From ee999fb3f17faa3af6028bf7130707fe0d4157a4 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Tue, 25 Jun 2013 16:08:10 +0200 Subject: mfd: max8998: Add support for Device Tree This patch adds Device Tree support to max8998 driver. Signed-off-by: Tomasz Figa Acked-by: Mark Brown Signed-off-by: Samuel Ortiz --- Documentation/devicetree/bindings/mfd/max8998.txt | 119 ++++++++++++++++++++ drivers/mfd/max8998.c | 67 ++++++++++- drivers/regulator/max8998.c | 131 +++++++++++++++++++++- drivers/rtc/rtc-max8998.c | 2 +- include/linux/mfd/max8998-private.h | 2 + include/linux/mfd/max8998.h | 2 + 6 files changed, 316 insertions(+), 7 deletions(-) create mode 100644 Documentation/devicetree/bindings/mfd/max8998.txt diff --git a/Documentation/devicetree/bindings/mfd/max8998.txt b/Documentation/devicetree/bindings/mfd/max8998.txt new file mode 100644 index 000000000000..23a3650ff2a2 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/max8998.txt @@ -0,0 +1,119 @@ +* Maxim MAX8998, National/TI LP3974 multi-function device + +The Maxim MAX8998 is a multi-function device which includes voltage/current +regulators, real time clock, battery charging controller and several +other sub-blocks. It is interfaced using an I2C interface. Each sub-block +is addressed by the host system using different i2c slave address. + +PMIC sub-block +-------------- + +The PMIC sub-block contains a number of voltage and current regulators, +with controllable parameters and dynamic voltage scaling capability. +In addition, it includes a real time clock and battery charging controller +as well. It is accessible at I2C address 0x66. + +Required properties: +- compatible: Should be one of the following: + - "maxim,max8998" for Maxim MAX8998 + - "national,lp3974" or "ti,lp3974" for National/TI LP3974. +- reg: Specifies the i2c slave address of the pmic block. It should be 0x66. + +Optional properties: +- interrupt-parent: Specifies the phandle of the interrupt controller to which + the interrupts from MAX8998 are routed to. +- interrupts: Interrupt specifiers for two interrupt sources. + - First interrupt specifier is for main interrupt. + - Second interrupt specifier is for power-on/-off interrupt. +- max8998,pmic-buck1-dvs-gpios: GPIO specifiers for two host gpios used + for buck 1 dvs. The format of the gpio specifier depends on the gpio + controller. +- max8998,pmic-buck2-dvs-gpio: GPIO specifier for host gpio used + for buck 2 dvs. The format of the gpio specifier depends on the gpio + controller. +- max8998,pmic-buck1-default-dvs-idx: Default voltage setting selected from + the possible 4 options selectable by the dvs gpios. The value of this + property should be 0, 1, 2 or 3. If not specified or out of range, + a default value of 0 is taken. +- max8998,pmic-buck2-default-dvs-idx: Default voltage setting selected from + the possible 2 options selectable by the dvs gpios. The value of this + property should be 0 or 1. If not specified or out of range, a default + value of 0 is taken. +- max8998,pmic-buck-voltage-lock: If present, disallows changing of + preprogrammed buck dvfs voltages. + +Additional properties required if max8998,pmic-buck1-dvs-gpios is defined: +- max8998,pmic-buck1-dvs-voltage: An array of 4 voltage values in microvolts + for buck1 regulator that can be selected using dvs gpio. + +Additional properties required if max8998,pmic-buck2-dvs-gpio is defined: +- max8998,pmic-buck2-dvs-voltage: An array of 2 voltage values in microvolts + for buck2 regulator that can be selected using dvs gpio. + +Regulators: All the regulators of MAX8998 to be instantiated shall be +listed in a child node named 'regulators'. Each regulator is represented +by a child node of the 'regulators' node. + + regulator-name { + /* standard regulator bindings here */ + }; + +Following regulators of the MAX8998 PMIC block are supported. Note that +the 'n' in regulator name, as in LDOn or BUCKn, represents the LDO or BUCK +number as described in MAX8998 datasheet. + + - LDOn + - valid values for n are 2 to 17 + - Example: LDO2, LDO10, LDO17 + - BUCKn + - valid values for n are 1 to 4. + - Example: BUCK1, BUCK2, BUCK3, BUCK4 + + - ENVICHG: Battery Charging Current Monitor Output. This is a fixed + voltage type regulator + + - ESAFEOUT1: (ldo19) + - ESAFEOUT2: (ld020) + +Standard regulator bindings are used inside regulator subnodes. Check + Documentation/devicetree/bindings/regulator/regulator.txt +for more details. + +Example: + + pmic@66 { + compatible = "maxim,max8998-pmic"; + reg = <0x66>; + interrupt-parent = <&wakeup_eint>; + interrupts = <4 0>, <3 0>; + + /* Buck 1 DVS settings */ + max8998,pmic-buck1-default-dvs-idx = <0>; + max8998,pmic-buck1-dvs-gpios = <&gpx0 0 1 0 0>, /* SET1 */ + <&gpx0 1 1 0 0>; /* SET2 */ + max8998,pmic-buck1-dvs-voltage = <1350000>, <1300000>, + <1000000>, <950000>; + + /* Buck 2 DVS settings */ + max8998,pmic-buck2-default-dvs-idx = <0>; + max8998,pmic-buck2-dvs-gpio = <&gpx0 0 3 0 0>; /* SET3 */ + max8998,pmic-buck2-dvs-voltage = <1350000>, <1300000>; + + /* Regulators to instantiate */ + regulators { + ldo2_reg: LDO2 { + regulator-name = "VDD_ALIVE_1.1V"; + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1100000>; + regulator-always-on; + }; + + buck1_reg: BUCK1 { + regulator-name = "VDD_ARM_1.2V"; + regulator-min-microvolt = <950000>; + regulator-max-microvolt = <1350000>; + regulator-always-on; + regulator-boot-on; + }; + }; + }; diff --git a/drivers/mfd/max8998.c b/drivers/mfd/max8998.c index d7218cc90945..21af51a499f4 100644 --- a/drivers/mfd/max8998.c +++ b/drivers/mfd/max8998.c @@ -20,12 +20,15 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include #include #include #include #include #include #include +#include +#include #include #include #include @@ -128,6 +131,56 @@ int max8998_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask) } EXPORT_SYMBOL(max8998_update_reg); +#ifdef CONFIG_OF +static struct of_device_id max8998_dt_match[] = { + { .compatible = "maxim,max8998", .data = (void *)TYPE_MAX8998 }, + { .compatible = "national,lp3974", .data = (void *)TYPE_LP3974 }, + { .compatible = "ti,lp3974", .data = (void *)TYPE_LP3974 }, + {}, +}; +MODULE_DEVICE_TABLE(of, max8998_dt_match); +#endif + +/* + * Only the common platform data elements for max8998 are parsed here from the + * device tree. Other sub-modules of max8998 such as pmic, rtc and others have + * to parse their own platform data elements from device tree. + * + * The max8998 platform data structure is instantiated here and the drivers for + * the sub-modules need not instantiate another instance while parsing their + * platform data. + */ +static struct max8998_platform_data *max8998_i2c_parse_dt_pdata( + struct device *dev) +{ + struct max8998_platform_data *pd; + + pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); + if (!pd) + return ERR_PTR(-ENOMEM); + + pd->ono = irq_of_parse_and_map(dev->of_node, 1); + + /* + * ToDo: the 'wakeup' member in the platform data is more of a linux + * specfic information. Hence, there is no binding for that yet and + * not parsed here. + */ + return pd; +} + +static inline int max8998_i2c_get_driver_data(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + if (IS_ENABLED(CONFIG_OF) && i2c->dev.of_node) { + const struct of_device_id *match; + match = of_match_node(max8998_dt_match, i2c->dev.of_node); + return (int)match->data; + } + + return (int)id->driver_data; +} + static int max8998_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -139,11 +192,20 @@ static int max8998_i2c_probe(struct i2c_client *i2c, if (max8998 == NULL) return -ENOMEM; + if (IS_ENABLED(CONFIG_OF) && i2c->dev.of_node) { + pdata = max8998_i2c_parse_dt_pdata(&i2c->dev); + if (IS_ERR(pdata)) { + ret = PTR_ERR(pdata); + goto err; + } + } + i2c_set_clientdata(i2c, max8998); max8998->dev = &i2c->dev; max8998->i2c = i2c; max8998->irq = i2c->irq; - max8998->type = id->driver_data; + max8998->type = max8998_i2c_get_driver_data(i2c, id); + max8998->pdata = pdata; if (pdata) { max8998->ono = pdata->ono; max8998->irq_base = pdata->irq_base; @@ -158,7 +220,7 @@ static int max8998_i2c_probe(struct i2c_client *i2c, pm_runtime_set_active(max8998->dev); - switch (id->driver_data) { + switch (max8998->type) { case TYPE_LP3974: ret = mfd_add_devices(max8998->dev, -1, lp3974_devs, ARRAY_SIZE(lp3974_devs), @@ -314,6 +376,7 @@ static struct i2c_driver max8998_i2c_driver = { .name = "max8998", .owner = THIS_MODULE, .pm = &max8998_pm, + .of_match_table = of_match_ptr(max8998_dt_match), }, .probe = max8998_i2c_probe, .remove = max8998_i2c_remove, diff --git a/drivers/regulator/max8998.c b/drivers/regulator/max8998.c index 8c45b93b7334..a4c53b2d1aaf 100644 --- a/drivers/regulator/max8998.c +++ b/drivers/regulator/max8998.c @@ -28,8 +28,11 @@ #include #include #include +#include +#include #include #include +#include #include #include @@ -589,13 +592,13 @@ static struct regulator_desc regulators[] = { .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, }, { - .name = "EN32KHz AP", + .name = "EN32KHz-AP", .id = MAX8998_EN32KHZ_AP, .ops = &max8998_others_ops, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, }, { - .name = "EN32KHz CP", + .name = "EN32KHz-CP", .id = MAX8998_EN32KHZ_CP, .ops = &max8998_others_ops, .type = REGULATOR_VOLTAGE, @@ -621,10 +624,122 @@ static struct regulator_desc regulators[] = { } }; +static int max8998_pmic_dt_parse_dvs_gpio(struct max8998_dev *iodev, + struct max8998_platform_data *pdata, + struct device_node *pmic_np) +{ + int gpio; + + gpio = of_get_named_gpio(pmic_np, "max8998,pmic-buck1-dvs-gpios", 0); + if (!gpio_is_valid(gpio)) { + dev_err(iodev->dev, "invalid buck1 gpio[0]: %d\n", gpio); + return -EINVAL; + } + pdata->buck1_set1 = gpio; + + gpio = of_get_named_gpio(pmic_np, "max8998,pmic-buck1-dvs-gpios", 1); + if (!gpio_is_valid(gpio)) { + dev_err(iodev->dev, "invalid buck1 gpio[1]: %d\n", gpio); + return -EINVAL; + } + pdata->buck1_set2 = gpio; + + gpio = of_get_named_gpio(pmic_np, "max8998,pmic-buck2-dvs-gpio", 0); + if (!gpio_is_valid(gpio)) { + dev_err(iodev->dev, "invalid buck 2 gpio: %d\n", gpio); + return -EINVAL; + } + pdata->buck2_set3 = gpio; + + return 0; +} + +static int max8998_pmic_dt_parse_pdata(struct max8998_dev *iodev, + struct max8998_platform_data *pdata) +{ + struct device_node *pmic_np = iodev->dev->of_node; + struct device_node *regulators_np, *reg_np; + struct max8998_regulator_data *rdata; + unsigned int i; + int ret; + + regulators_np = of_get_child_by_name(pmic_np, "regulators"); + if (!regulators_np) { + dev_err(iodev->dev, "could not find regulators sub-node\n"); + return -EINVAL; + } + + /* count the number of regulators to be supported in pmic */ + pdata->num_regulators = of_get_child_count(regulators_np); + + rdata = devm_kzalloc(iodev->dev, sizeof(*rdata) * + pdata->num_regulators, GFP_KERNEL); + if (!rdata) + return -ENOMEM; + + pdata->regulators = rdata; + for (i = 0; i < ARRAY_SIZE(regulators); ++i) { + reg_np = of_get_child_by_name(regulators_np, + regulators[i].name); + if (!reg_np) + continue; + + rdata->id = regulators[i].id; + rdata->initdata = of_get_regulator_init_data( + iodev->dev, reg_np); + rdata->reg_node = reg_np; + ++rdata; + } + pdata->num_regulators = rdata - pdata->regulators; + + ret = max8998_pmic_dt_parse_dvs_gpio(iodev, pdata, pmic_np); + if (ret) + return -EINVAL; + + if (of_find_property(pmic_np, "max8998,pmic-buck-voltage-lock", NULL)) + pdata->buck_voltage_lock = true; + + ret = of_property_read_u32(pmic_np, + "max8998,pmic-buck1-default-dvs-idx", + &pdata->buck1_default_idx); + if (!ret && pdata->buck1_default_idx >= 4) { + pdata->buck1_default_idx = 0; + dev_warn(iodev->dev, "invalid value for default dvs index, using 0 instead\n"); + } + + ret = of_property_read_u32(pmic_np, + "max8998,pmic-buck2-default-dvs-idx", + &pdata->buck2_default_idx); + if (!ret && pdata->buck2_default_idx >= 2) { + pdata->buck2_default_idx = 0; + dev_warn(iodev->dev, "invalid value for default dvs index, using 0 instead\n"); + } + + ret = of_property_read_u32_array(pmic_np, + "max8998,pmic-buck1-dvs-voltage", + pdata->buck1_voltage, + ARRAY_SIZE(pdata->buck1_voltage)); + if (ret) { + dev_err(iodev->dev, "buck1 voltages not specified\n"); + return -EINVAL; + } + + ret = of_property_read_u32_array(pmic_np, + "max8998,pmic-buck2-dvs-voltage", + pdata->buck2_voltage, + ARRAY_SIZE(pdata->buck2_voltage)); + if (ret) { + dev_err(iodev->dev, "buck2 voltages not specified\n"); + return -EINVAL; + } + + return 0; +} + static int max8998_pmic_probe(struct platform_device *pdev) { struct max8998_dev *iodev = dev_get_drvdata(pdev->dev.parent); - struct max8998_platform_data *pdata = dev_get_platdata(iodev->dev); + struct max8998_platform_data *pdata = iodev->pdata; struct regulator_config config = { }; struct regulator_dev **rdev; struct max8998_data *max8998; @@ -637,6 +752,12 @@ static int max8998_pmic_probe(struct platform_device *pdev) return -ENODEV; } + if (IS_ENABLED(CONFIG_OF) && iodev->dev->of_node) { + ret = max8998_pmic_dt_parse_pdata(iodev, pdata); + if (ret) + return ret; + } + max8998 = devm_kzalloc(&pdev->dev, sizeof(struct max8998_data), GFP_KERNEL); if (!max8998) @@ -750,13 +871,15 @@ static int max8998_pmic_probe(struct platform_device *pdev) } config.dev = max8998->dev; + config.of_node = pdata->regulators[i].reg_node; config.init_data = pdata->regulators[i].initdata; config.driver_data = max8998; rdev[i] = regulator_register(®ulators[index], &config); if (IS_ERR(rdev[i])) { ret = PTR_ERR(rdev[i]); - dev_err(max8998->dev, "regulator init failed\n"); + dev_err(max8998->dev, "regulator %s init failed (%d)\n", + regulators[index].name, ret); rdev[i] = NULL; goto err; } diff --git a/drivers/rtc/rtc-max8998.c b/drivers/rtc/rtc-max8998.c index 46f23014759b..042a8734bd28 100644 --- a/drivers/rtc/rtc-max8998.c +++ b/drivers/rtc/rtc-max8998.c @@ -253,7 +253,7 @@ static const struct rtc_class_ops max8998_rtc_ops = { static int max8998_rtc_probe(struct platform_device *pdev) { struct max8998_dev *max8998 = dev_get_drvdata(pdev->dev.parent); - struct max8998_platform_data *pdata = dev_get_platdata(max8998->dev); + struct max8998_platform_data *pdata = max8998->pdata; struct max8998_rtc_info *info; int ret; diff --git a/include/linux/mfd/max8998-private.h b/include/linux/mfd/max8998-private.h index bfb48b6fcc74..84844e0a5704 100644 --- a/include/linux/mfd/max8998-private.h +++ b/include/linux/mfd/max8998-private.h @@ -137,6 +137,7 @@ struct irq_domain; /** * struct max8998_dev - max8998 master device for sub-drivers * @dev: master device of the chip (can be used to access platform data) + * @pdata: platform data for the driver and subdrivers * @i2c: i2c client private data for regulator * @rtc: i2c client private data for rtc * @iolock: mutex for serializing io access @@ -150,6 +151,7 @@ struct irq_domain; */ struct max8998_dev { struct device *dev; + struct max8998_platform_data *pdata; struct i2c_client *i2c; struct i2c_client *rtc; struct mutex iolock; diff --git a/include/linux/mfd/max8998.h b/include/linux/mfd/max8998.h index ca56bb03bc69..e3956a654cbc 100644 --- a/include/linux/mfd/max8998.h +++ b/include/linux/mfd/max8998.h @@ -58,10 +58,12 @@ enum { * max8998_regulator_data - regulator data * @id: regulator id * @initdata: regulator init data (contraints, supplies, ...) + * @reg_node: DT node of regulator (unused on non-DT platforms) */ struct max8998_regulator_data { int id; struct regulator_init_data *initdata; + struct device_node *reg_node; }; /** -- cgit v1.2.3 From e51c288ea15b13c7d9982251c8397853f7cfb18f Mon Sep 17 00:00:00 2001 From: Kevin Strasser Date: Sun, 23 Jun 2013 21:00:06 -0700 Subject: watchdog: Kontron PLD watchdog timer driver Add watchdog timer support for the on-board PLD found on some Kontron embedded modules. Originally-From: Michael Brunner Signed-off-by: Kevin Strasser Acked-by: Guenter Roeck Acked-by: Darren Hart Acked-by: Wim Van Sebroeck Signed-off-by: Samuel Ortiz --- drivers/watchdog/Kconfig | 11 + drivers/watchdog/Makefile | 1 + drivers/watchdog/kempld_wdt.c | 581 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 593 insertions(+) create mode 100644 drivers/watchdog/kempld_wdt.c diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index e89fc3133972..7460d349df59 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -687,6 +687,17 @@ config HP_WATCHDOG To compile this driver as a module, choose M here: the module will be called hpwdt. +config KEMPLD_WDT + tristate "Kontron COM Watchdog Timer" + depends on MFD_KEMPLD + select WATCHDOG_CORE + help + Support for the PLD watchdog on some Kontron ETX and COMexpress + (ETXexpress) modules + + This driver can also be built as a module. If so, the module will be + called kempld_wdt. + config HPWDT_NMI_DECODING bool "NMI decoding support for the HP ProLiant iLO2+ Hardware Watchdog Timer" depends on HP_WATCHDOG diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index a300b948f254..ec268995b261 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -90,6 +90,7 @@ endif obj-$(CONFIG_IT8712F_WDT) += it8712f_wdt.o obj-$(CONFIG_IT87_WDT) += it87_wdt.o obj-$(CONFIG_HP_WATCHDOG) += hpwdt.o +obj-$(CONFIG_KEMPLD_WDT) += kempld_wdt.o obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o obj-$(CONFIG_PC87413_WDT) += pc87413_wdt.o diff --git a/drivers/watchdog/kempld_wdt.c b/drivers/watchdog/kempld_wdt.c new file mode 100644 index 000000000000..491419e0772a --- /dev/null +++ b/drivers/watchdog/kempld_wdt.c @@ -0,0 +1,581 @@ +/* + * Kontron PLD watchdog driver + * + * Copyright (c) 2010-2013 Kontron Europe GmbH + * Author: Michael Brunner + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License 2 as published + * by the Free Software Foundation. + * + * 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. + * + * Note: From the PLD watchdog point of view timeout and pretimeout are + * defined differently than in the kernel. + * First the pretimeout stage runs out before the timeout stage gets + * active. + * + * Kernel/API: P-----| pretimeout + * |-----------------------T timeout + * Watchdog: |-----------------P pretimeout_stage + * |-----T timeout_stage + */ + +#include +#include +#include +#include +#include +#include +#include + +#define KEMPLD_WDT_STAGE_TIMEOUT(x) (0x1b + (x) * 4) +#define KEMPLD_WDT_STAGE_CFG(x) (0x18 + (x)) +#define STAGE_CFG_GET_PRESCALER(x) (((x) & 0x30) >> 4) +#define STAGE_CFG_SET_PRESCALER(x) (((x) & 0x30) << 4) +#define STAGE_CFG_PRESCALER_MASK 0x30 +#define STAGE_CFG_ACTION_MASK 0x7 +#define STAGE_CFG_ASSERT (1 << 3) + +#define KEMPLD_WDT_MAX_STAGES 2 +#define KEMPLD_WDT_KICK 0x16 +#define KEMPLD_WDT_CFG 0x17 +#define KEMPLD_WDT_CFG_ENABLE 0x10 +#define KEMPLD_WDT_CFG_ENABLE_LOCK 0x8 +#define KEMPLD_WDT_CFG_GLOBAL_LOCK 0x80 + +enum { + ACTION_NONE = 0, + ACTION_RESET, + ACTION_NMI, + ACTION_SMI, + ACTION_SCI, + ACTION_DELAY, +}; + +enum { + STAGE_TIMEOUT = 0, + STAGE_PRETIMEOUT, +}; + +enum { + PRESCALER_21 = 0, + PRESCALER_17, + PRESCALER_12, +}; + +const u32 kempld_prescaler[] = { + [PRESCALER_21] = (1 << 21) - 1, + [PRESCALER_17] = (1 << 17) - 1, + [PRESCALER_12] = (1 << 12) - 1, + 0, +}; + +struct kempld_wdt_stage { + unsigned int id; + u32 mask; +}; + +struct kempld_wdt_data { + struct kempld_device_data *pld; + struct watchdog_device wdd; + unsigned int pretimeout; + struct kempld_wdt_stage stage[KEMPLD_WDT_MAX_STAGES]; +#ifdef CONFIG_PM + u8 pm_status_store; +#endif +}; + +#define DEFAULT_TIMEOUT 30 /* seconds */ +#define DEFAULT_PRETIMEOUT 0 + +static unsigned int timeout = DEFAULT_TIMEOUT; +module_param(timeout, uint, 0); +MODULE_PARM_DESC(timeout, + "Watchdog timeout in seconds. (>=0, default=" + __MODULE_STRING(DEFAULT_TIMEOUT) ")"); + +static unsigned int pretimeout = DEFAULT_PRETIMEOUT; +module_param(pretimeout, uint, 0); +MODULE_PARM_DESC(pretimeout, + "Watchdog pretimeout in seconds. (>=0, default=" + __MODULE_STRING(DEFAULT_PRETIMEOUT) ")"); + +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, + "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +static int kempld_wdt_set_stage_action(struct kempld_wdt_data *wdt_data, + struct kempld_wdt_stage *stage, + u8 action) +{ + struct kempld_device_data *pld = wdt_data->pld; + u8 stage_cfg; + + if (!stage || !stage->mask) + return -EINVAL; + + kempld_get_mutex(pld); + stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->id)); + stage_cfg &= ~STAGE_CFG_ACTION_MASK; + stage_cfg |= (action & STAGE_CFG_ACTION_MASK); + + if (action == ACTION_RESET) + stage_cfg |= STAGE_CFG_ASSERT; + else + stage_cfg &= ~STAGE_CFG_ASSERT; + + kempld_write8(pld, KEMPLD_WDT_STAGE_CFG(stage->id), stage_cfg); + kempld_release_mutex(pld); + + return 0; +} + +static int kempld_wdt_set_stage_timeout(struct kempld_wdt_data *wdt_data, + struct kempld_wdt_stage *stage, + unsigned int timeout) +{ + struct kempld_device_data *pld = wdt_data->pld; + u32 prescaler = kempld_prescaler[PRESCALER_21]; + u64 stage_timeout64; + u32 stage_timeout; + u32 remainder; + u8 stage_cfg; + + if (!stage) + return -EINVAL; + + stage_timeout64 = (u64)timeout * pld->pld_clock; + remainder = do_div(stage_timeout64, prescaler); + if (remainder) + stage_timeout64++; + + if (stage_timeout64 > stage->mask) + return -EINVAL; + + stage_timeout = stage_timeout64 & stage->mask; + + kempld_get_mutex(pld); + stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->id)); + stage_cfg &= ~STAGE_CFG_PRESCALER_MASK; + stage_cfg |= STAGE_CFG_SET_PRESCALER(prescaler); + kempld_write8(pld, KEMPLD_WDT_STAGE_CFG(stage->id), stage_cfg); + kempld_write32(pld, KEMPLD_WDT_STAGE_TIMEOUT(stage->id), + stage_timeout); + kempld_release_mutex(pld); + + return 0; +} + +/* + * kempld_get_mutex must be called prior to calling this function. + */ +static unsigned int kempld_wdt_get_timeout(struct kempld_wdt_data *wdt_data, + struct kempld_wdt_stage *stage) +{ + struct kempld_device_data *pld = wdt_data->pld; + unsigned int timeout; + u64 stage_timeout; + u32 prescaler; + u32 remainder; + u8 stage_cfg; + + if (!stage->mask) + return 0; + + stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->id)); + stage_timeout = kempld_read32(pld, KEMPLD_WDT_STAGE_TIMEOUT(stage->id)); + prescaler = kempld_prescaler[STAGE_CFG_GET_PRESCALER(stage_cfg)]; + + stage_timeout = (stage_timeout & stage->mask) * prescaler; + remainder = do_div(stage_timeout, pld->pld_clock); + if (remainder) + stage_timeout++; + + timeout = stage_timeout; + WARN_ON_ONCE(timeout != stage_timeout); + + return timeout; +} + +static int kempld_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd); + struct kempld_wdt_stage *pretimeout_stage; + struct kempld_wdt_stage *timeout_stage; + int ret; + + timeout_stage = &wdt_data->stage[STAGE_TIMEOUT]; + pretimeout_stage = &wdt_data->stage[STAGE_PRETIMEOUT]; + + if (pretimeout_stage->mask && wdt_data->pretimeout > 0) + timeout = wdt_data->pretimeout; + + ret = kempld_wdt_set_stage_action(wdt_data, timeout_stage, + ACTION_RESET); + if (ret) + return ret; + ret = kempld_wdt_set_stage_timeout(wdt_data, timeout_stage, + timeout); + if (ret) + return ret; + + wdd->timeout = timeout; + return 0; +} + +static int kempld_wdt_set_pretimeout(struct watchdog_device *wdd, + unsigned int pretimeout) +{ + struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd); + struct kempld_wdt_stage *pretimeout_stage; + u8 action = ACTION_NONE; + int ret; + + pretimeout_stage = &wdt_data->stage[STAGE_PRETIMEOUT]; + + if (!pretimeout_stage->mask) + return -ENXIO; + + if (pretimeout > wdd->timeout) + return -EINVAL; + + if (pretimeout > 0) + action = ACTION_NMI; + + ret = kempld_wdt_set_stage_action(wdt_data, pretimeout_stage, + action); + if (ret) + return ret; + ret = kempld_wdt_set_stage_timeout(wdt_data, pretimeout_stage, + wdd->timeout - pretimeout); + if (ret) + return ret; + + wdt_data->pretimeout = pretimeout; + return 0; +} + +static void kempld_wdt_update_timeouts(struct kempld_wdt_data *wdt_data) +{ + struct kempld_device_data *pld = wdt_data->pld; + struct kempld_wdt_stage *pretimeout_stage; + struct kempld_wdt_stage *timeout_stage; + unsigned int pretimeout, timeout; + + pretimeout_stage = &wdt_data->stage[STAGE_PRETIMEOUT]; + timeout_stage = &wdt_data->stage[STAGE_TIMEOUT]; + + kempld_get_mutex(pld); + pretimeout = kempld_wdt_get_timeout(wdt_data, pretimeout_stage); + timeout = kempld_wdt_get_timeout(wdt_data, timeout_stage); + kempld_release_mutex(pld); + + if (pretimeout) + wdt_data->pretimeout = timeout; + else + wdt_data->pretimeout = 0; + + wdt_data->wdd.timeout = pretimeout + timeout; +} + +static int kempld_wdt_start(struct watchdog_device *wdd) +{ + struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd); + struct kempld_device_data *pld = wdt_data->pld; + u8 status; + int ret; + + ret = kempld_wdt_set_timeout(wdd, wdd->timeout); + if (ret) + return ret; + + kempld_get_mutex(pld); + status = kempld_read8(pld, KEMPLD_WDT_CFG); + status |= KEMPLD_WDT_CFG_ENABLE; + kempld_write8(pld, KEMPLD_WDT_CFG, status); + status = kempld_read8(pld, KEMPLD_WDT_CFG); + kempld_release_mutex(pld); + + /* Check if the watchdog was enabled */ + if (!(status & KEMPLD_WDT_CFG_ENABLE)) + return -EACCES; + + return 0; +} + +static int kempld_wdt_stop(struct watchdog_device *wdd) +{ + struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd); + struct kempld_device_data *pld = wdt_data->pld; + u8 status; + + kempld_get_mutex(pld); + status = kempld_read8(pld, KEMPLD_WDT_CFG); + status &= ~KEMPLD_WDT_CFG_ENABLE; + kempld_write8(pld, KEMPLD_WDT_CFG, status); + status = kempld_read8(pld, KEMPLD_WDT_CFG); + kempld_release_mutex(pld); + + /* Check if the watchdog was disabled */ + if (status & KEMPLD_WDT_CFG_ENABLE) + return -EACCES; + + return 0; +} + +static int kempld_wdt_keepalive(struct watchdog_device *wdd) +{ + struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd); + struct kempld_device_data *pld = wdt_data->pld; + + kempld_get_mutex(pld); + kempld_write8(pld, KEMPLD_WDT_KICK, 'K'); + kempld_release_mutex(pld); + + return 0; +} + +static long kempld_wdt_ioctl(struct watchdog_device *wdd, unsigned int cmd, + unsigned long arg) +{ + struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd); + void __user *argp = (void __user *)arg; + int ret = -ENOIOCTLCMD; + int __user *p = argp; + int new_value; + + switch (cmd) { + case WDIOC_SETPRETIMEOUT: + if (get_user(new_value, p)) + return -EFAULT; + ret = kempld_wdt_set_pretimeout(wdd, new_value); + if (ret) + return ret; + ret = kempld_wdt_keepalive(wdd); + break; + case WDIOC_GETPRETIMEOUT: + ret = put_user(wdt_data->pretimeout, (int *)arg); + break; + } + + return ret; +} + +static int kempld_wdt_probe_stages(struct watchdog_device *wdd) +{ + struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd); + struct kempld_device_data *pld = wdt_data->pld; + struct kempld_wdt_stage *pretimeout_stage; + struct kempld_wdt_stage *timeout_stage; + u8 index, data, data_orig; + u32 mask; + int i, j; + + pretimeout_stage = &wdt_data->stage[STAGE_PRETIMEOUT]; + timeout_stage = &wdt_data->stage[STAGE_TIMEOUT]; + + pretimeout_stage->mask = 0; + timeout_stage->mask = 0; + + for (i = 0; i < 3; i++) { + index = KEMPLD_WDT_STAGE_TIMEOUT(i); + mask = 0; + + kempld_get_mutex(pld); + /* Probe each byte individually. */ + for (j = 0; j < 4; j++) { + data_orig = kempld_read8(pld, index + j); + kempld_write8(pld, index + j, 0x00); + data = kempld_read8(pld, index + j); + /* A failed write means this byte is reserved */ + if (data != 0x00) + break; + kempld_write8(pld, index + j, data_orig); + mask |= 0xff << (j * 8); + } + kempld_release_mutex(pld); + + /* Assign available stages to timeout and pretimeout */ + if (!timeout_stage->mask) { + timeout_stage->mask = mask; + timeout_stage->id = i; + } else { + if (pld->feature_mask & KEMPLD_FEATURE_BIT_NMI) { + pretimeout_stage->mask = timeout_stage->mask; + timeout_stage->mask = mask; + pretimeout_stage->id = timeout_stage->id; + timeout_stage->id = i; + } + break; + } + } + + if (!timeout_stage->mask) + return -ENODEV; + + return 0; +} + +static struct watchdog_info kempld_wdt_info = { + .identity = "KEMPLD Watchdog", + .options = WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE | + WDIOF_PRETIMEOUT +}; + +static struct watchdog_ops kempld_wdt_ops = { + .owner = THIS_MODULE, + .start = kempld_wdt_start, + .stop = kempld_wdt_stop, + .ping = kempld_wdt_keepalive, + .set_timeout = kempld_wdt_set_timeout, + .ioctl = kempld_wdt_ioctl, +}; + +static int kempld_wdt_probe(struct platform_device *pdev) +{ + struct kempld_device_data *pld = dev_get_drvdata(pdev->dev.parent); + struct kempld_wdt_data *wdt_data; + struct device *dev = &pdev->dev; + struct watchdog_device *wdd; + u8 status; + int ret = 0; + + wdt_data = devm_kzalloc(dev, sizeof(*wdt_data), GFP_KERNEL); + if (!wdt_data) + return -ENOMEM; + + wdt_data->pld = pld; + wdd = &wdt_data->wdd; + wdd->parent = dev; + + kempld_get_mutex(pld); + status = kempld_read8(pld, KEMPLD_WDT_CFG); + kempld_release_mutex(pld); + + /* Enable nowayout if watchdog is already locked */ + if (status & (KEMPLD_WDT_CFG_ENABLE_LOCK | + KEMPLD_WDT_CFG_GLOBAL_LOCK)) { + if (!nowayout) + dev_warn(dev, + "Forcing nowayout - watchdog lock enabled!\n"); + nowayout = true; + } + + wdd->info = &kempld_wdt_info; + wdd->ops = &kempld_wdt_ops; + + watchdog_set_drvdata(wdd, wdt_data); + watchdog_set_nowayout(wdd, nowayout); + + ret = kempld_wdt_probe_stages(wdd); + if (ret) + return ret; + + kempld_wdt_set_timeout(wdd, timeout); + kempld_wdt_set_pretimeout(wdd, pretimeout); + + /* Check if watchdog is already enabled */ + if (status & KEMPLD_WDT_CFG_ENABLE) { + /* Get current watchdog settings */ + kempld_wdt_update_timeouts(wdt_data); + dev_info(dev, "Watchdog was already enabled\n"); + } + + platform_set_drvdata(pdev, wdt_data); + ret = watchdog_register_device(wdd); + if (ret) + return ret; + + dev_info(dev, "Watchdog registered with %ds timeout\n", wdd->timeout); + + return 0; +} + +static void kempld_wdt_shutdown(struct platform_device *pdev) +{ + struct kempld_wdt_data *wdt_data = platform_get_drvdata(pdev); + + kempld_wdt_stop(&wdt_data->wdd); +} + +static int kempld_wdt_remove(struct platform_device *pdev) +{ + struct kempld_wdt_data *wdt_data = platform_get_drvdata(pdev); + struct watchdog_device *wdd = &wdt_data->wdd; + int ret = 0; + + if (!nowayout) + ret = kempld_wdt_stop(wdd); + watchdog_unregister_device(wdd); + + return ret; +} + +#ifdef CONFIG_PM +/* Disable watchdog if it is active during suspend */ +static int kempld_wdt_suspend(struct platform_device *pdev, + pm_message_t message) +{ + struct kempld_wdt_data *wdt_data = platform_get_drvdata(pdev); + struct kempld_device_data *pld = wdt_data->pld; + struct watchdog_device *wdd = &wdt_data->wdd; + + kempld_get_mutex(pld); + wdt_data->pm_status_store = kempld_read8(pld, KEMPLD_WDT_CFG); + kempld_release_mutex(pld); + + kempld_wdt_update_timeouts(wdt_data); + + if (wdt_data->pm_status_store & KEMPLD_WDT_CFG_ENABLE) + return kempld_wdt_stop(wdd); + + return 0; +} + +/* Enable watchdog and configure it if necessary */ +static int kempld_wdt_resume(struct platform_device *pdev) +{ + struct kempld_wdt_data *wdt_data = platform_get_drvdata(pdev); + struct watchdog_device *wdd = &wdt_data->wdd; + + /* + * If watchdog was stopped before suspend be sure it gets disabled + * again, for the case BIOS has enabled it during resume + */ + if (wdt_data->pm_status_store & KEMPLD_WDT_CFG_ENABLE) + return kempld_wdt_start(wdd); + else + return kempld_wdt_stop(wdd); +} +#else +#define kempld_wdt_suspend NULL +#define kempld_wdt_resume NULL +#endif + +static struct platform_driver kempld_wdt_driver = { + .driver = { + .name = "kempld-wdt", + .owner = THIS_MODULE, + }, + .probe = kempld_wdt_probe, + .remove = kempld_wdt_remove, + .shutdown = kempld_wdt_shutdown, + .suspend = kempld_wdt_suspend, + .resume = kempld_wdt_resume, +}; + +module_platform_driver(kempld_wdt_driver); + +MODULE_DESCRIPTION("KEM PLD Watchdog Driver"); +MODULE_AUTHOR("Michael Brunner "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); -- cgit v1.2.3 From dc6641822e412ff527e022ec752707ebb034add4 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 28 Jun 2013 15:12:23 +0100 Subject: mfd: sec: Remove fields not used since regmap conversion These were all used by the open coded I/O and IRQ implementations and are no longer referenced now that the regmap core variants are used instead. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- include/linux/mfd/samsung/core.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/include/linux/mfd/samsung/core.h b/include/linux/mfd/samsung/core.h index f0f4de3b4ccc..378ae8a04c6a 100644 --- a/include/linux/mfd/samsung/core.h +++ b/include/linux/mfd/samsung/core.h @@ -14,8 +14,6 @@ #ifndef __LINUX_MFD_SEC_CORE_H #define __LINUX_MFD_SEC_CORE_H -#define NUM_IRQ_REGS 4 - enum sec_device_type { S5M8751X, S5M8763X, @@ -44,8 +42,6 @@ struct sec_pmic_dev { struct regmap *regmap; struct i2c_client *i2c; struct i2c_client *rtc; - struct mutex iolock; - struct mutex irqlock; int device_type; int irq_base; @@ -53,8 +49,6 @@ struct sec_pmic_dev { struct regmap_irq_chip_data *irq_data; int ono; - u8 irq_masks_cur[NUM_IRQ_REGS]; - u8 irq_masks_cache[NUM_IRQ_REGS]; int type; bool wakeup; }; -- cgit v1.2.3 From 55b5940df9cefcd5cf1ba4b4f538b303b725b546 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 28 Jun 2013 15:14:11 +0100 Subject: MAINTAINERS: Add include directory to MFD file patterns Ensure that get_maintainer.pl does the right thing for header only changes. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 39fd4e794b1e..3d064ab5bc1a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5437,6 +5437,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-next.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-fixes.git S: Supported F: drivers/mfd/ +F: include/linux/mfd/ MULTIMEDIA CARD (MMC), SECURE DIGITAL (SD) AND SDIO SUBSYSTEM M: Chris Ball -- cgit v1.2.3 From f51e1eb63d9c28cec188337ee656a13be6980cfd Mon Sep 17 00:00:00 2001 From: "Srivatsa S. Bhat" Date: Mon, 1 Jul 2013 00:40:55 +0200 Subject: cpufreq: Fix cpufreq regression after suspend/resume MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Toralf Förster reported that the cpufreq ondemand governor behaves erratically (doesn't scale well) after a suspend/resume cycle. The problem was that the cpufreq subsystem's idea of the cpu frequencies differed from the actual frequencies set in the hardware after a suspend/resume cycle. Toralf bisected the problem to commit a66b2e5 (cpufreq: Preserve sysfs files across suspend/resume). Among other (harmless) things, that commit skipped the call to cpufreq_update_policy() in the resume path. But cpufreq_update_policy() plays an important role during resume, because it is responsible for checking if the BIOS changed the cpu frequencies behind our back and resynchronize the cpufreq subsystem's knowledge of the cpu frequencies, and update them accordingly. So, restore the call to cpufreq_update_policy() in the resume path to fix the cpufreq regression. Reported-and-tested-by: Toralf Förster Signed-off-by: Srivatsa S. Bhat Cc: 3.10+ Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq_stats.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c index 6d35caa91167..cd9e81713a71 100644 --- a/drivers/cpufreq/cpufreq_stats.c +++ b/drivers/cpufreq/cpufreq_stats.c @@ -349,6 +349,7 @@ static int __cpuinit cpufreq_stat_cpu_callback(struct notifier_block *nfb, switch (action) { case CPU_ONLINE: + case CPU_ONLINE_FROZEN: cpufreq_update_policy(cpu); break; case CPU_DOWN_PREPARE: -- cgit v1.2.3 From c35ae1796bd4865bad322645a7edb92d223dfb51 Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Thu, 27 Jun 2013 13:46:42 +0800 Subject: powerpc/eeh: Don't collect PCI-CFG data on PHB When the PHB is fenced or dead, it's pointless to collect the data from PCI config space of subordinate PCI devices since it should return 0xFF's. The patch also fixes overwritten buffer while getting PCI config data. Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/eeh.c | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c index e5796524631c..416fb432d7e2 100644 --- a/arch/powerpc/kernel/eeh.c +++ b/arch/powerpc/kernel/eeh.c @@ -232,16 +232,30 @@ void eeh_slot_error_detail(struct eeh_pe *pe, int severity) { size_t loglen = 0; struct eeh_dev *edev; + bool valid_cfg_log = true; - eeh_pci_enable(pe, EEH_OPT_THAW_MMIO); - eeh_ops->configure_bridge(pe); - eeh_pe_restore_bars(pe); - - pci_regs_buf[0] = 0; - eeh_pe_for_each_dev(pe, edev) { - loglen += eeh_gather_pci_data(edev, pci_regs_buf, - EEH_PCI_REGS_LOG_LEN); - } + /* + * When the PHB is fenced or dead, it's pointless to collect + * the data from PCI config space because it should return + * 0xFF's. For ER, we still retrieve the data from the PCI + * config space. + */ + if (eeh_probe_mode_dev() && + (pe->type & EEH_PE_PHB) && + (pe->state & (EEH_PE_ISOLATED | EEH_PE_PHB_DEAD))) + valid_cfg_log = false; + + if (valid_cfg_log) { + eeh_pci_enable(pe, EEH_OPT_THAW_MMIO); + eeh_ops->configure_bridge(pe); + eeh_pe_restore_bars(pe); + + pci_regs_buf[0] = 0; + eeh_pe_for_each_dev(pe, edev) { + loglen += eeh_gather_pci_data(edev, pci_regs_buf + loglen, + EEH_PCI_REGS_LOG_LEN - loglen); + } + } eeh_ops->get_log(pe, severity, pci_regs_buf, loglen); } -- cgit v1.2.3 From 652defed48757ae0dcc851beeb8fdd484bad40c6 Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Thu, 27 Jun 2013 13:46:43 +0800 Subject: powerpc/eeh: Check PCIe link after reset After reset (e.g. complete reset) in order to bring the fenced PHB back, the PCIe link might not be ready yet. The patch intends to make sure the PCIe link is ready before accessing its subordinate PCI devices. The patch also fixes that wrong values restored to PCI_COMMAND register for PCI bridges. Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/eeh_pe.c | 157 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 144 insertions(+), 13 deletions(-) diff --git a/arch/powerpc/kernel/eeh_pe.c b/arch/powerpc/kernel/eeh_pe.c index 55943fcdaeb8..016588a6f5ed 100644 --- a/arch/powerpc/kernel/eeh_pe.c +++ b/arch/powerpc/kernel/eeh_pe.c @@ -22,6 +22,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include #include #include #include @@ -567,30 +568,132 @@ void eeh_pe_state_clear(struct eeh_pe *pe, int state) eeh_pe_traverse(pe, __eeh_pe_state_clear, &state); } -/** - * eeh_restore_one_device_bars - Restore the Base Address Registers for one device - * @data: EEH device - * @flag: Unused +/* + * Some PCI bridges (e.g. PLX bridges) have primary/secondary + * buses assigned explicitly by firmware, and we probably have + * lost that after reset. So we have to delay the check until + * the PCI-CFG registers have been restored for the parent + * bridge. * - * Loads the PCI configuration space base address registers, - * the expansion ROM base address, the latency timer, and etc. - * from the saved values in the device node. + * Don't use normal PCI-CFG accessors, which probably has been + * blocked on normal path during the stage. So we need utilize + * eeh operations, which is always permitted. */ -static void *eeh_restore_one_device_bars(void *data, void *flag) +static void eeh_bridge_check_link(struct pci_dev *pdev, + struct device_node *dn) +{ + int cap; + uint32_t val; + int timeout = 0; + + /* + * We only check root port and downstream ports of + * PCIe switches + */ + if (!pci_is_pcie(pdev) || + (pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT && + pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM)) + return; + + pr_debug("%s: Check PCIe link for %s ...\n", + __func__, pci_name(pdev)); + + /* Check slot status */ + cap = pdev->pcie_cap; + eeh_ops->read_config(dn, cap + PCI_EXP_SLTSTA, 2, &val); + if (!(val & PCI_EXP_SLTSTA_PDS)) { + pr_debug(" No card in the slot (0x%04x) !\n", val); + return; + } + + /* Check power status if we have the capability */ + eeh_ops->read_config(dn, cap + PCI_EXP_SLTCAP, 2, &val); + if (val & PCI_EXP_SLTCAP_PCP) { + eeh_ops->read_config(dn, cap + PCI_EXP_SLTCTL, 2, &val); + if (val & PCI_EXP_SLTCTL_PCC) { + pr_debug(" In power-off state, power it on ...\n"); + val &= ~(PCI_EXP_SLTCTL_PCC | PCI_EXP_SLTCTL_PIC); + val |= (0x0100 & PCI_EXP_SLTCTL_PIC); + eeh_ops->write_config(dn, cap + PCI_EXP_SLTCTL, 2, val); + msleep(2 * 1000); + } + } + + /* Enable link */ + eeh_ops->read_config(dn, cap + PCI_EXP_LNKCTL, 2, &val); + val &= ~PCI_EXP_LNKCTL_LD; + eeh_ops->write_config(dn, cap + PCI_EXP_LNKCTL, 2, val); + + /* Check link */ + eeh_ops->read_config(dn, cap + PCI_EXP_LNKCAP, 4, &val); + if (!(val & PCI_EXP_LNKCAP_DLLLARC)) { + pr_debug(" No link reporting capability (0x%08x) \n", val); + msleep(1000); + return; + } + + /* Wait the link is up until timeout (5s) */ + timeout = 0; + while (timeout < 5000) { + msleep(20); + timeout += 20; + + eeh_ops->read_config(dn, cap + PCI_EXP_LNKSTA, 2, &val); + if (val & PCI_EXP_LNKSTA_DLLLA) + break; + } + + if (val & PCI_EXP_LNKSTA_DLLLA) + pr_debug(" Link up (%s)\n", + (val & PCI_EXP_LNKSTA_CLS_2_5GB) ? "2.5GB" : "5GB"); + else + pr_debug(" Link not ready (0x%04x)\n", val); +} + +#define BYTE_SWAP(OFF) (8*((OFF)/4)+3-(OFF)) +#define SAVED_BYTE(OFF) (((u8 *)(edev->config_space))[BYTE_SWAP(OFF)]) + +static void eeh_restore_bridge_bars(struct pci_dev *pdev, + struct eeh_dev *edev, + struct device_node *dn) +{ + int i; + + /* + * Device BARs: 0x10 - 0x18 + * Bus numbers and windows: 0x18 - 0x30 + */ + for (i = 4; i < 13; i++) + eeh_ops->write_config(dn, i*4, 4, edev->config_space[i]); + /* Rom: 0x38 */ + eeh_ops->write_config(dn, 14*4, 4, edev->config_space[14]); + + /* Cache line & Latency timer: 0xC 0xD */ + eeh_ops->write_config(dn, PCI_CACHE_LINE_SIZE, 1, + SAVED_BYTE(PCI_CACHE_LINE_SIZE)); + eeh_ops->write_config(dn, PCI_LATENCY_TIMER, 1, + SAVED_BYTE(PCI_LATENCY_TIMER)); + /* Max latency, min grant, interrupt ping and line: 0x3C */ + eeh_ops->write_config(dn, 15*4, 4, edev->config_space[15]); + + /* PCI Command: 0x4 */ + eeh_ops->write_config(dn, PCI_COMMAND, 4, edev->config_space[1]); + + /* Check the PCIe link is ready */ + eeh_bridge_check_link(pdev, dn); +} + +static void eeh_restore_device_bars(struct eeh_dev *edev, + struct device_node *dn) { int i; u32 cmd; - struct eeh_dev *edev = (struct eeh_dev *)data; - struct device_node *dn = eeh_dev_to_of_node(edev); for (i = 4; i < 10; i++) eeh_ops->write_config(dn, i*4, 4, edev->config_space[i]); /* 12 == Expansion ROM Address */ eeh_ops->write_config(dn, 12*4, 4, edev->config_space[12]); -#define BYTE_SWAP(OFF) (8*((OFF)/4)+3-(OFF)) -#define SAVED_BYTE(OFF) (((u8 *)(edev->config_space))[BYTE_SWAP(OFF)]) - eeh_ops->write_config(dn, PCI_CACHE_LINE_SIZE, 1, SAVED_BYTE(PCI_CACHE_LINE_SIZE)); eeh_ops->write_config(dn, PCI_LATENCY_TIMER, 1, @@ -613,6 +716,34 @@ static void *eeh_restore_one_device_bars(void *data, void *flag) else cmd &= ~PCI_COMMAND_SERR; eeh_ops->write_config(dn, PCI_COMMAND, 4, cmd); +} + +/** + * eeh_restore_one_device_bars - Restore the Base Address Registers for one device + * @data: EEH device + * @flag: Unused + * + * Loads the PCI configuration space base address registers, + * the expansion ROM base address, the latency timer, and etc. + * from the saved values in the device node. + */ +static void *eeh_restore_one_device_bars(void *data, void *flag) +{ + struct pci_dev *pdev = NULL; + struct eeh_dev *edev = (struct eeh_dev *)data; + struct device_node *dn = eeh_dev_to_of_node(edev); + + /* Trace the PCI bridge */ + if (eeh_probe_mode_dev()) { + pdev = eeh_dev_to_pci_dev(edev); + if (pdev->hdr_type != PCI_HEADER_TYPE_BRIDGE) + pdev = NULL; + } + + if (pdev) + eeh_restore_bridge_bars(pdev, edev, dn); + else + eeh_restore_device_bars(edev, dn); return NULL; } -- cgit v1.2.3 From 0b9e267d71d2e74d1108785928fd8c8c9dbf441e Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Thu, 27 Jun 2013 13:46:44 +0800 Subject: powerpc/powernv: Replace variables with flags We have 2 fields in "struct pnv_phb" to trace the states. The patch replace the fields with one and introduces flags for that. The patch doesn't impact the logic. Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/powernv/eeh-ioda.c | 8 ++++---- arch/powerpc/platforms/powernv/pci.c | 4 ++-- arch/powerpc/platforms/powernv/pci.h | 7 +++++-- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/arch/powerpc/platforms/powernv/eeh-ioda.c b/arch/powerpc/platforms/powernv/eeh-ioda.c index 84f3036511cb..85025d7e6396 100644 --- a/arch/powerpc/platforms/powernv/eeh-ioda.c +++ b/arch/powerpc/platforms/powernv/eeh-ioda.c @@ -132,7 +132,7 @@ static int ioda_eeh_post_init(struct pci_controller *hose) &ioda_eeh_dbgfs_ops); #endif - phb->eeh_enabled = 1; + phb->eeh_state |= PNV_EEH_STATE_ENABLED; } return 0; @@ -815,7 +815,7 @@ static int ioda_eeh_next_error(struct eeh_pe **pe) * removed, we needn't take care of it any more. */ phb = hose->private_data; - if (phb->removed) + if (phb->eeh_state & PNV_EEH_STATE_REMOVED) continue; rc = opal_pci_next_error(phb->opal_id, @@ -850,7 +850,7 @@ static int ioda_eeh_next_error(struct eeh_pe **pe) list_for_each_entry_safe(hose, tmp, &hose_list, list_node) { phb = hose->private_data; - phb->removed = 1; + phb->eeh_state |= PNV_EEH_STATE_REMOVED; } WARN(1, "EEH: dead IOC detected\n"); @@ -867,7 +867,7 @@ static int ioda_eeh_next_error(struct eeh_pe **pe) WARN(1, "EEH: dead PHB#%x detected\n", hose->global_number); - phb->removed = 1; + phb->eeh_state |= PNV_EEH_STATE_REMOVED; ret = 3; goto out; } else if (severity == OPAL_EEH_SEV_PHB_FENCED) { diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c index 577cbeadb0ea..4c91e6dd1af2 100644 --- a/arch/powerpc/platforms/powernv/pci.c +++ b/arch/powerpc/platforms/powernv/pci.c @@ -309,7 +309,7 @@ static int pnv_pci_read_config(struct pci_bus *bus, if (phb_pe && (phb_pe->state & EEH_PE_ISOLATED)) return PCIBIOS_SUCCESSFUL; - if (phb->eeh_enabled) { + if (phb->eeh_state & PNV_EEH_STATE_ENABLED) { if (*val == EEH_IO_ERROR_VALUE(size)) { busdn = pci_bus_to_OF_node(bus); for (dn = busdn->child; dn; dn = dn->sibling) { @@ -359,7 +359,7 @@ static int pnv_pci_write_config(struct pci_bus *bus, /* Check if the PHB got frozen due to an error (no response) */ #ifdef CONFIG_EEH - if (!phb->eeh_enabled) + if (!(phb->eeh_state & PNV_EEH_STATE_ENABLED)) pnv_pci_config_check_eeh(phb, bus, bdfn); #else pnv_pci_config_check_eeh(phb, bus, bdfn); diff --git a/arch/powerpc/platforms/powernv/pci.h b/arch/powerpc/platforms/powernv/pci.h index 43906e3ab26f..40bdf0219b96 100644 --- a/arch/powerpc/platforms/powernv/pci.h +++ b/arch/powerpc/platforms/powernv/pci.h @@ -78,6 +78,10 @@ struct pnv_eeh_ops { int (*configure_bridge)(struct eeh_pe *pe); int (*next_error)(struct eeh_pe **pe); }; + +#define PNV_EEH_STATE_ENABLED (1 << 0) /* EEH enabled */ +#define PNV_EEH_STATE_REMOVED (1 << 1) /* PHB removed */ + #endif /* CONFIG_EEH */ struct pnv_phb { @@ -92,8 +96,7 @@ struct pnv_phb { #ifdef CONFIG_EEH struct pnv_eeh_ops *eeh_ops; - int eeh_enabled; - int removed; + int eeh_state; #endif #ifdef CONFIG_DEBUG_FS -- cgit v1.2.3 From 88b6d14b2bb48ea4f66fedfe671f98544395b305 Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Thu, 27 Jun 2013 13:46:45 +0800 Subject: powerpc/eeh: Fix address catch for PowerNV On the PowerNV platform, the EEH address cache isn't built correctly because we skipped the EEH devices without binding PE. The patch fixes that. Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/eeh_cache.c | 2 +- arch/powerpc/platforms/powernv/pci-ioda.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/eeh_cache.c b/arch/powerpc/kernel/eeh_cache.c index 1d5d9a6ba740..858ebeaa2bac 100644 --- a/arch/powerpc/kernel/eeh_cache.c +++ b/arch/powerpc/kernel/eeh_cache.c @@ -194,7 +194,7 @@ static void __eeh_addr_cache_insert_dev(struct pci_dev *dev) } /* Skip any devices for which EEH is not enabled. */ - if (!edev->pe) { + if (!eeh_probe_mode_dev() && !edev->pe) { #ifdef DEBUG pr_info("PCI: skip building address cache for=%s - %s\n", pci_name(dev), dn->full_name); diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index dc4ec7956967..c393bf59f113 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c @@ -999,6 +999,7 @@ static void pnv_pci_ioda_fixup(void) pnv_pci_ioda_create_dbgfs(); #ifdef CONFIG_EEH + eeh_probe_mode_set(EEH_PROBE_MODE_DEV); eeh_addr_cache_build(); eeh_init(); #endif -- cgit v1.2.3 From 56ca4fde90009094b1a46971de3879d5f2dd724e Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Thu, 27 Jun 2013 13:46:46 +0800 Subject: powerpc/eeh: Refactor the output message We needn't the the whole backtrace other than one-line message in the error reporting interrupt handler. For errors triggered by access PCI config space or MMIO, we replace "WARN(1, ...)" with pr_err() and dump_stack(). The patch also adds more output messages to indicate what EEH core is doing. Besides, some printk() are replaced with pr_warning(). Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/eeh.c | 9 +++++++-- arch/powerpc/kernel/eeh_driver.c | 23 ++++++++++++++++++----- arch/powerpc/platforms/powernv/eeh-ioda.c | 25 ++++++++++++++++--------- 3 files changed, 41 insertions(+), 16 deletions(-) diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c index 416fb432d7e2..3a8f82fd9005 100644 --- a/arch/powerpc/kernel/eeh.c +++ b/arch/powerpc/kernel/eeh.c @@ -329,7 +329,9 @@ static int eeh_phb_check_failure(struct eeh_pe *pe) eeh_serialize_unlock(flags); eeh_send_failure_event(phb_pe); - WARN(1, "EEH: PHB failure detected\n"); + pr_err("EEH: PHB#%x failure detected\n", + phb_pe->phb->global_number); + dump_stack(); return 1; out: @@ -458,7 +460,10 @@ int eeh_dev_check_failure(struct eeh_dev *edev) * a stack trace will help the device-driver authors figure * out what happened. So print that out. */ - WARN(1, "EEH: failure detected\n"); + pr_err("EEH: Frozen PE#%x detected on PHB#%x\n", + pe->addr, pe->phb->global_number); + dump_stack(); + return 1; dn_unlock: diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c index 0974e1326842..2b1ce17cae50 100644 --- a/arch/powerpc/kernel/eeh_driver.c +++ b/arch/powerpc/kernel/eeh_driver.c @@ -425,6 +425,7 @@ static void eeh_handle_normal_event(struct eeh_pe *pe) * status ... if any child can't handle the reset, then the entire * slot is dlpar removed and added. */ + pr_info("EEH: Notify device drivers to shutdown\n"); eeh_pe_dev_traverse(pe, eeh_report_error, &result); /* Get the current PCI slot state. This can take a long time, @@ -432,7 +433,7 @@ static void eeh_handle_normal_event(struct eeh_pe *pe) */ rc = eeh_ops->wait_state(pe, MAX_WAIT_FOR_RECOVERY*1000); if (rc < 0 || rc == EEH_STATE_NOT_SUPPORT) { - printk(KERN_WARNING "EEH: Permanent failure\n"); + pr_warning("EEH: Permanent failure\n"); goto hard_fail; } @@ -440,6 +441,7 @@ static void eeh_handle_normal_event(struct eeh_pe *pe) * don't post the error log until after all dev drivers * have been informed. */ + pr_info("EEH: Collect temporary log\n"); eeh_slot_error_detail(pe, EEH_LOG_TEMP); /* If all device drivers were EEH-unaware, then shut @@ -447,15 +449,18 @@ static void eeh_handle_normal_event(struct eeh_pe *pe) * go down willingly, without panicing the system. */ if (result == PCI_ERS_RESULT_NONE) { + pr_info("EEH: Reset with hotplug activity\n"); rc = eeh_reset_device(pe, frozen_bus); if (rc) { - printk(KERN_WARNING "EEH: Unable to reset, rc=%d\n", rc); + pr_warning("%s: Unable to reset, err=%d\n", + __func__, rc); goto hard_fail; } } /* If all devices reported they can proceed, then re-enable MMIO */ if (result == PCI_ERS_RESULT_CAN_RECOVER) { + pr_info("EEH: Enable I/O for affected devices\n"); rc = eeh_pci_enable(pe, EEH_OPT_THAW_MMIO); if (rc < 0) @@ -463,6 +468,7 @@ static void eeh_handle_normal_event(struct eeh_pe *pe) if (rc) { result = PCI_ERS_RESULT_NEED_RESET; } else { + pr_info("EEH: Notify device drivers to resume I/O\n"); result = PCI_ERS_RESULT_NONE; eeh_pe_dev_traverse(pe, eeh_report_mmio_enabled, &result); } @@ -470,6 +476,7 @@ static void eeh_handle_normal_event(struct eeh_pe *pe) /* If all devices reported they can proceed, then re-enable DMA */ if (result == PCI_ERS_RESULT_CAN_RECOVER) { + pr_info("EEH: Enabled DMA for affected devices\n"); rc = eeh_pci_enable(pe, EEH_OPT_THAW_DMA); if (rc < 0) @@ -482,17 +489,22 @@ static void eeh_handle_normal_event(struct eeh_pe *pe) /* If any device has a hard failure, then shut off everything. */ if (result == PCI_ERS_RESULT_DISCONNECT) { - printk(KERN_WARNING "EEH: Device driver gave up\n"); + pr_warning("EEH: Device driver gave up\n"); goto hard_fail; } /* If any device called out for a reset, then reset the slot */ if (result == PCI_ERS_RESULT_NEED_RESET) { + pr_info("EEH: Reset without hotplug activity\n"); rc = eeh_reset_device(pe, NULL); if (rc) { - printk(KERN_WARNING "EEH: Cannot reset, rc=%d\n", rc); + pr_warning("%s: Cannot reset, err=%d\n", + __func__, rc); goto hard_fail; } + + pr_info("EEH: Notify device drivers " + "the completion of reset\n"); result = PCI_ERS_RESULT_NONE; eeh_pe_dev_traverse(pe, eeh_report_reset, &result); } @@ -500,11 +512,12 @@ static void eeh_handle_normal_event(struct eeh_pe *pe) /* All devices should claim they have recovered by now. */ if ((result != PCI_ERS_RESULT_RECOVERED) && (result != PCI_ERS_RESULT_NONE)) { - printk(KERN_WARNING "EEH: Not recovered\n"); + pr_warning("EEH: Not recovered\n"); goto hard_fail; } /* Tell all device drivers that they can resume operations */ + pr_info("EEH: Notify device driver to resume\n"); eeh_pe_dev_traverse(pe, eeh_report_resume, NULL); return; diff --git a/arch/powerpc/platforms/powernv/eeh-ioda.c b/arch/powerpc/platforms/powernv/eeh-ioda.c index 85025d7e6396..0cd1c4a71755 100644 --- a/arch/powerpc/platforms/powernv/eeh-ioda.c +++ b/arch/powerpc/platforms/powernv/eeh-ioda.c @@ -853,11 +853,14 @@ static int ioda_eeh_next_error(struct eeh_pe **pe) phb->eeh_state |= PNV_EEH_STATE_REMOVED; } - WARN(1, "EEH: dead IOC detected\n"); + pr_err("EEH: dead IOC detected\n"); ret = 4; goto out; - } else if (severity == OPAL_EEH_SEV_INF) + } else if (severity == OPAL_EEH_SEV_INF) { + pr_info("EEH: IOC informative error " + "detected\n"); ioda_eeh_hub_diag(hose); + } break; case OPAL_EEH_PHB_ERROR: @@ -865,8 +868,8 @@ static int ioda_eeh_next_error(struct eeh_pe **pe) if (ioda_eeh_get_phb_pe(hose, pe)) break; - WARN(1, "EEH: dead PHB#%x detected\n", - hose->global_number); + pr_err("EEH: dead PHB#%x detected\n", + hose->global_number); phb->eeh_state |= PNV_EEH_STATE_REMOVED; ret = 3; goto out; @@ -874,20 +877,24 @@ static int ioda_eeh_next_error(struct eeh_pe **pe) if (ioda_eeh_get_phb_pe(hose, pe)) break; - WARN(1, "EEH: fenced PHB#%x detected\n", - hose->global_number); + pr_err("EEH: fenced PHB#%x detected\n", + hose->global_number); ret = 2; goto out; - } else if (severity == OPAL_EEH_SEV_INF) + } else if (severity == OPAL_EEH_SEV_INF) { + pr_info("EEH: PHB#%x informative error " + "detected\n", + hose->global_number); ioda_eeh_phb_diag(hose); + } break; case OPAL_EEH_PE_ERROR: if (ioda_eeh_get_pe(hose, frozen_pe_no, pe)) break; - WARN(1, "EEH: Frozen PE#%x on PHB#%x detected\n", - (*pe)->addr, (*pe)->phb->global_number); + pr_err("EEH: Frozen PE#%x on PHB#%x detected\n", + (*pe)->addr, (*pe)->phb->global_number); ret = 1; goto out; } -- cgit v1.2.3 From eeb6361fdd3df59c1741522b3d8102f0f5efdd88 Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Thu, 27 Jun 2013 13:46:47 +0800 Subject: powerpc/eeh: Avoid build warnings The patch is for avoiding following build warnings: The function .pnv_pci_ioda_fixup() references the function __init .eeh_init(). This is often because .pnv_pci_ioda_fixup lacks a __init The function .pnv_pci_ioda_fixup() references the function __init .eeh_addr_cache_build(). This is often because .pnv_pci_ioda_fixup lacks a __init Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/eeh.h | 4 ++-- arch/powerpc/kernel/eeh.c | 2 +- arch/powerpc/kernel/eeh_cache.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h index dd65e311c93a..09a8743143f3 100644 --- a/arch/powerpc/include/asm/eeh.h +++ b/arch/powerpc/include/asm/eeh.h @@ -202,13 +202,13 @@ struct pci_bus *eeh_pe_bus_get(struct eeh_pe *pe); void *eeh_dev_init(struct device_node *dn, void *data); void eeh_dev_phb_init_dynamic(struct pci_controller *phb); -int __init eeh_init(void); +int eeh_init(void); int __init eeh_ops_register(struct eeh_ops *ops); int __exit eeh_ops_unregister(const char *name); unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned long val); int eeh_dev_check_failure(struct eeh_dev *edev); -void __init eeh_addr_cache_build(void); +void eeh_addr_cache_build(void); void eeh_add_device_tree_early(struct device_node *); void eeh_add_device_tree_late(struct pci_bus *); void eeh_add_sysfs_files(struct pci_bus *); diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c index 3a8f82fd9005..39954fe941b8 100644 --- a/arch/powerpc/kernel/eeh.c +++ b/arch/powerpc/kernel/eeh.c @@ -756,7 +756,7 @@ int __exit eeh_ops_unregister(const char *name) * Even if force-off is set, the EEH hardware is still enabled, so that * newer systems can boot. */ -int __init eeh_init(void) +int eeh_init(void) { struct pci_controller *hose, *tmp; struct device_node *phb; diff --git a/arch/powerpc/kernel/eeh_cache.c b/arch/powerpc/kernel/eeh_cache.c index 858ebeaa2bac..ea9a94cc97d2 100644 --- a/arch/powerpc/kernel/eeh_cache.c +++ b/arch/powerpc/kernel/eeh_cache.c @@ -285,7 +285,7 @@ void eeh_addr_cache_rmv_dev(struct pci_dev *dev) * Must be run late in boot process, after the pci controllers * have been scanned for devices (after all device resources are known). */ -void __init eeh_addr_cache_build(void) +void eeh_addr_cache_build(void) { struct device_node *dn; struct eeh_dev *edev; -- cgit v1.2.3 From 9bf41be6737327b7c06cd3f210a0cb599f4aa790 Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Thu, 27 Jun 2013 13:46:48 +0800 Subject: powerpc/powernv: Use dev-node in PCI config accessors Currently, we're using the combo (PCI bus + devfn) in the PCI config accessors and PCI config accessors in EEH depends on them. However, it's not safe to refer the PCI bus which might have been removed during hotplug. So we're using device node in the PCI config accessors and the corresponding backends just reuse them. The patch also fix one potential risk: We possiblly have frozen PE during the early PCI probe time, but we haven't setup the PE mapping yet. So the errors should be counted to PE#0. Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/powernv/eeh-powernv.c | 44 +--------- arch/powerpc/platforms/powernv/pci.c | 120 ++++++++++++++++----------- arch/powerpc/platforms/powernv/pci.h | 4 + 3 files changed, 79 insertions(+), 89 deletions(-) diff --git a/arch/powerpc/platforms/powernv/eeh-powernv.c b/arch/powerpc/platforms/powernv/eeh-powernv.c index 9559115424d6..969cce73055a 100644 --- a/arch/powerpc/platforms/powernv/eeh-powernv.c +++ b/arch/powerpc/platforms/powernv/eeh-powernv.c @@ -314,46 +314,6 @@ static int powernv_eeh_configure_bridge(struct eeh_pe *pe) return ret; } -/** - * powernv_eeh_read_config - Read PCI config space - * @dn: device node - * @where: PCI address - * @size: size to read - * @val: return value - * - * Read config space from the speicifed device - */ -static int powernv_eeh_read_config(struct device_node *dn, int where, - int size, u32 *val) -{ - struct eeh_dev *edev = of_node_to_eeh_dev(dn); - struct pci_dev *dev = eeh_dev_to_pci_dev(edev); - struct pci_controller *hose = edev->phb; - - return hose->ops->read(dev->bus, dev->devfn, where, size, val); -} - -/** - * powernv_eeh_write_config - Write PCI config space - * @dn: device node - * @where: PCI address - * @size: size to write - * @val: value to be written - * - * Write config space to the specified device - */ -static int powernv_eeh_write_config(struct device_node *dn, int where, - int size, u32 val) -{ - struct eeh_dev *edev = of_node_to_eeh_dev(dn); - struct pci_dev *dev = eeh_dev_to_pci_dev(edev); - struct pci_controller *hose = edev->phb; - - hose = pci_bus_to_host(dev->bus); - - return hose->ops->write(dev->bus, dev->devfn, where, size, val); -} - /** * powernv_eeh_next_error - Retrieve next EEH error to handle * @pe: Affected PE @@ -389,8 +349,8 @@ static struct eeh_ops powernv_eeh_ops = { .wait_state = powernv_eeh_wait_state, .get_log = powernv_eeh_get_log, .configure_bridge = powernv_eeh_configure_bridge, - .read_config = powernv_eeh_read_config, - .write_config = powernv_eeh_write_config, + .read_config = pnv_pci_cfg_read, + .write_config = pnv_pci_cfg_write, .next_error = powernv_eeh_next_error }; diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c index 4c91e6dd1af2..a28d3b5e6393 100644 --- a/arch/powerpc/platforms/powernv/pci.c +++ b/arch/powerpc/platforms/powernv/pci.c @@ -231,47 +231,50 @@ static void pnv_pci_handle_eeh_config(struct pnv_phb *phb, u32 pe_no) spin_unlock_irqrestore(&phb->lock, flags); } -static void pnv_pci_config_check_eeh(struct pnv_phb *phb, struct pci_bus *bus, - u32 bdfn) +static void pnv_pci_config_check_eeh(struct pnv_phb *phb, + struct device_node *dn) { s64 rc; u8 fstate; u16 pcierr; u32 pe_no; - /* Get PE# if we support IODA */ - pe_no = phb->bdfn_to_pe ? phb->bdfn_to_pe(phb, bus, bdfn & 0xff) : 0; + /* + * Get the PE#. During the PCI probe stage, we might not + * setup that yet. So all ER errors should be mapped to + * PE#0 + */ + pe_no = PCI_DN(dn)->pe_number; + if (pe_no == IODA_INVALID_PE) + pe_no = 0; /* Read freeze status */ rc = opal_pci_eeh_freeze_status(phb->opal_id, pe_no, &fstate, &pcierr, NULL); if (rc) { - pr_warning("PCI %d: Failed to read EEH status for PE#%d," - " err %lld\n", phb->hose->global_number, pe_no, rc); + pr_warning("%s: Can't read EEH status (PE#%d) for " + "%s, err %lld\n", + __func__, pe_no, dn->full_name, rc); return; } - cfg_dbg(" -> EEH check, bdfn=%04x PE%d fstate=%x\n", - bdfn, pe_no, fstate); + cfg_dbg(" -> EEH check, bdfn=%04x PE#%d fstate=%x\n", + (PCI_DN(dn)->busno << 8) | (PCI_DN(dn)->devfn), + pe_no, fstate); if (fstate != 0) pnv_pci_handle_eeh_config(phb, pe_no); } -static int pnv_pci_read_config(struct pci_bus *bus, - unsigned int devfn, - int where, int size, u32 *val) +int pnv_pci_cfg_read(struct device_node *dn, + int where, int size, u32 *val) { - struct pci_controller *hose = pci_bus_to_host(bus); - struct pnv_phb *phb = hose->private_data; + struct pci_dn *pdn = PCI_DN(dn); + struct pnv_phb *phb = pdn->phb->private_data; + u32 bdfn = (pdn->busno << 8) | pdn->devfn; #ifdef CONFIG_EEH - struct device_node *busdn, *dn; struct eeh_pe *phb_pe = NULL; #endif - u32 bdfn = (((uint64_t)bus->number) << 8) | devfn; s64 rc; - if (hose == NULL) - return PCIBIOS_DEVICE_NOT_FOUND; - switch (size) { case 1: { u8 v8; @@ -295,8 +298,8 @@ static int pnv_pci_read_config(struct pci_bus *bus, default: return PCIBIOS_FUNC_NOT_SUPPORTED; } - cfg_dbg("pnv_pci_read_config bus: %x devfn: %x +%x/%x -> %08x\n", - bus->number, devfn, where, size, *val); + cfg_dbg("%s: bus: %x devfn: %x +%x/%x -> %08x\n", + __func__, pdn->busno, pdn->devfn, where, size, *val); /* * Check if the specified PE has been put into frozen @@ -305,44 +308,33 @@ static int pnv_pci_read_config(struct pci_bus *bus, * PHB-fatal errors. */ #ifdef CONFIG_EEH - phb_pe = eeh_phb_pe_get(hose); + phb_pe = eeh_phb_pe_get(pdn->phb); if (phb_pe && (phb_pe->state & EEH_PE_ISOLATED)) return PCIBIOS_SUCCESSFUL; if (phb->eeh_state & PNV_EEH_STATE_ENABLED) { - if (*val == EEH_IO_ERROR_VALUE(size)) { - busdn = pci_bus_to_OF_node(bus); - for (dn = busdn->child; dn; dn = dn->sibling) { - struct pci_dn *pdn = PCI_DN(dn); - - if (pdn && pdn->devfn == devfn && - eeh_dev_check_failure(of_node_to_eeh_dev(dn))) - return PCIBIOS_DEVICE_NOT_FOUND; - } - } + if (*val == EEH_IO_ERROR_VALUE(size) && + eeh_dev_check_failure(of_node_to_eeh_dev(dn))) + return PCIBIOS_DEVICE_NOT_FOUND; } else { - pnv_pci_config_check_eeh(phb, bus, bdfn); + pnv_pci_config_check_eeh(phb, dn); } #else - pnv_pci_config_check_eeh(phb, bus, bdfn); + pnv_pci_config_check_eeh(phb, dn); #endif return PCIBIOS_SUCCESSFUL; } -static int pnv_pci_write_config(struct pci_bus *bus, - unsigned int devfn, - int where, int size, u32 val) +int pnv_pci_cfg_write(struct device_node *dn, + int where, int size, u32 val) { - struct pci_controller *hose = pci_bus_to_host(bus); - struct pnv_phb *phb = hose->private_data; - u32 bdfn = (((uint64_t)bus->number) << 8) | devfn; + struct pci_dn *pdn = PCI_DN(dn); + struct pnv_phb *phb = pdn->phb->private_data; + u32 bdfn = (pdn->busno << 8) | pdn->devfn; - if (hose == NULL) - return PCIBIOS_DEVICE_NOT_FOUND; - - cfg_dbg("pnv_pci_write_config bus: %x devfn: %x +%x/%x -> %08x\n", - bus->number, devfn, where, size, val); + cfg_dbg("%s: bus: %x devfn: %x +%x/%x -> %08x\n", + pdn->busno, pdn->devfn, where, size, val); switch (size) { case 1: opal_pci_config_write_byte(phb->opal_id, bdfn, where, val); @@ -360,16 +352,50 @@ static int pnv_pci_write_config(struct pci_bus *bus, /* Check if the PHB got frozen due to an error (no response) */ #ifdef CONFIG_EEH if (!(phb->eeh_state & PNV_EEH_STATE_ENABLED)) - pnv_pci_config_check_eeh(phb, bus, bdfn); + pnv_pci_config_check_eeh(phb, dn); #else - pnv_pci_config_check_eeh(phb, bus, bdfn); + pnv_pci_config_check_eeh(phb, dn); #endif return PCIBIOS_SUCCESSFUL; } +static int pnv_pci_read_config(struct pci_bus *bus, + unsigned int devfn, + int where, int size, u32 *val) +{ + struct device_node *dn, *busdn = pci_bus_to_OF_node(bus); + struct pci_dn *pdn; + + for (dn = busdn->child; dn; dn = dn->sibling) { + pdn = PCI_DN(dn); + if (pdn && pdn->devfn == devfn) + return pnv_pci_cfg_read(dn, where, size, val); + } + + *val = 0xFFFFFFFF; + return PCIBIOS_DEVICE_NOT_FOUND; + +} + +static int pnv_pci_write_config(struct pci_bus *bus, + unsigned int devfn, + int where, int size, u32 val) +{ + struct device_node *dn, *busdn = pci_bus_to_OF_node(bus); + struct pci_dn *pdn; + + for (dn = busdn->child; dn; dn = dn->sibling) { + pdn = PCI_DN(dn); + if (pdn && pdn->devfn == devfn) + return pnv_pci_cfg_write(dn, where, size, val); + } + + return PCIBIOS_DEVICE_NOT_FOUND; +} + struct pci_ops pnv_pci_ops = { - .read = pnv_pci_read_config, + .read = pnv_pci_read_config, .write = pnv_pci_write_config, }; diff --git a/arch/powerpc/platforms/powernv/pci.h b/arch/powerpc/platforms/powernv/pci.h index 40bdf0219b96..d633c64e05a1 100644 --- a/arch/powerpc/platforms/powernv/pci.h +++ b/arch/powerpc/platforms/powernv/pci.h @@ -182,6 +182,10 @@ extern struct pci_ops pnv_pci_ops; extern struct pnv_eeh_ops ioda_eeh_ops; #endif +int pnv_pci_cfg_read(struct device_node *dn, + int where, int size, u32 *val); +int pnv_pci_cfg_write(struct device_node *dn, + int where, int size, u32 val); extern void pnv_pci_setup_iommu_table(struct iommu_table *tbl, void *tce_mem, u64 tce_size, u64 dma_offset); -- cgit v1.2.3 From 4bb297113433048169c30a32c1e58b6a1b61b621 Mon Sep 17 00:00:00 2001 From: Aaro Koskinen Date: Sun, 30 Jun 2013 22:00:42 +0300 Subject: powerpc/windfarm: Fix overtemperature clearing With pm81/pm91/pm121, when the overtemperature state is entered, and when it remains on after skipped ticks, the driver will try to leave it too soon (immediately on the next tick). This is because the active FAILURE_OVERTEMP state is not visible in "new_failure" variable of the current tick. Furthermore, the driver will keep trying to clear condition in subsequent ticks as FAILURE_OVERTEMP remains set in the "last_failure" variable. These will start to trigger WARNINGS from windfarm core: [ 100.082735] windfarm: Clamping CPU frequency to minimum ! [ 100.108132] windfarm: Overtemp condition detected ! [ 101.952908] windfarm: Overtemp condition cleared ! [...] [ 102.980388] WARNING: at drivers/macintosh/windfarm_core.c:463 [...] [ 103.982227] WARNING: at drivers/macintosh/windfarm_core.c:463 [...] [ 105.030494] WARNING: at drivers/macintosh/windfarm_core.c:463 [...] [ 105.973666] WARNING: at drivers/macintosh/windfarm_core.c:463 [...] [ 106.977913] WARNING: at drivers/macintosh/windfarm_core.c:463 Fix by adding a helper global variable. We leave the overtemp state only after all failure bits have been cleared. I saw this error on iMac G5 iSight (pm121). Also pm81/pm91 are fixed based on the observation that these are almost identical/copy-pasted code. Signed-off-by: Aaro Koskinen Signed-off-by: Benjamin Herrenschmidt --- drivers/macintosh/windfarm_pm121.c | 6 +++++- drivers/macintosh/windfarm_pm81.c | 6 +++++- drivers/macintosh/windfarm_pm91.c | 6 +++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/macintosh/windfarm_pm121.c b/drivers/macintosh/windfarm_pm121.c index af605e915d41..7fe58b0ae8b4 100644 --- a/drivers/macintosh/windfarm_pm121.c +++ b/drivers/macintosh/windfarm_pm121.c @@ -276,6 +276,7 @@ static const char *loop_names[N_LOOPS] = { static unsigned int pm121_failure_state; static int pm121_readjust, pm121_skipping; +static bool pm121_overtemp; static s32 average_power; struct pm121_correction { @@ -847,6 +848,7 @@ static void pm121_tick(void) if (new_failure & FAILURE_OVERTEMP) { wf_set_overtemp(); pm121_skipping = 2; + pm121_overtemp = true; } /* We only clear the overtemp condition if overtemp is cleared @@ -855,8 +857,10 @@ static void pm121_tick(void) * the control loop levels, but we don't want to keep it clear * here in this case */ - if (new_failure == 0 && last_failure & FAILURE_OVERTEMP) + if (!pm121_failure_state && pm121_overtemp) { wf_clear_overtemp(); + pm121_overtemp = false; + } } diff --git a/drivers/macintosh/windfarm_pm81.c b/drivers/macintosh/windfarm_pm81.c index f84933ff3298..2a5e1b15b1d2 100644 --- a/drivers/macintosh/windfarm_pm81.c +++ b/drivers/macintosh/windfarm_pm81.c @@ -149,6 +149,7 @@ static int wf_smu_all_controls_ok, wf_smu_all_sensors_ok, wf_smu_started; static unsigned int wf_smu_failure_state; static int wf_smu_readjust, wf_smu_skipping; +static bool wf_smu_overtemp; /* * ****** System Fans Control Loop ****** @@ -593,6 +594,7 @@ static void wf_smu_tick(void) if (new_failure & FAILURE_OVERTEMP) { wf_set_overtemp(); wf_smu_skipping = 2; + wf_smu_overtemp = true; } /* We only clear the overtemp condition if overtemp is cleared @@ -601,8 +603,10 @@ static void wf_smu_tick(void) * the control loop levels, but we don't want to keep it clear * here in this case */ - if (new_failure == 0 && last_failure & FAILURE_OVERTEMP) + if (!wf_smu_failure_state && wf_smu_overtemp) { wf_clear_overtemp(); + wf_smu_overtemp = false; + } } static void wf_smu_new_control(struct wf_control *ct) diff --git a/drivers/macintosh/windfarm_pm91.c b/drivers/macintosh/windfarm_pm91.c index 2eb484f213c8..a8ac66cd3b13 100644 --- a/drivers/macintosh/windfarm_pm91.c +++ b/drivers/macintosh/windfarm_pm91.c @@ -76,6 +76,7 @@ static struct wf_control *cpufreq_clamp; /* Set to kick the control loop into life */ static int wf_smu_all_controls_ok, wf_smu_all_sensors_ok, wf_smu_started; +static bool wf_smu_overtemp; /* Failure handling.. could be nicer */ #define FAILURE_FAN 0x01 @@ -517,6 +518,7 @@ static void wf_smu_tick(void) if (new_failure & FAILURE_OVERTEMP) { wf_set_overtemp(); wf_smu_skipping = 2; + wf_smu_overtemp = true; } /* We only clear the overtemp condition if overtemp is cleared @@ -525,8 +527,10 @@ static void wf_smu_tick(void) * the control loop levels, but we don't want to keep it clear * here in this case */ - if (new_failure == 0 && last_failure & FAILURE_OVERTEMP) + if (!wf_smu_failure_state && wf_smu_overtemp) { wf_clear_overtemp(); + wf_smu_overtemp = false; + } } -- cgit v1.2.3 From 348c2298a6fd2b145e789739808d5e7598e275fc Mon Sep 17 00:00:00 2001 From: Kevin Hao Date: Thu, 27 Jun 2013 09:09:43 +0800 Subject: powerpc: Don't flush/invalidate the d/icache for an unknown relocation type For an unknown relocation type since the value of r4 is just the 8bit relocation type, the sum of r4 and r7 may yield an invalid memory address. For example: In normal case: r4 = c00xxxxx r7 = 40000000 r4 + r7 = 000xxxxx For an unknown relocation type: r4 = 000000xx r7 = 40000000 r4 + r7 = 400000xx 400000xx is an invalid memory address for a board which has just 512M memory. And for operations such as dcbst or icbi may cause bus error for an invalid memory address on some platforms and then cause the board reset. So we should skip the flush/invalidate the d/icache for an unknown relocation type. Signed-off-by: Kevin Hao Acked-by: Suzuki K. Poulose Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/reloc_32.S | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/reloc_32.S b/arch/powerpc/kernel/reloc_32.S index ef46ba6e094f..f366fedb0872 100644 --- a/arch/powerpc/kernel/reloc_32.S +++ b/arch/powerpc/kernel/reloc_32.S @@ -166,7 +166,7 @@ ha16: /* R_PPC_ADDR16_LO */ lo16: cmpwi r4, R_PPC_ADDR16_LO - bne nxtrela + bne unknown_type lwz r4, 0(r9) /* r_offset */ lwz r0, 8(r9) /* r_addend */ add r0, r0, r3 @@ -191,6 +191,7 @@ nxtrela: dcbst r4,r7 sync /* Ensure the data is flushed before icbi */ icbi r4,r7 +unknown_type: cmpwi r8, 0 /* relasz = 0 ? */ ble done add r9, r9, r6 /* move to next entry in the .rela table */ -- cgit v1.2.3 From 5524f3fc069b6435f9ff0db573e3a8b5082ef528 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 11 Jun 2013 13:57:05 -0600 Subject: powerpc/iommu: Remove unused pci_iommu_init() and pci_direct_iommu_init() pci_iommu_init() and pci_direct_iommu_init() are not referenced anywhere, so remove them. Signed-off-by: Bjorn Helgaas Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/iommu.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/arch/powerpc/include/asm/iommu.h b/arch/powerpc/include/asm/iommu.h index 98d14229f893..c34656a8925e 100644 --- a/arch/powerpc/include/asm/iommu.h +++ b/arch/powerpc/include/asm/iommu.h @@ -130,13 +130,6 @@ extern void iommu_init_early_pSeries(void); extern void iommu_init_early_dart(void); extern void iommu_init_early_pasemi(void); -#ifdef CONFIG_PCI -extern void pci_iommu_init(void); -extern void pci_direct_iommu_init(void); -#else -static inline void pci_iommu_init(void) { } -#endif - extern void alloc_dart_table(void); #if defined(CONFIG_PPC64) && defined(CONFIG_PM) static inline void iommu_save(void) -- cgit v1.2.3 From cc293bf7a9aa6fad68d107c79705732bd2fc57e3 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 13 Jun 2013 19:37:30 -0700 Subject: powerpc/idle: Convert use of typedef ctl_table to struct ctl_table This typedef is unnecessary and should just be removed. Signed-off-by: Joe Perches Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/idle.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/kernel/idle.c b/arch/powerpc/kernel/idle.c index 939ea7ef0dc8..d7216c9abda1 100644 --- a/arch/powerpc/kernel/idle.c +++ b/arch/powerpc/kernel/idle.c @@ -85,7 +85,7 @@ int powersave_nap; /* * Register the sysctl to set/clear powersave_nap. */ -static ctl_table powersave_nap_ctl_table[]={ +static struct ctl_table powersave_nap_ctl_table[] = { { .procname = "powersave-nap", .data = &powersave_nap, @@ -95,7 +95,7 @@ static ctl_table powersave_nap_ctl_table[]={ }, {} }; -static ctl_table powersave_nap_sysctl_root[] = { +static struct ctl_table powersave_nap_sysctl_root[] = { { .procname = "kernel", .mode = 0555, -- cgit v1.2.3 From 5eb969d0e8b8f38fca0b2c6c76f5dca01449664a Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 13 Jun 2013 19:37:37 -0700 Subject: macintosh: Convert use of typedef ctl_table to struct ctl_table This typedef is unnecessary and should just be removed. Signed-off-by: Joe Perches Signed-off-by: Benjamin Herrenschmidt --- drivers/macintosh/mac_hid.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/macintosh/mac_hid.c b/drivers/macintosh/mac_hid.c index 6a82388505f0..80d30e8e3389 100644 --- a/drivers/macintosh/mac_hid.c +++ b/drivers/macintosh/mac_hid.c @@ -181,7 +181,7 @@ static void mac_hid_stop_emulation(void) mac_hid_destroy_emumouse(); } -static int mac_hid_toggle_emumouse(ctl_table *table, int write, +static int mac_hid_toggle_emumouse(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { @@ -214,7 +214,7 @@ static int mac_hid_toggle_emumouse(ctl_table *table, int write, } /* file(s) in /proc/sys/dev/mac_hid */ -static ctl_table mac_hid_files[] = { +static struct ctl_table mac_hid_files[] = { { .procname = "mouse_button_emulation", .data = &mouse_emulate_buttons, @@ -240,7 +240,7 @@ static ctl_table mac_hid_files[] = { }; /* dir in /proc/sys/dev */ -static ctl_table mac_hid_dir[] = { +static struct ctl_table mac_hid_dir[] = { { .procname = "mac_hid", .maxlen = 0, @@ -251,7 +251,7 @@ static ctl_table mac_hid_dir[] = { }; /* /proc/sys/dev itself, in case that is not there yet */ -static ctl_table mac_hid_root_dir[] = { +static struct ctl_table mac_hid_root_dir[] = { { .procname = "dev", .maxlen = 0, -- cgit v1.2.3 From 061d19f279f9bebbdb1ee48bef8c25e03de32ae2 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Mon, 24 Jun 2013 15:30:09 -0400 Subject: powerpc: Delete __cpuinit usage from all users The __cpuinit type of throwaway sections might have made sense some time ago when RAM was more constrained, but now the savings do not offset the cost and complications. For example, the fix in commit 5e427ec2d0 ("x86: Fix bit corruption at CPU resume time") is a good example of the nasty type of bugs that can be created with improper use of the various __init prefixes. After a discussion on LKML[1] it was decided that cpuinit should go the way of devinit and be phased out. Once all the users are gone, we can then finally remove the macros themselves from linux/init.h. This removes all the powerpc uses of the __cpuinit macros. There are no __CPUINIT users in assembly files in powerpc. [1] https://lkml.org/lkml/2013/5/20/589 Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Josh Boyer Cc: Matt Porter Cc: Kumar Gala Cc: linuxppc-dev@lists.ozlabs.org Signed-off-by: Paul Gortmaker Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/rtas.h | 4 ++-- arch/powerpc/include/asm/vdso.h | 2 +- arch/powerpc/kernel/cacheinfo.c | 36 ++++++++++++++++++++-------------- arch/powerpc/kernel/rtas.c | 4 ++-- arch/powerpc/kernel/smp.c | 4 ++-- arch/powerpc/kernel/sysfs.c | 6 +++--- arch/powerpc/kernel/time.c | 1 - arch/powerpc/kernel/vdso.c | 2 +- arch/powerpc/mm/44x_mmu.c | 6 +++--- arch/powerpc/mm/hash_utils_64.c | 2 +- arch/powerpc/mm/mmu_context_nohash.c | 6 +++--- arch/powerpc/mm/numa.c | 7 +++---- arch/powerpc/mm/tlb_nohash.c | 2 +- arch/powerpc/perf/core-book3s.c | 4 ++-- arch/powerpc/platforms/44x/currituck.c | 4 ++-- arch/powerpc/platforms/44x/iss4xx.c | 4 ++-- arch/powerpc/platforms/85xx/smp.c | 6 +++--- arch/powerpc/platforms/powermac/smp.c | 2 +- arch/powerpc/platforms/powernv/smp.c | 2 +- 19 files changed, 54 insertions(+), 50 deletions(-) diff --git a/arch/powerpc/include/asm/rtas.h b/arch/powerpc/include/asm/rtas.h index 34fd70488d83..c7a8bfc9f6f5 100644 --- a/arch/powerpc/include/asm/rtas.h +++ b/arch/powerpc/include/asm/rtas.h @@ -350,8 +350,8 @@ static inline u32 rtas_config_addr(int busno, int devfn, int reg) (devfn << 8) | (reg & 0xff); } -extern void __cpuinit rtas_give_timebase(void); -extern void __cpuinit rtas_take_timebase(void); +extern void rtas_give_timebase(void); +extern void rtas_take_timebase(void); #ifdef CONFIG_PPC_RTAS static inline int page_is_rtas_user_buf(unsigned long pfn) diff --git a/arch/powerpc/include/asm/vdso.h b/arch/powerpc/include/asm/vdso.h index 50f261bc3e95..0d9cecddf8a4 100644 --- a/arch/powerpc/include/asm/vdso.h +++ b/arch/powerpc/include/asm/vdso.h @@ -22,7 +22,7 @@ extern unsigned long vdso64_rt_sigtramp; extern unsigned long vdso32_sigtramp; extern unsigned long vdso32_rt_sigtramp; -int __cpuinit vdso_getcpu_init(void); +int vdso_getcpu_init(void); #else /* __ASSEMBLY__ */ diff --git a/arch/powerpc/kernel/cacheinfo.c b/arch/powerpc/kernel/cacheinfo.c index 92c6b008dd2b..9262cf2bec4b 100644 --- a/arch/powerpc/kernel/cacheinfo.c +++ b/arch/powerpc/kernel/cacheinfo.c @@ -131,7 +131,8 @@ static const char *cache_type_string(const struct cache *cache) return cache_type_info[cache->type].name; } -static void __cpuinit cache_init(struct cache *cache, int type, int level, struct device_node *ofnode) +static void cache_init(struct cache *cache, int type, int level, + struct device_node *ofnode) { cache->type = type; cache->level = level; @@ -140,7 +141,7 @@ static void __cpuinit cache_init(struct cache *cache, int type, int level, struc list_add(&cache->list, &cache_list); } -static struct cache *__cpuinit new_cache(int type, int level, struct device_node *ofnode) +static struct cache *new_cache(int type, int level, struct device_node *ofnode) { struct cache *cache; @@ -324,7 +325,8 @@ static bool cache_node_is_unified(const struct device_node *np) return of_get_property(np, "cache-unified", NULL); } -static struct cache *__cpuinit cache_do_one_devnode_unified(struct device_node *node, int level) +static struct cache *cache_do_one_devnode_unified(struct device_node *node, + int level) { struct cache *cache; @@ -335,7 +337,8 @@ static struct cache *__cpuinit cache_do_one_devnode_unified(struct device_node * return cache; } -static struct cache *__cpuinit cache_do_one_devnode_split(struct device_node *node, int level) +static struct cache *cache_do_one_devnode_split(struct device_node *node, + int level) { struct cache *dcache, *icache; @@ -357,7 +360,7 @@ err: return NULL; } -static struct cache *__cpuinit cache_do_one_devnode(struct device_node *node, int level) +static struct cache *cache_do_one_devnode(struct device_node *node, int level) { struct cache *cache; @@ -369,7 +372,8 @@ static struct cache *__cpuinit cache_do_one_devnode(struct device_node *node, in return cache; } -static struct cache *__cpuinit cache_lookup_or_instantiate(struct device_node *node, int level) +static struct cache *cache_lookup_or_instantiate(struct device_node *node, + int level) { struct cache *cache; @@ -385,7 +389,7 @@ static struct cache *__cpuinit cache_lookup_or_instantiate(struct device_node *n return cache; } -static void __cpuinit link_cache_lists(struct cache *smaller, struct cache *bigger) +static void link_cache_lists(struct cache *smaller, struct cache *bigger) { while (smaller->next_local) { if (smaller->next_local == bigger) @@ -396,13 +400,13 @@ static void __cpuinit link_cache_lists(struct cache *smaller, struct cache *bigg smaller->next_local = bigger; } -static void __cpuinit do_subsidiary_caches_debugcheck(struct cache *cache) +static void do_subsidiary_caches_debugcheck(struct cache *cache) { WARN_ON_ONCE(cache->level != 1); WARN_ON_ONCE(strcmp(cache->ofnode->type, "cpu")); } -static void __cpuinit do_subsidiary_caches(struct cache *cache) +static void do_subsidiary_caches(struct cache *cache) { struct device_node *subcache_node; int level = cache->level; @@ -423,7 +427,7 @@ static void __cpuinit do_subsidiary_caches(struct cache *cache) } } -static struct cache *__cpuinit cache_chain_instantiate(unsigned int cpu_id) +static struct cache *cache_chain_instantiate(unsigned int cpu_id) { struct device_node *cpu_node; struct cache *cpu_cache = NULL; @@ -448,7 +452,7 @@ out: return cpu_cache; } -static struct cache_dir *__cpuinit cacheinfo_create_cache_dir(unsigned int cpu_id) +static struct cache_dir *cacheinfo_create_cache_dir(unsigned int cpu_id) { struct cache_dir *cache_dir; struct device *dev; @@ -653,7 +657,7 @@ static struct kobj_type cache_index_type = { .default_attrs = cache_index_default_attrs, }; -static void __cpuinit cacheinfo_create_index_opt_attrs(struct cache_index_dir *dir) +static void cacheinfo_create_index_opt_attrs(struct cache_index_dir *dir) { const char *cache_name; const char *cache_type; @@ -696,7 +700,8 @@ static void __cpuinit cacheinfo_create_index_opt_attrs(struct cache_index_dir *d kfree(buf); } -static void __cpuinit cacheinfo_create_index_dir(struct cache *cache, int index, struct cache_dir *cache_dir) +static void cacheinfo_create_index_dir(struct cache *cache, int index, + struct cache_dir *cache_dir) { struct cache_index_dir *index_dir; int rc; @@ -722,7 +727,8 @@ err: kfree(index_dir); } -static void __cpuinit cacheinfo_sysfs_populate(unsigned int cpu_id, struct cache *cache_list) +static void cacheinfo_sysfs_populate(unsigned int cpu_id, + struct cache *cache_list) { struct cache_dir *cache_dir; struct cache *cache; @@ -740,7 +746,7 @@ static void __cpuinit cacheinfo_sysfs_populate(unsigned int cpu_id, struct cache } } -void __cpuinit cacheinfo_cpu_online(unsigned int cpu_id) +void cacheinfo_cpu_online(unsigned int cpu_id) { struct cache *cache; diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c index 52add6f3e201..80b5ef403f68 100644 --- a/arch/powerpc/kernel/rtas.c +++ b/arch/powerpc/kernel/rtas.c @@ -1172,7 +1172,7 @@ int __init early_init_dt_scan_rtas(unsigned long node, static arch_spinlock_t timebase_lock; static u64 timebase = 0; -void __cpuinit rtas_give_timebase(void) +void rtas_give_timebase(void) { unsigned long flags; @@ -1189,7 +1189,7 @@ void __cpuinit rtas_give_timebase(void) local_irq_restore(flags); } -void __cpuinit rtas_take_timebase(void) +void rtas_take_timebase(void) { while (!timebase) barrier(); diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c index ee7ac5e6e28a..85398c71665c 100644 --- a/arch/powerpc/kernel/smp.c +++ b/arch/powerpc/kernel/smp.c @@ -480,7 +480,7 @@ static void cpu_idle_thread_init(unsigned int cpu, struct task_struct *idle) secondary_ti = current_set[cpu] = ti; } -int __cpuinit __cpu_up(unsigned int cpu, struct task_struct *tidle) +int __cpu_up(unsigned int cpu, struct task_struct *tidle) { int rc, c; @@ -610,7 +610,7 @@ static struct device_node *cpu_to_l2cache(int cpu) } /* Activate a secondary processor. */ -__cpuinit void start_secondary(void *unused) +void start_secondary(void *unused) { unsigned int cpu = smp_processor_id(); struct device_node *l2_cache; diff --git a/arch/powerpc/kernel/sysfs.c b/arch/powerpc/kernel/sysfs.c index e68a84568b8b..27a90b99ef67 100644 --- a/arch/powerpc/kernel/sysfs.c +++ b/arch/powerpc/kernel/sysfs.c @@ -341,7 +341,7 @@ static struct device_attribute pa6t_attrs[] = { #endif /* HAS_PPC_PMC_PA6T */ #endif /* HAS_PPC_PMC_CLASSIC */ -static void __cpuinit register_cpu_online(unsigned int cpu) +static void register_cpu_online(unsigned int cpu) { struct cpu *c = &per_cpu(cpu_devices, cpu); struct device *s = &c->dev; @@ -502,7 +502,7 @@ ssize_t arch_cpu_release(const char *buf, size_t count) #endif /* CONFIG_HOTPLUG_CPU */ -static int __cpuinit sysfs_cpu_notify(struct notifier_block *self, +static int sysfs_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu) { unsigned int cpu = (unsigned int)(long)hcpu; @@ -522,7 +522,7 @@ static int __cpuinit sysfs_cpu_notify(struct notifier_block *self, return NOTIFY_OK; } -static struct notifier_block __cpuinitdata sysfs_cpu_nb = { +static struct notifier_block sysfs_cpu_nb = { .notifier_call = sysfs_cpu_notify, }; diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index 5fc29ad7e26f..65ab9e909377 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c @@ -631,7 +631,6 @@ static int __init get_freq(char *name, int cells, unsigned long *val) return found; } -/* should become __cpuinit when secondary_cpu_time_init also is */ void start_cpu_decrementer(void) { #if defined(CONFIG_BOOKE) || defined(CONFIG_40x) diff --git a/arch/powerpc/kernel/vdso.c b/arch/powerpc/kernel/vdso.c index d4f463ac65b1..1d9c92621b36 100644 --- a/arch/powerpc/kernel/vdso.c +++ b/arch/powerpc/kernel/vdso.c @@ -711,7 +711,7 @@ static void __init vdso_setup_syscall_map(void) } #ifdef CONFIG_PPC64 -int __cpuinit vdso_getcpu_init(void) +int vdso_getcpu_init(void) { unsigned long cpu, node, val; diff --git a/arch/powerpc/mm/44x_mmu.c b/arch/powerpc/mm/44x_mmu.c index 2c9441ee6bb8..82b1ff759e26 100644 --- a/arch/powerpc/mm/44x_mmu.c +++ b/arch/powerpc/mm/44x_mmu.c @@ -41,7 +41,7 @@ int icache_44x_need_flush; unsigned long tlb_47x_boltmap[1024/8]; -static void __cpuinit ppc44x_update_tlb_hwater(void) +static void ppc44x_update_tlb_hwater(void) { extern unsigned int tlb_44x_patch_hwater_D[]; extern unsigned int tlb_44x_patch_hwater_I[]; @@ -134,7 +134,7 @@ static void __init ppc47x_update_boltmap(void) /* * "Pins" a 256MB TLB entry in AS0 for kernel lowmem for 47x type MMU */ -static void __cpuinit ppc47x_pin_tlb(unsigned int virt, unsigned int phys) +static void ppc47x_pin_tlb(unsigned int virt, unsigned int phys) { unsigned int rA; int bolted; @@ -229,7 +229,7 @@ void setup_initial_memory_limit(phys_addr_t first_memblock_base, } #ifdef CONFIG_SMP -void __cpuinit mmu_init_secondary(int cpu) +void mmu_init_secondary(int cpu) { unsigned long addr; unsigned long memstart = memstart_addr & ~(PPC_PIN_SIZE - 1); diff --git a/arch/powerpc/mm/hash_utils_64.c b/arch/powerpc/mm/hash_utils_64.c index 845231643987..6ecc38bd5b24 100644 --- a/arch/powerpc/mm/hash_utils_64.c +++ b/arch/powerpc/mm/hash_utils_64.c @@ -807,7 +807,7 @@ void __init early_init_mmu(void) } #ifdef CONFIG_SMP -void __cpuinit early_init_mmu_secondary(void) +void early_init_mmu_secondary(void) { /* Initialize hash table for that CPU */ if (!firmware_has_feature(FW_FEATURE_LPAR)) diff --git a/arch/powerpc/mm/mmu_context_nohash.c b/arch/powerpc/mm/mmu_context_nohash.c index 810f8e4d74d4..af3d78e19302 100644 --- a/arch/powerpc/mm/mmu_context_nohash.c +++ b/arch/powerpc/mm/mmu_context_nohash.c @@ -332,8 +332,8 @@ void destroy_context(struct mm_struct *mm) #ifdef CONFIG_SMP -static int __cpuinit mmu_context_cpu_notify(struct notifier_block *self, - unsigned long action, void *hcpu) +static int mmu_context_cpu_notify(struct notifier_block *self, + unsigned long action, void *hcpu) { unsigned int cpu = (unsigned int)(long)hcpu; @@ -366,7 +366,7 @@ static int __cpuinit mmu_context_cpu_notify(struct notifier_block *self, return NOTIFY_OK; } -static struct notifier_block __cpuinitdata mmu_context_cpu_nb = { +static struct notifier_block mmu_context_cpu_nb = { .notifier_call = mmu_context_cpu_notify, }; diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index 88c0425dc0a8..c792cd95e792 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -516,7 +516,7 @@ static int of_drconf_to_nid_single(struct of_drconf_cell *drmem, * Figure out to which domain a cpu belongs and stick it there. * Return the id of the domain used. */ -static int __cpuinit numa_setup_cpu(unsigned long lcpu) +static int numa_setup_cpu(unsigned long lcpu) { int nid = 0; struct device_node *cpu = of_get_cpu_node(lcpu, NULL); @@ -538,8 +538,7 @@ out: return nid; } -static int __cpuinit cpu_numa_callback(struct notifier_block *nfb, - unsigned long action, +static int cpu_numa_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) { unsigned long lcpu = (unsigned long)hcpu; @@ -919,7 +918,7 @@ static void __init *careful_zallocation(int nid, unsigned long size, return ret; } -static struct notifier_block __cpuinitdata ppc64_numa_nb = { +static struct notifier_block ppc64_numa_nb = { .notifier_call = cpu_numa_callback, .priority = 1 /* Must run before sched domains notifier. */ }; diff --git a/arch/powerpc/mm/tlb_nohash.c b/arch/powerpc/mm/tlb_nohash.c index 6888cad5103d..41cd68dee681 100644 --- a/arch/powerpc/mm/tlb_nohash.c +++ b/arch/powerpc/mm/tlb_nohash.c @@ -648,7 +648,7 @@ void __init early_init_mmu(void) __early_init_mmu(1); } -void __cpuinit early_init_mmu_secondary(void) +void early_init_mmu_secondary(void) { __early_init_mmu(0); } diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c index 29c6482890c8..af94a717c451 100644 --- a/arch/powerpc/perf/core-book3s.c +++ b/arch/powerpc/perf/core-book3s.c @@ -1786,7 +1786,7 @@ static void power_pmu_setup(int cpu) cpuhw->mmcr[0] = MMCR0_FC; } -static int __cpuinit +static int power_pmu_notifier(struct notifier_block *self, unsigned long action, void *hcpu) { unsigned int cpu = (long)hcpu; @@ -1803,7 +1803,7 @@ power_pmu_notifier(struct notifier_block *self, unsigned long action, void *hcpu return NOTIFY_OK; } -int __cpuinit register_power_pmu(struct power_pmu *pmu) +int register_power_pmu(struct power_pmu *pmu) { if (ppmu) return -EBUSY; /* something's already registered */ diff --git a/arch/powerpc/platforms/44x/currituck.c b/arch/powerpc/platforms/44x/currituck.c index c52e1b3c9be5..7f1b71a01c6a 100644 --- a/arch/powerpc/platforms/44x/currituck.c +++ b/arch/powerpc/platforms/44x/currituck.c @@ -91,12 +91,12 @@ static void __init ppc47x_init_irq(void) } #ifdef CONFIG_SMP -static void __cpuinit smp_ppc47x_setup_cpu(int cpu) +static void smp_ppc47x_setup_cpu(int cpu) { mpic_setup_this_cpu(); } -static int __cpuinit smp_ppc47x_kick_cpu(int cpu) +static int smp_ppc47x_kick_cpu(int cpu) { struct device_node *cpunode = of_get_cpu_node(cpu, NULL); const u64 *spin_table_addr_prop; diff --git a/arch/powerpc/platforms/44x/iss4xx.c b/arch/powerpc/platforms/44x/iss4xx.c index a28a8629727e..4241bc825800 100644 --- a/arch/powerpc/platforms/44x/iss4xx.c +++ b/arch/powerpc/platforms/44x/iss4xx.c @@ -81,12 +81,12 @@ static void __init iss4xx_init_irq(void) } #ifdef CONFIG_SMP -static void __cpuinit smp_iss4xx_setup_cpu(int cpu) +static void smp_iss4xx_setup_cpu(int cpu) { mpic_setup_this_cpu(); } -static int __cpuinit smp_iss4xx_kick_cpu(int cpu) +static int smp_iss4xx_kick_cpu(int cpu) { struct device_node *cpunode = of_get_cpu_node(cpu, NULL); const u64 *spin_table_addr_prop; diff --git a/arch/powerpc/platforms/85xx/smp.c b/arch/powerpc/platforms/85xx/smp.c index 6a1759939c6b..5ced4f5bb2b2 100644 --- a/arch/powerpc/platforms/85xx/smp.c +++ b/arch/powerpc/platforms/85xx/smp.c @@ -99,7 +99,7 @@ static void mpc85xx_take_timebase(void) } #ifdef CONFIG_HOTPLUG_CPU -static void __cpuinit smp_85xx_mach_cpu_die(void) +static void smp_85xx_mach_cpu_die(void) { unsigned int cpu = smp_processor_id(); u32 tmp; @@ -141,7 +141,7 @@ static inline u32 read_spin_table_addr_l(void *spin_table) return in_be32(&((struct epapr_spin_table *)spin_table)->addr_l); } -static int __cpuinit smp_85xx_kick_cpu(int nr) +static int smp_85xx_kick_cpu(int nr) { unsigned long flags; const u64 *cpu_rel_addr; @@ -362,7 +362,7 @@ static void mpc85xx_smp_machine_kexec(struct kimage *image) } #endif /* CONFIG_KEXEC */ -static void __cpuinit smp_85xx_setup_cpu(int cpu_nr) +static void smp_85xx_setup_cpu(int cpu_nr) { if (smp_85xx_ops.probe == smp_mpic_probe) mpic_setup_this_cpu(); diff --git a/arch/powerpc/platforms/powermac/smp.c b/arch/powerpc/platforms/powermac/smp.c index bdb738a69e41..49c9f9501c21 100644 --- a/arch/powerpc/platforms/powermac/smp.c +++ b/arch/powerpc/platforms/powermac/smp.c @@ -885,7 +885,7 @@ static int smp_core99_cpu_notify(struct notifier_block *self, return NOTIFY_OK; } -static struct notifier_block __cpuinitdata smp_core99_cpu_nb = { +static struct notifier_block smp_core99_cpu_nb = { .notifier_call = smp_core99_cpu_notify, }; #endif /* CONFIG_HOTPLUG_CPU */ diff --git a/arch/powerpc/platforms/powernv/smp.c b/arch/powerpc/platforms/powernv/smp.c index 77784aead1c2..89e3857af4e0 100644 --- a/arch/powerpc/platforms/powernv/smp.c +++ b/arch/powerpc/platforms/powernv/smp.c @@ -40,7 +40,7 @@ #define DBG(fmt...) #endif -static void __cpuinit pnv_smp_setup_cpu(int cpu) +static void pnv_smp_setup_cpu(int cpu) { if (cpu != boot_cpuid) xics_setup_cpu(); -- cgit v1.2.3 From 330dae19998072e97f550885a0087df6d6556816 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sun, 30 Jun 2013 12:03:23 +0200 Subject: mac: Make cuda_init_via() __init cuda_init_via() is called from find_via_cuda() only, which is __init. Signed-off-by: Geert Uytterhoeven Signed-off-by: Benjamin Herrenschmidt --- drivers/macintosh/via-cuda.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/macintosh/via-cuda.c b/drivers/macintosh/via-cuda.c index 86511c570dd8..d61f271d2207 100644 --- a/drivers/macintosh/via-cuda.c +++ b/drivers/macintosh/via-cuda.c @@ -259,7 +259,7 @@ cuda_probe(void) } while (0) static int -cuda_init_via(void) +__init cuda_init_via(void) { out_8(&via[DIRB], (in_8(&via[DIRB]) | TACK | TIP) & ~TREQ); /* TACK & TIP out */ out_8(&via[B], in_8(&via[B]) | TACK | TIP); /* negate them */ -- cgit v1.2.3 From cce606feb425093c8371089d392e336d186e125b Mon Sep 17 00:00:00 2001 From: Li Zhong Date: Thu, 16 May 2013 18:20:26 +0800 Subject: powerpc: Set cpu sibling mask before online cpu It seems following race is possible: cpu0 cpux smp_init->cpu_up->_cpu_up __cpu_up kick_cpu(1) ------------------------------------------------------------------------- waiting online ... ... notify CPU_STARTING set cpux active set cpux online ------------------------------------------------------------------------- finish waiting online ... sched_init_smp init_sched_domains(cpu_active_mask) build_sched_domains set cpux sibling info ------------------------------------------------------------------------- Execution of cpu0 and cpux could be concurrent between two separator lines. So if the cpux sibling information was set too late (normally impossible, but could be triggered by adding some delay in start_secondary, after setting cpu online), build_sched_domains() running on cpu0 might see cpux active, with an empty sibling mask, then cause some bad address accessing like following: [ 0.099855] Unable to handle kernel paging request for data at address 0xc00000038518078f [ 0.099868] Faulting instruction address: 0xc0000000000b7a64 [ 0.099883] Oops: Kernel access of bad area, sig: 11 [#1] [ 0.099895] PREEMPT SMP NR_CPUS=16 DEBUG_PAGEALLOC NUMA pSeries [ 0.099922] Modules linked in: [ 0.099940] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 3.10.0-rc1-00120-gb973425-dirty #16 [ 0.099956] task: c0000001fed80000 ti: c0000001fed7c000 task.ti: c0000001fed7c000 [ 0.099971] NIP: c0000000000b7a64 LR: c0000000000b7a40 CTR: c0000000000b4934 [ 0.099985] REGS: c0000001fed7f760 TRAP: 0300 Not tainted (3.10.0-rc1-00120-gb973425-dirty) [ 0.099997] MSR: 8000000000009032 CR: 24272828 XER: 20000003 [ 0.100045] SOFTE: 1 [ 0.100053] CFAR: c000000000445ee8 [ 0.100064] DAR: c00000038518078f, DSISR: 40000000 [ 0.100073] GPR00: 0000000000000080 c0000001fed7f9e0 c000000000c84d48 0000000000000010 GPR04: 0000000000000010 0000000000000000 c0000001fc55e090 0000000000000000 GPR08: ffffffffffffffff c000000000b80b30 c000000000c962d8 00000003845ffc5f GPR12: 0000000000000000 c00000000f33d000 c00000000000b9e4 0000000000000000 GPR16: 0000000000000000 0000000000000000 0000000000000001 0000000000000000 GPR20: c000000000ccf750 0000000000000000 c000000000c94d48 c0000001fc504000 GPR24: c0000001fc504000 c0000001fecef848 c000000000c94d48 c000000000ccf000 GPR28: c0000001fc522090 0000000000000010 c0000001fecef848 c0000001fed7fae0 [ 0.100293] NIP [c0000000000b7a64] .get_group+0x84/0xc4 [ 0.100307] LR [c0000000000b7a40] .get_group+0x60/0xc4 [ 0.100318] Call Trace: [ 0.100332] [c0000001fed7f9e0] [c0000000000dbce4] .lock_is_held+0xa8/0xd0 (unreliable) [ 0.100354] [c0000001fed7fa70] [c0000000000bf62c] .build_sched_domains+0x728/0xd14 [ 0.100375] [c0000001fed7fbe0] [c000000000af67bc] .sched_init_smp+0x4fc/0x654 [ 0.100394] [c0000001fed7fce0] [c000000000adce24] .kernel_init_freeable+0x17c/0x30c [ 0.100413] [c0000001fed7fdb0] [c00000000000ba08] .kernel_init+0x24/0x12c [ 0.100431] [c0000001fed7fe30] [c000000000009f74] .ret_from_kernel_thread+0x5c/0x68 [ 0.100445] Instruction dump: [ 0.100456] 38800010 38a00000 4838e3f5 60000000 7c6307b4 2fbf0000 419e0040 3d220001 [ 0.100496] 78601f24 39491590 e93e0008 7d6a002a <7d69582a> f97f0000 7d4a002a e93e0010 [ 0.100559] ---[ end trace 31fd0ba7d8756001 ]--- This patch tries to move the sibling maps updating before notify_cpu_starting() and cpu online, and a write barrier there to make sure sibling maps are updated before active and online mask. Signed-off-by: Li Zhong Reviewed-by: Srivatsa S. Bhat Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/smp.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c index 85398c71665c..38b0ba65a735 100644 --- a/arch/powerpc/kernel/smp.c +++ b/arch/powerpc/kernel/smp.c @@ -637,12 +637,10 @@ void start_secondary(void *unused) vdso_getcpu_init(); #endif - notify_cpu_starting(cpu); - set_cpu_online(cpu, true); /* Update sibling maps */ base = cpu_first_thread_sibling(cpu); for (i = 0; i < threads_per_core; i++) { - if (cpu_is_offline(base + i)) + if (cpu_is_offline(base + i) && (cpu != base + i)) continue; cpumask_set_cpu(cpu, cpu_sibling_mask(base + i)); cpumask_set_cpu(base + i, cpu_sibling_mask(cpu)); @@ -667,6 +665,10 @@ void start_secondary(void *unused) } of_node_put(l2_cache); + smp_wmb(); + notify_cpu_starting(cpu); + set_cpu_online(cpu, true); + local_irq_enable(); cpu_startup_entry(CPUHP_ONLINE); -- cgit v1.2.3 From 7029705a9d0544186c29ae09708b3e5adb512835 Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Tue, 21 May 2013 17:20:50 +0800 Subject: powerpc/nvram64: Need return the related error code on failure occurs When error occurs, need return the related error code to let upper caller know about it. ppc_md.nvram_size() can return the error code (e.g. core99_nvram_size() in 'arch/powerpc/platforms/powermac/nvram.c'). Also set ret value when only need it, so can save structions for normal cases. Signed-off-by: Chen Gang Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/nvram_64.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/arch/powerpc/kernel/nvram_64.c b/arch/powerpc/kernel/nvram_64.c index 48fbc2b97e95..8213ee1eb05a 100644 --- a/arch/powerpc/kernel/nvram_64.c +++ b/arch/powerpc/kernel/nvram_64.c @@ -84,22 +84,30 @@ static ssize_t dev_nvram_read(struct file *file, char __user *buf, char *tmp = NULL; ssize_t size; - ret = -ENODEV; - if (!ppc_md.nvram_size) + if (!ppc_md.nvram_size) { + ret = -ENODEV; goto out; + } - ret = 0; size = ppc_md.nvram_size(); - if (*ppos >= size || size < 0) + if (size < 0) { + ret = size; + goto out; + } + + if (*ppos >= size) { + ret = 0; goto out; + } count = min_t(size_t, count, size - *ppos); count = min(count, PAGE_SIZE); - ret = -ENOMEM; tmp = kmalloc(count, GFP_KERNEL); - if (!tmp) + if (!tmp) { + ret = -ENOMEM; goto out; + } ret = ppc_md.nvram_read(tmp, count, ppos); if (ret <= 0) -- cgit v1.2.3 From 91f5af2e6524f795c0ee5d2fdc3be40d0e427ae2 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 21 May 2013 20:45:40 +0200 Subject: macintosh/windfarm: Remove obsolete cleanup for clientdata A few new i2c-drivers came into the kernel which clear the clientdata-pointer on exit or error. This is obsolete meanwhile, the core will do it. Signed-off-by: Wolfram Sang Signed-off-by: Benjamin Herrenschmidt --- drivers/macintosh/windfarm_smu_sat.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/macintosh/windfarm_smu_sat.c b/drivers/macintosh/windfarm_smu_sat.c index d87f5ee04ca9..ad6223e88340 100644 --- a/drivers/macintosh/windfarm_smu_sat.c +++ b/drivers/macintosh/windfarm_smu_sat.c @@ -343,7 +343,6 @@ static int wf_sat_remove(struct i2c_client *client) wf_unregister_sensor(&sens->sens); } sat->i2c = NULL; - i2c_set_clientdata(client, NULL); kref_put(&sat->ref, wf_sat_release); return 0; -- cgit v1.2.3 From 8246aca7058f3f2c2ae503081777965cd8df7b90 Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Wed, 20 Mar 2013 14:30:12 +0800 Subject: powerpc/smp: Section mismatch from smp_release_cpus to __initdata spinning_secondaries the smp_release_cpus is a normal funciton and called in normal environments, but it calls the __initdata spinning_secondaries. need modify spinning_secondaries to match smp_release_cpus. the related warning: (the linker report boot_paca.33377, but it should be spinning_secondaries) ----------------------------------------------------------------------------- WARNING: arch/powerpc/kernel/built-in.o(.text+0x23176): Section mismatch in reference from the function .smp_release_cpus() to the variable .init.data:boot_paca.33377 The function .smp_release_cpus() references the variable __initdata boot_paca.33377. This is often because .smp_release_cpus lacks a __initdata annotation or the annotation of boot_paca.33377 is wrong. WARNING: arch/powerpc/kernel/built-in.o(.text+0x231fe): Section mismatch in reference from the function .smp_release_cpus() to the variable .init.data:boot_paca.33377 The function .smp_release_cpus() references the variable __initdata boot_paca.33377. This is often because .smp_release_cpus lacks a __initdata annotation or the annotation of boot_paca.33377 is wrong. ----------------------------------------------------------------------------- Signed-off-by: Chen Gang CC: Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/setup_64.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c index e379d3fd1694..389fb8077cc9 100644 --- a/arch/powerpc/kernel/setup_64.c +++ b/arch/powerpc/kernel/setup_64.c @@ -76,7 +76,7 @@ #endif int boot_cpuid = 0; -int __initdata spinning_secondaries; +int spinning_secondaries; u64 ppc64_pft_size; /* Pick defaults since we might want to patch instructions -- cgit v1.2.3 From ec207dcc91c62aeccb65b36c7764076f5e5aaddb Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Fri, 28 Jun 2013 21:12:14 +0800 Subject: powerpc/eeh: Update MAINTAINERS Update MAINTAINERS to reflect recent changes. Signed-off-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt --- MAINTAINERS | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 5be702cc8449..ebeceeb9dfab 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3103,6 +3103,13 @@ M: Maxim Levitsky S: Maintained F: drivers/media/rc/ene_ir.* +ENHANCED ERROR HANDLING (EEH) +M: Gavin Shan +L: linuxppc-dev@lists.ozlabs.org +S: Supported +F: Documentation/powerpc/eeh-pci-error-recovery.txt +F: arch/powerpc/kernel/eeh*.c + EPSON S1D13XXX FRAMEBUFFER DRIVER M: Kristoffer Ericson S: Maintained @@ -6149,7 +6156,6 @@ M: Linas Vepstas L: linux-pci@vger.kernel.org S: Supported F: Documentation/PCI/pci-error-recovery.txt -F: Documentation/powerpc/eeh-pci-error-recovery.txt PCI SUBSYSTEM M: Bjorn Helgaas -- cgit v1.2.3 From dd023217e17e72b46fb4d49c7734c426938c3dba Mon Sep 17 00:00:00 2001 From: Nathan Fontenot Date: Mon, 24 Jun 2013 22:08:05 -0500 Subject: powerpc/numa: Do not update sysfs cpu registration from invalid context The topology update code that updates the cpu node registration in sysfs should not be called while in stop_machine(). The register/unregister calls take a lock and may sleep. This patch moves these calls outside of the call to stop_machine(). Signed-off-by: Nathan Fontenot CC: Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/mm/numa.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index c792cd95e792..08397217e8ac 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -1432,11 +1432,9 @@ static int update_cpu_topology(void *data) if (cpu != update->cpu) continue; - unregister_cpu_under_node(update->cpu, update->old_nid); unmap_cpu_from_node(update->cpu); map_cpu_to_node(update->cpu, update->new_nid); vdso_getcpu_init(); - register_cpu_under_node(update->cpu, update->new_nid); } return 0; @@ -1484,6 +1482,9 @@ int arch_update_cpu_topology(void) stop_machine(update_cpu_topology, &updates[0], &updated_cpus); for (ud = &updates[0]; ud; ud = ud->next) { + unregister_cpu_under_node(ud->cpu, ud->old_nid); + register_cpu_under_node(ud->cpu, ud->new_nid); + dev = get_cpu_device(ud->cpu); if (dev) kobject_uevent(&dev->kobj, KOBJ_CHANGE); -- cgit v1.2.3 From 1d567cb4bd42d560a7621cac6f6aebe87343689e Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Tue, 25 Jun 2013 17:47:54 +1000 Subject: powerpc: Remove unreachable relocation on exception handlers We have relocation on exception handlers defined for h_data_storage and h_instr_storage. However we will never take relocation on exceptions for these because they can only come from a guest, and we never take relocation on exceptions when we transition from guest to host. We also have a handler for hmi_exception (Hypervisor Maintenance) which is defined in the architecture to never be delivered with relocation on, see see v2.07 Book III-S section 6.5. So remove the handlers, leaving a branch to self just to be double extra paranoid. Signed-off-by: Michael Ellerman CC: [v3.9+] Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/exceptions-64s.S | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S index e783453f910d..dcadf03814e1 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S @@ -793,14 +793,10 @@ system_call_relon_pSeries: STD_RELON_EXCEPTION_PSERIES(0x4d00, 0xd00, single_step) . = 0x4e00 - SET_SCRATCH0(r13) - EXCEPTION_PROLOG_0(PACA_EXGEN) - b h_data_storage_relon_hv + b . /* Can't happen, see v2.07 Book III-S section 6.5 */ . = 0x4e20 - SET_SCRATCH0(r13) - EXCEPTION_PROLOG_0(PACA_EXGEN) - b h_instr_storage_relon_hv + b . /* Can't happen, see v2.07 Book III-S section 6.5 */ . = 0x4e40 SET_SCRATCH0(r13) @@ -808,9 +804,7 @@ system_call_relon_pSeries: b emulation_assist_relon_hv . = 0x4e60 - SET_SCRATCH0(r13) - EXCEPTION_PROLOG_0(PACA_EXGEN) - b hmi_exception_relon_hv + b . /* Can't happen, see v2.07 Book III-S section 6.5 */ . = 0x4e80 SET_SCRATCH0(r13) @@ -1180,14 +1174,8 @@ tm_unavailable_common: __end_handlers: /* Equivalents to the above handlers for relocation-on interrupt vectors */ - STD_RELON_EXCEPTION_HV_OOL(0xe00, h_data_storage) - KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe00) - STD_RELON_EXCEPTION_HV_OOL(0xe20, h_instr_storage) - KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe20) STD_RELON_EXCEPTION_HV_OOL(0xe40, emulation_assist) KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe40) - STD_RELON_EXCEPTION_HV_OOL(0xe60, hmi_exception) - KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe60) MASKABLE_RELON_EXCEPTION_HV_OOL(0xe80, h_doorbell) KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe80) -- cgit v1.2.3 From c9f69518e5f08170bc857984a077f693d63171df Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Tue, 25 Jun 2013 17:47:55 +1000 Subject: powerpc: Remove KVMTEST from RELON exception handlers KVMTEST is a macro which checks whether we are taking an exception from guest context, if so we branch out of line and eventually call into the KVM code to handle the switch. When running real guests on bare metal (HV KVM) the hardware ensures that we never take a relocation on exception when transitioning from guest to host. For PR KVM we disable relocation on exceptions ourself in kvmppc_core_init_vm(), as of commit a413f47 "Disable relocation on exceptions whenever PR KVM is active". So convert all the RELON macros to use NOTEST, and drop the remaining KVM_HANDLER() definitions we have for 0xe40 and 0xe80. Signed-off-by: Michael Ellerman CC: [v3.9+] Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/exception-64s.h | 8 ++++---- arch/powerpc/kernel/exceptions-64s.S | 2 -- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/arch/powerpc/include/asm/exception-64s.h b/arch/powerpc/include/asm/exception-64s.h index 8e5fae8beaf6..484cb094ad42 100644 --- a/arch/powerpc/include/asm/exception-64s.h +++ b/arch/powerpc/include/asm/exception-64s.h @@ -358,12 +358,12 @@ label##_relon_pSeries: \ /* No guest interrupts come through here */ \ SET_SCRATCH0(r13); /* save r13 */ \ EXCEPTION_RELON_PROLOG_PSERIES(PACA_EXGEN, label##_common, \ - EXC_STD, KVMTEST_PR, vec) + EXC_STD, NOTEST, vec) #define STD_RELON_EXCEPTION_PSERIES_OOL(vec, label) \ .globl label##_relon_pSeries; \ label##_relon_pSeries: \ - EXCEPTION_PROLOG_1(PACA_EXGEN, KVMTEST_PR, vec); \ + EXCEPTION_PROLOG_1(PACA_EXGEN, NOTEST, vec); \ EXCEPTION_RELON_PROLOG_PSERIES_1(label##_common, EXC_STD) #define STD_RELON_EXCEPTION_HV(loc, vec, label) \ @@ -374,12 +374,12 @@ label##_relon_hv: \ /* No guest interrupts come through here */ \ SET_SCRATCH0(r13); /* save r13 */ \ EXCEPTION_RELON_PROLOG_PSERIES(PACA_EXGEN, label##_common, \ - EXC_HV, KVMTEST, vec) + EXC_HV, NOTEST, vec) #define STD_RELON_EXCEPTION_HV_OOL(vec, label) \ .globl label##_relon_hv; \ label##_relon_hv: \ - EXCEPTION_PROLOG_1(PACA_EXGEN, KVMTEST, vec); \ + EXCEPTION_PROLOG_1(PACA_EXGEN, NOTEST, vec); \ EXCEPTION_RELON_PROLOG_PSERIES_1(label##_common, EXC_HV) /* This associate vector numbers with bits in paca->irq_happened */ diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S index dcadf03814e1..89eba1fb5bbe 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S @@ -1175,9 +1175,7 @@ __end_handlers: /* Equivalents to the above handlers for relocation-on interrupt vectors */ STD_RELON_EXCEPTION_HV_OOL(0xe40, emulation_assist) - KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe40) MASKABLE_RELON_EXCEPTION_HV_OOL(0xe80, h_doorbell) - KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe80) STD_RELON_EXCEPTION_PSERIES_OOL(0xf00, performance_monitor) STD_RELON_EXCEPTION_PSERIES_OOL(0xf20, altivec_unavailable) -- cgit v1.2.3 From 021424a1fce335e05807fd770eb8e1da30a63eea Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Tue, 25 Jun 2013 17:47:56 +1000 Subject: powerpc: Rename and flesh out the facility unavailable exception handler The exception at 0xf60 is not the TM (Transactional Memory) unavailable exception, it is the "Facility Unavailable Exception", rename it as such. Flesh out the handler to acknowledge the fact that it can be called for many reasons, one of which is TM being unavailable. Use STD_EXCEPTION_COMMON() for the exception body, for some reason we had it open-coded, I've checked the generated code is identical. Signed-off-by: Michael Ellerman CC: [v3.10] Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/exceptions-64s.S | 21 +++++++-------------- arch/powerpc/kernel/traps.c | 33 +++++++++++++++++++++++++-------- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S index 89eba1fb5bbe..6ee74f8bae2f 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S @@ -341,10 +341,11 @@ vsx_unavailable_pSeries_1: EXCEPTION_PROLOG_0(PACA_EXGEN) b vsx_unavailable_pSeries +facility_unavailable_trampoline: . = 0xf60 SET_SCRATCH0(r13) EXCEPTION_PROLOG_0(PACA_EXGEN) - b tm_unavailable_pSeries + b facility_unavailable_pSeries #ifdef CONFIG_CBE_RAS STD_EXCEPTION_HV(0x1200, 0x1202, cbe_system_error) @@ -522,7 +523,7 @@ denorm_done: KVM_HANDLER_PR(PACA_EXGEN, EXC_STD, 0xf20) STD_EXCEPTION_PSERIES_OOL(0xf40, vsx_unavailable) KVM_HANDLER_PR(PACA_EXGEN, EXC_STD, 0xf40) - STD_EXCEPTION_PSERIES_OOL(0xf60, tm_unavailable) + STD_EXCEPTION_PSERIES_OOL(0xf60, facility_unavailable) KVM_HANDLER_PR(PACA_EXGEN, EXC_STD, 0xf60) /* @@ -829,11 +830,11 @@ vsx_unavailable_relon_pSeries_1: EXCEPTION_PROLOG_0(PACA_EXGEN) b vsx_unavailable_relon_pSeries -tm_unavailable_relon_pSeries_1: +facility_unavailable_relon_trampoline: . = 0x4f60 SET_SCRATCH0(r13) EXCEPTION_PROLOG_0(PACA_EXGEN) - b tm_unavailable_relon_pSeries + b facility_unavailable_relon_pSeries STD_RELON_EXCEPTION_PSERIES(0x5300, 0x1300, instruction_breakpoint) #ifdef CONFIG_PPC_DENORMALISATION @@ -1159,15 +1160,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_VSX) bl .vsx_unavailable_exception b .ret_from_except - .align 7 - .globl tm_unavailable_common -tm_unavailable_common: - EXCEPTION_PROLOG_COMMON(0xf60, PACA_EXGEN) - bl .save_nvgprs - DISABLE_INTS - addi r3,r1,STACK_FRAME_OVERHEAD - bl .tm_unavailable_exception - b .ret_from_except + STD_EXCEPTION_COMMON(0xf60, facility_unavailable, .facility_unavailable_exception) .align 7 .globl __end_handlers @@ -1180,7 +1173,7 @@ __end_handlers: STD_RELON_EXCEPTION_PSERIES_OOL(0xf00, performance_monitor) STD_RELON_EXCEPTION_PSERIES_OOL(0xf20, altivec_unavailable) STD_RELON_EXCEPTION_PSERIES_OOL(0xf40, vsx_unavailable) - STD_RELON_EXCEPTION_PSERIES_OOL(0xf60, tm_unavailable) + STD_RELON_EXCEPTION_PSERIES_OOL(0xf60, facility_unavailable) #if defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV) /* diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index 300daf3e7ab0..11f448812dc1 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -1286,25 +1286,42 @@ void vsx_unavailable_exception(struct pt_regs *regs) die("Unrecoverable VSX Unavailable Exception", regs, SIGABRT); } -void tm_unavailable_exception(struct pt_regs *regs) +void facility_unavailable_exception(struct pt_regs *regs) { + static char *facility_strings[] = { + "FPU", + "VMX/VSX", + "DSCR", + "PMU SPRs", + "BHRB", + "TM", + "AT", + "EBB", + "TAR", + }; + char *facility; + u64 value; + + value = mfspr(SPRN_FSCR) >> 56; + /* We restore the interrupt state now */ if (!arch_irq_disabled_regs(regs)) local_irq_enable(); - /* Currently we never expect a TMU exception. Catch - * this and kill the process! - */ - printk(KERN_EMERG "Unexpected TM unavailable exception at %lx " - "(msr %lx)\n", - regs->nip, regs->msr); + if (value < ARRAY_SIZE(facility_strings)) + facility = facility_strings[value]; + else + facility = "unknown"; + + pr_err("Facility '%s' unavailable, exception at 0x%lx, MSR=%lx\n", + facility, regs->nip, regs->msr); if (user_mode(regs)) { _exception(SIGILL, regs, ILL_ILLOPC, regs->nip); return; } - die("Unexpected TM unavailable exception", regs, SIGABRT); + die("Unexpected facility unavailable exception", regs, SIGABRT); } #ifdef CONFIG_PPC_TRANSACTIONAL_MEM -- cgit v1.2.3 From b14b6260efeee6eb8942c6e6420e31281892acb6 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Tue, 25 Jun 2013 17:47:57 +1000 Subject: powerpc: Wire up the HV facility unavailable exception Similar to the facility unavailble exception, except the facilities are controlled by HFSCR. Adapt the facility_unavailable_exception() so it can be called for either the regular or Hypervisor facility unavailable exceptions. Signed-off-by: Michael Ellerman CC: [v3.10] Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/exceptions-64s.S | 15 +++++++++++++++ arch/powerpc/kernel/traps.c | 16 ++++++++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S index 6ee74f8bae2f..359d949f4a4b 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S @@ -347,6 +347,12 @@ facility_unavailable_trampoline: EXCEPTION_PROLOG_0(PACA_EXGEN) b facility_unavailable_pSeries +hv_facility_unavailable_trampoline: + . = 0xf80 + SET_SCRATCH0(r13) + EXCEPTION_PROLOG_0(PACA_EXGEN) + b facility_unavailable_hv + #ifdef CONFIG_CBE_RAS STD_EXCEPTION_HV(0x1200, 0x1202, cbe_system_error) KVM_HANDLER_SKIP(PACA_EXGEN, EXC_HV, 0x1202) @@ -525,6 +531,8 @@ denorm_done: KVM_HANDLER_PR(PACA_EXGEN, EXC_STD, 0xf40) STD_EXCEPTION_PSERIES_OOL(0xf60, facility_unavailable) KVM_HANDLER_PR(PACA_EXGEN, EXC_STD, 0xf60) + STD_EXCEPTION_HV_OOL(0xf82, facility_unavailable) + KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xf82) /* * An interrupt came in while soft-disabled. We set paca->irq_happened, then: @@ -836,6 +844,12 @@ facility_unavailable_relon_trampoline: EXCEPTION_PROLOG_0(PACA_EXGEN) b facility_unavailable_relon_pSeries +hv_facility_unavailable_relon_trampoline: + . = 0x4f80 + SET_SCRATCH0(r13) + EXCEPTION_PROLOG_0(PACA_EXGEN) + b facility_unavailable_relon_hv + STD_RELON_EXCEPTION_PSERIES(0x5300, 0x1300, instruction_breakpoint) #ifdef CONFIG_PPC_DENORMALISATION . = 0x5500 @@ -1174,6 +1188,7 @@ __end_handlers: STD_RELON_EXCEPTION_PSERIES_OOL(0xf20, altivec_unavailable) STD_RELON_EXCEPTION_PSERIES_OOL(0xf40, vsx_unavailable) STD_RELON_EXCEPTION_PSERIES_OOL(0xf60, facility_unavailable) + STD_RELON_EXCEPTION_HV_OOL(0xf80, facility_unavailable) #if defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV) /* diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index 11f448812dc1..4a87fcbe3ba9 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -1299,10 +1299,18 @@ void facility_unavailable_exception(struct pt_regs *regs) "EBB", "TAR", }; - char *facility; + char *facility, *prefix; u64 value; - value = mfspr(SPRN_FSCR) >> 56; + if (regs->trap == 0xf60) { + value = mfspr(SPRN_FSCR); + prefix = ""; + } else { + value = mfspr(SPRN_HFSCR); + prefix = "Hypervisor "; + } + + value = value >> 56; /* We restore the interrupt state now */ if (!arch_irq_disabled_regs(regs)) @@ -1313,8 +1321,8 @@ void facility_unavailable_exception(struct pt_regs *regs) else facility = "unknown"; - pr_err("Facility '%s' unavailable, exception at 0x%lx, MSR=%lx\n", - facility, regs->nip, regs->msr); + pr_err("%sFacility '%s' unavailable, exception at 0x%lx, MSR=%lx\n", + prefix, facility, regs->nip, regs->msr); if (user_mode(regs)) { _exception(SIGILL, regs, ILL_ILLOPC, regs->nip); -- cgit v1.2.3 From d8bec4c9cd58f6d3679e09b7293851fb92ad7557 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Fri, 28 Jun 2013 18:15:10 +1000 Subject: powerpc/perf: Check that events only include valid bits on Power8 A mistake we have made in the past is that we pull out the fields we need from the event code, but don't check that there are no unknown bits set. This means that we can't ever assign meaning to those unknown bits in future. Although we have once again failed to do this at release, it is still early days for Power8 so I think we can still slip this in and get away with it. Signed-off-by: Michael Ellerman CC: [v3.10] Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/perf/power8-pmu.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/arch/powerpc/perf/power8-pmu.c b/arch/powerpc/perf/power8-pmu.c index f7d1c4fff303..84cdc6d892e3 100644 --- a/arch/powerpc/perf/power8-pmu.c +++ b/arch/powerpc/perf/power8-pmu.c @@ -109,6 +109,16 @@ #define EVENT_IS_MARKED (EVENT_MARKED_MASK << EVENT_MARKED_SHIFT) #define EVENT_PSEL_MASK 0xff /* PMCxSEL value */ +#define EVENT_VALID_MASK \ + ((EVENT_THRESH_MASK << EVENT_THRESH_SHIFT) | \ + (EVENT_SAMPLE_MASK << EVENT_SAMPLE_SHIFT) | \ + (EVENT_CACHE_SEL_MASK << EVENT_CACHE_SEL_SHIFT) | \ + (EVENT_PMC_MASK << EVENT_PMC_SHIFT) | \ + (EVENT_UNIT_MASK << EVENT_UNIT_SHIFT) | \ + (EVENT_COMBINE_MASK << EVENT_COMBINE_SHIFT) | \ + (EVENT_MARKED_MASK << EVENT_MARKED_SHIFT) | \ + EVENT_PSEL_MASK) + /* MMCRA IFM bits - POWER8 */ #define POWER8_MMCRA_IFM1 0x0000000040000000UL #define POWER8_MMCRA_IFM2 0x0000000080000000UL @@ -212,6 +222,9 @@ static int power8_get_constraint(u64 event, unsigned long *maskp, unsigned long mask = value = 0; + if (event & ~EVENT_VALID_MASK) + return -1; + pmc = (event >> EVENT_PMC_SHIFT) & EVENT_PMC_MASK; unit = (event >> EVENT_UNIT_SHIFT) & EVENT_UNIT_MASK; cache = (event >> EVENT_CACHE_SEL_SHIFT) & EVENT_CACHE_SEL_MASK; -- cgit v1.2.3 From 378a6ee99e4a431ec84e4e61893445c041c93007 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Fri, 28 Jun 2013 18:15:11 +1000 Subject: powerpc/perf: Rework disable logic in pmu_disable() In pmu_disable() we disable the PMU by setting the FC (Freeze Counters) bit in MMCR0. In order to do this we have to read/modify/write MMCR0. It's possible that we read a value from MMCR0 which has PMAO (PMU Alert Occurred) set. When we write that value back it will cause an interrupt to occur. We will then end up in the PMU interrupt handler even though we are supposed to have just disabled the PMU. We can avoid this by making sure we never write PMAO back. We should not lose interrupts because when the PMU is re-enabled the overflowed values will cause another interrupt. We also reorder the clearing of SAMPLE_ENABLE so that is done after the PMU is frozen. Otherwise there is a small window between the clearing of SAMPLE_ENABLE and the setting of FC where we could take an interrupt and incorrectly see SAMPLE_ENABLE not set. This would for example change the logic in perf_read_regs(). Signed-off-by: Michael Ellerman CC: [v3.10] Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/perf/core-book3s.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c index af94a717c451..5d502bf374ea 100644 --- a/arch/powerpc/perf/core-book3s.c +++ b/arch/powerpc/perf/core-book3s.c @@ -75,6 +75,7 @@ static unsigned int freeze_events_kernel = MMCR0_FCS; #define MMCR0_FCHV 0 #define MMCR0_PMCjCE MMCR0_PMCnCE +#define MMCR0_PMAO 0 #define SPRN_MMCRA SPRN_MMCR2 #define MMCRA_SAMPLE_ENABLE 0 @@ -852,7 +853,7 @@ static void write_mmcr0(struct cpu_hw_events *cpuhw, unsigned long mmcr0) static void power_pmu_disable(struct pmu *pmu) { struct cpu_hw_events *cpuhw; - unsigned long flags; + unsigned long flags, val; if (!ppmu) return; @@ -860,9 +861,6 @@ static void power_pmu_disable(struct pmu *pmu) cpuhw = &__get_cpu_var(cpu_hw_events); if (!cpuhw->disabled) { - cpuhw->disabled = 1; - cpuhw->n_added = 0; - /* * Check if we ever enabled the PMU on this cpu. */ @@ -871,6 +869,21 @@ static void power_pmu_disable(struct pmu *pmu) cpuhw->pmcs_enabled = 1; } + /* + * Set the 'freeze counters' bit, clear PMAO. + */ + val = mfspr(SPRN_MMCR0); + val |= MMCR0_FC; + val &= ~MMCR0_PMAO; + + /* + * The barrier is to make sure the mtspr has been + * executed and the PMU has frozen the events etc. + * before we return. + */ + write_mmcr0(cpuhw, val); + mb(); + /* * Disable instruction sampling if it was enabled */ @@ -880,14 +893,8 @@ static void power_pmu_disable(struct pmu *pmu) mb(); } - /* - * Set the 'freeze counters' bit. - * The barrier is to make sure the mtspr has been - * executed and the PMU has frozen the events - * before we return. - */ - write_mmcr0(cpuhw, mfspr(SPRN_MMCR0) | MMCR0_FC); - mb(); + cpuhw->disabled = 1; + cpuhw->n_added = 0; } local_irq_restore(flags); } -- cgit v1.2.3 From 7a7a41f9d5b28ac3a916b057a7d3cd3f435ee9a6 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Fri, 28 Jun 2013 18:15:12 +1000 Subject: powerpc/perf: Freeze PMC5/6 if we're not using them On Power8 we can freeze PMC5 and 6 if we're not using them. Normally they run all the time. As noticed by Anshuman, we should unfreeze them when we disable the PMU as there are legacy tools which expect them to run all the time. Signed-off-by: Michael Ellerman CC: [v3.10] Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/reg.h | 1 + arch/powerpc/perf/core-book3s.c | 5 +++-- arch/powerpc/perf/power8-pmu.c | 4 ++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h index 4a9e408644fe..362142b69d5b 100644 --- a/arch/powerpc/include/asm/reg.h +++ b/arch/powerpc/include/asm/reg.h @@ -626,6 +626,7 @@ #define MMCR0_TRIGGER 0x00002000UL /* TRIGGER enable */ #define MMCR0_PMAO 0x00000080UL /* performance monitor alert has occurred, set to 0 after handling exception */ #define MMCR0_SHRFC 0x00000040UL /* SHRre freeze conditions between threads */ +#define MMCR0_FC56 0x00000010UL /* freeze counters 5 and 6 */ #define MMCR0_FCTI 0x00000008UL /* freeze counters in tags inactive mode */ #define MMCR0_FCTA 0x00000004UL /* freeze counters in tags active mode */ #define MMCR0_FCWAIT 0x00000002UL /* freeze counter in WAIT state */ diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c index 5d502bf374ea..517a1350b09c 100644 --- a/arch/powerpc/perf/core-book3s.c +++ b/arch/powerpc/perf/core-book3s.c @@ -75,6 +75,7 @@ static unsigned int freeze_events_kernel = MMCR0_FCS; #define MMCR0_FCHV 0 #define MMCR0_PMCjCE MMCR0_PMCnCE +#define MMCR0_FC56 0 #define MMCR0_PMAO 0 #define SPRN_MMCRA SPRN_MMCR2 @@ -870,11 +871,11 @@ static void power_pmu_disable(struct pmu *pmu) } /* - * Set the 'freeze counters' bit, clear PMAO. + * Set the 'freeze counters' bit, clear PMAO/FC56. */ val = mfspr(SPRN_MMCR0); val |= MMCR0_FC; - val &= ~MMCR0_PMAO; + val &= ~(MMCR0_PMAO | MMCR0_FC56); /* * The barrier is to make sure the mtspr has been diff --git a/arch/powerpc/perf/power8-pmu.c b/arch/powerpc/perf/power8-pmu.c index 84cdc6d892e3..d59f5b2d4c2f 100644 --- a/arch/powerpc/perf/power8-pmu.c +++ b/arch/powerpc/perf/power8-pmu.c @@ -391,6 +391,10 @@ static int power8_compute_mmcr(u64 event[], int n_ev, if (pmc_inuse & 0x7c) mmcr[0] |= MMCR0_PMCjCE; + /* If we're not using PMC 5 or 6, freeze them */ + if (!(pmc_inuse & 0x60)) + mmcr[0] |= MMCR0_FC56; + mmcr[1] = mmcr1; mmcr[2] = mmcra; -- cgit v1.2.3 From 0a48843d6c5114cfa4a9540ee4d6af87628cec01 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Fri, 28 Jun 2013 18:15:13 +1000 Subject: powerpc/perf: Use existing out label in power_pmu_enable() In power_pmu_enable() we can use the existing out label to reduce the number of return paths. Signed-off-by: Michael Ellerman CC: [v3.10] Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/perf/core-book3s.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c index 517a1350b09c..1bb26d586e3c 100644 --- a/arch/powerpc/perf/core-book3s.c +++ b/arch/powerpc/perf/core-book3s.c @@ -919,12 +919,13 @@ static void power_pmu_enable(struct pmu *pmu) if (!ppmu) return; + local_irq_save(flags); + cpuhw = &__get_cpu_var(cpu_hw_events); - if (!cpuhw->disabled) { - local_irq_restore(flags); - return; - } + if (!cpuhw->disabled) + goto out; + cpuhw->disabled = 0; /* -- cgit v1.2.3 From 4ea355b5368bde0574c12430df53334c4be3bdcf Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Fri, 28 Jun 2013 18:15:14 +1000 Subject: powerpc/perf: Don't enable if we have zero events In power_pmu_enable() we still enable the PMU even if we have zero events. This should have no effect but doesn't make much sense. Instead just return after telling the hypervisor that we are not using the PMCs. Signed-off-by: Michael Ellerman CC: [v3.10] Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/perf/core-book3s.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c index 1bb26d586e3c..c91dc43e04de 100644 --- a/arch/powerpc/perf/core-book3s.c +++ b/arch/powerpc/perf/core-book3s.c @@ -926,6 +926,11 @@ static void power_pmu_enable(struct pmu *pmu) if (!cpuhw->disabled) goto out; + if (cpuhw->n_events == 0) { + ppc_set_pmu_inuse(0); + goto out; + } + cpuhw->disabled = 0; /* @@ -937,8 +942,6 @@ static void power_pmu_enable(struct pmu *pmu) if (!cpuhw->n_added) { mtspr(SPRN_MMCRA, cpuhw->mmcr[2] & ~MMCRA_SAMPLE_ENABLE); mtspr(SPRN_MMCR1, cpuhw->mmcr[1]); - if (cpuhw->n_events == 0) - ppc_set_pmu_inuse(0); goto out_enable; } -- cgit v1.2.3 From 2ac138ca21ad26c988ce7c91d27327f85beb7519 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Fri, 28 Jun 2013 18:15:15 +1000 Subject: powerpc/perf: Drop MMCRA from thread_struct In commit 59affcd "Context switch more PMU related SPRs" I added more PMU SPRs to thread_struct, later modified in commit b11ae95. To add insult to injury it turns out we don't need to switch MMCRA as it's only user readable, and the value is recomputed by the PMU code. Signed-off-by: Michael Ellerman Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/processor.h | 1 - arch/powerpc/kernel/asm-offsets.c | 1 - 2 files changed, 2 deletions(-) diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h index 9fe1129efa02..3f19df3cc7a3 100644 --- a/arch/powerpc/include/asm/processor.h +++ b/arch/powerpc/include/asm/processor.h @@ -289,7 +289,6 @@ struct thread_struct { unsigned long sier; unsigned long mmcr0; unsigned long mmcr2; - unsigned long mmcra; #endif }; diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index dc684b1af1c4..c7e8afc2ead0 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -132,7 +132,6 @@ int main(void) DEFINE(THREAD_SIER, offsetof(struct thread_struct, sier)); DEFINE(THREAD_MMCR0, offsetof(struct thread_struct, mmcr0)); DEFINE(THREAD_MMCR2, offsetof(struct thread_struct, mmcr2)); - DEFINE(THREAD_MMCRA, offsetof(struct thread_struct, mmcra)); #endif #ifdef CONFIG_PPC_TRANSACTIONAL_MEM DEFINE(PACATMSCRATCH, offsetof(struct paca_struct, tm_scratch)); -- cgit v1.2.3 From 330a1eb7775ba876dbd46b9885556e57f705e3d4 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Fri, 28 Jun 2013 18:15:16 +1000 Subject: powerpc/perf: Core EBB support for 64-bit book3s Add support for EBB (Event Based Branches) on 64-bit book3s. See the included documentation for more details. EBBs are a feature which allows the hardware to branch directly to a specified user space address when a PMU event overflows. This can be used by programs for self-monitoring with no kernel involvement in the inner loop. Most of the logic is in the generic book3s code, primarily to avoid a proliferation of PMU callbacks. Signed-off-by: Michael Ellerman Signed-off-by: Benjamin Herrenschmidt --- Documentation/powerpc/00-INDEX | 2 + Documentation/powerpc/pmu-ebb.txt | 137 +++++++++++++++++++++++ arch/powerpc/include/asm/perf_event_server.h | 6 + arch/powerpc/include/asm/processor.h | 3 +- arch/powerpc/include/asm/reg.h | 8 ++ arch/powerpc/include/asm/switch_to.h | 14 +++ arch/powerpc/kernel/process.c | 4 + arch/powerpc/perf/core-book3s.c | 161 ++++++++++++++++++++++++--- 8 files changed, 321 insertions(+), 14 deletions(-) create mode 100644 Documentation/powerpc/pmu-ebb.txt diff --git a/Documentation/powerpc/00-INDEX b/Documentation/powerpc/00-INDEX index dd9e92802ec0..05026ce1875e 100644 --- a/Documentation/powerpc/00-INDEX +++ b/Documentation/powerpc/00-INDEX @@ -14,6 +14,8 @@ hvcs.txt - IBM "Hypervisor Virtual Console Server" Installation Guide mpc52xx.txt - Linux 2.6.x on MPC52xx family +pmu-ebb.txt + - Description of the API for using the PMU with Event Based Branches. qe_firmware.txt - describes the layout of firmware binaries for the Freescale QUICC Engine and the code that parses and uploads the microcode therein. diff --git a/Documentation/powerpc/pmu-ebb.txt b/Documentation/powerpc/pmu-ebb.txt new file mode 100644 index 000000000000..73cd163dbfb8 --- /dev/null +++ b/Documentation/powerpc/pmu-ebb.txt @@ -0,0 +1,137 @@ +PMU Event Based Branches +======================== + +Event Based Branches (EBBs) are a feature which allows the hardware to +branch directly to a specified user space address when certain events occur. + +The full specification is available in Power ISA v2.07: + + https://www.power.org/documentation/power-isa-version-2-07/ + +One type of event for which EBBs can be configured is PMU exceptions. This +document describes the API for configuring the Power PMU to generate EBBs, +using the Linux perf_events API. + + +Terminology +----------- + +Throughout this document we will refer to an "EBB event" or "EBB events". This +just refers to a struct perf_event which has set the "EBB" flag in its +attr.config. All events which can be configured on the hardware PMU are +possible "EBB events". + + +Background +---------- + +When a PMU EBB occurs it is delivered to the currently running process. As such +EBBs can only sensibly be used by programs for self-monitoring. + +It is a feature of the perf_events API that events can be created on other +processes, subject to standard permission checks. This is also true of EBB +events, however unless the target process enables EBBs (via mtspr(BESCR)) no +EBBs will ever be delivered. + +This makes it possible for a process to enable EBBs for itself, but not +actually configure any events. At a later time another process can come along +and attach an EBB event to the process, which will then cause EBBs to be +delivered to the first process. It's not clear if this is actually useful. + + +When the PMU is configured for EBBs, all PMU interrupts are delivered to the +user process. This means once an EBB event is scheduled on the PMU, no non-EBB +events can be configured. This means that EBB events can not be run +concurrently with regular 'perf' commands, or any other perf events. + +It is however safe to run 'perf' commands on a process which is using EBBs. The +kernel will in general schedule the EBB event, and perf will be notified that +its events could not run. + +The exclusion between EBB events and regular events is implemented using the +existing "pinned" and "exclusive" attributes of perf_events. This means EBB +events will be given priority over other events, unless they are also pinned. +If an EBB event and a regular event are both pinned, then whichever is enabled +first will be scheduled and the other will be put in error state. See the +section below titled "Enabling an EBB event" for more information. + + +Creating an EBB event +--------------------- + +To request that an event is counted using EBB, the event code should have bit +63 set. + +EBB events must be created with a particular, and restrictive, set of +attributes - this is so that they interoperate correctly with the rest of the +perf_events subsystem. + +An EBB event must be created with the "pinned" and "exclusive" attributes set. +Note that if you are creating a group of EBB events, only the leader can have +these attributes set. + +An EBB event must NOT set any of the "inherit", "sample_period", "freq" or +"enable_on_exec" attributes. + +An EBB event must be attached to a task. This is specified to perf_event_open() +by passing a pid value, typically 0 indicating the current task. + +All events in a group must agree on whether they want EBB. That is all events +must request EBB, or none may request EBB. + +EBB events must specify the PMC they are to be counted on. This ensures +userspace is able to reliably determine which PMC the event is scheduled on. + + +Enabling an EBB event +--------------------- + +Once an EBB event has been successfully opened, it must be enabled with the +perf_events API. This can be achieved either via the ioctl() interface, or the +prctl() interface. + +However, due to the design of the perf_events API, enabling an event does not +guarantee that it has been scheduled on the PMU. To ensure that the EBB event +has been scheduled on the PMU, you must perform a read() on the event. If the +read() returns EOF, then the event has not been scheduled and EBBs are not +enabled. + +This behaviour occurs because the EBB event is pinned and exclusive. When the +EBB event is enabled it will force all other non-pinned events off the PMU. In +this case the enable will be successful. However if there is already an event +pinned on the PMU then the enable will not be successful. + + +Reading an EBB event +-------------------- + +It is possible to read() from an EBB event. However the results are +meaningless. Because interrupts are being delivered to the user process the +kernel is not able to count the event, and so will return a junk value. + + +Closing an EBB event +-------------------- + +When an EBB event is finished with, you can close it using close() as for any +regular event. If this is the last EBB event the PMU will be deconfigured and +no further PMU EBBs will be delivered. + + +EBB Handler +----------- + +The EBB handler is just regular userspace code, however it must be written in +the style of an interrupt handler. When the handler is entered all registers +are live (possibly) and so must be saved somehow before the handler can invoke +other code. + +It's up to the program how to handle this. For C programs a relatively simple +option is to create an interrupt frame on the stack and save registers there. + +Fork +---- + +EBB events are not inherited across fork. If the child process wishes to use +EBBs it should open a new event for itself. Similarly the EBB state in +BESCR/EBBHR/EBBRR is cleared across fork(). diff --git a/arch/powerpc/include/asm/perf_event_server.h b/arch/powerpc/include/asm/perf_event_server.h index f265049dd7d6..2dd7bfc459be 100644 --- a/arch/powerpc/include/asm/perf_event_server.h +++ b/arch/powerpc/include/asm/perf_event_server.h @@ -60,6 +60,7 @@ struct power_pmu { #define PPMU_HAS_SSLOT 0x00000020 /* Has sampled slot in MMCRA */ #define PPMU_HAS_SIER 0x00000040 /* Has SIER */ #define PPMU_BHRB 0x00000080 /* has BHRB feature enabled */ +#define PPMU_EBB 0x00000100 /* supports event based branch */ /* * Values for flags to get_alternatives() @@ -68,6 +69,11 @@ struct power_pmu { #define PPMU_LIMITED_PMC_REQD 2 /* have to put this on a limited PMC */ #define PPMU_ONLY_COUNT_RUN 4 /* only counting in run state */ +/* + * We use the event config bit 63 as a flag to request EBB. + */ +#define EVENT_CONFIG_EBB_SHIFT 63 + extern int register_power_pmu(struct power_pmu *); struct pt_regs; diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h index 3f19df3cc7a3..47a35b08b963 100644 --- a/arch/powerpc/include/asm/processor.h +++ b/arch/powerpc/include/asm/processor.h @@ -287,8 +287,9 @@ struct thread_struct { unsigned long siar; unsigned long sdar; unsigned long sier; - unsigned long mmcr0; unsigned long mmcr2; + unsigned mmcr0; + unsigned used_ebb; #endif }; diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h index 362142b69d5b..5d7d9c2a5473 100644 --- a/arch/powerpc/include/asm/reg.h +++ b/arch/powerpc/include/asm/reg.h @@ -621,6 +621,9 @@ #define MMCR0_PMXE 0x04000000UL /* performance monitor exception enable */ #define MMCR0_FCECE 0x02000000UL /* freeze ctrs on enabled cond or event */ #define MMCR0_TBEE 0x00400000UL /* time base exception enable */ +#define MMCR0_EBE 0x00100000UL /* Event based branch enable */ +#define MMCR0_PMCC 0x000c0000UL /* PMC control */ +#define MMCR0_PMCC_U6 0x00080000UL /* PMC1-6 are R/W by user (PR) */ #define MMCR0_PMC1CE 0x00008000UL /* PMC1 count enable*/ #define MMCR0_PMCjCE 0x00004000UL /* PMCj count enable*/ #define MMCR0_TRIGGER 0x00002000UL /* TRIGGER enable */ @@ -674,6 +677,11 @@ #define SIER_SIAR_VALID 0x0400000 /* SIAR contents valid */ #define SIER_SDAR_VALID 0x0200000 /* SDAR contents valid */ +/* When EBB is enabled, some of MMCR0/MMCR2/SIER are user accessible */ +#define MMCR0_USER_MASK (MMCR0_FC | MMCR0_PMXE | MMCR0_PMAO) +#define MMCR2_USER_MASK 0x4020100804020000UL /* (FC1P|FC2P|FC3P|FC4P|FC5P|FC6P) */ +#define SIER_USER_MASK 0x7fffffUL + #define SPRN_PA6T_MMCR0 795 #define PA6T_MMCR0_EN0 0x0000000000000001UL #define PA6T_MMCR0_EN1 0x0000000000000002UL diff --git a/arch/powerpc/include/asm/switch_to.h b/arch/powerpc/include/asm/switch_to.h index 200d763a0a67..49a13e0ef234 100644 --- a/arch/powerpc/include/asm/switch_to.h +++ b/arch/powerpc/include/asm/switch_to.h @@ -67,4 +67,18 @@ static inline void flush_spe_to_thread(struct task_struct *t) } #endif +static inline void clear_task_ebb(struct task_struct *t) +{ +#ifdef CONFIG_PPC_BOOK3S_64 + /* EBB perf events are not inherited, so clear all EBB state. */ + t->thread.bescr = 0; + t->thread.mmcr2 = 0; + t->thread.mmcr0 = 0; + t->thread.siar = 0; + t->thread.sdar = 0; + t->thread.sier = 0; + t->thread.used_ebb = 0; +#endif +} + #endif /* _ASM_POWERPC_SWITCH_TO_H */ diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index b0f3e3f77e72..f8a76e6207bd 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -916,7 +916,11 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src) flush_altivec_to_thread(src); flush_vsx_to_thread(src); flush_spe_to_thread(src); + *dst = *src; + + clear_task_ebb(dst); + return 0; } diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c index c91dc43e04de..a3985aee77fe 100644 --- a/arch/powerpc/perf/core-book3s.c +++ b/arch/powerpc/perf/core-book3s.c @@ -77,6 +77,9 @@ static unsigned int freeze_events_kernel = MMCR0_FCS; #define MMCR0_PMCjCE MMCR0_PMCnCE #define MMCR0_FC56 0 #define MMCR0_PMAO 0 +#define MMCR0_EBE 0 +#define MMCR0_PMCC 0 +#define MMCR0_PMCC_U6 0 #define SPRN_MMCRA SPRN_MMCR2 #define MMCRA_SAMPLE_ENABLE 0 @@ -104,6 +107,15 @@ static inline int siar_valid(struct pt_regs *regs) return 1; } +static bool is_ebb_event(struct perf_event *event) { return false; } +static int ebb_event_check(struct perf_event *event) { return 0; } +static void ebb_event_add(struct perf_event *event) { } +static void ebb_switch_out(unsigned long mmcr0) { } +static unsigned long ebb_switch_in(bool ebb, unsigned long mmcr0) +{ + return mmcr0; +} + static inline void power_pmu_bhrb_enable(struct perf_event *event) {} static inline void power_pmu_bhrb_disable(struct perf_event *event) {} void power_pmu_flush_branch_stack(void) {} @@ -464,6 +476,89 @@ void power_pmu_bhrb_read(struct cpu_hw_events *cpuhw) return; } +static bool is_ebb_event(struct perf_event *event) +{ + /* + * This could be a per-PMU callback, but we'd rather avoid the cost. We + * check that the PMU supports EBB, meaning those that don't can still + * use bit 63 of the event code for something else if they wish. + */ + return (ppmu->flags & PPMU_EBB) && + ((event->attr.config >> EVENT_CONFIG_EBB_SHIFT) & 1); +} + +static int ebb_event_check(struct perf_event *event) +{ + struct perf_event *leader = event->group_leader; + + /* Event and group leader must agree on EBB */ + if (is_ebb_event(leader) != is_ebb_event(event)) + return -EINVAL; + + if (is_ebb_event(event)) { + if (!(event->attach_state & PERF_ATTACH_TASK)) + return -EINVAL; + + if (!leader->attr.pinned || !leader->attr.exclusive) + return -EINVAL; + + if (event->attr.inherit || event->attr.sample_period || + event->attr.enable_on_exec || event->attr.freq) + return -EINVAL; + } + + return 0; +} + +static void ebb_event_add(struct perf_event *event) +{ + if (!is_ebb_event(event) || current->thread.used_ebb) + return; + + /* + * IFF this is the first time we've added an EBB event, set + * PMXE in the user MMCR0 so we can detect when it's cleared by + * userspace. We need this so that we can context switch while + * userspace is in the EBB handler (where PMXE is 0). + */ + current->thread.used_ebb = 1; + current->thread.mmcr0 |= MMCR0_PMXE; +} + +static void ebb_switch_out(unsigned long mmcr0) +{ + if (!(mmcr0 & MMCR0_EBE)) + return; + + current->thread.siar = mfspr(SPRN_SIAR); + current->thread.sier = mfspr(SPRN_SIER); + current->thread.sdar = mfspr(SPRN_SDAR); + current->thread.mmcr0 = mmcr0 & MMCR0_USER_MASK; + current->thread.mmcr2 = mfspr(SPRN_MMCR2) & MMCR2_USER_MASK; +} + +static unsigned long ebb_switch_in(bool ebb, unsigned long mmcr0) +{ + if (!ebb) + goto out; + + /* Enable EBB and read/write to all 6 PMCs for userspace */ + mmcr0 |= MMCR0_EBE | MMCR0_PMCC_U6; + + /* Add any bits from the user reg, FC or PMAO */ + mmcr0 |= current->thread.mmcr0; + + /* Be careful not to set PMXE if userspace had it cleared */ + if (!(current->thread.mmcr0 & MMCR0_PMXE)) + mmcr0 &= ~MMCR0_PMXE; + + mtspr(SPRN_SIAR, current->thread.siar); + mtspr(SPRN_SIER, current->thread.sier); + mtspr(SPRN_SDAR, current->thread.sdar); + mtspr(SPRN_MMCR2, current->thread.mmcr2); +out: + return mmcr0; +} #endif /* CONFIG_PPC64 */ static void perf_event_interrupt(struct pt_regs *regs); @@ -734,6 +829,13 @@ static void power_pmu_read(struct perf_event *event) if (!event->hw.idx) return; + + if (is_ebb_event(event)) { + val = read_pmc(event->hw.idx); + local64_set(&event->hw.prev_count, val); + return; + } + /* * Performance monitor interrupts come even when interrupts * are soft-disabled, as long as interrupts are hard-enabled. @@ -854,7 +956,7 @@ static void write_mmcr0(struct cpu_hw_events *cpuhw, unsigned long mmcr0) static void power_pmu_disable(struct pmu *pmu) { struct cpu_hw_events *cpuhw; - unsigned long flags, val; + unsigned long flags, mmcr0, val; if (!ppmu) return; @@ -871,11 +973,11 @@ static void power_pmu_disable(struct pmu *pmu) } /* - * Set the 'freeze counters' bit, clear PMAO/FC56. + * Set the 'freeze counters' bit, clear EBE/PMCC/PMAO/FC56. */ - val = mfspr(SPRN_MMCR0); + val = mmcr0 = mfspr(SPRN_MMCR0); val |= MMCR0_FC; - val &= ~(MMCR0_PMAO | MMCR0_FC56); + val &= ~(MMCR0_EBE | MMCR0_PMCC | MMCR0_PMAO | MMCR0_FC56); /* * The barrier is to make sure the mtspr has been @@ -896,7 +998,10 @@ static void power_pmu_disable(struct pmu *pmu) cpuhw->disabled = 1; cpuhw->n_added = 0; + + ebb_switch_out(mmcr0); } + local_irq_restore(flags); } @@ -911,15 +1016,15 @@ static void power_pmu_enable(struct pmu *pmu) struct cpu_hw_events *cpuhw; unsigned long flags; long i; - unsigned long val; + unsigned long val, mmcr0; s64 left; unsigned int hwc_index[MAX_HWEVENTS]; int n_lim; int idx; + bool ebb; if (!ppmu) return; - local_irq_save(flags); cpuhw = &__get_cpu_var(cpu_hw_events); @@ -933,6 +1038,13 @@ static void power_pmu_enable(struct pmu *pmu) cpuhw->disabled = 0; + /* + * EBB requires an exclusive group and all events must have the EBB + * flag set, or not set, so we can just check a single event. Also we + * know we have at least one event. + */ + ebb = is_ebb_event(cpuhw->event[0]); + /* * If we didn't change anything, or only removed events, * no need to recalculate MMCR* settings and reset the PMCs. @@ -1008,25 +1120,34 @@ static void power_pmu_enable(struct pmu *pmu) ++n_lim; continue; } - val = 0; - if (event->hw.sample_period) { - left = local64_read(&event->hw.period_left); - if (left < 0x80000000L) - val = 0x80000000L - left; + + if (ebb) + val = local64_read(&event->hw.prev_count); + else { + val = 0; + if (event->hw.sample_period) { + left = local64_read(&event->hw.period_left); + if (left < 0x80000000L) + val = 0x80000000L - left; + } + local64_set(&event->hw.prev_count, val); } - local64_set(&event->hw.prev_count, val); + event->hw.idx = idx; if (event->hw.state & PERF_HES_STOPPED) val = 0; write_pmc(idx, val); + perf_event_update_userpage(event); } cpuhw->n_limited = n_lim; cpuhw->mmcr[0] |= MMCR0_PMXE | MMCR0_FCECE; out_enable: + mmcr0 = ebb_switch_in(ebb, cpuhw->mmcr[0]); + mb(); - write_mmcr0(cpuhw, cpuhw->mmcr[0]); + write_mmcr0(cpuhw, mmcr0); /* * Enable instruction sampling if necessary @@ -1124,6 +1245,8 @@ static int power_pmu_add(struct perf_event *event, int ef_flags) event->hw.config = cpuhw->events[n0]; nocheck: + ebb_event_add(event); + ++cpuhw->n_events; ++cpuhw->n_added; @@ -1484,6 +1607,11 @@ static int power_pmu_event_init(struct perf_event *event) } } + /* Extra checks for EBB */ + err = ebb_event_check(event); + if (err) + return err; + /* * If this is in a group, check if it can go on with all the * other hardware events in the group. We assume the event @@ -1522,6 +1650,13 @@ static int power_pmu_event_init(struct perf_event *event) event->hw.last_period = event->hw.sample_period; local64_set(&event->hw.period_left, event->hw.last_period); + /* + * For EBB events we just context switch the PMC value, we don't do any + * of the sample_period logic. We use hw.prev_count for this. + */ + if (is_ebb_event(event)) + local64_set(&event->hw.prev_count, 0); + /* * See if we need to reserve the PMU. * If no events are currently in use, then we have to take a -- cgit v1.2.3 From 4df489991182d3a9337c0a4b1563077c0004f1ba Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Fri, 28 Jun 2013 18:15:17 +1000 Subject: powerpc/perf: Add power8 EBB support Add logic to the power8 PMU code to support EBB. Future processors would also be expected to implement similar constraints. At that time we could possibly factor these out into common code. Finally mark the power8 PMU as supporting EBB, which is the actual enable switch which allows EBBs to be configured. Signed-off-by: Michael Ellerman Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/perf/power8-pmu.c | 45 +++++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/arch/powerpc/perf/power8-pmu.c b/arch/powerpc/perf/power8-pmu.c index d59f5b2d4c2f..96a64d6a8bdf 100644 --- a/arch/powerpc/perf/power8-pmu.c +++ b/arch/powerpc/perf/power8-pmu.c @@ -31,9 +31,9 @@ * * 60 56 52 48 44 40 36 32 * | - - - - | - - - - | - - - - | - - - - | - - - - | - - - - | - - - - | - - - - | - * [ thresh_cmp ] [ thresh_ctl ] - * | - * thresh start/stop OR FAB match -* + * | [ thresh_cmp ] [ thresh_ctl ] + * | | + * *- EBB (Linux) thresh start/stop OR FAB match -* * * 28 24 20 16 12 8 4 0 * | - - - - | - - - - | - - - - | - - - - | - - - - | - - - - | - - - - | - - - - | @@ -85,6 +85,7 @@ * */ +#define EVENT_EBB_MASK 1ull #define EVENT_THR_CMP_SHIFT 40 /* Threshold CMP value */ #define EVENT_THR_CMP_MASK 0x3ff #define EVENT_THR_CTL_SHIFT 32 /* Threshold control value (start/stop) */ @@ -117,6 +118,7 @@ (EVENT_UNIT_MASK << EVENT_UNIT_SHIFT) | \ (EVENT_COMBINE_MASK << EVENT_COMBINE_SHIFT) | \ (EVENT_MARKED_MASK << EVENT_MARKED_SHIFT) | \ + (EVENT_EBB_MASK << EVENT_CONFIG_EBB_SHIFT) | \ EVENT_PSEL_MASK) /* MMCRA IFM bits - POWER8 */ @@ -140,10 +142,10 @@ * * 28 24 20 16 12 8 4 0 * | - - - - | - - - - | - - - - | - - - - | - - - - | - - - - | - - - - | - - - - | - * [ ] [ sample ] [ ] [6] [5] [4] [3] [2] [1] - * | | - * L1 I/D qualifier -* | Count of events for each PMC. - * | p1, p2, p3, p4, p5, p6. + * | [ ] [ sample ] [ ] [6] [5] [4] [3] [2] [1] + * EBB -* | | + * | | Count of events for each PMC. + * L1 I/D qualifier -* | p1, p2, p3, p4, p5, p6. * nc - number of counters -* * * The PMC fields P1..P6, and NC, are adder fields. As we accumulate constraints @@ -159,6 +161,9 @@ #define CNST_THRESH_VAL(v) (((v) & EVENT_THRESH_MASK) << 32) #define CNST_THRESH_MASK CNST_THRESH_VAL(EVENT_THRESH_MASK) +#define CNST_EBB_VAL(v) (((v) & EVENT_EBB_MASK) << 24) +#define CNST_EBB_MASK CNST_EBB_VAL(EVENT_EBB_MASK) + #define CNST_L1_QUAL_VAL(v) (((v) & 3) << 22) #define CNST_L1_QUAL_MASK CNST_L1_QUAL_VAL(3) @@ -217,7 +222,7 @@ static inline bool event_is_fab_match(u64 event) static int power8_get_constraint(u64 event, unsigned long *maskp, unsigned long *valp) { - unsigned int unit, pmc, cache; + unsigned int unit, pmc, cache, ebb; unsigned long mask, value; mask = value = 0; @@ -225,9 +230,13 @@ static int power8_get_constraint(u64 event, unsigned long *maskp, unsigned long if (event & ~EVENT_VALID_MASK) return -1; - pmc = (event >> EVENT_PMC_SHIFT) & EVENT_PMC_MASK; - unit = (event >> EVENT_UNIT_SHIFT) & EVENT_UNIT_MASK; - cache = (event >> EVENT_CACHE_SEL_SHIFT) & EVENT_CACHE_SEL_MASK; + pmc = (event >> EVENT_PMC_SHIFT) & EVENT_PMC_MASK; + unit = (event >> EVENT_UNIT_SHIFT) & EVENT_UNIT_MASK; + cache = (event >> EVENT_CACHE_SEL_SHIFT) & EVENT_CACHE_SEL_MASK; + ebb = (event >> EVENT_CONFIG_EBB_SHIFT) & EVENT_EBB_MASK; + + /* Clear the EBB bit in the event, so event checks work below */ + event &= ~(EVENT_EBB_MASK << EVENT_CONFIG_EBB_SHIFT); if (pmc) { if (pmc > 6) @@ -297,6 +306,18 @@ static int power8_get_constraint(u64 event, unsigned long *maskp, unsigned long value |= CNST_THRESH_VAL(event >> EVENT_THRESH_SHIFT); } + if (!pmc && ebb) + /* EBB events must specify the PMC */ + return -1; + + /* + * All events must agree on EBB, either all request it or none. + * EBB events are pinned & exclusive, so this should never actually + * hit, but we leave it as a fallback in case. + */ + mask |= CNST_EBB_VAL(ebb); + value |= CNST_EBB_MASK; + *maskp = mask; *valp = value; @@ -591,7 +612,7 @@ static struct power_pmu power8_pmu = { .get_constraint = power8_get_constraint, .get_alternatives = power8_get_alternatives, .disable_pmc = power8_disable_pmc, - .flags = PPMU_HAS_SSLOT | PPMU_HAS_SIER | PPMU_BHRB, + .flags = PPMU_HAS_SSLOT | PPMU_HAS_SIER | PPMU_BHRB | PPMU_EBB, .n_generic = ARRAY_SIZE(power8_generic_events), .generic_events = power8_generic_events, .attr_groups = power8_pmu_attr_groups, -- cgit v1.2.3 From 6e0b8bc965d25a8e0701eaca3fca5941b4f4b2b2 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Fri, 28 Jun 2013 18:15:18 +1000 Subject: powerpc/pseries: Inform the hypervisor we are using EBB regs On LPAR systems we need to inform the hypervisor that we are using the EBB registers. We do this by setting a bit in the Virtual Processor Area (VPA) - formerly known as the lppaca. For now we do this always, ie. we do not dynamically enable/disable. Signed-off-by: Michael Ellerman Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/lppaca.h | 3 ++- arch/powerpc/platforms/pseries/lpar.c | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/lppaca.h b/arch/powerpc/include/asm/lppaca.h index b1e7f2af1016..9b12f88d4adb 100644 --- a/arch/powerpc/include/asm/lppaca.h +++ b/arch/powerpc/include/asm/lppaca.h @@ -66,7 +66,8 @@ struct lppaca { u8 reserved6[48]; u8 cede_latency_hint; - u8 reserved7[7]; + u8 ebb_regs_in_use; + u8 reserved7[6]; u8 dtl_enable_mask; /* Dispatch Trace Log mask */ u8 donate_dedicated_cpu; /* Donate dedicated CPU cycles */ u8 fpregs_in_use; diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c index fd0f2f2a9b90..02d6e21619bb 100644 --- a/arch/powerpc/platforms/pseries/lpar.c +++ b/arch/powerpc/platforms/pseries/lpar.c @@ -71,6 +71,9 @@ void vpa_init(int cpu) if (cpu_has_feature(CPU_FTR_ALTIVEC)) lppaca_of(cpu).vmxregs_in_use = 1; + if (cpu_has_feature(CPU_FTR_ARCH_207S)) + lppaca_of(cpu).ebb_regs_in_use = 1; + addr = __pa(&lppaca_of(cpu)); ret = register_vpa(hwcpu, addr); -- cgit v1.2.3 From b56ece9a3ac3c9708b8f1cebf4ba24c258d40e52 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Sun, 30 Jun 2013 18:37:24 -0700 Subject: Input: add OLPC AP-SP driver The OLPC XO-1.75 and XO-4 laptops include a PS/2 touchpad and an AT keyboard, yet they do not have a hardware PS/2 controller. Instead, a firmware runs on a dedicated core ("Security Processor", part of the SoC) that acts as a PS/2 controller through bit-banging. Communication between the main cpu (Application Processor) and the Security Processor happens via a standard command mechanism implemented by the SoC. Add a driver for this interface to enable keyboard/mouse input on this platform. Original author: Saadia Baloch Signed-off-by: Daniel Drake Signed-off-by: Dmitry Torokhov --- .../devicetree/bindings/serio/olpc,ap-sp.txt | 13 + drivers/input/serio/Kconfig | 10 + drivers/input/serio/Makefile | 1 + drivers/input/serio/olpc_apsp.c | 287 +++++++++++++++++++++ 4 files changed, 311 insertions(+) create mode 100644 Documentation/devicetree/bindings/serio/olpc,ap-sp.txt create mode 100644 drivers/input/serio/olpc_apsp.c diff --git a/Documentation/devicetree/bindings/serio/olpc,ap-sp.txt b/Documentation/devicetree/bindings/serio/olpc,ap-sp.txt new file mode 100644 index 000000000000..0e72183f52bc --- /dev/null +++ b/Documentation/devicetree/bindings/serio/olpc,ap-sp.txt @@ -0,0 +1,13 @@ +OLPC AP-SP serio interface + +Required properties: +- compatible : "olpc,ap-sp" +- reg : base address and length of SoC's WTM registers +- interrupts : SP-AP interrupt + +Example: + ap-sp@d4290000 { + compatible = "olpc,ap-sp"; + reg = <0xd4290000 0x1000>; + interrupts = <40>; + } diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index aebfe3ecb945..d401a7dccaa7 100644 --- a/drivers/input/serio/Kconfig +++ b/drivers/input/serio/Kconfig @@ -255,4 +255,14 @@ config SERIO_APBPS2 To compile this driver as a module, choose M here: the module will be called apbps2. +config SERIO_OLPC_APSP + tristate "OLPC AP-SP input support" + depends on OF + help + Say Y here if you want support for the keyboard and touchpad included + in the OLPC XO-1.75 and XO-4 laptops. + + To compile this driver as a module, choose M here: the module will + be called olpc_apsp. + endif diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile index 8edb36c2cdb4..12298b1c0e71 100644 --- a/drivers/input/serio/Makefile +++ b/drivers/input/serio/Makefile @@ -27,3 +27,4 @@ obj-$(CONFIG_SERIO_XILINX_XPS_PS2) += xilinx_ps2.o obj-$(CONFIG_SERIO_ALTERA_PS2) += altera_ps2.o obj-$(CONFIG_SERIO_ARC_PS2) += arc_ps2.o obj-$(CONFIG_SERIO_APBPS2) += apbps2.o +obj-$(CONFIG_SERIO_OLPC_APSP) += olpc_apsp.o diff --git a/drivers/input/serio/olpc_apsp.c b/drivers/input/serio/olpc_apsp.c new file mode 100644 index 000000000000..818aa466b5d2 --- /dev/null +++ b/drivers/input/serio/olpc_apsp.c @@ -0,0 +1,287 @@ +/* + * OLPC serio driver for multiplexed input from Marvell MMP security processor + * + * Copyright (C) 2011-2013 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. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * The OLPC XO-1.75 and XO-4 laptops do not have a hardware PS/2 controller. + * Instead, the OLPC firmware runs a bit-banging PS/2 implementation on an + * otherwise-unused slow processor which is included in the Marvell MMP2/MMP3 + * SoC, known as the "Security Processor" (SP) or "Wireless Trusted Module" + * (WTM). This firmware then reports its results via the WTM registers, + * which we read from the Application Processor (AP, i.e. main CPU) in this + * driver. + * + * On the hardware side we have a PS/2 mouse and an AT keyboard, the data + * is multiplexed through this system. We create a serio port for each one, + * and demultiplex the data accordingly. + */ + +/* WTM register offsets */ +#define SECURE_PROCESSOR_COMMAND 0x40 +#define COMMAND_RETURN_STATUS 0x80 +#define COMMAND_FIFO_STATUS 0xc4 +#define PJ_RST_INTERRUPT 0xc8 +#define PJ_INTERRUPT_MASK 0xcc + +/* + * The upper byte of SECURE_PROCESSOR_COMMAND and COMMAND_RETURN_STATUS is + * used to identify which port (device) is being talked to. The lower byte + * is the data being sent/received. + */ +#define PORT_MASK 0xff00 +#define DATA_MASK 0x00ff +#define PORT_SHIFT 8 +#define KEYBOARD_PORT 0 +#define TOUCHPAD_PORT 1 + +/* COMMAND_FIFO_STATUS */ +#define CMD_CNTR_MASK 0x7 /* Number of pending/unprocessed commands */ +#define MAX_PENDING_CMDS 4 /* from device specs */ + +/* PJ_RST_INTERRUPT */ +#define SP_COMMAND_COMPLETE_RESET 0x1 + +/* PJ_INTERRUPT_MASK */ +#define INT_0 (1 << 0) + +/* COMMAND_FIFO_STATUS */ +#define CMD_STS_MASK 0x100 + +struct olpc_apsp { + struct device *dev; + struct serio *kbio; + struct serio *padio; + void __iomem *base; + int open_count; + int irq; +}; + +static int olpc_apsp_write(struct serio *port, unsigned char val) +{ + struct olpc_apsp *priv = port->port_data; + unsigned int i; + u32 which = 0; + + if (port == priv->padio) + which = TOUCHPAD_PORT << PORT_SHIFT; + else + which = KEYBOARD_PORT << PORT_SHIFT; + + dev_dbg(priv->dev, "olpc_apsp_write which=%x val=%x\n", which, val); + for (i = 0; i < 50; i++) { + u32 sts = readl(priv->base + COMMAND_FIFO_STATUS); + if ((sts & CMD_CNTR_MASK) < MAX_PENDING_CMDS) { + writel(which | val, + priv->base + SECURE_PROCESSOR_COMMAND); + return 0; + } + /* SP busy. This has not been seen in practice. */ + mdelay(1); + } + + dev_dbg(priv->dev, "olpc_apsp_write timeout, status=%x\n", + readl(priv->base + COMMAND_FIFO_STATUS)); + + return -ETIMEDOUT; +} + +static irqreturn_t olpc_apsp_rx(int irq, void *dev_id) +{ + struct olpc_apsp *priv = dev_id; + unsigned int w, tmp; + struct serio *serio; + + /* + * Write 1 to PJ_RST_INTERRUPT to acknowledge and clear the interrupt + * Write 0xff00 to SECURE_PROCESSOR_COMMAND. + */ + tmp = readl(priv->base + PJ_RST_INTERRUPT); + if (!(tmp & SP_COMMAND_COMPLETE_RESET)) { + dev_warn(priv->dev, "spurious interrupt?\n"); + return IRQ_NONE; + } + + w = readl(priv->base + COMMAND_RETURN_STATUS); + dev_dbg(priv->dev, "olpc_apsp_rx %x\n", w); + + if (w >> PORT_SHIFT == KEYBOARD_PORT) + serio = priv->kbio; + else + serio = priv->padio; + + serio_interrupt(serio, w & DATA_MASK, 0); + + /* Ack and clear interrupt */ + writel(tmp | SP_COMMAND_COMPLETE_RESET, priv->base + PJ_RST_INTERRUPT); + writel(PORT_MASK, priv->base + SECURE_PROCESSOR_COMMAND); + + pm_wakeup_event(priv->dev, 1000); + return IRQ_HANDLED; +} + +static int olpc_apsp_open(struct serio *port) +{ + struct olpc_apsp *priv = port->port_data; + unsigned int tmp; + + if (priv->open_count++ == 0) { + /* Enable interrupt 0 by clearing its bit */ + tmp = readl(priv->base + PJ_INTERRUPT_MASK); + writel(tmp & ~INT_0, priv->base + PJ_INTERRUPT_MASK); + } + + return 0; +} + +static void olpc_apsp_close(struct serio *port) +{ + struct olpc_apsp *priv = port->port_data; + unsigned int tmp; + + if (--priv->open_count == 0) { + /* Disable interrupt 0 */ + tmp = readl(priv->base + PJ_INTERRUPT_MASK); + writel(tmp | INT_0, priv->base + PJ_INTERRUPT_MASK); + } +} + +static int olpc_apsp_probe(struct platform_device *pdev) +{ + struct serio *kb_serio, *pad_serio; + struct olpc_apsp *priv; + struct resource *res; + struct device_node *np; + unsigned long l; + int error; + + priv = devm_kzalloc(&pdev->dev, sizeof(struct olpc_apsp), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + np = pdev->dev.of_node; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENOENT; + + priv->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->base)) { + dev_err(&pdev->dev, "Failed to map WTM registers\n"); + return PTR_ERR(priv->base); + } + + priv->irq = platform_get_irq(pdev, 0); + if (priv->irq < 0) + return priv->irq; + + l = readl(priv->base + COMMAND_FIFO_STATUS); + if (!(l & CMD_STS_MASK)) { + dev_err(&pdev->dev, "SP cannot accept commands.\n"); + return -EIO; + } + + /* KEYBOARD */ + kb_serio = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!kb_serio) + return -ENOMEM; + kb_serio->id.type = SERIO_8042_XL; + kb_serio->write = olpc_apsp_write; + kb_serio->open = olpc_apsp_open; + kb_serio->close = olpc_apsp_close; + kb_serio->port_data = priv; + kb_serio->dev.parent = &pdev->dev; + strlcpy(kb_serio->name, "sp keyboard", sizeof(kb_serio->name)); + strlcpy(kb_serio->phys, "sp/serio0", sizeof(kb_serio->phys)); + priv->kbio = kb_serio; + serio_register_port(kb_serio); + + /* TOUCHPAD */ + pad_serio = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!pad_serio) { + error = -ENOMEM; + goto err_pad; + } + pad_serio->id.type = SERIO_8042; + pad_serio->write = olpc_apsp_write; + pad_serio->open = olpc_apsp_open; + pad_serio->close = olpc_apsp_close; + pad_serio->port_data = priv; + pad_serio->dev.parent = &pdev->dev; + strlcpy(pad_serio->name, "sp touchpad", sizeof(pad_serio->name)); + strlcpy(pad_serio->phys, "sp/serio1", sizeof(pad_serio->phys)); + priv->padio = pad_serio; + serio_register_port(pad_serio); + + error = request_irq(priv->irq, olpc_apsp_rx, 0, "olpc-apsp", priv); + if (error) { + dev_err(&pdev->dev, "Failed to request IRQ\n"); + goto err_irq; + } + + priv->dev = &pdev->dev; + device_init_wakeup(priv->dev, 1); + platform_set_drvdata(pdev, priv); + + dev_dbg(&pdev->dev, "probed successfully.\n"); + return 0; + +err_irq: + serio_unregister_port(pad_serio); +err_pad: + serio_unregister_port(kb_serio); + return error; +} + +static int olpc_apsp_remove(struct platform_device *pdev) +{ + struct olpc_apsp *priv = platform_get_drvdata(pdev); + + free_irq(priv->irq, priv); + + serio_unregister_port(priv->kbio); + serio_unregister_port(priv->padio); + + return 0; +} + +static struct of_device_id olpc_apsp_dt_ids[] = { + { .compatible = "olpc,ap-sp", }, + {} +}; +MODULE_DEVICE_TABLE(of, olpc_apsp_dt_ids); + +static struct platform_driver olpc_apsp_driver = { + .probe = olpc_apsp_probe, + .remove = olpc_apsp_remove, + .driver = { + .name = "olpc-apsp", + .owner = THIS_MODULE, + .of_match_table = olpc_apsp_dt_ids, + }, +}; + +MODULE_DESCRIPTION("OLPC AP-SP serio driver"); +MODULE_LICENSE("GPL"); +module_platform_driver(olpc_apsp_driver); -- cgit v1.2.3 From d53635a980a37f1bb2d49032b31a25a3b0d49fb0 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 14 May 2013 16:34:54 +1000 Subject: drm/nouveau: pull in latest ucode builds from external tree Signed-off-by: Ben Skeggs --- .../drm/nouveau/core/engine/copy/fuc/nva3.fuc.h | 4 +- .../drm/nouveau/core/engine/copy/fuc/nvc0.fuc.h | 4 +- .../drm/nouveau/core/engine/crypt/fuc/nv98.fuc.h | 4 +- .../nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h | 57 ++++++++++++---------- .../nouveau/core/engine/graph/fuc/hubnvc0.fuc.h | 30 ++++++------ 5 files changed, 51 insertions(+), 48 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/copy/fuc/nva3.fuc.h b/drivers/gpu/drm/nouveau/core/engine/copy/fuc/nva3.fuc.h index c92520f3ed46..241b27201206 100644 --- a/drivers/gpu/drm/nouveau/core/engine/copy/fuc/nva3.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/copy/fuc/nva3.fuc.h @@ -1,4 +1,4 @@ -static u32 nva3_pcopy_data[] = { +uint32_t nva3_pcopy_data[] = { /* 0x0000: ctx_object */ 0x00000000, /* 0x0004: ctx_dma */ @@ -183,7 +183,7 @@ static u32 nva3_pcopy_data[] = { 0x00000800, }; -static u32 nva3_pcopy_code[] = { +uint32_t nva3_pcopy_code[] = { /* 0x0000: main */ 0x04fe04bd, 0x3517f000, diff --git a/drivers/gpu/drm/nouveau/core/engine/copy/fuc/nvc0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/copy/fuc/nvc0.fuc.h index 0d98c6c0958d..98cc4216a372 100644 --- a/drivers/gpu/drm/nouveau/core/engine/copy/fuc/nvc0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/copy/fuc/nvc0.fuc.h @@ -1,4 +1,4 @@ -static u32 nvc0_pcopy_data[] = { +uint32_t nvc0_pcopy_data[] = { /* 0x0000: ctx_object */ 0x00000000, /* 0x0004: ctx_query_address_high */ @@ -171,7 +171,7 @@ static u32 nvc0_pcopy_data[] = { 0x00000800, }; -static u32 nvc0_pcopy_code[] = { +uint32_t nvc0_pcopy_code[] = { /* 0x0000: main */ 0x04fe04bd, 0x3517f000, diff --git a/drivers/gpu/drm/nouveau/core/engine/crypt/fuc/nv98.fuc.h b/drivers/gpu/drm/nouveau/core/engine/crypt/fuc/nv98.fuc.h index 09962e4210e9..38676c74e6e0 100644 --- a/drivers/gpu/drm/nouveau/core/engine/crypt/fuc/nv98.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/crypt/fuc/nv98.fuc.h @@ -1,4 +1,4 @@ -static uint32_t nv98_pcrypt_data[] = { +uint32_t nv98_pcrypt_data[] = { /* 0x0000: ctx_dma */ /* 0x0000: ctx_dma_query */ 0x00000000, @@ -150,7 +150,7 @@ static uint32_t nv98_pcrypt_data[] = { 0x00000000, }; -static uint32_t nv98_pcrypt_code[] = { +uint32_t nv98_pcrypt_code[] = { 0x17f004bd, 0x0010fe35, 0xf10004fe, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h index 96050ddb22ca..a9711712de57 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h @@ -34,31 +34,34 @@ uint32_t nvc0_grgpc_data[] = { 0x00000000, /* 0x0064: chipsets */ 0x000000c0, - 0x012800c8, - 0x01e40194, + 0x013400d4, + 0x01f001a0, 0x000000c1, - 0x012c00c8, - 0x01f80194, + 0x013800d4, + 0x020401a0, 0x000000c3, - 0x012800c8, - 0x01f40194, + 0x013400d4, + 0x020001a0, 0x000000c4, - 0x012800c8, - 0x01f40194, + 0x013400d4, + 0x020001a0, 0x000000c8, - 0x012800c8, - 0x01e40194, + 0x013400d4, + 0x01f001a0, 0x000000ce, - 0x012800c8, - 0x01f40194, + 0x013400d4, + 0x020001a0, 0x000000cf, - 0x012800c8, - 0x01f00194, + 0x013400d4, + 0x01fc01a0, 0x000000d9, - 0x0194012c, - 0x025401f8, + 0x01a00138, + 0x02600204, + 0x000000d7, + 0x01a00138, + 0x02600204, 0x00000000, -/* 0x00c8: nvc0_gpc_mmio_head */ +/* 0x00d4: nvc0_gpc_mmio_head */ 0x00000380, 0x14000400, 0x20000450, @@ -83,10 +86,10 @@ uint32_t nvc0_grgpc_data[] = { 0x00000c8c, 0x08001000, 0x00001014, -/* 0x0128: nvc0_gpc_mmio_tail */ +/* 0x0134: nvc0_gpc_mmio_tail */ 0x00000c6c, -/* 0x012c: nvc1_gpc_mmio_tail */ -/* 0x012c: nvd9_gpc_mmio_head */ +/* 0x0138: nvc1_gpc_mmio_tail */ +/* 0x0138: nvd9_gpc_mmio_head */ 0x00000380, 0x04000400, 0x0800040c, @@ -113,8 +116,8 @@ uint32_t nvc0_grgpc_data[] = { 0x00000c8c, 0x08001000, 0x00001014, -/* 0x0194: nvd9_gpc_mmio_tail */ -/* 0x0194: nvc0_tpc_mmio_head */ +/* 0x01a0: nvd9_gpc_mmio_tail */ +/* 0x01a0: nvc0_tpc_mmio_head */ 0x00000018, 0x0000003c, 0x00000048, @@ -135,16 +138,16 @@ uint32_t nvc0_grgpc_data[] = { 0x4c000644, 0x00000698, 0x04000750, -/* 0x01e4: nvc0_tpc_mmio_tail */ +/* 0x01f0: nvc0_tpc_mmio_tail */ 0x00000758, 0x000002c4, 0x000006e0, -/* 0x01f0: nvcf_tpc_mmio_tail */ +/* 0x01fc: nvcf_tpc_mmio_tail */ 0x000004bc, -/* 0x01f4: nvc3_tpc_mmio_tail */ +/* 0x0200: nvc3_tpc_mmio_tail */ 0x00000544, -/* 0x01f8: nvc1_tpc_mmio_tail */ -/* 0x01f8: nvd9_tpc_mmio_head */ +/* 0x0204: nvc1_tpc_mmio_tail */ +/* 0x0204: nvd9_tpc_mmio_head */ 0x00000018, 0x0000003c, 0x00000048, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h index bb03d2a1d57b..b655117e8dac 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h @@ -30,23 +30,25 @@ uint32_t nvc0_grhub_data[] = { 0x00000000, /* 0x005c: chipsets */ 0x000000c0, - 0x013c00a0, + 0x014400a8, 0x000000c1, - 0x014000a0, + 0x014800a8, 0x000000c3, - 0x013c00a0, + 0x014400a8, 0x000000c4, - 0x013c00a0, + 0x014400a8, 0x000000c8, - 0x013c00a0, + 0x014400a8, 0x000000ce, - 0x013c00a0, + 0x014400a8, 0x000000cf, - 0x013c00a0, + 0x014400a8, 0x000000d9, - 0x01dc0140, + 0x01e40148, + 0x000000d7, + 0x01e40148, 0x00000000, -/* 0x00a0: nvc0_hub_mmio_head */ +/* 0x00a8: nvc0_hub_mmio_head */ 0x0417e91c, 0x04400204, 0x28404004, @@ -86,10 +88,10 @@ uint32_t nvc0_grhub_data[] = { 0x08408800, 0x0c408900, 0x00408980, -/* 0x013c: nvc0_hub_mmio_tail */ +/* 0x0144: nvc0_hub_mmio_tail */ 0x044064c0, -/* 0x0140: nvc1_hub_mmio_tail */ -/* 0x0140: nvd9_hub_mmio_head */ +/* 0x0148: nvc1_hub_mmio_tail */ +/* 0x0148: nvd9_hub_mmio_head */ 0x0417e91c, 0x04400204, 0x24404004, @@ -129,9 +131,7 @@ uint32_t nvc0_grhub_data[] = { 0x08408800, 0x0c408900, 0x00408980, -/* 0x01dc: nvd9_hub_mmio_tail */ - 0x00000000, - 0x00000000, +/* 0x01e4: nvd9_hub_mmio_tail */ 0x00000000, 0x00000000, 0x00000000, -- cgit v1.2.3 From b0bc5304fe672b4ccb5257e05f861402c02b2314 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 29 Apr 2013 09:31:05 +1000 Subject: drm/nve0/ce: create engine object for ce2 Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/core/engine/copy/nve0.c | 31 ++++++++++++++++++++++ drivers/gpu/drm/nouveau/core/engine/device/nve0.c | 3 +++ drivers/gpu/drm/nouveau/core/include/core/device.h | 1 + drivers/gpu/drm/nouveau/core/include/engine/copy.h | 1 + 4 files changed, 36 insertions(+) diff --git a/drivers/gpu/drm/nouveau/core/engine/copy/nve0.c b/drivers/gpu/drm/nouveau/core/engine/copy/nve0.c index dbbe9e8998fe..db351c99747d 100644 --- a/drivers/gpu/drm/nouveau/core/engine/copy/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/copy/nve0.c @@ -113,6 +113,26 @@ nve0_copy1_ctor(struct nouveau_object *parent, struct nouveau_object *engine, return 0; } +static int +nve0_copy2_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nve0_copy_priv *priv; + int ret; + + ret = nouveau_engine_create(parent, engine, oclass, true, + "PCE2", "copy2", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00200000; + nv_engine(priv)->cclass = &nve0_copy_cclass; + nv_engine(priv)->sclass = nve0_copy_sclass; + return 0; +} + struct nouveau_oclass nve0_copy0_oclass = { .handle = NV_ENGINE(COPY0, 0xe0), @@ -134,3 +154,14 @@ nve0_copy1_oclass = { .fini = _nouveau_engine_fini, }, }; + +struct nouveau_oclass +nve0_copy2_oclass = { + .handle = NV_ENGINE(COPY2, 0xe0), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nve0_copy2_ctor, + .dtor = _nouveau_engine_dtor, + .init = _nouveau_engine_init, + .fini = _nouveau_engine_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nve0.c b/drivers/gpu/drm/nouveau/core/engine/device/nve0.c index a354e409cdff..8a84d528057d 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/nve0.c @@ -79,6 +79,7 @@ nve0_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_DISP ] = &nve0_disp_oclass; device->oclass[NVDEV_ENGINE_COPY0 ] = &nve0_copy0_oclass; device->oclass[NVDEV_ENGINE_COPY1 ] = &nve0_copy1_oclass; + device->oclass[NVDEV_ENGINE_COPY2 ] = &nve0_copy2_oclass; device->oclass[NVDEV_ENGINE_BSP ] = &nve0_bsp_oclass; device->oclass[NVDEV_ENGINE_VP ] = &nve0_vp_oclass; device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass; @@ -108,6 +109,7 @@ nve0_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_DISP ] = &nve0_disp_oclass; device->oclass[NVDEV_ENGINE_COPY0 ] = &nve0_copy0_oclass; device->oclass[NVDEV_ENGINE_COPY1 ] = &nve0_copy1_oclass; + device->oclass[NVDEV_ENGINE_COPY2 ] = &nve0_copy2_oclass; device->oclass[NVDEV_ENGINE_BSP ] = &nve0_bsp_oclass; device->oclass[NVDEV_ENGINE_VP ] = &nve0_vp_oclass; device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass; @@ -137,6 +139,7 @@ nve0_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_DISP ] = &nve0_disp_oclass; device->oclass[NVDEV_ENGINE_COPY0 ] = &nve0_copy0_oclass; device->oclass[NVDEV_ENGINE_COPY1 ] = &nve0_copy1_oclass; + device->oclass[NVDEV_ENGINE_COPY2 ] = &nve0_copy2_oclass; device->oclass[NVDEV_ENGINE_BSP ] = &nve0_bsp_oclass; device->oclass[NVDEV_ENGINE_VP ] = &nve0_vp_oclass; device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass; diff --git a/drivers/gpu/drm/nouveau/core/include/core/device.h b/drivers/gpu/drm/nouveau/core/include/core/device.h index 05840f3eee98..cb6b4cc6fedd 100644 --- a/drivers/gpu/drm/nouveau/core/include/core/device.h +++ b/drivers/gpu/drm/nouveau/core/include/core/device.h @@ -49,6 +49,7 @@ enum nv_subdev_type { NVDEV_ENGINE_PPP, NVDEV_ENGINE_COPY0, NVDEV_ENGINE_COPY1, + NVDEV_ENGINE_COPY2, NVDEV_ENGINE_UNK1C1, NVDEV_ENGINE_VENC, NVDEV_ENGINE_DISP, diff --git a/drivers/gpu/drm/nouveau/core/include/engine/copy.h b/drivers/gpu/drm/nouveau/core/include/engine/copy.h index 8cad2cf28cef..316a28ae5f5c 100644 --- a/drivers/gpu/drm/nouveau/core/include/engine/copy.h +++ b/drivers/gpu/drm/nouveau/core/include/engine/copy.h @@ -8,5 +8,6 @@ extern struct nouveau_oclass nvc0_copy0_oclass; extern struct nouveau_oclass nvc0_copy1_oclass; extern struct nouveau_oclass nve0_copy0_oclass; extern struct nouveau_oclass nve0_copy1_oclass; +extern struct nouveau_oclass nve0_copy2_oclass; #endif -- cgit v1.2.3 From 01672ef454307bf63e93defb3599399b678ff58b Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 29 Apr 2013 09:35:28 +1000 Subject: drm/nve0/fifo: copy engine context stored in ramfc, not externally Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c index 56192a7242ae..4419fd23d0d1 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c @@ -138,10 +138,12 @@ nve0_fifo_context_attach(struct nouveau_object *parent, int ret; switch (nv_engidx(object->engine)) { - case NVDEV_ENGINE_SW : return 0; - case NVDEV_ENGINE_GR : + case NVDEV_ENGINE_SW : case NVDEV_ENGINE_COPY0: - case NVDEV_ENGINE_COPY1: addr = 0x0210; break; + case NVDEV_ENGINE_COPY1: + case NVDEV_ENGINE_COPY2: + return 0; + case NVDEV_ENGINE_GR : addr = 0x0210; break; case NVDEV_ENGINE_BSP : addr = 0x0270; break; case NVDEV_ENGINE_VP : addr = 0x0250; break; case NVDEV_ENGINE_PPP : addr = 0x0260; break; @@ -176,9 +178,10 @@ nve0_fifo_context_detach(struct nouveau_object *parent, bool suspend, switch (nv_engidx(object->engine)) { case NVDEV_ENGINE_SW : return 0; - case NVDEV_ENGINE_GR : case NVDEV_ENGINE_COPY0: - case NVDEV_ENGINE_COPY1: addr = 0x0210; break; + case NVDEV_ENGINE_COPY1: + case NVDEV_ENGINE_COPY2: addr = 0x0000; break; + case NVDEV_ENGINE_GR : addr = 0x0210; break; case NVDEV_ENGINE_BSP : addr = 0x0270; break; case NVDEV_ENGINE_VP : addr = 0x0250; break; case NVDEV_ENGINE_PPP : addr = 0x0260; break; @@ -194,9 +197,12 @@ nve0_fifo_context_detach(struct nouveau_object *parent, bool suspend, return -EBUSY; } - nv_wo32(base, addr + 0x00, 0x00000000); - nv_wo32(base, addr + 0x04, 0x00000000); - bar->flush(bar); + if (addr) { + nv_wo32(base, addr + 0x00, 0x00000000); + nv_wo32(base, addr + 0x04, 0x00000000); + bar->flush(bar); + } + return 0; } -- cgit v1.2.3 From 48506d17d55911c9e814108c88a9b0747313ba89 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 29 Apr 2013 09:36:42 +1000 Subject: drm/nve0/ce: link ce2 to its engine, rather than from graphics Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c | 3 ++- drivers/gpu/drm/nouveau/core/engine/graph/nve0.c | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c index 4419fd23d0d1..eff2b5791db9 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c @@ -44,7 +44,8 @@ static const struct { u64 subdev; u64 mask; } fifo_engine[] = { - _(NVDEV_ENGINE_GR , (1ULL << NVDEV_ENGINE_SW)), + _(NVDEV_ENGINE_GR , (1ULL << NVDEV_ENGINE_SW) | + (1ULL << NVDEV_ENGINE_COPY2)), _(NVDEV_ENGINE_VP , 0), _(NVDEV_ENGINE_PPP , 0), _(NVDEV_ENGINE_BSP , 0), diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c b/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c index 678c16f63055..b0a5a8847a89 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c @@ -36,7 +36,6 @@ nve0_graph_sclass[] = { { 0xa040, &nouveau_object_ofuncs }, { 0xa097, &nouveau_object_ofuncs }, { 0xa0c0, &nouveau_object_ofuncs }, - { 0xa0b5, &nouveau_object_ofuncs }, {} }; -- cgit v1.2.3 From aca78e91581c05a4bddc5118cfea55d1cd740bd6 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 29 Apr 2013 09:44:33 +1000 Subject: drm/nve0/ce: stub interrupt handler Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/core/engine/copy/nve0.c | 16 ++++++++++++++++ drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c | 1 + 2 files changed, 17 insertions(+) diff --git a/drivers/gpu/drm/nouveau/core/engine/copy/nve0.c b/drivers/gpu/drm/nouveau/core/engine/copy/nve0.c index db351c99747d..30f1ef1edcc5 100644 --- a/drivers/gpu/drm/nouveau/core/engine/copy/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/copy/nve0.c @@ -67,6 +67,19 @@ nve0_copy_cclass = { * PCOPY engine/subdev functions ******************************************************************************/ +static void +nve0_copy_intr(struct nouveau_subdev *subdev) +{ + const int ce = nv_subidx(nv_object(subdev)) - NVDEV_ENGINE_COPY0; + struct nve0_copy_priv *priv = (void *)subdev; + u32 stat = nv_rd32(priv, 0x104908 + (ce * 0x1000)); + + if (stat) { + nv_warn(priv, "unhandled intr 0x%08x\n", stat); + nv_wr32(priv, 0x104908 + (ce * 0x1000), stat); + } +} + static int nve0_copy0_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, void *data, u32 size, @@ -85,6 +98,7 @@ nve0_copy0_ctor(struct nouveau_object *parent, struct nouveau_object *engine, return ret; nv_subdev(priv)->unit = 0x00000040; + nv_subdev(priv)->intr = nve0_copy_intr; nv_engine(priv)->cclass = &nve0_copy_cclass; nv_engine(priv)->sclass = nve0_copy_sclass; return 0; @@ -108,6 +122,7 @@ nve0_copy1_ctor(struct nouveau_object *parent, struct nouveau_object *engine, return ret; nv_subdev(priv)->unit = 0x00000080; + nv_subdev(priv)->intr = nve0_copy_intr; nv_engine(priv)->cclass = &nve0_copy_cclass; nv_engine(priv)->sclass = nve0_copy_sclass; return 0; @@ -128,6 +143,7 @@ nve0_copy2_ctor(struct nouveau_object *parent, struct nouveau_object *engine, return ret; nv_subdev(priv)->unit = 0x00200000; + nv_subdev(priv)->intr = nve0_copy_intr; nv_engine(priv)->cclass = &nve0_copy_cclass; nv_engine(priv)->sclass = nve0_copy_sclass; return 0; diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c index 737bd4b682e1..c5da3babbc62 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c @@ -33,6 +33,7 @@ nvc0_mc_intr[] = { { 0x00000001, NVDEV_ENGINE_PPP }, { 0x00000020, NVDEV_ENGINE_COPY0 }, { 0x00000040, NVDEV_ENGINE_COPY1 }, + { 0x00000080, NVDEV_ENGINE_COPY2 }, { 0x00000100, NVDEV_ENGINE_FIFO }, { 0x00001000, NVDEV_ENGINE_GR }, { 0x00008000, NVDEV_ENGINE_BSP }, -- cgit v1.2.3 From 7ada785f186b5e68309c402249cd86b910a131c7 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 5 Mar 2013 12:10:24 +1000 Subject: drm/nouveau: pass generic subdev to calculation routines Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/core/subdev/clock/nv04.c | 2 +- drivers/gpu/drm/nouveau/core/subdev/clock/nv50.c | 2 +- drivers/gpu/drm/nouveau/core/subdev/clock/nva3.c | 4 ++-- drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c | 2 +- drivers/gpu/drm/nouveau/core/subdev/clock/pll.h | 4 ++-- drivers/gpu/drm/nouveau/core/subdev/clock/pllnv04.c | 17 ++++++++--------- drivers/gpu/drm/nouveau/core/subdev/clock/pllnva3.c | 4 ++-- 7 files changed, 17 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nv04.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nv04.c index b7fd1151166e..3c2fb68255a8 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/clock/nv04.c +++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nv04.c @@ -297,7 +297,7 @@ nv04_clock_pll_calc(struct nouveau_clock *clock, struct nvbios_pll *info, int clk, struct nouveau_pll_vals *pv) { int N1, M1, N2, M2, P; - int ret = nv04_pll_calc(clock, info, clk, &N1, &M1, &N2, &M2, &P); + int ret = nv04_pll_calc(nv_subdev(clock), info, clk, &N1, &M1, &N2, &M2, &P); if (ret) { pv->refclk = info->refclk; pv->N1 = N1; diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nv50.c index f4147f67eda6..5e9d5e283dc4 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/clock/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nv50.c @@ -47,7 +47,7 @@ nv50_clock_pll_set(struct nouveau_clock *clk, u32 type, u32 freq) return ret; } - ret = nv04_pll_calc(clk, &info, freq, &N1, &M1, &N2, &M2, &P); + ret = nv04_pll_calc(nv_subdev(clk), &info, freq, &N1, &M1, &N2, &M2, &P); if (!ret) { nv_error(clk, "failed pll calculation\n"); return ret; diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.c index 9068c98b96f6..2cedfd7020a0 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.c +++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.c @@ -45,7 +45,7 @@ nva3_clock_pll_set(struct nouveau_clock *clk, u32 type, u32 freq) if (ret) return ret; - ret = nva3_pll_calc(clk, &info, freq, &N, &fN, &M, &P); + ret = nva3_pll_calc(nv_subdev(clk), &info, freq, &N, &fN, &M, &P); if (ret < 0) return ret; @@ -72,7 +72,7 @@ nva3_clock_pll_calc(struct nouveau_clock *clock, struct nvbios_pll *info, { int ret, N, M, P; - ret = nva3_pll_calc(clock, info, clk, &N, NULL, &M, &P); + ret = nva3_pll_calc(nv_subdev(clock), info, clk, &N, NULL, &M, &P); if (ret > 0) { pv->refclk = info->refclk; diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c index 7c9626258a46..495b21f27662 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c @@ -45,7 +45,7 @@ nvc0_clock_pll_set(struct nouveau_clock *clk, u32 type, u32 freq) if (ret) return ret; - ret = nva3_pll_calc(clk, &info, freq, &N, &fN, &M, &P); + ret = nva3_pll_calc(nv_subdev(clk), &info, freq, &N, &fN, &M, &P); if (ret < 0) return ret; diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/pll.h b/drivers/gpu/drm/nouveau/core/subdev/clock/pll.h index ef2c0078f337..445b14c33a98 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/clock/pll.h +++ b/drivers/gpu/drm/nouveau/core/subdev/clock/pll.h @@ -1,9 +1,9 @@ #ifndef __NOUVEAU_PLL_H__ #define __NOUVEAU_PLL_H__ -int nv04_pll_calc(struct nouveau_clock *, struct nvbios_pll *, u32 freq, +int nv04_pll_calc(struct nouveau_subdev *, struct nvbios_pll *, u32 freq, int *N1, int *M1, int *N2, int *M2, int *P); -int nva3_pll_calc(struct nouveau_clock *, struct nvbios_pll *, u32 freq, +int nva3_pll_calc(struct nouveau_subdev *, struct nvbios_pll *, u32 freq, int *N, int *fN, int *M, int *P); #endif diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/pllnv04.c b/drivers/gpu/drm/nouveau/core/subdev/clock/pllnv04.c index a2ab6d051ba8..cf1ed0dc9bc9 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/clock/pllnv04.c +++ b/drivers/gpu/drm/nouveau/core/subdev/clock/pllnv04.c @@ -21,14 +21,13 @@ * SOFTWARE. */ -#include #include #include #include "pll.h" static int -getMNP_single(struct nouveau_clock *clock, struct nvbios_pll *info, int clk, +getMNP_single(struct nouveau_subdev *subdev, struct nvbios_pll *info, int clk, int *pN, int *pM, int *pP) { /* Find M, N and P for a single stage PLL @@ -39,7 +38,7 @@ getMNP_single(struct nouveau_clock *clock, struct nvbios_pll *info, int clk, * "clk" parameter in kHz * returns calculated clock */ - int cv = nouveau_bios(clock)->version.chip; + int cv = nouveau_bios(subdev)->version.chip; int minvco = info->vco1.min_freq, maxvco = info->vco1.max_freq; int minM = info->vco1.min_m, maxM = info->vco1.max_m; int minN = info->vco1.min_n, maxN = info->vco1.max_n; @@ -124,7 +123,7 @@ getMNP_single(struct nouveau_clock *clock, struct nvbios_pll *info, int clk, } static int -getMNP_double(struct nouveau_clock *clock, struct nvbios_pll *info, int clk, +getMNP_double(struct nouveau_subdev *subdev, struct nvbios_pll *info, int clk, int *pN1, int *pM1, int *pN2, int *pM2, int *pP) { /* Find M, N and P for a two stage PLL @@ -135,7 +134,7 @@ getMNP_double(struct nouveau_clock *clock, struct nvbios_pll *info, int clk, * "clk" parameter in kHz * returns calculated clock */ - int chip_version = nouveau_bios(clock)->version.chip; + int chip_version = nouveau_bios(subdev)->version.chip; int minvco1 = info->vco1.min_freq, maxvco1 = info->vco1.max_freq; int minvco2 = info->vco2.min_freq, maxvco2 = info->vco2.max_freq; int minU1 = info->vco1.min_inputfreq, minU2 = info->vco2.min_inputfreq; @@ -223,20 +222,20 @@ getMNP_double(struct nouveau_clock *clock, struct nvbios_pll *info, int clk, } int -nv04_pll_calc(struct nouveau_clock *clk, struct nvbios_pll *info, u32 freq, +nv04_pll_calc(struct nouveau_subdev *subdev, struct nvbios_pll *info, u32 freq, int *N1, int *M1, int *N2, int *M2, int *P) { int ret; if (!info->vco2.max_freq) { - ret = getMNP_single(clk, info, freq, N1, M1, P); + ret = getMNP_single(subdev, info, freq, N1, M1, P); *N2 = 1; *M2 = 1; } else { - ret = getMNP_double(clk, info, freq, N1, M1, N2, M2, P); + ret = getMNP_double(subdev, info, freq, N1, M1, N2, M2, P); } if (!ret) - nv_error(clk, "unable to compute acceptable pll values\n"); + nv_error(subdev, "unable to compute acceptable pll values\n"); return ret; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/pllnva3.c b/drivers/gpu/drm/nouveau/core/subdev/clock/pllnva3.c index eed5c16cf610..4497378ba1e8 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/clock/pllnva3.c +++ b/drivers/gpu/drm/nouveau/core/subdev/clock/pllnva3.c @@ -29,7 +29,7 @@ #include "pll.h" int -nva3_pll_calc(struct nouveau_clock *clock, struct nvbios_pll *info, +nva3_pll_calc(struct nouveau_subdev *subdev, struct nvbios_pll *info, u32 freq, int *pN, int *pfN, int *pM, int *P) { u32 best_err = ~0, err; @@ -72,7 +72,7 @@ nva3_pll_calc(struct nouveau_clock *clock, struct nvbios_pll *info, } if (unlikely(best_err == ~0)) { - nv_error(clock, "unable to find matching pll values\n"); + nv_error(subdev, "unable to find matching pll values\n"); return -EINVAL; } -- cgit v1.2.3 From 88524bc06926b243c75e5751eb3403c602b6a904 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 5 Mar 2013 10:53:54 +1000 Subject: drm/nouveau/devinit: move simple pll setting routines to devinit These are pretty much useless for reclocking purposes. Lets make it clearer what they're for and move them to DEVINIT to signify they're for the very simple PLL setting requirements of running the init tables. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/Makefile | 2 + drivers/gpu/drm/nouveau/core/engine/device/nv50.c | 8 +- drivers/gpu/drm/nouveau/core/engine/device/nvc0.c | 16 +- drivers/gpu/drm/nouveau/core/engine/device/nve0.c | 8 +- drivers/gpu/drm/nouveau/core/engine/disp/nv50.c | 6 +- drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c | 11 +- .../gpu/drm/nouveau/core/include/subdev/clock.h | 2 - .../gpu/drm/nouveau/core/include/subdev/devinit.h | 21 +- drivers/gpu/drm/nouveau/core/subdev/bios/init.c | 7 +- drivers/gpu/drm/nouveau/core/subdev/clock/nv04.c | 272 +---------------- drivers/gpu/drm/nouveau/core/subdev/clock/nv40.c | 1 - drivers/gpu/drm/nouveau/core/subdev/clock/nv50.c | 45 --- drivers/gpu/drm/nouveau/core/subdev/clock/nva3.c | 35 --- drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c | 36 --- drivers/gpu/drm/nouveau/core/subdev/devinit/base.c | 23 +- drivers/gpu/drm/nouveau/core/subdev/devinit/nv04.c | 329 +++++++++++++++++++-- drivers/gpu/drm/nouveau/core/subdev/devinit/nv05.c | 3 +- drivers/gpu/drm/nouveau/core/subdev/devinit/nv10.c | 3 +- drivers/gpu/drm/nouveau/core/subdev/devinit/nv1a.c | 4 +- drivers/gpu/drm/nouveau/core/subdev/devinit/nv20.c | 5 +- drivers/gpu/drm/nouveau/core/subdev/devinit/nv50.c | 78 +++-- drivers/gpu/drm/nouveau/core/subdev/devinit/nva3.c | 87 ++++++ drivers/gpu/drm/nouveau/core/subdev/devinit/nvc0.c | 88 ++++++ drivers/gpu/drm/nouveau/core/subdev/devinit/priv.h | 25 ++ 24 files changed, 628 insertions(+), 487 deletions(-) create mode 100644 drivers/gpu/drm/nouveau/core/subdev/devinit/nva3.c create mode 100644 drivers/gpu/drm/nouveau/core/subdev/devinit/nvc0.c create mode 100644 drivers/gpu/drm/nouveau/core/subdev/devinit/priv.h diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index 998e8b4444f3..f689d31e71fa 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile @@ -60,6 +60,8 @@ nouveau-y += core/subdev/devinit/nv10.o nouveau-y += core/subdev/devinit/nv1a.o nouveau-y += core/subdev/devinit/nv20.o nouveau-y += core/subdev/devinit/nv50.o +nouveau-y += core/subdev/devinit/nva3.o +nouveau-y += core/subdev/devinit/nvc0.o nouveau-y += core/subdev/fb/base.o nouveau-y += core/subdev/fb/nv04.o nouveau-y += core/subdev/fb/nv10.o diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nv50.c b/drivers/gpu/drm/nouveau/core/engine/device/nv50.c index 5e8c3de75593..5c1db3e1f0f2 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/nv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/nv50.c @@ -319,7 +319,7 @@ nv50_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_CLOCK ] = &nva3_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; - device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = &nva3_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv98_mc_oclass; device->oclass[NVDEV_SUBDEV_BUS ] = &nv50_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; @@ -346,7 +346,7 @@ nv50_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_CLOCK ] = &nva3_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; - device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = &nva3_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv98_mc_oclass; device->oclass[NVDEV_SUBDEV_BUS ] = &nv50_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; @@ -372,7 +372,7 @@ nv50_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_CLOCK ] = &nva3_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; - device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = &nva3_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv98_mc_oclass; device->oclass[NVDEV_SUBDEV_BUS ] = &nv50_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; @@ -398,7 +398,7 @@ nv50_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_CLOCK ] = &nva3_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; - device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = &nva3_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv98_mc_oclass; device->oclass[NVDEV_SUBDEV_BUS ] = &nv50_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c index a36e64e98ef3..1df3578a5315 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c @@ -62,7 +62,7 @@ nvc0_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; - device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = &nvc0_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nvc0_mc_oclass; device->oclass[NVDEV_SUBDEV_BUS ] = &nvc0_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; @@ -91,7 +91,7 @@ nvc0_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; - device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = &nvc0_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nvc0_mc_oclass; device->oclass[NVDEV_SUBDEV_BUS ] = &nvc0_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; @@ -120,7 +120,7 @@ nvc0_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; - device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = &nvc0_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nvc0_mc_oclass; device->oclass[NVDEV_SUBDEV_BUS ] = &nvc0_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; @@ -148,7 +148,7 @@ nvc0_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; - device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = &nvc0_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nvc0_mc_oclass; device->oclass[NVDEV_SUBDEV_BUS ] = &nvc0_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; @@ -177,7 +177,7 @@ nvc0_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; - device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = &nvc0_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nvc0_mc_oclass; device->oclass[NVDEV_SUBDEV_BUS ] = &nvc0_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; @@ -206,7 +206,7 @@ nvc0_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; - device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = &nvc0_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nvc0_mc_oclass; device->oclass[NVDEV_SUBDEV_BUS ] = &nvc0_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; @@ -234,7 +234,7 @@ nvc0_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nva3_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; - device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = &nvc0_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nvc0_mc_oclass; device->oclass[NVDEV_SUBDEV_BUS ] = &nvc0_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; @@ -263,7 +263,7 @@ nvc0_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; - device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = &nvc0_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nvc0_mc_oclass; device->oclass[NVDEV_SUBDEV_BUS ] = &nvc0_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nve0.c b/drivers/gpu/drm/nouveau/core/engine/device/nve0.c index 8a84d528057d..4e6ef62e88c8 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/nve0.c @@ -62,7 +62,7 @@ nve0_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; - device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = &nvc0_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nvc0_mc_oclass; device->oclass[NVDEV_SUBDEV_BUS ] = &nvc0_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; @@ -92,7 +92,7 @@ nve0_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; - device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = &nvc0_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nvc0_mc_oclass; device->oclass[NVDEV_SUBDEV_BUS ] = &nvc0_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; @@ -122,7 +122,7 @@ nve0_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; - device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = &nvc0_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nvc0_mc_oclass; device->oclass[NVDEV_SUBDEV_BUS ] = &nvc0_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; @@ -152,7 +152,7 @@ nve0_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; - device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = &nvc0_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nvc0_mc_oclass; device->oclass[NVDEV_SUBDEV_BUS ] = &nvc0_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c index 6a38402fa56c..8b42f45c2b1d 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c @@ -34,9 +34,9 @@ #include #include #include +#include #include #include -#include #include "nv50.h" @@ -987,10 +987,10 @@ nv50_disp_intr_unk20_0(struct nv50_disp_priv *priv, int head) static void nv50_disp_intr_unk20_1(struct nv50_disp_priv *priv, int head) { - struct nouveau_clock *clk = nouveau_clock(priv); + struct nouveau_devinit *devinit = nouveau_devinit(priv); u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff; if (pclk) - clk->pll_set(clk, PLL_VPLL0 + head, pclk); + devinit->pll_set(devinit, PLL_VPLL0 + head, pclk); } static void diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c index 019eacd8a68f..3ed10b00e81c 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c @@ -29,15 +29,14 @@ #include -#include -#include -#include - #include #include #include #include #include +#include +#include +#include #include "nv50.h" @@ -738,10 +737,10 @@ nvd0_disp_intr_unk2_0(struct nv50_disp_priv *priv, int head) static void nvd0_disp_intr_unk2_1(struct nv50_disp_priv *priv, int head) { - struct nouveau_clock *clk = nouveau_clock(priv); + struct nouveau_devinit *devinit = nouveau_devinit(priv); u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000; if (pclk) - clk->pll_set(clk, PLL_VPLL0 + head, pclk); + devinit->pll_set(devinit, PLL_VPLL0 + head, pclk); nv_wr32(priv, 0x612200 + (head * 0x800), 0x00000000); } diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/clock.h b/drivers/gpu/drm/nouveau/core/include/subdev/clock.h index 41b7a6a76f19..89ee289097a6 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/clock.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/clock.h @@ -10,8 +10,6 @@ struct nvbios_pll; struct nouveau_clock { struct nouveau_subdev base; - int (*pll_set)(struct nouveau_clock *, u32 type, u32 freq); - /*XXX: die, these are here *only* to support the completely * bat-shit insane what-was-nouveau_hw.c code */ diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/devinit.h b/drivers/gpu/drm/nouveau/core/include/subdev/devinit.h index 29e4cc1f6cc0..685c9b12ee4c 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/devinit.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/devinit.h @@ -8,6 +8,8 @@ struct nouveau_devinit { struct nouveau_subdev base; bool post; void (*meminit)(struct nouveau_devinit *); + int (*pll_set)(struct nouveau_devinit *, u32 type, u32 freq); + }; static inline struct nouveau_devinit * @@ -20,11 +22,20 @@ nouveau_devinit(void *obj) nouveau_devinit_create_((p), (e), (o), sizeof(**d), (void **)d) #define nouveau_devinit_destroy(p) \ nouveau_subdev_destroy(&(p)->base) +#define nouveau_devinit_init(p) ({ \ + struct nouveau_devinit *d = (p); \ + _nouveau_devinit_init(nv_object(d)); \ +}) +#define nouveau_devinit_fini(p,s) ({ \ + struct nouveau_devinit *d = (p); \ + _nouveau_devinit_fini(nv_object(d), (s)); \ +}) int nouveau_devinit_create_(struct nouveau_object *, struct nouveau_object *, struct nouveau_oclass *, int, void **); -int nouveau_devinit_init(struct nouveau_devinit *); -int nouveau_devinit_fini(struct nouveau_devinit *, bool suspend); +#define _nouveau_devinit_dtor _nouveau_subdev_dtor +int _nouveau_devinit_init(struct nouveau_object *); +int _nouveau_devinit_fini(struct nouveau_object *, bool suspend); extern struct nouveau_oclass nv04_devinit_oclass; extern struct nouveau_oclass nv05_devinit_oclass; @@ -32,9 +43,7 @@ extern struct nouveau_oclass nv10_devinit_oclass; extern struct nouveau_oclass nv1a_devinit_oclass; extern struct nouveau_oclass nv20_devinit_oclass; extern struct nouveau_oclass nv50_devinit_oclass; - -void nv04_devinit_dtor(struct nouveau_object *); -int nv04_devinit_init(struct nouveau_object *); -int nv04_devinit_fini(struct nouveau_object *, bool); +extern struct nouveau_oclass nva3_devinit_oclass; +extern struct nouveau_oclass nvc0_devinit_oclass; #endif diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/init.c b/drivers/gpu/drm/nouveau/core/subdev/bios/init.c index c434d398d16f..0687e6481438 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bios/init.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/init.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -300,9 +299,9 @@ init_wrauxr(struct nvbios_init *init, u32 addr, u8 data) static void init_prog_pll(struct nvbios_init *init, u32 id, u32 freq) { - struct nouveau_clock *clk = nouveau_clock(init->bios); - if (clk && clk->pll_set && init_exec(init)) { - int ret = clk->pll_set(clk, id, freq); + struct nouveau_devinit *devinit = nouveau_devinit(init->bios); + if (devinit->pll_set && init_exec(init)) { + int ret = devinit->pll_set(devinit, id, freq); if (ret) warn("failed to prog pll 0x%08x to %dkHz\n", id, freq); } diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nv04.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nv04.c index 3c2fb68255a8..a14277586595 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/clock/nv04.c +++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nv04.c @@ -22,9 +22,10 @@ * Authors: Ben Skeggs */ -#include #include #include +#include +#include #include "pll.h" @@ -32,266 +33,6 @@ struct nv04_clock_priv { struct nouveau_clock base; }; -static int -powerctrl_1_shift(int chip_version, int reg) -{ - int shift = -4; - - if (chip_version < 0x17 || chip_version == 0x1a || chip_version == 0x20) - return shift; - - switch (reg) { - case 0x680520: - shift += 4; - case 0x680508: - shift += 4; - case 0x680504: - shift += 4; - case 0x680500: - shift += 4; - } - - /* - * the shift for vpll regs is only used for nv3x chips with a single - * stage pll - */ - if (shift > 4 && (chip_version < 0x32 || chip_version == 0x35 || - chip_version == 0x36 || chip_version >= 0x40)) - shift = -4; - - return shift; -} - -static void -setPLL_single(struct nv04_clock_priv *priv, u32 reg, - struct nouveau_pll_vals *pv) -{ - int chip_version = nouveau_bios(priv)->version.chip; - uint32_t oldpll = nv_rd32(priv, reg); - int oldN = (oldpll >> 8) & 0xff, oldM = oldpll & 0xff; - uint32_t pll = (oldpll & 0xfff80000) | pv->log2P << 16 | pv->NM1; - uint32_t saved_powerctrl_1 = 0; - int shift_powerctrl_1 = powerctrl_1_shift(chip_version, reg); - - if (oldpll == pll) - return; /* already set */ - - if (shift_powerctrl_1 >= 0) { - saved_powerctrl_1 = nv_rd32(priv, 0x001584); - nv_wr32(priv, 0x001584, - (saved_powerctrl_1 & ~(0xf << shift_powerctrl_1)) | - 1 << shift_powerctrl_1); - } - - if (oldM && pv->M1 && (oldN / oldM < pv->N1 / pv->M1)) - /* upclock -- write new post divider first */ - nv_wr32(priv, reg, pv->log2P << 16 | (oldpll & 0xffff)); - else - /* downclock -- write new NM first */ - nv_wr32(priv, reg, (oldpll & 0xffff0000) | pv->NM1); - - if (chip_version < 0x17 && chip_version != 0x11) - /* wait a bit on older chips */ - msleep(64); - nv_rd32(priv, reg); - - /* then write the other half as well */ - nv_wr32(priv, reg, pll); - - if (shift_powerctrl_1 >= 0) - nv_wr32(priv, 0x001584, saved_powerctrl_1); -} - -static uint32_t -new_ramdac580(uint32_t reg1, bool ss, uint32_t ramdac580) -{ - bool head_a = (reg1 == 0x680508); - - if (ss) /* single stage pll mode */ - ramdac580 |= head_a ? 0x00000100 : 0x10000000; - else - ramdac580 &= head_a ? 0xfffffeff : 0xefffffff; - - return ramdac580; -} - -static void -setPLL_double_highregs(struct nv04_clock_priv *priv, u32 reg1, - struct nouveau_pll_vals *pv) -{ - int chip_version = nouveau_bios(priv)->version.chip; - bool nv3035 = chip_version == 0x30 || chip_version == 0x35; - uint32_t reg2 = reg1 + ((reg1 == 0x680520) ? 0x5c : 0x70); - uint32_t oldpll1 = nv_rd32(priv, reg1); - uint32_t oldpll2 = !nv3035 ? nv_rd32(priv, reg2) : 0; - uint32_t pll1 = (oldpll1 & 0xfff80000) | pv->log2P << 16 | pv->NM1; - uint32_t pll2 = (oldpll2 & 0x7fff0000) | 1 << 31 | pv->NM2; - uint32_t oldramdac580 = 0, ramdac580 = 0; - bool single_stage = !pv->NM2 || pv->N2 == pv->M2; /* nv41+ only */ - uint32_t saved_powerctrl_1 = 0, savedc040 = 0; - int shift_powerctrl_1 = powerctrl_1_shift(chip_version, reg1); - - /* model specific additions to generic pll1 and pll2 set up above */ - if (nv3035) { - pll1 = (pll1 & 0xfcc7ffff) | (pv->N2 & 0x18) << 21 | - (pv->N2 & 0x7) << 19 | 8 << 4 | (pv->M2 & 7) << 4; - pll2 = 0; - } - if (chip_version > 0x40 && reg1 >= 0x680508) { /* !nv40 */ - oldramdac580 = nv_rd32(priv, 0x680580); - ramdac580 = new_ramdac580(reg1, single_stage, oldramdac580); - if (oldramdac580 != ramdac580) - oldpll1 = ~0; /* force mismatch */ - if (single_stage) - /* magic value used by nvidia in single stage mode */ - pll2 |= 0x011f; - } - if (chip_version > 0x70) - /* magic bits set by the blob (but not the bios) on g71-73 */ - pll1 = (pll1 & 0x7fffffff) | (single_stage ? 0x4 : 0xc) << 28; - - if (oldpll1 == pll1 && oldpll2 == pll2) - return; /* already set */ - - if (shift_powerctrl_1 >= 0) { - saved_powerctrl_1 = nv_rd32(priv, 0x001584); - nv_wr32(priv, 0x001584, - (saved_powerctrl_1 & ~(0xf << shift_powerctrl_1)) | - 1 << shift_powerctrl_1); - } - - if (chip_version >= 0x40) { - int shift_c040 = 14; - - switch (reg1) { - case 0x680504: - shift_c040 += 2; - case 0x680500: - shift_c040 += 2; - case 0x680520: - shift_c040 += 2; - case 0x680508: - shift_c040 += 2; - } - - savedc040 = nv_rd32(priv, 0xc040); - if (shift_c040 != 14) - nv_wr32(priv, 0xc040, savedc040 & ~(3 << shift_c040)); - } - - if (oldramdac580 != ramdac580) - nv_wr32(priv, 0x680580, ramdac580); - - if (!nv3035) - nv_wr32(priv, reg2, pll2); - nv_wr32(priv, reg1, pll1); - - if (shift_powerctrl_1 >= 0) - nv_wr32(priv, 0x001584, saved_powerctrl_1); - if (chip_version >= 0x40) - nv_wr32(priv, 0xc040, savedc040); -} - -static void -setPLL_double_lowregs(struct nv04_clock_priv *priv, u32 NMNMreg, - struct nouveau_pll_vals *pv) -{ - /* When setting PLLs, there is a merry game of disabling and enabling - * various bits of hardware during the process. This function is a - * synthesis of six nv4x traces, nearly each card doing a subtly - * different thing. With luck all the necessary bits for each card are - * combined herein. Without luck it deviates from each card's formula - * so as to not work on any :) - */ - - uint32_t Preg = NMNMreg - 4; - bool mpll = Preg == 0x4020; - uint32_t oldPval = nv_rd32(priv, Preg); - uint32_t NMNM = pv->NM2 << 16 | pv->NM1; - uint32_t Pval = (oldPval & (mpll ? ~(0x77 << 16) : ~(7 << 16))) | - 0xc << 28 | pv->log2P << 16; - uint32_t saved4600 = 0; - /* some cards have different maskc040s */ - uint32_t maskc040 = ~(3 << 14), savedc040; - bool single_stage = !pv->NM2 || pv->N2 == pv->M2; - - if (nv_rd32(priv, NMNMreg) == NMNM && (oldPval & 0xc0070000) == Pval) - return; - - if (Preg == 0x4000) - maskc040 = ~0x333; - if (Preg == 0x4058) - maskc040 = ~(0xc << 24); - - if (mpll) { - struct nvbios_pll info; - uint8_t Pval2; - - if (nvbios_pll_parse(nouveau_bios(priv), Preg, &info)) - return; - - Pval2 = pv->log2P + info.bias_p; - if (Pval2 > info.max_p) - Pval2 = info.max_p; - Pval |= 1 << 28 | Pval2 << 20; - - saved4600 = nv_rd32(priv, 0x4600); - nv_wr32(priv, 0x4600, saved4600 | 8 << 28); - } - if (single_stage) - Pval |= mpll ? 1 << 12 : 1 << 8; - - nv_wr32(priv, Preg, oldPval | 1 << 28); - nv_wr32(priv, Preg, Pval & ~(4 << 28)); - if (mpll) { - Pval |= 8 << 20; - nv_wr32(priv, 0x4020, Pval & ~(0xc << 28)); - nv_wr32(priv, 0x4038, Pval & ~(0xc << 28)); - } - - savedc040 = nv_rd32(priv, 0xc040); - nv_wr32(priv, 0xc040, savedc040 & maskc040); - - nv_wr32(priv, NMNMreg, NMNM); - if (NMNMreg == 0x4024) - nv_wr32(priv, 0x403c, NMNM); - - nv_wr32(priv, Preg, Pval); - if (mpll) { - Pval &= ~(8 << 20); - nv_wr32(priv, 0x4020, Pval); - nv_wr32(priv, 0x4038, Pval); - nv_wr32(priv, 0x4600, saved4600); - } - - nv_wr32(priv, 0xc040, savedc040); - - if (mpll) { - nv_wr32(priv, 0x4020, Pval & ~(1 << 28)); - nv_wr32(priv, 0x4038, Pval & ~(1 << 28)); - } -} - -int -nv04_clock_pll_set(struct nouveau_clock *clk, u32 type, u32 freq) -{ - struct nv04_clock_priv *priv = (void *)clk; - struct nouveau_pll_vals pv; - struct nvbios_pll info; - int ret; - - ret = nvbios_pll_parse(nouveau_bios(priv), type > 0x405c ? - type : type - 4, &info); - if (ret) - return ret; - - ret = clk->pll_calc(clk, &info, freq, &pv); - if (!ret) - return ret; - - return clk->pll_prog(clk, type, &pv); -} - int nv04_clock_pll_calc(struct nouveau_clock *clock, struct nvbios_pll *info, int clk, struct nouveau_pll_vals *pv) @@ -313,17 +54,17 @@ int nv04_clock_pll_prog(struct nouveau_clock *clk, u32 reg1, struct nouveau_pll_vals *pv) { - struct nv04_clock_priv *priv = (void *)clk; + struct nouveau_devinit *devinit = nouveau_devinit(clk); int cv = nouveau_bios(clk)->version.chip; if (cv == 0x30 || cv == 0x31 || cv == 0x35 || cv == 0x36 || cv >= 0x40) { if (reg1 > 0x405c) - setPLL_double_highregs(priv, reg1, pv); + setPLL_double_highregs(devinit, reg1, pv); else - setPLL_double_lowregs(priv, reg1, pv); + setPLL_double_lowregs(devinit, reg1, pv); } else - setPLL_single(priv, reg1, pv); + setPLL_single(devinit, reg1, pv); return 0; } @@ -341,7 +82,6 @@ nv04_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; - priv->base.pll_set = nv04_clock_pll_set; priv->base.pll_calc = nv04_clock_pll_calc; priv->base.pll_prog = nv04_clock_pll_prog; return 0; diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nv40.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nv40.c index a4b2b7ebf9af..0db5dbfd91b5 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/clock/nv40.c +++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nv40.c @@ -41,7 +41,6 @@ nv40_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; - priv->base.pll_set = nv04_clock_pll_set; priv->base.pll_calc = nv04_clock_pll_calc; priv->base.pll_prog = nv04_clock_pll_prog; return 0; diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nv50.c index 5e9d5e283dc4..d09d3e78040c 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/clock/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nv50.c @@ -32,50 +32,6 @@ struct nv50_clock_priv { struct nouveau_clock base; }; -static int -nv50_clock_pll_set(struct nouveau_clock *clk, u32 type, u32 freq) -{ - struct nv50_clock_priv *priv = (void *)clk; - struct nouveau_bios *bios = nouveau_bios(priv); - struct nvbios_pll info; - int N1, M1, N2, M2, P; - int ret; - - ret = nvbios_pll_parse(bios, type, &info); - if (ret) { - nv_error(clk, "failed to retrieve pll data, %d\n", ret); - return ret; - } - - ret = nv04_pll_calc(nv_subdev(clk), &info, freq, &N1, &M1, &N2, &M2, &P); - if (!ret) { - nv_error(clk, "failed pll calculation\n"); - return ret; - } - - switch (info.type) { - case PLL_VPLL0: - case PLL_VPLL1: - nv_wr32(priv, info.reg + 0, 0x10000611); - nv_mask(priv, info.reg + 4, 0x00ff00ff, (M1 << 16) | N1); - nv_mask(priv, info.reg + 8, 0x7fff00ff, (P << 28) | - (M2 << 16) | N2); - break; - case PLL_MEMORY: - nv_mask(priv, info.reg + 0, 0x01ff0000, (P << 22) | - (info.bias_p << 19) | - (P << 16)); - nv_wr32(priv, info.reg + 4, (N1 << 8) | M1); - break; - default: - nv_mask(priv, info.reg + 0, 0x00070000, (P << 16)); - nv_wr32(priv, info.reg + 4, (N1 << 8) | M1); - break; - } - - return 0; -} - static int nv50_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, void *data, u32 size, @@ -89,7 +45,6 @@ nv50_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; - priv->base.pll_set = nv50_clock_pll_set; priv->base.pll_calc = nv04_clock_pll_calc; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.c index 2cedfd7020a0..f074cd20bc9c 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.c +++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nva3.c @@ -32,40 +32,6 @@ struct nva3_clock_priv { struct nouveau_clock base; }; -static int -nva3_clock_pll_set(struct nouveau_clock *clk, u32 type, u32 freq) -{ - struct nva3_clock_priv *priv = (void *)clk; - struct nouveau_bios *bios = nouveau_bios(priv); - struct nvbios_pll info; - int N, fN, M, P; - int ret; - - ret = nvbios_pll_parse(bios, type, &info); - if (ret) - return ret; - - ret = nva3_pll_calc(nv_subdev(clk), &info, freq, &N, &fN, &M, &P); - if (ret < 0) - return ret; - - switch (info.type) { - case PLL_VPLL0: - case PLL_VPLL1: - nv_wr32(priv, info.reg + 0, 0x50000610); - nv_mask(priv, info.reg + 4, 0x003fffff, - (P << 16) | (M << 8) | N); - nv_wr32(priv, info.reg + 8, fN); - break; - default: - nv_warn(priv, "0x%08x/%dKhz unimplemented\n", type, freq); - ret = -EINVAL; - break; - } - - return ret; -} - int nva3_clock_pll_calc(struct nouveau_clock *clock, struct nvbios_pll *info, int clk, struct nouveau_pll_vals *pv) @@ -97,7 +63,6 @@ nva3_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; - priv->base.pll_set = nva3_clock_pll_set; priv->base.pll_calc = nva3_clock_pll_calc; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c index 495b21f27662..439d81c26130 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/clock/nvc0.c @@ -32,41 +32,6 @@ struct nvc0_clock_priv { struct nouveau_clock base; }; -static int -nvc0_clock_pll_set(struct nouveau_clock *clk, u32 type, u32 freq) -{ - struct nvc0_clock_priv *priv = (void *)clk; - struct nouveau_bios *bios = nouveau_bios(priv); - struct nvbios_pll info; - int N, fN, M, P; - int ret; - - ret = nvbios_pll_parse(bios, type, &info); - if (ret) - return ret; - - ret = nva3_pll_calc(nv_subdev(clk), &info, freq, &N, &fN, &M, &P); - if (ret < 0) - return ret; - - switch (info.type) { - case PLL_VPLL0: - case PLL_VPLL1: - case PLL_VPLL2: - case PLL_VPLL3: - nv_mask(priv, info.reg + 0x0c, 0x00000000, 0x00000100); - nv_wr32(priv, info.reg + 0x04, (P << 16) | (N << 8) | M); - nv_wr32(priv, info.reg + 0x10, fN << 16); - break; - default: - nv_warn(priv, "0x%08x/%dKhz unimplemented\n", type, freq); - ret = -EINVAL; - break; - } - - return ret; -} - static int nvc0_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, void *data, u32 size, @@ -80,7 +45,6 @@ nvc0_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; - priv->base.pll_set = nvc0_clock_pll_set; priv->base.pll_calc = nva3_clock_pll_calc; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/devinit/base.c b/drivers/gpu/drm/nouveau/core/subdev/devinit/base.c index 5a07a39c1735..79c81d3d9bac 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/devinit/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/devinit/base.c @@ -29,18 +29,10 @@ #include int -nouveau_devinit_init(struct nouveau_devinit *devinit) +_nouveau_devinit_fini(struct nouveau_object *object, bool suspend) { - int ret = nouveau_subdev_init(&devinit->base); - if (ret) - return ret; + struct nouveau_devinit *devinit = (void *)object; - return nvbios_init(&devinit->base, devinit->post); -} - -int -nouveau_devinit_fini(struct nouveau_devinit *devinit, bool suspend) -{ /* force full reinit on resume */ if (suspend) devinit->post = true; @@ -48,6 +40,17 @@ nouveau_devinit_fini(struct nouveau_devinit *devinit, bool suspend) return nouveau_subdev_fini(&devinit->base, suspend); } +int +_nouveau_devinit_init(struct nouveau_object *object) +{ + struct nouveau_devinit *devinit = (void *)object; + int ret = nouveau_subdev_init(&devinit->base); + if (ret) + return ret; + + return nvbios_init(&devinit->base, devinit->post); +} + int nouveau_devinit_create_(struct nouveau_object *parent, struct nouveau_object *engine, diff --git a/drivers/gpu/drm/nouveau/core/subdev/devinit/nv04.c b/drivers/gpu/drm/nouveau/core/subdev/devinit/nv04.c index 7a72d9394340..b22357d9b821 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/devinit/nv04.c +++ b/drivers/gpu/drm/nouveau/core/subdev/devinit/nv04.c @@ -24,10 +24,10 @@ * */ -#include #include #include "fbmem.h" +#include "priv.h" struct nv04_devinit_priv { struct nouveau_devinit base; @@ -111,33 +111,298 @@ nv04_devinit_meminit(struct nouveau_devinit *devinit) } static int -nv04_devinit_ctor(struct nouveau_object *parent, struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) +powerctrl_1_shift(int chip_version, int reg) { - struct nv04_devinit_priv *priv; + int shift = -4; + + if (chip_version < 0x17 || chip_version == 0x1a || chip_version == 0x20) + return shift; + + switch (reg) { + case 0x680520: + shift += 4; + case 0x680508: + shift += 4; + case 0x680504: + shift += 4; + case 0x680500: + shift += 4; + } + + /* + * the shift for vpll regs is only used for nv3x chips with a single + * stage pll + */ + if (shift > 4 && (chip_version < 0x32 || chip_version == 0x35 || + chip_version == 0x36 || chip_version >= 0x40)) + shift = -4; + + return shift; +} + +void +setPLL_single(struct nouveau_devinit *devinit, u32 reg, + struct nouveau_pll_vals *pv) +{ + int chip_version = nouveau_bios(devinit)->version.chip; + uint32_t oldpll = nv_rd32(devinit, reg); + int oldN = (oldpll >> 8) & 0xff, oldM = oldpll & 0xff; + uint32_t pll = (oldpll & 0xfff80000) | pv->log2P << 16 | pv->NM1; + uint32_t saved_powerctrl_1 = 0; + int shift_powerctrl_1 = powerctrl_1_shift(chip_version, reg); + + if (oldpll == pll) + return; /* already set */ + + if (shift_powerctrl_1 >= 0) { + saved_powerctrl_1 = nv_rd32(devinit, 0x001584); + nv_wr32(devinit, 0x001584, + (saved_powerctrl_1 & ~(0xf << shift_powerctrl_1)) | + 1 << shift_powerctrl_1); + } + + if (oldM && pv->M1 && (oldN / oldM < pv->N1 / pv->M1)) + /* upclock -- write new post divider first */ + nv_wr32(devinit, reg, pv->log2P << 16 | (oldpll & 0xffff)); + else + /* downclock -- write new NM first */ + nv_wr32(devinit, reg, (oldpll & 0xffff0000) | pv->NM1); + + if (chip_version < 0x17 && chip_version != 0x11) + /* wait a bit on older chips */ + msleep(64); + nv_rd32(devinit, reg); + + /* then write the other half as well */ + nv_wr32(devinit, reg, pll); + + if (shift_powerctrl_1 >= 0) + nv_wr32(devinit, 0x001584, saved_powerctrl_1); +} + +static uint32_t +new_ramdac580(uint32_t reg1, bool ss, uint32_t ramdac580) +{ + bool head_a = (reg1 == 0x680508); + + if (ss) /* single stage pll mode */ + ramdac580 |= head_a ? 0x00000100 : 0x10000000; + else + ramdac580 &= head_a ? 0xfffffeff : 0xefffffff; + + return ramdac580; +} + +void +setPLL_double_highregs(struct nouveau_devinit *devinit, u32 reg1, + struct nouveau_pll_vals *pv) +{ + int chip_version = nouveau_bios(devinit)->version.chip; + bool nv3035 = chip_version == 0x30 || chip_version == 0x35; + uint32_t reg2 = reg1 + ((reg1 == 0x680520) ? 0x5c : 0x70); + uint32_t oldpll1 = nv_rd32(devinit, reg1); + uint32_t oldpll2 = !nv3035 ? nv_rd32(devinit, reg2) : 0; + uint32_t pll1 = (oldpll1 & 0xfff80000) | pv->log2P << 16 | pv->NM1; + uint32_t pll2 = (oldpll2 & 0x7fff0000) | 1 << 31 | pv->NM2; + uint32_t oldramdac580 = 0, ramdac580 = 0; + bool single_stage = !pv->NM2 || pv->N2 == pv->M2; /* nv41+ only */ + uint32_t saved_powerctrl_1 = 0, savedc040 = 0; + int shift_powerctrl_1 = powerctrl_1_shift(chip_version, reg1); + + /* model specific additions to generic pll1 and pll2 set up above */ + if (nv3035) { + pll1 = (pll1 & 0xfcc7ffff) | (pv->N2 & 0x18) << 21 | + (pv->N2 & 0x7) << 19 | 8 << 4 | (pv->M2 & 7) << 4; + pll2 = 0; + } + if (chip_version > 0x40 && reg1 >= 0x680508) { /* !nv40 */ + oldramdac580 = nv_rd32(devinit, 0x680580); + ramdac580 = new_ramdac580(reg1, single_stage, oldramdac580); + if (oldramdac580 != ramdac580) + oldpll1 = ~0; /* force mismatch */ + if (single_stage) + /* magic value used by nvidia in single stage mode */ + pll2 |= 0x011f; + } + if (chip_version > 0x70) + /* magic bits set by the blob (but not the bios) on g71-73 */ + pll1 = (pll1 & 0x7fffffff) | (single_stage ? 0x4 : 0xc) << 28; + + if (oldpll1 == pll1 && oldpll2 == pll2) + return; /* already set */ + + if (shift_powerctrl_1 >= 0) { + saved_powerctrl_1 = nv_rd32(devinit, 0x001584); + nv_wr32(devinit, 0x001584, + (saved_powerctrl_1 & ~(0xf << shift_powerctrl_1)) | + 1 << shift_powerctrl_1); + } + + if (chip_version >= 0x40) { + int shift_c040 = 14; + + switch (reg1) { + case 0x680504: + shift_c040 += 2; + case 0x680500: + shift_c040 += 2; + case 0x680520: + shift_c040 += 2; + case 0x680508: + shift_c040 += 2; + } + + savedc040 = nv_rd32(devinit, 0xc040); + if (shift_c040 != 14) + nv_wr32(devinit, 0xc040, savedc040 & ~(3 << shift_c040)); + } + + if (oldramdac580 != ramdac580) + nv_wr32(devinit, 0x680580, ramdac580); + + if (!nv3035) + nv_wr32(devinit, reg2, pll2); + nv_wr32(devinit, reg1, pll1); + + if (shift_powerctrl_1 >= 0) + nv_wr32(devinit, 0x001584, saved_powerctrl_1); + if (chip_version >= 0x40) + nv_wr32(devinit, 0xc040, savedc040); +} + +void +setPLL_double_lowregs(struct nouveau_devinit *devinit, u32 NMNMreg, + struct nouveau_pll_vals *pv) +{ + /* When setting PLLs, there is a merry game of disabling and enabling + * various bits of hardware during the process. This function is a + * synthesis of six nv4x traces, nearly each card doing a subtly + * different thing. With luck all the necessary bits for each card are + * combined herein. Without luck it deviates from each card's formula + * so as to not work on any :) + */ + + uint32_t Preg = NMNMreg - 4; + bool mpll = Preg == 0x4020; + uint32_t oldPval = nv_rd32(devinit, Preg); + uint32_t NMNM = pv->NM2 << 16 | pv->NM1; + uint32_t Pval = (oldPval & (mpll ? ~(0x77 << 16) : ~(7 << 16))) | + 0xc << 28 | pv->log2P << 16; + uint32_t saved4600 = 0; + /* some cards have different maskc040s */ + uint32_t maskc040 = ~(3 << 14), savedc040; + bool single_stage = !pv->NM2 || pv->N2 == pv->M2; + + if (nv_rd32(devinit, NMNMreg) == NMNM && (oldPval & 0xc0070000) == Pval) + return; + + if (Preg == 0x4000) + maskc040 = ~0x333; + if (Preg == 0x4058) + maskc040 = ~(0xc << 24); + + if (mpll) { + struct nvbios_pll info; + uint8_t Pval2; + + if (nvbios_pll_parse(nouveau_bios(devinit), Preg, &info)) + return; + + Pval2 = pv->log2P + info.bias_p; + if (Pval2 > info.max_p) + Pval2 = info.max_p; + Pval |= 1 << 28 | Pval2 << 20; + + saved4600 = nv_rd32(devinit, 0x4600); + nv_wr32(devinit, 0x4600, saved4600 | 8 << 28); + } + if (single_stage) + Pval |= mpll ? 1 << 12 : 1 << 8; + + nv_wr32(devinit, Preg, oldPval | 1 << 28); + nv_wr32(devinit, Preg, Pval & ~(4 << 28)); + if (mpll) { + Pval |= 8 << 20; + nv_wr32(devinit, 0x4020, Pval & ~(0xc << 28)); + nv_wr32(devinit, 0x4038, Pval & ~(0xc << 28)); + } + + savedc040 = nv_rd32(devinit, 0xc040); + nv_wr32(devinit, 0xc040, savedc040 & maskc040); + + nv_wr32(devinit, NMNMreg, NMNM); + if (NMNMreg == 0x4024) + nv_wr32(devinit, 0x403c, NMNM); + + nv_wr32(devinit, Preg, Pval); + if (mpll) { + Pval &= ~(8 << 20); + nv_wr32(devinit, 0x4020, Pval); + nv_wr32(devinit, 0x4038, Pval); + nv_wr32(devinit, 0x4600, saved4600); + } + + nv_wr32(devinit, 0xc040, savedc040); + + if (mpll) { + nv_wr32(devinit, 0x4020, Pval & ~(1 << 28)); + nv_wr32(devinit, 0x4038, Pval & ~(1 << 28)); + } +} + +int +nv04_devinit_pll_set(struct nouveau_devinit *devinit, u32 type, u32 freq) +{ + struct nouveau_bios *bios = nouveau_bios(devinit); + struct nouveau_pll_vals pv; + struct nvbios_pll info; + int cv = bios->version.chip; + int N1, M1, N2, M2, P; int ret; - ret = nouveau_devinit_create(parent, engine, oclass, &priv); - *pobject = nv_object(priv); + ret = nvbios_pll_parse(bios, type > 0x405c ? type : type - 4, &info); if (ret) return ret; - priv->base.meminit = nv04_devinit_meminit; - priv->owner = -1; + ret = nv04_pll_calc(nv_subdev(devinit), &info, freq, + &N1, &M1, &N2, &M2, &P); + if (!ret) + return -EINVAL; + + pv.refclk = info.refclk; + pv.N1 = N1; + pv.M1 = M1; + pv.N2 = N2; + pv.M2 = M2; + pv.log2P = P; + + if (cv == 0x30 || cv == 0x31 || cv == 0x35 || cv == 0x36 || + cv >= 0x40) { + if (type > 0x405c) + setPLL_double_highregs(devinit, type, &pv); + else + setPLL_double_lowregs(devinit, type, &pv); + } else + setPLL_single(devinit, type, &pv); + return 0; } -void -nv04_devinit_dtor(struct nouveau_object *object) +int +nv04_devinit_fini(struct nouveau_object *object, bool suspend) { struct nv04_devinit_priv *priv = (void *)object; - /* restore vga owner saved at first init, and lock crtc regs */ - nv_wrvgaowner(priv, priv->owner); - nv_lockvgac(priv, true); + /* make i2c busses accessible */ + nv_mask(priv, 0x000200, 0x00000001, 0x00000001); - nouveau_devinit_destroy(&priv->base); + /* unlock extended vga crtc regs, and unslave crtcs */ + nv_lockvgac(priv, false); + if (priv->owner < 0) + priv->owner = nv_rdvgaowner(priv); + nv_wrvgaowner(priv, 0); + + return nouveau_devinit_fini(&priv->base, suspend); } int @@ -160,21 +425,35 @@ nv04_devinit_init(struct nouveau_object *object) return nouveau_devinit_init(&priv->base); } -int -nv04_devinit_fini(struct nouveau_object *object, bool suspend) +void +nv04_devinit_dtor(struct nouveau_object *object) { struct nv04_devinit_priv *priv = (void *)object; - /* make i2c busses accessible */ - nv_mask(priv, 0x000200, 0x00000001, 0x00000001); + /* restore vga owner saved at first init, and lock crtc regs */ + nv_wrvgaowner(priv, priv->owner); + nv_lockvgac(priv, true); - /* unlock extended vga crtc regs, and unslave crtcs */ - nv_lockvgac(priv, false); - if (priv->owner < 0) - priv->owner = nv_rdvgaowner(priv); - nv_wrvgaowner(priv, 0); + nouveau_devinit_destroy(&priv->base); +} - return nouveau_devinit_fini(&priv->base, suspend); +static int +nv04_devinit_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv04_devinit_priv *priv; + int ret; + + ret = nouveau_devinit_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->base.meminit = nv04_devinit_meminit; + priv->base.pll_set = nv04_devinit_pll_set; + priv->owner = -1; + return 0; } struct nouveau_oclass diff --git a/drivers/gpu/drm/nouveau/core/subdev/devinit/nv05.c b/drivers/gpu/drm/nouveau/core/subdev/devinit/nv05.c index 191447d0d252..b1912a8a8942 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/devinit/nv05.c +++ b/drivers/gpu/drm/nouveau/core/subdev/devinit/nv05.c @@ -24,12 +24,12 @@ * */ -#include #include #include #include #include "fbmem.h" +#include "priv.h" struct nv05_devinit_priv { struct nouveau_devinit base; @@ -144,6 +144,7 @@ nv05_devinit_ctor(struct nouveau_object *parent, struct nouveau_object *engine, return ret; priv->base.meminit = nv05_devinit_meminit; + priv->base.pll_set = nv04_devinit_pll_set; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/devinit/nv10.c b/drivers/gpu/drm/nouveau/core/subdev/devinit/nv10.c index eb76ffab6b0c..463b08fa0968 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/devinit/nv10.c +++ b/drivers/gpu/drm/nouveau/core/subdev/devinit/nv10.c @@ -24,10 +24,10 @@ * */ -#include #include #include "fbmem.h" +#include "priv.h" struct nv10_devinit_priv { struct nouveau_devinit base; @@ -109,6 +109,7 @@ nv10_devinit_ctor(struct nouveau_object *parent, struct nouveau_object *engine, return ret; priv->base.meminit = nv10_devinit_meminit; + priv->base.pll_set = nv04_devinit_pll_set; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/devinit/nv1a.c b/drivers/gpu/drm/nouveau/core/subdev/devinit/nv1a.c index 5b2ba630d913..e9743cdabe75 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/devinit/nv1a.c +++ b/drivers/gpu/drm/nouveau/core/subdev/devinit/nv1a.c @@ -22,8 +22,7 @@ * Authors: Ben Skeggs */ -#include -#include +#include "priv.h" struct nv1a_devinit_priv { struct nouveau_devinit base; @@ -43,6 +42,7 @@ nv1a_devinit_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; + priv->base.pll_set = nv04_devinit_pll_set; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/devinit/nv20.c b/drivers/gpu/drm/nouveau/core/subdev/devinit/nv20.c index eb32e99005e4..6cc6080d3bc0 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/devinit/nv20.c +++ b/drivers/gpu/drm/nouveau/core/subdev/devinit/nv20.c @@ -24,9 +24,7 @@ * */ -#include -#include - +#include "priv.h" #include "fbmem.h" struct nv20_devinit_priv { @@ -81,6 +79,7 @@ nv20_devinit_ctor(struct nouveau_object *parent, struct nouveau_object *engine, return ret; priv->base.meminit = nv20_devinit_meminit; + priv->base.pll_set = nv04_devinit_pll_set; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/devinit/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/devinit/nv50.c index 4a8577838417..6df72247c477 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/devinit/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/devinit/nv50.c @@ -1,5 +1,5 @@ /* - * Copyright 2012 Red Hat Inc. + * Copyright 2013 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"), @@ -26,37 +26,55 @@ #include #include #include -#include #include -struct nv50_devinit_priv { - struct nouveau_devinit base; -}; +#include "priv.h" static int -nv50_devinit_ctor(struct nouveau_object *parent, struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) +nv50_devinit_pll_set(struct nouveau_devinit *devinit, u32 type, u32 freq) { - struct nv50_devinit_priv *priv; + struct nv50_devinit_priv *priv = (void *)devinit; + struct nouveau_bios *bios = nouveau_bios(priv); + struct nvbios_pll info; + int N1, M1, N2, M2, P; int ret; - ret = nouveau_devinit_create(parent, engine, oclass, &priv); - *pobject = nv_object(priv); - if (ret) + ret = nvbios_pll_parse(bios, type, &info); + if (ret) { + nv_error(devinit, "failed to retrieve pll data, %d\n", ret); return ret; + } - return 0; -} + ret = nv04_pll_calc(nv_subdev(devinit), &info, freq, &N1, &M1, &N2, &M2, &P); + if (!ret) { + nv_error(devinit, "failed pll calculation\n"); + return ret; + } -static void -nv50_devinit_dtor(struct nouveau_object *object) -{ - struct nv50_devinit_priv *priv = (void *)object; - nouveau_devinit_destroy(&priv->base); + switch (info.type) { + case PLL_VPLL0: + case PLL_VPLL1: + nv_wr32(priv, info.reg + 0, 0x10000611); + nv_mask(priv, info.reg + 4, 0x00ff00ff, (M1 << 16) | N1); + nv_mask(priv, info.reg + 8, 0x7fff00ff, (P << 28) | + (M2 << 16) | N2); + break; + case PLL_MEMORY: + nv_mask(priv, info.reg + 0, 0x01ff0000, (P << 22) | + (info.bias_p << 19) | + (P << 16)); + nv_wr32(priv, info.reg + 4, (N1 << 8) | M1); + break; + default: + nv_mask(priv, info.reg + 0, 0x00070000, (P << 16)); + nv_wr32(priv, info.reg + 4, (N1 << 8) | M1); + break; + } + + return 0; } -static int +int nv50_devinit_init(struct nouveau_object *object) { struct nouveau_bios *bios = nouveau_bios(object); @@ -103,10 +121,20 @@ nv50_devinit_init(struct nouveau_object *object) } static int -nv50_devinit_fini(struct nouveau_object *object, bool suspend) +nv50_devinit_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) { - struct nv50_devinit_priv *priv = (void *)object; - return nouveau_devinit_fini(&priv->base, suspend); + struct nv50_devinit_priv *priv; + int ret; + + ret = nouveau_devinit_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->base.pll_set = nv50_devinit_pll_set; + return 0; } struct nouveau_oclass @@ -114,8 +142,8 @@ nv50_devinit_oclass = { .handle = NV_SUBDEV(DEVINIT, 0x50), .ofuncs = &(struct nouveau_ofuncs) { .ctor = nv50_devinit_ctor, - .dtor = nv50_devinit_dtor, + .dtor = _nouveau_devinit_dtor, .init = nv50_devinit_init, - .fini = nv50_devinit_fini, + .fini = _nouveau_devinit_fini, }, }; diff --git a/drivers/gpu/drm/nouveau/core/subdev/devinit/nva3.c b/drivers/gpu/drm/nouveau/core/subdev/devinit/nva3.c new file mode 100644 index 000000000000..76a68b290141 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/devinit/nva3.c @@ -0,0 +1,87 @@ +/* + * Copyright 2013 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 "priv.h" + +static int +nva3_devinit_pll_set(struct nouveau_devinit *devinit, u32 type, u32 freq) +{ + struct nva3_devinit_priv *priv = (void *)devinit; + struct nouveau_bios *bios = nouveau_bios(priv); + struct nvbios_pll info; + int N, fN, M, P; + int ret; + + ret = nvbios_pll_parse(bios, type, &info); + if (ret) + return ret; + + ret = nva3_pll_calc(nv_subdev(devinit), &info, freq, &N, &fN, &M, &P); + if (ret < 0) + return ret; + + switch (info.type) { + case PLL_VPLL0: + case PLL_VPLL1: + nv_wr32(priv, info.reg + 0, 0x50000610); + nv_mask(priv, info.reg + 4, 0x003fffff, + (P << 16) | (M << 8) | N); + nv_wr32(priv, info.reg + 8, fN); + break; + default: + nv_warn(priv, "0x%08x/%dKhz unimplemented\n", type, freq); + ret = -EINVAL; + break; + } + + return ret; +} + +static int +nva3_devinit_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv50_devinit_priv *priv; + int ret; + + ret = nouveau_devinit_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->base.pll_set = nva3_devinit_pll_set; + return 0; +} + +struct nouveau_oclass +nva3_devinit_oclass = { + .handle = NV_SUBDEV(DEVINIT, 0xa3), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nva3_devinit_ctor, + .dtor = _nouveau_devinit_dtor, + .init = nv50_devinit_init, + .fini = _nouveau_devinit_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/devinit/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/devinit/nvc0.c new file mode 100644 index 000000000000..dd78efbcae1a --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/devinit/nvc0.c @@ -0,0 +1,88 @@ +/* + * Copyright 2013 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 "priv.h" + +static int +nvc0_devinit_pll_set(struct nouveau_devinit *devinit, u32 type, u32 freq) +{ + struct nvc0_devinit_priv *priv = (void *)devinit; + struct nouveau_bios *bios = nouveau_bios(priv); + struct nvbios_pll info; + int N, fN, M, P; + int ret; + + ret = nvbios_pll_parse(bios, type, &info); + if (ret) + return ret; + + ret = nva3_pll_calc(nv_subdev(devinit), &info, freq, &N, &fN, &M, &P); + if (ret < 0) + return ret; + + switch (info.type) { + case PLL_VPLL0: + case PLL_VPLL1: + case PLL_VPLL2: + case PLL_VPLL3: + nv_mask(priv, info.reg + 0x0c, 0x00000000, 0x00000100); + nv_wr32(priv, info.reg + 0x04, (P << 16) | (N << 8) | M); + nv_wr32(priv, info.reg + 0x10, fN << 16); + break; + default: + nv_warn(priv, "0x%08x/%dKhz unimplemented\n", type, freq); + ret = -EINVAL; + break; + } + + return ret; +} + +static int +nvc0_devinit_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv50_devinit_priv *priv; + int ret; + + ret = nouveau_devinit_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->base.pll_set = nvc0_devinit_pll_set; + return 0; +} + +struct nouveau_oclass +nvc0_devinit_oclass = { + .handle = NV_SUBDEV(DEVINIT, 0xa3), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nvc0_devinit_ctor, + .dtor = _nouveau_devinit_dtor, + .init = nv50_devinit_init, + .fini = _nouveau_devinit_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/devinit/priv.h b/drivers/gpu/drm/nouveau/core/subdev/devinit/priv.h new file mode 100644 index 000000000000..7d622e2b0171 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/devinit/priv.h @@ -0,0 +1,25 @@ +#ifndef __NVKM_DEVINIT_PRIV_H__ +#define __NVKM_DEVINIT_PRIV_H__ + +#include +#include +#include +#include + +void nv04_devinit_dtor(struct nouveau_object *); +int nv04_devinit_init(struct nouveau_object *); +int nv04_devinit_fini(struct nouveau_object *, bool); +int nv04_devinit_pll_set(struct nouveau_devinit *, u32, u32); + +void setPLL_single(struct nouveau_devinit *, u32, struct nouveau_pll_vals *); +void setPLL_double_highregs(struct nouveau_devinit *, u32, struct nouveau_pll_vals *); +void setPLL_double_lowregs(struct nouveau_devinit *, u32, struct nouveau_pll_vals *); + + +struct nv50_devinit_priv { + struct nouveau_devinit base; +}; + +int nv50_devinit_init(struct nouveau_object *); + +#endif -- cgit v1.2.3 From 54ecff3e1ad22fd44443acde7f27f213758fdddc Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 5 Mar 2013 12:32:06 +1000 Subject: drm/nouveau/clk: change init ordering, no longer needed by devinit And, will depend on FB/VOLT/DAEMON being ready when it gets initialised so that it can set/restore clocks. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/core/include/core/device.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/include/core/device.h b/drivers/gpu/drm/nouveau/core/include/core/device.h index cb6b4cc6fedd..99b6600fe80a 100644 --- a/drivers/gpu/drm/nouveau/core/include/core/device.h +++ b/drivers/gpu/drm/nouveau/core/include/core/device.h @@ -17,8 +17,7 @@ enum nv_subdev_type { NVDEV_SUBDEV_DEVINIT, NVDEV_SUBDEV_GPIO, NVDEV_SUBDEV_I2C, - NVDEV_SUBDEV_CLOCK, - NVDEV_SUBDEV_DEVINIT_LAST = NVDEV_SUBDEV_CLOCK, + NVDEV_SUBDEV_DEVINIT_LAST = NVDEV_SUBDEV_I2C, /* This grouping of subdevs are initialised right after they've * been created, and are allowed to assume any subdevs in the @@ -35,6 +34,7 @@ enum nv_subdev_type { NVDEV_SUBDEV_VM, NVDEV_SUBDEV_BAR, NVDEV_SUBDEV_VOLT, + NVDEV_SUBDEV_CLOCK, NVDEV_SUBDEV_THERM, NVDEV_ENGINE_DMAOBJ, -- cgit v1.2.3 From dceef5d87cc01358cc1434416f3272e2ddc3d97a Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 4 Mar 2013 13:01:21 +1000 Subject: drm/nouveau/fb: initialise vram controller as pfb sub-object Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/Makefile | 11 + drivers/gpu/drm/nouveau/core/engine/fifo/nv40.c | 2 +- drivers/gpu/drm/nouveau/core/include/subdev/fb.h | 95 +++------ drivers/gpu/drm/nouveau/core/subdev/fb/base.c | 125 ++++++----- drivers/gpu/drm/nouveau/core/subdev/fb/nv04.c | 54 +---- drivers/gpu/drm/nouveau/core/subdev/fb/nv10.c | 20 +- drivers/gpu/drm/nouveau/core/subdev/fb/nv1a.c | 32 +-- drivers/gpu/drm/nouveau/core/subdev/fb/nv20.c | 26 +-- drivers/gpu/drm/nouveau/core/subdev/fb/nv25.c | 9 +- drivers/gpu/drm/nouveau/core/subdev/fb/nv30.c | 9 +- drivers/gpu/drm/nouveau/core/subdev/fb/nv35.c | 9 +- drivers/gpu/drm/nouveau/core/subdev/fb/nv36.c | 9 +- drivers/gpu/drm/nouveau/core/subdev/fb/nv40.c | 25 +-- drivers/gpu/drm/nouveau/core/subdev/fb/nv41.c | 23 +- drivers/gpu/drm/nouveau/core/subdev/fb/nv44.c | 22 +- drivers/gpu/drm/nouveau/core/subdev/fb/nv46.c | 7 +- drivers/gpu/drm/nouveau/core/subdev/fb/nv47.c | 7 +- drivers/gpu/drm/nouveau/core/subdev/fb/nv49.c | 25 +-- drivers/gpu/drm/nouveau/core/subdev/fb/nv4e.c | 15 +- drivers/gpu/drm/nouveau/core/subdev/fb/nv50.c | 199 +----------------- drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c | 143 +------------ drivers/gpu/drm/nouveau/core/subdev/fb/priv.h | 87 ++++++++ drivers/gpu/drm/nouveau/core/subdev/fb/ramnv04.c | 95 +++++++++ drivers/gpu/drm/nouveau/core/subdev/fb/ramnv10.c | 61 ++++++ drivers/gpu/drm/nouveau/core/subdev/fb/ramnv1a.c | 71 +++++++ drivers/gpu/drm/nouveau/core/subdev/fb/ramnv20.c | 63 ++++++ drivers/gpu/drm/nouveau/core/subdev/fb/ramnv40.c | 65 ++++++ drivers/gpu/drm/nouveau/core/subdev/fb/ramnv41.c | 64 ++++++ drivers/gpu/drm/nouveau/core/subdev/fb/ramnv44.c | 62 ++++++ drivers/gpu/drm/nouveau/core/subdev/fb/ramnv49.c | 64 ++++++ drivers/gpu/drm/nouveau/core/subdev/fb/ramnv4e.c | 55 +++++ drivers/gpu/drm/nouveau/core/subdev/fb/ramnv50.c | 232 +++++++++++++++++++++ drivers/gpu/drm/nouveau/core/subdev/fb/ramnvc0.c | 186 +++++++++++++++++ drivers/gpu/drm/nouveau/core/subdev/instmem/nv50.c | 4 +- drivers/gpu/drm/nouveau/core/subdev/ltcg/nvc0.c | 2 +- drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c | 4 +- drivers/gpu/drm/nouveau/nouveau_bo.c | 2 +- drivers/gpu/drm/nouveau/nouveau_chan.c | 4 +- drivers/gpu/drm/nouveau/nouveau_fbcon.c | 4 +- drivers/gpu/drm/nouveau/nouveau_mem.c | 14 +- drivers/gpu/drm/nouveau/nouveau_ttm.c | 6 +- drivers/gpu/drm/nouveau/nv50_display.c | 18 +- drivers/gpu/drm/nouveau/nv50_pm.c | 4 +- drivers/gpu/drm/nouveau/nva3_pm.c | 4 +- drivers/gpu/drm/nouveau/nvc0_pm.c | 8 +- 45 files changed, 1313 insertions(+), 733 deletions(-) create mode 100644 drivers/gpu/drm/nouveau/core/subdev/fb/priv.h create mode 100644 drivers/gpu/drm/nouveau/core/subdev/fb/ramnv04.c create mode 100644 drivers/gpu/drm/nouveau/core/subdev/fb/ramnv10.c create mode 100644 drivers/gpu/drm/nouveau/core/subdev/fb/ramnv1a.c create mode 100644 drivers/gpu/drm/nouveau/core/subdev/fb/ramnv20.c create mode 100644 drivers/gpu/drm/nouveau/core/subdev/fb/ramnv40.c create mode 100644 drivers/gpu/drm/nouveau/core/subdev/fb/ramnv41.c create mode 100644 drivers/gpu/drm/nouveau/core/subdev/fb/ramnv44.c create mode 100644 drivers/gpu/drm/nouveau/core/subdev/fb/ramnv49.c create mode 100644 drivers/gpu/drm/nouveau/core/subdev/fb/ramnv4e.c create mode 100644 drivers/gpu/drm/nouveau/core/subdev/fb/ramnv50.c create mode 100644 drivers/gpu/drm/nouveau/core/subdev/fb/ramnvc0.c diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index f689d31e71fa..3eb0d08c315b 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile @@ -80,6 +80,17 @@ nouveau-y += core/subdev/fb/nv49.o nouveau-y += core/subdev/fb/nv4e.o nouveau-y += core/subdev/fb/nv50.o nouveau-y += core/subdev/fb/nvc0.o +nouveau-y += core/subdev/fb/ramnv04.o +nouveau-y += core/subdev/fb/ramnv10.o +nouveau-y += core/subdev/fb/ramnv1a.o +nouveau-y += core/subdev/fb/ramnv20.o +nouveau-y += core/subdev/fb/ramnv40.o +nouveau-y += core/subdev/fb/ramnv41.o +nouveau-y += core/subdev/fb/ramnv44.o +nouveau-y += core/subdev/fb/ramnv49.o +nouveau-y += core/subdev/fb/ramnv4e.o +nouveau-y += core/subdev/fb/ramnv50.o +nouveau-y += core/subdev/fb/ramnvc0.o nouveau-y += core/subdev/gpio/base.o nouveau-y += core/subdev/gpio/nv10.o nouveau-y += core/subdev/gpio/nv50.o diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nv40.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nv40.c index 2b1f91721225..5c7433d5069f 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nv40.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nv40.c @@ -320,7 +320,7 @@ nv40_fifo_init(struct nouveau_object *object) break; default: nv_wr32(priv, 0x002230, 0x00000000); - nv_wr32(priv, 0x002220, ((pfb->ram.size - 512 * 1024 + + nv_wr32(priv, 0x002220, ((pfb->ram->size - 512 * 1024 + priv->ramfc->addr) >> 16) | 0x00030000); break; diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/fb.h b/drivers/gpu/drm/nouveau/core/include/subdev/fb.h index da470e6851b1..2e7405084261 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/fb.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/fb.h @@ -53,31 +53,7 @@ struct nouveau_fb { bool (*memtype_valid)(struct nouveau_fb *, u32 memtype); - struct { - enum { - NV_MEM_TYPE_UNKNOWN = 0, - NV_MEM_TYPE_STOLEN, - NV_MEM_TYPE_SGRAM, - NV_MEM_TYPE_SDRAM, - NV_MEM_TYPE_DDR1, - NV_MEM_TYPE_DDR2, - NV_MEM_TYPE_DDR3, - NV_MEM_TYPE_GDDR2, - NV_MEM_TYPE_GDDR3, - NV_MEM_TYPE_GDDR4, - NV_MEM_TYPE_GDDR5 - } type; - u64 stolen; - u64 size; - - int ranks; - int parts; - - int (*init)(struct nouveau_fb *); - int (*get)(struct nouveau_fb *, u64 size, u32 align, - u32 size_nc, u32 type, struct nouveau_mem **); - void (*put)(struct nouveau_fb *, struct nouveau_mem **); - } ram; + struct nouveau_ram *ram; struct nouveau_mm vram; struct nouveau_mm tags; @@ -102,18 +78,6 @@ nouveau_fb(void *obj) return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_FB]; } -#define nouveau_fb_create(p,e,c,d) \ - nouveau_subdev_create((p), (e), (c), 0, "PFB", "fb", (d)) -int nouveau_fb_preinit(struct nouveau_fb *); -void nouveau_fb_destroy(struct nouveau_fb *); -int nouveau_fb_init(struct nouveau_fb *); -#define nouveau_fb_fini(p,s) \ - nouveau_subdev_fini(&(p)->base, (s)) - -void _nouveau_fb_dtor(struct nouveau_object *); -int _nouveau_fb_init(struct nouveau_object *); -#define _nouveau_fb_fini _nouveau_subdev_fini - extern struct nouveau_oclass nv04_fb_oclass; extern struct nouveau_oclass nv10_fb_oclass; extern struct nouveau_oclass nv1a_fb_oclass; @@ -132,40 +96,31 @@ extern struct nouveau_oclass nv4e_fb_oclass; extern struct nouveau_oclass nv50_fb_oclass; extern struct nouveau_oclass nvc0_fb_oclass; -struct nouveau_bios; -int nouveau_fb_bios_memtype(struct nouveau_bios *); - -bool nv04_fb_memtype_valid(struct nouveau_fb *, u32 memtype); - -void nv10_fb_tile_init(struct nouveau_fb *, int i, u32 addr, u32 size, - u32 pitch, u32 flags, struct nouveau_fb_tile *); -void nv10_fb_tile_fini(struct nouveau_fb *, int i, struct nouveau_fb_tile *); -void nv10_fb_tile_prog(struct nouveau_fb *, int, struct nouveau_fb_tile *); - -int nv20_fb_vram_init(struct nouveau_fb *); -void nv20_fb_tile_init(struct nouveau_fb *, int i, u32 addr, u32 size, - u32 pitch, u32 flags, struct nouveau_fb_tile *); -void nv20_fb_tile_fini(struct nouveau_fb *, int i, struct nouveau_fb_tile *); -void nv20_fb_tile_prog(struct nouveau_fb *, int, struct nouveau_fb_tile *); - -int nv30_fb_init(struct nouveau_object *); -void nv30_fb_tile_init(struct nouveau_fb *, int i, u32 addr, u32 size, - u32 pitch, u32 flags, struct nouveau_fb_tile *); - -void nv40_fb_tile_comp(struct nouveau_fb *, int i, u32 size, u32 flags, - struct nouveau_fb_tile *); - -int nv41_fb_vram_init(struct nouveau_fb *); -int nv41_fb_init(struct nouveau_object *); -void nv41_fb_tile_prog(struct nouveau_fb *, int, struct nouveau_fb_tile *); - -int nv44_fb_vram_init(struct nouveau_fb *); -int nv44_fb_init(struct nouveau_object *); -void nv44_fb_tile_prog(struct nouveau_fb *, int, struct nouveau_fb_tile *); +struct nouveau_ram { + struct nouveau_object base; + enum { + NV_MEM_TYPE_UNKNOWN = 0, + NV_MEM_TYPE_STOLEN, + NV_MEM_TYPE_SGRAM, + NV_MEM_TYPE_SDRAM, + NV_MEM_TYPE_DDR1, + NV_MEM_TYPE_DDR2, + NV_MEM_TYPE_DDR3, + NV_MEM_TYPE_GDDR2, + NV_MEM_TYPE_GDDR3, + NV_MEM_TYPE_GDDR4, + NV_MEM_TYPE_GDDR5 + } type; + u64 stolen; + u64 size; + u32 tags; -void nv46_fb_tile_init(struct nouveau_fb *, int i, u32 addr, u32 size, - u32 pitch, u32 flags, struct nouveau_fb_tile *); + int ranks; + int parts; -void nv50_fb_vram_del(struct nouveau_fb *, struct nouveau_mem **); + int (*get)(struct nouveau_fb *, u64 size, u32 align, + u32 size_nc, u32 type, struct nouveau_mem **); + void (*put)(struct nouveau_fb *, struct nouveau_mem **); +}; #endif diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/base.c b/drivers/gpu/drm/nouveau/core/subdev/fb/base.c index d62045f454b2..821cd75b86a3 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/base.c @@ -57,7 +57,57 @@ nouveau_fb_bios_memtype(struct nouveau_bios *bios) } int -nouveau_fb_preinit(struct nouveau_fb *pfb) +_nouveau_fb_fini(struct nouveau_object *object, bool suspend) +{ + struct nouveau_fb *pfb = (void *)object; + int ret; + + ret = nv_ofuncs(pfb->ram)->fini(nv_object(pfb->ram), suspend); + if (ret && suspend) + return ret; + + return nouveau_subdev_fini(&pfb->base, suspend); +} + +int +_nouveau_fb_init(struct nouveau_object *object) +{ + struct nouveau_fb *pfb = (void *)object; + int ret, i; + + ret = nouveau_subdev_init(&pfb->base); + if (ret) + return ret; + + ret = nv_ofuncs(pfb->ram)->init(nv_object(pfb->ram)); + if (ret) + return ret; + + for (i = 0; i < pfb->tile.regions; i++) + pfb->tile.prog(pfb, i, &pfb->tile.region[i]); + + return 0; +} + +void +_nouveau_fb_dtor(struct nouveau_object *object) +{ + struct nouveau_fb *pfb = (void *)object; + int i; + + for (i = 0; i < pfb->tile.regions; i++) + pfb->tile.fini(pfb, i, &pfb->tile.region[i]); + nouveau_mm_fini(&pfb->tags); + nouveau_mm_fini(&pfb->vram); + + nouveau_object_ref(NULL, (struct nouveau_object **)&pfb->ram); + nouveau_subdev_destroy(&pfb->base); +} + +int +nouveau_fb_create_(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, struct nouveau_oclass *ramcls, + int length, void **pobject) { static const char *name[] = { [NV_MEM_TYPE_UNKNOWN] = "unknown", @@ -72,69 +122,42 @@ nouveau_fb_preinit(struct nouveau_fb *pfb) [NV_MEM_TYPE_GDDR4 ] = "GDDR4", [NV_MEM_TYPE_GDDR5 ] = "GDDR5", }; - int ret, tags; + struct nouveau_object *ram; + struct nouveau_fb *pfb; + int ret; - tags = pfb->ram.init(pfb); - if (tags < 0 || !pfb->ram.size) { + ret = nouveau_subdev_create_(parent, engine, oclass, 0, "PFB", "fb", + length, pobject); + pfb = *pobject; + if (ret) + return ret; + + ret = nouveau_object_ctor(nv_object(pfb), nv_object(pfb), + ramcls, NULL, 0, &ram); + if (ret) { nv_fatal(pfb, "error detecting memory configuration!!\n"); - return (tags < 0) ? tags : -ERANGE; + return ret; } + atomic_dec(&ram->parent->refcount); + atomic_dec(&ram->engine->refcount); + pfb->ram = (void *)ram; + if (!nouveau_mm_initialised(&pfb->vram)) { - ret = nouveau_mm_init(&pfb->vram, 0, pfb->ram.size >> 12, 1); + ret = nouveau_mm_init(&pfb->vram, 0, pfb->ram->size >> 12, 1); if (ret) return ret; } if (!nouveau_mm_initialised(&pfb->tags)) { - ret = nouveau_mm_init(&pfb->tags, 0, tags ? ++tags : 0, 1); + ret = nouveau_mm_init(&pfb->tags, 0, pfb->ram->tags ? + ++pfb->ram->tags : 0, 1); if (ret) return ret; } - nv_info(pfb, "RAM type: %s\n", name[pfb->ram.type]); - nv_info(pfb, "RAM size: %d MiB\n", (int)(pfb->ram.size >> 20)); - nv_info(pfb, " ZCOMP: %d tags\n", tags); + nv_info(pfb, "RAM type: %s\n", name[pfb->ram->type]); + nv_info(pfb, "RAM size: %d MiB\n", (int)(pfb->ram->size >> 20)); + nv_info(pfb, " ZCOMP: %d tags\n", pfb->ram->tags); return 0; } - -void -nouveau_fb_destroy(struct nouveau_fb *pfb) -{ - int i; - - for (i = 0; i < pfb->tile.regions; i++) - pfb->tile.fini(pfb, i, &pfb->tile.region[i]); - nouveau_mm_fini(&pfb->tags); - nouveau_mm_fini(&pfb->vram); - - nouveau_subdev_destroy(&pfb->base); -} - -void -_nouveau_fb_dtor(struct nouveau_object *object) -{ - struct nouveau_fb *pfb = (void *)object; - nouveau_fb_destroy(pfb); -} -int -nouveau_fb_init(struct nouveau_fb *pfb) -{ - int ret, i; - - ret = nouveau_subdev_init(&pfb->base); - if (ret) - return ret; - - for (i = 0; i < pfb->tile.regions; i++) - pfb->tile.prog(pfb, i, &pfb->tile.region[i]); - - return 0; -} - -int -_nouveau_fb_init(struct nouveau_object *object) -{ - struct nouveau_fb *pfb = (void *)object; - return nouveau_fb_init(pfb); -} diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv04.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv04.c index 6e369f85361e..1f103c7b89fa 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv04.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv04.c @@ -22,24 +22,8 @@ * Authors: Ben Skeggs */ -#include +#include "priv.h" -#define NV04_PFB_BOOT_0 0x00100000 -# define NV04_PFB_BOOT_0_RAM_AMOUNT 0x00000003 -# define NV04_PFB_BOOT_0_RAM_AMOUNT_32MB 0x00000000 -# define NV04_PFB_BOOT_0_RAM_AMOUNT_4MB 0x00000001 -# define NV04_PFB_BOOT_0_RAM_AMOUNT_8MB 0x00000002 -# define NV04_PFB_BOOT_0_RAM_AMOUNT_16MB 0x00000003 -# define NV04_PFB_BOOT_0_RAM_WIDTH_128 0x00000004 -# define NV04_PFB_BOOT_0_RAM_TYPE 0x00000028 -# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_8MBIT 0x00000000 -# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT 0x00000008 -# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT_4BANK 0x00000010 -# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_16MBIT 0x00000018 -# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBIT 0x00000020 -# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBITX16 0x00000028 -# define NV04_PFB_BOOT_0_UMA_ENABLE 0x00000100 -# define NV04_PFB_BOOT_0_UMA_SIZE 0x0000f000 #define NV04_PFB_CFG0 0x00100200 struct nv04_fb_priv { @@ -55,37 +39,6 @@ nv04_fb_memtype_valid(struct nouveau_fb *pfb, u32 tile_flags) return false; } -static int -nv04_fb_vram_init(struct nouveau_fb *pfb) -{ - u32 boot0 = nv_rd32(pfb, NV04_PFB_BOOT_0); - if (boot0 & 0x00000100) { - pfb->ram.size = ((boot0 >> 12) & 0xf) * 2 + 2; - pfb->ram.size *= 1024 * 1024; - } else { - switch (boot0 & NV04_PFB_BOOT_0_RAM_AMOUNT) { - case NV04_PFB_BOOT_0_RAM_AMOUNT_32MB: - pfb->ram.size = 32 * 1024 * 1024; - break; - case NV04_PFB_BOOT_0_RAM_AMOUNT_16MB: - pfb->ram.size = 16 * 1024 * 1024; - break; - case NV04_PFB_BOOT_0_RAM_AMOUNT_8MB: - pfb->ram.size = 8 * 1024 * 1024; - break; - case NV04_PFB_BOOT_0_RAM_AMOUNT_4MB: - pfb->ram.size = 4 * 1024 * 1024; - break; - } - } - - if ((boot0 & 0x00000038) <= 0x10) - pfb->ram.type = NV_MEM_TYPE_SGRAM; - else - pfb->ram.type = NV_MEM_TYPE_SDRAM; - return 0; -} - static int nv04_fb_init(struct nouveau_object *object) { @@ -112,14 +65,13 @@ nv04_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv04_fb_priv *priv; int ret; - ret = nouveau_fb_create(parent, engine, oclass, &priv); + ret = nouveau_fb_create(parent, engine, oclass, &nv04_ram_oclass, &priv); *pobject = nv_object(priv); if (ret) return ret; priv->base.memtype_valid = nv04_fb_memtype_valid; - priv->base.ram.init = nv04_fb_vram_init; - return nouveau_fb_preinit(&priv->base); + return 0; } struct nouveau_oclass diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv10.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv10.c index edbbe26e858d..be069b5306b6 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv10.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv10.c @@ -24,25 +24,12 @@ * */ -#include +#include "priv.h" struct nv10_fb_priv { struct nouveau_fb base; }; -static int -nv10_fb_vram_init(struct nouveau_fb *pfb) -{ - u32 cfg0 = nv_rd32(pfb, 0x100200); - if (cfg0 & 0x00000001) - pfb->ram.type = NV_MEM_TYPE_DDR1; - else - pfb->ram.type = NV_MEM_TYPE_SDRAM; - - pfb->ram.size = nv_rd32(pfb, 0x10020c) & 0xff000000; - return 0; -} - void nv10_fb_tile_init(struct nouveau_fb *pfb, int i, u32 addr, u32 size, u32 pitch, u32 flags, struct nouveau_fb_tile *tile) @@ -78,18 +65,17 @@ nv10_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv10_fb_priv *priv; int ret; - ret = nouveau_fb_create(parent, engine, oclass, &priv); + ret = nouveau_fb_create(parent, engine, oclass, &nv10_ram_oclass, &priv); *pobject = nv_object(priv); if (ret) return ret; priv->base.memtype_valid = nv04_fb_memtype_valid; - priv->base.ram.init = nv10_fb_vram_init; priv->base.tile.regions = 8; priv->base.tile.init = nv10_fb_tile_init; priv->base.tile.fini = nv10_fb_tile_fini; priv->base.tile.prog = nv10_fb_tile_prog; - return nouveau_fb_preinit(&priv->base); + return 0; } struct nouveau_oclass diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv1a.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv1a.c index 48366841db4a..57a2af0079b3 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv1a.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv1a.c @@ -24,37 +24,12 @@ * */ -#include +#include "priv.h" struct nv1a_fb_priv { struct nouveau_fb base; }; -static int -nv1a_fb_vram_init(struct nouveau_fb *pfb) -{ - struct pci_dev *bridge; - u32 mem, mib; - - bridge = pci_get_bus_and_slot(0, PCI_DEVFN(0, 1)); - if (!bridge) { - nv_fatal(pfb, "no bridge device\n"); - return -ENODEV; - } - - if (nv_device(pfb)->chipset == 0x1a) { - pci_read_config_dword(bridge, 0x7c, &mem); - mib = ((mem >> 6) & 31) + 1; - } else { - pci_read_config_dword(bridge, 0x84, &mem); - mib = ((mem >> 4) & 127) + 1; - } - - pfb->ram.type = NV_MEM_TYPE_STOLEN; - pfb->ram.size = mib * 1024 * 1024; - return 0; -} - static int nv1a_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, void *data, u32 size, @@ -63,18 +38,17 @@ nv1a_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv1a_fb_priv *priv; int ret; - ret = nouveau_fb_create(parent, engine, oclass, &priv); + ret = nouveau_fb_create(parent, engine, oclass, &nv1a_ram_oclass, &priv); *pobject = nv_object(priv); if (ret) return ret; priv->base.memtype_valid = nv04_fb_memtype_valid; - priv->base.ram.init = nv1a_fb_vram_init; priv->base.tile.regions = 8; priv->base.tile.init = nv10_fb_tile_init; priv->base.tile.fini = nv10_fb_tile_fini; priv->base.tile.prog = nv10_fb_tile_prog; - return nouveau_fb_preinit(&priv->base); + return 0; } struct nouveau_oclass diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv20.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv20.c index 5d14612a2c8e..b18c4e63bb47 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv20.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv20.c @@ -24,29 +24,12 @@ * */ -#include +#include "priv.h" struct nv20_fb_priv { struct nouveau_fb base; }; -int -nv20_fb_vram_init(struct nouveau_fb *pfb) -{ - u32 pbus1218 = nv_rd32(pfb, 0x001218); - - switch (pbus1218 & 0x00000300) { - case 0x00000000: pfb->ram.type = NV_MEM_TYPE_SDRAM; break; - case 0x00000100: pfb->ram.type = NV_MEM_TYPE_DDR1; break; - case 0x00000200: pfb->ram.type = NV_MEM_TYPE_GDDR3; break; - case 0x00000300: pfb->ram.type = NV_MEM_TYPE_GDDR2; break; - } - pfb->ram.size = (nv_rd32(pfb, 0x10020c) & 0xff000000); - pfb->ram.parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1; - - return nv_rd32(pfb, 0x100320); -} - void nv20_fb_tile_init(struct nouveau_fb *pfb, int i, u32 addr, u32 size, u32 pitch, u32 flags, struct nouveau_fb_tile *tile) @@ -65,7 +48,7 @@ nv20_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags, struct nouveau_fb_tile *tile) { u32 tiles = DIV_ROUND_UP(size, 0x40); - u32 tags = round_up(tiles / pfb->ram.parts, 0x40); + u32 tags = round_up(tiles / pfb->ram->parts, 0x40); if (!nouveau_mm_head(&pfb->tags, 1, tags, tags, 1, &tile->tag)) { if (!(flags & 2)) tile->zcomp = 0x00000000; /* Z16 */ else tile->zcomp = 0x04000000; /* Z24S8 */ @@ -105,19 +88,18 @@ nv20_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv20_fb_priv *priv; int ret; - ret = nouveau_fb_create(parent, engine, oclass, &priv); + ret = nouveau_fb_create(parent, engine, oclass, &nv20_ram_oclass, &priv); *pobject = nv_object(priv); if (ret) return ret; priv->base.memtype_valid = nv04_fb_memtype_valid; - priv->base.ram.init = nv20_fb_vram_init; priv->base.tile.regions = 8; priv->base.tile.init = nv20_fb_tile_init; priv->base.tile.comp = nv20_fb_tile_comp; priv->base.tile.fini = nv20_fb_tile_fini; priv->base.tile.prog = nv20_fb_tile_prog; - return nouveau_fb_preinit(&priv->base); + return 0; } struct nouveau_oclass diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv25.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv25.c index 0042ace6bef9..32ccabf10c45 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv25.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv25.c @@ -24,7 +24,7 @@ * */ -#include +#include "priv.h" struct nv25_fb_priv { struct nouveau_fb base; @@ -35,7 +35,7 @@ nv25_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags, struct nouveau_fb_tile *tile) { u32 tiles = DIV_ROUND_UP(size, 0x40); - u32 tags = round_up(tiles / pfb->ram.parts, 0x40); + u32 tags = round_up(tiles / pfb->ram->parts, 0x40); if (!nouveau_mm_head(&pfb->tags, 1, tags, tags, 1, &tile->tag)) { if (!(flags & 2)) tile->zcomp = 0x00100000; /* Z16 */ else tile->zcomp = 0x00200000; /* Z24S8 */ @@ -54,19 +54,18 @@ nv25_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv25_fb_priv *priv; int ret; - ret = nouveau_fb_create(parent, engine, oclass, &priv); + ret = nouveau_fb_create(parent, engine, oclass, &nv20_ram_oclass, &priv); *pobject = nv_object(priv); if (ret) return ret; priv->base.memtype_valid = nv04_fb_memtype_valid; - priv->base.ram.init = nv20_fb_vram_init; priv->base.tile.regions = 8; priv->base.tile.init = nv20_fb_tile_init; priv->base.tile.comp = nv25_fb_tile_comp; priv->base.tile.fini = nv20_fb_tile_fini; priv->base.tile.prog = nv20_fb_tile_prog; - return nouveau_fb_preinit(&priv->base); + return 0; } struct nouveau_oclass diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv30.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv30.c index a7ba0d048aec..bef756d43d33 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv30.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv30.c @@ -24,7 +24,7 @@ * */ -#include +#include "priv.h" struct nv30_fb_priv { struct nouveau_fb base; @@ -54,7 +54,7 @@ nv30_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags, struct nouveau_fb_tile *tile) { u32 tiles = DIV_ROUND_UP(size, 0x40); - u32 tags = round_up(tiles / pfb->ram.parts, 0x40); + u32 tags = round_up(tiles / pfb->ram->parts, 0x40); if (!nouveau_mm_head(&pfb->tags, 1, tags, tags, 1, &tile->tag)) { if (flags & 2) tile->zcomp |= 0x01000000; /* Z16 */ else tile->zcomp |= 0x02000000; /* Z24S8 */ @@ -132,19 +132,18 @@ nv30_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv30_fb_priv *priv; int ret; - ret = nouveau_fb_create(parent, engine, oclass, &priv); + ret = nouveau_fb_create(parent, engine, oclass, &nv20_ram_oclass, &priv); *pobject = nv_object(priv); if (ret) return ret; priv->base.memtype_valid = nv04_fb_memtype_valid; - priv->base.ram.init = nv20_fb_vram_init; priv->base.tile.regions = 8; priv->base.tile.init = nv30_fb_tile_init; priv->base.tile.comp = nv30_fb_tile_comp; priv->base.tile.fini = nv20_fb_tile_fini; priv->base.tile.prog = nv20_fb_tile_prog; - return nouveau_fb_preinit(&priv->base); + return 0; } struct nouveau_oclass diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv35.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv35.c index 092f6f4f3521..097d8e3824f2 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv35.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv35.c @@ -24,7 +24,7 @@ * */ -#include +#include "priv.h" struct nv35_fb_priv { struct nouveau_fb base; @@ -35,7 +35,7 @@ nv35_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags, struct nouveau_fb_tile *tile) { u32 tiles = DIV_ROUND_UP(size, 0x40); - u32 tags = round_up(tiles / pfb->ram.parts, 0x40); + u32 tags = round_up(tiles / pfb->ram->parts, 0x40); if (!nouveau_mm_head(&pfb->tags, 1, tags, tags, 1, &tile->tag)) { if (flags & 2) tile->zcomp |= 0x04000000; /* Z16 */ else tile->zcomp |= 0x08000000; /* Z24S8 */ @@ -55,19 +55,18 @@ nv35_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv35_fb_priv *priv; int ret; - ret = nouveau_fb_create(parent, engine, oclass, &priv); + ret = nouveau_fb_create(parent, engine, oclass, &nv20_ram_oclass, &priv); *pobject = nv_object(priv); if (ret) return ret; priv->base.memtype_valid = nv04_fb_memtype_valid; - priv->base.ram.init = nv20_fb_vram_init; priv->base.tile.regions = 8; priv->base.tile.init = nv30_fb_tile_init; priv->base.tile.comp = nv35_fb_tile_comp; priv->base.tile.fini = nv20_fb_tile_fini; priv->base.tile.prog = nv20_fb_tile_prog; - return nouveau_fb_preinit(&priv->base); + return 0; } struct nouveau_oclass diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv36.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv36.c index 797ab3b821b9..9d6d9df896d9 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv36.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv36.c @@ -24,7 +24,7 @@ * */ -#include +#include "priv.h" struct nv36_fb_priv { struct nouveau_fb base; @@ -35,7 +35,7 @@ nv36_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags, struct nouveau_fb_tile *tile) { u32 tiles = DIV_ROUND_UP(size, 0x40); - u32 tags = round_up(tiles / pfb->ram.parts, 0x40); + u32 tags = round_up(tiles / pfb->ram->parts, 0x40); if (!nouveau_mm_head(&pfb->tags, 1, tags, tags, 1, &tile->tag)) { if (flags & 2) tile->zcomp |= 0x10000000; /* Z16 */ else tile->zcomp |= 0x20000000; /* Z24S8 */ @@ -55,19 +55,18 @@ nv36_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv36_fb_priv *priv; int ret; - ret = nouveau_fb_create(parent, engine, oclass, &priv); + ret = nouveau_fb_create(parent, engine, oclass, &nv20_ram_oclass, &priv); *pobject = nv_object(priv); if (ret) return ret; priv->base.memtype_valid = nv04_fb_memtype_valid; - priv->base.ram.init = nv20_fb_vram_init; priv->base.tile.regions = 8; priv->base.tile.init = nv30_fb_tile_init; priv->base.tile.comp = nv36_fb_tile_comp; priv->base.tile.fini = nv20_fb_tile_fini; priv->base.tile.prog = nv20_fb_tile_prog; - return nouveau_fb_preinit(&priv->base); + return 0; } struct nouveau_oclass diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv40.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv40.c index 65e131b90f37..33b4393a7829 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv40.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv40.c @@ -24,34 +24,18 @@ * */ -#include +#include "priv.h" struct nv40_fb_priv { struct nouveau_fb base; }; -static int -nv40_fb_vram_init(struct nouveau_fb *pfb) -{ - u32 pbus1218 = nv_rd32(pfb, 0x001218); - switch (pbus1218 & 0x00000300) { - case 0x00000000: pfb->ram.type = NV_MEM_TYPE_SDRAM; break; - case 0x00000100: pfb->ram.type = NV_MEM_TYPE_DDR1; break; - case 0x00000200: pfb->ram.type = NV_MEM_TYPE_GDDR3; break; - case 0x00000300: pfb->ram.type = NV_MEM_TYPE_DDR2; break; - } - - pfb->ram.size = nv_rd32(pfb, 0x10020c) & 0xff000000; - pfb->ram.parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1; - return nv_rd32(pfb, 0x100320); -} - void nv40_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags, struct nouveau_fb_tile *tile) { u32 tiles = DIV_ROUND_UP(size, 0x80); - u32 tags = round_up(tiles / pfb->ram.parts, 0x100); + u32 tags = round_up(tiles / pfb->ram->parts, 0x100); if ( (flags & 2) && !nouveau_mm_head(&pfb->tags, 1, tags, tags, 1, &tile->tag)) { tile->zcomp = 0x28000000; /* Z24S8_SPLIT_GRAD */ @@ -85,19 +69,18 @@ nv40_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv40_fb_priv *priv; int ret; - ret = nouveau_fb_create(parent, engine, oclass, &priv); + ret = nouveau_fb_create(parent, engine, oclass, &nv40_ram_oclass, &priv); *pobject = nv_object(priv); if (ret) return ret; priv->base.memtype_valid = nv04_fb_memtype_valid; - priv->base.ram.init = nv40_fb_vram_init; priv->base.tile.regions = 8; priv->base.tile.init = nv30_fb_tile_init; priv->base.tile.comp = nv40_fb_tile_comp; priv->base.tile.fini = nv20_fb_tile_fini; priv->base.tile.prog = nv20_fb_tile_prog; - return nouveau_fb_preinit(&priv->base); + return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv41.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv41.c index e9e5a08c41a1..02cd83789cd4 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv41.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv41.c @@ -24,28 +24,12 @@ * */ -#include +#include "priv.h" struct nv41_fb_priv { struct nouveau_fb base; }; -int -nv41_fb_vram_init(struct nouveau_fb *pfb) -{ - u32 pfb474 = nv_rd32(pfb, 0x100474); - if (pfb474 & 0x00000004) - pfb->ram.type = NV_MEM_TYPE_GDDR3; - if (pfb474 & 0x00000002) - pfb->ram.type = NV_MEM_TYPE_DDR2; - if (pfb474 & 0x00000001) - pfb->ram.type = NV_MEM_TYPE_DDR1; - - pfb->ram.size = nv_rd32(pfb, 0x10020c) & 0xff000000; - pfb->ram.parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1; - return nv_rd32(pfb, 0x100320); -} - void nv41_fb_tile_prog(struct nouveau_fb *pfb, int i, struct nouveau_fb_tile *tile) { @@ -78,19 +62,18 @@ nv41_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv41_fb_priv *priv; int ret; - ret = nouveau_fb_create(parent, engine, oclass, &priv); + ret = nouveau_fb_create(parent, engine, oclass, &nv41_ram_oclass, &priv); *pobject = nv_object(priv); if (ret) return ret; priv->base.memtype_valid = nv04_fb_memtype_valid; - priv->base.ram.init = nv41_fb_vram_init; priv->base.tile.regions = 12; priv->base.tile.init = nv30_fb_tile_init; priv->base.tile.comp = nv40_fb_tile_comp; priv->base.tile.fini = nv20_fb_tile_fini; priv->base.tile.prog = nv41_fb_tile_prog; - return nouveau_fb_preinit(&priv->base); + return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv44.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv44.c index ae89b5006f7a..c5246c29f293 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv44.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv44.c @@ -24,27 +24,12 @@ * */ -#include +#include "priv.h" struct nv44_fb_priv { struct nouveau_fb base; }; -int -nv44_fb_vram_init(struct nouveau_fb *pfb) -{ - u32 pfb474 = nv_rd32(pfb, 0x100474); - if (pfb474 & 0x00000004) - pfb->ram.type = NV_MEM_TYPE_GDDR3; - if (pfb474 & 0x00000002) - pfb->ram.type = NV_MEM_TYPE_DDR2; - if (pfb474 & 0x00000001) - pfb->ram.type = NV_MEM_TYPE_DDR1; - - pfb->ram.size = nv_rd32(pfb, 0x10020c) & 0xff000000; - return 0; -} - static void nv44_fb_tile_init(struct nouveau_fb *pfb, int i, u32 addr, u32 size, u32 pitch, u32 flags, struct nouveau_fb_tile *tile) @@ -87,18 +72,17 @@ nv44_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv44_fb_priv *priv; int ret; - ret = nouveau_fb_create(parent, engine, oclass, &priv); + ret = nouveau_fb_create(parent, engine, oclass, &nv44_ram_oclass, &priv); *pobject = nv_object(priv); if (ret) return ret; priv->base.memtype_valid = nv04_fb_memtype_valid; - priv->base.ram.init = nv44_fb_vram_init; priv->base.tile.regions = 12; priv->base.tile.init = nv44_fb_tile_init; priv->base.tile.fini = nv20_fb_tile_fini; priv->base.tile.prog = nv44_fb_tile_prog; - return nouveau_fb_preinit(&priv->base); + return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv46.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv46.c index 589b93ea2994..e2b57909bfca 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv46.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv46.c @@ -24,7 +24,7 @@ * */ -#include +#include "priv.h" struct nv46_fb_priv { struct nouveau_fb base; @@ -52,18 +52,17 @@ nv46_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv46_fb_priv *priv; int ret; - ret = nouveau_fb_create(parent, engine, oclass, &priv); + ret = nouveau_fb_create(parent, engine, oclass, &nv44_ram_oclass, &priv); *pobject = nv_object(priv); if (ret) return ret; priv->base.memtype_valid = nv04_fb_memtype_valid; - priv->base.ram.init = nv44_fb_vram_init; priv->base.tile.regions = 15; priv->base.tile.init = nv46_fb_tile_init; priv->base.tile.fini = nv20_fb_tile_fini; priv->base.tile.prog = nv44_fb_tile_prog; - return nouveau_fb_preinit(&priv->base); + return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv47.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv47.c index 818bba35b368..fe6a2278621d 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv47.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv47.c @@ -24,7 +24,7 @@ * */ -#include +#include "priv.h" struct nv47_fb_priv { struct nouveau_fb base; @@ -38,19 +38,18 @@ nv47_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv47_fb_priv *priv; int ret; - ret = nouveau_fb_create(parent, engine, oclass, &priv); + ret = nouveau_fb_create(parent, engine, oclass, &nv41_ram_oclass, &priv); *pobject = nv_object(priv); if (ret) return ret; priv->base.memtype_valid = nv04_fb_memtype_valid; - priv->base.ram.init = nv41_fb_vram_init; priv->base.tile.regions = 15; priv->base.tile.init = nv30_fb_tile_init; priv->base.tile.comp = nv40_fb_tile_comp; priv->base.tile.fini = nv20_fb_tile_fini; priv->base.tile.prog = nv41_fb_tile_prog; - return nouveau_fb_preinit(&priv->base); + return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv49.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv49.c index 84a31af16ab4..5eca99b8c7e2 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv49.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv49.c @@ -24,29 +24,12 @@ * */ -#include +#include "priv.h" struct nv49_fb_priv { struct nouveau_fb base; }; -static int -nv49_fb_vram_init(struct nouveau_fb *pfb) -{ - u32 pfb914 = nv_rd32(pfb, 0x100914); - - switch (pfb914 & 0x00000003) { - case 0x00000000: pfb->ram.type = NV_MEM_TYPE_DDR1; break; - case 0x00000001: pfb->ram.type = NV_MEM_TYPE_DDR2; break; - case 0x00000002: pfb->ram.type = NV_MEM_TYPE_GDDR3; break; - case 0x00000003: break; - } - - pfb->ram.size = nv_rd32(pfb, 0x10020c) & 0xff000000; - pfb->ram.parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1; - return nv_rd32(pfb, 0x100320); -} - static int nv49_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, void *data, u32 size, @@ -55,20 +38,18 @@ nv49_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv49_fb_priv *priv; int ret; - ret = nouveau_fb_create(parent, engine, oclass, &priv); + ret = nouveau_fb_create(parent, engine, oclass, &nv49_ram_oclass, &priv); *pobject = nv_object(priv); if (ret) return ret; priv->base.memtype_valid = nv04_fb_memtype_valid; - priv->base.ram.init = nv49_fb_vram_init; priv->base.tile.regions = 15; priv->base.tile.init = nv30_fb_tile_init; priv->base.tile.comp = nv40_fb_tile_comp; priv->base.tile.fini = nv20_fb_tile_fini; priv->base.tile.prog = nv41_fb_tile_prog; - - return nouveau_fb_preinit(&priv->base); + return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv4e.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv4e.c index 797fd558170b..1190b78a1e91 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv4e.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv4e.c @@ -24,20 +24,12 @@ * */ -#include +#include "priv.h" struct nv4e_fb_priv { struct nouveau_fb base; }; -static int -nv4e_fb_vram_init(struct nouveau_fb *pfb) -{ - pfb->ram.size = nv_rd32(pfb, 0x10020c) & 0xff000000; - pfb->ram.type = NV_MEM_TYPE_STOLEN; - return 0; -} - static int nv4e_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, void *data, u32 size, @@ -46,18 +38,17 @@ nv4e_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv4e_fb_priv *priv; int ret; - ret = nouveau_fb_create(parent, engine, oclass, &priv); + ret = nouveau_fb_create(parent, engine, oclass, &nv4e_ram_oclass, &priv); *pobject = nv_object(priv); if (ret) return ret; priv->base.memtype_valid = nv04_fb_memtype_valid; - priv->base.ram.init = nv4e_fb_vram_init; priv->base.tile.regions = 12; priv->base.tile.init = nv46_fb_tile_init; priv->base.tile.fini = nv20_fb_tile_fini; priv->base.tile.prog = nv44_fb_tile_prog; - return nouveau_fb_preinit(&priv->base); + return 0; } struct nouveau_oclass diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv50.c index 0772ec978165..da614ec5564b 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv50.c @@ -27,7 +27,7 @@ #include #include -#include +#include "priv.h" #include struct nv50_fb_priv { @@ -36,7 +36,8 @@ struct nv50_fb_priv { dma_addr_t r100c08; }; -static int types[0x80] = { +int +nv50_fb_memtype[0x80] = { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 0, @@ -50,192 +51,7 @@ static int types[0x80] = { static bool nv50_fb_memtype_valid(struct nouveau_fb *pfb, u32 memtype) { - return types[(memtype & 0xff00) >> 8] != 0; -} - -static u32 -nv50_fb_vram_rblock(struct nouveau_fb *pfb) -{ - int i, parts, colbits, rowbitsa, rowbitsb, banks; - u64 rowsize, predicted; - u32 r0, r4, rt, ru, rblock_size; - - r0 = nv_rd32(pfb, 0x100200); - r4 = nv_rd32(pfb, 0x100204); - rt = nv_rd32(pfb, 0x100250); - ru = nv_rd32(pfb, 0x001540); - nv_debug(pfb, "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 = 1 << (((r4 & 0x03000000) >> 24) + 2); - - rowsize = parts * banks * (1 << colbits) * 8; - predicted = rowsize << rowbitsa; - if (r0 & 0x00000004) - predicted += rowsize << rowbitsb; - - if (predicted != pfb->ram.size) { - nv_warn(pfb, "memory controller reports %d MiB VRAM\n", - (u32)(pfb->ram.size >> 20)); - } - - rblock_size = rowsize; - if (rt & 1) - rblock_size *= 3; - - nv_debug(pfb, "rblock %d bytes\n", rblock_size); - return rblock_size; -} - -static int -nv50_fb_vram_init(struct nouveau_fb *pfb) -{ - struct nouveau_device *device = nv_device(pfb); - struct nouveau_bios *bios = nouveau_bios(device); - const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */ - const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */ - u32 size, tags = 0; - int ret; - - pfb->ram.size = nv_rd32(pfb, 0x10020c); - pfb->ram.size = (pfb->ram.size & 0xffffff00) | - ((pfb->ram.size & 0x000000ff) << 32); - - size = (pfb->ram.size >> 12) - rsvd_head - rsvd_tail; - switch (device->chipset) { - case 0xaa: - case 0xac: - case 0xaf: /* IGPs, no reordering, no real VRAM */ - ret = nouveau_mm_init(&pfb->vram, rsvd_head, size, 1); - if (ret) - return ret; - - pfb->ram.type = NV_MEM_TYPE_STOLEN; - pfb->ram.stolen = (u64)nv_rd32(pfb, 0x100e10) << 12; - break; - default: - switch (nv_rd32(pfb, 0x100714) & 0x00000007) { - case 0: pfb->ram.type = NV_MEM_TYPE_DDR1; break; - case 1: - if (nouveau_fb_bios_memtype(bios) == NV_MEM_TYPE_DDR3) - pfb->ram.type = NV_MEM_TYPE_DDR3; - else - pfb->ram.type = NV_MEM_TYPE_DDR2; - break; - case 2: pfb->ram.type = NV_MEM_TYPE_GDDR3; break; - case 3: pfb->ram.type = NV_MEM_TYPE_GDDR4; break; - case 4: pfb->ram.type = NV_MEM_TYPE_GDDR5; break; - default: - break; - } - - ret = nouveau_mm_init(&pfb->vram, rsvd_head, size, - nv50_fb_vram_rblock(pfb) >> 12); - if (ret) - return ret; - - pfb->ram.ranks = (nv_rd32(pfb, 0x100200) & 0x4) ? 2 : 1; - tags = nv_rd32(pfb, 0x100320); - break; - } - - return tags; -} - -static int -nv50_fb_vram_new(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin, - u32 memtype, struct nouveau_mem **pmem) -{ - struct nv50_fb_priv *priv = (void *)pfb; - struct nouveau_mm *heap = &priv->base.vram; - struct nouveau_mm *tags = &priv->base.tags; - struct nouveau_mm_node *r; - struct nouveau_mem *mem; - int comp = (memtype & 0x300) >> 8; - int type = (memtype & 0x07f); - int back = (memtype & 0x800); - int min, max, ret; - - max = (size >> 12); - min = ncmin ? (ncmin >> 12) : max; - align >>= 12; - - mem = kzalloc(sizeof(*mem), GFP_KERNEL); - if (!mem) - return -ENOMEM; - - mutex_lock(&pfb->base.mutex); - if (comp) { - if (align == 16) { - int n = (max >> 4) * comp; - - ret = nouveau_mm_head(tags, 1, n, n, 1, &mem->tag); - if (ret) - mem->tag = NULL; - } - - if (unlikely(!mem->tag)) - comp = 0; - } - - INIT_LIST_HEAD(&mem->regions); - mem->memtype = (comp << 7) | type; - mem->size = max; - - type = types[type]; - do { - if (back) - ret = nouveau_mm_tail(heap, type, max, min, align, &r); - else - ret = nouveau_mm_head(heap, type, max, min, align, &r); - if (ret) { - mutex_unlock(&pfb->base.mutex); - pfb->ram.put(pfb, &mem); - return ret; - } - - list_add_tail(&r->rl_entry, &mem->regions); - max -= r->length; - } while (max); - mutex_unlock(&pfb->base.mutex); - - r = list_first_entry(&mem->regions, struct nouveau_mm_node, rl_entry); - mem->offset = (u64)r->offset << 12; - *pmem = mem; - return 0; -} - -void -nv50_fb_vram_del(struct nouveau_fb *pfb, struct nouveau_mem **pmem) -{ - struct nv50_fb_priv *priv = (void *)pfb; - struct nouveau_mm_node *this; - struct nouveau_mem *mem; - - mem = *pmem; - *pmem = NULL; - if (unlikely(mem == NULL)) - return; - - mutex_lock(&pfb->base.mutex); - while (!list_empty(&mem->regions)) { - this = list_first_entry(&mem->regions, typeof(*this), rl_entry); - - list_del(&this->rl_entry); - nouveau_mm_free(&priv->base.vram, &this); - } - - nouveau_mm_free(&priv->base.tags, &mem->tag); - mutex_unlock(&pfb->base.mutex); - - kfree(mem); + return nv50_fb_memtype[(memtype & 0xff00) >> 8] != 0; } static const struct nouveau_enum vm_dispatch_subclients[] = { @@ -432,7 +248,7 @@ nv50_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv50_fb_priv *priv; int ret; - ret = nouveau_fb_create(parent, engine, oclass, &priv); + ret = nouveau_fb_create(parent, engine, oclass, &nv50_ram_oclass, &priv); *pobject = nv_object(priv); if (ret) return ret; @@ -449,11 +265,8 @@ nv50_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, } priv->base.memtype_valid = nv50_fb_memtype_valid; - priv->base.ram.init = nv50_fb_vram_init; - priv->base.ram.get = nv50_fb_vram_new; - priv->base.ram.put = nv50_fb_vram_del; nv_subdev(priv)->intr = nv50_fb_intr; - return nouveau_fb_preinit(&priv->base); + return 0; } static void diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c index 86ad59203c8b..f35d76fd746d 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c @@ -22,9 +22,7 @@ * Authors: Ben Skeggs */ -#include -#include -#include +#include "priv.h" struct nvc0_fb_priv { struct nouveau_fb base; @@ -34,7 +32,6 @@ struct nvc0_fb_priv { extern const u8 nvc0_pte_storage_type_map[256]; - static bool nvc0_fb_memtype_valid(struct nouveau_fb *pfb, u32 tile_flags) { @@ -42,137 +39,6 @@ nvc0_fb_memtype_valid(struct nouveau_fb *pfb, u32 tile_flags) return likely((nvc0_pte_storage_type_map[memtype] != 0xff)); } -static int -nvc0_fb_vram_init(struct nouveau_fb *pfb) -{ - struct nouveau_bios *bios = nouveau_bios(pfb); - const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */ - const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */ - u32 parts = nv_rd32(pfb, 0x022438); - u32 pmask = nv_rd32(pfb, 0x022554); - u32 bsize = nv_rd32(pfb, 0x10f20c); - u32 offset, length; - bool uniform = true; - int ret, part; - - nv_debug(pfb, "0x100800: 0x%08x\n", nv_rd32(pfb, 0x100800)); - nv_debug(pfb, "parts 0x%08x mask 0x%08x\n", parts, pmask); - - pfb->ram.type = nouveau_fb_bios_memtype(bios); - pfb->ram.ranks = (nv_rd32(pfb, 0x10f200) & 0x00000004) ? 2 : 1; - - /* read amount of vram attached to each memory controller */ - for (part = 0; part < parts; part++) { - if (!(pmask & (1 << part))) { - u32 psize = nv_rd32(pfb, 0x11020c + (part * 0x1000)); - if (psize != bsize) { - if (psize < bsize) - bsize = psize; - uniform = false; - } - - nv_debug(pfb, "%d: mem_amount 0x%08x\n", part, psize); - pfb->ram.size += (u64)psize << 20; - } - } - - /* if all controllers have the same amount attached, there's no holes */ - if (uniform) { - offset = rsvd_head; - length = (pfb->ram.size >> 12) - rsvd_head - rsvd_tail; - return nouveau_mm_init(&pfb->vram, offset, length, 1); - } - - /* otherwise, address lowest common amount from 0GiB */ - ret = nouveau_mm_init(&pfb->vram, rsvd_head, (bsize << 8) * parts, 1); - if (ret) - return ret; - - /* and the rest starting from (8GiB + common_size) */ - offset = (0x0200000000ULL >> 12) + (bsize << 8); - length = (pfb->ram.size >> 12) - (bsize << 8) - rsvd_tail; - - ret = nouveau_mm_init(&pfb->vram, offset, length, 0); - if (ret) { - nouveau_mm_fini(&pfb->vram); - return ret; - } - - return 0; -} - -static int -nvc0_fb_vram_new(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin, - u32 memtype, struct nouveau_mem **pmem) -{ - struct nouveau_mm *mm = &pfb->vram; - struct nouveau_mm_node *r; - struct nouveau_mem *mem; - int type = (memtype & 0x0ff); - int back = (memtype & 0x800); - int ret; - const bool comp = nvc0_pte_storage_type_map[type] != type; - - size >>= 12; - align >>= 12; - ncmin >>= 12; - if (!ncmin) - ncmin = size; - - mem = kzalloc(sizeof(*mem), GFP_KERNEL); - if (!mem) - return -ENOMEM; - - INIT_LIST_HEAD(&mem->regions); - mem->size = size; - - mutex_lock(&pfb->base.mutex); - if (comp) { - struct nouveau_ltcg *ltcg = nouveau_ltcg(pfb->base.base.parent); - - /* compression only works with lpages */ - if (align == (1 << (17 - 12))) { - int n = size >> 5; - ltcg->tags_alloc(ltcg, n, &mem->tag); - } - if (unlikely(!mem->tag)) - type = nvc0_pte_storage_type_map[type]; - } - mem->memtype = type; - - do { - if (back) - ret = nouveau_mm_tail(mm, 1, size, ncmin, align, &r); - else - ret = nouveau_mm_head(mm, 1, size, ncmin, align, &r); - if (ret) { - mutex_unlock(&pfb->base.mutex); - pfb->ram.put(pfb, &mem); - return ret; - } - - list_add_tail(&r->rl_entry, &mem->regions); - size -= r->length; - } while (size); - mutex_unlock(&pfb->base.mutex); - - r = list_first_entry(&mem->regions, struct nouveau_mm_node, rl_entry); - mem->offset = (u64)r->offset << 12; - *pmem = mem; - return 0; -} - -static void -nvc0_fb_vram_del(struct nouveau_fb *pfb, struct nouveau_mem **pmem) -{ - struct nouveau_ltcg *ltcg = nouveau_ltcg(pfb->base.base.parent); - - if ((*pmem)->tag) - ltcg->tags_free(ltcg, &(*pmem)->tag); - - nv50_fb_vram_del(pfb, pmem); -} - static int nvc0_fb_init(struct nouveau_object *object) { @@ -212,15 +78,12 @@ nvc0_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nvc0_fb_priv *priv; int ret; - ret = nouveau_fb_create(parent, engine, oclass, &priv); + ret = nouveau_fb_create(parent, engine, oclass, &nvc0_ram_oclass, &priv); *pobject = nv_object(priv); if (ret) return ret; priv->base.memtype_valid = nvc0_fb_memtype_valid; - priv->base.ram.init = nvc0_fb_vram_init; - priv->base.ram.get = nvc0_fb_vram_new; - priv->base.ram.put = nvc0_fb_vram_del; priv->r100c10_page = alloc_page(GFP_KERNEL | __GFP_ZERO); if (priv->r100c10_page) { @@ -231,7 +94,7 @@ nvc0_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, return -EFAULT; } - return nouveau_fb_preinit(&priv->base); + return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/priv.h b/drivers/gpu/drm/nouveau/core/subdev/fb/priv.h new file mode 100644 index 000000000000..6c974dd83e8b --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/priv.h @@ -0,0 +1,87 @@ +#ifndef __NVKM_FB_PRIV_H__ +#define __NVKM_FB_PRIV_H__ + +#include + +#define nouveau_ram_create(p,e,o,d) \ + nouveau_object_create_((p), (e), (o), 0, sizeof(**d), (void **)d) +#define nouveau_ram_destroy(p) \ + nouveau_object_destroy(&(p)->base) +#define nouveau_ram_init(p) \ + nouveau_object_init(&(p)->base) +#define nouveau_ram_fini(p,s) \ + nouveau_object_fini(&(p)->base, (s)) + +#define _nouveau_ram_dtor nouveau_object_destroy +#define _nouveau_ram_init nouveau_object_init +#define _nouveau_ram_fini nouveau_object_fini + +extern struct nouveau_oclass nv04_ram_oclass; +extern struct nouveau_oclass nv10_ram_oclass; +extern struct nouveau_oclass nv1a_ram_oclass; +extern struct nouveau_oclass nv20_ram_oclass; +extern struct nouveau_oclass nv40_ram_oclass; +extern struct nouveau_oclass nv41_ram_oclass; +extern struct nouveau_oclass nv44_ram_oclass; +extern struct nouveau_oclass nv49_ram_oclass; +extern struct nouveau_oclass nv4e_ram_oclass; +extern struct nouveau_oclass nv50_ram_oclass; +extern struct nouveau_oclass nvc0_ram_oclass; + +#define nouveau_fb_create(p,e,c,r,d) \ + nouveau_fb_create_((p), (e), (c), (r), sizeof(**d), (void **)d) +#define nouveau_fb_destroy(p) ({ \ + struct nouveau_fb *pfb = (p); \ + _nouveau_fb_dtor(nv_object(pfb)); \ +}) +#define nouveau_fb_init(p) ({ \ + struct nouveau_fb *pfb = (p); \ + _nouveau_fb_init(nv_object(pfb)); \ +}) +#define nouveau_fb_fini(p,s) ({ \ + struct nouveau_fb *pfb = (p); \ + _nouveau_fb_fini(nv_object(pfb), (s)); \ +}) + +int nouveau_fb_create_(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, struct nouveau_oclass *, + int length, void **pobject); +void _nouveau_fb_dtor(struct nouveau_object *); +int _nouveau_fb_init(struct nouveau_object *); +int _nouveau_fb_fini(struct nouveau_object *, bool); + +struct nouveau_bios; +int nouveau_fb_bios_memtype(struct nouveau_bios *); + +bool nv04_fb_memtype_valid(struct nouveau_fb *, u32 memtype); + +void nv10_fb_tile_init(struct nouveau_fb *, int i, u32 addr, u32 size, + u32 pitch, u32 flags, struct nouveau_fb_tile *); +void nv10_fb_tile_fini(struct nouveau_fb *, int i, struct nouveau_fb_tile *); +void nv10_fb_tile_prog(struct nouveau_fb *, int, struct nouveau_fb_tile *); + +void nv20_fb_tile_init(struct nouveau_fb *, int i, u32 addr, u32 size, + u32 pitch, u32 flags, struct nouveau_fb_tile *); +void nv20_fb_tile_fini(struct nouveau_fb *, int i, struct nouveau_fb_tile *); +void nv20_fb_tile_prog(struct nouveau_fb *, int, struct nouveau_fb_tile *); + +int nv30_fb_init(struct nouveau_object *); +void nv30_fb_tile_init(struct nouveau_fb *, int i, u32 addr, u32 size, + u32 pitch, u32 flags, struct nouveau_fb_tile *); + +void nv40_fb_tile_comp(struct nouveau_fb *, int i, u32 size, u32 flags, + struct nouveau_fb_tile *); + +int nv41_fb_init(struct nouveau_object *); +void nv41_fb_tile_prog(struct nouveau_fb *, int, struct nouveau_fb_tile *); + +int nv44_fb_init(struct nouveau_object *); +void nv44_fb_tile_prog(struct nouveau_fb *, int, struct nouveau_fb_tile *); + +void nv46_fb_tile_init(struct nouveau_fb *, int i, u32 addr, u32 size, + u32 pitch, u32 flags, struct nouveau_fb_tile *); + +void nv50_ram_put(struct nouveau_fb *, struct nouveau_mem **); +extern int nv50_fb_memtype[0x80]; + +#endif diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv04.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv04.c new file mode 100644 index 000000000000..e781080d3327 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv04.c @@ -0,0 +1,95 @@ +/* + * Copyright 2013 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 + */ + +#define NV04_PFB_BOOT_0 0x00100000 +# define NV04_PFB_BOOT_0_RAM_AMOUNT 0x00000003 +# define NV04_PFB_BOOT_0_RAM_AMOUNT_32MB 0x00000000 +# define NV04_PFB_BOOT_0_RAM_AMOUNT_4MB 0x00000001 +# define NV04_PFB_BOOT_0_RAM_AMOUNT_8MB 0x00000002 +# define NV04_PFB_BOOT_0_RAM_AMOUNT_16MB 0x00000003 +# define NV04_PFB_BOOT_0_RAM_WIDTH_128 0x00000004 +# define NV04_PFB_BOOT_0_RAM_TYPE 0x00000028 +# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_8MBIT 0x00000000 +# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT 0x00000008 +# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT_4BANK 0x00000010 +# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_16MBIT 0x00000018 +# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBIT 0x00000020 +# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBITX16 0x00000028 +# define NV04_PFB_BOOT_0_UMA_ENABLE 0x00000100 +# define NV04_PFB_BOOT_0_UMA_SIZE 0x0000f000 + +#include "priv.h" + +static int +nv04_ram_create(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nouveau_fb *pfb = nouveau_fb(parent); + struct nouveau_ram *ram; + u32 boot0 = nv_rd32(pfb, NV04_PFB_BOOT_0); + int ret; + + ret = nouveau_ram_create(parent, engine, oclass, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + if (boot0 & 0x00000100) { + ram->size = ((boot0 >> 12) & 0xf) * 2 + 2; + ram->size *= 1024 * 1024; + } else { + switch (boot0 & NV04_PFB_BOOT_0_RAM_AMOUNT) { + case NV04_PFB_BOOT_0_RAM_AMOUNT_32MB: + ram->size = 32 * 1024 * 1024; + break; + case NV04_PFB_BOOT_0_RAM_AMOUNT_16MB: + ram->size = 16 * 1024 * 1024; + break; + case NV04_PFB_BOOT_0_RAM_AMOUNT_8MB: + ram->size = 8 * 1024 * 1024; + break; + case NV04_PFB_BOOT_0_RAM_AMOUNT_4MB: + ram->size = 4 * 1024 * 1024; + break; + } + } + + if ((boot0 & 0x00000038) <= 0x10) + ram->type = NV_MEM_TYPE_SGRAM; + else + ram->type = NV_MEM_TYPE_SDRAM; + return 0; +} + +struct nouveau_oclass +nv04_ram_oclass = { + .handle = 0, + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv04_ram_create, + .dtor = _nouveau_ram_dtor, + .init = _nouveau_ram_init, + .fini = _nouveau_ram_fini, + } +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv10.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv10.c new file mode 100644 index 000000000000..8311f3774edf --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv10.c @@ -0,0 +1,61 @@ +/* + * Copyright 2013 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 "priv.h" + +static int +nv10_ram_create(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nouveau_fb *pfb = nouveau_fb(parent); + struct nouveau_ram *ram; + u32 cfg0 = nv_rd32(pfb, 0x100200); + int ret; + + ret = nouveau_ram_create(parent, engine, oclass, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + if (cfg0 & 0x00000001) + ram->type = NV_MEM_TYPE_DDR1; + else + ram->type = NV_MEM_TYPE_SDRAM; + + ram->size = nv_rd32(pfb, 0x10020c) & 0xff000000; + return 0; +} + + +struct nouveau_oclass +nv10_ram_oclass = { + .handle = 0, + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv10_ram_create, + .dtor = _nouveau_ram_dtor, + .init = _nouveau_ram_init, + .fini = _nouveau_ram_fini, + } +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv1a.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv1a.c new file mode 100644 index 000000000000..d0caddfb9db0 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv1a.c @@ -0,0 +1,71 @@ +/* + * Copyright 2013 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 "priv.h" + +static int +nv1a_ram_create(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nouveau_fb *pfb = nouveau_fb(parent); + struct nouveau_ram *ram; + struct pci_dev *bridge; + u32 mem, mib; + int ret; + + bridge = pci_get_bus_and_slot(0, PCI_DEVFN(0, 1)); + if (!bridge) { + nv_fatal(pfb, "no bridge device\n"); + return -ENODEV; + } + + ret = nouveau_ram_create(parent, engine, oclass, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + if (nv_device(pfb)->chipset == 0x1a) { + pci_read_config_dword(bridge, 0x7c, &mem); + mib = ((mem >> 6) & 31) + 1; + } else { + pci_read_config_dword(bridge, 0x84, &mem); + mib = ((mem >> 4) & 127) + 1; + } + + ram->type = NV_MEM_TYPE_STOLEN; + ram->size = mib * 1024 * 1024; + return 0; +} + +struct nouveau_oclass +nv1a_ram_oclass = { + .handle = 0, + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv1a_ram_create, + .dtor = _nouveau_ram_dtor, + .init = _nouveau_ram_init, + .fini = _nouveau_ram_fini, + } +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv20.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv20.c new file mode 100644 index 000000000000..fdc11bba226d --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv20.c @@ -0,0 +1,63 @@ +/* + * Copyright 2013 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 "priv.h" + +static int +nv20_ram_create(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nouveau_fb *pfb = nouveau_fb(parent); + struct nouveau_ram *ram; + u32 pbus1218 = nv_rd32(pfb, 0x001218); + int ret; + + ret = nouveau_ram_create(parent, engine, oclass, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + switch (pbus1218 & 0x00000300) { + case 0x00000000: ram->type = NV_MEM_TYPE_SDRAM; break; + case 0x00000100: ram->type = NV_MEM_TYPE_DDR1; break; + case 0x00000200: ram->type = NV_MEM_TYPE_GDDR3; break; + case 0x00000300: ram->type = NV_MEM_TYPE_GDDR2; break; + } + ram->size = (nv_rd32(pfb, 0x10020c) & 0xff000000); + ram->parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1; + ram->tags = nv_rd32(pfb, 0x100320); + return 0; +} + +struct nouveau_oclass +nv20_ram_oclass = { + .handle = 0, + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv20_ram_create, + .dtor = _nouveau_ram_dtor, + .init = _nouveau_ram_init, + .fini = _nouveau_ram_fini, + } +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv40.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv40.c new file mode 100644 index 000000000000..ee49ac4dbdb6 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv40.c @@ -0,0 +1,65 @@ +/* + * Copyright 2013 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 "priv.h" + +static int +nv40_ram_create(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nouveau_fb *pfb = nouveau_fb(parent); + struct nouveau_ram *ram; + u32 pbus1218 = nv_rd32(pfb, 0x001218); + int ret; + + ret = nouveau_ram_create(parent, engine, oclass, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + switch (pbus1218 & 0x00000300) { + case 0x00000000: ram->type = NV_MEM_TYPE_SDRAM; break; + case 0x00000100: ram->type = NV_MEM_TYPE_DDR1; break; + case 0x00000200: ram->type = NV_MEM_TYPE_GDDR3; break; + case 0x00000300: ram->type = NV_MEM_TYPE_DDR2; break; + } + + ram->size = nv_rd32(pfb, 0x10020c) & 0xff000000; + ram->parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1; + ram->tags = nv_rd32(pfb, 0x100320); + return 0; +} + + +struct nouveau_oclass +nv40_ram_oclass = { + .handle = 0, + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv40_ram_create, + .dtor = _nouveau_ram_dtor, + .init = _nouveau_ram_init, + .fini = _nouveau_ram_fini, + } +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv41.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv41.c new file mode 100644 index 000000000000..1dab7e12abab --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv41.c @@ -0,0 +1,64 @@ +/* + * Copyright 2013 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 "priv.h" + +static int +nv41_ram_create(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nouveau_fb *pfb = nouveau_fb(parent); + struct nouveau_ram *ram; + u32 pfb474 = nv_rd32(pfb, 0x100474); + int ret; + + ret = nouveau_ram_create(parent, engine, oclass, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + if (pfb474 & 0x00000004) + ram->type = NV_MEM_TYPE_GDDR3; + if (pfb474 & 0x00000002) + ram->type = NV_MEM_TYPE_DDR2; + if (pfb474 & 0x00000001) + ram->type = NV_MEM_TYPE_DDR1; + + ram->size = nv_rd32(pfb, 0x10020c) & 0xff000000; + ram->parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1; + ram->tags = nv_rd32(pfb, 0x100320); + return 0; +} + +struct nouveau_oclass +nv41_ram_oclass = { + .handle = 0, + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv41_ram_create, + .dtor = _nouveau_ram_dtor, + .init = _nouveau_ram_init, + .fini = _nouveau_ram_fini, + } +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv44.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv44.c new file mode 100644 index 000000000000..25fff842e5c1 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv44.c @@ -0,0 +1,62 @@ +/* + * Copyright 2013 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 "priv.h" + +static int +nv44_ram_create(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nouveau_fb *pfb = nouveau_fb(parent); + struct nouveau_ram *ram; + u32 pfb474 = nv_rd32(pfb, 0x100474); + int ret; + + ret = nouveau_ram_create(parent, engine, oclass, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + if (pfb474 & 0x00000004) + ram->type = NV_MEM_TYPE_GDDR3; + if (pfb474 & 0x00000002) + ram->type = NV_MEM_TYPE_DDR2; + if (pfb474 & 0x00000001) + ram->type = NV_MEM_TYPE_DDR1; + + ram->size = nv_rd32(pfb, 0x10020c) & 0xff000000; + return 0; +} + +struct nouveau_oclass +nv44_ram_oclass = { + .handle = 0, + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv44_ram_create, + .dtor = _nouveau_ram_dtor, + .init = _nouveau_ram_init, + .fini = _nouveau_ram_fini, + } +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv49.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv49.c new file mode 100644 index 000000000000..19e3a9a63a02 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv49.c @@ -0,0 +1,64 @@ +/* + * Copyright 2013 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 "priv.h" + +static int +nv49_ram_create(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nouveau_fb *pfb = nouveau_fb(parent); + struct nouveau_ram *ram; + u32 pfb914 = nv_rd32(pfb, 0x100914); + int ret; + + ret = nouveau_ram_create(parent, engine, oclass, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + switch (pfb914 & 0x00000003) { + case 0x00000000: pfb->ram->type = NV_MEM_TYPE_DDR1; break; + case 0x00000001: pfb->ram->type = NV_MEM_TYPE_DDR2; break; + case 0x00000002: pfb->ram->type = NV_MEM_TYPE_GDDR3; break; + case 0x00000003: break; + } + + pfb->ram->size = nv_rd32(pfb, 0x10020c) & 0xff000000; + pfb->ram->parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1; + pfb->ram->tags = nv_rd32(pfb, 0x100320); + return 0; +} + +struct nouveau_oclass +nv49_ram_oclass = { + .handle = 0, + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv49_ram_create, + .dtor = _nouveau_ram_dtor, + .init = _nouveau_ram_init, + .fini = _nouveau_ram_fini, + } +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv4e.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv4e.c new file mode 100644 index 000000000000..7192aa6e5577 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv4e.c @@ -0,0 +1,55 @@ +/* + * Copyright 2013 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 "priv.h" + +static int +nv4e_ram_create(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nouveau_fb *pfb = nouveau_fb(parent); + struct nouveau_ram *ram; + int ret; + + ret = nouveau_ram_create(parent, engine, oclass, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + pfb->ram->size = nv_rd32(pfb, 0x10020c) & 0xff000000; + pfb->ram->type = NV_MEM_TYPE_STOLEN; + return 0; +} + +struct nouveau_oclass +nv4e_ram_oclass = { + .handle = 0, + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv4e_ram_create, + .dtor = _nouveau_ram_dtor, + .init = _nouveau_ram_init, + .fini = _nouveau_ram_fini, + } +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv50.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv50.c new file mode 100644 index 000000000000..af5aa7ee8ad9 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnv50.c @@ -0,0 +1,232 @@ +/* + * Copyright 2013 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 +#include +#include "priv.h" + +void +nv50_ram_put(struct nouveau_fb *pfb, struct nouveau_mem **pmem) +{ + struct nouveau_mm_node *this; + struct nouveau_mem *mem; + + mem = *pmem; + *pmem = NULL; + if (unlikely(mem == NULL)) + return; + + mutex_lock(&pfb->base.mutex); + while (!list_empty(&mem->regions)) { + this = list_first_entry(&mem->regions, typeof(*this), rl_entry); + + list_del(&this->rl_entry); + nouveau_mm_free(&pfb->vram, &this); + } + + nouveau_mm_free(&pfb->tags, &mem->tag); + mutex_unlock(&pfb->base.mutex); + + kfree(mem); +} + +static int +nv50_ram_get(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin, + u32 memtype, struct nouveau_mem **pmem) +{ + struct nouveau_mm *heap = &pfb->vram; + struct nouveau_mm *tags = &pfb->tags; + struct nouveau_mm_node *r; + struct nouveau_mem *mem; + int comp = (memtype & 0x300) >> 8; + int type = (memtype & 0x07f); + int back = (memtype & 0x800); + int min, max, ret; + + max = (size >> 12); + min = ncmin ? (ncmin >> 12) : max; + align >>= 12; + + mem = kzalloc(sizeof(*mem), GFP_KERNEL); + if (!mem) + return -ENOMEM; + + mutex_lock(&pfb->base.mutex); + if (comp) { + if (align == 16) { + int n = (max >> 4) * comp; + + ret = nouveau_mm_head(tags, 1, n, n, 1, &mem->tag); + if (ret) + mem->tag = NULL; + } + + if (unlikely(!mem->tag)) + comp = 0; + } + + INIT_LIST_HEAD(&mem->regions); + mem->memtype = (comp << 7) | type; + mem->size = max; + + type = nv50_fb_memtype[type]; + do { + if (back) + ret = nouveau_mm_tail(heap, type, max, min, align, &r); + else + ret = nouveau_mm_head(heap, type, max, min, align, &r); + if (ret) { + mutex_unlock(&pfb->base.mutex); + pfb->ram->put(pfb, &mem); + return ret; + } + + list_add_tail(&r->rl_entry, &mem->regions); + max -= r->length; + } while (max); + mutex_unlock(&pfb->base.mutex); + + r = list_first_entry(&mem->regions, struct nouveau_mm_node, rl_entry); + mem->offset = (u64)r->offset << 12; + *pmem = mem; + return 0; +} + +static u32 +nv50_fb_vram_rblock(struct nouveau_fb *pfb, struct nouveau_ram *ram) +{ + int i, parts, colbits, rowbitsa, rowbitsb, banks; + u64 rowsize, predicted; + u32 r0, r4, rt, ru, rblock_size; + + r0 = nv_rd32(pfb, 0x100200); + r4 = nv_rd32(pfb, 0x100204); + rt = nv_rd32(pfb, 0x100250); + ru = nv_rd32(pfb, 0x001540); + nv_debug(pfb, "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 = 1 << (((r4 & 0x03000000) >> 24) + 2); + + rowsize = parts * banks * (1 << colbits) * 8; + predicted = rowsize << rowbitsa; + if (r0 & 0x00000004) + predicted += rowsize << rowbitsb; + + if (predicted != ram->size) { + nv_warn(pfb, "memory controller reports %d MiB VRAM\n", + (u32)(ram->size >> 20)); + } + + rblock_size = rowsize; + if (rt & 1) + rblock_size *= 3; + + nv_debug(pfb, "rblock %d bytes\n", rblock_size); + return rblock_size; +} + +static int +nv50_ram_create(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 datasize, + struct nouveau_object **pobject) +{ + struct nouveau_fb *pfb = nouveau_fb(parent); + struct nouveau_device *device = nv_device(pfb); + struct nouveau_bios *bios = nouveau_bios(device); + struct nouveau_ram *ram; + const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */ + const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */ + u32 size; + int ret; + + ret = nouveau_ram_create(parent, engine, oclass, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + ram->size = nv_rd32(pfb, 0x10020c); + ram->size = (ram->size & 0xffffff00) | + ((ram->size & 0x000000ff) << 32); + + size = (ram->size >> 12) - rsvd_head - rsvd_tail; + switch (device->chipset) { + case 0xaa: + case 0xac: + case 0xaf: /* IGPs, no reordering, no real VRAM */ + ret = nouveau_mm_init(&pfb->vram, rsvd_head, size, 1); + if (ret) + return ret; + + ram->type = NV_MEM_TYPE_STOLEN; + ram->stolen = (u64)nv_rd32(pfb, 0x100e10) << 12; + break; + default: + switch (nv_rd32(pfb, 0x100714) & 0x00000007) { + case 0: ram->type = NV_MEM_TYPE_DDR1; break; + case 1: + if (nouveau_fb_bios_memtype(bios) == NV_MEM_TYPE_DDR3) + ram->type = NV_MEM_TYPE_DDR3; + else + ram->type = NV_MEM_TYPE_DDR2; + break; + case 2: ram->type = NV_MEM_TYPE_GDDR3; break; + case 3: ram->type = NV_MEM_TYPE_GDDR4; break; + case 4: ram->type = NV_MEM_TYPE_GDDR5; break; + default: + break; + } + + ret = nouveau_mm_init(&pfb->vram, rsvd_head, size, + nv50_fb_vram_rblock(pfb, ram) >> 12); + if (ret) + return ret; + + ram->ranks = (nv_rd32(pfb, 0x100200) & 0x4) ? 2 : 1; + ram->tags = nv_rd32(pfb, 0x100320); + break; + } + + ram->get = nv50_ram_get; + ram->put = nv50_ram_put; + return 0; +} + +struct nouveau_oclass +nv50_ram_oclass = { + .handle = 0, + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv50_ram_create, + .dtor = _nouveau_ram_dtor, + .init = _nouveau_ram_init, + .fini = _nouveau_ram_fini, + } +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnvc0.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnvc0.c new file mode 100644 index 000000000000..9c3634acbb9d --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnvc0.c @@ -0,0 +1,186 @@ +/* + * Copyright 2013 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 +#include + +#include "priv.h" + +extern const u8 nvc0_pte_storage_type_map[256]; + +void +nvc0_ram_put(struct nouveau_fb *pfb, struct nouveau_mem **pmem) +{ + struct nouveau_ltcg *ltcg = nouveau_ltcg(pfb); + + if ((*pmem)->tag) + ltcg->tags_free(ltcg, &(*pmem)->tag); + + nv50_ram_put(pfb, pmem); +} + +int +nvc0_ram_get(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin, + u32 memtype, struct nouveau_mem **pmem) +{ + struct nouveau_mm *mm = &pfb->vram; + struct nouveau_mm_node *r; + struct nouveau_mem *mem; + int type = (memtype & 0x0ff); + int back = (memtype & 0x800); + const bool comp = nvc0_pte_storage_type_map[type] != type; + int ret; + + size >>= 12; + align >>= 12; + ncmin >>= 12; + if (!ncmin) + ncmin = size; + + mem = kzalloc(sizeof(*mem), GFP_KERNEL); + if (!mem) + return -ENOMEM; + + INIT_LIST_HEAD(&mem->regions); + mem->size = size; + + mutex_lock(&pfb->base.mutex); + if (comp) { + struct nouveau_ltcg *ltcg = nouveau_ltcg(pfb); + + /* compression only works with lpages */ + if (align == (1 << (17 - 12))) { + int n = size >> 5; + ltcg->tags_alloc(ltcg, n, &mem->tag); + } + + if (unlikely(!mem->tag)) + type = nvc0_pte_storage_type_map[type]; + } + mem->memtype = type; + + do { + if (back) + ret = nouveau_mm_tail(mm, 1, size, ncmin, align, &r); + else + ret = nouveau_mm_head(mm, 1, size, ncmin, align, &r); + if (ret) { + mutex_unlock(&pfb->base.mutex); + pfb->ram->put(pfb, &mem); + return ret; + } + + list_add_tail(&r->rl_entry, &mem->regions); + size -= r->length; + } while (size); + mutex_unlock(&pfb->base.mutex); + + r = list_first_entry(&mem->regions, struct nouveau_mm_node, rl_entry); + mem->offset = (u64)r->offset << 12; + *pmem = mem; + return 0; +} + +static int +nvc0_ram_create(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nouveau_fb *pfb = nouveau_fb(parent); + struct nouveau_bios *bios = nouveau_bios(pfb); + struct nouveau_ram *ram; + const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */ + const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */ + u32 parts = nv_rd32(pfb, 0x022438); + u32 pmask = nv_rd32(pfb, 0x022554); + u32 bsize = nv_rd32(pfb, 0x10f20c); + u32 offset, length; + bool uniform = true; + int ret, part; + + ret = nouveau_ram_create(parent, engine, oclass, &ram); + *pobject = nv_object(ram); + if (ret) + return ret; + + nv_debug(pfb, "0x100800: 0x%08x\n", nv_rd32(pfb, 0x100800)); + nv_debug(pfb, "parts 0x%08x mask 0x%08x\n", parts, pmask); + + ram->type = nouveau_fb_bios_memtype(bios); + ram->ranks = (nv_rd32(pfb, 0x10f200) & 0x00000004) ? 2 : 1; + + /* read amount of vram attached to each memory controller */ + for (part = 0; part < parts; part++) { + if (!(pmask & (1 << part))) { + u32 psize = nv_rd32(pfb, 0x11020c + (part * 0x1000)); + if (psize != bsize) { + if (psize < bsize) + bsize = psize; + uniform = false; + } + + nv_debug(pfb, "%d: mem_amount 0x%08x\n", part, psize); + ram->size += (u64)psize << 20; + } + } + + /* if all controllers have the same amount attached, there's no holes */ + if (uniform) { + offset = rsvd_head; + length = (ram->size >> 12) - rsvd_head - rsvd_tail; + ret = nouveau_mm_init(&pfb->vram, offset, length, 1); + } else { + /* otherwise, address lowest common amount from 0GiB */ + ret = nouveau_mm_init(&pfb->vram, rsvd_head, + (bsize << 8) * parts, 1); + if (ret) + return ret; + + /* and the rest starting from (8GiB + common_size) */ + offset = (0x0200000000ULL >> 12) + (bsize << 8); + length = (ram->size >> 12) - (bsize << 8) - rsvd_tail; + + ret = nouveau_mm_init(&pfb->vram, offset, length, 0); + if (ret) + nouveau_mm_fini(&pfb->vram); + } + + if (ret) + return ret; + + ram->get = nvc0_ram_get; + ram->put = nvc0_ram_put; + return 0; +} + +struct nouveau_oclass +nvc0_ram_oclass = { + .handle = 0, + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nvc0_ram_create, + .dtor = _nouveau_ram_dtor, + .init = _nouveau_ram_init, + .fini = _nouveau_ram_fini, + } +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/instmem/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/instmem/nv50.c index cfc7e31461de..97bc5dff93e7 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/instmem/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/instmem/nv50.c @@ -56,7 +56,7 @@ nv50_instobj_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; - ret = pfb->ram.get(pfb, size, align, 0, 0x800, &node->mem); + ret = pfb->ram->get(pfb, size, align, 0, 0x800, &node->mem); if (ret) return ret; @@ -71,7 +71,7 @@ nv50_instobj_dtor(struct nouveau_object *object) { struct nv50_instobj_priv *node = (void *)object; struct nouveau_fb *pfb = nouveau_fb(object); - pfb->ram.put(pfb, &node->mem); + pfb->ram->put(pfb, &node->mem); nouveau_instobj_destroy(&node->base); } diff --git a/drivers/gpu/drm/nouveau/core/subdev/ltcg/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/ltcg/nvc0.c index fb794e997fbc..bcca883018f4 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/ltcg/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/ltcg/nvc0.c @@ -122,7 +122,7 @@ nvc0_ltcg_init_tag_ram(struct nouveau_fb *pfb, struct nvc0_ltcg_priv *priv) nv_wr32(priv, 0x17e000, priv->part_nr); /* tags for 1/4 of VRAM should be enough (8192/4 per GiB of VRAM) */ - priv->num_tags = (pfb->ram.size >> 17) / 4; + priv->num_tags = (pfb->ram->size >> 17) / 4; if (priv->num_tags > (1 << 17)) priv->num_tags = 1 << 17; /* we have 17 bits in PTE */ priv->num_tags = (priv->num_tags + 63) & ~63; /* round up to 64 */ diff --git a/drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c index e067f81c97b3..3a3693e74367 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c @@ -86,8 +86,8 @@ nv50_vm_map(struct nouveau_vma *vma, struct nouveau_gpuobj *pgt, /* IGPs don't have real VRAM, re-target to stolen system memory */ target = 0; - if (nouveau_fb(vma->vm->vmm)->ram.stolen) { - phys += nouveau_fb(vma->vm->vmm)->ram.stolen; + if (nouveau_fb(vma->vm->vmm)->ram->stolen) { + phys += nouveau_fb(vma->vm->vmm)->ram->stolen; target = 3; } diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index 7ff10711a4d0..86eef68517cb 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -255,7 +255,7 @@ set_placement_range(struct nouveau_bo *nvbo, uint32_t type) { struct nouveau_drm *drm = nouveau_bdev(nvbo->bo.bdev); struct nouveau_fb *pfb = nouveau_fb(drm->device); - u32 vram_pages = pfb->ram.size >> PAGE_SHIFT; + u32 vram_pages = pfb->ram->size >> PAGE_SHIFT; if (nv_device(drm->device)->card_type == NV_10 && nvbo->tile_mode && (type & TTM_PL_FLAG_VRAM) && diff --git a/drivers/gpu/drm/nouveau/nouveau_chan.c b/drivers/gpu/drm/nouveau/nouveau_chan.c index eaa80a2b81ee..e84f4c32331b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_chan.c +++ b/drivers/gpu/drm/nouveau/nouveau_chan.c @@ -147,7 +147,7 @@ nouveau_channel_prep(struct nouveau_drm *drm, struct nouveau_cli *cli, args.limit = client->vm->vmm->limit - 1; } else if (chan->push.buffer->bo.mem.mem_type == TTM_PL_VRAM) { - u64 limit = pfb->ram.size - imem->reserved - 1; + u64 limit = pfb->ram->size - imem->reserved - 1; if (device->card_type == NV_04) { /* nv04 vram pushbuf hack, retarget to its location in * the framebuffer bar rather than direct vram access.. @@ -282,7 +282,7 @@ nouveau_channel_init(struct nouveau_channel *chan, u32 vram, u32 gart) } else { args.flags = NV_DMA_TARGET_VRAM | NV_DMA_ACCESS_RDWR; args.start = 0; - args.limit = pfb->ram.size - imem->reserved - 1; + args.limit = pfb->ram->size - imem->reserved - 1; } ret = nouveau_object_new(nv_object(client), chan->handle, vram, diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index b03531781580..51fe6406edab 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -467,10 +467,10 @@ nouveau_fbcon_init(struct drm_device *dev) drm_fb_helper_single_add_all_connectors(&fbcon->helper); - if (pfb->ram.size <= 32 * 1024 * 1024) + if (pfb->ram->size <= 32 * 1024 * 1024) preferred_bpp = 8; else - if (pfb->ram.size <= 64 * 1024 * 1024) + if (pfb->ram->size <= 64 * 1024 * 1024) preferred_bpp = 16; else preferred_bpp = 32; diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c index 7e0ff10a2759..4f6a572f2258 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mem.c +++ b/drivers/gpu/drm/nouveau/nouveau_mem.c @@ -125,7 +125,7 @@ nv50_mem_timing_calc(struct drm_device *dev, u32 freq, t->reg[7] = 0x4000202 | (e->tCL - 1) << 16; /* XXX: P.version == 1 only has DDR2 and GDDR3? */ - if (pfb->ram.type == NV_MEM_TYPE_DDR2) { + if (pfb->ram->type == NV_MEM_TYPE_DDR2) { t->reg[5] |= (e->tCL + 3) << 8; t->reg[6] |= (t->tCWL - 2) << 8; t->reg[8] |= (e->tCL - 4); @@ -428,7 +428,7 @@ nouveau_mem_timing_calc(struct drm_device *dev, u32 freq, break; } - switch (pfb->ram.type * !ret) { + switch (pfb->ram->type * !ret) { case NV_MEM_TYPE_GDDR3: ret = nouveau_mem_gddr3_mr(dev, freq, e, len, boot, t); break; @@ -455,7 +455,7 @@ nouveau_mem_timing_calc(struct drm_device *dev, u32 freq, else dll_off = !!(ramcfg[2] & 0x40); - switch (pfb->ram.type) { + switch (pfb->ram->type) { case NV_MEM_TYPE_GDDR3: t->mr[1] &= ~0x00000040; t->mr[1] |= 0x00000040 * dll_off; @@ -522,7 +522,7 @@ nouveau_mem_timing_read(struct drm_device *dev, struct nouveau_pm_memtiming *t) t->odt = 0; t->drive_strength = 0; - switch (pfb->ram.type) { + switch (pfb->ram->type) { case NV_MEM_TYPE_DDR3: t->odt |= (t->mr[1] & 0x200) >> 7; case NV_MEM_TYPE_DDR2: @@ -551,7 +551,7 @@ nouveau_mem_exec(struct nouveau_mem_exec_func *exec, u32 mr[3] = { info->mr[0], info->mr[1], info->mr[2] }; u32 mr1_dlloff; - switch (pfb->ram.type) { + switch (pfb->ram->type) { case NV_MEM_TYPE_DDR2: tDLLK = 2000; mr1_dlloff = 0x00000001; @@ -572,7 +572,7 @@ nouveau_mem_exec(struct nouveau_mem_exec_func *exec, } /* fetch current MRs */ - switch (pfb->ram.type) { + switch (pfb->ram->type) { case NV_MEM_TYPE_GDDR3: case NV_MEM_TYPE_DDR3: mr[2] = exec->mrg(exec, 2); @@ -639,7 +639,7 @@ nouveau_mem_exec(struct nouveau_mem_exec_func *exec, exec->mrs (exec, 0, info->mr[0] | 0x00000000); exec->wait(exec, tMRD); exec->wait(exec, tDLLK); - if (pfb->ram.type == NV_MEM_TYPE_GDDR3) + if (pfb->ram->type == NV_MEM_TYPE_GDDR3) exec->precharge(exec); } diff --git a/drivers/gpu/drm/nouveau/nouveau_ttm.c b/drivers/gpu/drm/nouveau/nouveau_ttm.c index f19a15a3bc03..2311b7aca2d4 100644 --- a/drivers/gpu/drm/nouveau/nouveau_ttm.c +++ b/drivers/gpu/drm/nouveau/nouveau_ttm.c @@ -69,7 +69,7 @@ nouveau_vram_manager_del(struct ttm_mem_type_manager *man, struct nouveau_drm *drm = nouveau_bdev(man->bdev); struct nouveau_fb *pfb = nouveau_fb(drm->device); nouveau_mem_node_cleanup(mem->mm_node); - pfb->ram.put(pfb, (struct nouveau_mem **)&mem->mm_node); + pfb->ram->put(pfb, (struct nouveau_mem **)&mem->mm_node); } static int @@ -88,7 +88,7 @@ nouveau_vram_manager_new(struct ttm_mem_type_manager *man, if (nvbo->tile_flags & NOUVEAU_GEM_TILE_NONCONTIG) size_nc = 1 << nvbo->page_shift; - ret = pfb->ram.get(pfb, mem->num_pages << PAGE_SHIFT, + ret = pfb->ram->get(pfb, mem->num_pages << PAGE_SHIFT, mem->page_alignment << PAGE_SHIFT, size_nc, (nvbo->tile_flags >> 8) & 0x3ff, &node); if (ret) { @@ -386,7 +386,7 @@ nouveau_ttm_init(struct nouveau_drm *drm) } /* VRAM init */ - drm->gem.vram_available = nouveau_fb(drm->device)->ram.size; + drm->gem.vram_available = nouveau_fb(drm->device)->ram->size; drm->gem.vram_available -= nouveau_instmem(drm->device)->reserved; ret = ttm_bo_init_mm(&drm->ttm.bdev, TTM_PL_VRAM, diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index dd5e01f89f28..54dc6355b0c2 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -159,7 +159,7 @@ nv50_dmac_create_fbdma(struct nouveau_object *core, u32 parent) .flags = NV_DMA_TARGET_VRAM | NV_DMA_ACCESS_RDWR, .start = 0, - .limit = pfb->ram.size - 1, + .limit = pfb->ram->size - 1, .conf0 = NV50_DMA_CONF0_ENABLE | NV50_DMA_CONF0_PART_256, }, sizeof(struct nv_dma_class), &object); @@ -172,7 +172,7 @@ nv50_dmac_create_fbdma(struct nouveau_object *core, u32 parent) .flags = NV_DMA_TARGET_VRAM | NV_DMA_ACCESS_RDWR, .start = 0, - .limit = pfb->ram.size - 1, + .limit = pfb->ram->size - 1, .conf0 = NV50_DMA_CONF0_ENABLE | 0x70 | NV50_DMA_CONF0_PART_256, }, sizeof(struct nv_dma_class), &object); @@ -185,7 +185,7 @@ nv50_dmac_create_fbdma(struct nouveau_object *core, u32 parent) .flags = NV_DMA_TARGET_VRAM | NV_DMA_ACCESS_RDWR, .start = 0, - .limit = pfb->ram.size - 1, + .limit = pfb->ram->size - 1, .conf0 = NV50_DMA_CONF0_ENABLE | 0x7a | NV50_DMA_CONF0_PART_256, }, sizeof(struct nv_dma_class), &object); @@ -204,7 +204,7 @@ nvc0_dmac_create_fbdma(struct nouveau_object *core, u32 parent) .flags = NV_DMA_TARGET_VRAM | NV_DMA_ACCESS_RDWR, .start = 0, - .limit = pfb->ram.size - 1, + .limit = pfb->ram->size - 1, .conf0 = NVC0_DMA_CONF0_ENABLE, }, sizeof(struct nv_dma_class), &object); if (ret) @@ -216,7 +216,7 @@ nvc0_dmac_create_fbdma(struct nouveau_object *core, u32 parent) .flags = NV_DMA_TARGET_VRAM | NV_DMA_ACCESS_RDWR, .start = 0, - .limit = pfb->ram.size - 1, + .limit = pfb->ram->size - 1, .conf0 = NVC0_DMA_CONF0_ENABLE | 0xfe, }, sizeof(struct nv_dma_class), &object); if (ret) @@ -228,7 +228,7 @@ nvc0_dmac_create_fbdma(struct nouveau_object *core, u32 parent) .flags = NV_DMA_TARGET_VRAM | NV_DMA_ACCESS_RDWR, .start = 0, - .limit = pfb->ram.size - 1, + .limit = pfb->ram->size - 1, .conf0 = NVC0_DMA_CONF0_ENABLE | 0xfe, }, sizeof(struct nv_dma_class), &object); return ret; @@ -246,7 +246,7 @@ nvd0_dmac_create_fbdma(struct nouveau_object *core, u32 parent) .flags = NV_DMA_TARGET_VRAM | NV_DMA_ACCESS_RDWR, .start = 0, - .limit = pfb->ram.size - 1, + .limit = pfb->ram->size - 1, .conf0 = NVD0_DMA_CONF0_ENABLE | NVD0_DMA_CONF0_PAGE_LP, }, sizeof(struct nv_dma_class), &object); @@ -259,7 +259,7 @@ nvd0_dmac_create_fbdma(struct nouveau_object *core, u32 parent) .flags = NV_DMA_TARGET_VRAM | NV_DMA_ACCESS_RDWR, .start = 0, - .limit = pfb->ram.size - 1, + .limit = pfb->ram->size - 1, .conf0 = NVD0_DMA_CONF0_ENABLE | 0xfe | NVD0_DMA_CONF0_PAGE_LP, }, sizeof(struct nv_dma_class), &object); @@ -316,7 +316,7 @@ nv50_dmac_create(struct nouveau_object *core, u32 bclass, u8 head, .flags = NV_DMA_TARGET_VRAM | NV_DMA_ACCESS_RDWR, .start = 0, - .limit = pfb->ram.size - 1, + .limit = pfb->ram->size - 1, }, sizeof(struct nv_dma_class), &object); if (ret) return ret; diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c index 69620e39c90c..4efc33fa73fc 100644 --- a/drivers/gpu/drm/nouveau/nv50_pm.c +++ b/drivers/gpu/drm/nouveau/nv50_pm.c @@ -493,12 +493,12 @@ mclk_mrs(struct nouveau_mem_exec_func *exec, int mr, u32 data) struct hwsq_ucode *hwsq = &info->mclk_hwsq; if (mr <= 1) { - if (pfb->ram.ranks > 1) + if (pfb->ram->ranks > 1) hwsq_wr32(hwsq, 0x1002c8 + ((mr - 0) * 4), data); hwsq_wr32(hwsq, 0x1002c0 + ((mr - 0) * 4), data); } else if (mr <= 3) { - if (pfb->ram.ranks > 1) + if (pfb->ram->ranks > 1) hwsq_wr32(hwsq, 0x1002e8 + ((mr - 2) * 4), data); hwsq_wr32(hwsq, 0x1002e0 + ((mr - 2) * 4), data); } diff --git a/drivers/gpu/drm/nouveau/nva3_pm.c b/drivers/gpu/drm/nouveau/nva3_pm.c index 863f010fafeb..0d0ed597fea8 100644 --- a/drivers/gpu/drm/nouveau/nva3_pm.c +++ b/drivers/gpu/drm/nouveau/nva3_pm.c @@ -389,12 +389,12 @@ mclk_mrs(struct nouveau_mem_exec_func *exec, int mr, u32 data) struct nouveau_device *device = nouveau_dev(exec->dev); struct nouveau_fb *pfb = nouveau_fb(device); if (mr <= 1) { - if (pfb->ram.ranks > 1) + if (pfb->ram->ranks > 1) nv_wr32(device, 0x1002c8 + ((mr - 0) * 4), data); nv_wr32(device, 0x1002c0 + ((mr - 0) * 4), data); } else if (mr <= 3) { - if (pfb->ram.ranks > 1) + if (pfb->ram->ranks > 1) nv_wr32(device, 0x1002e8 + ((mr - 2) * 4), data); nv_wr32(device, 0x1002e0 + ((mr - 2) * 4), data); } diff --git a/drivers/gpu/drm/nouveau/nvc0_pm.c b/drivers/gpu/drm/nouveau/nvc0_pm.c index 0d34eb581179..3b7041cb013f 100644 --- a/drivers/gpu/drm/nouveau/nvc0_pm.c +++ b/drivers/gpu/drm/nouveau/nvc0_pm.c @@ -477,7 +477,7 @@ mclk_mrg(struct nouveau_mem_exec_func *exec, int mr) { struct nouveau_device *device = nouveau_dev(exec->dev); struct nouveau_fb *pfb = nouveau_fb(device); - if (pfb->ram.type != NV_MEM_TYPE_GDDR5) { + if (pfb->ram->type != NV_MEM_TYPE_GDDR5) { if (mr <= 1) return nv_rd32(device, 0x10f300 + ((mr - 0) * 4)); return nv_rd32(device, 0x10f320 + ((mr - 2) * 4)); @@ -496,15 +496,15 @@ mclk_mrs(struct nouveau_mem_exec_func *exec, int mr, u32 data) { struct nouveau_device *device = nouveau_dev(exec->dev); struct nouveau_fb *pfb = nouveau_fb(device); - if (pfb->ram.type != NV_MEM_TYPE_GDDR5) { + if (pfb->ram->type != NV_MEM_TYPE_GDDR5) { if (mr <= 1) { nv_wr32(device, 0x10f300 + ((mr - 0) * 4), data); - if (pfb->ram.ranks > 1) + if (pfb->ram->ranks > 1) nv_wr32(device, 0x10f308 + ((mr - 0) * 4), data); } else if (mr <= 3) { nv_wr32(device, 0x10f320 + ((mr - 2) * 4), data); - if (pfb->ram.ranks > 1) + if (pfb->ram->ranks > 1) nv_wr32(device, 0x10f328 + ((mr - 2) * 4), data); } } else { -- cgit v1.2.3 From da746d4ec9605302386bab46ea8dfdd66f94560c Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 24 Jan 2013 12:15:45 +1000 Subject: drm/nva3/clk: minor improvements to fractional N calculation Helps us to get identical numbers to the binary driver for (at least) Kepler memory PLLs, and fixes a rounding error. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/core/subdev/clock/pllnva3.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/subdev/clock/pllnva3.c b/drivers/gpu/drm/nouveau/core/subdev/clock/pllnva3.c index 4497378ba1e8..2fe1f712eefa 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/clock/pllnva3.c +++ b/drivers/gpu/drm/nouveau/core/subdev/clock/pllnva3.c @@ -50,8 +50,15 @@ nva3_pll_calc(struct nouveau_subdev *subdev, struct nvbios_pll *info, u32 tmp = freq * *P * M; N = tmp / info->refclk; fN = tmp % info->refclk; - if (!pfN && fN >= info->refclk / 2) - N++; + + if (!pfN) { + if (fN >= info->refclk / 2) + N++; + } else { + if (fN < info->refclk / 2) + N--; + fN = tmp - (N * info->refclk); + } if (N < info->vco1.min_n) continue; @@ -66,7 +73,8 @@ nva3_pll_calc(struct nouveau_subdev *subdev, struct nvbios_pll *info, } if (pfN) { - *pfN = (((fN << 13) / info->refclk) - 4096) & 0xffff; + *pfN = ((fN << 13) + info->refclk / 2) / info->refclk; + *pfN = (*pfN - 4096) & 0xffff; return freq; } } -- cgit v1.2.3 From 8d6f585d00f6268533616adb17dec83fda5ea5d4 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 7 May 2013 09:09:46 +1000 Subject: drm/nve0/fifo: create our playlists up-front, at startup Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c | 28 ++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c index eff2b5791db9..0338e66bc620 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c @@ -97,18 +97,6 @@ nve0_fifo_playlist_update(struct nve0_fifo_priv *priv, u32 engine) mutex_lock(&nv_subdev(priv)->mutex); cur = engn->playlist[engn->cur_playlist]; - if (unlikely(cur == NULL)) { - int ret = nouveau_gpuobj_new(nv_object(priv), NULL, - 0x8000, 0x1000, 0, &cur); - if (ret) { - mutex_unlock(&nv_subdev(priv)->mutex); - nv_error(priv, "playlist alloc failed\n"); - return; - } - - engn->playlist[engn->cur_playlist] = cur; - } - engn->cur_playlist = !engn->cur_playlist; for (i = 0, p = 0; i < priv->base.max; i++) { @@ -599,13 +587,25 @@ nve0_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_object **pobject) { struct nve0_fifo_priv *priv; - int ret; + int ret, i; ret = nouveau_fifo_create(parent, engine, oclass, 0, 4095, &priv); *pobject = nv_object(priv); if (ret) return ret; + for (i = 0; i < FIFO_ENGINE_NR; i++) { + ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x8000, 0x1000, + 0, &priv->engine[i].playlist[0]); + if (ret) + return ret; + + ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x8000, 0x1000, + 0, &priv->engine[i].playlist[1]); + if (ret) + return ret; + } + ret = nouveau_gpuobj_new(nv_object(priv), NULL, 4096 * 0x200, 0x1000, NVOBJ_FLAG_ZERO_ALLOC, &priv->user.mem); if (ret) @@ -636,7 +636,7 @@ nve0_fifo_dtor(struct nouveau_object *object) nouveau_gpuobj_unmap(&priv->user.bar); nouveau_gpuobj_ref(NULL, &priv->user.mem); - for (i = 0; i < ARRAY_SIZE(priv->engine); i++) { + for (i = 0; i < FIFO_ENGINE_NR; i++) { nouveau_gpuobj_ref(NULL, &priv->engine[i].playlist[1]); nouveau_gpuobj_ref(NULL, &priv->engine[i].playlist[0]); } -- cgit v1.2.3 From 3d8a6ed2470e6de2c318219bbab26a6561388630 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 7 May 2013 13:04:18 +1000 Subject: drm/nve0/gr: s/tp/tpc/ NVIDIA's name... Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/core/engine/graph/nve0.c | 53 ++++++++++++------------ 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c b/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c index b0a5a8847a89..8cb12c74d6e5 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c @@ -121,13 +121,13 @@ static const struct nouveau_enum nve0_sked_error[] = { }; static void -nve0_graph_mp_trap(struct nvc0_graph_priv *priv, int gpc, int tp) +nve0_graph_mp_trap(struct nvc0_graph_priv *priv, int gpc, int tpc) { + u32 werr = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x648)); + u32 gerr = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x650)); int i; - u32 werr = nv_rd32(priv, TPC_UNIT(gpc, tp, 0x648)); - u32 gerr = nv_rd32(priv, TPC_UNIT(gpc, tp, 0x650)); - nv_error(priv, "GPC%i/TP%i/MP trap:", gpc, tp); + nv_error(priv, "GPC%i/TPC%i/MP trap:", gpc, tpc); for (i = 0; i <= 31; ++i) { if (!(gerr & (1 << i))) @@ -135,6 +135,7 @@ nve0_graph_mp_trap(struct nvc0_graph_priv *priv, int gpc, int tp) pr_cont(" "); nouveau_enum_print(nve0_mp_global_error, i); } + if (werr) { pr_cont(" "); nouveau_enum_print(nve0_mp_warp_error, werr & 0xffff); @@ -142,51 +143,51 @@ nve0_graph_mp_trap(struct nvc0_graph_priv *priv, int gpc, int tp) pr_cont("\n"); /* disable MP trap to avoid spam */ - nv_mask(priv, TPC_UNIT(gpc, tp, 0x50c), 0x2, 0x0); + nv_mask(priv, TPC_UNIT(gpc, tpc, 0x50c), 0x2, 0x0); /* TODO: figure out how to resume after an MP trap */ } static void -nve0_graph_tp_trap(struct nvc0_graph_priv *priv, int gpc, int tp) +nve0_graph_tpc_trap(struct nvc0_graph_priv *priv, int gpc, int tpc) { - u32 stat = nv_rd32(priv, TPC_UNIT(gpc, tp, 0x508)); + u32 stat = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x508)); if (stat & 0x1) { - u32 trap = nv_rd32(priv, TPC_UNIT(gpc, tp, 0x224)); - nv_error(priv, "GPC%i/TP%i/TEX trap: %08x\n", - gpc, tp, trap); + u32 trap = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x224)); + nv_error(priv, "GPC%i/TPC%i/TEX trap: %08x\n", + gpc, tpc, trap); - nv_wr32(priv, TPC_UNIT(gpc, tp, 0x224), 0xc0000000); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x224), 0xc0000000); stat &= ~0x1; } if (stat & 0x2) { - nve0_graph_mp_trap(priv, gpc, tp); + nve0_graph_mp_trap(priv, gpc, tpc); stat &= ~0x2; } if (stat & 0x4) { - u32 trap = nv_rd32(priv, TPC_UNIT(gpc, tp, 0x084)); - nv_error(priv, "GPC%i/TP%i/POLY trap: %08x\n", - gpc, tp, trap); + u32 trap = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x084)); + nv_error(priv, "GPC%i/TPC%i/POLY trap: %08x\n", + gpc, tpc, trap); - nv_wr32(priv, TPC_UNIT(gpc, tp, 0x084), 0xc0000000); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x084), 0xc0000000); stat &= ~0x4; } if (stat & 0x8) { - u32 trap = nv_rd32(priv, TPC_UNIT(gpc, tp, 0x48c)); - nv_error(priv, "GPC%i/TP%i/L1C trap: %08x\n", - gpc, tp, trap); + u32 trap = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x48c)); + nv_error(priv, "GPC%i/TPC%i/L1C trap: %08x\n", + gpc, tpc, trap); - nv_wr32(priv, TPC_UNIT(gpc, tp, 0x48c), 0xc0000000); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x48c), 0xc0000000); stat &= ~0x8; } if (stat) { - nv_error(priv, "GPC%i/TP%i: unknown stat %08x\n", - gpc, tp, stat); + nv_error(priv, "GPC%i/TPC%i: unknown stat %08x\n", + gpc, tpc, stat); } } @@ -198,7 +199,7 @@ nve0_graph_gpc_trap(struct nvc0_graph_priv *priv) for (gpc = 0; gpc < 4; ++gpc) { u32 stat; - int tp; + int tpc; if (!(mask & (1 << gpc))) continue; @@ -257,9 +258,9 @@ nve0_graph_gpc_trap(struct nvc0_graph_priv *priv) stat &= ~0x0008; } - for (tp = 0; tp < 8; ++tp) { - if (stat & (1 << (16 + tp))) - nve0_graph_tp_trap(priv, gpc, tp); + for (tpc = 0; tpc < 8; ++tpc) { + if (stat & (1 << (16 + tpc))) + nve0_graph_tpc_trap(priv, gpc, tpc); } stat &= ~0xff0000; -- cgit v1.2.3 From 16b133df331459004cf77b9b82f68ff3a2bef2be Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 7 May 2013 13:13:41 +1000 Subject: drm/nve0/gr: attempt to resume after sm traps Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/core/engine/graph/nve0.c | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c b/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c index 8cb12c74d6e5..84249f8c99c6 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c @@ -89,9 +89,9 @@ static const struct nouveau_enum nve0_mp_warp_error[] = { {} }; -static const struct nouveau_enum nve0_mp_global_error[] = { - { 2, "MULTIPLE_WARP_ERRORS" }, - { 3, "OUT_OF_STACK_SPACE" }, +static const struct nouveau_bitfield nve0_mp_global_error[] = { + { 0x00000004, "MULTIPLE_WARP_ERRORS" }, + { 0x00000008, "OUT_OF_STACK_SPACE" }, {} }; @@ -125,27 +125,17 @@ nve0_graph_mp_trap(struct nvc0_graph_priv *priv, int gpc, int tpc) { u32 werr = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x648)); u32 gerr = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x650)); - int i; nv_error(priv, "GPC%i/TPC%i/MP trap:", gpc, tpc); - - for (i = 0; i <= 31; ++i) { - if (!(gerr & (1 << i))) - continue; - pr_cont(" "); - nouveau_enum_print(nve0_mp_global_error, i); - } - + nouveau_bitfield_print(nve0_mp_global_error, gerr); if (werr) { pr_cont(" "); nouveau_enum_print(nve0_mp_warp_error, werr & 0xffff); } pr_cont("\n"); - /* disable MP trap to avoid spam */ - nv_mask(priv, TPC_UNIT(gpc, tpc, 0x50c), 0x2, 0x0); - - /* TODO: figure out how to resume after an MP trap */ + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x648), 0x00000000); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x650), gerr); } static void -- cgit v1.2.3 From fec43a722abd3cec0cc730b4257771860706f8b7 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 13 May 2013 11:54:05 +1000 Subject: drm/nvc0/gr: port mp trap handling from calim's kepler code Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c | 44 ++++++++++++++++++++---- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c index f9b9d82c287f..af40e654ac89 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c @@ -237,6 +237,43 @@ nvc0_graph_ctxctl_isr(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x409c20, ustat); } +static const struct nouveau_enum nvc0_mp_warp_error[] = { + { 0x00, "NO_ERROR" }, + { 0x01, "STACK_MISMATCH" }, + { 0x05, "MISALIGNED_PC" }, + { 0x08, "MISALIGNED_GPR" }, + { 0x09, "INVALID_OPCODE" }, + { 0x0d, "GPR_OUT_OF_BOUNDS" }, + { 0x0e, "MEM_OUT_OF_BOUNDS" }, + { 0x0f, "UNALIGNED_MEM_ACCESS" }, + { 0x11, "INVALID_PARAM" }, + {} +}; + +static const struct nouveau_bitfield nvc0_mp_global_error[] = { + { 0x00000004, "MULTIPLE_WARP_ERRORS" }, + { 0x00000008, "OUT_OF_STACK_SPACE" }, + {} +}; + +static void +nvc0_graph_trap_mp(struct nvc0_graph_priv *priv, int gpc, int tpc) +{ + u32 werr = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x648)); + u32 gerr = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x650)); + + nv_error(priv, "GPC%i/TPC%i/MP trap:", gpc, tpc); + nouveau_bitfield_print(nvc0_mp_global_error, gerr); + if (werr) { + pr_cont(" "); + nouveau_enum_print(nvc0_mp_warp_error, werr & 0xffff); + } + pr_cont("\n"); + + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x648), 0x00000000); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x650), gerr); +} + static void nvc0_graph_trap_tpc(struct nvc0_graph_priv *priv, int gpc, int tpc) { @@ -251,12 +288,7 @@ nvc0_graph_trap_tpc(struct nvc0_graph_priv *priv, int gpc, int tpc) } if (stat & 0x00000002) { - u32 trap0 = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x0644)); - u32 trap1 = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x064c)); - nv_error(priv, "GPC%d/TPC%d/MP: 0x%08x 0x%08x\n", - gpc, tpc, trap0, trap1); - nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x0644), 0x001ffffe); - nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x064c), 0x0000000f); + nvc0_graph_trap_mp(priv, gpc, tpc); nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x0508), 0x00000002); stat &= ~0x00000002; } -- cgit v1.2.3 From c3032adb5c097775235e3a1959b71ab6421e4ffb Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 13 May 2013 20:41:18 +1000 Subject: drm/nv50/vm: handle bar tlb flushes internally Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/core/include/subdev/vm.h | 2 +- drivers/gpu/drm/nouveau/core/subdev/bar/nv50.c | 13 +++++++++---- drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c | 3 +++ 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/vm.h b/drivers/gpu/drm/nouveau/core/include/subdev/vm.h index 9d595efe667a..c888f93e0ee6 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/vm.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/vm.h @@ -58,7 +58,7 @@ struct nouveau_vm { int refcount; struct list_head pgd_list; - atomic_t engref[64]; //NVDEV_SUBDEV_NR]; + atomic_t engref[NVDEV_SUBDEV_NR]; struct nouveau_vm_pgt *pgt; u32 fpde; diff --git a/drivers/gpu/drm/nouveau/core/subdev/bar/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/bar/nv50.c index 649f1ced1fe0..160d27f3c7b4 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bar/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bar/nv50.c @@ -53,7 +53,6 @@ nv50_bar_kmap(struct nouveau_bar *bar, struct nouveau_mem *mem, return ret; nouveau_vm_map(vma, mem); - nv50_vm_flush_engine(nv_subdev(bar), 6); return 0; } @@ -69,7 +68,6 @@ nv50_bar_umap(struct nouveau_bar *bar, struct nouveau_mem *mem, return ret; nouveau_vm_map(vma, mem); - nv50_vm_flush_engine(nv_subdev(bar), 6); return 0; } @@ -77,7 +75,6 @@ static void nv50_bar_unmap(struct nouveau_bar *bar, struct nouveau_vma *vma) { nouveau_vm_unmap(vma); - nv50_vm_flush_engine(nv_subdev(bar), 6); nouveau_vm_put(vma); } @@ -147,6 +144,8 @@ nv50_bar_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; + atomic_inc(&vm->engref[NVDEV_SUBDEV_BAR]); + ret = nouveau_gpuobj_new(nv_object(priv), heap, ((limit-- - start) >> 12) * 8, 0x1000, NVOBJ_FLAG_ZERO_ALLOC, &vm->pgt[0].obj[0]); @@ -179,6 +178,8 @@ nv50_bar_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; + atomic_inc(&vm->engref[NVDEV_SUBDEV_BAR]); + ret = nouveau_vm_ref(vm, &priv->bar1_vm, priv->pgd); nouveau_vm_ref(NULL, &vm, NULL); if (ret) @@ -237,7 +238,11 @@ nv50_bar_init(struct nouveau_object *object) nv_mask(priv, 0x000200, 0x00000100, 0x00000000); nv_mask(priv, 0x000200, 0x00000100, 0x00000100); - nv50_vm_flush_engine(nv_subdev(priv), 6); + nv_wr32(priv, 0x100c80, 0x00060001); + if (!nv_wait(priv, 0x100c80, 0x00000001, 0x00000000)) { + nv_error(priv, "vm flush timeout\n"); + return -EBUSY; + } nv_wr32(priv, 0x001704, 0x00000000 | priv->mem->addr >> 12); nv_wr32(priv, 0x001704, 0x40000000 | priv->mem->addr >> 12); diff --git a/drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c index 3a3693e74367..6ed85efd89b2 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c @@ -155,6 +155,9 @@ nv50_vm_flush(struct nouveau_vm *vm) int i; for (i = 0; i < NVDEV_SUBDEV_NR; i++) { + if (atomic_read(&vm->engref[i]) && i == NVDEV_SUBDEV_BAR) { + nv50_vm_flush_engine(nv_subdev(vm->vmm), 6); + } else if (atomic_read(&vm->engref[i])) { engine = nouveau_engine(vm->vmm, i); if (engine && engine->tlb_flush) -- cgit v1.2.3 From 464d636bd0a7a905209816d1dee0838ccb79e57a Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 13 May 2013 20:55:46 +1000 Subject: drm/nv50/vm: remove explicit vm knowledge from engines This reverses the lock ordering between VM and gr/nv84:nvc0. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/core/engine/copy/nva3.c | 8 ---- drivers/gpu/drm/nouveau/core/engine/crypt/nv84.c | 8 ---- drivers/gpu/drm/nouveau/core/engine/crypt/nv98.c | 8 ---- drivers/gpu/drm/nouveau/core/engine/graph/nv50.c | 18 +++------ drivers/gpu/drm/nouveau/core/engine/mpeg/nv50.c | 8 ---- drivers/gpu/drm/nouveau/core/engine/mpeg/nv84.c | 1 - drivers/gpu/drm/nouveau/core/include/engine/mpeg.h | 1 - drivers/gpu/drm/nouveau/core/include/subdev/vm.h | 1 - drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c | 44 ++++++++++++---------- 9 files changed, 31 insertions(+), 66 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/copy/nva3.c b/drivers/gpu/drm/nouveau/core/engine/copy/nva3.c index d6dc2a65ccd1..85f2e03dcf3f 100644 --- a/drivers/gpu/drm/nouveau/core/engine/copy/nva3.c +++ b/drivers/gpu/drm/nouveau/core/engine/copy/nva3.c @@ -116,13 +116,6 @@ nva3_copy_intr(struct nouveau_subdev *subdev) nouveau_engctx_put(engctx); } -static int -nva3_copy_tlb_flush(struct nouveau_engine *engine) -{ - nv50_vm_flush_engine(&engine->base, 0x0d); - return 0; -} - static int nva3_copy_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, void *data, u32 size, @@ -142,7 +135,6 @@ nva3_copy_ctor(struct nouveau_object *parent, struct nouveau_object *engine, nv_subdev(priv)->intr = nva3_copy_intr; nv_engine(priv)->cclass = &nva3_copy_cclass; nv_engine(priv)->sclass = nva3_copy_sclass; - nv_engine(priv)->tlb_flush = nva3_copy_tlb_flush; nv_falcon(priv)->code.data = nva3_pcopy_code; nv_falcon(priv)->code.size = sizeof(nva3_pcopy_code); nv_falcon(priv)->data.data = nva3_pcopy_data; diff --git a/drivers/gpu/drm/nouveau/core/engine/crypt/nv84.c b/drivers/gpu/drm/nouveau/core/engine/crypt/nv84.c index 5bc021f471f9..2551dafbec73 100644 --- a/drivers/gpu/drm/nouveau/core/engine/crypt/nv84.c +++ b/drivers/gpu/drm/nouveau/core/engine/crypt/nv84.c @@ -140,13 +140,6 @@ nv84_crypt_intr(struct nouveau_subdev *subdev) nouveau_engctx_put(engctx); } -static int -nv84_crypt_tlb_flush(struct nouveau_engine *engine) -{ - nv50_vm_flush_engine(&engine->base, 0x0a); - return 0; -} - static int nv84_crypt_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, void *data, u32 size, @@ -165,7 +158,6 @@ nv84_crypt_ctor(struct nouveau_object *parent, struct nouveau_object *engine, nv_subdev(priv)->intr = nv84_crypt_intr; nv_engine(priv)->cclass = &nv84_crypt_cclass; nv_engine(priv)->sclass = nv84_crypt_sclass; - nv_engine(priv)->tlb_flush = nv84_crypt_tlb_flush; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/engine/crypt/nv98.c b/drivers/gpu/drm/nouveau/core/engine/crypt/nv98.c index 8bf8955051d4..83ec3a30f93f 100644 --- a/drivers/gpu/drm/nouveau/core/engine/crypt/nv98.c +++ b/drivers/gpu/drm/nouveau/core/engine/crypt/nv98.c @@ -118,13 +118,6 @@ nv98_crypt_intr(struct nouveau_subdev *subdev) nouveau_engctx_put(engctx); } -static int -nv98_crypt_tlb_flush(struct nouveau_engine *engine) -{ - nv50_vm_flush_engine(&engine->base, 0x0a); - return 0; -} - static int nv98_crypt_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, void *data, u32 size, @@ -143,7 +136,6 @@ nv98_crypt_ctor(struct nouveau_object *parent, struct nouveau_object *engine, nv_subdev(priv)->intr = nv98_crypt_intr; nv_engine(priv)->cclass = &nv98_crypt_cclass; nv_engine(priv)->sclass = nv98_crypt_sclass; - nv_engine(priv)->tlb_flush = nv98_crypt_tlb_flush; nv_falcon(priv)->code.data = nv98_pcrypt_code; nv_falcon(priv)->code.size = sizeof(nv98_pcrypt_code); nv_falcon(priv)->data.data = nv98_pcrypt_data; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nv50.c b/drivers/gpu/drm/nouveau/core/engine/graph/nv50.c index 1ac36110ca19..03de5175dd9f 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nv50.c @@ -186,13 +186,6 @@ nv50_graph_cclass = { * PGRAPH engine/subdev functions ******************************************************************************/ -static int -nv50_graph_tlb_flush(struct nouveau_engine *engine) -{ - nv50_vm_flush_engine(&engine->base, 0x00); - return 0; -} - static const struct nouveau_bitfield nv50_pgraph_status[] = { { 0x00000001, "BUSY" }, /* set when any bit is set */ { 0x00000002, "DISPATCH" }, @@ -302,8 +295,10 @@ nv84_graph_tlb_flush(struct nouveau_engine *engine) nv_rd32(priv, 0x400388)); } - nv50_vm_flush_engine(&engine->base, 0x00); + nv_wr32(priv, 0x100c80, 0x00000001); + if (!nv_wait(priv, 0x100c80, 0x00000001, 0x00000000)) + nv_error(priv, "vm flush timeout\n"); nv_mask(priv, 0x400500, 0x00000001, 0x00000001); spin_unlock_irqrestore(&priv->lock, flags); return timeout ? -EBUSY : 0; @@ -857,10 +852,9 @@ nv50_graph_ctor(struct nouveau_object *parent, struct nouveau_object *engine, }; - if (nv_device(priv)->chipset == 0x50 || - nv_device(priv)->chipset == 0xac) - nv_engine(priv)->tlb_flush = nv50_graph_tlb_flush; - else + /* unfortunate hw bug workaround... */ + if (nv_device(priv)->chipset != 0x50 && + nv_device(priv)->chipset != 0xac) nv_engine(priv)->tlb_flush = nv84_graph_tlb_flush; spin_lock_init(&priv->lock); diff --git a/drivers/gpu/drm/nouveau/core/engine/mpeg/nv50.c b/drivers/gpu/drm/nouveau/core/engine/mpeg/nv50.c index bc7d12b30fc1..37a2bd9e8078 100644 --- a/drivers/gpu/drm/nouveau/core/engine/mpeg/nv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/mpeg/nv50.c @@ -125,13 +125,6 @@ nv50_mpeg_cclass = { * PMPEG engine/subdev functions ******************************************************************************/ -int -nv50_mpeg_tlb_flush(struct nouveau_engine *engine) -{ - nv50_vm_flush_engine(&engine->base, 0x08); - return 0; -} - void nv50_mpeg_intr(struct nouveau_subdev *subdev) { @@ -191,7 +184,6 @@ nv50_mpeg_ctor(struct nouveau_object *parent, struct nouveau_object *engine, nv_subdev(priv)->intr = nv50_vpe_intr; nv_engine(priv)->cclass = &nv50_mpeg_cclass; nv_engine(priv)->sclass = nv50_mpeg_sclass; - nv_engine(priv)->tlb_flush = nv50_mpeg_tlb_flush; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/engine/mpeg/nv84.c b/drivers/gpu/drm/nouveau/core/engine/mpeg/nv84.c index 8f805b44d59e..96f5aa92677b 100644 --- a/drivers/gpu/drm/nouveau/core/engine/mpeg/nv84.c +++ b/drivers/gpu/drm/nouveau/core/engine/mpeg/nv84.c @@ -88,7 +88,6 @@ nv84_mpeg_ctor(struct nouveau_object *parent, struct nouveau_object *engine, nv_subdev(priv)->intr = nv50_mpeg_intr; nv_engine(priv)->cclass = &nv84_mpeg_cclass; nv_engine(priv)->sclass = nv84_mpeg_sclass; - nv_engine(priv)->tlb_flush = nv50_mpeg_tlb_flush; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/include/engine/mpeg.h b/drivers/gpu/drm/nouveau/core/include/engine/mpeg.h index bbf0d4a5bbd7..1d1a89a06ee4 100644 --- a/drivers/gpu/drm/nouveau/core/include/engine/mpeg.h +++ b/drivers/gpu/drm/nouveau/core/include/engine/mpeg.h @@ -54,7 +54,6 @@ extern struct nouveau_ofuncs nv50_mpeg_ofuncs; int nv50_mpeg_context_ctor(struct nouveau_object *, struct nouveau_object *, struct nouveau_oclass *, void *, u32, struct nouveau_object **); -int nv50_mpeg_tlb_flush(struct nouveau_engine *); void nv50_mpeg_intr(struct nouveau_subdev *); int nv50_mpeg_init(struct nouveau_object *); diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/vm.h b/drivers/gpu/drm/nouveau/core/include/subdev/vm.h index c888f93e0ee6..7a0815c87bd2 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/vm.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/vm.h @@ -117,7 +117,6 @@ int nv04_vm_create(struct nouveau_vmmgr *, u64, u64, u64, struct nouveau_vm **); void nv04_vmmgr_dtor(struct nouveau_object *); -void nv50_vm_flush_engine(struct nouveau_subdev *, int engine); void nvc0_vm_flush_engine(struct nouveau_subdev *, u64 addr, int type); /* nouveau_vm.c */ diff --git a/drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c index 6ed85efd89b2..966e61434c7a 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c @@ -151,31 +151,37 @@ nv50_vm_unmap(struct nouveau_gpuobj *pgt, u32 pte, u32 cnt) static void nv50_vm_flush(struct nouveau_vm *vm) { + struct nv50_vmmgr_priv *priv = (void *)vm->vmm; struct nouveau_engine *engine; - int i; + unsigned long flags; + int i, vme; + spin_lock_irqsave(&priv->lock, flags); for (i = 0; i < NVDEV_SUBDEV_NR; i++) { - if (atomic_read(&vm->engref[i]) && i == NVDEV_SUBDEV_BAR) { - nv50_vm_flush_engine(nv_subdev(vm->vmm), 6); - } else - if (atomic_read(&vm->engref[i])) { - engine = nouveau_engine(vm->vmm, i); - if (engine && engine->tlb_flush) - engine->tlb_flush(engine); + if (!atomic_read(&vm->engref[i])) + continue; + + /* unfortunate hw bug workaround... */ + engine = nouveau_engine(priv, i); + if (engine && engine->tlb_flush) { + engine->tlb_flush(engine); + continue; } - } -} -void -nv50_vm_flush_engine(struct nouveau_subdev *subdev, int engine) -{ - struct nv50_vmmgr_priv *priv = (void *)nouveau_vmmgr(subdev); - unsigned long flags; + switch (i) { + case NVDEV_ENGINE_GR : vme = 0x00; break; + case NVDEV_SUBDEV_BAR : vme = 0x06; break; + case NVDEV_ENGINE_MPEG : vme = 0x08; break; + case NVDEV_ENGINE_CRYPT: vme = 0x0a; break; + case NVDEV_ENGINE_COPY0: vme = 0x0d; break; + default: + continue; + } - spin_lock_irqsave(&priv->lock, flags); - nv_wr32(subdev, 0x100c80, (engine << 16) | 1); - if (!nv_wait(subdev, 0x100c80, 0x00000001, 0x00000000)) - nv_error(subdev, "vm flush timeout: engine %d\n", engine); + nv_wr32(priv, 0x100c80, (vme << 16) | 1); + if (!nv_wait(priv, 0x100c80, 0x00000001, 0x00000000)) + nv_error(priv, "vm flush timeout: engine %d\n", vme); + } spin_unlock_irqrestore(&priv->lock, flags); } -- cgit v1.2.3 From ca97a36698ca3f76d3cee542e69dcf1b66210b0c Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 13 May 2013 21:13:15 +1000 Subject: drm/nv50-/vm: take mutex rather than irqsave spinlock These operations can take quite some time, and we really don't want to have to hold a spinlock for too long. Now that the lock ordering for vm and the gr/nv84 hw bug workaround has been reversed, it's possible to use a mutex here. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c | 7 ++----- drivers/gpu/drm/nouveau/core/subdev/vm/nvc0.c | 7 ++----- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c index 966e61434c7a..50c66122cc89 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c @@ -31,7 +31,6 @@ struct nv50_vmmgr_priv { struct nouveau_vmmgr base; - spinlock_t lock; }; static void @@ -153,10 +152,9 @@ nv50_vm_flush(struct nouveau_vm *vm) { struct nv50_vmmgr_priv *priv = (void *)vm->vmm; struct nouveau_engine *engine; - unsigned long flags; int i, vme; - spin_lock_irqsave(&priv->lock, flags); + mutex_lock(&nv_subdev(priv)->mutex); for (i = 0; i < NVDEV_SUBDEV_NR; i++) { if (!atomic_read(&vm->engref[i])) continue; @@ -182,7 +180,7 @@ nv50_vm_flush(struct nouveau_vm *vm) if (!nv_wait(priv, 0x100c80, 0x00000001, 0x00000000)) nv_error(priv, "vm flush timeout: engine %d\n", vme); } - spin_unlock_irqrestore(&priv->lock, flags); + mutex_unlock(&nv_subdev(priv)->mutex); } static int @@ -220,7 +218,6 @@ nv50_vmmgr_ctor(struct nouveau_object *parent, struct nouveau_object *engine, priv->base.map_sg = nv50_vm_map_sg; priv->base.unmap = nv50_vm_unmap; priv->base.flush = nv50_vm_flush; - spin_lock_init(&priv->lock); return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/vm/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/vm/nvc0.c index 4c3b0a23b9d6..beb09743aaff 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/vm/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/vm/nvc0.c @@ -32,7 +32,6 @@ struct nvc0_vmmgr_priv { struct nouveau_vmmgr base; - spinlock_t lock; }; @@ -164,12 +163,11 @@ void nvc0_vm_flush_engine(struct nouveau_subdev *subdev, u64 addr, int type) { struct nvc0_vmmgr_priv *priv = (void *)nouveau_vmmgr(subdev); - unsigned long flags; /* looks like maybe a "free flush slots" counter, the * faster you write to 0x100cbc to more it decreases */ - spin_lock_irqsave(&priv->lock, flags); + mutex_lock(&nv_subdev(priv)->mutex); if (!nv_wait_ne(subdev, 0x100c80, 0x00ff0000, 0x00000000)) { nv_error(subdev, "vm timeout 0: 0x%08x %d\n", nv_rd32(subdev, 0x100c80), type); @@ -183,7 +181,7 @@ nvc0_vm_flush_engine(struct nouveau_subdev *subdev, u64 addr, int type) nv_error(subdev, "vm timeout 1: 0x%08x %d\n", nv_rd32(subdev, 0x100c80), type); } - spin_unlock_irqrestore(&priv->lock, flags); + mutex_unlock(&nv_subdev(priv)->mutex); } static void @@ -227,7 +225,6 @@ nvc0_vmmgr_ctor(struct nouveau_object *parent, struct nouveau_object *engine, priv->base.map_sg = nvc0_vm_map_sg; priv->base.unmap = nvc0_vm_unmap; priv->base.flush = nvc0_vm_flush; - spin_lock_init(&priv->lock); return 0; } -- cgit v1.2.3 From 15cace591788552717269f0d1a5f292b08af39ed Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 13 May 2013 22:07:16 +1000 Subject: drm/nvc0/vm: handle bar tlb flushes internally Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/core/include/subdev/vm.h | 2 - drivers/gpu/drm/nouveau/core/subdev/bar/nvc0.c | 10 ++--- drivers/gpu/drm/nouveau/core/subdev/vm/nvc0.c | 50 ++++++++++++------------ 3 files changed, 28 insertions(+), 34 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/vm.h b/drivers/gpu/drm/nouveau/core/include/subdev/vm.h index 7a0815c87bd2..f2e87b105666 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/vm.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/vm.h @@ -117,8 +117,6 @@ int nv04_vm_create(struct nouveau_vmmgr *, u64, u64, u64, struct nouveau_vm **); void nv04_vmmgr_dtor(struct nouveau_object *); -void nvc0_vm_flush_engine(struct nouveau_subdev *, u64 addr, int type); - /* nouveau_vm.c */ int nouveau_vm_create(struct nouveau_vmmgr *, u64 offset, u64 length, u64 mm_offset, u32 block, struct nouveau_vm **); diff --git a/drivers/gpu/drm/nouveau/core/subdev/bar/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/bar/nvc0.c index f8a44956dec1..b2ec7411eb2e 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bar/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bar/nvc0.c @@ -51,7 +51,6 @@ nvc0_bar_kmap(struct nouveau_bar *bar, struct nouveau_mem *mem, return ret; nouveau_vm_map(vma, mem); - nvc0_vm_flush_engine(nv_subdev(bar), priv->bar[0].pgd->addr, 5); return 0; } @@ -68,18 +67,13 @@ nvc0_bar_umap(struct nouveau_bar *bar, struct nouveau_mem *mem, return ret; nouveau_vm_map(vma, mem); - nvc0_vm_flush_engine(nv_subdev(bar), priv->bar[1].pgd->addr, 5); return 0; } static void nvc0_bar_unmap(struct nouveau_bar *bar, struct nouveau_vma *vma) { - struct nvc0_bar_priv *priv = (void *)bar; - int i = !(vma->vm == priv->bar[0].vm); - nouveau_vm_unmap(vma); - nvc0_vm_flush_engine(nv_subdev(bar), priv->bar[i].pgd->addr, 5); nouveau_vm_put(vma); } @@ -116,6 +110,8 @@ nvc0_bar_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; + atomic_inc(&vm->engref[NVDEV_SUBDEV_BAR]); + ret = nouveau_gpuobj_new(nv_object(priv), NULL, (pci_resource_len(pdev, 3) >> 12) * 8, 0x1000, NVOBJ_FLAG_ZERO_ALLOC, @@ -150,6 +146,8 @@ nvc0_bar_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; + atomic_inc(&vm->engref[NVDEV_SUBDEV_BAR]); + ret = nouveau_vm_ref(vm, &priv->bar[1].vm, priv->bar[1].pgd); nouveau_vm_ref(NULL, &vm, NULL); if (ret) diff --git a/drivers/gpu/drm/nouveau/core/subdev/vm/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/vm/nvc0.c index beb09743aaff..6c3aea55d503 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/vm/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/vm/nvc0.c @@ -159,39 +159,37 @@ nvc0_vm_unmap(struct nouveau_gpuobj *pgt, u32 pte, u32 cnt) } } -void -nvc0_vm_flush_engine(struct nouveau_subdev *subdev, u64 addr, int type) -{ - struct nvc0_vmmgr_priv *priv = (void *)nouveau_vmmgr(subdev); - - /* looks like maybe a "free flush slots" counter, the - * faster you write to 0x100cbc to more it decreases - */ - mutex_lock(&nv_subdev(priv)->mutex); - if (!nv_wait_ne(subdev, 0x100c80, 0x00ff0000, 0x00000000)) { - nv_error(subdev, "vm timeout 0: 0x%08x %d\n", - nv_rd32(subdev, 0x100c80), type); - } - - nv_wr32(subdev, 0x100cb8, addr >> 8); - nv_wr32(subdev, 0x100cbc, 0x80000000 | type); - - /* wait for flush to be queued? */ - if (!nv_wait(subdev, 0x100c80, 0x00008000, 0x00008000)) { - nv_error(subdev, "vm timeout 1: 0x%08x %d\n", - nv_rd32(subdev, 0x100c80), type); - } - mutex_unlock(&nv_subdev(priv)->mutex); -} - static void nvc0_vm_flush(struct nouveau_vm *vm) { + struct nvc0_vmmgr_priv *priv = (void *)vm->vmm; struct nouveau_vm_pgd *vpgd; + u32 type; + type = 0x00000001; /* PAGE_ALL */ + if (atomic_read(&vm->engref[NVDEV_SUBDEV_BAR])) + type |= 0x00000004; /* HUB_ONLY */ + + mutex_lock(&nv_subdev(priv)->mutex); list_for_each_entry(vpgd, &vm->pgd_list, head) { - nvc0_vm_flush_engine(nv_subdev(vm->vmm), vpgd->obj->addr, 1); + /* looks like maybe a "free flush slots" counter, the + * faster you write to 0x100cbc to more it decreases + */ + if (!nv_wait_ne(priv, 0x100c80, 0x00ff0000, 0x00000000)) { + nv_error(priv, "vm timeout 0: 0x%08x %d\n", + nv_rd32(priv, 0x100c80), type); + } + + nv_wr32(priv, 0x100cb8, vpgd->obj->addr >> 8); + nv_wr32(priv, 0x100cbc, 0x80000000 | type); + + /* wait for flush to be queued? */ + if (!nv_wait(priv, 0x100c80, 0x00008000, 0x00008000)) { + nv_error(priv, "vm timeout 1: 0x%08x %d\n", + nv_rd32(priv, 0x100c80), type); + } } + mutex_unlock(&nv_subdev(priv)->mutex); } static int -- cgit v1.2.3 From 4e67bee8e129c072e5498bd192b9cb8aa7e62a89 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 13 May 2013 22:26:26 +1000 Subject: drm/nouveau/vm: take subdev mutex, not the mm, protects against race with vm/nvc0 nvc0_vm_flush() accesses the pgd list, which will soon be able to race with vm_unlink() during channel destruction. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/core/subdev/vm/base.c | 33 ++++++++++++++------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/subdev/vm/base.c b/drivers/gpu/drm/nouveau/core/subdev/vm/base.c index 77c67fc970e6..6fc389163532 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/vm/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/vm/base.c @@ -236,9 +236,9 @@ nouveau_vm_unmap_pgt(struct nouveau_vm *vm, int big, u32 fpde, u32 lpde) vmm->map_pgt(vpgd->obj, pde, vpgt->obj); } - mutex_unlock(&vm->mm.mutex); + mutex_unlock(&nv_subdev(vmm)->mutex); nouveau_gpuobj_ref(NULL, &pgt); - mutex_lock(&vm->mm.mutex); + mutex_lock(&nv_subdev(vmm)->mutex); } } @@ -256,18 +256,18 @@ nouveau_vm_map_pgt(struct nouveau_vm *vm, u32 pde, u32 type) pgt_size = (1 << (vmm->pgt_bits + 12)) >> type; pgt_size *= 8; - mutex_unlock(&vm->mm.mutex); + mutex_unlock(&nv_subdev(vmm)->mutex); ret = nouveau_gpuobj_new(nv_object(vm->vmm), NULL, pgt_size, 0x1000, NVOBJ_FLAG_ZERO_ALLOC, &pgt); - mutex_lock(&vm->mm.mutex); + mutex_lock(&nv_subdev(vmm)->mutex); if (unlikely(ret)) return ret; /* someone beat us to filling the PDE while we didn't have the lock */ if (unlikely(vpgt->refcount[big]++)) { - mutex_unlock(&vm->mm.mutex); + mutex_unlock(&nv_subdev(vmm)->mutex); nouveau_gpuobj_ref(NULL, &pgt); - mutex_lock(&vm->mm.mutex); + mutex_lock(&nv_subdev(vmm)->mutex); return 0; } @@ -289,11 +289,11 @@ nouveau_vm_get(struct nouveau_vm *vm, u64 size, u32 page_shift, u32 fpde, lpde, pde; int ret; - mutex_lock(&vm->mm.mutex); + mutex_lock(&nv_subdev(vmm)->mutex); ret = nouveau_mm_head(&vm->mm, page_shift, msize, msize, align, &vma->node); if (unlikely(ret != 0)) { - mutex_unlock(&vm->mm.mutex); + mutex_unlock(&nv_subdev(vmm)->mutex); return ret; } @@ -314,11 +314,11 @@ nouveau_vm_get(struct nouveau_vm *vm, u64 size, u32 page_shift, if (pde != fpde) nouveau_vm_unmap_pgt(vm, big, fpde, pde - 1); nouveau_mm_free(&vm->mm, &vma->node); - mutex_unlock(&vm->mm.mutex); + mutex_unlock(&nv_subdev(vmm)->mutex); return ret; } } - mutex_unlock(&vm->mm.mutex); + mutex_unlock(&nv_subdev(vmm)->mutex); vma->vm = vm; vma->offset = (u64)vma->node->offset << 12; @@ -338,10 +338,10 @@ nouveau_vm_put(struct nouveau_vma *vma) fpde = (vma->node->offset >> vmm->pgt_bits); lpde = (vma->node->offset + vma->node->length - 1) >> vmm->pgt_bits; - mutex_lock(&vm->mm.mutex); + mutex_lock(&nv_subdev(vmm)->mutex); nouveau_vm_unmap_pgt(vm, vma->node->type != vmm->spg_shift, fpde, lpde); nouveau_mm_free(&vm->mm, &vma->node); - mutex_unlock(&vm->mm.mutex); + mutex_unlock(&nv_subdev(vmm)->mutex); } int @@ -405,24 +405,25 @@ nouveau_vm_link(struct nouveau_vm *vm, struct nouveau_gpuobj *pgd) nouveau_gpuobj_ref(pgd, &vpgd->obj); - mutex_lock(&vm->mm.mutex); + mutex_lock(&nv_subdev(vmm)->mutex); for (i = vm->fpde; i <= vm->lpde; i++) vmm->map_pgt(pgd, i, vm->pgt[i - vm->fpde].obj); list_add(&vpgd->head, &vm->pgd_list); - mutex_unlock(&vm->mm.mutex); + mutex_unlock(&nv_subdev(vmm)->mutex); return 0; } static void nouveau_vm_unlink(struct nouveau_vm *vm, struct nouveau_gpuobj *mpgd) { + struct nouveau_vmmgr *vmm = vm->vmm; struct nouveau_vm_pgd *vpgd, *tmp; struct nouveau_gpuobj *pgd = NULL; if (!mpgd) return; - mutex_lock(&vm->mm.mutex); + mutex_lock(&nv_subdev(vmm)->mutex); list_for_each_entry_safe(vpgd, tmp, &vm->pgd_list, head) { if (vpgd->obj == mpgd) { pgd = vpgd->obj; @@ -431,7 +432,7 @@ nouveau_vm_unlink(struct nouveau_vm *vm, struct nouveau_gpuobj *mpgd) break; } } - mutex_unlock(&vm->mm.mutex); + mutex_unlock(&nv_subdev(vmm)->mutex); nouveau_gpuobj_ref(NULL, &pgd); } -- cgit v1.2.3 From 51a506c012edb4e8a34e3596df806e7d32067d8b Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 13 May 2013 22:30:56 +1000 Subject: drm/nouveau/core: remove nouveau_mm.mutex, no more users Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/core/core/mm.c | 1 - drivers/gpu/drm/nouveau/core/include/core/mm.h | 2 -- drivers/gpu/drm/nouveau/nouveau_ttm.c | 4 ++-- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/core/mm.c b/drivers/gpu/drm/nouveau/core/core/mm.c index 0261a11b2ae0..d8291724dbd4 100644 --- a/drivers/gpu/drm/nouveau/core/core/mm.c +++ b/drivers/gpu/drm/nouveau/core/core/mm.c @@ -208,7 +208,6 @@ nouveau_mm_init(struct nouveau_mm *mm, u32 offset, u32 length, u32 block) struct nouveau_mm_node *node; if (block) { - mutex_init(&mm->mutex); INIT_LIST_HEAD(&mm->nodes); INIT_LIST_HEAD(&mm->free); mm->block_size = block; diff --git a/drivers/gpu/drm/nouveau/core/include/core/mm.h b/drivers/gpu/drm/nouveau/core/include/core/mm.h index 2514e81ade02..2bf7d0e32261 100644 --- a/drivers/gpu/drm/nouveau/core/include/core/mm.h +++ b/drivers/gpu/drm/nouveau/core/include/core/mm.h @@ -15,8 +15,6 @@ struct nouveau_mm { struct list_head nodes; struct list_head free; - struct mutex mutex; - u32 block_size; int heap_nodes; }; diff --git a/drivers/gpu/drm/nouveau/nouveau_ttm.c b/drivers/gpu/drm/nouveau/nouveau_ttm.c index 2311b7aca2d4..01e3154f8969 100644 --- a/drivers/gpu/drm/nouveau/nouveau_ttm.c +++ b/drivers/gpu/drm/nouveau/nouveau_ttm.c @@ -111,7 +111,7 @@ nouveau_vram_manager_debug(struct ttm_mem_type_manager *man, const char *prefix) struct nouveau_mm_node *r; u32 total = 0, free = 0; - mutex_lock(&mm->mutex); + mutex_lock(&nv_subdev(pfb)->mutex); list_for_each_entry(r, &mm->nodes, nl_entry) { printk(KERN_DEBUG "%s %d: 0x%010llx 0x%010llx\n", prefix, r->type, ((u64)r->offset << 12), @@ -121,7 +121,7 @@ nouveau_vram_manager_debug(struct ttm_mem_type_manager *man, const char *prefix) if (!r->type) free += r->length; } - mutex_unlock(&mm->mutex); + mutex_unlock(&nv_subdev(pfb)->mutex); printk(KERN_DEBUG "%s total: 0x%010llx free: 0x%010llx\n", prefix, (u64)total << 12, (u64)free << 12); -- cgit v1.2.3 From 780194b1b9f5fdbaa06dd71e60b31ceaaedafbe4 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 7 May 2013 08:36:33 +1000 Subject: drm/nouveau/vm: make each vma take a reference on its parent vm Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/core/subdev/vm/base.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/core/subdev/vm/base.c b/drivers/gpu/drm/nouveau/core/subdev/vm/base.c index 6fc389163532..34d3fbfbd631 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/vm/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/vm/base.c @@ -320,7 +320,8 @@ nouveau_vm_get(struct nouveau_vm *vm, u64 size, u32 page_shift, } mutex_unlock(&nv_subdev(vmm)->mutex); - vma->vm = vm; + vma->vm = NULL; + nouveau_vm_ref(vm, &vma->vm, NULL); vma->offset = (u64)vma->node->offset << 12; vma->access = access; return 0; @@ -342,6 +343,8 @@ nouveau_vm_put(struct nouveau_vma *vma) nouveau_vm_unmap_pgt(vm, vma->node->type != vmm->spg_shift, fpde, lpde); nouveau_mm_free(&vm->mm, &vma->node); mutex_unlock(&nv_subdev(vmm)->mutex); + + nouveau_vm_ref(NULL, &vma->vm, NULL); } int -- cgit v1.2.3 From c4c7044ffc1ba973e2ec0f0dc94980b49101d877 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 7 May 2013 09:48:30 +1000 Subject: drm/nouveau: delay busy bo vma removal until fence signals As opposed to an explicit wait. Allows userspace to not stall waiting on buffer deletion. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_bo.c | 7 +--- drivers/gpu/drm/nouveau/nouveau_fence.c | 73 ++++++++++++++++++++++++++++++--- drivers/gpu/drm/nouveau/nouveau_fence.h | 2 + drivers/gpu/drm/nouveau/nouveau_gem.c | 41 ++++++++++++++++-- 4 files changed, 108 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index 86eef68517cb..a1cf8255db50 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -1550,13 +1550,8 @@ void nouveau_bo_vma_del(struct nouveau_bo *nvbo, struct nouveau_vma *vma) { if (vma->node) { - if (nvbo->bo.mem.mem_type != TTM_PL_SYSTEM) { - spin_lock(&nvbo->bo.bdev->fence_lock); - ttm_bo_wait(&nvbo->bo, false, false, false); - spin_unlock(&nvbo->bo.bdev->fence_lock); + if (nvbo->bo.mem.mem_type != TTM_PL_SYSTEM) nouveau_vm_unmap(vma); - } - nouveau_vm_put(vma); list_del(&vma->head); } diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c index 6c946837a0aa..1680d9187bab 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.c +++ b/drivers/gpu/drm/nouveau/nouveau_fence.c @@ -35,15 +35,34 @@ #include +struct fence_work { + struct work_struct base; + struct list_head head; + void (*func)(void *); + void *data; +}; + +static void +nouveau_fence_signal(struct nouveau_fence *fence) +{ + struct fence_work *work, *temp; + + list_for_each_entry_safe(work, temp, &fence->work, head) { + schedule_work(&work->base); + list_del(&work->head); + } + + fence->channel = NULL; + list_del(&fence->head); +} + void nouveau_fence_context_del(struct nouveau_fence_chan *fctx) { struct nouveau_fence *fence, *fnext; spin_lock(&fctx->lock); list_for_each_entry_safe(fence, fnext, &fctx->pending, head) { - fence->channel = NULL; - list_del(&fence->head); - nouveau_fence_unref(&fence); + nouveau_fence_signal(fence); } spin_unlock(&fctx->lock); } @@ -56,6 +75,50 @@ nouveau_fence_context_new(struct nouveau_fence_chan *fctx) spin_lock_init(&fctx->lock); } +static void +nouveau_fence_work_handler(struct work_struct *kwork) +{ + struct fence_work *work = container_of(kwork, typeof(*work), base); + work->func(work->data); + kfree(work); +} + +void +nouveau_fence_work(struct nouveau_fence *fence, + void (*func)(void *), void *data) +{ + struct nouveau_channel *chan = fence->channel; + struct nouveau_fence_chan *fctx; + struct fence_work *work = NULL; + + if (nouveau_fence_done(fence)) { + func(data); + return; + } + + fctx = chan->fence; + work = kmalloc(sizeof(*work), GFP_KERNEL); + if (!work) { + WARN_ON(nouveau_fence_wait(fence, false, false)); + func(data); + return; + } + + spin_lock(&fctx->lock); + if (!fence->channel) { + spin_unlock(&fctx->lock); + kfree(work); + func(data); + return; + } + + INIT_WORK(&work->base, nouveau_fence_work_handler); + work->func = func; + work->data = data; + list_add(&work->head, &fence->work); + spin_unlock(&fctx->lock); +} + static void nouveau_fence_update(struct nouveau_channel *chan) { @@ -67,8 +130,7 @@ nouveau_fence_update(struct nouveau_channel *chan) if (fctx->read(chan) < fence->sequence) break; - fence->channel = NULL; - list_del(&fence->head); + nouveau_fence_signal(fence); nouveau_fence_unref(&fence); } spin_unlock(&fctx->lock); @@ -265,6 +327,7 @@ nouveau_fence_new(struct nouveau_channel *chan, bool sysmem, if (!fence) return -ENOMEM; + INIT_LIST_HEAD(&fence->work); fence->sysmem = sysmem; kref_init(&fence->kref); diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.h b/drivers/gpu/drm/nouveau/nouveau_fence.h index c89943407b52..c57bb61da58c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.h +++ b/drivers/gpu/drm/nouveau/nouveau_fence.h @@ -5,6 +5,7 @@ struct nouveau_drm; struct nouveau_fence { struct list_head head; + struct list_head work; struct kref kref; bool sysmem; @@ -22,6 +23,7 @@ void nouveau_fence_unref(struct nouveau_fence **); int nouveau_fence_emit(struct nouveau_fence *, struct nouveau_channel *); bool nouveau_fence_done(struct nouveau_fence *); +void nouveau_fence_work(struct nouveau_fence *, void (*)(void *), void *); int nouveau_fence_wait(struct nouveau_fence *, bool lazy, bool intr); int nouveau_fence_sync(struct nouveau_fence *, struct nouveau_channel *); diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c index b4b4d0c1f4af..c0e324b557c1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.c +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -101,6 +101,41 @@ out: return ret; } +static void +nouveau_gem_object_delete(void *data) +{ + struct nouveau_vma *vma = data; + nouveau_vm_unmap(vma); + nouveau_vm_put(vma); + kfree(vma); +} + +static void +nouveau_gem_object_unmap(struct nouveau_bo *nvbo, struct nouveau_vma *vma) +{ + const bool mapped = nvbo->bo.mem.mem_type != TTM_PL_SYSTEM; + struct nouveau_fence *fence = NULL; + + list_del(&vma->head); + + if (mapped) { + spin_lock(&nvbo->bo.bdev->fence_lock); + if (nvbo->bo.sync_obj) + fence = nouveau_fence_ref(nvbo->bo.sync_obj); + spin_unlock(&nvbo->bo.bdev->fence_lock); + } + + if (fence) { + nouveau_fence_work(fence, nouveau_gem_object_delete, vma); + } else { + if (mapped) + nouveau_vm_unmap(vma); + nouveau_vm_put(vma); + kfree(vma); + } + nouveau_fence_unref(&fence); +} + void nouveau_gem_object_close(struct drm_gem_object *gem, struct drm_file *file_priv) { @@ -118,10 +153,8 @@ nouveau_gem_object_close(struct drm_gem_object *gem, struct drm_file *file_priv) vma = nouveau_bo_vma_find(nvbo, cli->base.vm); if (vma) { - if (--vma->refcount == 0) { - nouveau_bo_vma_del(nvbo, vma); - kfree(vma); - } + if (--vma->refcount == 0) + nouveau_gem_object_unmap(nvbo, vma); } ttm_bo_unreserve(&nvbo->bo); } -- cgit v1.2.3 From 99bd5537bd22256866d83033e0aab2586616bcc2 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 6 May 2013 11:35:37 +1000 Subject: drm/nve6/gr: update initial register/context values Signed-off-by: Ben Skeggs --- .../gpu/drm/nouveau/core/engine/graph/ctxnve0.c | 217 +++++++++---------- .../drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc | 26 ++- .../nouveau/core/engine/graph/fuc/gpcnve0.fuc.h | 23 ++- drivers/gpu/drm/nouveau/core/engine/graph/nve0.c | 230 ++++++++++++++++++++- 4 files changed, 383 insertions(+), 113 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve0.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve0.c index ae27dae3fe38..d4e54745876b 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve0.c @@ -749,31 +749,37 @@ nve0_grctx_generate_icmd(struct nvc0_graph_priv *priv) nv_icmd(priv, 0x000841, 0x08000080); nv_icmd(priv, 0x000842, 0x00400008); nv_icmd(priv, 0x000843, 0x08000080); - nv_icmd(priv, 0x000818, 0x00000000); - nv_icmd(priv, 0x000819, 0x00000000); - nv_icmd(priv, 0x00081a, 0x00000000); - nv_icmd(priv, 0x00081b, 0x00000000); - nv_icmd(priv, 0x00081c, 0x00000000); - nv_icmd(priv, 0x00081d, 0x00000000); - nv_icmd(priv, 0x00081e, 0x00000000); - nv_icmd(priv, 0x00081f, 0x00000000); - nv_icmd(priv, 0x000848, 0x00000000); - nv_icmd(priv, 0x000849, 0x00000000); - nv_icmd(priv, 0x00084a, 0x00000000); - nv_icmd(priv, 0x00084b, 0x00000000); - nv_icmd(priv, 0x00084c, 0x00000000); - nv_icmd(priv, 0x00084d, 0x00000000); - nv_icmd(priv, 0x00084e, 0x00000000); - nv_icmd(priv, 0x00084f, 0x00000000); - nv_icmd(priv, 0x000850, 0x00000000); - nv_icmd(priv, 0x000851, 0x00000000); - nv_icmd(priv, 0x000852, 0x00000000); - nv_icmd(priv, 0x000853, 0x00000000); - nv_icmd(priv, 0x000854, 0x00000000); - nv_icmd(priv, 0x000855, 0x00000000); - nv_icmd(priv, 0x000856, 0x00000000); - nv_icmd(priv, 0x000857, 0x00000000); - nv_icmd(priv, 0x000738, 0x00000000); + switch (nv_device(priv)->chipset) { + case 0xe6: + break; + default: + nv_icmd(priv, 0x000818, 0x00000000); + nv_icmd(priv, 0x000819, 0x00000000); + nv_icmd(priv, 0x00081a, 0x00000000); + nv_icmd(priv, 0x00081b, 0x00000000); + nv_icmd(priv, 0x00081c, 0x00000000); + nv_icmd(priv, 0x00081d, 0x00000000); + nv_icmd(priv, 0x00081e, 0x00000000); + nv_icmd(priv, 0x00081f, 0x00000000); + nv_icmd(priv, 0x000848, 0x00000000); + nv_icmd(priv, 0x000849, 0x00000000); + nv_icmd(priv, 0x00084a, 0x00000000); + nv_icmd(priv, 0x00084b, 0x00000000); + nv_icmd(priv, 0x00084c, 0x00000000); + nv_icmd(priv, 0x00084d, 0x00000000); + nv_icmd(priv, 0x00084e, 0x00000000); + nv_icmd(priv, 0x00084f, 0x00000000); + nv_icmd(priv, 0x000850, 0x00000000); + nv_icmd(priv, 0x000851, 0x00000000); + nv_icmd(priv, 0x000852, 0x00000000); + nv_icmd(priv, 0x000853, 0x00000000); + nv_icmd(priv, 0x000854, 0x00000000); + nv_icmd(priv, 0x000855, 0x00000000); + nv_icmd(priv, 0x000856, 0x00000000); + nv_icmd(priv, 0x000857, 0x00000000); + nv_icmd(priv, 0x000738, 0x00000000); + break; + } nv_icmd(priv, 0x0006aa, 0x00000001); nv_icmd(priv, 0x0006ab, 0x00000002); nv_icmd(priv, 0x0006ac, 0x00000080); @@ -862,31 +868,37 @@ nve0_grctx_generate_icmd(struct nvc0_graph_priv *priv) nv_icmd(priv, 0x000813, 0x00000006); nv_icmd(priv, 0x000814, 0x00000008); nv_icmd(priv, 0x000957, 0x00000003); - nv_icmd(priv, 0x000818, 0x00000000); - nv_icmd(priv, 0x000819, 0x00000000); - nv_icmd(priv, 0x00081a, 0x00000000); - nv_icmd(priv, 0x00081b, 0x00000000); - nv_icmd(priv, 0x00081c, 0x00000000); - nv_icmd(priv, 0x00081d, 0x00000000); - nv_icmd(priv, 0x00081e, 0x00000000); - nv_icmd(priv, 0x00081f, 0x00000000); - nv_icmd(priv, 0x000848, 0x00000000); - nv_icmd(priv, 0x000849, 0x00000000); - nv_icmd(priv, 0x00084a, 0x00000000); - nv_icmd(priv, 0x00084b, 0x00000000); - nv_icmd(priv, 0x00084c, 0x00000000); - nv_icmd(priv, 0x00084d, 0x00000000); - nv_icmd(priv, 0x00084e, 0x00000000); - nv_icmd(priv, 0x00084f, 0x00000000); - nv_icmd(priv, 0x000850, 0x00000000); - nv_icmd(priv, 0x000851, 0x00000000); - nv_icmd(priv, 0x000852, 0x00000000); - nv_icmd(priv, 0x000853, 0x00000000); - nv_icmd(priv, 0x000854, 0x00000000); - nv_icmd(priv, 0x000855, 0x00000000); - nv_icmd(priv, 0x000856, 0x00000000); - nv_icmd(priv, 0x000857, 0x00000000); - nv_icmd(priv, 0x000738, 0x00000000); + switch (nv_device(priv)->chipset) { + case 0xe6: + break; + default: + nv_icmd(priv, 0x000818, 0x00000000); + nv_icmd(priv, 0x000819, 0x00000000); + nv_icmd(priv, 0x00081a, 0x00000000); + nv_icmd(priv, 0x00081b, 0x00000000); + nv_icmd(priv, 0x00081c, 0x00000000); + nv_icmd(priv, 0x00081d, 0x00000000); + nv_icmd(priv, 0x00081e, 0x00000000); + nv_icmd(priv, 0x00081f, 0x00000000); + nv_icmd(priv, 0x000848, 0x00000000); + nv_icmd(priv, 0x000849, 0x00000000); + nv_icmd(priv, 0x00084a, 0x00000000); + nv_icmd(priv, 0x00084b, 0x00000000); + nv_icmd(priv, 0x00084c, 0x00000000); + nv_icmd(priv, 0x00084d, 0x00000000); + nv_icmd(priv, 0x00084e, 0x00000000); + nv_icmd(priv, 0x00084f, 0x00000000); + nv_icmd(priv, 0x000850, 0x00000000); + nv_icmd(priv, 0x000851, 0x00000000); + nv_icmd(priv, 0x000852, 0x00000000); + nv_icmd(priv, 0x000853, 0x00000000); + nv_icmd(priv, 0x000854, 0x00000000); + nv_icmd(priv, 0x000855, 0x00000000); + nv_icmd(priv, 0x000856, 0x00000000); + nv_icmd(priv, 0x000857, 0x00000000); + nv_icmd(priv, 0x000738, 0x00000000); + break; + } nv_icmd(priv, 0x000b07, 0x00000002); nv_icmd(priv, 0x000b08, 0x00000100); nv_icmd(priv, 0x000b09, 0x00000100); @@ -2162,7 +2174,14 @@ nve0_grctx_generate_902d(struct nvc0_graph_priv *priv) nv_mthd(priv, 0x902d, 0x0244, 0x00000080); nv_mthd(priv, 0x902d, 0x0248, 0x00000100); nv_mthd(priv, 0x902d, 0x024c, 0x00000100); - nv_mthd(priv, 0x902d, 0x3410, 0x00000000); + switch (nv_device(priv)->chipset) { + case 0xe6: + nv_mthd(priv, 0x902d, 0x3410, 0x80002006); + break; + default: + nv_mthd(priv, 0x902d, 0x3410, 0x00000000); + break; + } } static void @@ -2310,6 +2329,11 @@ nve0_graph_generate_unk58xx(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x405a00, 0x0); nv_wr32(priv, 0x405a04, 0x0); nv_wr32(priv, 0x405a18, 0x0); +} + +static void +nve0_graph_generate_unk5bxx(struct nvc0_graph_priv *priv) +{ nv_wr32(priv, 0x405b00, 0x0); nv_wr32(priv, 0x405b10, 0x1000); } @@ -2394,6 +2418,8 @@ nve0_graph_generate_unk88xx(struct nvc0_graph_priv *priv) static void nve0_graph_generate_gpc(struct nvc0_graph_priv *priv) { + int i; + nv_wr32(priv, 0x418380, 0x16); nv_wr32(priv, 0x418400, 0x38004e00); nv_wr32(priv, 0x418404, 0x71e0ffff); @@ -2434,62 +2460,15 @@ nve0_graph_generate_gpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x418924, 0x0); nv_wr32(priv, 0x418928, 0xffff00); nv_wr32(priv, 0x41892c, 0xff00); - nv_wr32(priv, 0x418a00, 0x0); - nv_wr32(priv, 0x418a04, 0x0); - nv_wr32(priv, 0x418a08, 0x0); - nv_wr32(priv, 0x418a0c, 0x10000); - nv_wr32(priv, 0x418a10, 0x0); - nv_wr32(priv, 0x418a14, 0x0); - nv_wr32(priv, 0x418a18, 0x0); - nv_wr32(priv, 0x418a20, 0x0); - nv_wr32(priv, 0x418a24, 0x0); - nv_wr32(priv, 0x418a28, 0x0); - nv_wr32(priv, 0x418a2c, 0x10000); - nv_wr32(priv, 0x418a30, 0x0); - nv_wr32(priv, 0x418a34, 0x0); - nv_wr32(priv, 0x418a38, 0x0); - nv_wr32(priv, 0x418a40, 0x0); - nv_wr32(priv, 0x418a44, 0x0); - nv_wr32(priv, 0x418a48, 0x0); - nv_wr32(priv, 0x418a4c, 0x10000); - nv_wr32(priv, 0x418a50, 0x0); - nv_wr32(priv, 0x418a54, 0x0); - nv_wr32(priv, 0x418a58, 0x0); - nv_wr32(priv, 0x418a60, 0x0); - nv_wr32(priv, 0x418a64, 0x0); - nv_wr32(priv, 0x418a68, 0x0); - nv_wr32(priv, 0x418a6c, 0x10000); - nv_wr32(priv, 0x418a70, 0x0); - nv_wr32(priv, 0x418a74, 0x0); - nv_wr32(priv, 0x418a78, 0x0); - nv_wr32(priv, 0x418a80, 0x0); - nv_wr32(priv, 0x418a84, 0x0); - nv_wr32(priv, 0x418a88, 0x0); - nv_wr32(priv, 0x418a8c, 0x10000); - nv_wr32(priv, 0x418a90, 0x0); - nv_wr32(priv, 0x418a94, 0x0); - nv_wr32(priv, 0x418a98, 0x0); - nv_wr32(priv, 0x418aa0, 0x0); - nv_wr32(priv, 0x418aa4, 0x0); - nv_wr32(priv, 0x418aa8, 0x0); - nv_wr32(priv, 0x418aac, 0x10000); - nv_wr32(priv, 0x418ab0, 0x0); - nv_wr32(priv, 0x418ab4, 0x0); - nv_wr32(priv, 0x418ab8, 0x0); - nv_wr32(priv, 0x418ac0, 0x0); - nv_wr32(priv, 0x418ac4, 0x0); - nv_wr32(priv, 0x418ac8, 0x0); - nv_wr32(priv, 0x418acc, 0x10000); - nv_wr32(priv, 0x418ad0, 0x0); - nv_wr32(priv, 0x418ad4, 0x0); - nv_wr32(priv, 0x418ad8, 0x0); - nv_wr32(priv, 0x418ae0, 0x0); - nv_wr32(priv, 0x418ae4, 0x0); - nv_wr32(priv, 0x418ae8, 0x0); - nv_wr32(priv, 0x418aec, 0x10000); - nv_wr32(priv, 0x418af0, 0x0); - nv_wr32(priv, 0x418af4, 0x0); - nv_wr32(priv, 0x418af8, 0x0); + for (i = 0; i < 8; i++) { + nv_wr32(priv, 0x418a00 + (i * 0x20), 0x0); + nv_wr32(priv, 0x418a04 + (i * 0x20), 0x0); + nv_wr32(priv, 0x418a08 + (i * 0x20), 0x0); + nv_wr32(priv, 0x418a0c + (i * 0x20), 0x10000); + nv_wr32(priv, 0x418a10 + (i * 0x20), 0x0); + nv_wr32(priv, 0x418a14 + (i * 0x20), 0x0); + nv_wr32(priv, 0x418a18 + (i * 0x20), 0x0); + } nv_wr32(priv, 0x418b00, 0x6); nv_wr32(priv, 0x418b08, 0xa418820); nv_wr32(priv, 0x418b0c, 0x62080e6); @@ -2567,7 +2546,14 @@ nve0_graph_generate_tpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419e90, 0x0); nv_wr32(priv, 0x419e94, 0x0); nv_wr32(priv, 0x419e98, 0x0); - nv_wr32(priv, 0x419eac, 0x1fcf); + switch (nv_device(priv)->chipset) { + case 0xe6: + nv_wr32(priv, 0x419eac, 0x1f8f); + break; + default: + nv_wr32(priv, 0x419eac, 0x1fcf); + break; + } nv_wr32(priv, 0x419eb0, 0xd3f); nv_wr32(priv, 0x419ec8, 0x1304f); nv_wr32(priv, 0x419f30, 0x0); @@ -2579,7 +2565,21 @@ nve0_graph_generate_tpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419f48, 0x0); nv_wr32(priv, 0x419f4c, 0x0); nv_wr32(priv, 0x419f58, 0x0); + switch (nv_device(priv)->chipset) { + case 0xe6: + nv_wr32(priv, 0x419f70, 0x0); + break; + default: + break; + } nv_wr32(priv, 0x419f78, 0xb); + switch (nv_device(priv)->chipset) { + case 0xe6: + nv_wr32(priv, 0x419f7c, 0x27a); + break; + default: + break; + } } static void @@ -2624,6 +2624,7 @@ nve0_grctx_generate(struct nvc0_graph_priv *priv) nve0_graph_generate_unk46xx(priv); nve0_graph_generate_unk47xx(priv); nve0_graph_generate_unk58xx(priv); + nve0_graph_generate_unk5bxx(priv); nve0_graph_generate_unk60xx(priv); nve0_graph_generate_unk64xx(priv); nve0_graph_generate_unk70xx(priv); diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc index 62ab231cd6b6..f58c4d0762d3 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc @@ -60,8 +60,8 @@ chipsets: .b8 0xe6 0 0 0 .b16 #nve4_gpc_mmio_head .b16 #nve4_gpc_mmio_tail -.b16 #nve4_tpc_mmio_head -.b16 #nve4_tpc_mmio_tail +.b16 #nve6_tpc_mmio_head +.b16 #nve6_tpc_mmio_tail .b8 0 0 0 0 // GPC mmio lists @@ -123,6 +123,28 @@ mmctx_data(0x000758, 1) mmctx_data(0x000778, 1) nve4_tpc_mmio_tail: +nve6_tpc_mmio_head: +mmctx_data(0x000048, 1) +mmctx_data(0x000064, 1) +mmctx_data(0x000088, 1) +mmctx_data(0x000200, 6) +mmctx_data(0x00021c, 2) +mmctx_data(0x000230, 1) +mmctx_data(0x0002c4, 1) +mmctx_data(0x000400, 3) +mmctx_data(0x000420, 3) +mmctx_data(0x0004e8, 1) +mmctx_data(0x0004f4, 1) +mmctx_data(0x000604, 4) +mmctx_data(0x000644, 22) +mmctx_data(0x0006ac, 2) +mmctx_data(0x0006c8, 1) +mmctx_data(0x000730, 8) +mmctx_data(0x000758, 1) +mmctx_data(0x000770, 1) +mmctx_data(0x000778, 2) +nve6_tpc_mmio_tail: + .section #nve0_grgpc_code bra #init define(`include_code') diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h index 09ee4702c8b2..321834f15311 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h @@ -41,7 +41,7 @@ uint32_t nve0_grgpc_data[] = { 0x01580110, 0x000000e6, 0x0110008c, - 0x01580110, + 0x01a40158, 0x00000000, /* 0x008c: nve4_gpc_mmio_head */ 0x00000380, @@ -97,6 +97,27 @@ uint32_t nve0_grgpc_data[] = { 0x1c000730, 0x00000758, 0x00000778, +/* 0x0158: nve4_tpc_mmio_tail */ +/* 0x0158: nve6_tpc_mmio_head */ + 0x00000048, + 0x00000064, + 0x00000088, + 0x14000200, + 0x0400021c, + 0x00000230, + 0x000002c4, + 0x08000400, + 0x08000420, + 0x000004e8, + 0x000004f4, + 0x0c000604, + 0x54000644, + 0x040006ac, + 0x000006c8, + 0x1c000730, + 0x00000758, + 0x00000770, + 0x04000778, }; uint32_t nve0_grgpc_code[] = { diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c b/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c index 84249f8c99c6..b7324138df02 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c @@ -512,19 +512,224 @@ nve0_graph_init_regs(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x400124, 0x00000002); } +static void +nve0_graph_init_unk40xx(struct nvc0_graph_priv *priv) +{ + nv_wr32(priv, 0x40415c, 0x00000000); + nv_wr32(priv, 0x404170, 0x00000000); +} + +static void +nve0_graph_init_unk44xx(struct nvc0_graph_priv *priv) +{ + nv_wr32(priv, 0x404488, 0x00000000); + nv_wr32(priv, 0x40448c, 0x00000000); +} + +static void +nve0_graph_init_unk78xx(struct nvc0_graph_priv *priv) +{ + nv_wr32(priv, 0x407808, 0x00000000); +} + +static void +nve0_graph_init_unk60xx(struct nvc0_graph_priv *priv) +{ + nv_wr32(priv, 0x406024, 0x00000000); +} + +static void +nve0_graph_init_unk64xx(struct nvc0_graph_priv *priv) +{ + nv_wr32(priv, 0x4064f0, 0x00000000); + nv_wr32(priv, 0x4064f4, 0x00000000); + nv_wr32(priv, 0x4064f8, 0x00000000); +} + +static void +nve0_graph_init_unk58xx(struct nvc0_graph_priv *priv) +{ + nv_wr32(priv, 0x405844, 0x00ffffff); + nv_wr32(priv, 0x405850, 0x00000000); + nv_wr32(priv, 0x405900, 0x0000ff34); + nv_wr32(priv, 0x405908, 0x00000000); + nv_wr32(priv, 0x405928, 0x00000000); + nv_wr32(priv, 0x40592c, 0x00000000); +} + +static void +nve0_graph_init_unk80xx(struct nvc0_graph_priv *priv) +{ + nv_wr32(priv, 0x40803c, 0x00000000); +} + +static void +nve0_graph_init_unk70xx(struct nvc0_graph_priv *priv) +{ + nv_wr32(priv, 0x407010, 0x00000000); +} + +static void +nve0_graph_init_unk5bxx(struct nvc0_graph_priv *priv) +{ + nv_wr32(priv, 0x405b50, 0x00000000); +} + +static void +nve0_graph_init_gpc(struct nvc0_graph_priv *priv) +{ + nv_wr32(priv, 0x418408, 0x00000000); + nv_wr32(priv, 0x4184a0, 0x00000000); + nv_wr32(priv, 0x4184a4, 0x00000000); + nv_wr32(priv, 0x4184a8, 0x00000000); + nv_wr32(priv, 0x418604, 0x00000000); + nv_wr32(priv, 0x418680, 0x00000000); + nv_wr32(priv, 0x418714, 0x00000000); + nv_wr32(priv, 0x418384, 0x00000000); + nv_wr32(priv, 0x418814, 0x00000000); + nv_wr32(priv, 0x418818, 0x00000000); + nv_wr32(priv, 0x41881c, 0x00000000); + nv_wr32(priv, 0x418b04, 0x00000000); + nv_wr32(priv, 0x4188c8, 0x00000000); + nv_wr32(priv, 0x4188cc, 0x00000000); + nv_wr32(priv, 0x4188d0, 0x00010000); + nv_wr32(priv, 0x4188d4, 0x00000001); + nv_wr32(priv, 0x418910, 0x00010001); + nv_wr32(priv, 0x418914, 0x00000301); + nv_wr32(priv, 0x418918, 0x00800000); + nv_wr32(priv, 0x418980, 0x77777770); + nv_wr32(priv, 0x418984, 0x77777777); + nv_wr32(priv, 0x418988, 0x77777777); + nv_wr32(priv, 0x41898c, 0x77777777); + nv_wr32(priv, 0x418c04, 0x00000000); + nv_wr32(priv, 0x418c64, 0x00000000); + nv_wr32(priv, 0x418c68, 0x00000000); + nv_wr32(priv, 0x418c88, 0x00000000); + nv_wr32(priv, 0x418cb4, 0x00000000); + nv_wr32(priv, 0x418cb8, 0x00000000); + nv_wr32(priv, 0x418d00, 0x00000000); + nv_wr32(priv, 0x418d28, 0x00000000); + nv_wr32(priv, 0x418d2c, 0x00000000); + nv_wr32(priv, 0x418f00, 0x00000000); + nv_wr32(priv, 0x418f08, 0x00000000); + nv_wr32(priv, 0x418f20, 0x00000000); + nv_wr32(priv, 0x418f24, 0x00000000); + nv_wr32(priv, 0x418e00, 0x00000060); + nv_wr32(priv, 0x418e08, 0x00000000); + nv_wr32(priv, 0x418e1c, 0x00000000); + nv_wr32(priv, 0x418e20, 0x00000000); + nv_wr32(priv, 0x41900c, 0x00000000); + nv_wr32(priv, 0x419018, 0x00000000); +} + +static void +nve0_graph_init_tpc(struct nvc0_graph_priv *priv) +{ + nv_wr32(priv, 0x419d0c, 0x00000000); + nv_wr32(priv, 0x419d10, 0x00000014); + nv_wr32(priv, 0x419ab0, 0x00000000); + nv_wr32(priv, 0x419ac8, 0x00000000); + nv_wr32(priv, 0x419ab8, 0x000000e7); + nv_wr32(priv, 0x419abc, 0x00000000); + nv_wr32(priv, 0x419ac0, 0x00000000); + nv_wr32(priv, 0x419ab4, 0x00000000); + nv_wr32(priv, 0x41980c, 0x00000010); + nv_wr32(priv, 0x419844, 0x00000000); + nv_wr32(priv, 0x419850, 0x00000004); + nv_wr32(priv, 0x419854, 0x00000000); + nv_wr32(priv, 0x419858, 0x00000000); + nv_wr32(priv, 0x419c98, 0x00000000); + nv_wr32(priv, 0x419ca8, 0x00000000); + nv_wr32(priv, 0x419cb0, 0x01000000); + nv_wr32(priv, 0x419cb4, 0x00000000); + nv_wr32(priv, 0x419cb8, 0x00b08bea); + nv_wr32(priv, 0x419c84, 0x00010384); + nv_wr32(priv, 0x419cbc, 0x28137646); + nv_wr32(priv, 0x419cc0, 0x00000000); + nv_wr32(priv, 0x419cc4, 0x00000000); + nv_wr32(priv, 0x419c80, 0x00020232); + nv_wr32(priv, 0x419c0c, 0x00000000); + nv_wr32(priv, 0x419e00, 0x00000000); + nv_wr32(priv, 0x419ea0, 0x00000000); + nv_wr32(priv, 0x419ee4, 0x00000000); + nv_wr32(priv, 0x419ea4, 0x00000100); + nv_wr32(priv, 0x419ea8, 0x00000000); + nv_wr32(priv, 0x419eb4, 0x00000000); + nv_wr32(priv, 0x419eb8, 0x00000000); + nv_wr32(priv, 0x419ebc, 0x00000000); + nv_wr32(priv, 0x419ec0, 0x00000000); + nv_wr32(priv, 0x419edc, 0x00000000); + nv_wr32(priv, 0x419f00, 0x00000000); + nv_wr32(priv, 0x419f74, 0x00000555); +} + +static void +nve0_graph_init_tpcunk(struct nvc0_graph_priv *priv) +{ + nv_wr32(priv, 0x41be04, 0x00000000); + nv_wr32(priv, 0x41be08, 0x00000004); + nv_wr32(priv, 0x41be0c, 0x00000000); + nv_wr32(priv, 0x41be10, 0x003b8bc7); + nv_wr32(priv, 0x41be14, 0x00000000); + nv_wr32(priv, 0x41be18, 0x00000000); + nv_wr32(priv, 0x41bfd4, 0x00800000); + nv_wr32(priv, 0x41bfdc, 0x00000000); + nv_wr32(priv, 0x41bff8, 0x00000000); + nv_wr32(priv, 0x41bffc, 0x00000000); + nv_wr32(priv, 0x41becc, 0x00000000); + nv_wr32(priv, 0x41bee8, 0x00000000); + nv_wr32(priv, 0x41beec, 0x00000000); +} + +static void +nve0_graph_init_unk88xx(struct nvc0_graph_priv *priv) +{ + nv_wr32(priv, 0x40880c, 0x00000000); + nv_wr32(priv, 0x408850, 0x00000004); + nv_wr32(priv, 0x408910, 0x00000000); + nv_wr32(priv, 0x408914, 0x00000000); + nv_wr32(priv, 0x408918, 0x00000000); + nv_wr32(priv, 0x40891c, 0x00000000); + nv_wr32(priv, 0x408920, 0x00000000); + nv_wr32(priv, 0x408924, 0x00000000); + nv_wr32(priv, 0x408928, 0x00000000); + nv_wr32(priv, 0x40892c, 0x00000000); + nv_wr32(priv, 0x408930, 0x00000000); + nv_wr32(priv, 0x408950, 0x00000000); + nv_wr32(priv, 0x408954, 0x0000ffff); + nv_wr32(priv, 0x408958, 0x00000034); + nv_wr32(priv, 0x408984, 0x00000000); + nv_wr32(priv, 0x408988, 0x08040201); + nv_wr32(priv, 0x40898c, 0x80402010); +} + static void nve0_graph_init_units(struct nvc0_graph_priv *priv) { nv_wr32(priv, 0x409ffc, 0x00000000); nv_wr32(priv, 0x409c14, 0x00003e3e); - nv_wr32(priv, 0x409c24, 0x000f0000); + switch (nv_device(priv)->chipset) { + case 0xe6: + nv_wr32(priv, 0x409c24, 0x000f0001); + break; + default: + nv_wr32(priv, 0x409c24, 0x000f0000); + break; + } nv_wr32(priv, 0x404000, 0xc0000000); nv_wr32(priv, 0x404600, 0xc0000000); nv_wr32(priv, 0x408030, 0xc0000000); nv_wr32(priv, 0x404490, 0xc0000000); nv_wr32(priv, 0x406018, 0xc0000000); - nv_wr32(priv, 0x407020, 0xc0000000); + switch (nv_device(priv)->chipset) { + case 0xe6: + nv_wr32(priv, 0x407020, 0x40000000); + break; + default: + nv_wr32(priv, 0x407020, 0xc0000000); + break; + } nv_wr32(priv, 0x405840, 0xc0000000); nv_wr32(priv, 0x405844, 0x00ffffff); @@ -760,6 +965,27 @@ nve0_graph_init(struct nouveau_object *object) nve0_graph_init_obj418880(priv); nve0_graph_init_regs(priv); + + switch (nv_device(priv)->chipset) { + case 0xe6: + nve0_graph_init_unk40xx(priv); + nve0_graph_init_unk44xx(priv); + nve0_graph_init_unk78xx(priv); + nve0_graph_init_unk60xx(priv); + nve0_graph_init_unk64xx(priv); + nve0_graph_init_unk58xx(priv); + nve0_graph_init_unk80xx(priv); + nve0_graph_init_unk70xx(priv); + nve0_graph_init_unk5bxx(priv); + nve0_graph_init_gpc(priv); + nve0_graph_init_tpc(priv); + nve0_graph_init_tpcunk(priv); + nve0_graph_init_unk88xx(priv); + break; + default: + break; + } + nve0_graph_init_gpc_0(priv); nv_wr32(priv, 0x400500, 0x00010001); -- cgit v1.2.3 From 507cd5b553d88216a8d74ac9f2c73caceb3cd236 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 6 May 2013 15:27:44 +1000 Subject: drm/nve7/gr: update initial register/context values Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/core/engine/graph/ctxnve0.c | 6 ++++++ drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc | 4 ++-- drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h | 2 +- drivers/gpu/drm/nouveau/core/engine/graph/nve0.c | 3 +++ 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve0.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve0.c index d4e54745876b..f884ffbd408e 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve0.c @@ -750,6 +750,7 @@ nve0_grctx_generate_icmd(struct nvc0_graph_priv *priv) nv_icmd(priv, 0x000842, 0x00400008); nv_icmd(priv, 0x000843, 0x08000080); switch (nv_device(priv)->chipset) { + case 0xe7: case 0xe6: break; default: @@ -869,6 +870,7 @@ nve0_grctx_generate_icmd(struct nvc0_graph_priv *priv) nv_icmd(priv, 0x000814, 0x00000008); nv_icmd(priv, 0x000957, 0x00000003); switch (nv_device(priv)->chipset) { + case 0xe7: case 0xe6: break; default: @@ -2178,6 +2180,7 @@ nve0_grctx_generate_902d(struct nvc0_graph_priv *priv) case 0xe6: nv_mthd(priv, 0x902d, 0x3410, 0x80002006); break; + case 0xe7: default: nv_mthd(priv, 0x902d, 0x3410, 0x00000000); break; @@ -2547,6 +2550,7 @@ nve0_graph_generate_tpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419e94, 0x0); nv_wr32(priv, 0x419e98, 0x0); switch (nv_device(priv)->chipset) { + case 0xe7: case 0xe6: nv_wr32(priv, 0x419eac, 0x1f8f); break; @@ -2566,6 +2570,7 @@ nve0_graph_generate_tpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419f4c, 0x0); nv_wr32(priv, 0x419f58, 0x0); switch (nv_device(priv)->chipset) { + case 0xe7: case 0xe6: nv_wr32(priv, 0x419f70, 0x0); break; @@ -2574,6 +2579,7 @@ nve0_graph_generate_tpc(struct nvc0_graph_priv *priv) } nv_wr32(priv, 0x419f78, 0xb); switch (nv_device(priv)->chipset) { + case 0xe7: case 0xe6: nv_wr32(priv, 0x419f7c, 0x27a); break; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc index f58c4d0762d3..2aed9a54062d 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc @@ -55,8 +55,8 @@ chipsets: .b8 0xe7 0 0 0 .b16 #nve4_gpc_mmio_head .b16 #nve4_gpc_mmio_tail -.b16 #nve4_tpc_mmio_head -.b16 #nve4_tpc_mmio_tail +.b16 #nve6_tpc_mmio_head +.b16 #nve6_tpc_mmio_tail .b8 0xe6 0 0 0 .b16 #nve4_gpc_mmio_head .b16 #nve4_gpc_mmio_tail diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h index 321834f15311..1f33a66f96af 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h @@ -38,7 +38,7 @@ uint32_t nve0_grgpc_data[] = { 0x01580110, 0x000000e7, 0x0110008c, - 0x01580110, + 0x01a40158, 0x000000e6, 0x0110008c, 0x01a40158, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c b/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c index b7324138df02..c80132c8f01e 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c @@ -709,6 +709,7 @@ nve0_graph_init_units(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x409ffc, 0x00000000); nv_wr32(priv, 0x409c14, 0x00003e3e); switch (nv_device(priv)->chipset) { + case 0xe7: case 0xe6: nv_wr32(priv, 0x409c24, 0x000f0001); break; @@ -723,6 +724,7 @@ nve0_graph_init_units(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x404490, 0xc0000000); nv_wr32(priv, 0x406018, 0xc0000000); switch (nv_device(priv)->chipset) { + case 0xe7: case 0xe6: nv_wr32(priv, 0x407020, 0x40000000); break; @@ -967,6 +969,7 @@ nve0_graph_init(struct nouveau_object *object) nve0_graph_init_regs(priv); switch (nv_device(priv)->chipset) { + case 0xe7: case 0xe6: nve0_graph_init_unk40xx(priv); nve0_graph_init_unk44xx(priv); -- cgit v1.2.3 From cb1e06e0e3c3b10c99276a37b3b5884e7ec7f549 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 6 May 2013 16:00:20 +1000 Subject: drm/nvf0/gr: initial register/context setup Signed-off-by: Ben Skeggs --- .../gpu/drm/nouveau/core/engine/graph/ctxnve0.c | 260 +++++- .../drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc | 59 ++ .../nouveau/core/engine/graph/fuc/gpcnve0.fuc.h | 77 +- .../drm/nouveau/core/engine/graph/fuc/hubnve0.fuc | 74 +- .../nouveau/core/engine/graph/fuc/hubnve0.fuc.h | 955 ++++++++++++--------- drivers/gpu/drm/nouveau/core/engine/graph/nve0.c | 114 ++- 6 files changed, 1057 insertions(+), 482 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve0.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve0.c index f884ffbd408e..574a1deffcb9 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve0.c @@ -2190,6 +2190,15 @@ nve0_grctx_generate_902d(struct nvc0_graph_priv *priv) static void nve0_graph_generate_unk40xx(struct nvc0_graph_priv *priv) { + switch (nv_device(priv)->chipset) { + case 0xf0: + nv_wr32(priv, 0x404004, 0x00000000); + nv_wr32(priv, 0x404008, 0x00000000); + nv_wr32(priv, 0x40400c, 0x00000000); + break; + default: + break; + } nv_wr32(priv, 0x404010, 0x0); nv_wr32(priv, 0x404014, 0x0); nv_wr32(priv, 0x404018, 0x0); @@ -2197,6 +2206,19 @@ nve0_graph_generate_unk40xx(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x404020, 0x0); nv_wr32(priv, 0x404024, 0xe000); nv_wr32(priv, 0x404028, 0x0); + switch (nv_device(priv)->chipset) { + case 0xf0: + nv_wr32(priv, 0x40402c, 0x00000000); + nv_wr32(priv, 0x404030, 0x00000000); + nv_wr32(priv, 0x404034, 0x00000000); + nv_wr32(priv, 0x404038, 0x00000000); + nv_wr32(priv, 0x40403c, 0x00000000); + nv_wr32(priv, 0x404040, 0x00000000); + nv_wr32(priv, 0x404044, 0x00000000); + break; + default: + break; + } nv_wr32(priv, 0x4040a8, 0x0); nv_wr32(priv, 0x4040ac, 0x0); nv_wr32(priv, 0x4040b0, 0x0); @@ -2214,6 +2236,22 @@ nve0_graph_generate_unk40xx(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x4040e4, 0x0); nv_wr32(priv, 0x4040e8, 0x1000); nv_wr32(priv, 0x4040f8, 0x0); + switch (nv_device(priv)->chipset) { + case 0xf0: + nv_wr32(priv, 0x404100, 0x00000000); + nv_wr32(priv, 0x404104, 0x00000000); + nv_wr32(priv, 0x404108, 0x00000000); + nv_wr32(priv, 0x40410c, 0x00000000); + nv_wr32(priv, 0x404110, 0x00000000); + nv_wr32(priv, 0x404114, 0x00000000); + nv_wr32(priv, 0x404118, 0x00000000); + nv_wr32(priv, 0x40411c, 0x00000000); + nv_wr32(priv, 0x404120, 0x00000000); + nv_wr32(priv, 0x404124, 0x00000000); + break; + default: + break; + } nv_wr32(priv, 0x404130, 0x0); nv_wr32(priv, 0x404134, 0x0); nv_wr32(priv, 0x404138, 0x20000040); @@ -2221,14 +2259,32 @@ nve0_graph_generate_unk40xx(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x404154, 0x400); nv_wr32(priv, 0x404158, 0x200); nv_wr32(priv, 0x404164, 0x55); + switch (nv_device(priv)->chipset) { + case 0xf0: + nv_wr32(priv, 0x40417c, 0x00000000); + nv_wr32(priv, 0x404180, 0x00000000); + break; + default: + break; + } nv_wr32(priv, 0x4041a0, 0x0); nv_wr32(priv, 0x4041a4, 0x0); nv_wr32(priv, 0x4041a8, 0x0); nv_wr32(priv, 0x4041ac, 0x0); - nv_wr32(priv, 0x404200, 0x0); - nv_wr32(priv, 0x404204, 0x0); - nv_wr32(priv, 0x404208, 0x0); - nv_wr32(priv, 0x40420c, 0x0); + switch (nv_device(priv)->chipset) { + case 0xf0: + nv_wr32(priv, 0x404200, 0xa197); + nv_wr32(priv, 0x404204, 0xa1c0); + nv_wr32(priv, 0x404208, 0xa140); + nv_wr32(priv, 0x40420c, 0x902d); + break; + default: + nv_wr32(priv, 0x404200, 0x0); + nv_wr32(priv, 0x404204, 0x0); + nv_wr32(priv, 0x404208, 0x0); + nv_wr32(priv, 0x40420c, 0x0); + break; + } } static void @@ -2246,7 +2302,13 @@ nve0_graph_generate_unk44xx(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x404428, 0x0); nv_wr32(priv, 0x40442c, 0x0); nv_wr32(priv, 0x404430, 0x0); - nv_wr32(priv, 0x404434, 0x0); + switch (nv_device(priv)->chipset) { + case 0xf0: + break; + default: + nv_wr32(priv, 0x404434, 0x0); + break; + } nv_wr32(priv, 0x404438, 0x0); nv_wr32(priv, 0x404460, 0x0); nv_wr32(priv, 0x404464, 0x0); @@ -2339,12 +2401,26 @@ nve0_graph_generate_unk5bxx(struct nvc0_graph_priv *priv) { nv_wr32(priv, 0x405b00, 0x0); nv_wr32(priv, 0x405b10, 0x1000); + switch (nv_device(priv)->chipset) { + case 0xf0: + nv_wr32(priv, 0x405b20, 0x04000000); + break; + default: + break; + } } static void nve0_graph_generate_unk60xx(struct nvc0_graph_priv *priv) { - nv_wr32(priv, 0x406020, 0x4103c1); + switch (nv_device(priv)->chipset) { + case 0xf0: + nv_wr32(priv, 0x406020, 0x34103c1); + break; + default: + nv_wr32(priv, 0x406020, 0x4103c1); + break; + } nv_wr32(priv, 0x406028, 0x1); nv_wr32(priv, 0x40602c, 0x1); nv_wr32(priv, 0x406030, 0x1); @@ -2356,11 +2432,27 @@ nve0_graph_generate_unk64xx(struct nvc0_graph_priv *priv) { nv_wr32(priv, 0x4064a8, 0x0); nv_wr32(priv, 0x4064ac, 0x3fff); + switch (nv_device(priv)->chipset) { + case 0xf0: + nv_wr32(priv, 0x4064b0, 0x0); + break; + default: + break; + } nv_wr32(priv, 0x4064b4, 0x0); nv_wr32(priv, 0x4064b8, 0x0); - nv_wr32(priv, 0x4064c0, 0x801a00f0); - nv_wr32(priv, 0x4064c4, 0x192ffff); - nv_wr32(priv, 0x4064c8, 0x1800600); + switch (nv_device(priv)->chipset) { + case 0xf0: + nv_wr32(priv, 0x4064c0, 0x802000f0); + nv_wr32(priv, 0x4064c4, 0x192ffff); + nv_wr32(priv, 0x4064c8, 0x18007c0); + break; + default: + nv_wr32(priv, 0x4064c0, 0x801a00f0); + nv_wr32(priv, 0x4064c4, 0x192ffff); + nv_wr32(priv, 0x4064c8, 0x1800600); + break; + } nv_wr32(priv, 0x4064cc, 0x0); nv_wr32(priv, 0x4064d0, 0x0); nv_wr32(priv, 0x4064d4, 0x0); @@ -2376,7 +2468,13 @@ nve0_graph_generate_unk64xx(struct nvc0_graph_priv *priv) static void nve0_graph_generate_unk70xx(struct nvc0_graph_priv *priv) { - nv_wr32(priv, 0x407040, 0x0); + switch (nv_device(priv)->chipset) { + case 0xf0: + break; + default: + nv_wr32(priv, 0x407040, 0x0); + break; + } } static void @@ -2408,9 +2506,23 @@ nve0_graph_generate_unk80xx(struct nvc0_graph_priv *priv) static void nve0_graph_generate_unk88xx(struct nvc0_graph_priv *priv) { - nv_wr32(priv, 0x408800, 0x2802a3c); + switch (nv_device(priv)->chipset) { + case 0xf0: + nv_wr32(priv, 0x408800, 0x12802a3c); + break; + default: + nv_wr32(priv, 0x408800, 0x2802a3c); + break; + } nv_wr32(priv, 0x408804, 0x40); - nv_wr32(priv, 0x408808, 0x1043e005); + switch (nv_device(priv)->chipset) { + case 0xf0: + nv_wr32(priv, 0x408808, 0x1003e005); + break; + default: + nv_wr32(priv, 0x408808, 0x1043e005); + break; + } nv_wr32(priv, 0x408840, 0xb); nv_wr32(priv, 0x408900, 0x3080b801); nv_wr32(priv, 0x408904, 0x62000001); @@ -2447,7 +2559,14 @@ nve0_graph_generate_gpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x418710, 0x0); nv_wr32(priv, 0x418800, 0x7006860a); nv_wr32(priv, 0x418808, 0x0); - nv_wr32(priv, 0x41880c, 0x0); + switch (nv_device(priv)->chipset) { + case 0xf0: + nv_wr32(priv, 0x41880c, 0x30); + break; + default: + nv_wr32(priv, 0x41880c, 0x0); + break; + } nv_wr32(priv, 0x418810, 0x0); nv_wr32(priv, 0x418828, 0x44); nv_wr32(priv, 0x418830, 0x10000001); @@ -2493,6 +2612,13 @@ nve0_graph_generate_gpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x418c6c, 0x1); nv_wr32(priv, 0x418c80, 0x20200004); nv_wr32(priv, 0x418c8c, 0x1); + switch (nv_device(priv)->chipset) { + case 0xf0: + nv_wr32(priv, 0x418d24, 0x0); + break; + default: + break; + } nv_wr32(priv, 0x419000, 0x780); nv_wr32(priv, 0x419004, 0x0); nv_wr32(priv, 0x419008, 0x0); @@ -2512,31 +2638,71 @@ nve0_graph_generate_tpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419a10, 0x0); nv_wr32(priv, 0x419a14, 0x200); nv_wr32(priv, 0x419a1c, 0xc000); - nv_wr32(priv, 0x419a20, 0x800); + switch (nv_device(priv)->chipset) { + case 0xf0: + nv_wr32(priv, 0x419a20, 0x20800); + break; + default: + nv_wr32(priv, 0x419a20, 0x800); + break; + } nv_wr32(priv, 0x419a30, 0x1); nv_wr32(priv, 0x419ac4, 0x37f440); - nv_wr32(priv, 0x419c00, 0xa); + switch (nv_device(priv)->chipset) { + case 0xf0: + nv_wr32(priv, 0x419c00, 0x1a); + break; + default: + nv_wr32(priv, 0x419c00, 0xa); + break; + } nv_wr32(priv, 0x419c04, 0x80000006); nv_wr32(priv, 0x419c08, 0x2); nv_wr32(priv, 0x419c20, 0x0); nv_wr32(priv, 0x419c24, 0x84210); nv_wr32(priv, 0x419c28, 0x3efbefbe); nv_wr32(priv, 0x419ce8, 0x0); - nv_wr32(priv, 0x419cf4, 0x3203); - nv_wr32(priv, 0x419e04, 0x0); - nv_wr32(priv, 0x419e08, 0x0); - nv_wr32(priv, 0x419e0c, 0x0); - nv_wr32(priv, 0x419e10, 0x402); + switch (nv_device(priv)->chipset) { + case 0xf0: + nv_wr32(priv, 0x419cf4, 0x203); + nv_wr32(priv, 0x419e04, 0x0); + nv_wr32(priv, 0x419e08, 0x1d); + nv_wr32(priv, 0x419e0c, 0x0); + nv_wr32(priv, 0x419e10, 0x1c02); + + break; + default: + nv_wr32(priv, 0x419cf4, 0x3203); + nv_wr32(priv, 0x419e04, 0x0); + nv_wr32(priv, 0x419e08, 0x0); + nv_wr32(priv, 0x419e0c, 0x0); + nv_wr32(priv, 0x419e10, 0x402); + break; + } nv_wr32(priv, 0x419e44, 0x13eff2); nv_wr32(priv, 0x419e48, 0x0); nv_wr32(priv, 0x419e4c, 0x7f); nv_wr32(priv, 0x419e50, 0x0); nv_wr32(priv, 0x419e54, 0x0); - nv_wr32(priv, 0x419e58, 0x0); + switch (nv_device(priv)->chipset) { + case 0xf0: + nv_wr32(priv, 0x419e58, 0x1); + break; + default: + nv_wr32(priv, 0x419e58, 0x0); + break; + } nv_wr32(priv, 0x419e5c, 0x0); nv_wr32(priv, 0x419e60, 0x0); nv_wr32(priv, 0x419e64, 0x0); - nv_wr32(priv, 0x419e68, 0x0); + switch (nv_device(priv)->chipset) { + case 0xf0: + nv_wr32(priv, 0x419e68, 0x2); + break; + default: + nv_wr32(priv, 0x419e68, 0x0); + break; + } nv_wr32(priv, 0x419e6c, 0x0); nv_wr32(priv, 0x419e70, 0x0); nv_wr32(priv, 0x419e74, 0x0); @@ -2553,37 +2719,49 @@ nve0_graph_generate_tpc(struct nvc0_graph_priv *priv) case 0xe7: case 0xe6: nv_wr32(priv, 0x419eac, 0x1f8f); + nv_wr32(priv, 0x419eb0, 0xd3f); + break; + case 0xf0: + nv_wr32(priv, 0x419eac, 0x1fcf); + nv_wr32(priv, 0x419eb0, 0xdb00da0); + nv_wr32(priv, 0x419eb8, 0x0); break; default: nv_wr32(priv, 0x419eac, 0x1fcf); + nv_wr32(priv, 0x419eb0, 0xd3f); break; } - nv_wr32(priv, 0x419eb0, 0xd3f); nv_wr32(priv, 0x419ec8, 0x1304f); nv_wr32(priv, 0x419f30, 0x0); nv_wr32(priv, 0x419f34, 0x0); nv_wr32(priv, 0x419f38, 0x0); nv_wr32(priv, 0x419f3c, 0x0); - nv_wr32(priv, 0x419f40, 0x0); - nv_wr32(priv, 0x419f44, 0x0); - nv_wr32(priv, 0x419f48, 0x0); - nv_wr32(priv, 0x419f4c, 0x0); - nv_wr32(priv, 0x419f58, 0x0); switch (nv_device(priv)->chipset) { - case 0xe7: - case 0xe6: - nv_wr32(priv, 0x419f70, 0x0); + case 0xf0: + nv_wr32(priv, 0x419f40, 0x18); break; default: + nv_wr32(priv, 0x419f40, 0x0); break; } - nv_wr32(priv, 0x419f78, 0xb); + nv_wr32(priv, 0x419f44, 0x0); + nv_wr32(priv, 0x419f48, 0x0); + nv_wr32(priv, 0x419f4c, 0x0); + nv_wr32(priv, 0x419f58, 0x0); switch (nv_device(priv)->chipset) { case 0xe7: case 0xe6: + nv_wr32(priv, 0x419f70, 0x0); + nv_wr32(priv, 0x419f78, 0xb); nv_wr32(priv, 0x419f7c, 0x27a); break; + case 0xf0: + nv_wr32(priv, 0x419f70, 0x7300); + nv_wr32(priv, 0x419f78, 0xeb); + nv_wr32(priv, 0x419f7c, 0x404); + break; default: + nv_wr32(priv, 0x419f78, 0xb); break; } } @@ -2592,9 +2770,23 @@ static void nve0_graph_generate_tpcunk(struct nvc0_graph_priv *priv) { nv_wr32(priv, 0x41be24, 0x6); - nv_wr32(priv, 0x41bec0, 0x12180000); + switch (nv_device(priv)->chipset) { + case 0xf0: + nv_wr32(priv, 0x41bec0, 0x10000000); + break; + default: + nv_wr32(priv, 0x41bec0, 0x12180000); + break; + } nv_wr32(priv, 0x41bec4, 0x37f7f); - nv_wr32(priv, 0x41bee4, 0x6480430); + switch (nv_device(priv)->chipset) { + case 0xf0: + nv_wr32(priv, 0x41bee4, 0x0); + break; + default: + nv_wr32(priv, 0x41bee4, 0x6480430); + break; + } nv_wr32(priv, 0x41bf00, 0xa418820); nv_wr32(priv, 0x41bf04, 0x62080e6); nv_wr32(priv, 0x41bf08, 0x20398a4); diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc index 2aed9a54062d..e906ca68674d 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc @@ -62,6 +62,11 @@ chipsets: .b16 #nve4_gpc_mmio_tail .b16 #nve6_tpc_mmio_head .b16 #nve6_tpc_mmio_tail +.b8 0xf0 0 0 0 +.b16 #nvf0_gpc_mmio_head +.b16 #nvf0_gpc_mmio_tail +.b16 #nvf0_tpc_mmio_head +.b16 #nvf0_tpc_mmio_tail .b8 0 0 0 0 // GPC mmio lists @@ -101,6 +106,37 @@ mmctx_data(0x0031d0, 1) mmctx_data(0x0031e0, 2) nve4_gpc_mmio_tail: +nvf0_gpc_mmio_head: +mmctx_data(0x000380, 1) +mmctx_data(0x000400, 2) +mmctx_data(0x00040c, 3) +mmctx_data(0x000450, 9) +mmctx_data(0x000600, 1) +mmctx_data(0x000684, 1) +mmctx_data(0x000700, 5) +mmctx_data(0x000800, 1) +mmctx_data(0x000808, 3) +mmctx_data(0x000828, 1) +mmctx_data(0x000830, 1) +mmctx_data(0x0008d8, 1) +mmctx_data(0x0008e0, 1) +mmctx_data(0x0008e8, 6) +mmctx_data(0x00091c, 1) +mmctx_data(0x000924, 3) +mmctx_data(0x000b00, 1) +mmctx_data(0x000b08, 6) +mmctx_data(0x000bb8, 1) +mmctx_data(0x000c08, 1) +mmctx_data(0x000c10, 8) +mmctx_data(0x000c40, 1) +mmctx_data(0x000c6c, 1) +mmctx_data(0x000c80, 1) +mmctx_data(0x000c8c, 1) +mmctx_data(0x000d24, 1) +mmctx_data(0x001000, 3) +mmctx_data(0x001014, 1) +nvf0_gpc_mmio_tail: + // TPC mmio lists nve4_tpc_mmio_head: mmctx_data(0x000048, 1) @@ -145,6 +181,29 @@ mmctx_data(0x000770, 1) mmctx_data(0x000778, 2) nve6_tpc_mmio_tail: +nvf0_tpc_mmio_head: +mmctx_data(0x000048, 1) +mmctx_data(0x000064, 1) +mmctx_data(0x000088, 1) +mmctx_data(0x000200, 6) +mmctx_data(0x00021c, 2) +mmctx_data(0x000230, 1) +mmctx_data(0x0002c4, 1) +mmctx_data(0x000400, 3) +mmctx_data(0x000420, 3) +mmctx_data(0x0004e8, 1) +mmctx_data(0x0004f4, 1) +mmctx_data(0x000604, 4) +mmctx_data(0x000644, 22) +mmctx_data(0x0006ac, 2) +mmctx_data(0x0006b8, 1) +mmctx_data(0x0006c8, 1) +mmctx_data(0x000730, 8) +mmctx_data(0x000758, 1) +mmctx_data(0x000770, 1) +mmctx_data(0x000778, 2) +nvf0_tpc_mmio_tail: + .section #nve0_grgpc_code bra #init define(`include_code') diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h index 1f33a66f96af..592433954d41 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h @@ -34,16 +34,19 @@ uint32_t nve0_grgpc_data[] = { 0x00000000, /* 0x0064: chipsets */ 0x000000e4, - 0x0110008c, - 0x01580110, + 0x011c0098, + 0x01d4018c, 0x000000e7, - 0x0110008c, - 0x01a40158, + 0x011c0098, + 0x022001d4, 0x000000e6, - 0x0110008c, - 0x01a40158, + 0x011c0098, + 0x022001d4, + 0x000000f0, + 0x018c011c, + 0x02700220, 0x00000000, -/* 0x008c: nve4_gpc_mmio_head */ +/* 0x0098: nve4_gpc_mmio_head */ 0x00000380, 0x04000400, 0x0800040c, @@ -77,8 +80,38 @@ uint32_t nve0_grgpc_data[] = { 0x14003100, 0x000031d0, 0x040031e0, -/* 0x0110: nve4_gpc_mmio_tail */ -/* 0x0110: nve4_tpc_mmio_head */ +/* 0x011c: nve4_gpc_mmio_tail */ +/* 0x011c: nvf0_gpc_mmio_head */ + 0x00000380, + 0x04000400, + 0x0800040c, + 0x20000450, + 0x00000600, + 0x00000684, + 0x10000700, + 0x00000800, + 0x08000808, + 0x00000828, + 0x00000830, + 0x000008d8, + 0x000008e0, + 0x140008e8, + 0x0000091c, + 0x08000924, + 0x00000b00, + 0x14000b08, + 0x00000bb8, + 0x00000c08, + 0x1c000c10, + 0x00000c40, + 0x00000c6c, + 0x00000c80, + 0x00000c8c, + 0x00000d24, + 0x08001000, + 0x00001014, +/* 0x018c: nvf0_gpc_mmio_tail */ +/* 0x018c: nve4_tpc_mmio_head */ 0x00000048, 0x00000064, 0x00000088, @@ -97,8 +130,29 @@ uint32_t nve0_grgpc_data[] = { 0x1c000730, 0x00000758, 0x00000778, -/* 0x0158: nve4_tpc_mmio_tail */ -/* 0x0158: nve6_tpc_mmio_head */ +/* 0x01d4: nve4_tpc_mmio_tail */ +/* 0x01d4: nve6_tpc_mmio_head */ + 0x00000048, + 0x00000064, + 0x00000088, + 0x14000200, + 0x0400021c, + 0x00000230, + 0x000002c4, + 0x08000400, + 0x08000420, + 0x000004e8, + 0x000004f4, + 0x0c000604, + 0x54000644, + 0x040006ac, + 0x000006c8, + 0x1c000730, + 0x00000758, + 0x00000770, + 0x04000778, +/* 0x0220: nve6_tpc_mmio_tail */ +/* 0x0220: nvf0_tpc_mmio_head */ 0x00000048, 0x00000064, 0x00000088, @@ -113,6 +167,7 @@ uint32_t nve0_grgpc_data[] = { 0x0c000604, 0x54000644, 0x040006ac, + 0x000006b8, 0x000006c8, 0x1c000730, 0x00000758, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc index 7fe9d7cf486b..b57a3db8df71 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc @@ -37,6 +37,15 @@ hub_mmio_list_tail: .b32 0 ctx_current: .b32 0 +.align 256 +chan_data: +chan_mmio_count: .b32 0 +chan_mmio_address: .b32 0 + +.align 256 +xfer_data: .b32 0 + +.align 256 chipsets: .b8 0xe4 0 0 0 .b16 #nve4_hub_mmio_head @@ -47,6 +56,9 @@ chipsets: .b8 0xe6 0 0 0 .b16 #nve4_hub_mmio_head .b16 #nve4_hub_mmio_tail +.b8 0xf0 0 0 0 +.b16 #nvf0_hub_mmio_head +.b16 #nvf0_hub_mmio_tail .b8 0 0 0 0 nve4_hub_mmio_head: @@ -103,13 +115,61 @@ mmctx_data(0x408900, 3) mmctx_data(0x408980, 1) nve4_hub_mmio_tail: -.align 256 -chan_data: -chan_mmio_count: .b32 0 -chan_mmio_address: .b32 0 - -.align 256 -xfer_data: .b32 0 +nvf0_hub_mmio_head: +mmctx_data(0x17e91c, 2) +mmctx_data(0x400204, 2) +mmctx_data(0x404004, 17) +mmctx_data(0x4040a8, 9) +mmctx_data(0x4040d0, 7) +mmctx_data(0x4040f8, 1) +mmctx_data(0x404100, 10) +mmctx_data(0x404130, 3) +mmctx_data(0x404150, 3) +mmctx_data(0x404164, 1) +mmctx_data(0x40417c, 2) +mmctx_data(0x4041a0, 4) +mmctx_data(0x404200, 4) +mmctx_data(0x404404, 12) +mmctx_data(0x404438, 1) +mmctx_data(0x404460, 4) +mmctx_data(0x404480, 1) +mmctx_data(0x404498, 1) +mmctx_data(0x404604, 4) +mmctx_data(0x404618, 4) +mmctx_data(0x40462c, 2) +mmctx_data(0x404640, 1) +mmctx_data(0x404654, 1) +mmctx_data(0x404660, 1) +mmctx_data(0x404678, 19) +mmctx_data(0x4046c8, 3) +mmctx_data(0x404700, 3) +mmctx_data(0x404718, 10) +mmctx_data(0x404744, 2) +mmctx_data(0x404754, 1) +mmctx_data(0x405800, 1) +mmctx_data(0x405830, 3) +mmctx_data(0x405854, 1) +mmctx_data(0x405870, 4) +mmctx_data(0x405a00, 2) +mmctx_data(0x405a18, 1) +mmctx_data(0x405b00, 1) +mmctx_data(0x405b10, 1) +mmctx_data(0x405b20, 1) +mmctx_data(0x406020, 1) +mmctx_data(0x406028, 4) +mmctx_data(0x4064a8, 5) +mmctx_data(0x4064c0, 12) +mmctx_data(0x4064fc, 1) +mmctx_data(0x407804, 1) +mmctx_data(0x40780c, 6) +mmctx_data(0x4078bc, 1) +mmctx_data(0x408000, 7) +mmctx_data(0x408064, 1) +mmctx_data(0x408800, 3) +mmctx_data(0x408840, 1) +mmctx_data(0x408900, 3) +mmctx_data(0x408980, 1) +nvf0_hub_mmio_tail: .section #nve0_grhub_code bra #init diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc.h index e3421af68ab9..f22422e09045 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc.h @@ -28,67 +28,7 @@ uint32_t nve0_grhub_data[] = { 0x00000000, /* 0x0058: ctx_current */ 0x00000000, -/* 0x005c: chipsets */ - 0x000000e4, - 0x01440078, - 0x000000e7, - 0x01440078, - 0x000000e6, - 0x01440078, 0x00000000, -/* 0x0078: nve4_hub_mmio_head */ - 0x0417e91c, - 0x04400204, - 0x18404010, - 0x204040a8, - 0x184040d0, - 0x004040f8, - 0x08404130, - 0x08404150, - 0x00404164, - 0x0c4041a0, - 0x0c404200, - 0x34404404, - 0x0c404460, - 0x00404480, - 0x00404498, - 0x0c404604, - 0x0c404618, - 0x0440462c, - 0x00404640, - 0x00404654, - 0x00404660, - 0x48404678, - 0x084046c8, - 0x08404700, - 0x24404718, - 0x04404744, - 0x00404754, - 0x00405800, - 0x08405830, - 0x00405854, - 0x0c405870, - 0x04405a00, - 0x00405a18, - 0x00405b00, - 0x00405b10, - 0x00406020, - 0x0c406028, - 0x044064a8, - 0x044064b4, - 0x2c4064c0, - 0x004064fc, - 0x00407040, - 0x00407804, - 0x1440780c, - 0x004078bc, - 0x18408000, - 0x00408064, - 0x08408800, - 0x00408840, - 0x08408900, - 0x00408980, -/* 0x0144: nve4_hub_mmio_tail */ 0x00000000, 0x00000000, 0x00000000, @@ -129,6 +69,26 @@ uint32_t nve0_grhub_data[] = { 0x00000000, 0x00000000, 0x00000000, +/* 0x0100: chan_data */ +/* 0x0100: chan_mmio_count */ + 0x00000000, +/* 0x0104: chan_mmio_address */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, @@ -136,10 +96,7 @@ uint32_t nve0_grhub_data[] = { 0x00000000, 0x00000000, 0x00000000, -/* 0x0200: chan_data */ -/* 0x0200: chan_mmio_count */ 0x00000000, -/* 0x0204: chan_mmio_address */ 0x00000000, 0x00000000, 0x00000000, @@ -179,6 +136,7 @@ uint32_t nve0_grhub_data[] = { 0x00000000, 0x00000000, 0x00000000, +/* 0x0200: xfer_data */ 0x00000000, 0x00000000, 0x00000000, @@ -203,8 +161,163 @@ uint32_t nve0_grhub_data[] = { 0x00000000, 0x00000000, 0x00000000, -/* 0x0300: xfer_data */ 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0300: chipsets */ + 0x000000e4, + 0x03f00324, + 0x000000e7, + 0x03f00324, + 0x000000e6, + 0x03f00324, + 0x000000f0, + 0x04c403f0, + 0x00000000, +/* 0x0324: nve4_hub_mmio_head */ + 0x0417e91c, + 0x04400204, + 0x18404010, + 0x204040a8, + 0x184040d0, + 0x004040f8, + 0x08404130, + 0x08404150, + 0x00404164, + 0x0c4041a0, + 0x0c404200, + 0x34404404, + 0x0c404460, + 0x00404480, + 0x00404498, + 0x0c404604, + 0x0c404618, + 0x0440462c, + 0x00404640, + 0x00404654, + 0x00404660, + 0x48404678, + 0x084046c8, + 0x08404700, + 0x24404718, + 0x04404744, + 0x00404754, + 0x00405800, + 0x08405830, + 0x00405854, + 0x0c405870, + 0x04405a00, + 0x00405a18, + 0x00405b00, + 0x00405b10, + 0x00406020, + 0x0c406028, + 0x044064a8, + 0x044064b4, + 0x2c4064c0, + 0x004064fc, + 0x00407040, + 0x00407804, + 0x1440780c, + 0x004078bc, + 0x18408000, + 0x00408064, + 0x08408800, + 0x00408840, + 0x08408900, + 0x00408980, +/* 0x03f0: nve4_hub_mmio_tail */ +/* 0x03f0: nvf0_hub_mmio_head */ + 0x0417e91c, + 0x04400204, + 0x40404004, + 0x204040a8, + 0x184040d0, + 0x004040f8, + 0x24404100, + 0x08404130, + 0x08404150, + 0x00404164, + 0x0440417c, + 0x0c4041a0, + 0x0c404200, + 0x2c404404, + 0x00404438, + 0x0c404460, + 0x00404480, + 0x00404498, + 0x0c404604, + 0x0c404618, + 0x0440462c, + 0x00404640, + 0x00404654, + 0x00404660, + 0x48404678, + 0x084046c8, + 0x08404700, + 0x24404718, + 0x04404744, + 0x00404754, + 0x00405800, + 0x08405830, + 0x00405854, + 0x0c405870, + 0x04405a00, + 0x00405a18, + 0x00405b00, + 0x00405b10, + 0x00405b20, + 0x00406020, + 0x0c406028, + 0x104064a8, + 0x2c4064c0, + 0x004064fc, + 0x00407804, + 0x1440780c, + 0x004078bc, + 0x18408000, + 0x00408064, + 0x08408800, + 0x00408840, + 0x08408900, + 0x00408980, }; uint32_t nve0_grhub_code[] = { @@ -440,7 +553,7 @@ uint32_t nve0_grhub_code[] = { 0x0017f100, 0x0227f012, 0xf10012d0, - 0xfe05b917, + 0xfe05ba17, 0x17f10010, 0x10d00400, 0x0437f1c0, @@ -474,385 +587,385 @@ uint32_t nve0_grhub_code[] = { 0x4021d000, 0x080027f1, 0xcf0624b6, - 0xf7f00022, -/* 0x03a9: init_find_chipset */ - 0x08f0b654, - 0xb800f398, - 0x0bf40432, - 0x0034b00b, - 0xf8f11bf4, -/* 0x03bd: init_context */ - 0x0017f100, - 0x02fe5801, - 0xf003ff58, - 0x0e8000e3, - 0x150f8014, - 0x013d21f5, - 0x070037f1, - 0x950634b6, - 0x34d00814, - 0x4034d000, - 0x130030b7, - 0xb6001fbb, - 0x3fd002f5, - 0x0815b600, - 0xb60110b6, - 0x1fb90814, - 0x6321f502, - 0x001fbb02, - 0xf1000398, - 0xf0200047, -/* 0x040e: init_gpc */ - 0x4ea05043, - 0x1fb90804, - 0x8d21f402, - 0x08004ea0, - 0xf4022fb9, - 0x4ea08d21, - 0xf4bd010c, + 0xf7f10022, +/* 0x03aa: init_find_chipset */ + 0xf0b602f8, + 0x00f39808, + 0xf40432b8, + 0x34b00b0b, + 0xf11bf400, +/* 0x03be: init_context */ + 0x17f100f8, + 0xfe580100, + 0x03ff5802, + 0x8000e3f0, + 0x0f80140e, + 0x3d21f515, + 0x0037f101, + 0x0634b607, + 0xd0081495, + 0x34d00034, + 0x0030b740, + 0x001fbb13, + 0xd002f5b6, + 0x15b6003f, + 0x0110b608, + 0xb90814b6, + 0x21f5021f, + 0x1fbb0263, + 0x00039800, + 0x200047f1, +/* 0x040f: init_gpc */ + 0xa05043f0, + 0xb908044e, + 0x21f4021f, + 0x004ea08d, + 0x022fb908, 0xa08d21f4, - 0xf401044e, - 0x4ea08d21, - 0xf7f00100, - 0x8d21f402, - 0x08004ea0, -/* 0x0440: init_gpc_wait */ - 0xc86821f4, - 0x0bf41fff, - 0x044ea0fa, + 0xbd010c4e, + 0x8d21f4f4, + 0x01044ea0, + 0xa08d21f4, + 0xf001004e, + 0x21f402f7, + 0x004ea08d, +/* 0x0441: init_gpc_wait */ 0x6821f408, - 0xb7001fbb, - 0xb6800040, - 0x1bf40132, - 0x0027f1b4, - 0x0624b608, - 0xb74021d0, - 0xbd080020, - 0x1f19f014, -/* 0x0473: main */ - 0xf40021d0, - 0x28f40031, - 0x08d7f000, - 0xf43921f4, - 0xe4b1f401, - 0x1bf54001, - 0x87f100d1, - 0x84b6083c, - 0xf094bd06, - 0x89d00499, - 0x0017f100, - 0x0614b60b, - 0xcf4012cf, - 0x13c80011, - 0x7e0bf41f, - 0xf41f23c8, - 0x20f95a0b, - 0xf10212b9, + 0xf41fffc8, + 0x4ea0fa0b, + 0x21f40804, + 0x001fbb68, + 0x800040b7, + 0xf40132b6, + 0x27f1b41b, + 0x24b60800, + 0x4021d006, + 0x080020b7, + 0x19f014bd, + 0x0021d01f, +/* 0x0474: main */ + 0xf40031f4, + 0xd7f00028, + 0x3921f408, + 0xb1f401f4, + 0xf54001e4, + 0xf100d11b, 0xb6083c87, 0x94bd0684, - 0xd00799f0, - 0x32f40089, - 0x0231f401, - 0x07fb21f5, - 0x085c87f1, + 0xd00499f0, + 0x17f10089, + 0x14b60b00, + 0x4012cf06, + 0xc80011cf, + 0x0bf41f13, + 0x1f23c87e, + 0xf95a0bf4, + 0x0212b920, + 0x083c87f1, 0xbd0684b6, 0x0799f094, - 0xfc0089d0, - 0x3c87f120, + 0xf40089d0, + 0x31f40132, + 0xfc21f502, + 0x5c87f107, 0x0684b608, 0x99f094bd, - 0x0089d006, - 0xf50131f4, - 0xf107fb21, - 0xb6085c87, - 0x94bd0684, - 0xd00699f0, - 0x0ef40089, -/* 0x0509: chsw_prev_no_next */ - 0xb920f931, - 0x32f40212, - 0x0232f401, - 0x07fb21f5, - 0x17f120fc, - 0x14b60b00, - 0x0012d006, -/* 0x0527: chsw_no_prev */ - 0xc8130ef4, - 0x0bf41f23, - 0x0131f40d, - 0xf50232f4, -/* 0x0537: chsw_done */ - 0xf107fb21, - 0xb60b0c17, - 0x27f00614, - 0x0012d001, + 0x0089d007, + 0x87f120fc, + 0x84b6083c, + 0xf094bd06, + 0x89d00699, + 0x0131f400, + 0x07fc21f5, 0x085c87f1, 0xbd0684b6, - 0x0499f094, - 0xf50089d0, -/* 0x0557: main_not_ctx_switch */ - 0xb0ff200e, - 0x1bf401e4, - 0x02f2b90d, - 0x078f21f5, -/* 0x0567: main_not_ctx_chan */ - 0xb0420ef4, - 0x1bf402e4, - 0x3c87f12e, + 0x0699f094, + 0xf40089d0, +/* 0x050a: chsw_prev_no_next */ + 0x20f9310e, + 0xf40212b9, + 0x32f40132, + 0xfc21f502, + 0xf120fc07, + 0xb60b0017, + 0x12d00614, + 0x130ef400, +/* 0x0528: chsw_no_prev */ + 0xf41f23c8, + 0x31f40d0b, + 0x0232f401, + 0x07fc21f5, +/* 0x0538: chsw_done */ + 0x0b0c17f1, + 0xf00614b6, + 0x12d00127, + 0x5c87f100, 0x0684b608, 0x99f094bd, - 0x0089d007, - 0xf40132f4, - 0x21f50232, - 0x87f107fb, - 0x84b6085c, + 0x0089d004, + 0xff200ef5, +/* 0x0558: main_not_ctx_switch */ + 0xf401e4b0, + 0xf2b90d1b, + 0x9021f502, + 0x420ef407, +/* 0x0568: main_not_ctx_chan */ + 0xf402e4b0, + 0x87f12e1b, + 0x84b6083c, 0xf094bd06, 0x89d00799, - 0x110ef400, -/* 0x0598: main_not_ctx_save */ - 0xf010ef94, - 0x21f501f5, - 0x0ef502ec, -/* 0x05a6: main_done */ - 0x17f1fed1, - 0x14b60820, - 0xf024bd06, - 0x12d01f29, - 0xbe0ef500, -/* 0x05b9: ih */ - 0xfe80f9fe, - 0x80f90188, - 0xa0f990f9, - 0xd0f9b0f9, - 0xf0f9e0f9, - 0xc4800acf, - 0x0bf404ab, - 0x00b7f11d, - 0x08d7f019, - 0xcf40becf, - 0x21f400bf, - 0x00b0b704, - 0x01e7f004, -/* 0x05ef: ih_no_fifo */ - 0xe400bed0, - 0xf40100ab, - 0xd7f00d0b, - 0x01e7f108, - 0x0421f440, -/* 0x0600: ih_no_ctxsw */ - 0x0104b7f1, - 0xabffb0bd, - 0x0d0bf4b4, - 0x0c1ca7f1, - 0xd006a4b6, -/* 0x0616: ih_no_other */ - 0x0ad000ab, - 0xfcf0fc40, - 0xfcd0fce0, - 0xfca0fcb0, - 0xfe80fc90, - 0x80fc0088, - 0xf80032f4, -/* 0x0631: ctx_4170s */ - 0x70e7f101, + 0x0132f400, + 0xf50232f4, + 0xf107fc21, + 0xb6085c87, + 0x94bd0684, + 0xd00799f0, + 0x0ef40089, +/* 0x0599: main_not_ctx_save */ + 0x10ef9411, + 0xf501f5f0, + 0xf502ec21, +/* 0x05a7: main_done */ + 0xf1fed10e, + 0xb6082017, + 0x24bd0614, + 0xd01f29f0, + 0x0ef50012, +/* 0x05ba: ih */ + 0x80f9febe, + 0xf90188fe, + 0xf990f980, + 0xf9b0f9a0, + 0xf9e0f9d0, + 0x800acff0, + 0xf404abc4, + 0xb7f11d0b, + 0xd7f01900, + 0x40becf08, + 0xf400bfcf, + 0xb0b70421, + 0xe7f00400, + 0x00bed001, +/* 0x05f0: ih_no_fifo */ + 0x0100abe4, + 0xf00d0bf4, + 0xe7f108d7, + 0x21f44001, +/* 0x0601: ih_no_ctxsw */ + 0x04b7f104, + 0xffb0bd01, + 0x0bf4b4ab, + 0x1ca7f10d, + 0x06a4b60c, +/* 0x0617: ih_no_other */ + 0xd000abd0, + 0xf0fc400a, + 0xd0fce0fc, + 0xa0fcb0fc, + 0x80fc90fc, + 0xfc0088fe, + 0x0032f480, +/* 0x0632: ctx_4170s */ + 0xe7f101f8, + 0xe3f04170, + 0x10f5f040, + 0xf88d21f4, +/* 0x0641: ctx_4170w */ + 0x70e7f100, 0x40e3f041, - 0xf410f5f0, - 0x00f88d21, -/* 0x0640: ctx_4170w */ - 0x4170e7f1, - 0xf440e3f0, - 0xf4f06821, - 0xf31bf410, -/* 0x0652: ctx_redswitch */ - 0xe7f100f8, - 0xe4b60614, - 0x70f7f106, - 0x00efd002, -/* 0x0663: ctx_redswitch_delay */ - 0xb608f7f0, - 0x1bf401f2, - 0x70f7f1fd, - 0x00efd007, -/* 0x0672: ctx_86c */ - 0xe7f100f8, - 0xe4b6086c, - 0x00efd006, - 0x8a14e7f1, - 0xf440e3f0, - 0xe7f18d21, - 0xe3f0a86c, - 0x8d21f441, -/* 0x0692: ctx_load */ - 0x87f100f8, - 0x84b6083c, - 0xf094bd06, - 0x89d00599, - 0x0ca7f000, - 0xf1c921f4, - 0xb60a2417, - 0x10d00614, - 0x0037f100, - 0x0634b60b, - 0xf14032d0, - 0xb60a0c17, - 0x47f00614, - 0x0012d007, -/* 0x06cb: ctx_chan_wait_0 */ - 0xcf4014d0, - 0x44f04014, - 0xfa1bf41f, - 0xfe0032d0, - 0x2af0000b, - 0x0424b61f, - 0xf10220b6, + 0xf06821f4, + 0x1bf410f4, +/* 0x0653: ctx_redswitch */ + 0xf100f8f3, + 0xb60614e7, + 0xf7f106e4, + 0xefd00270, + 0x08f7f000, +/* 0x0664: ctx_redswitch_delay */ + 0xf401f2b6, + 0xf7f1fd1b, + 0xefd00770, +/* 0x0673: ctx_86c */ + 0xf100f800, + 0xb6086ce7, + 0xefd006e4, + 0x14e7f100, + 0x40e3f08a, + 0xf18d21f4, + 0xf0a86ce7, + 0x21f441e3, +/* 0x0693: ctx_load */ + 0xf100f88d, 0xb6083c87, 0x94bd0684, - 0xd00899f0, - 0x17f10089, - 0x14b60a04, - 0x0012d006, - 0x0a2017f1, + 0xd00599f0, + 0xa7f00089, + 0xc921f40c, + 0x0a2417f1, + 0xd00614b6, + 0x37f10010, + 0x34b60b00, + 0x4032d006, + 0x0a0c17f1, 0xf00614b6, - 0x23f10227, - 0x12d08000, - 0x1017f000, - 0x030027f1, - 0xfa0223f0, - 0x03f80512, - 0x085c87f1, + 0x12d00747, + 0x4014d000, +/* 0x06cc: ctx_chan_wait_0 */ + 0xf04014cf, + 0x1bf41f44, + 0x0032d0fa, + 0xf0000bfe, + 0x24b61f2a, + 0x0220b604, + 0x083c87f1, 0xbd0684b6, 0x0899f094, - 0x980089d0, - 0x14b6c101, - 0xc0029818, - 0xfd0825b6, - 0x01800512, - 0x3c87f116, + 0xf10089d0, + 0xb60a0417, + 0x12d00614, + 0x2017f100, + 0x0614b60a, + 0xf10227f0, + 0xd0800023, + 0x17f00012, + 0x0027f110, + 0x0223f002, + 0xf80512fa, + 0x5c87f103, 0x0684b608, 0x99f094bd, - 0x0089d009, - 0x0a0427f1, - 0xd00624b6, - 0x27f00021, - 0x2017f101, - 0x0614b60a, - 0xf10012d0, - 0xf0020017, - 0x01fa0613, - 0xf103f805, + 0x0089d008, + 0xb6810198, + 0x02981814, + 0x0825b680, + 0x800512fd, + 0x87f11601, + 0x84b6083c, + 0xf094bd06, + 0x89d00999, + 0x0427f100, + 0x0624b60a, + 0xf00021d0, + 0x17f10127, + 0x14b60a20, + 0x0012d006, + 0x010017f1, + 0xfa0613f0, + 0x03f80501, + 0x085c87f1, + 0xbd0684b6, + 0x0999f094, + 0xf10089d0, 0xb6085c87, 0x94bd0684, - 0xd00999f0, - 0x87f10089, - 0x84b6085c, - 0xf094bd06, - 0x89d00599, -/* 0x078f: ctx_chan */ - 0xf500f800, - 0xf0069221, - 0x21f40ca7, - 0x1017f1c9, - 0x0614b60a, - 0xd00527f0, -/* 0x07a6: ctx_chan_wait */ - 0x12cf0012, - 0x0522fd00, - 0xf8fa1bf4, -/* 0x07b1: ctx_mmio_exec */ - 0x81039800, - 0x0a0427f1, - 0xd00624b6, - 0x34bd0023, -/* 0x07c0: ctx_mmio_loop */ - 0xf4ff34c4, - 0x57f10f1b, - 0x53f00300, - 0x0535fa06, -/* 0x07d2: ctx_mmio_pull */ - 0x4e9803f8, - 0xc14f98c0, - 0xb68d21f4, - 0x12b60830, - 0xdf1bf401, -/* 0x07e4: ctx_mmio_done */ - 0xd0160398, - 0x00800023, - 0x0017f180, - 0x0613f002, - 0xf80601fa, -/* 0x07fb: ctx_xfer */ - 0xf100f803, - 0xb60c00f7, - 0xe7f006f4, - 0x80fed004, -/* 0x0808: ctx_xfer_idle */ - 0xf100fecf, - 0xf42000e4, - 0x11f4f91b, - 0x0d02f406, -/* 0x0818: ctx_xfer_pre */ - 0xf510f7f0, - 0xf4067221, -/* 0x0822: ctx_xfer_pre_load */ - 0xf7f01c11, - 0x3121f502, - 0x4021f506, - 0x5221f506, - 0xf5f4bd06, - 0xf5063121, -/* 0x083b: ctx_xfer_exec */ - 0x98069221, - 0x27f11601, - 0x24b60414, - 0x0020d006, - 0xa500e7f1, - 0xb941e3f0, - 0x21f4021f, - 0x04e0b68d, - 0xf001fcf0, - 0x24b6022c, - 0x05f2fd01, - 0xf18d21f4, - 0xf04afc17, - 0x27f00213, - 0x0012d00c, - 0x020721f5, - 0x47fc27f1, - 0xd00223f0, - 0x2cf00020, - 0x0320b601, - 0xf00012d0, - 0xa5f001ac, - 0x00b7f006, - 0x98140c98, - 0xe7f0150d, - 0x5c21f500, - 0x08a7f001, - 0x010321f5, - 0x020721f5, - 0xf02201f4, - 0x21f40ca7, - 0x1017f1c9, - 0x0614b60a, - 0xd00527f0, -/* 0x08c2: ctx_xfer_post_save_wait */ - 0x12cf0012, - 0x0522fd00, - 0xf4fa1bf4, -/* 0x08ce: ctx_xfer_post */ - 0xf7f02e02, - 0x3121f502, - 0xf5f4bd06, - 0xf5067221, - 0xf5022621, - 0xbd064021, - 0x3121f5f4, - 0x1011f406, - 0xfd800198, - 0x0bf40511, - 0xb121f507, -/* 0x08f9: ctx_xfer_no_post_mmio */ -/* 0x08f9: ctx_xfer_done */ - 0x0000f807, + 0xd00599f0, + 0x00f80089, +/* 0x0790: ctx_chan */ + 0x069321f5, + 0xf40ca7f0, + 0x17f1c921, + 0x14b60a10, + 0x0527f006, +/* 0x07a7: ctx_chan_wait */ + 0xcf0012d0, + 0x22fd0012, + 0xfa1bf405, +/* 0x07b2: ctx_mmio_exec */ + 0x039800f8, + 0x0427f141, + 0x0624b60a, + 0xbd0023d0, +/* 0x07c1: ctx_mmio_loop */ + 0xff34c434, + 0xf10f1bf4, + 0xf0020057, + 0x35fa0653, +/* 0x07d3: ctx_mmio_pull */ + 0x9803f805, + 0x4f98804e, + 0x8d21f481, + 0xb60830b6, + 0x1bf40112, +/* 0x07e5: ctx_mmio_done */ + 0x160398df, + 0x800023d0, + 0x17f14000, + 0x13f00100, + 0x0601fa06, + 0x00f803f8, +/* 0x07fc: ctx_xfer */ + 0x0c00f7f1, + 0xf006f4b6, + 0xfed004e7, +/* 0x0809: ctx_xfer_idle */ + 0x00fecf80, + 0x2000e4f1, + 0xf4f91bf4, + 0x02f40611, +/* 0x0819: ctx_xfer_pre */ + 0x10f7f00d, + 0x067321f5, +/* 0x0823: ctx_xfer_pre_load */ + 0xf01c11f4, + 0x21f502f7, + 0x21f50632, + 0x21f50641, + 0xf4bd0653, + 0x063221f5, + 0x069321f5, +/* 0x083c: ctx_xfer_exec */ + 0xf1160198, + 0xb6041427, + 0x20d00624, + 0x00e7f100, + 0x41e3f0a5, + 0xf4021fb9, + 0xe0b68d21, + 0x01fcf004, + 0xb6022cf0, + 0xf2fd0124, + 0x8d21f405, + 0x4afc17f1, + 0xf00213f0, + 0x12d00c27, + 0x0721f500, + 0xfc27f102, + 0x0223f047, + 0xf00020d0, + 0x20b6012c, + 0x0012d003, + 0xf001acf0, + 0xb7f006a5, + 0x140c9800, + 0xf0150d98, + 0x21f500e7, + 0xa7f0015c, + 0x0321f508, + 0x0721f501, + 0x2201f402, + 0xf40ca7f0, + 0x17f1c921, + 0x14b60a10, + 0x0527f006, +/* 0x08c3: ctx_xfer_post_save_wait */ + 0xcf0012d0, + 0x22fd0012, + 0xfa1bf405, +/* 0x08cf: ctx_xfer_post */ + 0xf02e02f4, + 0x21f502f7, + 0xf4bd0632, + 0x067321f5, + 0x022621f5, + 0x064121f5, + 0x21f5f4bd, + 0x11f40632, + 0x40019810, + 0xf40511fd, + 0x21f5070b, +/* 0x08fa: ctx_xfer_no_post_mmio */ +/* 0x08fa: ctx_xfer_done */ + 0x00f807b2, 0x00000000, }; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c b/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c index c80132c8f01e..9a3c2a31a533 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c @@ -517,6 +517,13 @@ nve0_graph_init_unk40xx(struct nvc0_graph_priv *priv) { nv_wr32(priv, 0x40415c, 0x00000000); nv_wr32(priv, 0x404170, 0x00000000); + switch (nv_device(priv)->chipset) { + case 0xf0: + nv_wr32(priv, 0x4041b4, 0x00000000); + break; + default: + break; + } } static void @@ -551,7 +558,14 @@ nve0_graph_init_unk58xx(struct nvc0_graph_priv *priv) { nv_wr32(priv, 0x405844, 0x00ffffff); nv_wr32(priv, 0x405850, 0x00000000); - nv_wr32(priv, 0x405900, 0x0000ff34); + switch (nv_device(priv)->chipset) { + case 0xf0: + nv_wr32(priv, 0x405900, 0x0000ff00); + break; + default: + nv_wr32(priv, 0x405900, 0x0000ff34); + break; + } nv_wr32(priv, 0x405908, 0x00000000); nv_wr32(priv, 0x405928, 0x00000000); nv_wr32(priv, 0x40592c, 0x00000000); @@ -567,11 +581,26 @@ static void nve0_graph_init_unk70xx(struct nvc0_graph_priv *priv) { nv_wr32(priv, 0x407010, 0x00000000); + switch (nv_device(priv)->chipset) { + case 0xf0: + nv_wr32(priv, 0x407040, 0x80440424); + nv_wr32(priv, 0x407048, 0x0000000a); + break; + default: + break; + } } static void nve0_graph_init_unk5bxx(struct nvc0_graph_priv *priv) { + switch (nv_device(priv)->chipset) { + case 0xf0: + nv_wr32(priv, 0x505b44, 0x00000000); + break; + default: + break; + } nv_wr32(priv, 0x405b50, 0x00000000); } @@ -610,11 +639,25 @@ nve0_graph_init_gpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x418d00, 0x00000000); nv_wr32(priv, 0x418d28, 0x00000000); nv_wr32(priv, 0x418d2c, 0x00000000); - nv_wr32(priv, 0x418f00, 0x00000000); + switch (nv_device(priv)->chipset) { + case 0xf0: + nv_wr32(priv, 0x418f00, 0x00000400); + break; + default: + nv_wr32(priv, 0x418f00, 0x00000000); + break; + } nv_wr32(priv, 0x418f08, 0x00000000); nv_wr32(priv, 0x418f20, 0x00000000); nv_wr32(priv, 0x418f24, 0x00000000); - nv_wr32(priv, 0x418e00, 0x00000060); + switch (nv_device(priv)->chipset) { + case 0xf0: + nv_wr32(priv, 0x418e00, 0x00000000); + break; + default: + nv_wr32(priv, 0x418e00, 0x00000060); + break; + } nv_wr32(priv, 0x418e08, 0x00000000); nv_wr32(priv, 0x418e1c, 0x00000000); nv_wr32(priv, 0x418e20, 0x00000000); @@ -630,9 +673,24 @@ nve0_graph_init_tpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419ab0, 0x00000000); nv_wr32(priv, 0x419ac8, 0x00000000); nv_wr32(priv, 0x419ab8, 0x000000e7); + switch (nv_device(priv)->chipset) { + case 0xf0: + nv_wr32(priv, 0x419aec, 0x00000000); + break; + default: + break; + } nv_wr32(priv, 0x419abc, 0x00000000); nv_wr32(priv, 0x419ac0, 0x00000000); nv_wr32(priv, 0x419ab4, 0x00000000); + switch (nv_device(priv)->chipset) { + case 0xf0: + nv_wr32(priv, 0x419aa8, 0x00000000); + nv_wr32(priv, 0x419aac, 0x00000000); + break; + default: + break; + } nv_wr32(priv, 0x41980c, 0x00000010); nv_wr32(priv, 0x419844, 0x00000000); nv_wr32(priv, 0x419850, 0x00000004); @@ -644,23 +702,59 @@ nve0_graph_init_tpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419cb4, 0x00000000); nv_wr32(priv, 0x419cb8, 0x00b08bea); nv_wr32(priv, 0x419c84, 0x00010384); - nv_wr32(priv, 0x419cbc, 0x28137646); + switch (nv_device(priv)->chipset) { + case 0xf0: + nv_wr32(priv, 0x419cbc, 0x281b3646); + break; + default: + nv_wr32(priv, 0x419cbc, 0x28137646); + break; + } nv_wr32(priv, 0x419cc0, 0x00000000); nv_wr32(priv, 0x419cc4, 0x00000000); - nv_wr32(priv, 0x419c80, 0x00020232); - nv_wr32(priv, 0x419c0c, 0x00000000); - nv_wr32(priv, 0x419e00, 0x00000000); + switch (nv_device(priv)->chipset) { + case 0xf0: + nv_wr32(priv, 0x419c80, 0x00020230); + nv_wr32(priv, 0x419ccc, 0x00000000); + nv_wr32(priv, 0x419cd0, 0x00000000); + nv_wr32(priv, 0x419c0c, 0x00000000); + nv_wr32(priv, 0x419e00, 0x00000080); + break; + default: + nv_wr32(priv, 0x419c80, 0x00020232); + nv_wr32(priv, 0x419c0c, 0x00000000); + nv_wr32(priv, 0x419e00, 0x00000000); + break; + } nv_wr32(priv, 0x419ea0, 0x00000000); nv_wr32(priv, 0x419ee4, 0x00000000); nv_wr32(priv, 0x419ea4, 0x00000100); nv_wr32(priv, 0x419ea8, 0x00000000); nv_wr32(priv, 0x419eb4, 0x00000000); - nv_wr32(priv, 0x419eb8, 0x00000000); + switch (nv_device(priv)->chipset) { + case 0xf0: + break; + default: + nv_wr32(priv, 0x419eb8, 0x00000000); + break; + } nv_wr32(priv, 0x419ebc, 0x00000000); nv_wr32(priv, 0x419ec0, 0x00000000); nv_wr32(priv, 0x419edc, 0x00000000); nv_wr32(priv, 0x419f00, 0x00000000); - nv_wr32(priv, 0x419f74, 0x00000555); + switch (nv_device(priv)->chipset) { + case 0xf0: + nv_wr32(priv, 0x419ed0, 0x00003234); + nv_wr32(priv, 0x419f74, 0x00015555); + nv_wr32(priv, 0x419f80, 0x00000000); + nv_wr32(priv, 0x419f84, 0x00000000); + nv_wr32(priv, 0x419f88, 0x00000000); + nv_wr32(priv, 0x419f8c, 0x00000000); + break; + default: + nv_wr32(priv, 0x419f74, 0x00000555); + break; + } } static void @@ -726,6 +820,7 @@ nve0_graph_init_units(struct nvc0_graph_priv *priv) switch (nv_device(priv)->chipset) { case 0xe7: case 0xe6: + case 0xf0: nv_wr32(priv, 0x407020, 0x40000000); break; default: @@ -971,6 +1066,7 @@ nve0_graph_init(struct nouveau_object *object) switch (nv_device(priv)->chipset) { case 0xe7: case 0xe6: + case 0xf0: nve0_graph_init_unk40xx(priv); nve0_graph_init_unk44xx(priv); nve0_graph_init_unk78xx(priv); -- cgit v1.2.3 From a8004a9edd8c6ee86c3e263c9082ddf64797a667 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 6 May 2013 16:44:17 +1000 Subject: drm/nvc0-/gr: bump maximum gpc/tpc limits Needed for GK110, separate commit to catch any unexpected breaks to other parts of the code. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h index c870dad0f670..af7212d96f3f 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h @@ -38,8 +38,8 @@ #include #include -#define GPC_MAX 4 -#define TPC_MAX 32 +#define GPC_MAX 32 +#define TPC_MAX (GPC_MAX * 8) #define ROP_BCAST(r) (0x408800 + (r)) #define ROP_UNIT(u, r) (0x410000 + (u) * 0x400 + (r)) @@ -124,6 +124,8 @@ nvc0_graph_class(void *obj) case 0xe7: case 0xe6: return 0xa097; + case 0xf0: + return 0xa197; default: return 0; } -- cgit v1.2.3 From 1dd44acfab048893b7f8028d63fe82df483bc5a8 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 7 May 2013 14:30:52 +1000 Subject: drm/nve4/gr: update initial register/context values Signed-off-by: Ben Skeggs --- .../gpu/drm/nouveau/core/engine/graph/ctxnve0.c | 74 +--------------------- .../drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc | 31 ++------- .../nouveau/core/engine/graph/fuc/gpcnve0.fuc.h | 32 ++-------- drivers/gpu/drm/nouveau/core/engine/graph/nve0.c | 51 +++++---------- 4 files changed, 31 insertions(+), 157 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve0.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve0.c index 574a1deffcb9..848570b4c519 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve0.c @@ -749,38 +749,6 @@ nve0_grctx_generate_icmd(struct nvc0_graph_priv *priv) nv_icmd(priv, 0x000841, 0x08000080); nv_icmd(priv, 0x000842, 0x00400008); nv_icmd(priv, 0x000843, 0x08000080); - switch (nv_device(priv)->chipset) { - case 0xe7: - case 0xe6: - break; - default: - nv_icmd(priv, 0x000818, 0x00000000); - nv_icmd(priv, 0x000819, 0x00000000); - nv_icmd(priv, 0x00081a, 0x00000000); - nv_icmd(priv, 0x00081b, 0x00000000); - nv_icmd(priv, 0x00081c, 0x00000000); - nv_icmd(priv, 0x00081d, 0x00000000); - nv_icmd(priv, 0x00081e, 0x00000000); - nv_icmd(priv, 0x00081f, 0x00000000); - nv_icmd(priv, 0x000848, 0x00000000); - nv_icmd(priv, 0x000849, 0x00000000); - nv_icmd(priv, 0x00084a, 0x00000000); - nv_icmd(priv, 0x00084b, 0x00000000); - nv_icmd(priv, 0x00084c, 0x00000000); - nv_icmd(priv, 0x00084d, 0x00000000); - nv_icmd(priv, 0x00084e, 0x00000000); - nv_icmd(priv, 0x00084f, 0x00000000); - nv_icmd(priv, 0x000850, 0x00000000); - nv_icmd(priv, 0x000851, 0x00000000); - nv_icmd(priv, 0x000852, 0x00000000); - nv_icmd(priv, 0x000853, 0x00000000); - nv_icmd(priv, 0x000854, 0x00000000); - nv_icmd(priv, 0x000855, 0x00000000); - nv_icmd(priv, 0x000856, 0x00000000); - nv_icmd(priv, 0x000857, 0x00000000); - nv_icmd(priv, 0x000738, 0x00000000); - break; - } nv_icmd(priv, 0x0006aa, 0x00000001); nv_icmd(priv, 0x0006ab, 0x00000002); nv_icmd(priv, 0x0006ac, 0x00000080); @@ -869,38 +837,6 @@ nve0_grctx_generate_icmd(struct nvc0_graph_priv *priv) nv_icmd(priv, 0x000813, 0x00000006); nv_icmd(priv, 0x000814, 0x00000008); nv_icmd(priv, 0x000957, 0x00000003); - switch (nv_device(priv)->chipset) { - case 0xe7: - case 0xe6: - break; - default: - nv_icmd(priv, 0x000818, 0x00000000); - nv_icmd(priv, 0x000819, 0x00000000); - nv_icmd(priv, 0x00081a, 0x00000000); - nv_icmd(priv, 0x00081b, 0x00000000); - nv_icmd(priv, 0x00081c, 0x00000000); - nv_icmd(priv, 0x00081d, 0x00000000); - nv_icmd(priv, 0x00081e, 0x00000000); - nv_icmd(priv, 0x00081f, 0x00000000); - nv_icmd(priv, 0x000848, 0x00000000); - nv_icmd(priv, 0x000849, 0x00000000); - nv_icmd(priv, 0x00084a, 0x00000000); - nv_icmd(priv, 0x00084b, 0x00000000); - nv_icmd(priv, 0x00084c, 0x00000000); - nv_icmd(priv, 0x00084d, 0x00000000); - nv_icmd(priv, 0x00084e, 0x00000000); - nv_icmd(priv, 0x00084f, 0x00000000); - nv_icmd(priv, 0x000850, 0x00000000); - nv_icmd(priv, 0x000851, 0x00000000); - nv_icmd(priv, 0x000852, 0x00000000); - nv_icmd(priv, 0x000853, 0x00000000); - nv_icmd(priv, 0x000854, 0x00000000); - nv_icmd(priv, 0x000855, 0x00000000); - nv_icmd(priv, 0x000856, 0x00000000); - nv_icmd(priv, 0x000857, 0x00000000); - nv_icmd(priv, 0x000738, 0x00000000); - break; - } nv_icmd(priv, 0x000b07, 0x00000002); nv_icmd(priv, 0x000b08, 0x00000100); nv_icmd(priv, 0x000b09, 0x00000100); @@ -2180,6 +2116,7 @@ nve0_grctx_generate_902d(struct nvc0_graph_priv *priv) case 0xe6: nv_mthd(priv, 0x902d, 0x3410, 0x80002006); break; + case 0xe4: case 0xe7: default: nv_mthd(priv, 0x902d, 0x3410, 0x00000000); @@ -2716,6 +2653,7 @@ nve0_graph_generate_tpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419e94, 0x0); nv_wr32(priv, 0x419e98, 0x0); switch (nv_device(priv)->chipset) { + case 0xe4: case 0xe7: case 0xe6: nv_wr32(priv, 0x419eac, 0x1f8f); @@ -2726,10 +2664,6 @@ nve0_graph_generate_tpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419eb0, 0xdb00da0); nv_wr32(priv, 0x419eb8, 0x0); break; - default: - nv_wr32(priv, 0x419eac, 0x1fcf); - nv_wr32(priv, 0x419eb0, 0xd3f); - break; } nv_wr32(priv, 0x419ec8, 0x1304f); nv_wr32(priv, 0x419f30, 0x0); @@ -2749,6 +2683,7 @@ nve0_graph_generate_tpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419f4c, 0x0); nv_wr32(priv, 0x419f58, 0x0); switch (nv_device(priv)->chipset) { + case 0xe4: case 0xe7: case 0xe6: nv_wr32(priv, 0x419f70, 0x0); @@ -2760,9 +2695,6 @@ nve0_graph_generate_tpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419f78, 0xeb); nv_wr32(priv, 0x419f7c, 0x404); break; - default: - nv_wr32(priv, 0x419f78, 0xb); - break; } } diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc index e906ca68674d..ccaeb50aa76b 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc @@ -55,13 +55,13 @@ chipsets: .b8 0xe7 0 0 0 .b16 #nve4_gpc_mmio_head .b16 #nve4_gpc_mmio_tail -.b16 #nve6_tpc_mmio_head -.b16 #nve6_tpc_mmio_tail +.b16 #nve4_tpc_mmio_head +.b16 #nve4_tpc_mmio_tail .b8 0xe6 0 0 0 .b16 #nve4_gpc_mmio_head .b16 #nve4_gpc_mmio_tail -.b16 #nve6_tpc_mmio_head -.b16 #nve6_tpc_mmio_tail +.b16 #nve4_tpc_mmio_head +.b16 #nve4_tpc_mmio_tail .b8 0xf0 0 0 0 .b16 #nvf0_gpc_mmio_head .b16 #nvf0_gpc_mmio_tail @@ -156,30 +156,9 @@ mmctx_data(0x0006ac, 2) mmctx_data(0x0006c8, 1) mmctx_data(0x000730, 8) mmctx_data(0x000758, 1) -mmctx_data(0x000778, 1) -nve4_tpc_mmio_tail: - -nve6_tpc_mmio_head: -mmctx_data(0x000048, 1) -mmctx_data(0x000064, 1) -mmctx_data(0x000088, 1) -mmctx_data(0x000200, 6) -mmctx_data(0x00021c, 2) -mmctx_data(0x000230, 1) -mmctx_data(0x0002c4, 1) -mmctx_data(0x000400, 3) -mmctx_data(0x000420, 3) -mmctx_data(0x0004e8, 1) -mmctx_data(0x0004f4, 1) -mmctx_data(0x000604, 4) -mmctx_data(0x000644, 22) -mmctx_data(0x0006ac, 2) -mmctx_data(0x0006c8, 1) -mmctx_data(0x000730, 8) -mmctx_data(0x000758, 1) mmctx_data(0x000770, 1) mmctx_data(0x000778, 2) -nve6_tpc_mmio_tail: +nve4_tpc_mmio_tail: nvf0_tpc_mmio_head: mmctx_data(0x000048, 1) diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h index 592433954d41..419bd5da1e00 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h @@ -35,16 +35,16 @@ uint32_t nve0_grgpc_data[] = { /* 0x0064: chipsets */ 0x000000e4, 0x011c0098, - 0x01d4018c, + 0x01d8018c, 0x000000e7, 0x011c0098, - 0x022001d4, + 0x01d8018c, 0x000000e6, 0x011c0098, - 0x022001d4, + 0x01d8018c, 0x000000f0, 0x018c011c, - 0x02700220, + 0x022801d8, 0x00000000, /* 0x0098: nve4_gpc_mmio_head */ 0x00000380, @@ -112,26 +112,6 @@ uint32_t nve0_grgpc_data[] = { 0x00001014, /* 0x018c: nvf0_gpc_mmio_tail */ /* 0x018c: nve4_tpc_mmio_head */ - 0x00000048, - 0x00000064, - 0x00000088, - 0x14000200, - 0x0400021c, - 0x00000230, - 0x000002c4, - 0x08000400, - 0x08000420, - 0x000004e8, - 0x000004f4, - 0x0c000604, - 0x54000644, - 0x040006ac, - 0x000006c8, - 0x1c000730, - 0x00000758, - 0x00000778, -/* 0x01d4: nve4_tpc_mmio_tail */ -/* 0x01d4: nve6_tpc_mmio_head */ 0x00000048, 0x00000064, 0x00000088, @@ -151,8 +131,8 @@ uint32_t nve0_grgpc_data[] = { 0x00000758, 0x00000770, 0x04000778, -/* 0x0220: nve6_tpc_mmio_tail */ -/* 0x0220: nvf0_tpc_mmio_head */ +/* 0x01d8: nve4_tpc_mmio_tail */ +/* 0x01d8: nvf0_tpc_mmio_head */ 0x00000048, 0x00000064, 0x00000088, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c b/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c index 9a3c2a31a533..f4685bb66eb8 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c @@ -475,6 +475,7 @@ nve0_graph_ctor(struct nouveau_object *parent, struct nouveau_object *engine, case 0xe6: priv->magic_not_rop_nr = 1; break; + case 0xf0: default: break; } @@ -803,11 +804,12 @@ nve0_graph_init_units(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x409ffc, 0x00000000); nv_wr32(priv, 0x409c14, 0x00003e3e); switch (nv_device(priv)->chipset) { + case 0xe4: case 0xe7: case 0xe6: nv_wr32(priv, 0x409c24, 0x000f0001); break; - default: + case 0xf0: nv_wr32(priv, 0x409c24, 0x000f0000); break; } @@ -817,16 +819,7 @@ nve0_graph_init_units(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x408030, 0xc0000000); nv_wr32(priv, 0x404490, 0xc0000000); nv_wr32(priv, 0x406018, 0xc0000000); - switch (nv_device(priv)->chipset) { - case 0xe7: - case 0xe6: - case 0xf0: - nv_wr32(priv, 0x407020, 0x40000000); - break; - default: - nv_wr32(priv, 0x407020, 0xc0000000); - break; - } + nv_wr32(priv, 0x407020, 0x40000000); nv_wr32(priv, 0x405840, 0xc0000000); nv_wr32(priv, 0x405844, 0x00ffffff); @@ -1062,29 +1055,19 @@ nve0_graph_init(struct nouveau_object *object) nve0_graph_init_obj418880(priv); nve0_graph_init_regs(priv); - - switch (nv_device(priv)->chipset) { - case 0xe7: - case 0xe6: - case 0xf0: - nve0_graph_init_unk40xx(priv); - nve0_graph_init_unk44xx(priv); - nve0_graph_init_unk78xx(priv); - nve0_graph_init_unk60xx(priv); - nve0_graph_init_unk64xx(priv); - nve0_graph_init_unk58xx(priv); - nve0_graph_init_unk80xx(priv); - nve0_graph_init_unk70xx(priv); - nve0_graph_init_unk5bxx(priv); - nve0_graph_init_gpc(priv); - nve0_graph_init_tpc(priv); - nve0_graph_init_tpcunk(priv); - nve0_graph_init_unk88xx(priv); - break; - default: - break; - } - + nve0_graph_init_unk40xx(priv); + nve0_graph_init_unk44xx(priv); + nve0_graph_init_unk78xx(priv); + nve0_graph_init_unk60xx(priv); + nve0_graph_init_unk64xx(priv); + nve0_graph_init_unk58xx(priv); + nve0_graph_init_unk80xx(priv); + nve0_graph_init_unk70xx(priv); + nve0_graph_init_unk5bxx(priv); + nve0_graph_init_gpc(priv); + nve0_graph_init_tpc(priv); + nve0_graph_init_tpcunk(priv); + nve0_graph_init_unk88xx(priv); nve0_graph_init_gpc_0(priv); nv_wr32(priv, 0x400500, 0x00010001); -- cgit v1.2.3 From 37c3afd07c73efca89d89e5dfccdcdd0b1d539f8 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 13 May 2013 08:33:52 +1000 Subject: drm/nvd9/gr: update initial register/context values Signed-off-by: Ben Skeggs --- .../gpu/drm/nouveau/core/engine/graph/ctxnvc0.c | 354 +++++++++++++++++---- .../drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc | 2 +- .../nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h | 2 +- .../drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc | 2 +- .../nouveau/core/engine/graph/fuc/hubnvc0.fuc.h | 2 +- drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c | 194 ++++++++++- 6 files changed, 482 insertions(+), 74 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c index 4cc6269d4077..31a84162632b 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c @@ -1323,8 +1323,16 @@ nvc0_grctx_generate_9097(struct nvc0_graph_priv *priv) nv_mthd(priv, 0x9097, 0x1450, 0x00300008); nv_mthd(priv, 0x9097, 0x1454, 0x04000080); nv_mthd(priv, 0x9097, 0x0214, 0x00000000); - /* in trace, right after 0x90c0, not here */ - nv_mthd(priv, 0x9097, 0x3410, 0x80002006); + + switch (nv_device(priv)->chipset) { + case 0xd9: + case 0xd7: + break; + default: + /* in trace, right after 0x90c0, not here */ + nv_mthd(priv, 0x9097, 0x3410, 0x80002006); + break; + } } static void @@ -1417,6 +1425,8 @@ nvc0_grctx_generate_90c0(struct nvc0_graph_priv *priv) for (i = 0; nv_device(priv)->chipset >= 0xd0 && i < 4; i++) { nv_mthd(priv, 0x90c0, 0x2710 + (i * 0x40), 0x00014000); nv_mthd(priv, 0x90c0, 0x2730 + (i * 0x40), 0x00014000); + } + for (i = 0; nv_device(priv)->chipset >= 0xd0 && i < 4; i++) { nv_mthd(priv, 0x90c0, 0x2714 + (i * 0x40), 0x00000040); nv_mthd(priv, 0x90c0, 0x2734 + (i * 0x40), 0x00000040); } @@ -1456,7 +1466,14 @@ nvc0_grctx_generate_dispatch(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x404020, 0x00000000); nv_wr32(priv, 0x404024, 0x00000000); nv_wr32(priv, 0x404028, 0x00000000); - nv_wr32(priv, 0x40402c, 0x00000000); + switch (nv_device(priv)->chipset) { + case 0xd9: + case 0xd7: + nv_wr32(priv, 0x40402c, 0x00000000); + break; + default: + break; + } nv_wr32(priv, 0x404044, 0x00000000); nv_wr32(priv, 0x404094, 0x00000000); nv_wr32(priv, 0x404098, 0x00000000); @@ -1472,6 +1489,14 @@ nvc0_grctx_generate_dispatch(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x4040c0, 0x00000000); nv_wr32(priv, 0x4040c4, 0x00000000); nv_wr32(priv, 0x4040c8, 0xf0000087); + switch (nv_device(priv)->chipset) { + case 0xd9: + case 0xd7: + nv_wr32(priv, 0x4040d0, 0x00000000); + break; + default: + break; + } nv_wr32(priv, 0x4040d4, 0x00000000); nv_wr32(priv, 0x4040d8, 0x00000000); nv_wr32(priv, 0x4040dc, 0x00000000); @@ -1487,7 +1512,14 @@ nvc0_grctx_generate_dispatch(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x404158, 0x00000200); nv_wr32(priv, 0x404164, 0x00000055); nv_wr32(priv, 0x404168, 0x00000000); - nv_wr32(priv, 0x404174, 0x00000000); + switch (nv_device(priv)->chipset) { + case 0xd9: + case 0xd7: + break; + default: + nv_wr32(priv, 0x404174, 0x00000000); + break; + } nv_wr32(priv, 0x404178, 0x00000000); nv_wr32(priv, 0x40417c, 0x00000000); for (i = 0; i < 8; i++) @@ -1657,12 +1689,23 @@ nvc0_grctx_generate_unk64xx(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x4064ac, 0x00003fff); nv_wr32(priv, 0x4064b4, 0x00000000); nv_wr32(priv, 0x4064b8, 0x00000000); - if (nv_device(priv)->chipset >= 0xd0) + switch (nv_device(priv)->chipset) { + case 0xd9: + case 0xd7: nv_wr32(priv, 0x4064bc, 0x00000000); - if (nv_device(priv)->chipset == 0xc1 || - nv_device(priv)->chipset >= 0xd0) { + break; + default: + break; + } + switch (nv_device(priv)->chipset) { + case 0xc1: + case 0xd9: + case 0xd7: nv_wr32(priv, 0x4064c0, 0x80140078); nv_wr32(priv, 0x4064c4, 0x0086ffff); + break; + default: + break; } } @@ -1695,46 +1738,63 @@ nvc0_grctx_generate_ccache(struct nvc0_graph_priv *priv) static void nvc0_grctx_generate_rop(struct nvc0_graph_priv *priv) { - int chipset = nv_device(priv)->chipset; - /* ROPC_BROADCAST */ nv_wr32(priv, 0x408800, 0x02802a3c); nv_wr32(priv, 0x408804, 0x00000040); - if (chipset >= 0xd0) { - nv_wr32(priv, 0x408808, 0x1043e005); - nv_wr32(priv, 0x408900, 0x3080b801); - nv_wr32(priv, 0x408904, 0x1043e005); - nv_wr32(priv, 0x408908, 0x00c8102f); - } else - if (chipset == 0xc1) { + switch (nv_device(priv)->chipset) { + case 0xc1: nv_wr32(priv, 0x408808, 0x1003e005); nv_wr32(priv, 0x408900, 0x3080b801); nv_wr32(priv, 0x408904, 0x62000001); nv_wr32(priv, 0x408908, 0x00c80929); - } else { + nv_wr32(priv, 0x40890c, 0x00000000); + break; + case 0xd9: + case 0xd7: + nv_wr32(priv, 0x408808, 0x1043e005); + nv_wr32(priv, 0x408900, 0x3080b801); + nv_wr32(priv, 0x408904, 0x1043e005); + nv_wr32(priv, 0x408908, 0x00c8102f); + break; + default: nv_wr32(priv, 0x408808, 0x0003e00d); nv_wr32(priv, 0x408900, 0x3080b801); nv_wr32(priv, 0x408904, 0x02000001); nv_wr32(priv, 0x408908, 0x00c80929); + nv_wr32(priv, 0x40890c, 0x00000000); + break; } - nv_wr32(priv, 0x40890c, 0x00000000); nv_wr32(priv, 0x408980, 0x0000011d); } static void nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) { - int chipset = nv_device(priv)->chipset; int i; /* GPC_BROADCAST */ nv_wr32(priv, 0x418380, 0x00000016); nv_wr32(priv, 0x418400, 0x38004e00); nv_wr32(priv, 0x418404, 0x71e0ffff); - nv_wr32(priv, 0x418408, 0x00000000); + switch (nv_device(priv)->chipset) { + case 0xd9: + case 0xd7: + break; + default: + nv_wr32(priv, 0x418408, 0x00000000); + break; + } nv_wr32(priv, 0x41840c, 0x00001008); nv_wr32(priv, 0x418410, 0x0fff0fff); - nv_wr32(priv, 0x418414, chipset < 0xd0 ? 0x00200fff : 0x02200fff); + switch (nv_device(priv)->chipset) { + case 0xd9: + case 0xd7: + nv_wr32(priv, 0x418414, 0x02200fff); + break; + default: + nv_wr32(priv, 0x418414, 0x00200fff); + break; + } nv_wr32(priv, 0x418450, 0x00000000); nv_wr32(priv, 0x418454, 0x00000000); nv_wr32(priv, 0x418458, 0x00000000); @@ -1749,17 +1809,39 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x418700, 0x00000002); nv_wr32(priv, 0x418704, 0x00000080); nv_wr32(priv, 0x418708, 0x00000000); - nv_wr32(priv, 0x41870c, chipset < 0xd0 ? 0x07c80000 : 0x00000000); + switch (nv_device(priv)->chipset) { + case 0xd9: + case 0xd7: + nv_wr32(priv, 0x41870c, 0x00000000); + break; + default: + nv_wr32(priv, 0x41870c, 0x07c80000); + break; + } nv_wr32(priv, 0x418710, 0x00000000); - nv_wr32(priv, 0x418800, chipset < 0xd0 ? 0x0006860a : 0x7006860a); + switch (nv_device(priv)->chipset) { + case 0xd9: + case 0xd7: + nv_wr32(priv, 0x418800, 0x7006860a); + break; + default: + nv_wr32(priv, 0x418800, 0x0006860a); + break; + } nv_wr32(priv, 0x418808, 0x00000000); nv_wr32(priv, 0x41880c, 0x00000000); nv_wr32(priv, 0x418810, 0x00000000); nv_wr32(priv, 0x418828, 0x00008442); - if (chipset == 0xc1 || chipset >= 0xd0) + switch (nv_device(priv)->chipset) { + case 0xc1: + case 0xd9: + case 0xd7: nv_wr32(priv, 0x418830, 0x10000001); - else + break; + default: nv_wr32(priv, 0x418830, 0x00000001); + break; + } nv_wr32(priv, 0x4188d8, 0x00000008); nv_wr32(priv, 0x4188e0, 0x01000000); nv_wr32(priv, 0x4188e8, 0x00000000); @@ -1767,12 +1849,18 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x4188f0, 0x00000000); nv_wr32(priv, 0x4188f4, 0x00000000); nv_wr32(priv, 0x4188f8, 0x00000000); - if (chipset >= 0xd0) - nv_wr32(priv, 0x4188fc, 0x20100008); - else if (chipset == 0xc1) + switch (nv_device(priv)->chipset) { + case 0xc1: nv_wr32(priv, 0x4188fc, 0x00100018); - else + break; + case 0xd9: + case 0xd7: + nv_wr32(priv, 0x4188fc, 0x20100008); + break; + default: nv_wr32(priv, 0x4188fc, 0x00100000); + break; + } nv_wr32(priv, 0x41891c, 0x00ff00ff); nv_wr32(priv, 0x418924, 0x00000000); nv_wr32(priv, 0x418928, 0x00ffff00); @@ -1786,7 +1874,15 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x418a14 + (i * 0x20), 0x00000000); nv_wr32(priv, 0x418a18 + (i * 0x20), 0x00000000); } - nv_wr32(priv, 0x418b00, chipset < 0xd0 ? 0x00000000 : 0x00000006); + switch (nv_device(priv)->chipset) { + case 0xd9: + case 0xd7: + nv_wr32(priv, 0x418b00, 0x00000006); + break; + default: + nv_wr32(priv, 0x418b00, 0x00000000); + break; + } nv_wr32(priv, 0x418b08, 0x0a418820); nv_wr32(priv, 0x418b0c, 0x062080e6); nv_wr32(priv, 0x418b10, 0x020398a4); @@ -1803,8 +1899,15 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x418c24, 0x00000000); nv_wr32(priv, 0x418c28, 0x00000000); nv_wr32(priv, 0x418c2c, 0x00000000); - if (chipset == 0xc1 || chipset >= 0xd0) + switch (nv_device(priv)->chipset) { + case 0xc1: + case 0xd9: + case 0xd7: nv_wr32(priv, 0x418c6c, 0x00000001); + break; + default: + break; + } nv_wr32(priv, 0x418c80, 0x20200004); nv_wr32(priv, 0x418c8c, 0x00000001); nv_wr32(priv, 0x419000, 0x00000780); @@ -1816,16 +1919,20 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) static void nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) { - int chipset = nv_device(priv)->chipset; - /* GPC_BROADCAST.TP_BROADCAST */ nv_wr32(priv, 0x419818, 0x00000000); nv_wr32(priv, 0x41983c, 0x00038bc7); nv_wr32(priv, 0x419848, 0x00000000); - if (chipset == 0xc1 || chipset >= 0xd0) + switch (nv_device(priv)->chipset) { + case 0xc1: + case 0xd9: + case 0xd7: nv_wr32(priv, 0x419864, 0x00000129); - else + break; + default: nv_wr32(priv, 0x419864, 0x0000012a); + break; + } nv_wr32(priv, 0x419888, 0x00000000); nv_wr32(priv, 0x419a00, 0x000001f0); nv_wr32(priv, 0x419a04, 0x00000001); @@ -1835,10 +1942,18 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419a14, 0x00000200); nv_wr32(priv, 0x419a1c, 0x00000000); nv_wr32(priv, 0x419a20, 0x00000800); - if (chipset >= 0xd0) + switch (nv_device(priv)->chipset) { + case 0xc0: + case 0xc8: + break; + case 0xd9: + case 0xd7: nv_wr32(priv, 0x00419ac4, 0x0017f440); - else if (chipset != 0xc0 && chipset != 0xc8) + break; + default: nv_wr32(priv, 0x00419ac4, 0x0007f440); + break; + } nv_wr32(priv, 0x419b00, 0x0a418820); nv_wr32(priv, 0x419b04, 0x062080e6); nv_wr32(priv, 0x419b08, 0x020398a4); @@ -1846,34 +1961,66 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419b10, 0x0a418820); nv_wr32(priv, 0x419b14, 0x000000e6); nv_wr32(priv, 0x419bd0, 0x00900103); - if (chipset == 0xc1 || chipset >= 0xd0) + switch (nv_device(priv)->chipset) { + case 0xc1: + case 0xd9: + case 0xd7: nv_wr32(priv, 0x419be0, 0x00400001); - else + break; + default: nv_wr32(priv, 0x419be0, 0x00000001); + break; + } nv_wr32(priv, 0x419be4, 0x00000000); - nv_wr32(priv, 0x419c00, chipset < 0xd0 ? 0x00000002 : 0x0000000a); + switch (nv_device(priv)->chipset) { + case 0xd9: + case 0xd7: + nv_wr32(priv, 0x419c00, 0x0000000a); + break; + default: + nv_wr32(priv, 0x419c00, 0x00000002); + break; + } nv_wr32(priv, 0x419c04, 0x00000006); nv_wr32(priv, 0x419c08, 0x00000002); nv_wr32(priv, 0x419c20, 0x00000000); - if (nv_device(priv)->chipset >= 0xd0) { + switch (nv_device(priv)->chipset) { + case 0xce: + case 0xcf: + nv_wr32(priv, 0x419cb0, 0x00020048); + break; + case 0xd9: + case 0xd7: nv_wr32(priv, 0x419c24, 0x00084210); nv_wr32(priv, 0x419c28, 0x3cf3cf3c); nv_wr32(priv, 0x419cb0, 0x00020048); - } else - if (chipset == 0xce || chipset == 0xcf) { - nv_wr32(priv, 0x419cb0, 0x00020048); - } else { + break; + default: nv_wr32(priv, 0x419cb0, 0x00060048); + break; } nv_wr32(priv, 0x419ce8, 0x00000000); nv_wr32(priv, 0x419cf4, 0x00000183); - if (chipset == 0xc1 || chipset >= 0xd0) + switch (nv_device(priv)->chipset) { + case 0xc1: + case 0xd9: + case 0xd7: nv_wr32(priv, 0x419d20, 0x12180000); - else + break; + default: nv_wr32(priv, 0x419d20, 0x02180000); + break; + } nv_wr32(priv, 0x419d24, 0x00001fff); - if (chipset == 0xc1 || chipset >= 0xd0) + switch (nv_device(priv)->chipset) { + case 0xc1: + case 0xd9: + case 0xd7: nv_wr32(priv, 0x419d44, 0x02180218); + break; + default: + break; + } nv_wr32(priv, 0x419e04, 0x00000000); nv_wr32(priv, 0x419e08, 0x00000000); nv_wr32(priv, 0x419e0c, 0x00000000); @@ -1899,12 +2046,44 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419e8c, 0x00000000); nv_wr32(priv, 0x419e90, 0x00000000); nv_wr32(priv, 0x419e98, 0x00000000); - if (chipset != 0xc0 && chipset != 0xc8) + switch (nv_device(priv)->chipset) { + case 0xc0: + case 0xc8: + break; + case 0xd9: + case 0xd7: + nv_wr32(priv, 0x419ee0, 0x00010110); + break; + default: nv_wr32(priv, 0x419ee0, 0x00011110); - nv_wr32(priv, 0x419f50, 0x00000000); - nv_wr32(priv, 0x419f54, 0x00000000); - if (chipset != 0xc0 && chipset != 0xc8) + break; + } + switch (nv_device(priv)->chipset) { + case 0xc0: + case 0xc8: + nv_wr32(priv, 0x419f50, 0x00000000); + nv_wr32(priv, 0x419f54, 0x00000000); + break; + case 0xd9: + case 0xd7: + nv_wr32(priv, 0x419f30, 0x00000000); + nv_wr32(priv, 0x419f34, 0x00000000); + nv_wr32(priv, 0x419f38, 0x00000000); + nv_wr32(priv, 0x419f3c, 0x00000000); + nv_wr32(priv, 0x419f40, 0x00000000); + nv_wr32(priv, 0x419f44, 0x00000000); + nv_wr32(priv, 0x419f48, 0x00000000); + nv_wr32(priv, 0x419f4c, 0x00000000); + nv_wr32(priv, 0x419f50, 0x00000000); + nv_wr32(priv, 0x419f54, 0x00000000); nv_wr32(priv, 0x419f58, 0x00000000); + break; + default: + nv_wr32(priv, 0x419f50, 0x00000000); + nv_wr32(priv, 0x419f54, 0x00000000); + nv_wr32(priv, 0x419f58, 0x00000000); + break; + } } int @@ -1952,32 +2131,37 @@ nvc0_grctx_generate(struct nvc0_graph_priv *priv) mmio_list(0x419008, 0x00000000, 0, 0); mmio_list(0x418808, 0x00000000, 8, 0); mmio_list(0x41880c, 0x80000018, 0, 0); - if (nv_device(priv)->chipset != 0xc1) { + switch (nv_device(priv)->chipset) { + case 0xc1: + case 0xd9: + case 0xd7: tmp = 0x02180000; - mmio_list(0x405830, tmp, 0, 0); + mmio_list(0x405830, 0x00000218 | tmp, 0, 0); + mmio_list(0x4064c4, 0x0086ffff, 0, 0); for (gpc = 0; gpc < priv->gpc_nr; gpc++) { for (tpc = 0; tpc < priv->tpc_nr[gpc]; tpc++) { u32 reg = TPC_UNIT(gpc, tpc, 0x0520); + mmio_list(reg, 0x10000000 | tmp, 0, 0); + tmp += 0x0324; + } + for (tpc = 0; tpc < priv->tpc_nr[gpc]; tpc++) { + u32 reg = TPC_UNIT(gpc, tpc, 0x0544); mmio_list(reg, tmp, 0, 0); tmp += 0x0324; } } - } else { + break; + default: tmp = 0x02180000; - mmio_list(0x405830, 0x00000218 | tmp, 0, 0); - mmio_list(0x4064c4, 0x0086ffff, 0, 0); + mmio_list(0x405830, tmp, 0, 0); for (gpc = 0; gpc < priv->gpc_nr; gpc++) { for (tpc = 0; tpc < priv->tpc_nr[gpc]; tpc++) { u32 reg = TPC_UNIT(gpc, tpc, 0x0520); - mmio_list(reg, 0x10000000 | tmp, 0, 0); - tmp += 0x0324; - } - for (tpc = 0; tpc < priv->tpc_nr[gpc]; tpc++) { - u32 reg = TPC_UNIT(gpc, tpc, 0x0544); mmio_list(reg, tmp, 0, 0); tmp += 0x0324; } } + break; } for (tpc = 0, id = 0; tpc < 4; tpc++) { @@ -2209,9 +2393,14 @@ nvc0_grctx_generate(struct nvc0_graph_priv *priv) nv_icmd(priv, 0x00000215, 0x00000040); nv_icmd(priv, 0x00000216, 0x00000040); nv_icmd(priv, 0x00000217, 0x00000040); - if (nv_device(priv)->chipset >= 0xd0) { - for (i = 0x0400; i <= 0x0417; i++) + switch (nv_device(priv)->chipset) { + case 0xd9: + case 0xd7: + for (i = 0x400; i <= 0x417; i++) nv_icmd(priv, i, 0x00000040); + break; + default: + break; } nv_icmd(priv, 0x00000218, 0x0000c080); nv_icmd(priv, 0x00000219, 0x0000c080); @@ -2221,9 +2410,14 @@ nvc0_grctx_generate(struct nvc0_graph_priv *priv) nv_icmd(priv, 0x0000021d, 0x0000c080); nv_icmd(priv, 0x0000021e, 0x0000c080); nv_icmd(priv, 0x0000021f, 0x0000c080); - if (nv_device(priv)->chipset >= 0xd0) { - for (i = 0x0440; i <= 0x0457; i++) + switch (nv_device(priv)->chipset) { + case 0xd9: + case 0xd7: + for (i = 0x440; i <= 0x457; i++) nv_icmd(priv, i, 0x0000c080); + break; + default: + break; } nv_icmd(priv, 0x000000ad, 0x0000013e); nv_icmd(priv, 0x000000e1, 0x00000010); @@ -2787,9 +2981,14 @@ nvc0_grctx_generate(struct nvc0_graph_priv *priv) nv_icmd(priv, 0x0000053f, 0xffff0000); nv_icmd(priv, 0x00000585, 0x0000003f); nv_icmd(priv, 0x00000576, 0x00000003); - if (nv_device(priv)->chipset == 0xc1 || - nv_device(priv)->chipset >= 0xd0) + switch (nv_device(priv)->chipset) { + case 0xd9: + case 0xd7: nv_icmd(priv, 0x0000057b, 0x00000059); + break; + default: + break; + } nv_icmd(priv, 0x00000586, 0x00000040); nv_icmd(priv, 0x00000582, 0x00000080); nv_icmd(priv, 0x00000583, 0x00000080); @@ -2890,8 +3089,14 @@ nvc0_grctx_generate(struct nvc0_graph_priv *priv) nv_icmd(priv, 0x00000957, 0x00000003); nv_icmd(priv, 0x0000095e, 0x20164010); nv_icmd(priv, 0x0000095f, 0x00000020); - if (nv_device(priv)->chipset >= 0xd0) + switch (nv_device(priv)->chipset) { + case 0xd9: + case 0xd7: nv_icmd(priv, 0x0000097d, 0x00000020); + break; + default: + break; + } nv_icmd(priv, 0x00000683, 0x00000006); nv_icmd(priv, 0x00000685, 0x003fffff); nv_icmd(priv, 0x00000687, 0x00000c48); @@ -3020,6 +3225,8 @@ nvc0_grctx_generate(struct nvc0_graph_priv *priv) nv_icmd(priv, 0x00000825, 0x00000100); nv_icmd(priv, 0x00000826, 0x00000001); nv_icmd(priv, 0x0001e100, 0x00000001); + + nv_wr32(priv, 0x400208, 0x00000000); nv_wr32(priv, 0x404154, 0x00000400); @@ -3032,6 +3239,15 @@ nvc0_grctx_generate(struct nvc0_graph_priv *priv) nvc0_grctx_generate_9039(priv); nvc0_grctx_generate_90c0(priv); + switch (nv_device(priv)->chipset) { + case 0xd9: + case 0xd7: + nv_mthd(priv, 0x902d, 0x3410, 0x80002006); + break; + default: + break; + } + nv_wr32(priv, 0x000260, r000260); return nvc0_grctx_fini(&info); diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc index f7055af0f2a6..a9f499c6729b 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc @@ -208,7 +208,7 @@ mmctx_data(0x000604, 4) mmctx_data(0x000644, 20) mmctx_data(0x000698, 1) mmctx_data(0x0006e0, 1) -mmctx_data(0x000750, 3) +mmctx_data(0x000730, 11) nvd9_tpc_mmio_tail: .section #nvc0_grgpc_code diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h index a9711712de57..b8c9fc3b32bd 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h @@ -170,7 +170,7 @@ uint32_t nvc0_grgpc_data[] = { 0x4c000644, 0x00000698, 0x000006e0, - 0x08000750, + 0x28000730, }; uint32_t nvc0_grgpc_code[] = { diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc index 7fbdebb2bafb..56735d0654bf 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc @@ -149,7 +149,7 @@ mmctx_data(0x4078bc, 1) mmctx_data(0x408000, 7) mmctx_data(0x408064, 1) mmctx_data(0x408800, 3) -mmctx_data(0x408900, 4) +mmctx_data(0x408900, 3) mmctx_data(0x408980, 1) nvd9_hub_mmio_tail: diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h index b655117e8dac..eb59892bc488 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h @@ -129,7 +129,7 @@ uint32_t nvc0_grhub_data[] = { 0x18408000, 0x00408064, 0x08408800, - 0x0c408900, + 0x08408900, 0x00408980, /* 0x01e4: nvd9_hub_mmio_tail */ 0x00000000, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c index af40e654ac89..766870c4a27c 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c @@ -716,6 +716,180 @@ nvc0_graph_init_regs(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x400124, 0x00000002); } +static void +nvc0_graph_init_unk40xx(struct nvc0_graph_priv *priv) +{ + nv_wr32(priv, 0x40415c, 0x00000000); + nv_wr32(priv, 0x404170, 0x00000000); +} + +static void +nvc0_graph_init_unk44xx(struct nvc0_graph_priv *priv) +{ + nv_wr32(priv, 0x404488, 0x00000000); + nv_wr32(priv, 0x40448c, 0x00000000); +} + +static void +nvc0_graph_init_unk78xx(struct nvc0_graph_priv *priv) +{ + nv_wr32(priv, 0x407808, 0x00000000); +} + +static void +nvc0_graph_init_unk60xx(struct nvc0_graph_priv *priv) +{ + nv_wr32(priv, 0x406024, 0x00000000); +} + +static void +nvc0_graph_init_unk64xx(struct nvc0_graph_priv *priv) +{ + nv_wr32(priv, 0x4064f0, 0x00000000); + nv_wr32(priv, 0x4064f4, 0x00000000); + nv_wr32(priv, 0x4064f8, 0x00000000); +} + +static void +nvc0_graph_init_unk58xx(struct nvc0_graph_priv *priv) +{ + nv_wr32(priv, 0x405844, 0x00ffffff); + nv_wr32(priv, 0x405850, 0x00000000); + nv_wr32(priv, 0x405900, 0x00002834); + nv_wr32(priv, 0x405908, 0x00000000); + nv_wr32(priv, 0x405928, 0x00000000); + nv_wr32(priv, 0x40592c, 0x00000000); +} + +static void +nvc0_graph_init_unk80xx(struct nvc0_graph_priv *priv) +{ + nv_wr32(priv, 0x40803c, 0x00000000); +} + +static void +nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) +{ + nv_wr32(priv, 0x418408, 0x00000000); + nv_wr32(priv, 0x4184a0, 0x00000000); + nv_wr32(priv, 0x4184a4, 0x00000000); + nv_wr32(priv, 0x4184a8, 0x00000000); + nv_wr32(priv, 0x418604, 0x00000000); + nv_wr32(priv, 0x418680, 0x00000000); + nv_wr32(priv, 0x418714, 0x00000000); + nv_wr32(priv, 0x418384, 0x00000000); + nv_wr32(priv, 0x418814, 0x00000000); + nv_wr32(priv, 0x418818, 0x00000000); + nv_wr32(priv, 0x41881c, 0x00000000); + nv_wr32(priv, 0x418b04, 0x00000000); + nv_wr32(priv, 0x4188c8, 0x00000000); + nv_wr32(priv, 0x4188cc, 0x00000000); + nv_wr32(priv, 0x4188d0, 0x00010000); + nv_wr32(priv, 0x4188d4, 0x00000001); + nv_wr32(priv, 0x418910, 0x00010001); + nv_wr32(priv, 0x418914, 0x00000301); + nv_wr32(priv, 0x418918, 0x00800000); + nv_wr32(priv, 0x418980, 0x77777770); + nv_wr32(priv, 0x418984, 0x77777777); + nv_wr32(priv, 0x418988, 0x77777777); + nv_wr32(priv, 0x41898c, 0x77777777); + nv_wr32(priv, 0x418c04, 0x00000000); + nv_wr32(priv, 0x418c64, 0x00000000); + nv_wr32(priv, 0x418c68, 0x00000000); + nv_wr32(priv, 0x418c88, 0x00000000); + nv_wr32(priv, 0x418cb4, 0x00000000); + nv_wr32(priv, 0x418cb8, 0x00000000); + nv_wr32(priv, 0x418d00, 0x00000000); + nv_wr32(priv, 0x418d28, 0x00000000); + nv_wr32(priv, 0x418d2c, 0x00000000); + nv_wr32(priv, 0x418f00, 0x00000000); + nv_wr32(priv, 0x418f08, 0x00000000); + nv_wr32(priv, 0x418f20, 0x00000000); + nv_wr32(priv, 0x418f24, 0x00000000); + nv_wr32(priv, 0x418e00, 0x00000003); + nv_wr32(priv, 0x418e08, 0x00000000); + nv_wr32(priv, 0x418e1c, 0x00000000); + nv_wr32(priv, 0x418e20, 0x00000000); + nv_wr32(priv, 0x41900c, 0x00000000); + nv_wr32(priv, 0x419018, 0x00000000); +} + +static void +nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) +{ + nv_wr32(priv, 0x419d08, 0x00000000); + nv_wr32(priv, 0x419d0c, 0x00000000); + nv_wr32(priv, 0x419d10, 0x00000014); + nv_wr32(priv, 0x419ab0, 0x00000000); + nv_wr32(priv, 0x419ac8, 0x00000000); + nv_wr32(priv, 0x419ab8, 0x000000e7); + nv_wr32(priv, 0x419abc, 0x00000000); + nv_wr32(priv, 0x419ac0, 0x00000000); + nv_wr32(priv, 0x419ab4, 0x00000000); + nv_wr32(priv, 0x41980c, 0x00000010); + nv_wr32(priv, 0x419810, 0x00000000); + nv_wr32(priv, 0x419814, 0x00000004); + nv_wr32(priv, 0x419844, 0x00000000); + nv_wr32(priv, 0x41984c, 0x0000a918); + nv_wr32(priv, 0x419850, 0x00000000); + nv_wr32(priv, 0x419854, 0x00000000); + nv_wr32(priv, 0x419858, 0x00000000); + nv_wr32(priv, 0x41985c, 0x00000000); + nv_wr32(priv, 0x419880, 0x00000002); + nv_wr32(priv, 0x419c98, 0x00000000); + nv_wr32(priv, 0x419ca8, 0x80000000); + nv_wr32(priv, 0x419cb4, 0x00000000); + nv_wr32(priv, 0x419cb8, 0x00008bf4); + nv_wr32(priv, 0x419cbc, 0x28137606); + nv_wr32(priv, 0x419cc0, 0x00000000); + nv_wr32(priv, 0x419cc4, 0x00000000); + nv_wr32(priv, 0x419bd4, 0x00800000); + nv_wr32(priv, 0x419bdc, 0x00000000); + nv_wr32(priv, 0x419bf8, 0x00000000); + nv_wr32(priv, 0x419bfc, 0x00000000); + nv_wr32(priv, 0x419d2c, 0x00000000); + nv_wr32(priv, 0x419d48, 0x00000000); + nv_wr32(priv, 0x419d4c, 0x00000000); + nv_wr32(priv, 0x419c0c, 0x00000000); + nv_wr32(priv, 0x419e00, 0x00000000); + nv_wr32(priv, 0x419ea0, 0x00000000); + nv_wr32(priv, 0x419ea4, 0x00000100); + nv_wr32(priv, 0x419ea8, 0x02001100); + nv_wr32(priv, 0x419eac, 0x11100702); + nv_wr32(priv, 0x419eb0, 0x00000003); + nv_wr32(priv, 0x419eb4, 0x00000000); + nv_wr32(priv, 0x419eb8, 0x00000000); + nv_wr32(priv, 0x419ebc, 0x00000000); + nv_wr32(priv, 0x419ec0, 0x00000000); + nv_wr32(priv, 0x419ec8, 0x0e063818); + nv_wr32(priv, 0x419ecc, 0x0e060e06); + nv_wr32(priv, 0x419ed0, 0x00003818); + nv_wr32(priv, 0x419ed4, 0x011104f1); + nv_wr32(priv, 0x419edc, 0x00000000); + nv_wr32(priv, 0x419f00, 0x00000000); + nv_wr32(priv, 0x419f2c, 0x00000000); +} + +static void +nvc0_graph_init_unk88xx(struct nvc0_graph_priv *priv) +{ + nv_wr32(priv, 0x40880c, 0x00000000); + nv_wr32(priv, 0x408910, 0x00000000); + nv_wr32(priv, 0x408914, 0x00000000); + nv_wr32(priv, 0x408918, 0x00000000); + nv_wr32(priv, 0x40891c, 0x00000000); + nv_wr32(priv, 0x408920, 0x00000000); + nv_wr32(priv, 0x408924, 0x00000000); + nv_wr32(priv, 0x408928, 0x00000000); + nv_wr32(priv, 0x40892c, 0x00000000); + nv_wr32(priv, 0x408930, 0x00000000); + nv_wr32(priv, 0x408950, 0x00000000); + nv_wr32(priv, 0x408954, 0x0000ffff); + nv_wr32(priv, 0x408984, 0x00000000); + nv_wr32(priv, 0x408988, 0x08040201); + nv_wr32(priv, 0x40898c, 0x80402010); +} + static void nvc0_graph_init_gpc_0(struct nvc0_graph_priv *priv) { @@ -957,7 +1131,25 @@ nvc0_graph_init(struct nouveau_object *object) nvc0_graph_init_obj418880(priv); nvc0_graph_init_regs(priv); - /*nvc0_graph_init_unitplemented_magics(priv);*/ + + switch (nv_device(priv)->chipset) { + case 0xd9: + case 0xd7: + nvc0_graph_init_unk40xx(priv); + nvc0_graph_init_unk44xx(priv); + nvc0_graph_init_unk78xx(priv); + nvc0_graph_init_unk60xx(priv); + nvc0_graph_init_unk64xx(priv); + nvc0_graph_init_unk58xx(priv); + nvc0_graph_init_unk80xx(priv); + nvc0_graph_init_gpc(priv); + nvc0_graph_init_tpc(priv); + nvc0_graph_init_unk88xx(priv); + break; + default: + break; + } + nvc0_graph_init_gpc_0(priv); /*nvc0_graph_init_unitplemented_c242(priv);*/ -- cgit v1.2.3 From d8b02dbbc37de874728181963b1ec9ef874cc81d Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 13 May 2013 12:57:10 +1000 Subject: drm/nvc0/gr: update initial register/context values Signed-off-by: Ben Skeggs --- .../gpu/drm/nouveau/core/engine/graph/ctxnvc0.c | 62 +- .../drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc | 57 +- .../nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h | 87 +- .../drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc | 63 +- .../nouveau/core/engine/graph/fuc/hubnvc0.fuc.h | 1065 +++++++++++--------- drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c | 252 ++++- 6 files changed, 1042 insertions(+), 544 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c index 31a84162632b..416dc9b16978 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c @@ -1325,6 +1325,7 @@ nvc0_grctx_generate_9097(struct nvc0_graph_priv *priv) nv_mthd(priv, 0x9097, 0x0214, 0x00000000); switch (nv_device(priv)->chipset) { + case 0xc0: case 0xd9: case 0xd7: break; @@ -1471,6 +1472,7 @@ nvc0_grctx_generate_dispatch(struct nvc0_graph_priv *priv) case 0xd7: nv_wr32(priv, 0x40402c, 0x00000000); break; + case 0xc0: default: break; } @@ -1490,6 +1492,7 @@ nvc0_grctx_generate_dispatch(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x4040c4, 0x00000000); nv_wr32(priv, 0x4040c8, 0xf0000087); switch (nv_device(priv)->chipset) { + case 0xc0: case 0xd9: case 0xd7: nv_wr32(priv, 0x4040d0, 0x00000000); @@ -1516,6 +1519,7 @@ nvc0_grctx_generate_dispatch(struct nvc0_graph_priv *priv) case 0xd9: case 0xd7: break; + case 0xc0: default: nv_wr32(priv, 0x404174, 0x00000000); break; @@ -1645,20 +1649,24 @@ nvc0_grctx_generate_unk47xx(struct nvc0_graph_priv *priv) static void nvc0_grctx_generate_shaders(struct nvc0_graph_priv *priv) { - - if (nv_device(priv)->chipset >= 0xd0) { + switch (nv_device(priv)->chipset) { + case 0xc1: nv_wr32(priv, 0x405800, 0x0f8000bf); nv_wr32(priv, 0x405830, 0x02180218); - nv_wr32(priv, 0x405834, 0x08000000); - } else - if (nv_device(priv)->chipset == 0xc1) { + nv_wr32(priv, 0x405834, 0x00000000); + break; + case 0xd9: + case 0xd7: nv_wr32(priv, 0x405800, 0x0f8000bf); nv_wr32(priv, 0x405830, 0x02180218); - nv_wr32(priv, 0x405834, 0x00000000); - } else { + nv_wr32(priv, 0x405834, 0x08000000); + break; + case 0xc0: + default: nv_wr32(priv, 0x405800, 0x078000bf); nv_wr32(priv, 0x405830, 0x02180000); nv_wr32(priv, 0x405834, 0x00000000); + break; } nv_wr32(priv, 0x405838, 0x00000000); nv_wr32(priv, 0x405854, 0x00000000); @@ -1694,6 +1702,7 @@ nvc0_grctx_generate_unk64xx(struct nvc0_graph_priv *priv) case 0xd7: nv_wr32(priv, 0x4064bc, 0x00000000); break; + case 0xc0: default: break; } @@ -1704,6 +1713,7 @@ nvc0_grctx_generate_unk64xx(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x4064c0, 0x80140078); nv_wr32(priv, 0x4064c4, 0x0086ffff); break; + case 0xc0: default: break; } @@ -1742,6 +1752,12 @@ nvc0_grctx_generate_rop(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x408800, 0x02802a3c); nv_wr32(priv, 0x408804, 0x00000040); switch (nv_device(priv)->chipset) { + case 0xc0: + nv_wr32(priv, 0x408808, 0x0003e00d); + nv_wr32(priv, 0x408900, 0x3080b801); + nv_wr32(priv, 0x408904, 0x02000001); + nv_wr32(priv, 0x408908, 0x00c80929); + break; case 0xc1: nv_wr32(priv, 0x408808, 0x1003e005); nv_wr32(priv, 0x408900, 0x3080b801); @@ -1780,6 +1796,7 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) case 0xd9: case 0xd7: break; + case 0xc0: default: nv_wr32(priv, 0x418408, 0x00000000); break; @@ -1791,6 +1808,7 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) case 0xd7: nv_wr32(priv, 0x418414, 0x02200fff); break; + case 0xc0: default: nv_wr32(priv, 0x418414, 0x00200fff); break; @@ -1814,6 +1832,7 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) case 0xd7: nv_wr32(priv, 0x41870c, 0x00000000); break; + case 0xc0: default: nv_wr32(priv, 0x41870c, 0x07c80000); break; @@ -1824,6 +1843,7 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) case 0xd7: nv_wr32(priv, 0x418800, 0x7006860a); break; + case 0xc0: default: nv_wr32(priv, 0x418800, 0x0006860a); break; @@ -1838,6 +1858,7 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) case 0xd7: nv_wr32(priv, 0x418830, 0x10000001); break; + case 0xc0: default: nv_wr32(priv, 0x418830, 0x00000001); break; @@ -1857,6 +1878,7 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) case 0xd7: nv_wr32(priv, 0x4188fc, 0x20100008); break; + case 0xc0: default: nv_wr32(priv, 0x4188fc, 0x00100000); break; @@ -1879,6 +1901,7 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) case 0xd7: nv_wr32(priv, 0x418b00, 0x00000006); break; + case 0xc0: default: nv_wr32(priv, 0x418b00, 0x00000000); break; @@ -1905,6 +1928,7 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) case 0xd7: nv_wr32(priv, 0x418c6c, 0x00000001); break; + case 0xc0: default: break; } @@ -1929,6 +1953,7 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) case 0xd7: nv_wr32(priv, 0x419864, 0x00000129); break; + case 0xc0: default: nv_wr32(priv, 0x419864, 0x0000012a); break; @@ -1940,8 +1965,14 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419a0c, 0x00020000); nv_wr32(priv, 0x419a10, 0x00000000); nv_wr32(priv, 0x419a14, 0x00000200); - nv_wr32(priv, 0x419a1c, 0x00000000); - nv_wr32(priv, 0x419a20, 0x00000800); + switch (nv_device(priv)->chipset) { + case 0xc0: + break; + default: + nv_wr32(priv, 0x419a1c, 0x00000000); + nv_wr32(priv, 0x419a20, 0x00000800); + break; + } switch (nv_device(priv)->chipset) { case 0xc0: case 0xc8: @@ -1967,6 +1998,7 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) case 0xd7: nv_wr32(priv, 0x419be0, 0x00400001); break; + case 0xc0: default: nv_wr32(priv, 0x419be0, 0x00000001); break; @@ -1977,6 +2009,7 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) case 0xd7: nv_wr32(priv, 0x419c00, 0x0000000a); break; + case 0xc0: default: nv_wr32(priv, 0x419c00, 0x00000002); break; @@ -1995,6 +2028,7 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419c28, 0x3cf3cf3c); nv_wr32(priv, 0x419cb0, 0x00020048); break; + case 0xc0: default: nv_wr32(priv, 0x419cb0, 0x00060048); break; @@ -2007,6 +2041,7 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) case 0xd7: nv_wr32(priv, 0x419d20, 0x12180000); break; + case 0xc0: default: nv_wr32(priv, 0x419d20, 0x02180000); break; @@ -2018,6 +2053,7 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) case 0xd7: nv_wr32(priv, 0x419d44, 0x02180218); break; + case 0xc0: default: break; } @@ -2399,6 +2435,7 @@ nvc0_grctx_generate(struct nvc0_graph_priv *priv) for (i = 0x400; i <= 0x417; i++) nv_icmd(priv, i, 0x00000040); break; + case 0xc0: default: break; } @@ -2416,6 +2453,8 @@ nvc0_grctx_generate(struct nvc0_graph_priv *priv) for (i = 0x440; i <= 0x457; i++) nv_icmd(priv, i, 0x0000c080); break; + case 0xc0: + break; default: break; } @@ -2986,6 +3025,7 @@ nvc0_grctx_generate(struct nvc0_graph_priv *priv) case 0xd7: nv_icmd(priv, 0x0000057b, 0x00000059); break; + case 0xc0: default: break; } @@ -3094,6 +3134,7 @@ nvc0_grctx_generate(struct nvc0_graph_priv *priv) case 0xd7: nv_icmd(priv, 0x0000097d, 0x00000020); break; + case 0xc0: default: break; } @@ -3240,6 +3281,9 @@ nvc0_grctx_generate(struct nvc0_graph_priv *priv) nvc0_grctx_generate_90c0(priv); switch (nv_device(priv)->chipset) { + case 0xc0: + nv_mthd(priv, 0x902d, 0x3410, 0x00000000); + break; case 0xd9: case 0xd7: nv_mthd(priv, 0x902d, 0x3410, 0x80002006); diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc index a9f499c6729b..1034ff15b032 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc @@ -48,10 +48,10 @@ cmd_queue: queue_init // chipset descriptions chipsets: .b8 0xc0 0 0 0 -.b16 #nvc0_gpc_mmio_head -.b16 #nvc0_gpc_mmio_tail -.b16 #nvc0_tpc_mmio_head -.b16 #nvc0_tpc_mmio_tail +.b16 #nnvc0_gpc_mmio_head +.b16 #nnvc0_gpc_mmio_tail +.b16 #nnvc0_tpc_mmio_head +.b16 #nnvc0_tpc_mmio_tail .b8 0xc1 0 0 0 .b16 #nvc0_gpc_mmio_head .b16 #nvc1_gpc_mmio_tail @@ -124,6 +124,33 @@ nvc0_gpc_mmio_tail: mmctx_data(0x000c6c, 1); nvc1_gpc_mmio_tail: +nnvc0_gpc_mmio_head: +mmctx_data(0x000380, 1) +mmctx_data(0x000400, 6) +mmctx_data(0x000450, 9) +mmctx_data(0x000600, 1) +mmctx_data(0x000684, 1) +mmctx_data(0x000700, 5) +mmctx_data(0x000800, 1) +mmctx_data(0x000808, 3) +mmctx_data(0x000828, 1) +mmctx_data(0x000830, 1) +mmctx_data(0x0008d8, 1) +mmctx_data(0x0008e0, 1) +mmctx_data(0x0008e8, 6) +mmctx_data(0x00091c, 1) +mmctx_data(0x000924, 3) +mmctx_data(0x000b00, 1) +mmctx_data(0x000b08, 6) +mmctx_data(0x000bb8, 1) +mmctx_data(0x000c08, 1) +mmctx_data(0x000c10, 8) +mmctx_data(0x000c80, 1) +mmctx_data(0x000c8c, 1) +mmctx_data(0x001000, 3) +mmctx_data(0x001014, 1) +nnvc0_gpc_mmio_tail: + nvd9_gpc_mmio_head: mmctx_data(0x000380, 1) mmctx_data(0x000400, 2) @@ -185,6 +212,28 @@ nvc3_tpc_mmio_tail: mmctx_data(0x000544, 1) nvc1_tpc_mmio_tail: +nnvc0_tpc_mmio_head: +mmctx_data(0x000018, 1) +mmctx_data(0x00003c, 1) +mmctx_data(0x000048, 1) +mmctx_data(0x000064, 1) +mmctx_data(0x000088, 1) +mmctx_data(0x000200, 6) +mmctx_data(0x000300, 6) +mmctx_data(0x0003d0, 1) +mmctx_data(0x0003e0, 2) +mmctx_data(0x000400, 3) +mmctx_data(0x000420, 1) +mmctx_data(0x0004b0, 1) +mmctx_data(0x0004e8, 1) +mmctx_data(0x0004f4, 1) +mmctx_data(0x000520, 2) +mmctx_data(0x000604, 4) +mmctx_data(0x000644, 20) +mmctx_data(0x000698, 1) +mmctx_data(0x000750, 2) +nnvc0_tpc_mmio_tail: + nvd9_tpc_mmio_head: mmctx_data(0x000018, 1) mmctx_data(0x00003c, 1) diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h index b8c9fc3b32bd..427ddf06316c 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h @@ -34,32 +34,32 @@ uint32_t nvc0_grgpc_data[] = { 0x00000000, /* 0x0064: chipsets */ 0x000000c0, - 0x013400d4, - 0x01f001a0, + 0x01980138, + 0x02b00264, 0x000000c1, 0x013800d4, - 0x020401a0, + 0x02640200, 0x000000c3, 0x013400d4, - 0x020001a0, + 0x02600200, 0x000000c4, 0x013400d4, - 0x020001a0, + 0x02600200, 0x000000c8, 0x013400d4, - 0x01f001a0, + 0x02500200, 0x000000ce, 0x013400d4, - 0x020001a0, + 0x02600200, 0x000000cf, 0x013400d4, - 0x01fc01a0, + 0x025c0200, 0x000000d9, - 0x01a00138, - 0x02600204, + 0x02000198, + 0x030c02b0, 0x000000d7, - 0x01a00138, - 0x02600204, + 0x02000198, + 0x030c02b0, 0x00000000, /* 0x00d4: nvc0_gpc_mmio_head */ 0x00000380, @@ -89,7 +89,33 @@ uint32_t nvc0_grgpc_data[] = { /* 0x0134: nvc0_gpc_mmio_tail */ 0x00000c6c, /* 0x0138: nvc1_gpc_mmio_tail */ -/* 0x0138: nvd9_gpc_mmio_head */ +/* 0x0138: nnvc0_gpc_mmio_head */ + 0x00000380, + 0x14000400, + 0x20000450, + 0x00000600, + 0x00000684, + 0x10000700, + 0x00000800, + 0x08000808, + 0x00000828, + 0x00000830, + 0x000008d8, + 0x000008e0, + 0x140008e8, + 0x0000091c, + 0x08000924, + 0x00000b00, + 0x14000b08, + 0x00000bb8, + 0x00000c08, + 0x1c000c10, + 0x00000c80, + 0x00000c8c, + 0x08001000, + 0x00001014, +/* 0x0198: nnvc0_gpc_mmio_tail */ +/* 0x0198: nvd9_gpc_mmio_head */ 0x00000380, 0x04000400, 0x0800040c, @@ -116,8 +142,8 @@ uint32_t nvc0_grgpc_data[] = { 0x00000c8c, 0x08001000, 0x00001014, -/* 0x01a0: nvd9_gpc_mmio_tail */ -/* 0x01a0: nvc0_tpc_mmio_head */ +/* 0x0200: nvd9_gpc_mmio_tail */ +/* 0x0200: nvc0_tpc_mmio_head */ 0x00000018, 0x0000003c, 0x00000048, @@ -138,16 +164,37 @@ uint32_t nvc0_grgpc_data[] = { 0x4c000644, 0x00000698, 0x04000750, -/* 0x01f0: nvc0_tpc_mmio_tail */ +/* 0x0250: nvc0_tpc_mmio_tail */ 0x00000758, 0x000002c4, 0x000006e0, -/* 0x01fc: nvcf_tpc_mmio_tail */ +/* 0x025c: nvcf_tpc_mmio_tail */ 0x000004bc, -/* 0x0200: nvc3_tpc_mmio_tail */ +/* 0x0260: nvc3_tpc_mmio_tail */ 0x00000544, -/* 0x0204: nvc1_tpc_mmio_tail */ -/* 0x0204: nvd9_tpc_mmio_head */ +/* 0x0264: nvc1_tpc_mmio_tail */ +/* 0x0264: nnvc0_tpc_mmio_head */ + 0x00000018, + 0x0000003c, + 0x00000048, + 0x00000064, + 0x00000088, + 0x14000200, + 0x14000300, + 0x000003d0, + 0x040003e0, + 0x08000400, + 0x00000420, + 0x000004b0, + 0x000004e8, + 0x000004f4, + 0x04000520, + 0x0c000604, + 0x4c000644, + 0x00000698, + 0x04000750, +/* 0x02b0: nnvc0_tpc_mmio_tail */ +/* 0x02b0: nvd9_tpc_mmio_head */ 0x00000018, 0x0000003c, 0x00000048, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc index 56735d0654bf..9f0768e2719d 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc @@ -37,10 +37,19 @@ hub_mmio_list_tail: .b32 0 ctx_current: .b32 0 +.align 256 +chan_data: +chan_mmio_count: .b32 0 +chan_mmio_address: .b32 0 + +.align 256 +xfer_data: .b32 0 + +.align 256 chipsets: .b8 0xc0 0 0 0 -.b16 #nvc0_hub_mmio_head -.b16 #nvc0_hub_mmio_tail +.b16 #nnvc0_hub_mmio_head +.b16 #nnvc0_hub_mmio_tail .b8 0xc1 0 0 0 .b16 #nvc0_hub_mmio_head .b16 #nvc1_hub_mmio_tail @@ -111,6 +120,48 @@ nvc0_hub_mmio_tail: mmctx_data(0x4064c0, 2) nvc1_hub_mmio_tail: +nnvc0_hub_mmio_head: +mmctx_data(0x17e91c, 2) +mmctx_data(0x400204, 2) +mmctx_data(0x404004, 11) +mmctx_data(0x404044, 1) +mmctx_data(0x404094, 14) +mmctx_data(0x4040d0, 7) +mmctx_data(0x4040f8, 1) +mmctx_data(0x404130, 3) +mmctx_data(0x404150, 3) +mmctx_data(0x404164, 2) +mmctx_data(0x404174, 3) +mmctx_data(0x404200, 8) +mmctx_data(0x404404, 14) +mmctx_data(0x404460, 4) +mmctx_data(0x404480, 1) +mmctx_data(0x404498, 1) +mmctx_data(0x404604, 4) +mmctx_data(0x404618, 32) +mmctx_data(0x404698, 21) +mmctx_data(0x4046f0, 2) +mmctx_data(0x404700, 22) +mmctx_data(0x405800, 1) +mmctx_data(0x405830, 3) +mmctx_data(0x405854, 1) +mmctx_data(0x405870, 4) +mmctx_data(0x405a00, 2) +mmctx_data(0x405a18, 1) +mmctx_data(0x406020, 1) +mmctx_data(0x406028, 4) +mmctx_data(0x4064a8, 2) +mmctx_data(0x4064b4, 2) +mmctx_data(0x407804, 1) +mmctx_data(0x40780c, 6) +mmctx_data(0x4078bc, 1) +mmctx_data(0x408000, 7) +mmctx_data(0x408064, 1) +mmctx_data(0x408800, 3) +mmctx_data(0x408900, 3) +mmctx_data(0x408980, 1) +nnvc0_hub_mmio_tail: + nvd9_hub_mmio_head: mmctx_data(0x17e91c, 2) mmctx_data(0x400204, 2) @@ -153,14 +204,6 @@ mmctx_data(0x408900, 3) mmctx_data(0x408980, 1) nvd9_hub_mmio_tail: -.align 256 -chan_data: -chan_mmio_count: .b32 0 -chan_mmio_address: .b32 0 - -.align 256 -xfer_data: .b32 0 - .section #nvc0_grhub_code bra #init define(`include_code') diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h index eb59892bc488..fc5f9727ae76 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h @@ -28,27 +28,200 @@ uint32_t nvc0_grhub_data[] = { 0x00000000, /* 0x0058: ctx_current */ 0x00000000, -/* 0x005c: chipsets */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0100: chan_data */ +/* 0x0100: chan_mmio_count */ + 0x00000000, +/* 0x0104: chan_mmio_address */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0200: xfer_data */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0300: chipsets */ 0x000000c0, - 0x014400a8, + 0x048803ec, 0x000000c1, - 0x014800a8, + 0x03ec034c, 0x000000c3, - 0x014400a8, + 0x03e8034c, 0x000000c4, - 0x014400a8, + 0x03e8034c, 0x000000c8, - 0x014400a8, + 0x03e8034c, 0x000000ce, - 0x014400a8, + 0x03e8034c, 0x000000cf, - 0x014400a8, + 0x03e8034c, 0x000000d9, - 0x01e40148, + 0x05240488, 0x000000d7, - 0x01e40148, + 0x05240488, 0x00000000, -/* 0x00a8: nvc0_hub_mmio_head */ +/* 0x034c: nvc0_hub_mmio_head */ 0x0417e91c, 0x04400204, 0x28404004, @@ -88,10 +261,51 @@ uint32_t nvc0_grhub_data[] = { 0x08408800, 0x0c408900, 0x00408980, -/* 0x0144: nvc0_hub_mmio_tail */ +/* 0x03e8: nvc0_hub_mmio_tail */ 0x044064c0, -/* 0x0148: nvc1_hub_mmio_tail */ -/* 0x0148: nvd9_hub_mmio_head */ +/* 0x03ec: nvc1_hub_mmio_tail */ +/* 0x03ec: nnvc0_hub_mmio_head */ + 0x0417e91c, + 0x04400204, + 0x28404004, + 0x00404044, + 0x34404094, + 0x184040d0, + 0x004040f8, + 0x08404130, + 0x08404150, + 0x04404164, + 0x08404174, + 0x1c404200, + 0x34404404, + 0x0c404460, + 0x00404480, + 0x00404498, + 0x0c404604, + 0x7c404618, + 0x50404698, + 0x044046f0, + 0x54404700, + 0x00405800, + 0x08405830, + 0x00405854, + 0x0c405870, + 0x04405a00, + 0x00405a18, + 0x00406020, + 0x0c406028, + 0x044064a8, + 0x044064b4, + 0x00407804, + 0x1440780c, + 0x004078bc, + 0x18408000, + 0x00408064, + 0x08408800, + 0x08408900, + 0x00408980, +/* 0x0488: nnvc0_hub_mmio_tail */ +/* 0x0488: nvd9_hub_mmio_head */ 0x0417e91c, 0x04400204, 0x24404004, @@ -131,83 +345,6 @@ uint32_t nvc0_grhub_data[] = { 0x08408800, 0x08408900, 0x00408980, -/* 0x01e4: nvd9_hub_mmio_tail */ - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, -/* 0x0200: chan_data */ -/* 0x0200: chan_mmio_count */ - 0x00000000, -/* 0x0204: chan_mmio_address */ - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, -/* 0x0300: xfer_data */ - 0x00000000, }; uint32_t nvc0_grhub_code[] = { @@ -443,7 +580,7 @@ uint32_t nvc0_grhub_code[] = { 0x0017f100, 0x0227f012, 0xf10012d0, - 0xfe05b917, + 0xfe05ba17, 0x17f10010, 0x10d00400, 0x0437f1c0, @@ -477,403 +614,403 @@ uint32_t nvc0_grhub_code[] = { 0x4021d000, 0x080027f1, 0xcf0624b6, - 0xf7f00022, -/* 0x03a9: init_find_chipset */ - 0x08f0b654, - 0xb800f398, - 0x0bf40432, - 0x0034b00b, - 0xf8f11bf4, -/* 0x03bd: init_context */ - 0x0017f100, - 0x02fe5801, - 0xf003ff58, - 0x0e8000e3, - 0x150f8014, - 0x013d21f5, - 0x070037f1, - 0x950634b6, - 0x34d00814, - 0x4034d000, - 0x130030b7, - 0xb6001fbb, - 0x3fd002f5, - 0x0815b600, - 0xb60110b6, - 0x1fb90814, - 0x6321f502, - 0x001fbb02, - 0xf1000398, - 0xf0200047, -/* 0x040e: init_gpc */ - 0x4ea05043, - 0x1fb90804, - 0x8d21f402, - 0x08004ea0, - 0xf4022fb9, - 0x4ea08d21, - 0xf4bd010c, + 0xf7f10022, +/* 0x03aa: init_find_chipset */ + 0xf0b602f8, + 0x00f39808, + 0xf40432b8, + 0x34b00b0b, + 0xf11bf400, +/* 0x03be: init_context */ + 0x17f100f8, + 0xfe580100, + 0x03ff5802, + 0x8000e3f0, + 0x0f80140e, + 0x3d21f515, + 0x0037f101, + 0x0634b607, + 0xd0081495, + 0x34d00034, + 0x0030b740, + 0x001fbb13, + 0xd002f5b6, + 0x15b6003f, + 0x0110b608, + 0xb90814b6, + 0x21f5021f, + 0x1fbb0263, + 0x00039800, + 0x200047f1, +/* 0x040f: init_gpc */ + 0xa05043f0, + 0xb908044e, + 0x21f4021f, + 0x004ea08d, + 0x022fb908, 0xa08d21f4, - 0xf401044e, - 0x4ea08d21, - 0xf7f00100, - 0x8d21f402, - 0x08004ea0, -/* 0x0440: init_gpc_wait */ - 0xc86821f4, - 0x0bf41fff, - 0x044ea0fa, + 0xbd010c4e, + 0x8d21f4f4, + 0x01044ea0, + 0xa08d21f4, + 0xf001004e, + 0x21f402f7, + 0x004ea08d, +/* 0x0441: init_gpc_wait */ 0x6821f408, - 0xb7001fbb, - 0xb6800040, - 0x1bf40132, - 0x0027f1b4, - 0x0624b608, - 0xb74021d0, - 0xbd080020, - 0x1f19f014, -/* 0x0473: main */ - 0xf40021d0, - 0x28f40031, - 0x08d7f000, - 0xf43921f4, - 0xe4b1f401, - 0x1bf54001, - 0x87f100d1, - 0x84b6083c, - 0xf094bd06, - 0x89d00499, - 0x0017f100, - 0x0614b60b, - 0xcf4012cf, - 0x13c80011, - 0x7e0bf41f, - 0xf41f23c8, - 0x20f95a0b, - 0xf10212b9, + 0xf41fffc8, + 0x4ea0fa0b, + 0x21f40804, + 0x001fbb68, + 0x800040b7, + 0xf40132b6, + 0x27f1b41b, + 0x24b60800, + 0x4021d006, + 0x080020b7, + 0x19f014bd, + 0x0021d01f, +/* 0x0474: main */ + 0xf40031f4, + 0xd7f00028, + 0x3921f408, + 0xb1f401f4, + 0xf54001e4, + 0xf100d11b, 0xb6083c87, 0x94bd0684, - 0xd00799f0, - 0x32f40089, - 0x0231f401, - 0x082921f5, - 0x085c87f1, + 0xd00499f0, + 0x17f10089, + 0x14b60b00, + 0x4012cf06, + 0xc80011cf, + 0x0bf41f13, + 0x1f23c87e, + 0xf95a0bf4, + 0x0212b920, + 0x083c87f1, 0xbd0684b6, 0x0799f094, - 0xfc0089d0, - 0x3c87f120, + 0xf40089d0, + 0x31f40132, + 0x2a21f502, + 0x5c87f108, 0x0684b608, 0x99f094bd, - 0x0089d006, - 0xf50131f4, - 0xf1082921, - 0xb6085c87, - 0x94bd0684, - 0xd00699f0, - 0x0ef40089, -/* 0x0509: chsw_prev_no_next */ - 0xb920f931, - 0x32f40212, - 0x0232f401, - 0x082921f5, - 0x17f120fc, - 0x14b60b00, - 0x0012d006, -/* 0x0527: chsw_no_prev */ - 0xc8130ef4, - 0x0bf41f23, - 0x0131f40d, - 0xf50232f4, -/* 0x0537: chsw_done */ - 0xf1082921, - 0xb60b0c17, - 0x27f00614, - 0x0012d001, + 0x0089d007, + 0x87f120fc, + 0x84b6083c, + 0xf094bd06, + 0x89d00699, + 0x0131f400, + 0x082a21f5, 0x085c87f1, 0xbd0684b6, - 0x0499f094, - 0xf50089d0, -/* 0x0557: main_not_ctx_switch */ - 0xb0ff200e, - 0x1bf401e4, - 0x02f2b90d, - 0x07b521f5, -/* 0x0567: main_not_ctx_chan */ - 0xb0420ef4, - 0x1bf402e4, - 0x3c87f12e, + 0x0699f094, + 0xf40089d0, +/* 0x050a: chsw_prev_no_next */ + 0x20f9310e, + 0xf40212b9, + 0x32f40132, + 0x2a21f502, + 0xf120fc08, + 0xb60b0017, + 0x12d00614, + 0x130ef400, +/* 0x0528: chsw_no_prev */ + 0xf41f23c8, + 0x31f40d0b, + 0x0232f401, + 0x082a21f5, +/* 0x0538: chsw_done */ + 0x0b0c17f1, + 0xf00614b6, + 0x12d00127, + 0x5c87f100, 0x0684b608, 0x99f094bd, - 0x0089d007, - 0xf40132f4, - 0x21f50232, - 0x87f10829, - 0x84b6085c, + 0x0089d004, + 0xff200ef5, +/* 0x0558: main_not_ctx_switch */ + 0xf401e4b0, + 0xf2b90d1b, + 0xb621f502, + 0x420ef407, +/* 0x0568: main_not_ctx_chan */ + 0xf402e4b0, + 0x87f12e1b, + 0x84b6083c, 0xf094bd06, 0x89d00799, - 0x110ef400, -/* 0x0598: main_not_ctx_save */ - 0xf010ef94, - 0x21f501f5, - 0x0ef502ec, -/* 0x05a6: main_done */ - 0x17f1fed1, - 0x14b60820, - 0xf024bd06, - 0x12d01f29, - 0xbe0ef500, -/* 0x05b9: ih */ - 0xfe80f9fe, - 0x80f90188, - 0xa0f990f9, - 0xd0f9b0f9, - 0xf0f9e0f9, - 0xc4800acf, - 0x0bf404ab, - 0x00b7f11d, - 0x08d7f019, - 0xcf40becf, - 0x21f400bf, - 0x00b0b704, - 0x01e7f004, -/* 0x05ef: ih_no_fifo */ - 0xe400bed0, - 0xf40100ab, - 0xd7f00d0b, - 0x01e7f108, - 0x0421f440, -/* 0x0600: ih_no_ctxsw */ - 0x0104b7f1, - 0xabffb0bd, - 0x0d0bf4b4, - 0x0c1ca7f1, - 0xd006a4b6, -/* 0x0616: ih_no_other */ - 0x0ad000ab, - 0xfcf0fc40, - 0xfcd0fce0, - 0xfca0fcb0, - 0xfe80fc90, - 0x80fc0088, - 0xf80032f4, -/* 0x0631: ctx_4160s */ - 0x60e7f101, - 0x40e3f041, - 0xf401f7f0, -/* 0x063e: ctx_4160s_wait */ - 0x21f48d21, - 0x04ffc868, - 0xf8fa0bf4, -/* 0x0649: ctx_4160c */ - 0x60e7f100, - 0x40e3f041, - 0x21f4f4bd, -/* 0x0657: ctx_4170s */ - 0xf100f88d, - 0xf04170e7, - 0xf5f040e3, - 0x8d21f410, -/* 0x0666: ctx_4170w */ + 0x0132f400, + 0xf50232f4, + 0xf1082a21, + 0xb6085c87, + 0x94bd0684, + 0xd00799f0, + 0x0ef40089, +/* 0x0599: main_not_ctx_save */ + 0x10ef9411, + 0xf501f5f0, + 0xf502ec21, +/* 0x05a7: main_done */ + 0xf1fed10e, + 0xb6082017, + 0x24bd0614, + 0xd01f29f0, + 0x0ef50012, +/* 0x05ba: ih */ + 0x80f9febe, + 0xf90188fe, + 0xf990f980, + 0xf9b0f9a0, + 0xf9e0f9d0, + 0x800acff0, + 0xf404abc4, + 0xb7f11d0b, + 0xd7f01900, + 0x40becf08, + 0xf400bfcf, + 0xb0b70421, + 0xe7f00400, + 0x00bed001, +/* 0x05f0: ih_no_fifo */ + 0x0100abe4, + 0xf00d0bf4, + 0xe7f108d7, + 0x21f44001, +/* 0x0601: ih_no_ctxsw */ + 0x04b7f104, + 0xffb0bd01, + 0x0bf4b4ab, + 0x1ca7f10d, + 0x06a4b60c, +/* 0x0617: ih_no_other */ + 0xd000abd0, + 0xf0fc400a, + 0xd0fce0fc, + 0xa0fcb0fc, + 0x80fc90fc, + 0xfc0088fe, + 0x0032f480, +/* 0x0632: ctx_4160s */ + 0xe7f101f8, + 0xe3f04160, + 0x01f7f040, +/* 0x063f: ctx_4160s_wait */ + 0xf48d21f4, + 0xffc86821, + 0xfa0bf404, +/* 0x064a: ctx_4160c */ 0xe7f100f8, - 0xe3f04170, - 0x6821f440, - 0xf410f4f0, - 0x00f8f31b, -/* 0x0678: ctx_redswitch */ - 0x0614e7f1, - 0xf106e4b6, - 0xd00270f7, - 0xf7f000ef, -/* 0x0689: ctx_redswitch_delay */ - 0x01f2b608, - 0xf1fd1bf4, - 0xd00770f7, - 0x00f800ef, -/* 0x0698: ctx_86c */ - 0x086ce7f1, - 0xd006e4b6, - 0xe7f100ef, - 0xe3f08a14, - 0x8d21f440, - 0xa86ce7f1, - 0xf441e3f0, + 0xe3f04160, + 0xf4f4bd40, 0x00f88d21, -/* 0x06b8: ctx_load */ - 0x083c87f1, - 0xbd0684b6, - 0x0599f094, - 0xf00089d0, - 0x21f40ca7, - 0x2417f1c9, - 0x0614b60a, - 0xf10010d0, - 0xb60b0037, - 0x32d00634, - 0x0c17f140, - 0x0614b60a, - 0xd00747f0, - 0x14d00012, -/* 0x06f1: ctx_chan_wait_0 */ - 0x4014cf40, - 0xf41f44f0, - 0x32d0fa1b, - 0x000bfe00, - 0xb61f2af0, - 0x20b60424, - 0x3c87f102, +/* 0x0658: ctx_4170s */ + 0x4170e7f1, + 0xf040e3f0, + 0x21f410f5, +/* 0x0667: ctx_4170w */ + 0xf100f88d, + 0xf04170e7, + 0x21f440e3, + 0x10f4f068, + 0xf8f31bf4, +/* 0x0679: ctx_redswitch */ + 0x14e7f100, + 0x06e4b606, + 0x0270f7f1, + 0xf000efd0, +/* 0x068a: ctx_redswitch_delay */ + 0xf2b608f7, + 0xfd1bf401, + 0x0770f7f1, + 0xf800efd0, +/* 0x0699: ctx_86c */ + 0x6ce7f100, + 0x06e4b608, + 0xf100efd0, + 0xf08a14e7, + 0x21f440e3, + 0x6ce7f18d, + 0x41e3f0a8, + 0xf88d21f4, +/* 0x06b9: ctx_load */ + 0x3c87f100, 0x0684b608, 0x99f094bd, - 0x0089d008, - 0x0a0417f1, + 0x0089d005, + 0xf40ca7f0, + 0x17f1c921, + 0x14b60a24, + 0x0010d006, + 0x0b0037f1, + 0xd00634b6, + 0x17f14032, + 0x14b60a0c, + 0x0747f006, + 0xd00012d0, +/* 0x06f2: ctx_chan_wait_0 */ + 0x14cf4014, + 0x1f44f040, + 0xd0fa1bf4, + 0x0bfe0032, + 0x1f2af000, + 0xb60424b6, + 0x87f10220, + 0x84b6083c, + 0xf094bd06, + 0x89d00899, + 0x0417f100, + 0x0614b60a, + 0xf10012d0, + 0xb60a2017, + 0x27f00614, + 0x0023f102, + 0x0012d080, + 0xf11017f0, + 0xf0020027, + 0x12fa0223, + 0xf103f805, + 0xb6085c87, + 0x94bd0684, + 0xd00899f0, + 0x01980089, + 0x1814b681, + 0xb6800298, + 0x12fd0825, + 0x16018005, + 0x083c87f1, + 0xbd0684b6, + 0x0999f094, + 0xf10089d0, + 0xb60a0427, + 0x21d00624, + 0x0127f000, + 0x0a2017f1, 0xd00614b6, 0x17f10012, - 0x14b60a20, - 0x0227f006, - 0x800023f1, - 0xf00012d0, - 0x27f11017, - 0x23f00300, - 0x0512fa02, + 0x13f00100, + 0x0501fa06, 0x87f103f8, 0x84b6085c, 0xf094bd06, - 0x89d00899, - 0xc1019800, - 0x981814b6, - 0x25b6c002, - 0x0512fd08, - 0xf1160180, - 0xb6083c87, - 0x94bd0684, - 0xd00999f0, - 0x27f10089, - 0x24b60a04, - 0x0021d006, - 0xf10127f0, - 0xb60a2017, - 0x12d00614, - 0x0017f100, - 0x0613f002, - 0xf80501fa, - 0x5c87f103, + 0x89d00999, + 0x5c87f100, 0x0684b608, 0x99f094bd, - 0x0089d009, - 0x085c87f1, - 0xbd0684b6, - 0x0599f094, - 0xf80089d0, -/* 0x07b5: ctx_chan */ - 0x3121f500, - 0xb821f506, - 0x0ca7f006, - 0xf1c921f4, - 0xb60a1017, - 0x27f00614, - 0x0012d005, -/* 0x07d0: ctx_chan_wait */ - 0xfd0012cf, - 0x1bf40522, - 0x4921f5fa, -/* 0x07df: ctx_mmio_exec */ - 0x9800f806, - 0x27f18103, - 0x24b60a04, - 0x0023d006, -/* 0x07ee: ctx_mmio_loop */ - 0x34c434bd, - 0x0f1bf4ff, - 0x030057f1, - 0xfa0653f0, - 0x03f80535, -/* 0x0800: ctx_mmio_pull */ - 0x98c04e98, - 0x21f4c14f, - 0x0830b68d, - 0xf40112b6, -/* 0x0812: ctx_mmio_done */ - 0x0398df1b, - 0x0023d016, - 0xf1800080, - 0xf0020017, - 0x01fa0613, - 0xf803f806, -/* 0x0829: ctx_xfer */ - 0x00f7f100, - 0x06f4b60c, - 0xd004e7f0, -/* 0x0836: ctx_xfer_idle */ - 0xfecf80fe, - 0x00e4f100, - 0xf91bf420, - 0xf40611f4, -/* 0x0846: ctx_xfer_pre */ - 0xf7f01102, - 0x9821f510, - 0x3121f506, - 0x1c11f406, -/* 0x0854: ctx_xfer_pre_load */ - 0xf502f7f0, - 0xf5065721, - 0xf5066621, - 0xbd067821, - 0x5721f5f4, - 0xb821f506, -/* 0x086d: ctx_xfer_exec */ - 0x16019806, - 0x041427f1, - 0xd00624b6, - 0xe7f10020, - 0xe3f0a500, - 0x021fb941, - 0xb68d21f4, - 0xfcf004e0, - 0x022cf001, - 0xfd0124b6, - 0x21f405f2, - 0xfc17f18d, - 0x0213f04a, - 0xd00c27f0, - 0x21f50012, - 0x27f10207, - 0x23f047fc, - 0x0020d002, - 0xb6012cf0, - 0x12d00320, - 0x01acf000, - 0xf006a5f0, - 0x0c9800b7, - 0x150d9814, - 0xf500e7f0, - 0xf0015c21, - 0x21f508a7, - 0x21f50103, - 0x01f40207, - 0x0ca7f022, - 0xf1c921f4, - 0xb60a1017, - 0x27f00614, - 0x0012d005, -/* 0x08f4: ctx_xfer_post_save_wait */ - 0xfd0012cf, - 0x1bf40522, - 0x3202f4fa, -/* 0x0900: ctx_xfer_post */ - 0xf502f7f0, - 0xbd065721, - 0x9821f5f4, - 0x2621f506, - 0x6621f502, - 0xf5f4bd06, - 0xf4065721, - 0x01981011, - 0x0511fd80, - 0xf5070bf4, -/* 0x092b: ctx_xfer_no_post_mmio */ - 0xf507df21, -/* 0x092f: ctx_xfer_done */ - 0xf8064921, - 0x00000000, + 0x0089d005, +/* 0x07b6: ctx_chan */ + 0x21f500f8, + 0x21f50632, + 0xa7f006b9, + 0xc921f40c, + 0x0a1017f1, + 0xf00614b6, + 0x12d00527, +/* 0x07d1: ctx_chan_wait */ + 0x0012cf00, + 0xf40522fd, + 0x21f5fa1b, + 0x00f8064a, +/* 0x07e0: ctx_mmio_exec */ + 0xf1410398, + 0xb60a0427, + 0x23d00624, +/* 0x07ef: ctx_mmio_loop */ + 0xc434bd00, + 0x1bf4ff34, + 0x0057f10f, + 0x0653f002, + 0xf80535fa, +/* 0x0801: ctx_mmio_pull */ + 0x804e9803, + 0xf4814f98, + 0x30b68d21, + 0x0112b608, +/* 0x0813: ctx_mmio_done */ + 0x98df1bf4, + 0x23d01603, + 0x40008000, + 0x010017f1, + 0xfa0613f0, + 0x03f80601, +/* 0x082a: ctx_xfer */ + 0xf7f100f8, + 0xf4b60c00, + 0x04e7f006, +/* 0x0837: ctx_xfer_idle */ + 0xcf80fed0, + 0xe4f100fe, + 0x1bf42000, + 0x0611f4f9, +/* 0x0847: ctx_xfer_pre */ + 0xf01102f4, + 0x21f510f7, + 0x21f50699, + 0x11f40632, +/* 0x0855: ctx_xfer_pre_load */ + 0x02f7f01c, + 0x065821f5, + 0x066721f5, + 0x067921f5, + 0x21f5f4bd, + 0x21f50658, +/* 0x086e: ctx_xfer_exec */ + 0x019806b9, + 0x1427f116, + 0x0624b604, + 0xf10020d0, + 0xf0a500e7, + 0x1fb941e3, + 0x8d21f402, + 0xf004e0b6, + 0x2cf001fc, + 0x0124b602, + 0xf405f2fd, + 0x17f18d21, + 0x13f04afc, + 0x0c27f002, + 0xf50012d0, + 0xf1020721, + 0xf047fc27, + 0x20d00223, + 0x012cf000, + 0xd00320b6, + 0xacf00012, + 0x06a5f001, + 0x9800b7f0, + 0x0d98140c, + 0x00e7f015, + 0x015c21f5, + 0xf508a7f0, + 0xf5010321, + 0xf4020721, + 0xa7f02201, + 0xc921f40c, + 0x0a1017f1, + 0xf00614b6, + 0x12d00527, +/* 0x08f5: ctx_xfer_post_save_wait */ + 0x0012cf00, + 0xf40522fd, + 0x02f4fa1b, +/* 0x0901: ctx_xfer_post */ + 0x02f7f032, + 0x065821f5, + 0x21f5f4bd, + 0x21f50699, + 0x21f50226, + 0xf4bd0667, + 0x065821f5, + 0x981011f4, + 0x11fd4001, + 0x070bf405, + 0x07e021f5, +/* 0x092c: ctx_xfer_no_post_mmio */ + 0x064a21f5, +/* 0x0930: ctx_xfer_done */ + 0x000000f8, 0x00000000, 0x00000000, 0x00000000, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c index 766870c4a27c..9d705bab9dcd 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c @@ -745,9 +745,17 @@ nvc0_graph_init_unk60xx(struct nvc0_graph_priv *priv) static void nvc0_graph_init_unk64xx(struct nvc0_graph_priv *priv) { - nv_wr32(priv, 0x4064f0, 0x00000000); - nv_wr32(priv, 0x4064f4, 0x00000000); - nv_wr32(priv, 0x4064f8, 0x00000000); + switch (nv_device(priv)->chipset) { + case 0xd9: + case 0xd7: + nv_wr32(priv, 0x4064f0, 0x00000000); + nv_wr32(priv, 0x4064f4, 0x00000000); + nv_wr32(priv, 0x4064f8, 0x00000000); + break; + case 0xc0: + default: + break; + } } static void @@ -755,10 +763,26 @@ nvc0_graph_init_unk58xx(struct nvc0_graph_priv *priv) { nv_wr32(priv, 0x405844, 0x00ffffff); nv_wr32(priv, 0x405850, 0x00000000); - nv_wr32(priv, 0x405900, 0x00002834); + switch (nv_device(priv)->chipset) { + case 0xd9: + case 0xd7: + nv_wr32(priv, 0x405900, 0x00002834); + break; + case 0xc0: + default: + break; + } nv_wr32(priv, 0x405908, 0x00000000); - nv_wr32(priv, 0x405928, 0x00000000); - nv_wr32(priv, 0x40592c, 0x00000000); + switch (nv_device(priv)->chipset) { + case 0xd9: + case 0xd7: + nv_wr32(priv, 0x405928, 0x00000000); + nv_wr32(priv, 0x40592c, 0x00000000); + break; + case 0xc0: + default: + break; + } } static void @@ -770,19 +794,53 @@ nvc0_graph_init_unk80xx(struct nvc0_graph_priv *priv) static void nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) { - nv_wr32(priv, 0x418408, 0x00000000); + switch (nv_device(priv)->chipset) { + case 0xd9: + case 0xd7: + nv_wr32(priv, 0x418408, 0x00000000); + break; + case 0xc0: + default: + break; + } nv_wr32(priv, 0x4184a0, 0x00000000); - nv_wr32(priv, 0x4184a4, 0x00000000); - nv_wr32(priv, 0x4184a8, 0x00000000); + switch (nv_device(priv)->chipset) { + case 0xd9: + case 0xd7: + nv_wr32(priv, 0x4184a4, 0x00000000); + nv_wr32(priv, 0x4184a8, 0x00000000); + break; + case 0xc0: + default: + break; + } nv_wr32(priv, 0x418604, 0x00000000); nv_wr32(priv, 0x418680, 0x00000000); - nv_wr32(priv, 0x418714, 0x00000000); + switch (nv_device(priv)->chipset) { + case 0xd9: + case 0xd7: + nv_wr32(priv, 0x418714, 0x00000000); + break; + case 0xc0: + default: + nv_wr32(priv, 0x418714, 0x80000000); + break; + } nv_wr32(priv, 0x418384, 0x00000000); nv_wr32(priv, 0x418814, 0x00000000); nv_wr32(priv, 0x418818, 0x00000000); nv_wr32(priv, 0x41881c, 0x00000000); nv_wr32(priv, 0x418b04, 0x00000000); - nv_wr32(priv, 0x4188c8, 0x00000000); + switch (nv_device(priv)->chipset) { + case 0xd9: + case 0xd7: + nv_wr32(priv, 0x4188c8, 0x00000000); + break; + case 0xc0: + default: + nv_wr32(priv, 0x4188c8, 0x80000000); + break; + } nv_wr32(priv, 0x4188cc, 0x00000000); nv_wr32(priv, 0x4188d0, 0x00010000); nv_wr32(priv, 0x4188d4, 0x00000001); @@ -794,22 +852,63 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x418988, 0x77777777); nv_wr32(priv, 0x41898c, 0x77777777); nv_wr32(priv, 0x418c04, 0x00000000); - nv_wr32(priv, 0x418c64, 0x00000000); - nv_wr32(priv, 0x418c68, 0x00000000); + switch (nv_device(priv)->chipset) { + case 0xd9: + case 0xd7: + nv_wr32(priv, 0x418c64, 0x00000000); + nv_wr32(priv, 0x418c68, 0x00000000); + break; + case 0xc0: + default: + break; + } nv_wr32(priv, 0x418c88, 0x00000000); - nv_wr32(priv, 0x418cb4, 0x00000000); - nv_wr32(priv, 0x418cb8, 0x00000000); + switch (nv_device(priv)->chipset) { + case 0xd9: + case 0xd7: + nv_wr32(priv, 0x418cb4, 0x00000000); + nv_wr32(priv, 0x418cb8, 0x00000000); + break; + case 0xc0: + default: + break; + } nv_wr32(priv, 0x418d00, 0x00000000); - nv_wr32(priv, 0x418d28, 0x00000000); - nv_wr32(priv, 0x418d2c, 0x00000000); - nv_wr32(priv, 0x418f00, 0x00000000); + switch (nv_device(priv)->chipset) { + case 0xd9: + case 0xd7: + nv_wr32(priv, 0x418d28, 0x00000000); + nv_wr32(priv, 0x418d2c, 0x00000000); + nv_wr32(priv, 0x418f00, 0x00000000); + break; + case 0xc0: + default: + break; + } nv_wr32(priv, 0x418f08, 0x00000000); - nv_wr32(priv, 0x418f20, 0x00000000); - nv_wr32(priv, 0x418f24, 0x00000000); - nv_wr32(priv, 0x418e00, 0x00000003); + switch (nv_device(priv)->chipset) { + case 0xd9: + case 0xd7: + nv_wr32(priv, 0x418f20, 0x00000000); + nv_wr32(priv, 0x418f24, 0x00000000); + nv_wr32(priv, 0x418e00, 0x00000003); + break; + case 0xc0: + default: + nv_wr32(priv, 0x418e00, 0x00000050); + break; + } nv_wr32(priv, 0x418e08, 0x00000000); - nv_wr32(priv, 0x418e1c, 0x00000000); - nv_wr32(priv, 0x418e20, 0x00000000); + switch (nv_device(priv)->chipset) { + case 0xd9: + case 0xd7: + nv_wr32(priv, 0x418e1c, 0x00000000); + nv_wr32(priv, 0x418e20, 0x00000000); + break; + case 0xc0: + default: + break; + } nv_wr32(priv, 0x41900c, 0x00000000); nv_wr32(priv, 0x419018, 0x00000000); } @@ -821,21 +920,64 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419d0c, 0x00000000); nv_wr32(priv, 0x419d10, 0x00000014); nv_wr32(priv, 0x419ab0, 0x00000000); - nv_wr32(priv, 0x419ac8, 0x00000000); + switch (nv_device(priv)->chipset) { + case 0xd9: + case 0xd7: + nv_wr32(priv, 0x419ac8, 0x00000000); + break; + case 0xc0: + default: + break; + } nv_wr32(priv, 0x419ab8, 0x000000e7); nv_wr32(priv, 0x419abc, 0x00000000); nv_wr32(priv, 0x419ac0, 0x00000000); - nv_wr32(priv, 0x419ab4, 0x00000000); - nv_wr32(priv, 0x41980c, 0x00000010); + switch (nv_device(priv)->chipset) { + case 0xd9: + case 0xd7: + nv_wr32(priv, 0x419ab4, 0x00000000); + nv_wr32(priv, 0x41980c, 0x00000010); + break; + case 0xc0: + default: + nv_wr32(priv, 0x41980c, 0x00000000); + break; + } nv_wr32(priv, 0x419810, 0x00000000); - nv_wr32(priv, 0x419814, 0x00000004); + switch (nv_device(priv)->chipset) { + case 0xd9: + case 0xd7: + nv_wr32(priv, 0x419814, 0x00000004); + break; + case 0xc0: + default: + nv_wr32(priv, 0x419814, 0x00000000); + break; + } nv_wr32(priv, 0x419844, 0x00000000); - nv_wr32(priv, 0x41984c, 0x0000a918); + switch (nv_device(priv)->chipset) { + case 0xd9: + case 0xd7: + nv_wr32(priv, 0x41984c, 0x0000a918); + break; + case 0xc0: + default: + nv_wr32(priv, 0x41984c, 0x00005bc5); + break; + } nv_wr32(priv, 0x419850, 0x00000000); nv_wr32(priv, 0x419854, 0x00000000); nv_wr32(priv, 0x419858, 0x00000000); nv_wr32(priv, 0x41985c, 0x00000000); - nv_wr32(priv, 0x419880, 0x00000002); + switch (nv_device(priv)->chipset) { + case 0xd9: + case 0xd7: + nv_wr32(priv, 0x419880, 0x00000002); + break; + case 0xc0: + default: + break; + } nv_wr32(priv, 0x419c98, 0x00000000); nv_wr32(priv, 0x419ca8, 0x80000000); nv_wr32(priv, 0x419cb4, 0x00000000); @@ -845,25 +987,60 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419cc4, 0x00000000); nv_wr32(priv, 0x419bd4, 0x00800000); nv_wr32(priv, 0x419bdc, 0x00000000); - nv_wr32(priv, 0x419bf8, 0x00000000); - nv_wr32(priv, 0x419bfc, 0x00000000); + switch (nv_device(priv)->chipset) { + case 0xd9: + case 0xd7: + nv_wr32(priv, 0x419bf8, 0x00000000); + nv_wr32(priv, 0x419bfc, 0x00000000); + break; + case 0xc0: + default: + break; + } nv_wr32(priv, 0x419d2c, 0x00000000); - nv_wr32(priv, 0x419d48, 0x00000000); - nv_wr32(priv, 0x419d4c, 0x00000000); + switch (nv_device(priv)->chipset) { + case 0xd9: + case 0xd7: + nv_wr32(priv, 0x419d48, 0x00000000); + nv_wr32(priv, 0x419d4c, 0x00000000); + break; + case 0xc0: + default: + break; + } nv_wr32(priv, 0x419c0c, 0x00000000); nv_wr32(priv, 0x419e00, 0x00000000); nv_wr32(priv, 0x419ea0, 0x00000000); nv_wr32(priv, 0x419ea4, 0x00000100); - nv_wr32(priv, 0x419ea8, 0x02001100); + switch (nv_device(priv)->chipset) { + case 0xd9: + case 0xd7: + nv_wr32(priv, 0x419ea8, 0x02001100); + break; + case 0xc0: + default: + nv_wr32(priv, 0x419ea8, 0x00001100); + break; + } nv_wr32(priv, 0x419eac, 0x11100702); nv_wr32(priv, 0x419eb0, 0x00000003); nv_wr32(priv, 0x419eb4, 0x00000000); nv_wr32(priv, 0x419eb8, 0x00000000); nv_wr32(priv, 0x419ebc, 0x00000000); nv_wr32(priv, 0x419ec0, 0x00000000); - nv_wr32(priv, 0x419ec8, 0x0e063818); - nv_wr32(priv, 0x419ecc, 0x0e060e06); - nv_wr32(priv, 0x419ed0, 0x00003818); + switch (nv_device(priv)->chipset) { + case 0xd9: + case 0xd7: + nv_wr32(priv, 0x419ec8, 0x0e063818); + nv_wr32(priv, 0x419ecc, 0x0e060e06); + nv_wr32(priv, 0x419ed0, 0x00003818); + break; + case 0xc0: + default: + nv_wr32(priv, 0x419ec8, 0x06060618); + nv_wr32(priv, 0x419ed0, 0x0eff0e38); + break; + } nv_wr32(priv, 0x419ed4, 0x011104f1); nv_wr32(priv, 0x419edc, 0x00000000); nv_wr32(priv, 0x419f00, 0x00000000); @@ -1133,6 +1310,7 @@ nvc0_graph_init(struct nouveau_object *object) nvc0_graph_init_regs(priv); switch (nv_device(priv)->chipset) { + case 0xc0: case 0xd9: case 0xd7: nvc0_graph_init_unk40xx(priv); -- cgit v1.2.3 From 8b637ae3a3d8142db23eed3100245c2a2390358b Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 13 May 2013 14:45:56 +1000 Subject: drm/nvc3/gr: update initial register/context values Signed-off-by: Ben Skeggs --- .../gpu/drm/nouveau/core/engine/graph/ctxnvc0.c | 29 ++++++++++++++++++ .../drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc | 33 ++++++++++++++++++--- .../nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h | 34 ++++++++++++++++++---- .../drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc | 4 +-- .../nouveau/core/engine/graph/fuc/hubnvc0.fuc.h | 2 +- drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c | 22 ++++++++++++++ 6 files changed, 112 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c index 416dc9b16978..27e97c0e45ad 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c @@ -1326,6 +1326,7 @@ nvc0_grctx_generate_9097(struct nvc0_graph_priv *priv) switch (nv_device(priv)->chipset) { case 0xc0: + case 0xc3: case 0xd9: case 0xd7: break; @@ -1473,6 +1474,7 @@ nvc0_grctx_generate_dispatch(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x40402c, 0x00000000); break; case 0xc0: + case 0xc3: default: break; } @@ -1493,6 +1495,7 @@ nvc0_grctx_generate_dispatch(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x4040c8, 0xf0000087); switch (nv_device(priv)->chipset) { case 0xc0: + case 0xc3: case 0xd9: case 0xd7: nv_wr32(priv, 0x4040d0, 0x00000000); @@ -1520,6 +1523,7 @@ nvc0_grctx_generate_dispatch(struct nvc0_graph_priv *priv) case 0xd7: break; case 0xc0: + case 0xc3: default: nv_wr32(priv, 0x404174, 0x00000000); break; @@ -1662,6 +1666,7 @@ nvc0_grctx_generate_shaders(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x405834, 0x08000000); break; case 0xc0: + case 0xc3: default: nv_wr32(priv, 0x405800, 0x078000bf); nv_wr32(priv, 0x405830, 0x02180000); @@ -1703,6 +1708,7 @@ nvc0_grctx_generate_unk64xx(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x4064bc, 0x00000000); break; case 0xc0: + case 0xc3: default: break; } @@ -1714,6 +1720,7 @@ nvc0_grctx_generate_unk64xx(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x4064c4, 0x0086ffff); break; case 0xc0: + case 0xc3: default: break; } @@ -1753,6 +1760,7 @@ nvc0_grctx_generate_rop(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x408804, 0x00000040); switch (nv_device(priv)->chipset) { case 0xc0: + case 0xc3: nv_wr32(priv, 0x408808, 0x0003e00d); nv_wr32(priv, 0x408900, 0x3080b801); nv_wr32(priv, 0x408904, 0x02000001); @@ -1797,6 +1805,7 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) case 0xd7: break; case 0xc0: + case 0xc3: default: nv_wr32(priv, 0x418408, 0x00000000); break; @@ -1809,6 +1818,7 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x418414, 0x02200fff); break; case 0xc0: + case 0xc3: default: nv_wr32(priv, 0x418414, 0x00200fff); break; @@ -1833,6 +1843,7 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x41870c, 0x00000000); break; case 0xc0: + case 0xc3: default: nv_wr32(priv, 0x41870c, 0x07c80000); break; @@ -1844,6 +1855,7 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x418800, 0x7006860a); break; case 0xc0: + case 0xc3: default: nv_wr32(priv, 0x418800, 0x0006860a); break; @@ -1859,6 +1871,7 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x418830, 0x10000001); break; case 0xc0: + case 0xc3: default: nv_wr32(priv, 0x418830, 0x00000001); break; @@ -1879,6 +1892,7 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x4188fc, 0x20100008); break; case 0xc0: + case 0xc3: default: nv_wr32(priv, 0x4188fc, 0x00100000); break; @@ -1902,6 +1916,7 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x418b00, 0x00000006); break; case 0xc0: + case 0xc3: default: nv_wr32(priv, 0x418b00, 0x00000000); break; @@ -1929,6 +1944,7 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x418c6c, 0x00000001); break; case 0xc0: + case 0xc3: default: break; } @@ -1954,6 +1970,7 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419864, 0x00000129); break; case 0xc0: + case 0xc3: default: nv_wr32(priv, 0x419864, 0x0000012a); break; @@ -1968,6 +1985,7 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) switch (nv_device(priv)->chipset) { case 0xc0: break; + case 0xc3: default: nv_wr32(priv, 0x419a1c, 0x00000000); nv_wr32(priv, 0x419a20, 0x00000800); @@ -1981,6 +1999,7 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) case 0xd7: nv_wr32(priv, 0x00419ac4, 0x0017f440); break; + case 0xc3: default: nv_wr32(priv, 0x00419ac4, 0x0007f440); break; @@ -1999,6 +2018,7 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419be0, 0x00400001); break; case 0xc0: + case 0xc3: default: nv_wr32(priv, 0x419be0, 0x00000001); break; @@ -2010,6 +2030,7 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419c00, 0x0000000a); break; case 0xc0: + case 0xc3: default: nv_wr32(priv, 0x419c00, 0x00000002); break; @@ -2018,6 +2039,7 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419c08, 0x00000002); nv_wr32(priv, 0x419c20, 0x00000000); switch (nv_device(priv)->chipset) { + case 0xc3: case 0xce: case 0xcf: nv_wr32(priv, 0x419cb0, 0x00020048); @@ -2042,6 +2064,7 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419d20, 0x12180000); break; case 0xc0: + case 0xc3: default: nv_wr32(priv, 0x419d20, 0x02180000); break; @@ -2054,6 +2077,7 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419d44, 0x02180218); break; case 0xc0: + case 0xc3: default: break; } @@ -2090,6 +2114,7 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) case 0xd7: nv_wr32(priv, 0x419ee0, 0x00010110); break; + case 0xc3: default: nv_wr32(priv, 0x419ee0, 0x00011110); break; @@ -2100,6 +2125,7 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419f50, 0x00000000); nv_wr32(priv, 0x419f54, 0x00000000); break; + case 0xc3: case 0xd9: case 0xd7: nv_wr32(priv, 0x419f30, 0x00000000); @@ -2436,6 +2462,7 @@ nvc0_grctx_generate(struct nvc0_graph_priv *priv) nv_icmd(priv, i, 0x00000040); break; case 0xc0: + case 0xc3: default: break; } @@ -2454,6 +2481,7 @@ nvc0_grctx_generate(struct nvc0_graph_priv *priv) nv_icmd(priv, i, 0x0000c080); break; case 0xc0: + case 0xc3: break; default: break; @@ -3282,6 +3310,7 @@ nvc0_grctx_generate(struct nvc0_graph_priv *priv) switch (nv_device(priv)->chipset) { case 0xc0: + case 0xc3: nv_mthd(priv, 0x902d, 0x3410, 0x00000000); break; case 0xd9: diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc index 1034ff15b032..f9874a536969 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc @@ -58,10 +58,10 @@ chipsets: .b16 #nvc0_tpc_mmio_head .b16 #nvc1_tpc_mmio_tail .b8 0xc3 0 0 0 -.b16 #nvc0_gpc_mmio_head -.b16 #nvc0_gpc_mmio_tail -.b16 #nvc0_tpc_mmio_head -.b16 #nvc3_tpc_mmio_tail +.b16 #nnvc0_gpc_mmio_head +.b16 #nnvc0_gpc_mmio_tail +.b16 #nnvc3_tpc_mmio_head +.b16 #nnvc3_tpc_mmio_tail .b8 0xc4 0 0 0 .b16 #nvc0_gpc_mmio_head .b16 #nvc0_gpc_mmio_tail @@ -234,6 +234,31 @@ mmctx_data(0x000698, 1) mmctx_data(0x000750, 2) nnvc0_tpc_mmio_tail: +nnvc3_tpc_mmio_head: +mmctx_data(0x000018, 1) +mmctx_data(0x00003c, 1) +mmctx_data(0x000048, 1) +mmctx_data(0x000064, 1) +mmctx_data(0x000088, 1) +mmctx_data(0x000200, 6) +mmctx_data(0x00021c, 2) +mmctx_data(0x0002c4, 1) +mmctx_data(0x000300, 6) +mmctx_data(0x0003d0, 1) +mmctx_data(0x0003e0, 2) +mmctx_data(0x000400, 3) +mmctx_data(0x000420, 1) +mmctx_data(0x0004b0, 1) +mmctx_data(0x0004e8, 1) +mmctx_data(0x0004f4, 1) +mmctx_data(0x000520, 2) +mmctx_data(0x000604, 4) +mmctx_data(0x000644, 20) +mmctx_data(0x000698, 1) +mmctx_data(0x0006e0, 1) +mmctx_data(0x000730, 11) +nnvc3_tpc_mmio_tail: + nvd9_tpc_mmio_head: mmctx_data(0x000018, 1) mmctx_data(0x00003c, 1) diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h index 427ddf06316c..0db048131d30 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h @@ -40,8 +40,8 @@ uint32_t nvc0_grgpc_data[] = { 0x013800d4, 0x02640200, 0x000000c3, - 0x013400d4, - 0x02600200, + 0x01980138, + 0x030802b0, 0x000000c4, 0x013400d4, 0x02600200, @@ -56,10 +56,10 @@ uint32_t nvc0_grgpc_data[] = { 0x025c0200, 0x000000d9, 0x02000198, - 0x030c02b0, + 0x03640308, 0x000000d7, 0x02000198, - 0x030c02b0, + 0x03640308, 0x00000000, /* 0x00d4: nvc0_gpc_mmio_head */ 0x00000380, @@ -194,7 +194,31 @@ uint32_t nvc0_grgpc_data[] = { 0x00000698, 0x04000750, /* 0x02b0: nnvc0_tpc_mmio_tail */ -/* 0x02b0: nvd9_tpc_mmio_head */ +/* 0x02b0: nnvc3_tpc_mmio_head */ + 0x00000018, + 0x0000003c, + 0x00000048, + 0x00000064, + 0x00000088, + 0x14000200, + 0x0400021c, + 0x000002c4, + 0x14000300, + 0x000003d0, + 0x040003e0, + 0x08000400, + 0x00000420, + 0x000004b0, + 0x000004e8, + 0x000004f4, + 0x04000520, + 0x0c000604, + 0x4c000644, + 0x00000698, + 0x000006e0, + 0x28000730, +/* 0x0308: nnvc3_tpc_mmio_tail */ +/* 0x0308: nvd9_tpc_mmio_head */ 0x00000018, 0x0000003c, 0x00000048, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc index 9f0768e2719d..58e8d010ae50 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc @@ -54,8 +54,8 @@ chipsets: .b16 #nvc0_hub_mmio_head .b16 #nvc1_hub_mmio_tail .b8 0xc3 0 0 0 -.b16 #nvc0_hub_mmio_head -.b16 #nvc0_hub_mmio_tail +.b16 #nnvc0_hub_mmio_head +.b16 #nnvc0_hub_mmio_tail .b8 0xc4 0 0 0 .b16 #nvc0_hub_mmio_head .b16 #nvc0_hub_mmio_tail diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h index fc5f9727ae76..6466bcf8db97 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h @@ -207,7 +207,7 @@ uint32_t nvc0_grhub_data[] = { 0x000000c1, 0x03ec034c, 0x000000c3, - 0x03e8034c, + 0x048803ec, 0x000000c4, 0x03e8034c, 0x000000c8, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c index 9d705bab9dcd..5f39dd20842f 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c @@ -753,6 +753,7 @@ nvc0_graph_init_unk64xx(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x4064f8, 0x00000000); break; case 0xc0: + case 0xc3: default: break; } @@ -764,6 +765,7 @@ nvc0_graph_init_unk58xx(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x405844, 0x00ffffff); nv_wr32(priv, 0x405850, 0x00000000); switch (nv_device(priv)->chipset) { + case 0xc3: case 0xd9: case 0xd7: nv_wr32(priv, 0x405900, 0x00002834); @@ -780,6 +782,7 @@ nvc0_graph_init_unk58xx(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x40592c, 0x00000000); break; case 0xc0: + case 0xc3: default: break; } @@ -800,6 +803,7 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x418408, 0x00000000); break; case 0xc0: + case 0xc3: default: break; } @@ -811,6 +815,7 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x4184a8, 0x00000000); break; case 0xc0: + case 0xc3: default: break; } @@ -822,6 +827,7 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x418714, 0x00000000); break; case 0xc0: + case 0xc3: default: nv_wr32(priv, 0x418714, 0x80000000); break; @@ -837,6 +843,7 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x4188c8, 0x00000000); break; case 0xc0: + case 0xc3: default: nv_wr32(priv, 0x4188c8, 0x80000000); break; @@ -859,6 +866,7 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x418c68, 0x00000000); break; case 0xc0: + case 0xc3: default: break; } @@ -870,6 +878,7 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x418cb8, 0x00000000); break; case 0xc0: + case 0xc3: default: break; } @@ -882,6 +891,7 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x418f00, 0x00000000); break; case 0xc0: + case 0xc3: default: break; } @@ -894,6 +904,7 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x418e00, 0x00000003); break; case 0xc0: + case 0xc3: default: nv_wr32(priv, 0x418e00, 0x00000050); break; @@ -906,6 +917,7 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x418e20, 0x00000000); break; case 0xc0: + case 0xc3: default: break; } @@ -921,6 +933,7 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419d10, 0x00000014); nv_wr32(priv, 0x419ab0, 0x00000000); switch (nv_device(priv)->chipset) { + case 0xc3: case 0xd9: case 0xd7: nv_wr32(priv, 0x419ac8, 0x00000000); @@ -939,6 +952,7 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x41980c, 0x00000010); break; case 0xc0: + case 0xc3: default: nv_wr32(priv, 0x41980c, 0x00000000); break; @@ -950,6 +964,7 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419814, 0x00000004); break; case 0xc0: + case 0xc3: default: nv_wr32(priv, 0x419814, 0x00000000); break; @@ -961,6 +976,7 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x41984c, 0x0000a918); break; case 0xc0: + case 0xc3: default: nv_wr32(priv, 0x41984c, 0x00005bc5); break; @@ -970,6 +986,7 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419858, 0x00000000); nv_wr32(priv, 0x41985c, 0x00000000); switch (nv_device(priv)->chipset) { + case 0xc3: case 0xd9: case 0xd7: nv_wr32(priv, 0x419880, 0x00000002); @@ -994,6 +1011,7 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419bfc, 0x00000000); break; case 0xc0: + case 0xc3: default: break; } @@ -1005,6 +1023,7 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419d4c, 0x00000000); break; case 0xc0: + case 0xc3: default: break; } @@ -1018,6 +1037,7 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419ea8, 0x02001100); break; case 0xc0: + case 0xc3: default: nv_wr32(priv, 0x419ea8, 0x00001100); break; @@ -1029,6 +1049,7 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419ebc, 0x00000000); nv_wr32(priv, 0x419ec0, 0x00000000); switch (nv_device(priv)->chipset) { + case 0xc3: case 0xd9: case 0xd7: nv_wr32(priv, 0x419ec8, 0x0e063818); @@ -1311,6 +1332,7 @@ nvc0_graph_init(struct nouveau_object *object) switch (nv_device(priv)->chipset) { case 0xc0: + case 0xc3: case 0xd9: case 0xd7: nvc0_graph_init_unk40xx(priv); -- cgit v1.2.3 From 58ef23056ae0bc060086f71ad04254e188a30ff0 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 13 May 2013 18:29:02 +1000 Subject: drm/nvc1/gr: update initial register/context values Signed-off-by: Ben Skeggs --- .../gpu/drm/nouveau/core/engine/graph/ctxnvc0.c | 24 +++++++++++++++- .../drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc | 16 +++++------ .../nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h | 32 +++++++++++----------- .../drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc | 6 ++-- .../nouveau/core/engine/graph/fuc/hubnvc0.fuc.h | 14 +++++----- drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c | 23 ++++++++++++++++ 6 files changed, 80 insertions(+), 35 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c index 27e97c0e45ad..f98d0878dd12 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c @@ -1327,6 +1327,7 @@ nvc0_grctx_generate_9097(struct nvc0_graph_priv *priv) switch (nv_device(priv)->chipset) { case 0xc0: case 0xc3: + case 0xc1: case 0xd9: case 0xd7: break; @@ -1475,6 +1476,7 @@ nvc0_grctx_generate_dispatch(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc1: default: break; } @@ -1496,6 +1498,7 @@ nvc0_grctx_generate_dispatch(struct nvc0_graph_priv *priv) switch (nv_device(priv)->chipset) { case 0xc0: case 0xc3: + case 0xc1: case 0xd9: case 0xd7: nv_wr32(priv, 0x4040d0, 0x00000000); @@ -1524,6 +1527,7 @@ nvc0_grctx_generate_dispatch(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc1: default: nv_wr32(priv, 0x404174, 0x00000000); break; @@ -1709,6 +1713,7 @@ nvc0_grctx_generate_unk64xx(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc1: default: break; } @@ -1771,7 +1776,6 @@ nvc0_grctx_generate_rop(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x408900, 0x3080b801); nv_wr32(priv, 0x408904, 0x62000001); nv_wr32(priv, 0x408908, 0x00c80929); - nv_wr32(priv, 0x40890c, 0x00000000); break; case 0xd9: case 0xd7: @@ -1806,6 +1810,7 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc1: default: nv_wr32(priv, 0x418408, 0x00000000); break; @@ -1819,6 +1824,7 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc1: default: nv_wr32(priv, 0x418414, 0x00200fff); break; @@ -1844,6 +1850,7 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc1: default: nv_wr32(priv, 0x41870c, 0x07c80000); break; @@ -1856,6 +1863,7 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc1: default: nv_wr32(priv, 0x418800, 0x0006860a); break; @@ -1917,6 +1925,7 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc1: default: nv_wr32(priv, 0x418b00, 0x00000000); break; @@ -1986,6 +1995,7 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) case 0xc0: break; case 0xc3: + case 0xc1: default: nv_wr32(priv, 0x419a1c, 0x00000000); nv_wr32(priv, 0x419a20, 0x00000800); @@ -2000,6 +2010,7 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x00419ac4, 0x0017f440); break; case 0xc3: + case 0xc1: default: nv_wr32(priv, 0x00419ac4, 0x0007f440); break; @@ -2031,6 +2042,7 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc1: default: nv_wr32(priv, 0x419c00, 0x00000002); break; @@ -2040,6 +2052,7 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419c20, 0x00000000); switch (nv_device(priv)->chipset) { case 0xc3: + case 0xc1: case 0xce: case 0xcf: nv_wr32(priv, 0x419cb0, 0x00020048); @@ -2115,6 +2128,7 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419ee0, 0x00010110); break; case 0xc3: + case 0xc1: default: nv_wr32(priv, 0x419ee0, 0x00011110); break; @@ -2126,6 +2140,7 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419f54, 0x00000000); break; case 0xc3: + case 0xc1: case 0xd9: case 0xd7: nv_wr32(priv, 0x419f30, 0x00000000); @@ -2463,6 +2478,7 @@ nvc0_grctx_generate(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc1: default: break; } @@ -2482,6 +2498,7 @@ nvc0_grctx_generate(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc1: break; default: break; @@ -3049,11 +3066,13 @@ nvc0_grctx_generate(struct nvc0_graph_priv *priv) nv_icmd(priv, 0x00000585, 0x0000003f); nv_icmd(priv, 0x00000576, 0x00000003); switch (nv_device(priv)->chipset) { + case 0xc1: case 0xd9: case 0xd7: nv_icmd(priv, 0x0000057b, 0x00000059); break; case 0xc0: + case 0xc3: default: break; } @@ -3163,6 +3182,8 @@ nvc0_grctx_generate(struct nvc0_graph_priv *priv) nv_icmd(priv, 0x0000097d, 0x00000020); break; case 0xc0: + case 0xc3: + case 0xc1: default: break; } @@ -3311,6 +3332,7 @@ nvc0_grctx_generate(struct nvc0_graph_priv *priv) switch (nv_device(priv)->chipset) { case 0xc0: case 0xc3: + case 0xc1: nv_mthd(priv, 0x902d, 0x3410, 0x00000000); break; case 0xd9: diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc index f9874a536969..79a3a501180a 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc @@ -53,10 +53,10 @@ chipsets: .b16 #nnvc0_tpc_mmio_head .b16 #nnvc0_tpc_mmio_tail .b8 0xc1 0 0 0 -.b16 #nvc0_gpc_mmio_head -.b16 #nvc1_gpc_mmio_tail -.b16 #nvc0_tpc_mmio_head -.b16 #nvc1_tpc_mmio_tail +.b16 #nnvc0_gpc_mmio_head +.b16 #nnvc1_gpc_mmio_tail +.b16 #nnvc3_tpc_mmio_head +.b16 #nnvc1_tpc_mmio_tail .b8 0xc3 0 0 0 .b16 #nnvc0_gpc_mmio_head .b16 #nnvc0_gpc_mmio_tail @@ -121,8 +121,6 @@ mmctx_data(0x000c8c, 1) mmctx_data(0x001000, 3) mmctx_data(0x001014, 1) nvc0_gpc_mmio_tail: -mmctx_data(0x000c6c, 1); -nvc1_gpc_mmio_tail: nnvc0_gpc_mmio_head: mmctx_data(0x000380, 1) @@ -150,6 +148,8 @@ mmctx_data(0x000c8c, 1) mmctx_data(0x001000, 3) mmctx_data(0x001014, 1) nnvc0_gpc_mmio_tail: +mmctx_data(0x000c6c, 1); +nnvc1_gpc_mmio_tail: nvd9_gpc_mmio_head: mmctx_data(0x000380, 1) @@ -209,8 +209,6 @@ mmctx_data(0x0006e0, 1) nvcf_tpc_mmio_tail: mmctx_data(0x0004bc, 1) nvc3_tpc_mmio_tail: -mmctx_data(0x000544, 1) -nvc1_tpc_mmio_tail: nnvc0_tpc_mmio_head: mmctx_data(0x000018, 1) @@ -258,6 +256,8 @@ mmctx_data(0x000698, 1) mmctx_data(0x0006e0, 1) mmctx_data(0x000730, 11) nnvc3_tpc_mmio_tail: +mmctx_data(0x000544, 1) +nnvc1_tpc_mmio_tail: nvd9_tpc_mmio_head: mmctx_data(0x000018, 1) diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h index 0db048131d30..1a16cbf561c3 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h @@ -34,14 +34,14 @@ uint32_t nvc0_grgpc_data[] = { 0x00000000, /* 0x0064: chipsets */ 0x000000c0, - 0x01980138, - 0x02b00264, + 0x01940134, + 0x02ac0260, 0x000000c1, - 0x013800d4, - 0x02640200, + 0x01980134, + 0x030802ac, 0x000000c3, - 0x01980138, - 0x030802b0, + 0x01940134, + 0x030402ac, 0x000000c4, 0x013400d4, 0x02600200, @@ -87,9 +87,7 @@ uint32_t nvc0_grgpc_data[] = { 0x08001000, 0x00001014, /* 0x0134: nvc0_gpc_mmio_tail */ - 0x00000c6c, -/* 0x0138: nvc1_gpc_mmio_tail */ -/* 0x0138: nnvc0_gpc_mmio_head */ +/* 0x0134: nnvc0_gpc_mmio_head */ 0x00000380, 0x14000400, 0x20000450, @@ -114,7 +112,9 @@ uint32_t nvc0_grgpc_data[] = { 0x00000c8c, 0x08001000, 0x00001014, -/* 0x0198: nnvc0_gpc_mmio_tail */ +/* 0x0194: nnvc0_gpc_mmio_tail */ + 0x00000c6c, +/* 0x0198: nnvc1_gpc_mmio_tail */ /* 0x0198: nvd9_gpc_mmio_head */ 0x00000380, 0x04000400, @@ -171,9 +171,7 @@ uint32_t nvc0_grgpc_data[] = { /* 0x025c: nvcf_tpc_mmio_tail */ 0x000004bc, /* 0x0260: nvc3_tpc_mmio_tail */ - 0x00000544, -/* 0x0264: nvc1_tpc_mmio_tail */ -/* 0x0264: nnvc0_tpc_mmio_head */ +/* 0x0260: nnvc0_tpc_mmio_head */ 0x00000018, 0x0000003c, 0x00000048, @@ -193,8 +191,8 @@ uint32_t nvc0_grgpc_data[] = { 0x4c000644, 0x00000698, 0x04000750, -/* 0x02b0: nnvc0_tpc_mmio_tail */ -/* 0x02b0: nnvc3_tpc_mmio_head */ +/* 0x02ac: nnvc0_tpc_mmio_tail */ +/* 0x02ac: nnvc3_tpc_mmio_head */ 0x00000018, 0x0000003c, 0x00000048, @@ -217,7 +215,9 @@ uint32_t nvc0_grgpc_data[] = { 0x00000698, 0x000006e0, 0x28000730, -/* 0x0308: nnvc3_tpc_mmio_tail */ +/* 0x0304: nnvc3_tpc_mmio_tail */ + 0x00000544, +/* 0x0308: nnvc1_tpc_mmio_tail */ /* 0x0308: nvd9_tpc_mmio_head */ 0x00000018, 0x0000003c, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc index 58e8d010ae50..5305b0928e82 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc @@ -51,7 +51,7 @@ chipsets: .b16 #nnvc0_hub_mmio_head .b16 #nnvc0_hub_mmio_tail .b8 0xc1 0 0 0 -.b16 #nvc0_hub_mmio_head +.b16 #nnvc0_hub_mmio_head .b16 #nvc1_hub_mmio_tail .b8 0xc3 0 0 0 .b16 #nnvc0_hub_mmio_head @@ -117,8 +117,6 @@ mmctx_data(0x408800, 3) mmctx_data(0x408900, 4) mmctx_data(0x408980, 1) nvc0_hub_mmio_tail: -mmctx_data(0x4064c0, 2) -nvc1_hub_mmio_tail: nnvc0_hub_mmio_head: mmctx_data(0x17e91c, 2) @@ -161,6 +159,8 @@ mmctx_data(0x408800, 3) mmctx_data(0x408900, 3) mmctx_data(0x408980, 1) nnvc0_hub_mmio_tail: +mmctx_data(0x4064c0, 2) +nvc1_hub_mmio_tail: nvd9_hub_mmio_head: mmctx_data(0x17e91c, 2) diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h index 6466bcf8db97..1cdf9e991a3f 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h @@ -203,11 +203,11 @@ uint32_t nvc0_grhub_data[] = { 0x00000000, /* 0x0300: chipsets */ 0x000000c0, - 0x048803ec, + 0x048403e8, 0x000000c1, - 0x03ec034c, + 0x048803e8, 0x000000c3, - 0x048803ec, + 0x048403e8, 0x000000c4, 0x03e8034c, 0x000000c8, @@ -262,9 +262,7 @@ uint32_t nvc0_grhub_data[] = { 0x0c408900, 0x00408980, /* 0x03e8: nvc0_hub_mmio_tail */ - 0x044064c0, -/* 0x03ec: nvc1_hub_mmio_tail */ -/* 0x03ec: nnvc0_hub_mmio_head */ +/* 0x03e8: nnvc0_hub_mmio_head */ 0x0417e91c, 0x04400204, 0x28404004, @@ -304,7 +302,9 @@ uint32_t nvc0_grhub_data[] = { 0x08408800, 0x08408900, 0x00408980, -/* 0x0488: nnvc0_hub_mmio_tail */ +/* 0x0484: nnvc0_hub_mmio_tail */ + 0x044064c0, +/* 0x0488: nvc1_hub_mmio_tail */ /* 0x0488: nvd9_hub_mmio_head */ 0x0417e91c, 0x04400204, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c index 5f39dd20842f..18bcc34e9f81 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c @@ -754,6 +754,7 @@ nvc0_graph_init_unk64xx(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc1: default: break; } @@ -766,6 +767,7 @@ nvc0_graph_init_unk58xx(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x405850, 0x00000000); switch (nv_device(priv)->chipset) { case 0xc3: + case 0xc1: case 0xd9: case 0xd7: nv_wr32(priv, 0x405900, 0x00002834); @@ -783,6 +785,7 @@ nvc0_graph_init_unk58xx(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc1: default: break; } @@ -804,6 +807,7 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc1: default: break; } @@ -816,6 +820,7 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc1: default: break; } @@ -824,6 +829,7 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) switch (nv_device(priv)->chipset) { case 0xd9: case 0xd7: + case 0xc1: nv_wr32(priv, 0x418714, 0x00000000); break; case 0xc0: @@ -840,6 +846,7 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) switch (nv_device(priv)->chipset) { case 0xd9: case 0xd7: + case 0xc1: nv_wr32(priv, 0x4188c8, 0x00000000); break; case 0xc0: @@ -867,6 +874,7 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc1: default: break; } @@ -879,6 +887,7 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc1: default: break; } @@ -892,6 +901,7 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc1: default: break; } @@ -901,6 +911,8 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) case 0xd7: nv_wr32(priv, 0x418f20, 0x00000000); nv_wr32(priv, 0x418f24, 0x00000000); + /*fall-through*/ + case 0xc1: nv_wr32(priv, 0x418e00, 0x00000003); break; case 0xc0: @@ -918,6 +930,7 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc1: default: break; } @@ -934,6 +947,7 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419ab0, 0x00000000); switch (nv_device(priv)->chipset) { case 0xc3: + case 0xc1: case 0xd9: case 0xd7: nv_wr32(priv, 0x419ac8, 0x00000000); @@ -953,6 +967,7 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc1: default: nv_wr32(priv, 0x41980c, 0x00000000); break; @@ -961,6 +976,7 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) switch (nv_device(priv)->chipset) { case 0xd9: case 0xd7: + case 0xc1: nv_wr32(priv, 0x419814, 0x00000004); break; case 0xc0: @@ -977,6 +993,7 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc1: default: nv_wr32(priv, 0x41984c, 0x00005bc5); break; @@ -987,6 +1004,7 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x41985c, 0x00000000); switch (nv_device(priv)->chipset) { case 0xc3: + case 0xc1: case 0xd9: case 0xd7: nv_wr32(priv, 0x419880, 0x00000002); @@ -1012,6 +1030,7 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc1: default: break; } @@ -1024,6 +1043,7 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc1: default: break; } @@ -1038,6 +1058,7 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc1: default: nv_wr32(priv, 0x419ea8, 0x00001100); break; @@ -1050,6 +1071,7 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419ec0, 0x00000000); switch (nv_device(priv)->chipset) { case 0xc3: + case 0xc1: case 0xd9: case 0xd7: nv_wr32(priv, 0x419ec8, 0x0e063818); @@ -1333,6 +1355,7 @@ nvc0_graph_init(struct nouveau_object *object) switch (nv_device(priv)->chipset) { case 0xc0: case 0xc3: + case 0xc1: case 0xd9: case 0xd7: nvc0_graph_init_unk40xx(priv); -- cgit v1.2.3 From dba50728fdf22d9c7e7d2cac7fc5d2e8715aadcd Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 14 May 2013 09:23:52 +1000 Subject: drm/nvc4/gr: update initial register/context values Signed-off-by: Ben Skeggs --- .../gpu/drm/nouveau/core/engine/graph/ctxnvc0.c | 31 ++++++++++++++++++++++ .../drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc | 8 +++--- .../nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h | 4 +-- .../drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc | 4 +-- .../nouveau/core/engine/graph/fuc/hubnvc0.fuc.h | 2 +- drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c | 22 +++++++++++++++ 6 files changed, 62 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c index f98d0878dd12..52712fbc856b 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c @@ -1327,6 +1327,7 @@ nvc0_grctx_generate_9097(struct nvc0_graph_priv *priv) switch (nv_device(priv)->chipset) { case 0xc0: case 0xc3: + case 0xc4: case 0xc1: case 0xd9: case 0xd7: @@ -1476,6 +1477,7 @@ nvc0_grctx_generate_dispatch(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc4: case 0xc1: default: break; @@ -1498,6 +1500,7 @@ nvc0_grctx_generate_dispatch(struct nvc0_graph_priv *priv) switch (nv_device(priv)->chipset) { case 0xc0: case 0xc3: + case 0xc4: case 0xc1: case 0xd9: case 0xd7: @@ -1527,6 +1530,7 @@ nvc0_grctx_generate_dispatch(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc4: case 0xc1: default: nv_wr32(priv, 0x404174, 0x00000000); @@ -1671,6 +1675,7 @@ nvc0_grctx_generate_shaders(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc4: default: nv_wr32(priv, 0x405800, 0x078000bf); nv_wr32(priv, 0x405830, 0x02180000); @@ -1713,6 +1718,7 @@ nvc0_grctx_generate_unk64xx(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc4: case 0xc1: default: break; @@ -1726,6 +1732,7 @@ nvc0_grctx_generate_unk64xx(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc4: default: break; } @@ -1766,6 +1773,7 @@ nvc0_grctx_generate_rop(struct nvc0_graph_priv *priv) switch (nv_device(priv)->chipset) { case 0xc0: case 0xc3: + case 0xc4: nv_wr32(priv, 0x408808, 0x0003e00d); nv_wr32(priv, 0x408900, 0x3080b801); nv_wr32(priv, 0x408904, 0x02000001); @@ -1810,6 +1818,7 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc4: case 0xc1: default: nv_wr32(priv, 0x418408, 0x00000000); @@ -1824,6 +1833,7 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc4: case 0xc1: default: nv_wr32(priv, 0x418414, 0x00200fff); @@ -1850,6 +1860,7 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc4: case 0xc1: default: nv_wr32(priv, 0x41870c, 0x07c80000); @@ -1863,6 +1874,7 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc4: case 0xc1: default: nv_wr32(priv, 0x418800, 0x0006860a); @@ -1880,6 +1892,7 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc4: default: nv_wr32(priv, 0x418830, 0x00000001); break; @@ -1901,6 +1914,7 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc4: default: nv_wr32(priv, 0x4188fc, 0x00100000); break; @@ -1925,6 +1939,7 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc4: case 0xc1: default: nv_wr32(priv, 0x418b00, 0x00000000); @@ -1954,6 +1969,7 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc4: default: break; } @@ -1980,6 +1996,7 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc4: default: nv_wr32(priv, 0x419864, 0x0000012a); break; @@ -1995,6 +2012,7 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) case 0xc0: break; case 0xc3: + case 0xc4: case 0xc1: default: nv_wr32(priv, 0x419a1c, 0x00000000); @@ -2010,6 +2028,7 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x00419ac4, 0x0017f440); break; case 0xc3: + case 0xc4: case 0xc1: default: nv_wr32(priv, 0x00419ac4, 0x0007f440); @@ -2030,6 +2049,7 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc4: default: nv_wr32(priv, 0x419be0, 0x00000001); break; @@ -2042,6 +2062,7 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc4: case 0xc1: default: nv_wr32(priv, 0x419c00, 0x00000002); @@ -2052,6 +2073,7 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419c20, 0x00000000); switch (nv_device(priv)->chipset) { case 0xc3: + case 0xc4: case 0xc1: case 0xce: case 0xcf: @@ -2078,6 +2100,7 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc4: default: nv_wr32(priv, 0x419d20, 0x02180000); break; @@ -2091,6 +2114,7 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc4: default: break; } @@ -2128,6 +2152,7 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419ee0, 0x00010110); break; case 0xc3: + case 0xc4: case 0xc1: default: nv_wr32(priv, 0x419ee0, 0x00011110); @@ -2140,6 +2165,7 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419f54, 0x00000000); break; case 0xc3: + case 0xc4: case 0xc1: case 0xd9: case 0xd7: @@ -2478,6 +2504,7 @@ nvc0_grctx_generate(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc4: case 0xc1: default: break; @@ -2498,6 +2525,7 @@ nvc0_grctx_generate(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc4: case 0xc1: break; default: @@ -3073,6 +3101,7 @@ nvc0_grctx_generate(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc4: default: break; } @@ -3183,6 +3212,7 @@ nvc0_grctx_generate(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc4: case 0xc1: default: break; @@ -3332,6 +3362,7 @@ nvc0_grctx_generate(struct nvc0_graph_priv *priv) switch (nv_device(priv)->chipset) { case 0xc0: case 0xc3: + case 0xc4: case 0xc1: nv_mthd(priv, 0x902d, 0x3410, 0x00000000); break; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc index 79a3a501180a..c5dd2f68d41c 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc @@ -63,10 +63,10 @@ chipsets: .b16 #nnvc3_tpc_mmio_head .b16 #nnvc3_tpc_mmio_tail .b8 0xc4 0 0 0 -.b16 #nvc0_gpc_mmio_head -.b16 #nvc0_gpc_mmio_tail -.b16 #nvc0_tpc_mmio_head -.b16 #nvc3_tpc_mmio_tail +.b16 #nnvc0_gpc_mmio_head +.b16 #nnvc0_gpc_mmio_tail +.b16 #nnvc3_tpc_mmio_head +.b16 #nnvc3_tpc_mmio_tail .b8 0xc8 0 0 0 .b16 #nvc0_gpc_mmio_head .b16 #nvc0_gpc_mmio_tail diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h index 1a16cbf561c3..f3a560ce438e 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h @@ -43,8 +43,8 @@ uint32_t nvc0_grgpc_data[] = { 0x01940134, 0x030402ac, 0x000000c4, - 0x013400d4, - 0x02600200, + 0x01940134, + 0x030402ac, 0x000000c8, 0x013400d4, 0x02500200, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc index 5305b0928e82..92df038abc48 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc @@ -57,8 +57,8 @@ chipsets: .b16 #nnvc0_hub_mmio_head .b16 #nnvc0_hub_mmio_tail .b8 0xc4 0 0 0 -.b16 #nvc0_hub_mmio_head -.b16 #nvc0_hub_mmio_tail +.b16 #nnvc0_hub_mmio_head +.b16 #nnvc0_hub_mmio_tail .b8 0xc8 0 0 0 .b16 #nvc0_hub_mmio_head .b16 #nvc0_hub_mmio_tail diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h index 1cdf9e991a3f..bf4f4b32e25b 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h @@ -209,7 +209,7 @@ uint32_t nvc0_grhub_data[] = { 0x000000c3, 0x048403e8, 0x000000c4, - 0x03e8034c, + 0x048403e8, 0x000000c8, 0x03e8034c, 0x000000ce, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c index 18bcc34e9f81..664747d62906 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c @@ -754,6 +754,7 @@ nvc0_graph_init_unk64xx(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc4: case 0xc1: default: break; @@ -767,6 +768,7 @@ nvc0_graph_init_unk58xx(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x405850, 0x00000000); switch (nv_device(priv)->chipset) { case 0xc3: + case 0xc4: case 0xc1: case 0xd9: case 0xd7: @@ -785,6 +787,7 @@ nvc0_graph_init_unk58xx(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc4: case 0xc1: default: break; @@ -807,6 +810,7 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc4: case 0xc1: default: break; @@ -820,6 +824,7 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc4: case 0xc1: default: break; @@ -834,6 +839,7 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc4: default: nv_wr32(priv, 0x418714, 0x80000000); break; @@ -851,6 +857,7 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc4: default: nv_wr32(priv, 0x4188c8, 0x80000000); break; @@ -874,6 +881,7 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc4: case 0xc1: default: break; @@ -887,6 +895,7 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc4: case 0xc1: default: break; @@ -901,6 +910,7 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc4: case 0xc1: default: break; @@ -917,6 +927,7 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc4: default: nv_wr32(priv, 0x418e00, 0x00000050); break; @@ -930,6 +941,7 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc4: case 0xc1: default: break; @@ -947,6 +959,7 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419ab0, 0x00000000); switch (nv_device(priv)->chipset) { case 0xc3: + case 0xc4: case 0xc1: case 0xd9: case 0xd7: @@ -967,6 +980,7 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc4: case 0xc1: default: nv_wr32(priv, 0x41980c, 0x00000000); @@ -981,6 +995,7 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc4: default: nv_wr32(priv, 0x419814, 0x00000000); break; @@ -993,6 +1008,7 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc4: case 0xc1: default: nv_wr32(priv, 0x41984c, 0x00005bc5); @@ -1004,6 +1020,7 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x41985c, 0x00000000); switch (nv_device(priv)->chipset) { case 0xc3: + case 0xc4: case 0xc1: case 0xd9: case 0xd7: @@ -1030,6 +1047,7 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc4: case 0xc1: default: break; @@ -1043,6 +1061,7 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc4: case 0xc1: default: break; @@ -1058,6 +1077,7 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc3: + case 0xc4: case 0xc1: default: nv_wr32(priv, 0x419ea8, 0x00001100); @@ -1071,6 +1091,7 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419ec0, 0x00000000); switch (nv_device(priv)->chipset) { case 0xc3: + case 0xc4: case 0xc1: case 0xd9: case 0xd7: @@ -1355,6 +1376,7 @@ nvc0_graph_init(struct nouveau_object *object) switch (nv_device(priv)->chipset) { case 0xc0: case 0xc3: + case 0xc4: case 0xc1: case 0xd9: case 0xd7: -- cgit v1.2.3 From eb12f57be6f457d317562fda251214d1851134fc Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 14 May 2013 10:54:32 +1000 Subject: drm/nvc8/gr: update initial register/context values Signed-off-by: Ben Skeggs --- .../gpu/drm/nouveau/core/engine/graph/ctxnvc0.c | 28 ++++++++++++++++ .../drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc | 8 ++--- .../nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h | 4 +-- .../drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc | 4 +-- .../nouveau/core/engine/graph/fuc/hubnvc0.fuc.h | 2 +- drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c | 38 +++++++++++++++++++++- 6 files changed, 74 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c index 52712fbc856b..e0305bd8eedb 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c @@ -1329,6 +1329,7 @@ nvc0_grctx_generate_9097(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc1: + case 0xc8: case 0xd9: case 0xd7: break; @@ -1479,6 +1480,7 @@ nvc0_grctx_generate_dispatch(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc1: + case 0xc8: default: break; } @@ -1502,6 +1504,7 @@ nvc0_grctx_generate_dispatch(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc1: + case 0xc8: case 0xd9: case 0xd7: nv_wr32(priv, 0x4040d0, 0x00000000); @@ -1532,6 +1535,7 @@ nvc0_grctx_generate_dispatch(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc1: + case 0xc8: default: nv_wr32(priv, 0x404174, 0x00000000); break; @@ -1676,6 +1680,7 @@ nvc0_grctx_generate_shaders(struct nvc0_graph_priv *priv) case 0xc0: case 0xc3: case 0xc4: + case 0xc8: default: nv_wr32(priv, 0x405800, 0x078000bf); nv_wr32(priv, 0x405830, 0x02180000); @@ -1720,6 +1725,7 @@ nvc0_grctx_generate_unk64xx(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc1: + case 0xc8: default: break; } @@ -1733,6 +1739,7 @@ nvc0_grctx_generate_unk64xx(struct nvc0_graph_priv *priv) case 0xc0: case 0xc3: case 0xc4: + case 0xc8: default: break; } @@ -1774,6 +1781,7 @@ nvc0_grctx_generate_rop(struct nvc0_graph_priv *priv) case 0xc0: case 0xc3: case 0xc4: + case 0xc8: nv_wr32(priv, 0x408808, 0x0003e00d); nv_wr32(priv, 0x408900, 0x3080b801); nv_wr32(priv, 0x408904, 0x02000001); @@ -1820,6 +1828,7 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc1: + case 0xc8: default: nv_wr32(priv, 0x418408, 0x00000000); break; @@ -1835,6 +1844,7 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc1: + case 0xc8: default: nv_wr32(priv, 0x418414, 0x00200fff); break; @@ -1862,6 +1872,7 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc1: + case 0xc8: default: nv_wr32(priv, 0x41870c, 0x07c80000); break; @@ -1876,6 +1887,7 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc1: + case 0xc8: default: nv_wr32(priv, 0x418800, 0x0006860a); break; @@ -1893,6 +1905,7 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) case 0xc0: case 0xc3: case 0xc4: + case 0xc8: default: nv_wr32(priv, 0x418830, 0x00000001); break; @@ -1915,6 +1928,7 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) case 0xc0: case 0xc3: case 0xc4: + case 0xc8: default: nv_wr32(priv, 0x4188fc, 0x00100000); break; @@ -1941,6 +1955,7 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc1: + case 0xc8: default: nv_wr32(priv, 0x418b00, 0x00000000); break; @@ -1970,6 +1985,7 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) case 0xc0: case 0xc3: case 0xc4: + case 0xc8: default: break; } @@ -1997,6 +2013,7 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) case 0xc0: case 0xc3: case 0xc4: + case 0xc8: default: nv_wr32(priv, 0x419864, 0x0000012a); break; @@ -2014,6 +2031,7 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc1: + case 0xc8: default: nv_wr32(priv, 0x419a1c, 0x00000000); nv_wr32(priv, 0x419a20, 0x00000800); @@ -2050,6 +2068,7 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) case 0xc0: case 0xc3: case 0xc4: + case 0xc8: default: nv_wr32(priv, 0x419be0, 0x00000001); break; @@ -2064,6 +2083,7 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc1: + case 0xc8: default: nv_wr32(priv, 0x419c00, 0x00000002); break; @@ -2086,6 +2106,7 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419cb0, 0x00020048); break; case 0xc0: + case 0xc8: default: nv_wr32(priv, 0x419cb0, 0x00060048); break; @@ -2101,6 +2122,7 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) case 0xc0: case 0xc3: case 0xc4: + case 0xc8: default: nv_wr32(priv, 0x419d20, 0x02180000); break; @@ -2115,6 +2137,7 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) case 0xc0: case 0xc3: case 0xc4: + case 0xc8: default: break; } @@ -2506,6 +2529,7 @@ nvc0_grctx_generate(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc1: + case 0xc8: default: break; } @@ -2527,6 +2551,7 @@ nvc0_grctx_generate(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc1: + case 0xc8: break; default: break; @@ -3095,6 +3120,7 @@ nvc0_grctx_generate(struct nvc0_graph_priv *priv) nv_icmd(priv, 0x00000576, 0x00000003); switch (nv_device(priv)->chipset) { case 0xc1: + case 0xc8: case 0xd9: case 0xd7: nv_icmd(priv, 0x0000057b, 0x00000059); @@ -3208,6 +3234,7 @@ nvc0_grctx_generate(struct nvc0_graph_priv *priv) switch (nv_device(priv)->chipset) { case 0xd9: case 0xd7: + case 0xc8: nv_icmd(priv, 0x0000097d, 0x00000020); break; case 0xc0: @@ -3364,6 +3391,7 @@ nvc0_grctx_generate(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc1: + case 0xc8: nv_mthd(priv, 0x902d, 0x3410, 0x00000000); break; case 0xd9: diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc index c5dd2f68d41c..4539e33174b7 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc @@ -68,10 +68,10 @@ chipsets: .b16 #nnvc3_tpc_mmio_head .b16 #nnvc3_tpc_mmio_tail .b8 0xc8 0 0 0 -.b16 #nvc0_gpc_mmio_head -.b16 #nvc0_gpc_mmio_tail -.b16 #nvc0_tpc_mmio_head -.b16 #nvc0_tpc_mmio_tail +.b16 #nnvc0_gpc_mmio_head +.b16 #nnvc0_gpc_mmio_tail +.b16 #nnvc0_tpc_mmio_head +.b16 #nnvc0_tpc_mmio_tail .b8 0xce 0 0 0 .b16 #nvc0_gpc_mmio_head .b16 #nvc0_gpc_mmio_tail diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h index f3a560ce438e..bad9a16a9463 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h @@ -46,8 +46,8 @@ uint32_t nvc0_grgpc_data[] = { 0x01940134, 0x030402ac, 0x000000c8, - 0x013400d4, - 0x02500200, + 0x01940134, + 0x02ac0260, 0x000000ce, 0x013400d4, 0x02600200, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc index 92df038abc48..6eb5168a3811 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc @@ -60,8 +60,8 @@ chipsets: .b16 #nnvc0_hub_mmio_head .b16 #nnvc0_hub_mmio_tail .b8 0xc8 0 0 0 -.b16 #nvc0_hub_mmio_head -.b16 #nvc0_hub_mmio_tail +.b16 #nnvc0_hub_mmio_head +.b16 #nnvc0_hub_mmio_tail .b8 0xce 0 0 0 .b16 #nvc0_hub_mmio_head .b16 #nvc0_hub_mmio_tail diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h index bf4f4b32e25b..9d5517407dfb 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h @@ -211,7 +211,7 @@ uint32_t nvc0_grhub_data[] = { 0x000000c4, 0x048403e8, 0x000000c8, - 0x03e8034c, + 0x048403e8, 0x000000ce, 0x03e8034c, 0x000000cf, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c index 664747d62906..f146ebc9c08d 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c @@ -756,6 +756,7 @@ nvc0_graph_init_unk64xx(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc1: + case 0xc8: default: break; } @@ -775,6 +776,7 @@ nvc0_graph_init_unk58xx(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x405900, 0x00002834); break; case 0xc0: + case 0xc8: default: break; } @@ -789,6 +791,7 @@ nvc0_graph_init_unk58xx(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc1: + case 0xc8: default: break; } @@ -812,6 +815,7 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc1: + case 0xc8: default: break; } @@ -826,6 +830,7 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc1: + case 0xc8: default: break; } @@ -840,6 +845,7 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) case 0xc0: case 0xc3: case 0xc4: + case 0xc8: default: nv_wr32(priv, 0x418714, 0x80000000); break; @@ -853,6 +859,7 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) case 0xd9: case 0xd7: case 0xc1: + case 0xc8: nv_wr32(priv, 0x4188c8, 0x00000000); break; case 0xc0: @@ -883,6 +890,7 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc1: + case 0xc8: default: break; } @@ -897,6 +905,7 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc1: + case 0xc8: default: break; } @@ -912,6 +921,7 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc1: + case 0xc8: default: break; } @@ -928,6 +938,7 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) case 0xc0: case 0xc3: case 0xc4: + case 0xc8: default: nv_wr32(priv, 0x418e00, 0x00000050); break; @@ -943,6 +954,7 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc1: + case 0xc8: default: break; } @@ -966,6 +978,7 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419ac8, 0x00000000); break; case 0xc0: + case 0xc8: default: break; } @@ -982,6 +995,7 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc1: + case 0xc8: default: nv_wr32(priv, 0x41980c, 0x00000000); break; @@ -996,6 +1010,7 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) case 0xc0: case 0xc3: case 0xc4: + case 0xc8: default: nv_wr32(priv, 0x419814, 0x00000000); break; @@ -1010,6 +1025,7 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc1: + case 0xc8: default: nv_wr32(priv, 0x41984c, 0x00005bc5); break; @@ -1027,6 +1043,7 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419880, 0x00000002); break; case 0xc0: + case 0xc8: default: break; } @@ -1049,6 +1066,7 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc1: + case 0xc8: default: break; } @@ -1063,6 +1081,7 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc1: + case 0xc8: default: break; } @@ -1079,11 +1098,26 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc1: + case 0xc8: default: nv_wr32(priv, 0x419ea8, 0x00001100); break; } - nv_wr32(priv, 0x419eac, 0x11100702); + + switch (nv_device(priv)->chipset) { + case 0xc8: + nv_wr32(priv, 0x419eac, 0x11100f02); + break; + case 0xc0: + case 0xc3: + case 0xc4: + case 0xc1: + case 0xd9: + case 0xd7: + default: + nv_wr32(priv, 0x419eac, 0x11100702); + break; + } nv_wr32(priv, 0x419eb0, 0x00000003); nv_wr32(priv, 0x419eb4, 0x00000000); nv_wr32(priv, 0x419eb8, 0x00000000); @@ -1100,6 +1134,7 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419ed0, 0x00003818); break; case 0xc0: + case 0xc8: default: nv_wr32(priv, 0x419ec8, 0x06060618); nv_wr32(priv, 0x419ed0, 0x0eff0e38); @@ -1378,6 +1413,7 @@ nvc0_graph_init(struct nouveau_object *object) case 0xc3: case 0xc4: case 0xc1: + case 0xc8: case 0xd9: case 0xd7: nvc0_graph_init_unk40xx(priv); -- cgit v1.2.3 From 57f0ec159b77df764a6948f8a612b0b825cd8350 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 14 May 2013 13:09:28 +1000 Subject: drm/nvc0/gr: cleanup register lists, and add nvce/nvcf to switches Signed-off-by: Ben Skeggs --- .../gpu/drm/nouveau/core/engine/graph/ctxnvc0.c | 205 ++++++++++++++------- .../drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc | 197 ++++---------------- .../nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h | 180 +++--------------- .../drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc | 111 ++--------- .../nouveau/core/engine/graph/fuc/hubnvc0.fuc.h | 111 ++--------- drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c | 140 ++++++++++---- 6 files changed, 344 insertions(+), 600 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c index e0305bd8eedb..3be7b950eece 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c @@ -1323,21 +1323,6 @@ nvc0_grctx_generate_9097(struct nvc0_graph_priv *priv) nv_mthd(priv, 0x9097, 0x1450, 0x00300008); nv_mthd(priv, 0x9097, 0x1454, 0x04000080); nv_mthd(priv, 0x9097, 0x0214, 0x00000000); - - switch (nv_device(priv)->chipset) { - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc1: - case 0xc8: - case 0xd9: - case 0xd7: - break; - default: - /* in trace, right after 0x90c0, not here */ - nv_mthd(priv, 0x9097, 0x3410, 0x80002006); - break; - } } static void @@ -1481,7 +1466,11 @@ nvc0_grctx_generate_dispatch(struct nvc0_graph_priv *priv) case 0xc4: case 0xc1: case 0xc8: + case 0xce: + case 0xcf: + break; default: + BUG_ON(1); break; } nv_wr32(priv, 0x404044, 0x00000000); @@ -1499,19 +1488,7 @@ nvc0_grctx_generate_dispatch(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x4040c0, 0x00000000); nv_wr32(priv, 0x4040c4, 0x00000000); nv_wr32(priv, 0x4040c8, 0xf0000087); - switch (nv_device(priv)->chipset) { - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc1: - case 0xc8: - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x4040d0, 0x00000000); - break; - default: - break; - } + nv_wr32(priv, 0x4040d0, 0x00000000); nv_wr32(priv, 0x4040d4, 0x00000000); nv_wr32(priv, 0x4040d8, 0x00000000); nv_wr32(priv, 0x4040dc, 0x00000000); @@ -1536,9 +1513,13 @@ nvc0_grctx_generate_dispatch(struct nvc0_graph_priv *priv) case 0xc4: case 0xc1: case 0xc8: - default: + case 0xce: + case 0xcf: nv_wr32(priv, 0x404174, 0x00000000); break; + default: + BUG_ON(1); + break; } nv_wr32(priv, 0x404178, 0x00000000); nv_wr32(priv, 0x40417c, 0x00000000); @@ -1681,11 +1662,15 @@ nvc0_grctx_generate_shaders(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc8: - default: + case 0xce: + case 0xcf: nv_wr32(priv, 0x405800, 0x078000bf); nv_wr32(priv, 0x405830, 0x02180000); nv_wr32(priv, 0x405834, 0x00000000); break; + default: + BUG_ON(1); + break; } nv_wr32(priv, 0x405838, 0x00000000); nv_wr32(priv, 0x405854, 0x00000000); @@ -1720,27 +1705,19 @@ nvc0_grctx_generate_unk64xx(struct nvc0_graph_priv *priv) case 0xd9: case 0xd7: nv_wr32(priv, 0x4064bc, 0x00000000); - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc1: - case 0xc8: - default: - break; - } - switch (nv_device(priv)->chipset) { - case 0xc1: - case 0xd9: - case 0xd7: nv_wr32(priv, 0x4064c0, 0x80140078); nv_wr32(priv, 0x4064c4, 0x0086ffff); break; case 0xc0: case 0xc3: case 0xc4: + case 0xc1: case 0xc8: + case 0xce: + case 0xcf: + break; default: + BUG_ON(1); break; } } @@ -1782,6 +1759,8 @@ nvc0_grctx_generate_rop(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc8: + case 0xce: + case 0xcf: nv_wr32(priv, 0x408808, 0x0003e00d); nv_wr32(priv, 0x408900, 0x3080b801); nv_wr32(priv, 0x408904, 0x02000001); @@ -1801,11 +1780,7 @@ nvc0_grctx_generate_rop(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x408908, 0x00c8102f); break; default: - nv_wr32(priv, 0x408808, 0x0003e00d); - nv_wr32(priv, 0x408900, 0x3080b801); - nv_wr32(priv, 0x408904, 0x02000001); - nv_wr32(priv, 0x408908, 0x00c80929); - nv_wr32(priv, 0x40890c, 0x00000000); + BUG_ON(1); break; } nv_wr32(priv, 0x408980, 0x0000011d); @@ -1829,9 +1804,13 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) case 0xc4: case 0xc1: case 0xc8: - default: + case 0xce: + case 0xcf: nv_wr32(priv, 0x418408, 0x00000000); break; + default: + BUG_ON(1); + break; } nv_wr32(priv, 0x41840c, 0x00001008); nv_wr32(priv, 0x418410, 0x0fff0fff); @@ -1845,9 +1824,13 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) case 0xc4: case 0xc1: case 0xc8: - default: + case 0xce: + case 0xcf: nv_wr32(priv, 0x418414, 0x00200fff); break; + default: + BUG_ON(1); + break; } nv_wr32(priv, 0x418450, 0x00000000); nv_wr32(priv, 0x418454, 0x00000000); @@ -1873,9 +1856,13 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) case 0xc4: case 0xc1: case 0xc8: - default: + case 0xce: + case 0xcf: nv_wr32(priv, 0x41870c, 0x07c80000); break; + default: + BUG_ON(1); + break; } nv_wr32(priv, 0x418710, 0x00000000); switch (nv_device(priv)->chipset) { @@ -1888,9 +1875,13 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) case 0xc4: case 0xc1: case 0xc8: - default: + case 0xce: + case 0xcf: nv_wr32(priv, 0x418800, 0x0006860a); break; + default: + BUG_ON(1); + break; } nv_wr32(priv, 0x418808, 0x00000000); nv_wr32(priv, 0x41880c, 0x00000000); @@ -1906,9 +1897,13 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc8: - default: + case 0xce: + case 0xcf: nv_wr32(priv, 0x418830, 0x00000001); break; + default: + BUG_ON(1); + break; } nv_wr32(priv, 0x4188d8, 0x00000008); nv_wr32(priv, 0x4188e0, 0x01000000); @@ -1929,9 +1924,13 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc8: - default: + case 0xce: + case 0xcf: nv_wr32(priv, 0x4188fc, 0x00100000); break; + default: + BUG_ON(1); + break; } nv_wr32(priv, 0x41891c, 0x00ff00ff); nv_wr32(priv, 0x418924, 0x00000000); @@ -1956,9 +1955,13 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) case 0xc4: case 0xc1: case 0xc8: - default: + case 0xce: + case 0xcf: nv_wr32(priv, 0x418b00, 0x00000000); break; + default: + BUG_ON(1); + break; } nv_wr32(priv, 0x418b08, 0x0a418820); nv_wr32(priv, 0x418b0c, 0x062080e6); @@ -1986,7 +1989,11 @@ nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc8: + case 0xce: + case 0xcf: + break; default: + BUG_ON(1); break; } nv_wr32(priv, 0x418c80, 0x20200004); @@ -2014,9 +2021,13 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc8: - default: + case 0xce: + case 0xcf: nv_wr32(priv, 0x419864, 0x0000012a); break; + default: + BUG_ON(1); + break; } nv_wr32(priv, 0x419888, 0x00000000); nv_wr32(priv, 0x419a00, 0x000001f0); @@ -2032,10 +2043,16 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) case 0xc4: case 0xc1: case 0xc8: - default: + case 0xce: + case 0xcf: + case 0xd9: + case 0xd7: nv_wr32(priv, 0x419a1c, 0x00000000); nv_wr32(priv, 0x419a20, 0x00000800); break; + default: + BUG_ON(1); + break; } switch (nv_device(priv)->chipset) { case 0xc0: @@ -2048,9 +2065,13 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc1: - default: + case 0xce: + case 0xcf: nv_wr32(priv, 0x00419ac4, 0x0007f440); break; + default: + BUG_ON(1); + break; } nv_wr32(priv, 0x419b00, 0x0a418820); nv_wr32(priv, 0x419b04, 0x062080e6); @@ -2069,9 +2090,13 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc8: - default: + case 0xce: + case 0xcf: nv_wr32(priv, 0x419be0, 0x00000001); break; + default: + BUG_ON(1); + break; } nv_wr32(priv, 0x419be4, 0x00000000); switch (nv_device(priv)->chipset) { @@ -2084,9 +2109,13 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) case 0xc4: case 0xc1: case 0xc8: - default: + case 0xce: + case 0xcf: nv_wr32(priv, 0x419c00, 0x00000002); break; + default: + BUG_ON(1); + break; } nv_wr32(priv, 0x419c04, 0x00000006); nv_wr32(priv, 0x419c08, 0x00000002); @@ -2107,9 +2136,11 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc8: - default: nv_wr32(priv, 0x419cb0, 0x00060048); break; + default: + BUG_ON(1); + break; } nv_wr32(priv, 0x419ce8, 0x00000000); nv_wr32(priv, 0x419cf4, 0x00000183); @@ -2123,9 +2154,13 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc8: - default: + case 0xce: + case 0xcf: nv_wr32(priv, 0x419d20, 0x02180000); break; + default: + BUG_ON(1); + break; } nv_wr32(priv, 0x419d24, 0x00001fff); switch (nv_device(priv)->chipset) { @@ -2138,7 +2173,11 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc8: + case 0xce: + case 0xcf: + break; default: + BUG_ON(1); break; } nv_wr32(priv, 0x419e04, 0x00000000); @@ -2177,9 +2216,13 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc1: - default: + case 0xce: + case 0xcf: nv_wr32(priv, 0x419ee0, 0x00011110); break; + default: + BUG_ON(1); + break; } switch (nv_device(priv)->chipset) { case 0xc0: @@ -2190,6 +2233,8 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc1: + case 0xce: + case 0xcf: case 0xd9: case 0xd7: nv_wr32(priv, 0x419f30, 0x00000000); @@ -2204,10 +2249,9 @@ nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x419f54, 0x00000000); nv_wr32(priv, 0x419f58, 0x00000000); break; + break; default: - nv_wr32(priv, 0x419f50, 0x00000000); - nv_wr32(priv, 0x419f54, 0x00000000); - nv_wr32(priv, 0x419f58, 0x00000000); + BUG_ON(1); break; } } @@ -2277,7 +2321,13 @@ nvc0_grctx_generate(struct nvc0_graph_priv *priv) } } break; - default: + break; + case 0xc0: + case 0xc3: + case 0xc4: + case 0xc8: + case 0xce: + case 0xcf: tmp = 0x02180000; mmio_list(0x405830, tmp, 0, 0); for (gpc = 0; gpc < priv->gpc_nr; gpc++) { @@ -2288,6 +2338,9 @@ nvc0_grctx_generate(struct nvc0_graph_priv *priv) } } break; + default: + BUG_ON(1); + break; } for (tpc = 0, id = 0; tpc < 4; tpc++) { @@ -2530,7 +2583,11 @@ nvc0_grctx_generate(struct nvc0_graph_priv *priv) case 0xc4: case 0xc1: case 0xc8: + case 0xce: + case 0xcf: + break; default: + BUG_ON(1); break; } nv_icmd(priv, 0x00000218, 0x0000c080); @@ -2552,8 +2609,11 @@ nvc0_grctx_generate(struct nvc0_graph_priv *priv) case 0xc4: case 0xc1: case 0xc8: + case 0xce: + case 0xcf: break; default: + BUG_ON(1); break; } nv_icmd(priv, 0x000000ad, 0x0000013e); @@ -3128,7 +3188,11 @@ nvc0_grctx_generate(struct nvc0_graph_priv *priv) case 0xc0: case 0xc3: case 0xc4: + case 0xce: + case 0xcf: + break; default: + BUG_ON(1); break; } nv_icmd(priv, 0x00000586, 0x00000040); @@ -3241,7 +3305,11 @@ nvc0_grctx_generate(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc1: + case 0xce: + case 0xcf: + break; default: + BUG_ON(1); break; } nv_icmd(priv, 0x00000683, 0x00000006); @@ -3392,6 +3460,8 @@ nvc0_grctx_generate(struct nvc0_graph_priv *priv) case 0xc4: case 0xc1: case 0xc8: + case 0xce: + case 0xcf: nv_mthd(priv, 0x902d, 0x3410, 0x00000000); break; case 0xd9: @@ -3399,6 +3469,7 @@ nvc0_grctx_generate(struct nvc0_graph_priv *priv) nv_mthd(priv, 0x902d, 0x3410, 0x80002006); break; default: + BUG_ON(1); break; } diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc index 4539e33174b7..61a6b43ece19 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc @@ -48,30 +48,30 @@ cmd_queue: queue_init // chipset descriptions chipsets: .b8 0xc0 0 0 0 -.b16 #nnvc0_gpc_mmio_head -.b16 #nnvc0_gpc_mmio_tail -.b16 #nnvc0_tpc_mmio_head -.b16 #nnvc0_tpc_mmio_tail +.b16 #nvc0_gpc_mmio_head +.b16 #nvc0_gpc_mmio_tail +.b16 #nvc0_tpc_mmio_head +.b16 #nvc0_tpc_mmio_tail .b8 0xc1 0 0 0 -.b16 #nnvc0_gpc_mmio_head -.b16 #nnvc1_gpc_mmio_tail -.b16 #nnvc3_tpc_mmio_head -.b16 #nnvc1_tpc_mmio_tail +.b16 #nvc0_gpc_mmio_head +.b16 #nvc1_gpc_mmio_tail +.b16 #nvc0_tpc_mmio_head +.b16 #nvc1_tpc_mmio_tail .b8 0xc3 0 0 0 -.b16 #nnvc0_gpc_mmio_head -.b16 #nnvc0_gpc_mmio_tail -.b16 #nnvc3_tpc_mmio_head -.b16 #nnvc3_tpc_mmio_tail +.b16 #nvc0_gpc_mmio_head +.b16 #nvc0_gpc_mmio_tail +.b16 #nvc0_tpc_mmio_head +.b16 #nvc3_tpc_mmio_tail .b8 0xc4 0 0 0 -.b16 #nnvc0_gpc_mmio_head -.b16 #nnvc0_gpc_mmio_tail -.b16 #nnvc3_tpc_mmio_head -.b16 #nnvc3_tpc_mmio_tail +.b16 #nvc0_gpc_mmio_head +.b16 #nvc0_gpc_mmio_tail +.b16 #nvc0_tpc_mmio_head +.b16 #nvc3_tpc_mmio_tail .b8 0xc8 0 0 0 -.b16 #nnvc0_gpc_mmio_head -.b16 #nnvc0_gpc_mmio_tail -.b16 #nnvc0_tpc_mmio_head -.b16 #nnvc0_tpc_mmio_tail +.b16 #nvc0_gpc_mmio_head +.b16 #nvc0_gpc_mmio_tail +.b16 #nvc0_tpc_mmio_head +.b16 #nvc0_tpc_mmio_tail .b8 0xce 0 0 0 .b16 #nvc0_gpc_mmio_head .b16 #nvc0_gpc_mmio_tail @@ -81,23 +81,26 @@ chipsets: .b16 #nvc0_gpc_mmio_head .b16 #nvc0_gpc_mmio_tail .b16 #nvc0_tpc_mmio_head -.b16 #nvcf_tpc_mmio_tail +.b16 #nvc3_tpc_mmio_tail .b8 0xd9 0 0 0 .b16 #nvd9_gpc_mmio_head -.b16 #nvd9_gpc_mmio_tail -.b16 #nvd9_tpc_mmio_head +.b16 #nvc1_gpc_mmio_tail +.b16 #nvc0_tpc_mmio_head .b16 #nvd9_tpc_mmio_tail .b8 0xd7 0 0 0 .b16 #nvd9_gpc_mmio_head -.b16 #nvd9_gpc_mmio_tail -.b16 #nvd9_tpc_mmio_head +.b16 #nvc1_gpc_mmio_tail +.b16 #nvc0_tpc_mmio_head .b16 #nvd9_tpc_mmio_tail .b8 0 0 0 0 // GPC mmio lists nvc0_gpc_mmio_head: +mmctx_data(0x000408, 1) +nvd9_gpc_mmio_head: mmctx_data(0x000380, 1) -mmctx_data(0x000400, 6) +mmctx_data(0x000400, 2); +mmctx_data(0x00040c, 3); mmctx_data(0x000450, 9) mmctx_data(0x000600, 1) mmctx_data(0x000684, 1) @@ -121,64 +124,8 @@ mmctx_data(0x000c8c, 1) mmctx_data(0x001000, 3) mmctx_data(0x001014, 1) nvc0_gpc_mmio_tail: - -nnvc0_gpc_mmio_head: -mmctx_data(0x000380, 1) -mmctx_data(0x000400, 6) -mmctx_data(0x000450, 9) -mmctx_data(0x000600, 1) -mmctx_data(0x000684, 1) -mmctx_data(0x000700, 5) -mmctx_data(0x000800, 1) -mmctx_data(0x000808, 3) -mmctx_data(0x000828, 1) -mmctx_data(0x000830, 1) -mmctx_data(0x0008d8, 1) -mmctx_data(0x0008e0, 1) -mmctx_data(0x0008e8, 6) -mmctx_data(0x00091c, 1) -mmctx_data(0x000924, 3) -mmctx_data(0x000b00, 1) -mmctx_data(0x000b08, 6) -mmctx_data(0x000bb8, 1) -mmctx_data(0x000c08, 1) -mmctx_data(0x000c10, 8) -mmctx_data(0x000c80, 1) -mmctx_data(0x000c8c, 1) -mmctx_data(0x001000, 3) -mmctx_data(0x001014, 1) -nnvc0_gpc_mmio_tail: mmctx_data(0x000c6c, 1); -nnvc1_gpc_mmio_tail: - -nvd9_gpc_mmio_head: -mmctx_data(0x000380, 1) -mmctx_data(0x000400, 2) -mmctx_data(0x00040c, 3) -mmctx_data(0x000450, 9) -mmctx_data(0x000600, 1) -mmctx_data(0x000684, 1) -mmctx_data(0x000700, 5) -mmctx_data(0x000800, 1) -mmctx_data(0x000808, 3) -mmctx_data(0x000828, 1) -mmctx_data(0x000830, 1) -mmctx_data(0x0008d8, 1) -mmctx_data(0x0008e0, 1) -mmctx_data(0x0008e8, 6) -mmctx_data(0x00091c, 1) -mmctx_data(0x000924, 3) -mmctx_data(0x000b00, 1) -mmctx_data(0x000b08, 6) -mmctx_data(0x000bb8, 1) -mmctx_data(0x000c08, 1) -mmctx_data(0x000c10, 8) -mmctx_data(0x000c6c, 1) -mmctx_data(0x000c80, 1) -mmctx_data(0x000c8c, 1) -mmctx_data(0x001000, 3) -mmctx_data(0x001014, 1) -nvd9_gpc_mmio_tail: +nvc1_gpc_mmio_tail: // TPC mmio lists nvc0_tpc_mmio_head: @@ -188,7 +135,6 @@ mmctx_data(0x000048, 1) mmctx_data(0x000064, 1) mmctx_data(0x000088, 1) mmctx_data(0x000200, 6) -mmctx_data(0x00021c, 2) mmctx_data(0x000300, 6) mmctx_data(0x0003d0, 1) mmctx_data(0x0003e0, 2) @@ -203,86 +149,15 @@ mmctx_data(0x000644, 20) mmctx_data(0x000698, 1) mmctx_data(0x000750, 2) nvc0_tpc_mmio_tail: -mmctx_data(0x000758, 1) -mmctx_data(0x0002c4, 1) -mmctx_data(0x0006e0, 1) -nvcf_tpc_mmio_tail: -mmctx_data(0x0004bc, 1) -nvc3_tpc_mmio_tail: - -nnvc0_tpc_mmio_head: -mmctx_data(0x000018, 1) -mmctx_data(0x00003c, 1) -mmctx_data(0x000048, 1) -mmctx_data(0x000064, 1) -mmctx_data(0x000088, 1) -mmctx_data(0x000200, 6) -mmctx_data(0x000300, 6) -mmctx_data(0x0003d0, 1) -mmctx_data(0x0003e0, 2) -mmctx_data(0x000400, 3) -mmctx_data(0x000420, 1) -mmctx_data(0x0004b0, 1) -mmctx_data(0x0004e8, 1) -mmctx_data(0x0004f4, 1) -mmctx_data(0x000520, 2) -mmctx_data(0x000604, 4) -mmctx_data(0x000644, 20) -mmctx_data(0x000698, 1) -mmctx_data(0x000750, 2) -nnvc0_tpc_mmio_tail: - -nnvc3_tpc_mmio_head: -mmctx_data(0x000018, 1) -mmctx_data(0x00003c, 1) -mmctx_data(0x000048, 1) -mmctx_data(0x000064, 1) -mmctx_data(0x000088, 1) -mmctx_data(0x000200, 6) mmctx_data(0x00021c, 2) mmctx_data(0x0002c4, 1) -mmctx_data(0x000300, 6) -mmctx_data(0x0003d0, 1) -mmctx_data(0x0003e0, 2) -mmctx_data(0x000400, 3) -mmctx_data(0x000420, 1) -mmctx_data(0x0004b0, 1) -mmctx_data(0x0004e8, 1) -mmctx_data(0x0004f4, 1) -mmctx_data(0x000520, 2) -mmctx_data(0x000604, 4) -mmctx_data(0x000644, 20) -mmctx_data(0x000698, 1) -mmctx_data(0x0006e0, 1) -mmctx_data(0x000730, 11) -nnvc3_tpc_mmio_tail: -mmctx_data(0x000544, 1) -nnvc1_tpc_mmio_tail: - -nvd9_tpc_mmio_head: -mmctx_data(0x000018, 1) -mmctx_data(0x00003c, 1) -mmctx_data(0x000048, 1) -mmctx_data(0x000064, 1) -mmctx_data(0x000088, 1) -mmctx_data(0x000200, 6) -mmctx_data(0x00021c, 2) -mmctx_data(0x0002c4, 1) -mmctx_data(0x000300, 6) -mmctx_data(0x0003d0, 1) -mmctx_data(0x0003e0, 2) -mmctx_data(0x000400, 3) -mmctx_data(0x000420, 3) -mmctx_data(0x0004b0, 1) -mmctx_data(0x0004e8, 1) -mmctx_data(0x0004f4, 1) -mmctx_data(0x000520, 2) +mmctx_data(0x000730, 8) +mmctx_data(0x000758, 1) +nvc3_tpc_mmio_tail: mmctx_data(0x000544, 1) -mmctx_data(0x000604, 4) -mmctx_data(0x000644, 20) -mmctx_data(0x000698, 1) -mmctx_data(0x0006e0, 1) -mmctx_data(0x000730, 11) +nvc1_tpc_mmio_tail: +mmctx_data(0x000424, 2); +mmctx_data(0x0006e0, 1); nvd9_tpc_mmio_tail: .section #nvc0_grgpc_code diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h index bad9a16a9463..cafcc638042a 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h @@ -34,88 +34,36 @@ uint32_t nvc0_grgpc_data[] = { 0x00000000, /* 0x0064: chipsets */ 0x000000c0, - 0x01940134, - 0x02ac0260, + 0x013c00d4, + 0x018c0140, 0x000000c1, - 0x01980134, - 0x030802ac, + 0x014000d4, + 0x01a00140, 0x000000c3, - 0x01940134, - 0x030402ac, + 0x013c00d4, + 0x019c0140, 0x000000c4, - 0x01940134, - 0x030402ac, + 0x013c00d4, + 0x019c0140, 0x000000c8, - 0x01940134, - 0x02ac0260, + 0x013c00d4, + 0x018c0140, 0x000000ce, - 0x013400d4, - 0x02600200, + 0x013c00d4, + 0x019c0140, 0x000000cf, - 0x013400d4, - 0x025c0200, + 0x013c00d4, + 0x019c0140, 0x000000d9, - 0x02000198, - 0x03640308, + 0x014000d8, + 0x01a80140, 0x000000d7, - 0x02000198, - 0x03640308, + 0x014000d8, + 0x01a80140, 0x00000000, /* 0x00d4: nvc0_gpc_mmio_head */ - 0x00000380, - 0x14000400, - 0x20000450, - 0x00000600, - 0x00000684, - 0x10000700, - 0x00000800, - 0x08000808, - 0x00000828, - 0x00000830, - 0x000008d8, - 0x000008e0, - 0x140008e8, - 0x0000091c, - 0x08000924, - 0x00000b00, - 0x14000b08, - 0x00000bb8, - 0x00000c08, - 0x1c000c10, - 0x00000c80, - 0x00000c8c, - 0x08001000, - 0x00001014, -/* 0x0134: nvc0_gpc_mmio_tail */ -/* 0x0134: nnvc0_gpc_mmio_head */ - 0x00000380, - 0x14000400, - 0x20000450, - 0x00000600, - 0x00000684, - 0x10000700, - 0x00000800, - 0x08000808, - 0x00000828, - 0x00000830, - 0x000008d8, - 0x000008e0, - 0x140008e8, - 0x0000091c, - 0x08000924, - 0x00000b00, - 0x14000b08, - 0x00000bb8, - 0x00000c08, - 0x1c000c10, - 0x00000c80, - 0x00000c8c, - 0x08001000, - 0x00001014, -/* 0x0194: nnvc0_gpc_mmio_tail */ - 0x00000c6c, -/* 0x0198: nnvc1_gpc_mmio_tail */ -/* 0x0198: nvd9_gpc_mmio_head */ + 0x00000408, +/* 0x00d8: nvd9_gpc_mmio_head */ 0x00000380, 0x04000400, 0x0800040c, @@ -137,41 +85,14 @@ uint32_t nvc0_grgpc_data[] = { 0x00000bb8, 0x00000c08, 0x1c000c10, - 0x00000c6c, 0x00000c80, 0x00000c8c, 0x08001000, 0x00001014, -/* 0x0200: nvd9_gpc_mmio_tail */ -/* 0x0200: nvc0_tpc_mmio_head */ - 0x00000018, - 0x0000003c, - 0x00000048, - 0x00000064, - 0x00000088, - 0x14000200, - 0x0400021c, - 0x14000300, - 0x000003d0, - 0x040003e0, - 0x08000400, - 0x00000420, - 0x000004b0, - 0x000004e8, - 0x000004f4, - 0x04000520, - 0x0c000604, - 0x4c000644, - 0x00000698, - 0x04000750, -/* 0x0250: nvc0_tpc_mmio_tail */ - 0x00000758, - 0x000002c4, - 0x000006e0, -/* 0x025c: nvcf_tpc_mmio_tail */ - 0x000004bc, -/* 0x0260: nvc3_tpc_mmio_tail */ -/* 0x0260: nnvc0_tpc_mmio_head */ +/* 0x013c: nvc0_gpc_mmio_tail */ + 0x00000c6c, +/* 0x0140: nvc1_gpc_mmio_tail */ +/* 0x0140: nvc0_tpc_mmio_head */ 0x00000018, 0x0000003c, 0x00000048, @@ -191,57 +112,16 @@ uint32_t nvc0_grgpc_data[] = { 0x4c000644, 0x00000698, 0x04000750, -/* 0x02ac: nnvc0_tpc_mmio_tail */ -/* 0x02ac: nnvc3_tpc_mmio_head */ - 0x00000018, - 0x0000003c, - 0x00000048, - 0x00000064, - 0x00000088, - 0x14000200, - 0x0400021c, - 0x000002c4, - 0x14000300, - 0x000003d0, - 0x040003e0, - 0x08000400, - 0x00000420, - 0x000004b0, - 0x000004e8, - 0x000004f4, - 0x04000520, - 0x0c000604, - 0x4c000644, - 0x00000698, - 0x000006e0, - 0x28000730, -/* 0x0304: nnvc3_tpc_mmio_tail */ - 0x00000544, -/* 0x0308: nnvc1_tpc_mmio_tail */ -/* 0x0308: nvd9_tpc_mmio_head */ - 0x00000018, - 0x0000003c, - 0x00000048, - 0x00000064, - 0x00000088, - 0x14000200, +/* 0x018c: nvc0_tpc_mmio_tail */ 0x0400021c, 0x000002c4, - 0x14000300, - 0x000003d0, - 0x040003e0, - 0x08000400, - 0x08000420, - 0x000004b0, - 0x000004e8, - 0x000004f4, - 0x04000520, + 0x1c000730, + 0x00000758, +/* 0x019c: nvc3_tpc_mmio_tail */ 0x00000544, - 0x0c000604, - 0x4c000644, - 0x00000698, +/* 0x01a0: nvc1_tpc_mmio_tail */ + 0x04000424, 0x000006e0, - 0x28000730, }; uint32_t nvc0_grgpc_code[] = { diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc index 6eb5168a3811..9f174be6bc82 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc @@ -48,20 +48,20 @@ xfer_data: .b32 0 .align 256 chipsets: .b8 0xc0 0 0 0 -.b16 #nnvc0_hub_mmio_head -.b16 #nnvc0_hub_mmio_tail +.b16 #nvc0_hub_mmio_head +.b16 #nvc0_hub_mmio_tail .b8 0xc1 0 0 0 -.b16 #nnvc0_hub_mmio_head +.b16 #nvc0_hub_mmio_head .b16 #nvc1_hub_mmio_tail .b8 0xc3 0 0 0 -.b16 #nnvc0_hub_mmio_head -.b16 #nnvc0_hub_mmio_tail +.b16 #nvc0_hub_mmio_head +.b16 #nvc0_hub_mmio_tail .b8 0xc4 0 0 0 -.b16 #nnvc0_hub_mmio_head -.b16 #nnvc0_hub_mmio_tail +.b16 #nvc0_hub_mmio_head +.b16 #nvc0_hub_mmio_tail .b8 0xc8 0 0 0 -.b16 #nnvc0_hub_mmio_head -.b16 #nnvc0_hub_mmio_tail +.b16 #nvc0_hub_mmio_head +.b16 #nvc0_hub_mmio_tail .b8 0xce 0 0 0 .b16 #nvc0_hub_mmio_head .b16 #nvc0_hub_mmio_tail @@ -77,91 +77,8 @@ chipsets: .b8 0 0 0 0 nvc0_hub_mmio_head: -mmctx_data(0x17e91c, 2) -mmctx_data(0x400204, 2) -mmctx_data(0x404004, 11) -mmctx_data(0x404044, 1) -mmctx_data(0x404094, 14) -mmctx_data(0x4040d0, 7) -mmctx_data(0x4040f8, 1) -mmctx_data(0x404130, 3) -mmctx_data(0x404150, 3) -mmctx_data(0x404164, 2) -mmctx_data(0x404174, 3) -mmctx_data(0x404200, 8) -mmctx_data(0x404404, 14) -mmctx_data(0x404460, 4) -mmctx_data(0x404480, 1) -mmctx_data(0x404498, 1) -mmctx_data(0x404604, 4) -mmctx_data(0x404618, 32) -mmctx_data(0x404698, 21) -mmctx_data(0x4046f0, 2) -mmctx_data(0x404700, 22) -mmctx_data(0x405800, 1) -mmctx_data(0x405830, 3) -mmctx_data(0x405854, 1) -mmctx_data(0x405870, 4) -mmctx_data(0x405a00, 2) -mmctx_data(0x405a18, 1) -mmctx_data(0x406020, 1) -mmctx_data(0x406028, 4) -mmctx_data(0x4064a8, 2) -mmctx_data(0x4064b4, 2) -mmctx_data(0x407804, 1) -mmctx_data(0x40780c, 6) -mmctx_data(0x4078bc, 1) -mmctx_data(0x408000, 7) -mmctx_data(0x408064, 1) -mmctx_data(0x408800, 3) -mmctx_data(0x408900, 4) -mmctx_data(0x408980, 1) -nvc0_hub_mmio_tail: - -nnvc0_hub_mmio_head: -mmctx_data(0x17e91c, 2) -mmctx_data(0x400204, 2) -mmctx_data(0x404004, 11) -mmctx_data(0x404044, 1) -mmctx_data(0x404094, 14) -mmctx_data(0x4040d0, 7) -mmctx_data(0x4040f8, 1) -mmctx_data(0x404130, 3) -mmctx_data(0x404150, 3) -mmctx_data(0x404164, 2) -mmctx_data(0x404174, 3) -mmctx_data(0x404200, 8) -mmctx_data(0x404404, 14) -mmctx_data(0x404460, 4) -mmctx_data(0x404480, 1) -mmctx_data(0x404498, 1) -mmctx_data(0x404604, 4) -mmctx_data(0x404618, 32) -mmctx_data(0x404698, 21) -mmctx_data(0x4046f0, 2) -mmctx_data(0x404700, 22) -mmctx_data(0x405800, 1) -mmctx_data(0x405830, 3) -mmctx_data(0x405854, 1) -mmctx_data(0x405870, 4) -mmctx_data(0x405a00, 2) -mmctx_data(0x405a18, 1) -mmctx_data(0x406020, 1) -mmctx_data(0x406028, 4) -mmctx_data(0x4064a8, 2) -mmctx_data(0x4064b4, 2) -mmctx_data(0x407804, 1) -mmctx_data(0x40780c, 6) -mmctx_data(0x4078bc, 1) -mmctx_data(0x408000, 7) -mmctx_data(0x408064, 1) -mmctx_data(0x408800, 3) -mmctx_data(0x408900, 3) -mmctx_data(0x408980, 1) -nnvc0_hub_mmio_tail: -mmctx_data(0x4064c0, 2) -nvc1_hub_mmio_tail: - +mmctx_data(0x40402c, 1) +mmctx_data(0x404174, 1) nvd9_hub_mmio_head: mmctx_data(0x17e91c, 2) mmctx_data(0x400204, 2) @@ -193,7 +110,7 @@ mmctx_data(0x405a18, 1) mmctx_data(0x406020, 1) mmctx_data(0x406028, 4) mmctx_data(0x4064a8, 2) -mmctx_data(0x4064b4, 5) +mmctx_data(0x4064b4, 2) mmctx_data(0x407804, 1) mmctx_data(0x40780c, 6) mmctx_data(0x4078bc, 1) @@ -202,6 +119,10 @@ mmctx_data(0x408064, 1) mmctx_data(0x408800, 3) mmctx_data(0x408900, 3) mmctx_data(0x408980, 1) +nvc0_hub_mmio_tail: +mmctx_data(0x4064c0, 2) +nvc1_hub_mmio_tail: +mmctx_data(0x4064bc, 3) nvd9_hub_mmio_tail: .section #nvc0_grhub_code diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h index 9d5517407dfb..0953c2db2d13 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h @@ -203,109 +203,28 @@ uint32_t nvc0_grhub_data[] = { 0x00000000, /* 0x0300: chipsets */ 0x000000c0, - 0x048403e8, + 0x03f0034c, 0x000000c1, - 0x048803e8, + 0x03f4034c, 0x000000c3, - 0x048403e8, + 0x03f0034c, 0x000000c4, - 0x048403e8, + 0x03f0034c, 0x000000c8, - 0x048403e8, + 0x03f0034c, 0x000000ce, - 0x03e8034c, + 0x03f0034c, 0x000000cf, - 0x03e8034c, + 0x03f0034c, 0x000000d9, - 0x05240488, + 0x03f80354, 0x000000d7, - 0x05240488, + 0x03f80354, 0x00000000, /* 0x034c: nvc0_hub_mmio_head */ - 0x0417e91c, - 0x04400204, - 0x28404004, - 0x00404044, - 0x34404094, - 0x184040d0, - 0x004040f8, - 0x08404130, - 0x08404150, - 0x04404164, - 0x08404174, - 0x1c404200, - 0x34404404, - 0x0c404460, - 0x00404480, - 0x00404498, - 0x0c404604, - 0x7c404618, - 0x50404698, - 0x044046f0, - 0x54404700, - 0x00405800, - 0x08405830, - 0x00405854, - 0x0c405870, - 0x04405a00, - 0x00405a18, - 0x00406020, - 0x0c406028, - 0x044064a8, - 0x044064b4, - 0x00407804, - 0x1440780c, - 0x004078bc, - 0x18408000, - 0x00408064, - 0x08408800, - 0x0c408900, - 0x00408980, -/* 0x03e8: nvc0_hub_mmio_tail */ -/* 0x03e8: nnvc0_hub_mmio_head */ - 0x0417e91c, - 0x04400204, - 0x28404004, - 0x00404044, - 0x34404094, - 0x184040d0, - 0x004040f8, - 0x08404130, - 0x08404150, - 0x04404164, - 0x08404174, - 0x1c404200, - 0x34404404, - 0x0c404460, - 0x00404480, - 0x00404498, - 0x0c404604, - 0x7c404618, - 0x50404698, - 0x044046f0, - 0x54404700, - 0x00405800, - 0x08405830, - 0x00405854, - 0x0c405870, - 0x04405a00, - 0x00405a18, - 0x00406020, - 0x0c406028, - 0x044064a8, - 0x044064b4, - 0x00407804, - 0x1440780c, - 0x004078bc, - 0x18408000, - 0x00408064, - 0x08408800, - 0x08408900, - 0x00408980, -/* 0x0484: nnvc0_hub_mmio_tail */ - 0x044064c0, -/* 0x0488: nvc1_hub_mmio_tail */ -/* 0x0488: nvd9_hub_mmio_head */ + 0x0040402c, + 0x00404174, +/* 0x0354: nvd9_hub_mmio_head */ 0x0417e91c, 0x04400204, 0x24404004, @@ -336,7 +255,7 @@ uint32_t nvc0_grhub_data[] = { 0x00406020, 0x0c406028, 0x044064a8, - 0x104064b4, + 0x044064b4, 0x00407804, 0x1440780c, 0x004078bc, @@ -345,6 +264,10 @@ uint32_t nvc0_grhub_data[] = { 0x08408800, 0x08408900, 0x00408980, +/* 0x03f0: nvc0_hub_mmio_tail */ + 0x044064c0, +/* 0x03f4: nvc1_hub_mmio_tail */ + 0x084064bc, }; uint32_t nvc0_grhub_code[] = { diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c index f146ebc9c08d..d61c833be09f 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c @@ -757,7 +757,11 @@ nvc0_graph_init_unk64xx(struct nvc0_graph_priv *priv) case 0xc4: case 0xc1: case 0xc8: + case 0xce: + case 0xcf: + break; default: + BUG_ON(1); break; } } @@ -771,13 +775,17 @@ nvc0_graph_init_unk58xx(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc1: + case 0xce: + case 0xcf: case 0xd9: case 0xd7: nv_wr32(priv, 0x405900, 0x00002834); break; case 0xc0: case 0xc8: + break; default: + BUG_ON(1); break; } nv_wr32(priv, 0x405908, 0x00000000); @@ -792,7 +800,11 @@ nvc0_graph_init_unk58xx(struct nvc0_graph_priv *priv) case 0xc4: case 0xc1: case 0xc8: + case 0xce: + case 0xcf: + break; default: + BUG_ON(1); break; } } @@ -816,7 +828,11 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) case 0xc4: case 0xc1: case 0xc8: + case 0xce: + case 0xcf: + break; default: + BUG_ON(1); break; } nv_wr32(priv, 0x4184a0, 0x00000000); @@ -831,7 +847,11 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) case 0xc4: case 0xc1: case 0xc8: + case 0xce: + case 0xcf: + break; default: + BUG_ON(1); break; } nv_wr32(priv, 0x418604, 0x00000000); @@ -846,9 +866,13 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc8: - default: + case 0xce: + case 0xcf: nv_wr32(priv, 0x418714, 0x80000000); break; + default: + BUG_ON(1); + break; } nv_wr32(priv, 0x418384, 0x00000000); nv_wr32(priv, 0x418814, 0x00000000); @@ -865,9 +889,13 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) case 0xc0: case 0xc3: case 0xc4: - default: + case 0xce: + case 0xcf: nv_wr32(priv, 0x4188c8, 0x80000000); break; + default: + BUG_ON(1); + break; } nv_wr32(priv, 0x4188cc, 0x00000000); nv_wr32(priv, 0x4188d0, 0x00010000); @@ -891,7 +919,11 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) case 0xc4: case 0xc1: case 0xc8: + case 0xce: + case 0xcf: + break; default: + BUG_ON(1); break; } nv_wr32(priv, 0x418c88, 0x00000000); @@ -906,7 +938,11 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) case 0xc4: case 0xc1: case 0xc8: + case 0xce: + case 0xcf: + break; default: + BUG_ON(1); break; } nv_wr32(priv, 0x418d00, 0x00000000); @@ -922,7 +958,11 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) case 0xc4: case 0xc1: case 0xc8: + case 0xce: + case 0xcf: + break; default: + BUG_ON(1); break; } nv_wr32(priv, 0x418f08, 0x00000000); @@ -939,9 +979,13 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc8: - default: + case 0xce: + case 0xcf: nv_wr32(priv, 0x418e00, 0x00000050); break; + default: + BUG_ON(1); + break; } nv_wr32(priv, 0x418e08, 0x00000000); switch (nv_device(priv)->chipset) { @@ -955,7 +999,11 @@ nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) case 0xc4: case 0xc1: case 0xc8: + case 0xce: + case 0xcf: + break; default: + BUG_ON(1); break; } nv_wr32(priv, 0x41900c, 0x00000000); @@ -973,13 +1021,17 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc1: + case 0xce: + case 0xcf: case 0xd9: case 0xd7: nv_wr32(priv, 0x419ac8, 0x00000000); break; case 0xc0: case 0xc8: + break; default: + BUG_ON(1); break; } nv_wr32(priv, 0x419ab8, 0x000000e7); @@ -996,9 +1048,13 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) case 0xc4: case 0xc1: case 0xc8: - default: + case 0xce: + case 0xcf: nv_wr32(priv, 0x41980c, 0x00000000); break; + default: + BUG_ON(1); + break; } nv_wr32(priv, 0x419810, 0x00000000); switch (nv_device(priv)->chipset) { @@ -1011,9 +1067,13 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc8: - default: + case 0xce: + case 0xcf: nv_wr32(priv, 0x419814, 0x00000000); break; + default: + BUG_ON(1); + break; } nv_wr32(priv, 0x419844, 0x00000000); switch (nv_device(priv)->chipset) { @@ -1026,9 +1086,13 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) case 0xc4: case 0xc1: case 0xc8: - default: + case 0xce: + case 0xcf: nv_wr32(priv, 0x41984c, 0x00005bc5); break; + default: + BUG_ON(1); + break; } nv_wr32(priv, 0x419850, 0x00000000); nv_wr32(priv, 0x419854, 0x00000000); @@ -1038,13 +1102,17 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc1: + case 0xce: + case 0xcf: case 0xd9: case 0xd7: nv_wr32(priv, 0x419880, 0x00000002); break; case 0xc0: case 0xc8: + break; default: + BUG_ON(1); break; } nv_wr32(priv, 0x419c98, 0x00000000); @@ -1067,7 +1135,11 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) case 0xc4: case 0xc1: case 0xc8: + case 0xce: + case 0xcf: + break; default: + BUG_ON(1); break; } nv_wr32(priv, 0x419d2c, 0x00000000); @@ -1082,7 +1154,11 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) case 0xc4: case 0xc1: case 0xc8: + case 0xce: + case 0xcf: + break; default: + BUG_ON(1); break; } nv_wr32(priv, 0x419c0c, 0x00000000); @@ -1099,9 +1175,13 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) case 0xc4: case 0xc1: case 0xc8: - default: + case 0xce: + case 0xcf: nv_wr32(priv, 0x419ea8, 0x00001100); break; + default: + BUG_ON(1); + break; } switch (nv_device(priv)->chipset) { @@ -1112,11 +1192,15 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc1: + case 0xce: + case 0xcf: case 0xd9: case 0xd7: - default: nv_wr32(priv, 0x419eac, 0x11100702); break; + default: + BUG_ON(1); + break; } nv_wr32(priv, 0x419eb0, 0x00000003); nv_wr32(priv, 0x419eb4, 0x00000000); @@ -1127,6 +1211,8 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) case 0xc3: case 0xc4: case 0xc1: + case 0xce: + case 0xcf: case 0xd9: case 0xd7: nv_wr32(priv, 0x419ec8, 0x0e063818); @@ -1135,10 +1221,12 @@ nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) break; case 0xc0: case 0xc8: - default: nv_wr32(priv, 0x419ec8, 0x06060618); nv_wr32(priv, 0x419ed0, 0x0eff0e38); break; + default: + BUG_ON(1); + break; } nv_wr32(priv, 0x419ed4, 0x011104f1); nv_wr32(priv, 0x419edc, 0x00000000); @@ -1407,30 +1495,16 @@ nvc0_graph_init(struct nouveau_object *object) nvc0_graph_init_obj418880(priv); nvc0_graph_init_regs(priv); - - switch (nv_device(priv)->chipset) { - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc1: - case 0xc8: - case 0xd9: - case 0xd7: - nvc0_graph_init_unk40xx(priv); - nvc0_graph_init_unk44xx(priv); - nvc0_graph_init_unk78xx(priv); - nvc0_graph_init_unk60xx(priv); - nvc0_graph_init_unk64xx(priv); - nvc0_graph_init_unk58xx(priv); - nvc0_graph_init_unk80xx(priv); - nvc0_graph_init_gpc(priv); - nvc0_graph_init_tpc(priv); - nvc0_graph_init_unk88xx(priv); - break; - default: - break; - } - + nvc0_graph_init_unk40xx(priv); + nvc0_graph_init_unk44xx(priv); + nvc0_graph_init_unk78xx(priv); + nvc0_graph_init_unk60xx(priv); + nvc0_graph_init_unk64xx(priv); + nvc0_graph_init_unk58xx(priv); + nvc0_graph_init_unk80xx(priv); + nvc0_graph_init_gpc(priv); + nvc0_graph_init_tpc(priv); + nvc0_graph_init_unk88xx(priv); nvc0_graph_init_gpc_0(priv); /*nvc0_graph_init_unitplemented_c242(priv);*/ -- cgit v1.2.3 From 36798b61ed799962e08d49a632fee94b5177d4ac Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 11 Jun 2013 14:17:25 +0200 Subject: drm/nouveau/vm: perform a bar flush when flushing vm Appears to fix the regression from "drm/nvc0/vm: handle bar tlb flushes internally". nvidia always seems to do this flush after writing values. Signed-off-by: Maarten Lankhorst Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c | 4 ++++ drivers/gpu/drm/nouveau/core/subdev/vm/nvc0.c | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c index 50c66122cc89..486c813b9ea9 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c @@ -27,6 +27,7 @@ #include #include +#include #include struct nv50_vmmgr_priv { @@ -151,9 +152,12 @@ static void nv50_vm_flush(struct nouveau_vm *vm) { struct nv50_vmmgr_priv *priv = (void *)vm->vmm; + struct nouveau_bar *bar = nouveau_bar(priv); struct nouveau_engine *engine; int i, vme; + bar->flush(bar); + mutex_lock(&nv_subdev(priv)->mutex); for (i = 0; i < NVDEV_SUBDEV_NR; i++) { if (!atomic_read(&vm->engref[i])) diff --git a/drivers/gpu/drm/nouveau/core/subdev/vm/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/vm/nvc0.c index 6c3aea55d503..668cf964e4a9 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/vm/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/vm/nvc0.c @@ -29,6 +29,7 @@ #include #include #include +#include struct nvc0_vmmgr_priv { struct nouveau_vmmgr base; @@ -163,9 +164,12 @@ static void nvc0_vm_flush(struct nouveau_vm *vm) { struct nvc0_vmmgr_priv *priv = (void *)vm->vmm; + struct nouveau_bar *bar = nouveau_bar(priv); struct nouveau_vm_pgd *vpgd; u32 type; + bar->flush(bar); + type = 0x00000001; /* PAGE_ALL */ if (atomic_read(&vm->engref[NVDEV_SUBDEV_BAR])) type |= 0x00000004; /* HUB_ONLY */ -- cgit v1.2.3 From 79442c3af0525e81d4598e272abe5db60c489c62 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Mon, 17 Jun 2013 15:09:09 +0200 Subject: drm/nouveau: remove limit on gart Most graphics cards nowadays have a multiple of this limit as their vram, so limiting GART doesn't seem to make much sense. Signed-off-by: Maarten >Lnkhorst Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_ttm.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_ttm.c b/drivers/gpu/drm/nouveau/nouveau_ttm.c index 01e3154f8969..d0382f7e86c8 100644 --- a/drivers/gpu/drm/nouveau/nouveau_ttm.c +++ b/drivers/gpu/drm/nouveau/nouveau_ttm.c @@ -168,9 +168,6 @@ nouveau_gart_manager_new(struct ttm_mem_type_manager *man, struct nouveau_bo *nvbo = nouveau_bo(bo); struct nouveau_mem *node; - if (unlikely((mem->num_pages << PAGE_SHIFT) >= 512 * 1024 * 1024)) - return -ENOMEM; - node = kzalloc(sizeof(*node), GFP_KERNEL); if (!node) return -ENOMEM; @@ -403,8 +400,6 @@ nouveau_ttm_init(struct nouveau_drm *drm) /* GART init */ if (drm->agp.stat != ENABLED) { drm->gem.gart_available = nouveau_vmmgr(drm->device)->limit; - if (drm->gem.gart_available > 512 * 1024 * 1024) - drm->gem.gart_available = 512 * 1024 * 1024; } else { drm->gem.gart_available = drm->agp.size; } -- cgit v1.2.3 From d2898713fbd6431d7c09a52eb5e814805fcf8194 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 25 Jun 2013 12:26:42 +1000 Subject: drm/nouveau/kms: don't fail if there's no dcb table entries Fixes module not loading on Tesla K20. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_bios.c | 3 --- drivers/gpu/drm/nouveau/nouveau_display.c | 13 +++++++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 6aa2137e093a..e09817df7934 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -1878,9 +1878,6 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios) if (dcb->version < 0x21) merge_like_dcb_entries(dev, dcb); - if (!dcb->entries) - return -ENXIO; - /* dump connector table entries to log, if any exist */ idx = -1; while ((conn = olddcb_conn(dev, ++idx))) { diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index f17dc2ab03ec..0b6c296e6ef3 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -332,10 +332,15 @@ nouveau_display_create(struct drm_device *dev) if (nouveau_modeset == 1 || (nouveau_modeset < 0 && pclass == PCI_CLASS_DISPLAY_VGA)) { - if (nv_device(drm->device)->card_type < NV_50) - ret = nv04_display_create(dev); - else - ret = nv50_display_create(dev); + if (drm->vbios.dcb.entries) { + if (nv_device(drm->device)->card_type < NV_50) + ret = nv04_display_create(dev); + else + ret = nv50_display_create(dev); + } else { + ret = 0; + } + if (ret) goto disp_create_err; -- cgit v1.2.3 From a0fd4ec8f1ac1d966d33d1a18205b72830f9b24f Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 27 Jun 2013 13:59:01 +1000 Subject: drm/nouveau/core: move falcon class to engine/ Not really "core" per-se. About to merge Ilia's work adding another similar class for the VP2 xtensa engines, so, seems like a good time to move all these to engine/. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/Makefile | 2 +- drivers/gpu/drm/nouveau/core/core/falcon.c | 250 --------------------- drivers/gpu/drm/nouveau/core/engine/bsp/nvc0.c | 3 +- drivers/gpu/drm/nouveau/core/engine/bsp/nve0.c | 3 +- drivers/gpu/drm/nouveau/core/engine/copy/nva3.c | 13 +- drivers/gpu/drm/nouveau/core/engine/copy/nvc0.c | 10 +- drivers/gpu/drm/nouveau/core/engine/crypt/nv98.c | 2 +- drivers/gpu/drm/nouveau/core/engine/falcon.c | 249 ++++++++++++++++++++ drivers/gpu/drm/nouveau/core/engine/ppp/nvc0.c | 3 +- drivers/gpu/drm/nouveau/core/engine/vp/nvc0.c | 3 +- drivers/gpu/drm/nouveau/core/engine/vp/nve0.c | 3 +- drivers/gpu/drm/nouveau/core/include/core/falcon.h | 81 ------- .../gpu/drm/nouveau/core/include/engine/falcon.h | 81 +++++++ 13 files changed, 350 insertions(+), 353 deletions(-) delete mode 100644 drivers/gpu/drm/nouveau/core/core/falcon.c create mode 100644 drivers/gpu/drm/nouveau/core/engine/falcon.c delete mode 100644 drivers/gpu/drm/nouveau/core/include/core/falcon.h create mode 100644 drivers/gpu/drm/nouveau/core/include/engine/falcon.h diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index 3eb0d08c315b..5a2695f2759a 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile @@ -12,7 +12,6 @@ nouveau-y += core/core/engctx.o nouveau-y += core/core/engine.o nouveau-y += core/core/enum.o nouveau-y += core/core/event.o -nouveau-y += core/core/falcon.o nouveau-y += core/core/gpuobj.o nouveau-y += core/core/handle.o nouveau-y += core/core/mm.o @@ -142,6 +141,7 @@ nouveau-y += core/subdev/vm/nv44.o nouveau-y += core/subdev/vm/nv50.o nouveau-y += core/subdev/vm/nvc0.o +nouveau-y += core/engine/falcon.o nouveau-y += core/engine/dmaobj/base.o nouveau-y += core/engine/dmaobj/nv04.o nouveau-y += core/engine/dmaobj/nv50.o diff --git a/drivers/gpu/drm/nouveau/core/core/falcon.c b/drivers/gpu/drm/nouveau/core/core/falcon.c deleted file mode 100644 index e05c15777588..000000000000 --- a/drivers/gpu/drm/nouveau/core/core/falcon.c +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright 2012 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. - */ - -#include - -#include - -u32 -_nouveau_falcon_rd32(struct nouveau_object *object, u64 addr) -{ - struct nouveau_falcon *falcon = (void *)object; - return nv_rd32(falcon, falcon->addr + addr); -} - -void -_nouveau_falcon_wr32(struct nouveau_object *object, u64 addr, u32 data) -{ - struct nouveau_falcon *falcon = (void *)object; - nv_wr32(falcon, falcon->addr + addr, data); -} - -int -_nouveau_falcon_init(struct nouveau_object *object) -{ - struct nouveau_device *device = nv_device(object); - struct nouveau_falcon *falcon = (void *)object; - const struct firmware *fw; - char name[32] = "internal"; - int ret, i; - u32 caps; - - /* enable engine, and determine its capabilities */ - ret = nouveau_engine_init(&falcon->base); - if (ret) - return ret; - - if (device->chipset < 0xa3 || - device->chipset == 0xaa || device->chipset == 0xac) { - falcon->version = 0; - falcon->secret = (falcon->addr == 0x087000) ? 1 : 0; - } else { - caps = nv_ro32(falcon, 0x12c); - falcon->version = (caps & 0x0000000f); - falcon->secret = (caps & 0x00000030) >> 4; - } - - caps = nv_ro32(falcon, 0x108); - falcon->code.limit = (caps & 0x000001ff) << 8; - falcon->data.limit = (caps & 0x0003fe00) >> 1; - - nv_debug(falcon, "falcon version: %d\n", falcon->version); - nv_debug(falcon, "secret level: %d\n", falcon->secret); - nv_debug(falcon, "code limit: %d\n", falcon->code.limit); - nv_debug(falcon, "data limit: %d\n", falcon->data.limit); - - /* wait for 'uc halted' to be signalled before continuing */ - if (falcon->secret && falcon->version < 4) { - if (!falcon->version) - nv_wait(falcon, 0x008, 0x00000010, 0x00000010); - else - nv_wait(falcon, 0x180, 0x80000000, 0); - nv_wo32(falcon, 0x004, 0x00000010); - } - - /* disable all interrupts */ - nv_wo32(falcon, 0x014, 0xffffffff); - - /* no default ucode provided by the engine implementation, try and - * locate a "self-bootstrapping" firmware image for the engine - */ - if (!falcon->code.data) { - snprintf(name, sizeof(name), "nouveau/nv%02x_fuc%03x", - device->chipset, falcon->addr >> 12); - - ret = request_firmware(&fw, name, &device->pdev->dev); - if (ret == 0) { - falcon->code.data = kmemdup(fw->data, fw->size, GFP_KERNEL); - falcon->code.size = fw->size; - falcon->data.data = NULL; - falcon->data.size = 0; - release_firmware(fw); - } - - falcon->external = true; - } - - /* next step is to try and load "static code/data segment" firmware - * images for the engine - */ - if (!falcon->code.data) { - snprintf(name, sizeof(name), "nouveau/nv%02x_fuc%03xd", - device->chipset, falcon->addr >> 12); - - ret = request_firmware(&fw, name, &device->pdev->dev); - if (ret) { - nv_error(falcon, "unable to load firmware data\n"); - return ret; - } - - falcon->data.data = kmemdup(fw->data, fw->size, GFP_KERNEL); - falcon->data.size = fw->size; - release_firmware(fw); - if (!falcon->data.data) - return -ENOMEM; - - snprintf(name, sizeof(name), "nouveau/nv%02x_fuc%03xc", - device->chipset, falcon->addr >> 12); - - ret = request_firmware(&fw, name, &device->pdev->dev); - if (ret) { - nv_error(falcon, "unable to load firmware code\n"); - return ret; - } - - falcon->code.data = kmemdup(fw->data, fw->size, GFP_KERNEL); - falcon->code.size = fw->size; - release_firmware(fw); - if (!falcon->code.data) - return -ENOMEM; - } - - nv_debug(falcon, "firmware: %s (%s)\n", name, falcon->data.data ? - "static code/data segments" : "self-bootstrapping"); - - /* ensure any "self-bootstrapping" firmware image is in vram */ - if (!falcon->data.data && !falcon->core) { - ret = nouveau_gpuobj_new(object->parent, NULL, - falcon->code.size, 256, 0, - &falcon->core); - if (ret) { - nv_error(falcon, "core allocation failed, %d\n", ret); - return ret; - } - - for (i = 0; i < falcon->code.size; i += 4) - nv_wo32(falcon->core, i, falcon->code.data[i / 4]); - } - - /* upload firmware bootloader (or the full code segments) */ - if (falcon->core) { - if (device->card_type < NV_C0) - nv_wo32(falcon, 0x618, 0x04000000); - else - nv_wo32(falcon, 0x618, 0x00000114); - nv_wo32(falcon, 0x11c, 0); - nv_wo32(falcon, 0x110, falcon->core->addr >> 8); - nv_wo32(falcon, 0x114, 0); - nv_wo32(falcon, 0x118, 0x00006610); - } else { - if (falcon->code.size > falcon->code.limit || - falcon->data.size > falcon->data.limit) { - nv_error(falcon, "ucode exceeds falcon limit(s)\n"); - return -EINVAL; - } - - if (falcon->version < 3) { - nv_wo32(falcon, 0xff8, 0x00100000); - for (i = 0; i < falcon->code.size / 4; i++) - nv_wo32(falcon, 0xff4, falcon->code.data[i]); - } else { - nv_wo32(falcon, 0x180, 0x01000000); - for (i = 0; i < falcon->code.size / 4; i++) { - if ((i & 0x3f) == 0) - nv_wo32(falcon, 0x188, i >> 6); - nv_wo32(falcon, 0x184, falcon->code.data[i]); - } - } - } - - /* upload data segment (if necessary), zeroing the remainder */ - if (falcon->version < 3) { - nv_wo32(falcon, 0xff8, 0x00000000); - for (i = 0; !falcon->core && i < falcon->data.size / 4; i++) - nv_wo32(falcon, 0xff4, falcon->data.data[i]); - for (; i < falcon->data.limit; i += 4) - nv_wo32(falcon, 0xff4, 0x00000000); - } else { - nv_wo32(falcon, 0x1c0, 0x01000000); - for (i = 0; !falcon->core && i < falcon->data.size / 4; i++) - nv_wo32(falcon, 0x1c4, falcon->data.data[i]); - for (; i < falcon->data.limit / 4; i++) - nv_wo32(falcon, 0x1c4, 0x00000000); - } - - /* start it running */ - nv_wo32(falcon, 0x10c, 0x00000001); /* BLOCK_ON_FIFO */ - nv_wo32(falcon, 0x104, 0x00000000); /* ENTRY */ - nv_wo32(falcon, 0x100, 0x00000002); /* TRIGGER */ - nv_wo32(falcon, 0x048, 0x00000003); /* FIFO | CHSW */ - return 0; -} - -int -_nouveau_falcon_fini(struct nouveau_object *object, bool suspend) -{ - struct nouveau_falcon *falcon = (void *)object; - - if (!suspend) { - nouveau_gpuobj_ref(NULL, &falcon->core); - if (falcon->external) { - kfree(falcon->data.data); - kfree(falcon->code.data); - falcon->code.data = NULL; - } - } - - nv_mo32(falcon, 0x048, 0x00000003, 0x00000000); - nv_wo32(falcon, 0x014, 0xffffffff); - - return nouveau_engine_fini(&falcon->base, suspend); -} - -int -nouveau_falcon_create_(struct nouveau_object *parent, - struct nouveau_object *engine, - struct nouveau_oclass *oclass, u32 addr, bool enable, - const char *iname, const char *fname, - int length, void **pobject) -{ - struct nouveau_falcon *falcon; - int ret; - - ret = nouveau_engine_create_(parent, engine, oclass, enable, iname, - fname, length, pobject); - falcon = *pobject; - if (ret) - return ret; - - falcon->addr = addr; - return 0; -} diff --git a/drivers/gpu/drm/nouveau/core/engine/bsp/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/bsp/nvc0.c index 0a5aa6bb0870..262c9f5f5f60 100644 --- a/drivers/gpu/drm/nouveau/core/engine/bsp/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/bsp/nvc0.c @@ -22,8 +22,7 @@ * Authors: Maarten Lankhorst */ -#include - +#include #include struct nvc0_bsp_priv { diff --git a/drivers/gpu/drm/nouveau/core/engine/bsp/nve0.c b/drivers/gpu/drm/nouveau/core/engine/bsp/nve0.c index d4f23bbd75b4..c46882c83982 100644 --- a/drivers/gpu/drm/nouveau/core/engine/bsp/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/bsp/nve0.c @@ -22,8 +22,7 @@ * Authors: Ben Skeggs */ -#include - +#include #include struct nve0_bsp_priv { diff --git a/drivers/gpu/drm/nouveau/core/engine/copy/nva3.c b/drivers/gpu/drm/nouveau/core/engine/copy/nva3.c index 85f2e03dcf3f..f31527733e00 100644 --- a/drivers/gpu/drm/nouveau/core/engine/copy/nva3.c +++ b/drivers/gpu/drm/nouveau/core/engine/copy/nva3.c @@ -22,16 +22,17 @@ * Authors: Ben Skeggs */ -#include -#include -#include -#include +#include +#include +#include #include #include -#include -#include +#include +#include +#include + #include "fuc/nva3.fuc.h" diff --git a/drivers/gpu/drm/nouveau/core/engine/copy/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/copy/nvc0.c index b3ed2737e21f..993df09ad643 100644 --- a/drivers/gpu/drm/nouveau/core/engine/copy/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/copy/nvc0.c @@ -22,13 +22,15 @@ * Authors: Ben Skeggs */ -#include -#include -#include - +#include #include #include +#include +#include +#include +#include + #include "fuc/nvc0.fuc.h" struct nvc0_copy_priv { diff --git a/drivers/gpu/drm/nouveau/core/engine/crypt/nv98.c b/drivers/gpu/drm/nouveau/core/engine/crypt/nv98.c index 83ec3a30f93f..c7082377ec76 100644 --- a/drivers/gpu/drm/nouveau/core/engine/crypt/nv98.c +++ b/drivers/gpu/drm/nouveau/core/engine/crypt/nv98.c @@ -27,11 +27,11 @@ #include #include #include -#include #include #include +#include #include #include diff --git a/drivers/gpu/drm/nouveau/core/engine/falcon.c b/drivers/gpu/drm/nouveau/core/engine/falcon.c new file mode 100644 index 000000000000..3c7a31f7590e --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/falcon.c @@ -0,0 +1,249 @@ +/* + * Copyright 2012 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. + */ + +#include +#include + +u32 +_nouveau_falcon_rd32(struct nouveau_object *object, u64 addr) +{ + struct nouveau_falcon *falcon = (void *)object; + return nv_rd32(falcon, falcon->addr + addr); +} + +void +_nouveau_falcon_wr32(struct nouveau_object *object, u64 addr, u32 data) +{ + struct nouveau_falcon *falcon = (void *)object; + nv_wr32(falcon, falcon->addr + addr, data); +} + +int +_nouveau_falcon_init(struct nouveau_object *object) +{ + struct nouveau_device *device = nv_device(object); + struct nouveau_falcon *falcon = (void *)object; + const struct firmware *fw; + char name[32] = "internal"; + int ret, i; + u32 caps; + + /* enable engine, and determine its capabilities */ + ret = nouveau_engine_init(&falcon->base); + if (ret) + return ret; + + if (device->chipset < 0xa3 || + device->chipset == 0xaa || device->chipset == 0xac) { + falcon->version = 0; + falcon->secret = (falcon->addr == 0x087000) ? 1 : 0; + } else { + caps = nv_ro32(falcon, 0x12c); + falcon->version = (caps & 0x0000000f); + falcon->secret = (caps & 0x00000030) >> 4; + } + + caps = nv_ro32(falcon, 0x108); + falcon->code.limit = (caps & 0x000001ff) << 8; + falcon->data.limit = (caps & 0x0003fe00) >> 1; + + nv_debug(falcon, "falcon version: %d\n", falcon->version); + nv_debug(falcon, "secret level: %d\n", falcon->secret); + nv_debug(falcon, "code limit: %d\n", falcon->code.limit); + nv_debug(falcon, "data limit: %d\n", falcon->data.limit); + + /* wait for 'uc halted' to be signalled before continuing */ + if (falcon->secret && falcon->version < 4) { + if (!falcon->version) + nv_wait(falcon, 0x008, 0x00000010, 0x00000010); + else + nv_wait(falcon, 0x180, 0x80000000, 0); + nv_wo32(falcon, 0x004, 0x00000010); + } + + /* disable all interrupts */ + nv_wo32(falcon, 0x014, 0xffffffff); + + /* no default ucode provided by the engine implementation, try and + * locate a "self-bootstrapping" firmware image for the engine + */ + if (!falcon->code.data) { + snprintf(name, sizeof(name), "nouveau/nv%02x_fuc%03x", + device->chipset, falcon->addr >> 12); + + ret = request_firmware(&fw, name, &device->pdev->dev); + if (ret == 0) { + falcon->code.data = kmemdup(fw->data, fw->size, GFP_KERNEL); + falcon->code.size = fw->size; + falcon->data.data = NULL; + falcon->data.size = 0; + release_firmware(fw); + } + + falcon->external = true; + } + + /* next step is to try and load "static code/data segment" firmware + * images for the engine + */ + if (!falcon->code.data) { + snprintf(name, sizeof(name), "nouveau/nv%02x_fuc%03xd", + device->chipset, falcon->addr >> 12); + + ret = request_firmware(&fw, name, &device->pdev->dev); + if (ret) { + nv_error(falcon, "unable to load firmware data\n"); + return ret; + } + + falcon->data.data = kmemdup(fw->data, fw->size, GFP_KERNEL); + falcon->data.size = fw->size; + release_firmware(fw); + if (!falcon->data.data) + return -ENOMEM; + + snprintf(name, sizeof(name), "nouveau/nv%02x_fuc%03xc", + device->chipset, falcon->addr >> 12); + + ret = request_firmware(&fw, name, &device->pdev->dev); + if (ret) { + nv_error(falcon, "unable to load firmware code\n"); + return ret; + } + + falcon->code.data = kmemdup(fw->data, fw->size, GFP_KERNEL); + falcon->code.size = fw->size; + release_firmware(fw); + if (!falcon->code.data) + return -ENOMEM; + } + + nv_debug(falcon, "firmware: %s (%s)\n", name, falcon->data.data ? + "static code/data segments" : "self-bootstrapping"); + + /* ensure any "self-bootstrapping" firmware image is in vram */ + if (!falcon->data.data && !falcon->core) { + ret = nouveau_gpuobj_new(object->parent, NULL, + falcon->code.size, 256, 0, + &falcon->core); + if (ret) { + nv_error(falcon, "core allocation failed, %d\n", ret); + return ret; + } + + for (i = 0; i < falcon->code.size; i += 4) + nv_wo32(falcon->core, i, falcon->code.data[i / 4]); + } + + /* upload firmware bootloader (or the full code segments) */ + if (falcon->core) { + if (device->card_type < NV_C0) + nv_wo32(falcon, 0x618, 0x04000000); + else + nv_wo32(falcon, 0x618, 0x00000114); + nv_wo32(falcon, 0x11c, 0); + nv_wo32(falcon, 0x110, falcon->core->addr >> 8); + nv_wo32(falcon, 0x114, 0); + nv_wo32(falcon, 0x118, 0x00006610); + } else { + if (falcon->code.size > falcon->code.limit || + falcon->data.size > falcon->data.limit) { + nv_error(falcon, "ucode exceeds falcon limit(s)\n"); + return -EINVAL; + } + + if (falcon->version < 3) { + nv_wo32(falcon, 0xff8, 0x00100000); + for (i = 0; i < falcon->code.size / 4; i++) + nv_wo32(falcon, 0xff4, falcon->code.data[i]); + } else { + nv_wo32(falcon, 0x180, 0x01000000); + for (i = 0; i < falcon->code.size / 4; i++) { + if ((i & 0x3f) == 0) + nv_wo32(falcon, 0x188, i >> 6); + nv_wo32(falcon, 0x184, falcon->code.data[i]); + } + } + } + + /* upload data segment (if necessary), zeroing the remainder */ + if (falcon->version < 3) { + nv_wo32(falcon, 0xff8, 0x00000000); + for (i = 0; !falcon->core && i < falcon->data.size / 4; i++) + nv_wo32(falcon, 0xff4, falcon->data.data[i]); + for (; i < falcon->data.limit; i += 4) + nv_wo32(falcon, 0xff4, 0x00000000); + } else { + nv_wo32(falcon, 0x1c0, 0x01000000); + for (i = 0; !falcon->core && i < falcon->data.size / 4; i++) + nv_wo32(falcon, 0x1c4, falcon->data.data[i]); + for (; i < falcon->data.limit / 4; i++) + nv_wo32(falcon, 0x1c4, 0x00000000); + } + + /* start it running */ + nv_wo32(falcon, 0x10c, 0x00000001); /* BLOCK_ON_FIFO */ + nv_wo32(falcon, 0x104, 0x00000000); /* ENTRY */ + nv_wo32(falcon, 0x100, 0x00000002); /* TRIGGER */ + nv_wo32(falcon, 0x048, 0x00000003); /* FIFO | CHSW */ + return 0; +} + +int +_nouveau_falcon_fini(struct nouveau_object *object, bool suspend) +{ + struct nouveau_falcon *falcon = (void *)object; + + if (!suspend) { + nouveau_gpuobj_ref(NULL, &falcon->core); + if (falcon->external) { + kfree(falcon->data.data); + kfree(falcon->code.data); + falcon->code.data = NULL; + } + } + + nv_mo32(falcon, 0x048, 0x00000003, 0x00000000); + nv_wo32(falcon, 0x014, 0xffffffff); + + return nouveau_engine_fini(&falcon->base, suspend); +} + +int +nouveau_falcon_create_(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, u32 addr, bool enable, + const char *iname, const char *fname, + int length, void **pobject) +{ + struct nouveau_falcon *falcon; + int ret; + + ret = nouveau_engine_create_(parent, engine, oclass, enable, iname, + fname, length, pobject); + falcon = *pobject; + if (ret) + return ret; + + falcon->addr = addr; + return 0; +} diff --git a/drivers/gpu/drm/nouveau/core/engine/ppp/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/ppp/nvc0.c index ebf0d860e2dd..98072c1ff360 100644 --- a/drivers/gpu/drm/nouveau/core/engine/ppp/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/ppp/nvc0.c @@ -22,8 +22,7 @@ * Authors: Maarten Lankhorst */ -#include - +#include #include struct nvc0_ppp_priv { diff --git a/drivers/gpu/drm/nouveau/core/engine/vp/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/vp/nvc0.c index f761949d7039..1879229b60eb 100644 --- a/drivers/gpu/drm/nouveau/core/engine/vp/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/vp/nvc0.c @@ -22,8 +22,7 @@ * Authors: Maarten Lankhorst */ -#include - +#include #include struct nvc0_vp_priv { diff --git a/drivers/gpu/drm/nouveau/core/engine/vp/nve0.c b/drivers/gpu/drm/nouveau/core/engine/vp/nve0.c index 2384ce5dbe16..d28ecbf7bc49 100644 --- a/drivers/gpu/drm/nouveau/core/engine/vp/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/vp/nve0.c @@ -22,8 +22,7 @@ * Authors: Ben Skeggs */ -#include - +#include #include struct nve0_vp_priv { diff --git a/drivers/gpu/drm/nouveau/core/include/core/falcon.h b/drivers/gpu/drm/nouveau/core/include/core/falcon.h deleted file mode 100644 index 1edec386ab36..000000000000 --- a/drivers/gpu/drm/nouveau/core/include/core/falcon.h +++ /dev/null @@ -1,81 +0,0 @@ -#ifndef __NOUVEAU_FALCON_H__ -#define __NOUVEAU_FALCON_H__ - -#include -#include -#include - -struct nouveau_falcon_chan { - struct nouveau_engctx base; -}; - -#define nouveau_falcon_context_create(p,e,c,g,s,a,f,d) \ - nouveau_engctx_create((p), (e), (c), (g), (s), (a), (f), (d)) -#define nouveau_falcon_context_destroy(d) \ - nouveau_engctx_destroy(&(d)->base) -#define nouveau_falcon_context_init(d) \ - nouveau_engctx_init(&(d)->base) -#define nouveau_falcon_context_fini(d,s) \ - nouveau_engctx_fini(&(d)->base, (s)) - -#define _nouveau_falcon_context_ctor _nouveau_engctx_ctor -#define _nouveau_falcon_context_dtor _nouveau_engctx_dtor -#define _nouveau_falcon_context_init _nouveau_engctx_init -#define _nouveau_falcon_context_fini _nouveau_engctx_fini -#define _nouveau_falcon_context_rd32 _nouveau_engctx_rd32 -#define _nouveau_falcon_context_wr32 _nouveau_engctx_wr32 - -struct nouveau_falcon_data { - bool external; -}; - -struct nouveau_falcon { - struct nouveau_engine base; - - u32 addr; - u8 version; - u8 secret; - - struct nouveau_gpuobj *core; - bool external; - - struct { - u32 limit; - u32 *data; - u32 size; - } code; - - struct { - u32 limit; - u32 *data; - u32 size; - } data; -}; - -#define nv_falcon(priv) (&(priv)->base) - -#define nouveau_falcon_create(p,e,c,b,d,i,f,r) \ - nouveau_falcon_create_((p), (e), (c), (b), (d), (i), (f), \ - sizeof(**r),(void **)r) -#define nouveau_falcon_destroy(p) \ - nouveau_engine_destroy(&(p)->base) -#define nouveau_falcon_init(p) ({ \ - struct nouveau_falcon *falcon = (p); \ - _nouveau_falcon_init(nv_object(falcon)); \ -}) -#define nouveau_falcon_fini(p,s) ({ \ - struct nouveau_falcon *falcon = (p); \ - _nouveau_falcon_fini(nv_object(falcon), (s)); \ -}) - -int nouveau_falcon_create_(struct nouveau_object *, struct nouveau_object *, - struct nouveau_oclass *, u32, bool, const char *, - const char *, int, void **); - -#define _nouveau_falcon_dtor _nouveau_engine_dtor -int _nouveau_falcon_init(struct nouveau_object *); -int _nouveau_falcon_fini(struct nouveau_object *, bool); -u32 _nouveau_falcon_rd32(struct nouveau_object *, u64); -void _nouveau_falcon_wr32(struct nouveau_object *, u64, u32); - -#endif diff --git a/drivers/gpu/drm/nouveau/core/include/engine/falcon.h b/drivers/gpu/drm/nouveau/core/include/engine/falcon.h new file mode 100644 index 000000000000..1edec386ab36 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/include/engine/falcon.h @@ -0,0 +1,81 @@ +#ifndef __NOUVEAU_FALCON_H__ +#define __NOUVEAU_FALCON_H__ + +#include +#include +#include + +struct nouveau_falcon_chan { + struct nouveau_engctx base; +}; + +#define nouveau_falcon_context_create(p,e,c,g,s,a,f,d) \ + nouveau_engctx_create((p), (e), (c), (g), (s), (a), (f), (d)) +#define nouveau_falcon_context_destroy(d) \ + nouveau_engctx_destroy(&(d)->base) +#define nouveau_falcon_context_init(d) \ + nouveau_engctx_init(&(d)->base) +#define nouveau_falcon_context_fini(d,s) \ + nouveau_engctx_fini(&(d)->base, (s)) + +#define _nouveau_falcon_context_ctor _nouveau_engctx_ctor +#define _nouveau_falcon_context_dtor _nouveau_engctx_dtor +#define _nouveau_falcon_context_init _nouveau_engctx_init +#define _nouveau_falcon_context_fini _nouveau_engctx_fini +#define _nouveau_falcon_context_rd32 _nouveau_engctx_rd32 +#define _nouveau_falcon_context_wr32 _nouveau_engctx_wr32 + +struct nouveau_falcon_data { + bool external; +}; + +struct nouveau_falcon { + struct nouveau_engine base; + + u32 addr; + u8 version; + u8 secret; + + struct nouveau_gpuobj *core; + bool external; + + struct { + u32 limit; + u32 *data; + u32 size; + } code; + + struct { + u32 limit; + u32 *data; + u32 size; + } data; +}; + +#define nv_falcon(priv) (&(priv)->base) + +#define nouveau_falcon_create(p,e,c,b,d,i,f,r) \ + nouveau_falcon_create_((p), (e), (c), (b), (d), (i), (f), \ + sizeof(**r),(void **)r) +#define nouveau_falcon_destroy(p) \ + nouveau_engine_destroy(&(p)->base) +#define nouveau_falcon_init(p) ({ \ + struct nouveau_falcon *falcon = (p); \ + _nouveau_falcon_init(nv_object(falcon)); \ +}) +#define nouveau_falcon_fini(p,s) ({ \ + struct nouveau_falcon *falcon = (p); \ + _nouveau_falcon_fini(nv_object(falcon), (s)); \ +}) + +int nouveau_falcon_create_(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, u32, bool, const char *, + const char *, int, void **); + +#define _nouveau_falcon_dtor _nouveau_engine_dtor +int _nouveau_falcon_init(struct nouveau_object *); +int _nouveau_falcon_fini(struct nouveau_object *, bool); +u32 _nouveau_falcon_rd32(struct nouveau_object *, u64); +void _nouveau_falcon_wr32(struct nouveau_object *, u64, u32); + +#endif -- cgit v1.2.3 From 0d4a1450c95801c21ba4db109303fbad62378b91 Mon Sep 17 00:00:00 2001 From: Ilia Mirkin Date: Thu, 27 Jun 2013 14:04:20 +1000 Subject: drm/nouveau/vdec: fork vp3 implementations from vp2 Signed-off-by: Ilia Mirkin Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/Makefile | 2 + drivers/gpu/drm/nouveau/core/engine/bsp/nv98.c | 93 +++++++++++++++++++++++ drivers/gpu/drm/nouveau/core/engine/device/nv50.c | 28 +++---- drivers/gpu/drm/nouveau/core/engine/vp/nv98.c | 93 +++++++++++++++++++++++ drivers/gpu/drm/nouveau/core/include/engine/bsp.h | 1 + drivers/gpu/drm/nouveau/core/include/engine/vp.h | 1 + 6 files changed, 204 insertions(+), 14 deletions(-) create mode 100644 drivers/gpu/drm/nouveau/core/engine/bsp/nv98.c create mode 100644 drivers/gpu/drm/nouveau/core/engine/vp/nv98.c diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index 5a2695f2759a..78f9aa24f1fd 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile @@ -148,6 +148,7 @@ nouveau-y += core/engine/dmaobj/nv50.o nouveau-y += core/engine/dmaobj/nvc0.o nouveau-y += core/engine/dmaobj/nvd0.o nouveau-y += core/engine/bsp/nv84.o +nouveau-y += core/engine/bsp/nv98.o nouveau-y += core/engine/bsp/nvc0.o nouveau-y += core/engine/bsp/nve0.o nouveau-y += core/engine/copy/nva3.o @@ -222,6 +223,7 @@ nouveau-y += core/engine/software/nv10.o nouveau-y += core/engine/software/nv50.o nouveau-y += core/engine/software/nvc0.o nouveau-y += core/engine/vp/nv84.o +nouveau-y += core/engine/vp/nv98.o nouveau-y += core/engine/vp/nvc0.o nouveau-y += core/engine/vp/nve0.o diff --git a/drivers/gpu/drm/nouveau/core/engine/bsp/nv98.c b/drivers/gpu/drm/nouveau/core/engine/bsp/nv98.c new file mode 100644 index 000000000000..8bf92b0e6d82 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/bsp/nv98.c @@ -0,0 +1,93 @@ +/* + * Copyright 2012 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 +#include + +#include + +struct nv98_bsp_priv { + struct nouveau_engine base; +}; + +/******************************************************************************* + * BSP object classes + ******************************************************************************/ + +static struct nouveau_oclass +nv98_bsp_sclass[] = { + {}, +}; + +/******************************************************************************* + * BSP context + ******************************************************************************/ + +static struct nouveau_oclass +nv98_bsp_cclass = { + .handle = NV_ENGCTX(BSP, 0x98), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nouveau_engctx_ctor, + .dtor = _nouveau_engctx_dtor, + .init = _nouveau_engctx_init, + .fini = _nouveau_engctx_fini, + .rd32 = _nouveau_engctx_rd32, + .wr32 = _nouveau_engctx_wr32, + }, +}; + +/******************************************************************************* + * BSP engine/subdev functions + ******************************************************************************/ + +static int +nv98_bsp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv98_bsp_priv *priv; + int ret; + + ret = nouveau_engine_create(parent, engine, oclass, true, + "PBSP", "bsp", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x04008000; + nv_engine(priv)->cclass = &nv98_bsp_cclass; + nv_engine(priv)->sclass = nv98_bsp_sclass; + return 0; +} + +struct nouveau_oclass +nv98_bsp_oclass = { + .handle = NV_ENGINE(BSP, 0x98), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv98_bsp_ctor, + .dtor = _nouveau_engine_dtor, + .init = _nouveau_engine_init, + .fini = _nouveau_engine_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nv50.c b/drivers/gpu/drm/nouveau/core/engine/device/nv50.c index 5c1db3e1f0f2..ffc18b80c5d9 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/nv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/nv50.c @@ -227,9 +227,9 @@ nv50_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_FIFO ] = &nv84_fifo_oclass; device->oclass[NVDEV_ENGINE_SW ] = &nv50_software_oclass; device->oclass[NVDEV_ENGINE_GR ] = &nv50_graph_oclass; - device->oclass[NVDEV_ENGINE_VP ] = &nv84_vp_oclass; + device->oclass[NVDEV_ENGINE_VP ] = &nv98_vp_oclass; device->oclass[NVDEV_ENGINE_CRYPT ] = &nv98_crypt_oclass; - device->oclass[NVDEV_ENGINE_BSP ] = &nv84_bsp_oclass; + device->oclass[NVDEV_ENGINE_BSP ] = &nv98_bsp_oclass; device->oclass[NVDEV_ENGINE_PPP ] = &nv98_ppp_oclass; device->oclass[NVDEV_ENGINE_DISP ] = &nv94_disp_oclass; break; @@ -279,9 +279,9 @@ nv50_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_FIFO ] = &nv84_fifo_oclass; device->oclass[NVDEV_ENGINE_SW ] = &nv50_software_oclass; device->oclass[NVDEV_ENGINE_GR ] = &nv50_graph_oclass; - device->oclass[NVDEV_ENGINE_VP ] = &nv84_vp_oclass; + device->oclass[NVDEV_ENGINE_VP ] = &nv98_vp_oclass; device->oclass[NVDEV_ENGINE_CRYPT ] = &nv98_crypt_oclass; - device->oclass[NVDEV_ENGINE_BSP ] = &nv84_bsp_oclass; + device->oclass[NVDEV_ENGINE_BSP ] = &nv98_bsp_oclass; device->oclass[NVDEV_ENGINE_PPP ] = &nv98_ppp_oclass; device->oclass[NVDEV_ENGINE_DISP ] = &nv94_disp_oclass; break; @@ -305,9 +305,9 @@ nv50_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_FIFO ] = &nv84_fifo_oclass; device->oclass[NVDEV_ENGINE_SW ] = &nv50_software_oclass; device->oclass[NVDEV_ENGINE_GR ] = &nv50_graph_oclass; - device->oclass[NVDEV_ENGINE_VP ] = &nv84_vp_oclass; + device->oclass[NVDEV_ENGINE_VP ] = &nv98_vp_oclass; device->oclass[NVDEV_ENGINE_CRYPT ] = &nv98_crypt_oclass; - device->oclass[NVDEV_ENGINE_BSP ] = &nv84_bsp_oclass; + device->oclass[NVDEV_ENGINE_BSP ] = &nv98_bsp_oclass; device->oclass[NVDEV_ENGINE_PPP ] = &nv98_ppp_oclass; device->oclass[NVDEV_ENGINE_DISP ] = &nv94_disp_oclass; break; @@ -332,8 +332,8 @@ nv50_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_SW ] = &nv50_software_oclass; device->oclass[NVDEV_ENGINE_GR ] = &nv50_graph_oclass; device->oclass[NVDEV_ENGINE_MPEG ] = &nv84_mpeg_oclass; - device->oclass[NVDEV_ENGINE_VP ] = &nv84_vp_oclass; - device->oclass[NVDEV_ENGINE_BSP ] = &nv84_bsp_oclass; + device->oclass[NVDEV_ENGINE_VP ] = &nv98_vp_oclass; + device->oclass[NVDEV_ENGINE_BSP ] = &nv98_bsp_oclass; device->oclass[NVDEV_ENGINE_PPP ] = &nv98_ppp_oclass; device->oclass[NVDEV_ENGINE_COPY0 ] = &nva3_copy_oclass; device->oclass[NVDEV_ENGINE_DISP ] = &nva3_disp_oclass; @@ -358,8 +358,8 @@ nv50_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_FIFO ] = &nv84_fifo_oclass; device->oclass[NVDEV_ENGINE_SW ] = &nv50_software_oclass; device->oclass[NVDEV_ENGINE_GR ] = &nv50_graph_oclass; - device->oclass[NVDEV_ENGINE_VP ] = &nv84_vp_oclass; - device->oclass[NVDEV_ENGINE_BSP ] = &nv84_bsp_oclass; + device->oclass[NVDEV_ENGINE_VP ] = &nv98_vp_oclass; + device->oclass[NVDEV_ENGINE_BSP ] = &nv98_bsp_oclass; device->oclass[NVDEV_ENGINE_PPP ] = &nv98_ppp_oclass; device->oclass[NVDEV_ENGINE_COPY0 ] = &nva3_copy_oclass; device->oclass[NVDEV_ENGINE_DISP ] = &nva3_disp_oclass; @@ -384,8 +384,8 @@ nv50_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_FIFO ] = &nv84_fifo_oclass; device->oclass[NVDEV_ENGINE_SW ] = &nv50_software_oclass; device->oclass[NVDEV_ENGINE_GR ] = &nv50_graph_oclass; - device->oclass[NVDEV_ENGINE_VP ] = &nv84_vp_oclass; - device->oclass[NVDEV_ENGINE_BSP ] = &nv84_bsp_oclass; + device->oclass[NVDEV_ENGINE_VP ] = &nv98_vp_oclass; + device->oclass[NVDEV_ENGINE_BSP ] = &nv98_bsp_oclass; device->oclass[NVDEV_ENGINE_PPP ] = &nv98_ppp_oclass; device->oclass[NVDEV_ENGINE_COPY0 ] = &nva3_copy_oclass; device->oclass[NVDEV_ENGINE_DISP ] = &nva3_disp_oclass; @@ -410,8 +410,8 @@ nv50_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_FIFO ] = &nv84_fifo_oclass; device->oclass[NVDEV_ENGINE_SW ] = &nv50_software_oclass; device->oclass[NVDEV_ENGINE_GR ] = &nv50_graph_oclass; - device->oclass[NVDEV_ENGINE_VP ] = &nv84_vp_oclass; - device->oclass[NVDEV_ENGINE_BSP ] = &nv84_bsp_oclass; + device->oclass[NVDEV_ENGINE_VP ] = &nv98_vp_oclass; + device->oclass[NVDEV_ENGINE_BSP ] = &nv98_bsp_oclass; device->oclass[NVDEV_ENGINE_PPP ] = &nv98_ppp_oclass; device->oclass[NVDEV_ENGINE_COPY0 ] = &nva3_copy_oclass; device->oclass[NVDEV_ENGINE_DISP ] = &nva3_disp_oclass; diff --git a/drivers/gpu/drm/nouveau/core/engine/vp/nv98.c b/drivers/gpu/drm/nouveau/core/engine/vp/nv98.c new file mode 100644 index 000000000000..8a8236bc84de --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/vp/nv98.c @@ -0,0 +1,93 @@ +/* + * Copyright 2012 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 +#include + +#include + +struct nv98_vp_priv { + struct nouveau_engine base; +}; + +/******************************************************************************* + * VP object classes + ******************************************************************************/ + +static struct nouveau_oclass +nv98_vp_sclass[] = { + {}, +}; + +/******************************************************************************* + * PVP context + ******************************************************************************/ + +static struct nouveau_oclass +nv98_vp_cclass = { + .handle = NV_ENGCTX(VP, 0x98), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nouveau_engctx_ctor, + .dtor = _nouveau_engctx_dtor, + .init = _nouveau_engctx_init, + .fini = _nouveau_engctx_fini, + .rd32 = _nouveau_engctx_rd32, + .wr32 = _nouveau_engctx_wr32, + }, +}; + +/******************************************************************************* + * PVP engine/subdev functions + ******************************************************************************/ + +static int +nv98_vp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv98_vp_priv *priv; + int ret; + + ret = nouveau_engine_create(parent, engine, oclass, true, + "PVP", "vp", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x01020000; + nv_engine(priv)->cclass = &nv98_vp_cclass; + nv_engine(priv)->sclass = nv98_vp_sclass; + return 0; +} + +struct nouveau_oclass +nv98_vp_oclass = { + .handle = NV_ENGINE(VP, 0x98), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv98_vp_ctor, + .dtor = _nouveau_engine_dtor, + .init = _nouveau_engine_init, + .fini = _nouveau_engine_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/include/engine/bsp.h b/drivers/gpu/drm/nouveau/core/include/engine/bsp.h index 13ccdf54dfad..67662e2c4547 100644 --- a/drivers/gpu/drm/nouveau/core/include/engine/bsp.h +++ b/drivers/gpu/drm/nouveau/core/include/engine/bsp.h @@ -2,6 +2,7 @@ #define __NOUVEAU_BSP_H__ extern struct nouveau_oclass nv84_bsp_oclass; +extern struct nouveau_oclass nv98_bsp_oclass; extern struct nouveau_oclass nvc0_bsp_oclass; extern struct nouveau_oclass nve0_bsp_oclass; diff --git a/drivers/gpu/drm/nouveau/core/include/engine/vp.h b/drivers/gpu/drm/nouveau/core/include/engine/vp.h index d7b287b115bf..39baebec7fbb 100644 --- a/drivers/gpu/drm/nouveau/core/include/engine/vp.h +++ b/drivers/gpu/drm/nouveau/core/include/engine/vp.h @@ -2,6 +2,7 @@ #define __NOUVEAU_VP_H__ extern struct nouveau_oclass nv84_vp_oclass; +extern struct nouveau_oclass nv98_vp_oclass; extern struct nouveau_oclass nvc0_vp_oclass; extern struct nouveau_oclass nve0_vp_oclass; -- cgit v1.2.3 From 44b1e3bd6adc050fac1daccee5bbff019daadc8e Mon Sep 17 00:00:00 2001 From: Ilia Mirkin Date: Thu, 27 Jun 2013 14:08:22 +1000 Subject: drm/nouveau/core: xtensa engine base class implementation Signed-off-by: Ilia Mirkin Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/Makefile | 1 + drivers/gpu/drm/nouveau/core/engine/xtensa.c | 170 +++++++++++++++++++++ .../gpu/drm/nouveau/core/include/engine/xtensa.h | 38 +++++ 3 files changed, 209 insertions(+) create mode 100644 drivers/gpu/drm/nouveau/core/engine/xtensa.c create mode 100644 drivers/gpu/drm/nouveau/core/include/engine/xtensa.h diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index 78f9aa24f1fd..b59cfd79bd79 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile @@ -142,6 +142,7 @@ nouveau-y += core/subdev/vm/nv50.o nouveau-y += core/subdev/vm/nvc0.o nouveau-y += core/engine/falcon.o +nouveau-y += core/engine/xtensa.o nouveau-y += core/engine/dmaobj/base.o nouveau-y += core/engine/dmaobj/nv04.o nouveau-y += core/engine/dmaobj/nv50.o diff --git a/drivers/gpu/drm/nouveau/core/engine/xtensa.c b/drivers/gpu/drm/nouveau/core/engine/xtensa.c new file mode 100644 index 000000000000..0639bc59d0a5 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/xtensa.c @@ -0,0 +1,170 @@ +/* + * Copyright 2013 Ilia Mirkin + * + * 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. + */ + +#include + +u32 +_nouveau_xtensa_rd32(struct nouveau_object *object, u64 addr) +{ + struct nouveau_xtensa *xtensa = (void *)object; + return nv_rd32(xtensa, xtensa->addr + addr); +} + +void +_nouveau_xtensa_wr32(struct nouveau_object *object, u64 addr, u32 data) +{ + struct nouveau_xtensa *xtensa = (void *)object; + nv_wr32(xtensa, xtensa->addr + addr, data); +} + +int +_nouveau_xtensa_engctx_ctor(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nouveau_engctx *engctx; + int ret; + + ret = nouveau_engctx_create(parent, engine, oclass, NULL, + 0x10000, 0x1000, + NVOBJ_FLAG_ZERO_ALLOC, &engctx); + *pobject = nv_object(engctx); + return ret; +} + +void +_nouveau_xtensa_intr(struct nouveau_subdev *subdev) +{ + struct nouveau_xtensa *xtensa = (void *)subdev; + u32 unk104 = nv_ro32(xtensa, 0xd04); + u32 intr = nv_ro32(xtensa, 0xc20); + u32 chan = nv_ro32(xtensa, 0xc28); + u32 unk10c = nv_ro32(xtensa, 0xd0c); + + if (intr & 0x10) + nv_warn(xtensa, "Watchdog interrupt, engine hung.\n"); + nv_wo32(xtensa, 0xc20, intr); + intr = nv_ro32(xtensa, 0xc20); + if (unk104 == 0x10001 && unk10c == 0x200 && chan && !intr) { + nv_debug(xtensa, "Enabling FIFO_CTRL\n"); + nv_mask(xtensa, xtensa->addr + 0xd94, 0, xtensa->fifo_val); + } +} + +int +nouveau_xtensa_create_(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, u32 addr, bool enable, + const char *iname, const char *fname, + int length, void **pobject) +{ + struct nouveau_xtensa *xtensa; + int ret; + + ret = nouveau_engine_create_(parent, engine, oclass, enable, iname, + fname, length, pobject); + xtensa = *pobject; + if (ret) + return ret; + + nv_subdev(xtensa)->intr = _nouveau_xtensa_intr; + + xtensa->addr = addr; + + return 0; +} + +int +_nouveau_xtensa_init(struct nouveau_object *object) +{ + struct nouveau_device *device = nv_device(object); + struct nouveau_xtensa *xtensa = (void *)object; + const struct firmware *fw; + char name[32]; + int i, ret; + u32 tmp; + + ret = nouveau_engine_init(&xtensa->base); + if (ret) + return ret; + + if (!xtensa->gpu_fw) { + snprintf(name, sizeof(name), "nouveau/nv84_xuc%03x", + xtensa->addr >> 12); + + ret = request_firmware(&fw, name, &device->pdev->dev); + if (ret) { + nv_warn(xtensa, "unable to load firmware %s\n", name); + return ret; + } + + ret = nouveau_gpuobj_new(object, NULL, fw->size, 0x1000, 0, + &xtensa->gpu_fw); + if (ret) { + release_firmware(fw); + return ret; + } + + nv_debug(xtensa, "Loading firmware to address: 0x%llx\n", + xtensa->gpu_fw->addr); + + for (i = 0; i < fw->size / 4; i++) + nv_wo32(xtensa->gpu_fw, i * 4, *((u32 *)fw->data + i)); + release_firmware(fw); + } + + nv_wo32(xtensa, 0xd10, 0x1fffffff); /* ?? */ + nv_wo32(xtensa, 0xd08, 0x0fffffff); /* ?? */ + + nv_wo32(xtensa, 0xd28, xtensa->unkd28); /* ?? */ + nv_wo32(xtensa, 0xc20, 0x3f); /* INTR */ + nv_wo32(xtensa, 0xd84, 0x3f); /* INTR_EN */ + + nv_wo32(xtensa, 0xcc0, xtensa->gpu_fw->addr >> 8); /* XT_REGION_BASE */ + nv_wo32(xtensa, 0xcc4, 0x1c); /* XT_REGION_SETUP */ + nv_wo32(xtensa, 0xcc8, xtensa->gpu_fw->size >> 8); /* XT_REGION_LIMIT */ + + tmp = nv_rd32(xtensa, 0x0); + nv_wo32(xtensa, 0xde0, tmp); /* SCRATCH_H2X */ + + nv_wo32(xtensa, 0xce8, 0xf); /* XT_REGION_SETUP */ + + nv_wo32(xtensa, 0xc20, 0x3f); /* INTR */ + nv_wo32(xtensa, 0xd84, 0x3f); /* INTR_EN */ + + return 0; +} + +int +_nouveau_xtensa_fini(struct nouveau_object *object, bool suspend) +{ + struct nouveau_xtensa *xtensa = (void *)object; + + nv_wo32(xtensa, 0xd84, 0); /* INTR_EN */ + nv_wo32(xtensa, 0xd94, 0); /* FIFO_CTRL */ + + if (!suspend) + nouveau_gpuobj_ref(NULL, &xtensa->gpu_fw); + + return nouveau_engine_fini(&xtensa->base, suspend); +} diff --git a/drivers/gpu/drm/nouveau/core/include/engine/xtensa.h b/drivers/gpu/drm/nouveau/core/include/engine/xtensa.h new file mode 100644 index 000000000000..306100f31f02 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/include/engine/xtensa.h @@ -0,0 +1,38 @@ +#ifndef __NOUVEAU_XTENSA_H__ +#define __NOUVEAU_XTENSA_H__ + +#include +#include +#include + +struct nouveau_xtensa { + struct nouveau_engine base; + + u32 addr; + struct nouveau_gpuobj *gpu_fw; + u32 fifo_val; + u32 unkd28; +}; + +#define nouveau_xtensa_create(p,e,c,b,d,i,f,r) \ + nouveau_xtensa_create_((p), (e), (c), (b), (d), (i), (f), \ + sizeof(**r),(void **)r) + +int _nouveau_xtensa_engctx_ctor(struct nouveau_object *, + struct nouveau_object *, + struct nouveau_oclass *, void *, u32, + struct nouveau_object **); + +void _nouveau_xtensa_intr(struct nouveau_subdev *); +int nouveau_xtensa_create_(struct nouveau_object *, + struct nouveau_object *, + struct nouveau_oclass *, u32, bool, + const char *, const char *, + int, void **); +#define _nouveau_xtensa_dtor _nouveau_engine_dtor +int _nouveau_xtensa_init(struct nouveau_object *); +int _nouveau_xtensa_fini(struct nouveau_object *, bool); +u32 _nouveau_xtensa_rd32(struct nouveau_object *, u64); +void _nouveau_xtensa_wr32(struct nouveau_object *, u64, u32); + +#endif -- cgit v1.2.3 From a0376b1481fdb9c9e8064ea0c5af8bd80da3f8f3 Mon Sep 17 00:00:00 2001 From: Ilia Mirkin Date: Thu, 27 Jun 2013 14:12:46 +1000 Subject: drm/nouveau/vp/nv84: initial vp2 engine implementation Signed-off-by: Ilia Mirkin Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c | 2 ++ drivers/gpu/drm/nouveau/core/engine/vp/nv84.c | 27 ++++++++++++------------- drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c | 1 + drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c | 1 + 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c index 35b94bd18808..65519dc75ba7 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c @@ -56,6 +56,7 @@ nv84_fifo_context_attach(struct nouveau_object *parent, switch (nv_engidx(object->engine)) { case NVDEV_ENGINE_SW : return 0; case NVDEV_ENGINE_GR : addr = 0x0020; break; + case NVDEV_ENGINE_VP : addr = 0x0040; break; case NVDEV_ENGINE_MPEG : addr = 0x0060; break; case NVDEV_ENGINE_CRYPT: addr = 0x00a0; break; case NVDEV_ENGINE_COPY0: addr = 0x00c0; break; @@ -89,6 +90,7 @@ nv84_fifo_context_detach(struct nouveau_object *parent, bool suspend, switch (nv_engidx(object->engine)) { case NVDEV_ENGINE_SW : return 0; case NVDEV_ENGINE_GR : engn = 0; addr = 0x0020; break; + case NVDEV_ENGINE_VP : engn = 3; addr = 0x0040; break; case NVDEV_ENGINE_MPEG : engn = 1; addr = 0x0060; break; case NVDEV_ENGINE_CRYPT: engn = 4; addr = 0x00a0; break; case NVDEV_ENGINE_COPY0: engn = 2; addr = 0x00c0; break; diff --git a/drivers/gpu/drm/nouveau/core/engine/vp/nv84.c b/drivers/gpu/drm/nouveau/core/engine/vp/nv84.c index 261cd96e6951..fd6272b8cdb2 100644 --- a/drivers/gpu/drm/nouveau/core/engine/vp/nv84.c +++ b/drivers/gpu/drm/nouveau/core/engine/vp/nv84.c @@ -19,24 +19,19 @@ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * - * Authors: Ben Skeggs + * Authors: Ben Skeggs, Ilia Mirkin */ -#include -#include - +#include #include -struct nv84_vp_priv { - struct nouveau_engine base; -}; - /******************************************************************************* * VP object classes ******************************************************************************/ static struct nouveau_oclass nv84_vp_sclass[] = { + { 0x7476, &nouveau_object_ofuncs }, {}, }; @@ -48,7 +43,7 @@ static struct nouveau_oclass nv84_vp_cclass = { .handle = NV_ENGCTX(VP, 0x84), .ofuncs = &(struct nouveau_ofuncs) { - .ctor = _nouveau_engctx_ctor, + .ctor = _nouveau_xtensa_engctx_ctor, .dtor = _nouveau_engctx_dtor, .init = _nouveau_engctx_init, .fini = _nouveau_engctx_fini, @@ -66,10 +61,10 @@ nv84_vp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, void *data, u32 size, struct nouveau_object **pobject) { - struct nv84_vp_priv *priv; + struct nouveau_xtensa *priv; int ret; - ret = nouveau_engine_create(parent, engine, oclass, true, + ret = nouveau_xtensa_create(parent, engine, oclass, 0xf000, true, "PVP", "vp", &priv); *pobject = nv_object(priv); if (ret) @@ -78,6 +73,8 @@ nv84_vp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, nv_subdev(priv)->unit = 0x01020000; nv_engine(priv)->cclass = &nv84_vp_cclass; nv_engine(priv)->sclass = nv84_vp_sclass; + priv->fifo_val = 0x111; + priv->unkd28 = 0x9c544; return 0; } @@ -86,8 +83,10 @@ nv84_vp_oclass = { .handle = NV_ENGINE(VP, 0x84), .ofuncs = &(struct nouveau_ofuncs) { .ctor = nv84_vp_ctor, - .dtor = _nouveau_engine_dtor, - .init = _nouveau_engine_init, - .fini = _nouveau_engine_fini, + .dtor = _nouveau_xtensa_dtor, + .init = _nouveau_xtensa_init, + .fini = _nouveau_xtensa_fini, + .rd32 = _nouveau_xtensa_rd32, + .wr32 = _nouveau_xtensa_wr32, }, }; diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c index d796924f9930..0cb322a5e72c 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c @@ -35,6 +35,7 @@ nv50_mc_intr[] = { { 0x00001000, NVDEV_ENGINE_GR }, { 0x00004000, NVDEV_ENGINE_CRYPT }, /* NV84- */ { 0x00008000, NVDEV_ENGINE_BSP }, /* NV84- */ + { 0x00020000, NVDEV_ENGINE_VP }, /* NV84- */ { 0x00100000, NVDEV_SUBDEV_TIMER }, { 0x00200000, NVDEV_SUBDEV_GPIO }, { 0x04000000, NVDEV_ENGINE_DISP }, diff --git a/drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c index 486c813b9ea9..fcae49f678b5 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c @@ -172,6 +172,7 @@ nv50_vm_flush(struct nouveau_vm *vm) switch (i) { case NVDEV_ENGINE_GR : vme = 0x00; break; + case NVDEV_ENGINE_VP : vme = 0x01; break; case NVDEV_SUBDEV_BAR : vme = 0x06; break; case NVDEV_ENGINE_MPEG : vme = 0x08; break; case NVDEV_ENGINE_CRYPT: vme = 0x0a; break; -- cgit v1.2.3 From 05f9a5bc58381f58095d8789e1c2d4e18758c2bc Mon Sep 17 00:00:00 2001 From: Ilia Mirkin Date: Thu, 27 Jun 2013 14:14:01 +1000 Subject: drm/nouveau/bsp/nv84: initial vp2 engine implementation Signed-off-by: Ilia Mirkin Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/core/engine/bsp/nv84.c | 27 ++++++++++++------------- drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c | 2 ++ drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c | 1 + 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/bsp/nv84.c b/drivers/gpu/drm/nouveau/core/engine/bsp/nv84.c index 1d9f614cb97d..1e8e75c0684a 100644 --- a/drivers/gpu/drm/nouveau/core/engine/bsp/nv84.c +++ b/drivers/gpu/drm/nouveau/core/engine/bsp/nv84.c @@ -19,24 +19,19 @@ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * - * Authors: Ben Skeggs + * Authors: Ben Skeggs, Ilia Mirkin */ -#include -#include - +#include #include -struct nv84_bsp_priv { - struct nouveau_engine base; -}; - /******************************************************************************* * BSP object classes ******************************************************************************/ static struct nouveau_oclass nv84_bsp_sclass[] = { + { 0x74b0, &nouveau_object_ofuncs }, {}, }; @@ -48,7 +43,7 @@ static struct nouveau_oclass nv84_bsp_cclass = { .handle = NV_ENGCTX(BSP, 0x84), .ofuncs = &(struct nouveau_ofuncs) { - .ctor = _nouveau_engctx_ctor, + .ctor = _nouveau_xtensa_engctx_ctor, .dtor = _nouveau_engctx_dtor, .init = _nouveau_engctx_init, .fini = _nouveau_engctx_fini, @@ -66,10 +61,10 @@ nv84_bsp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, void *data, u32 size, struct nouveau_object **pobject) { - struct nv84_bsp_priv *priv; + struct nouveau_xtensa *priv; int ret; - ret = nouveau_engine_create(parent, engine, oclass, true, + ret = nouveau_xtensa_create(parent, engine, oclass, 0x103000, true, "PBSP", "bsp", &priv); *pobject = nv_object(priv); if (ret) @@ -78,6 +73,8 @@ nv84_bsp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, nv_subdev(priv)->unit = 0x04008000; nv_engine(priv)->cclass = &nv84_bsp_cclass; nv_engine(priv)->sclass = nv84_bsp_sclass; + priv->fifo_val = 0x1111; + priv->unkd28 = 0x90044; return 0; } @@ -86,8 +83,10 @@ nv84_bsp_oclass = { .handle = NV_ENGINE(BSP, 0x84), .ofuncs = &(struct nouveau_ofuncs) { .ctor = nv84_bsp_ctor, - .dtor = _nouveau_engine_dtor, - .init = _nouveau_engine_init, - .fini = _nouveau_engine_fini, + .dtor = _nouveau_xtensa_dtor, + .init = _nouveau_xtensa_init, + .fini = _nouveau_xtensa_fini, + .rd32 = _nouveau_xtensa_rd32, + .wr32 = _nouveau_xtensa_wr32, }, }; diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c index 65519dc75ba7..7f53196cff52 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c @@ -58,6 +58,7 @@ nv84_fifo_context_attach(struct nouveau_object *parent, case NVDEV_ENGINE_GR : addr = 0x0020; break; case NVDEV_ENGINE_VP : addr = 0x0040; break; case NVDEV_ENGINE_MPEG : addr = 0x0060; break; + case NVDEV_ENGINE_BSP : addr = 0x0080; break; case NVDEV_ENGINE_CRYPT: addr = 0x00a0; break; case NVDEV_ENGINE_COPY0: addr = 0x00c0; break; default: @@ -92,6 +93,7 @@ nv84_fifo_context_detach(struct nouveau_object *parent, bool suspend, case NVDEV_ENGINE_GR : engn = 0; addr = 0x0020; break; case NVDEV_ENGINE_VP : engn = 3; addr = 0x0040; break; case NVDEV_ENGINE_MPEG : engn = 1; addr = 0x0060; break; + case NVDEV_ENGINE_BSP : engn = 5; addr = 0x0080; break; case NVDEV_ENGINE_CRYPT: engn = 4; addr = 0x00a0; break; case NVDEV_ENGINE_COPY0: engn = 2; addr = 0x00c0; break; default: diff --git a/drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c index fcae49f678b5..07dd1fe2d6fb 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/vm/nv50.c @@ -175,6 +175,7 @@ nv50_vm_flush(struct nouveau_vm *vm) case NVDEV_ENGINE_VP : vme = 0x01; break; case NVDEV_SUBDEV_BAR : vme = 0x06; break; case NVDEV_ENGINE_MPEG : vme = 0x08; break; + case NVDEV_ENGINE_BSP : vme = 0x09; break; case NVDEV_ENGINE_CRYPT: vme = 0x0a; break; case NVDEV_ENGINE_COPY0: vme = 0x0d; break; default: -- cgit v1.2.3 From e99716f13d3a499f95a17e5442ef39270e4fc38b Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 5 Jun 2013 10:28:12 +1000 Subject: drm/gr/nvc0-: merge nvc0/nve0 ucode, and use cpp instead of m4 No code changes, proven by envyas producing identical binaries. Signed-off-by: Ben Skeggs --- .../gpu/drm/nouveau/core/engine/graph/fuc/com.fuc | 379 +++++++++++ .../gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc | 369 ++++++++++ .../drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc | 365 +--------- .../nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h | 4 +- .../drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc | 365 +--------- .../nouveau/core/engine/graph/fuc/gpcnve0.fuc.h | 4 +- .../gpu/drm/nouveau/core/engine/graph/fuc/hub.fuc | 755 +++++++++++++++++++++ .../drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc | 740 +------------------- .../nouveau/core/engine/graph/fuc/hubnvc0.fuc.h | 4 +- .../drm/nouveau/core/engine/graph/fuc/hubnve0.fuc | 714 +------------------ .../nouveau/core/engine/graph/fuc/hubnve0.fuc.h | 4 +- .../drm/nouveau/core/engine/graph/fuc/macros.fuc | 53 ++ .../gpu/drm/nouveau/core/engine/graph/fuc/nvc0.fuc | 400 ----------- .../gpu/drm/nouveau/core/engine/graph/fuc/nve0.fuc | 400 ----------- drivers/gpu/drm/nouveau/core/engine/graph/fuc/os.h | 7 + 15 files changed, 1623 insertions(+), 2940 deletions(-) create mode 100644 drivers/gpu/drm/nouveau/core/engine/graph/fuc/com.fuc create mode 100644 drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc create mode 100644 drivers/gpu/drm/nouveau/core/engine/graph/fuc/hub.fuc create mode 100644 drivers/gpu/drm/nouveau/core/engine/graph/fuc/macros.fuc delete mode 100644 drivers/gpu/drm/nouveau/core/engine/graph/fuc/nvc0.fuc delete mode 100644 drivers/gpu/drm/nouveau/core/engine/graph/fuc/nve0.fuc create mode 100644 drivers/gpu/drm/nouveau/core/engine/graph/fuc/os.h diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/com.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/com.fuc new file mode 100644 index 000000000000..da18885c559c --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/com.fuc @@ -0,0 +1,379 @@ +/* fuc microcode util functions for nvc0 PGRAPH + * + * Copyright 2011 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 + */ + +#ifdef INCLUDE_CODE +// queue_put - add request to queue +// +// In : $r13 queue pointer +// $r14 command +// $r15 data +// +queue_put: + // make sure we have space.. + ld b32 $r8 D[$r13 + 0x0] // GET + ld b32 $r9 D[$r13 + 0x4] // PUT + xor $r8 8 + cmpu b32 $r8 $r9 + bra ne #queue_put_next + mov $r15 E_CMD_OVERFLOW + call #error + ret + + // store cmd/data on queue + queue_put_next: + and $r8 $r9 7 + shl b32 $r8 3 + add b32 $r8 $r13 + add b32 $r8 8 + st b32 D[$r8 + 0x0] $r14 + st b32 D[$r8 + 0x4] $r15 + + // update PUT + add b32 $r9 1 + and $r9 0xf + st b32 D[$r13 + 0x4] $r9 + ret + +// queue_get - fetch request from queue +// +// In : $r13 queue pointer +// +// Out: $p1 clear on success (data available) +// $r14 command +// $r15 data +// +queue_get: + bset $flags $p1 + ld b32 $r8 D[$r13 + 0x0] // GET + ld b32 $r9 D[$r13 + 0x4] // PUT + cmpu b32 $r8 $r9 + bra e #queue_get_done + // fetch first cmd/data pair + and $r9 $r8 7 + shl b32 $r9 3 + add b32 $r9 $r13 + add b32 $r9 8 + ld b32 $r14 D[$r9 + 0x0] + ld b32 $r15 D[$r9 + 0x4] + + // update GET + add b32 $r8 1 + and $r8 0xf + st b32 D[$r13 + 0x0] $r8 + bclr $flags $p1 +queue_get_done: + ret + +// nv_rd32 - read 32-bit value from nv register +// +// In : $r14 register +// Out: $r15 value +// +nv_rd32: + mov $r11 0x728 + shl b32 $r11 6 + mov b32 $r12 $r14 + bset $r12 31 // MMIO_CTRL_PENDING + iowr I[$r11 + 0x000] $r12 // MMIO_CTRL + nv_rd32_wait: + iord $r12 I[$r11 + 0x000] + xbit $r12 $r12 31 + bra ne #nv_rd32_wait + mov $r10 6 // DONE_MMIO_RD + call #wait_doneo + iord $r15 I[$r11 + 0x100] // MMIO_RDVAL + ret + +// nv_wr32 - write 32-bit value to nv register +// +// In : $r14 register +// $r15 value +// +nv_wr32: + mov $r11 0x728 + shl b32 $r11 6 + iowr I[$r11 + 0x200] $r15 // MMIO_WRVAL + mov b32 $r12 $r14 + bset $r12 31 // MMIO_CTRL_PENDING + bset $r12 30 // MMIO_CTRL_WRITE + iowr I[$r11 + 0x000] $r12 // MMIO_CTRL + nv_wr32_wait: + iord $r12 I[$r11 + 0x000] + xbit $r12 $r12 31 + bra ne #nv_wr32_wait + ret + +// (re)set watchdog timer +// +// In : $r15 timeout +// +watchdog_reset: + mov $r8 0x430 + shl b32 $r8 6 + bset $r15 31 + iowr I[$r8 + 0x000] $r15 + ret + +// clear watchdog timer +watchdog_clear: + mov $r8 0x430 + shl b32 $r8 6 + iowr I[$r8 + 0x000] $r0 + ret + +// wait_donez - wait on FUC_DONE bit to become clear +// +// In : $r10 bit to wait on +// +wait_donez: + trace_set(T_WAIT); + mov $r8 0x818 + shl b32 $r8 6 + iowr I[$r8 + 0x000] $r10 + wait_donez_ne: + mov $r8 0x400 + shl b32 $r8 6 + iord $r8 I[$r8 + 0x000] + xbit $r8 $r8 $r10 + bra ne #wait_donez_ne + trace_clr(T_WAIT) + ret + +// wait_doneo - wait on FUC_DONE bit to become set +// +// In : $r10 bit to wait on +// +wait_doneo: + trace_set(T_WAIT); + mov $r8 0x818 + shl b32 $r8 6 + iowr I[$r8 + 0x000] $r10 + wait_doneo_e: + mov $r8 0x400 + shl b32 $r8 6 + iord $r8 I[$r8 + 0x000] + xbit $r8 $r8 $r10 + bra e #wait_doneo_e + trace_clr(T_WAIT) + ret + +// mmctx_size - determine size of a mmio list transfer +// +// In : $r14 mmio list head +// $r15 mmio list tail +// Out: $r15 transfer size (in bytes) +// +mmctx_size: + clear b32 $r9 + nv_mmctx_size_loop: + ld b32 $r8 D[$r14] + shr b32 $r8 26 + add b32 $r8 1 + shl b32 $r8 2 + add b32 $r9 $r8 + add b32 $r14 4 + cmpu b32 $r14 $r15 + bra ne #nv_mmctx_size_loop + mov b32 $r15 $r9 + ret + +// mmctx_xfer - execute a list of mmio transfers +// +// In : $r10 flags +// bit 0: direction (0 = save, 1 = load) +// bit 1: set if first transfer +// bit 2: set if last transfer +// $r11 base +// $r12 mmio list head +// $r13 mmio list tail +// $r14 multi_stride +// $r15 multi_mask +// +mmctx_xfer: + trace_set(T_MMCTX) + mov $r8 0x710 + shl b32 $r8 6 + clear b32 $r9 + or $r11 $r11 + bra e #mmctx_base_disabled + iowr I[$r8 + 0x000] $r11 // MMCTX_BASE + bset $r9 0 // BASE_EN + mmctx_base_disabled: + or $r14 $r14 + bra e #mmctx_multi_disabled + iowr I[$r8 + 0x200] $r14 // MMCTX_MULTI_STRIDE + iowr I[$r8 + 0x300] $r15 // MMCTX_MULTI_MASK + bset $r9 1 // MULTI_EN + mmctx_multi_disabled: + add b32 $r8 0x100 + + xbit $r11 $r10 0 + shl b32 $r11 16 // DIR + bset $r11 12 // QLIMIT = 0x10 + xbit $r14 $r10 1 + shl b32 $r14 17 + or $r11 $r14 // START_TRIGGER + iowr I[$r8 + 0x000] $r11 // MMCTX_CTRL + + // loop over the mmio list, and send requests to the hw + mmctx_exec_loop: + // wait for space in mmctx queue + mmctx_wait_free: + iord $r14 I[$r8 + 0x000] // MMCTX_CTRL + and $r14 0x1f + bra e #mmctx_wait_free + + // queue up an entry + ld b32 $r14 D[$r12] + or $r14 $r9 + iowr I[$r8 + 0x300] $r14 + add b32 $r12 4 + cmpu b32 $r12 $r13 + bra ne #mmctx_exec_loop + + xbit $r11 $r10 2 + bra ne #mmctx_stop + // wait for queue to empty + mmctx_fini_wait: + iord $r11 I[$r8 + 0x000] // MMCTX_CTRL + and $r11 0x1f + cmpu b32 $r11 0x10 + bra ne #mmctx_fini_wait + mov $r10 2 // DONE_MMCTX + call #wait_donez + bra #mmctx_done + mmctx_stop: + xbit $r11 $r10 0 + shl b32 $r11 16 // DIR + bset $r11 12 // QLIMIT = 0x10 + bset $r11 18 // STOP_TRIGGER + iowr I[$r8 + 0x000] $r11 // MMCTX_CTRL + mmctx_stop_wait: + // wait for STOP_TRIGGER to clear + iord $r11 I[$r8 + 0x000] // MMCTX_CTRL + xbit $r11 $r11 18 + bra ne #mmctx_stop_wait + mmctx_done: + trace_clr(T_MMCTX) + ret + +// Wait for DONE_STRAND +// +strand_wait: + push $r10 + mov $r10 2 + call #wait_donez + pop $r10 + ret + +// unknown - call before issuing strand commands +// +strand_pre: + mov $r8 0x4afc + sethi $r8 0x20000 + mov $r9 0xc + iowr I[$r8] $r9 + call #strand_wait + ret + +// unknown - call after issuing strand commands +// +strand_post: + mov $r8 0x4afc + sethi $r8 0x20000 + mov $r9 0xd + iowr I[$r8] $r9 + call #strand_wait + ret + +// Selects strand set?! +// +// In: $r14 id +// +strand_set: + mov $r10 0x4ffc + sethi $r10 0x20000 + sub b32 $r11 $r10 0x500 + mov $r12 0xf + iowr I[$r10 + 0x000] $r12 // 0x93c = 0xf + mov $r12 0xb + iowr I[$r11 + 0x000] $r12 // 0x928 = 0xb + call #strand_wait + iowr I[$r10 + 0x000] $r14 // 0x93c = + mov $r12 0xa + iowr I[$r11 + 0x000] $r12 // 0x928 = 0xa + call #strand_wait + ret + +// Initialise strand context data +// +// In : $r15 context base +// Out: $r15 context size (in bytes) +// +// Strandset(?) 3 hardcoded currently +// +strand_ctx_init: + trace_set(T_STRINIT) + call #strand_pre + mov $r14 3 + call #strand_set + mov $r10 0x46fc + sethi $r10 0x20000 + add b32 $r11 $r10 0x400 + iowr I[$r10 + 0x100] $r0 // STRAND_FIRST_GENE = 0 + mov $r12 1 + iowr I[$r11 + 0x000] $r12 // STRAND_CMD = LATCH_FIRST_GENE + call #strand_wait + sub b32 $r12 $r0 1 + iowr I[$r10 + 0x000] $r12 // STRAND_GENE_CNT = 0xffffffff + mov $r12 2 + iowr I[$r11 + 0x000] $r12 // STRAND_CMD = LATCH_GENE_CNT + call #strand_wait + call #strand_post + + // read the size of each strand, poke the context offset of + // each into STRAND_{SAVE,LOAD}_SWBASE now, no need to worry + // about it later then. + mov $r8 0x880 + shl b32 $r8 6 + iord $r9 I[$r8 + 0x000] // STRANDS + add b32 $r8 0x2200 + shr b32 $r14 $r15 8 + ctx_init_strand_loop: + iowr I[$r8 + 0x000] $r14 // STRAND_SAVE_SWBASE + iowr I[$r8 + 0x100] $r14 // STRAND_LOAD_SWBASE + iord $r10 I[$r8 + 0x200] // STRAND_SIZE + shr b32 $r10 6 + add b32 $r10 1 + add b32 $r14 $r10 + add b32 $r8 4 + sub b32 $r9 1 + bra ne #ctx_init_strand_loop + + shl b32 $r14 8 + sub b32 $r15 $r14 $r15 + trace_clr(T_STRINIT) + ret +#endif diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc new file mode 100644 index 000000000000..4770e8c99432 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc @@ -0,0 +1,369 @@ +/* fuc microcode for nvc0 PGRAPH/GPC + * + * Copyright 2011 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 + */ + +/* TODO + * - bracket certain functions with scratch writes, useful for debugging + * - watchdog timer around ctx operations + */ + +#ifdef INCLUDE_DATA +gpc_id: .b32 0 +gpc_mmio_list_head: .b32 0 +gpc_mmio_list_tail: .b32 0 + +tpc_count: .b32 0 +tpc_mask: .b32 0 +tpc_mmio_list_head: .b32 0 +tpc_mmio_list_tail: .b32 0 + +cmd_queue: queue_init +#endif + +#ifdef INCLUDE_CODE +// reports an exception to the host +// +// In: $r15 error code (see nvc0.fuc) +// +error: + push $r14 + mov $r14 -0x67ec // 0x9814 + sethi $r14 0x400000 + call #nv_wr32 // HUB_CTXCTL_CC_SCRATCH[5] = error code + add b32 $r14 0x41c + mov $r15 1 + call #nv_wr32 // HUB_CTXCTL_INTR_UP_SET + pop $r14 + ret + +// GPC fuc initialisation, executed by triggering ucode start, will +// fall through to main loop after completion. +// +// Input: +// CC_SCRATCH[0]: chipset (PMC_BOOT_0 read returns 0x0bad0bad... sigh) +// CC_SCRATCH[1]: context base +// +// Output: +// CC_SCRATCH[0]: +// 31:31: set to signal completion +// CC_SCRATCH[1]: +// 31:0: GPC context size +// +init: + clear b32 $r0 + mov $sp $r0 + + // enable fifo access + mov $r1 0x1200 + mov $r2 2 + iowr I[$r1 + 0x000] $r2 // FIFO_ENABLE + + // setup i0 handler, and route all interrupts to it + mov $r1 #ih + mov $iv0 $r1 + mov $r1 0x400 + iowr I[$r1 + 0x300] $r0 // INTR_DISPATCH + + // enable fifo interrupt + mov $r2 4 + iowr I[$r1 + 0x000] $r2 // INTR_EN_SET + + // enable interrupts + bset $flags ie0 + + // figure out which GPC we are, and how many TPCs we have + mov $r1 0x608 + shl b32 $r1 6 + iord $r2 I[$r1 + 0x000] // UNITS + mov $r3 1 + and $r2 0x1f + shl b32 $r3 $r2 + sub b32 $r3 1 + st b32 D[$r0 + #tpc_count] $r2 + st b32 D[$r0 + #tpc_mask] $r3 + add b32 $r1 0x400 + iord $r2 I[$r1 + 0x000] // MYINDEX + st b32 D[$r0 + #gpc_id] $r2 + + // find context data for this chipset + mov $r2 0x800 + shl b32 $r2 6 + iord $r2 I[$r2 + 0x000] // CC_SCRATCH[0] + mov $r1 #chipsets - 12 + init_find_chipset: + add b32 $r1 12 + ld b32 $r3 D[$r1 + 0x00] + cmpu b32 $r3 $r2 + bra e #init_context + cmpu b32 $r3 0 + bra ne #init_find_chipset + // unknown chipset + ret + + // initialise context base, and size tracking + init_context: + mov $r2 0x800 + shl b32 $r2 6 + iord $r2 I[$r2 + 0x100] // CC_SCRATCH[1], initial base + clear b32 $r3 // track GPC context size here + + // set mmctx base addresses now so we don't have to do it later, + // they don't currently ever change + mov $r4 0x700 + shl b32 $r4 6 + shr b32 $r5 $r2 8 + iowr I[$r4 + 0x000] $r5 // MMCTX_SAVE_SWBASE + iowr I[$r4 + 0x100] $r5 // MMCTX_LOAD_SWBASE + + // calculate GPC mmio context size, store the chipset-specific + // mmio list pointers somewhere we can get at them later without + // re-parsing the chipset list + clear b32 $r14 + clear b32 $r15 + ld b16 $r14 D[$r1 + 4] + ld b16 $r15 D[$r1 + 6] + st b16 D[$r0 + #gpc_mmio_list_head] $r14 + st b16 D[$r0 + #gpc_mmio_list_tail] $r15 + call #mmctx_size + add b32 $r2 $r15 + add b32 $r3 $r15 + + // calculate per-TPC mmio context size, store the list pointers + ld b16 $r14 D[$r1 + 8] + ld b16 $r15 D[$r1 + 10] + st b16 D[$r0 + #tpc_mmio_list_head] $r14 + st b16 D[$r0 + #tpc_mmio_list_tail] $r15 + call #mmctx_size + ld b32 $r14 D[$r0 + #tpc_count] + mulu $r14 $r15 + add b32 $r2 $r14 + add b32 $r3 $r14 + + // round up base/size to 256 byte boundary (for strand SWBASE) + add b32 $r4 0x1300 + shr b32 $r3 2 + iowr I[$r4 + 0x000] $r3 // MMCTX_LOAD_COUNT, wtf for?!? + shr b32 $r2 8 + shr b32 $r3 6 + add b32 $r2 1 + add b32 $r3 1 + shl b32 $r2 8 + shl b32 $r3 8 + + // calculate size of strand context data + mov b32 $r15 $r2 + call #strand_ctx_init + add b32 $r3 $r15 + + // save context size, and tell HUB we're done + mov $r1 0x800 + shl b32 $r1 6 + iowr I[$r1 + 0x100] $r3 // CC_SCRATCH[1] = context size + add b32 $r1 0x800 + clear b32 $r2 + bset $r2 31 + iowr I[$r1 + 0x000] $r2 // CC_SCRATCH[0] |= 0x80000000 + +// Main program loop, very simple, sleeps until woken up by the interrupt +// handler, pulls a command from the queue and executes its handler +// +main: + bset $flags $p0 + sleep $p0 + mov $r13 #cmd_queue + call #queue_get + bra $p1 #main + + // 0x0000-0x0003 are all context transfers + cmpu b32 $r14 0x04 + bra nc #main_not_ctx_xfer + // fetch $flags and mask off $p1/$p2 + mov $r1 $flags + mov $r2 0x0006 + not b32 $r2 + and $r1 $r2 + // set $p1/$p2 according to transfer type + shl b32 $r14 1 + or $r1 $r14 + mov $flags $r1 + // transfer context data + call #ctx_xfer + bra #main + + main_not_ctx_xfer: + shl b32 $r15 $r14 16 + or $r15 E_BAD_COMMAND + call #error + bra #main + +// interrupt handler +ih: + push $r8 + mov $r8 $flags + push $r8 + push $r9 + push $r10 + push $r11 + push $r13 + push $r14 + push $r15 + + // incoming fifo command? + iord $r10 I[$r0 + 0x200] // INTR + and $r11 $r10 0x00000004 + bra e #ih_no_fifo + // queue incoming fifo command for later processing + mov $r11 0x1900 + mov $r13 #cmd_queue + iord $r14 I[$r11 + 0x100] // FIFO_CMD + iord $r15 I[$r11 + 0x000] // FIFO_DATA + call #queue_put + add b32 $r11 0x400 + mov $r14 1 + iowr I[$r11 + 0x000] $r14 // FIFO_ACK + + // ack, and wake up main() + ih_no_fifo: + iowr I[$r0 + 0x100] $r10 // INTR_ACK + + pop $r15 + pop $r14 + pop $r13 + pop $r11 + pop $r10 + pop $r9 + pop $r8 + mov $flags $r8 + pop $r8 + bclr $flags $p0 + iret + +// Set this GPC's bit in HUB_BAR, used to signal completion of various +// activities to the HUB fuc +// +hub_barrier_done: + mov $r15 1 + ld b32 $r14 D[$r0 + #gpc_id] + shl b32 $r15 $r14 + mov $r14 -0x6be8 // 0x409418 - HUB_BAR_SET + sethi $r14 0x400000 + call #nv_wr32 + ret + +// Disables various things, waits a bit, and re-enables them.. +// +// Not sure how exactly this helps, perhaps "ENABLE" is not such a +// good description for the bits we turn off? Anyways, without this, +// funny things happen. +// +ctx_redswitch: + mov $r14 0x614 + shl b32 $r14 6 + mov $r15 0x020 + iowr I[$r14] $r15 // GPC_RED_SWITCH = POWER + mov $r15 8 + ctx_redswitch_delay: + sub b32 $r15 1 + bra ne #ctx_redswitch_delay + mov $r15 0xa20 + iowr I[$r14] $r15 // GPC_RED_SWITCH = UNK11, ENABLE, POWER + ret + +// Transfer GPC context data between GPU and storage area +// +// In: $r15 context base address +// $p1 clear on save, set on load +// $p2 set if opposite direction done/will be done, so: +// on save it means: "a load will follow this save" +// on load it means: "a save preceeded this load" +// +ctx_xfer: + // set context base address + mov $r1 0xa04 + shl b32 $r1 6 + iowr I[$r1 + 0x000] $r15// MEM_BASE + bra not $p1 #ctx_xfer_not_load + call #ctx_redswitch + ctx_xfer_not_load: + + // strands + mov $r1 0x4afc + sethi $r1 0x20000 + mov $r2 0xc + iowr I[$r1] $r2 // STRAND_CMD(0x3f) = 0x0c + call #strand_wait + mov $r2 0x47fc + sethi $r2 0x20000 + iowr I[$r2] $r0 // STRAND_FIRST_GENE(0x3f) = 0x00 + xbit $r2 $flags $p1 + add b32 $r2 3 + iowr I[$r1] $r2 // STRAND_CMD(0x3f) = 0x03/0x04 (SAVE/LOAD) + + // mmio context + xbit $r10 $flags $p1 // direction + or $r10 2 // first + mov $r11 0x0000 + sethi $r11 0x500000 + ld b32 $r12 D[$r0 + #gpc_id] + shl b32 $r12 15 + add b32 $r11 $r12 // base = NV_PGRAPH_GPCn + ld b32 $r12 D[$r0 + #gpc_mmio_list_head] + ld b32 $r13 D[$r0 + #gpc_mmio_list_tail] + mov $r14 0 // not multi + call #mmctx_xfer + + // per-TPC mmio context + xbit $r10 $flags $p1 // direction + or $r10 4 // last + mov $r11 0x4000 + sethi $r11 0x500000 // base = NV_PGRAPH_GPC0_TPC0 + ld b32 $r12 D[$r0 + #gpc_id] + shl b32 $r12 15 + add b32 $r11 $r12 // base = NV_PGRAPH_GPCn_TPC0 + ld b32 $r12 D[$r0 + #tpc_mmio_list_head] + ld b32 $r13 D[$r0 + #tpc_mmio_list_tail] + ld b32 $r15 D[$r0 + #tpc_mask] + mov $r14 0x800 // stride = 0x800 + call #mmctx_xfer + + // wait for strands to finish + call #strand_wait + + // if load, or a save without a load following, do some + // unknown stuff that's done after finishing a block of + // strand commands + bra $p1 #ctx_xfer_post + bra not $p2 #ctx_xfer_done + ctx_xfer_post: + mov $r1 0x4afc + sethi $r1 0x20000 + mov $r2 0xd + iowr I[$r1] $r2 // STRAND_CMD(0x3f) = 0x0d + call #strand_wait + + // mark completion in HUB's barrier + ctx_xfer_done: + call #hub_barrier_done + ret +#endif diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc index 61a6b43ece19..c2d9e59bb58f 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc @@ -1,6 +1,5 @@ -/* fuc microcode for nvc0 PGRAPH/GPC - * - * Copyright 2011 Red Hat Inc. +/* + * Copyright 2013 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"), @@ -20,32 +19,17 @@ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * - * Authors: Ben Skeggs + * Authors: Ben Skeggs */ -/* To build: - * m4 gpcnvc0.fuc | envyas -a -w -m fuc -V fuc3 -o gpcnvc0.fuc.h - */ - -/* TODO - * - bracket certain functions with scratch writes, useful for debugging - * - watchdog timer around ctx operations - */ +#define NVGF +#include "macros.fuc" .section #nvc0_grgpc_data -include(`nvc0.fuc') -gpc_id: .b32 0 -gpc_mmio_list_head: .b32 0 -gpc_mmio_list_tail: .b32 0 - -tpc_count: .b32 0 -tpc_mask: .b32 0 -tpc_mmio_list_head: .b32 0 -tpc_mmio_list_tail: .b32 0 +#define INCLUDE_DATA +#include "com.fuc" +#include "gpc.fuc" -cmd_queue: queue_init - -// chipset descriptions chipsets: .b8 0xc0 0 0 0 .b16 #nvc0_gpc_mmio_head @@ -159,335 +143,12 @@ nvc1_tpc_mmio_tail: mmctx_data(0x000424, 2); mmctx_data(0x0006e0, 1); nvd9_tpc_mmio_tail: +#undef INCLUDE_DATA .section #nvc0_grgpc_code +#define INCLUDE_CODE bra #init -define(`include_code') -include(`nvc0.fuc') - -// reports an exception to the host -// -// In: $r15 error code (see nvc0.fuc) -// -error: - push $r14 - mov $r14 -0x67ec // 0x9814 - sethi $r14 0x400000 - call #nv_wr32 // HUB_CTXCTL_CC_SCRATCH[5] = error code - add b32 $r14 0x41c - mov $r15 1 - call #nv_wr32 // HUB_CTXCTL_INTR_UP_SET - pop $r14 - ret - -// GPC fuc initialisation, executed by triggering ucode start, will -// fall through to main loop after completion. -// -// Input: -// CC_SCRATCH[0]: chipset (PMC_BOOT_0 read returns 0x0bad0bad... sigh) -// CC_SCRATCH[1]: context base -// -// Output: -// CC_SCRATCH[0]: -// 31:31: set to signal completion -// CC_SCRATCH[1]: -// 31:0: GPC context size -// -init: - clear b32 $r0 - mov $sp $r0 - - // enable fifo access - mov $r1 0x1200 - mov $r2 2 - iowr I[$r1 + 0x000] $r2 // FIFO_ENABLE - - // setup i0 handler, and route all interrupts to it - mov $r1 #ih - mov $iv0 $r1 - mov $r1 0x400 - iowr I[$r1 + 0x300] $r0 // INTR_DISPATCH - - // enable fifo interrupt - mov $r2 4 - iowr I[$r1 + 0x000] $r2 // INTR_EN_SET - - // enable interrupts - bset $flags ie0 - - // figure out which GPC we are, and how many TPCs we have - mov $r1 0x608 - shl b32 $r1 6 - iord $r2 I[$r1 + 0x000] // UNITS - mov $r3 1 - and $r2 0x1f - shl b32 $r3 $r2 - sub b32 $r3 1 - st b32 D[$r0 + #tpc_count] $r2 - st b32 D[$r0 + #tpc_mask] $r3 - add b32 $r1 0x400 - iord $r2 I[$r1 + 0x000] // MYINDEX - st b32 D[$r0 + #gpc_id] $r2 - - // find context data for this chipset - mov $r2 0x800 - shl b32 $r2 6 - iord $r2 I[$r2 + 0x000] // CC_SCRATCH[0] - mov $r1 #chipsets - 12 - init_find_chipset: - add b32 $r1 12 - ld b32 $r3 D[$r1 + 0x00] - cmpu b32 $r3 $r2 - bra e #init_context - cmpu b32 $r3 0 - bra ne #init_find_chipset - // unknown chipset - ret - - // initialise context base, and size tracking - init_context: - mov $r2 0x800 - shl b32 $r2 6 - iord $r2 I[$r2 + 0x100] // CC_SCRATCH[1], initial base - clear b32 $r3 // track GPC context size here - - // set mmctx base addresses now so we don't have to do it later, - // they don't currently ever change - mov $r4 0x700 - shl b32 $r4 6 - shr b32 $r5 $r2 8 - iowr I[$r4 + 0x000] $r5 // MMCTX_SAVE_SWBASE - iowr I[$r4 + 0x100] $r5 // MMCTX_LOAD_SWBASE - - // calculate GPC mmio context size, store the chipset-specific - // mmio list pointers somewhere we can get at them later without - // re-parsing the chipset list - clear b32 $r14 - clear b32 $r15 - ld b16 $r14 D[$r1 + 4] - ld b16 $r15 D[$r1 + 6] - st b16 D[$r0 + #gpc_mmio_list_head] $r14 - st b16 D[$r0 + #gpc_mmio_list_tail] $r15 - call #mmctx_size - add b32 $r2 $r15 - add b32 $r3 $r15 - - // calculate per-TPC mmio context size, store the list pointers - ld b16 $r14 D[$r1 + 8] - ld b16 $r15 D[$r1 + 10] - st b16 D[$r0 + #tpc_mmio_list_head] $r14 - st b16 D[$r0 + #tpc_mmio_list_tail] $r15 - call #mmctx_size - ld b32 $r14 D[$r0 + #tpc_count] - mulu $r14 $r15 - add b32 $r2 $r14 - add b32 $r3 $r14 - - // round up base/size to 256 byte boundary (for strand SWBASE) - add b32 $r4 0x1300 - shr b32 $r3 2 - iowr I[$r4 + 0x000] $r3 // MMCTX_LOAD_COUNT, wtf for?!? - shr b32 $r2 8 - shr b32 $r3 6 - add b32 $r2 1 - add b32 $r3 1 - shl b32 $r2 8 - shl b32 $r3 8 - - // calculate size of strand context data - mov b32 $r15 $r2 - call #strand_ctx_init - add b32 $r3 $r15 - - // save context size, and tell HUB we're done - mov $r1 0x800 - shl b32 $r1 6 - iowr I[$r1 + 0x100] $r3 // CC_SCRATCH[1] = context size - add b32 $r1 0x800 - clear b32 $r2 - bset $r2 31 - iowr I[$r1 + 0x000] $r2 // CC_SCRATCH[0] |= 0x80000000 - -// Main program loop, very simple, sleeps until woken up by the interrupt -// handler, pulls a command from the queue and executes its handler -// -main: - bset $flags $p0 - sleep $p0 - mov $r13 #cmd_queue - call #queue_get - bra $p1 #main - - // 0x0000-0x0003 are all context transfers - cmpu b32 $r14 0x04 - bra nc #main_not_ctx_xfer - // fetch $flags and mask off $p1/$p2 - mov $r1 $flags - mov $r2 0x0006 - not b32 $r2 - and $r1 $r2 - // set $p1/$p2 according to transfer type - shl b32 $r14 1 - or $r1 $r14 - mov $flags $r1 - // transfer context data - call #ctx_xfer - bra #main - - main_not_ctx_xfer: - shl b32 $r15 $r14 16 - or $r15 E_BAD_COMMAND - call #error - bra #main - -// interrupt handler -ih: - push $r8 - mov $r8 $flags - push $r8 - push $r9 - push $r10 - push $r11 - push $r13 - push $r14 - push $r15 - - // incoming fifo command? - iord $r10 I[$r0 + 0x200] // INTR - and $r11 $r10 0x00000004 - bra e #ih_no_fifo - // queue incoming fifo command for later processing - mov $r11 0x1900 - mov $r13 #cmd_queue - iord $r14 I[$r11 + 0x100] // FIFO_CMD - iord $r15 I[$r11 + 0x000] // FIFO_DATA - call #queue_put - add b32 $r11 0x400 - mov $r14 1 - iowr I[$r11 + 0x000] $r14 // FIFO_ACK - - // ack, and wake up main() - ih_no_fifo: - iowr I[$r0 + 0x100] $r10 // INTR_ACK - - pop $r15 - pop $r14 - pop $r13 - pop $r11 - pop $r10 - pop $r9 - pop $r8 - mov $flags $r8 - pop $r8 - bclr $flags $p0 - iret - -// Set this GPC's bit in HUB_BAR, used to signal completion of various -// activities to the HUB fuc -// -hub_barrier_done: - mov $r15 1 - ld b32 $r14 D[$r0 + #gpc_id] - shl b32 $r15 $r14 - mov $r14 -0x6be8 // 0x409418 - HUB_BAR_SET - sethi $r14 0x400000 - call #nv_wr32 - ret - -// Disables various things, waits a bit, and re-enables them.. -// -// Not sure how exactly this helps, perhaps "ENABLE" is not such a -// good description for the bits we turn off? Anyways, without this, -// funny things happen. -// -ctx_redswitch: - mov $r14 0x614 - shl b32 $r14 6 - mov $r15 0x020 - iowr I[$r14] $r15 // GPC_RED_SWITCH = POWER - mov $r15 8 - ctx_redswitch_delay: - sub b32 $r15 1 - bra ne #ctx_redswitch_delay - mov $r15 0xa20 - iowr I[$r14] $r15 // GPC_RED_SWITCH = UNK11, ENABLE, POWER - ret - -// Transfer GPC context data between GPU and storage area -// -// In: $r15 context base address -// $p1 clear on save, set on load -// $p2 set if opposite direction done/will be done, so: -// on save it means: "a load will follow this save" -// on load it means: "a save preceeded this load" -// -ctx_xfer: - // set context base address - mov $r1 0xa04 - shl b32 $r1 6 - iowr I[$r1 + 0x000] $r15// MEM_BASE - bra not $p1 #ctx_xfer_not_load - call #ctx_redswitch - ctx_xfer_not_load: - - // strands - mov $r1 0x4afc - sethi $r1 0x20000 - mov $r2 0xc - iowr I[$r1] $r2 // STRAND_CMD(0x3f) = 0x0c - call #strand_wait - mov $r2 0x47fc - sethi $r2 0x20000 - iowr I[$r2] $r0 // STRAND_FIRST_GENE(0x3f) = 0x00 - xbit $r2 $flags $p1 - add b32 $r2 3 - iowr I[$r1] $r2 // STRAND_CMD(0x3f) = 0x03/0x04 (SAVE/LOAD) - - // mmio context - xbit $r10 $flags $p1 // direction - or $r10 2 // first - mov $r11 0x0000 - sethi $r11 0x500000 - ld b32 $r12 D[$r0 + #gpc_id] - shl b32 $r12 15 - add b32 $r11 $r12 // base = NV_PGRAPH_GPCn - ld b32 $r12 D[$r0 + #gpc_mmio_list_head] - ld b32 $r13 D[$r0 + #gpc_mmio_list_tail] - mov $r14 0 // not multi - call #mmctx_xfer - - // per-TPC mmio context - xbit $r10 $flags $p1 // direction - or $r10 4 // last - mov $r11 0x4000 - sethi $r11 0x500000 // base = NV_PGRAPH_GPC0_TPC0 - ld b32 $r12 D[$r0 + #gpc_id] - shl b32 $r12 15 - add b32 $r11 $r12 // base = NV_PGRAPH_GPCn_TPC0 - ld b32 $r12 D[$r0 + #tpc_mmio_list_head] - ld b32 $r13 D[$r0 + #tpc_mmio_list_tail] - ld b32 $r15 D[$r0 + #tpc_mask] - mov $r14 0x800 // stride = 0x800 - call #mmctx_xfer - - // wait for strands to finish - call #strand_wait - - // if load, or a save without a load following, do some - // unknown stuff that's done after finishing a block of - // strand commands - bra $p1 #ctx_xfer_post - bra not $p2 #ctx_xfer_done - ctx_xfer_post: - mov $r1 0x4afc - sethi $r1 0x20000 - mov $r2 0xd - iowr I[$r1] $r2 // STRAND_CMD(0x3f) = 0x0d - call #strand_wait - - // mark completion in HUB's barrier - ctx_xfer_done: - call #hub_barrier_done - ret - +#include "com.fuc" +#include "gpc.fuc" .align 256 +#undef INCLUDE_CODE diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h index cafcc638042a..66ec1acaadee 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h @@ -192,7 +192,7 @@ uint32_t nvc0_grgpc_code[] = { 0x0089d000, 0x081887f1, 0xd00684b6, -/* 0x00e2: wait_done_wait_donez */ +/* 0x00e2: wait_donez_ne */ 0x87f1008a, 0x84b60400, 0x0088cf06, @@ -209,7 +209,7 @@ uint32_t nvc0_grgpc_code[] = { 0x87f10089, 0x84b60818, 0x008ad006, -/* 0x011c: wait_done_wait_doneo */ +/* 0x011c: wait_doneo_e */ 0x040087f1, 0xcf0684b6, 0x8aff0088, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc index ccaeb50aa76b..2fc585eeff95 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc @@ -1,6 +1,5 @@ -/* fuc microcode for nve0 PGRAPH/GPC - * - * Copyright 2011 Red Hat Inc. +/* + * Copyright 2013 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"), @@ -20,32 +19,17 @@ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * - * Authors: Ben Skeggs + * Authors: Ben Skeggs */ -/* To build: - * m4 nve0_grgpc.fuc | envyas -a -w -m fuc -V nva3 -o nve0_grgpc.fuc.h - */ - -/* TODO - * - bracket certain functions with scratch writes, useful for debugging - * - watchdog timer around ctx operations - */ +#define NVGK +#include "macros.fuc" .section #nve0_grgpc_data -include(`nve0.fuc') -gpc_id: .b32 0 -gpc_mmio_list_head: .b32 0 -gpc_mmio_list_tail: .b32 0 - -tpc_count: .b32 0 -tpc_mask: .b32 0 -tpc_mmio_list_head: .b32 0 -tpc_mmio_list_tail: .b32 0 +#define INCLUDE_DATA +#include "com.fuc" +#include "gpc.fuc" -cmd_queue: queue_init - -// chipset descriptions chipsets: .b8 0xe4 0 0 0 .b16 #nve4_gpc_mmio_head @@ -182,335 +166,12 @@ mmctx_data(0x000758, 1) mmctx_data(0x000770, 1) mmctx_data(0x000778, 2) nvf0_tpc_mmio_tail: +#undef INCLUDE_DATA .section #nve0_grgpc_code +#define INCLUDE_CODE bra #init -define(`include_code') -include(`nve0.fuc') - -// reports an exception to the host -// -// In: $r15 error code (see nve0.fuc) -// -error: - push $r14 - mov $r14 -0x67ec // 0x9814 - sethi $r14 0x400000 - call #nv_wr32 // HUB_CTXCTL_CC_SCRATCH[5] = error code - add b32 $r14 0x41c - mov $r15 1 - call #nv_wr32 // HUB_CTXCTL_INTR_UP_SET - pop $r14 - ret - -// GPC fuc initialisation, executed by triggering ucode start, will -// fall through to main loop after completion. -// -// Input: -// CC_SCRATCH[0]: chipset (PMC_BOOT_0 read returns 0x0bad0bad... sigh) -// CC_SCRATCH[1]: context base -// -// Output: -// CC_SCRATCH[0]: -// 31:31: set to signal completion -// CC_SCRATCH[1]: -// 31:0: GPC context size -// -init: - clear b32 $r0 - mov $sp $r0 - - // enable fifo access - mov $r1 0x1200 - mov $r2 2 - iowr I[$r1 + 0x000] $r2 // FIFO_ENABLE - - // setup i0 handler, and route all interrupts to it - mov $r1 #ih - mov $iv0 $r1 - mov $r1 0x400 - iowr I[$r1 + 0x300] $r0 // INTR_DISPATCH - - // enable fifo interrupt - mov $r2 4 - iowr I[$r1 + 0x000] $r2 // INTR_EN_SET - - // enable interrupts - bset $flags ie0 - - // figure out which GPC we are, and how many TPCs we have - mov $r1 0x608 - shl b32 $r1 6 - iord $r2 I[$r1 + 0x000] // UNITS - mov $r3 1 - and $r2 0x1f - shl b32 $r3 $r2 - sub b32 $r3 1 - st b32 D[$r0 + #tpc_count] $r2 - st b32 D[$r0 + #tpc_mask] $r3 - add b32 $r1 0x400 - iord $r2 I[$r1 + 0x000] // MYINDEX - st b32 D[$r0 + #gpc_id] $r2 - - // find context data for this chipset - mov $r2 0x800 - shl b32 $r2 6 - iord $r2 I[$r2 + 0x000] // CC_SCRATCH[0] - mov $r1 #chipsets - 12 - init_find_chipset: - add b32 $r1 12 - ld b32 $r3 D[$r1 + 0x00] - cmpu b32 $r3 $r2 - bra e #init_context - cmpu b32 $r3 0 - bra ne #init_find_chipset - // unknown chipset - ret - - // initialise context base, and size tracking - init_context: - mov $r2 0x800 - shl b32 $r2 6 - iord $r2 I[$r2 + 0x100] // CC_SCRATCH[1], initial base - clear b32 $r3 // track GPC context size here - - // set mmctx base addresses now so we don't have to do it later, - // they don't currently ever change - mov $r4 0x700 - shl b32 $r4 6 - shr b32 $r5 $r2 8 - iowr I[$r4 + 0x000] $r5 // MMCTX_SAVE_SWBASE - iowr I[$r4 + 0x100] $r5 // MMCTX_LOAD_SWBASE - - // calculate GPC mmio context size, store the chipset-specific - // mmio list pointers somewhere we can get at them later without - // re-parsing the chipset list - clear b32 $r14 - clear b32 $r15 - ld b16 $r14 D[$r1 + 4] - ld b16 $r15 D[$r1 + 6] - st b16 D[$r0 + #gpc_mmio_list_head] $r14 - st b16 D[$r0 + #gpc_mmio_list_tail] $r15 - call #mmctx_size - add b32 $r2 $r15 - add b32 $r3 $r15 - - // calculate per-TPC mmio context size, store the list pointers - ld b16 $r14 D[$r1 + 8] - ld b16 $r15 D[$r1 + 10] - st b16 D[$r0 + #tpc_mmio_list_head] $r14 - st b16 D[$r0 + #tpc_mmio_list_tail] $r15 - call #mmctx_size - ld b32 $r14 D[$r0 + #tpc_count] - mulu $r14 $r15 - add b32 $r2 $r14 - add b32 $r3 $r14 - - // round up base/size to 256 byte boundary (for strand SWBASE) - add b32 $r4 0x1300 - shr b32 $r3 2 - iowr I[$r4 + 0x000] $r3 // MMCTX_LOAD_COUNT, wtf for?!? - shr b32 $r2 8 - shr b32 $r3 6 - add b32 $r2 1 - add b32 $r3 1 - shl b32 $r2 8 - shl b32 $r3 8 - - // calculate size of strand context data - mov b32 $r15 $r2 - call #strand_ctx_init - add b32 $r3 $r15 - - // save context size, and tell HUB we're done - mov $r1 0x800 - shl b32 $r1 6 - iowr I[$r1 + 0x100] $r3 // CC_SCRATCH[1] = context size - add b32 $r1 0x800 - clear b32 $r2 - bset $r2 31 - iowr I[$r1 + 0x000] $r2 // CC_SCRATCH[0] |= 0x80000000 - -// Main program loop, very simple, sleeps until woken up by the interrupt -// handler, pulls a command from the queue and executes its handler -// -main: - bset $flags $p0 - sleep $p0 - mov $r13 #cmd_queue - call #queue_get - bra $p1 #main - - // 0x0000-0x0003 are all context transfers - cmpu b32 $r14 0x04 - bra nc #main_not_ctx_xfer - // fetch $flags and mask off $p1/$p2 - mov $r1 $flags - mov $r2 0x0006 - not b32 $r2 - and $r1 $r2 - // set $p1/$p2 according to transfer type - shl b32 $r14 1 - or $r1 $r14 - mov $flags $r1 - // transfer context data - call #ctx_xfer - bra #main - - main_not_ctx_xfer: - shl b32 $r15 $r14 16 - or $r15 E_BAD_COMMAND - call #error - bra #main - -// interrupt handler -ih: - push $r8 - mov $r8 $flags - push $r8 - push $r9 - push $r10 - push $r11 - push $r13 - push $r14 - push $r15 - - // incoming fifo command? - iord $r10 I[$r0 + 0x200] // INTR - and $r11 $r10 0x00000004 - bra e #ih_no_fifo - // queue incoming fifo command for later processing - mov $r11 0x1900 - mov $r13 #cmd_queue - iord $r14 I[$r11 + 0x100] // FIFO_CMD - iord $r15 I[$r11 + 0x000] // FIFO_DATA - call #queue_put - add b32 $r11 0x400 - mov $r14 1 - iowr I[$r11 + 0x000] $r14 // FIFO_ACK - - // ack, and wake up main() - ih_no_fifo: - iowr I[$r0 + 0x100] $r10 // INTR_ACK - - pop $r15 - pop $r14 - pop $r13 - pop $r11 - pop $r10 - pop $r9 - pop $r8 - mov $flags $r8 - pop $r8 - bclr $flags $p0 - iret - -// Set this GPC's bit in HUB_BAR, used to signal completion of various -// activities to the HUB fuc -// -hub_barrier_done: - mov $r15 1 - ld b32 $r14 D[$r0 + #gpc_id] - shl b32 $r15 $r14 - mov $r14 -0x6be8 // 0x409418 - HUB_BAR_SET - sethi $r14 0x400000 - call #nv_wr32 - ret - -// Disables various things, waits a bit, and re-enables them.. -// -// Not sure how exactly this helps, perhaps "ENABLE" is not such a -// good description for the bits we turn off? Anyways, without this, -// funny things happen. -// -ctx_redswitch: - mov $r14 0x614 - shl b32 $r14 6 - mov $r15 0x020 - iowr I[$r14] $r15 // GPC_RED_SWITCH = POWER - mov $r15 8 - ctx_redswitch_delay: - sub b32 $r15 1 - bra ne #ctx_redswitch_delay - mov $r15 0xa20 - iowr I[$r14] $r15 // GPC_RED_SWITCH = UNK11, ENABLE, POWER - ret - -// Transfer GPC context data between GPU and storage area -// -// In: $r15 context base address -// $p1 clear on save, set on load -// $p2 set if opposite direction done/will be done, so: -// on save it means: "a load will follow this save" -// on load it means: "a save preceeded this load" -// -ctx_xfer: - // set context base address - mov $r1 0xa04 - shl b32 $r1 6 - iowr I[$r1 + 0x000] $r15// MEM_BASE - bra not $p1 #ctx_xfer_not_load - call #ctx_redswitch - ctx_xfer_not_load: - - // strands - mov $r1 0x4afc - sethi $r1 0x20000 - mov $r2 0xc - iowr I[$r1] $r2 // STRAND_CMD(0x3f) = 0x0c - call #strand_wait - mov $r2 0x47fc - sethi $r2 0x20000 - iowr I[$r2] $r0 // STRAND_FIRST_GENE(0x3f) = 0x00 - xbit $r2 $flags $p1 - add b32 $r2 3 - iowr I[$r1] $r2 // STRAND_CMD(0x3f) = 0x03/0x04 (SAVE/LOAD) - - // mmio context - xbit $r10 $flags $p1 // direction - or $r10 2 // first - mov $r11 0x0000 - sethi $r11 0x500000 - ld b32 $r12 D[$r0 + #gpc_id] - shl b32 $r12 15 - add b32 $r11 $r12 // base = NV_PGRAPH_GPCn - ld b32 $r12 D[$r0 + #gpc_mmio_list_head] - ld b32 $r13 D[$r0 + #gpc_mmio_list_tail] - mov $r14 0 // not multi - call #mmctx_xfer - - // per-TPC mmio context - xbit $r10 $flags $p1 // direction - or $r10 4 // last - mov $r11 0x4000 - sethi $r11 0x500000 // base = NV_PGRAPH_GPC0_TPC0 - ld b32 $r12 D[$r0 + #gpc_id] - shl b32 $r12 15 - add b32 $r11 $r12 // base = NV_PGRAPH_GPCn_TPC0 - ld b32 $r12 D[$r0 + #tpc_mmio_list_head] - ld b32 $r13 D[$r0 + #tpc_mmio_list_tail] - ld b32 $r15 D[$r0 + #tpc_mask] - mov $r14 0x800 // stride = 0x800 - call #mmctx_xfer - - // wait for strands to finish - call #strand_wait - - // if load, or a save without a load following, do some - // unknown stuff that's done after finishing a block of - // strand commands - bra $p1 #ctx_xfer_post - bra not $p2 #ctx_xfer_done - ctx_xfer_post: - mov $r1 0x4afc - sethi $r1 0x20000 - mov $r2 0xd - iowr I[$r1] $r2 // STRAND_CMD(0x3f) = 0x0d - call #strand_wait - - // mark completion in HUB's barrier - ctx_xfer_done: - call #hub_barrier_done - ret - +#include "com.fuc" +#include "gpc.fuc" .align 256 +#undef INCLUDE_CODE diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h index 419bd5da1e00..504ae96cd3dd 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h @@ -223,7 +223,7 @@ uint32_t nve0_grgpc_code[] = { 0x0089d000, 0x081887f1, 0xd00684b6, -/* 0x00e2: wait_done_wait_donez */ +/* 0x00e2: wait_donez_ne */ 0x87f1008a, 0x84b60400, 0x0088cf06, @@ -240,7 +240,7 @@ uint32_t nve0_grgpc_code[] = { 0x87f10089, 0x84b60818, 0x008ad006, -/* 0x011c: wait_done_wait_doneo */ +/* 0x011c: wait_doneo_e */ 0x040087f1, 0xcf0684b6, 0x8aff0088, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hub.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hub.fuc new file mode 100644 index 000000000000..5c68bf6d69aa --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hub.fuc @@ -0,0 +1,755 @@ +/* fuc microcode for nvc0 PGRAPH/HUB + * + * Copyright 2011 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 + */ + +#ifdef INCLUDE_DATA +gpc_count: .b32 0 +rop_count: .b32 0 +cmd_queue: queue_init +hub_mmio_list_head: .b32 0 +hub_mmio_list_tail: .b32 0 + +ctx_current: .b32 0 + +.align 256 +chan_data: +chan_mmio_count: .b32 0 +chan_mmio_address: .b32 0 + +.align 256 +xfer_data: .skip 256 + +#endif + +#ifdef INCLUDE_CODE +// reports an exception to the host +// +// In: $r15 error code (see nvc0.fuc) +// +error: + push $r14 + mov $r14 0x814 + shl b32 $r14 6 + iowr I[$r14 + 0x000] $r15 // CC_SCRATCH[5] = error code + mov $r14 0xc1c + shl b32 $r14 6 + mov $r15 1 + iowr I[$r14 + 0x000] $r15 // INTR_UP_SET + pop $r14 + ret + +// HUB fuc initialisation, executed by triggering ucode start, will +// fall through to main loop after completion. +// +// Input: +// CC_SCRATCH[0]: chipset (PMC_BOOT_0 read returns 0x0bad0bad... sigh) +// +// Output: +// CC_SCRATCH[0]: +// 31:31: set to signal completion +// CC_SCRATCH[1]: +// 31:0: total PGRAPH context size +// +init: + clear b32 $r0 + mov $sp $r0 + mov $xdbase $r0 + + // enable fifo access + mov $r1 0x1200 + mov $r2 2 + iowr I[$r1 + 0x000] $r2 // FIFO_ENABLE + + // setup i0 handler, and route all interrupts to it + mov $r1 #ih + mov $iv0 $r1 + mov $r1 0x400 + iowr I[$r1 + 0x300] $r0 // INTR_DISPATCH + + // route HUB_CHANNEL_SWITCH to fuc interrupt 8 + mov $r3 0x404 + shl b32 $r3 6 + mov $r2 0x2003 // { HUB_CHANNEL_SWITCH, ZERO } -> intr 8 + iowr I[$r3 + 0x000] $r2 + + // not sure what these are, route them because NVIDIA does, and + // the IRQ handler will signal the host if we ever get one.. we + // may find out if/why we need to handle these if so.. + // + mov $r2 0x2004 + iowr I[$r3 + 0x004] $r2 // { 0x04, ZERO } -> intr 9 + mov $r2 0x200b + iowr I[$r3 + 0x008] $r2 // { 0x0b, ZERO } -> intr 10 + mov $r2 0x200c + iowr I[$r3 + 0x01c] $r2 // { 0x0c, ZERO } -> intr 15 + + // enable all INTR_UP interrupts + mov $r2 0xc24 + shl b32 $r2 6 + not b32 $r3 $r0 + iowr I[$r2] $r3 + + // enable fifo, ctxsw, 9, 10, 15 interrupts + mov $r2 -0x78fc // 0x8704 + sethi $r2 0 + iowr I[$r1 + 0x000] $r2 // INTR_EN_SET + + // fifo level triggered, rest edge + sub b32 $r1 0x100 + mov $r2 4 + iowr I[$r1] $r2 + + // enable interrupts + bset $flags ie0 + + // fetch enabled GPC/ROP counts + mov $r14 -0x69fc // 0x409604 + sethi $r14 0x400000 + call #nv_rd32 + extr $r1 $r15 16:20 + st b32 D[$r0 + #rop_count] $r1 + and $r15 0x1f + st b32 D[$r0 + #gpc_count] $r15 + + // set BAR_REQMASK to GPC mask + mov $r1 1 + shl b32 $r1 $r15 + sub b32 $r1 1 + mov $r2 0x40c + shl b32 $r2 6 + iowr I[$r2 + 0x000] $r1 + iowr I[$r2 + 0x100] $r1 + + // find context data for this chipset + mov $r2 0x800 + shl b32 $r2 6 + iord $r2 I[$r2 + 0x000] // CC_SCRATCH[0] + mov $r15 #chipsets - 8 + init_find_chipset: + add b32 $r15 8 + ld b32 $r3 D[$r15 + 0x00] + cmpu b32 $r3 $r2 + bra e #init_context + cmpu b32 $r3 0 + bra ne #init_find_chipset + // unknown chipset + ret + + // context size calculation, reserve first 256 bytes for use by fuc + init_context: + mov $r1 256 + + // calculate size of mmio context data + ld b16 $r14 D[$r15 + 4] + ld b16 $r15 D[$r15 + 6] + sethi $r14 0 + st b32 D[$r0 + #hub_mmio_list_head] $r14 + st b32 D[$r0 + #hub_mmio_list_tail] $r15 + call #mmctx_size + + // set mmctx base addresses now so we don't have to do it later, + // they don't (currently) ever change + mov $r3 0x700 + shl b32 $r3 6 + shr b32 $r4 $r1 8 + iowr I[$r3 + 0x000] $r4 // MMCTX_SAVE_SWBASE + iowr I[$r3 + 0x100] $r4 // MMCTX_LOAD_SWBASE + add b32 $r3 0x1300 + add b32 $r1 $r15 + shr b32 $r15 2 + iowr I[$r3 + 0x000] $r15 // MMCTX_LOAD_COUNT, wtf for?!? + + // strands, base offset needs to be aligned to 256 bytes + shr b32 $r1 8 + add b32 $r1 1 + shl b32 $r1 8 + mov b32 $r15 $r1 + call #strand_ctx_init + add b32 $r1 $r15 + + // initialise each GPC in sequence by passing in the offset of its + // context data in GPCn_CC_SCRATCH[1], and starting its FUC (which + // has previously been uploaded by the host) running. + // + // the GPC fuc init sequence will set GPCn_CC_SCRATCH[0] bit 31 + // when it has completed, and return the size of its context data + // in GPCn_CC_SCRATCH[1] + // + ld b32 $r3 D[$r0 + #gpc_count] + mov $r4 0x2000 + sethi $r4 0x500000 + init_gpc: + // setup, and start GPC ucode running + add b32 $r14 $r4 0x804 + mov b32 $r15 $r1 + call #nv_wr32 // CC_SCRATCH[1] = ctx offset + add b32 $r14 $r4 0x800 + mov b32 $r15 $r2 + call #nv_wr32 // CC_SCRATCH[0] = chipset + add b32 $r14 $r4 0x10c + clear b32 $r15 + call #nv_wr32 + add b32 $r14 $r4 0x104 + call #nv_wr32 // ENTRY + add b32 $r14 $r4 0x100 + mov $r15 2 // CTRL_START_TRIGGER + call #nv_wr32 // CTRL + + // wait for it to complete, and adjust context size + add b32 $r14 $r4 0x800 + init_gpc_wait: + call #nv_rd32 + xbit $r15 $r15 31 + bra e #init_gpc_wait + add b32 $r14 $r4 0x804 + call #nv_rd32 + add b32 $r1 $r15 + + // next! + add b32 $r4 0x8000 + sub b32 $r3 1 + bra ne #init_gpc + + // save context size, and tell host we're ready + mov $r2 0x800 + shl b32 $r2 6 + iowr I[$r2 + 0x100] $r1 // CC_SCRATCH[1] = context size + add b32 $r2 0x800 + clear b32 $r1 + bset $r1 31 + iowr I[$r2 + 0x000] $r1 // CC_SCRATCH[0] |= 0x80000000 + +// Main program loop, very simple, sleeps until woken up by the interrupt +// handler, pulls a command from the queue and executes its handler +// +main: + // sleep until we have something to do + bset $flags $p0 + sleep $p0 + mov $r13 #cmd_queue + call #queue_get + bra $p1 #main + + // context switch, requested by GPU? + cmpu b32 $r14 0x4001 + bra ne #main_not_ctx_switch + trace_set(T_AUTO) + mov $r1 0xb00 + shl b32 $r1 6 + iord $r2 I[$r1 + 0x100] // CHAN_NEXT + iord $r1 I[$r1 + 0x000] // CHAN_CUR + + xbit $r3 $r1 31 + bra e #chsw_no_prev + xbit $r3 $r2 31 + bra e #chsw_prev_no_next + push $r2 + mov b32 $r2 $r1 + trace_set(T_SAVE) + bclr $flags $p1 + bset $flags $p2 + call #ctx_xfer + trace_clr(T_SAVE); + pop $r2 + trace_set(T_LOAD); + bset $flags $p1 + call #ctx_xfer + trace_clr(T_LOAD); + bra #chsw_done + chsw_prev_no_next: + push $r2 + mov b32 $r2 $r1 + bclr $flags $p1 + bclr $flags $p2 + call #ctx_xfer + pop $r2 + mov $r1 0xb00 + shl b32 $r1 6 + iowr I[$r1] $r2 + bra #chsw_done + chsw_no_prev: + xbit $r3 $r2 31 + bra e #chsw_done + bset $flags $p1 + bclr $flags $p2 + call #ctx_xfer + + // ack the context switch request + chsw_done: + mov $r1 0xb0c + shl b32 $r1 6 + mov $r2 1 + iowr I[$r1 + 0x000] $r2 // 0x409b0c + trace_clr(T_AUTO) + bra #main + + // request to set current channel? (*not* a context switch) + main_not_ctx_switch: + cmpu b32 $r14 0x0001 + bra ne #main_not_ctx_chan + mov b32 $r2 $r15 + call #ctx_chan + bra #main_done + + // request to store current channel context? + main_not_ctx_chan: + cmpu b32 $r14 0x0002 + bra ne #main_not_ctx_save + trace_set(T_SAVE) + bclr $flags $p1 + bclr $flags $p2 + call #ctx_xfer + trace_clr(T_SAVE) + bra #main_done + + main_not_ctx_save: + shl b32 $r15 $r14 16 + or $r15 E_BAD_COMMAND + call #error + bra #main + + main_done: + mov $r1 0x820 + shl b32 $r1 6 + clear b32 $r2 + bset $r2 31 + iowr I[$r1 + 0x000] $r2 // CC_SCRATCH[0] |= 0x80000000 + bra #main + +// interrupt handler +ih: + push $r8 + mov $r8 $flags + push $r8 + push $r9 + push $r10 + push $r11 + push $r13 + push $r14 + push $r15 + + // incoming fifo command? + iord $r10 I[$r0 + 0x200] // INTR + and $r11 $r10 0x00000004 + bra e #ih_no_fifo + // queue incoming fifo command for later processing + mov $r11 0x1900 + mov $r13 #cmd_queue + iord $r14 I[$r11 + 0x100] // FIFO_CMD + iord $r15 I[$r11 + 0x000] // FIFO_DATA + call #queue_put + add b32 $r11 0x400 + mov $r14 1 + iowr I[$r11 + 0x000] $r14 // FIFO_ACK + + // context switch request? + ih_no_fifo: + and $r11 $r10 0x00000100 + bra e #ih_no_ctxsw + // enqueue a context switch for later processing + mov $r13 #cmd_queue + mov $r14 0x4001 + call #queue_put + + // anything we didn't handle, bring it to the host's attention + ih_no_ctxsw: + mov $r11 0x104 + not b32 $r11 + and $r11 $r10 $r11 + bra e #ih_no_other + mov $r10 0xc1c + shl b32 $r10 6 + iowr I[$r10] $r11 // INTR_UP_SET + + // ack, and wake up main() + ih_no_other: + iowr I[$r0 + 0x100] $r10 // INTR_ACK + + pop $r15 + pop $r14 + pop $r13 + pop $r11 + pop $r10 + pop $r9 + pop $r8 + mov $flags $r8 + pop $r8 + bclr $flags $p0 + iret + +#ifdef NVGF +// Not real sure, but, MEM_CMD 7 will hang forever if this isn't done +ctx_4160s: + mov $r14 0x4160 + sethi $r14 0x400000 + mov $r15 1 + call #nv_wr32 + ctx_4160s_wait: + call #nv_rd32 + xbit $r15 $r15 4 + bra e #ctx_4160s_wait + ret + +// Without clearing again at end of xfer, some things cause PGRAPH +// to hang with STATUS=0x00000007 until it's cleared.. fbcon can +// still function with it set however... +ctx_4160c: + mov $r14 0x4160 + sethi $r14 0x400000 + clear b32 $r15 + call #nv_wr32 + ret +#endif + +// Again, not real sure +// +// In: $r15 value to set 0x404170 to +// +ctx_4170s: + mov $r14 0x4170 + sethi $r14 0x400000 + or $r15 0x10 + call #nv_wr32 + ret + +// Waits for a ctx_4170s() call to complete +// +ctx_4170w: + mov $r14 0x4170 + sethi $r14 0x400000 + call #nv_rd32 + and $r15 0x10 + bra ne #ctx_4170w + ret + +// Disables various things, waits a bit, and re-enables them.. +// +// Not sure how exactly this helps, perhaps "ENABLE" is not such a +// good description for the bits we turn off? Anyways, without this, +// funny things happen. +// +ctx_redswitch: + mov $r14 0x614 + shl b32 $r14 6 + mov $r15 0x270 + iowr I[$r14] $r15 // HUB_RED_SWITCH = ENABLE_GPC, POWER_ALL + mov $r15 8 + ctx_redswitch_delay: + sub b32 $r15 1 + bra ne #ctx_redswitch_delay + mov $r15 0x770 + iowr I[$r14] $r15 // HUB_RED_SWITCH = ENABLE_ALL, POWER_ALL + ret + +// Not a clue what this is for, except that unless the value is 0x10, the +// strand context is saved (and presumably restored) incorrectly.. +// +// In: $r15 value to set to (0x00/0x10 are used) +// +ctx_86c: + mov $r14 0x86c + shl b32 $r14 6 + iowr I[$r14] $r15 // HUB(0x86c) = val + mov $r14 -0x75ec + sethi $r14 0x400000 + call #nv_wr32 // ROP(0xa14) = val + mov $r14 -0x5794 + sethi $r14 0x410000 + call #nv_wr32 // GPC(0x86c) = val + ret + +// ctx_load - load's a channel's ctxctl data, and selects its vm +// +// In: $r2 channel address +// +ctx_load: + trace_set(T_CHAN) + + // switch to channel, somewhat magic in parts.. + mov $r10 12 // DONE_UNK12 + call #wait_donez + mov $r1 0xa24 + shl b32 $r1 6 + iowr I[$r1 + 0x000] $r0 // 0x409a24 + mov $r3 0xb00 + shl b32 $r3 6 + iowr I[$r3 + 0x100] $r2 // CHAN_NEXT + mov $r1 0xa0c + shl b32 $r1 6 + mov $r4 7 + iowr I[$r1 + 0x000] $r2 // MEM_CHAN + iowr I[$r1 + 0x100] $r4 // MEM_CMD + ctx_chan_wait_0: + iord $r4 I[$r1 + 0x100] + and $r4 0x1f + bra ne #ctx_chan_wait_0 + iowr I[$r3 + 0x000] $r2 // CHAN_CUR + + // load channel header, fetch PGRAPH context pointer + mov $xtargets $r0 + bclr $r2 31 + shl b32 $r2 4 + add b32 $r2 2 + + trace_set(T_LCHAN) + mov $r1 0xa04 + shl b32 $r1 6 + iowr I[$r1 + 0x000] $r2 // MEM_BASE + mov $r1 0xa20 + shl b32 $r1 6 + mov $r2 0x0002 + sethi $r2 0x80000000 + iowr I[$r1 + 0x000] $r2 // MEM_TARGET = vram + mov $r1 0x10 // chan + 0x0210 + mov $r2 #xfer_data + sethi $r2 0x00020000 // 16 bytes + xdld $r1 $r2 + xdwait + trace_clr(T_LCHAN) + + // update current context + ld b32 $r1 D[$r0 + #xfer_data + 4] + shl b32 $r1 24 + ld b32 $r2 D[$r0 + #xfer_data + 0] + shr b32 $r2 8 + or $r1 $r2 + st b32 D[$r0 + #ctx_current] $r1 + + // set transfer base to start of context, and fetch context header + trace_set(T_LCTXH) + mov $r2 0xa04 + shl b32 $r2 6 + iowr I[$r2 + 0x000] $r1 // MEM_BASE + mov $r2 1 + mov $r1 0xa20 + shl b32 $r1 6 + iowr I[$r1 + 0x000] $r2 // MEM_TARGET = vm + mov $r1 #chan_data + sethi $r1 0x00060000 // 256 bytes + xdld $r0 $r1 + xdwait + trace_clr(T_LCTXH) + + trace_clr(T_CHAN) + ret + +// ctx_chan - handler for HUB_SET_CHAN command, will set a channel as +// the active channel for ctxctl, but not actually transfer +// any context data. intended for use only during initial +// context construction. +// +// In: $r2 channel address +// +ctx_chan: +#ifdef NVGF + call #ctx_4160s +#endif + call #ctx_load + mov $r10 12 // DONE_UNK12 + call #wait_donez + mov $r1 0xa10 + shl b32 $r1 6 + mov $r2 5 + iowr I[$r1 + 0x000] $r2 // MEM_CMD = 5 (???) + ctx_chan_wait: + iord $r2 I[$r1 + 0x000] + or $r2 $r2 + bra ne #ctx_chan_wait +#ifdef NVGF + call #ctx_4160c +#endif + ret + +// Execute per-context state overrides list +// +// Only executed on the first load of a channel. Might want to look into +// removing this and having the host directly modify the channel's context +// to change this state... The nouveau DRM already builds this list as +// it's definitely needed for NVIDIA's, so we may as well use it for now +// +// Input: $r1 mmio list length +// +ctx_mmio_exec: + // set transfer base to be the mmio list + ld b32 $r3 D[$r0 + #chan_mmio_address] + mov $r2 0xa04 + shl b32 $r2 6 + iowr I[$r2 + 0x000] $r3 // MEM_BASE + + clear b32 $r3 + ctx_mmio_loop: + // fetch next 256 bytes of mmio list if necessary + and $r4 $r3 0xff + bra ne #ctx_mmio_pull + mov $r5 #xfer_data + sethi $r5 0x00060000 // 256 bytes + xdld $r3 $r5 + xdwait + + // execute a single list entry + ctx_mmio_pull: + ld b32 $r14 D[$r4 + #xfer_data + 0x00] + ld b32 $r15 D[$r4 + #xfer_data + 0x04] + call #nv_wr32 + + // next! + add b32 $r3 8 + sub b32 $r1 1 + bra ne #ctx_mmio_loop + + // set transfer base back to the current context + ctx_mmio_done: + ld b32 $r3 D[$r0 + #ctx_current] + iowr I[$r2 + 0x000] $r3 // MEM_BASE + + // disable the mmio list now, we don't need/want to execute it again + st b32 D[$r0 + #chan_mmio_count] $r0 + mov $r1 #chan_data + sethi $r1 0x00060000 // 256 bytes + xdst $r0 $r1 + xdwait + ret + +// Transfer HUB context data between GPU and storage area +// +// In: $r2 channel address +// $p1 clear on save, set on load +// $p2 set if opposite direction done/will be done, so: +// on save it means: "a load will follow this save" +// on load it means: "a save preceeded this load" +// +ctx_xfer: + // according to mwk, some kind of wait for idle + mov $r15 0xc00 + shl b32 $r15 6 + mov $r14 4 + iowr I[$r15 + 0x200] $r14 + ctx_xfer_idle: + iord $r14 I[$r15 + 0x000] + and $r14 0x2000 + bra ne #ctx_xfer_idle + + bra not $p1 #ctx_xfer_pre + bra $p2 #ctx_xfer_pre_load + ctx_xfer_pre: + mov $r15 0x10 + call #ctx_86c +#ifdef NVGF + call #ctx_4160s +#endif + bra not $p1 #ctx_xfer_exec + + ctx_xfer_pre_load: + mov $r15 2 + call #ctx_4170s + call #ctx_4170w + call #ctx_redswitch + clear b32 $r15 + call #ctx_4170s + call #ctx_load + + // fetch context pointer, and initiate xfer on all GPCs + ctx_xfer_exec: + ld b32 $r1 D[$r0 + #ctx_current] + mov $r2 0x414 + shl b32 $r2 6 + iowr I[$r2 + 0x000] $r0 // BAR_STATUS = reset + mov $r14 -0x5b00 + sethi $r14 0x410000 + mov b32 $r15 $r1 + call #nv_wr32 // GPC_BCAST_WRCMD_DATA = ctx pointer + add b32 $r14 4 + xbit $r15 $flags $p1 + xbit $r2 $flags $p2 + shl b32 $r2 1 + or $r15 $r2 + call #nv_wr32 // GPC_BCAST_WRCMD_CMD = GPC_XFER(type) + + // strands + mov $r1 0x4afc + sethi $r1 0x20000 + mov $r2 0xc + iowr I[$r1] $r2 // STRAND_CMD(0x3f) = 0x0c + call #strand_wait + mov $r2 0x47fc + sethi $r2 0x20000 + iowr I[$r2] $r0 // STRAND_FIRST_GENE(0x3f) = 0x00 + xbit $r2 $flags $p1 + add b32 $r2 3 + iowr I[$r1] $r2 // STRAND_CMD(0x3f) = 0x03/0x04 (SAVE/LOAD) + + // mmio context + xbit $r10 $flags $p1 // direction + or $r10 6 // first, last + mov $r11 0 // base = 0 + ld b32 $r12 D[$r0 + #hub_mmio_list_head] + ld b32 $r13 D[$r0 + #hub_mmio_list_tail] + mov $r14 0 // not multi + call #mmctx_xfer + + // wait for GPCs to all complete + mov $r10 8 // DONE_BAR + call #wait_doneo + + // wait for strand xfer to complete + call #strand_wait + + // post-op + bra $p1 #ctx_xfer_post + mov $r10 12 // DONE_UNK12 + call #wait_donez + mov $r1 0xa10 + shl b32 $r1 6 + mov $r2 5 + iowr I[$r1] $r2 // MEM_CMD + ctx_xfer_post_save_wait: + iord $r2 I[$r1] + or $r2 $r2 + bra ne #ctx_xfer_post_save_wait + + bra $p2 #ctx_xfer_done + ctx_xfer_post: + mov $r15 2 + call #ctx_4170s + clear b32 $r15 + call #ctx_86c + call #strand_post + call #ctx_4170w + clear b32 $r15 + call #ctx_4170s + + bra not $p1 #ctx_xfer_no_post_mmio + ld b32 $r1 D[$r0 + #chan_mmio_count] + or $r1 $r1 + bra e #ctx_xfer_no_post_mmio + call #ctx_mmio_exec + + ctx_xfer_no_post_mmio: +#ifdef NVGF + call #ctx_4160c +#endif + + ctx_xfer_done: + ret +#endif diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc index 9f174be6bc82..f144f665b807 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc @@ -1,6 +1,5 @@ -/* fuc microcode for nvc0 PGRAPH/HUB - * - * Copyright 2011 Red Hat Inc. +/* + * Copyright 2013 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"), @@ -20,32 +19,17 @@ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * - * Authors: Ben Skeggs + * Authors: Ben Skeggs */ -/* To build: - * m4 hubnvc0.fuc | envyas -a -w -m fuc -V fuc3 -o hubnvc0.fuc.h - */ +#define NVGF +#include "macros.fuc" .section #nvc0_grhub_data -include(`nvc0.fuc') -gpc_count: .b32 0 -rop_count: .b32 0 -cmd_queue: queue_init -hub_mmio_list_head: .b32 0 -hub_mmio_list_tail: .b32 0 - -ctx_current: .b32 0 - -.align 256 -chan_data: -chan_mmio_count: .b32 0 -chan_mmio_address: .b32 0 +#define INCLUDE_DATA +#include "com.fuc" +#include "hub.fuc" -.align 256 -xfer_data: .b32 0 - -.align 256 chipsets: .b8 0xc0 0 0 0 .b16 #nvc0_hub_mmio_head @@ -124,710 +108,12 @@ mmctx_data(0x4064c0, 2) nvc1_hub_mmio_tail: mmctx_data(0x4064bc, 3) nvd9_hub_mmio_tail: +#undef INCLUDE_DATA .section #nvc0_grhub_code +#define INCLUDE_CODE bra #init -define(`include_code') -include(`nvc0.fuc') - -// reports an exception to the host -// -// In: $r15 error code (see nvc0.fuc) -// -error: - push $r14 - mov $r14 0x814 - shl b32 $r14 6 - iowr I[$r14 + 0x000] $r15 // CC_SCRATCH[5] = error code - mov $r14 0xc1c - shl b32 $r14 6 - mov $r15 1 - iowr I[$r14 + 0x000] $r15 // INTR_UP_SET - pop $r14 - ret - -// HUB fuc initialisation, executed by triggering ucode start, will -// fall through to main loop after completion. -// -// Input: -// CC_SCRATCH[0]: chipset (PMC_BOOT_0 read returns 0x0bad0bad... sigh) -// -// Output: -// CC_SCRATCH[0]: -// 31:31: set to signal completion -// CC_SCRATCH[1]: -// 31:0: total PGRAPH context size -// -init: - clear b32 $r0 - mov $sp $r0 - mov $xdbase $r0 - - // enable fifo access - mov $r1 0x1200 - mov $r2 2 - iowr I[$r1 + 0x000] $r2 // FIFO_ENABLE - - // setup i0 handler, and route all interrupts to it - mov $r1 #ih - mov $iv0 $r1 - mov $r1 0x400 - iowr I[$r1 + 0x300] $r0 // INTR_DISPATCH - - // route HUB_CHANNEL_SWITCH to fuc interrupt 8 - mov $r3 0x404 - shl b32 $r3 6 - mov $r2 0x2003 // { HUB_CHANNEL_SWITCH, ZERO } -> intr 8 - iowr I[$r3 + 0x000] $r2 - - // not sure what these are, route them because NVIDIA does, and - // the IRQ handler will signal the host if we ever get one.. we - // may find out if/why we need to handle these if so.. - // - mov $r2 0x2004 - iowr I[$r3 + 0x004] $r2 // { 0x04, ZERO } -> intr 9 - mov $r2 0x200b - iowr I[$r3 + 0x008] $r2 // { 0x0b, ZERO } -> intr 10 - mov $r2 0x200c - iowr I[$r3 + 0x01c] $r2 // { 0x0c, ZERO } -> intr 15 - - // enable all INTR_UP interrupts - mov $r2 0xc24 - shl b32 $r2 6 - not b32 $r3 $r0 - iowr I[$r2] $r3 - - // enable fifo, ctxsw, 9, 10, 15 interrupts - mov $r2 -0x78fc // 0x8704 - sethi $r2 0 - iowr I[$r1 + 0x000] $r2 // INTR_EN_SET - - // fifo level triggered, rest edge - sub b32 $r1 0x100 - mov $r2 4 - iowr I[$r1] $r2 - - // enable interrupts - bset $flags ie0 - - // fetch enabled GPC/ROP counts - mov $r14 -0x69fc // 0x409604 - sethi $r14 0x400000 - call #nv_rd32 - extr $r1 $r15 16:20 - st b32 D[$r0 + #rop_count] $r1 - and $r15 0x1f - st b32 D[$r0 + #gpc_count] $r15 - - // set BAR_REQMASK to GPC mask - mov $r1 1 - shl b32 $r1 $r15 - sub b32 $r1 1 - mov $r2 0x40c - shl b32 $r2 6 - iowr I[$r2 + 0x000] $r1 - iowr I[$r2 + 0x100] $r1 - - // find context data for this chipset - mov $r2 0x800 - shl b32 $r2 6 - iord $r2 I[$r2 + 0x000] // CC_SCRATCH[0] - mov $r15 #chipsets - 8 - init_find_chipset: - add b32 $r15 8 - ld b32 $r3 D[$r15 + 0x00] - cmpu b32 $r3 $r2 - bra e #init_context - cmpu b32 $r3 0 - bra ne #init_find_chipset - // unknown chipset - ret - - // context size calculation, reserve first 256 bytes for use by fuc - init_context: - mov $r1 256 - - // calculate size of mmio context data - ld b16 $r14 D[$r15 + 4] - ld b16 $r15 D[$r15 + 6] - sethi $r14 0 - st b32 D[$r0 + #hub_mmio_list_head] $r14 - st b32 D[$r0 + #hub_mmio_list_tail] $r15 - call #mmctx_size - - // set mmctx base addresses now so we don't have to do it later, - // they don't (currently) ever change - mov $r3 0x700 - shl b32 $r3 6 - shr b32 $r4 $r1 8 - iowr I[$r3 + 0x000] $r4 // MMCTX_SAVE_SWBASE - iowr I[$r3 + 0x100] $r4 // MMCTX_LOAD_SWBASE - add b32 $r3 0x1300 - add b32 $r1 $r15 - shr b32 $r15 2 - iowr I[$r3 + 0x000] $r15 // MMCTX_LOAD_COUNT, wtf for?!? - - // strands, base offset needs to be aligned to 256 bytes - shr b32 $r1 8 - add b32 $r1 1 - shl b32 $r1 8 - mov b32 $r15 $r1 - call #strand_ctx_init - add b32 $r1 $r15 - - // initialise each GPC in sequence by passing in the offset of its - // context data in GPCn_CC_SCRATCH[1], and starting its FUC (which - // has previously been uploaded by the host) running. - // - // the GPC fuc init sequence will set GPCn_CC_SCRATCH[0] bit 31 - // when it has completed, and return the size of its context data - // in GPCn_CC_SCRATCH[1] - // - ld b32 $r3 D[$r0 + #gpc_count] - mov $r4 0x2000 - sethi $r4 0x500000 - init_gpc: - // setup, and start GPC ucode running - add b32 $r14 $r4 0x804 - mov b32 $r15 $r1 - call #nv_wr32 // CC_SCRATCH[1] = ctx offset - add b32 $r14 $r4 0x800 - mov b32 $r15 $r2 - call #nv_wr32 // CC_SCRATCH[0] = chipset - add b32 $r14 $r4 0x10c - clear b32 $r15 - call #nv_wr32 - add b32 $r14 $r4 0x104 - call #nv_wr32 // ENTRY - add b32 $r14 $r4 0x100 - mov $r15 2 // CTRL_START_TRIGGER - call #nv_wr32 // CTRL - - // wait for it to complete, and adjust context size - add b32 $r14 $r4 0x800 - init_gpc_wait: - call #nv_rd32 - xbit $r15 $r15 31 - bra e #init_gpc_wait - add b32 $r14 $r4 0x804 - call #nv_rd32 - add b32 $r1 $r15 - - // next! - add b32 $r4 0x8000 - sub b32 $r3 1 - bra ne #init_gpc - - // save context size, and tell host we're ready - mov $r2 0x800 - shl b32 $r2 6 - iowr I[$r2 + 0x100] $r1 // CC_SCRATCH[1] = context size - add b32 $r2 0x800 - clear b32 $r1 - bset $r1 31 - iowr I[$r2 + 0x000] $r1 // CC_SCRATCH[0] |= 0x80000000 - -// Main program loop, very simple, sleeps until woken up by the interrupt -// handler, pulls a command from the queue and executes its handler -// -main: - // sleep until we have something to do - bset $flags $p0 - sleep $p0 - mov $r13 #cmd_queue - call #queue_get - bra $p1 #main - - // context switch, requested by GPU? - cmpu b32 $r14 0x4001 - bra ne #main_not_ctx_switch - trace_set(T_AUTO) - mov $r1 0xb00 - shl b32 $r1 6 - iord $r2 I[$r1 + 0x100] // CHAN_NEXT - iord $r1 I[$r1 + 0x000] // CHAN_CUR - - xbit $r3 $r1 31 - bra e #chsw_no_prev - xbit $r3 $r2 31 - bra e #chsw_prev_no_next - push $r2 - mov b32 $r2 $r1 - trace_set(T_SAVE) - bclr $flags $p1 - bset $flags $p2 - call #ctx_xfer - trace_clr(T_SAVE); - pop $r2 - trace_set(T_LOAD); - bset $flags $p1 - call #ctx_xfer - trace_clr(T_LOAD); - bra #chsw_done - chsw_prev_no_next: - push $r2 - mov b32 $r2 $r1 - bclr $flags $p1 - bclr $flags $p2 - call #ctx_xfer - pop $r2 - mov $r1 0xb00 - shl b32 $r1 6 - iowr I[$r1] $r2 - bra #chsw_done - chsw_no_prev: - xbit $r3 $r2 31 - bra e #chsw_done - bset $flags $p1 - bclr $flags $p2 - call #ctx_xfer - - // ack the context switch request - chsw_done: - mov $r1 0xb0c - shl b32 $r1 6 - mov $r2 1 - iowr I[$r1 + 0x000] $r2 // 0x409b0c - trace_clr(T_AUTO) - bra #main - - // request to set current channel? (*not* a context switch) - main_not_ctx_switch: - cmpu b32 $r14 0x0001 - bra ne #main_not_ctx_chan - mov b32 $r2 $r15 - call #ctx_chan - bra #main_done - - // request to store current channel context? - main_not_ctx_chan: - cmpu b32 $r14 0x0002 - bra ne #main_not_ctx_save - trace_set(T_SAVE) - bclr $flags $p1 - bclr $flags $p2 - call #ctx_xfer - trace_clr(T_SAVE) - bra #main_done - - main_not_ctx_save: - shl b32 $r15 $r14 16 - or $r15 E_BAD_COMMAND - call #error - bra #main - - main_done: - mov $r1 0x820 - shl b32 $r1 6 - clear b32 $r2 - bset $r2 31 - iowr I[$r1 + 0x000] $r2 // CC_SCRATCH[0] |= 0x80000000 - bra #main - -// interrupt handler -ih: - push $r8 - mov $r8 $flags - push $r8 - push $r9 - push $r10 - push $r11 - push $r13 - push $r14 - push $r15 - - // incoming fifo command? - iord $r10 I[$r0 + 0x200] // INTR - and $r11 $r10 0x00000004 - bra e #ih_no_fifo - // queue incoming fifo command for later processing - mov $r11 0x1900 - mov $r13 #cmd_queue - iord $r14 I[$r11 + 0x100] // FIFO_CMD - iord $r15 I[$r11 + 0x000] // FIFO_DATA - call #queue_put - add b32 $r11 0x400 - mov $r14 1 - iowr I[$r11 + 0x000] $r14 // FIFO_ACK - - // context switch request? - ih_no_fifo: - and $r11 $r10 0x00000100 - bra e #ih_no_ctxsw - // enqueue a context switch for later processing - mov $r13 #cmd_queue - mov $r14 0x4001 - call #queue_put - - // anything we didn't handle, bring it to the host's attention - ih_no_ctxsw: - mov $r11 0x104 - not b32 $r11 - and $r11 $r10 $r11 - bra e #ih_no_other - mov $r10 0xc1c - shl b32 $r10 6 - iowr I[$r10] $r11 // INTR_UP_SET - - // ack, and wake up main() - ih_no_other: - iowr I[$r0 + 0x100] $r10 // INTR_ACK - - pop $r15 - pop $r14 - pop $r13 - pop $r11 - pop $r10 - pop $r9 - pop $r8 - mov $flags $r8 - pop $r8 - bclr $flags $p0 - iret - -// Not real sure, but, MEM_CMD 7 will hang forever if this isn't done -ctx_4160s: - mov $r14 0x4160 - sethi $r14 0x400000 - mov $r15 1 - call #nv_wr32 - ctx_4160s_wait: - call #nv_rd32 - xbit $r15 $r15 4 - bra e #ctx_4160s_wait - ret - -// Without clearing again at end of xfer, some things cause PGRAPH -// to hang with STATUS=0x00000007 until it's cleared.. fbcon can -// still function with it set however... -ctx_4160c: - mov $r14 0x4160 - sethi $r14 0x400000 - clear b32 $r15 - call #nv_wr32 - ret - -// Again, not real sure -// -// In: $r15 value to set 0x404170 to -// -ctx_4170s: - mov $r14 0x4170 - sethi $r14 0x400000 - or $r15 0x10 - call #nv_wr32 - ret - -// Waits for a ctx_4170s() call to complete -// -ctx_4170w: - mov $r14 0x4170 - sethi $r14 0x400000 - call #nv_rd32 - and $r15 0x10 - bra ne #ctx_4170w - ret - -// Disables various things, waits a bit, and re-enables them.. -// -// Not sure how exactly this helps, perhaps "ENABLE" is not such a -// good description for the bits we turn off? Anyways, without this, -// funny things happen. -// -ctx_redswitch: - mov $r14 0x614 - shl b32 $r14 6 - mov $r15 0x270 - iowr I[$r14] $r15 // HUB_RED_SWITCH = ENABLE_GPC, POWER_ALL - mov $r15 8 - ctx_redswitch_delay: - sub b32 $r15 1 - bra ne #ctx_redswitch_delay - mov $r15 0x770 - iowr I[$r14] $r15 // HUB_RED_SWITCH = ENABLE_ALL, POWER_ALL - ret - -// Not a clue what this is for, except that unless the value is 0x10, the -// strand context is saved (and presumably restored) incorrectly.. -// -// In: $r15 value to set to (0x00/0x10 are used) -// -ctx_86c: - mov $r14 0x86c - shl b32 $r14 6 - iowr I[$r14] $r15 // HUB(0x86c) = val - mov $r14 -0x75ec - sethi $r14 0x400000 - call #nv_wr32 // ROP(0xa14) = val - mov $r14 -0x5794 - sethi $r14 0x410000 - call #nv_wr32 // GPC(0x86c) = val - ret - -// ctx_load - load's a channel's ctxctl data, and selects its vm -// -// In: $r2 channel address -// -ctx_load: - trace_set(T_CHAN) - - // switch to channel, somewhat magic in parts.. - mov $r10 12 // DONE_UNK12 - call #wait_donez - mov $r1 0xa24 - shl b32 $r1 6 - iowr I[$r1 + 0x000] $r0 // 0x409a24 - mov $r3 0xb00 - shl b32 $r3 6 - iowr I[$r3 + 0x100] $r2 // CHAN_NEXT - mov $r1 0xa0c - shl b32 $r1 6 - mov $r4 7 - iowr I[$r1 + 0x000] $r2 // MEM_CHAN - iowr I[$r1 + 0x100] $r4 // MEM_CMD - ctx_chan_wait_0: - iord $r4 I[$r1 + 0x100] - and $r4 0x1f - bra ne #ctx_chan_wait_0 - iowr I[$r3 + 0x000] $r2 // CHAN_CUR - - // load channel header, fetch PGRAPH context pointer - mov $xtargets $r0 - bclr $r2 31 - shl b32 $r2 4 - add b32 $r2 2 - - trace_set(T_LCHAN) - mov $r1 0xa04 - shl b32 $r1 6 - iowr I[$r1 + 0x000] $r2 // MEM_BASE - mov $r1 0xa20 - shl b32 $r1 6 - mov $r2 0x0002 - sethi $r2 0x80000000 - iowr I[$r1 + 0x000] $r2 // MEM_TARGET = vram - mov $r1 0x10 // chan + 0x0210 - mov $r2 #xfer_data - sethi $r2 0x00020000 // 16 bytes - xdld $r1 $r2 - xdwait - trace_clr(T_LCHAN) - - // update current context - ld b32 $r1 D[$r0 + #xfer_data + 4] - shl b32 $r1 24 - ld b32 $r2 D[$r0 + #xfer_data + 0] - shr b32 $r2 8 - or $r1 $r2 - st b32 D[$r0 + #ctx_current] $r1 - - // set transfer base to start of context, and fetch context header - trace_set(T_LCTXH) - mov $r2 0xa04 - shl b32 $r2 6 - iowr I[$r2 + 0x000] $r1 // MEM_BASE - mov $r2 1 - mov $r1 0xa20 - shl b32 $r1 6 - iowr I[$r1 + 0x000] $r2 // MEM_TARGET = vm - mov $r1 #chan_data - sethi $r1 0x00060000 // 256 bytes - xdld $r0 $r1 - xdwait - trace_clr(T_LCTXH) - - trace_clr(T_CHAN) - ret - -// ctx_chan - handler for HUB_SET_CHAN command, will set a channel as -// the active channel for ctxctl, but not actually transfer -// any context data. intended for use only during initial -// context construction. -// -// In: $r2 channel address -// -ctx_chan: - call #ctx_4160s - call #ctx_load - mov $r10 12 // DONE_UNK12 - call #wait_donez - mov $r1 0xa10 - shl b32 $r1 6 - mov $r2 5 - iowr I[$r1 + 0x000] $r2 // MEM_CMD = 5 (???) - ctx_chan_wait: - iord $r2 I[$r1 + 0x000] - or $r2 $r2 - bra ne #ctx_chan_wait - call #ctx_4160c - ret - -// Execute per-context state overrides list -// -// Only executed on the first load of a channel. Might want to look into -// removing this and having the host directly modify the channel's context -// to change this state... The nouveau DRM already builds this list as -// it's definitely needed for NVIDIA's, so we may as well use it for now -// -// Input: $r1 mmio list length -// -ctx_mmio_exec: - // set transfer base to be the mmio list - ld b32 $r3 D[$r0 + #chan_mmio_address] - mov $r2 0xa04 - shl b32 $r2 6 - iowr I[$r2 + 0x000] $r3 // MEM_BASE - - clear b32 $r3 - ctx_mmio_loop: - // fetch next 256 bytes of mmio list if necessary - and $r4 $r3 0xff - bra ne #ctx_mmio_pull - mov $r5 #xfer_data - sethi $r5 0x00060000 // 256 bytes - xdld $r3 $r5 - xdwait - - // execute a single list entry - ctx_mmio_pull: - ld b32 $r14 D[$r4 + #xfer_data + 0x00] - ld b32 $r15 D[$r4 + #xfer_data + 0x04] - call #nv_wr32 - - // next! - add b32 $r3 8 - sub b32 $r1 1 - bra ne #ctx_mmio_loop - - // set transfer base back to the current context - ctx_mmio_done: - ld b32 $r3 D[$r0 + #ctx_current] - iowr I[$r2 + 0x000] $r3 // MEM_BASE - - // disable the mmio list now, we don't need/want to execute it again - st b32 D[$r0 + #chan_mmio_count] $r0 - mov $r1 #chan_data - sethi $r1 0x00060000 // 256 bytes - xdst $r0 $r1 - xdwait - ret - -// Transfer HUB context data between GPU and storage area -// -// In: $r2 channel address -// $p1 clear on save, set on load -// $p2 set if opposite direction done/will be done, so: -// on save it means: "a load will follow this save" -// on load it means: "a save preceeded this load" -// -ctx_xfer: - // according to mwk, some kind of wait for idle - mov $r15 0xc00 - shl b32 $r15 6 - mov $r14 4 - iowr I[$r15 + 0x200] $r14 - ctx_xfer_idle: - iord $r14 I[$r15 + 0x000] - and $r14 0x2000 - bra ne #ctx_xfer_idle - - bra not $p1 #ctx_xfer_pre - bra $p2 #ctx_xfer_pre_load - ctx_xfer_pre: - mov $r15 0x10 - call #ctx_86c - call #ctx_4160s - bra not $p1 #ctx_xfer_exec - - ctx_xfer_pre_load: - mov $r15 2 - call #ctx_4170s - call #ctx_4170w - call #ctx_redswitch - clear b32 $r15 - call #ctx_4170s - call #ctx_load - - // fetch context pointer, and initiate xfer on all GPCs - ctx_xfer_exec: - ld b32 $r1 D[$r0 + #ctx_current] - mov $r2 0x414 - shl b32 $r2 6 - iowr I[$r2 + 0x000] $r0 // BAR_STATUS = reset - mov $r14 -0x5b00 - sethi $r14 0x410000 - mov b32 $r15 $r1 - call #nv_wr32 // GPC_BCAST_WRCMD_DATA = ctx pointer - add b32 $r14 4 - xbit $r15 $flags $p1 - xbit $r2 $flags $p2 - shl b32 $r2 1 - or $r15 $r2 - call #nv_wr32 // GPC_BCAST_WRCMD_CMD = GPC_XFER(type) - - // strands - mov $r1 0x4afc - sethi $r1 0x20000 - mov $r2 0xc - iowr I[$r1] $r2 // STRAND_CMD(0x3f) = 0x0c - call #strand_wait - mov $r2 0x47fc - sethi $r2 0x20000 - iowr I[$r2] $r0 // STRAND_FIRST_GENE(0x3f) = 0x00 - xbit $r2 $flags $p1 - add b32 $r2 3 - iowr I[$r1] $r2 // STRAND_CMD(0x3f) = 0x03/0x04 (SAVE/LOAD) - - // mmio context - xbit $r10 $flags $p1 // direction - or $r10 6 // first, last - mov $r11 0 // base = 0 - ld b32 $r12 D[$r0 + #hub_mmio_list_head] - ld b32 $r13 D[$r0 + #hub_mmio_list_tail] - mov $r14 0 // not multi - call #mmctx_xfer - - // wait for GPCs to all complete - mov $r10 8 // DONE_BAR - call #wait_doneo - - // wait for strand xfer to complete - call #strand_wait - - // post-op - bra $p1 #ctx_xfer_post - mov $r10 12 // DONE_UNK12 - call #wait_donez - mov $r1 0xa10 - shl b32 $r1 6 - mov $r2 5 - iowr I[$r1] $r2 // MEM_CMD - ctx_xfer_post_save_wait: - iord $r2 I[$r1] - or $r2 $r2 - bra ne #ctx_xfer_post_save_wait - - bra $p2 #ctx_xfer_done - ctx_xfer_post: - mov $r15 2 - call #ctx_4170s - clear b32 $r15 - call #ctx_86c - call #strand_post - call #ctx_4170w - clear b32 $r15 - call #ctx_4170s - - bra not $p1 #ctx_xfer_no_post_mmio - ld b32 $r1 D[$r0 + #chan_mmio_count] - or $r1 $r1 - bra e #ctx_xfer_no_post_mmio - call #ctx_mmio_exec - - ctx_xfer_no_post_mmio: - call #ctx_4160c - - ctx_xfer_done: - ret - +#include "com.fuc" +#include "hub.fuc" .align 256 +#undef INCLUDE_CODE diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h index 0953c2db2d13..d1bf23001830 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h @@ -338,7 +338,7 @@ uint32_t nvc0_grhub_code[] = { 0x0089d000, 0x081887f1, 0xd00684b6, -/* 0x00e2: wait_done_wait_donez */ +/* 0x00e2: wait_donez_ne */ 0x87f1008a, 0x84b60400, 0x0088cf06, @@ -355,7 +355,7 @@ uint32_t nvc0_grhub_code[] = { 0x87f10089, 0x84b60818, 0x008ad006, -/* 0x011c: wait_done_wait_doneo */ +/* 0x011c: wait_doneo_e */ 0x040087f1, 0xcf0684b6, 0x8aff0088, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc index b57a3db8df71..c7225db6486c 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc @@ -1,6 +1,5 @@ -/* fuc microcode for nve0 PGRAPH/HUB - * - * Copyright 2011 Red Hat Inc. +/* + * Copyright 2013 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"), @@ -20,32 +19,17 @@ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * - * Authors: Ben Skeggs + * Authors: Ben Skeggs */ -/* To build: - * m4 nve0_grhub.fuc | envyas -a -w -m fuc -V nva3 -o nve0_grhub.fuc.h - */ +#define NVGK +#include "macros.fuc" .section #nve0_grhub_data -include(`nve0.fuc') -gpc_count: .b32 0 -rop_count: .b32 0 -cmd_queue: queue_init -hub_mmio_list_head: .b32 0 -hub_mmio_list_tail: .b32 0 - -ctx_current: .b32 0 - -.align 256 -chan_data: -chan_mmio_count: .b32 0 -chan_mmio_address: .b32 0 +#define INCLUDE_DATA +#include "com.fuc" +#include "hub.fuc" -.align 256 -xfer_data: .b32 0 - -.align 256 chipsets: .b8 0xe4 0 0 0 .b16 #nve4_hub_mmio_head @@ -170,684 +154,12 @@ mmctx_data(0x408840, 1) mmctx_data(0x408900, 3) mmctx_data(0x408980, 1) nvf0_hub_mmio_tail: +#undef INCLUDE_DATA .section #nve0_grhub_code +#define INCLUDE_CODE bra #init -define(`include_code') -include(`nve0.fuc') - -// reports an exception to the host -// -// In: $r15 error code (see nve0.fuc) -// -error: - push $r14 - mov $r14 0x814 - shl b32 $r14 6 - iowr I[$r14 + 0x000] $r15 // CC_SCRATCH[5] = error code - mov $r14 0xc1c - shl b32 $r14 6 - mov $r15 1 - iowr I[$r14 + 0x000] $r15 // INTR_UP_SET - pop $r14 - ret - -// HUB fuc initialisation, executed by triggering ucode start, will -// fall through to main loop after completion. -// -// Input: -// CC_SCRATCH[0]: chipset (PMC_BOOT_0 read returns 0x0bad0bad... sigh) -// -// Output: -// CC_SCRATCH[0]: -// 31:31: set to signal completion -// CC_SCRATCH[1]: -// 31:0: total PGRAPH context size -// -init: - clear b32 $r0 - mov $sp $r0 - mov $xdbase $r0 - - // enable fifo access - mov $r1 0x1200 - mov $r2 2 - iowr I[$r1 + 0x000] $r2 // FIFO_ENABLE - - // setup i0 handler, and route all interrupts to it - mov $r1 #ih - mov $iv0 $r1 - mov $r1 0x400 - iowr I[$r1 + 0x300] $r0 // INTR_DISPATCH - - // route HUB_CHANNEL_SWITCH to fuc interrupt 8 - mov $r3 0x404 - shl b32 $r3 6 - mov $r2 0x2003 // { HUB_CHANNEL_SWITCH, ZERO } -> intr 8 - iowr I[$r3 + 0x000] $r2 - - // not sure what these are, route them because NVIDIA does, and - // the IRQ handler will signal the host if we ever get one.. we - // may find out if/why we need to handle these if so.. - // - mov $r2 0x2004 - iowr I[$r3 + 0x004] $r2 // { 0x04, ZERO } -> intr 9 - mov $r2 0x200b - iowr I[$r3 + 0x008] $r2 // { 0x0b, ZERO } -> intr 10 - mov $r2 0x200c - iowr I[$r3 + 0x01c] $r2 // { 0x0c, ZERO } -> intr 15 - - // enable all INTR_UP interrupts - mov $r2 0xc24 - shl b32 $r2 6 - not b32 $r3 $r0 - iowr I[$r2] $r3 - - // enable fifo, ctxsw, 9, 10, 15 interrupts - mov $r2 -0x78fc // 0x8704 - sethi $r2 0 - iowr I[$r1 + 0x000] $r2 // INTR_EN_SET - - // fifo level triggered, rest edge - sub b32 $r1 0x100 - mov $r2 4 - iowr I[$r1] $r2 - - // enable interrupts - bset $flags ie0 - - // fetch enabled GPC/ROP counts - mov $r14 -0x69fc // 0x409604 - sethi $r14 0x400000 - call #nv_rd32 - extr $r1 $r15 16:20 - st b32 D[$r0 + #rop_count] $r1 - and $r15 0x1f - st b32 D[$r0 + #gpc_count] $r15 - - // set BAR_REQMASK to GPC mask - mov $r1 1 - shl b32 $r1 $r15 - sub b32 $r1 1 - mov $r2 0x40c - shl b32 $r2 6 - iowr I[$r2 + 0x000] $r1 - iowr I[$r2 + 0x100] $r1 - - // find context data for this chipset - mov $r2 0x800 - shl b32 $r2 6 - iord $r2 I[$r2 + 0x000] // CC_SCRATCH[0] - mov $r15 #chipsets - 8 - init_find_chipset: - add b32 $r15 8 - ld b32 $r3 D[$r15 + 0x00] - cmpu b32 $r3 $r2 - bra e #init_context - cmpu b32 $r3 0 - bra ne #init_find_chipset - // unknown chipset - ret - - // context size calculation, reserve first 256 bytes for use by fuc - init_context: - mov $r1 256 - - // calculate size of mmio context data - ld b16 $r14 D[$r15 + 4] - ld b16 $r15 D[$r15 + 6] - sethi $r14 0 - st b32 D[$r0 + #hub_mmio_list_head] $r14 - st b32 D[$r0 + #hub_mmio_list_tail] $r15 - call #mmctx_size - - // set mmctx base addresses now so we don't have to do it later, - // they don't (currently) ever change - mov $r3 0x700 - shl b32 $r3 6 - shr b32 $r4 $r1 8 - iowr I[$r3 + 0x000] $r4 // MMCTX_SAVE_SWBASE - iowr I[$r3 + 0x100] $r4 // MMCTX_LOAD_SWBASE - add b32 $r3 0x1300 - add b32 $r1 $r15 - shr b32 $r15 2 - iowr I[$r3 + 0x000] $r15 // MMCTX_LOAD_COUNT, wtf for?!? - - // strands, base offset needs to be aligned to 256 bytes - shr b32 $r1 8 - add b32 $r1 1 - shl b32 $r1 8 - mov b32 $r15 $r1 - call #strand_ctx_init - add b32 $r1 $r15 - - // initialise each GPC in sequence by passing in the offset of its - // context data in GPCn_CC_SCRATCH[1], and starting its FUC (which - // has previously been uploaded by the host) running. - // - // the GPC fuc init sequence will set GPCn_CC_SCRATCH[0] bit 31 - // when it has completed, and return the size of its context data - // in GPCn_CC_SCRATCH[1] - // - ld b32 $r3 D[$r0 + #gpc_count] - mov $r4 0x2000 - sethi $r4 0x500000 - init_gpc: - // setup, and start GPC ucode running - add b32 $r14 $r4 0x804 - mov b32 $r15 $r1 - call #nv_wr32 // CC_SCRATCH[1] = ctx offset - add b32 $r14 $r4 0x800 - mov b32 $r15 $r2 - call #nv_wr32 // CC_SCRATCH[0] = chipset - add b32 $r14 $r4 0x10c - clear b32 $r15 - call #nv_wr32 - add b32 $r14 $r4 0x104 - call #nv_wr32 // ENTRY - add b32 $r14 $r4 0x100 - mov $r15 2 // CTRL_START_TRIGGER - call #nv_wr32 // CTRL - - // wait for it to complete, and adjust context size - add b32 $r14 $r4 0x800 - init_gpc_wait: - call #nv_rd32 - xbit $r15 $r15 31 - bra e #init_gpc_wait - add b32 $r14 $r4 0x804 - call #nv_rd32 - add b32 $r1 $r15 - - // next! - add b32 $r4 0x8000 - sub b32 $r3 1 - bra ne #init_gpc - - // save context size, and tell host we're ready - mov $r2 0x800 - shl b32 $r2 6 - iowr I[$r2 + 0x100] $r1 // CC_SCRATCH[1] = context size - add b32 $r2 0x800 - clear b32 $r1 - bset $r1 31 - iowr I[$r2 + 0x000] $r1 // CC_SCRATCH[0] |= 0x80000000 - -// Main program loop, very simple, sleeps until woken up by the interrupt -// handler, pulls a command from the queue and executes its handler -// -main: - // sleep until we have something to do - bset $flags $p0 - sleep $p0 - mov $r13 #cmd_queue - call #queue_get - bra $p1 #main - - // context switch, requested by GPU? - cmpu b32 $r14 0x4001 - bra ne #main_not_ctx_switch - trace_set(T_AUTO) - mov $r1 0xb00 - shl b32 $r1 6 - iord $r2 I[$r1 + 0x100] // CHAN_NEXT - iord $r1 I[$r1 + 0x000] // CHAN_CUR - - xbit $r3 $r1 31 - bra e #chsw_no_prev - xbit $r3 $r2 31 - bra e #chsw_prev_no_next - push $r2 - mov b32 $r2 $r1 - trace_set(T_SAVE) - bclr $flags $p1 - bset $flags $p2 - call #ctx_xfer - trace_clr(T_SAVE); - pop $r2 - trace_set(T_LOAD); - bset $flags $p1 - call #ctx_xfer - trace_clr(T_LOAD); - bra #chsw_done - chsw_prev_no_next: - push $r2 - mov b32 $r2 $r1 - bclr $flags $p1 - bclr $flags $p2 - call #ctx_xfer - pop $r2 - mov $r1 0xb00 - shl b32 $r1 6 - iowr I[$r1] $r2 - bra #chsw_done - chsw_no_prev: - xbit $r3 $r2 31 - bra e #chsw_done - bset $flags $p1 - bclr $flags $p2 - call #ctx_xfer - - // ack the context switch request - chsw_done: - mov $r1 0xb0c - shl b32 $r1 6 - mov $r2 1 - iowr I[$r1 + 0x000] $r2 // 0x409b0c - trace_clr(T_AUTO) - bra #main - - // request to set current channel? (*not* a context switch) - main_not_ctx_switch: - cmpu b32 $r14 0x0001 - bra ne #main_not_ctx_chan - mov b32 $r2 $r15 - call #ctx_chan - bra #main_done - - // request to store current channel context? - main_not_ctx_chan: - cmpu b32 $r14 0x0002 - bra ne #main_not_ctx_save - trace_set(T_SAVE) - bclr $flags $p1 - bclr $flags $p2 - call #ctx_xfer - trace_clr(T_SAVE) - bra #main_done - - main_not_ctx_save: - shl b32 $r15 $r14 16 - or $r15 E_BAD_COMMAND - call #error - bra #main - - main_done: - mov $r1 0x820 - shl b32 $r1 6 - clear b32 $r2 - bset $r2 31 - iowr I[$r1 + 0x000] $r2 // CC_SCRATCH[0] |= 0x80000000 - bra #main - -// interrupt handler -ih: - push $r8 - mov $r8 $flags - push $r8 - push $r9 - push $r10 - push $r11 - push $r13 - push $r14 - push $r15 - - // incoming fifo command? - iord $r10 I[$r0 + 0x200] // INTR - and $r11 $r10 0x00000004 - bra e #ih_no_fifo - // queue incoming fifo command for later processing - mov $r11 0x1900 - mov $r13 #cmd_queue - iord $r14 I[$r11 + 0x100] // FIFO_CMD - iord $r15 I[$r11 + 0x000] // FIFO_DATA - call #queue_put - add b32 $r11 0x400 - mov $r14 1 - iowr I[$r11 + 0x000] $r14 // FIFO_ACK - - // context switch request? - ih_no_fifo: - and $r11 $r10 0x00000100 - bra e #ih_no_ctxsw - // enqueue a context switch for later processing - mov $r13 #cmd_queue - mov $r14 0x4001 - call #queue_put - - // anything we didn't handle, bring it to the host's attention - ih_no_ctxsw: - mov $r11 0x104 - not b32 $r11 - and $r11 $r10 $r11 - bra e #ih_no_other - mov $r10 0xc1c - shl b32 $r10 6 - iowr I[$r10] $r11 // INTR_UP_SET - - // ack, and wake up main() - ih_no_other: - iowr I[$r0 + 0x100] $r10 // INTR_ACK - - pop $r15 - pop $r14 - pop $r13 - pop $r11 - pop $r10 - pop $r9 - pop $r8 - mov $flags $r8 - pop $r8 - bclr $flags $p0 - iret - -// Again, not real sure -// -// In: $r15 value to set 0x404170 to -// -ctx_4170s: - mov $r14 0x4170 - sethi $r14 0x400000 - or $r15 0x10 - call #nv_wr32 - ret - -// Waits for a ctx_4170s() call to complete -// -ctx_4170w: - mov $r14 0x4170 - sethi $r14 0x400000 - call #nv_rd32 - and $r15 0x10 - bra ne #ctx_4170w - ret - -// Disables various things, waits a bit, and re-enables them.. -// -// Not sure how exactly this helps, perhaps "ENABLE" is not such a -// good description for the bits we turn off? Anyways, without this, -// funny things happen. -// -ctx_redswitch: - mov $r14 0x614 - shl b32 $r14 6 - mov $r15 0x270 - iowr I[$r14] $r15 // HUB_RED_SWITCH = ENABLE_GPC, POWER_ALL - mov $r15 8 - ctx_redswitch_delay: - sub b32 $r15 1 - bra ne #ctx_redswitch_delay - mov $r15 0x770 - iowr I[$r14] $r15 // HUB_RED_SWITCH = ENABLE_ALL, POWER_ALL - ret - -// Not a clue what this is for, except that unless the value is 0x10, the -// strand context is saved (and presumably restored) incorrectly.. -// -// In: $r15 value to set to (0x00/0x10 are used) -// -ctx_86c: - mov $r14 0x86c - shl b32 $r14 6 - iowr I[$r14] $r15 // HUB(0x86c) = val - mov $r14 -0x75ec - sethi $r14 0x400000 - call #nv_wr32 // ROP(0xa14) = val - mov $r14 -0x5794 - sethi $r14 0x410000 - call #nv_wr32 // GPC(0x86c) = val - ret - -// ctx_load - load's a channel's ctxctl data, and selects its vm -// -// In: $r2 channel address -// -ctx_load: - trace_set(T_CHAN) - - // switch to channel, somewhat magic in parts.. - mov $r10 12 // DONE_UNK12 - call #wait_donez - mov $r1 0xa24 - shl b32 $r1 6 - iowr I[$r1 + 0x000] $r0 // 0x409a24 - mov $r3 0xb00 - shl b32 $r3 6 - iowr I[$r3 + 0x100] $r2 // CHAN_NEXT - mov $r1 0xa0c - shl b32 $r1 6 - mov $r4 7 - iowr I[$r1 + 0x000] $r2 // MEM_CHAN - iowr I[$r1 + 0x100] $r4 // MEM_CMD - ctx_chan_wait_0: - iord $r4 I[$r1 + 0x100] - and $r4 0x1f - bra ne #ctx_chan_wait_0 - iowr I[$r3 + 0x000] $r2 // CHAN_CUR - - // load channel header, fetch PGRAPH context pointer - mov $xtargets $r0 - bclr $r2 31 - shl b32 $r2 4 - add b32 $r2 2 - - trace_set(T_LCHAN) - mov $r1 0xa04 - shl b32 $r1 6 - iowr I[$r1 + 0x000] $r2 // MEM_BASE - mov $r1 0xa20 - shl b32 $r1 6 - mov $r2 0x0002 - sethi $r2 0x80000000 - iowr I[$r1 + 0x000] $r2 // MEM_TARGET = vram - mov $r1 0x10 // chan + 0x0210 - mov $r2 #xfer_data - sethi $r2 0x00020000 // 16 bytes - xdld $r1 $r2 - xdwait - trace_clr(T_LCHAN) - - // update current context - ld b32 $r1 D[$r0 + #xfer_data + 4] - shl b32 $r1 24 - ld b32 $r2 D[$r0 + #xfer_data + 0] - shr b32 $r2 8 - or $r1 $r2 - st b32 D[$r0 + #ctx_current] $r1 - - // set transfer base to start of context, and fetch context header - trace_set(T_LCTXH) - mov $r2 0xa04 - shl b32 $r2 6 - iowr I[$r2 + 0x000] $r1 // MEM_BASE - mov $r2 1 - mov $r1 0xa20 - shl b32 $r1 6 - iowr I[$r1 + 0x000] $r2 // MEM_TARGET = vm - mov $r1 #chan_data - sethi $r1 0x00060000 // 256 bytes - xdld $r0 $r1 - xdwait - trace_clr(T_LCTXH) - - trace_clr(T_CHAN) - ret - -// ctx_chan - handler for HUB_SET_CHAN command, will set a channel as -// the active channel for ctxctl, but not actually transfer -// any context data. intended for use only during initial -// context construction. -// -// In: $r2 channel address -// -ctx_chan: - call #ctx_load - mov $r10 12 // DONE_UNK12 - call #wait_donez - mov $r1 0xa10 - shl b32 $r1 6 - mov $r2 5 - iowr I[$r1 + 0x000] $r2 // MEM_CMD = 5 (???) - ctx_chan_wait: - iord $r2 I[$r1 + 0x000] - or $r2 $r2 - bra ne #ctx_chan_wait - ret - -// Execute per-context state overrides list -// -// Only executed on the first load of a channel. Might want to look into -// removing this and having the host directly modify the channel's context -// to change this state... The nouveau DRM already builds this list as -// it's definitely needed for NVIDIA's, so we may as well use it for now -// -// Input: $r1 mmio list length -// -ctx_mmio_exec: - // set transfer base to be the mmio list - ld b32 $r3 D[$r0 + #chan_mmio_address] - mov $r2 0xa04 - shl b32 $r2 6 - iowr I[$r2 + 0x000] $r3 // MEM_BASE - - clear b32 $r3 - ctx_mmio_loop: - // fetch next 256 bytes of mmio list if necessary - and $r4 $r3 0xff - bra ne #ctx_mmio_pull - mov $r5 #xfer_data - sethi $r5 0x00060000 // 256 bytes - xdld $r3 $r5 - xdwait - - // execute a single list entry - ctx_mmio_pull: - ld b32 $r14 D[$r4 + #xfer_data + 0x00] - ld b32 $r15 D[$r4 + #xfer_data + 0x04] - call #nv_wr32 - - // next! - add b32 $r3 8 - sub b32 $r1 1 - bra ne #ctx_mmio_loop - - // set transfer base back to the current context - ctx_mmio_done: - ld b32 $r3 D[$r0 + #ctx_current] - iowr I[$r2 + 0x000] $r3 // MEM_BASE - - // disable the mmio list now, we don't need/want to execute it again - st b32 D[$r0 + #chan_mmio_count] $r0 - mov $r1 #chan_data - sethi $r1 0x00060000 // 256 bytes - xdst $r0 $r1 - xdwait - ret - -// Transfer HUB context data between GPU and storage area -// -// In: $r2 channel address -// $p1 clear on save, set on load -// $p2 set if opposite direction done/will be done, so: -// on save it means: "a load will follow this save" -// on load it means: "a save preceeded this load" -// -ctx_xfer: - // according to mwk, some kind of wait for idle - mov $r15 0xc00 - shl b32 $r15 6 - mov $r14 4 - iowr I[$r15 + 0x200] $r14 - ctx_xfer_idle: - iord $r14 I[$r15 + 0x000] - and $r14 0x2000 - bra ne #ctx_xfer_idle - - bra not $p1 #ctx_xfer_pre - bra $p2 #ctx_xfer_pre_load - ctx_xfer_pre: - mov $r15 0x10 - call #ctx_86c - bra not $p1 #ctx_xfer_exec - - ctx_xfer_pre_load: - mov $r15 2 - call #ctx_4170s - call #ctx_4170w - call #ctx_redswitch - clear b32 $r15 - call #ctx_4170s - call #ctx_load - - // fetch context pointer, and initiate xfer on all GPCs - ctx_xfer_exec: - ld b32 $r1 D[$r0 + #ctx_current] - mov $r2 0x414 - shl b32 $r2 6 - iowr I[$r2 + 0x000] $r0 // BAR_STATUS = reset - mov $r14 -0x5b00 - sethi $r14 0x410000 - mov b32 $r15 $r1 - call #nv_wr32 // GPC_BCAST_WRCMD_DATA = ctx pointer - add b32 $r14 4 - xbit $r15 $flags $p1 - xbit $r2 $flags $p2 - shl b32 $r2 1 - or $r15 $r2 - call #nv_wr32 // GPC_BCAST_WRCMD_CMD = GPC_XFER(type) - - // strands - mov $r1 0x4afc - sethi $r1 0x20000 - mov $r2 0xc - iowr I[$r1] $r2 // STRAND_CMD(0x3f) = 0x0c - call #strand_wait - mov $r2 0x47fc - sethi $r2 0x20000 - iowr I[$r2] $r0 // STRAND_FIRST_GENE(0x3f) = 0x00 - xbit $r2 $flags $p1 - add b32 $r2 3 - iowr I[$r1] $r2 // STRAND_CMD(0x3f) = 0x03/0x04 (SAVE/LOAD) - - // mmio context - xbit $r10 $flags $p1 // direction - or $r10 6 // first, last - mov $r11 0 // base = 0 - ld b32 $r12 D[$r0 + #hub_mmio_list_head] - ld b32 $r13 D[$r0 + #hub_mmio_list_tail] - mov $r14 0 // not multi - call #mmctx_xfer - - // wait for GPCs to all complete - mov $r10 8 // DONE_BAR - call #wait_doneo - - // wait for strand xfer to complete - call #strand_wait - - // post-op - bra $p1 #ctx_xfer_post - mov $r10 12 // DONE_UNK12 - call #wait_donez - mov $r1 0xa10 - shl b32 $r1 6 - mov $r2 5 - iowr I[$r1] $r2 // MEM_CMD - ctx_xfer_post_save_wait: - iord $r2 I[$r1] - or $r2 $r2 - bra ne #ctx_xfer_post_save_wait - - bra $p2 #ctx_xfer_done - ctx_xfer_post: - mov $r15 2 - call #ctx_4170s - clear b32 $r15 - call #ctx_86c - call #strand_post - call #ctx_4170w - clear b32 $r15 - call #ctx_4170s - - bra not $p1 #ctx_xfer_no_post_mmio - ld b32 $r1 D[$r0 + #chan_mmio_count] - or $r1 $r1 - bra e #ctx_xfer_no_post_mmio - call #ctx_mmio_exec - - ctx_xfer_no_post_mmio: - - ctx_xfer_done: - ret - +#include "com.fuc" +#include "hub.fuc" .align 256 +#undef INCLUDE_CODE diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc.h index f22422e09045..623e8698ace1 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc.h @@ -388,7 +388,7 @@ uint32_t nve0_grhub_code[] = { 0x0089d000, 0x081887f1, 0xd00684b6, -/* 0x00e2: wait_done_wait_donez */ +/* 0x00e2: wait_donez_ne */ 0x87f1008a, 0x84b60400, 0x0088cf06, @@ -405,7 +405,7 @@ uint32_t nve0_grhub_code[] = { 0x87f10089, 0x84b60818, 0x008ad006, -/* 0x011c: wait_done_wait_doneo */ +/* 0x011c: wait_doneo_e */ 0x040087f1, 0xcf0684b6, 0x8aff0088, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/macros.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/macros.fuc new file mode 100644 index 000000000000..43a0b9476efd --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/macros.fuc @@ -0,0 +1,53 @@ +/* + * Copyright 2013 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 "os.h" + +#define mmctx_data(r,c) .b32 (((c - 1) << 26) | r) +#define queue_init .skip 72 // (2 * 4) + ((8 * 4) * 2) + +#define T_WAIT 0 +#define T_MMCTX 1 +#define T_STRWAIT 2 +#define T_STRINIT 3 +#define T_AUTO 4 +#define T_CHAN 5 +#define T_LOAD 6 +#define T_SAVE 7 +#define T_LCHAN 8 +#define T_LCTXH 9 + +#define trace_set(bit) /* +*/ mov $r8 0x83c /* +*/ shl b32 $r8 6 /* +*/ clear b32 $r9 /* +*/ bset $r9 bit /* +*/ iowr I[$r8 + 0x000] $r9 + +#define trace_clr(bit) /* +*/ mov $r8 0x85c /* +*/ shl b32 $r8 6 /* +*/ clear b32 $r9 /* +*/ bset $r9 bit /* +*/ iowr I[$r8 + 0x000] $r9 diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/nvc0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/nvc0.fuc deleted file mode 100644 index e6b228844a32..000000000000 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/nvc0.fuc +++ /dev/null @@ -1,400 +0,0 @@ -/* fuc microcode util functions for nvc0 PGRAPH - * - * Copyright 2011 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 - */ - -define(`mmctx_data', `.b32 eval((($2 - 1) << 26) | $1)') -define(`queue_init', `.skip eval((2 * 4) + ((8 * 4) * 2))') - -ifdef(`include_code', ` -// Error codes -define(`E_BAD_COMMAND', 0x01) -define(`E_CMD_OVERFLOW', 0x02) - -// Util macros to help with debugging ucode hangs etc -define(`T_WAIT', 0) -define(`T_MMCTX', 1) -define(`T_STRWAIT', 2) -define(`T_STRINIT', 3) -define(`T_AUTO', 4) -define(`T_CHAN', 5) -define(`T_LOAD', 6) -define(`T_SAVE', 7) -define(`T_LCHAN', 8) -define(`T_LCTXH', 9) - -define(`trace_set', ` - mov $r8 0x83c - shl b32 $r8 6 - clear b32 $r9 - bset $r9 $1 - iowr I[$r8 + 0x000] $r9 // CC_SCRATCH[7] -') - -define(`trace_clr', ` - mov $r8 0x85c - shl b32 $r8 6 - clear b32 $r9 - bset $r9 $1 - iowr I[$r8 + 0x000] $r9 // CC_SCRATCH[7] -') - -// queue_put - add request to queue -// -// In : $r13 queue pointer -// $r14 command -// $r15 data -// -queue_put: - // make sure we have space.. - ld b32 $r8 D[$r13 + 0x0] // GET - ld b32 $r9 D[$r13 + 0x4] // PUT - xor $r8 8 - cmpu b32 $r8 $r9 - bra ne #queue_put_next - mov $r15 E_CMD_OVERFLOW - call #error - ret - - // store cmd/data on queue - queue_put_next: - and $r8 $r9 7 - shl b32 $r8 3 - add b32 $r8 $r13 - add b32 $r8 8 - st b32 D[$r8 + 0x0] $r14 - st b32 D[$r8 + 0x4] $r15 - - // update PUT - add b32 $r9 1 - and $r9 0xf - st b32 D[$r13 + 0x4] $r9 - ret - -// queue_get - fetch request from queue -// -// In : $r13 queue pointer -// -// Out: $p1 clear on success (data available) -// $r14 command -// $r15 data -// -queue_get: - bset $flags $p1 - ld b32 $r8 D[$r13 + 0x0] // GET - ld b32 $r9 D[$r13 + 0x4] // PUT - cmpu b32 $r8 $r9 - bra e #queue_get_done - // fetch first cmd/data pair - and $r9 $r8 7 - shl b32 $r9 3 - add b32 $r9 $r13 - add b32 $r9 8 - ld b32 $r14 D[$r9 + 0x0] - ld b32 $r15 D[$r9 + 0x4] - - // update GET - add b32 $r8 1 - and $r8 0xf - st b32 D[$r13 + 0x0] $r8 - bclr $flags $p1 -queue_get_done: - ret - -// nv_rd32 - read 32-bit value from nv register -// -// In : $r14 register -// Out: $r15 value -// -nv_rd32: - mov $r11 0x728 - shl b32 $r11 6 - mov b32 $r12 $r14 - bset $r12 31 // MMIO_CTRL_PENDING - iowr I[$r11 + 0x000] $r12 // MMIO_CTRL - nv_rd32_wait: - iord $r12 I[$r11 + 0x000] - xbit $r12 $r12 31 - bra ne #nv_rd32_wait - mov $r10 6 // DONE_MMIO_RD - call #wait_doneo - iord $r15 I[$r11 + 0x100] // MMIO_RDVAL - ret - -// nv_wr32 - write 32-bit value to nv register -// -// In : $r14 register -// $r15 value -// -nv_wr32: - mov $r11 0x728 - shl b32 $r11 6 - iowr I[$r11 + 0x200] $r15 // MMIO_WRVAL - mov b32 $r12 $r14 - bset $r12 31 // MMIO_CTRL_PENDING - bset $r12 30 // MMIO_CTRL_WRITE - iowr I[$r11 + 0x000] $r12 // MMIO_CTRL - nv_wr32_wait: - iord $r12 I[$r11 + 0x000] - xbit $r12 $r12 31 - bra ne #nv_wr32_wait - ret - -// (re)set watchdog timer -// -// In : $r15 timeout -// -watchdog_reset: - mov $r8 0x430 - shl b32 $r8 6 - bset $r15 31 - iowr I[$r8 + 0x000] $r15 - ret - -// clear watchdog timer -watchdog_clear: - mov $r8 0x430 - shl b32 $r8 6 - iowr I[$r8 + 0x000] $r0 - ret - -// wait_done{z,o} - wait on FUC_DONE bit to become clear/set -// -// In : $r10 bit to wait on -// -define(`wait_done', ` -$1: - trace_set(T_WAIT); - mov $r8 0x818 - shl b32 $r8 6 - iowr I[$r8 + 0x000] $r10 // CC_SCRATCH[6] = wait bit - wait_done_$1: - mov $r8 0x400 - shl b32 $r8 6 - iord $r8 I[$r8 + 0x000] // DONE - xbit $r8 $r8 $r10 - bra $2 #wait_done_$1 - trace_clr(T_WAIT) - ret -') -wait_done(wait_donez, ne) -wait_done(wait_doneo, e) - -// mmctx_size - determine size of a mmio list transfer -// -// In : $r14 mmio list head -// $r15 mmio list tail -// Out: $r15 transfer size (in bytes) -// -mmctx_size: - clear b32 $r9 - nv_mmctx_size_loop: - ld b32 $r8 D[$r14] - shr b32 $r8 26 - add b32 $r8 1 - shl b32 $r8 2 - add b32 $r9 $r8 - add b32 $r14 4 - cmpu b32 $r14 $r15 - bra ne #nv_mmctx_size_loop - mov b32 $r15 $r9 - ret - -// mmctx_xfer - execute a list of mmio transfers -// -// In : $r10 flags -// bit 0: direction (0 = save, 1 = load) -// bit 1: set if first transfer -// bit 2: set if last transfer -// $r11 base -// $r12 mmio list head -// $r13 mmio list tail -// $r14 multi_stride -// $r15 multi_mask -// -mmctx_xfer: - trace_set(T_MMCTX) - mov $r8 0x710 - shl b32 $r8 6 - clear b32 $r9 - or $r11 $r11 - bra e #mmctx_base_disabled - iowr I[$r8 + 0x000] $r11 // MMCTX_BASE - bset $r9 0 // BASE_EN - mmctx_base_disabled: - or $r14 $r14 - bra e #mmctx_multi_disabled - iowr I[$r8 + 0x200] $r14 // MMCTX_MULTI_STRIDE - iowr I[$r8 + 0x300] $r15 // MMCTX_MULTI_MASK - bset $r9 1 // MULTI_EN - mmctx_multi_disabled: - add b32 $r8 0x100 - - xbit $r11 $r10 0 - shl b32 $r11 16 // DIR - bset $r11 12 // QLIMIT = 0x10 - xbit $r14 $r10 1 - shl b32 $r14 17 - or $r11 $r14 // START_TRIGGER - iowr I[$r8 + 0x000] $r11 // MMCTX_CTRL - - // loop over the mmio list, and send requests to the hw - mmctx_exec_loop: - // wait for space in mmctx queue - mmctx_wait_free: - iord $r14 I[$r8 + 0x000] // MMCTX_CTRL - and $r14 0x1f - bra e #mmctx_wait_free - - // queue up an entry - ld b32 $r14 D[$r12] - or $r14 $r9 - iowr I[$r8 + 0x300] $r14 - add b32 $r12 4 - cmpu b32 $r12 $r13 - bra ne #mmctx_exec_loop - - xbit $r11 $r10 2 - bra ne #mmctx_stop - // wait for queue to empty - mmctx_fini_wait: - iord $r11 I[$r8 + 0x000] // MMCTX_CTRL - and $r11 0x1f - cmpu b32 $r11 0x10 - bra ne #mmctx_fini_wait - mov $r10 2 // DONE_MMCTX - call #wait_donez - bra #mmctx_done - mmctx_stop: - xbit $r11 $r10 0 - shl b32 $r11 16 // DIR - bset $r11 12 // QLIMIT = 0x10 - bset $r11 18 // STOP_TRIGGER - iowr I[$r8 + 0x000] $r11 // MMCTX_CTRL - mmctx_stop_wait: - // wait for STOP_TRIGGER to clear - iord $r11 I[$r8 + 0x000] // MMCTX_CTRL - xbit $r11 $r11 18 - bra ne #mmctx_stop_wait - mmctx_done: - trace_clr(T_MMCTX) - ret - -// Wait for DONE_STRAND -// -strand_wait: - push $r10 - mov $r10 2 - call #wait_donez - pop $r10 - ret - -// unknown - call before issuing strand commands -// -strand_pre: - mov $r8 0x4afc - sethi $r8 0x20000 - mov $r9 0xc - iowr I[$r8] $r9 - call #strand_wait - ret - -// unknown - call after issuing strand commands -// -strand_post: - mov $r8 0x4afc - sethi $r8 0x20000 - mov $r9 0xd - iowr I[$r8] $r9 - call #strand_wait - ret - -// Selects strand set?! -// -// In: $r14 id -// -strand_set: - mov $r10 0x4ffc - sethi $r10 0x20000 - sub b32 $r11 $r10 0x500 - mov $r12 0xf - iowr I[$r10 + 0x000] $r12 // 0x93c = 0xf - mov $r12 0xb - iowr I[$r11 + 0x000] $r12 // 0x928 = 0xb - call #strand_wait - iowr I[$r10 + 0x000] $r14 // 0x93c = - mov $r12 0xa - iowr I[$r11 + 0x000] $r12 // 0x928 = 0xa - call #strand_wait - ret - -// Initialise strand context data -// -// In : $r15 context base -// Out: $r15 context size (in bytes) -// -// Strandset(?) 3 hardcoded currently -// -strand_ctx_init: - trace_set(T_STRINIT) - call #strand_pre - mov $r14 3 - call #strand_set - mov $r10 0x46fc - sethi $r10 0x20000 - add b32 $r11 $r10 0x400 - iowr I[$r10 + 0x100] $r0 // STRAND_FIRST_GENE = 0 - mov $r12 1 - iowr I[$r11 + 0x000] $r12 // STRAND_CMD = LATCH_FIRST_GENE - call #strand_wait - sub b32 $r12 $r0 1 - iowr I[$r10 + 0x000] $r12 // STRAND_GENE_CNT = 0xffffffff - mov $r12 2 - iowr I[$r11 + 0x000] $r12 // STRAND_CMD = LATCH_GENE_CNT - call #strand_wait - call #strand_post - - // read the size of each strand, poke the context offset of - // each into STRAND_{SAVE,LOAD}_SWBASE now, no need to worry - // about it later then. - mov $r8 0x880 - shl b32 $r8 6 - iord $r9 I[$r8 + 0x000] // STRANDS - add b32 $r8 0x2200 - shr b32 $r14 $r15 8 - ctx_init_strand_loop: - iowr I[$r8 + 0x000] $r14 // STRAND_SAVE_SWBASE - iowr I[$r8 + 0x100] $r14 // STRAND_LOAD_SWBASE - iord $r10 I[$r8 + 0x200] // STRAND_SIZE - shr b32 $r10 6 - add b32 $r10 1 - add b32 $r14 $r10 - add b32 $r8 4 - sub b32 $r9 1 - bra ne #ctx_init_strand_loop - - shl b32 $r14 8 - sub b32 $r15 $r14 $r15 - trace_clr(T_STRINIT) - ret -') diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/nve0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/nve0.fuc deleted file mode 100644 index f16a5d53319d..000000000000 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/nve0.fuc +++ /dev/null @@ -1,400 +0,0 @@ -/* fuc microcode util functions for nve0 PGRAPH - * - * Copyright 2011 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 - */ - -define(`mmctx_data', `.b32 eval((($2 - 1) << 26) | $1)') -define(`queue_init', `.skip eval((2 * 4) + ((8 * 4) * 2))') - -ifdef(`include_code', ` -// Error codes -define(`E_BAD_COMMAND', 0x01) -define(`E_CMD_OVERFLOW', 0x02) - -// Util macros to help with debugging ucode hangs etc -define(`T_WAIT', 0) -define(`T_MMCTX', 1) -define(`T_STRWAIT', 2) -define(`T_STRINIT', 3) -define(`T_AUTO', 4) -define(`T_CHAN', 5) -define(`T_LOAD', 6) -define(`T_SAVE', 7) -define(`T_LCHAN', 8) -define(`T_LCTXH', 9) - -define(`trace_set', ` - mov $r8 0x83c - shl b32 $r8 6 - clear b32 $r9 - bset $r9 $1 - iowr I[$r8 + 0x000] $r9 // CC_SCRATCH[7] -') - -define(`trace_clr', ` - mov $r8 0x85c - shl b32 $r8 6 - clear b32 $r9 - bset $r9 $1 - iowr I[$r8 + 0x000] $r9 // CC_SCRATCH[7] -') - -// queue_put - add request to queue -// -// In : $r13 queue pointer -// $r14 command -// $r15 data -// -queue_put: - // make sure we have space.. - ld b32 $r8 D[$r13 + 0x0] // GET - ld b32 $r9 D[$r13 + 0x4] // PUT - xor $r8 8 - cmpu b32 $r8 $r9 - bra ne #queue_put_next - mov $r15 E_CMD_OVERFLOW - call #error - ret - - // store cmd/data on queue - queue_put_next: - and $r8 $r9 7 - shl b32 $r8 3 - add b32 $r8 $r13 - add b32 $r8 8 - st b32 D[$r8 + 0x0] $r14 - st b32 D[$r8 + 0x4] $r15 - - // update PUT - add b32 $r9 1 - and $r9 0xf - st b32 D[$r13 + 0x4] $r9 - ret - -// queue_get - fetch request from queue -// -// In : $r13 queue pointer -// -// Out: $p1 clear on success (data available) -// $r14 command -// $r15 data -// -queue_get: - bset $flags $p1 - ld b32 $r8 D[$r13 + 0x0] // GET - ld b32 $r9 D[$r13 + 0x4] // PUT - cmpu b32 $r8 $r9 - bra e #queue_get_done - // fetch first cmd/data pair - and $r9 $r8 7 - shl b32 $r9 3 - add b32 $r9 $r13 - add b32 $r9 8 - ld b32 $r14 D[$r9 + 0x0] - ld b32 $r15 D[$r9 + 0x4] - - // update GET - add b32 $r8 1 - and $r8 0xf - st b32 D[$r13 + 0x0] $r8 - bclr $flags $p1 -queue_get_done: - ret - -// nv_rd32 - read 32-bit value from nv register -// -// In : $r14 register -// Out: $r15 value -// -nv_rd32: - mov $r11 0x728 - shl b32 $r11 6 - mov b32 $r12 $r14 - bset $r12 31 // MMIO_CTRL_PENDING - iowr I[$r11 + 0x000] $r12 // MMIO_CTRL - nv_rd32_wait: - iord $r12 I[$r11 + 0x000] - xbit $r12 $r12 31 - bra ne #nv_rd32_wait - mov $r10 6 // DONE_MMIO_RD - call #wait_doneo - iord $r15 I[$r11 + 0x100] // MMIO_RDVAL - ret - -// nv_wr32 - write 32-bit value to nv register -// -// In : $r14 register -// $r15 value -// -nv_wr32: - mov $r11 0x728 - shl b32 $r11 6 - iowr I[$r11 + 0x200] $r15 // MMIO_WRVAL - mov b32 $r12 $r14 - bset $r12 31 // MMIO_CTRL_PENDING - bset $r12 30 // MMIO_CTRL_WRITE - iowr I[$r11 + 0x000] $r12 // MMIO_CTRL - nv_wr32_wait: - iord $r12 I[$r11 + 0x000] - xbit $r12 $r12 31 - bra ne #nv_wr32_wait - ret - -// (re)set watchdog timer -// -// In : $r15 timeout -// -watchdog_reset: - mov $r8 0x430 - shl b32 $r8 6 - bset $r15 31 - iowr I[$r8 + 0x000] $r15 - ret - -// clear watchdog timer -watchdog_clear: - mov $r8 0x430 - shl b32 $r8 6 - iowr I[$r8 + 0x000] $r0 - ret - -// wait_done{z,o} - wait on FUC_DONE bit to become clear/set -// -// In : $r10 bit to wait on -// -define(`wait_done', ` -$1: - trace_set(T_WAIT); - mov $r8 0x818 - shl b32 $r8 6 - iowr I[$r8 + 0x000] $r10 // CC_SCRATCH[6] = wait bit - wait_done_$1: - mov $r8 0x400 - shl b32 $r8 6 - iord $r8 I[$r8 + 0x000] // DONE - xbit $r8 $r8 $r10 - bra $2 #wait_done_$1 - trace_clr(T_WAIT) - ret -') -wait_done(wait_donez, ne) -wait_done(wait_doneo, e) - -// mmctx_size - determine size of a mmio list transfer -// -// In : $r14 mmio list head -// $r15 mmio list tail -// Out: $r15 transfer size (in bytes) -// -mmctx_size: - clear b32 $r9 - nv_mmctx_size_loop: - ld b32 $r8 D[$r14] - shr b32 $r8 26 - add b32 $r8 1 - shl b32 $r8 2 - add b32 $r9 $r8 - add b32 $r14 4 - cmpu b32 $r14 $r15 - bra ne #nv_mmctx_size_loop - mov b32 $r15 $r9 - ret - -// mmctx_xfer - execute a list of mmio transfers -// -// In : $r10 flags -// bit 0: direction (0 = save, 1 = load) -// bit 1: set if first transfer -// bit 2: set if last transfer -// $r11 base -// $r12 mmio list head -// $r13 mmio list tail -// $r14 multi_stride -// $r15 multi_mask -// -mmctx_xfer: - trace_set(T_MMCTX) - mov $r8 0x710 - shl b32 $r8 6 - clear b32 $r9 - or $r11 $r11 - bra e #mmctx_base_disabled - iowr I[$r8 + 0x000] $r11 // MMCTX_BASE - bset $r9 0 // BASE_EN - mmctx_base_disabled: - or $r14 $r14 - bra e #mmctx_multi_disabled - iowr I[$r8 + 0x200] $r14 // MMCTX_MULTI_STRIDE - iowr I[$r8 + 0x300] $r15 // MMCTX_MULTI_MASK - bset $r9 1 // MULTI_EN - mmctx_multi_disabled: - add b32 $r8 0x100 - - xbit $r11 $r10 0 - shl b32 $r11 16 // DIR - bset $r11 12 // QLIMIT = 0x10 - xbit $r14 $r10 1 - shl b32 $r14 17 - or $r11 $r14 // START_TRIGGER - iowr I[$r8 + 0x000] $r11 // MMCTX_CTRL - - // loop over the mmio list, and send requests to the hw - mmctx_exec_loop: - // wait for space in mmctx queue - mmctx_wait_free: - iord $r14 I[$r8 + 0x000] // MMCTX_CTRL - and $r14 0x1f - bra e #mmctx_wait_free - - // queue up an entry - ld b32 $r14 D[$r12] - or $r14 $r9 - iowr I[$r8 + 0x300] $r14 - add b32 $r12 4 - cmpu b32 $r12 $r13 - bra ne #mmctx_exec_loop - - xbit $r11 $r10 2 - bra ne #mmctx_stop - // wait for queue to empty - mmctx_fini_wait: - iord $r11 I[$r8 + 0x000] // MMCTX_CTRL - and $r11 0x1f - cmpu b32 $r11 0x10 - bra ne #mmctx_fini_wait - mov $r10 2 // DONE_MMCTX - call #wait_donez - bra #mmctx_done - mmctx_stop: - xbit $r11 $r10 0 - shl b32 $r11 16 // DIR - bset $r11 12 // QLIMIT = 0x10 - bset $r11 18 // STOP_TRIGGER - iowr I[$r8 + 0x000] $r11 // MMCTX_CTRL - mmctx_stop_wait: - // wait for STOP_TRIGGER to clear - iord $r11 I[$r8 + 0x000] // MMCTX_CTRL - xbit $r11 $r11 18 - bra ne #mmctx_stop_wait - mmctx_done: - trace_clr(T_MMCTX) - ret - -// Wait for DONE_STRAND -// -strand_wait: - push $r10 - mov $r10 2 - call #wait_donez - pop $r10 - ret - -// unknown - call before issuing strand commands -// -strand_pre: - mov $r8 0x4afc - sethi $r8 0x20000 - mov $r9 0xc - iowr I[$r8] $r9 - call #strand_wait - ret - -// unknown - call after issuing strand commands -// -strand_post: - mov $r8 0x4afc - sethi $r8 0x20000 - mov $r9 0xd - iowr I[$r8] $r9 - call #strand_wait - ret - -// Selects strand set?! -// -// In: $r14 id -// -strand_set: - mov $r10 0x4ffc - sethi $r10 0x20000 - sub b32 $r11 $r10 0x500 - mov $r12 0xf - iowr I[$r10 + 0x000] $r12 // 0x93c = 0xf - mov $r12 0xb - iowr I[$r11 + 0x000] $r12 // 0x928 = 0xb - call #strand_wait - iowr I[$r10 + 0x000] $r14 // 0x93c = - mov $r12 0xa - iowr I[$r11 + 0x000] $r12 // 0x928 = 0xa - call #strand_wait - ret - -// Initialise strand context data -// -// In : $r15 context base -// Out: $r15 context size (in bytes) -// -// Strandset(?) 3 hardcoded currently -// -strand_ctx_init: - trace_set(T_STRINIT) - call #strand_pre - mov $r14 3 - call #strand_set - mov $r10 0x46fc - sethi $r10 0x20000 - add b32 $r11 $r10 0x400 - iowr I[$r10 + 0x100] $r0 // STRAND_FIRST_GENE = 0 - mov $r12 1 - iowr I[$r11 + 0x000] $r12 // STRAND_CMD = LATCH_FIRST_GENE - call #strand_wait - sub b32 $r12 $r0 1 - iowr I[$r10 + 0x000] $r12 // STRAND_GENE_CNT = 0xffffffff - mov $r12 2 - iowr I[$r11 + 0x000] $r12 // STRAND_CMD = LATCH_GENE_CNT - call #strand_wait - call #strand_post - - // read the size of each strand, poke the context offset of - // each into STRAND_{SAVE,LOAD}_SWBASE now, no need to worry - // about it later then. - mov $r8 0x880 - shl b32 $r8 6 - iord $r9 I[$r8 + 0x000] // STRANDS - add b32 $r8 0x2200 - shr b32 $r14 $r15 8 - ctx_init_strand_loop: - iowr I[$r8 + 0x000] $r14 // STRAND_SAVE_SWBASE - iowr I[$r8 + 0x100] $r14 // STRAND_LOAD_SWBASE - iord $r10 I[$r8 + 0x200] // STRAND_SIZE - shr b32 $r10 6 - add b32 $r10 1 - add b32 $r14 $r10 - add b32 $r8 4 - sub b32 $r9 1 - bra ne #ctx_init_strand_loop - - shl b32 $r14 8 - sub b32 $r15 $r14 $r15 - trace_clr(T_STRINIT) - ret -') diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/os.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/os.h new file mode 100644 index 000000000000..fd1d380de094 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/os.h @@ -0,0 +1,7 @@ +#ifndef __NVKM_GRAPH_OS_H__ +#define __NVKM_GRAPH_OS_H__ + +#define E_BAD_COMMAND 0x00000001 +#define E_CMD_OVERFLOW 0x00000002 + +#endif -- cgit v1.2.3 From 791dc143ed2c441f5202d8721609d94dce9fcf88 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Thu, 27 Jun 2013 17:35:53 +1000 Subject: drm/nvd0-/disp: handle case where display engine is missing/disabled Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c | 3 +++ drivers/gpu/drm/nouveau/core/engine/disp/nve0.c | 3 +++ drivers/gpu/drm/nouveau/core/engine/disp/nvf0.c | 3 +++ drivers/gpu/drm/nouveau/core/subdev/bios/base.c | 6 +++++- drivers/gpu/drm/nouveau/core/subdev/devinit/nvc0.c | 2 ++ drivers/gpu/drm/nouveau/nouveau_bios.c | 7 +------ 6 files changed, 17 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c index 3ed10b00e81c..52dd7a1db729 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c @@ -958,6 +958,9 @@ nvd0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, int heads = nv_rd32(parent, 0x022448); int ret; + if (nv_rd32(parent, 0x022500) & 0x00000001) + return -ENODEV; + ret = nouveau_disp_create(parent, engine, oclass, heads, "PDISP", "display", &priv); *pobject = nv_object(priv); diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c index 20725b363d58..fb1fe6ae5e74 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c @@ -54,6 +54,9 @@ nve0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, int heads = nv_rd32(parent, 0x022448); int ret; + if (nv_rd32(parent, 0x022500) & 0x00000001) + return -ENODEV; + ret = nouveau_disp_create(parent, engine, oclass, heads, "PDISP", "display", &priv); *pobject = nv_object(priv); diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nvf0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nvf0.c index a488c36e40f9..42aa6b97dbea 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nvf0.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nvf0.c @@ -54,6 +54,9 @@ nvf0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, int heads = nv_rd32(parent, 0x022448); int ret; + if (nv_rd32(parent, 0x022500) & 0x00000001) + return -ENODEV; + ret = nouveau_disp_create(parent, engine, oclass, heads, "PDISP", "display", &priv); *pobject = nv_object(priv); diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/base.c b/drivers/gpu/drm/nouveau/core/subdev/bios/base.c index 0e2c1a4f1659..aa0fbbec7f08 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bios/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/base.c @@ -85,11 +85,15 @@ static void nouveau_bios_shadow_pramin(struct nouveau_bios *bios) { struct nouveau_device *device = nv_device(bios); + u64 addr = 0; u32 bar0 = 0; int i; if (device->card_type >= NV_50) { - u64 addr = (u64)(nv_rd32(bios, 0x619f04) & 0xffffff00) << 8; + if ( device->card_type < NV_C0 || + !(nv_rd32(bios, 0x022500) & 0x00000001)) + addr = (u64)(nv_rd32(bios, 0x619f04) & 0xffffff00) << 8; + if (!addr) { addr = (u64)nv_rd32(bios, 0x001700) << 16; addr += 0xf0000; diff --git a/drivers/gpu/drm/nouveau/core/subdev/devinit/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/devinit/nvc0.c index dd78efbcae1a..af407a8637c7 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/devinit/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/devinit/nvc0.c @@ -73,6 +73,8 @@ nvc0_devinit_ctor(struct nouveau_object *parent, struct nouveau_object *engine, return ret; priv->base.pll_set = nvc0_devinit_pll_set; + if (nv_rd32(priv, 0x022500) & 0x00000001) + priv->base.post = true; return 0; } diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index e09817df7934..3e7287675ecf 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -2051,19 +2051,14 @@ nouveau_bios_posted(struct drm_device *dev) struct nouveau_drm *drm = nouveau_drm(dev); unsigned htotal; - if (nv_device(drm->device)->card_type >= NV_50) { - if (NVReadVgaCrtc(dev, 0, 0x00) == 0 && - NVReadVgaCrtc(dev, 0, 0x1a) == 0) - return false; + if (nv_device(drm->device)->card_type >= NV_50) return true; - } htotal = NVReadVgaCrtc(dev, 0, 0x06); htotal |= (NVReadVgaCrtc(dev, 0, 0x07) & 0x01) << 8; htotal |= (NVReadVgaCrtc(dev, 0, 0x07) & 0x20) << 4; htotal |= (NVReadVgaCrtc(dev, 0, 0x25) & 0x01) << 10; htotal |= (NVReadVgaCrtc(dev, 0, 0x41) & 0x01) << 11; - return (htotal != 0); } -- cgit v1.2.3 From 9664877ed8956b81feb3bd6b3b2621b5fcdb624f Mon Sep 17 00:00:00 2001 From: Ferruh Yigit Date: Sun, 30 Jun 2013 18:46:56 -0700 Subject: Input: cyttsp - I2C driver split into two modules Existing I2C code is for TrueTouch Gen3 devices TrueTouch Gen4 device is using same protocol, will split driver into two pieces to use common code with both drivers. Read/Write functions parameter list modified, since shared code will be used by two separate drivers and these drivers are not sharing same structs, parameters updated to use common structures. Signed-off-by: Ferruh Yigit Acked-by: Greg Kroah-Hartman Acked-by: Javier Martinez Canillas Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Makefile | 2 +- drivers/input/touchscreen/cyttsp_core.c | 6 +- drivers/input/touchscreen/cyttsp_core.h | 11 +++- drivers/input/touchscreen/cyttsp_i2c.c | 50 +---------------- drivers/input/touchscreen/cyttsp_i2c_common.c | 79 +++++++++++++++++++++++++++ drivers/input/touchscreen/cyttsp_spi.c | 38 ++++++------- 6 files changed, 112 insertions(+), 74 deletions(-) create mode 100644 drivers/input/touchscreen/cyttsp_i2c_common.c diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 6bfbeab67c9f..2b378b12ee6b 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -18,7 +18,7 @@ obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR) += auo-pixcir-ts.o obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o -obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o +obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o cyttsp_i2c_common.o obj-$(CONFIG_TOUCHSCREEN_CYTTSP_SPI) += cyttsp_spi.o obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o obj-$(CONFIG_TOUCHSCREEN_DA9052) += da9052_tsi.o diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c index 8e60437ac85b..4824fa343897 100644 --- a/drivers/input/touchscreen/cyttsp_core.c +++ b/drivers/input/touchscreen/cyttsp_core.c @@ -84,7 +84,8 @@ static int ttsp_read_block_data(struct cyttsp *ts, u8 command, int tries; for (tries = 0; tries < CY_NUM_RETRY; tries++) { - error = ts->bus_ops->read(ts, command, length, buf); + error = ts->bus_ops->read(ts->dev, ts->xfer_buf, command, + length, buf); if (!error) return 0; @@ -101,7 +102,8 @@ static int ttsp_write_block_data(struct cyttsp *ts, u8 command, int tries; for (tries = 0; tries < CY_NUM_RETRY; tries++) { - error = ts->bus_ops->write(ts, command, length, buf); + error = ts->bus_ops->write(ts->dev, ts->xfer_buf, command, + length, buf); if (!error) return 0; diff --git a/drivers/input/touchscreen/cyttsp_core.h b/drivers/input/touchscreen/cyttsp_core.h index 1aa3c6967e70..d0c9e488b05d 100644 --- a/drivers/input/touchscreen/cyttsp_core.h +++ b/drivers/input/touchscreen/cyttsp_core.h @@ -112,9 +112,10 @@ struct cyttsp; struct cyttsp_bus_ops { u16 bustype; - int (*write)(struct cyttsp *ts, - u8 addr, u8 length, const void *values); - int (*read)(struct cyttsp *ts, u8 addr, u8 length, void *values); + int (*write)(struct device *dev, u8 *xfer_buf, u8 addr, u8 length, + const void *values); + int (*read)(struct device *dev, u8 *xfer_buf, u8 addr, u8 length, + void *values); }; enum cyttsp_state { @@ -144,6 +145,10 @@ struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops, struct device *dev, int irq, size_t xfer_buf_size); void cyttsp_remove(struct cyttsp *ts); +int cyttsp_i2c_write_block_data(struct device *dev, u8 *xfer_buf, u8 addr, + u8 length, const void *values); +int cyttsp_i2c_read_block_data(struct device *dev, u8 *xfer_buf, u8 addr, + u8 length, void *values); extern const struct dev_pm_ops cyttsp_pm_ops; #endif /* __CYTTSP_CORE_H__ */ diff --git a/drivers/input/touchscreen/cyttsp_i2c.c b/drivers/input/touchscreen/cyttsp_i2c.c index 4dbdf44b8fc5..63104a86a9bd 100644 --- a/drivers/input/touchscreen/cyttsp_i2c.c +++ b/drivers/input/touchscreen/cyttsp_i2c.c @@ -1,5 +1,5 @@ /* - * Source for: + * cyttsp_i2c.c * Cypress TrueTouch(TM) Standard Product (TTSP) I2C touchscreen driver. * For use with Cypress Txx3xx parts. * Supported parts include: @@ -19,11 +19,7 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Contact Cypress Semiconductor at www.cypress.com + * Contact Cypress Semiconductor at www.cypress.com * */ @@ -34,47 +30,6 @@ #define CY_I2C_DATA_SIZE 128 -static int cyttsp_i2c_read_block_data(struct cyttsp *ts, - u8 addr, u8 length, void *values) -{ - struct i2c_client *client = to_i2c_client(ts->dev); - struct i2c_msg msgs[] = { - { - .addr = client->addr, - .flags = 0, - .len = 1, - .buf = &addr, - }, - { - .addr = client->addr, - .flags = I2C_M_RD, - .len = length, - .buf = values, - }, - }; - int retval; - - retval = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); - if (retval < 0) - return retval; - - return retval != ARRAY_SIZE(msgs) ? -EIO : 0; -} - -static int cyttsp_i2c_write_block_data(struct cyttsp *ts, - u8 addr, u8 length, const void *values) -{ - struct i2c_client *client = to_i2c_client(ts->dev); - int retval; - - ts->xfer_buf[0] = addr; - memcpy(&ts->xfer_buf[1], values, length); - - retval = i2c_master_send(client, ts->xfer_buf, length + 1); - - return retval < 0 ? retval : 0; -} - static const struct cyttsp_bus_ops cyttsp_i2c_bus_ops = { .bustype = BUS_I2C, .write = cyttsp_i2c_write_block_data, @@ -98,7 +53,6 @@ static int cyttsp_i2c_probe(struct i2c_client *client, return PTR_ERR(ts); i2c_set_clientdata(client, ts); - return 0; } diff --git a/drivers/input/touchscreen/cyttsp_i2c_common.c b/drivers/input/touchscreen/cyttsp_i2c_common.c new file mode 100644 index 000000000000..07c553fbcef2 --- /dev/null +++ b/drivers/input/touchscreen/cyttsp_i2c_common.c @@ -0,0 +1,79 @@ +/* + * cyttsp_i2c_common.c + * Cypress TrueTouch(TM) Standard Product (TTSP) I2C touchscreen driver. + * For use with Cypress Txx3xx and Txx4xx parts. + * Supported parts include: + * CY8CTST341 + * CY8CTMA340 + * TMA4XX + * TMA1036 + * + * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. + * Copyright (C) 2012 Javier Martinez Canillas + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Contact Cypress Semiconductor at www.cypress.com + * + */ + +#include +#include +#include +#include +#include + +int cyttsp_i2c_read_block_data(struct device *dev, u8 *xfer_buf, + u8 addr, u8 length, void *values) +{ + struct i2c_client *client = to_i2c_client(dev); + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = &addr, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = values, + }, + }; + int retval; + + retval = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (retval < 0) + return retval; + + return retval != ARRAY_SIZE(msgs) ? -EIO : 0; +} +EXPORT_SYMBOL_GPL(cyttsp_i2c_read_block_data); + +int cyttsp_i2c_write_block_data(struct device *dev, u8 *xfer_buf, + u8 addr, u8 length, const void *values) +{ + struct i2c_client *client = to_i2c_client(dev); + int retval; + + xfer_buf[0] = addr; + memcpy(&xfer_buf[1], values, length); + + retval = i2c_master_send(client, xfer_buf, length + 1); + + return retval < 0 ? retval : 0; +} +EXPORT_SYMBOL_GPL(cyttsp_i2c_write_block_data); + + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Cypress"); diff --git a/drivers/input/touchscreen/cyttsp_spi.c b/drivers/input/touchscreen/cyttsp_spi.c index 861b7f77605b..1df625337b84 100644 --- a/drivers/input/touchscreen/cyttsp_spi.c +++ b/drivers/input/touchscreen/cyttsp_spi.c @@ -8,6 +8,7 @@ * * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. * Copyright (C) 2012 Javier Martinez Canillas + * Copyright (C) 2013 Cypress Semiconductor * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -19,11 +20,7 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Contact Cypress Semiconductor at www.cypress.com + * Contact Cypress Semiconductor at www.cypress.com * */ @@ -43,19 +40,19 @@ #define CY_SPI_DATA_BUF_SIZE (CY_SPI_CMD_BYTES + CY_SPI_DATA_SIZE) #define CY_SPI_BITS_PER_WORD 8 -static int cyttsp_spi_xfer(struct cyttsp *ts, +static int cyttsp_spi_xfer(struct device *dev, u8 *xfer_buf, u8 op, u8 reg, u8 *buf, int length) { - struct spi_device *spi = to_spi_device(ts->dev); + struct spi_device *spi = to_spi_device(dev); struct spi_message msg; struct spi_transfer xfer[2]; - u8 *wr_buf = &ts->xfer_buf[0]; - u8 *rd_buf = &ts->xfer_buf[CY_SPI_DATA_BUF_SIZE]; + u8 *wr_buf = &xfer_buf[0]; + u8 *rd_buf = &xfer_buf[CY_SPI_DATA_BUF_SIZE]; int retval; int i; if (length > CY_SPI_DATA_SIZE) { - dev_err(ts->dev, "%s: length %d is too big.\n", + dev_err(dev, "%s: length %d is too big.\n", __func__, length); return -EINVAL; } @@ -95,13 +92,13 @@ static int cyttsp_spi_xfer(struct cyttsp *ts, break; default: - dev_err(ts->dev, "%s: bad operation code=%d\n", __func__, op); + dev_err(dev, "%s: bad operation code=%d\n", __func__, op); return -EINVAL; } retval = spi_sync(spi, &msg); if (retval < 0) { - dev_dbg(ts->dev, "%s: spi_sync() error %d, len=%d, op=%d\n", + dev_dbg(dev, "%s: spi_sync() error %d, len=%d, op=%d\n", __func__, retval, xfer[1].len, op); /* @@ -113,14 +110,13 @@ static int cyttsp_spi_xfer(struct cyttsp *ts, if (rd_buf[CY_SPI_SYNC_BYTE] != CY_SPI_SYNC_ACK1 || rd_buf[CY_SPI_SYNC_BYTE + 1] != CY_SPI_SYNC_ACK2) { - - dev_dbg(ts->dev, "%s: operation %d failed\n", __func__, op); + dev_dbg(dev, "%s: operation %d failed\n", __func__, op); for (i = 0; i < CY_SPI_CMD_BYTES; i++) - dev_dbg(ts->dev, "%s: test rd_buf[%d]:0x%02x\n", + dev_dbg(dev, "%s: test rd_buf[%d]:0x%02x\n", __func__, i, rd_buf[i]); for (i = 0; i < length; i++) - dev_dbg(ts->dev, "%s: test buf[%d]:0x%02x\n", + dev_dbg(dev, "%s: test buf[%d]:0x%02x\n", __func__, i, buf[i]); return -EIO; @@ -129,16 +125,18 @@ static int cyttsp_spi_xfer(struct cyttsp *ts, return 0; } -static int cyttsp_spi_read_block_data(struct cyttsp *ts, +static int cyttsp_spi_read_block_data(struct device *dev, u8 *xfer_buf, u8 addr, u8 length, void *data) { - return cyttsp_spi_xfer(ts, CY_SPI_RD_OP, addr, data, length); + return cyttsp_spi_xfer(dev, xfer_buf, CY_SPI_RD_OP, addr, data, + length); } -static int cyttsp_spi_write_block_data(struct cyttsp *ts, +static int cyttsp_spi_write_block_data(struct device *dev, u8 *xfer_buf, u8 addr, u8 length, const void *data) { - return cyttsp_spi_xfer(ts, CY_SPI_WR_OP, addr, (void *)data, length); + return cyttsp_spi_xfer(dev, xfer_buf, CY_SPI_WR_OP, addr, (void *)data, + length); } static const struct cyttsp_bus_ops cyttsp_spi_bus_ops = { -- cgit v1.2.3 From 17fb1563d69b63fe7a79570fe870cf7e530cd2cd Mon Sep 17 00:00:00 2001 From: Ferruh Yigit Date: Sun, 30 Jun 2013 18:49:02 -0700 Subject: Input: cyttsp4 - add core driver for Cypress TMA4XX touchscreen devices Cypress TrueTouch(tm) Standard Product controllers, Generetion4 devices, Core driver. Core driver is interface between host and TTSP controller and processes data sent by controller. Responsibilities of module are IRQ handling, reading system information registers and sending multi-touch protocol type B events. Signed-off-by: Ferruh Yigit Acked-by: Greg Kroah-Hartman Acked-by: Javier Martinez Canillas Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 12 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/cyttsp4_core.c | 2169 ++++++++++++++++++++++++++++++ drivers/input/touchscreen/cyttsp4_core.h | 472 +++++++ include/linux/platform_data/cyttsp4.h | 76 ++ 5 files changed, 2730 insertions(+) create mode 100644 drivers/input/touchscreen/cyttsp4_core.c create mode 100644 drivers/input/touchscreen/cyttsp4_core.h create mode 100644 include/linux/platform_data/cyttsp4.h diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index f9a5fd89bc02..66df8df88140 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -167,6 +167,18 @@ config TOUCHSCREEN_CYTTSP_SPI To compile this driver as a module, choose M here: the module will be called cyttsp_spi. +config TOUCHSCREEN_CYTTSP4_CORE + tristate "Cypress TrueTouch Gen4 Touchscreen Driver" + help + Core driver for Cypress TrueTouch(tm) Standard Product + Generation4 touchscreen controllers. + + Say Y here if you have a Cypress Gen4 touchscreen. + + If unsure, say N. + + To compile this driver as a module, choose M here. + config TOUCHSCREEN_DA9034 tristate "Touchscreen support for Dialog Semiconductor DA9034" depends on PMIC_DA903X diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 2b378b12ee6b..f44500674c32 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o cyttsp_i2c_common.o obj-$(CONFIG_TOUCHSCREEN_CYTTSP_SPI) += cyttsp_spi.o +obj-$(CONFIG_TOUCHSCREEN_CYTTSP4_CORE) += cyttsp4_core.o obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o obj-$(CONFIG_TOUCHSCREEN_DA9052) += da9052_tsi.o obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o diff --git a/drivers/input/touchscreen/cyttsp4_core.c b/drivers/input/touchscreen/cyttsp4_core.c new file mode 100644 index 000000000000..963da052c15e --- /dev/null +++ b/drivers/input/touchscreen/cyttsp4_core.c @@ -0,0 +1,2169 @@ +/* + * cyttsp4_core.c + * Cypress TrueTouch(TM) Standard Product V4 Core driver module. + * For use with Cypress Txx4xx parts. + * Supported parts include: + * TMA4XX + * TMA1036 + * + * Copyright (C) 2012 Cypress Semiconductor + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Contact Cypress Semiconductor at www.cypress.com + * + */ + +#include "cyttsp4_core.h" +#include +#include +#include +#include +#include +#include +#include + +/* Timeout in ms. */ +#define CY_CORE_REQUEST_EXCLUSIVE_TIMEOUT 500 +#define CY_CORE_SLEEP_REQUEST_EXCLUSIVE_TIMEOUT 5000 +#define CY_CORE_MODE_CHANGE_TIMEOUT 1000 +#define CY_CORE_RESET_AND_WAIT_TIMEOUT 500 +#define CY_CORE_WAKEUP_TIMEOUT 500 + +#define CY_CORE_STARTUP_RETRY_COUNT 3 + +static const u8 ldr_exit[] = { + 0xFF, 0x01, 0x3B, 0x00, 0x00, 0x4F, 0x6D, 0x17 +}; + +static const u8 ldr_err_app[] = { + 0x01, 0x02, 0x00, 0x00, 0x55, 0xDD, 0x17 +}; + +static inline size_t merge_bytes(u8 high, u8 low) +{ + return (high << 8) + low; +} + +#ifdef VERBOSE_DEBUG +static void cyttsp4_pr_buf(struct device *dev, u8 *pr_buf, u8 *dptr, int size, + const char *data_name) +{ + int i, k; + const char fmt[] = "%02X "; + int max; + + if (!size) + return; + + max = (CY_MAX_PRBUF_SIZE - 1) - sizeof(CY_PR_TRUNCATED); + + pr_buf[0] = 0; + for (i = k = 0; i < size && k < max; i++, k += 3) + scnprintf(pr_buf + k, CY_MAX_PRBUF_SIZE, fmt, dptr[i]); + + dev_vdbg(dev, "%s: %s[0..%d]=%s%s\n", __func__, data_name, size - 1, + pr_buf, size <= max ? "" : CY_PR_TRUNCATED); +} +#else +#define cyttsp4_pr_buf(dev, pr_buf, dptr, size, data_name) do { } while (0) +#endif + +static int cyttsp4_load_status_regs(struct cyttsp4 *cd) +{ + struct cyttsp4_sysinfo *si = &cd->sysinfo; + struct device *dev = cd->dev; + int rc; + + rc = cyttsp4_adap_read(cd, CY_REG_BASE, si->si_ofs.mode_size, + si->xy_mode); + if (rc < 0) + dev_err(dev, "%s: fail read mode regs r=%d\n", + __func__, rc); + else + cyttsp4_pr_buf(dev, cd->pr_buf, si->xy_mode, + si->si_ofs.mode_size, "xy_mode"); + + return rc; +} + +static int cyttsp4_handshake(struct cyttsp4 *cd, u8 mode) +{ + u8 cmd = mode ^ CY_HST_TOGGLE; + int rc; + + /* + * Mode change issued, handshaking now will cause endless mode change + * requests, for sync mode modechange will do same with handshake + * */ + if (mode & CY_HST_MODE_CHANGE) + return 0; + + rc = cyttsp4_adap_write(cd, CY_REG_BASE, sizeof(cmd), &cmd); + if (rc < 0) + dev_err(cd->dev, "%s: bus write fail on handshake (ret=%d)\n", + __func__, rc); + + return rc; +} + +static int cyttsp4_hw_soft_reset(struct cyttsp4 *cd) +{ + u8 cmd = CY_HST_RESET; + int rc = cyttsp4_adap_write(cd, CY_REG_BASE, sizeof(cmd), &cmd); + if (rc < 0) { + dev_err(cd->dev, "%s: FAILED to execute SOFT reset\n", + __func__); + return rc; + } + return 0; +} + +static int cyttsp4_hw_hard_reset(struct cyttsp4 *cd) +{ + if (cd->cpdata->xres) { + cd->cpdata->xres(cd->cpdata, cd->dev); + dev_dbg(cd->dev, "%s: execute HARD reset\n", __func__); + return 0; + } + dev_err(cd->dev, "%s: FAILED to execute HARD reset\n", __func__); + return -ENOSYS; +} + +static int cyttsp4_hw_reset(struct cyttsp4 *cd) +{ + int rc = cyttsp4_hw_hard_reset(cd); + if (rc == -ENOSYS) + rc = cyttsp4_hw_soft_reset(cd); + return rc; +} + +/* + * Gets number of bits for a touch filed as parameter, + * sets maximum value for field which is used as bit mask + * and returns number of bytes required for that field + */ +static int cyttsp4_bits_2_bytes(unsigned int nbits, size_t *max) +{ + *max = 1 << nbits; + return (nbits + 7) / 8; +} + +static int cyttsp4_si_data_offsets(struct cyttsp4 *cd) +{ + struct cyttsp4_sysinfo *si = &cd->sysinfo; + int rc = cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(si->si_data), + &si->si_data); + if (rc < 0) { + dev_err(cd->dev, "%s: fail read sysinfo data offsets r=%d\n", + __func__, rc); + return rc; + } + + /* Print sysinfo data offsets */ + cyttsp4_pr_buf(cd->dev, cd->pr_buf, (u8 *)&si->si_data, + sizeof(si->si_data), "sysinfo_data_offsets"); + + /* convert sysinfo data offset bytes into integers */ + + si->si_ofs.map_sz = merge_bytes(si->si_data.map_szh, + si->si_data.map_szl); + si->si_ofs.map_sz = merge_bytes(si->si_data.map_szh, + si->si_data.map_szl); + si->si_ofs.cydata_ofs = merge_bytes(si->si_data.cydata_ofsh, + si->si_data.cydata_ofsl); + si->si_ofs.test_ofs = merge_bytes(si->si_data.test_ofsh, + si->si_data.test_ofsl); + si->si_ofs.pcfg_ofs = merge_bytes(si->si_data.pcfg_ofsh, + si->si_data.pcfg_ofsl); + si->si_ofs.opcfg_ofs = merge_bytes(si->si_data.opcfg_ofsh, + si->si_data.opcfg_ofsl); + si->si_ofs.ddata_ofs = merge_bytes(si->si_data.ddata_ofsh, + si->si_data.ddata_ofsl); + si->si_ofs.mdata_ofs = merge_bytes(si->si_data.mdata_ofsh, + si->si_data.mdata_ofsl); + return rc; +} + +static int cyttsp4_si_get_cydata(struct cyttsp4 *cd) +{ + struct cyttsp4_sysinfo *si = &cd->sysinfo; + int read_offset; + int mfgid_sz, calc_mfgid_sz; + void *p; + int rc; + + si->si_ofs.cydata_size = si->si_ofs.test_ofs - si->si_ofs.cydata_ofs; + dev_dbg(cd->dev, "%s: cydata size: %Zd\n", __func__, + si->si_ofs.cydata_size); + + p = krealloc(si->si_ptrs.cydata, si->si_ofs.cydata_size, GFP_KERNEL); + if (p == NULL) { + dev_err(cd->dev, "%s: fail alloc cydata memory\n", __func__); + return -ENOMEM; + } + si->si_ptrs.cydata = p; + + read_offset = si->si_ofs.cydata_ofs; + + /* Read the CYDA registers up to MFGID field */ + rc = cyttsp4_adap_read(cd, read_offset, + offsetof(struct cyttsp4_cydata, mfgid_sz) + + sizeof(si->si_ptrs.cydata->mfgid_sz), + si->si_ptrs.cydata); + if (rc < 0) { + dev_err(cd->dev, "%s: fail read cydata r=%d\n", + __func__, rc); + return rc; + } + + /* Check MFGID size */ + mfgid_sz = si->si_ptrs.cydata->mfgid_sz; + calc_mfgid_sz = si->si_ofs.cydata_size - sizeof(struct cyttsp4_cydata); + if (mfgid_sz != calc_mfgid_sz) { + dev_err(cd->dev, "%s: mismatch in MFGID size, reported:%d calculated:%d\n", + __func__, mfgid_sz, calc_mfgid_sz); + return -EINVAL; + } + + read_offset += offsetof(struct cyttsp4_cydata, mfgid_sz) + + sizeof(si->si_ptrs.cydata->mfgid_sz); + + /* Read the CYDA registers for MFGID field */ + rc = cyttsp4_adap_read(cd, read_offset, si->si_ptrs.cydata->mfgid_sz, + si->si_ptrs.cydata->mfg_id); + if (rc < 0) { + dev_err(cd->dev, "%s: fail read cydata r=%d\n", + __func__, rc); + return rc; + } + + read_offset += si->si_ptrs.cydata->mfgid_sz; + + /* Read the rest of the CYDA registers */ + rc = cyttsp4_adap_read(cd, read_offset, + sizeof(struct cyttsp4_cydata) + - offsetof(struct cyttsp4_cydata, cyito_idh), + &si->si_ptrs.cydata->cyito_idh); + if (rc < 0) { + dev_err(cd->dev, "%s: fail read cydata r=%d\n", + __func__, rc); + return rc; + } + + cyttsp4_pr_buf(cd->dev, cd->pr_buf, (u8 *)si->si_ptrs.cydata, + si->si_ofs.cydata_size, "sysinfo_cydata"); + return rc; +} + +static int cyttsp4_si_get_test_data(struct cyttsp4 *cd) +{ + struct cyttsp4_sysinfo *si = &cd->sysinfo; + void *p; + int rc; + + si->si_ofs.test_size = si->si_ofs.pcfg_ofs - si->si_ofs.test_ofs; + + p = krealloc(si->si_ptrs.test, si->si_ofs.test_size, GFP_KERNEL); + if (p == NULL) { + dev_err(cd->dev, "%s: fail alloc test memory\n", __func__); + return -ENOMEM; + } + si->si_ptrs.test = p; + + rc = cyttsp4_adap_read(cd, si->si_ofs.test_ofs, si->si_ofs.test_size, + si->si_ptrs.test); + if (rc < 0) { + dev_err(cd->dev, "%s: fail read test data r=%d\n", + __func__, rc); + return rc; + } + + cyttsp4_pr_buf(cd->dev, cd->pr_buf, + (u8 *)si->si_ptrs.test, si->si_ofs.test_size, + "sysinfo_test_data"); + if (si->si_ptrs.test->post_codel & + CY_POST_CODEL_WDG_RST) + dev_info(cd->dev, "%s: %s codel=%02X\n", + __func__, "Reset was a WATCHDOG RESET", + si->si_ptrs.test->post_codel); + + if (!(si->si_ptrs.test->post_codel & + CY_POST_CODEL_CFG_DATA_CRC_FAIL)) + dev_info(cd->dev, "%s: %s codel=%02X\n", __func__, + "Config Data CRC FAIL", + si->si_ptrs.test->post_codel); + + if (!(si->si_ptrs.test->post_codel & + CY_POST_CODEL_PANEL_TEST_FAIL)) + dev_info(cd->dev, "%s: %s codel=%02X\n", + __func__, "PANEL TEST FAIL", + si->si_ptrs.test->post_codel); + + dev_info(cd->dev, "%s: SCANNING is %s codel=%02X\n", + __func__, si->si_ptrs.test->post_codel & 0x08 ? + "ENABLED" : "DISABLED", + si->si_ptrs.test->post_codel); + return rc; +} + +static int cyttsp4_si_get_pcfg_data(struct cyttsp4 *cd) +{ + struct cyttsp4_sysinfo *si = &cd->sysinfo; + void *p; + int rc; + + si->si_ofs.pcfg_size = si->si_ofs.opcfg_ofs - si->si_ofs.pcfg_ofs; + + p = krealloc(si->si_ptrs.pcfg, si->si_ofs.pcfg_size, GFP_KERNEL); + if (p == NULL) { + rc = -ENOMEM; + dev_err(cd->dev, "%s: fail alloc pcfg memory r=%d\n", + __func__, rc); + return rc; + } + si->si_ptrs.pcfg = p; + + rc = cyttsp4_adap_read(cd, si->si_ofs.pcfg_ofs, si->si_ofs.pcfg_size, + si->si_ptrs.pcfg); + if (rc < 0) { + dev_err(cd->dev, "%s: fail read pcfg data r=%d\n", + __func__, rc); + return rc; + } + + si->si_ofs.max_x = merge_bytes((si->si_ptrs.pcfg->res_xh + & CY_PCFG_RESOLUTION_X_MASK), si->si_ptrs.pcfg->res_xl); + si->si_ofs.x_origin = !!(si->si_ptrs.pcfg->res_xh + & CY_PCFG_ORIGIN_X_MASK); + si->si_ofs.max_y = merge_bytes((si->si_ptrs.pcfg->res_yh + & CY_PCFG_RESOLUTION_Y_MASK), si->si_ptrs.pcfg->res_yl); + si->si_ofs.y_origin = !!(si->si_ptrs.pcfg->res_yh + & CY_PCFG_ORIGIN_Y_MASK); + si->si_ofs.max_p = merge_bytes(si->si_ptrs.pcfg->max_zh, + si->si_ptrs.pcfg->max_zl); + + cyttsp4_pr_buf(cd->dev, cd->pr_buf, + (u8 *)si->si_ptrs.pcfg, + si->si_ofs.pcfg_size, "sysinfo_pcfg_data"); + return rc; +} + +static int cyttsp4_si_get_opcfg_data(struct cyttsp4 *cd) +{ + struct cyttsp4_sysinfo *si = &cd->sysinfo; + struct cyttsp4_tch_abs_params *tch; + struct cyttsp4_tch_rec_params *tch_old, *tch_new; + enum cyttsp4_tch_abs abs; + int i; + void *p; + int rc; + + si->si_ofs.opcfg_size = si->si_ofs.ddata_ofs - si->si_ofs.opcfg_ofs; + + p = krealloc(si->si_ptrs.opcfg, si->si_ofs.opcfg_size, GFP_KERNEL); + if (p == NULL) { + dev_err(cd->dev, "%s: fail alloc opcfg memory\n", __func__); + rc = -ENOMEM; + goto cyttsp4_si_get_opcfg_data_exit; + } + si->si_ptrs.opcfg = p; + + rc = cyttsp4_adap_read(cd, si->si_ofs.opcfg_ofs, si->si_ofs.opcfg_size, + si->si_ptrs.opcfg); + if (rc < 0) { + dev_err(cd->dev, "%s: fail read opcfg data r=%d\n", + __func__, rc); + goto cyttsp4_si_get_opcfg_data_exit; + } + si->si_ofs.cmd_ofs = si->si_ptrs.opcfg->cmd_ofs; + si->si_ofs.rep_ofs = si->si_ptrs.opcfg->rep_ofs; + si->si_ofs.rep_sz = (si->si_ptrs.opcfg->rep_szh * 256) + + si->si_ptrs.opcfg->rep_szl; + si->si_ofs.num_btns = si->si_ptrs.opcfg->num_btns; + si->si_ofs.num_btn_regs = (si->si_ofs.num_btns + + CY_NUM_BTN_PER_REG - 1) / CY_NUM_BTN_PER_REG; + si->si_ofs.tt_stat_ofs = si->si_ptrs.opcfg->tt_stat_ofs; + si->si_ofs.obj_cfg0 = si->si_ptrs.opcfg->obj_cfg0; + si->si_ofs.max_tchs = si->si_ptrs.opcfg->max_tchs & + CY_BYTE_OFS_MASK; + si->si_ofs.tch_rec_size = si->si_ptrs.opcfg->tch_rec_size & + CY_BYTE_OFS_MASK; + + /* Get the old touch fields */ + for (abs = CY_TCH_X; abs < CY_NUM_TCH_FIELDS; abs++) { + tch = &si->si_ofs.tch_abs[abs]; + tch_old = &si->si_ptrs.opcfg->tch_rec_old[abs]; + + tch->ofs = tch_old->loc & CY_BYTE_OFS_MASK; + tch->size = cyttsp4_bits_2_bytes(tch_old->size, + &tch->max); + tch->bofs = (tch_old->loc & CY_BOFS_MASK) >> CY_BOFS_SHIFT; + } + + /* button fields */ + si->si_ofs.btn_rec_size = si->si_ptrs.opcfg->btn_rec_size; + si->si_ofs.btn_diff_ofs = si->si_ptrs.opcfg->btn_diff_ofs; + si->si_ofs.btn_diff_size = si->si_ptrs.opcfg->btn_diff_size; + + if (si->si_ofs.tch_rec_size > CY_TMA1036_TCH_REC_SIZE) { + /* Get the extended touch fields */ + for (i = 0; i < CY_NUM_EXT_TCH_FIELDS; abs++, i++) { + tch = &si->si_ofs.tch_abs[abs]; + tch_new = &si->si_ptrs.opcfg->tch_rec_new[i]; + + tch->ofs = tch_new->loc & CY_BYTE_OFS_MASK; + tch->size = cyttsp4_bits_2_bytes(tch_new->size, + &tch->max); + tch->bofs = (tch_new->loc & CY_BOFS_MASK) >> CY_BOFS_SHIFT; + } + } + + for (abs = 0; abs < CY_TCH_NUM_ABS; abs++) { + dev_dbg(cd->dev, "%s: tch_rec_%s\n", __func__, + cyttsp4_tch_abs_string[abs]); + dev_dbg(cd->dev, "%s: ofs =%2Zd\n", __func__, + si->si_ofs.tch_abs[abs].ofs); + dev_dbg(cd->dev, "%s: siz =%2Zd\n", __func__, + si->si_ofs.tch_abs[abs].size); + dev_dbg(cd->dev, "%s: max =%2Zd\n", __func__, + si->si_ofs.tch_abs[abs].max); + dev_dbg(cd->dev, "%s: bofs=%2Zd\n", __func__, + si->si_ofs.tch_abs[abs].bofs); + } + + si->si_ofs.mode_size = si->si_ofs.tt_stat_ofs + 1; + si->si_ofs.data_size = si->si_ofs.max_tchs * + si->si_ptrs.opcfg->tch_rec_size; + + cyttsp4_pr_buf(cd->dev, cd->pr_buf, (u8 *)si->si_ptrs.opcfg, + si->si_ofs.opcfg_size, "sysinfo_opcfg_data"); + +cyttsp4_si_get_opcfg_data_exit: + return rc; +} + +static int cyttsp4_si_get_ddata(struct cyttsp4 *cd) +{ + struct cyttsp4_sysinfo *si = &cd->sysinfo; + void *p; + int rc; + + si->si_ofs.ddata_size = si->si_ofs.mdata_ofs - si->si_ofs.ddata_ofs; + + p = krealloc(si->si_ptrs.ddata, si->si_ofs.ddata_size, GFP_KERNEL); + if (p == NULL) { + dev_err(cd->dev, "%s: fail alloc ddata memory\n", __func__); + return -ENOMEM; + } + si->si_ptrs.ddata = p; + + rc = cyttsp4_adap_read(cd, si->si_ofs.ddata_ofs, si->si_ofs.ddata_size, + si->si_ptrs.ddata); + if (rc < 0) + dev_err(cd->dev, "%s: fail read ddata data r=%d\n", + __func__, rc); + else + cyttsp4_pr_buf(cd->dev, cd->pr_buf, + (u8 *)si->si_ptrs.ddata, + si->si_ofs.ddata_size, "sysinfo_ddata"); + return rc; +} + +static int cyttsp4_si_get_mdata(struct cyttsp4 *cd) +{ + struct cyttsp4_sysinfo *si = &cd->sysinfo; + void *p; + int rc; + + si->si_ofs.mdata_size = si->si_ofs.map_sz - si->si_ofs.mdata_ofs; + + p = krealloc(si->si_ptrs.mdata, si->si_ofs.mdata_size, GFP_KERNEL); + if (p == NULL) { + dev_err(cd->dev, "%s: fail alloc mdata memory\n", __func__); + return -ENOMEM; + } + si->si_ptrs.mdata = p; + + rc = cyttsp4_adap_read(cd, si->si_ofs.mdata_ofs, si->si_ofs.mdata_size, + si->si_ptrs.mdata); + if (rc < 0) + dev_err(cd->dev, "%s: fail read mdata data r=%d\n", + __func__, rc); + else + cyttsp4_pr_buf(cd->dev, cd->pr_buf, + (u8 *)si->si_ptrs.mdata, + si->si_ofs.mdata_size, "sysinfo_mdata"); + return rc; +} + +static int cyttsp4_si_get_btn_data(struct cyttsp4 *cd) +{ + struct cyttsp4_sysinfo *si = &cd->sysinfo; + int btn; + int num_defined_keys; + u16 *key_table; + void *p; + int rc = 0; + + if (si->si_ofs.num_btns) { + si->si_ofs.btn_keys_size = si->si_ofs.num_btns * + sizeof(struct cyttsp4_btn); + + p = krealloc(si->btn, si->si_ofs.btn_keys_size, + GFP_KERNEL|__GFP_ZERO); + if (p == NULL) { + dev_err(cd->dev, "%s: %s\n", __func__, + "fail alloc btn_keys memory"); + return -ENOMEM; + } + si->btn = p; + + if (cd->cpdata->sett[CY_IC_GRPNUM_BTN_KEYS] == NULL) + num_defined_keys = 0; + else if (cd->cpdata->sett[CY_IC_GRPNUM_BTN_KEYS]->data == NULL) + num_defined_keys = 0; + else + num_defined_keys = cd->cpdata->sett + [CY_IC_GRPNUM_BTN_KEYS]->size; + + for (btn = 0; btn < si->si_ofs.num_btns && + btn < num_defined_keys; btn++) { + key_table = (u16 *)cd->cpdata->sett + [CY_IC_GRPNUM_BTN_KEYS]->data; + si->btn[btn].key_code = key_table[btn]; + si->btn[btn].state = CY_BTN_RELEASED; + si->btn[btn].enabled = true; + } + for (; btn < si->si_ofs.num_btns; btn++) { + si->btn[btn].key_code = KEY_RESERVED; + si->btn[btn].state = CY_BTN_RELEASED; + si->btn[btn].enabled = true; + } + + return rc; + } + + si->si_ofs.btn_keys_size = 0; + kfree(si->btn); + si->btn = NULL; + return rc; +} + +static int cyttsp4_si_get_op_data_ptrs(struct cyttsp4 *cd) +{ + struct cyttsp4_sysinfo *si = &cd->sysinfo; + void *p; + + p = krealloc(si->xy_mode, si->si_ofs.mode_size, GFP_KERNEL|__GFP_ZERO); + if (p == NULL) + return -ENOMEM; + si->xy_mode = p; + + p = krealloc(si->xy_data, si->si_ofs.data_size, GFP_KERNEL|__GFP_ZERO); + if (p == NULL) + return -ENOMEM; + si->xy_data = p; + + p = krealloc(si->btn_rec_data, + si->si_ofs.btn_rec_size * si->si_ofs.num_btns, + GFP_KERNEL|__GFP_ZERO); + if (p == NULL) + return -ENOMEM; + si->btn_rec_data = p; + + return 0; +} + +static void cyttsp4_si_put_log_data(struct cyttsp4 *cd) +{ + struct cyttsp4_sysinfo *si = &cd->sysinfo; + dev_dbg(cd->dev, "%s: cydata_ofs =%4Zd siz=%4Zd\n", __func__, + si->si_ofs.cydata_ofs, si->si_ofs.cydata_size); + dev_dbg(cd->dev, "%s: test_ofs =%4Zd siz=%4Zd\n", __func__, + si->si_ofs.test_ofs, si->si_ofs.test_size); + dev_dbg(cd->dev, "%s: pcfg_ofs =%4Zd siz=%4Zd\n", __func__, + si->si_ofs.pcfg_ofs, si->si_ofs.pcfg_size); + dev_dbg(cd->dev, "%s: opcfg_ofs =%4Zd siz=%4Zd\n", __func__, + si->si_ofs.opcfg_ofs, si->si_ofs.opcfg_size); + dev_dbg(cd->dev, "%s: ddata_ofs =%4Zd siz=%4Zd\n", __func__, + si->si_ofs.ddata_ofs, si->si_ofs.ddata_size); + dev_dbg(cd->dev, "%s: mdata_ofs =%4Zd siz=%4Zd\n", __func__, + si->si_ofs.mdata_ofs, si->si_ofs.mdata_size); + + dev_dbg(cd->dev, "%s: cmd_ofs =%4Zd\n", __func__, + si->si_ofs.cmd_ofs); + dev_dbg(cd->dev, "%s: rep_ofs =%4Zd\n", __func__, + si->si_ofs.rep_ofs); + dev_dbg(cd->dev, "%s: rep_sz =%4Zd\n", __func__, + si->si_ofs.rep_sz); + dev_dbg(cd->dev, "%s: num_btns =%4Zd\n", __func__, + si->si_ofs.num_btns); + dev_dbg(cd->dev, "%s: num_btn_regs =%4Zd\n", __func__, + si->si_ofs.num_btn_regs); + dev_dbg(cd->dev, "%s: tt_stat_ofs =%4Zd\n", __func__, + si->si_ofs.tt_stat_ofs); + dev_dbg(cd->dev, "%s: tch_rec_size =%4Zd\n", __func__, + si->si_ofs.tch_rec_size); + dev_dbg(cd->dev, "%s: max_tchs =%4Zd\n", __func__, + si->si_ofs.max_tchs); + dev_dbg(cd->dev, "%s: mode_size =%4Zd\n", __func__, + si->si_ofs.mode_size); + dev_dbg(cd->dev, "%s: data_size =%4Zd\n", __func__, + si->si_ofs.data_size); + dev_dbg(cd->dev, "%s: map_sz =%4Zd\n", __func__, + si->si_ofs.map_sz); + + dev_dbg(cd->dev, "%s: btn_rec_size =%2Zd\n", __func__, + si->si_ofs.btn_rec_size); + dev_dbg(cd->dev, "%s: btn_diff_ofs =%2Zd\n", __func__, + si->si_ofs.btn_diff_ofs); + dev_dbg(cd->dev, "%s: btn_diff_size =%2Zd\n", __func__, + si->si_ofs.btn_diff_size); + + dev_dbg(cd->dev, "%s: max_x = 0x%04ZX (%Zd)\n", __func__, + si->si_ofs.max_x, si->si_ofs.max_x); + dev_dbg(cd->dev, "%s: x_origin = %Zd (%s)\n", __func__, + si->si_ofs.x_origin, + si->si_ofs.x_origin == CY_NORMAL_ORIGIN ? + "left corner" : "right corner"); + dev_dbg(cd->dev, "%s: max_y = 0x%04ZX (%Zd)\n", __func__, + si->si_ofs.max_y, si->si_ofs.max_y); + dev_dbg(cd->dev, "%s: y_origin = %Zd (%s)\n", __func__, + si->si_ofs.y_origin, + si->si_ofs.y_origin == CY_NORMAL_ORIGIN ? + "upper corner" : "lower corner"); + dev_dbg(cd->dev, "%s: max_p = 0x%04ZX (%Zd)\n", __func__, + si->si_ofs.max_p, si->si_ofs.max_p); + + dev_dbg(cd->dev, "%s: xy_mode=%p xy_data=%p\n", __func__, + si->xy_mode, si->xy_data); +} + +static int cyttsp4_get_sysinfo_regs(struct cyttsp4 *cd) +{ + struct cyttsp4_sysinfo *si = &cd->sysinfo; + int rc; + + rc = cyttsp4_si_data_offsets(cd); + if (rc < 0) + return rc; + + rc = cyttsp4_si_get_cydata(cd); + if (rc < 0) + return rc; + + rc = cyttsp4_si_get_test_data(cd); + if (rc < 0) + return rc; + + rc = cyttsp4_si_get_pcfg_data(cd); + if (rc < 0) + return rc; + + rc = cyttsp4_si_get_opcfg_data(cd); + if (rc < 0) + return rc; + + rc = cyttsp4_si_get_ddata(cd); + if (rc < 0) + return rc; + + rc = cyttsp4_si_get_mdata(cd); + if (rc < 0) + return rc; + + rc = cyttsp4_si_get_btn_data(cd); + if (rc < 0) + return rc; + + rc = cyttsp4_si_get_op_data_ptrs(cd); + if (rc < 0) { + dev_err(cd->dev, "%s: failed to get_op_data\n", + __func__); + return rc; + } + + cyttsp4_si_put_log_data(cd); + + /* provide flow control handshake */ + rc = cyttsp4_handshake(cd, si->si_data.hst_mode); + if (rc < 0) + dev_err(cd->dev, "%s: handshake fail on sysinfo reg\n", + __func__); + + si->ready = true; + return rc; +} + +static void cyttsp4_queue_startup_(struct cyttsp4 *cd) +{ + if (cd->startup_state == STARTUP_NONE) { + cd->startup_state = STARTUP_QUEUED; + schedule_work(&cd->startup_work); + dev_dbg(cd->dev, "%s: cyttsp4_startup queued\n", __func__); + } else { + dev_dbg(cd->dev, "%s: startup_state = %d\n", __func__, + cd->startup_state); + } +} + +static void cyttsp4_report_slot_liftoff(struct cyttsp4_mt_data *md, + int max_slots) +{ + int t; + + if (md->num_prv_tch == 0) + return; + + for (t = 0; t < max_slots; t++) { + input_mt_slot(md->input, t); + input_mt_report_slot_state(md->input, + MT_TOOL_FINGER, false); + } +} + +static void cyttsp4_lift_all(struct cyttsp4_mt_data *md) +{ + if (!md->si) + return; + + if (md->num_prv_tch != 0) { + cyttsp4_report_slot_liftoff(md, + md->si->si_ofs.tch_abs[CY_TCH_T].max); + input_sync(md->input); + md->num_prv_tch = 0; + } +} + +static void cyttsp4_get_touch_axis(struct cyttsp4_mt_data *md, + int *axis, int size, int max, u8 *xy_data, int bofs) +{ + int nbyte; + int next; + + for (nbyte = 0, *axis = 0, next = 0; nbyte < size; nbyte++) { + dev_vdbg(&md->input->dev, + "%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p" + " xy_data[%d]=%02X(%d) bofs=%d\n", + __func__, *axis, *axis, size, max, xy_data, next, + xy_data[next], xy_data[next], bofs); + *axis = (*axis * 256) + (xy_data[next] >> bofs); + next++; + } + + *axis &= max - 1; + + dev_vdbg(&md->input->dev, + "%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p" + " xy_data[%d]=%02X(%d)\n", + __func__, *axis, *axis, size, max, xy_data, next, + xy_data[next], xy_data[next]); +} + +static void cyttsp4_get_touch(struct cyttsp4_mt_data *md, + struct cyttsp4_touch *touch, u8 *xy_data) +{ + struct device *dev = &md->input->dev; + struct cyttsp4_sysinfo *si = md->si; + enum cyttsp4_tch_abs abs; + int tmp; + bool flipped; + + for (abs = CY_TCH_X; abs < CY_TCH_NUM_ABS; abs++) { + cyttsp4_get_touch_axis(md, &touch->abs[abs], + si->si_ofs.tch_abs[abs].size, + si->si_ofs.tch_abs[abs].max, + xy_data + si->si_ofs.tch_abs[abs].ofs, + si->si_ofs.tch_abs[abs].bofs); + dev_vdbg(dev, "%s: get %s=%04X(%d)\n", __func__, + cyttsp4_tch_abs_string[abs], + touch->abs[abs], touch->abs[abs]); + } + + if (md->pdata->flags & CY_FLAG_FLIP) { + tmp = touch->abs[CY_TCH_X]; + touch->abs[CY_TCH_X] = touch->abs[CY_TCH_Y]; + touch->abs[CY_TCH_Y] = tmp; + flipped = true; + } else + flipped = false; + + if (md->pdata->flags & CY_FLAG_INV_X) { + if (flipped) + touch->abs[CY_TCH_X] = md->si->si_ofs.max_y - + touch->abs[CY_TCH_X]; + else + touch->abs[CY_TCH_X] = md->si->si_ofs.max_x - + touch->abs[CY_TCH_X]; + } + if (md->pdata->flags & CY_FLAG_INV_Y) { + if (flipped) + touch->abs[CY_TCH_Y] = md->si->si_ofs.max_x - + touch->abs[CY_TCH_Y]; + else + touch->abs[CY_TCH_Y] = md->si->si_ofs.max_y - + touch->abs[CY_TCH_Y]; + } + + dev_vdbg(dev, "%s: flip=%s inv-x=%s inv-y=%s x=%04X(%d) y=%04X(%d)\n", + __func__, flipped ? "true" : "false", + md->pdata->flags & CY_FLAG_INV_X ? "true" : "false", + md->pdata->flags & CY_FLAG_INV_Y ? "true" : "false", + touch->abs[CY_TCH_X], touch->abs[CY_TCH_X], + touch->abs[CY_TCH_Y], touch->abs[CY_TCH_Y]); +} + +static void cyttsp4_final_sync(struct input_dev *input, int max_slots, int *ids) +{ + int t; + + for (t = 0; t < max_slots; t++) { + if (ids[t]) + continue; + input_mt_slot(input, t); + input_mt_report_slot_state(input, MT_TOOL_FINGER, false); + } + + input_sync(input); +} + +static void cyttsp4_get_mt_touches(struct cyttsp4_mt_data *md, int num_cur_tch) +{ + struct device *dev = &md->input->dev; + struct cyttsp4_sysinfo *si = md->si; + struct cyttsp4_touch tch; + int sig; + int i, j, t = 0; + int ids[max(CY_TMA1036_MAX_TCH, CY_TMA4XX_MAX_TCH)]; + + memset(ids, 0, si->si_ofs.tch_abs[CY_TCH_T].max * sizeof(int)); + for (i = 0; i < num_cur_tch; i++) { + cyttsp4_get_touch(md, &tch, si->xy_data + + (i * si->si_ofs.tch_rec_size)); + if ((tch.abs[CY_TCH_T] < md->pdata->frmwrk->abs + [(CY_ABS_ID_OST * CY_NUM_ABS_SET) + CY_MIN_OST]) || + (tch.abs[CY_TCH_T] > md->pdata->frmwrk->abs + [(CY_ABS_ID_OST * CY_NUM_ABS_SET) + CY_MAX_OST])) { + dev_err(dev, "%s: tch=%d -> bad trk_id=%d max_id=%d\n", + __func__, i, tch.abs[CY_TCH_T], + md->pdata->frmwrk->abs[(CY_ABS_ID_OST * + CY_NUM_ABS_SET) + CY_MAX_OST]); + continue; + } + + /* use 0 based track id's */ + sig = md->pdata->frmwrk->abs + [(CY_ABS_ID_OST * CY_NUM_ABS_SET) + 0]; + if (sig != CY_IGNORE_VALUE) { + t = tch.abs[CY_TCH_T] - md->pdata->frmwrk->abs + [(CY_ABS_ID_OST * CY_NUM_ABS_SET) + CY_MIN_OST]; + if (tch.abs[CY_TCH_E] == CY_EV_LIFTOFF) { + dev_dbg(dev, "%s: t=%d e=%d lift-off\n", + __func__, t, tch.abs[CY_TCH_E]); + goto cyttsp4_get_mt_touches_pr_tch; + } + input_mt_slot(md->input, t); + input_mt_report_slot_state(md->input, MT_TOOL_FINGER, + true); + ids[t] = true; + } + + /* all devices: position and pressure fields */ + for (j = 0; j <= CY_ABS_W_OST; j++) { + sig = md->pdata->frmwrk->abs[((CY_ABS_X_OST + j) * + CY_NUM_ABS_SET) + 0]; + if (sig != CY_IGNORE_VALUE) + input_report_abs(md->input, sig, + tch.abs[CY_TCH_X + j]); + } + if (si->si_ofs.tch_rec_size > CY_TMA1036_TCH_REC_SIZE) { + /* + * TMA400 size and orientation fields: + * if pressure is non-zero and major touch + * signal is zero, then set major and minor touch + * signals to minimum non-zero value + */ + if (tch.abs[CY_TCH_P] > 0 && tch.abs[CY_TCH_MAJ] == 0) + tch.abs[CY_TCH_MAJ] = tch.abs[CY_TCH_MIN] = 1; + + /* Get the extended touch fields */ + for (j = 0; j < CY_NUM_EXT_TCH_FIELDS; j++) { + sig = md->pdata->frmwrk->abs + [((CY_ABS_MAJ_OST + j) * + CY_NUM_ABS_SET) + 0]; + if (sig != CY_IGNORE_VALUE) + input_report_abs(md->input, sig, + tch.abs[CY_TCH_MAJ + j]); + } + } + +cyttsp4_get_mt_touches_pr_tch: + if (si->si_ofs.tch_rec_size > CY_TMA1036_TCH_REC_SIZE) + dev_dbg(dev, + "%s: t=%d x=%d y=%d z=%d M=%d m=%d o=%d e=%d\n", + __func__, t, + tch.abs[CY_TCH_X], + tch.abs[CY_TCH_Y], + tch.abs[CY_TCH_P], + tch.abs[CY_TCH_MAJ], + tch.abs[CY_TCH_MIN], + tch.abs[CY_TCH_OR], + tch.abs[CY_TCH_E]); + else + dev_dbg(dev, + "%s: t=%d x=%d y=%d z=%d e=%d\n", __func__, + t, + tch.abs[CY_TCH_X], + tch.abs[CY_TCH_Y], + tch.abs[CY_TCH_P], + tch.abs[CY_TCH_E]); + } + + cyttsp4_final_sync(md->input, si->si_ofs.tch_abs[CY_TCH_T].max, ids); + + md->num_prv_tch = num_cur_tch; + + return; +} + +/* read xy_data for all current touches */ +static int cyttsp4_xy_worker(struct cyttsp4 *cd) +{ + struct cyttsp4_mt_data *md = &cd->md; + struct device *dev = &md->input->dev; + struct cyttsp4_sysinfo *si = md->si; + u8 num_cur_tch; + u8 hst_mode; + u8 rep_len; + u8 rep_stat; + u8 tt_stat; + int rc = 0; + + /* + * Get event data from cyttsp4 device. + * The event data includes all data + * for all active touches. + * Event data also includes button data + */ + /* + * Use 2 reads: + * 1st read to get mode + button bytes + touch count (core) + * 2nd read (optional) to get touch 1 - touch n data + */ + hst_mode = si->xy_mode[CY_REG_BASE]; + rep_len = si->xy_mode[si->si_ofs.rep_ofs]; + rep_stat = si->xy_mode[si->si_ofs.rep_ofs + 1]; + tt_stat = si->xy_mode[si->si_ofs.tt_stat_ofs]; + dev_vdbg(dev, "%s: %s%02X %s%d %s%02X %s%02X\n", __func__, + "hst_mode=", hst_mode, "rep_len=", rep_len, + "rep_stat=", rep_stat, "tt_stat=", tt_stat); + + num_cur_tch = GET_NUM_TOUCHES(tt_stat); + dev_vdbg(dev, "%s: num_cur_tch=%d\n", __func__, num_cur_tch); + + if (rep_len == 0 && num_cur_tch > 0) { + dev_err(dev, "%s: report length error rep_len=%d num_tch=%d\n", + __func__, rep_len, num_cur_tch); + goto cyttsp4_xy_worker_exit; + } + + /* read touches */ + if (num_cur_tch > 0) { + rc = cyttsp4_adap_read(cd, si->si_ofs.tt_stat_ofs + 1, + num_cur_tch * si->si_ofs.tch_rec_size, + si->xy_data); + if (rc < 0) { + dev_err(dev, "%s: read fail on touch regs r=%d\n", + __func__, rc); + goto cyttsp4_xy_worker_exit; + } + } + + /* print xy data */ + cyttsp4_pr_buf(dev, cd->pr_buf, si->xy_data, num_cur_tch * + si->si_ofs.tch_rec_size, "xy_data"); + + /* check any error conditions */ + if (IS_BAD_PKT(rep_stat)) { + dev_dbg(dev, "%s: Invalid buffer detected\n", __func__); + rc = 0; + goto cyttsp4_xy_worker_exit; + } + + if (IS_LARGE_AREA(tt_stat)) + dev_dbg(dev, "%s: Large area detected\n", __func__); + + if (num_cur_tch > si->si_ofs.max_tchs) { + dev_err(dev, "%s: too many tch; set to max tch (n=%d c=%Zd)\n", + __func__, num_cur_tch, si->si_ofs.max_tchs); + num_cur_tch = si->si_ofs.max_tchs; + } + + /* extract xy_data for all currently reported touches */ + dev_vdbg(dev, "%s: extract data num_cur_tch=%d\n", __func__, + num_cur_tch); + if (num_cur_tch) + cyttsp4_get_mt_touches(md, num_cur_tch); + else + cyttsp4_lift_all(md); + + rc = 0; + +cyttsp4_xy_worker_exit: + return rc; +} + +static int cyttsp4_mt_attention(struct cyttsp4 *cd) +{ + struct device *dev = cd->dev; + struct cyttsp4_mt_data *md = &cd->md; + int rc = 0; + + if (!md->si) + return 0; + + mutex_lock(&md->report_lock); + if (!md->is_suspended) { + /* core handles handshake */ + rc = cyttsp4_xy_worker(cd); + } else { + dev_vdbg(dev, "%s: Ignoring report while suspended\n", + __func__); + } + mutex_unlock(&md->report_lock); + if (rc < 0) + dev_err(dev, "%s: xy_worker error r=%d\n", __func__, rc); + + return rc; +} + +static irqreturn_t cyttsp4_irq(int irq, void *handle) +{ + struct cyttsp4 *cd = handle; + struct device *dev = cd->dev; + enum cyttsp4_mode cur_mode; + u8 cmd_ofs = cd->sysinfo.si_ofs.cmd_ofs; + u8 mode[3]; + int rc; + + /* + * Check whether this IRQ should be ignored (external) + * This should be the very first thing to check since + * ignore_irq may be set for a very short period of time + */ + if (atomic_read(&cd->ignore_irq)) { + dev_vdbg(dev, "%s: Ignoring IRQ\n", __func__); + return IRQ_HANDLED; + } + + dev_dbg(dev, "%s int:0x%x\n", __func__, cd->int_status); + + mutex_lock(&cd->system_lock); + + /* Just to debug */ + if (cd->sleep_state == SS_SLEEP_ON || cd->sleep_state == SS_SLEEPING) + dev_vdbg(dev, "%s: Received IRQ while in sleep\n", __func__); + + rc = cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(mode), mode); + if (rc) { + dev_err(cd->dev, "%s: Fail read adapter r=%d\n", __func__, rc); + goto cyttsp4_irq_exit; + } + dev_vdbg(dev, "%s mode[0-2]:0x%X 0x%X 0x%X\n", __func__, + mode[0], mode[1], mode[2]); + + if (IS_BOOTLOADER(mode[0], mode[1])) { + cur_mode = CY_MODE_BOOTLOADER; + dev_vdbg(dev, "%s: bl running\n", __func__); + if (cd->mode == CY_MODE_BOOTLOADER) { + /* Signal bootloader heartbeat heard */ + wake_up(&cd->wait_q); + goto cyttsp4_irq_exit; + } + + /* switch to bootloader */ + dev_dbg(dev, "%s: restart switch to bl m=%d -> m=%d\n", + __func__, cd->mode, cur_mode); + + /* catch operation->bl glitch */ + if (cd->mode != CY_MODE_UNKNOWN) { + /* Incase startup_state do not let startup_() */ + cd->mode = CY_MODE_UNKNOWN; + cyttsp4_queue_startup_(cd); + goto cyttsp4_irq_exit; + } + + /* + * do not wake thread on this switch since + * it is possible to get an early heartbeat + * prior to performing the reset + */ + cd->mode = cur_mode; + + goto cyttsp4_irq_exit; + } + + switch (mode[0] & CY_HST_MODE) { + case CY_HST_OPERATE: + cur_mode = CY_MODE_OPERATIONAL; + dev_vdbg(dev, "%s: operational\n", __func__); + break; + case CY_HST_CAT: + cur_mode = CY_MODE_CAT; + dev_vdbg(dev, "%s: CaT\n", __func__); + break; + case CY_HST_SYSINFO: + cur_mode = CY_MODE_SYSINFO; + dev_vdbg(dev, "%s: sysinfo\n", __func__); + break; + default: + cur_mode = CY_MODE_UNKNOWN; + dev_err(dev, "%s: unknown HST mode 0x%02X\n", __func__, + mode[0]); + break; + } + + /* Check whether this IRQ should be ignored (internal) */ + if (cd->int_status & CY_INT_IGNORE) { + dev_vdbg(dev, "%s: Ignoring IRQ\n", __func__); + goto cyttsp4_irq_exit; + } + + /* Check for wake up interrupt */ + if (cd->int_status & CY_INT_AWAKE) { + cd->int_status &= ~CY_INT_AWAKE; + wake_up(&cd->wait_q); + dev_vdbg(dev, "%s: Received wake up interrupt\n", __func__); + goto cyttsp4_irq_handshake; + } + + /* Expecting mode change interrupt */ + if ((cd->int_status & CY_INT_MODE_CHANGE) + && (mode[0] & CY_HST_MODE_CHANGE) == 0) { + cd->int_status &= ~CY_INT_MODE_CHANGE; + dev_dbg(dev, "%s: finish mode switch m=%d -> m=%d\n", + __func__, cd->mode, cur_mode); + cd->mode = cur_mode; + wake_up(&cd->wait_q); + goto cyttsp4_irq_handshake; + } + + /* compare current core mode to current device mode */ + dev_vdbg(dev, "%s: cd->mode=%d cur_mode=%d\n", + __func__, cd->mode, cur_mode); + if ((mode[0] & CY_HST_MODE_CHANGE) == 0 && cd->mode != cur_mode) { + /* Unexpected mode change occurred */ + dev_err(dev, "%s %d->%d 0x%x\n", __func__, cd->mode, + cur_mode, cd->int_status); + dev_dbg(dev, "%s: Unexpected mode change, startup\n", + __func__); + cyttsp4_queue_startup_(cd); + goto cyttsp4_irq_exit; + } + + /* Expecting command complete interrupt */ + dev_vdbg(dev, "%s: command byte:0x%x\n", __func__, mode[cmd_ofs]); + if ((cd->int_status & CY_INT_EXEC_CMD) + && mode[cmd_ofs] & CY_CMD_COMPLETE) { + cd->int_status &= ~CY_INT_EXEC_CMD; + dev_vdbg(dev, "%s: Received command complete interrupt\n", + __func__); + wake_up(&cd->wait_q); + /* + * It is possible to receive a single interrupt for + * command complete and touch/button status report. + * Continue processing for a possible status report. + */ + } + + /* This should be status report, read status regs */ + if (cd->mode == CY_MODE_OPERATIONAL) { + dev_vdbg(dev, "%s: Read status registers\n", __func__); + rc = cyttsp4_load_status_regs(cd); + if (rc < 0) + dev_err(dev, "%s: fail read mode regs r=%d\n", + __func__, rc); + } + + cyttsp4_mt_attention(cd); + +cyttsp4_irq_handshake: + /* handshake the event */ + dev_vdbg(dev, "%s: Handshake mode=0x%02X r=%d\n", + __func__, mode[0], rc); + rc = cyttsp4_handshake(cd, mode[0]); + if (rc < 0) + dev_err(dev, "%s: Fail handshake mode=0x%02X r=%d\n", + __func__, mode[0], rc); + + /* + * a non-zero udelay period is required for using + * IRQF_TRIGGER_LOW in order to delay until the + * device completes isr deassert + */ + udelay(cd->cpdata->level_irq_udelay); + +cyttsp4_irq_exit: + mutex_unlock(&cd->system_lock); + return IRQ_HANDLED; +} + +static void cyttsp4_start_wd_timer(struct cyttsp4 *cd) +{ + if (!CY_WATCHDOG_TIMEOUT) + return; + + mod_timer(&cd->watchdog_timer, jiffies + + msecs_to_jiffies(CY_WATCHDOG_TIMEOUT)); +} + +static void cyttsp4_stop_wd_timer(struct cyttsp4 *cd) +{ + if (!CY_WATCHDOG_TIMEOUT) + return; + + /* + * Ensure we wait until the watchdog timer + * running on a different CPU finishes + */ + del_timer_sync(&cd->watchdog_timer); + cancel_work_sync(&cd->watchdog_work); + del_timer_sync(&cd->watchdog_timer); +} + +static void cyttsp4_watchdog_timer(unsigned long handle) +{ + struct cyttsp4 *cd = (struct cyttsp4 *)handle; + + dev_vdbg(cd->dev, "%s: Watchdog timer triggered\n", __func__); + + if (!cd) + return; + + if (!work_pending(&cd->watchdog_work)) + schedule_work(&cd->watchdog_work); + + return; +} + +static int cyttsp4_request_exclusive(struct cyttsp4 *cd, void *ownptr, + int timeout_ms) +{ + int t = msecs_to_jiffies(timeout_ms); + bool with_timeout = (timeout_ms != 0); + + mutex_lock(&cd->system_lock); + if (!cd->exclusive_dev && cd->exclusive_waits == 0) { + cd->exclusive_dev = ownptr; + goto exit; + } + + cd->exclusive_waits++; +wait: + mutex_unlock(&cd->system_lock); + if (with_timeout) { + t = wait_event_timeout(cd->wait_q, !cd->exclusive_dev, t); + if (IS_TMO(t)) { + dev_err(cd->dev, "%s: tmo waiting exclusive access\n", + __func__); + mutex_lock(&cd->system_lock); + cd->exclusive_waits--; + mutex_unlock(&cd->system_lock); + return -ETIME; + } + } else { + wait_event(cd->wait_q, !cd->exclusive_dev); + } + mutex_lock(&cd->system_lock); + if (cd->exclusive_dev) + goto wait; + cd->exclusive_dev = ownptr; + cd->exclusive_waits--; +exit: + mutex_unlock(&cd->system_lock); + + return 0; +} + +/* + * returns error if was not owned + */ +static int cyttsp4_release_exclusive(struct cyttsp4 *cd, void *ownptr) +{ + mutex_lock(&cd->system_lock); + if (cd->exclusive_dev != ownptr) { + mutex_unlock(&cd->system_lock); + return -EINVAL; + } + + dev_vdbg(cd->dev, "%s: exclusive_dev %p freed\n", + __func__, cd->exclusive_dev); + cd->exclusive_dev = NULL; + wake_up(&cd->wait_q); + mutex_unlock(&cd->system_lock); + return 0; +} + +static int cyttsp4_wait_bl_heartbeat(struct cyttsp4 *cd) +{ + long t; + int rc = 0; + + /* wait heartbeat */ + dev_vdbg(cd->dev, "%s: wait heartbeat...\n", __func__); + t = wait_event_timeout(cd->wait_q, cd->mode == CY_MODE_BOOTLOADER, + msecs_to_jiffies(CY_CORE_RESET_AND_WAIT_TIMEOUT)); + if (IS_TMO(t)) { + dev_err(cd->dev, "%s: tmo waiting bl heartbeat cd->mode=%d\n", + __func__, cd->mode); + rc = -ETIME; + } + + return rc; +} + +static int cyttsp4_wait_sysinfo_mode(struct cyttsp4 *cd) +{ + long t; + + dev_vdbg(cd->dev, "%s: wait sysinfo...\n", __func__); + + t = wait_event_timeout(cd->wait_q, cd->mode == CY_MODE_SYSINFO, + msecs_to_jiffies(CY_CORE_MODE_CHANGE_TIMEOUT)); + if (IS_TMO(t)) { + dev_err(cd->dev, "%s: tmo waiting exit bl cd->mode=%d\n", + __func__, cd->mode); + mutex_lock(&cd->system_lock); + cd->int_status &= ~CY_INT_MODE_CHANGE; + mutex_unlock(&cd->system_lock); + return -ETIME; + } + + return 0; +} + +static int cyttsp4_reset_and_wait(struct cyttsp4 *cd) +{ + int rc; + + /* reset hardware */ + mutex_lock(&cd->system_lock); + dev_dbg(cd->dev, "%s: reset hw...\n", __func__); + rc = cyttsp4_hw_reset(cd); + cd->mode = CY_MODE_UNKNOWN; + mutex_unlock(&cd->system_lock); + if (rc < 0) { + dev_err(cd->dev, "%s:Fail hw reset r=%d\n", __func__, rc); + return rc; + } + + return cyttsp4_wait_bl_heartbeat(cd); +} + +/* + * returns err if refused or timeout; block until mode change complete + * bit is set (mode change interrupt) + */ +static int cyttsp4_set_mode(struct cyttsp4 *cd, int new_mode) +{ + u8 new_dev_mode; + u8 mode; + long t; + int rc; + + switch (new_mode) { + case CY_MODE_OPERATIONAL: + new_dev_mode = CY_HST_OPERATE; + break; + case CY_MODE_SYSINFO: + new_dev_mode = CY_HST_SYSINFO; + break; + case CY_MODE_CAT: + new_dev_mode = CY_HST_CAT; + break; + default: + dev_err(cd->dev, "%s: invalid mode: %02X(%d)\n", + __func__, new_mode, new_mode); + return -EINVAL; + } + + /* change mode */ + dev_dbg(cd->dev, "%s: %s=%p new_dev_mode=%02X new_mode=%d\n", + __func__, "have exclusive", cd->exclusive_dev, + new_dev_mode, new_mode); + + mutex_lock(&cd->system_lock); + rc = cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(mode), &mode); + if (rc < 0) { + mutex_unlock(&cd->system_lock); + dev_err(cd->dev, "%s: Fail read mode r=%d\n", + __func__, rc); + goto exit; + } + + /* Clear device mode bits and set to new mode */ + mode &= ~CY_HST_MODE; + mode |= new_dev_mode | CY_HST_MODE_CHANGE; + + cd->int_status |= CY_INT_MODE_CHANGE; + rc = cyttsp4_adap_write(cd, CY_REG_BASE, sizeof(mode), &mode); + mutex_unlock(&cd->system_lock); + if (rc < 0) { + dev_err(cd->dev, "%s: Fail write mode change r=%d\n", + __func__, rc); + goto exit; + } + + /* wait for mode change done interrupt */ + t = wait_event_timeout(cd->wait_q, + (cd->int_status & CY_INT_MODE_CHANGE) == 0, + msecs_to_jiffies(CY_CORE_MODE_CHANGE_TIMEOUT)); + dev_dbg(cd->dev, "%s: back from wait t=%ld cd->mode=%d\n", + __func__, t, cd->mode); + + if (IS_TMO(t)) { + dev_err(cd->dev, "%s: %s\n", __func__, + "tmo waiting mode change"); + mutex_lock(&cd->system_lock); + cd->int_status &= ~CY_INT_MODE_CHANGE; + mutex_unlock(&cd->system_lock); + rc = -EINVAL; + } + +exit: + return rc; +} + +static void cyttsp4_watchdog_work(struct work_struct *work) +{ + struct cyttsp4 *cd = + container_of(work, struct cyttsp4, watchdog_work); + u8 *mode; + int retval; + + if (cd == NULL) { + dev_err(cd->dev, "%s: NULL context pointer\n", __func__); + return; + } + + mutex_lock(&cd->system_lock); + retval = cyttsp4_load_status_regs(cd); + if (retval < 0) { + dev_err(cd->dev, + "%s: failed to access device in watchdog timer r=%d\n", + __func__, retval); + cyttsp4_queue_startup_(cd); + goto cyttsp4_timer_watchdog_exit_error; + } + mode = &cd->sysinfo.xy_mode[CY_REG_BASE]; + if (IS_BOOTLOADER(mode[0], mode[1])) { + dev_err(cd->dev, + "%s: device found in bootloader mode when operational mode\n", + __func__); + cyttsp4_queue_startup_(cd); + goto cyttsp4_timer_watchdog_exit_error; + } + + cyttsp4_start_wd_timer(cd); +cyttsp4_timer_watchdog_exit_error: + mutex_unlock(&cd->system_lock); + return; +} + +static int cyttsp4_core_sleep_(struct cyttsp4 *cd) +{ + enum cyttsp4_sleep_state ss = SS_SLEEP_ON; + enum cyttsp4_int_state int_status = CY_INT_IGNORE; + int rc = 0; + u8 mode[2]; + + /* Already in sleep mode? */ + mutex_lock(&cd->system_lock); + if (cd->sleep_state == SS_SLEEP_ON) { + mutex_unlock(&cd->system_lock); + return 0; + } + cd->sleep_state = SS_SLEEPING; + mutex_unlock(&cd->system_lock); + + cyttsp4_stop_wd_timer(cd); + + /* Wait until currently running IRQ handler exits and disable IRQ */ + disable_irq(cd->irq); + + dev_vdbg(cd->dev, "%s: write DEEP SLEEP...\n", __func__); + mutex_lock(&cd->system_lock); + rc = cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(mode), &mode); + if (rc) { + mutex_unlock(&cd->system_lock); + dev_err(cd->dev, "%s: Fail read adapter r=%d\n", __func__, rc); + goto error; + } + + if (IS_BOOTLOADER(mode[0], mode[1])) { + mutex_unlock(&cd->system_lock); + dev_err(cd->dev, "%s: Device in BOOTLADER mode.\n", __func__); + rc = -EINVAL; + goto error; + } + + mode[0] |= CY_HST_SLEEP; + rc = cyttsp4_adap_write(cd, CY_REG_BASE, sizeof(mode[0]), &mode[0]); + mutex_unlock(&cd->system_lock); + if (rc) { + dev_err(cd->dev, "%s: Fail write adapter r=%d\n", __func__, rc); + goto error; + } + dev_vdbg(cd->dev, "%s: write DEEP SLEEP succeeded\n", __func__); + + if (cd->cpdata->power) { + dev_dbg(cd->dev, "%s: Power down HW\n", __func__); + rc = cd->cpdata->power(cd->cpdata, 0, cd->dev, &cd->ignore_irq); + } else { + dev_dbg(cd->dev, "%s: No power function\n", __func__); + rc = 0; + } + if (rc < 0) { + dev_err(cd->dev, "%s: HW Power down fails r=%d\n", + __func__, rc); + goto error; + } + + /* Give time to FW to sleep */ + msleep(50); + + goto exit; + +error: + ss = SS_SLEEP_OFF; + int_status = CY_INT_NONE; + cyttsp4_start_wd_timer(cd); + +exit: + mutex_lock(&cd->system_lock); + cd->sleep_state = ss; + cd->int_status |= int_status; + mutex_unlock(&cd->system_lock); + enable_irq(cd->irq); + return rc; +} + +static int cyttsp4_core_sleep(struct cyttsp4 *cd) +{ + int rc; + + rc = cyttsp4_request_exclusive(cd, cd->dev, + CY_CORE_SLEEP_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return 0; + } + + rc = cyttsp4_core_sleep_(cd); + + if (cyttsp4_release_exclusive(cd, cd->dev) < 0) + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + else + dev_vdbg(cd->dev, "%s: pass release exclusive\n", __func__); + + return rc; +} + +static int cyttsp4_core_wake_(struct cyttsp4 *cd) +{ + struct device *dev = cd->dev; + int rc; + u8 mode; + int t; + + /* Already woken? */ + mutex_lock(&cd->system_lock); + if (cd->sleep_state == SS_SLEEP_OFF) { + mutex_unlock(&cd->system_lock); + return 0; + } + cd->int_status &= ~CY_INT_IGNORE; + cd->int_status |= CY_INT_AWAKE; + cd->sleep_state = SS_WAKING; + + if (cd->cpdata->power) { + dev_dbg(dev, "%s: Power up HW\n", __func__); + rc = cd->cpdata->power(cd->cpdata, 1, dev, &cd->ignore_irq); + } else { + dev_dbg(dev, "%s: No power function\n", __func__); + rc = -ENOSYS; + } + if (rc < 0) { + dev_err(dev, "%s: HW Power up fails r=%d\n", + __func__, rc); + + /* Initiate a read transaction to wake up */ + cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(mode), &mode); + } else + dev_vdbg(cd->dev, "%s: HW power up succeeds\n", + __func__); + mutex_unlock(&cd->system_lock); + + t = wait_event_timeout(cd->wait_q, + (cd->int_status & CY_INT_AWAKE) == 0, + msecs_to_jiffies(CY_CORE_WAKEUP_TIMEOUT)); + if (IS_TMO(t)) { + dev_err(dev, "%s: TMO waiting for wakeup\n", __func__); + mutex_lock(&cd->system_lock); + cd->int_status &= ~CY_INT_AWAKE; + /* Try starting up */ + cyttsp4_queue_startup_(cd); + mutex_unlock(&cd->system_lock); + } + + mutex_lock(&cd->system_lock); + cd->sleep_state = SS_SLEEP_OFF; + mutex_unlock(&cd->system_lock); + + cyttsp4_start_wd_timer(cd); + + return 0; +} + +static int cyttsp4_core_wake(struct cyttsp4 *cd) +{ + int rc; + + rc = cyttsp4_request_exclusive(cd, cd->dev, + CY_CORE_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return 0; + } + + rc = cyttsp4_core_wake_(cd); + + if (cyttsp4_release_exclusive(cd, cd->dev) < 0) + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + else + dev_vdbg(cd->dev, "%s: pass release exclusive\n", __func__); + + return rc; +} + +static int cyttsp4_startup_(struct cyttsp4 *cd) +{ + int retry = CY_CORE_STARTUP_RETRY_COUNT; + int rc; + + cyttsp4_stop_wd_timer(cd); + +reset: + if (retry != CY_CORE_STARTUP_RETRY_COUNT) + dev_dbg(cd->dev, "%s: Retry %d\n", __func__, + CY_CORE_STARTUP_RETRY_COUNT - retry); + + /* reset hardware and wait for heartbeat */ + rc = cyttsp4_reset_and_wait(cd); + if (rc < 0) { + dev_err(cd->dev, "%s: Error on h/w reset r=%d\n", __func__, rc); + if (retry--) + goto reset; + goto exit; + } + + /* exit bl into sysinfo mode */ + dev_vdbg(cd->dev, "%s: write exit ldr...\n", __func__); + mutex_lock(&cd->system_lock); + cd->int_status &= ~CY_INT_IGNORE; + cd->int_status |= CY_INT_MODE_CHANGE; + + rc = cyttsp4_adap_write(cd, CY_REG_BASE, sizeof(ldr_exit), + (u8 *)ldr_exit); + mutex_unlock(&cd->system_lock); + if (rc < 0) { + dev_err(cd->dev, "%s: Fail write r=%d\n", __func__, rc); + if (retry--) + goto reset; + goto exit; + } + + rc = cyttsp4_wait_sysinfo_mode(cd); + if (rc < 0) { + u8 buf[sizeof(ldr_err_app)]; + int rc1; + + /* Check for invalid/corrupted touch application */ + rc1 = cyttsp4_adap_read(cd, CY_REG_BASE, sizeof(ldr_err_app), + buf); + if (rc1) { + dev_err(cd->dev, "%s: Fail read r=%d\n", __func__, rc1); + } else if (!memcmp(buf, ldr_err_app, sizeof(ldr_err_app))) { + dev_err(cd->dev, "%s: Error launching touch application\n", + __func__); + mutex_lock(&cd->system_lock); + cd->invalid_touch_app = true; + mutex_unlock(&cd->system_lock); + goto exit_no_wd; + } + + if (retry--) + goto reset; + goto exit; + } + + mutex_lock(&cd->system_lock); + cd->invalid_touch_app = false; + mutex_unlock(&cd->system_lock); + + /* read sysinfo data */ + dev_vdbg(cd->dev, "%s: get sysinfo regs..\n", __func__); + rc = cyttsp4_get_sysinfo_regs(cd); + if (rc < 0) { + dev_err(cd->dev, "%s: failed to get sysinfo regs rc=%d\n", + __func__, rc); + if (retry--) + goto reset; + goto exit; + } + + rc = cyttsp4_set_mode(cd, CY_MODE_OPERATIONAL); + if (rc < 0) { + dev_err(cd->dev, "%s: failed to set mode to operational rc=%d\n", + __func__, rc); + if (retry--) + goto reset; + goto exit; + } + + cyttsp4_lift_all(&cd->md); + + /* restore to sleep if was suspended */ + mutex_lock(&cd->system_lock); + if (cd->sleep_state == SS_SLEEP_ON) { + cd->sleep_state = SS_SLEEP_OFF; + mutex_unlock(&cd->system_lock); + cyttsp4_core_sleep_(cd); + goto exit_no_wd; + } + mutex_unlock(&cd->system_lock); + +exit: + cyttsp4_start_wd_timer(cd); +exit_no_wd: + return rc; +} + +static int cyttsp4_startup(struct cyttsp4 *cd) +{ + int rc; + + mutex_lock(&cd->system_lock); + cd->startup_state = STARTUP_RUNNING; + mutex_unlock(&cd->system_lock); + + rc = cyttsp4_request_exclusive(cd, cd->dev, + CY_CORE_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + goto exit; + } + + rc = cyttsp4_startup_(cd); + + if (cyttsp4_release_exclusive(cd, cd->dev) < 0) + /* Don't return fail code, mode is already changed. */ + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + else + dev_vdbg(cd->dev, "%s: pass release exclusive\n", __func__); + +exit: + mutex_lock(&cd->system_lock); + cd->startup_state = STARTUP_NONE; + mutex_unlock(&cd->system_lock); + + /* Wake the waiters for end of startup */ + wake_up(&cd->wait_q); + + return rc; +} + +static void cyttsp4_startup_work_function(struct work_struct *work) +{ + struct cyttsp4 *cd = container_of(work, struct cyttsp4, startup_work); + int rc; + + rc = cyttsp4_startup(cd); + if (rc < 0) + dev_err(cd->dev, "%s: Fail queued startup r=%d\n", + __func__, rc); +} + +static void cyttsp4_free_si_ptrs(struct cyttsp4 *cd) +{ + struct cyttsp4_sysinfo *si = &cd->sysinfo; + + if (!si) + return; + + kfree(si->si_ptrs.cydata); + kfree(si->si_ptrs.test); + kfree(si->si_ptrs.pcfg); + kfree(si->si_ptrs.opcfg); + kfree(si->si_ptrs.ddata); + kfree(si->si_ptrs.mdata); + kfree(si->btn); + kfree(si->xy_mode); + kfree(si->xy_data); + kfree(si->btn_rec_data); +} + +#if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM_RUNTIME) +static int cyttsp4_core_suspend(struct device *dev) +{ + struct cyttsp4 *cd = dev_get_drvdata(dev); + struct cyttsp4_mt_data *md = &cd->md; + int rc; + + md->is_suspended = true; + + rc = cyttsp4_core_sleep(cd); + if (rc < 0) { + dev_err(dev, "%s: Error on sleep\n", __func__); + return -EAGAIN; + } + return 0; +} + +static int cyttsp4_core_resume(struct device *dev) +{ + struct cyttsp4 *cd = dev_get_drvdata(dev); + struct cyttsp4_mt_data *md = &cd->md; + int rc; + + md->is_suspended = false; + + rc = cyttsp4_core_wake(cd); + if (rc < 0) { + dev_err(dev, "%s: Error on wake\n", __func__); + return -EAGAIN; + } + + return 0; +} +#endif + +const struct dev_pm_ops cyttsp4_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(cyttsp4_core_suspend, cyttsp4_core_resume) + SET_RUNTIME_PM_OPS(cyttsp4_core_suspend, cyttsp4_core_resume, NULL) +}; +EXPORT_SYMBOL_GPL(cyttsp4_pm_ops); + +static int cyttsp4_mt_open(struct input_dev *input) +{ + pm_runtime_get(input->dev.parent); + return 0; +} + +static void cyttsp4_mt_close(struct input_dev *input) +{ + struct cyttsp4_mt_data *md = input_get_drvdata(input); + mutex_lock(&md->report_lock); + if (!md->is_suspended) + pm_runtime_put(input->dev.parent); + mutex_unlock(&md->report_lock); +} + + +static int cyttsp4_setup_input_device(struct cyttsp4 *cd) +{ + struct device *dev = cd->dev; + struct cyttsp4_mt_data *md = &cd->md; + int signal = CY_IGNORE_VALUE; + int max_x, max_y, max_p, min, max; + int max_x_tmp, max_y_tmp; + int i; + int rc; + + dev_vdbg(dev, "%s: Initialize event signals\n", __func__); + __set_bit(EV_ABS, md->input->evbit); + __set_bit(EV_REL, md->input->evbit); + __set_bit(EV_KEY, md->input->evbit); + + max_x_tmp = md->si->si_ofs.max_x; + max_y_tmp = md->si->si_ofs.max_y; + + /* get maximum values from the sysinfo data */ + if (md->pdata->flags & CY_FLAG_FLIP) { + max_x = max_y_tmp - 1; + max_y = max_x_tmp - 1; + } else { + max_x = max_x_tmp - 1; + max_y = max_y_tmp - 1; + } + max_p = md->si->si_ofs.max_p; + + /* set event signal capabilities */ + for (i = 0; i < (md->pdata->frmwrk->size / CY_NUM_ABS_SET); i++) { + signal = md->pdata->frmwrk->abs + [(i * CY_NUM_ABS_SET) + CY_SIGNAL_OST]; + if (signal != CY_IGNORE_VALUE) { + __set_bit(signal, md->input->absbit); + min = md->pdata->frmwrk->abs + [(i * CY_NUM_ABS_SET) + CY_MIN_OST]; + max = md->pdata->frmwrk->abs + [(i * CY_NUM_ABS_SET) + CY_MAX_OST]; + if (i == CY_ABS_ID_OST) { + /* shift track ids down to start at 0 */ + max = max - min; + min = min - min; + } else if (i == CY_ABS_X_OST) + max = max_x; + else if (i == CY_ABS_Y_OST) + max = max_y; + else if (i == CY_ABS_P_OST) + max = max_p; + input_set_abs_params(md->input, signal, min, max, + md->pdata->frmwrk->abs + [(i * CY_NUM_ABS_SET) + CY_FUZZ_OST], + md->pdata->frmwrk->abs + [(i * CY_NUM_ABS_SET) + CY_FLAT_OST]); + dev_dbg(dev, "%s: register signal=%02X min=%d max=%d\n", + __func__, signal, min, max); + if ((i == CY_ABS_ID_OST) && + (md->si->si_ofs.tch_rec_size < + CY_TMA4XX_TCH_REC_SIZE)) + break; + } + } + + input_mt_init_slots(md->input, md->si->si_ofs.tch_abs[CY_TCH_T].max, + INPUT_MT_DIRECT); + rc = input_register_device(md->input); + if (rc < 0) + dev_err(dev, "%s: Error, failed register input device r=%d\n", + __func__, rc); + return rc; +} + +static int cyttsp4_mt_probe(struct cyttsp4 *cd) +{ + struct device *dev = cd->dev; + struct cyttsp4_mt_data *md = &cd->md; + struct cyttsp4_mt_platform_data *pdata = cd->pdata->mt_pdata; + int rc = 0; + + mutex_init(&md->report_lock); + md->pdata = pdata; + /* Create the input device and register it. */ + dev_vdbg(dev, "%s: Create the input device and register it\n", + __func__); + md->input = input_allocate_device(); + if (md->input == NULL) { + dev_err(dev, "%s: Error, failed to allocate input device\n", + __func__); + rc = -ENOSYS; + goto error_alloc_failed; + } + + md->input->name = pdata->inp_dev_name; + scnprintf(md->phys, sizeof(md->phys)-1, "%s", dev_name(dev)); + md->input->phys = md->phys; + md->input->id.bustype = cd->bus_ops->bustype; + md->input->dev.parent = dev; + md->input->open = cyttsp4_mt_open; + md->input->close = cyttsp4_mt_close; + input_set_drvdata(md->input, md); + + /* get sysinfo */ + md->si = &cd->sysinfo; + if (!md->si) { + dev_err(dev, "%s: Fail get sysinfo pointer from core p=%p\n", + __func__, md->si); + goto error_get_sysinfo; + } + + rc = cyttsp4_setup_input_device(cd); + if (rc) + goto error_init_input; + + return 0; + +error_init_input: + input_free_device(md->input); +error_get_sysinfo: + input_set_drvdata(md->input, NULL); +error_alloc_failed: + dev_err(dev, "%s failed.\n", __func__); + return rc; +} + +struct cyttsp4 *cyttsp4_probe(const struct cyttsp4_bus_ops *ops, + struct device *dev, u16 irq, size_t xfer_buf_size) +{ + struct cyttsp4 *cd; + struct cyttsp4_platform_data *pdata = dev_get_platdata(dev); + unsigned long irq_flags; + int rc = 0; + + if (!pdata || !pdata->core_pdata || !pdata->mt_pdata) { + dev_err(dev, "%s: Missing platform data\n", __func__); + rc = -ENODEV; + goto error_no_pdata; + } + + cd = kzalloc(sizeof(*cd), GFP_KERNEL); + if (!cd) { + dev_err(dev, "%s: Error, kzalloc\n", __func__); + rc = -ENOMEM; + goto error_alloc_data; + } + + cd->xfer_buf = kzalloc(xfer_buf_size, GFP_KERNEL); + if (!cd->xfer_buf) { + dev_err(dev, "%s: Error, kzalloc\n", __func__); + rc = -ENOMEM; + goto error_alloc_data; + } + + /* Initialize device info */ + cd->dev = dev; + cd->pdata = pdata; + cd->cpdata = pdata->core_pdata; + cd->bus_ops = ops; + + /* Initialize mutexes and spinlocks */ + mutex_init(&cd->system_lock); + mutex_init(&cd->adap_lock); + + /* Initialize wait queue */ + init_waitqueue_head(&cd->wait_q); + + /* Initialize works */ + INIT_WORK(&cd->startup_work, cyttsp4_startup_work_function); + INIT_WORK(&cd->watchdog_work, cyttsp4_watchdog_work); + + /* Initialize IRQ */ + cd->irq = gpio_to_irq(cd->cpdata->irq_gpio); + if (cd->irq < 0) { + rc = -EINVAL; + goto error_gpio_irq; + } + + dev_set_drvdata(dev, cd); + + /* Call platform init function */ + if (cd->cpdata->init) { + dev_dbg(cd->dev, "%s: Init HW\n", __func__); + rc = cd->cpdata->init(cd->cpdata, 1, cd->dev); + } else { + dev_dbg(cd->dev, "%s: No HW INIT function\n", __func__); + rc = 0; + } + if (rc < 0) + dev_err(cd->dev, "%s: HW Init fail r=%d\n", __func__, rc); + + dev_dbg(dev, "%s: initialize threaded irq=%d\n", __func__, cd->irq); + if (cd->cpdata->level_irq_udelay > 0) + /* use level triggered interrupts */ + irq_flags = IRQF_TRIGGER_LOW | IRQF_ONESHOT; + else + /* use edge triggered interrupts */ + irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + + rc = request_threaded_irq(cd->irq, NULL, cyttsp4_irq, irq_flags, + dev_name(dev), cd); + if (rc < 0) { + dev_err(dev, "%s: Error, could not request irq\n", __func__); + goto error_request_irq; + } + + /* Setup watchdog timer */ + setup_timer(&cd->watchdog_timer, cyttsp4_watchdog_timer, + (unsigned long)cd); + + /* + * call startup directly to ensure that the device + * is tested before leaving the probe + */ + rc = cyttsp4_startup(cd); + + /* Do not fail probe if startup fails but the device is detected */ + if (rc < 0 && cd->mode == CY_MODE_UNKNOWN) { + dev_err(cd->dev, "%s: Fail initial startup r=%d\n", + __func__, rc); + goto error_startup; + } + + rc = cyttsp4_mt_probe(cd); + if (rc < 0) { + dev_err(dev, "%s: Error, fail mt probe\n", __func__); + goto error_startup; + } + + pm_runtime_enable(dev); + + return cd; + +error_startup: + cancel_work_sync(&cd->startup_work); + cyttsp4_stop_wd_timer(cd); + pm_runtime_disable(dev); + cyttsp4_free_si_ptrs(cd); + free_irq(cd->irq, cd); +error_request_irq: + if (cd->cpdata->init) + cd->cpdata->init(cd->cpdata, 0, dev); + dev_set_drvdata(dev, NULL); +error_gpio_irq: + kfree(cd); +error_alloc_data: +error_no_pdata: + dev_err(dev, "%s failed.\n", __func__); + return ERR_PTR(rc); +} +EXPORT_SYMBOL_GPL(cyttsp4_probe); + +static void cyttsp4_mt_release(struct cyttsp4_mt_data *md) +{ + input_unregister_device(md->input); + input_set_drvdata(md->input, NULL); +} + +int cyttsp4_remove(struct cyttsp4 *cd) +{ + struct device *dev = cd->dev; + + cyttsp4_mt_release(&cd->md); + + /* + * Suspend the device before freeing the startup_work and stopping + * the watchdog since sleep function restarts watchdog on failure + */ + pm_runtime_suspend(dev); + pm_runtime_disable(dev); + + cancel_work_sync(&cd->startup_work); + + cyttsp4_stop_wd_timer(cd); + + free_irq(cd->irq, cd); + if (cd->cpdata->init) + cd->cpdata->init(cd->cpdata, 0, dev); + dev_set_drvdata(dev, NULL); + cyttsp4_free_si_ptrs(cd); + kfree(cd); + return 0; +} +EXPORT_SYMBOL_GPL(cyttsp4_remove); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard touchscreen core driver"); +MODULE_AUTHOR("Cypress"); diff --git a/drivers/input/touchscreen/cyttsp4_core.h b/drivers/input/touchscreen/cyttsp4_core.h new file mode 100644 index 000000000000..86a254354136 --- /dev/null +++ b/drivers/input/touchscreen/cyttsp4_core.h @@ -0,0 +1,472 @@ +/* + * cyttsp4_core.h + * Cypress TrueTouch(TM) Standard Product V4 Core driver module. + * For use with Cypress Txx4xx parts. + * Supported parts include: + * TMA4XX + * TMA1036 + * + * Copyright (C) 2012 Cypress Semiconductor + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Contact Cypress Semiconductor at www.cypress.com + * + */ + +#ifndef _LINUX_CYTTSP4_CORE_H +#define _LINUX_CYTTSP4_CORE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CY_REG_BASE 0x00 + +#define CY_POST_CODEL_WDG_RST 0x01 +#define CY_POST_CODEL_CFG_DATA_CRC_FAIL 0x02 +#define CY_POST_CODEL_PANEL_TEST_FAIL 0x04 + +#define CY_NUM_BTN_PER_REG 4 + +/* touch record system information offset masks and shifts */ +#define CY_BYTE_OFS_MASK 0x1F +#define CY_BOFS_MASK 0xE0 +#define CY_BOFS_SHIFT 5 + +#define CY_TMA1036_TCH_REC_SIZE 6 +#define CY_TMA4XX_TCH_REC_SIZE 9 +#define CY_TMA1036_MAX_TCH 0x0E +#define CY_TMA4XX_MAX_TCH 0x1E + +#define CY_NORMAL_ORIGIN 0 /* upper, left corner */ +#define CY_INVERT_ORIGIN 1 /* lower, right corner */ + +/* helpers */ +#define GET_NUM_TOUCHES(x) ((x) & 0x1F) +#define IS_LARGE_AREA(x) ((x) & 0x20) +#define IS_BAD_PKT(x) ((x) & 0x20) +#define IS_BOOTLOADER(hst_mode, reset_detect) \ + ((hst_mode) & 0x01 || (reset_detect) != 0) +#define IS_TMO(t) ((t) == 0) + + +enum cyttsp_cmd_bits { + CY_CMD_COMPLETE = (1 << 6), +}; + +/* Timeout in ms. */ +#define CY_WATCHDOG_TIMEOUT 1000 + +#define CY_MAX_PRINT_SIZE 512 +#ifdef VERBOSE_DEBUG +#define CY_MAX_PRBUF_SIZE PIPE_BUF +#define CY_PR_TRUNCATED " truncated..." +#endif + +enum cyttsp4_ic_grpnum { + CY_IC_GRPNUM_RESERVED, + CY_IC_GRPNUM_CMD_REGS, + CY_IC_GRPNUM_TCH_REP, + CY_IC_GRPNUM_DATA_REC, + CY_IC_GRPNUM_TEST_REC, + CY_IC_GRPNUM_PCFG_REC, + CY_IC_GRPNUM_TCH_PARM_VAL, + CY_IC_GRPNUM_TCH_PARM_SIZE, + CY_IC_GRPNUM_RESERVED1, + CY_IC_GRPNUM_RESERVED2, + CY_IC_GRPNUM_OPCFG_REC, + CY_IC_GRPNUM_DDATA_REC, + CY_IC_GRPNUM_MDATA_REC, + CY_IC_GRPNUM_TEST_REGS, + CY_IC_GRPNUM_BTN_KEYS, + CY_IC_GRPNUM_TTHE_REGS, + CY_IC_GRPNUM_NUM +}; + +enum cyttsp4_int_state { + CY_INT_NONE, + CY_INT_IGNORE = (1 << 0), + CY_INT_MODE_CHANGE = (1 << 1), + CY_INT_EXEC_CMD = (1 << 2), + CY_INT_AWAKE = (1 << 3), +}; + +enum cyttsp4_mode { + CY_MODE_UNKNOWN, + CY_MODE_BOOTLOADER = (1 << 1), + CY_MODE_OPERATIONAL = (1 << 2), + CY_MODE_SYSINFO = (1 << 3), + CY_MODE_CAT = (1 << 4), + CY_MODE_STARTUP = (1 << 5), + CY_MODE_LOADER = (1 << 6), + CY_MODE_CHANGE_MODE = (1 << 7), + CY_MODE_CHANGED = (1 << 8), + CY_MODE_CMD_COMPLETE = (1 << 9), +}; + +enum cyttsp4_sleep_state { + SS_SLEEP_OFF, + SS_SLEEP_ON, + SS_SLEEPING, + SS_WAKING, +}; + +enum cyttsp4_startup_state { + STARTUP_NONE, + STARTUP_QUEUED, + STARTUP_RUNNING, +}; + +#define CY_NUM_REVCTRL 8 +struct cyttsp4_cydata { + u8 ttpidh; + u8 ttpidl; + u8 fw_ver_major; + u8 fw_ver_minor; + u8 revctrl[CY_NUM_REVCTRL]; + u8 blver_major; + u8 blver_minor; + u8 jtag_si_id3; + u8 jtag_si_id2; + u8 jtag_si_id1; + u8 jtag_si_id0; + u8 mfgid_sz; + u8 cyito_idh; + u8 cyito_idl; + u8 cyito_verh; + u8 cyito_verl; + u8 ttsp_ver_major; + u8 ttsp_ver_minor; + u8 device_info; + u8 mfg_id[]; +} __packed; + +struct cyttsp4_test { + u8 post_codeh; + u8 post_codel; +} __packed; + +struct cyttsp4_pcfg { + u8 electrodes_x; + u8 electrodes_y; + u8 len_xh; + u8 len_xl; + u8 len_yh; + u8 len_yl; + u8 res_xh; + u8 res_xl; + u8 res_yh; + u8 res_yl; + u8 max_zh; + u8 max_zl; + u8 panel_info0; +} __packed; + +struct cyttsp4_tch_rec_params { + u8 loc; + u8 size; +} __packed; + +#define CY_NUM_TCH_FIELDS 7 +#define CY_NUM_EXT_TCH_FIELDS 3 +struct cyttsp4_opcfg { + u8 cmd_ofs; + u8 rep_ofs; + u8 rep_szh; + u8 rep_szl; + u8 num_btns; + u8 tt_stat_ofs; + u8 obj_cfg0; + u8 max_tchs; + u8 tch_rec_size; + struct cyttsp4_tch_rec_params tch_rec_old[CY_NUM_TCH_FIELDS]; + u8 btn_rec_size; /* btn record size (in bytes) */ + u8 btn_diff_ofs; /* btn data loc, diff counts */ + u8 btn_diff_size; /* btn size of diff counts (in bits) */ + struct cyttsp4_tch_rec_params tch_rec_new[CY_NUM_EXT_TCH_FIELDS]; +} __packed; + +struct cyttsp4_sysinfo_ptr { + struct cyttsp4_cydata *cydata; + struct cyttsp4_test *test; + struct cyttsp4_pcfg *pcfg; + struct cyttsp4_opcfg *opcfg; + struct cyttsp4_ddata *ddata; + struct cyttsp4_mdata *mdata; +} __packed; + +struct cyttsp4_sysinfo_data { + u8 hst_mode; + u8 reserved; + u8 map_szh; + u8 map_szl; + u8 cydata_ofsh; + u8 cydata_ofsl; + u8 test_ofsh; + u8 test_ofsl; + u8 pcfg_ofsh; + u8 pcfg_ofsl; + u8 opcfg_ofsh; + u8 opcfg_ofsl; + u8 ddata_ofsh; + u8 ddata_ofsl; + u8 mdata_ofsh; + u8 mdata_ofsl; +} __packed; + +enum cyttsp4_tch_abs { /* for ordering within the extracted touch data array */ + CY_TCH_X, /* X */ + CY_TCH_Y, /* Y */ + CY_TCH_P, /* P (Z) */ + CY_TCH_T, /* TOUCH ID */ + CY_TCH_E, /* EVENT ID */ + CY_TCH_O, /* OBJECT ID */ + CY_TCH_W, /* SIZE */ + CY_TCH_MAJ, /* TOUCH_MAJOR */ + CY_TCH_MIN, /* TOUCH_MINOR */ + CY_TCH_OR, /* ORIENTATION */ + CY_TCH_NUM_ABS +}; + +static const char * const cyttsp4_tch_abs_string[] = { + [CY_TCH_X] = "X", + [CY_TCH_Y] = "Y", + [CY_TCH_P] = "P", + [CY_TCH_T] = "T", + [CY_TCH_E] = "E", + [CY_TCH_O] = "O", + [CY_TCH_W] = "W", + [CY_TCH_MAJ] = "MAJ", + [CY_TCH_MIN] = "MIN", + [CY_TCH_OR] = "OR", + [CY_TCH_NUM_ABS] = "INVALID" +}; + +struct cyttsp4_touch { + int abs[CY_TCH_NUM_ABS]; +}; + +struct cyttsp4_tch_abs_params { + size_t ofs; /* abs byte offset */ + size_t size; /* size in bits */ + size_t max; /* max value */ + size_t bofs; /* bit offset */ +}; + +struct cyttsp4_sysinfo_ofs { + size_t chip_type; + size_t cmd_ofs; + size_t rep_ofs; + size_t rep_sz; + size_t num_btns; + size_t num_btn_regs; /* ceil(num_btns/4) */ + size_t tt_stat_ofs; + size_t tch_rec_size; + size_t obj_cfg0; + size_t max_tchs; + size_t mode_size; + size_t data_size; + size_t map_sz; + size_t max_x; + size_t x_origin; /* left or right corner */ + size_t max_y; + size_t y_origin; /* upper or lower corner */ + size_t max_p; + size_t cydata_ofs; + size_t test_ofs; + size_t pcfg_ofs; + size_t opcfg_ofs; + size_t ddata_ofs; + size_t mdata_ofs; + size_t cydata_size; + size_t test_size; + size_t pcfg_size; + size_t opcfg_size; + size_t ddata_size; + size_t mdata_size; + size_t btn_keys_size; + struct cyttsp4_tch_abs_params tch_abs[CY_TCH_NUM_ABS]; + size_t btn_rec_size; /* btn record size (in bytes) */ + size_t btn_diff_ofs;/* btn data loc ,diff counts, (Op-Mode byte ofs) */ + size_t btn_diff_size;/* btn size of diff counts (in bits) */ +}; + +enum cyttsp4_btn_state { + CY_BTN_RELEASED, + CY_BTN_PRESSED, + CY_BTN_NUM_STATE +}; + +struct cyttsp4_btn { + bool enabled; + int state; /* CY_BTN_PRESSED, CY_BTN_RELEASED */ + int key_code; +}; + +struct cyttsp4_sysinfo { + bool ready; + struct cyttsp4_sysinfo_data si_data; + struct cyttsp4_sysinfo_ptr si_ptrs; + struct cyttsp4_sysinfo_ofs si_ofs; + struct cyttsp4_btn *btn; /* button states */ + u8 *btn_rec_data; /* button diff count data */ + u8 *xy_mode; /* operational mode and status regs */ + u8 *xy_data; /* operational touch regs */ +}; + +struct cyttsp4_mt_data { + struct cyttsp4_mt_platform_data *pdata; + struct cyttsp4_sysinfo *si; + struct input_dev *input; + struct mutex report_lock; + bool is_suspended; + char phys[NAME_MAX]; + int num_prv_tch; +}; + +struct cyttsp4 { + struct device *dev; + struct mutex system_lock; + struct mutex adap_lock; + enum cyttsp4_mode mode; + enum cyttsp4_sleep_state sleep_state; + enum cyttsp4_startup_state startup_state; + int int_status; + wait_queue_head_t wait_q; + int irq; + struct work_struct startup_work; + struct work_struct watchdog_work; + struct timer_list watchdog_timer; + struct cyttsp4_sysinfo sysinfo; + void *exclusive_dev; + int exclusive_waits; + atomic_t ignore_irq; + bool invalid_touch_app; + struct cyttsp4_mt_data md; + struct cyttsp4_platform_data *pdata; + struct cyttsp4_core_platform_data *cpdata; + const struct cyttsp4_bus_ops *bus_ops; + u8 *xfer_buf; +#ifdef VERBOSE_DEBUG + u8 pr_buf[CY_MAX_PRBUF_SIZE]; +#endif +}; + +struct cyttsp4_bus_ops { + u16 bustype; + int (*write)(struct device *dev, u8 *xfer_buf, u8 addr, u8 length, + const void *values); + int (*read)(struct device *dev, u8 *xfer_buf, u8 addr, u8 length, + void *values); +}; + +enum cyttsp4_hst_mode_bits { + CY_HST_TOGGLE = (1 << 7), + CY_HST_MODE_CHANGE = (1 << 3), + CY_HST_MODE = (7 << 4), + CY_HST_OPERATE = (0 << 4), + CY_HST_SYSINFO = (1 << 4), + CY_HST_CAT = (2 << 4), + CY_HST_LOWPOW = (1 << 2), + CY_HST_SLEEP = (1 << 1), + CY_HST_RESET = (1 << 0), +}; + +/* abs settings */ +#define CY_IGNORE_VALUE 0xFFFF + +/* abs signal capabilities offsets in the frameworks array */ +enum cyttsp4_sig_caps { + CY_SIGNAL_OST, + CY_MIN_OST, + CY_MAX_OST, + CY_FUZZ_OST, + CY_FLAT_OST, + CY_NUM_ABS_SET /* number of signal capability fields */ +}; + +/* abs axis signal offsets in the framworks array */ +enum cyttsp4_sig_ost { + CY_ABS_X_OST, + CY_ABS_Y_OST, + CY_ABS_P_OST, + CY_ABS_W_OST, + CY_ABS_ID_OST, + CY_ABS_MAJ_OST, + CY_ABS_MIN_OST, + CY_ABS_OR_OST, + CY_NUM_ABS_OST /* number of abs signals */ +}; + +enum cyttsp4_flags { + CY_FLAG_NONE = 0x00, + CY_FLAG_HOVER = 0x04, + CY_FLAG_FLIP = 0x08, + CY_FLAG_INV_X = 0x10, + CY_FLAG_INV_Y = 0x20, + CY_FLAG_VKEYS = 0x40, +}; + +enum cyttsp4_object_id { + CY_OBJ_STANDARD_FINGER, + CY_OBJ_LARGE_OBJECT, + CY_OBJ_STYLUS, + CY_OBJ_HOVER, +}; + +enum cyttsp4_event_id { + CY_EV_NO_EVENT, + CY_EV_TOUCHDOWN, + CY_EV_MOVE, /* significant displacement (> act dist) */ + CY_EV_LIFTOFF, /* record reports last position */ +}; + +/* x-axis resolution of panel in pixels */ +#define CY_PCFG_RESOLUTION_X_MASK 0x7F + +/* y-axis resolution of panel in pixels */ +#define CY_PCFG_RESOLUTION_Y_MASK 0x7F + +/* x-axis, 0:origin is on left side of panel, 1: right */ +#define CY_PCFG_ORIGIN_X_MASK 0x80 + +/* y-axis, 0:origin is on top side of panel, 1: bottom */ +#define CY_PCFG_ORIGIN_Y_MASK 0x80 + +static inline int cyttsp4_adap_read(struct cyttsp4 *ts, u8 addr, int size, + void *buf) +{ + return ts->bus_ops->read(ts->dev, ts->xfer_buf, addr, size, buf); +} + +static inline int cyttsp4_adap_write(struct cyttsp4 *ts, u8 addr, int size, + const void *buf) +{ + return ts->bus_ops->write(ts->dev, ts->xfer_buf, addr, size, buf); +} + +extern struct cyttsp4 *cyttsp4_probe(const struct cyttsp4_bus_ops *ops, + struct device *dev, u16 irq, size_t xfer_buf_size); +extern int cyttsp4_remove(struct cyttsp4 *ts); +int cyttsp_i2c_write_block_data(struct device *dev, u8 *xfer_buf, u8 addr, + u8 length, const void *values); +int cyttsp_i2c_read_block_data(struct device *dev, u8 *xfer_buf, u8 addr, + u8 length, void *values); +extern const struct dev_pm_ops cyttsp4_pm_ops; + +#endif /* _LINUX_CYTTSP4_CORE_H */ diff --git a/include/linux/platform_data/cyttsp4.h b/include/linux/platform_data/cyttsp4.h new file mode 100644 index 000000000000..6eba54aff1dc --- /dev/null +++ b/include/linux/platform_data/cyttsp4.h @@ -0,0 +1,76 @@ +/* + * Header file for: + * Cypress TrueTouch(TM) Standard Product (TTSP) touchscreen drivers. + * For use with Cypress Txx3xx parts. + * Supported parts include: + * CY8CTST341 + * CY8CTMA340 + * + * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. + * Copyright (C) 2012 Javier Martinez Canillas + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contact Cypress Semiconductor at www.cypress.com (kev@cypress.com) + * + */ +#ifndef _CYTTSP4_H_ +#define _CYTTSP4_H_ + +#define CYTTSP4_MT_NAME "cyttsp4_mt" +#define CYTTSP4_I2C_NAME "cyttsp4_i2c_adapter" +#define CYTTSP4_SPI_NAME "cyttsp4_spi_adapter" + +#define CY_TOUCH_SETTINGS_MAX 32 + +struct touch_framework { + const uint16_t *abs; + uint8_t size; + uint8_t enable_vkeys; +} __packed; + +struct cyttsp4_mt_platform_data { + struct touch_framework *frmwrk; + unsigned short flags; + char const *inp_dev_name; +}; + +struct touch_settings { + const uint8_t *data; + uint32_t size; + uint8_t tag; +} __packed; + +struct cyttsp4_core_platform_data { + int irq_gpio; + int rst_gpio; + int level_irq_udelay; + int (*xres)(struct cyttsp4_core_platform_data *pdata, + struct device *dev); + int (*init)(struct cyttsp4_core_platform_data *pdata, + int on, struct device *dev); + int (*power)(struct cyttsp4_core_platform_data *pdata, + int on, struct device *dev, atomic_t *ignore_irq); + int (*irq_stat)(struct cyttsp4_core_platform_data *pdata, + struct device *dev); + struct touch_settings *sett[CY_TOUCH_SETTINGS_MAX]; +}; + +struct cyttsp4_platform_data { + struct cyttsp4_core_platform_data *core_pdata; + struct cyttsp4_mt_platform_data *mt_pdata; +}; + +#endif /* _CYTTSP4_H_ */ -- cgit v1.2.3 From 4f9e868007005bccbec0517a632e873fa70e98c4 Mon Sep 17 00:00:00 2001 From: Ferruh Yigit Date: Sun, 30 Jun 2013 18:49:44 -0700 Subject: Input: cyttsp4 - I2C driver for Cypress TMA4XX touchscreen devices Cypress TrueTouch(tm) Standard Product controllers, Generation4 devices, I2C adapter module. This driver adds communication support with TTSP controller using I2C bus. Signed-off-by: Ferruh Yigit Acked-by: Greg Kroah-Hartman Acked-by: Javier Martinez Canillas Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 9 ++++ drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/cyttsp4_i2c.c | 90 +++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+) create mode 100644 drivers/input/touchscreen/cyttsp4_i2c.c diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 66df8df88140..1645e8e4e788 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -179,6 +179,15 @@ config TOUCHSCREEN_CYTTSP4_CORE To compile this driver as a module, choose M here. +config TOUCHSCREEN_CYTTSP4_I2C + tristate "support I2C bus connection" + depends on TOUCHSCREEN_CYTTSP4_CORE && I2C + help + Say Y here if the touchscreen is connected via I2C bus. + + To compile this driver as a module, choose M here: the + module will be called cyttsp4_i2c. + config TOUCHSCREEN_DA9034 tristate "Touchscreen support for Dialog Semiconductor DA9034" depends on PMIC_DA903X diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index f44500674c32..1ef807c7d870 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o cyttsp_i2c_common.o obj-$(CONFIG_TOUCHSCREEN_CYTTSP_SPI) += cyttsp_spi.o obj-$(CONFIG_TOUCHSCREEN_CYTTSP4_CORE) += cyttsp4_core.o +obj-$(CONFIG_TOUCHSCREEN_CYTTSP4_I2C) += cyttsp4_i2c.o cyttsp_i2c_common.o obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o obj-$(CONFIG_TOUCHSCREEN_DA9052) += da9052_tsi.o obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o diff --git a/drivers/input/touchscreen/cyttsp4_i2c.c b/drivers/input/touchscreen/cyttsp4_i2c.c new file mode 100644 index 000000000000..8e2012c79058 --- /dev/null +++ b/drivers/input/touchscreen/cyttsp4_i2c.c @@ -0,0 +1,90 @@ +/* + * cyttsp_i2c.c + * Cypress TrueTouch(TM) Standard Product (TTSP) I2C touchscreen driver. + * For use with Cypress Txx4xx parts. + * Supported parts include: + * TMA4XX + * TMA1036 + * + * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. + * Copyright (C) 2012 Javier Martinez Canillas + * Copyright (C) 2013 Cypress Semiconductor + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Contact Cypress Semiconductor at www.cypress.com + * + */ + +#include "cyttsp4_core.h" + +#include +#include + +#define CYTTSP4_I2C_DATA_SIZE (3 * 256) + +static const struct cyttsp4_bus_ops cyttsp4_i2c_bus_ops = { + .bustype = BUS_I2C, + .write = cyttsp_i2c_write_block_data, + .read = cyttsp_i2c_read_block_data, +}; + +static int cyttsp4_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct cyttsp4 *ts; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "I2C functionality not Supported\n"); + return -EIO; + } + + ts = cyttsp4_probe(&cyttsp4_i2c_bus_ops, &client->dev, client->irq, + CYTTSP4_I2C_DATA_SIZE); + + if (IS_ERR(ts)) + return PTR_ERR(ts); + + return 0; +} + +static int cyttsp4_i2c_remove(struct i2c_client *client) +{ + struct cyttsp4 *ts = i2c_get_clientdata(client); + + cyttsp4_remove(ts); + + return 0; +} + +static const struct i2c_device_id cyttsp4_i2c_id[] = { + { CYTTSP4_I2C_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cyttsp4_i2c_id); + +static struct i2c_driver cyttsp4_i2c_driver = { + .driver = { + .name = CYTTSP4_I2C_NAME, + .owner = THIS_MODULE, + .pm = &cyttsp4_pm_ops, + }, + .probe = cyttsp4_i2c_probe, + .remove = cyttsp4_i2c_remove, + .id_table = cyttsp4_i2c_id, +}; + +module_i2c_driver(cyttsp4_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) I2C driver"); +MODULE_AUTHOR("Cypress"); +MODULE_ALIAS("i2c:cyttsp4"); -- cgit v1.2.3 From 67bf12ca50d524f9e225347fe63533562e2004de Mon Sep 17 00:00:00 2001 From: Ferruh Yigit Date: Sun, 30 Jun 2013 18:50:05 -0700 Subject: Input: cyttsp4 - SPI driver for Cypress TMA4XX touchscreen devices Cypress TrueTouch(tm) Standard Product controllers, Generation4 devices, SPI adapter module. This driver adds communication support with TTSP controller using SPI bus. Signed-off-by: Ferruh Yigit Acked-by: Greg Kroah-Hartman Acked-by: Javier Martinez Canillas Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 9 ++ drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/cyttsp4_spi.c | 205 ++++++++++++++++++++++++++++++++ 3 files changed, 215 insertions(+) create mode 100644 drivers/input/touchscreen/cyttsp4_spi.c diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 1645e8e4e788..2d70089c1183 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -188,6 +188,15 @@ config TOUCHSCREEN_CYTTSP4_I2C To compile this driver as a module, choose M here: the module will be called cyttsp4_i2c. +config TOUCHSCREEN_CYTTSP4_SPI + tristate "support SPI bus connection" + depends on TOUCHSCREEN_CYTTSP4_CORE && SPI_MASTER + help + Say Y here if the touchscreen is connected via SPI bus. + + To compile this driver as a module, choose M here: the + module will be called cyttsp4_spi. + config TOUCHSCREEN_DA9034 tristate "Touchscreen support for Dialog Semiconductor DA9034" depends on PMIC_DA903X diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 1ef807c7d870..f5216c1bf53e 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o cyttsp_i2c_common.o obj-$(CONFIG_TOUCHSCREEN_CYTTSP_SPI) += cyttsp_spi.o obj-$(CONFIG_TOUCHSCREEN_CYTTSP4_CORE) += cyttsp4_core.o obj-$(CONFIG_TOUCHSCREEN_CYTTSP4_I2C) += cyttsp4_i2c.o cyttsp_i2c_common.o +obj-$(CONFIG_TOUCHSCREEN_CYTTSP4_SPI) += cyttsp4_spi.o obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o obj-$(CONFIG_TOUCHSCREEN_DA9052) += da9052_tsi.o obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o diff --git a/drivers/input/touchscreen/cyttsp4_spi.c b/drivers/input/touchscreen/cyttsp4_spi.c new file mode 100644 index 000000000000..f8f891bead34 --- /dev/null +++ b/drivers/input/touchscreen/cyttsp4_spi.c @@ -0,0 +1,205 @@ +/* + * Source for: + * Cypress TrueTouch(TM) Standard Product (TTSP) SPI touchscreen driver. + * For use with Cypress Txx4xx parts. + * Supported parts include: + * TMA4XX + * TMA1036 + * + * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. + * Copyright (C) 2012 Javier Martinez Canillas + * Copyright (C) 2013 Cypress Semiconductor + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Contact Cypress Semiconductor at www.cypress.com + * + */ + +#include "cyttsp4_core.h" + +#include +#include +#include + +#define CY_SPI_WR_OP 0x00 /* r/~w */ +#define CY_SPI_RD_OP 0x01 +#define CY_SPI_BITS_PER_WORD 8 +#define CY_SPI_A8_BIT 0x02 +#define CY_SPI_WR_HEADER_BYTES 2 +#define CY_SPI_RD_HEADER_BYTES 1 +#define CY_SPI_CMD_BYTES 2 +#define CY_SPI_SYNC_BYTE 0 +#define CY_SPI_SYNC_ACK 0x62 /* from TRM *A protocol */ +#define CY_SPI_DATA_SIZE (2 * 256) + +#define CY_SPI_DATA_BUF_SIZE (CY_SPI_CMD_BYTES + CY_SPI_DATA_SIZE) + +static int cyttsp_spi_xfer(struct device *dev, u8 *xfer_buf, + u8 op, u8 reg, u8 *buf, int length) +{ + struct spi_device *spi = to_spi_device(dev); + struct spi_message msg; + struct spi_transfer xfer[2]; + u8 *wr_buf = &xfer_buf[0]; + u8 rd_buf[CY_SPI_CMD_BYTES]; + int retval; + int i; + + if (length > CY_SPI_DATA_SIZE) { + dev_err(dev, "%s: length %d is too big.\n", + __func__, length); + return -EINVAL; + } + + memset(wr_buf, 0, CY_SPI_DATA_BUF_SIZE); + memset(rd_buf, 0, CY_SPI_CMD_BYTES); + + if (reg > 255) + wr_buf[0] = op + CY_SPI_A8_BIT; + else + wr_buf[0] = op; + if (op == CY_SPI_WR_OP) + wr_buf[1] = reg % 256; + if (op == CY_SPI_WR_OP && length > 0) + memcpy(wr_buf + CY_SPI_CMD_BYTES, buf, length); + + memset(xfer, 0, sizeof(xfer)); + spi_message_init(&msg); + + /* + We set both TX and RX buffers because Cypress TTSP + requires full duplex operation. + */ + xfer[0].tx_buf = wr_buf; + xfer[0].rx_buf = rd_buf; + switch (op) { + case CY_SPI_WR_OP: + xfer[0].len = length + CY_SPI_CMD_BYTES; + spi_message_add_tail(&xfer[0], &msg); + break; + + case CY_SPI_RD_OP: + xfer[0].len = CY_SPI_RD_HEADER_BYTES; + spi_message_add_tail(&xfer[0], &msg); + + xfer[1].rx_buf = buf; + xfer[1].len = length; + spi_message_add_tail(&xfer[1], &msg); + break; + + default: + dev_err(dev, "%s: bad operation code=%d\n", __func__, op); + return -EINVAL; + } + + retval = spi_sync(spi, &msg); + if (retval < 0) { + dev_dbg(dev, "%s: spi_sync() error %d, len=%d, op=%d\n", + __func__, retval, xfer[1].len, op); + + /* + * do not return here since was a bad ACK sequence + * let the following ACK check handle any errors and + * allow silent retries + */ + } + + if (rd_buf[CY_SPI_SYNC_BYTE] != CY_SPI_SYNC_ACK) { + dev_dbg(dev, "%s: operation %d failed\n", __func__, op); + + for (i = 0; i < CY_SPI_CMD_BYTES; i++) + dev_dbg(dev, "%s: test rd_buf[%d]:0x%02x\n", + __func__, i, rd_buf[i]); + for (i = 0; i < length; i++) + dev_dbg(dev, "%s: test buf[%d]:0x%02x\n", + __func__, i, buf[i]); + + return -EIO; + } + + return 0; +} + +static int cyttsp_spi_read_block_data(struct device *dev, u8 *xfer_buf, + u8 addr, u8 length, void *data) +{ + int rc; + + rc = cyttsp_spi_xfer(dev, xfer_buf, CY_SPI_WR_OP, addr, NULL, 0); + if (rc) + return rc; + else + return cyttsp_spi_xfer(dev, xfer_buf, CY_SPI_RD_OP, addr, data, + length); +} + +static int cyttsp_spi_write_block_data(struct device *dev, u8 *xfer_buf, + u8 addr, u8 length, const void *data) +{ + return cyttsp_spi_xfer(dev, xfer_buf, CY_SPI_WR_OP, addr, (void *)data, + length); +} + +static const struct cyttsp4_bus_ops cyttsp_spi_bus_ops = { + .bustype = BUS_SPI, + .write = cyttsp_spi_write_block_data, + .read = cyttsp_spi_read_block_data, +}; + +static int cyttsp4_spi_probe(struct spi_device *spi) +{ + struct cyttsp4 *ts; + int error; + + /* Set up SPI*/ + spi->bits_per_word = CY_SPI_BITS_PER_WORD; + spi->mode = SPI_MODE_0; + error = spi_setup(spi); + if (error < 0) { + dev_err(&spi->dev, "%s: SPI setup error %d\n", + __func__, error); + return error; + } + + ts = cyttsp4_probe(&cyttsp_spi_bus_ops, &spi->dev, spi->irq, + CY_SPI_DATA_BUF_SIZE); + + if (IS_ERR(ts)) + return PTR_ERR(ts); + + return 0; +} + +static int cyttsp4_spi_remove(struct spi_device *spi) +{ + struct cyttsp4 *ts = spi_get_drvdata(spi); + cyttsp4_remove(ts); + + return 0; +} + +static struct spi_driver cyttsp4_spi_driver = { + .driver = { + .name = CYTTSP4_SPI_NAME, + .owner = THIS_MODULE, + .pm = &cyttsp4_pm_ops, + }, + .probe = cyttsp4_spi_probe, + .remove = cyttsp4_spi_remove, +}; + +module_spi_driver(cyttsp4_spi_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) SPI driver"); +MODULE_AUTHOR("Cypress"); +MODULE_ALIAS("spi:cyttsp4"); -- cgit v1.2.3 From 83ff59a066637a6c28844bbf43009459408240f4 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Fri, 3 May 2013 14:55:23 -0400 Subject: UBI: support ubi_num on mtd.ubi command line I want to be able to add UBI volumes with specific numbers, but the command line API doesn't have that atm. Add an additional token to support it. Artem: amended the patch a little bit. Signed-off-by: Mike Frysinger Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/build.c | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 8ff08ecb9383..a3503821d0c7 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -47,7 +47,7 @@ #define MTD_PARAM_LEN_MAX 64 /* Maximum number of comma-separated items in the 'mtd=' parameter */ -#define MTD_PARAM_MAX_COUNT 3 +#define MTD_PARAM_MAX_COUNT 4 /* Maximum value for the number of bad PEBs per 1024 PEBs */ #define MAX_MTD_UBI_BEB_LIMIT 768 @@ -67,6 +67,7 @@ */ struct mtd_dev_param { char name[MTD_PARAM_LEN_MAX]; + int ubi_num; int vid_hdr_offs; int max_beb_per1024; }; @@ -1269,7 +1270,7 @@ static int __init ubi_init(void) } mutex_lock(&ubi_devices_mutex); - err = ubi_attach_mtd_dev(mtd, UBI_DEV_NUM_AUTO, + err = ubi_attach_mtd_dev(mtd, p->ubi_num, p->vid_hdr_offs, p->max_beb_per1024); mutex_unlock(&ubi_devices_mutex); if (err < 0) { @@ -1387,7 +1388,7 @@ static int __init ubi_mtd_param_parse(const char *val, struct kernel_param *kp) struct mtd_dev_param *p; char buf[MTD_PARAM_LEN_MAX]; char *pbuf = &buf[0]; - char *tokens[MTD_PARAM_MAX_COUNT]; + char *tokens[MTD_PARAM_MAX_COUNT], *token; if (!val) return -EINVAL; @@ -1427,37 +1428,53 @@ static int __init ubi_mtd_param_parse(const char *val, struct kernel_param *kp) p = &mtd_dev_param[mtd_devs]; strcpy(&p->name[0], tokens[0]); - if (tokens[1]) - p->vid_hdr_offs = bytes_str_to_int(tokens[1]); + token = tokens[1]; + if (token) { + p->vid_hdr_offs = bytes_str_to_int(token); - if (p->vid_hdr_offs < 0) - return p->vid_hdr_offs; + if (p->vid_hdr_offs < 0) + return p->vid_hdr_offs; + } - if (tokens[2]) { - int err = kstrtoint(tokens[2], 10, &p->max_beb_per1024); + token = tokens[2]; + if (token) { + int err = kstrtoint(token, 10, &p->max_beb_per1024); if (err) { ubi_err("bad value for max_beb_per1024 parameter: %s", - tokens[2]); + token); return -EINVAL; } } + token = tokens[3]; + if (token) { + int err = kstrtoint(token, 10, &p->ubi_num); + + if (err) { + ubi_err("bad value for ubi_num parameter: %s", token); + return -EINVAL; + } + } else + p->ubi_num = UBI_DEV_NUM_AUTO; + mtd_devs += 1; return 0; } module_param_call(mtd, ubi_mtd_param_parse, NULL, NULL, 000); -MODULE_PARM_DESC(mtd, "MTD devices to attach. Parameter format: mtd=[,[,max_beb_per1024]].\n" +MODULE_PARM_DESC(mtd, "MTD devices to attach. Parameter format: mtd=[,[,max_beb_per1024[,ubi_num]]].\n" "Multiple \"mtd\" parameters may be specified.\n" "MTD devices may be specified by their number, name, or path to the MTD character device node.\n" "Optional \"vid_hdr_offs\" parameter specifies UBI VID header position to be used by UBI. (default value if 0)\n" "Optional \"max_beb_per1024\" parameter specifies the maximum expected bad eraseblock per 1024 eraseblocks. (default value (" __stringify(CONFIG_MTD_UBI_BEB_LIMIT) ") if 0)\n" + "Optional \"ubi_num\" parameter specifies UBI device number which have to be assigned to the newly created UBI device (assigned automatically by default)\n" "\n" "Example 1: mtd=/dev/mtd0 - attach MTD device /dev/mtd0.\n" "Example 2: mtd=content,1984 mtd=4 - attach MTD device with name \"content\" using VID header offset 1984, and MTD device number 4 with default VID header offset.\n" "Example 3: mtd=/dev/mtd1,0,25 - attach MTD device /dev/mtd1 using default VID header offset and reserve 25*nand_size_in_blocks/1024 erase blocks for bad block handling.\n" + "Example 4: mtd=/dev/mtd1,0,0,5 - attach MTD device /dev/mtd1 to UBI 5 and using default values for the other fields.\n" "\t(e.g. if the NAND *chipset* has 4096 PEB, 100 will be reserved for this UBI device)."); #ifdef CONFIG_MTD_UBI_FASTMAP module_param(fm_autoconvert, bool, 0644); -- cgit v1.2.3 From 88a8e4aa08f428da3a2a34890732a446ec9f2f5d Mon Sep 17 00:00:00 2001 From: Kevin Hilman Date: Fri, 28 Jun 2013 14:35:52 -0700 Subject: i2c: iop3xxx: fix build failure after waitqueue changes There has long been a syntax problem in iop3xx_i2c_wait_event() which has been somehow hidden by the macros in . After some recent cleanup/rework of the wait_event_* helpers, the bug has come out from hiding and now results in build failure: /work/kernel/next/drivers/i2c/busses/i2c-iop3xx.c: In function 'iop3xx_i2c_wait_event': /work/kernel/next/drivers/i2c/busses/i2c-iop3xx.c:176:143: error: expected ')' before ';' token /work/kernel/next/drivers/i2c/busses/i2c-iop3xx.c:176:157: error: expected ')' before ';' token /work/kernel/next/drivers/i2c/busses/i2c-iop3xx.c:176:213: error: expected ')' before ';' token /work/kernel/next/drivers/i2c/busses/i2c-iop3xx.c:176:291: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement] /work/kernel/next/drivers/i2c/busses/i2c-iop3xx.c:176:551: error: expected ')' before ';' token /work/kernel/next/drivers/i2c/busses/i2c-iop3xx.c:176:565: error: expected ')' before ';' token /work/kernel/next/drivers/i2c/busses/i2c-iop3xx.c:176:764: error: expected ')' before ';' token /work/kernel/next/drivers/i2c/busses/i2c-iop3xx.c:176:778: error: expected ')' b Fix by removing stray ';' Signed-off-by: Kevin Hilman Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-iop3xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-iop3xx.c b/drivers/i2c/busses/i2c-iop3xx.c index bc993331c695..dd24aa0424a9 100644 --- a/drivers/i2c/busses/i2c-iop3xx.c +++ b/drivers/i2c/busses/i2c-iop3xx.c @@ -176,7 +176,7 @@ iop3xx_i2c_wait_event(struct i2c_algo_iop3xx_data *iop3xx_adap, interrupted = wait_event_interruptible_timeout ( iop3xx_adap->waitq, (done = compare( sr = iop3xx_i2c_get_srstat(iop3xx_adap) ,flags )), - 1 * HZ; + 1 * HZ ); if ((rc = iop3xx_i2c_error(sr)) < 0) { *status = sr; -- cgit v1.2.3 From e0b9b7b06704eab2b95372a7c8daf9c0cce46bd0 Mon Sep 17 00:00:00 2001 From: Kevin Strasser Date: Sun, 23 Jun 2013 21:00:04 -0700 Subject: i2c: Kontron PLD i2c bus driver Add i2c support for the on-board PLD found on some Kontron embedded modules. Originally-From: Michael Brunner Signed-off-by: Kevin Strasser Acked-by: Guenter Roeck Acked-by: Darren Hart Signed-off-by: Wolfram Sang --- drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-kempld.c | 410 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 421 insertions(+) create mode 100644 drivers/i2c/busses/i2c-kempld.c diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index b865c8979aec..3c046097be71 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -485,6 +485,16 @@ config I2C_IOP3XX This driver can also be built as a module. If so, the module will be called i2c-iop3xx. +config I2C_KEMPLD + tristate "Kontron COM I2C Controller" + depends on MFD_KEMPLD + help + This enables support for the I2C bus interface on some Kontron ETX + and COMexpress (ETXexpress) modules. + + This driver can also be built as a module. If so, the module + will be called i2c-kempld. + config I2C_MPC tristate "MPC107/824x/85xx/512x/52xx/83xx/86xx" depends on PPC diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 385f99dd1b45..d00997f3eb3b 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -47,6 +47,7 @@ obj-$(CONFIG_I2C_HIGHLANDER) += i2c-highlander.o obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.o obj-$(CONFIG_I2C_IMX) += i2c-imx.o obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o +obj-$(CONFIG_I2C_KEMPLD) += i2c-kempld.o obj-$(CONFIG_I2C_MPC) += i2c-mpc.o obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o obj-$(CONFIG_I2C_MXS) += i2c-mxs.o diff --git a/drivers/i2c/busses/i2c-kempld.c b/drivers/i2c/busses/i2c-kempld.c new file mode 100644 index 000000000000..ccec916bc3eb --- /dev/null +++ b/drivers/i2c/busses/i2c-kempld.c @@ -0,0 +1,410 @@ +/* + * I2C bus driver for Kontron COM modules + * + * Copyright (c) 2010-2013 Kontron Europe GmbH + * Author: Michael Brunner + * + * The driver is based on the i2c-ocores driver by Peter Korsgaard. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License 2 as published + * by the Free Software Foundation. + * + * 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 +#include +#include +#include +#include + +#define KEMPLD_I2C_PRELOW 0x0b +#define KEMPLD_I2C_PREHIGH 0x0c +#define KEMPLD_I2C_DATA 0x0e + +#define KEMPLD_I2C_CTRL 0x0d +#define I2C_CTRL_IEN 0x40 +#define I2C_CTRL_EN 0x80 + +#define KEMPLD_I2C_STAT 0x0f +#define I2C_STAT_IF 0x01 +#define I2C_STAT_TIP 0x02 +#define I2C_STAT_ARBLOST 0x20 +#define I2C_STAT_BUSY 0x40 +#define I2C_STAT_NACK 0x80 + +#define KEMPLD_I2C_CMD 0x0f +#define I2C_CMD_START 0x91 +#define I2C_CMD_STOP 0x41 +#define I2C_CMD_READ 0x21 +#define I2C_CMD_WRITE 0x11 +#define I2C_CMD_READ_ACK 0x21 +#define I2C_CMD_READ_NACK 0x29 +#define I2C_CMD_IACK 0x01 + +#define KEMPLD_I2C_FREQ_MAX 2700 /* 2.7 mHz */ +#define KEMPLD_I2C_FREQ_STD 100 /* 100 kHz */ + +enum { + STATE_DONE = 0, + STATE_INIT, + STATE_ADDR, + STATE_ADDR10, + STATE_START, + STATE_WRITE, + STATE_READ, + STATE_ERROR, +}; + +struct kempld_i2c_data { + struct device *dev; + struct kempld_device_data *pld; + struct i2c_adapter adap; + struct i2c_msg *msg; + int pos; + int nmsgs; + int state; + bool was_active; +}; + +static unsigned int bus_frequency = KEMPLD_I2C_FREQ_STD; +module_param(bus_frequency, uint, 0); +MODULE_PARM_DESC(bus_frequency, "Set I2C bus frequency in kHz (default=" + __MODULE_STRING(KEMPLD_I2C_FREQ_STD)")"); + +static int i2c_bus = -1; +module_param(i2c_bus, int, 0); +MODULE_PARM_DESC(i2c_bus, "Set I2C bus number (default=-1 for dynamic assignment)"); + +static bool i2c_gpio_mux; +module_param(i2c_gpio_mux, bool, 0); +MODULE_PARM_DESC(i2c_gpio_mux, "Enable I2C port on GPIO out (default=false)"); + +/* + * kempld_get_mutex must be called prior to calling this function. + */ +static int kempld_i2c_process(struct kempld_i2c_data *i2c) +{ + struct kempld_device_data *pld = i2c->pld; + u8 stat = kempld_read8(pld, KEMPLD_I2C_STAT); + struct i2c_msg *msg = i2c->msg; + u8 addr; + + /* Ready? */ + if (stat & I2C_STAT_TIP) + return -EBUSY; + + if (i2c->state == STATE_DONE || i2c->state == STATE_ERROR) { + /* Stop has been sent */ + kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_IACK); + if (i2c->state == STATE_ERROR) + return -EIO; + return 0; + } + + /* Error? */ + if (stat & I2C_STAT_ARBLOST) { + i2c->state = STATE_ERROR; + kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_STOP); + return -EAGAIN; + } + + if (i2c->state == STATE_INIT) { + if (stat & I2C_STAT_BUSY) + return -EBUSY; + + i2c->state = STATE_ADDR; + } + + if (i2c->state == STATE_ADDR) { + /* 10 bit address? */ + if (i2c->msg->flags & I2C_M_TEN) { + addr = 0xf0 | ((i2c->msg->addr >> 7) & 0x6); + i2c->state = STATE_ADDR10; + } else { + addr = (i2c->msg->addr << 1); + i2c->state = STATE_START; + } + + /* Set read bit if necessary */ + addr |= (i2c->msg->flags & I2C_M_RD) ? 1 : 0; + + kempld_write8(pld, KEMPLD_I2C_DATA, addr); + kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_START); + + return 0; + } + + /* Second part of 10 bit addressing */ + if (i2c->state == STATE_ADDR10) { + kempld_write8(pld, KEMPLD_I2C_DATA, i2c->msg->addr & 0xff); + kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_WRITE); + + i2c->state = STATE_START; + return 0; + } + + if (i2c->state == STATE_START || i2c->state == STATE_WRITE) { + i2c->state = (msg->flags & I2C_M_RD) ? STATE_READ : STATE_WRITE; + + if (stat & I2C_STAT_NACK) { + i2c->state = STATE_ERROR; + kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_STOP); + return -ENXIO; + } + } else { + msg->buf[i2c->pos++] = kempld_read8(pld, KEMPLD_I2C_DATA); + } + + if (i2c->pos >= msg->len) { + i2c->nmsgs--; + i2c->msg++; + i2c->pos = 0; + msg = i2c->msg; + + if (i2c->nmsgs) { + if (!(msg->flags & I2C_M_NOSTART)) { + i2c->state = STATE_ADDR; + return 0; + } else { + i2c->state = (msg->flags & I2C_M_RD) + ? STATE_READ : STATE_WRITE; + } + } else { + i2c->state = STATE_DONE; + kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_STOP); + return 0; + } + } + + if (i2c->state == STATE_READ) { + kempld_write8(pld, KEMPLD_I2C_CMD, i2c->pos == (msg->len - 1) ? + I2C_CMD_READ_NACK : I2C_CMD_READ_ACK); + } else { + kempld_write8(pld, KEMPLD_I2C_DATA, msg->buf[i2c->pos++]); + kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_WRITE); + } + + return 0; +} + +static int kempld_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num) +{ + struct kempld_i2c_data *i2c = i2c_get_adapdata(adap); + struct kempld_device_data *pld = i2c->pld; + unsigned long timeout = jiffies + HZ; + int ret; + + i2c->msg = msgs; + i2c->pos = 0; + i2c->nmsgs = num; + i2c->state = STATE_INIT; + + /* Handle the transfer */ + while (time_before(jiffies, timeout)) { + kempld_get_mutex(pld); + ret = kempld_i2c_process(i2c); + kempld_release_mutex(pld); + + if (i2c->state == STATE_DONE || i2c->state == STATE_ERROR) + return (i2c->state == STATE_DONE) ? num : ret; + + if (ret == 0) + timeout = jiffies + HZ; + + usleep_range(5, 15); + } + + i2c->state = STATE_ERROR; + + return -ETIMEDOUT; +} + +/* + * kempld_get_mutex must be called prior to calling this function. + */ +static void kempld_i2c_device_init(struct kempld_i2c_data *i2c) +{ + struct kempld_device_data *pld = i2c->pld; + u16 prescale_corr; + long prescale; + u8 ctrl; + u8 stat; + u8 cfg; + + /* Make sure the device is disabled */ + ctrl = kempld_read8(pld, KEMPLD_I2C_CTRL); + ctrl &= ~(I2C_CTRL_EN | I2C_CTRL_IEN); + kempld_write8(pld, KEMPLD_I2C_CTRL, ctrl); + + if (bus_frequency > KEMPLD_I2C_FREQ_MAX) + bus_frequency = KEMPLD_I2C_FREQ_MAX; + + if (pld->info.spec_major == 1) + prescale = pld->pld_clock / bus_frequency * 5 - 1000; + else + prescale = pld->pld_clock / bus_frequency * 4 - 3000; + + if (prescale < 0) + prescale = 0; + + /* Round to the best matching value */ + prescale_corr = prescale / 1000; + if (prescale % 1000 >= 500) + prescale_corr++; + + kempld_write8(pld, KEMPLD_I2C_PRELOW, prescale_corr & 0xff); + kempld_write8(pld, KEMPLD_I2C_PREHIGH, prescale_corr >> 8); + + /* Activate I2C bus output on GPIO pins */ + cfg = kempld_read8(pld, KEMPLD_CFG); + if (i2c_gpio_mux) + cfg |= KEMPLD_CFG_GPIO_I2C_MUX; + else + cfg &= ~KEMPLD_CFG_GPIO_I2C_MUX; + kempld_write8(pld, KEMPLD_CFG, cfg); + + /* Enable the device */ + kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_IACK); + ctrl |= I2C_CTRL_EN; + kempld_write8(pld, KEMPLD_I2C_CTRL, ctrl); + + stat = kempld_read8(pld, KEMPLD_I2C_STAT); + if (stat & I2C_STAT_BUSY) + kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_STOP); +} + +static u32 kempld_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm kempld_i2c_algorithm = { + .master_xfer = kempld_i2c_xfer, + .functionality = kempld_i2c_func, +}; + +static struct i2c_adapter kempld_i2c_adapter = { + .owner = THIS_MODULE, + .name = "i2c-kempld", + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, + .algo = &kempld_i2c_algorithm, +}; + +static int kempld_i2c_probe(struct platform_device *pdev) +{ + struct kempld_device_data *pld = dev_get_drvdata(pdev->dev.parent); + struct kempld_i2c_data *i2c; + int ret; + u8 ctrl; + + i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + i2c->pld = pld; + i2c->dev = &pdev->dev; + i2c->adap = kempld_i2c_adapter; + i2c->adap.dev.parent = i2c->dev; + i2c_set_adapdata(&i2c->adap, i2c); + platform_set_drvdata(pdev, i2c); + + kempld_get_mutex(pld); + ctrl = kempld_read8(pld, KEMPLD_I2C_CTRL); + + if (ctrl & I2C_CTRL_EN) + i2c->was_active = true; + + kempld_i2c_device_init(i2c); + kempld_release_mutex(pld); + + /* Add I2C adapter to I2C tree */ + if (i2c_bus >= -1) + i2c->adap.nr = i2c_bus; + ret = i2c_add_numbered_adapter(&i2c->adap); + if (ret) + return ret; + + dev_info(i2c->dev, "I2C bus initialized at %dkHz\n", + bus_frequency); + + return 0; +} + +static int kempld_i2c_remove(struct platform_device *pdev) +{ + struct kempld_i2c_data *i2c = platform_get_drvdata(pdev); + struct kempld_device_data *pld = i2c->pld; + u8 ctrl; + + kempld_get_mutex(pld); + /* + * Disable I2C logic if it was not activated before the + * driver loaded + */ + if (!i2c->was_active) { + ctrl = kempld_read8(pld, KEMPLD_I2C_CTRL); + ctrl &= ~I2C_CTRL_EN; + kempld_write8(pld, KEMPLD_I2C_CTRL, ctrl); + } + kempld_release_mutex(pld); + + i2c_del_adapter(&i2c->adap); + + return 0; +} + +#ifdef CONFIG_PM +static int kempld_i2c_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct kempld_i2c_data *i2c = platform_get_drvdata(pdev); + struct kempld_device_data *pld = i2c->pld; + u8 ctrl; + + kempld_get_mutex(pld); + ctrl = kempld_read8(pld, KEMPLD_I2C_CTRL); + ctrl &= ~I2C_CTRL_EN; + kempld_write8(pld, KEMPLD_I2C_CTRL, ctrl); + kempld_release_mutex(pld); + + return 0; +} + +static int kempld_i2c_resume(struct platform_device *pdev) +{ + struct kempld_i2c_data *i2c = platform_get_drvdata(pdev); + struct kempld_device_data *pld = i2c->pld; + + kempld_get_mutex(pld); + kempld_i2c_device_init(i2c); + kempld_release_mutex(pld); + + return 0; +} +#else +#define kempld_i2c_suspend NULL +#define kempld_i2c_resume NULL +#endif + +static struct platform_driver kempld_i2c_driver = { + .driver = { + .name = "kempld-i2c", + .owner = THIS_MODULE, + }, + .probe = kempld_i2c_probe, + .remove = kempld_i2c_remove, + .suspend = kempld_i2c_suspend, + .resume = kempld_i2c_resume, +}; + +module_platform_driver(kempld_i2c_driver); + +MODULE_DESCRIPTION("KEM PLD I2C Driver"); +MODULE_AUTHOR("Michael Brunner "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:kempld_i2c"); -- cgit v1.2.3 From 74251fe21bfa9310ddba9e0436d1fcf389e602ee Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 1 Jul 2013 17:54:09 +1000 Subject: powerpc/powernv: Fix iommu initialization again So because those things always end up in trainwrecks... In 7846de406 we moved back the iommu initialization earlier, essentially undoing 37f02195b which was causing us endless trouble... except that in the meantime we had merged 959c9bdd58 (to workaround the original breakage) which is now ... broken :-) This fixes it by doing a partial revert of the latter (we keep the ppc_md. path which will be needed in the hotplug case, which happens also during some EEH error recovery situations). Signed-off-by: Benjamin Herrenschmidt CC: [v3.10] --- arch/powerpc/platforms/powernv/pci-ioda.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index c393bf59f113..49b57b9f835d 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c @@ -443,6 +443,17 @@ static void pnv_pci_ioda_dma_dev_setup(struct pnv_phb *phb, struct pci_dev *pdev set_iommu_table_base(&pdev->dev, &pe->tce32_table); } +static void pnv_ioda_setup_bus_dma(struct pnv_ioda_pe *pe, struct pci_bus *bus) +{ + struct pci_dev *dev; + + list_for_each_entry(dev, &bus->devices, bus_list) { + set_iommu_table_base(&dev->dev, &pe->tce32_table); + if (dev->subordinate) + pnv_ioda_setup_bus_dma(pe, dev->subordinate); + } +} + static void pnv_pci_ioda1_tce_invalidate(struct iommu_table *tbl, u64 *startp, u64 *endp) { @@ -599,6 +610,11 @@ static void pnv_pci_ioda_setup_dma_pe(struct pnv_phb *phb, iommu_init_table(tbl, phb->hose->node); iommu_register_group(tbl, pci_domain_nr(pe->pbus), pe->pe_number); + if (pe->pdev) + set_iommu_table_base(&pe->pdev->dev, tbl); + else + pnv_ioda_setup_bus_dma(pe, pe->pbus); + return; fail: /* XXX Failure: Try to fallback to 64-bit only ? */ @@ -670,6 +686,11 @@ static void pnv_pci_ioda2_setup_dma_pe(struct pnv_phb *phb, } iommu_init_table(tbl, phb->hose->node); + if (pe->pdev) + set_iommu_table_base(&pe->pdev->dev, tbl); + else + pnv_ioda_setup_bus_dma(pe, pe->pbus); + return; fail: if (pe->tce32_seg >= 0) -- cgit v1.2.3 From 6bbbca735936e15b9431882eceddcf6dff76e03c Mon Sep 17 00:00:00 2001 From: Aruna Balakrishnaiah Date: Thu, 27 Jun 2013 14:02:56 +0530 Subject: pstore: Pass header size in the pstore write callback Header size is needed to distinguish between header and the dump data. Incorporate the addition of new argument (hsize) in the pstore write callback. Signed-off-by: Aruna Balakrishnaiah Acked-by: Kees Cook Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/pseries/nvram.c | 4 +++- drivers/acpi/apei/erst.c | 4 ++-- drivers/firmware/efi/efi-pstore.c | 2 +- fs/pstore/platform.c | 10 ++++++---- fs/pstore/ram.c | 3 ++- include/linux/pstore.h | 8 ++++---- 6 files changed, 18 insertions(+), 13 deletions(-) diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c index 14cc486709f6..3f0e7d67d747 100644 --- a/arch/powerpc/platforms/pseries/nvram.c +++ b/arch/powerpc/platforms/pseries/nvram.c @@ -502,6 +502,7 @@ static int nvram_pstore_open(struct pstore_info *psi) * @part: pstore writes data to registered buffer in parts, * part number will indicate the same. * @count: Indicates oops count + * @hsize: Size of header added by pstore * @size: number of bytes written to the registered buffer * @psi: registered pstore_info structure * @@ -512,7 +513,8 @@ static int nvram_pstore_open(struct pstore_info *psi) static int nvram_pstore_write(enum pstore_type_id type, enum kmsg_dump_reason reason, u64 *id, unsigned int part, int count, - size_t size, struct pstore_info *psi) + size_t hsize, size_t size, + struct pstore_info *psi) { int rc; struct oops_log_info *oops_hdr = (struct oops_log_info *) oops_buf; diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c index 6d894bfd8b8f..a9cf96085f85 100644 --- a/drivers/acpi/apei/erst.c +++ b/drivers/acpi/apei/erst.c @@ -935,7 +935,7 @@ static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count, struct timespec *time, char **buf, struct pstore_info *psi); static int erst_writer(enum pstore_type_id type, enum kmsg_dump_reason reason, - u64 *id, unsigned int part, int count, + u64 *id, unsigned int part, int count, size_t hsize, size_t size, struct pstore_info *psi); static int erst_clearer(enum pstore_type_id type, u64 id, int count, struct timespec time, struct pstore_info *psi); @@ -1055,7 +1055,7 @@ out: } static int erst_writer(enum pstore_type_id type, enum kmsg_dump_reason reason, - u64 *id, unsigned int part, int count, + u64 *id, unsigned int part, int count, size_t hsize, size_t size, struct pstore_info *psi) { struct cper_pstore_record *rcd = (struct cper_pstore_record *) diff --git a/drivers/firmware/efi/efi-pstore.c b/drivers/firmware/efi/efi-pstore.c index 202d2c85ba2e..452800e005b6 100644 --- a/drivers/firmware/efi/efi-pstore.c +++ b/drivers/firmware/efi/efi-pstore.c @@ -104,7 +104,7 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type, static int efi_pstore_write(enum pstore_type_id type, enum kmsg_dump_reason reason, u64 *id, - unsigned int part, int count, size_t size, + unsigned int part, int count, size_t hsize, size_t size, struct pstore_info *psi) { char name[DUMP_NAME_LEN]; diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c index 86d1038b5a12..4637ec4169cd 100644 --- a/fs/pstore/platform.c +++ b/fs/pstore/platform.c @@ -159,7 +159,7 @@ static void pstore_dump(struct kmsg_dumper *dumper, break; ret = psinfo->write(PSTORE_TYPE_DMESG, reason, &id, part, - oopscount, hsize + len, psinfo); + oopscount, hsize, hsize + len, psinfo); if (ret == 0 && reason == KMSG_DUMP_OOPS && pstore_is_mounted()) pstore_new_entry = 1; @@ -196,7 +196,7 @@ static void pstore_console_write(struct console *con, const char *s, unsigned c) spin_lock_irqsave(&psinfo->buf_lock, flags); } memcpy(psinfo->buf, s, c); - psinfo->write(PSTORE_TYPE_CONSOLE, 0, &id, 0, 0, c, psinfo); + psinfo->write(PSTORE_TYPE_CONSOLE, 0, &id, 0, 0, 0, c, psinfo); spin_unlock_irqrestore(&psinfo->buf_lock, flags); s += c; c = e - s; @@ -221,9 +221,11 @@ static void pstore_register_console(void) {} static int pstore_write_compat(enum pstore_type_id type, enum kmsg_dump_reason reason, u64 *id, unsigned int part, int count, - size_t size, struct pstore_info *psi) + size_t hsize, size_t size, + struct pstore_info *psi) { - return psi->write_buf(type, reason, id, part, psinfo->buf, size, psi); + return psi->write_buf(type, reason, id, part, psinfo->buf, hsize, + size, psi); } /* diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index 1376e5a8f0d6..c6bb77ca35b5 100644 --- a/fs/pstore/ram.c +++ b/fs/pstore/ram.c @@ -195,7 +195,8 @@ static size_t ramoops_write_kmsg_hdr(struct persistent_ram_zone *prz) static int notrace ramoops_pstore_write_buf(enum pstore_type_id type, enum kmsg_dump_reason reason, u64 *id, unsigned int part, - const char *buf, size_t size, + const char *buf, + size_t hsize, size_t size, struct pstore_info *psi) { struct ramoops_context *cxt = psi->data; diff --git a/include/linux/pstore.h b/include/linux/pstore.h index 656699fcc7d7..4aa80ba830a2 100644 --- a/include/linux/pstore.h +++ b/include/linux/pstore.h @@ -58,12 +58,12 @@ struct pstore_info { struct pstore_info *psi); int (*write)(enum pstore_type_id type, enum kmsg_dump_reason reason, u64 *id, - unsigned int part, int count, size_t size, - struct pstore_info *psi); + unsigned int part, int count, size_t hsize, + size_t size, struct pstore_info *psi); int (*write_buf)(enum pstore_type_id type, enum kmsg_dump_reason reason, u64 *id, - unsigned int part, const char *buf, size_t size, - struct pstore_info *psi); + unsigned int part, const char *buf, size_t hsize, + size_t size, struct pstore_info *psi); int (*erase)(enum pstore_type_id type, u64 id, int count, struct timespec time, struct pstore_info *psi); -- cgit v1.2.3 From fbfe86fc0c53911cf243479f7d26c37dcb1243f1 Mon Sep 17 00:00:00 2001 From: Aruna Balakrishnaiah Date: Thu, 27 Jun 2013 14:03:04 +0530 Subject: powerpc/pseries: Re-organise the oops compression code nvram_compress() and zip_oops() is used by the nvram_pstore_write API to compress oops messages hence re-organise the functions accordingly to avoid forward declarations. Signed-off-by: Aruna Balakrishnaiah Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/pseries/nvram.c | 104 ++++++++++++++++----------------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c index 3f0e7d67d747..588bab5da8cb 100644 --- a/arch/powerpc/platforms/pseries/nvram.c +++ b/arch/powerpc/platforms/pseries/nvram.c @@ -486,6 +486,58 @@ static int clobbering_unread_rtas_event(void) NVRAM_RTAS_READ_TIMEOUT); } +/* Derived from logfs_compress() */ +static int nvram_compress(const void *in, void *out, size_t inlen, + size_t outlen) +{ + int err, ret; + + ret = -EIO; + err = zlib_deflateInit2(&stream, COMPR_LEVEL, Z_DEFLATED, WINDOW_BITS, + MEM_LEVEL, Z_DEFAULT_STRATEGY); + if (err != Z_OK) + goto error; + + stream.next_in = in; + stream.avail_in = inlen; + stream.total_in = 0; + stream.next_out = out; + stream.avail_out = outlen; + stream.total_out = 0; + + err = zlib_deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) + goto error; + + err = zlib_deflateEnd(&stream); + if (err != Z_OK) + goto error; + + if (stream.total_out >= stream.total_in) + goto error; + + ret = stream.total_out; +error: + return ret; +} + +/* Compress the text from big_oops_buf into oops_buf. */ +static int zip_oops(size_t text_len) +{ + struct oops_log_info *oops_hdr = (struct oops_log_info *)oops_buf; + int zipped_len = nvram_compress(big_oops_buf, oops_data, text_len, + oops_data_sz); + if (zipped_len < 0) { + pr_err("nvram: compression failed; returned %d\n", zipped_len); + pr_err("nvram: logging uncompressed oops/panic report\n"); + return -1; + } + oops_hdr->version = OOPS_HDR_VERSION; + oops_hdr->report_length = (u16) zipped_len; + oops_hdr->timestamp = get_seconds(); + return 0; +} + #ifdef CONFIG_PSTORE static int nvram_pstore_open(struct pstore_info *psi) { @@ -759,58 +811,6 @@ int __init pSeries_nvram_init(void) } -/* Derived from logfs_compress() */ -static int nvram_compress(const void *in, void *out, size_t inlen, - size_t outlen) -{ - int err, ret; - - ret = -EIO; - err = zlib_deflateInit2(&stream, COMPR_LEVEL, Z_DEFLATED, WINDOW_BITS, - MEM_LEVEL, Z_DEFAULT_STRATEGY); - if (err != Z_OK) - goto error; - - stream.next_in = in; - stream.avail_in = inlen; - stream.total_in = 0; - stream.next_out = out; - stream.avail_out = outlen; - stream.total_out = 0; - - err = zlib_deflate(&stream, Z_FINISH); - if (err != Z_STREAM_END) - goto error; - - err = zlib_deflateEnd(&stream); - if (err != Z_OK) - goto error; - - if (stream.total_out >= stream.total_in) - goto error; - - ret = stream.total_out; -error: - return ret; -} - -/* Compress the text from big_oops_buf into oops_buf. */ -static int zip_oops(size_t text_len) -{ - struct oops_log_info *oops_hdr = (struct oops_log_info *)oops_buf; - int zipped_len = nvram_compress(big_oops_buf, oops_data, text_len, - oops_data_sz); - if (zipped_len < 0) { - pr_err("nvram: compression failed; returned %d\n", zipped_len); - pr_err("nvram: logging uncompressed oops/panic report\n"); - return -1; - } - oops_hdr->version = OOPS_HDR_VERSION; - oops_hdr->report_length = (u16) zipped_len; - oops_hdr->timestamp = get_seconds(); - return 0; -} - /* * This is our kmsg_dump callback, called after an oops or panic report * has been written to the printk buffer. We want to capture as much -- cgit v1.2.3 From 40847e56609c21692ebe593abbf72c1e7f4af206 Mon Sep 17 00:00:00 2001 From: Aruna Balakrishnaiah Date: Thu, 27 Jun 2013 14:03:13 +0530 Subject: powerpc/pseries: Support compression of oops text via pstore The patch set supports compression of oops messages while writing to NVRAM, this helps in capturing more of oops data to lnx,oops-log. The pstore file for oops messages will be in decompressed format making it readable. In case compression fails, the patch takes care of copying the header added by pstore and last oops_data_sz bytes of big_oops_buf to NVRAM so that we have recent oops messages in lnx,oops-log. In case decompression fails, it will result in absence of oops file but still have files (in /dev/pstore) for other partitions. Signed-off-by: Aruna Balakrishnaiah Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/pseries/nvram.c | 131 +++++++++++++++++++++++++++++---- 1 file changed, 117 insertions(+), 14 deletions(-) diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c index 588bab5da8cb..9f8671a44551 100644 --- a/arch/powerpc/platforms/pseries/nvram.c +++ b/arch/powerpc/platforms/pseries/nvram.c @@ -539,6 +539,65 @@ static int zip_oops(size_t text_len) } #ifdef CONFIG_PSTORE +/* Derived from logfs_uncompress */ +int nvram_decompress(void *in, void *out, size_t inlen, size_t outlen) +{ + int err, ret; + + ret = -EIO; + err = zlib_inflateInit(&stream); + if (err != Z_OK) + goto error; + + stream.next_in = in; + stream.avail_in = inlen; + stream.total_in = 0; + stream.next_out = out; + stream.avail_out = outlen; + stream.total_out = 0; + + err = zlib_inflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) + goto error; + + err = zlib_inflateEnd(&stream); + if (err != Z_OK) + goto error; + + ret = stream.total_out; +error: + return ret; +} + +static int unzip_oops(char *oops_buf, char *big_buf) +{ + struct oops_log_info *oops_hdr = (struct oops_log_info *)oops_buf; + u64 timestamp = oops_hdr->timestamp; + char *big_oops_data = NULL; + char *oops_data_buf = NULL; + size_t big_oops_data_sz; + int unzipped_len; + + big_oops_data = big_buf + sizeof(struct oops_log_info); + big_oops_data_sz = big_oops_buf_sz - sizeof(struct oops_log_info); + oops_data_buf = oops_buf + sizeof(struct oops_log_info); + + unzipped_len = nvram_decompress(oops_data_buf, big_oops_data, + oops_hdr->report_length, + big_oops_data_sz); + + if (unzipped_len < 0) { + pr_err("nvram: decompression failed; returned %d\n", + unzipped_len); + return -1; + } + oops_hdr = (struct oops_log_info *)big_buf; + oops_hdr->version = OOPS_HDR_VERSION; + oops_hdr->report_length = (u16) unzipped_len; + oops_hdr->timestamp = timestamp; + return 0; +} + static int nvram_pstore_open(struct pstore_info *psi) { /* Reset the iterator to start reading partitions again */ @@ -569,6 +628,7 @@ static int nvram_pstore_write(enum pstore_type_id type, struct pstore_info *psi) { int rc; + unsigned int err_type = ERR_TYPE_KERNEL_PANIC; struct oops_log_info *oops_hdr = (struct oops_log_info *) oops_buf; /* part 1 has the recent messages from printk buffer */ @@ -579,8 +639,30 @@ static int nvram_pstore_write(enum pstore_type_id type, oops_hdr->version = OOPS_HDR_VERSION; oops_hdr->report_length = (u16) size; oops_hdr->timestamp = get_seconds(); + + if (big_oops_buf) { + rc = zip_oops(size); + /* + * If compression fails copy recent log messages from + * big_oops_buf to oops_data. + */ + if (rc != 0) { + size_t diff = size - oops_data_sz + hsize; + + if (size > oops_data_sz) { + memcpy(oops_data, big_oops_buf, hsize); + memcpy(oops_data + hsize, big_oops_buf + diff, + oops_data_sz - hsize); + + oops_hdr->report_length = (u16) oops_data_sz; + } else + memcpy(oops_data, big_oops_buf, size); + } else + err_type = ERR_TYPE_KERNEL_PANIC_GZ; + } + rc = nvram_write_os_partition(&oops_log_partition, oops_buf, - (int) (sizeof(*oops_hdr) + size), ERR_TYPE_KERNEL_PANIC, + (int) (sizeof(*oops_hdr) + oops_hdr->report_length), err_type, count); if (rc != 0) @@ -602,10 +684,11 @@ static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type, struct oops_log_info *oops_hdr; unsigned int err_type, id_no, size = 0; struct nvram_os_partition *part = NULL; - char *buff = NULL; - int sig = 0; + char *buff = NULL, *big_buff = NULL; + int rc, sig = 0; loff_t p; +read_partition: read_type++; switch (nvram_type_ids[read_type]) { @@ -668,6 +751,25 @@ static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type, if (nvram_type_ids[read_type] == PSTORE_TYPE_DMESG) { oops_hdr = (struct oops_log_info *)buff; *buf = buff + sizeof(*oops_hdr); + + if (err_type == ERR_TYPE_KERNEL_PANIC_GZ) { + big_buff = kmalloc(big_oops_buf_sz, GFP_KERNEL); + if (!big_buff) + return -ENOMEM; + + rc = unzip_oops(buff, big_buff); + + if (rc != 0) { + kfree(buff); + kfree(big_buff); + goto read_partition; + } + + oops_hdr = (struct oops_log_info *)big_buff; + *buf = big_buff + sizeof(*oops_hdr); + kfree(buff); + } + time->tv_sec = oops_hdr->timestamp; time->tv_nsec = 0; return oops_hdr->report_length; @@ -689,17 +791,18 @@ static int nvram_pstore_init(void) { int rc = 0; - nvram_pstore_info.buf = oops_data; - nvram_pstore_info.bufsize = oops_data_sz; + if (big_oops_buf) { + nvram_pstore_info.buf = big_oops_buf; + nvram_pstore_info.bufsize = big_oops_buf_sz; + } else { + nvram_pstore_info.buf = oops_data; + nvram_pstore_info.bufsize = oops_data_sz; + } rc = pstore_register(&nvram_pstore_info); if (rc != 0) pr_err("nvram: pstore_register() failed, defaults to " "kmsg_dump; returned %d\n", rc); - else - /*TODO: Support compression when pstore is configured */ - pr_info("nvram: Compression of oops text supported only when " - "pstore is not configured"); return rc; } @@ -733,11 +836,6 @@ static void __init nvram_init_oops_partition(int rtas_partition_exists) oops_data = oops_buf + sizeof(struct oops_log_info); oops_data_sz = oops_log_partition.size - sizeof(struct oops_log_info); - rc = nvram_pstore_init(); - - if (!rc) - return; - /* * Figure compression (preceded by elimination of each line's * severity prefix) will reduce the oops/panic report to at most @@ -761,6 +859,11 @@ static void __init nvram_init_oops_partition(int rtas_partition_exists) stream.workspace = NULL; } + rc = nvram_pstore_init(); + + if (!rc) + return; + rc = kmsg_dump_register(&nvram_kmsg_dumper); if (rc != 0) { pr_err("nvram: kmsg_dump_register() failed; returned %d\n", rc); -- cgit v1.2.3 From 73845adf3357c3c71da25e18f44e5a9924d666d5 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Wed, 12 Jun 2013 17:27:30 -0300 Subject: drm/i915: rename intel_dp_destroy to intel_dp_connector_destroy Because it's the function that destroys the connector, not the encoder. And we already have intel_dp_encoder_destroy. This has annoyed me for a long time. Signed-off-by: Paulo Zanoni Reviewed-by: Zoltan Nyul Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 591502c6f34c..24a44ede867d 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2681,7 +2681,7 @@ done: } static void -intel_dp_destroy(struct drm_connector *connector) +intel_dp_connector_destroy(struct drm_connector *connector) { struct intel_connector *intel_connector = to_intel_connector(connector); @@ -2724,7 +2724,7 @@ static const struct drm_connector_funcs intel_dp_connector_funcs = { .detect = intel_dp_detect, .fill_modes = drm_helper_probe_single_connector_modes, .set_property = intel_dp_set_property, - .destroy = intel_dp_destroy, + .destroy = intel_dp_connector_destroy, }; static const struct drm_connector_helper_funcs intel_dp_connector_helper_funcs = { -- cgit v1.2.3 From 6a9c4b35e6696a63805b6da5e4889c6986e9ee1b Mon Sep 17 00:00:00 2001 From: Rui Guo Date: Wed, 19 Jun 2013 21:10:23 +0800 Subject: drm/i915: Fix PCH detect with multiple ISA bridges in VM In some virtualized environments (e.g. XEN), there is irrelevant ISA bridge in the system. To work reliably, we should scan trhough all the ISA bridge devices and check for the first match, instead of only checking the first one. Signed-off-by: Rui Guo [danvet: Fixup conflict with the num_pch_pll removal. And add subsystem header to the commit message headline.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index deaa32e8113b..062cbda1bf4a 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -465,9 +465,15 @@ void intel_detect_pch(struct drm_device *dev) * make graphics device passthrough work easy for VMM, that only * need to expose ISA bridge to let driver know the real hardware * underneath. This is a requirement from virtualization team. + * + * In some virtualized environments (e.g. XEN), there is irrelevant + * ISA bridge in the system. To work reliably, we should scan trhough + * all the ISA bridge devices and check for the first match, instead + * of only checking the first one. */ pch = pci_get_class(PCI_CLASS_BRIDGE_ISA << 8, NULL); - if (pch) { + while (pch) { + struct pci_dev *curr = pch; if (pch->vendor == PCI_VENDOR_ID_INTEL) { unsigned short id; id = pch->device & INTEL_PCH_DEVICE_ID_MASK; @@ -496,10 +502,18 @@ void intel_detect_pch(struct drm_device *dev) DRM_DEBUG_KMS("Found LynxPoint LP PCH\n"); WARN_ON(!IS_HASWELL(dev)); WARN_ON(!IS_ULT(dev)); + } else { + goto check_next; } + pci_dev_put(pch); + break; } - pci_dev_put(pch); +check_next: + pch = pci_get_class(PCI_CLASS_BRIDGE_ISA << 8, curr); + pci_dev_put(curr); } + if (!pch) + DRM_DEBUG_KMS("No PCH found?\n"); } bool i915_semaphore_is_enabled(struct drm_device *dev) -- cgit v1.2.3 From 1625e7e549c50fb57a1e1ab1cb0f5735c84c9029 Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Mon, 24 Jun 2013 11:47:48 -0400 Subject: drm/i915: make compact dma scatter lists creation work with SWIOTLB backend. Git commit 90797e6d1ec0dfde6ba62a48b9ee3803887d6ed4 ("drm/i915: create compact dma scatter lists for gem objects") makes certain assumptions about the under laying DMA API that are not always correct. On a ThinkPad X230 with an Intel HD 4000 with Xen during the bootup I see: [drm:intel_pipe_set_base] *ERROR* pin & fence failed [drm:intel_crtc_set_config] *ERROR* failed to set mode on [CRTC:3], err = -28 Bit of debugging traced it down to dma_map_sg failing (in i915_gem_gtt_prepare_object) as some of the SG entries were huge (3MB). That unfortunately are sizes that the SWIOTLB is incapable of handling - the maximum it can handle is a an entry of 512KB of virtual contiguous memory for its bounce buffer. (See IO_TLB_SEGSIZE). Previous to the above mention git commit the SG entries were of 4KB, and the code introduced by above git commit squashed the CPU contiguous PFNs in one big virtual address provided to DMA API. This patch is a simple semi-revert - were we emulate the old behavior if we detect that SWIOTLB is online. If it is not online then we continue on with the new compact scatter gather mechanism. An alternative solution would be for the the '.get_pages' and the i915_gem_gtt_prepare_object to retry with smaller max gap of the amount of PFNs that can be combined together - but with this issue discovered during rc7 that might be too risky. Reported-and-Tested-by: Konrad Rzeszutek Wilk CC: Chris Wilson CC: Imre Deak CC: Daniel Vetter CC: David Airlie CC: Signed-off-by: Konrad Rzeszutek Wilk Cc: stable@vger.kernel.org Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index a6178baccb56..e31eeb1d7fd6 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1802,7 +1802,14 @@ i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj) gfp |= __GFP_NORETRY | __GFP_NOWARN | __GFP_NO_KSWAPD; gfp &= ~(__GFP_IO | __GFP_WAIT); } - +#ifdef CONFIG_SWIOTLB + if (swiotlb_nr_tbl()) { + st->nents++; + sg_set_page(sg, page, PAGE_SIZE, 0); + sg = sg_next(sg); + continue; + } +#endif if (!i || page_to_pfn(page) != last_pfn + 1) { if (i) sg = sg_next(sg); @@ -1813,8 +1820,10 @@ i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj) } last_pfn = page_to_pfn(page); } - - sg_mark_end(sg); +#ifdef CONFIG_SWIOTLB + if (!swiotlb_nr_tbl()) +#endif + sg_mark_end(sg); obj->pages = st; if (i915_gem_object_needs_bit17_swizzle(obj)) -- cgit v1.2.3 From 0f4f7b57954dda93b10e3b46594b0bfe24bba22c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 24 Jun 2013 18:32:36 +0200 Subject: drm/i915: tune down DIDL warning about too many outputs Nothing the user (nor we) really can do about this, but upsets a nice quiet boot. Note that this happens mostly on SDVs where OEMs obviously haven't had a chance yet to appropriately trim the output list. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=65988 Reviewed-by: Damien Lespiau [danvet: Amend commit message a bit to clarify a question from Paulo.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_opregion.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c index 79be7cfd3152..cfb8fb68f09c 100644 --- a/drivers/gpu/drm/i915/intel_opregion.c +++ b/drivers/gpu/drm/i915/intel_opregion.c @@ -311,8 +311,8 @@ static void intel_didl_outputs(struct drm_device *dev) list_for_each_entry(acpi_cdev, &acpi_video_bus->children, node) { if (i >= 8) { - dev_printk(KERN_ERR, &dev->pdev->dev, - "More than 8 outputs detected via ACPI\n"); + dev_dbg(&dev->pdev->dev, + "More than 8 outputs detected via ACPI\n"); return; } status = @@ -338,8 +338,8 @@ blind_set: list_for_each_entry(connector, &dev->mode_config.connector_list, head) { int output_type = ACPI_OTHER_OUTPUT; if (i >= 8) { - dev_printk(KERN_ERR, &dev->pdev->dev, - "More than 8 outputs in connector list\n"); + dev_dbg(&dev->pdev->dev, + "More than 8 outputs in connector list\n"); return; } switch (connector->connector_type) { -- cgit v1.2.3 From 3765f3048651586e2617793e9efe184ff8c79a97 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Fri, 7 Jun 2013 16:03:50 +0300 Subject: drm/i915: fix build warning on format specifier mismatch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drivers/gpu/drm/i915/i915_gem.c: In function ‘i915_gem_object_bind_to_gtt’: drivers/gpu/drm/i915/i915_gem.c:3002:3: warning: format ‘%ld’ expects argument of type ‘long int’, but argument 5 has type ‘size_t’ [-Wformat] v2: Use %zu instead of %d. Two char patch, and 100% wrong. (Ville) Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index e31eeb1d7fd6..fa074cec8587 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3112,7 +3112,7 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, * before evicting everything in a vain attempt to find space. */ if (obj->base.size > gtt_max) { - DRM_ERROR("Attempting to bind an object larger than the aperture: object=%zd > %s aperture=%ld\n", + DRM_ERROR("Attempting to bind an object larger than the aperture: object=%zd > %s aperture=%zu\n", obj->base.size, map_and_fenceable ? "mappable" : "total", gtt_max); -- cgit v1.2.3 From f5adf94e5fed2468eef4f0c094b66bf834770d7b Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Mon, 24 Jun 2013 18:29:34 +0100 Subject: drm/i915: Introduce an HAS_IPS() macro Follow the trend and don't code conditions with platforms but with features. Signed-off-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 2 +- drivers/gpu/drm/i915/i915_drv.h | 2 ++ drivers/gpu/drm/i915/intel_display.c | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index d4e78b64ca87..f72d5a3fdfba 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1483,7 +1483,7 @@ static int i915_ips_status(struct seq_file *m, void *unused) struct drm_device *dev = node->minor->dev; struct drm_i915_private *dev_priv = dev->dev_private; - if (!IS_ULT(dev)) { + if (!HAS_IPS(dev)) { seq_puts(m, "not supported\n"); return 0; } diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 9e1bf6dcbb2a..cc1d6056ab70 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1474,6 +1474,8 @@ struct drm_i915_file_private { #define HAS_PIPE_CXSR(dev) (INTEL_INFO(dev)->has_pipe_cxsr) #define I915_HAS_FBC(dev) (INTEL_INFO(dev)->has_fbc) +#define HAS_IPS(dev) (IS_ULT(dev)) + #define HAS_PIPE_CONTROL(dev) (INTEL_INFO(dev)->gen >= 5) #define HAS_DDI(dev) (INTEL_INFO(dev)->has_ddi) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index b08d1f9ce0de..17d5c7a3468b 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3250,7 +3250,7 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) /* IPS only exists on ULT machines and is tied to pipe A. */ static bool hsw_crtc_supports_ips(struct intel_crtc *crtc) { - return IS_ULT(crtc->base.dev) && crtc->pipe == PIPE_A; + return HAS_IPS(crtc->base.dev) && crtc->pipe == PIPE_A; } static void hsw_enable_ips(struct intel_crtc *crtc) @@ -4069,7 +4069,7 @@ static int intel_crtc_compute_config(struct intel_crtc *crtc, pipe_config->pipe_bpp = 8*3; } - if (IS_HASWELL(dev)) + if (HAS_IPS(dev)) hsw_compute_ips_config(crtc, pipe_config); /* XXX: PCH clock sharing is done in ->mode_set, so make sure the old -- cgit v1.2.3 From 4f7fd7095d85cd31c86cb9ba87bc301319630ccc Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 24 Jun 2013 21:33:28 +0200 Subject: drm/i915: Fix up sdvo hpd pins for i965g/gm Bspec seems to be full of lies, at least it disagress with reality: Two systems corrobated that SDVO hpd bits are the same as on gen3. v2: Update comment a bit. Cc: Arthur Ranyan Cc: Chris Wilson Tested-by: Chris Wilson Reported-and-tested-by: Alex Fiestas Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=58405 Cc: stable@vger.kernel.org Acked-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 13 ++----------- drivers/gpu/drm/i915/i915_reg.h | 13 ++++++------- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 7857430943ec..611da3a74479 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -70,15 +70,6 @@ static const u32 hpd_status_gen4[] = { [HPD_PORT_D] = PORTD_HOTPLUG_INT_STATUS }; -static const u32 hpd_status_i965[] = { - [HPD_CRT] = CRT_HOTPLUG_INT_STATUS, - [HPD_SDVO_B] = SDVOB_HOTPLUG_INT_STATUS_I965, - [HPD_SDVO_C] = SDVOC_HOTPLUG_INT_STATUS_I965, - [HPD_PORT_B] = PORTB_HOTPLUG_INT_STATUS, - [HPD_PORT_C] = PORTC_HOTPLUG_INT_STATUS, - [HPD_PORT_D] = PORTD_HOTPLUG_INT_STATUS -}; - static const u32 hpd_status_i915[] = { /* i915 and valleyview are the same */ [HPD_CRT] = CRT_HOTPLUG_INT_STATUS, [HPD_SDVO_B] = SDVOB_HOTPLUG_INT_STATUS_I915, @@ -3449,13 +3440,13 @@ static irqreturn_t i965_irq_handler(int irq, void *arg) u32 hotplug_status = I915_READ(PORT_HOTPLUG_STAT); u32 hotplug_trigger = hotplug_status & (IS_G4X(dev) ? HOTPLUG_INT_STATUS_G4X : - HOTPLUG_INT_STATUS_I965); + HOTPLUG_INT_STATUS_I915); DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x\n", hotplug_status); if (hotplug_trigger) { if (hotplug_irq_storm_detect(dev, hotplug_trigger, - IS_G4X(dev) ? hpd_status_gen4 : hpd_status_i965)) + IS_G4X(dev) ? hpd_status_gen4 : hpd_status_i915)) i915_hpd_irq_setup(dev); queue_work(dev_priv->wq, &dev_priv->hotplug_work); diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 2102ff32ee20..137be4c1abd1 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -1874,6 +1874,12 @@ /* SDVO is different across gen3/4 */ #define SDVOC_HOTPLUG_INT_STATUS_G4X (1 << 3) #define SDVOB_HOTPLUG_INT_STATUS_G4X (1 << 2) +/* + * Bspec seems to be seriously misleaded about the SDVO hpd bits on i965g/gm, + * since reality corrobates that they're the same as on gen3. But keep these + * bits here (and the comment!) to help any other lost wanderers back onto the + * right tracks. + */ #define SDVOC_HOTPLUG_INT_STATUS_I965 (3 << 4) #define SDVOB_HOTPLUG_INT_STATUS_I965 (3 << 2) #define SDVOC_HOTPLUG_INT_STATUS_I915 (1 << 7) @@ -1885,13 +1891,6 @@ PORTC_HOTPLUG_INT_STATUS | \ PORTD_HOTPLUG_INT_STATUS) -#define HOTPLUG_INT_STATUS_I965 (CRT_HOTPLUG_INT_STATUS | \ - SDVOB_HOTPLUG_INT_STATUS_I965 | \ - SDVOC_HOTPLUG_INT_STATUS_I965 | \ - PORTB_HOTPLUG_INT_STATUS | \ - PORTC_HOTPLUG_INT_STATUS | \ - PORTD_HOTPLUG_INT_STATUS) - #define HOTPLUG_INT_STATUS_I915 (CRT_HOTPLUG_INT_STATUS | \ SDVOB_HOTPLUG_INT_STATUS_I915 | \ SDVOC_HOTPLUG_INT_STATUS_I915 | \ -- cgit v1.2.3 From bf67dfeb6899e140710d2138535b519dd8e46417 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 25 Jun 2013 11:06:52 +0200 Subject: drm/i915: don't scream into dmesg when a modeset fails There are legit cases, e.g. when userspace asks for something impossible. So tune it down to debug output like we do with all other userspace-triggerable warnings. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=66111#c5 Reviewed-by: Chris Wilson [danvet: Rebased.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 17d5c7a3468b..623acf49358e 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8753,8 +8753,8 @@ static int intel_crtc_set_config(struct drm_mode_set *set) } if (ret) { - DRM_ERROR("failed to set mode on [CRTC:%d], err = %d\n", - set->crtc->base.id, ret); + DRM_DEBUG_KMS("failed to set mode on [CRTC:%d], err = %d\n", + set->crtc->base.id, ret); fail: intel_set_config_restore_state(dev, config); -- cgit v1.2.3 From e4e9222d4b5e041c3b5a54ee21d6c62e7cc56609 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Tue, 25 Jun 2013 16:38:21 +0300 Subject: drm/i915: Remove duplicated WaForceL3Serialization:vlv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No need to apply WaForceL3Serialization:vlv twice. Signed-off-by: Ville Syrjälä Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_pm.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index b27bda07f4ae..159254ffdb1d 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -4834,10 +4834,6 @@ static void valleyview_init_clock_gating(struct drm_device *dev) I915_WRITE(GEN7_ROW_CHICKEN2, _MASKED_BIT_ENABLE(DOP_CLOCK_GATING_DISABLE)); - /* WaForceL3Serialization:vlv */ - I915_WRITE(GEN7_L3SQCREG4, I915_READ(GEN7_L3SQCREG4) & - ~L3SQ_URB_READ_CAM_MATCH_DISABLE); - /* This is required by WaCatErrorRejectionIssue:vlv */ I915_WRITE(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG, I915_READ(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG) | -- cgit v1.2.3 From a35cdaa0e13e24f3fccc518bfef1516aa8a8a665 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 25 Jun 2013 17:26:45 +0100 Subject: drm/i915: Detect invalid scanout pitches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Report back the user error of attempting to setup a CRTC with an invalid framebuffer pitch. This is trickier than it should be as on gen4, there is a restriction that tiled surfaces must have a stride less than 16k - which is less than the largest supported CRTC size. v2: Fix the limits for gen3 v3: Move check into intel_framebuffer_init() and fix VLV limits. (vsyrjala) v4: Use idiomatic '>=' for generation checks References: https://bugs.freedesktop.org/show_bug.cgi?id=65099 Signed-off-by: Chris Wilson Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 623acf49358e..d723819a450c 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -9121,6 +9121,7 @@ int intel_framebuffer_init(struct drm_device *dev, struct drm_mode_fb_cmd2 *mode_cmd, struct drm_i915_gem_object *obj) { + int pitch_limit; int ret; if (obj->tiling_mode == I915_TILING_Y) { @@ -9134,10 +9135,26 @@ int intel_framebuffer_init(struct drm_device *dev, return -EINVAL; } - /* FIXME <= Gen4 stride limits are bit unclear */ - if (mode_cmd->pitches[0] > 32768) { - DRM_DEBUG("pitch (%d) must be at less than 32768\n", - mode_cmd->pitches[0]); + if (INTEL_INFO(dev)->gen >= 5 && !IS_VALLEYVIEW(dev)) { + pitch_limit = 32*1024; + } else if (INTEL_INFO(dev)->gen >= 4) { + if (obj->tiling_mode) + pitch_limit = 16*1024; + else + pitch_limit = 32*1024; + } else if (INTEL_INFO(dev)->gen >= 3) { + if (obj->tiling_mode) + pitch_limit = 8*1024; + else + pitch_limit = 16*1024; + } else + /* XXX DSPC is limited to 4k tiled */ + pitch_limit = 8*1024; + + if (mode_cmd->pitches[0] > pitch_limit) { + DRM_DEBUG("%s pitch (%d) must be at less than %d\n", + obj->tiling_mode ? "tiled" : "linear", + mode_cmd->pitches[0], pitch_limit); return -EINVAL; } -- cgit v1.2.3 From 73008b989faf4200907a858f9b902ee29d6edbea Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Tue, 25 Jun 2013 19:21:01 +0300 Subject: drm/i915: Clean up VLV rps code a bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Always print both the MHz value and raw register value for rps stuff. Also kill a somewhat pointless local 'rpe' variable and just use dev_priv->rps.rpe_delay. While at it clean up the caps in "GPU" and "Punit" debug messages. Signed-off-by: Ville Syrjälä Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_pm.c | 50 ++++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 159254ffdb1d..33aa2d6674e5 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3080,10 +3080,11 @@ void valleyview_set_rps(struct drm_device *dev, u8 val) WARN_ON(val > dev_priv->rps.max_delay); WARN_ON(val < dev_priv->rps.min_delay); - DRM_DEBUG_DRIVER("gpu freq request from %d to %d\n", + DRM_DEBUG_DRIVER("GPU freq request from %d MHz (%u) to %d MHz (%u)\n", vlv_gpu_freq(dev_priv->mem_freq, dev_priv->rps.cur_delay), - vlv_gpu_freq(dev_priv->mem_freq, val)); + dev_priv->rps.cur_delay, + vlv_gpu_freq(dev_priv->mem_freq, val), val); if (val == dev_priv->rps.cur_delay) return; @@ -3101,8 +3102,9 @@ void valleyview_set_rps(struct drm_device *dev, u8 val) pval = vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS); if ((pval >> 8) != val) - DRM_DEBUG_DRIVER("punit overrode freq: %d requested, but got %d\n", - val, pval >> 8); + DRM_DEBUG_DRIVER("Punit overrode GPU freq: %d MHz (%u) requested, but got %d Mhz (%u)\n", + vlv_gpu_freq(dev_priv->mem_freq, val), val, + vlv_gpu_freq(dev_priv->mem_freq, pval >> 8), pval >> 8); /* Make sure we continue to get interrupts * until we hit the minimum or maximum frequencies. @@ -3496,7 +3498,7 @@ static void valleyview_enable_rps(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct intel_ring_buffer *ring; - u32 gtfifodbg, val, rpe; + u32 gtfifodbg, val; int i; WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock)); @@ -3557,31 +3559,39 @@ static void valleyview_enable_rps(struct drm_device *dev) DRM_DEBUG_DRIVER("GPLL enabled? %s\n", val & 0x10 ? "yes" : "no"); DRM_DEBUG_DRIVER("GPU status: 0x%08x\n", val); - DRM_DEBUG_DRIVER("current GPU freq: %d\n", - vlv_gpu_freq(dev_priv->mem_freq, (val >> 8) & 0xff)); dev_priv->rps.cur_delay = (val >> 8) & 0xff; + DRM_DEBUG_DRIVER("current GPU freq: %d MHz (%u)\n", + vlv_gpu_freq(dev_priv->mem_freq, + dev_priv->rps.cur_delay), + dev_priv->rps.cur_delay); dev_priv->rps.max_delay = valleyview_rps_max_freq(dev_priv); dev_priv->rps.hw_max = dev_priv->rps.max_delay; - DRM_DEBUG_DRIVER("max GPU freq: %d\n", vlv_gpu_freq(dev_priv->mem_freq, - dev_priv->rps.max_delay)); + DRM_DEBUG_DRIVER("max GPU freq: %d MHz (%u)\n", + vlv_gpu_freq(dev_priv->mem_freq, + dev_priv->rps.max_delay), + dev_priv->rps.max_delay); - rpe = valleyview_rps_rpe_freq(dev_priv); - DRM_DEBUG_DRIVER("RPe GPU freq: %d\n", - vlv_gpu_freq(dev_priv->mem_freq, rpe)); - dev_priv->rps.rpe_delay = rpe; + dev_priv->rps.rpe_delay = valleyview_rps_rpe_freq(dev_priv); + DRM_DEBUG_DRIVER("RPe GPU freq: %d MHz (%u)\n", + vlv_gpu_freq(dev_priv->mem_freq, + dev_priv->rps.rpe_delay), + dev_priv->rps.rpe_delay); - val = valleyview_rps_min_freq(dev_priv); - DRM_DEBUG_DRIVER("min GPU freq: %d\n", vlv_gpu_freq(dev_priv->mem_freq, - val)); - dev_priv->rps.min_delay = val; + dev_priv->rps.min_delay = valleyview_rps_min_freq(dev_priv); + DRM_DEBUG_DRIVER("min GPU freq: %d MHz (%u)\n", + vlv_gpu_freq(dev_priv->mem_freq, + dev_priv->rps.min_delay), + dev_priv->rps.min_delay); - DRM_DEBUG_DRIVER("setting GPU freq to %d\n", - vlv_gpu_freq(dev_priv->mem_freq, rpe)); + DRM_DEBUG_DRIVER("setting GPU freq to %d MHz (%u)\n", + vlv_gpu_freq(dev_priv->mem_freq, + dev_priv->rps.rpe_delay), + dev_priv->rps.rpe_delay); INIT_DELAYED_WORK(&dev_priv->rps.vlv_work, vlv_rps_timer_work); - valleyview_set_rps(dev_priv->dev, rpe); + valleyview_set_rps(dev_priv->dev, dev_priv->rps.rpe_delay); /* requires MSI enabled */ I915_WRITE(GEN6_PMIER, GEN6_PM_RPS_EVENTS); -- cgit v1.2.3 From 80814ae4dae5d2070b1ca848df728feb6a10e6f2 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Tue, 25 Jun 2013 19:21:02 +0300 Subject: drm/i915: Don't wait for Punit after each freq change on VLV MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It seems that even though Punit reports the frequency change to have been completed, it still reports the old frequency in the status register for some time. So rather than polling for Punit to complete the frequency change after each request, poll before. This gets rid of the spurious "Punit overrode GPU freq" messages. This also lets us continue working while Punit is performing the actual frequency change. As a result, openarena demo088-test1 timedemo average fps is increased by ~5 fps, and the slowest frame duration is reduced by ~25%. The sysfs cur_freq file always reads the current frequency from Punit anyway, so having rps.cur_delay be slightly off at times doesn't matter. Signed-off-by: Ville Syrjälä Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_pm.c | 53 +++++++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 33aa2d6674e5..909dbe1e39fc 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3069,17 +3069,49 @@ void gen6_set_rps(struct drm_device *dev, u8 val) trace_intel_gpu_freq_change(val * 50); } +/* + * Wait until the previous freq change has completed, + * or the timeout elapsed, and then update our notion + * of the current GPU frequency. + */ +static void vlv_update_rps_cur_delay(struct drm_i915_private *dev_priv) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(10); + u32 pval; + + WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock)); + + do { + pval = vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS); + if (time_after(jiffies, timeout)) { + DRM_DEBUG_DRIVER("timed out waiting for Punit\n"); + break; + } + udelay(10); + } while (pval & 1); + + pval >>= 8; + + if (pval != dev_priv->rps.cur_delay) + DRM_DEBUG_DRIVER("Punit overrode GPU freq: %d MHz (%u) requested, but got %d Mhz (%u)\n", + vlv_gpu_freq(dev_priv->mem_freq, dev_priv->rps.cur_delay), + dev_priv->rps.cur_delay, + vlv_gpu_freq(dev_priv->mem_freq, pval), pval); + + dev_priv->rps.cur_delay = pval; +} + void valleyview_set_rps(struct drm_device *dev, u8 val) { struct drm_i915_private *dev_priv = dev->dev_private; - unsigned long timeout = jiffies + msecs_to_jiffies(10); u32 limits = gen6_rps_limits(dev_priv, &val); - u32 pval; WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock)); WARN_ON(val > dev_priv->rps.max_delay); WARN_ON(val < dev_priv->rps.min_delay); + vlv_update_rps_cur_delay(dev_priv); + DRM_DEBUG_DRIVER("GPU freq request from %d MHz (%u) to %d MHz (%u)\n", vlv_gpu_freq(dev_priv->mem_freq, dev_priv->rps.cur_delay), @@ -3091,27 +3123,12 @@ void valleyview_set_rps(struct drm_device *dev, u8 val) vlv_punit_write(dev_priv, PUNIT_REG_GPU_FREQ_REQ, val); - do { - pval = vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS); - if (time_after(jiffies, timeout)) { - DRM_DEBUG_DRIVER("timed out waiting for Punit\n"); - break; - } - udelay(10); - } while (pval & 1); - - pval = vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS); - if ((pval >> 8) != val) - DRM_DEBUG_DRIVER("Punit overrode GPU freq: %d MHz (%u) requested, but got %d Mhz (%u)\n", - vlv_gpu_freq(dev_priv->mem_freq, val), val, - vlv_gpu_freq(dev_priv->mem_freq, pval >> 8), pval >> 8); - /* Make sure we continue to get interrupts * until we hit the minimum or maximum frequencies. */ I915_WRITE(GEN6_RP_INTERRUPT_LIMITS, limits); - dev_priv->rps.cur_delay = pval >> 8; + dev_priv->rps.cur_delay = val; trace_intel_gpu_freq_change(vlv_gpu_freq(dev_priv->mem_freq, val)); } -- cgit v1.2.3 From d8289c9e7bb34b136433a44937e3ebe3a7beea1c Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Tue, 25 Jun 2013 19:21:05 +0300 Subject: drm/i915: Make the rps new_delay comparison more readable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Eliminate the weird inverted logic from the rps new_delay comparison. Signed-off-by: Ville Syrjälä Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 611da3a74479..62f8b2d8809c 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -707,8 +707,8 @@ static void gen6_pm_rps_work(struct work_struct *work) /* sysfs frequency interfaces may have snuck in while servicing the * interrupt */ - if (!(new_delay > dev_priv->rps.max_delay || - new_delay < dev_priv->rps.min_delay)) { + if (new_delay >= dev_priv->rps.min_delay && + new_delay <= dev_priv->rps.max_delay) { if (IS_VALLEYVIEW(dev_priv->dev)) valleyview_set_rps(dev_priv->dev, new_delay); else -- cgit v1.2.3 From 7a67092a25d854a4f5c53a6495d33e250896f9db Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Tue, 25 Jun 2013 19:21:06 +0300 Subject: drm/i915: GEN6_RP_INTERRUPT_LIMITS doesn't seem to exist on VLV MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I can't find GEN6_RP_INTERRUPT_LIMITS (0xA014) anywhere in VLV docs. Reading it always returns zero from what I can tell, and eliminating it doesn't seem to make any difference to the behaviour of the system. Signed-off-by: Ville Syrjälä Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_pm.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 909dbe1e39fc..ed929667c5ba 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3104,7 +3104,8 @@ static void vlv_update_rps_cur_delay(struct drm_i915_private *dev_priv) void valleyview_set_rps(struct drm_device *dev, u8 val) { struct drm_i915_private *dev_priv = dev->dev_private; - u32 limits = gen6_rps_limits(dev_priv, &val); + + gen6_rps_limits(dev_priv, &val); WARN_ON(!mutex_is_locked(&dev_priv->rps.hw_lock)); WARN_ON(val > dev_priv->rps.max_delay); @@ -3123,11 +3124,6 @@ void valleyview_set_rps(struct drm_device *dev, u8 val) vlv_punit_write(dev_priv, PUNIT_REG_GPU_FREQ_REQ, val); - /* Make sure we continue to get interrupts - * until we hit the minimum or maximum frequencies. - */ - I915_WRITE(GEN6_RP_INTERRUPT_LIMITS, limits); - dev_priv->rps.cur_delay = val; trace_intel_gpu_freq_change(vlv_gpu_freq(dev_priv->mem_freq, val)); -- cgit v1.2.3 From 6dc5848899a7ddbf1d02f104a97dde7ba0200693 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Tue, 25 Jun 2013 21:38:10 +0300 Subject: drm/i915: Don't increase the GPU frequency from the delayed VLV rps timer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There's little point in increasing the GPU frequency from the delayed rps work on VLV. Now when the GPU is idle, the GPU frequency actually keeps dropping gradually until it hits the minimum, whereas previously it just ping-ponged constantly between RPe and RPe-1. Signed-off-by: Ville Syrjälä Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_pm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index ed929667c5ba..ccbdd83f5220 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3461,7 +3461,8 @@ static void vlv_rps_timer_work(struct work_struct *work) * min freq available. */ mutex_lock(&dev_priv->rps.hw_lock); - valleyview_set_rps(dev_priv->dev, dev_priv->rps.rpe_delay); + if (dev_priv->rps.cur_delay > dev_priv->rps.rpe_delay) + valleyview_set_rps(dev_priv->dev, dev_priv->rps.rpe_delay); mutex_unlock(&dev_priv->rps.hw_lock); } -- cgit v1.2.3 From 7425034a3361145c109510892d1e5154af2cdfed Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Tue, 25 Jun 2013 21:38:11 +0300 Subject: drm/i915: Jump to at least RPe on VLV when increasing the GPU frequency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the current GPU frquency is below RPe, and we're asked to increase it, just go directly to RPe. This should provide better performance faster than letting the frequency trickle up in response to the up threshold interrupts. For now just do it for VLV, since that matches quite closely how VLV used to operate when the rps delayed timer kept things at RPe always. Signed-off-by: Ville Syrjälä Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 62f8b2d8809c..d6bd0d7a7486 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -699,9 +699,17 @@ static void gen6_pm_rps_work(struct work_struct *work) mutex_lock(&dev_priv->rps.hw_lock); - if (pm_iir & GEN6_PM_RP_UP_THRESHOLD) + if (pm_iir & GEN6_PM_RP_UP_THRESHOLD) { new_delay = dev_priv->rps.cur_delay + 1; - else + + /* + * For better performance, jump directly + * to RPe if we're below it. + */ + if (IS_VALLEYVIEW(dev_priv->dev) && + dev_priv->rps.cur_delay < dev_priv->rps.rpe_delay) + new_delay = dev_priv->rps.rpe_delay; + } else new_delay = dev_priv->rps.cur_delay - 1; /* sysfs frequency interfaces may have snuck in while servicing the -- cgit v1.2.3 From 99750bd46f6ad3816fe2045a34fac432114c8196 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Fri, 14 Jun 2013 14:02:52 +0300 Subject: drm/i915: Fix VLV PLL LPF coefficients for DAC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current PLL settings produce a rather unstable picture when I hook up a VLV to my HP ZR24w display via a VGA cable. According to VLV2A0_DP_eDP_HDMI_DPIO_driver_vbios_notes_9, we should use the the same LPF coefficients for DAC as we do for HDMI and RBR DP. And indeed that seems to cure the shivers. v2: Add the name of the relevant document to the commit message Signed-off-by: Ville Syrjälä Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index d723819a450c..749d4282f5ad 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4404,6 +4404,7 @@ static void vlv_update_pll(struct intel_crtc *crtc) /* Set HBR and RBR LPF coefficients */ if (crtc->config.port_clock == 162000 || + intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_ANALOG) || intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_HDMI)) vlv_dpio_write(dev_priv, DPIO_LFP_COEFF(pipe), 0x005f0021); -- cgit v1.2.3 From 4abb2c39811eb81a653461d5ed8c75c528cb2245 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Fri, 14 Jun 2013 14:02:53 +0300 Subject: drm/i915: s/LFP/LPF in DPIO PLL register names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LPF is short for "low pass filter". Signed-off-by: Ville Syrjälä Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 8 ++++---- drivers/gpu/drm/i915/i915_reg.h | 6 +++--- drivers/gpu/drm/i915/intel_display.c | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index f72d5a3fdfba..04cf6c09710a 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1862,10 +1862,10 @@ static int i915_dpio_info(struct seq_file *m, void *data) seq_printf(m, "DPIO_CORE_CLK_B: 0x%08x\n", vlv_dpio_read(dev_priv, _DPIO_CORE_CLK_B)); - seq_printf(m, "DPIO_LFP_COEFF_A: 0x%08x\n", - vlv_dpio_read(dev_priv, _DPIO_LFP_COEFF_A)); - seq_printf(m, "DPIO_LFP_COEFF_B: 0x%08x\n", - vlv_dpio_read(dev_priv, _DPIO_LFP_COEFF_B)); + seq_printf(m, "DPIO_LPF_COEFF_A: 0x%08x\n", + vlv_dpio_read(dev_priv, _DPIO_LPF_COEFF_A)); + seq_printf(m, "DPIO_LPF_COEFF_B: 0x%08x\n", + vlv_dpio_read(dev_priv, _DPIO_LPF_COEFF_B)); seq_printf(m, "DPIO_FASTCLK_DISABLE: 0x%08x\n", vlv_dpio_read(dev_priv, DPIO_FASTCLK_DISABLE)); diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 137be4c1abd1..b6f1fd988df5 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -448,9 +448,9 @@ #define _DPIO_PLL_CML_B 0x806c #define DPIO_PLL_CML(pipe) _PIPE(pipe, _DPIO_PLL_CML_A, _DPIO_PLL_CML_B) -#define _DPIO_LFP_COEFF_A 0x8048 -#define _DPIO_LFP_COEFF_B 0x8068 -#define DPIO_LFP_COEFF(pipe) _PIPE(pipe, _DPIO_LFP_COEFF_A, _DPIO_LFP_COEFF_B) +#define _DPIO_LPF_COEFF_A 0x8048 +#define _DPIO_LPF_COEFF_B 0x8068 +#define DPIO_LPF_COEFF(pipe) _PIPE(pipe, _DPIO_LPF_COEFF_A, _DPIO_LPF_COEFF_B) #define DPIO_CALIBRATION 0x80ac diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 749d4282f5ad..85f3eb74d2b7 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4406,10 +4406,10 @@ static void vlv_update_pll(struct intel_crtc *crtc) if (crtc->config.port_clock == 162000 || intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_ANALOG) || intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_HDMI)) - vlv_dpio_write(dev_priv, DPIO_LFP_COEFF(pipe), + vlv_dpio_write(dev_priv, DPIO_LPF_COEFF(pipe), 0x005f0021); else - vlv_dpio_write(dev_priv, DPIO_LFP_COEFF(pipe), + vlv_dpio_write(dev_priv, DPIO_LPF_COEFF(pipe), 0x00d0000f); if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_EDP) || -- cgit v1.2.3 From 2af2c4909b7c8bc76beef25941b2218b3bd8e4fc Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Tue, 25 Jun 2013 14:16:34 +0300 Subject: Revert "drm/i915: Don't use the HDMI port color range bit on Valleyview" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PIPECONF color range bit doesn't appear to be effective, on HDMI outputs at least. The color range bit in the port register works though, so let's use it. I have not yet verified whether the PIPECONF bit works on DP outputs. This reverts commit 83a2af88f80ebf8104c9e083b786668b00f5b9ce. Signed-off-by: Ville Syrjälä Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_hdmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index bc12518a21b4..98df2a0c85bd 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -602,7 +602,7 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder, u32 hdmi_val; hdmi_val = SDVO_ENCODING_HDMI; - if (!HAS_PCH_SPLIT(dev) && !IS_VALLEYVIEW(dev)) + if (!HAS_PCH_SPLIT(dev)) hdmi_val |= intel_hdmi->color_range; if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) hdmi_val |= SDVO_VSYNC_ACTIVE_HIGH; -- cgit v1.2.3 From 921c3b677bf6340cd92800fb99350532674dea29 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Tue, 25 Jun 2013 14:16:35 +0300 Subject: drm/i915: Fix VLV sprite register offsets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We forgot to add VLV_DISPLAY_BASE to the VLV sprite registers, which caused the sprites to not work at all. Signed-off-by: Ville Syrjälä Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 50 ++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index b6f1fd988df5..e1df06d1917f 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -3487,7 +3487,7 @@ #define SPRGAMC(pipe) _PIPE(pipe, _SPRA_GAMC, _SPRB_GAMC) #define SPRSURFLIVE(pipe) _PIPE(pipe, _SPRA_SURFLIVE, _SPRB_SURFLIVE) -#define _SPACNTR 0x72180 +#define _SPACNTR (VLV_DISPLAY_BASE + 0x72180) #define SP_ENABLE (1<<31) #define SP_GEAMMA_ENABLE (1<<30) #define SP_PIXFORMAT_MASK (0xf<<26) @@ -3506,30 +3506,30 @@ #define SP_YUV_ORDER_YVYU (2<<16) #define SP_YUV_ORDER_VYUY (3<<16) #define SP_TILED (1<<10) -#define _SPALINOFF 0x72184 -#define _SPASTRIDE 0x72188 -#define _SPAPOS 0x7218c -#define _SPASIZE 0x72190 -#define _SPAKEYMINVAL 0x72194 -#define _SPAKEYMSK 0x72198 -#define _SPASURF 0x7219c -#define _SPAKEYMAXVAL 0x721a0 -#define _SPATILEOFF 0x721a4 -#define _SPACONSTALPHA 0x721a8 -#define _SPAGAMC 0x721f4 - -#define _SPBCNTR 0x72280 -#define _SPBLINOFF 0x72284 -#define _SPBSTRIDE 0x72288 -#define _SPBPOS 0x7228c -#define _SPBSIZE 0x72290 -#define _SPBKEYMINVAL 0x72294 -#define _SPBKEYMSK 0x72298 -#define _SPBSURF 0x7229c -#define _SPBKEYMAXVAL 0x722a0 -#define _SPBTILEOFF 0x722a4 -#define _SPBCONSTALPHA 0x722a8 -#define _SPBGAMC 0x722f4 +#define _SPALINOFF (VLV_DISPLAY_BASE + 0x72184) +#define _SPASTRIDE (VLV_DISPLAY_BASE + 0x72188) +#define _SPAPOS (VLV_DISPLAY_BASE + 0x7218c) +#define _SPASIZE (VLV_DISPLAY_BASE + 0x72190) +#define _SPAKEYMINVAL (VLV_DISPLAY_BASE + 0x72194) +#define _SPAKEYMSK (VLV_DISPLAY_BASE + 0x72198) +#define _SPASURF (VLV_DISPLAY_BASE + 0x7219c) +#define _SPAKEYMAXVAL (VLV_DISPLAY_BASE + 0x721a0) +#define _SPATILEOFF (VLV_DISPLAY_BASE + 0x721a4) +#define _SPACONSTALPHA (VLV_DISPLAY_BASE + 0x721a8) +#define _SPAGAMC (VLV_DISPLAY_BASE + 0x721f4) + +#define _SPBCNTR (VLV_DISPLAY_BASE + 0x72280) +#define _SPBLINOFF (VLV_DISPLAY_BASE + 0x72284) +#define _SPBSTRIDE (VLV_DISPLAY_BASE + 0x72288) +#define _SPBPOS (VLV_DISPLAY_BASE + 0x7228c) +#define _SPBSIZE (VLV_DISPLAY_BASE + 0x72290) +#define _SPBKEYMINVAL (VLV_DISPLAY_BASE + 0x72294) +#define _SPBKEYMSK (VLV_DISPLAY_BASE + 0x72298) +#define _SPBSURF (VLV_DISPLAY_BASE + 0x7229c) +#define _SPBKEYMAXVAL (VLV_DISPLAY_BASE + 0x722a0) +#define _SPBTILEOFF (VLV_DISPLAY_BASE + 0x722a4) +#define _SPBCONSTALPHA (VLV_DISPLAY_BASE + 0x722a8) +#define _SPBGAMC (VLV_DISPLAY_BASE + 0x722f4) #define SPCNTR(pipe, plane) _PIPE(pipe * 2 + plane, _SPACNTR, _SPBCNTR) #define SPLINOFF(pipe, plane) _PIPE(pipe * 2 + plane, _SPALINOFF, _SPBLINOFF) -- cgit v1.2.3 From a0de80a0e07032a111230ec92eca563f9d93648d Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Tue, 25 Jun 2013 21:53:40 -0700 Subject: drm/i915: Fix context sizes on HSW With updates to the spec, we can actually see the context layout, and how many dwords are allocated. That table suggests we need 70720 bytes per HW context. Rounded up, this is 18 pages. Looking at what lives after the current 4 pages we use, I can't see too much important (mostly it's d3d related), but there are a couple of things which look scary. I am hopeful this can explain some of our odd HSW failures. v2: Make the context only 17 pages. The power context space isn't used ever, and execlists aren't used in our driver, making the actual total 66944 bytes. v3: Add a comment to the code. (Jesse & Paulo) Reported-by: "Azad, Vinit" Cc: stable@vger.kernel.org Reviewed-by: Jesse Barnes Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_context.c | 2 +- drivers/gpu/drm/i915/i915_reg.h | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index ff471454968d..51b7a2171cae 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -113,7 +113,7 @@ static int get_context_size(struct drm_device *dev) case 7: reg = I915_READ(GEN7_CXT_SIZE); if (IS_HASWELL(dev)) - ret = HSW_CXT_TOTAL_SIZE(reg) * 64; + ret = HSW_CXT_TOTAL_SIZE; else ret = GEN7_CXT_TOTAL_SIZE(reg) * 64; break; diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index e1df06d1917f..f2326fc60ac9 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -1718,14 +1718,13 @@ GEN7_CXT_EXTENDED_SIZE(ctx_reg) + \ GEN7_CXT_GT1_SIZE(ctx_reg) + \ GEN7_CXT_VFSTATE_SIZE(ctx_reg)) -#define HSW_CXT_POWER_SIZE(ctx_reg) ((ctx_reg >> 26) & 0x3f) -#define HSW_CXT_RING_SIZE(ctx_reg) ((ctx_reg >> 23) & 0x7) -#define HSW_CXT_RENDER_SIZE(ctx_reg) ((ctx_reg >> 15) & 0xff) -#define HSW_CXT_TOTAL_SIZE(ctx_reg) (HSW_CXT_POWER_SIZE(ctx_reg) + \ - HSW_CXT_RING_SIZE(ctx_reg) + \ - HSW_CXT_RENDER_SIZE(ctx_reg) + \ - GEN7_CXT_VFSTATE_SIZE(ctx_reg)) - +/* Haswell does have the CXT_SIZE register however it does not appear to be + * valid. Now, docs explain in dwords what is in the context object. The full + * size is 70720 bytes, however, the power context and execlist context will + * never be saved (power context is stored elsewhere, and execlists don't work + * on HSW) - so the final size is 66944 bytes, which rounds to 17 pages. + */ +#define HSW_CXT_TOTAL_SIZE (17 * PAGE_SIZE) /* * Overlay regs -- cgit v1.2.3 From 4bc9d4301573403c578e545b34dceac61891f39c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 27 Jun 2013 13:44:58 +0200 Subject: drm/i915: fix locking around ironlake_enable|disable_display_irq The haswell unclaimed register handling code forgot to take the spinlock. Since this is in the context of the non-rentrant interupt handler and we only have one interrupt handler it is sufficient to just grab the spinlock - we do not need to exclude any other interrupts from running on the same cpu. To prevent such gaffles in the future sprinkle assert_spin_locked over these functions. Unfornately this requires us to hold the spinlock in the ironlake postinstall hook where it is not strictly required: Currently that is run in single-threaded context and with userspace exlcuded from running concurrent ioctls. Add a comment explaining this. v2: ivb_can_enable_err_int also needs to be protected by the spinlock. To ensure this won't happen in the future again also sprinkle a spinlock assert in there. v3: Kill the 2nd call to ivb_can_enable_err_int I've accidentally left behind, spotted by Paulo. Cc: Paulo Zanoni Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index d6bd0d7a7486..61cd87999df3 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -86,6 +86,8 @@ static void i915_hpd_irq_setup(struct drm_device *dev); static void ironlake_enable_display_irq(drm_i915_private_t *dev_priv, u32 mask) { + assert_spin_locked(&dev_priv->irq_lock); + if ((dev_priv->irq_mask & mask) != 0) { dev_priv->irq_mask &= ~mask; I915_WRITE(DEIMR, dev_priv->irq_mask); @@ -96,6 +98,8 @@ ironlake_enable_display_irq(drm_i915_private_t *dev_priv, u32 mask) static void ironlake_disable_display_irq(drm_i915_private_t *dev_priv, u32 mask) { + assert_spin_locked(&dev_priv->irq_lock); + if ((dev_priv->irq_mask & mask) != mask) { dev_priv->irq_mask |= mask; I915_WRITE(DEIMR, dev_priv->irq_mask); @@ -109,6 +113,8 @@ static bool ivb_can_enable_err_int(struct drm_device *dev) struct intel_crtc *crtc; enum pipe pipe; + assert_spin_locked(&dev_priv->irq_lock); + for_each_pipe(pipe) { crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]); @@ -1217,8 +1223,11 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg) /* On Haswell, also mask ERR_INT because we don't want to risk * generating "unclaimed register" interrupts from inside the interrupt * handler. */ - if (IS_HASWELL(dev)) + if (IS_HASWELL(dev)) { + spin_lock(&dev_priv->irq_lock); ironlake_disable_display_irq(dev_priv, DE_ERR_INT_IVB); + spin_unlock(&dev_priv->irq_lock); + } gt_iir = I915_READ(GTIIR); if (gt_iir) { @@ -1271,8 +1280,12 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg) ret = IRQ_HANDLED; } - if (IS_HASWELL(dev) && ivb_can_enable_err_int(dev)) - ironlake_enable_display_irq(dev_priv, DE_ERR_INT_IVB); + if (IS_HASWELL(dev)) { + spin_lock(&dev_priv->irq_lock); + if (ivb_can_enable_err_int(dev)) + ironlake_enable_display_irq(dev_priv, DE_ERR_INT_IVB); + spin_unlock(&dev_priv->irq_lock); + } I915_WRITE(DEIER, de_ier); POSTING_READ(DEIER); @@ -2697,6 +2710,8 @@ static void ibx_irq_postinstall(struct drm_device *dev) static int ironlake_irq_postinstall(struct drm_device *dev) { + unsigned long irqflags; + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; /* enable kind of interrupts always enabled */ u32 display_mask = DE_MASTER_IRQ_CONTROL | DE_GSE | DE_PCH_EVENT | @@ -2735,7 +2750,13 @@ static int ironlake_irq_postinstall(struct drm_device *dev) /* Clear & enable PCU event interrupts */ I915_WRITE(DEIIR, DE_PCU_EVENT); I915_WRITE(DEIER, I915_READ(DEIER) | DE_PCU_EVENT); + + /* spinlocking not required here for correctness since interrupt + * setup is guaranteed to run in single-threaded context. But we + * need it to make the assert_spin_locked happy. */ + spin_lock_irqsave(&dev_priv->irq_lock, irqflags); ironlake_enable_display_irq(dev_priv, DE_PCU_EVENT); + spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); } return 0; -- cgit v1.2.3 From 6005ce42433df3f69de99d7e730383a6adb852ef Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 27 Jun 2013 13:44:59 +0200 Subject: drm/i915: close tiny race in the ilk pcu even interrupt setup By the time we write DEIER in the postinstall hook the interrupt handler could run any time. And it does modify DEIER to handle interrupts. Hence the DEIER read-modify-write cycle for enabling the PCU event source is racy. Close this races the same way we handle vblank interrupts: Unconditionally enable the interrupt in the IER register, but conditionally mask it in IMR. The later poses no such race since the interrupt handler does not touch DEIMR. Also update the comment, the clearing has already happened unconditionally above. v2: Actually shove the updated comment into the right train^W commit, as spotted by Paulo. Cc: Paulo Zanoni Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 61cd87999df3..bff9abda81c6 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -2725,7 +2725,8 @@ static int ironlake_irq_postinstall(struct drm_device *dev) /* should always can generate irq */ I915_WRITE(DEIIR, I915_READ(DEIIR)); I915_WRITE(DEIMR, dev_priv->irq_mask); - I915_WRITE(DEIER, display_mask | DE_PIPEA_VBLANK | DE_PIPEB_VBLANK); + I915_WRITE(DEIER, display_mask | + DE_PIPEA_VBLANK | DE_PIPEB_VBLANK | DE_PCU_EVENT); POSTING_READ(DEIER); dev_priv->gt_irq_mask = ~0; @@ -2747,11 +2748,9 @@ static int ironlake_irq_postinstall(struct drm_device *dev) ibx_irq_postinstall(dev); if (IS_IRONLAKE_M(dev)) { - /* Clear & enable PCU event interrupts */ - I915_WRITE(DEIIR, DE_PCU_EVENT); - I915_WRITE(DEIER, I915_READ(DEIER) | DE_PCU_EVENT); - - /* spinlocking not required here for correctness since interrupt + /* Enable PCU event interrupts + * + * spinlocking not required here for correctness since interrupt * setup is guaranteed to run in single-threaded context. But we * need it to make the assert_spin_locked happy. */ spin_lock_irqsave(&dev_priv->irq_lock, irqflags); -- cgit v1.2.3 From 22062dbacf7ccc325c8b0ccc3fb90bf487be5c5c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 27 Jun 2013 17:52:11 +0200 Subject: drm/i915: s/hotplug_irq_storm_detect/intel_hpd_irq_handler/ The combination of Paulo's fifo underrun detection code and Egbert's hpd storm handling code unfortunately made the hpd storm handling code racy. To avoid duplicating tricky interrupt locking code over all platforms start with a bit of refactoring. This patch is the very first step since in the end the irq storm handling code will handle all hotplug logic (and so also encapsulate the locking nicely). v2: Rebase on top of the i965g/gm sdvo hpd fix. Cc: Egbert Eich Reviewed-by: Egbert Eich Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index bff9abda81c6..378ade0d8c9f 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -875,9 +875,9 @@ static void gen6_queue_rps_work(struct drm_i915_private *dev_priv, #define HPD_STORM_DETECT_PERIOD 1000 #define HPD_STORM_THRESHOLD 5 -static inline bool hotplug_irq_storm_detect(struct drm_device *dev, - u32 hotplug_trigger, - const u32 *hpd) +static inline bool intel_hpd_irq_handler(struct drm_device *dev, + u32 hotplug_trigger, + const u32 *hpd) { drm_i915_private_t *dev_priv = dev->dev_private; unsigned long irqflags; @@ -1018,7 +1018,7 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg) DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x\n", hotplug_status); if (hotplug_trigger) { - if (hotplug_irq_storm_detect(dev, hotplug_trigger, hpd_status_i915)) + if (intel_hpd_irq_handler(dev, hotplug_trigger, hpd_status_i915)) i915_hpd_irq_setup(dev); queue_work(dev_priv->wq, &dev_priv->hotplug_work); @@ -1049,7 +1049,7 @@ static void ibx_irq_handler(struct drm_device *dev, u32 pch_iir) u32 hotplug_trigger = pch_iir & SDE_HOTPLUG_MASK; if (hotplug_trigger) { - if (hotplug_irq_storm_detect(dev, hotplug_trigger, hpd_ibx)) + if (intel_hpd_irq_handler(dev, hotplug_trigger, hpd_ibx)) ibx_hpd_irq_setup(dev); queue_work(dev_priv->wq, &dev_priv->hotplug_work); } @@ -1154,7 +1154,7 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir) u32 hotplug_trigger = pch_iir & SDE_HOTPLUG_MASK_CPT; if (hotplug_trigger) { - if (hotplug_irq_storm_detect(dev, hotplug_trigger, hpd_cpt)) + if (intel_hpd_irq_handler(dev, hotplug_trigger, hpd_cpt)) ibx_hpd_irq_setup(dev); queue_work(dev_priv->wq, &dev_priv->hotplug_work); } @@ -3232,7 +3232,7 @@ static irqreturn_t i915_irq_handler(int irq, void *arg) DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x\n", hotplug_status); if (hotplug_trigger) { - if (hotplug_irq_storm_detect(dev, hotplug_trigger, hpd_status_i915)) + if (intel_hpd_irq_handler(dev, hotplug_trigger, hpd_status_i915)) i915_hpd_irq_setup(dev); queue_work(dev_priv->wq, &dev_priv->hotplug_work); @@ -3473,7 +3473,7 @@ static irqreturn_t i965_irq_handler(int irq, void *arg) DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x\n", hotplug_status); if (hotplug_trigger) { - if (hotplug_irq_storm_detect(dev, hotplug_trigger, + if (intel_hpd_irq_handler(dev, hotplug_trigger, IS_G4X(dev) ? hpd_status_gen4 : hpd_status_i915)) i915_hpd_irq_setup(dev); queue_work(dev_priv->wq, -- cgit v1.2.3 From 10a504de56f0451c828a9207e36c5610d5b0209a Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 27 Jun 2013 17:52:12 +0200 Subject: drm/i915: fold the hpd_irq_setup call into intel_hpd_irq_handler We already have a vfunc for this (and other parts of the hpd storm handling code already use it). v2: Rebase on top of the i965g/gm sdvo hpd fix. Cc: Egbert Eich Reviewed-by: Egbert Eich Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 378ade0d8c9f..c1e9b0a0861c 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -79,9 +79,6 @@ static const u32 hpd_status_i915[] = { /* i915 and valleyview are the same */ [HPD_PORT_D] = PORTD_HOTPLUG_INT_STATUS }; -static void ibx_hpd_irq_setup(struct drm_device *dev); -static void i915_hpd_irq_setup(struct drm_device *dev); - /* For display hotplug interrupt */ static void ironlake_enable_display_irq(drm_i915_private_t *dev_priv, u32 mask) @@ -875,14 +872,14 @@ static void gen6_queue_rps_work(struct drm_i915_private *dev_priv, #define HPD_STORM_DETECT_PERIOD 1000 #define HPD_STORM_THRESHOLD 5 -static inline bool intel_hpd_irq_handler(struct drm_device *dev, +static inline void intel_hpd_irq_handler(struct drm_device *dev, u32 hotplug_trigger, const u32 *hpd) { drm_i915_private_t *dev_priv = dev->dev_private; unsigned long irqflags; int i; - bool ret = false; + bool storm_detected = false; spin_lock_irqsave(&dev_priv->irq_lock, irqflags); @@ -902,7 +899,7 @@ static inline bool intel_hpd_irq_handler(struct drm_device *dev, dev_priv->hpd_stats[i].hpd_mark = HPD_MARK_DISABLED; dev_priv->hpd_event_bits &= ~(1 << i); DRM_DEBUG_KMS("HPD interrupt storm detected on PIN %d\n", i); - ret = true; + storm_detected = true; } else { dev_priv->hpd_stats[i].hpd_cnt++; } @@ -910,7 +907,8 @@ static inline bool intel_hpd_irq_handler(struct drm_device *dev, spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); - return ret; + if (storm_detected) + dev_priv->display.hpd_irq_setup(dev); } static void gmbus_irq_handler(struct drm_device *dev) @@ -1018,8 +1016,7 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg) DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x\n", hotplug_status); if (hotplug_trigger) { - if (intel_hpd_irq_handler(dev, hotplug_trigger, hpd_status_i915)) - i915_hpd_irq_setup(dev); + intel_hpd_irq_handler(dev, hotplug_trigger, hpd_status_i915); queue_work(dev_priv->wq, &dev_priv->hotplug_work); } @@ -1049,8 +1046,7 @@ static void ibx_irq_handler(struct drm_device *dev, u32 pch_iir) u32 hotplug_trigger = pch_iir & SDE_HOTPLUG_MASK; if (hotplug_trigger) { - if (intel_hpd_irq_handler(dev, hotplug_trigger, hpd_ibx)) - ibx_hpd_irq_setup(dev); + intel_hpd_irq_handler(dev, hotplug_trigger, hpd_ibx); queue_work(dev_priv->wq, &dev_priv->hotplug_work); } if (pch_iir & SDE_AUDIO_POWER_MASK) { @@ -1154,8 +1150,7 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir) u32 hotplug_trigger = pch_iir & SDE_HOTPLUG_MASK_CPT; if (hotplug_trigger) { - if (intel_hpd_irq_handler(dev, hotplug_trigger, hpd_cpt)) - ibx_hpd_irq_setup(dev); + intel_hpd_irq_handler(dev, hotplug_trigger, hpd_cpt); queue_work(dev_priv->wq, &dev_priv->hotplug_work); } if (pch_iir & SDE_AUDIO_POWER_MASK_CPT) { @@ -3232,8 +3227,7 @@ static irqreturn_t i915_irq_handler(int irq, void *arg) DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x\n", hotplug_status); if (hotplug_trigger) { - if (intel_hpd_irq_handler(dev, hotplug_trigger, hpd_status_i915)) - i915_hpd_irq_setup(dev); + intel_hpd_irq_handler(dev, hotplug_trigger, hpd_status_i915); queue_work(dev_priv->wq, &dev_priv->hotplug_work); } @@ -3473,9 +3467,8 @@ static irqreturn_t i965_irq_handler(int irq, void *arg) DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x\n", hotplug_status); if (hotplug_trigger) { - if (intel_hpd_irq_handler(dev, hotplug_trigger, - IS_G4X(dev) ? hpd_status_gen4 : hpd_status_i915)) - i915_hpd_irq_setup(dev); + intel_hpd_irq_handler(dev, hotplug_trigger, + IS_G4X(dev) ? hpd_status_gen4 : hpd_status_i915); queue_work(dev_priv->wq, &dev_priv->hotplug_work); } -- cgit v1.2.3 From 5876fa0d9e9097d980a72152f4967a52b1adaaac Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 27 Jun 2013 17:52:13 +0200 Subject: drm/i915: fold the queue_work into intel_hpd_irq_handler Everywhere the same. Note that this patch leaves unnecessary braces behind, but the next patch will kill those all anyway (including the if itself) so I've figured I can keep the diff a bit smaller. v2: Rebase on top of the i965g/gm sdvo hpd fix. Cc: Egbert Eich Reviewed-by: Egbert Eich Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index c1e9b0a0861c..531df31075d2 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -909,6 +909,9 @@ static inline void intel_hpd_irq_handler(struct drm_device *dev, if (storm_detected) dev_priv->display.hpd_irq_setup(dev); + + queue_work(dev_priv->wq, + &dev_priv->hotplug_work); } static void gmbus_irq_handler(struct drm_device *dev) @@ -1017,8 +1020,6 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg) hotplug_status); if (hotplug_trigger) { intel_hpd_irq_handler(dev, hotplug_trigger, hpd_status_i915); - queue_work(dev_priv->wq, - &dev_priv->hotplug_work); } I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status); I915_READ(PORT_HOTPLUG_STAT); @@ -1047,7 +1048,6 @@ static void ibx_irq_handler(struct drm_device *dev, u32 pch_iir) if (hotplug_trigger) { intel_hpd_irq_handler(dev, hotplug_trigger, hpd_ibx); - queue_work(dev_priv->wq, &dev_priv->hotplug_work); } if (pch_iir & SDE_AUDIO_POWER_MASK) { int port = ffs((pch_iir & SDE_AUDIO_POWER_MASK) >> @@ -1151,7 +1151,6 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir) if (hotplug_trigger) { intel_hpd_irq_handler(dev, hotplug_trigger, hpd_cpt); - queue_work(dev_priv->wq, &dev_priv->hotplug_work); } if (pch_iir & SDE_AUDIO_POWER_MASK_CPT) { int port = ffs((pch_iir & SDE_AUDIO_POWER_MASK_CPT) >> @@ -3228,8 +3227,6 @@ static irqreturn_t i915_irq_handler(int irq, void *arg) hotplug_status); if (hotplug_trigger) { intel_hpd_irq_handler(dev, hotplug_trigger, hpd_status_i915); - queue_work(dev_priv->wq, - &dev_priv->hotplug_work); } I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status); POSTING_READ(PORT_HOTPLUG_STAT); @@ -3469,8 +3466,6 @@ static irqreturn_t i965_irq_handler(int irq, void *arg) if (hotplug_trigger) { intel_hpd_irq_handler(dev, hotplug_trigger, IS_G4X(dev) ? hpd_status_gen4 : hpd_status_i915); - queue_work(dev_priv->wq, - &dev_priv->hotplug_work); } I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status); I915_READ(PORT_HOTPLUG_STAT); -- cgit v1.2.3 From 91d131d21e4916f84e5957cc25bea6dd355dfe77 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 27 Jun 2013 17:52:14 +0200 Subject: drm/i915: fold the no-irq check into intel_hpd_irq_handler The usual pattern for our sub-function irq_handlers is that they check for the no-irq case themselves. This results in more streamlined code in the upper irq handlers. v2: Rebase on top of the i965g/gm sdvo hpd fix. Cc: Egbert Eich Reviewed-by: Egbert Eich Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 531df31075d2..b29b76fe1123 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -881,6 +881,9 @@ static inline void intel_hpd_irq_handler(struct drm_device *dev, int i; bool storm_detected = false; + if (!hotplug_trigger) + return; + spin_lock_irqsave(&dev_priv->irq_lock, irqflags); for (i = 1; i < HPD_NUM_PINS; i++) { @@ -1018,9 +1021,9 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg) DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x\n", hotplug_status); - if (hotplug_trigger) { - intel_hpd_irq_handler(dev, hotplug_trigger, hpd_status_i915); - } + + intel_hpd_irq_handler(dev, hotplug_trigger, hpd_status_i915); + I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status); I915_READ(PORT_HOTPLUG_STAT); } @@ -1046,9 +1049,8 @@ static void ibx_irq_handler(struct drm_device *dev, u32 pch_iir) int pipe; u32 hotplug_trigger = pch_iir & SDE_HOTPLUG_MASK; - if (hotplug_trigger) { - intel_hpd_irq_handler(dev, hotplug_trigger, hpd_ibx); - } + intel_hpd_irq_handler(dev, hotplug_trigger, hpd_ibx); + if (pch_iir & SDE_AUDIO_POWER_MASK) { int port = ffs((pch_iir & SDE_AUDIO_POWER_MASK) >> SDE_AUDIO_POWER_SHIFT); @@ -1149,9 +1151,8 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir) int pipe; u32 hotplug_trigger = pch_iir & SDE_HOTPLUG_MASK_CPT; - if (hotplug_trigger) { - intel_hpd_irq_handler(dev, hotplug_trigger, hpd_cpt); - } + intel_hpd_irq_handler(dev, hotplug_trigger, hpd_cpt); + if (pch_iir & SDE_AUDIO_POWER_MASK_CPT) { int port = ffs((pch_iir & SDE_AUDIO_POWER_MASK_CPT) >> SDE_AUDIO_POWER_SHIFT_CPT); @@ -3225,9 +3226,9 @@ static irqreturn_t i915_irq_handler(int irq, void *arg) DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x\n", hotplug_status); - if (hotplug_trigger) { - intel_hpd_irq_handler(dev, hotplug_trigger, hpd_status_i915); - } + + intel_hpd_irq_handler(dev, hotplug_trigger, hpd_status_i915); + I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status); POSTING_READ(PORT_HOTPLUG_STAT); } @@ -3463,10 +3464,10 @@ static irqreturn_t i965_irq_handler(int irq, void *arg) DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x\n", hotplug_status); - if (hotplug_trigger) { - intel_hpd_irq_handler(dev, hotplug_trigger, - IS_G4X(dev) ? hpd_status_gen4 : hpd_status_i915); - } + + intel_hpd_irq_handler(dev, hotplug_trigger, + IS_G4X(dev) ? hpd_status_gen4 : hpd_status_i915); + I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status); I915_READ(PORT_HOTPLUG_STAT); } -- cgit v1.2.3 From b5ea2d5681522f1b8ef886b5ac039903bf1d39fe Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 27 Jun 2013 17:52:15 +0200 Subject: drm/i915: fix hpd interrupt register locking Our interrupt handler (in hardirq context) could race with the timer (in softirq context), hence we need to hold the spinlock around the call to ->hdp_irq_setup in intel_hpd_irq_handler, too. But as an optimization (and more so to clarify things) we don't need to do the irqsave/restore dance in the hardirq context. Note also that on ilk+ the race isn't just against the hotplug reenable timer, but also against the fifo underrun reporting. That one also modifies the SDEIMR register (again protected by the same dev_priv->irq_lock). To lock things down again sprinkle a assert_spin_locked. But exclude the functions touching SDEIMR for now, I want to extract them all into a new helper function (like we do already for pipestate, display interrupts and all the various gt interrupts). v2: Add the missing 't' Egbert spotted in a comment. v3: Actually fix the right misspelled comment (Paulo). Cc: Egbert Eich Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index b29b76fe1123..3d92a7cef154 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -877,15 +877,13 @@ static inline void intel_hpd_irq_handler(struct drm_device *dev, const u32 *hpd) { drm_i915_private_t *dev_priv = dev->dev_private; - unsigned long irqflags; int i; bool storm_detected = false; if (!hotplug_trigger) return; - spin_lock_irqsave(&dev_priv->irq_lock, irqflags); - + spin_lock(&dev_priv->irq_lock); for (i = 1; i < HPD_NUM_PINS; i++) { if (!(hpd[i] & hotplug_trigger) || @@ -908,10 +906,9 @@ static inline void intel_hpd_irq_handler(struct drm_device *dev, } } - spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); - if (storm_detected) dev_priv->display.hpd_irq_setup(dev); + spin_unlock(&dev_priv->irq_lock); queue_work(dev_priv->wq, &dev_priv->hotplug_work); @@ -3380,6 +3377,8 @@ static void i915_hpd_irq_setup(struct drm_device *dev) struct intel_encoder *intel_encoder; u32 hotplug_en; + assert_spin_locked(&dev_priv->irq_lock); + if (I915_HAS_HOTPLUG(dev)) { hotplug_en = I915_READ(PORT_HOTPLUG_EN); hotplug_en &= ~HOTPLUG_INT_EN_MASK; @@ -3663,6 +3662,7 @@ void intel_hpd_init(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; struct drm_mode_config *mode_config = &dev->mode_config; struct drm_connector *connector; + unsigned long irqflags; int i; for (i = 1; i < HPD_NUM_PINS; i++) { @@ -3675,6 +3675,11 @@ void intel_hpd_init(struct drm_device *dev) if (!connector->polled && I915_HAS_HOTPLUG(dev) && intel_connector->encoder->hpd_pin > HPD_NONE) connector->polled = DRM_CONNECTOR_POLL_HPD; } + + /* Interrupt setup is already guaranteed to be single-threaded, this is + * just to make the assert_spin_locked checks happy. */ + spin_lock_irqsave(&dev_priv->irq_lock, irqflags); if (dev_priv->display.hpd_irq_setup) dev_priv->display.hpd_irq_setup(dev); + spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); } -- cgit v1.2.3 From 63000ef656190b65a8ae4d00acd7f22b6d92415d Mon Sep 17 00:00:00 2001 From: Xiong Zhang Date: Fri, 28 Jun 2013 12:59:06 +0800 Subject: drm/i915: correct intel_dp_get_config() function for DevCPT On DevCPT, the control register for Transcoder DP Sync Polarity is TRANS_DP_CTL, not DP_CTL. Without this patch, Many call trace occur on CPT machine with DP monitor. The call trace is like: *ERROR* mismatch in adjusted_mode.flags(expected X,found X) v2: use intel-crtc to simple patch, suggested by Daniel. Signed-off-by: Xiong Zhang [danvet: Extend the encoder->get_config comment to specify that we now also depend upon intel_encoder->base.crtc being correct. Also bikeshed s/intel_crtc/crtc/.] Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=65287 Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 35 +++++++++++++++++++++++++---------- drivers/gpu/drm/i915/intel_drv.h | 3 ++- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 24a44ede867d..b73971234013 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1324,20 +1324,35 @@ static void intel_dp_get_config(struct intel_encoder *encoder, struct intel_crtc_config *pipe_config) { struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); - struct drm_i915_private *dev_priv = encoder->base.dev->dev_private; u32 tmp, flags = 0; + struct drm_device *dev = encoder->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + enum port port = dp_to_dig_port(intel_dp)->port; + struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); - tmp = I915_READ(intel_dp->output_reg); + if ((port == PORT_A) || !HAS_PCH_CPT(dev)) { + tmp = I915_READ(intel_dp->output_reg); + if (tmp & DP_SYNC_HS_HIGH) + flags |= DRM_MODE_FLAG_PHSYNC; + else + flags |= DRM_MODE_FLAG_NHSYNC; - if (tmp & DP_SYNC_HS_HIGH) - flags |= DRM_MODE_FLAG_PHSYNC; - else - flags |= DRM_MODE_FLAG_NHSYNC; + if (tmp & DP_SYNC_VS_HIGH) + flags |= DRM_MODE_FLAG_PVSYNC; + else + flags |= DRM_MODE_FLAG_NVSYNC; + } else { + tmp = I915_READ(TRANS_DP_CTL(crtc->pipe)); + if (tmp & TRANS_DP_HSYNC_ACTIVE_HIGH) + flags |= DRM_MODE_FLAG_PHSYNC; + else + flags |= DRM_MODE_FLAG_NHSYNC; - if (tmp & DP_SYNC_VS_HIGH) - flags |= DRM_MODE_FLAG_PVSYNC; - else - flags |= DRM_MODE_FLAG_NVSYNC; + if (tmp & TRANS_DP_VSYNC_ACTIVE_HIGH) + flags |= DRM_MODE_FLAG_PVSYNC; + else + flags |= DRM_MODE_FLAG_NVSYNC; + } pipe_config->adjusted_mode.flags |= flags; } diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 9ddbe3b49fef..c8c9b6f48230 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -141,7 +141,8 @@ struct intel_encoder { bool (*get_hw_state)(struct intel_encoder *, enum pipe *pipe); /* Reconstructs the equivalent mode flags for the current hardware * state. This must be called _after_ display->get_pipe_config has - * pre-filled the pipe config. */ + * pre-filled the pipe config. Note that intel_encoder->base.crtc must + * be set correctly before calling this function. */ void (*get_config)(struct intel_encoder *, struct intel_crtc_config *pipe_config); int crtc_mask; -- cgit v1.2.3 From daa13e1ca587bc773c1aae415ed1af6554117bd4 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 28 Jun 2013 16:54:08 +0100 Subject: drm/i915: Only clear write-domains after a successful wait-seqno In the introduction of the non-blocking wait, I cut'n'pasted the wait completion code from normal locked path. Unfortunately, this neglected that the normal path returned early if the wait returned early. The result is that read-only waits may return whilst the GPU is still writing to the bo. Fixes regression from commit 3236f57a0162391f84b93f39fc1882c49a8998c7 [v3.7] Author: Chris Wilson Date: Fri Aug 24 09:35:09 2012 +0100 drm/i915: Use a non-blocking wait for set-to-domain ioctl Signed-off-by: Chris Wilson Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=66163 Cc: stable@vger.kernel.org Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index fa074cec8587..18025fd45d40 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1160,7 +1160,8 @@ i915_gem_object_wait_rendering__nonblocking(struct drm_i915_gem_object *obj, /* Manually manage the write flush as we may have not yet * retired the buffer. */ - if (obj->last_write_seqno && + if (ret == 0 && + obj->last_write_seqno && i915_seqno_passed(seqno, obj->last_write_seqno)) { obj->last_write_seqno = 0; obj->base.write_domain &= ~I915_GEM_GPU_DOMAINS; -- cgit v1.2.3 From d26e3af842023603747f1566caff5f471508bbd4 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sat, 29 Jun 2013 22:05:26 +0100 Subject: drm/i915: Refactor the wait_rendering completion into a common routine Harmonise the completion logic between the non-blocking and normal wait_rendering paths, and move that logic into a common function. In the process, we note that the last_write_seqno is by definition the earlier of the two read/write seqnos and so all successful waits will have passed the last_write_seqno. Therefore we can unconditionally clear the write seqno and its domains in the completion logic. v2: Add the missing ring parameter, because sometimes it is good to have things compile. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 48 ++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 18025fd45d40..769f75262feb 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1087,6 +1087,25 @@ i915_wait_seqno(struct intel_ring_buffer *ring, uint32_t seqno) interruptible, NULL); } +static int +i915_gem_object_wait_rendering__tail(struct drm_i915_gem_object *obj, + struct intel_ring_buffer *ring) +{ + i915_gem_retire_requests_ring(ring); + + /* Manually manage the write flush as we may have not yet + * retired the buffer. + * + * Note that the last_write_seqno is always the earlier of + * the two (read/write) seqno, so if we haved successfully waited, + * we know we have passed the last write. + */ + obj->last_write_seqno = 0; + obj->base.write_domain &= ~I915_GEM_GPU_DOMAINS; + + return 0; +} + /** * Ensures that all rendering to the object has completed and the object is * safe to unbind from the GTT or access from the CPU. @@ -1107,18 +1126,7 @@ i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj, if (ret) return ret; - i915_gem_retire_requests_ring(ring); - - /* Manually manage the write flush as we may have not yet - * retired the buffer. - */ - if (obj->last_write_seqno && - i915_seqno_passed(seqno, obj->last_write_seqno)) { - obj->last_write_seqno = 0; - obj->base.write_domain &= ~I915_GEM_GPU_DOMAINS; - } - - return 0; + return i915_gem_object_wait_rendering__tail(obj, ring); } /* A nonblocking variant of the above wait. This is a highly dangerous routine @@ -1154,20 +1162,10 @@ i915_gem_object_wait_rendering__nonblocking(struct drm_i915_gem_object *obj, mutex_unlock(&dev->struct_mutex); ret = __wait_seqno(ring, seqno, reset_counter, true, NULL); mutex_lock(&dev->struct_mutex); + if (ret) + return ret; - i915_gem_retire_requests_ring(ring); - - /* Manually manage the write flush as we may have not yet - * retired the buffer. - */ - if (ret == 0 && - obj->last_write_seqno && - i915_seqno_passed(seqno, obj->last_write_seqno)) { - obj->last_write_seqno = 0; - obj->base.write_domain &= ~I915_GEM_GPU_DOMAINS; - } - - return ret; + return i915_gem_object_wait_rendering__tail(obj, ring); } /** -- cgit v1.2.3 From baf27f9b17bf2f369f3865e38c41d2163e8d815d Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sat, 29 Jun 2013 23:26:50 +0100 Subject: drm/i915: Break up the large vsnprintf() in print_error_buffers() So it appears that I have encountered some bogosity when trying to call i915_error_printf() with many arguments from print_error_buffers(). The symptom is that the vsnprintf parser tries to interpret an integer arg as a character string, the resulting OOPS indicating stack corruption. Replacing the single call with its 13 format specifiers and arguments with multiple calls to i915_error_printf() worked fine. This patch goes one step further and introduced i915_error_puts() to pass the strings simply. It may not fix the root cause, but it does prevent my box from dying and I think helps make print_error_buffers() more friendly. Signed-off-by: Chris Wilson Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=66077 Cc: Mika Kuoppala Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 109 ++++++++++++++++++++++++++---------- 1 file changed, 79 insertions(+), 30 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 04cf6c09710a..47d6c748057e 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -647,41 +647,44 @@ static const char *purgeable_flag(int purgeable) return purgeable ? " purgeable" : ""; } -static void i915_error_vprintf(struct drm_i915_error_state_buf *e, - const char *f, va_list args) +static bool __i915_error_ok(struct drm_i915_error_state_buf *e) { - unsigned len; if (!e->err && WARN(e->bytes > (e->size - 1), "overflow")) { e->err = -ENOSPC; - return; + return false; } if (e->bytes == e->size - 1 || e->err) - return; + return false; - /* Seek the first printf which is hits start position */ - if (e->pos < e->start) { - len = vsnprintf(NULL, 0, f, args); - if (e->pos + len <= e->start) { - e->pos += len; - return; - } + return true; +} - /* First vsnprintf needs to fit in full for memmove*/ - if (len >= e->size) { - e->err = -EIO; - return; - } +static bool __i915_error_seek(struct drm_i915_error_state_buf *e, + unsigned len) +{ + if (e->pos + len <= e->start) { + e->pos += len; + return false; } - len = vsnprintf(e->buf + e->bytes, e->size - e->bytes, f, args); - if (len >= e->size - e->bytes) - len = e->size - e->bytes - 1; + /* First vsnprintf needs to fit in its entirety for memmove */ + if (len >= e->size) { + e->err = -EIO; + return false; + } + return true; +} + +static void __i915_error_advance(struct drm_i915_error_state_buf *e, + unsigned len) +{ /* If this is first printf in this window, adjust it so that * start position matches start of the buffer */ + if (e->pos < e->start) { const size_t off = e->start - e->pos; @@ -701,6 +704,51 @@ static void i915_error_vprintf(struct drm_i915_error_state_buf *e, e->pos += len; } +static void i915_error_vprintf(struct drm_i915_error_state_buf *e, + const char *f, va_list args) +{ + unsigned len; + + if (!__i915_error_ok(e)) + return; + + /* Seek the first printf which is hits start position */ + if (e->pos < e->start) { + len = vsnprintf(NULL, 0, f, args); + if (!__i915_error_seek(e, len)) + return; + } + + len = vsnprintf(e->buf + e->bytes, e->size - e->bytes, f, args); + if (len >= e->size - e->bytes) + len = e->size - e->bytes - 1; + + __i915_error_advance(e, len); +} + +static void i915_error_puts(struct drm_i915_error_state_buf *e, + const char *str) +{ + unsigned len; + + if (!__i915_error_ok(e)) + return; + + len = strlen(str); + + /* Seek the first printf which is hits start position */ + if (e->pos < e->start) { + if (!__i915_error_seek(e, len)) + return; + } + + if (len >= e->size - e->bytes) + len = e->size - e->bytes - 1; + memcpy(e->buf + e->bytes, str, len); + + __i915_error_advance(e, len); +} + void i915_error_printf(struct drm_i915_error_state_buf *e, const char *f, ...) { va_list args; @@ -711,6 +759,7 @@ void i915_error_printf(struct drm_i915_error_state_buf *e, const char *f, ...) } #define err_printf(e, ...) i915_error_printf(e, __VA_ARGS__) +#define err_puts(e, s) i915_error_puts(e, s) static void print_error_buffers(struct drm_i915_error_state_buf *m, const char *name, @@ -720,26 +769,26 @@ static void print_error_buffers(struct drm_i915_error_state_buf *m, err_printf(m, "%s [%d]:\n", name, count); while (count--) { - err_printf(m, " %08x %8u %02x %02x %x %x%s%s%s%s%s%s%s", + err_printf(m, " %08x %8u %02x %02x %x %x", err->gtt_offset, err->size, err->read_domains, err->write_domain, - err->rseqno, err->wseqno, - pin_flag(err->pinned), - tiling_flag(err->tiling), - dirty_flag(err->dirty), - purgeable_flag(err->purgeable), - err->ring != -1 ? " " : "", - ring_str(err->ring), - cache_level_str(err->cache_level)); + err->rseqno, err->wseqno); + err_puts(m, pin_flag(err->pinned)); + err_puts(m, tiling_flag(err->tiling)); + err_puts(m, dirty_flag(err->dirty)); + err_puts(m, purgeable_flag(err->purgeable)); + err_puts(m, err->ring != -1 ? " " : ""); + err_puts(m, ring_str(err->ring)); + err_puts(m, cache_level_str(err->cache_level)); if (err->name) err_printf(m, " (name: %d)", err->name); if (err->fence_reg != I915_FENCE_REG_NONE) err_printf(m, " (fence: %d)", err->fence_reg); - err_printf(m, "\n"); + err_puts(m, "\n"); err++; } } -- cgit v1.2.3 From 05843c07ee501cc89a484b2d02528282838a2318 Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Mon, 1 Jul 2013 11:27:16 +0200 Subject: ALSA: hda - Add Dell SSID to support Headset Mic recording This is X5 Precision - Diesel platform. Signed-off-by: Kailang Yang Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 7d6a9f5d2b06..14ac9b0e740c 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3585,6 +3585,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1028, 0x05c9, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x05ca, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x05cb, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x05cc, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x05cd, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x05de, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x05e0, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x05e9, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), @@ -3604,6 +3606,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1028, 0x0608, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0609, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0613, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x15cc, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x15cd, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2), SND_PCI_QUIRK(0x103c, 0x18e6, "HP", ALC269_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x1973, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1), -- cgit v1.2.3 From 7c9e6150f2e7cbd60e0bc9a19118ca1dc97d2780 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Mon, 1 Jul 2013 16:16:10 +0800 Subject: ASoC: mxs: register saif mclk to clock framework Mostly the mxs system design uses saif0 mclk output as the clock source of codec. Since the mclk is implemented as a general divider with the saif clk as the parent clock, let's register the mclk as a basic clk-divider to common clock framework. Then with it being a clock provdier, clk_get() call in codec driver probe function will just work. Signed-off-by: Shawn Guo Signed-off-by: Mark Brown --- sound/soc/mxs/mxs-saif.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c index d31dc52fa862..9ad6dccf791d 100644 --- a/sound/soc/mxs/mxs-saif.c +++ b/sound/soc/mxs/mxs-saif.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -662,6 +663,33 @@ static irqreturn_t mxs_saif_irq(int irq, void *dev_id) return IRQ_HANDLED; } +static int mxs_saif_mclk_init(struct platform_device *pdev) +{ + struct mxs_saif *saif = platform_get_drvdata(pdev); + struct device_node *np = pdev->dev.of_node; + struct clk *clk; + int ret; + + clk = clk_register_divider(&pdev->dev, "mxs_saif_mclk", + __clk_get_name(saif->clk), 0, + saif->base + SAIF_CTRL, + BP_SAIF_CTRL_BITCLK_MULT_RATE, 3, + 0, NULL); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + if (ret == -EEXIST) + return 0; + dev_err(&pdev->dev, "failed to register mclk: %d\n", ret); + return PTR_ERR(clk); + } + + ret = of_clk_add_provider(np, of_clk_src_simple_get, clk); + if (ret) + return ret; + + return 0; +} + static int mxs_saif_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -769,6 +797,13 @@ static int mxs_saif_probe(struct platform_device *pdev) platform_set_drvdata(pdev, saif); + /* We only support saif0 being tx and clock master */ + if (saif->id == 0) { + ret = mxs_saif_mclk_init(pdev); + if (ret) + dev_warn(&pdev->dev, "failed to init clocks\n"); + } + ret = snd_soc_register_component(&pdev->dev, &mxs_saif_component, &mxs_saif_dai, 1); if (ret) { -- cgit v1.2.3 From f3ecdd7bf6a782a6eafe9c2c5d39fcb487e2a154 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sat, 29 Jun 2013 11:37:29 +0800 Subject: regulator: max77693: Fix trivial typo Fix trivial typo in the equation to check upper bound of current setting. Signed-off-by: Axel Lin Acked-by: Jonghwa Lee Signed-off-by: Mark Brown --- drivers/regulator/max77693.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/max77693.c b/drivers/regulator/max77693.c index 674ece7c832b..d45a4dd17131 100644 --- a/drivers/regulator/max77693.c +++ b/drivers/regulator/max77693.c @@ -97,7 +97,7 @@ static int max77693_chg_set_current_limit(struct regulator_dev *rdev, while (chg_min_uA + CHGIN_ILIM_STEP_20mA * sel < min_uA) sel++; - if (chg_min_uA * CHGIN_ILIM_STEP_20mA * sel > max_uA) + if (chg_min_uA + CHGIN_ILIM_STEP_20mA * sel > max_uA) return -EINVAL; /* the first four codes for charger current are all 60mA */ -- cgit v1.2.3 From 8c7e7ddf31ca9943b70e58d1c47d059beda435bd Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 30 Jun 2013 00:33:52 +0800 Subject: regulator: max77693: Remove NULL test for rmatch[i].init_data The implementation in of_regulator_match() already ensures match->init_data is not NULL for all matched cases if the return value of of_regulator_match() > 0. Thus remove NULL test for rmatch[i].init_data. This patch also fixes the condition for loop iteration. The for loop should iterate "matched" times rather than ARRAY_SIZE(regulators) because we only allocate "matched" number of entries for rdata. Though in most cases, "matched" == ARRAY_SIZE(regulators). Signed-off-by: Axel Lin Acked-by: Jonghwa Lee Signed-off-by: Mark Brown --- drivers/regulator/max77693.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/regulator/max77693.c b/drivers/regulator/max77693.c index d45a4dd17131..ce4b96c15eba 100644 --- a/drivers/regulator/max77693.c +++ b/drivers/regulator/max77693.c @@ -190,9 +190,7 @@ static int max77693_pmic_dt_parse_rdata(struct device *dev, tmp = *rdata; - for (i = 0; i < ARRAY_SIZE(regulators); i++) { - if (!rmatch[i].init_data) - continue; + for (i = 0; i < matched; i++) { tmp->initdata = rmatch[i].init_data; tmp->of_node = rmatch[i].of_node; tmp->id = regulators[i].id; -- cgit v1.2.3 From e4760363ea14b7b707ba68ab780c8ecf98a84c15 Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Mon, 1 Jul 2013 15:30:51 +0300 Subject: remoteproc/omap: fix a sparse warning This patch fixes a sparse warning in the omap remoteproc code when OMAP_REMOTEPROC is disabled. include/linux/platform_data/remoteproc-omap.h:76:13: warning: symbol 'omap_rproc_reserve_cma' was not declared. Should it be static? Signed-off-by: Suman Anna Signed-off-by: Ohad Ben-Cohen --- include/linux/platform_data/remoteproc-omap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/platform_data/remoteproc-omap.h b/include/linux/platform_data/remoteproc-omap.h index 3c1c6444ec4b..bfbd12b41162 100644 --- a/include/linux/platform_data/remoteproc-omap.h +++ b/include/linux/platform_data/remoteproc-omap.h @@ -50,7 +50,7 @@ void __init omap_rproc_reserve_cma(void); #else -void __init omap_rproc_reserve_cma(void) +static inline void __init omap_rproc_reserve_cma(void) { } -- cgit v1.2.3 From 5bc7247ac47cf100309e4d7e24214889c46a1636 Mon Sep 17 00:00:00 2001 From: Miao Xie Date: Thu, 6 Jun 2013 03:28:03 +0000 Subject: Btrfs: fix broken nocow after balance Balance will create reloc_root for each fs root, and it's going to record last_snapshot to filter shared blocks. The side effect of setting last_snapshot is to break nocow attributes of files. Since the extents are not shared by the relocation tree after the balance, we can recover the old last_snapshot safely if no one snapshoted the source tree. We fix the above problem by this way. Reported-by: Kyle Gates Signed-off-by: Liu Bo Signed-off-by: Miao Xie Signed-off-by: Josef Bacik --- fs/btrfs/relocation.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index aa559f1161df..4a404b44b0ad 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -1305,6 +1305,7 @@ static struct btrfs_root *create_reloc_root(struct btrfs_trans_handle *trans, struct extent_buffer *eb; struct btrfs_root_item *root_item; struct btrfs_key root_key; + u64 last_snap = 0; int ret; root_item = kmalloc(sizeof(*root_item), GFP_NOFS); @@ -1320,6 +1321,7 @@ static struct btrfs_root *create_reloc_root(struct btrfs_trans_handle *trans, BTRFS_TREE_RELOC_OBJECTID); BUG_ON(ret); + last_snap = btrfs_root_last_snapshot(&root->root_item); btrfs_set_root_last_snapshot(&root->root_item, trans->transid - 1); } else { @@ -1345,6 +1347,12 @@ static struct btrfs_root *create_reloc_root(struct btrfs_trans_handle *trans, memset(&root_item->drop_progress, 0, sizeof(struct btrfs_disk_key)); root_item->drop_level = 0; + /* + * abuse rtransid, it is safe because it is impossible to + * receive data into a relocation tree. + */ + btrfs_set_root_rtransid(root_item, last_snap); + btrfs_set_root_otransid(root_item, trans->transid); } btrfs_tree_unlock(eb); @@ -2272,8 +2280,12 @@ void free_reloc_roots(struct list_head *list) static noinline_for_stack int merge_reloc_roots(struct reloc_control *rc) { + struct btrfs_trans_handle *trans; struct btrfs_root *root; struct btrfs_root *reloc_root; + u64 last_snap; + u64 otransid; + u64 objectid; LIST_HEAD(reloc_roots); int found = 0; int ret = 0; @@ -2307,12 +2319,44 @@ again: } else { list_del_init(&reloc_root->root_list); } + + /* + * we keep the old last snapshod transid in rtranid when we + * created the relocation tree. + */ + last_snap = btrfs_root_rtransid(&reloc_root->root_item); + otransid = btrfs_root_otransid(&reloc_root->root_item); + objectid = reloc_root->root_key.offset; + ret = btrfs_drop_snapshot(reloc_root, rc->block_rsv, 0, 1); if (ret < 0) { if (list_empty(&reloc_root->root_list)) list_add_tail(&reloc_root->root_list, &reloc_roots); goto out; + } else if (!ret) { + /* + * recover the last snapshot tranid to avoid + * the space balance break NOCOW. + */ + root = read_fs_root(rc->extent_root->fs_info, + objectid); + if (IS_ERR(root)) + continue; + + if (btrfs_root_refs(&root->root_item) == 0) + continue; + + trans = btrfs_join_transaction(root); + BUG_ON(IS_ERR(trans)); + + /* Check if the fs/file tree was snapshoted or not. */ + if (btrfs_root_last_snapshot(&root->root_item) == + otransid - 1) + btrfs_set_root_last_snapshot(&root->root_item, + last_snap); + + btrfs_end_transaction(trans, root); } } -- cgit v1.2.3 From 3fb4037599a9f14b6c58e2a813aeb03bfa03dd8f Mon Sep 17 00:00:00 2001 From: Jie Liu Date: Thu, 6 Jun 2013 13:38:50 +0000 Subject: btrfs: fix the code comments for LZO compression workspace Fix the code comments for lzo compression workspace. The buf item is used to store the decompressed data and cbuf is used to store the compressed data. Signed-off-by: Jie Liu Signed-off-by: Josef Bacik --- fs/btrfs/lzo.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/lzo.c b/fs/btrfs/lzo.c index 743b86fa4fcb..f93151a98886 100644 --- a/fs/btrfs/lzo.c +++ b/fs/btrfs/lzo.c @@ -31,8 +31,8 @@ struct workspace { void *mem; - void *buf; /* where compressed data goes */ - void *cbuf; /* where decompressed data goes */ + void *buf; /* where decompressed data goes */ + void *cbuf; /* where compressed data goes */ struct list_head list; }; -- cgit v1.2.3 From f971fe29b14eedd4abc389593b77fbdf94ac2d59 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Mon, 10 Jun 2013 11:52:32 -0400 Subject: Btrfs: wake up delayed ref flushing waiters on abort I hit a deadlock because we aborted when flushing delayed refs but didn't wake any of the other flushers up and so everybody was just sleeping forever. This should fix the problem. Thanks, Signed-off-by: Josef Bacik --- fs/btrfs/extent-tree.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index f84d53bc2f5d..ca1893edda52 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -2629,6 +2629,7 @@ again: spin_unlock(&delayed_refs->lock); btrfs_abort_transaction(trans, root, ret); atomic_dec(&delayed_refs->procs_running_refs); + wake_up(&delayed_refs->wait); return ret; } -- cgit v1.2.3 From 501407aab8c947911b10cf5a0e0043019d5a4f17 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Mon, 10 Jun 2013 16:47:23 -0400 Subject: Btrfs: stop waiting on current trans if we aborted I hit a hang when run_delayed_refs returned an error in the beginning of btrfs_commit_transaction. If we decide we need to commit the transaction in btrfs_end_transaction we'll set BLOCKED and start to commit, but if we get an error this early on we'll just exit without committing. This is fine, except that anybody else who tried to start a transaction will sit in wait_current_trans() since we're set to BLOCKED and we never set it to something else and woke people up. To fix this we want to check for trans->aborted everywhere we wait for the transaction state to change, and make btrfs_abort_transaction() wake up any waiters there may be. All the callers will notice that the transaction has aborted and exit out properly. Thanks, Signed-off-by: Josef Bacik --- fs/btrfs/super.c | 3 +++ fs/btrfs/transaction.c | 12 ++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 14d2be03f95a..8eb6191d86da 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -265,6 +265,9 @@ void __btrfs_abort_transaction(struct btrfs_trans_handle *trans, return; } ACCESS_ONCE(trans->transaction->aborted) = errno; + /* Wake up anybody who may be waiting on this transaction */ + wake_up(&root->fs_info->transaction_wait); + wake_up(&root->fs_info->transaction_blocked_wait); __btrfs_std_error(root->fs_info, function, line, errno, NULL); } /* diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index 8c8b80085e75..c11b7efcc561 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -302,7 +302,8 @@ int btrfs_record_root_in_trans(struct btrfs_trans_handle *trans, static inline int is_transaction_blocked(struct btrfs_transaction *trans) { return (trans->state >= TRANS_STATE_BLOCKED && - trans->state < TRANS_STATE_UNBLOCKED); + trans->state < TRANS_STATE_UNBLOCKED && + !trans->aborted); } /* wait for commit against the current transaction to become unblocked @@ -320,7 +321,8 @@ static void wait_current_trans(struct btrfs_root *root) spin_unlock(&root->fs_info->trans_lock); wait_event(root->fs_info->transaction_wait, - cur_trans->state >= TRANS_STATE_UNBLOCKED); + cur_trans->state >= TRANS_STATE_UNBLOCKED || + cur_trans->aborted); put_transaction(cur_trans); } else { spin_unlock(&root->fs_info->trans_lock); @@ -1392,7 +1394,8 @@ static void wait_current_trans_commit_start(struct btrfs_root *root, struct btrfs_transaction *trans) { wait_event(root->fs_info->transaction_blocked_wait, - trans->state >= TRANS_STATE_COMMIT_START); + trans->state >= TRANS_STATE_COMMIT_START || + trans->aborted); } /* @@ -1403,7 +1406,8 @@ static void wait_current_trans_commit_start_and_unblock(struct btrfs_root *root, struct btrfs_transaction *trans) { wait_event(root->fs_info->transaction_wait, - trans->state >= TRANS_STATE_UNBLOCKED); + trans->state >= TRANS_STATE_UNBLOCKED || + trans->aborted); } /* -- cgit v1.2.3 From 1be41b78bc688fc634bf30965d2be692c99fd11d Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Wed, 12 Jun 2013 13:56:06 -0400 Subject: Btrfs: fix transaction throttling for delayed refs Dave has this fs_mark script that can make btrfs abort with sufficient amount of ram. This is because with more ram we can keep more dirty metadata in cache which in a round about way makes for many more pending delayed refs. What happens is we end up not throttling the transaction enough so when we go to commit the transaction when we've completely filled the file system we'll abort() because we use all of the space in the global reserve and we still have delayed refs to run. To fix this we need to make the delayed ref flushing and the transaction throttling dependant upon the number of delayed refs that we have instead of how much reserved space is left in the global reserve. With this patch we not only stop aborting transactions but we also get a smoother run speed with fs_mark and it makes us about 10% faster. Thanks, Reported-by: David Sterba Signed-off-by: Josef Bacik --- fs/btrfs/ctree.h | 2 ++ fs/btrfs/extent-tree.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++---- fs/btrfs/transaction.c | 24 +++++++++----------- 3 files changed, 69 insertions(+), 18 deletions(-) diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 0049fe0f3f74..76e4983b39ea 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -3056,6 +3056,8 @@ static inline u64 btrfs_calc_trunc_metadata_size(struct btrfs_root *root, num_items; } +int btrfs_should_throttle_delayed_refs(struct btrfs_trans_handle *trans, + struct btrfs_root *root); void btrfs_put_block_group(struct btrfs_block_group_cache *cache); int btrfs_run_delayed_refs(struct btrfs_trans_handle *trans, struct btrfs_root *root, unsigned long count); diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index ca1893edda52..6d5c5f73ad64 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -2526,6 +2526,51 @@ static int refs_newer(struct btrfs_delayed_ref_root *delayed_refs, int seq, return 0; } +static inline u64 heads_to_leaves(struct btrfs_root *root, u64 heads) +{ + u64 num_bytes; + + num_bytes = heads * (sizeof(struct btrfs_extent_item) + + sizeof(struct btrfs_extent_inline_ref)); + if (!btrfs_fs_incompat(root->fs_info, SKINNY_METADATA)) + num_bytes += heads * sizeof(struct btrfs_tree_block_info); + + /* + * We don't ever fill up leaves all the way so multiply by 2 just to be + * closer to what we're really going to want to ouse. + */ + return div64_u64(num_bytes, BTRFS_LEAF_DATA_SIZE(root)); +} + +int btrfs_should_throttle_delayed_refs(struct btrfs_trans_handle *trans, + struct btrfs_root *root) +{ + struct btrfs_block_rsv *global_rsv; + u64 num_heads = trans->transaction->delayed_refs.num_heads_ready; + u64 num_bytes; + int ret = 0; + + num_bytes = btrfs_calc_trans_metadata_size(root, 1); + num_heads = heads_to_leaves(root, num_heads); + if (num_heads > 1) + num_bytes += (num_heads - 1) * root->leafsize; + num_bytes <<= 1; + global_rsv = &root->fs_info->global_block_rsv; + + /* + * If we can't allocate any more chunks lets make sure we have _lots_ of + * wiggle room since running delayed refs can create more delayed refs. + */ + if (global_rsv->space_info->full) + num_bytes <<= 1; + + spin_lock(&global_rsv->lock); + if (global_rsv->reserved <= num_bytes) + ret = 1; + spin_unlock(&global_rsv->lock); + return ret; +} + /* * this starts processing the delayed reference count updates and * extent insertions we have queued up so far. count can be @@ -2573,7 +2618,8 @@ progress: old = atomic_cmpxchg(&delayed_refs->procs_running_refs, 0, 1); if (old) { DEFINE_WAIT(__wait); - if (delayed_refs->num_entries < 16348) + if (delayed_refs->flushing || + !btrfs_should_throttle_delayed_refs(trans, root)) return 0; prepare_to_wait(&delayed_refs->wait, &__wait, @@ -2608,7 +2654,7 @@ again: while (1) { if (!(run_all || run_most) && - delayed_refs->num_heads_ready < 64) + !btrfs_should_throttle_delayed_refs(trans, root)) break; /* @@ -8665,8 +8711,15 @@ int btrfs_trim_fs(struct btrfs_root *root, struct fstrim_range *range) if (end - start >= range->minlen) { if (!block_group_cache_done(cache)) { ret = cache_block_group(cache, 0); - if (!ret) - wait_block_group_cache_done(cache); + if (ret) { + btrfs_put_block_group(cache); + break; + } + ret = wait_block_group_cache_done(cache); + if (ret) { + btrfs_put_block_group(cache); + break; + } } ret = btrfs_trim_block_group(cache, &group_trimmed, diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index c11b7efcc561..c916ebdc689a 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -615,10 +615,11 @@ void btrfs_throttle(struct btrfs_root *root) static int should_end_transaction(struct btrfs_trans_handle *trans, struct btrfs_root *root) { - int ret; + if (root->fs_info->global_block_rsv.space_info->full && + btrfs_should_throttle_delayed_refs(trans, root)) + return 1; - ret = btrfs_block_rsv_check(root, &root->fs_info->global_block_rsv, 5); - return ret ? 1 : 0; + return !!btrfs_block_rsv_check(root, &root->fs_info->global_block_rsv, 5); } int btrfs_should_end_transaction(struct btrfs_trans_handle *trans, @@ -649,7 +650,7 @@ static int __btrfs_end_transaction(struct btrfs_trans_handle *trans, { struct btrfs_transaction *cur_trans = trans->transaction; struct btrfs_fs_info *info = root->fs_info; - int count = 0; + unsigned long cur = trans->delayed_ref_updates; int lock = (trans->type != TRANS_JOIN_NOLOCK); int err = 0; @@ -678,17 +679,11 @@ static int __btrfs_end_transaction(struct btrfs_trans_handle *trans, if (!list_empty(&trans->new_bgs)) btrfs_create_pending_block_groups(trans, root); - while (count < 1) { - unsigned long cur = trans->delayed_ref_updates; + trans->delayed_ref_updates = 0; + if (btrfs_should_throttle_delayed_refs(trans, root)) { + cur = max_t(unsigned long, cur, 1); trans->delayed_ref_updates = 0; - if (cur && - trans->transaction->delayed_refs.num_heads_ready > 64) { - trans->delayed_ref_updates = 0; - btrfs_run_delayed_refs(trans, root, cur); - } else { - break; - } - count++; + btrfs_run_delayed_refs(trans, root, cur); } btrfs_trans_release_metadata(trans, root); @@ -1626,6 +1621,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, * start sending their work down. */ cur_trans->delayed_refs.flushing = 1; + smp_wmb(); if (!list_empty(&trans->new_bgs)) btrfs_create_pending_block_groups(trans, root); -- cgit v1.2.3 From d88d46c6e06cb47cd3b951287ccaf414e96560d0 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Mon, 10 Jun 2013 12:59:04 +0000 Subject: Btrfs: free csums when we're done scrubbing an extent A user reported scrub taking up an unreasonable amount of ram as it ran. This is because we lookup the csums for the extent we're scrubbing but don't free it up until after we're done with the scrub, which means we can take up a whole lot of ram. This patch fixes this by dropping the csums once we're done with the extent we've scrubbed. The user reported this to fix their problem. Thanks, Reported-and-tested-by: Remco Hosman Signed-off-by: Josef Bacik --- fs/btrfs/scrub.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c index 79bd479317cb..cb308a3a9300 100644 --- a/fs/btrfs/scrub.c +++ b/fs/btrfs/scrub.c @@ -2505,6 +2505,7 @@ again: if (ret) goto out; + scrub_free_csums(sctx); if (extent_logical + extent_len < key.objectid + bytes) { logical += increment; -- cgit v1.2.3 From da61d31a78dc2116fa725c92d4eca36dfbc3da8b Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Wed, 12 Jun 2013 16:20:08 -0400 Subject: Btrfs: cleanup backref search commit root flag stuff Looking into this backref problem I noticed we're using a macro to what turns out to essentially be a NULL check to see if we need to search the commit root. I'm killing this, let's just do what everybody else does and checks if trans == NULL. I've also made it so we pass in the path to __resolve_indirect_refs which will have the search_commit_root flag set properly already and that way we can avoid allocating another path when we have a perfectly good one to use. Thanks, Signed-off-by: Josef Bacik --- fs/btrfs/backref.c | 41 ++++++++++++++++------------------------- fs/btrfs/backref.h | 2 -- 2 files changed, 16 insertions(+), 27 deletions(-) diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c index 290e347b6db3..431ea9211bc1 100644 --- a/fs/btrfs/backref.c +++ b/fs/btrfs/backref.c @@ -255,13 +255,11 @@ static int add_all_parents(struct btrfs_root *root, struct btrfs_path *path, * to a logical address */ static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info, - int search_commit_root, - u64 time_seq, - struct __prelim_ref *ref, - struct ulist *parents, - const u64 *extent_item_pos) + struct btrfs_path *path, u64 time_seq, + struct __prelim_ref *ref, + struct ulist *parents, + const u64 *extent_item_pos) { - struct btrfs_path *path; struct btrfs_root *root; struct btrfs_key root_key; struct extent_buffer *eb; @@ -269,11 +267,6 @@ static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info, int root_level; int level = ref->level; - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - path->search_commit_root = !!search_commit_root; - root_key.objectid = ref->root_id; root_key.type = BTRFS_ROOT_ITEM_KEY; root_key.offset = (u64)-1; @@ -314,7 +307,8 @@ static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info, time_seq, ref->wanted_disk_byte, extent_item_pos); out: - btrfs_free_path(path); + path->lowest_level = 0; + btrfs_release_path(path); return ret; } @@ -322,7 +316,7 @@ out: * resolve all indirect backrefs from the list */ static int __resolve_indirect_refs(struct btrfs_fs_info *fs_info, - int search_commit_root, u64 time_seq, + struct btrfs_path *path, u64 time_seq, struct list_head *head, const u64 *extent_item_pos) { @@ -349,9 +343,8 @@ static int __resolve_indirect_refs(struct btrfs_fs_info *fs_info, continue; if (ref->count == 0) continue; - err = __resolve_indirect_ref(fs_info, search_commit_root, - time_seq, ref, parents, - extent_item_pos); + err = __resolve_indirect_ref(fs_info, path, time_seq, ref, + parents, extent_item_pos); if (err == -ENOMEM) goto out; if (err) @@ -795,7 +788,6 @@ static int find_parent_nodes(struct btrfs_trans_handle *trans, struct btrfs_delayed_ref_head *head; int info_level = 0; int ret; - int search_commit_root = (trans == BTRFS_BACKREF_SEARCH_COMMIT_ROOT); struct list_head prefs_delayed; struct list_head prefs; struct __prelim_ref *ref; @@ -810,7 +802,8 @@ static int find_parent_nodes(struct btrfs_trans_handle *trans, path = btrfs_alloc_path(); if (!path) return -ENOMEM; - path->search_commit_root = !!search_commit_root; + if (!trans) + path->search_commit_root = 1; /* * grab both a lock on the path and a lock on the delayed ref head. @@ -825,7 +818,7 @@ again: goto out; BUG_ON(ret == 0); - if (trans != BTRFS_BACKREF_SEARCH_COMMIT_ROOT) { + if (trans) { /* * look if there are updates for this ref queued and lock the * head @@ -890,8 +883,8 @@ again: __merge_refs(&prefs, 1); - ret = __resolve_indirect_refs(fs_info, search_commit_root, time_seq, - &prefs, extent_item_pos); + ret = __resolve_indirect_refs(fs_info, path, time_seq, &prefs, + extent_item_pos); if (ret) goto out; @@ -1459,7 +1452,7 @@ int iterate_extent_inodes(struct btrfs_fs_info *fs_info, iterate_extent_inodes_t *iterate, void *ctx) { int ret; - struct btrfs_trans_handle *trans; + struct btrfs_trans_handle *trans = NULL; struct ulist *refs = NULL; struct ulist *roots = NULL; struct ulist_node *ref_node = NULL; @@ -1471,9 +1464,7 @@ int iterate_extent_inodes(struct btrfs_fs_info *fs_info, pr_debug("resolving all inodes for extent %llu\n", extent_item_objectid); - if (search_commit_root) { - trans = BTRFS_BACKREF_SEARCH_COMMIT_ROOT; - } else { + if (!search_commit_root) { trans = btrfs_join_transaction(fs_info->extent_root); if (IS_ERR(trans)) return PTR_ERR(trans); diff --git a/fs/btrfs/backref.h b/fs/btrfs/backref.h index 0f446d7ca2c0..8f2e76702932 100644 --- a/fs/btrfs/backref.h +++ b/fs/btrfs/backref.h @@ -23,8 +23,6 @@ #include "ulist.h" #include "extent_io.h" -#define BTRFS_BACKREF_SEARCH_COMMIT_ROOT ((struct btrfs_trans_handle *)0) - struct inode_fs_paths { struct btrfs_path *btrfs_path; struct btrfs_root *fs_root; -- cgit v1.2.3 From aee68ee5f5427b91be5b23459993134ca64ecf00 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Thu, 13 Jun 2013 13:50:23 -0400 Subject: Btrfs: fix not being able to find skinny extents during relocate We unconditionally search for the EXTENT_ITEM_KEY for metadata during balance, and then check the key that we found to see if it is actually a METADATA_ITEM_KEY, but this doesn't work right because METADATA is a higher key value, so if what we are looking for happens to be the first item in the leaf the search will dump us out at the previous leaf, and we won't find our item. So instead do what we do everywhere else, search for the skinny extent first and if we don't find it go back and re-search for the extent item. This patch fixes the panic I was hitting when balancing a large file system with skinny extents. Thanks, Signed-off-by: Josef Bacik --- fs/btrfs/relocation.c | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index 4a404b44b0ad..d91f106df665 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -3309,6 +3309,8 @@ static int __add_tree_block(struct reloc_control *rc, struct btrfs_path *path; struct btrfs_key key; int ret; + bool skinny = btrfs_fs_incompat(rc->extent_root->fs_info, + SKINNY_METADATA); if (tree_block_processed(bytenr, blocksize, rc)) return 0; @@ -3319,10 +3321,15 @@ static int __add_tree_block(struct reloc_control *rc, path = btrfs_alloc_path(); if (!path) return -ENOMEM; - +again: key.objectid = bytenr; - key.type = BTRFS_EXTENT_ITEM_KEY; - key.offset = blocksize; + if (skinny) { + key.type = BTRFS_METADATA_ITEM_KEY; + key.offset = (u64)-1; + } else { + key.type = BTRFS_EXTENT_ITEM_KEY; + key.offset = blocksize; + } path->search_commit_root = 1; path->skip_locking = 1; @@ -3330,11 +3337,23 @@ static int __add_tree_block(struct reloc_control *rc, if (ret < 0) goto out; - btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); - if (ret > 0) { - if (key.objectid == bytenr && - key.type == BTRFS_METADATA_ITEM_KEY) - ret = 0; + if (ret > 0 && skinny) { + if (path->slots[0]) { + path->slots[0]--; + btrfs_item_key_to_cpu(path->nodes[0], &key, + path->slots[0]); + if (key.objectid == bytenr && + (key.type == BTRFS_METADATA_ITEM_KEY || + (key.type == BTRFS_EXTENT_ITEM_KEY && + key.offset == blocksize))) + ret = 0; + } + + if (ret) { + skinny = false; + btrfs_release_path(path); + goto again; + } } BUG_ON(ret); -- cgit v1.2.3 From 90b6d2830a72ff008c9bbc8dfbf7aaec90be458f Mon Sep 17 00:00:00 2001 From: Wang Sheng-Hui Date: Fri, 14 Jun 2013 16:21:24 +0800 Subject: Btrfs: fix the comment typo for btrfs_attach_transaction_barrier The comment is for btrfs_attach_transaction_barrier, not for btrfs_attach_transaction. Fix the typo. Signed-off-by: Wang Sheng-Hui Acked-by: Miao Xie Signed-off-by: Josef Bacik --- fs/btrfs/transaction.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index c916ebdc689a..bcfa32c91b5d 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -529,7 +529,7 @@ struct btrfs_trans_handle *btrfs_attach_transaction(struct btrfs_root *root) } /* - * btrfs_attach_transaction() - catch the running transaction + * btrfs_attach_transaction_barrier() - catch the running transaction * * It is similar to the above function, the differentia is this one * will wait for all the inactive transactions until they fully -- cgit v1.2.3 From fdf8e2ea3cba9ef03087482b11258d844d6cbea3 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Fri, 14 Jun 2013 16:58:23 -0400 Subject: Btrfs: unlock extent range on enospc in compressed submit A user reported a deadlock where the async submit thread was blocked on the lock_extent() lock, and then everybody behind him was locked on the page lock for the page he was holding. Looking at the code I noticed we do not unlock the extent range when we get ENOSPC and goto retry. This is bad because we immediately try to lock that range again to do the cow, which will cause a deadlock. Fix this by unlocking the range. Thanks, Signed-off-by: Josef Bacik --- fs/btrfs/inode.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index a2df4690b000..509112da6118 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -700,8 +700,12 @@ retry: async_extent->nr_pages = 0; async_extent->pages = NULL; - if (ret == -ENOSPC) + if (ret == -ENOSPC) { + unlock_extent(io_tree, async_extent->start, + async_extent->start + + async_extent->ram_size - 1); goto retry; + } goto out_free; } -- cgit v1.2.3 From bdf7c00e8f56386dd1df545b37cf59d55ce4216d Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Mon, 17 Jun 2013 13:44:48 -0400 Subject: Btrfs: optimize read_block_for_search This patch does two things, first it only does one call to btrfs_buffer_uptodate() with the gen specified instead of once with 0 and then again with gen specified. The other thing is to call btrfs_read_buffer() on the buffer we've found instead of dropping it and then calling read_tree_block(). This will keep us from doing yet another radix tree lookup for a buffer we've already found. Thanks, Signed-off-by: Josef Bacik --- fs/btrfs/ctree.c | 47 ++++++++++++++++++++--------------------------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index 1c9dc71089ce..c85cde761248 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -2359,35 +2359,28 @@ read_block_for_search(struct btrfs_trans_handle *trans, tmp = btrfs_find_tree_block(root, blocknr, blocksize); if (tmp) { /* first we do an atomic uptodate check */ - if (btrfs_buffer_uptodate(tmp, 0, 1) > 0) { - if (btrfs_buffer_uptodate(tmp, gen, 1) > 0) { - /* - * we found an up to date block without - * sleeping, return - * right away - */ - *eb_ret = tmp; - return 0; - } - /* the pages were up to date, but we failed - * the generation number check. Do a full - * read for the generation number that is correct. - * We must do this without dropping locks so - * we can trust our generation number - */ - free_extent_buffer(tmp); - btrfs_set_path_blocking(p); + if (btrfs_buffer_uptodate(tmp, gen, 1) > 0) { + *eb_ret = tmp; + return 0; + } - /* now we're allowed to do a blocking uptodate check */ - tmp = read_tree_block(root, blocknr, blocksize, gen); - if (tmp && btrfs_buffer_uptodate(tmp, gen, 0) > 0) { - *eb_ret = tmp; - return 0; - } - free_extent_buffer(tmp); - btrfs_release_path(p); - return -EIO; + /* the pages were up to date, but we failed + * the generation number check. Do a full + * read for the generation number that is correct. + * We must do this without dropping locks so + * we can trust our generation number + */ + btrfs_set_path_blocking(p); + + /* now we're allowed to do a blocking uptodate check */ + ret = btrfs_read_buffer(tmp, gen); + if (!ret) { + *eb_ret = tmp; + return 0; } + free_extent_buffer(tmp); + btrfs_release_path(p); + return -EIO; } /* -- cgit v1.2.3 From 0b08851fdaa5f9f74357345d7be44ea584665d5f Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Mon, 17 Jun 2013 14:23:02 -0400 Subject: Btrfs: optimize reada_for_balance This patch does two things. First we no longer explicitly read in the blocks we're trying to readahead. For things like balance_level we may never actually use the blocks so this just adds uneeded latency, and balance_level and split_node will both read in the blocks they care about explicitly so if the blocks need to be waited on it will be done there. Secondly we no longer drop the path if we do readahead, we just set the path blocking before we call reada_for_balance() and then we're good to go. Hopefully this will cut down on the number of re-searches. Thanks, Signed-off-by: Josef Bacik --- fs/btrfs/ctree.c | 46 +++++++++------------------------------------- 1 file changed, 9 insertions(+), 37 deletions(-) diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index c85cde761248..c32d03dff4fc 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -2178,12 +2178,8 @@ static void reada_for_search(struct btrfs_root *root, } } -/* - * returns -EAGAIN if it had to drop the path, or zero if everything was in - * cache - */ -static noinline int reada_for_balance(struct btrfs_root *root, - struct btrfs_path *path, int level) +static noinline void reada_for_balance(struct btrfs_root *root, + struct btrfs_path *path, int level) { int slot; int nritems; @@ -2192,12 +2188,11 @@ static noinline int reada_for_balance(struct btrfs_root *root, u64 gen; u64 block1 = 0; u64 block2 = 0; - int ret = 0; int blocksize; parent = path->nodes[level + 1]; if (!parent) - return 0; + return; nritems = btrfs_header_nritems(parent); slot = path->slots[level + 1]; @@ -2224,28 +2219,11 @@ static noinline int reada_for_balance(struct btrfs_root *root, block2 = 0; free_extent_buffer(eb); } - if (block1 || block2) { - ret = -EAGAIN; - - /* release the whole path */ - btrfs_release_path(path); - /* read the blocks */ - if (block1) - readahead_tree_block(root, block1, blocksize, 0); - if (block2) - readahead_tree_block(root, block2, blocksize, 0); - - if (block1) { - eb = read_tree_block(root, block1, blocksize, 0); - free_extent_buffer(eb); - } - if (block2) { - eb = read_tree_block(root, block2, blocksize, 0); - free_extent_buffer(eb); - } - } - return ret; + if (block1) + readahead_tree_block(root, block1, blocksize, 0); + if (block2) + readahead_tree_block(root, block2, blocksize, 0); } @@ -2441,11 +2419,8 @@ setup_nodes_for_search(struct btrfs_trans_handle *trans, goto again; } - sret = reada_for_balance(root, p, level); - if (sret) - goto again; - btrfs_set_path_blocking(p); + reada_for_balance(root, p, level); sret = split_node(trans, root, p, level); btrfs_clear_path_blocking(p, NULL, 0); @@ -2465,11 +2440,8 @@ setup_nodes_for_search(struct btrfs_trans_handle *trans, goto again; } - sret = reada_for_balance(root, p, level); - if (sret) - goto again; - btrfs_set_path_blocking(p); + reada_for_balance(root, p, level); sret = balance_level(trans, root, p, level); btrfs_clear_path_blocking(p, NULL, 0); -- cgit v1.2.3 From a71754fc68f740b7ed46bb83123c63fbbc130611 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Mon, 17 Jun 2013 17:14:39 -0400 Subject: Btrfs: move btrfs_truncate_page to btrfs_cont_expand instead of btrfs_truncate This has plagued us forever and I'm so over working around it. When we truncate down to a non-page aligned offset we will call btrfs_truncate_page to zero out the end of the page and write it back to disk, this will keep us from exposing stale data if we truncate back up from that point. The problem with this is it requires data space to do this, and people don't really expect to get ENOSPC from truncate() for these sort of things. This also tends to bite the orphan cleanup stuff too which keeps people from mounting. To get around this we can just move this into btrfs_cont_expand() to make sure if we are truncating up from a non-page size aligned i_size we will zero out the rest of this page so that we don't expose stale data. This will give ENOSPC if you try to truncate() up or if you try to write past the end of isize, which is much more reasonable. This fixes xfstests generic/083 failing to mount because of the orphan cleanup failing. Thanks, Signed-off-by: Josef Bacik --- fs/btrfs/extent_io.c | 11 ++++++++++- fs/btrfs/file.c | 21 +++++++++++++++------ fs/btrfs/inode.c | 15 ++++++++++----- 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 03ca3ab95898..a83d7019ede9 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -2477,11 +2477,12 @@ static void end_bio_extent_readpage(struct bio *bio, int err) struct extent_state *cached = NULL; struct extent_state *state; struct btrfs_io_bio *io_bio = btrfs_io_bio(bio); + struct inode *inode = page->mapping->host; pr_debug("end_bio_extent_readpage: bi_sector=%llu, err=%d, " "mirror=%lu\n", (u64)bio->bi_sector, err, io_bio->mirror_num); - tree = &BTRFS_I(page->mapping->host)->io_tree; + tree = &BTRFS_I(inode)->io_tree; /* We always issue full-page reads, but if some block * in a page fails to read, blk_update_request() will @@ -2555,6 +2556,14 @@ static void end_bio_extent_readpage(struct bio *bio, int err) unlock_extent_cached(tree, start, end, &cached, GFP_ATOMIC); if (uptodate) { + loff_t i_size = i_size_read(inode); + pgoff_t end_index = i_size >> PAGE_CACHE_SHIFT; + unsigned offset; + + /* Zero out the end if this page straddles i_size */ + offset = i_size & (PAGE_CACHE_SIZE-1); + if (page->index == end_index && offset) + zero_user_segment(page, offset, PAGE_CACHE_SIZE); SetPageUptodate(page); } else { ClearPageUptodate(page); diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 185af15ad9e4..5ffde5603686 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -2173,12 +2173,6 @@ static long btrfs_fallocate(struct file *file, int mode, goto out_reserve_fail; } - /* - * wait for ordered IO before we have any locks. We'll loop again - * below with the locks held. - */ - btrfs_wait_ordered_range(inode, alloc_start, alloc_end - alloc_start); - mutex_lock(&inode->i_mutex); ret = inode_newsize_ok(inode, alloc_end); if (ret) @@ -2189,8 +2183,23 @@ static long btrfs_fallocate(struct file *file, int mode, alloc_start); if (ret) goto out; + } else { + /* + * If we are fallocating from the end of the file onward we + * need to zero out the end of the page if i_size lands in the + * middle of a page. + */ + ret = btrfs_truncate_page(inode, inode->i_size, 0, 0); + if (ret) + goto out; } + /* + * wait for ordered IO before we have any locks. We'll loop again + * below with the locks held. + */ + btrfs_wait_ordered_range(inode, alloc_start, alloc_end - alloc_start); + locked_end = alloc_end - 1; while (1) { struct btrfs_ordered_extent *ordered; diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 509112da6118..b7fa96f72ecd 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -4253,6 +4253,15 @@ int btrfs_cont_expand(struct inode *inode, loff_t oldsize, loff_t size) u64 hole_size; int err = 0; + /* + * If our size started in the middle of a page we need to zero out the + * rest of the page before we expand the i_size, otherwise we could + * expose stale data. + */ + err = btrfs_truncate_page(inode, oldsize, 0, 0); + if (err) + return err; + if (size <= hole_start) return 0; @@ -7565,16 +7574,12 @@ static int btrfs_truncate(struct inode *inode) { struct btrfs_root *root = BTRFS_I(inode)->root; struct btrfs_block_rsv *rsv; - int ret; + int ret = 0; int err = 0; struct btrfs_trans_handle *trans; u64 mask = root->sectorsize - 1; u64 min_size = btrfs_calc_trunc_metadata_size(root, 1); - ret = btrfs_truncate_page(inode, inode->i_size, 0, 0); - if (ret) - return ret; - btrfs_wait_ordered_range(inode, inode->i_size & (~mask), (u64)-1); btrfs_ordered_update_i_size(inode, inode->i_size, NULL); -- cgit v1.2.3 From b6356a013a0933e42556c2def806f06caf459acf Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Mon, 1 Jul 2013 17:01:56 +0300 Subject: remoteproc: free carveout memories only after unmapping them It is not preferable to have the allocated pages for carveout memories freed before they are unmapped. The code that deals with the cleanup of carveout memories is therefore moved after the corresponding mapping entries were cleaned up. This is mostly a no-op since the remote processors are already stopped when the cleanup function is called, but this will make the cleanup code follow the exact reverse path of allocation. Signed-off-by: Suman Anna Signed-off-by: Ohad Ben-Cohen --- drivers/remoteproc/remoteproc_core.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index 7e33536b5c2b..9fef20022ab4 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -762,13 +762,6 @@ static void rproc_resource_cleanup(struct rproc *rproc) kfree(entry); } - /* clean up carveout allocations */ - list_for_each_entry_safe(entry, tmp, &rproc->carveouts, node) { - dma_free_coherent(dev->parent, entry->len, entry->va, entry->dma); - list_del(&entry->node); - kfree(entry); - } - /* clean up iommu mapping entries */ list_for_each_entry_safe(entry, tmp, &rproc->mappings, node) { size_t unmapped; @@ -783,6 +776,13 @@ static void rproc_resource_cleanup(struct rproc *rproc) list_del(&entry->node); kfree(entry); } + + /* clean up carveout allocations */ + list_for_each_entry_safe(entry, tmp, &rproc->carveouts, node) { + dma_free_coherent(dev->parent, entry->len, entry->va, entry->dma); + list_del(&entry->node); + kfree(entry); + } } /* -- cgit v1.2.3 From 95cee62cb4776a65229a6b6d5969be56589d95c1 Mon Sep 17 00:00:00 2001 From: Thomas Meyer Date: Mon, 1 Jul 2013 17:23:58 +0300 Subject: remoteproc: Cocci spatch "memdup.spatch" Use kmemdup instead of kmalloc + memcpy. Signed-off-by: Thomas Meyer Signed-off-by: Ohad Ben-Cohen --- drivers/remoteproc/remoteproc_core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index 9fef20022ab4..3cd85a638afa 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -914,11 +914,10 @@ static void rproc_fw_config_virtio(const struct firmware *fw, void *context) * will be stored in the cached_table. Before the device is started, * cached_table will be copied into devic memory. */ - rproc->cached_table = kmalloc(tablesz, GFP_KERNEL); + rproc->cached_table = kmemdup(table, tablesz, GFP_KERNEL); if (!rproc->cached_table) goto out; - memcpy(rproc->cached_table, table, tablesz); rproc->table_ptr = rproc->cached_table; /* count the number of notify-ids */ -- cgit v1.2.3 From 8d38ef1948bd415a5cb653a5c0ec16f3402aaca1 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 1 Jul 2013 08:28:58 -0600 Subject: vfio/type1: Fix leak on error path We also don't handle unpinning zero pages as an error on other exits so we can fix that inconsistency by rolling in the next conditional return. Reported-by: Dan Carpenter Signed-off-by: Alex Williamson --- drivers/vfio/vfio_iommu_type1.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c index 98231d10890c..a9807dea3887 100644 --- a/drivers/vfio/vfio_iommu_type1.c +++ b/drivers/vfio/vfio_iommu_type1.c @@ -436,6 +436,12 @@ static int vfio_remove_dma_overlap(struct vfio_iommu *iommu, dma_addr_t start, } /* Split existing */ + + /* + * Allocate our tracking structure early even though it may not + * be used. An Allocation failure later loses track of pages and + * is more difficult to unwind. + */ split = kzalloc(sizeof(*split), GFP_KERNEL); if (!split) return -ENOMEM; @@ -443,12 +449,9 @@ static int vfio_remove_dma_overlap(struct vfio_iommu *iommu, dma_addr_t start, offset = start - dma->iova; ret = vfio_unmap_unpin(iommu, dma, start, size); - if (ret) - return ret; - - if (!*size) { + if (ret || !*size) { kfree(split); - return -EINVAL; + return ret; } tmp = dma->size; -- cgit v1.2.3 From d783d9ad4be34c23c5b68a7a611937a87dff630c Mon Sep 17 00:00:00 2001 From: Hendrik Brueckner Date: Fri, 28 Jun 2013 15:06:59 +0200 Subject: s390/hwsampler: Updated misleading member names in hws_data_entry There is a misleading naming of the program parameter fields, so correct them according to their names as outlined in "The Load-Program-Parameter and the CPU-measurement Facilities" (SA23-2260-03). Signed-off-by: Hendrik Brueckner Signed-off-by: Martin Schwidefsky --- arch/s390/oprofile/hwsampler.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/s390/oprofile/hwsampler.h b/arch/s390/oprofile/hwsampler.h index 1912f3bb190c..0022e1ebfbde 100644 --- a/arch/s390/oprofile/hwsampler.h +++ b/arch/s390/oprofile/hwsampler.h @@ -81,8 +81,8 @@ struct hws_data_entry { unsigned int:16; unsigned int prim_asn:16; /* primary ASN */ unsigned long long ia; /* Instruction Address */ - unsigned long long lpp; /* Logical-Partition Program Param. */ - unsigned long long vpp; /* Virtual-Machine Program Param. */ + unsigned long long gpp; /* Guest Program Parameter */ + unsigned long long hpp; /* Host Program Parameter */ }; struct hws_trailer_entry { -- cgit v1.2.3 From b99a946de6a988a38043180d42e6b7a2f07d4f57 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Wed, 30 Jan 2013 09:26:11 +0000 Subject: s390/dasd: Clarify comment dasd_cancel_req will never return 1, only 0. Signed-off-by: Hannes Reinecke Signed-off-by: Stefan Weinhuber Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index d72a9216ee2e..498548980d78 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -2402,8 +2402,7 @@ int dasd_sleep_on_immediatly(struct dasd_ccw_req *cqr) * Cancels a request that was started with dasd_sleep_on_req. * This is useful to timeout requests. The request will be * terminated if it is currently in i/o. - * Returns 1 if the request has been terminated. - * 0 if there was no need to terminate the request (not started yet) + * Returns 0 if request termination was successful * negative error code if termination failed * Cancellation of a request is an asynchronous operation! The calling * function has to wait until the request is properly returned via callback. @@ -2440,7 +2439,6 @@ int dasd_cancel_req(struct dasd_ccw_req *cqr) return rc; } - /* * SECTION: Operations of the dasd_block layer. */ -- cgit v1.2.3 From 1f1ee9ad6b298400c7a44f860231ac1d3cb7bfd6 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Wed, 30 Jan 2013 09:26:12 +0000 Subject: s390/dasd: make number of retries configurable Instead of having the number of retries hard-coded in the various functions we should be using a default retry value, which can be modified via sysfs. Signed-off-by: Hannes Reinecke Signed-off-by: Stefan Weinhuber Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd_devmap.c | 41 ++++++++++++++++++++++++++++++++++++++++ drivers/s390/block/dasd_diag.c | 3 ++- drivers/s390/block/dasd_eckd.c | 11 +++++++---- drivers/s390/block/dasd_fba.c | 5 ++++- drivers/s390/block/dasd_int.h | 3 +++ 5 files changed, 57 insertions(+), 6 deletions(-) diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index a71bb8aaca1d..bc3e7afac18b 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -1240,6 +1240,46 @@ dasd_expires_store(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(expires, 0644, dasd_expires_show, dasd_expires_store); +static ssize_t +dasd_retries_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct dasd_device *device; + int len; + + device = dasd_device_from_cdev(to_ccwdev(dev)); + if (IS_ERR(device)) + return -ENODEV; + len = snprintf(buf, PAGE_SIZE, "%lu\n", device->default_retries); + dasd_put_device(device); + return len; +} + +static ssize_t +dasd_retries_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct dasd_device *device; + unsigned long val; + + device = dasd_device_from_cdev(to_ccwdev(dev)); + if (IS_ERR(device)) + return -ENODEV; + + if ((strict_strtoul(buf, 10, &val) != 0) || + (val > DASD_RETRIES_MAX)) { + dasd_put_device(device); + return -EINVAL; + } + + if (val) + device->default_retries = val; + + dasd_put_device(device); + return count; +} + +static DEVICE_ATTR(retries, 0644, dasd_retries_show, dasd_retries_store); + static ssize_t dasd_reservation_policy_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -1350,6 +1390,7 @@ static struct attribute * dasd_attrs[] = { &dev_attr_erplog.attr, &dev_attr_failfast.attr, &dev_attr_expires.attr, + &dev_attr_retries.attr, &dev_attr_reservation_policy.attr, &dev_attr_last_known_reservation_state.attr, &dev_attr_safe_offline.attr, diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c index cc0603358522..154842242c3d 100644 --- a/drivers/s390/block/dasd_diag.c +++ b/drivers/s390/block/dasd_diag.c @@ -359,6 +359,7 @@ dasd_diag_check_device(struct dasd_device *device) } device->default_expires = DIAG_TIMEOUT; + device->default_retries = DIAG_MAX_RETRIES; /* Figure out position of label block */ switch (private->rdc_data.vdev_class) { @@ -555,7 +556,7 @@ static struct dasd_ccw_req *dasd_diag_build_cp(struct dasd_device *memdev, recid++; } } - cqr->retries = DIAG_MAX_RETRIES; + cqr->retries = memdev->default_retries; cqr->buildclk = get_tod_clock(); if (blk_noretry_request(req) || block->base->features & DASD_FEATURE_FAILFAST) diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 696735962938..05f5694c9c49 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -1682,6 +1682,9 @@ dasd_eckd_check_characteristics(struct dasd_device *device) /* set default timeout */ device->default_expires = DASD_EXPIRES; + /* set default retry count */ + device->default_retries = DASD_RETRIES; + if (private->gneq) { value = 1; for (i = 0; i < private->gneq->timeout.value; i++) @@ -2659,7 +2662,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_single( cqr->block = block; cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */ cqr->lpm = startdev->path_data.ppm; - cqr->retries = 256; + cqr->retries = startdev->default_retries; cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; return cqr; @@ -2834,7 +2837,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_track( cqr->block = block; cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */ cqr->lpm = startdev->path_data.ppm; - cqr->retries = 256; + cqr->retries = startdev->default_retries; cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; return cqr; @@ -3127,7 +3130,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track( cqr->block = block; cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */ cqr->lpm = startdev->path_data.ppm; - cqr->retries = 256; + cqr->retries = startdev->default_retries; cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; return cqr; @@ -3330,7 +3333,7 @@ static struct dasd_ccw_req *dasd_raw_build_cp(struct dasd_device *startdev, cqr->block = block; cqr->expires = startdev->default_expires * HZ; cqr->lpm = startdev->path_data.ppm; - cqr->retries = 256; + cqr->retries = startdev->default_retries; cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c index 4dd0e2f6047e..d232c83ea95a 100644 --- a/drivers/s390/block/dasd_fba.c +++ b/drivers/s390/block/dasd_fba.c @@ -29,6 +29,8 @@ #endif /* PRINTK_HEADER */ #define PRINTK_HEADER "dasd(fba):" +#define FBA_DEFAULT_RETRIES 32 + #define DASD_FBA_CCW_WRITE 0x41 #define DASD_FBA_CCW_READ 0x42 #define DASD_FBA_CCW_LOCATE 0x43 @@ -167,6 +169,7 @@ dasd_fba_check_characteristics(struct dasd_device *device) } device->default_expires = DASD_EXPIRES; + device->default_retries = FBA_DEFAULT_RETRIES; device->path_data.opm = LPM_ANYPATH; readonly = dasd_device_is_ro(device); @@ -369,7 +372,7 @@ static struct dasd_ccw_req *dasd_fba_build_cp(struct dasd_device * memdev, cqr->memdev = memdev; cqr->block = block; cqr->expires = memdev->default_expires * HZ; /* default 5 minutes */ - cqr->retries = 32; + cqr->retries = memdev->default_retries; cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; return cqr; diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index 0785bd9bd5b6..ad42075868c7 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -224,6 +224,8 @@ struct dasd_ccw_req { /* default expiration time*/ #define DASD_EXPIRES 300 #define DASD_EXPIRES_MAX 40000000 +#define DASD_RETRIES 256 +#define DASD_RETRIES_MAX 32768 /* per dasd_ccw_req flags */ #define DASD_CQR_FLAGS_USE_ERP 0 /* use ERP for this request */ @@ -466,6 +468,7 @@ struct dasd_device { /* default expiration time in s */ unsigned long default_expires; + unsigned long default_retries; struct dentry *debugfs_dentry; struct dasd_profile profile; -- cgit v1.2.3 From 1fbdb8be9bfc91efd45720493c7ecae884ae22bd Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Wed, 30 Jan 2013 09:26:13 +0000 Subject: s390/dasd: process all requests in the device tasklet Originally the DASD device tasklet would process the entries on the ccw_queue until the first non-final request was found. Which was okay as long as all requests have the same retries and expires parameter. However, as we're now allowing to modify both it is possible to have requests _after_ the first request which already have expired. So we need to check all requests in the device tasklet. Signed-off-by: Hannes Reinecke Signed-off-by: Stefan Weinhuber Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 498548980d78..000e5140bda4 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -1787,11 +1787,11 @@ static void __dasd_device_process_ccw_queue(struct dasd_device *device, list_for_each_safe(l, n, &device->ccw_queue) { cqr = list_entry(l, struct dasd_ccw_req, devlist); - /* Stop list processing at the first non-final request. */ + /* Skip any non-final request. */ if (cqr->status == DASD_CQR_QUEUED || cqr->status == DASD_CQR_IN_IO || cqr->status == DASD_CQR_CLEAR_PENDING) - break; + continue; if (cqr->status == DASD_CQR_ERROR) { __dasd_device_recovery(device, cqr); } -- cgit v1.2.3 From a2ace46632fb38c7a3771f2f0d235a4295e83bcf Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Wed, 30 Jan 2013 09:26:14 +0000 Subject: s390/dasd: Implement block timeout handling This patch implements generic block layer timeout handling callbacks for DASDs. When the timeout expires the respective cqr is aborted. With this timeout handler time-critical request abort is guaranteed as the abort does not depend on the internal state of the various DASD driver queues. Signed-off-by: Hannes Reinecke Acked-by: Stefan Weinhuber Signed-off-by: Stefan Weinhuber Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd.c | 76 ++++++++++++++++++++++++++++++++++++++++++ drivers/s390/block/dasd_diag.c | 5 ++- drivers/s390/block/dasd_eckd.c | 4 +++ drivers/s390/block/dasd_fba.c | 5 ++- 4 files changed, 88 insertions(+), 2 deletions(-) diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 000e5140bda4..87478becedb0 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -2573,8 +2573,10 @@ static void __dasd_process_request_queue(struct dasd_block *block) */ cqr->callback_data = (void *) req; cqr->status = DASD_CQR_FILLED; + req->completion_data = cqr; blk_start_request(req); list_add_tail(&cqr->blocklist, &block->ccw_queue); + INIT_LIST_HEAD(&cqr->devlist); dasd_profile_start(block, cqr, req); } } @@ -2861,6 +2863,80 @@ static void do_dasd_request(struct request_queue *queue) spin_unlock(&block->queue_lock); } +/* + * Block timeout callback, called from the block layer + * + * request_queue lock is held on entry. + * + * Return values: + * BLK_EH_RESET_TIMER if the request should be left running + * BLK_EH_NOT_HANDLED if the request is handled or terminated + * by the driver. + */ +enum blk_eh_timer_return dasd_times_out(struct request *req) +{ + struct dasd_ccw_req *cqr = req->completion_data; + struct dasd_block *block = req->q->queuedata; + struct dasd_device *device; + int rc = 0; + + if (!cqr) + return BLK_EH_NOT_HANDLED; + + device = cqr->startdev ? cqr->startdev : block->base; + DBF_DEV_EVENT(DBF_WARNING, device, + " dasd_times_out cqr %p status %x", + cqr, cqr->status); + + spin_lock(&block->queue_lock); + spin_lock(get_ccwdev_lock(device->cdev)); + cqr->retries = -1; + cqr->intrc = -ETIMEDOUT; + if (cqr->status >= DASD_CQR_QUEUED) { + spin_unlock(get_ccwdev_lock(device->cdev)); + rc = dasd_cancel_req(cqr); + } else if (cqr->status == DASD_CQR_FILLED || + cqr->status == DASD_CQR_NEED_ERP) { + cqr->status = DASD_CQR_TERMINATED; + spin_unlock(get_ccwdev_lock(device->cdev)); + } else if (cqr->status == DASD_CQR_IN_ERP) { + struct dasd_ccw_req *searchcqr, *nextcqr, *tmpcqr; + + list_for_each_entry_safe(searchcqr, nextcqr, + &block->ccw_queue, blocklist) { + tmpcqr = searchcqr; + while (tmpcqr->refers) + tmpcqr = tmpcqr->refers; + if (tmpcqr != cqr) + continue; + /* searchcqr is an ERP request for cqr */ + searchcqr->retries = -1; + searchcqr->intrc = -ETIMEDOUT; + if (searchcqr->status >= DASD_CQR_QUEUED) { + spin_unlock(get_ccwdev_lock(device->cdev)); + rc = dasd_cancel_req(searchcqr); + spin_lock(get_ccwdev_lock(device->cdev)); + } else if ((searchcqr->status == DASD_CQR_FILLED) || + (searchcqr->status == DASD_CQR_NEED_ERP)) { + searchcqr->status = DASD_CQR_TERMINATED; + rc = 0; + } else if (searchcqr->status == DASD_CQR_IN_ERP) { + /* + * Shouldn't happen; most recent ERP + * request is at the front of queue + */ + continue; + } + break; + } + spin_unlock(get_ccwdev_lock(device->cdev)); + } + dasd_schedule_block_bh(block); + spin_unlock(&block->queue_lock); + + return rc ? BLK_EH_RESET_TIMER : BLK_EH_NOT_HANDLED; +} + /* * Allocate and initialize request queue and default I/O scheduler. */ diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c index 154842242c3d..feca317b33de 100644 --- a/drivers/s390/block/dasd_diag.c +++ b/drivers/s390/block/dasd_diag.c @@ -583,7 +583,10 @@ dasd_diag_free_cp(struct dasd_ccw_req *cqr, struct request *req) static void dasd_diag_handle_terminated_request(struct dasd_ccw_req *cqr) { - cqr->status = DASD_CQR_FILLED; + if (cqr->retries < 0) + cqr->status = DASD_CQR_FAILED; + else + cqr->status = DASD_CQR_FILLED; }; /* Fill in IOCTL data for device. */ diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 05f5694c9c49..e61a6deea3c0 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -2381,6 +2381,10 @@ sleep: static void dasd_eckd_handle_terminated_request(struct dasd_ccw_req *cqr) { + if (cqr->retries < 0) { + cqr->status = DASD_CQR_FAILED; + return; + } cqr->status = DASD_CQR_FILLED; if (cqr->block && (cqr->startdev != cqr->block->base)) { dasd_eckd_reset_ccw_to_base_io(cqr); diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c index d232c83ea95a..9cbc8c32ba59 100644 --- a/drivers/s390/block/dasd_fba.c +++ b/drivers/s390/block/dasd_fba.c @@ -428,7 +428,10 @@ out: static void dasd_fba_handle_terminated_request(struct dasd_ccw_req *cqr) { - cqr->status = DASD_CQR_FILLED; + if (cqr->retries < 0) + cqr->status = DASD_CQR_FAILED; + else + cqr->status = DASD_CQR_FILLED; }; static int -- cgit v1.2.3 From f6f80340308cdcb84db45d3abf95f23d5b66051d Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Wed, 30 Jan 2013 09:26:15 +0000 Subject: s390/dasd: Reduce amount of messages for specific errors Whenever a request has been aborted internally by the driver there is no sense data to be had. And printing lots of messages stalls the system, so better to print out a short one-liner. Signed-off-by: Hannes Reinecke Signed-off-by: Stefan Weinhuber Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd_erp.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/s390/block/dasd_erp.c b/drivers/s390/block/dasd_erp.c index 3250cb471f78..8d11f773a752 100644 --- a/drivers/s390/block/dasd_erp.c +++ b/drivers/s390/block/dasd_erp.c @@ -159,6 +159,14 @@ dasd_log_sense(struct dasd_ccw_req *cqr, struct irb *irb) struct dasd_device *device; device = cqr->startdev; + if (cqr->intrc == -ETIMEDOUT) { + dev_err(&device->cdev->dev, "cqr %p timeout error", cqr); + return; + } + if (cqr->intrc == -ENOLINK) { + dev_err(&device->cdev->dev, "cqr %p transport error", cqr); + return; + } /* dump sense data */ if (device->discipline && device->discipline->dump_sense) device->discipline->dump_sense(device, cqr, irb); -- cgit v1.2.3 From d1ffc1f8660e749a2683f19ee7cfdd837568b37a Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Wed, 30 Jan 2013 09:26:16 +0000 Subject: block/dasd: detailed I/O errors The DASD driver is using FASTFAIL as an equivalent to the transport errors in SCSI. And the 'steal lock' function maps roughly to a reservation error. So we should be returning the appropriate error codes when completing a request. Acked-by: Jens Axboe Signed-off-by: Hannes Reinecke Signed-off-by: Stefan Weinhuber Signed-off-by: Martin Schwidefsky --- block/blk-core.c | 3 +++ drivers/s390/block/dasd.c | 16 +++++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/block/blk-core.c b/block/blk-core.c index d5745b5833c9..321443226888 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -2315,6 +2315,9 @@ bool blk_update_request(struct request *req, int error, unsigned int nr_bytes) case -EBADE: error_type = "critical nexus"; break; + case -ETIMEDOUT: + error_type = "timeout"; + break; case -EIO: default: error_type = "I/O"; diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 87478becedb0..b97624bf183d 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -2183,7 +2183,7 @@ static int _dasd_sleep_on(struct dasd_ccw_req *maincqr, int interruptible) test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) && (!dasd_eer_enabled(device))) { cqr->status = DASD_CQR_FAILED; - cqr->intrc = -EAGAIN; + cqr->intrc = -ENOLINK; continue; } /* Don't try to start requests if device is stopped */ @@ -2590,8 +2590,17 @@ static void __dasd_cleanup_cqr(struct dasd_ccw_req *cqr) req = (struct request *) cqr->callback_data; dasd_profile_end(cqr->block, cqr, req); status = cqr->block->base->discipline->free_cp(cqr, req); - if (status <= 0) - error = status ? status : -EIO; + if (status < 0) + error = status; + else if (status == 0) { + if (cqr->intrc == -EPERM) + error = -EBADE; + else if (cqr->intrc == -ENOLINK || + cqr->intrc == -ETIMEDOUT) + error = cqr->intrc; + else + error = -EIO; + } __blk_end_request_all(req, error); } @@ -2692,6 +2701,7 @@ static void __dasd_block_start_head(struct dasd_block *block) test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) && (!dasd_eer_enabled(block->base))) { cqr->status = DASD_CQR_FAILED; + cqr->intrc = -ENOLINK; dasd_schedule_block_bh(block); continue; } -- cgit v1.2.3 From 80bd7181b036c7b4118eb19cfff3b555889596e6 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Wed, 30 Jan 2013 09:26:17 +0000 Subject: block: check for timeout function in blk_rq_timed_out() rq_timed_out_fn might have been unset while the request was in flight, so we need to check for it in blk_rq_timed_out(). Acked-by: Jens Axboe Signed-off-by: Hannes Reinecke Signed-off-by: Stefan Weinhuber Signed-off-by: Martin Schwidefsky --- block/blk-timeout.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/block/blk-timeout.c b/block/blk-timeout.c index 6e4744cbfb56..65f103563969 100644 --- a/block/blk-timeout.c +++ b/block/blk-timeout.c @@ -82,9 +82,10 @@ void blk_delete_timer(struct request *req) static void blk_rq_timed_out(struct request *req) { struct request_queue *q = req->q; - enum blk_eh_timer_return ret; + enum blk_eh_timer_return ret = BLK_EH_RESET_TIMER; - ret = q->rq_timed_out_fn(req); + if (q->rq_timed_out_fn) + ret = q->rq_timed_out_fn(req); switch (ret) { case BLK_EH_HANDLED: __blk_complete_request(req); -- cgit v1.2.3 From 3d71ad32167c9124d5621b54c37a74ef38aa93b0 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Wed, 30 Jan 2013 09:26:18 +0000 Subject: s390/dasd: Add 'timeout' attribute This patch adds a 'timeout' attibute to the DASD driver. When set to non-zero, the blk_timeout function will be enabled with the timeout specified in the attribute. Setting 'timeout' to '0' will disable block timeouts. Signed-off-by: Hannes Reinecke Signed-off-by: Stefan Weinhuber Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd.c | 2 ++ drivers/s390/block/dasd_devmap.c | 56 ++++++++++++++++++++++++++++++++++++++++ drivers/s390/block/dasd_int.h | 4 +++ 3 files changed, 62 insertions(+) diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index b97624bf183d..54f4bb8060c1 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -2894,6 +2894,8 @@ enum blk_eh_timer_return dasd_times_out(struct request *req) return BLK_EH_NOT_HANDLED; device = cqr->startdev ? cqr->startdev : block->base; + if (!device->blk_timeout) + return BLK_EH_RESET_TIMER; DBF_DEV_EVENT(DBF_WARNING, device, " dasd_times_out cqr %p status %x", cqr, cqr->status); diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index bc3e7afac18b..58bc6eb49de1 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -1280,6 +1280,61 @@ dasd_retries_store(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(retries, 0644, dasd_retries_show, dasd_retries_store); +static ssize_t +dasd_timeout_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct dasd_device *device; + int len; + + device = dasd_device_from_cdev(to_ccwdev(dev)); + if (IS_ERR(device)) + return -ENODEV; + len = snprintf(buf, PAGE_SIZE, "%lu\n", device->blk_timeout); + dasd_put_device(device); + return len; +} + +static ssize_t +dasd_timeout_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct dasd_device *device; + struct request_queue *q; + unsigned long val, flags; + + device = dasd_device_from_cdev(to_ccwdev(dev)); + if (IS_ERR(device) || !device->block) + return -ENODEV; + + if ((strict_strtoul(buf, 10, &val) != 0) || + val > UINT_MAX / HZ) { + dasd_put_device(device); + return -EINVAL; + } + q = device->block->request_queue; + if (!q) { + dasd_put_device(device); + return -ENODEV; + } + spin_lock_irqsave(&device->block->request_queue_lock, flags); + if (!val) + blk_queue_rq_timed_out(q, NULL); + else + blk_queue_rq_timed_out(q, dasd_times_out); + + device->blk_timeout = val; + + blk_queue_rq_timeout(q, device->blk_timeout * HZ); + spin_unlock_irqrestore(&device->block->request_queue_lock, flags); + + dasd_put_device(device); + return count; +} + +static DEVICE_ATTR(timeout, 0644, + dasd_timeout_show, dasd_timeout_store); + static ssize_t dasd_reservation_policy_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -1391,6 +1446,7 @@ static struct attribute * dasd_attrs[] = { &dev_attr_failfast.attr, &dev_attr_expires.attr, &dev_attr_retries.attr, + &dev_attr_timeout.attr, &dev_attr_reservation_policy.attr, &dev_attr_last_known_reservation_state.attr, &dev_attr_safe_offline.attr, diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index ad42075868c7..2bd03f45a72e 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -470,6 +470,8 @@ struct dasd_device { unsigned long default_expires; unsigned long default_retries; + unsigned long blk_timeout; + struct dentry *debugfs_dentry; struct dasd_profile profile; }; @@ -663,6 +665,8 @@ void dasd_free_device(struct dasd_device *); struct dasd_block *dasd_alloc_block(void); void dasd_free_block(struct dasd_block *); +enum blk_eh_timer_return dasd_times_out(struct request *req); + void dasd_enable_device(struct dasd_device *); void dasd_set_target_state(struct dasd_device *, int); void dasd_kick_device(struct dasd_device *); -- cgit v1.2.3 From 5ea34a01423a27d4526f3551e8542f2f991bd4a0 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Wed, 30 Jan 2013 09:26:19 +0000 Subject: s390/dasd: Fail all requests when DASD_FLAG_ABORTIO is set Whenever a DASD request encounters a timeout we might need to abort all outstanding requests on this or even other devices. This is especially useful if one wants to fail all devices on one side of a RAID10 configuration, even though only one device exhibited an error. To handle this I've introduced a new device flag DASD_FLAG_ABORTIO. This flag is evaluated in __dasd_process_request_queue() and will invoke blk_abort_request() for all outstanding requests with DASD_CQR_FLAGS_FAILFAST set. This will cause any of these requests to be aborted immediately if the blk_timeout function is activated. The DASD_FLAG_ABORTIO is also evaluated in __dasd_process_request_queue to abort all new request which would have the DASD_CQR_FLAGS_FAILFAST bit set. The flag can be set with the new ioctls 'BIODASDABORTIO' and removed with 'BIODASDALLOWIO'. Signed-off-by: Hannes Reinecke Signed-off-by: Stefan Weinhuber Signed-off-by: Martin Schwidefsky --- arch/s390/include/uapi/asm/dasd.h | 4 +++ drivers/s390/block/dasd.c | 13 +++++++-- drivers/s390/block/dasd_int.h | 3 ++ drivers/s390/block/dasd_ioctl.c | 59 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 76 insertions(+), 3 deletions(-) diff --git a/arch/s390/include/uapi/asm/dasd.h b/arch/s390/include/uapi/asm/dasd.h index 38eca3ba40e2..5812a3b2df9e 100644 --- a/arch/s390/include/uapi/asm/dasd.h +++ b/arch/s390/include/uapi/asm/dasd.h @@ -261,6 +261,10 @@ struct dasd_snid_ioctl_data { #define BIODASDQUIESCE _IO(DASD_IOCTL_LETTER,6) /* Resume IO on device */ #define BIODASDRESUME _IO(DASD_IOCTL_LETTER,7) +/* Abort all I/O on a device */ +#define BIODASDABORTIO _IO(DASD_IOCTL_LETTER, 240) +/* Allow I/O on a device */ +#define BIODASDALLOWIO _IO(DASD_IOCTL_LETTER, 241) /* retrieve API version number */ diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 54f4bb8060c1..17150a778984 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -38,9 +38,6 @@ */ #define DASD_CHANQ_MAX_SIZE 4 -#define DASD_SLEEPON_START_TAG (void *) 1 -#define DASD_SLEEPON_END_TAG (void *) 2 - /* * SECTION: exported variables of dasd.c */ @@ -2535,6 +2532,16 @@ static void __dasd_process_request_queue(struct dasd_block *block) __blk_end_request_all(req, -EIO); continue; } + if (test_bit(DASD_FLAG_ABORTALL, &basedev->flags) && + (basedev->features & DASD_FEATURE_FAILFAST || + blk_noretry_request(req))) { + DBF_DEV_EVENT(DBF_ERR, basedev, + "Rejecting failfast request %p", + req); + blk_start_request(req); + __blk_end_request_all(req, -ETIMEDOUT); + continue; + } cqr = basedev->discipline->build_cp(basedev, block, req); if (IS_ERR(cqr)) { if (PTR_ERR(cqr) == -EBUSY) diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index 2bd03f45a72e..690001af0d09 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -524,7 +524,10 @@ struct dasd_block { #define DASD_FLAG_SUSPENDED 9 /* The device was suspended */ #define DASD_FLAG_SAFE_OFFLINE 10 /* safe offline processing requested*/ #define DASD_FLAG_SAFE_OFFLINE_RUNNING 11 /* safe offline running */ +#define DASD_FLAG_ABORTALL 12 /* Abort all noretry requests */ +#define DASD_SLEEPON_START_TAG ((void *) 1) +#define DASD_SLEEPON_END_TAG ((void *) 2) void dasd_put_device_wake(struct dasd_device *); diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c index 8be1b51e9311..25a0f2f8b0b9 100644 --- a/drivers/s390/block/dasd_ioctl.c +++ b/drivers/s390/block/dasd_ioctl.c @@ -140,6 +140,59 @@ static int dasd_ioctl_resume(struct dasd_block *block) return 0; } +/* + * Abort all failfast I/O on a device. + */ +static int dasd_ioctl_abortio(struct dasd_block *block) +{ + unsigned long flags; + struct dasd_device *base; + struct dasd_ccw_req *cqr, *n; + + base = block->base; + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + if (test_and_set_bit(DASD_FLAG_ABORTALL, &base->flags)) + return 0; + DBF_DEV_EVENT(DBF_NOTICE, base, "%s", "abortall flag set"); + + spin_lock_irqsave(&block->request_queue_lock, flags); + spin_lock(&block->queue_lock); + list_for_each_entry_safe(cqr, n, &block->ccw_queue, blocklist) { + if (test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) && + cqr->callback_data && + cqr->callback_data != DASD_SLEEPON_START_TAG && + cqr->callback_data != DASD_SLEEPON_END_TAG) { + spin_unlock(&block->queue_lock); + blk_abort_request(cqr->callback_data); + spin_lock(&block->queue_lock); + } + } + spin_unlock(&block->queue_lock); + spin_unlock_irqrestore(&block->request_queue_lock, flags); + + dasd_schedule_block_bh(block); + return 0; +} + +/* + * Allow I/O on a device + */ +static int dasd_ioctl_allowio(struct dasd_block *block) +{ + struct dasd_device *base; + + base = block->base; + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + if (test_and_clear_bit(DASD_FLAG_ABORTALL, &base->flags)) + DBF_DEV_EVENT(DBF_NOTICE, base, "%s", "abortall flag unset"); + + return 0; +} + /* * performs formatting of _device_ according to _fdata_ * Note: The discipline's format_function is assumed to deliver formatting @@ -458,6 +511,12 @@ int dasd_ioctl(struct block_device *bdev, fmode_t mode, case BIODASDRESUME: rc = dasd_ioctl_resume(block); break; + case BIODASDABORTIO: + rc = dasd_ioctl_abortio(block); + break; + case BIODASDALLOWIO: + rc = dasd_ioctl_allowio(block); + break; case BIODASDFMT: rc = dasd_ioctl_format(bdev, argp); break; -- cgit v1.2.3 From 491205a8b45e3d9b594e1e7a997284f2e82f22e9 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 13 May 2013 20:35:37 -0500 Subject: rbd: Use min_t() to fix comparison of distinct pointer types warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drivers/block/rbd.c: In function ‘zero_pages’: drivers/block/rbd.c:1102: warning: comparison of distinct pointer types lacks a cast Remove the hackish casts and use min_t() to fix this. Signed-off-by: Geert Uytterhoeven Reviewed-by: Alex Elder --- drivers/block/rbd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index aff789d6fccd..b4f00e22743d 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1153,8 +1153,8 @@ static void zero_pages(struct page **pages, u64 offset, u64 end) unsigned long flags; void *kaddr; - page_offset = (size_t)(offset & ~PAGE_MASK); - length = min(PAGE_SIZE - page_offset, (size_t)(end - offset)); + page_offset = offset & ~PAGE_MASK; + length = min_t(size_t, PAGE_SIZE - page_offset, end - offset); local_irq_save(flags); kaddr = kmap_atomic(*page); memset(kaddr + page_offset, 0, length); -- cgit v1.2.3 From 4d1bf79aff7962ab0654d66127ebb6eec17460ab Mon Sep 17 00:00:00 2001 From: Jim Schutt Date: Wed, 15 May 2013 10:38:12 -0600 Subject: ceph: fix up comment for ceph_count_locks() as to which lock to hold Signed-off-by: Jim Schutt Reviewed-by: Alex Elder --- fs/ceph/locks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ceph/locks.c b/fs/ceph/locks.c index ebbf680378e2..89788515a63d 100644 --- a/fs/ceph/locks.c +++ b/fs/ceph/locks.c @@ -169,7 +169,7 @@ int ceph_flock(struct file *file, int cmd, struct file_lock *fl) } /** - * Must be called with BKL already held. Fills in the passed + * Must be called with lock_flocks() already held. Fills in the passed * counter variables, so you can prepare pagelist metadata before calling * ceph_encode_locks. */ -- cgit v1.2.3 From 912c317d4600b81664ad8f3d3ba6c1f2ff4b49c2 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 13 May 2013 20:35:38 -0500 Subject: rbd: drop original request earlier for existence check The reference to the original request dropped at the end of rbd_img_obj_exists_callback() corresponds to the reference taken in rbd_img_obj_exists_submit() to account for the stat request referring to it. Move the put of that reference up right after clearing that pointer to make its purpose more obvious. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index b4f00e22743d..291802c52c65 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -2535,6 +2535,7 @@ static void rbd_img_obj_exists_callback(struct rbd_obj_request *obj_request) */ orig_request = obj_request->obj_request; obj_request->obj_request = NULL; + rbd_obj_request_put(orig_request); rbd_assert(orig_request); rbd_assert(orig_request->img_request); @@ -2555,7 +2556,6 @@ static void rbd_img_obj_exists_callback(struct rbd_obj_request *obj_request) if (!rbd_dev->parent_overlap) { struct ceph_osd_client *osdc; - rbd_obj_request_put(orig_request); osdc = &rbd_dev->rbd_client->client->osdc; result = rbd_obj_request_submit(osdc, orig_request); if (!result) @@ -2585,7 +2585,6 @@ static void rbd_img_obj_exists_callback(struct rbd_obj_request *obj_request) out: if (orig_request->result) rbd_obj_request_complete(orig_request); - rbd_obj_request_put(orig_request); } static int rbd_img_obj_exists_submit(struct rbd_obj_request *obj_request) -- cgit v1.2.3 From c213b50b7dcbf06abcfbf1e4eee5b76586718bd9 Mon Sep 17 00:00:00 2001 From: Emil Goode Date: Tue, 28 May 2013 16:59:00 +0200 Subject: ceph: improve error handling in ceph_mdsmap_decode This patch makes the following improvements to the error handling in the ceph_mdsmap_decode function: - Add a NULL check for return value from kcalloc - Make use of the variable err Signed-off-by: Emil Goode Signed-off-by: Sage Weil --- fs/ceph/mdsmap.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/ceph/mdsmap.c b/fs/ceph/mdsmap.c index 9278dec9e940..d4d38977dcbb 100644 --- a/fs/ceph/mdsmap.c +++ b/fs/ceph/mdsmap.c @@ -138,6 +138,8 @@ struct ceph_mdsmap *ceph_mdsmap_decode(void **p, void *end) m->m_info[mds].export_targets = kcalloc(num_export_targets, sizeof(u32), GFP_NOFS); + if (m->m_info[mds].export_targets == NULL) + goto badmem; for (j = 0; j < num_export_targets; j++) m->m_info[mds].export_targets[j] = ceph_decode_32(&pexport_targets); @@ -170,7 +172,7 @@ bad: DUMP_PREFIX_OFFSET, 16, 1, start, end - start, true); ceph_mdsmap_destroy(m); - return ERR_PTR(-EINVAL); + return ERR_PTR(err); } void ceph_mdsmap_destroy(struct ceph_mdsmap *m) -- cgit v1.2.3 From 6af8652849a15e407b458a271ef9154e472f6dd4 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 29 May 2013 06:46:56 -0500 Subject: ceph: tidy ceph_mdsmap_decode() a little I introduced a new temporary variable "info" instead of "m->m_info[mds]". Also I reversed the if condition and pulled everything in one indent level. Signed-off-by: Dan Carpenter Reviewed-by: Alex Elder --- fs/ceph/mdsmap.c | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/fs/ceph/mdsmap.c b/fs/ceph/mdsmap.c index d4d38977dcbb..132b64eeecd4 100644 --- a/fs/ceph/mdsmap.c +++ b/fs/ceph/mdsmap.c @@ -92,6 +92,7 @@ struct ceph_mdsmap *ceph_mdsmap_decode(void **p, void *end) u32 num_export_targets; void *pexport_targets = NULL; struct ceph_timespec laggy_since; + struct ceph_mds_info *info; ceph_decode_need(p, end, sizeof(u64)*2 + 1 + sizeof(u32), bad); global_id = ceph_decode_64(p); @@ -126,26 +127,27 @@ struct ceph_mdsmap *ceph_mdsmap_decode(void **p, void *end) i+1, n, global_id, mds, inc, ceph_pr_addr(&addr.in_addr), ceph_mds_state_name(state)); - if (mds >= 0 && mds < m->m_max_mds && state > 0) { - m->m_info[mds].global_id = global_id; - m->m_info[mds].state = state; - m->m_info[mds].addr = addr; - m->m_info[mds].laggy = - (laggy_since.tv_sec != 0 || - laggy_since.tv_nsec != 0); - m->m_info[mds].num_export_targets = num_export_targets; - if (num_export_targets) { - m->m_info[mds].export_targets = - kcalloc(num_export_targets, sizeof(u32), - GFP_NOFS); - if (m->m_info[mds].export_targets == NULL) - goto badmem; - for (j = 0; j < num_export_targets; j++) - m->m_info[mds].export_targets[j] = - ceph_decode_32(&pexport_targets); - } else { - m->m_info[mds].export_targets = NULL; - } + + if (mds < 0 || mds >= m->m_max_mds || state <= 0) + continue; + + info = &m->m_info[mds]; + info->global_id = global_id; + info->state = state; + info->addr = addr; + info->laggy = (laggy_since.tv_sec != 0 || + laggy_since.tv_nsec != 0); + info->num_export_targets = num_export_targets; + if (num_export_targets) { + info->export_targets = kcalloc(num_export_targets, + sizeof(u32), GFP_NOFS); + if (info->export_targets == NULL) + goto badmem; + for (j = 0; j < num_export_targets; j++) + info->export_targets[j] = + ceph_decode_32(&pexport_targets); + } else { + info->export_targets = NULL; } } -- cgit v1.2.3 From 69e9aa99989c68763373d54076d82405d1000f46 Mon Sep 17 00:00:00 2001 From: Manish Badarkhe Date: Mon, 1 Jul 2013 11:35:19 -0700 Subject: ARM: davinci: da850-evm: remove vref from touchscreen platform data The vref field was not used by the driver and was removed from the platform data structure. Signed-off-by: Manish Badarkhe Signed-off-by: Dmitry Torokhov --- arch/arm/mach-davinci/board-da850-evm.c | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index c2dfe06563df..1b6775ab3b5a 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -991,7 +991,6 @@ static struct regulator_init_data tps65070_regulator_data[] = { static struct touchscreen_init_data tps6507x_touchscreen_data = { .poll_period = 30, /* ms between touch samples */ .min_pressure = 0x30, /* minimum pressure to trigger touch */ - .vref = 0, /* turn off vref when not using A/D */ .vendor = 0, /* /sys/class/input/input?/id/vendor */ .product = 65070, /* /sys/class/input/input?/id/product */ .version = 0x100, /* /sys/class/input/input?/id/version */ -- cgit v1.2.3 From 5705b8aca5a80141de5637ff0e23b31b26f2c5bf Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 15 Apr 2013 11:11:00 -0700 Subject: Input: tps6507x-ts - convert to polled input device infrastructure There is no need to roll our own polling scheme when we already have one implemented by the core. Tested-by: Manish Badarkhe Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/tps6507x-ts.c | 126 ++++++++++++-------------------- include/linux/mfd/tps6507x.h | 1 - 2 files changed, 48 insertions(+), 79 deletions(-) diff --git a/drivers/input/touchscreen/tps6507x-ts.c b/drivers/input/touchscreen/tps6507x-ts.c index d3c2967f86fb..94cde2cb1491 100644 --- a/drivers/input/touchscreen/tps6507x-ts.c +++ b/drivers/input/touchscreen/tps6507x-ts.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -38,14 +39,12 @@ struct ts_event { }; struct tps6507x_ts { - struct input_dev *input_dev; struct device *dev; + struct input_polled_dev *poll_dev; + struct tps6507x_dev *mfd; char phys[32]; - struct delayed_work work; struct ts_event tc; - struct tps6507x_dev *mfd; u16 min_pressure; - unsigned long poll_period; /* ms */ bool pendown; }; @@ -156,13 +155,11 @@ static s32 tps6507x_adc_standby(struct tps6507x_ts *tsc) return ret; } -static void tps6507x_ts_handler(struct work_struct *work) +static void tps6507x_ts_poll(struct input_polled_dev *poll_dev) { - struct tps6507x_ts *tsc = container_of(work, - struct tps6507x_ts, work.work); - struct input_dev *input_dev = tsc->input_dev; + struct tps6507x_ts *tsc = poll_dev->private; + struct input_dev *input_dev = poll_dev->input; bool pendown; - int schd; s32 ret; ret = tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_PRESSURE, @@ -206,62 +203,65 @@ static void tps6507x_ts_handler(struct work_struct *work) } done: - /* always poll if not using interrupts */ - schd = schedule_delayed_work(&tsc->work, - msecs_to_jiffies(tsc->poll_period)); - if (!schd) - dev_err(tsc->dev, "re-schedule failed"); - - ret = tps6507x_adc_standby(tsc); + tps6507x_adc_standby(tsc); } static int tps6507x_ts_probe(struct platform_device *pdev) { - int error; - struct tps6507x_ts *tsc; struct tps6507x_dev *tps6507x_dev = dev_get_drvdata(pdev->dev.parent); - struct touchscreen_init_data *init_data; + const struct tps6507x_board *tps_board; + const struct touchscreen_init_data *init_data; + struct tps6507x_ts *tsc; + struct input_polled_dev *poll_dev; struct input_dev *input_dev; - struct tps6507x_board *tps_board; - int schd; + int error; - /** + /* * tps_board points to pmic related constants * coming from the board-evm file. */ - - tps_board = (struct tps6507x_board *)tps6507x_dev->dev->platform_data; - + tps_board = dev_get_platdata(tps6507x_dev->dev); if (!tps_board) { dev_err(tps6507x_dev->dev, "Could not find tps6507x platform data\n"); - return -EIO; + return -ENODEV; } - /** + /* * init_data points to array of regulator_init structures * coming from the board-evm file. */ - init_data = tps_board->tps6507x_ts_init_data; tsc = kzalloc(sizeof(struct tps6507x_ts), GFP_KERNEL); if (!tsc) { dev_err(tps6507x_dev->dev, "failed to allocate driver data\n"); - error = -ENOMEM; - goto err0; + return -ENOMEM; } - tps6507x_dev->ts = tsc; tsc->mfd = tps6507x_dev; tsc->dev = tps6507x_dev->dev; - input_dev = input_allocate_device(); - if (!input_dev) { - dev_err(tsc->dev, "Failed to allocate input device.\n"); + tsc->min_pressure = init_data ? + init_data->min_pressure : TPS_DEFAULT_MIN_PRESSURE; + + snprintf(tsc->phys, sizeof(tsc->phys), + "%s/input0", dev_name(tsc->dev)); + + poll_dev = input_allocate_polled_device(); + if (!poll_dev) { + dev_err(tsc->dev, "Failed to allocate polled input device.\n"); error = -ENOMEM; - goto err1; + goto err_free_mem; } + tsc->poll_dev = poll_dev; + + poll_dev->private = tsc; + poll_dev->poll = tps6507x_ts_poll; + poll_dev->poll_interval = init_data ? + init_data->poll_period : TSC_DEFAULT_POLL_PERIOD; + + input_dev = poll_dev->input; input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); @@ -270,72 +270,42 @@ static int tps6507x_ts_probe(struct platform_device *pdev) input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_10BIT, 0, 0); input_dev->name = "TPS6507x Touchscreen"; - input_dev->id.bustype = BUS_I2C; - input_dev->dev.parent = tsc->dev; - - snprintf(tsc->phys, sizeof(tsc->phys), - "%s/input0", dev_name(tsc->dev)); input_dev->phys = tsc->phys; - - dev_dbg(tsc->dev, "device: %s\n", input_dev->phys); - - input_set_drvdata(input_dev, tsc); - - tsc->input_dev = input_dev; - - INIT_DELAYED_WORK(&tsc->work, tps6507x_ts_handler); - + input_dev->dev.parent = tsc->dev; + input_dev->id.bustype = BUS_I2C; if (init_data) { - tsc->poll_period = init_data->poll_period; - tsc->min_pressure = init_data->min_pressure; input_dev->id.vendor = init_data->vendor; input_dev->id.product = init_data->product; input_dev->id.version = init_data->version; - } else { - tsc->poll_period = TSC_DEFAULT_POLL_PERIOD; - tsc->min_pressure = TPS_DEFAULT_MIN_PRESSURE; } error = tps6507x_adc_standby(tsc); if (error) - goto err2; + goto err_free_polled_dev; - error = input_register_device(input_dev); + error = input_register_polled_device(poll_dev); if (error) - goto err2; - - schd = schedule_delayed_work(&tsc->work, - msecs_to_jiffies(tsc->poll_period)); + goto err_free_polled_dev; - if (!schd) { - dev_err(tsc->dev, "schedule failed"); - goto err2; - } - platform_set_drvdata(pdev, tps6507x_dev); + platform_set_drvdata(pdev, tsc); return 0; -err2: - cancel_delayed_work_sync(&tsc->work); - input_free_device(input_dev); -err1: +err_free_polled_dev: + input_free_polled_device(poll_dev); +err_free_mem: kfree(tsc); - tps6507x_dev->ts = NULL; -err0: return error; } static int tps6507x_ts_remove(struct platform_device *pdev) { - struct tps6507x_dev *tps6507x_dev = platform_get_drvdata(pdev); - struct tps6507x_ts *tsc = tps6507x_dev->ts; - struct input_dev *input_dev = tsc->input_dev; - - cancel_delayed_work_sync(&tsc->work); + struct tps6507x_ts *tsc = platform_get_drvdata(pdev); + struct input_polled_dev *poll_dev = tsc->poll_dev; - input_unregister_device(input_dev); + input_unregister_polled_device(poll_dev); + input_free_polled_device(poll_dev); - tps6507x_dev->ts = NULL; kfree(tsc); return 0; diff --git a/include/linux/mfd/tps6507x.h b/include/linux/mfd/tps6507x.h index c923e4864f55..c2ae56933539 100644 --- a/include/linux/mfd/tps6507x.h +++ b/include/linux/mfd/tps6507x.h @@ -163,7 +163,6 @@ struct tps6507x_dev { /* Client devices */ struct tps6507x_pmic *pmic; - struct tps6507x_ts *ts; }; #endif /* __LINUX_MFD_TPS6507X_H */ -- cgit v1.2.3 From aa71d830c4467801105c2d60c7b8676dee92fa40 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 28 Jun 2013 12:55:48 -0400 Subject: drm/radeon: remove sumo dpm/uvd bringup leftovers Function doesn't do anything useful. Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/sumo_dpm.c | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/drivers/gpu/drm/radeon/sumo_dpm.c b/drivers/gpu/drm/radeon/sumo_dpm.c index dbad293bfed5..0c3d7526cda5 100644 --- a/drivers/gpu/drm/radeon/sumo_dpm.c +++ b/drivers/gpu/drm/radeon/sumo_dpm.c @@ -1133,22 +1133,6 @@ static void sumo_cleanup_asic(struct radeon_device *rdev) sumo_take_smu_control(rdev, false); } -static void sumo_uvd_init(struct radeon_device *rdev) -{ - u32 tmp; - - tmp = RREG32(CG_VCLK_CNTL); - tmp &= ~VCLK_DIR_CNTL_EN; - WREG32(CG_VCLK_CNTL, tmp); - - tmp = RREG32(CG_DCLK_CNTL); - tmp &= ~DCLK_DIR_CNTL_EN; - WREG32(CG_DCLK_CNTL, tmp); - - /* 100 Mhz */ - radeon_set_uvd_clocks(rdev, 10000, 10000); -} - static int sumo_set_thermal_temperature_range(struct radeon_device *rdev, int min_temp, int max_temp) { @@ -1348,7 +1332,6 @@ void sumo_dpm_setup_asic(struct radeon_device *rdev) sumo_program_acpi_power_level(rdev); sumo_enable_acpi_pm(rdev); sumo_take_smu_control(rdev, true); - sumo_uvd_init(rdev); } void sumo_dpm_display_configuration_changed(struct radeon_device *rdev) -- cgit v1.2.3 From 5c7524bf068ebe077254a6c37fcfa27e6cb6a1f3 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 1 Jul 2013 13:32:49 -0400 Subject: drm/radeon/atom: fix endian bug in radeon_atom_init_mc_reg_table() Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/radeon_atombios.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index a8296e0f8543..dfcf74a89014 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -3732,7 +3732,7 @@ int radeon_atom_init_mc_reg_table(struct radeon_device *rdev, } num_ranges++; } - reg_data += reg_block->usRegDataBlkSize; + reg_data += le16_to_cpu(reg_block->usRegDataBlkSize); } if (*(u32 *)reg_data != END_OF_REG_DATA_BLOCK) return -EINVAL; -- cgit v1.2.3 From 4da18e26e0cc2387597ff57ac33df1bc6369fbed Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 1 Jul 2013 13:33:53 -0400 Subject: drm/radeon: fix typo in radeon_atom_init_mc_reg_table() Bad pointer math. Fixes hangs in state transitions with BTC+ asics. Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/radeon_atombios.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index dfcf74a89014..70d8687e60e0 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -3732,7 +3732,8 @@ int radeon_atom_init_mc_reg_table(struct radeon_device *rdev, } num_ranges++; } - reg_data += le16_to_cpu(reg_block->usRegDataBlkSize); + reg_data = (ATOM_MEMORY_SETTING_DATA_BLOCK *) + ((u8 *)reg_data + le16_to_cpu(reg_block->usRegDataBlkSize)); } if (*(u32 *)reg_data != END_OF_REG_DATA_BLOCK) return -EINVAL; -- cgit v1.2.3 From 4b5c006ef2bd70ca8c06d74694e0e56719f15d91 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 1 Jul 2013 16:04:02 -0400 Subject: drm/radeon/dpm: re-enable state transitions for BTC Was disabled due to stability issues on certain boards caused by the a bug in the parsing of the atom mc reg tables. That's fixed now so re-enable. Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/btc_dpm.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/gpu/drm/radeon/btc_dpm.c b/drivers/gpu/drm/radeon/btc_dpm.c index bab018583417..f072660c7665 100644 --- a/drivers/gpu/drm/radeon/btc_dpm.c +++ b/drivers/gpu/drm/radeon/btc_dpm.c @@ -2326,14 +2326,11 @@ int btc_dpm_set_power_state(struct radeon_device *rdev) return ret; } -#if 0 - /* XXX */ ret = rv770_unrestrict_performance_levels_after_switch(rdev); if (ret) { DRM_ERROR("rv770_unrestrict_performance_levels_after_switch failed\n"); return ret; } -#endif return 0; } -- cgit v1.2.3 From 7ad8d0687bb5030c3328bc7229a3183ce179ab25 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 1 Jul 2013 16:07:18 -0400 Subject: drm/radeon/dpm: re-enable state transitions for Cayman Was disabled due to stability issues on certain boards caused by the a bug in the parsing of the atom mc reg tables. That's fixed now so re-enable. Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/ni_dpm.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/gpu/drm/radeon/ni_dpm.c b/drivers/gpu/drm/radeon/ni_dpm.c index 777d17e61312..4712851808a3 100644 --- a/drivers/gpu/drm/radeon/ni_dpm.c +++ b/drivers/gpu/drm/radeon/ni_dpm.c @@ -1036,7 +1036,6 @@ static int ni_restrict_performance_levels_before_switch(struct radeon_device *rd 0 : -EINVAL; } -#if 0 static int ni_unrestrict_performance_levels_after_switch(struct radeon_device *rdev) { if (ni_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetForcedLevels, 0) != PPSMC_Result_OK) @@ -1045,7 +1044,6 @@ static int ni_unrestrict_performance_levels_after_switch(struct radeon_device *r return (ni_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetEnabledLevels, 0) == PPSMC_Result_OK) ? 0 : -EINVAL; } -#endif static void ni_stop_smc(struct radeon_device *rdev) { @@ -3832,14 +3830,11 @@ int ni_dpm_set_power_state(struct radeon_device *rdev) return ret; } -#if 0 - /* XXX */ ret = ni_unrestrict_performance_levels_after_switch(rdev); if (ret) { DRM_ERROR("ni_unrestrict_performance_levels_after_switch failed\n"); return ret; } -#endif return 0; } -- cgit v1.2.3 From 1316b79256062f7a2e66f0833dcb9728ec748805 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 28 Jun 2013 09:28:39 -0400 Subject: drm/radeon/dpm: add infrastructure to support debugfs info This lays the frameworks to report realtime power level feedback. Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/radeon.h | 2 ++ drivers/gpu/drm/radeon/radeon_pm.c | 40 +++++++++++++++++++++++++------------- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 7e3fef4e6938..f51807f04f65 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -1667,6 +1667,7 @@ struct radeon_asic { u32 (*get_sclk)(struct radeon_device *rdev, bool low); u32 (*get_mclk)(struct radeon_device *rdev, bool low); void (*print_power_state)(struct radeon_device *rdev, struct radeon_ps *ps); + void (*debugfs_print_current_performance_level)(struct radeon_device *rdev, struct seq_file *m); } dpm; /* pageflipping */ struct { @@ -2433,6 +2434,7 @@ void radeon_ring_write(struct radeon_ring *ring, uint32_t v); #define radeon_dpm_get_sclk(rdev, l) rdev->asic->dpm.get_sclk((rdev), (l)) #define radeon_dpm_get_mclk(rdev, l) rdev->asic->dpm.get_mclk((rdev), (l)) #define radeon_dpm_print_power_state(rdev, ps) rdev->asic->dpm.print_power_state((rdev), (ps)) +#define radeon_dpm_debugfs_print_current_performance_level(rdev, m) rdev->asic->dpm.debugfs_print_current_performance_level((rdev), (m)) /* Common functions */ /* AGP */ diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 9737baeb711d..075f2fa56897 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -1062,6 +1062,11 @@ static int radeon_pm_init_dpm(struct radeon_device *rdev) ret = device_create_file(rdev->dev, &dev_attr_power_method); if (ret) DRM_ERROR("failed to create device file for power method\n"); + + if (radeon_debugfs_pm_init(rdev)) { + DRM_ERROR("Failed to register debugfs file for dpm!\n"); + } + DRM_INFO("radeon: dpm initialized\n"); } @@ -1389,19 +1394,28 @@ static int radeon_debugfs_pm_info(struct seq_file *m, void *data) struct drm_device *dev = node->minor->dev; struct radeon_device *rdev = dev->dev_private; - seq_printf(m, "default engine clock: %u0 kHz\n", rdev->pm.default_sclk); - /* radeon_get_engine_clock is not reliable on APUs so just print the current clock */ - if ((rdev->family >= CHIP_PALM) && (rdev->flags & RADEON_IS_IGP)) - seq_printf(m, "current engine clock: %u0 kHz\n", rdev->pm.current_sclk); - else - seq_printf(m, "current engine clock: %u0 kHz\n", radeon_get_engine_clock(rdev)); - seq_printf(m, "default memory clock: %u0 kHz\n", rdev->pm.default_mclk); - if (rdev->asic->pm.get_memory_clock) - seq_printf(m, "current memory clock: %u0 kHz\n", radeon_get_memory_clock(rdev)); - if (rdev->pm.current_vddc) - seq_printf(m, "voltage: %u mV\n", rdev->pm.current_vddc); - if (rdev->asic->pm.get_pcie_lanes) - seq_printf(m, "PCIE lanes: %d\n", radeon_get_pcie_lanes(rdev)); + if (rdev->pm.dpm_enabled) { + mutex_lock(&rdev->pm.mutex); + if (rdev->asic->dpm.debugfs_print_current_performance_level) + radeon_dpm_debugfs_print_current_performance_level(rdev, m); + else + seq_printf(m, "Unsupported\n"); + mutex_unlock(&rdev->pm.mutex); + } else { + seq_printf(m, "default engine clock: %u0 kHz\n", rdev->pm.default_sclk); + /* radeon_get_engine_clock is not reliable on APUs so just print the current clock */ + if ((rdev->family >= CHIP_PALM) && (rdev->flags & RADEON_IS_IGP)) + seq_printf(m, "current engine clock: %u0 kHz\n", rdev->pm.current_sclk); + else + seq_printf(m, "current engine clock: %u0 kHz\n", radeon_get_engine_clock(rdev)); + seq_printf(m, "default memory clock: %u0 kHz\n", rdev->pm.default_mclk); + if (rdev->asic->pm.get_memory_clock) + seq_printf(m, "current memory clock: %u0 kHz\n", radeon_get_memory_clock(rdev)); + if (rdev->pm.current_vddc) + seq_printf(m, "voltage: %u mV\n", rdev->pm.current_vddc); + if (rdev->asic->pm.get_pcie_lanes) + seq_printf(m, "PCIE lanes: %d\n", radeon_get_pcie_lanes(rdev)); + } return 0; } -- cgit v1.2.3 From 242916a5ee55a0ed49a2c74ee4d835fe9d8ca463 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 28 Jun 2013 14:20:53 -0400 Subject: drm/radeon/dpm: add debugfs support for rv6xx This allows you to look at the current DPM state via debugfs. Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/radeon_asic.c | 1 + drivers/gpu/drm/radeon/radeon_asic.h | 2 ++ drivers/gpu/drm/radeon/rv6xx_dpm.c | 25 +++++++++++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index c3df589715a0..58d4cc5c7c97 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -1160,6 +1160,7 @@ static struct radeon_asic rv6xx_asic = { .get_sclk = &rv6xx_dpm_get_sclk, .get_mclk = &rv6xx_dpm_get_mclk, .print_power_state = &rv6xx_dpm_print_power_state, + .debugfs_print_current_performance_level = &rv6xx_dpm_debugfs_print_current_performance_level, }, .pflip = { .pre_page_flip = &rs600_pre_page_flip, diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 2497d0a02de5..6b1ede11ed20 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -416,6 +416,8 @@ u32 rv6xx_dpm_get_sclk(struct radeon_device *rdev, bool low); u32 rv6xx_dpm_get_mclk(struct radeon_device *rdev, bool low); void rv6xx_dpm_print_power_state(struct radeon_device *rdev, struct radeon_ps *ps); +void rv6xx_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev, + struct seq_file *m); /* rs780 dpm */ int rs780_dpm_init(struct radeon_device *rdev); int rs780_dpm_enable(struct radeon_device *rdev); diff --git a/drivers/gpu/drm/radeon/rv6xx_dpm.c b/drivers/gpu/drm/radeon/rv6xx_dpm.c index 0e8b7d9b954b..33705c5c8369 100644 --- a/drivers/gpu/drm/radeon/rv6xx_dpm.c +++ b/drivers/gpu/drm/radeon/rv6xx_dpm.c @@ -2027,6 +2027,31 @@ void rv6xx_dpm_print_power_state(struct radeon_device *rdev, r600_dpm_print_ps_status(rdev, rps); } +void rv6xx_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev, + struct seq_file *m) +{ + struct radeon_ps *rps = rdev->pm.dpm.current_ps; + struct rv6xx_ps *ps = rv6xx_get_ps(rps); + struct rv6xx_pl *pl; + u32 current_index = + (RREG32(TARGET_AND_CURRENT_PROFILE_INDEX) & CURRENT_PROFILE_INDEX_MASK) >> + CURRENT_PROFILE_INDEX_SHIFT; + + if (current_index > 2) { + seq_printf(m, "invalid dpm profile %d\n", current_index); + } else { + if (current_index == 0) + pl = &ps->low; + else if (current_index == 1) + pl = &ps->medium; + else /* current_index == 2 */ + pl = &ps->high; + seq_printf(m, "uvd vclk: %d dclk: %d\n", rps->vclk, rps->dclk); + seq_printf(m, "power level %d sclk: %u mclk: %u vddc: %u\n", + current_index, pl->sclk, pl->mclk, pl->vddc); + } +} + void rv6xx_dpm_fini(struct radeon_device *rdev) { int i; -- cgit v1.2.3 From bd210d11cd37bee3da729e56288b5b4a038f88bd Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 28 Jun 2013 10:06:26 -0400 Subject: drm/radeon/dpm: add debugfs support for 7xx/evergreen/btc This allows you to look at the current DPM state via debugfs. Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/radeon_asic.c | 3 +++ drivers/gpu/drm/radeon/radeon_asic.h | 2 ++ drivers/gpu/drm/radeon/rv770_dpm.c | 30 ++++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/rv770d.h | 4 ++++ 4 files changed, 39 insertions(+) diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index 58d4cc5c7c97..a2a34f86434b 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -1392,6 +1392,7 @@ static struct radeon_asic rv770_asic = { .get_sclk = &rv770_dpm_get_sclk, .get_mclk = &rv770_dpm_get_mclk, .print_power_state = &rv770_dpm_print_power_state, + .debugfs_print_current_performance_level = &rv770_dpm_debugfs_print_current_performance_level, }, .pflip = { .pre_page_flip = &rs600_pre_page_flip, @@ -1514,6 +1515,7 @@ static struct radeon_asic evergreen_asic = { .get_sclk = &rv770_dpm_get_sclk, .get_mclk = &rv770_dpm_get_mclk, .print_power_state = &rv770_dpm_print_power_state, + .debugfs_print_current_performance_level = &rv770_dpm_debugfs_print_current_performance_level, }, .pflip = { .pre_page_flip = &evergreen_pre_page_flip, @@ -1758,6 +1760,7 @@ static struct radeon_asic btc_asic = { .get_sclk = &btc_dpm_get_sclk, .get_mclk = &btc_dpm_get_mclk, .print_power_state = &rv770_dpm_print_power_state, + .debugfs_print_current_performance_level = &rv770_dpm_debugfs_print_current_performance_level, }, .pflip = { .pre_page_flip = &evergreen_pre_page_flip, diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 6b1ede11ed20..39f5d96d7c49 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -476,6 +476,8 @@ u32 rv770_dpm_get_sclk(struct radeon_device *rdev, bool low); u32 rv770_dpm_get_mclk(struct radeon_device *rdev, bool low); void rv770_dpm_print_power_state(struct radeon_device *rdev, struct radeon_ps *ps); +void rv770_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev, + struct seq_file *m); /* * evergreen diff --git a/drivers/gpu/drm/radeon/rv770_dpm.c b/drivers/gpu/drm/radeon/rv770_dpm.c index 7f6fa6221234..2436b5c7e66e 100644 --- a/drivers/gpu/drm/radeon/rv770_dpm.c +++ b/drivers/gpu/drm/radeon/rv770_dpm.c @@ -2430,6 +2430,36 @@ void rv770_dpm_print_power_state(struct radeon_device *rdev, r600_dpm_print_ps_status(rdev, rps); } +void rv770_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev, + struct seq_file *m) +{ + struct radeon_ps *rps = rdev->pm.dpm.current_ps; + struct rv7xx_ps *ps = rv770_get_ps(rps); + struct rv7xx_pl *pl; + u32 current_index = + (RREG32(TARGET_AND_CURRENT_PROFILE_INDEX) & CURRENT_PROFILE_INDEX_MASK) >> + CURRENT_PROFILE_INDEX_SHIFT; + + if (current_index > 2) { + seq_printf(m, "invalid dpm profile %d\n", current_index); + } else { + if (current_index == 0) + pl = &ps->low; + else if (current_index == 1) + pl = &ps->medium; + else /* current_index == 2 */ + pl = &ps->high; + seq_printf(m, "uvd vclk: %d dclk: %d\n", rps->vclk, rps->dclk); + if (rdev->family >= CHIP_CEDAR) { + seq_printf(m, "power level %d sclk: %u mclk: %u vddc: %u vddci: %u\n", + current_index, pl->sclk, pl->mclk, pl->vddc, pl->vddci); + } else { + seq_printf(m, "power level %d sclk: %u mclk: %u vddc: %u\n", + current_index, pl->sclk, pl->mclk, pl->vddc); + } + } +} + void rv770_dpm_fini(struct radeon_device *rdev) { int i; diff --git a/drivers/gpu/drm/radeon/rv770d.h b/drivers/gpu/drm/radeon/rv770d.h index 784eeaf315c3..6bef2b7d601b 100644 --- a/drivers/gpu/drm/radeon/rv770d.h +++ b/drivers/gpu/drm/radeon/rv770d.h @@ -207,6 +207,10 @@ # define MUX_TCLK_TO_XCLK (1 << 8) # define XTALIN_DIVIDE (1 << 9) +#define TARGET_AND_CURRENT_PROFILE_INDEX 0x66c +# define CURRENT_PROFILE_INDEX_MASK (0xf << 4) +# define CURRENT_PROFILE_INDEX_SHIFT 4 + #define S0_VID_LOWER_SMIO_CNTL 0x678 #define S1_VID_LOWER_SMIO_CNTL 0x67c #define S2_VID_LOWER_SMIO_CNTL 0x680 -- cgit v1.2.3 From fb70160c5f2ae1b0648801f39138b25990f7ae18 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 28 Jun 2013 10:47:56 -0400 Subject: drm/radeon/dpm: add debugfs support for ON/LN This allows you to look at the current DPM state via debugfs. Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/radeon_asic.c | 1 + drivers/gpu/drm/radeon/radeon_asic.h | 2 ++ drivers/gpu/drm/radeon/sumo_dpm.c | 28 ++++++++++++++++++++++++++++ 3 files changed, 31 insertions(+) diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index a2a34f86434b..6d4304cce83f 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -1638,6 +1638,7 @@ static struct radeon_asic sumo_asic = { .get_sclk = &sumo_dpm_get_sclk, .get_mclk = &sumo_dpm_get_mclk, .print_power_state = &sumo_dpm_print_power_state, + .debugfs_print_current_performance_level = &sumo_dpm_debugfs_print_current_performance_level, }, .pflip = { .pre_page_flip = &evergreen_pre_page_flip, diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 39f5d96d7c49..958d30f33066 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -565,6 +565,8 @@ u32 sumo_dpm_get_sclk(struct radeon_device *rdev, bool low); u32 sumo_dpm_get_mclk(struct radeon_device *rdev, bool low); void sumo_dpm_print_power_state(struct radeon_device *rdev, struct radeon_ps *ps); +void sumo_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev, + struct seq_file *m); /* * cayman diff --git a/drivers/gpu/drm/radeon/sumo_dpm.c b/drivers/gpu/drm/radeon/sumo_dpm.c index 0c3d7526cda5..68fefb916582 100644 --- a/drivers/gpu/drm/radeon/sumo_dpm.c +++ b/drivers/gpu/drm/radeon/sumo_dpm.c @@ -1752,6 +1752,34 @@ void sumo_dpm_print_power_state(struct radeon_device *rdev, r600_dpm_print_ps_status(rdev, rps); } +void sumo_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev, + struct seq_file *m) +{ + struct sumo_power_info *pi = sumo_get_pi(rdev); + struct radeon_ps *rps = rdev->pm.dpm.current_ps; + struct sumo_ps *ps = sumo_get_ps(rps); + struct sumo_pl *pl; + u32 current_index = + (RREG32(TARGET_AND_CURRENT_PROFILE_INDEX) & CURR_INDEX_MASK) >> + CURR_INDEX_SHIFT; + + if (current_index == BOOST_DPM_LEVEL) { + pl = &pi->boost_pl; + seq_printf(m, "uvd vclk: %d dclk: %d\n", rps->vclk, rps->dclk); + seq_printf(m, "power level %d sclk: %u vddc: %u\n", + current_index, pl->sclk, + sumo_convert_voltage_index_to_value(rdev, pl->vddc_index)); + } else if (current_index >= ps->num_levels) { + seq_printf(m, "invalid dpm profile %d\n", current_index); + } else { + pl = &ps->levels[current_index]; + seq_printf(m, "uvd vclk: %d dclk: %d\n", rps->vclk, rps->dclk); + seq_printf(m, "power level %d sclk: %u vddc: %u\n", + current_index, pl->sclk, + sumo_convert_voltage_index_to_value(rdev, pl->vddc_index)); + } +} + void sumo_dpm_fini(struct radeon_device *rdev) { int i; -- cgit v1.2.3 From 490ab9314bb7f70af227fd905571d6cfc10688fb Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 28 Jun 2013 12:01:38 -0400 Subject: drm/radeon/dpm: add debugfs support for TN This allows you to look at the current DPM state via debugfs. Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/radeon_asic.c | 1 + drivers/gpu/drm/radeon/radeon_asic.h | 2 ++ drivers/gpu/drm/radeon/trinity_dpm.c | 21 +++++++++++++++++++++ 3 files changed, 24 insertions(+) diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index 6d4304cce83f..691654740906 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -2108,6 +2108,7 @@ static struct radeon_asic trinity_asic = { .get_sclk = &trinity_dpm_get_sclk, .get_mclk = &trinity_dpm_get_mclk, .print_power_state = &trinity_dpm_print_power_state, + .debugfs_print_current_performance_level = &trinity_dpm_debugfs_print_current_performance_level, }, .pflip = { .pre_page_flip = &evergreen_pre_page_flip, diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 958d30f33066..5904d897586d 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -626,6 +626,8 @@ u32 trinity_dpm_get_sclk(struct radeon_device *rdev, bool low); u32 trinity_dpm_get_mclk(struct radeon_device *rdev, bool low); void trinity_dpm_print_power_state(struct radeon_device *rdev, struct radeon_ps *ps); +void trinity_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev, + struct seq_file *m); /* DCE6 - SI */ void dce6_bandwidth_update(struct radeon_device *rdev); diff --git a/drivers/gpu/drm/radeon/trinity_dpm.c b/drivers/gpu/drm/radeon/trinity_dpm.c index fce825e112ff..502d9153c4d5 100644 --- a/drivers/gpu/drm/radeon/trinity_dpm.c +++ b/drivers/gpu/drm/radeon/trinity_dpm.c @@ -1855,6 +1855,27 @@ void trinity_dpm_print_power_state(struct radeon_device *rdev, r600_dpm_print_ps_status(rdev, rps); } +void trinity_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev, + struct seq_file *m) +{ + struct radeon_ps *rps = rdev->pm.dpm.current_ps; + struct trinity_ps *ps = trinity_get_ps(rps); + struct trinity_pl *pl; + u32 current_index = + (RREG32(TARGET_AND_CURRENT_PROFILE_INDEX) & CURRENT_STATE_MASK) >> + CURRENT_STATE_SHIFT; + + if (current_index >= ps->num_levels) { + seq_printf(m, "invalid dpm profile %d\n", current_index); + } else { + pl = &ps->levels[current_index]; + seq_printf(m, "uvd vclk: %d dclk: %d\n", rps->vclk, rps->dclk); + seq_printf(m, "power level %d sclk: %u vddc: %u\n", + current_index, pl->sclk, + trinity_convert_voltage_index_to_value(rdev, pl->vddc_index)); + } +} + void trinity_dpm_fini(struct radeon_device *rdev) { int i; -- cgit v1.2.3 From bdf0c4f07d5fbda79569a11116053bed44873c8a Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 28 Jun 2013 17:49:02 -0400 Subject: drm/radeon/dpm: add debugfs support for cayman This allows you to look at the current DPM state via debugfs. Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/ni_dpm.c | 20 ++++++++++++++++++++ drivers/gpu/drm/radeon/nid.h | 4 ++++ drivers/gpu/drm/radeon/radeon_asic.c | 1 + drivers/gpu/drm/radeon/radeon_asic.h | 2 ++ 4 files changed, 27 insertions(+) diff --git a/drivers/gpu/drm/radeon/ni_dpm.c b/drivers/gpu/drm/radeon/ni_dpm.c index 4712851808a3..8497ca6bb0b1 100644 --- a/drivers/gpu/drm/radeon/ni_dpm.c +++ b/drivers/gpu/drm/radeon/ni_dpm.c @@ -4287,6 +4287,26 @@ void ni_dpm_print_power_state(struct radeon_device *rdev, r600_dpm_print_ps_status(rdev, rps); } +void ni_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev, + struct seq_file *m) +{ + struct radeon_ps *rps = rdev->pm.dpm.current_ps; + struct ni_ps *ps = ni_get_ps(rps); + struct rv7xx_pl *pl; + u32 current_index = + (RREG32(TARGET_AND_CURRENT_PROFILE_INDEX) & CURRENT_STATE_INDEX_MASK) >> + CURRENT_STATE_INDEX_SHIFT; + + if (current_index >= ps->performance_level_count) { + seq_printf(m, "invalid dpm profile %d\n", current_index); + } else { + pl = &ps->performance_levels[current_index]; + seq_printf(m, "uvd vclk: %d dclk: %d\n", rps->vclk, rps->dclk); + seq_printf(m, "power level %d sclk: %u mclk: %u vddc: %u vddci: %u\n", + current_index, pl->sclk, pl->mclk, pl->vddc, pl->vddci); + } +} + u32 ni_dpm_get_sclk(struct radeon_device *rdev, bool low) { struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); diff --git a/drivers/gpu/drm/radeon/nid.h b/drivers/gpu/drm/radeon/nid.h index 95693c77351d..fe24a93542ec 100644 --- a/drivers/gpu/drm/radeon/nid.h +++ b/drivers/gpu/drm/radeon/nid.h @@ -618,6 +618,10 @@ # define MRDCKD0_BYPASS (1 << 30) # define MRDCKD1_BYPASS (1 << 31) +#define TARGET_AND_CURRENT_PROFILE_INDEX 0x66c +# define CURRENT_STATE_INDEX_MASK (0xf << 4) +# define CURRENT_STATE_INDEX_SHIFT 4 + #define CG_AT 0x6d4 # define CG_R(x) ((x) << 0) # define CG_R_MASK (0xffff << 0) diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index 691654740906..5b3a122912f7 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -1936,6 +1936,7 @@ static struct radeon_asic cayman_asic = { .get_sclk = &ni_dpm_get_sclk, .get_mclk = &ni_dpm_get_mclk, .print_power_state = &ni_dpm_print_power_state, + .debugfs_print_current_performance_level = &ni_dpm_debugfs_print_current_performance_level, }, .pflip = { .pre_page_flip = &evergreen_pre_page_flip, diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 5904d897586d..7524edb26152 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -613,6 +613,8 @@ u32 ni_dpm_get_sclk(struct radeon_device *rdev, bool low); u32 ni_dpm_get_mclk(struct radeon_device *rdev, bool low); void ni_dpm_print_power_state(struct radeon_device *rdev, struct radeon_ps *ps); +void ni_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev, + struct seq_file *m); int trinity_dpm_init(struct radeon_device *rdev); int trinity_dpm_enable(struct radeon_device *rdev); void trinity_dpm_disable(struct radeon_device *rdev); -- cgit v1.2.3 From 7982128c3d447df27db963af67bc6b8dc7efb1de Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 28 Jun 2013 18:02:19 -0400 Subject: drm/radeon/dpm: add debugfs support for SI This allows you to look at the current DPM state via debugfs. Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/radeon_asic.c | 1 + drivers/gpu/drm/radeon/radeon_asic.h | 2 ++ drivers/gpu/drm/radeon/si_dpm.c | 19 +++++++++++++++++++ drivers/gpu/drm/radeon/sid.h | 4 ++++ 4 files changed, 26 insertions(+) diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index 5b3a122912f7..a5b244dc50ca 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -2282,6 +2282,7 @@ static struct radeon_asic si_asic = { .get_sclk = &ni_dpm_get_sclk, .get_mclk = &ni_dpm_get_mclk, .print_power_state = &ni_dpm_print_power_state, + .debugfs_print_current_performance_level = &si_dpm_debugfs_print_current_performance_level, }, .pflip = { .pre_page_flip = &evergreen_pre_page_flip, diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 7524edb26152..6822c7aeacaa 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -677,6 +677,8 @@ int si_dpm_set_power_state(struct radeon_device *rdev); void si_dpm_post_set_power_state(struct radeon_device *rdev); void si_dpm_fini(struct radeon_device *rdev); void si_dpm_display_configuration_changed(struct radeon_device *rdev); +void si_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev, + struct seq_file *m); /* DCE8 - CIK */ void dce8_bandwidth_update(struct radeon_device *rdev); diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c index 6918f070eb52..46e9fc56cee7 100644 --- a/drivers/gpu/drm/radeon/si_dpm.c +++ b/drivers/gpu/drm/radeon/si_dpm.c @@ -6385,3 +6385,22 @@ void si_dpm_fini(struct radeon_device *rdev) r600_free_extended_power_table(rdev); } +void si_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev, + struct seq_file *m) +{ + struct radeon_ps *rps = rdev->pm.dpm.current_ps; + struct ni_ps *ps = ni_get_ps(rps); + struct rv7xx_pl *pl; + u32 current_index = + (RREG32(TARGET_AND_CURRENT_PROFILE_INDEX) & CURRENT_STATE_INDEX_MASK) >> + CURRENT_STATE_INDEX_SHIFT; + + if (current_index >= ps->performance_level_count) { + seq_printf(m, "invalid dpm profile %d\n", current_index); + } else { + pl = &ps->performance_levels[current_index]; + seq_printf(m, "uvd vclk: %d dclk: %d\n", rps->vclk, rps->dclk); + seq_printf(m, "power level %d sclk: %u mclk: %u vddc: %u vddci: %u pcie gen: %u\n", + current_index, pl->sclk, pl->mclk, pl->vddc, pl->vddci, pl->pcie_gen + 1); + } +} diff --git a/drivers/gpu/drm/radeon/sid.h b/drivers/gpu/drm/radeon/sid.h index 299d657d0168..12a20eb77d0c 100644 --- a/drivers/gpu/drm/radeon/sid.h +++ b/drivers/gpu/drm/radeon/sid.h @@ -220,6 +220,10 @@ # define GFX_CLK_OFF_ACPI_D3 (1 << 13) # define DYN_LIGHT_SLEEP_EN (1 << 14) +#define TARGET_AND_CURRENT_PROFILE_INDEX 0x798 +# define CURRENT_STATE_INDEX_MASK (0xf << 4) +# define CURRENT_STATE_INDEX_SHIFT 4 + #define CG_FTV 0x7bc #define CG_FFCT_0 0x7c0 -- cgit v1.2.3 From b01978cacfd7e3a4ca703b0e48f2e18de8865df5 Mon Sep 17 00:00:00 2001 From: Jack Morgenstein Date: Thu, 27 Jun 2013 19:05:21 +0300 Subject: net/mlx4_core: Dynamic VST to VST vlan/qos changes Within VST mode, enable modifying the vlan and/or qos for a VF without requiring unbind/rebind. This requires firmware which supports the UPDATE_QP command. (If the command is not available, we fall back to requiring unbind/bind to activate these changes). To avoid race conditions with modify-qp on QPs that are affected by update-qp, this operation is performed on the comm_wq. If the update operation succeeds for all the necessary QPs, a vlan_unregister is performed for the abandoned vlan id. Signed-off-by: Jack Morgenstein Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/cmd.c | 122 ++++++++++++++++- drivers/net/ethernet/mellanox/mlx4/fw.c | 5 +- drivers/net/ethernet/mellanox/mlx4/mlx4.h | 20 +++ .../net/ethernet/mellanox/mlx4/resource_tracker.c | 145 ++++++++++++++++++++- include/linux/mlx4/cmd.h | 1 + include/linux/mlx4/device.h | 3 +- include/linux/mlx4/qp.h | 34 +++++ 7 files changed, 319 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c index df04c8206ebe..7b927891dc35 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c @@ -112,6 +112,14 @@ enum { GO_BIT_TIMEOUT_MSECS = 10000 }; +enum mlx4_vlan_transition { + MLX4_VLAN_TRANSITION_VST_VST = 0, + MLX4_VLAN_TRANSITION_VST_VGT = 1, + MLX4_VLAN_TRANSITION_VGT_VST = 2, + MLX4_VLAN_TRANSITION_VGT_VGT = 3, +}; + + struct mlx4_cmd_context { struct completion done; int result; @@ -792,6 +800,15 @@ static int mlx4_MAD_IFC_wrapper(struct mlx4_dev *dev, int slave, vhcr->op, MLX4_CMD_TIME_CLASS_C, MLX4_CMD_NATIVE); } +int MLX4_CMD_UPDATE_QP_wrapper(struct mlx4_dev *dev, int slave, + struct mlx4_vhcr *vhcr, + struct mlx4_cmd_mailbox *inbox, + struct mlx4_cmd_mailbox *outbox, + struct mlx4_cmd_info *cmd) +{ + return -EPERM; +} + int mlx4_DMA_wrapper(struct mlx4_dev *dev, int slave, struct mlx4_vhcr *vhcr, struct mlx4_cmd_mailbox *inbox, @@ -1225,6 +1242,15 @@ static struct mlx4_cmd_info cmd_info[] = { .verify = NULL, .wrapper = mlx4_GEN_QP_wrapper }, + { + .opcode = MLX4_CMD_UPDATE_QP, + .has_inbox = false, + .has_outbox = false, + .out_is_imm = false, + .encode_slave_id = false, + .verify = NULL, + .wrapper = MLX4_CMD_UPDATE_QP_wrapper + }, { .opcode = MLX4_CMD_CONF_SPECIAL_QP, .has_inbox = false, @@ -1495,6 +1521,72 @@ out: return ret; } + +int mlx4_master_immediate_activate_vlan_qos(struct mlx4_priv *priv, + int slave, int port) +{ + struct mlx4_vport_oper_state *vp_oper; + struct mlx4_vport_state *vp_admin; + struct mlx4_vf_immed_vlan_work *work; + int err; + int admin_vlan_ix = NO_INDX; + + vp_oper = &priv->mfunc.master.vf_oper[slave].vport[port]; + vp_admin = &priv->mfunc.master.vf_admin[slave].vport[port]; + + if (vp_oper->state.default_vlan == vp_admin->default_vlan && + vp_oper->state.default_qos == vp_admin->default_qos) + return 0; + + work = kzalloc(sizeof(*work), GFP_KERNEL); + if (!work) + return -ENOMEM; + + if (vp_oper->state.default_vlan != vp_admin->default_vlan) { + err = __mlx4_register_vlan(&priv->dev, port, + vp_admin->default_vlan, + &admin_vlan_ix); + if (err) { + mlx4_warn((&priv->dev), + "No vlan resources slave %d, port %d\n", + slave, port); + return err; + } + work->flags |= MLX4_VF_IMMED_VLAN_FLAG_VLAN; + mlx4_dbg((&(priv->dev)), + "alloc vlan %d idx %d slave %d port %d\n", + (int)(vp_admin->default_vlan), + admin_vlan_ix, slave, port); + } + + /* save original vlan ix and vlan id */ + work->orig_vlan_id = vp_oper->state.default_vlan; + work->orig_vlan_ix = vp_oper->vlan_idx; + + /* handle new qos */ + if (vp_oper->state.default_qos != vp_admin->default_qos) + work->flags |= MLX4_VF_IMMED_VLAN_FLAG_QOS; + + if (work->flags & MLX4_VF_IMMED_VLAN_FLAG_VLAN) + vp_oper->vlan_idx = admin_vlan_ix; + + vp_oper->state.default_vlan = vp_admin->default_vlan; + vp_oper->state.default_qos = vp_admin->default_qos; + + /* iterate over QPs owned by this slave, using UPDATE_QP */ + work->port = port; + work->slave = slave; + work->qos = vp_oper->state.default_qos; + work->vlan_id = vp_oper->state.default_vlan; + work->vlan_ix = vp_oper->vlan_idx; + work->priv = priv; + INIT_WORK(&work->work, mlx4_vf_immed_vlan_work_handler); + queue_work(priv->mfunc.master.comm_wq, &work->work); + + return 0; +} + + static int mlx4_master_activate_admin_state(struct mlx4_priv *priv, int slave) { int port, err; @@ -2109,11 +2201,18 @@ int mlx4_set_vf_mac(struct mlx4_dev *dev, int port, int vf, u64 mac) } EXPORT_SYMBOL_GPL(mlx4_set_vf_mac); +static int calculate_transition(u16 oper_vlan, u16 admin_vlan) +{ + return (2 * (oper_vlan == MLX4_VGT) + (admin_vlan == MLX4_VGT)); +} + int mlx4_set_vf_vlan(struct mlx4_dev *dev, int port, int vf, u16 vlan, u8 qos) { struct mlx4_priv *priv = mlx4_priv(dev); - struct mlx4_vport_state *s_info; + struct mlx4_vport_oper_state *vf_oper; + struct mlx4_vport_state *vf_admin; int slave; + enum mlx4_vlan_transition vlan_trans; if ((!mlx4_is_master(dev)) || !(dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_VLAN_CONTROL)) @@ -2126,12 +2225,25 @@ int mlx4_set_vf_vlan(struct mlx4_dev *dev, int port, int vf, u16 vlan, u8 qos) if (slave < 0) return -EINVAL; - s_info = &priv->mfunc.master.vf_admin[slave].vport[port]; + vf_admin = &priv->mfunc.master.vf_admin[slave].vport[port]; + vf_oper = &priv->mfunc.master.vf_oper[slave].vport[port]; + if ((0 == vlan) && (0 == qos)) - s_info->default_vlan = MLX4_VGT; + vf_admin->default_vlan = MLX4_VGT; else - s_info->default_vlan = vlan; - s_info->default_qos = qos; + vf_admin->default_vlan = vlan; + vf_admin->default_qos = qos; + + vlan_trans = calculate_transition(vf_oper->state.default_vlan, + vf_admin->default_vlan); + + if (priv->mfunc.master.slave_state[slave].active && + dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_UPDATE_QP && + vlan_trans == MLX4_VLAN_TRANSITION_VST_VST) { + mlx4_info(dev, "updating vf %d port %d config params immediately\n", + vf, port); + mlx4_master_immediate_activate_vlan_qos(priv, slave, port); + } return 0; } EXPORT_SYMBOL_GPL(mlx4_set_vf_vlan); diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index 569bbe3e7403..8873d6802c80 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -133,7 +133,8 @@ static void dump_dev_cap_flags2(struct mlx4_dev *dev, u64 flags) [4] = "Automatic MAC reassignment support", [5] = "Time stamping support", [6] = "VST (control vlan insertion/stripping) support", - [7] = "FSM (MAC anti-spoofing) support" + [7] = "FSM (MAC anti-spoofing) support", + [8] = "Dynamic QP updates support" }; int i; @@ -659,6 +660,8 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) QUERY_DEV_CAP_MAX_COUNTERS_OFFSET); MLX4_GET(field32, outbox, QUERY_DEV_CAP_EXT_2_FLAGS_OFFSET); + if (field32 & (1 << 16)) + dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_UPDATE_QP; if (field32 & (1 << 26)) dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_VLAN_CONTROL; if (field32 & (1 << 20)) diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index 75272935a3f7..5abcb6501e30 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -571,6 +571,24 @@ struct mlx4_cmd { u8 comm_toggle; }; +enum { + MLX4_VF_IMMED_VLAN_FLAG_VLAN = 1 << 0, + MLX4_VF_IMMED_VLAN_FLAG_QOS = 1 << 1, +}; +struct mlx4_vf_immed_vlan_work { + struct work_struct work; + struct mlx4_priv *priv; + int flags; + int slave; + int vlan_ix; + int orig_vlan_ix; + u8 port; + u8 qos; + u16 vlan_id; + u16 orig_vlan_id; +}; + + struct mlx4_uar_table { struct mlx4_bitmap bitmap; }; @@ -1218,4 +1236,6 @@ static inline spinlock_t *mlx4_tlock(struct mlx4_dev *dev) #define NOT_MASKED_PD_BITS 17 +void mlx4_vf_immed_vlan_work_handler(struct work_struct *_work); + #endif /* MLX4_H */ diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index 1157f028a90f..46323a20060c 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -101,6 +101,8 @@ struct res_qp { spinlock_t mcg_spl; int local_qpn; atomic_t ref_count; + u32 qpc_flags; + u8 sched_queue; }; enum res_mtt_states { @@ -355,7 +357,7 @@ static void update_gid(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *inbox, static int update_vport_qp_param(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *inbox, - u8 slave) + u8 slave, u32 qpn) { struct mlx4_qp_context *qpc = inbox->buf + 8; struct mlx4_vport_oper_state *vp_oper; @@ -369,9 +371,17 @@ static int update_vport_qp_param(struct mlx4_dev *dev, if (MLX4_VGT != vp_oper->state.default_vlan) { qp_type = (be32_to_cpu(qpc->flags) >> 16) & 0xff; - if (MLX4_QP_ST_RC == qp_type) + if (MLX4_QP_ST_RC == qp_type || + (MLX4_QP_ST_UD == qp_type && + !mlx4_is_qp_reserved(dev, qpn))) return -EINVAL; + /* the reserved QPs (special, proxy, tunnel) + * do not operate over vlans + */ + if (mlx4_is_qp_reserved(dev, qpn)) + return 0; + /* force strip vlan by clear vsd */ qpc->param3 &= ~cpu_to_be32(MLX4_STRIP_VLAN); if (0 != vp_oper->state.default_vlan) { @@ -2114,6 +2124,8 @@ int mlx4_RST2INIT_QP_wrapper(struct mlx4_dev *dev, int slave, if (err) return err; qp->local_qpn = local_qpn; + qp->sched_queue = 0; + qp->qpc_flags = be32_to_cpu(qpc->flags); err = get_res(dev, slave, mtt_base, RES_MTT, &mtt); if (err) @@ -2836,6 +2848,9 @@ int mlx4_INIT2RTR_QP_wrapper(struct mlx4_dev *dev, int slave, { int err; struct mlx4_qp_context *qpc = inbox->buf + 8; + int qpn = vhcr->in_modifier & 0x7fffff; + struct res_qp *qp; + u8 orig_sched_queue; err = verify_qp_parameters(dev, inbox, QP_TRANS_INIT2RTR, slave); if (err) @@ -2844,11 +2859,30 @@ int mlx4_INIT2RTR_QP_wrapper(struct mlx4_dev *dev, int slave, update_pkey_index(dev, slave, inbox); update_gid(dev, inbox, (u8)slave); adjust_proxy_tun_qkey(dev, vhcr, qpc); - err = update_vport_qp_param(dev, inbox, slave); + orig_sched_queue = qpc->pri_path.sched_queue; + err = update_vport_qp_param(dev, inbox, slave, qpn); if (err) return err; - return mlx4_GEN_QP_wrapper(dev, slave, vhcr, inbox, outbox, cmd); + err = get_res(dev, slave, qpn, RES_QP, &qp); + if (err) + return err; + if (qp->com.from_state != RES_QP_HW) { + err = -EBUSY; + goto out; + } + + err = mlx4_DMA_wrapper(dev, slave, vhcr, inbox, outbox, cmd); +out: + /* if no error, save sched queue value passed in by VF. This is + * essentially the QOS value provided by the VF. This will be useful + * if we allow dynamic changes from VST back to VGT + */ + if (!err) + qp->sched_queue = orig_sched_queue; + + put_res(dev, slave, qpn, RES_QP); + return err; } int mlx4_RTR2RTS_QP_wrapper(struct mlx4_dev *dev, int slave, @@ -3932,3 +3966,106 @@ void mlx4_delete_all_resources_for_slave(struct mlx4_dev *dev, int slave) rem_slave_xrcdns(dev, slave); mutex_unlock(&priv->mfunc.master.res_tracker.slave_list[slave].mutex); } + +void mlx4_vf_immed_vlan_work_handler(struct work_struct *_work) +{ + struct mlx4_vf_immed_vlan_work *work = + container_of(_work, struct mlx4_vf_immed_vlan_work, work); + struct mlx4_cmd_mailbox *mailbox; + struct mlx4_update_qp_context *upd_context; + struct mlx4_dev *dev = &work->priv->dev; + struct mlx4_resource_tracker *tracker = + &work->priv->mfunc.master.res_tracker; + struct list_head *qp_list = + &tracker->slave_list[work->slave].res_list[RES_QP]; + struct res_qp *qp; + struct res_qp *tmp; + u64 qp_mask = ((1ULL << MLX4_UPD_QP_PATH_MASK_ETH_TX_BLOCK_UNTAGGED) | + (1ULL << MLX4_UPD_QP_PATH_MASK_ETH_TX_BLOCK_1P) | + (1ULL << MLX4_UPD_QP_PATH_MASK_ETH_TX_BLOCK_TAGGED) | + (1ULL << MLX4_UPD_QP_PATH_MASK_ETH_RX_BLOCK_UNTAGGED) | + (1ULL << MLX4_UPD_QP_PATH_MASK_ETH_RX_BLOCK_1P) | + (1ULL << MLX4_UPD_QP_PATH_MASK_ETH_RX_BLOCK_TAGGED) | + (1ULL << MLX4_UPD_QP_PATH_MASK_VLAN_INDEX) | + (1ULL << MLX4_UPD_QP_PATH_MASK_SCHED_QUEUE)); + + int err; + int port, errors = 0; + u8 vlan_control; + + if (mlx4_is_slave(dev)) { + mlx4_warn(dev, "Trying to update-qp in slave %d\n", + work->slave); + goto out; + } + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + goto out; + + if (!work->vlan_id) + vlan_control = MLX4_VLAN_CTRL_ETH_TX_BLOCK_TAGGED | + MLX4_VLAN_CTRL_ETH_RX_BLOCK_TAGGED; + else + vlan_control = MLX4_VLAN_CTRL_ETH_TX_BLOCK_TAGGED | + MLX4_VLAN_CTRL_ETH_RX_BLOCK_PRIO_TAGGED | + MLX4_VLAN_CTRL_ETH_RX_BLOCK_UNTAGGED; + + upd_context = mailbox->buf; + upd_context->primary_addr_path_mask = cpu_to_be64(qp_mask); + upd_context->qp_context.pri_path.vlan_control = vlan_control; + upd_context->qp_context.pri_path.vlan_index = work->vlan_ix; + + spin_lock_irq(mlx4_tlock(dev)); + list_for_each_entry_safe(qp, tmp, qp_list, com.list) { + spin_unlock_irq(mlx4_tlock(dev)); + if (qp->com.owner == work->slave) { + if (qp->com.from_state != RES_QP_HW || + !qp->sched_queue || /* no INIT2RTR trans yet */ + mlx4_is_qp_reserved(dev, qp->local_qpn) || + qp->qpc_flags & (1 << MLX4_RSS_QPC_FLAG_OFFSET)) { + spin_lock_irq(mlx4_tlock(dev)); + continue; + } + port = (qp->sched_queue >> 6 & 1) + 1; + if (port != work->port) { + spin_lock_irq(mlx4_tlock(dev)); + continue; + } + upd_context->qp_context.pri_path.sched_queue = + qp->sched_queue & 0xC7; + upd_context->qp_context.pri_path.sched_queue |= + ((work->qos & 0x7) << 3); + + err = mlx4_cmd(dev, mailbox->dma, + qp->local_qpn & 0xffffff, + 0, MLX4_CMD_UPDATE_QP, + MLX4_CMD_TIME_CLASS_C, MLX4_CMD_NATIVE); + if (err) { + mlx4_info(dev, "UPDATE_QP failed for slave %d, " + "port %d, qpn %d (%d)\n", + work->slave, port, qp->local_qpn, + err); + errors++; + } + } + spin_lock_irq(mlx4_tlock(dev)); + } + spin_unlock_irq(mlx4_tlock(dev)); + mlx4_free_cmd_mailbox(dev, mailbox); + + if (errors) + mlx4_err(dev, "%d UPDATE_QP failures for slave %d, port %d\n", + errors, work->slave, work->port); + + /* unregister previous vlan_id if needed and we had no errors + * while updating the QPs + */ + if (work->flags & MLX4_VF_IMMED_VLAN_FLAG_VLAN && !errors && + NO_INDX != work->orig_vlan_ix) + __mlx4_unregister_vlan(&work->priv->dev, work->port, + work->orig_vlan_ix); +out: + kfree(work); + return; +} diff --git a/include/linux/mlx4/cmd.h b/include/linux/mlx4/cmd.h index 8074a9711cf1..bb1c8096a7eb 100644 --- a/include/linux/mlx4/cmd.h +++ b/include/linux/mlx4/cmd.h @@ -111,6 +111,7 @@ enum { MLX4_CMD_INIT2INIT_QP = 0x2d, MLX4_CMD_SUSPEND_QP = 0x32, MLX4_CMD_UNSUSPEND_QP = 0x33, + MLX4_CMD_UPDATE_QP = 0x61, /* special QP and management commands */ MLX4_CMD_CONF_SPECIAL_QP = 0x23, MLX4_CMD_MAD_IFC = 0x24, diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index a51b0134ce18..52c23a892bab 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -157,7 +157,8 @@ enum { MLX4_DEV_CAP_FLAGS2_REASSIGN_MAC_EN = 1LL << 4, MLX4_DEV_CAP_FLAG2_TS = 1LL << 5, MLX4_DEV_CAP_FLAG2_VLAN_CONTROL = 1LL << 6, - MLX4_DEV_CAP_FLAG2_FSM = 1LL << 7 + MLX4_DEV_CAP_FLAG2_FSM = 1LL << 7, + MLX4_DEV_CAP_FLAG2_UPDATE_QP = 1LL << 8 }; enum { diff --git a/include/linux/mlx4/qp.h b/include/linux/mlx4/qp.h index 352eec9df1b8..f43e32aa054a 100644 --- a/include/linux/mlx4/qp.h +++ b/include/linux/mlx4/qp.h @@ -206,6 +206,40 @@ struct mlx4_qp_context { u32 reserved5[10]; }; +struct mlx4_update_qp_context { + __be64 qp_mask; + __be64 primary_addr_path_mask; + __be64 secondary_addr_path_mask; + u64 reserved1; + struct mlx4_qp_context qp_context; + u64 reserved2[58]; +}; + +enum { + MLX4_UPD_QP_MASK_PM_STATE = 32, + MLX4_UPD_QP_MASK_VSD = 33, +}; + +enum { + MLX4_UPD_QP_PATH_MASK_PKEY_INDEX = 0 + 32, + MLX4_UPD_QP_PATH_MASK_FSM = 1 + 32, + MLX4_UPD_QP_PATH_MASK_MAC_INDEX = 2 + 32, + MLX4_UPD_QP_PATH_MASK_FVL = 3 + 32, + MLX4_UPD_QP_PATH_MASK_CV = 4 + 32, + MLX4_UPD_QP_PATH_MASK_VLAN_INDEX = 5 + 32, + MLX4_UPD_QP_PATH_MASK_ETH_HIDE_CQE_VLAN = 6 + 32, + MLX4_UPD_QP_PATH_MASK_ETH_TX_BLOCK_UNTAGGED = 7 + 32, + MLX4_UPD_QP_PATH_MASK_ETH_TX_BLOCK_1P = 8 + 32, + MLX4_UPD_QP_PATH_MASK_ETH_TX_BLOCK_TAGGED = 9 + 32, + MLX4_UPD_QP_PATH_MASK_ETH_RX_BLOCK_UNTAGGED = 10 + 32, + MLX4_UPD_QP_PATH_MASK_ETH_RX_BLOCK_1P = 11 + 32, + MLX4_UPD_QP_PATH_MASK_ETH_RX_BLOCK_TAGGED = 12 + 32, + MLX4_UPD_QP_PATH_MASK_FEUP = 13 + 32, + MLX4_UPD_QP_PATH_MASK_SCHED_QUEUE = 14 + 32, + MLX4_UPD_QP_PATH_MASK_IF_COUNTER_INDEX = 15 + 32, + MLX4_UPD_QP_PATH_MASK_FVL_RX = 16 + 32, +}; + enum { /* param3 */ MLX4_STRIP_VLAN = 1 << 30 }; -- cgit v1.2.3 From 0a6eac24583848490e9a9c02daef5e33c997431f Mon Sep 17 00:00:00 2001 From: Rony Efraim Date: Thu, 27 Jun 2013 19:05:22 +0300 Subject: net/mlx4_core: Add HW enforcement to VF link state When the firmware supports the UPDATE_QP command, if the VF link is disabled, block all QPs opened by the VF, by programming the UPDATE_QP command to drop all RX & TX traffic to/from these QPs. Operates only in VST mode. Signed-off-by: Rony Efraim Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/cmd.c | 57 ++++++++++++++-------- drivers/net/ethernet/mellanox/mlx4/mlx4.h | 1 + .../net/ethernet/mellanox/mlx4/resource_tracker.c | 22 +++++++-- include/linux/mlx4/qp.h | 2 + 4 files changed, 60 insertions(+), 22 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c index 7b927891dc35..707a7d0d08e1 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c @@ -1521,6 +1521,10 @@ out: return ret; } +static int calculate_transition(u16 oper_vlan, u16 admin_vlan) +{ + return (2 * (oper_vlan == MLX4_VGT) + (admin_vlan == MLX4_VGT)); +} int mlx4_master_immediate_activate_vlan_qos(struct mlx4_priv *priv, int slave, int port) @@ -1528,16 +1532,37 @@ int mlx4_master_immediate_activate_vlan_qos(struct mlx4_priv *priv, struct mlx4_vport_oper_state *vp_oper; struct mlx4_vport_state *vp_admin; struct mlx4_vf_immed_vlan_work *work; + struct mlx4_dev *dev = &(priv->dev); int err; int admin_vlan_ix = NO_INDX; + enum mlx4_vlan_transition vlan_trans; vp_oper = &priv->mfunc.master.vf_oper[slave].vport[port]; vp_admin = &priv->mfunc.master.vf_admin[slave].vport[port]; if (vp_oper->state.default_vlan == vp_admin->default_vlan && - vp_oper->state.default_qos == vp_admin->default_qos) + vp_oper->state.default_qos == vp_admin->default_qos && + vp_oper->state.link_state == vp_admin->link_state) return 0; + vlan_trans = calculate_transition(vp_oper->state.default_vlan, + vp_admin->default_vlan); + + if (!(priv->mfunc.master.slave_state[slave].active && + dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_UPDATE_QP && + vlan_trans == MLX4_VLAN_TRANSITION_VST_VST)) { + /* even if the UPDATE_QP command isn't supported, we still want + * to set this VF link according to the admin directive + */ + vp_oper->state.link_state = vp_admin->link_state; + return -1; + } + + mlx4_dbg(dev, "updating immediately admin params slave %d port %d\n", + slave, port); + mlx4_dbg(dev, "vlan %d QoS %d link down %d\n", vp_admin->default_vlan, + vp_admin->default_qos, vp_admin->link_state); + work = kzalloc(sizeof(*work), GFP_KERNEL); if (!work) return -ENOMEM; @@ -1572,6 +1597,10 @@ int mlx4_master_immediate_activate_vlan_qos(struct mlx4_priv *priv, vp_oper->state.default_vlan = vp_admin->default_vlan; vp_oper->state.default_qos = vp_admin->default_qos; + vp_oper->state.link_state = vp_admin->link_state; + + if (vp_admin->link_state == IFLA_VF_LINK_STATE_DISABLE) + work->flags |= MLX4_VF_IMMED_VLAN_FLAG_LINK_DISABLE; /* iterate over QPs owned by this slave, using UPDATE_QP */ work->port = port; @@ -2201,10 +2230,6 @@ int mlx4_set_vf_mac(struct mlx4_dev *dev, int port, int vf, u64 mac) } EXPORT_SYMBOL_GPL(mlx4_set_vf_mac); -static int calculate_transition(u16 oper_vlan, u16 admin_vlan) -{ - return (2 * (oper_vlan == MLX4_VGT) + (admin_vlan == MLX4_VGT)); -} int mlx4_set_vf_vlan(struct mlx4_dev *dev, int port, int vf, u16 vlan, u8 qos) { @@ -2212,7 +2237,6 @@ int mlx4_set_vf_vlan(struct mlx4_dev *dev, int port, int vf, u16 vlan, u8 qos) struct mlx4_vport_oper_state *vf_oper; struct mlx4_vport_state *vf_admin; int slave; - enum mlx4_vlan_transition vlan_trans; if ((!mlx4_is_master(dev)) || !(dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_VLAN_CONTROL)) @@ -2234,16 +2258,10 @@ int mlx4_set_vf_vlan(struct mlx4_dev *dev, int port, int vf, u16 vlan, u8 qos) vf_admin->default_vlan = vlan; vf_admin->default_qos = qos; - vlan_trans = calculate_transition(vf_oper->state.default_vlan, - vf_admin->default_vlan); - - if (priv->mfunc.master.slave_state[slave].active && - dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_UPDATE_QP && - vlan_trans == MLX4_VLAN_TRANSITION_VST_VST) { - mlx4_info(dev, "updating vf %d port %d config params immediately\n", + if (mlx4_master_immediate_activate_vlan_qos(priv, slave, port)) + mlx4_info(dev, + "updating vf %d port %d config will take effect on next VF restart\n", vf, port); - mlx4_master_immediate_activate_vlan_qos(priv, slave, port); - } return 0; } EXPORT_SYMBOL_GPL(mlx4_set_vf_vlan); @@ -2307,7 +2325,6 @@ int mlx4_set_vf_link_state(struct mlx4_dev *dev, int port, int vf, int link_stat { struct mlx4_priv *priv = mlx4_priv(dev); struct mlx4_vport_state *s_info; - struct mlx4_vport_oper_state *vp_oper; int slave; u8 link_stat_event; @@ -2337,14 +2354,16 @@ int mlx4_set_vf_link_state(struct mlx4_dev *dev, int port, int vf, int link_stat link_state, slave, port); return -EINVAL; }; - /* update the admin & oper state on the link state */ s_info = &priv->mfunc.master.vf_admin[slave].vport[port]; - vp_oper = &priv->mfunc.master.vf_oper[slave].vport[port]; s_info->link_state = link_state; - vp_oper->state.link_state = link_state; /* send event */ mlx4_gen_port_state_change_eqe(dev, slave, port, link_stat_event); + + if (mlx4_master_immediate_activate_vlan_qos(priv, slave, port)) + mlx4_dbg(dev, + "updating vf %d port %d no link state HW enforcment\n", + vf, port); return 0; } EXPORT_SYMBOL_GPL(mlx4_set_vf_link_state); diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index 5abcb6501e30..17d9277e33ef 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -574,6 +574,7 @@ struct mlx4_cmd { enum { MLX4_VF_IMMED_VLAN_FLAG_VLAN = 1 << 0, MLX4_VF_IMMED_VLAN_FLAG_QOS = 1 << 1, + MLX4_VF_IMMED_VLAN_FLAG_LINK_DISABLE = 1 << 2, }; struct mlx4_vf_immed_vlan_work { struct work_struct work; diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index 46323a20060c..f984a89c27df 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -384,7 +384,17 @@ static int update_vport_qp_param(struct mlx4_dev *dev, /* force strip vlan by clear vsd */ qpc->param3 &= ~cpu_to_be32(MLX4_STRIP_VLAN); - if (0 != vp_oper->state.default_vlan) { + + if (vp_oper->state.link_state == IFLA_VF_LINK_STATE_DISABLE && + dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_UPDATE_QP) { + qpc->pri_path.vlan_control = + MLX4_VLAN_CTRL_ETH_TX_BLOCK_TAGGED | + MLX4_VLAN_CTRL_ETH_TX_BLOCK_PRIO_TAGGED | + MLX4_VLAN_CTRL_ETH_TX_BLOCK_UNTAGGED | + MLX4_VLAN_CTRL_ETH_RX_BLOCK_PRIO_TAGGED | + MLX4_VLAN_CTRL_ETH_RX_BLOCK_UNTAGGED | + MLX4_VLAN_CTRL_ETH_RX_BLOCK_TAGGED; + } else if (0 != vp_oper->state.default_vlan) { qpc->pri_path.vlan_control = MLX4_VLAN_CTRL_ETH_TX_BLOCK_TAGGED | MLX4_VLAN_CTRL_ETH_RX_BLOCK_PRIO_TAGGED | @@ -4002,8 +4012,14 @@ void mlx4_vf_immed_vlan_work_handler(struct work_struct *_work) mailbox = mlx4_alloc_cmd_mailbox(dev); if (IS_ERR(mailbox)) goto out; - - if (!work->vlan_id) + if (work->flags & MLX4_VF_IMMED_VLAN_FLAG_LINK_DISABLE) /* block all */ + vlan_control = MLX4_VLAN_CTRL_ETH_TX_BLOCK_TAGGED | + MLX4_VLAN_CTRL_ETH_TX_BLOCK_PRIO_TAGGED | + MLX4_VLAN_CTRL_ETH_TX_BLOCK_UNTAGGED | + MLX4_VLAN_CTRL_ETH_RX_BLOCK_PRIO_TAGGED | + MLX4_VLAN_CTRL_ETH_RX_BLOCK_UNTAGGED | + MLX4_VLAN_CTRL_ETH_RX_BLOCK_TAGGED; + else if (!work->vlan_id) vlan_control = MLX4_VLAN_CTRL_ETH_TX_BLOCK_TAGGED | MLX4_VLAN_CTRL_ETH_RX_BLOCK_TAGGED; else diff --git a/include/linux/mlx4/qp.h b/include/linux/mlx4/qp.h index f43e32aa054a..262deac02c9e 100644 --- a/include/linux/mlx4/qp.h +++ b/include/linux/mlx4/qp.h @@ -152,6 +152,8 @@ enum { /* fl */ }; enum { /* vlan_control */ MLX4_VLAN_CTRL_ETH_TX_BLOCK_TAGGED = 1 << 6, + MLX4_VLAN_CTRL_ETH_TX_BLOCK_PRIO_TAGGED = 1 << 5, /* 802.1p priority tag */ + MLX4_VLAN_CTRL_ETH_TX_BLOCK_UNTAGGED = 1 << 4, MLX4_VLAN_CTRL_ETH_RX_BLOCK_TAGGED = 1 << 2, MLX4_VLAN_CTRL_ETH_RX_BLOCK_PRIO_TAGGED = 1 << 1, /* 802.1p priority tag */ MLX4_VLAN_CTRL_ETH_RX_BLOCK_UNTAGGED = 1 << 0 -- cgit v1.2.3 From ef0cc4b1d296ce041e3b24d103177dd70642740c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 29 Jun 2013 19:23:13 +0200 Subject: alx: treat flow control correctly in alx_set_pauseparam() Even when alx_setup_speed_duplex() is called, we still need to call alx_cfg_mac_flowcontrol() and set hw->flowctrl if flow control changed. This was a bug I accidentally introduced while simplifying the original driver. Reported-by: Ben Hutchings Signed-off-by: Johannes Berg Signed-off-by: David S. Miller --- drivers/net/ethernet/atheros/alx/ethtool.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/atheros/alx/ethtool.c b/drivers/net/ethernet/atheros/alx/ethtool.c index 6fa2aec2bc81..50a91d012bce 100644 --- a/drivers/net/ethernet/atheros/alx/ethtool.c +++ b/drivers/net/ethernet/atheros/alx/ethtool.c @@ -187,7 +187,8 @@ static int alx_set_pauseparam(struct net_device *netdev, if (reconfig_phy) { err = alx_setup_speed_duplex(hw, hw->adv_cfg, fc); - return err; + if (err) + return err; } /* flow control on mac */ -- cgit v1.2.3 From 17fdd35268f3856702dae5c3c0d8f756ec2c6d2d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 29 Jun 2013 19:23:14 +0200 Subject: alx: fix 100mbit/half duplex speed translation 100mbit half duplex is ADVERTISED_100baseT_Half, not ADVERTISED_10baseT_Half. Signed-off-by: Johannes Berg Signed-off-by: David S. Miller --- drivers/net/ethernet/atheros/alx/hw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/atheros/alx/hw.c b/drivers/net/ethernet/atheros/alx/hw.c index 220a16ad0e49..dc71cfb6ce61 100644 --- a/drivers/net/ethernet/atheros/alx/hw.c +++ b/drivers/net/ethernet/atheros/alx/hw.c @@ -1134,7 +1134,7 @@ static inline u32 alx_speed_to_ethadv(int speed) case SPEED_100 + DUPLEX_FULL: return ADVERTISED_100baseT_Full; case SPEED_100 + DUPLEX_HALF: - return ADVERTISED_10baseT_Half; + return ADVERTISED_100baseT_Half; case SPEED_10 + DUPLEX_FULL: return ADVERTISED_10baseT_Full; case SPEED_10 + DUPLEX_HALF: -- cgit v1.2.3 From c43861d35a73cfc86ae0506b60e73852849bca6a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 29 Jun 2013 19:23:15 +0200 Subject: alx: remove NET_CORE Kconfig select That select doesn't make any sense, remove it. Signed-off-by: Johannes Berg Signed-off-by: David S. Miller --- drivers/net/ethernet/atheros/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/ethernet/atheros/Kconfig b/drivers/net/ethernet/atheros/Kconfig index 39e8d6cc8843..58ad37c733bc 100644 --- a/drivers/net/ethernet/atheros/Kconfig +++ b/drivers/net/ethernet/atheros/Kconfig @@ -67,7 +67,6 @@ config ALX tristate "Qualcomm Atheros AR816x/AR817x support" depends on PCI select CRC32 - select NET_CORE select MDIO help This driver supports the Qualcomm Atheros L1F ethernet adapter, -- cgit v1.2.3 From 4a134c39db2d9d6f31c55e7c3773fc33189c9320 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 29 Jun 2013 19:23:16 +0200 Subject: alx: make sizes unsigned The ring sizes should be unsigned, pointed out by Ben Hutchings. Reported-by: Ben Hutchings Signed-off-by: Johannes Berg Signed-off-by: David S. Miller --- drivers/net/ethernet/atheros/alx/alx.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/atheros/alx/alx.h b/drivers/net/ethernet/atheros/alx/alx.h index 50b3ae2b143d..d71103dbf2cd 100644 --- a/drivers/net/ethernet/atheros/alx/alx.h +++ b/drivers/net/ethernet/atheros/alx/alx.h @@ -85,16 +85,16 @@ struct alx_priv { struct { dma_addr_t dma; void *virt; - int size; + unsigned int size; } descmem; /* protect int_mask updates */ spinlock_t irq_lock; u32 int_mask; - int tx_ringsz; - int rx_ringsz; - int rxbuf_size; + unsigned int tx_ringsz; + unsigned int rx_ringsz; + unsigned int rxbuf_size; struct napi_struct napi; struct alx_tx_queue txq; -- cgit v1.2.3 From a5b87cc9e0538bf6680d431e0076d778e5bae38e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 29 Jun 2013 19:23:17 +0200 Subject: alx: separate link speed/duplex fields As suggested by Ben Hutchings, use separate fields to track current link speed and duplex setting. Reported-by: Ben Hutchings Signed-off-by: Johannes Berg Signed-off-by: David S. Miller --- drivers/net/ethernet/atheros/alx/ethtool.c | 31 ++----- drivers/net/ethernet/atheros/alx/hw.c | 139 +++++++++++++---------------- drivers/net/ethernet/atheros/alx/hw.h | 24 ++++- drivers/net/ethernet/atheros/alx/main.c | 37 ++++---- 4 files changed, 106 insertions(+), 125 deletions(-) diff --git a/drivers/net/ethernet/atheros/alx/ethtool.c b/drivers/net/ethernet/atheros/alx/ethtool.c index 50a91d012bce..5e19e08b691b 100644 --- a/drivers/net/ethernet/atheros/alx/ethtool.c +++ b/drivers/net/ethernet/atheros/alx/ethtool.c @@ -85,14 +85,8 @@ static int alx_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) } } - if (hw->link_speed != SPEED_UNKNOWN) { - ethtool_cmd_speed_set(ecmd, - hw->link_speed - hw->link_speed % 10); - ecmd->duplex = hw->link_speed % 10; - } else { - ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN); - ecmd->duplex = DUPLEX_UNKNOWN; - } + ethtool_cmd_speed_set(ecmd, hw->link_speed); + ecmd->duplex = hw->duplex; return 0; } @@ -110,24 +104,11 @@ static int alx_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) return -EINVAL; adv_cfg = ecmd->advertising | ADVERTISED_Autoneg; } else { - int speed = ethtool_cmd_speed(ecmd); - - switch (speed + ecmd->duplex) { - case SPEED_10 + DUPLEX_HALF: - adv_cfg = ADVERTISED_10baseT_Half; - break; - case SPEED_10 + DUPLEX_FULL: - adv_cfg = ADVERTISED_10baseT_Full; - break; - case SPEED_100 + DUPLEX_HALF: - adv_cfg = ADVERTISED_100baseT_Half; - break; - case SPEED_100 + DUPLEX_FULL: - adv_cfg = ADVERTISED_100baseT_Full; - break; - default: + adv_cfg = alx_speed_to_ethadv(ethtool_cmd_speed(ecmd), + ecmd->duplex); + + if (!adv_cfg || adv_cfg == ADVERTISED_1000baseT_Full) return -EINVAL; - } } hw->adv_cfg = adv_cfg; diff --git a/drivers/net/ethernet/atheros/alx/hw.c b/drivers/net/ethernet/atheros/alx/hw.c index dc71cfb6ce61..aed48a7f1ca6 100644 --- a/drivers/net/ethernet/atheros/alx/hw.c +++ b/drivers/net/ethernet/atheros/alx/hw.c @@ -624,12 +624,12 @@ void alx_start_mac(struct alx_hw *hw) alx_write_mem32(hw, ALX_TXQ0, txq | ALX_TXQ0_EN); mac = hw->rx_ctrl; - if (hw->link_speed % 10 == DUPLEX_FULL) + if (hw->duplex == DUPLEX_FULL) mac |= ALX_MAC_CTRL_FULLD; else mac &= ~ALX_MAC_CTRL_FULLD; ALX_SET_FIELD(mac, ALX_MAC_CTRL_SPEED, - hw->link_speed >= SPEED_1000 ? ALX_MAC_CTRL_SPEED_1000 : + hw->link_speed == SPEED_1000 ? ALX_MAC_CTRL_SPEED_1000 : ALX_MAC_CTRL_SPEED_10_100); mac |= ALX_MAC_CTRL_TX_EN | ALX_MAC_CTRL_RX_EN; hw->rx_ctrl = mac; @@ -790,28 +790,22 @@ void alx_post_phy_link(struct alx_hw *hw) u16 phy_val, len, agc; u8 revid = alx_hw_revision(hw); bool adj_th = revid == ALX_REV_B0; - int speed; - - if (hw->link_speed == SPEED_UNKNOWN) - speed = SPEED_UNKNOWN; - else - speed = hw->link_speed - hw->link_speed % 10; if (revid != ALX_REV_B0 && !alx_is_rev_a(revid)) return; /* 1000BT/AZ, wrong cable length */ - if (speed != SPEED_UNKNOWN) { + if (hw->link_speed != SPEED_UNKNOWN) { alx_read_phy_ext(hw, ALX_MIIEXT_PCS, ALX_MIIEXT_CLDCTRL6, &phy_val); len = ALX_GET_FIELD(phy_val, ALX_CLDCTRL6_CAB_LEN); alx_read_phy_dbg(hw, ALX_MIIDBG_AGC, &phy_val); agc = ALX_GET_FIELD(phy_val, ALX_AGC_2_VGA); - if ((speed == SPEED_1000 && + if ((hw->link_speed == SPEED_1000 && (len > ALX_CLDCTRL6_CAB_LEN_SHORT1G || (len == 0 && agc > ALX_AGC_LONG1G_LIMT))) || - (speed == SPEED_100 && + (hw->link_speed == SPEED_100 && (len > ALX_CLDCTRL6_CAB_LEN_SHORT100M || (len == 0 && agc > ALX_AGC_LONG100M_LIMT)))) { alx_write_phy_dbg(hw, ALX_MIIDBG_AZ_ANADECT, @@ -831,10 +825,10 @@ void alx_post_phy_link(struct alx_hw *hw) /* threshold adjust */ if (adj_th && hw->lnk_patch) { - if (speed == SPEED_100) { + if (hw->link_speed == SPEED_100) { alx_write_phy_dbg(hw, ALX_MIIDBG_MSE16DB, ALX_MSE16DB_UP); - } else if (speed == SPEED_1000) { + } else if (hw->link_speed == SPEED_1000) { /* * Giga link threshold, raise the tolerance of * noise 50% @@ -869,7 +863,7 @@ void alx_post_phy_link(struct alx_hw *hw) * 1. phy link must be established before calling this function * 2. wol option (pattern,magic,link,etc.) is configed before call it. */ -int alx_pre_suspend(struct alx_hw *hw, int speed) +int alx_pre_suspend(struct alx_hw *hw, int speed, u8 duplex) { u32 master, mac, phy, val; int err = 0; @@ -897,9 +891,9 @@ int alx_pre_suspend(struct alx_hw *hw, int speed) mac |= ALX_MAC_CTRL_RX_EN | ALX_MAC_CTRL_BRD_EN; if (hw->sleep_ctrl & ALX_SLEEP_CIFS) mac |= ALX_MAC_CTRL_TX_EN; - if (speed % 10 == DUPLEX_FULL) + if (duplex == DUPLEX_FULL) mac |= ALX_MAC_CTRL_FULLD; - if (speed >= SPEED_1000) + if (speed == SPEED_1000) ALX_SET_FIELD(mac, ALX_MAC_CTRL_SPEED, ALX_MAC_CTRL_SPEED_1000); phy |= ALX_PHY_CTRL_DSPRST_OUT; @@ -938,7 +932,7 @@ bool alx_phy_configured(struct alx_hw *hw) return cfg == hw_cfg; } -int alx_get_phy_link(struct alx_hw *hw, int *speed) +int alx_read_phy_link(struct alx_hw *hw) { struct pci_dev *pdev = hw->pdev; u16 bmsr, giga; @@ -953,7 +947,8 @@ int alx_get_phy_link(struct alx_hw *hw, int *speed) return err; if (!(bmsr & BMSR_LSTATUS)) { - *speed = SPEED_UNKNOWN; + hw->link_speed = SPEED_UNKNOWN; + hw->duplex = DUPLEX_UNKNOWN; return 0; } @@ -967,20 +962,20 @@ int alx_get_phy_link(struct alx_hw *hw, int *speed) switch (giga & ALX_GIGA_PSSR_SPEED) { case ALX_GIGA_PSSR_1000MBS: - *speed = SPEED_1000; + hw->link_speed = SPEED_1000; break; case ALX_GIGA_PSSR_100MBS: - *speed = SPEED_100; + hw->link_speed = SPEED_100; break; case ALX_GIGA_PSSR_10MBS: - *speed = SPEED_10; + hw->link_speed = SPEED_10; break; default: goto wrong_speed; } - *speed += (giga & ALX_GIGA_PSSR_DPLX) ? DUPLEX_FULL : DUPLEX_HALF; - return 1; + hw->duplex = (giga & ALX_GIGA_PSSR_DPLX) ? DUPLEX_FULL : DUPLEX_HALF; + return 0; wrong_speed: dev_err(&pdev->dev, "invalid PHY speed/duplex: 0x%x\n", giga); @@ -1126,81 +1121,67 @@ void alx_configure_basic(struct alx_hw *hw) alx_write_mem32(hw, ALX_WRR, val); } -static inline u32 alx_speed_to_ethadv(int speed) -{ - switch (speed) { - case SPEED_1000 + DUPLEX_FULL: - return ADVERTISED_1000baseT_Full; - case SPEED_100 + DUPLEX_FULL: - return ADVERTISED_100baseT_Full; - case SPEED_100 + DUPLEX_HALF: - return ADVERTISED_100baseT_Half; - case SPEED_10 + DUPLEX_FULL: - return ADVERTISED_10baseT_Full; - case SPEED_10 + DUPLEX_HALF: - return ADVERTISED_10baseT_Half; - default: - return 0; - } -} - -int alx_select_powersaving_speed(struct alx_hw *hw, int *speed) +int alx_select_powersaving_speed(struct alx_hw *hw, int *speed, u8 *duplex) { - int i, err, spd; + int i, err; u16 lpa; - err = alx_get_phy_link(hw, &spd); - if (err < 0) + err = alx_read_phy_link(hw); + if (err) return err; - if (spd == SPEED_UNKNOWN) + if (hw->link_speed == SPEED_UNKNOWN) { + *speed = SPEED_UNKNOWN; + *duplex = DUPLEX_UNKNOWN; return 0; + } err = alx_read_phy_reg(hw, MII_LPA, &lpa); if (err) return err; if (!(lpa & LPA_LPACK)) { - *speed = spd; + *speed = hw->link_speed; return 0; } - if (lpa & LPA_10FULL) - *speed = SPEED_10 + DUPLEX_FULL; - else if (lpa & LPA_10HALF) - *speed = SPEED_10 + DUPLEX_HALF; - else if (lpa & LPA_100FULL) - *speed = SPEED_100 + DUPLEX_FULL; - else - *speed = SPEED_100 + DUPLEX_HALF; - - if (*speed != spd) { - err = alx_write_phy_reg(hw, ALX_MII_IER, 0); - if (err) - return err; - err = alx_setup_speed_duplex(hw, - alx_speed_to_ethadv(*speed) | - ADVERTISED_Autoneg, - ALX_FC_ANEG | ALX_FC_RX | - ALX_FC_TX); - if (err) - return err; + if (lpa & LPA_10FULL) { + *speed = SPEED_10; + *duplex = DUPLEX_FULL; + } else if (lpa & LPA_10HALF) { + *speed = SPEED_10; + *duplex = DUPLEX_HALF; + } else if (lpa & LPA_100FULL) { + *speed = SPEED_100; + *duplex = DUPLEX_FULL; + } else { + *speed = SPEED_100; + *duplex = DUPLEX_HALF; + } - /* wait for linkup */ - for (i = 0; i < ALX_MAX_SETUP_LNK_CYCLE; i++) { - int speed2; + if (*speed == hw->link_speed && *duplex == hw->duplex) + return 0; + err = alx_write_phy_reg(hw, ALX_MII_IER, 0); + if (err) + return err; + err = alx_setup_speed_duplex(hw, alx_speed_to_ethadv(*speed, *duplex) | + ADVERTISED_Autoneg, ALX_FC_ANEG | + ALX_FC_RX | ALX_FC_TX); + if (err) + return err; - msleep(100); + /* wait for linkup */ + for (i = 0; i < ALX_MAX_SETUP_LNK_CYCLE; i++) { + msleep(100); - err = alx_get_phy_link(hw, &speed2); - if (err < 0) - return err; - if (speed2 != SPEED_UNKNOWN) - break; - } - if (i == ALX_MAX_SETUP_LNK_CYCLE) - return -ETIMEDOUT; + err = alx_read_phy_link(hw); + if (err < 0) + return err; + if (hw->link_speed != SPEED_UNKNOWN) + break; } + if (i == ALX_MAX_SETUP_LNK_CYCLE) + return -ETIMEDOUT; return 0; } diff --git a/drivers/net/ethernet/atheros/alx/hw.h b/drivers/net/ethernet/atheros/alx/hw.h index 65e723d2172a..a60e35c1c1b5 100644 --- a/drivers/net/ethernet/atheros/alx/hw.h +++ b/drivers/net/ethernet/atheros/alx/hw.h @@ -412,10 +412,11 @@ struct alx_hw { u32 smb_timer; /* SPEED_* + DUPLEX_*, SPEED_UNKNOWN if link is down */ int link_speed; + u8 duplex; /* auto-neg advertisement or force mode config */ - u32 adv_cfg; u8 flowctrl; + u32 adv_cfg; u32 sleep_ctrl; @@ -478,12 +479,12 @@ void alx_reset_pcie(struct alx_hw *hw); void alx_enable_aspm(struct alx_hw *hw, bool l0s_en, bool l1_en); int alx_setup_speed_duplex(struct alx_hw *hw, u32 ethadv, u8 flowctrl); void alx_post_phy_link(struct alx_hw *hw); -int alx_pre_suspend(struct alx_hw *hw, int speed); +int alx_pre_suspend(struct alx_hw *hw, int speed, u8 duplex); int alx_read_phy_reg(struct alx_hw *hw, u16 reg, u16 *phy_data); int alx_write_phy_reg(struct alx_hw *hw, u16 reg, u16 phy_data); int alx_read_phy_ext(struct alx_hw *hw, u8 dev, u16 reg, u16 *pdata); int alx_write_phy_ext(struct alx_hw *hw, u8 dev, u16 reg, u16 data); -int alx_get_phy_link(struct alx_hw *hw, int *speed); +int alx_read_phy_link(struct alx_hw *hw); int alx_clear_phy_intr(struct alx_hw *hw); int alx_config_wol(struct alx_hw *hw); void alx_cfg_mac_flowcontrol(struct alx_hw *hw, u8 fc); @@ -493,7 +494,22 @@ void alx_set_macaddr(struct alx_hw *hw, const u8 *addr); bool alx_phy_configured(struct alx_hw *hw); void alx_configure_basic(struct alx_hw *hw); void alx_disable_rss(struct alx_hw *hw); -int alx_select_powersaving_speed(struct alx_hw *hw, int *speed); +int alx_select_powersaving_speed(struct alx_hw *hw, int *speed, u8 *duplex); bool alx_get_phy_info(struct alx_hw *hw); +static inline u32 alx_speed_to_ethadv(int speed, u8 duplex) +{ + if (speed == SPEED_1000 && duplex == DUPLEX_FULL) + return ADVERTISED_1000baseT_Full; + if (speed == SPEED_100 && duplex == DUPLEX_FULL) + return ADVERTISED_100baseT_Full; + if (speed == SPEED_100 && duplex== DUPLEX_HALF) + return ADVERTISED_100baseT_Half; + if (speed == SPEED_10 && duplex == DUPLEX_FULL) + return ADVERTISED_10baseT_Full; + if (speed == SPEED_10 && duplex == DUPLEX_HALF) + return ADVERTISED_10baseT_Half; + return 0; +} + #endif diff --git a/drivers/net/ethernet/atheros/alx/main.c b/drivers/net/ethernet/atheros/alx/main.c index 418de8b13165..148b4b976474 100644 --- a/drivers/net/ethernet/atheros/alx/main.c +++ b/drivers/net/ethernet/atheros/alx/main.c @@ -712,6 +712,7 @@ static int alx_init_sw(struct alx_priv *alx) hw->dma_chnl = hw->max_dma_chnl; hw->ith_tpd = alx->tx_ringsz / 3; hw->link_speed = SPEED_UNKNOWN; + hw->duplex = DUPLEX_UNKNOWN; hw->adv_cfg = ADVERTISED_Autoneg | ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | @@ -758,6 +759,7 @@ static void alx_halt(struct alx_priv *alx) alx_netif_stop(alx); hw->link_speed = SPEED_UNKNOWN; + hw->duplex = DUPLEX_UNKNOWN; alx_reset_mac(hw); @@ -869,18 +871,18 @@ static void __alx_stop(struct alx_priv *alx) alx_free_rings(alx); } -static const char *alx_speed_desc(u16 speed) +static const char *alx_speed_desc(struct alx_hw *hw) { - switch (speed) { - case SPEED_1000 + DUPLEX_FULL: + switch (alx_speed_to_ethadv(hw->link_speed, hw->duplex)) { + case ADVERTISED_1000baseT_Full: return "1 Gbps Full"; - case SPEED_100 + DUPLEX_FULL: + case ADVERTISED_100baseT_Full: return "100 Mbps Full"; - case SPEED_100 + DUPLEX_HALF: + case ADVERTISED_100baseT_Half: return "100 Mbps Half"; - case SPEED_10 + DUPLEX_FULL: + case ADVERTISED_10baseT_Full: return "10 Mbps Full"; - case SPEED_10 + DUPLEX_HALF: + case ADVERTISED_10baseT_Half: return "10 Mbps Half"; default: return "Unknown speed"; @@ -891,7 +893,8 @@ static void alx_check_link(struct alx_priv *alx) { struct alx_hw *hw = &alx->hw; unsigned long flags; - int speed, old_speed; + int old_speed; + u8 old_duplex; int err; /* clear PHY internal interrupt status, otherwise the main @@ -899,7 +902,9 @@ static void alx_check_link(struct alx_priv *alx) */ alx_clear_phy_intr(hw); - err = alx_get_phy_link(hw, &speed); + old_speed = hw->link_speed; + old_duplex = hw->duplex; + err = alx_read_phy_link(hw); if (err < 0) goto reset; @@ -908,15 +913,12 @@ static void alx_check_link(struct alx_priv *alx) alx_write_mem32(hw, ALX_IMR, alx->int_mask); spin_unlock_irqrestore(&alx->irq_lock, flags); - old_speed = hw->link_speed; - - if (old_speed == speed) + if (old_speed == hw->link_speed) return; - hw->link_speed = speed; - if (speed != SPEED_UNKNOWN) { + if (hw->link_speed != SPEED_UNKNOWN) { netif_info(alx, link, alx->dev, - "NIC Up: %s\n", alx_speed_desc(speed)); + "NIC Up: %s\n", alx_speed_desc(hw)); alx_post_phy_link(hw); alx_enable_aspm(hw, true, true); alx_start_mac(hw); @@ -965,6 +967,7 @@ static int __alx_shutdown(struct pci_dev *pdev, bool *wol_en) struct net_device *netdev = alx->dev; struct alx_hw *hw = &alx->hw; int err, speed; + u8 duplex; netif_device_detach(netdev); @@ -977,13 +980,13 @@ static int __alx_shutdown(struct pci_dev *pdev, bool *wol_en) return err; #endif - err = alx_select_powersaving_speed(hw, &speed); + err = alx_select_powersaving_speed(hw, &speed, &duplex); if (err) return err; err = alx_clear_phy_intr(hw); if (err) return err; - err = alx_pre_suspend(hw, speed); + err = alx_pre_suspend(hw, speed, duplex); if (err) return err; err = alx_config_wol(hw); -- cgit v1.2.3 From 46ab9b347d677976b2b4072837cb4065839c0b80 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 29 Jun 2013 19:23:18 +0200 Subject: alx: fix MAC address alignment problem In two places, parts of MAC addresses are used as u32/u16 values. This can cause alignment problems, use put_unaligned and get_unaligned to fix this. Reported-by: Ben Hutchings Signed-off-by: Johannes Berg Signed-off-by: David S. Miller --- drivers/net/ethernet/atheros/alx/hw.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/atheros/alx/hw.c b/drivers/net/ethernet/atheros/alx/hw.c index aed48a7f1ca6..ea99e5d8743f 100644 --- a/drivers/net/ethernet/atheros/alx/hw.c +++ b/drivers/net/ethernet/atheros/alx/hw.c @@ -282,8 +282,8 @@ static bool alx_read_macaddr(struct alx_hw *hw, u8 *addr) mac1 = alx_read_mem32(hw, ALX_STAD1); /* addr should be big-endian */ - *(__be32 *)(addr + 2) = cpu_to_be32(mac0); - *(__be16 *)addr = cpu_to_be16(mac1); + put_unaligned(cpu_to_be32(mac0), (__be32 *)(addr + 2)); + put_unaligned(cpu_to_be16(mac1), (__be16 *)addr); return is_valid_ether_addr(addr); } @@ -326,9 +326,9 @@ void alx_set_macaddr(struct alx_hw *hw, const u8 *addr) u32 val; /* for example: 00-0B-6A-F6-00-DC * STAD0=6AF600DC, STAD1=000B */ - val = be32_to_cpu(*(__be32 *)(addr + 2)); + val = be32_to_cpu(get_unaligned((__be32 *)(addr + 2))); alx_write_mem32(hw, ALX_STAD0, val); - val = be16_to_cpu(*(__be16 *)addr); + val = be16_to_cpu(get_unaligned((__be16 *)addr)); alx_write_mem32(hw, ALX_STAD1, val); } -- cgit v1.2.3 From 7ec5689461989fd80f1cf82ae084f5d50a5e63ee Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 29 Jun 2013 19:23:19 +0200 Subject: alx: fix ethtool support code A number of places treated features wrongly, listing not-supported features instead of supported ones. Also, the get_drvinfo ethtool callback isn't needed, and alx_get_pauseparam can be simplified. Reported-by: Ben Hutchings Signed-off-by: Johannes Berg Signed-off-by: David S. Miller --- drivers/net/ethernet/atheros/alx/ethtool.c | 64 ++++++++++++++---------------- 1 file changed, 29 insertions(+), 35 deletions(-) diff --git a/drivers/net/ethernet/atheros/alx/ethtool.c b/drivers/net/ethernet/atheros/alx/ethtool.c index 5e19e08b691b..926100626d60 100644 --- a/drivers/net/ethernet/atheros/alx/ethtool.c +++ b/drivers/net/ethernet/atheros/alx/ethtool.c @@ -46,21 +46,37 @@ #include "reg.h" #include "hw.h" +static u32 alx_get_supported_speeds(struct alx_hw *hw) +{ + u32 supported = SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full; + + if (alx_hw_giga(hw)) + supported |= SUPPORTED_1000baseT_Full; + + BUILD_BUG_ON(SUPPORTED_10baseT_Half != ADVERTISED_10baseT_Half); + BUILD_BUG_ON(SUPPORTED_10baseT_Full != ADVERTISED_10baseT_Full); + BUILD_BUG_ON(SUPPORTED_100baseT_Half != ADVERTISED_100baseT_Half); + BUILD_BUG_ON(SUPPORTED_100baseT_Full != ADVERTISED_100baseT_Full); + BUILD_BUG_ON(SUPPORTED_1000baseT_Full != ADVERTISED_1000baseT_Full); + + return supported; +} static int alx_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) { struct alx_priv *alx = netdev_priv(netdev); struct alx_hw *hw = &alx->hw; - ecmd->supported = SUPPORTED_10baseT_Half | - SUPPORTED_10baseT_Full | - SUPPORTED_100baseT_Half | - SUPPORTED_100baseT_Full | - SUPPORTED_Autoneg | + ecmd->supported = SUPPORTED_Autoneg | SUPPORTED_TP | - SUPPORTED_Pause; + SUPPORTED_Pause | + SUPPORTED_Asym_Pause; if (alx_hw_giga(hw)) ecmd->supported |= SUPPORTED_1000baseT_Full; + ecmd->supported |= alx_get_supported_speeds(hw); ecmd->advertising = ADVERTISED_TP; if (hw->adv_cfg & ADVERTISED_Autoneg) @@ -68,6 +84,7 @@ static int alx_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) ecmd->port = PORT_TP; ecmd->phy_address = 0; + if (hw->adv_cfg & ADVERTISED_Autoneg) ecmd->autoneg = AUTONEG_ENABLE; else @@ -100,7 +117,7 @@ static int alx_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) ASSERT_RTNL(); if (ecmd->autoneg == AUTONEG_ENABLE) { - if (ecmd->advertising & ADVERTISED_1000baseT_Half) + if (ecmd->advertising & ~alx_get_supported_speeds(hw)) return -EINVAL; adv_cfg = ecmd->advertising | ADVERTISED_Autoneg; } else { @@ -121,21 +138,10 @@ static void alx_get_pauseparam(struct net_device *netdev, struct alx_priv *alx = netdev_priv(netdev); struct alx_hw *hw = &alx->hw; - if (hw->flowctrl & ALX_FC_ANEG && - hw->adv_cfg & ADVERTISED_Autoneg) - pause->autoneg = AUTONEG_ENABLE; - else - pause->autoneg = AUTONEG_DISABLE; - - if (hw->flowctrl & ALX_FC_TX) - pause->tx_pause = 1; - else - pause->tx_pause = 0; - - if (hw->flowctrl & ALX_FC_RX) - pause->rx_pause = 1; - else - pause->rx_pause = 0; + pause->autoneg = !!(hw->flowctrl & ALX_FC_ANEG && + hw->adv_cfg & ADVERTISED_Autoneg); + pause->tx_pause = !!(hw->flowctrl & ALX_FC_TX); + pause->rx_pause = !!(hw->flowctrl & ALX_FC_RX); } @@ -214,8 +220,7 @@ static int alx_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) struct alx_priv *alx = netdev_priv(netdev); struct alx_hw *hw = &alx->hw; - if (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE | - WAKE_UCAST | WAKE_BCAST | WAKE_MCAST)) + if (wol->wolopts & ~(WAKE_MAGIC | WAKE_PHY)) return -EOPNOTSUPP; hw->sleep_ctrl = 0; @@ -230,22 +235,11 @@ static int alx_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) return 0; } -static void alx_get_drvinfo(struct net_device *netdev, - struct ethtool_drvinfo *drvinfo) -{ - struct alx_priv *alx = netdev_priv(netdev); - - strlcpy(drvinfo->driver, alx_drv_name, sizeof(drvinfo->driver)); - strlcpy(drvinfo->bus_info, pci_name(alx->hw.pdev), - sizeof(drvinfo->bus_info)); -} - const struct ethtool_ops alx_ethtool_ops = { .get_settings = alx_get_settings, .set_settings = alx_set_settings, .get_pauseparam = alx_get_pauseparam, .set_pauseparam = alx_set_pauseparam, - .get_drvinfo = alx_get_drvinfo, .get_msglevel = alx_get_msglevel, .set_msglevel = alx_set_msglevel, .get_wol = alx_get_wol, -- cgit v1.2.3 From fb825a550a1af75323cee9d62d6fb818384c8c95 Mon Sep 17 00:00:00 2001 From: Pravin B Shelar Date: Fri, 28 Jun 2013 16:07:40 -0700 Subject: openvswitch: Add Kconfig dependency on GRE-DEMUX. Openvswitch uses function from NET_IPGRE_DEMUX module. Add Kconfig dependency to fix following compilation errors: http://marc.info/?l=linux-netdev&m=137244035226634 CC: Jesse Gross Reported-by: Randy Dunlap Signed-off-by: Pravin Shelar Acked-by: Randy Dunlap Acked-by: Jesse Gross Signed-off-by: David S. Miller --- net/openvswitch/Kconfig | 16 ++++++++++++++-- net/openvswitch/vport-gre.c | 5 +++-- net/openvswitch/vport.c | 2 +- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/net/openvswitch/Kconfig b/net/openvswitch/Kconfig index 9fbc04a31ed6..27ee56b688a3 100644 --- a/net/openvswitch/Kconfig +++ b/net/openvswitch/Kconfig @@ -19,8 +19,6 @@ config OPENVSWITCH which is able to accept configuration from a variety of sources and translate it into packet processing rules. - Open vSwitch GRE support depends on CONFIG_NET_IPGRE_DEMUX. - See http://openvswitch.org for more information and userspace utilities. @@ -28,3 +26,17 @@ config OPENVSWITCH called openvswitch. If unsure, say N. + +config OPENVSWITCH_GRE + bool "Open vSwitch GRE tunneling support" + depends on INET + depends on OPENVSWITCH + depends on NET_IPGRE_DEMUX && !(OPENVSWITCH=y && NET_IPGRE_DEMUX=m) + default y + ---help--- + If you say Y here, then the Open vSwitch will be able create GRE + vport. + + Say N to exclude this support and reduce the binary size. + + If unsure, say Y. diff --git a/net/openvswitch/vport-gre.c b/net/openvswitch/vport-gre.c index 943e5c431354..493e9775dcda 100644 --- a/net/openvswitch/vport-gre.c +++ b/net/openvswitch/vport-gre.c @@ -16,7 +16,7 @@ * 02110-1301, USA */ -#if IS_ENABLED(CONFIG_NET_IPGRE_DEMUX) +#ifdef CONFIG_OPENVSWITCH_GRE #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include @@ -271,4 +271,5 @@ const struct vport_ops ovs_gre_vport_ops = { .get_name = gre_get_name, .send = gre_tnl_send, }; -#endif + +#endif /* OPENVSWITCH_GRE */ diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c index ba81294219ac..d4c7fa04ce08 100644 --- a/net/openvswitch/vport.c +++ b/net/openvswitch/vport.c @@ -39,7 +39,7 @@ static const struct vport_ops *vport_ops_list[] = { &ovs_netdev_vport_ops, &ovs_internal_vport_ops, -#if IS_ENABLED(CONFIG_NET_IPGRE_DEMUX) +#ifdef CONFIG_OPENVSWITCH_GRE &ovs_gre_vport_ops, #endif }; -- cgit v1.2.3 From 5c29fb12e8fb8a8105ea048cb160fd79a85a52bb Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Thu, 27 Jun 2013 22:46:04 +0200 Subject: ipv6: only apply anti-spoofing checks to not-pointopoint tunnels Because of commit 218774dc341f219bfcf940304a081b121a0e8099 ("ipv6: add anti-spoofing checks for 6to4 and 6rd") the sit driver dropped packets for 2002::/16 destinations and sources even when configured to work as a tunnel with fixed endpoint. We may only apply the 6rd/6to4 anti-spoofing checks if the device is not in pointopoint mode. This was an oversight from me in the above commit, sorry. Thanks to Roman Mamedov for reporting this! Reported-by: Roman Mamedov Cc: David Miller Cc: YOSHIFUJI Hideaki Signed-off-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- net/ipv6/sit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 97a0bfe2c293..85ff37b1ce02 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -593,7 +593,7 @@ static int ipip6_rcv(struct sk_buff *skb) tunnel->dev->stats.rx_errors++; goto out; } - } else { + } else if (!(tunnel->dev->flags&IFF_POINTOPOINT)) { if (is_spoofed_6rd(tunnel, iph->saddr, &ipv6_hdr(skb)->saddr) || is_spoofed_6rd(tunnel, iph->daddr, -- cgit v1.2.3 From 52bd4c0c1551daa2efa7bb9e01a2f4ea6d1311bb Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Fri, 28 Jun 2013 17:35:48 +0200 Subject: ipv6: fix ecmp lookup when oif is specified There is no reason to skip ECMP lookup when oif is specified, but this implies to check oif given by user when selecting another route. When the new route does not match oif requirement, we simply keep the initial one. Spotted-by: dingzhi Signed-off-by: Nicolas Dichtel Signed-off-by: David S. Miller --- net/ipv6/route.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 7ca87b37c0ef..9ff0b78a9c15 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -83,6 +83,7 @@ static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb, u32 mtu); static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb); +static int rt6_score_route(struct rt6_info *rt, int oif, int strict); #ifdef CONFIG_IPV6_ROUTE_INFO static struct rt6_info *rt6_add_route_info(struct net *net, @@ -394,7 +395,8 @@ static int rt6_info_hash_nhsfn(unsigned int candidate_count, } static struct rt6_info *rt6_multipath_select(struct rt6_info *match, - struct flowi6 *fl6) + struct flowi6 *fl6, int oif, + int strict) { struct rt6_info *sibling, *next_sibling; int route_choosen; @@ -408,6 +410,8 @@ static struct rt6_info *rt6_multipath_select(struct rt6_info *match, &match->rt6i_siblings, rt6i_siblings) { route_choosen--; if (route_choosen == 0) { + if (rt6_score_route(sibling, oif, strict) < 0) + break; match = sibling; break; } @@ -743,7 +747,7 @@ restart: rt = fn->leaf; rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags); if (rt->rt6i_nsiblings && fl6->flowi6_oif == 0) - rt = rt6_multipath_select(rt, fl6); + rt = rt6_multipath_select(rt, fl6, fl6->flowi6_oif, flags); BACKTRACK(net, &fl6->saddr); out: dst_use(&rt->dst, jiffies); @@ -875,8 +879,8 @@ restart_2: restart: rt = rt6_select(fn, oif, strict | reachable); - if (rt->rt6i_nsiblings && oif == 0) - rt = rt6_multipath_select(rt, fl6); + if (rt->rt6i_nsiblings) + rt = rt6_multipath_select(rt, fl6, oif, strict | reachable); BACKTRACK(net, &fl6->saddr); if (rt == net->ipv6.ip6_null_entry || rt->rt6i_flags & RTF_CACHE) -- cgit v1.2.3 From 772e42b07fbdc650206746e00cb2914e362594a3 Mon Sep 17 00:00:00 2001 From: Christoph Müllner Date: Thu, 27 Jun 2013 21:18:23 +0200 Subject: net: fec: Fix multicast list setup in fec_restart(). Setup the multicast list of the net_device instead of clearing it blindly. This restores the multicast groups in case of a link down/up event or when resuming from suspend. Signed-off-by: Christoph Muellner Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fec_main.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index ed6180e7db2f..5664acd8e0f4 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -59,6 +59,8 @@ #include "fec.h" +static void set_multicast_list(struct net_device *ndev); + #if defined(CONFIG_ARM) #define FEC_ALIGNMENT 0xf #else @@ -470,9 +472,8 @@ fec_restart(struct net_device *ndev, int duplex) /* Clear any outstanding interrupt. */ writel(0xffc00000, fep->hwp + FEC_IEVENT); - /* Reset all multicast. */ - writel(0, fep->hwp + FEC_GRP_HASH_TABLE_HIGH); - writel(0, fep->hwp + FEC_GRP_HASH_TABLE_LOW); + /* Setup multicast filter. */ + set_multicast_list(ndev); #ifndef CONFIG_M5272 writel(0, fep->hwp + FEC_HASH_TABLE_HIGH); writel(0, fep->hwp + FEC_HASH_TABLE_LOW); -- cgit v1.2.3 From 383eda32b891002d5064cc97243c94b7cf78137a Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 27 Jun 2013 21:57:49 -0700 Subject: xen: Use more current logging styles Instead of mixing printk and pr_ forms, just use pr_ Miscellaneous changes around these conversions: Add a missing newline to avoid message interleaving, coalesce formats, reflow modified lines to 80 columns. Signed-off-by: Joe Perches Acked-by: Ian Campbell Acked-by: Konrad Rzeszutek Wilk Signed-off-by: David S. Miller --- drivers/net/xen-netback/netback.c | 7 +++---- drivers/net/xen-netfront.c | 28 +++++++++++++--------------- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c index 130bcb217d2c..64828de25d9a 100644 --- a/drivers/net/xen-netback/netback.c +++ b/drivers/net/xen-netback/netback.c @@ -1890,9 +1890,8 @@ static int __init netback_init(void) return -ENODEV; if (fatal_skb_slots < XEN_NETBK_LEGACY_SLOTS_MAX) { - printk(KERN_INFO - "xen-netback: fatal_skb_slots too small (%d), bump it to XEN_NETBK_LEGACY_SLOTS_MAX (%d)\n", - fatal_skb_slots, XEN_NETBK_LEGACY_SLOTS_MAX); + pr_info("fatal_skb_slots too small (%d), bump it to XEN_NETBK_LEGACY_SLOTS_MAX (%d)\n", + fatal_skb_slots, XEN_NETBK_LEGACY_SLOTS_MAX); fatal_skb_slots = XEN_NETBK_LEGACY_SLOTS_MAX; } @@ -1921,7 +1920,7 @@ static int __init netback_init(void) "netback/%u", group); if (IS_ERR(netbk->task)) { - printk(KERN_ALERT "kthread_create() fails at netback\n"); + pr_alert("kthread_create() fails at netback\n"); del_timer(&netbk->net_timer); rc = PTR_ERR(netbk->task); goto failed_init; diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index 76a22365d4e9..ff7f111fffee 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -29,6 +29,8 @@ * IN THE SOFTWARE. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -385,9 +387,8 @@ static void xennet_tx_buf_gc(struct net_device *dev) skb = np->tx_skbs[id].skb; if (unlikely(gnttab_query_foreign_access( np->grant_tx_ref[id]) != 0)) { - printk(KERN_ALERT "xennet_tx_buf_gc: warning " - "-- grant still in use by backend " - "domain.\n"); + pr_alert("%s: warning -- grant still in use by backend domain\n", + __func__); BUG(); } gnttab_end_foreign_access_ref( @@ -804,14 +805,14 @@ static int xennet_set_skb_gso(struct sk_buff *skb, { if (!gso->u.gso.size) { if (net_ratelimit()) - printk(KERN_WARNING "GSO size must not be zero.\n"); + pr_warn("GSO size must not be zero\n"); return -EINVAL; } /* Currently only TCPv4 S.O. is supported. */ if (gso->u.gso.type != XEN_NETIF_GSO_TYPE_TCPV4) { if (net_ratelimit()) - printk(KERN_WARNING "Bad GSO type %d.\n", gso->u.gso.type); + pr_warn("Bad GSO type %d\n", gso->u.gso.type); return -EINVAL; } @@ -910,9 +911,8 @@ static int checksum_setup(struct net_device *dev, struct sk_buff *skb) break; default: if (net_ratelimit()) - printk(KERN_ERR "Attempting to checksum a non-" - "TCP/UDP packet, dropping a protocol" - " %d packet", iph->protocol); + pr_err("Attempting to checksum a non-TCP/UDP packet, dropping a protocol %d packet\n", + iph->protocol); goto out; } @@ -1359,14 +1359,14 @@ static struct net_device *xennet_create_dev(struct xenbus_device *dev) /* A grant for every tx ring slot */ if (gnttab_alloc_grant_references(TX_MAX_TARGET, &np->gref_tx_head) < 0) { - printk(KERN_ALERT "#### netfront can't alloc tx grant refs\n"); + pr_alert("can't alloc tx grant refs\n"); err = -ENOMEM; goto exit_free_stats; } /* A grant for every rx ring slot */ if (gnttab_alloc_grant_references(RX_MAX_TARGET, &np->gref_rx_head) < 0) { - printk(KERN_ALERT "#### netfront can't alloc rx grant refs\n"); + pr_alert("can't alloc rx grant refs\n"); err = -ENOMEM; goto exit_free_tx; } @@ -1430,16 +1430,14 @@ static int netfront_probe(struct xenbus_device *dev, err = register_netdev(info->netdev); if (err) { - printk(KERN_WARNING "%s: register_netdev err=%d\n", - __func__, err); + pr_warn("%s: register_netdev err=%d\n", __func__, err); goto fail; } err = xennet_sysfs_addif(info->netdev); if (err) { unregister_netdev(info->netdev); - printk(KERN_WARNING "%s: add sysfs failed err=%d\n", - __func__, err); + pr_warn("%s: add sysfs failed err=%d\n", __func__, err); goto fail; } @@ -2116,7 +2114,7 @@ static int __init netif_init(void) if (xen_hvm_domain() && !xen_platform_pci_unplug) return -ENODEV; - printk(KERN_INFO "Initialising Xen virtual ethernet driver.\n"); + pr_info("Initialising Xen virtual ethernet driver\n"); return xenbus_register_frontend(&netfront_driver); } -- cgit v1.2.3 From 0d909dcdeffeee3c42703dc1d17766d94fd47b71 Mon Sep 17 00:00:00 2001 From: Byungho An Date: Fri, 28 Jun 2013 16:35:31 +0900 Subject: net: stmmac: fixed operator typo This patch fixed operator typo from & to ==. Due to incorrect operator, the result is incorrect. Signed-off-by: Byungho An Acked-by: Giuseppe Cavallaro Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index ee919ca8b8a0..4fb74a07c01c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -788,13 +788,13 @@ static void stmmac_check_pcs_mode(struct stmmac_priv *priv) int interface = priv->plat->interface; if (priv->dma_cap.pcs) { - if ((interface & PHY_INTERFACE_MODE_RGMII) || - (interface & PHY_INTERFACE_MODE_RGMII_ID) || - (interface & PHY_INTERFACE_MODE_RGMII_RXID) || - (interface & PHY_INTERFACE_MODE_RGMII_TXID)) { + if ((interface == PHY_INTERFACE_MODE_RGMII) || + (interface == PHY_INTERFACE_MODE_RGMII_ID) || + (interface == PHY_INTERFACE_MODE_RGMII_RXID) || + (interface == PHY_INTERFACE_MODE_RGMII_TXID)) { pr_debug("STMMAC: PCS RGMII support enable\n"); priv->pcs = STMMAC_PCS_RGMII; - } else if (interface & PHY_INTERFACE_MODE_SGMII) { + } else if (interface == PHY_INTERFACE_MODE_SGMII) { pr_debug("STMMAC: PCS SGMII support enable\n"); priv->pcs = STMMAC_PCS_SGMII; } -- cgit v1.2.3 From 61369d0259a724606f3941a46d2c75155d66cf2c Mon Sep 17 00:00:00 2001 From: Byungho An Date: Fri, 28 Jun 2013 16:35:32 +0900 Subject: net: stmmac: fixed enh_desc set always zero This patch fixed that enh_desc value is always zero. Due to calling order of stmmac_selec_desc_mode(), enh_desc value is always zero. Even though mac is set to use enhanced dma descriptor, if enh_desc is zero, functions related dma descriptor are not working correctly. Signed-off-by: Byungho An Acked-by: Giuseppe Cavallaro Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 4fb74a07c01c..520693385d8d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -2566,9 +2566,6 @@ static int stmmac_hw_init(struct stmmac_priv *priv) /* Get and dump the chip ID */ priv->synopsys_id = stmmac_get_synopsys_id(priv); - /* To use alternate (extended) or normal descriptor structures */ - stmmac_selec_desc_mode(priv); - /* To use the chained or ring mode */ if (chain_mode) { priv->hw->chain = &chain_mode_ops; @@ -2603,6 +2600,9 @@ static int stmmac_hw_init(struct stmmac_priv *priv) } else pr_info(" No HW DMA feature register supported"); + /* To use alternate (extended) or normal descriptor structures */ + stmmac_selec_desc_mode(priv); + ret = priv->hw->mac->rx_ipc(priv->ioaddr); if (!ret) { pr_warn(" RX IPC Checksum Offload not configured.\n"); -- cgit v1.2.3 From c9ab4d85de222f3390c67aedc9c18a50e767531e Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 28 Jun 2013 02:37:42 -0700 Subject: neighbour: fix a race in neigh_destroy() There is a race in neighbour code, because neigh_destroy() uses skb_queue_purge(&neigh->arp_queue) without holding neighbour lock, while other parts of the code assume neighbour rwlock is what protects arp_queue Convert all skb_queue_purge() calls to the __skb_queue_purge() variant Use __skb_queue_head_init() instead of skb_queue_head_init() to make clear we do not use arp_queue.lock And hold neigh->lock in neigh_destroy() to close the race. Reported-by: Joe Jin Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/core/neighbour.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 2569ab2cafbe..b7de821f98df 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -231,7 +231,7 @@ static void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev) we must kill timers etc. and move it to safe state. */ - skb_queue_purge(&n->arp_queue); + __skb_queue_purge(&n->arp_queue); n->arp_queue_len_bytes = 0; n->output = neigh_blackhole; if (n->nud_state & NUD_VALID) @@ -286,7 +286,7 @@ static struct neighbour *neigh_alloc(struct neigh_table *tbl, struct net_device if (!n) goto out_entries; - skb_queue_head_init(&n->arp_queue); + __skb_queue_head_init(&n->arp_queue); rwlock_init(&n->lock); seqlock_init(&n->ha_lock); n->updated = n->used = now; @@ -708,7 +708,9 @@ void neigh_destroy(struct neighbour *neigh) if (neigh_del_timer(neigh)) pr_warn("Impossible event\n"); - skb_queue_purge(&neigh->arp_queue); + write_lock_bh(&neigh->lock); + __skb_queue_purge(&neigh->arp_queue); + write_unlock_bh(&neigh->lock); neigh->arp_queue_len_bytes = 0; if (dev->netdev_ops->ndo_neigh_destroy) @@ -858,7 +860,7 @@ static void neigh_invalidate(struct neighbour *neigh) neigh->ops->error_report(neigh, skb); write_lock(&neigh->lock); } - skb_queue_purge(&neigh->arp_queue); + __skb_queue_purge(&neigh->arp_queue); neigh->arp_queue_len_bytes = 0; } @@ -1210,7 +1212,7 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, write_lock_bh(&neigh->lock); } - skb_queue_purge(&neigh->arp_queue); + __skb_queue_purge(&neigh->arp_queue); neigh->arp_queue_len_bytes = 0; } out: -- cgit v1.2.3 From ad6276e0fe724b1c8ac3a0bf138d151665ab2349 Mon Sep 17 00:00:00 2001 From: Eliezer Tamir Date: Fri, 28 Jun 2013 15:59:26 +0300 Subject: net: fix LLS debug_smp_processor_id() warning Our use of sched_clock is OK because we don't mind the side effects of calling it and occasionally waking up on a different CPU. When CONFIG_DEBUG_PREEMPT is on, disable preempt before calling sched_clock() so we don't trigger a debug_smp_processor_id() warning. Reported-by: Cody P Schafer Signed-off-by: Eliezer Tamir Signed-off-by: David S. Miller --- include/net/ll_poll.h | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/include/net/ll_poll.h b/include/net/ll_poll.h index 5bf2b3a6129e..6d45e6f6ff4c 100644 --- a/include/net/ll_poll.h +++ b/include/net/ll_poll.h @@ -37,20 +37,40 @@ extern unsigned int sysctl_net_ll_poll __read_mostly; #define LL_FLUSH_FAILED -1 #define LL_FLUSH_BUSY -2 -/* we can use sched_clock() because we don't care much about precision +/* a wrapper to make debug_smp_processor_id() happy + * we can use sched_clock() because we don't care much about precision * we only care that the average is bounded - * we don't mind a ~2.5% imprecision so <<10 instead of *1000 + */ +#ifdef CONFIG_DEBUG_PREEMPT +static inline u64 ll_sched_clock(void) +{ + u64 rc; + + preempt_disable_notrace(); + rc = sched_clock(); + preempt_enable_no_resched_notrace(); + + return rc; +} +#else /* CONFIG_DEBUG_PREEMPT */ +static inline u64 ll_sched_clock(void) +{ + return sched_clock(); +} +#endif /* CONFIG_DEBUG_PREEMPT */ + +/* we don't mind a ~2.5% imprecision so <<10 instead of *1000 * sk->sk_ll_usec is a u_int so this can't overflow */ static inline u64 ll_sk_end_time(struct sock *sk) { - return ((u64)ACCESS_ONCE(sk->sk_ll_usec) << 10) + sched_clock(); + return ((u64)ACCESS_ONCE(sk->sk_ll_usec) << 10) + ll_sched_clock(); } /* in poll/select we use the global sysctl_net_ll_poll value */ static inline u64 ll_end_time(void) { - return ((u64)ACCESS_ONCE(sysctl_net_ll_poll) << 10) + sched_clock(); + return ((u64)ACCESS_ONCE(sysctl_net_ll_poll) << 10) + ll_sched_clock(); } static inline bool sk_valid_ll(struct sock *sk) @@ -61,7 +81,7 @@ static inline bool sk_valid_ll(struct sock *sk) static inline bool can_poll_ll(u64 end_time) { - return !time_after64(sched_clock(), end_time); + return !time_after64(ll_sched_clock(), end_time); } /* when used in sock_poll() nonblock is known at compile time to be true -- cgit v1.2.3 From 91e2fd337839319c7745e2cb84cfbf8cf1426a1a Mon Sep 17 00:00:00 2001 From: Eliezer Tamir Date: Fri, 28 Jun 2013 15:59:35 +0300 Subject: net: avoid calling sched_clock when LLS is off Change Low Latency Sockets code for select and poll so that when LLS is disabled sched_clock() is never called. Also, avoid sending POLL_LL to sockets if disabled. Reported-by: Andi Kleen Signed-off-by: Eliezer Tamir Signed-off-by: David S. Miller --- fs/select.c | 11 +++++++---- include/net/ll_poll.h | 17 +++++++++++++++-- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/fs/select.c b/fs/select.c index 79b876eb91da..36540754bad7 100644 --- a/fs/select.c +++ b/fs/select.c @@ -402,7 +402,7 @@ int do_select(int n, fd_set_bits *fds, struct timespec *end_time) poll_table *wait; int retval, i, timed_out = 0; unsigned long slack = 0; - unsigned int ll_flag = POLL_LL; + unsigned int ll_flag = ll_get_flag(); u64 ll_time = ll_end_time(); rcu_read_lock(); @@ -497,7 +497,8 @@ int do_select(int n, fd_set_bits *fds, struct timespec *end_time) break; } - if (can_ll && can_poll_ll(ll_time)) + /* only if on, have sockets with POLL_LL and not out of time */ + if (ll_flag && can_ll && can_poll_ll(ll_time)) continue; /* @@ -768,7 +769,7 @@ static int do_poll(unsigned int nfds, struct poll_list *list, ktime_t expire, *to = NULL; int timed_out = 0, count = 0; unsigned long slack = 0; - unsigned int ll_flag = POLL_LL; + unsigned int ll_flag = ll_get_flag(); u64 ll_time = ll_end_time(); /* Optimise the no-wait case */ @@ -817,8 +818,10 @@ static int do_poll(unsigned int nfds, struct poll_list *list, if (count || timed_out) break; - if (can_ll && can_poll_ll(ll_time)) + /* only if on, have sockets with POLL_LL and not out of time */ + if (ll_flag && can_ll && can_poll_ll(ll_time)) continue; + /* * If this is the first loop and we have a timeout * given, then we convert to ktime_t and set the to diff --git a/include/net/ll_poll.h b/include/net/ll_poll.h index 6d45e6f6ff4c..6c06f7c0a22d 100644 --- a/include/net/ll_poll.h +++ b/include/net/ll_poll.h @@ -37,6 +37,11 @@ extern unsigned int sysctl_net_ll_poll __read_mostly; #define LL_FLUSH_FAILED -1 #define LL_FLUSH_BUSY -2 +static inline unsigned int ll_get_flag(void) +{ + return sysctl_net_ll_poll ? POLL_LL : 0; +} + /* a wrapper to make debug_smp_processor_id() happy * we can use sched_clock() because we don't care much about precision * we only care that the average is bounded @@ -67,10 +72,14 @@ static inline u64 ll_sk_end_time(struct sock *sk) return ((u64)ACCESS_ONCE(sk->sk_ll_usec) << 10) + ll_sched_clock(); } -/* in poll/select we use the global sysctl_net_ll_poll value */ +/* in poll/select we use the global sysctl_net_ll_poll value + * only call sched_clock() if enabled + */ static inline u64 ll_end_time(void) { - return ((u64)ACCESS_ONCE(sysctl_net_ll_poll) << 10) + ll_sched_clock(); + u64 end_time = ACCESS_ONCE(sysctl_net_ll_poll); + + return end_time ? (end_time << 10) + ll_sched_clock() : 0; } static inline bool sk_valid_ll(struct sock *sk) @@ -141,6 +150,10 @@ static inline void sk_mark_ll(struct sock *sk, struct sk_buff *skb) } #else /* CONFIG_NET_LL_RX_POLL */ +static inline unsigned long ll_get_flag(void) +{ + return 0; +} static inline u64 sk_ll_end_time(struct sock *sk) { -- cgit v1.2.3 From 442340639194762df7e61e8aabae44a18896eca1 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 14 May 2013 16:53:40 -0400 Subject: svcrpc: introduce init_svc_cred Common helper to zero out fields of the svc_cred. Signed-off-by: J. Bruce Fields --- include/linux/sunrpc/svcauth.h | 7 +++++++ net/sunrpc/auth_gss/svcauth_gss.c | 6 ++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/include/linux/sunrpc/svcauth.h b/include/linux/sunrpc/svcauth.h index ff374ab30839..95c956613a9c 100644 --- a/include/linux/sunrpc/svcauth.h +++ b/include/linux/sunrpc/svcauth.h @@ -25,6 +25,13 @@ struct svc_cred { char *cr_principal; /* for gss */ }; +static inline void init_svc_cred(struct svc_cred *cred) +{ + cred->cr_group_info = NULL; + cred->cr_principal = NULL; + cred->cr_gss_mech = NULL; +} + static inline void free_svc_cred(struct svc_cred *cred) { if (cred->cr_group_info) diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index 29b4ba93ab3c..8d7860ee2398 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -377,8 +377,7 @@ rsc_init(struct cache_head *cnew, struct cache_head *ctmp) new->handle.data = tmp->handle.data; tmp->handle.data = NULL; new->mechctx = NULL; - new->cred.cr_group_info = NULL; - new->cred.cr_principal = NULL; + init_svc_cred(&new->cred); } static void @@ -392,9 +391,8 @@ update_rsc(struct cache_head *cnew, struct cache_head *ctmp) memset(&new->seqdata, 0, sizeof(new->seqdata)); spin_lock_init(&new->seqdata.sd_lock); new->cred = tmp->cred; - tmp->cred.cr_group_info = NULL; new->cred.cr_principal = tmp->cred.cr_principal; - tmp->cred.cr_principal = NULL; + init_svc_cred(&tmp->cred); } static struct cache_head * -- cgit v1.2.3 From 0dc1531aca7fd1440918bd55844a054e9c29acad Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 14 May 2013 16:07:13 -0400 Subject: svcrpc: store gss mech in svc_cred Store a pointer to the gss mechanism used in the rq_cred and cl_cred. This will make it easier to enforce SP4_MACH_CRED, which needs to compare the mechanism used on the exchange_id with that used on protected operations. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 3 +++ include/linux/sunrpc/gss_api.h | 2 ++ include/linux/sunrpc/svcauth.h | 4 ++++ net/sunrpc/auth_gss/gss_mech_switch.c | 5 ++++- net/sunrpc/auth_gss/svcauth_gss.c | 4 +--- 5 files changed, 14 insertions(+), 4 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 72f0c4e9a942..109b43402e82 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1188,6 +1188,9 @@ static int copy_cred(struct svc_cred *target, struct svc_cred *source) target->cr_gid = source->cr_gid; target->cr_group_info = source->cr_group_info; get_group_info(target->cr_group_info); + target->cr_gss_mech = source->cr_gss_mech; + if (source->cr_gss_mech) + gss_mech_get(source->cr_gss_mech); return 0; } diff --git a/include/linux/sunrpc/gss_api.h b/include/linux/sunrpc/gss_api.h index 161463e59624..1f911ccb2a75 100644 --- a/include/linux/sunrpc/gss_api.h +++ b/include/linux/sunrpc/gss_api.h @@ -151,6 +151,8 @@ struct gss_api_mech *gss_mech_get_by_pseudoflavor(u32); /* Fill in an array with a list of supported pseudoflavors */ int gss_mech_list_pseudoflavors(rpc_authflavor_t *, int); +struct gss_api_mech * gss_mech_get(struct gss_api_mech *); + /* For every successful gss_mech_get or gss_mech_get_by_* call there must be a * corresponding call to gss_mech_put. */ void gss_mech_put(struct gss_api_mech *); diff --git a/include/linux/sunrpc/svcauth.h b/include/linux/sunrpc/svcauth.h index 95c956613a9c..8d71d6577459 100644 --- a/include/linux/sunrpc/svcauth.h +++ b/include/linux/sunrpc/svcauth.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -23,6 +24,7 @@ struct svc_cred { struct group_info *cr_group_info; u32 cr_flavor; /* pseudoflavor */ char *cr_principal; /* for gss */ + struct gss_api_mech *cr_gss_mech; }; static inline void init_svc_cred(struct svc_cred *cred) @@ -37,6 +39,8 @@ static inline void free_svc_cred(struct svc_cred *cred) if (cred->cr_group_info) put_group_info(cred->cr_group_info); kfree(cred->cr_principal); + gss_mech_put(cred->cr_gss_mech); + init_svc_cred(cred); } struct svc_rqst; /* forward decl */ diff --git a/net/sunrpc/auth_gss/gss_mech_switch.c b/net/sunrpc/auth_gss/gss_mech_switch.c index defa9d33925c..27ce26240932 100644 --- a/net/sunrpc/auth_gss/gss_mech_switch.c +++ b/net/sunrpc/auth_gss/gss_mech_switch.c @@ -139,11 +139,12 @@ void gss_mech_unregister(struct gss_api_mech *gm) } EXPORT_SYMBOL_GPL(gss_mech_unregister); -static struct gss_api_mech *gss_mech_get(struct gss_api_mech *gm) +struct gss_api_mech *gss_mech_get(struct gss_api_mech *gm) { __module_get(gm->gm_owner); return gm; } +EXPORT_SYMBOL(gss_mech_get); static struct gss_api_mech * _gss_mech_get_by_name(const char *name) @@ -360,6 +361,7 @@ gss_pseudoflavor_to_service(struct gss_api_mech *gm, u32 pseudoflavor) } return 0; } +EXPORT_SYMBOL(gss_pseudoflavor_to_service); char * gss_service_to_auth_domain_name(struct gss_api_mech *gm, u32 service) @@ -379,6 +381,7 @@ gss_mech_put(struct gss_api_mech * gm) if (gm) module_put(gm->gm_owner); } +EXPORT_SYMBOL(gss_mech_put); /* The mech could probably be determined from the token instead, but it's just * as easy for now to pass it in. */ diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index 8d7860ee2398..0265bb3a8689 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -391,7 +391,6 @@ update_rsc(struct cache_head *cnew, struct cache_head *ctmp) memset(&new->seqdata, 0, sizeof(new->seqdata)); spin_lock_init(&new->seqdata.sd_lock); new->cred = tmp->cred; - new->cred.cr_principal = tmp->cred.cr_principal; init_svc_cred(&tmp->cred); } @@ -485,7 +484,7 @@ static int rsc_parse(struct cache_detail *cd, len = qword_get(&mesg, buf, mlen); if (len < 0) goto out; - gm = gss_mech_get_by_name(buf); + gm = rsci.cred.cr_gss_mech = gss_mech_get_by_name(buf); status = -EOPNOTSUPP; if (!gm) goto out; @@ -515,7 +514,6 @@ static int rsc_parse(struct cache_detail *cd, rscp = rsc_update(cd, &rsci, rscp); status = 0; out: - gss_mech_put(gm); rsc_free(&rsci); if (rscp) cache_put(&rscp->h, cd); -- cgit v1.2.3 From 57266a6e916e2522ea61758a3ee5576b60156791 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Sat, 13 Apr 2013 14:27:29 -0400 Subject: nfsd4: implement minimal SP4_MACH_CRED Do a minimal SP4_MACH_CRED implementation suggested by Trond, ignoring the client-provided spo_must_* arrays and just enforcing credential checks for the minimum required operations. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 76 ++++++++++++++++++++++++++++++++++++++++++++--------- fs/nfsd/nfs4xdr.c | 22 ++++++++++++++++ fs/nfsd/state.h | 1 + 3 files changed, 87 insertions(+), 12 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 109b43402e82..2383d24e258f 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1265,6 +1265,31 @@ same_creds(struct svc_cred *cr1, struct svc_cred *cr2) return 0 == strcmp(cr1->cr_principal, cr2->cr_principal); } +static bool svc_rqst_integrity_protected(struct svc_rqst *rqstp) +{ + struct svc_cred *cr = &rqstp->rq_cred; + u32 service; + + service = gss_pseudoflavor_to_service(cr->cr_gss_mech, cr->cr_flavor); + return service == RPC_GSS_SVC_INTEGRITY || + service == RPC_GSS_SVC_PRIVACY; +} + +static bool mach_creds_match(struct nfs4_client *cl, struct svc_rqst *rqstp) +{ + struct svc_cred *cr = &rqstp->rq_cred; + + if (!cl->cl_mach_cred) + return true; + if (cl->cl_cred.cr_gss_mech != cr->cr_gss_mech) + return false; + if (!svc_rqst_integrity_protected(rqstp)) + return false; + if (!cr->cr_principal) + return false; + return 0 == strcmp(cl->cl_cred.cr_principal, cr->cr_principal); +} + static void gen_clid(struct nfs4_client *clp, struct nfsd_net *nn) { static u32 current_clientid = 1; @@ -1642,16 +1667,16 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, if (exid->flags & ~EXCHGID4_FLAG_MASK_A) return nfserr_inval; - /* Currently only support SP4_NONE */ switch (exid->spa_how) { + case SP4_MACH_CRED: + if (!svc_rqst_integrity_protected(rqstp)) + return nfserr_inval; case SP4_NONE: break; default: /* checked by xdr code */ WARN_ON_ONCE(1); case SP4_SSV: return nfserr_encr_alg_unsupp; - case SP4_MACH_CRED: - return nfserr_serverfault; /* no excuse :-/ */ } /* Cases below refer to rfc 5661 section 18.35.4: */ @@ -1666,6 +1691,10 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, status = nfserr_inval; goto out; } + if (!mach_creds_match(conf, rqstp)) { + status = nfserr_wrong_cred; + goto out; + } if (!creds_match) { /* case 9 */ status = nfserr_perm; goto out; @@ -1713,6 +1742,7 @@ out_new: goto out; } new->cl_minorversion = cstate->minorversion; + new->cl_mach_cred = (exid->spa_how == SP4_MACH_CRED); gen_clid(new, nn); add_to_unconfirmed(new); @@ -1877,6 +1907,9 @@ nfsd4_create_session(struct svc_rqst *rqstp, WARN_ON_ONCE(conf && unconf); if (conf) { + status = nfserr_wrong_cred; + if (!mach_creds_match(conf, rqstp)) + goto out_free_conn; cs_slot = &conf->cl_cs_slot; status = check_slot_seqid(cr_ses->seqid, cs_slot->sl_seqid, 0); if (status == nfserr_replay_cache) { @@ -1893,6 +1926,9 @@ nfsd4_create_session(struct svc_rqst *rqstp, status = nfserr_clid_inuse; goto out_free_conn; } + status = nfserr_wrong_cred; + if (!mach_creds_match(unconf, rqstp)) + goto out_free_conn; cs_slot = &unconf->cl_cs_slot; status = check_slot_seqid(cr_ses->seqid, cs_slot->sl_seqid, 0); if (status) { @@ -1989,6 +2025,9 @@ __be32 nfsd4_bind_conn_to_session(struct svc_rqst *rqstp, status = nfserr_badsession; if (!session) goto out; + status = nfserr_wrong_cred; + if (!mach_creds_match(session->se_client, rqstp)) + goto out; status = nfsd4_map_bcts_dir(&bcts->dir); if (status) goto out; @@ -2031,6 +2070,9 @@ nfsd4_destroy_session(struct svc_rqst *r, status = nfserr_badsession; if (!ses) goto out_client_lock; + status = nfserr_wrong_cred; + if (!mach_creds_match(ses->se_client, r)) + goto out_client_lock; status = mark_session_dead_locked(ses); if (status) goto out_client_lock; @@ -2061,26 +2103,31 @@ static struct nfsd4_conn *__nfsd4_find_conn(struct svc_xprt *xpt, struct nfsd4_s return NULL; } -static void nfsd4_sequence_check_conn(struct nfsd4_conn *new, struct nfsd4_session *ses) +static __be32 nfsd4_sequence_check_conn(struct nfsd4_conn *new, struct nfsd4_session *ses) { struct nfs4_client *clp = ses->se_client; struct nfsd4_conn *c; + __be32 status = nfs_ok; int ret; spin_lock(&clp->cl_lock); c = __nfsd4_find_conn(new->cn_xprt, ses); - if (c) { - spin_unlock(&clp->cl_lock); - free_conn(new); - return; - } + if (c) + goto out_free; + status = nfserr_conn_not_bound_to_session; + if (clp->cl_mach_cred) + goto out_free; __nfsd4_hash_conn(new, ses); spin_unlock(&clp->cl_lock); ret = nfsd4_register_conn(new); if (ret) /* oops; xprt is already down: */ nfsd4_conn_lost(&new->cn_xpt_user); - return; + return nfs_ok; +out_free: + spin_unlock(&clp->cl_lock); + free_conn(new); + return status; } static bool nfsd4_session_too_many_ops(struct svc_rqst *rqstp, struct nfsd4_session *session) @@ -2172,8 +2219,10 @@ nfsd4_sequence(struct svc_rqst *rqstp, if (status) goto out_put_session; - nfsd4_sequence_check_conn(conn, session); + status = nfsd4_sequence_check_conn(conn, session); conn = NULL; + if (status) + goto out_put_session; /* Success! bump slot seqid */ slot->sl_seqid = seq->seqid; @@ -2235,7 +2284,10 @@ nfsd4_destroy_clientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *csta status = nfserr_stale_clientid; goto out; } - + if (!mach_creds_match(clp, rqstp)) { + status = nfserr_wrong_cred; + goto out; + } expire_client(clp); out: nfs4_unlock_state(); diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 170ea7e1ae25..3126210383bd 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -3321,6 +3321,14 @@ nfsd4_encode_write(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_w return nfserr; } +static const u32 nfs4_minimal_spo_must_enforce[2] = { + [1] = 1 << (OP_BIND_CONN_TO_SESSION - 32) | + 1 << (OP_EXCHANGE_ID - 32) | + 1 << (OP_CREATE_SESSION - 32) | + 1 << (OP_DESTROY_SESSION - 32) | + 1 << (OP_DESTROY_CLIENTID - 32) +}; + static __be32 nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_exchange_id *exid) @@ -3359,6 +3367,20 @@ nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr, /* state_protect4_r. Currently only support SP4_NONE */ BUG_ON(exid->spa_how != SP4_NONE); WRITE32(exid->spa_how); + switch (exid->spa_how) { + case SP4_NONE: + break; + case SP4_MACH_CRED: + /* spo_must_enforce bitmap: */ + WRITE32(2); + WRITE32(nfs4_minimal_spo_must_enforce[0]); + WRITE32(nfs4_minimal_spo_must_enforce[1]); + /* empty spo_must_allow bitmap: */ + WRITE32(0); + break; + default: + WARN_ON_ONCE(1); + } /* The server_owner struct */ WRITE64(minor_id); /* Minor id */ diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 274e2a114e05..424d8f5f2317 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -246,6 +246,7 @@ struct nfs4_client { nfs4_verifier cl_verifier; /* generated by client */ time_t cl_time; /* time of last lease renewal */ struct sockaddr_storage cl_addr; /* client ipaddress */ + bool cl_mach_cred; /* SP4_MACH_CRED in force */ struct svc_cred cl_cred; /* setclientid principal */ clientid_t cl_clientid; /* generated by server */ nfs4_verifier cl_confirm; /* generated by server */ -- cgit v1.2.3 From b78724b70599f66a91c6d6c897a81f4f87f549f4 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 15 May 2013 17:34:39 -0400 Subject: nfsd4: fail attempts to request gss on the backchannel We don't support gss on the backchannel. We should state that fact up front rather than just letting things continue and later making the client try to figure out why the backchannel isn't working. Trond suggested instead returning NFS4ERR_NOENT. I think it would be tricky for the client to distinguish between the case "I don't support gss on the backchannel" and "I can't find that in my cache, please create another context and try that instead", and I'd prefer something that currently doesn't have any other meaning for this operation, hence the (somewhat arbitrary) NFS4ERR_ENCR_ALG_UNSUPP. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 2383d24e258f..c4f63399832c 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1872,6 +1872,24 @@ static __be32 check_backchannel_attrs(struct nfsd4_channel_attrs *ca) return nfs_ok; } +static __be32 nfsd4_check_cb_sec(struct nfsd4_cb_sec *cbs) +{ + switch (cbs->flavor) { + case RPC_AUTH_NULL: + case RPC_AUTH_UNIX: + return nfs_ok; + default: + /* + * GSS case: the spec doesn't allow us to return this + * error. But it also doesn't allow us not to support + * GSS. + * I'd rather this fail hard than return some error the + * client might think it can already handle: + */ + return nfserr_encr_alg_unsupp; + } +} + __be32 nfsd4_create_session(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, @@ -1887,6 +1905,9 @@ nfsd4_create_session(struct svc_rqst *rqstp, if (cr_ses->flags & ~SESSION4_FLAG_MASK_A) return nfserr_inval; + status = nfsd4_check_cb_sec(&cr_ses->cb_sec); + if (status) + return status; status = check_forechannel_attrs(&cr_ses->fore_channel, nn); if (status) return status; @@ -1996,7 +2017,11 @@ __be32 nfsd4_backchannel_ctl(struct svc_rqst *rqstp, struct nfsd4_compound_state { struct nfsd4_session *session = cstate->session; struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); + __be32 status; + status = nfsd4_check_cb_sec(&bc->bc_cb_sec); + if (status) + return status; spin_lock(&nn->client_lock); session->se_cb_prog = bc->bc_cb_program; session->se_cb_sec = bc->bc_cb_sec; -- cgit v1.2.3 From 57569a707082a337e4a61a657521d79cac3528bf Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 17 May 2013 16:25:32 -0400 Subject: nfsd4: allow client to send no cb_sec flavors In testing I notice that some of the pynfs tests forget to send any cb_sec flavors, and that we haven't necessarily errored out in that case before. I'll fix pynfs, but am also inclined to default to trying AUTH_NONE in that case in case this is something clients actually do. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4xdr.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 3126210383bd..171fe5e4af67 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -460,7 +460,11 @@ static __be32 nfsd4_decode_cb_sec(struct nfsd4_compoundargs *argp, struct nfsd4_ /* callback_sec_params4 */ READ_BUF(4); READ32(nr_secflavs); - cbs->flavor = (u32)(-1); + if (nr_secflavs) + cbs->flavor = (u32)(-1); + else + /* Is this legal? Be generous, take it to mean AUTH_NONE: */ + cbs->flavor = 0; for (i = 0; i < nr_secflavs; ++i) { READ_BUF(4); READ32(dummy); -- cgit v1.2.3 From 9a0590aec3ca5e86a64b87e004244abc4dd1faf9 Mon Sep 17 00:00:00 2001 From: Steve Dickson Date: Wed, 15 May 2013 14:51:49 -0400 Subject: NFSD: Don't give out read delegations on creates When an exclusive create is done with the mode bits set (aka open(testfile, O_CREAT | O_EXCL, 0777)) this causes a OPEN op followed by a SETATTR op. When a read delegation is given in the OPEN, it causes the SETATTR to delay with EAGAIN until the delegation is recalled. This patch caused exclusive creates to give out a write delegation (which turn into no delegation) which allows the SETATTR seamlessly succeed. Signed-off-by: Steve Dickson [bfields: do this for any CREATE, not just exclusive; comment] Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index c4f63399832c..44dcea96bfcb 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -3113,8 +3113,17 @@ nfs4_open_delegation(struct net *net, struct svc_fh *fh, goto out; if (!cb_up || !(oo->oo_flags & NFS4_OO_CONFIRMED)) goto out; + /* + * Also, if the file was opened for write or + * create, there's a good chance the client's + * about to write to it, resulting in an + * immediate recall (since we don't support + * write delegations): + */ if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE) flag = NFS4_OPEN_DELEGATE_WRITE; + else if (open->op_create == NFS4_OPEN_CREATE) + flag = NFS4_OPEN_DELEGATE_WRITE; else flag = NFS4_OPEN_DELEGATE_READ; break; -- cgit v1.2.3 From 99c415156c49571d0f045a8cb56e0bc24225b9d9 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 21 May 2013 16:21:25 -0400 Subject: nfsd4: clean up nfs4_open_delegation The nfs4_open_delegation logic is unecessarily baroque. Also stop pretending we support write delegations in several places. Some day we will support write delegations, but when that happens adding back in these flag parameters will be the easy part. For now they're just confusing. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 78 +++++++++++++++++++++++------------------------------ 1 file changed, 34 insertions(+), 44 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 44dcea96bfcb..fcc0610fe308 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -364,19 +364,12 @@ static struct nfs4_ol_stateid * nfs4_alloc_stateid(struct nfs4_client *clp) } static struct nfs4_delegation * -alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct svc_fh *current_fh, u32 type) +alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct svc_fh *current_fh) { struct nfs4_delegation *dp; struct nfs4_file *fp = stp->st_file; dprintk("NFSD alloc_init_deleg\n"); - /* - * Major work on the lease subsystem (for example, to support - * calbacks on stat) will be required before we can support - * write delegations properly. - */ - if (type != NFS4_OPEN_DELEGATE_READ) - return NULL; if (fp->fi_had_conflict) return NULL; if (num_delegations > max_delegations) @@ -397,7 +390,7 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct sv INIT_LIST_HEAD(&dp->dl_recall_lru); get_nfs4_file(fp); dp->dl_file = fp; - dp->dl_type = type; + dp->dl_type = NFS4_OPEN_DELEGATE_READ; fh_copy_shallow(&dp->dl_fh, ¤t_fh->fh_handle); dp->dl_time = 0; atomic_set(&dp->dl_count, 1); @@ -3020,13 +3013,13 @@ static struct file_lock *nfs4_alloc_init_lease(struct nfs4_delegation *dp, int f return fl; } -static int nfs4_setlease(struct nfs4_delegation *dp, int flag) +static int nfs4_setlease(struct nfs4_delegation *dp) { struct nfs4_file *fp = dp->dl_file; struct file_lock *fl; int status; - fl = nfs4_alloc_init_lease(dp, flag); + fl = nfs4_alloc_init_lease(dp, NFS4_OPEN_DELEGATE_READ); if (!fl) return -ENOMEM; fl->fl_file = find_readable_file(fp); @@ -3044,12 +3037,12 @@ static int nfs4_setlease(struct nfs4_delegation *dp, int flag) return 0; } -static int nfs4_set_delegation(struct nfs4_delegation *dp, int flag) +static int nfs4_set_delegation(struct nfs4_delegation *dp) { struct nfs4_file *fp = dp->dl_file; if (!fp->fi_lease) - return nfs4_setlease(dp, flag); + return nfs4_setlease(dp); spin_lock(&recall_lock); if (fp->fi_had_conflict) { spin_unlock(&recall_lock); @@ -3085,6 +3078,9 @@ static void nfsd4_open_deleg_none_ext(struct nfsd4_open *open, int status) /* * Attempt to hand out a delegation. + * + * Note we don't support write delegations, and won't until the vfs has + * proper support for them. */ static void nfs4_open_delegation(struct net *net, struct svc_fh *fh, @@ -3093,26 +3089,26 @@ nfs4_open_delegation(struct net *net, struct svc_fh *fh, struct nfs4_delegation *dp; struct nfs4_openowner *oo = container_of(stp->st_stateowner, struct nfs4_openowner, oo_owner); int cb_up; - int status = 0, flag = 0; + int status = 0; cb_up = nfsd4_cb_channel_good(oo->oo_owner.so_client); - flag = NFS4_OPEN_DELEGATE_NONE; open->op_recall = 0; switch (open->op_claim_type) { case NFS4_OPEN_CLAIM_PREVIOUS: if (!cb_up) open->op_recall = 1; - flag = open->op_delegate_type; - if (flag == NFS4_OPEN_DELEGATE_NONE) - goto out; + if (open->op_delegate_type != NFS4_OPEN_DELEGATE_READ) + goto out_no_deleg; break; case NFS4_OPEN_CLAIM_NULL: - /* Let's not give out any delegations till everyone's - * had the chance to reclaim theirs.... */ + /* + * Let's not give out any delegations till everyone's + * had the chance to reclaim theirs.... + */ if (locks_in_grace(net)) - goto out; + goto out_no_deleg; if (!cb_up || !(oo->oo_flags & NFS4_OO_CONFIRMED)) - goto out; + goto out_no_deleg; /* * Also, if the file was opened for write or * create, there's a good chance the client's @@ -3121,20 +3117,17 @@ nfs4_open_delegation(struct net *net, struct svc_fh *fh, * write delegations): */ if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE) - flag = NFS4_OPEN_DELEGATE_WRITE; - else if (open->op_create == NFS4_OPEN_CREATE) - flag = NFS4_OPEN_DELEGATE_WRITE; - else - flag = NFS4_OPEN_DELEGATE_READ; + goto out_no_deleg; + if (open->op_create == NFS4_OPEN_CREATE) + goto out_no_deleg; break; default: - goto out; + goto out_no_deleg; } - - dp = alloc_init_deleg(oo->oo_owner.so_client, stp, fh, flag); + dp = alloc_init_deleg(oo->oo_owner.so_client, stp, fh); if (dp == NULL) goto out_no_deleg; - status = nfs4_set_delegation(dp, flag); + status = nfs4_set_delegation(dp); if (status) goto out_free; @@ -3142,24 +3135,21 @@ nfs4_open_delegation(struct net *net, struct svc_fh *fh, dprintk("NFSD: delegation stateid=" STATEID_FMT "\n", STATEID_VAL(&dp->dl_stid.sc_stateid)); -out: - open->op_delegate_type = flag; - if (flag == NFS4_OPEN_DELEGATE_NONE) { - if (open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS && - open->op_delegate_type != NFS4_OPEN_DELEGATE_NONE) - dprintk("NFSD: WARNING: refusing delegation reclaim\n"); - - /* 4.1 client asking for a delegation? */ - if (open->op_deleg_want) - nfsd4_open_deleg_none_ext(open, status); - } + open->op_delegate_type = NFS4_OPEN_DELEGATE_READ; return; out_free: unhash_stid(&dp->dl_stid); nfs4_put_delegation(dp); out_no_deleg: - flag = NFS4_OPEN_DELEGATE_NONE; - goto out; + open->op_delegate_type = NFS4_OPEN_DELEGATE_NONE; + if (open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS && + open->op_delegate_type != NFS4_OPEN_DELEGATE_NONE) + dprintk("NFSD: WARNING: refusing delegation reclaim\n"); + + /* 4.1 client asking for a delegation? */ + if (open->op_deleg_want) + nfsd4_open_deleg_none_ext(open, status); + return; } static void nfsd4_deleg_xgrade_none_ext(struct nfsd4_open *open, -- cgit v1.2.3 From 247500820ebd02ad87525db5d9b199e5b66f6636 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 21 Jun 2013 11:48:11 -0400 Subject: nfsd4: fix decoding of compounds across page boundaries A freebsd NFSv4.0 client was getting rare IO errors expanding a tarball. A network trace showed the server returning BAD_XDR on the final getattr of a getattr+write+getattr compound. The final getattr started on a page boundary. I believe the Linux client ignores errors on the post-write getattr, and that that's why we haven't seen this before. Cc: stable@vger.kernel.org Reported-by: Rick Macklem Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4xdr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 171fe5e4af67..e34f5ebfc811 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -167,8 +167,8 @@ static __be32 *read_buf(struct nfsd4_compoundargs *argp, u32 nbytes) */ memcpy(p, argp->p, avail); /* step to next page */ - argp->p = page_address(argp->pagelist[0]); argp->pagelist++; + argp->p = page_address(argp->pagelist[0]); if (argp->pagelen < PAGE_SIZE) { argp->end = argp->p + (argp->pagelen>>2); argp->pagelen = 0; -- cgit v1.2.3 From 590b743143eae8db40abdfd1ab20bc51ee0ee5db Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 21 Jun 2013 17:06:29 -0400 Subject: nfsd4: minor read_buf cleanup The code to step to the next page seems reasonably self-contained. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4xdr.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index e34f5ebfc811..c102d2509a2a 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -139,6 +139,19 @@ xdr_error: \ } \ } while (0) +static void next_decode_page(struct nfsd4_compoundargs *argp) +{ + argp->pagelist++; + argp->p = page_address(argp->pagelist[0]); + if (argp->pagelen < PAGE_SIZE) { + argp->end = argp->p + (argp->pagelen>>2); + argp->pagelen = 0; + } else { + argp->end = argp->p + (PAGE_SIZE>>2); + argp->pagelen -= PAGE_SIZE; + } +} + static __be32 *read_buf(struct nfsd4_compoundargs *argp, u32 nbytes) { /* We want more bytes than seem to be available. @@ -166,16 +179,7 @@ static __be32 *read_buf(struct nfsd4_compoundargs *argp, u32 nbytes) * guarantee p points to at least nbytes bytes. */ memcpy(p, argp->p, avail); - /* step to next page */ - argp->pagelist++; - argp->p = page_address(argp->pagelist[0]); - if (argp->pagelen < PAGE_SIZE) { - argp->end = argp->p + (argp->pagelen>>2); - argp->pagelen = 0; - } else { - argp->end = argp->p + (PAGE_SIZE>>2); - argp->pagelen -= PAGE_SIZE; - } + next_decode_page(argp); memcpy(((char*)p)+avail, argp->p, (nbytes - avail)); argp->p += XDR_QUADLEN(nbytes - avail); return p; -- cgit v1.2.3 From cf3aa02cb4a0c5af5557dd47f15a08a7df33182a Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 26 Jun 2013 11:09:06 -0400 Subject: svcrpc: fix handling of too-short rpc's If we detect that an rpc is too short, we abort and close the connection. Except, there's a bug here: we're leaving sk_datalen nonzero without leaving any pages in the sk_pages array. The most likely result of the inconsistency is a subsequent crash in svc_tcp_clear_pages. Also demote the BUG_ON in svc_tcp_clear_pages to a WARN. Cc: stable@kernel.org Signed-off-by: J. Bruce Fields --- net/sunrpc/svcsock.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index 0f679df7d072..df74919c81c0 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -917,7 +917,10 @@ static void svc_tcp_clear_pages(struct svc_sock *svsk) len = svsk->sk_datalen; npages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT; for (i = 0; i < npages; i++) { - BUG_ON(svsk->sk_pages[i] == NULL); + if (svsk->sk_pages[i] == NULL) { + WARN_ON_ONCE(1); + continue; + } put_page(svsk->sk_pages[i]); svsk->sk_pages[i] = NULL; } @@ -1092,8 +1095,10 @@ static int svc_tcp_recvfrom(struct svc_rqst *rqstp) goto err_noclose; } - if (svc_sock_reclen(svsk) < 8) + if (svc_sock_reclen(svsk) < 8) { + svsk->sk_datalen = 0; goto err_delete; /* client is nuts. */ + } rqstp->rq_arg.len = svsk->sk_datalen; rqstp->rq_arg.page_base = 0; -- cgit v1.2.3 From 1f691b07c5dc51b2055834f58c0f351defd97f27 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 26 Jun 2013 10:55:40 -0400 Subject: svcrpc: don't error out on small tcp fragment Though clients we care about mostly don't do this, it is possible for rpc requests to be sent in multiple fragments. Here we have a sanity check to ensure that the final received rpc isn't too small--except that the number we're actually checking is the length of just the final fragment, not of the whole rpc. So a perfectly legal rpc that's unluckily fragmented could cause the server to close the connection here. Cc: stable@vger.kernel.org Signed-off-by: J. Bruce Fields --- net/sunrpc/svcsock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index df74919c81c0..305374d4fb98 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -1095,7 +1095,7 @@ static int svc_tcp_recvfrom(struct svc_rqst *rqstp) goto err_noclose; } - if (svc_sock_reclen(svsk) < 8) { + if (svsk->sk_datalen < 8) { svsk->sk_datalen = 0; goto err_delete; /* client is nuts. */ } -- cgit v1.2.3 From 89f6c3362cb5a6bce96dbe6aa15b4749c2262b21 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 19 Jun 2013 15:47:37 -0400 Subject: nfsd4: delegation-based open reclaims should bypass permissions We saw a v4.0 client's create fail as follows: - open create succeeds and gets a read delegation - client attempts to set mode on new file, gets DELAY while server recalls delegation. - client attempts a CLAIM_DELEGATE_CUR open using the delegation, gets error because of new file mode. This probably can't happen on a recent kernel since we're no longer giving out delegations on create opens. Nevertheless, it's a bug--reclaim opens should bypass permission checks. Reported-by: Steve Dickson Reported-by: Trond Myklebust Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4proc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 1a1ff247bc59..a7cee864e7b2 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -296,7 +296,8 @@ do_open_lookup(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, stru nfsd4_set_open_owner_reply_cache(cstate, open, resfh); accmode = NFSD_MAY_NOP; - if (open->op_created) + if (open->op_created || + open->op_claim_type == NFS4_OPEN_CLAIM_DELEGATE_CUR) accmode |= NFSD_MAY_OWNER_OVERRIDE; status = do_open_permission(rqstp, resfh, open, accmode); set_change_info(&open->op_cinfo, current_fh); -- cgit v1.2.3 From 0a262ffb75275e48caa17e80b96504354e94f6c2 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 17 Jun 2013 17:29:40 -0400 Subject: nfsd4: do not throw away 4.1 lock state on last unlock This reverts commit eb2099f31b0f090684a64ef8df44a30ff7c45fc2 "nfsd4: release lockowners on last unlock in 4.1 case". Trond identified language in rfc 5661 section 8.2.4 which forbids this behavior: Stateids associated with byte-range locks are an exception. They remain valid even if a LOCKU frees all remaining locks, so long as the open file with which they are associated remains open, unless the client frees the stateids via the FREE_STATEID operation. And bakeathon 2013 testing found a 4.1 freebsd client was getting an incorrect BAD_STATEID return from a FREE_STATEID in the above situation and then failing. The spec language honestly was probably a mistake but at this point with implementations already following it we're probably stuck with that. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index fcc0610fe308..a7d8a11943ee 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -4514,7 +4514,6 @@ __be32 nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_locku *locku) { - struct nfs4_lockowner *lo; struct nfs4_ol_stateid *stp; struct file *filp = NULL; struct file_lock *file_lock = NULL; @@ -4547,10 +4546,9 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, status = nfserr_jukebox; goto out; } - lo = lockowner(stp->st_stateowner); locks_init_lock(file_lock); file_lock->fl_type = F_UNLCK; - file_lock->fl_owner = (fl_owner_t)lo; + file_lock->fl_owner = (fl_owner_t)lockowner(stp->st_stateowner); file_lock->fl_pid = current->tgid; file_lock->fl_file = filp; file_lock->fl_flags = FL_POSIX; @@ -4569,11 +4567,6 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, update_stateid(&stp->st_stid.sc_stateid); memcpy(&locku->lu_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t)); - if (nfsd4_has_session(cstate) && !check_for_locks(stp->st_file, lo)) { - WARN_ON_ONCE(cstate->replay_owner); - release_lockowner(lo); - } - out: nfsd4_bump_seqid(cstate, status); if (!cstate->replay_owner) -- cgit v1.2.3 From d08d32e6e5c0fee127d3b20d70f5b59d8af0a261 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 21 Jun 2013 11:05:32 -0400 Subject: nfsd4: return delegation immediately if lease fails This case shouldn't happen--the administrator shouldn't really allow other applications access to the export until clients have had the chance to reclaim their state--but if it does then we should set the "return this lease immediately" bit on the reply. That still leaves some small races, but it's the best the protocol allows us to do in the case a lease is ripped out from under us.... Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index a7d8a11943ee..d44a4bf71cef 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -3143,8 +3143,10 @@ out_free: out_no_deleg: open->op_delegate_type = NFS4_OPEN_DELEGATE_NONE; if (open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS && - open->op_delegate_type != NFS4_OPEN_DELEGATE_NONE) + open->op_delegate_type != NFS4_OPEN_DELEGATE_NONE) { dprintk("NFSD: WARNING: refusing delegation reclaim\n"); + open->op_recall = 1; + } /* 4.1 client asking for a delegation? */ if (open->op_deleg_want) -- cgit v1.2.3 From f9e1aedc6c79f18bb56caa3735b94217c4ec1e0c Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Thu, 13 Jun 2013 12:53:42 +1000 Subject: sunrpc/cache: remove races with queuing an upcall. We currently queue an upcall after setting CACHE_PENDING, and dequeue after clearing CACHE_PENDING. So a request should only be present when CACHE_PENDING is set. However we don't combine the test and the enqueue/dequeue in a protected region, so it is possible (if unlikely) for a race to result in a request being queued without CACHE_PENDING set, or a request to be absent despite CACHE_PENDING. So: include a test for CACHE_PENDING inside the regions of enqueue and dequeue where queue_lock is held, and abort the operation if the value is not as expected. Also remove the early 'return' from cache_dequeue() to ensure that it always removes all entries: As there is no locking between setting CACHE_PENDING and calling sunrpc_cache_pipe_upcall it is not inconceivable for some other thread to clear CACHE_PENDING and then someone else to set it and call sunrpc_cache_pipe_upcall, both before the original threads completed the call. With this, it perfectly safe and correct to: - call cache_dequeue() if and only if we have just cleared CACHE_PENDING - call sunrpc_cache_pipe_upcall() (via cache_make_upcall) if and only if we have just set CACHE_PENDING. Reported-by: Bodo Stroesser Signed-off-by: NeilBrown Signed-off-by: Bodo Stroesser Signed-off-by: J. Bruce Fields --- net/sunrpc/cache.c | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index 3b3f14fc02c5..8e964ae8d7b5 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -1036,23 +1036,32 @@ static int cache_release(struct inode *inode, struct file *filp, static void cache_dequeue(struct cache_detail *detail, struct cache_head *ch) { - struct cache_queue *cq; + struct cache_queue *cq, *tmp; + struct cache_request *cr; + struct list_head dequeued; + + INIT_LIST_HEAD(&dequeued); spin_lock(&queue_lock); - list_for_each_entry(cq, &detail->queue, list) + list_for_each_entry_safe(cq, tmp, &detail->queue, list) if (!cq->reader) { - struct cache_request *cr = container_of(cq, struct cache_request, q); + cr = container_of(cq, struct cache_request, q); if (cr->item != ch) continue; + if (test_bit(CACHE_PENDING, &ch->flags)) + /* Lost a race and it is pending again */ + break; if (cr->readers != 0) continue; - list_del(&cr->q.list); - spin_unlock(&queue_lock); - cache_put(cr->item, detail); - kfree(cr->buf); - kfree(cr); - return; + list_move(&cr->q.list, &dequeued); } spin_unlock(&queue_lock); + while (!list_empty(&dequeued)) { + cr = list_entry(dequeued.next, struct cache_request, q.list); + list_del(&cr->q.list); + cache_put(cr->item, detail); + kfree(cr->buf); + kfree(cr); + } } /* @@ -1166,6 +1175,7 @@ int sunrpc_cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h) char *buf; struct cache_request *crq; + int ret = 0; if (!detail->cache_request) return -EINVAL; @@ -1191,10 +1201,18 @@ int sunrpc_cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h) crq->len = 0; crq->readers = 0; spin_lock(&queue_lock); - list_add_tail(&crq->q.list, &detail->queue); + if (test_bit(CACHE_PENDING, &h->flags)) + list_add_tail(&crq->q.list, &detail->queue); + else + /* Lost a race, no longer PENDING, so don't enqueue */ + ret = -EAGAIN; spin_unlock(&queue_lock); wake_up(&queue_wait); - return 0; + if (ret == -EAGAIN) { + kfree(buf); + kfree(crq); + } + return ret; } EXPORT_SYMBOL_GPL(sunrpc_cache_pipe_upcall); -- cgit v1.2.3 From 2a1c7f53fd31e46f51780b61eb99fffef4c3c5a6 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Thu, 13 Jun 2013 12:53:42 +1000 Subject: sunrpc/cache: use cache_fresh_unlocked consistently and correctly. cache_fresh_unlocked() is called when a cache entry has been updated and ensures that if there were any pending upcalls, they are cleared. So every time we update a cache entry, we should call this, and this should be the only way that we try to clear pending calls (that sort of uniformity makes code sooo much easier to read). try_to_negate_entry() will (possibly) mark an entry as negative. If it doesn't, it is because the entry already is VALID. So the entry will be valid on exit, so it is appropriate to call cache_fresh_unlocked(). So tidy up try_to_negate_entry() to do that, and remove partial open-coded cache_fresh_unlocked() from the one call-site of try_to_negate_entry(). In the other branch of the 'switch(cache_make_upcall())', we again have a partial open-coded version of cache_fresh_unlocked(). Replace that with a real call. And again in cache_clean(), use a real call to cache_fresh_unlocked(). These call sites might previously have called cache_revisit_request() if CACHE_PENDING wasn't set. This is never necessary because cache_revisit_request() can only do anything if the item is in the cache_defer_hash, However any time that an item is added to the cache_defer_hash (setup_deferral), the code immediately tests CACHE_PENDING, and removes the entry again if it is clear. So all other places we only need to 'cache_revisit_request' if we've just cleared CACHE_PENDING. Reported-by: Bodo Stroesser Signed-off-by: NeilBrown Signed-off-by: J. Bruce Fields --- net/sunrpc/cache.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index 8e964ae8d7b5..29c463396a2d 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -228,15 +228,14 @@ static int try_to_negate_entry(struct cache_detail *detail, struct cache_head *h write_lock(&detail->hash_lock); rv = cache_is_valid(h); - if (rv != -EAGAIN) { - write_unlock(&detail->hash_lock); - return rv; + if (rv == -EAGAIN) { + set_bit(CACHE_NEGATIVE, &h->flags); + cache_fresh_locked(h, seconds_since_boot()+CACHE_NEW_EXPIRY); + rv = -ENOENT; } - set_bit(CACHE_NEGATIVE, &h->flags); - cache_fresh_locked(h, seconds_since_boot()+CACHE_NEW_EXPIRY); write_unlock(&detail->hash_lock); cache_fresh_unlocked(h, detail); - return -ENOENT; + return rv; } /* @@ -275,13 +274,10 @@ int cache_check(struct cache_detail *detail, if (!test_and_set_bit(CACHE_PENDING, &h->flags)) { switch (cache_make_upcall(detail, h)) { case -EINVAL: - clear_bit(CACHE_PENDING, &h->flags); - cache_revisit_request(h); rv = try_to_negate_entry(detail, h); break; case -EAGAIN: - clear_bit(CACHE_PENDING, &h->flags); - cache_revisit_request(h); + cache_fresh_unlocked(h, detail); break; } } @@ -457,9 +453,7 @@ static int cache_clean(void) current_index ++; spin_unlock(&cache_list_lock); if (ch) { - if (test_and_clear_bit(CACHE_PENDING, &ch->flags)) - cache_dequeue(current_detail, ch); - cache_revisit_request(ch); + cache_fresh_unlocked(ch, d); cache_put(ch, d); } } else -- cgit v1.2.3 From 013920eb5db97e99a4c30c8400f1c616e2a8b0a2 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Thu, 13 Jun 2013 12:53:42 +1000 Subject: sunrpc/cache: ensure items removed from cache do not have pending upcalls. It is possible for a race to set CACHE_PENDING after cache_clean() has removed a cache entry from the cache. If CACHE_PENDING is still set when the entry is finally 'put', the cache_dequeue() will never happen and we can leak memory. So set a new flag 'CACHE_CLEANED' when we remove something from the cache, and don't queue any upcall if it is set. If CACHE_PENDING is set before CACHE_CLEANED, the call that cache_clean() makes to cache_fresh_unlocked() will free memory as needed. If CACHE_PENDING is set after CACHE_CLEANED, the test in sunrpc_cache_pipe_upcall will ensure that the memory is not allocated. Reported-by: Signed-off-by: NeilBrown Signed-off-by: J. Bruce Fields --- include/linux/sunrpc/cache.h | 1 + net/sunrpc/cache.c | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/include/linux/sunrpc/cache.h b/include/linux/sunrpc/cache.h index 303399b1ba59..8419f7dbdab2 100644 --- a/include/linux/sunrpc/cache.h +++ b/include/linux/sunrpc/cache.h @@ -57,6 +57,7 @@ struct cache_head { #define CACHE_VALID 0 /* Entry contains valid data */ #define CACHE_NEGATIVE 1 /* Negative entry - there is no match for the key */ #define CACHE_PENDING 2 /* An upcall has been sent but no reply received yet*/ +#define CACHE_CLEANED 3 /* Entry has been cleaned from cache */ #define CACHE_NEW_EXPIRY 120 /* keep new things pending confirmation for 120 seconds */ diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index 29c463396a2d..b12144c5edd0 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -306,7 +306,7 @@ EXPORT_SYMBOL_GPL(cache_check); * a current pointer into that list and into the table * for that entry. * - * Each time clean_cache is called it finds the next non-empty entry + * Each time cache_clean is called it finds the next non-empty entry * in the current table and walks the list in that entry * looking for entries that can be removed. * @@ -453,6 +453,7 @@ static int cache_clean(void) current_index ++; spin_unlock(&cache_list_lock); if (ch) { + set_bit(CACHE_CLEANED, &ch->flags); cache_fresh_unlocked(ch, d); cache_put(ch, d); } @@ -1178,6 +1179,9 @@ int sunrpc_cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h) warn_no_listener(detail); return -EINVAL; } + if (test_bit(CACHE_CLEANED, &h->flags)) + /* Too late to make an upcall */ + return -EAGAIN; buf = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!buf) -- cgit v1.2.3 From 7715cde86857d4bb40f43f1ee971cf906eaf1b9c Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Thu, 13 Jun 2013 12:53:42 +1000 Subject: net/sunrpc: xpt_auth_cache should be ignored when expired. commit d202cce8963d9268ff355a386e20243e8332b308 sunrpc: never return expired entries in sunrpc_cache_lookup moved the 'entry is expired' test from cache_check to sunrpc_cache_lookup, so that it happened early and some races could safely be ignored. However the ip_map (in svcauth_unix.c) has a separate single-item cache which allows quick lookup without locking. An entry in this case would not be subject to the expiry test and so could be used well after it has expired. This is not normally a big problem because the first time it is used after it is expired an up-call will be scheduled to refresh the entry (if it hasn't been scheduled already) and the old entry will then be invalidated. So on the second attempt to use it after it has expired, ip_map_cached_get will discard it. However that is subtle and not ideal, so replace the "!cache_valid" test with "cache_is_expired". In doing this we drop the test on the "CACHE_VALID" bit. This is unnecessary as the bit is never cleared, and an entry will only be cached if the bit is set. Reported-by: Bodo Stroesser Signed-off-by: NeilBrown Signed-off-by: J. Bruce Fields --- include/linux/sunrpc/cache.h | 48 +++++++++++++++++++------------------------- net/sunrpc/cache.c | 6 ------ net/sunrpc/svcauth_unix.c | 4 ++-- 3 files changed, 23 insertions(+), 35 deletions(-) diff --git a/include/linux/sunrpc/cache.h b/include/linux/sunrpc/cache.h index 8419f7dbdab2..6ce690de447f 100644 --- a/include/linux/sunrpc/cache.h +++ b/include/linux/sunrpc/cache.h @@ -149,6 +149,24 @@ struct cache_deferred_req { int too_many); }; +/* + * timestamps kept in the cache are expressed in seconds + * since boot. This is the best for measuring differences in + * real time. + */ +static inline time_t seconds_since_boot(void) +{ + struct timespec boot; + getboottime(&boot); + return get_seconds() - boot.tv_sec; +} + +static inline time_t convert_to_wallclock(time_t sinceboot) +{ + struct timespec boot; + getboottime(&boot); + return boot.tv_sec + sinceboot; +} extern const struct file_operations cache_file_operations_pipefs; extern const struct file_operations content_file_operations_pipefs; @@ -182,15 +200,10 @@ static inline void cache_put(struct cache_head *h, struct cache_detail *cd) kref_put(&h->ref, cd->cache_put); } -static inline int cache_valid(struct cache_head *h) +static inline int cache_is_expired(struct cache_detail *detail, struct cache_head *h) { - /* If an item has been unhashed pending removal when - * the refcount drops to 0, the expiry_time will be - * set to 0. We don't want to consider such items - * valid in this context even though CACHE_VALID is - * set. - */ - return (h->expiry_time != 0 && test_bit(CACHE_VALID, &h->flags)); + return (h->expiry_time < seconds_since_boot()) || + (detail->flush_time > h->last_refresh); } extern int cache_check(struct cache_detail *detail, @@ -251,25 +264,6 @@ static inline int get_uint(char **bpp, unsigned int *anint) return 0; } -/* - * timestamps kept in the cache are expressed in seconds - * since boot. This is the best for measuring differences in - * real time. - */ -static inline time_t seconds_since_boot(void) -{ - struct timespec boot; - getboottime(&boot); - return get_seconds() - boot.tv_sec; -} - -static inline time_t convert_to_wallclock(time_t sinceboot) -{ - struct timespec boot; - getboottime(&boot); - return boot.tv_sec + sinceboot; -} - static inline time_t get_expiry(char **bpp) { int rv; diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index b12144c5edd0..5478a015ab00 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -50,12 +50,6 @@ static void cache_init(struct cache_head *h) h->last_refresh = now; } -static inline int cache_is_expired(struct cache_detail *detail, struct cache_head *h) -{ - return (h->expiry_time < seconds_since_boot()) || - (detail->flush_time > h->last_refresh); -} - struct cache_head *sunrpc_cache_lookup(struct cache_detail *detail, struct cache_head *key, int hash) { diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c index 06bdf5a1082c..a98853dfccdc 100644 --- a/net/sunrpc/svcauth_unix.c +++ b/net/sunrpc/svcauth_unix.c @@ -347,13 +347,13 @@ ip_map_cached_get(struct svc_xprt *xprt) spin_lock(&xprt->xpt_lock); ipm = xprt->xpt_auth_cache; if (ipm != NULL) { - if (!cache_valid(&ipm->h)) { + sn = net_generic(xprt->xpt_net, sunrpc_net_id); + if (cache_is_expired(sn->ip_map_cache, &ipm->h)) { /* * The entry has been invalidated since it was * remembered, e.g. by a second mount from the * same IP address. */ - sn = net_generic(xprt->xpt_net, sunrpc_net_id); xprt->xpt_auth_cache = NULL; spin_unlock(&xprt->xpt_lock); cache_put(&ipm->h, sn->ip_map_cache); -- cgit v1.2.3 From 0bebc633f1428163c9659fd16b34c745e60a0757 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Thu, 13 Jun 2013 12:53:42 +1000 Subject: sunrpc: Don't schedule an upcall on a replaced cache entry. When a cache entry is replaced, the "expiry_time" get set to zero by a call to "cache_fresh_locked(..., 0)" at the end of "sunrpc_cache_update". This low expiry time makes cache_check() think that the 'refresh_age' is negative, so the 'age' is comparatively large and a refresh is triggered. However refreshing a replaced entry it pointless, it cannot achieve anything useful. So teach cache_check to ignore a low refresh_age when expiry_time is zero. Reported-by: Bodo Stroesser Signed-off-by: NeilBrown Signed-off-by: J. Bruce Fields --- net/sunrpc/cache.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index 5478a015ab00..49eb37010aa3 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -262,7 +262,8 @@ int cache_check(struct cache_detail *detail, if (rqstp == NULL) { if (rv == -EAGAIN) rv = -ENOENT; - } else if (rv == -EAGAIN || age > refresh_age/2) { + } else if (rv == -EAGAIN || + (h->expiry_time != 0 && age > refresh_age/2)) { dprintk("RPC: Want update, refage=%ld, age=%ld\n", refresh_age, age); if (!test_and_set_bit(CACHE_PENDING, &h->flags)) { -- cgit v1.2.3 From e2a800beaca1f580945773e57d1a0e7cd37b1056 Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Mon, 1 Jul 2013 14:19:50 +1000 Subject: powerpc/hw_brk: Fix off by one error when validating DAWR region end The Data Address Watchpoint Register (DAWR) on POWER8 can take a 512 byte range but this range must not cross a 512 byte boundary. Unfortunately we were off by one when calculating the end of the region, hence we were not allowing some breakpoint regions which were actually valid. This fixes this error. Signed-off-by: Michael Neuling Reported-by: Edjunior Barbosa Machado Cc: stable@vger.kernel.org # 3.9+ Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/hw_breakpoint.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/hw_breakpoint.c b/arch/powerpc/kernel/hw_breakpoint.c index 1150ae7c22c3..f0b47d1a6b0e 100644 --- a/arch/powerpc/kernel/hw_breakpoint.c +++ b/arch/powerpc/kernel/hw_breakpoint.c @@ -176,7 +176,7 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp) length_max = 512 ; /* 64 doublewords */ /* DAWR region can't cross 512 boundary */ if ((bp->attr.bp_addr >> 10) != - ((bp->attr.bp_addr + bp->attr.bp_len) >> 10)) + ((bp->attr.bp_addr + bp->attr.bp_len - 1) >> 10)) return -EINVAL; } if (info->len > -- cgit v1.2.3 From c039e3a8ddd52139d0f81711ecd757772f868b22 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Tue, 2 Jul 2013 08:13:52 +1000 Subject: powerpc: Handle both new style and old style reserve maps When Jeremy introduced the new device-tree based reserve map, he made the code in early_reserve_mem_dt() bail out if it found one, thus not reserving the initrd nor processing the old style map. I hit problems with variants of kexec that didn't put the initrd in the new style map either. While these could/will be fixed, I believe we should be safe here and rather reserve more than not enough. We could have a firmware passing stuff via the new style map, and in the middle, a kexec that knows nothing about it and adding other things to the old style map. I don't see a big issue with processing both and reserving everything that needs to be. memblock_reserve() supports overlaps fine these days. Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/prom.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c index 9c753bc9885d..eb23ac92abb9 100644 --- a/arch/powerpc/kernel/prom.c +++ b/arch/powerpc/kernel/prom.c @@ -559,7 +559,7 @@ void __init early_init_dt_setup_initrd_arch(unsigned long start, } #endif -static bool __init early_reserve_mem_dt(void) +static void __init early_reserve_mem_dt(void) { unsigned long i, len, dt_root; const __be32 *prop; @@ -569,7 +569,9 @@ static bool __init early_reserve_mem_dt(void) prop = of_get_flat_dt_prop(dt_root, "reserved-ranges", &len); if (!prop) - return false; + return; + + DBG("Found new-style reserved-ranges\n"); /* Each reserved range is an (address,size) pair, 2 cells each, * totalling 4 cells per range. */ @@ -579,11 +581,11 @@ static bool __init early_reserve_mem_dt(void) base = of_read_number(prop + (i * 4) + 0, 2); size = of_read_number(prop + (i * 4) + 2, 2); - if (size) + if (size) { + DBG("reserving: %llx -> %llx\n", base, size); memblock_reserve(base, size); + } } - - return true; } static void __init early_reserve_mem(void) @@ -601,20 +603,16 @@ static void __init early_reserve_mem(void) self_size = initial_boot_params->totalsize; memblock_reserve(self_base, self_size); - /* - * Try looking for reserved-regions property in the DT first; if - * it's present, it'll contain all of the necessary reservation - * info - */ - if (early_reserve_mem_dt()) - return; + /* Look for the new "reserved-regions" property in the DT */ + early_reserve_mem_dt(); #ifdef CONFIG_BLK_DEV_INITRD - /* then reserve the initrd, if any */ - if (initrd_start && (initrd_end > initrd_start)) + /* Then reserve the initrd, if any */ + if (initrd_start && (initrd_end > initrd_start)) { memblock_reserve(_ALIGN_DOWN(__pa(initrd_start), PAGE_SIZE), _ALIGN_UP(initrd_end, PAGE_SIZE) - _ALIGN_DOWN(initrd_start, PAGE_SIZE)); + } #endif /* CONFIG_BLK_DEV_INITRD */ #ifdef CONFIG_PPC32 @@ -626,6 +624,8 @@ static void __init early_reserve_mem(void) u32 base_32, size_32; u32 *reserve_map_32 = (u32 *)reserve_map; + DBG("Found old 32-bit reserve map\n"); + while (1) { base_32 = *(reserve_map_32++); size_32 = *(reserve_map_32++); @@ -640,6 +640,9 @@ static void __init early_reserve_mem(void) return; } #endif + DBG("Processing reserve map\n"); + + /* Handle the reserve map in the fdt blob if it exists */ while (1) { base = *(reserve_map++); size = *(reserve_map++); -- cgit v1.2.3 From da002d8924a49e8d8e289d07d46339e12dd56899 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 1 Jul 2013 20:56:18 +0100 Subject: mfd: wm8994: Remove duplicate check for active JACKDET Probably the result of a mismerge or rebase failing to notice that the hunk had already been applied. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm8994-core.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index ccdddf919069..781115e8dca9 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -259,20 +259,6 @@ static int wm8994_suspend(struct device *dev) break; } - switch (wm8994->type) { - case WM1811: - ret = wm8994_reg_read(wm8994, WM8994_ANTIPOP_2); - if (ret < 0) { - dev_err(dev, "Failed to read jackdet: %d\n", ret); - } else if (ret & WM1811_JACKDET_MODE_MASK) { - dev_dbg(dev, "CODEC still active, ignoring suspend\n"); - return 0; - } - break; - default: - break; - } - /* Disable LDO pulldowns while the device is suspended if we * don't know that something will be driving them. */ if (!wm8994->ldo_ena_always_driven) -- cgit v1.2.3 From 25f311fa58c18c19ae1348336265ccb8368638f0 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 1 Jul 2013 23:02:19 +0100 Subject: mfd: sec: Provide max_register to regmap Enable debugfs register dumps and greater error checking within the regmap API providing the maximum register to the regmap API. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/sec-core.c | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/drivers/mfd/sec-core.c b/drivers/mfd/sec-core.c index cc896d1c1cdd..79767681483a 100644 --- a/drivers/mfd/sec-core.c +++ b/drivers/mfd/sec-core.c @@ -25,6 +25,9 @@ #include #include #include +#include +#include +#include #include static struct mfd_cell s5m8751_devs[] = { @@ -105,6 +108,26 @@ static struct regmap_config sec_regmap_config = { .val_bits = 8, }; +static struct regmap_config s2mps11_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = S2MPS11_REG_L38CTRL, +}; + +static struct regmap_config s5m8763_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = S5M8763_REG_LBCNFG2, +}; + +static struct regmap_config s5m8767_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = S5M8767_REG_LDO28CTRL, +}; #ifdef CONFIG_OF /* @@ -160,6 +183,7 @@ static int sec_pmic_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct sec_platform_data *pdata = i2c->dev.platform_data; + const struct regmap_config *regmap; struct sec_pmic_dev *sec_pmic; int ret; @@ -190,7 +214,22 @@ static int sec_pmic_probe(struct i2c_client *i2c, sec_pmic->pdata = pdata; } - sec_pmic->regmap = devm_regmap_init_i2c(i2c, &sec_regmap_config); + switch (sec_pmic->device_type) { + case S2MPS11X: + regmap = &s2mps11_regmap_config; + break; + case S5M8763X: + regmap = &s5m8763_regmap_config; + break; + case S5M8767X: + regmap = &s5m8767_regmap_config; + break; + default: + regmap = &sec_regmap_config; + break; + } + + sec_pmic->regmap = devm_regmap_init_i2c(i2c, regmap); if (IS_ERR(sec_pmic->regmap)) { ret = PTR_ERR(sec_pmic->regmap); dev_err(&i2c->dev, "Failed to allocate register map: %d\n", -- cgit v1.2.3 From 86d379690c3b005418fafc9afdcdfc731a043862 Mon Sep 17 00:00:00 2001 From: Hongtao Jia Date: Wed, 10 Apr 2013 10:52:55 +0800 Subject: powerpc/mpic: Add get_version API both for internal and external use MPIC version is useful information for both mpic_alloc() and mpic_init(). The patch provide an API to get MPIC version for reusing the code. Also, some other IP block may need MPIC version for their own use. The API for external use is also provided. Signed-off-by: Jia Hongtao Signed-off-by: Li Yang Signed-off-by: Scott Wood --- arch/powerpc/include/asm/mpic.h | 3 +++ arch/powerpc/sysdev/mpic.c | 32 +++++++++++++++++++++++++------- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/arch/powerpc/include/asm/mpic.h b/arch/powerpc/include/asm/mpic.h index c0f9ef90f0b8..ea6bf7220da9 100644 --- a/arch/powerpc/include/asm/mpic.h +++ b/arch/powerpc/include/asm/mpic.h @@ -393,6 +393,9 @@ struct mpic #define MPIC_REGSET_STANDARD MPIC_REGSET(0) /* Original MPIC */ #define MPIC_REGSET_TSI108 MPIC_REGSET(1) /* Tsi108/109 PIC */ +/* Get the version of primary MPIC */ +extern u32 fsl_mpic_primary_get_version(void); + /* Allocate the controller structure and setup the linux irq descs * for the range if interrupts passed in. No HW initialization is * actually performed. diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c index 3cc2f9159ab1..1a4e19c6a688 100644 --- a/arch/powerpc/sysdev/mpic.c +++ b/arch/powerpc/sysdev/mpic.c @@ -1173,10 +1173,33 @@ static struct irq_domain_ops mpic_host_ops = { .xlate = mpic_host_xlate, }; +static u32 fsl_mpic_get_version(struct mpic *mpic) +{ + u32 brr1; + + if (!(mpic->flags & MPIC_FSL)) + return 0; + + brr1 = _mpic_read(mpic->reg_type, &mpic->thiscpuregs, + MPIC_FSL_BRR1); + + return brr1 & MPIC_FSL_BRR1_VER; +} + /* * Exported functions */ +u32 fsl_mpic_primary_get_version(void) +{ + struct mpic *mpic = mpic_primary; + + if (mpic) + return fsl_mpic_get_version(mpic); + + return 0; +} + struct mpic * __init mpic_alloc(struct device_node *node, phys_addr_t phys_addr, unsigned int flags, @@ -1323,7 +1346,6 @@ struct mpic * __init mpic_alloc(struct device_node *node, mpic_map(mpic, mpic->paddr, &mpic->tmregs, MPIC_INFO(TIMER_BASE), 0x1000); if (mpic->flags & MPIC_FSL) { - u32 brr1; int ret; /* @@ -1334,9 +1356,7 @@ struct mpic * __init mpic_alloc(struct device_node *node, mpic_map(mpic, mpic->paddr, &mpic->thiscpuregs, MPIC_CPU_THISBASE, 0x1000); - brr1 = _mpic_read(mpic->reg_type, &mpic->thiscpuregs, - MPIC_FSL_BRR1); - fsl_version = brr1 & MPIC_FSL_BRR1_VER; + fsl_version = fsl_mpic_get_version(mpic); /* Error interrupt mask register (EIMR) is required for * handling individual device error interrupts. EIMR @@ -1526,9 +1546,7 @@ void __init mpic_init(struct mpic *mpic) mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), 0xf); if (mpic->flags & MPIC_FSL) { - u32 brr1 = _mpic_read(mpic->reg_type, &mpic->thiscpuregs, - MPIC_FSL_BRR1); - u32 version = brr1 & MPIC_FSL_BRR1_VER; + u32 version = fsl_mpic_get_version(mpic); /* * Timer group B is present at the latest in MPIC 3.1 (e.g. -- cgit v1.2.3 From 2dd1c132d5c22677843658f5736f0de1cdf57bc1 Mon Sep 17 00:00:00 2001 From: Chunhe Lan Date: Fri, 19 Apr 2013 16:13:18 +0800 Subject: powerpc/fsl: Enable CONFIG_E1000E in mpc85xx_smp_defconfig On the most boards of Freescale platform, they use the PCI-Express Intel(R) PRO/1000 gigabit ethernet card to work. So enable the corresponding driver for it. Signed-off-by: Chunhe Lan Signed-off-by: Scott Wood --- arch/powerpc/configs/mpc85xx_smp_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/powerpc/configs/mpc85xx_smp_defconfig b/arch/powerpc/configs/mpc85xx_smp_defconfig index 165e6b32baef..152fa05b15e4 100644 --- a/arch/powerpc/configs/mpc85xx_smp_defconfig +++ b/arch/powerpc/configs/mpc85xx_smp_defconfig @@ -131,6 +131,7 @@ CONFIG_DUMMY=y CONFIG_FS_ENET=y CONFIG_UCC_GETH=y CONFIG_GIANFAR=y +CONFIG_E1000E=y CONFIG_MARVELL_PHY=y CONFIG_DAVICOM_PHY=y CONFIG_CICADA_PHY=y -- cgit v1.2.3 From 7601f59765a48bef329a429101eabcb0e2503634 Mon Sep 17 00:00:00 2001 From: LEROY Christophe Date: Thu, 18 Apr 2013 07:26:11 +0200 Subject: powerpc/8xx: Erroneous double irq_eoi() on CPM IRQ in MPC8xx irq_eoi() is already called by generic_handle_irq() so it shall not be called a again Signed-off-by: Christophe Leroy Signed-off-by: Scott Wood --- arch/powerpc/platforms/8xx/m8xx_setup.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/arch/powerpc/platforms/8xx/m8xx_setup.c b/arch/powerpc/platforms/8xx/m8xx_setup.c index 806cbbd86ec6..587a2828b06c 100644 --- a/arch/powerpc/platforms/8xx/m8xx_setup.c +++ b/arch/powerpc/platforms/8xx/m8xx_setup.c @@ -219,19 +219,12 @@ void mpc8xx_restart(char *cmd) static void cpm_cascade(unsigned int irq, struct irq_desc *desc) { - struct irq_chip *chip; - int cascade_irq; - - if ((cascade_irq = cpm_get_irq()) >= 0) { - struct irq_desc *cdesc = irq_to_desc(cascade_irq); + struct irq_chip *chip = irq_desc_get_chip(desc); + int cascade_irq = cpm_get_irq(); + if (cascade_irq >= 0) generic_handle_irq(cascade_irq); - chip = irq_desc_get_chip(cdesc); - chip->irq_eoi(&cdesc->irq_data); - } - - chip = irq_desc_get_chip(desc); chip->irq_eoi(&desc->irq_data); } -- cgit v1.2.3 From 9837b43c5f3514e5d28f65f1513f4dc6759d2810 Mon Sep 17 00:00:00 2001 From: Kevin Hao Date: Thu, 11 Apr 2013 09:32:34 +0800 Subject: powerpc/85xx: enable coreint for all the 64bit boards With the patch 7230c564 (powerpc: Rework lazy-interrupt handling), it seems that the coreint works pretty well on the 85xx 64bit kernel. So use the coreint by default for these boards. Signed-off-by: Kevin Hao Signed-off-by: Scott Wood --- arch/powerpc/platforms/85xx/p5020_ds.c | 5 ----- arch/powerpc/platforms/85xx/p5040_ds.c | 5 ----- arch/powerpc/platforms/85xx/t4240_qds.c | 5 ----- 3 files changed, 15 deletions(-) diff --git a/arch/powerpc/platforms/85xx/p5020_ds.c b/arch/powerpc/platforms/85xx/p5020_ds.c index 753a42c29d4d..39cfa4044e6c 100644 --- a/arch/powerpc/platforms/85xx/p5020_ds.c +++ b/arch/powerpc/platforms/85xx/p5020_ds.c @@ -75,12 +75,7 @@ define_machine(p5020_ds) { #ifdef CONFIG_PCI .pcibios_fixup_bus = fsl_pcibios_fixup_bus, #endif -/* coreint doesn't play nice with lazy EE, use legacy mpic for now */ -#ifdef CONFIG_PPC64 - .get_irq = mpic_get_irq, -#else .get_irq = mpic_get_coreint_irq, -#endif .restart = fsl_rstcr_restart, .calibrate_decr = generic_calibrate_decr, .progress = udbg_progress, diff --git a/arch/powerpc/platforms/85xx/p5040_ds.c b/arch/powerpc/platforms/85xx/p5040_ds.c index 11381851828e..f70e74cddf97 100644 --- a/arch/powerpc/platforms/85xx/p5040_ds.c +++ b/arch/powerpc/platforms/85xx/p5040_ds.c @@ -66,12 +66,7 @@ define_machine(p5040_ds) { #ifdef CONFIG_PCI .pcibios_fixup_bus = fsl_pcibios_fixup_bus, #endif -/* coreint doesn't play nice with lazy EE, use legacy mpic for now */ -#ifdef CONFIG_PPC64 - .get_irq = mpic_get_irq, -#else .get_irq = mpic_get_coreint_irq, -#endif .restart = fsl_rstcr_restart, .calibrate_decr = generic_calibrate_decr, .progress = udbg_progress, diff --git a/arch/powerpc/platforms/85xx/t4240_qds.c b/arch/powerpc/platforms/85xx/t4240_qds.c index 5998e9f33304..91ead6b1b8af 100644 --- a/arch/powerpc/platforms/85xx/t4240_qds.c +++ b/arch/powerpc/platforms/85xx/t4240_qds.c @@ -75,12 +75,7 @@ define_machine(t4240_qds) { #ifdef CONFIG_PCI .pcibios_fixup_bus = fsl_pcibios_fixup_bus, #endif -/* coreint doesn't play nice with lazy EE, use legacy mpic for now */ -#ifdef CONFIG_PPC64 - .get_irq = mpic_get_irq, -#else .get_irq = mpic_get_coreint_irq, -#endif .restart = fsl_rstcr_restart, .calibrate_decr = generic_calibrate_decr, .progress = udbg_progress, -- cgit v1.2.3 From 5ff04b7287d87c1db74f47360365905ed9a97ff7 Mon Sep 17 00:00:00 2001 From: "Dongsheng.wang@freescale.com" Date: Tue, 9 Apr 2013 10:22:29 +0800 Subject: powerpc/mpic: add irq_set_wake support Add irq_set_wake support. Just add IRQF_NO_SUSPEND to desc->action->flag. So the wake up interrupt will not be disable in suspend_device_irqs. Signed-off-by: Wang Dongsheng Signed-off-by: Scott Wood --- arch/powerpc/sysdev/mpic.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c index 1a4e19c6a688..4635d11f2dc2 100644 --- a/arch/powerpc/sysdev/mpic.c +++ b/arch/powerpc/sysdev/mpic.c @@ -920,6 +920,22 @@ int mpic_set_irq_type(struct irq_data *d, unsigned int flow_type) return IRQ_SET_MASK_OK_NOCOPY; } +static int mpic_irq_set_wake(struct irq_data *d, unsigned int on) +{ + struct irq_desc *desc = container_of(d, struct irq_desc, irq_data); + struct mpic *mpic = mpic_from_irq_data(d); + + if (!(mpic->flags & MPIC_FSL)) + return -ENXIO; + + if (on) + desc->action->flags |= IRQF_NO_SUSPEND; + else + desc->action->flags &= ~IRQF_NO_SUSPEND; + + return 0; +} + void mpic_set_vector(unsigned int virq, unsigned int vector) { struct mpic *mpic = mpic_from_irq(virq); @@ -957,6 +973,7 @@ static struct irq_chip mpic_irq_chip = { .irq_unmask = mpic_unmask_irq, .irq_eoi = mpic_end_irq, .irq_set_type = mpic_set_irq_type, + .irq_set_wake = mpic_irq_set_wake, }; #ifdef CONFIG_SMP @@ -971,6 +988,7 @@ static struct irq_chip mpic_tm_chip = { .irq_mask = mpic_mask_tm, .irq_unmask = mpic_unmask_tm, .irq_eoi = mpic_end_irq, + .irq_set_wake = mpic_irq_set_wake, }; #ifdef CONFIG_MPIC_U3_HT_IRQS -- cgit v1.2.3 From 36ca09be6ff77a4e5b54b8b68ed7be7aa184250b Mon Sep 17 00:00:00 2001 From: "Dongsheng.wang@freescale.com" Date: Tue, 9 Apr 2013 10:22:30 +0800 Subject: powerpc/mpic: add global timer support The MPIC global timer is a hardware timer inside the Freescale PIC complying with OpenPIC standard. When the specified interval times out, the hardware timer generates an interrupt. The driver currently is only tested on fsl chip, but it can potentially support other global timers complying to OpenPIC standard. The two independent groups of global timer on fsl chip, group A and group B, are identical in their functionality, except that they appear at different locations within the PIC register map. The hardware timer can be cascaded to create timers larger than the default 31-bit global timers. Timer cascade fields allow configuration of up to two 63-bit timers. But These two groups of timers cannot be cascaded together. It can be used as a wakeup source for low power modes. It also could be used as periodical timer for protocols, drivers and etc. Signed-off-by: Wang Dongsheng Signed-off-by: Li Yang Signed-off-by: Scott Wood --- arch/powerpc/include/asm/mpic_timer.h | 46 +++ arch/powerpc/platforms/Kconfig | 12 + arch/powerpc/sysdev/Makefile | 1 + arch/powerpc/sysdev/mpic_timer.c | 593 ++++++++++++++++++++++++++++++++++ 4 files changed, 652 insertions(+) create mode 100644 arch/powerpc/include/asm/mpic_timer.h create mode 100644 arch/powerpc/sysdev/mpic_timer.c diff --git a/arch/powerpc/include/asm/mpic_timer.h b/arch/powerpc/include/asm/mpic_timer.h new file mode 100644 index 000000000000..0e23cd4ac8aa --- /dev/null +++ b/arch/powerpc/include/asm/mpic_timer.h @@ -0,0 +1,46 @@ +/* + * arch/powerpc/include/asm/mpic_timer.h + * + * Header file for Mpic Global Timer + * + * Copyright 2013 Freescale Semiconductor, Inc. + * + * Author: Wang Dongsheng + * Li Yang + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __MPIC_TIMER__ +#define __MPIC_TIMER__ + +#include +#include + +struct mpic_timer { + void *dev; + struct cascade_priv *cascade_handle; + unsigned int num; + unsigned int irq; +}; + +#ifdef CONFIG_MPIC_TIMER +struct mpic_timer *mpic_request_timer(irq_handler_t fn, void *dev, + const struct timeval *time); +void mpic_start_timer(struct mpic_timer *handle); +void mpic_stop_timer(struct mpic_timer *handle); +void mpic_get_remain_time(struct mpic_timer *handle, struct timeval *time); +void mpic_free_timer(struct mpic_timer *handle); +#else +struct mpic_timer *mpic_request_timer(irq_handler_t fn, void *dev, + const struct timeval *time) { return NULL; } +void mpic_start_timer(struct mpic_timer *handle) { } +void mpic_stop_timer(struct mpic_timer *handle) { } +void mpic_get_remain_time(struct mpic_timer *handle, struct timeval *time) { } +void mpic_free_timer(struct mpic_timer *handle) { } +#endif + +#endif diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig index bed8c607588c..7d07d9e8e1dd 100644 --- a/arch/powerpc/platforms/Kconfig +++ b/arch/powerpc/platforms/Kconfig @@ -86,6 +86,18 @@ config MPIC bool default n +config MPIC_TIMER + bool "MPIC Global Timer" + depends on MPIC && FSL_SOC + default n + help + The MPIC global timer is a hardware timer inside the + Freescale PIC complying with OpenPIC standard. When the + specified interval times out, the hardware timer generates + an interrupt. The driver currently is only tested on fsl + chip, but it can potentially support other global timers + complying with the OpenPIC standard. + config PPC_EPAPR_HV_PIC bool default n diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile index 99464a7bdb3b..2eb72c1a4291 100644 --- a/arch/powerpc/sysdev/Makefile +++ b/arch/powerpc/sysdev/Makefile @@ -4,6 +4,7 @@ ccflags-$(CONFIG_PPC64) := $(NO_MINIMAL_TOC) mpic-msi-obj-$(CONFIG_PCI_MSI) += mpic_msi.o mpic_u3msi.o mpic_pasemi_msi.o obj-$(CONFIG_MPIC) += mpic.o $(mpic-msi-obj-y) +obj-$(CONFIG_MPIC_TIMER) += mpic_timer.o mpic-msgr-obj-$(CONFIG_MPIC_MSGR) += mpic_msgr.o obj-$(CONFIG_MPIC) += mpic.o $(mpic-msi-obj-y) $(mpic-msgr-obj-y) obj-$(CONFIG_PPC_EPAPR_HV_PIC) += ehv_pic.o diff --git a/arch/powerpc/sysdev/mpic_timer.c b/arch/powerpc/sysdev/mpic_timer.c new file mode 100644 index 000000000000..c06db92a4fb1 --- /dev/null +++ b/arch/powerpc/sysdev/mpic_timer.c @@ -0,0 +1,593 @@ +/* + * MPIC timer driver + * + * Copyright 2013 Freescale Semiconductor, Inc. + * Author: Dongsheng Wang + * Li Yang + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define FSL_GLOBAL_TIMER 0x1 + +/* Clock Ratio + * Divide by 64 0x00000300 + * Divide by 32 0x00000200 + * Divide by 16 0x00000100 + * Divide by 8 0x00000000 (Hardware default div) + */ +#define MPIC_TIMER_TCR_CLKDIV 0x00000300 + +#define MPIC_TIMER_TCR_ROVR_OFFSET 24 + +#define TIMER_STOP 0x80000000 +#define TIMERS_PER_GROUP 4 +#define MAX_TICKS (~0U >> 1) +#define MAX_TICKS_CASCADE (~0U) +#define TIMER_OFFSET(num) (1 << (TIMERS_PER_GROUP - 1 - num)) + +/* tv_usec should be less than ONE_SECOND, otherwise use tv_sec */ +#define ONE_SECOND 1000000 + +struct timer_regs { + u32 gtccr; + u32 res0[3]; + u32 gtbcr; + u32 res1[3]; + u32 gtvpr; + u32 res2[3]; + u32 gtdr; + u32 res3[3]; +}; + +struct cascade_priv { + u32 tcr_value; /* TCR register: CASC & ROVR value */ + unsigned int cascade_map; /* cascade map */ + unsigned int timer_num; /* cascade control timer */ +}; + +struct timer_group_priv { + struct timer_regs __iomem *regs; + struct mpic_timer timer[TIMERS_PER_GROUP]; + struct list_head node; + unsigned int timerfreq; + unsigned int idle; + unsigned int flags; + spinlock_t lock; + void __iomem *group_tcr; +}; + +static struct cascade_priv cascade_timer[] = { + /* cascade timer 0 and 1 */ + {0x1, 0xc, 0x1}, + /* cascade timer 1 and 2 */ + {0x2, 0x6, 0x2}, + /* cascade timer 2 and 3 */ + {0x4, 0x3, 0x3} +}; + +static LIST_HEAD(timer_group_list); + +static void convert_ticks_to_time(struct timer_group_priv *priv, + const u64 ticks, struct timeval *time) +{ + u64 tmp_sec; + + time->tv_sec = (__kernel_time_t)div_u64(ticks, priv->timerfreq); + tmp_sec = (u64)time->tv_sec * (u64)priv->timerfreq; + + time->tv_usec = (__kernel_suseconds_t) + div_u64((ticks - tmp_sec) * 1000000, priv->timerfreq); + + return; +} + +/* the time set by the user is converted to "ticks" */ +static int convert_time_to_ticks(struct timer_group_priv *priv, + const struct timeval *time, u64 *ticks) +{ + u64 max_value; /* prevent u64 overflow */ + u64 tmp = 0; + + u64 tmp_sec; + u64 tmp_ms; + u64 tmp_us; + + max_value = div_u64(ULLONG_MAX, priv->timerfreq); + + if (time->tv_sec > max_value || + (time->tv_sec == max_value && time->tv_usec > 0)) + return -EINVAL; + + tmp_sec = (u64)time->tv_sec * (u64)priv->timerfreq; + tmp += tmp_sec; + + tmp_ms = time->tv_usec / 1000; + tmp_ms = div_u64((u64)tmp_ms * (u64)priv->timerfreq, 1000); + tmp += tmp_ms; + + tmp_us = time->tv_usec % 1000; + tmp_us = div_u64((u64)tmp_us * (u64)priv->timerfreq, 1000000); + tmp += tmp_us; + + *ticks = tmp; + + return 0; +} + +/* detect whether there is a cascade timer available */ +static struct mpic_timer *detect_idle_cascade_timer( + struct timer_group_priv *priv) +{ + struct cascade_priv *casc_priv; + unsigned int map; + unsigned int array_size = ARRAY_SIZE(cascade_timer); + unsigned int num; + unsigned int i; + unsigned long flags; + + casc_priv = cascade_timer; + for (i = 0; i < array_size; i++) { + spin_lock_irqsave(&priv->lock, flags); + map = casc_priv->cascade_map & priv->idle; + if (map == casc_priv->cascade_map) { + num = casc_priv->timer_num; + priv->timer[num].cascade_handle = casc_priv; + + /* set timer busy */ + priv->idle &= ~casc_priv->cascade_map; + spin_unlock_irqrestore(&priv->lock, flags); + return &priv->timer[num]; + } + spin_unlock_irqrestore(&priv->lock, flags); + casc_priv++; + } + + return NULL; +} + +static int set_cascade_timer(struct timer_group_priv *priv, u64 ticks, + unsigned int num) +{ + struct cascade_priv *casc_priv; + u32 tcr; + u32 tmp_ticks; + u32 rem_ticks; + + /* set group tcr reg for cascade */ + casc_priv = priv->timer[num].cascade_handle; + if (!casc_priv) + return -EINVAL; + + tcr = casc_priv->tcr_value | + (casc_priv->tcr_value << MPIC_TIMER_TCR_ROVR_OFFSET); + setbits32(priv->group_tcr, tcr); + + tmp_ticks = div_u64_rem(ticks, MAX_TICKS_CASCADE, &rem_ticks); + + out_be32(&priv->regs[num].gtccr, 0); + out_be32(&priv->regs[num].gtbcr, tmp_ticks | TIMER_STOP); + + out_be32(&priv->regs[num - 1].gtccr, 0); + out_be32(&priv->regs[num - 1].gtbcr, rem_ticks); + + return 0; +} + +static struct mpic_timer *get_cascade_timer(struct timer_group_priv *priv, + u64 ticks) +{ + struct mpic_timer *allocated_timer; + + /* Two cascade timers: Support the maximum time */ + const u64 max_ticks = (u64)MAX_TICKS * (u64)MAX_TICKS_CASCADE; + int ret; + + if (ticks > max_ticks) + return NULL; + + /* detect idle timer */ + allocated_timer = detect_idle_cascade_timer(priv); + if (!allocated_timer) + return NULL; + + /* set ticks to timer */ + ret = set_cascade_timer(priv, ticks, allocated_timer->num); + if (ret < 0) + return NULL; + + return allocated_timer; +} + +static struct mpic_timer *get_timer(const struct timeval *time) +{ + struct timer_group_priv *priv; + struct mpic_timer *timer; + + u64 ticks; + unsigned int num; + unsigned int i; + unsigned long flags; + int ret; + + list_for_each_entry(priv, &timer_group_list, node) { + ret = convert_time_to_ticks(priv, time, &ticks); + if (ret < 0) + return NULL; + + if (ticks > MAX_TICKS) { + if (!(priv->flags & FSL_GLOBAL_TIMER)) + return NULL; + + timer = get_cascade_timer(priv, ticks); + if (!timer) + continue; + + return timer; + } + + for (i = 0; i < TIMERS_PER_GROUP; i++) { + /* one timer: Reverse allocation */ + num = TIMERS_PER_GROUP - 1 - i; + spin_lock_irqsave(&priv->lock, flags); + if (priv->idle & (1 << i)) { + /* set timer busy */ + priv->idle &= ~(1 << i); + /* set ticks & stop timer */ + out_be32(&priv->regs[num].gtbcr, + ticks | TIMER_STOP); + out_be32(&priv->regs[num].gtccr, 0); + priv->timer[num].cascade_handle = NULL; + spin_unlock_irqrestore(&priv->lock, flags); + return &priv->timer[num]; + } + spin_unlock_irqrestore(&priv->lock, flags); + } + } + + return NULL; +} + +/** + * mpic_start_timer - start hardware timer + * @handle: the timer to be started. + * + * It will do ->fn(->dev) callback from the hardware interrupt at + * the ->timeval point in the future. + */ +void mpic_start_timer(struct mpic_timer *handle) +{ + struct timer_group_priv *priv = container_of(handle, + struct timer_group_priv, timer[handle->num]); + + clrbits32(&priv->regs[handle->num].gtbcr, TIMER_STOP); +} +EXPORT_SYMBOL(mpic_start_timer); + +/** + * mpic_stop_timer - stop hardware timer + * @handle: the timer to be stoped + * + * The timer periodically generates an interrupt. Unless user stops the timer. + */ +void mpic_stop_timer(struct mpic_timer *handle) +{ + struct timer_group_priv *priv = container_of(handle, + struct timer_group_priv, timer[handle->num]); + struct cascade_priv *casc_priv; + + setbits32(&priv->regs[handle->num].gtbcr, TIMER_STOP); + + casc_priv = priv->timer[handle->num].cascade_handle; + if (casc_priv) { + out_be32(&priv->regs[handle->num].gtccr, 0); + out_be32(&priv->regs[handle->num - 1].gtccr, 0); + } else { + out_be32(&priv->regs[handle->num].gtccr, 0); + } +} +EXPORT_SYMBOL(mpic_stop_timer); + +/** + * mpic_get_remain_time - get timer time + * @handle: the timer to be selected. + * @time: time for timer + * + * Query timer remaining time. + */ +void mpic_get_remain_time(struct mpic_timer *handle, struct timeval *time) +{ + struct timer_group_priv *priv = container_of(handle, + struct timer_group_priv, timer[handle->num]); + struct cascade_priv *casc_priv; + + u64 ticks; + u32 tmp_ticks; + + casc_priv = priv->timer[handle->num].cascade_handle; + if (casc_priv) { + tmp_ticks = in_be32(&priv->regs[handle->num].gtccr); + ticks = ((u64)tmp_ticks & UINT_MAX) * (u64)MAX_TICKS_CASCADE; + tmp_ticks = in_be32(&priv->regs[handle->num - 1].gtccr); + ticks += tmp_ticks; + } else { + ticks = in_be32(&priv->regs[handle->num].gtccr); + } + + convert_ticks_to_time(priv, ticks, time); +} +EXPORT_SYMBOL(mpic_get_remain_time); + +/** + * mpic_free_timer - free hardware timer + * @handle: the timer to be removed. + * + * Free the timer. + * + * Note: can not be used in interrupt context. + */ +void mpic_free_timer(struct mpic_timer *handle) +{ + struct timer_group_priv *priv = container_of(handle, + struct timer_group_priv, timer[handle->num]); + + struct cascade_priv *casc_priv; + unsigned long flags; + + mpic_stop_timer(handle); + + casc_priv = priv->timer[handle->num].cascade_handle; + + free_irq(priv->timer[handle->num].irq, priv->timer[handle->num].dev); + + spin_lock_irqsave(&priv->lock, flags); + if (casc_priv) { + u32 tcr; + tcr = casc_priv->tcr_value | (casc_priv->tcr_value << + MPIC_TIMER_TCR_ROVR_OFFSET); + clrbits32(priv->group_tcr, tcr); + priv->idle |= casc_priv->cascade_map; + priv->timer[handle->num].cascade_handle = NULL; + } else { + priv->idle |= TIMER_OFFSET(handle->num); + } + spin_unlock_irqrestore(&priv->lock, flags); +} +EXPORT_SYMBOL(mpic_free_timer); + +/** + * mpic_request_timer - get a hardware timer + * @fn: interrupt handler function + * @dev: callback function of the data + * @time: time for timer + * + * This executes the "request_irq", returning NULL + * else "handle" on success. + */ +struct mpic_timer *mpic_request_timer(irq_handler_t fn, void *dev, + const struct timeval *time) +{ + struct mpic_timer *allocated_timer; + int ret; + + if (list_empty(&timer_group_list)) + return NULL; + + if (!(time->tv_sec + time->tv_usec) || + time->tv_sec < 0 || time->tv_usec < 0) + return NULL; + + if (time->tv_usec > ONE_SECOND) + return NULL; + + allocated_timer = get_timer(time); + if (!allocated_timer) + return NULL; + + ret = request_irq(allocated_timer->irq, fn, + IRQF_TRIGGER_LOW, "global-timer", dev); + if (ret) { + mpic_free_timer(allocated_timer); + return NULL; + } + + allocated_timer->dev = dev; + + return allocated_timer; +} +EXPORT_SYMBOL(mpic_request_timer); + +static int timer_group_get_freq(struct device_node *np, + struct timer_group_priv *priv) +{ + u32 div; + + if (priv->flags & FSL_GLOBAL_TIMER) { + struct device_node *dn; + + dn = of_find_compatible_node(NULL, NULL, "fsl,mpic"); + if (dn) { + of_property_read_u32(dn, "clock-frequency", + &priv->timerfreq); + of_node_put(dn); + } + } + + if (priv->timerfreq <= 0) + return -EINVAL; + + if (priv->flags & FSL_GLOBAL_TIMER) { + div = (1 << (MPIC_TIMER_TCR_CLKDIV >> 8)) * 8; + priv->timerfreq /= div; + } + + return 0; +} + +static int timer_group_get_irq(struct device_node *np, + struct timer_group_priv *priv) +{ + const u32 all_timer[] = { 0, TIMERS_PER_GROUP }; + const u32 *p; + u32 offset; + u32 count; + + unsigned int i; + unsigned int j; + unsigned int irq_index = 0; + unsigned int irq; + int len; + + p = of_get_property(np, "fsl,available-ranges", &len); + if (p && len % (2 * sizeof(u32)) != 0) { + pr_err("%s: malformed available-ranges property.\n", + np->full_name); + return -EINVAL; + } + + if (!p) { + p = all_timer; + len = sizeof(all_timer); + } + + len /= 2 * sizeof(u32); + + for (i = 0; i < len; i++) { + offset = p[i * 2]; + count = p[i * 2 + 1]; + for (j = 0; j < count; j++) { + irq = irq_of_parse_and_map(np, irq_index); + if (!irq) { + pr_err("%s: irq parse and map failed.\n", + np->full_name); + return -EINVAL; + } + + /* Set timer idle */ + priv->idle |= TIMER_OFFSET((offset + j)); + priv->timer[offset + j].irq = irq; + priv->timer[offset + j].num = offset + j; + irq_index++; + } + } + + return 0; +} + +static void timer_group_init(struct device_node *np) +{ + struct timer_group_priv *priv; + unsigned int i = 0; + int ret; + + priv = kzalloc(sizeof(struct timer_group_priv), GFP_KERNEL); + if (!priv) { + pr_err("%s: cannot allocate memory for group.\n", + np->full_name); + return; + } + + if (of_device_is_compatible(np, "fsl,mpic-global-timer")) + priv->flags |= FSL_GLOBAL_TIMER; + + priv->regs = of_iomap(np, i++); + if (!priv->regs) { + pr_err("%s: cannot ioremap timer register address.\n", + np->full_name); + goto out; + } + + if (priv->flags & FSL_GLOBAL_TIMER) { + priv->group_tcr = of_iomap(np, i++); + if (!priv->group_tcr) { + pr_err("%s: cannot ioremap tcr address.\n", + np->full_name); + goto out; + } + } + + ret = timer_group_get_freq(np, priv); + if (ret < 0) { + pr_err("%s: cannot get timer frequency.\n", np->full_name); + goto out; + } + + ret = timer_group_get_irq(np, priv); + if (ret < 0) { + pr_err("%s: cannot get timer irqs.\n", np->full_name); + goto out; + } + + spin_lock_init(&priv->lock); + + /* Init FSL timer hardware */ + if (priv->flags & FSL_GLOBAL_TIMER) + setbits32(priv->group_tcr, MPIC_TIMER_TCR_CLKDIV); + + list_add_tail(&priv->node, &timer_group_list); + + return; + +out: + if (priv->regs) + iounmap(priv->regs); + + if (priv->group_tcr) + iounmap(priv->group_tcr); + + kfree(priv); +} + +static void mpic_timer_resume(void) +{ + struct timer_group_priv *priv; + + list_for_each_entry(priv, &timer_group_list, node) { + /* Init FSL timer hardware */ + if (priv->flags & FSL_GLOBAL_TIMER) + setbits32(priv->group_tcr, MPIC_TIMER_TCR_CLKDIV); + } +} + +static const struct of_device_id mpic_timer_ids[] = { + { .compatible = "fsl,mpic-global-timer", }, + {}, +}; + +static struct syscore_ops mpic_timer_syscore_ops = { + .resume = mpic_timer_resume, +}; + +static int __init mpic_timer_init(void) +{ + struct device_node *np = NULL; + + for_each_matching_node(np, mpic_timer_ids) + timer_group_init(np); + + register_syscore_ops(&mpic_timer_syscore_ops); + + if (list_empty(&timer_group_list)) + return -ENODEV; + + return 0; +} +subsys_initcall(mpic_timer_init); -- cgit v1.2.3 From 9e6f31a9dbc58f6a5661f8dc8c919441b8d3ced4 Mon Sep 17 00:00:00 2001 From: "Dongsheng.wang@freescale.com" Date: Tue, 9 Apr 2013 10:22:31 +0800 Subject: powerpc/mpic: create mpic subsystem object Register a mpic subsystem at /sys/devices/system/ Signed-off-by: Wang Dongsheng Signed-off-by: Scott Wood --- arch/powerpc/include/asm/mpic.h | 2 ++ arch/powerpc/sysdev/mpic.c | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/arch/powerpc/include/asm/mpic.h b/arch/powerpc/include/asm/mpic.h index ea6bf7220da9..4a1ac9fbf186 100644 --- a/arch/powerpc/include/asm/mpic.h +++ b/arch/powerpc/include/asm/mpic.h @@ -339,6 +339,8 @@ struct mpic #endif }; +extern struct bus_type mpic_subsys; + /* * MPIC flags (passed to mpic_alloc) * diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c index 4635d11f2dc2..1be54faf60dd 100644 --- a/arch/powerpc/sysdev/mpic.c +++ b/arch/powerpc/sysdev/mpic.c @@ -48,6 +48,12 @@ #define DBG(fmt...) #endif +struct bus_type mpic_subsys = { + .name = "mpic", + .dev_name = "mpic", +}; +EXPORT_SYMBOL_GPL(mpic_subsys); + static struct mpic *mpics; static struct mpic *mpic_primary; static DEFINE_RAW_SPINLOCK(mpic_lock); @@ -2035,6 +2041,8 @@ static struct syscore_ops mpic_syscore_ops = { static int mpic_init_sys(void) { register_syscore_ops(&mpic_syscore_ops); + subsys_system_register(&mpic_subsys, NULL); + return 0; } -- cgit v1.2.3 From a63b3bc7db32b63bfe5f48fa8582f931db81c86e Mon Sep 17 00:00:00 2001 From: "Dongsheng.wang@freescale.com" Date: Tue, 9 Apr 2013 10:22:32 +0800 Subject: powerpc/fsl: add MPIC timer wakeup support The driver provides a way to wake up the system by the MPIC timer. For example, echo 5 > /sys/devices/system/mpic/timer_wakeup echo standby > /sys/power/state After 5 seconds the MPIC timer will generate an interrupt to wake up the system. Signed-off-by: Wang Dongsheng Signed-off-by: Zhao Chenhui Signed-off-by: Li Yang --- arch/powerpc/platforms/Kconfig | 9 ++ arch/powerpc/sysdev/Makefile | 1 + arch/powerpc/sysdev/fsl_mpic_timer_wakeup.c | 161 ++++++++++++++++++++++++++++ 3 files changed, 171 insertions(+) create mode 100644 arch/powerpc/sysdev/fsl_mpic_timer_wakeup.c diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig index 7d07d9e8e1dd..0847ee6cd616 100644 --- a/arch/powerpc/platforms/Kconfig +++ b/arch/powerpc/platforms/Kconfig @@ -98,6 +98,15 @@ config MPIC_TIMER chip, but it can potentially support other global timers complying with the OpenPIC standard. +config FSL_MPIC_TIMER_WAKEUP + tristate "Freescale MPIC global timer wakeup driver" + depends on FSL_SOC && MPIC_TIMER && PM + default n + help + The driver provides a way to wake up the system by MPIC + timer. + e.g. "echo 5 > /sys/devices/system/mpic/timer_wakeup" + config PPC_EPAPR_HV_PIC bool default n diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile index 2eb72c1a4291..f67ac900d870 100644 --- a/arch/powerpc/sysdev/Makefile +++ b/arch/powerpc/sysdev/Makefile @@ -5,6 +5,7 @@ ccflags-$(CONFIG_PPC64) := $(NO_MINIMAL_TOC) mpic-msi-obj-$(CONFIG_PCI_MSI) += mpic_msi.o mpic_u3msi.o mpic_pasemi_msi.o obj-$(CONFIG_MPIC) += mpic.o $(mpic-msi-obj-y) obj-$(CONFIG_MPIC_TIMER) += mpic_timer.o +obj-$(CONFIG_FSL_MPIC_TIMER_WAKEUP) += fsl_mpic_timer_wakeup.o mpic-msgr-obj-$(CONFIG_MPIC_MSGR) += mpic_msgr.o obj-$(CONFIG_MPIC) += mpic.o $(mpic-msi-obj-y) $(mpic-msgr-obj-y) obj-$(CONFIG_PPC_EPAPR_HV_PIC) += ehv_pic.o diff --git a/arch/powerpc/sysdev/fsl_mpic_timer_wakeup.c b/arch/powerpc/sysdev/fsl_mpic_timer_wakeup.c new file mode 100644 index 000000000000..1707bf04dec6 --- /dev/null +++ b/arch/powerpc/sysdev/fsl_mpic_timer_wakeup.c @@ -0,0 +1,161 @@ +/* + * MPIC timer wakeup driver + * + * Copyright 2013 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +struct fsl_mpic_timer_wakeup { + struct mpic_timer *timer; + struct work_struct free_work; +}; + +static struct fsl_mpic_timer_wakeup *fsl_wakeup; +static DEFINE_MUTEX(sysfs_lock); + +static void fsl_free_resource(struct work_struct *ws) +{ + struct fsl_mpic_timer_wakeup *wakeup = + container_of(ws, struct fsl_mpic_timer_wakeup, free_work); + + mutex_lock(&sysfs_lock); + + if (wakeup->timer) { + disable_irq_wake(wakeup->timer->irq); + mpic_free_timer(wakeup->timer); + } + + wakeup->timer = NULL; + mutex_unlock(&sysfs_lock); +} + +static irqreturn_t fsl_mpic_timer_irq(int irq, void *dev_id) +{ + struct fsl_mpic_timer_wakeup *wakeup = dev_id; + + schedule_work(&wakeup->free_work); + + return wakeup->timer ? IRQ_HANDLED : IRQ_NONE; +} + +static ssize_t fsl_timer_wakeup_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct timeval interval; + int val = 0; + + mutex_lock(&sysfs_lock); + if (fsl_wakeup->timer) { + mpic_get_remain_time(fsl_wakeup->timer, &interval); + val = interval.tv_sec + 1; + } + mutex_unlock(&sysfs_lock); + + return sprintf(buf, "%d\n", val); +} + +static ssize_t fsl_timer_wakeup_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct timeval interval; + int ret; + + interval.tv_usec = 0; + if (kstrtol(buf, 0, &interval.tv_sec)) + return -EINVAL; + + mutex_lock(&sysfs_lock); + + if (fsl_wakeup->timer) { + disable_irq_wake(fsl_wakeup->timer->irq); + mpic_free_timer(fsl_wakeup->timer); + fsl_wakeup->timer = NULL; + } + + if (!interval.tv_sec) { + mutex_unlock(&sysfs_lock); + return count; + } + + fsl_wakeup->timer = mpic_request_timer(fsl_mpic_timer_irq, + fsl_wakeup, &interval); + if (!fsl_wakeup->timer) { + mutex_unlock(&sysfs_lock); + return -EINVAL; + } + + ret = enable_irq_wake(fsl_wakeup->timer->irq); + if (ret) { + mpic_free_timer(fsl_wakeup->timer); + fsl_wakeup->timer = NULL; + mutex_unlock(&sysfs_lock); + + return ret; + } + + mpic_start_timer(fsl_wakeup->timer); + + mutex_unlock(&sysfs_lock); + + return count; +} + +static struct device_attribute mpic_attributes = __ATTR(timer_wakeup, 0644, + fsl_timer_wakeup_show, fsl_timer_wakeup_store); + +static int __init fsl_wakeup_sys_init(void) +{ + int ret; + + fsl_wakeup = kzalloc(sizeof(struct fsl_mpic_timer_wakeup), GFP_KERNEL); + if (!fsl_wakeup) + return -ENOMEM; + + INIT_WORK(&fsl_wakeup->free_work, fsl_free_resource); + + ret = device_create_file(mpic_subsys.dev_root, &mpic_attributes); + if (ret) + kfree(fsl_wakeup); + + return ret; +} + +static void __exit fsl_wakeup_sys_exit(void) +{ + device_remove_file(mpic_subsys.dev_root, &mpic_attributes); + + mutex_lock(&sysfs_lock); + + if (fsl_wakeup->timer) { + disable_irq_wake(fsl_wakeup->timer->irq); + mpic_free_timer(fsl_wakeup->timer); + } + + kfree(fsl_wakeup); + + mutex_unlock(&sysfs_lock); +} + +module_init(fsl_wakeup_sys_init); +module_exit(fsl_wakeup_sys_exit); + +MODULE_DESCRIPTION("Freescale MPIC global timer wakeup driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Wang Dongsheng "); -- cgit v1.2.3 From 5a008ffa73b4401251d548c10cadac6f8a67cfb5 Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Fri, 28 Jun 2013 17:17:49 +0200 Subject: net: qmi_wwan: fixup Sierra Wireless MC8305 entry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MC8305 module got an additional entry added based solely on information from a Windows driver *.inf file. We now have the actual descriptor layout from one of these modules, and it consists of two alternate configurations where cfg #1 is a normal Gobi 2k layout and cfg #2 is MBIM only, using interface numbers 5 and 6 for MBIM control and data. The extra Windows driver entry for interface number 5 was most likely a bug. Deleting the bogus entry to avoid unnecessary qmi_wwan probe failures when using the MBIM configuration. Reported-by: Lana Black Signed-off-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/qmi_wwan.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index d095d0d3056b..1d1021c50bc9 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -625,7 +625,6 @@ static const struct usb_device_id products[] = { {QMI_GOBI_DEVICE(0x1199, 0x9009)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */ {QMI_GOBI_DEVICE(0x1199, 0x900a)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */ {QMI_GOBI_DEVICE(0x1199, 0x9011)}, /* Sierra Wireless Gobi 2000 Modem device (MC8305) */ - {QMI_FIXED_INTF(0x1199, 0x9011, 5)}, /* alternate interface number!? */ {QMI_GOBI_DEVICE(0x16d8, 0x8002)}, /* CMDTech Gobi 2000 Modem device (VU922) */ {QMI_GOBI_DEVICE(0x05c6, 0x9205)}, /* Gobi 2000 Modem device */ {QMI_GOBI_DEVICE(0x1199, 0x9013)}, /* Sierra Wireless Gobi 3000 Modem device (MC8355) */ -- cgit v1.2.3 From aa3aba1cbcbe823bba623c7cab33d84ddf0fb6cd Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Fri, 28 Jun 2013 17:17:50 +0200 Subject: net: qmi_wwan: add Option GTM681W MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A standard Gobi 3000 reference design module. Reported-by: Richard Weinberger Signed-off-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/qmi_wwan.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 1d1021c50bc9..e4a0843b9bb6 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -612,6 +612,7 @@ static const struct usb_device_id products[] = { {QMI_GOBI_DEVICE(0x05c6, 0x9265)}, /* Asus Gobi 2000 Modem device (VR305) */ {QMI_GOBI_DEVICE(0x05c6, 0x9235)}, /* Top Global Gobi 2000 Modem device (VR306) */ {QMI_GOBI_DEVICE(0x05c6, 0x9275)}, /* iRex Technologies Gobi 2000 Modem device (VR307) */ + {QMI_GOBI_DEVICE(0x0af0, 0x8120)}, /* Option GTM681W */ {QMI_GOBI_DEVICE(0x1199, 0x68a5)}, /* Sierra Wireless Modem */ {QMI_GOBI_DEVICE(0x1199, 0x68a9)}, /* Sierra Wireless Modem */ {QMI_GOBI_DEVICE(0x1199, 0x9001)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */ -- cgit v1.2.3 From d0b5e516298fba30774e2df22cfbd00ecb09c298 Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Fri, 28 Jun 2013 17:17:51 +0200 Subject: net: qmi_wwan: add TP-LINK MA260 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/qmi_wwan.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index e4a0843b9bb6..6de110263109 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -582,6 +582,7 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x1199, 0x901c, 8)}, /* Sierra Wireless EM7700 */ {QMI_FIXED_INTF(0x1bbb, 0x011e, 4)}, /* Telekom Speedstick LTE II (Alcatel One Touch L100V LTE) */ {QMI_FIXED_INTF(0x2357, 0x0201, 4)}, /* TP-LINK HSUPA Modem MA180 */ + {QMI_FIXED_INTF(0x2357, 0x9000, 4)}, /* TP-LINK MA260 */ {QMI_FIXED_INTF(0x1bc7, 0x1200, 5)}, /* Telit LE920 */ {QMI_FIXED_INTF(0x1e2d, 0x12d1, 4)}, /* Cinterion PLxx */ -- cgit v1.2.3 From 6e94a780374ed31b280f939d4757e8d7858dff16 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Thu, 27 Jun 2013 10:58:31 -0400 Subject: tracing: Failed to create system directory Running the following: # cd /sys/kernel/debug/tracing # echo p:i do_sys_open > kprobe_events # echo p:j schedule >> kprobe_events # cat kprobe_events p:kprobes/i do_sys_open p:kprobes/j schedule # echo p:i do_sys_open >> kprobe_events # cat kprobe_events p:kprobes/j schedule p:kprobes/i do_sys_open # ls /sys/kernel/debug/tracing/events/kprobes/ enable filter j Notice that the 'i' is missing from the kprobes directory. The console produces: "Failed to create system directory kprobes" This is because kprobes passes in a allocated name for the system and the ftrace event subsystem saves off that name instead of creating a duplicate for it. But the kprobes may free the system name making the pointer to it invalid. This bug was introduced by 92edca073c37 "tracing: Use direct field, type and system names" which switched from using kstrdup() on the system name in favor of just keeping apointer to it, as the internal ftrace event system names are static and exist for the life of the computer being booted. Instead of reverting back to duplicating system names again, we can use core_kernel_data() to determine if the passed in name was allocated or static. Then use the MSB of the ref_count to be a flag to keep track if the name was allocated or not. Then we can still save from having to duplicate strings that will always exist, but still copy the ones that may be freed. Cc: stable@vger.kernel.org # 3.10 Reported-by: "zhangwei(Jovi)" Reported-by: Masami Hiramatsu Tested-by: Masami Hiramatsu Signed-off-by: Steven Rostedt --- kernel/trace/trace_events.c | 41 +++++++++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index f57b01574a30..903a0bf2685e 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -41,6 +41,23 @@ static LIST_HEAD(ftrace_common_fields); static struct kmem_cache *field_cachep; static struct kmem_cache *file_cachep; +#define SYSTEM_FL_FREE_NAME (1 << 31) + +static inline int system_refcount(struct event_subsystem *system) +{ + return system->ref_count & ~SYSTEM_FL_FREE_NAME; +} + +static int system_refcount_inc(struct event_subsystem *system) +{ + return (system->ref_count++) & ~SYSTEM_FL_FREE_NAME; +} + +static int system_refcount_dec(struct event_subsystem *system) +{ + return (--system->ref_count) & ~SYSTEM_FL_FREE_NAME; +} + /* Double loops, do not use break, only goto's work */ #define do_for_each_event_file(tr, file) \ list_for_each_entry(tr, &ftrace_trace_arrays, list) { \ @@ -344,8 +361,8 @@ static void __put_system(struct event_subsystem *system) { struct event_filter *filter = system->filter; - WARN_ON_ONCE(system->ref_count == 0); - if (--system->ref_count) + WARN_ON_ONCE(system_refcount(system) == 0); + if (system_refcount_dec(system)) return; list_del(&system->list); @@ -354,13 +371,15 @@ static void __put_system(struct event_subsystem *system) kfree(filter->filter_string); kfree(filter); } + if (system->ref_count & SYSTEM_FL_FREE_NAME) + kfree(system->name); kfree(system); } static void __get_system(struct event_subsystem *system) { - WARN_ON_ONCE(system->ref_count == 0); - system->ref_count++; + WARN_ON_ONCE(system_refcount(system) == 0); + system_refcount_inc(system); } static void __get_system_dir(struct ftrace_subsystem_dir *dir) @@ -374,7 +393,7 @@ static void __put_system_dir(struct ftrace_subsystem_dir *dir) { WARN_ON_ONCE(dir->ref_count == 0); /* If the subsystem is about to be freed, the dir must be too */ - WARN_ON_ONCE(dir->subsystem->ref_count == 1 && dir->ref_count != 1); + WARN_ON_ONCE(system_refcount(dir->subsystem) == 1 && dir->ref_count != 1); __put_system(dir->subsystem); if (!--dir->ref_count) @@ -1274,7 +1293,15 @@ create_new_subsystem(const char *name) return NULL; system->ref_count = 1; - system->name = name; + + /* Only allocate if dynamic (kprobes and modules) */ + if (!core_kernel_data((unsigned long)name)) { + system->ref_count |= SYSTEM_FL_FREE_NAME; + system->name = kstrdup(name, GFP_KERNEL); + if (!system->name) + goto out_free; + } else + system->name = name; system->filter = NULL; @@ -1287,6 +1314,8 @@ create_new_subsystem(const char *name) return system; out_free: + if (system->ref_count & SYSTEM_FL_FREE_NAME) + kfree(system->name); kfree(system); return NULL; } -- cgit v1.2.3 From 288e984e622336bab8bc3dfdf2f190816362d9a1 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Thu, 20 Jun 2013 19:38:06 +0200 Subject: tracing/kprobes: Avoid perf_trace_buf_*() if ->perf_events is empty perf_trace_buf_prepare() + perf_trace_buf_submit() make no sense if this task/CPU has no active counters. Change kprobe_perf_func() and kretprobe_perf_func() to check call->perf_events beforehand and return if this list is empty. For example, "perf record -e some_probe -p1". Only /sbin/init will report, all other threads which hit the same probe will do perf_trace_buf_prepare/perf_trace_buf_submit just to realize that nobody wants perf_swevent_event(). Link: http://lkml.kernel.org/r/20130620173806.GA13151@redhat.com Acked-by: Masami Hiramatsu Signed-off-by: Oleg Nesterov Signed-off-by: Steven Rostedt --- kernel/trace/trace_kprobe.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c index f2374172ba7b..c35bebe53ffe 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c @@ -1156,6 +1156,10 @@ kprobe_perf_func(struct trace_probe *tp, struct pt_regs *regs) int size, __size, dsize; int rctx; + head = this_cpu_ptr(call->perf_events); + if (hlist_empty(head)) + return; + dsize = __get_data_size(tp, regs); __size = sizeof(*entry) + tp->size + dsize; size = ALIGN(__size + sizeof(u32), sizeof(u64)); @@ -1171,8 +1175,6 @@ kprobe_perf_func(struct trace_probe *tp, struct pt_regs *regs) entry->ip = (unsigned long)tp->rp.kp.addr; memset(&entry[1], 0, dsize); store_trace_args(sizeof(*entry), tp, regs, (u8 *)&entry[1], dsize); - - head = this_cpu_ptr(call->perf_events); perf_trace_buf_submit(entry, size, rctx, entry->ip, 1, regs, head, NULL); } @@ -1188,6 +1190,10 @@ kretprobe_perf_func(struct trace_probe *tp, struct kretprobe_instance *ri, int size, __size, dsize; int rctx; + head = this_cpu_ptr(call->perf_events); + if (hlist_empty(head)) + return; + dsize = __get_data_size(tp, regs); __size = sizeof(*entry) + tp->size + dsize; size = ALIGN(__size + sizeof(u32), sizeof(u64)); @@ -1203,8 +1209,6 @@ kretprobe_perf_func(struct trace_probe *tp, struct kretprobe_instance *ri, entry->func = (unsigned long)tp->rp.kp.addr; entry->ret_ip = (unsigned long)ri->ret_addr; store_trace_args(sizeof(*entry), tp, regs, (u8 *)&entry[1], dsize); - - head = this_cpu_ptr(call->perf_events); perf_trace_buf_submit(entry, size, rctx, entry->ret_ip, 1, regs, head, NULL); } -- cgit v1.2.3 From 3fe3d6193e7cd7b4dd2bde10772f048bdefea4ee Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Thu, 20 Jun 2013 19:38:09 +0200 Subject: tracing/kprobes: Kill probe_enable_lock enable_trace_probe() and disable_trace_probe() should not worry about serialization, the caller (perf_trace_init or __ftrace_set_clr_event) holds event_mutex. They are also called by kprobe_trace_self_tests_init(), but this __init function can't race with itself or trace_events.c And note that this code depended on event_mutex even before 41a7dd420c which introduced probe_enable_lock. In fact it assumes that the caller kprobe_register() can never race with itself. Otherwise, say, tp->flags manipulations are racy. Link: http://lkml.kernel.org/r/20130620173809.GA13158@redhat.com Acked-by: Masami Hiramatsu Signed-off-by: Oleg Nesterov Signed-off-by: Steven Rostedt --- kernel/trace/trace_kprobe.c | 43 ++++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c index c35bebe53ffe..282f86cfd304 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c @@ -183,16 +183,15 @@ static struct trace_probe *find_trace_probe(const char *event, return NULL; } +/* + * This and enable_trace_probe/disable_trace_probe rely on event_mutex + * held by the caller, __ftrace_set_clr_event(). + */ static int trace_probe_nr_files(struct trace_probe *tp) { - struct ftrace_event_file **file; + struct ftrace_event_file **file = rcu_dereference_raw(tp->files); int ret = 0; - /* - * Since all tp->files updater is protected by probe_enable_lock, - * we don't need to lock an rcu_read_lock. - */ - file = rcu_dereference_raw(tp->files); if (file) while (*(file++)) ret++; @@ -200,8 +199,6 @@ static int trace_probe_nr_files(struct trace_probe *tp) return ret; } -static DEFINE_MUTEX(probe_enable_lock); - /* * Enable trace_probe * if the file is NULL, enable "perf" handler, or enable "trace" handler. @@ -211,8 +208,6 @@ enable_trace_probe(struct trace_probe *tp, struct ftrace_event_file *file) { int ret = 0; - mutex_lock(&probe_enable_lock); - if (file) { struct ftrace_event_file **new, **old; int n = trace_probe_nr_files(tp); @@ -223,7 +218,7 @@ enable_trace_probe(struct trace_probe *tp, struct ftrace_event_file *file) GFP_KERNEL); if (!new) { ret = -ENOMEM; - goto out_unlock; + goto out; } memcpy(new, old, n * sizeof(struct ftrace_event_file *)); new[n] = file; @@ -246,10 +241,7 @@ enable_trace_probe(struct trace_probe *tp, struct ftrace_event_file *file) else ret = enable_kprobe(&tp->rp.kp); } - - out_unlock: - mutex_unlock(&probe_enable_lock); - + out: return ret; } @@ -282,8 +274,6 @@ disable_trace_probe(struct trace_probe *tp, struct ftrace_event_file *file) { int ret = 0; - mutex_lock(&probe_enable_lock); - if (file) { struct ftrace_event_file **new, **old; int n = trace_probe_nr_files(tp); @@ -292,7 +282,7 @@ disable_trace_probe(struct trace_probe *tp, struct ftrace_event_file *file) old = rcu_dereference_raw(tp->files); if (n == 0 || trace_probe_file_index(tp, file) < 0) { ret = -EINVAL; - goto out_unlock; + goto out; } if (n == 1) { /* Remove the last file */ @@ -303,7 +293,7 @@ disable_trace_probe(struct trace_probe *tp, struct ftrace_event_file *file) GFP_KERNEL); if (!new) { ret = -ENOMEM; - goto out_unlock; + goto out; } /* This copy & check loop copies the NULL stopper too */ @@ -326,10 +316,7 @@ disable_trace_probe(struct trace_probe *tp, struct ftrace_event_file *file) else disable_kprobe(&tp->rp.kp); } - - out_unlock: - mutex_unlock(&probe_enable_lock); - + out: return ret; } @@ -1214,6 +1201,12 @@ kretprobe_perf_func(struct trace_probe *tp, struct kretprobe_instance *ri, } #endif /* CONFIG_PERF_EVENTS */ +/* + * called by perf_trace_init() or __ftrace_set_clr_event() under event_mutex. + * + * kprobe_trace_self_tests_init() does enable_trace_probe/disable_trace_probe + * lockless, but we can't race with this __init function. + */ static __kprobes int kprobe_register(struct ftrace_event_call *event, enum trace_reg type, void *data) @@ -1379,6 +1372,10 @@ find_trace_probe_file(struct trace_probe *tp, struct trace_array *tr) return NULL; } +/* + * Nobody but us can call enable_trace_probe/disable_trace_probe at this + * stage, we can do this lockless. + */ static __init int kprobe_trace_self_tests_init(void) { int ret, warn = 0; -- cgit v1.2.3 From a439059610ecd257dba29a612729132e470d118f Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Sat, 29 Jun 2013 00:08:04 -0500 Subject: tracing: Simplify code for showing of soft disabled flag Rather than enumerating each permutation, build the enable state string up from the combination of states. This also allows for the simpler addition of more states. Link: http://lkml.kernel.org/r/9aff5af6dee2f5a40ca30df41c39d5f33e998d7a.1372479499.git.tom.zanussi@linux.intel.com Signed-off-by: Tom Zanussi Signed-off-by: Steven Rostedt --- kernel/trace/trace_events.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 903a0bf2685e..7ee08b95c384 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -638,17 +638,17 @@ event_enable_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) { struct ftrace_event_file *file = filp->private_data; - char *buf; + char buf[4] = "0"; - if (file->flags & FTRACE_EVENT_FL_ENABLED) { - if (file->flags & FTRACE_EVENT_FL_SOFT_DISABLED) - buf = "0*\n"; - else if (file->flags & FTRACE_EVENT_FL_SOFT_MODE) - buf = "1*\n"; - else - buf = "1\n"; - } else - buf = "0\n"; + if (file->flags & FTRACE_EVENT_FL_ENABLED && + !(file->flags & FTRACE_EVENT_FL_SOFT_DISABLED)) + strcpy(buf, "1"); + + if (file->flags & FTRACE_EVENT_FL_SOFT_DISABLED || + file->flags & FTRACE_EVENT_FL_SOFT_MODE) + strcat(buf, "*"); + + strcat(buf, "\n"); return simple_read_from_buffer(ubuf, cnt, ppos, buf, strlen(buf)); } -- cgit v1.2.3 From 44a6a4ee1aed0eada8f21e6db81b4cd099788f82 Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Sat, 29 Jun 2013 00:08:05 -0500 Subject: tracing: Add missing syscall_metadata comment Add the missing syscall_metadata description for the enter_fields struct member. Link: http://lkml.kernel.org/r/74c3407cd1e5d37f2c5aaca637aa4d35f66f1aa2.1372479499.git.tom.zanussi@linux.intel.com Signed-off-by: Tom Zanussi Signed-off-by: Steven Rostedt --- include/trace/syscall.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/trace/syscall.h b/include/trace/syscall.h index 84bc4197e736..fed853f3d7aa 100644 --- a/include/trace/syscall.h +++ b/include/trace/syscall.h @@ -16,6 +16,7 @@ * @nb_args: number of parameters it takes * @types: list of types as strings * @args: list of args as strings (args[i] matches types[i]) + * @enter_fields: list of fields for syscall_enter trace event * @enter_event: associated syscall_enter trace event * @exit_event: associated syscall_exit trace event */ -- cgit v1.2.3 From 3baa5e4cf224b8a55220cc841bb475e164b84ceb Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Sat, 29 Jun 2013 00:08:07 -0500 Subject: tracing: Fix disabling of soft disable The comment on the soft disable 'disable' case of __ftrace_event_enable_disable() states that the soft disable bit should be cleared in that case, but currently only the soft mode bit is actually cleared. This essentially leaves the standard non-soft-enable enable/disable paths as the only way to clear the soft disable flag, but the soft disable bit should also be cleared when removing a trigger with '!'. Also, the SOFT_DISABLED bit should never be set if SOFT_MODE is cleared. This fixes the above discrepancies. Link: http://lkml.kernel.org/r/b9c68dd50bc07019e6c67d3f9b29be4ef1b2badb.1372479499.git.tom.zanussi@linux.intel.com Signed-off-by: Tom Zanussi Signed-off-by: Steven Rostedt --- kernel/trace/trace_events.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 7ee08b95c384..5892470bc2ee 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -291,9 +291,11 @@ static int __ftrace_event_enable_disable(struct ftrace_event_file *file, } call->class->reg(call, TRACE_REG_UNREGISTER, file); } - /* If in SOFT_MODE, just set the SOFT_DISABLE_BIT */ + /* If in SOFT_MODE, just set the SOFT_DISABLE_BIT, else clear it */ if (file->flags & FTRACE_EVENT_FL_SOFT_MODE) set_bit(FTRACE_EVENT_FL_SOFT_DISABLED_BIT, &file->flags); + else + clear_bit(FTRACE_EVENT_FL_SOFT_DISABLED_BIT, &file->flags); break; case 1: /* -- cgit v1.2.3 From b04d52e368e2cf526abb2bab61f304eaea126af2 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Thu, 20 Jun 2013 19:38:14 +0200 Subject: tracing/kprobes: Turn trace_probe->files into list_head I think that "ftrace_event_file *trace_probe[]" complicates the code for no reason, turn it into list_head to simplify the code. enable_trace_probe() no longer needs synchronize_sched(). This needs the extra sizeof(list_head) memory for every attached ftrace_event_file, hopefully not a problem in this case. Link: http://lkml.kernel.org/r/20130620173814.GA13165@redhat.com Acked-by: Masami Hiramatsu Signed-off-by: Oleg Nesterov Signed-off-by: Steven Rostedt --- kernel/trace/trace_kprobe.c | 138 ++++++++++++-------------------------------- 1 file changed, 37 insertions(+), 101 deletions(-) diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c index 282f86cfd304..405b5b0f903e 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c @@ -35,12 +35,17 @@ struct trace_probe { const char *symbol; /* symbol name */ struct ftrace_event_class class; struct ftrace_event_call call; - struct ftrace_event_file * __rcu *files; + struct list_head files; ssize_t size; /* trace entry size */ unsigned int nr_args; struct probe_arg args[]; }; +struct event_file_link { + struct ftrace_event_file *file; + struct list_head list; +}; + #define SIZEOF_TRACE_PROBE(n) \ (offsetof(struct trace_probe, args) + \ (sizeof(struct probe_arg) * (n))) @@ -150,6 +155,7 @@ static struct trace_probe *alloc_trace_probe(const char *group, goto error; INIT_LIST_HEAD(&tp->list); + INIT_LIST_HEAD(&tp->files); return tp; error: kfree(tp->call.name); @@ -183,22 +189,6 @@ static struct trace_probe *find_trace_probe(const char *event, return NULL; } -/* - * This and enable_trace_probe/disable_trace_probe rely on event_mutex - * held by the caller, __ftrace_set_clr_event(). - */ -static int trace_probe_nr_files(struct trace_probe *tp) -{ - struct ftrace_event_file **file = rcu_dereference_raw(tp->files); - int ret = 0; - - if (file) - while (*(file++)) - ret++; - - return ret; -} - /* * Enable trace_probe * if the file is NULL, enable "perf" handler, or enable "trace" handler. @@ -209,29 +199,18 @@ enable_trace_probe(struct trace_probe *tp, struct ftrace_event_file *file) int ret = 0; if (file) { - struct ftrace_event_file **new, **old; - int n = trace_probe_nr_files(tp); - - old = rcu_dereference_raw(tp->files); - /* 1 is for new one and 1 is for stopper */ - new = kzalloc((n + 2) * sizeof(struct ftrace_event_file *), - GFP_KERNEL); - if (!new) { + struct event_file_link *link; + + link = kmalloc(sizeof(*link), GFP_KERNEL); + if (!link) { ret = -ENOMEM; goto out; } - memcpy(new, old, n * sizeof(struct ftrace_event_file *)); - new[n] = file; - /* The last one keeps a NULL */ - rcu_assign_pointer(tp->files, new); - tp->flags |= TP_FLAG_TRACE; + link->file = file; + list_add_tail_rcu(&link->list, &tp->files); - if (old) { - /* Make sure the probe is done with old files */ - synchronize_sched(); - kfree(old); - } + tp->flags |= TP_FLAG_TRACE; } else tp->flags |= TP_FLAG_PROFILE; @@ -245,24 +224,16 @@ enable_trace_probe(struct trace_probe *tp, struct ftrace_event_file *file) return ret; } -static int -trace_probe_file_index(struct trace_probe *tp, struct ftrace_event_file *file) +static struct event_file_link * +find_event_file_link(struct trace_probe *tp, struct ftrace_event_file *file) { - struct ftrace_event_file **files; - int i; + struct event_file_link *link; - /* - * Since all tp->files updater is protected by probe_enable_lock, - * we don't need to lock an rcu_read_lock. - */ - files = rcu_dereference_raw(tp->files); - if (files) { - for (i = 0; files[i]; i++) - if (files[i] == file) - return i; - } + list_for_each_entry(link, &tp->files, list) + if (link->file == file) + return link; - return -1; + return NULL; } /* @@ -275,38 +246,23 @@ disable_trace_probe(struct trace_probe *tp, struct ftrace_event_file *file) int ret = 0; if (file) { - struct ftrace_event_file **new, **old; - int n = trace_probe_nr_files(tp); - int i, j; + struct event_file_link *link; - old = rcu_dereference_raw(tp->files); - if (n == 0 || trace_probe_file_index(tp, file) < 0) { + link = find_event_file_link(tp, file); + if (!link) { ret = -EINVAL; goto out; } - if (n == 1) { /* Remove the last file */ - tp->flags &= ~TP_FLAG_TRACE; - new = NULL; - } else { - new = kzalloc(n * sizeof(struct ftrace_event_file *), - GFP_KERNEL); - if (!new) { - ret = -ENOMEM; - goto out; - } - - /* This copy & check loop copies the NULL stopper too */ - for (i = 0, j = 0; j < n && i < n + 1; i++) - if (old[i] != file) - new[j++] = old[i]; - } + list_del_rcu(&link->list); + /* synchronize with kprobe_trace_func/kretprobe_trace_func */ + synchronize_sched(); + kfree(link); - rcu_assign_pointer(tp->files, new); + if (!list_empty(&tp->files)) + goto out; - /* Make sure the probe is done with old files */ - synchronize_sched(); - kfree(old); + tp->flags &= ~TP_FLAG_TRACE; } else tp->flags &= ~TP_FLAG_PROFILE; @@ -871,20 +827,10 @@ __kprobe_trace_func(struct trace_probe *tp, struct pt_regs *regs, static __kprobes void kprobe_trace_func(struct trace_probe *tp, struct pt_regs *regs) { - /* - * Note: preempt is already disabled around the kprobe handler. - * However, we still need an smp_read_barrier_depends() corresponding - * to smp_wmb() in rcu_assign_pointer() to access the pointer. - */ - struct ftrace_event_file **file = rcu_dereference_raw(tp->files); - - if (unlikely(!file)) - return; + struct event_file_link *link; - while (*file) { - __kprobe_trace_func(tp, regs, *file); - file++; - } + list_for_each_entry_rcu(link, &tp->files, list) + __kprobe_trace_func(tp, regs, link->file); } /* Kretprobe handler */ @@ -931,20 +877,10 @@ static __kprobes void kretprobe_trace_func(struct trace_probe *tp, struct kretprobe_instance *ri, struct pt_regs *regs) { - /* - * Note: preempt is already disabled around the kprobe handler. - * However, we still need an smp_read_barrier_depends() corresponding - * to smp_wmb() in rcu_assign_pointer() to access the pointer. - */ - struct ftrace_event_file **file = rcu_dereference_raw(tp->files); - - if (unlikely(!file)) - return; + struct event_file_link *link; - while (*file) { - __kretprobe_trace_func(tp, ri, regs, *file); - file++; - } + list_for_each_entry_rcu(link, &tp->files, list) + __kretprobe_trace_func(tp, ri, regs, link->file); } /* Event entry printers */ -- cgit v1.2.3 From 10246fa35d4ffdfe472185d4cbf9c2dfd9a9f023 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Mon, 1 Jul 2013 15:58:24 -0400 Subject: tracing: Use flag buffer_disabled for irqsoff tracer If the ring buffer is disabled and the irqsoff tracer records a trace it will clear out its buffer and lose the data it had previously recorded. Currently there's a callback when writing to the tracing_of file, but if tracing is disabled via the function tracer trigger, it will not inform the irqsoff tracer to stop recording. By using the "mirror" flag (buffer_disabled) in the trace_array, that keeps track of the status of the trace_array's buffer, it gives the irqsoff tracer a fast way to know if it should record a new trace or not. The flag may be a little behind the real state of the buffer, but it should not affect the trace too much. It's more important for the irqsoff tracer to be fast. Reported-by: Dave Jones Signed-off-by: Steven Rostedt --- kernel/trace/trace.c | 101 ++++++++++++++++++++++++++++++------------- kernel/trace/trace_irqsoff.c | 4 +- 2 files changed, 72 insertions(+), 33 deletions(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index c4c9296b1916..0dc50711d656 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -226,9 +226,24 @@ cycle_t ftrace_now(int cpu) return ts; } +/** + * tracing_is_enabled - Show if global_trace has been disabled + * + * Shows if the global trace has been enabled or not. It uses the + * mirror flag "buffer_disabled" to be used in fast paths such as for + * the irqsoff tracer. But it may be inaccurate due to races. If you + * need to know the accurate state, use tracing_is_on() which is a little + * slower, but accurate. + */ int tracing_is_enabled(void) { - return tracing_is_on(); + /* + * For quick access (irqsoff uses this in fast path), just + * return the mirror variable of the state of the ring buffer. + * It's a little racy, but we don't really care. + */ + smp_rmb(); + return !global_trace.buffer_disabled; } /* @@ -341,6 +356,23 @@ unsigned long trace_flags = TRACE_ITER_PRINT_PARENT | TRACE_ITER_PRINTK | TRACE_ITER_GRAPH_TIME | TRACE_ITER_RECORD_CMD | TRACE_ITER_OVERWRITE | TRACE_ITER_IRQ_INFO | TRACE_ITER_MARKERS | TRACE_ITER_FUNCTION; +void tracer_tracing_on(struct trace_array *tr) +{ + if (tr->trace_buffer.buffer) + ring_buffer_record_on(tr->trace_buffer.buffer); + /* + * This flag is looked at when buffers haven't been allocated + * yet, or by some tracers (like irqsoff), that just want to + * know if the ring buffer has been disabled, but it can handle + * races of where it gets disabled but we still do a record. + * As the check is in the fast path of the tracers, it is more + * important to be fast than accurate. + */ + tr->buffer_disabled = 0; + /* Make the flag seen by readers */ + smp_wmb(); +} + /** * tracing_on - enable tracing buffers * @@ -349,15 +381,7 @@ unsigned long trace_flags = TRACE_ITER_PRINT_PARENT | TRACE_ITER_PRINTK | */ void tracing_on(void) { - if (global_trace.trace_buffer.buffer) - ring_buffer_record_on(global_trace.trace_buffer.buffer); - /* - * This flag is only looked at when buffers haven't been - * allocated yet. We don't really care about the race - * between setting this flag and actually turning - * on the buffer. - */ - global_trace.buffer_disabled = 0; + tracer_tracing_on(&global_trace); } EXPORT_SYMBOL_GPL(tracing_on); @@ -551,6 +575,23 @@ void tracing_snapshot_alloc(void) EXPORT_SYMBOL_GPL(tracing_snapshot_alloc); #endif /* CONFIG_TRACER_SNAPSHOT */ +void tracer_tracing_off(struct trace_array *tr) +{ + if (tr->trace_buffer.buffer) + ring_buffer_record_off(tr->trace_buffer.buffer); + /* + * This flag is looked at when buffers haven't been allocated + * yet, or by some tracers (like irqsoff), that just want to + * know if the ring buffer has been disabled, but it can handle + * races of where it gets disabled but we still do a record. + * As the check is in the fast path of the tracers, it is more + * important to be fast than accurate. + */ + tr->buffer_disabled = 1; + /* Make the flag seen by readers */ + smp_wmb(); +} + /** * tracing_off - turn off tracing buffers * @@ -561,15 +602,7 @@ EXPORT_SYMBOL_GPL(tracing_snapshot_alloc); */ void tracing_off(void) { - if (global_trace.trace_buffer.buffer) - ring_buffer_record_off(global_trace.trace_buffer.buffer); - /* - * This flag is only looked at when buffers haven't been - * allocated yet. We don't really care about the race - * between setting this flag and actually turning - * on the buffer. - */ - global_trace.buffer_disabled = 1; + tracer_tracing_off(&global_trace); } EXPORT_SYMBOL_GPL(tracing_off); @@ -579,14 +612,25 @@ void disable_trace_on_warning(void) tracing_off(); } +/** + * tracer_tracing_is_on - show real state of ring buffer enabled + * @tr : the trace array to know if ring buffer is enabled + * + * Shows real state of the ring buffer if it is enabled or not. + */ +int tracer_tracing_is_on(struct trace_array *tr) +{ + if (tr->trace_buffer.buffer) + return ring_buffer_record_is_on(tr->trace_buffer.buffer); + return !tr->buffer_disabled; +} + /** * tracing_is_on - show state of ring buffers enabled */ int tracing_is_on(void) { - if (global_trace.trace_buffer.buffer) - return ring_buffer_record_is_on(global_trace.trace_buffer.buffer); - return !global_trace.buffer_disabled; + return tracer_tracing_is_on(&global_trace); } EXPORT_SYMBOL_GPL(tracing_is_on); @@ -3958,7 +4002,7 @@ static int tracing_wait_pipe(struct file *filp) * * iter->pos will be 0 if we haven't read anything. */ - if (!tracing_is_enabled() && iter->pos) + if (!tracing_is_on() && iter->pos) break; } @@ -5631,15 +5675,10 @@ rb_simple_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) { struct trace_array *tr = filp->private_data; - struct ring_buffer *buffer = tr->trace_buffer.buffer; char buf[64]; int r; - if (buffer) - r = ring_buffer_record_is_on(buffer); - else - r = 0; - + r = tracer_tracing_is_on(tr); r = sprintf(buf, "%d\n", r); return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); @@ -5661,11 +5700,11 @@ rb_simple_write(struct file *filp, const char __user *ubuf, if (buffer) { mutex_lock(&trace_types_lock); if (val) { - ring_buffer_record_on(buffer); + tracer_tracing_on(tr); if (tr->current_trace->start) tr->current_trace->start(tr); } else { - ring_buffer_record_off(buffer); + tracer_tracing_off(tr); if (tr->current_trace->stop) tr->current_trace->stop(tr); } diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c index b19d065a28cb..2aefbee93a6d 100644 --- a/kernel/trace/trace_irqsoff.c +++ b/kernel/trace/trace_irqsoff.c @@ -373,7 +373,7 @@ start_critical_timing(unsigned long ip, unsigned long parent_ip) struct trace_array_cpu *data; unsigned long flags; - if (likely(!tracer_enabled)) + if (!tracer_enabled || !tracing_is_enabled()) return; cpu = raw_smp_processor_id(); @@ -416,7 +416,7 @@ stop_critical_timing(unsigned long ip, unsigned long parent_ip) else return; - if (!tracer_enabled) + if (!tracer_enabled || !tracing_is_enabled()) return; data = per_cpu_ptr(tr->trace_buffer.data, cpu); -- cgit v1.2.3 From cf6735a4b103b801753748531e3658cdc8cafa5e Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Thu, 20 Jun 2013 19:38:11 +0200 Subject: tracing/kprobes: Don't pass addr=ip to perf_trace_buf_submit() kprobe_perf_func() and kretprobe_perf_func() pass addr=ip to perf_trace_buf_submit() for no reason. This sets perf_sample_data->addr for PERF_SAMPLE_ADDR, we already have perf_sample_data->ip initialized if PERF_SAMPLE_IP. Link: http://lkml.kernel.org/r/20130620173811.GA13161@redhat.com Signed-off-by: Oleg Nesterov Signed-off-by: Steven Rostedt --- kernel/trace/trace_kprobe.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c index 405b5b0f903e..7ed6976493c8 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c @@ -1098,8 +1098,7 @@ kprobe_perf_func(struct trace_probe *tp, struct pt_regs *regs) entry->ip = (unsigned long)tp->rp.kp.addr; memset(&entry[1], 0, dsize); store_trace_args(sizeof(*entry), tp, regs, (u8 *)&entry[1], dsize); - perf_trace_buf_submit(entry, size, rctx, - entry->ip, 1, regs, head, NULL); + perf_trace_buf_submit(entry, size, rctx, 0, 1, regs, head, NULL); } /* Kretprobe profile handler */ @@ -1132,8 +1131,7 @@ kretprobe_perf_func(struct trace_probe *tp, struct kretprobe_instance *ri, entry->func = (unsigned long)tp->rp.kp.addr; entry->ret_ip = (unsigned long)ri->ret_addr; store_trace_args(sizeof(*entry), tp, regs, (u8 *)&entry[1], dsize); - perf_trace_buf_submit(entry, size, rctx, - entry->ret_ip, 1, regs, head, NULL); + perf_trace_buf_submit(entry, size, rctx, 0, 1, regs, head, NULL); } #endif /* CONFIG_PERF_EVENTS */ -- cgit v1.2.3 From f1ed7c741fcd0c3d7d318e7c19813d89934b9296 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Thu, 27 Jun 2013 22:18:06 -0400 Subject: ftrace: Do not run selftest if command line parameter is set If the kernel command line ftrace filter parameters are set (ftrace_filter or ftrace_notrace), force the function self test to pass, with a warning why it was forced. If the user adds a filter to the kernel command line, it is assumed that they know what they are doing, and the self test should just not run instead of failing (which disables function tracing) or clearing the filter, as that will probably annoy the user. If the user wants the selftest to run, the message will tell them why it did not. Signed-off-by: Steven Rostedt --- kernel/trace/ftrace.c | 5 +++++ kernel/trace/trace.h | 1 + kernel/trace/trace_selftest.c | 18 ++++++++++++++++-- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 26e19105cdcc..67708f46baae 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -3537,8 +3537,12 @@ EXPORT_SYMBOL_GPL(ftrace_set_global_notrace); static char ftrace_notrace_buf[FTRACE_FILTER_SIZE] __initdata; static char ftrace_filter_buf[FTRACE_FILTER_SIZE] __initdata; +/* Used by function selftest to not test if filter is set */ +bool ftrace_filter_param __initdata; + static int __init set_ftrace_notrace(char *str) { + ftrace_filter_param = true; strlcpy(ftrace_notrace_buf, str, FTRACE_FILTER_SIZE); return 1; } @@ -3546,6 +3550,7 @@ __setup("ftrace_notrace=", set_ftrace_notrace); static int __init set_ftrace_filter(char *str) { + ftrace_filter_param = true; strlcpy(ftrace_filter_buf, str, FTRACE_FILTER_SIZE); return 1; } diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 711ca7d3e7f1..a88939e666b7 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -776,6 +776,7 @@ print_graph_function_flags(struct trace_iterator *iter, u32 flags) extern struct list_head ftrace_pids; #ifdef CONFIG_FUNCTION_TRACER +extern bool ftrace_filter_param __initdata; static inline int ftrace_trace_task(struct task_struct *task) { if (list_empty(&ftrace_pids)) diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c index 2901e3b88590..a7329b7902f8 100644 --- a/kernel/trace/trace_selftest.c +++ b/kernel/trace/trace_selftest.c @@ -640,13 +640,20 @@ out: * Enable ftrace, sleep 1/10 second, and then read the trace * buffer to see if all is in order. */ -int +__init int trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr) { int save_ftrace_enabled = ftrace_enabled; unsigned long count; int ret; +#ifdef CONFIG_DYNAMIC_FTRACE + if (ftrace_filter_param) { + printk(KERN_CONT " ... kernel command line filter set: force PASS ... "); + return 0; + } +#endif + /* make sure msleep has been recorded */ msleep(1); @@ -727,13 +734,20 @@ static int trace_graph_entry_watchdog(struct ftrace_graph_ent *trace) * Pretty much the same than for the function tracer from which the selftest * has been borrowed. */ -int +__init int trace_selftest_startup_function_graph(struct tracer *trace, struct trace_array *tr) { int ret; unsigned long count; +#ifdef CONFIG_DYNAMIC_FTRACE + if (ftrace_filter_param) { + printk(KERN_CONT " ... kernel command line filter set: force PASS ... "); + return 0; + } +#endif + /* * Simulate the init() callback but we attach a watchdog callback * to detect and recover from possible hangs -- cgit v1.2.3 From aec0a40a6f78843c0ce73f7398230ee5184f896d Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 28 Jun 2013 07:40:57 -0700 Subject: netem: use rb tree to implement the time queue Following typical setup to implement a ~100 ms RTT and big amount of reorders has very poor performance because netem implements the time queue using a linked list. ----------------------------------------------------------- ETH=eth0 IFB=ifb0 modprobe ifb ip link set dev $IFB up tc qdisc add dev $ETH ingress 2>/dev/null tc filter add dev $ETH parent ffff: \ protocol ip u32 match u32 0 0 flowid 1:1 action mirred egress \ redirect dev $IFB ethtool -K $ETH gro off tso off gso off tc qdisc add dev $IFB root netem delay 50ms 10ms limit 100000 tc qd add dev $ETH root netem delay 50ms limit 100000 --------------------------------------------------------- Switch netem time queue to a rb tree, so this kind of setup can work at high speed. Signed-off-by: Eric Dumazet Cc: Stephen Hemminger Signed-off-by: David S. Miller --- net/sched/sch_netem.c | 109 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 85 insertions(+), 24 deletions(-) diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index 3d2acc7a9c80..ed0082cf8eff 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -68,7 +69,8 @@ */ struct netem_sched_data { - /* internal t(ime)fifo qdisc uses sch->q and sch->limit */ + /* internal t(ime)fifo qdisc uses t_root and sch->limit */ + struct rb_root t_root; /* optional qdisc for classful handling (NULL at netem init) */ struct Qdisc *qdisc; @@ -128,10 +130,35 @@ struct netem_sched_data { */ struct netem_skb_cb { psched_time_t time_to_send; + ktime_t tstamp_save; }; +/* Because space in skb->cb[] is tight, netem overloads skb->next/prev/tstamp + * to hold a rb_node structure. + * + * If struct sk_buff layout is changed, the following checks will complain. + */ +static struct rb_node *netem_rb_node(struct sk_buff *skb) +{ + BUILD_BUG_ON(offsetof(struct sk_buff, next) != 0); + BUILD_BUG_ON(offsetof(struct sk_buff, prev) != + offsetof(struct sk_buff, next) + sizeof(skb->next)); + BUILD_BUG_ON(offsetof(struct sk_buff, tstamp) != + offsetof(struct sk_buff, prev) + sizeof(skb->prev)); + BUILD_BUG_ON(sizeof(struct rb_node) > sizeof(skb->next) + + sizeof(skb->prev) + + sizeof(skb->tstamp)); + return (struct rb_node *)&skb->next; +} + +static struct sk_buff *netem_rb_to_skb(struct rb_node *rb) +{ + return (struct sk_buff *)rb; +} + static inline struct netem_skb_cb *netem_skb_cb(struct sk_buff *skb) { + /* we assume we can use skb next/prev/tstamp as storage for rb_node */ qdisc_cb_private_validate(skb, sizeof(struct netem_skb_cb)); return (struct netem_skb_cb *)qdisc_skb_cb(skb)->data; } @@ -333,20 +360,23 @@ static psched_time_t packet_len_2_sched_time(unsigned int len, struct netem_sche static void tfifo_enqueue(struct sk_buff *nskb, struct Qdisc *sch) { - struct sk_buff_head *list = &sch->q; + struct netem_sched_data *q = qdisc_priv(sch); psched_time_t tnext = netem_skb_cb(nskb)->time_to_send; - struct sk_buff *skb = skb_peek_tail(list); + struct rb_node **p = &q->t_root.rb_node, *parent = NULL; - /* Optimize for add at tail */ - if (likely(!skb || tnext >= netem_skb_cb(skb)->time_to_send)) - return __skb_queue_tail(list, nskb); + while (*p) { + struct sk_buff *skb; - skb_queue_reverse_walk(list, skb) { + parent = *p; + skb = netem_rb_to_skb(parent); if (tnext >= netem_skb_cb(skb)->time_to_send) - break; + p = &parent->rb_right; + else + p = &parent->rb_left; } - - __skb_queue_after(list, skb, nskb); + rb_link_node(netem_rb_node(nskb), parent, p); + rb_insert_color(netem_rb_node(nskb), &q->t_root); + sch->q.qlen++; } /* @@ -436,23 +466,28 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch) now = psched_get_time(); if (q->rate) { - struct sk_buff_head *list = &sch->q; + struct sk_buff *last; - if (!skb_queue_empty(list)) { + if (!skb_queue_empty(&sch->q)) + last = skb_peek_tail(&sch->q); + else + last = netem_rb_to_skb(rb_last(&q->t_root)); + if (last) { /* * Last packet in queue is reference point (now), * calculate this time bonus and subtract * from delay. */ - delay -= netem_skb_cb(skb_peek_tail(list))->time_to_send - now; + delay -= netem_skb_cb(last)->time_to_send - now; delay = max_t(psched_tdiff_t, 0, delay); - now = netem_skb_cb(skb_peek_tail(list))->time_to_send; + now = netem_skb_cb(last)->time_to_send; } delay += packet_len_2_sched_time(skb->len, q); } cb->time_to_send = now + delay; + cb->tstamp_save = skb->tstamp; ++q->counter; tfifo_enqueue(skb, sch); } else { @@ -476,6 +511,21 @@ static unsigned int netem_drop(struct Qdisc *sch) unsigned int len; len = qdisc_queue_drop(sch); + + if (!len) { + struct rb_node *p = rb_first(&q->t_root); + + if (p) { + struct sk_buff *skb = netem_rb_to_skb(p); + + rb_erase(p, &q->t_root); + sch->q.qlen--; + skb->next = NULL; + skb->prev = NULL; + len = qdisc_pkt_len(skb); + kfree_skb(skb); + } + } if (!len && q->qdisc && q->qdisc->ops->drop) len = q->qdisc->ops->drop(q->qdisc); if (len) @@ -488,19 +538,32 @@ static struct sk_buff *netem_dequeue(struct Qdisc *sch) { struct netem_sched_data *q = qdisc_priv(sch); struct sk_buff *skb; + struct rb_node *p; if (qdisc_is_throttled(sch)) return NULL; tfifo_dequeue: - skb = qdisc_peek_head(sch); + skb = __skb_dequeue(&sch->q); if (skb) { - const struct netem_skb_cb *cb = netem_skb_cb(skb); +deliver: + sch->qstats.backlog -= qdisc_pkt_len(skb); + qdisc_unthrottled(sch); + qdisc_bstats_update(sch, skb); + return skb; + } + p = rb_first(&q->t_root); + if (p) { + skb = netem_rb_to_skb(p); /* if more time remaining? */ - if (cb->time_to_send <= psched_get_time()) { - __skb_unlink(skb, &sch->q); - sch->qstats.backlog -= qdisc_pkt_len(skb); + if (netem_skb_cb(skb)->time_to_send <= psched_get_time()) { + rb_erase(p, &q->t_root); + + sch->q.qlen--; + skb->next = NULL; + skb->prev = NULL; + skb->tstamp = netem_skb_cb(skb)->tstamp_save; #ifdef CONFIG_NET_CLS_ACT /* @@ -522,10 +585,7 @@ tfifo_dequeue: } goto tfifo_dequeue; } -deliver: - qdisc_unthrottled(sch); - qdisc_bstats_update(sch, skb); - return skb; + goto deliver; } if (q->qdisc) { @@ -533,7 +593,8 @@ deliver: if (skb) goto deliver; } - qdisc_watchdog_schedule(&q->watchdog, cb->time_to_send); + qdisc_watchdog_schedule(&q->watchdog, + netem_skb_cb(skb)->time_to_send); } if (q->qdisc) { -- cgit v1.2.3 From 2d71619c59fac95a5415a326162fa046161b938c Mon Sep 17 00:00:00 2001 From: Alexander Z Lam Date: Mon, 1 Jul 2013 15:31:24 -0700 Subject: tracing: Make trace_marker use the correct per-instance buffer The trace_marker file was present for each new instance created, but it added the trace mark to the global trace buffer instead of to the instance's buffer. Link: http://lkml.kernel.org/r/1372717885-4543-2-git-send-email-azl@google.com Cc: David Sharp Cc: Vaibhav Nagarnaik Cc: Alexander Z Lam Cc: stable@vger.kernel.org # 3.10 Signed-off-by: Alexander Z Lam Signed-off-by: Steven Rostedt --- kernel/trace/trace.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 0dc50711d656..e04e7119633d 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -4391,6 +4391,7 @@ tracing_mark_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *fpos) { unsigned long addr = (unsigned long)ubuf; + struct trace_array *tr = filp->private_data; struct ring_buffer_event *event; struct ring_buffer *buffer; struct print_entry *entry; @@ -4450,7 +4451,7 @@ tracing_mark_write(struct file *filp, const char __user *ubuf, local_save_flags(irq_flags); size = sizeof(*entry) + cnt + 2; /* possible \n added */ - buffer = global_trace.trace_buffer.buffer; + buffer = tr->trace_buffer.buffer; event = trace_buffer_lock_reserve(buffer, TRACE_PRINT, size, irq_flags, preempt_count()); if (!event) { -- cgit v1.2.3 From 4ccb93ce7439b63c31bc7597bfffd13567fa483d Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Fri, 28 Jun 2013 12:13:52 -0400 Subject: x25: Fix broken locking in ioctl error paths. Two of the x25 ioctl cases have error paths that break out of the function without unlocking the socket, leading to this warning: ================================================ [ BUG: lock held when returning to user space! ] 3.10.0-rc7+ #36 Not tainted ------------------------------------------------ trinity-child2/31407 is leaving the kernel with locks still held! 1 lock held by trinity-child2/31407: #0: (sk_lock-AF_X25){+.+.+.}, at: [] x25_ioctl+0x8a/0x740 [x25] Signed-off-by: Dave Jones Signed-off-by: David S. Miller --- net/x25/af_x25.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c index 1d964e23853f..45a3ab5612c1 100644 --- a/net/x25/af_x25.c +++ b/net/x25/af_x25.c @@ -1583,11 +1583,11 @@ out_cud_release: case SIOCX25CALLACCPTAPPRV: { rc = -EINVAL; lock_sock(sk); - if (sk->sk_state != TCP_CLOSE) - break; - clear_bit(X25_ACCPT_APPRV_FLAG, &x25->flags); + if (sk->sk_state == TCP_CLOSE) { + clear_bit(X25_ACCPT_APPRV_FLAG, &x25->flags); + rc = 0; + } release_sock(sk); - rc = 0; break; } @@ -1595,14 +1595,15 @@ out_cud_release: rc = -EINVAL; lock_sock(sk); if (sk->sk_state != TCP_ESTABLISHED) - break; + goto out_sendcallaccpt_release; /* must call accptapprv above */ if (test_bit(X25_ACCPT_APPRV_FLAG, &x25->flags)) - break; + goto out_sendcallaccpt_release; x25_write_internal(sk, X25_CALL_ACCEPTED); x25->state = X25_STATE_3; - release_sock(sk); rc = 0; +out_sendcallaccpt_release: + release_sock(sk); break; } -- cgit v1.2.3 From b1a5a34bd0b8767ea689e68f8ea513e9710b671e Mon Sep 17 00:00:00 2001 From: Changli Gao Date: Sat, 29 Jun 2013 00:15:51 +0800 Subject: net: Swap ver and type in pppoe_hdr Ver and type in pppoe_hdr should be swapped as defined by RFC2516 section-4. Signed-off-by: David S. Miller --- include/uapi/linux/if_pppox.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/uapi/linux/if_pppox.h b/include/uapi/linux/if_pppox.h index 0b46fd57c8f6..e36a4aecd311 100644 --- a/include/uapi/linux/if_pppox.h +++ b/include/uapi/linux/if_pppox.h @@ -135,11 +135,11 @@ struct pppoe_tag { struct pppoe_hdr { #if defined(__LITTLE_ENDIAN_BITFIELD) - __u8 ver : 4; __u8 type : 4; + __u8 ver : 4; #elif defined(__BIG_ENDIAN_BITFIELD) - __u8 type : 4; __u8 ver : 4; + __u8 type : 4; #else #error "Please fix " #endif -- cgit v1.2.3 From a82274151af2b075163e3c42c828529dee311487 Mon Sep 17 00:00:00 2001 From: Alexander Z Lam Date: Mon, 1 Jul 2013 19:37:54 -0700 Subject: tracing: Protect ftrace_trace_arrays list in trace_events.c There are multiple places where the ftrace_trace_arrays list is accessed in trace_events.c without the trace_types_lock held. Link: http://lkml.kernel.org/r/1372732674-22726-1-git-send-email-azl@google.com Cc: Vaibhav Nagarnaik Cc: David Sharp Cc: Alexander Z Lam Cc: stable@vger.kernel.org # 3.10 Signed-off-by: Alexander Z Lam Signed-off-by: Steven Rostedt --- kernel/trace/trace.c | 2 +- kernel/trace/trace.h | 2 ++ kernel/trace/trace_events.c | 11 ++++++++++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index e04e7119633d..e36da7ff59bf 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -266,7 +266,7 @@ static struct tracer *trace_types __read_mostly; /* * trace_types_lock is used to protect the trace_types list. */ -static DEFINE_MUTEX(trace_types_lock); +DEFINE_MUTEX(trace_types_lock); /* * serialize the access of the ring buffer diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index a88939e666b7..2c3cba59552d 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -224,6 +224,8 @@ enum { extern struct list_head ftrace_trace_arrays; +extern struct mutex trace_types_lock; + /* * The global tracer (top) should be the first trace array added, * but we check the flag anyway. diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 5892470bc2ee..35c6f23c71b2 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -1008,6 +1008,7 @@ static int subsystem_open(struct inode *inode, struct file *filp) int ret; /* Make sure the system still exists */ + mutex_lock(&trace_types_lock); mutex_lock(&event_mutex); list_for_each_entry(tr, &ftrace_trace_arrays, list) { list_for_each_entry(dir, &tr->systems, list) { @@ -1023,6 +1024,7 @@ static int subsystem_open(struct inode *inode, struct file *filp) } exit_loop: mutex_unlock(&event_mutex); + mutex_unlock(&trace_types_lock); if (!system) return -ENODEV; @@ -1617,6 +1619,7 @@ static void __add_event_to_tracers(struct ftrace_event_call *call, int trace_add_event_call(struct ftrace_event_call *call) { int ret; + mutex_lock(&trace_types_lock); mutex_lock(&event_mutex); ret = __register_event(call, NULL); @@ -1624,11 +1627,13 @@ int trace_add_event_call(struct ftrace_event_call *call) __add_event_to_tracers(call, NULL); mutex_unlock(&event_mutex); + mutex_unlock(&trace_types_lock); return ret; } /* - * Must be called under locking both of event_mutex and trace_event_sem. + * Must be called under locking of trace_types_lock, event_mutex and + * trace_event_sem. */ static void __trace_remove_event_call(struct ftrace_event_call *call) { @@ -1640,11 +1645,13 @@ static void __trace_remove_event_call(struct ftrace_event_call *call) /* Remove an event_call */ void trace_remove_event_call(struct ftrace_event_call *call) { + mutex_lock(&trace_types_lock); mutex_lock(&event_mutex); down_write(&trace_event_sem); __trace_remove_event_call(call); up_write(&trace_event_sem); mutex_unlock(&event_mutex); + mutex_unlock(&trace_types_lock); } #define for_each_event(event, start, end) \ @@ -1788,6 +1795,7 @@ static int trace_module_notify(struct notifier_block *self, { struct module *mod = data; + mutex_lock(&trace_types_lock); mutex_lock(&event_mutex); switch (val) { case MODULE_STATE_COMING: @@ -1798,6 +1806,7 @@ static int trace_module_notify(struct notifier_block *self, break; } mutex_unlock(&event_mutex); + mutex_unlock(&trace_types_lock); return 0; } -- cgit v1.2.3 From 77ef8bbc87be7ad10b410247efc6d0f10676b401 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Mon, 1 Jul 2013 20:32:58 +0200 Subject: drm: make drm_mm_init() return void There is no reason to return "int" as this function never fails. Furthermore, several drivers (ast, sis) already depend on this. Signed-off-by: David Herrmann Reviewed-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_gem.c | 8 ++------ drivers/gpu/drm/drm_mm.c | 4 +--- drivers/gpu/drm/ttm/ttm_bo.c | 6 +----- drivers/gpu/drm/ttm/ttm_bo_manager.c | 8 +------- include/drm/drm_mm.h | 6 +++--- 5 files changed, 8 insertions(+), 24 deletions(-) diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index bcedaf7b73b9..603f256152ef 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -108,12 +108,8 @@ drm_gem_init(struct drm_device *dev) return -ENOMEM; } - if (drm_mm_init(&mm->offset_manager, DRM_FILE_PAGE_OFFSET_START, - DRM_FILE_PAGE_OFFSET_SIZE)) { - drm_ht_remove(&mm->offset_hash); - kfree(mm); - return -ENOMEM; - } + drm_mm_init(&mm->offset_manager, DRM_FILE_PAGE_OFFSET_START, + DRM_FILE_PAGE_OFFSET_SIZE); return 0; } diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index 07cf99cc8862..7917729ee61d 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c @@ -669,7 +669,7 @@ int drm_mm_clean(struct drm_mm * mm) } EXPORT_SYMBOL(drm_mm_clean); -int drm_mm_init(struct drm_mm * mm, unsigned long start, unsigned long size) +void drm_mm_init(struct drm_mm * mm, unsigned long start, unsigned long size) { INIT_LIST_HEAD(&mm->hole_stack); INIT_LIST_HEAD(&mm->unused_nodes); @@ -690,8 +690,6 @@ int drm_mm_init(struct drm_mm * mm, unsigned long start, unsigned long size) list_add_tail(&mm->head_node.hole_stack, &mm->hole_stack); mm->color_adjust = NULL; - - return 0; } EXPORT_SYMBOL(drm_mm_init); diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 6e6975c8596f..cb9dd674670c 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -1455,9 +1455,7 @@ int ttm_bo_device_init(struct ttm_bo_device *bdev, goto out_no_sys; bdev->addr_space_rb = RB_ROOT; - ret = drm_mm_init(&bdev->addr_space_mm, file_page_offset, 0x10000000); - if (unlikely(ret != 0)) - goto out_no_addr_mm; + drm_mm_init(&bdev->addr_space_mm, file_page_offset, 0x10000000); INIT_DELAYED_WORK(&bdev->wq, ttm_bo_delayed_workqueue); INIT_LIST_HEAD(&bdev->ddestroy); @@ -1471,8 +1469,6 @@ int ttm_bo_device_init(struct ttm_bo_device *bdev, mutex_unlock(&glob->device_list_mutex); return 0; -out_no_addr_mm: - ttm_bo_clean_mm(bdev, 0); out_no_sys: return ret; } diff --git a/drivers/gpu/drm/ttm/ttm_bo_manager.c b/drivers/gpu/drm/ttm/ttm_bo_manager.c index 9212494e9072..e4367f91472a 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_manager.c +++ b/drivers/gpu/drm/ttm/ttm_bo_manager.c @@ -103,18 +103,12 @@ static int ttm_bo_man_init(struct ttm_mem_type_manager *man, unsigned long p_size) { struct ttm_range_manager *rman; - int ret; rman = kzalloc(sizeof(*rman), GFP_KERNEL); if (!rman) return -ENOMEM; - ret = drm_mm_init(&rman->mm, 0, p_size); - if (ret) { - kfree(rman); - return ret; - } - + drm_mm_init(&rman->mm, 0, p_size); spin_lock_init(&rman->lock); man->priv = rman; return 0; diff --git a/include/drm/drm_mm.h b/include/drm/drm_mm.h index 88591ef8fa24..de9242542f05 100644 --- a/include/drm/drm_mm.h +++ b/include/drm/drm_mm.h @@ -275,9 +275,9 @@ static inline struct drm_mm_node *drm_mm_search_free_in_range_color( return drm_mm_search_free_in_range_generic(mm, size, alignment, color, start, end, best_match); } -extern int drm_mm_init(struct drm_mm *mm, - unsigned long start, - unsigned long size); +extern void drm_mm_init(struct drm_mm *mm, + unsigned long start, + unsigned long size); extern void drm_mm_takedown(struct drm_mm *mm); extern int drm_mm_clean(struct drm_mm *mm); extern int drm_mm_pre_get(struct drm_mm *mm); -- cgit v1.2.3 From 4f6de4d51f4a3ab06a85e91e708cc89a513ef30c Mon Sep 17 00:00:00 2001 From: Mathias Krause Date: Tue, 2 Jul 2013 15:35:11 +0930 Subject: module: don't modify argument of module_kallsyms_lookup_name() If we pass a pointer to a const string in the form "module:symbol" module_kallsyms_lookup_name() will try to split the string at the colon, i.e., will try to modify r/o data. That will, in fact, fail on a kernel with enabled CONFIG_DEBUG_RODATA. Avoid modifying the passed string in module_kallsyms_lookup_name(), modify find_module_all() instead to pass it the module name length. Signed-off-by: Mathias Krause Signed-off-by: Rusty Russell --- kernel/module.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/kernel/module.c b/kernel/module.c index b049939177f6..a1951aba7a03 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -455,7 +455,7 @@ const struct kernel_symbol *find_symbol(const char *name, EXPORT_SYMBOL_GPL(find_symbol); /* Search for module by name: must hold module_mutex. */ -static struct module *find_module_all(const char *name, +static struct module *find_module_all(const char *name, size_t len, bool even_unformed) { struct module *mod; @@ -463,7 +463,7 @@ static struct module *find_module_all(const char *name, list_for_each_entry(mod, &modules, list) { if (!even_unformed && mod->state == MODULE_STATE_UNFORMED) continue; - if (strcmp(mod->name, name) == 0) + if (strlen(mod->name) == len && !memcmp(mod->name, name, len)) return mod; } return NULL; @@ -471,7 +471,7 @@ static struct module *find_module_all(const char *name, struct module *find_module(const char *name) { - return find_module_all(name, false); + return find_module_all(name, strlen(name), false); } EXPORT_SYMBOL_GPL(find_module); @@ -3027,7 +3027,7 @@ static bool finished_loading(const char *name) bool ret; mutex_lock(&module_mutex); - mod = find_module_all(name, true); + mod = find_module_all(name, strlen(name), true); ret = !mod || mod->state == MODULE_STATE_LIVE || mod->state == MODULE_STATE_GOING; mutex_unlock(&module_mutex); @@ -3165,7 +3165,8 @@ static int add_unformed_module(struct module *mod) again: mutex_lock(&module_mutex); - if ((old = find_module_all(mod->name, true)) != NULL) { + old = find_module_all(mod->name, strlen(mod->name), true); + if (old != NULL) { if (old->state == MODULE_STATE_COMING || old->state == MODULE_STATE_UNFORMED) { /* Wait in case it fails to load. */ @@ -3576,10 +3577,8 @@ unsigned long module_kallsyms_lookup_name(const char *name) /* Don't lock: we're in enough trouble already. */ preempt_disable(); if ((colon = strchr(name, ':')) != NULL) { - *colon = '\0'; - if ((mod = find_module(name)) != NULL) + if ((mod = find_module_all(name, colon - name, false)) != NULL) ret = mod_find_symname(mod, colon+1); - *colon = ':'; } else { list_for_each_entry_rcu(mod, &modules, list) { if (mod->state == MODULE_STATE_UNFORMED) -- cgit v1.2.3 From b634d130e46a093ddf716ae9cf1bfa258ede36cf Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Tue, 2 Jul 2013 15:35:11 +0930 Subject: There is no /sys/parameters There is no such path as /sys/parameters, module parameters live in /sys/module/*/parameters. Signed-off-by: Jean Delvare Cc: Rusty Russell Signed-off-by: Rusty Russell --- include/linux/moduleparam.h | 2 +- kernel/params.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/moduleparam.h b/include/linux/moduleparam.h index 137b4198fc03..27d9da3f86ff 100644 --- a/include/linux/moduleparam.h +++ b/include/linux/moduleparam.h @@ -439,7 +439,7 @@ extern struct kernel_param_ops param_ops_string; extern int param_set_copystring(const char *val, const struct kernel_param *); extern int param_get_string(char *buffer, const struct kernel_param *kp); -/* for exporting parameters in /sys/parameters */ +/* for exporting parameters in /sys/module/.../parameters */ struct module; diff --git a/kernel/params.c b/kernel/params.c index 53b958fcd639..440e65d1a544 100644 --- a/kernel/params.c +++ b/kernel/params.c @@ -787,7 +787,7 @@ static void __init kernel_add_sysfs_param(const char *name, } /* - * param_sysfs_builtin - add contents in /sys/parameters for built-in modules + * param_sysfs_builtin - add sysfs parameters for built-in modules * * Add module_parameters to sysfs for "modules" built into the kernel. * -- cgit v1.2.3 From 86f120031a21fe4e7333a84f55d9e243c781e6e9 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Tue, 2 Jul 2013 15:35:11 +0930 Subject: ABI: Clarify when /sys/module/MODULENAME is created /sys/module/MODULENAME is not created unconditionally. This can be confusing so document the current conditions. Signed-off-by: Jean Delvare Cc: Rusty Russell Cc: Rob Landley Signed-off-by: Rusty Russell --- Documentation/ABI/stable/sysfs-module | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Documentation/ABI/stable/sysfs-module b/Documentation/ABI/stable/sysfs-module index a0dd21c6db59..6272ae5fb366 100644 --- a/Documentation/ABI/stable/sysfs-module +++ b/Documentation/ABI/stable/sysfs-module @@ -4,9 +4,13 @@ Description: /sys/module/MODULENAME The name of the module that is in the kernel. This - module name will show up either if the module is built - directly into the kernel, or if it is loaded as a - dynamic module. + module name will always show up if the module is loaded as a + dynamic module. If it is built directly into the kernel, it + will only show up if it has a version or at least one + parameter. + + Note: The conditions of creation in the built-in case are not + by design and may be removed in the future. /sys/module/MODULENAME/parameters This directory contains individual files that are each -- cgit v1.2.3 From 54041d8a73337411b485ff76957fb106cb5d40d0 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 2 Jul 2013 15:35:12 +0930 Subject: modules: don't fail to load on unknown parameters. Although parameters are supposed to be part of the kernel API, experimental parameters are often removed. In addition, downgrading a kernel might cause previously-working modules to fail to load. On balance, it's probably better to warn, and load the module anyway. This may let through a typo, but at least the logs will show it. Reported-by: Andy Lutomirski Signed-off-by: Rusty Russell --- kernel/module.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/kernel/module.c b/kernel/module.c index a1951aba7a03..5184877ce98a 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -3212,6 +3212,17 @@ out: return err; } +static int unknown_module_param_cb(char *param, char *val, const char *modname) +{ + /* Check for magic 'dyndbg' arg */ + int ret = ddebug_dyndbg_module_param_cb(param, val, modname); + if (ret != 0) { + printk(KERN_WARNING "%s: unknown parameter '%s' ignored\n", + modname, param); + } + return 0; +} + /* Allocate and load the module: note that size of section 0 is always zero, and we rely on this for optional sections. */ static int load_module(struct load_info *info, const char __user *uargs, @@ -3298,7 +3309,7 @@ static int load_module(struct load_info *info, const char __user *uargs, /* Module is ready to execute: parsing args may do that. */ err = parse_args(mod->name, mod->args, mod->kp, mod->num_kp, - -32768, 32767, &ddebug_dyndbg_module_param_cb); + -32768, 32767, unknown_module_param_cb); if (err < 0) goto bug_cleanup; -- cgit v1.2.3 From 95cc2c02cfe08b8bb910d2563725f96a5c48c327 Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Tue, 2 Jul 2013 15:35:12 +0930 Subject: Fix comment typo "CONFIG_PAE" Signed-off-by: Paul Bolle Signed-off-by: Rusty Russell --- drivers/lguest/page_tables.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/lguest/page_tables.c b/drivers/lguest/page_tables.c index 5b9ac32801c7..a35d8d100165 100644 --- a/drivers/lguest/page_tables.c +++ b/drivers/lguest/page_tables.c @@ -70,7 +70,7 @@ /*H:320 * The page table code is curly enough to need helper functions to keep it * clear and clean. The kernel itself provides many of them; one advantage - * of insisting that the Guest and Host use the same CONFIG_PAE setting. + * of insisting that the Guest and Host use the same CONFIG_X86_PAE setting. * * There are two functions which return pointers to the shadow (aka "real") * page tables. -- cgit v1.2.3 From f11335db5e3901f6afc2eafa03a3b970562538b2 Mon Sep 17 00:00:00 2001 From: Andrew Vagin Date: Tue, 2 Jul 2013 15:35:13 +0930 Subject: virtio-pci: fix leaks of msix_affinity_masks vp_dev->msix_vectors should be initialized before allocating msix_affinity_masks, otherwise vp_free_vectors will not free these objects. unreferenced object 0xffff88010f969d88 (size 512): comm "systemd-udevd", pid 158, jiffies 4294673645 (age 80.545s) hex dump (first 32 bytes): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ backtrace: [] kmemleak_alloc+0x5e/0xc0 [] kmem_cache_alloc_node_trace+0x141/0x2c0 [] alloc_cpumask_var_node+0x23/0x80 [] alloc_cpumask_var+0xe/0x10 [] vp_try_to_find_vqs+0x25d/0x810 [] vp_find_vqs+0x81/0xb0 [] init_vqs+0x85/0x120 [virtio_balloon] [] virtballoon_probe+0xf9/0x1a0 [virtio_balloon] [] virtio_dev_probe+0xde/0x140 [] driver_probe_device+0x98/0x3a0 [] __driver_attach+0xab/0xb0 [] bus_for_each_dev+0x94/0xb0 [] driver_attach+0x1e/0x20 [] bus_add_driver+0x200/0x280 [] driver_register+0x74/0x160 [] register_virtio_driver+0x20/0x40 v2: change msix_vectors uncoditionaly in vp_free_vectors Cc: Rusty Russell Cc: "Michael S. Tsirkin" Cc: Jason Wang Signed-off-by: Andrew Vagin Acked-by: Michael S. Tsirkin Acked-by: Jason Wang Signed-off-by: Rusty Russell --- drivers/virtio/virtio_pci.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c index a7ce73029f59..1aba255b5879 100644 --- a/drivers/virtio/virtio_pci.c +++ b/drivers/virtio/virtio_pci.c @@ -289,9 +289,9 @@ static void vp_free_vectors(struct virtio_device *vdev) pci_disable_msix(vp_dev->pci_dev); vp_dev->msix_enabled = 0; - vp_dev->msix_vectors = 0; } + vp_dev->msix_vectors = 0; vp_dev->msix_used_vectors = 0; kfree(vp_dev->msix_names); vp_dev->msix_names = NULL; @@ -309,6 +309,8 @@ static int vp_request_msix_vectors(struct virtio_device *vdev, int nvectors, unsigned i, v; int err = -ENOMEM; + vp_dev->msix_vectors = nvectors; + vp_dev->msix_entries = kmalloc(nvectors * sizeof *vp_dev->msix_entries, GFP_KERNEL); if (!vp_dev->msix_entries) @@ -336,7 +338,6 @@ static int vp_request_msix_vectors(struct virtio_device *vdev, int nvectors, err = -ENOSPC; if (err) goto error; - vp_dev->msix_vectors = nvectors; vp_dev->msix_enabled = 1; /* Set the vector used for configuration */ -- cgit v1.2.3 From 8c6bab4f3874d31804a00782c48a8f244a0d3cc0 Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Tue, 2 Jul 2013 15:35:13 +0930 Subject: virtio_balloon: leak_balloon(): only tell host if we got pages deflated balloon_page_dequeue() can return NULL. If it does for the first page being freed then leak_balloon() will create a scatter list with len=0. Which in turn seems to generate an invalid virtio request. I didn't get this in practice, I found it by code review. On the other hand, such an invalid virtio request will cause errors in QEMU and fill_balloon() also performs the same check implemented by this commit. This bug was introduced in e2250429. Signed-off-by: Luiz Capitulino Acked-by: Rafael Aquini Signed-off-by: Andrew Morton Signed-off-by: Rusty Russell Cc: stable@kernel.org # 3.9 --- drivers/virtio/virtio_balloon.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index bd3ae324a1a2..71af7b5abe01 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c @@ -191,7 +191,8 @@ static void leak_balloon(struct virtio_balloon *vb, size_t num) * virtio_has_feature(vdev, VIRTIO_BALLOON_F_MUST_TELL_HOST); * is true, we *have* to do it in this order */ - tell_host(vb, vb->deflate_vq); + if (vb->num_pfns != 0) + tell_host(vb, vb->deflate_vq); mutex_unlock(&vb->balloon_lock); release_pages_by_pfn(vb->pfns, vb->num_pfns); } -- cgit v1.2.3 From 8fd9a6365eb8a79a2d8f0e0baa01f4db6345e8dd Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 2 Jul 2013 15:35:13 +0930 Subject: tools/lguest: fix missing rmb(). The virtio spec was missing a barrier in example code, so I went back to look at the lguest code. Indeed, we need one. Signed-off-by: Rusty Russell --- tools/lguest/lguest.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c index 07a03452c227..49ed25b5bbb9 100644 --- a/tools/lguest/lguest.c +++ b/tools/lguest/lguest.c @@ -177,6 +177,7 @@ static struct termios orig_term; * in precise order. */ #define wmb() __asm__ __volatile__("" : : : "memory") +#define rmb() __asm__ __volatile__("" : : : "memory") #define mb() __asm__ __volatile__("" : : : "memory") /* Wrapper for the last available index. Makes it easier to change. */ @@ -676,6 +677,12 @@ static unsigned wait_for_vq_desc(struct virtqueue *vq, errx(1, "Guest moved used index from %u to %u", last_avail, vq->vring.avail->idx); + /* + * Make sure we read the descriptor number *after* we read the ring + * update; don't let the cpu or compiler change the order. + */ + rmb(); + /* * Grab the next descriptor number they're advertising, and increment * the index we've seen. @@ -694,6 +701,12 @@ static unsigned wait_for_vq_desc(struct virtqueue *vq, desc = vq->vring.desc; i = head; + /* + * We have to read the descriptor after we read the descriptor number, + * but there's a data dependency there so the CPU shouldn't reorder + * that: no rmb() required. + */ + /* * If this is an indirect entry, then this buffer contains a descriptor * table which we handle as if it's any normal descriptor chain. -- cgit v1.2.3 From 0d69a65e97fc8090ee83c8639137b4b5c8ece237 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 2 Jul 2013 15:35:14 +0930 Subject: tools/lguest: real barriers. Lguest guests are UP, but the host is probably SMP, so real barriers are required in case the device thread and the guest are on different CPUs. Signed-off-by: Rusty Russell --- tools/lguest/lguest.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c index 49ed25b5bbb9..a64f5cb0c15b 100644 --- a/tools/lguest/lguest.c +++ b/tools/lguest/lguest.c @@ -177,8 +177,8 @@ static struct termios orig_term; * in precise order. */ #define wmb() __asm__ __volatile__("" : : : "memory") -#define rmb() __asm__ __volatile__("" : : : "memory") -#define mb() __asm__ __volatile__("" : : : "memory") +#define rmb() __asm__ __volatile__("lock; addl $0,0(%%esp)" : : : "memory") +#define mb() __asm__ __volatile__("lock; addl $0,0(%%esp)" : : : "memory") /* Wrapper for the last available index. Makes it easier to change. */ #define lg_last_avail(vq) ((vq)->last_avail_idx) -- cgit v1.2.3 From 1067964305df131ede2c08c2f3c9b3892640f1c6 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Fri, 28 Jun 2013 19:49:39 +0200 Subject: lib: vsprintf: add IPv4/v6 generic %p[Ii]S[pfs] format specifier In order to avoid making code that deals with printing both, IPv4 and IPv6 addresses, unnecessary complicated as for example ... if (sa.sa_family == AF_INET6) printk("... %pI6 ...", ..sin6_addr); else printk("... %pI4 ...", ..sin_addr.s_addr); ... it would be better to introduce a format specifier that can deal with those kind of situations internally; just as we have a "struct sockaddr" for generic mapping into "struct sockaddr_in" or "struct sockaddr_in6" as e.g. done in "union sctp_addr". Then, we could reduce the above statement into something like: printk("... %pIS ..", &sockaddr); In case our pointer is NULL, pointer() then deals with that already at an earlier point in time internally. While we're at it, support for both %piS/%pIS, where 'S' stands for sockaddr, comes (almost) for free. Additionally to that, postfix specifiers 'p', 'f' and 's' are supported as suggested and initially implemented in 2009 by Joe Perches [1]. Handling of those additional specifiers orientate on the initial RFC that was proposed. Also we support IPv6 compressed format specified by 'c' and various other IPv4 extensions as stated in the documentation part. Likely, there are many other areas than just SCTP in the kernel to make use of this extension as well. [1] http://patchwork.ozlabs.org/patch/31480/ Signed-off-by: Daniel Borkmann CC: Joe Perches CC: linux-kernel@vger.kernel.org Signed-off-by: David S. Miller --- Documentation/printk-formats.txt | 32 ++++++++++ lib/vsprintf.c | 124 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 154 insertions(+), 2 deletions(-) diff --git a/Documentation/printk-formats.txt b/Documentation/printk-formats.txt index 3af5ae6c9c11..3e8cb73ac43c 100644 --- a/Documentation/printk-formats.txt +++ b/Documentation/printk-formats.txt @@ -121,6 +121,38 @@ IPv6 addresses: print a compressed IPv6 address as described by http://tools.ietf.org/html/rfc5952 +IPv4/IPv6 addresses (generic, with port, flowinfo, scope): + + %pIS 1.2.3.4 or 0001:0002:0003:0004:0005:0006:0007:0008 + %piS 001.002.003.004 or 00010002000300040005000600070008 + %pISc 1.2.3.4 or 1:2:3:4:5:6:7:8 + %pISpc 1.2.3.4:12345 or [1:2:3:4:5:6:7:8]:12345 + %p[Ii]S[pfschnbl] + + For printing an IP address without the need to distinguish whether it's + of type AF_INET or AF_INET6, a pointer to a valid 'struct sockaddr', + specified through 'IS' or 'iS', can be passed to this format specifier. + + The additional 'p', 'f', and 's' specifiers are used to specify port + (IPv4, IPv6), flowinfo (IPv6) and scope (IPv6). Ports have a ':' prefix, + flowinfo a '/' and scope a '%', each followed by the actual value. + + In case of an IPv6 address the compressed IPv6 address as described by + http://tools.ietf.org/html/rfc5952 is being used if the additional + specifier 'c' is given. The IPv6 address is surrounded by '[', ']' in + case of additional specifiers 'p', 'f' or 's' as suggested by + https://tools.ietf.org/html/draft-ietf-6man-text-addr-representation-07 + + In case of IPv4 addresses, the additional 'h', 'n', 'b', and 'l' + specifiers can be used as well and are ignored in case of an IPv6 + address. + + Further examples: + + %pISfc 1.2.3.4 or [1:2:3:4:5:6:7:8]/123456789 + %pISsc 1.2.3.4 or [1:2:3:4:5:6:7:8]%1234567890 + %pISpfc 1.2.3.4:12345 or [1:2:3:4:5:6:7:8]:12345/123456789 + UUID/GUID addresses: %pUb 00010203-0405-0607-0809-0a0b0c0d0e0f diff --git a/lib/vsprintf.c b/lib/vsprintf.c index e149c6416384..31febc0b70df 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -922,6 +922,103 @@ char *ip4_addr_string(char *buf, char *end, const u8 *addr, return string(buf, end, ip4_addr, spec); } +static noinline_for_stack +char *ip6_addr_string_sa(char *buf, char *end, const struct sockaddr_in6 *sa, + struct printf_spec spec, const char *fmt) +{ + bool have_p = false, have_s = false, have_f = false, have_c = false; + char ip6_addr[sizeof("[xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255]") + + sizeof(":12345") + sizeof("/123456789") + + sizeof("%1234567890")]; + char *p = ip6_addr, *pend = ip6_addr + sizeof(ip6_addr); + const u8 *addr = (const u8 *) &sa->sin6_addr; + char fmt6[2] = { fmt[0], '6' }; + u8 off = 0; + + fmt++; + while (isalpha(*++fmt)) { + switch (*fmt) { + case 'p': + have_p = true; + break; + case 'f': + have_f = true; + break; + case 's': + have_s = true; + break; + case 'c': + have_c = true; + break; + } + } + + if (have_p || have_s || have_f) { + *p = '['; + off = 1; + } + + if (fmt6[0] == 'I' && have_c) + p = ip6_compressed_string(ip6_addr + off, addr); + else + p = ip6_string(ip6_addr + off, addr, fmt6); + + if (have_p || have_s || have_f) + *p++ = ']'; + + if (have_p) { + *p++ = ':'; + p = number(p, pend, ntohs(sa->sin6_port), spec); + } + if (have_f) { + *p++ = '/'; + p = number(p, pend, ntohl(sa->sin6_flowinfo & + IPV6_FLOWINFO_MASK), spec); + } + if (have_s) { + *p++ = '%'; + p = number(p, pend, sa->sin6_scope_id, spec); + } + *p = '\0'; + + return string(buf, end, ip6_addr, spec); +} + +static noinline_for_stack +char *ip4_addr_string_sa(char *buf, char *end, const struct sockaddr_in *sa, + struct printf_spec spec, const char *fmt) +{ + bool have_p = false; + char *p, ip4_addr[sizeof("255.255.255.255") + sizeof(":12345")]; + char *pend = ip4_addr + sizeof(ip4_addr); + const u8 *addr = (const u8 *) &sa->sin_addr.s_addr; + char fmt4[3] = { fmt[0], '4', 0 }; + + fmt++; + while (isalpha(*++fmt)) { + switch (*fmt) { + case 'p': + have_p = true; + break; + case 'h': + case 'l': + case 'n': + case 'b': + fmt4[2] = *fmt; + break; + } + } + + p = ip4_string(ip4_addr, addr, fmt4); + if (have_p) { + *p++ = ':'; + p = number(p, pend, ntohs(sa->sin_port), spec); + } + *p = '\0'; + + return string(buf, end, ip4_addr, spec); +} + static noinline_for_stack char *uuid_string(char *buf, char *end, const u8 *addr, struct printf_spec spec, const char *fmt) @@ -1007,11 +1104,17 @@ int kptr_restrict __read_mostly; * - 'I' [46] for IPv4/IPv6 addresses printed in the usual way * IPv4 uses dot-separated decimal without leading 0's (1.2.3.4) * IPv6 uses colon separated network-order 16 bit hex with leading 0's + * [S][pfs] + * Generic IPv4/IPv6 address (struct sockaddr *) that falls back to + * [4] or [6] and is able to print port [p], flowinfo [f], scope [s] * - 'i' [46] for 'raw' IPv4/IPv6 addresses * IPv6 omits the colons (01020304...0f) * IPv4 uses dot-separated decimal with leading 0's (010.123.045.006) - * - '[Ii]4[hnbl]' IPv4 addresses in host, network, big or little endian order - * - 'I6c' for IPv6 addresses printed as specified by + * [S][pfs] + * Generic IPv4/IPv6 address (struct sockaddr *) that falls back to + * [4] or [6] and is able to print port [p], flowinfo [f], scope [s] + * - '[Ii][4S][hnbl]' IPv4 addresses in host, network, big or little endian order + * - 'I[6S]c' for IPv6 addresses printed as specified by * http://tools.ietf.org/html/rfc5952 * - 'U' For a 16 byte UUID/GUID, it prints the UUID/GUID in the form * "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" @@ -1093,6 +1196,21 @@ char *pointer(const char *fmt, char *buf, char *end, void *ptr, return ip6_addr_string(buf, end, ptr, spec, fmt); case '4': return ip4_addr_string(buf, end, ptr, spec, fmt); + case 'S': { + const union { + struct sockaddr raw; + struct sockaddr_in v4; + struct sockaddr_in6 v6; + } *sa = ptr; + + switch (sa->raw.sa_family) { + case AF_INET: + return ip4_addr_string_sa(buf, end, &sa->v4, spec, fmt); + case AF_INET6: + return ip6_addr_string_sa(buf, end, &sa->v6, spec, fmt); + default: + return string(buf, end, "(invalid address)", spec); + }} } break; case 'U': @@ -1370,6 +1488,8 @@ qualifier: * %pI6 print an IPv6 address with colons * %pi6 print an IPv6 address without colons * %pI6c print an IPv6 address as specified by RFC 5952 + * %pIS depending on sa_family of 'struct sockaddr *' print IPv4/IPv6 address + * %piS depending on sa_family of 'struct sockaddr *' print IPv4/IPv6 address * %pU[bBlL] print a UUID/GUID in big or little endian using lower or upper * case. * %*ph[CDN] a variable-length hex string with a separator (supports up to 64 -- cgit v1.2.3 From bb33381d0c97cdee25f2cdab540b6e2bd16fa03b Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Fri, 28 Jun 2013 19:49:40 +0200 Subject: net: sctp: rework debugging framework to use pr_debug and friends We should get rid of all own SCTP debug printk macros and use the ones that the kernel offers anyway instead. This makes the code more readable and conform to the kernel code, and offers all the features of dynamic debbuging that pr_debug() et al has, such as only turning on/off portions of debug messages at runtime through debugfs. The runtime cost of having CONFIG_DYNAMIC_DEBUG enabled, but none of the debug statements printing, is negligible [1]. If kernel debugging is completly turned off, then these statements will also compile into "empty" functions. While we're at it, we also need to change the Kconfig option as it /now/ only refers to the ifdef'ed code portions in outqueue.c that enable further debugging/tracing of SCTP transaction fields. Also, since SCTP_ASSERT code was enabled with this Kconfig option and has now been removed, we transform those code parts into WARNs resp. where appropriate BUG_ONs so that those bugs can be more easily detected as probably not many people have SCTP debugging permanently turned on. To turn on all SCTP debugging, the following steps are needed: # mount -t debugfs none /sys/kernel/debug # echo -n 'module sctp +p' > /sys/kernel/debug/dynamic_debug/control This can be done more fine-grained on a per file, per line basis and others as described in [2]. [1] https://www.kernel.org/doc/ols/2009/ols2009-pages-39-46.pdf [2] Documentation/dynamic-debug-howto.txt Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/net/sctp/sctp.h | 75 --------------------- net/sctp/Kconfig | 12 ++-- net/sctp/associola.c | 67 ++++++++++--------- net/sctp/chunk.c | 5 +- net/sctp/debug.c | 4 -- net/sctp/endpointola.c | 5 +- net/sctp/input.c | 6 +- net/sctp/inqueue.c | 9 ++- net/sctp/ipv6.c | 21 +++--- net/sctp/output.c | 40 +++++------ net/sctp/outqueue.c | 160 ++++++++++++++++++++------------------------ net/sctp/protocol.c | 48 +++++++------- net/sctp/sm_make_chunk.c | 25 +++---- net/sctp/sm_sideeffect.c | 96 +++++++++++---------------- net/sctp/sm_statefuns.c | 77 +++++++++++++--------- net/sctp/socket.c | 168 ++++++++++++++++++++++------------------------- net/sctp/transport.c | 49 +++++++------- 17 files changed, 383 insertions(+), 484 deletions(-) diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index e6b95bc4d8e6..d8e37ecea691 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -83,16 +83,6 @@ #include #include - -/* Set SCTP_DEBUG flag via config if not already set. */ -#ifndef SCTP_DEBUG -#ifdef CONFIG_SCTP_DBG_MSG -#define SCTP_DEBUG 1 -#else -#define SCTP_DEBUG 0 -#endif /* CONFIG_SCTP_DBG */ -#endif /* SCTP_DEBUG */ - #ifdef CONFIG_IP_SCTP_MODULE #define SCTP_PROTOSW_FLAG 0 #else /* static! */ @@ -270,61 +260,6 @@ static inline void sctp_max_rto(struct sctp_association *asoc, } } -/* Print debugging messages. */ -#if SCTP_DEBUG -extern int sctp_debug_flag; -#define SCTP_DEBUG_PRINTK(fmt, args...) \ -do { \ - if (sctp_debug_flag) \ - printk(KERN_DEBUG pr_fmt(fmt), ##args); \ -} while (0) -#define SCTP_DEBUG_PRINTK_CONT(fmt, args...) \ -do { \ - if (sctp_debug_flag) \ - pr_cont(fmt, ##args); \ -} while (0) -#define SCTP_DEBUG_PRINTK_IPADDR(fmt_lead, fmt_trail, \ - args_lead, addr, args_trail...) \ -do { \ - const union sctp_addr *_addr = (addr); \ - if (sctp_debug_flag) { \ - if (_addr->sa.sa_family == AF_INET6) { \ - printk(KERN_DEBUG \ - pr_fmt(fmt_lead "%pI6" fmt_trail), \ - args_lead, \ - &_addr->v6.sin6_addr, \ - args_trail); \ - } else { \ - printk(KERN_DEBUG \ - pr_fmt(fmt_lead "%pI4" fmt_trail), \ - args_lead, \ - &_addr->v4.sin_addr.s_addr, \ - args_trail); \ - } \ - } \ -} while (0) -#define SCTP_ENABLE_DEBUG { sctp_debug_flag = 1; } -#define SCTP_DISABLE_DEBUG { sctp_debug_flag = 0; } - -#define SCTP_ASSERT(expr, str, func) \ - if (!(expr)) { \ - SCTP_DEBUG_PRINTK("Assertion Failed: %s(%s) at %s:%s:%d\n", \ - str, (#expr), __FILE__, __func__, __LINE__); \ - func; \ - } - -#else /* SCTP_DEBUG */ - -#define SCTP_DEBUG_PRINTK(whatever...) -#define SCTP_DEBUG_PRINTK_CONT(fmt, args...) -#define SCTP_DEBUG_PRINTK_IPADDR(whatever...) -#define SCTP_ENABLE_DEBUG -#define SCTP_DISABLE_DEBUG -#define SCTP_ASSERT(expr, str, func) - -#endif /* SCTP_DEBUG */ - - /* * Macros for keeping a global reference of object allocations. */ @@ -597,16 +532,6 @@ static inline int param_type2af(__be16 type) } } -/* Perform some sanity checks. */ -static inline int sctp_sanity_check(void) -{ - SCTP_ASSERT(sizeof(struct sctp_ulpevent) <= - sizeof(((struct sk_buff *)0)->cb), - "SCTP: ulpevent does not fit in skb!\n", return 0); - - return 1; -} - /* Warning: The following hash functions assume a power of two 'size'. */ /* This is the hash function for the SCTP port hash table. */ static inline int sctp_phashfn(struct net *net, __u16 lport) diff --git a/net/sctp/Kconfig b/net/sctp/Kconfig index cf4852814e0c..d80bf1aebaed 100644 --- a/net/sctp/Kconfig +++ b/net/sctp/Kconfig @@ -30,7 +30,8 @@ menuconfig IP_SCTP homing at either or both ends of an association." To compile this protocol support as a module, choose M here: the - module will be called sctp. + module will be called sctp. Debug messages are handeled by the + kernel's dynamic debugging framework. If in doubt, say N. @@ -48,13 +49,14 @@ config NET_SCTPPROBE To compile this code as a module, choose M here: the module will be called sctp_probe. -config SCTP_DBG_MSG - bool "SCTP: Debug messages" +config SCTP_DBG_TSNS + bool "SCTP: Debug transactions" help - If you say Y, this will enable verbose debugging messages. + If you say Y, this will enable transaction debugging, visible + from the kernel's dynamic debugging framework. If unsure, say N. However, if you are running into problems, use - this option to gather detailed trace information + this option to gather outqueue trace information. config SCTP_DBG_OBJCNT bool "SCTP: Debug object counts" diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 9a383a8774e8..bce5b79662a6 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -357,7 +357,8 @@ struct sctp_association *sctp_association_new(const struct sctp_endpoint *ep, goto fail_init; SCTP_DBG_OBJCNT_INC(assoc); - SCTP_DEBUG_PRINTK("Created asoc %p\n", asoc); + + pr_debug("Created asoc %p\n", asoc); return asoc; @@ -455,7 +456,10 @@ void sctp_association_free(struct sctp_association *asoc) /* Cleanup and free up an association. */ static void sctp_association_destroy(struct sctp_association *asoc) { - SCTP_ASSERT(asoc->base.dead, "Assoc is not dead", return); + if (unlikely(!asoc->base.dead)) { + WARN(1, "Attempt to destroy undead association %p!\n", asoc); + return; + } sctp_endpoint_put(asoc->ep); sock_put(asoc->base.sk); @@ -536,11 +540,8 @@ void sctp_assoc_rm_peer(struct sctp_association *asoc, struct list_head *pos; struct sctp_transport *transport; - SCTP_DEBUG_PRINTK_IPADDR("sctp_assoc_rm_peer:association %p addr: ", - " port: %d\n", - asoc, - (&peer->ipaddr), - ntohs(peer->ipaddr.v4.sin_port)); + pr_debug("%s: association:%p addr:%pISpc\n", + __func__, asoc, &peer->ipaddr.sa); /* If we are to remove the current retran_path, update it * to the next peer before removing this peer from the list. @@ -636,12 +637,8 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc, /* AF_INET and AF_INET6 share common port field. */ port = ntohs(addr->v4.sin_port); - SCTP_DEBUG_PRINTK_IPADDR("sctp_assoc_add_peer:association %p addr: ", - " port: %d state:%d\n", - asoc, - addr, - port, - peer_state); + pr_debug("%s: association:%p addr:%pISpc state:%d\n", __func__, + asoc, &addr->sa, peer_state); /* Set the port if it has not been set yet. */ if (0 == asoc->peer.port) @@ -708,8 +705,9 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc, else asoc->pathmtu = peer->pathmtu; - SCTP_DEBUG_PRINTK("sctp_assoc_add_peer:association %p PMTU set to " - "%d\n", asoc, asoc->pathmtu); + pr_debug("%s: association:%p PMTU set to %d\n", __func__, asoc, + asoc->pathmtu); + peer->pmtu_pending = 0; asoc->frag_point = sctp_frag_point(asoc, asoc->pathmtu); @@ -1349,12 +1347,8 @@ void sctp_assoc_update_retran_path(struct sctp_association *asoc) else t = asoc->peer.retran_path; - SCTP_DEBUG_PRINTK_IPADDR("sctp_assoc_update_retran_path:association" - " %p addr: ", - " port: %d\n", - asoc, - (&t->ipaddr), - ntohs(t->ipaddr.v4.sin_port)); + pr_debug("%s: association:%p addr:%pISpc\n", __func__, asoc, + &t->ipaddr.sa); } /* Choose the transport for sending retransmit packet. */ @@ -1401,8 +1395,8 @@ void sctp_assoc_sync_pmtu(struct sock *sk, struct sctp_association *asoc) asoc->frag_point = sctp_frag_point(asoc, pmtu); } - SCTP_DEBUG_PRINTK("%s: asoc:%p, pmtu:%d, frag_point:%d\n", - __func__, asoc, asoc->pathmtu, asoc->frag_point); + pr_debug("%s: asoc:%p, pmtu:%d, frag_point:%d\n", __func__, asoc, + asoc->pathmtu, asoc->frag_point); } /* Should we send a SACK to update our peer? */ @@ -1454,9 +1448,9 @@ void sctp_assoc_rwnd_increase(struct sctp_association *asoc, unsigned int len) asoc->rwnd_press -= change; } - SCTP_DEBUG_PRINTK("%s: asoc %p rwnd increased by %d to (%u, %u) " - "- %u\n", __func__, asoc, len, asoc->rwnd, - asoc->rwnd_over, asoc->a_rwnd); + pr_debug("%s: asoc:%p rwnd increased by %d to (%u, %u) - %u\n", + __func__, asoc, len, asoc->rwnd, asoc->rwnd_over, + asoc->a_rwnd); /* Send a window update SACK if the rwnd has increased by at least the * minimum of the association's PMTU and half of the receive buffer. @@ -1465,9 +1459,11 @@ void sctp_assoc_rwnd_increase(struct sctp_association *asoc, unsigned int len) */ if (sctp_peer_needs_update(asoc)) { asoc->a_rwnd = asoc->rwnd; - SCTP_DEBUG_PRINTK("%s: Sending window update SACK- asoc: %p " - "rwnd: %u a_rwnd: %u\n", __func__, - asoc, asoc->rwnd, asoc->a_rwnd); + + pr_debug("%s: sending window update SACK- asoc:%p rwnd:%u " + "a_rwnd:%u\n", __func__, asoc, asoc->rwnd, + asoc->a_rwnd); + sack = sctp_make_sack(asoc); if (!sack) return; @@ -1489,8 +1485,10 @@ void sctp_assoc_rwnd_decrease(struct sctp_association *asoc, unsigned int len) int rx_count; int over = 0; - SCTP_ASSERT(asoc->rwnd, "rwnd zero", return); - SCTP_ASSERT(!asoc->rwnd_over, "rwnd_over not zero", return); + if (unlikely(!asoc->rwnd || asoc->rwnd_over)) + pr_debug("%s: association:%p has asoc->rwnd:%u, " + "asoc->rwnd_over:%u!\n", __func__, asoc, + asoc->rwnd, asoc->rwnd_over); if (asoc->ep->rcvbuf_policy) rx_count = atomic_read(&asoc->rmem_alloc); @@ -1515,9 +1513,10 @@ void sctp_assoc_rwnd_decrease(struct sctp_association *asoc, unsigned int len) asoc->rwnd_over = len - asoc->rwnd; asoc->rwnd = 0; } - SCTP_DEBUG_PRINTK("%s: asoc %p rwnd decreased by %d to (%u, %u, %u)\n", - __func__, asoc, len, asoc->rwnd, - asoc->rwnd_over, asoc->rwnd_press); + + pr_debug("%s: asoc:%p rwnd decreased by %d to (%u, %u, %u)\n", + __func__, asoc, len, asoc->rwnd, asoc->rwnd_over, + asoc->rwnd_press); } /* Build the bind address list for the association based on info from the diff --git a/net/sctp/chunk.c b/net/sctp/chunk.c index 7135fc0c087a..5780565f5b7d 100644 --- a/net/sctp/chunk.c +++ b/net/sctp/chunk.c @@ -193,8 +193,9 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc, msg->expires_at = jiffies + msecs_to_jiffies(sinfo->sinfo_timetolive); msg->can_abandon = 1; - SCTP_DEBUG_PRINTK("%s: msg:%p expires_at: %ld jiffies:%ld\n", - __func__, msg, msg->expires_at, jiffies); + + pr_debug("%s: msg:%p expires_at:%ld jiffies:%ld\n", __func__, + msg, msg->expires_at, jiffies); } /* This is the biggest possible DATA chunk that can fit into diff --git a/net/sctp/debug.c b/net/sctp/debug.c index ec997cfe0a7e..f4998780d6df 100644 --- a/net/sctp/debug.c +++ b/net/sctp/debug.c @@ -47,10 +47,6 @@ #include -#if SCTP_DEBUG -int sctp_debug_flag = 1; /* Initially enable DEBUG */ -#endif /* SCTP_DEBUG */ - /* These are printable forms of Chunk ID's from section 3.1. */ static const char *const sctp_cid_tbl[SCTP_NUM_BASE_CHUNK_TYPES] = { "DATA", diff --git a/net/sctp/endpointola.c b/net/sctp/endpointola.c index b26999d508ba..9e3d257de0e0 100644 --- a/net/sctp/endpointola.c +++ b/net/sctp/endpointola.c @@ -249,7 +249,10 @@ static void sctp_endpoint_destroy(struct sctp_endpoint *ep) { struct sock *sk; - SCTP_ASSERT(ep->base.dead, "Endpoint is not dead", return); + if (unlikely(!ep->base.dead)) { + WARN(1, "Attempt to destroy undead endpoint %p!\n", ep); + return; + } /* Free the digest buffer */ kfree(ep->digest); diff --git a/net/sctp/input.c b/net/sctp/input.c index 4cfc74699a3f..3fa4d858c35a 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -454,8 +454,6 @@ void sctp_icmp_proto_unreachable(struct sock *sk, struct sctp_association *asoc, struct sctp_transport *t) { - SCTP_DEBUG_PRINTK("%s\n", __func__); - if (sock_owned_by_user(sk)) { if (timer_pending(&t->proto_unreach_timer)) return; @@ -464,10 +462,12 @@ void sctp_icmp_proto_unreachable(struct sock *sk, jiffies + (HZ/20))) sctp_association_hold(asoc); } - } else { struct net *net = sock_net(sk); + pr_debug("%s: unrecognized next header type " + "encountered!\n", __func__); + if (del_timer(&t->proto_unreach_timer)) sctp_association_put(asoc); diff --git a/net/sctp/inqueue.c b/net/sctp/inqueue.c index 3221d073448c..cb25f040fed0 100644 --- a/net/sctp/inqueue.c +++ b/net/sctp/inqueue.c @@ -219,10 +219,10 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue) chunk->end_of_packet = 1; } - SCTP_DEBUG_PRINTK("+++sctp_inq_pop+++ chunk %p[%s]," - " length %d, skb->len %d\n",chunk, - sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type)), - ntohs(chunk->chunk_hdr->length), chunk->skb->len); + pr_debug("+++sctp_inq_pop+++ chunk:%p[%s], length:%d, skb->len:%d\n", + chunk, sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type)), + ntohs(chunk->chunk_hdr->length), chunk->skb->len); + return chunk; } @@ -238,4 +238,3 @@ void sctp_inq_set_th_handler(struct sctp_inq *q, work_func_t callback) { INIT_WORK(&q->immediate, callback); } - diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index adeaa0e64f52..09ffcc912d23 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -239,9 +239,8 @@ static int sctp_v6_xmit(struct sk_buff *skb, struct sctp_transport *transport) fl6.daddr = *rt0->addr; } - SCTP_DEBUG_PRINTK("%s: skb:%p, len:%d, src:%pI6 dst:%pI6\n", - __func__, skb, skb->len, - &fl6.saddr, &fl6.daddr); + pr_debug("%s: skb:%p, len:%d, src:%pI6 dst:%pI6\n", __func__, skb, + skb->len, &fl6.saddr, &fl6.daddr); SCTP_INC_STATS(sock_net(sk), SCTP_MIB_OUTSCTPPACKS); @@ -276,7 +275,7 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr, if (ipv6_addr_type(&daddr->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL) fl6->flowi6_oif = daddr->v6.sin6_scope_id; - SCTP_DEBUG_PRINTK("%s: DST=%pI6 ", __func__, &fl6->daddr); + pr_debug("%s: dst=%pI6 ", __func__, &fl6->daddr); if (asoc) fl6->fl6_sport = htons(asoc->base.bind_addr.port); @@ -284,7 +283,8 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr, if (saddr) { fl6->saddr = saddr->v6.sin6_addr; fl6->fl6_sport = saddr->v6.sin6_port; - SCTP_DEBUG_PRINTK("SRC=%pI6 - ", &fl6->saddr); + + pr_debug("src=%pI6 - ", &fl6->saddr); } dst = ip6_dst_lookup_flow(sk, fl6, NULL, false); @@ -348,13 +348,16 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr, out: if (!IS_ERR_OR_NULL(dst)) { struct rt6_info *rt; + rt = (struct rt6_info *)dst; t->dst = dst; - SCTP_DEBUG_PRINTK("rt6_dst:%pI6 rt6_src:%pI6\n", - &rt->rt6i_dst.addr, &fl6->saddr); + + pr_debug("rt6_dst:%pI6 rt6_src:%pI6\n", &rt->rt6i_dst.addr, + &fl6->saddr); } else { t->dst = NULL; - SCTP_DEBUG_PRINTK("NO ROUTE\n"); + + pr_debug("no route\n"); } } @@ -377,7 +380,7 @@ static void sctp_v6_get_saddr(struct sctp_sock *sk, struct flowi6 *fl6 = &fl->u.ip6; union sctp_addr *saddr = &t->saddr; - SCTP_DEBUG_PRINTK("%s: asoc:%p dst:%p\n", __func__, t->asoc, t->dst); + pr_debug("%s: asoc:%p dst:%p\n", __func__, t->asoc, t->dst); if (t->dst) { saddr->v6.sin6_family = AF_INET6; diff --git a/net/sctp/output.c b/net/sctp/output.c index bbef4a7a9b56..a46d1eb41762 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -93,8 +93,7 @@ struct sctp_packet *sctp_packet_config(struct sctp_packet *packet, { struct sctp_chunk *chunk = NULL; - SCTP_DEBUG_PRINTK("%s: packet:%p vtag:0x%x\n", __func__, - packet, vtag); + pr_debug("%s: packet:%p vtag:0x%x\n", __func__, packet, vtag); packet->vtag = vtag; @@ -119,8 +118,7 @@ struct sctp_packet *sctp_packet_init(struct sctp_packet *packet, struct sctp_association *asoc = transport->asoc; size_t overhead; - SCTP_DEBUG_PRINTK("%s: packet:%p transport:%p\n", __func__, - packet, transport); + pr_debug("%s: packet:%p transport:%p\n", __func__, packet, transport); packet->transport = transport; packet->source_port = sport; @@ -145,7 +143,7 @@ void sctp_packet_free(struct sctp_packet *packet) { struct sctp_chunk *chunk, *tmp; - SCTP_DEBUG_PRINTK("%s: packet:%p\n", __func__, packet); + pr_debug("%s: packet:%p\n", __func__, packet); list_for_each_entry_safe(chunk, tmp, &packet->chunk_list, list) { list_del_init(&chunk->list); @@ -167,8 +165,7 @@ sctp_xmit_t sctp_packet_transmit_chunk(struct sctp_packet *packet, sctp_xmit_t retval; int error = 0; - SCTP_DEBUG_PRINTK("%s: packet:%p chunk:%p\n", __func__, - packet, chunk); + pr_debug("%s: packet:%p chunk:%p\n", __func__, packet, chunk); switch ((retval = (sctp_packet_append_chunk(packet, chunk)))) { case SCTP_XMIT_PMTU_FULL: @@ -334,8 +331,7 @@ sctp_xmit_t sctp_packet_append_chunk(struct sctp_packet *packet, { sctp_xmit_t retval = SCTP_XMIT_OK; - SCTP_DEBUG_PRINTK("%s: packet:%p chunk:%p\n", __func__, packet, - chunk); + pr_debug("%s: packet:%p chunk:%p\n", __func__, packet, chunk); /* Data chunks are special. Before seeing what else we can * bundle into this packet, check to see if we are allowed to @@ -402,7 +398,7 @@ int sctp_packet_transmit(struct sctp_packet *packet) unsigned char *auth = NULL; /* pointer to auth in skb data */ __u32 cksum_buf_len = sizeof(struct sctphdr); - SCTP_DEBUG_PRINTK("%s: packet:%p\n", __func__, packet); + pr_debug("%s: packet:%p\n", __func__, packet); /* Do NOT generate a chunkless packet. */ if (list_empty(&packet->chunk_list)) @@ -472,7 +468,9 @@ int sctp_packet_transmit(struct sctp_packet *packet) * * [This whole comment explains WORD_ROUND() below.] */ - SCTP_DEBUG_PRINTK("***sctp_transmit_packet***\n"); + + pr_debug("***sctp_transmit_packet***\n"); + list_for_each_entry_safe(chunk, tmp, &packet->chunk_list, list) { list_del_init(&chunk->list); if (sctp_chunk_is_data(chunk)) { @@ -505,16 +503,13 @@ int sctp_packet_transmit(struct sctp_packet *packet) memcpy(skb_put(nskb, chunk->skb->len), chunk->skb->data, chunk->skb->len); - SCTP_DEBUG_PRINTK("%s %p[%s] %s 0x%x, %s %d, %s %d, %s %d\n", - "*** Chunk", chunk, - sctp_cname(SCTP_ST_CHUNK( - chunk->chunk_hdr->type)), - chunk->has_tsn ? "TSN" : "No TSN", - chunk->has_tsn ? - ntohl(chunk->subh.data_hdr->tsn) : 0, - "length", ntohs(chunk->chunk_hdr->length), - "chunk->skb->len", chunk->skb->len, - "rtt_in_progress", chunk->rtt_in_progress); + pr_debug("*** Chunk:%p[%s] %s 0x%x, length:%d, chunk->skb->len:%d, " + "rtt_in_progress:%d\n", chunk, + sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type)), + chunk->has_tsn ? "TSN" : "No TSN", + chunk->has_tsn ? ntohl(chunk->subh.data_hdr->tsn) : 0, + ntohs(chunk->chunk_hdr->length), chunk->skb->len, + chunk->rtt_in_progress); /* * If this is a control chunk, this is our last @@ -606,8 +601,7 @@ int sctp_packet_transmit(struct sctp_packet *packet) } } - SCTP_DEBUG_PRINTK("***sctp_transmit_packet*** skb len %d\n", - nskb->len); + pr_debug("***sctp_transmit_packet*** skb->len:%d\n", nskb->len); nskb->local_df = packet->ipfragok; (*tp->af_specific->sctp_xmit)(nskb, tp); diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index be35e2dbcc9a..511b3b35d609 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -299,10 +299,10 @@ int sctp_outq_tail(struct sctp_outq *q, struct sctp_chunk *chunk) struct net *net = sock_net(q->asoc->base.sk); int error = 0; - SCTP_DEBUG_PRINTK("sctp_outq_tail(%p, %p[%s])\n", - q, chunk, chunk && chunk->chunk_hdr ? - sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type)) - : "Illegal Chunk"); + pr_debug("%s: outq:%p, chunk:%p[%s]\n", __func__, q, chunk, + chunk && chunk->chunk_hdr ? + sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type)) : + "illegal chunk"); /* If it is data, queue it up, otherwise, send it * immediately. @@ -328,10 +328,10 @@ int sctp_outq_tail(struct sctp_outq *q, struct sctp_chunk *chunk) break; default: - SCTP_DEBUG_PRINTK("outqueueing (%p, %p[%s])\n", - q, chunk, chunk && chunk->chunk_hdr ? - sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type)) - : "Illegal Chunk"); + pr_debug("%s: outqueueing: outq:%p, chunk:%p[%s])\n", + __func__, q, chunk, chunk && chunk->chunk_hdr ? + sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type)) : + "illegal chunk"); sctp_outq_tail_data(q, chunk); if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED) @@ -460,14 +460,10 @@ void sctp_retransmit_mark(struct sctp_outq *q, } } - SCTP_DEBUG_PRINTK("%s: transport: %p, reason: %d, " - "cwnd: %d, ssthresh: %d, flight_size: %d, " - "pba: %d\n", __func__, - transport, reason, - transport->cwnd, transport->ssthresh, - transport->flight_size, - transport->partial_bytes_acked); - + pr_debug("%s: transport:%p, reason:%d, cwnd:%d, ssthresh:%d, " + "flight_size:%d, pba:%d\n", __func__, transport, reason, + transport->cwnd, transport->ssthresh, transport->flight_size, + transport->partial_bytes_acked); } /* Mark all the eligible packets on a transport for retransmission and force @@ -1014,19 +1010,13 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) sctp_transport_burst_limited(transport); } - SCTP_DEBUG_PRINTK("sctp_outq_flush(%p, %p[%s]), ", - q, chunk, - chunk && chunk->chunk_hdr ? - sctp_cname(SCTP_ST_CHUNK( - chunk->chunk_hdr->type)) - : "Illegal Chunk"); - - SCTP_DEBUG_PRINTK("TX TSN 0x%x skb->head " - "%p skb->users %d.\n", - ntohl(chunk->subh.data_hdr->tsn), - chunk->skb ?chunk->skb->head : NULL, - chunk->skb ? - atomic_read(&chunk->skb->users) : -1); + pr_debug("%s: outq:%p, chunk:%p[%s], tx-tsn:0x%x skb->head:%p " + "skb->users:%d\n", + __func__, q, chunk, chunk && chunk->chunk_hdr ? + sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type)) : + "illegal chunk", ntohl(chunk->subh.data_hdr->tsn), + chunk->skb ? chunk->skb->head : NULL, chunk->skb ? + atomic_read(&chunk->skb->users) : -1); /* Add the chunk to the packet. */ status = sctp_packet_transmit_chunk(packet, chunk, 0); @@ -1038,10 +1028,10 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) /* We could not append this chunk, so put * the chunk back on the output queue. */ - SCTP_DEBUG_PRINTK("sctp_outq_flush: could " - "not transmit TSN: 0x%x, status: %d\n", - ntohl(chunk->subh.data_hdr->tsn), - status); + pr_debug("%s: could not transmit tsn:0x%x, status:%d\n", + __func__, ntohl(chunk->subh.data_hdr->tsn), + status); + sctp_outq_head_data(q, chunk); goto sctp_flush_out; break; @@ -1284,11 +1274,10 @@ int sctp_outq_sack(struct sctp_outq *q, struct sctp_chunk *chunk) sctp_generate_fwdtsn(q, sack_ctsn); - SCTP_DEBUG_PRINTK("%s: sack Cumulative TSN Ack is 0x%x.\n", - __func__, sack_ctsn); - SCTP_DEBUG_PRINTK("%s: Cumulative TSN Ack of association, " - "%p is 0x%x. Adv peer ack point: 0x%x\n", - __func__, asoc, ctsn, asoc->adv_peer_ack_point); + pr_debug("%s: sack cumulative tsn ack:0x%x\n", __func__, sack_ctsn); + pr_debug("%s: cumulative tsn ack of assoc:%p is 0x%x, " + "advertised peer ack point:0x%x\n", __func__, asoc, ctsn, + asoc->adv_peer_ack_point); /* See if all chunks are acked. * Make sure the empty queue handler will get run later. @@ -1304,7 +1293,7 @@ int sctp_outq_sack(struct sctp_outq *q, struct sctp_chunk *chunk) goto finish; } - SCTP_DEBUG_PRINTK("sack queue is empty.\n"); + pr_debug("%s: sack queue is empty\n", __func__); finish: return q->empty; } @@ -1348,7 +1337,7 @@ static void sctp_check_transmitted(struct sctp_outq *q, /* These state variables are for coherent debug output. --xguo */ -#if SCTP_DEBUG +#ifdef CONFIG_SCTP_DBG_TSNS __u32 dbg_ack_tsn = 0; /* An ACKed TSN range starts here... */ __u32 dbg_last_ack_tsn = 0; /* ...and finishes here. */ __u32 dbg_kept_tsn = 0; /* An un-ACKed range starts here... */ @@ -1359,7 +1348,7 @@ static void sctp_check_transmitted(struct sctp_outq *q, * -1: We need to initialize. */ int dbg_prt_state = -1; -#endif /* SCTP_DEBUG */ +#endif /* CONFIG_SCTP_DBG_TSNS */ sack_ctsn = ntohl(sack->cum_tsn_ack); @@ -1483,7 +1472,7 @@ static void sctp_check_transmitted(struct sctp_outq *q, list_add_tail(lchunk, &tlist); } -#if SCTP_DEBUG +#ifdef CONFIG_SCTP_DBG_TSNS switch (dbg_prt_state) { case 0: /* last TSN was ACKed */ if (dbg_last_ack_tsn + 1 == tsn) { @@ -1497,42 +1486,39 @@ static void sctp_check_transmitted(struct sctp_outq *q, /* Display the end of the * current range. */ - SCTP_DEBUG_PRINTK_CONT("-%08x", - dbg_last_ack_tsn); + pr_cont("-%08x", dbg_last_ack_tsn); } /* Start a new range. */ - SCTP_DEBUG_PRINTK_CONT(",%08x", tsn); + pr_cont(",%08x", tsn); dbg_ack_tsn = tsn; break; case 1: /* The last TSN was NOT ACKed. */ if (dbg_last_kept_tsn != dbg_kept_tsn) { /* Display the end of current range. */ - SCTP_DEBUG_PRINTK_CONT("-%08x", - dbg_last_kept_tsn); + pr_cont("-%08x", dbg_last_kept_tsn); } - SCTP_DEBUG_PRINTK_CONT("\n"); - + pr_cont("\n"); /* FALL THROUGH... */ default: /* This is the first-ever TSN we examined. */ /* Start a new range of ACK-ed TSNs. */ - SCTP_DEBUG_PRINTK("ACKed: %08x", tsn); + pr_debug("ACKed: %08x", tsn); + dbg_prt_state = 0; dbg_ack_tsn = tsn; } dbg_last_ack_tsn = tsn; -#endif /* SCTP_DEBUG */ +#endif /* CONFIG_SCTP_DBG_TSNS */ } else { if (tchunk->tsn_gap_acked) { - SCTP_DEBUG_PRINTK("%s: Receiver reneged on " - "data TSN: 0x%x\n", - __func__, - tsn); + pr_debug("%s: receiver reneged on data TSN:0x%x\n", + __func__, tsn); + tchunk->tsn_gap_acked = 0; if (tchunk->transport) @@ -1552,7 +1538,7 @@ static void sctp_check_transmitted(struct sctp_outq *q, list_add_tail(lchunk, &tlist); -#if SCTP_DEBUG +#ifdef CONFIG_SCTP_DBG_TSNS /* See the above comments on ACK-ed TSNs. */ switch (dbg_prt_state) { case 1: @@ -1560,50 +1546,47 @@ static void sctp_check_transmitted(struct sctp_outq *q, break; if (dbg_last_kept_tsn != dbg_kept_tsn) - SCTP_DEBUG_PRINTK_CONT("-%08x", - dbg_last_kept_tsn); + pr_cont("-%08x", dbg_last_kept_tsn); - SCTP_DEBUG_PRINTK_CONT(",%08x", tsn); + pr_cont(",%08x", tsn); dbg_kept_tsn = tsn; break; case 0: if (dbg_last_ack_tsn != dbg_ack_tsn) - SCTP_DEBUG_PRINTK_CONT("-%08x", - dbg_last_ack_tsn); - SCTP_DEBUG_PRINTK_CONT("\n"); + pr_cont("-%08x", dbg_last_ack_tsn); + pr_cont("\n"); /* FALL THROUGH... */ default: - SCTP_DEBUG_PRINTK("KEPT: %08x",tsn); + pr_debug("KEPT: %08x", tsn); + dbg_prt_state = 1; dbg_kept_tsn = tsn; } dbg_last_kept_tsn = tsn; -#endif /* SCTP_DEBUG */ +#endif /* CONFIG_SCTP_DBG_TSNS */ } } -#if SCTP_DEBUG +#ifdef CONFIG_SCTP_DBG_TSNS /* Finish off the last range, displaying its ending TSN. */ switch (dbg_prt_state) { case 0: - if (dbg_last_ack_tsn != dbg_ack_tsn) { - SCTP_DEBUG_PRINTK_CONT("-%08x\n", dbg_last_ack_tsn); - } else { - SCTP_DEBUG_PRINTK_CONT("\n"); - } - break; - + if (dbg_last_ack_tsn != dbg_ack_tsn) + pr_cont("-%08x\n", dbg_last_ack_tsn); + else + pr_cont("\n"); + break; case 1: - if (dbg_last_kept_tsn != dbg_kept_tsn) { - SCTP_DEBUG_PRINTK_CONT("-%08x\n", dbg_last_kept_tsn); - } else { - SCTP_DEBUG_PRINTK_CONT("\n"); - } + if (dbg_last_kept_tsn != dbg_kept_tsn) + pr_cont("-%08x\n", dbg_last_kept_tsn); + else + pr_cont("\n"); + break; } -#endif /* SCTP_DEBUG */ +#endif /* CONFIG_SCTP_DBG_TSNS */ if (transport) { if (bytes_acked) { struct sctp_association *asoc = transport->asoc; @@ -1676,9 +1659,9 @@ static void sctp_check_transmitted(struct sctp_outq *q, !list_empty(&tlist) && (sack_ctsn+2 == q->asoc->next_tsn) && q->asoc->state < SCTP_STATE_SHUTDOWN_PENDING) { - SCTP_DEBUG_PRINTK("%s: SACK received for zero " - "window probe: %u\n", - __func__, sack_ctsn); + pr_debug("%s: sack received for zero window " + "probe:%u\n", __func__, sack_ctsn); + q->asoc->overall_error_count = 0; transport->error_count = 0; } @@ -1739,10 +1722,8 @@ static void sctp_mark_missing(struct sctp_outq *q, count_of_newacks, tsn)) { chunk->tsn_missing_report++; - SCTP_DEBUG_PRINTK( - "%s: TSN 0x%x missing counter: %d\n", - __func__, tsn, - chunk->tsn_missing_report); + pr_debug("%s: tsn:0x%x missing counter:%d\n", + __func__, tsn, chunk->tsn_missing_report); } } /* @@ -1762,11 +1743,10 @@ static void sctp_mark_missing(struct sctp_outq *q, if (do_fast_retransmit) sctp_retransmit(q, transport, SCTP_RTXR_FAST_RTX); - SCTP_DEBUG_PRINTK("%s: transport: %p, cwnd: %d, " - "ssthresh: %d, flight_size: %d, pba: %d\n", - __func__, transport, transport->cwnd, - transport->ssthresh, transport->flight_size, - transport->partial_bytes_acked); + pr_debug("%s: transport:%p, cwnd:%d, ssthresh:%d, " + "flight_size:%d, pba:%d\n", __func__, transport, + transport->cwnd, transport->ssthresh, + transport->flight_size, transport->partial_bytes_acked); } } diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 1de49c802d83..4a17494d736c 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -451,8 +451,8 @@ static void sctp_v4_get_dst(struct sctp_transport *t, union sctp_addr *saddr, fl4->fl4_sport = saddr->v4.sin_port; } - SCTP_DEBUG_PRINTK("%s: DST:%pI4, SRC:%pI4 - ", - __func__, &fl4->daddr, &fl4->saddr); + pr_debug("%s: dst:%pI4, src:%pI4 - ", __func__, &fl4->daddr, + &fl4->saddr); rt = ip_route_output_key(sock_net(sk), fl4); if (!IS_ERR(rt)) @@ -513,10 +513,10 @@ out_unlock: out: t->dst = dst; if (dst) - SCTP_DEBUG_PRINTK("rt_dst:%pI4, rt_src:%pI4\n", - &fl4->daddr, &fl4->saddr); + pr_debug("rt_dst:%pI4, rt_src:%pI4\n", + &fl4->daddr, &fl4->saddr); else - SCTP_DEBUG_PRINTK("NO ROUTE\n"); + pr_debug("no route\n"); } /* For v4, the source address is cached in the route entry(dst). So no need @@ -604,9 +604,9 @@ static void sctp_addr_wq_timeout_handler(unsigned long arg) spin_lock_bh(&net->sctp.addr_wq_lock); list_for_each_entry_safe(addrw, temp, &net->sctp.addr_waitq, list) { - SCTP_DEBUG_PRINTK_IPADDR("sctp_addrwq_timo_handler: the first ent in wq %p is ", - " for cmd %d at entry %p\n", &net->sctp.addr_waitq, &addrw->a, addrw->state, - addrw); + pr_debug("%s: the first ent in wq:%p is addr:%pISc for cmd:%d at " + "entry:%p\n", __func__, &net->sctp.addr_waitq, &addrw->a.sa, + addrw->state, addrw); #if IS_ENABLED(CONFIG_IPV6) /* Now we send an ASCONF for each association */ @@ -623,8 +623,10 @@ static void sctp_addr_wq_timeout_handler(unsigned long arg) addrw->state == SCTP_ADDR_NEW) { unsigned long timeo_val; - SCTP_DEBUG_PRINTK("sctp_timo_handler: this is on DAD, trying %d sec later\n", - SCTP_ADDRESS_TICK_DELAY); + pr_debug("%s: this is on DAD, trying %d sec " + "later\n", __func__, + SCTP_ADDRESS_TICK_DELAY); + timeo_val = jiffies; timeo_val += msecs_to_jiffies(SCTP_ADDRESS_TICK_DELAY); mod_timer(&net->sctp.addr_wq_timer, timeo_val); @@ -641,7 +643,7 @@ static void sctp_addr_wq_timeout_handler(unsigned long arg) continue; sctp_bh_lock_sock(sk); if (sctp_asconf_mgmt(sp, addrw) < 0) - SCTP_DEBUG_PRINTK("sctp_addrwq_timo_handler: sctp_asconf_mgmt failed\n"); + pr_debug("%s: sctp_asconf_mgmt failed\n", __func__); sctp_bh_unlock_sock(sk); } #if IS_ENABLED(CONFIG_IPV6) @@ -707,9 +709,10 @@ void sctp_addr_wq_mgmt(struct net *net, struct sctp_sockaddr_entry *addr, int cm addrw = sctp_addr_wq_lookup(net, addr); if (addrw) { if (addrw->state != cmd) { - SCTP_DEBUG_PRINTK_IPADDR("sctp_addr_wq_mgmt offsets existing entry for %d ", - " in wq %p\n", addrw->state, &addrw->a, - &net->sctp.addr_waitq); + pr_debug("%s: offsets existing entry for %d, addr:%pISc " + "in wq:%p\n", __func__, addrw->state, &addrw->a.sa, + &net->sctp.addr_waitq); + list_del(&addrw->list); kfree(addrw); } @@ -725,8 +728,9 @@ void sctp_addr_wq_mgmt(struct net *net, struct sctp_sockaddr_entry *addr, int cm } addrw->state = cmd; list_add_tail(&addrw->list, &net->sctp.addr_waitq); - SCTP_DEBUG_PRINTK_IPADDR("sctp_addr_wq_mgmt add new entry for cmd:%d ", - " in wq %p\n", addrw->state, &addrw->a, &net->sctp.addr_waitq); + + pr_debug("%s: add new entry for cmd:%d, addr:%pISc in wq:%p\n", + __func__, addrw->state, &addrw->a.sa, &net->sctp.addr_waitq); if (!timer_pending(&net->sctp.addr_wq_timer)) { timeo_val = jiffies; @@ -952,15 +956,14 @@ static inline int sctp_v4_xmit(struct sk_buff *skb, { struct inet_sock *inet = inet_sk(skb->sk); - SCTP_DEBUG_PRINTK("%s: skb:%p, len:%d, src:%pI4, dst:%pI4\n", - __func__, skb, skb->len, - &transport->fl.u.ip4.saddr, - &transport->fl.u.ip4.daddr); + pr_debug("%s: skb:%p, len:%d, src:%pI4, dst:%pI4\n", __func__, skb, + skb->len, &transport->fl.u.ip4.saddr, &transport->fl.u.ip4.daddr); inet->pmtudisc = transport->param_flags & SPP_PMTUD_ENABLE ? IP_PMTUDISC_DO : IP_PMTUDISC_DONT; SCTP_INC_STATS(sock_net(&inet->sk), SCTP_MIB_OUTSCTPPACKS); + return ip_queue_xmit(skb, &transport->fl); } @@ -1321,9 +1324,8 @@ static __init int sctp_init(void) int max_share; int order; - /* SCTP_DEBUG sanity check. */ - if (!sctp_sanity_check()) - goto out; + BUILD_BUG_ON(sizeof(struct sctp_ulpevent) > + sizeof(((struct sk_buff *) 0)->cb)); /* Allocate bind_bucket and chunk caches. */ status = -ENOBUFS; diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index dd71f1f9ba10..362ae6e2fd93 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -741,7 +741,8 @@ struct sctp_chunk *sctp_make_sack(const struct sctp_association *asoc) memset(gabs, 0, sizeof(gabs)); ctsn = sctp_tsnmap_get_ctsn(map); - SCTP_DEBUG_PRINTK("sackCTSNAck sent: 0x%x.\n", ctsn); + + pr_debug("%s: sackCTSNAck sent:0x%x\n", __func__, ctsn); /* How much room is needed in the chunk? */ num_gabs = sctp_tsnmap_num_gabs(map, gabs); @@ -1287,10 +1288,8 @@ struct sctp_chunk *sctp_chunkify(struct sk_buff *skb, if (!retval) goto nodata; - - if (!sk) { - SCTP_DEBUG_PRINTK("chunkifying skb %p w/o an sk\n", skb); - } + if (!sk) + pr_debug("%s: chunkifying skb:%p w/o an sk\n", __func__, skb); INIT_LIST_HEAD(&retval->list); retval->skb = skb; @@ -2191,8 +2190,9 @@ static sctp_ierror_t sctp_verify_param(struct net *net, break; fallthrough: default: - SCTP_DEBUG_PRINTK("Unrecognized param: %d for chunk %d.\n", - ntohs(param.p->type), cid); + pr_debug("%s: unrecognized param:%d for chunk:%d\n", + __func__, ntohs(param.p->type), cid); + retval = sctp_process_unk_param(asoc, param, chunk, err_chunk); break; } @@ -2516,7 +2516,7 @@ do_addr_param: break; case SCTP_PARAM_HOST_NAME_ADDRESS: - SCTP_DEBUG_PRINTK("unimplemented SCTP_HOST_NAME_ADDRESS\n"); + pr_debug("%s: unimplemented SCTP_HOST_NAME_ADDRESS\n", __func__); break; case SCTP_PARAM_SUPPORTED_ADDRESS_TYPES: @@ -2662,8 +2662,8 @@ fall_through: * called prior to this routine. Simply log the error * here. */ - SCTP_DEBUG_PRINTK("Ignoring param: %d for association %p.\n", - ntohs(param.p->type), asoc); + pr_debug("%s: ignoring param:%d for association:%p.\n", + __func__, ntohs(param.p->type), asoc); break; } @@ -2805,7 +2805,10 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc, totallen += paramlen; totallen += addr_param_len; del_pickup = 1; - SCTP_DEBUG_PRINTK("mkasconf_update_ip: picked same-scope del_pending addr, totallen for all addresses is %d\n", totallen); + + pr_debug("%s: picked same-scope del_pending addr, " + "totallen for all addresses is %d\n", + __func__, totallen); } } diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index ff91f47b0239..cf6f84518222 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -257,7 +257,7 @@ void sctp_generate_t3_rtx_event(unsigned long peer) sctp_bh_lock_sock(asoc->base.sk); if (sock_owned_by_user(asoc->base.sk)) { - SCTP_DEBUG_PRINTK("%s:Sock is busy.\n", __func__); + pr_debug("%s: sock is busy\n", __func__); /* Try again later. */ if (!mod_timer(&transport->T3_rtx_timer, jiffies + (HZ/20))) @@ -297,9 +297,8 @@ static void sctp_generate_timeout_event(struct sctp_association *asoc, sctp_bh_lock_sock(asoc->base.sk); if (sock_owned_by_user(asoc->base.sk)) { - SCTP_DEBUG_PRINTK("%s:Sock is busy: timer %d\n", - __func__, - timeout_type); + pr_debug("%s: sock is busy: timer %d\n", __func__, + timeout_type); /* Try again later. */ if (!mod_timer(&asoc->timers[timeout_type], jiffies + (HZ/20))) @@ -377,7 +376,7 @@ void sctp_generate_heartbeat_event(unsigned long data) sctp_bh_lock_sock(asoc->base.sk); if (sock_owned_by_user(asoc->base.sk)) { - SCTP_DEBUG_PRINTK("%s:Sock is busy.\n", __func__); + pr_debug("%s: sock is busy\n", __func__); /* Try again later. */ if (!mod_timer(&transport->hb_timer, jiffies + (HZ/20))) @@ -415,7 +414,7 @@ void sctp_generate_proto_unreach_event(unsigned long data) sctp_bh_lock_sock(asoc->base.sk); if (sock_owned_by_user(asoc->base.sk)) { - SCTP_DEBUG_PRINTK("%s:Sock is busy.\n", __func__); + pr_debug("%s: sock is busy\n", __func__); /* Try again later. */ if (!mod_timer(&transport->proto_unreach_timer, @@ -521,11 +520,9 @@ static void sctp_do_8_2_transport_strike(sctp_cmd_seq_t *commands, if (transport->state != SCTP_INACTIVE && (transport->error_count > transport->pathmaxrxt)) { - SCTP_DEBUG_PRINTK_IPADDR("transport_strike:association %p", - " transport IP: port:%d failed.\n", - asoc, - (&transport->ipaddr), - ntohs(transport->ipaddr.v4.sin_port)); + pr_debug("%s: association:%p transport addr:%pISpc failed\n", + __func__, asoc, &transport->ipaddr.sa); + sctp_assoc_control_transport(asoc, transport, SCTP_TRANSPORT_DOWN, SCTP_FAILED_THRESHOLD); @@ -804,8 +801,7 @@ static void sctp_cmd_new_state(sctp_cmd_seq_t *cmds, asoc->state = state; - SCTP_DEBUG_PRINTK("sctp_cmd_new_state: asoc %p[%s]\n", - asoc, sctp_state_tbl[state]); + pr_debug("%s: asoc:%p[%s]\n", __func__, asoc, sctp_state_tbl[state]); if (sctp_style(sk, TCP)) { /* Change the sk->sk_state of a TCP-style socket that has @@ -1017,15 +1013,11 @@ static void sctp_cmd_t1_timer_update(struct sctp_association *asoc, asoc->timeouts[timer] = asoc->max_init_timeo; } asoc->init_cycle++; - SCTP_DEBUG_PRINTK( - "T1 %s Timeout adjustment" - " init_err_counter: %d" - " cycle: %d" - " timeout: %ld\n", - name, - asoc->init_err_counter, - asoc->init_cycle, - asoc->timeouts[timer]); + + pr_debug("%s: T1[%s] timeout adjustment init_err_counter:%d" + " cycle:%d timeout:%ld\n", __func__, name, + asoc->init_err_counter, asoc->init_cycle, + asoc->timeouts[timer]); } } @@ -1080,23 +1072,19 @@ static void sctp_cmd_send_asconf(struct sctp_association *asoc) * main flow of sctp_do_sm() to keep attention focused on the real * functionality there. */ -#define DEBUG_PRE \ - SCTP_DEBUG_PRINTK("sctp_do_sm prefn: " \ - "ep %p, %s, %s, asoc %p[%s], %s\n", \ - ep, sctp_evttype_tbl[event_type], \ - (*debug_fn)(subtype), asoc, \ - sctp_state_tbl[state], state_fn->name) - -#define DEBUG_POST \ - SCTP_DEBUG_PRINTK("sctp_do_sm postfn: " \ - "asoc %p, status: %s\n", \ - asoc, sctp_status_tbl[status]) - -#define DEBUG_POST_SFX \ - SCTP_DEBUG_PRINTK("sctp_do_sm post sfx: error %d, asoc %p[%s]\n", \ - error, asoc, \ - sctp_state_tbl[(asoc && sctp_id2assoc(ep->base.sk, \ - sctp_assoc2id(asoc)))?asoc->state:SCTP_STATE_CLOSED]) +#define debug_pre_sfn() \ + pr_debug("%s[pre-fn]: ep:%p, %s, %s, asoc:%p[%s], %s\n", __func__, \ + ep, sctp_evttype_tbl[event_type], (*debug_fn)(subtype), \ + asoc, sctp_state_tbl[state], state_fn->name) + +#define debug_post_sfn() \ + pr_debug("%s[post-fn]: asoc:%p, status:%s\n", __func__, asoc, \ + sctp_status_tbl[status]) + +#define debug_post_sfx() \ + pr_debug("%s[post-sfx]: error:%d, asoc:%p[%s]\n", __func__, error, \ + asoc, sctp_state_tbl[(asoc && sctp_id2assoc(ep->base.sk, \ + sctp_assoc2id(asoc))) ? asoc->state : SCTP_STATE_CLOSED]) /* * This is the master state machine processing function. @@ -1116,7 +1104,6 @@ int sctp_do_sm(struct net *net, sctp_event_t event_type, sctp_subtype_t subtype, sctp_disposition_t status; int error = 0; typedef const char *(printfn_t)(sctp_subtype_t); - static printfn_t *table[] = { NULL, sctp_cname, sctp_tname, sctp_oname, sctp_pname, }; @@ -1129,21 +1116,18 @@ int sctp_do_sm(struct net *net, sctp_event_t event_type, sctp_subtype_t subtype, sctp_init_cmd_seq(&commands); - DEBUG_PRE; + debug_pre_sfn(); status = (*state_fn->fn)(net, ep, asoc, subtype, event_arg, &commands); - DEBUG_POST; + debug_post_sfn(); error = sctp_side_effects(event_type, subtype, state, ep, asoc, event_arg, status, &commands, gfp); - DEBUG_POST_SFX; + debug_post_sfx(); return error; } -#undef DEBUG_PRE -#undef DEBUG_POST - /***************************************************************** * This the master state function side effect processing function. *****************************************************************/ @@ -1172,9 +1156,9 @@ static int sctp_side_effects(sctp_event_t event_type, sctp_subtype_t subtype, switch (status) { case SCTP_DISPOSITION_DISCARD: - SCTP_DEBUG_PRINTK("Ignored sctp protocol event - state %d, " - "event_type %d, event_id %d\n", - state, event_type, subtype.chunk); + pr_debug("%s: ignored sctp protocol event - state:%d, " + "event_type:%d, event_id:%d\n", __func__, state, + event_type, subtype.chunk); break; case SCTP_DISPOSITION_NOMEM: @@ -1425,18 +1409,18 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, case SCTP_CMD_CHUNK_ULP: /* Send a chunk to the sockets layer. */ - SCTP_DEBUG_PRINTK("sm_sideff: %s %p, %s %p.\n", - "chunk_up:", cmd->obj.chunk, - "ulpq:", &asoc->ulpq); + pr_debug("%s: sm_sideff: chunk_up:%p, ulpq:%p\n", + __func__, cmd->obj.chunk, &asoc->ulpq); + sctp_ulpq_tail_data(&asoc->ulpq, cmd->obj.chunk, GFP_ATOMIC); break; case SCTP_CMD_EVENT_ULP: /* Send a notification to the sockets layer. */ - SCTP_DEBUG_PRINTK("sm_sideff: %s %p, %s %p.\n", - "event_up:",cmd->obj.ulpevent, - "ulpq:",&asoc->ulpq); + pr_debug("%s: sm_sideff: event_up:%p, ulpq:%p\n", + __func__, cmd->obj.ulpevent, &asoc->ulpq); + sctp_ulpq_tail_event(&asoc->ulpq, cmd->obj.ulpevent); break; @@ -1601,7 +1585,7 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, break; case SCTP_CMD_REPORT_BAD_TAG: - SCTP_DEBUG_PRINTK("vtag mismatch!\n"); + pr_debug("%s: vtag mismatch!\n", __func__); break; case SCTP_CMD_STRIKE: diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index b3d186856513..f6b7109195a6 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -1179,9 +1179,9 @@ sctp_disposition_t sctp_sf_backbeat_8_3(struct net *net, /* Check if the timestamp looks valid. */ if (time_after(hbinfo->sent_at, jiffies) || time_after(jiffies, hbinfo->sent_at + max_interval)) { - SCTP_DEBUG_PRINTK("%s: HEARTBEAT ACK with invalid timestamp " - "received for transport: %p\n", - __func__, link); + pr_debug("%s: HEARTBEAT ACK with invalid timestamp received " + "for transport:%p\n", __func__, link); + return SCTP_DISPOSITION_DISCARD; } @@ -2562,7 +2562,8 @@ static sctp_disposition_t sctp_stop_t1_and_abort(struct net *net, const struct sctp_association *asoc, struct sctp_transport *transport) { - SCTP_DEBUG_PRINTK("ABORT received (INIT).\n"); + pr_debug("%s: ABORT received (INIT)\n", __func__); + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, SCTP_STATE(SCTP_STATE_CLOSED)); SCTP_INC_STATS(net, SCTP_MIB_ABORTEDS); @@ -2572,6 +2573,7 @@ static sctp_disposition_t sctp_stop_t1_and_abort(struct net *net, /* CMD_INIT_FAILED will DELETE_TCB. */ sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, SCTP_PERR(error)); + return SCTP_DISPOSITION_ABORT; } @@ -2637,8 +2639,9 @@ sctp_disposition_t sctp_sf_do_9_2_shutdown(struct net *net, ctsn = ntohl(sdh->cum_tsn_ack); if (TSN_lt(ctsn, asoc->ctsn_ack_point)) { - SCTP_DEBUG_PRINTK("ctsn %x\n", ctsn); - SCTP_DEBUG_PRINTK("ctsn_ack_point %x\n", asoc->ctsn_ack_point); + pr_debug("%s: ctsn:%x, ctsn_ack_point:%x\n", __func__, ctsn, + asoc->ctsn_ack_point); + return SCTP_DISPOSITION_DISCARD; } @@ -2721,8 +2724,9 @@ sctp_disposition_t sctp_sf_do_9_2_shut_ctsn(struct net *net, ctsn = ntohl(sdh->cum_tsn_ack); if (TSN_lt(ctsn, asoc->ctsn_ack_point)) { - SCTP_DEBUG_PRINTK("ctsn %x\n", ctsn); - SCTP_DEBUG_PRINTK("ctsn_ack_point %x\n", asoc->ctsn_ack_point); + pr_debug("%s: ctsn:%x, ctsn_ack_point:%x\n", __func__, ctsn, + asoc->ctsn_ack_point); + return SCTP_DISPOSITION_DISCARD; } @@ -3174,8 +3178,9 @@ sctp_disposition_t sctp_sf_eat_sack_6_2(struct net *net, * Point indicates an out-of-order SACK. */ if (TSN_lt(ctsn, asoc->ctsn_ack_point)) { - SCTP_DEBUG_PRINTK("ctsn %x\n", ctsn); - SCTP_DEBUG_PRINTK("ctsn_ack_point %x\n", asoc->ctsn_ack_point); + pr_debug("%s: ctsn:%x, ctsn_ack_point:%x\n", __func__, ctsn, + asoc->ctsn_ack_point); + return SCTP_DISPOSITION_DISCARD; } @@ -3859,7 +3864,7 @@ sctp_disposition_t sctp_sf_eat_fwd_tsn(struct net *net, skb_pull(chunk->skb, len); tsn = ntohl(fwdtsn_hdr->new_cum_tsn); - SCTP_DEBUG_PRINTK("%s: TSN 0x%x.\n", __func__, tsn); + pr_debug("%s: TSN 0x%x\n", __func__, tsn); /* The TSN is too high--silently discard the chunk and count on it * getting retransmitted later. @@ -3927,7 +3932,7 @@ sctp_disposition_t sctp_sf_eat_fwd_tsn_fast( skb_pull(chunk->skb, len); tsn = ntohl(fwdtsn_hdr->new_cum_tsn); - SCTP_DEBUG_PRINTK("%s: TSN 0x%x.\n", __func__, tsn); + pr_debug("%s: TSN 0x%x\n", __func__, tsn); /* The TSN is too high--silently discard the chunk and count on it * getting retransmitted later. @@ -4166,7 +4171,7 @@ sctp_disposition_t sctp_sf_unk_chunk(struct net *net, struct sctp_chunk *err_chunk; sctp_chunkhdr_t *hdr; - SCTP_DEBUG_PRINTK("Processing the unknown chunk id %d.\n", type.chunk); + pr_debug("%s: processing unknown chunk id:%d\n", __func__, type.chunk); if (!sctp_vtag_verify(unk_chunk, asoc)) return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); @@ -4256,7 +4261,8 @@ sctp_disposition_t sctp_sf_discard_chunk(struct net *net, return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); - SCTP_DEBUG_PRINTK("Chunk %d is discarded\n", type.chunk); + pr_debug("%s: chunk:%d is discarded\n", __func__, type.chunk); + return SCTP_DISPOSITION_DISCARD; } @@ -5184,7 +5190,9 @@ sctp_disposition_t sctp_sf_ignore_primitive( void *arg, sctp_cmd_seq_t *commands) { - SCTP_DEBUG_PRINTK("Primitive type %d is ignored.\n", type.primitive); + pr_debug("%s: primitive type:%d is ignored\n", __func__, + type.primitive); + return SCTP_DISPOSITION_DISCARD; } @@ -5379,7 +5387,9 @@ sctp_disposition_t sctp_sf_ignore_other(struct net *net, void *arg, sctp_cmd_seq_t *commands) { - SCTP_DEBUG_PRINTK("The event other type %d is ignored\n", type.other); + pr_debug("%s: the event other type:%d is ignored\n", + __func__, type.other); + return SCTP_DISPOSITION_DISCARD; } @@ -5527,7 +5537,8 @@ sctp_disposition_t sctp_sf_t1_init_timer_expire(struct net *net, struct sctp_bind_addr *bp; int attempts = asoc->init_err_counter + 1; - SCTP_DEBUG_PRINTK("Timer T1 expired (INIT).\n"); + pr_debug("%s: timer T1 expired (INIT)\n", __func__); + SCTP_INC_STATS(net, SCTP_MIB_T1_INIT_EXPIREDS); if (attempts <= asoc->max_init_attempts) { @@ -5546,9 +5557,10 @@ sctp_disposition_t sctp_sf_t1_init_timer_expire(struct net *net, sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl)); } else { - SCTP_DEBUG_PRINTK("Giving up on INIT, attempts: %d" - " max_init_attempts: %d\n", - attempts, asoc->max_init_attempts); + pr_debug("%s: giving up on INIT, attempts:%d " + "max_init_attempts:%d\n", __func__, attempts, + asoc->max_init_attempts); + sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, SCTP_ERROR(ETIMEDOUT)); sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, @@ -5588,7 +5600,8 @@ sctp_disposition_t sctp_sf_t1_cookie_timer_expire(struct net *net, struct sctp_chunk *repl = NULL; int attempts = asoc->init_err_counter + 1; - SCTP_DEBUG_PRINTK("Timer T1 expired (COOKIE-ECHO).\n"); + pr_debug("%s: timer T1 expired (COOKIE-ECHO)\n", __func__); + SCTP_INC_STATS(net, SCTP_MIB_T1_COOKIE_EXPIREDS); if (attempts <= asoc->max_init_attempts) { @@ -5636,7 +5649,8 @@ sctp_disposition_t sctp_sf_t2_timer_expire(struct net *net, { struct sctp_chunk *reply = NULL; - SCTP_DEBUG_PRINTK("Timer T2 expired.\n"); + pr_debug("%s: timer T2 expired\n", __func__); + SCTP_INC_STATS(net, SCTP_MIB_T2_SHUTDOWN_EXPIREDS); ((struct sctp_association *)asoc)->shutdown_retries++; @@ -5777,7 +5791,8 @@ sctp_disposition_t sctp_sf_t5_timer_expire(struct net *net, { struct sctp_chunk *reply = NULL; - SCTP_DEBUG_PRINTK("Timer T5 expired.\n"); + pr_debug("%s: timer T5 expired\n", __func__); + SCTP_INC_STATS(net, SCTP_MIB_T5_SHUTDOWN_GUARD_EXPIREDS); reply = sctp_make_abort(asoc, NULL, 0); @@ -5892,7 +5907,8 @@ sctp_disposition_t sctp_sf_timer_ignore(struct net *net, void *arg, sctp_cmd_seq_t *commands) { - SCTP_DEBUG_PRINTK("Timer %d ignored.\n", type.chunk); + pr_debug("%s: timer %d ignored\n", __func__, type.chunk); + return SCTP_DISPOSITION_CONSUME; } @@ -6102,7 +6118,7 @@ static int sctp_eat_data(const struct sctp_association *asoc, skb_pull(chunk->skb, sizeof(sctp_datahdr_t)); tsn = ntohl(data_hdr->tsn); - SCTP_DEBUG_PRINTK("eat_data: TSN 0x%x.\n", tsn); + pr_debug("%s: TSN 0x%x\n", __func__, tsn); /* ASSERT: Now skb->data is really the user data. */ @@ -6179,12 +6195,12 @@ static int sctp_eat_data(const struct sctp_association *asoc, */ if (sctp_tsnmap_has_gap(map) && (sctp_tsnmap_get_ctsn(map) + 1) == tsn) { - SCTP_DEBUG_PRINTK("Reneging for tsn:%u\n", tsn); + pr_debug("%s: reneging for tsn:%u\n", __func__, tsn); deliver = SCTP_CMD_RENEGE; } else { - SCTP_DEBUG_PRINTK("Discard tsn: %u len: %Zd, " - "rwnd: %d\n", tsn, datalen, - asoc->rwnd); + pr_debug("%s: discard tsn:%u len:%zu, rwnd:%d\n", + __func__, tsn, datalen, asoc->rwnd); + return SCTP_IERROR_IGNORE_TSN; } } @@ -6199,7 +6215,8 @@ static int sctp_eat_data(const struct sctp_association *asoc, if (*sk->sk_prot_creator->memory_pressure) { if (sctp_tsnmap_has_gap(map) && (sctp_tsnmap_get_ctsn(map) + 1) == tsn) { - SCTP_DEBUG_PRINTK("Under Pressure! Reneging for tsn:%u\n", tsn); + pr_debug("%s: under pressure, reneging for tsn:%u\n", + __func__, tsn); deliver = SCTP_CMD_RENEGE; } } diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 66fcdcfe1b74..d5c6a2870473 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -281,8 +281,8 @@ static int sctp_bind(struct sock *sk, struct sockaddr *addr, int addr_len) sctp_lock_sock(sk); - SCTP_DEBUG_PRINTK("sctp_bind(sk: %p, addr: %p, addr_len: %d)\n", - sk, addr, addr_len); + pr_debug("%s: sk:%p, addr:%p, addr_len:%d\n", __func__, sk, + addr, addr_len); /* Disallow binding twice. */ if (!sctp_sk(sk)->ep->base.bind_addr.port) @@ -342,19 +342,15 @@ static int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len) /* Common sockaddr verification. */ af = sctp_sockaddr_af(sp, addr, len); if (!af) { - SCTP_DEBUG_PRINTK("sctp_do_bind(sk: %p, newaddr: %p, len: %d) EINVAL\n", - sk, addr, len); + pr_debug("%s: sk:%p, newaddr:%p, len:%d EINVAL\n", + __func__, sk, addr, len); return -EINVAL; } snum = ntohs(addr->v4.sin_port); - SCTP_DEBUG_PRINTK_IPADDR("sctp_do_bind(sk: %p, new addr: ", - ", port: %d, new port: %d, len: %d)\n", - sk, - addr, - bp->port, snum, - len); + pr_debug("%s: sk:%p, new addr:%pISc, port:%d, new port:%d, len:%d\n", + __func__, sk, &addr->sa, bp->port, snum, len); /* PF specific bind() address verification. */ if (!sp->pf->bind_verify(sp, addr)) @@ -368,9 +364,8 @@ static int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len) if (!snum) snum = bp->port; else if (snum != bp->port) { - SCTP_DEBUG_PRINTK("sctp_do_bind:" - " New port %d does not match existing port " - "%d.\n", snum, bp->port); + pr_debug("%s: new port %d doesn't match existing port " + "%d\n", __func__, snum, bp->port); return -EINVAL; } } @@ -468,8 +463,8 @@ static int sctp_bindx_add(struct sock *sk, struct sockaddr *addrs, int addrcnt) struct sockaddr *sa_addr; struct sctp_af *af; - SCTP_DEBUG_PRINTK("sctp_bindx_add (sk: %p, addrs: %p, addrcnt: %d)\n", - sk, addrs, addrcnt); + pr_debug("%s: sk:%p, addrs:%p, addrcnt:%d\n", __func__, sk, + addrs, addrcnt); addr_buf = addrs; for (cnt = 0; cnt < addrcnt; cnt++) { @@ -535,11 +530,10 @@ static int sctp_send_asconf_add_ip(struct sock *sk, sp = sctp_sk(sk); ep = sp->ep; - SCTP_DEBUG_PRINTK("%s: (sk: %p, addrs: %p, addrcnt: %d)\n", - __func__, sk, addrs, addrcnt); + pr_debug("%s: sk:%p, addrs:%p, addrcnt:%d\n", + __func__, sk, addrs, addrcnt); list_for_each_entry(asoc, &ep->asocs, asocs) { - if (!asoc->peer.asconf_capable) continue; @@ -646,8 +640,8 @@ static int sctp_bindx_rem(struct sock *sk, struct sockaddr *addrs, int addrcnt) union sctp_addr *sa_addr; struct sctp_af *af; - SCTP_DEBUG_PRINTK("sctp_bindx_rem (sk: %p, addrs: %p, addrcnt: %d)\n", - sk, addrs, addrcnt); + pr_debug("%s: sk:%p, addrs:%p, addrcnt:%d\n", + __func__, sk, addrs, addrcnt); addr_buf = addrs; for (cnt = 0; cnt < addrcnt; cnt++) { @@ -740,8 +734,8 @@ static int sctp_send_asconf_del_ip(struct sock *sk, sp = sctp_sk(sk); ep = sp->ep; - SCTP_DEBUG_PRINTK("%s: (sk: %p, addrs: %p, addrcnt: %d)\n", - __func__, sk, addrs, addrcnt); + pr_debug("%s: sk:%p, addrs:%p, addrcnt:%d\n", + __func__, sk, addrs, addrcnt); list_for_each_entry(asoc, &ep->asocs, asocs) { @@ -808,9 +802,11 @@ static int sctp_send_asconf_del_ip(struct sock *sk, sin6 = (struct sockaddr_in6 *)addrs; asoc->asconf_addr_del_pending->v6.sin6_addr = sin6->sin6_addr; } - SCTP_DEBUG_PRINTK_IPADDR("send_asconf_del_ip: keep the last address asoc: %p ", - " at %p\n", asoc, asoc->asconf_addr_del_pending, - asoc->asconf_addr_del_pending); + + pr_debug("%s: keep the last address asoc:%p %pISc at %p\n", + __func__, asoc, &asoc->asconf_addr_del_pending->sa, + asoc->asconf_addr_del_pending); + asoc->src_out_of_asoc_ok = 1; stored = 1; goto skip_mkasconf; @@ -972,8 +968,8 @@ static int sctp_setsockopt_bindx(struct sock* sk, void *addr_buf; struct sctp_af *af; - SCTP_DEBUG_PRINTK("sctp_setsockopt_bindx: sk %p addrs %p" - " addrs_size %d opt %d\n", sk, addrs, addrs_size, op); + pr_debug("%s: sk:%p addrs:%p addrs_size:%d opt:%d\n", + __func__, sk, addrs, addrs_size, op); if (unlikely(addrs_size <= 0)) return -EINVAL; @@ -1231,10 +1227,9 @@ static int __sctp_connect(struct sock* sk, asoc = NULL; out_free: + pr_debug("%s: took out_free path with asoc:%p kaddrs:%p err:%d\n", + __func__, asoc, kaddrs, err); - SCTP_DEBUG_PRINTK("About to exit __sctp_connect() free asoc: %p" - " kaddrs: %p err: %d\n", - asoc, kaddrs, err); if (asoc) { /* sctp_primitive_ASSOCIATE may have added this association * To the hash table, try to unhash it, just in case, its a noop @@ -1316,8 +1311,8 @@ static int __sctp_setsockopt_connectx(struct sock* sk, int err = 0; struct sockaddr *kaddrs; - SCTP_DEBUG_PRINTK("%s - sk %p addrs %p addrs_size %d\n", - __func__, sk, addrs, addrs_size); + pr_debug("%s: sk:%p addrs:%p addrs_size:%d\n", + __func__, sk, addrs, addrs_size); if (unlikely(addrs_size <= 0)) return -EINVAL; @@ -1468,7 +1463,7 @@ static void sctp_close(struct sock *sk, long timeout) struct list_head *pos, *temp; unsigned int data_was_unread; - SCTP_DEBUG_PRINTK("sctp_close(sk: 0x%p, timeout:%ld)\n", sk, timeout); + pr_debug("%s: sk:%p, timeout:%ld\n", __func__, sk, timeout); sctp_lock_sock(sk); sk->sk_shutdown = SHUTDOWN_MASK; @@ -1594,14 +1589,12 @@ static int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, struct sctp_datamsg *datamsg; int msg_flags = msg->msg_flags; - SCTP_DEBUG_PRINTK("sctp_sendmsg(sk: %p, msg: %p, msg_len: %zu)\n", - sk, msg, msg_len); - err = 0; sp = sctp_sk(sk); ep = sp->ep; - SCTP_DEBUG_PRINTK("Using endpoint: %p.\n", ep); + pr_debug("%s: sk:%p, msg:%p, msg_len:%zu ep:%p\n", __func__, sk, + msg, msg_len, ep); /* We cannot send a message over a TCP-style listening socket. */ if (sctp_style(sk, TCP) && sctp_sstate(sk, LISTENING)) { @@ -1611,9 +1604,8 @@ static int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, /* Parse out the SCTP CMSGs. */ err = sctp_msghdr_parse(msg, &cmsgs); - if (err) { - SCTP_DEBUG_PRINTK("msghdr parse err = %x\n", err); + pr_debug("%s: msghdr parse err:%x\n", __func__, err); goto out_nounlock; } @@ -1645,8 +1637,8 @@ static int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, associd = sinfo->sinfo_assoc_id; } - SCTP_DEBUG_PRINTK("msg_len: %zu, sinfo_flags: 0x%x\n", - msg_len, sinfo_flags); + pr_debug("%s: msg_len:%zu, sinfo_flags:0x%x\n", __func__, + msg_len, sinfo_flags); /* SCTP_EOF or SCTP_ABORT cannot be set on a TCP-style socket. */ if (sctp_style(sk, TCP) && (sinfo_flags & (SCTP_EOF | SCTP_ABORT))) { @@ -1675,7 +1667,7 @@ static int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, transport = NULL; - SCTP_DEBUG_PRINTK("About to look up association.\n"); + pr_debug("%s: about to look up association\n", __func__); sctp_lock_sock(sk); @@ -1705,7 +1697,7 @@ static int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, } if (asoc) { - SCTP_DEBUG_PRINTK("Just looked up association: %p.\n", asoc); + pr_debug("%s: just looked up association:%p\n", __func__, asoc); /* We cannot send a message on a TCP-style SCTP_SS_ESTABLISHED * socket that has an association in CLOSED state. This can @@ -1718,8 +1710,9 @@ static int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, } if (sinfo_flags & SCTP_EOF) { - SCTP_DEBUG_PRINTK("Shutting down association: %p\n", - asoc); + pr_debug("%s: shutting down association:%p\n", + __func__, asoc); + sctp_primitive_SHUTDOWN(net, asoc, NULL); err = 0; goto out_unlock; @@ -1732,7 +1725,9 @@ static int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, goto out_unlock; } - SCTP_DEBUG_PRINTK("Aborting association: %p\n", asoc); + pr_debug("%s: aborting association:%p\n", + __func__, asoc); + sctp_primitive_ABORT(net, asoc, chunk); err = 0; goto out_unlock; @@ -1741,7 +1736,7 @@ static int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, /* Do we need to create the association? */ if (!asoc) { - SCTP_DEBUG_PRINTK("There is no association yet.\n"); + pr_debug("%s: there is no association yet\n", __func__); if (sinfo_flags & (SCTP_EOF | SCTP_ABORT)) { err = -EINVAL; @@ -1840,7 +1835,7 @@ static int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, } /* ASSERT: we have a valid association at this point. */ - SCTP_DEBUG_PRINTK("We have a valid association.\n"); + pr_debug("%s: we have a valid association\n", __func__); if (!sinfo) { /* If the user didn't specify SNDRCVINFO, make up one with @@ -1909,7 +1904,8 @@ static int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, err = sctp_primitive_ASSOCIATE(net, asoc, NULL); if (err < 0) goto out_free; - SCTP_DEBUG_PRINTK("We associated primitively.\n"); + + pr_debug("%s: we associated primitively\n", __func__); } /* Break the message into multiple chunks of maximum size. */ @@ -1936,17 +1932,15 @@ static int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, */ err = sctp_primitive_SEND(net, asoc, datamsg); /* Did the lower layer accept the chunk? */ - if (err) + if (err) { sctp_datamsg_free(datamsg); - else - sctp_datamsg_put(datamsg); + goto out_free; + } - SCTP_DEBUG_PRINTK("We sent primitively.\n"); + pr_debug("%s: we sent primitively\n", __func__); - if (err) - goto out_free; - else - err = msg_len; + sctp_datamsg_put(datamsg); + err = msg_len; /* If we are already past ASSOCIATE, the lower * layers are responsible for association cleanup. @@ -2041,10 +2035,9 @@ static int sctp_recvmsg(struct kiocb *iocb, struct sock *sk, int err = 0; int skb_len; - SCTP_DEBUG_PRINTK("sctp_recvmsg(%s: %p, %s: %p, %s: %zd, %s: %d, %s: " - "0x%x, %s: %p)\n", "sk", sk, "msghdr", msg, - "len", len, "knoblauch", noblock, - "flags", flags, "addr_len", addr_len); + pr_debug("%s: sk:%p, msghdr:%p, len:%zd, noblock:%d, flags:0x%x, " + "addr_len:%p)\n", __func__, sk, msg, len, noblock, flags, + addr_len); sctp_lock_sock(sk); @@ -3086,7 +3079,7 @@ static int sctp_setsockopt_peer_primary_addr(struct sock *sk, char __user *optva err = sctp_send_asconf(asoc, chunk); - SCTP_DEBUG_PRINTK("We set peer primary addr primitively.\n"); + pr_debug("%s: we set peer primary addr primitively\n", __func__); return err; } @@ -3561,8 +3554,7 @@ static int sctp_setsockopt(struct sock *sk, int level, int optname, { int retval = 0; - SCTP_DEBUG_PRINTK("sctp_setsockopt(sk: %p... optname: %d)\n", - sk, optname); + pr_debug("%s: sk:%p, optname:%d\n", __func__, sk, optname); /* I can hardly begin to describe how wrong this is. This is * so broken as to be worse than useless. The API draft @@ -3724,8 +3716,8 @@ static int sctp_connect(struct sock *sk, struct sockaddr *addr, sctp_lock_sock(sk); - SCTP_DEBUG_PRINTK("%s - sk: %p, sockaddr: %p, addr_len: %d\n", - __func__, sk, addr, addr_len); + pr_debug("%s: sk:%p, sockaddr:%p, addr_len:%d\n", __func__, sk, + addr, addr_len); /* Validate addr_len before calling common connect/connectx routine. */ af = sctp_get_af_specific(addr->sa_family); @@ -3855,7 +3847,7 @@ static int sctp_init_sock(struct sock *sk) struct net *net = sock_net(sk); struct sctp_sock *sp; - SCTP_DEBUG_PRINTK("sctp_init_sock(sk: %p)\n", sk); + pr_debug("%s: sk:%p\n", __func__, sk); sp = sctp_sk(sk); @@ -3990,7 +3982,7 @@ static void sctp_destroy_sock(struct sock *sk) { struct sctp_sock *sp; - SCTP_DEBUG_PRINTK("sctp_destroy_sock(sk: %p)\n", sk); + pr_debug("%s: sk:%p\n", __func__, sk); /* Release our hold on the endpoint. */ sp = sctp_sk(sk); @@ -4123,9 +4115,9 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, goto out; } - SCTP_DEBUG_PRINTK("sctp_getsockopt_sctp_status(%d): %d %d %d\n", - len, status.sstat_state, status.sstat_rwnd, - status.sstat_assoc_id); + pr_debug("%s: len:%d, state:%d, rwnd:%d, assoc_id:%d\n", + __func__, len, status.sstat_state, status.sstat_rwnd, + status.sstat_assoc_id); if (copy_to_user(optval, &status, len)) { retval = -EFAULT; @@ -4333,8 +4325,8 @@ static int sctp_getsockopt_peeloff(struct sock *sk, int len, char __user *optval return PTR_ERR(newfile); } - SCTP_DEBUG_PRINTK("%s: sk: %p newsk: %p sd: %d\n", - __func__, sk, newsock->sk, retval); + pr_debug("%s: sk:%p, newsk:%p, sd:%d\n", __func__, sk, newsock->sk, + retval); /* Return the fd mapped to the new socket. */ if (put_user(len, optlen)) { @@ -4467,7 +4459,7 @@ static int sctp_getsockopt_peer_addr_params(struct sock *sk, int len, trans = sctp_addr_id2transport(sk, ¶ms.spp_address, params.spp_assoc_id); if (!trans) { - SCTP_DEBUG_PRINTK("Failed no transport\n"); + pr_debug("%s: failed no transport\n", __func__); return -EINVAL; } } @@ -4478,7 +4470,7 @@ static int sctp_getsockopt_peer_addr_params(struct sock *sk, int len, */ asoc = sctp_id2assoc(sk, params.spp_assoc_id); if (!asoc && params.spp_assoc_id && sctp_style(sk, UDP)) { - SCTP_DEBUG_PRINTK("Failed no association\n"); + pr_debug("%s: failed no association\n", __func__); return -EINVAL; } @@ -5698,8 +5690,7 @@ static int sctp_getsockopt_assoc_stats(struct sock *sk, int len, if (put_user(len, optlen)) return -EFAULT; - SCTP_DEBUG_PRINTK("sctp_getsockopt_assoc_stat(%d): %d\n", - len, sas.sas_assoc_id); + pr_debug("%s: len:%d, assoc_id:%d\n", __func__, len, sas.sas_assoc_id); if (copy_to_user(optval, &sas, len)) return -EFAULT; @@ -5713,8 +5704,7 @@ static int sctp_getsockopt(struct sock *sk, int level, int optname, int retval = 0; int len; - SCTP_DEBUG_PRINTK("sctp_getsockopt(sk: %p... optname: %d)\n", - sk, optname); + pr_debug("%s: sk:%p, optname:%d\n", __func__, sk, optname); /* I can hardly begin to describe how wrong this is. This is * so broken as to be worse than useless. The API draft @@ -5894,7 +5884,8 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr) snum = ntohs(addr->v4.sin_port); - SCTP_DEBUG_PRINTK("sctp_get_port() begins, snum=%d\n", snum); + pr_debug("%s: begins, snum:%d\n", __func__, snum); + sctp_local_bh_disable(); if (snum == 0) { @@ -5960,7 +5951,8 @@ pp_found: int reuse = sk->sk_reuse; struct sock *sk2; - SCTP_DEBUG_PRINTK("sctp_get_port() found a possible match\n"); + pr_debug("%s: found a possible match\n", __func__); + if (pp->fastreuse && sk->sk_reuse && sk->sk_state != SCTP_SS_LISTENING) goto success; @@ -5990,7 +5982,8 @@ pp_found: goto fail_unlock; } } - SCTP_DEBUG_PRINTK("sctp_get_port(): Found a match\n"); + + pr_debug("%s: found a match\n", __func__); } pp_not_found: /* If there was a hash table miss, create a new port. */ @@ -6479,8 +6472,8 @@ static struct sk_buff *sctp_skb_recv_datagram(struct sock *sk, int flags, timeo = sock_rcvtimeo(sk, noblock); - SCTP_DEBUG_PRINTK("Timeout: timeo: %ld, MAX: %ld.\n", - timeo, MAX_SCHEDULE_TIMEOUT); + pr_debug("%s: timeo:%ld, max:%ld\n", __func__, timeo, + MAX_SCHEDULE_TIMEOUT); do { /* Again only user level code calls this function, @@ -6611,8 +6604,8 @@ static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p, long current_timeo = *timeo_p; DEFINE_WAIT(wait); - SCTP_DEBUG_PRINTK("wait_for_sndbuf: asoc=%p, timeo=%ld, msg_len=%zu\n", - asoc, (long)(*timeo_p), msg_len); + pr_debug("%s: asoc:%p, timeo:%ld, msg_len:%zu\n", __func__, asoc, + *timeo_p, msg_len); /* Increment the association's refcnt. */ sctp_association_hold(asoc); @@ -6718,8 +6711,7 @@ static int sctp_wait_for_connect(struct sctp_association *asoc, long *timeo_p) long current_timeo = *timeo_p; DEFINE_WAIT(wait); - SCTP_DEBUG_PRINTK("%s: asoc=%p, timeo=%ld\n", __func__, asoc, - (long)(*timeo_p)); + pr_debug("%s: asoc:%p, timeo:%ld\n", __func__, asoc, *timeo_p); /* Increment the association's refcnt. */ sctp_association_hold(asoc); diff --git a/net/sctp/transport.c b/net/sctp/transport.c index 5d3c71bbd197..bdbbc3fd7c14 100644 --- a/net/sctp/transport.c +++ b/net/sctp/transport.c @@ -176,7 +176,10 @@ static void sctp_transport_destroy_rcu(struct rcu_head *head) */ static void sctp_transport_destroy(struct sctp_transport *transport) { - SCTP_ASSERT(transport->dead, "Transport is not dead", return); + if (unlikely(!transport->dead)) { + WARN(1, "Attempt to destroy undead transport %p!\n", transport); + return; + } call_rcu(&transport->rcu, sctp_transport_destroy_rcu); @@ -317,11 +320,9 @@ void sctp_transport_put(struct sctp_transport *transport) /* Update transport's RTO based on the newly calculated RTT. */ void sctp_transport_update_rto(struct sctp_transport *tp, __u32 rtt) { - /* Check for valid transport. */ - SCTP_ASSERT(tp, "NULL transport", return); - - /* We should not be doing any RTO updates unless rto_pending is set. */ - SCTP_ASSERT(tp->rto_pending, "rto_pending not set", return); + if (unlikely(!tp->rto_pending)) + /* We should not be doing any RTO updates unless rto_pending is set. */ + pr_debug("%s: rto_pending not set on transport %p!\n", __func__, tp); if (tp->rttvar || tp->srtt) { struct net *net = sock_net(tp->asoc->base.sk); @@ -377,9 +378,8 @@ void sctp_transport_update_rto(struct sctp_transport *tp, __u32 rtt) */ tp->rto_pending = 0; - SCTP_DEBUG_PRINTK("%s: transport: %p, rtt: %d, srtt: %d " - "rttvar: %d, rto: %ld\n", __func__, - tp, rtt, tp->srtt, tp->rttvar, tp->rto); + pr_debug("%s: transport:%p, rtt:%d, srtt:%d rttvar:%d, rto:%ld\n", + __func__, tp, rtt, tp->srtt, tp->rttvar, tp->rto); } /* This routine updates the transport's cwnd and partial_bytes_acked @@ -433,12 +433,11 @@ void sctp_transport_raise_cwnd(struct sctp_transport *transport, cwnd += pmtu; else cwnd += bytes_acked; - SCTP_DEBUG_PRINTK("%s: SLOW START: transport: %p, " - "bytes_acked: %d, cwnd: %d, ssthresh: %d, " - "flight_size: %d, pba: %d\n", - __func__, - transport, bytes_acked, cwnd, - ssthresh, flight_size, pba); + + pr_debug("%s: slow start: transport:%p, bytes_acked:%d, " + "cwnd:%d, ssthresh:%d, flight_size:%d, pba:%d\n", + __func__, transport, bytes_acked, cwnd, ssthresh, + flight_size, pba); } else { /* RFC 2960 7.2.2 Whenever cwnd is greater than ssthresh, * upon each SACK arrival that advances the Cumulative TSN Ack @@ -459,12 +458,12 @@ void sctp_transport_raise_cwnd(struct sctp_transport *transport, cwnd += pmtu; pba = ((cwnd < pba) ? (pba - cwnd) : 0); } - SCTP_DEBUG_PRINTK("%s: CONGESTION AVOIDANCE: " - "transport: %p, bytes_acked: %d, cwnd: %d, " - "ssthresh: %d, flight_size: %d, pba: %d\n", - __func__, - transport, bytes_acked, cwnd, - ssthresh, flight_size, pba); + + pr_debug("%s: congestion avoidance: transport:%p, " + "bytes_acked:%d, cwnd:%d, ssthresh:%d, " + "flight_size:%d, pba:%d\n", __func__, + transport, bytes_acked, cwnd, ssthresh, + flight_size, pba); } transport->cwnd = cwnd; @@ -558,10 +557,10 @@ void sctp_transport_lower_cwnd(struct sctp_transport *transport, } transport->partial_bytes_acked = 0; - SCTP_DEBUG_PRINTK("%s: transport: %p reason: %d cwnd: " - "%d ssthresh: %d\n", __func__, - transport, reason, - transport->cwnd, transport->ssthresh); + + pr_debug("%s: transport:%p, reason:%d, cwnd:%d, ssthresh:%d\n", + __func__, transport, reason, transport->cwnd, + transport->ssthresh); } /* Apply Max.Burst limit to the congestion window: -- cgit v1.2.3 From 6c734fb8592f6768170e48e7102cb2f0a1bb9759 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Sat, 29 Jun 2013 12:02:59 +0800 Subject: gre: fix a regression in ioctl When testing GRE tunnel, I got: # ip tunnel show get tunnel gre0 failed: Invalid argument get tunnel gre1 failed: Invalid argument This is a regression introduced by commit c54419321455631079c7d ("GRE: Refactor GRE tunneling code.") because previously we only check the parameters for SIOCADDTUNNEL and SIOCCHGTUNNEL, after that commit, the check is moved for all commands. So, just check for SIOCADDTUNNEL and SIOCCHGTUNNEL. After this patch I got: # ip tunnel show gre0: gre/ip remote any local any ttl inherit nopmtudisc gre1: gre/ip remote 192.168.122.101 local 192.168.122.45 ttl inherit Cc: Pravin B Shelar Cc: "David S. Miller" Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- net/ipv4/ip_gre.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index c326e869993a..1f6eab66f7ce 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -314,10 +314,11 @@ static int ipgre_tunnel_ioctl(struct net_device *dev, if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) return -EFAULT; - if (p.iph.version != 4 || p.iph.protocol != IPPROTO_GRE || - p.iph.ihl != 5 || (p.iph.frag_off&htons(~IP_DF)) || - ((p.i_flags|p.o_flags)&(GRE_VERSION|GRE_ROUTING))) { - return -EINVAL; + if (cmd == SIOCADDTUNNEL || cmd == SIOCCHGTUNNEL) { + if (p.iph.version != 4 || p.iph.protocol != IPPROTO_GRE || + p.iph.ihl != 5 || (p.iph.frag_off&htons(~IP_DF)) || + ((p.i_flags|p.o_flags)&(GRE_VERSION|GRE_ROUTING))) + return -EINVAL; } p.i_flags = gre_flags_to_tnl_flags(p.i_flags); p.o_flags = gre_flags_to_tnl_flags(p.o_flags); -- cgit v1.2.3 From ab6c7a0a43c2eaafa57583822b619b22637b49c7 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Sat, 29 Jun 2013 13:00:57 +0800 Subject: vti: remove duplicated code to fix a memory leak vti module allocates dev->tstats twice: in vti_fb_tunnel_init() and in vti_tunnel_init(), this lead to a memory leak of dev->tstats. Just remove the duplicated operations in vti_fb_tunnel_init(). (candidate for -stable) Cc: Stephen Hemminger Cc: Saurabh Mohan Cc: "David S. Miller" Signed-off-by: Cong Wang Acked-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/ipv4/ip_vti.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c index c118f6b576bb..17cc0ffa8c0d 100644 --- a/net/ipv4/ip_vti.c +++ b/net/ipv4/ip_vti.c @@ -606,17 +606,10 @@ static int __net_init vti_fb_tunnel_init(struct net_device *dev) struct iphdr *iph = &tunnel->parms.iph; struct vti_net *ipn = net_generic(dev_net(dev), vti_net_id); - tunnel->dev = dev; - strcpy(tunnel->parms.name, dev->name); - iph->version = 4; iph->protocol = IPPROTO_IPIP; iph->ihl = 5; - dev->tstats = alloc_percpu(struct pcpu_tstats); - if (!dev->tstats) - return -ENOMEM; - dev_hold(dev); rcu_assign_pointer(ipn->tunnels_wc[0], tunnel); return 0; -- cgit v1.2.3 From 8965779d2c0e6ab246c82a405236b1fb2adae6b2 Mon Sep 17 00:00:00 2001 From: Amerigo Wang Date: Sat, 29 Jun 2013 21:30:49 +0800 Subject: ipv6,mcast: always hold idev->lock before mca_lock dingtianhong reported the following deadlock detected by lockdep: ====================================================== [ INFO: possible circular locking dependency detected ] 3.4.24.05-0.1-default #1 Not tainted ------------------------------------------------------- ksoftirqd/0/3 is trying to acquire lock: (&ndev->lock){+.+...}, at: [] ipv6_get_lladdr+0x74/0x120 but task is already holding lock: (&mc->mca_lock){+.+...}, at: [] mld_send_report+0x40/0x150 which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #1 (&mc->mca_lock){+.+...}: [] validate_chain+0x637/0x730 [] __lock_acquire+0x2f7/0x500 [] lock_acquire+0x114/0x150 [] rt_spin_lock+0x4a/0x60 [] igmp6_group_added+0x3b/0x120 [] ipv6_mc_up+0x38/0x60 [] ipv6_find_idev+0x3d/0x80 [] addrconf_notify+0x3d5/0x4b0 [] notifier_call_chain+0x3f/0x80 [] raw_notifier_call_chain+0x11/0x20 [] call_netdevice_notifiers+0x32/0x60 [] __dev_notify_flags+0x34/0x80 [] dev_change_flags+0x40/0x70 [] do_setlink+0x237/0x8a0 [] rtnl_newlink+0x3ec/0x600 [] rtnetlink_rcv_msg+0x160/0x310 [] netlink_rcv_skb+0x89/0xb0 [] rtnetlink_rcv+0x27/0x40 [] netlink_unicast+0x140/0x180 [] netlink_sendmsg+0x33e/0x380 [] sock_sendmsg+0x112/0x130 [] __sys_sendmsg+0x44e/0x460 [] sys_sendmsg+0x44/0x70 [] system_call_fastpath+0x16/0x1b -> #0 (&ndev->lock){+.+...}: [] check_prev_add+0x3de/0x440 [] validate_chain+0x637/0x730 [] __lock_acquire+0x2f7/0x500 [] lock_acquire+0x114/0x150 [] rt_read_lock+0x42/0x60 [] ipv6_get_lladdr+0x74/0x120 [] mld_newpack+0xb6/0x160 [] add_grhead+0xab/0xc0 [] add_grec+0x3ab/0x460 [] mld_send_report+0x5a/0x150 [] igmp6_timer_handler+0x4e/0xb0 [] call_timer_fn+0xca/0x1d0 [] run_timer_softirq+0x1df/0x2e0 [] handle_pending_softirqs+0xf7/0x1f0 [] __do_softirq_common+0x7b/0xf0 [] __thread_do_softirq+0x1af/0x210 [] run_ksoftirqd+0xe1/0x1f0 [] kthread+0xae/0xc0 [] kernel_thread_helper+0x4/0x10 actually we can just hold idev->lock before taking pmc->mca_lock, and avoid taking idev->lock again when iterating idev->addr_list, since the upper callers of mld_newpack() already take read_lock_bh(&idev->lock). Reported-by: dingtianhong Cc: dingtianhong Cc: Hideaki YOSHIFUJI Cc: David S. Miller Cc: Hannes Frederic Sowa Tested-by: Ding Tianhong Tested-by: Chen Weilong Signed-off-by: Cong Wang Acked-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- include/net/addrconf.h | 3 +++ net/ipv6/addrconf.c | 4 ++-- net/ipv6/mcast.c | 18 ++++++++++-------- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/include/net/addrconf.h b/include/net/addrconf.h index f68eaf574d7e..c7b181cb47a6 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -86,6 +86,9 @@ extern int ipv6_dev_get_saddr(struct net *net, const struct in6_addr *daddr, unsigned int srcprefs, struct in6_addr *saddr); +extern int __ipv6_get_lladdr(struct inet6_dev *idev, + struct in6_addr *addr, + unsigned char banned_flags); extern int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr, unsigned char banned_flags); diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 12dd2fec045c..75fd93bdd0d3 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -1444,8 +1444,8 @@ try_nextdev: } EXPORT_SYMBOL(ipv6_dev_get_saddr); -static int __ipv6_get_lladdr(struct inet6_dev *idev, struct in6_addr *addr, - unsigned char banned_flags) +int __ipv6_get_lladdr(struct inet6_dev *idev, struct in6_addr *addr, + unsigned char banned_flags) { struct inet6_ifaddr *ifp; int err = -EADDRNOTAVAIL; diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 502c877cbf10..99cd65c715cd 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -1351,8 +1351,9 @@ static void ip6_mc_hdr(struct sock *sk, struct sk_buff *skb, hdr->daddr = *daddr; } -static struct sk_buff *mld_newpack(struct net_device *dev, int size) +static struct sk_buff *mld_newpack(struct inet6_dev *idev, int size) { + struct net_device *dev = idev->dev; struct net *net = dev_net(dev); struct sock *sk = net->ipv6.igmp_sk; struct sk_buff *skb; @@ -1377,7 +1378,7 @@ static struct sk_buff *mld_newpack(struct net_device *dev, int size) skb_reserve(skb, hlen); - if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) { + if (__ipv6_get_lladdr(idev, &addr_buf, IFA_F_TENTATIVE)) { /* : * use unspecified address as the source address * when a valid link-local address is not available. @@ -1474,7 +1475,7 @@ static struct sk_buff *add_grhead(struct sk_buff *skb, struct ifmcaddr6 *pmc, struct mld2_grec *pgr; if (!skb) - skb = mld_newpack(dev, dev->mtu); + skb = mld_newpack(pmc->idev, dev->mtu); if (!skb) return NULL; pgr = (struct mld2_grec *)skb_put(skb, sizeof(struct mld2_grec)); @@ -1494,7 +1495,8 @@ static struct sk_buff *add_grhead(struct sk_buff *skb, struct ifmcaddr6 *pmc, static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc, int type, int gdeleted, int sdeleted) { - struct net_device *dev = pmc->idev->dev; + struct inet6_dev *idev = pmc->idev; + struct net_device *dev = idev->dev; struct mld2_report *pmr; struct mld2_grec *pgr = NULL; struct ip6_sf_list *psf, *psf_next, *psf_prev, **psf_list; @@ -1523,7 +1525,7 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc, AVAILABLE(skb) < grec_size(pmc, type, gdeleted, sdeleted)) { if (skb) mld_sendpack(skb); - skb = mld_newpack(dev, dev->mtu); + skb = mld_newpack(idev, dev->mtu); } } first = 1; @@ -1550,7 +1552,7 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc, pgr->grec_nsrcs = htons(scount); if (skb) mld_sendpack(skb); - skb = mld_newpack(dev, dev->mtu); + skb = mld_newpack(idev, dev->mtu); first = 1; scount = 0; } @@ -1605,8 +1607,8 @@ static void mld_send_report(struct inet6_dev *idev, struct ifmcaddr6 *pmc) struct sk_buff *skb = NULL; int type; + read_lock_bh(&idev->lock); if (!pmc) { - read_lock_bh(&idev->lock); for (pmc=idev->mc_list; pmc; pmc=pmc->next) { if (pmc->mca_flags & MAF_NOREPORT) continue; @@ -1618,7 +1620,6 @@ static void mld_send_report(struct inet6_dev *idev, struct ifmcaddr6 *pmc) skb = add_grec(skb, pmc, type, 0, 0); spin_unlock_bh(&pmc->mca_lock); } - read_unlock_bh(&idev->lock); } else { spin_lock_bh(&pmc->mca_lock); if (pmc->mca_sfcount[MCAST_EXCLUDE]) @@ -1628,6 +1629,7 @@ static void mld_send_report(struct inet6_dev *idev, struct ifmcaddr6 *pmc) skb = add_grec(skb, pmc, type, 0, 0); spin_unlock_bh(&pmc->mca_lock); } + read_unlock_bh(&idev->lock); if (skb) mld_sendpack(skb); } -- cgit v1.2.3 From d8eb8f9963a55ccf6ebafa4bfdb9f70c17067825 Mon Sep 17 00:00:00 2001 From: Enrico Mioso Date: Sat, 29 Jun 2013 15:39:19 +0200 Subject: qmi_wwan: add ONDA MT689DC device ID (fwd) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Another QMI-speaking device by ZTE, re-branded by ONDA! I'm connected ovr this device's QMI interface right now, so I can say I tested it! :) Note: a follow-up patch was posted to the linux-usb mailing list, to prevent the option driver from binding to the device's QMI interface, making it unusable. Signed-off-by: Enrico Mioso Acked-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/qmi_wwan.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 6de110263109..cdddb396e4f8 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -523,6 +523,7 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x19d2, 0x0002, 1)}, {QMI_FIXED_INTF(0x19d2, 0x0012, 1)}, {QMI_FIXED_INTF(0x19d2, 0x0017, 3)}, + {QMI_FIXED_INTF(0x19d2, 0x0019, 3)}, /* ONDA MT689DC */ {QMI_FIXED_INTF(0x19d2, 0x0021, 4)}, {QMI_FIXED_INTF(0x19d2, 0x0025, 1)}, {QMI_FIXED_INTF(0x19d2, 0x0031, 4)}, -- cgit v1.2.3 From 15627e847e4356ebdd49e7c3f10a671819995be6 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Mon, 1 Jul 2013 00:13:27 +0100 Subject: cassini: Make missing firmware non-fatal The firmware patch for the Saturn PHY fixes a bug, but is not absolutely essential. And its licence is unclear, so it is not included in all distributions. Just log an error message and continue if it is missing or invalid. References: http://bugs.debian.org/712674 Signed-off-by: Ben Hutchings Tested-by: Jose Andres Arias Velichko (against 3.2) Signed-off-by: David S. Miller --- drivers/net/ethernet/sun/cassini.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/sun/cassini.c b/drivers/net/ethernet/sun/cassini.c index 4c682a3d0424..759441b29e53 100644 --- a/drivers/net/ethernet/sun/cassini.c +++ b/drivers/net/ethernet/sun/cassini.c @@ -808,44 +808,43 @@ static int cas_reset_mii_phy(struct cas *cp) return limit <= 0; } -static int cas_saturn_firmware_init(struct cas *cp) +static void cas_saturn_firmware_init(struct cas *cp) { const struct firmware *fw; const char fw_name[] = "sun/cassini.bin"; int err; if (PHY_NS_DP83065 != cp->phy_id) - return 0; + return; err = request_firmware(&fw, fw_name, &cp->pdev->dev); if (err) { pr_err("Failed to load firmware \"%s\"\n", fw_name); - return err; + return; } if (fw->size < 2) { pr_err("bogus length %zu in \"%s\"\n", fw->size, fw_name); - err = -EINVAL; goto out; } cp->fw_load_addr= fw->data[1] << 8 | fw->data[0]; cp->fw_size = fw->size - 2; cp->fw_data = vmalloc(cp->fw_size); - if (!cp->fw_data) { - err = -ENOMEM; + if (!cp->fw_data) goto out; - } memcpy(cp->fw_data, &fw->data[2], cp->fw_size); out: release_firmware(fw); - return err; } static void cas_saturn_firmware_load(struct cas *cp) { int i; + if (!cp->fw_data) + return; + cas_phy_powerdown(cp); /* expanded memory access mode */ @@ -5083,8 +5082,7 @@ static int cas_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (cas_check_invariants(cp)) goto err_out_iounmap; if (cp->cas_flags & CAS_FLAG_SATURN) - if (cas_saturn_firmware_init(cp)) - goto err_out_iounmap; + cas_saturn_firmware_init(cp); cp->init_block = (struct cas_init_block *) pci_alloc_consistent(pdev, sizeof(struct cas_init_block), -- cgit v1.2.3 From e02010adeeb21ef56d6b9b68c785ed1ecc832aee Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Mon, 1 Jul 2013 11:31:36 +0200 Subject: net: sctp: get rid of SCTP_DBG_TSNS entirely After having reworked the debugging framework, Neil and Vlad agreed to get rid of the leftover SCTP_DBG_TSNS code for a couple of reasons: We can use systemtap scripts to investigate these things, we now have pr_debug() helpers that make life easier, and if we really need anything else besides those tools, we will be forced to come up with something better than we have there. Therefore, get rid of this ifdef debugging code entirely for now. Signed-off-by: Daniel Borkmann CC: Vlad Yasevich CC: Neil Horman Acked-by: Neil Horman Acked-by: Vlad Yasevich Signed-off-by: David S. Miller --- net/sctp/Kconfig | 9 ----- net/sctp/outqueue.c | 105 ---------------------------------------------------- 2 files changed, 114 deletions(-) diff --git a/net/sctp/Kconfig b/net/sctp/Kconfig index d80bf1aebaed..71c1a598d9bc 100644 --- a/net/sctp/Kconfig +++ b/net/sctp/Kconfig @@ -49,15 +49,6 @@ config NET_SCTPPROBE To compile this code as a module, choose M here: the module will be called sctp_probe. -config SCTP_DBG_TSNS - bool "SCTP: Debug transactions" - help - If you say Y, this will enable transaction debugging, visible - from the kernel's dynamic debugging framework. - - If unsure, say N. However, if you are running into problems, use - this option to gather outqueue trace information. - config SCTP_DBG_OBJCNT bool "SCTP: Debug object counts" depends on PROC_FS diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index 511b3b35d609..cb80a8e060b7 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -1335,21 +1335,6 @@ static void sctp_check_transmitted(struct sctp_outq *q, int bytes_acked = 0; int migrate_bytes = 0; - /* These state variables are for coherent debug output. --xguo */ - -#ifdef CONFIG_SCTP_DBG_TSNS - __u32 dbg_ack_tsn = 0; /* An ACKed TSN range starts here... */ - __u32 dbg_last_ack_tsn = 0; /* ...and finishes here. */ - __u32 dbg_kept_tsn = 0; /* An un-ACKed range starts here... */ - __u32 dbg_last_kept_tsn = 0; /* ...and finishes here. */ - - /* 0 : The last TSN was ACKed. - * 1 : The last TSN was NOT ACKed (i.e. KEPT). - * -1: We need to initialize. - */ - int dbg_prt_state = -1; -#endif /* CONFIG_SCTP_DBG_TSNS */ - sack_ctsn = ntohl(sack->cum_tsn_ack); INIT_LIST_HEAD(&tlist); @@ -1471,49 +1456,6 @@ static void sctp_check_transmitted(struct sctp_outq *q, */ list_add_tail(lchunk, &tlist); } - -#ifdef CONFIG_SCTP_DBG_TSNS - switch (dbg_prt_state) { - case 0: /* last TSN was ACKed */ - if (dbg_last_ack_tsn + 1 == tsn) { - /* This TSN belongs to the - * current ACK range. - */ - break; - } - - if (dbg_last_ack_tsn != dbg_ack_tsn) { - /* Display the end of the - * current range. - */ - pr_cont("-%08x", dbg_last_ack_tsn); - } - - /* Start a new range. */ - pr_cont(",%08x", tsn); - dbg_ack_tsn = tsn; - break; - - case 1: /* The last TSN was NOT ACKed. */ - if (dbg_last_kept_tsn != dbg_kept_tsn) { - /* Display the end of current range. */ - pr_cont("-%08x", dbg_last_kept_tsn); - } - - pr_cont("\n"); - /* FALL THROUGH... */ - default: - /* This is the first-ever TSN we examined. */ - /* Start a new range of ACK-ed TSNs. */ - pr_debug("ACKed: %08x", tsn); - - dbg_prt_state = 0; - dbg_ack_tsn = tsn; - } - - dbg_last_ack_tsn = tsn; -#endif /* CONFIG_SCTP_DBG_TSNS */ - } else { if (tchunk->tsn_gap_acked) { pr_debug("%s: receiver reneged on data TSN:0x%x\n", @@ -1537,56 +1479,9 @@ static void sctp_check_transmitted(struct sctp_outq *q, } list_add_tail(lchunk, &tlist); - -#ifdef CONFIG_SCTP_DBG_TSNS - /* See the above comments on ACK-ed TSNs. */ - switch (dbg_prt_state) { - case 1: - if (dbg_last_kept_tsn + 1 == tsn) - break; - - if (dbg_last_kept_tsn != dbg_kept_tsn) - pr_cont("-%08x", dbg_last_kept_tsn); - - pr_cont(",%08x", tsn); - dbg_kept_tsn = tsn; - break; - - case 0: - if (dbg_last_ack_tsn != dbg_ack_tsn) - pr_cont("-%08x", dbg_last_ack_tsn); - - pr_cont("\n"); - /* FALL THROUGH... */ - default: - pr_debug("KEPT: %08x", tsn); - - dbg_prt_state = 1; - dbg_kept_tsn = tsn; - } - - dbg_last_kept_tsn = tsn; -#endif /* CONFIG_SCTP_DBG_TSNS */ } } -#ifdef CONFIG_SCTP_DBG_TSNS - /* Finish off the last range, displaying its ending TSN. */ - switch (dbg_prt_state) { - case 0: - if (dbg_last_ack_tsn != dbg_ack_tsn) - pr_cont("-%08x\n", dbg_last_ack_tsn); - else - pr_cont("\n"); - break; - case 1: - if (dbg_last_kept_tsn != dbg_kept_tsn) - pr_cont("-%08x\n", dbg_last_kept_tsn); - else - pr_cont("\n"); - break; - } -#endif /* CONFIG_SCTP_DBG_TSNS */ if (transport) { if (bytes_acked) { struct sctp_association *asoc = transport->asoc; -- cgit v1.2.3 From 058eec4116935c5640299913e1e0715e87ec622a Mon Sep 17 00:00:00 2001 From: Michal Schmidt Date: Mon, 1 Jul 2013 17:23:05 +0200 Subject: bnx2x: remove zeroing of dump data buffer There is no need to initialize the dump data with zeros. data is allocated with vzalloc, so it's already zero-filled. More importantly, the memset is harmful, because dump->len (the length requested by userspace) can be bigger than the allocated buffer (whose size is determined by asking the driver's .get_dump_flag method). Signed-off-by: Michal Schmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index b8c067d1a0f2..4f75ae854b3d 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -985,8 +985,6 @@ static int bnx2x_get_dump_data(struct net_device *dev, struct bnx2x *bp = netdev_priv(dev); struct dump_header dump_hdr = {0}; - memset(p, 0, dump->len); - /* Disable parity attentions as long as following dump may * cause false alarms by reading never written registers. We * will re-enable parity attentions right after the dump. -- cgit v1.2.3 From 5bb680d6cbe36de9d7ba12b05f845c91a8692318 Mon Sep 17 00:00:00 2001 From: Michal Schmidt Date: Mon, 1 Jul 2013 17:23:06 +0200 Subject: bnx2x: fix dump flag handling bnx2x interprets the dump flag as an index of a register preset. It is important to validate the index to avoid out of bounds memory accesses. Signed-off-by: Michal Schmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c | 3 +++ drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 2 ++ 2 files changed, 5 insertions(+) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index 4f75ae854b3d..cf5cebc358b8 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -959,6 +959,9 @@ static int bnx2x_set_dump(struct net_device *dev, struct ethtool_dump *val) struct bnx2x *bp = netdev_priv(dev); /* Use the ethtool_dump "flag" field as the dump preset index */ + if (val->flag < 1 || val->flag > DUMP_MAX_PRESETS) + return -EINVAL; + bp->dump_preset_idx = val->flag; return 0; } diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 740518bbcb5f..15a528bda87c 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -11636,6 +11636,8 @@ static int bnx2x_init_bp(struct bnx2x *bp) bp->min_msix_vec_cnt = 2; BNX2X_DEV_INFO("bp->min_msix_vec_cnt %d", bp->min_msix_vec_cnt); + bp->dump_preset_idx = 1; + return rc; } -- cgit v1.2.3 From 8cc2d927c26a677415a9d0d23b9a043107f948c3 Mon Sep 17 00:00:00 2001 From: Michal Schmidt Date: Mon, 1 Jul 2013 17:23:20 +0200 Subject: bnx2x: fill in sane dump flag information bnx2x did not set dump->version in its .get_dump_flag() method. Let's set it to BNX2X_DUMP_VERSION. It's not a particularly nice number (0x50acff01 currently), but at least it's something deterministic. dump->flag should report the value previously set using ethtool -W. Signed-off-by: Michal Schmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index cf5cebc358b8..c5f225101684 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -971,12 +971,12 @@ static int bnx2x_get_dump_flag(struct net_device *dev, { struct bnx2x *bp = netdev_priv(dev); + dump->version = BNX2X_DUMP_VERSION; + dump->flag = bp->dump_preset_idx; /* Calculate the requested preset idx length */ dump->len = bnx2x_get_preset_regs_len(dev, bp->dump_preset_idx); DP(BNX2X_MSG_ETHTOOL, "Get dump preset %d length=%d\n", bp->dump_preset_idx, dump->len); - - dump->flag = ETHTOOL_GET_DUMP_DATA; return 0; } -- cgit v1.2.3 From c590b5e2f05b5e98e614382582b7ae4cddb37599 Mon Sep 17 00:00:00 2001 From: Michal Schmidt Date: Mon, 1 Jul 2013 17:23:30 +0200 Subject: ethtool: make .get_dump_data() harder to misuse by drivers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As the patch "bnx2x: remove zeroing of dump data buffer" showed, it is too easy implement .get_dump_data incorrectly in a driver. Let's make sure drivers cannot get confused by userspace requesting a too big dump. Also WARN if the driver sets dump->len to something weird and make sure the length reported to userspace is the actual length of data copied to userspace. Signed-off-by: Michal Schmidt Reviewed-by: Ben Hutchings Signed-off-by: David S. Miller --- net/core/ethtool.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 9255bbdf81ff..ab5fa6336c84 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -1320,10 +1320,19 @@ static int ethtool_get_dump_data(struct net_device *dev, if (ret) return ret; - len = (tmp.len > dump.len) ? dump.len : tmp.len; + len = min(tmp.len, dump.len); if (!len) return -EFAULT; + /* Don't ever let the driver think there's more space available + * than it requested with .get_dump_flag(). + */ + dump.len = len; + + /* Always allocate enough space to hold the whole thing so that the + * driver does not need to check the length and bother with partial + * dumping. + */ data = vzalloc(tmp.len); if (!data) return -ENOMEM; @@ -1331,6 +1340,16 @@ static int ethtool_get_dump_data(struct net_device *dev, if (ret) goto out; + /* There are two sane possibilities: + * 1. The driver's .get_dump_data() does not touch dump.len. + * 2. Or it may set dump.len to how much it really writes, which + * should be tmp.len (or len if it can do a partial dump). + * In any case respond to userspace with the actual length of data + * it's receiving. + */ + WARN_ON(dump.len != len && dump.len != tmp.len); + dump.len = len; + if (copy_to_user(useraddr, &dump, sizeof(dump))) { ret = -EFAULT; goto out; -- cgit v1.2.3 From 37beae65fa4a683b2f17f3472708ab58d134b03c Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Mon, 1 Jul 2013 18:09:50 +0200 Subject: tools: selftests: psock_tpacket: get rid of macro wrappers The TPACKET_V3 test code consists of a lot of unecessary macro wrappers that rather obfuscate what members are accessed in what way. So get rid of them and make the code more readable. Also credit Chetan for providing tpacket_v3 example code. Furthermore, get rid of private offset usage, as we do not need it here. Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller --- tools/testing/selftests/net/psock_tpacket.c | 59 ++++++++++------------------- 1 file changed, 20 insertions(+), 39 deletions(-) diff --git a/tools/testing/selftests/net/psock_tpacket.c b/tools/testing/selftests/net/psock_tpacket.c index c41b58640a05..24adf709bd9d 100644 --- a/tools/testing/selftests/net/psock_tpacket.c +++ b/tools/testing/selftests/net/psock_tpacket.c @@ -1,6 +1,7 @@ /* * Copyright 2013 Red Hat, Inc. * Author: Daniel Borkmann + * Chetan Loke (TPACKET_V3 usage example) * * A basic test of packet socket's TPACKET_V1/TPACKET_V2/TPACKET_V3 behavior. * @@ -71,18 +72,8 @@ # define __align_tpacket(x) __attribute__((aligned(TPACKET_ALIGN(x)))) #endif -#define BLOCK_STATUS(x) ((x)->h1.block_status) -#define BLOCK_NUM_PKTS(x) ((x)->h1.num_pkts) -#define BLOCK_O2FP(x) ((x)->h1.offset_to_first_pkt) -#define BLOCK_LEN(x) ((x)->h1.blk_len) -#define BLOCK_SNUM(x) ((x)->h1.seq_num) -#define BLOCK_O2PRIV(x) ((x)->offset_to_priv) -#define BLOCK_PRIV(x) ((void *) ((uint8_t *) (x) + BLOCK_O2PRIV(x))) -#define BLOCK_HDR_LEN (ALIGN_8(sizeof(struct block_desc))) -#define ALIGN_8(x) (((x) + 8 - 1) & ~(8 - 1)) -#define BLOCK_PLUS_PRIV(sz_pri) (BLOCK_HDR_LEN + ALIGN_8((sz_pri))) - #define NUM_PACKETS 100 +#define ALIGN_8(x) (((x) + 8 - 1) & ~(8 - 1)) struct ring { struct iovec *rd; @@ -476,41 +467,30 @@ static uint64_t __v3_prev_block_seq_num = 0; void __v3_test_block_seq_num(struct block_desc *pbd) { - if (__v3_prev_block_seq_num + 1 != BLOCK_SNUM(pbd)) { + if (__v3_prev_block_seq_num + 1 != pbd->h1.seq_num) { fprintf(stderr, "\nprev_block_seq_num:%"PRIu64", expected " "seq:%"PRIu64" != actual seq:%"PRIu64"\n", __v3_prev_block_seq_num, __v3_prev_block_seq_num + 1, - (uint64_t) BLOCK_SNUM(pbd)); + (uint64_t) pbd->h1.seq_num); exit(1); } - __v3_prev_block_seq_num = BLOCK_SNUM(pbd); + __v3_prev_block_seq_num = pbd->h1.seq_num; } static void __v3_test_block_len(struct block_desc *pbd, uint32_t bytes, int block_num) { - if (BLOCK_NUM_PKTS(pbd)) { - if (bytes != BLOCK_LEN(pbd)) { - fprintf(stderr, "\nblock:%u with %upackets, expected " - "len:%u != actual len:%u\n", block_num, - BLOCK_NUM_PKTS(pbd), bytes, BLOCK_LEN(pbd)); - exit(1); - } - } else { - if (BLOCK_LEN(pbd) != BLOCK_PLUS_PRIV(13)) { - fprintf(stderr, "\nblock:%u, expected len:%lu != " - "actual len:%u\n", block_num, BLOCK_HDR_LEN, - BLOCK_LEN(pbd)); - exit(1); - } + if (pbd->h1.num_pkts && bytes != pbd->h1.blk_len) { + fprintf(stderr, "\nblock:%u with %upackets, expected " + "len:%u != actual len:%u\n", block_num, + pbd->h1.num_pkts, bytes, pbd->h1.blk_len); + exit(1); } } static void __v3_test_block_header(struct block_desc *pbd, const int block_num) { - uint32_t block_status = BLOCK_STATUS(pbd); - - if ((block_status & TP_STATUS_USER) == 0) { + if ((pbd->h1.block_status & TP_STATUS_USER) == 0) { fprintf(stderr, "\nblock %u: not in TP_STATUS_USER\n", block_num); exit(1); } @@ -520,14 +500,15 @@ static void __v3_test_block_header(struct block_desc *pbd, const int block_num) static void __v3_walk_block(struct block_desc *pbd, const int block_num) { - int num_pkts = BLOCK_NUM_PKTS(pbd), i; - unsigned long bytes = 0; - unsigned long bytes_with_padding = BLOCK_PLUS_PRIV(13); + int num_pkts = pbd->h1.num_pkts, i; + unsigned long bytes = 0, bytes_with_padding = ALIGN_8(sizeof(*pbd)); struct tpacket3_hdr *ppd; __v3_test_block_header(pbd, block_num); - ppd = (struct tpacket3_hdr *) ((uint8_t *) pbd + BLOCK_O2FP(pbd)); + ppd = (struct tpacket3_hdr *) ((uint8_t *) pbd + + pbd->h1.offset_to_first_pkt); + for (i = 0; i < num_pkts; ++i) { bytes += ppd->tp_snaplen; @@ -551,7 +532,7 @@ static void __v3_walk_block(struct block_desc *pbd, const int block_num) void __v3_flush_block(struct block_desc *pbd) { - BLOCK_STATUS(pbd) = TP_STATUS_KERNEL; + pbd->h1.block_status = TP_STATUS_KERNEL; __sync_synchronize(); } @@ -577,7 +558,7 @@ static void walk_v3_rx(int sock, struct ring *ring) while (total_packets < NUM_PACKETS * 2) { pbd = (struct block_desc *) ring->rd[block_num].iov_base; - while ((BLOCK_STATUS(pbd) & TP_STATUS_USER) == 0) + while ((pbd->h1.block_status & TP_STATUS_USER) == 0) poll(&pfd, 1, 1); __v3_walk_block(pbd, block_num); @@ -624,8 +605,8 @@ static void __v1_v2_fill(struct ring *ring, unsigned int blocks) static void __v3_fill(struct ring *ring, unsigned int blocks) { ring->req3.tp_retire_blk_tov = 64; - ring->req3.tp_sizeof_priv = 13; - ring->req3.tp_feature_req_word |= TP_FT_REQ_FILL_RXHASH; + ring->req3.tp_sizeof_priv = 0; + ring->req3.tp_feature_req_word = TP_FT_REQ_FILL_RXHASH; ring->req3.tp_block_size = getpagesize() << 2; ring->req3.tp_frame_size = TPACKET_ALIGNMENT << 7; -- cgit v1.2.3 From a05b1019cfe5250baf24e26e2e7112cf66054114 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Mon, 1 Jul 2013 18:10:36 +0200 Subject: net: sctp: prevent checksum.h from double inclusion The header file checksum.h is missing proper defines that prevents it from double inclusion. Signed-off-by: Daniel Borkmann Acked-by: Neil Horman Signed-off-by: David S. Miller --- include/net/sctp/checksum.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/net/sctp/checksum.h b/include/net/sctp/checksum.h index 5a2110d3176d..0cb08e6fb6df 100644 --- a/include/net/sctp/checksum.h +++ b/include/net/sctp/checksum.h @@ -42,6 +42,9 @@ * be incorporated into the next SCTP release. */ +#ifndef __sctp_checksum_h__ +#define __sctp_checksum_h__ + #include #include #include @@ -81,3 +84,5 @@ static inline __le32 sctp_end_cksum(__u32 crc32) { return cpu_to_le32(~crc32); } + +#endif /* __sctp_checksum_h__ */ -- cgit v1.2.3 From a7599398a312fd7b81aba0188ca4749f781f5a52 Mon Sep 17 00:00:00 2001 From: Aaron Marburg Date: Tue, 2 Jul 2013 10:00:06 +1200 Subject: net: ipheth: Add USB ID for iPad mini Adds the USB device ID (0x12ab) to the ipheth network-over-USB-tethering driver for iOS devices. Applied and tested against mainline tag v3.10 (as well as 3.8.x and 3.6.y kernel for Raspbian on Raspberry pi) Signed-off-by: Aaron Marburg Signed-off-by: David S. Miller --- drivers/net/usb/ipheth.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/usb/ipheth.c b/drivers/net/usb/ipheth.c index 534d8becbbdc..ff8594d8dd2d 100644 --- a/drivers/net/usb/ipheth.c +++ b/drivers/net/usb/ipheth.c @@ -60,6 +60,7 @@ #define USB_PRODUCT_IPHONE_3GS 0x1294 #define USB_PRODUCT_IPHONE_4 0x1297 #define USB_PRODUCT_IPAD 0x129a +#define USB_PRODUCT_IPAD_MINI 0x12ab #define USB_PRODUCT_IPHONE_4_VZW 0x129c #define USB_PRODUCT_IPHONE_4S 0x12a0 #define USB_PRODUCT_IPHONE_5 0x12a8 @@ -106,6 +107,10 @@ static struct usb_device_id ipheth_table[] = { USB_VENDOR_APPLE, USB_PRODUCT_IPAD, IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, IPHETH_USBINTF_PROTO) }, + { USB_DEVICE_AND_INTERFACE_INFO( + USB_VENDOR_APPLE, USB_PRODUCT_IPAD_MINI, + IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, + IPHETH_USBINTF_PROTO) }, { USB_DEVICE_AND_INTERFACE_INFO( USB_VENDOR_APPLE, USB_PRODUCT_IPHONE_4_VZW, IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS, -- cgit v1.2.3 From b48410b4dc9c7306df21e508cc6739a55c350eb8 Mon Sep 17 00:00:00 2001 From: Rami Rosen Date: Mon, 1 Jul 2013 22:19:45 +0300 Subject: ipv4: remove fib_update_nh_saddrs() declaration. This patch removes the fib_update_nh_saddrs() declaration from include/net/ip_fib.h, as the fib_update_nh_saddrs() method was removed in coomit 436c3b6 ("ipv4: Invalidate nexthop cache nh_saddr more correctly"). Signed-off-by: Rami Rosen Signed-off-by: David S. Miller --- include/net/ip_fib.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index aac85534d7b5..cbf2be37c91a 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -291,7 +291,6 @@ static inline int fib_num_tclassid_users(struct net *net) extern int ip_fib_check_default(__be32 gw, struct net_device *dev); extern int fib_sync_down_dev(struct net_device *dev, int force); extern int fib_sync_down_addr(struct net *net, __be32 local); -extern void fib_update_nh_saddrs(struct net_device *dev); extern int fib_sync_up(struct net_device *dev); extern void fib_select_multipath(struct fib_result *res); -- cgit v1.2.3 From 4787c368a9bca39e173d702389ee2eaf0520abc1 Mon Sep 17 00:00:00 2001 From: Seiji Aguchi Date: Fri, 28 Jun 2013 14:02:11 -0400 Subject: x86/tracing: Add irq_enter/exit() in smp_trace_reschedule_interrupt() Reschedule vector tracepoints may be called in cpu idle state. This causes lockdep check warning below. The tracepoint requires rcu but for accuracy it also requires irq_enter() (tracepoints record the irq context), thus, the tracepoint interrupt handler should be calling irq_enter() and not rcu_irq_enter() (irq_enter() calls rcu_irq_enter()). So, add irq_enter/exit() to smp_trace_reschedule_interrupt() with common pre/post processing functions, smp_entering_irq() and exiting_irq() (exiting_irq() calls just irq_exit() in arch/x86/include/asm/apic.h), because these can be shared among reschedule, call_function, and call_function_single vectors. [ 50.720557] Testing event reschedule_exit: [ 50.721349] [ 50.721502] =============================== [ 50.721835] [ INFO: suspicious RCU usage. ] [ 50.722169] 3.10.0-rc6-00004-gcf910e8 #190 Not tainted [ 50.722582] ------------------------------- [ 50.722915] /c/kernel-tests/src/linux/arch/x86/include/asm/trace/irq_vectors.h:50 suspicious rcu_dereference_check() usage! [ 50.723770] [ 50.723770] other info that might help us debug this: [ 50.723770] [ 50.724385] [ 50.724385] RCU used illegally from idle CPU! [ 50.724385] rcu_scheduler_active = 1, debug_locks = 0 [ 50.725232] RCU used illegally from extended quiescent state! [ 50.725690] no locks held by swapper/0/0. [ 50.726010] [ 50.726010] stack backtrace: [...] Signed-off-by: Seiji Aguchi Reviewed-by: Steven Rostedt Link: http://lkml.kernel.org/r/51CDCFA3.9080101@hds.com Signed-off-by: Ingo Molnar --- arch/x86/kernel/smp.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/arch/x86/kernel/smp.c b/arch/x86/kernel/smp.c index f4fe0b8879e0..cdaa347dfcad 100644 --- a/arch/x86/kernel/smp.c +++ b/arch/x86/kernel/smp.c @@ -265,23 +265,30 @@ void smp_reschedule_interrupt(struct pt_regs *regs) */ } -void smp_trace_reschedule_interrupt(struct pt_regs *regs) +static inline void smp_entering_irq(void) { ack_APIC_irq(); + irq_enter(); +} + +void smp_trace_reschedule_interrupt(struct pt_regs *regs) +{ + /* + * Need to call irq_enter() before calling the trace point. + * __smp_reschedule_interrupt() calls irq_enter/exit() too (in + * scheduler_ipi(). This is OK, since those functions are allowed + * to nest. + */ + smp_entering_irq(); trace_reschedule_entry(RESCHEDULE_VECTOR); __smp_reschedule_interrupt(); trace_reschedule_exit(RESCHEDULE_VECTOR); + exiting_irq(); /* * KVM uses this interrupt to force a cpu out of guest mode */ } -static inline void call_function_entering_irq(void) -{ - ack_APIC_irq(); - irq_enter(); -} - static inline void __smp_call_function_interrupt(void) { generic_smp_call_function_interrupt(); @@ -290,14 +297,14 @@ static inline void __smp_call_function_interrupt(void) void smp_call_function_interrupt(struct pt_regs *regs) { - call_function_entering_irq(); + smp_entering_irq(); __smp_call_function_interrupt(); exiting_irq(); } void smp_trace_call_function_interrupt(struct pt_regs *regs) { - call_function_entering_irq(); + smp_entering_irq(); trace_call_function_entry(CALL_FUNCTION_VECTOR); __smp_call_function_interrupt(); trace_call_function_exit(CALL_FUNCTION_VECTOR); @@ -312,14 +319,14 @@ static inline void __smp_call_function_single_interrupt(void) void smp_call_function_single_interrupt(struct pt_regs *regs) { - call_function_entering_irq(); + smp_entering_irq(); __smp_call_function_single_interrupt(); exiting_irq(); } void smp_trace_call_function_single_interrupt(struct pt_regs *regs) { - call_function_entering_irq(); + smp_entering_irq(); trace_call_function_single_entry(CALL_FUNCTION_SINGLE_VECTOR); __smp_call_function_single_interrupt(); trace_call_function_single_exit(CALL_FUNCTION_SINGLE_VECTOR); -- cgit v1.2.3 From 8ef2c3bca599710a410cbecb4867e27728fef043 Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Tue, 2 Jul 2013 00:08:54 +0100 Subject: xen-netback: xenbus.c: use more current logging styles Convert one printk to pr_. Add a missing newline in several places to avoid message interleaving, coalesce formats, reflow modified lines to 80 columns. Signed-off-by: Joe Perches Signed-off-by: Wei Liu Signed-off-by: David S. Miller --- drivers/net/xen-netback/xenbus.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/net/xen-netback/xenbus.c b/drivers/net/xen-netback/xenbus.c index 04bd860d16a9..1fe48fe364ed 100644 --- a/drivers/net/xen-netback/xenbus.c +++ b/drivers/net/xen-netback/xenbus.c @@ -130,7 +130,7 @@ static int netback_probe(struct xenbus_device *dev, "feature-split-event-channels", "%u", separate_tx_rx_irq); if (err) - pr_debug("Error writing feature-split-event-channels"); + pr_debug("Error writing feature-split-event-channels\n"); err = xenbus_switch_state(dev, XenbusStateInitWait); if (err) @@ -145,7 +145,7 @@ abort_transaction: xenbus_transaction_end(xbt, 1); xenbus_dev_fatal(dev, err, "%s", message); fail: - pr_debug("failed"); + pr_debug("failed\n"); netback_remove(dev); return err; } @@ -228,15 +228,14 @@ static void frontend_changed(struct xenbus_device *dev, { struct backend_info *be = dev_get_drvdata(&dev->dev); - pr_debug("frontend state %s", xenbus_strstate(frontend_state)); + pr_debug("frontend state %s\n", xenbus_strstate(frontend_state)); be->frontend_state = frontend_state; switch (frontend_state) { case XenbusStateInitialising: if (dev->state == XenbusStateClosed) { - printk(KERN_INFO "%s: %s: prepare for reconnect\n", - __func__, dev->nodename); + pr_info("%s: prepare for reconnect\n", dev->nodename); xenbus_switch_state(dev, XenbusStateInitWait); } break; -- cgit v1.2.3 From f3ad89217fa8088203b6c040584987028d524456 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 2 Jul 2013 08:57:47 +0800 Subject: net: ethernet: davinci_emac: remove redundant dev_err call in davinci_emac_probe() There is a error message within devm_ioremap_resource already, so remove the dev_err call to avoid redundant error message. Signed-off-by: Wei Yongjun Acked-by: Lad, Prabhakar Acked-by: Mugunthan V N Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/davinci_emac.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c index f118d7133128..07b176bcf929 100644 --- a/drivers/net/ethernet/ti/davinci_emac.c +++ b/drivers/net/ethernet/ti/davinci_emac.c @@ -1870,7 +1870,6 @@ static int davinci_emac_probe(struct platform_device *pdev) priv->emac_base_phys = res->start + pdata->ctrl_reg_offset; priv->remap_addr = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(priv->remap_addr)) { - dev_err(&pdev->dev, "unable to map IO\n"); rc = PTR_ERR(priv->remap_addr); goto no_pdata; } -- cgit v1.2.3 From e1558a93b61962710733dc8c11a2bc765607f1cd Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 2 Jul 2013 09:02:07 +0800 Subject: l2tp: add missing .owner to struct pppox_proto Add missing .owner of struct pppox_proto. This prevents the module from being removed from underneath its users. Signed-off-by: Wei Yongjun Signed-off-by: David S. Miller --- net/l2tp/l2tp_ppp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c index 8dec6876dc50..5ebee2ded9e9 100644 --- a/net/l2tp/l2tp_ppp.c +++ b/net/l2tp/l2tp_ppp.c @@ -1793,7 +1793,8 @@ static const struct proto_ops pppol2tp_ops = { static const struct pppox_proto pppol2tp_proto = { .create = pppol2tp_create, - .ioctl = pppol2tp_ioctl + .ioctl = pppol2tp_ioctl, + .owner = THIS_MODULE, }; #ifdef CONFIG_L2TP_V3 -- cgit v1.2.3 From 3b7b514f44bff05d26a6499c4d4fac2a83938e6e Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Tue, 2 Jul 2013 14:49:34 +0800 Subject: ipip: fix a regression in ioctl This is a regression introduced by commit fd58156e456d9f68fe0448 (IPIP: Use ip-tunneling code.) Similar to GRE tunnel, previously we only check the parameters for SIOCADDTUNNEL and SIOCCHGTUNNEL, after that commit, the check is moved for all commands. So, just check for SIOCADDTUNNEL and SIOCCHGTUNNEL. Also, the check for i_key, o_key etc. is suspicious too, which did not exist before, reset them before passing to ip_tunnel_ioctl(). Cc: Pravin B Shelar Cc: "David S. Miller" Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- net/ipv4/ipip.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index e6905fbda2a2..51fc2a1dcdd3 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -244,11 +244,13 @@ ipip_tunnel_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) return -EFAULT; - if (p.iph.version != 4 || p.iph.protocol != IPPROTO_IPIP || - p.iph.ihl != 5 || (p.iph.frag_off&htons(~IP_DF))) - return -EINVAL; - if (p.i_key || p.o_key || p.i_flags || p.o_flags) - return -EINVAL; + if (cmd == SIOCADDTUNNEL || cmd == SIOCCHGTUNNEL) { + if (p.iph.version != 4 || p.iph.protocol != IPPROTO_IPIP || + p.iph.ihl != 5 || (p.iph.frag_off&htons(~IP_DF))) + return -EINVAL; + } + + p.i_key = p.o_key = p.i_flags = p.o_flags = 0; if (p.iph.ttl) p.iph.frag_off |= htons(IP_DF); -- cgit v1.2.3 From 1d8b368ab4aacfc3f864655baad4d31a3028ec1a Mon Sep 17 00:00:00 2001 From: Aruna Balakrishnaiah Date: Tue, 2 Jul 2013 11:06:54 +0530 Subject: pstore: Add hsize argument in write_buf call of pstore_ftrace_call Incorporate the addition of hsize argument in write_buf callback of pstore. This was forgotten in 6bbbca735936e15b9431882eceddcf6dff76e03c pstore: Pass header size in the pstore write callback Causing a build failure when ftrace and pstore are enabled. Signed-off-by: Aruna Balakrishnaiah Signed-off-by: Benjamin Herrenschmidt --- fs/pstore/ftrace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/pstore/ftrace.c b/fs/pstore/ftrace.c index 43b12807a51d..76a4eeb92982 100644 --- a/fs/pstore/ftrace.c +++ b/fs/pstore/ftrace.c @@ -44,7 +44,7 @@ static void notrace pstore_ftrace_call(unsigned long ip, rec.parent_ip = parent_ip; pstore_ftrace_encode_cpu(&rec, raw_smp_processor_id()); psinfo->write_buf(PSTORE_TYPE_FTRACE, 0, NULL, 0, (void *)&rec, - sizeof(rec), psinfo); + 0, sizeof(rec), psinfo); local_irq_restore(flags); } -- cgit v1.2.3 From 06efce71166b222b6715c73700f5e9b228d68999 Mon Sep 17 00:00:00 2001 From: Jim Baxter Date: Thu, 27 Jun 2013 19:25:08 +0100 Subject: net: fec: Fix Transmitted bytes counter The tx_bytes field was not being updated so the network card statistics showed 0.0B transmitted. Signed-off-by: Jim Baxter Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fec_main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 5664acd8e0f4..aff8a5cd17c7 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -739,6 +739,7 @@ fec_enet_tx(struct net_device *ndev) ndev->stats.tx_carrier_errors++; } else { ndev->stats.tx_packets++; + ndev->stats.tx_bytes += bdp->cbd_datlen; } if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS) && -- cgit v1.2.3 From 1fc4c84d49558ceca04761abd3f7eb5224af75b2 Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Sat, 29 Jun 2013 12:03:06 +0200 Subject: net: cdc_ether: allow combined control and data interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some Icera based Huawei modems handled by this driver are not completely CDC ECM compliant, using the same USB interface for both control and data. The CDC functional descriptors include a Union naming this interface as both master and slave, so it is supportable by relaxing the descriptor parsing in case these interfaces are identical. This has been tested on a Huawei K3806 and verified to add support for that device. Reported-and-tested-by: Enrico Mioso Signed-off-by: Bjørn Mork Acked-by: Oliver Neukum Signed-off-by: David S. Miller --- drivers/net/usb/cdc_ether.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c index 04ee044dde51..4393f1483126 100644 --- a/drivers/net/usb/cdc_ether.c +++ b/drivers/net/usb/cdc_ether.c @@ -215,6 +215,10 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf) goto bad_desc; } + /* some devices merge these - skip class check */ + if (info->control == info->data) + goto next_desc; + /* a data interface altsetting does the real i/o */ d = &info->data->cur_altsetting->desc; if (d->bInterfaceClass != USB_CLASS_CDC_DATA) { @@ -304,19 +308,23 @@ next_desc: /* claim data interface and set it up ... with side effects. * network traffic can't flow until an altsetting is enabled. */ - status = usb_driver_claim_interface(driver, info->data, dev); - if (status < 0) - return status; + if (info->data != info->control) { + status = usb_driver_claim_interface(driver, info->data, dev); + if (status < 0) + return status; + } status = usbnet_get_endpoints(dev, info->data); if (status < 0) { /* ensure immediate exit from usbnet_disconnect */ usb_set_intfdata(info->data, NULL); - usb_driver_release_interface(driver, info->data); + if (info->data != info->control) + usb_driver_release_interface(driver, info->data); return status; } /* status endpoint: optional for CDC Ethernet, not RNDIS (or ACM) */ - dev->status = NULL; + if (info->data != info->control) + dev->status = NULL; if (info->control->cur_altsetting->desc.bNumEndpoints == 1) { struct usb_endpoint_descriptor *desc; @@ -349,6 +357,10 @@ void usbnet_cdc_unbind(struct usbnet *dev, struct usb_interface *intf) struct cdc_state *info = (void *) &dev->data; struct usb_driver *driver = driver_of(intf); + /* combined interface - nothing to do */ + if (info->data == info->control) + return; + /* disconnect master --> disconnect slave */ if (intf == info->control && info->data) { /* ensure immediate exit from usbnet_disconnect */ -- cgit v1.2.3 From 446f8d81ca2d9cefb614e87f2fabcc996a9e4e7e Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 2 Jul 2013 10:48:31 +0200 Subject: drm/i915: Don't try to tear down the stolen drm_mm if it's not there Every other place properly checks whether we've managed to set up the stolen allocator at boot-up properly, with the exception of the cleanup code. Which results in an ugly *ERROR* Memory manager not clean. Delaying takedown at module unload time since the drm_mm isn't initialized at all. v2: While at it check whether the stolen drm_mm is initialized instead of the more obscure stolen_base == 0 check. v3: Fix up the logic. Also we need to keep the stolen_base check in i915_gem_object_create_stolen_for_preallocated since that can be called before stolen memory is fully set up. Spotted by Chris Wilson. v4: Readd the conversion in i915_gem_object_create_stolen_for_preallocated, the check is for the dev_priv->mm.gtt_space drm_mm, the stolen allocatot must already be initialized when calling that function (if we indeed have stolen memory). Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=65953 Cc: Chris Wilson Tested-by: lu hua (v3) Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_stolen.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c index f713294618fe..982d4732cecf 100644 --- a/drivers/gpu/drm/i915/i915_gem_stolen.c +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c @@ -147,7 +147,7 @@ int i915_gem_stolen_setup_compression(struct drm_device *dev, int size) { struct drm_i915_private *dev_priv = dev->dev_private; - if (dev_priv->mm.stolen_base == 0) + if (!drm_mm_initialized(&dev_priv->mm.stolen)) return -ENODEV; if (size < dev_priv->cfb_size) @@ -179,6 +179,9 @@ void i915_gem_cleanup_stolen(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + if (!drm_mm_initialized(&dev_priv->mm.stolen)) + return; + i915_gem_stolen_cleanup_compression(dev); drm_mm_takedown(&dev_priv->mm.stolen); } @@ -300,7 +303,7 @@ i915_gem_object_create_stolen(struct drm_device *dev, u32 size) struct drm_i915_gem_object *obj; struct drm_mm_node *stolen; - if (dev_priv->mm.stolen_base == 0) + if (!drm_mm_initialized(&dev_priv->mm.stolen)) return NULL; DRM_DEBUG_KMS("creating stolen object: size=%x\n", size); @@ -331,7 +334,7 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev, struct drm_i915_gem_object *obj; struct drm_mm_node *stolen; - if (dev_priv->mm.stolen_base == 0) + if (!drm_mm_initialized(&dev_priv->mm.stolen)) return NULL; DRM_DEBUG_KMS("creating preallocated stolen object: stolen_offset=%x, gtt_offset=%x, size=%x\n", -- cgit v1.2.3 From 42748752f6ada54faa4f39fa471dabce62eaa79a Mon Sep 17 00:00:00 2001 From: James Hogan Date: Fri, 28 Jun 2013 16:09:47 +0100 Subject: metag: use clear_tasks_mm_cpumask() Checking for process->mm is not enough because process' main thread may exit or detach its mm via use_mm(), but other threads may still have a valid mm. To fix this we would need to use find_lock_task_mm(), which would walk up all threads and returns an appropriate task (with task lock held). clear_tasks_mm_cpumask() was introduced in v3.5-rc1 to fix this issue, so let's use it for metag too. Signed-off-by: James Hogan Cc: "Srivatsa S. Bhat" Cc: Thomas Gleixner Cc: Oleg Nesterov Cc: Anton Vorontsov --- arch/metag/kernel/smp.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/arch/metag/kernel/smp.c b/arch/metag/kernel/smp.c index 86fdda4a85c4..b81351538c48 100644 --- a/arch/metag/kernel/smp.c +++ b/arch/metag/kernel/smp.c @@ -276,7 +276,6 @@ static DECLARE_COMPLETION(cpu_killed); int __cpuexit __cpu_disable(void) { unsigned int cpu = smp_processor_id(); - struct task_struct *p; /* * Take this CPU offline. Once we clear this, we can't return, @@ -296,12 +295,7 @@ int __cpuexit __cpu_disable(void) flush_cache_all(); local_flush_tlb_all(); - read_lock(&tasklist_lock); - for_each_process(p) { - if (p->mm) - cpumask_clear_cpu(cpu, mm_cpumask(p->mm)); - } - read_unlock(&tasklist_lock); + clear_tasks_mm_cpumask(cpu); return 0; } -- cgit v1.2.3 From 234c7f1ac109ad1af4ae410463d03ccebf3d6657 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Fri, 28 Jun 2013 13:56:02 +0100 Subject: metag: smp: enable irqs after set_cpu_online In secondary_start_kernel() interrupts should be enabled with local_irq_enable() after the cpu is marked as online with set_cpu_online(). Otherwise it's possible for a timer interrupt to trigger a softirq, which if the cpu is marked as offline may have it's affinity altered. Reported-by: Kirill Tkhai Signed-off-by: James Hogan Acked-by: Thomas Gleixner Reviewed-by: "Srivatsa S. Bhat" Cc: Kirill Tkhai --- arch/metag/kernel/smp.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/arch/metag/kernel/smp.c b/arch/metag/kernel/smp.c index b81351538c48..09979f203a4d 100644 --- a/arch/metag/kernel/smp.c +++ b/arch/metag/kernel/smp.c @@ -379,12 +379,7 @@ asmlinkage void secondary_start_kernel(void) setup_priv(); - /* - * Enable local interrupts. - */ - tbi_startup_interrupt(TBID_SIGNUM_TRT); notify_cpu_starting(cpu); - local_irq_enable(); pr_info("CPU%u (thread %u): Booted secondary processor\n", cpu, cpu_2_hwthread_id[cpu]); @@ -397,6 +392,12 @@ asmlinkage void secondary_start_kernel(void) */ set_cpu_online(cpu, true); + /* + * Enable local interrupts. + */ + tbi_startup_interrupt(TBID_SIGNUM_TRT); + local_irq_enable(); + /* * OK, it's off to the idle thread for us */ -- cgit v1.2.3 From 9649814432faa9016952895684120942da0d8481 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Fri, 28 Jun 2013 15:03:05 +0100 Subject: metag: smp: don't spin waiting for CPU to start Use a completion to block until a secondary CPU has started up, like ARM do, instead of a loop of udelays. On Meta, SMP is really SMT, with each "CPU" being a different hardware thread on the same Meta processor core, so as well as being more efficient and latency friendly, using a completion prevents the bogomips of the secondary CPU from being drastically skewed every time by the execution of the tight in-cache udelay loop on the other CPU. Signed-off-by: James Hogan Acked-by: Thomas Gleixner Reviewed-by: "Srivatsa S. Bhat" --- arch/metag/kernel/smp.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/arch/metag/kernel/smp.c b/arch/metag/kernel/smp.c index 09979f203a4d..e413875cf6d2 100644 --- a/arch/metag/kernel/smp.c +++ b/arch/metag/kernel/smp.c @@ -8,6 +8,7 @@ * published by the Free Software Foundation. */ #include +#include #include #include #include @@ -62,6 +63,8 @@ static DEFINE_PER_CPU(struct ipi_data, ipi_data) = { static DEFINE_SPINLOCK(boot_lock); +static DECLARE_COMPLETION(cpu_running); + /* * "thread" is assumed to be a valid Meta hardware thread ID. */ @@ -235,20 +238,12 @@ int __cpuinit __cpu_up(unsigned int cpu, struct task_struct *idle) */ ret = boot_secondary(thread, idle); if (ret == 0) { - unsigned long timeout; - /* * CPU was successfully started, wait for it * to come online or time out. */ - timeout = jiffies + HZ; - while (time_before(jiffies, timeout)) { - if (cpu_online(cpu)) - break; - - udelay(10); - barrier(); - } + wait_for_completion_timeout(&cpu_running, + msecs_to_jiffies(1000)); if (!cpu_online(cpu)) ret = -EIO; @@ -391,6 +386,7 @@ asmlinkage void secondary_start_kernel(void) * OK, now it's safe to let the boot CPU continue */ set_cpu_online(cpu, true); + complete(&cpu_running); /* * Enable local interrupts. -- cgit v1.2.3 From 0429ffeff460c4302bd1520e696bafe446e15181 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 2 Jul 2013 13:10:28 +0100 Subject: ASoC: samsung: Remove obsolete GPIO based DT pinmuxing Since the Samsung platforms have moved to pinctrl for pin muxing and that is handled in the core the old GPIO based muxing code can just be removed. Something similar had been submitted by Thomas Abraham back in March but a resubmission following review never happened. Signed-off-by: Mark Brown --- sound/soc/samsung/i2s.c | 66 +++---------------------------------------------- 1 file changed, 4 insertions(+), 62 deletions(-) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 82ebb1a51479..7a1734697434 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -1016,52 +1016,6 @@ static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec) return i2s; } -#ifdef CONFIG_OF -static int samsung_i2s_parse_dt_gpio(struct i2s_dai *i2s) -{ - struct device *dev = &i2s->pdev->dev; - int index, gpio, ret; - - for (index = 0; index < 7; index++) { - gpio = of_get_gpio(dev->of_node, index); - if (!gpio_is_valid(gpio)) { - dev_err(dev, "invalid gpio[%d]: %d\n", index, gpio); - goto free_gpio; - } - - ret = gpio_request(gpio, dev_name(dev)); - if (ret) { - dev_err(dev, "gpio [%d] request failed\n", gpio); - goto free_gpio; - } - i2s->gpios[index] = gpio; - } - return 0; - -free_gpio: - while (--index >= 0) - gpio_free(i2s->gpios[index]); - return -EINVAL; -} - -static void samsung_i2s_dt_gpio_free(struct i2s_dai *i2s) -{ - unsigned int index; - for (index = 0; index < 7; index++) - gpio_free(i2s->gpios[index]); -} -#else -static int samsung_i2s_parse_dt_gpio(struct i2s_dai *dai) -{ - return -EINVAL; -} - -static void samsung_i2s_dt_gpio_free(struct i2s_dai *dai) -{ -} - -#endif - static const struct of_device_id exynos_i2s_match[]; static inline int samsung_i2s_get_driver_data(struct platform_device *pdev) @@ -1235,18 +1189,10 @@ static int samsung_i2s_probe(struct platform_device *pdev) pri_dai->sec_dai = sec_dai; } - if (np) { - if (samsung_i2s_parse_dt_gpio(pri_dai)) { - dev_err(&pdev->dev, "Unable to configure gpio\n"); - ret = -EINVAL; - goto err; - } - } else { - if (i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) { - dev_err(&pdev->dev, "Unable to configure gpio\n"); - ret = -EINVAL; - goto err; - } + if (i2s_pdata && i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) { + dev_err(&pdev->dev, "Unable to configure gpio\n"); + ret = -EINVAL; + goto err; } snd_soc_register_component(&pri_dai->pdev->dev, &samsung_i2s_component, @@ -1267,14 +1213,10 @@ static int samsung_i2s_remove(struct platform_device *pdev) { struct i2s_dai *i2s, *other; struct resource *res; - struct s3c_audio_pdata *i2s_pdata = pdev->dev.platform_data; i2s = dev_get_drvdata(&pdev->dev); other = i2s->pri_dai ? : i2s->sec_dai; - if (!i2s_pdata->cfg_gpio && pdev->dev.of_node) - samsung_i2s_dt_gpio_free(i2s->pri_dai); - if (other) { other->pri_dai = NULL; other->sec_dai = NULL; -- cgit v1.2.3 From c9b5a266b103af873abb9ac03bc3d067702c8f4b Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 26 Jun 2013 12:17:32 +0200 Subject: tick: Make oneshot broadcast robust vs. CPU offlining In periodic mode we remove offline cpus from the broadcast propagation mask. In oneshot mode we fail to do so. This was not a problem so far, but the recent changes to the broadcast propagation introduced a constellation which can result in a NULL pointer dereference. What happens is: CPU0 CPU1 idle() arch_idle() tick_broadcast_oneshot_control(OFF); set cpu1 in tick_broadcast_force_mask if (cpu_offline()) arch_cpu_dead() cpu_dead_cleanup(cpu1) cpu1 tickdevice pointer = NULL broadcast interrupt dereference cpu1 tickdevice pointer -> OOPS We dereference the pointer because cpu1 is still set in tick_broadcast_force_mask and tick_do_broadcast() expects a valid cpumask and therefor lacks any further checks. Remove the cpu from the tick_broadcast_force_mask before we set the tick device pointer to NULL. Also add a sanity check to the oneshot broadcast function, so we can detect such issues w/o crashing the machine. Reported-by: Prarit Bhargava Cc: athorlton@sgi.com Cc: CAI Qian Link: http://lkml.kernel.org/r/alpine.DEB.2.02.1306261303260.4013@ionos.tec.linutronix.de Signed-off-by: Thomas Gleixner --- kernel/time/tick-broadcast.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c index d067c01586f5..4790037163f6 100644 --- a/kernel/time/tick-broadcast.c +++ b/kernel/time/tick-broadcast.c @@ -532,6 +532,13 @@ again: cpumask_or(tmpmask, tmpmask, tick_broadcast_force_mask); cpumask_clear(tick_broadcast_force_mask); + /* + * Sanity check. Catch the case where we try to broadcast to + * offline cpus. + */ + if (WARN_ON_ONCE(!cpumask_subset(tmpmask, cpu_online_mask))) + cpumask_and(tmpmask, tmpmask, cpu_online_mask); + /* * Wakeup the cpus which have an expired event. */ @@ -773,10 +780,12 @@ void tick_shutdown_broadcast_oneshot(unsigned int *cpup) raw_spin_lock_irqsave(&tick_broadcast_lock, flags); /* - * Clear the broadcast mask flag for the dead cpu, but do not - * stop the broadcast device! + * Clear the broadcast masks for the dead cpu, but do not stop + * the broadcast device! */ cpumask_clear_cpu(cpu, tick_broadcast_oneshot_mask); + cpumask_clear_cpu(cpu, tick_broadcast_pending_mask); + cpumask_clear_cpu(cpu, tick_broadcast_force_mask); raw_spin_unlock_irqrestore(&tick_broadcast_lock, flags); } -- cgit v1.2.3 From 1f73a9806bdd07a5106409bbcab3884078bd34fe Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 1 Jul 2013 22:14:10 +0200 Subject: tick: Prevent uncontrolled switch to oneshot mode When the system switches from periodic to oneshot mode, the broadcast logic causes a possibility that a CPU which has not yet switched to oneshot mode puts its own clock event device into oneshot mode without updating the state and the timer handler. CPU0 CPU1 per cpu tickdev is in periodic mode and switched to broadcast Switch to oneshot mode tick_broadcast_switch_to_oneshot() cpumask_copy(tick_oneshot_broacast_mask, tick_broadcast_mask); broadcast device mode = oneshot Timer interrupt irq_enter() tick_check_oneshot_broadcast() dev->set_mode(ONESHOT); tick_handle_periodic() if (dev->mode == ONESHOT) dev->next_event += period; FAIL. We fail, because dev->next_event contains KTIME_MAX, if the device was in periodic mode before the uncontrolled switch to oneshot happened. We must copy the broadcast bits over to the oneshot mask, because otherwise a CPU which relies on the broadcast would not been woken up anymore after the broadcast device switched to oneshot mode. So we need to verify in tick_check_oneshot_broadcast() whether the CPU has already switched to oneshot mode. If not, leave the device untouched and let the CPU switch controlled into oneshot mode. This is a long standing bug, which was never noticed, because the main user of the broadcast x86 cannot run into that scenario, AFAICT. The nonarchitected timer mess of ARM creates a gazillion of differently broken abominations which trigger the shortcomings of that broadcast code, which better had never been necessary in the first place. Reported-and-tested-by: Stehle Vincent-B46079 Reviewed-by: Stephen Boyd Cc: John Stultz , Cc: Mark Rutland Link: http://lkml.kernel.org/r/alpine.DEB.2.02.1307012153060.4013@ionos.tec.linutronix.de Cc: stable@vger.kernel.org Signed-off-by: Thomas Gleixner --- kernel/time/tick-broadcast.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c index 4790037163f6..248f80dba746 100644 --- a/kernel/time/tick-broadcast.c +++ b/kernel/time/tick-broadcast.c @@ -492,7 +492,15 @@ void tick_check_oneshot_broadcast(int cpu) if (cpumask_test_cpu(cpu, tick_broadcast_oneshot_mask)) { struct tick_device *td = &per_cpu(tick_cpu_device, cpu); - clockevents_set_mode(td->evtdev, CLOCK_EVT_MODE_ONESHOT); + /* + * We might be in the middle of switching over from + * periodic to oneshot. If the CPU has not yet + * switched over, leave the device alone. + */ + if (td->mode == TICKDEV_MODE_ONESHOT) { + clockevents_set_mode(td->evtdev, + CLOCK_EVT_MODE_ONESHOT); + } } } -- cgit v1.2.3 From 07bd1172902e782f288e4d44b1fde7dec0f08b6f Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 1 Jul 2013 22:14:10 +0200 Subject: tick: Sanitize broadcast control logic The recent implementation of a generic dummy timer resulted in a different registration order of per cpu local timers which made the broadcast control logic go belly up. If the dummy timer is the first clock event device which is registered for a CPU, then it is installed, the broadcast timer is initialized and the CPU is marked as broadcast target. If a real clock event device is installed after that, we can fail to take the CPU out of the broadcast mask. In the worst case we end up with two periodic timer events firing for the same CPU. One from the per cpu hardware device and one from the broadcast. Now the problem is that we have no way to distinguish whether the system is in a state which makes broadcasting necessary or the broadcast bit was set due to the nonfunctional dummy timer installment. To solve this we need to keep track of the system state seperately and provide a more detailed decision logic whether we keep the CPU in broadcast mode or not. The old decision logic only clears the broadcast mode, if the newly installed clock event device is not affected by power states. The new logic clears the broadcast mode if one of the following is true: - The new device is not affected by power states. - The system is not in a power state affected mode - The system has switched to oneshot mode. The oneshot broadcast is controlled from the deep idle state. The CPU is not in idle at this point, so it's safe to remove it from the mask. If we clear the broadcast bit for the CPU when a new device is installed, we also shutdown the broadcast device when this was the last CPU in the broadcast mask. If the broadcast bit is kept, then we leave the new device in shutdown state and rely on the broadcast to deliver the timer interrupts via the broadcast ipis. Reported-and-tested-by: Stehle Vincent-B46079 Reviewed-by: Stephen Boyd Cc: John Stultz , Cc: Mark Rutland Link: http://lkml.kernel.org/r/alpine.DEB.2.02.1307012153060.4013@ionos.tec.linutronix.de Cc: stable@vger.kernel.org Signed-off-by: Thomas Gleixner --- kernel/time/tick-broadcast.c | 70 +++++++++++++++++++++++++++++++++++++------- kernel/time/tick-common.c | 3 +- 2 files changed, 61 insertions(+), 12 deletions(-) diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c index 248f80dba746..4430fa695b48 100644 --- a/kernel/time/tick-broadcast.c +++ b/kernel/time/tick-broadcast.c @@ -30,6 +30,7 @@ static struct tick_device tick_broadcast_device; static cpumask_var_t tick_broadcast_mask; +static cpumask_var_t tick_broadcast_on; static cpumask_var_t tmpmask; static DEFINE_RAW_SPINLOCK(tick_broadcast_lock); static int tick_broadcast_force; @@ -140,8 +141,9 @@ static void tick_device_setup_broadcast_func(struct clock_event_device *dev) */ int tick_device_uses_broadcast(struct clock_event_device *dev, int cpu) { + struct clock_event_device *bc = tick_broadcast_device.evtdev; unsigned long flags; - int ret = 0; + int ret; raw_spin_lock_irqsave(&tick_broadcast_lock, flags); @@ -155,20 +157,59 @@ int tick_device_uses_broadcast(struct clock_event_device *dev, int cpu) dev->event_handler = tick_handle_periodic; tick_device_setup_broadcast_func(dev); cpumask_set_cpu(cpu, tick_broadcast_mask); - tick_broadcast_start_periodic(tick_broadcast_device.evtdev); + tick_broadcast_start_periodic(bc); ret = 1; } else { /* - * When the new device is not affected by the stop - * feature and the cpu is marked in the broadcast mask - * then clear the broadcast bit. + * Clear the broadcast bit for this cpu if the + * device is not power state affected. */ - if (!(dev->features & CLOCK_EVT_FEAT_C3STOP)) { - int cpu = smp_processor_id(); + if (!(dev->features & CLOCK_EVT_FEAT_C3STOP)) cpumask_clear_cpu(cpu, tick_broadcast_mask); - tick_broadcast_clear_oneshot(cpu); - } else { + else tick_device_setup_broadcast_func(dev); + + /* + * Clear the broadcast bit if the CPU is not in + * periodic broadcast on state. + */ + if (!cpumask_test_cpu(cpu, tick_broadcast_on)) + cpumask_clear_cpu(cpu, tick_broadcast_mask); + + switch (tick_broadcast_device.mode) { + case TICKDEV_MODE_ONESHOT: + /* + * If the system is in oneshot mode we can + * unconditionally clear the oneshot mask bit, + * because the CPU is running and therefore + * not in an idle state which causes the power + * state affected device to stop. Let the + * caller initialize the device. + */ + tick_broadcast_clear_oneshot(cpu); + ret = 0; + break; + + case TICKDEV_MODE_PERIODIC: + /* + * If the system is in periodic mode, check + * whether the broadcast device can be + * switched off now. + */ + if (cpumask_empty(tick_broadcast_mask) && bc) + clockevents_shutdown(bc); + /* + * If we kept the cpu in the broadcast mask, + * tell the caller to leave the per cpu device + * in shutdown state. The periodic interrupt + * is delivered by the broadcast device. + */ + ret = cpumask_test_cpu(cpu, tick_broadcast_mask); + break; + default: + /* Nothing to do */ + ret = 0; + break; } } raw_spin_unlock_irqrestore(&tick_broadcast_lock, flags); @@ -298,6 +339,7 @@ static void tick_do_broadcast_on_off(unsigned long *reason) switch (*reason) { case CLOCK_EVT_NOTIFY_BROADCAST_ON: case CLOCK_EVT_NOTIFY_BROADCAST_FORCE: + cpumask_set_cpu(cpu, tick_broadcast_on); if (!cpumask_test_and_set_cpu(cpu, tick_broadcast_mask)) { if (tick_broadcast_device.mode == TICKDEV_MODE_PERIODIC) @@ -307,8 +349,12 @@ static void tick_do_broadcast_on_off(unsigned long *reason) tick_broadcast_force = 1; break; case CLOCK_EVT_NOTIFY_BROADCAST_OFF: - if (!tick_broadcast_force && - cpumask_test_and_clear_cpu(cpu, tick_broadcast_mask)) { + if (tick_broadcast_force) + break; + cpumask_clear_cpu(cpu, tick_broadcast_on); + if (!tick_device_is_functional(dev)) + break; + if (cpumask_test_and_clear_cpu(cpu, tick_broadcast_mask)) { if (tick_broadcast_device.mode == TICKDEV_MODE_PERIODIC) tick_setup_periodic(dev, 0); @@ -366,6 +412,7 @@ void tick_shutdown_broadcast(unsigned int *cpup) bc = tick_broadcast_device.evtdev; cpumask_clear_cpu(cpu, tick_broadcast_mask); + cpumask_clear_cpu(cpu, tick_broadcast_on); if (tick_broadcast_device.mode == TICKDEV_MODE_PERIODIC) { if (bc && cpumask_empty(tick_broadcast_mask)) @@ -821,6 +868,7 @@ bool tick_broadcast_oneshot_available(void) void __init tick_broadcast_init(void) { zalloc_cpumask_var(&tick_broadcast_mask, GFP_NOWAIT); + zalloc_cpumask_var(&tick_broadcast_on, GFP_NOWAIT); zalloc_cpumask_var(&tmpmask, GFP_NOWAIT); #ifdef CONFIG_TICK_ONESHOT zalloc_cpumask_var(&tick_broadcast_oneshot_mask, GFP_NOWAIT); diff --git a/kernel/time/tick-common.c b/kernel/time/tick-common.c index edd45f64162f..64522ecdfe0e 100644 --- a/kernel/time/tick-common.c +++ b/kernel/time/tick-common.c @@ -194,7 +194,8 @@ static void tick_setup_device(struct tick_device *td, * When global broadcasting is active, check if the current * device is registered as a placeholder for broadcast mode. * This allows us to handle this x86 misfeature in a generic - * way. + * way. This function also returns !=0 when we keep the + * current active broadcast state for this CPU. */ if (tick_device_uses_broadcast(newdev, cpu)) return; -- cgit v1.2.3 From f6b30d32d242243a967993789c4cf550d840727e Mon Sep 17 00:00:00 2001 From: James Hogan Date: Mon, 1 Jul 2013 13:44:10 +0100 Subject: metag: kick: add missing irq_enter/exit to kick_handler() kick_handler() doesn't have an irq_enter/exit pair, but it's used for handling SMP IPIs which require work to be done in softirqs, which are invoked from irq_exit() when the hard irq nest count reaches 0. The scheduler_ipi() callback in the IPI handler calls irq_enter/exit itself, but this is inside kick_handler()'s spin lock critical section, so if an invoked softirq issues an IPI the kick_handler() will be re-entered on the same CPU and will deadlock. This is easily fixed by adding the missing irq_enter/exit to kick_handler() so that the hard irq nest count doesn't reach 0 until after the spin lock has been released. Ideally the spin lock protected handler list will also be replaced by a lockless RCU protected list since it is certainly mostly read. That can be done in a later change though. Signed-off-by: James Hogan Acked-by: Thomas Gleixner --- arch/metag/kernel/kick.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/metag/kernel/kick.c b/arch/metag/kernel/kick.c index 50fcbec98cd2..beb377621322 100644 --- a/arch/metag/kernel/kick.c +++ b/arch/metag/kernel/kick.c @@ -26,6 +26,8 @@ * pass it as an argument. */ #include +#include +#include #include #include #include @@ -66,6 +68,7 @@ EXPORT_SYMBOL(kick_unregister_func); TBIRES kick_handler(TBIRES State, int SigNum, int Triggers, int Inst, PTBI pTBI) { + struct pt_regs *old_regs; struct kick_irq_handler *kh; struct list_head *lh; int handled = 0; @@ -79,6 +82,9 @@ kick_handler(TBIRES State, int SigNum, int Triggers, int Inst, PTBI pTBI) trace_hardirqs_off(); + old_regs = set_irq_regs((struct pt_regs *)State.Sig.pCtx); + irq_enter(); + /* * There is no need to disable interrupts here because we * can't nest KICK interrupts in a KICK interrupt handler. @@ -97,5 +103,8 @@ kick_handler(TBIRES State, int SigNum, int Triggers, int Inst, PTBI pTBI) WARN_ON(!handled); + irq_exit(); + set_irq_regs(old_regs); + return tail_end(ret); } -- cgit v1.2.3 From 8b8b2412994fffd5a8ab3b9209fa0aa9f0fcee4c Mon Sep 17 00:00:00 2001 From: James Hogan Date: Mon, 1 Jul 2013 15:36:38 +0100 Subject: metag: cpu hotplug: route_irq: preserve irq mask The route_irq() function needs to preserve the irq mask by using the _irqsave/irqrestore variants of raw spin lock functions instead of the _irq variants. This is because it is called from __cpu_disable() (via migrate_irqs()), which is called with IRQs disabled, so using the _irq variants re-enables IRQs. This appears to have been causing occasional hits of the BUG_ON(!irqs_disabled()) in __irq_work_run() during CPU hotplug soak testing: BUG: failure at kernel/irq_work.c:122/__irq_work_run()! Signed-off-by: James Hogan Acked-by: Thomas Gleixner Reviewed-by: Srivatsa S. Bhat --- arch/metag/kernel/irq.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/metag/kernel/irq.c b/arch/metag/kernel/irq.c index d91b1e91346c..2a2c9d55187e 100644 --- a/arch/metag/kernel/irq.c +++ b/arch/metag/kernel/irq.c @@ -279,11 +279,12 @@ static void route_irq(struct irq_data *data, unsigned int irq, unsigned int cpu) { struct irq_desc *desc = irq_to_desc(irq); struct irq_chip *chip = irq_data_get_irq_chip(data); + unsigned long flags; - raw_spin_lock_irq(&desc->lock); + raw_spin_lock_irqsave(&desc->lock, flags); if (chip->irq_set_affinity) chip->irq_set_affinity(data, cpumask_of(cpu), false); - raw_spin_unlock_irq(&desc->lock); + raw_spin_unlock_irqrestore(&desc->lock, flags); } /* -- cgit v1.2.3 From 713759291c9ff2f8191cfb6600b87c49832b4c8f Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Tue, 2 Jul 2013 09:11:39 -0400 Subject: drm/radeon/dpm: clarify debugfs warning For chips without debugfs dpm support say that it's not implemented rather than not supported to avoid confusion about DPM support in general. Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/radeon_pm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 075f2fa56897..ebbdb477745a 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -1399,7 +1399,7 @@ static int radeon_debugfs_pm_info(struct seq_file *m, void *data) if (rdev->asic->dpm.debugfs_print_current_performance_level) radeon_dpm_debugfs_print_current_performance_level(rdev, m); else - seq_printf(m, "Unsupported\n"); + seq_printf(m, "Debugfs support not implemented for this asic\n"); mutex_unlock(&rdev->pm.mutex); } else { seq_printf(m, "default engine clock: %u0 kHz\n", rdev->pm.default_sclk); -- cgit v1.2.3 From ff451961a8b2a17667a7bfa39c86fb9b351445db Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Mon, 1 Jul 2013 22:50:29 -0400 Subject: tracing: Add trace_array_get/put() to handle instance refs better Commit a695cb58162 "tracing: Prevent deleting instances when they are being read" tried to fix a race between deleting a trace instance and reading contents of a trace file. But it wasn't good enough. The following could crash the kernel: # cd /sys/kernel/debug/tracing/instances # ( while :; do mkdir foo; rmdir foo; done ) & # ( while :; do cat foo/trace &> /dev/null; done ) & Luckily this can only be done by root user, but it should be fixed regardless. The problem is that a delete of the file can happen after the reader starts to open the file but before it grabs the trace_types_mutex. The solution is to validate the trace array before using it. If the trace array does not exist in the list of trace arrays, then it returns -ENODEV. There's a possibility that a trace_array could be deleted and a new one created and the open would open its file instead. But that is very minor as it will just return the data of the new trace array, it may confuse the user but it will not crash the system. As this can only be done by root anyway, the race will only occur if root is deleting what its trying to read at the same time. Cc: stable@vger.kernel.org # 3.10 Reported-by: Alexander Lam Signed-off-by: Steven Rostedt --- kernel/trace/trace.c | 83 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 65 insertions(+), 18 deletions(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index e36da7ff59bf..6be9df1aa513 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -204,6 +204,37 @@ static struct trace_array global_trace; LIST_HEAD(ftrace_trace_arrays); +int trace_array_get(struct trace_array *this_tr) +{ + struct trace_array *tr; + int ret = -ENODEV; + + mutex_lock(&trace_types_lock); + list_for_each_entry(tr, &ftrace_trace_arrays, list) { + if (tr == this_tr) { + tr->ref++; + ret = 0; + break; + } + } + mutex_unlock(&trace_types_lock); + + return ret; +} + +static void __trace_array_put(struct trace_array *this_tr) +{ + WARN_ON(!this_tr->ref); + this_tr->ref--; +} + +void trace_array_put(struct trace_array *this_tr) +{ + mutex_lock(&trace_types_lock); + __trace_array_put(this_tr); + mutex_unlock(&trace_types_lock); +} + int filter_current_check_discard(struct ring_buffer *buffer, struct ftrace_event_call *call, void *rec, struct ring_buffer_event *event) @@ -2831,10 +2862,9 @@ static const struct seq_operations tracer_seq_ops = { }; static struct trace_iterator * -__tracing_open(struct inode *inode, struct file *file, bool snapshot) +__tracing_open(struct trace_array *tr, struct trace_cpu *tc, + struct inode *inode, struct file *file, bool snapshot) { - struct trace_cpu *tc = inode->i_private; - struct trace_array *tr = tc->tr; struct trace_iterator *iter; int cpu; @@ -2913,8 +2943,6 @@ __tracing_open(struct inode *inode, struct file *file, bool snapshot) tracing_iter_reset(iter, cpu); } - tr->ref++; - mutex_unlock(&trace_types_lock); return iter; @@ -2944,17 +2972,20 @@ static int tracing_release(struct inode *inode, struct file *file) struct trace_array *tr; int cpu; - if (!(file->f_mode & FMODE_READ)) + /* Writes do not use seq_file, need to grab tr from inode */ + if (!(file->f_mode & FMODE_READ)) { + struct trace_cpu *tc = inode->i_private; + + trace_array_put(tc->tr); return 0; + } iter = m->private; tr = iter->tr; + trace_array_put(tr); mutex_lock(&trace_types_lock); - WARN_ON(!tr->ref); - tr->ref--; - for_each_tracing_cpu(cpu) { if (iter->buffer_iter[cpu]) ring_buffer_read_finish(iter->buffer_iter[cpu]); @@ -2973,20 +3004,23 @@ static int tracing_release(struct inode *inode, struct file *file) kfree(iter->trace); kfree(iter->buffer_iter); seq_release_private(inode, file); + return 0; } static int tracing_open(struct inode *inode, struct file *file) { + struct trace_cpu *tc = inode->i_private; + struct trace_array *tr = tc->tr; struct trace_iterator *iter; int ret = 0; + if (trace_array_get(tr) < 0) + return -ENODEV; + /* If this file was open for write, then erase contents */ if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) { - struct trace_cpu *tc = inode->i_private; - struct trace_array *tr = tc->tr; - if (tc->cpu == RING_BUFFER_ALL_CPUS) tracing_reset_online_cpus(&tr->trace_buffer); else @@ -2994,12 +3028,16 @@ static int tracing_open(struct inode *inode, struct file *file) } if (file->f_mode & FMODE_READ) { - iter = __tracing_open(inode, file, false); + iter = __tracing_open(tr, tc, inode, file, false); if (IS_ERR(iter)) ret = PTR_ERR(iter); else if (trace_flags & TRACE_ITER_LATENCY_FMT) iter->iter_flags |= TRACE_FILE_LAT_FMT; } + + if (ret < 0) + trace_array_put(tr); + return ret; } @@ -4575,12 +4613,16 @@ struct ftrace_buffer_info { static int tracing_snapshot_open(struct inode *inode, struct file *file) { struct trace_cpu *tc = inode->i_private; + struct trace_array *tr = tc->tr; struct trace_iterator *iter; struct seq_file *m; int ret = 0; + if (trace_array_get(tr) < 0) + return -ENODEV; + if (file->f_mode & FMODE_READ) { - iter = __tracing_open(inode, file, true); + iter = __tracing_open(tr, tc, inode, file, true); if (IS_ERR(iter)) ret = PTR_ERR(iter); } else { @@ -4593,13 +4635,16 @@ static int tracing_snapshot_open(struct inode *inode, struct file *file) kfree(m); return -ENOMEM; } - iter->tr = tc->tr; + iter->tr = tr; iter->trace_buffer = &tc->tr->max_buffer; iter->cpu_file = tc->cpu; m->private = iter; file->private_data = m; } + if (ret < 0) + trace_array_put(tr); + return ret; } @@ -4680,9 +4725,12 @@ out: static int tracing_snapshot_release(struct inode *inode, struct file *file) { struct seq_file *m = file->private_data; + int ret; + + ret = tracing_release(inode, file); if (file->f_mode & FMODE_READ) - return tracing_release(inode, file); + return ret; /* If write only, the seq_file is just a stub */ if (m) @@ -4927,8 +4975,7 @@ static int tracing_buffers_release(struct inode *inode, struct file *file) mutex_lock(&trace_types_lock); - WARN_ON(!iter->tr->ref); - iter->tr->ref--; + __trace_array_put(iter->tr); if (info->spare) ring_buffer_free_read_page(iter->trace_buffer->buffer, info->spare); -- cgit v1.2.3 From 7b85af63034818e43aee6c1d7bf1c7c6796a9073 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Mon, 1 Jul 2013 23:34:22 -0400 Subject: tracing: Get trace_array ref counts when accessing trace files When a trace file is opened that may access a trace array, it must increment its ref count to prevent it from being deleted. Cc: stable@vger.kernel.org # 3.10 Reported-by: Alexander Lam Signed-off-by: Steven Rostedt --- kernel/trace/trace.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 112 insertions(+), 9 deletions(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 6be9df1aa513..6d9bd9b43e43 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -2965,6 +2965,43 @@ int tracing_open_generic(struct inode *inode, struct file *filp) return 0; } +/* + * Open and update trace_array ref count. + * Must have the current trace_array passed to it. + */ +int tracing_open_generic_tr(struct inode *inode, struct file *filp) +{ + struct trace_array *tr = inode->i_private; + + if (tracing_disabled) + return -ENODEV; + + if (trace_array_get(tr) < 0) + return -ENODEV; + + filp->private_data = inode->i_private; + + return 0; + +} + +int tracing_open_generic_tc(struct inode *inode, struct file *filp) +{ + struct trace_cpu *tc = inode->i_private; + struct trace_array *tr = tc->tr; + + if (tracing_disabled) + return -ENODEV; + + if (trace_array_get(tr) < 0) + return -ENODEV; + + filp->private_data = inode->i_private; + + return 0; + +} + static int tracing_release(struct inode *inode, struct file *file) { struct seq_file *m = file->private_data; @@ -3008,6 +3045,32 @@ static int tracing_release(struct inode *inode, struct file *file) return 0; } +static int tracing_release_generic_tr(struct inode *inode, struct file *file) +{ + struct trace_array *tr = inode->i_private; + + trace_array_put(tr); + return 0; +} + +static int tracing_release_generic_tc(struct inode *inode, struct file *file) +{ + struct trace_cpu *tc = inode->i_private; + struct trace_array *tr = tc->tr; + + trace_array_put(tr); + return 0; +} + +static int tracing_single_release_tr(struct inode *inode, struct file *file) +{ + struct trace_array *tr = inode->i_private; + + trace_array_put(tr); + + return single_release(inode, file); +} + static int tracing_open(struct inode *inode, struct file *file) { struct trace_cpu *tc = inode->i_private; @@ -3394,9 +3457,14 @@ tracing_trace_options_write(struct file *filp, const char __user *ubuf, static int tracing_trace_options_open(struct inode *inode, struct file *file) { + struct trace_array *tr = inode->i_private; + if (tracing_disabled) return -ENODEV; + if (trace_array_get(tr) < 0) + return -ENODEV; + return single_open(file, tracing_trace_options_show, inode->i_private); } @@ -3404,7 +3472,7 @@ static const struct file_operations tracing_iter_fops = { .open = tracing_trace_options_open, .read = seq_read, .llseek = seq_lseek, - .release = single_release, + .release = tracing_single_release_tr, .write = tracing_trace_options_write, }; @@ -3892,6 +3960,9 @@ static int tracing_open_pipe(struct inode *inode, struct file *filp) if (tracing_disabled) return -ENODEV; + if (trace_array_get(tr) < 0) + return -ENODEV; + mutex_lock(&trace_types_lock); /* create a buffer to store the information to pass to userspace */ @@ -3944,6 +4015,7 @@ out: fail: kfree(iter->trace); kfree(iter); + __trace_array_put(tr); mutex_unlock(&trace_types_lock); return ret; } @@ -3951,6 +4023,8 @@ fail: static int tracing_release_pipe(struct inode *inode, struct file *file) { struct trace_iterator *iter = file->private_data; + struct trace_cpu *tc = inode->i_private; + struct trace_array *tr = tc->tr; mutex_lock(&trace_types_lock); @@ -3964,6 +4038,8 @@ static int tracing_release_pipe(struct inode *inode, struct file *file) kfree(iter->trace); kfree(iter); + trace_array_put(tr); + return 0; } @@ -4421,6 +4497,8 @@ tracing_free_buffer_release(struct inode *inode, struct file *filp) /* resize the ring buffer to 0 */ tracing_resize_ring_buffer(tr, 0, RING_BUFFER_ALL_CPUS); + trace_array_put(tr); + return 0; } @@ -4597,10 +4675,20 @@ static ssize_t tracing_clock_write(struct file *filp, const char __user *ubuf, static int tracing_clock_open(struct inode *inode, struct file *file) { + struct trace_array *tr = inode->i_private; + int ret; + if (tracing_disabled) return -ENODEV; - return single_open(file, tracing_clock_show, inode->i_private); + if (trace_array_get(tr)) + return -ENODEV; + + ret = single_open(file, tracing_clock_show, inode->i_private); + if (ret < 0) + trace_array_put(tr); + + return ret; } struct ftrace_buffer_info { @@ -4796,34 +4884,38 @@ static const struct file_operations tracing_pipe_fops = { }; static const struct file_operations tracing_entries_fops = { - .open = tracing_open_generic, + .open = tracing_open_generic_tc, .read = tracing_entries_read, .write = tracing_entries_write, .llseek = generic_file_llseek, + .release = tracing_release_generic_tc, }; static const struct file_operations tracing_total_entries_fops = { - .open = tracing_open_generic, + .open = tracing_open_generic_tr, .read = tracing_total_entries_read, .llseek = generic_file_llseek, + .release = tracing_release_generic_tr, }; static const struct file_operations tracing_free_buffer_fops = { + .open = tracing_open_generic_tr, .write = tracing_free_buffer_write, .release = tracing_free_buffer_release, }; static const struct file_operations tracing_mark_fops = { - .open = tracing_open_generic, + .open = tracing_open_generic_tr, .write = tracing_mark_write, .llseek = generic_file_llseek, + .release = tracing_release_generic_tr, }; static const struct file_operations trace_clock_fops = { .open = tracing_clock_open, .read = seq_read, .llseek = seq_lseek, - .release = single_release, + .release = tracing_single_release_tr, .write = tracing_clock_write, }; @@ -4851,13 +4943,19 @@ static int tracing_buffers_open(struct inode *inode, struct file *filp) struct trace_cpu *tc = inode->i_private; struct trace_array *tr = tc->tr; struct ftrace_buffer_info *info; + int ret; if (tracing_disabled) return -ENODEV; + if (trace_array_get(tr) < 0) + return -ENODEV; + info = kzalloc(sizeof(*info), GFP_KERNEL); - if (!info) + if (!info) { + trace_array_put(tr); return -ENOMEM; + } mutex_lock(&trace_types_lock); @@ -4875,7 +4973,11 @@ static int tracing_buffers_open(struct inode *inode, struct file *filp) mutex_unlock(&trace_types_lock); - return nonseekable_open(inode, filp); + ret = nonseekable_open(inode, filp); + if (ret < 0) + trace_array_put(tr); + + return ret; } static unsigned int @@ -5765,9 +5867,10 @@ rb_simple_write(struct file *filp, const char __user *ubuf, } static const struct file_operations rb_simple_fops = { - .open = tracing_open_generic, + .open = tracing_open_generic_tr, .read = rb_simple_read, .write = rb_simple_write, + .release = tracing_release_generic_tr, .llseek = default_llseek, }; -- cgit v1.2.3 From cf3b1c2ba362ecea5b8e07d370233e25f3d6366d Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Tue, 2 Jul 2013 08:17:17 -0700 Subject: leds: mc13783: Fix "uninitialized variable" warning drivers/leds/leds-mc13783.c: In function 'mc13xxx_led_probe': drivers/leds/leds-mc13783.c:195:2: warning: 'ret' may be used uninitialized in this function [-Wmaybe-uninitialized] Signed-off-by: Alexander Shiyan Signed-off-by: Bryan Wu --- drivers/leds/leds-mc13783.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/leds/leds-mc13783.c b/drivers/leds/leds-mc13783.c index f4de98052aaa..fa9b439323bd 100644 --- a/drivers/leds/leds-mc13783.c +++ b/drivers/leds/leds-mc13783.c @@ -208,7 +208,7 @@ static int __init mc13xxx_led_probe(struct platform_device *pdev) struct mc13xxx_led_devtype *devtype = (struct mc13xxx_led_devtype *)pdev->id_entry->driver_data; struct mc13xxx_leds *leds; - int i, id, num_leds, ret; + int i, id, num_leds, ret = -ENODATA; u32 reg, init_led = 0; if (!pdata) { -- cgit v1.2.3 From f23b5a59955c0ea13c6da211fb06f39348e3c794 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Wed, 19 Jun 2013 10:16:26 -0400 Subject: Btrfs: check for actual acls rather than just xattrs when caching no acl We have an optimization that will go ahead and cache no acls on an inode if there are no xattrs on the inode. This saves us a lookup later to check the acls for writes or any other access. The problem is I use selinux so I always have an xattr on inodes, so make this test a little smarter and check for the actual acl hash on the key and if it isn't there then we still get to cache no acl which makes everybody who uses selinux a little happier. Thanks, Signed-off-by: Josef Bacik --- fs/btrfs/inode.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index b7fa96f72ecd..8edcdf6910f7 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -41,6 +41,7 @@ #include #include #include +#include #include "compat.h" #include "ctree.h" #include "disk-io.h" @@ -56,6 +57,7 @@ #include "free-space-cache.h" #include "inode-map.h" #include "backref.h" +#include "hash.h" struct btrfs_iget_args { u64 ino; @@ -3300,8 +3302,17 @@ static noinline int acls_after_inode_item(struct extent_buffer *leaf, { u32 nritems = btrfs_header_nritems(leaf); struct btrfs_key found_key; + static u64 xattr_access = 0; + static u64 xattr_default = 0; int scanned = 0; + if (!xattr_access) { + xattr_access = btrfs_name_hash(POSIX_ACL_XATTR_ACCESS, + strlen(POSIX_ACL_XATTR_ACCESS)); + xattr_default = btrfs_name_hash(POSIX_ACL_XATTR_DEFAULT, + strlen(POSIX_ACL_XATTR_DEFAULT)); + } + slot++; while (slot < nritems) { btrfs_item_key_to_cpu(leaf, &found_key, slot); @@ -3311,8 +3322,11 @@ static noinline int acls_after_inode_item(struct extent_buffer *leaf, return 0; /* we found an xattr, assume we've got an acl */ - if (found_key.type == BTRFS_XATTR_ITEM_KEY) - return 1; + if (found_key.type == BTRFS_XATTR_ITEM_KEY) { + if (found_key.offset == xattr_access || + found_key.offset == xattr_default) + return 1; + } /* * we found a key greater than an xattr key, there can't -- cgit v1.2.3 From b150a4f10d8786a204db1ae3dccada17f950cf54 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Wed, 19 Jun 2013 15:00:04 -0400 Subject: Btrfs: use a percpu to keep track of possibly pinned bytes There are all of these checks in the ENOSPC code to see if committing the transaction would free up enough space to make the allocation. This is because early on we just committed the transaction and hoped and prayed, which resulted in cases where it took _forever_ to get an ENOSPC when we really were out of space. So we check space_info->bytes_pinned, except this isn't completely true because it doesn't account for space we may free but are stuck in delayed refs. So tests like xfstests 226 would fail because we wouldn't commit the transaction to free up the data space. So instead add a percpu counter that will be a little fuzzier, it will add bytes as soon as we try to free up the space, and remove any space it doesn't actually free up when we get around to doing the actual free. We then 0 out this counter every transaction period so we have a better idea of how much space we will actually free up by committing this transaction. With this patch we now pass xfstests 226. Thanks, Signed-off-by: Josef Bacik --- fs/btrfs/ctree.h | 12 ++++++++++ fs/btrfs/extent-tree.c | 59 +++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 66 insertions(+), 5 deletions(-) diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 76e4983b39ea..b528a5509cb8 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -1101,6 +1101,18 @@ struct btrfs_space_info { u64 disk_total; /* total bytes on disk, takes mirrors into account */ + /* + * bytes_pinned is kept in line with what is actually pinned, as in + * we've called update_block_group and dropped the bytes_used counter + * and increased the bytes_pinned counter. However this means that + * bytes_pinned does not reflect the bytes that will be pinned once the + * delayed refs are flushed, so this counter is inc'ed everytime we call + * btrfs_free_extent so it is a realtime count of what will be freed + * once the transaction is committed. It will be zero'ed everytime the + * transaction commits. + */ + struct percpu_counter total_bytes_pinned; + /* * we bump reservation progress every time we decrement * bytes_reserved. This way people waiting for reservations diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 6d5c5f73ad64..bbd3db7d0833 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "compat.h" #include "hash.h" #include "ctree.h" @@ -3357,6 +3358,7 @@ static int update_space_info(struct btrfs_fs_info *info, u64 flags, struct btrfs_space_info *found; int i; int factor; + int ret; if (flags & (BTRFS_BLOCK_GROUP_DUP | BTRFS_BLOCK_GROUP_RAID1 | BTRFS_BLOCK_GROUP_RAID10)) @@ -3380,6 +3382,12 @@ static int update_space_info(struct btrfs_fs_info *info, u64 flags, if (!found) return -ENOMEM; + ret = percpu_counter_init(&found->total_bytes_pinned, 0); + if (ret) { + kfree(found); + return ret; + } + for (i = 0; i < BTRFS_NR_RAID_TYPES; i++) INIT_LIST_HEAD(&found->block_groups[i]); init_rwsem(&found->groups_sem); @@ -3612,10 +3620,11 @@ alloc: } /* - * If we have less pinned bytes than we want to allocate then - * don't bother committing the transaction, it won't help us. + * If we don't have enough pinned space to deal with this + * allocation don't bother committing the transaction. */ - if (data_sinfo->bytes_pinned < bytes) + if (percpu_counter_compare(&data_sinfo->total_bytes_pinned, + bytes) < 0) committed = 1; spin_unlock(&data_sinfo->lock); @@ -3624,6 +3633,7 @@ commit_trans: if (!committed && !atomic_read(&root->fs_info->open_ioctl_trans)) { committed = 1; + trans = btrfs_join_transaction(root); if (IS_ERR(trans)) return PTR_ERR(trans); @@ -4044,7 +4054,8 @@ static int may_commit_transaction(struct btrfs_root *root, /* See if there is enough pinned space to make this reservation */ spin_lock(&space_info->lock); - if (space_info->bytes_pinned >= bytes) { + if (percpu_counter_compare(&space_info->total_bytes_pinned, + bytes) >= 0) { spin_unlock(&space_info->lock); goto commit; } @@ -4059,7 +4070,8 @@ static int may_commit_transaction(struct btrfs_root *root, spin_lock(&space_info->lock); spin_lock(&delayed_rsv->lock); - if (space_info->bytes_pinned + delayed_rsv->size < bytes) { + if (percpu_counter_compare(&space_info->total_bytes_pinned, + bytes - delayed_rsv->size) >= 0) { spin_unlock(&delayed_rsv->lock); spin_unlock(&space_info->lock); return -ENOSPC; @@ -5397,6 +5409,7 @@ void btrfs_prepare_extent_commit(struct btrfs_trans_handle *trans, struct btrfs_caching_control *next; struct btrfs_caching_control *caching_ctl; struct btrfs_block_group_cache *cache; + struct btrfs_space_info *space_info; down_write(&fs_info->extent_commit_sem); @@ -5419,6 +5432,9 @@ void btrfs_prepare_extent_commit(struct btrfs_trans_handle *trans, up_write(&fs_info->extent_commit_sem); + list_for_each_entry_rcu(space_info, &fs_info->space_info, list) + percpu_counter_set(&space_info->total_bytes_pinned, 0); + update_global_block_rsv(fs_info); } @@ -5516,6 +5532,27 @@ int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans, return 0; } +static void add_pinned_bytes(struct btrfs_fs_info *fs_info, u64 num_bytes, + u64 owner, u64 root_objectid) +{ + struct btrfs_space_info *space_info; + u64 flags; + + if (owner < BTRFS_FIRST_FREE_OBJECTID) { + if (root_objectid == BTRFS_CHUNK_TREE_OBJECTID) + flags = BTRFS_BLOCK_GROUP_SYSTEM; + else + flags = BTRFS_BLOCK_GROUP_METADATA; + } else { + flags = BTRFS_BLOCK_GROUP_DATA; + } + + space_info = __find_space_info(fs_info, flags); + BUG_ON(!space_info); /* Logic bug */ + percpu_counter_add(&space_info->total_bytes_pinned, num_bytes); +} + + static int __btrfs_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 bytenr, u64 num_bytes, u64 parent, @@ -5736,6 +5773,8 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, goto out; } } + add_pinned_bytes(root->fs_info, -num_bytes, owner_objectid, + root_objectid); } else { if (found_extent) { BUG_ON(is_data && refs_to_drop != @@ -5859,6 +5898,7 @@ void btrfs_free_tree_block(struct btrfs_trans_handle *trans, u64 parent, int last_ref) { struct btrfs_block_group_cache *cache = NULL; + int pin = 1; int ret; if (root->root_key.objectid != BTRFS_TREE_LOG_OBJECTID) { @@ -5891,8 +5931,14 @@ void btrfs_free_tree_block(struct btrfs_trans_handle *trans, btrfs_add_free_space(cache, buf->start, buf->len); btrfs_update_reserved_bytes(cache, buf->len, RESERVE_FREE); + pin = 0; } out: + if (pin) + add_pinned_bytes(root->fs_info, buf->len, + btrfs_header_level(buf), + root->root_key.objectid); + /* * Deleting the buffer, clear the corrupt flag since it doesn't matter * anymore. @@ -5909,6 +5955,8 @@ int btrfs_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root *root, int ret; struct btrfs_fs_info *fs_info = root->fs_info; + add_pinned_bytes(root->fs_info, num_bytes, owner, root_objectid); + /* * tree log blocks never actually go into the extent allocation * tree, just update pinning info and exit early. @@ -8152,6 +8200,7 @@ int btrfs_free_block_groups(struct btrfs_fs_info *info) dump_space_info(space_info, 0, 0); } } + percpu_counter_destroy(&space_info->total_bytes_pinned); list_del(&space_info->list); kfree(space_info); } -- cgit v1.2.3 From 925a6efb8ff0c2bdbec107ed9890e62650c83306 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Thu, 20 Jun 2013 12:31:27 -0400 Subject: Btrfs: stop using try_to_writeback_inodes_sb_nr to flush delalloc try_to_writeback_inodes_sb_nr returns 1 if writeback is already underway, which is completely fraking useless for us as we need to make sure pages are actually written before we go and check if there are ordered extents. So replace this with an open coding of try_to_writeback_inodes_sb_nr minus the writeback underway check so that we are sure to actually have flushed some dirty pages out and will have ordered extents to use. With this patch xfstests generic/273 now passes. Thanks, Signed-off-by: Josef Bacik --- fs/btrfs/extent-tree.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index bbd3db7d0833..5154b91f6380 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -3943,12 +3943,11 @@ static void btrfs_writeback_inodes_sb_nr(struct btrfs_root *root, unsigned long nr_pages) { struct super_block *sb = root->fs_info->sb; - int started; - /* If we can not start writeback, just sync all the delalloc file. */ - started = try_to_writeback_inodes_sb_nr(sb, nr_pages, - WB_REASON_FS_FREE_SPACE); - if (!started) { + if (down_read_trylock(&sb->s_umount)) { + writeback_inodes_sb_nr(sb, nr_pages, WB_REASON_FS_FREE_SPACE); + up_read(&sb->s_umount); + } else { /* * We needn't worry the filesystem going from r/w to r/o though * we don't acquire ->s_umount mutex, because the filesystem -- cgit v1.2.3 From 7ee9e4405f264e9eda808aa5ca4522746a1af9c1 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Fri, 21 Jun 2013 16:37:03 -0400 Subject: Btrfs: check if we can nocow if we don't have data space We always just try and reserve data space when we write, but if we are out of space but have prealloc'ed extents we should still successfully write. This patch will try and see if we can write to prealloc'ed space and if we can go ahead and allow the write to continue. With this patch we now pass xfstests generic/274. Thanks, Signed-off-by: Josef Bacik --- fs/btrfs/ctree.h | 4 ++ fs/btrfs/extent-tree.c | 1 + fs/btrfs/extent_io.c | 3 ++ fs/btrfs/extent_io.h | 1 + fs/btrfs/file.c | 125 ++++++++++++++++++++++++++++++++++++++++++++----- fs/btrfs/inode.c | 40 ++++++++++------ 6 files changed, 148 insertions(+), 26 deletions(-) diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index b528a5509cb8..e795bf135e80 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -3552,6 +3552,10 @@ void btrfs_wait_and_free_delalloc_work(struct btrfs_delalloc_work *work); struct extent_map *btrfs_get_extent_fiemap(struct inode *inode, struct page *page, size_t pg_offset, u64 start, u64 len, int create); +noinline int can_nocow_extent(struct btrfs_trans_handle *trans, + struct inode *inode, u64 offset, u64 *len, + u64 *orig_start, u64 *orig_block_len, + u64 *ram_bytes); /* RHEL and EL kernels have a patch that renames PG_checked to FsMisc */ #if defined(ClearPageFsMisc) && !defined(ClearPageChecked) diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 5154b91f6380..11ba82e43e8b 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -3666,6 +3666,7 @@ void btrfs_free_reserved_data_space(struct inode *inode, u64 bytes) data_sinfo = root->fs_info->data_sinfo; spin_lock(&data_sinfo->lock); + WARN_ON(data_sinfo->bytes_may_use < bytes); data_sinfo->bytes_may_use -= bytes; trace_btrfs_space_reservation(root->fs_info, "space_info", data_sinfo->flags, bytes, 0); diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index a83d7019ede9..f8586a957a02 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -543,6 +543,9 @@ int clear_extent_bit(struct extent_io_tree *tree, u64 start, u64 end, btrfs_debug_check_extent_io_range(tree->mapping->host, start, end); + if (bits & EXTENT_DELALLOC) + bits |= EXTENT_NORESERVE; + if (delete) bits |= ~EXTENT_CTLBITS; bits |= EXTENT_FIRST_DELALLOC; diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h index 41fb81e7ec53..3b8c4e26e1da 100644 --- a/fs/btrfs/extent_io.h +++ b/fs/btrfs/extent_io.h @@ -19,6 +19,7 @@ #define EXTENT_FIRST_DELALLOC (1 << 12) #define EXTENT_NEED_WAIT (1 << 13) #define EXTENT_DAMAGED (1 << 14) +#define EXTENT_NORESERVE (1 << 15) #define EXTENT_IOBITS (EXTENT_LOCKED | EXTENT_WRITEBACK) #define EXTENT_CTLBITS (EXTENT_DO_ACCOUNTING | EXTENT_FIRST_DELALLOC) diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 5ffde5603686..2d70849cec92 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -1312,6 +1312,56 @@ fail: } +static noinline int check_can_nocow(struct inode *inode, loff_t pos, + size_t *write_bytes) +{ + struct btrfs_trans_handle *trans; + struct btrfs_root *root = BTRFS_I(inode)->root; + struct btrfs_ordered_extent *ordered; + u64 lockstart, lockend; + u64 num_bytes; + int ret; + + lockstart = round_down(pos, root->sectorsize); + lockend = lockstart + round_up(*write_bytes, root->sectorsize) - 1; + + while (1) { + lock_extent(&BTRFS_I(inode)->io_tree, lockstart, lockend); + ordered = btrfs_lookup_ordered_range(inode, lockstart, + lockend - lockstart + 1); + if (!ordered) { + break; + } + unlock_extent(&BTRFS_I(inode)->io_tree, lockstart, lockend); + btrfs_start_ordered_extent(inode, ordered, 1); + btrfs_put_ordered_extent(ordered); + } + + trans = btrfs_join_transaction(root); + if (IS_ERR(trans)) { + unlock_extent(&BTRFS_I(inode)->io_tree, lockstart, lockend); + return PTR_ERR(trans); + } + + num_bytes = lockend - lockstart + 1; + ret = can_nocow_extent(trans, inode, lockstart, &num_bytes, NULL, NULL, + NULL); + btrfs_end_transaction(trans, root); + if (ret <= 0) { + ret = 0; + } else { + clear_extent_bit(&BTRFS_I(inode)->io_tree, lockstart, lockend, + EXTENT_DIRTY | EXTENT_DELALLOC | + EXTENT_DO_ACCOUNTING | EXTENT_DEFRAG, 0, 0, + NULL, GFP_NOFS); + *write_bytes = min_t(size_t, *write_bytes, num_bytes); + } + + unlock_extent(&BTRFS_I(inode)->io_tree, lockstart, lockend); + + return ret; +} + static noinline ssize_t __btrfs_buffered_write(struct file *file, struct iov_iter *i, loff_t pos) @@ -1319,10 +1369,12 @@ static noinline ssize_t __btrfs_buffered_write(struct file *file, struct inode *inode = file_inode(file); struct btrfs_root *root = BTRFS_I(inode)->root; struct page **pages = NULL; + u64 release_bytes = 0; unsigned long first_index; size_t num_written = 0; int nrptrs; int ret = 0; + bool only_release_metadata = false; bool force_page_uptodate = false; nrptrs = min((iov_iter_count(i) + PAGE_CACHE_SIZE - 1) / @@ -1343,6 +1395,7 @@ static noinline ssize_t __btrfs_buffered_write(struct file *file, offset); size_t num_pages = (write_bytes + offset + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; + size_t reserve_bytes; size_t dirty_pages; size_t copied; @@ -1357,11 +1410,41 @@ static noinline ssize_t __btrfs_buffered_write(struct file *file, break; } - ret = btrfs_delalloc_reserve_space(inode, - num_pages << PAGE_CACHE_SHIFT); + reserve_bytes = num_pages << PAGE_CACHE_SHIFT; + ret = btrfs_check_data_free_space(inode, reserve_bytes); + if (ret == -ENOSPC && + (BTRFS_I(inode)->flags & (BTRFS_INODE_NODATACOW | + BTRFS_INODE_PREALLOC))) { + ret = check_can_nocow(inode, pos, &write_bytes); + if (ret > 0) { + only_release_metadata = true; + /* + * our prealloc extent may be smaller than + * write_bytes, so scale down. + */ + num_pages = (write_bytes + offset + + PAGE_CACHE_SIZE - 1) >> + PAGE_CACHE_SHIFT; + reserve_bytes = num_pages << PAGE_CACHE_SHIFT; + ret = 0; + } else { + ret = -ENOSPC; + } + } + if (ret) break; + ret = btrfs_delalloc_reserve_metadata(inode, reserve_bytes); + if (ret) { + if (!only_release_metadata) + btrfs_free_reserved_data_space(inode, + reserve_bytes); + break; + } + + release_bytes = reserve_bytes; + /* * This is going to setup the pages array with the number of * pages we want, so we don't really need to worry about the @@ -1370,11 +1453,8 @@ static noinline ssize_t __btrfs_buffered_write(struct file *file, ret = prepare_pages(root, file, pages, num_pages, pos, first_index, write_bytes, force_page_uptodate); - if (ret) { - btrfs_delalloc_release_space(inode, - num_pages << PAGE_CACHE_SHIFT); + if (ret) break; - } copied = btrfs_copy_from_user(pos, num_pages, write_bytes, pages, i); @@ -1404,30 +1484,46 @@ static noinline ssize_t __btrfs_buffered_write(struct file *file, * managed to copy. */ if (num_pages > dirty_pages) { + release_bytes = (num_pages - dirty_pages) << + PAGE_CACHE_SHIFT; if (copied > 0) { spin_lock(&BTRFS_I(inode)->lock); BTRFS_I(inode)->outstanding_extents++; spin_unlock(&BTRFS_I(inode)->lock); } - btrfs_delalloc_release_space(inode, - (num_pages - dirty_pages) << - PAGE_CACHE_SHIFT); + if (only_release_metadata) + btrfs_delalloc_release_metadata(inode, + release_bytes); + else + btrfs_delalloc_release_space(inode, + release_bytes); } + release_bytes = dirty_pages << PAGE_CACHE_SHIFT; if (copied > 0) { ret = btrfs_dirty_pages(root, inode, pages, dirty_pages, pos, copied, NULL); if (ret) { - btrfs_delalloc_release_space(inode, - dirty_pages << PAGE_CACHE_SHIFT); btrfs_drop_pages(pages, num_pages); break; } } + release_bytes = 0; btrfs_drop_pages(pages, num_pages); + if (only_release_metadata && copied > 0) { + u64 lockstart = round_down(pos, root->sectorsize); + u64 lockend = lockstart + + (dirty_pages << PAGE_CACHE_SHIFT) - 1; + + set_extent_bit(&BTRFS_I(inode)->io_tree, lockstart, + lockend, EXTENT_NORESERVE, NULL, + NULL, GFP_NOFS); + only_release_metadata = false; + } + cond_resched(); balance_dirty_pages_ratelimited(inode->i_mapping); @@ -1440,6 +1536,13 @@ static noinline ssize_t __btrfs_buffered_write(struct file *file, kfree(pages); + if (release_bytes) { + if (only_release_metadata) + btrfs_delalloc_release_metadata(inode, release_bytes); + else + btrfs_delalloc_release_space(inode, release_bytes); + } + return num_written ? num_written : ret; } diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 8edcdf6910f7..4d7c02258390 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -1641,7 +1641,7 @@ static void btrfs_clear_bit_hook(struct inode *inode, btrfs_delalloc_release_metadata(inode, len); if (root->root_key.objectid != BTRFS_DATA_RELOC_TREE_OBJECTID - && do_list) + && do_list && !(state->state & EXTENT_NORESERVE)) btrfs_free_reserved_data_space(inode, len); __percpu_counter_add(&root->fs_info->delalloc_bytes, -len, @@ -6396,10 +6396,10 @@ out: * returns 1 when the nocow is safe, < 1 on error, 0 if the * block must be cow'd */ -static noinline int can_nocow_odirect(struct btrfs_trans_handle *trans, - struct inode *inode, u64 offset, u64 *len, - u64 *orig_start, u64 *orig_block_len, - u64 *ram_bytes) +noinline int can_nocow_extent(struct btrfs_trans_handle *trans, + struct inode *inode, u64 offset, u64 *len, + u64 *orig_start, u64 *orig_block_len, + u64 *ram_bytes) { struct btrfs_path *path; int ret; @@ -6413,7 +6413,7 @@ static noinline int can_nocow_odirect(struct btrfs_trans_handle *trans, u64 num_bytes; int slot; int found_type; - + bool nocow = (BTRFS_I(inode)->flags & BTRFS_INODE_NODATACOW); path = btrfs_alloc_path(); if (!path) return -ENOMEM; @@ -6453,18 +6453,28 @@ static noinline int can_nocow_odirect(struct btrfs_trans_handle *trans, /* not a regular extent, must cow */ goto out; } + + if (!nocow && found_type == BTRFS_FILE_EXTENT_REG) + goto out; + disk_bytenr = btrfs_file_extent_disk_bytenr(leaf, fi); + if (disk_bytenr == 0) + goto out; + + if (btrfs_file_extent_compression(leaf, fi) || + btrfs_file_extent_encryption(leaf, fi) || + btrfs_file_extent_other_encoding(leaf, fi)) + goto out; + backref_offset = btrfs_file_extent_offset(leaf, fi); - *orig_start = key.offset - backref_offset; - *orig_block_len = btrfs_file_extent_disk_num_bytes(leaf, fi); - *ram_bytes = btrfs_file_extent_ram_bytes(leaf, fi); + if (orig_start) { + *orig_start = key.offset - backref_offset; + *orig_block_len = btrfs_file_extent_disk_num_bytes(leaf, fi); + *ram_bytes = btrfs_file_extent_ram_bytes(leaf, fi); + } extent_end = key.offset + btrfs_file_extent_num_bytes(leaf, fi); - if (extent_end < offset + *len) { - /* extent doesn't include our full range, must cow */ - goto out; - } if (btrfs_extent_readonly(root, disk_bytenr)) goto out; @@ -6708,8 +6718,8 @@ static int btrfs_get_blocks_direct(struct inode *inode, sector_t iblock, if (IS_ERR(trans)) goto must_cow; - if (can_nocow_odirect(trans, inode, start, &len, &orig_start, - &orig_block_len, &ram_bytes) == 1) { + if (can_nocow_extent(trans, inode, start, &len, &orig_start, + &orig_block_len, &ram_bytes) == 1) { if (type == BTRFS_ORDERED_PREALLOC) { free_extent_map(em); em = create_pinned_em(inode, start, len, -- cgit v1.2.3 From f51a4a1826ff810eb9c00cadff8978b028c40756 Mon Sep 17 00:00:00 2001 From: Miao Xie Date: Wed, 19 Jun 2013 10:36:09 +0800 Subject: Btrfs: remove btrfs_sector_sum structure Using the structure btrfs_sector_sum to keep the checksum value is unnecessary, because the extents that btrfs_sector_sum points to are continuous, we can find out the expected checksums by btrfs_ordered_sum's bytenr and the offset, so we can remove btrfs_sector_sum's bytenr. After removing bytenr, there is only one member in the structure, so it makes no sense to keep the structure, just remove it, and use a u32 array to store the checksum value. By this change, we don't use the while loop to get the checksums one by one. Now, we can get several checksum value at one time, it improved the performance by ~74% on my SSD (31MB/s -> 54MB/s). test command: # dd if=/dev/zero of=/mnt/btrfs/file0 bs=1M count=1024 oflag=sync Signed-off-by: Miao Xie Signed-off-by: Josef Bacik --- fs/btrfs/file-item.c | 144 ++++++++++++++++++------------------------------ fs/btrfs/ordered-data.c | 19 +++---- fs/btrfs/ordered-data.h | 25 ++------- fs/btrfs/relocation.c | 14 +---- fs/btrfs/scrub.c | 16 ++---- 5 files changed, 76 insertions(+), 142 deletions(-) diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c index b193bf324a41..a7bfc9541803 100644 --- a/fs/btrfs/file-item.c +++ b/fs/btrfs/file-item.c @@ -34,8 +34,7 @@ #define MAX_ORDERED_SUM_BYTES(r) ((PAGE_SIZE - \ sizeof(struct btrfs_ordered_sum)) / \ - sizeof(struct btrfs_sector_sum) * \ - (r)->sectorsize - (r)->sectorsize) + sizeof(u32) * (r)->sectorsize) int btrfs_insert_file_extent(struct btrfs_trans_handle *trans, struct btrfs_root *root, @@ -297,7 +296,6 @@ int btrfs_lookup_csums_range(struct btrfs_root *root, u64 start, u64 end, struct btrfs_path *path; struct extent_buffer *leaf; struct btrfs_ordered_sum *sums; - struct btrfs_sector_sum *sector_sum; struct btrfs_csum_item *item; LIST_HEAD(tmplist); unsigned long offset; @@ -368,34 +366,28 @@ int btrfs_lookup_csums_range(struct btrfs_root *root, u64 start, u64 end, struct btrfs_csum_item); while (start < csum_end) { size = min_t(size_t, csum_end - start, - MAX_ORDERED_SUM_BYTES(root)); + MAX_ORDERED_SUM_BYTES(root)); sums = kzalloc(btrfs_ordered_sum_size(root, size), - GFP_NOFS); + GFP_NOFS); if (!sums) { ret = -ENOMEM; goto fail; } - sector_sum = sums->sums; sums->bytenr = start; - sums->len = size; + sums->len = (int)size; offset = (start - key.offset) >> root->fs_info->sb->s_blocksize_bits; offset *= csum_size; + size >>= root->fs_info->sb->s_blocksize_bits; - while (size > 0) { - read_extent_buffer(path->nodes[0], - §or_sum->sum, - ((unsigned long)item) + - offset, csum_size); - sector_sum->bytenr = start; - - size -= root->sectorsize; - start += root->sectorsize; - offset += csum_size; - sector_sum++; - } + read_extent_buffer(path->nodes[0], + sums->sums, + ((unsigned long)item) + offset, + csum_size * size); + + start += root->sectorsize * size; list_add_tail(&sums->list, &tmplist); } path->slots[0]++; @@ -417,23 +409,20 @@ int btrfs_csum_one_bio(struct btrfs_root *root, struct inode *inode, struct bio *bio, u64 file_start, int contig) { struct btrfs_ordered_sum *sums; - struct btrfs_sector_sum *sector_sum; struct btrfs_ordered_extent *ordered; char *data; struct bio_vec *bvec = bio->bi_io_vec; int bio_index = 0; + int index; unsigned long total_bytes = 0; unsigned long this_sum_bytes = 0; u64 offset; - u64 disk_bytenr; WARN_ON(bio->bi_vcnt <= 0); sums = kzalloc(btrfs_ordered_sum_size(root, bio->bi_size), GFP_NOFS); if (!sums) return -ENOMEM; - sector_sum = sums->sums; - disk_bytenr = (u64)bio->bi_sector << 9; sums->len = bio->bi_size; INIT_LIST_HEAD(&sums->list); @@ -444,7 +433,8 @@ int btrfs_csum_one_bio(struct btrfs_root *root, struct inode *inode, ordered = btrfs_lookup_ordered_extent(inode, offset); BUG_ON(!ordered); /* Logic error */ - sums->bytenr = ordered->start; + sums->bytenr = (u64)bio->bi_sector << 9; + index = 0; while (bio_index < bio->bi_vcnt) { if (!contig) @@ -463,28 +453,27 @@ int btrfs_csum_one_bio(struct btrfs_root *root, struct inode *inode, sums = kzalloc(btrfs_ordered_sum_size(root, bytes_left), GFP_NOFS); BUG_ON(!sums); /* -ENOMEM */ - sector_sum = sums->sums; sums->len = bytes_left; ordered = btrfs_lookup_ordered_extent(inode, offset); BUG_ON(!ordered); /* Logic error */ - sums->bytenr = ordered->start; + sums->bytenr = ((u64)bio->bi_sector << 9) + + total_bytes; + index = 0; } data = kmap_atomic(bvec->bv_page); - sector_sum->sum = ~(u32)0; - sector_sum->sum = btrfs_csum_data(data + bvec->bv_offset, - sector_sum->sum, - bvec->bv_len); + sums->sums[index] = ~(u32)0; + sums->sums[index] = btrfs_csum_data(data + bvec->bv_offset, + sums->sums[index], + bvec->bv_len); kunmap_atomic(data); - btrfs_csum_final(sector_sum->sum, - (char *)§or_sum->sum); - sector_sum->bytenr = disk_bytenr; + btrfs_csum_final(sums->sums[index], + (char *)(sums->sums + index)); - sector_sum++; bio_index++; + index++; total_bytes += bvec->bv_len; this_sum_bytes += bvec->bv_len; - disk_bytenr += bvec->bv_len; offset += bvec->bv_len; bvec++; } @@ -672,62 +661,46 @@ out: return ret; } -static u64 btrfs_sector_sum_left(struct btrfs_ordered_sum *sums, - struct btrfs_sector_sum *sector_sum, - u64 total_bytes, u64 sectorsize) -{ - u64 tmp = sectorsize; - u64 next_sector = sector_sum->bytenr; - struct btrfs_sector_sum *next = sector_sum + 1; - - while ((tmp + total_bytes) < sums->len) { - if (next_sector + sectorsize != next->bytenr) - break; - tmp += sectorsize; - next_sector = next->bytenr; - next++; - } - return tmp; -} - int btrfs_csum_file_blocks(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_ordered_sum *sums) { - u64 bytenr; - int ret; struct btrfs_key file_key; struct btrfs_key found_key; - u64 next_offset; - u64 total_bytes = 0; - int found_next; struct btrfs_path *path; struct btrfs_csum_item *item; struct btrfs_csum_item *item_end; struct extent_buffer *leaf = NULL; + u64 next_offset; + u64 total_bytes = 0; u64 csum_offset; - struct btrfs_sector_sum *sector_sum; + u64 bytenr; u32 nritems; u32 ins_size; + int index = 0; + int found_next; + int ret; u16 csum_size = btrfs_super_csum_size(root->fs_info->super_copy); path = btrfs_alloc_path(); if (!path) return -ENOMEM; - - sector_sum = sums->sums; again: next_offset = (u64)-1; found_next = 0; + bytenr = sums->bytenr + total_bytes; file_key.objectid = BTRFS_EXTENT_CSUM_OBJECTID; - file_key.offset = sector_sum->bytenr; - bytenr = sector_sum->bytenr; + file_key.offset = bytenr; btrfs_set_key_type(&file_key, BTRFS_EXTENT_CSUM_KEY); - item = btrfs_lookup_csum(trans, root, path, sector_sum->bytenr, 1); + item = btrfs_lookup_csum(trans, root, path, bytenr, 1); if (!IS_ERR(item)) { - leaf = path->nodes[0]; ret = 0; + leaf = path->nodes[0]; + item_end = btrfs_item_ptr(leaf, path->slots[0], + struct btrfs_csum_item); + item_end = (struct btrfs_csum_item *)((char *)item_end + + btrfs_item_size_nr(leaf, path->slots[0])); goto found; } ret = PTR_ERR(item); @@ -807,8 +780,7 @@ again: free_space = btrfs_leaf_free_space(root, leaf) - sizeof(struct btrfs_item) - csum_size; - tmp = btrfs_sector_sum_left(sums, sector_sum, total_bytes, - root->sectorsize); + tmp = sums->len - total_bytes; tmp >>= root->fs_info->sb->s_blocksize_bits; WARN_ON(tmp < 1); @@ -822,6 +794,7 @@ again: diff *= csum_size; btrfs_extend_item(root, path, diff); + ret = 0; goto csum; } @@ -831,8 +804,7 @@ insert: if (found_next) { u64 tmp; - tmp = btrfs_sector_sum_left(sums, sector_sum, total_bytes, - root->sectorsize); + tmp = sums->len - total_bytes; tmp >>= root->fs_info->sb->s_blocksize_bits; tmp = min(tmp, (next_offset - file_key.offset) >> root->fs_info->sb->s_blocksize_bits); @@ -853,31 +825,25 @@ insert: WARN_ON(1); goto fail_unlock; } -csum: leaf = path->nodes[0]; +csum: item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_csum_item); - ret = 0; + item_end = (struct btrfs_csum_item *)((unsigned char *)item + + btrfs_item_size_nr(leaf, path->slots[0])); item = (struct btrfs_csum_item *)((unsigned char *)item + csum_offset * csum_size); found: - item_end = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_csum_item); - item_end = (struct btrfs_csum_item *)((unsigned char *)item_end + - btrfs_item_size_nr(leaf, path->slots[0])); -next_sector: - - write_extent_buffer(leaf, §or_sum->sum, (unsigned long)item, csum_size); - - total_bytes += root->sectorsize; - sector_sum++; - if (total_bytes < sums->len) { - item = (struct btrfs_csum_item *)((char *)item + - csum_size); - if (item < item_end && bytenr + PAGE_CACHE_SIZE == - sector_sum->bytenr) { - bytenr = sector_sum->bytenr; - goto next_sector; - } - } + ins_size = (u32)(sums->len - total_bytes) >> + root->fs_info->sb->s_blocksize_bits; + ins_size *= csum_size; + ins_size = min_t(u32, (unsigned long)item_end - (unsigned long)item, + ins_size); + write_extent_buffer(leaf, sums->sums + index, (unsigned long)item, + ins_size); + + ins_size /= csum_size; + total_bytes += ins_size * root->sectorsize; + index += ins_size; btrfs_mark_buffer_dirty(path->nodes[0]); if (total_bytes < sums->len) { diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c index 665c640e3ea6..81369827e514 100644 --- a/fs/btrfs/ordered-data.c +++ b/fs/btrfs/ordered-data.c @@ -1032,7 +1032,6 @@ int btrfs_find_ordered_sum(struct inode *inode, u64 offset, u64 disk_bytenr, u32 *sum, int len) { struct btrfs_ordered_sum *ordered_sum; - struct btrfs_sector_sum *sector_sums; struct btrfs_ordered_extent *ordered; struct btrfs_ordered_inode_tree *tree = &BTRFS_I(inode)->ordered_tree; unsigned long num_sectors; @@ -1050,18 +1049,16 @@ int btrfs_find_ordered_sum(struct inode *inode, u64 offset, u64 disk_bytenr, disk_bytenr < ordered_sum->bytenr + ordered_sum->len) { i = (disk_bytenr - ordered_sum->bytenr) >> inode->i_sb->s_blocksize_bits; - sector_sums = ordered_sum->sums + i; num_sectors = ordered_sum->len >> inode->i_sb->s_blocksize_bits; - for (; i < num_sectors; i++) { - if (sector_sums[i].bytenr == disk_bytenr) { - sum[index] = sector_sums[i].sum; - index++; - if (index == len) - goto out; - disk_bytenr += sectorsize; - } - } + num_sectors = min_t(int, len - index, num_sectors - i); + memcpy(sum + index, ordered_sum->sums + i, + num_sectors); + + index += (int)num_sectors; + if (index == len) + goto out; + disk_bytenr += num_sectors * sectorsize; } } out: diff --git a/fs/btrfs/ordered-data.h b/fs/btrfs/ordered-data.h index d082d43e00e5..68844d59ee6f 100644 --- a/fs/btrfs/ordered-data.h +++ b/fs/btrfs/ordered-data.h @@ -26,18 +26,6 @@ struct btrfs_ordered_inode_tree { struct rb_node *last; }; -/* - * these are used to collect checksums done just before bios submission. - * They are attached via a list into the ordered extent, and - * checksum items are inserted into the tree after all the blocks in - * the ordered extent are on disk - */ -struct btrfs_sector_sum { - /* bytenr on disk */ - u64 bytenr; - u32 sum; -}; - struct btrfs_ordered_sum { /* bytenr is the start of this extent on disk */ u64 bytenr; @@ -45,10 +33,10 @@ struct btrfs_ordered_sum { /* * this is the length in bytes covered by the sums array below. */ - unsigned long len; + int len; struct list_head list; - /* last field is a variable length array of btrfs_sector_sums */ - struct btrfs_sector_sum sums[]; + /* last field is a variable length array of csums */ + u32 sums[]; }; /* @@ -149,11 +137,8 @@ struct btrfs_ordered_extent { static inline int btrfs_ordered_sum_size(struct btrfs_root *root, unsigned long bytes) { - unsigned long num_sectors = (bytes + root->sectorsize - 1) / - root->sectorsize; - num_sectors++; - return sizeof(struct btrfs_ordered_sum) + - num_sectors * sizeof(struct btrfs_sector_sum); + int num_sectors = (int)DIV_ROUND_UP(bytes, root->sectorsize); + return sizeof(struct btrfs_ordered_sum) + num_sectors * sizeof(u32); } static inline void diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index d91f106df665..12096496cc99 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -4458,10 +4458,8 @@ out: int btrfs_reloc_clone_csums(struct inode *inode, u64 file_pos, u64 len) { struct btrfs_ordered_sum *sums; - struct btrfs_sector_sum *sector_sum; struct btrfs_ordered_extent *ordered; struct btrfs_root *root = BTRFS_I(inode)->root; - size_t offset; int ret; u64 disk_bytenr; LIST_HEAD(list); @@ -4475,19 +4473,13 @@ int btrfs_reloc_clone_csums(struct inode *inode, u64 file_pos, u64 len) if (ret) goto out; + disk_bytenr = ordered->start; while (!list_empty(&list)) { sums = list_entry(list.next, struct btrfs_ordered_sum, list); list_del_init(&sums->list); - sector_sum = sums->sums; - sums->bytenr = ordered->start; - - offset = 0; - while (offset < sums->len) { - sector_sum->bytenr += ordered->start - disk_bytenr; - sector_sum++; - offset += root->sectorsize; - } + sums->bytenr = disk_bytenr; + disk_bytenr += sums->len; btrfs_add_ordered_sum(inode, ordered, sums); } diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c index cb308a3a9300..63144e4ca9e1 100644 --- a/fs/btrfs/scrub.c +++ b/fs/btrfs/scrub.c @@ -2126,8 +2126,7 @@ static int scrub_find_csum(struct scrub_ctx *sctx, u64 logical, u64 len, u8 *csum) { struct btrfs_ordered_sum *sum = NULL; - int ret = 0; - unsigned long i; + unsigned long index; unsigned long num_sectors; while (!list_empty(&sctx->csum_list)) { @@ -2146,19 +2145,14 @@ static int scrub_find_csum(struct scrub_ctx *sctx, u64 logical, u64 len, if (!sum) return 0; + index = ((u32)(logical - sum->bytenr)) / sctx->sectorsize; num_sectors = sum->len / sctx->sectorsize; - for (i = 0; i < num_sectors; ++i) { - if (sum->sums[i].bytenr == logical) { - memcpy(csum, &sum->sums[i].sum, sctx->csum_size); - ret = 1; - break; - } - } - if (ret && i == num_sectors - 1) { + memcpy(csum, sum->sums + index, sctx->csum_size); + if (index == num_sectors - 1) { list_del(&sum->list); kfree(sum); } - return ret; + return 1; } /* scrub extent tries to collect up to 64 kB for each bio */ -- cgit v1.2.3 From e6da5d2ec9870ddadf4dbc6a1835470636df25bb Mon Sep 17 00:00:00 2001 From: Miao Xie Date: Wed, 19 Jun 2013 18:19:17 +0800 Subject: Btrfs: cleanup redundant code in btrfs_submit_direct() Signed-off-by: Miao Xie Signed-off-by: Josef Bacik --- fs/btrfs/inode.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 4d7c02258390..0a43d42268f7 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -7148,7 +7148,6 @@ static void btrfs_submit_direct(int rw, struct bio *dio_bio, { struct btrfs_root *root = BTRFS_I(inode)->root; struct btrfs_dio_private *dip; - struct bio_vec *bvec = dio_bio->bi_io_vec; struct bio *io_bio; int skip_sum; int write = rw & REQ_WRITE; @@ -7170,16 +7169,9 @@ static void btrfs_submit_direct(int rw, struct bio *dio_bio, } dip->private = dio_bio->bi_private; - io_bio->bi_private = dio_bio->bi_private; dip->inode = inode; dip->logical_offset = file_offset; - - dip->bytes = 0; - do { - dip->bytes += bvec->bv_len; - bvec++; - } while (bvec <= (dio_bio->bi_io_vec + dio_bio->bi_vcnt - 1)); - + dip->bytes = dio_bio->bi_size; dip->disk_bytenr = (u64)dio_bio->bi_sector << 9; io_bio->bi_private = dip; dip->errors = 0; -- cgit v1.2.3 From a70c6172e714c70439b8321a201635f56a178913 Mon Sep 17 00:00:00 2001 From: Miao Xie Date: Wed, 19 Jun 2013 18:20:17 +0800 Subject: Btrfs: fix wrong mirror number tuning Now reading the data from the target device of the replace operation is allowed, so the mirror number that is greater than the stripes number of a chunk is valid, we will tune it when we find there is no target device later. Fix it. Signed-off-by: Miao Xie Signed-off-by: Josef Bacik --- fs/btrfs/volumes.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index c58bf19a8a48..7789598eeb75 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -4420,9 +4420,6 @@ static int __btrfs_map_block(struct btrfs_fs_info *fs_info, int rw, map = (struct map_lookup *)em->bdev; offset = logical - em->start; - if (mirror_num > map->num_stripes) - mirror_num = 0; - stripe_len = map->stripe_len; stripe_nr = offset; /* -- cgit v1.2.3 From 68a7342c51c950428d90cd15da898c63d6c33267 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Thu, 27 Jun 2013 11:32:16 -0400 Subject: Btrfs: cleanup orphaned root orphan item I hit a weird problem were my root item had been deleted but the orphan item had not. This isn't necessarily a problem, but it keeps the file system from being mounted. To fix this we just need to axe the orphan item if we can't find the fs root when we're putting them altogether. With this patch I was able to successfully mount my file system. Thanks, Signed-off-by: Josef Bacik --- fs/btrfs/root-tree.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/root-tree.c b/fs/btrfs/root-tree.c index 723a5312763f..ffb1036ef10d 100644 --- a/fs/btrfs/root-tree.c +++ b/fs/btrfs/root-tree.c @@ -228,6 +228,10 @@ int btrfs_find_orphan_roots(struct btrfs_root *tree_root) struct btrfs_root *root; int err = 0; int ret; + bool can_recover = true; + + if (tree_root->fs_info->sb->s_flags & MS_RDONLY) + can_recover = false; path = btrfs_alloc_path(); if (!path) @@ -268,9 +272,32 @@ int btrfs_find_orphan_roots(struct btrfs_root *tree_root) key.offset++; root = btrfs_read_fs_root(tree_root, &root_key); - if (IS_ERR(root)) { - err = PTR_ERR(root); + err = PTR_RET(root); + if (err && err != -ENOENT) { break; + } else if (err == -ENOENT) { + struct btrfs_trans_handle *trans; + + btrfs_release_path(path); + + trans = btrfs_join_transaction(tree_root); + if (IS_ERR(trans)) { + err = PTR_ERR(trans); + btrfs_error(tree_root->fs_info, err, + "Failed to start trans to delete " + "orphan item"); + break; + } + err = btrfs_del_orphan_item(trans, tree_root, + root_key.objectid); + btrfs_end_transaction(trans, tree_root); + if (err) { + btrfs_error(tree_root->fs_info, err, + "Failed to delete root orphan " + "item"); + break; + } + continue; } if (btrfs_root_refs(&root->root_item) == 0) { -- cgit v1.2.3 From 6df9a95e63395f595d0d1eb5d561dd6c91c40270 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Thu, 27 Jun 2013 13:22:46 -0400 Subject: Btrfs: make the chunk allocator completely tree lockless When adjusting the enospc rules for relocation I ran into a deadlock because we were relocating the only system chunk and that forced us to try and allocate a new system chunk while holding locks in the chunk tree, which caused us to deadlock. To fix this I've moved all of the dev extent addition and chunk addition out to the delayed chunk completion stuff. We still keep the in-memory stuff which makes sure everything is consistent. One change I had to make was to search the commit root of the device tree to find a free dev extent, and hold onto any chunk em's that we allocated in that transaction so we do not allocate the same dev extent twice. This has the side effect of fixing a bug with balance that has been there ever since balance existed. Basically you can free a block group and it's dev extent and then immediately allocate that dev extent for a new block group and write stuff to that dev extent, all within the same transaction. So if you happen to crash during a balance you could come back to a completely broken file system. This patch should keep these sort of things from happening in the future since we won't be able to allocate free'd dev extents until after the transaction commits. This has passed all of the xfstests and my super annoying stress test followed by a balance. Thanks, Signed-off-by: Josef Bacik --- fs/btrfs/extent-tree.c | 15 ++- fs/btrfs/transaction.c | 9 ++ fs/btrfs/transaction.h | 1 + fs/btrfs/volumes.c | 304 ++++++++++++++++++++++--------------------------- fs/btrfs/volumes.h | 6 +- 5 files changed, 166 insertions(+), 169 deletions(-) diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 11ba82e43e8b..0236de711989 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -7950,6 +7950,7 @@ int btrfs_can_relocate(struct btrfs_root *root, u64 bytenr) struct btrfs_space_info *space_info; struct btrfs_fs_devices *fs_devices = root->fs_info->fs_devices; struct btrfs_device *device; + struct btrfs_trans_handle *trans; u64 min_free; u64 dev_min = 1; u64 dev_nr = 0; @@ -8036,6 +8037,13 @@ int btrfs_can_relocate(struct btrfs_root *root, u64 bytenr) do_div(min_free, dev_min); } + /* We need to do this so that we can look at pending chunks */ + trans = btrfs_join_transaction(root); + if (IS_ERR(trans)) { + ret = PTR_ERR(trans); + goto out; + } + mutex_lock(&root->fs_info->chunk_mutex); list_for_each_entry(device, &fs_devices->alloc_list, dev_alloc_list) { u64 dev_offset; @@ -8046,7 +8054,7 @@ int btrfs_can_relocate(struct btrfs_root *root, u64 bytenr) */ if (device->total_bytes > device->bytes_used + min_free && !device->is_tgtdev_for_dev_replace) { - ret = find_free_dev_extent(device, min_free, + ret = find_free_dev_extent(trans, device, min_free, &dev_offset, NULL); if (!ret) dev_nr++; @@ -8058,6 +8066,7 @@ int btrfs_can_relocate(struct btrfs_root *root, u64 bytenr) } } mutex_unlock(&root->fs_info->chunk_mutex); + btrfs_end_transaction(trans, root); out: btrfs_put_block_group(block_group); return ret; @@ -8423,6 +8432,10 @@ void btrfs_create_pending_block_groups(struct btrfs_trans_handle *trans, sizeof(item)); if (ret) btrfs_abort_transaction(trans, extent_root, ret); + ret = btrfs_finish_chunk_alloc(trans, extent_root, + key.objectid, key.offset); + if (ret) + btrfs_abort_transaction(trans, extent_root, ret); } } diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index bcfa32c91b5d..d58cce77fc6c 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -63,6 +63,14 @@ static void put_transaction(struct btrfs_transaction *transaction) if (atomic_dec_and_test(&transaction->use_count)) { BUG_ON(!list_empty(&transaction->list)); WARN_ON(transaction->delayed_refs.root.rb_node); + while (!list_empty(&transaction->pending_chunks)) { + struct extent_map *em; + + em = list_first_entry(&transaction->pending_chunks, + struct extent_map, list); + list_del_init(&em->list); + free_extent_map(em); + } kmem_cache_free(btrfs_transaction_cachep, transaction); } } @@ -202,6 +210,7 @@ loop: INIT_LIST_HEAD(&cur_trans->pending_snapshots); INIT_LIST_HEAD(&cur_trans->ordered_operations); + INIT_LIST_HEAD(&cur_trans->pending_chunks); list_add_tail(&cur_trans->list, &fs_info->trans_list); extent_io_tree_init(&cur_trans->dirty_pages, fs_info->btree_inode->i_mapping); diff --git a/fs/btrfs/transaction.h b/fs/btrfs/transaction.h index 66d2a6ccbf05..005b0375d18c 100644 --- a/fs/btrfs/transaction.h +++ b/fs/btrfs/transaction.h @@ -56,6 +56,7 @@ struct btrfs_transaction { wait_queue_head_t commit_wait; struct list_head pending_snapshots; struct list_head ordered_operations; + struct list_head pending_chunks; struct btrfs_delayed_ref_root delayed_refs; int aborted; }; diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 7789598eeb75..b2d1eacc07c9 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -982,6 +982,35 @@ out: return ret; } +static int contains_pending_extent(struct btrfs_trans_handle *trans, + struct btrfs_device *device, + u64 *start, u64 len) +{ + struct extent_map *em; + int ret = 0; + + list_for_each_entry(em, &trans->transaction->pending_chunks, list) { + struct map_lookup *map; + int i; + + map = (struct map_lookup *)em->bdev; + for (i = 0; i < map->num_stripes; i++) { + if (map->stripes[i].dev != device) + continue; + if (map->stripes[i].physical >= *start + len || + map->stripes[i].physical + em->orig_block_len <= + *start) + continue; + *start = map->stripes[i].physical + + em->orig_block_len; + ret = 1; + } + } + + return ret; +} + + /* * find_free_dev_extent - find free space in the specified device * @device: the device which we search the free space in @@ -1002,7 +1031,8 @@ out: * But if we don't find suitable free space, it is used to store the size of * the max free space. */ -int find_free_dev_extent(struct btrfs_device *device, u64 num_bytes, +int find_free_dev_extent(struct btrfs_trans_handle *trans, + struct btrfs_device *device, u64 num_bytes, u64 *start, u64 *len) { struct btrfs_key key; @@ -1026,21 +1056,22 @@ int find_free_dev_extent(struct btrfs_device *device, u64 num_bytes, */ search_start = max(root->fs_info->alloc_start, 1024ull * 1024); + path = btrfs_alloc_path(); + if (!path) + return -ENOMEM; +again: max_hole_start = search_start; max_hole_size = 0; hole_size = 0; if (search_start >= search_end || device->is_tgtdev_for_dev_replace) { ret = -ENOSPC; - goto error; + goto out; } - path = btrfs_alloc_path(); - if (!path) { - ret = -ENOMEM; - goto error; - } path->reada = 2; + path->search_commit_root = 1; + path->skip_locking = 1; key.objectid = device->devid; key.offset = search_start; @@ -1081,6 +1112,15 @@ int find_free_dev_extent(struct btrfs_device *device, u64 num_bytes, if (key.offset > search_start) { hole_size = key.offset - search_start; + /* + * Have to check before we set max_hole_start, otherwise + * we could end up sending back this offset anyway. + */ + if (contains_pending_extent(trans, device, + &search_start, + hole_size)) + hole_size = 0; + if (hole_size > max_hole_size) { max_hole_start = search_start; max_hole_size = hole_size; @@ -1124,6 +1164,11 @@ next: max_hole_size = hole_size; } + if (contains_pending_extent(trans, device, &search_start, hole_size)) { + btrfs_release_path(path); + goto again; + } + /* See above. */ if (hole_size < num_bytes) ret = -ENOSPC; @@ -1132,7 +1177,6 @@ next: out: btrfs_free_path(path); -error: *start = max_hole_start; if (len) *len = max_hole_size; @@ -1244,47 +1288,22 @@ out: return ret; } -static noinline int find_next_chunk(struct btrfs_root *root, - u64 objectid, u64 *offset) +static u64 find_next_chunk(struct btrfs_fs_info *fs_info) { - struct btrfs_path *path; - int ret; - struct btrfs_key key; - struct btrfs_chunk *chunk; - struct btrfs_key found_key; - - path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - - key.objectid = objectid; - key.offset = (u64)-1; - key.type = BTRFS_CHUNK_ITEM_KEY; - - ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); - if (ret < 0) - goto error; - - BUG_ON(ret == 0); /* Corruption */ + struct extent_map_tree *em_tree; + struct extent_map *em; + struct rb_node *n; + u64 ret = 0; - ret = btrfs_previous_item(root, path, 0, BTRFS_CHUNK_ITEM_KEY); - if (ret) { - *offset = 0; - } else { - btrfs_item_key_to_cpu(path->nodes[0], &found_key, - path->slots[0]); - if (found_key.objectid != objectid) - *offset = 0; - else { - chunk = btrfs_item_ptr(path->nodes[0], path->slots[0], - struct btrfs_chunk); - *offset = found_key.offset + - btrfs_chunk_length(path->nodes[0], chunk); - } + em_tree = &fs_info->mapping_tree.map_tree; + read_lock(&em_tree->lock); + n = rb_last(&em_tree->map); + if (n) { + em = rb_entry(n, struct extent_map, rb_node); + ret = em->start + em->len; } - ret = 0; -error: - btrfs_free_path(path); + read_unlock(&em_tree->lock); + return ret; } @@ -3666,10 +3685,8 @@ static void check_raid56_incompat_flag(struct btrfs_fs_info *info, u64 type) } static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans, - struct btrfs_root *extent_root, - struct map_lookup **map_ret, - u64 *num_bytes_out, u64 *stripe_size_out, - u64 start, u64 type) + struct btrfs_root *extent_root, u64 start, + u64 type) { struct btrfs_fs_info *info = extent_root->fs_info; struct btrfs_fs_devices *fs_devices = info->fs_devices; @@ -3776,7 +3793,7 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans, if (total_avail == 0) continue; - ret = find_free_dev_extent(device, + ret = find_free_dev_extent(trans, device, max_stripe_size * dev_stripes, &dev_offset, &max_avail); if (ret && ret != -ENOSPC) @@ -3888,12 +3905,8 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans, map->type = type; map->sub_stripes = sub_stripes; - *map_ret = map; num_bytes = stripe_size * data_stripes; - *stripe_size_out = stripe_size; - *num_bytes_out = num_bytes; - trace_btrfs_chunk_alloc(info->chunk_root, map, start, num_bytes); em = alloc_extent_map(); @@ -3906,38 +3919,26 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans, em->len = num_bytes; em->block_start = 0; em->block_len = em->len; + em->orig_block_len = stripe_size; em_tree = &extent_root->fs_info->mapping_tree.map_tree; write_lock(&em_tree->lock); ret = add_extent_mapping(em_tree, em, 0); + if (!ret) { + list_add_tail(&em->list, &trans->transaction->pending_chunks); + atomic_inc(&em->refs); + } write_unlock(&em_tree->lock); if (ret) { free_extent_map(em); goto error; } - for (i = 0; i < map->num_stripes; ++i) { - struct btrfs_device *device; - u64 dev_offset; - - device = map->stripes[i].dev; - dev_offset = map->stripes[i].physical; - - ret = btrfs_alloc_dev_extent(trans, device, - info->chunk_root->root_key.objectid, - BTRFS_FIRST_CHUNK_TREE_OBJECTID, - start, dev_offset, stripe_size); - if (ret) - goto error_dev_extent; - } - ret = btrfs_make_block_group(trans, extent_root, 0, type, BTRFS_FIRST_CHUNK_TREE_OBJECTID, start, num_bytes); - if (ret) { - i = map->num_stripes - 1; - goto error_dev_extent; - } + if (ret) + goto error_del_extent; free_extent_map(em); check_raid56_incompat_flag(extent_root->fs_info, type); @@ -3945,18 +3946,7 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans, kfree(devices_info); return 0; -error_dev_extent: - for (; i >= 0; i--) { - struct btrfs_device *device; - int err; - - device = map->stripes[i].dev; - err = btrfs_free_dev_extent(trans, device, start); - if (err) { - btrfs_abort_transaction(trans, extent_root, err); - break; - } - } +error_del_extent: write_lock(&em_tree->lock); remove_extent_mapping(em_tree, em); write_unlock(&em_tree->lock); @@ -3971,33 +3961,68 @@ error: return ret; } -static int __finish_chunk_alloc(struct btrfs_trans_handle *trans, +int btrfs_finish_chunk_alloc(struct btrfs_trans_handle *trans, struct btrfs_root *extent_root, - struct map_lookup *map, u64 chunk_offset, - u64 chunk_size, u64 stripe_size) + u64 chunk_offset, u64 chunk_size) { - u64 dev_offset; struct btrfs_key key; struct btrfs_root *chunk_root = extent_root->fs_info->chunk_root; struct btrfs_device *device; struct btrfs_chunk *chunk; struct btrfs_stripe *stripe; - size_t item_size = btrfs_chunk_item_size(map->num_stripes); - int index = 0; + struct extent_map_tree *em_tree; + struct extent_map *em; + struct map_lookup *map; + size_t item_size; + u64 dev_offset; + u64 stripe_size; + int i = 0; int ret; + em_tree = &extent_root->fs_info->mapping_tree.map_tree; + read_lock(&em_tree->lock); + em = lookup_extent_mapping(em_tree, chunk_offset, chunk_size); + read_unlock(&em_tree->lock); + + if (!em) { + btrfs_crit(extent_root->fs_info, "unable to find logical " + "%Lu len %Lu", chunk_offset, chunk_size); + return -EINVAL; + } + + if (em->start != chunk_offset || em->len != chunk_size) { + btrfs_crit(extent_root->fs_info, "found a bad mapping, wanted" + " %Lu-%Lu, found %Lu-%Lu\n", chunk_offset, + chunk_size, em->start, em->len); + free_extent_map(em); + return -EINVAL; + } + + map = (struct map_lookup *)em->bdev; + item_size = btrfs_chunk_item_size(map->num_stripes); + stripe_size = em->orig_block_len; + chunk = kzalloc(item_size, GFP_NOFS); - if (!chunk) - return -ENOMEM; + if (!chunk) { + ret = -ENOMEM; + goto out; + } + + for (i = 0; i < map->num_stripes; i++) { + device = map->stripes[i].dev; + dev_offset = map->stripes[i].physical; - index = 0; - while (index < map->num_stripes) { - device = map->stripes[index].dev; device->bytes_used += stripe_size; ret = btrfs_update_device(trans, device); if (ret) - goto out_free; - index++; + goto out; + ret = btrfs_alloc_dev_extent(trans, device, + chunk_root->root_key.objectid, + BTRFS_FIRST_CHUNK_TREE_OBJECTID, + chunk_offset, dev_offset, + stripe_size); + if (ret) + goto out; } spin_lock(&extent_root->fs_info->free_chunk_lock); @@ -4005,17 +4030,15 @@ static int __finish_chunk_alloc(struct btrfs_trans_handle *trans, map->num_stripes); spin_unlock(&extent_root->fs_info->free_chunk_lock); - index = 0; stripe = &chunk->stripe; - while (index < map->num_stripes) { - device = map->stripes[index].dev; - dev_offset = map->stripes[index].physical; + for (i = 0; i < map->num_stripes; i++) { + device = map->stripes[i].dev; + dev_offset = map->stripes[i].physical; btrfs_set_stack_stripe_devid(stripe, device->devid); btrfs_set_stack_stripe_offset(stripe, dev_offset); memcpy(stripe->dev_uuid, device->uuid, BTRFS_UUID_SIZE); stripe++; - index++; } btrfs_set_stack_chunk_length(chunk, chunk_size); @@ -4033,7 +4056,6 @@ static int __finish_chunk_alloc(struct btrfs_trans_handle *trans, key.offset = chunk_offset; ret = btrfs_insert_item(trans, chunk_root, &key, chunk, item_size); - if (ret == 0 && map->type & BTRFS_BLOCK_GROUP_SYSTEM) { /* * TODO: Cleanup of inserted chunk root in case of @@ -4043,8 +4065,9 @@ static int __finish_chunk_alloc(struct btrfs_trans_handle *trans, item_size); } -out_free: +out: kfree(chunk); + free_extent_map(em); return ret; } @@ -4059,27 +4082,9 @@ int btrfs_alloc_chunk(struct btrfs_trans_handle *trans, struct btrfs_root *extent_root, u64 type) { u64 chunk_offset; - u64 chunk_size; - u64 stripe_size; - struct map_lookup *map; - struct btrfs_root *chunk_root = extent_root->fs_info->chunk_root; - int ret; - ret = find_next_chunk(chunk_root, BTRFS_FIRST_CHUNK_TREE_OBJECTID, - &chunk_offset); - if (ret) - return ret; - - ret = __btrfs_alloc_chunk(trans, extent_root, &map, &chunk_size, - &stripe_size, chunk_offset, type); - if (ret) - return ret; - - ret = __finish_chunk_alloc(trans, extent_root, map, chunk_offset, - chunk_size, stripe_size); - if (ret) - return ret; - return 0; + chunk_offset = find_next_chunk(extent_root->fs_info); + return __btrfs_alloc_chunk(trans, extent_root, chunk_offset, type); } static noinline int init_first_rw_device(struct btrfs_trans_handle *trans, @@ -4088,66 +4093,31 @@ static noinline int init_first_rw_device(struct btrfs_trans_handle *trans, { u64 chunk_offset; u64 sys_chunk_offset; - u64 chunk_size; - u64 sys_chunk_size; - u64 stripe_size; - u64 sys_stripe_size; u64 alloc_profile; - struct map_lookup *map; - struct map_lookup *sys_map; struct btrfs_fs_info *fs_info = root->fs_info; struct btrfs_root *extent_root = fs_info->extent_root; int ret; - ret = find_next_chunk(fs_info->chunk_root, - BTRFS_FIRST_CHUNK_TREE_OBJECTID, &chunk_offset); - if (ret) - return ret; - + chunk_offset = find_next_chunk(fs_info); alloc_profile = btrfs_get_alloc_profile(extent_root, 0); - ret = __btrfs_alloc_chunk(trans, extent_root, &map, &chunk_size, - &stripe_size, chunk_offset, alloc_profile); + ret = __btrfs_alloc_chunk(trans, extent_root, chunk_offset, + alloc_profile); if (ret) return ret; - sys_chunk_offset = chunk_offset + chunk_size; - + sys_chunk_offset = find_next_chunk(root->fs_info); alloc_profile = btrfs_get_alloc_profile(fs_info->chunk_root, 0); - ret = __btrfs_alloc_chunk(trans, extent_root, &sys_map, - &sys_chunk_size, &sys_stripe_size, - sys_chunk_offset, alloc_profile); + ret = __btrfs_alloc_chunk(trans, extent_root, sys_chunk_offset, + alloc_profile); if (ret) { btrfs_abort_transaction(trans, root, ret); goto out; } ret = btrfs_add_device(trans, fs_info->chunk_root, device); - if (ret) { - btrfs_abort_transaction(trans, root, ret); - goto out; - } - - /* - * Modifying chunk tree needs allocating new blocks from both - * system block group and metadata block group. So we only can - * do operations require modifying the chunk tree after both - * block groups were created. - */ - ret = __finish_chunk_alloc(trans, extent_root, map, chunk_offset, - chunk_size, stripe_size); - if (ret) { - btrfs_abort_transaction(trans, root, ret); - goto out; - } - - ret = __finish_chunk_alloc(trans, extent_root, sys_map, - sys_chunk_offset, sys_chunk_size, - sys_stripe_size); if (ret) btrfs_abort_transaction(trans, root, ret); - out: - return ret; } diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 857acd34ccde..86705583480d 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -316,7 +316,8 @@ int btrfs_recover_balance(struct btrfs_fs_info *fs_info); int btrfs_pause_balance(struct btrfs_fs_info *fs_info); int btrfs_cancel_balance(struct btrfs_fs_info *fs_info); int btrfs_chunk_readonly(struct btrfs_root *root, u64 chunk_offset); -int find_free_dev_extent(struct btrfs_device *device, u64 num_bytes, +int find_free_dev_extent(struct btrfs_trans_handle *trans, + struct btrfs_device *device, u64 num_bytes, u64 *start, u64 *max_avail); void btrfs_dev_stat_inc_and_print(struct btrfs_device *dev, int index); int btrfs_get_dev_stats(struct btrfs_root *root, @@ -337,6 +338,9 @@ int btrfs_is_parity_mirror(struct btrfs_mapping_tree *map_tree, unsigned long btrfs_full_stripe_len(struct btrfs_root *root, struct btrfs_mapping_tree *map_tree, u64 logical); +int btrfs_finish_chunk_alloc(struct btrfs_trans_handle *trans, + struct btrfs_root *extent_root, + u64 chunk_offset, u64 chunk_size); static inline void btrfs_dev_stat_inc(struct btrfs_device *dev, int index) { -- cgit v1.2.3 From 26b258919006fc2d76a50b8247d7dea73207b583 Mon Sep 17 00:00:00 2001 From: Miao Xie Date: Thu, 27 Jun 2013 18:50:58 +0800 Subject: Btrfs: fix oops when recovering the file data by scrub function We get oops while running btrfs replace start test, ------------[ cut here ]------------ kernel BUG at mm/filemap.c:608! [SNIP] Call Trace: [] copy_nocow_pages_for_inode+0x217/0x3f0 [btrfs] [] ? scrub_print_warning_inode+0x230/0x230 [btrfs] [] ? scrub_print_warning_inode+0x230/0x230 [btrfs] [] iterate_extent_inodes+0x1ae/0x300 [btrfs] [] iterate_inodes_from_logical+0x92/0xb0 [btrfs] [] ? scrub_print_warning_inode+0x230/0x230 [btrfs] [] copy_nocow_pages_worker+0x97/0x150 [btrfs] [] worker_loop+0x134/0x540 [btrfs] [] ? __schedule+0x3ca/0x7f0 [] ? btrfs_queue_worker+0x300/0x300 [btrfs] [] kthread+0xc0/0xd0 [] ? flush_kthread_worker+0x80/0x80 [] ret_from_fork+0x7c/0xb0 [] ? flush_kthread_worker+0x80/0x80 [SNIP] RIP [] unlock_page+0x35/0x40 RSP ---[ end trace 421e79ad0dd72c7d ]--- it is because we forgot to lock the page again after we read data to the page. Fix it. Signed-off-by: Lin Feng Signed-off-by: Miao Xie Signed-off-by: Josef Bacik --- fs/btrfs/scrub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c index 63144e4ca9e1..c1647f8c1cd0 100644 --- a/fs/btrfs/scrub.c +++ b/fs/btrfs/scrub.c @@ -3258,7 +3258,7 @@ static int copy_nocow_pages_for_inode(u64 inum, u64 offset, u64 root, void *ctx) ret = ret_sub; goto next_page; } - wait_on_page_locked(page); + lock_page(page); if (!PageUptodate(page)) { ret = -EIO; goto next_page; -- cgit v1.2.3 From 826aa0a82c5b9d2c8016c02b552e8f82f5b1e660 Mon Sep 17 00:00:00 2001 From: Miao Xie Date: Thu, 27 Jun 2013 18:50:59 +0800 Subject: Btrfs: cleanup the code of copy_nocow_pages_for_inode() - It make no sense that we continue to do something after the error happened, just go back with this patch. - remove some check of copy_nocow_pages_for_inode(), such as page check after write, inode check in the end of the function, because we are sure they exist. - remove the unnecessary goto in the return value check of the write Signed-off-by: Miao Xie Signed-off-by: Josef Bacik --- fs/btrfs/scrub.c | 48 +++++++++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c index c1647f8c1cd0..186ea82b75f7 100644 --- a/fs/btrfs/scrub.c +++ b/fs/btrfs/scrub.c @@ -3199,16 +3199,18 @@ out: static int copy_nocow_pages_for_inode(u64 inum, u64 offset, u64 root, void *ctx) { - unsigned long index; struct scrub_copy_nocow_ctx *nocow_ctx = ctx; - int ret = 0; + struct btrfs_fs_info *fs_info = nocow_ctx->sctx->dev_root->fs_info; struct btrfs_key key; - struct inode *inode = NULL; + struct inode *inode; + struct page *page; struct btrfs_root *local_root; u64 physical_for_dev_replace; u64 len; - struct btrfs_fs_info *fs_info = nocow_ctx->sctx->dev_root->fs_info; + unsigned long index; int srcu_index; + int ret; + int err; key.objectid = root; key.type = BTRFS_ROOT_ITEM_KEY; @@ -3230,19 +3232,17 @@ static int copy_nocow_pages_for_inode(u64 inum, u64 offset, u64 root, void *ctx) if (IS_ERR(inode)) return PTR_ERR(inode); + ret = 0; physical_for_dev_replace = nocow_ctx->physical_for_dev_replace; len = nocow_ctx->len; while (len >= PAGE_CACHE_SIZE) { - struct page *page = NULL; - int ret_sub; - index = offset >> PAGE_CACHE_SHIFT; page = find_or_create_page(inode->i_mapping, index, GFP_NOFS); if (!page) { pr_err("find_or_create_page() failed\n"); ret = -ENOMEM; - goto next_page; + goto out; } if (PageUptodate(page)) { @@ -3250,12 +3250,12 @@ static int copy_nocow_pages_for_inode(u64 inum, u64 offset, u64 root, void *ctx) goto next_page; } else { ClearPageError(page); - ret_sub = extent_read_full_page(&BTRFS_I(inode)-> + err = extent_read_full_page(&BTRFS_I(inode)-> io_tree, page, btrfs_get_extent, nocow_ctx->mirror_num); - if (ret_sub) { - ret = ret_sub; + if (err) { + ret = err; goto next_page; } lock_page(page); @@ -3264,25 +3264,23 @@ static int copy_nocow_pages_for_inode(u64 inum, u64 offset, u64 root, void *ctx) goto next_page; } } - ret_sub = write_page_nocow(nocow_ctx->sctx, - physical_for_dev_replace, page); - if (ret_sub) { - ret = ret_sub; - goto next_page; - } - + err = write_page_nocow(nocow_ctx->sctx, + physical_for_dev_replace, page); + if (err) + ret = err; next_page: - if (page) { - unlock_page(page); - put_page(page); - } + unlock_page(page); + page_cache_release(page); + + if (ret) + break; + offset += PAGE_CACHE_SIZE; physical_for_dev_replace += PAGE_CACHE_SIZE; len -= PAGE_CACHE_SIZE; } - - if (inode) - iput(inode); +out: + iput(inode); return ret; } -- cgit v1.2.3 From edd1400be9f983f521c7397740d810fa210ee52f Mon Sep 17 00:00:00 2001 From: Miao Xie Date: Thu, 27 Jun 2013 18:51:00 +0800 Subject: Btrfs: fix several potential problems in copy_nocow_pages_for_inode - It makes no sense that we deal with a inode in the dead tree. - fix the race between dio and page copy by waiting the dio completion - avoid the page copy vs truncate/punch hole - check if the page is in the page cache or not Signed-off-by: Miao Xie Signed-off-by: Josef Bacik --- fs/btrfs/scrub.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c index 186ea82b75f7..4ba2a69a60ad 100644 --- a/fs/btrfs/scrub.c +++ b/fs/btrfs/scrub.c @@ -3224,6 +3224,11 @@ static int copy_nocow_pages_for_inode(u64 inum, u64 offset, u64 root, void *ctx) return PTR_ERR(local_root); } + if (btrfs_root_refs(&local_root->root_item) == 0) { + srcu_read_unlock(&fs_info->subvol_srcu, srcu_index); + return -ENOENT; + } + key.type = BTRFS_INODE_ITEM_KEY; key.objectid = inum; key.offset = 0; @@ -3232,12 +3237,16 @@ static int copy_nocow_pages_for_inode(u64 inum, u64 offset, u64 root, void *ctx) if (IS_ERR(inode)) return PTR_ERR(inode); + /* Avoid truncate/dio/punch hole.. */ + mutex_lock(&inode->i_mutex); + inode_dio_wait(inode); + ret = 0; physical_for_dev_replace = nocow_ctx->physical_for_dev_replace; len = nocow_ctx->len; while (len >= PAGE_CACHE_SIZE) { index = offset >> PAGE_CACHE_SHIFT; - +again: page = find_or_create_page(inode->i_mapping, index, GFP_NOFS); if (!page) { pr_err("find_or_create_page() failed\n"); @@ -3258,7 +3267,18 @@ static int copy_nocow_pages_for_inode(u64 inum, u64 offset, u64 root, void *ctx) ret = err; goto next_page; } + lock_page(page); + /* + * If the page has been remove from the page cache, + * the data on it is meaningless, because it may be + * old one, the new data may be written into the new + * page in the page cache. + */ + if (page->mapping != inode->i_mapping) { + page_cache_release(page); + goto again; + } if (!PageUptodate(page)) { ret = -EIO; goto next_page; @@ -3280,6 +3300,7 @@ next_page: len -= PAGE_CACHE_SIZE; } out: + mutex_unlock(&inode->i_mutex); iput(inode); return ret; } -- cgit v1.2.3 From 35f0399db6658f465b00893bdd13b992a0acfef0 Mon Sep 17 00:00:00 2001 From: Liu Bo Date: Fri, 28 Jun 2013 12:37:45 +0800 Subject: Btrfs: fix crash regarding to ulist_add_merge Several users reported this crash of NULL pointer or general protection, the story is that we add a rbtree for speedup ulist iteration, and we use krealloc() to address ulist growth, and krealloc() use memcpy to copy old data to new memory area, so it's OK for an array as it doesn't use pointers while it's not OK for a rbtree as it uses pointers. So krealloc() will mess up our rbtree and it ends up with crash. Reviewed-by: Wang Shilong Signed-off-by: Liu Bo Signed-off-by: Josef Bacik --- fs/btrfs/ulist.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/fs/btrfs/ulist.c b/fs/btrfs/ulist.c index 7b417e20efe2..b0a523b2c60e 100644 --- a/fs/btrfs/ulist.c +++ b/fs/btrfs/ulist.c @@ -205,6 +205,10 @@ int ulist_add_merge(struct ulist *ulist, u64 val, u64 aux, u64 new_alloced = ulist->nodes_alloced + 128; struct ulist_node *new_nodes; void *old = NULL; + int i; + + for (i = 0; i < ulist->nnodes; i++) + rb_erase(&ulist->nodes[i].rb_node, &ulist->root); /* * if nodes_alloced == ULIST_SIZE no memory has been allocated @@ -224,6 +228,17 @@ int ulist_add_merge(struct ulist *ulist, u64 val, u64 aux, ulist->nodes = new_nodes; ulist->nodes_alloced = new_alloced; + + /* + * krealloc actually uses memcpy, which does not copy rb_node + * pointers, so we have to do it ourselves. Otherwise we may + * be bitten by crashes. + */ + for (i = 0; i < ulist->nnodes; i++) { + ret = ulist_rbtree_insert(ulist, &ulist->nodes[i]); + if (ret < 0) + return ret; + } } ulist->nodes[ulist->nnodes].val = val; ulist->nodes[ulist->nnodes].aux = aux; -- cgit v1.2.3 From 261c84b662f93e0eb75bccd6cd732391d005060a Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Fri, 28 Jun 2013 13:11:22 -0400 Subject: Btrfs: make backref walking code handle skinny metadata I missed fixing the backref stuff when I introduced the skinny metadata. If you try and do things like snapshot aware defrag with skinny metadata you are going to see tons of warnings related to the backref count being less than 0. This is because the delayed refs will be found for stuff just fine, but it won't find the skinny metadata extent refs. With this patch I'm not seeing warnings anymore. Thanks, Reviewed-by: Liu Bo Signed-off-by: Josef Bacik --- fs/btrfs/backref.c | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c index 431ea9211bc1..eaf133384a8f 100644 --- a/fs/btrfs/backref.c +++ b/fs/btrfs/backref.c @@ -597,6 +597,7 @@ static int __add_inline_refs(struct btrfs_fs_info *fs_info, int slot; struct extent_buffer *leaf; struct btrfs_key key; + struct btrfs_key found_key; unsigned long ptr; unsigned long end; struct btrfs_extent_item *ei; @@ -614,17 +615,21 @@ static int __add_inline_refs(struct btrfs_fs_info *fs_info, ei = btrfs_item_ptr(leaf, slot, struct btrfs_extent_item); flags = btrfs_extent_flags(leaf, ei); + btrfs_item_key_to_cpu(leaf, &found_key, slot); ptr = (unsigned long)(ei + 1); end = (unsigned long)ei + item_size; - if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) { + if (found_key.type == BTRFS_EXTENT_ITEM_KEY && + flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) { struct btrfs_tree_block_info *info; info = (struct btrfs_tree_block_info *)ptr; *info_level = btrfs_tree_block_level(leaf, info); ptr += sizeof(struct btrfs_tree_block_info); BUG_ON(ptr > end); + } else if (found_key.type == BTRFS_METADATA_ITEM_KEY) { + *info_level = found_key.offset; } else { BUG_ON(!(flags & BTRFS_EXTENT_FLAG_DATA)); } @@ -796,8 +801,11 @@ static int find_parent_nodes(struct btrfs_trans_handle *trans, INIT_LIST_HEAD(&prefs_delayed); key.objectid = bytenr; - key.type = BTRFS_EXTENT_ITEM_KEY; key.offset = (u64)-1; + if (btrfs_fs_incompat(fs_info, SKINNY_METADATA)) + key.type = BTRFS_METADATA_ITEM_KEY; + else + key.type = BTRFS_EXTENT_ITEM_KEY; path = btrfs_alloc_path(); if (!path) @@ -862,7 +870,8 @@ again: slot = path->slots[0]; btrfs_item_key_to_cpu(leaf, &key, slot); if (key.objectid == bytenr && - key.type == BTRFS_EXTENT_ITEM_KEY) { + (key.type == BTRFS_EXTENT_ITEM_KEY || + key.type == BTRFS_METADATA_ITEM_KEY)) { ret = __add_inline_refs(fs_info, path, bytenr, &info_level, &prefs); if (ret) @@ -1276,12 +1285,16 @@ int extent_from_logical(struct btrfs_fs_info *fs_info, u64 logical, { int ret; u64 flags; + u64 size = 0; u32 item_size; struct extent_buffer *eb; struct btrfs_extent_item *ei; struct btrfs_key key; - key.type = BTRFS_EXTENT_ITEM_KEY; + if (btrfs_fs_incompat(fs_info, SKINNY_METADATA)) + key.type = BTRFS_METADATA_ITEM_KEY; + else + key.type = BTRFS_EXTENT_ITEM_KEY; key.objectid = logical; key.offset = (u64)-1; @@ -1294,9 +1307,15 @@ int extent_from_logical(struct btrfs_fs_info *fs_info, u64 logical, return ret; btrfs_item_key_to_cpu(path->nodes[0], found_key, path->slots[0]); - if (found_key->type != BTRFS_EXTENT_ITEM_KEY || + if (found_key->type == BTRFS_METADATA_ITEM_KEY) + size = fs_info->extent_root->leafsize; + else if (found_key->type == BTRFS_EXTENT_ITEM_KEY) + size = found_key->offset; + + if ((found_key->type != BTRFS_EXTENT_ITEM_KEY && + found_key->type != BTRFS_METADATA_ITEM_KEY) || found_key->objectid > logical || - found_key->objectid + found_key->offset <= logical) { + found_key->objectid + size <= logical) { pr_debug("logical %llu is not within any extent\n", (unsigned long long)logical); return -ENOENT; -- cgit v1.2.3 From f1ca7e98a67da618d8595866e0860308525154da Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Sat, 29 Jun 2013 23:15:19 -0400 Subject: Btrfs: hold the tree mod lock in __tree_mod_log_rewind We need to hold the tree mod log lock in __tree_mod_log_rewind since we walk forward in the tree mod entries, otherwise we'll end up with random entries and trip the BUG_ON() at the front of __tree_mod_log_rewind. This fixes the panics people were seeing when running find /whatever -type f -exec btrfs fi defrag {} \; Thansk, Cc: stable@vger.kernel.org Signed-off-by: Josef Bacik --- fs/btrfs/ctree.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index c32d03dff4fc..7921e1d9d59c 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -1161,8 +1161,8 @@ __tree_mod_log_oldest_root(struct btrfs_fs_info *fs_info, * time_seq). */ static void -__tree_mod_log_rewind(struct extent_buffer *eb, u64 time_seq, - struct tree_mod_elem *first_tm) +__tree_mod_log_rewind(struct btrfs_fs_info *fs_info, struct extent_buffer *eb, + u64 time_seq, struct tree_mod_elem *first_tm) { u32 n; struct rb_node *next; @@ -1172,6 +1172,7 @@ __tree_mod_log_rewind(struct extent_buffer *eb, u64 time_seq, unsigned long p_size = sizeof(struct btrfs_key_ptr); n = btrfs_header_nritems(eb); + tree_mod_log_read_lock(fs_info); while (tm && tm->seq >= time_seq) { /* * all the operations are recorded with the operator used for @@ -1226,6 +1227,7 @@ __tree_mod_log_rewind(struct extent_buffer *eb, u64 time_seq, if (tm->index != first_tm->index) break; } + tree_mod_log_read_unlock(fs_info); btrfs_set_header_nritems(eb, n); } @@ -1274,7 +1276,7 @@ tree_mod_log_rewind(struct btrfs_fs_info *fs_info, struct extent_buffer *eb, extent_buffer_get(eb_rewin); btrfs_tree_read_lock(eb_rewin); - __tree_mod_log_rewind(eb_rewin, time_seq, tm); + __tree_mod_log_rewind(fs_info, eb_rewin, time_seq, tm); WARN_ON(btrfs_header_nritems(eb_rewin) > BTRFS_NODEPTRS_PER_BLOCK(fs_info->tree_root)); @@ -1350,7 +1352,7 @@ get_old_root(struct btrfs_root *root, u64 time_seq) btrfs_set_header_generation(eb, old_generation); } if (tm) - __tree_mod_log_rewind(eb, time_seq, tm); + __tree_mod_log_rewind(root->fs_info, eb, time_seq, tm); else WARN_ON(btrfs_header_level(eb) != 0); WARN_ON(btrfs_header_nritems(eb) > BTRFS_NODEPTRS_PER_BLOCK(root)); -- cgit v1.2.3 From 7fb7d76f96bfcbea25007d190ba828b18e13d29d Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Mon, 1 Jul 2013 16:10:16 -0400 Subject: Btrfs: only do the tree_mod_log_free_eb if this is our last ref There is another bug in the tree mod log stuff in that we're calling tree_mod_log_free_eb every single time a block is cow'ed. The problem with this is that if this block is shared by multiple snapshots we will call this multiple times per block, so if we go to rewind the mod log for this block we'll BUG_ON() in __tree_mod_log_rewind because we try to rewind a free twice. We only want to call tree_mod_log_free_eb if we are actually freeing the block. With this patch I no longer hit the panic in __tree_mod_log_rewind. Thanks, Cc: stable@vger.kernel.org Reviewed-by: Jan Schmidt Signed-off-by: Josef Bacik --- fs/btrfs/ctree.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index 7921e1d9d59c..5bf4c39e2ad6 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -1089,7 +1089,8 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans, btrfs_set_node_ptr_generation(parent, parent_slot, trans->transid); btrfs_mark_buffer_dirty(parent); - tree_mod_log_free_eb(root->fs_info, buf); + if (last_ref) + tree_mod_log_free_eb(root->fs_info, buf); btrfs_free_tree_block(trans, root, buf, parent_start, last_ref); } -- cgit v1.2.3 From 0e267c44c3a402d35111d1935be1167240b5b79f Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Tue, 2 Jul 2013 10:38:02 -0400 Subject: Btrfs: wait ordered range before doing direct io My recent truncate patch uncovered this bug, but I can reproduce it without the truncate patch. If you mount with -o compress-force, do a direct write to some area, do a buffered write to some other area, and then do a direct read you will get the wrong data for where you did the buffered write. This is because the generic direct io helpers only call filemap_write_and_wait once, and for compression we need it twice. So to be safe add the btrfs_wait_ordered_range to the start of the direct io function to make sure any compressed writes have truly been written. This patch makes xfstests 130 pass when you mount with -o compress-force=lzo. Thanks, Signed-off-by: Josef Bacik --- fs/btrfs/inode.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 0a43d42268f7..55dda871437f 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -7270,8 +7270,16 @@ static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb, atomic_inc(&inode->i_dio_count); smp_mb__after_atomic_inc(); + /* + * The generic stuff only does filemap_write_and_wait_range, which isn't + * enough if we've written compressed pages to this area, so we need to + * call btrfs_wait_ordered_range to make absolutely sure that any + * outstanding dirty pages are on disk. + */ + count = iov_length(iov, nr_segs); + btrfs_wait_ordered_range(inode, offset, count); + if (rw & WRITE) { - count = iov_length(iov, nr_segs); /* * If the write DIO is beyond the EOF, we need update * the isize, but it is protected by i_mutex. So we can -- cgit v1.2.3 From 7cc47d139f9a815a91bd9e7377063238c69a0423 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Sun, 30 Jun 2013 14:37:11 +1000 Subject: cxgb3: Missing rtnl lock in error recovery When exercising error injection on IBM pseries machine, I hit the following warning: [ 251.450043] RTAS: event: 89, Type: Platform Error, Severity: 2 [ 253.549822] cxgb3 0006:01:00.0: enabling device (0140 -> 0142) [ 253.713560] cxgb3 0006:01:00.0: adapter recovering, PEX ERR 0x100 [ 254.895437] RTNL: assertion failed at net/core/dev.c (2031) [ 254.895467] CPU: 6 PID: 5449 Comm: eehd Tainted: G W 3.10.0-rc7-00157-gea461ab #19 [ 254.895474] Call Trace: [ 254.895483] [c000000fac56f7d0] [c000000000014dcc] .show_stack+0x7c/0x1f0 (unreliable) [ 254.895493] [c000000fac56f8a0] [c0000000007ba318] .dump_stack+0x28/0x3c [ 254.895500] [c000000fac56f910] [c0000000006c0384] .netif_set_real_num_tx_queues+0x224/0x230 [ 254.895515] [c000000fac56f9b0] [d00000000ef35510] .cxgb_open+0x80/0x3f0 [cxgb3] [ 254.895525] [c000000fac56fa50] [d00000000ef35914] .t3_resume_ports+0x94/0x100 [cxgb3] [ 254.895533] [c000000fac56fae0] [c00000000005fc8c] .eeh_report_resume+0x8c/0xd0 [ 254.895539] [c000000fac56fb60] [c00000000005e9fc] .eeh_pe_dev_traverse+0x9c/0x190 [ 254.895545] [c000000fac56fc10] [c000000000060000] .eeh_handle_event+0x110/0x330 [ 254.895551] [c000000fac56fca0] [c000000000060350] .eeh_event_handler+0x130/0x1a0 [ 254.895558] [c000000fac56fd30] [c0000000000ad758] .kthread+0xe8/0xf0 [ 254.895566] [c000000fac56fe30] [c00000000000a05c] .ret_from_kernel_thread+0x5c/0x80 It appears that t3_resume_ports() is called with the rtnl_lock held from the fatal error task but not from the PCI error callbacks. This fixes it. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c index 71497e835f42..b650951791dd 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c +++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c @@ -3037,7 +3037,9 @@ static void t3_io_resume(struct pci_dev *pdev) CH_ALERT(adapter, "adapter recovering, PEX ERR 0x%x\n", t3_read_reg(adapter, A_PCIE_PEX_ERR)); + rtnl_lock(); t3_resume_ports(adapter); + rtnl_unlock(); } static const struct pci_error_handlers t3_err_handler = { -- cgit v1.2.3 From b9eef55c2ab33053ae236b5d383965f9ee6a0094 Mon Sep 17 00:00:00 2001 From: Jim Baxter Date: Mon, 1 Jul 2013 14:57:54 +0100 Subject: net: fec: Fix RMON registers on imx6 commit 38ae92d "fec: Add support for reading RMON registers" causes the imx6Q to crash. This fixes it by only enabling the RMON registers, the registers are already cleared by the MAC being reset. Signed-off-by: Jim Baxter Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fec_main.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index aff8a5cd17c7..b6768821757b 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -606,11 +606,8 @@ fec_restart(struct net_device *ndev, int duplex) ecntl |= (1 << 4); #ifndef CONFIG_M5272 - /* Disable, clear, and enable the MIB */ - writel(1 << 31, fep->hwp + FEC_MIB_CTRLSTAT); - for (i = RMON_T_DROP; i < IEEE_R_OCTETS_OK; i++) - writel(0, fep->hwp + i); - writel(0, fep->hwp + FEC_MIB_CTRLSTAT); + /* Enable the MIB statistic event counters */ + writel(0 << 31, fep->hwp + FEC_MIB_CTRLSTAT); #endif /* And last, enable the transmit and receive processing */ -- cgit v1.2.3 From 8822b64a0fa64a5dd1dfcf837c5b0be83f8c05d1 Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Mon, 1 Jul 2013 20:21:30 +0200 Subject: ipv6: call udp_push_pending_frames when uncorking a socket with AF_INET pending data We accidentally call down to ip6_push_pending_frames when uncorking pending AF_INET data on a ipv6 socket. This results in the following splat (from Dave Jones): skbuff: skb_under_panic: text:ffffffff816765f6 len:48 put:40 head:ffff88013deb6df0 data:ffff88013deb6dec tail:0x2c end:0xc0 dev: ------------[ cut here ]------------ kernel BUG at net/core/skbuff.c:126! invalid opcode: 0000 [#1] PREEMPT SMP DEBUG_PAGEALLOC Modules linked in: dccp_ipv4 dccp 8021q garp bridge stp dlci mpoa snd_seq_dummy sctp fuse hidp tun bnep nfnetlink scsi_transport_iscsi rfcomm can_raw can_bcm af_802154 appletalk caif_socket can caif ipt_ULOG x25 rose af_key pppoe pppox ipx phonet irda llc2 ppp_generic slhc p8023 psnap p8022 llc crc_ccitt atm bluetooth +netrom ax25 nfc rfkill rds af_rxrpc coretemp hwmon kvm_intel kvm crc32c_intel snd_hda_codec_realtek ghash_clmulni_intel microcode pcspkr snd_hda_codec_hdmi snd_hda_intel snd_hda_codec snd_hwdep usb_debug snd_seq snd_seq_device snd_pcm e1000e snd_page_alloc snd_timer ptp snd pps_core soundcore xfs libcrc32c CPU: 2 PID: 8095 Comm: trinity-child2 Not tainted 3.10.0-rc7+ #37 task: ffff8801f52c2520 ti: ffff8801e6430000 task.ti: ffff8801e6430000 RIP: 0010:[] [] skb_panic+0x63/0x65 RSP: 0018:ffff8801e6431de8 EFLAGS: 00010282 RAX: 0000000000000086 RBX: ffff8802353d3cc0 RCX: 0000000000000006 RDX: 0000000000003b90 RSI: ffff8801f52c2ca0 RDI: ffff8801f52c2520 RBP: ffff8801e6431e08 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000001 R11: 0000000000000001 R12: ffff88022ea0c800 R13: ffff88022ea0cdf8 R14: ffff8802353ecb40 R15: ffffffff81cc7800 FS: 00007f5720a10740(0000) GS:ffff880244c00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000005862000 CR3: 000000022843c000 CR4: 00000000001407e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000600 Stack: ffff88013deb6dec 000000000000002c 00000000000000c0 ffffffff81a3f6e4 ffff8801e6431e18 ffffffff8159a9aa ffff8801e6431e90 ffffffff816765f6 ffffffff810b756b 0000000700000002 ffff8801e6431e40 0000fea9292aa8c0 Call Trace: [] skb_push+0x3a/0x40 [] ip6_push_pending_frames+0x1f6/0x4d0 [] ? mark_held_locks+0xbb/0x140 [] udp_v6_push_pending_frames+0x2b9/0x3d0 [] ? udplite_getfrag+0x20/0x20 [] udp_lib_setsockopt+0x1aa/0x1f0 [] ? fget_light+0x387/0x4f0 [] udpv6_setsockopt+0x34/0x40 [] sock_common_setsockopt+0x14/0x20 [] SyS_setsockopt+0x71/0xd0 [] tracesys+0xdd/0xe2 Code: 00 00 48 89 44 24 10 8b 87 d8 00 00 00 48 89 44 24 08 48 8b 87 e8 00 00 00 48 c7 c7 c0 04 aa 81 48 89 04 24 31 c0 e8 e1 7e ff ff <0f> 0b 55 48 89 e5 0f 0b 55 48 89 e5 0f 0b 55 48 89 e5 0f 0b 55 RIP [] skb_panic+0x63/0x65 RSP This patch adds a check if the pending data is of address family AF_INET and directly calls udp_push_ending_frames from udp_v6_push_pending_frames if that is the case. This bug was found by Dave Jones with trinity. (Also move the initialization of fl6 below the AF_INET check, even if not strictly necessary.) Cc: Dave Jones Cc: YOSHIFUJI Hideaki Signed-off-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- include/net/udp.h | 1 + net/ipv4/udp.c | 3 ++- net/ipv6/udp.c | 7 ++++++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/include/net/udp.h b/include/net/udp.h index b30a71a51839..74c10ec5e74f 100644 --- a/include/net/udp.h +++ b/include/net/udp.h @@ -181,6 +181,7 @@ extern int udp_get_port(struct sock *sk, unsigned short snum, extern void udp_err(struct sk_buff *, u32); extern int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t len); +extern int udp_push_pending_frames(struct sock *sk); extern void udp_flush_pending_frames(struct sock *sk); extern int udp_rcv(struct sk_buff *skb); extern int udp_ioctl(struct sock *sk, int cmd, unsigned long arg); diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 959502afd8d9..6b270e53c207 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -800,7 +800,7 @@ send: /* * Push out all pending data as one UDP datagram. Socket is locked. */ -static int udp_push_pending_frames(struct sock *sk) +int udp_push_pending_frames(struct sock *sk) { struct udp_sock *up = udp_sk(sk); struct inet_sock *inet = inet_sk(sk); @@ -819,6 +819,7 @@ out: up->pending = 0; return err; } +EXPORT_SYMBOL(udp_push_pending_frames); int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t len) diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index f77e34c5a0e2..b6f31437a1f8 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -959,11 +959,16 @@ static int udp_v6_push_pending_frames(struct sock *sk) struct udphdr *uh; struct udp_sock *up = udp_sk(sk); struct inet_sock *inet = inet_sk(sk); - struct flowi6 *fl6 = &inet->cork.fl.u.ip6; + struct flowi6 *fl6; int err = 0; int is_udplite = IS_UDPLITE(sk); __wsum csum = 0; + if (up->pending == AF_INET) + return udp_push_pending_frames(sk); + + fl6 = &inet->cork.fl.u.ip6; + /* Grab the skbuff where UDP header space exists. */ if ((skb = skb_peek(&sk->sk_write_queue)) == NULL) goto out; -- cgit v1.2.3 From 75a493e60ac4bbe2e977e7129d6d8cbb0dd236be Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Tue, 2 Jul 2013 08:04:05 +0200 Subject: ipv6: ip6_append_data_mtu did not care about pmtudisc and frag_size If the socket had an IPV6_MTU value set, ip6_append_data_mtu lost track of this when appending the second frame on a corked socket. This results in the following splat: [37598.993962] ------------[ cut here ]------------ [37598.994008] kernel BUG at net/core/skbuff.c:2064! [37598.994008] invalid opcode: 0000 [#1] SMP [37598.994008] Modules linked in: tcp_lp uvcvideo videobuf2_vmalloc videobuf2_memops videobuf2_core videodev media vfat fat usb_storage fuse ebtable_nat xt_CHECKSUM bridge stp llc ipt_MASQUERADE nf_conntrack_netbios_ns nf_conntrack_broadcast ip6table_mangle ip6t_REJECT nf_conntrack_ipv6 nf_defrag_ipv6 iptable_nat +nf_nat_ipv4 nf_nat iptable_mangle nf_conntrack_ipv4 nf_defrag_ipv4 xt_conntrack nf_conntrack ebtable_filter ebtables ip6table_filter ip6_tables be2iscsi iscsi_boot_sysfs bnx2i cnic uio cxgb4i cxgb4 cxgb3i cxgb3 mdio libcxgbi ib_iser rdma_cm ib_addr iw_cm ib_cm ib_sa ib_mad ib_core iscsi_tcp libiscsi_tcp libiscsi +scsi_transport_iscsi rfcomm bnep iTCO_wdt iTCO_vendor_support snd_hda_codec_conexant arc4 iwldvm mac80211 snd_hda_intel acpi_cpufreq mperf coretemp snd_hda_codec microcode cdc_wdm cdc_acm [37598.994008] snd_hwdep cdc_ether snd_seq snd_seq_device usbnet mii joydev btusb snd_pcm bluetooth i2c_i801 e1000e lpc_ich mfd_core ptp iwlwifi pps_core snd_page_alloc mei cfg80211 snd_timer thinkpad_acpi snd tpm_tis soundcore rfkill tpm tpm_bios vhost_net tun macvtap macvlan kvm_intel kvm uinput binfmt_misc +dm_crypt i915 i2c_algo_bit drm_kms_helper drm i2c_core wmi video [37598.994008] CPU 0 [37598.994008] Pid: 27320, comm: t2 Not tainted 3.9.6-200.fc18.x86_64 #1 LENOVO 27744PG/27744PG [37598.994008] RIP: 0010:[] [] skb_copy_and_csum_bits+0x325/0x330 [37598.994008] RSP: 0018:ffff88003670da18 EFLAGS: 00010202 [37598.994008] RAX: ffff88018105c018 RBX: 0000000000000004 RCX: 00000000000006c0 [37598.994008] RDX: ffff88018105a6c0 RSI: ffff88018105a000 RDI: ffff8801e1b0aa00 [37598.994008] RBP: ffff88003670da78 R08: 0000000000000000 R09: ffff88018105c040 [37598.994008] R10: ffff8801e1b0aa00 R11: 0000000000000000 R12: 000000000000fff8 [37598.994008] R13: 00000000000004fc R14: 00000000ffff0504 R15: 0000000000000000 [37598.994008] FS: 00007f28eea59740(0000) GS:ffff88023bc00000(0000) knlGS:0000000000000000 [37598.994008] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b [37598.994008] CR2: 0000003d935789e0 CR3: 00000000365cb000 CR4: 00000000000407f0 [37598.994008] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [37598.994008] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 [37598.994008] Process t2 (pid: 27320, threadinfo ffff88003670c000, task ffff88022c162ee0) [37598.994008] Stack: [37598.994008] ffff88022e098a00 ffff88020f973fc0 0000000000000008 00000000000004c8 [37598.994008] ffff88020f973fc0 00000000000004c4 ffff88003670da78 ffff8801e1b0a200 [37598.994008] 0000000000000018 00000000000004c8 ffff88020f973fc0 00000000000004c4 [37598.994008] Call Trace: [37598.994008] [] ip6_append_data+0xccf/0xfe0 [37598.994008] [] ? ip_copy_metadata+0x1a0/0x1a0 [37598.994008] [] ? _raw_spin_lock_bh+0x16/0x40 [37598.994008] [] udpv6_sendmsg+0x1ed/0xc10 [37598.994008] [] ? sock_has_perm+0x75/0x90 [37598.994008] [] inet_sendmsg+0x63/0xb0 [37598.994008] [] ? selinux_socket_sendmsg+0x23/0x30 [37598.994008] [] sock_sendmsg+0xb0/0xe0 [37598.994008] [] ? __switch_to+0x181/0x4a0 [37598.994008] [] sys_sendto+0x12d/0x180 [37598.994008] [] ? __audit_syscall_entry+0x94/0xf0 [37598.994008] [] ? syscall_trace_enter+0x231/0x240 [37598.994008] [] tracesys+0xdd/0xe2 [37598.994008] Code: fe 07 00 00 48 c7 c7 04 28 a6 81 89 45 a0 4c 89 4d b8 44 89 5d a8 e8 1b ac b1 ff 44 8b 5d a8 4c 8b 4d b8 8b 45 a0 e9 cf fe ff ff <0f> 0b 66 0f 1f 84 00 00 00 00 00 66 66 66 66 90 55 48 89 e5 48 [37598.994008] RIP [] skb_copy_and_csum_bits+0x325/0x330 [37598.994008] RSP [37599.007323] ---[ end trace d69f6a17f8ac8eee ]--- While there, also check if path mtu discovery is activated for this socket. The logic was adapted from ip6_append_data when first writing on the corked socket. This bug was introduced with commit 0c1833797a5a6ec23ea9261d979aa18078720b74 ("ipv6: fix incorrect ipsec fragment"). v2: a) Replace IPV6_PMTU_DISC_DO with IPV6_PMTUDISC_PROBE. b) Don't pass ipv6_pinfo to ip6_append_data_mtu (suggestion by Gao feng, thanks!). c) Change mtu to unsigned int, else we get a warning about non-matching types because of the min()-macro type-check. Acked-by: Gao feng Cc: YOSHIFUJI Hideaki Signed-off-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- net/ipv6/ip6_output.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index dae1949019d7..be7589ef5cf9 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1093,11 +1093,12 @@ static inline struct ipv6_rt_hdr *ip6_rthdr_dup(struct ipv6_rt_hdr *src, return src ? kmemdup(src, (src->hdrlen + 1) * 8, gfp) : NULL; } -static void ip6_append_data_mtu(int *mtu, +static void ip6_append_data_mtu(unsigned int *mtu, int *maxfraglen, unsigned int fragheaderlen, struct sk_buff *skb, - struct rt6_info *rt) + struct rt6_info *rt, + bool pmtuprobe) { if (!(rt->dst.flags & DST_XFRM_TUNNEL)) { if (skb == NULL) { @@ -1109,7 +1110,9 @@ static void ip6_append_data_mtu(int *mtu, * this fragment is not first, the headers * space is regarded as data space. */ - *mtu = dst_mtu(rt->dst.path); + *mtu = min(*mtu, pmtuprobe ? + rt->dst.dev->mtu : + dst_mtu(rt->dst.path)); } *maxfraglen = ((*mtu - fragheaderlen) & ~7) + fragheaderlen - sizeof(struct frag_hdr); @@ -1126,11 +1129,10 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, struct ipv6_pinfo *np = inet6_sk(sk); struct inet_cork *cork; struct sk_buff *skb, *skb_prev = NULL; - unsigned int maxfraglen, fragheaderlen; + unsigned int maxfraglen, fragheaderlen, mtu; int exthdrlen; int dst_exthdrlen; int hh_len; - int mtu; int copy; int err; int offset = 0; @@ -1287,7 +1289,9 @@ alloc_new_skb: /* update mtu and maxfraglen if necessary */ if (skb == NULL || skb_prev == NULL) ip6_append_data_mtu(&mtu, &maxfraglen, - fragheaderlen, skb, rt); + fragheaderlen, skb, rt, + np->pmtudisc == + IPV6_PMTUDISC_PROBE); skb_prev = skb; -- cgit v1.2.3 From 7e6d4da837385d9aa2e5fd84e0a6042cddc9e708 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 2 Jul 2013 10:55:31 +0200 Subject: nlmon: use standard rtnetlink link api for add/del devices It is not nice when netdev is created right after module load and with some implicit name. So rather change nlmon to use standard rtnl link API. Signed-off-by: Jiri Pirko Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- drivers/net/nlmon.c | 49 ++++++++++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/drivers/net/nlmon.c b/drivers/net/nlmon.c index a0baf56f5826..b57ce5f48962 100644 --- a/drivers/net/nlmon.c +++ b/drivers/net/nlmon.c @@ -4,6 +4,7 @@ #include #include #include +#include struct pcpu_lstats { u64 packets; @@ -56,16 +57,24 @@ static void nlmon_dev_uninit(struct net_device *dev) free_percpu(dev->lstats); } -static struct netlink_tap nlmon_tap; +struct nlmon { + struct netlink_tap nt; +}; static int nlmon_open(struct net_device *dev) { - return netlink_add_tap(&nlmon_tap); + struct nlmon *nlmon = netdev_priv(dev); + + nlmon->nt.dev = dev; + nlmon->nt.module = THIS_MODULE; + return netlink_add_tap(&nlmon->nt); } static int nlmon_close(struct net_device *dev) { - return netlink_remove_tap(&nlmon_tap); + struct nlmon *nlmon = netdev_priv(dev); + + return netlink_remove_tap(&nlmon->nt); } static struct rtnl_link_stats64 * @@ -119,10 +128,6 @@ static const struct net_device_ops nlmon_ops = { .ndo_change_mtu = nlmon_change_mtu, }; -static struct netlink_tap nlmon_tap __read_mostly = { - .module = THIS_MODULE, -}; - static void nlmon_setup(struct net_device *dev) { dev->type = ARPHRD_NETLINK; @@ -142,27 +147,28 @@ static void nlmon_setup(struct net_device *dev) dev->mtu = NLMSG_GOODSIZE; } -static __init int nlmon_register(void) +static int nlmon_validate(struct nlattr *tb[], struct nlattr *data[]) { - int err; - struct net_device *nldev; - - nldev = nlmon_tap.dev = alloc_netdev(0, "netlink", nlmon_setup); - if (unlikely(nldev == NULL)) - return -ENOMEM; + if (tb[IFLA_ADDRESS]) + return -EINVAL; + return 0; +} - err = register_netdev(nldev); - if (unlikely(err)) - free_netdev(nldev); +static struct rtnl_link_ops nlmon_link_ops __read_mostly = { + .kind = "nlmon", + .priv_size = sizeof(struct nlmon), + .setup = nlmon_setup, + .validate = nlmon_validate, +}; - return err; +static __init int nlmon_register(void) +{ + return rtnl_link_register(&nlmon_link_ops); } static __exit void nlmon_unregister(void) { - struct net_device *nldev = nlmon_tap.dev; - - unregister_netdev(nldev); + rtnl_link_unregister(&nlmon_link_ops); } module_init(nlmon_register); @@ -172,3 +178,4 @@ MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Daniel Borkmann "); MODULE_AUTHOR("Mathieu Geli "); MODULE_DESCRIPTION("Netlink monitoring device"); +MODULE_ALIAS_RTNL_LINK("nlmon"); -- cgit v1.2.3 From 8e2e2fa47129532a30cff6c25a47078dc97d9260 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Tue, 2 Jul 2013 15:30:53 -0400 Subject: tracing: Add trace_array_get/put() to event handling Commit a695cb58162 "tracing: Prevent deleting instances when they are being read" tried to fix a race between deleting a trace instance and reading contents of a trace file. But it wasn't good enough. The following could crash the kernel: # cd /sys/kernel/debug/tracing/instances # ( while :; do mkdir foo; rmdir foo; done ) & # ( while :; do echo 1 > foo/events/sched/sched_switch 2> /dev/null; done ) & Luckily this can only be done by root user, but it should be fixed regardless. The problem is that a delete of the file can happen after the write to the event is opened, but before the enabling happens. The solution is to make sure the trace_array is available before succeeding in opening for write, and incerment the ref counter while opened. Now the instance can be deleted when the events are writing to the buffer, but the deletion of the instance will disable all events before the instance is actually deleted. Cc: stable@vger.kernel.org # 3.10 Reported-by: Alexander Lam Signed-off-by: Steven Rostedt --- kernel/trace/trace.h | 3 +++ kernel/trace/trace_events.c | 55 +++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 2c3cba59552d..c7fbf93f1b7c 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -226,6 +226,9 @@ extern struct list_head ftrace_trace_arrays; extern struct mutex trace_types_lock; +extern int trace_array_get(struct trace_array *tr); +extern void trace_array_put(struct trace_array *tr); + /* * The global tracer (top) should be the first trace array added, * but we check the flag anyway. diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 35c6f23c71b2..920e08fb53b3 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -409,6 +409,35 @@ static void put_system(struct ftrace_subsystem_dir *dir) mutex_unlock(&event_mutex); } +/* + * Open and update trace_array ref count. + * Must have the current trace_array passed to it. + */ +static int tracing_open_generic_file(struct inode *inode, struct file *filp) +{ + struct ftrace_event_file *file = inode->i_private; + struct trace_array *tr = file->tr; + int ret; + + if (trace_array_get(tr) < 0) + return -ENODEV; + + ret = tracing_open_generic(inode, filp); + if (ret < 0) + trace_array_put(tr); + return ret; +} + +static int tracing_release_generic_file(struct inode *inode, struct file *filp) +{ + struct ftrace_event_file *file = inode->i_private; + struct trace_array *tr = file->tr; + + trace_array_put(tr); + + return 0; +} + /* * __ftrace_set_clr_event(NULL, NULL, NULL, set) will set/unset all events. */ @@ -1032,9 +1061,17 @@ static int subsystem_open(struct inode *inode, struct file *filp) /* Some versions of gcc think dir can be uninitialized here */ WARN_ON(!dir); + /* Still need to increment the ref count of the system */ + if (trace_array_get(tr) < 0) { + put_system(dir); + return -ENODEV; + } + ret = tracing_open_generic(inode, filp); - if (ret < 0) + if (ret < 0) { + trace_array_put(tr); put_system(dir); + } return ret; } @@ -1045,16 +1082,23 @@ static int system_tr_open(struct inode *inode, struct file *filp) struct trace_array *tr = inode->i_private; int ret; + if (trace_array_get(tr) < 0) + return -ENODEV; + /* Make a temporary dir that has no system but points to tr */ dir = kzalloc(sizeof(*dir), GFP_KERNEL); - if (!dir) + if (!dir) { + trace_array_put(tr); return -ENOMEM; + } dir->tr = tr; ret = tracing_open_generic(inode, filp); - if (ret < 0) + if (ret < 0) { + trace_array_put(tr); kfree(dir); + } filp->private_data = dir; @@ -1065,6 +1109,8 @@ static int subsystem_release(struct inode *inode, struct file *file) { struct ftrace_subsystem_dir *dir = file->private_data; + trace_array_put(dir->tr); + /* * If dir->subsystem is NULL, then this is a temporary * descriptor that was made for a trace_array to enable @@ -1192,9 +1238,10 @@ static const struct file_operations ftrace_set_event_fops = { }; static const struct file_operations ftrace_enable_fops = { - .open = tracing_open_generic, + .open = tracing_open_generic_file, .read = event_enable_read, .write = event_enable_write, + .release = tracing_release_generic_file, .llseek = default_llseek, }; -- cgit v1.2.3 From bf0936e196ec21b604106578043d4c14831f99e7 Mon Sep 17 00:00:00 2001 From: Mike Lothian Date: Tue, 2 Jul 2013 17:38:11 -0400 Subject: drm/radeon/dpm: fix compilation with certain versions of gcc Add #include to *_dpm.c files Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/ni_dpm.c | 1 + drivers/gpu/drm/radeon/rv6xx_dpm.c | 1 + drivers/gpu/drm/radeon/rv770_dpm.c | 1 + drivers/gpu/drm/radeon/si_dpm.c | 1 + drivers/gpu/drm/radeon/sumo_dpm.c | 1 + drivers/gpu/drm/radeon/trinity_dpm.c | 1 + 6 files changed, 6 insertions(+) diff --git a/drivers/gpu/drm/radeon/ni_dpm.c b/drivers/gpu/drm/radeon/ni_dpm.c index 8497ca6bb0b1..a4cb99c2da85 100644 --- a/drivers/gpu/drm/radeon/ni_dpm.c +++ b/drivers/gpu/drm/radeon/ni_dpm.c @@ -28,6 +28,7 @@ #include "ni_dpm.h" #include "atom.h" #include +#include #define MC_CG_ARB_FREQ_F0 0x0a #define MC_CG_ARB_FREQ_F1 0x0b diff --git a/drivers/gpu/drm/radeon/rv6xx_dpm.c b/drivers/gpu/drm/radeon/rv6xx_dpm.c index 33705c5c8369..8303de267ee5 100644 --- a/drivers/gpu/drm/radeon/rv6xx_dpm.c +++ b/drivers/gpu/drm/radeon/rv6xx_dpm.c @@ -28,6 +28,7 @@ #include "r600_dpm.h" #include "rv6xx_dpm.h" #include "atom.h" +#include static u32 rv6xx_scale_count_given_unit(struct radeon_device *rdev, u32 unscaled_count, u32 unit); diff --git a/drivers/gpu/drm/radeon/rv770_dpm.c b/drivers/gpu/drm/radeon/rv770_dpm.c index 2436b5c7e66e..9af464d48eaa 100644 --- a/drivers/gpu/drm/radeon/rv770_dpm.c +++ b/drivers/gpu/drm/radeon/rv770_dpm.c @@ -29,6 +29,7 @@ #include "rv770_dpm.h" #include "cypress_dpm.h" #include "atom.h" +#include #define MC_CG_ARB_FREQ_F0 0x0a #define MC_CG_ARB_FREQ_F1 0x0b diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c index 46e9fc56cee7..a7e97cd05e96 100644 --- a/drivers/gpu/drm/radeon/si_dpm.c +++ b/drivers/gpu/drm/radeon/si_dpm.c @@ -28,6 +28,7 @@ #include "si_dpm.h" #include "atom.h" #include +#include #define MC_CG_ARB_FREQ_F0 0x0a #define MC_CG_ARB_FREQ_F1 0x0b diff --git a/drivers/gpu/drm/radeon/sumo_dpm.c b/drivers/gpu/drm/radeon/sumo_dpm.c index 68fefb916582..bf187a5b3d58 100644 --- a/drivers/gpu/drm/radeon/sumo_dpm.c +++ b/drivers/gpu/drm/radeon/sumo_dpm.c @@ -27,6 +27,7 @@ #include "r600_dpm.h" #include "cypress_dpm.h" #include "sumo_dpm.h" +#include #define SUMO_MAX_DEEPSLEEP_DIVIDER_ID 5 #define SUMO_MINIMUM_ENGINE_CLOCK 800 diff --git a/drivers/gpu/drm/radeon/trinity_dpm.c b/drivers/gpu/drm/radeon/trinity_dpm.c index 502d9153c4d5..b02b5ad92121 100644 --- a/drivers/gpu/drm/radeon/trinity_dpm.c +++ b/drivers/gpu/drm/radeon/trinity_dpm.c @@ -26,6 +26,7 @@ #include "trinityd.h" #include "r600_dpm.h" #include "trinity_dpm.h" +#include #define TRINITY_MAX_DEEPSLEEP_DIVIDER_ID 5 #define TRINITY_MINIMUM_ENGINE_CLOCK 800 -- cgit v1.2.3 From 1bc2774d866444c5b316fd1dc2b782f20c762cf4 Mon Sep 17 00:00:00 2001 From: Eliezer Tamir Date: Tue, 2 Jul 2013 23:22:47 +0300 Subject: net: convert lls to use time_in_range() Time in range will fail safely if we move to a different cpu with an extremely large clock skew. Add time_in_range64() and convert lls to use it. changelog: v2 - fixed double call to sched_clock in can_poll_ll - fixed checkpatchisms Signed-off-by: Eliezer Tamir Signed-off-by: David S. Miller --- fs/select.c | 10 ++++++---- include/linux/jiffies.h | 4 ++++ include/net/ll_poll.h | 26 +++++++++++++++++--------- 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/fs/select.c b/fs/select.c index 36540754bad7..f28a58592725 100644 --- a/fs/select.c +++ b/fs/select.c @@ -403,7 +403,8 @@ int do_select(int n, fd_set_bits *fds, struct timespec *end_time) int retval, i, timed_out = 0; unsigned long slack = 0; unsigned int ll_flag = ll_get_flag(); - u64 ll_time = ll_end_time(); + u64 ll_start = ll_start_time(ll_flag); + u64 ll_time = ll_run_time(); rcu_read_lock(); retval = max_select_fd(n, fds); @@ -498,7 +499,7 @@ int do_select(int n, fd_set_bits *fds, struct timespec *end_time) } /* only if on, have sockets with POLL_LL and not out of time */ - if (ll_flag && can_ll && can_poll_ll(ll_time)) + if (ll_flag && can_ll && can_poll_ll(ll_start, ll_time)) continue; /* @@ -770,7 +771,8 @@ static int do_poll(unsigned int nfds, struct poll_list *list, int timed_out = 0, count = 0; unsigned long slack = 0; unsigned int ll_flag = ll_get_flag(); - u64 ll_time = ll_end_time(); + u64 ll_start = ll_start_time(ll_flag); + u64 ll_time = ll_run_time(); /* Optimise the no-wait case */ if (end_time && !end_time->tv_sec && !end_time->tv_nsec) { @@ -819,7 +821,7 @@ static int do_poll(unsigned int nfds, struct poll_list *list, break; /* only if on, have sockets with POLL_LL and not out of time */ - if (ll_flag && can_ll && can_poll_ll(ll_time)) + if (ll_flag && can_ll && can_poll_ll(ll_start, ll_time)) continue; /* diff --git a/include/linux/jiffies.h b/include/linux/jiffies.h index 8fb8edf12417..97ba4e78a37e 100644 --- a/include/linux/jiffies.h +++ b/include/linux/jiffies.h @@ -139,6 +139,10 @@ static inline u64 get_jiffies_64(void) ((__s64)(a) - (__s64)(b) >= 0)) #define time_before_eq64(a,b) time_after_eq64(b,a) +#define time_in_range64(a, b, c) \ + (time_after_eq64(a, b) && \ + time_before_eq64(a, c)) + /* * These four macros compare jiffies and 'a' for convenience. */ diff --git a/include/net/ll_poll.h b/include/net/ll_poll.h index 6c06f7c0a22d..b76f004f18a9 100644 --- a/include/net/ll_poll.h +++ b/include/net/ll_poll.h @@ -67,19 +67,23 @@ static inline u64 ll_sched_clock(void) /* we don't mind a ~2.5% imprecision so <<10 instead of *1000 * sk->sk_ll_usec is a u_int so this can't overflow */ -static inline u64 ll_sk_end_time(struct sock *sk) +static inline u64 ll_sk_run_time(struct sock *sk) { - return ((u64)ACCESS_ONCE(sk->sk_ll_usec) << 10) + ll_sched_clock(); + return (u64)ACCESS_ONCE(sk->sk_ll_usec) << 10; } /* in poll/select we use the global sysctl_net_ll_poll value * only call sched_clock() if enabled */ -static inline u64 ll_end_time(void) +static inline u64 ll_run_time(void) { - u64 end_time = ACCESS_ONCE(sysctl_net_ll_poll); + return (u64)ACCESS_ONCE(sysctl_net_ll_poll) << 10; +} - return end_time ? (end_time << 10) + ll_sched_clock() : 0; +/* if flag is not set we don't need to know the time */ +static inline u64 ll_start_time(unsigned int flag) +{ + return flag ? ll_sched_clock() : 0; } static inline bool sk_valid_ll(struct sock *sk) @@ -88,9 +92,12 @@ static inline bool sk_valid_ll(struct sock *sk) !need_resched() && !signal_pending(current); } -static inline bool can_poll_ll(u64 end_time) +/* careful! time_in_range64 will evaluate now twice */ +static inline bool can_poll_ll(u64 start_time, u64 run_time) { - return !time_after64(ll_sched_clock(), end_time); + u64 now = ll_sched_clock(); + + return time_in_range64(now, start_time, start_time + run_time); } /* when used in sock_poll() nonblock is known at compile time to be true @@ -98,7 +105,8 @@ static inline bool can_poll_ll(u64 end_time) */ static inline bool sk_poll_ll(struct sock *sk, int nonblock) { - u64 end_time = nonblock ? 0 : ll_sk_end_time(sk); + u64 start_time = ll_start_time(!nonblock); + u64 run_time = ll_sk_run_time(sk); const struct net_device_ops *ops; struct napi_struct *napi; int rc = false; @@ -129,7 +137,7 @@ static inline bool sk_poll_ll(struct sock *sk, int nonblock) LINUX_MIB_LOWLATENCYRXPACKETS, rc); } while (!nonblock && skb_queue_empty(&sk->sk_receive_queue) && - can_poll_ll(end_time)); + can_poll_ll(start_time, run_time)); rc = !skb_queue_empty(&sk->sk_receive_queue); out: -- cgit v1.2.3 From 06a23fe31ca3992863721f21bdb0307af93da807 Mon Sep 17 00:00:00 2001 From: Isaku Yamahata Date: Tue, 2 Jul 2013 20:30:10 +0900 Subject: core/dev: set pkt_type after eth_type_trans() in dev_forward_skb() The dev_forward_skb() assignment of pkt_type should be done after the call to eth_type_trans(). ip-encapsulated packets can be handled by localhost. But skb->pkt_type can be PACKET_OTHERHOST when packet comes via veth into ip tunnel device. In that case, the packet is dropped by ip_rcv(). Although this example uses gretap. l2tp-eth also has same issue. For l2tp-eth case, add dummy device for ip address and ip l2tp command. netns A | root netns | netns B veth<->veth=bridge=gretap <-loop back-> gretap=bridge=veth<->veth arp packet -> pkt_type BROADCAST------------>ip_rcv()------------------------> <- arp reply pkt_type ip_rcv()<-----------------OTHERHOST drop sample operations ip link add tapa type gretap remote 172.17.107.4 local 172.17.107.3 ip link add tapb type gretap remote 172.17.107.3 local 172.17.107.4 ip link set tapa up ip link set tapb up ip address add 172.17.107.3 dev tapa ip address add 172.17.107.4 dev tapb ip route get 172.17.107.3 > local 172.17.107.3 dev lo src 172.17.107.3 > cache ip route get 172.17.107.4 > local 172.17.107.4 dev lo src 172.17.107.4 > cache ip link add vetha type veth peer name vetha-peer ip link add vethb type veth peer name vethb-peer brctl addbr bra brctl addbr brb brctl addif bra tapa brctl addif bra vetha-peer brctl addif brb tapb brctl addif brb vethb-peer brctl show > bridge name bridge id STP enabled interfaces > bra 8000.6ea21e758ff1 no tapa > vetha-peer > brb 8000.420020eb92d5 no tapb > vethb-peer ip link set vetha-peer up ip link set vethb-peer up ip link set bra up ip link set brb up ip netns add a ip netns add b ip link set vetha netns a ip link set vethb netns b ip netns exec a ip address add 10.0.0.3/24 dev vetha ip netns exec b ip address add 10.0.0.4/24 dev vethb ip netns exec a ip link set vetha up ip netns exec b ip link set vethb up ip netns exec a arping -I vetha 10.0.0.4 ARPING 10.0.0.4 from 10.0.0.3 vetha ^CSent 2 probes (2 broadcast(s)) Received 0 response(s) Cc: Jason Wang Cc: "Michael S. Tsirkin" Cc: Eric Dumazet Cc: Patrick McHardy Cc: Hong Zhiguo Cc: Rami Rosen Cc: Tom Parkin Cc: Cong Wang Cc: Pravin B Shelar Cc: Jesse Gross Cc: dev@openvswitch.org Signed-off-by: Isaku Yamahata Signed-off-by: David S. Miller --- net/core/dev.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/core/dev.c b/net/core/dev.c index 370354a9c5f6..6a93cd8cd264 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1659,6 +1659,12 @@ int dev_forward_skb(struct net_device *dev, struct sk_buff *skb) } skb_scrub_packet(skb); skb->protocol = eth_type_trans(skb, dev); + + /* eth_type_trans() can set pkt_type. + * clear pkt_type _after_ calling eth_type_trans() + */ + skb->pkt_type = PACKET_HOST; + return netif_rx(skb); } EXPORT_SYMBOL_GPL(dev_forward_skb); -- cgit v1.2.3 From 83d7af64ac9eaf4f4db7228677bc25f23c383790 Mon Sep 17 00:00:00 2001 From: Giuseppe CAVALLARO Date: Tue, 2 Jul 2013 14:12:36 +0200 Subject: stmmac: dity-up and rework the driver debug levels Prior this patch, the internal debugging was based on ifdef and also some printk were useless because many info are exposed via ethtool. This patch remove all the ifdef defines and now we only use netif_msg_XXX levels. Signed-off-by: Giuseppe Cavallaro Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/common.h | 10 -- .../net/ethernet/stmicro/stmmac/dwmac1000_core.c | 57 ++++------ .../net/ethernet/stmicro/stmmac/dwmac1000_dma.c | 10 +- .../net/ethernet/stmicro/stmmac/dwmac100_core.c | 4 - drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c | 8 +- drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c | 72 +++++------- drivers/net/ethernet/stmicro/stmmac/enh_desc.c | 95 +++++----------- drivers/net/ethernet/stmicro/stmmac/norm_desc.c | 4 +- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 124 +++++++-------------- 9 files changed, 117 insertions(+), 267 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 7788fbe44f0a..9911b9323f00 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -38,16 +38,6 @@ #include "descs.h" #include "mmc.h" -#undef CHIP_DEBUG_PRINT -/* Turn-on extra printk debug for MAC core, dma and descriptors */ -/* #define CHIP_DEBUG_PRINT */ - -#ifdef CHIP_DEBUG_PRINT -#define CHIP_DBG(fmt, args...) printk(fmt, ## args) -#else -#define CHIP_DBG(fmt, args...) do { } while (0) -#endif - /* Synopsys Core versions */ #define DWMAC_CORE_3_40 0x34 #define DWMAC_CORE_3_50 0x35 diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c index 7e05e8d0f1c2..cdd926832e27 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c @@ -91,8 +91,8 @@ static void dwmac1000_set_filter(struct net_device *dev, int id) unsigned int value = 0; unsigned int perfect_addr_number; - CHIP_DBG(KERN_INFO "%s: # mcasts %d, # unicast %d\n", - __func__, netdev_mc_count(dev), netdev_uc_count(dev)); + pr_debug("%s: # mcasts %d, # unicast %d\n", __func__, + netdev_mc_count(dev), netdev_uc_count(dev)); if (dev->flags & IFF_PROMISC) value = GMAC_FRAME_FILTER_PR; @@ -152,7 +152,7 @@ static void dwmac1000_set_filter(struct net_device *dev, int id) #endif writel(value, ioaddr + GMAC_FRAME_FILTER); - CHIP_DBG(KERN_INFO "\tFilter: 0x%08x\n\tHash: HI 0x%08x, LO 0x%08x\n", + pr_debug("\tFilter: 0x%08x\n\tHash: HI 0x%08x, LO 0x%08x\n", readl(ioaddr + GMAC_FRAME_FILTER), readl(ioaddr + GMAC_HASH_HIGH), readl(ioaddr + GMAC_HASH_LOW)); } @@ -162,18 +162,18 @@ static void dwmac1000_flow_ctrl(void __iomem *ioaddr, unsigned int duplex, { unsigned int flow = 0; - CHIP_DBG(KERN_DEBUG "GMAC Flow-Control:\n"); + pr_debug("GMAC Flow-Control:\n"); if (fc & FLOW_RX) { - CHIP_DBG(KERN_DEBUG "\tReceive Flow-Control ON\n"); + pr_debug("\tReceive Flow-Control ON\n"); flow |= GMAC_FLOW_CTRL_RFE; } if (fc & FLOW_TX) { - CHIP_DBG(KERN_DEBUG "\tTransmit Flow-Control ON\n"); + pr_debug("\tTransmit Flow-Control ON\n"); flow |= GMAC_FLOW_CTRL_TFE; } if (duplex) { - CHIP_DBG(KERN_DEBUG "\tduplex mode: PAUSE %d\n", pause_time); + pr_debug("\tduplex mode: PAUSE %d\n", pause_time); flow |= (pause_time << GMAC_FLOW_CTRL_PT_SHIFT); } @@ -185,11 +185,11 @@ static void dwmac1000_pmt(void __iomem *ioaddr, unsigned long mode) unsigned int pmt = 0; if (mode & WAKE_MAGIC) { - CHIP_DBG(KERN_DEBUG "GMAC: WOL Magic frame\n"); + pr_debug("GMAC: WOL Magic frame\n"); pmt |= power_down | magic_pkt_en; } if (mode & WAKE_UCAST) { - CHIP_DBG(KERN_DEBUG "GMAC: WOL on global unicast\n"); + pr_debug("GMAC: WOL on global unicast\n"); pmt |= global_unicast; } @@ -203,23 +203,13 @@ static int dwmac1000_irq_status(void __iomem *ioaddr, int ret = 0; /* Not used events (e.g. MMC interrupts) are not handled. */ - if ((intr_status & mmc_tx_irq)) { - CHIP_DBG(KERN_INFO "GMAC: MMC tx interrupt: 0x%08x\n", - readl(ioaddr + GMAC_MMC_TX_INTR)); + if ((intr_status & mmc_tx_irq)) x->mmc_tx_irq_n++; - } - if (unlikely(intr_status & mmc_rx_irq)) { - CHIP_DBG(KERN_INFO "GMAC: MMC rx interrupt: 0x%08x\n", - readl(ioaddr + GMAC_MMC_RX_INTR)); + if (unlikely(intr_status & mmc_rx_irq)) x->mmc_rx_irq_n++; - } - if (unlikely(intr_status & mmc_rx_csum_offload_irq)) { - CHIP_DBG(KERN_INFO "GMAC: MMC rx csum offload: 0x%08x\n", - readl(ioaddr + GMAC_MMC_RX_CSUM_OFFLOAD)); + if (unlikely(intr_status & mmc_rx_csum_offload_irq)) x->mmc_rx_csum_offload_irq_n++; - } if (unlikely(intr_status & pmt_irq)) { - CHIP_DBG(KERN_INFO "GMAC: received Magic frame\n"); /* clear the PMT bits 5 and 6 by reading the PMT status reg */ readl(ioaddr + GMAC_PMT); x->irq_receive_pmt_irq_n++; @@ -229,32 +219,22 @@ static int dwmac1000_irq_status(void __iomem *ioaddr, /* Clean LPI interrupt by reading the Reg 12 */ ret = readl(ioaddr + LPI_CTRL_STATUS); - if (ret & LPI_CTRL_STATUS_TLPIEN) { - CHIP_DBG(KERN_INFO "GMAC TX entered in LPI\n"); + if (ret & LPI_CTRL_STATUS_TLPIEN) x->irq_tx_path_in_lpi_mode_n++; - } - if (ret & LPI_CTRL_STATUS_TLPIEX) { - CHIP_DBG(KERN_INFO "GMAC TX exit from LPI\n"); + if (ret & LPI_CTRL_STATUS_TLPIEX) x->irq_tx_path_exit_lpi_mode_n++; - } - if (ret & LPI_CTRL_STATUS_RLPIEN) { - CHIP_DBG(KERN_INFO "GMAC RX entered in LPI\n"); + if (ret & LPI_CTRL_STATUS_RLPIEN) x->irq_rx_path_in_lpi_mode_n++; - } - if (ret & LPI_CTRL_STATUS_RLPIEX) { - CHIP_DBG(KERN_INFO "GMAC RX exit from LPI\n"); + if (ret & LPI_CTRL_STATUS_RLPIEX) x->irq_rx_path_exit_lpi_mode_n++; - } } if ((intr_status & pcs_ane_irq) || (intr_status & pcs_link_irq)) { - CHIP_DBG(KERN_INFO "GMAC PCS ANE IRQ\n"); readl(ioaddr + GMAC_AN_STATUS); x->irq_pcs_ane_n++; } if (intr_status & rgmii_irq) { u32 status = readl(ioaddr + GMAC_S_R_GMII); - CHIP_DBG(KERN_INFO "GMAC RGMII/SGMII interrupt\n"); x->irq_rgmii_n++; /* Save and dump the link status. */ @@ -271,11 +251,12 @@ static int dwmac1000_irq_status(void __iomem *ioaddr, x->pcs_speed = SPEED_10; x->pcs_link = 1; - pr_debug("Link is Up - %d/%s\n", (int)x->pcs_speed, + pr_debug("%s: Link is Up - %d/%s\n", __func__, + (int)x->pcs_speed, x->pcs_duplex ? "Full" : "Half"); } else { x->pcs_link = 0; - pr_debug("Link is Down\n"); + pr_debug("%s: Link is Down\n", __func__); } } diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c index 2c431b616058..0c2058a69fd2 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c @@ -116,7 +116,7 @@ static void dwmac1000_dma_operation_mode(void __iomem *ioaddr, int txmode, u32 csr6 = readl(ioaddr + DMA_CONTROL); if (txmode == SF_DMA_MODE) { - CHIP_DBG(KERN_DEBUG "GMAC: enable TX store and forward mode\n"); + pr_debug("GMAC: enable TX store and forward mode\n"); /* Transmit COE type 2 cannot be done in cut-through mode. */ csr6 |= DMA_CONTROL_TSF; /* Operating on second frame increase the performance @@ -124,8 +124,7 @@ static void dwmac1000_dma_operation_mode(void __iomem *ioaddr, int txmode, */ csr6 |= DMA_CONTROL_OSF; } else { - CHIP_DBG(KERN_DEBUG "GMAC: disabling TX SF (threshold %d)\n", - txmode); + pr_debug("GMAC: disabling TX SF (threshold %d)\n", txmode); csr6 &= ~DMA_CONTROL_TSF; csr6 &= DMA_CONTROL_TC_TX_MASK; /* Set the transmit threshold */ @@ -142,11 +141,10 @@ static void dwmac1000_dma_operation_mode(void __iomem *ioaddr, int txmode, } if (rxmode == SF_DMA_MODE) { - CHIP_DBG(KERN_DEBUG "GMAC: enable RX store and forward mode\n"); + pr_debug("GMAC: enable RX store and forward mode\n"); csr6 |= DMA_CONTROL_RSF; } else { - CHIP_DBG(KERN_DEBUG "GMAC: disable RX SF mode (threshold %d)\n", - rxmode); + pr_debug("GMAC: disable RX SF mode (threshold %d)\n", rxmode); csr6 &= ~DMA_CONTROL_RSF; csr6 &= DMA_CONTROL_TC_RX_MASK; if (rxmode <= 32) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c index 007bb2be3f10..5857d677dac1 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c @@ -135,10 +135,6 @@ static void dwmac100_set_filter(struct net_device *dev, int id) } writel(value, ioaddr + MAC_CONTROL); - - CHIP_DBG(KERN_INFO "%s: Filter: 0x%08x Hash: HI 0x%08x, LO 0x%08x\n", - __func__, readl(ioaddr + MAC_CONTROL), - readl(ioaddr + MAC_HASH_HIGH), readl(ioaddr + MAC_HASH_LOW)); } static void dwmac100_flow_ctrl(void __iomem *ioaddr, unsigned int duplex, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c index 67551c154138..7d1dce9e7ffc 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c @@ -90,14 +90,14 @@ static void dwmac100_dump_dma_regs(void __iomem *ioaddr) { int i; - CHIP_DBG(KERN_DEBUG "DWMAC 100 DMA CSR\n"); + pr_debug("DWMAC 100 DMA CSR\n"); for (i = 0; i < 9; i++) pr_debug("\t CSR%d (offset 0x%x): 0x%08x\n", i, (DMA_BUS_MODE + i * 4), readl(ioaddr + DMA_BUS_MODE + i * 4)); - CHIP_DBG(KERN_DEBUG "\t CSR20 (offset 0x%x): 0x%08x\n", - DMA_CUR_TX_BUF_ADDR, readl(ioaddr + DMA_CUR_TX_BUF_ADDR)); - CHIP_DBG(KERN_DEBUG "\t CSR21 (offset 0x%x): 0x%08x\n", + + pr_debug("\tCSR20 (0x%x): 0x%08x, CSR21 (0x%x): 0x%08x\n", + DMA_CUR_TX_BUF_ADDR, readl(ioaddr + DMA_CUR_TX_BUF_ADDR), DMA_CUR_RX_BUF_ADDR, readl(ioaddr + DMA_CUR_RX_BUF_ADDR)); } diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c index 491d7e930603..484e3cf9c414 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c @@ -24,13 +24,6 @@ #include "common.h" #include "dwmac_dma.h" -#undef DWMAC_DMA_DEBUG -#ifdef DWMAC_DMA_DEBUG -#define DWMAC_LIB_DBG(fmt, args...) printk(fmt, ## args) -#else -#define DWMAC_LIB_DBG(fmt, args...) do { } while (0) -#endif - #define GMAC_HI_REG_AE 0x80000000 /* CSR1 enables the transmit DMA to check for new descriptor */ @@ -85,24 +78,24 @@ static void show_tx_process_state(unsigned int status) switch (state) { case 0: - pr_info("- TX (Stopped): Reset or Stop command\n"); + pr_debug("- TX (Stopped): Reset or Stop command\n"); break; case 1: - pr_info("- TX (Running):Fetching the Tx desc\n"); + pr_debug("- TX (Running):Fetching the Tx desc\n"); break; case 2: - pr_info("- TX (Running): Waiting for end of tx\n"); + pr_debug("- TX (Running): Waiting for end of tx\n"); break; case 3: - pr_info("- TX (Running): Reading the data " + pr_debug("- TX (Running): Reading the data " "and queuing the data into the Tx buf\n"); break; case 6: - pr_info("- TX (Suspended): Tx Buff Underflow " + pr_debug("- TX (Suspended): Tx Buff Underflow " "or an unavailable Transmit descriptor\n"); break; case 7: - pr_info("- TX (Running): Closing Tx descriptor\n"); + pr_debug("- TX (Running): Closing Tx descriptor\n"); break; default: break; @@ -116,29 +109,29 @@ static void show_rx_process_state(unsigned int status) switch (state) { case 0: - pr_info("- RX (Stopped): Reset or Stop command\n"); + pr_debug("- RX (Stopped): Reset or Stop command\n"); break; case 1: - pr_info("- RX (Running): Fetching the Rx desc\n"); + pr_debug("- RX (Running): Fetching the Rx desc\n"); break; case 2: - pr_info("- RX (Running):Checking for end of pkt\n"); + pr_debug("- RX (Running):Checking for end of pkt\n"); break; case 3: - pr_info("- RX (Running): Waiting for Rx pkt\n"); + pr_debug("- RX (Running): Waiting for Rx pkt\n"); break; case 4: - pr_info("- RX (Suspended): Unavailable Rx buf\n"); + pr_debug("- RX (Suspended): Unavailable Rx buf\n"); break; case 5: - pr_info("- RX (Running): Closing Rx descriptor\n"); + pr_debug("- RX (Running): Closing Rx descriptor\n"); break; case 6: - pr_info("- RX(Running): Flushing the current frame" + pr_debug("- RX(Running): Flushing the current frame" " from the Rx buf\n"); break; case 7: - pr_info("- RX (Running): Queuing the Rx frame" + pr_debug("- RX (Running): Queuing the Rx frame" " from the Rx buf into memory\n"); break; default: @@ -154,51 +147,37 @@ int dwmac_dma_interrupt(void __iomem *ioaddr, /* read the status register (CSR5) */ u32 intr_status = readl(ioaddr + DMA_STATUS); - DWMAC_LIB_DBG(KERN_INFO "%s: [CSR5: 0x%08x]\n", __func__, intr_status); #ifdef DWMAC_DMA_DEBUG - /* It displays the DMA process states (CSR5 register) */ + /* Enable it to monitor DMA rx/tx status in case of critical problems */ + pr_debug("%s: [CSR5: 0x%08x]\n", __func__, intr_status); show_tx_process_state(intr_status); show_rx_process_state(intr_status); #endif /* ABNORMAL interrupts */ if (unlikely(intr_status & DMA_STATUS_AIS)) { - DWMAC_LIB_DBG(KERN_INFO "CSR5[15] DMA ABNORMAL IRQ: "); if (unlikely(intr_status & DMA_STATUS_UNF)) { - DWMAC_LIB_DBG(KERN_INFO "transmit underflow\n"); ret = tx_hard_error_bump_tc; x->tx_undeflow_irq++; } - if (unlikely(intr_status & DMA_STATUS_TJT)) { - DWMAC_LIB_DBG(KERN_INFO "transmit jabber\n"); + if (unlikely(intr_status & DMA_STATUS_TJT)) x->tx_jabber_irq++; - } - if (unlikely(intr_status & DMA_STATUS_OVF)) { - DWMAC_LIB_DBG(KERN_INFO "recv overflow\n"); + + if (unlikely(intr_status & DMA_STATUS_OVF)) x->rx_overflow_irq++; - } - if (unlikely(intr_status & DMA_STATUS_RU)) { - DWMAC_LIB_DBG(KERN_INFO "receive buffer unavailable\n"); + + if (unlikely(intr_status & DMA_STATUS_RU)) x->rx_buf_unav_irq++; - } - if (unlikely(intr_status & DMA_STATUS_RPS)) { - DWMAC_LIB_DBG(KERN_INFO "receive process stopped\n"); + if (unlikely(intr_status & DMA_STATUS_RPS)) x->rx_process_stopped_irq++; - } - if (unlikely(intr_status & DMA_STATUS_RWT)) { - DWMAC_LIB_DBG(KERN_INFO "receive watchdog\n"); + if (unlikely(intr_status & DMA_STATUS_RWT)) x->rx_watchdog_irq++; - } - if (unlikely(intr_status & DMA_STATUS_ETI)) { - DWMAC_LIB_DBG(KERN_INFO "transmit early interrupt\n"); + if (unlikely(intr_status & DMA_STATUS_ETI)) x->tx_early_irq++; - } if (unlikely(intr_status & DMA_STATUS_TPS)) { - DWMAC_LIB_DBG(KERN_INFO "transmit process stopped\n"); x->tx_process_stopped_irq++; ret = tx_hard_error; } if (unlikely(intr_status & DMA_STATUS_FBI)) { - DWMAC_LIB_DBG(KERN_INFO "fatal bus error\n"); x->fatal_bus_error_irq++; ret = tx_hard_error; } @@ -224,12 +203,11 @@ int dwmac_dma_interrupt(void __iomem *ioaddr, /* Optional hardware blocks, interrupts should be disabled */ if (unlikely(intr_status & (DMA_STATUS_GPI | DMA_STATUS_GMI | DMA_STATUS_GLI))) - pr_info("%s: unexpected status %08x\n", __func__, intr_status); + pr_warn("%s: unexpected status %08x\n", __func__, intr_status); /* Clear the interrupt by writing a logic 1 to the CSR5[15-0] */ writel((intr_status & 0x1ffff), ioaddr + DMA_STATUS); - DWMAC_LIB_DBG(KERN_INFO "\n\n"); return ret; } diff --git a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c index 0fbc8fafa706..7e6628a91514 100644 --- a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c +++ b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c @@ -33,54 +33,40 @@ static int enh_desc_get_tx_status(void *data, struct stmmac_extra_stats *x, struct net_device_stats *stats = (struct net_device_stats *)data; if (unlikely(p->des01.etx.error_summary)) { - CHIP_DBG(KERN_ERR "GMAC TX error... 0x%08x\n", p->des01.etx); - if (unlikely(p->des01.etx.jabber_timeout)) { - CHIP_DBG(KERN_ERR "\tjabber_timeout error\n"); + if (unlikely(p->des01.etx.jabber_timeout)) x->tx_jabber++; - } if (unlikely(p->des01.etx.frame_flushed)) { - CHIP_DBG(KERN_ERR "\tframe_flushed error\n"); x->tx_frame_flushed++; dwmac_dma_flush_tx_fifo(ioaddr); } if (unlikely(p->des01.etx.loss_carrier)) { - CHIP_DBG(KERN_ERR "\tloss_carrier error\n"); x->tx_losscarrier++; stats->tx_carrier_errors++; } if (unlikely(p->des01.etx.no_carrier)) { - CHIP_DBG(KERN_ERR "\tno_carrier error\n"); x->tx_carrier++; stats->tx_carrier_errors++; } - if (unlikely(p->des01.etx.late_collision)) { - CHIP_DBG(KERN_ERR "\tlate_collision error\n"); + if (unlikely(p->des01.etx.late_collision)) stats->collisions += p->des01.etx.collision_count; - } - if (unlikely(p->des01.etx.excessive_collisions)) { - CHIP_DBG(KERN_ERR "\texcessive_collisions\n"); + + if (unlikely(p->des01.etx.excessive_collisions)) stats->collisions += p->des01.etx.collision_count; - } - if (unlikely(p->des01.etx.excessive_deferral)) { - CHIP_DBG(KERN_INFO "\texcessive tx_deferral\n"); + + if (unlikely(p->des01.etx.excessive_deferral)) x->tx_deferred++; - } if (unlikely(p->des01.etx.underflow_error)) { - CHIP_DBG(KERN_ERR "\tunderflow error\n"); dwmac_dma_flush_tx_fifo(ioaddr); x->tx_underflow++; } - if (unlikely(p->des01.etx.ip_header_error)) { - CHIP_DBG(KERN_ERR "\tTX IP header csum error\n"); + if (unlikely(p->des01.etx.ip_header_error)) x->tx_ip_header_error++; - } if (unlikely(p->des01.etx.payload_error)) { - CHIP_DBG(KERN_ERR "\tAddr/Payload csum error\n"); x->tx_payload_error++; dwmac_dma_flush_tx_fifo(ioaddr); } @@ -88,15 +74,12 @@ static int enh_desc_get_tx_status(void *data, struct stmmac_extra_stats *x, ret = -1; } - if (unlikely(p->des01.etx.deferred)) { - CHIP_DBG(KERN_INFO "GMAC TX status: tx deferred\n"); + if (unlikely(p->des01.etx.deferred)) x->tx_deferred++; - } + #ifdef STMMAC_VLAN_TAG_USED - if (p->des01.etx.vlan_frame) { - CHIP_DBG(KERN_INFO "GMAC TX status: VLAN frame\n"); + if (p->des01.etx.vlan_frame) x->tx_vlan++; - } #endif return ret; @@ -123,30 +106,20 @@ static int enh_desc_coe_rdes0(int ipc_err, int type, int payload_err) * 0 1 1 | COE bypassed.. no IPv4/6 frame * 0 1 0 | Reserved. */ - if (status == 0x0) { - CHIP_DBG(KERN_INFO "RX Des0 status: IEEE 802.3 Type frame.\n"); + if (status == 0x0) ret = llc_snap; - } else if (status == 0x4) { - CHIP_DBG(KERN_INFO "RX Des0 status: IPv4/6 No CSUM errorS.\n"); + else if (status == 0x4) ret = good_frame; - } else if (status == 0x5) { - CHIP_DBG(KERN_ERR "RX Des0 status: IPv4/6 Payload Error.\n"); + else if (status == 0x5) ret = csum_none; - } else if (status == 0x6) { - CHIP_DBG(KERN_ERR "RX Des0 status: IPv4/6 Header Error.\n"); + else if (status == 0x6) ret = csum_none; - } else if (status == 0x7) { - CHIP_DBG(KERN_ERR - "RX Des0 status: IPv4/6 Header and Payload Error.\n"); + else if (status == 0x7) ret = csum_none; - } else if (status == 0x1) { - CHIP_DBG(KERN_ERR - "RX Des0 status: IPv4/6 unsupported IP PAYLOAD.\n"); + else if (status == 0x1) ret = discard_frame; - } else if (status == 0x3) { - CHIP_DBG(KERN_ERR "RX Des0 status: No IPv4, IPv6 frame.\n"); + else if (status == 0x3) ret = discard_frame; - } return ret; } @@ -208,36 +181,26 @@ static int enh_desc_get_rx_status(void *data, struct stmmac_extra_stats *x, struct net_device_stats *stats = (struct net_device_stats *)data; if (unlikely(p->des01.erx.error_summary)) { - CHIP_DBG(KERN_ERR "GMAC RX Error Summary 0x%08x\n", - p->des01.erx); if (unlikely(p->des01.erx.descriptor_error)) { - CHIP_DBG(KERN_ERR "\tdescriptor error\n"); x->rx_desc++; stats->rx_length_errors++; } - if (unlikely(p->des01.erx.overflow_error)) { - CHIP_DBG(KERN_ERR "\toverflow error\n"); + if (unlikely(p->des01.erx.overflow_error)) x->rx_gmac_overflow++; - } if (unlikely(p->des01.erx.ipc_csum_error)) - CHIP_DBG(KERN_ERR "\tIPC Csum Error/Giant frame\n"); + pr_err("\tIPC Csum Error/Giant frame\n"); if (unlikely(p->des01.erx.late_collision)) { - CHIP_DBG(KERN_ERR "\tlate_collision error\n"); - stats->collisions++; stats->collisions++; } - if (unlikely(p->des01.erx.receive_watchdog)) { - CHIP_DBG(KERN_ERR "\treceive_watchdog error\n"); + if (unlikely(p->des01.erx.receive_watchdog)) x->rx_watchdog++; - } - if (unlikely(p->des01.erx.error_gmii)) { - CHIP_DBG(KERN_ERR "\tReceive Error\n"); + + if (unlikely(p->des01.erx.error_gmii)) x->rx_mii++; - } + if (unlikely(p->des01.erx.crc_error)) { - CHIP_DBG(KERN_ERR "\tCRC error\n"); x->rx_crc++; stats->rx_crc_errors++; } @@ -251,30 +214,24 @@ static int enh_desc_get_rx_status(void *data, struct stmmac_extra_stats *x, ret = enh_desc_coe_rdes0(p->des01.erx.ipc_csum_error, p->des01.erx.frame_type, p->des01.erx.rx_mac_addr); - if (unlikely(p->des01.erx.dribbling)) { - CHIP_DBG(KERN_ERR "GMAC RX: dribbling error\n"); + if (unlikely(p->des01.erx.dribbling)) x->dribbling_bit++; - } + if (unlikely(p->des01.erx.sa_filter_fail)) { - CHIP_DBG(KERN_ERR "GMAC RX : Source Address filter fail\n"); x->sa_rx_filter_fail++; ret = discard_frame; } if (unlikely(p->des01.erx.da_filter_fail)) { - CHIP_DBG(KERN_ERR "GMAC RX : Dest Address filter fail\n"); x->da_rx_filter_fail++; ret = discard_frame; } if (unlikely(p->des01.erx.length_error)) { - CHIP_DBG(KERN_ERR "GMAC RX: length_error error\n"); x->rx_length++; ret = discard_frame; } #ifdef STMMAC_VLAN_TAG_USED - if (p->des01.erx.vlan_tag) { - CHIP_DBG(KERN_INFO "GMAC RX: VLAN frame tagged\n"); + if (p->des01.erx.vlan_tag) x->rx_vlan++; - } #endif return ret; diff --git a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c index 11775b99afc5..35ad4f427ae2 100644 --- a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c +++ b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c @@ -52,10 +52,8 @@ static int ndesc_get_tx_status(void *data, struct stmmac_extra_stats *x, ret = -1; } - if (p->des01.etx.vlan_frame) { - CHIP_DBG(KERN_INFO "GMAC TX status: VLAN frame\n"); + if (p->des01.etx.vlan_frame) x->tx_vlan++; - } if (unlikely(p->des01.tx.deferred)) x->tx_deferred++; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 520693385d8d..62e31054bd24 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -51,32 +51,6 @@ #include "stmmac_ptp.h" #include "stmmac.h" -#undef STMMAC_DEBUG -/*#define STMMAC_DEBUG*/ -#ifdef STMMAC_DEBUG -#define DBG(nlevel, klevel, fmt, args...) \ - ((void)(netif_msg_##nlevel(priv) && \ - printk(KERN_##klevel fmt, ## args))) -#else -#define DBG(nlevel, klevel, fmt, args...) do { } while (0) -#endif - -#undef STMMAC_RX_DEBUG -/*#define STMMAC_RX_DEBUG*/ -#ifdef STMMAC_RX_DEBUG -#define RX_DBG(fmt, args...) printk(fmt, ## args) -#else -#define RX_DBG(fmt, args...) do { } while (0) -#endif - -#undef STMMAC_XMIT_DEBUG -/*#define STMMAC_XMIT_DEBUG*/ -#ifdef STMMAC_XMIT_DEBUG -#define TX_DBG(fmt, args...) printk(fmt, ## args) -#else -#define TX_DBG(fmt, args...) do { } while (0) -#endif - #define STMMAC_ALIGN(x) L1_CACHE_ALIGN(x) #define JUMBO_LEN 9000 @@ -214,19 +188,17 @@ static void stmmac_clk_csr_set(struct stmmac_priv *priv) } } -#if defined(STMMAC_XMIT_DEBUG) || defined(STMMAC_RX_DEBUG) static void print_pkt(unsigned char *buf, int len) { int j; - pr_info("len = %d byte, buf addr: 0x%p", len, buf); + pr_debug("len = %d byte, buf addr: 0x%p", len, buf); for (j = 0; j < len; j++) { if ((j % 16) == 0) - pr_info("\n %03x:", j); - pr_info(" %02x", buf[j]); + pr_debug("\n %03x:", j); + pr_debug(" %02x", buf[j]); } - pr_info("\n"); + pr_debug("\n"); } -#endif /* minimum number of free TX descriptors required to wake up TX process */ #define STMMAC_TX_THRESH(x) (x->dma_tx_size/4) @@ -698,9 +670,6 @@ static void stmmac_adjust_link(struct net_device *dev) if (phydev == NULL) return; - DBG(probe, DEBUG, "stmmac_adjust_link: called. address %d link %d\n", - phydev->addr, phydev->link); - spin_lock_irqsave(&priv->lock, flags); if (phydev->link) { @@ -772,8 +741,6 @@ static void stmmac_adjust_link(struct net_device *dev) stmmac_eee_adjust(priv); spin_unlock_irqrestore(&priv->lock, flags); - - DBG(probe, DEBUG, "stmmac_adjust_link: exiting\n"); } /** @@ -1014,8 +981,9 @@ static void init_dma_desc_rings(struct net_device *dev) if (bfsize < BUF_SIZE_16KiB) bfsize = stmmac_set_bfsize(dev->mtu, priv->dma_buf_sz); - DBG(probe, INFO, "stmmac: txsize %d, rxsize %d, bfsize %d\n", - txsize, rxsize, bfsize); + if (netif_msg_probe(priv)) + pr_debug("%s: txsize %d, rxsize %d, bfsize %d\n", __func__, + txsize, rxsize, bfsize); if (priv->extend_desc) { priv->dma_erx = dma_alloc_coherent(priv->device, rxsize * @@ -1051,12 +1019,13 @@ static void init_dma_desc_rings(struct net_device *dev) GFP_KERNEL); priv->tx_skbuff = kmalloc_array(txsize, sizeof(struct sk_buff *), GFP_KERNEL); - if (netif_msg_drv(priv)) + if (netif_msg_probe(priv)) { pr_debug("(%s) dma_rx_phy=0x%08x dma_tx_phy=0x%08x\n", __func__, (u32) priv->dma_rx_phy, (u32) priv->dma_tx_phy); - /* RX INITIALIZATION */ - DBG(probe, INFO, "stmmac: SKB addresses:\nskb\t\tskb data\tdma data\n"); + /* RX INITIALIZATION */ + pr_debug("\tSKB addresses:\nskb\t\tskb data\tdma data\n"); + } for (i = 0; i < rxsize; i++) { struct dma_desc *p; if (priv->extend_desc) @@ -1067,8 +1036,10 @@ static void init_dma_desc_rings(struct net_device *dev) if (stmmac_init_rx_buffers(priv, p, i)) break; - DBG(probe, INFO, "[%p]\t[%p]\t[%x]\n", priv->rx_skbuff[i], - priv->rx_skbuff[i]->data, priv->rx_skbuff_dma[i]); + if (netif_msg_probe(priv)) + pr_debug("[%p]\t[%p]\t[%x]\n", priv->rx_skbuff[i], + priv->rx_skbuff[i]->data, + (unsigned int)priv->rx_skbuff_dma[i]); } priv->cur_rx = 0; priv->dirty_rx = (unsigned int)(i - rxsize); @@ -1243,8 +1214,9 @@ static void stmmac_tx_clean(struct stmmac_priv *priv) stmmac_get_tx_hwtstamp(priv, entry, skb); } - TX_DBG("%s: curr %d, dirty %d\n", __func__, - priv->cur_tx, priv->dirty_tx); + if (netif_msg_tx_done(priv)) + pr_debug("%s: curr %d, dirty %d\n", __func__, + priv->cur_tx, priv->dirty_tx); if (likely(priv->tx_skbuff_dma[entry])) { dma_unmap_single(priv->device, @@ -1269,7 +1241,8 @@ static void stmmac_tx_clean(struct stmmac_priv *priv) netif_tx_lock(priv->dev); if (netif_queue_stopped(priv->dev) && stmmac_tx_avail(priv) > STMMAC_TX_THRESH(priv)) { - TX_DBG("%s: restart transmit\n", __func__); + if (netif_msg_tx_done(priv)) + pr_debug("%s: restart transmit\n", __func__); netif_wake_queue(priv->dev); } netif_tx_unlock(priv->dev); @@ -1658,7 +1631,7 @@ static int stmmac_open(struct net_device *dev) pr_warn("%s: failed debugFS registration\n", __func__); #endif /* Start the ball rolling... */ - DBG(probe, DEBUG, "%s: DMA RX/TX processes started...\n", dev->name); + pr_debug("%s: DMA RX/TX processes started...\n", dev->name); priv->hw->dma->start_tx(priv->ioaddr); priv->hw->dma->start_rx(priv->ioaddr); @@ -1800,16 +1773,6 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) entry = priv->cur_tx % txsize; -#ifdef STMMAC_XMIT_DEBUG - if ((skb->len > ETH_FRAME_LEN) || nfrags) - pr_debug("%s: [entry %d]: skb addr %p len: %d nopagedlen: %d\n" - "\tn_frags: %d - ip_summed: %d - %s gso\n" - "\ttx_count_frames %d\n", __func__, entry, - skb, skb->len, nopaged_len, nfrags, skb->ip_summed, - !skb_is_gso(skb) ? "isn't" : "is", - priv->tx_count_frames); -#endif - csum_insertion = (skb->ip_summed == CHECKSUM_PARTIAL); if (priv->extend_desc) @@ -1819,12 +1782,6 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) first = desc; -#ifdef STMMAC_XMIT_DEBUG - if ((nfrags > 0) || (skb->len > ETH_FRAME_LEN)) - pr_debug("\tskb len: %d, nopaged_len: %d,\n" - "\t\tn_frags: %d, ip_summed: %d\n", - skb->len, nopaged_len, nfrags, skb->ip_summed); -#endif priv->tx_skbuff[entry] = skb; /* To program the descriptors according to the size of the frame */ @@ -1860,7 +1817,6 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) else desc = priv->dma_tx + entry; - TX_DBG("\t[entry %d] segment len: %d\n", entry, len); desc->des2 = skb_frag_dma_map(priv->device, frag, 0, len, DMA_TO_DEVICE); priv->tx_skbuff_dma[entry] = desc->des2; @@ -1884,8 +1840,6 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) if (priv->tx_coal_frames > priv->tx_count_frames) { priv->hw->desc->clear_tx_ic(desc); priv->xstats.tx_reset_ic_bit++; - TX_DBG("\t[entry %d]: tx_count_frames %d\n", entry, - priv->tx_count_frames); mod_timer(&priv->txtimer, STMMAC_COAL_TIMER(priv->tx_coal_timer)); } else @@ -1897,22 +1851,22 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) priv->cur_tx++; -#ifdef STMMAC_XMIT_DEBUG if (netif_msg_pktdata(priv)) { - pr_info("%s: curr %d dirty=%d entry=%d, first=%p, nfrags=%d", + pr_debug("%s: curr %d dirty=%d entry=%d, first=%p, nfrags=%d", __func__, (priv->cur_tx % txsize), (priv->dirty_tx % txsize), entry, first, nfrags); + if (priv->extend_desc) stmmac_display_ring((void *)priv->dma_etx, txsize, 1); else stmmac_display_ring((void *)priv->dma_tx, txsize, 0); - pr_info(">>> frame to be transmitted: "); + pr_debug(">>> frame to be transmitted: "); print_pkt(skb->data, skb->len); } -#endif if (unlikely(stmmac_tx_avail(priv) <= (MAX_SKB_FRAGS + 1))) { - TX_DBG("%s: stop transmitted packets\n", __func__); + if (netif_msg_hw(priv)) + pr_debug("%s: stop transmitted packets\n", __func__); netif_stop_queue(dev); } @@ -1972,7 +1926,8 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv) priv->hw->ring->refill_desc3(priv, p); - RX_DBG(KERN_INFO "\trefill entry #%d\n", entry); + if (netif_msg_rx_status(priv)) + pr_debug("\trefill entry #%d\n", entry); } wmb(); priv->hw->desc->set_rx_owner(p); @@ -1995,15 +1950,13 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) unsigned int count = 0; int coe = priv->plat->rx_coe; -#ifdef STMMAC_RX_DEBUG - if (netif_msg_hw(priv)) { - pr_debug(">>> stmmac_rx: descriptor ring:\n"); + if (netif_msg_rx_status(priv)) { + pr_debug("%s: descriptor ring:\n", __func__); if (priv->extend_desc) stmmac_display_ring((void *)priv->dma_erx, rxsize, 1); else stmmac_display_ring((void *)priv->dma_rx, rxsize, 0); } -#endif while (count < limit) { int status; struct dma_desc *p; @@ -2057,15 +2010,14 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) */ if (unlikely(status != llc_snap)) frame_len -= ETH_FCS_LEN; -#ifdef STMMAC_RX_DEBUG - if (frame_len > ETH_FRAME_LEN) - pr_debug("\tRX frame size %d, COE status: %d\n", - frame_len, status); - if (netif_msg_hw(priv)) + if (netif_msg_rx_status(priv)) { pr_debug("\tdesc: %p [entry %d] buff=0x%x\n", p, entry, p->des2); -#endif + if (frame_len > ETH_FRAME_LEN) + pr_debug("\tframe size %d, COE: %d\n", + frame_len, status); + } skb = priv->rx_skbuff[entry]; if (unlikely(!skb)) { pr_err("%s: Inconsistent Rx descriptor chain\n", @@ -2082,12 +2034,12 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) dma_unmap_single(priv->device, priv->rx_skbuff_dma[entry], priv->dma_buf_sz, DMA_FROM_DEVICE); -#ifdef STMMAC_RX_DEBUG + if (netif_msg_pktdata(priv)) { - pr_info(" frame received (%dbytes)", frame_len); + pr_debug("frame received (%dbytes)", frame_len); print_pkt(skb->data, frame_len); } -#endif + skb->protocol = eth_type_trans(skb, priv->dev); if (unlikely(!coe)) -- cgit v1.2.3 From 8a59bd3e9b296b93b905b5509c4ff540ee0e00bf Mon Sep 17 00:00:00 2001 From: Yann Droneaud Date: Tue, 2 Jul 2013 18:39:36 +0200 Subject: sctp: use get_unused_fd_flags(0) instead of get_unused_fd() Macro get_unused_fd() is used to allocate a file descriptor with default flags. Those default flags (0) can be "unsafe": O_CLOEXEC must be used by default to not leak file descriptor across exec(). Instead of macro get_unused_fd(), functions anon_inode_getfd() or get_unused_fd_flags() should be used with flags given by userspace. If not possible, flags should be set to O_CLOEXEC to provide userspace with a default safe behavor. In a further patch, get_unused_fd() will be removed so that new code start using anon_inode_getfd() or get_unused_fd_flags() with correct flags. This patch replaces calls to get_unused_fd() with equivalent call to get_unused_fd_flags(0) to preserve current behavor for existing code. The hard coded flag value (0) should be reviewed on a per-subsystem basis, and, if possible, set to O_CLOEXEC. Signed-off-by: Yann Droneaud Acked-by: Vlad Yasevich Signed-off-by: David S. Miller --- net/sctp/socket.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sctp/socket.c b/net/sctp/socket.c index d5c6a2870473..c6670d2e3f8d 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -4312,7 +4312,7 @@ static int sctp_getsockopt_peeloff(struct sock *sk, int len, char __user *optval goto out; /* Map the socket to an unused fd that can be returned to the user. */ - retval = get_unused_fd(); + retval = get_unused_fd_flags(0); if (retval < 0) { sock_release(newsock); goto out; -- cgit v1.2.3 From b6dc01a43aaca24e6e6928e24d9b37ba599f1e3c Mon Sep 17 00:00:00 2001 From: James Chapman Date: Tue, 2 Jul 2013 20:28:58 +0100 Subject: l2tp: do data sequence number handling in a separate func This change moves some code handling data sequence numbers into a separate function to avoid too much indentation. This is to prepare for some changes to data sequence number handling in subsequent patches. Signed-off-by: James Chapman Signed-off-by: David S. Miller --- net/l2tp/l2tp_core.c | 54 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index 6984c3a353cd..5ca29659171d 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -542,6 +542,38 @@ static inline int l2tp_verify_udp_checksum(struct sock *sk, return __skb_checksum_complete(skb); } +/* If packet has sequence numbers, queue it if acceptable. Returns 0 if + * acceptable, else non-zero. + */ +static int l2tp_recv_data_seq(struct l2tp_session *session, struct sk_buff *skb) +{ + if (session->reorder_timeout != 0) { + /* Packet reordering enabled. Add skb to session's + * reorder queue, in order of ns. + */ + l2tp_recv_queue_skb(session, skb); + } else { + /* Packet reordering disabled. Discard out-of-sequence + * packets + */ + if (L2TP_SKB_CB(skb)->ns != session->nr) { + atomic_long_inc(&session->stats.rx_seq_discards); + l2tp_dbg(session, L2TP_MSG_SEQ, + "%s: oos pkt %u len %d discarded, waiting for %u, reorder_q_len=%d\n", + session->name, L2TP_SKB_CB(skb)->ns, + L2TP_SKB_CB(skb)->length, session->nr, + skb_queue_len(&session->reorder_q)); + goto discard; + } + skb_queue_tail(&session->reorder_q, skb); + } + + return 0; + +discard: + return 1; +} + /* Do receive processing of L2TP data frames. We handle both L2TPv2 * and L2TPv3 data frames here. * @@ -757,26 +789,8 @@ void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb, * enabled. Saved L2TP protocol info is stored in skb->sb[]. */ if (L2TP_SKB_CB(skb)->has_seq) { - if (session->reorder_timeout != 0) { - /* Packet reordering enabled. Add skb to session's - * reorder queue, in order of ns. - */ - l2tp_recv_queue_skb(session, skb); - } else { - /* Packet reordering disabled. Discard out-of-sequence - * packets - */ - if (L2TP_SKB_CB(skb)->ns != session->nr) { - atomic_long_inc(&session->stats.rx_seq_discards); - l2tp_dbg(session, L2TP_MSG_SEQ, - "%s: oos pkt %u len %d discarded, waiting for %u, reorder_q_len=%d\n", - session->name, L2TP_SKB_CB(skb)->ns, - L2TP_SKB_CB(skb)->length, session->nr, - skb_queue_len(&session->reorder_q)); - goto discard; - } - skb_queue_tail(&session->reorder_q, skb); - } + if (l2tp_recv_data_seq(session, skb)) + goto discard; } else { /* No sequence numbers. Add the skb to the tail of the * reorder queue. This ensures that it will be -- cgit v1.2.3 From 8a1631d588a39e826f4248e60310498d5266c6fa Mon Sep 17 00:00:00 2001 From: James Chapman Date: Tue, 2 Jul 2013 20:28:59 +0100 Subject: l2tp: make datapath sequence number support RFC-compliant The L2TP datapath is not currently RFC-compliant when sequence numbers are used in L2TP data packets. According to the L2TP RFC, any received sequence number NR greater than or equal to the next expected NR is acceptable, where the "greater than or equal to" test is determined by the NR wrap point. This differs for L2TPv2 and L2TPv3, so add state in the session context to hold the max NR value and the NR window size in order to do the acceptable sequence number value check. These might be configurable later, but for now we derive it from the tunnel L2TP version, which determines the sequence number field size. Signed-off-by: James Chapman Signed-off-by: David S. Miller --- net/l2tp/l2tp_core.c | 36 +++++++++++++++++++++++++++++++----- net/l2tp/l2tp_core.h | 2 ++ 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index 5ca29659171d..735cc06971ef 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -414,10 +414,7 @@ static void l2tp_recv_dequeue_skb(struct l2tp_session *session, struct sk_buff * if (L2TP_SKB_CB(skb)->has_seq) { /* Bump our Nr */ session->nr++; - if (tunnel->version == L2TP_HDR_VER_2) - session->nr &= 0xffff; - else - session->nr &= 0xffffff; + session->nr &= session->nr_max; l2tp_dbg(session, L2TP_MSG_SEQ, "%s: updated nr to %hu\n", session->name, session->nr); @@ -542,11 +539,34 @@ static inline int l2tp_verify_udp_checksum(struct sock *sk, return __skb_checksum_complete(skb); } +static int l2tp_seq_check_rx_window(struct l2tp_session *session, u32 nr) +{ + u32 nws; + + if (nr >= session->nr) + nws = nr - session->nr; + else + nws = (session->nr_max + 1) - (session->nr - nr); + + return nws < session->nr_window_size; +} + /* If packet has sequence numbers, queue it if acceptable. Returns 0 if * acceptable, else non-zero. */ static int l2tp_recv_data_seq(struct l2tp_session *session, struct sk_buff *skb) { + if (!l2tp_seq_check_rx_window(session, L2TP_SKB_CB(skb)->ns)) { + /* Packet sequence number is outside allowed window. + * Discard it. + */ + l2tp_dbg(session, L2TP_MSG_SEQ, + "%s: pkt %u len %d discarded, outside window, nr=%u\n", + session->name, L2TP_SKB_CB(skb)->ns, + L2TP_SKB_CB(skb)->length, session->nr); + goto discard; + } + if (session->reorder_timeout != 0) { /* Packet reordering enabled. Add skb to session's * reorder queue, in order of ns. @@ -556,7 +576,8 @@ static int l2tp_recv_data_seq(struct l2tp_session *session, struct sk_buff *skb) /* Packet reordering disabled. Discard out-of-sequence * packets */ - if (L2TP_SKB_CB(skb)->ns != session->nr) { + if ((L2TP_SKB_CB(skb)->ns != session->nr) && + (!session->reorder_skip)) { atomic_long_inc(&session->stats.rx_seq_discards); l2tp_dbg(session, L2TP_MSG_SEQ, "%s: oos pkt %u len %d discarded, waiting for %u, reorder_q_len=%d\n", @@ -1826,6 +1847,11 @@ struct l2tp_session *l2tp_session_create(int priv_size, struct l2tp_tunnel *tunn session->session_id = session_id; session->peer_session_id = peer_session_id; session->nr = 0; + if (tunnel->version == L2TP_HDR_VER_2) + session->nr_max = 0xffff; + else + session->nr_max = 0xffffff; + session->nr_window_size = session->nr_max / 2; sprintf(&session->name[0], "sess %u/%u", tunnel->tunnel_id, session->session_id); diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h index 485a490fd990..4b9a3b724423 100644 --- a/net/l2tp/l2tp_core.h +++ b/net/l2tp/l2tp_core.h @@ -102,6 +102,8 @@ struct l2tp_session { u32 nr; /* session NR state (receive) */ u32 ns; /* session NR state (send) */ struct sk_buff_head reorder_q; /* receive reorder queue */ + u32 nr_max; /* max NR. Depends on tunnel */ + u32 nr_window_size; /* NR window size */ struct hlist_node hlist; /* Hash list node */ atomic_t ref_count; -- cgit v1.2.3 From a0dbd822273ce7660bf35525d61d7a8ac5e679a3 Mon Sep 17 00:00:00 2001 From: James Chapman Date: Tue, 2 Jul 2013 20:29:00 +0100 Subject: l2tp: make datapath resilient to packet loss when sequence numbers enabled If L2TP data sequence numbers are enabled and reordering is not enabled, data reception stops if a packet is lost since the kernel waits for a sequence number that is never resent. (When reordering is enabled, data reception restarts when the reorder timeout expires.) If no reorder timeout is set, we should count the number of in-sequence packets after the out-of-sequence (OOS) condition is detected, and reset sequence number state after a number of such packets are received. For now, the number of in-sequence packets while in OOS state which cause the sequence number state to be reset is hard-coded to 5. This could be configurable later. Signed-off-by: James Chapman Signed-off-by: David S. Miller --- net/l2tp/l2tp_core.c | 36 +++++++++++++++++++++++++++++++----- net/l2tp/l2tp_core.h | 3 +++ 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index 735cc06971ef..feae495a0a30 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -572,12 +572,33 @@ static int l2tp_recv_data_seq(struct l2tp_session *session, struct sk_buff *skb) * reorder queue, in order of ns. */ l2tp_recv_queue_skb(session, skb); + goto out; + } + + /* Packet reordering disabled. Discard out-of-sequence packets, while + * tracking the number if in-sequence packets after the first OOS packet + * is seen. After nr_oos_count_max in-sequence packets, reset the + * sequence number to re-enable packet reception. + */ + if (L2TP_SKB_CB(skb)->ns == session->nr) { + skb_queue_tail(&session->reorder_q, skb); } else { - /* Packet reordering disabled. Discard out-of-sequence - * packets - */ - if ((L2TP_SKB_CB(skb)->ns != session->nr) && - (!session->reorder_skip)) { + u32 nr_oos = L2TP_SKB_CB(skb)->ns; + u32 nr_next = (session->nr_oos + 1) & session->nr_max; + + if (nr_oos == nr_next) + session->nr_oos_count++; + else + session->nr_oos_count = 0; + + session->nr_oos = nr_oos; + if (session->nr_oos_count > session->nr_oos_count_max) { + session->reorder_skip = 1; + l2tp_dbg(session, L2TP_MSG_SEQ, + "%s: %d oos packets received. Resetting sequence numbers\n", + session->name, session->nr_oos_count); + } + if (!session->reorder_skip) { atomic_long_inc(&session->stats.rx_seq_discards); l2tp_dbg(session, L2TP_MSG_SEQ, "%s: oos pkt %u len %d discarded, waiting for %u, reorder_q_len=%d\n", @@ -589,6 +610,7 @@ static int l2tp_recv_data_seq(struct l2tp_session *session, struct sk_buff *skb) skb_queue_tail(&session->reorder_q, skb); } +out: return 0; discard: @@ -1852,6 +1874,10 @@ struct l2tp_session *l2tp_session_create(int priv_size, struct l2tp_tunnel *tunn else session->nr_max = 0xffffff; session->nr_window_size = session->nr_max / 2; + session->nr_oos_count_max = 4; + + /* Use NR of first received packet */ + session->reorder_skip = 1; sprintf(&session->name[0], "sess %u/%u", tunnel->tunnel_id, session->session_id); diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h index 4b9a3b724423..66a559b104b6 100644 --- a/net/l2tp/l2tp_core.h +++ b/net/l2tp/l2tp_core.h @@ -104,6 +104,9 @@ struct l2tp_session { struct sk_buff_head reorder_q; /* receive reorder queue */ u32 nr_max; /* max NR. Depends on tunnel */ u32 nr_window_size; /* NR window size */ + u32 nr_oos; /* NR of last OOS packet */ + int nr_oos_count; /* For OOS recovery */ + int nr_oos_count_max; struct hlist_node hlist; /* Hash list node */ atomic_t ref_count; -- cgit v1.2.3 From 0baac4db56b1895743391a348ee9ce4ae5076f9f Mon Sep 17 00:00:00 2001 From: CoolCold Date: Wed, 26 Jun 2013 19:46:58 +0400 Subject: md: remove doubled description for sync_max, merging it within sync_min/sync_max After last md.txt edits for sync_min/max, sync_max description became doubled. Removing 1st copy, merging details into common sync_min/sync_max section. Signed-off-by: Roman Ovchinnikov Signed-off-by: NeilBrown --- Documentation/md.txt | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Documentation/md.txt b/Documentation/md.txt index e0ddd327632d..fbb2fcbf16b6 100644 --- a/Documentation/md.txt +++ b/Documentation/md.txt @@ -566,13 +566,6 @@ also have when it reaches the current sync_max (below) and possibly at other times. - sync_max - This is a number of sectors at which point a resync/recovery - process will pause. When a resync is active, the value can - only ever be increased, never decreased. The value of 'max' - effectively disables the limit. - - sync_speed This shows the current actual speed, in K/sec, of the current sync_action. It is averaged over the last 30 seconds. @@ -593,6 +586,12 @@ also have that number to reach sync_max. Then you can either increase "sync_max", or can write 'idle' to "sync_action". + The value of 'max' for "sync_max" effectively disables the limit. + When a resync is active, the value can only ever be increased, + never decreased. + The value of '0' is the minimum for "sync_min". + + Each active md device may also have attributes specific to the personality module that manages it. -- cgit v1.2.3 From 78eaa0d4cbcdb345992fa3dd22b3bcbb473cc064 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Tue, 2 Jul 2013 15:58:05 +1000 Subject: md/raid10: fix two bugs affecting RAID10 reshape. 1/ If a RAID10 is being reshaped to a fewer number of devices and is stopped while this is ongoing, then when the array is reassembled the 'mirrors' array will be allocated too small. This will lead to an access error or memory corruption. 2/ A sanity test for a reshaping RAID10 array is restarted is slightly incorrect. Due to the first bug, this is suitable for any -stable kernel since 3.5 where this code was introduced. Cc: stable@vger.kernel.org (v3.5+) Signed-off-by: NeilBrown --- drivers/md/raid10.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index aa8ba0760cac..3480bf7c20d4 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -3554,7 +3554,7 @@ static struct r10conf *setup_conf(struct mddev *mddev) /* FIXME calc properly */ conf->mirrors = kzalloc(sizeof(struct raid10_info)*(mddev->raid_disks + - max(0,mddev->delta_disks)), + max(0,-mddev->delta_disks)), GFP_KERNEL); if (!conf->mirrors) goto out; @@ -3713,7 +3713,7 @@ static int run(struct mddev *mddev) conf->geo.far_offset == 0) goto out_free_conf; if (conf->prev.far_copies != 1 && - conf->geo.far_offset == 0) + conf->prev.far_offset == 0) goto out_free_conf; } -- cgit v1.2.3 From 23a3647bc4f93bac3776c66dc2c7f7f68b3cd662 Mon Sep 17 00:00:00 2001 From: Pravin B Shelar Date: Tue, 2 Jul 2013 10:57:33 -0700 Subject: ip_tunnels: Use skb-len to PMTU check. In path mtu check, ip header total length works for gre device but not for gre-tap device. Use skb len which is consistent for all tunneling types. This is old bug in gre. This also fixes mtu calculation bug introduced by commit c54419321455631079c7d (GRE: Refactor GRE tunneling code). Reported-by: Timo Teras Signed-off-by: Pravin B Shelar Signed-off-by: David S. Miller --- net/ipv4/ip_tunnel.c | 99 +++++++++++++++++++++++++++++----------------------- 1 file changed, 55 insertions(+), 44 deletions(-) diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index 394cebc96d22..945734b2f209 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c @@ -472,6 +472,54 @@ drop: } EXPORT_SYMBOL_GPL(ip_tunnel_rcv); +static int tnl_update_pmtu(struct net_device *dev, struct sk_buff *skb, + struct rtable *rt, __be16 df) +{ + struct ip_tunnel *tunnel = netdev_priv(dev); + int pkt_size = skb->len - tunnel->hlen; + int mtu; + + if (df) + mtu = dst_mtu(&rt->dst) - dev->hard_header_len + - sizeof(struct iphdr) - tunnel->hlen; + else + mtu = skb_dst(skb) ? dst_mtu(skb_dst(skb)) : dev->mtu; + + if (skb_dst(skb)) + skb_dst(skb)->ops->update_pmtu(skb_dst(skb), NULL, skb, mtu); + + if (skb->protocol == htons(ETH_P_IP)) { + if (!skb_is_gso(skb) && + (df & htons(IP_DF)) && mtu < pkt_size) { + memset(IPCB(skb), 0, sizeof(*IPCB(skb))); + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu)); + return -E2BIG; + } + } +#if IS_ENABLED(CONFIG_IPV6) + else if (skb->protocol == htons(ETH_P_IPV6)) { + struct rt6_info *rt6 = (struct rt6_info *)skb_dst(skb); + + if (rt6 && mtu < dst_mtu(skb_dst(skb)) && + mtu >= IPV6_MIN_MTU) { + if ((tunnel->parms.iph.daddr && + !ipv4_is_multicast(tunnel->parms.iph.daddr)) || + rt6->rt6i_dst.plen == 128) { + rt6->rt6i_flags |= RTF_MODIFIED; + dst_metric_set(skb_dst(skb), RTAX_MTU, mtu); + } + } + + if (!skb_is_gso(skb) && mtu >= IPV6_MIN_MTU && + mtu < pkt_size) { + icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); + return -E2BIG; + } + } +#endif + return 0; +} + void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, const struct iphdr *tnl_params, const u8 protocol) { @@ -483,7 +531,6 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, struct rtable *rt; /* Route to the other host */ unsigned int max_headroom; /* The extra header space needed */ __be32 dst; - int mtu; int err; inner_iph = (const struct iphdr *)skb_inner_network_header(skb); @@ -560,51 +607,11 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, dev->stats.collisions++; goto tx_error; } - df = tnl_params->frag_off; - if (df) - mtu = dst_mtu(&rt->dst) - dev->hard_header_len - - sizeof(struct iphdr); - else - mtu = skb_dst(skb) ? dst_mtu(skb_dst(skb)) : dev->mtu; - - if (skb_dst(skb)) - skb_dst(skb)->ops->update_pmtu(skb_dst(skb), NULL, skb, mtu); - - if (skb->protocol == htons(ETH_P_IP)) { - df |= (inner_iph->frag_off&htons(IP_DF)); - - if (!skb_is_gso(skb) && - (inner_iph->frag_off&htons(IP_DF)) && - mtu < ntohs(inner_iph->tot_len)) { - memset(IPCB(skb), 0, sizeof(*IPCB(skb))); - icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu)); - ip_rt_put(rt); - goto tx_error; - } - } -#if IS_ENABLED(CONFIG_IPV6) - else if (skb->protocol == htons(ETH_P_IPV6)) { - struct rt6_info *rt6 = (struct rt6_info *)skb_dst(skb); - - if (rt6 && mtu < dst_mtu(skb_dst(skb)) && - mtu >= IPV6_MIN_MTU) { - if ((tunnel->parms.iph.daddr && - !ipv4_is_multicast(tunnel->parms.iph.daddr)) || - rt6->rt6i_dst.plen == 128) { - rt6->rt6i_flags |= RTF_MODIFIED; - dst_metric_set(skb_dst(skb), RTAX_MTU, mtu); - } - } - - if (!skb_is_gso(skb) && mtu >= IPV6_MIN_MTU && - mtu < skb->len) { - icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu); - ip_rt_put(rt); - goto tx_error; - } + if (tnl_update_pmtu(dev, skb, rt, tnl_params->frag_off)) { + ip_rt_put(rt); + goto tx_error; } -#endif if (tunnel->net != dev_net(dev)) skb_scrub_packet(skb); @@ -631,6 +638,10 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, ttl = ip4_dst_hoplimit(&rt->dst); } + df = tnl_params->frag_off; + if (skb->protocol == htons(ETH_P_IP)) + df |= (inner_iph->frag_off&htons(IP_DF)); + max_headroom = LL_RESERVED_SPACE(rt->dst.dev) + sizeof(struct iphdr) + rt->dst.header_len; if (max_headroom > dev->needed_headroom) { -- cgit v1.2.3 From 2a6c24afab70dbcfee49f4c76e1511eec1a3298b Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Tue, 2 Jul 2013 14:48:23 -0400 Subject: tracing: Fix race between deleting buffer and setting events While analyzing the code, I discovered that there's a potential race between deleting a trace instance and setting events. There are a few races that can occur if events are being traced as the buffer is being deleted. Mostly the problem comes with freeing the descriptor used by the trace event callback. To prevent problems like this, the events are disabled before the buffer is deleted. The problem with the current solution is that the event_mutex is let go between disabling the events and freeing the files, which means that the events could be enabled again while the freeing takes place. Cc: stable@vger.kernel.org # 3.10 Signed-off-by: Steven Rostedt --- kernel/trace/trace_events.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 920e08fb53b3..7d854290bf81 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -441,14 +441,14 @@ static int tracing_release_generic_file(struct inode *inode, struct file *filp) /* * __ftrace_set_clr_event(NULL, NULL, NULL, set) will set/unset all events. */ -static int __ftrace_set_clr_event(struct trace_array *tr, const char *match, - const char *sub, const char *event, int set) +static int +__ftrace_set_clr_event_nolock(struct trace_array *tr, const char *match, + const char *sub, const char *event, int set) { struct ftrace_event_file *file; struct ftrace_event_call *call; int ret = -EINVAL; - mutex_lock(&event_mutex); list_for_each_entry(file, &tr->events, list) { call = file->event_call; @@ -474,6 +474,17 @@ static int __ftrace_set_clr_event(struct trace_array *tr, const char *match, ret = 0; } + + return ret; +} + +static int __ftrace_set_clr_event(struct trace_array *tr, const char *match, + const char *sub, const char *event, int set) +{ + int ret; + + mutex_lock(&event_mutex); + ret = __ftrace_set_clr_event_nolock(tr, match, sub, event, set); mutex_unlock(&event_mutex); return ret; @@ -2408,11 +2419,11 @@ early_event_add_tracer(struct dentry *parent, struct trace_array *tr) int event_trace_del_tracer(struct trace_array *tr) { - /* Disable any running events */ - __ftrace_set_clr_event(tr, NULL, NULL, NULL, 0); - mutex_lock(&event_mutex); + /* Disable any running events */ + __ftrace_set_clr_event_nolock(tr, NULL, NULL, NULL, 0); + down_write(&trace_event_sem); __trace_remove_event_dirs(tr); debugfs_remove_recursive(tr->event_dir); -- cgit v1.2.3 From fa44063f9ef163c3a4c8d8c0465bb8a056b42035 Mon Sep 17 00:00:00 2001 From: "zhangwei(Jovi)" Date: Thu, 13 Jun 2013 14:21:51 +0800 Subject: uprobes: Fix return value in error handling path When wrong argument is passed into uprobe_events it does not return an error: [root@jovi tracing]# echo 'p:myprobe /bin/bash' > uprobe_events [root@jovi tracing]# The proper response is: [root@jovi tracing]# echo 'p:myprobe /bin/bash' > uprobe_events -bash: echo: write error: Invalid argument Link: http://lkml.kernel.org/r/51B964FF.5000106@huawei.com Cc: Frederic Weisbecker Cc: Cc: stable@vger.kernel.org # 3.5+ Signed-off-by: zhangwei(Jovi) Signed-off-by: Steven Rostedt --- kernel/trace/trace_uprobe.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c index 32494fb0ee64..d5d0cd368a56 100644 --- a/kernel/trace/trace_uprobe.c +++ b/kernel/trace/trace_uprobe.c @@ -283,8 +283,10 @@ static int create_trace_uprobe(int argc, char **argv) return -EINVAL; } arg = strchr(argv[1], ':'); - if (!arg) + if (!arg) { + ret = -EINVAL; goto fail_address_parse; + } *arg++ = '\0'; filename = argv[1]; -- cgit v1.2.3 From 11034ae9c20f4057a6127fc965906417978e69b2 Mon Sep 17 00:00:00 2001 From: "zhangwei(Jovi)" Date: Wed, 10 Apr 2013 11:26:23 +0800 Subject: tracing: Fix irqs-off tag display in syscall tracing All syscall tracing irqs-off tags are wrong, the syscall enter entry doesn't disable irqs. [root@jovi tracing]#echo "syscalls:sys_enter_open" > set_event [root@jovi tracing]# cat trace # tracer: nop # # entries-in-buffer/entries-written: 13/13 #P:2 # # _-----=> irqs-off # / _----=> need-resched # | / _---=> hardirq/softirq # || / _--=> preempt-depth # ||| / delay # TASK-PID CPU# |||| TIMESTAMP FUNCTION # | | | |||| | | irqbalance-513 [000] d... 56115.496766: sys_open(filename: 804e1a6, flags: 0, mode: 1b6) irqbalance-513 [000] d... 56115.497008: sys_open(filename: 804e1bb, flags: 0, mode: 1b6) sendmail-771 [000] d... 56115.827982: sys_open(filename: b770e6d1, flags: 0, mode: 1b6) The reason is syscall tracing doesn't record irq_flags into buffer. The proper display is: [root@jovi tracing]#echo "syscalls:sys_enter_open" > set_event [root@jovi tracing]# cat trace # tracer: nop # # entries-in-buffer/entries-written: 14/14 #P:2 # # _-----=> irqs-off # / _----=> need-resched # | / _---=> hardirq/softirq # || / _--=> preempt-depth # ||| / delay # TASK-PID CPU# |||| TIMESTAMP FUNCTION # | | | |||| | | irqbalance-514 [001] .... 46.213921: sys_open(filename: 804e1a6, flags: 0, mode: 1b6) irqbalance-514 [001] .... 46.214160: sys_open(filename: 804e1bb, flags: 0, mode: 1b6) <...>-920 [001] .... 47.307260: sys_open(filename: 4e82a0c5, flags: 80000, mode: 0) Link: http://lkml.kernel.org/r/1365564393-10972-3-git-send-email-jovi.zhangwei@huawei.com Cc: stable@vger.kernel.org # 2.6.35 Signed-off-by: zhangwei(Jovi) Signed-off-by: Steven Rostedt --- kernel/trace/trace_syscalls.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/kernel/trace/trace_syscalls.c b/kernel/trace/trace_syscalls.c index 8f2ac73c7a5f..322e16461072 100644 --- a/kernel/trace/trace_syscalls.c +++ b/kernel/trace/trace_syscalls.c @@ -306,6 +306,8 @@ static void ftrace_syscall_enter(void *data, struct pt_regs *regs, long id) struct syscall_metadata *sys_data; struct ring_buffer_event *event; struct ring_buffer *buffer; + unsigned long irq_flags; + int pc; int syscall_nr; int size; @@ -321,9 +323,12 @@ static void ftrace_syscall_enter(void *data, struct pt_regs *regs, long id) size = sizeof(*entry) + sizeof(unsigned long) * sys_data->nb_args; + local_save_flags(irq_flags); + pc = preempt_count(); + buffer = tr->trace_buffer.buffer; event = trace_buffer_lock_reserve(buffer, - sys_data->enter_event->event.type, size, 0, 0); + sys_data->enter_event->event.type, size, irq_flags, pc); if (!event) return; @@ -333,7 +338,8 @@ static void ftrace_syscall_enter(void *data, struct pt_regs *regs, long id) if (!filter_current_check_discard(buffer, sys_data->enter_event, entry, event)) - trace_current_buffer_unlock_commit(buffer, event, 0, 0); + trace_current_buffer_unlock_commit(buffer, event, + irq_flags, pc); } static void ftrace_syscall_exit(void *data, struct pt_regs *regs, long ret) @@ -343,6 +349,8 @@ static void ftrace_syscall_exit(void *data, struct pt_regs *regs, long ret) struct syscall_metadata *sys_data; struct ring_buffer_event *event; struct ring_buffer *buffer; + unsigned long irq_flags; + int pc; int syscall_nr; syscall_nr = trace_get_syscall_nr(current, regs); @@ -355,9 +363,13 @@ static void ftrace_syscall_exit(void *data, struct pt_regs *regs, long ret) if (!sys_data) return; + local_save_flags(irq_flags); + pc = preempt_count(); + buffer = tr->trace_buffer.buffer; event = trace_buffer_lock_reserve(buffer, - sys_data->exit_event->event.type, sizeof(*entry), 0, 0); + sys_data->exit_event->event.type, sizeof(*entry), + irq_flags, pc); if (!event) return; @@ -367,7 +379,8 @@ static void ftrace_syscall_exit(void *data, struct pt_regs *regs, long ret) if (!filter_current_check_discard(buffer, sys_data->exit_event, entry, event)) - trace_current_buffer_unlock_commit(buffer, event, 0, 0); + trace_current_buffer_unlock_commit(buffer, event, + irq_flags, pc); } static int reg_event_syscall_enter(struct ftrace_event_file *file, -- cgit v1.2.3 From 5280bcef91e706770cc1706eb97353e3513322b9 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Tue, 2 Jul 2013 19:59:57 -0400 Subject: tracing: Make tracer_tracing_{off,on,is_on}() static I have patches that will use tracer_tracing_on/off/is_on() in other files, but as they are not ready to be merged yet, and Fengguang Wu's sparse scripts pointed out that these functions were not declared anywhere, I'll make them static for now. When these functions are required to be used elsewhere, I'll remove the static then. Reported-by: kbuild test robot Signed-off-by: Steven Rostedt --- kernel/trace/trace.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 6d9bd9b43e43..48aceb8a0328 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -387,7 +387,7 @@ unsigned long trace_flags = TRACE_ITER_PRINT_PARENT | TRACE_ITER_PRINTK | TRACE_ITER_GRAPH_TIME | TRACE_ITER_RECORD_CMD | TRACE_ITER_OVERWRITE | TRACE_ITER_IRQ_INFO | TRACE_ITER_MARKERS | TRACE_ITER_FUNCTION; -void tracer_tracing_on(struct trace_array *tr) +static void tracer_tracing_on(struct trace_array *tr) { if (tr->trace_buffer.buffer) ring_buffer_record_on(tr->trace_buffer.buffer); @@ -606,7 +606,7 @@ void tracing_snapshot_alloc(void) EXPORT_SYMBOL_GPL(tracing_snapshot_alloc); #endif /* CONFIG_TRACER_SNAPSHOT */ -void tracer_tracing_off(struct trace_array *tr) +static void tracer_tracing_off(struct trace_array *tr) { if (tr->trace_buffer.buffer) ring_buffer_record_off(tr->trace_buffer.buffer); @@ -649,7 +649,7 @@ void disable_trace_on_warning(void) * * Shows real state of the ring buffer if it is enabled or not. */ -int tracer_tracing_is_on(struct trace_array *tr) +static int tracer_tracing_is_on(struct trace_array *tr) { if (tr->trace_buffer.buffer) return ring_buffer_record_is_on(tr->trace_buffer.buffer); -- cgit v1.2.3 From 4480361c3c592fcbce3ef74e030719f0715e3a7e Mon Sep 17 00:00:00 2001 From: "zhangwei(Jovi)" Date: Wed, 10 Apr 2013 11:26:28 +0800 Subject: tracing: Remove TRACE_EVENT_TYPE enum definition TRACE_EVENT_TYPE enum is not used at present, remove it. Link: http://lkml.kernel.org/r/1365564393-10972-8-git-send-email-jovi.zhangwei@huawei.com Signed-off-by: zhangwei(Jovi) Signed-off-by: Steven Rostedt --- kernel/trace/trace.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index c7fbf93f1b7c..1cbba04976b4 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -907,12 +907,6 @@ static inline void trace_branch_disable(void) /* set ring buffers to default size if not already done so */ int tracing_update_buffers(void); -/* trace event type bit fields, not numeric */ -enum { - TRACE_EVENT_TYPE_PRINTF = 1, - TRACE_EVENT_TYPE_RAW = 2, -}; - struct ftrace_event_field { struct list_head link; const char *name; -- cgit v1.2.3 From 8de1eb02778b64f8b292db531cf39a429f84315f Mon Sep 17 00:00:00 2001 From: "zhangwei(Jovi)" Date: Wed, 10 Apr 2013 11:26:30 +0800 Subject: tracing: Remove ftrace() function The only caller of function ftrace(...) was removed a long time ago, so remove the function body as well. Link: http://lkml.kernel.org/r/1365564393-10972-10-git-send-email-jovi.zhangwei@huawei.com Signed-off-by: zhangwei(Jovi) Signed-off-by: Steven Rostedt --- kernel/trace/trace.c | 9 --------- kernel/trace/trace.h | 5 ----- 2 files changed, 14 deletions(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 48aceb8a0328..f6fed9e51c64 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -1637,15 +1637,6 @@ trace_function(struct trace_array *tr, __buffer_unlock_commit(buffer, event); } -void -ftrace(struct trace_array *tr, struct trace_array_cpu *data, - unsigned long ip, unsigned long parent_ip, unsigned long flags, - int pc) -{ - if (likely(!atomic_read(&data->disabled))) - trace_function(tr, ip, parent_ip, flags, pc); -} - #ifdef CONFIG_STACKTRACE #define FTRACE_STACK_MAX_ENTRIES (PAGE_SIZE / sizeof(unsigned long)) diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 1cbba04976b4..a4ed382dea2f 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -559,11 +559,6 @@ void tracing_iter_reset(struct trace_iterator *iter, int cpu); void poll_wait_pipe(struct trace_iterator *iter); -void ftrace(struct trace_array *tr, - struct trace_array_cpu *data, - unsigned long ip, - unsigned long parent_ip, - unsigned long flags, int pc); void tracing_sched_switch_trace(struct trace_array *tr, struct task_struct *prev, struct task_struct *next, -- cgit v1.2.3 From dcc302232c1f9b3ca16f6b8ee190eb0b1a8a0da3 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Tue, 2 Jul 2013 20:30:52 -0400 Subject: tracing: Make tracing_open_generic_{tr,tc}() static I have patches that will use tracing_open_generic_tr/tc() in other files, but as they are not ready to be merged yet, and Fengguang Wu's sparse scripts pointed out that these functions were not declared anywhere, I'll make them static for now. When these functions are required to be used elsewhere, I'll remove the static then. Reported-by: kbuild test robot Signed-off-by: Steven Rostedt --- kernel/trace/trace.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index f6fed9e51c64..dc473b51415f 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -2960,7 +2960,7 @@ int tracing_open_generic(struct inode *inode, struct file *filp) * Open and update trace_array ref count. * Must have the current trace_array passed to it. */ -int tracing_open_generic_tr(struct inode *inode, struct file *filp) +static int tracing_open_generic_tr(struct inode *inode, struct file *filp) { struct trace_array *tr = inode->i_private; @@ -2976,7 +2976,7 @@ int tracing_open_generic_tr(struct inode *inode, struct file *filp) } -int tracing_open_generic_tc(struct inode *inode, struct file *filp) +static int tracing_open_generic_tc(struct inode *inode, struct file *filp) { struct trace_cpu *tc = inode->i_private; struct trace_array *tr = tc->tr; -- cgit v1.2.3 From 8d8022e8aba85192e937f1f0f7450e256d66ae5c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 3 Jul 2013 10:06:28 +0930 Subject: module: do percpu allocation after uniqueness check. No, really! v3.8-rc1-5-g1fb9341 was supposed to stop parallel kvm loads exhausting percpu memory on large machines: Now we have a new state MODULE_STATE_UNFORMED, we can insert the module into the list (and thus guarantee its uniqueness) before we allocate the per-cpu region. In my defence, it didn't actually say the patch did this. Just that we "can". This patch actually *does* it. Signed-off-by: Rusty Russell Tested-by: Jim Hull Cc: stable@kernel.org # 3.8 --- kernel/module.c | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/kernel/module.c b/kernel/module.c index 5184877ce98a..d1a161be7b04 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -2940,7 +2940,6 @@ static struct module *layout_and_allocate(struct load_info *info, int flags) { /* Module within temporary copy. */ struct module *mod; - Elf_Shdr *pcpusec; int err; mod = setup_load_info(info, flags); @@ -2955,17 +2954,10 @@ static struct module *layout_and_allocate(struct load_info *info, int flags) err = module_frob_arch_sections(info->hdr, info->sechdrs, info->secstrings, mod); if (err < 0) - goto out; + return ERR_PTR(err); - pcpusec = &info->sechdrs[info->index.pcpu]; - if (pcpusec->sh_size) { - /* We have a special allocation for this section. */ - err = percpu_modalloc(mod, - pcpusec->sh_size, pcpusec->sh_addralign); - if (err) - goto out; - pcpusec->sh_flags &= ~(unsigned long)SHF_ALLOC; - } + /* We will do a special allocation for per-cpu sections later. */ + info->sechdrs[info->index.pcpu].sh_flags &= ~(unsigned long)SHF_ALLOC; /* Determine total sizes, and put offsets in sh_entsize. For now this is done generically; there doesn't appear to be any @@ -2976,17 +2968,22 @@ static struct module *layout_and_allocate(struct load_info *info, int flags) /* Allocate and move to the final place */ err = move_module(mod, info); if (err) - goto free_percpu; + return ERR_PTR(err); /* Module has been copied to its final place now: return it. */ mod = (void *)info->sechdrs[info->index.mod].sh_addr; kmemleak_load_module(mod, info); return mod; +} -free_percpu: - percpu_modfree(mod); -out: - return ERR_PTR(err); +static int alloc_module_percpu(struct module *mod, struct load_info *info) +{ + Elf_Shdr *pcpusec = &info->sechdrs[info->index.pcpu]; + if (!pcpusec->sh_size) + return 0; + + /* We have a special allocation for this section. */ + return percpu_modalloc(mod, pcpusec->sh_size, pcpusec->sh_addralign); } /* mod is no longer valid after this! */ @@ -3262,6 +3259,11 @@ static int load_module(struct load_info *info, const char __user *uargs, } #endif + /* To avoid stressing percpu allocator, do this once we're unique. */ + err = alloc_module_percpu(mod, info); + if (err) + goto unlink_mod; + /* Now module is in final location, initialize linked lists, etc. */ err = module_unload_init(mod); if (err) -- cgit v1.2.3 From 9eb76d7797b892a1dad4f2efb6f786681306dd13 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 3 Jul 2013 10:06:29 +0930 Subject: module: cleanup call chain. Fold alloc_module_percpu into percpu_modalloc(). Signed-off-by: Rusty Russell --- kernel/module.c | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/kernel/module.c b/kernel/module.c index d1a161be7b04..c18107942ac2 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -482,23 +482,28 @@ static inline void __percpu *mod_percpu(struct module *mod) return mod->percpu; } -static int percpu_modalloc(struct module *mod, - unsigned long size, unsigned long align) +static int percpu_modalloc(struct module *mod, struct load_info *info) { + Elf_Shdr *pcpusec = &info->sechdrs[info->index.pcpu]; + unsigned long align = pcpusec->sh_addralign; + + if (!pcpusec->sh_size) + return 0; + if (align > PAGE_SIZE) { printk(KERN_WARNING "%s: per-cpu alignment %li > %li\n", mod->name, align, PAGE_SIZE); align = PAGE_SIZE; } - mod->percpu = __alloc_reserved_percpu(size, align); + mod->percpu = __alloc_reserved_percpu(pcpusec->sh_size, align); if (!mod->percpu) { printk(KERN_WARNING "%s: Could not allocate %lu bytes percpu data\n", - mod->name, size); + mod->name, (unsigned long)pcpusec->sh_size); return -ENOMEM; } - mod->percpu_size = size; + mod->percpu_size = pcpusec->sh_size; return 0; } @@ -563,10 +568,12 @@ static inline void __percpu *mod_percpu(struct module *mod) { return NULL; } -static inline int percpu_modalloc(struct module *mod, - unsigned long size, unsigned long align) +static int percpu_modalloc(struct module *mod, struct load_info *info) { - return -ENOMEM; + /* UP modules shouldn't have this section: ENOMEM isn't quite right */ + if (info->sechdrs[info->index.pcpu].sh_size != 0) + return -ENOMEM; + return 0; } static inline void percpu_modfree(struct module *mod) { @@ -2976,16 +2983,6 @@ static struct module *layout_and_allocate(struct load_info *info, int flags) return mod; } -static int alloc_module_percpu(struct module *mod, struct load_info *info) -{ - Elf_Shdr *pcpusec = &info->sechdrs[info->index.pcpu]; - if (!pcpusec->sh_size) - return 0; - - /* We have a special allocation for this section. */ - return percpu_modalloc(mod, pcpusec->sh_size, pcpusec->sh_addralign); -} - /* mod is no longer valid after this! */ static void module_deallocate(struct module *mod, struct load_info *info) { @@ -3260,7 +3257,7 @@ static int load_module(struct load_info *info, const char __user *uargs, #endif /* To avoid stressing percpu allocator, do this once we're unique. */ - err = alloc_module_percpu(mod, info); + err = percpu_modalloc(mod, info); if (err) goto unlink_mod; -- cgit v1.2.3 From 0fb82ec0268b5e46b0fefc677c9fa62707c61c67 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 2 Jul 2013 20:32:39 -0700 Subject: Input: cyttsp4 - silence shift wrap warning "*max" is a size_t (long) type but "1" is an int so static checkers complain that the shift could wrap. Signed-off-by: Dan Carpenter Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/cyttsp4_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/cyttsp4_core.c b/drivers/input/touchscreen/cyttsp4_core.c index 963da052c15e..7aa4a349a07e 100644 --- a/drivers/input/touchscreen/cyttsp4_core.c +++ b/drivers/input/touchscreen/cyttsp4_core.c @@ -153,7 +153,7 @@ static int cyttsp4_hw_reset(struct cyttsp4 *cd) */ static int cyttsp4_bits_2_bytes(unsigned int nbits, size_t *max) { - *max = 1 << nbits; + *max = 1UL << nbits; return (nbits + 7) / 8; } -- cgit v1.2.3 From 7c4c018aadadf1db2c27d6b9f0280700a2619861 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 2 Jul 2013 20:32:53 -0700 Subject: Input: cyttsp4 - silence NULL dereference warning If "cd" were NULL then we would dereference it when we print the error message. Fortunately enough, it can't ever be NULL so we can remove those lines. Signed-off-by: Dan Carpenter Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/cyttsp4_core.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/input/touchscreen/cyttsp4_core.c b/drivers/input/touchscreen/cyttsp4_core.c index 7aa4a349a07e..851e3ff80437 100644 --- a/drivers/input/touchscreen/cyttsp4_core.c +++ b/drivers/input/touchscreen/cyttsp4_core.c @@ -1450,11 +1450,6 @@ static void cyttsp4_watchdog_work(struct work_struct *work) u8 *mode; int retval; - if (cd == NULL) { - dev_err(cd->dev, "%s: NULL context pointer\n", __func__); - return; - } - mutex_lock(&cd->system_lock); retval = cyttsp4_load_status_regs(cd); if (retval < 0) { -- cgit v1.2.3 From 394fc05b7967a043e26b4343011a319390801c8b Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 2 Jul 2013 20:32:58 -0700 Subject: Input: cyttsp4 - leak on error path in probe() We leak "cd" if the cd->xfer_buf allocation fails. It was weird to "goto error_gpio_irq" so I changed the label name. (Label names should reflect the label location not the goto location otherwise you get an "all roads lead to Rome problem"). Signed-off-by: Dan Carpenter Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/cyttsp4_core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/input/touchscreen/cyttsp4_core.c b/drivers/input/touchscreen/cyttsp4_core.c index 851e3ff80437..a7987e170071 100644 --- a/drivers/input/touchscreen/cyttsp4_core.c +++ b/drivers/input/touchscreen/cyttsp4_core.c @@ -2025,7 +2025,7 @@ struct cyttsp4 *cyttsp4_probe(const struct cyttsp4_bus_ops *ops, if (!cd->xfer_buf) { dev_err(dev, "%s: Error, kzalloc\n", __func__); rc = -ENOMEM; - goto error_alloc_data; + goto error_free_cd; } /* Initialize device info */ @@ -2049,7 +2049,7 @@ struct cyttsp4 *cyttsp4_probe(const struct cyttsp4_bus_ops *ops, cd->irq = gpio_to_irq(cd->cpdata->irq_gpio); if (cd->irq < 0) { rc = -EINVAL; - goto error_gpio_irq; + goto error_free_cd; } dev_set_drvdata(dev, cd); @@ -2117,7 +2117,7 @@ error_request_irq: if (cd->cpdata->init) cd->cpdata->init(cd->cpdata, 0, dev); dev_set_drvdata(dev, NULL); -error_gpio_irq: +error_free_cd: kfree(cd); error_alloc_data: error_no_pdata: -- cgit v1.2.3 From 9d9a04ee758b4c1fcc7586d065cdde7a7607e156 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 1 Jul 2013 11:46:27 -0700 Subject: HID: apple: Add support for the 2013 Macbook Air This patch adds keyboard support for MacbookAir6,2 as WELLSPRING8 (0x0291, 0x0292, 0x0293). The touchpad is handled in a separate bcm5974 patch, as usual. Cc: stable@vger.kernel.org Reported-and-tested-by: Brad Ford Signed-off-by: Henrik Rydberg Signed-off-by: Jiri Kosina Signed-off-by: Dmitry Torokhov --- drivers/hid/hid-apple.c | 6 ++++++ drivers/hid/hid-core.c | 6 ++++++ drivers/hid/hid-ids.h | 3 +++ 3 files changed, 15 insertions(+) diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index feae88b53fcd..c7710b5c69af 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -524,6 +524,12 @@ static const struct hid_device_id apple_devices[] = { .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS), .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI), + .driver_data = APPLE_HAS_FN }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_ISO), + .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_JIS), + .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO), diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 264f55099940..402f48689943 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1547,6 +1547,9 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_ISO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_JIS) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS) }, @@ -2179,6 +2182,9 @@ static const struct hid_device_id hid_mouse_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_ISO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_JIS) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) }, { } diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 38535c9243d5..216888538c50 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -135,6 +135,9 @@ #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS 0x023b #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI 0x0255 #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO 0x0256 +#define USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI 0x0291 +#define USB_DEVICE_ID_APPLE_WELLSPRING8_ISO 0x0292 +#define USB_DEVICE_ID_APPLE_WELLSPRING8_JIS 0x0293 #define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a #define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b #define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240 -- cgit v1.2.3 From 148c1c8ad3c4170186ebe6ea5900adde27d2a0e7 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 1 Jul 2013 11:47:51 -0700 Subject: Input: bcm5974 - add support for the 2013 MacBook Air The June 2013 Macbook Air (13'') has a new trackpad protocol; four new values are inserted in the header, and the mode switch is no longer needed. This patch adds support for the new devices. Cc: stable@vger.kernel.org Reported-and-tested-by: Brad Ford Signed-off-by: Henrik Rydberg Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/bcm5974.c | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/drivers/input/mouse/bcm5974.c b/drivers/input/mouse/bcm5974.c index 2baff1b79a55..4ef4d5e198ae 100644 --- a/drivers/input/mouse/bcm5974.c +++ b/drivers/input/mouse/bcm5974.c @@ -88,6 +88,10 @@ #define USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI 0x0259 #define USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO 0x025a #define USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS 0x025b +/* MacbookAir6,2 (unibody, June 2013) */ +#define USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI 0x0291 +#define USB_DEVICE_ID_APPLE_WELLSPRING8_ISO 0x0292 +#define USB_DEVICE_ID_APPLE_WELLSPRING8_JIS 0x0293 #define BCM5974_DEVICE(prod) { \ .match_flags = (USB_DEVICE_ID_MATCH_DEVICE | \ @@ -145,6 +149,10 @@ static const struct usb_device_id bcm5974_table[] = { BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI), BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO), BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS), + /* MacbookAir6,2 */ + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI), + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING8_ISO), + BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING8_JIS), /* Terminating entry */ {} }; @@ -172,15 +180,18 @@ struct bt_data { /* trackpad header types */ enum tp_type { TYPE1, /* plain trackpad */ - TYPE2 /* button integrated in trackpad */ + TYPE2, /* button integrated in trackpad */ + TYPE3 /* additional header fields since June 2013 */ }; /* trackpad finger data offsets, le16-aligned */ #define FINGER_TYPE1 (13 * sizeof(__le16)) #define FINGER_TYPE2 (15 * sizeof(__le16)) +#define FINGER_TYPE3 (19 * sizeof(__le16)) /* trackpad button data offsets */ #define BUTTON_TYPE2 15 +#define BUTTON_TYPE3 23 /* list of device capability bits */ #define HAS_INTEGRATED_BUTTON 1 @@ -400,6 +411,19 @@ static const struct bcm5974_config bcm5974_config_table[] = { { SN_COORD, -150, 6730 }, { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION } }, + { + USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI, + USB_DEVICE_ID_APPLE_WELLSPRING8_ISO, + USB_DEVICE_ID_APPLE_WELLSPRING8_JIS, + HAS_INTEGRATED_BUTTON, + 0, sizeof(struct bt_data), + 0x83, TYPE3, FINGER_TYPE3, FINGER_TYPE3 + SIZEOF_ALL_FINGERS, + { SN_PRESSURE, 0, 300 }, + { SN_WIDTH, 0, 2048 }, + { SN_COORD, -4620, 5140 }, + { SN_COORD, -150, 6600 }, + { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION } + }, {} }; @@ -557,6 +581,9 @@ static int report_tp_state(struct bcm5974 *dev, int size) input_report_key(input, BTN_LEFT, ibt); } + if (c->tp_type == TYPE3) + input_report_key(input, BTN_LEFT, dev->tp_data[BUTTON_TYPE3]); + input_sync(input); return 0; @@ -572,9 +599,14 @@ static int report_tp_state(struct bcm5974 *dev, int size) static int bcm5974_wellspring_mode(struct bcm5974 *dev, bool on) { - char *data = kmalloc(8, GFP_KERNEL); int retval = 0, size; + char *data; + + /* Type 3 does not require a mode switch */ + if (dev->cfg.tp_type == TYPE3) + return 0; + data = kmalloc(8, GFP_KERNEL); if (!data) { dev_err(&dev->intf->dev, "out of memory\n"); retval = -ENOMEM; -- cgit v1.2.3 From 9ff9f6dab781371ccb658f7a8567223da3267da3 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 2 Jul 2013 23:08:18 -0700 Subject: Input: tps6507x-ts - select INPUT_POLLDEV Since the driver was converted to polled device infrastructure we need to make sure it is enabled. Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 2d70089c1183..3b9758b5f4d7 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -909,6 +909,7 @@ config TOUCHSCREEN_STMPE config TOUCHSCREEN_TPS6507X tristate "TPS6507x based touchscreens" depends on I2C + select INPUT_POLLDEV help Say Y here if you have a TPS6507x based touchscreen controller. -- cgit v1.2.3 From 419076f59fc5916bd200b45225c83437b37ab189 Mon Sep 17 00:00:00 2001 From: Eliezer Tamir Date: Wed, 3 Jul 2013 06:41:24 +0300 Subject: net: lls fix build with allnoconfig correct placeholder declarations to prevent build breakage when !CONFIG_NET_LL_RX_POLL Signed-off-by: Eliezer Tamir Signed-off-by: David S. Miller --- include/net/ll_poll.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/net/ll_poll.h b/include/net/ll_poll.h index b76f004f18a9..0d620ba19bc5 100644 --- a/include/net/ll_poll.h +++ b/include/net/ll_poll.h @@ -163,12 +163,12 @@ static inline unsigned long ll_get_flag(void) return 0; } -static inline u64 sk_ll_end_time(struct sock *sk) +static inline u64 ll_start_time(unsigned int flag) { return 0; } -static inline u64 ll_end_time(void) +static inline u64 ll_run_time(void) { return 0; } @@ -191,7 +191,7 @@ static inline void sk_mark_ll(struct sock *sk, struct sk_buff *skb) { } -static inline bool can_poll_ll(u64 end_time) +static inline bool can_poll_ll(u64 start_time, u64 run_time) { return false; } -- cgit v1.2.3 From 39e24a16283c6780a9049db75a91bcf6b5943b9a Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 24 Jun 2013 15:06:57 +0530 Subject: regulator: s5m8767: Update s5m8767-regulator bindings document s5m8767 regulator is used on Exynos platforms which use pin controller to configure GPIOs. Update the example accordingly. [This had previously been broken by code changes which failed to update the documentation -- broonie] Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown --- .../devicetree/bindings/regulator/s5m8767-regulator.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/regulator/s5m8767-regulator.txt b/Documentation/devicetree/bindings/regulator/s5m8767-regulator.txt index a35ff99003a5..7364f7114fbd 100644 --- a/Documentation/devicetree/bindings/regulator/s5m8767-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/s5m8767-regulator.txt @@ -103,13 +103,13 @@ Example: s5m8767,pmic-buck-default-dvs-idx = <0>; - s5m8767,pmic-buck-dvs-gpios = <&gpx0 0 1 0 0>, /* DVS1 */ - <&gpx0 1 1 0 0>, /* DVS2 */ - <&gpx0 2 1 0 0>; /* DVS3 */ + s5m8767,pmic-buck-dvs-gpios = <&gpx0 0 0>, /* DVS1 */ + <&gpx0 1 0>, /* DVS2 */ + <&gpx0 2 0>; /* DVS3 */ - s5m8767,pmic-buck-ds-gpios = <&gpx2 3 1 0 0>, /* SET1 */ - <&gpx2 4 1 0 0>, /* SET2 */ - <&gpx2 5 1 0 0>; /* SET3 */ + s5m8767,pmic-buck-ds-gpios = <&gpx2 3 0>, /* SET1 */ + <&gpx2 4 0>, /* SET2 */ + <&gpx2 5 0>; /* SET3 */ s5m8767,pmic-buck2-dvs-voltage = <1350000>, <1300000>, <1250000>, <1200000>, -- cgit v1.2.3 From d50235b7bc3ee0a0427984d763ea7534149531b4 Mon Sep 17 00:00:00 2001 From: Jianpeng Ma Date: Wed, 3 Jul 2013 13:25:24 +0200 Subject: elevator: Fix a race in elevator switching There's a race between elevator switching and normal io operation. Because the allocation of struct elevator_queue and struct elevator_data don't in a atomic operation.So there are have chance to use NULL ->elevator_data. For example: Thread A: Thread B blk_queu_bio elevator_switch spin_lock_irq(q->queue_block) elevator_alloc elv_merge elevator_init_fn Because call elevator_alloc, it can't hold queue_lock and the ->elevator_data is NULL.So at the same time, threadA call elv_merge and nedd some info of elevator_data.So the crash happened. Move the elevator_alloc into func elevator_init_fn, it make the operations in a atomic operation. Using the follow method can easy reproduce this bug 1:dd if=/dev/sdb of=/dev/null 2:while true;do echo noop > scheduler;echo deadline > scheduler;done The test method also use this method. Signed-off-by: Jianpeng Ma Signed-off-by: Jens Axboe --- block/cfq-iosched.c | 17 ++++++++++++++--- block/deadline-iosched.c | 16 +++++++++++++--- block/elevator.c | 25 +++++-------------------- block/noop-iosched.c | 17 ++++++++++++++--- include/linux/elevator.h | 6 +++++- 5 files changed, 51 insertions(+), 30 deletions(-) diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index d5cd3131c57a..d5bbdcfd0dab 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -4347,18 +4347,28 @@ static void cfq_exit_queue(struct elevator_queue *e) kfree(cfqd); } -static int cfq_init_queue(struct request_queue *q) +static int cfq_init_queue(struct request_queue *q, struct elevator_type *e) { struct cfq_data *cfqd; struct blkcg_gq *blkg __maybe_unused; int i, ret; + struct elevator_queue *eq; + + eq = elevator_alloc(q, e); + if (!eq) + return -ENOMEM; cfqd = kmalloc_node(sizeof(*cfqd), GFP_KERNEL | __GFP_ZERO, q->node); - if (!cfqd) + if (!cfqd) { + kobject_put(&eq->kobj); return -ENOMEM; + } + eq->elevator_data = cfqd; cfqd->queue = q; - q->elevator->elevator_data = cfqd; + spin_lock_irq(q->queue_lock); + q->elevator = eq; + spin_unlock_irq(q->queue_lock); /* Init root service tree */ cfqd->grp_service_tree = CFQ_RB_ROOT; @@ -4433,6 +4443,7 @@ static int cfq_init_queue(struct request_queue *q) out_free: kfree(cfqd); + kobject_put(&eq->kobj); return ret; } diff --git a/block/deadline-iosched.c b/block/deadline-iosched.c index ba19a3afab79..20614a332362 100644 --- a/block/deadline-iosched.c +++ b/block/deadline-iosched.c @@ -337,13 +337,21 @@ static void deadline_exit_queue(struct elevator_queue *e) /* * initialize elevator private data (deadline_data). */ -static int deadline_init_queue(struct request_queue *q) +static int deadline_init_queue(struct request_queue *q, struct elevator_type *e) { struct deadline_data *dd; + struct elevator_queue *eq; + + eq = elevator_alloc(q, e); + if (!eq) + return -ENOMEM; dd = kmalloc_node(sizeof(*dd), GFP_KERNEL | __GFP_ZERO, q->node); - if (!dd) + if (!dd) { + kobject_put(&eq->kobj); return -ENOMEM; + } + eq->elevator_data = dd; INIT_LIST_HEAD(&dd->fifo_list[READ]); INIT_LIST_HEAD(&dd->fifo_list[WRITE]); @@ -355,7 +363,9 @@ static int deadline_init_queue(struct request_queue *q) dd->front_merges = 1; dd->fifo_batch = fifo_batch; - q->elevator->elevator_data = dd; + spin_lock_irq(q->queue_lock); + q->elevator = eq; + spin_unlock_irq(q->queue_lock); return 0; } diff --git a/block/elevator.c b/block/elevator.c index eba5b04c29b1..668394d18588 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -150,7 +150,7 @@ void __init load_default_elevator_module(void) static struct kobj_type elv_ktype; -static struct elevator_queue *elevator_alloc(struct request_queue *q, +struct elevator_queue *elevator_alloc(struct request_queue *q, struct elevator_type *e) { struct elevator_queue *eq; @@ -170,6 +170,7 @@ err: elevator_put(e); return NULL; } +EXPORT_SYMBOL(elevator_alloc); static void elevator_release(struct kobject *kobj) { @@ -221,16 +222,7 @@ int elevator_init(struct request_queue *q, char *name) } } - q->elevator = elevator_alloc(q, e); - if (!q->elevator) - return -ENOMEM; - - err = e->ops.elevator_init_fn(q); - if (err) { - kobject_put(&q->elevator->kobj); - return err; - } - + err = e->ops.elevator_init_fn(q, e); return 0; } EXPORT_SYMBOL(elevator_init); @@ -935,16 +927,9 @@ static int elevator_switch(struct request_queue *q, struct elevator_type *new_e) spin_unlock_irq(q->queue_lock); /* allocate, init and register new elevator */ - err = -ENOMEM; - q->elevator = elevator_alloc(q, new_e); - if (!q->elevator) - goto fail_init; - - err = new_e->ops.elevator_init_fn(q); - if (err) { - kobject_put(&q->elevator->kobj); + err = new_e->ops.elevator_init_fn(q, new_e); + if (err) goto fail_init; - } if (registered) { err = elv_register_queue(q); diff --git a/block/noop-iosched.c b/block/noop-iosched.c index 5d1bf70e33d5..3de89d4690f3 100644 --- a/block/noop-iosched.c +++ b/block/noop-iosched.c @@ -59,16 +59,27 @@ noop_latter_request(struct request_queue *q, struct request *rq) return list_entry(rq->queuelist.next, struct request, queuelist); } -static int noop_init_queue(struct request_queue *q) +static int noop_init_queue(struct request_queue *q, struct elevator_type *e) { struct noop_data *nd; + struct elevator_queue *eq; + + eq = elevator_alloc(q, e); + if (!eq) + return -ENOMEM; nd = kmalloc_node(sizeof(*nd), GFP_KERNEL, q->node); - if (!nd) + if (!nd) { + kobject_put(&eq->kobj); return -ENOMEM; + } + eq->elevator_data = nd; INIT_LIST_HEAD(&nd->queue); - q->elevator->elevator_data = nd; + + spin_lock_irq(q->queue_lock); + q->elevator = eq; + spin_unlock_irq(q->queue_lock); return 0; } diff --git a/include/linux/elevator.h b/include/linux/elevator.h index acd0312d46fb..306dd8cd0b6f 100644 --- a/include/linux/elevator.h +++ b/include/linux/elevator.h @@ -7,6 +7,7 @@ #ifdef CONFIG_BLOCK struct io_cq; +struct elevator_type; typedef int (elevator_merge_fn) (struct request_queue *, struct request **, struct bio *); @@ -35,7 +36,8 @@ typedef void (elevator_put_req_fn) (struct request *); typedef void (elevator_activate_req_fn) (struct request_queue *, struct request *); typedef void (elevator_deactivate_req_fn) (struct request_queue *, struct request *); -typedef int (elevator_init_fn) (struct request_queue *); +typedef int (elevator_init_fn) (struct request_queue *, + struct elevator_type *e); typedef void (elevator_exit_fn) (struct elevator_queue *); struct elevator_ops @@ -155,6 +157,8 @@ extern int elevator_init(struct request_queue *, char *); extern void elevator_exit(struct elevator_queue *); extern int elevator_change(struct request_queue *, const char *); extern bool elv_rq_merge_ok(struct request *, struct bio *); +extern struct elevator_queue *elevator_alloc(struct request_queue *, + struct elevator_type *); /* * Helper functions. -- cgit v1.2.3 From 1ba65ae4bdbd43265c51ee4c30ff21a48124b6d8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 3 Jul 2013 14:01:32 +0200 Subject: ALSA: vmaster: Fix the regression of missing vmaster hook call The commit [1ca2f2ec: ALSA: vmaster: Add snd_ctl_sync_vmaster() helper function] changed master_put() function and the check for the required vmaster hook call is wrongly performed now, which results in the missing hook call upon "Master Playback Switch" value changes. This patch corrects the check logic. Signed-off-by: Takashi Iwai --- sound/core/vmaster.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/core/vmaster.c b/sound/core/vmaster.c index 5df8dc25ad80..842a97d5fc3a 100644 --- a/sound/core/vmaster.c +++ b/sound/core/vmaster.c @@ -348,7 +348,7 @@ static int master_put(struct snd_kcontrol *kcontrol, err = sync_slaves(master, old_val, new_val); if (err < 0) return err; - if (master->hook && first_init) + if (master->hook && !first_init) master->hook(master->hook_private_data, master->val); return 1; } -- cgit v1.2.3 From 46a1c2c7ae53de2a5676754b54a73c591a3951d2 Mon Sep 17 00:00:00 2001 From: Jie Liu Date: Tue, 25 Jun 2013 12:02:13 +0800 Subject: vfs: export lseek_execute() to modules For those file systems(btrfs/ext4/ocfs2/tmpfs) that support SEEK_DATA/SEEK_HOLE functions, we end up handling the similar matter in lseek_execute() to update the current file offset to the desired offset if it is valid, ceph also does the simliar things at ceph_llseek(). To reduce the duplications, this patch make lseek_execute() public accessible so that we can call it directly from the underlying file systems. Thanks Dave Chinner for this suggestion. [AV: call it vfs_setpos(), don't bring the removed 'inode' argument back] v2->v1: - Add kernel-doc comments for lseek_execute() - Call lseek_execute() in ceph->llseek() Signed-off-by: Jie Liu Cc: Dave Chinner Cc: Al Viro Cc: Andi Kleen Cc: Andrew Morton Cc: Christoph Hellwig Cc: Chris Mason Cc: Josef Bacik Cc: Ben Myers Cc: Ted Tso Cc: Hugh Dickins Cc: Mark Fasheh Cc: Joel Becker Cc: Sage Weil Signed-off-by: Al Viro --- fs/btrfs/file.c | 15 +-------------- fs/ceph/file.c | 11 +---------- fs/ext4/file.c | 24 ++---------------------- fs/ocfs2/file.c | 12 +----------- fs/read_write.c | 19 ++++++++++++++++--- fs/xfs/xfs_file.c | 6 ++---- include/linux/fs.h | 1 + mm/shmem.c | 5 +---- 8 files changed, 25 insertions(+), 68 deletions(-) diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 4205ba752d40..89da56a58b63 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -2425,20 +2425,7 @@ static loff_t btrfs_file_llseek(struct file *file, loff_t offset, int whence) } } - if (offset < 0 && !(file->f_mode & FMODE_UNSIGNED_OFFSET)) { - offset = -EINVAL; - goto out; - } - if (offset > inode->i_sb->s_maxbytes) { - offset = -EINVAL; - goto out; - } - - /* Special lock needed here? */ - if (offset != file->f_pos) { - file->f_pos = offset; - file->f_version = 0; - } + offset = vfs_setpos(file, offset, inode->i_sb->s_maxbytes); out: mutex_unlock(&inode->i_mutex); return offset; diff --git a/fs/ceph/file.c b/fs/ceph/file.c index 656e16907430..16c989d3e23c 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -866,16 +866,7 @@ static loff_t ceph_llseek(struct file *file, loff_t offset, int whence) break; } - if (offset < 0 || offset > inode->i_sb->s_maxbytes) { - offset = -EINVAL; - goto out; - } - - /* Special lock needed here? */ - if (offset != file->f_pos) { - file->f_pos = offset; - file->f_version = 0; - } + offset = vfs_setpos(file, offset, inode->i_sb->s_maxbytes); out: mutex_unlock(&inode->i_mutex); diff --git a/fs/ext4/file.c b/fs/ext4/file.c index b1b4d51b5d86..469361dbe619 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -494,17 +494,7 @@ static loff_t ext4_seek_data(struct file *file, loff_t offset, loff_t maxsize) if (dataoff > isize) return -ENXIO; - if (dataoff < 0 && !(file->f_mode & FMODE_UNSIGNED_OFFSET)) - return -EINVAL; - if (dataoff > maxsize) - return -EINVAL; - - if (dataoff != file->f_pos) { - file->f_pos = dataoff; - file->f_version = 0; - } - - return dataoff; + return vfs_setpos(file, dataoff, maxsize); } /* @@ -580,17 +570,7 @@ static loff_t ext4_seek_hole(struct file *file, loff_t offset, loff_t maxsize) if (holeoff > isize) holeoff = isize; - if (holeoff < 0 && !(file->f_mode & FMODE_UNSIGNED_OFFSET)) - return -EINVAL; - if (holeoff > maxsize) - return -EINVAL; - - if (holeoff != file->f_pos) { - file->f_pos = holeoff; - file->f_version = 0; - } - - return holeoff; + return vfs_setpos(file, holeoff, maxsize); } /* diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 8a38714f1d92..41000f223ca4 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -2646,17 +2646,7 @@ static loff_t ocfs2_file_llseek(struct file *file, loff_t offset, int whence) goto out; } - if (offset < 0 && !(file->f_mode & FMODE_UNSIGNED_OFFSET)) - ret = -EINVAL; - if (!ret && offset > inode->i_sb->s_maxbytes) - ret = -EINVAL; - if (ret) - goto out; - - if (offset != file->f_pos) { - file->f_pos = offset; - file->f_version = 0; - } + offset = vfs_setpos(file, offset, inode->i_sb->s_maxbytes); out: mutex_unlock(&inode->i_mutex); diff --git a/fs/read_write.c b/fs/read_write.c index 37d16e82b575..122a3846d9e1 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -41,7 +41,19 @@ static inline int unsigned_offsets(struct file *file) return file->f_mode & FMODE_UNSIGNED_OFFSET; } -static loff_t lseek_execute(struct file *file, loff_t offset, loff_t maxsize) +/** + * vfs_setpos - update the file offset for lseek + * @file: file structure in question + * @offset: file offset to seek to + * @maxsize: maximum file size + * + * This is a low-level filesystem helper for updating the file offset to + * the value specified by @offset if the given offset is valid and it is + * not equal to the current file offset. + * + * Return the specified offset on success and -EINVAL on invalid offset. + */ +loff_t vfs_setpos(struct file *file, loff_t offset, loff_t maxsize) { if (offset < 0 && !unsigned_offsets(file)) return -EINVAL; @@ -54,6 +66,7 @@ static loff_t lseek_execute(struct file *file, loff_t offset, loff_t maxsize) } return offset; } +EXPORT_SYMBOL(vfs_setpos); /** * generic_file_llseek_size - generic llseek implementation for regular files @@ -94,7 +107,7 @@ generic_file_llseek_size(struct file *file, loff_t offset, int whence, * like SEEK_SET. */ spin_lock(&file->f_lock); - offset = lseek_execute(file, file->f_pos + offset, maxsize); + offset = vfs_setpos(file, file->f_pos + offset, maxsize); spin_unlock(&file->f_lock); return offset; case SEEK_DATA: @@ -116,7 +129,7 @@ generic_file_llseek_size(struct file *file, loff_t offset, int whence, break; } - return lseek_execute(file, offset, maxsize); + return vfs_setpos(file, offset, maxsize); } EXPORT_SYMBOL(generic_file_llseek_size); diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index 0ad2b95fca12..de3dc98f4e8f 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -1268,8 +1268,7 @@ xfs_seek_data( } out: - if (offset != file->f_pos) - file->f_pos = offset; + offset = vfs_setpos(file, offset, inode->i_sb->s_maxbytes); out_unlock: xfs_iunlock_map_shared(ip, lock); @@ -1377,8 +1376,7 @@ out: * situation in particular. */ offset = min_t(loff_t, offset, isize); - if (offset != file->f_pos) - file->f_pos = offset; + offset = vfs_setpos(file, offset, inode->i_sb->s_maxbytes); out_unlock: xfs_iunlock_map_shared(ip, lock); diff --git a/include/linux/fs.h b/include/linux/fs.h index a137a73fc1fe..bccb1924ec93 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2426,6 +2426,7 @@ extern void file_ra_state_init(struct file_ra_state *ra, struct address_space *mapping); extern loff_t noop_llseek(struct file *file, loff_t offset, int whence); extern loff_t no_llseek(struct file *file, loff_t offset, int whence); +extern loff_t vfs_setpos(struct file *file, loff_t offset, loff_t maxsize); extern loff_t generic_file_llseek(struct file *file, loff_t offset, int whence); extern loff_t generic_file_llseek_size(struct file *file, loff_t offset, int whence, loff_t maxsize, loff_t eof); diff --git a/mm/shmem.c b/mm/shmem.c index f887358dabc5..118dfa4952f4 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -1798,10 +1798,7 @@ static loff_t shmem_file_llseek(struct file *file, loff_t offset, int whence) } } - if (offset >= 0 && offset != file->f_pos) { - file->f_pos = offset; - file->f_version = 0; - } + offset = vfs_setpos(file, offset, MAX_LFS_FILESIZE); mutex_unlock(&inode->i_mutex); return offset; } -- cgit v1.2.3 From af51a2ac36d1f96bee30438ec95a51e4635d1e33 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 29 Jun 2013 13:23:08 +0400 Subject: ext4: ->tmpfile() support very similar to ext3 counterpart... Signed-off-by: Al Viro --- fs/ext4/namei.c | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 6653fc35ecb7..f91002f8c017 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -2299,6 +2299,45 @@ retry: return err; } +static int ext4_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) +{ + handle_t *handle; + struct inode *inode; + int err, retries = 0; + + dquot_initialize(dir); + +retry: + inode = ext4_new_inode_start_handle(dir, mode, + NULL, 0, NULL, + EXT4_HT_DIR, + EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb) + + 4 + EXT4_XATTR_TRANS_BLOCKS); + handle = ext4_journal_current_handle(); + err = PTR_ERR(inode); + if (!IS_ERR(inode)) { + inode->i_op = &ext4_file_inode_operations; + inode->i_fop = &ext4_file_operations; + ext4_set_aops(inode); + err = ext4_orphan_add(handle, inode); + if (err) + goto err_drop_inode; + mark_inode_dirty(inode); + d_tmpfile(dentry, inode); + unlock_new_inode(inode); + } + if (handle) + ext4_journal_stop(handle); + if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries)) + goto retry; + return err; +err_drop_inode: + ext4_journal_stop(handle); + unlock_new_inode(inode); + iput(inode); + return err; +} + struct ext4_dir_entry_2 *ext4_init_dot_dotdot(struct inode *inode, struct ext4_dir_entry_2 *de, int blocksize, int csum_size, @@ -2906,7 +2945,7 @@ static int ext4_link(struct dentry *old_dentry, retry: handle = ext4_journal_start(dir, EXT4_HT_DIR, (EXT4_DATA_TRANS_BLOCKS(dir->i_sb) + - EXT4_INDEX_EXTRA_TRANS_BLOCKS)); + EXT4_INDEX_EXTRA_TRANS_BLOCKS) + 1); if (IS_ERR(handle)) return PTR_ERR(handle); @@ -2920,6 +2959,11 @@ retry: err = ext4_add_entry(handle, dentry, inode); if (!err) { ext4_mark_inode_dirty(handle, inode); + /* this can happen only for tmpfile being + * linked the first time + */ + if (inode->i_nlink == 1) + ext4_orphan_del(handle, inode); d_instantiate(dentry, inode); } else { drop_nlink(inode); @@ -3172,6 +3216,7 @@ const struct inode_operations ext4_dir_inode_operations = { .mkdir = ext4_mkdir, .rmdir = ext4_rmdir, .mknod = ext4_mknod, + .tmpfile = ext4_tmpfile, .rename = ext4_rename, .setattr = ext4_setattr, .setxattr = generic_setxattr, -- cgit v1.2.3 From 48bde8d3620f5f3c6ae9ff599eb404055ae51664 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 3 Jul 2013 16:19:23 +0400 Subject: Document ->tmpfile() Signed-off-by: Al Viro --- Documentation/filesystems/Locking | 2 ++ Documentation/filesystems/vfs.txt | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index 7d9ca7a83fcc..e95d3131309e 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -64,6 +64,7 @@ prototypes: int (*atomic_open)(struct inode *, struct dentry *, struct file *, unsigned open_flag, umode_t create_mode, int *opened); + int (*tmpfile) (struct inode *, struct dentry *, umode_t); locking rules: all may block @@ -91,6 +92,7 @@ removexattr: yes fiemap: no update_time: no atomic_open: yes +tmpfile: no Additionally, ->rmdir(), ->unlink() and ->rename() have ->i_mutex on victim. diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index 51ba44e3fc40..aeff462c7228 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt @@ -360,6 +360,8 @@ struct inode_operations { int (*removexattr) (struct dentry *, const char *); void (*update_time)(struct inode *, struct timespec *, int); int (*atomic_open)(struct inode *, struct dentry *, + int (*tmpfile) (struct inode *, struct dentry *, umode_t); +} ____cacheline_aligned; struct file *, unsigned open_flag, umode_t create_mode, int *opened); }; @@ -472,6 +474,9 @@ otherwise noted. component is negative or needs lookup. Cached positive dentries are still handled by f_op->open(). + tmpfile: called in the end of O_TMPFILE open(). Optional, equivalent to + atomically creating, opening and unlinking a file in given directory. + The Address Space Object ======================== -- cgit v1.2.3 From 04130cc973a96df33b1429024fd6dec59fa35a84 Mon Sep 17 00:00:00 2001 From: Daniel Tang Date: Sun, 9 Jun 2013 12:33:55 +1000 Subject: Fix a build warning in scripts/mod/file2alias.c On some systems, __used is already defined in sys/cdefs.h and causes a build warning: scripts/mod/file2alias.c:85:1: warning: "__used" redefined In file included from /usr/include/stdio.h:64, from scripts/mod/modpost.h:1, from scripts/mod/file2alias.c:13: /usr/include/sys/cdefs.h:146:1: warning: this is the location of the previous definition This adds an extra check before defining the __used macro to see if the macro was already defined elsewhere. Signed-off-by: Daniel Tang Signed-off-by: Michal Marek --- scripts/mod/file2alias.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 45f9a3377dcd..ab554564569a 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -79,10 +79,12 @@ struct devtable **__start___devtable, **__stop___devtable; extern struct devtable *__start___devtable[], *__stop___devtable[]; #endif /* __MACH__ */ -#if __GNUC__ == 3 && __GNUC_MINOR__ < 3 -# define __used __attribute__((__unused__)) -#else -# define __used __attribute__((__used__)) +#if !defined(__used) +# if __GNUC__ == 3 && __GNUC_MINOR__ < 3 +# define __used __attribute__((__unused__)) +# else +# define __used __attribute__((__used__)) +# endif #endif /* Define a variable f that holds the value of field f of struct devid -- cgit v1.2.3 From d2aae8477cd00325bb7c7c7e95be488088900c48 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Thu, 2 May 2013 14:50:37 +0200 Subject: Makefile: Fix install error with make -j option Make modules_install fails with -j option: DEPMOD Usage: .../.source/linux/scripts/depmod.sh /sbin/depmod make[1]: *** [_modinst_post] Error 1 Adding kernelrelease dependency to fix this. Signed-off-by: Robert Richter Cc: Signed-off-by: Michal Marek --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 6280aa192c36..29339b430126 100644 --- a/Makefile +++ b/Makefile @@ -981,7 +981,7 @@ _modinst_: # boot a modules.dep even before / is mounted read-write. However the # boot script depmod is the master version. PHONY += _modinst_post -_modinst_post: _modinst_ +_modinst_post: include/config/kernel.release _modinst_ $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.fwinst obj=firmware __fw_modinst $(call cmd,depmod) -- cgit v1.2.3 From 90d06a46835ba73deffb483970fdc2bffa4bb274 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 18 Jun 2013 14:49:29 -0700 Subject: coccicheck: span checks across CPUs This adds parallelism by default to the "coccicheck" target using spatch's "-max" and "-index" arguments. Signed-off-by: Kees Cook Signed-off-by: Nicolas Palix Signed-off-by: Michal Marek --- Documentation/coccinelle.txt | 5 +++++ scripts/coccicheck | 31 ++++++++++++++++++++++++++++--- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/Documentation/coccinelle.txt b/Documentation/coccinelle.txt index 18de78599dd4..408439d07889 100644 --- a/Documentation/coccinelle.txt +++ b/Documentation/coccinelle.txt @@ -91,6 +91,11 @@ To enable verbose messages set the V= variable, for example: make coccicheck MODE=report V=1 +By default, coccicheck tries to run as parallel as possible. To change +the parallelism, set the J= variable. For example, to run across 4 CPUs: + + make coccicheck MODE=report J=4 + Using Coccinelle with a single semantic patch ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/scripts/coccicheck b/scripts/coccicheck index 9d8780cdbcd4..ad8e8ffd3c7e 100755 --- a/scripts/coccicheck +++ b/scripts/coccicheck @@ -2,15 +2,24 @@ SPATCH="`which ${SPATCH:=spatch}`" +trap kill_running SIGTERM SIGINT +declare -a SPATCH_PID + # The verbosity may be set by the environmental parameter V= # as for example with 'make V=1 coccicheck' if [ -n "$V" -a "$V" != "0" ]; then - VERBOSE=1 + VERBOSE="$V" else VERBOSE=0 fi +if [ -z "$J" ]; then + NPROC=$(getconf _NPROCESSORS_ONLN) +else + NPROC="$J" +fi + FLAGS="$SPFLAGS -very_quiet" # spatch only allows include directories with the syntax "-I include" @@ -69,12 +78,28 @@ if [ "$ONLINE" = "0" ] ; then fi run_cmd() { + local i if [ $VERBOSE -ne 0 ] ; then - echo "Running: $@" + echo "Running ($NPROC in parallel): $@" fi - eval $@ + for i in $(seq 0 $(( NPROC - 1)) ); do + eval "$@ -max $NPROC -index $i &" + SPATCH_PID[$i]=$! + if [ $VERBOSE -eq 2 ] ; then + echo "${SPATCH_PID[$i]} running" + fi + done + wait } +kill_running() { + for i in $(seq $(( NPROC - 1 )) ); do + if [ $VERBOSE -eq 2 ] ; then + echo "Killing ${SPATCH_PID[$i]}" + fi + kill ${SPATCH_PID[$i]} 2>/dev/null + done +} coccinelle () { COCCI="$1" -- cgit v1.2.3 From 4b92b2aa98d917a379512b82cf94ea1ffcfc97af Mon Sep 17 00:00:00 2001 From: Nicolas Palix Date: Thu, 20 Jun 2013 13:10:55 +0200 Subject: Coccinelle: Update section of MAINTAINERS Add Documentation/coccinelle.txt in the COCCINELLE section. Signed-off-by: Nicolas Palix Signed-off-by: Michal Marek --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 73b1b3a38ff0..2d940510f42b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2103,6 +2103,7 @@ L: cocci@systeme.lip6.fr (moderated for non-subscribers) T: git git://git.kernel.org/pub/scm/linux/kernel/git/mmarek/kbuild.git misc W: http://coccinelle.lip6.fr/ S: Supported +F: Documentation/coccinelle.txt F: scripts/coccinelle/ F: scripts/coccicheck -- cgit v1.2.3 From 78a95b9b4b3dcb53736a1834e8557d3e1668562a Mon Sep 17 00:00:00 2001 From: Nicolas Palix Date: Thu, 20 Jun 2013 14:00:19 +0200 Subject: Coccinelle: Update the documentation - The new default mode is 'report'. - The available modes are detailed a bit more. - Some information about the use of spatch options are also given concerning the use of indexing tools. Signed-off-by: Nicolas Palix Signed-off-by: Michal Marek --- Documentation/coccinelle.txt | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/Documentation/coccinelle.txt b/Documentation/coccinelle.txt index 408439d07889..81a329fcd532 100644 --- a/Documentation/coccinelle.txt +++ b/Documentation/coccinelle.txt @@ -48,7 +48,7 @@ A Coccinelle-specific target is defined in the top level Makefile. This target is named 'coccicheck' and calls the 'coccicheck' front-end in the 'scripts' directory. -Four modes are defined: patch, report, context, and org. The mode to +Four basic modes are defined: patch, report, context, and org. The mode to use is specified by setting the MODE variable with 'MODE='. 'patch' proposes a fix, when possible. @@ -62,18 +62,24 @@ diff-like style.Lines of interest are indicated with '-'. 'org' generates a report in the Org mode format of Emacs. Note that not all semantic patches implement all modes. For easy use -of Coccinelle, the default mode is "chain" which tries the previous -modes in the order above until one succeeds. +of Coccinelle, the default mode is "report". -To make a report for every semantic patch, run the following command: +Two other modes provide some common combinations of these modes. - make coccicheck MODE=report +'chain' tries the previous modes in the order above until one succeeds. -NB: The 'report' mode is the default one. +'rep+ctxt' runs successively the report mode and the context mode. + It should be used with the C option (described later) + which checks the code on a file basis. -To produce patches, run: +Examples: + To make a report for every semantic patch, run the following command: - make coccicheck MODE=patch + make coccicheck MODE=report + + To produce patches, run: + + make coccicheck MODE=patch The coccicheck target applies every semantic patch available in the @@ -129,26 +135,33 @@ To check only newly edited code, use the value 2 for the C flag, i.e. make C=2 CHECK="scripts/coccicheck" +In these modes, which works on a file basis, there is no information +about semantic patches displayed, and no commit message proposed. + This runs every semantic patch in scripts/coccinelle by default. The COCCI variable may additionally be used to only apply a single semantic patch as shown in the previous section. -The "chain" mode is the default. You can select another one with the +The "report" mode is the default. You can select another one with the MODE variable explained above. -In this mode, there is no information about semantic patches -displayed, and no commit message proposed. - Additional flags ~~~~~~~~~~~~~~~~~~ Additional flags can be passed to spatch through the SPFLAGS variable. - make SPFLAGS=--use_glimpse coccicheck + make SPFLAGS=--use-glimpse coccicheck + make SPFLAGS=--use-idutils coccicheck See spatch --help to learn more about spatch options. +Note that the '--use-glimpse' and '--use-idutils' options +require external tools for indexing the code. None of them is +thus active by default. However, by indexing the code with +one of these tools, and according to the cocci file used, +spatch could proceed the entire code base more quickly. + Proposing new semantic patches ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- cgit v1.2.3 From f7b167113753e95ae61383e234f8d10142782ace Mon Sep 17 00:00:00 2001 From: "strnape1@fel.cvut.cz" Date: Wed, 8 May 2013 23:03:08 +0200 Subject: scripts: Coccinelle script for pci_free_consistent() Created coccinelle script for reporting missing pci_free_consistent() calls. Signed-off-by: Petr Strnad Signed-off-by: Nicolas Palix Signed-off-by: Michal Marek --- scripts/coccinelle/free/pci_free_consistent.cocci | 52 +++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 scripts/coccinelle/free/pci_free_consistent.cocci diff --git a/scripts/coccinelle/free/pci_free_consistent.cocci b/scripts/coccinelle/free/pci_free_consistent.cocci new file mode 100644 index 000000000000..43600ccb62a8 --- /dev/null +++ b/scripts/coccinelle/free/pci_free_consistent.cocci @@ -0,0 +1,52 @@ +/// Find missing pci_free_consistent for every pci_alloc_consistent. +/// +// Confidence: Moderate +// Copyright: (C) 2013 Petr Strnad. GPLv2. +// URL: http://coccinelle.lip6.fr/ +// Keywords: pci_free_consistent, pci_alloc_consistent +// Options: --no-includes --include-headers + +virtual report +virtual org + +@search@ +local idexpression id; +expression x,y,z,e; +position p1,p2; +type T; +@@ + +id = pci_alloc_consistent@p1(x,y,&z) +... when != e = id +if (id == NULL || ...) { ... return ...; } +... when != pci_free_consistent(x,y,id,z) + when != if (id) { ... pci_free_consistent(x,y,id,z) ... } + when != if (y) { ... pci_free_consistent(x,y,id,z) ... } + when != e = (T)id + when exists +( +return 0; +| +return 1; +| +return id; +| +return@p2 ...; +) + +@script:python depends on report@ +p1 << search.p1; +p2 << search.p2; +@@ + +msg = "ERROR: missing pci_free_consistent; pci_alloc_consistent on line %s and return without freeing on line %s" % (p1[0].line,p2[0].line) +coccilib.report.print_report(p2[0],msg) + +@script:python depends on org@ +p1 << search.p1; +p2 << search.p2; +@@ + +msg = "ERROR: missing pci_free_consistent; pci_alloc_consistent on line %s and return without freeing on line %s" % (p1[0].line,p2[0].line) +cocci.print_main(msg,p1) +cocci.print_secs("",p2) -- cgit v1.2.3 From b35310627f396efcc25d71fb343b3aa02039d20d Mon Sep 17 00:00:00 2001 From: Yaakov Selkowitz Date: Tue, 25 Jun 2013 20:13:37 -0500 Subject: tools/include: use stdint types for user-space byteshift headers Commit a07f7672d7cf0ff0d6e548a9feb6e0bd016d9c6c added user-space copies of the byteshift headers to be used by hostprogs, changing e.g. u8 to __u8. However, in order to cross-compile the kernel from a non-Linux system, stdint.h types need to be used instead of linux/types.h types. Signed-off-by: Yaakov Selkowitz Signed-off-by: Michal Marek --- tools/include/tools/be_byteshift.h | 34 +++++++++++++++++----------------- tools/include/tools/le_byteshift.h | 34 +++++++++++++++++----------------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/tools/include/tools/be_byteshift.h b/tools/include/tools/be_byteshift.h index f4912e2668ba..84c17d836578 100644 --- a/tools/include/tools/be_byteshift.h +++ b/tools/include/tools/be_byteshift.h @@ -1,68 +1,68 @@ #ifndef _TOOLS_BE_BYTESHIFT_H #define _TOOLS_BE_BYTESHIFT_H -#include +#include -static inline __u16 __get_unaligned_be16(const __u8 *p) +static inline uint16_t __get_unaligned_be16(const uint8_t *p) { return p[0] << 8 | p[1]; } -static inline __u32 __get_unaligned_be32(const __u8 *p) +static inline uint32_t __get_unaligned_be32(const uint8_t *p) { return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; } -static inline __u64 __get_unaligned_be64(const __u8 *p) +static inline uint64_t __get_unaligned_be64(const uint8_t *p) { - return (__u64)__get_unaligned_be32(p) << 32 | + return (uint64_t)__get_unaligned_be32(p) << 32 | __get_unaligned_be32(p + 4); } -static inline void __put_unaligned_be16(__u16 val, __u8 *p) +static inline void __put_unaligned_be16(uint16_t val, uint8_t *p) { *p++ = val >> 8; *p++ = val; } -static inline void __put_unaligned_be32(__u32 val, __u8 *p) +static inline void __put_unaligned_be32(uint32_t val, uint8_t *p) { __put_unaligned_be16(val >> 16, p); __put_unaligned_be16(val, p + 2); } -static inline void __put_unaligned_be64(__u64 val, __u8 *p) +static inline void __put_unaligned_be64(uint64_t val, uint8_t *p) { __put_unaligned_be32(val >> 32, p); __put_unaligned_be32(val, p + 4); } -static inline __u16 get_unaligned_be16(const void *p) +static inline uint16_t get_unaligned_be16(const void *p) { - return __get_unaligned_be16((const __u8 *)p); + return __get_unaligned_be16((const uint8_t *)p); } -static inline __u32 get_unaligned_be32(const void *p) +static inline uint32_t get_unaligned_be32(const void *p) { - return __get_unaligned_be32((const __u8 *)p); + return __get_unaligned_be32((const uint8_t *)p); } -static inline __u64 get_unaligned_be64(const void *p) +static inline uint64_t get_unaligned_be64(const void *p) { - return __get_unaligned_be64((const __u8 *)p); + return __get_unaligned_be64((const uint8_t *)p); } -static inline void put_unaligned_be16(__u16 val, void *p) +static inline void put_unaligned_be16(uint16_t val, void *p) { __put_unaligned_be16(val, p); } -static inline void put_unaligned_be32(__u32 val, void *p) +static inline void put_unaligned_be32(uint32_t val, void *p) { __put_unaligned_be32(val, p); } -static inline void put_unaligned_be64(__u64 val, void *p) +static inline void put_unaligned_be64(uint64_t val, void *p) { __put_unaligned_be64(val, p); } diff --git a/tools/include/tools/le_byteshift.h b/tools/include/tools/le_byteshift.h index c99d45a68bda..8fe9f2488ec7 100644 --- a/tools/include/tools/le_byteshift.h +++ b/tools/include/tools/le_byteshift.h @@ -1,68 +1,68 @@ #ifndef _TOOLS_LE_BYTESHIFT_H #define _TOOLS_LE_BYTESHIFT_H -#include +#include -static inline __u16 __get_unaligned_le16(const __u8 *p) +static inline uint16_t __get_unaligned_le16(const uint8_t *p) { return p[0] | p[1] << 8; } -static inline __u32 __get_unaligned_le32(const __u8 *p) +static inline uint32_t __get_unaligned_le32(const uint8_t *p) { return p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24; } -static inline __u64 __get_unaligned_le64(const __u8 *p) +static inline uint64_t __get_unaligned_le64(const uint8_t *p) { - return (__u64)__get_unaligned_le32(p + 4) << 32 | + return (uint64_t)__get_unaligned_le32(p + 4) << 32 | __get_unaligned_le32(p); } -static inline void __put_unaligned_le16(__u16 val, __u8 *p) +static inline void __put_unaligned_le16(uint16_t val, uint8_t *p) { *p++ = val; *p++ = val >> 8; } -static inline void __put_unaligned_le32(__u32 val, __u8 *p) +static inline void __put_unaligned_le32(uint32_t val, uint8_t *p) { __put_unaligned_le16(val >> 16, p + 2); __put_unaligned_le16(val, p); } -static inline void __put_unaligned_le64(__u64 val, __u8 *p) +static inline void __put_unaligned_le64(uint64_t val, uint8_t *p) { __put_unaligned_le32(val >> 32, p + 4); __put_unaligned_le32(val, p); } -static inline __u16 get_unaligned_le16(const void *p) +static inline uint16_t get_unaligned_le16(const void *p) { - return __get_unaligned_le16((const __u8 *)p); + return __get_unaligned_le16((const uint8_t *)p); } -static inline __u32 get_unaligned_le32(const void *p) +static inline uint32_t get_unaligned_le32(const void *p) { - return __get_unaligned_le32((const __u8 *)p); + return __get_unaligned_le32((const uint8_t *)p); } -static inline __u64 get_unaligned_le64(const void *p) +static inline uint64_t get_unaligned_le64(const void *p) { - return __get_unaligned_le64((const __u8 *)p); + return __get_unaligned_le64((const uint8_t *)p); } -static inline void put_unaligned_le16(__u16 val, void *p) +static inline void put_unaligned_le16(uint16_t val, void *p) { __put_unaligned_le16(val, p); } -static inline void put_unaligned_le32(__u32 val, void *p) +static inline void put_unaligned_le32(uint32_t val, void *p) { __put_unaligned_le32(val, p); } -static inline void put_unaligned_le64(__u64 val, void *p) +static inline void put_unaligned_le64(uint64_t val, void *p) { __put_unaligned_le64(val, p); } -- cgit v1.2.3 From 55ccb616a6e42052edb37e9c4f82cf8854a59429 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Fri, 28 Jun 2013 00:06:42 +0000 Subject: posix_cpu_timer: consolidate expiry time type The posix cpu timer expiry time is stored in a union of two types: a 64 bits field if we rely on scheduler precise accounting, or a cputime_t if we rely on jiffies. This results in quite some duplicate code and special cases to handle the two types. Just unify this into a single 64 bits field. cputime_t can always fit into it. Signed-off-by: Frederic Weisbecker Cc: Stanislaw Gruszka Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: Ingo Molnar Cc: Oleg Nesterov Cc: KOSAKI Motohiro Cc: Olivier Langlois Signed-off-by: Andrew Morton --- include/linux/posix-timers.h | 16 ++- kernel/posix-cpu-timers.c | 266 +++++++++++++++++-------------------------- 2 files changed, 117 insertions(+), 165 deletions(-) diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h index 7794d75ed155..907f3fd191ac 100644 --- a/include/linux/posix-timers.h +++ b/include/linux/posix-timers.h @@ -7,14 +7,20 @@ #include #include -union cpu_time_count { - cputime_t cpu; - unsigned long long sched; -}; + +static inline unsigned long long cputime_to_expires(cputime_t expires) +{ + return (__force unsigned long long)expires; +} + +static inline cputime_t expires_to_cputime(unsigned long long expires) +{ + return (__force cputime_t)expires; +} struct cpu_timer_list { struct list_head entry; - union cpu_time_count expires, incr; + unsigned long long expires, incr; struct task_struct *task; int firing; }; diff --git a/kernel/posix-cpu-timers.c b/kernel/posix-cpu-timers.c index 42670e9b44e0..c3c4ea1225a4 100644 --- a/kernel/posix-cpu-timers.c +++ b/kernel/posix-cpu-timers.c @@ -51,59 +51,28 @@ static int check_clock(const clockid_t which_clock) return error; } -static inline union cpu_time_count +static inline unsigned long long timespec_to_sample(const clockid_t which_clock, const struct timespec *tp) { - union cpu_time_count ret; - ret.sched = 0; /* high half always zero when .cpu used */ + unsigned long long ret; + + ret = 0; /* high half always zero when .cpu used */ if (CPUCLOCK_WHICH(which_clock) == CPUCLOCK_SCHED) { - ret.sched = (unsigned long long)tp->tv_sec * NSEC_PER_SEC + tp->tv_nsec; + ret = (unsigned long long)tp->tv_sec * NSEC_PER_SEC + tp->tv_nsec; } else { - ret.cpu = timespec_to_cputime(tp); + ret = cputime_to_expires(timespec_to_cputime(tp)); } return ret; } static void sample_to_timespec(const clockid_t which_clock, - union cpu_time_count cpu, + unsigned long long expires, struct timespec *tp) { if (CPUCLOCK_WHICH(which_clock) == CPUCLOCK_SCHED) - *tp = ns_to_timespec(cpu.sched); + *tp = ns_to_timespec(expires); else - cputime_to_timespec(cpu.cpu, tp); -} - -static inline int cpu_time_before(const clockid_t which_clock, - union cpu_time_count now, - union cpu_time_count then) -{ - if (CPUCLOCK_WHICH(which_clock) == CPUCLOCK_SCHED) { - return now.sched < then.sched; - } else { - return now.cpu < then.cpu; - } -} -static inline void cpu_time_add(const clockid_t which_clock, - union cpu_time_count *acc, - union cpu_time_count val) -{ - if (CPUCLOCK_WHICH(which_clock) == CPUCLOCK_SCHED) { - acc->sched += val.sched; - } else { - acc->cpu += val.cpu; - } -} -static inline union cpu_time_count cpu_time_sub(const clockid_t which_clock, - union cpu_time_count a, - union cpu_time_count b) -{ - if (CPUCLOCK_WHICH(which_clock) == CPUCLOCK_SCHED) { - a.sched -= b.sched; - } else { - a.cpu -= b.cpu; - } - return a; + cputime_to_timespec((__force cputime_t)expires, tp); } /* @@ -111,47 +80,31 @@ static inline union cpu_time_count cpu_time_sub(const clockid_t which_clock, * given the current clock sample. */ static void bump_cpu_timer(struct k_itimer *timer, - union cpu_time_count now) + unsigned long long now) { int i; + unsigned long long delta, incr; - if (timer->it.cpu.incr.sched == 0) + if (timer->it.cpu.incr == 0) return; - if (CPUCLOCK_WHICH(timer->it_clock) == CPUCLOCK_SCHED) { - unsigned long long delta, incr; + if (now < timer->it.cpu.expires) + return; - if (now.sched < timer->it.cpu.expires.sched) - return; - incr = timer->it.cpu.incr.sched; - delta = now.sched + incr - timer->it.cpu.expires.sched; - /* Don't use (incr*2 < delta), incr*2 might overflow. */ - for (i = 0; incr < delta - incr; i++) - incr = incr << 1; - for (; i >= 0; incr >>= 1, i--) { - if (delta < incr) - continue; - timer->it.cpu.expires.sched += incr; - timer->it_overrun += 1 << i; - delta -= incr; - } - } else { - cputime_t delta, incr; + incr = timer->it.cpu.incr; + delta = now + incr - timer->it.cpu.expires; - if (now.cpu < timer->it.cpu.expires.cpu) - return; - incr = timer->it.cpu.incr.cpu; - delta = now.cpu + incr - timer->it.cpu.expires.cpu; - /* Don't use (incr*2 < delta), incr*2 might overflow. */ - for (i = 0; incr < delta - incr; i++) - incr += incr; - for (; i >= 0; incr = incr >> 1, i--) { - if (delta < incr) - continue; - timer->it.cpu.expires.cpu += incr; - timer->it_overrun += 1 << i; - delta -= incr; - } + /* Don't use (incr*2 < delta), incr*2 might overflow. */ + for (i = 0; incr < delta - incr; i++) + incr = incr << 1; + + for (; i >= 0; incr >>= 1, i--) { + if (delta < incr) + continue; + + timer->it.cpu.expires += incr; + timer->it_overrun += 1 << i; + delta -= incr; } } @@ -170,21 +123,21 @@ static inline int task_cputime_zero(const struct task_cputime *cputime) return 0; } -static inline cputime_t prof_ticks(struct task_struct *p) +static inline unsigned long long prof_ticks(struct task_struct *p) { cputime_t utime, stime; task_cputime(p, &utime, &stime); - return utime + stime; + return cputime_to_expires(utime + stime); } -static inline cputime_t virt_ticks(struct task_struct *p) +static inline unsigned long long virt_ticks(struct task_struct *p) { cputime_t utime; task_cputime(p, &utime, NULL); - return utime; + return cputime_to_expires(utime); } static int @@ -225,19 +178,19 @@ posix_cpu_clock_set(const clockid_t which_clock, const struct timespec *tp) * Sample a per-thread clock for the given task. */ static int cpu_clock_sample(const clockid_t which_clock, struct task_struct *p, - union cpu_time_count *cpu) + unsigned long long *sample) { switch (CPUCLOCK_WHICH(which_clock)) { default: return -EINVAL; case CPUCLOCK_PROF: - cpu->cpu = prof_ticks(p); + *sample = prof_ticks(p); break; case CPUCLOCK_VIRT: - cpu->cpu = virt_ticks(p); + *sample = virt_ticks(p); break; case CPUCLOCK_SCHED: - cpu->sched = task_sched_runtime(p); + *sample = task_sched_runtime(p); break; } return 0; @@ -284,7 +237,7 @@ void thread_group_cputimer(struct task_struct *tsk, struct task_cputime *times) */ static int cpu_clock_sample_group(const clockid_t which_clock, struct task_struct *p, - union cpu_time_count *cpu) + unsigned long long *sample) { struct task_cputime cputime; @@ -293,15 +246,15 @@ static int cpu_clock_sample_group(const clockid_t which_clock, return -EINVAL; case CPUCLOCK_PROF: thread_group_cputime(p, &cputime); - cpu->cpu = cputime.utime + cputime.stime; + *sample = cputime_to_expires(cputime.utime + cputime.stime); break; case CPUCLOCK_VIRT: thread_group_cputime(p, &cputime); - cpu->cpu = cputime.utime; + *sample = cputime_to_expires(cputime.utime); break; case CPUCLOCK_SCHED: thread_group_cputime(p, &cputime); - cpu->sched = cputime.sum_exec_runtime; + *sample = cputime.sum_exec_runtime; break; } return 0; @@ -312,7 +265,7 @@ static int posix_cpu_clock_get(const clockid_t which_clock, struct timespec *tp) { const pid_t pid = CPUCLOCK_PID(which_clock); int error = -EINVAL; - union cpu_time_count rtn; + unsigned long long rtn; if (pid == 0) { /* @@ -461,30 +414,30 @@ static void cleanup_timers(struct list_head *head, list_for_each_entry_safe(timer, next, head, entry) { list_del_init(&timer->entry); - if (timer->expires.cpu < ptime) { - timer->expires.cpu = 0; + if (timer->expires < cputime_to_expires(ptime)) { + timer->expires = 0; } else { - timer->expires.cpu -= ptime; + timer->expires -= cputime_to_expires(ptime); } } ++head; list_for_each_entry_safe(timer, next, head, entry) { list_del_init(&timer->entry); - if (timer->expires.cpu < utime) { - timer->expires.cpu = 0; + if (timer->expires < cputime_to_expires(utime)) { + timer->expires = 0; } else { - timer->expires.cpu -= utime; + timer->expires -= cputime_to_expires(utime); } } ++head; list_for_each_entry_safe(timer, next, head, entry) { list_del_init(&timer->entry); - if (timer->expires.sched < sum_exec_runtime) { - timer->expires.sched = 0; + if (timer->expires < sum_exec_runtime) { + timer->expires = 0; } else { - timer->expires.sched -= sum_exec_runtime; + timer->expires -= sum_exec_runtime; } } } @@ -516,7 +469,7 @@ void posix_cpu_timers_exit_group(struct task_struct *tsk) tsk->se.sum_exec_runtime + sig->sum_sched_runtime); } -static void clear_dead_task(struct k_itimer *timer, union cpu_time_count now) +static void clear_dead_task(struct k_itimer *timer, unsigned long long now) { /* * That's all for this thread or process. @@ -524,9 +477,7 @@ static void clear_dead_task(struct k_itimer *timer, union cpu_time_count now) */ put_task_struct(timer->it.cpu.task); timer->it.cpu.task = NULL; - timer->it.cpu.expires = cpu_time_sub(timer->it_clock, - timer->it.cpu.expires, - now); + timer->it.cpu.expires -= now; } static inline int expires_gt(cputime_t expires, cputime_t new_exp) @@ -558,14 +509,14 @@ static void arm_timer(struct k_itimer *timer) listpos = head; list_for_each_entry(next, head, entry) { - if (cpu_time_before(timer->it_clock, nt->expires, next->expires)) + if (nt->expires < next->expires) break; listpos = &next->entry; } list_add(&nt->entry, listpos); if (listpos == head) { - union cpu_time_count *exp = &nt->expires; + unsigned long long exp = nt->expires; /* * We are the new earliest-expiring POSIX 1.b timer, hence @@ -576,17 +527,17 @@ static void arm_timer(struct k_itimer *timer) switch (CPUCLOCK_WHICH(timer->it_clock)) { case CPUCLOCK_PROF: - if (expires_gt(cputime_expires->prof_exp, exp->cpu)) - cputime_expires->prof_exp = exp->cpu; + if (expires_gt(cputime_expires->prof_exp, expires_to_cputime(exp))) + cputime_expires->prof_exp = expires_to_cputime(exp); break; case CPUCLOCK_VIRT: - if (expires_gt(cputime_expires->virt_exp, exp->cpu)) - cputime_expires->virt_exp = exp->cpu; + if (expires_gt(cputime_expires->virt_exp, expires_to_cputime(exp))) + cputime_expires->virt_exp = expires_to_cputime(exp); break; case CPUCLOCK_SCHED: if (cputime_expires->sched_exp == 0 || - cputime_expires->sched_exp > exp->sched) - cputime_expires->sched_exp = exp->sched; + cputime_expires->sched_exp > exp) + cputime_expires->sched_exp = exp; break; } } @@ -601,20 +552,20 @@ static void cpu_timer_fire(struct k_itimer *timer) /* * User don't want any signal. */ - timer->it.cpu.expires.sched = 0; + timer->it.cpu.expires = 0; } else if (unlikely(timer->sigq == NULL)) { /* * This a special case for clock_nanosleep, * not a normal timer from sys_timer_create. */ wake_up_process(timer->it_process); - timer->it.cpu.expires.sched = 0; - } else if (timer->it.cpu.incr.sched == 0) { + timer->it.cpu.expires = 0; + } else if (timer->it.cpu.incr == 0) { /* * One-shot timer. Clear it as soon as it's fired. */ posix_timer_event(timer, 0); - timer->it.cpu.expires.sched = 0; + timer->it.cpu.expires = 0; } else if (posix_timer_event(timer, ++timer->it_requeue_pending)) { /* * The signal did not get queued because the signal @@ -632,7 +583,7 @@ static void cpu_timer_fire(struct k_itimer *timer) */ static int cpu_timer_sample_group(const clockid_t which_clock, struct task_struct *p, - union cpu_time_count *cpu) + unsigned long long *sample) { struct task_cputime cputime; @@ -641,13 +592,13 @@ static int cpu_timer_sample_group(const clockid_t which_clock, default: return -EINVAL; case CPUCLOCK_PROF: - cpu->cpu = cputime.utime + cputime.stime; + *sample = cputime_to_expires(cputime.utime + cputime.stime); break; case CPUCLOCK_VIRT: - cpu->cpu = cputime.utime; + *sample = cputime_to_expires(cputime.utime); break; case CPUCLOCK_SCHED: - cpu->sched = cputime.sum_exec_runtime + task_delta_exec(p); + *sample = cputime.sum_exec_runtime + task_delta_exec(p); break; } return 0; @@ -694,7 +645,7 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int flags, struct itimerspec *new, struct itimerspec *old) { struct task_struct *p = timer->it.cpu.task; - union cpu_time_count old_expires, new_expires, old_incr, val; + unsigned long long old_expires, new_expires, old_incr, val; int ret; if (unlikely(p == NULL)) { @@ -749,7 +700,7 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int flags, } if (old) { - if (old_expires.sched == 0) { + if (old_expires == 0) { old->it_value.tv_sec = 0; old->it_value.tv_nsec = 0; } else { @@ -764,11 +715,8 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int flags, * new setting. */ bump_cpu_timer(timer, val); - if (cpu_time_before(timer->it_clock, val, - timer->it.cpu.expires)) { - old_expires = cpu_time_sub( - timer->it_clock, - timer->it.cpu.expires, val); + if (val < timer->it.cpu.expires) { + old_expires = timer->it.cpu.expires - val; sample_to_timespec(timer->it_clock, old_expires, &old->it_value); @@ -791,8 +739,8 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int flags, goto out; } - if (new_expires.sched != 0 && !(flags & TIMER_ABSTIME)) { - cpu_time_add(timer->it_clock, &new_expires, val); + if (new_expires != 0 && !(flags & TIMER_ABSTIME)) { + new_expires += val; } /* @@ -801,8 +749,7 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int flags, * arm the timer (we'll just fake it for timer_gettime). */ timer->it.cpu.expires = new_expires; - if (new_expires.sched != 0 && - cpu_time_before(timer->it_clock, val, new_expires)) { + if (new_expires != 0 && val < new_expires) { arm_timer(timer); } @@ -826,8 +773,7 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int flags, timer->it_overrun_last = 0; timer->it_overrun = -1; - if (new_expires.sched != 0 && - !cpu_time_before(timer->it_clock, val, new_expires)) { + if (new_expires != 0 && !(val < new_expires)) { /* * The designated time already passed, so we notify * immediately, even if the thread never runs to @@ -849,7 +795,7 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int flags, static void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec *itp) { - union cpu_time_count now; + unsigned long long now; struct task_struct *p = timer->it.cpu.task; int clear_dead; @@ -859,7 +805,7 @@ static void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec *itp) sample_to_timespec(timer->it_clock, timer->it.cpu.incr, &itp->it_interval); - if (timer->it.cpu.expires.sched == 0) { /* Timer not armed at all. */ + if (timer->it.cpu.expires == 0) { /* Timer not armed at all. */ itp->it_value.tv_sec = itp->it_value.tv_nsec = 0; return; } @@ -891,7 +837,7 @@ static void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec *itp) */ put_task_struct(p); timer->it.cpu.task = NULL; - timer->it.cpu.expires.sched = 0; + timer->it.cpu.expires = 0; read_unlock(&tasklist_lock); goto dead; } else { @@ -912,10 +858,9 @@ static void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec *itp) goto dead; } - if (cpu_time_before(timer->it_clock, now, timer->it.cpu.expires)) { + if (now < timer->it.cpu.expires) { sample_to_timespec(timer->it_clock, - cpu_time_sub(timer->it_clock, - timer->it.cpu.expires, now), + timer->it.cpu.expires - now, &itp->it_value); } else { /* @@ -946,8 +891,8 @@ static void check_thread_timers(struct task_struct *tsk, struct cpu_timer_list *t = list_first_entry(timers, struct cpu_timer_list, entry); - if (!--maxfire || prof_ticks(tsk) < t->expires.cpu) { - tsk->cputime_expires.prof_exp = t->expires.cpu; + if (!--maxfire || prof_ticks(tsk) < t->expires) { + tsk->cputime_expires.prof_exp = expires_to_cputime(t->expires); break; } t->firing = 1; @@ -961,8 +906,8 @@ static void check_thread_timers(struct task_struct *tsk, struct cpu_timer_list *t = list_first_entry(timers, struct cpu_timer_list, entry); - if (!--maxfire || virt_ticks(tsk) < t->expires.cpu) { - tsk->cputime_expires.virt_exp = t->expires.cpu; + if (!--maxfire || virt_ticks(tsk) < t->expires) { + tsk->cputime_expires.virt_exp = expires_to_cputime(t->expires); break; } t->firing = 1; @@ -976,8 +921,8 @@ static void check_thread_timers(struct task_struct *tsk, struct cpu_timer_list *t = list_first_entry(timers, struct cpu_timer_list, entry); - if (!--maxfire || tsk->se.sum_exec_runtime < t->expires.sched) { - tsk->cputime_expires.sched_exp = t->expires.sched; + if (!--maxfire || tsk->se.sum_exec_runtime < t->expires) { + tsk->cputime_expires.sched_exp = t->expires; break; } t->firing = 1; @@ -1030,7 +975,8 @@ static void stop_process_timers(struct signal_struct *sig) static u32 onecputick; static void check_cpu_itimer(struct task_struct *tsk, struct cpu_itimer *it, - cputime_t *expires, cputime_t cur_time, int signo) + unsigned long long *expires, + unsigned long long cur_time, int signo) { if (!it->expires) return; @@ -1068,7 +1014,7 @@ static void check_process_timers(struct task_struct *tsk, { int maxfire; struct signal_struct *const sig = tsk->signal; - cputime_t utime, ptime, virt_expires, prof_expires; + unsigned long long utime, ptime, virt_expires, prof_expires; unsigned long long sum_sched_runtime, sched_expires; struct list_head *timers = sig->cpu_timers; struct task_cputime cputime; @@ -1078,8 +1024,8 @@ static void check_process_timers(struct task_struct *tsk, * Collect the current process totals. */ thread_group_cputimer(tsk, &cputime); - utime = cputime.utime; - ptime = utime + cputime.stime; + utime = cputime_to_expires(cputime.utime); + ptime = utime + cputime_to_expires(cputime.stime); sum_sched_runtime = cputime.sum_exec_runtime; maxfire = 20; prof_expires = 0; @@ -1087,8 +1033,8 @@ static void check_process_timers(struct task_struct *tsk, struct cpu_timer_list *tl = list_first_entry(timers, struct cpu_timer_list, entry); - if (!--maxfire || ptime < tl->expires.cpu) { - prof_expires = tl->expires.cpu; + if (!--maxfire || ptime < tl->expires) { + prof_expires = tl->expires; break; } tl->firing = 1; @@ -1102,8 +1048,8 @@ static void check_process_timers(struct task_struct *tsk, struct cpu_timer_list *tl = list_first_entry(timers, struct cpu_timer_list, entry); - if (!--maxfire || utime < tl->expires.cpu) { - virt_expires = tl->expires.cpu; + if (!--maxfire || utime < tl->expires) { + virt_expires = tl->expires; break; } tl->firing = 1; @@ -1117,8 +1063,8 @@ static void check_process_timers(struct task_struct *tsk, struct cpu_timer_list *tl = list_first_entry(timers, struct cpu_timer_list, entry); - if (!--maxfire || sum_sched_runtime < tl->expires.sched) { - sched_expires = tl->expires.sched; + if (!--maxfire || sum_sched_runtime < tl->expires) { + sched_expires = tl->expires; break; } tl->firing = 1; @@ -1162,8 +1108,8 @@ static void check_process_timers(struct task_struct *tsk, } } - sig->cputime_expires.prof_exp = prof_expires; - sig->cputime_expires.virt_exp = virt_expires; + sig->cputime_expires.prof_exp = expires_to_cputime(prof_expires); + sig->cputime_expires.virt_exp = expires_to_cputime(virt_expires); sig->cputime_expires.sched_exp = sched_expires; if (task_cputime_zero(&sig->cputime_expires)) stop_process_timers(sig); @@ -1176,7 +1122,7 @@ static void check_process_timers(struct task_struct *tsk, void posix_cpu_timer_schedule(struct k_itimer *timer) { struct task_struct *p = timer->it.cpu.task; - union cpu_time_count now; + unsigned long long now; if (unlikely(p == NULL)) /* @@ -1205,7 +1151,7 @@ void posix_cpu_timer_schedule(struct k_itimer *timer) */ put_task_struct(p); timer->it.cpu.task = p = NULL; - timer->it.cpu.expires.sched = 0; + timer->it.cpu.expires = 0; goto out_unlock; } else if (unlikely(p->exit_state) && thread_group_empty(p)) { /* @@ -1387,7 +1333,7 @@ void run_posix_cpu_timers(struct task_struct *tsk) void set_process_cpu_timer(struct task_struct *tsk, unsigned int clock_idx, cputime_t *newval, cputime_t *oldval) { - union cpu_time_count now; + unsigned long long now; BUG_ON(clock_idx == CPUCLOCK_SCHED); cpu_timer_sample_group(clock_idx, tsk, &now); @@ -1399,17 +1345,17 @@ void set_process_cpu_timer(struct task_struct *tsk, unsigned int clock_idx, * it to be absolute. */ if (*oldval) { - if (*oldval <= now.cpu) { + if (*oldval <= now) { /* Just about to fire. */ *oldval = cputime_one_jiffy; } else { - *oldval -= now.cpu; + *oldval -= now; } } if (!*newval) goto out; - *newval += now.cpu; + *newval += now; } /* @@ -1459,7 +1405,7 @@ static int do_cpu_nanosleep(const clockid_t which_clock, int flags, } while (!signal_pending(current)) { - if (timer.it.cpu.expires.sched == 0) { + if (timer.it.cpu.expires == 0) { /* * Our timer fired and was reset, below * deletion can not fail. -- cgit v1.2.3 From 1a7fa510b38e518d11365883934f1afa41625424 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Fri, 28 Jun 2013 00:06:42 +0000 Subject: posix_cpu_timers: consolidate timer list cleanups Cleaning up the posix cpu timers on task exit shares some common code among timer list types, most notably the list traversal and expiry time update. Unify this in a common helper. Signed-off-by: Frederic Weisbecker Cc: Stanislaw Gruszka Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: Ingo Molnar Cc: Oleg Nesterov Cc: KOSAKI Motohiro Cc: Olivier Langlois Signed-off-by: Andrew Morton --- kernel/posix-cpu-timers.c | 48 +++++++++++++++++++---------------------------- 1 file changed, 19 insertions(+), 29 deletions(-) diff --git a/kernel/posix-cpu-timers.c b/kernel/posix-cpu-timers.c index c3c4ea1225a4..b1450cee6d6d 100644 --- a/kernel/posix-cpu-timers.c +++ b/kernel/posix-cpu-timers.c @@ -399,6 +399,21 @@ static int posix_cpu_timer_del(struct k_itimer *timer) return ret; } +static void cleanup_timers_list(struct list_head *head, + unsigned long long curr) +{ + struct cpu_timer_list *timer, *next; + + list_for_each_entry_safe(timer, next, head, entry) { + list_del_init(&timer->entry); + if (timer->expires < curr) { + timer->expires = 0; + } else { + timer->expires -= curr; + } + } +} + /* * Clean out CPU timers still ticking when a thread exited. The task * pointer is cleared, and the expiry time is replaced with the residual @@ -409,37 +424,12 @@ static void cleanup_timers(struct list_head *head, cputime_t utime, cputime_t stime, unsigned long long sum_exec_runtime) { - struct cpu_timer_list *timer, *next; - cputime_t ptime = utime + stime; - list_for_each_entry_safe(timer, next, head, entry) { - list_del_init(&timer->entry); - if (timer->expires < cputime_to_expires(ptime)) { - timer->expires = 0; - } else { - timer->expires -= cputime_to_expires(ptime); - } - } - - ++head; - list_for_each_entry_safe(timer, next, head, entry) { - list_del_init(&timer->entry); - if (timer->expires < cputime_to_expires(utime)) { - timer->expires = 0; - } else { - timer->expires -= cputime_to_expires(utime); - } - } + cputime_t ptime = utime + stime; - ++head; - list_for_each_entry_safe(timer, next, head, entry) { - list_del_init(&timer->entry); - if (timer->expires < sum_exec_runtime) { - timer->expires = 0; - } else { - timer->expires -= sum_exec_runtime; - } - } + cleanup_timers_list(head, cputime_to_expires(ptime)); + cleanup_timers_list(++head, cputime_to_expires(utime)); + cleanup_timers_list(++head, sum_exec_runtime); } /* -- cgit v1.2.3 From 2473f3e7a97ce8bc0fe7596cdb361b21221418eb Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Fri, 28 Jun 2013 00:06:43 +0000 Subject: posix_cpu_timers: consolidate expired timers check Consolidate the common code amongst per thread and per process timers list on tick time. List traversal, expiry check and subsequent updates can be shared in a common helper. Signed-off-by: Frederic Weisbecker Cc: Stanislaw Gruszka Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: Ingo Molnar Cc: Oleg Nesterov Cc: KOSAKI Motohiro Cc: Olivier Langlois Signed-off-by: Andrew Morton --- kernel/posix-cpu-timers.c | 118 +++++++++++++--------------------------------- 1 file changed, 33 insertions(+), 85 deletions(-) diff --git a/kernel/posix-cpu-timers.c b/kernel/posix-cpu-timers.c index b1450cee6d6d..92a4fbf44f86 100644 --- a/kernel/posix-cpu-timers.c +++ b/kernel/posix-cpu-timers.c @@ -862,6 +862,28 @@ static void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec *itp) } } +static unsigned long long +check_timers_list(struct list_head *timers, + struct list_head *firing, + unsigned long long curr) +{ + int maxfire = 20; + + while (!list_empty(timers)) { + struct cpu_timer_list *t; + + t = list_first_entry(timers, struct cpu_timer_list, entry); + + if (!--maxfire || curr < t->expires) + return t->expires; + + t->firing = 1; + list_move_tail(&t->entry, firing); + } + + return 0; +} + /* * Check for any per-thread CPU timers that have fired and move them off * the tsk->cpu_timers[N] list onto the firing list. Here we update the @@ -870,54 +892,20 @@ static void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec *itp) static void check_thread_timers(struct task_struct *tsk, struct list_head *firing) { - int maxfire; struct list_head *timers = tsk->cpu_timers; struct signal_struct *const sig = tsk->signal; + struct task_cputime *tsk_expires = &tsk->cputime_expires; + unsigned long long expires; unsigned long soft; - maxfire = 20; - tsk->cputime_expires.prof_exp = 0; - while (!list_empty(timers)) { - struct cpu_timer_list *t = list_first_entry(timers, - struct cpu_timer_list, - entry); - if (!--maxfire || prof_ticks(tsk) < t->expires) { - tsk->cputime_expires.prof_exp = expires_to_cputime(t->expires); - break; - } - t->firing = 1; - list_move_tail(&t->entry, firing); - } + expires = check_timers_list(timers, firing, prof_ticks(tsk)); + tsk_expires->prof_exp = expires_to_cputime(expires); - ++timers; - maxfire = 20; - tsk->cputime_expires.virt_exp = 0; - while (!list_empty(timers)) { - struct cpu_timer_list *t = list_first_entry(timers, - struct cpu_timer_list, - entry); - if (!--maxfire || virt_ticks(tsk) < t->expires) { - tsk->cputime_expires.virt_exp = expires_to_cputime(t->expires); - break; - } - t->firing = 1; - list_move_tail(&t->entry, firing); - } + expires = check_timers_list(++timers, firing, virt_ticks(tsk)); + tsk_expires->virt_exp = expires_to_cputime(expires); - ++timers; - maxfire = 20; - tsk->cputime_expires.sched_exp = 0; - while (!list_empty(timers)) { - struct cpu_timer_list *t = list_first_entry(timers, - struct cpu_timer_list, - entry); - if (!--maxfire || tsk->se.sum_exec_runtime < t->expires) { - tsk->cputime_expires.sched_exp = t->expires; - break; - } - t->firing = 1; - list_move_tail(&t->entry, firing); - } + tsk_expires->sched_exp = check_timers_list(++timers, firing, + tsk->se.sum_exec_runtime); /* * Check for the special case thread timers. @@ -1002,7 +990,6 @@ static void check_cpu_itimer(struct task_struct *tsk, struct cpu_itimer *it, static void check_process_timers(struct task_struct *tsk, struct list_head *firing) { - int maxfire; struct signal_struct *const sig = tsk->signal; unsigned long long utime, ptime, virt_expires, prof_expires; unsigned long long sum_sched_runtime, sched_expires; @@ -1017,49 +1004,10 @@ static void check_process_timers(struct task_struct *tsk, utime = cputime_to_expires(cputime.utime); ptime = utime + cputime_to_expires(cputime.stime); sum_sched_runtime = cputime.sum_exec_runtime; - maxfire = 20; - prof_expires = 0; - while (!list_empty(timers)) { - struct cpu_timer_list *tl = list_first_entry(timers, - struct cpu_timer_list, - entry); - if (!--maxfire || ptime < tl->expires) { - prof_expires = tl->expires; - break; - } - tl->firing = 1; - list_move_tail(&tl->entry, firing); - } - ++timers; - maxfire = 20; - virt_expires = 0; - while (!list_empty(timers)) { - struct cpu_timer_list *tl = list_first_entry(timers, - struct cpu_timer_list, - entry); - if (!--maxfire || utime < tl->expires) { - virt_expires = tl->expires; - break; - } - tl->firing = 1; - list_move_tail(&tl->entry, firing); - } - - ++timers; - maxfire = 20; - sched_expires = 0; - while (!list_empty(timers)) { - struct cpu_timer_list *tl = list_first_entry(timers, - struct cpu_timer_list, - entry); - if (!--maxfire || sum_sched_runtime < tl->expires) { - sched_expires = tl->expires; - break; - } - tl->firing = 1; - list_move_tail(&tl->entry, firing); - } + prof_expires = check_timers_list(timers, firing, ptime); + virt_expires = check_timers_list(++timers, firing, utime); + sched_expires = check_timers_list(++timers, firing, sum_sched_runtime); /* * Check for the special case process timers. -- cgit v1.2.3 From 0bc4b0cf15708fca04095232c4e448634e94d029 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Fri, 28 Jun 2013 00:06:43 +0000 Subject: selftests: add basic posix timers selftests Add some initial basic tests on a few posix timers interface such as setitimer() and timer_settime(). These simply check that expiration happens in a reasonable timeframe after expected elapsed clock time (user time, user + system time, real time, ...). This is helpful for finding basic breakages while hacking on this subsystem. Signed-off-by: Frederic Weisbecker Cc: Stanislaw Gruszka Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: Steven Rostedt Cc: KOSAKI Motohiro Cc: Olivier Langlois Signed-off-by: Andrew Morton --- tools/testing/selftests/Makefile | 1 + tools/testing/selftests/timers/Makefile | 8 + tools/testing/selftests/timers/posix_timers.c | 221 ++++++++++++++++++++++++++ 3 files changed, 230 insertions(+) create mode 100644 tools/testing/selftests/timers/Makefile create mode 100644 tools/testing/selftests/timers/posix_timers.c diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 0a63658065f0..4cb14cae3791 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -6,6 +6,7 @@ TARGETS += memory-hotplug TARGETS += mqueue TARGETS += net TARGETS += ptrace +TARGETS += timers TARGETS += vm all: diff --git a/tools/testing/selftests/timers/Makefile b/tools/testing/selftests/timers/Makefile new file mode 100644 index 000000000000..eb2859f4ad21 --- /dev/null +++ b/tools/testing/selftests/timers/Makefile @@ -0,0 +1,8 @@ +all: + gcc posix_timers.c -o posix_timers -lrt + +run_tests: all + ./posix_timers + +clean: + rm -f ./posix_timers diff --git a/tools/testing/selftests/timers/posix_timers.c b/tools/testing/selftests/timers/posix_timers.c new file mode 100644 index 000000000000..4fa655d68a81 --- /dev/null +++ b/tools/testing/selftests/timers/posix_timers.c @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2013 Red Hat, Inc., Frederic Weisbecker + * + * Licensed under the terms of the GNU GPL License version 2 + * + * Selftests for a few posix timers interface. + * + * Kernel loop code stolen from Steven Rostedt + */ + +#include +#include +#include +#include +#include +#include + +#define DELAY 2 +#define USECS_PER_SEC 1000000 + +static volatile int done; + +/* Busy loop in userspace to elapse ITIMER_VIRTUAL */ +static void user_loop(void) +{ + while (!done); +} + +/* + * Try to spend as much time as possible in kernelspace + * to elapse ITIMER_PROF. + */ +static void kernel_loop(void) +{ + void *addr = sbrk(0); + + while (!done) { + brk(addr + 4096); + brk(addr); + } +} + +/* + * Sleep until ITIMER_REAL expiration. + */ +static void idle_loop(void) +{ + pause(); +} + +static void sig_handler(int nr) +{ + done = 1; +} + +/* + * Check the expected timer expiration matches the GTOD elapsed delta since + * we armed the timer. Keep a 0.5 sec error margin due to various jitter. + */ +static int check_diff(struct timeval start, struct timeval end) +{ + long long diff; + + diff = end.tv_usec - start.tv_usec; + diff += (end.tv_sec - start.tv_sec) * USECS_PER_SEC; + + if (abs(diff - DELAY * USECS_PER_SEC) > USECS_PER_SEC / 2) { + printf("Diff too high: %lld..", diff); + return -1; + } + + return 0; +} + +static int check_itimer(int which) +{ + int err; + struct timeval start, end; + struct itimerval val = { + .it_value.tv_sec = DELAY, + }; + + printf("Check itimer "); + + if (which == ITIMER_VIRTUAL) + printf("virtual... "); + else if (which == ITIMER_PROF) + printf("prof... "); + else if (which == ITIMER_REAL) + printf("real... "); + + fflush(stdout); + + done = 0; + + if (which == ITIMER_VIRTUAL) + signal(SIGVTALRM, sig_handler); + else if (which == ITIMER_PROF) + signal(SIGPROF, sig_handler); + else if (which == ITIMER_REAL) + signal(SIGALRM, sig_handler); + + err = gettimeofday(&start, NULL); + if (err < 0) { + perror("Can't call gettimeofday()\n"); + return -1; + } + + err = setitimer(which, &val, NULL); + if (err < 0) { + perror("Can't set timer\n"); + return -1; + } + + if (which == ITIMER_VIRTUAL) + user_loop(); + else if (which == ITIMER_PROF) + kernel_loop(); + else if (which == ITIMER_REAL) + idle_loop(); + + gettimeofday(&end, NULL); + if (err < 0) { + perror("Can't call gettimeofday()\n"); + return -1; + } + + if (!check_diff(start, end)) + printf("[OK]\n"); + else + printf("[FAIL]\n"); + + return 0; +} + +static int check_timer_create(int which) +{ + int err; + timer_t id; + struct timeval start, end; + struct itimerspec val = { + .it_value.tv_sec = DELAY, + }; + + printf("Check timer_create() "); + if (which == CLOCK_THREAD_CPUTIME_ID) { + printf("per thread... "); + } else if (which == CLOCK_PROCESS_CPUTIME_ID) { + printf("per process... "); + } + fflush(stdout); + + done = 0; + timer_create(which, NULL, &id); + if (err < 0) { + perror("Can't create timer\n"); + return -1; + } + signal(SIGALRM, sig_handler); + + err = gettimeofday(&start, NULL); + if (err < 0) { + perror("Can't call gettimeofday()\n"); + return -1; + } + + err = timer_settime(id, 0, &val, NULL); + if (err < 0) { + perror("Can't set timer\n"); + return -1; + } + + user_loop(); + + gettimeofday(&end, NULL); + if (err < 0) { + perror("Can't call gettimeofday()\n"); + return -1; + } + + if (!check_diff(start, end)) + printf("[OK]\n"); + else + printf("[FAIL]\n"); + + return 0; +} + +int main(int argc, char **argv) +{ + int err; + + printf("Testing posix timers. False negative may happen on CPU execution \n"); + printf("based timers if other threads run on the CPU...\n"); + + if (check_itimer(ITIMER_VIRTUAL) < 0) + return -1; + + if (check_itimer(ITIMER_PROF) < 0) + return -1; + + if (check_itimer(ITIMER_REAL) < 0) + return -1; + + if (check_timer_create(CLOCK_THREAD_CPUTIME_ID) < 0) + return -1; + + /* + * It's unfortunately hard to reliably test a timer expiration + * on parallel multithread cputime. We could arm it to expire + * on DELAY * nr_threads, with nr_threads busy looping, then wait + * the normal DELAY since the time is elapsing nr_threads faster. + * But for that we need to ensure we have real physical free CPUs + * to ensure true parallelism. So test only one thread until we + * find a better solution. + */ + if (check_timer_create(CLOCK_PROCESS_CPUTIME_ID) < 0) + return -1; + + return 0; +} -- cgit v1.2.3 From 76cdcdd979ce00f5037804d73da583fb488ec1b2 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Fri, 28 Jun 2013 00:06:43 +0000 Subject: posix-timers: correctly get dying task time sample in posix_cpu_timer_schedule() In order to re-arm a timer after it fired, we take a sample of the current process or thread cputime. If the task is dying though, we don't arm anything but we cache the remaining timer expiration delta for further reads. Something similar is performed in posix_cpu_timer_get() but here we forget to take the process wide cputime sample before caching it. As a result we are storing random stack content, leading every further reads of that timer to return junk values. Fix this by taking the appropriate sample in the case of process wide timers. This probably doesn't matter much in practice because, at this stage, the thread is the last one in the group and we reached exit_notify(). This implies that we called exit_itimers() and there should be no more timers to handle for that task. So this is likely dead code anyway but let's fix the current logic and the warning that came along: kernel/posix-cpu-timers.c: In function 'posix_cpu_timer_schedule': kernel/posix-cpu-timers.c:1127: warning: 'now' may be used uninitialized in this function Then we can start to think further about cleaning up that code. Reported-by: Andrew Morton Reported-by: Chen Gang Signed-off-by: Frederic Weisbecker Cc: Stanislaw Gruszka Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: Ingo Molnar Cc: Oleg Nesterov Cc: Chen Gang Cc: KOSAKI Motohiro Cc: Olivier Langlois Signed-off-by: Andrew Morton --- kernel/posix-cpu-timers.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/posix-cpu-timers.c b/kernel/posix-cpu-timers.c index 92a4fbf44f86..4ebd8ad07c66 100644 --- a/kernel/posix-cpu-timers.c +++ b/kernel/posix-cpu-timers.c @@ -1097,6 +1097,7 @@ void posix_cpu_timer_schedule(struct k_itimer *timer) * not yet reaped. Take this opportunity to * drop our task ref. */ + cpu_timer_sample_group(timer->it_clock, p, &now); clear_dead_task(timer, now); goto out_unlock; } -- cgit v1.2.3 From a0b2062b0904ef07944c4a6e4d0f88ee44f1e9f2 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Fri, 28 Jun 2013 00:06:43 +0000 Subject: posix_timers: fix racy timer delta caching on task exit When a task exits, we perform a caching of the remaining cputime delta before expiring of its timers. This is done from the following places: * When the task is reaped. We iterate through its list of posix cpu timers and store the remaining timer delta to the timer struct instead of the absolute value. (See posix_cpu_timers_exit() / posix_cpu_timers_exit_group() ) * When we call posix_cpu_timer_get() or posix_cpu_timer_schedule(). If the timer's task is considered dying when watched from these places, the same conversion from absolute to relative expiry time is performed. Then the given task's reference is released. (See clear_dead_task() ). The relevance of this caching is questionable but this is another and deeper debate. The big issue here is that these two sources of caching don't mix up very well together. More specifically, the caching can easily be done twice, resulting in a wrong delta as it gets spuriously substracted a second time by the elapsed clock. This can happen in the following scenario: 1) The task exits and gets reaped: we call posix_cpu_timers_exit() and the absolute timer expiry values are converted to a relative delta. 2) timer_gettime() -> posix_cpu_timer_get() is called and relies on clear_dead_task() because tsk->exit_state == EXIT_DEAD. The delta gets substracted again by the elapsed clock and we return a wrong result. To fix this, just remove the caching done on task reaping time. It doesn't bring much value on its own. The caching done from posix_cpu_timer_get/schedule is enough. And it would also be hard to get it really right: we could make it put and clear the target task in the timer struct so that readers know if they are dealing with a relative cached of absolute value. But it would be racy. The only safe way to do it would be to lock the itimer->it_lock so that we know nobody reads the cputime expiry value while we modify it and its target task reference. Doing so would involve some funny workarounds to avoid circular lock against the sighand lock. There is just no reason to maintain this. The user visible effect of this patch can be observed by running the following code: it creates a subthread that launches a posix cputimer which expires after 10 seconds. But then the subthread only busy loops for 2 seconds and exits. The parent reaps the subthread and read the timer value. Its expected value should the be the initial timer's expiration value minus the cputime elapsed in the subthread. Roughly 10 - 2 = 8 seconds: #include #include #include #include #include static timer_t id; static struct itimerspec val = { .it_value.tv_sec = 10, }, new; static void *thread(void *unused) { int err; struct timeval start, end, diff; timer_create(CLOCK_THREAD_CPUTIME_ID, NULL, &id); if (err < 0) { perror("Can't create timer\n"); return NULL; } /* Arm 10 sec timer */ err = timer_settime(id, 0, &val, NULL); if (err < 0) { perror("Can't set timer\n"); return NULL; } /* Exit after 2 seconds of execution */ gettimeofday(&start, NULL); do { gettimeofday(&end, NULL); timersub(&end, &start, &diff); } while (diff.tv_sec < 2); return NULL; } int main(int argc, char **argv) { pthread_t pthread; int err; err = pthread_create(&pthread, NULL, thread, NULL); if (err) { perror("Can't create thread\n"); return -1; } pthread_join(pthread, NULL); /* Just wait a little bit to make sure the child got reaped */ sleep(1); err = timer_gettime(id, &new); if (err) perror("Can't get timer value\n"); printf("%d %ld\n", new.it_value.tv_sec, new.it_value.tv_nsec); return 0; } Before the patch: $ ./posix_cpu_timers 6 2278074 After the patch: $ ./posix_cpu_timers 8 1158766 Before the patch, the elapsed time got two more seconds spuriously accounted. Signed-off-by: Frederic Weisbecker Cc: Stanislaw Gruszka Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: Ingo Molnar Cc: Oleg Nesterov Cc: KOSAKI Motohiro Cc: Olivier Langlois Signed-off-by: Andrew Morton --- kernel/posix-cpu-timers.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/kernel/posix-cpu-timers.c b/kernel/posix-cpu-timers.c index 4ebd8ad07c66..c7f31aa272f7 100644 --- a/kernel/posix-cpu-timers.c +++ b/kernel/posix-cpu-timers.c @@ -404,14 +404,8 @@ static void cleanup_timers_list(struct list_head *head, { struct cpu_timer_list *timer, *next; - list_for_each_entry_safe(timer, next, head, entry) { + list_for_each_entry_safe(timer, next, head, entry) list_del_init(&timer->entry); - if (timer->expires < curr) { - timer->expires = 0; - } else { - timer->expires -= curr; - } - } } /* @@ -459,15 +453,21 @@ void posix_cpu_timers_exit_group(struct task_struct *tsk) tsk->se.sum_exec_runtime + sig->sum_sched_runtime); } -static void clear_dead_task(struct k_itimer *timer, unsigned long long now) +static void clear_dead_task(struct k_itimer *itimer, unsigned long long now) { + struct cpu_timer_list *timer = &itimer->it.cpu; + /* * That's all for this thread or process. * We leave our residual in expires to be reported. */ - put_task_struct(timer->it.cpu.task); - timer->it.cpu.task = NULL; - timer->it.cpu.expires -= now; + put_task_struct(timer->task); + timer->task = NULL; + if (timer->expires < now) { + timer->expires = 0; + } else { + timer->expires -= now; + } } static inline int expires_gt(cputime_t expires, cputime_t new_exp) -- cgit v1.2.3 From 3685c18e17f12438d0a83331c1b6a5b00fade7a1 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 2 Jul 2013 18:10:09 +0200 Subject: HID: kye: Add report fixup for Genius Gila Gaming mouse Genius Gila Gaming Mouse presents an obviously wrong report descriptor. the Consumer control (report ID 3) is the following: 0x05, 0x0c, // Usage Page (Consumer Devices) 105 0x09, 0x01, // Usage (Consumer Control) 107 0xa1, 0x01, // Collection (Application) 109 0x85, 0x03, // Report ID (3) 111 0x19, 0x00, // Usage Minimum (0) 113 0x2a, 0xff, 0x7f, // Usage Maximum (32767) 115 0x15, 0x00, // Logical Minimum (0) 118 0x26, 0xff, 0x7f, // Logical Maximum (32767) 120 0x75, 0x10, // Report Size (16) 123 0x95, 0x03, // Report Count (3) 125 0x81, 0x00, // Input (Data,Arr,Abs) 127 0x75, 0x08, // Report Size (8) 129 0x95, 0x01, // Report Count (1) 131 0x81, 0x01, // Input (Cnst,Arr,Abs) 133 0xc0, // End Collection 135 So the first input whithin this report has a count of 3 but a usage range of 32768. So this value is obviously wrong as it should not be greater than the report count. Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=959721 Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 1 + drivers/hid/hid-kye.c | 21 +++++++++++++++++++++ 3 files changed, 23 insertions(+) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 8f616bd81eee..27aa7c76b93d 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1589,6 +1589,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ION, USB_DEVICE_ID_ICADE) }, { HID_USB_DEVICE(USB_VENDOR_ID_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) }, { HID_USB_DEVICE(USB_VENDOR_ID_KEYTOUCH, USB_DEVICE_ID_KEYTOUCH_IEC) }, + { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_GENIUS_GILA_GAMING_MOUSE) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_I405X) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 3da75dd4c323..b2b692ea3457 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -474,6 +474,7 @@ #define USB_VENDOR_ID_KYE 0x0458 #define USB_DEVICE_ID_KYE_ERGO_525V 0x0087 +#define USB_DEVICE_ID_GENIUS_GILA_GAMING_MOUSE 0x0138 #define USB_DEVICE_ID_KYE_GPEN_560 0x5003 #define USB_DEVICE_ID_KYE_EASYPEN_I405X 0x5010 #define USB_DEVICE_ID_KYE_MOUSEPEN_I608X 0x5011 diff --git a/drivers/hid/hid-kye.c b/drivers/hid/hid-kye.c index 6af90dbdc3d4..1e2ee2aa84a0 100644 --- a/drivers/hid/hid-kye.c +++ b/drivers/hid/hid-kye.c @@ -314,6 +314,25 @@ static __u8 *kye_report_fixup(struct hid_device *hdev, __u8 *rdesc, *rsize = sizeof(easypen_m610x_rdesc_fixed); } break; + case USB_DEVICE_ID_GENIUS_GILA_GAMING_MOUSE: + /* + * the fixup that need to be done: + * - change Usage Maximum in the Comsumer Control + * (report ID 3) to a reasonable value + */ + if (*rsize >= 135 && + /* Usage Page (Consumer Devices) */ + rdesc[104] == 0x05 && rdesc[105] == 0x0c && + /* Usage (Consumer Control) */ + rdesc[106] == 0x09 && rdesc[107] == 0x01 && + /* Usage Maximum > 12287 */ + rdesc[114] == 0x2a && rdesc[116] > 0x2f) { + hid_info(hdev, + "fixing up Genius Gila Gaming Mouse " + "report descriptor\n"); + rdesc[116] = 0x2f; + } + break; } return rdesc; } @@ -407,6 +426,8 @@ static const struct hid_device_id kye_devices[] = { USB_DEVICE_ID_KYE_MOUSEPEN_I608X) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X) }, + { HID_USB_DEVICE(USB_VENDOR_ID_KYE, + USB_DEVICE_ID_GENIUS_GILA_GAMING_MOUSE) }, { } }; MODULE_DEVICE_TABLE(hid, kye_devices); -- cgit v1.2.3 From 90068348b7566c83efbf1985d7853a831aabfaf4 Mon Sep 17 00:00:00 2001 From: Yadwinder Singh Brar Date: Mon, 24 Jun 2013 16:50:55 +0530 Subject: regulator: s2mps11: Convert ramp rate to uV/us and set default ramp rate This patch makes driver to use uV/us as units of ramp_delay. It makes driver in compliance with regulator framework and make ramp rate precise. This patch also sets default ramp rate in regulator descriptor which can be used in case if case ramp rate is not set in regulator constraints. Signed-off-by: Yadwinder Singh Brar Signed-off-by: Mark Brown --- drivers/regulator/s2mps11.c | 7 ++++++- include/linux/mfd/samsung/s2mps11.h | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c index c9f16e17920f..2f62564ca936 100644 --- a/drivers/regulator/s2mps11.c +++ b/drivers/regulator/s2mps11.c @@ -42,7 +42,7 @@ static int get_ramp_delay(int ramp_delay) { unsigned char cnt = 0; - ramp_delay /= 6; + ramp_delay /= 6250; while (true) { ramp_delay = ramp_delay >> 1; @@ -113,6 +113,7 @@ static struct regulator_ops s2mps11_buck_ops = { .min_uV = S2MPS11_BUCK_MIN1, \ .uV_step = S2MPS11_BUCK_STEP1, \ .n_voltages = S2MPS11_BUCK_N_VOLTAGES, \ + .ramp_delay = S2MPS11_RAMP_DELAY, \ .vsel_reg = S2MPS11_REG_B1CTRL2 + (num - 1) * 2, \ .vsel_mask = S2MPS11_BUCK_VSEL_MASK, \ .enable_reg = S2MPS11_REG_B1CTRL1 + (num - 1) * 2, \ @@ -128,6 +129,7 @@ static struct regulator_ops s2mps11_buck_ops = { .min_uV = S2MPS11_BUCK_MIN1, \ .uV_step = S2MPS11_BUCK_STEP1, \ .n_voltages = S2MPS11_BUCK_N_VOLTAGES, \ + .ramp_delay = S2MPS11_RAMP_DELAY, \ .vsel_reg = S2MPS11_REG_B5CTRL2, \ .vsel_mask = S2MPS11_BUCK_VSEL_MASK, \ .enable_reg = S2MPS11_REG_B5CTRL1, \ @@ -143,6 +145,7 @@ static struct regulator_ops s2mps11_buck_ops = { .min_uV = S2MPS11_BUCK_MIN1, \ .uV_step = S2MPS11_BUCK_STEP1, \ .n_voltages = S2MPS11_BUCK_N_VOLTAGES, \ + .ramp_delay = S2MPS11_RAMP_DELAY, \ .vsel_reg = S2MPS11_REG_B6CTRL2 + (num - 6) * 2, \ .vsel_mask = S2MPS11_BUCK_VSEL_MASK, \ .enable_reg = S2MPS11_REG_B6CTRL1 + (num - 6) * 2, \ @@ -158,6 +161,7 @@ static struct regulator_ops s2mps11_buck_ops = { .min_uV = S2MPS11_BUCK_MIN3, \ .uV_step = S2MPS11_BUCK_STEP3, \ .n_voltages = S2MPS11_BUCK_N_VOLTAGES, \ + .ramp_delay = S2MPS11_RAMP_DELAY, \ .vsel_reg = S2MPS11_REG_B9CTRL2, \ .vsel_mask = S2MPS11_BUCK_VSEL_MASK, \ .enable_reg = S2MPS11_REG_B9CTRL1, \ @@ -173,6 +177,7 @@ static struct regulator_ops s2mps11_buck_ops = { .min_uV = S2MPS11_BUCK_MIN2, \ .uV_step = S2MPS11_BUCK_STEP2, \ .n_voltages = S2MPS11_BUCK_N_VOLTAGES, \ + .ramp_delay = S2MPS11_RAMP_DELAY, \ .vsel_reg = S2MPS11_REG_B10CTRL2, \ .vsel_mask = S2MPS11_BUCK_VSEL_MASK, \ .enable_reg = S2MPS11_REG_B10CTRL1, \ diff --git a/include/linux/mfd/samsung/s2mps11.h b/include/linux/mfd/samsung/s2mps11.h index ad2252f239d7..4e94dc65f987 100644 --- a/include/linux/mfd/samsung/s2mps11.h +++ b/include/linux/mfd/samsung/s2mps11.h @@ -189,6 +189,7 @@ enum s2mps11_regulators { #define S2MPS11_ENABLE_SHIFT 0x06 #define S2MPS11_LDO_N_VOLTAGES (S2MPS11_LDO_VSEL_MASK + 1) #define S2MPS11_BUCK_N_VOLTAGES (S2MPS11_BUCK_VSEL_MASK + 1) +#define S2MPS11_RAMP_DELAY 25000 /* uV/us */ #define S2MPS11_PMIC_EN_SHIFT 6 #define S2MPS11_REGULATOR_MAX (S2MPS11_REG_MAX - 3) -- cgit v1.2.3 From 97191d734f6ac028e5e6dcd574378c1544a16c0b Mon Sep 17 00:00:00 2001 From: Vincent Stehlé Date: Tue, 2 Jul 2013 11:46:54 +0200 Subject: i2c-designware: use div_u64 to fix link MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes the following link error: drivers/built-in.o: In function `dw_i2c_probe': of_iommu.c:(.text+0x18c8f0): undefined reference to `__aeabi_uldivmod' make: *** [vmlinux] Error 1 Signed-off-by: Vincent Stehlé Tested-by: Kevin Hilman Reviewed-by: Christian Ruppert Acked-by: Arnd Bergmann Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-designware-platdrv.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index def79b5fd4c8..4c5fadabe49d 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -122,7 +122,8 @@ static int dw_i2c_probe(struct platform_device *pdev) of_property_read_u32(pdev->dev.of_node, "i2c-sda-hold-time-ns", &ht); - dev->sda_hold_time = ((u64)ic_clk * ht + 500000) / 1000000; + dev->sda_hold_time = div_u64((u64)ic_clk * ht + 500000, + 1000000); } dev->functionality = -- cgit v1.2.3 From 4d47dde47f7dd95042fa56283d948f50dd4b509c Mon Sep 17 00:00:00 2001 From: 张忠山 Date: Sun, 30 Jun 2013 17:09:28 +0800 Subject: kbuild: create directory for dir/file.o MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When add a obj with dir to obj-y, like this obj-y += dir/file.o The $(obj)/dir not created, this patch fix this. When try to add a file(which in a subdir) to my board's obj-y, the build progress crashed. For example, I use at91rm9200ek board, and in kernel dir run: mkdir objtree make O=objtree at91rm9200_defconfig mkdir arch/arm/mach-at91/dir touch arch/arm/mach-at91/dir/file.c and edit arch/arm/mach-at91/dir/file.c to add some code. then edit arch/arm/mach-at91/Makefile, change the following line: obj-$(CONFIG_MACH_AT91RM9200EK) += board-rm9200ek.o to: obj-$(CONFIG_MACH_AT91RM9200EK) += board-rm9200ek.o dir/file.o Now build it: make O=objtree Then the error appears: ... CC arch/arm/mach-at91/board-rm9200dk.o CC arch/arm/mach-at91/board-rm9200ek.o CC arch/arm/mach-at91/dir/file.o linux-2.6/arch/arm/mach-at91/dir/file.c:5: fatal error: opening dependency file arch/arm/mach-at91/dir/.file.o.d: No such file or directory Check the objtree: LANG=en ls objtree/arch/arm/mach-at91/dir ls: cannot access objtree/arch/arm/mach-at91/dir: No such file or directory It's apparently that the target dir not created for file.o Check kbuild source code. It seems that kbuild create dirs for that in $(obj-dirs). But if the dir need not to create a built-in.o, It should never in $(obj-dirs). So I make this patch to make sure It in $(obj-dirs) this bug caused by commit f5fb976520a53f45f8bbf2e851f16b3b5558d485 Signed-off-by: 张忠山 Signed-off-by: Michal Marek --- scripts/Makefile.lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index e5c81aa60778..a5c05ae95225 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -63,7 +63,7 @@ multi-objs := $(multi-objs-y) $(multi-objs-m) subdir-obj-y := $(filter %/built-in.o, $(obj-y)) # $(obj-dirs) is a list of directories that contain object files -obj-dirs := $(dir $(multi-objs) $(subdir-obj-y)) +obj-dirs := $(dir $(multi-objs) $(obj-y)) # Replace multi-part objects by their individual parts, look at local dir only real-objs-y := $(foreach m, $(filter-out $(subdir-obj-y), $(obj-y)), $(if $(strip $($(m:.o=-objs)) $($(m:.o=-y))),$($(m:.o=-objs)) $($(m:.o=-y)),$(m))) $(extra-y) -- cgit v1.2.3 From 93f14468491747d6d3efd0b3a42785b1d51a127a Mon Sep 17 00:00:00 2001 From: Nicolas Palix Date: Thu, 20 Jun 2013 13:10:56 +0200 Subject: Coccinelle: Update the options used to the new option scheme spatch has changed its option scheme. E.g., --no_show_diff is now --no-show-diff This patch updates: - scripts/coccicheck - Semantic patches under scripts/coccinelle/ Signed-off-by: Nicolas Palix Signed-off-by: Michal Marek --- scripts/coccicheck | 28 +++++++++++----------- .../coccinelle/api/alloc/drop_kmalloc_cast.cocci | 2 +- scripts/coccinelle/api/alloc/kzalloc-simple.cocci | 2 +- scripts/coccinelle/api/d_find_alias.cocci | 2 +- .../coccinelle/api/devm_request_and_ioremap.cocci | 2 +- scripts/coccinelle/api/kstrdup.cocci | 2 +- scripts/coccinelle/api/memdup.cocci | 2 +- scripts/coccinelle/api/memdup_user.cocci | 2 +- scripts/coccinelle/api/ptr_ret.cocci | 2 +- scripts/coccinelle/api/simple_open.cocci | 2 +- scripts/coccinelle/free/devm_free.cocci | 2 +- scripts/coccinelle/free/kfree.cocci | 2 +- scripts/coccinelle/free/kfreeaddr.cocci | 2 +- scripts/coccinelle/iterators/fen.cocci | 2 +- scripts/coccinelle/iterators/itnull.cocci | 2 +- .../coccinelle/iterators/list_entry_update.cocci | 2 +- scripts/coccinelle/iterators/use_after_iter.cocci | 2 +- scripts/coccinelle/locks/call_kern.cocci | 2 +- scripts/coccinelle/locks/double_lock.cocci | 2 +- scripts/coccinelle/locks/flags.cocci | 2 +- scripts/coccinelle/locks/mini_lock.cocci | 2 +- scripts/coccinelle/misc/boolinit.cocci | 2 +- scripts/coccinelle/misc/cstptr.cocci | 2 +- scripts/coccinelle/misc/doubleinit.cocci | 2 +- scripts/coccinelle/misc/ifaddr.cocci | 2 +- scripts/coccinelle/misc/ifcol.cocci | 2 +- scripts/coccinelle/misc/noderef.cocci | 2 +- scripts/coccinelle/misc/orplus.cocci | 2 +- scripts/coccinelle/misc/warn.cocci | 2 +- scripts/coccinelle/null/eno.cocci | 2 +- scripts/coccinelle/null/kmerr.cocci | 2 +- scripts/coccinelle/tests/doublebitand.cocci | 2 +- scripts/coccinelle/tests/doubletest.cocci | 2 +- scripts/coccinelle/tests/odd_ptr_err.cocci | 2 +- 34 files changed, 47 insertions(+), 47 deletions(-) diff --git a/scripts/coccicheck b/scripts/coccicheck index ad8e8ffd3c7e..d3757adb1260 100755 --- a/scripts/coccicheck +++ b/scripts/coccicheck @@ -20,7 +20,7 @@ else NPROC="$J" fi -FLAGS="$SPFLAGS -very_quiet" +FLAGS="$SPFLAGS --very-quiet" # spatch only allows include directories with the syntax "-I include" # while gcc also allows "-Iinclude" and "-include include" @@ -36,14 +36,14 @@ if [ "$C" = "1" -o "$C" = "2" ]; then else ONLINE=0 if [ "$KBUILD_EXTMOD" = "" ] ; then - OPTIONS="-dir $srctree $COCCIINCLUDE" + OPTIONS="--dir $srctree $COCCIINCLUDE" else - OPTIONS="-dir $KBUILD_EXTMOD $COCCIINCLUDE" + OPTIONS="--dir $KBUILD_EXTMOD $COCCIINCLUDE" fi fi if [ "$KBUILD_EXTMOD" != "" ] ; then - OPTIONS="-patch $srctree $OPTIONS" + OPTIONS="--patch $srctree $OPTIONS" fi if [ ! -x "$SPATCH" ]; then @@ -67,7 +67,7 @@ if [ "$MODE" = "chain" ] ; then echo 'All available modes will be tried (in that order): patch, report, context, org' fi elif [ "$MODE" = "report" -o "$MODE" = "org" ] ; then - FLAGS="$FLAGS -no_show_diff" + FLAGS="$FLAGS --no-show-diff" fi if [ "$ONLINE" = "0" ] ; then @@ -83,7 +83,7 @@ run_cmd() { echo "Running ($NPROC in parallel): $@" fi for i in $(seq 0 $(( NPROC - 1)) ); do - eval "$@ -max $NPROC -index $i &" + eval "$@ --max $NPROC --index $i &" SPATCH_PID[$i]=$! if [ $VERBOSE -eq 2 ] ; then echo "${SPATCH_PID[$i]} running" @@ -106,7 +106,7 @@ coccinelle () { OPT=`grep "Option" $COCCI | cut -d':' -f2` -# The option '-parse_cocci' can be used to syntactically check the SmPL files. +# The option '--parse-cocci' can be used to syntactically check the SmPL files. # # $SPATCH -D $MODE $FLAGS -parse_cocci $COCCI $OPT > /dev/null @@ -147,20 +147,20 @@ coccinelle () { if [ "$MODE" = "chain" ] ; then run_cmd $SPATCH -D patch \ - $FLAGS -sp_file $COCCI $OPT $OPTIONS || \ + $FLAGS --cocci-file $COCCI $OPT $OPTIONS || \ run_cmd $SPATCH -D report \ - $FLAGS -sp_file $COCCI $OPT $OPTIONS -no_show_diff || \ + $FLAGS --cocci-file $COCCI $OPT $OPTIONS --no-show-diff || \ run_cmd $SPATCH -D context \ - $FLAGS -sp_file $COCCI $OPT $OPTIONS || \ + $FLAGS --cocci-file $COCCI $OPT $OPTIONS || \ run_cmd $SPATCH -D org \ - $FLAGS -sp_file $COCCI $OPT $OPTIONS -no_show_diff || exit 1 + $FLAGS --cocci-file $COCCI $OPT $OPTIONS --no-show-diff || exit 1 elif [ "$MODE" = "rep+ctxt" ] ; then run_cmd $SPATCH -D report \ - $FLAGS -sp_file $COCCI $OPT $OPTIONS -no_show_diff && \ + $FLAGS --cocci-file $COCCI $OPT $OPTIONS --no-show-diff && \ run_cmd $SPATCH -D context \ - $FLAGS -sp_file $COCCI $OPT $OPTIONS || exit 1 + $FLAGS --cocci-file $COCCI $OPT $OPTIONS || exit 1 else - run_cmd $SPATCH -D $MODE $FLAGS -sp_file $COCCI $OPT $OPTIONS || exit 1 + run_cmd $SPATCH -D $MODE $FLAGS --cocci-file $COCCI $OPT $OPTIONS || exit 1 fi } diff --git a/scripts/coccinelle/api/alloc/drop_kmalloc_cast.cocci b/scripts/coccinelle/api/alloc/drop_kmalloc_cast.cocci index 7d4771d449c3..bd5d08b882ee 100644 --- a/scripts/coccinelle/api/alloc/drop_kmalloc_cast.cocci +++ b/scripts/coccinelle/api/alloc/drop_kmalloc_cast.cocci @@ -5,7 +5,7 @@ // Confidence: High // Copyright: 2009,2010 Nicolas Palix, DIKU. GPLv2. // URL: http://coccinelle.lip6.fr/ -// Options: -no_includes -include_headers +// Options: --no-includes --include-headers // // Keywords: kmalloc, kzalloc, kcalloc // Version min: < 2.6.12 kmalloc diff --git a/scripts/coccinelle/api/alloc/kzalloc-simple.cocci b/scripts/coccinelle/api/alloc/kzalloc-simple.cocci index 046b9b16f8f9..52c55e4fa67d 100644 --- a/scripts/coccinelle/api/alloc/kzalloc-simple.cocci +++ b/scripts/coccinelle/api/alloc/kzalloc-simple.cocci @@ -9,7 +9,7 @@ // Copyright: (C) 2009-2010 Julia Lawall, Nicolas Palix, DIKU. GPLv2. // Copyright: (C) 2009-2010 Gilles Muller, INRIA/LiP6. GPLv2. // URL: http://coccinelle.lip6.fr/rules/kzalloc.html -// Options: -no_includes -include_headers +// Options: --no-includes --include-headers // // Keywords: kmalloc, kzalloc // Version min: < 2.6.12 kmalloc diff --git a/scripts/coccinelle/api/d_find_alias.cocci b/scripts/coccinelle/api/d_find_alias.cocci index a9694a8d3e5a..9594c9f7eb8d 100644 --- a/scripts/coccinelle/api/d_find_alias.cocci +++ b/scripts/coccinelle/api/d_find_alias.cocci @@ -4,7 +4,7 @@ // // Confidence: Moderate // URL: http://coccinelle.lip6.fr/ -// Options: -include_headers +// Options: --include-headers virtual context virtual org diff --git a/scripts/coccinelle/api/devm_request_and_ioremap.cocci b/scripts/coccinelle/api/devm_request_and_ioremap.cocci index 46beb81406ab..562ec88b6352 100644 --- a/scripts/coccinelle/api/devm_request_and_ioremap.cocci +++ b/scripts/coccinelle/api/devm_request_and_ioremap.cocci @@ -10,7 +10,7 @@ // Copyright: (C) 2011 Gilles Muller, INRIA/LiP6. GPLv2. // URL: http://coccinelle.lip6.fr/ // Comments: -// Options: -no_includes -include_headers +// Options: --no-includes --include-headers virtual patch virtual org diff --git a/scripts/coccinelle/api/kstrdup.cocci b/scripts/coccinelle/api/kstrdup.cocci index 07a74b2c6196..09cba54ed0cf 100644 --- a/scripts/coccinelle/api/kstrdup.cocci +++ b/scripts/coccinelle/api/kstrdup.cocci @@ -6,7 +6,7 @@ // Copyright: (C) 2010-2012 Gilles Muller, INRIA/LiP6. GPLv2. // URL: http://coccinelle.lip6.fr/ // Comments: -// Options: -no_includes -include_headers +// Options: --no-includes --include-headers virtual patch virtual context diff --git a/scripts/coccinelle/api/memdup.cocci b/scripts/coccinelle/api/memdup.cocci index 4dceab6d54de..3d1aa71b7579 100644 --- a/scripts/coccinelle/api/memdup.cocci +++ b/scripts/coccinelle/api/memdup.cocci @@ -6,7 +6,7 @@ // Copyright: (C) 2010-2012 Gilles Muller, INRIA/LiP6. GPLv2. // URL: http://coccinelle.lip6.fr/ // Comments: -// Options: -no_includes -include_headers +// Options: --no-includes --include-headers virtual patch virtual context diff --git a/scripts/coccinelle/api/memdup_user.cocci b/scripts/coccinelle/api/memdup_user.cocci index 2b131a8a1306..c606231b0e46 100644 --- a/scripts/coccinelle/api/memdup_user.cocci +++ b/scripts/coccinelle/api/memdup_user.cocci @@ -7,7 +7,7 @@ // Copyright: (C) 2010-2012 Gilles Muller, INRIA/LiP6. GPLv2. // URL: http://coccinelle.lip6.fr/ // Comments: -// Options: -no_includes -include_headers +// Options: --no-includes --include-headers virtual patch virtual context diff --git a/scripts/coccinelle/api/ptr_ret.cocci b/scripts/coccinelle/api/ptr_ret.cocci index 15f076fdecbe..2274638d005b 100644 --- a/scripts/coccinelle/api/ptr_ret.cocci +++ b/scripts/coccinelle/api/ptr_ret.cocci @@ -5,7 +5,7 @@ // Copyright: (C) 2012 Julia Lawall, INRIA/LIP6. GPLv2. // Copyright: (C) 2012 Gilles Muller, INRIA/LiP6. GPLv2. // URL: http://coccinelle.lip6.fr/ -// Options: -no_includes -include_headers +// Options: --no-includes --include-headers // // Keywords: ERR_PTR, PTR_ERR, PTR_RET // Version min: 2.6.39 diff --git a/scripts/coccinelle/api/simple_open.cocci b/scripts/coccinelle/api/simple_open.cocci index 05962f7be155..b67e174f3d95 100644 --- a/scripts/coccinelle/api/simple_open.cocci +++ b/scripts/coccinelle/api/simple_open.cocci @@ -4,7 +4,7 @@ /// // Confidence: High // Comments: -// Options: -no_includes -include_headers +// Options: --no-includes --include-headers virtual patch virtual report diff --git a/scripts/coccinelle/free/devm_free.cocci b/scripts/coccinelle/free/devm_free.cocci index 0a1e36146d76..3d9349012bb3 100644 --- a/scripts/coccinelle/free/devm_free.cocci +++ b/scripts/coccinelle/free/devm_free.cocci @@ -18,7 +18,7 @@ // Copyright: (C) 2011 Gilles Muller, INRIA/LiP6. GPLv2. // URL: http://coccinelle.lip6.fr/ // Comments: -// Options: -no_includes -include_headers +// Options: --no-includes --include-headers virtual org virtual report diff --git a/scripts/coccinelle/free/kfree.cocci b/scripts/coccinelle/free/kfree.cocci index d9ae6d89c2f5..577b78056990 100644 --- a/scripts/coccinelle/free/kfree.cocci +++ b/scripts/coccinelle/free/kfree.cocci @@ -10,7 +10,7 @@ // Copyright: (C) 2010-2012 Gilles Muller, INRIA/LiP6. GPLv2. // URL: http://coccinelle.lip6.fr/ // Comments: -// Options: -no_includes -include_headers +// Options: --no-includes --include-headers virtual org virtual report diff --git a/scripts/coccinelle/free/kfreeaddr.cocci b/scripts/coccinelle/free/kfreeaddr.cocci index 662e997da834..ce8aacc314cb 100644 --- a/scripts/coccinelle/free/kfreeaddr.cocci +++ b/scripts/coccinelle/free/kfreeaddr.cocci @@ -4,7 +4,7 @@ // Copyright: (C) 2013 Julia Lawall, INRIA/LIP6. GPLv2. // URL: http://coccinelle.lip6.fr/ // Comments: -// Options: -no_includes -include_headers +// Options: --no-includes --include-headers virtual org virtual report diff --git a/scripts/coccinelle/iterators/fen.cocci b/scripts/coccinelle/iterators/fen.cocci index 0a40af828c43..48c152f224e1 100644 --- a/scripts/coccinelle/iterators/fen.cocci +++ b/scripts/coccinelle/iterators/fen.cocci @@ -7,7 +7,7 @@ // Copyright: (C) 2010-2012 Gilles Muller, INRIA/LiP6. GPLv2. // URL: http://coccinelle.lip6.fr/ // Comments: -// Options: -no_includes -include_headers +// Options: --no-includes --include-headers virtual patch virtual context diff --git a/scripts/coccinelle/iterators/itnull.cocci b/scripts/coccinelle/iterators/itnull.cocci index 259899f6838e..f58732b56a40 100644 --- a/scripts/coccinelle/iterators/itnull.cocci +++ b/scripts/coccinelle/iterators/itnull.cocci @@ -11,7 +11,7 @@ // Copyright: (C) 2010-2012 Gilles Muller, INRIA/LiP6. GPLv2. // URL: http://coccinelle.lip6.fr/ // Comments: -// Options: -no_includes -include_headers +// Options: --no-includes --include-headers virtual patch virtual context diff --git a/scripts/coccinelle/iterators/list_entry_update.cocci b/scripts/coccinelle/iterators/list_entry_update.cocci index b2967475679b..873f444e7137 100644 --- a/scripts/coccinelle/iterators/list_entry_update.cocci +++ b/scripts/coccinelle/iterators/list_entry_update.cocci @@ -9,7 +9,7 @@ // Copyright: (C) 2010 Gilles Muller, INRIA/LiP6. GPLv2. // URL: http://coccinelle.lip6.fr/ // Comments: -// Options: -no_includes -include_headers +// Options: --no-includes --include-headers virtual context virtual org diff --git a/scripts/coccinelle/iterators/use_after_iter.cocci b/scripts/coccinelle/iterators/use_after_iter.cocci index 06284c57a951..f085f5968c52 100644 --- a/scripts/coccinelle/iterators/use_after_iter.cocci +++ b/scripts/coccinelle/iterators/use_after_iter.cocci @@ -11,7 +11,7 @@ // Copyright: (C) 2012 Gilles Muller, INRIA/LIP6. GPLv2. // URL: http://coccinelle.lip6.fr/ // Comments: -// Options: -no_includes -include_headers +// Options: --no-includes --include-headers virtual context virtual org diff --git a/scripts/coccinelle/locks/call_kern.cocci b/scripts/coccinelle/locks/call_kern.cocci index 8f10b49603c3..669b24436248 100644 --- a/scripts/coccinelle/locks/call_kern.cocci +++ b/scripts/coccinelle/locks/call_kern.cocci @@ -9,7 +9,7 @@ // Copyright: (C) 2012 Gilles Muller, INRIA/LiP6. GPLv2. // URL: http://coccinelle.lip6.fr/ // Comments: -// Options: -no_includes -include_headers +// Options: --no-includes --include-headers virtual patch virtual context diff --git a/scripts/coccinelle/locks/double_lock.cocci b/scripts/coccinelle/locks/double_lock.cocci index 63b24e682fad..002752f97dca 100644 --- a/scripts/coccinelle/locks/double_lock.cocci +++ b/scripts/coccinelle/locks/double_lock.cocci @@ -8,7 +8,7 @@ // Copyright: (C) 2010 Gilles Muller, INRIA/LiP6. GPLv2. // URL: http://coccinelle.lip6.fr/ // Comments: -// Options: -no_includes -include_headers +// Options: --no-includes --include-headers virtual org virtual report diff --git a/scripts/coccinelle/locks/flags.cocci b/scripts/coccinelle/locks/flags.cocci index 1c4ffe6fd846..debd70e46267 100644 --- a/scripts/coccinelle/locks/flags.cocci +++ b/scripts/coccinelle/locks/flags.cocci @@ -6,7 +6,7 @@ // Copyright: (C) 2010-2012 Gilles Muller, INRIA/LiP6. GPLv2. // URL: http://coccinelle.lip6.fr/ // Comments: -// Options: -no_includes -include_headers +// Options: --no-includes --include-headers virtual context virtual org diff --git a/scripts/coccinelle/locks/mini_lock.cocci b/scripts/coccinelle/locks/mini_lock.cocci index 3267d7410bd5..47f649b0ea87 100644 --- a/scripts/coccinelle/locks/mini_lock.cocci +++ b/scripts/coccinelle/locks/mini_lock.cocci @@ -11,7 +11,7 @@ // Copyright: (C) 2010-2012 Gilles Muller, INRIA/LiP6. GPLv2. // URL: http://coccinelle.lip6.fr/ // Comments: -// Options: -no_includes -include_headers +// Options: --no-includes --include-headers virtual context virtual org diff --git a/scripts/coccinelle/misc/boolinit.cocci b/scripts/coccinelle/misc/boolinit.cocci index 97ce41ce8135..b9abed49cd95 100644 --- a/scripts/coccinelle/misc/boolinit.cocci +++ b/scripts/coccinelle/misc/boolinit.cocci @@ -6,7 +6,7 @@ // Copyright: (C) 2012 Julia Lawall, INRIA/LIP6. GPLv2. // Copyright: (C) 2012 Gilles Muller, INRIA/LiP6. GPLv2. // URL: http://coccinelle.lip6.fr/ -// Options: -include_headers +// Options: --include-headers virtual patch virtual context diff --git a/scripts/coccinelle/misc/cstptr.cocci b/scripts/coccinelle/misc/cstptr.cocci index d42564484528..f0368b3d4563 100644 --- a/scripts/coccinelle/misc/cstptr.cocci +++ b/scripts/coccinelle/misc/cstptr.cocci @@ -6,7 +6,7 @@ // Copyright: (C) 2012 Gilles Muller, INRIA/LiP6. GPLv2. // URL: http://coccinelle.lip6.fr/ // Comments: -// Options: -no_includes -include_headers +// Options: --no-includes --include-headers virtual org virtual report diff --git a/scripts/coccinelle/misc/doubleinit.cocci b/scripts/coccinelle/misc/doubleinit.cocci index cf74a00cf597..c0c3371d25e0 100644 --- a/scripts/coccinelle/misc/doubleinit.cocci +++ b/scripts/coccinelle/misc/doubleinit.cocci @@ -8,7 +8,7 @@ // Copyright: (C) 2010-2012 Gilles Muller, INRIA/LiP6. GPLv2. // URL: http://coccinelle.lip6.fr/ // Comments: requires at least Coccinelle 0.2.4, lex or parse error otherwise -// Options: -no_includes -include_headers +// Options: --no-includes --include-headers virtual org virtual report diff --git a/scripts/coccinelle/misc/ifaddr.cocci b/scripts/coccinelle/misc/ifaddr.cocci index 3e4089a77000..8aebd1875e75 100644 --- a/scripts/coccinelle/misc/ifaddr.cocci +++ b/scripts/coccinelle/misc/ifaddr.cocci @@ -6,7 +6,7 @@ // Copyright: (C) 2012 Gilles Muller, INRIA/LiP6. GPLv2. // URL: http://coccinelle.lip6.fr/ // Comments: -// Options: -no_includes -include_headers +// Options: --no-includes --include-headers virtual org virtual report diff --git a/scripts/coccinelle/misc/ifcol.cocci b/scripts/coccinelle/misc/ifcol.cocci index b7ed91dbeb95..d0d00ef1f12a 100644 --- a/scripts/coccinelle/misc/ifcol.cocci +++ b/scripts/coccinelle/misc/ifcol.cocci @@ -13,7 +13,7 @@ // Copyright: (C) 2010 Gilles Muller, INRIA/LiP6. GPLv2. // URL: http://coccinelle.lip6.fr/ // Comments: -// Options: -no_includes -include_headers +// Options: --no-includes --include-headers virtual org virtual report diff --git a/scripts/coccinelle/misc/noderef.cocci b/scripts/coccinelle/misc/noderef.cocci index c1707214e602..80a831c91161 100644 --- a/scripts/coccinelle/misc/noderef.cocci +++ b/scripts/coccinelle/misc/noderef.cocci @@ -6,7 +6,7 @@ // Copyright: (C) 2012 Gilles Muller, INRIA/LiP6. GPLv2. // URL: http://coccinelle.lip6.fr/ // Comments: -// Options: -no_includes -include_headers +// Options: --no-includes --include-headers virtual org virtual report diff --git a/scripts/coccinelle/misc/orplus.cocci b/scripts/coccinelle/misc/orplus.cocci index 4a28cef1484e..81fabf379390 100644 --- a/scripts/coccinelle/misc/orplus.cocci +++ b/scripts/coccinelle/misc/orplus.cocci @@ -7,7 +7,7 @@ // Copyright: (C) 2013 Gilles Muller, INRIA/LIP6. GPLv2. // URL: http://coccinelle.lip6.fr/ // Comments: -// Options: -no_includes -include_headers +// Options: --no-includes --include-headers virtual org virtual report diff --git a/scripts/coccinelle/misc/warn.cocci b/scripts/coccinelle/misc/warn.cocci index fda8c3558e4f..d2e5b6cedb84 100644 --- a/scripts/coccinelle/misc/warn.cocci +++ b/scripts/coccinelle/misc/warn.cocci @@ -5,7 +5,7 @@ // Copyright: (C) 2012 Gilles Muller, INRIA/LiP6. GPLv2. // URL: http://coccinelle.lip6.fr/ // Comments: -// Options: -no_includes -include_headers +// Options: --no-includes --include-headers virtual patch virtual context diff --git a/scripts/coccinelle/null/eno.cocci b/scripts/coccinelle/null/eno.cocci index ed961a1f7d11..9bd29aa83399 100644 --- a/scripts/coccinelle/null/eno.cocci +++ b/scripts/coccinelle/null/eno.cocci @@ -6,7 +6,7 @@ // Copyright: (C) 2010-2012 Gilles Muller, INRIA/LiP6. GPLv2. // URL: http://coccinelle.lip6.fr/ // Comments: -// Options: -no_includes -include_headers +// Options: --no-includes --include-headers virtual patch virtual context diff --git a/scripts/coccinelle/null/kmerr.cocci b/scripts/coccinelle/null/kmerr.cocci index 949bf656c64c..5354a7903ccb 100644 --- a/scripts/coccinelle/null/kmerr.cocci +++ b/scripts/coccinelle/null/kmerr.cocci @@ -10,7 +10,7 @@ // Copyright: (C) 2010 Gilles Muller, INRIA/LiP6. GPLv2. // URL: http://coccinelle.lip6.fr/ // Comments: -// Options: -no_includes -include_headers +// Options: --no-includes --include-headers virtual context virtual org diff --git a/scripts/coccinelle/tests/doublebitand.cocci b/scripts/coccinelle/tests/doublebitand.cocci index 9ba73d05a77e..72f1572aaec3 100644 --- a/scripts/coccinelle/tests/doublebitand.cocci +++ b/scripts/coccinelle/tests/doublebitand.cocci @@ -10,7 +10,7 @@ // Copyright: (C) 2010 Gilles Muller, INRIA/LiP6. GPLv2. // URL: http://coccinelle.lip6.fr/ // Comments: -// Options: -no_includes -include_headers +// Options: --no-includes --include-headers virtual context virtual org diff --git a/scripts/coccinelle/tests/doubletest.cocci b/scripts/coccinelle/tests/doubletest.cocci index 13a2c0e8a4bf..78d74c22ca12 100644 --- a/scripts/coccinelle/tests/doubletest.cocci +++ b/scripts/coccinelle/tests/doubletest.cocci @@ -8,7 +8,7 @@ // Copyright: (C) 2010 Gilles Muller, INRIA/LiP6. GPLv2. // URL: http://coccinelle.lip6.fr/ // Comments: -// Options: -no_includes -include_headers +// Options: --no-includes --include-headers virtual context virtual org diff --git a/scripts/coccinelle/tests/odd_ptr_err.cocci b/scripts/coccinelle/tests/odd_ptr_err.cocci index e8dd8a6b28a2..cfe0a35cf2dd 100644 --- a/scripts/coccinelle/tests/odd_ptr_err.cocci +++ b/scripts/coccinelle/tests/odd_ptr_err.cocci @@ -7,7 +7,7 @@ // Copyright: (C) 2012 Gilles Muller, INRIA. GPLv2. // URL: http://coccinelle.lip6.fr/ // Comments: -// Options: -no_includes -include_headers +// Options: --no-includes --include-headers virtual patch virtual context -- cgit v1.2.3 From ec97946ed038f4b3faa587bc76152b198805b0c4 Mon Sep 17 00:00:00 2001 From: Nicolas Palix Date: Wed, 3 Jul 2013 16:41:01 +0200 Subject: Coccinelle: Update information about the minimal version required The naming convention of options has changed one year ago. The options have been recently updated in the cocci file and in scripts/coccicheck. This patch also adds this information in the documentation. Signed-off-by: Nicolas Palix Signed-off-by: Michal Marek --- Documentation/coccinelle.txt | 19 ++++++++----------- scripts/coccicheck | 5 +++++ 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/Documentation/coccinelle.txt b/Documentation/coccinelle.txt index 81a329fcd532..7f773d51fdd9 100644 --- a/Documentation/coccinelle.txt +++ b/Documentation/coccinelle.txt @@ -6,15 +6,17 @@ Copyright 2010 Gilles Muller Getting Coccinelle ~~~~~~~~~~~~~~~~~~~~ -The semantic patches included in the kernel use the 'virtual rule' -feature which was introduced in Coccinelle version 0.1.11. +The semantic patches included in the kernel use features and options +which are provided by Coccinelle version 1.0.0-rc11 and above. +Using earlier versions will fail as the option names used by +the Coccinelle files and coccicheck have been updated. -Coccinelle (>=0.2.0) is available through the package manager +Coccinelle is available through the package manager of many distributions, e.g. : - - Debian (>=squeeze) - - Fedora (>=13) - - Ubuntu (>=10.04 Lucid Lynx) + - Debian + - Fedora + - Ubuntu - OpenSUSE - Arch Linux - NetBSD @@ -36,11 +38,6 @@ as a regular user, and install it with sudo make install -The semantic patches in the kernel will work best with Coccinelle version -0.2.4 or later. Using earlier versions may incur some parse errors in the -semantic patch code, but any results that are obtained should still be -correct. - Using Coccinelle on the Linux kernel ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/scripts/coccicheck b/scripts/coccicheck index d3757adb1260..bbf901afb606 100755 --- a/scripts/coccicheck +++ b/scripts/coccicheck @@ -1,5 +1,10 @@ #!/bin/bash +# +# This script requires at least spatch +# version 1.0.0-rc11. +# + SPATCH="`which ${SPATCH:=spatch}`" trap kill_running SIGTERM SIGINT -- cgit v1.2.3 From e631227f698f39969eb476d297f3ac65b43b51a5 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Wed, 3 Jul 2013 11:18:08 -0400 Subject: drm/radeon: fix endian bug in radeon_atom_get_mclk_range_table() Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/radeon_atombios.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index 70d8687e60e0..b1777d10d0b5 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -3639,7 +3639,7 @@ int radeon_atom_get_mclk_range_table(struct radeon_device *rdev, p = (u8 *)vram_module->asMemTiming; for (i = 0; i < mclk_range_table->num_entries; i++) { format = (ATOM_MEMORY_TIMING_FORMAT *)p; - mclk_range_table->mclk[i] = format->ulClkRange; + mclk_range_table->mclk[i] = le32_to_cpu(format->ulClkRange); p += mem_timing_size; } } else -- cgit v1.2.3 From 0124853eb1eda5e193e4753bd5d5ac77085027b2 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Wed, 3 Jul 2013 12:02:10 -0400 Subject: drm/radeon/aruba: disable additional rlc features They cause problems with dynamic clocking. Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/evergreen.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index 0de5b74f0287..2e1de4fd2975 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -4043,8 +4043,6 @@ static void evergreen_rlc_start(struct radeon_device *rdev) if (rdev->flags & RADEON_IS_IGP) { mask |= GFX_POWER_GATING_ENABLE | GFX_POWER_GATING_SRC; - if (rdev->family == CHIP_ARUBA) - mask |= DYN_PER_SIMD_PG_ENABLE | LB_CNT_SPIM_ACTIVE | LOAD_BALANCE_ENABLE; } WREG32(RLC_CNTL, mask); -- cgit v1.2.3 From 2b90eddcd7091dd631ead1d79e28e79ad589bb8d Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Wed, 3 Jul 2013 15:07:28 -0400 Subject: drm/radeon/sumo: disable PG when changing UVD clocks Causes hangs for some people. Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/sumo_dpm.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/radeon/sumo_dpm.c b/drivers/gpu/drm/radeon/sumo_dpm.c index bf187a5b3d58..b13448f13ee8 100644 --- a/drivers/gpu/drm/radeon/sumo_dpm.c +++ b/drivers/gpu/drm/radeon/sumo_dpm.c @@ -811,6 +811,23 @@ static void sumo_program_bootup_state(struct radeon_device *rdev) sumo_power_level_enable(rdev, i, false); } +static void sumo_setup_uvd_clocks(struct radeon_device *rdev, + struct radeon_ps *new_rps, + struct radeon_ps *old_rps) +{ + struct sumo_power_info *pi = sumo_get_pi(rdev); + + if (pi->enable_gfx_power_gating) { + sumo_gfx_powergating_enable(rdev, false); + } + + radeon_set_uvd_clocks(rdev, new_rps->vclk, new_rps->dclk); + + if (pi->enable_gfx_power_gating) { + sumo_gfx_powergating_enable(rdev, true); + } +} + static void sumo_set_uvd_clock_before_set_eng_clock(struct radeon_device *rdev, struct radeon_ps *new_rps, struct radeon_ps *old_rps) @@ -826,7 +843,7 @@ static void sumo_set_uvd_clock_before_set_eng_clock(struct radeon_device *rdev, current_ps->levels[current_ps->num_levels - 1].sclk) return; - radeon_set_uvd_clocks(rdev, new_rps->vclk, new_rps->dclk); + sumo_setup_uvd_clocks(rdev, new_rps, old_rps); } static void sumo_set_uvd_clock_after_set_eng_clock(struct radeon_device *rdev, @@ -844,7 +861,7 @@ static void sumo_set_uvd_clock_after_set_eng_clock(struct radeon_device *rdev, current_ps->levels[current_ps->num_levels - 1].sclk) return; - radeon_set_uvd_clocks(rdev, new_rps->vclk, new_rps->dclk); + sumo_setup_uvd_clocks(rdev, new_rps, old_rps); } void sumo_take_smu_control(struct radeon_device *rdev, bool enable) -- cgit v1.2.3 From 62fa44bf7b75e3e482655baa15309bf3ea122bd3 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Wed, 3 Jul 2013 15:01:45 -0400 Subject: drm/radeon/tn: disable PG when changing UVD clocks Causes hangs for some people. Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/trinity_dpm.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/gpu/drm/radeon/trinity_dpm.c b/drivers/gpu/drm/radeon/trinity_dpm.c index b02b5ad92121..8a32bcc6bbb5 100644 --- a/drivers/gpu/drm/radeon/trinity_dpm.c +++ b/drivers/gpu/drm/radeon/trinity_dpm.c @@ -921,6 +921,10 @@ static void trinity_setup_uvd_clocks(struct radeon_device *rdev, { struct trinity_power_info *pi = trinity_get_pi(rdev); + if (pi->enable_gfx_power_gating) { + trinity_gfx_powergating_enable(rdev, false); + } + if (pi->uvd_dpm) { if (trinity_uvd_clocks_zero(new_rps) && !trinity_uvd_clocks_zero(old_rps)) { @@ -946,6 +950,10 @@ static void trinity_setup_uvd_clocks(struct radeon_device *rdev, radeon_set_uvd_clocks(rdev, new_rps->vclk, new_rps->dclk); } + + if (pi->enable_gfx_power_gating) { + trinity_gfx_powergating_enable(rdev, true); + } } static void trinity_set_uvd_clock_before_set_eng_clock(struct radeon_device *rdev, -- cgit v1.2.3 From 338a95a95508537e23c82d59a2d87be6fde4b6ff Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Wed, 3 Jul 2013 15:14:25 -0400 Subject: drm/radeon/sumo: implement support for disable_gfx_power_gating_in_uvd flag Some asic revisions need to disable PG when UVD is active. Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/sumo_dpm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/radeon/sumo_dpm.c b/drivers/gpu/drm/radeon/sumo_dpm.c index b13448f13ee8..dc599060a9a4 100644 --- a/drivers/gpu/drm/radeon/sumo_dpm.c +++ b/drivers/gpu/drm/radeon/sumo_dpm.c @@ -824,7 +824,9 @@ static void sumo_setup_uvd_clocks(struct radeon_device *rdev, radeon_set_uvd_clocks(rdev, new_rps->vclk, new_rps->dclk); if (pi->enable_gfx_power_gating) { - sumo_gfx_powergating_enable(rdev, true); + if (!pi->disable_gfx_power_gating_in_uvd || + !r600_is_uvd_state(new_rps->class, new_rps->class2)) + sumo_gfx_powergating_enable(rdev, true); } } -- cgit v1.2.3 From c50cd357887acf9fd7af3a5d492911bd825555a2 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Mon, 1 Jul 2013 19:24:00 +0200 Subject: net: gre: move GSO functions to gre_offload Similarly to TCP/UDP offloading, move all related GRE functions to gre_offload.c to make things more explicit and similar to the rest of the code. Suggested-by: Eric Dumazet Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/net/gre.h | 4 + net/ipv4/Makefile | 1 + net/ipv4/gre.c | 514 ------------------------------------------------- net/ipv4/gre_demux.c | 414 +++++++++++++++++++++++++++++++++++++++ net/ipv4/gre_offload.c | 127 ++++++++++++ 5 files changed, 546 insertions(+), 514 deletions(-) delete mode 100644 net/ipv4/gre.c create mode 100644 net/ipv4/gre_demux.c create mode 100644 net/ipv4/gre_offload.c diff --git a/include/net/gre.h b/include/net/gre.h index a5a4ddf05300..57e4afdf7879 100644 --- a/include/net/gre.h +++ b/include/net/gre.h @@ -32,6 +32,10 @@ struct gre_cisco_protocol { int gre_cisco_register(struct gre_cisco_protocol *proto); int gre_cisco_unregister(struct gre_cisco_protocol *proto); + +int gre_offload_init(void); +void gre_offload_exit(void); + void gre_build_header(struct sk_buff *skb, const struct tnl_ptk_info *tpi, int hdr_len); struct sk_buff *gre_handle_offloads(struct sk_buff *skb, bool gre_csum); diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile index 86ded0bac9c7..4b81e91c80fe 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_PROC_FS) += proc.o obj-$(CONFIG_IP_MULTIPLE_TABLES) += fib_rules.o obj-$(CONFIG_IP_MROUTE) += ipmr.o obj-$(CONFIG_NET_IPIP) += ipip.o +gre-y := gre_demux.o gre_offload.o obj-$(CONFIG_NET_IPGRE_DEMUX) += gre.o obj-$(CONFIG_NET_IPGRE) += ip_gre.o obj-$(CONFIG_NET_IPVTI) += ip_vti.o diff --git a/net/ipv4/gre.c b/net/ipv4/gre.c deleted file mode 100644 index ba4803e609b5..000000000000 --- a/net/ipv4/gre.c +++ /dev/null @@ -1,514 +0,0 @@ -/* - * GRE over IPv4 demultiplexer driver - * - * Authors: Dmitry Kozlov (xeb@mail.ru) - * - * 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. - * - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -static const struct gre_protocol __rcu *gre_proto[GREPROTO_MAX] __read_mostly; -static struct gre_cisco_protocol __rcu *gre_cisco_proto_list[GRE_IP_PROTO_MAX]; - -int gre_add_protocol(const struct gre_protocol *proto, u8 version) -{ - if (version >= GREPROTO_MAX) - return -EINVAL; - - return (cmpxchg((const struct gre_protocol **)&gre_proto[version], NULL, proto) == NULL) ? - 0 : -EBUSY; -} -EXPORT_SYMBOL_GPL(gre_add_protocol); - -int gre_del_protocol(const struct gre_protocol *proto, u8 version) -{ - int ret; - - if (version >= GREPROTO_MAX) - return -EINVAL; - - ret = (cmpxchg((const struct gre_protocol **)&gre_proto[version], proto, NULL) == proto) ? - 0 : -EBUSY; - - if (ret) - return ret; - - synchronize_rcu(); - return 0; -} -EXPORT_SYMBOL_GPL(gre_del_protocol); - -void gre_build_header(struct sk_buff *skb, const struct tnl_ptk_info *tpi, - int hdr_len) -{ - struct gre_base_hdr *greh; - - skb_push(skb, hdr_len); - - greh = (struct gre_base_hdr *)skb->data; - greh->flags = tnl_flags_to_gre_flags(tpi->flags); - greh->protocol = tpi->proto; - - if (tpi->flags&(TUNNEL_KEY|TUNNEL_CSUM|TUNNEL_SEQ)) { - __be32 *ptr = (__be32 *)(((u8 *)greh) + hdr_len - 4); - - if (tpi->flags&TUNNEL_SEQ) { - *ptr = tpi->seq; - ptr--; - } - if (tpi->flags&TUNNEL_KEY) { - *ptr = tpi->key; - ptr--; - } - if (tpi->flags&TUNNEL_CSUM && - !(skb_shinfo(skb)->gso_type & SKB_GSO_GRE)) { - *ptr = 0; - *(__sum16 *)ptr = csum_fold(skb_checksum(skb, 0, - skb->len, 0)); - } - } -} -EXPORT_SYMBOL_GPL(gre_build_header); - -struct sk_buff *gre_handle_offloads(struct sk_buff *skb, bool gre_csum) -{ - int err; - - if (likely(!skb->encapsulation)) { - skb_reset_inner_headers(skb); - skb->encapsulation = 1; - } - - if (skb_is_gso(skb)) { - err = skb_unclone(skb, GFP_ATOMIC); - if (unlikely(err)) - goto error; - skb_shinfo(skb)->gso_type |= SKB_GSO_GRE; - return skb; - } else if (skb->ip_summed == CHECKSUM_PARTIAL && gre_csum) { - err = skb_checksum_help(skb); - if (unlikely(err)) - goto error; - } else if (skb->ip_summed != CHECKSUM_PARTIAL) - skb->ip_summed = CHECKSUM_NONE; - - return skb; -error: - kfree_skb(skb); - return ERR_PTR(err); -} -EXPORT_SYMBOL_GPL(gre_handle_offloads); - -static __sum16 check_checksum(struct sk_buff *skb) -{ - __sum16 csum = 0; - - switch (skb->ip_summed) { - case CHECKSUM_COMPLETE: - csum = csum_fold(skb->csum); - - if (!csum) - break; - /* Fall through. */ - - case CHECKSUM_NONE: - skb->csum = 0; - csum = __skb_checksum_complete(skb); - skb->ip_summed = CHECKSUM_COMPLETE; - break; - } - - return csum; -} - -static int parse_gre_header(struct sk_buff *skb, struct tnl_ptk_info *tpi, - bool *csum_err) -{ - unsigned int ip_hlen = ip_hdrlen(skb); - const struct gre_base_hdr *greh; - __be32 *options; - int hdr_len; - - if (unlikely(!pskb_may_pull(skb, sizeof(struct gre_base_hdr)))) - return -EINVAL; - - greh = (struct gre_base_hdr *)(skb_network_header(skb) + ip_hlen); - if (unlikely(greh->flags & (GRE_VERSION | GRE_ROUTING))) - return -EINVAL; - - tpi->flags = gre_flags_to_tnl_flags(greh->flags); - hdr_len = ip_gre_calc_hlen(tpi->flags); - - if (!pskb_may_pull(skb, hdr_len)) - return -EINVAL; - - greh = (struct gre_base_hdr *)(skb_network_header(skb) + ip_hlen); - tpi->proto = greh->protocol; - - options = (__be32 *)(greh + 1); - if (greh->flags & GRE_CSUM) { - if (check_checksum(skb)) { - *csum_err = true; - return -EINVAL; - } - options++; - } - - if (greh->flags & GRE_KEY) { - tpi->key = *options; - options++; - } else - tpi->key = 0; - - if (unlikely(greh->flags & GRE_SEQ)) { - tpi->seq = *options; - options++; - } else - tpi->seq = 0; - - /* WCCP version 1 and 2 protocol decoding. - * - Change protocol to IP - * - When dealing with WCCPv2, Skip extra 4 bytes in GRE header - */ - if (greh->flags == 0 && tpi->proto == htons(ETH_P_WCCP)) { - tpi->proto = htons(ETH_P_IP); - if ((*(u8 *)options & 0xF0) != 0x40) { - hdr_len += 4; - if (!pskb_may_pull(skb, hdr_len)) - return -EINVAL; - } - } - - return iptunnel_pull_header(skb, hdr_len, tpi->proto); -} - -static int gre_cisco_rcv(struct sk_buff *skb) -{ - struct tnl_ptk_info tpi; - int i; - bool csum_err = false; - - if (parse_gre_header(skb, &tpi, &csum_err) < 0) - goto drop; - - rcu_read_lock(); - for (i = 0; i < GRE_IP_PROTO_MAX; i++) { - struct gre_cisco_protocol *proto; - int ret; - - proto = rcu_dereference(gre_cisco_proto_list[i]); - if (!proto) - continue; - ret = proto->handler(skb, &tpi); - if (ret == PACKET_RCVD) { - rcu_read_unlock(); - return 0; - } - } - rcu_read_unlock(); - - icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); -drop: - kfree_skb(skb); - return 0; -} - -static void gre_cisco_err(struct sk_buff *skb, u32 info) -{ - /* All the routers (except for Linux) return only - * 8 bytes of packet payload. It means, that precise relaying of - * ICMP in the real Internet is absolutely infeasible. - * - * Moreover, Cisco "wise men" put GRE key to the third word - * in GRE header. It makes impossible maintaining even soft - * state for keyed - * GRE tunnels with enabled checksum. Tell them "thank you". - * - * Well, I wonder, rfc1812 was written by Cisco employee, - * what the hell these idiots break standards established - * by themselves??? - */ - - const int type = icmp_hdr(skb)->type; - const int code = icmp_hdr(skb)->code; - struct tnl_ptk_info tpi; - bool csum_err = false; - int i; - - if (parse_gre_header(skb, &tpi, &csum_err)) { - if (!csum_err) /* ignore csum errors. */ - return; - } - - if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) { - ipv4_update_pmtu(skb, dev_net(skb->dev), info, - skb->dev->ifindex, 0, IPPROTO_GRE, 0); - return; - } - if (type == ICMP_REDIRECT) { - ipv4_redirect(skb, dev_net(skb->dev), skb->dev->ifindex, 0, - IPPROTO_GRE, 0); - return; - } - - rcu_read_lock(); - for (i = 0; i < GRE_IP_PROTO_MAX; i++) { - struct gre_cisco_protocol *proto; - - proto = rcu_dereference(gre_cisco_proto_list[i]); - if (!proto) - continue; - - if (proto->err_handler(skb, info, &tpi) == PACKET_RCVD) - goto out; - - } -out: - rcu_read_unlock(); -} - -static int gre_rcv(struct sk_buff *skb) -{ - const struct gre_protocol *proto; - u8 ver; - int ret; - - if (!pskb_may_pull(skb, 12)) - goto drop; - - ver = skb->data[1]&0x7f; - if (ver >= GREPROTO_MAX) - goto drop; - - rcu_read_lock(); - proto = rcu_dereference(gre_proto[ver]); - if (!proto || !proto->handler) - goto drop_unlock; - ret = proto->handler(skb); - rcu_read_unlock(); - return ret; - -drop_unlock: - rcu_read_unlock(); -drop: - kfree_skb(skb); - return NET_RX_DROP; -} - -static void gre_err(struct sk_buff *skb, u32 info) -{ - const struct gre_protocol *proto; - const struct iphdr *iph = (const struct iphdr *)skb->data; - u8 ver = skb->data[(iph->ihl<<2) + 1]&0x7f; - - if (ver >= GREPROTO_MAX) - return; - - rcu_read_lock(); - proto = rcu_dereference(gre_proto[ver]); - if (proto && proto->err_handler) - proto->err_handler(skb, info); - rcu_read_unlock(); -} - -static struct sk_buff *gre_gso_segment(struct sk_buff *skb, - netdev_features_t features) -{ - struct sk_buff *segs = ERR_PTR(-EINVAL); - netdev_features_t enc_features; - int ghl = GRE_HEADER_SECTION; - struct gre_base_hdr *greh; - int mac_len = skb->mac_len; - __be16 protocol = skb->protocol; - int tnl_hlen; - bool csum; - - if (unlikely(skb_shinfo(skb)->gso_type & - ~(SKB_GSO_TCPV4 | - SKB_GSO_TCPV6 | - SKB_GSO_UDP | - SKB_GSO_DODGY | - SKB_GSO_TCP_ECN | - SKB_GSO_GRE))) - goto out; - - if (unlikely(!pskb_may_pull(skb, sizeof(*greh)))) - goto out; - - greh = (struct gre_base_hdr *)skb_transport_header(skb); - - if (greh->flags & GRE_KEY) - ghl += GRE_HEADER_SECTION; - if (greh->flags & GRE_SEQ) - ghl += GRE_HEADER_SECTION; - if (greh->flags & GRE_CSUM) { - ghl += GRE_HEADER_SECTION; - csum = true; - } else - csum = false; - - /* setup inner skb. */ - skb->protocol = greh->protocol; - skb->encapsulation = 0; - - if (unlikely(!pskb_may_pull(skb, ghl))) - goto out; - __skb_pull(skb, ghl); - skb_reset_mac_header(skb); - skb_set_network_header(skb, skb_inner_network_offset(skb)); - skb->mac_len = skb_inner_network_offset(skb); - - /* segment inner packet. */ - enc_features = skb->dev->hw_enc_features & netif_skb_features(skb); - segs = skb_mac_gso_segment(skb, enc_features); - if (!segs || IS_ERR(segs)) - goto out; - - skb = segs; - tnl_hlen = skb_tnl_header_len(skb); - do { - __skb_push(skb, ghl); - if (csum) { - __be32 *pcsum; - - if (skb_has_shared_frag(skb)) { - int err; - - err = __skb_linearize(skb); - if (err) { - kfree_skb(segs); - segs = ERR_PTR(err); - goto out; - } - } - - greh = (struct gre_base_hdr *)(skb->data); - pcsum = (__be32 *)(greh + 1); - *pcsum = 0; - *(__sum16 *)pcsum = csum_fold(skb_checksum(skb, 0, skb->len, 0)); - } - __skb_push(skb, tnl_hlen - ghl); - - skb_reset_mac_header(skb); - skb_set_network_header(skb, mac_len); - skb->mac_len = mac_len; - skb->protocol = protocol; - } while ((skb = skb->next)); -out: - return segs; -} - -static int gre_gso_send_check(struct sk_buff *skb) -{ - if (!skb->encapsulation) - return -EINVAL; - return 0; -} - -static const struct net_protocol net_gre_protocol = { - .handler = gre_rcv, - .err_handler = gre_err, - .netns_ok = 1, -}; - -static const struct net_offload gre_offload = { - .callbacks = { - .gso_send_check = gre_gso_send_check, - .gso_segment = gre_gso_segment, - }, -}; - -static const struct gre_protocol ipgre_protocol = { - .handler = gre_cisco_rcv, - .err_handler = gre_cisco_err, -}; - -int gre_cisco_register(struct gre_cisco_protocol *newp) -{ - struct gre_cisco_protocol **proto = (struct gre_cisco_protocol **) - &gre_cisco_proto_list[newp->priority]; - - return (cmpxchg(proto, NULL, newp) == NULL) ? 0 : -EBUSY; -} -EXPORT_SYMBOL_GPL(gre_cisco_register); - -int gre_cisco_unregister(struct gre_cisco_protocol *del_proto) -{ - struct gre_cisco_protocol **proto = (struct gre_cisco_protocol **) - &gre_cisco_proto_list[del_proto->priority]; - int ret; - - ret = (cmpxchg(proto, del_proto, NULL) == del_proto) ? 0 : -EINVAL; - - if (ret) - return ret; - - synchronize_net(); - return 0; -} -EXPORT_SYMBOL_GPL(gre_cisco_unregister); - -static int __init gre_init(void) -{ - pr_info("GRE over IPv4 demultiplexor driver\n"); - - if (inet_add_protocol(&net_gre_protocol, IPPROTO_GRE) < 0) { - pr_err("can't add protocol\n"); - goto err; - } - - if (gre_add_protocol(&ipgre_protocol, GREPROTO_CISCO) < 0) { - pr_info("%s: can't add ipgre handler\n", __func__); - goto err_gre; - } - - if (inet_add_offload(&gre_offload, IPPROTO_GRE)) { - pr_err("can't add protocol offload\n"); - goto err_gso; - } - - return 0; -err_gso: - gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO); -err_gre: - inet_del_protocol(&net_gre_protocol, IPPROTO_GRE); -err: - return -EAGAIN; -} - -static void __exit gre_exit(void) -{ - inet_del_offload(&gre_offload, IPPROTO_GRE); - gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO); - inet_del_protocol(&net_gre_protocol, IPPROTO_GRE); -} - -module_init(gre_init); -module_exit(gre_exit); - -MODULE_DESCRIPTION("GRE over IPv4 demultiplexer driver"); -MODULE_AUTHOR("D. Kozlov (xeb@mail.ru)"); -MODULE_LICENSE("GPL"); diff --git a/net/ipv4/gre_demux.c b/net/ipv4/gre_demux.c new file mode 100644 index 000000000000..736c9fc3ef93 --- /dev/null +++ b/net/ipv4/gre_demux.c @@ -0,0 +1,414 @@ +/* + * GRE over IPv4 demultiplexer driver + * + * Authors: Dmitry Kozlov (xeb@mail.ru) + * + * 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. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static const struct gre_protocol __rcu *gre_proto[GREPROTO_MAX] __read_mostly; +static struct gre_cisco_protocol __rcu *gre_cisco_proto_list[GRE_IP_PROTO_MAX]; + +int gre_add_protocol(const struct gre_protocol *proto, u8 version) +{ + if (version >= GREPROTO_MAX) + return -EINVAL; + + return (cmpxchg((const struct gre_protocol **)&gre_proto[version], NULL, proto) == NULL) ? + 0 : -EBUSY; +} +EXPORT_SYMBOL_GPL(gre_add_protocol); + +int gre_del_protocol(const struct gre_protocol *proto, u8 version) +{ + int ret; + + if (version >= GREPROTO_MAX) + return -EINVAL; + + ret = (cmpxchg((const struct gre_protocol **)&gre_proto[version], proto, NULL) == proto) ? + 0 : -EBUSY; + + if (ret) + return ret; + + synchronize_rcu(); + return 0; +} +EXPORT_SYMBOL_GPL(gre_del_protocol); + +void gre_build_header(struct sk_buff *skb, const struct tnl_ptk_info *tpi, + int hdr_len) +{ + struct gre_base_hdr *greh; + + skb_push(skb, hdr_len); + + greh = (struct gre_base_hdr *)skb->data; + greh->flags = tnl_flags_to_gre_flags(tpi->flags); + greh->protocol = tpi->proto; + + if (tpi->flags&(TUNNEL_KEY|TUNNEL_CSUM|TUNNEL_SEQ)) { + __be32 *ptr = (__be32 *)(((u8 *)greh) + hdr_len - 4); + + if (tpi->flags&TUNNEL_SEQ) { + *ptr = tpi->seq; + ptr--; + } + if (tpi->flags&TUNNEL_KEY) { + *ptr = tpi->key; + ptr--; + } + if (tpi->flags&TUNNEL_CSUM && + !(skb_shinfo(skb)->gso_type & SKB_GSO_GRE)) { + *ptr = 0; + *(__sum16 *)ptr = csum_fold(skb_checksum(skb, 0, + skb->len, 0)); + } + } +} +EXPORT_SYMBOL_GPL(gre_build_header); + +struct sk_buff *gre_handle_offloads(struct sk_buff *skb, bool gre_csum) +{ + int err; + + if (likely(!skb->encapsulation)) { + skb_reset_inner_headers(skb); + skb->encapsulation = 1; + } + + if (skb_is_gso(skb)) { + err = skb_unclone(skb, GFP_ATOMIC); + if (unlikely(err)) + goto error; + skb_shinfo(skb)->gso_type |= SKB_GSO_GRE; + return skb; + } else if (skb->ip_summed == CHECKSUM_PARTIAL && gre_csum) { + err = skb_checksum_help(skb); + if (unlikely(err)) + goto error; + } else if (skb->ip_summed != CHECKSUM_PARTIAL) + skb->ip_summed = CHECKSUM_NONE; + + return skb; +error: + kfree_skb(skb); + return ERR_PTR(err); +} +EXPORT_SYMBOL_GPL(gre_handle_offloads); + +static __sum16 check_checksum(struct sk_buff *skb) +{ + __sum16 csum = 0; + + switch (skb->ip_summed) { + case CHECKSUM_COMPLETE: + csum = csum_fold(skb->csum); + + if (!csum) + break; + /* Fall through. */ + + case CHECKSUM_NONE: + skb->csum = 0; + csum = __skb_checksum_complete(skb); + skb->ip_summed = CHECKSUM_COMPLETE; + break; + } + + return csum; +} + +static int parse_gre_header(struct sk_buff *skb, struct tnl_ptk_info *tpi, + bool *csum_err) +{ + unsigned int ip_hlen = ip_hdrlen(skb); + const struct gre_base_hdr *greh; + __be32 *options; + int hdr_len; + + if (unlikely(!pskb_may_pull(skb, sizeof(struct gre_base_hdr)))) + return -EINVAL; + + greh = (struct gre_base_hdr *)(skb_network_header(skb) + ip_hlen); + if (unlikely(greh->flags & (GRE_VERSION | GRE_ROUTING))) + return -EINVAL; + + tpi->flags = gre_flags_to_tnl_flags(greh->flags); + hdr_len = ip_gre_calc_hlen(tpi->flags); + + if (!pskb_may_pull(skb, hdr_len)) + return -EINVAL; + + greh = (struct gre_base_hdr *)(skb_network_header(skb) + ip_hlen); + tpi->proto = greh->protocol; + + options = (__be32 *)(greh + 1); + if (greh->flags & GRE_CSUM) { + if (check_checksum(skb)) { + *csum_err = true; + return -EINVAL; + } + options++; + } + + if (greh->flags & GRE_KEY) { + tpi->key = *options; + options++; + } else + tpi->key = 0; + + if (unlikely(greh->flags & GRE_SEQ)) { + tpi->seq = *options; + options++; + } else + tpi->seq = 0; + + /* WCCP version 1 and 2 protocol decoding. + * - Change protocol to IP + * - When dealing with WCCPv2, Skip extra 4 bytes in GRE header + */ + if (greh->flags == 0 && tpi->proto == htons(ETH_P_WCCP)) { + tpi->proto = htons(ETH_P_IP); + if ((*(u8 *)options & 0xF0) != 0x40) { + hdr_len += 4; + if (!pskb_may_pull(skb, hdr_len)) + return -EINVAL; + } + } + + return iptunnel_pull_header(skb, hdr_len, tpi->proto); +} + +static int gre_cisco_rcv(struct sk_buff *skb) +{ + struct tnl_ptk_info tpi; + int i; + bool csum_err = false; + + if (parse_gre_header(skb, &tpi, &csum_err) < 0) + goto drop; + + rcu_read_lock(); + for (i = 0; i < GRE_IP_PROTO_MAX; i++) { + struct gre_cisco_protocol *proto; + int ret; + + proto = rcu_dereference(gre_cisco_proto_list[i]); + if (!proto) + continue; + ret = proto->handler(skb, &tpi); + if (ret == PACKET_RCVD) { + rcu_read_unlock(); + return 0; + } + } + rcu_read_unlock(); + + icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); +drop: + kfree_skb(skb); + return 0; +} + +static void gre_cisco_err(struct sk_buff *skb, u32 info) +{ + /* All the routers (except for Linux) return only + * 8 bytes of packet payload. It means, that precise relaying of + * ICMP in the real Internet is absolutely infeasible. + * + * Moreover, Cisco "wise men" put GRE key to the third word + * in GRE header. It makes impossible maintaining even soft + * state for keyed + * GRE tunnels with enabled checksum. Tell them "thank you". + * + * Well, I wonder, rfc1812 was written by Cisco employee, + * what the hell these idiots break standards established + * by themselves??? + */ + + const int type = icmp_hdr(skb)->type; + const int code = icmp_hdr(skb)->code; + struct tnl_ptk_info tpi; + bool csum_err = false; + int i; + + if (parse_gre_header(skb, &tpi, &csum_err)) { + if (!csum_err) /* ignore csum errors. */ + return; + } + + if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) { + ipv4_update_pmtu(skb, dev_net(skb->dev), info, + skb->dev->ifindex, 0, IPPROTO_GRE, 0); + return; + } + if (type == ICMP_REDIRECT) { + ipv4_redirect(skb, dev_net(skb->dev), skb->dev->ifindex, 0, + IPPROTO_GRE, 0); + return; + } + + rcu_read_lock(); + for (i = 0; i < GRE_IP_PROTO_MAX; i++) { + struct gre_cisco_protocol *proto; + + proto = rcu_dereference(gre_cisco_proto_list[i]); + if (!proto) + continue; + + if (proto->err_handler(skb, info, &tpi) == PACKET_RCVD) + goto out; + + } +out: + rcu_read_unlock(); +} + +static int gre_rcv(struct sk_buff *skb) +{ + const struct gre_protocol *proto; + u8 ver; + int ret; + + if (!pskb_may_pull(skb, 12)) + goto drop; + + ver = skb->data[1]&0x7f; + if (ver >= GREPROTO_MAX) + goto drop; + + rcu_read_lock(); + proto = rcu_dereference(gre_proto[ver]); + if (!proto || !proto->handler) + goto drop_unlock; + ret = proto->handler(skb); + rcu_read_unlock(); + return ret; + +drop_unlock: + rcu_read_unlock(); +drop: + kfree_skb(skb); + return NET_RX_DROP; +} + +static void gre_err(struct sk_buff *skb, u32 info) +{ + const struct gre_protocol *proto; + const struct iphdr *iph = (const struct iphdr *)skb->data; + u8 ver = skb->data[(iph->ihl<<2) + 1]&0x7f; + + if (ver >= GREPROTO_MAX) + return; + + rcu_read_lock(); + proto = rcu_dereference(gre_proto[ver]); + if (proto && proto->err_handler) + proto->err_handler(skb, info); + rcu_read_unlock(); +} + +static const struct net_protocol net_gre_protocol = { + .handler = gre_rcv, + .err_handler = gre_err, + .netns_ok = 1, +}; + +static const struct gre_protocol ipgre_protocol = { + .handler = gre_cisco_rcv, + .err_handler = gre_cisco_err, +}; + +int gre_cisco_register(struct gre_cisco_protocol *newp) +{ + struct gre_cisco_protocol **proto = (struct gre_cisco_protocol **) + &gre_cisco_proto_list[newp->priority]; + + return (cmpxchg(proto, NULL, newp) == NULL) ? 0 : -EBUSY; +} +EXPORT_SYMBOL_GPL(gre_cisco_register); + +int gre_cisco_unregister(struct gre_cisco_protocol *del_proto) +{ + struct gre_cisco_protocol **proto = (struct gre_cisco_protocol **) + &gre_cisco_proto_list[del_proto->priority]; + int ret; + + ret = (cmpxchg(proto, del_proto, NULL) == del_proto) ? 0 : -EINVAL; + + if (ret) + return ret; + + synchronize_net(); + return 0; +} +EXPORT_SYMBOL_GPL(gre_cisco_unregister); + +static int __init gre_init(void) +{ + pr_info("GRE over IPv4 demultiplexor driver\n"); + + if (inet_add_protocol(&net_gre_protocol, IPPROTO_GRE) < 0) { + pr_err("can't add protocol\n"); + goto err; + } + + if (gre_add_protocol(&ipgre_protocol, GREPROTO_CISCO) < 0) { + pr_info("%s: can't add ipgre handler\n", __func__); + goto err_gre; + } + + if (gre_offload_init()) { + pr_err("can't add protocol offload\n"); + goto err_gso; + } + + return 0; +err_gso: + gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO); +err_gre: + inet_del_protocol(&net_gre_protocol, IPPROTO_GRE); +err: + return -EAGAIN; +} + +static void __exit gre_exit(void) +{ + gre_offload_exit(); + + gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO); + inet_del_protocol(&net_gre_protocol, IPPROTO_GRE); +} + +module_init(gre_init); +module_exit(gre_exit); + +MODULE_DESCRIPTION("GRE over IPv4 demultiplexer driver"); +MODULE_AUTHOR("D. Kozlov (xeb@mail.ru)"); +MODULE_LICENSE("GPL"); diff --git a/net/ipv4/gre_offload.c b/net/ipv4/gre_offload.c new file mode 100644 index 000000000000..a9d8cd2bff4b --- /dev/null +++ b/net/ipv4/gre_offload.c @@ -0,0 +1,127 @@ +/* + * IPV4 GSO/GRO offload support + * Linux INET implementation + * + * 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. + * + * GRE GSO support + */ + +#include +#include +#include + +static int gre_gso_send_check(struct sk_buff *skb) +{ + if (!skb->encapsulation) + return -EINVAL; + return 0; +} + +static struct sk_buff *gre_gso_segment(struct sk_buff *skb, + netdev_features_t features) +{ + struct sk_buff *segs = ERR_PTR(-EINVAL); + netdev_features_t enc_features; + int ghl = GRE_HEADER_SECTION; + struct gre_base_hdr *greh; + int mac_len = skb->mac_len; + __be16 protocol = skb->protocol; + int tnl_hlen; + bool csum; + + if (unlikely(skb_shinfo(skb)->gso_type & + ~(SKB_GSO_TCPV4 | + SKB_GSO_TCPV6 | + SKB_GSO_UDP | + SKB_GSO_DODGY | + SKB_GSO_TCP_ECN | + SKB_GSO_GRE))) + goto out; + + if (unlikely(!pskb_may_pull(skb, sizeof(*greh)))) + goto out; + + greh = (struct gre_base_hdr *)skb_transport_header(skb); + + if (greh->flags & GRE_KEY) + ghl += GRE_HEADER_SECTION; + if (greh->flags & GRE_SEQ) + ghl += GRE_HEADER_SECTION; + if (greh->flags & GRE_CSUM) { + ghl += GRE_HEADER_SECTION; + csum = true; + } else + csum = false; + + /* setup inner skb. */ + skb->protocol = greh->protocol; + skb->encapsulation = 0; + + if (unlikely(!pskb_may_pull(skb, ghl))) + goto out; + + __skb_pull(skb, ghl); + skb_reset_mac_header(skb); + skb_set_network_header(skb, skb_inner_network_offset(skb)); + skb->mac_len = skb_inner_network_offset(skb); + + /* segment inner packet. */ + enc_features = skb->dev->hw_enc_features & netif_skb_features(skb); + segs = skb_mac_gso_segment(skb, enc_features); + if (!segs || IS_ERR(segs)) + goto out; + + skb = segs; + tnl_hlen = skb_tnl_header_len(skb); + do { + __skb_push(skb, ghl); + if (csum) { + __be32 *pcsum; + + if (skb_has_shared_frag(skb)) { + int err; + + err = __skb_linearize(skb); + if (err) { + kfree_skb(segs); + segs = ERR_PTR(err); + goto out; + } + } + + greh = (struct gre_base_hdr *)(skb->data); + pcsum = (__be32 *)(greh + 1); + *pcsum = 0; + *(__sum16 *)pcsum = csum_fold(skb_checksum(skb, 0, skb->len, 0)); + } + __skb_push(skb, tnl_hlen - ghl); + + skb_reset_mac_header(skb); + skb_set_network_header(skb, mac_len); + skb->mac_len = mac_len; + skb->protocol = protocol; + } while ((skb = skb->next)); +out: + return segs; +} + +static const struct net_offload gre_offload = { + .callbacks = { + .gso_send_check = gre_gso_send_check, + .gso_segment = gre_gso_segment, + }, +}; + +int __init gre_offload_init(void) +{ + return inet_add_offload(&gre_offload, IPPROTO_GRE); +} + +void __exit gre_offload_exit(void) +{ + inet_del_offload(&gre_offload, IPPROTO_GRE); +} -- cgit v1.2.3 From 96e4dac66f69d28af2b736e723364efbbdf9fdee Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 22 May 2013 20:54:25 -0500 Subject: libceph: add lingering request reference when registered When an osd request is set to linger, the osd client holds onto the request so it can be re-submitted following certain osd map changes. The osd client holds a reference to the request until it is unregistered. This is used by rbd for watch requests. Currently, the reference is taken when the request is marked with the linger flag. This means that if an error occurs after that time but before the the request completes successfully, that reference is leaked. There's really no reason to take the reference until the request is registered in the the osd client's list of lingering requests, and that only happens when the lingering (watch) request completes successfully. So take that reference only when it gets registered following succesful completion, and drop it (as before) when the request gets unregistered. This avoids the reference problem on error in rbd. Rearrange ceph_osdc_unregister_linger_request() to avoid using the request pointer after it may have been freed. And hold an extra reference in kick_requests() while handling a linger request that has not yet been registered, to ensure it doesn't go away. This resolves: http://tracker.ceph.com/issues/3859 Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- net/ceph/osd_client.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 3a246a6cab47..e0abb83b520e 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -1174,6 +1174,7 @@ static void __register_linger_request(struct ceph_osd_client *osdc, struct ceph_osd_request *req) { dout("__register_linger_request %p\n", req); + ceph_osdc_get_request(req); list_add_tail(&req->r_linger_item, &osdc->req_linger); if (req->r_osd) list_add_tail(&req->r_linger_osd, @@ -1196,6 +1197,7 @@ static void __unregister_linger_request(struct ceph_osd_client *osdc, if (list_empty(&req->r_osd_item)) req->r_osd = NULL; } + ceph_osdc_put_request(req); } void ceph_osdc_unregister_linger_request(struct ceph_osd_client *osdc, @@ -1203,9 +1205,8 @@ void ceph_osdc_unregister_linger_request(struct ceph_osd_client *osdc, { mutex_lock(&osdc->request_mutex); if (req->r_linger) { - __unregister_linger_request(osdc, req); req->r_linger = 0; - ceph_osdc_put_request(req); + __unregister_linger_request(osdc, req); } mutex_unlock(&osdc->request_mutex); } @@ -1217,11 +1218,6 @@ void ceph_osdc_set_request_linger(struct ceph_osd_client *osdc, if (!req->r_linger) { dout("set_request_linger %p\n", req); req->r_linger = 1; - /* - * caller is now responsible for calling - * unregister_linger_request - */ - ceph_osdc_get_request(req); } } EXPORT_SYMBOL(ceph_osdc_set_request_linger); @@ -1633,8 +1629,10 @@ static void kick_requests(struct ceph_osd_client *osdc, int force_resend) dout("%p tid %llu restart on osd%d\n", req, req->r_tid, req->r_osd ? req->r_osd->o_osd : -1); + ceph_osdc_get_request(req); __unregister_request(osdc, req); __register_linger_request(osdc, req); + ceph_osdc_put_request(req); continue; } -- cgit v1.2.3 From e215605417b87732c6debf65da6d953016a1e5bc Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 22 May 2013 20:54:25 -0500 Subject: rbd: flush dcache after zeroing page data Neither zero_bio_chain() nor zero_pages() contains a call to flush caches after zeroing a portion of a page. This can cause problems on architectures that have caches that allow virtual address aliasing. This resolves: http://tracker.ceph.com/issues/4777 Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 291802c52c65..cb728a01a19f 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1126,6 +1126,7 @@ static void zero_bio_chain(struct bio *chain, int start_ofs) buf = bvec_kmap_irq(bv, &flags); memset(buf + remainder, 0, bv->bv_len - remainder); + flush_dcache_page(bv->bv_page); bvec_kunmap_irq(buf, &flags); } pos += bv->bv_len; @@ -1158,6 +1159,7 @@ static void zero_pages(struct page **pages, u64 offset, u64 end) local_irq_save(flags); kaddr = kmap_atomic(*page); memset(kaddr + page_offset, 0, length); + flush_dcache_page(*page); kunmap_atomic(kaddr); local_irq_restore(flags); -- cgit v1.2.3 From 3b5cf2a2f1746a253d56f54ffbb45170c90b1cbd Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 29 May 2013 11:18:59 -0500 Subject: rbd: clean up a few things in the refresh path This includes a few relatively small fixes I found while examining the code that refreshes image information. This resolves: http://tracker.ceph.com/issues/5040 Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 53 +++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index cb728a01a19f..d4902c52bf26 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -2860,7 +2860,7 @@ static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data) (unsigned int)opcode); ret = rbd_dev_refresh(rbd_dev); if (ret) - rbd_warn(rbd_dev, ": header refresh error (%d)\n", ret); + rbd_warn(rbd_dev, "header refresh error (%d)\n", ret); rbd_obj_notify_ack(rbd_dev, notify_id); } @@ -3340,8 +3340,8 @@ static int rbd_dev_refresh(struct rbd_device *rbd_dev) int ret; rbd_assert(rbd_image_format_valid(rbd_dev->image_format)); - mapping_size = rbd_dev->mapping.size; mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); + mapping_size = rbd_dev->mapping.size; if (rbd_dev->image_format == 1) ret = rbd_dev_v1_header_info(rbd_dev); else @@ -3814,6 +3814,7 @@ static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev) void *end; u64 pool_id; char *image_id; + u64 snap_id; u64 overlap; int ret; @@ -3873,24 +3874,56 @@ static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev) (unsigned long long)pool_id, U32_MAX); goto out_err; } - parent_spec->pool_id = pool_id; image_id = ceph_extract_encoded_string(&p, end, NULL, GFP_KERNEL); if (IS_ERR(image_id)) { ret = PTR_ERR(image_id); goto out_err; } - parent_spec->image_id = image_id; - ceph_decode_64_safe(&p, end, parent_spec->snap_id, out_err); + ceph_decode_64_safe(&p, end, snap_id, out_err); ceph_decode_64_safe(&p, end, overlap, out_err); - if (overlap) { - rbd_spec_put(rbd_dev->parent_spec); + /* + * The parent won't change (except when the clone is + * flattened, already handled that). So we only need to + * record the parent spec we have not already done so. + */ + if (!rbd_dev->parent_spec) { + parent_spec->pool_id = pool_id; + parent_spec->image_id = image_id; + parent_spec->snap_id = snap_id; rbd_dev->parent_spec = parent_spec; parent_spec = NULL; /* rbd_dev now owns this */ - rbd_dev->parent_overlap = overlap; - } else { - rbd_warn(rbd_dev, "ignoring parent of clone with overlap 0\n"); + } + + /* + * We always update the parent overlap. If it's zero we + * treat it specially. + */ + rbd_dev->parent_overlap = overlap; + smp_mb(); + if (!overlap) { + + /* A null parent_spec indicates it's the initial probe */ + + if (parent_spec) { + /* + * The overlap has become zero, so the clone + * must have been resized down to 0 at some + * point. Treat this the same as a flatten. + */ + rbd_dev_parent_put(rbd_dev); + pr_info("%s: clone image now standalone\n", + rbd_dev->disk->disk_name); + } else { + /* + * For the initial probe, if we find the + * overlap is zero we just pretend there was + * no parent image. + */ + rbd_warn(rbd_dev, "ignoring parent of " + "clone with overlap 0\n"); + } } out: ret = 0; -- cgit v1.2.3 From 08f75463c15e26e9d67a7c992ce7dd8964c6cbdd Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 29 May 2013 11:19:00 -0500 Subject: rbd: protect against duplicate client creation If more than one rbd image has the same ceph cluster configuration (same options, same set of monitors, same keys) they normally share a single rbd client. When an image is getting mapped, rbd looks to see if an existing client can be used, and creates a new one if not. The lookup and creation are not done under a common lock though, so mapping two images concurrently could lead to duplicate clients getting set up needlessly. This isn't a major problem, but it's wasteful and different from what's intended. This patch fixes that by using the control mutex to protect both the lookup and (if needed) creation of the client. It was previously used just when creating. This resolves: http://tracker.ceph.com/issues/3094 Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index d4902c52bf26..fd2795d1136a 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -520,7 +520,7 @@ static const struct block_device_operations rbd_bd_ops = { /* * Initialize an rbd client instance. Success or not, this function - * consumes ceph_opts. + * consumes ceph_opts. Caller holds ctl_mutex. */ static struct rbd_client *rbd_client_create(struct ceph_options *ceph_opts) { @@ -535,30 +535,25 @@ static struct rbd_client *rbd_client_create(struct ceph_options *ceph_opts) kref_init(&rbdc->kref); INIT_LIST_HEAD(&rbdc->node); - mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); - rbdc->client = ceph_create_client(ceph_opts, rbdc, 0, 0); if (IS_ERR(rbdc->client)) - goto out_mutex; + goto out_rbdc; ceph_opts = NULL; /* Now rbdc->client is responsible for ceph_opts */ ret = ceph_open_session(rbdc->client); if (ret < 0) - goto out_err; + goto out_client; spin_lock(&rbd_client_list_lock); list_add_tail(&rbdc->node, &rbd_client_list); spin_unlock(&rbd_client_list_lock); - mutex_unlock(&ctl_mutex); dout("%s: rbdc %p\n", __func__, rbdc); return rbdc; - -out_err: +out_client: ceph_destroy_client(rbdc->client); -out_mutex: - mutex_unlock(&ctl_mutex); +out_rbdc: kfree(rbdc); out_opt: if (ceph_opts) @@ -682,11 +677,13 @@ static struct rbd_client *rbd_get_client(struct ceph_options *ceph_opts) { struct rbd_client *rbdc; + mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); rbdc = rbd_client_find(ceph_opts); if (rbdc) /* using an existing client */ ceph_destroy_options(ceph_opts); else rbdc = rbd_client_create(ceph_opts); + mutex_unlock(&ctl_mutex); return rbdc; } -- cgit v1.2.3 From 4974341eb99861720d54db9337bf1fe78eb8b9d0 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 29 May 2013 11:19:00 -0500 Subject: libceph: print more info for short message header If an osd client response message arrives that has a front section that's too big for the buffer set aside to receive it, a warning gets reported and a new buffer is allocated. The warning says nothing about which connection had the problem. Add the peer type and number to what gets reported, to be a bit more informative. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- net/ceph/osd_client.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index e0abb83b520e..61147fe70181 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -2454,8 +2454,10 @@ static struct ceph_msg *get_reply(struct ceph_connection *con, ceph_msg_revoke_incoming(req->r_reply); if (front > req->r_reply->front.iov_len) { - pr_warning("get_reply front %d > preallocated %d\n", - front, (int)req->r_reply->front.iov_len); + pr_warning("get_reply front %d > preallocated %d (%u#%llu)\n", + front, (int)req->r_reply->front.iov_len, + (unsigned int)con->peer_name.type, + le64_to_cpu(con->peer_name.num)); m = ceph_msg_new(CEPH_MSG_OSD_OPREPLY, front, GFP_NOFS, false); if (!m) goto out; -- cgit v1.2.3 From 751cc0e3cfabdda87c4c21519253c6751e97a8d4 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 31 May 2013 15:17:01 -0500 Subject: rbd: set removing flag while holding list lock When unmapping a device, its id is supplied, and that is used to look up which rbd device should be unmapped. Looking up the device involves searching the rbd device list while holding a spinlock that protects access to that list. Currently all of this is done under protection of the control lock, but that protection is going away soon. To ensure the rbd_dev is still valid (still on the list) while setting its REMOVING flag, do so while still holding the list lock. To do so, get rid of __rbd_get_dev(), and open code what it did in the one place it was used. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 53 ++++++++++++++++++++++------------------------------- 1 file changed, 22 insertions(+), 31 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index fd2795d1136a..9eead4879c90 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -5090,23 +5090,6 @@ err_out_module: return (ssize_t)rc; } -static struct rbd_device *__rbd_get_dev(unsigned long dev_id) -{ - struct list_head *tmp; - struct rbd_device *rbd_dev; - - spin_lock(&rbd_dev_list_lock); - list_for_each(tmp, &rbd_dev_list) { - rbd_dev = list_entry(tmp, struct rbd_device, node); - if (rbd_dev->dev_id == dev_id) { - spin_unlock(&rbd_dev_list_lock); - return rbd_dev; - } - } - spin_unlock(&rbd_dev_list_lock); - return NULL; -} - static void rbd_dev_device_release(struct device *dev) { struct rbd_device *rbd_dev = dev_to_rbd_dev(dev); @@ -5151,7 +5134,8 @@ static ssize_t rbd_remove(struct bus_type *bus, size_t count) { struct rbd_device *rbd_dev = NULL; - int target_id; + struct list_head *tmp; + int dev_id; unsigned long ul; int ret; @@ -5160,26 +5144,33 @@ static ssize_t rbd_remove(struct bus_type *bus, return ret; /* convert to int; abort if we lost anything in the conversion */ - target_id = (int) ul; - if (target_id != ul) + dev_id = (int)ul; + if (dev_id != ul) return -EINVAL; mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); - rbd_dev = __rbd_get_dev(target_id); - if (!rbd_dev) { - ret = -ENOENT; - goto done; + ret = -ENOENT; + spin_lock(&rbd_dev_list_lock); + list_for_each(tmp, &rbd_dev_list) { + rbd_dev = list_entry(tmp, struct rbd_device, node); + if (rbd_dev->dev_id == dev_id) { + ret = 0; + break; + } } - - spin_lock_irq(&rbd_dev->lock); - if (rbd_dev->open_count) - ret = -EBUSY; - else - set_bit(RBD_DEV_FLAG_REMOVING, &rbd_dev->flags); - spin_unlock_irq(&rbd_dev->lock); + if (!ret) { + spin_lock_irq(&rbd_dev->lock); + if (rbd_dev->open_count) + ret = -EBUSY; + else + set_bit(RBD_DEV_FLAG_REMOVING, &rbd_dev->flags); + spin_unlock_irq(&rbd_dev->lock); + } + spin_unlock(&rbd_dev_list_lock); if (ret < 0) goto done; + rbd_bus_del_dev(rbd_dev); ret = rbd_dev_header_watch_sync(rbd_dev, false); if (ret) -- cgit v1.2.3 From 82a442d239695a242c4d584464c9606322cd02aa Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 31 May 2013 17:40:44 -0500 Subject: rbd: protect against concurrent unmaps Make sure two concurrent unmap operations on the same rbd device won't collide, by only proceeding with the removal and cleanup of a device if is not already underway. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 9eead4879c90..305c740778c6 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -5137,6 +5137,7 @@ static ssize_t rbd_remove(struct bus_type *bus, struct list_head *tmp; int dev_id; unsigned long ul; + bool already = false; int ret; ret = strict_strtoul(buf, 10, &ul); @@ -5164,11 +5165,12 @@ static ssize_t rbd_remove(struct bus_type *bus, if (rbd_dev->open_count) ret = -EBUSY; else - set_bit(RBD_DEV_FLAG_REMOVING, &rbd_dev->flags); + already = test_and_set_bit(RBD_DEV_FLAG_REMOVING, + &rbd_dev->flags); spin_unlock_irq(&rbd_dev->lock); } spin_unlock(&rbd_dev_list_lock); - if (ret < 0) + if (ret < 0 || already) goto done; rbd_bus_del_dev(rbd_dev); -- cgit v1.2.3 From 1ba0f1e7975ad07557f7a931522bdcd813ae35f6 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 31 May 2013 15:17:01 -0500 Subject: rbd: don't hold ctl_mutex to get/put device When an rbd device is first getting mapped, its device registration is protected the control mutex. There is no need to do that though, because the device has already been assigned an id that's guaranteed to be unique. An unmap of an rbd device won't proceed if the device has a non-zero open count or is already being unmapped. So there's no need to hold the control mutex in that case either. Finally, an rbd device can't be opened if it is being removed, and it won't go away if there is a non-zero open count. So here too there's no need to hold the control mutex while getting or putting a reference to an rbd device's Linux device structure. Drop the mutex calls in these cases. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 305c740778c6..d735eb13847d 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -489,10 +489,8 @@ static int rbd_open(struct block_device *bdev, fmode_t mode) if (removing) return -ENOENT; - mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); (void) get_device(&rbd_dev->dev); set_device_ro(bdev, rbd_dev->mapping.read_only); - mutex_unlock(&ctl_mutex); return 0; } @@ -507,9 +505,7 @@ static void rbd_release(struct gendisk *disk, fmode_t mode) spin_unlock_irq(&rbd_dev->lock); rbd_assert(open_count_before > 0); - mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); put_device(&rbd_dev->dev); - mutex_unlock(&ctl_mutex); } static const struct block_device_operations rbd_bd_ops = { @@ -4332,8 +4328,6 @@ static int rbd_bus_add_dev(struct rbd_device *rbd_dev) struct device *dev; int ret; - mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); - dev = &rbd_dev->dev; dev->bus = &rbd_bus_type; dev->type = &rbd_device_type; @@ -4342,8 +4336,6 @@ static int rbd_bus_add_dev(struct rbd_device *rbd_dev) dev_set_name(dev, "%d", rbd_dev->dev_id); ret = device_register(dev); - mutex_unlock(&ctl_mutex); - return ret; } @@ -5149,8 +5141,6 @@ static ssize_t rbd_remove(struct bus_type *bus, if (dev_id != ul) return -EINVAL; - mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); - ret = -ENOENT; spin_lock(&rbd_dev_list_lock); list_for_each(tmp, &rbd_dev_list) { @@ -5171,7 +5161,7 @@ static ssize_t rbd_remove(struct bus_type *bus, } spin_unlock(&rbd_dev_list_lock); if (ret < 0 || already) - goto done; + return ret; rbd_bus_del_dev(rbd_dev); ret = rbd_dev_header_watch_sync(rbd_dev, false); @@ -5179,11 +5169,8 @@ static ssize_t rbd_remove(struct bus_type *bus, rbd_warn(rbd_dev, "failed to cancel watch event (%d)\n", ret); rbd_dev_image_release(rbd_dev); module_put(THIS_MODULE); - ret = count; -done: - mutex_unlock(&ctl_mutex); - return ret; + return count; } /* -- cgit v1.2.3 From cfbf6377b696d88461eef6966bef9e6184111183 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 31 May 2013 17:40:45 -0500 Subject: rbd: use rwsem to protect header updates Updating an image header needs to be protected to ensure it's done consistently. However distinct headers can be updated concurrently without a problem. Instead of using the global control lock to serialize headder updates, just rely on the header semaphore. (It's already used, this just moves it out to cover a broader section of the code.) That leaves the control mutex protecting only the creation of rbd clients, so rename it. This resolves: http://tracker.ceph.com/issues/5222 Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index d735eb13847d..44c44c6071ed 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -372,7 +372,7 @@ enum rbd_dev_flags { RBD_DEV_FLAG_REMOVING, /* this mapping is being removed */ }; -static DEFINE_MUTEX(ctl_mutex); /* Serialize open/close/setup/teardown */ +static DEFINE_MUTEX(client_mutex); /* Serialize client creation */ static LIST_HEAD(rbd_dev_list); /* devices */ static DEFINE_SPINLOCK(rbd_dev_list_lock); @@ -516,7 +516,7 @@ static const struct block_device_operations rbd_bd_ops = { /* * Initialize an rbd client instance. Success or not, this function - * consumes ceph_opts. Caller holds ctl_mutex. + * consumes ceph_opts. Caller holds client_mutex. */ static struct rbd_client *rbd_client_create(struct ceph_options *ceph_opts) { @@ -673,13 +673,13 @@ static struct rbd_client *rbd_get_client(struct ceph_options *ceph_opts) { struct rbd_client *rbdc; - mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); + mutex_lock_nested(&client_mutex, SINGLE_DEPTH_NESTING); rbdc = rbd_client_find(ceph_opts); if (rbdc) /* using an existing client */ ceph_destroy_options(ceph_opts); else rbdc = rbd_client_create(ceph_opts); - mutex_unlock(&ctl_mutex); + mutex_unlock(&client_mutex); return rbdc; } @@ -833,7 +833,6 @@ static int rbd_header_from_disk(struct rbd_device *rbd_dev, /* We won't fail any more, fill in the header */ - down_write(&rbd_dev->header_rwsem); if (first_time) { header->object_prefix = object_prefix; header->obj_order = ondisk->options.order; @@ -862,8 +861,6 @@ static int rbd_header_from_disk(struct rbd_device *rbd_dev, if (rbd_dev->mapping.size != header->image_size) rbd_dev->mapping.size = header->image_size; - up_write(&rbd_dev->header_rwsem); - return 0; out_2big: ret = -EIO; @@ -3333,7 +3330,7 @@ static int rbd_dev_refresh(struct rbd_device *rbd_dev) int ret; rbd_assert(rbd_image_format_valid(rbd_dev->image_format)); - mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); + down_write(&rbd_dev->header_rwsem); mapping_size = rbd_dev->mapping.size; if (rbd_dev->image_format == 1) ret = rbd_dev_v1_header_info(rbd_dev); @@ -3343,7 +3340,8 @@ static int rbd_dev_refresh(struct rbd_device *rbd_dev) /* If it's a mapped snapshot, validate its EXISTS flag */ rbd_exists_validate(rbd_dev); - mutex_unlock(&ctl_mutex); + up_write(&rbd_dev->header_rwsem); + if (mapping_size != rbd_dev->mapping.size) { sector_t size; @@ -4272,16 +4270,14 @@ static int rbd_dev_v2_header_info(struct rbd_device *rbd_dev) bool first_time = rbd_dev->header.object_prefix == NULL; int ret; - down_write(&rbd_dev->header_rwsem); - ret = rbd_dev_v2_image_size(rbd_dev); if (ret) - goto out; + return ret; if (first_time) { ret = rbd_dev_v2_header_onetime(rbd_dev); if (ret) - goto out; + return ret; } /* @@ -4296,7 +4292,7 @@ static int rbd_dev_v2_header_info(struct rbd_device *rbd_dev) ret = rbd_dev_v2_parent_info(rbd_dev); if (ret) - goto out; + return ret; /* * Print a warning if this is the initial probe and @@ -4317,8 +4313,6 @@ static int rbd_dev_v2_header_info(struct rbd_device *rbd_dev) ret = rbd_dev_v2_snap_context(rbd_dev); dout("rbd_dev_v2_snap_context returned %d\n", ret); -out: - up_write(&rbd_dev->header_rwsem); return ret; } -- cgit v1.2.3 From d552c6191bcd952991ffdfff585c8849e8be911d Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 31 May 2013 20:13:09 -0500 Subject: rbd: take a little credit Add a name to the list of authors. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 44c44c6071ed..14c6dc92ef0e 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -5272,6 +5272,7 @@ static void __exit rbd_exit(void) module_init(rbd_init); module_exit(rbd_exit); +MODULE_AUTHOR("Alex Elder "); MODULE_AUTHOR("Sage Weil "); MODULE_AUTHOR("Yehuda Sadeh "); MODULE_DESCRIPTION("rados block device"); -- cgit v1.2.3 From eb845ff13a44477f8a411baedbf11d678b9daf0a Mon Sep 17 00:00:00 2001 From: "Yan, Zheng" Date: Fri, 31 May 2013 15:54:44 +0800 Subject: libceph: fix safe completion handle_reply() calls complete_request() only if the first OSD reply has ONDISK flag. Signed-off-by: Yan, Zheng Reviewed-by: Sage Weil --- include/linux/ceph/osd_client.h | 1 - net/ceph/osd_client.c | 17 ++++++++--------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/include/linux/ceph/osd_client.h b/include/linux/ceph/osd_client.h index 186db0bf4951..ce6df39f60ff 100644 --- a/include/linux/ceph/osd_client.h +++ b/include/linux/ceph/osd_client.h @@ -145,7 +145,6 @@ struct ceph_osd_request { s32 r_reply_op_result[CEPH_OSD_MAX_OP]; int r_got_reply; int r_linger; - int r_completed; struct ceph_osd_client *r_osdc; struct kref r_kref; diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 61147fe70181..3480b058794b 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -1522,6 +1522,8 @@ static void handle_reply(struct ceph_osd_client *osdc, struct ceph_msg *msg, for (i = 0; i < numops; i++) req->r_reply_op_result[i] = ceph_decode_32(&p); + already_completed = req->r_got_reply; + if (!req->r_got_reply) { req->r_result = result; @@ -1552,16 +1554,14 @@ static void handle_reply(struct ceph_osd_client *osdc, struct ceph_msg *msg, ((flags & CEPH_OSD_FLAG_WRITE) == 0)) __unregister_request(osdc, req); - already_completed = req->r_completed; - req->r_completed = 1; mutex_unlock(&osdc->request_mutex); - if (already_completed) - goto done; - if (req->r_callback) - req->r_callback(req, msg); - else - complete_all(&req->r_completion); + if (!already_completed) { + if (req->r_callback) + req->r_callback(req, msg); + else + complete_all(&req->r_completion); + } if (flags & CEPH_OSD_FLAG_ONDISK) complete_request(req); @@ -2121,7 +2121,6 @@ int ceph_osdc_start_request(struct ceph_osd_client *osdc, __register_request(osdc, req); req->r_sent = 0; req->r_got_reply = 0; - req->r_completed = 0; rc = __map_request(osdc, req, 0); if (rc < 0) { if (nofail) { -- cgit v1.2.3 From ccca4e37b1a912da3db68aee826557ea66145273 Mon Sep 17 00:00:00 2001 From: "Yan, Zheng" Date: Sun, 2 Jun 2013 18:40:23 +0800 Subject: libceph: fix truncate size calculation check the "not truncated yet" case Signed-off-by: Yan, Zheng Reviewed-by: Sage Weil --- net/ceph/osd_client.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 3480b058794b..540dd29c9210 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -733,12 +733,14 @@ struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc, object_size = le32_to_cpu(layout->fl_object_size); object_base = off - objoff; - if (truncate_size <= object_base) { - truncate_size = 0; - } else { - truncate_size -= object_base; - if (truncate_size > object_size) - truncate_size = object_size; + if (!(truncate_seq == 1 && truncate_size == -1ULL)) { + if (truncate_size <= object_base) { + truncate_size = 0; + } else { + truncate_size -= object_base; + if (truncate_size > object_size) + truncate_size = object_size; + } } osd_req_op_extent_init(req, 0, opcode, objoff, objlen, -- cgit v1.2.3 From bb137f84d1d8f692233b590f7cae14abbdc1e0c1 Mon Sep 17 00:00:00 2001 From: "Yan, Zheng" Date: Mon, 3 Jun 2013 18:22:17 +0800 Subject: ceph: fix cap release race ceph_encode_inode_release() can race with ceph_open() and release caps wanted by open files. So it should call __ceph_caps_wanted() to get the wanted caps. Signed-off-by: Yan, Zheng Reviewed-by: Sage Weil --- fs/ceph/caps.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index da0f9b8a3bcb..54c290b083ab 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -3042,21 +3042,19 @@ int ceph_encode_inode_release(void **p, struct inode *inode, (cap->issued & unless) == 0)) { if ((cap->issued & drop) && (cap->issued & unless) == 0) { - dout("encode_inode_release %p cap %p %s -> " - "%s\n", inode, cap, + int wanted = __ceph_caps_wanted(ci); + if ((ci->i_ceph_flags & CEPH_I_NODELAY) == 0) + wanted |= cap->mds_wanted; + dout("encode_inode_release %p cap %p " + "%s -> %s, wanted %s -> %s\n", inode, cap, ceph_cap_string(cap->issued), - ceph_cap_string(cap->issued & ~drop)); + ceph_cap_string(cap->issued & ~drop), + ceph_cap_string(cap->mds_wanted), + ceph_cap_string(wanted)); + cap->issued &= ~drop; cap->implemented &= ~drop; - if (ci->i_ceph_flags & CEPH_I_NODELAY) { - int wanted = __ceph_caps_wanted(ci); - dout(" wanted %s -> %s (act %s)\n", - ceph_cap_string(cap->mds_wanted), - ceph_cap_string(cap->mds_wanted & - ~wanted), - ceph_cap_string(wanted)); - cap->mds_wanted &= wanted; - } + cap->mds_wanted = wanted; } else { dout("encode_inode_release %p cap %p %s" " (force)\n", inode, cap, -- cgit v1.2.3 From 3803da4963db01da6a983ab589ebe2e6ccb97ba9 Mon Sep 17 00:00:00 2001 From: "Yan, Zheng" Date: Fri, 31 May 2013 16:26:44 +0800 Subject: ceph: reset iov_len when discarding cap release messages Signed-off-by: Yan, Zheng Reviewed-by: Sage Weil --- fs/ceph/mds_client.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 4d2920304be8..ddbd5907d41b 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -1391,6 +1391,7 @@ static void discard_cap_releases(struct ceph_mds_client *mdsc, num = le32_to_cpu(head->num); dout("discard_cap_releases mds%d %p %u\n", session->s_mds, msg, num); head->num = cpu_to_le32(0); + msg->front.iov_len = sizeof(*head); session->s_num_cap_releases += num; /* requeue completed messages */ -- cgit v1.2.3 From fc2744aa12da7182509b1059aa3ab53754d0c83a Mon Sep 17 00:00:00 2001 From: "Yan, Zheng" Date: Fri, 31 May 2013 16:48:29 +0800 Subject: ceph: fix race between page writeback and truncate The client can receive truncate request from MDS at any time. So the page writeback code need to get i_size, truncate_seq and truncate_size atomically Signed-off-by: Yan, Zheng Reviewed-by: Sage Weil --- fs/ceph/addr.c | 84 ++++++++++++++++++++++++++++------------------------------ 1 file changed, 40 insertions(+), 44 deletions(-) diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c index 3e68ac101040..3500b74c32ed 100644 --- a/fs/ceph/addr.c +++ b/fs/ceph/addr.c @@ -438,13 +438,12 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc) struct ceph_inode_info *ci; struct ceph_fs_client *fsc; struct ceph_osd_client *osdc; - loff_t page_off = page_offset(page); - int len = PAGE_CACHE_SIZE; - loff_t i_size; - int err = 0; struct ceph_snap_context *snapc, *oldest; - u64 snap_size = 0; + loff_t page_off = page_offset(page); long writeback_stat; + u64 truncate_size, snap_size = 0; + u32 truncate_seq; + int err = 0, len = PAGE_CACHE_SIZE; dout("writepage %p idx %lu\n", page, page->index); @@ -474,13 +473,20 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc) } ceph_put_snap_context(oldest); + spin_lock(&ci->i_ceph_lock); + truncate_seq = ci->i_truncate_seq; + truncate_size = ci->i_truncate_size; + if (!snap_size) + snap_size = i_size_read(inode); + spin_unlock(&ci->i_ceph_lock); + /* is this a partial page at end of file? */ - if (snap_size) - i_size = snap_size; - else - i_size = i_size_read(inode); - if (i_size < page_off + len) - len = i_size - page_off; + if (page_off >= snap_size) { + dout("%p page eof %llu\n", page, snap_size); + goto out; + } + if (snap_size < page_off + len) + len = snap_size - page_off; dout("writepage %p page %p index %lu on %llu~%u snapc %p\n", inode, page, page->index, page_off, len, snapc); @@ -494,7 +500,7 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc) err = ceph_osdc_writepages(osdc, ceph_vino(inode), &ci->i_layout, snapc, page_off, len, - ci->i_truncate_seq, ci->i_truncate_size, + truncate_seq, truncate_size, &inode->i_mtime, &page, 1); if (err < 0) { dout("writepage setting page/mapping error %d %p\n", err, page); @@ -631,25 +637,6 @@ static void writepages_finish(struct ceph_osd_request *req, ceph_osdc_put_request(req); } -static struct ceph_osd_request * -ceph_writepages_osd_request(struct inode *inode, u64 offset, u64 *len, - struct ceph_snap_context *snapc, int num_ops) -{ - struct ceph_fs_client *fsc; - struct ceph_inode_info *ci; - struct ceph_vino vino; - - fsc = ceph_inode_to_client(inode); - ci = ceph_inode(inode); - vino = ceph_vino(inode); - /* BUG_ON(vino.snap != CEPH_NOSNAP); */ - - return ceph_osdc_new_request(&fsc->client->osdc, &ci->i_layout, - vino, offset, len, num_ops, CEPH_OSD_OP_WRITE, - CEPH_OSD_FLAG_WRITE|CEPH_OSD_FLAG_ONDISK, - snapc, ci->i_truncate_seq, ci->i_truncate_size, true); -} - /* * initiate async writeback */ @@ -658,7 +645,8 @@ static int ceph_writepages_start(struct address_space *mapping, { struct inode *inode = mapping->host; struct ceph_inode_info *ci = ceph_inode(inode); - struct ceph_fs_client *fsc; + struct ceph_fs_client *fsc = ceph_inode_to_client(inode); + struct ceph_vino vino = ceph_vino(inode); pgoff_t index, start, end; int range_whole = 0; int should_loop = 1; @@ -670,7 +658,8 @@ static int ceph_writepages_start(struct address_space *mapping, unsigned wsize = 1 << inode->i_blkbits; struct ceph_osd_request *req = NULL; int do_sync; - u64 snap_size; + u64 truncate_size, snap_size; + u32 truncate_seq; /* * Include a 'sync' in the OSD request if this is a data @@ -685,7 +674,6 @@ static int ceph_writepages_start(struct address_space *mapping, wbc->sync_mode == WB_SYNC_NONE ? "NONE" : (wbc->sync_mode == WB_SYNC_ALL ? "ALL" : "HOLD")); - fsc = ceph_inode_to_client(inode); if (fsc->mount_state == CEPH_MOUNT_SHUTDOWN) { pr_warning("writepage_start %p on forced umount\n", inode); return -EIO; /* we're in a forced umount, don't write! */ @@ -728,6 +716,14 @@ retry: snap_size = i_size_read(inode); dout(" oldest snapc is %p seq %lld (%d snaps)\n", snapc, snapc->seq, snapc->num_snaps); + + spin_lock(&ci->i_ceph_lock); + truncate_seq = ci->i_truncate_seq; + truncate_size = ci->i_truncate_size; + if (!snap_size) + snap_size = i_size_read(inode); + spin_unlock(&ci->i_ceph_lock); + if (last_snapc && snapc != last_snapc) { /* if we switched to a newer snapc, restart our scan at the * start of the original file range. */ @@ -739,7 +735,6 @@ retry: while (!done && index <= end) { int num_ops = do_sync ? 2 : 1; - struct ceph_vino vino; unsigned i; int first; pgoff_t next; @@ -833,17 +828,18 @@ get_more_pages: * that it will use. */ if (locked_pages == 0) { - size_t size; - BUG_ON(pages); - /* prepare async write request */ offset = (u64)page_offset(page); len = wsize; - req = ceph_writepages_osd_request(inode, - offset, &len, snapc, - num_ops); - + req = ceph_osdc_new_request(&fsc->client->osdc, + &ci->i_layout, vino, + offset, &len, num_ops, + CEPH_OSD_OP_WRITE, + CEPH_OSD_FLAG_WRITE | + CEPH_OSD_FLAG_ONDISK, + snapc, truncate_seq, + truncate_size, true); if (IS_ERR(req)) { rc = PTR_ERR(req); unlock_page(page); @@ -854,8 +850,8 @@ get_more_pages: req->r_inode = inode; max_pages = calc_pages_for(0, (u64)len); - size = max_pages * sizeof (*pages); - pages = kmalloc(size, GFP_NOFS); + pages = kmalloc(max_pages * sizeof (*pages), + GFP_NOFS); if (!pages) { pool = fsc->wb_pagevec_pool; pages = mempool_alloc(pool, GFP_NOFS); -- cgit v1.2.3 From b8c2f3ae2d9f2b975a0e1a9c5652829ef8a4f06c Mon Sep 17 00:00:00 2001 From: "Yan, Zheng" Date: Fri, 31 May 2013 16:37:11 +0800 Subject: ceph: check migrate seq before changing auth cap We may receive old request reply from the exporter MDS after receiving the importer MDS' cap import message. Signed-off-by: Yan, Zheng Reviewed-by: Sage Weil --- fs/ceph/caps.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 54c290b083ab..790f88b15daf 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -612,9 +612,11 @@ retry: __cap_delay_requeue(mdsc, ci); } - if (flags & CEPH_CAP_FLAG_AUTH) - ci->i_auth_cap = cap; - else if (ci->i_auth_cap == cap) { + if (flags & CEPH_CAP_FLAG_AUTH) { + if (ci->i_auth_cap == NULL || + ceph_seq_cmp(ci->i_auth_cap->mseq, mseq) < 0) + ci->i_auth_cap = cap; + } else if (ci->i_auth_cap == cap) { ci->i_auth_cap = NULL; spin_lock(&mdsc->cap_dirty_lock); if (!list_empty(&ci->i_dirty_item)) { -- cgit v1.2.3 From 667ca05cd9f02f0a345446abc362484c019d4d71 Mon Sep 17 00:00:00 2001 From: "Yan, Zheng" Date: Fri, 31 May 2013 16:25:36 +0800 Subject: ceph: clear migrate seq when MDS restarts Signed-off-by: Yan, Zheng Reviewed-by: Sage Weil --- fs/ceph/mds_client.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index ddbd5907d41b..6272c7884e66 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -2455,6 +2455,7 @@ static int encode_caps_cb(struct inode *inode, struct ceph_cap *cap, spin_lock(&ci->i_ceph_lock); cap->seq = 0; /* reset cap seq */ cap->issue_seq = 0; /* and issue_seq */ + cap->mseq = 0; /* and migrate_seq */ if (recon_state->flock) { rec.v2.cap_id = cpu_to_le64(cap->cap_id); -- cgit v1.2.3 From e976cad0f0dbe5440a4ca38e29e1f932d9319125 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Sun, 9 Jun 2013 08:40:39 -0700 Subject: rbd: fix a couple warnings gcc isn't quite smart enough and generates these warnings: drivers/block/rbd.c: In function 'rbd_img_request_fill': drivers/block/rbd.c:1266:22: warning: 'bio_list' may be used uninitialized in this function [-Wmaybe-uninitialized] drivers/block/rbd.c:2186:14: note: 'bio_list' was declared here drivers/block/rbd.c:2247:10: warning: 'pages' may be used uninitialized in this function [-Wmaybe-uninitialized] even though they are initialized for their respective code paths. Signed-off-by: Sage Weil --- drivers/block/rbd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 14c6dc92ef0e..4ad2ad9a5bb0 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -2163,9 +2163,9 @@ static int rbd_img_request_fill(struct rbd_img_request *img_request, struct rbd_obj_request *obj_request = NULL; struct rbd_obj_request *next_obj_request; bool write_request = img_request_write_test(img_request); - struct bio *bio_list; + struct bio *bio_list = 0; unsigned int bio_offset = 0; - struct page **pages; + struct page **pages = 0; u64 img_offset; u64 resid; u16 opcode; -- cgit v1.2.3 From 005c46970e3a2a4b95da220eab43b87307646335 Mon Sep 17 00:00:00 2001 From: "Yan, Zheng" Date: Fri, 31 May 2013 16:40:24 +0800 Subject: ceph: move inode to proper flushing list when auth MDS changes Signed-off-by: Yan, Zheng Reviewed-by: Sage Weil --- fs/ceph/caps.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 790f88b15daf..9a5ccc9e0d62 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -1982,8 +1982,15 @@ static void kick_flushing_inode_caps(struct ceph_mds_client *mdsc, cap = ci->i_auth_cap; dout("kick_flushing_inode_caps %p flushing %s flush_seq %lld\n", inode, ceph_cap_string(ci->i_flushing_caps), ci->i_cap_flush_seq); + __ceph_flush_snaps(ci, &session, 1); + if (ci->i_flushing_caps) { + spin_lock(&mdsc->cap_dirty_lock); + list_move_tail(&ci->i_flushing_item, + &cap->session->s_cap_flushing); + spin_unlock(&mdsc->cap_dirty_lock); + delayed = __send_cap(mdsc, cap, CEPH_CAP_OP_FLUSH, __ceph_caps_used(ci), __ceph_caps_wanted(ci), -- cgit v1.2.3 From a1dc1937337a93e699eaa56968b7de6e1a9e77cf Mon Sep 17 00:00:00 2001 From: majianpeng Date: Wed, 19 Jun 2013 14:58:10 +0800 Subject: ceph: fix sleeping function called from invalid context. [ 1121.231883] BUG: sleeping function called from invalid context at kernel/rwsem.c:20 [ 1121.231935] in_atomic(): 1, irqs_disabled(): 0, pid: 9831, name: mv [ 1121.231971] 1 lock held by mv/9831: [ 1121.231973] #0: (&(&ci->i_ceph_lock)->rlock){+.+...},at:[] ceph_getxattr+0x58/0x1d0 [ceph] [ 1121.231998] CPU: 3 PID: 9831 Comm: mv Not tainted 3.10.0-rc6+ #215 [ 1121.232000] Hardware name: To Be Filled By O.E.M. To Be Filled By O.E.M./To be filled by O.E.M., BIOS 080015 11/09/2011 [ 1121.232027] ffff88006d355a80 ffff880092f69ce0 ffffffff8168348c ffff880092f69cf8 [ 1121.232045] ffffffff81070435 ffff88006d355a20 ffff880092f69d20 ffffffff816899ba [ 1121.232052] 0000000300000004 ffff8800b76911d0 ffff88006d355a20 ffff880092f69d68 [ 1121.232056] Call Trace: [ 1121.232062] [] dump_stack+0x19/0x1b [ 1121.232067] [] __might_sleep+0xe5/0x110 [ 1121.232071] [] down_read+0x2a/0x98 [ 1121.232080] [] ceph_vxattrcb_layout+0x60/0xf0 [ceph] [ 1121.232088] [] ceph_getxattr+0x9f/0x1d0 [ceph] [ 1121.232093] [] vfs_getxattr+0xa8/0xd0 [ 1121.232097] [] getxattr+0xab/0x1c0 [ 1121.232100] [] ? final_putname+0x22/0x50 [ 1121.232104] [] ? kmem_cache_free+0xb0/0x260 [ 1121.232107] [] ? final_putname+0x22/0x50 [ 1121.232110] [] ? trace_hardirqs_on+0xd/0x10 [ 1121.232114] [] ? sysret_check+0x1b/0x56 [ 1121.232120] [] SyS_fgetxattr+0x6c/0xc0 [ 1121.232125] [] system_call_fastpath+0x16/0x1b [ 1121.232129] BUG: scheduling while atomic: mv/9831/0x10000002 [ 1121.232154] 1 lock held by mv/9831: [ 1121.232156] #0: (&(&ci->i_ceph_lock)->rlock){+.+...}, at: [] ceph_getxattr+0x58/0x1d0 [ceph] I think move the ci->i_ceph_lock down is safe because we can't free ceph_inode_info at there. CC: stable@vger.kernel.org # 3.8+ Signed-off-by: Jianpeng Ma Reviewed-by: Sage Weil --- fs/ceph/xattr.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c index 9b6b2b6dd164..be661d8f532a 100644 --- a/fs/ceph/xattr.c +++ b/fs/ceph/xattr.c @@ -675,17 +675,18 @@ ssize_t ceph_getxattr(struct dentry *dentry, const char *name, void *value, if (!ceph_is_valid_xattr(name)) return -ENODATA; - spin_lock(&ci->i_ceph_lock); - dout("getxattr %p ver=%lld index_ver=%lld\n", inode, - ci->i_xattrs.version, ci->i_xattrs.index_version); /* let's see if a virtual xattr was requested */ vxattr = ceph_match_vxattr(inode, name); if (vxattr && !(vxattr->exists_cb && !vxattr->exists_cb(ci))) { err = vxattr->getxattr_cb(ci, value, size); - goto out; + return err; } + spin_lock(&ci->i_ceph_lock); + dout("getxattr %p ver=%lld index_ver=%lld\n", inode, + ci->i_xattrs.version, ci->i_xattrs.index_version); + if (__ceph_caps_issued_mask(ci, CEPH_CAP_XATTR_SHARED, 1) && (ci->i_xattrs.index_version >= ci->i_xattrs.version)) { goto get_xattr; -- cgit v1.2.3 From c62988ec0910a2d480fecb2f0140a36fcdc7b691 Mon Sep 17 00:00:00 2001 From: majianpeng Date: Wed, 19 Jun 2013 15:12:06 +0800 Subject: ceph: avoid meaningless calling ceph_caps_revoking if sync_mode == WB_SYNC_ALL. Signed-off-by: Jianpeng Ma Reviewed-by: Sage Weil --- fs/ceph/addr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c index 3500b74c32ed..afb2fc241061 100644 --- a/fs/ceph/addr.c +++ b/fs/ceph/addr.c @@ -666,8 +666,8 @@ static int ceph_writepages_start(struct address_space *mapping, * integrity write (e.g., O_SYNC write or fsync()), or if our * cap is being revoked. */ - do_sync = wbc->sync_mode == WB_SYNC_ALL; - if (ceph_caps_revoking(ci, CEPH_CAP_FILE_BUFFER)) + if ((wbc->sync_mode == WB_SYNC_ALL) || + ceph_caps_revoking(ci, CEPH_CAP_FILE_BUFFER)) do_sync = 1; dout("writepages_start %p dosync=%d (mode=%s)\n", inode, do_sync, -- cgit v1.2.3 From 0405a1499df42a2b9fd4906096c6bb950e15e850 Mon Sep 17 00:00:00 2001 From: Jianpeng Ma Date: Sun, 23 Jun 2013 09:48:38 -0700 Subject: ceph: remove sb_start/end_write in ceph_aio_write. Either in vfs_write or io_submit,it call file_start/end_write. The different between file_start/end_write and sb_start/end_write is file_ only handle regular file.But i think in ceph_aio_write,it only for regular file. Signed-off-by: Jianpeng Ma Acked-by: Yan, Zheng --- fs/ceph/file.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/ceph/file.c b/fs/ceph/file.c index 656e16907430..7c69f4f0dee6 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -716,7 +716,6 @@ static ssize_t ceph_aio_write(struct kiocb *iocb, const struct iovec *iov, if (ceph_snap(inode) != CEPH_NOSNAP) return -EROFS; - sb_start_write(inode->i_sb); mutex_lock(&inode->i_mutex); hold_mutex = true; @@ -809,7 +808,6 @@ retry_snap: out: if (hold_mutex) mutex_unlock(&inode->i_mutex); - sb_end_write(inode->i_sb); current->backing_dev_info = NULL; return written ? written : err; -- cgit v1.2.3 From fb3101b6f0db9ae3f35dc8e6ec908d0af8cdf12e Mon Sep 17 00:00:00 2001 From: majianpeng Date: Tue, 25 Jun 2013 14:48:19 +0800 Subject: ceph: Free mdsc if alloc mdsc->mdsmap failed. Signed-off-by: Jianpeng Ma Reviewed-by: Sage Weil --- fs/ceph/mds_client.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 6272c7884e66..3eb1b4470c85 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -3042,8 +3042,10 @@ int ceph_mdsc_init(struct ceph_fs_client *fsc) fsc->mdsc = mdsc; mutex_init(&mdsc->mutex); mdsc->mdsmap = kzalloc(sizeof(*mdsc->mdsmap), GFP_NOFS); - if (mdsc->mdsmap == NULL) + if (mdsc->mdsmap == NULL) { + kfree(mdsc); return -ENOMEM; + } init_completion(&mdsc->safe_umount_waiters); init_waitqueue_head(&mdsc->session_close_wq); -- cgit v1.2.3 From 93faca6ef45822b0825bb181859b1a8911e9c4c1 Mon Sep 17 00:00:00 2001 From: majianpeng Date: Wed, 26 Jun 2013 11:15:27 +0800 Subject: ceph: Reconstruct the func ceph_reserve_caps. Drop ignored return value. Fix allocation failure case to not leak. Signed-off-by: Jianpeng Ma Reviewed-by: Sage Weil --- fs/ceph/caps.c | 21 +++++++-------------- fs/ceph/super.h | 2 +- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 9a5ccc9e0d62..8ec27b130cc9 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -147,7 +147,7 @@ void ceph_adjust_min_caps(struct ceph_mds_client *mdsc, int delta) spin_unlock(&mdsc->caps_list_lock); } -int ceph_reserve_caps(struct ceph_mds_client *mdsc, +void ceph_reserve_caps(struct ceph_mds_client *mdsc, struct ceph_cap_reservation *ctx, int need) { int i; @@ -155,7 +155,6 @@ int ceph_reserve_caps(struct ceph_mds_client *mdsc, int have; int alloc = 0; LIST_HEAD(newcaps); - int ret = 0; dout("reserve caps ctx=%p need=%d\n", ctx, need); @@ -174,14 +173,15 @@ int ceph_reserve_caps(struct ceph_mds_client *mdsc, for (i = have; i < need; i++) { cap = kmem_cache_alloc(ceph_cap_cachep, GFP_NOFS); - if (!cap) { - ret = -ENOMEM; - goto out_alloc_count; - } + if (!cap) + break; list_add(&cap->caps_item, &newcaps); alloc++; } - BUG_ON(have + alloc != need); + /* we didn't manage to reserve as much as we needed */ + if (have + alloc != need) + pr_warn("reserve caps ctx=%p ENOMEM need=%d got=%d\n", + ctx, need, have + alloc); spin_lock(&mdsc->caps_list_lock); mdsc->caps_total_count += alloc; @@ -197,13 +197,6 @@ int ceph_reserve_caps(struct ceph_mds_client *mdsc, dout("reserve caps ctx=%p %d = %d used + %d resv + %d avail\n", ctx, mdsc->caps_total_count, mdsc->caps_use_count, mdsc->caps_reserve_count, mdsc->caps_avail_count); - return 0; - -out_alloc_count: - /* we didn't manage to reserve as much as we needed */ - pr_warning("reserve caps ctx=%p ENOMEM need=%d got=%d\n", - ctx, need, have); - return ret; } int ceph_unreserve_caps(struct ceph_mds_client *mdsc, diff --git a/fs/ceph/super.h b/fs/ceph/super.h index 7ccfdb4aea2e..dfbb729b3130 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -534,7 +534,7 @@ extern int __ceph_caps_mds_wanted(struct ceph_inode_info *ci); extern void ceph_caps_init(struct ceph_mds_client *mdsc); extern void ceph_caps_finalize(struct ceph_mds_client *mdsc); extern void ceph_adjust_min_caps(struct ceph_mds_client *mdsc, int delta); -extern int ceph_reserve_caps(struct ceph_mds_client *mdsc, +extern void ceph_reserve_caps(struct ceph_mds_client *mdsc, struct ceph_cap_reservation *ctx, int need); extern int ceph_unreserve_caps(struct ceph_mds_client *mdsc, struct ceph_cap_reservation *ctx); -- cgit v1.2.3 From 2cb33cac622afde897aa02d3dcd9fbba8bae839e Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Thu, 20 Jun 2013 13:13:59 -0700 Subject: libceph: Fix NULL pointer dereference in auth client code A malicious monitor can craft an auth reply message that could cause a NULL function pointer dereference in the client's kernel. To prevent this, the auth_none protocol handler needs an empty ceph_auth_client_ops->build_request() function. CVE-2013-1059 Signed-off-by: Tyler Hicks Reported-by: Chanam Park Reviewed-by: Seth Arnold Reviewed-by: Sage Weil Cc: stable@vger.kernel.org --- net/ceph/auth_none.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/ceph/auth_none.c b/net/ceph/auth_none.c index 925ca583c09c..8c93fa8d81bc 100644 --- a/net/ceph/auth_none.c +++ b/net/ceph/auth_none.c @@ -39,6 +39,11 @@ static int should_authenticate(struct ceph_auth_client *ac) return xi->starting; } +static int build_request(struct ceph_auth_client *ac, void *buf, void *end) +{ + return 0; +} + /* * the generic auth code decode the global_id, and we carry no actual * authenticate state, so nothing happens here. @@ -106,6 +111,7 @@ static const struct ceph_auth_client_ops ceph_auth_none_ops = { .destroy = destroy, .is_authenticated = is_authenticated, .should_authenticate = should_authenticate, + .build_request = build_request, .handle_reply = handle_reply, .create_authorizer = ceph_auth_none_create_authorizer, .destroy_authorizer = ceph_auth_none_destroy_authorizer, -- cgit v1.2.3 From 5446429630257f4723829409337a26c076907d5d Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Mon, 1 Jul 2013 18:33:39 -0400 Subject: ceph: avoid accessing invalid memory when mounting ceph with a dev name that starts with a slash, ceph would attempt to access the character before that slash. Since we don't actually own that byte of memory, we would trigger an invalid access: [ 43.499934] BUG: unable to handle kernel paging request at ffff880fa3a97fff [ 43.500984] IP: [] parse_mount_options+0x1a4/0x300 [ 43.501491] PGD 743b067 PUD 10283c4067 PMD 10282a6067 PTE 8000000fa3a97060 [ 43.502301] Oops: 0000 [#1] PREEMPT SMP DEBUG_PAGEALLOC [ 43.503006] Dumping ftrace buffer: [ 43.503596] (ftrace buffer empty) [ 43.504046] CPU: 0 PID: 10879 Comm: mount Tainted: G W 3.10.0-sasha #1129 [ 43.504851] task: ffff880fa625b000 ti: ffff880fa3412000 task.ti: ffff880fa3412000 [ 43.505608] RIP: 0010:[] [] parse_mount_options$ [ 43.506552] RSP: 0018:ffff880fa3413d08 EFLAGS: 00010286 [ 43.507133] RAX: ffff880fa3a98000 RBX: ffff880fa3a98000 RCX: 0000000000000000 [ 43.507893] RDX: ffff880fa3a98001 RSI: 000000000000002f RDI: ffff880fa3a98000 [ 43.508610] RBP: ffff880fa3413d58 R08: 0000000000001f99 R09: ffff880fa3fe64c0 [ 43.509426] R10: ffff880fa3413d98 R11: ffff880fa38710d8 R12: ffff880fa3413da0 [ 43.509792] R13: ffff880fa3a97fff R14: 0000000000000000 R15: ffff880fa3413d90 [ 43.509792] FS: 00007fa9c48757e0(0000) GS:ffff880fd2600000(0000) knlGS:000000000000$ [ 43.509792] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b [ 43.509792] CR2: ffff880fa3a97fff CR3: 0000000fa3bb9000 CR4: 00000000000006b0 [ 43.509792] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 43.509792] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 [ 43.509792] Stack: [ 43.509792] 0000e5180000000e ffffffff85ca1900 ffff880fa38710d8 ffff880fa3413d98 [ 43.509792] 0000000000000120 0000000000000000 ffff880fa3a98000 0000000000000000 [ 43.509792] ffffffff85cf32a0 0000000000000000 ffff880fa3413dc8 ffffffff818f3c72 [ 43.509792] Call Trace: [ 43.509792] [] ceph_mount+0xa2/0x390 [ 43.509792] [] ? pcpu_alloc+0x334/0x3c0 [ 43.509792] [] mount_fs+0x8d/0x1a0 [ 43.509792] [] ? __alloc_percpu+0x10/0x20 [ 43.509792] [] vfs_kern_mount+0x79/0x100 [ 43.509792] [] do_new_mount+0xcd/0x1c0 [ 43.509792] [] do_mount+0x15d/0x210 [ 43.509792] [] ? strndup_user+0x45/0x60 [ 43.509792] [] SyS_mount+0x9d/0xe0 [ 43.509792] [] tracesys+0xdd/0xe2 [ 43.509792] Code: 4c 8b 5d c0 74 0a 48 8d 50 01 49 89 14 24 eb 17 31 c0 48 83 c9 ff $ [ 43.509792] RIP [] parse_mount_options+0x1a4/0x300 [ 43.509792] RSP [ 43.509792] CR2: ffff880fa3a97fff [ 43.509792] ---[ end trace 22469cd81e93af51 ]--- Signed-off-by: Sasha Levin Reviewed-by: Sage Weil --- fs/ceph/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ceph/super.c b/fs/ceph/super.c index 7d377c9a5e35..6627b26a800c 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -357,7 +357,7 @@ static int parse_mount_options(struct ceph_mount_options **pfsopt, } err = -EINVAL; dev_name_end--; /* back up to ':' separator */ - if (*dev_name_end != ':') { + if (dev_name_end < dev_name || *dev_name_end != ':') { pr_err("device name is missing path (no : separator in %s)\n", dev_name); goto out; -- cgit v1.2.3 From b415bf4f9fe25f39934f5c464125e4a2dffb6d08 Mon Sep 17 00:00:00 2001 From: "Yan, Zheng" Date: Tue, 2 Jul 2013 12:40:19 +0800 Subject: ceph: fix pending vmtruncate race The locking order for pending vmtruncate is wrong, it can lead to following race: write wmtruncate work ------------------------ ---------------------- lock i_mutex check i_truncate_pending check i_truncate_pending truncate_inode_pages() lock i_mutex (blocked) copy data to page cache unlock i_mutex truncate_inode_pages() The fix is take i_mutex before calling __ceph_do_pending_vmtruncate() Fixes: http://tracker.ceph.com/issues/5453 Signed-off-by: Yan, Zheng Reviewed-by: Sage Weil --- fs/ceph/caps.c | 6 +++++- fs/ceph/file.c | 2 +- fs/ceph/inode.c | 14 ++++++-------- fs/ceph/super.h | 2 +- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 8ec27b130cc9..16266f3e9a33 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -2057,7 +2057,11 @@ static int try_get_cap_refs(struct ceph_inode_info *ci, int need, int want, /* finish pending truncate */ while (ci->i_truncate_pending) { spin_unlock(&ci->i_ceph_lock); - __ceph_do_pending_vmtruncate(inode, !(need & CEPH_CAP_FILE_WR)); + if (!(need & CEPH_CAP_FILE_WR)) + mutex_lock(&inode->i_mutex); + __ceph_do_pending_vmtruncate(inode); + if (!(need & CEPH_CAP_FILE_WR)) + mutex_unlock(&inode->i_mutex); spin_lock(&ci->i_ceph_lock); } diff --git a/fs/ceph/file.c b/fs/ceph/file.c index 7c69f4f0dee6..a44d5153179b 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -822,7 +822,7 @@ static loff_t ceph_llseek(struct file *file, loff_t offset, int whence) int ret; mutex_lock(&inode->i_mutex); - __ceph_do_pending_vmtruncate(inode, false); + __ceph_do_pending_vmtruncate(inode); if (whence == SEEK_END || whence == SEEK_DATA || whence == SEEK_HOLE) { ret = ceph_do_getattr(inode, CEPH_STAT_CAP_SIZE); diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index be0f7e20d62e..4906ada4a97c 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -1465,7 +1465,9 @@ static void ceph_vmtruncate_work(struct work_struct *work) struct inode *inode = &ci->vfs_inode; dout("vmtruncate_work %p\n", inode); - __ceph_do_pending_vmtruncate(inode, true); + mutex_lock(&inode->i_mutex); + __ceph_do_pending_vmtruncate(inode); + mutex_unlock(&inode->i_mutex); iput(inode); } @@ -1492,7 +1494,7 @@ void ceph_queue_vmtruncate(struct inode *inode) * Make sure any pending truncation is applied before doing anything * that may depend on it. */ -void __ceph_do_pending_vmtruncate(struct inode *inode, bool needlock) +void __ceph_do_pending_vmtruncate(struct inode *inode) { struct ceph_inode_info *ci = ceph_inode(inode); u64 to; @@ -1525,11 +1527,7 @@ retry: ci->i_truncate_pending, to); spin_unlock(&ci->i_ceph_lock); - if (needlock) - mutex_lock(&inode->i_mutex); truncate_inode_pages(inode->i_mapping, to); - if (needlock) - mutex_unlock(&inode->i_mutex); spin_lock(&ci->i_ceph_lock); if (to == ci->i_truncate_size) { @@ -1588,7 +1586,7 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr) if (ceph_snap(inode) != CEPH_NOSNAP) return -EROFS; - __ceph_do_pending_vmtruncate(inode, false); + __ceph_do_pending_vmtruncate(inode); err = inode_change_ok(inode, attr); if (err != 0) @@ -1770,7 +1768,7 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr) ceph_cap_string(dirtied), mask); ceph_mdsc_put_request(req); - __ceph_do_pending_vmtruncate(inode, false); + __ceph_do_pending_vmtruncate(inode); return err; out: spin_unlock(&ci->i_ceph_lock); diff --git a/fs/ceph/super.h b/fs/ceph/super.h index dfbb729b3130..cbded572345e 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -692,7 +692,7 @@ extern int ceph_readdir_prepopulate(struct ceph_mds_request *req, extern int ceph_inode_holds_cap(struct inode *inode, int mask); extern int ceph_inode_set_size(struct inode *inode, loff_t size); -extern void __ceph_do_pending_vmtruncate(struct inode *inode, bool needlock); +extern void __ceph_do_pending_vmtruncate(struct inode *inode); extern void ceph_queue_vmtruncate(struct inode *inode); extern void ceph_queue_invalidate(struct inode *inode); -- cgit v1.2.3 From b1530f57042297f85330a140a6921b6f95fe74d3 Mon Sep 17 00:00:00 2001 From: "Yan, Zheng" Date: Tue, 2 Jul 2013 12:40:20 +0800 Subject: ceph: fix cap revoke race If caps are been revoking by the auth MDS, don't consider them as issued even they are still issued by non-auth MDS. The non-auth MDS should also be revoking/exporting these caps, the client just hasn't received the cap revoke/export message. The race I encountered is: When caps are exporting to new MDS, the client receives cap import message and cap revoke message from the new MDS, then receives cap export message from the old MDS. When the client receives cap revoke message from the new MDS, the revoking caps are still issued by the old MDS, so the client does nothing. Later when the cap export message is received, the client removes the caps issued by the old MDS. (Another way to fix the race is calling ceph_check_caps() in handle_cap_export()) Signed-off-by: Yan, Zheng Reviewed-by: Sage Weil --- fs/ceph/caps.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 16266f3e9a33..7045a8dfaad4 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -690,6 +690,15 @@ int __ceph_caps_issued(struct ceph_inode_info *ci, int *implemented) if (implemented) *implemented |= cap->implemented; } + /* + * exclude caps issued by non-auth MDS, but are been revoking + * by the auth MDS. The non-auth MDS should be revoking/exporting + * these caps, but the message is delayed. + */ + if (ci->i_auth_cap) { + cap = ci->i_auth_cap; + have &= ~cap->implemented | cap->issued; + } return have; } -- cgit v1.2.3 From 6ee6b95373dfa1d0a4c9bc76689ec10a60c1d6f2 Mon Sep 17 00:00:00 2001 From: "Yan, Zheng" Date: Tue, 2 Jul 2013 12:40:21 +0800 Subject: ceph: fix race between cap issue and revoke If we receive new caps from the auth MDS and the non-auth MDS is revoking the newly issued caps, we should release the caps from the non-auth MDS. The scenario is filelock's state changes from SYNC to LOCK. Non-auth MDS revokes Fc cap, the client gets Fc cap from the auth MDS at the same time. Signed-off-by: Yan, Zheng Reviewed-by: Sage Weil --- fs/ceph/caps.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 7045a8dfaad4..25442b40c25a 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -806,22 +806,28 @@ int __ceph_caps_issued_mask(struct ceph_inode_info *ci, int mask, int touch) /* * Return true if mask caps are currently being revoked by an MDS. */ -int ceph_caps_revoking(struct ceph_inode_info *ci, int mask) +int __ceph_caps_revoking_other(struct ceph_inode_info *ci, + struct ceph_cap *ocap, int mask) { - struct inode *inode = &ci->vfs_inode; struct ceph_cap *cap; struct rb_node *p; - int ret = 0; - spin_lock(&ci->i_ceph_lock); for (p = rb_first(&ci->i_caps); p; p = rb_next(p)) { cap = rb_entry(p, struct ceph_cap, ci_node); - if (__cap_is_valid(cap) && - (cap->implemented & ~cap->issued & mask)) { - ret = 1; - break; - } + if (cap != ocap && __cap_is_valid(cap) && + (cap->implemented & ~cap->issued & mask)) + return 1; } + return 0; +} + +int ceph_caps_revoking(struct ceph_inode_info *ci, int mask) +{ + struct inode *inode = &ci->vfs_inode; + int ret; + + spin_lock(&ci->i_ceph_lock); + ret = __ceph_caps_revoking_other(ci, NULL, mask); spin_unlock(&ci->i_ceph_lock); dout("ceph_caps_revoking %p %s = %d\n", inode, ceph_cap_string(mask), ret); @@ -2488,6 +2494,11 @@ static void handle_cap_grant(struct inode *inode, struct ceph_mds_caps *grant, } else { dout("grant: %s -> %s\n", ceph_cap_string(cap->issued), ceph_cap_string(newcaps)); + /* non-auth MDS is revoking the newly grant caps ? */ + if (cap == ci->i_auth_cap && + __ceph_caps_revoking_other(ci, cap, newcaps)) + check_caps = 2; + cap->issued = newcaps; cap->implemented |= newcaps; /* add bits only, to * avoid stepping on a -- cgit v1.2.3 From 61c5d6bf7074ee32d014dcdf7698dc8c59eb712d Mon Sep 17 00:00:00 2001 From: "Yan, Zheng" Date: Mon, 24 Jun 2013 14:41:27 +0800 Subject: libceph: call r_unsafe_callback when unsafe reply is received We can't use !req->r_sent to check if OSD request is sent for the first time, this is because __cancel_request() zeros req->r_sent when OSD map changes. Rather than adding a new variable to struct ceph_osd_request to indicate if it's sent for the first time, We can call the unsafe callback only when unsafe OSD reply is received. If OSD's first reply is safe, just skip calling the unsafe callback. The purpose of unsafe callback is adding unsafe request to a list, so that fsync(2) can wait for the safe reply. fsync(2) doesn't need to wait for a write(2) that hasn't returned yet. So it's OK to add request to the unsafe list when the first OSD reply is received. (ceph_sync_write() returns after receiving the first OSD reply) Signed-off-by: Yan, Zheng Reviewed-by: Sage Weil --- net/ceph/osd_client.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 540dd29c9210..dd47889adc4a 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -1337,10 +1337,6 @@ static void __send_request(struct ceph_osd_client *osdc, ceph_msg_get(req->r_request); /* send consumes a ref */ - /* Mark the request unsafe if this is the first timet's being sent. */ - - if (!req->r_sent && req->r_unsafe_callback) - req->r_unsafe_callback(req, true); req->r_sent = req->r_osd->o_incarnation; ceph_con_send(&req->r_osd->o_con, req->r_request); @@ -1431,8 +1427,6 @@ static void handle_osds_timeout(struct work_struct *work) static void complete_request(struct ceph_osd_request *req) { - if (req->r_unsafe_callback) - req->r_unsafe_callback(req, false); complete_all(&req->r_safe_completion); /* fsync waiter */ } @@ -1559,14 +1553,20 @@ static void handle_reply(struct ceph_osd_client *osdc, struct ceph_msg *msg, mutex_unlock(&osdc->request_mutex); if (!already_completed) { + if (req->r_unsafe_callback && + result >= 0 && !(flags & CEPH_OSD_FLAG_ONDISK)) + req->r_unsafe_callback(req, true); if (req->r_callback) req->r_callback(req, msg); else complete_all(&req->r_completion); } - if (flags & CEPH_OSD_FLAG_ONDISK) + if (flags & CEPH_OSD_FLAG_ONDISK) { + if (req->r_unsafe_callback && already_completed) + req->r_unsafe_callback(req, false); complete_request(req); + } done: dout("req=%p req->r_linger=%d\n", req, req->r_linger); -- cgit v1.2.3 From c846ef7deba2d4f75138cf6a4b137b7e0e7659af Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Wed, 3 Jul 2013 15:00:41 -0700 Subject: include/linux/smp.h:on_each_cpu(): switch back to a macro Commit f21afc25f9ed ("smp.h: Use local_irq_{save,restore}() in !SMP version of on_each_cpu()") converted on_each_cpu() to a C function. This required inclusion of irqflags.h, which broke ia64 and mn10300 (at least) due to header ordering hell. Switch on_each_cpu() back to a macro to fix this. Reported-by: Geert Uytterhoeven Acked-by: Geert Uytterhoeven Cc: David Daney Cc: Ralf Baechle Cc: Stephen Rothwell Cc: [3.10.x] Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/smp.h | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/include/linux/smp.h b/include/linux/smp.h index c8488763277f..c181399f2c20 100644 --- a/include/linux/smp.h +++ b/include/linux/smp.h @@ -11,7 +11,6 @@ #include #include #include -#include extern void cpu_idle(void); @@ -140,17 +139,14 @@ static inline int up_smp_call_function(smp_call_func_t func, void *info) } #define smp_call_function(func, info, wait) \ (up_smp_call_function(func, info)) - -static inline int on_each_cpu(smp_call_func_t func, void *info, int wait) -{ - unsigned long flags; - - local_irq_save(flags); - func(info); - local_irq_restore(flags); - return 0; -} - +#define on_each_cpu(func, info, wait) \ + ({ \ + unsigned long __flags; \ + local_irq_save(__flags); \ + func(info); \ + local_irq_restore(__flags); \ + 0; \ + }) /* * Note we still need to test the mask even for UP * because we actually can get an empty mask from -- cgit v1.2.3 From fe74650166dd6905b0cf66594eb79222dc9d7109 Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Wed, 3 Jul 2013 15:00:42 -0700 Subject: arch: c6x: mm: include "asm/uaccess.h" to pass compiling Need include "asm/uaccess.h" to pass compiling. The related error (with allmodconfig): arch/c6x/mm/init.c: In function `paging_init': arch/c6x/mm/init.c:46:2: error: implicit declaration of function `set_fs' [-Werror=implicit-function-declaration] arch/c6x/mm/init.c:46:9: error: `KERNEL_DS' undeclared (first use in this function) arch/c6x/mm/init.c:46:9: note: each undeclared identifier is reported only once for each function it appears in Signed-off-by: Chen Gang Cc: [3.10.x] Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/c6x/mm/init.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/c6x/mm/init.c b/arch/c6x/mm/init.c index a9fcd89b251b..b74ccb5a7690 100644 --- a/arch/c6x/mm/init.c +++ b/arch/c6x/mm/init.c @@ -18,6 +18,7 @@ #include #include +#include /* * ZERO_PAGE is a special page that is used for zero-initialized -- cgit v1.2.3 From da331ba8e9c5de72a27e50f71105395bba6eebe0 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 3 Jul 2013 15:00:43 -0700 Subject: drivers/dma/pl330.c: fix locking in pl330_free_chan_resources() tasklet_kill() may sleep so call it before taking pch->lock. Fixes following lockup: BUG: scheduling while atomic: cat/2383/0x00000002 Modules linked in: unwind_backtrace+0x0/0xfc __schedule_bug+0x4c/0x58 __schedule+0x690/0x6e0 sys_sched_yield+0x70/0x78 tasklet_kill+0x34/0x8c pl330_free_chan_resources+0x24/0x88 dma_chan_put+0x4c/0x50 [...] BUG: spinlock lockup suspected on CPU#0, swapper/0/0 lock: 0xe52aa04c, .magic: dead4ead, .owner: cat/2383, .owner_cpu: 1 unwind_backtrace+0x0/0xfc do_raw_spin_lock+0x194/0x204 _raw_spin_lock_irqsave+0x20/0x28 pl330_tasklet+0x2c/0x5a8 tasklet_action+0xfc/0x114 __do_softirq+0xe4/0x19c irq_exit+0x98/0x9c handle_IPI+0x124/0x16c gic_handle_irq+0x64/0x68 __irq_svc+0x40/0x70 cpuidle_wrap_enter+0x4c/0xa0 cpuidle_enter_state+0x18/0x68 cpuidle_idle_call+0xac/0xe0 cpu_idle+0xac/0xf0 Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Kyungmin Park Acked-by: Jassi Brar Cc: Vinod Koul Cc: Tomasz Figa Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/dma/pl330.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index a17553f7c028..7ec82f0667eb 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -2485,10 +2485,10 @@ static void pl330_free_chan_resources(struct dma_chan *chan) struct dma_pl330_chan *pch = to_pchan(chan); unsigned long flags; - spin_lock_irqsave(&pch->lock, flags); - tasklet_kill(&pch->task); + spin_lock_irqsave(&pch->lock, flags); + pl330_release_channel(pch->pl330_chid); pch->pl330_chid = NULL; -- cgit v1.2.3 From 3b1f9f53b18b2007803de17ab79cea736d5baf81 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Wed, 3 Jul 2013 15:00:44 -0700 Subject: sound/soc/codecs/si476x.c: don't use 0bNNN spacr64 gcc-3.4.5 (at least) spits this back. Cc: Andrey Smirnov Cc: Mark Brown Cc: Takashi Iwai Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- sound/soc/codecs/si476x.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/si476x.c b/sound/soc/codecs/si476x.c index 721587c9cd84..73e205c892a0 100644 --- a/sound/soc/codecs/si476x.c +++ b/sound/soc/codecs/si476x.c @@ -38,9 +38,9 @@ enum si476x_digital_io_output_format { SI476X_DIGITAL_IO_SAMPLE_SIZE_SHIFT = 8, }; -#define SI476X_DIGITAL_IO_OUTPUT_WIDTH_MASK ((0b111 << SI476X_DIGITAL_IO_SLOT_SIZE_SHIFT) | \ - (0b111 << SI476X_DIGITAL_IO_SAMPLE_SIZE_SHIFT)) -#define SI476X_DIGITAL_IO_OUTPUT_FORMAT_MASK (0b1111110) +#define SI476X_DIGITAL_IO_OUTPUT_WIDTH_MASK ((0x7 << SI476X_DIGITAL_IO_SLOT_SIZE_SHIFT) | \ + (0x7 << SI476X_DIGITAL_IO_SAMPLE_SIZE_SHIFT)) +#define SI476X_DIGITAL_IO_OUTPUT_FORMAT_MASK (0x7e) enum si476x_daudio_formats { SI476X_DAUDIO_MODE_I2S = (0x0 << 1), -- cgit v1.2.3 From 7121064b214bbc97007ec4d0e70aaeffef1b55f7 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 3 Jul 2013 15:00:45 -0700 Subject: configfs: use capped length for ->store_attribute() The difference between "count" and "len" is that "len" is capped at 4095. Changing it like this makes it match how sysfs_write_file() is implemented. This is a static analysis patch. I haven't found any store_attribute() functions where this change makes a difference. Signed-off-by: Dan Carpenter Acked-by: Joel Becker Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/configfs/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/configfs/file.c b/fs/configfs/file.c index 2b6cb23dd14e..1d1c41f1014d 100644 --- a/fs/configfs/file.c +++ b/fs/configfs/file.c @@ -203,7 +203,7 @@ configfs_write_file(struct file *file, const char __user *buf, size_t count, lof mutex_lock(&buffer->mutex); len = fill_write_buffer(buffer, buf, count); if (len > 0) - len = flush_write_buffer(file->f_path.dentry, buffer, count); + len = flush_write_buffer(file->f_path.dentry, buffer, len); if (len > 0) *ppos += len; mutex_unlock(&buffer->mutex); -- cgit v1.2.3 From 82d627cf1f41c6ca550bb404dfbc4bae2c954611 Mon Sep 17 00:00:00 2001 From: Joseph Qi Date: Wed, 3 Jul 2013 15:00:46 -0700 Subject: fs/ocfs2/dlm/dlmrecovery.c: remove duplicate declarations Below 3 functions have already been declared in dlmcommon.h, so we have no need to declare them again in dlmrecovery.c: dlm_complete_recovery_thread dlm_launch_recovery_thread dlm_kick_recovery_thread Signed-off-by: Joseph Qi Cc: Joel Becker Cc: Mark Fasheh Acked-by: Sunil Mushran Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/ocfs2/dlm/dlmrecovery.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/fs/ocfs2/dlm/dlmrecovery.c b/fs/ocfs2/dlm/dlmrecovery.c index e68588e6b1e8..3e18641626b4 100644 --- a/fs/ocfs2/dlm/dlmrecovery.c +++ b/fs/ocfs2/dlm/dlmrecovery.c @@ -55,9 +55,6 @@ static void dlm_do_local_recovery_cleanup(struct dlm_ctxt *dlm, u8 dead_node); static int dlm_recovery_thread(void *data); -void dlm_complete_recovery_thread(struct dlm_ctxt *dlm); -int dlm_launch_recovery_thread(struct dlm_ctxt *dlm); -void dlm_kick_recovery_thread(struct dlm_ctxt *dlm); static int dlm_do_recovery(struct dlm_ctxt *dlm); static int dlm_pick_recovery_master(struct dlm_ctxt *dlm); -- cgit v1.2.3 From 22ab9014bfdaf97449759aef946871aabe78bb40 Mon Sep 17 00:00:00 2001 From: Joseph Qi Date: Wed, 3 Jul 2013 15:00:47 -0700 Subject: fs/ocfs2/dlm/dlmrecovery.c:dlm_request_all_locks(): ret should be int instead of enum In dlm_request_all_locks, ret is type enum. But o2net_send_message returns a type int value. Then it will never run into the following error branch. So we should change the ret type from enum to int. Signed-off-by: Joseph Qi Cc: Joel Becker Cc: Mark Fasheh Acked-by: Sunil Mushran Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/ocfs2/dlm/dlmrecovery.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/ocfs2/dlm/dlmrecovery.c b/fs/ocfs2/dlm/dlmrecovery.c index 3e18641626b4..53e5c0284fa2 100644 --- a/fs/ocfs2/dlm/dlmrecovery.c +++ b/fs/ocfs2/dlm/dlmrecovery.c @@ -786,7 +786,7 @@ static int dlm_request_all_locks(struct dlm_ctxt *dlm, u8 request_from, u8 dead_node) { struct dlm_lock_request lr; - enum dlm_status ret; + int ret; mlog(0, "\n"); @@ -799,7 +799,6 @@ static int dlm_request_all_locks(struct dlm_ctxt *dlm, u8 request_from, lr.dead_node = dead_node; // send message - ret = DLM_NOLOCKMGR; ret = o2net_send_message(DLM_LOCK_REQUEST_MSG, dlm->key, &lr, sizeof(lr), request_from, NULL); -- cgit v1.2.3 From 13eb98874c837b4ca0cb2db6a4fe3551e51b7632 Mon Sep 17 00:00:00 2001 From: Joseph Qi Date: Wed, 3 Jul 2013 15:00:48 -0700 Subject: ocfs2: should not use le32_add_cpu to set ocfs2_dinode i_flags If we use le32_add_cpu to set ocfs2_dinode i_flags, it may lead to the corresponding flag corrupted. So we should change it to bitwise and/or operation. Signed-off-by: Joseph Qi Cc: Joel Becker Cc: Mark Fasheh Cc: shencanquan Reviewed-by: Jie Liu Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/ocfs2/namei.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c index b4a5cdf9dbc5..75964addfe32 100644 --- a/fs/ocfs2/namei.c +++ b/fs/ocfs2/namei.c @@ -522,7 +522,7 @@ static int __ocfs2_mknod_locked(struct inode *dir, fe->i_last_eb_blk = 0; strcpy(fe->i_signature, OCFS2_INODE_SIGNATURE); - le32_add_cpu(&fe->i_flags, OCFS2_VALID_FL); + fe->i_flags |= cpu_to_le32(OCFS2_VALID_FL); fe->i_atime = fe->i_ctime = fe->i_mtime = cpu_to_le64(CURRENT_TIME.tv_sec); fe->i_mtime_nsec = fe->i_ctime_nsec = fe->i_atime_nsec = @@ -2044,7 +2044,7 @@ static int ocfs2_orphan_add(struct ocfs2_super *osb, goto leave; } - le32_add_cpu(&fe->i_flags, OCFS2_ORPHANED_FL); + fe->i_flags |= cpu_to_le32(OCFS2_ORPHANED_FL); OCFS2_I(inode)->ip_flags &= ~OCFS2_INODE_SKIP_ORPHAN_DIR; /* Record which orphan dir our inode now resides @@ -2434,7 +2434,7 @@ int ocfs2_mv_orphaned_inode_to_new(struct inode *dir, } di = (struct ocfs2_dinode *)di_bh->b_data; - le32_add_cpu(&di->i_flags, -OCFS2_ORPHANED_FL); + di->i_flags &= ~cpu_to_le32(OCFS2_ORPHANED_FL); di->i_orphaned_slot = 0; set_nlink(inode, 1); ocfs2_set_links_count(di, inode->i_nlink); -- cgit v1.2.3 From 40c7f2eaf59230135fd91a398924bdbf873378ec Mon Sep 17 00:00:00 2001 From: Xue jiufei Date: Wed, 3 Jul 2013 15:00:50 -0700 Subject: ocfs2: add missing dlm_put() in dlm_begin_reco_handler() dlm_begin_reco_handler() returns without putting dlm when dlm recovery state is DLM_RECO_STATE_FINALIZE. Signed-off-by: joyce Reviewed-by: Jie Liu Acked-by: Joel Becker Cc: Mark Fasheh Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/ocfs2/dlm/dlmrecovery.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/ocfs2/dlm/dlmrecovery.c b/fs/ocfs2/dlm/dlmrecovery.c index 53e5c0284fa2..773bd32bfd8c 100644 --- a/fs/ocfs2/dlm/dlmrecovery.c +++ b/fs/ocfs2/dlm/dlmrecovery.c @@ -2692,6 +2692,7 @@ int dlm_begin_reco_handler(struct o2net_msg *msg, u32 len, void *data, dlm->name, br->node_idx, br->dead_node, dlm->reco.dead_node, dlm->reco.new_master); spin_unlock(&dlm->spinlock); + dlm_put(dlm); return -EAGAIN; } spin_unlock(&dlm->spinlock); -- cgit v1.2.3 From 8fa9d17f93ee8eb79d595c98cef0944f6ee5de75 Mon Sep 17 00:00:00 2001 From: Goldwyn Rodrigues Date: Wed, 3 Jul 2013 15:00:51 -0700 Subject: ocfs2: remove unecessary variable needs_checkpoint Code cleanup: needs_checkpoint is assigned to but never used. Delete the variable. Signed-off-by: Goldwyn Rodrigues Cc: Jeff Liu Acked-by: Joel Becker Cc: Mark Fasheh Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/ocfs2/journal.h | 1 - fs/ocfs2/ocfs2.h | 1 - fs/ocfs2/super.c | 6 ++---- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/fs/ocfs2/journal.h b/fs/ocfs2/journal.h index a3385b63ff5e..0a992737dcaf 100644 --- a/fs/ocfs2/journal.h +++ b/fs/ocfs2/journal.h @@ -200,7 +200,6 @@ void ocfs2_complete_quota_recovery(struct ocfs2_super *osb); static inline void ocfs2_start_checkpoint(struct ocfs2_super *osb) { - atomic_set(&osb->needs_checkpoint, 1); wake_up(&osb->checkpoint_event); } diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h index d355e6e36b36..3a903470c794 100644 --- a/fs/ocfs2/ocfs2.h +++ b/fs/ocfs2/ocfs2.h @@ -347,7 +347,6 @@ struct ocfs2_super struct task_struct *recovery_thread_task; int disable_recovery; wait_queue_head_t checkpoint_event; - atomic_t needs_checkpoint; struct ocfs2_journal *journal; unsigned long osb_commit_interval; diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index 01b85165552b..854d80955bf8 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -286,10 +286,9 @@ static int ocfs2_osb_dump(struct ocfs2_super *osb, char *buf, int len) spin_unlock(&osb->osb_lock); out += snprintf(buf + out, len - out, - "%10s => Pid: %d Interval: %lu Needs: %d\n", "Commit", + "%10s => Pid: %d Interval: %lu\n", "Commit", (osb->commit_task ? task_pid_nr(osb->commit_task) : -1), - osb->osb_commit_interval, - atomic_read(&osb->needs_checkpoint)); + osb->osb_commit_interval); out += snprintf(buf + out, len - out, "%10s => State: %d TxnId: %lu NumTxns: %d\n", @@ -2154,7 +2153,6 @@ static int ocfs2_initialize_super(struct super_block *sb, } init_waitqueue_head(&osb->checkpoint_event); - atomic_set(&osb->needs_checkpoint, 0); osb->s_atime_quantum = OCFS2_DEFAULT_ATIME_QUANTUM; -- cgit v1.2.3 From 33add0e3a09a62c7796ea9838243c1cd933d8543 Mon Sep 17 00:00:00 2001 From: Joseph Qi Date: Wed, 3 Jul 2013 15:00:52 -0700 Subject: ocfs2: fix mutex_unlock and possible memory leak in ocfs2_remove_btree_range In ocfs2_remove_btree_range, when calling ocfs2_lock_refcount_tree and ocfs2_prepare_refcount_change_for_del failed, it goes to out and then tries to call mutex_unlock without mutex_lock before. And when calling ocfs2_reserve_blocks_for_rec_trunc failed, it should free ref_tree before return. Signed-off-by: Joseph Qi Reviewed-by: Jie Liu Cc: Joel Becker Cc: Mark Fasheh Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/ocfs2/alloc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c index b8a9d87231b1..17e6bdde96c5 100644 --- a/fs/ocfs2/alloc.c +++ b/fs/ocfs2/alloc.c @@ -5655,7 +5655,7 @@ int ocfs2_remove_btree_range(struct inode *inode, &ref_tree, NULL); if (ret) { mlog_errno(ret); - goto out; + goto bail; } ret = ocfs2_prepare_refcount_change_for_del(inode, @@ -5666,7 +5666,7 @@ int ocfs2_remove_btree_range(struct inode *inode, &extra_blocks); if (ret < 0) { mlog_errno(ret); - goto out; + goto bail; } } @@ -5674,7 +5674,7 @@ int ocfs2_remove_btree_range(struct inode *inode, extra_blocks); if (ret) { mlog_errno(ret); - return ret; + goto bail; } mutex_lock(&tl_inode->i_mutex); @@ -5734,7 +5734,7 @@ out_commit: ocfs2_commit_trans(osb, handle); out: mutex_unlock(&tl_inode->i_mutex); - +bail: if (meta_ac) ocfs2_free_alloc_context(meta_ac); -- cgit v1.2.3 From 40bd62eb7fb8447798732e809a676ebaf2a7f910 Mon Sep 17 00:00:00 2001 From: Goldwyn Rodrigues Date: Wed, 3 Jul 2013 15:00:53 -0700 Subject: fs/ocfs2/journal.h: add bits_wanted while calculating credits in ocfs2_calc_extend_credits While adding extends to a file, the credits are calculated incorrectly and if the requested clusters is more than one (or more because we used a conservative limit) then we run out of journal credits and we hit an assert in journalling code. The function parameter bits_wanted variable was not used at all. Signed-off-by: Goldwyn Rodrigues Reviewed-by: Jie Liu Cc: Joel Becker Cc: Mark Fasheh Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/ocfs2/journal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ocfs2/journal.h b/fs/ocfs2/journal.h index 0a992737dcaf..96f9ac237e86 100644 --- a/fs/ocfs2/journal.h +++ b/fs/ocfs2/journal.h @@ -537,7 +537,7 @@ static inline int ocfs2_calc_extend_credits(struct super_block *sb, extent_blocks = 1 + 1 + le16_to_cpu(root_el->l_tree_depth); return bitmap_blocks + sysfile_bitmap_blocks + extent_blocks + - ocfs2_quota_trans_credits(sb); + ocfs2_quota_trans_credits(sb) + bits_wanted; } static inline int ocfs2_calc_symlink_credits(struct super_block *sb) -- cgit v1.2.3 From d3e3b41b3dffff50c66651d60146b155d6ce0484 Mon Sep 17 00:00:00 2001 From: Younger Liu Date: Wed, 3 Jul 2013 15:00:54 -0700 Subject: fs/ocfs2/cluster/tcp.c: free sc->sc_page in sc_kref_release() There is a memory leak in sc_kref_release(). When free struct o2net_sock_container (sc), we should release sc->sc_page. Signed-off-by: Younger Liu Reviewed-by: Jie Liu Cc: Joel Becker Cc: Mark Fasheh Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/ocfs2/cluster/tcp.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/ocfs2/cluster/tcp.c b/fs/ocfs2/cluster/tcp.c index aa88bd8bcedc..bb9a48bfbd01 100644 --- a/fs/ocfs2/cluster/tcp.c +++ b/fs/ocfs2/cluster/tcp.c @@ -406,6 +406,9 @@ static void sc_kref_release(struct kref *kref) sc->sc_node = NULL; o2net_debug_del_sc(sc); + + if (sc->sc_page) + __free_page(sc->sc_page); kfree(sc); } -- cgit v1.2.3 From b30f14c490d0e3ff1b7c0952ccd343408557484e Mon Sep 17 00:00:00 2001 From: Junxiao Bi Date: Wed, 3 Jul 2013 15:00:55 -0700 Subject: ocfs2: xattr: remove useless free space checking Free space checking will be done in ocfs2_xattr_ibody_init(). So remove here. [akpm@linux-foundation.org: remove unused local] Signed-off-by: Junxiao Bi Reviewed-by: Jie Liu Acked-by: Joel Becker Cc: Mark Fasheh Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/ocfs2/xattr.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c index 2e3ea308c144..0deabcdaf916 100644 --- a/fs/ocfs2/xattr.c +++ b/fs/ocfs2/xattr.c @@ -2751,20 +2751,12 @@ static int ocfs2_xattr_ibody_set(struct inode *inode, { int ret; struct ocfs2_inode_info *oi = OCFS2_I(inode); - struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data; struct ocfs2_xa_loc loc; if (inode->i_sb->s_blocksize == OCFS2_MIN_BLOCKSIZE) return -ENOSPC; down_write(&oi->ip_alloc_sem); - if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL)) { - if (!ocfs2_xattr_has_space_inline(inode, di)) { - ret = -ENOSPC; - goto out; - } - } - if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL)) { ret = ocfs2_xattr_ibody_init(inode, xs->inode_bh, ctxt); if (ret) { -- cgit v1.2.3 From 096b2ef83c87d7a7e0d2838f7f1391c74fdad0e6 Mon Sep 17 00:00:00 2001 From: Xue jiufei Date: Wed, 3 Jul 2013 15:00:56 -0700 Subject: ocfs2: dlmlock_master() should return DLM_NORMAL after adding lock to blocked list dlmlock_master() returns DLM_RECOVERING/DLM_MIGRATING/ DLM_FORWAR after adding lock to blocked list if lockres has the state DLM_LOCK_RES_RECOVERING/DLM_LOCK_RES_MIGRATING/ DLM_LOCK_RES_IN_PROGRESS. so it will retry in dlmlock(). And this may cause dlm_thread fall into an infinite loop Thread1 dlm_thread calls dlm_lock->dlmlock_master, if lockresA is in state DLM_LOCK_RES_RECOVERING, calls __dlm_wait_on_lockres() and waits until others threads clear this state; If cannot grant this lock, adding lock to blocked list, and return DLM_RECOVERING; Grant this lock and move it to grant list; After a while, retry and calls list_add_tail(), adding lock to blocked list again. Granted and blocked list of this lockres will become the following conditions: lock_res->granted.next = dlm_lock->list_head; lock_res->blocked.next = dlm_lock->list_head; dlm_lock->list_head.next = dlm_lock_resource->blocked; When dlm_thread traverses the granted list, it will fall into an endless loop, checking dlm_lock.list_head, dlm_lock->list_head.next (i.e.lock_res->blocked), lock_res->blocked.next(i.e.dlm_lock.list_head again) ..... Signed-off-by: joyce Reviewed-by: jensen Cc: Jeff Liu Acked-by: Sunil Mushran Cc: Mark Fasheh Cc: Joel Becker Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/ocfs2/dlm/dlmlock.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/ocfs2/dlm/dlmlock.c b/fs/ocfs2/dlm/dlmlock.c index 975810b98492..47e67c2d228f 100644 --- a/fs/ocfs2/dlm/dlmlock.c +++ b/fs/ocfs2/dlm/dlmlock.c @@ -178,6 +178,7 @@ static enum dlm_status dlmlock_master(struct dlm_ctxt *dlm, lock->ml.node); } } else { + status = DLM_NORMAL; dlm_lock_get(lock); list_add_tail(&lock->list, &res->blocked); kick_thread = 1; -- cgit v1.2.3 From ea45466aec98b68faaf354901c42e281e58af50a Mon Sep 17 00:00:00 2001 From: Younger Liu Date: Wed, 3 Jul 2013 15:00:58 -0700 Subject: ocfs2: need rollback when journal_access failed in ocfs2_orphan_add() While adding a file into orphan dir in ocfs2_orphan_add(), it calls __ocfs2_add_entry() before ocfs2_journal_access_di(). If ocfs2_journal_access_di() failed, the file is added into orphan dir, and orphan dir dinode updated, but file dinode has not been updated. Accordingly, the data is not consistent between file dinode and orphan dir. So, need to call ocfs2_journal_access_di() before __ocfs2_add_entry(), and if ocfs2_journal_access_di() failed, orphan_fe and orphan_dir_inode->i_nlink need rollback. This bug was added by 3939fda4 ("Ocfs2: Journaling i_flags and i_orphaned_slot when adding inode to orphan dir."). Signed-off-by: Younger Liu Acked-by: Jeff Liu Cc: Sunil Mushran Cc: Mark Fasheh Cc: Joel Becker Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/ocfs2/namei.c | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c index 75964addfe32..30842f5cf590 100644 --- a/fs/ocfs2/namei.c +++ b/fs/ocfs2/namei.c @@ -2012,6 +2012,21 @@ static int ocfs2_orphan_add(struct ocfs2_super *osb, goto leave; } + /* + * We're going to journal the change of i_flags and i_orphaned_slot. + * It's safe anyway, though some callers may duplicate the journaling. + * Journaling within the func just make the logic look more + * straightforward. + */ + status = ocfs2_journal_access_di(handle, + INODE_CACHE(inode), + fe_bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (status < 0) { + mlog_errno(status); + goto leave; + } + /* we're a cluster, and nlink can change on disk from * underneath us... */ orphan_fe = (struct ocfs2_dinode *) orphan_dir_bh->b_data; @@ -2026,22 +2041,7 @@ static int ocfs2_orphan_add(struct ocfs2_super *osb, orphan_dir_bh, lookup); if (status < 0) { mlog_errno(status); - goto leave; - } - - /* - * We're going to journal the change of i_flags and i_orphaned_slot. - * It's safe anyway, though some callers may duplicate the journaling. - * Journaling within the func just make the logic look more - * straightforward. - */ - status = ocfs2_journal_access_di(handle, - INODE_CACHE(inode), - fe_bh, - OCFS2_JOURNAL_ACCESS_WRITE); - if (status < 0) { - mlog_errno(status); - goto leave; + goto rollback; } fe->i_flags |= cpu_to_le32(OCFS2_ORPHANED_FL); @@ -2057,11 +2057,16 @@ static int ocfs2_orphan_add(struct ocfs2_super *osb, trace_ocfs2_orphan_add_end((unsigned long long)OCFS2_I(inode)->ip_blkno, osb->slot_num); +rollback: + if (status < 0) { + if (S_ISDIR(inode->i_mode)) + ocfs2_add_links_count(orphan_fe, -1); + set_nlink(orphan_dir_inode, ocfs2_read_links_count(orphan_fe)); + } + leave: brelse(orphan_dir_bh); - if (status) - mlog_errno(status); return status; } -- cgit v1.2.3 From 493098413bdb45f223ff0552e2f734849491dbbe Mon Sep 17 00:00:00 2001 From: Jie Liu Date: Wed, 3 Jul 2013 15:00:59 -0700 Subject: ocfs2: rework transaction rollback in ocfs2_relink_block_group() In ocfs2_relink_block_group(), we roll back all those changes if notify intent to modify buffers for metadata update failed even if the relevant buffer has not yet been modified/got dirty at that point, that are not quite right because of: - None buffer has been modified/dirty if failed to call ocfs2_journal_access_gd() against the previous block group buffer - Only the previous block group buffer has got dirty if failed to call ocfs2_journal_access_gd() against the block group buffer - There is no need to roll back the change for file entry buffer at all Those problems will not cause anything wrong but unnecessary. This patch fix them and kill the useless bg_ptr variable as well. Signed-off-by: Jie Liu Cc: Younger Liu Cc: Sunil Mushran Cc: Mark Fasheh Cc: Joel Becker Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/ocfs2/suballoc.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/fs/ocfs2/suballoc.c b/fs/ocfs2/suballoc.c index b7e74b580c0f..101d16d78162 100644 --- a/fs/ocfs2/suballoc.c +++ b/fs/ocfs2/suballoc.c @@ -1422,7 +1422,7 @@ static int ocfs2_relink_block_group(handle_t *handle, int status; /* there is a really tiny chance the journal calls could fail, * but we wouldn't want inconsistent blocks in *any* case. */ - u64 fe_ptr, bg_ptr, prev_bg_ptr; + u64 bg_ptr, prev_bg_ptr; struct ocfs2_dinode *fe = (struct ocfs2_dinode *) fe_bh->b_data; struct ocfs2_group_desc *bg = (struct ocfs2_group_desc *) bg_bh->b_data; struct ocfs2_group_desc *prev_bg = (struct ocfs2_group_desc *) prev_bg_bh->b_data; @@ -1437,7 +1437,6 @@ static int ocfs2_relink_block_group(handle_t *handle, (unsigned long long)le64_to_cpu(bg->bg_blkno), (unsigned long long)le64_to_cpu(prev_bg->bg_blkno)); - fe_ptr = le64_to_cpu(fe->id2.i_chain.cl_recs[chain].c_blkno); bg_ptr = le64_to_cpu(bg->bg_next_group); prev_bg_ptr = le64_to_cpu(prev_bg->bg_next_group); @@ -1446,7 +1445,7 @@ static int ocfs2_relink_block_group(handle_t *handle, OCFS2_JOURNAL_ACCESS_WRITE); if (status < 0) { mlog_errno(status); - goto out_rollback; + goto out; } prev_bg->bg_next_group = bg->bg_next_group; @@ -1456,7 +1455,7 @@ static int ocfs2_relink_block_group(handle_t *handle, bg_bh, OCFS2_JOURNAL_ACCESS_WRITE); if (status < 0) { mlog_errno(status); - goto out_rollback; + goto out_rollback_prev_bg; } bg->bg_next_group = fe->id2.i_chain.cl_recs[chain].c_blkno; @@ -1466,21 +1465,21 @@ static int ocfs2_relink_block_group(handle_t *handle, fe_bh, OCFS2_JOURNAL_ACCESS_WRITE); if (status < 0) { mlog_errno(status); - goto out_rollback; + goto out_rollback_bg; } fe->id2.i_chain.cl_recs[chain].c_blkno = bg->bg_blkno; ocfs2_journal_dirty(handle, fe_bh); -out_rollback: - if (status < 0) { - fe->id2.i_chain.cl_recs[chain].c_blkno = cpu_to_le64(fe_ptr); - bg->bg_next_group = cpu_to_le64(bg_ptr); - prev_bg->bg_next_group = cpu_to_le64(prev_bg_ptr); - } +out: + return status; - if (status) - mlog_errno(status); +out_rollback_bg: + bg->bg_next_group = cpu_to_le64(bg_ptr); +out_rollback_prev_bg: + prev_bg->bg_next_group = cpu_to_le64(prev_bg_ptr); + + mlog_errno(status); return status; } -- cgit v1.2.3 From 25e2892101ba541dce8593c698d70ccc278bc1fd Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Wed, 3 Jul 2013 15:01:00 -0700 Subject: ocfs2: remove duplicated mlog_errno() in ocfs2_relink_block_group Cc: Jie Liu Cc: Joel Becker Cc: Mark Fasheh Cc: Sunil Mushran Cc: Younger Liu Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/ocfs2/suballoc.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/fs/ocfs2/suballoc.c b/fs/ocfs2/suballoc.c index 101d16d78162..5397c07ce608 100644 --- a/fs/ocfs2/suballoc.c +++ b/fs/ocfs2/suballoc.c @@ -1443,44 +1443,38 @@ static int ocfs2_relink_block_group(handle_t *handle, status = ocfs2_journal_access_gd(handle, INODE_CACHE(alloc_inode), prev_bg_bh, OCFS2_JOURNAL_ACCESS_WRITE); - if (status < 0) { - mlog_errno(status); + if (status < 0) goto out; - } prev_bg->bg_next_group = bg->bg_next_group; ocfs2_journal_dirty(handle, prev_bg_bh); status = ocfs2_journal_access_gd(handle, INODE_CACHE(alloc_inode), bg_bh, OCFS2_JOURNAL_ACCESS_WRITE); - if (status < 0) { - mlog_errno(status); + if (status < 0) goto out_rollback_prev_bg; - } bg->bg_next_group = fe->id2.i_chain.cl_recs[chain].c_blkno; ocfs2_journal_dirty(handle, bg_bh); status = ocfs2_journal_access_di(handle, INODE_CACHE(alloc_inode), fe_bh, OCFS2_JOURNAL_ACCESS_WRITE); - if (status < 0) { - mlog_errno(status); + if (status < 0) goto out_rollback_bg; - } fe->id2.i_chain.cl_recs[chain].c_blkno = bg->bg_blkno; ocfs2_journal_dirty(handle, fe_bh); out: + if (status < 0) + mlog_errno(status); return status; out_rollback_bg: bg->bg_next_group = cpu_to_le64(bg_ptr); out_rollback_prev_bg: prev_bg->bg_next_group = cpu_to_le64(prev_bg_ptr); - - mlog_errno(status); - return status; + goto out; } static inline int ocfs2_block_group_reasonably_empty(struct ocfs2_group_desc *bg, -- cgit v1.2.3 From b5a8bb717e29b549584cc27ac8e109a803c4f8c4 Mon Sep 17 00:00:00 2001 From: Younger Liu Date: Wed, 3 Jul 2013 15:01:01 -0700 Subject: ocfs2: fix readonly issue in ocfs2_unlink() While deleting a file with ocfs2_unlink(), there is a bug in this function. This bug will result in filesystem read-only. After calling ocfs2_orphan_add(), the file which will be deleted is added into orphan dir. If ocfs2_delete_entry() fails, the file still exists in the parent dir. And this scenario introduces a conflict of metadata. If a file is added into orphan dir, when we put inode of the file with iput(), the inode i_flags is setted (~OCFS2_VALID_FL) in ocfs2_remove_inode(), and then write back to disk. But as previously mentioned, the file still exists in the parent dir. On other nodes, the file can be still accessed. When first read the file with ocfs2_read_blocks() from disk, It will check and avalidate inode using ocfs2_validate_inode_block(). So File system will be readonly because the inode is invalid. In other words, the inode i_flags has been set (~OCFS2_VALID_FL). [akpm@linux-foundation.org: cleanups] [jeff.liu@oracle.com: s/inode_is_unlinkable/ocfs2_inode_is_unlinkable/] Signed-off-by: Younger Liu Signed-off-by: Jensen Cc: Jie Liu Cc: Joel Becker Cc: Mark Fasheh Cc: Sunil Mushran Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/ocfs2/namei.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c index 30842f5cf590..be3f8676a438 100644 --- a/fs/ocfs2/namei.c +++ b/fs/ocfs2/namei.c @@ -773,7 +773,7 @@ static int ocfs2_remote_dentry_delete(struct dentry *dentry) return ret; } -static inline int inode_is_unlinkable(struct inode *inode) +static inline int ocfs2_inode_is_unlinkable(struct inode *inode) { if (S_ISDIR(inode->i_mode)) { if (inode->i_nlink == 2) @@ -791,6 +791,7 @@ static int ocfs2_unlink(struct inode *dir, { int status; int child_locked = 0; + bool is_unlinkable = false; struct inode *inode = dentry->d_inode; struct inode *orphan_dir = NULL; struct ocfs2_super *osb = OCFS2_SB(dir->i_sb); @@ -865,7 +866,7 @@ static int ocfs2_unlink(struct inode *dir, goto leave; } - if (inode_is_unlinkable(inode)) { + if (ocfs2_inode_is_unlinkable(inode)) { status = ocfs2_prepare_orphan_dir(osb, &orphan_dir, OCFS2_I(inode)->ip_blkno, orphan_name, &orphan_insert); @@ -873,6 +874,7 @@ static int ocfs2_unlink(struct inode *dir, mlog_errno(status); goto leave; } + is_unlinkable = true; } handle = ocfs2_start_trans(osb, ocfs2_unlink_credits(osb->sb)); @@ -892,15 +894,6 @@ static int ocfs2_unlink(struct inode *dir, fe = (struct ocfs2_dinode *) fe_bh->b_data; - if (inode_is_unlinkable(inode)) { - status = ocfs2_orphan_add(osb, handle, inode, fe_bh, orphan_name, - &orphan_insert, orphan_dir); - if (status < 0) { - mlog_errno(status); - goto leave; - } - } - /* delete the name from the parent dir */ status = ocfs2_delete_entry(handle, dir, &lookup); if (status < 0) { @@ -923,6 +916,14 @@ static int ocfs2_unlink(struct inode *dir, mlog_errno(status); if (S_ISDIR(inode->i_mode)) inc_nlink(dir); + goto leave; + } + + if (is_unlinkable) { + status = ocfs2_orphan_add(osb, handle, inode, fe_bh, + orphan_name, &orphan_insert, orphan_dir); + if (status < 0) + mlog_errno(status); } leave: -- cgit v1.2.3 From ef962df057aaafd714f5c22ba3de1be459571fdf Mon Sep 17 00:00:00 2001 From: Junxiao Bi Date: Wed, 3 Jul 2013 15:01:03 -0700 Subject: ocfs2: xattr: fix inlined xattr reflink Inlined xattr shared free space of inode block with inlined data or data extent record, so the size of the later two should be adjusted when inlined xattr is enabled. See ocfs2_xattr_ibody_init(). But this isn't done well when reflink. For inode with inlined data, its max inlined data size is adjusted in ocfs2_duplicate_inline_data(), no problem. But for inode with data extent record, its record count isn't adjusted. Fix it, or data extent record and inlined xattr may overwrite each other, then cause data corruption or xattr failure. One panic caused by this bug in our test environment is the following: kernel BUG at fs/ocfs2/xattr.c:1435! invalid opcode: 0000 [#1] SMP Pid: 10871, comm: multi_reflink_t Not tainted 2.6.39-300.17.1.el5uek #1 RIP: ocfs2_xa_offset_pointer+0x17/0x20 [ocfs2] RSP: e02b:ffff88007a587948 EFLAGS: 00010283 RAX: 0000000000000000 RBX: 0000000000000010 RCX: 00000000000051e4 RDX: ffff880057092060 RSI: 0000000000000f80 RDI: ffff88007a587a68 RBP: ffff88007a587948 R08: 00000000000062f4 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000010 R13: ffff88007a587a68 R14: 0000000000000001 R15: ffff88007a587c68 FS: 00007fccff7f06e0(0000) GS:ffff88007fc00000(0000) knlGS:0000000000000000 CS: e033 DS: 0000 ES: 0000 CR0: 000000008005003b CR2: 00000000015cf000 CR3: 000000007aa76000 CR4: 0000000000000660 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 Process multi_reflink_t Call Trace: ocfs2_xa_reuse_entry+0x60/0x280 [ocfs2] ocfs2_xa_prepare_entry+0x17e/0x2a0 [ocfs2] ocfs2_xa_set+0xcc/0x250 [ocfs2] ocfs2_xattr_ibody_set+0x98/0x230 [ocfs2] __ocfs2_xattr_set_handle+0x4f/0x700 [ocfs2] ocfs2_xattr_set+0x6c6/0x890 [ocfs2] ocfs2_xattr_user_set+0x46/0x50 [ocfs2] generic_setxattr+0x70/0x90 __vfs_setxattr_noperm+0x80/0x1a0 vfs_setxattr+0xa9/0xb0 setxattr+0xc3/0x120 sys_fsetxattr+0xa8/0xd0 system_call_fastpath+0x16/0x1b Signed-off-by: Junxiao Bi Reviewed-by: Jie Liu Acked-by: Joel Becker Cc: Mark Fasheh Cc: Sunil Mushran Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/ocfs2/xattr.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c index 0deabcdaf916..317ef0abccbb 100644 --- a/fs/ocfs2/xattr.c +++ b/fs/ocfs2/xattr.c @@ -6491,6 +6491,16 @@ static int ocfs2_reflink_xattr_inline(struct ocfs2_xattr_reflink *args) } new_oi = OCFS2_I(args->new_inode); + /* + * Adjust extent record count to reserve space for extended attribute. + * Inline data count had been adjusted in ocfs2_duplicate_inline_data(). + */ + if (!(new_oi->ip_dyn_features & OCFS2_INLINE_DATA_FL) && + !(ocfs2_inode_is_fast_symlink(args->new_inode))) { + struct ocfs2_extent_list *el = &new_di->id2.i_list; + le16_add_cpu(&el->l_count, -(inline_size / + sizeof(struct ocfs2_extent_rec))); + } spin_lock(&new_oi->ip_lock); new_oi->ip_dyn_features |= OCFS2_HAS_XATTR_FL | OCFS2_INLINE_XATTR_FL; new_di->i_dyn_features = cpu_to_le16(new_oi->ip_dyn_features); -- cgit v1.2.3 From e873fdb5259ac62cf099621590645470c12a1d21 Mon Sep 17 00:00:00 2001 From: Noboru Iwamatsu Date: Wed, 3 Jul 2013 15:01:04 -0700 Subject: ocfs2: submit disk heartbeat bio using WRITE_SYNC Under heavy I/O load, writing the disk heartbeat can be forced to wait for minutes, and this causes the node to be fenced. This patch tries to use WRITE_SYNC in submitting the heartbeat bio, so that writing the heartbeat will have a priority over other requests. Signed-off-by: Noboru Iwamatsu Acked-by: Tao Ma Acked-by: Sunil Mushran Cc: Srinivas Eeeda Reviewed-by: Jie Liu Tested-by: Gurudas Pai Cc: Joel Becker Cc: Mark Fasheh Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/ocfs2/cluster/heartbeat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c index 42252bf64b51..f89b46b66235 100644 --- a/fs/ocfs2/cluster/heartbeat.c +++ b/fs/ocfs2/cluster/heartbeat.c @@ -500,7 +500,7 @@ static int o2hb_issue_node_write(struct o2hb_region *reg, } atomic_inc(&write_wc->wc_num_reqs); - submit_bio(WRITE, bio); + submit_bio(WRITE_SYNC, bio); status = 0; bail: -- cgit v1.2.3 From 70f651edb75b68e3c039d137a56be84f53211558 Mon Sep 17 00:00:00 2001 From: Jie Liu Date: Wed, 3 Jul 2013 15:01:06 -0700 Subject: ocfs2: consolidate o2hb_global_hearbeat_mode_set() naming convention s/o2hb_global_hearbeat_mode_set/o2hb_global_heartbeat_mode_set/ to make the signature of those routines in a consistent manner with others for heartbeating. Signed-off-by: Jie Liu Acked-by: Sunil Mushran Cc: Gurudas Pai Cc: Joel Becker Cc: Mark Fasheh Cc: Noboru Iwamatsu Cc: Srinivas Eeeda Cc: Tao Ma Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/ocfs2/cluster/heartbeat.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c index f89b46b66235..956d79d1fdde 100644 --- a/fs/ocfs2/cluster/heartbeat.c +++ b/fs/ocfs2/cluster/heartbeat.c @@ -176,7 +176,7 @@ static void o2hb_dead_threshold_set(unsigned int threshold) } } -static int o2hb_global_hearbeat_mode_set(unsigned int hb_mode) +static int o2hb_global_heartbeat_mode_set(unsigned int hb_mode) { int ret = -1; @@ -2271,7 +2271,7 @@ ssize_t o2hb_heartbeat_group_mode_store(struct o2hb_heartbeat_group *group, if (strnicmp(page, o2hb_heartbeat_mode_desc[i], len)) continue; - ret = o2hb_global_hearbeat_mode_set(i); + ret = o2hb_global_heartbeat_mode_set(i); if (!ret) printk(KERN_NOTICE "o2hb: Heartbeat mode set to %s\n", o2hb_heartbeat_mode_desc[i]); @@ -2304,7 +2304,7 @@ static struct configfs_attribute *o2hb_heartbeat_group_attrs[] = { NULL, }; -static struct configfs_item_operations o2hb_hearbeat_group_item_ops = { +static struct configfs_item_operations o2hb_heartbeat_group_item_ops = { .show_attribute = o2hb_heartbeat_group_show, .store_attribute = o2hb_heartbeat_group_store, }; @@ -2316,7 +2316,7 @@ static struct configfs_group_operations o2hb_heartbeat_group_group_ops = { static struct config_item_type o2hb_heartbeat_group_type = { .ct_group_ops = &o2hb_heartbeat_group_group_ops, - .ct_item_ops = &o2hb_hearbeat_group_item_ops, + .ct_item_ops = &o2hb_heartbeat_group_item_ops, .ct_attrs = o2hb_heartbeat_group_attrs, .ct_owner = THIS_MODULE, }; -- cgit v1.2.3 From b4d8ed4f8e527751c7ece6cef73f6212808e6130 Mon Sep 17 00:00:00 2001 From: Jie Liu Date: Wed, 3 Jul 2013 15:01:07 -0700 Subject: ocfs2: fix a comments typo at o2quo_hb_still_up() Fix a comment typo in o2quo_hb_still_up() Signed-off-by: Jie Liu Cc: Gurudas Pai Cc: Joel Becker Cc: Mark Fasheh Cc: Noboru Iwamatsu Cc: Srinivas Eeeda Cc: Sunil Mushran Cc: Tao Ma Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/ocfs2/cluster/quorum.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ocfs2/cluster/quorum.c b/fs/ocfs2/cluster/quorum.c index c19897d0fe14..1ec141e758d7 100644 --- a/fs/ocfs2/cluster/quorum.c +++ b/fs/ocfs2/cluster/quorum.c @@ -264,7 +264,7 @@ void o2quo_hb_still_up(u8 node) /* This is analogous to hb_up. as a node's connection comes up we delay the * quorum decision until we see it heartbeating. the hold will be droped in * hb_up or hb_down. it might be perpetuated by con_err until hb_down. if - * it's already heartbeating we we might be dropping a hold that conn_up got. + * it's already heartbeating we might be dropping a hold that conn_up got. * */ void o2quo_conn_up(u8 node) { -- cgit v1.2.3 From 44e89cb8e279abdf83581a10e592c2451bae7824 Mon Sep 17 00:00:00 2001 From: Jie Liu Date: Wed, 3 Jul 2013 15:01:08 -0700 Subject: ocfs2: adjust switch_case syntax at o2net_state_change() Adjust switch..case syntax at o2net_state_change to meet the kernel coding standard. s/printk/pr_info/. [akpm@linux-foundation.org: revert pr_foo() change] Signed-off-by: Jie Liu Acked-by: Joel Becker Cc: Gurudas Pai Cc: Mark Fasheh Cc: Noboru Iwamatsu Cc: Srinivas Eeeda Cc: Sunil Mushran Cc: Tao Ma Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/ocfs2/cluster/tcp.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/fs/ocfs2/cluster/tcp.c b/fs/ocfs2/cluster/tcp.c index bb9a48bfbd01..d644dc611425 100644 --- a/fs/ocfs2/cluster/tcp.c +++ b/fs/ocfs2/cluster/tcp.c @@ -633,19 +633,19 @@ static void o2net_state_change(struct sock *sk) state_change = sc->sc_state_change; switch(sk->sk_state) { - /* ignore connecting sockets as they make progress */ - case TCP_SYN_SENT: - case TCP_SYN_RECV: - break; - case TCP_ESTABLISHED: - o2net_sc_queue_work(sc, &sc->sc_connect_work); - break; - default: - printk(KERN_INFO "o2net: Connection to " SC_NODEF_FMT - " shutdown, state %d\n", - SC_NODEF_ARGS(sc), sk->sk_state); - o2net_sc_queue_work(sc, &sc->sc_shutdown_work); - break; + /* ignore connecting sockets as they make progress */ + case TCP_SYN_SENT: + case TCP_SYN_RECV: + break; + case TCP_ESTABLISHED: + o2net_sc_queue_work(sc, &sc->sc_connect_work); + break; + default: + printk(KERN_INFO "o2net: Connection to " SC_NODEF_FMT + " shutdown, state %d\n", + SC_NODEF_ARGS(sc), sk->sk_state); + o2net_sc_queue_work(sc, &sc->sc_shutdown_work); + break; } out: read_unlock(&sk->sk_callback_lock); -- cgit v1.2.3 From 4a184b4ff424e544359f081087723fc36efe603e Mon Sep 17 00:00:00 2001 From: Xue jiufei Date: Wed, 3 Jul 2013 15:01:10 -0700 Subject: ocfs2: fix NULL pointer dereference when traversing o2hb_all_regions There may exist NULL pointer dereference in config_item_name() when one volume (say Volume A) unmounts while another (say Volume B) mounting. Volume A Volume B already Mounted. Unmounting, call o2hb_heartbeat_group_drop_item() -> config_item_put(item) set reg(A)->item.ci_name to NULL in function config_item_cleanup(). begin mounting, call o2hb_region_pin() and tranverse all regions. When reading reg(A)->item.ci_name, it causes NULL pointer dereference. call o2hb_region_release() and del reg(A) from list. So we should skip accessing regions that is going to release when tranverse o2hb_all_regions. Signed-off-by: Yiwen Jiang Signed-off-by: joyce Acked-by: Joel Becker Cc: Mark Fasheh Cc: Sunil Mushran Cc: Jie Liu Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/ocfs2/cluster/heartbeat.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fs/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c index 956d79d1fdde..5c1c864e81cc 100644 --- a/fs/ocfs2/cluster/heartbeat.c +++ b/fs/ocfs2/cluster/heartbeat.c @@ -2389,6 +2389,9 @@ static int o2hb_region_pin(const char *region_uuid) assert_spin_locked(&o2hb_live_lock); list_for_each_entry(reg, &o2hb_all_regions, hr_all_item) { + if (reg->hr_item_dropped) + continue; + uuid = config_item_name(®->hr_item); /* local heartbeat */ @@ -2439,6 +2442,9 @@ static void o2hb_region_unpin(const char *region_uuid) assert_spin_locked(&o2hb_live_lock); list_for_each_entry(reg, &o2hb_all_regions, hr_all_item) { + if (reg->hr_item_dropped) + continue; + uuid = config_item_name(®->hr_item); if (region_uuid) { if (strcmp(region_uuid, uuid)) @@ -2654,6 +2660,9 @@ int o2hb_get_all_regions(char *region_uuids, u8 max_regions) p = region_uuids; list_for_each_entry(reg, &o2hb_all_regions, hr_all_item) { + if (reg->hr_item_dropped) + continue; + mlog(0, "Region: %s\n", config_item_name(®->hr_item)); if (numregs < max_regions) { memcpy(p, config_item_name(®->hr_item), -- cgit v1.2.3 From 31bd8fbb41b1fdf61f80e3e506574b43fad5e478 Mon Sep 17 00:00:00 2001 From: Libo Chen Date: Wed, 3 Jul 2013 15:01:11 -0700 Subject: drivers/cdrom/gdrom.c: fix device number leak Without this patch, gdrom_major will leak when gd.cd_info alloc fails. Signed-off-by: Libo Chen Cc: Jens Axboe Acked-by: Tejun Heo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/cdrom/gdrom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cdrom/gdrom.c b/drivers/cdrom/gdrom.c index 4afcb65cc623..5980cb9af857 100644 --- a/drivers/cdrom/gdrom.c +++ b/drivers/cdrom/gdrom.c @@ -830,9 +830,9 @@ probe_fail_cdrom_register: del_gendisk(gd.disk); probe_fail_no_disk: kfree(gd.cd_info); +probe_fail_no_mem: unregister_blkdev(gdrom_major, GDROM_DEV_NAME); gdrom_major = 0; -probe_fail_no_mem: pr_warning("Probe failed - error is 0x%X\n", err); return err; } -- cgit v1.2.3 From 8b0d77f13192678019f07cbc6b3338d6d91f1cf4 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Wed, 3 Jul 2013 15:01:12 -0700 Subject: block/compat_ioctl.c: do not leak info to user-space There is a hole in struct hd_geometry, so we have to zero the struct on stack before copying it to user-space. Signed-off-by: Cong Wang Cc: Jens Axboe Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- block/compat_ioctl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/block/compat_ioctl.c b/block/compat_ioctl.c index 7c668c8a6f95..7e5d474dc6ba 100644 --- a/block/compat_ioctl.c +++ b/block/compat_ioctl.c @@ -59,6 +59,7 @@ static int compat_hdio_getgeo(struct gendisk *disk, struct block_device *bdev, if (!disk->fops->getgeo) return -ENOTTY; + memset(&geo, 0, sizeof(geo)); /* * We need to set the startsect first, the driver may * want to override it. -- cgit v1.2.3 From 542db01579fbb7ea7d1f7bb9ddcef1559df660b2 Mon Sep 17 00:00:00 2001 From: Jonathan Salwan Date: Wed, 3 Jul 2013 15:01:13 -0700 Subject: drivers/cdrom/cdrom.c: use kzalloc() for failing hardware In drivers/cdrom/cdrom.c mmc_ioctl_cdrom_read_data() allocates a memory area with kmalloc in line 2885. 2885 cgc->buffer = kmalloc(blocksize, GFP_KERNEL); 2886 if (cgc->buffer == NULL) 2887 return -ENOMEM; In line 2908 we can find the copy_to_user function: 2908 if (!ret && copy_to_user(arg, cgc->buffer, blocksize)) The cgc->buffer is never cleaned and initialized before this function. If ret = 0 with the previous basic block, it's possible to display some memory bytes in kernel space from userspace. When we read a block from the disk it normally fills the ->buffer but if the drive is malfunctioning there is a chance that it would only be partially filled. The result is an leak information to userspace. Signed-off-by: Dan Carpenter Cc: Jens Axboe Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/cdrom/cdrom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index d620b4495745..8a3aff724d98 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -2882,7 +2882,7 @@ static noinline int mmc_ioctl_cdrom_read_data(struct cdrom_device_info *cdi, if (lba < 0) return -EINVAL; - cgc->buffer = kmalloc(blocksize, GFP_KERNEL); + cgc->buffer = kzalloc(blocksize, GFP_KERNEL); if (cgc->buffer == NULL) return -ENOMEM; -- cgit v1.2.3 From ffc8b30866879ed9ba62bd0a86fecdbd51cd3d19 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 3 Jul 2013 15:01:14 -0700 Subject: block: do not pass disk names as format strings Disk names may contain arbitrary strings, so they must not be interpreted as format strings. It seems that only md allows arbitrary strings to be used for disk names, but this could allow for a local memory corruption from uid 0 into ring 0. CVE-2013-2851 Signed-off-by: Kees Cook Cc: Jens Axboe Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- block/genhd.c | 2 +- drivers/block/nbd.c | 3 ++- drivers/scsi/osd/osd_uld.c | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/block/genhd.c b/block/genhd.c index e9094b375c05..dadf42b454a3 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -512,7 +512,7 @@ static void register_disk(struct gendisk *disk) ddev->parent = disk->driverfs_dev; - dev_set_name(ddev, disk->disk_name); + dev_set_name(ddev, "%s", disk->disk_name); /* delay uevents, until we scanned partition table */ dev_set_uevent_suppress(ddev, 1); diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 037288e7874d..46b35f7acfde 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -714,7 +714,8 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd, else blk_queue_flush(nbd->disk->queue, 0); - thread = kthread_create(nbd_thread, nbd, nbd->disk->disk_name); + thread = kthread_create(nbd_thread, nbd, "%s", + nbd->disk->disk_name); if (IS_ERR(thread)) { mutex_lock(&nbd->tx_lock); return PTR_ERR(thread); diff --git a/drivers/scsi/osd/osd_uld.c b/drivers/scsi/osd/osd_uld.c index 0fab6b5c7b82..9d86947d67fe 100644 --- a/drivers/scsi/osd/osd_uld.c +++ b/drivers/scsi/osd/osd_uld.c @@ -485,7 +485,7 @@ static int osd_probe(struct device *dev) oud->class_dev.class = &osd_uld_class; oud->class_dev.parent = dev; oud->class_dev.release = __remove; - error = dev_set_name(&oud->class_dev, disk->disk_name); + error = dev_set_name(&oud->class_dev, "%s", disk->disk_name); if (error) { OSD_ERR("dev_set_name failed => %d\n", error); goto err_put_cdev; -- cgit v1.2.3 From 1c8fca1d92e14859159a82b8a380d220139b7344 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 3 Jul 2013 15:01:15 -0700 Subject: crypto: sanitize argument for format string The template lookup interface does not provide a way to use format strings, so make sure that the interface cannot be abused accidentally. Signed-off-by: Kees Cook Cc: Herbert Xu Cc: "David S. Miller" Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- crypto/algapi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crypto/algapi.c b/crypto/algapi.c index 6149a6e09643..7a1ae87f1683 100644 --- a/crypto/algapi.c +++ b/crypto/algapi.c @@ -495,7 +495,8 @@ static struct crypto_template *__crypto_lookup_template(const char *name) struct crypto_template *crypto_lookup_template(const char *name) { - return try_then_request_module(__crypto_lookup_template(name), name); + return try_then_request_module(__crypto_lookup_template(name), "%s", + name); } EXPORT_SYMBOL_GPL(crypto_lookup_template); -- cgit v1.2.3 From 040fa02077de01c7e08fa75be6125e4ca5636011 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Wed, 3 Jul 2013 15:01:16 -0700 Subject: clear_refs: sanitize accepted commands declaration This is the implementation of the soft-dirty bit concept that should help keep track of changes in user memory, which in turn is very-very required by the checkpoint-restore project (http://criu.org). To create a dump of an application(s) we save all the information about it to files, and the biggest part of such dump is the contents of tasks' memory. However, there are usage scenarios where it's not required to get _all_ the task memory while creating a dump. For example, when doing periodical dumps, it's only required to take full memory dump only at the first step and then take incremental changes of memory. Another example is live migration. We copy all the memory to the destination node without stopping all tasks, then stop them, check for what pages has changed, dump it and the rest of the state, then copy it to the destination node. This decreases freeze time significantly. That said, some help from kernel to watch how processes modify the contents of their memory is required. The proposal is to track changes with the help of new soft-dirty bit this way: 1. First do "echo 4 > /proc/$pid/clear_refs". At that point kernel clears the soft dirty _and_ the writable bits from all ptes of process $pid. From now on every write to any page will result in #pf and the subsequent call to pte_mkdirty/pmd_mkdirty, which in turn will set the soft dirty flag. 2. Then read the /proc/$pid/pagemap2 and check the soft-dirty bit reported there (the 55'th one). If set, the respective pte was written to since last call to clear refs. The soft-dirty bit is the _PAGE_BIT_HIDDEN one. Although it's used by kmemcheck, the latter one marks kernel pages with it, while the former bit is put on user pages so they do not conflict to each other. This patch: A new clear-refs type will be added in the next patch, so prepare code for that. [akpm@linux-foundation.org: don't assume that sizeof(enum clear_refs_types) == sizeof(int)] Signed-off-by: Pavel Emelyanov Cc: Matt Mackall Cc: Xiao Guangrong Cc: Glauber Costa Cc: Marcelo Tosatti Cc: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/proc/task_mmu.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 3e636d864d56..dad0809db551 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -688,6 +688,13 @@ const struct file_operations proc_tid_smaps_operations = { .release = seq_release_private, }; +enum clear_refs_types { + CLEAR_REFS_ALL = 1, + CLEAR_REFS_ANON, + CLEAR_REFS_MAPPED, + CLEAR_REFS_LAST, +}; + static int clear_refs_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, struct mm_walk *walk) { @@ -719,10 +726,6 @@ static int clear_refs_pte_range(pmd_t *pmd, unsigned long addr, return 0; } -#define CLEAR_REFS_ALL 1 -#define CLEAR_REFS_ANON 2 -#define CLEAR_REFS_MAPPED 3 - static ssize_t clear_refs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { @@ -730,7 +733,8 @@ static ssize_t clear_refs_write(struct file *file, const char __user *buf, char buffer[PROC_NUMBUF]; struct mm_struct *mm; struct vm_area_struct *vma; - int type; + enum clear_refs_types type; + int itype; int rv; memset(buffer, 0, sizeof(buffer)); @@ -738,10 +742,11 @@ static ssize_t clear_refs_write(struct file *file, const char __user *buf, count = sizeof(buffer) - 1; if (copy_from_user(buffer, buf, count)) return -EFAULT; - rv = kstrtoint(strstrip(buffer), 10, &type); + rv = kstrtoint(strstrip(buffer), 10, &itype); if (rv < 0) return rv; - if (type < CLEAR_REFS_ALL || type > CLEAR_REFS_MAPPED) + type = (enum clear_refs_types)itype; + if (type < CLEAR_REFS_ALL || type >= CLEAR_REFS_LAST) return -EINVAL; task = get_proc_task(file_inode(file)); if (!task) -- cgit v1.2.3 From af9de7eb180fa9b74c2cdc256349304a58c63c02 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Wed, 3 Jul 2013 15:01:18 -0700 Subject: clear_refs: introduce private struct for mm_walk In the next patch the clear-refs-type will be required in clear_refs_pte_range funciton, so prepare the walk->private to carry this info. Signed-off-by: Pavel Emelyanov Cc: Matt Mackall Cc: Xiao Guangrong Cc: Glauber Costa Cc: Marcelo Tosatti Cc: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/proc/task_mmu.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index dad0809db551..ef6f6c62dfee 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -695,10 +695,15 @@ enum clear_refs_types { CLEAR_REFS_LAST, }; +struct clear_refs_private { + struct vm_area_struct *vma; +}; + static int clear_refs_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, struct mm_walk *walk) { - struct vm_area_struct *vma = walk->private; + struct clear_refs_private *cp = walk->private; + struct vm_area_struct *vma = cp->vma; pte_t *pte, ptent; spinlock_t *ptl; struct page *page; @@ -753,13 +758,16 @@ static ssize_t clear_refs_write(struct file *file, const char __user *buf, return -ESRCH; mm = get_task_mm(task); if (mm) { + struct clear_refs_private cp = { + }; struct mm_walk clear_refs_walk = { .pmd_entry = clear_refs_pte_range, .mm = mm, + .private = &cp, }; down_read(&mm->mmap_sem); for (vma = mm->mmap; vma; vma = vma->vm_next) { - clear_refs_walk.private = vma; + cp.vma = vma; if (is_vm_hugetlb_page(vma)) continue; /* -- cgit v1.2.3 From 2b0a9f017548f05e42fbf7e67c4a626c1ebd5e12 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Wed, 3 Jul 2013 15:01:19 -0700 Subject: pagemap: introduce pagemap_entry_t without pmshift bits These bits are always constant (== PAGE_SHIFT) and just occupy space in the entry. Moreover, in next patch we will need to report one more bit in the pagemap, but all bits are already busy on it. That said, describe the pagemap entry that has 6 more free zero bits. Signed-off-by: Pavel Emelyanov Cc: Matt Mackall Cc: Xiao Guangrong Cc: Glauber Costa Cc: Marcelo Tosatti Cc: KOSAKI Motohiro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/proc/task_mmu.c | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index ef6f6c62dfee..39d641292579 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -807,6 +807,7 @@ typedef struct { struct pagemapread { int pos, len; pagemap_entry_t *buffer; + bool v2; }; #define PAGEMAP_WALK_SIZE (PMD_SIZE) @@ -820,14 +821,16 @@ struct pagemapread { #define PM_PSHIFT_BITS 6 #define PM_PSHIFT_OFFSET (PM_STATUS_OFFSET - PM_PSHIFT_BITS) #define PM_PSHIFT_MASK (((1LL << PM_PSHIFT_BITS) - 1) << PM_PSHIFT_OFFSET) -#define PM_PSHIFT(x) (((u64) (x) << PM_PSHIFT_OFFSET) & PM_PSHIFT_MASK) +#define __PM_PSHIFT(x) (((u64) (x) << PM_PSHIFT_OFFSET) & PM_PSHIFT_MASK) #define PM_PFRAME_MASK ((1LL << PM_PSHIFT_OFFSET) - 1) #define PM_PFRAME(x) ((x) & PM_PFRAME_MASK) +/* in "new" pagemap pshift bits are occupied with more status bits */ +#define PM_STATUS2(v2, x) (__PM_PSHIFT(v2 ? x : PAGE_SHIFT)) #define PM_PRESENT PM_STATUS(4LL) #define PM_SWAP PM_STATUS(2LL) #define PM_FILE PM_STATUS(1LL) -#define PM_NOT_PRESENT PM_PSHIFT(PAGE_SHIFT) +#define PM_NOT_PRESENT(v2) PM_STATUS2(v2, 0) #define PM_END_OF_BUFFER 1 static inline pagemap_entry_t make_pme(u64 val) @@ -850,7 +853,7 @@ static int pagemap_pte_hole(unsigned long start, unsigned long end, struct pagemapread *pm = walk->private; unsigned long addr; int err = 0; - pagemap_entry_t pme = make_pme(PM_NOT_PRESENT); + pagemap_entry_t pme = make_pme(PM_NOT_PRESENT(pm->v2)); for (addr = start; addr < end; addr += PAGE_SIZE) { err = add_to_pagemap(addr, &pme, pm); @@ -860,7 +863,7 @@ static int pagemap_pte_hole(unsigned long start, unsigned long end, return err; } -static void pte_to_pagemap_entry(pagemap_entry_t *pme, +static void pte_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *pm, struct vm_area_struct *vma, unsigned long addr, pte_t pte) { u64 frame, flags; @@ -879,18 +882,18 @@ static void pte_to_pagemap_entry(pagemap_entry_t *pme, if (is_migration_entry(entry)) page = migration_entry_to_page(entry); } else { - *pme = make_pme(PM_NOT_PRESENT); + *pme = make_pme(PM_NOT_PRESENT(pm->v2)); return; } if (page && !PageAnon(page)) flags |= PM_FILE; - *pme = make_pme(PM_PFRAME(frame) | PM_PSHIFT(PAGE_SHIFT) | flags); + *pme = make_pme(PM_PFRAME(frame) | PM_STATUS2(pm->v2, 0) | flags); } #ifdef CONFIG_TRANSPARENT_HUGEPAGE -static void thp_pmd_to_pagemap_entry(pagemap_entry_t *pme, +static void thp_pmd_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *pm, pmd_t pmd, int offset) { /* @@ -900,12 +903,12 @@ static void thp_pmd_to_pagemap_entry(pagemap_entry_t *pme, */ if (pmd_present(pmd)) *pme = make_pme(PM_PFRAME(pmd_pfn(pmd) + offset) - | PM_PSHIFT(PAGE_SHIFT) | PM_PRESENT); + | PM_STATUS2(pm->v2, 0) | PM_PRESENT); else - *pme = make_pme(PM_NOT_PRESENT); + *pme = make_pme(PM_NOT_PRESENT(pm->v2)); } #else -static inline void thp_pmd_to_pagemap_entry(pagemap_entry_t *pme, +static inline void thp_pmd_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *pm, pmd_t pmd, int offset) { } @@ -918,7 +921,7 @@ static int pagemap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, struct pagemapread *pm = walk->private; pte_t *pte; int err = 0; - pagemap_entry_t pme = make_pme(PM_NOT_PRESENT); + pagemap_entry_t pme = make_pme(PM_NOT_PRESENT(pm->v2)); /* find the first VMA at or above 'addr' */ vma = find_vma(walk->mm, addr); @@ -928,7 +931,7 @@ static int pagemap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, offset = (addr & ~PAGEMAP_WALK_MASK) >> PAGE_SHIFT; - thp_pmd_to_pagemap_entry(&pme, *pmd, offset); + thp_pmd_to_pagemap_entry(&pme, pm, *pmd, offset); err = add_to_pagemap(addr, &pme, pm); if (err) break; @@ -945,7 +948,7 @@ static int pagemap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, * and need a new, higher one */ if (vma && (addr >= vma->vm_end)) { vma = find_vma(walk->mm, addr); - pme = make_pme(PM_NOT_PRESENT); + pme = make_pme(PM_NOT_PRESENT(pm->v2)); } /* check that 'vma' actually covers this address, @@ -953,7 +956,7 @@ static int pagemap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, if (vma && (vma->vm_start <= addr) && !is_vm_hugetlb_page(vma)) { pte = pte_offset_map(pmd, addr); - pte_to_pagemap_entry(&pme, vma, addr, *pte); + pte_to_pagemap_entry(&pme, pm, vma, addr, *pte); /* unmap before userspace copy */ pte_unmap(pte); } @@ -968,14 +971,14 @@ static int pagemap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, } #ifdef CONFIG_HUGETLB_PAGE -static void huge_pte_to_pagemap_entry(pagemap_entry_t *pme, +static void huge_pte_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *pm, pte_t pte, int offset) { if (pte_present(pte)) *pme = make_pme(PM_PFRAME(pte_pfn(pte) + offset) - | PM_PSHIFT(PAGE_SHIFT) | PM_PRESENT); + | PM_STATUS2(pm->v2, 0) | PM_PRESENT); else - *pme = make_pme(PM_NOT_PRESENT); + *pme = make_pme(PM_NOT_PRESENT(pm->v2)); } /* This function walks within one hugetlb entry in the single call */ @@ -989,7 +992,7 @@ static int pagemap_hugetlb_range(pte_t *pte, unsigned long hmask, for (; addr != end; addr += PAGE_SIZE) { int offset = (addr & ~hmask) >> PAGE_SHIFT; - huge_pte_to_pagemap_entry(&pme, *pte, offset); + huge_pte_to_pagemap_entry(&pme, pm, *pte, offset); err = add_to_pagemap(addr, &pme, pm); if (err) return err; @@ -1051,6 +1054,7 @@ static ssize_t pagemap_read(struct file *file, char __user *buf, if (!count) goto out_task; + pm.v2 = false; pm.len = PM_ENTRY_BYTES * (PAGEMAP_WALK_SIZE >> PAGE_SHIFT); pm.buffer = kmalloc(pm.len, GFP_TEMPORARY); ret = -ENOMEM; -- cgit v1.2.3 From 0f8975ec4db2c8b5bd111b211292ca9be0feb6b8 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Wed, 3 Jul 2013 15:01:20 -0700 Subject: mm: soft-dirty bits for user memory changes tracking The soft-dirty is a bit on a PTE which helps to track which pages a task writes to. In order to do this tracking one should 1. Clear soft-dirty bits from PTEs ("echo 4 > /proc/PID/clear_refs) 2. Wait some time. 3. Read soft-dirty bits (55'th in /proc/PID/pagemap2 entries) To do this tracking, the writable bit is cleared from PTEs when the soft-dirty bit is. Thus, after this, when the task tries to modify a page at some virtual address the #PF occurs and the kernel sets the soft-dirty bit on the respective PTE. Note, that although all the task's address space is marked as r/o after the soft-dirty bits clear, the #PF-s that occur after that are processed fast. This is so, since the pages are still mapped to physical memory, and thus all the kernel does is finds this fact out and puts back writable, dirty and soft-dirty bits on the PTE. Another thing to note, is that when mremap moves PTEs they are marked with soft-dirty as well, since from the user perspective mremap modifies the virtual memory at mremap's new address. Signed-off-by: Pavel Emelyanov Cc: Matt Mackall Cc: Xiao Guangrong Cc: Glauber Costa Cc: Marcelo Tosatti Cc: KOSAKI Motohiro Cc: Stephen Rothwell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/filesystems/proc.txt | 7 +++++- Documentation/vm/soft-dirty.txt | 36 +++++++++++++++++++++++++++ arch/Kconfig | 3 +++ arch/x86/Kconfig | 1 + arch/x86/include/asm/pgtable.h | 24 ++++++++++++++++-- arch/x86/include/asm/pgtable_types.h | 12 +++++++++ fs/proc/task_mmu.c | 47 ++++++++++++++++++++++++++++++++---- include/asm-generic/pgtable.h | 22 +++++++++++++++++ mm/Kconfig | 12 +++++++++ mm/huge_memory.c | 2 +- mm/mremap.c | 2 +- 11 files changed, 158 insertions(+), 10 deletions(-) create mode 100644 Documentation/vm/soft-dirty.txt diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index fd8d0d594fc7..fcc22c982a25 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt @@ -473,7 +473,8 @@ This file is only present if the CONFIG_MMU kernel configuration option is enabled. The /proc/PID/clear_refs is used to reset the PG_Referenced and ACCESSED/YOUNG -bits on both physical and virtual pages associated with a process. +bits on both physical and virtual pages associated with a process, and the +soft-dirty bit on pte (see Documentation/vm/soft-dirty.txt for details). To clear the bits for all the pages associated with the process > echo 1 > /proc/PID/clear_refs @@ -482,6 +483,10 @@ To clear the bits for the anonymous pages associated with the process To clear the bits for the file mapped pages associated with the process > echo 3 > /proc/PID/clear_refs + +To clear the soft-dirty bit + > echo 4 > /proc/PID/clear_refs + Any other value written to /proc/PID/clear_refs will have no effect. The /proc/pid/pagemap gives the PFN, which can be used to find the pageflags diff --git a/Documentation/vm/soft-dirty.txt b/Documentation/vm/soft-dirty.txt new file mode 100644 index 000000000000..9a12a5956bc0 --- /dev/null +++ b/Documentation/vm/soft-dirty.txt @@ -0,0 +1,36 @@ + SOFT-DIRTY PTEs + + The soft-dirty is a bit on a PTE which helps to track which pages a task +writes to. In order to do this tracking one should + + 1. Clear soft-dirty bits from the task's PTEs. + + This is done by writing "4" into the /proc/PID/clear_refs file of the + task in question. + + 2. Wait some time. + + 3. Read soft-dirty bits from the PTEs. + + This is done by reading from the /proc/PID/pagemap. The bit 55 of the + 64-bit qword is the soft-dirty one. If set, the respective PTE was + written to since step 1. + + + Internally, to do this tracking, the writable bit is cleared from PTEs +when the soft-dirty bit is cleared. So, after this, when the task tries to +modify a page at some virtual address the #PF occurs and the kernel sets +the soft-dirty bit on the respective PTE. + + Note, that although all the task's address space is marked as r/o after the +soft-dirty bits clear, the #PF-s that occur after that are processed fast. +This is so, since the pages are still mapped to physical memory, and thus all +the kernel does is finds this fact out and puts both writable and soft-dirty +bits on the PTE. + + + This feature is actively used by the checkpoint-restore project. You +can find more details about it on http://criu.org + + +-- Pavel Emelyanov, Apr 9, 2013 diff --git a/arch/Kconfig b/arch/Kconfig index a4429bcd609e..8d2ae24b9f4a 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -365,6 +365,9 @@ config HAVE_IRQ_TIME_ACCOUNTING config HAVE_ARCH_TRANSPARENT_HUGEPAGE bool +config HAVE_ARCH_SOFT_DIRTY + bool + config HAVE_MOD_ARCH_SPECIFIC bool help diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index b094816a7e0f..10764a3d62cc 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -102,6 +102,7 @@ config X86 select HAVE_ARCH_SECCOMP_FILTER select BUILDTIME_EXTABLE_SORT select GENERIC_CMOS_UPDATE + select HAVE_ARCH_SOFT_DIRTY select CLOCKSOURCE_WATCHDOG select GENERIC_CLOCKEVENTS select ARCH_CLOCKSOURCE_DATA if X86_64 diff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h index 5b0818bc8963..7dc305a46058 100644 --- a/arch/x86/include/asm/pgtable.h +++ b/arch/x86/include/asm/pgtable.h @@ -207,7 +207,7 @@ static inline pte_t pte_mkexec(pte_t pte) static inline pte_t pte_mkdirty(pte_t pte) { - return pte_set_flags(pte, _PAGE_DIRTY); + return pte_set_flags(pte, _PAGE_DIRTY | _PAGE_SOFT_DIRTY); } static inline pte_t pte_mkyoung(pte_t pte) @@ -271,7 +271,7 @@ static inline pmd_t pmd_wrprotect(pmd_t pmd) static inline pmd_t pmd_mkdirty(pmd_t pmd) { - return pmd_set_flags(pmd, _PAGE_DIRTY); + return pmd_set_flags(pmd, _PAGE_DIRTY | _PAGE_SOFT_DIRTY); } static inline pmd_t pmd_mkhuge(pmd_t pmd) @@ -294,6 +294,26 @@ static inline pmd_t pmd_mknotpresent(pmd_t pmd) return pmd_clear_flags(pmd, _PAGE_PRESENT); } +static inline int pte_soft_dirty(pte_t pte) +{ + return pte_flags(pte) & _PAGE_SOFT_DIRTY; +} + +static inline int pmd_soft_dirty(pmd_t pmd) +{ + return pmd_flags(pmd) & _PAGE_SOFT_DIRTY; +} + +static inline pte_t pte_mksoft_dirty(pte_t pte) +{ + return pte_set_flags(pte, _PAGE_SOFT_DIRTY); +} + +static inline pmd_t pmd_mksoft_dirty(pmd_t pmd) +{ + return pmd_set_flags(pmd, _PAGE_SOFT_DIRTY); +} + /* * Mask out unsupported bits in a present pgprot. Non-present pgprots * can use those bits for other purposes, so leave them be. diff --git a/arch/x86/include/asm/pgtable_types.h b/arch/x86/include/asm/pgtable_types.h index e6423002c10b..c98ac63aae48 100644 --- a/arch/x86/include/asm/pgtable_types.h +++ b/arch/x86/include/asm/pgtable_types.h @@ -55,6 +55,18 @@ #define _PAGE_HIDDEN (_AT(pteval_t, 0)) #endif +/* + * The same hidden bit is used by kmemcheck, but since kmemcheck + * works on kernel pages while soft-dirty engine on user space, + * they do not conflict with each other. + */ + +#ifdef CONFIG_MEM_SOFT_DIRTY +#define _PAGE_SOFT_DIRTY (_AT(pteval_t, 1) << _PAGE_BIT_HIDDEN) +#else +#define _PAGE_SOFT_DIRTY (_AT(pteval_t, 0)) +#endif + #if defined(CONFIG_X86_64) || defined(CONFIG_X86_PAE) #define _PAGE_NX (_AT(pteval_t, 1) << _PAGE_BIT_NX) #else diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 39d641292579..a18e065c1c3e 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -692,13 +693,32 @@ enum clear_refs_types { CLEAR_REFS_ALL = 1, CLEAR_REFS_ANON, CLEAR_REFS_MAPPED, + CLEAR_REFS_SOFT_DIRTY, CLEAR_REFS_LAST, }; struct clear_refs_private { struct vm_area_struct *vma; + enum clear_refs_types type; }; +static inline void clear_soft_dirty(struct vm_area_struct *vma, + unsigned long addr, pte_t *pte) +{ +#ifdef CONFIG_MEM_SOFT_DIRTY + /* + * The soft-dirty tracker uses #PF-s to catch writes + * to pages, so write-protect the pte as well. See the + * Documentation/vm/soft-dirty.txt for full description + * of how soft-dirty works. + */ + pte_t ptent = *pte; + ptent = pte_wrprotect(ptent); + ptent = pte_clear_flags(ptent, _PAGE_SOFT_DIRTY); + set_pte_at(vma->vm_mm, addr, pte, ptent); +#endif +} + static int clear_refs_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, struct mm_walk *walk) { @@ -718,6 +738,11 @@ static int clear_refs_pte_range(pmd_t *pmd, unsigned long addr, if (!pte_present(ptent)) continue; + if (cp->type == CLEAR_REFS_SOFT_DIRTY) { + clear_soft_dirty(vma, addr, pte); + continue; + } + page = vm_normal_page(vma, addr, ptent); if (!page) continue; @@ -759,6 +784,7 @@ static ssize_t clear_refs_write(struct file *file, const char __user *buf, mm = get_task_mm(task); if (mm) { struct clear_refs_private cp = { + .type = type, }; struct mm_walk clear_refs_walk = { .pmd_entry = clear_refs_pte_range, @@ -766,6 +792,8 @@ static ssize_t clear_refs_write(struct file *file, const char __user *buf, .private = &cp, }; down_read(&mm->mmap_sem); + if (type == CLEAR_REFS_SOFT_DIRTY) + mmu_notifier_invalidate_range_start(mm, 0, -1); for (vma = mm->mmap; vma; vma = vma->vm_next) { cp.vma = vma; if (is_vm_hugetlb_page(vma)) @@ -786,6 +814,8 @@ static ssize_t clear_refs_write(struct file *file, const char __user *buf, walk_page_range(vma->vm_start, vma->vm_end, &clear_refs_walk); } + if (type == CLEAR_REFS_SOFT_DIRTY) + mmu_notifier_invalidate_range_end(mm, 0, -1); flush_tlb_mm(mm); up_read(&mm->mmap_sem); mmput(mm); @@ -827,6 +857,7 @@ struct pagemapread { /* in "new" pagemap pshift bits are occupied with more status bits */ #define PM_STATUS2(v2, x) (__PM_PSHIFT(v2 ? x : PAGE_SHIFT)) +#define __PM_SOFT_DIRTY (1LL) #define PM_PRESENT PM_STATUS(4LL) #define PM_SWAP PM_STATUS(2LL) #define PM_FILE PM_STATUS(1LL) @@ -868,6 +899,7 @@ static void pte_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *pm, { u64 frame, flags; struct page *page = NULL; + int flags2 = 0; if (pte_present(pte)) { frame = pte_pfn(pte); @@ -888,13 +920,15 @@ static void pte_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *pm, if (page && !PageAnon(page)) flags |= PM_FILE; + if (pte_soft_dirty(pte)) + flags2 |= __PM_SOFT_DIRTY; - *pme = make_pme(PM_PFRAME(frame) | PM_STATUS2(pm->v2, 0) | flags); + *pme = make_pme(PM_PFRAME(frame) | PM_STATUS2(pm->v2, flags2) | flags); } #ifdef CONFIG_TRANSPARENT_HUGEPAGE static void thp_pmd_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *pm, - pmd_t pmd, int offset) + pmd_t pmd, int offset, int pmd_flags2) { /* * Currently pmd for thp is always present because thp can not be @@ -903,13 +937,13 @@ static void thp_pmd_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *p */ if (pmd_present(pmd)) *pme = make_pme(PM_PFRAME(pmd_pfn(pmd) + offset) - | PM_STATUS2(pm->v2, 0) | PM_PRESENT); + | PM_STATUS2(pm->v2, pmd_flags2) | PM_PRESENT); else *pme = make_pme(PM_NOT_PRESENT(pm->v2)); } #else static inline void thp_pmd_to_pagemap_entry(pagemap_entry_t *pme, struct pagemapread *pm, - pmd_t pmd, int offset) + pmd_t pmd, int offset, int pmd_flags2) { } #endif @@ -926,12 +960,15 @@ static int pagemap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, /* find the first VMA at or above 'addr' */ vma = find_vma(walk->mm, addr); if (vma && pmd_trans_huge_lock(pmd, vma) == 1) { + int pmd_flags2; + + pmd_flags2 = (pmd_soft_dirty(*pmd) ? __PM_SOFT_DIRTY : 0); for (; addr != end; addr += PAGE_SIZE) { unsigned long offset; offset = (addr & ~PAGEMAP_WALK_MASK) >> PAGE_SHIFT; - thp_pmd_to_pagemap_entry(&pme, pm, *pmd, offset); + thp_pmd_to_pagemap_entry(&pme, pm, *pmd, offset, pmd_flags2); err = add_to_pagemap(addr, &pme, pm); if (err) break; diff --git a/include/asm-generic/pgtable.h b/include/asm-generic/pgtable.h index b1836987d506..a7126d28f4cf 100644 --- a/include/asm-generic/pgtable.h +++ b/include/asm-generic/pgtable.h @@ -396,6 +396,28 @@ static inline void ptep_modify_prot_commit(struct mm_struct *mm, #define arch_start_context_switch(prev) do {} while (0) #endif +#ifndef CONFIG_HAVE_ARCH_SOFT_DIRTY +static inline int pte_soft_dirty(pte_t pte) +{ + return 0; +} + +static inline int pmd_soft_dirty(pmd_t pmd) +{ + return 0; +} + +static inline pte_t pte_mksoft_dirty(pte_t pte) +{ + return pte; +} + +static inline pmd_t pmd_mksoft_dirty(pmd_t pmd) +{ + return pmd; +} +#endif + #ifndef __HAVE_PFNMAP_TRACKING /* * Interfaces that can be used by architecture code to keep track of diff --git a/mm/Kconfig b/mm/Kconfig index f5e698e30d4a..7e28ecfa8aa4 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -477,3 +477,15 @@ config FRONTSWAP and swap data is stored as normal on the matching swap device. If unsure, say Y to enable frontswap. + +config MEM_SOFT_DIRTY + bool "Track memory changes" + depends on CHECKPOINT_RESTORE && HAVE_ARCH_SOFT_DIRTY + select PROC_PAGE_MONITOR + help + This option enables memory changes tracking by introducing a + soft-dirty bit on pte-s. This bit it set when someone writes + into a page just as regular dirty bit, but unlike the latter + it can be cleared by hands. + + See Documentation/vm/soft-dirty.txt for more details. diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 362c329b83fe..d8b3b850150c 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -1429,7 +1429,7 @@ int move_huge_pmd(struct vm_area_struct *vma, struct vm_area_struct *new_vma, if (ret == 1) { pmd = pmdp_get_and_clear(mm, old_addr, old_pmd); VM_BUG_ON(!pmd_none(*new_pmd)); - set_pmd_at(mm, new_addr, new_pmd, pmd); + set_pmd_at(mm, new_addr, new_pmd, pmd_mksoft_dirty(pmd)); spin_unlock(&mm->page_table_lock); } out: diff --git a/mm/mremap.c b/mm/mremap.c index 463a25705ac6..3708655378e9 100644 --- a/mm/mremap.c +++ b/mm/mremap.c @@ -126,7 +126,7 @@ static void move_ptes(struct vm_area_struct *vma, pmd_t *old_pmd, continue; pte = ptep_get_and_clear(mm, old_addr, old_pte); pte = move_pte(pte, new_vma->vm_page_prot, old_addr, new_addr); - set_pte_at(mm, new_addr, new_pte, pte); + set_pte_at(mm, new_addr, new_pte, pte_mksoft_dirty(pte)); } arch_leave_lazy_mmu_mode(); -- cgit v1.2.3 From 541c237c0923f567c9c4cabb8a81635baadc713f Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Wed, 3 Jul 2013 15:01:22 -0700 Subject: pagemap: prepare to reuse constant bits with page-shift In order to reuse bits from pagemap entries gracefully, we leave the entries as is but on pagemap open emit a warning in dmesg, that bits 55-60 are about to change in a couple of releases. Next, if a user issues soft-dirty clear command via the clear_refs file (it was disabled before v3.9) we assume that he's aware of the new pagemap format, note that fact and report the bits in pagemap in the new manner. The "migration strategy" looks like this then: 1. existing users are not affected -- they don't touch soft-dirty feature, thus see old bits in pagemap, but are warned and have time to fix themselves 2. those who use soft-dirty know about new pagemap format 3. some time soon we get rid of any signs of page-shift in pagemap as well as this trick with clear-soft-dirty affecting pagemap format. Signed-off-by: Pavel Emelyanov Cc: Matt Mackall Cc: Xiao Guangrong Cc: Glauber Costa Cc: Marcelo Tosatti Cc: KOSAKI Motohiro Cc: Stephen Rothwell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/vm/pagemap.txt | 3 ++- fs/proc/task_mmu.c | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/Documentation/vm/pagemap.txt b/Documentation/vm/pagemap.txt index 7587493c67f1..fd7c3cfddd8e 100644 --- a/Documentation/vm/pagemap.txt +++ b/Documentation/vm/pagemap.txt @@ -15,7 +15,8 @@ There are three components to pagemap: * Bits 0-54 page frame number (PFN) if present * Bits 0-4 swap type if swapped * Bits 5-54 swap offset if swapped - * Bits 55-60 page shift (page size = 1<= CLEAR_REFS_LAST) return -EINVAL; + + if (type == CLEAR_REFS_SOFT_DIRTY) { + soft_dirty_cleared = true; + pr_warn_once("The pagemap bits 55-60 has changed their meaning! " + "See the linux/Documentation/vm/pagemap.txt for details.\n"); + } + task = get_proc_task(file_inode(file)); if (!task) return -ESRCH; @@ -1091,7 +1115,7 @@ static ssize_t pagemap_read(struct file *file, char __user *buf, if (!count) goto out_task; - pm.v2 = false; + pm.v2 = soft_dirty_cleared; pm.len = PM_ENTRY_BYTES * (PAGEMAP_WALK_SIZE >> PAGE_SHIFT); pm.buffer = kmalloc(pm.len, GFP_TEMPORARY); ret = -ENOMEM; @@ -1164,9 +1188,18 @@ out: return ret; } +static int pagemap_open(struct inode *inode, struct file *file) +{ + pr_warn_once("Bits 55-60 of /proc/PID/pagemap entries are about " + "to stop being page-shift some time soon. See the " + "linux/Documentation/vm/pagemap.txt for details.\n"); + return 0; +} + const struct file_operations proc_pagemap_operations = { .llseek = mem_lseek, /* borrow this */ .read = pagemap_read, + .open = pagemap_open, }; #endif /* CONFIG_PROC_PAGE_MONITOR */ -- cgit v1.2.3 From ffbdccf5e1facd18b54429a749667fb185c10f20 Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Wed, 3 Jul 2013 15:01:23 -0700 Subject: mm, memcg: don't take task_lock in task_in_mem_cgroup For processes that have detached their mm's, task_in_mem_cgroup() unnecessarily takes task_lock() when rcu_read_lock() is all that is necessary to call mem_cgroup_from_task(). While we're here, switch task_in_mem_cgroup() to return bool. Signed-off-by: David Rientjes Cc: KAMEZAWA Hiroyuki Cc: Johannes Weiner Acked-by: Michal Hocko Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/memcontrol.h | 9 +++++---- mm/memcontrol.c | 11 ++++++----- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index d6183f06d8c1..7b4d9d79570b 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -77,7 +77,8 @@ extern void mem_cgroup_uncharge_cache_page(struct page *page); bool __mem_cgroup_same_or_subtree(const struct mem_cgroup *root_memcg, struct mem_cgroup *memcg); -int task_in_mem_cgroup(struct task_struct *task, const struct mem_cgroup *memcg); +bool task_in_mem_cgroup(struct task_struct *task, + const struct mem_cgroup *memcg); extern struct mem_cgroup *try_get_mem_cgroup_from_page(struct page *page); extern struct mem_cgroup *mem_cgroup_from_task(struct task_struct *p); @@ -273,10 +274,10 @@ static inline bool mm_match_cgroup(struct mm_struct *mm, return true; } -static inline int task_in_mem_cgroup(struct task_struct *task, - const struct mem_cgroup *memcg) +static inline bool task_in_mem_cgroup(struct task_struct *task, + const struct mem_cgroup *memcg) { - return 1; + return true; } static inline struct cgroup_subsys_state diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 194721839cf5..4748966b1511 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -1448,11 +1448,12 @@ static bool mem_cgroup_same_or_subtree(const struct mem_cgroup *root_memcg, return ret; } -int task_in_mem_cgroup(struct task_struct *task, const struct mem_cgroup *memcg) +bool task_in_mem_cgroup(struct task_struct *task, + const struct mem_cgroup *memcg) { - int ret; struct mem_cgroup *curr = NULL; struct task_struct *p; + bool ret; p = find_lock_task_mm(task); if (p) { @@ -1464,14 +1465,14 @@ int task_in_mem_cgroup(struct task_struct *task, const struct mem_cgroup *memcg) * killer still needs to detect if they have already been oom * killed to prevent needlessly killing additional tasks. */ - task_lock(task); + rcu_read_lock(); curr = mem_cgroup_from_task(task); if (curr) css_get(&curr->css); - task_unlock(task); + rcu_read_unlock(); } if (!curr) - return 0; + return false; /* * We should check use_hierarchy of "memcg" not "curr". Because checking * use_hierarchy of "curr" here make this function true if hierarchy is -- cgit v1.2.3 From b430e9d1c6d416306d44dbf3aa3148be7af78abc Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Wed, 3 Jul 2013 15:01:24 -0700 Subject: mm: remove compressed copy from zram in-memory Swap subsystem does lazy swap slot free with expecting the page would be swapped out again so we can avoid unnecessary write. But the problem in in-memory swap(ex, zram) is that it consumes memory space until vm_swap_full(ie, used half of all of swap device) condition meet. It could be bad if we use multiple swap device, small in-memory swap and big storage swap or in-memory swap alone. This patch makes swap subsystem free swap slot as soon as swap-read is completed and make the swapcache page dirty so the page should be written out the swap device to reclaim it. It means we never lose it. I tested this patch with kernel compile workload. 1. before compile time : 9882.42 zram max wasted space by fragmentation: 13471881 byte memory space consumed by zram: 174227456 byte the number of slot free notify: 206684 2. after compile time : 9653.90 zram max wasted space by fragmentation: 11805932 byte memory space consumed by zram: 154001408 byte the number of slot free notify: 426972 [akpm@linux-foundation.org: tweak comment text] [artem.savkov@gmail.com: fix BUG due to non-swapcache pages in end_swap_bio_read()] [akpm@linux-foundation.org: invert unlikely() test, augment comment, 80-col cleanup] Signed-off-by: Dan Magenheimer Signed-off-by: Minchan Kim Signed-off-by: Artem Savkov Cc: Hugh Dickins Cc: Seth Jennings Cc: Nitin Gupta Cc: Konrad Rzeszutek Wilk Cc: Shaohua Li Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/page_io.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/mm/page_io.c b/mm/page_io.c index a8a3ef45fed7..ba05b64e5d8d 100644 --- a/mm/page_io.c +++ b/mm/page_io.c @@ -21,6 +21,7 @@ #include #include #include +#include #include static struct bio *get_swap_bio(gfp_t gfp_flags, @@ -80,9 +81,54 @@ void end_swap_bio_read(struct bio *bio, int err) imajor(bio->bi_bdev->bd_inode), iminor(bio->bi_bdev->bd_inode), (unsigned long long)bio->bi_sector); - } else { - SetPageUptodate(page); + goto out; } + + SetPageUptodate(page); + + /* + * There is no guarantee that the page is in swap cache - the software + * suspend code (at least) uses end_swap_bio_read() against a non- + * swapcache page. So we must check PG_swapcache before proceeding with + * this optimization. + */ + if (likely(PageSwapCache(page))) { + struct swap_info_struct *sis; + + sis = page_swap_info(page); + if (sis->flags & SWP_BLKDEV) { + /* + * The swap subsystem performs lazy swap slot freeing, + * expecting that the page will be swapped out again. + * So we can avoid an unnecessary write if the page + * isn't redirtied. + * This is good for real swap storage because we can + * reduce unnecessary I/O and enhance wear-leveling + * if an SSD is used as the as swap device. + * But if in-memory swap device (eg zram) is used, + * this causes a duplicated copy between uncompressed + * data in VM-owned memory and compressed data in + * zram-owned memory. So let's free zram-owned memory + * and make the VM-owned decompressed page *dirty*, + * so the page should be swapped out somewhere again if + * we again wish to reclaim it. + */ + struct gendisk *disk = sis->bdev->bd_disk; + if (disk->fops->swap_slot_free_notify) { + swp_entry_t entry; + unsigned long offset; + + entry.val = page_private(page); + offset = swp_offset(entry); + + SetPageDirty(page); + disk->fops->swap_slot_free_notify(sis->bdev, + offset); + } + } + } + +out: unlock_page(page); bio_put(bio); } -- cgit v1.2.3 From d6e932177090463e5c709e9e61bbd705a33a1609 Mon Sep 17 00:00:00 2001 From: Libin Date: Wed, 3 Jul 2013 15:01:26 -0700 Subject: mm: use vma_pages() to replace (vm_end - vm_start) >> PAGE_SHIFT (*->vm_end - *->vm_start) >> PAGE_SHIFT operation is implemented as a inline funcion vma_pages() in linux/mm.h, so using it. Signed-off-by: Libin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/memory.c | 2 +- mm/mmap.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mm/memory.c b/mm/memory.c index 95d0cce63583..a101bbcacfd7 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -2904,7 +2904,7 @@ static inline void unmap_mapping_range_tree(struct rb_root *root, details->first_index, details->last_index) { vba = vma->vm_pgoff; - vea = vba + ((vma->vm_end - vma->vm_start) >> PAGE_SHIFT) - 1; + vea = vba + vma_pages(vma) - 1; /* Assume for now that PAGE_CACHE_SHIFT == PAGE_SHIFT */ zba = details->first_index; if (zba < vba) diff --git a/mm/mmap.c b/mm/mmap.c index f681e1842fad..8468ffd05bae 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -955,7 +955,7 @@ can_vma_merge_after(struct vm_area_struct *vma, unsigned long vm_flags, if (is_mergeable_vma(vma, file, vm_flags) && is_mergeable_anon_vma(anon_vma, vma->anon_vma, vma)) { pgoff_t vm_pglen; - vm_pglen = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; + vm_pglen = vma_pages(vma); if (vma->vm_pgoff + vm_pglen == vm_pgoff) return 1; } -- cgit v1.2.3 From ef9f515a4c38c759c56c7d1e760cc243b6d516f4 Mon Sep 17 00:00:00 2001 From: Libin Date: Wed, 3 Jul 2013 15:01:26 -0700 Subject: ncpfs: use vma_pages() to replace (vm_end - vm_start) >> PAGE_SHIFT (*->vm_end - *->vm_start) >> PAGE_SHIFT operation is implemented as a inline funcion vma_pages() in linux/mm.h, so using it. Signed-off-by: Libin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/ncpfs/mmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ncpfs/mmap.c b/fs/ncpfs/mmap.c index ee24df5af1f9..3c5dd55d284c 100644 --- a/fs/ncpfs/mmap.c +++ b/fs/ncpfs/mmap.c @@ -117,7 +117,7 @@ int ncp_mmap(struct file *file, struct vm_area_struct *vma) return -EINVAL; /* we do not support files bigger than 4GB... We eventually supports just 4GB... */ - if (((vma->vm_end - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff + if (vma_pages(vma) + vma->vm_pgoff > (1U << (32 - PAGE_SHIFT))) return -EFBIG; -- cgit v1.2.3 From 52c2dad914ca3ac84926106d95ddf47de2f40b45 Mon Sep 17 00:00:00 2001 From: Libin Date: Wed, 3 Jul 2013 15:01:27 -0700 Subject: uio: use vma_pages() to replace (vm_end - vm_start) >> PAGE_SHIFT (*->vm_end - *->vm_start) >> PAGE_SHIFT operation is implemented as a inline funcion vma_pages() in linux/mm.h, so using it. Signed-off-by: Libin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/uio/uio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index b645c47501b4..3b96f18593b3 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -677,7 +677,7 @@ static int uio_mmap(struct file *filep, struct vm_area_struct *vma) if (mi < 0) return -EINVAL; - requested_pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; + requested_pages = vma_pages(vma); actual_pages = ((idev->info->mem[mi].addr & ~PAGE_MASK) + idev->info->mem[mi].size + PAGE_SIZE -1) >> PAGE_SHIFT; if (requested_pages > actual_pages) -- cgit v1.2.3 From 4008bab7b3969ad9f9dd1d02096a3f0aa5610bd2 Mon Sep 17 00:00:00 2001 From: Cody P Schafer Date: Wed, 3 Jul 2013 15:01:28 -0700 Subject: mm/page_alloc: factor out setting of pcp->high and pcp->batch "Problems" with the current code: 1: there is a lack of synchronization in setting ->high and ->batch in percpu_pagelist_fraction_sysctl_handler() 2: stop_machine() in zone_pcp_update() is unnecissary. 3: zone_pcp_update() does not consider the case where percpu_pagelist_fraction is non-zero To fix: 1: add memory barriers, a safe ->batch value, an update side mutex when updating ->high and ->batch, and use ACCESS_ONCE() for ->batch users that expect a stable value. 2: avoid draining pages in zone_pcp_update(), rely upon the memory barriers added to fix #1 3: factor out quite a few functions, and then call the appropriate one. Note that it results in a change to the behavior of zone_pcp_update(), which is used by memory_hotplug. I'm rather certain that I've diserned (and preserved) the essential behavior (changing ->high and ->batch), and only eliminated unneeded actions (draining the per cpu pages), but this may not be the case. Further note that the draining of pages that previously took place in zone_pcp_update() occured after repeated draining when attempting to offline a page, and after the offline has "succeeded". It appears that the draining was added to zone_pcp_update() to avoid refactoring setup_pageset() into 2 funtions. This patch: Creates pageset_set_batch() for use in setup_pageset(). pageset_set_batch() imitates the functionality of setup_pagelist_highmark(), but uses the boot time (percpu_pagelist_fraction == 0) calculations for determining ->high based on ->batch. Signed-off-by: Cody P Schafer Cc: Gilad Ben-Yossef Cc: KOSAKI Motohiro Cc: Mel Gorman Cc: Pekka Enberg Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/page_alloc.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index c3edb624fccf..d4bcc20ab6f0 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -4032,6 +4032,14 @@ static int __meminit zone_batchsize(struct zone *zone) #endif } +/* a companion to setup_pagelist_highmark() */ +static void pageset_set_batch(struct per_cpu_pageset *p, unsigned long batch) +{ + struct per_cpu_pages *pcp = &p->pcp; + pcp->high = 6 * batch; + pcp->batch = max(1UL, 1 * batch); +} + static void setup_pageset(struct per_cpu_pageset *p, unsigned long batch) { struct per_cpu_pages *pcp; @@ -4041,8 +4049,7 @@ static void setup_pageset(struct per_cpu_pageset *p, unsigned long batch) pcp = &p->pcp; pcp->count = 0; - pcp->high = 6 * batch; - pcp->batch = max(1UL, 1 * batch); + pageset_set_batch(p, batch); for (migratetype = 0; migratetype < MIGRATE_PCPTYPES; migratetype++) INIT_LIST_HEAD(&pcp->lists[migratetype]); } @@ -4051,7 +4058,6 @@ static void setup_pageset(struct per_cpu_pageset *p, unsigned long batch) * setup_pagelist_highmark() sets the high water mark for hot per_cpu_pagelist * to the value high for the pageset p. */ - static void setup_pagelist_highmark(struct per_cpu_pageset *p, unsigned long high) { -- cgit v1.2.3 From c8e251fadc6220261f6e0c6b8a4f1cdf27626165 Mon Sep 17 00:00:00 2001 From: Cody P Schafer Date: Wed, 3 Jul 2013 15:01:29 -0700 Subject: mm/page_alloc: prevent concurrent updaters of pcp ->batch and ->high Because we are going to rely upon a careful transision between old and new ->high and ->batch values using memory barriers and will remove stop_machine(), we need to prevent multiple updaters from interweaving their memory writes. Add a simple mutex to protect both update loops. Signed-off-by: Cody P Schafer Cc: Gilad Ben-Yossef Cc: KOSAKI Motohiro Cc: Mel Gorman Cc: Pekka Enberg Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/page_alloc.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index d4bcc20ab6f0..8d4335779633 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -65,6 +65,9 @@ #include #include "internal.h" +/* prevent >1 _updater_ of zone percpu pageset ->high and ->batch fields */ +static DEFINE_MUTEX(pcp_batch_high_lock); + #ifdef CONFIG_USE_PERCPU_NUMA_NODE_ID DEFINE_PER_CPU(int, numa_node); EXPORT_PER_CPU_SYMBOL(numa_node); @@ -5557,6 +5560,8 @@ int percpu_pagelist_fraction_sysctl_handler(ctl_table *table, int write, ret = proc_dointvec_minmax(table, write, buffer, length, ppos); if (!write || (ret < 0)) return ret; + + mutex_lock(&pcp_batch_high_lock); for_each_populated_zone(zone) { for_each_possible_cpu(cpu) { unsigned long high; @@ -5565,6 +5570,7 @@ int percpu_pagelist_fraction_sysctl_handler(ctl_table *table, int write, per_cpu_ptr(zone->pageset, cpu), high); } } + mutex_unlock(&pcp_batch_high_lock); return 0; } @@ -6078,7 +6084,9 @@ static int __meminit __zone_pcp_update(void *data) void __meminit zone_pcp_update(struct zone *zone) { + mutex_lock(&pcp_batch_high_lock); stop_machine(__zone_pcp_update, zone, NULL); + mutex_unlock(&pcp_batch_high_lock); } #endif -- cgit v1.2.3 From 8d7a8fa97abeb4fd6b3975d32c9f859875157770 Mon Sep 17 00:00:00 2001 From: Cody P Schafer Date: Wed, 3 Jul 2013 15:01:31 -0700 Subject: mm/page_alloc: insert memory barriers to allow async update of pcp batch and high Introduce pageset_update() to perform a safe transision from one set of pcp->{batch,high} to a new set using memory barriers. This ensures that batch is always set to a safe value (1) prior to updating high, and ensure that high is fully updated before setting the real value of batch. It avoids ->batch ever rising above ->high. Suggested by Gilad Ben-Yossef in these threads: https://lkml.org/lkml/2013/4/9/23 https://lkml.org/lkml/2013/4/10/49 Also reproduces his proposed comment. Signed-off-by: Cody P Schafer Reviewed-by: Gilad Ben-Yossef Cc: KOSAKI Motohiro Cc: Mel Gorman Cc: Pekka Enberg Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/page_alloc.c | 41 ++++++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 8d4335779633..eaaef2a09424 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -4035,12 +4035,37 @@ static int __meminit zone_batchsize(struct zone *zone) #endif } +/* + * pcp->high and pcp->batch values are related and dependent on one another: + * ->batch must never be higher then ->high. + * The following function updates them in a safe manner without read side + * locking. + * + * Any new users of pcp->batch and pcp->high should ensure they can cope with + * those fields changing asynchronously (acording the the above rule). + * + * mutex_is_locked(&pcp_batch_high_lock) required when calling this function + * outside of boot time (or some other assurance that no concurrent updaters + * exist). + */ +static void pageset_update(struct per_cpu_pages *pcp, unsigned long high, + unsigned long batch) +{ + /* start with a fail safe value for batch */ + pcp->batch = 1; + smp_wmb(); + + /* Update high, then batch, in order */ + pcp->high = high; + smp_wmb(); + + pcp->batch = batch; +} + /* a companion to setup_pagelist_highmark() */ static void pageset_set_batch(struct per_cpu_pageset *p, unsigned long batch) { - struct per_cpu_pages *pcp = &p->pcp; - pcp->high = 6 * batch; - pcp->batch = max(1UL, 1 * batch); + pageset_update(&p->pcp, 6 * batch, max(1UL, 1 * batch)); } static void setup_pageset(struct per_cpu_pageset *p, unsigned long batch) @@ -4064,13 +4089,11 @@ static void setup_pageset(struct per_cpu_pageset *p, unsigned long batch) static void setup_pagelist_highmark(struct per_cpu_pageset *p, unsigned long high) { - struct per_cpu_pages *pcp; + unsigned long batch = max(1UL, high / 4); + if ((high / 4) > (PAGE_SHIFT * 8)) + batch = PAGE_SHIFT * 8; - pcp = &p->pcp; - pcp->high = high; - pcp->batch = max(1UL, high/4); - if ((high/4) > (PAGE_SHIFT * 8)) - pcp->batch = PAGE_SHIFT * 8; + pageset_update(&p->pcp, high, batch); } static void __meminit setup_zone_pageset(struct zone *zone) -- cgit v1.2.3 From 998d39cb236fe464af86a3492a24d2f67ee1efc2 Mon Sep 17 00:00:00 2001 From: Cody P Schafer Date: Wed, 3 Jul 2013 15:01:32 -0700 Subject: mm/page_alloc: protect pcp->batch accesses with ACCESS_ONCE pcp->batch could change at any point, avoid relying on it being a stable value. Signed-off-by: Cody P Schafer Cc: Gilad Ben-Yossef Cc: KOSAKI Motohiro Cc: Mel Gorman Cc: Pekka Enberg Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/page_alloc.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index eaaef2a09424..97b8f861e63d 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -1182,10 +1182,12 @@ void drain_zone_pages(struct zone *zone, struct per_cpu_pages *pcp) { unsigned long flags; int to_drain; + unsigned long batch; local_irq_save(flags); - if (pcp->count >= pcp->batch) - to_drain = pcp->batch; + batch = ACCESS_ONCE(pcp->batch); + if (pcp->count >= batch) + to_drain = batch; else to_drain = pcp->count; if (to_drain > 0) { @@ -1353,8 +1355,9 @@ void free_hot_cold_page(struct page *page, int cold) list_add(&page->lru, &pcp->lists[migratetype]); pcp->count++; if (pcp->count >= pcp->high) { - free_pcppages_bulk(zone, pcp->batch, pcp); - pcp->count -= pcp->batch; + unsigned long batch = ACCESS_ONCE(pcp->batch); + free_pcppages_bulk(zone, batch, pcp); + pcp->count -= batch; } out: -- cgit v1.2.3 From 0a647f3811d6af56405a819341ceac23e31d4572 Mon Sep 17 00:00:00 2001 From: Cody P Schafer Date: Wed, 3 Jul 2013 15:01:33 -0700 Subject: mm/page_alloc: convert zone_pcp_update() to rely on memory barriers instead of stop_machine() zone_pcp_update()'s goal is to adjust the ->high and ->mark members of a percpu pageset based on a zone's ->managed_pages. We don't need to drain the entire percpu pageset just to modify these fields. This lets us avoid calling setup_pageset() (and the draining required to call it) and instead allows simply setting the fields' values (with some attention paid to memory barriers to prevent the relationship between ->batch and ->high from being thrown off). This does change the behavior of zone_pcp_update() as the percpu pagesets will not be drained when zone_pcp_update() is called (they will end up being shrunk, not completely drained, later when a 0-order page is freed in free_hot_cold_page()). Signed-off-by: Cody P Schafer Cc: Gilad Ben-Yossef Cc: KOSAKI Motohiro Cc: Mel Gorman Cc: Pekka Enberg Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/page_alloc.c | 33 +++++++++------------------------ 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 97b8f861e63d..8125263be60f 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -6085,33 +6085,18 @@ void free_contig_range(unsigned long pfn, unsigned nr_pages) #endif #ifdef CONFIG_MEMORY_HOTPLUG -static int __meminit __zone_pcp_update(void *data) -{ - struct zone *zone = data; - int cpu; - unsigned long batch = zone_batchsize(zone), flags; - - for_each_possible_cpu(cpu) { - struct per_cpu_pageset *pset; - struct per_cpu_pages *pcp; - - pset = per_cpu_ptr(zone->pageset, cpu); - pcp = &pset->pcp; - - local_irq_save(flags); - if (pcp->count > 0) - free_pcppages_bulk(zone, pcp->count, pcp); - drain_zonestat(zone, pset); - setup_pageset(pset, batch); - local_irq_restore(flags); - } - return 0; -} - +/* + * The zone indicated has a new number of managed_pages; batch sizes and percpu + * page high values need to be recalulated. + */ void __meminit zone_pcp_update(struct zone *zone) { + unsigned cpu; + unsigned long batch; mutex_lock(&pcp_batch_high_lock); - stop_machine(__zone_pcp_update, zone, NULL); + batch = zone_batchsize(zone); + for_each_possible_cpu(cpu) + pageset_set_batch(per_cpu_ptr(zone->pageset, cpu), batch); mutex_unlock(&pcp_batch_high_lock); } #endif -- cgit v1.2.3 From 22a7f12b1606327f0e11fcdf9043ae00bf9917df Mon Sep 17 00:00:00 2001 From: Cody P Schafer Date: Wed, 3 Jul 2013 15:01:34 -0700 Subject: mm/page_alloc: when handling percpu_pagelist_fraction, don't unneedly recalulate high Simply moves calculation of the new 'high' value outside the for_each_possible_cpu() loop, as it does not depend on the cpu. Signed-off-by: Cody P Schafer Cc: Gilad Ben-Yossef Cc: KOSAKI Motohiro Cc: Mel Gorman Cc: Pekka Enberg Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/page_alloc.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 8125263be60f..386de0f11bea 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -5575,7 +5575,6 @@ int lowmem_reserve_ratio_sysctl_handler(ctl_table *table, int write, * cpu. It is the fraction of total pages in each zone that a hot per cpu pagelist * can have before it gets flushed back to buddy allocator. */ - int percpu_pagelist_fraction_sysctl_handler(ctl_table *table, int write, void __user *buffer, size_t *length, loff_t *ppos) { @@ -5589,12 +5588,11 @@ int percpu_pagelist_fraction_sysctl_handler(ctl_table *table, int write, mutex_lock(&pcp_batch_high_lock); for_each_populated_zone(zone) { - for_each_possible_cpu(cpu) { - unsigned long high; - high = zone->managed_pages / percpu_pagelist_fraction; + unsigned long high; + high = zone->managed_pages / percpu_pagelist_fraction; + for_each_possible_cpu(cpu) setup_pagelist_highmark( - per_cpu_ptr(zone->pageset, cpu), high); - } + per_cpu_ptr(zone->pageset, cpu), high); } mutex_unlock(&pcp_batch_high_lock); return 0; -- cgit v1.2.3 From 88c90dbccaaed35991b5336fec84294de1d23538 Mon Sep 17 00:00:00 2001 From: Cody P Schafer Date: Wed, 3 Jul 2013 15:01:35 -0700 Subject: mm/page_alloc: factor setup_pageset() into pageset_init() and pageset_set_batch() Signed-off-by: Cody P Schafer Cc: Gilad Ben-Yossef Cc: KOSAKI Motohiro Cc: Mel Gorman Cc: Pekka Enberg Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/page_alloc.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 386de0f11bea..a235149d9406 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -4071,7 +4071,7 @@ static void pageset_set_batch(struct per_cpu_pageset *p, unsigned long batch) pageset_update(&p->pcp, 6 * batch, max(1UL, 1 * batch)); } -static void setup_pageset(struct per_cpu_pageset *p, unsigned long batch) +static void pageset_init(struct per_cpu_pageset *p) { struct per_cpu_pages *pcp; int migratetype; @@ -4080,11 +4080,16 @@ static void setup_pageset(struct per_cpu_pageset *p, unsigned long batch) pcp = &p->pcp; pcp->count = 0; - pageset_set_batch(p, batch); for (migratetype = 0; migratetype < MIGRATE_PCPTYPES; migratetype++) INIT_LIST_HEAD(&pcp->lists[migratetype]); } +static void setup_pageset(struct per_cpu_pageset *p, unsigned long batch) +{ + pageset_init(p); + pageset_set_batch(p, batch); +} + /* * setup_pagelist_highmark() sets the high water mark for hot per_cpu_pagelist * to the value high for the pageset p. -- cgit v1.2.3 From dd1895e2c5c9ed3a791d1d8eb4a6a3e241ec9d6e Mon Sep 17 00:00:00 2001 From: Cody P Schafer Date: Wed, 3 Jul 2013 15:01:36 -0700 Subject: mm/page_alloc: relocate comment to be directly above code it refers to. Signed-off-by: Cody P Schafer Cc: Gilad Ben-Yossef Cc: KOSAKI Motohiro Cc: Mel Gorman Cc: Pekka Enberg Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/page_alloc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index a235149d9406..2793ce50f316 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -3711,12 +3711,12 @@ void __ref build_all_zonelists(pg_data_t *pgdat, struct zone *zone) mminit_verify_zonelist(); cpuset_init_current_mems_allowed(); } else { - /* we have to stop all cpus to guarantee there is no user - of zonelist */ #ifdef CONFIG_MEMORY_HOTPLUG if (zone) setup_zone_pageset(zone); #endif + /* we have to stop all cpus to guarantee there is no user + of zonelist */ stop_machine(__build_all_zonelists, pgdat, NULL); /* cpuset refresh routine should be here */ } -- cgit v1.2.3 From 56cef2b85c28d81efd39f2eeaddce28678756fe3 Mon Sep 17 00:00:00 2001 From: Cody P Schafer Date: Wed, 3 Jul 2013 15:01:38 -0700 Subject: mm/page_alloc: factor zone_pageset_init() out of setup_zone_pageset() Signed-off-by: Cody P Schafer Cc: Gilad Ben-Yossef Cc: KOSAKI Motohiro Cc: Mel Gorman Cc: Pekka Enberg Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/page_alloc.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 2793ce50f316..ee6fe7faabad 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -4104,22 +4104,25 @@ static void setup_pagelist_highmark(struct per_cpu_pageset *p, pageset_update(&p->pcp, high, batch); } +static void __meminit zone_pageset_init(struct zone *zone, int cpu) +{ + struct per_cpu_pageset *pcp = per_cpu_ptr(zone->pageset, cpu); + + pageset_init(pcp); + if (percpu_pagelist_fraction) + setup_pagelist_highmark(pcp, + (zone->managed_pages / + percpu_pagelist_fraction)); + else + pageset_set_batch(pcp, zone_batchsize(zone)); +} + static void __meminit setup_zone_pageset(struct zone *zone) { int cpu; - zone->pageset = alloc_percpu(struct per_cpu_pageset); - - for_each_possible_cpu(cpu) { - struct per_cpu_pageset *pcp = per_cpu_ptr(zone->pageset, cpu); - - setup_pageset(pcp, zone_batchsize(zone)); - - if (percpu_pagelist_fraction) - setup_pagelist_highmark(pcp, - (zone->managed_pages / - percpu_pagelist_fraction)); - } + for_each_possible_cpu(cpu) + zone_pageset_init(zone, cpu); } /* -- cgit v1.2.3 From 737af4c0110fc69a81dc7464a74a4113f7645255 Mon Sep 17 00:00:00 2001 From: Cody P Schafer Date: Wed, 3 Jul 2013 15:01:39 -0700 Subject: mm/page_alloc: in zone_pcp_update(), uze zone_pageset_init() Previously, zone_pcp_update() called pageset_set_batch() directly, essentially assuming that percpu_pagelist_fraction == 0. Correct this by calling zone_pageset_init(), which chooses the appropriate ->batch and ->high calculations. Signed-off-by: Cody P Schafer Cc: Gilad Ben-Yossef Cc: KOSAKI Motohiro Cc: Mel Gorman Cc: Pekka Enberg Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/page_alloc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index ee6fe7faabad..c7344d17660b 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -6098,11 +6098,9 @@ void free_contig_range(unsigned long pfn, unsigned nr_pages) void __meminit zone_pcp_update(struct zone *zone) { unsigned cpu; - unsigned long batch; mutex_lock(&pcp_batch_high_lock); - batch = zone_batchsize(zone); for_each_possible_cpu(cpu) - pageset_set_batch(per_cpu_ptr(zone->pageset, cpu), batch); + zone_pageset_init(zone, cpu); mutex_unlock(&pcp_batch_high_lock); } #endif -- cgit v1.2.3 From 3664033c56f211a3dcf28d9d68c604ed447d8d79 Mon Sep 17 00:00:00 2001 From: Cody P Schafer Date: Wed, 3 Jul 2013 15:01:40 -0700 Subject: mm/page_alloc: rename setup_pagelist_highmark() to match naming of pageset_set_batch() Signed-off-by: Cody P Schafer Cc: Gilad Ben-Yossef Cc: KOSAKI Motohiro Cc: Mel Gorman Cc: Pekka Enberg Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/page_alloc.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index c7344d17660b..03a3f943d98e 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -4065,7 +4065,7 @@ static void pageset_update(struct per_cpu_pages *pcp, unsigned long high, pcp->batch = batch; } -/* a companion to setup_pagelist_highmark() */ +/* a companion to pageset_set_high() */ static void pageset_set_batch(struct per_cpu_pageset *p, unsigned long batch) { pageset_update(&p->pcp, 6 * batch, max(1UL, 1 * batch)); @@ -4091,10 +4091,10 @@ static void setup_pageset(struct per_cpu_pageset *p, unsigned long batch) } /* - * setup_pagelist_highmark() sets the high water mark for hot per_cpu_pagelist + * pageset_set_high() sets the high water mark for hot per_cpu_pagelist * to the value high for the pageset p. */ -static void setup_pagelist_highmark(struct per_cpu_pageset *p, +static void pageset_set_high(struct per_cpu_pageset *p, unsigned long high) { unsigned long batch = max(1UL, high / 4); @@ -4110,7 +4110,7 @@ static void __meminit zone_pageset_init(struct zone *zone, int cpu) pageset_init(pcp); if (percpu_pagelist_fraction) - setup_pagelist_highmark(pcp, + pageset_set_high(pcp, (zone->managed_pages / percpu_pagelist_fraction)); else @@ -5599,8 +5599,8 @@ int percpu_pagelist_fraction_sysctl_handler(ctl_table *table, int write, unsigned long high; high = zone->managed_pages / percpu_pagelist_fraction; for_each_possible_cpu(cpu) - setup_pagelist_highmark( - per_cpu_ptr(zone->pageset, cpu), high); + pageset_set_high(per_cpu_ptr(zone->pageset, cpu), + high); } mutex_unlock(&pcp_batch_high_lock); return 0; -- cgit v1.2.3 From 169f6c1999ca6d0c5e06e8d810817ed3d1ebf017 Mon Sep 17 00:00:00 2001 From: Cody P Schafer Date: Wed, 3 Jul 2013 15:01:41 -0700 Subject: mm/page_alloc: don't re-init pageset in zone_pcp_update() When memory hotplug is triggered, we call pageset_init() on per-cpu-pagesets which both contain pages and are in use, causing both the leakage of those pages and (potentially) bad behaviour if a page is allocated from a pageset while it is being cleared. Avoid this by factoring out pageset_set_high_and_batch() (which contains all needed logic too set a pageset's ->high and ->batch inrespective of system state) from zone_pageset_init() and using the new pageset_set_high_and_batch() instead of zone_pageset_init() in zone_pcp_update(). Signed-off-by: Cody P Schafer Cc: Valdis Kletnieks Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/page_alloc.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 03a3f943d98e..fab9506273be 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -4104,11 +4104,9 @@ static void pageset_set_high(struct per_cpu_pageset *p, pageset_update(&p->pcp, high, batch); } -static void __meminit zone_pageset_init(struct zone *zone, int cpu) +static void __meminit pageset_set_high_and_batch(struct zone *zone, + struct per_cpu_pageset *pcp) { - struct per_cpu_pageset *pcp = per_cpu_ptr(zone->pageset, cpu); - - pageset_init(pcp); if (percpu_pagelist_fraction) pageset_set_high(pcp, (zone->managed_pages / @@ -4117,6 +4115,14 @@ static void __meminit zone_pageset_init(struct zone *zone, int cpu) pageset_set_batch(pcp, zone_batchsize(zone)); } +static void __meminit zone_pageset_init(struct zone *zone, int cpu) +{ + struct per_cpu_pageset *pcp = per_cpu_ptr(zone->pageset, cpu); + + pageset_init(pcp); + pageset_set_high_and_batch(zone, pcp); +} + static void __meminit setup_zone_pageset(struct zone *zone) { int cpu; @@ -6100,7 +6106,8 @@ void __meminit zone_pcp_update(struct zone *zone) unsigned cpu; mutex_lock(&pcp_batch_high_lock); for_each_possible_cpu(cpu) - zone_pageset_init(zone, cpu); + pageset_set_high_and_batch(zone, + per_cpu_ptr(zone->pageset, cpu)); mutex_unlock(&pcp_batch_high_lock); } #endif -- cgit v1.2.3 From 75485363ce8552698bfb9970d901f755d5713cca Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Wed, 3 Jul 2013 15:01:42 -0700 Subject: mm: vmscan: limit the number of pages kswapd reclaims at each priority This series does not fix all the current known problems with reclaim but it addresses one important swapping bug when there is background IO. Changelog since V3 - Drop the slab shrink changes in light of Glaubers series and discussions highlighted that there were a number of potential problems with the patch. (mel) - Rebased to 3.10-rc1 Changelog since V2 - Preserve ratio properly for proportional scanning (kamezawa) Changelog since V1 - Rename ZONE_DIRTY to ZONE_TAIL_LRU_DIRTY (andi) - Reformat comment in shrink_page_list (andi) - Clarify some comments (dhillf) - Rework how the proportional scanning is preserved - Add PageReclaim check before kswapd starts writeback - Reset sc.nr_reclaimed on every full zone scan Kswapd and page reclaim behaviour has been screwy in one way or the other for a long time. Very broadly speaking it worked in the far past because machines were limited in memory so it did not have that many pages to scan and it stalled congestion_wait() frequently to prevent it going completely nuts. In recent times it has behaved very unsatisfactorily with some of the problems compounded by the removal of stall logic and the introduction of transparent hugepage support with high-order reclaims. There are many variations of bugs that are rooted in this area. One example is reports of a large copy operations or backup causing the machine to grind to a halt or applications pushed to swap. Sometimes in low memory situations a large percentage of memory suddenly gets reclaimed. In other cases an application starts and kswapd hits 100% CPU usage for prolonged periods of time and so on. There is now talk of introducing features like an extra free kbytes tunable to work around aspects of the problem instead of trying to deal with it. It's compounded by the problem that it can be very workload and machine specific. This series aims at addressing some of the worst of these problems without attempting to fundmentally alter how page reclaim works. Patches 1-2 limits the number of pages kswapd reclaims while still obeying the anon/file proportion of the LRUs it should be scanning. Patches 3-4 control how and when kswapd raises its scanning priority and deletes the scanning restart logic which is tricky to follow. Patch 5 notes that it is too easy for kswapd to reach priority 0 when scanning and then reclaim the world. Down with that sort of thing. Patch 6 notes that kswapd starts writeback based on scanning priority which is not necessarily related to dirty pages. It will have kswapd writeback pages if a number of unqueued dirty pages have been recently encountered at the tail of the LRU. Patch 7 notes that sometimes kswapd should stall waiting on IO to complete to reduce LRU churn and the likelihood that it'll reclaim young clean pages or push applications to swap. It will cause kswapd to block on IO if it detects that pages being reclaimed under writeback are recycling through the LRU before the IO completes. Patchies 8-9 are cosmetic but balance_pgdat() is easier to follow after they are applied. This was tested using memcached+memcachetest while some background IO was in progress as implemented by the parallel IO tests implement in MM Tests. memcachetest benchmarks how many operations/second memcached can service and it is run multiple times. It starts with no background IO and then re-runs the test with larger amounts of IO in the background to roughly simulate a large copy in progress. The expectation is that the IO should have little or no impact on memcachetest which is running entirely in memory. 3.10.0-rc1 3.10.0-rc1 vanilla lessdisrupt-v4 Ops memcachetest-0M 22155.00 ( 0.00%) 22180.00 ( 0.11%) Ops memcachetest-715M 22720.00 ( 0.00%) 22355.00 ( -1.61%) Ops memcachetest-2385M 3939.00 ( 0.00%) 23450.00 (495.33%) Ops memcachetest-4055M 3628.00 ( 0.00%) 24341.00 (570.92%) Ops io-duration-0M 0.00 ( 0.00%) 0.00 ( 0.00%) Ops io-duration-715M 12.00 ( 0.00%) 7.00 ( 41.67%) Ops io-duration-2385M 118.00 ( 0.00%) 21.00 ( 82.20%) Ops io-duration-4055M 162.00 ( 0.00%) 36.00 ( 77.78%) Ops swaptotal-0M 0.00 ( 0.00%) 0.00 ( 0.00%) Ops swaptotal-715M 140134.00 ( 0.00%) 18.00 ( 99.99%) Ops swaptotal-2385M 392438.00 ( 0.00%) 0.00 ( 0.00%) Ops swaptotal-4055M 449037.00 ( 0.00%) 27864.00 ( 93.79%) Ops swapin-0M 0.00 ( 0.00%) 0.00 ( 0.00%) Ops swapin-715M 0.00 ( 0.00%) 0.00 ( 0.00%) Ops swapin-2385M 148031.00 ( 0.00%) 0.00 ( 0.00%) Ops swapin-4055M 135109.00 ( 0.00%) 0.00 ( 0.00%) Ops minorfaults-0M 1529984.00 ( 0.00%) 1530235.00 ( -0.02%) Ops minorfaults-715M 1794168.00 ( 0.00%) 1613750.00 ( 10.06%) Ops minorfaults-2385M 1739813.00 ( 0.00%) 1609396.00 ( 7.50%) Ops minorfaults-4055M 1754460.00 ( 0.00%) 1614810.00 ( 7.96%) Ops majorfaults-0M 0.00 ( 0.00%) 0.00 ( 0.00%) Ops majorfaults-715M 185.00 ( 0.00%) 180.00 ( 2.70%) Ops majorfaults-2385M 24472.00 ( 0.00%) 101.00 ( 99.59%) Ops majorfaults-4055M 22302.00 ( 0.00%) 229.00 ( 98.97%) Note how the vanilla kernels performance collapses when there is enough IO taking place in the background. This drop in performance is part of what users complain of when they start backups. Note how the swapin and major fault figures indicate that processes were being pushed to swap prematurely. With the series applied, there is no noticable performance drop and while there is still some swap activity, it's tiny. 20 iterations of this test were run in total and averaged. Every 5 iterations, additional IO was generated in the background using dd to measure how the workload was impacted. The 0M, 715M, 2385M and 4055M subblock refer to the amount of IO going on in the background at each iteration. So memcachetest-2385M is reporting how many transactions/second memcachetest recorded on average over 5 iterations while there was 2385M of IO going on in the ground. There are six blocks of information reported here memcachetest is the transactions/second reported by memcachetest. In the vanilla kernel note that performance drops from around 22K/sec to just under 4K/second when there is 2385M of IO going on in the background. This is one type of performance collapse users complain about if a large cp or backup starts in the background io-duration refers to how long it takes for the background IO to complete. It's showing that with the patched kernel that the IO completes faster while not interfering with the memcache workload swaptotal is the total amount of swap traffic. With the patched kernel, the total amount of swapping is much reduced although it is still not zero. swapin in this case is an indication as to whether we are swap trashing. The closer the swapin/swapout ratio is to 1, the worse the trashing is. Note with the patched kernel that there is no swapin activity indicating that all the pages swapped were really inactive unused pages. minorfaults are just minor faults. An increased number of minor faults can indicate that page reclaim is unmapping the pages but not swapping them out before they are faulted back in. With the patched kernel, there is only a small change in minor faults majorfaults are just major faults in the target workload and a high number can indicate that a workload is being prematurely swapped. With the patched kernel, major faults are much reduced. As there are no swapin's recorded so it's not being swapped. The likely explanation is that that libraries or configuration files used by the workload during startup get paged out by the background IO. Overall with the series applied, there is no noticable performance drop due to background IO and while there is still some swap activity, it's tiny and the lack of swapins imply that the swapped pages were inactive and unused. 3.10.0-rc1 3.10.0-rc1 vanilla lessdisrupt-v4 Page Ins 1234608 101892 Page Outs 12446272 11810468 Swap Ins 283406 0 Swap Outs 698469 27882 Direct pages scanned 0 136480 Kswapd pages scanned 6266537 5369364 Kswapd pages reclaimed 1088989 930832 Direct pages reclaimed 0 120901 Kswapd efficiency 17% 17% Kswapd velocity 5398.371 4635.115 Direct efficiency 100% 88% Direct velocity 0.000 117.817 Percentage direct scans 0% 2% Page writes by reclaim 1655843 4009929 Page writes file 957374 3982047 Page writes anon 698469 27882 Page reclaim immediate 5245 1745 Page rescued immediate 0 0 Slabs scanned 33664 25216 Direct inode steals 0 0 Kswapd inode steals 19409 778 Kswapd skipped wait 0 0 THP fault alloc 35 30 THP collapse alloc 472 401 THP splits 27 22 THP fault fallback 0 0 THP collapse fail 0 1 Compaction stalls 0 4 Compaction success 0 0 Compaction failures 0 4 Page migrate success 0 0 Page migrate failure 0 0 Compaction pages isolated 0 0 Compaction migrate scanned 0 0 Compaction free scanned 0 0 Compaction cost 0 0 NUMA PTE updates 0 0 NUMA hint faults 0 0 NUMA hint local faults 0 0 NUMA pages migrated 0 0 AutoNUMA cost 0 0 Unfortunately, note that there is a small amount of direct reclaim due to kswapd no longer reclaiming the world. ftrace indicates that the direct reclaim stalls are mostly harmless with the vast bulk of the stalls incurred by dd 23 tclsh-3367 38 memcachetest-13733 49 memcachetest-12443 57 tee-3368 1541 dd-13826 1981 dd-12539 A consequence of the direct reclaim for dd is that the processes for the IO workload may show a higher system CPU usage. There is also a risk that kswapd not reclaiming the world may mean that it stays awake balancing zones, does not stall on the appropriate events and continually scans pages it cannot reclaim consuming CPU. This will be visible as continued high CPU usage but in my own tests I only saw a single spike lasting less than a second and I did not observe any problems related to reclaim while running the series on my desktop. This patch: The number of pages kswapd can reclaim is bound by the number of pages it scans which is related to the size of the zone and the scanning priority. In many cases the priority remains low because it's reset every SWAP_CLUSTER_MAX reclaimed pages but in the event kswapd scans a large number of pages it cannot reclaim, it will raise the priority and potentially discard a large percentage of the zone as sc->nr_to_reclaim is ULONG_MAX. The user-visible effect is a reclaim "spike" where a large percentage of memory is suddenly freed. It would be bad enough if this was just unused memory but because of how anon/file pages are balanced it is possible that applications get pushed to swap unnecessarily. This patch limits the number of pages kswapd will reclaim to the high watermark. Reclaim will still overshoot due to it not being a hard limit as shrink_lruvec() will ignore the sc.nr_to_reclaim at DEF_PRIORITY but it prevents kswapd reclaiming the world at higher priorities. The number of pages it reclaims is not adjusted for high-order allocations as kswapd will reclaim excessively if it is to balance zones for high-order allocations. Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Reviewed-by: Michal Hocko Acked-by: Johannes Weiner Acked-by: KAMEZAWA Hiroyuki Cc: Jiri Slaby Cc: Valdis Kletnieks Tested-by: Zlatko Calusic Cc: dormando Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/vmscan.c | 49 +++++++++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index fa6a85378ee4..cdbc0699ea21 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -2600,6 +2600,32 @@ static bool prepare_kswapd_sleep(pg_data_t *pgdat, int order, long remaining, return pgdat_balanced(pgdat, order, classzone_idx); } +/* + * kswapd shrinks the zone by the number of pages required to reach + * the high watermark. + */ +static void kswapd_shrink_zone(struct zone *zone, + struct scan_control *sc, + unsigned long lru_pages) +{ + unsigned long nr_slab; + struct reclaim_state *reclaim_state = current->reclaim_state; + struct shrink_control shrink = { + .gfp_mask = sc->gfp_mask, + }; + + /* Reclaim above the high watermark. */ + sc->nr_to_reclaim = max(SWAP_CLUSTER_MAX, high_wmark_pages(zone)); + shrink_zone(zone, sc); + + reclaim_state->reclaimed_slab = 0; + nr_slab = shrink_slab(&shrink, sc->nr_scanned, lru_pages); + sc->nr_reclaimed += reclaim_state->reclaimed_slab; + + if (nr_slab == 0 && !zone_reclaimable(zone)) + zone->all_unreclaimable = 1; +} + /* * For kswapd, balance_pgdat() will work across all this node's zones until * they are all at high_wmark_pages(zone). @@ -2627,24 +2653,15 @@ static unsigned long balance_pgdat(pg_data_t *pgdat, int order, bool pgdat_is_balanced = false; int i; int end_zone = 0; /* Inclusive. 0 = ZONE_DMA */ - struct reclaim_state *reclaim_state = current->reclaim_state; unsigned long nr_soft_reclaimed; unsigned long nr_soft_scanned; struct scan_control sc = { .gfp_mask = GFP_KERNEL, .may_unmap = 1, .may_swap = 1, - /* - * kswapd doesn't want to be bailed out while reclaim. because - * we want to put equal scanning pressure on each zone. - */ - .nr_to_reclaim = ULONG_MAX, .order = order, .target_mem_cgroup = NULL, }; - struct shrink_control shrink = { - .gfp_mask = sc.gfp_mask, - }; loop_again: sc.priority = DEF_PRIORITY; sc.nr_reclaimed = 0; @@ -2716,7 +2733,7 @@ loop_again: */ for (i = 0; i <= end_zone; i++) { struct zone *zone = pgdat->node_zones + i; - int nr_slab, testorder; + int testorder; unsigned long balance_gap; if (!populated_zone(zone)) @@ -2764,16 +2781,8 @@ loop_again: if ((buffer_heads_over_limit && is_highmem_idx(i)) || !zone_balanced(zone, testorder, - balance_gap, end_zone)) { - shrink_zone(zone, &sc); - - reclaim_state->reclaimed_slab = 0; - nr_slab = shrink_slab(&shrink, sc.nr_scanned, lru_pages); - sc.nr_reclaimed += reclaim_state->reclaimed_slab; - - if (nr_slab == 0 && !zone_reclaimable(zone)) - zone->all_unreclaimable = 1; - } + balance_gap, end_zone)) + kswapd_shrink_zone(zone, &sc, lru_pages); /* * If we're getting trouble reclaiming, start doing -- cgit v1.2.3 From e82e0561dae9f3ae5a21fc2d3d3ccbe69d90be46 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Wed, 3 Jul 2013 15:01:44 -0700 Subject: mm: vmscan: obey proportional scanning requirements for kswapd Simplistically, the anon and file LRU lists are scanned proportionally depending on the value of vm.swappiness although there are other factors taken into account by get_scan_count(). The patch "mm: vmscan: Limit the number of pages kswapd reclaims" limits the number of pages kswapd reclaims but it breaks this proportional scanning and may evenly shrink anon/file LRUs regardless of vm.swappiness. This patch preserves the proportional scanning and reclaim. It does mean that kswapd will reclaim more than requested but the number of pages will be related to the high watermark. [mhocko@suse.cz: Correct proportional reclaim for memcg and simplify] [kamezawa.hiroyu@jp.fujitsu.com: Recalculate scan based on target] [hannes@cmpxchg.org: Account for already scanned pages properly] Signed-off-by: Mel Gorman Acked-by: Rik van Riel Reviewed-by: Michal Hocko Cc: Johannes Weiner Acked-by: KAMEZAWA Hiroyuki Cc: Jiri Slaby Cc: Valdis Kletnieks Tested-by: Zlatko Calusic Cc: dormando Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/vmscan.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 59 insertions(+), 8 deletions(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index cdbc0699ea21..26ad67f1962c 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -1822,17 +1822,25 @@ out: static void shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc) { unsigned long nr[NR_LRU_LISTS]; + unsigned long targets[NR_LRU_LISTS]; unsigned long nr_to_scan; enum lru_list lru; unsigned long nr_reclaimed = 0; unsigned long nr_to_reclaim = sc->nr_to_reclaim; struct blk_plug plug; + bool scan_adjusted = false; get_scan_count(lruvec, sc, nr); + /* Record the original scan target for proportional adjustments later */ + memcpy(targets, nr, sizeof(nr)); + blk_start_plug(&plug); while (nr[LRU_INACTIVE_ANON] || nr[LRU_ACTIVE_FILE] || nr[LRU_INACTIVE_FILE]) { + unsigned long nr_anon, nr_file, percentage; + unsigned long nr_scanned; + for_each_evictable_lru(lru) { if (nr[lru]) { nr_to_scan = min(nr[lru], SWAP_CLUSTER_MAX); @@ -1842,17 +1850,60 @@ static void shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc) lruvec, sc); } } + + if (nr_reclaimed < nr_to_reclaim || scan_adjusted) + continue; + /* - * On large memory systems, scan >> priority can become - * really large. This is fine for the starting priority; - * we want to put equal scanning pressure on each zone. - * However, if the VM has a harder time of freeing pages, - * with multiple processes reclaiming pages, the total - * freeing target can get unreasonably large. + * For global direct reclaim, reclaim only the number of pages + * requested. Less care is taken to scan proportionally as it + * is more important to minimise direct reclaim stall latency + * than it is to properly age the LRU lists. */ - if (nr_reclaimed >= nr_to_reclaim && - sc->priority < DEF_PRIORITY) + if (global_reclaim(sc) && !current_is_kswapd()) break; + + /* + * For kswapd and memcg, reclaim at least the number of pages + * requested. Ensure that the anon and file LRUs shrink + * proportionally what was requested by get_scan_count(). We + * stop reclaiming one LRU and reduce the amount scanning + * proportional to the original scan target. + */ + nr_file = nr[LRU_INACTIVE_FILE] + nr[LRU_ACTIVE_FILE]; + nr_anon = nr[LRU_INACTIVE_ANON] + nr[LRU_ACTIVE_ANON]; + + if (nr_file > nr_anon) { + unsigned long scan_target = targets[LRU_INACTIVE_ANON] + + targets[LRU_ACTIVE_ANON] + 1; + lru = LRU_BASE; + percentage = nr_anon * 100 / scan_target; + } else { + unsigned long scan_target = targets[LRU_INACTIVE_FILE] + + targets[LRU_ACTIVE_FILE] + 1; + lru = LRU_FILE; + percentage = nr_file * 100 / scan_target; + } + + /* Stop scanning the smaller of the LRU */ + nr[lru] = 0; + nr[lru + LRU_ACTIVE] = 0; + + /* + * Recalculate the other LRU scan count based on its original + * scan target and the percentage scanning already complete + */ + lru = (lru == LRU_FILE) ? LRU_BASE : LRU_FILE; + nr_scanned = targets[lru] - nr[lru]; + nr[lru] = targets[lru] * (100 - percentage) / 100; + nr[lru] -= min(nr[lru], nr_scanned); + + lru += LRU_ACTIVE; + nr_scanned = targets[lru] - nr[lru]; + nr[lru] = targets[lru] * (100 - percentage) / 100; + nr[lru] -= min(nr[lru], nr_scanned); + + scan_adjusted = true; } blk_finish_plug(&plug); sc->nr_reclaimed += nr_reclaimed; -- cgit v1.2.3 From b8e83b942a16eb73e63406592d3178207a4f07a1 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Wed, 3 Jul 2013 15:01:45 -0700 Subject: mm: vmscan: flatten kswapd priority loop kswapd stops raising the scanning priority when at least SWAP_CLUSTER_MAX pages have been reclaimed or the pgdat is considered balanced. It then rechecks if it needs to restart at DEF_PRIORITY and whether high-order reclaim needs to be reset. This is not wrong per-se but it is confusing to follow and forcing kswapd to stay at DEF_PRIORITY may require several restarts before it has scanned enough pages to meet the high watermark even at 100% efficiency. This patch irons out the logic a bit by controlling when priority is raised and removing the "goto loop_again". This patch has kswapd raise the scanning priority until it is scanning enough pages that it could meet the high watermark in one shrink of the LRU lists if it is able to reclaim at 100% efficiency. It will not raise the scanning prioirty higher unless it is failing to reclaim any pages. To avoid infinite looping for high-order allocation requests kswapd will not reclaim for high-order allocations when it has reclaimed at least twice the number of pages as the allocation request. Signed-off-by: Mel Gorman Acked-by: Johannes Weiner Reviewed-by: Michal Hocko Cc: KAMEZAWA Hiroyuki Cc: Rik van Riel Cc: Jiri Slaby Cc: Valdis Kletnieks Tested-by: Zlatko Calusic Cc: dormando Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/vmscan.c | 86 +++++++++++++++++++++++++++++-------------------------------- 1 file changed, 41 insertions(+), 45 deletions(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index 26ad67f1962c..1c10ee512215 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -2654,8 +2654,12 @@ static bool prepare_kswapd_sleep(pg_data_t *pgdat, int order, long remaining, /* * kswapd shrinks the zone by the number of pages required to reach * the high watermark. + * + * Returns true if kswapd scanned at least the requested number of pages to + * reclaim. This is used to determine if the scanning priority needs to be + * raised. */ -static void kswapd_shrink_zone(struct zone *zone, +static bool kswapd_shrink_zone(struct zone *zone, struct scan_control *sc, unsigned long lru_pages) { @@ -2675,6 +2679,8 @@ static void kswapd_shrink_zone(struct zone *zone, if (nr_slab == 0 && !zone_reclaimable(zone)) zone->all_unreclaimable = 1; + + return sc->nr_scanned >= sc->nr_to_reclaim; } /* @@ -2701,26 +2707,26 @@ static void kswapd_shrink_zone(struct zone *zone, static unsigned long balance_pgdat(pg_data_t *pgdat, int order, int *classzone_idx) { - bool pgdat_is_balanced = false; int i; int end_zone = 0; /* Inclusive. 0 = ZONE_DMA */ unsigned long nr_soft_reclaimed; unsigned long nr_soft_scanned; struct scan_control sc = { .gfp_mask = GFP_KERNEL, + .priority = DEF_PRIORITY, .may_unmap = 1, .may_swap = 1, + .may_writepage = !laptop_mode, .order = order, .target_mem_cgroup = NULL, }; -loop_again: - sc.priority = DEF_PRIORITY; - sc.nr_reclaimed = 0; - sc.may_writepage = !laptop_mode; count_vm_event(PAGEOUTRUN); do { unsigned long lru_pages = 0; + bool raise_priority = true; + + sc.nr_reclaimed = 0; /* * Scan in the highmem->dma direction for the highest @@ -2762,10 +2768,8 @@ loop_again: } } - if (i < 0) { - pgdat_is_balanced = true; + if (i < 0) goto out; - } for (i = 0; i <= end_zone; i++) { struct zone *zone = pgdat->node_zones + i; @@ -2832,8 +2836,16 @@ loop_again: if ((buffer_heads_over_limit && is_highmem_idx(i)) || !zone_balanced(zone, testorder, - balance_gap, end_zone)) - kswapd_shrink_zone(zone, &sc, lru_pages); + balance_gap, end_zone)) { + /* + * There should be no need to raise the + * scanning priority if enough pages are + * already being scanned that high + * watermark would be met at 100% efficiency. + */ + if (kswapd_shrink_zone(zone, &sc, lru_pages)) + raise_priority = false; + } /* * If we're getting trouble reclaiming, start doing @@ -2868,46 +2880,29 @@ loop_again: pfmemalloc_watermark_ok(pgdat)) wake_up(&pgdat->pfmemalloc_wait); - if (pgdat_balanced(pgdat, order, *classzone_idx)) { - pgdat_is_balanced = true; - break; /* kswapd: all done */ - } - /* - * We do this so kswapd doesn't build up large priorities for - * example when it is freeing in parallel with allocators. It - * matches the direct reclaim path behaviour in terms of impact - * on zone->*_priority. + * Fragmentation may mean that the system cannot be rebalanced + * for high-order allocations in all zones. If twice the + * allocation size has been reclaimed and the zones are still + * not balanced then recheck the watermarks at order-0 to + * prevent kswapd reclaiming excessively. Assume that a + * process requested a high-order can direct reclaim/compact. */ - if (sc.nr_reclaimed >= SWAP_CLUSTER_MAX) - break; - } while (--sc.priority >= 0); - -out: - if (!pgdat_is_balanced) { - cond_resched(); + if (order && sc.nr_reclaimed >= 2UL << order) + order = sc.order = 0; - try_to_freeze(); + /* Check if kswapd should be suspending */ + if (try_to_freeze() || kthread_should_stop()) + break; /* - * Fragmentation may mean that the system cannot be - * rebalanced for high-order allocations in all zones. - * At this point, if nr_reclaimed < SWAP_CLUSTER_MAX, - * it means the zones have been fully scanned and are still - * not balanced. For high-order allocations, there is - * little point trying all over again as kswapd may - * infinite loop. - * - * Instead, recheck all watermarks at order-0 as they - * are the most important. If watermarks are ok, kswapd will go - * back to sleep. High-order users can still perform direct - * reclaim if they wish. + * Raise priority if scanning rate is too low or there was no + * progress in reclaiming pages */ - if (sc.nr_reclaimed < SWAP_CLUSTER_MAX) - order = sc.order = 0; - - goto loop_again; - } + if (raise_priority || !sc.nr_reclaimed) + sc.priority--; + } while (sc.priority >= 0 && + !pgdat_balanced(pgdat, order, *classzone_idx)); /* * If kswapd was reclaiming at a higher order, it has the option of @@ -2936,6 +2931,7 @@ out: compact_pgdat(pgdat, order); } +out: /* * Return the order we were reclaiming at so prepare_kswapd_sleep() * makes a decision on the order we were last reclaiming at. However, -- cgit v1.2.3 From 2ab44f434586b8ccb11f781b4c2730492e6628f5 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Wed, 3 Jul 2013 15:01:47 -0700 Subject: mm: vmscan: decide whether to compact the pgdat based on reclaim progress In the past, kswapd makes a decision on whether to compact memory after the pgdat was considered balanced. This more or less worked but it is late to make such a decision and does not fit well now that kswapd makes a decision whether to exit the zone scanning loop depending on reclaim progress. This patch will compact a pgdat if at least the requested number of pages were reclaimed from unbalanced zones for a given priority. If any zone is currently balanced, kswapd will not call compaction as it is expected the necessary pages are already available. Signed-off-by: Mel Gorman Acked-by: Johannes Weiner Reviewed-by: Michal Hocko Cc: KAMEZAWA Hiroyuki Cc: Rik van Riel Cc: Jiri Slaby Cc: Valdis Kletnieks Tested-by: Zlatko Calusic Cc: dormando Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/vmscan.c | 59 ++++++++++++++++++++++++++++++----------------------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index 1c10ee512215..cd0980393bac 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -2661,7 +2661,8 @@ static bool prepare_kswapd_sleep(pg_data_t *pgdat, int order, long remaining, */ static bool kswapd_shrink_zone(struct zone *zone, struct scan_control *sc, - unsigned long lru_pages) + unsigned long lru_pages, + unsigned long *nr_attempted) { unsigned long nr_slab; struct reclaim_state *reclaim_state = current->reclaim_state; @@ -2677,6 +2678,9 @@ static bool kswapd_shrink_zone(struct zone *zone, nr_slab = shrink_slab(&shrink, sc->nr_scanned, lru_pages); sc->nr_reclaimed += reclaim_state->reclaimed_slab; + /* Account for the number of pages attempted to reclaim */ + *nr_attempted += sc->nr_to_reclaim; + if (nr_slab == 0 && !zone_reclaimable(zone)) zone->all_unreclaimable = 1; @@ -2724,7 +2728,9 @@ static unsigned long balance_pgdat(pg_data_t *pgdat, int order, do { unsigned long lru_pages = 0; + unsigned long nr_attempted = 0; bool raise_priority = true; + bool pgdat_needs_compaction = (order > 0); sc.nr_reclaimed = 0; @@ -2774,7 +2780,21 @@ static unsigned long balance_pgdat(pg_data_t *pgdat, int order, for (i = 0; i <= end_zone; i++) { struct zone *zone = pgdat->node_zones + i; + if (!populated_zone(zone)) + continue; + lru_pages += zone_reclaimable_pages(zone); + + /* + * If any zone is currently balanced then kswapd will + * not call compaction as it is expected that the + * necessary pages are already available. + */ + if (pgdat_needs_compaction && + zone_watermark_ok(zone, order, + low_wmark_pages(zone), + *classzone_idx, 0)) + pgdat_needs_compaction = false; } /* @@ -2843,7 +2863,8 @@ static unsigned long balance_pgdat(pg_data_t *pgdat, int order, * already being scanned that high * watermark would be met at 100% efficiency. */ - if (kswapd_shrink_zone(zone, &sc, lru_pages)) + if (kswapd_shrink_zone(zone, &sc, lru_pages, + &nr_attempted)) raise_priority = false; } @@ -2895,6 +2916,13 @@ static unsigned long balance_pgdat(pg_data_t *pgdat, int order, if (try_to_freeze() || kthread_should_stop()) break; + /* + * Compact if necessary and kswapd is reclaiming at least the + * high watermark number of pages as requsted + */ + if (pgdat_needs_compaction && sc.nr_reclaimed > nr_attempted) + compact_pgdat(pgdat, order); + /* * Raise priority if scanning rate is too low or there was no * progress in reclaiming pages @@ -2904,33 +2932,6 @@ static unsigned long balance_pgdat(pg_data_t *pgdat, int order, } while (sc.priority >= 0 && !pgdat_balanced(pgdat, order, *classzone_idx)); - /* - * If kswapd was reclaiming at a higher order, it has the option of - * sleeping without all zones being balanced. Before it does, it must - * ensure that the watermarks for order-0 on *all* zones are met and - * that the congestion flags are cleared. The congestion flag must - * be cleared as kswapd is the only mechanism that clears the flag - * and it is potentially going to sleep here. - */ - if (order) { - int zones_need_compaction = 1; - - for (i = 0; i <= end_zone; i++) { - struct zone *zone = pgdat->node_zones + i; - - if (!populated_zone(zone)) - continue; - - /* Check if the memory needs to be defragmented. */ - if (zone_watermark_ok(zone, order, - low_wmark_pages(zone), *classzone_idx, 0)) - zones_need_compaction = 0; - } - - if (zones_need_compaction) - compact_pgdat(pgdat, order); - } - out: /* * Return the order we were reclaiming at so prepare_kswapd_sleep() -- cgit v1.2.3 From 9aa41348a8d11427feec350b21dcdd4330fd20c4 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Wed, 3 Jul 2013 15:01:48 -0700 Subject: mm: vmscan: do not allow kswapd to scan at maximum priority Page reclaim at priority 0 will scan the entire LRU as priority 0 is considered to be a near OOM condition. Kswapd can reach priority 0 quite easily if it is encountering a large number of pages it cannot reclaim such as pages under writeback. When this happens, kswapd reclaims very aggressively even though there may be no real risk of allocation failure or OOM. This patch prevents kswapd reaching priority 0 and trying to reclaim the world. Direct reclaimers will still reach priority 0 in the event of an OOM situation. Signed-off-by: Mel Gorman Acked-by: Rik van Riel Acked-by: Johannes Weiner Reviewed-by: Michal Hocko Cc: KAMEZAWA Hiroyuki Cc: Jiri Slaby Cc: Valdis Kletnieks Tested-by: Zlatko Calusic Cc: dormando Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/vmscan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index cd0980393bac..1505c573719d 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -2929,7 +2929,7 @@ static unsigned long balance_pgdat(pg_data_t *pgdat, int order, */ if (raise_priority || !sc.nr_reclaimed) sc.priority--; - } while (sc.priority >= 0 && + } while (sc.priority >= 1 && !pgdat_balanced(pgdat, order, *classzone_idx)); out: -- cgit v1.2.3 From d43006d503ac921c7df4f94d13c17db6f13c9d26 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Wed, 3 Jul 2013 15:01:50 -0700 Subject: mm: vmscan: have kswapd writeback pages based on dirty pages encountered, not priority Currently kswapd queues dirty pages for writeback if scanning at an elevated priority but the priority kswapd scans at is not related to the number of unqueued dirty encountered. Since commit "mm: vmscan: Flatten kswapd priority loop", the priority is related to the size of the LRU and the zone watermark which is no indication as to whether kswapd should write pages or not. This patch tracks if an excessive number of unqueued dirty pages are being encountered at the end of the LRU. If so, it indicates that dirty pages are being recycled before flusher threads can clean them and flags the zone so that kswapd will start writing pages until the zone is balanced. Signed-off-by: Mel Gorman Acked-by: Johannes Weiner Reviewed-by: Michal Hocko Cc: KAMEZAWA Hiroyuki Cc: Rik van Riel Cc: Jiri Slaby Cc: Valdis Kletnieks Tested-by: Zlatko Calusic Cc: dormando Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mmzone.h | 9 +++++++++ mm/vmscan.c | 31 +++++++++++++++++++++++++------ 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 5c76737d836b..2aaf72f7e345 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -495,6 +495,10 @@ typedef enum { ZONE_CONGESTED, /* zone has many dirty pages backed by * a congested BDI */ + ZONE_TAIL_LRU_DIRTY, /* reclaim scanning has recently found + * many dirty file pages at the tail + * of the LRU. + */ } zone_flags_t; static inline void zone_set_flag(struct zone *zone, zone_flags_t flag) @@ -517,6 +521,11 @@ static inline int zone_is_reclaim_congested(const struct zone *zone) return test_bit(ZONE_CONGESTED, &zone->flags); } +static inline int zone_is_reclaim_dirty(const struct zone *zone) +{ + return test_bit(ZONE_TAIL_LRU_DIRTY, &zone->flags); +} + static inline int zone_is_reclaim_locked(const struct zone *zone) { return test_bit(ZONE_RECLAIM_LOCKED, &zone->flags); diff --git a/mm/vmscan.c b/mm/vmscan.c index 1505c573719d..d6c916d808ba 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -676,13 +676,14 @@ static unsigned long shrink_page_list(struct list_head *page_list, struct zone *zone, struct scan_control *sc, enum ttu_flags ttu_flags, - unsigned long *ret_nr_dirty, + unsigned long *ret_nr_unqueued_dirty, unsigned long *ret_nr_writeback, bool force_reclaim) { LIST_HEAD(ret_pages); LIST_HEAD(free_pages); int pgactivate = 0; + unsigned long nr_unqueued_dirty = 0; unsigned long nr_dirty = 0; unsigned long nr_congested = 0; unsigned long nr_reclaimed = 0; @@ -808,14 +809,17 @@ static unsigned long shrink_page_list(struct list_head *page_list, if (PageDirty(page)) { nr_dirty++; + if (!PageWriteback(page)) + nr_unqueued_dirty++; + /* * Only kswapd can writeback filesystem pages to - * avoid risk of stack overflow but do not writeback - * unless under significant pressure. + * avoid risk of stack overflow but only writeback + * if many dirty pages have been encountered. */ if (page_is_file_cache(page) && (!current_is_kswapd() || - sc->priority >= DEF_PRIORITY - 2)) { + !zone_is_reclaim_dirty(zone))) { /* * Immediately reclaim when written back. * Similar in principal to deactivate_page() @@ -960,7 +964,7 @@ keep: list_splice(&ret_pages, page_list); count_vm_events(PGACTIVATE, pgactivate); mem_cgroup_uncharge_end(); - *ret_nr_dirty += nr_dirty; + *ret_nr_unqueued_dirty += nr_unqueued_dirty; *ret_nr_writeback += nr_writeback; return nr_reclaimed; } @@ -1373,6 +1377,15 @@ shrink_inactive_list(unsigned long nr_to_scan, struct lruvec *lruvec, (nr_taken >> (DEF_PRIORITY - sc->priority))) wait_iff_congested(zone, BLK_RW_ASYNC, HZ/10); + /* + * Similarly, if many dirty pages are encountered that are not + * currently being written then flag that kswapd should start + * writing back pages. + */ + if (global_reclaim(sc) && nr_dirty && + nr_dirty >= (nr_taken >> (DEF_PRIORITY - sc->priority))) + zone_set_flag(zone, ZONE_TAIL_LRU_DIRTY); + trace_mm_vmscan_lru_shrink_inactive(zone->zone_pgdat->node_id, zone_idx(zone), nr_scanned, nr_reclaimed, @@ -2769,8 +2782,12 @@ static unsigned long balance_pgdat(pg_data_t *pgdat, int order, end_zone = i; break; } else { - /* If balanced, clear the congested flag */ + /* + * If balanced, clear the dirty and congested + * flags + */ zone_clear_flag(zone, ZONE_CONGESTED); + zone_clear_flag(zone, ZONE_TAIL_LRU_DIRTY); } } @@ -2888,8 +2905,10 @@ static unsigned long balance_pgdat(pg_data_t *pgdat, int order, * possible there are dirty pages backed by * congested BDIs but as pressure is relieved, * speculatively avoid congestion waits + * or writing pages from kswapd context. */ zone_clear_flag(zone, ZONE_CONGESTED); + zone_clear_flag(zone, ZONE_TAIL_LRU_DIRTY); } /* -- cgit v1.2.3 From 283aba9f9e0e4882bf09bd37a2983379a6fae805 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Wed, 3 Jul 2013 15:01:51 -0700 Subject: mm: vmscan: block kswapd if it is encountering pages under writeback Historically, kswapd used to congestion_wait() at higher priorities if it was not making forward progress. This made no sense as the failure to make progress could be completely independent of IO. It was later replaced by wait_iff_congested() and removed entirely by commit 258401a6 (mm: don't wait on congested zones in balance_pgdat()) as it was duplicating logic in shrink_inactive_list(). This is problematic. If kswapd encounters many pages under writeback and it continues to scan until it reaches the high watermark then it will quickly skip over the pages under writeback and reclaim clean young pages or push applications out to swap. The use of wait_iff_congested() is not suited to kswapd as it will only stall if the underlying BDI is really congested or a direct reclaimer was unable to write to the underlying BDI. kswapd bypasses the BDI congestion as it sets PF_SWAPWRITE but even if this was taken into account then it would cause direct reclaimers to stall on writeback which is not desirable. This patch sets a ZONE_WRITEBACK flag if direct reclaim or kswapd is encountering too many pages under writeback. If this flag is set and kswapd encounters a PageReclaim page under writeback then it'll assume that the LRU lists are being recycled too quickly before IO can complete and block waiting for some IO to complete. Signed-off-by: Mel Gorman Reviewed-by: Michal Hocko Acked-by: Rik van Riel Cc: Johannes Weiner Cc: KAMEZAWA Hiroyuki Cc: Jiri Slaby Cc: Valdis Kletnieks Tested-by: Zlatko Calusic Cc: dormando Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mmzone.h | 8 +++++ mm/vmscan.c | 82 ++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 68 insertions(+), 22 deletions(-) diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 2aaf72f7e345..fce64afba042 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -499,6 +499,9 @@ typedef enum { * many dirty file pages at the tail * of the LRU. */ + ZONE_WRITEBACK, /* reclaim scanning has recently found + * many pages under writeback + */ } zone_flags_t; static inline void zone_set_flag(struct zone *zone, zone_flags_t flag) @@ -526,6 +529,11 @@ static inline int zone_is_reclaim_dirty(const struct zone *zone) return test_bit(ZONE_TAIL_LRU_DIRTY, &zone->flags); } +static inline int zone_is_reclaim_writeback(const struct zone *zone) +{ + return test_bit(ZONE_WRITEBACK, &zone->flags); +} + static inline int zone_is_reclaim_locked(const struct zone *zone) { return test_bit(ZONE_RECLAIM_LOCKED, &zone->flags); diff --git a/mm/vmscan.c b/mm/vmscan.c index d6c916d808ba..1109de0c35bf 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -724,25 +724,55 @@ static unsigned long shrink_page_list(struct list_head *page_list, may_enter_fs = (sc->gfp_mask & __GFP_FS) || (PageSwapCache(page) && (sc->gfp_mask & __GFP_IO)); + /* + * If a page at the tail of the LRU is under writeback, there + * are three cases to consider. + * + * 1) If reclaim is encountering an excessive number of pages + * under writeback and this page is both under writeback and + * PageReclaim then it indicates that pages are being queued + * for IO but are being recycled through the LRU before the + * IO can complete. Waiting on the page itself risks an + * indefinite stall if it is impossible to writeback the + * page due to IO error or disconnected storage so instead + * block for HZ/10 or until some IO completes then clear the + * ZONE_WRITEBACK flag to recheck if the condition exists. + * + * 2) Global reclaim encounters a page, memcg encounters a + * page that is not marked for immediate reclaim or + * the caller does not have __GFP_IO. In this case mark + * the page for immediate reclaim and continue scanning. + * + * __GFP_IO is checked because a loop driver thread might + * enter reclaim, and deadlock if it waits on a page for + * which it is needed to do the write (loop masks off + * __GFP_IO|__GFP_FS for this reason); but more thought + * would probably show more reasons. + * + * Don't require __GFP_FS, since we're not going into the + * FS, just waiting on its writeback completion. Worryingly, + * ext4 gfs2 and xfs allocate pages with + * grab_cache_page_write_begin(,,AOP_FLAG_NOFS), so testing + * may_enter_fs here is liable to OOM on them. + * + * 3) memcg encounters a page that is not already marked + * PageReclaim. memcg does not have any dirty pages + * throttling so we could easily OOM just because too many + * pages are in writeback and there is nothing else to + * reclaim. Wait for the writeback to complete. + */ if (PageWriteback(page)) { - /* - * memcg doesn't have any dirty pages throttling so we - * could easily OOM just because too many pages are in - * writeback and there is nothing else to reclaim. - * - * Check __GFP_IO, certainly because a loop driver - * thread might enter reclaim, and deadlock if it waits - * on a page for which it is needed to do the write - * (loop masks off __GFP_IO|__GFP_FS for this reason); - * but more thought would probably show more reasons. - * - * Don't require __GFP_FS, since we're not going into - * the FS, just waiting on its writeback completion. - * Worryingly, ext4 gfs2 and xfs allocate pages with - * grab_cache_page_write_begin(,,AOP_FLAG_NOFS), so - * testing may_enter_fs here is liable to OOM on them. - */ - if (global_reclaim(sc) || + /* Case 1 above */ + if (current_is_kswapd() && + PageReclaim(page) && + zone_is_reclaim_writeback(zone)) { + unlock_page(page); + congestion_wait(BLK_RW_ASYNC, HZ/10); + zone_clear_flag(zone, ZONE_WRITEBACK); + goto keep; + + /* Case 2 above */ + } else if (global_reclaim(sc) || !PageReclaim(page) || !(sc->gfp_mask & __GFP_IO)) { /* * This is slightly racy - end_page_writeback() @@ -757,9 +787,13 @@ static unsigned long shrink_page_list(struct list_head *page_list, */ SetPageReclaim(page); nr_writeback++; + goto keep_locked; + + /* Case 3 above */ + } else { + wait_on_page_writeback(page); } - wait_on_page_writeback(page); } if (!force_reclaim) @@ -1374,8 +1408,10 @@ shrink_inactive_list(unsigned long nr_to_scan, struct lruvec *lruvec, * isolated page is PageWriteback */ if (nr_writeback && nr_writeback >= - (nr_taken >> (DEF_PRIORITY - sc->priority))) + (nr_taken >> (DEF_PRIORITY - sc->priority))) { wait_iff_congested(zone, BLK_RW_ASYNC, HZ/10); + zone_set_flag(zone, ZONE_WRITEBACK); + } /* * Similarly, if many dirty pages are encountered that are not @@ -2669,8 +2705,8 @@ static bool prepare_kswapd_sleep(pg_data_t *pgdat, int order, long remaining, * the high watermark. * * Returns true if kswapd scanned at least the requested number of pages to - * reclaim. This is used to determine if the scanning priority needs to be - * raised. + * reclaim or if the lack of progress was due to pages under writeback. + * This is used to determine if the scanning priority needs to be raised. */ static bool kswapd_shrink_zone(struct zone *zone, struct scan_control *sc, @@ -2697,6 +2733,8 @@ static bool kswapd_shrink_zone(struct zone *zone, if (nr_slab == 0 && !zone_reclaimable(zone)) zone->all_unreclaimable = 1; + zone_clear_flag(zone, ZONE_WRITEBACK); + return sc->nr_scanned >= sc->nr_to_reclaim; } -- cgit v1.2.3 From b7ea3c417b6c2e74ca1cb051568f60377908928d Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Wed, 3 Jul 2013 15:01:53 -0700 Subject: mm: vmscan: check if kswapd should writepage once per pgdat scan Currently kswapd checks if it should start writepage as it shrinks each zone without taking into consideration if the zone is balanced or not. This is not wrong as such but it does not make much sense either. This patch checks once per pgdat scan if kswapd should be writing pages. Signed-off-by: Mel Gorman Reviewed-by: Michal Hocko Acked-by: Rik van Riel Acked-by: Johannes Weiner Cc: KAMEZAWA Hiroyuki Cc: Jiri Slaby Cc: Valdis Kletnieks Tested-by: Zlatko Calusic Cc: dormando Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/vmscan.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index 1109de0c35bf..a2d0c6842616 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -2852,6 +2852,13 @@ static unsigned long balance_pgdat(pg_data_t *pgdat, int order, pgdat_needs_compaction = false; } + /* + * If we're getting trouble reclaiming, start doing writepage + * even in laptop mode. + */ + if (sc.priority < DEF_PRIORITY - 2) + sc.may_writepage = 1; + /* * Now scan the zone in the dma->highmem direction, stopping * at the last zone which needs scanning. @@ -2923,13 +2930,6 @@ static unsigned long balance_pgdat(pg_data_t *pgdat, int order, raise_priority = false; } - /* - * If we're getting trouble reclaiming, start doing - * writepage even in laptop mode. - */ - if (sc.priority < DEF_PRIORITY - 2) - sc.may_writepage = 1; - if (zone->all_unreclaimable) { if (end_zone && end_zone == i) end_zone--; -- cgit v1.2.3 From 7c954f6de6b630de30f265a079aad359f159ebe9 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Wed, 3 Jul 2013 15:01:54 -0700 Subject: mm: vmscan: move logic from balance_pgdat() to kswapd_shrink_zone() balance_pgdat() is very long and some of the logic can and should be internal to kswapd_shrink_zone(). Move it so the flow of balance_pgdat() is marginally easier to follow. Signed-off-by: Mel Gorman Acked-by: Johannes Weiner Reviewed-by: Michal Hocko Acked-by: Rik van Riel Cc: KAMEZAWA Hiroyuki Cc: Jiri Slaby Cc: Valdis Kletnieks Tested-by: Zlatko Calusic Cc: dormando Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/vmscan.c | 110 +++++++++++++++++++++++++++++------------------------------- 1 file changed, 54 insertions(+), 56 deletions(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index a2d0c6842616..4a43c289b23a 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -2709,18 +2709,53 @@ static bool prepare_kswapd_sleep(pg_data_t *pgdat, int order, long remaining, * This is used to determine if the scanning priority needs to be raised. */ static bool kswapd_shrink_zone(struct zone *zone, + int classzone_idx, struct scan_control *sc, unsigned long lru_pages, unsigned long *nr_attempted) { unsigned long nr_slab; + int testorder = sc->order; + unsigned long balance_gap; struct reclaim_state *reclaim_state = current->reclaim_state; struct shrink_control shrink = { .gfp_mask = sc->gfp_mask, }; + bool lowmem_pressure; /* Reclaim above the high watermark. */ sc->nr_to_reclaim = max(SWAP_CLUSTER_MAX, high_wmark_pages(zone)); + + /* + * Kswapd reclaims only single pages with compaction enabled. Trying + * too hard to reclaim until contiguous free pages have become + * available can hurt performance by evicting too much useful data + * from memory. Do not reclaim more than needed for compaction. + */ + if (IS_ENABLED(CONFIG_COMPACTION) && sc->order && + compaction_suitable(zone, sc->order) != + COMPACT_SKIPPED) + testorder = 0; + + /* + * We put equal pressure on every zone, unless one zone has way too + * many pages free already. The "too many pages" is defined as the + * high wmark plus a "gap" where the gap is either the low + * watermark or 1% of the zone, whichever is smaller. + */ + balance_gap = min(low_wmark_pages(zone), + (zone->managed_pages + KSWAPD_ZONE_BALANCE_GAP_RATIO-1) / + KSWAPD_ZONE_BALANCE_GAP_RATIO); + + /* + * If there is no low memory pressure or the zone is balanced then no + * reclaim is necessary + */ + lowmem_pressure = (buffer_heads_over_limit && is_highmem(zone)); + if (!lowmem_pressure && zone_balanced(zone, testorder, + balance_gap, classzone_idx)) + return true; + shrink_zone(zone, sc); reclaim_state->reclaimed_slab = 0; @@ -2735,6 +2770,18 @@ static bool kswapd_shrink_zone(struct zone *zone, zone_clear_flag(zone, ZONE_WRITEBACK); + /* + * If a zone reaches its high watermark, consider it to be no longer + * congested. It's possible there are dirty pages backed by congested + * BDIs but as pressure is relieved, speculatively avoid congestion + * waits. + */ + if (!zone->all_unreclaimable && + zone_balanced(zone, testorder, 0, classzone_idx)) { + zone_clear_flag(zone, ZONE_CONGESTED); + zone_clear_flag(zone, ZONE_TAIL_LRU_DIRTY); + } + return sc->nr_scanned >= sc->nr_to_reclaim; } @@ -2870,8 +2917,6 @@ static unsigned long balance_pgdat(pg_data_t *pgdat, int order, */ for (i = 0; i <= end_zone; i++) { struct zone *zone = pgdat->node_zones + i; - int testorder; - unsigned long balance_gap; if (!populated_zone(zone)) continue; @@ -2892,61 +2937,14 @@ static unsigned long balance_pgdat(pg_data_t *pgdat, int order, sc.nr_reclaimed += nr_soft_reclaimed; /* - * We put equal pressure on every zone, unless - * one zone has way too many pages free - * already. The "too many pages" is defined - * as the high wmark plus a "gap" where the - * gap is either the low watermark or 1% - * of the zone, whichever is smaller. - */ - balance_gap = min(low_wmark_pages(zone), - (zone->managed_pages + - KSWAPD_ZONE_BALANCE_GAP_RATIO-1) / - KSWAPD_ZONE_BALANCE_GAP_RATIO); - /* - * Kswapd reclaims only single pages with compaction - * enabled. Trying too hard to reclaim until contiguous - * free pages have become available can hurt performance - * by evicting too much useful data from memory. - * Do not reclaim more than needed for compaction. + * There should be no need to raise the scanning + * priority if enough pages are already being scanned + * that that high watermark would be met at 100% + * efficiency. */ - testorder = order; - if (IS_ENABLED(CONFIG_COMPACTION) && order && - compaction_suitable(zone, order) != - COMPACT_SKIPPED) - testorder = 0; - - if ((buffer_heads_over_limit && is_highmem_idx(i)) || - !zone_balanced(zone, testorder, - balance_gap, end_zone)) { - /* - * There should be no need to raise the - * scanning priority if enough pages are - * already being scanned that high - * watermark would be met at 100% efficiency. - */ - if (kswapd_shrink_zone(zone, &sc, lru_pages, - &nr_attempted)) - raise_priority = false; - } - - if (zone->all_unreclaimable) { - if (end_zone && end_zone == i) - end_zone--; - continue; - } - - if (zone_balanced(zone, testorder, 0, end_zone)) - /* - * If a zone reaches its high watermark, - * consider it to be no longer congested. It's - * possible there are dirty pages backed by - * congested BDIs but as pressure is relieved, - * speculatively avoid congestion waits - * or writing pages from kswapd context. - */ - zone_clear_flag(zone, ZONE_CONGESTED); - zone_clear_flag(zone, ZONE_TAIL_LRU_DIRTY); + if (kswapd_shrink_zone(zone, end_zone, &sc, + lru_pages, &nr_attempted)) + raise_priority = false; } /* -- cgit v1.2.3 From e2be15f6c3eecedfbe1550cca8d72c5057abbbd2 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Wed, 3 Jul 2013 15:01:57 -0700 Subject: mm: vmscan: stall page reclaim and writeback pages based on dirty/writepage pages encountered Further testing of the "Reduce system disruption due to kswapd" discovered a few problems. First and foremost, it's possible for pages under writeback to be freed which will lead to badness. Second, as pages were not being swapped the file LRU was being scanned faster and clean file pages were being reclaimed. In some cases this results in increased read IO to re-read data from disk. Third, more pages were being written from kswapd context which can adversly affect IO performance. Lastly, it was observed that PageDirty pages are not necessarily dirty on all filesystems (buffers can be clean while PageDirty is set and ->writepage generates no IO) and not all filesystems set PageWriteback when the page is being written (e.g. ext3). This disconnect confuses the reclaim stalling logic. This follow-up series is aimed at these problems. The tests were based on three kernels vanilla: kernel 3.9 as that is what the current mmotm uses as a baseline mmotm-20130522 is mmotm as of 22nd May with "Reduce system disruption due to kswapd" applied on top as per what should be in Andrew's tree right now lessdisrupt-v7r10 is this follow-up series on top of the mmotm kernel The first test used memcached+memcachetest while some background IO was in progress as implemented by the parallel IO tests implement in MM Tests. memcachetest benchmarks how many operations/second memcached can service. It starts with no background IO on a freshly created ext4 filesystem and then re-runs the test with larger amounts of IO in the background to roughly simulate a large copy in progress. The expectation is that the IO should have little or no impact on memcachetest which is running entirely in memory. parallelio 3.9.0 3.9.0 3.9.0 vanilla mm1-mmotm-20130522 mm1-lessdisrupt-v7r10 Ops memcachetest-0M 23117.00 ( 0.00%) 22780.00 ( -1.46%) 22763.00 ( -1.53%) Ops memcachetest-715M 23774.00 ( 0.00%) 23299.00 ( -2.00%) 22934.00 ( -3.53%) Ops memcachetest-2385M 4208.00 ( 0.00%) 24154.00 (474.00%) 23765.00 (464.76%) Ops memcachetest-4055M 4104.00 ( 0.00%) 25130.00 (512.33%) 24614.00 (499.76%) Ops io-duration-0M 0.00 ( 0.00%) 0.00 ( 0.00%) 0.00 ( 0.00%) Ops io-duration-715M 12.00 ( 0.00%) 7.00 ( 41.67%) 6.00 ( 50.00%) Ops io-duration-2385M 116.00 ( 0.00%) 21.00 ( 81.90%) 21.00 ( 81.90%) Ops io-duration-4055M 160.00 ( 0.00%) 36.00 ( 77.50%) 35.00 ( 78.12%) Ops swaptotal-0M 0.00 ( 0.00%) 0.00 ( 0.00%) 0.00 ( 0.00%) Ops swaptotal-715M 140138.00 ( 0.00%) 18.00 ( 99.99%) 18.00 ( 99.99%) Ops swaptotal-2385M 385682.00 ( 0.00%) 0.00 ( 0.00%) 0.00 ( 0.00%) Ops swaptotal-4055M 418029.00 ( 0.00%) 0.00 ( 0.00%) 0.00 ( 0.00%) Ops swapin-0M 0.00 ( 0.00%) 0.00 ( 0.00%) 0.00 ( 0.00%) Ops swapin-715M 144.00 ( 0.00%) 0.00 ( 0.00%) 0.00 ( 0.00%) Ops swapin-2385M 134227.00 ( 0.00%) 0.00 ( 0.00%) 0.00 ( 0.00%) Ops swapin-4055M 125618.00 ( 0.00%) 0.00 ( 0.00%) 0.00 ( 0.00%) Ops minorfaults-0M 1536429.00 ( 0.00%) 1531632.00 ( 0.31%) 1533541.00 ( 0.19%) Ops minorfaults-715M 1786996.00 ( 0.00%) 1612148.00 ( 9.78%) 1608832.00 ( 9.97%) Ops minorfaults-2385M 1757952.00 ( 0.00%) 1614874.00 ( 8.14%) 1613541.00 ( 8.21%) Ops minorfaults-4055M 1774460.00 ( 0.00%) 1633400.00 ( 7.95%) 1630881.00 ( 8.09%) Ops majorfaults-0M 1.00 ( 0.00%) 0.00 ( 0.00%) 0.00 ( 0.00%) Ops majorfaults-715M 184.00 ( 0.00%) 167.00 ( 9.24%) 166.00 ( 9.78%) Ops majorfaults-2385M 24444.00 ( 0.00%) 155.00 ( 99.37%) 93.00 ( 99.62%) Ops majorfaults-4055M 21357.00 ( 0.00%) 147.00 ( 99.31%) 134.00 ( 99.37%) memcachetest is the transactions/second reported by memcachetest. In the vanilla kernel note that performance drops from around 23K/sec to just over 4K/second when there is 2385M of IO going on in the background. With current mmotm, there is no collapse in performance and with this follow-up series there is little change. swaptotal is the total amount of swap traffic. With mmotm and the follow-up series, the total amount of swapping is much reduced. 3.9.0 3.9.0 3.9.0 vanillamm1-mmotm-20130522mm1-lessdisrupt-v7r10 Minor Faults 11160152 10706748 10622316 Major Faults 46305 755 678 Swap Ins 260249 0 0 Swap Outs 683860 18 18 Direct pages scanned 0 678 2520 Kswapd pages scanned 6046108 8814900 1639279 Kswapd pages reclaimed 1081954 1172267 1094635 Direct pages reclaimed 0 566 2304 Kswapd efficiency 17% 13% 66% Kswapd velocity 5217.560 7618.953 1414.879 Direct efficiency 100% 83% 91% Direct velocity 0.000 0.586 2.175 Percentage direct scans 0% 0% 0% Zone normal velocity 5105.086 6824.681 671.158 Zone dma32 velocity 112.473 794.858 745.896 Zone dma velocity 0.000 0.000 0.000 Page writes by reclaim 1929612.000 6861768.000 32821.000 Page writes file 1245752 6861750 32803 Page writes anon 683860 18 18 Page reclaim immediate 7484 40 239 Sector Reads 1130320 93996 86900 Sector Writes 13508052 10823500 11804436 Page rescued immediate 0 0 0 Slabs scanned 33536 27136 18560 Direct inode steals 0 0 0 Kswapd inode steals 8641 1035 0 Kswapd skipped wait 0 0 0 THP fault alloc 8 37 33 THP collapse alloc 508 552 515 THP splits 24 1 1 THP fault fallback 0 0 0 THP collapse fail 0 0 0 There are a number of observations to make here 1. Swap outs are almost eliminated. Swap ins are 0 indicating that the pages swapped were really unused anonymous pages. Related to that, major faults are much reduced. 2. kswapd efficiency was impacted by the initial series but with these follow-up patches, the efficiency is now at 66% indicating that far fewer pages were skipped during scanning due to dirty or writeback pages. 3. kswapd velocity is reduced indicating that fewer pages are being scanned with the follow-up series as kswapd now stalls when the tail of the LRU queue is full of unqueued dirty pages. The stall gives flushers a chance to catch-up so kswapd can reclaim clean pages when it wakes 4. In light of Zlatko's recent reports about zone scanning imbalances, mmtests now reports scanning velocity on a per-zone basis. With mainline, you can see that the scanning activity is dominated by the Normal zone with over 45 times more scanning in Normal than the DMA32 zone. With the series currently in mmotm, the ratio is slightly better but it is still the case that the bulk of scanning is in the highest zone. With this follow-up series, the ratio of scanning between the Normal and DMA32 zone is roughly equal. 5. As Dave Chinner observed, the current patches in mmotm increased the number of pages written from kswapd context which is expected to adversly impact IO performance. With the follow-up patches, far fewer pages are written from kswapd context than the mainline kernel 6. With the series in mmotm, fewer inodes were reclaimed by kswapd. With the follow-up series, there is less slab shrinking activity and no inodes were reclaimed. 7. Note that "Sectors Read" is drastically reduced implying that the source data being used for the IO is not being aggressively discarded due to page reclaim skipping over dirty pages and reclaiming clean pages. Note that the reducion in reads could also be due to inode data not being re-read from disk after a slab shrink. 3.9.0 3.9.0 3.9.0 vanillamm1-mmotm-20130522mm1-lessdisrupt-v7r10 Mean sda-avgqz 166.99 32.09 33.44 Mean sda-await 853.64 192.76 185.43 Mean sda-r_await 6.31 9.24 5.97 Mean sda-w_await 2992.81 202.65 192.43 Max sda-avgqz 1409.91 718.75 698.98 Max sda-await 6665.74 3538.00 3124.23 Max sda-r_await 58.96 111.95 58.00 Max sda-w_await 28458.94 3977.29 3148.61 In light of the changes in writes from reclaim context, the number of reads and Dave Chinner's concerns about IO performance I took a closer look at the IO stats for the test disk. Few observations 1. The average queue size is reduced by the initial series and roughly the same with this follow up. 2. Average wait times for writes are reduced and as the IO is completing faster it at least implies that the gain is because flushers are writing the files efficiently instead of page reclaim getting in the way. 3. The reduction in maximum write latency is staggering. 28 seconds down to 3 seconds. Jan Kara asked how NFS is affected by all of this. Unstable pages can be taken into account as one of the patches in the series shows but it is still the case that filesystems with unusual handling of dirty or writeback could still be treated better. Tests like postmark, fsmark and largedd showed up nothing useful. On my test setup, pages are simply not being written back from reclaim context with or without the patches and there are no changes in performance. My test setup probably is just not strong enough network-wise to be really interesting. I ran a longer-lived memcached test with IO going to NFS instead of a local disk parallelio 3.9.0 3.9.0 3.9.0 vanilla mm1-mmotm-20130522 mm1-lessdisrupt-v7r10 Ops memcachetest-0M 23323.00 ( 0.00%) 23241.00 ( -0.35%) 23321.00 ( -0.01%) Ops memcachetest-715M 25526.00 ( 0.00%) 24763.00 ( -2.99%) 23242.00 ( -8.95%) Ops memcachetest-2385M 8814.00 ( 0.00%) 26924.00 (205.47%) 23521.00 (166.86%) Ops memcachetest-4055M 5835.00 ( 0.00%) 26827.00 (359.76%) 25560.00 (338.05%) Ops io-duration-0M 0.00 ( 0.00%) 0.00 ( 0.00%) 0.00 ( 0.00%) Ops io-duration-715M 65.00 ( 0.00%) 71.00 ( -9.23%) 11.00 ( 83.08%) Ops io-duration-2385M 129.00 ( 0.00%) 94.00 ( 27.13%) 53.00 ( 58.91%) Ops io-duration-4055M 301.00 ( 0.00%) 100.00 ( 66.78%) 108.00 ( 64.12%) Ops swaptotal-0M 0.00 ( 0.00%) 0.00 ( 0.00%) 0.00 ( 0.00%) Ops swaptotal-715M 14394.00 ( 0.00%) 949.00 ( 93.41%) 63.00 ( 99.56%) Ops swaptotal-2385M 401483.00 ( 0.00%) 24437.00 ( 93.91%) 30118.00 ( 92.50%) Ops swaptotal-4055M 554123.00 ( 0.00%) 35688.00 ( 93.56%) 63082.00 ( 88.62%) Ops swapin-0M 0.00 ( 0.00%) 0.00 ( 0.00%) 0.00 ( 0.00%) Ops swapin-715M 4522.00 ( 0.00%) 560.00 ( 87.62%) 63.00 ( 98.61%) Ops swapin-2385M 169861.00 ( 0.00%) 5026.00 ( 97.04%) 13917.00 ( 91.81%) Ops swapin-4055M 192374.00 ( 0.00%) 10056.00 ( 94.77%) 25729.00 ( 86.63%) Ops minorfaults-0M 1445969.00 ( 0.00%) 1520878.00 ( -5.18%) 1454024.00 ( -0.56%) Ops minorfaults-715M 1557288.00 ( 0.00%) 1528482.00 ( 1.85%) 1535776.00 ( 1.38%) Ops minorfaults-2385M 1692896.00 ( 0.00%) 1570523.00 ( 7.23%) 1559622.00 ( 7.87%) Ops minorfaults-4055M 1654985.00 ( 0.00%) 1581456.00 ( 4.44%) 1596713.00 ( 3.52%) Ops majorfaults-0M 0.00 ( 0.00%) 1.00 (-99.00%) 0.00 ( 0.00%) Ops majorfaults-715M 763.00 ( 0.00%) 265.00 ( 65.27%) 75.00 ( 90.17%) Ops majorfaults-2385M 23861.00 ( 0.00%) 894.00 ( 96.25%) 2189.00 ( 90.83%) Ops majorfaults-4055M 27210.00 ( 0.00%) 1569.00 ( 94.23%) 4088.00 ( 84.98%) 1. Performance does not collapse due to IO which is good. IO is also completing faster. Note with mmotm, IO completes in a third of the time and faster again with this series applied 2. Swapping is reduced, although not eliminated. The figures for the follow-up look bad but it does vary a bit as the stalling is not perfect for nfs or filesystems like ext3 with unusual handling of dirty and writeback pages 3. There are swapins, particularly with larger amounts of IO indicating that active pages are being reclaimed. However, the number of much reduced. 3.9.0 3.9.0 3.9.0 vanillamm1-mmotm-20130522mm1-lessdisrupt-v7r10 Minor Faults 36339175 35025445 35219699 Major Faults 310964 27108 51887 Swap Ins 2176399 173069 333316 Swap Outs 3344050 357228 504824 Direct pages scanned 8972 77283 43242 Kswapd pages scanned 20899983 8939566 14772851 Kswapd pages reclaimed 6193156 5172605 5231026 Direct pages reclaimed 8450 73802 39514 Kswapd efficiency 29% 57% 35% Kswapd velocity 3929.743 1847.499 3058.840 Direct efficiency 94% 95% 91% Direct velocity 1.687 15.972 8.954 Percentage direct scans 0% 0% 0% Zone normal velocity 3721.907 939.103 2185.142 Zone dma32 velocity 209.522 924.368 882.651 Zone dma velocity 0.000 0.000 0.000 Page writes by reclaim 4082185.000 526319.000 537114.000 Page writes file 738135 169091 32290 Page writes anon 3344050 357228 504824 Page reclaim immediate 9524 170 5595843 Sector Reads 8909900 861192 1483680 Sector Writes 13428980 1488744 2076800 Page rescued immediate 0 0 0 Slabs scanned 38016 31744 28672 Direct inode steals 0 0 0 Kswapd inode steals 424 0 0 Kswapd skipped wait 0 0 0 THP fault alloc 14 15 119 THP collapse alloc 1767 1569 1618 THP splits 30 29 25 THP fault fallback 0 0 0 THP collapse fail 8 5 0 Compaction stalls 17 41 100 Compaction success 7 31 95 Compaction failures 10 10 5 Page migrate success 7083 22157 62217 Page migrate failure 0 0 0 Compaction pages isolated 14847 48758 135830 Compaction migrate scanned 18328 48398 138929 Compaction free scanned 2000255 355827 1720269 Compaction cost 7 24 68 I guess the main takeaway again is the much reduced page writes from reclaim context and reduced reads. 3.9.0 3.9.0 3.9.0 vanillamm1-mmotm-20130522mm1-lessdisrupt-v7r10 Mean sda-avgqz 23.58 0.35 0.44 Mean sda-await 133.47 15.72 15.46 Mean sda-r_await 4.72 4.69 3.95 Mean sda-w_await 507.69 28.40 33.68 Max sda-avgqz 680.60 12.25 23.14 Max sda-await 3958.89 221.83 286.22 Max sda-r_await 63.86 61.23 67.29 Max sda-w_await 11710.38 883.57 1767.28 And as before, write wait times are much reduced. This patch: The patch "mm: vmscan: Have kswapd writeback pages based on dirty pages encountered, not priority" decides whether to writeback pages from reclaim context based on the number of dirty pages encountered. This situation is flagged too easily and flushers are not given the chance to catch up resulting in more pages being written from reclaim context and potentially impacting IO performance. The check for PageWriteback is also misplaced as it happens within a PageDirty check which is nonsense as the dirty may have been cleared for IO. The accounting is updated very late and pages that are already under writeback, were reactivated, could not unmapped or could not be released are all missed. Similarly, a page is considered congested for reasons other than being congested and pages that cannot be written out in the correct context are skipped. Finally, it considers stalling and writing back filesystem pages due to encountering dirty anonymous pages at the tail of the LRU which is dumb. This patch causes kswapd to begin writing filesystem pages from reclaim context only if page reclaim found that all filesystem pages at the tail of the LRU were unqueued dirty pages. Before it starts writing filesystem pages, it will stall to give flushers a chance to catch up. The decision on whether wait_iff_congested is also now determined by dirty filesystem pages only. Congested pages are based on whether the underlying BDI is congested regardless of the context of the reclaiming process. Signed-off-by: Mel Gorman Cc: Johannes Weiner Cc: Michal Hocko Cc: Rik van Riel Cc: KAMEZAWA Hiroyuki Cc: Jiri Slaby Cc: Valdis Kletnieks Cc: Zlatko Calusic Cc: dormando Cc: Trond Myklebust Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/vmscan.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 13 deletions(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index 4a43c289b23a..999ef0b9399a 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -669,6 +669,25 @@ static enum page_references page_check_references(struct page *page, return PAGEREF_RECLAIM; } +/* Check if a page is dirty or under writeback */ +static void page_check_dirty_writeback(struct page *page, + bool *dirty, bool *writeback) +{ + /* + * Anonymous pages are not handled by flushers and must be written + * from reclaim context. Do not stall reclaim based on them + */ + if (!page_is_file_cache(page)) { + *dirty = false; + *writeback = false; + return; + } + + /* By default assume that the page flags are accurate */ + *dirty = PageDirty(page); + *writeback = PageWriteback(page); +} + /* * shrink_page_list() returns the number of reclaimed pages */ @@ -697,6 +716,7 @@ static unsigned long shrink_page_list(struct list_head *page_list, struct page *page; int may_enter_fs; enum page_references references = PAGEREF_RECLAIM_CLEAN; + bool dirty, writeback; cond_resched(); @@ -724,6 +744,24 @@ static unsigned long shrink_page_list(struct list_head *page_list, may_enter_fs = (sc->gfp_mask & __GFP_FS) || (PageSwapCache(page) && (sc->gfp_mask & __GFP_IO)); + /* + * The number of dirty pages determines if a zone is marked + * reclaim_congested which affects wait_iff_congested. kswapd + * will stall and start writing pages if the tail of the LRU + * is all dirty unqueued pages. + */ + page_check_dirty_writeback(page, &dirty, &writeback); + if (dirty || writeback) + nr_dirty++; + + if (dirty && !writeback) + nr_unqueued_dirty++; + + /* Treat this page as congested if underlying BDI is */ + mapping = page_mapping(page); + if (mapping && bdi_write_congested(mapping->backing_dev_info)) + nr_congested++; + /* * If a page at the tail of the LRU is under writeback, there * are three cases to consider. @@ -819,9 +857,10 @@ static unsigned long shrink_page_list(struct list_head *page_list, if (!add_to_swap(page, page_list)) goto activate_locked; may_enter_fs = 1; - } - mapping = page_mapping(page); + /* Adding to swap updated mapping */ + mapping = page_mapping(page); + } /* * The page is mapped into the page tables of one or more @@ -841,11 +880,6 @@ static unsigned long shrink_page_list(struct list_head *page_list, } if (PageDirty(page)) { - nr_dirty++; - - if (!PageWriteback(page)) - nr_unqueued_dirty++; - /* * Only kswapd can writeback filesystem pages to * avoid risk of stack overflow but only writeback @@ -876,7 +910,6 @@ static unsigned long shrink_page_list(struct list_head *page_list, /* Page is dirty, try to write it out here */ switch (pageout(page, mapping, sc)) { case PAGE_KEEP: - nr_congested++; goto keep_locked; case PAGE_ACTIVATE: goto activate_locked; @@ -1318,7 +1351,7 @@ shrink_inactive_list(unsigned long nr_to_scan, struct lruvec *lruvec, unsigned long nr_scanned; unsigned long nr_reclaimed = 0; unsigned long nr_taken; - unsigned long nr_dirty = 0; + unsigned long nr_unqueued_dirty = 0; unsigned long nr_writeback = 0; isolate_mode_t isolate_mode = 0; int file = is_file_lru(lru); @@ -1361,7 +1394,7 @@ shrink_inactive_list(unsigned long nr_to_scan, struct lruvec *lruvec, return 0; nr_reclaimed = shrink_page_list(&page_list, zone, sc, TTU_UNMAP, - &nr_dirty, &nr_writeback, false); + &nr_unqueued_dirty, &nr_writeback, false); spin_lock_irq(&zone->lru_lock); @@ -1416,11 +1449,13 @@ shrink_inactive_list(unsigned long nr_to_scan, struct lruvec *lruvec, /* * Similarly, if many dirty pages are encountered that are not * currently being written then flag that kswapd should start - * writing back pages. + * writing back pages and stall to give a chance for flushers + * to catch up. */ - if (global_reclaim(sc) && nr_dirty && - nr_dirty >= (nr_taken >> (DEF_PRIORITY - sc->priority))) + if (global_reclaim(sc) && nr_unqueued_dirty == nr_taken) { + congestion_wait(BLK_RW_ASYNC, HZ/10); zone_set_flag(zone, ZONE_TAIL_LRU_DIRTY); + } trace_mm_vmscan_lru_shrink_inactive(zone->zone_pgdat->node_id, zone_idx(zone), -- cgit v1.2.3 From b1a6f21e3b2315d46ae8af88a8f4eb8ea2763107 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Wed, 3 Jul 2013 15:01:58 -0700 Subject: mm: vmscan: stall page reclaim after a list of pages have been processed Commit "mm: vmscan: Block kswapd if it is encountering pages under writeback" blocks page reclaim if it encounters pages under writeback marked for immediate reclaim. It blocks while pages are still isolated from the LRU which is unnecessary. This patch defers the blocking until after the isolated pages have been processed and tidies up some of the comments. Signed-off-by: Mel Gorman Cc: Johannes Weiner Cc: Michal Hocko Cc: Rik van Riel Cc: KAMEZAWA Hiroyuki Cc: Jiri Slaby Cc: Valdis Kletnieks Cc: Zlatko Calusic Cc: dormando Cc: Trond Myklebust Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/vmscan.c | 49 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index 999ef0b9399a..5b1a79c8f0cb 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -697,6 +697,7 @@ static unsigned long shrink_page_list(struct list_head *page_list, enum ttu_flags ttu_flags, unsigned long *ret_nr_unqueued_dirty, unsigned long *ret_nr_writeback, + unsigned long *ret_nr_immediate, bool force_reclaim) { LIST_HEAD(ret_pages); @@ -707,6 +708,7 @@ static unsigned long shrink_page_list(struct list_head *page_list, unsigned long nr_congested = 0; unsigned long nr_reclaimed = 0; unsigned long nr_writeback = 0; + unsigned long nr_immediate = 0; cond_resched(); @@ -773,8 +775,8 @@ static unsigned long shrink_page_list(struct list_head *page_list, * IO can complete. Waiting on the page itself risks an * indefinite stall if it is impossible to writeback the * page due to IO error or disconnected storage so instead - * block for HZ/10 or until some IO completes then clear the - * ZONE_WRITEBACK flag to recheck if the condition exists. + * note that the LRU is being scanned too quickly and the + * caller can stall after page list has been processed. * * 2) Global reclaim encounters a page, memcg encounters a * page that is not marked for immediate reclaim or @@ -804,10 +806,8 @@ static unsigned long shrink_page_list(struct list_head *page_list, if (current_is_kswapd() && PageReclaim(page) && zone_is_reclaim_writeback(zone)) { - unlock_page(page); - congestion_wait(BLK_RW_ASYNC, HZ/10); - zone_clear_flag(zone, ZONE_WRITEBACK); - goto keep; + nr_immediate++; + goto keep_locked; /* Case 2 above */ } else if (global_reclaim(sc) || @@ -1033,6 +1033,7 @@ keep: mem_cgroup_uncharge_end(); *ret_nr_unqueued_dirty += nr_unqueued_dirty; *ret_nr_writeback += nr_writeback; + *ret_nr_immediate += nr_immediate; return nr_reclaimed; } @@ -1044,7 +1045,7 @@ unsigned long reclaim_clean_pages_from_list(struct zone *zone, .priority = DEF_PRIORITY, .may_unmap = 1, }; - unsigned long ret, dummy1, dummy2; + unsigned long ret, dummy1, dummy2, dummy3; struct page *page, *next; LIST_HEAD(clean_pages); @@ -1057,7 +1058,7 @@ unsigned long reclaim_clean_pages_from_list(struct zone *zone, ret = shrink_page_list(&clean_pages, zone, &sc, TTU_UNMAP|TTU_IGNORE_ACCESS, - &dummy1, &dummy2, true); + &dummy1, &dummy2, &dummy3, true); list_splice(&clean_pages, page_list); __mod_zone_page_state(zone, NR_ISOLATED_FILE, -ret); return ret; @@ -1353,6 +1354,7 @@ shrink_inactive_list(unsigned long nr_to_scan, struct lruvec *lruvec, unsigned long nr_taken; unsigned long nr_unqueued_dirty = 0; unsigned long nr_writeback = 0; + unsigned long nr_immediate = 0; isolate_mode_t isolate_mode = 0; int file = is_file_lru(lru); struct zone *zone = lruvec_zone(lruvec); @@ -1394,7 +1396,8 @@ shrink_inactive_list(unsigned long nr_to_scan, struct lruvec *lruvec, return 0; nr_reclaimed = shrink_page_list(&page_list, zone, sc, TTU_UNMAP, - &nr_unqueued_dirty, &nr_writeback, false); + &nr_unqueued_dirty, &nr_writeback, &nr_immediate, + false); spin_lock_irq(&zone->lru_lock); @@ -1447,14 +1450,28 @@ shrink_inactive_list(unsigned long nr_to_scan, struct lruvec *lruvec, } /* - * Similarly, if many dirty pages are encountered that are not - * currently being written then flag that kswapd should start - * writing back pages and stall to give a chance for flushers - * to catch up. + * memcg will stall in page writeback so only consider forcibly + * stalling for global reclaim */ - if (global_reclaim(sc) && nr_unqueued_dirty == nr_taken) { - congestion_wait(BLK_RW_ASYNC, HZ/10); - zone_set_flag(zone, ZONE_TAIL_LRU_DIRTY); + if (global_reclaim(sc)) { + /* + * If dirty pages are scanned that are not queued for IO, it + * implies that flushers are not keeping up. In this case, flag + * the zone ZONE_TAIL_LRU_DIRTY and kswapd will start writing + * pages from reclaim context. It will forcibly stall in the + * next check. + */ + if (nr_unqueued_dirty == nr_taken) + zone_set_flag(zone, ZONE_TAIL_LRU_DIRTY); + + /* + * In addition, if kswapd scans pages marked marked for + * immediate reclaim and under writeback (nr_immediate), it + * implies that pages are cycling through the LRU faster than + * they are written so also forcibly stall. + */ + if (nr_unqueued_dirty == nr_taken || nr_immediate) + congestion_wait(BLK_RW_ASYNC, HZ/10); } trace_mm_vmscan_lru_shrink_inactive(zone->zone_pgdat->node_id, -- cgit v1.2.3 From f7ab8db791a8692f5ed4201dbae25722c1732a8d Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Wed, 3 Jul 2013 15:02:00 -0700 Subject: mm: vmscan: set zone flags before blocking In shrink_page_list a decision may be made to stall and flag a zone as ZONE_WRITEBACK so that if a large number of unqueued dirty pages are encountered later then the reclaimer will stall. Set ZONE_WRITEBACK before potentially going to sleep so it is noticed sooner. Signed-off-by: Mel Gorman Cc: Johannes Weiner Cc: Michal Hocko Cc: Rik van Riel Cc: KAMEZAWA Hiroyuki Cc: Jiri Slaby Cc: Valdis Kletnieks Cc: Zlatko Calusic Cc: dormando Cc: Trond Myklebust Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/vmscan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index 5b1a79c8f0cb..5f80d018bffa 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -1445,8 +1445,8 @@ shrink_inactive_list(unsigned long nr_to_scan, struct lruvec *lruvec, */ if (nr_writeback && nr_writeback >= (nr_taken >> (DEF_PRIORITY - sc->priority))) { - wait_iff_congested(zone, BLK_RW_ASYNC, HZ/10); zone_set_flag(zone, ZONE_WRITEBACK); + wait_iff_congested(zone, BLK_RW_ASYNC, HZ/10); } /* -- cgit v1.2.3 From 8e950282804558e4605401b9c79c1d34f0d73507 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Wed, 3 Jul 2013 15:02:02 -0700 Subject: mm: vmscan: move direct reclaim wait_iff_congested into shrink_list shrink_inactive_list makes decisions on whether to stall based on the number of dirty pages encountered. The wait_iff_congested() call in shrink_page_list does no such thing and it's arbitrary. This patch moves the decision on whether to set ZONE_CONGESTED and the wait_iff_congested call into shrink_page_list. This keeps all the decisions on whether to stall or not in the one place. Signed-off-by: Mel Gorman Cc: Johannes Weiner Cc: Michal Hocko Cc: Rik van Riel Cc: KAMEZAWA Hiroyuki Cc: Jiri Slaby Cc: Valdis Kletnieks Cc: Zlatko Calusic Cc: dormando Cc: Trond Myklebust Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/vmscan.c | 62 ++++++++++++++++++++++++++++++++----------------------------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index 5f80d018bffa..4898daf074cf 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -695,7 +695,9 @@ static unsigned long shrink_page_list(struct list_head *page_list, struct zone *zone, struct scan_control *sc, enum ttu_flags ttu_flags, + unsigned long *ret_nr_dirty, unsigned long *ret_nr_unqueued_dirty, + unsigned long *ret_nr_congested, unsigned long *ret_nr_writeback, unsigned long *ret_nr_immediate, bool force_reclaim) @@ -1017,20 +1019,13 @@ keep: VM_BUG_ON(PageLRU(page) || PageUnevictable(page)); } - /* - * Tag a zone as congested if all the dirty pages encountered were - * backed by a congested BDI. In this case, reclaimers should just - * back off and wait for congestion to clear because further reclaim - * will encounter the same problem - */ - if (nr_dirty && nr_dirty == nr_congested && global_reclaim(sc)) - zone_set_flag(zone, ZONE_CONGESTED); - free_hot_cold_page_list(&free_pages, 1); list_splice(&ret_pages, page_list); count_vm_events(PGACTIVATE, pgactivate); mem_cgroup_uncharge_end(); + *ret_nr_dirty += nr_dirty; + *ret_nr_congested += nr_congested; *ret_nr_unqueued_dirty += nr_unqueued_dirty; *ret_nr_writeback += nr_writeback; *ret_nr_immediate += nr_immediate; @@ -1045,7 +1040,7 @@ unsigned long reclaim_clean_pages_from_list(struct zone *zone, .priority = DEF_PRIORITY, .may_unmap = 1, }; - unsigned long ret, dummy1, dummy2, dummy3; + unsigned long ret, dummy1, dummy2, dummy3, dummy4, dummy5; struct page *page, *next; LIST_HEAD(clean_pages); @@ -1057,8 +1052,8 @@ unsigned long reclaim_clean_pages_from_list(struct zone *zone, } ret = shrink_page_list(&clean_pages, zone, &sc, - TTU_UNMAP|TTU_IGNORE_ACCESS, - &dummy1, &dummy2, &dummy3, true); + TTU_UNMAP|TTU_IGNORE_ACCESS, + &dummy1, &dummy2, &dummy3, &dummy4, &dummy5, true); list_splice(&clean_pages, page_list); __mod_zone_page_state(zone, NR_ISOLATED_FILE, -ret); return ret; @@ -1352,6 +1347,8 @@ shrink_inactive_list(unsigned long nr_to_scan, struct lruvec *lruvec, unsigned long nr_scanned; unsigned long nr_reclaimed = 0; unsigned long nr_taken; + unsigned long nr_dirty = 0; + unsigned long nr_congested = 0; unsigned long nr_unqueued_dirty = 0; unsigned long nr_writeback = 0; unsigned long nr_immediate = 0; @@ -1396,8 +1393,9 @@ shrink_inactive_list(unsigned long nr_to_scan, struct lruvec *lruvec, return 0; nr_reclaimed = shrink_page_list(&page_list, zone, sc, TTU_UNMAP, - &nr_unqueued_dirty, &nr_writeback, &nr_immediate, - false); + &nr_dirty, &nr_unqueued_dirty, &nr_congested, + &nr_writeback, &nr_immediate, + false); spin_lock_irq(&zone->lru_lock); @@ -1431,7 +1429,7 @@ shrink_inactive_list(unsigned long nr_to_scan, struct lruvec *lruvec, * same way balance_dirty_pages() manages. * * This scales the number of dirty pages that must be under writeback - * before throttling depending on priority. It is a simple backoff + * before a zone gets flagged ZONE_WRITEBACK. It is a simple backoff * function that has the most effect in the range DEF_PRIORITY to * DEF_PRIORITY-2 which is the priority reclaim is considered to be * in trouble and reclaim is considered to be in trouble. @@ -1442,18 +1440,27 @@ shrink_inactive_list(unsigned long nr_to_scan, struct lruvec *lruvec, * ... * DEF_PRIORITY-6 For SWAP_CLUSTER_MAX isolated pages, throttle if any * isolated page is PageWriteback + * + * Once a zone is flagged ZONE_WRITEBACK, kswapd will count the number + * of pages under pages flagged for immediate reclaim and stall if any + * are encountered in the nr_immediate check below. */ if (nr_writeback && nr_writeback >= - (nr_taken >> (DEF_PRIORITY - sc->priority))) { + (nr_taken >> (DEF_PRIORITY - sc->priority))) zone_set_flag(zone, ZONE_WRITEBACK); - wait_iff_congested(zone, BLK_RW_ASYNC, HZ/10); - } /* * memcg will stall in page writeback so only consider forcibly * stalling for global reclaim */ if (global_reclaim(sc)) { + /* + * Tag a zone as congested if all the dirty pages scanned were + * backed by a congested BDI and wait_iff_congested will stall. + */ + if (nr_dirty && nr_dirty == nr_congested) + zone_set_flag(zone, ZONE_CONGESTED); + /* * If dirty pages are scanned that are not queued for IO, it * implies that flushers are not keeping up. In this case, flag @@ -1474,6 +1481,14 @@ shrink_inactive_list(unsigned long nr_to_scan, struct lruvec *lruvec, congestion_wait(BLK_RW_ASYNC, HZ/10); } + /* + * Stall direct reclaim for IO completions if underlying BDIs or zone + * is congested. Allow kswapd to continue until it starts encountering + * unqueued dirty pages or cycling through the LRU too quickly. + */ + if (!sc->hibernation_mode && !current_is_kswapd()) + wait_iff_congested(zone, BLK_RW_ASYNC, HZ/10); + trace_mm_vmscan_lru_shrink_inactive(zone->zone_pgdat->node_id, zone_idx(zone), nr_scanned, nr_reclaimed, @@ -2374,17 +2389,6 @@ static unsigned long do_try_to_free_pages(struct zonelist *zonelist, WB_REASON_TRY_TO_FREE_PAGES); sc->may_writepage = 1; } - - /* Take a nap, wait for some writeback to complete */ - if (!sc->hibernation_mode && sc->nr_scanned && - sc->priority < DEF_PRIORITY - 2) { - struct zone *preferred_zone; - - first_zones_zonelist(zonelist, gfp_zone(sc->gfp_mask), - &cpuset_current_mems_allowed, - &preferred_zone); - wait_iff_congested(preferred_zone, BLK_RW_ASYNC, HZ/10); - } } while (--sc->priority >= 0); out: -- cgit v1.2.3 From d04e8acd03e5c3421ef18e3da7bc88d56179ca42 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Wed, 3 Jul 2013 15:02:03 -0700 Subject: mm: vmscan: treat pages marked for immediate reclaim as zone congestion Currently a zone will only be marked congested if the underlying BDI is congested but if dirty pages are spread across zones it is possible that an individual zone is full of dirty pages without being congested. The impact is that zone gets scanned very quickly potentially reclaiming really clean pages. This patch treats pages marked for immediate reclaim as congested for the purposes of marking a zone ZONE_CONGESTED and stalling in wait_iff_congested. Signed-off-by: Mel Gorman Cc: Johannes Weiner Cc: Michal Hocko Cc: Rik van Riel Cc: KAMEZAWA Hiroyuki Cc: Jiri Slaby Cc: Valdis Kletnieks Cc: Zlatko Calusic Cc: dormando Cc: Trond Myklebust Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/vmscan.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index 4898daf074cf..bf4778479e3a 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -761,9 +761,15 @@ static unsigned long shrink_page_list(struct list_head *page_list, if (dirty && !writeback) nr_unqueued_dirty++; - /* Treat this page as congested if underlying BDI is */ + /* + * Treat this page as congested if the underlying BDI is or if + * pages are cycling through the LRU so quickly that the + * pages marked for immediate reclaim are making it to the + * end of the LRU a second time. + */ mapping = page_mapping(page); - if (mapping && bdi_write_congested(mapping->backing_dev_info)) + if ((mapping && bdi_write_congested(mapping->backing_dev_info)) || + (writeback && PageReclaim(page))) nr_congested++; /* -- cgit v1.2.3 From b45972265f823ed01eae0867a176320071665787 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Wed, 3 Jul 2013 15:02:05 -0700 Subject: mm: vmscan: take page buffers dirty and locked state into account Page reclaim keeps track of dirty and under writeback pages and uses it to determine if wait_iff_congested() should stall or if kswapd should begin writing back pages. This fails to account for buffer pages that can be under writeback but not PageWriteback which is the case for filesystems like ext3 ordered mode. Furthermore, PageDirty buffer pages can have all the buffers clean and writepage does no IO so it should not be accounted as congested. This patch adds an address_space operation that filesystems may optionally use to check if a page is really dirty or really under writeback. An implementation is provided for for buffer_heads is added and used for block operations and ext3 in ordered mode. By default the page flags are obeyed. Credit goes to Jan Kara for identifying that the page flags alone are not sufficient for ext3 and sanity checking a number of ideas on how the problem could be addressed. Signed-off-by: Mel Gorman Cc: Johannes Weiner Cc: Michal Hocko Cc: Rik van Riel Cc: KAMEZAWA Hiroyuki Cc: Jiri Slaby Cc: Valdis Kletnieks Cc: Zlatko Calusic Cc: dormando Cc: Trond Myklebust Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/block_dev.c | 1 + fs/buffer.c | 34 ++++++++++++++++++++++++++++++++++ fs/ext3/inode.c | 1 + include/linux/buffer_head.h | 3 +++ include/linux/fs.h | 1 + mm/vmscan.c | 10 ++++++++++ 6 files changed, 50 insertions(+) diff --git a/fs/block_dev.c b/fs/block_dev.c index 431b6a04ebfd..bb43ce081d6e 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -1562,6 +1562,7 @@ static const struct address_space_operations def_blk_aops = { .writepages = generic_writepages, .releasepage = blkdev_releasepage, .direct_IO = blkdev_direct_IO, + .is_dirty_writeback = buffer_check_dirty_writeback, }; const struct file_operations def_blk_fops = { diff --git a/fs/buffer.c b/fs/buffer.c index f93392e2df12..4d7433534f5c 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -82,6 +82,40 @@ void unlock_buffer(struct buffer_head *bh) } EXPORT_SYMBOL(unlock_buffer); +/* + * Returns if the page has dirty or writeback buffers. If all the buffers + * are unlocked and clean then the PageDirty information is stale. If + * any of the pages are locked, it is assumed they are locked for IO. + */ +void buffer_check_dirty_writeback(struct page *page, + bool *dirty, bool *writeback) +{ + struct buffer_head *head, *bh; + *dirty = false; + *writeback = false; + + BUG_ON(!PageLocked(page)); + + if (!page_has_buffers(page)) + return; + + if (PageWriteback(page)) + *writeback = true; + + head = page_buffers(page); + bh = head; + do { + if (buffer_locked(bh)) + *writeback = true; + + if (buffer_dirty(bh)) + *dirty = true; + + bh = bh->b_this_page; + } while (bh != head); +} +EXPORT_SYMBOL(buffer_check_dirty_writeback); + /* * Block until a buffer comes unlocked. This doesn't stop it * from becoming locked again - you have to lock it yourself diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index f67668f724ba..2bd85486b879 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -1985,6 +1985,7 @@ static const struct address_space_operations ext3_ordered_aops = { .direct_IO = ext3_direct_IO, .migratepage = buffer_migrate_page, .is_partially_uptodate = block_is_partially_uptodate, + .is_dirty_writeback = buffer_check_dirty_writeback, .error_remove_page = generic_error_remove_page, }; diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index f5a3b838ddb0..91fa9a94ae92 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -139,6 +139,9 @@ BUFFER_FNS(Prio, prio) }) #define page_has_buffers(page) PagePrivate(page) +void buffer_check_dirty_writeback(struct page *page, + bool *dirty, bool *writeback); + /* * Declarations */ diff --git a/include/linux/fs.h b/include/linux/fs.h index 2b82c8041490..99be011e00de 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -380,6 +380,7 @@ struct address_space_operations { int (*launder_page) (struct page *); int (*is_partially_uptodate) (struct page *, read_descriptor_t *, unsigned long); + void (*is_dirty_writeback) (struct page *, bool *, bool *); int (*error_remove_page)(struct address_space *, struct page *); /* swapfile support */ diff --git a/mm/vmscan.c b/mm/vmscan.c index bf4778479e3a..c85794399848 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -673,6 +673,8 @@ static enum page_references page_check_references(struct page *page, static void page_check_dirty_writeback(struct page *page, bool *dirty, bool *writeback) { + struct address_space *mapping; + /* * Anonymous pages are not handled by flushers and must be written * from reclaim context. Do not stall reclaim based on them @@ -686,6 +688,14 @@ static void page_check_dirty_writeback(struct page *page, /* By default assume that the page flags are accurate */ *dirty = PageDirty(page); *writeback = PageWriteback(page); + + /* Verify dirty/writeback state if the filesystem supports it */ + if (!page_has_private(page)) + return; + + mapping = page_mapping(page); + if (mapping && mapping->a_ops->is_dirty_writeback) + mapping->a_ops->is_dirty_writeback(page, dirty, writeback); } /* -- cgit v1.2.3 From f919b19614f06711cba300c1bb1e3d94c9ca21b0 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Wed, 3 Jul 2013 15:02:06 -0700 Subject: fs: nfs: inform the VM about pages being committed or unstable VM page reclaim uses dirty and writeback page states to determine if flushers are cleaning pages too slowly and that page reclaim should stall waiting on flushers to catch up. Page state in NFS is a bit more complex and a clean page can be unreclaimable due to being unstable which is effectively "dirty" from the perspective of the VM from reclaim context. Similarly, if the inode is currently being committed then it's similar to being under writeback. This patch adds a is_dirty_writeback() handled for NFS that checks if a pages backing inode is being committed and should be accounted as writeback and if a page has private state indicating that it is effectively dirty. Signed-off-by: Mel Gorman Cc: Johannes Weiner Cc: Michal Hocko Cc: Rik van Riel Cc: KAMEZAWA Hiroyuki Cc: Jiri Slaby Cc: Valdis Kletnieks Cc: Zlatko Calusic Cc: dormando Cc: Trond Myklebust Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/nfs/file.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 6b4a79f4ad1d..94e94bd11aae 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -495,6 +495,35 @@ static int nfs_release_page(struct page *page, gfp_t gfp) return nfs_fscache_release_page(page, gfp); } +static void nfs_check_dirty_writeback(struct page *page, + bool *dirty, bool *writeback) +{ + struct nfs_inode *nfsi; + struct address_space *mapping = page_file_mapping(page); + + if (!mapping || PageSwapCache(page)) + return; + + /* + * Check if an unstable page is currently being committed and + * if so, have the VM treat it as if the page is under writeback + * so it will not block due to pages that will shortly be freeable. + */ + nfsi = NFS_I(mapping->host); + if (test_bit(NFS_INO_COMMIT, &nfsi->flags)) { + *writeback = true; + return; + } + + /* + * If PagePrivate() is set, then the page is not freeable and as the + * inode is not being committed, it's not going to be cleaned in the + * near future so treat it as dirty + */ + if (PagePrivate(page)) + *dirty = true; +} + /* * Attempt to clear the private state associated with a page when an error * occurs that requires the cached contents of an inode to be written back or @@ -542,6 +571,7 @@ const struct address_space_operations nfs_file_aops = { .direct_IO = nfs_direct_IO, .migratepage = nfs_migrate_page, .launder_page = nfs_launder_page, + .is_dirty_writeback = nfs_check_dirty_writeback, .error_remove_page = generic_error_remove_page, #ifdef CONFIG_NFS_SWAP .swap_activate = nfs_swap_activate, -- cgit v1.2.3 From 72c3b51bda557ab38d6c5b2af750c27cba15f828 Mon Sep 17 00:00:00 2001 From: Cody P Schafer Date: Wed, 3 Jul 2013 15:02:08 -0700 Subject: mm: fix comment referring to non-existent size_seqlock, change to span_seqlock Signed-off-by: Cody P Schafer Acked-by: David Rientjes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mmzone.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index fce64afba042..9ec7cffcae21 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -733,7 +733,7 @@ typedef struct pglist_data { * or node_spanned_pages stay constant. Holding this will also * guarantee that any pfn_valid() stays that way. * - * Nests above zone->lock and zone->size_seqlock. + * Nests above zone->lock and zone->span_seqlock */ spinlock_t node_size_lock; #endif -- cgit v1.2.3 From 114d4b79f7e561fd76fb98eba79e18df7cdd60f0 Mon Sep 17 00:00:00 2001 From: Cody P Schafer Date: Wed, 3 Jul 2013 15:02:09 -0700 Subject: mmzone: note that node_size_lock should be manipulated via pgdat_resize_lock() Signed-off-by: Cody P Schafer Cc: David Rientjes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mmzone.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 9ec7cffcae21..e511f9429f1e 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -733,6 +733,9 @@ typedef struct pglist_data { * or node_spanned_pages stay constant. Holding this will also * guarantee that any pfn_valid() stays that way. * + * pgdat_resize_lock() and pgdat_resize_unlock() are provided to + * manipulate node_size_lock without checking for CONFIG_MEMORY_HOTPLUG. + * * Nests above zone->lock and zone->span_seqlock */ spinlock_t node_size_lock; -- cgit v1.2.3 From aa47228a18e6d49369df877463095b899aff495f Mon Sep 17 00:00:00 2001 From: Cody P Schafer Date: Wed, 3 Jul 2013 15:02:10 -0700 Subject: memory_hotplug: use pgdat_resize_lock() in online_pages() mmzone.h documents node_size_lock (which pgdat_resize_lock() locks) as follows: * Must be held any time you expect node_start_pfn, node_present_pages * or node_spanned_pages stay constant. [...] So actually hold it when we update node_present_pages in online_pages(). Signed-off-by: Cody P Schafer Cc: David Rientjes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/memory_hotplug.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index 1ad92b46753e..527c51084bb8 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -918,6 +918,7 @@ static void node_states_set_node(int node, struct memory_notify *arg) int __ref online_pages(unsigned long pfn, unsigned long nr_pages, int online_type) { + unsigned long flags; unsigned long onlined_pages = 0; struct zone *zone; int need_zonelists_rebuild = 0; @@ -996,7 +997,11 @@ int __ref online_pages(unsigned long pfn, unsigned long nr_pages, int online_typ zone->managed_pages += onlined_pages; zone->present_pages += onlined_pages; + + pgdat_resize_lock(zone->zone_pgdat, &flags); zone->zone_pgdat->node_present_pages += onlined_pages; + pgdat_resize_unlock(zone->zone_pgdat, &flags); + if (onlined_pages) { node_states_set_node(zone_to_nid(zone), &arg); if (need_zonelists_rebuild) -- cgit v1.2.3 From d702909f0aa14fe678d74d7f974aa66bfb211d0b Mon Sep 17 00:00:00 2001 From: Cody P Schafer Date: Wed, 3 Jul 2013 15:02:11 -0700 Subject: memory_hotplug: use pgdat_resize_lock() in __offline_pages() mmzone.h documents node_size_lock (which pgdat_resize_lock() locks) as follows: * Must be held any time you expect node_start_pfn, node_present_pages * or node_spanned_pages stay constant. [...] So actually hold it when we update node_present_pages in __offline_pages(). [akpm@linux-foundation.org: fix build] Signed-off-by: Cody P Schafer Cc: David Rientjes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/memory_hotplug.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index 527c51084bb8..a66d0023d219 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -1492,6 +1492,7 @@ static int __ref __offline_pages(unsigned long start_pfn, unsigned long pfn, nr_pages, expire; long offlined_pages; int ret, drain, retry_max, node; + unsigned long flags; struct zone *zone; struct memory_notify arg; @@ -1585,7 +1586,11 @@ repeat: /* removal success */ zone->managed_pages -= offlined_pages; zone->present_pages -= offlined_pages; + + pgdat_resize_lock(zone->zone_pgdat, &flags); zone->zone_pgdat->node_present_pages -= offlined_pages; + pgdat_resize_unlock(zone->zone_pgdat, &flags); + totalram_pages -= offlined_pages; init_per_zone_wmark_min(); -- cgit v1.2.3 From 0fa73b86ef0797ca4fde5334117ca0b330f08030 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Wed, 3 Jul 2013 15:02:11 -0700 Subject: include/linux/mm.h: add PAGE_ALIGNED() helper To test whether an address is aligned to PAGE_SIZE. Cc: HATAYAMA Daisuke Cc: "Eric W. Biederman" , Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mm.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/linux/mm.h b/include/linux/mm.h index 66d881f1d576..949bd7035895 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -52,6 +52,9 @@ extern unsigned long sysctl_admin_reserve_kbytes; /* to align the pointer to the (next) page boundary */ #define PAGE_ALIGN(addr) ALIGN(addr, PAGE_SIZE) +/* test whether an address (unsigned long or pointer) is aligned to PAGE_SIZE */ +#define PAGE_ALIGNED(addr) IS_ALIGNED((unsigned long)addr, PAGE_SIZE) + /* * Linux kernel virtual memory manager primitives. * The idea being to have a "virtual" mm in the same way -- cgit v1.2.3 From b27eb186608c89ef0979ae47c649859ceaa1b2e7 Mon Sep 17 00:00:00 2001 From: HATAYAMA Daisuke Date: Wed, 3 Jul 2013 15:02:13 -0700 Subject: vmcore: clean up read_vmcore() Rewrite part of read_vmcore() that reads objects in vmcore_list in the same way as part reading ELF headers, by which some duplicated and redundant codes are removed. Signed-off-by: HATAYAMA Daisuke Acked-by: Vivek Goyal Cc: KOSAKI Motohiro Cc: Atsushi Kumagai Cc: Lisa Mitchell Cc: Zhang Yanfei Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/proc/vmcore.c | 68 +++++++++++++++++--------------------------------------- 1 file changed, 20 insertions(+), 48 deletions(-) diff --git a/fs/proc/vmcore.c b/fs/proc/vmcore.c index 17f7e080d7ff..ab0c92e64411 100644 --- a/fs/proc/vmcore.c +++ b/fs/proc/vmcore.c @@ -118,27 +118,6 @@ static ssize_t read_from_oldmem(char *buf, size_t count, return read; } -/* Maps vmcore file offset to respective physical address in memroy. */ -static u64 map_offset_to_paddr(loff_t offset, struct list_head *vc_list, - struct vmcore **m_ptr) -{ - struct vmcore *m; - u64 paddr; - - list_for_each_entry(m, vc_list, list) { - u64 start, end; - start = m->offset; - end = m->offset + m->size - 1; - if (offset >= start && offset <= end) { - paddr = m->paddr + offset - start; - *m_ptr = m; - return paddr; - } - } - *m_ptr = NULL; - return 0; -} - /* Read from the ELF header and then the crash dump. On error, negative value is * returned otherwise number of bytes read are returned. */ @@ -147,8 +126,8 @@ static ssize_t read_vmcore(struct file *file, char __user *buffer, { ssize_t acc = 0, tmp; size_t tsz; - u64 start, nr_bytes; - struct vmcore *curr_m = NULL; + u64 start; + struct vmcore *m = NULL; if (buflen == 0 || *fpos >= vmcore_size) return 0; @@ -174,33 +153,26 @@ static ssize_t read_vmcore(struct file *file, char __user *buffer, return acc; } - start = map_offset_to_paddr(*fpos, &vmcore_list, &curr_m); - if (!curr_m) - return -EINVAL; - - while (buflen) { - tsz = min_t(size_t, buflen, PAGE_SIZE - (start & ~PAGE_MASK)); - - /* Calculate left bytes in current memory segment. */ - nr_bytes = (curr_m->size - (start - curr_m->paddr)); - if (tsz > nr_bytes) - tsz = nr_bytes; - - tmp = read_from_oldmem(buffer, tsz, &start, 1); - if (tmp < 0) - return tmp; - buflen -= tsz; - *fpos += tsz; - buffer += tsz; - acc += tsz; - if (start >= (curr_m->paddr + curr_m->size)) { - if (curr_m->list.next == &vmcore_list) - return acc; /*EOF*/ - curr_m = list_entry(curr_m->list.next, - struct vmcore, list); - start = curr_m->paddr; + list_for_each_entry(m, &vmcore_list, list) { + if (*fpos < m->offset + m->size) { + tsz = m->offset + m->size - *fpos; + if (buflen < tsz) + tsz = buflen; + start = m->paddr + *fpos - m->offset; + tmp = read_from_oldmem(buffer, tsz, &start, 1); + if (tmp < 0) + return tmp; + buflen -= tsz; + *fpos += tsz; + buffer += tsz; + acc += tsz; + + /* leave now if filled buffer already */ + if (buflen == 0) + return acc; } } + return acc; } -- cgit v1.2.3 From f2bdacdd597d8d05c3d5f5d36273084f7ef7e6f5 Mon Sep 17 00:00:00 2001 From: HATAYAMA Daisuke Date: Wed, 3 Jul 2013 15:02:14 -0700 Subject: vmcore: allocate buffer for ELF headers on page-size alignment Allocate ELF headers on page-size boundary using __get_free_pages() instead of kmalloc(). Later patch will merge PT_NOTE entries into a single unique one and decrease the buffer size actually used. Keep original buffer size in variable elfcorebuf_sz_orig to kfree the buffer later and actually used buffer size with rounded up to page-size boundary in variable elfcorebuf_sz separately. The size of part of the ELF buffer exported from /proc/vmcore is elfcorebuf_sz. The merged, removed PT_NOTE entries, i.e. the range [elfcorebuf_sz, elfcorebuf_sz_orig], is filled with 0. Use size of the ELF headers as an initial offset value in set_vmcore_list_offsets_elf{64,32} and process_ptload_program_headers_elf{64,32} in order to indicate that the offset includes the holes towards the page boundary. As a result, both set_vmcore_list_offsets_elf{64,32} have the same definition. Merge them as set_vmcore_list_offsets. [akpm@linux-foundation.org: add free_elfcorebuf(), cleanups] Signed-off-by: HATAYAMA Daisuke Acked-by: Vivek Goyal Cc: KOSAKI Motohiro Cc: Atsushi Kumagai Cc: Lisa Mitchell Cc: Zhang Yanfei Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/proc/vmcore.c | 119 ++++++++++++++++++++++++------------------------------- 1 file changed, 51 insertions(+), 68 deletions(-) diff --git a/fs/proc/vmcore.c b/fs/proc/vmcore.c index ab0c92e64411..0b1c04e5e2c5 100644 --- a/fs/proc/vmcore.c +++ b/fs/proc/vmcore.c @@ -32,6 +32,7 @@ static LIST_HEAD(vmcore_list); /* Stores the pointer to the buffer containing kernel elf core headers. */ static char *elfcorebuf; static size_t elfcorebuf_sz; +static size_t elfcorebuf_sz_orig; /* Total size of vmcore file. */ static u64 vmcore_size; @@ -186,7 +187,7 @@ static struct vmcore* __init get_new_element(void) return kzalloc(sizeof(struct vmcore), GFP_KERNEL); } -static u64 __init get_vmcore_size_elf64(char *elfptr) +static u64 __init get_vmcore_size_elf64(char *elfptr, size_t elfsz) { int i; u64 size; @@ -195,7 +196,7 @@ static u64 __init get_vmcore_size_elf64(char *elfptr) ehdr_ptr = (Elf64_Ehdr *)elfptr; phdr_ptr = (Elf64_Phdr*)(elfptr + sizeof(Elf64_Ehdr)); - size = sizeof(Elf64_Ehdr) + ((ehdr_ptr->e_phnum) * sizeof(Elf64_Phdr)); + size = elfsz; for (i = 0; i < ehdr_ptr->e_phnum; i++) { size += phdr_ptr->p_memsz; phdr_ptr++; @@ -203,7 +204,7 @@ static u64 __init get_vmcore_size_elf64(char *elfptr) return size; } -static u64 __init get_vmcore_size_elf32(char *elfptr) +static u64 __init get_vmcore_size_elf32(char *elfptr, size_t elfsz) { int i; u64 size; @@ -212,7 +213,7 @@ static u64 __init get_vmcore_size_elf32(char *elfptr) ehdr_ptr = (Elf32_Ehdr *)elfptr; phdr_ptr = (Elf32_Phdr*)(elfptr + sizeof(Elf32_Ehdr)); - size = sizeof(Elf32_Ehdr) + ((ehdr_ptr->e_phnum) * sizeof(Elf32_Phdr)); + size = elfsz; for (i = 0; i < ehdr_ptr->e_phnum; i++) { size += phdr_ptr->p_memsz; phdr_ptr++; @@ -294,6 +295,8 @@ static int __init merge_note_headers_elf64(char *elfptr, size_t *elfsz, i = (nr_ptnote - 1) * sizeof(Elf64_Phdr); *elfsz = *elfsz - i; memmove(tmp, tmp+i, ((*elfsz)-sizeof(Elf64_Ehdr)-sizeof(Elf64_Phdr))); + memset(elfptr + *elfsz, 0, i); + *elfsz = roundup(*elfsz, PAGE_SIZE); /* Modify e_phnum to reflect merged headers. */ ehdr_ptr->e_phnum = ehdr_ptr->e_phnum - nr_ptnote + 1; @@ -375,6 +378,8 @@ static int __init merge_note_headers_elf32(char *elfptr, size_t *elfsz, i = (nr_ptnote - 1) * sizeof(Elf32_Phdr); *elfsz = *elfsz - i; memmove(tmp, tmp+i, ((*elfsz)-sizeof(Elf32_Ehdr)-sizeof(Elf32_Phdr))); + memset(elfptr + *elfsz, 0, i); + *elfsz = roundup(*elfsz, PAGE_SIZE); /* Modify e_phnum to reflect merged headers. */ ehdr_ptr->e_phnum = ehdr_ptr->e_phnum - nr_ptnote + 1; @@ -398,8 +403,7 @@ static int __init process_ptload_program_headers_elf64(char *elfptr, phdr_ptr = (Elf64_Phdr*)(elfptr + sizeof(Elf64_Ehdr)); /* PT_NOTE hdr */ /* First program header is PT_NOTE header. */ - vmcore_off = sizeof(Elf64_Ehdr) + - (ehdr_ptr->e_phnum) * sizeof(Elf64_Phdr) + + vmcore_off = elfsz + phdr_ptr->p_memsz; /* Note sections */ for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) { @@ -435,8 +439,7 @@ static int __init process_ptload_program_headers_elf32(char *elfptr, phdr_ptr = (Elf32_Phdr*)(elfptr + sizeof(Elf32_Ehdr)); /* PT_NOTE hdr */ /* First program header is PT_NOTE header. */ - vmcore_off = sizeof(Elf32_Ehdr) + - (ehdr_ptr->e_phnum) * sizeof(Elf32_Phdr) + + vmcore_off = elfsz + phdr_ptr->p_memsz; /* Note sections */ for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) { @@ -459,18 +462,14 @@ static int __init process_ptload_program_headers_elf32(char *elfptr, } /* Sets offset fields of vmcore elements. */ -static void __init set_vmcore_list_offsets_elf64(char *elfptr, - struct list_head *vc_list) +static void __init set_vmcore_list_offsets(size_t elfsz, + struct list_head *vc_list) { loff_t vmcore_off; - Elf64_Ehdr *ehdr_ptr; struct vmcore *m; - ehdr_ptr = (Elf64_Ehdr *)elfptr; - /* Skip Elf header and program headers. */ - vmcore_off = sizeof(Elf64_Ehdr) + - (ehdr_ptr->e_phnum) * sizeof(Elf64_Phdr); + vmcore_off = elfsz; list_for_each_entry(m, vc_list, list) { m->offset = vmcore_off; @@ -478,24 +477,10 @@ static void __init set_vmcore_list_offsets_elf64(char *elfptr, } } -/* Sets offset fields of vmcore elements. */ -static void __init set_vmcore_list_offsets_elf32(char *elfptr, - struct list_head *vc_list) +static void free_elfcorebuf(void) { - loff_t vmcore_off; - Elf32_Ehdr *ehdr_ptr; - struct vmcore *m; - - ehdr_ptr = (Elf32_Ehdr *)elfptr; - - /* Skip Elf header and program headers. */ - vmcore_off = sizeof(Elf32_Ehdr) + - (ehdr_ptr->e_phnum) * sizeof(Elf32_Phdr); - - list_for_each_entry(m, vc_list, list) { - m->offset = vmcore_off; - vmcore_off += m->size; - } + free_pages((unsigned long)elfcorebuf, get_order(elfcorebuf_sz_orig)); + elfcorebuf = NULL; } static int __init parse_crash_elf64_headers(void) @@ -526,31 +511,31 @@ static int __init parse_crash_elf64_headers(void) } /* Read in all elf headers. */ - elfcorebuf_sz = sizeof(Elf64_Ehdr) + ehdr.e_phnum * sizeof(Elf64_Phdr); - elfcorebuf = kmalloc(elfcorebuf_sz, GFP_KERNEL); + elfcorebuf_sz_orig = sizeof(Elf64_Ehdr) + + ehdr.e_phnum * sizeof(Elf64_Phdr); + elfcorebuf_sz = elfcorebuf_sz_orig; + elfcorebuf = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, + get_order(elfcorebuf_sz_orig)); if (!elfcorebuf) return -ENOMEM; addr = elfcorehdr_addr; - rc = read_from_oldmem(elfcorebuf, elfcorebuf_sz, &addr, 0); - if (rc < 0) { - kfree(elfcorebuf); - return rc; - } + rc = read_from_oldmem(elfcorebuf, elfcorebuf_sz_orig, &addr, 0); + if (rc < 0) + goto fail; /* Merge all PT_NOTE headers into one. */ rc = merge_note_headers_elf64(elfcorebuf, &elfcorebuf_sz, &vmcore_list); - if (rc) { - kfree(elfcorebuf); - return rc; - } + if (rc) + goto fail; rc = process_ptload_program_headers_elf64(elfcorebuf, elfcorebuf_sz, &vmcore_list); - if (rc) { - kfree(elfcorebuf); - return rc; - } - set_vmcore_list_offsets_elf64(elfcorebuf, &vmcore_list); + if (rc) + goto fail; + set_vmcore_list_offsets(elfcorebuf_sz, &vmcore_list); return 0; +fail: + free_elfcorebuf(); + return rc; } static int __init parse_crash_elf32_headers(void) @@ -581,31 +566,30 @@ static int __init parse_crash_elf32_headers(void) } /* Read in all elf headers. */ - elfcorebuf_sz = sizeof(Elf32_Ehdr) + ehdr.e_phnum * sizeof(Elf32_Phdr); - elfcorebuf = kmalloc(elfcorebuf_sz, GFP_KERNEL); + elfcorebuf_sz_orig = sizeof(Elf32_Ehdr) + ehdr.e_phnum * sizeof(Elf32_Phdr); + elfcorebuf_sz = elfcorebuf_sz_orig; + elfcorebuf = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, + get_order(elfcorebuf_sz_orig)); if (!elfcorebuf) return -ENOMEM; addr = elfcorehdr_addr; - rc = read_from_oldmem(elfcorebuf, elfcorebuf_sz, &addr, 0); - if (rc < 0) { - kfree(elfcorebuf); - return rc; - } + rc = read_from_oldmem(elfcorebuf, elfcorebuf_sz_orig, &addr, 0); + if (rc < 0) + goto fail; /* Merge all PT_NOTE headers into one. */ rc = merge_note_headers_elf32(elfcorebuf, &elfcorebuf_sz, &vmcore_list); - if (rc) { - kfree(elfcorebuf); - return rc; - } + if (rc) + goto fail; rc = process_ptload_program_headers_elf32(elfcorebuf, elfcorebuf_sz, &vmcore_list); - if (rc) { - kfree(elfcorebuf); - return rc; - } - set_vmcore_list_offsets_elf32(elfcorebuf, &vmcore_list); + if (rc) + goto fail; + set_vmcore_list_offsets(elfcorebuf_sz, &vmcore_list); return 0; +fail: + free_elfcorebuf(); + return rc; } static int __init parse_crash_elf_headers(void) @@ -629,14 +613,14 @@ static int __init parse_crash_elf_headers(void) return rc; /* Determine vmcore size. */ - vmcore_size = get_vmcore_size_elf64(elfcorebuf); + vmcore_size = get_vmcore_size_elf64(elfcorebuf, elfcorebuf_sz); } else if (e_ident[EI_CLASS] == ELFCLASS32) { rc = parse_crash_elf32_headers(); if (rc) return rc; /* Determine vmcore size. */ - vmcore_size = get_vmcore_size_elf32(elfcorebuf); + vmcore_size = get_vmcore_size_elf32(elfcorebuf, elfcorebuf_sz); } else { pr_warn("Warning: Core image elf header is not sane\n"); return -EINVAL; @@ -683,7 +667,6 @@ void vmcore_cleanup(void) list_del(&m->list); kfree(m); } - kfree(elfcorebuf); - elfcorebuf = NULL; + free_elfcorebuf(); } EXPORT_SYMBOL_GPL(vmcore_cleanup); -- cgit v1.2.3 From 7f614cd1e052ebbddee7ea49c725dc75fee74a5a Mon Sep 17 00:00:00 2001 From: HATAYAMA Daisuke Date: Wed, 3 Jul 2013 15:02:15 -0700 Subject: vmcore: treat memory chunks referenced by PT_LOAD program header entries in page-size boundary in vmcore_list Treat memory chunks referenced by PT_LOAD program header entries in page-size boundary in vmcore_list. Formally, for each range [start, end], we set up the corresponding vmcore object in vmcore_list to [rounddown(start, PAGE_SIZE), roundup(end, PAGE_SIZE)]. This change affects layout of /proc/vmcore. The gaps generated by the rearrangement are newly made visible to applications as holes. Concretely, they are two ranges [rounddown(start, PAGE_SIZE), start] and [end, roundup(end, PAGE_SIZE)]. Suppose variable m points at a vmcore object in vmcore_list, and variable phdr points at the program header of PT_LOAD type the variable m corresponds to. Then, pictorially: m->offset +---------------+ | hole | phdr->p_offset = +---------------+ m->offset + (paddr - start) | |\ | kernel memory | phdr->p_memsz | |/ +---------------+ | hole | m->offset + m->size +---------------+ where m->offset and m->offset + m->size are always page-size aligned. Signed-off-by: HATAYAMA Daisuke Acked-by: Vivek Goyal Cc: KOSAKI Motohiro Cc: Atsushi Kumagai Cc: Lisa Mitchell Cc: Zhang Yanfei Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/proc/vmcore.c | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/fs/proc/vmcore.c b/fs/proc/vmcore.c index 0b1c04e5e2c5..78c87a1ac01a 100644 --- a/fs/proc/vmcore.c +++ b/fs/proc/vmcore.c @@ -407,20 +407,27 @@ static int __init process_ptload_program_headers_elf64(char *elfptr, phdr_ptr->p_memsz; /* Note sections */ for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) { + u64 paddr, start, end, size; + if (phdr_ptr->p_type != PT_LOAD) continue; + paddr = phdr_ptr->p_offset; + start = rounddown(paddr, PAGE_SIZE); + end = roundup(paddr + phdr_ptr->p_memsz, PAGE_SIZE); + size = end - start; + /* Add this contiguous chunk of memory to vmcore list.*/ new = get_new_element(); if (!new) return -ENOMEM; - new->paddr = phdr_ptr->p_offset; - new->size = phdr_ptr->p_memsz; + new->paddr = start; + new->size = size; list_add_tail(&new->list, vc_list); /* Update the program header offset. */ - phdr_ptr->p_offset = vmcore_off; - vmcore_off = vmcore_off + phdr_ptr->p_memsz; + phdr_ptr->p_offset = vmcore_off + (paddr - start); + vmcore_off = vmcore_off + size; } return 0; } @@ -443,20 +450,27 @@ static int __init process_ptload_program_headers_elf32(char *elfptr, phdr_ptr->p_memsz; /* Note sections */ for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) { + u64 paddr, start, end, size; + if (phdr_ptr->p_type != PT_LOAD) continue; + paddr = phdr_ptr->p_offset; + start = rounddown(paddr, PAGE_SIZE); + end = roundup(paddr + phdr_ptr->p_memsz, PAGE_SIZE); + size = end - start; + /* Add this contiguous chunk of memory to vmcore list.*/ new = get_new_element(); if (!new) return -ENOMEM; - new->paddr = phdr_ptr->p_offset; - new->size = phdr_ptr->p_memsz; + new->paddr = start; + new->size = size; list_add_tail(&new->list, vc_list); /* Update the program header offset */ - phdr_ptr->p_offset = vmcore_off; - vmcore_off = vmcore_off + phdr_ptr->p_memsz; + phdr_ptr->p_offset = vmcore_off + (paddr - start); + vmcore_off = vmcore_off + size; } return 0; } -- cgit v1.2.3 From cef2ac3f6c8ab532e49cf69d05f540931ad8ee64 Mon Sep 17 00:00:00 2001 From: HATAYAMA Daisuke Date: Wed, 3 Jul 2013 15:02:17 -0700 Subject: vmalloc: make find_vm_area check in range Currently, __find_vmap_area searches for the kernel VM area starting at a given address. This patch changes this behavior so that it searches for the kernel VM area to which the address belongs. This change is needed by remap_vmalloc_range_partial to be introduced in later patch that receives any position of kernel VM area as target address. This patch changes the condition (addr > va->va_start) to the equivalent (addr >= va->va_end) by taking advantage of the fact that each kernel VM area is non-overlapping. Signed-off-by: HATAYAMA Daisuke Acked-by: KOSAKI Motohiro Cc: Vivek Goyal Cc: Atsushi Kumagai Cc: Lisa Mitchell Cc: Zhang Yanfei Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/vmalloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/vmalloc.c b/mm/vmalloc.c index d365724feb05..3875fa2f0f60 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -292,7 +292,7 @@ static struct vmap_area *__find_vmap_area(unsigned long addr) va = rb_entry(n, struct vmap_area, rb_node); if (addr < va->va_start) n = n->rb_left; - else if (addr > va->va_start) + else if (addr >= va->va_end) n = n->rb_right; else return va; -- cgit v1.2.3 From e69e9d4aee712a22665f008ae0550bb3d7c7f7c1 Mon Sep 17 00:00:00 2001 From: HATAYAMA Daisuke Date: Wed, 3 Jul 2013 15:02:18 -0700 Subject: vmalloc: introduce remap_vmalloc_range_partial We want to allocate ELF note segment buffer on the 2nd kernel in vmalloc space and remap it to user-space in order to reduce the risk that memory allocation fails on system with huge number of CPUs and so with huge ELF note segment that exceeds 11-order block size. Although there's already remap_vmalloc_range for the purpose of remapping vmalloc memory to user-space, we need to specify user-space range via vma. Mmap on /proc/vmcore needs to remap range across multiple objects, so the interface that requires vma to cover full range is problematic. This patch introduces remap_vmalloc_range_partial that receives user-space range as a pair of base address and size and can be used for mmap on /proc/vmcore case. remap_vmalloc_range is rewritten using remap_vmalloc_range_partial. [akpm@linux-foundation.org: use PAGE_ALIGNED()] Signed-off-by: HATAYAMA Daisuke Cc: KOSAKI Motohiro Cc: Vivek Goyal Cc: Atsushi Kumagai Cc: Lisa Mitchell Cc: Zhang Yanfei Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/vmalloc.h | 4 +++ mm/vmalloc.c | 67 +++++++++++++++++++++++++++++++++---------------- 2 files changed, 49 insertions(+), 22 deletions(-) diff --git a/include/linux/vmalloc.h b/include/linux/vmalloc.h index 7d5773a99f20..dd0a2c810529 100644 --- a/include/linux/vmalloc.h +++ b/include/linux/vmalloc.h @@ -82,6 +82,10 @@ extern void *vmap(struct page **pages, unsigned int count, unsigned long flags, pgprot_t prot); extern void vunmap(const void *addr); +extern int remap_vmalloc_range_partial(struct vm_area_struct *vma, + unsigned long uaddr, void *kaddr, + unsigned long size); + extern int remap_vmalloc_range(struct vm_area_struct *vma, void *addr, unsigned long pgoff); void vmalloc_sync_all(void); diff --git a/mm/vmalloc.c b/mm/vmalloc.c index 3875fa2f0f60..b7259906a806 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -1476,10 +1476,9 @@ static void __vunmap(const void *addr, int deallocate_pages) if (!addr) return; - if ((PAGE_SIZE-1) & (unsigned long)addr) { - WARN(1, KERN_ERR "Trying to vfree() bad address (%p)\n", addr); + if (WARN(!PAGE_ALIGNED(addr), "Trying to vfree() bad address (%p)\n", + addr)); return; - } area = remove_vm_area(addr); if (unlikely(!area)) { @@ -2148,42 +2147,43 @@ finished: } /** - * remap_vmalloc_range - map vmalloc pages to userspace - * @vma: vma to cover (map full range of vma) - * @addr: vmalloc memory - * @pgoff: number of pages into addr before first page to map + * remap_vmalloc_range_partial - map vmalloc pages to userspace + * @vma: vma to cover + * @uaddr: target user address to start at + * @kaddr: virtual address of vmalloc kernel memory + * @size: size of map area * * Returns: 0 for success, -Exxx on failure * - * This function checks that addr is a valid vmalloc'ed area, and - * that it is big enough to cover the vma. Will return failure if - * that criteria isn't met. + * This function checks that @kaddr is a valid vmalloc'ed area, + * and that it is big enough to cover the range starting at + * @uaddr in @vma. Will return failure if that criteria isn't + * met. * * Similar to remap_pfn_range() (see mm/memory.c) */ -int remap_vmalloc_range(struct vm_area_struct *vma, void *addr, - unsigned long pgoff) +int remap_vmalloc_range_partial(struct vm_area_struct *vma, unsigned long uaddr, + void *kaddr, unsigned long size) { struct vm_struct *area; - unsigned long uaddr = vma->vm_start; - unsigned long usize = vma->vm_end - vma->vm_start; - if ((PAGE_SIZE-1) & (unsigned long)addr) + size = PAGE_ALIGN(size); + + if (!PAGE_ALIGNED(uaddr) || !PAGE_ALIGNED(kaddr)) return -EINVAL; - area = find_vm_area(addr); + area = find_vm_area(kaddr); if (!area) return -EINVAL; if (!(area->flags & VM_USERMAP)) return -EINVAL; - if (usize + (pgoff << PAGE_SHIFT) > area->size - PAGE_SIZE) + if (kaddr + size > area->addr + area->size) return -EINVAL; - addr += pgoff << PAGE_SHIFT; do { - struct page *page = vmalloc_to_page(addr); + struct page *page = vmalloc_to_page(kaddr); int ret; ret = vm_insert_page(vma, uaddr, page); @@ -2191,14 +2191,37 @@ int remap_vmalloc_range(struct vm_area_struct *vma, void *addr, return ret; uaddr += PAGE_SIZE; - addr += PAGE_SIZE; - usize -= PAGE_SIZE; - } while (usize > 0); + kaddr += PAGE_SIZE; + size -= PAGE_SIZE; + } while (size > 0); vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; return 0; } +EXPORT_SYMBOL(remap_vmalloc_range_partial); + +/** + * remap_vmalloc_range - map vmalloc pages to userspace + * @vma: vma to cover (map full range of vma) + * @addr: vmalloc memory + * @pgoff: number of pages into addr before first page to map + * + * Returns: 0 for success, -Exxx on failure + * + * This function checks that addr is a valid vmalloc'ed area, and + * that it is big enough to cover the vma. Will return failure if + * that criteria isn't met. + * + * Similar to remap_pfn_range() (see mm/memory.c) + */ +int remap_vmalloc_range(struct vm_area_struct *vma, void *addr, + unsigned long pgoff) +{ + return remap_vmalloc_range_partial(vma, vma->vm_start, + addr + (pgoff << PAGE_SHIFT), + vma->vm_end - vma->vm_start); +} EXPORT_SYMBOL(remap_vmalloc_range); /* -- cgit v1.2.3 From 087350c9dcf1b38c597b31d7761f7366e2866e6b Mon Sep 17 00:00:00 2001 From: HATAYAMA Daisuke Date: Wed, 3 Jul 2013 15:02:19 -0700 Subject: vmcore: allocate ELF note segment in the 2nd kernel vmalloc memory The reasons why we don't allocate ELF note segment in the 1st kernel (old memory) on page boundary is to keep backward compatibility for old kernels, and that if doing so, we waste not a little memory due to round-up operation to fit the memory to page boundary since most of the buffers are in per-cpu area. ELF notes are per-cpu, so total size of ELF note segments depends on number of CPUs. The current maximum number of CPUs on x86_64 is 5192, and there's already system with 4192 CPUs in SGI, where total size amounts to 1MB. This can be larger in the near future or possibly even now on another architecture that has larger size of note per a single cpu. Thus, to avoid the case where memory allocation for large block fails, we allocate vmcore objects on vmalloc memory. This patch adds elfnotes_buf and elfnotes_sz variables to keep pointer to the ELF note segment buffer and its size. There's no longer the vmcore object that corresponds to the ELF note segment in vmcore_list. Accordingly, read_vmcore() has new case for ELF note segment and set_vmcore_list_offsets_elf{64,32}() and other helper functions starts calculating offset from sum of size of ELF headers and size of ELF note segment. [akpm@linux-foundation.org: use min(), fix error-path vzalloc() leaks] Signed-off-by: HATAYAMA Daisuke Acked-by: Vivek Goyal Cc: KOSAKI Motohiro Cc: Atsushi Kumagai Cc: Lisa Mitchell Cc: Zhang Yanfei Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/proc/vmcore.c | 359 ++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 286 insertions(+), 73 deletions(-) diff --git a/fs/proc/vmcore.c b/fs/proc/vmcore.c index 78c87a1ac01a..9b9270eb0599 100644 --- a/fs/proc/vmcore.c +++ b/fs/proc/vmcore.c @@ -34,6 +34,9 @@ static char *elfcorebuf; static size_t elfcorebuf_sz; static size_t elfcorebuf_sz_orig; +static char *elfnotes_buf; +static size_t elfnotes_sz; + /* Total size of vmcore file. */ static u64 vmcore_size; @@ -139,9 +142,7 @@ static ssize_t read_vmcore(struct file *file, char __user *buffer, /* Read ELF core header */ if (*fpos < elfcorebuf_sz) { - tsz = elfcorebuf_sz - *fpos; - if (buflen < tsz) - tsz = buflen; + tsz = min(elfcorebuf_sz - (size_t)*fpos, buflen); if (copy_to_user(buffer, elfcorebuf + *fpos, tsz)) return -EFAULT; buflen -= tsz; @@ -154,11 +155,27 @@ static ssize_t read_vmcore(struct file *file, char __user *buffer, return acc; } + /* Read Elf note segment */ + if (*fpos < elfcorebuf_sz + elfnotes_sz) { + void *kaddr; + + tsz = min(elfcorebuf_sz + elfnotes_sz - (size_t)*fpos, buflen); + kaddr = elfnotes_buf + *fpos - elfcorebuf_sz; + if (copy_to_user(buffer, kaddr, tsz)) + return -EFAULT; + buflen -= tsz; + *fpos += tsz; + buffer += tsz; + acc += tsz; + + /* leave now if filled buffer already */ + if (buflen == 0) + return acc; + } + list_for_each_entry(m, &vmcore_list, list) { if (*fpos < m->offset + m->size) { - tsz = m->offset + m->size - *fpos; - if (buflen < tsz) - tsz = buflen; + tsz = min_t(size_t, m->offset + m->size - *fpos, buflen); start = m->paddr + *fpos - m->offset; tmp = read_from_oldmem(buffer, tsz, &start, 1); if (tmp < 0) @@ -221,27 +238,27 @@ static u64 __init get_vmcore_size_elf32(char *elfptr, size_t elfsz) return size; } -/* Merges all the PT_NOTE headers into one. */ -static int __init merge_note_headers_elf64(char *elfptr, size_t *elfsz, - struct list_head *vc_list) +/** + * update_note_header_size_elf64 - update p_memsz member of each PT_NOTE entry + * + * @ehdr_ptr: ELF header + * + * This function updates p_memsz member of each PT_NOTE entry in the + * program header table pointed to by @ehdr_ptr to real size of ELF + * note segment. + */ +static int __init update_note_header_size_elf64(const Elf64_Ehdr *ehdr_ptr) { - int i, nr_ptnote=0, rc=0; - char *tmp; - Elf64_Ehdr *ehdr_ptr; - Elf64_Phdr phdr, *phdr_ptr; + int i, rc=0; + Elf64_Phdr *phdr_ptr; Elf64_Nhdr *nhdr_ptr; - u64 phdr_sz = 0, note_off; - ehdr_ptr = (Elf64_Ehdr *)elfptr; - phdr_ptr = (Elf64_Phdr*)(elfptr + sizeof(Elf64_Ehdr)); + phdr_ptr = (Elf64_Phdr *)(ehdr_ptr + 1); for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) { - int j; void *notes_section; - struct vmcore *new; u64 offset, max_sz, sz, real_sz = 0; if (phdr_ptr->p_type != PT_NOTE) continue; - nr_ptnote++; max_sz = phdr_ptr->p_memsz; offset = phdr_ptr->p_offset; notes_section = kmalloc(max_sz, GFP_KERNEL); @@ -253,7 +270,7 @@ static int __init merge_note_headers_elf64(char *elfptr, size_t *elfsz, return rc; } nhdr_ptr = notes_section; - for (j = 0; j < max_sz; j += sz) { + while (real_sz < max_sz) { if (nhdr_ptr->n_namesz == 0) break; sz = sizeof(Elf64_Nhdr) + @@ -262,26 +279,122 @@ static int __init merge_note_headers_elf64(char *elfptr, size_t *elfsz, real_sz += sz; nhdr_ptr = (Elf64_Nhdr*)((char*)nhdr_ptr + sz); } - - /* Add this contiguous chunk of notes section to vmcore list.*/ - new = get_new_element(); - if (!new) { - kfree(notes_section); - return -ENOMEM; - } - new->paddr = phdr_ptr->p_offset; - new->size = real_sz; - list_add_tail(&new->list, vc_list); - phdr_sz += real_sz; kfree(notes_section); + phdr_ptr->p_memsz = real_sz; } + return 0; +} + +/** + * get_note_number_and_size_elf64 - get the number of PT_NOTE program + * headers and sum of real size of their ELF note segment headers and + * data. + * + * @ehdr_ptr: ELF header + * @nr_ptnote: buffer for the number of PT_NOTE program headers + * @sz_ptnote: buffer for size of unique PT_NOTE program header + * + * This function is used to merge multiple PT_NOTE program headers + * into a unique single one. The resulting unique entry will have + * @sz_ptnote in its phdr->p_mem. + * + * It is assumed that program headers with PT_NOTE type pointed to by + * @ehdr_ptr has already been updated by update_note_header_size_elf64 + * and each of PT_NOTE program headers has actual ELF note segment + * size in its p_memsz member. + */ +static int __init get_note_number_and_size_elf64(const Elf64_Ehdr *ehdr_ptr, + int *nr_ptnote, u64 *sz_ptnote) +{ + int i; + Elf64_Phdr *phdr_ptr; + + *nr_ptnote = *sz_ptnote = 0; + + phdr_ptr = (Elf64_Phdr *)(ehdr_ptr + 1); + for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) { + if (phdr_ptr->p_type != PT_NOTE) + continue; + *nr_ptnote += 1; + *sz_ptnote += phdr_ptr->p_memsz; + } + + return 0; +} + +/** + * copy_notes_elf64 - copy ELF note segments in a given buffer + * + * @ehdr_ptr: ELF header + * @notes_buf: buffer into which ELF note segments are copied + * + * This function is used to copy ELF note segment in the 1st kernel + * into the buffer @notes_buf in the 2nd kernel. It is assumed that + * size of the buffer @notes_buf is equal to or larger than sum of the + * real ELF note segment headers and data. + * + * It is assumed that program headers with PT_NOTE type pointed to by + * @ehdr_ptr has already been updated by update_note_header_size_elf64 + * and each of PT_NOTE program headers has actual ELF note segment + * size in its p_memsz member. + */ +static int __init copy_notes_elf64(const Elf64_Ehdr *ehdr_ptr, char *notes_buf) +{ + int i, rc=0; + Elf64_Phdr *phdr_ptr; + + phdr_ptr = (Elf64_Phdr*)(ehdr_ptr + 1); + + for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) { + u64 offset; + if (phdr_ptr->p_type != PT_NOTE) + continue; + offset = phdr_ptr->p_offset; + rc = read_from_oldmem(notes_buf, phdr_ptr->p_memsz, &offset, 0); + if (rc < 0) + return rc; + notes_buf += phdr_ptr->p_memsz; + } + + return 0; +} + +/* Merges all the PT_NOTE headers into one. */ +static int __init merge_note_headers_elf64(char *elfptr, size_t *elfsz, + char **notes_buf, size_t *notes_sz) +{ + int i, nr_ptnote=0, rc=0; + char *tmp; + Elf64_Ehdr *ehdr_ptr; + Elf64_Phdr phdr; + u64 phdr_sz = 0, note_off; + + ehdr_ptr = (Elf64_Ehdr *)elfptr; + + rc = update_note_header_size_elf64(ehdr_ptr); + if (rc < 0) + return rc; + + rc = get_note_number_and_size_elf64(ehdr_ptr, &nr_ptnote, &phdr_sz); + if (rc < 0) + return rc; + + *notes_sz = roundup(phdr_sz, PAGE_SIZE); + *notes_buf = vzalloc(*notes_sz); + if (!*notes_buf) + return -ENOMEM; + + rc = copy_notes_elf64(ehdr_ptr, *notes_buf); + if (rc < 0) + return rc; + /* Prepare merged PT_NOTE program header. */ phdr.p_type = PT_NOTE; phdr.p_flags = 0; note_off = sizeof(Elf64_Ehdr) + (ehdr_ptr->e_phnum - nr_ptnote +1) * sizeof(Elf64_Phdr); - phdr.p_offset = note_off; + phdr.p_offset = roundup(note_off, PAGE_SIZE); phdr.p_vaddr = phdr.p_paddr = 0; phdr.p_filesz = phdr.p_memsz = phdr_sz; phdr.p_align = 0; @@ -304,27 +417,27 @@ static int __init merge_note_headers_elf64(char *elfptr, size_t *elfsz, return 0; } -/* Merges all the PT_NOTE headers into one. */ -static int __init merge_note_headers_elf32(char *elfptr, size_t *elfsz, - struct list_head *vc_list) +/** + * update_note_header_size_elf32 - update p_memsz member of each PT_NOTE entry + * + * @ehdr_ptr: ELF header + * + * This function updates p_memsz member of each PT_NOTE entry in the + * program header table pointed to by @ehdr_ptr to real size of ELF + * note segment. + */ +static int __init update_note_header_size_elf32(const Elf32_Ehdr *ehdr_ptr) { - int i, nr_ptnote=0, rc=0; - char *tmp; - Elf32_Ehdr *ehdr_ptr; - Elf32_Phdr phdr, *phdr_ptr; + int i, rc=0; + Elf32_Phdr *phdr_ptr; Elf32_Nhdr *nhdr_ptr; - u64 phdr_sz = 0, note_off; - ehdr_ptr = (Elf32_Ehdr *)elfptr; - phdr_ptr = (Elf32_Phdr*)(elfptr + sizeof(Elf32_Ehdr)); + phdr_ptr = (Elf32_Phdr *)(ehdr_ptr + 1); for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) { - int j; void *notes_section; - struct vmcore *new; u64 offset, max_sz, sz, real_sz = 0; if (phdr_ptr->p_type != PT_NOTE) continue; - nr_ptnote++; max_sz = phdr_ptr->p_memsz; offset = phdr_ptr->p_offset; notes_section = kmalloc(max_sz, GFP_KERNEL); @@ -336,7 +449,7 @@ static int __init merge_note_headers_elf32(char *elfptr, size_t *elfsz, return rc; } nhdr_ptr = notes_section; - for (j = 0; j < max_sz; j += sz) { + while (real_sz < max_sz) { if (nhdr_ptr->n_namesz == 0) break; sz = sizeof(Elf32_Nhdr) + @@ -345,26 +458,122 @@ static int __init merge_note_headers_elf32(char *elfptr, size_t *elfsz, real_sz += sz; nhdr_ptr = (Elf32_Nhdr*)((char*)nhdr_ptr + sz); } - - /* Add this contiguous chunk of notes section to vmcore list.*/ - new = get_new_element(); - if (!new) { - kfree(notes_section); - return -ENOMEM; - } - new->paddr = phdr_ptr->p_offset; - new->size = real_sz; - list_add_tail(&new->list, vc_list); - phdr_sz += real_sz; kfree(notes_section); + phdr_ptr->p_memsz = real_sz; + } + + return 0; +} + +/** + * get_note_number_and_size_elf32 - get the number of PT_NOTE program + * headers and sum of real size of their ELF note segment headers and + * data. + * + * @ehdr_ptr: ELF header + * @nr_ptnote: buffer for the number of PT_NOTE program headers + * @sz_ptnote: buffer for size of unique PT_NOTE program header + * + * This function is used to merge multiple PT_NOTE program headers + * into a unique single one. The resulting unique entry will have + * @sz_ptnote in its phdr->p_mem. + * + * It is assumed that program headers with PT_NOTE type pointed to by + * @ehdr_ptr has already been updated by update_note_header_size_elf32 + * and each of PT_NOTE program headers has actual ELF note segment + * size in its p_memsz member. + */ +static int __init get_note_number_and_size_elf32(const Elf32_Ehdr *ehdr_ptr, + int *nr_ptnote, u64 *sz_ptnote) +{ + int i; + Elf32_Phdr *phdr_ptr; + + *nr_ptnote = *sz_ptnote = 0; + + phdr_ptr = (Elf32_Phdr *)(ehdr_ptr + 1); + for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) { + if (phdr_ptr->p_type != PT_NOTE) + continue; + *nr_ptnote += 1; + *sz_ptnote += phdr_ptr->p_memsz; } + return 0; +} + +/** + * copy_notes_elf32 - copy ELF note segments in a given buffer + * + * @ehdr_ptr: ELF header + * @notes_buf: buffer into which ELF note segments are copied + * + * This function is used to copy ELF note segment in the 1st kernel + * into the buffer @notes_buf in the 2nd kernel. It is assumed that + * size of the buffer @notes_buf is equal to or larger than sum of the + * real ELF note segment headers and data. + * + * It is assumed that program headers with PT_NOTE type pointed to by + * @ehdr_ptr has already been updated by update_note_header_size_elf32 + * and each of PT_NOTE program headers has actual ELF note segment + * size in its p_memsz member. + */ +static int __init copy_notes_elf32(const Elf32_Ehdr *ehdr_ptr, char *notes_buf) +{ + int i, rc=0; + Elf32_Phdr *phdr_ptr; + + phdr_ptr = (Elf32_Phdr*)(ehdr_ptr + 1); + + for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) { + u64 offset; + if (phdr_ptr->p_type != PT_NOTE) + continue; + offset = phdr_ptr->p_offset; + rc = read_from_oldmem(notes_buf, phdr_ptr->p_memsz, &offset, 0); + if (rc < 0) + return rc; + notes_buf += phdr_ptr->p_memsz; + } + + return 0; +} + +/* Merges all the PT_NOTE headers into one. */ +static int __init merge_note_headers_elf32(char *elfptr, size_t *elfsz, + char **notes_buf, size_t *notes_sz) +{ + int i, nr_ptnote=0, rc=0; + char *tmp; + Elf32_Ehdr *ehdr_ptr; + Elf32_Phdr phdr; + u64 phdr_sz = 0, note_off; + + ehdr_ptr = (Elf32_Ehdr *)elfptr; + + rc = update_note_header_size_elf32(ehdr_ptr); + if (rc < 0) + return rc; + + rc = get_note_number_and_size_elf32(ehdr_ptr, &nr_ptnote, &phdr_sz); + if (rc < 0) + return rc; + + *notes_sz = roundup(phdr_sz, PAGE_SIZE); + *notes_buf = vzalloc(*notes_sz); + if (!*notes_buf) + return -ENOMEM; + + rc = copy_notes_elf32(ehdr_ptr, *notes_buf); + if (rc < 0) + return rc; + /* Prepare merged PT_NOTE program header. */ phdr.p_type = PT_NOTE; phdr.p_flags = 0; note_off = sizeof(Elf32_Ehdr) + (ehdr_ptr->e_phnum - nr_ptnote +1) * sizeof(Elf32_Phdr); - phdr.p_offset = note_off; + phdr.p_offset = roundup(note_off, PAGE_SIZE); phdr.p_vaddr = phdr.p_paddr = 0; phdr.p_filesz = phdr.p_memsz = phdr_sz; phdr.p_align = 0; @@ -391,6 +600,7 @@ static int __init merge_note_headers_elf32(char *elfptr, size_t *elfsz, * the new offset fields of exported program headers. */ static int __init process_ptload_program_headers_elf64(char *elfptr, size_t elfsz, + size_t elfnotes_sz, struct list_head *vc_list) { int i; @@ -402,9 +612,8 @@ static int __init process_ptload_program_headers_elf64(char *elfptr, ehdr_ptr = (Elf64_Ehdr *)elfptr; phdr_ptr = (Elf64_Phdr*)(elfptr + sizeof(Elf64_Ehdr)); /* PT_NOTE hdr */ - /* First program header is PT_NOTE header. */ - vmcore_off = elfsz + - phdr_ptr->p_memsz; /* Note sections */ + /* Skip Elf header, program headers and Elf note segment. */ + vmcore_off = elfsz + elfnotes_sz; for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) { u64 paddr, start, end, size; @@ -434,6 +643,7 @@ static int __init process_ptload_program_headers_elf64(char *elfptr, static int __init process_ptload_program_headers_elf32(char *elfptr, size_t elfsz, + size_t elfnotes_sz, struct list_head *vc_list) { int i; @@ -445,9 +655,8 @@ static int __init process_ptload_program_headers_elf32(char *elfptr, ehdr_ptr = (Elf32_Ehdr *)elfptr; phdr_ptr = (Elf32_Phdr*)(elfptr + sizeof(Elf32_Ehdr)); /* PT_NOTE hdr */ - /* First program header is PT_NOTE header. */ - vmcore_off = elfsz + - phdr_ptr->p_memsz; /* Note sections */ + /* Skip Elf header, program headers and Elf note segment. */ + vmcore_off = elfsz + elfnotes_sz; for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) { u64 paddr, start, end, size; @@ -476,14 +685,14 @@ static int __init process_ptload_program_headers_elf32(char *elfptr, } /* Sets offset fields of vmcore elements. */ -static void __init set_vmcore_list_offsets(size_t elfsz, +static void __init set_vmcore_list_offsets(size_t elfsz, size_t elfnotes_sz, struct list_head *vc_list) { loff_t vmcore_off; struct vmcore *m; - /* Skip Elf header and program headers. */ - vmcore_off = elfsz; + /* Skip Elf header, program headers and Elf note segment. */ + vmcore_off = elfsz + elfnotes_sz; list_for_each_entry(m, vc_list, list) { m->offset = vmcore_off; @@ -495,6 +704,8 @@ static void free_elfcorebuf(void) { free_pages((unsigned long)elfcorebuf, get_order(elfcorebuf_sz_orig)); elfcorebuf = NULL; + vfree(elfnotes_buf); + elfnotes_buf = NULL; } static int __init parse_crash_elf64_headers(void) @@ -538,14 +749,15 @@ static int __init parse_crash_elf64_headers(void) goto fail; /* Merge all PT_NOTE headers into one. */ - rc = merge_note_headers_elf64(elfcorebuf, &elfcorebuf_sz, &vmcore_list); + rc = merge_note_headers_elf64(elfcorebuf, &elfcorebuf_sz, + &elfnotes_buf, &elfnotes_sz); if (rc) goto fail; rc = process_ptload_program_headers_elf64(elfcorebuf, elfcorebuf_sz, - &vmcore_list); + elfnotes_sz, &vmcore_list); if (rc) goto fail; - set_vmcore_list_offsets(elfcorebuf_sz, &vmcore_list); + set_vmcore_list_offsets(elfcorebuf_sz, elfnotes_sz, &vmcore_list); return 0; fail: free_elfcorebuf(); @@ -592,14 +804,15 @@ static int __init parse_crash_elf32_headers(void) goto fail; /* Merge all PT_NOTE headers into one. */ - rc = merge_note_headers_elf32(elfcorebuf, &elfcorebuf_sz, &vmcore_list); + rc = merge_note_headers_elf32(elfcorebuf, &elfcorebuf_sz, + &elfnotes_buf, &elfnotes_sz); if (rc) goto fail; rc = process_ptload_program_headers_elf32(elfcorebuf, elfcorebuf_sz, - &vmcore_list); + elfnotes_sz, &vmcore_list); if (rc) goto fail; - set_vmcore_list_offsets(elfcorebuf_sz, &vmcore_list); + set_vmcore_list_offsets(elfcorebuf_sz, elfnotes_sz, &vmcore_list); return 0; fail: free_elfcorebuf(); -- cgit v1.2.3 From ef9e78fd2753213ea01d77f7a76a9cb6ad0f50a7 Mon Sep 17 00:00:00 2001 From: HATAYAMA Daisuke Date: Wed, 3 Jul 2013 15:02:21 -0700 Subject: vmcore: allow user process to remap ELF note segment buffer Now ELF note segment has been copied in the buffer on vmalloc memory. To allow user process to remap the ELF note segment buffer with remap_vmalloc_page, the corresponding VM area object has to have VM_USERMAP flag set. [akpm@linux-foundation.org: use the conventional comment layout] Signed-off-by: HATAYAMA Daisuke Acked-by: Vivek Goyal Cc: KOSAKI Motohiro Cc: Atsushi Kumagai Cc: Lisa Mitchell Cc: Zhang Yanfei Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/proc/vmcore.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/fs/proc/vmcore.c b/fs/proc/vmcore.c index 9b9270eb0599..1082492e02fc 100644 --- a/fs/proc/vmcore.c +++ b/fs/proc/vmcore.c @@ -369,6 +369,7 @@ static int __init merge_note_headers_elf64(char *elfptr, size_t *elfsz, Elf64_Ehdr *ehdr_ptr; Elf64_Phdr phdr; u64 phdr_sz = 0, note_off; + struct vm_struct *vm; ehdr_ptr = (Elf64_Ehdr *)elfptr; @@ -385,6 +386,14 @@ static int __init merge_note_headers_elf64(char *elfptr, size_t *elfsz, if (!*notes_buf) return -ENOMEM; + /* + * Allow users to remap ELF note segment buffer on vmalloc memory using + * remap_vmalloc_range.() + */ + vm = find_vm_area(*notes_buf); + BUG_ON(!vm); + vm->flags |= VM_USERMAP; + rc = copy_notes_elf64(ehdr_ptr, *notes_buf); if (rc < 0) return rc; @@ -548,6 +557,7 @@ static int __init merge_note_headers_elf32(char *elfptr, size_t *elfsz, Elf32_Ehdr *ehdr_ptr; Elf32_Phdr phdr; u64 phdr_sz = 0, note_off; + struct vm_struct *vm; ehdr_ptr = (Elf32_Ehdr *)elfptr; @@ -564,6 +574,14 @@ static int __init merge_note_headers_elf32(char *elfptr, size_t *elfsz, if (!*notes_buf) return -ENOMEM; + /* + * Allow users to remap ELF note segment buffer on vmalloc memory using + * remap_vmalloc_range() + */ + vm = find_vm_area(*notes_buf); + BUG_ON(!vm); + vm->flags |= VM_USERMAP; + rc = copy_notes_elf32(ehdr_ptr, *notes_buf); if (rc < 0) return rc; -- cgit v1.2.3 From 591ff71664e764a3806e341370f3c758cb2e7e3c Mon Sep 17 00:00:00 2001 From: HATAYAMA Daisuke Date: Wed, 3 Jul 2013 15:02:22 -0700 Subject: vmcore: calculate vmcore file size from buffer size and total size of vmcore objects The previous patches newly added holes before each chunk of memory and the holes need to be count in vmcore file size. There are two ways to count file size in such a way: 1) suppose m is a poitner to the last vmcore object in vmcore_list. Then file size is (m->offset + m->size), or 2) calculate sum of size of buffers for ELF header, program headers, ELF note segments and objects in vmcore_list. Although 1) is more direct and simpler than 2), 2) seems better in that it reflects internal object structure of /proc/vmcore. Thus, this patch changes get_vmcore_size_elf{64, 32} so that it calculates size in the way of 2). As a result, both get_vmcore_size_elf{64, 32} have the same definition. Merge them as get_vmcore_size. Signed-off-by: HATAYAMA Daisuke Acked-by: Vivek Goyal Cc: KOSAKI Motohiro Cc: Atsushi Kumagai Cc: Lisa Mitchell Cc: Zhang Yanfei Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/proc/vmcore.c | 44 +++++++++++--------------------------------- 1 file changed, 11 insertions(+), 33 deletions(-) diff --git a/fs/proc/vmcore.c b/fs/proc/vmcore.c index 1082492e02fc..8ec648368985 100644 --- a/fs/proc/vmcore.c +++ b/fs/proc/vmcore.c @@ -204,36 +204,15 @@ static struct vmcore* __init get_new_element(void) return kzalloc(sizeof(struct vmcore), GFP_KERNEL); } -static u64 __init get_vmcore_size_elf64(char *elfptr, size_t elfsz) +static u64 __init get_vmcore_size(size_t elfsz, size_t elfnotesegsz, + struct list_head *vc_list) { - int i; u64 size; - Elf64_Ehdr *ehdr_ptr; - Elf64_Phdr *phdr_ptr; - - ehdr_ptr = (Elf64_Ehdr *)elfptr; - phdr_ptr = (Elf64_Phdr*)(elfptr + sizeof(Elf64_Ehdr)); - size = elfsz; - for (i = 0; i < ehdr_ptr->e_phnum; i++) { - size += phdr_ptr->p_memsz; - phdr_ptr++; - } - return size; -} - -static u64 __init get_vmcore_size_elf32(char *elfptr, size_t elfsz) -{ - int i; - u64 size; - Elf32_Ehdr *ehdr_ptr; - Elf32_Phdr *phdr_ptr; + struct vmcore *m; - ehdr_ptr = (Elf32_Ehdr *)elfptr; - phdr_ptr = (Elf32_Phdr*)(elfptr + sizeof(Elf32_Ehdr)); - size = elfsz; - for (i = 0; i < ehdr_ptr->e_phnum; i++) { - size += phdr_ptr->p_memsz; - phdr_ptr++; + size = elfsz + elfnotesegsz; + list_for_each_entry(m, vc_list, list) { + size += m->size; } return size; } @@ -856,20 +835,19 @@ static int __init parse_crash_elf_headers(void) rc = parse_crash_elf64_headers(); if (rc) return rc; - - /* Determine vmcore size. */ - vmcore_size = get_vmcore_size_elf64(elfcorebuf, elfcorebuf_sz); } else if (e_ident[EI_CLASS] == ELFCLASS32) { rc = parse_crash_elf32_headers(); if (rc) return rc; - - /* Determine vmcore size. */ - vmcore_size = get_vmcore_size_elf32(elfcorebuf, elfcorebuf_sz); } else { pr_warn("Warning: Core image elf header is not sane\n"); return -EINVAL; } + + /* Determine vmcore size. */ + vmcore_size = get_vmcore_size(elfcorebuf_sz, elfnotes_sz, + &vmcore_list); + return 0; } -- cgit v1.2.3 From 83086978c63afd7c73e1c173c84aeab184c1e916 Mon Sep 17 00:00:00 2001 From: HATAYAMA Daisuke Date: Wed, 3 Jul 2013 15:02:23 -0700 Subject: vmcore: support mmap() on /proc/vmcore This patch introduces mmap_vmcore(). Don't permit writable nor executable mapping even with mprotect() because this mmap() is aimed at reading crash dump memory. Non-writable mapping is also requirement of remap_pfn_range() when mapping linear pages on non-consecutive physical pages; see is_cow_mapping(). Set VM_MIXEDMAP flag to remap memory by remap_pfn_range and by remap_vmalloc_range_pertial at the same time for a single vma. do_munmap() can correctly clean partially remapped vma with two functions in abnormal case. See zap_pte_range(), vm_normal_page() and their comments for details. On x86-32 PAE kernels, mmap() supports at most 16TB memory only. This limitation comes from the fact that the third argument of remap_pfn_range(), pfn, is of 32-bit length on x86-32: unsigned long. [akpm@linux-foundation.org: use min(), switch to conventional error-unwinding approach] Signed-off-by: HATAYAMA Daisuke Acked-by: Vivek Goyal Cc: KOSAKI Motohiro Cc: Atsushi Kumagai Cc: Lisa Mitchell Cc: Zhang Yanfei Tested-by: Maxim Uvarov Cc: Arnd Bergmann Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/proc/vmcore.c | 136 +++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 116 insertions(+), 20 deletions(-) diff --git a/fs/proc/vmcore.c b/fs/proc/vmcore.c index 8ec648368985..28503172f2e4 100644 --- a/fs/proc/vmcore.c +++ b/fs/proc/vmcore.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include "internal.h" @@ -194,9 +195,122 @@ static ssize_t read_vmcore(struct file *file, char __user *buffer, return acc; } +/** + * alloc_elfnotes_buf - allocate buffer for ELF note segment in + * vmalloc memory + * + * @notes_sz: size of buffer + * + * If CONFIG_MMU is defined, use vmalloc_user() to allow users to mmap + * the buffer to user-space by means of remap_vmalloc_range(). + * + * If CONFIG_MMU is not defined, use vzalloc() since mmap_vmcore() is + * disabled and there's no need to allow users to mmap the buffer. + */ +static inline char *alloc_elfnotes_buf(size_t notes_sz) +{ +#ifdef CONFIG_MMU + return vmalloc_user(notes_sz); +#else + return vzalloc(notes_sz); +#endif +} + +/* + * Disable mmap_vmcore() if CONFIG_MMU is not defined. MMU is + * essential for mmap_vmcore() in order to map physically + * non-contiguous objects (ELF header, ELF note segment and memory + * regions in the 1st kernel pointed to by PT_LOAD entries) into + * virtually contiguous user-space in ELF layout. + */ +#ifdef CONFIG_MMU +static int mmap_vmcore(struct file *file, struct vm_area_struct *vma) +{ + size_t size = vma->vm_end - vma->vm_start; + u64 start, end, len, tsz; + struct vmcore *m; + + start = (u64)vma->vm_pgoff << PAGE_SHIFT; + end = start + size; + + if (size > vmcore_size || end > vmcore_size) + return -EINVAL; + + if (vma->vm_flags & (VM_WRITE | VM_EXEC)) + return -EPERM; + + vma->vm_flags &= ~(VM_MAYWRITE | VM_MAYEXEC); + vma->vm_flags |= VM_MIXEDMAP; + + len = 0; + + if (start < elfcorebuf_sz) { + u64 pfn; + + tsz = min(elfcorebuf_sz - (size_t)start, size); + pfn = __pa(elfcorebuf + start) >> PAGE_SHIFT; + if (remap_pfn_range(vma, vma->vm_start, pfn, tsz, + vma->vm_page_prot)) + return -EAGAIN; + size -= tsz; + start += tsz; + len += tsz; + + if (size == 0) + return 0; + } + + if (start < elfcorebuf_sz + elfnotes_sz) { + void *kaddr; + + tsz = min(elfcorebuf_sz + elfnotes_sz - (size_t)start, size); + kaddr = elfnotes_buf + start - elfcorebuf_sz; + if (remap_vmalloc_range_partial(vma, vma->vm_start + len, + kaddr, tsz)) + goto fail; + size -= tsz; + start += tsz; + len += tsz; + + if (size == 0) + return 0; + } + + list_for_each_entry(m, &vmcore_list, list) { + if (start < m->offset + m->size) { + u64 paddr = 0; + + tsz = min_t(size_t, m->offset + m->size - start, size); + paddr = m->paddr + start - m->offset; + if (remap_pfn_range(vma, vma->vm_start + len, + paddr >> PAGE_SHIFT, tsz, + vma->vm_page_prot)) + goto fail; + size -= tsz; + start += tsz; + len += tsz; + + if (size == 0) + return 0; + } + } + + return 0; +fail: + do_munmap(vma->vm_mm, vma->vm_start, len); + return -EAGAIN; +} +#else +static int mmap_vmcore(struct file *file, struct vm_area_struct *vma) +{ + return -ENOSYS; +} +#endif + static const struct file_operations proc_vmcore_operations = { .read = read_vmcore, .llseek = default_llseek, + .mmap = mmap_vmcore, }; static struct vmcore* __init get_new_element(void) @@ -348,7 +462,6 @@ static int __init merge_note_headers_elf64(char *elfptr, size_t *elfsz, Elf64_Ehdr *ehdr_ptr; Elf64_Phdr phdr; u64 phdr_sz = 0, note_off; - struct vm_struct *vm; ehdr_ptr = (Elf64_Ehdr *)elfptr; @@ -361,18 +474,10 @@ static int __init merge_note_headers_elf64(char *elfptr, size_t *elfsz, return rc; *notes_sz = roundup(phdr_sz, PAGE_SIZE); - *notes_buf = vzalloc(*notes_sz); + *notes_buf = alloc_elfnotes_buf(*notes_sz); if (!*notes_buf) return -ENOMEM; - /* - * Allow users to remap ELF note segment buffer on vmalloc memory using - * remap_vmalloc_range.() - */ - vm = find_vm_area(*notes_buf); - BUG_ON(!vm); - vm->flags |= VM_USERMAP; - rc = copy_notes_elf64(ehdr_ptr, *notes_buf); if (rc < 0) return rc; @@ -536,7 +641,6 @@ static int __init merge_note_headers_elf32(char *elfptr, size_t *elfsz, Elf32_Ehdr *ehdr_ptr; Elf32_Phdr phdr; u64 phdr_sz = 0, note_off; - struct vm_struct *vm; ehdr_ptr = (Elf32_Ehdr *)elfptr; @@ -549,18 +653,10 @@ static int __init merge_note_headers_elf32(char *elfptr, size_t *elfsz, return rc; *notes_sz = roundup(phdr_sz, PAGE_SIZE); - *notes_buf = vzalloc(*notes_sz); + *notes_buf = alloc_elfnotes_buf(*notes_sz); if (!*notes_buf) return -ENOMEM; - /* - * Allow users to remap ELF note segment buffer on vmalloc memory using - * remap_vmalloc_range() - */ - vm = find_vm_area(*notes_buf); - BUG_ON(!vm); - vm->flags |= VM_USERMAP; - rc = copy_notes_elf32(ehdr_ptr, *notes_buf); if (rc < 0) return rc; -- cgit v1.2.3 From f968ef1c55199301e98c28fe7dfa8ace05ecdb96 Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Wed, 3 Jul 2013 15:02:25 -0700 Subject: memcg: update TODO list in Documentation hugetlb cgroup has already been implemented. Signed-off-by: Li Zefan Acked-by: KAMEZAWA Hiroyuki Acked-by: Rob Landley Cc: Michal Hocko Cc: Johannes Weiner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/cgroups/memory.txt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Documentation/cgroups/memory.txt b/Documentation/cgroups/memory.txt index ddf4f93967a9..327acec6f90b 100644 --- a/Documentation/cgroups/memory.txt +++ b/Documentation/cgroups/memory.txt @@ -834,10 +834,9 @@ Test: 12. TODO -1. Add support for accounting huge pages (as a separate controller) -2. Make per-cgroup scanner reclaim not-shared pages first -3. Teach controller to account for shared-pages -4. Start reclamation in the background when the limit is +1. Make per-cgroup scanner reclaim not-shared pages first +2. Teach controller to account for shared-pages +3. Start reclamation in the background when the limit is not yet hit but the usage is getting closer Summary -- cgit v1.2.3 From c6286c983900c77410a951874f1589f4a41fbbae Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Wed, 3 Jul 2013 15:02:26 -0700 Subject: mm: add tracepoints for LRU activation and insertions Andrew Perepechko reported a problem whereby pages are being prematurely evicted as the mark_page_accessed() hint is ignored for pages that are currently on a pagevec -- http://www.spinics.net/lists/linux-ext4/msg37340.html . Alexey Lyahkov and Robin Dong have also reported problems recently that could be due to hot pages reaching the end of the inactive list too quickly and be reclaimed. Rather than addressing this on a per-filesystem basis, this series aims to fix the mark_page_accessed() interface by deferring what LRU a page is added to pagevec drain time and allowing mark_page_accessed() to call SetPageActive on a pagevec page. Patch 1 adds two tracepoints for LRU page activation and insertion. Using these processes it's possible to build a model of pages in the LRU that can be processed offline. Patch 2 defers making the decision on what LRU to add a page to until when the pagevec is drained. Patch 3 searches the local pagevec for pages to mark PageActive on mark_page_accessed. The changelog explains why only the local pagevec is examined. Patches 4 and 5 tidy up the API. postmark, a dd-based test and fs-mark both single and threaded mode were run but none of them showed any performance degradation or gain as a result of the patch. Using patch 1, I built a *very* basic model of the LRU to examine offline what the average age of different page types on the LRU were in milliseconds. Of course, capturing the trace distorts the test as it's written to local disk but it does not matter for the purposes of this test. The average age of pages in milliseconds were vanilla deferdrain Average age mapped anon: 1454 1250 Average age mapped file: 127841 155552 Average age unmapped anon: 85 235 Average age unmapped file: 73633 38884 Average age unmapped buffers: 74054 116155 The LRU activity was mostly files which you'd expect for a dd-based workload. Note that the average age of buffer pages is increased by the series and it is expected this is due to the fact that the buffer pages are now getting added to the active list when drained from the pagevecs. Note that the average age of the unmapped file data is decreased as they are still added to the inactive list and are reclaimed before the buffers. There is no guarantee this is a universal win for all workloads and it would be nice if the filesystem people gave some thought as to whether this decision is generally a win or a loss. This patch: Using these tracepoints it is possible to model LRU activity and the average residency of pages of different types. This can be used to debug problems related to premature reclaim of pages of particular types. Signed-off-by: Mel Gorman Reviewed-by: Rik van Riel Cc: Jan Kara Cc: Johannes Weiner Cc: Alexey Lyahkov Cc: Andrew Perepechko Cc: Robin Dong Cc: Theodore Tso Cc: Hugh Dickins Cc: Rik van Riel Cc: Bernd Schubert Cc: David Howells Cc: Trond Myklebust Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/trace/events/pagemap.h | 89 ++++++++++++++++++++++++++++++++++++++++++ mm/swap.c | 5 +++ 2 files changed, 94 insertions(+) create mode 100644 include/trace/events/pagemap.h diff --git a/include/trace/events/pagemap.h b/include/trace/events/pagemap.h new file mode 100644 index 000000000000..1c9fabde69e4 --- /dev/null +++ b/include/trace/events/pagemap.h @@ -0,0 +1,89 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM pagemap + +#if !defined(_TRACE_PAGEMAP_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_PAGEMAP_H + +#include +#include + +#define PAGEMAP_MAPPED 0x0001u +#define PAGEMAP_ANONYMOUS 0x0002u +#define PAGEMAP_FILE 0x0004u +#define PAGEMAP_SWAPCACHE 0x0008u +#define PAGEMAP_SWAPBACKED 0x0010u +#define PAGEMAP_MAPPEDDISK 0x0020u +#define PAGEMAP_BUFFERS 0x0040u + +#define trace_pagemap_flags(page) ( \ + (PageAnon(page) ? PAGEMAP_ANONYMOUS : PAGEMAP_FILE) | \ + (page_mapped(page) ? PAGEMAP_MAPPED : 0) | \ + (PageSwapCache(page) ? PAGEMAP_SWAPCACHE : 0) | \ + (PageSwapBacked(page) ? PAGEMAP_SWAPBACKED : 0) | \ + (PageMappedToDisk(page) ? PAGEMAP_MAPPEDDISK : 0) | \ + (page_has_private(page) ? PAGEMAP_BUFFERS : 0) \ + ) + +TRACE_EVENT(mm_lru_insertion, + + TP_PROTO( + struct page *page, + unsigned long pfn, + int lru, + unsigned long flags + ), + + TP_ARGS(page, pfn, lru, flags), + + TP_STRUCT__entry( + __field(struct page *, page ) + __field(unsigned long, pfn ) + __field(int, lru ) + __field(unsigned long, flags ) + ), + + TP_fast_assign( + __entry->page = page; + __entry->pfn = pfn; + __entry->lru = lru; + __entry->flags = flags; + ), + + /* Flag format is based on page-types.c formatting for pagemap */ + TP_printk("page=%p pfn=%lu lru=%d flags=%s%s%s%s%s%s", + __entry->page, + __entry->pfn, + __entry->lru, + __entry->flags & PAGEMAP_MAPPED ? "M" : " ", + __entry->flags & PAGEMAP_ANONYMOUS ? "a" : "f", + __entry->flags & PAGEMAP_SWAPCACHE ? "s" : " ", + __entry->flags & PAGEMAP_SWAPBACKED ? "b" : " ", + __entry->flags & PAGEMAP_MAPPEDDISK ? "d" : " ", + __entry->flags & PAGEMAP_BUFFERS ? "B" : " ") +); + +TRACE_EVENT(mm_lru_activate, + + TP_PROTO(struct page *page, unsigned long pfn), + + TP_ARGS(page, pfn), + + TP_STRUCT__entry( + __field(struct page *, page ) + __field(unsigned long, pfn ) + ), + + TP_fast_assign( + __entry->page = page; + __entry->pfn = pfn; + ), + + /* Flag format is based on page-types.c formatting for pagemap */ + TP_printk("page=%p pfn=%lu", __entry->page, __entry->pfn) + +); + +#endif /* _TRACE_PAGEMAP_H */ + +/* This part must be outside protection */ +#include diff --git a/mm/swap.c b/mm/swap.c index dfd7d71d6841..53c9ceb7b816 100644 --- a/mm/swap.c +++ b/mm/swap.c @@ -34,6 +34,9 @@ #include "internal.h" +#define CREATE_TRACE_POINTS +#include + /* How many pages do we try to swap or page in/out together? */ int page_cluster; @@ -384,6 +387,7 @@ static void __activate_page(struct page *page, struct lruvec *lruvec, SetPageActive(page); lru += LRU_ACTIVE; add_page_to_lru_list(page, lruvec, lru); + trace_mm_lru_activate(page, page_to_pfn(page)); __count_vm_event(PGACTIVATE); update_page_reclaim_stat(lruvec, file, 1); @@ -808,6 +812,7 @@ static void __pagevec_lru_add_fn(struct page *page, struct lruvec *lruvec, SetPageActive(page); add_page_to_lru_list(page, lruvec, lru); update_page_reclaim_stat(lruvec, file, active); + trace_mm_lru_insertion(page, page_to_pfn(page), lru, trace_pagemap_flags(page)); } /* -- cgit v1.2.3 From 13f7f78981e49f288d871bb918545ef5c952e00b Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Wed, 3 Jul 2013 15:02:28 -0700 Subject: mm: pagevec: defer deciding which LRU to add a page to until pagevec drain time mark_page_accessed() cannot activate an inactive page that is located on an inactive LRU pagevec. Hints from filesystems may be ignored as a result. In preparation for fixing that problem, this patch removes the per-LRU pagevecs and leaves just one pagevec. The final LRU the page is added to is deferred until the pagevec is drained. This means that fewer pagevecs are available and potentially there is greater contention on the LRU lock. However, this only applies in the case where there is an almost perfect mix of file, anon, active and inactive pages being added to the LRU. In practice I expect that we are adding stream of pages of a particular time and that the changes in contention will barely be measurable. Signed-off-by: Mel Gorman Acked-by: Rik van Riel Cc: Jan Kara Acked-by: Johannes Weiner Cc: Alexey Lyahkov Cc: Andrew Perepechko Cc: Robin Dong Cc: Theodore Tso Cc: Hugh Dickins Cc: Rik van Riel Cc: Bernd Schubert Cc: David Howells Cc: Trond Myklebust Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/swap.c | 47 +++++++++++++++++++++-------------------------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/mm/swap.c b/mm/swap.c index 53c9ceb7b816..868b493431c2 100644 --- a/mm/swap.c +++ b/mm/swap.c @@ -40,7 +40,7 @@ /* How many pages do we try to swap or page in/out together? */ int page_cluster; -static DEFINE_PER_CPU(struct pagevec[NR_LRU_LISTS], lru_add_pvecs); +static DEFINE_PER_CPU(struct pagevec, lru_add_pvec); static DEFINE_PER_CPU(struct pagevec, lru_rotate_pvecs); static DEFINE_PER_CPU(struct pagevec, lru_deactivate_pvecs); @@ -452,22 +452,25 @@ void mark_page_accessed(struct page *page) EXPORT_SYMBOL(mark_page_accessed); /* - * Order of operations is important: flush the pagevec when it's already - * full, not when adding the last page, to make sure that last page is - * not added to the LRU directly when passed to this function. Because - * mark_page_accessed() (called after this when writing) only activates - * pages that are on the LRU, linear writes in subpage chunks would see - * every PAGEVEC_SIZE page activated, which is unexpected. + * Queue the page for addition to the LRU via pagevec. The decision on whether + * to add the page to the [in]active [file|anon] list is deferred until the + * pagevec is drained. This gives a chance for the caller of __lru_cache_add() + * have the page added to the active list using mark_page_accessed(). */ void __lru_cache_add(struct page *page, enum lru_list lru) { - struct pagevec *pvec = &get_cpu_var(lru_add_pvecs)[lru]; + struct pagevec *pvec = &get_cpu_var(lru_add_pvec); + + if (is_active_lru(lru)) + SetPageActive(page); + else + ClearPageActive(page); page_cache_get(page); if (!pagevec_space(pvec)) __pagevec_lru_add(pvec, lru); pagevec_add(pvec, page); - put_cpu_var(lru_add_pvecs); + put_cpu_var(lru_add_pvec); } EXPORT_SYMBOL(__lru_cache_add); @@ -480,13 +483,11 @@ void lru_cache_add_lru(struct page *page, enum lru_list lru) { if (PageActive(page)) { VM_BUG_ON(PageUnevictable(page)); - ClearPageActive(page); } else if (PageUnevictable(page)) { VM_BUG_ON(PageActive(page)); - ClearPageUnevictable(page); } - VM_BUG_ON(PageLRU(page) || PageActive(page) || PageUnevictable(page)); + VM_BUG_ON(PageLRU(page)); __lru_cache_add(page, lru); } @@ -587,15 +588,10 @@ static void lru_deactivate_fn(struct page *page, struct lruvec *lruvec, */ void lru_add_drain_cpu(int cpu) { - struct pagevec *pvecs = per_cpu(lru_add_pvecs, cpu); - struct pagevec *pvec; - int lru; + struct pagevec *pvec = &per_cpu(lru_add_pvec, cpu); - for_each_lru(lru) { - pvec = &pvecs[lru - LRU_BASE]; - if (pagevec_count(pvec)) - __pagevec_lru_add(pvec, lru); - } + if (pagevec_count(pvec)) + __pagevec_lru_add(pvec, NR_LRU_LISTS); pvec = &per_cpu(lru_rotate_pvecs, cpu); if (pagevec_count(pvec)) { @@ -799,17 +795,16 @@ void lru_add_page_tail(struct page *page, struct page *page_tail, static void __pagevec_lru_add_fn(struct page *page, struct lruvec *lruvec, void *arg) { - enum lru_list lru = (enum lru_list)arg; - int file = is_file_lru(lru); - int active = is_active_lru(lru); + enum lru_list requested_lru = (enum lru_list)arg; + int file = page_is_file_cache(page); + int active = PageActive(page); + enum lru_list lru = page_lru(page); - VM_BUG_ON(PageActive(page)); + WARN_ON_ONCE(requested_lru < NR_LRU_LISTS && requested_lru != lru); VM_BUG_ON(PageUnevictable(page)); VM_BUG_ON(PageLRU(page)); SetPageLRU(page); - if (active) - SetPageActive(page); add_page_to_lru_list(page, lruvec, lru); update_page_reclaim_stat(lruvec, file, active); trace_mm_lru_insertion(page, page_to_pfn(page), lru, trace_pagemap_flags(page)); -- cgit v1.2.3 From 059285a25f30c13ed4f5d91cecd6094b9b20bb7b Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Wed, 3 Jul 2013 15:02:30 -0700 Subject: mm: activate !PageLRU pages on mark_page_accessed if page is on local pagevec If a page is on a pagevec then it is !PageLRU and mark_page_accessed() may fail to move a page to the active list as expected. Now that the LRU is selected at LRU drain time, mark pages PageActive if they are on the local pagevec so it gets moved to the correct list at LRU drain time. Using a debugging patch it was found that for a simple git checkout based workload that pages were never added to the active file list in practice but with this patch applied they are. before after LRU Add Active File 0 750583 LRU Add Active Anon 2640587 2702818 LRU Add Inactive File 8833662 8068353 LRU Add Inactive Anon 207 200 Note that only pages on the local pagevec are considered on purpose. A !PageLRU page could be in the process of being released, reclaimed, migrated or on a remote pagevec that is currently being drained. Marking it PageActive is vunerable to races where PageLRU and Active bits are checked at the wrong time. Page reclaim will trigger VM_BUG_ONs but depending on when the race hits, it could also free a PageActive page to the page allocator and trigger a bad_page warning. Similarly a potential race exists between a per-cpu drain on a pagevec list and an activation on a remote CPU. lru_add_drain_cpu __pagevec_lru_add lru = page_lru(page); mark_page_accessed if (PageLRU(page)) activate_page else SetPageActive SetPageLRU(page); add_page_to_lru_list(page, lruvec, lru); In this case a PageActive page is added to the inactivate list and later the inactive/active stats will get skewed. While the PageActive checks in vmscan could be removed and potentially dealt with, a skew in the statistics would be very difficult to detect. Hence this patch deals just with the common case where a page being marked accessed has just been added to the local pagevec. Signed-off-by: Mel Gorman Cc: Jan Kara Cc: Rik van Riel Acked-by: Johannes Weiner Cc: Alexey Lyahkov Cc: Andrew Perepechko Cc: Robin Dong Cc: Theodore Tso Cc: Hugh Dickins Cc: Rik van Riel Cc: Bernd Schubert Cc: David Howells Cc: Trond Myklebust Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/swap.c | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/mm/swap.c b/mm/swap.c index 868b493431c2..c53d161fc76d 100644 --- a/mm/swap.c +++ b/mm/swap.c @@ -432,6 +432,33 @@ void activate_page(struct page *page) } #endif +static void __lru_cache_activate_page(struct page *page) +{ + struct pagevec *pvec = &get_cpu_var(lru_add_pvec); + int i; + + /* + * Search backwards on the optimistic assumption that the page being + * activated has just been added to this pagevec. Note that only + * the local pagevec is examined as a !PageLRU page could be in the + * process of being released, reclaimed, migrated or on a remote + * pagevec that is currently being drained. Furthermore, marking + * a remote pagevec's page PageActive potentially hits a race where + * a page is marked PageActive just after it is added to the inactive + * list causing accounting errors and BUG_ON checks to trigger. + */ + for (i = pagevec_count(pvec) - 1; i >= 0; i--) { + struct page *pagevec_page = pvec->pages[i]; + + if (pagevec_page == page) { + SetPageActive(page); + break; + } + } + + put_cpu_var(lru_add_pvec); +} + /* * Mark a page as having seen activity. * @@ -442,8 +469,18 @@ void activate_page(struct page *page) void mark_page_accessed(struct page *page) { if (!PageActive(page) && !PageUnevictable(page) && - PageReferenced(page) && PageLRU(page)) { - activate_page(page); + PageReferenced(page)) { + + /* + * If the page is on the LRU, queue it for activation via + * activate_page_pvecs. Otherwise, assume the page is on a + * pagevec, mark it active and it'll be moved to the active + * LRU on the next drain. + */ + if (PageLRU(page)) + activate_page(page); + else + __lru_cache_activate_page(page); ClearPageReferenced(page); } else if (!PageReferenced(page)) { SetPageReferenced(page); -- cgit v1.2.3 From a0b8cab3b9b2efadabdcff264c450ca515e2619c Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Wed, 3 Jul 2013 15:02:32 -0700 Subject: mm: remove lru parameter from __pagevec_lru_add and remove parts of pagevec API Now that the LRU to add a page to is decided at LRU-add time, remove the misleading lru parameter from __pagevec_lru_add. A consequence of this is that the pagevec_lru_add_file, pagevec_lru_add_anon and similar helpers are misleading as the caller no longer has direct control over what LRU the page is added to. Unused helpers are removed by this patch and existing users of pagevec_lru_add_file() are converted to use lru_cache_add_file() directly and use the per-cpu pagevecs instead of creating their own pagevec. Signed-off-by: Mel Gorman Reviewed-by: Jan Kara Reviewed-by: Rik van Riel Acked-by: Johannes Weiner Cc: Alexey Lyahkov Cc: Andrew Perepechko Cc: Robin Dong Cc: Theodore Tso Cc: Hugh Dickins Cc: Rik van Riel Cc: Bernd Schubert Cc: David Howells Cc: Trond Myklebust Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/cachefiles/rdwr.c | 30 +++++++----------------------- fs/nfs/dir.c | 7 ++----- include/linux/pagevec.h | 34 +--------------------------------- mm/swap.c | 12 ++++-------- 4 files changed, 14 insertions(+), 69 deletions(-) diff --git a/fs/cachefiles/rdwr.c b/fs/cachefiles/rdwr.c index 317f9ee9c991..ebaff368120d 100644 --- a/fs/cachefiles/rdwr.c +++ b/fs/cachefiles/rdwr.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "internal.h" /* @@ -227,8 +228,7 @@ static void cachefiles_read_copier(struct fscache_operation *_op) */ static int cachefiles_read_backing_file_one(struct cachefiles_object *object, struct fscache_retrieval *op, - struct page *netpage, - struct pagevec *pagevec) + struct page *netpage) { struct cachefiles_one_read *monitor; struct address_space *bmapping; @@ -237,8 +237,6 @@ static int cachefiles_read_backing_file_one(struct cachefiles_object *object, _enter(""); - pagevec_reinit(pagevec); - _debug("read back %p{%lu,%d}", netpage, netpage->index, page_count(netpage)); @@ -283,9 +281,7 @@ installed_new_backing_page: backpage = newpage; newpage = NULL; - page_cache_get(backpage); - pagevec_add(pagevec, backpage); - __pagevec_lru_add_file(pagevec); + lru_cache_add_file(backpage); read_backing_page: ret = bmapping->a_ops->readpage(NULL, backpage); @@ -452,8 +448,7 @@ int cachefiles_read_or_alloc_page(struct fscache_retrieval *op, if (block) { /* submit the apparently valid page to the backing fs to be * read from disk */ - ret = cachefiles_read_backing_file_one(object, op, page, - &pagevec); + ret = cachefiles_read_backing_file_one(object, op, page); } else if (cachefiles_has_space(cache, 0, 1) == 0) { /* there's space in the cache we can use */ fscache_mark_page_cached(op, page); @@ -482,14 +477,11 @@ static int cachefiles_read_backing_file(struct cachefiles_object *object, { struct cachefiles_one_read *monitor = NULL; struct address_space *bmapping = object->backer->d_inode->i_mapping; - struct pagevec lru_pvec; struct page *newpage = NULL, *netpage, *_n, *backpage = NULL; int ret = 0; _enter(""); - pagevec_init(&lru_pvec, 0); - list_for_each_entry_safe(netpage, _n, list, lru) { list_del(&netpage->lru); @@ -534,9 +526,7 @@ static int cachefiles_read_backing_file(struct cachefiles_object *object, backpage = newpage; newpage = NULL; - page_cache_get(backpage); - if (!pagevec_add(&lru_pvec, backpage)) - __pagevec_lru_add_file(&lru_pvec); + lru_cache_add_file(backpage); reread_backing_page: ret = bmapping->a_ops->readpage(NULL, backpage); @@ -559,9 +549,7 @@ static int cachefiles_read_backing_file(struct cachefiles_object *object, goto nomem; } - page_cache_get(netpage); - if (!pagevec_add(&lru_pvec, netpage)) - __pagevec_lru_add_file(&lru_pvec); + lru_cache_add_file(netpage); /* install a monitor */ page_cache_get(netpage); @@ -643,9 +631,7 @@ static int cachefiles_read_backing_file(struct cachefiles_object *object, fscache_mark_page_cached(op, netpage); - page_cache_get(netpage); - if (!pagevec_add(&lru_pvec, netpage)) - __pagevec_lru_add_file(&lru_pvec); + lru_cache_add_file(netpage); /* the netpage is unlocked and marked up to date here */ fscache_end_io(op, netpage, 0); @@ -661,8 +647,6 @@ static int cachefiles_read_backing_file(struct cachefiles_object *object, out: /* tidy up */ - pagevec_lru_add_file(&lru_pvec); - if (newpage) page_cache_release(newpage); if (netpage) diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 5d051419527b..d7ed697133f0 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -1758,7 +1759,6 @@ EXPORT_SYMBOL_GPL(nfs_unlink); */ int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { - struct pagevec lru_pvec; struct page *page; char *kaddr; struct iattr attr; @@ -1798,11 +1798,8 @@ int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) * No big deal if we can't add this page to the page cache here. * READLINK will get the missing page from the server if needed. */ - pagevec_init(&lru_pvec, 0); - if (!add_to_page_cache(page, dentry->d_inode->i_mapping, 0, + if (!add_to_page_cache_lru(page, dentry->d_inode->i_mapping, 0, GFP_KERNEL)) { - pagevec_add(&lru_pvec, page); - pagevec_lru_add_file(&lru_pvec); SetPageUptodate(page); unlock_page(page); } else diff --git a/include/linux/pagevec.h b/include/linux/pagevec.h index 2aa12b8499c0..e4dbfab37729 100644 --- a/include/linux/pagevec.h +++ b/include/linux/pagevec.h @@ -21,7 +21,7 @@ struct pagevec { }; void __pagevec_release(struct pagevec *pvec); -void __pagevec_lru_add(struct pagevec *pvec, enum lru_list lru); +void __pagevec_lru_add(struct pagevec *pvec); unsigned pagevec_lookup(struct pagevec *pvec, struct address_space *mapping, pgoff_t start, unsigned nr_pages); unsigned pagevec_lookup_tag(struct pagevec *pvec, @@ -64,36 +64,4 @@ static inline void pagevec_release(struct pagevec *pvec) __pagevec_release(pvec); } -static inline void __pagevec_lru_add_anon(struct pagevec *pvec) -{ - __pagevec_lru_add(pvec, LRU_INACTIVE_ANON); -} - -static inline void __pagevec_lru_add_active_anon(struct pagevec *pvec) -{ - __pagevec_lru_add(pvec, LRU_ACTIVE_ANON); -} - -static inline void __pagevec_lru_add_file(struct pagevec *pvec) -{ - __pagevec_lru_add(pvec, LRU_INACTIVE_FILE); -} - -static inline void __pagevec_lru_add_active_file(struct pagevec *pvec) -{ - __pagevec_lru_add(pvec, LRU_ACTIVE_FILE); -} - -static inline void pagevec_lru_add_file(struct pagevec *pvec) -{ - if (pagevec_count(pvec)) - __pagevec_lru_add_file(pvec); -} - -static inline void pagevec_lru_add_anon(struct pagevec *pvec) -{ - if (pagevec_count(pvec)) - __pagevec_lru_add_anon(pvec); -} - #endif /* _LINUX_PAGEVEC_H */ diff --git a/mm/swap.c b/mm/swap.c index c53d161fc76d..6a9d0c43924a 100644 --- a/mm/swap.c +++ b/mm/swap.c @@ -505,7 +505,7 @@ void __lru_cache_add(struct page *page, enum lru_list lru) page_cache_get(page); if (!pagevec_space(pvec)) - __pagevec_lru_add(pvec, lru); + __pagevec_lru_add(pvec); pagevec_add(pvec, page); put_cpu_var(lru_add_pvec); } @@ -628,7 +628,7 @@ void lru_add_drain_cpu(int cpu) struct pagevec *pvec = &per_cpu(lru_add_pvec, cpu); if (pagevec_count(pvec)) - __pagevec_lru_add(pvec, NR_LRU_LISTS); + __pagevec_lru_add(pvec); pvec = &per_cpu(lru_rotate_pvecs, cpu); if (pagevec_count(pvec)) { @@ -832,12 +832,10 @@ void lru_add_page_tail(struct page *page, struct page *page_tail, static void __pagevec_lru_add_fn(struct page *page, struct lruvec *lruvec, void *arg) { - enum lru_list requested_lru = (enum lru_list)arg; int file = page_is_file_cache(page); int active = PageActive(page); enum lru_list lru = page_lru(page); - WARN_ON_ONCE(requested_lru < NR_LRU_LISTS && requested_lru != lru); VM_BUG_ON(PageUnevictable(page)); VM_BUG_ON(PageLRU(page)); @@ -851,11 +849,9 @@ static void __pagevec_lru_add_fn(struct page *page, struct lruvec *lruvec, * Add the passed pages to the LRU, then drop the caller's refcount * on them. Reinitialises the caller's pagevec. */ -void __pagevec_lru_add(struct pagevec *pvec, enum lru_list lru) +void __pagevec_lru_add(struct pagevec *pvec) { - VM_BUG_ON(is_unevictable_lru(lru)); - - pagevec_lru_move_fn(pvec, __pagevec_lru_add_fn, (void *)lru); + pagevec_lru_move_fn(pvec, __pagevec_lru_add_fn, NULL); } EXPORT_SYMBOL(__pagevec_lru_add); -- cgit v1.2.3 From c53954a092d07c5684d31ea1fc813d262cff08a5 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Wed, 3 Jul 2013 15:02:34 -0700 Subject: mm: remove lru parameter from __lru_cache_add and lru_cache_add_lru Similar to __pagevec_lru_add, this patch removes the LRU parameter from __lru_cache_add and lru_cache_add_lru as the caller does not control the exact LRU the page gets added to. lru_cache_add_lru gets renamed to lru_cache_add the name is silly without the lru parameter. With the parameter removed, it is required that the caller indicate if they want the page added to the active or inactive list by setting or clearing PageActive respectively. [akpm@linux-foundation.org: Suggested the patch] [gang.chen@asianux.com: fix used-unintialized warning] Signed-off-by: Mel Gorman Signed-off-by: Chen Gang Cc: Jan Kara Cc: Rik van Riel Acked-by: Johannes Weiner Cc: Alexey Lyahkov Cc: Andrew Perepechko Cc: Robin Dong Cc: Theodore Tso Cc: Hugh Dickins Cc: Rik van Riel Cc: Bernd Schubert Cc: David Howells Cc: Trond Myklebust Cc: Mel Gorman Cc: Rik van Riel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/swap.h | 11 +++++++---- mm/rmap.c | 7 ++++--- mm/swap.c | 17 +++++++---------- mm/vmscan.c | 5 ++--- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/include/linux/swap.h b/include/linux/swap.h index 1701ce4be746..85d74373002c 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -10,6 +10,7 @@ #include #include #include +#include #include struct notifier_block; @@ -233,8 +234,8 @@ extern unsigned long nr_free_pagecache_pages(void); /* linux/mm/swap.c */ -extern void __lru_cache_add(struct page *, enum lru_list lru); -extern void lru_cache_add_lru(struct page *, enum lru_list lru); +extern void __lru_cache_add(struct page *); +extern void lru_cache_add(struct page *); extern void lru_add_page_tail(struct page *page, struct page *page_tail, struct lruvec *lruvec, struct list_head *head); extern void activate_page(struct page *); @@ -254,12 +255,14 @@ extern void add_page_to_unevictable_list(struct page *page); */ static inline void lru_cache_add_anon(struct page *page) { - __lru_cache_add(page, LRU_INACTIVE_ANON); + ClearPageActive(page); + __lru_cache_add(page); } static inline void lru_cache_add_file(struct page *page) { - __lru_cache_add(page, LRU_INACTIVE_FILE); + ClearPageActive(page); + __lru_cache_add(page); } /* linux/mm/vmscan.c */ diff --git a/mm/rmap.c b/mm/rmap.c index 6280da86b5d6..e22ceeb6e5ec 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -1093,9 +1093,10 @@ void page_add_new_anon_rmap(struct page *page, else __inc_zone_page_state(page, NR_ANON_TRANSPARENT_HUGEPAGES); __page_set_anon_rmap(page, vma, address, 1); - if (!mlocked_vma_newpage(vma, page)) - lru_cache_add_lru(page, LRU_ACTIVE_ANON); - else + if (!mlocked_vma_newpage(vma, page)) { + SetPageActive(page); + lru_cache_add(page); + } else add_page_to_unevictable_list(page); } diff --git a/mm/swap.c b/mm/swap.c index 6a9d0c43924a..4a1d0d2c52fa 100644 --- a/mm/swap.c +++ b/mm/swap.c @@ -494,15 +494,10 @@ EXPORT_SYMBOL(mark_page_accessed); * pagevec is drained. This gives a chance for the caller of __lru_cache_add() * have the page added to the active list using mark_page_accessed(). */ -void __lru_cache_add(struct page *page, enum lru_list lru) +void __lru_cache_add(struct page *page) { struct pagevec *pvec = &get_cpu_var(lru_add_pvec); - if (is_active_lru(lru)) - SetPageActive(page); - else - ClearPageActive(page); - page_cache_get(page); if (!pagevec_space(pvec)) __pagevec_lru_add(pvec); @@ -512,11 +507,10 @@ void __lru_cache_add(struct page *page, enum lru_list lru) EXPORT_SYMBOL(__lru_cache_add); /** - * lru_cache_add_lru - add a page to a page list + * lru_cache_add - add a page to a page list * @page: the page to be added to the LRU. - * @lru: the LRU list to which the page is added. */ -void lru_cache_add_lru(struct page *page, enum lru_list lru) +void lru_cache_add(struct page *page) { if (PageActive(page)) { VM_BUG_ON(PageUnevictable(page)); @@ -525,7 +519,7 @@ void lru_cache_add_lru(struct page *page, enum lru_list lru) } VM_BUG_ON(PageLRU(page)); - __lru_cache_add(page, lru); + __lru_cache_add(page); } /** @@ -745,6 +739,9 @@ void release_pages(struct page **pages, int nr, int cold) del_page_from_lru_list(page, lruvec, page_off_lru(page)); } + /* Clear Active bit in case of parallel mark_page_accessed */ + ClearPageActive(page); + list_add(&page->lru, &pages_to_free); } if (zone) diff --git a/mm/vmscan.c b/mm/vmscan.c index c85794399848..99b3ac7771ad 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -546,7 +546,6 @@ int remove_mapping(struct address_space *mapping, struct page *page) void putback_lru_page(struct page *page) { int lru; - int active = !!TestClearPageActive(page); int was_unevictable = PageUnevictable(page); VM_BUG_ON(PageLRU(page)); @@ -561,8 +560,8 @@ redo: * unevictable page on [in]active list. * We know how to handle that. */ - lru = active + page_lru_base_type(page); - lru_cache_add_lru(page, lru); + lru = page_lru_base_type(page); + lru_cache_add(page); } else { /* * Put unevictable pages directly on zone's unevictable -- cgit v1.2.3 From dacbde0963d62a4962d5e8a5cc38dfd1f016124b Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Wed, 3 Jul 2013 15:02:35 -0700 Subject: mm/page_alloc.c: add additional checking and return value for the 'table->data' - check the length of the procfs data before copying it into a fixed size array. - when __parse_numa_zonelist_order() fails, save the error code for return. - 'char*' --> 'char *' coding style fix Signed-off-by: Chen Gang Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/page_alloc.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index fab9506273be..a662c74a0f5d 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -3256,18 +3256,25 @@ int numa_zonelist_order_handler(ctl_table *table, int write, static DEFINE_MUTEX(zl_order_mutex); mutex_lock(&zl_order_mutex); - if (write) - strcpy(saved_string, (char*)table->data); + if (write) { + if (strlen((char *)table->data) >= NUMA_ZONELIST_ORDER_LEN) { + ret = -EINVAL; + goto out; + } + strcpy(saved_string, (char *)table->data); + } ret = proc_dostring(table, write, buffer, length, ppos); if (ret) goto out; if (write) { int oldval = user_zonelist_order; - if (__parse_numa_zonelist_order((char*)table->data)) { + + ret = __parse_numa_zonelist_order((char *)table->data); + if (ret) { /* * bogus value. restore saved string */ - strncpy((char*)table->data, saved_string, + strncpy((char *)table->data, saved_string, NUMA_ZONELIST_ORDER_LEN); user_zonelist_order = oldval; } else if (oldval != user_zonelist_order) { -- cgit v1.2.3 From 9bde916bc73255dcee3d8aded990443675daa707 Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Wed, 3 Jul 2013 15:02:36 -0700 Subject: mm/nommu.c: add additional check for vread() just like vwrite() has done vwrite() checks for overflow. vread() should do the same thing. Since vwrite() checks the source buffer address, vread() should check the destination buffer address. Signed-off-by: Chen Gang Cc: Al Viro Cc: Michel Lespinasse Cc: Rik van Riel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/nommu.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mm/nommu.c b/mm/nommu.c index 298884dcd6e7..1898b2fe9da5 100644 --- a/mm/nommu.c +++ b/mm/nommu.c @@ -282,6 +282,10 @@ EXPORT_SYMBOL(vmalloc_to_pfn); long vread(char *buf, char *addr, unsigned long count) { + /* Don't allow overflow */ + if ((unsigned long) buf + count < count) + count = -(unsigned long) buf; + memcpy(buf, addr, count); return count; } -- cgit v1.2.3 From f15bdfa802bfa5eb6b4b5a241b97ec9fa1204a35 Mon Sep 17 00:00:00 2001 From: Naoya Horiguchi Date: Wed, 3 Jul 2013 15:02:37 -0700 Subject: mm/memory-failure.c: fix memory leak in successful soft offlining After a successful page migration by soft offlining, the source page is not properly freed and it's never reusable even if we unpoison it afterward. This is caused by the race between freeing page and setting PG_hwpoison. In successful soft offlining, the source page is put (and the refcount becomes 0) by putback_lru_page() in unmap_and_move(), where it's linked to pagevec and actual freeing back to buddy is delayed. So if PG_hwpoison is set for the page before freeing, the freeing does not functions as expected (in such case freeing aborts in free_pages_prepare() check.) This patch tries to make sure to free the source page before setting PG_hwpoison on it. To avoid reallocating, the page keeps MIGRATE_ISOLATE until after setting PG_hwpoison. This patch also removes obsolete comments about "keeping elevated refcount" because what they say is not true. Unlike memory_failure(), soft_offline_page() uses no special page isolation code, and the soft-offlined pages have no elevated. Signed-off-by: Naoya Horiguchi Cc: Andi Kleen Cc: Mel Gorman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/memory-failure.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/mm/memory-failure.c b/mm/memory-failure.c index ceb0c7f1932f..2c13aa7a0164 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -1410,7 +1410,8 @@ static int __get_any_page(struct page *p, unsigned long pfn, int flags) /* * Isolate the page, so that it doesn't get reallocated if it - * was free. + * was free. This flag should be kept set until the source page + * is freed and PG_hwpoison on it is set. */ set_migratetype_isolate(p, true); /* @@ -1433,7 +1434,6 @@ static int __get_any_page(struct page *p, unsigned long pfn, int flags) /* Not a free page */ ret = 1; } - unset_migratetype_isolate(p, MIGRATE_MOVABLE); unlock_memory_hotplug(); return ret; } @@ -1494,7 +1494,6 @@ static int soft_offline_huge_page(struct page *page, int flags) atomic_long_add(1 << compound_trans_order(hpage), &num_poisoned_pages); } - /* keep elevated page count for bad page */ return ret; } @@ -1559,7 +1558,7 @@ int soft_offline_page(struct page *page, int flags) atomic_long_inc(&num_poisoned_pages); } } - /* keep elevated page count for bad page */ + unset_migratetype_isolate(page, MIGRATE_MOVABLE); return ret; } @@ -1625,7 +1624,22 @@ static int __soft_offline_page(struct page *page, int flags) if (ret > 0) ret = -EIO; } else { + /* + * After page migration succeeds, the source page can + * be trapped in pagevec and actual freeing is delayed. + * Freeing code works differently based on PG_hwpoison, + * so there's a race. We need to make sure that the + * source page should be freed back to buddy before + * setting PG_hwpoison. + */ + if (!is_free_buddy_page(page)) + lru_add_drain_all(); + if (!is_free_buddy_page(page)) + drain_all_pages(); SetPageHWPoison(page); + if (!is_free_buddy_page(page)) + pr_info("soft offline: %#lx: page leaked\n", + pfn); atomic_long_inc(&num_poisoned_pages); } } else { -- cgit v1.2.3 From 4996eed867a7215958267252fafddc41d5f26140 Mon Sep 17 00:00:00 2001 From: Toshi Kani Date: Wed, 3 Jul 2013 15:02:39 -0700 Subject: mm/memory_hotplug.c: change normal message to use pr_debug During early boot-up, iomem_resource is set up from the boot descriptor table, such as EFI Memory Table and e820. Later, acpi_memory_device_add() calls add_memory() for each ACPI memory device object as it enumerates ACPI namespace. This add_memory() call is expected to fail in register_memory_resource() at boot since iomem_resource has been set up from EFI/e820. As a result, add_memory() returns -EEXIST, which acpi_memory_device_add() handles as the normal case. This scheme works fine, but the following error message is logged for every ACPI memory device object during boot-up. "System RAM resource %pR cannot be added\n" This patch changes register_memory_resource() to use pr_debug() for the message as it shows up under the normal case. Signed-off-by: Toshi Kani Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/memory_hotplug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index a66d0023d219..e3097f299f67 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -75,7 +75,7 @@ static struct resource *register_memory_resource(u64 start, u64 size) res->end = start + size - 1; res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; if (request_resource(&iomem_resource, res) < 0) { - printk("System RAM resource %pR cannot be added\n", res); + pr_debug("System RAM resource %pR cannot be added\n", res); kfree(res); res = NULL; } -- cgit v1.2.3 From cea27eb2a202959783f81254c48c250ddd80e129 Mon Sep 17 00:00:00 2001 From: Wanpeng Li Date: Wed, 3 Jul 2013 15:02:40 -0700 Subject: mm/memory-hotplug: fix lowmem count overflow when offline pages The logic for the memory-remove code fails to correctly account the Total High Memory when a memory block which contains High Memory is offlined as shown in the example below. The following patch fixes it. Before logic memory remove: MemTotal: 7603740 kB MemFree: 6329612 kB Buffers: 94352 kB Cached: 872008 kB SwapCached: 0 kB Active: 626932 kB Inactive: 519216 kB Active(anon): 180776 kB Inactive(anon): 222944 kB Active(file): 446156 kB Inactive(file): 296272 kB Unevictable: 0 kB Mlocked: 0 kB HighTotal: 7294672 kB HighFree: 5704696 kB LowTotal: 309068 kB LowFree: 624916 kB After logic memory remove: MemTotal: 7079452 kB MemFree: 5805976 kB Buffers: 94372 kB Cached: 872000 kB SwapCached: 0 kB Active: 626936 kB Inactive: 519236 kB Active(anon): 180780 kB Inactive(anon): 222944 kB Active(file): 446156 kB Inactive(file): 296292 kB Unevictable: 0 kB Mlocked: 0 kB HighTotal: 7294672 kB HighFree: 5181024 kB LowTotal: 4294752076 kB LowFree: 624952 kB [mhocko@suse.cz: fix CONFIG_HIGHMEM=n build] Signed-off-by: Wanpeng Li Reviewed-by: Michal Hocko Cc: KAMEZAWA Hiroyuki Cc: David Rientjes Cc: [2.6.24+] Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/page_alloc.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index a662c74a0f5d..d711dcdda362 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -6185,6 +6185,10 @@ __offline_isolated_pages(unsigned long start_pfn, unsigned long end_pfn) list_del(&page->lru); rmv_page_order(page); zone->free_area[order].nr_free--; +#ifdef CONFIG_HIGHMEM + if (PageHighMem(page)) + totalhigh_pages -= 1 << order; +#endif for (i = 0; i < (1 << order); i++) SetPageReserved((page+i)); pfn += (1 << order); -- cgit v1.2.3 From 4c42efa266030f32227f35fabbdd986fcf8f2ec4 Mon Sep 17 00:00:00 2001 From: Wanpeng Li Date: Wed, 3 Jul 2013 15:02:41 -0700 Subject: mm/pageblock: remove get/set_pageblock_flags get_pageblock_flags and set_pageblock_flags are not used any more, this patch removes them. Signed-off-by: Wanpeng Li Reviewed-by: Michal Hocko Cc: KAMEZAWA Hiroyuki Cc: David Rientjes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/pageblock-flags.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/include/linux/pageblock-flags.h b/include/linux/pageblock-flags.h index be655e4a2a75..2ee8cd2466b5 100644 --- a/include/linux/pageblock-flags.h +++ b/include/linux/pageblock-flags.h @@ -80,10 +80,4 @@ void set_pageblock_flags_group(struct page *page, unsigned long flags, PB_migrate_skip) #endif /* CONFIG_COMPACTION */ -#define get_pageblock_flags(page) \ - get_pageblock_flags_group(page, 0, PB_migrate_end) -#define set_pageblock_flags(page, flags) \ - set_pageblock_flags_group(page, flags, \ - 0, PB_migrate_end) - #endif /* PAGEBLOCK_FLAGS_H */ -- cgit v1.2.3 From 5f1e31d2f5977d910d0b2f5018173e99241d1940 Mon Sep 17 00:00:00 2001 From: Wanpeng Li Date: Wed, 3 Jul 2013 15:02:42 -0700 Subject: mm/hugetlb: remove hugetlb_prefault hugetlb_prefault() is not used any more, this patch removes it. Signed-off-by: Wanpeng Li Reviewed-by: Michal Hocko Cc: KAMEZAWA Hiroyuki Cc: David Rientjes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/hugetlb.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index 89d4fbf681e7..c2b1801a160b 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -55,7 +55,6 @@ void __unmap_hugepage_range_final(struct mmu_gather *tlb, void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma, unsigned long start, unsigned long end, struct page *ref_page); -int hugetlb_prefault(struct address_space *, struct vm_area_struct *); void hugetlb_report_meminfo(struct seq_file *); int hugetlb_report_node_meminfo(int, char *); void hugetlb_show_meminfo(void); @@ -114,7 +113,6 @@ static inline unsigned long hugetlb_total_pages(void) #define follow_hugetlb_page(m,v,p,vs,a,b,i,w) ({ BUG(); 0; }) #define follow_huge_addr(mm, addr, write) ERR_PTR(-EINVAL) #define copy_hugetlb_page_range(src, dst, vma) ({ BUG(); 0; }) -#define hugetlb_prefault(mapping, vma) ({ BUG(); 0; }) static inline void hugetlb_report_meminfo(struct seq_file *m) { } -- cgit v1.2.3 From 2415cf12e04d415b16d9c2f2a705bcd6cd9a0474 Mon Sep 17 00:00:00 2001 From: Wanpeng Li Date: Wed, 3 Jul 2013 15:02:43 -0700 Subject: mm/hugetlb: use already existing interface huge_page_shift Use the already existing interface huge_page_shift instead of h->order + PAGE_SHIFT. Signed-off-by: Wanpeng Li Cc: KAMEZAWA Hiroyuki Cc: David Rientjes Cc: Benjamin Herrenschmidt Reviewed-by: Michal Hocko Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/powerpc/mm/hugetlbpage.c | 2 +- mm/hugetlb.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/mm/hugetlbpage.c b/arch/powerpc/mm/hugetlbpage.c index 77fdd2cef33b..4210549ac95e 100644 --- a/arch/powerpc/mm/hugetlbpage.c +++ b/arch/powerpc/mm/hugetlbpage.c @@ -357,7 +357,7 @@ void add_gpage(u64 addr, u64 page_size, unsigned long number_of_pages) int alloc_bootmem_huge_page(struct hstate *hstate) { struct huge_bootmem_page *m; - int idx = shift_to_mmu_psize(hstate->order + PAGE_SHIFT); + int idx = shift_to_mmu_psize(huge_page_shift(hstate)); int nr_gpages = gpage_freearray[idx].nr_gpages; if (nr_gpages == 0) diff --git a/mm/hugetlb.c b/mm/hugetlb.c index aed085ad11a8..fe095158859e 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -319,7 +319,7 @@ unsigned long vma_kernel_pagesize(struct vm_area_struct *vma) hstate = hstate_vma(vma); - return 1UL << (hstate->order + PAGE_SHIFT); + return 1UL << huge_page_shift(hstate); } EXPORT_SYMBOL_GPL(vma_kernel_pagesize); -- cgit v1.2.3 From 917d9290af749fac9c4d90bacf18699c9d8ba28d Mon Sep 17 00:00:00 2001 From: Tim Chen Date: Wed, 3 Jul 2013 15:02:44 -0700 Subject: mm: tune vm_committed_as percpu_counter batching size Currently the per cpu counter's batch size for memory accounting is configured as twice the number of cpus in the system. However, for system with very large memory, it is more appropriate to make it proportional to the memory size per cpu in the system. For example, for a x86_64 system with 64 cpus and 128 GB of memory, the batch size is only 2*64 pages (0.5 MB). So any memory accounting changes of more than 0.5MB will overflow the per cpu counter into the global counter. Instead, for the new scheme, the batch size is configured to be 0.4% of the memory/cpu = 8MB (128 GB/64 /256), which is more inline with the memory size. I've done a repeated brk test of 800KB (from will-it-scale test suite) with 80 concurrent processes on a 4 socket Westmere machine with a total of 40 cores. Without the patch, about 80% of cpu is spent on spin-lock contention within the vm_committed_as counter. With the patch, there's a 73x speedup on the benchmark and the lock contention drops off almost entirely. [akpm@linux-foundation.org: fix section mismatch] Signed-off-by: Tim Chen Cc: Tejun Heo Cc: Eric Dumazet Cc: Dave Hansen Cc: Andi Kleen Cc: Wu Fengguang Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mman.h | 8 +++++++- mm/mm_init.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/include/linux/mman.h b/include/linux/mman.h index 9aa863da287f..92dc257251e4 100644 --- a/include/linux/mman.h +++ b/include/linux/mman.h @@ -11,11 +11,17 @@ extern int sysctl_overcommit_memory; extern int sysctl_overcommit_ratio; extern struct percpu_counter vm_committed_as; +#ifdef CONFIG_SMP +extern s32 vm_committed_as_batch; +#else +#define vm_committed_as_batch 0 +#endif + unsigned long vm_memory_committed(void); static inline void vm_acct_memory(long pages) { - percpu_counter_add(&vm_committed_as, pages); + __percpu_counter_add(&vm_committed_as, pages, vm_committed_as_batch); } static inline void vm_unacct_memory(long pages) diff --git a/mm/mm_init.c b/mm/mm_init.c index c280a02ea11e..633c08863fd8 100644 --- a/mm/mm_init.c +++ b/mm/mm_init.c @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include "internal.h" #ifdef CONFIG_DEBUG_MEMORY_INIT @@ -147,6 +149,51 @@ early_param("mminit_loglevel", set_mminit_loglevel); struct kobject *mm_kobj; EXPORT_SYMBOL_GPL(mm_kobj); +#ifdef CONFIG_SMP +s32 vm_committed_as_batch = 32; + +static void __meminit mm_compute_batch(void) +{ + u64 memsized_batch; + s32 nr = num_present_cpus(); + s32 batch = max_t(s32, nr*2, 32); + + /* batch size set to 0.4% of (total memory/#cpus), or max int32 */ + memsized_batch = min_t(u64, (totalram_pages/nr)/256, 0x7fffffff); + + vm_committed_as_batch = max_t(s32, memsized_batch, batch); +} + +static int __meminit mm_compute_batch_notifier(struct notifier_block *self, + unsigned long action, void *arg) +{ + switch (action) { + case MEM_ONLINE: + case MEM_OFFLINE: + mm_compute_batch(); + default: + break; + } + return NOTIFY_OK; +} + +static struct notifier_block compute_batch_nb __meminitdata = { + .notifier_call = mm_compute_batch_notifier, + .priority = IPC_CALLBACK_PRI, /* use lowest priority */ +}; + +static int __init mm_compute_batch_init(void) +{ + mm_compute_batch(); + register_hotmemory_notifier(&compute_batch_nb); + + return 0; +} + +__initcall(mm_compute_batch_init); + +#endif + static int __init mm_sysfs_init(void) { mm_kobj = kobject_create_and_add("mm", kernel_kobj); -- cgit v1.2.3 From dcf6b7ddd7df8965727746f89c59229b23180e5a Mon Sep 17 00:00:00 2001 From: Rafael Aquini Date: Wed, 3 Jul 2013 15:02:46 -0700 Subject: swap: discard while swapping only if SWAP_FLAG_DISCARD_PAGES Considering the use cases where the swap device supports discard: a) and can do it quickly; b) but it's slow to do in small granularities (or concurrent with other I/O); c) but the implementation is so horrendous that you don't even want to send one down; And assuming that the sysadmin considers it useful to send the discards down at all, we would (probably) want the following solutions: i. do the fine-grained discards for freed swap pages, if device is capable of doing so optimally; ii. do single-time (batched) swap area discards, either at swapon or via something like fstrim (not implemented yet); iii. allow doing both single-time and fine-grained discards; or iv. turn it off completely (default behavior) As implemented today, one can only enable/disable discards for swap, but one cannot select, for instance, solution (ii) on a swap device like (b) even though the single-time discard is regarded to be interesting, or necessary to the workload because it would imply (1), and the device is not capable of performing it optimally. This patch addresses the scenario depicted above by introducing a way to ensure the (probably) wanted solutions (i, ii, iii and iv) can be flexibly flagged through swapon(8) to allow a sysadmin to select the best suitable swap discard policy accordingly to system constraints. This patch introduces SWAP_FLAG_DISCARD_PAGES and SWAP_FLAG_DISCARD_ONCE new flags to allow more flexibe swap discard policies being flagged through swapon(8). The default behavior is to keep both single-time, or batched, area discards (SWAP_FLAG_DISCARD_ONCE) and fine-grained discards for page-clusters (SWAP_FLAG_DISCARD_PAGES) enabled, in order to keep consistentcy with older kernel behavior, as well as maintain compatibility with older swapon(8). However, through the new introduced flags the best suitable discard policy can be selected accordingly to any given swap device constraint. [akpm@linux-foundation.org: tweak comments] Signed-off-by: Rafael Aquini Acked-by: KOSAKI Motohiro Cc: Hugh Dickins Cc: Shaohua Li Cc: Karel Zak Cc: Jeff Moyer Cc: Rik van Riel Cc: Larry Woodman Cc: Mel Gorman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/swap.h | 13 +++++++++---- mm/swapfile.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 59 insertions(+), 9 deletions(-) diff --git a/include/linux/swap.h b/include/linux/swap.h index 85d74373002c..d95cde5e257d 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -20,10 +20,13 @@ struct bio; #define SWAP_FLAG_PREFER 0x8000 /* set if swap priority specified */ #define SWAP_FLAG_PRIO_MASK 0x7fff #define SWAP_FLAG_PRIO_SHIFT 0 -#define SWAP_FLAG_DISCARD 0x10000 /* discard swap cluster after use */ +#define SWAP_FLAG_DISCARD 0x10000 /* enable discard for swap */ +#define SWAP_FLAG_DISCARD_ONCE 0x20000 /* discard swap area at swapon-time */ +#define SWAP_FLAG_DISCARD_PAGES 0x40000 /* discard page-clusters after use */ #define SWAP_FLAGS_VALID (SWAP_FLAG_PRIO_MASK | SWAP_FLAG_PREFER | \ - SWAP_FLAG_DISCARD) + SWAP_FLAG_DISCARD | SWAP_FLAG_DISCARD_ONCE | \ + SWAP_FLAG_DISCARD_PAGES) static inline int current_is_kswapd(void) { @@ -147,14 +150,16 @@ struct swap_extent { enum { SWP_USED = (1 << 0), /* is slot in swap_info[] used? */ SWP_WRITEOK = (1 << 1), /* ok to write to this swap? */ - SWP_DISCARDABLE = (1 << 2), /* swapon+blkdev support discard */ + SWP_DISCARDABLE = (1 << 2), /* blkdev support discard */ SWP_DISCARDING = (1 << 3), /* now discarding a free cluster */ SWP_SOLIDSTATE = (1 << 4), /* blkdev seeks are cheap */ SWP_CONTINUED = (1 << 5), /* swap_map has count continuation */ SWP_BLKDEV = (1 << 6), /* its a block device */ SWP_FILE = (1 << 7), /* set after swap_activate success */ + SWP_AREA_DISCARD = (1 << 8), /* single-time swap area discards */ + SWP_PAGE_DISCARD = (1 << 9), /* freed swap page-cluster discards */ /* add others here before... */ - SWP_SCANNING = (1 << 8), /* refcount in scan_swap_map */ + SWP_SCANNING = (1 << 10), /* refcount in scan_swap_map */ }; #define SWAP_CLUSTER_MAX 32UL diff --git a/mm/swapfile.c b/mm/swapfile.c index 746af55b8455..36af6eeaa67e 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -212,7 +212,7 @@ static unsigned long scan_swap_map(struct swap_info_struct *si, si->cluster_nr = SWAPFILE_CLUSTER - 1; goto checks; } - if (si->flags & SWP_DISCARDABLE) { + if (si->flags & SWP_PAGE_DISCARD) { /* * Start range check on racing allocations, in case * they overlap the cluster we eventually decide on @@ -322,7 +322,7 @@ checks: if (si->lowest_alloc) { /* - * Only set when SWP_DISCARDABLE, and there's a scan + * Only set when SWP_PAGE_DISCARD, and there's a scan * for a free cluster in progress or just completed. */ if (found_free_cluster) { @@ -2016,6 +2016,20 @@ static int setup_swap_map_and_extents(struct swap_info_struct *p, return nr_extents; } +/* + * Helper to sys_swapon determining if a given swap + * backing device queue supports DISCARD operations. + */ +static bool swap_discardable(struct swap_info_struct *si) +{ + struct request_queue *q = bdev_get_queue(si->bdev); + + if (!q || !blk_queue_discard(q)) + return false; + + return true; +} + SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags) { struct swap_info_struct *p; @@ -2123,8 +2137,37 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags) p->flags |= SWP_SOLIDSTATE; p->cluster_next = 1 + (prandom_u32() % p->highest_bit); } - if ((swap_flags & SWAP_FLAG_DISCARD) && discard_swap(p) == 0) - p->flags |= SWP_DISCARDABLE; + + if ((swap_flags & SWAP_FLAG_DISCARD) && swap_discardable(p)) { + /* + * When discard is enabled for swap with no particular + * policy flagged, we set all swap discard flags here in + * order to sustain backward compatibility with older + * swapon(8) releases. + */ + p->flags |= (SWP_DISCARDABLE | SWP_AREA_DISCARD | + SWP_PAGE_DISCARD); + + /* + * By flagging sys_swapon, a sysadmin can tell us to + * either do single-time area discards only, or to just + * perform discards for released swap page-clusters. + * Now it's time to adjust the p->flags accordingly. + */ + if (swap_flags & SWAP_FLAG_DISCARD_ONCE) + p->flags &= ~SWP_PAGE_DISCARD; + else if (swap_flags & SWAP_FLAG_DISCARD_PAGES) + p->flags &= ~SWP_AREA_DISCARD; + + /* issue a swapon-time discard if it's still required */ + if (p->flags & SWP_AREA_DISCARD) { + int err = discard_swap(p); + if (unlikely(err)) + printk(KERN_ERR + "swapon: discard_swap(%p): %d\n", + p, err); + } + } } mutex_lock(&swapon_mutex); @@ -2135,11 +2178,13 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags) enable_swap_info(p, prio, swap_map, frontswap_map); printk(KERN_INFO "Adding %uk swap on %s. " - "Priority:%d extents:%d across:%lluk %s%s%s\n", + "Priority:%d extents:%d across:%lluk %s%s%s%s%s\n", p->pages<<(PAGE_SHIFT-10), name->name, p->prio, nr_extents, (unsigned long long)span<<(PAGE_SHIFT-10), (p->flags & SWP_SOLIDSTATE) ? "SS" : "", (p->flags & SWP_DISCARDABLE) ? "D" : "", + (p->flags & SWP_AREA_DISCARD) ? "s" : "", + (p->flags & SWP_PAGE_DISCARD) ? "c" : "", (frontswap_map) ? "FS" : ""); mutex_unlock(&swapon_mutex); -- cgit v1.2.3 From 11199692d83dd3fe1511203024fb9853d176ec4c Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:02:48 -0700 Subject: mm: change signature of free_reserved_area() to fix building warnings Change signature of free_reserved_area() according to Russell King's suggestion to fix following build warnings: arch/arm/mm/init.c: In function 'mem_init': arch/arm/mm/init.c:603:2: warning: passing argument 1 of 'free_reserved_area' makes integer from pointer without a cast [enabled by default] free_reserved_area(__va(PHYS_PFN_OFFSET), swapper_pg_dir, 0, NULL); ^ In file included from include/linux/mman.h:4:0, from arch/arm/mm/init.c:15: include/linux/mm.h:1301:22: note: expected 'long unsigned int' but argument is of type 'void *' extern unsigned long free_reserved_area(unsigned long start, unsigned long end, mm/page_alloc.c: In function 'free_reserved_area': >> mm/page_alloc.c:5134:3: warning: passing argument 1 of 'virt_to_phys' makes pointer from integer without a cast [enabled by default] In file included from arch/mips/include/asm/page.h:49:0, from include/linux/mmzone.h:20, from include/linux/gfp.h:4, from include/linux/mm.h:8, from mm/page_alloc.c:18: arch/mips/include/asm/io.h:119:29: note: expected 'const volatile void *' but argument is of type 'long unsigned int' mm/page_alloc.c: In function 'free_area_init_nodes': mm/page_alloc.c:5030:34: warning: array subscript is below array bounds [-Warray-bounds] Also address some minor code review comments. Signed-off-by: Jiang Liu Reported-by: Arnd Bergmann Cc: "H. Peter Anvin" Cc: "Michael S. Tsirkin" Cc: Cc: Catalin Marinas Cc: Chris Metcalf Cc: David Howells Cc: Geert Uytterhoeven Cc: Ingo Molnar Cc: Jeremy Fitzhardinge Cc: Jianguo Wu Cc: Joonsoo Kim Cc: Kamezawa Hiroyuki Cc: Konrad Rzeszutek Wilk Cc: Marek Szyprowski Cc: Mel Gorman Cc: Michel Lespinasse Cc: Minchan Kim Cc: Rik van Riel Cc: Rusty Russell Cc: Tang Chen Cc: Tejun Heo Cc: Thomas Gleixner Cc: Wen Congyang Cc: Will Deacon Cc: Yasuaki Ishimatsu Cc: Yinghai Lu Cc: Russell King Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/alpha/kernel/sys_nautilus.c | 4 ++-- arch/alpha/mm/init.c | 2 +- arch/arc/mm/init.c | 2 +- arch/arm/mm/init.c | 2 +- arch/arm64/mm/init.c | 2 +- arch/avr32/mm/init.c | 2 +- arch/blackfin/mm/init.c | 2 +- arch/c6x/mm/init.c | 2 +- arch/frv/mm/init.c | 2 +- arch/h8300/mm/init.c | 2 +- arch/ia64/mm/init.c | 3 +-- arch/m32r/mm/init.c | 2 +- arch/m68k/mm/init.c | 2 +- arch/metag/mm/init.c | 3 ++- arch/microblaze/mm/init.c | 2 +- arch/mips/mm/init.c | 3 ++- arch/mn10300/mm/init.c | 3 ++- arch/openrisc/mm/init.c | 2 +- arch/parisc/mm/init.c | 3 ++- arch/powerpc/kernel/kvm.c | 2 +- arch/powerpc/mm/mem.c | 2 +- arch/s390/mm/init.c | 3 ++- arch/score/mm/init.c | 3 ++- arch/sh/mm/init.c | 2 +- arch/sparc/mm/init_32.c | 4 ++-- arch/sparc/mm/init_64.c | 4 ++-- arch/um/kernel/mem.c | 2 +- arch/unicore32/mm/init.c | 2 +- arch/xtensa/mm/init.c | 2 +- include/linux/mm.h | 5 ++--- mm/page_alloc.c | 19 ++++++++++--------- 31 files changed, 50 insertions(+), 45 deletions(-) diff --git a/arch/alpha/kernel/sys_nautilus.c b/arch/alpha/kernel/sys_nautilus.c index 1d4aabfcf9a1..891bd274ccb5 100644 --- a/arch/alpha/kernel/sys_nautilus.c +++ b/arch/alpha/kernel/sys_nautilus.c @@ -238,8 +238,8 @@ nautilus_init_pci(void) if (pci_mem < memtop) memtop = pci_mem; if (memtop > alpha_mv.min_mem_address) { - free_reserved_area((unsigned long)__va(alpha_mv.min_mem_address), - (unsigned long)__va(memtop), 0, NULL); + free_reserved_area(__va(alpha_mv.min_mem_address), + __va(memtop), 0, NULL); printk("nautilus_init_pci: %ldk freed\n", (memtop - alpha_mv.min_mem_address) >> 10); } diff --git a/arch/alpha/mm/init.c b/arch/alpha/mm/init.c index 0ba85ee4a466..d54848d4e464 100644 --- a/arch/alpha/mm/init.c +++ b/arch/alpha/mm/init.c @@ -326,6 +326,6 @@ free_initmem(void) void free_initrd_mem(unsigned long start, unsigned long end) { - free_reserved_area(start, end, 0, "initrd"); + free_reserved_area((void *)start, (void *)end, 0, "initrd"); } #endif diff --git a/arch/arc/mm/init.c b/arch/arc/mm/init.c index 4a177365b2c4..dce02e4716a1 100644 --- a/arch/arc/mm/init.c +++ b/arch/arc/mm/init.c @@ -152,7 +152,7 @@ void __init_refok free_initmem(void) #ifdef CONFIG_BLK_DEV_INITRD void __init free_initrd_mem(unsigned long start, unsigned long end) { - free_reserved_area(start, end, 0, "initrd"); + free_reserved_area((void *)start, (void *)end, 0, "initrd"); } #endif diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 2ffee02d1d5c..7fae391caf86 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -745,7 +745,7 @@ void free_initrd_mem(unsigned long start, unsigned long end) { if (!keep_initrd) { poison_init_mem((void *)start, PAGE_ALIGN(end) - start); - free_reserved_area(start, end, 0, "initrd"); + free_reserved_area((void *)start, (void *)end, 0, "initrd"); } } diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c index f497ca77925a..6041e4008a83 100644 --- a/arch/arm64/mm/init.c +++ b/arch/arm64/mm/init.c @@ -398,7 +398,7 @@ void free_initrd_mem(unsigned long start, unsigned long end) { if (!keep_initrd) { poison_init_mem((void *)start, PAGE_ALIGN(end) - start); - free_reserved_area(start, end, 0, "initrd"); + free_reserved_area((void *)start, (void *)end, 0, "initrd"); } } diff --git a/arch/avr32/mm/init.c b/arch/avr32/mm/init.c index e66e8406f992..5a79fa08cb3c 100644 --- a/arch/avr32/mm/init.c +++ b/arch/avr32/mm/init.c @@ -154,6 +154,6 @@ void free_initmem(void) #ifdef CONFIG_BLK_DEV_INITRD void free_initrd_mem(unsigned long start, unsigned long end) { - free_reserved_area(start, end, 0, "initrd"); + free_reserved_area((void *)start, (void *)end, 0, "initrd"); } #endif diff --git a/arch/blackfin/mm/init.c b/arch/blackfin/mm/init.c index 82d01a71207f..8e9eab272811 100644 --- a/arch/blackfin/mm/init.c +++ b/arch/blackfin/mm/init.c @@ -133,7 +133,7 @@ void __init mem_init(void) void __init free_initrd_mem(unsigned long start, unsigned long end) { #ifndef CONFIG_MPU - free_reserved_area(start, end, 0, "initrd"); + free_reserved_area((void *)start, (void *)end, 0, "initrd"); #endif } #endif diff --git a/arch/c6x/mm/init.c b/arch/c6x/mm/init.c index b74ccb5a7690..07bfcc98a3b7 100644 --- a/arch/c6x/mm/init.c +++ b/arch/c6x/mm/init.c @@ -78,7 +78,7 @@ void __init mem_init(void) #ifdef CONFIG_BLK_DEV_INITRD void __init free_initrd_mem(unsigned long start, unsigned long end) { - free_reserved_area(start, end, 0, "initrd"); + free_reserved_area((void *)start, (void *)end, 0, "initrd"); } #endif diff --git a/arch/frv/mm/init.c b/arch/frv/mm/init.c index dee354fa6b64..a67f3a5897b8 100644 --- a/arch/frv/mm/init.c +++ b/arch/frv/mm/init.c @@ -173,6 +173,6 @@ void free_initmem(void) #ifdef CONFIG_BLK_DEV_INITRD void __init free_initrd_mem(unsigned long start, unsigned long end) { - free_reserved_area(start, end, 0, "initrd"); + free_reserved_area((void *)start, (void *)end, 0, "initrd"); } /* end free_initrd_mem() */ #endif diff --git a/arch/h8300/mm/init.c b/arch/h8300/mm/init.c index ff349d70a29b..57e03c59861d 100644 --- a/arch/h8300/mm/init.c +++ b/arch/h8300/mm/init.c @@ -161,7 +161,7 @@ void __init mem_init(void) #ifdef CONFIG_BLK_DEV_INITRD void free_initrd_mem(unsigned long start, unsigned long end) { - free_reserved_area(start, end, 0, "initrd"); + free_reserved_area((void *)start, (void *)end, 0, "initrd"); } #endif diff --git a/arch/ia64/mm/init.c b/arch/ia64/mm/init.c index d1fe4b402601..da568c2e839f 100644 --- a/arch/ia64/mm/init.c +++ b/arch/ia64/mm/init.c @@ -154,8 +154,7 @@ ia64_init_addr_space (void) void free_initmem (void) { - free_reserved_area((unsigned long)ia64_imva(__init_begin), - (unsigned long)ia64_imva(__init_end), + free_reserved_area(ia64_imva(__init_begin), ia64_imva(__init_end), 0, "unused kernel"); } diff --git a/arch/m32r/mm/init.c b/arch/m32r/mm/init.c index ab4cbce91a9b..d80412d0c14e 100644 --- a/arch/m32r/mm/init.c +++ b/arch/m32r/mm/init.c @@ -191,6 +191,6 @@ void free_initmem(void) *======================================================================*/ void free_initrd_mem(unsigned long start, unsigned long end) { - free_reserved_area(start, end, 0, "initrd"); + free_reserved_area((void *)start, (void *)end, 0, "initrd"); } #endif diff --git a/arch/m68k/mm/init.c b/arch/m68k/mm/init.c index 1af2ca3411f6..95de725534e9 100644 --- a/arch/m68k/mm/init.c +++ b/arch/m68k/mm/init.c @@ -202,6 +202,6 @@ void __init mem_init(void) #ifdef CONFIG_BLK_DEV_INITRD void free_initrd_mem(unsigned long start, unsigned long end) { - free_reserved_area(start, end, 0, "initrd"); + free_reserved_area((void *)start, (void *)end, 0, "initrd"); } #endif diff --git a/arch/metag/mm/init.c b/arch/metag/mm/init.c index d05b8455c44c..5e2238dd72e0 100644 --- a/arch/metag/mm/init.c +++ b/arch/metag/mm/init.c @@ -414,7 +414,8 @@ void free_initmem(void) #ifdef CONFIG_BLK_DEV_INITRD void free_initrd_mem(unsigned long start, unsigned long end) { - free_reserved_area(start, end, POISON_FREE_INITMEM, "initrd"); + free_reserved_area((void *)start, (void *)end, POISON_FREE_INITMEM, + "initrd"); } #endif diff --git a/arch/microblaze/mm/init.c b/arch/microblaze/mm/init.c index b38ae3acfeb4..d7b8ada9345f 100644 --- a/arch/microblaze/mm/init.c +++ b/arch/microblaze/mm/init.c @@ -235,7 +235,7 @@ void __init setup_memory(void) #ifdef CONFIG_BLK_DEV_INITRD void free_initrd_mem(unsigned long start, unsigned long end) { - free_reserved_area(start, end, 0, "initrd"); + free_reserved_area((void *)start, (void *)end, 0, "initrd"); } #endif diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c index 9b973e0af9cb..268f2a94031b 100644 --- a/arch/mips/mm/init.c +++ b/arch/mips/mm/init.c @@ -440,7 +440,8 @@ void free_init_pages(const char *what, unsigned long begin, unsigned long end) #ifdef CONFIG_BLK_DEV_INITRD void free_initrd_mem(unsigned long start, unsigned long end) { - free_reserved_area(start, end, POISON_FREE_INITMEM, "initrd"); + free_reserved_area((void *)start, (void *)end, POISON_FREE_INITMEM, + "initrd"); } #endif diff --git a/arch/mn10300/mm/init.c b/arch/mn10300/mm/init.c index 5a8ace63a6b4..e19049d1f2b9 100644 --- a/arch/mn10300/mm/init.c +++ b/arch/mn10300/mm/init.c @@ -152,6 +152,7 @@ void free_initmem(void) #ifdef CONFIG_BLK_DEV_INITRD void free_initrd_mem(unsigned long start, unsigned long end) { - free_reserved_area(start, end, POISON_FREE_INITMEM, "initrd"); + free_reserved_area((void *)start, (void *)end, POISON_FREE_INITMEM, + "initrd"); } #endif diff --git a/arch/openrisc/mm/init.c b/arch/openrisc/mm/init.c index b3cbc6703837..ab113325dc4c 100644 --- a/arch/openrisc/mm/init.c +++ b/arch/openrisc/mm/init.c @@ -261,7 +261,7 @@ void __init mem_init(void) #ifdef CONFIG_BLK_DEV_INITRD void free_initrd_mem(unsigned long start, unsigned long end) { - free_reserved_area(start, end, 0, "initrd"); + free_reserved_area((void *)start, (void *)end, 0, "initrd"); } #endif diff --git a/arch/parisc/mm/init.c b/arch/parisc/mm/init.c index 505b56c6b9b9..3223d5e4a372 100644 --- a/arch/parisc/mm/init.c +++ b/arch/parisc/mm/init.c @@ -1101,6 +1101,7 @@ void flush_tlb_all(void) #ifdef CONFIG_BLK_DEV_INITRD void free_initrd_mem(unsigned long start, unsigned long end) { - num_physpages += free_reserved_area(start, end, 0, "initrd"); + num_physpages += free_reserved_area((void *)start, (void *)end, 0, + "initrd"); } #endif diff --git a/arch/powerpc/kernel/kvm.c b/arch/powerpc/kernel/kvm.c index 6782221d49bd..5e4830a33c08 100644 --- a/arch/powerpc/kernel/kvm.c +++ b/arch/powerpc/kernel/kvm.c @@ -756,7 +756,7 @@ static __init void kvm_free_tmp(void) end = (ulong)&kvm_tmp[ARRAY_SIZE(kvm_tmp)] & PAGE_MASK; /* Free the tmp space we don't need */ - free_reserved_area(start, end, 0, NULL); + free_reserved_area((void *)start, (void *)end, 0, NULL); } static int __init kvm_guest_init(void) diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c index 0988a26e0413..347c5b1bbd62 100644 --- a/arch/powerpc/mm/mem.c +++ b/arch/powerpc/mm/mem.c @@ -407,7 +407,7 @@ void free_initmem(void) #ifdef CONFIG_BLK_DEV_INITRD void __init free_initrd_mem(unsigned long start, unsigned long end) { - free_reserved_area(start, end, 0, "initrd"); + free_reserved_area((void *)start, (void *)end, 0, "initrd"); } #endif diff --git a/arch/s390/mm/init.c b/arch/s390/mm/init.c index 89ebae4008f2..0878c89fe7d2 100644 --- a/arch/s390/mm/init.c +++ b/arch/s390/mm/init.c @@ -172,7 +172,8 @@ void free_initmem(void) #ifdef CONFIG_BLK_DEV_INITRD void __init free_initrd_mem(unsigned long start, unsigned long end) { - free_reserved_area(start, end, POISON_FREE_INITMEM, "initrd"); + free_reserved_area((void *)start, (void *)end, POISON_FREE_INITMEM, + "initrd"); } #endif diff --git a/arch/score/mm/init.c b/arch/score/mm/init.c index 0940682ab38b..f5dd61eb4544 100644 --- a/arch/score/mm/init.c +++ b/arch/score/mm/init.c @@ -108,7 +108,8 @@ void __init mem_init(void) #ifdef CONFIG_BLK_DEV_INITRD void free_initrd_mem(unsigned long start, unsigned long end) { - free_reserved_area(start, end, POISON_FREE_INITMEM, "initrd"); + free_reserved_area((void *)start, (void *)end, POISON_FREE_INITMEM, + "initrd"); } #endif diff --git a/arch/sh/mm/init.c b/arch/sh/mm/init.c index 20f9ead650d3..b892a9b7d7e3 100644 --- a/arch/sh/mm/init.c +++ b/arch/sh/mm/init.c @@ -505,7 +505,7 @@ void free_initmem(void) #ifdef CONFIG_BLK_DEV_INITRD void free_initrd_mem(unsigned long start, unsigned long end) { - free_reserved_area(start, end, 0, "initrd"); + free_reserved_area((void *)start, (void *)end, 0, "initrd"); } #endif diff --git a/arch/sparc/mm/init_32.c b/arch/sparc/mm/init_32.c index af472cf7c69a..d5f9c023826f 100644 --- a/arch/sparc/mm/init_32.c +++ b/arch/sparc/mm/init_32.c @@ -372,8 +372,8 @@ void free_initmem (void) #ifdef CONFIG_BLK_DEV_INITRD void free_initrd_mem(unsigned long start, unsigned long end) { - num_physpages += free_reserved_area(start, end, POISON_FREE_INITMEM, - "initrd"); + num_physpages += free_reserved_area((void *)start, (void *)end, + POISON_FREE_INITMEM, "initrd"); } #endif diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c index 04fd55a6e461..8269deb84eda 100644 --- a/arch/sparc/mm/init_64.c +++ b/arch/sparc/mm/init_64.c @@ -2131,8 +2131,8 @@ void free_initmem(void) #ifdef CONFIG_BLK_DEV_INITRD void free_initrd_mem(unsigned long start, unsigned long end) { - num_physpages += free_reserved_area(start, end, POISON_FREE_INITMEM, - "initrd"); + num_physpages += free_reserved_area((void *)start, (void *)end, + POISON_FREE_INITMEM, "initrd"); } #endif diff --git a/arch/um/kernel/mem.c b/arch/um/kernel/mem.c index 9df292b270a8..2aa7a2448d58 100644 --- a/arch/um/kernel/mem.c +++ b/arch/um/kernel/mem.c @@ -244,7 +244,7 @@ void free_initmem(void) #ifdef CONFIG_BLK_DEV_INITRD void free_initrd_mem(unsigned long start, unsigned long end) { - free_reserved_area(start, end, 0, "initrd"); + free_reserved_area((void *)start, (void *)end, 0, "initrd"); } #endif diff --git a/arch/unicore32/mm/init.c b/arch/unicore32/mm/init.c index 63df12d71ce3..220755cc9700 100644 --- a/arch/unicore32/mm/init.c +++ b/arch/unicore32/mm/init.c @@ -486,7 +486,7 @@ static int keep_initrd; void free_initrd_mem(unsigned long start, unsigned long end) { if (!keep_initrd) - free_reserved_area(start, end, 0, "initrd"); + free_reserved_area((void *)start, (void *)end, 0, "initrd"); } static int __init keepinitrd_setup(char *__unused) diff --git a/arch/xtensa/mm/init.c b/arch/xtensa/mm/init.c index bba125b4bb06..4d658efc3289 100644 --- a/arch/xtensa/mm/init.c +++ b/arch/xtensa/mm/init.c @@ -214,7 +214,7 @@ extern int initrd_is_mapped; void free_initrd_mem(unsigned long start, unsigned long end) { if (initrd_is_mapped) - free_reserved_area(start, end, 0, "initrd"); + free_reserved_area((void *)start, (void *)end, 0, "initrd"); } #endif diff --git a/include/linux/mm.h b/include/linux/mm.h index 949bd7035895..be1b96ce0650 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1311,7 +1311,7 @@ extern void free_initmem(void); * "poison" if it's non-zero. * Return pages freed into the buddy system. */ -extern unsigned long free_reserved_area(unsigned long start, unsigned long end, +extern unsigned long free_reserved_area(void *start, void *end, int poison, char *s); #ifdef CONFIG_HIGHMEM /* @@ -1355,8 +1355,7 @@ static inline unsigned long free_initmem_default(int poison) { extern char __init_begin[], __init_end[]; - return free_reserved_area(PAGE_ALIGN((unsigned long)&__init_begin) , - ((unsigned long)&__init_end) & PAGE_MASK, + return free_reserved_area(&__init_begin, &__init_end, poison, "unused kernel"); } diff --git a/mm/page_alloc.c b/mm/page_alloc.c index d711dcdda362..be18ccd017bb 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -5206,25 +5206,26 @@ early_param("movablecore", cmdline_parse_movablecore); #endif /* CONFIG_HAVE_MEMBLOCK_NODE_MAP */ -unsigned long free_reserved_area(unsigned long start, unsigned long end, - int poison, char *s) +unsigned long free_reserved_area(void *start, void *end, int poison, char *s) { - unsigned long pages, pos; + void *pos; + unsigned long pages = 0; - pos = start = PAGE_ALIGN(start); - end &= PAGE_MASK; - for (pages = 0; pos < end; pos += PAGE_SIZE, pages++) { + start = (void *)PAGE_ALIGN((unsigned long)start); + end = (void *)((unsigned long)end & PAGE_MASK); + for (pos = start; pos < end; pos += PAGE_SIZE, pages++) { if (poison) - memset((void *)pos, poison, PAGE_SIZE); - free_reserved_page(virt_to_page((void *)pos)); + memset(pos, poison, PAGE_SIZE); + free_reserved_page(virt_to_page(pos)); } if (pages && s) - pr_info("Freeing %s memory: %ldK (%lx - %lx)\n", + pr_info("Freeing %s memory: %ldK (%p - %p)\n", s, pages << (PAGE_SHIFT - 10), start, end); return pages; } +EXPORT_SYMBOL(free_reserved_area); #ifdef CONFIG_HIGHMEM void free_highmem_page(struct page *page) -- cgit v1.2.3 From dbe67df4ba78c79db547c7864e1120981c144c97 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:02:51 -0700 Subject: mm: enhance free_reserved_area() to support poisoning memory with zero Address more review comments from last round of code review. 1) Enhance free_reserved_area() to support poisoning freed memory with pattern '0'. This could be used to get rid of poison_init_mem() on ARM64. 2) A previous patch has disabled memory poison for initmem on s390 by mistake, so restore to the original behavior. 3) Remove redundant PAGE_ALIGN() when calling free_reserved_area(). Signed-off-by: Jiang Liu Cc: Geert Uytterhoeven Cc: "H. Peter Anvin" Cc: "Michael S. Tsirkin" Cc: Cc: Arnd Bergmann Cc: Catalin Marinas Cc: Chris Metcalf Cc: David Howells Cc: Ingo Molnar Cc: Jeremy Fitzhardinge Cc: Jianguo Wu Cc: Joonsoo Kim Cc: Kamezawa Hiroyuki Cc: Konrad Rzeszutek Wilk Cc: Marek Szyprowski Cc: Mel Gorman Cc: Michel Lespinasse Cc: Minchan Kim Cc: Rik van Riel Cc: Rusty Russell Cc: Tang Chen Cc: Tejun Heo Cc: Thomas Gleixner Cc: Wen Congyang Cc: Will Deacon Cc: Yasuaki Ishimatsu Cc: Yinghai Lu Cc: Russell King Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/alpha/kernel/sys_nautilus.c | 2 +- arch/alpha/mm/init.c | 4 ++-- arch/arc/mm/init.c | 4 ++-- arch/arm/mm/init.c | 8 ++++---- arch/arm64/mm/init.c | 4 ++-- arch/avr32/mm/init.c | 4 ++-- arch/blackfin/mm/init.c | 4 ++-- arch/c6x/mm/init.c | 4 ++-- arch/cris/mm/init.c | 2 +- arch/frv/mm/init.c | 4 ++-- arch/h8300/mm/init.c | 4 ++-- arch/ia64/mm/init.c | 2 +- arch/m32r/mm/init.c | 4 ++-- arch/m68k/mm/init.c | 4 ++-- arch/microblaze/mm/init.c | 4 ++-- arch/openrisc/mm/init.c | 4 ++-- arch/parisc/mm/init.c | 4 ++-- arch/powerpc/kernel/kvm.c | 9 ++------- arch/powerpc/mm/mem.c | 2 +- arch/s390/mm/init.c | 2 +- arch/sh/mm/init.c | 4 ++-- arch/um/kernel/mem.c | 2 +- arch/unicore32/mm/init.c | 4 ++-- arch/xtensa/mm/init.c | 4 ++-- include/linux/mm.h | 7 ++++--- mm/page_alloc.c | 2 +- 26 files changed, 49 insertions(+), 53 deletions(-) diff --git a/arch/alpha/kernel/sys_nautilus.c b/arch/alpha/kernel/sys_nautilus.c index 891bd274ccb5..837c0fa58317 100644 --- a/arch/alpha/kernel/sys_nautilus.c +++ b/arch/alpha/kernel/sys_nautilus.c @@ -239,7 +239,7 @@ nautilus_init_pci(void) memtop = pci_mem; if (memtop > alpha_mv.min_mem_address) { free_reserved_area(__va(alpha_mv.min_mem_address), - __va(memtop), 0, NULL); + __va(memtop), -1, NULL); printk("nautilus_init_pci: %ldk freed\n", (memtop - alpha_mv.min_mem_address) >> 10); } diff --git a/arch/alpha/mm/init.c b/arch/alpha/mm/init.c index d54848d4e464..218c29c14bb3 100644 --- a/arch/alpha/mm/init.c +++ b/arch/alpha/mm/init.c @@ -319,13 +319,13 @@ mem_init(void) void free_initmem(void) { - free_initmem_default(0); + free_initmem_default(-1); } #ifdef CONFIG_BLK_DEV_INITRD void free_initrd_mem(unsigned long start, unsigned long end) { - free_reserved_area((void *)start, (void *)end, 0, "initrd"); + free_reserved_area((void *)start, (void *)end, -1, "initrd"); } #endif diff --git a/arch/arc/mm/init.c b/arch/arc/mm/init.c index dce02e4716a1..f9c707712096 100644 --- a/arch/arc/mm/init.c +++ b/arch/arc/mm/init.c @@ -146,13 +146,13 @@ void __init mem_init(void) */ void __init_refok free_initmem(void) { - free_initmem_default(0); + free_initmem_default(-1); } #ifdef CONFIG_BLK_DEV_INITRD void __init free_initrd_mem(unsigned long start, unsigned long end) { - free_reserved_area((void *)start, (void *)end, 0, "initrd"); + free_reserved_area((void *)start, (void *)end, -1, "initrd"); } #endif diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 7fae391caf86..2070651c1bb4 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -601,7 +601,7 @@ void __init mem_init(void) #ifdef CONFIG_SA1111 /* now that our DMA memory is actually so designated, we can free it */ - free_reserved_area(__va(PHYS_PFN_OFFSET), swapper_pg_dir, 0, NULL); + free_reserved_area(__va(PHYS_PFN_OFFSET), swapper_pg_dir, -1, NULL); #endif free_highpages(); @@ -729,12 +729,12 @@ void free_initmem(void) extern char __tcm_start, __tcm_end; poison_init_mem(&__tcm_start, &__tcm_end - &__tcm_start); - free_reserved_area(&__tcm_start, &__tcm_end, 0, "TCM link"); + free_reserved_area(&__tcm_start, &__tcm_end, -1, "TCM link"); #endif poison_init_mem(__init_begin, __init_end - __init_begin); if (!machine_is_integrator() && !machine_is_cintegrator()) - free_initmem_default(0); + free_initmem_default(-1); } #ifdef CONFIG_BLK_DEV_INITRD @@ -745,7 +745,7 @@ void free_initrd_mem(unsigned long start, unsigned long end) { if (!keep_initrd) { poison_init_mem((void *)start, PAGE_ALIGN(end) - start); - free_reserved_area((void *)start, (void *)end, 0, "initrd"); + free_reserved_area((void *)start, (void *)end, -1, "initrd"); } } diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c index 6041e4008a83..997c6345cdd6 100644 --- a/arch/arm64/mm/init.c +++ b/arch/arm64/mm/init.c @@ -387,7 +387,7 @@ void __init mem_init(void) void free_initmem(void) { poison_init_mem(__init_begin, __init_end - __init_begin); - free_initmem_default(0); + free_initmem_default(-1); } #ifdef CONFIG_BLK_DEV_INITRD @@ -398,7 +398,7 @@ void free_initrd_mem(unsigned long start, unsigned long end) { if (!keep_initrd) { poison_init_mem((void *)start, PAGE_ALIGN(end) - start); - free_reserved_area((void *)start, (void *)end, 0, "initrd"); + free_reserved_area((void *)start, (void *)end, -1, "initrd"); } } diff --git a/arch/avr32/mm/init.c b/arch/avr32/mm/init.c index 5a79fa08cb3c..b079e04f6954 100644 --- a/arch/avr32/mm/init.c +++ b/arch/avr32/mm/init.c @@ -148,12 +148,12 @@ void __init mem_init(void) void free_initmem(void) { - free_initmem_default(0); + free_initmem_default(-1); } #ifdef CONFIG_BLK_DEV_INITRD void free_initrd_mem(unsigned long start, unsigned long end) { - free_reserved_area((void *)start, (void *)end, 0, "initrd"); + free_reserved_area((void *)start, (void *)end, -1, "initrd"); } #endif diff --git a/arch/blackfin/mm/init.c b/arch/blackfin/mm/init.c index 8e9eab272811..fa241f5a7dcf 100644 --- a/arch/blackfin/mm/init.c +++ b/arch/blackfin/mm/init.c @@ -133,7 +133,7 @@ void __init mem_init(void) void __init free_initrd_mem(unsigned long start, unsigned long end) { #ifndef CONFIG_MPU - free_reserved_area((void *)start, (void *)end, 0, "initrd"); + free_reserved_area((void *)start, (void *)end, -1, "initrd"); #endif } #endif @@ -141,7 +141,7 @@ void __init free_initrd_mem(unsigned long start, unsigned long end) void __init_refok free_initmem(void) { #if defined CONFIG_RAMKERNEL && !defined CONFIG_MPU - free_initmem_default(0); + free_initmem_default(-1); if (memory_start == (unsigned long)(&__init_end)) memory_start = (unsigned long)(&__init_begin); #endif diff --git a/arch/c6x/mm/init.c b/arch/c6x/mm/init.c index 07bfcc98a3b7..3987a20fdee6 100644 --- a/arch/c6x/mm/init.c +++ b/arch/c6x/mm/init.c @@ -78,11 +78,11 @@ void __init mem_init(void) #ifdef CONFIG_BLK_DEV_INITRD void __init free_initrd_mem(unsigned long start, unsigned long end) { - free_reserved_area((void *)start, (void *)end, 0, "initrd"); + free_reserved_area((void *)start, (void *)end, -1, "initrd"); } #endif void __init free_initmem(void) { - free_initmem_default(0); + free_initmem_default(-1); } diff --git a/arch/cris/mm/init.c b/arch/cris/mm/init.c index 9ac80946dada..8fec26392ae7 100644 --- a/arch/cris/mm/init.c +++ b/arch/cris/mm/init.c @@ -65,5 +65,5 @@ mem_init(void) void free_initmem(void) { - free_initmem_default(0); + free_initmem_default(-1); } diff --git a/arch/frv/mm/init.c b/arch/frv/mm/init.c index a67f3a5897b8..8ba9d22d0d91 100644 --- a/arch/frv/mm/init.c +++ b/arch/frv/mm/init.c @@ -162,7 +162,7 @@ void __init mem_init(void) void free_initmem(void) { #if defined(CONFIG_RAMKERNEL) && !defined(CONFIG_PROTECT_KERNEL) - free_initmem_default(0); + free_initmem_default(-1); #endif } /* end free_initmem() */ @@ -173,6 +173,6 @@ void free_initmem(void) #ifdef CONFIG_BLK_DEV_INITRD void __init free_initrd_mem(unsigned long start, unsigned long end) { - free_reserved_area((void *)start, (void *)end, 0, "initrd"); + free_reserved_area((void *)start, (void *)end, -1, "initrd"); } /* end free_initrd_mem() */ #endif diff --git a/arch/h8300/mm/init.c b/arch/h8300/mm/init.c index 57e03c59861d..c831f1dba132 100644 --- a/arch/h8300/mm/init.c +++ b/arch/h8300/mm/init.c @@ -161,7 +161,7 @@ void __init mem_init(void) #ifdef CONFIG_BLK_DEV_INITRD void free_initrd_mem(unsigned long start, unsigned long end) { - free_reserved_area((void *)start, (void *)end, 0, "initrd"); + free_reserved_area((void *)start, (void *)end, -1, "initrd"); } #endif @@ -169,7 +169,7 @@ void free_initmem(void) { #ifdef CONFIG_RAMKERNEL - free_initmem_default(0); + free_initmem_default(-1); #endif } diff --git a/arch/ia64/mm/init.c b/arch/ia64/mm/init.c index da568c2e839f..f8a4f38b0ad5 100644 --- a/arch/ia64/mm/init.c +++ b/arch/ia64/mm/init.c @@ -155,7 +155,7 @@ void free_initmem (void) { free_reserved_area(ia64_imva(__init_begin), ia64_imva(__init_end), - 0, "unused kernel"); + -1, "unused kernel"); } void __init diff --git a/arch/m32r/mm/init.c b/arch/m32r/mm/init.c index d80412d0c14e..cca87d918436 100644 --- a/arch/m32r/mm/init.c +++ b/arch/m32r/mm/init.c @@ -181,7 +181,7 @@ void __init mem_init(void) *======================================================================*/ void free_initmem(void) { - free_initmem_default(0); + free_initmem_default(-1); } #ifdef CONFIG_BLK_DEV_INITRD @@ -191,6 +191,6 @@ void free_initmem(void) *======================================================================*/ void free_initrd_mem(unsigned long start, unsigned long end) { - free_reserved_area((void *)start, (void *)end, 0, "initrd"); + free_reserved_area((void *)start, (void *)end, -1, "initrd"); } #endif diff --git a/arch/m68k/mm/init.c b/arch/m68k/mm/init.c index 95de725534e9..ab0b54ca5d85 100644 --- a/arch/m68k/mm/init.c +++ b/arch/m68k/mm/init.c @@ -110,7 +110,7 @@ void __init paging_init(void) void free_initmem(void) { #ifndef CONFIG_MMU_SUN3 - free_initmem_default(0); + free_initmem_default(-1); #endif /* CONFIG_MMU_SUN3 */ } @@ -202,6 +202,6 @@ void __init mem_init(void) #ifdef CONFIG_BLK_DEV_INITRD void free_initrd_mem(unsigned long start, unsigned long end) { - free_reserved_area((void *)start, (void *)end, 0, "initrd"); + free_reserved_area((void *)start, (void *)end, -1, "initrd"); } #endif diff --git a/arch/microblaze/mm/init.c b/arch/microblaze/mm/init.c index d7b8ada9345f..d149e0ebb767 100644 --- a/arch/microblaze/mm/init.c +++ b/arch/microblaze/mm/init.c @@ -235,13 +235,13 @@ void __init setup_memory(void) #ifdef CONFIG_BLK_DEV_INITRD void free_initrd_mem(unsigned long start, unsigned long end) { - free_reserved_area((void *)start, (void *)end, 0, "initrd"); + free_reserved_area((void *)start, (void *)end, -1, "initrd"); } #endif void free_initmem(void) { - free_initmem_default(0); + free_initmem_default(-1); } void __init mem_init(void) diff --git a/arch/openrisc/mm/init.c b/arch/openrisc/mm/init.c index ab113325dc4c..c371e4a0fcac 100644 --- a/arch/openrisc/mm/init.c +++ b/arch/openrisc/mm/init.c @@ -261,11 +261,11 @@ void __init mem_init(void) #ifdef CONFIG_BLK_DEV_INITRD void free_initrd_mem(unsigned long start, unsigned long end) { - free_reserved_area((void *)start, (void *)end, 0, "initrd"); + free_reserved_area((void *)start, (void *)end, -1, "initrd"); } #endif void free_initmem(void) { - free_initmem_default(0); + free_initmem_default(-1); } diff --git a/arch/parisc/mm/init.c b/arch/parisc/mm/init.c index 3223d5e4a372..ebac7bd76b56 100644 --- a/arch/parisc/mm/init.c +++ b/arch/parisc/mm/init.c @@ -532,7 +532,7 @@ void free_initmem(void) * pages are no-longer executable */ flush_icache_range(init_begin, init_end); - num_physpages += free_initmem_default(0); + num_physpages += free_initmem_default(-1); /* set up a new led state on systems shipped LED State panel */ pdc_chassis_send_status(PDC_CHASSIS_DIRECT_BCOMPLETE); @@ -1101,7 +1101,7 @@ void flush_tlb_all(void) #ifdef CONFIG_BLK_DEV_INITRD void free_initrd_mem(unsigned long start, unsigned long end) { - num_physpages += free_reserved_area((void *)start, (void *)end, 0, + num_physpages += free_reserved_area((void *)start, (void *)end, -1, "initrd"); } #endif diff --git a/arch/powerpc/kernel/kvm.c b/arch/powerpc/kernel/kvm.c index 5e4830a33c08..db28032e320e 100644 --- a/arch/powerpc/kernel/kvm.c +++ b/arch/powerpc/kernel/kvm.c @@ -750,13 +750,8 @@ EXPORT_SYMBOL_GPL(kvm_hypercall); static __init void kvm_free_tmp(void) { - unsigned long start, end; - - start = (ulong)&kvm_tmp[kvm_tmp_index + (PAGE_SIZE - 1)] & PAGE_MASK; - end = (ulong)&kvm_tmp[ARRAY_SIZE(kvm_tmp)] & PAGE_MASK; - - /* Free the tmp space we don't need */ - free_reserved_area((void *)start, (void *)end, 0, NULL); + free_reserved_area(&kvm_tmp[kvm_tmp_index], + &kvm_tmp[ARRAY_SIZE(kvm_tmp)], -1, NULL); } static int __init kvm_guest_init(void) diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c index 347c5b1bbd62..7f47a05f55af 100644 --- a/arch/powerpc/mm/mem.c +++ b/arch/powerpc/mm/mem.c @@ -407,7 +407,7 @@ void free_initmem(void) #ifdef CONFIG_BLK_DEV_INITRD void __init free_initrd_mem(unsigned long start, unsigned long end) { - free_reserved_area((void *)start, (void *)end, 0, "initrd"); + free_reserved_area((void *)start, (void *)end, -1, "initrd"); } #endif diff --git a/arch/s390/mm/init.c b/arch/s390/mm/init.c index 0878c89fe7d2..bf01d18422ec 100644 --- a/arch/s390/mm/init.c +++ b/arch/s390/mm/init.c @@ -166,7 +166,7 @@ void __init mem_init(void) void free_initmem(void) { - free_initmem_default(0); + free_initmem_default(POISON_FREE_INITMEM); } #ifdef CONFIG_BLK_DEV_INITRD diff --git a/arch/sh/mm/init.c b/arch/sh/mm/init.c index b892a9b7d7e3..d3af56b7a098 100644 --- a/arch/sh/mm/init.c +++ b/arch/sh/mm/init.c @@ -499,13 +499,13 @@ void __init mem_init(void) void free_initmem(void) { - free_initmem_default(0); + free_initmem_default(-1); } #ifdef CONFIG_BLK_DEV_INITRD void free_initrd_mem(unsigned long start, unsigned long end) { - free_reserved_area((void *)start, (void *)end, 0, "initrd"); + free_reserved_area((void *)start, (void *)end, -1, "initrd"); } #endif diff --git a/arch/um/kernel/mem.c b/arch/um/kernel/mem.c index 2aa7a2448d58..8ff0b7ae8ec0 100644 --- a/arch/um/kernel/mem.c +++ b/arch/um/kernel/mem.c @@ -244,7 +244,7 @@ void free_initmem(void) #ifdef CONFIG_BLK_DEV_INITRD void free_initrd_mem(unsigned long start, unsigned long end) { - free_reserved_area((void *)start, (void *)end, 0, "initrd"); + free_reserved_area((void *)start, (void *)end, -1, "initrd"); } #endif diff --git a/arch/unicore32/mm/init.c b/arch/unicore32/mm/init.c index 220755cc9700..df9b8abcb6a5 100644 --- a/arch/unicore32/mm/init.c +++ b/arch/unicore32/mm/init.c @@ -476,7 +476,7 @@ void __init mem_init(void) void free_initmem(void) { - free_initmem_default(0); + free_initmem_default(-1); } #ifdef CONFIG_BLK_DEV_INITRD @@ -486,7 +486,7 @@ static int keep_initrd; void free_initrd_mem(unsigned long start, unsigned long end) { if (!keep_initrd) - free_reserved_area((void *)start, (void *)end, 0, "initrd"); + free_reserved_area((void *)start, (void *)end, -1, "initrd"); } static int __init keepinitrd_setup(char *__unused) diff --git a/arch/xtensa/mm/init.c b/arch/xtensa/mm/init.c index 4d658efc3289..026d29bee30b 100644 --- a/arch/xtensa/mm/init.c +++ b/arch/xtensa/mm/init.c @@ -214,11 +214,11 @@ extern int initrd_is_mapped; void free_initrd_mem(unsigned long start, unsigned long end) { if (initrd_is_mapped) - free_reserved_area((void *)start, (void *)end, 0, "initrd"); + free_reserved_area((void *)start, (void *)end, -1, "initrd"); } #endif void free_initmem(void) { - free_initmem_default(0); + free_initmem_default(-1); } diff --git a/include/linux/mm.h b/include/linux/mm.h index be1b96ce0650..083cc0ba2384 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1308,7 +1308,7 @@ extern void free_initmem(void); /* * Free reserved pages within range [PAGE_ALIGN(start), end & PAGE_MASK) * into the buddy system. The freed pages will be poisoned with pattern - * "poison" if it's non-zero. + * "poison" if it's within range [0, UCHAR_MAX]. * Return pages freed into the buddy system. */ extern unsigned long free_reserved_area(void *start, void *end, @@ -1348,8 +1348,9 @@ static inline void mark_page_reserved(struct page *page) /* * Default method to free all the __init memory into the buddy system. - * The freed pages will be poisoned with pattern "poison" if it is - * non-zero. Return pages freed into the buddy system. + * The freed pages will be poisoned with pattern "poison" if it's within + * range [0, UCHAR_MAX]. + * Return pages freed into the buddy system. */ static inline unsigned long free_initmem_default(int poison) { diff --git a/mm/page_alloc.c b/mm/page_alloc.c index be18ccd017bb..6780b2e18aa1 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -5214,7 +5214,7 @@ unsigned long free_reserved_area(void *start, void *end, int poison, char *s) start = (void *)PAGE_ALIGN((unsigned long)start); end = (void *)((unsigned long)end & PAGE_MASK); for (pos = start; pos < end; pos += PAGE_SIZE, pages++) { - if (poison) + if ((unsigned int)poison <= 0xFF) memset(pos, poison, PAGE_SIZE); free_reserved_page(virt_to_page(pos)); } -- cgit v1.2.3 From 9af5b80765e4c30b8eba8218724459226fa5e81f Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:02:54 -0700 Subject: mm/ARM64: kill poison_init_mem() Use free_reserved_area() to poison initmem memory pages and kill poison_init_mem() on ARM64. Signed-off-by: Jiang Liu Acked-by: Catalin Marinas Cc: Will Deacon Cc: "H. Peter Anvin" Cc: "Michael S. Tsirkin" Cc: Cc: Arnd Bergmann Cc: Chris Metcalf Cc: David Howells Cc: Geert Uytterhoeven Cc: Ingo Molnar Cc: Jeremy Fitzhardinge Cc: Jianguo Wu Cc: Joonsoo Kim Cc: Kamezawa Hiroyuki Cc: Konrad Rzeszutek Wilk Cc: Marek Szyprowski Cc: Mel Gorman Cc: Michel Lespinasse Cc: Minchan Kim Cc: Rik van Riel Cc: Rusty Russell Cc: Tang Chen Cc: Tejun Heo Cc: Thomas Gleixner Cc: Wen Congyang Cc: Yasuaki Ishimatsu Cc: Yinghai Lu Cc: Russell King Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/arm64/mm/init.c | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c index 997c6345cdd6..a398eb9018bb 100644 --- a/arch/arm64/mm/init.c +++ b/arch/arm64/mm/init.c @@ -197,14 +197,6 @@ void __init bootmem_init(void) max_pfn = max_low_pfn = max; } -/* - * Poison init memory with an undefined instruction (0x0). - */ -static inline void poison_init_mem(void *s, size_t count) -{ - memset(s, 0, count); -} - #ifndef CONFIG_SPARSEMEM_VMEMMAP static inline void free_memmap(unsigned long start_pfn, unsigned long end_pfn) { @@ -386,8 +378,7 @@ void __init mem_init(void) void free_initmem(void) { - poison_init_mem(__init_begin, __init_end - __init_begin); - free_initmem_default(-1); + free_initmem_default(0); } #ifdef CONFIG_BLK_DEV_INITRD @@ -396,10 +387,8 @@ static int keep_initrd; void free_initrd_mem(unsigned long start, unsigned long end) { - if (!keep_initrd) { - poison_init_mem((void *)start, PAGE_ALIGN(end) - start); - free_reserved_area((void *)start, (void *)end, -1, "initrd"); - } + if (!keep_initrd) + free_reserved_area((void *)start, (void *)end, 0, "initrd"); } static int __init keepinitrd_setup(char *__unused) -- cgit v1.2.3 From c88442ec45f30d587b38b935a14acde4e217a926 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:02:58 -0700 Subject: mm/x86: use free_reserved_area() to simplify code Use common help function free_reserved_area() to simplify code. Signed-off-by: Jiang Liu Cc: Thomas Gleixner Cc: Ingo Molnar Cc: "H. Peter Anvin" Cc: Yinghai Lu Cc: Tang Chen Cc: Wen Congyang Cc: Jianguo Wu Cc: "Michael S. Tsirkin" Cc: Cc: Arnd Bergmann Cc: Catalin Marinas Cc: Chris Metcalf Cc: David Howells Cc: Geert Uytterhoeven Cc: Jeremy Fitzhardinge Cc: Joonsoo Kim Cc: Kamezawa Hiroyuki Cc: Konrad Rzeszutek Wilk Cc: Marek Szyprowski Cc: Mel Gorman Cc: Michel Lespinasse Cc: Minchan Kim Cc: Rik van Riel Cc: Rusty Russell Cc: Tejun Heo Cc: Will Deacon Cc: Yasuaki Ishimatsu Cc: Russell King Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/x86/mm/init.c | 14 +++----------- arch/x86/mm/init_64.c | 5 ++--- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index 1f34e9219775..2ec29ac78ae6 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -494,7 +494,6 @@ int devmem_is_allowed(unsigned long pagenr) void free_init_pages(char *what, unsigned long begin, unsigned long end) { - unsigned long addr; unsigned long begin_aligned, end_aligned; /* Make sure boundaries are page aligned */ @@ -509,8 +508,6 @@ void free_init_pages(char *what, unsigned long begin, unsigned long end) if (begin >= end) return; - addr = begin; - /* * If debugging page accesses then do not free this memory but * mark them not present - any buggy init-section access will @@ -529,18 +526,13 @@ void free_init_pages(char *what, unsigned long begin, unsigned long end) set_memory_nx(begin, (end - begin) >> PAGE_SHIFT); set_memory_rw(begin, (end - begin) >> PAGE_SHIFT); - printk(KERN_INFO "Freeing %s: %luk freed\n", what, (end - begin) >> 10); - - for (; addr < end; addr += PAGE_SIZE) { - memset((void *)addr, POISON_FREE_INITMEM, PAGE_SIZE); - free_reserved_page(virt_to_page(addr)); - } + free_reserved_area((void *)begin, (void *)end, POISON_FREE_INITMEM, what); #endif } void free_initmem(void) { - free_init_pages("unused kernel memory", + free_init_pages("unused kernel", (unsigned long)(&__init_begin), (unsigned long)(&__init_end)); } @@ -566,7 +558,7 @@ void __init free_initrd_mem(unsigned long start, unsigned long end) * - relocate_initrd() * So here We can do PAGE_ALIGN() safely to get partial page to be freed */ - free_init_pages("initrd memory", start, PAGE_ALIGN(end)); + free_init_pages("initrd", start, PAGE_ALIGN(end)); } #endif diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index b3940b6b4d7e..b7bdf7bebf3b 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -1166,11 +1166,10 @@ void mark_rodata_ro(void) set_memory_ro(start, (end-start) >> PAGE_SHIFT); #endif - free_init_pages("unused kernel memory", + free_init_pages("unused kernel", (unsigned long) __va(__pa_symbol(text_end)), (unsigned long) __va(__pa_symbol(rodata_start))); - - free_init_pages("unused kernel memory", + free_init_pages("unused kernel", (unsigned long) __va(__pa_symbol(rodata_end)), (unsigned long) __va(__pa_symbol(_sdata))); } -- cgit v1.2.3 From abd1b6d65fc49b7b95724c94a4e5892a3b3fc618 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:03:01 -0700 Subject: mm/tile: use common help functions to free reserved pages Use common help functions to free reserved pages. Signed-off-by: Jiang Liu Cc: Chris Metcalf Cc: Wen Congyang Cc: "H. Peter Anvin" Cc: "Michael S. Tsirkin" Cc: Cc: Arnd Bergmann Cc: Catalin Marinas Cc: David Howells Cc: Geert Uytterhoeven Cc: Ingo Molnar Cc: Jeremy Fitzhardinge Cc: Jianguo Wu Cc: Joonsoo Kim Cc: Kamezawa Hiroyuki Cc: Konrad Rzeszutek Wilk Cc: Marek Szyprowski Cc: Mel Gorman Cc: Michel Lespinasse Cc: Minchan Kim Cc: Rik van Riel Cc: Rusty Russell Cc: Tang Chen Cc: Tejun Heo Cc: Thomas Gleixner Cc: Will Deacon Cc: Yasuaki Ishimatsu Cc: Yinghai Lu Cc: Russell King Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/tile/mm/init.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/arch/tile/mm/init.c b/arch/tile/mm/init.c index 2749515a0547..ccfeb3f2e769 100644 --- a/arch/tile/mm/init.c +++ b/arch/tile/mm/init.c @@ -720,7 +720,7 @@ static void __init init_free_pfn_range(unsigned long start, unsigned long end) } init_page_count(page); __free_pages(page, order); - totalram_pages += count; + adjust_managed_page_count(page, count); page += count; pfn += count; @@ -1024,16 +1024,13 @@ static void free_init_pages(char *what, unsigned long begin, unsigned long end) pte_clear(&init_mm, addr, ptep); continue; } - __ClearPageReserved(page); - init_page_count(page); if (pte_huge(*ptep)) BUG_ON(!kdata_huge); else set_pte_at(&init_mm, addr, ptep, pfn_pte(pfn, PAGE_KERNEL)); memset((void *)addr, POISON_FREE_INITMEM, PAGE_SIZE); - free_page(addr); - totalram_pages++; + free_reserved_page(page); } pr_info("Freeing %s: %ldk freed\n", what, (end - begin) >> 10); } -- cgit v1.2.3 From 834405c3b6aebf6853663796401cdfe11aac6275 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:03:04 -0700 Subject: mm: fix some trivial typos in comments Fix some trivial typos in comments. Signed-off-by: Jiang Liu Cc: Wen Congyang Cc: Tang Chen Cc: Yasuaki Ishimatsu Cc: Mel Gorman Cc: Minchan Kim Cc: Marek Szyprowski Cc: "H. Peter Anvin" Cc: "Michael S. Tsirkin" Cc: Cc: Arnd Bergmann Cc: Catalin Marinas Cc: Chris Metcalf Cc: David Howells Cc: Geert Uytterhoeven Cc: Ingo Molnar Cc: Jeremy Fitzhardinge Cc: Jianguo Wu Cc: Joonsoo Kim Cc: Kamezawa Hiroyuki Cc: Konrad Rzeszutek Wilk Cc: Michel Lespinasse Cc: Rik van Riel Cc: Rusty Russell Cc: Tejun Heo Cc: Thomas Gleixner Cc: Will Deacon Cc: Yinghai Lu Cc: Russell King Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/memory_hotplug.c | 2 +- mm/page_alloc.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index e3097f299f67..6096cb918735 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -309,7 +309,7 @@ static int __meminit move_pfn_range_left(struct zone *z1, struct zone *z2, /* can't move pfns which are higher than @z2 */ if (end_pfn > zone_end_pfn(z2)) goto out_fail; - /* the move out part mast at the left most of @z2 */ + /* the move out part must be at the left most of @z2 */ if (start_pfn > z2->zone_start_pfn) goto out_fail; /* must included/overlap */ diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 6780b2e18aa1..657daea88aa8 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -2845,7 +2845,7 @@ EXPORT_SYMBOL(free_pages_exact); * nr_free_zone_pages() counts the number of counts pages which are beyond the * high watermark within all zones at or below a given zone index. For each * zone, the number of pages is calculated as: - * present_pages - high_pages + * managed_pages - high_pages */ static unsigned long nr_free_zone_pages(int offset) { -- cgit v1.2.3 From 4f9f47745e948eca18bb97c82dbb4d53f2380086 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:03:07 -0700 Subject: mm: use managed_pages to calculate default zonelist order Use zone->managed_pages instead of zone->present_pages to calculate default zonelist order because managed_pages means allocatable pages. Signed-off-by: Jiang Liu Cc: Mel Gorman Cc: Minchan Kim Cc: Marek Szyprowski Cc: "H. Peter Anvin" Cc: "Michael S. Tsirkin" Cc: Cc: Arnd Bergmann Cc: Catalin Marinas Cc: Chris Metcalf Cc: David Howells Cc: Geert Uytterhoeven Cc: Ingo Molnar Cc: Jeremy Fitzhardinge Cc: Jianguo Wu Cc: Joonsoo Kim Cc: Kamezawa Hiroyuki Cc: Konrad Rzeszutek Wilk Cc: Michel Lespinasse Cc: Rik van Riel Cc: Rusty Russell Cc: Tang Chen Cc: Tejun Heo Cc: Thomas Gleixner Cc: Wen Congyang Cc: Will Deacon Cc: Yasuaki Ishimatsu Cc: Yinghai Lu Cc: Russell King Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/page_alloc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 657daea88aa8..f22542f6dc12 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -3438,8 +3438,8 @@ static int default_zonelist_order(void) z = &NODE_DATA(nid)->node_zones[zone_type]; if (populated_zone(z)) { if (zone_type < ZONE_NORMAL) - low_kmem_size += z->present_pages; - total_size += z->present_pages; + low_kmem_size += z->managed_pages; + total_size += z->managed_pages; } else if (zone_type == ZONE_NORMAL) { /* * If any node has only lowmem, then node order -- cgit v1.2.3 From 7b4b2a0d6c8500350784beb83a6a55e60ea3bea3 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:03:11 -0700 Subject: mm: accurately calculate zone->managed_pages for highmem zones Commit "mm: introduce new field 'managed_pages' to struct zone" assumes that all highmem pages will be freed into the buddy system by function mem_init(). But that's not always true, some architectures may reserve some highmem pages during boot. For example PPC may allocate highmem pages for giagant HugeTLB pages, and several architectures have code to check PageReserved flag to exclude highmem pages allocated during boot when freeing highmem pages into the buddy system. So treat highmem pages in the same way as normal pages, that is to: 1) reset zone->managed_pages to zero in mem_init(). 2) recalculate managed_pages when freeing pages into the buddy system. Signed-off-by: Jiang Liu Cc: "H. Peter Anvin" Cc: Tejun Heo Cc: Joonsoo Kim Cc: Yinghai Lu Cc: Mel Gorman Cc: Minchan Kim Cc: Kamezawa Hiroyuki Cc: Marek Szyprowski Cc: "Michael S. Tsirkin" Cc: Cc: Arnd Bergmann Cc: Catalin Marinas Cc: Chris Metcalf Cc: David Howells Cc: Geert Uytterhoeven Cc: Ingo Molnar Cc: Jeremy Fitzhardinge Cc: Jianguo Wu Cc: Konrad Rzeszutek Wilk Cc: Michel Lespinasse Cc: Rik van Riel Cc: Rusty Russell Cc: Tang Chen Cc: Thomas Gleixner Cc: Wen Congyang Cc: Will Deacon Cc: Yasuaki Ishimatsu Cc: Russell King Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/metag/mm/init.c | 6 ++++++ arch/x86/mm/highmem_32.c | 6 ++++++ include/linux/bootmem.h | 1 + mm/bootmem.c | 32 ++++++++++++++++++-------------- mm/nobootmem.c | 30 ++++++++++++++++-------------- mm/page_alloc.c | 1 + 6 files changed, 48 insertions(+), 28 deletions(-) diff --git a/arch/metag/mm/init.c b/arch/metag/mm/init.c index 5e2238dd72e0..d7595f58fad5 100644 --- a/arch/metag/mm/init.c +++ b/arch/metag/mm/init.c @@ -380,6 +380,12 @@ void __init mem_init(void) #ifdef CONFIG_HIGHMEM unsigned long tmp; + + /* + * Explicitly reset zone->managed_pages because highmem pages are + * freed before calling free_all_bootmem_node(); + */ + reset_all_zones_managed_pages(); for (tmp = highstart_pfn; tmp < highend_pfn; tmp++) free_highmem_page(pfn_to_page(tmp)); num_physpages += totalhigh_pages; diff --git a/arch/x86/mm/highmem_32.c b/arch/x86/mm/highmem_32.c index 252b8f5489ba..4500142bc4aa 100644 --- a/arch/x86/mm/highmem_32.c +++ b/arch/x86/mm/highmem_32.c @@ -1,6 +1,7 @@ #include #include #include /* for totalram_pages */ +#include void *kmap(struct page *page) { @@ -121,6 +122,11 @@ void __init set_highmem_pages_init(void) struct zone *zone; int nid; + /* + * Explicitly reset zone->managed_pages because set_highmem_pages_init() + * is invoked before free_all_bootmem() + */ + reset_all_zones_managed_pages(); for_each_zone(zone) { unsigned long zone_start_pfn, zone_end_pfn; diff --git a/include/linux/bootmem.h b/include/linux/bootmem.h index 5f0b0e1f7c08..0e48c3221d82 100644 --- a/include/linux/bootmem.h +++ b/include/linux/bootmem.h @@ -46,6 +46,7 @@ extern unsigned long init_bootmem(unsigned long addr, unsigned long memend); extern unsigned long free_all_bootmem_node(pg_data_t *pgdat); extern unsigned long free_all_bootmem(void); +extern void reset_all_zones_managed_pages(void); extern void free_bootmem_node(pg_data_t *pgdat, unsigned long addr, diff --git a/mm/bootmem.c b/mm/bootmem.c index 2b0bcb019ec2..eb792323187b 100644 --- a/mm/bootmem.c +++ b/mm/bootmem.c @@ -241,20 +241,26 @@ static unsigned long __init free_all_bootmem_core(bootmem_data_t *bdata) return count; } -static void reset_node_lowmem_managed_pages(pg_data_t *pgdat) +static int reset_managed_pages_done __initdata; + +static inline void __init reset_node_managed_pages(pg_data_t *pgdat) { struct zone *z; - /* - * In free_area_init_core(), highmem zone's managed_pages is set to - * present_pages, and bootmem allocator doesn't allocate from highmem - * zones. So there's no need to recalculate managed_pages because all - * highmem pages will be managed by the buddy system. Here highmem - * zone also includes highmem movable zone. - */ + if (reset_managed_pages_done) + return; + for (z = pgdat->node_zones; z < pgdat->node_zones + MAX_NR_ZONES; z++) - if (!is_highmem(z)) - z->managed_pages = 0; + z->managed_pages = 0; +} + +void __init reset_all_zones_managed_pages(void) +{ + struct pglist_data *pgdat; + + for_each_online_pgdat(pgdat) + reset_node_managed_pages(pgdat); + reset_managed_pages_done = 1; } /** @@ -266,7 +272,7 @@ static void reset_node_lowmem_managed_pages(pg_data_t *pgdat) unsigned long __init free_all_bootmem_node(pg_data_t *pgdat) { register_page_bootmem_info_node(pgdat); - reset_node_lowmem_managed_pages(pgdat); + reset_node_managed_pages(pgdat); return free_all_bootmem_core(pgdat->bdata); } @@ -279,10 +285,8 @@ unsigned long __init free_all_bootmem(void) { unsigned long total_pages = 0; bootmem_data_t *bdata; - struct pglist_data *pgdat; - for_each_online_pgdat(pgdat) - reset_node_lowmem_managed_pages(pgdat); + reset_all_zones_managed_pages(); list_for_each_entry(bdata, &bdata_list, list) total_pages += free_all_bootmem_core(bdata); diff --git a/mm/nobootmem.c b/mm/nobootmem.c index bdd3fa2fc73b..0ae8d91365af 100644 --- a/mm/nobootmem.c +++ b/mm/nobootmem.c @@ -137,20 +137,25 @@ static unsigned long __init free_low_memory_core_early(void) return count; } -static void reset_node_lowmem_managed_pages(pg_data_t *pgdat) +static int reset_managed_pages_done __initdata; + +static inline void __init reset_node_managed_pages(pg_data_t *pgdat) { struct zone *z; - /* - * In free_area_init_core(), highmem zone's managed_pages is set to - * present_pages, and bootmem allocator doesn't allocate from highmem - * zones. So there's no need to recalculate managed_pages because all - * highmem pages will be managed by the buddy system. Here highmem - * zone also includes highmem movable zone. - */ + if (reset_managed_pages_done) + return; for (z = pgdat->node_zones; z < pgdat->node_zones + MAX_NR_ZONES; z++) - if (!is_highmem(z)) - z->managed_pages = 0; + z->managed_pages = 0; +} + +void __init reset_all_zones_managed_pages(void) +{ + struct pglist_data *pgdat; + + for_each_online_pgdat(pgdat) + reset_node_managed_pages(pgdat); + reset_managed_pages_done = 1; } /** @@ -160,10 +165,7 @@ static void reset_node_lowmem_managed_pages(pg_data_t *pgdat) */ unsigned long __init free_all_bootmem(void) { - struct pglist_data *pgdat; - - for_each_online_pgdat(pgdat) - reset_node_lowmem_managed_pages(pgdat); + reset_all_zones_managed_pages(); /* * We need to use MAX_NUMNODES instead of NODE_DATA(0)->node_id diff --git a/mm/page_alloc.c b/mm/page_alloc.c index f22542f6dc12..22438eba00b6 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -5232,6 +5232,7 @@ void free_highmem_page(struct page *page) { __free_reserved_page(page); totalram_pages++; + page_zone(page)->managed_pages++; totalhigh_pages++; } #endif -- cgit v1.2.3 From c3d5f5f0c2bc4eabeaf49f1a21e1aeb965246cd2 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:03:14 -0700 Subject: mm: use a dedicated lock to protect totalram_pages and zone->managed_pages Currently lock_memory_hotplug()/unlock_memory_hotplug() are used to protect totalram_pages and zone->managed_pages. Other than the memory hotplug driver, totalram_pages and zone->managed_pages may also be modified at runtime by other drivers, such as Xen balloon, virtio_balloon etc. For those cases, memory hotplug lock is a little too heavy, so introduce a dedicated lock to protect totalram_pages and zone->managed_pages. Now we have a simplified locking rules totalram_pages and zone->managed_pages as: 1) no locking for read accesses because they are unsigned long. 2) no locking for write accesses at boot time in single-threaded context. 3) serialize write accesses at runtime by acquiring the dedicated managed_page_count_lock. Also adjust zone->managed_pages when freeing reserved pages into the buddy system, to keep totalram_pages and zone->managed_pages in consistence. [akpm@linux-foundation.org: don't export adjust_managed_page_count to modules (for now)] Signed-off-by: Jiang Liu Cc: Mel Gorman Cc: Michel Lespinasse Cc: Rik van Riel Cc: Minchan Kim Cc: "H. Peter Anvin" Cc: "Michael S. Tsirkin" Cc: Cc: Arnd Bergmann Cc: Catalin Marinas Cc: Chris Metcalf Cc: David Howells Cc: Geert Uytterhoeven Cc: Ingo Molnar Cc: Jeremy Fitzhardinge Cc: Jianguo Wu Cc: Joonsoo Kim Cc: Kamezawa Hiroyuki Cc: Konrad Rzeszutek Wilk Cc: Marek Szyprowski Cc: Rusty Russell Cc: Tang Chen Cc: Tejun Heo Cc: Thomas Gleixner Cc: Wen Congyang Cc: Will Deacon Cc: Yasuaki Ishimatsu Cc: Yinghai Lu Cc: Russell King Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mm.h | 6 ++---- include/linux/mmzone.h | 14 ++++++++++---- mm/page_alloc.c | 11 +++++++++++ 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/include/linux/mm.h b/include/linux/mm.h index 083cc0ba2384..4310f80ce956 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1313,6 +1313,7 @@ extern void free_initmem(void); */ extern unsigned long free_reserved_area(void *start, void *end, int poison, char *s); + #ifdef CONFIG_HIGHMEM /* * Free a highmem page into the buddy system, adjusting totalhigh_pages @@ -1321,10 +1322,7 @@ extern unsigned long free_reserved_area(void *start, void *end, extern void free_highmem_page(struct page *page); #endif -static inline void adjust_managed_page_count(struct page *page, long count) -{ - totalram_pages += count; -} +extern void adjust_managed_page_count(struct page *page, long count); /* Free the reserved page into the buddy system, so it gets managed. */ static inline void __free_reserved_page(struct page *page) diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index e511f9429f1e..09d381b71fd8 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -474,10 +474,16 @@ struct zone { * frequently read in proximity to zone->lock. It's good to * give them a chance of being in the same cacheline. * - * Write access to present_pages and managed_pages at runtime should - * be protected by lock_memory_hotplug()/unlock_memory_hotplug(). - * Any reader who can't tolerant drift of present_pages and - * managed_pages should hold memory hotplug lock to get a stable value. + * Write access to present_pages at runtime should be protected by + * lock_memory_hotplug()/unlock_memory_hotplug(). Any reader who can't + * tolerant drift of present_pages should hold memory hotplug lock to + * get a stable value. + * + * Read access to managed_pages should be safe because it's unsigned + * long. Write access to zone->managed_pages and totalram_pages are + * protected by managed_page_count_lock at runtime. Idealy only + * adjust_managed_page_count() should be used instead of directly + * touching zone->managed_pages and totalram_pages. */ unsigned long spanned_pages; unsigned long present_pages; diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 22438eba00b6..93f292a60cb0 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -103,6 +103,9 @@ nodemask_t node_states[NR_NODE_STATES] __read_mostly = { }; EXPORT_SYMBOL(node_states); +/* Protect totalram_pages and zone->managed_pages */ +static DEFINE_SPINLOCK(managed_page_count_lock); + unsigned long totalram_pages __read_mostly; unsigned long totalreserve_pages __read_mostly; /* @@ -5206,6 +5209,14 @@ early_param("movablecore", cmdline_parse_movablecore); #endif /* CONFIG_HAVE_MEMBLOCK_NODE_MAP */ +void adjust_managed_page_count(struct page *page, long count) +{ + spin_lock(&managed_page_count_lock); + page_zone(page)->managed_pages += count; + totalram_pages += count; + spin_unlock(&managed_page_count_lock); +} + unsigned long free_reserved_area(void *start, void *end, int poison, char *s) { void *pos; -- cgit v1.2.3 From 170a5a7eb2bf10161197e5490fbc29ca4561aedb Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:03:17 -0700 Subject: mm: make __free_pages_bootmem() only available at boot time In order to simpilify management of totalram_pages and zone->managed_pages, make __free_pages_bootmem() only available at boot time. With this change applied, __free_pages_bootmem() will only be used by bootmem.c and nobootmem.c at boot time, so mark it as __init. Other callers of __free_pages_bootmem() have been converted to use free_reserved_page(), which handles totalram_pages and zone->managed_pages in a safer way. This patch also fix a bug in free_pagetable() for x86_64, which should increase zone->managed_pages instead of zone->present_pages when freeing reserved pages. And now we have managed_pages_count_lock to protect totalram_pages and zone->managed_pages, so remove the redundant ppb_lock lock in put_page_bootmem(). This greatly simplifies the locking rules. Signed-off-by: Jiang Liu Cc: Thomas Gleixner Cc: Ingo Molnar Cc: "H. Peter Anvin" Cc: Yinghai Lu Cc: Wen Congyang Cc: Tang Chen Cc: Yasuaki Ishimatsu Cc: Mel Gorman Cc: Minchan Kim Cc: "Michael S. Tsirkin" Cc: Cc: Arnd Bergmann Cc: Catalin Marinas Cc: Chris Metcalf Cc: David Howells Cc: Geert Uytterhoeven Cc: Jeremy Fitzhardinge Cc: Jianguo Wu Cc: Joonsoo Kim Cc: Kamezawa Hiroyuki Cc: Konrad Rzeszutek Wilk Cc: Marek Szyprowski Cc: Michel Lespinasse Cc: Rik van Riel Cc: Rusty Russell Cc: Tejun Heo Cc: Will Deacon Cc: Russell King Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/x86/mm/init_64.c | 18 ++---------------- mm/memory_hotplug.c | 16 ++-------------- mm/page_alloc.c | 9 +-------- 3 files changed, 5 insertions(+), 38 deletions(-) diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index b7bdf7bebf3b..ec312a92b137 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -712,36 +712,22 @@ EXPORT_SYMBOL_GPL(arch_add_memory); static void __meminit free_pagetable(struct page *page, int order) { - struct zone *zone; - bool bootmem = false; unsigned long magic; unsigned int nr_pages = 1 << order; /* bootmem page has reserved flag */ if (PageReserved(page)) { __ClearPageReserved(page); - bootmem = true; magic = (unsigned long)page->lru.next; if (magic == SECTION_INFO || magic == MIX_SECTION_INFO) { while (nr_pages--) put_page_bootmem(page++); } else - __free_pages_bootmem(page, order); + while (nr_pages--) + free_reserved_page(page++); } else free_pages((unsigned long)page_address(page), order); - - /* - * SECTION_INFO pages and MIX_SECTION_INFO pages - * are all allocated by bootmem. - */ - if (bootmem) { - zone = page_zone(page); - zone_span_writelock(zone); - zone->present_pages += nr_pages; - zone_span_writeunlock(zone); - totalram_pages += nr_pages; - } } static void __meminit free_pte_table(pte_t *pte_start, pmd_t *pmd) diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index 6096cb918735..814ecb2d262f 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -101,12 +101,9 @@ void get_page_bootmem(unsigned long info, struct page *page, atomic_inc(&page->_count); } -/* reference to __meminit __free_pages_bootmem is valid - * so use __ref to tell modpost not to generate a warning */ -void __ref put_page_bootmem(struct page *page) +void put_page_bootmem(struct page *page) { unsigned long type; - static DEFINE_MUTEX(ppb_lock); type = (unsigned long) page->lru.next; BUG_ON(type < MEMORY_HOTPLUG_MIN_BOOTMEM_TYPE || @@ -116,17 +113,8 @@ void __ref put_page_bootmem(struct page *page) ClearPagePrivate(page); set_page_private(page, 0); INIT_LIST_HEAD(&page->lru); - - /* - * Please refer to comment for __free_pages_bootmem() - * for why we serialize here. - */ - mutex_lock(&ppb_lock); - __free_pages_bootmem(page, 0); - mutex_unlock(&ppb_lock); - totalram_pages++; + free_reserved_page(page); } - } #ifdef CONFIG_HAVE_BOOTMEM_INFO_NODE diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 93f292a60cb0..2437a7e17aba 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -745,14 +745,7 @@ static void __free_pages_ok(struct page *page, unsigned int order) local_irq_restore(flags); } -/* - * Read access to zone->managed_pages is safe because it's unsigned long, - * but we still need to serialize writers. Currently all callers of - * __free_pages_bootmem() except put_page_bootmem() should only be used - * at boot time. So for shorter boot time, we shift the burden to - * put_page_bootmem() to serialize writers. - */ -void __meminit __free_pages_bootmem(struct page *page, unsigned int order) +void __init __free_pages_bootmem(struct page *page, unsigned int order) { unsigned int nr_pages = 1 << order; unsigned int loop; -- cgit v1.2.3 From 3dcc0571cd64816309765b7c7e4691a4cadf2ee7 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:03:21 -0700 Subject: mm: correctly update zone->managed_pages Enhance adjust_managed_page_count() to adjust totalhigh_pages for highmem pages. And change code which directly adjusts totalram_pages to use adjust_managed_page_count() because it adjusts totalram_pages, totalhigh_pages and zone->managed_pages altogether in a safe way. Remove inc_totalhigh_pages() and dec_totalhigh_pages() from xen/balloon driver bacause adjust_managed_page_count() has already adjusted totalhigh_pages. This patch also fixes two bugs: 1) enhances virtio_balloon driver to adjust totalhigh_pages when reserve/unreserve pages. 2) enhance memory_hotplug.c to adjust totalhigh_pages when hot-removing memory. We still need to deal with modifications of totalram_pages in file arch/powerpc/platforms/pseries/cmm.c, but need help from PPC experts. [akpm@linux-foundation.org: remove ifdef, per Wanpeng Li, virtio_balloon.c cleanup, per Sergei] [akpm@linux-foundation.org: export adjust_managed_page_count() to modules, for drivers/virtio/virtio_balloon.c] Signed-off-by: Jiang Liu Cc: Chris Metcalf Cc: Rusty Russell Cc: "Michael S. Tsirkin" Cc: Konrad Rzeszutek Wilk Cc: Jeremy Fitzhardinge Cc: Wen Congyang Cc: Tang Chen Cc: Yasuaki Ishimatsu Cc: Mel Gorman Cc: Minchan Kim Cc: "H. Peter Anvin" Cc: Cc: Arnd Bergmann Cc: Catalin Marinas Cc: David Howells Cc: Geert Uytterhoeven Cc: Ingo Molnar Cc: Jianguo Wu Cc: Joonsoo Kim Cc: Kamezawa Hiroyuki Cc: Marek Szyprowski Cc: Michel Lespinasse Cc: Rik van Riel Cc: Tejun Heo Cc: Thomas Gleixner Cc: Will Deacon Cc: Yinghai Lu Cc: Russell King Cc: Sergei Shtylyov Cc: Wu Fengguang Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/virtio/virtio_balloon.c | 7 ++++--- drivers/xen/balloon.c | 23 +++++------------------ mm/hugetlb.c | 2 +- mm/memory_hotplug.c | 16 +++------------- mm/page_alloc.c | 11 ++++++----- 5 files changed, 19 insertions(+), 40 deletions(-) diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index bd3ae324a1a2..0098810df69d 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c @@ -148,7 +148,7 @@ static void fill_balloon(struct virtio_balloon *vb, size_t num) } set_page_pfns(vb->pfns + vb->num_pfns, page); vb->num_pages += VIRTIO_BALLOON_PAGES_PER_PAGE; - totalram_pages--; + adjust_managed_page_count(page, -1); } /* Did we get any? */ @@ -163,8 +163,9 @@ static void release_pages_by_pfn(const u32 pfns[], unsigned int num) /* Find pfns pointing at start of each page, get pages and free them. */ for (i = 0; i < num; i += VIRTIO_BALLOON_PAGES_PER_PAGE) { - balloon_page_free(balloon_pfn_to_page(pfns[i])); - totalram_pages++; + struct page *page = balloon_pfn_to_page(pfns[i]); + balloon_page_free(page); + adjust_managed_page_count(page, 1); } } diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c index 930fb6817901..c8aab4e97833 100644 --- a/drivers/xen/balloon.c +++ b/drivers/xen/balloon.c @@ -89,14 +89,6 @@ EXPORT_SYMBOL_GPL(balloon_stats); /* We increase/decrease in batches which fit in a page */ static xen_pfn_t frame_list[PAGE_SIZE / sizeof(unsigned long)]; -#ifdef CONFIG_HIGHMEM -#define inc_totalhigh_pages() (totalhigh_pages++) -#define dec_totalhigh_pages() (totalhigh_pages--) -#else -#define inc_totalhigh_pages() do {} while (0) -#define dec_totalhigh_pages() do {} while (0) -#endif - /* List of ballooned pages, threaded through the mem_map array. */ static LIST_HEAD(ballooned_pages); @@ -132,9 +124,7 @@ static void __balloon_append(struct page *page) static void balloon_append(struct page *page) { __balloon_append(page); - if (PageHighMem(page)) - dec_totalhigh_pages(); - totalram_pages--; + adjust_managed_page_count(page, -1); } /* balloon_retrieve: rescue a page from the balloon, if it is not empty. */ @@ -151,13 +141,12 @@ static struct page *balloon_retrieve(bool prefer_highmem) page = list_entry(ballooned_pages.next, struct page, lru); list_del(&page->lru); - if (PageHighMem(page)) { + if (PageHighMem(page)) balloon_stats.balloon_high--; - inc_totalhigh_pages(); - } else + else balloon_stats.balloon_low--; - totalram_pages++; + adjust_managed_page_count(page, 1); return page; } @@ -372,9 +361,7 @@ static enum bp_state increase_reservation(unsigned long nr_pages) #endif /* Relinquish the page back to the allocator. */ - ClearPageReserved(page); - init_page_count(page); - __free_page(page); + __free_reserved_page(page); } balloon_stats.current_pages += rc; diff --git a/mm/hugetlb.c b/mm/hugetlb.c index fe095158859e..83aff0a4d093 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -1263,7 +1263,7 @@ static void __init gather_bootmem_prealloc(void) * side-effects, like CommitLimit going negative. */ if (h->order > (MAX_ORDER - 1)) - totalram_pages += 1 << h->order; + adjust_managed_page_count(page, 1 << h->order); } } diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index 814ecb2d262f..5e34922124a3 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -772,20 +772,13 @@ EXPORT_SYMBOL_GPL(__online_page_set_limits); void __online_page_increment_counters(struct page *page) { - totalram_pages++; - -#ifdef CONFIG_HIGHMEM - if (PageHighMem(page)) - totalhigh_pages++; -#endif + adjust_managed_page_count(page, 1); } EXPORT_SYMBOL_GPL(__online_page_increment_counters); void __online_page_free(struct page *page) { - ClearPageReserved(page); - init_page_count(page); - __free_page(page); + __free_reserved_page(page); } EXPORT_SYMBOL_GPL(__online_page_free); @@ -983,7 +976,6 @@ int __ref online_pages(unsigned long pfn, unsigned long nr_pages, int online_typ return ret; } - zone->managed_pages += onlined_pages; zone->present_pages += onlined_pages; pgdat_resize_lock(zone->zone_pgdat, &flags); @@ -1572,15 +1564,13 @@ repeat: /* reset pagetype flags and makes migrate type to be MOVABLE */ undo_isolate_page_range(start_pfn, end_pfn, MIGRATE_MOVABLE); /* removal success */ - zone->managed_pages -= offlined_pages; + adjust_managed_page_count(pfn_to_page(start_pfn), -offlined_pages); zone->present_pages -= offlined_pages; pgdat_resize_lock(zone->zone_pgdat, &flags); zone->zone_pgdat->node_present_pages -= offlined_pages; pgdat_resize_unlock(zone->zone_pgdat, &flags); - totalram_pages -= offlined_pages; - init_per_zone_wmark_min(); if (!populated_zone(zone)) { diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 2437a7e17aba..1481439ee2e4 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -780,11 +780,7 @@ void __init init_cma_reserved_pageblock(struct page *page) set_page_refcounted(page); set_pageblock_migratetype(page, MIGRATE_CMA); __free_pages(page, pageblock_order); - totalram_pages += pageblock_nr_pages; -#ifdef CONFIG_HIGHMEM - if (PageHighMem(page)) - totalhigh_pages += pageblock_nr_pages; -#endif + adjust_managed_page_count(page, pageblock_nr_pages); } #endif @@ -5207,8 +5203,13 @@ void adjust_managed_page_count(struct page *page, long count) spin_lock(&managed_page_count_lock); page_zone(page)->managed_pages += count; totalram_pages += count; +#ifdef CONFIG_HIGHMEM + if (PageHighMem(page)) + totalhigh_pages += count; +#endif spin_unlock(&managed_page_count_lock); } +EXPORT_SYMBOL(adjust_managed_page_count); unsigned long free_reserved_area(void *start, void *end, int poison, char *s) { -- cgit v1.2.3 From 0c988534737a358fdff42fcce78f0ff1a12dbfc5 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:03:24 -0700 Subject: mm: concentrate modification of totalram_pages into the mm core Concentrate code to modify totalram_pages into the mm core, so the arch memory initialized code doesn't need to take care of it. With these changes applied, only following functions from mm core modify global variable totalram_pages: free_bootmem_late(), free_all_bootmem(), free_all_bootmem_node(), adjust_managed_page_count(). With this patch applied, it will be much more easier for us to keep totalram_pages and zone->managed_pages in consistence. Signed-off-by: Jiang Liu Acked-by: David Howells Cc: "H. Peter Anvin" Cc: "Michael S. Tsirkin" Cc: Cc: Arnd Bergmann Cc: Catalin Marinas Cc: Chris Metcalf Cc: Geert Uytterhoeven Cc: Ingo Molnar Cc: Jeremy Fitzhardinge Cc: Jianguo Wu Cc: Joonsoo Kim Cc: Kamezawa Hiroyuki Cc: Konrad Rzeszutek Wilk Cc: Marek Szyprowski Cc: Mel Gorman Cc: Michel Lespinasse Cc: Minchan Kim Cc: Rik van Riel Cc: Rusty Russell Cc: Tang Chen Cc: Tejun Heo Cc: Thomas Gleixner Cc: Wen Congyang Cc: Will Deacon Cc: Yasuaki Ishimatsu Cc: Yinghai Lu Cc: Russell King Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/alpha/mm/init.c | 2 +- arch/alpha/mm/numa.c | 2 +- arch/arc/mm/init.c | 2 +- arch/arm/mm/init.c | 3 +-- arch/arm64/mm/init.c | 2 +- arch/avr32/mm/init.c | 2 -- arch/blackfin/mm/init.c | 2 +- arch/c6x/mm/init.c | 2 +- arch/cris/mm/init.c | 2 +- arch/frv/mm/init.c | 2 +- arch/h8300/mm/init.c | 2 +- arch/hexagon/mm/init.c | 2 +- arch/ia64/mm/init.c | 2 +- arch/m32r/mm/init.c | 2 +- arch/m68k/mm/init.c | 4 ++-- arch/metag/mm/init.c | 5 +---- arch/microblaze/mm/init.c | 2 +- arch/mips/mm/init.c | 2 +- arch/mips/sgi-ip27/ip27-memory.c | 2 +- arch/mn10300/mm/init.c | 2 +- arch/openrisc/mm/init.c | 2 +- arch/parisc/mm/init.c | 4 ++-- arch/powerpc/mm/mem.c | 5 ++--- arch/s390/mm/init.c | 2 +- arch/score/mm/init.c | 2 +- arch/sh/mm/init.c | 2 +- arch/sparc/mm/init_32.c | 3 +-- arch/sparc/mm/init_64.c | 2 +- arch/tile/mm/init.c | 2 +- arch/um/kernel/mem.c | 2 +- arch/unicore32/mm/init.c | 2 +- arch/x86/mm/init_32.c | 2 +- arch/x86/mm/init_64.c | 2 +- arch/xtensa/mm/init.c | 2 +- mm/bootmem.c | 9 ++++++++- mm/nobootmem.c | 7 ++++++- 36 files changed, 50 insertions(+), 46 deletions(-) diff --git a/arch/alpha/mm/init.c b/arch/alpha/mm/init.c index 218c29c14bb3..eee47a453d7d 100644 --- a/arch/alpha/mm/init.c +++ b/arch/alpha/mm/init.c @@ -309,7 +309,7 @@ void __init mem_init(void) { max_mapnr = num_physpages = max_low_pfn; - totalram_pages += free_all_bootmem(); + free_all_bootmem(); high_memory = (void *) __va(max_low_pfn * PAGE_SIZE); printk_memory_info(); diff --git a/arch/alpha/mm/numa.c b/arch/alpha/mm/numa.c index 33885048fa36..857452c13c4d 100644 --- a/arch/alpha/mm/numa.c +++ b/arch/alpha/mm/numa.c @@ -334,7 +334,7 @@ void __init mem_init(void) /* * This will free up the bootmem, ie, slot 0 memory */ - totalram_pages += free_all_bootmem_node(NODE_DATA(nid)); + free_all_bootmem_node(NODE_DATA(nid)); pfn = NODE_DATA(nid)->node_start_pfn; for (i = 0; i < node_spanned_pages(nid); i++, pfn++) diff --git a/arch/arc/mm/init.c b/arch/arc/mm/init.c index f9c707712096..c668a600f652 100644 --- a/arch/arc/mm/init.c +++ b/arch/arc/mm/init.c @@ -111,7 +111,7 @@ void __init mem_init(void) high_memory = (void *)(CONFIG_LINUX_LINK_BASE + arc_mem_sz); - totalram_pages = free_all_bootmem(); + free_all_bootmem(); /* count all reserved pages [kernel code/data/mem_map..] */ reserved_pages = 0; diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 2070651c1bb4..06e9ce17d1d2 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -596,8 +596,7 @@ void __init mem_init(void) /* this will put all unused low memory onto the freelists */ free_unused_memmap(&meminfo); - - totalram_pages += free_all_bootmem(); + free_all_bootmem(); #ifdef CONFIG_SA1111 /* now that our DMA memory is actually so designated, we can free it */ diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c index a398eb9018bb..93de98afedd7 100644 --- a/arch/arm64/mm/init.c +++ b/arch/arm64/mm/init.c @@ -284,7 +284,7 @@ void __init mem_init(void) free_unused_memmap(); #endif - totalram_pages += free_all_bootmem(); + free_all_bootmem(); reserved_pages = free_pages = 0; diff --git a/arch/avr32/mm/init.c b/arch/avr32/mm/init.c index b079e04f6954..af6890fd7319 100644 --- a/arch/avr32/mm/init.c +++ b/arch/avr32/mm/init.c @@ -117,8 +117,6 @@ void __init mem_init(void) if (pgdat->node_spanned_pages != 0) node_pages = free_all_bootmem_node(pgdat); - totalram_pages += node_pages; - for (i = 0; i < node_pages; i++) if (PageReserved(pgdat->node_mem_map + i)) reservedpages++; diff --git a/arch/blackfin/mm/init.c b/arch/blackfin/mm/init.c index fa241f5a7dcf..c73d80ef564f 100644 --- a/arch/blackfin/mm/init.c +++ b/arch/blackfin/mm/init.c @@ -104,7 +104,7 @@ void __init mem_init(void) printk(KERN_DEBUG "Kernel managed physical pages: %lu\n", num_physpages); /* This will put all low memory onto the freelists. */ - totalram_pages = free_all_bootmem(); + free_all_bootmem(); reservedpages = 0; for (tmp = ARCH_PFN_OFFSET; tmp < max_mapnr; tmp++) diff --git a/arch/c6x/mm/init.c b/arch/c6x/mm/init.c index 3987a20fdee6..c9ae8ce731d5 100644 --- a/arch/c6x/mm/init.c +++ b/arch/c6x/mm/init.c @@ -65,7 +65,7 @@ void __init mem_init(void) high_memory = (void *)(memory_end & PAGE_MASK); /* this will put all memory onto the freelists */ - totalram_pages = free_all_bootmem(); + free_all_bootmem(); codek = (_etext - _stext) >> 10; datak = (_end - _sdata) >> 10; diff --git a/arch/cris/mm/init.c b/arch/cris/mm/init.c index 8fec26392ae7..52b8b56ae305 100644 --- a/arch/cris/mm/init.c +++ b/arch/cris/mm/init.c @@ -33,7 +33,7 @@ mem_init(void) max_mapnr = num_physpages = max_low_pfn - min_low_pfn; /* this will put all memory onto the freelists */ - totalram_pages = free_all_bootmem(); + free_all_bootmem(); reservedpages = 0; for (tmp = 0; tmp < max_mapnr; tmp++) { diff --git a/arch/frv/mm/init.c b/arch/frv/mm/init.c index 8ba9d22d0d91..3dcc88803a4f 100644 --- a/arch/frv/mm/init.c +++ b/arch/frv/mm/init.c @@ -123,7 +123,7 @@ void __init mem_init(void) int codek = 0, datak = 0; /* this will put all low memory onto the freelists */ - totalram_pages = free_all_bootmem(); + free_all_bootmem(); #ifdef CONFIG_MMU for (loop = 0 ; loop < npages ; loop++) diff --git a/arch/h8300/mm/init.c b/arch/h8300/mm/init.c index c831f1dba132..a506dd4724e0 100644 --- a/arch/h8300/mm/init.c +++ b/arch/h8300/mm/init.c @@ -140,7 +140,7 @@ void __init mem_init(void) max_mapnr = num_physpages = MAP_NR(high_memory); /* this will put all low memory onto the freelists */ - totalram_pages = free_all_bootmem(); + free_all_bootmem(); codek = (_etext - _stext) >> 10; datak = (__bss_stop - _sdata) >> 10; diff --git a/arch/hexagon/mm/init.c b/arch/hexagon/mm/init.c index 2561d259a296..0ab5b4350e93 100644 --- a/arch/hexagon/mm/init.c +++ b/arch/hexagon/mm/init.c @@ -70,7 +70,7 @@ unsigned long long kmap_generation; void __init mem_init(void) { /* No idea where this is actually declared. Seems to evade LXR. */ - totalram_pages += free_all_bootmem(); + free_all_bootmem(); num_physpages = bootmem_lastpg-ARCH_PFN_OFFSET; printk(KERN_INFO "totalram_pages = %ld\n", totalram_pages); diff --git a/arch/ia64/mm/init.c b/arch/ia64/mm/init.c index f8a4f38b0ad5..d141f7ea0be5 100644 --- a/arch/ia64/mm/init.c +++ b/arch/ia64/mm/init.c @@ -622,7 +622,7 @@ mem_init (void) for_each_online_pgdat(pgdat) if (pgdat->bdata->node_bootmem_map) - totalram_pages += free_all_bootmem_node(pgdat); + free_all_bootmem_node(pgdat); reserved_pages = 0; efi_memmap_walk(count_reserved_pages, &reserved_pages); diff --git a/arch/m32r/mm/init.c b/arch/m32r/mm/init.c index cca87d918436..a501838233ab 100644 --- a/arch/m32r/mm/init.c +++ b/arch/m32r/mm/init.c @@ -158,7 +158,7 @@ void __init mem_init(void) /* this will put all low memory onto the freelists */ for_each_online_node(nid) - totalram_pages += free_all_bootmem_node(NODE_DATA(nid)); + free_all_bootmem_node(NODE_DATA(nid)); reservedpages = reservedpages_count() - hole_pages; codesize = (unsigned long) &_etext - (unsigned long)&_text; diff --git a/arch/m68k/mm/init.c b/arch/m68k/mm/init.c index ab0b54ca5d85..614c60a04459 100644 --- a/arch/m68k/mm/init.c +++ b/arch/m68k/mm/init.c @@ -155,11 +155,11 @@ void __init mem_init(void) int i; /* this will put all memory onto the freelists */ - totalram_pages = num_physpages = 0; + num_physpages = 0; for_each_online_pgdat(pgdat) { num_physpages += pgdat->node_present_pages; - totalram_pages += free_all_bootmem_node(pgdat); + free_all_bootmem_node(pgdat); for (i = 0; i < pgdat->node_spanned_pages; i++) { struct page *page = pgdat->node_mem_map + i; char *addr = page_to_virt(page); diff --git a/arch/metag/mm/init.c b/arch/metag/mm/init.c index d7595f58fad5..ce81d7c43983 100644 --- a/arch/metag/mm/init.c +++ b/arch/metag/mm/init.c @@ -393,14 +393,11 @@ void __init mem_init(void) for_each_online_node(nid) { pg_data_t *pgdat = NODE_DATA(nid); - unsigned long node_pages = 0; num_physpages += pgdat->node_present_pages; if (pgdat->node_spanned_pages) - node_pages = free_all_bootmem_node(pgdat); - - totalram_pages += node_pages; + free_all_bootmem_node(pgdat); } pr_info("Memory: %luk/%luk available\n", diff --git a/arch/microblaze/mm/init.c b/arch/microblaze/mm/init.c index d149e0ebb767..b384cbc2c8f2 100644 --- a/arch/microblaze/mm/init.c +++ b/arch/microblaze/mm/init.c @@ -252,7 +252,7 @@ void __init mem_init(void) high_memory = (void *)__va(memory_start + lowmem_size - 1); /* this will put all memory onto the freelists */ - totalram_pages += free_all_bootmem(); + free_all_bootmem(); for_each_online_pgdat(pgdat) { unsigned long i; diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c index 268f2a94031b..e7333f15b1b7 100644 --- a/arch/mips/mm/init.c +++ b/arch/mips/mm/init.c @@ -374,7 +374,7 @@ void __init mem_init(void) #endif high_memory = (void *) __va(max_low_pfn << PAGE_SHIFT); - totalram_pages += free_all_bootmem(); + free_all_bootmem(); setup_zero_pages(); /* Setup zeroed pages. */ reservedpages = ram = 0; diff --git a/arch/mips/sgi-ip27/ip27-memory.c b/arch/mips/sgi-ip27/ip27-memory.c index 1230f56429d7..aecac4a08360 100644 --- a/arch/mips/sgi-ip27/ip27-memory.c +++ b/arch/mips/sgi-ip27/ip27-memory.c @@ -489,7 +489,7 @@ void __init mem_init(void) /* * This will free up the bootmem, ie, slot 0 memory. */ - totalram_pages += free_all_bootmem_node(NODE_DATA(node)); + free_all_bootmem_node(NODE_DATA(node)); } setup_zero_pages(); /* This comes from node 0 */ diff --git a/arch/mn10300/mm/init.c b/arch/mn10300/mm/init.c index e19049d1f2b9..7590d91627f2 100644 --- a/arch/mn10300/mm/init.c +++ b/arch/mn10300/mm/init.c @@ -114,7 +114,7 @@ void __init mem_init(void) memset(empty_zero_page, 0, PAGE_SIZE); /* this will put all low memory onto the freelists */ - totalram_pages += free_all_bootmem(); + free_all_bootmem(); reservedpages = 0; for (tmp = 0; tmp < num_physpages; tmp++) diff --git a/arch/openrisc/mm/init.c b/arch/openrisc/mm/init.c index c371e4a0fcac..16c1e135cf34 100644 --- a/arch/openrisc/mm/init.c +++ b/arch/openrisc/mm/init.c @@ -207,7 +207,7 @@ static int __init free_pages_init(void) int reservedpages, pfn; /* this will put all low memory onto the freelists */ - totalram_pages = free_all_bootmem(); + free_all_bootmem(); reservedpages = 0; for (pfn = 0; pfn < max_low_pfn; pfn++) { diff --git a/arch/parisc/mm/init.c b/arch/parisc/mm/init.c index ebac7bd76b56..d8aaaf06ede2 100644 --- a/arch/parisc/mm/init.c +++ b/arch/parisc/mm/init.c @@ -593,13 +593,13 @@ void __init mem_init(void) #ifndef CONFIG_DISCONTIGMEM max_mapnr = page_to_pfn(virt_to_page(high_memory - 1)) + 1; - totalram_pages += free_all_bootmem(); + free_all_bootmem(); #else { int i; for (i = 0; i < npmem_ranges; i++) - totalram_pages += free_all_bootmem_node(NODE_DATA(i)); + free_all_bootmem_node(NODE_DATA(i)); } #endif diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c index 7f47a05f55af..3bcfc0d0d322 100644 --- a/arch/powerpc/mm/mem.c +++ b/arch/powerpc/mm/mem.c @@ -318,13 +318,12 @@ void __init mem_init(void) for_each_online_node(nid) { if (NODE_DATA(nid)->node_spanned_pages != 0) { printk("freeing bootmem node %d\n", nid); - totalram_pages += - free_all_bootmem_node(NODE_DATA(nid)); + free_all_bootmem_node(NODE_DATA(nid)); } } #else max_mapnr = max_pfn; - totalram_pages += free_all_bootmem(); + free_all_bootmem(); #endif for_each_online_pgdat(pgdat) { for (i = 0; i < pgdat->node_spanned_pages; i++) { diff --git a/arch/s390/mm/init.c b/arch/s390/mm/init.c index bf01d18422ec..a2aafe1b2300 100644 --- a/arch/s390/mm/init.c +++ b/arch/s390/mm/init.c @@ -144,7 +144,7 @@ void __init mem_init(void) cmma_init(); /* this will put all low memory onto the freelists */ - totalram_pages += free_all_bootmem(); + free_all_bootmem(); setup_zero_pages(); /* Setup zeroed pages. */ reservedpages = 0; diff --git a/arch/score/mm/init.c b/arch/score/mm/init.c index f5dd61eb4544..a8b917742dec 100644 --- a/arch/score/mm/init.c +++ b/arch/score/mm/init.c @@ -79,7 +79,7 @@ void __init mem_init(void) unsigned long tmp, ram = 0; high_memory = (void *) __va(max_low_pfn << PAGE_SHIFT); - totalram_pages += free_all_bootmem(); + free_all_bootmem(); setup_zero_page(); /* Setup zeroed pages. */ reservedpages = 0; diff --git a/arch/sh/mm/init.c b/arch/sh/mm/init.c index d3af56b7a098..fc0c8e1c32a7 100644 --- a/arch/sh/mm/init.c +++ b/arch/sh/mm/init.c @@ -422,7 +422,7 @@ void __init mem_init(void) num_physpages += pgdat->node_present_pages; if (pgdat->node_spanned_pages) - totalram_pages += free_all_bootmem_node(pgdat); + free_all_bootmem_node(pgdat); node_high_memory = (void *)__va((pgdat->node_start_pfn + diff --git a/arch/sparc/mm/init_32.c b/arch/sparc/mm/init_32.c index d5f9c023826f..a438abb5495e 100644 --- a/arch/sparc/mm/init_32.c +++ b/arch/sparc/mm/init_32.c @@ -323,8 +323,7 @@ void __init mem_init(void) max_mapnr = last_valid_pfn - pfn_base; high_memory = __va(max_low_pfn << PAGE_SHIFT); - - totalram_pages = free_all_bootmem(); + free_all_bootmem(); for (i = 0; sp_banks[i].num_bytes != 0; i++) { unsigned long start_pfn = sp_banks[i].base_addr >> PAGE_SHIFT; diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c index 8269deb84eda..752d73837f9e 100644 --- a/arch/sparc/mm/init_64.c +++ b/arch/sparc/mm/init_64.c @@ -2061,7 +2061,7 @@ void __init mem_init(void) high_memory = __va(last_valid_pfn << PAGE_SHIFT); register_page_bootmem_info(); - totalram_pages = free_all_bootmem(); + free_all_bootmem(); /* We subtract one to account for the mem_map_zero page * allocated below. diff --git a/arch/tile/mm/init.c b/arch/tile/mm/init.c index ccfeb3f2e769..45ce26d4e474 100644 --- a/arch/tile/mm/init.c +++ b/arch/tile/mm/init.c @@ -846,7 +846,7 @@ void __init mem_init(void) set_max_mapnr_init(); /* this will put all bootmem onto the freelists */ - totalram_pages += free_all_bootmem(); + free_all_bootmem(); #ifndef CONFIG_64BIT /* count all remaining LOWMEM and give all HIGHMEM to page allocator */ diff --git a/arch/um/kernel/mem.c b/arch/um/kernel/mem.c index 8ff0b7ae8ec0..b0c763094ffb 100644 --- a/arch/um/kernel/mem.c +++ b/arch/um/kernel/mem.c @@ -65,7 +65,7 @@ void __init mem_init(void) uml_reserved = brk_end; /* this will put all low memory onto the freelists */ - totalram_pages = free_all_bootmem(); + free_all_bootmem(); max_low_pfn = totalram_pages; #ifdef CONFIG_HIGHMEM setup_highmem(end_iomem, highmem); diff --git a/arch/unicore32/mm/init.c b/arch/unicore32/mm/init.c index df9b8abcb6a5..7d1356c466b9 100644 --- a/arch/unicore32/mm/init.c +++ b/arch/unicore32/mm/init.c @@ -392,7 +392,7 @@ void __init mem_init(void) free_unused_memmap(&meminfo); /* this will put all unused low memory onto the freelists */ - totalram_pages += free_all_bootmem(); + free_all_bootmem(); reserved_pages = free_pages = 0; diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c index 3ac7e319918d..9fa46baada27 100644 --- a/arch/x86/mm/init_32.c +++ b/arch/x86/mm/init_32.c @@ -759,7 +759,7 @@ void __init mem_init(void) set_highmem_pages_init(); /* this will put all low memory onto the freelists */ - totalram_pages += free_all_bootmem(); + free_all_bootmem(); reservedpages = 0; for (tmp = 0; tmp < max_low_pfn; tmp++) diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index ec312a92b137..9577638f3ead 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -1054,7 +1054,7 @@ void __init mem_init(void) register_page_bootmem_info(); /* this will put all memory onto the freelists */ - totalram_pages = free_all_bootmem(); + free_all_bootmem(); absent_pages = absent_pages_in_range(0, max_pfn); reservedpages = max_pfn - totalram_pages - absent_pages; diff --git a/arch/xtensa/mm/init.c b/arch/xtensa/mm/init.c index 026d29bee30b..663c1619562c 100644 --- a/arch/xtensa/mm/init.c +++ b/arch/xtensa/mm/init.c @@ -184,7 +184,7 @@ void __init mem_init(void) #error HIGHGMEM not implemented in init.c #endif - totalram_pages += free_all_bootmem(); + free_all_bootmem(); reservedpages = ram = 0; for (tmp = 0; tmp < max_mapnr; tmp++) { diff --git a/mm/bootmem.c b/mm/bootmem.c index eb792323187b..58609bbf584e 100644 --- a/mm/bootmem.c +++ b/mm/bootmem.c @@ -271,9 +271,14 @@ void __init reset_all_zones_managed_pages(void) */ unsigned long __init free_all_bootmem_node(pg_data_t *pgdat) { + unsigned long pages; + register_page_bootmem_info_node(pgdat); reset_node_managed_pages(pgdat); - return free_all_bootmem_core(pgdat->bdata); + pages = free_all_bootmem_core(pgdat->bdata); + totalram_pages += pages; + + return pages; } /** @@ -291,6 +296,8 @@ unsigned long __init free_all_bootmem(void) list_for_each_entry(bdata, &bdata_list, list) total_pages += free_all_bootmem_core(bdata); + totalram_pages += total_pages; + return total_pages; } diff --git a/mm/nobootmem.c b/mm/nobootmem.c index 0ae8d91365af..61107cf55bb3 100644 --- a/mm/nobootmem.c +++ b/mm/nobootmem.c @@ -165,6 +165,8 @@ void __init reset_all_zones_managed_pages(void) */ unsigned long __init free_all_bootmem(void) { + unsigned long pages; + reset_all_zones_managed_pages(); /* @@ -172,7 +174,10 @@ unsigned long __init free_all_bootmem(void) * because in some case like Node0 doesn't have RAM installed * low ram will be on Node1 */ - return free_low_memory_core_early(); + pages = free_low_memory_core_early(); + totalram_pages += pages; + + return pages; } /** -- cgit v1.2.3 From cdd91a77043ba81585236ef61f65c18222b212e6 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:03:27 -0700 Subject: mm: report available pages as "MemTotal" for each NUMA node As reported by https://bugzilla.kernel.org/show_bug.cgi?id=53501, "MemTotal" from /proc/meminfo means memory pages managed by the buddy system (managed_pages), but "MemTotal" from /sys/.../node/nodex/meminfo means physical pages present (present_pages) within the NUMA node. There's a difference between managed_pages and present_pages due to bootmem allocator and reserved pages. And Documentation/filesystems/proc.txt says MemTotal: Total usable ram (i.e. physical ram minus a few reserved bits and the kernel binary code) So change /sys/.../node/nodex/meminfo to report available pages within the node as "MemTotal". Signed-off-by: Jiang Liu Reported-by: Cc: Mel Gorman Cc: Minchan Kim Cc: "H. Peter Anvin" Cc: "Michael S. Tsirkin" Cc: Arnd Bergmann Cc: Catalin Marinas Cc: Chris Metcalf Cc: David Howells Cc: Geert Uytterhoeven Cc: Ingo Molnar Cc: Jeremy Fitzhardinge Cc: Jianguo Wu Cc: Joonsoo Kim Cc: Kamezawa Hiroyuki Cc: Konrad Rzeszutek Wilk Cc: Marek Szyprowski Cc: Michel Lespinasse Cc: Rik van Riel Cc: Rusty Russell Cc: Tang Chen Cc: Tejun Heo Cc: Thomas Gleixner Cc: Wen Congyang Cc: Will Deacon Cc: Yasuaki Ishimatsu Cc: Yinghai Lu Cc: Russell King Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/page_alloc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 1481439ee2e4..d9445c4f5fd7 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -2904,9 +2904,13 @@ EXPORT_SYMBOL(si_meminfo); #ifdef CONFIG_NUMA void si_meminfo_node(struct sysinfo *val, int nid) { + int zone_type; /* needs to be signed */ + unsigned long managed_pages = 0; pg_data_t *pgdat = NODE_DATA(nid); - val->totalram = pgdat->node_present_pages; + for (zone_type = 0; zone_type < MAX_NR_ZONES; zone_type++) + managed_pages += pgdat->node_zones[zone_type].managed_pages; + val->totalram = managed_pages; val->freeram = node_page_state(nid, NR_FREE_PAGES); #ifdef CONFIG_HIGHMEM val->totalhigh = pgdat->node_zones[ZONE_HIGHMEM].managed_pages; -- cgit v1.2.3 From f60e2a968e2bebe34986f49251017f72b725d8c0 Mon Sep 17 00:00:00 2001 From: Sergey Dyasly Date: Wed, 3 Jul 2013 15:03:30 -0700 Subject: memcg: Kconfig info update Now there are only 2 members in struct page_cgroup. Update config MEMCG description accordingly. Signed-off-by: Sergey Dyasly Acked-by: Michal Hocko Acked-by: KOSAKI Motohiro Acked-by: Johannes Weiner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- init/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/init/Kconfig b/init/Kconfig index 118895cc1f67..ef10d83bc379 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -899,7 +899,7 @@ config MEMCG Note that setting this option increases fixed memory overhead associated with each page of memory in the system. By this, - 20(40)bytes/PAGE_SIZE on 32(64)bit system will be occupied by memory + 8(16)bytes/PAGE_SIZE on 32(64)bit system will be occupied by memory usage tracking struct at boot. Total amount of this is printed out at boot. -- cgit v1.2.3 From e6c495a96ce02574e765d5140039a64c8d4e8c9e Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Wed, 3 Jul 2013 15:03:31 -0700 Subject: mm: fix the TLB range flushed when __tlb_remove_page() runs out of slots zap_pte_range loops from @addr to @end. In the middle, if it runs out of batching slots, TLB entries needs to be flushed for @start to @interim, NOT @interim to @end. Since ARC port doesn't use page free batching I can't test it myself but this seems like the right thing to do. Observed this when working on a fix for the issue at thread: http://www.spinics.net/lists/linux-arch/msg21736.html Signed-off-by: Vineet Gupta Cc: Mel Gorman Cc: Hugh Dickins Cc: Rik van Riel Cc: David Rientjes Cc: Peter Zijlstra Acked-by: Catalin Marinas Cc: Max Filippov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/memory.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/mm/memory.c b/mm/memory.c index a101bbcacfd7..407533219673 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1101,6 +1101,7 @@ static unsigned long zap_pte_range(struct mmu_gather *tlb, spinlock_t *ptl; pte_t *start_pte; pte_t *pte; + unsigned long range_start = addr; again: init_rss_vec(rss); @@ -1206,12 +1207,14 @@ again: force_flush = 0; #ifdef HAVE_GENERIC_MMU_GATHER - tlb->start = addr; - tlb->end = end; + tlb->start = range_start; + tlb->end = addr; #endif tlb_flush_mmu(tlb); - if (addr != end) + if (addr != end) { + range_start = addr; goto again; + } } return addr; -- cgit v1.2.3 From 1622d1abdf7b554eb7cc40c0d08d989176732242 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:03:33 -0700 Subject: vmlinux.lds: add comments for global variables and clean up useless declarations The original goal of this patchset is to fix the bug reported by https://bugzilla.kernel.org/show_bug.cgi?id=53501 Now it has also been expanded to reduce common code used by memory initializion. Patch 1-7: 1) add comments for global variables exported by vmlinux.lds 2) normalize global variables exported by vmlinux.lds Patch 8: Introduce helper functions mem_init_print_info() and get_num_physpages() Patch 9: Avoid using global variable num_physpages at runtime Patch 10: Don't update num_physpages in memory_hotplug.c Patch 11-40: Modify arch mm initialization code to: 1) Simplify mem_init() by using mem_init_print_info() 2) Prepare for killing global variable num_physpages Patch 41: Kill the global variable num_physpages With all patches applied, mem_init(), free_initmem(), free_initrd_mem() could be as simple as below. This patch series has reduced about 1.2K lines of code in total. #ifndef CONFIG_DISCONTIGMEM void __init mem_init(void) { max_mapnr = max_low_pfn; free_all_bootmem(); high_memory = (void *) __va(max_low_pfn * PAGE_SIZE); mem_init_print_info(NULL); } #endif /* CONFIG_DISCONTIGMEM */ void free_initmem(void) { free_initmem_default(-1); } #ifdef CONFIG_BLK_DEV_INITRD void free_initrd_mem(unsigned long start, unsigned long end) { free_reserved_area(start, end, -1, "initrd"); } #endif Due to hardware resource limitations, I have only tested this on x86_64. And the messages reported on an x86_64 system are: Log message before applying patches: Memory: 7745676k/8910848k available (6934k kernel code, 836024k absent, 329148k reserved, 6343k data, 1012k init) Log message after applying patches: Memory: 7744624K/8074824K available (6969K kernel code, 1011K data, 2828K rodata, 1016K init, 9640K bss, 330200K reserved) Great thanks to Vineet Gupta for testing on ARC. This patch: Document global variables exported from vmlinux.lds. 1) Add comments about usage guidelines for global variables exported from vmlinux.lds.S. 2) Remove unused __initdata_begin[] and __initdata_end[]. Signed-off-by: Jiang Liu Acked-by: Arnd Bergmann Cc: Arnd Bergmann Cc: Vineet Gupta Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-generic/sections.h | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/include/asm-generic/sections.h b/include/asm-generic/sections.h index c1a1216e29ce..f1a24b5c3b90 100644 --- a/include/asm-generic/sections.h +++ b/include/asm-generic/sections.h @@ -3,6 +3,26 @@ /* References to section boundaries */ +/* + * Usage guidelines: + * _text, _data: architecture specific, don't use them in arch-independent code + * [_stext, _etext]: contains .text.* sections, may also contain .rodata.* + * and/or .init.* sections + * [_sdata, _edata]: contains .data.* sections, may also contain .rodata.* + * and/or .init.* sections. + * [__start_rodata, __end_rodata]: contains .rodata.* sections + * [__init_begin, __init_end]: contains .init.* sections, but .init.text.* + * may be out of this range on some architectures. + * [_sinittext, _einittext]: contains .init.text.* sections + * [__bss_start, __bss_stop]: contains BSS sections + * + * Following global variables are optional and may be unavailable on some + * architectures and/or kernel configurations. + * _text, _data + * __kprobes_text_start, __kprobes_text_end + * __entry_text_start, __entry_text_end + * __ctors_start, __ctors_end + */ extern char _text[], _stext[], _etext[]; extern char _data[], _sdata[], _edata[]; extern char __bss_start[], __bss_stop[]; @@ -12,7 +32,6 @@ extern char _end[]; extern char __per_cpu_load[], __per_cpu_start[], __per_cpu_end[]; extern char __kprobes_text_start[], __kprobes_text_end[]; extern char __entry_text_start[], __entry_text_end[]; -extern char __initdata_begin[], __initdata_end[]; extern char __start_rodata[], __end_rodata[]; /* Start and end of .ctors section - used for constructor calls. */ -- cgit v1.2.3 From 2e555f8d0f81d8e28a63fe432ec7bcc26e95006b Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:03:34 -0700 Subject: avr32: normalize global variables exported by vmlinux.lds Normalize global variables exported by vmlinux.lds to conform usage guidelines from include/asm-generic/sections.h. Use _text to mark the start of the kernel image including the head text, and _stext to mark the start of the .text section. Signed-off-by: Jiang Liu Acked-by: Hans-Christian Egtvedt Cc: Haavard Skinnemoen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/avr32/kernel/setup.c | 2 +- arch/avr32/kernel/vmlinux.lds.S | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/avr32/kernel/setup.c b/arch/avr32/kernel/setup.c index b4247f478065..209ae5ad3495 100644 --- a/arch/avr32/kernel/setup.c +++ b/arch/avr32/kernel/setup.c @@ -555,7 +555,7 @@ void __init setup_arch (char **cmdline_p) { struct clk *cpu_clk; - init_mm.start_code = (unsigned long)_text; + init_mm.start_code = (unsigned long)_stext; init_mm.end_code = (unsigned long)_etext; init_mm.end_data = (unsigned long)_edata; init_mm.brk = (unsigned long)_end; diff --git a/arch/avr32/kernel/vmlinux.lds.S b/arch/avr32/kernel/vmlinux.lds.S index 9cd2bd91d64a..a4589176bed5 100644 --- a/arch/avr32/kernel/vmlinux.lds.S +++ b/arch/avr32/kernel/vmlinux.lds.S @@ -23,7 +23,7 @@ SECTIONS { . = CONFIG_ENTRY_ADDRESS; .init : AT(ADDR(.init) - LOAD_OFFSET) { - _stext = .; + _text = .; __init_begin = .; _sinittext = .; *(.text.reset) @@ -46,7 +46,7 @@ SECTIONS .text : AT(ADDR(.text) - LOAD_OFFSET) { _evba = .; - _text = .; + _stext = .; *(.ex.text) *(.irq.text) KPROBES_TEXT -- cgit v1.2.3 From 06256f8f719917581a221221bb851fcf88de564b Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:03:35 -0700 Subject: c6x: normalize global variables exported by vmlinux.lds Normalize global variables exported by vmlinux.lds to conform usage guidelines from include/asm-generic/sections.h. Use _text to mark the start of the kernel image including the head text, and _stext to mark the start of the .text section. This patch also fixes possible bugs due to current address layout that [__init_begin, __init_end] is a sub-range of [_stext, _etext] and pages within range [__init_begin, __init_end] will be freed by free_initmem(). Signed-off-by: Jiang Liu Cc: Mark Salter Cc: Aurelien Jacquiot Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/c6x/kernel/vmlinux.lds.S | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/c6x/kernel/vmlinux.lds.S b/arch/c6x/kernel/vmlinux.lds.S index 1d81c4c129ec..279d80725128 100644 --- a/arch/c6x/kernel/vmlinux.lds.S +++ b/arch/c6x/kernel/vmlinux.lds.S @@ -54,16 +54,15 @@ SECTIONS } . = ALIGN(PAGE_SIZE); + __init_begin = .; .init : { - _stext = .; _sinittext = .; HEAD_TEXT INIT_TEXT _einittext = .; } - __init_begin = _stext; INIT_DATA_SECTION(16) PERCPU_SECTION(128) @@ -74,6 +73,7 @@ SECTIONS .text : { _text = .; + _stext = .; TEXT_TEXT SCHED_TEXT LOCK_TEXT -- cgit v1.2.3 From 5dd7cd11a0dde589e3532cfdc06372a1af9fdba7 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:03:36 -0700 Subject: h8300: normalize global variables exported by vmlinux.lds Generate mandatory global variables __bss_start/__bss_stop in file vmlinux.lds. Also remove one unused declaration of _text. Signed-off-by: Jiang Liu Cc: Yoshinori Sato Cc: Jiang Liu Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/h8300/boot/compressed/misc.c | 1 - arch/h8300/kernel/vmlinux.lds.S | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/h8300/boot/compressed/misc.c b/arch/h8300/boot/compressed/misc.c index 51ab6cbd030f..4a1e3dd43948 100644 --- a/arch/h8300/boot/compressed/misc.c +++ b/arch/h8300/boot/compressed/misc.c @@ -79,7 +79,6 @@ static void error(char *m); int puts(const char *); -extern int _text; /* Defined in vmlinux.lds.S */ extern int _end; static unsigned long free_mem_ptr; static unsigned long free_mem_end_ptr; diff --git a/arch/h8300/kernel/vmlinux.lds.S b/arch/h8300/kernel/vmlinux.lds.S index 03d356d96e5d..3253fed42ac1 100644 --- a/arch/h8300/kernel/vmlinux.lds.S +++ b/arch/h8300/kernel/vmlinux.lds.S @@ -132,10 +132,12 @@ SECTIONS { . = ALIGN(0x4) ; __sbss = . ; + ___bss_start = . ; *(.bss*) . = ALIGN(0x4) ; *(COMMON) . = ALIGN(0x4) ; + ___bss_stop = . ; __ebss = . ; __end = . ; __ramstart = .; -- cgit v1.2.3 From ae49b83dcacfb69e22092cab688c415c2f2d870c Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:03:37 -0700 Subject: score: normalize global variables exported by vmlinux.lds Generate mandatory global variables _sdata in file vmlinux.lds. Signed-off-by: Jiang Liu Cc: Chen Liqin Cc: Lennox Wu Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/score/kernel/vmlinux.lds.S | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/score/kernel/vmlinux.lds.S b/arch/score/kernel/vmlinux.lds.S index eebcbaa4e978..7274b5c4287e 100644 --- a/arch/score/kernel/vmlinux.lds.S +++ b/arch/score/kernel/vmlinux.lds.S @@ -49,6 +49,7 @@ SECTIONS } . = ALIGN(16); + _sdata = .; /* Start of data section */ RODATA EXCEPTION_TABLE(16) -- cgit v1.2.3 From 40a3b8df7be3b813cef7e32f74dea398274c2d47 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:03:39 -0700 Subject: tile: normalize global variables exported by vmlinux.lds Normalize global variables exported by vmlinux.lds to conform usage guidelines from include/asm-generic/sections.h. 1) Use _text to mark the start of the kernel image including the head text, and _stext to mark the start of the .text section. 2) Export mandatory global variables __init_begin and __init_end. Signed-off-by: Jiang Liu Acked-by: Chris Metcalf Cc: Rusty Russell Cc: Bjorn Helgaas Cc: "David S. Miller" Cc: Wen Congyang Cc: David Howells Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/tile/include/asm/sections.h | 2 +- arch/tile/kernel/setup.c | 4 ++-- arch/tile/kernel/vmlinux.lds.S | 4 +++- arch/tile/mm/init.c | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/arch/tile/include/asm/sections.h b/arch/tile/include/asm/sections.h index d062d463fca9..7d8a935a9238 100644 --- a/arch/tile/include/asm/sections.h +++ b/arch/tile/include/asm/sections.h @@ -34,7 +34,7 @@ extern char __sys_cmpxchg_grab_lock[]; extern char __start_atomic_asm_code[], __end_atomic_asm_code[]; #endif -/* Handle the discontiguity between _sdata and _stext. */ +/* Handle the discontiguity between _sdata and _text. */ static inline int arch_is_kernel_data(unsigned long addr) { return addr >= (unsigned long)_sdata && diff --git a/arch/tile/kernel/setup.c b/arch/tile/kernel/setup.c index 7a5aa1a7864e..41818e3ce97e 100644 --- a/arch/tile/kernel/setup.c +++ b/arch/tile/kernel/setup.c @@ -307,8 +307,8 @@ static void __cpuinit store_permanent_mappings(void) hv_store_mapping(addr, pages << PAGE_SHIFT, pa); } - hv_store_mapping((HV_VirtAddr)_stext, - (uint32_t)(_einittext - _stext), 0); + hv_store_mapping((HV_VirtAddr)_text, + (uint32_t)(_einittext - _text), 0); } /* diff --git a/arch/tile/kernel/vmlinux.lds.S b/arch/tile/kernel/vmlinux.lds.S index 631f10de12fe..a13ed902afbb 100644 --- a/arch/tile/kernel/vmlinux.lds.S +++ b/arch/tile/kernel/vmlinux.lds.S @@ -27,7 +27,6 @@ SECTIONS .intrpt1 (LOAD_OFFSET) : AT ( 0 ) /* put at the start of physical memory */ { _text = .; - _stext = .; *(.intrpt1) } :intrpt1 =0 @@ -36,6 +35,7 @@ SECTIONS /* Now the real code */ . = ALIGN(0x20000); + _stext = .; .text : AT (ADDR(.text) - LOAD_OFFSET) { HEAD_TEXT SCHED_TEXT @@ -58,11 +58,13 @@ SECTIONS #define LOAD_OFFSET PAGE_OFFSET . = ALIGN(PAGE_SIZE); + __init_begin = .; VMLINUX_SYMBOL(_sinitdata) = .; INIT_DATA_SECTION(16) :data =0 PERCPU_SECTION(L2_CACHE_BYTES) . = ALIGN(PAGE_SIZE); VMLINUX_SYMBOL(_einitdata) = .; + __init_end = .; _sdata = .; /* Start of data section */ diff --git a/arch/tile/mm/init.c b/arch/tile/mm/init.c index 45ce26d4e474..f2ac2f405775 100644 --- a/arch/tile/mm/init.c +++ b/arch/tile/mm/init.c @@ -562,7 +562,7 @@ static void __init kernel_physical_mapping_init(pgd_t *pgd_base) prot = ktext_set_nocache(prot); } - BUG_ON(address != (unsigned long)_stext); + BUG_ON(address != (unsigned long)_text); pte = NULL; for (; address < (unsigned long)_einittext; pfn++, address += PAGE_SIZE) { -- cgit v1.2.3 From a214a8c68bcdef2fb0803425f7fe36fe41030d3f Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:03:40 -0700 Subject: UML: normalize global variables exported by vmlinux.lds Normalize global variables exported by vmlinux.lds to conform usage guidelines from include/asm-generic/sections.h. 1) Use _text to mark the start of the kernel image including the head text, and _stext to mark the start of the .text section. 2) Export mandatory global variables __bss_stop. 3) Adjust __init_begin and __init_end to avoid acrossing .text and .data sections. Signed-off-by: Jiang Liu Cc: Jeff Dike Cc: Richard Weinberger Cc: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/um/include/asm/common.lds.S | 1 - arch/um/kernel/dyn.lds.S | 6 ++++-- arch/um/kernel/uml.lds.S | 7 +++++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/arch/um/include/asm/common.lds.S b/arch/um/include/asm/common.lds.S index 4938de5512d2..1dd5bd8a8c59 100644 --- a/arch/um/include/asm/common.lds.S +++ b/arch/um/include/asm/common.lds.S @@ -57,7 +57,6 @@ *(.uml.initcall.init) __uml_initcall_end = .; } - __init_end = .; SECURITY_INIT diff --git a/arch/um/kernel/dyn.lds.S b/arch/um/kernel/dyn.lds.S index fb8fd6fb6563..adde088aeeff 100644 --- a/arch/um/kernel/dyn.lds.S +++ b/arch/um/kernel/dyn.lds.S @@ -14,8 +14,6 @@ SECTIONS __binary_start = .; . = ALIGN(4096); /* Init code and data */ _text = .; - _stext = .; - __init_begin = .; INIT_TEXT_SECTION(PAGE_SIZE) . = ALIGN(PAGE_SIZE); @@ -67,6 +65,7 @@ SECTIONS } =0x90909090 .plt : { *(.plt) } .text : { + _stext = .; TEXT_TEXT SCHED_TEXT LOCK_TEXT @@ -91,7 +90,9 @@ SECTIONS #include + __init_begin = .; init.data : { INIT_DATA } + __init_end = .; /* Ensure the __preinit_array_start label is properly aligned. We could instead move the label definition inside the section, but @@ -155,6 +156,7 @@ SECTIONS . = ALIGN(32 / 8); . = ALIGN(32 / 8); } + __bss_stop = .; _end = .; PROVIDE (end = .); diff --git a/arch/um/kernel/uml.lds.S b/arch/um/kernel/uml.lds.S index ff65fb4f1a95..6899195602b7 100644 --- a/arch/um/kernel/uml.lds.S +++ b/arch/um/kernel/uml.lds.S @@ -20,13 +20,12 @@ SECTIONS . = START + SIZEOF_HEADERS; _text = .; - _stext = .; - __init_begin = .; INIT_TEXT_SECTION(0) . = ALIGN(PAGE_SIZE); .text : { + _stext = .; TEXT_TEXT SCHED_TEXT LOCK_TEXT @@ -62,7 +61,10 @@ SECTIONS #include + __init_begin = .; init.data : { INIT_DATA } + __init_end = .; + .data : { INIT_TASK_DATA(KERNEL_STACK_SIZE) @@ -97,6 +99,7 @@ SECTIONS PROVIDE(_bss_start = .); SBSS(0) BSS(0) + __bss_stop = .; _end = .; PROVIDE (end = .); -- cgit v1.2.3 From 7ee3d4e8cd560500192d80ca84d7f15d6dee0807 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:03:41 -0700 Subject: mm: introduce helper function mem_init_print_info() to simplify mem_init() Introduce helper function mem_init_print_info() to simplify mem_init() across different architectures, which also unifies the format and information printed. Function mem_init_print_info() calculates memory statistics information without walking each page, so it should be a little faster on some architectures. Also introduce another helper get_num_physpages() to kill the global variable num_physpages. Signed-off-by: Jiang Liu Cc: Mel Gorman Cc: Michel Lespinasse Cc: Rik van Riel Cc: Minchan Kim Cc: Marek Szyprowski Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mm.h | 12 ++++++++++++ mm/page_alloc.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/include/linux/mm.h b/include/linux/mm.h index 4310f80ce956..09c235301dbb 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1323,6 +1323,7 @@ extern void free_highmem_page(struct page *page); #endif extern void adjust_managed_page_count(struct page *page, long count); +extern void mem_init_print_info(const char *str); /* Free the reserved page into the buddy system, so it gets managed. */ static inline void __free_reserved_page(struct page *page) @@ -1358,6 +1359,17 @@ static inline unsigned long free_initmem_default(int poison) poison, "unused kernel"); } +static inline unsigned long get_num_physpages(void) +{ + int nid; + unsigned long phys_pages = 0; + + for_each_online_node(nid) + phys_pages += node_present_pages(nid); + + return phys_pages; +} + #ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP /* * With CONFIG_HAVE_MEMBLOCK_NODE_MAP set, an architecture may initialise its diff --git a/mm/page_alloc.c b/mm/page_alloc.c index d9445c4f5fd7..327516b7aee9 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -61,6 +61,7 @@ #include #include +#include #include #include #include "internal.h" @@ -5246,6 +5247,57 @@ void free_highmem_page(struct page *page) } #endif + +void __init mem_init_print_info(const char *str) +{ + unsigned long physpages, codesize, datasize, rosize, bss_size; + unsigned long init_code_size, init_data_size; + + physpages = get_num_physpages(); + codesize = _etext - _stext; + datasize = _edata - _sdata; + rosize = __end_rodata - __start_rodata; + bss_size = __bss_stop - __bss_start; + init_data_size = __init_end - __init_begin; + init_code_size = _einittext - _sinittext; + + /* + * Detect special cases and adjust section sizes accordingly: + * 1) .init.* may be embedded into .data sections + * 2) .init.text.* may be out of [__init_begin, __init_end], + * please refer to arch/tile/kernel/vmlinux.lds.S. + * 3) .rodata.* may be embedded into .text or .data sections. + */ +#define adj_init_size(start, end, size, pos, adj) \ + if (start <= pos && pos < end && size > adj) \ + size -= adj; + + adj_init_size(__init_begin, __init_end, init_data_size, + _sinittext, init_code_size); + adj_init_size(_stext, _etext, codesize, _sinittext, init_code_size); + adj_init_size(_sdata, _edata, datasize, __init_begin, init_data_size); + adj_init_size(_stext, _etext, codesize, __start_rodata, rosize); + adj_init_size(_sdata, _edata, datasize, __start_rodata, rosize); + +#undef adj_init_size + + printk("Memory: %luK/%luK available " + "(%luK kernel code, %luK rwdata, %luK rodata, " + "%luK init, %luK bss, %luK reserved" +#ifdef CONFIG_HIGHMEM + ", %luK highmem" +#endif + "%s%s)\n", + nr_free_pages() << (PAGE_SHIFT-10), physpages << (PAGE_SHIFT-10), + codesize >> 10, datasize >> 10, rosize >> 10, + (init_data_size + init_code_size) >> 10, bss_size >> 10, + (physpages - totalram_pages) << (PAGE_SHIFT-10), +#ifdef CONFIG_HIGHMEM + totalhigh_pages << (PAGE_SHIFT-10), +#endif + str ? ", " : "", str ? str : ""); +} + /** * set_dma_reserve - set the specified number of pages reserved in the first zone * @new_dma_reserve: The number of pages to mark reserved -- cgit v1.2.3 From 0ed5fd138539940a493dc69359cb2f49de70ad89 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:03:43 -0700 Subject: mm: use totalram_pages instead of num_physpages at runtime The global variable num_physpages is scheduled to be removed, so use totalram_pages instead of num_physpages at runtime. Signed-off-by: Jiang Liu Cc: Miklos Szeredi Cc: "David S. Miller" Cc: Alexey Kuznetsov Cc: James Morris Cc: Hideaki YOSHIFUJI Cc: Patrick McHardy Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/fuse/inode.c | 2 +- kernel/power/snapshot.c | 4 ++-- net/ipv4/inet_fragment.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 9a0cdde14a08..0b578598c6ac 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -785,7 +785,7 @@ static const struct super_operations fuse_super_operations = { static void sanitize_global_limit(unsigned *limit) { if (*limit == 0) - *limit = ((num_physpages << PAGE_SHIFT) >> 13) / + *limit = ((totalram_pages << PAGE_SHIFT) >> 13) / sizeof(struct fuse_req); if (*limit >= 1 << 16) diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index 0de28576807d..8b5d1cd933f4 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c @@ -1651,7 +1651,7 @@ unsigned long snapshot_get_image_size(void) static int init_header(struct swsusp_info *info) { memset(info, 0, sizeof(struct swsusp_info)); - info->num_physpages = num_physpages; + info->num_physpages = get_num_physpages(); info->image_pages = nr_copy_pages; info->pages = snapshot_get_image_size(); info->size = info->pages; @@ -1795,7 +1795,7 @@ static int check_header(struct swsusp_info *info) char *reason; reason = check_image_kernel(info); - if (!reason && info->num_physpages != num_physpages) + if (!reason && info->num_physpages != get_num_physpages()) reason = "memory size"; if (reason) { printk(KERN_ERR "PM: Image mismatch: %s\n", reason); diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c index 7e06641e36ae..cec539458307 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c @@ -93,7 +93,7 @@ void inet_frags_init(struct inet_frags *f) } rwlock_init(&f->lock); - f->rnd = (u32) ((num_physpages ^ (num_physpages>>7)) ^ + f->rnd = (u32) ((totalram_pages ^ (totalram_pages >> 7)) ^ (jiffies ^ (jiffies >> 6))); setup_timer(&f->secret_timer, inet_frag_secret_rebuild, -- cgit v1.2.3 From e461d627d5c0957457eb354843f3c29b50646d63 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:03:44 -0700 Subject: mm/hotplug: prepare for removing num_physpages Prepare for removing num_physpages. Signed-off-by: Jiang Liu Cc: Wen Congyang Cc: Tang Chen Cc: Yasuaki Ishimatsu Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/memory_hotplug.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index 5e34922124a3..106602e5a70e 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -763,10 +763,6 @@ EXPORT_SYMBOL_GPL(restore_online_page_callback); void __online_page_set_limits(struct page *page) { - unsigned long pfn = page_to_pfn(page); - - if (pfn >= num_physpages) - num_physpages = pfn + 1; } EXPORT_SYMBOL_GPL(__online_page_set_limits); -- cgit v1.2.3 From d385d9ee7add81254cb7094d616138089ea500f5 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:03:45 -0700 Subject: mm/alpha: prepare for removing num_physpages and simplify mem_init() Prepare for removing num_physpages and simplify mem_init(). Signed-off-by: Jiang Liu Cc: Richard Henderson Cc: Ivan Kokshaysky Cc: Matt Turner Cc: David Howells Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/alpha/mm/init.c | 32 ++------------------------------ arch/alpha/mm/numa.c | 34 ++-------------------------------- 2 files changed, 4 insertions(+), 62 deletions(-) diff --git a/arch/alpha/mm/init.c b/arch/alpha/mm/init.c index eee47a453d7d..af91010990b0 100644 --- a/arch/alpha/mm/init.c +++ b/arch/alpha/mm/init.c @@ -277,42 +277,14 @@ srm_paging_stop (void) #endif #ifndef CONFIG_DISCONTIGMEM -static void __init -printk_memory_info(void) -{ - unsigned long codesize, reservedpages, datasize, initsize, tmp; - extern int page_is_ram(unsigned long) __init; - - /* printk all informations */ - reservedpages = 0; - for (tmp = 0; tmp < max_low_pfn; tmp++) - /* - * Only count reserved RAM pages - */ - if (page_is_ram(tmp) && PageReserved(mem_map+tmp)) - reservedpages++; - - codesize = (unsigned long) &_etext - (unsigned long) &_text; - datasize = (unsigned long) &_edata - (unsigned long) &_data; - initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; - - printk("Memory: %luk/%luk available (%luk kernel code, %luk reserved, %luk data, %luk init)\n", - nr_free_pages() << (PAGE_SHIFT-10), - max_mapnr << (PAGE_SHIFT-10), - codesize >> 10, - reservedpages << (PAGE_SHIFT-10), - datasize >> 10, - initsize >> 10); -} - void __init mem_init(void) { - max_mapnr = num_physpages = max_low_pfn; + max_mapnr = max_low_pfn; free_all_bootmem(); high_memory = (void *) __va(max_low_pfn * PAGE_SIZE); - printk_memory_info(); + mem_init_print_info(NULL); } #endif /* CONFIG_DISCONTIGMEM */ diff --git a/arch/alpha/mm/numa.c b/arch/alpha/mm/numa.c index 857452c13c4d..0894b3a8b70f 100644 --- a/arch/alpha/mm/numa.c +++ b/arch/alpha/mm/numa.c @@ -129,8 +129,6 @@ setup_memory_node(int nid, void *kernel_end) if (node_max_pfn > max_low_pfn) max_pfn = max_low_pfn = node_max_pfn; - num_physpages += node_max_pfn - node_min_pfn; - #if 0 /* we'll try this one again in a little while */ /* Cute trick to make sure our local node data is on local memory */ node_data[nid] = (pg_data_t *)(__va(node_min_pfn << PAGE_SHIFT)); @@ -324,37 +322,9 @@ void __init paging_init(void) void __init mem_init(void) { - unsigned long codesize, reservedpages, datasize, initsize, pfn; - extern int page_is_ram(unsigned long) __init; - unsigned long nid, i; high_memory = (void *) __va(max_low_pfn << PAGE_SHIFT); - - reservedpages = 0; - for_each_online_node(nid) { - /* - * This will free up the bootmem, ie, slot 0 memory - */ - free_all_bootmem_node(NODE_DATA(nid)); - - pfn = NODE_DATA(nid)->node_start_pfn; - for (i = 0; i < node_spanned_pages(nid); i++, pfn++) - if (page_is_ram(pfn) && - PageReserved(nid_page_nr(nid, i))) - reservedpages++; - } - - codesize = (unsigned long) &_etext - (unsigned long) &_text; - datasize = (unsigned long) &_edata - (unsigned long) &_data; - initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; - - printk("Memory: %luk/%luk available (%luk kernel code, %luk reserved, " - "%luk data, %luk init)\n", - nr_free_pages() << (PAGE_SHIFT-10), - num_physpages << (PAGE_SHIFT-10), - codesize >> 10, - reservedpages << (PAGE_SHIFT-10), - datasize >> 10, - initsize >> 10); + free_all_bootmem(); + mem_init_print_info(NULL); #if 0 mem_stress(); #endif -- cgit v1.2.3 From de35e1b828cb21d027265847ca32e5b9cff1c1e9 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:03:47 -0700 Subject: mm/ARC: prepare for removing num_physpages and simplify mem_init() Prepare for removing num_physpages and simplify mem_init(). Signed-off-by: Jiang Liu Acked-by: Vineet Gupta # for arch/arc Cc: James Hogan Cc: Rob Herring Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/arc/mm/init.c | 36 +++--------------------------------- 1 file changed, 3 insertions(+), 33 deletions(-) diff --git a/arch/arc/mm/init.c b/arch/arc/mm/init.c index c668a600f652..a08ce7185423 100644 --- a/arch/arc/mm/init.c +++ b/arch/arc/mm/init.c @@ -74,7 +74,7 @@ void __init setup_arch_memory(void) /* Last usable page of low mem (no HIGHMEM yet for ARC port) */ max_low_pfn = max_pfn = PFN_DOWN(end_mem); - max_mapnr = num_physpages = max_low_pfn - min_low_pfn; + max_mapnr = max_low_pfn - min_low_pfn; /*------------- reserve kernel image -----------------------*/ memblock_reserve(CONFIG_LINUX_LINK_BASE, @@ -84,7 +84,7 @@ void __init setup_arch_memory(void) /*-------------- node setup --------------------------------*/ memset(zones_size, 0, sizeof(zones_size)); - zones_size[ZONE_NORMAL] = num_physpages; + zones_size[ZONE_NORMAL] = max_low_pfn - min_low_pfn; /* * We can't use the helper free_area_init(zones[]) because it uses @@ -106,39 +106,9 @@ void __init setup_arch_memory(void) */ void __init mem_init(void) { - int codesize, datasize, initsize, reserved_pages, free_pages; - int tmp; - high_memory = (void *)(CONFIG_LINUX_LINK_BASE + arc_mem_sz); - free_all_bootmem(); - - /* count all reserved pages [kernel code/data/mem_map..] */ - reserved_pages = 0; - for (tmp = 0; tmp < max_mapnr; tmp++) - if (PageReserved(mem_map + tmp)) - reserved_pages++; - - /* XXX: nr_free_pages() is equivalent */ - free_pages = max_mapnr - reserved_pages; - - /* - * For the purpose of display below, split the "reserve mem" - * kernel code/data is already shown explicitly, - * Show any other reservations (mem_map[ ] et al) - */ - reserved_pages -= (((unsigned int)_end - CONFIG_LINUX_LINK_BASE) >> - PAGE_SHIFT); - - codesize = _etext - _text; - datasize = _end - _etext; - initsize = __init_end - __init_begin; - - pr_info("Memory Available: %dM / %ldM (%dK code, %dK data, %dK init, %dK reserv)\n", - PAGES_TO_MB(free_pages), - TO_MB(arc_mem_sz), - TO_KB(codesize), TO_KB(datasize), TO_KB(initsize), - PAGES_TO_KB(reserved_pages)); + mem_init_print_info(NULL); } /* -- cgit v1.2.3 From 2450c97323e635a04f7b2f4b68680ab2c151bbbf Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:03:48 -0700 Subject: mm/ARM: prepare for removing num_physpages and simplify mem_init() Prepare for removing num_physpages and simplify mem_init(). Signed-off-by: Jiang Liu Cc: Russell King Cc: Catalin Marinas Cc: Will Deacon Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/arm/mm/init.c | 47 ++--------------------------------------------- 1 file changed, 2 insertions(+), 45 deletions(-) diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 06e9ce17d1d2..6833cbead6cc 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -583,9 +583,6 @@ static void __init free_highpages(void) */ void __init mem_init(void) { - unsigned long reserved_pages, free_pages; - struct memblock_region *reg; - int i; #ifdef CONFIG_HAVE_TCM /* These pointers are filled in on TCM detection */ extern u32 dtcm_end; @@ -605,47 +602,7 @@ void __init mem_init(void) free_highpages(); - reserved_pages = free_pages = 0; - - for_each_bank(i, &meminfo) { - struct membank *bank = &meminfo.bank[i]; - unsigned int pfn1, pfn2; - struct page *page, *end; - - pfn1 = bank_pfn_start(bank); - pfn2 = bank_pfn_end(bank); - - page = pfn_to_page(pfn1); - end = pfn_to_page(pfn2 - 1) + 1; - - do { - if (PageReserved(page)) - reserved_pages++; - else if (!page_count(page)) - free_pages++; - page++; - } while (page < end); - } - - /* - * Since our memory may not be contiguous, calculate the - * real number of pages we have in this system - */ - printk(KERN_INFO "Memory:"); - num_physpages = 0; - for_each_memblock(memory, reg) { - unsigned long pages = memblock_region_memory_end_pfn(reg) - - memblock_region_memory_base_pfn(reg); - num_physpages += pages; - printk(" %ldMB", pages >> (20 - PAGE_SHIFT)); - } - printk(" = %luMB total\n", num_physpages >> (20 - PAGE_SHIFT)); - - printk(KERN_NOTICE "Memory: %luk/%luk available, %luk reserved, %luK highmem\n", - nr_free_pages() << (PAGE_SHIFT-10), - free_pages << (PAGE_SHIFT-10), - reserved_pages << (PAGE_SHIFT-10), - totalhigh_pages << (PAGE_SHIFT-10)); + mem_init_print_info(NULL); #define MLK(b, t) b, t, ((t) - (b)) >> 10 #define MLM(b, t) b, t, ((t) - (b)) >> 20 @@ -711,7 +668,7 @@ void __init mem_init(void) BUG_ON(PKMAP_BASE + LAST_PKMAP * PAGE_SIZE > PAGE_OFFSET); #endif - if (PAGE_SIZE >= 16384 && num_physpages <= 128) { + if (PAGE_SIZE >= 16384 && get_num_physpages() <= 128) { extern int sysctl_overcommit_memory; /* * On a machine this small we won't get -- cgit v1.2.3 From bee4ebd117ac943308dd57bc0a5a3cc539f0eaac Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:03:49 -0700 Subject: mm/ARM64: prepare for removing num_physpages and simplify mem_init() Prepare for removing num_physpages and simplify mem_init(). Signed-off-by: Jiang Liu Cc: Russell King Cc: Catalin Marinas Cc: Will Deacon Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/arm64/mm/init.c | 48 +++--------------------------------------------- 1 file changed, 3 insertions(+), 45 deletions(-) diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c index 93de98afedd7..b16c778ea0de 100644 --- a/arch/arm64/mm/init.c +++ b/arch/arm64/mm/init.c @@ -272,59 +272,17 @@ static void __init free_unused_memmap(void) */ void __init mem_init(void) { - unsigned long reserved_pages, free_pages; - struct memblock_region *reg; - arm64_swiotlb_init(); max_mapnr = pfn_to_page(max_pfn + PHYS_PFN_OFFSET) - mem_map; #ifndef CONFIG_SPARSEMEM_VMEMMAP - /* this will put all unused low memory onto the freelists */ free_unused_memmap(); #endif - + /* this will put all unused low memory onto the freelists */ free_all_bootmem(); - reserved_pages = free_pages = 0; - - for_each_memblock(memory, reg) { - unsigned int pfn1, pfn2; - struct page *page, *end; - - pfn1 = __phys_to_pfn(reg->base); - pfn2 = pfn1 + __phys_to_pfn(reg->size); - - page = pfn_to_page(pfn1); - end = pfn_to_page(pfn2 - 1) + 1; - - do { - if (PageReserved(page)) - reserved_pages++; - else if (!page_count(page)) - free_pages++; - page++; - } while (page < end); - } - - /* - * Since our memory may not be contiguous, calculate the real number - * of pages we have in this system. - */ - pr_info("Memory:"); - num_physpages = 0; - for_each_memblock(memory, reg) { - unsigned long pages = memblock_region_memory_end_pfn(reg) - - memblock_region_memory_base_pfn(reg); - num_physpages += pages; - printk(" %ldMB", pages >> (20 - PAGE_SHIFT)); - } - printk(" = %luMB total\n", num_physpages >> (20 - PAGE_SHIFT)); - - pr_notice("Memory: %luk/%luk available, %luk reserved\n", - nr_free_pages() << (PAGE_SHIFT-10), - free_pages << (PAGE_SHIFT-10), - reserved_pages << (PAGE_SHIFT-10)); + mem_init_print_info(); #define MLK(b, t) b, t, ((t) - (b)) >> 10 #define MLM(b, t) b, t, ((t) - (b)) >> 20 @@ -366,7 +324,7 @@ void __init mem_init(void) BUILD_BUG_ON(TASK_SIZE_64 > MODULES_VADDR); BUG_ON(TASK_SIZE_64 > MODULES_VADDR); - if (PAGE_SIZE >= 16384 && num_physpages <= 128) { + if (PAGE_SIZE >= 16384 && get_num_physpages() <= 128) { extern int sysctl_overcommit_memory; /* * On a machine this small we won't get anywhere without -- cgit v1.2.3 From 6703bdf669f0e4bbeb8f9c1afb87c54bdd60e852 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:03:50 -0700 Subject: mm/AVR32: prepare for removing num_physpages and simplify mem_init() Prepare for removing num_physpages and simplify mem_init(). Signed-off-by: Jiang Liu Acked-by: Hans-Christian Egtvedt Cc: Haavard Skinnemoen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/avr32/mm/init.c | 29 ++++------------------------- 1 file changed, 4 insertions(+), 25 deletions(-) diff --git a/arch/avr32/mm/init.c b/arch/avr32/mm/init.c index af6890fd7319..0fc04b9323cf 100644 --- a/arch/avr32/mm/init.c +++ b/arch/avr32/mm/init.c @@ -100,26 +100,16 @@ void __init paging_init(void) void __init mem_init(void) { - int codesize, reservedpages, datasize, initsize; - int nid, i; + pg_data_t *pgdat; - reservedpages = 0; high_memory = NULL; /* this will put all low memory onto the freelists */ - for_each_online_node(nid) { - pg_data_t *pgdat = NODE_DATA(nid); - unsigned long node_pages = 0; + for_each_online_pgdat(pgdat) { void *node_high_memory; - num_physpages += pgdat->node_present_pages; - if (pgdat->node_spanned_pages != 0) - node_pages = free_all_bootmem_node(pgdat); - - for (i = 0; i < node_pages; i++) - if (PageReserved(pgdat->node_mem_map + i)) - reservedpages++; + free_all_bootmem_node(pgdat); node_high_memory = (void *)((pgdat->node_start_pfn + pgdat->node_spanned_pages) @@ -130,18 +120,7 @@ void __init mem_init(void) max_mapnr = MAP_NR(high_memory); - codesize = (unsigned long)_etext - (unsigned long)_text; - datasize = (unsigned long)_edata - (unsigned long)_data; - initsize = (unsigned long)__init_end - (unsigned long)__init_begin; - - printk ("Memory: %luk/%luk available (%dk kernel code, " - "%dk reserved, %dk data, %dk init)\n", - nr_free_pages() << (PAGE_SHIFT - 10), - totalram_pages << (PAGE_SHIFT - 10), - codesize >> 10, - reservedpages << (PAGE_SHIFT - 10), - datasize >> 10, - initsize >> 10); + mem_init_print_info(NULL); } void free_initmem(void) -- cgit v1.2.3 From d9d7e769815c9cb66c8a4b144f066bb957ebd98e Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:03:51 -0700 Subject: mm/blackfin: prepare for removing num_physpages and simplify mem_init() Prepare for removing num_physpages and simplify mem_init(). Signed-off-by: Jiang Liu Cc: Mike Frysinger Cc: Bob Liu Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/blackfin/mm/init.c | 38 ++++++-------------------------------- 1 file changed, 6 insertions(+), 32 deletions(-) diff --git a/arch/blackfin/mm/init.c b/arch/blackfin/mm/init.c index c73d80ef564f..166842de3dc7 100644 --- a/arch/blackfin/mm/init.c +++ b/arch/blackfin/mm/init.c @@ -90,43 +90,17 @@ asmlinkage void __init init_pda(void) void __init mem_init(void) { - unsigned int codek = 0, datak = 0, initk = 0; - unsigned int reservedpages = 0, freepages = 0; - unsigned long tmp; - unsigned long start_mem = memory_start; - unsigned long end_mem = memory_end; + char buf[64]; - end_mem &= PAGE_MASK; - high_memory = (void *)end_mem; - - start_mem = PAGE_ALIGN(start_mem); - max_mapnr = num_physpages = MAP_NR(high_memory); - printk(KERN_DEBUG "Kernel managed physical pages: %lu\n", num_physpages); + high_memory = (void *)(memory_end & PAGE_MASK); + max_mapnr = MAP_NR(high_memory); + printk(KERN_DEBUG "Kernel managed physical pages: %lu\n", max_mapnr); /* This will put all low memory onto the freelists. */ free_all_bootmem(); - reservedpages = 0; - for (tmp = ARCH_PFN_OFFSET; tmp < max_mapnr; tmp++) - if (PageReserved(pfn_to_page(tmp))) - reservedpages++; - freepages = max_mapnr - ARCH_PFN_OFFSET - reservedpages; - - /* do not count in kernel image between _rambase and _ramstart */ - reservedpages -= (_ramstart - _rambase) >> PAGE_SHIFT; -#if (defined(CONFIG_BFIN_EXTMEM_ICACHEABLE) && ANOMALY_05000263) - reservedpages += (_ramend - memory_end - DMA_UNCACHED_REGION) >> PAGE_SHIFT; -#endif - - codek = (_etext - _stext) >> 10; - initk = (__init_end - __init_begin) >> 10; - datak = ((_ramstart - _rambase) >> 10) - codek - initk; - - printk(KERN_INFO - "Memory available: %luk/%luk RAM, " - "(%uk init code, %uk kernel code, %uk data, %uk dma, %uk reserved)\n", - (unsigned long) freepages << (PAGE_SHIFT-10), (_ramend - CONFIG_PHY_RAM_BASE_ADDRESS) >> 10, - initk, codek, datak, DMA_UNCACHED_REGION >> 10, (reservedpages << (PAGE_SHIFT-10))); + snprintf(buf, sizeof(buf) - 1, "%uK DMA", DMA_UNCACHED_REGION >> 10); + mem_init_print_info(buf); } #ifdef CONFIG_BLK_DEV_INITRD -- cgit v1.2.3 From 02f5532445c4aed57697b612e72b2a334eeb10ce Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:03:52 -0700 Subject: mm/c6x: prepare for removing num_physpages and simplify mem_init() Prepare for removing num_physpages and simplify mem_init(). Signed-off-by: Jiang Liu Cc: Mark Salter Cc: Aurelien Jacquiot Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/c6x/mm/init.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/arch/c6x/mm/init.c b/arch/c6x/mm/init.c index c9ae8ce731d5..63f5560d6eb2 100644 --- a/arch/c6x/mm/init.c +++ b/arch/c6x/mm/init.c @@ -58,21 +58,12 @@ void __init paging_init(void) void __init mem_init(void) { - int codek, datak; - unsigned long tmp; - unsigned long len = memory_end - memory_start; - high_memory = (void *)(memory_end & PAGE_MASK); /* this will put all memory onto the freelists */ free_all_bootmem(); - codek = (_etext - _stext) >> 10; - datak = (_end - _sdata) >> 10; - - tmp = nr_free_pages() << PAGE_SHIFT; - printk(KERN_INFO "Memory: %luk/%luk RAM (%dk kernel code, %dk data)\n", - tmp >> 10, len >> 10, codek, datak); + mem_init_print_info(NULL); } #ifdef CONFIG_BLK_DEV_INITRD -- cgit v1.2.3 From 4e422de996da62e933dcc0fd3c2d7fe513cf32a2 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:03:54 -0700 Subject: mm/cris: prepare for removing num_physpages and simplify mem_init() Prepare for removing num_physpages and simplify mem_init(). Signed-off-by: Jiang Liu Acked-by: Jesper Nilsson Cc: Mikael Starvik Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/cris/mm/init.c | 33 ++------------------------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/arch/cris/mm/init.c b/arch/cris/mm/init.c index 52b8b56ae305..c81af5bd9167 100644 --- a/arch/cris/mm/init.c +++ b/arch/cris/mm/init.c @@ -19,9 +19,6 @@ unsigned long empty_zero_page; void __init mem_init(void) { - int codesize, reservedpages, datasize, initsize; - unsigned long tmp; - BUG_ON(!mem_map); /* max/min_low_pfn was set by setup.c @@ -29,35 +26,9 @@ mem_init(void) * * high_memory was also set in setup.c */ - - max_mapnr = num_physpages = max_low_pfn - min_low_pfn; - - /* this will put all memory onto the freelists */ + max_mapnr = max_low_pfn - min_low_pfn; free_all_bootmem(); - - reservedpages = 0; - for (tmp = 0; tmp < max_mapnr; tmp++) { - /* - * Only count reserved RAM pages - */ - if (PageReserved(mem_map + tmp)) - reservedpages++; - } - - codesize = (unsigned long) &_etext - (unsigned long) &_stext; - datasize = (unsigned long) &_edata - (unsigned long) &_etext; - initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; - - printk(KERN_INFO - "Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data, " - "%dk init)\n" , - nr_free_pages() << (PAGE_SHIFT-10), - max_mapnr << (PAGE_SHIFT-10), - codesize >> 10, - reservedpages << (PAGE_SHIFT-10), - datasize >> 10, - initsize >> 10 - ); + mem_init_print_info(NULL); } /* free the pages occupied by initialization code */ -- cgit v1.2.3 From 3f2b73c3c3e59cb9b94490d664b2439cd9c540e2 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:03:55 -0700 Subject: mm/frv: prepare for removing num_physpages and simplify mem_init() Prepare for removing num_physpages and simplify mem_init(). Signed-off-by: Jiang Liu Cc: David Howells Cc: Andi Kleen Cc: Geert Uytterhoeven Cc: Greg Kroah-Hartman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/frv/kernel/setup.c | 13 +++++++------ arch/frv/mm/init.c | 49 ++++++++++++++----------------------------------- 2 files changed, 21 insertions(+), 41 deletions(-) diff --git a/arch/frv/kernel/setup.c b/arch/frv/kernel/setup.c index a5136474c6fd..f78f8cb44093 100644 --- a/arch/frv/kernel/setup.c +++ b/arch/frv/kernel/setup.c @@ -876,6 +876,7 @@ late_initcall(setup_arch_serial); static void __init setup_linux_memory(void) { unsigned long bootmap_size, low_top_pfn, kstart, kend, high_mem; + unsigned long physpages; kstart = (unsigned long) &__kernel_image_start - PAGE_OFFSET; kend = (unsigned long) &__kernel_image_end - PAGE_OFFSET; @@ -893,19 +894,19 @@ static void __init setup_linux_memory(void) ); /* pass the memory that the kernel can immediately use over to the bootmem allocator */ - max_mapnr = num_physpages = (memory_end - memory_start) >> PAGE_SHIFT; + max_mapnr = physpages = (memory_end - memory_start) >> PAGE_SHIFT; low_top_pfn = (KERNEL_LOWMEM_END - KERNEL_LOWMEM_START) >> PAGE_SHIFT; high_mem = 0; - if (num_physpages > low_top_pfn) { + if (physpages > low_top_pfn) { #ifdef CONFIG_HIGHMEM - high_mem = num_physpages - low_top_pfn; + high_mem = physpages - low_top_pfn; #else - max_mapnr = num_physpages = low_top_pfn; + max_mapnr = physpages = low_top_pfn; #endif } else { - low_top_pfn = num_physpages; + low_top_pfn = physpages; } min_low_pfn = memory_start >> PAGE_SHIFT; @@ -979,7 +980,7 @@ static void __init setup_uclinux_memory(void) free_bootmem(memory_start, memory_end - memory_start); high_memory = (void *) (memory_end & PAGE_MASK); - max_mapnr = num_physpages = ((unsigned long) high_memory - PAGE_OFFSET) >> PAGE_SHIFT; + max_mapnr = ((unsigned long) high_memory - PAGE_OFFSET) >> PAGE_SHIFT; min_low_pfn = memory_start >> PAGE_SHIFT; max_low_pfn = memory_end >> PAGE_SHIFT; diff --git a/arch/frv/mm/init.c b/arch/frv/mm/init.c index 3dcc88803a4f..88a159743528 100644 --- a/arch/frv/mm/init.c +++ b/arch/frv/mm/init.c @@ -78,7 +78,7 @@ void __init paging_init(void) memset((void *) empty_zero_page, 0, PAGE_SIZE); #ifdef CONFIG_HIGHMEM - if (num_physpages - num_mappedpages) { + if (get_num_physpages() - num_mappedpages) { pgd_t *pge; pud_t *pue; pmd_t *pme; @@ -96,7 +96,7 @@ void __init paging_init(void) */ zones_size[ZONE_NORMAL] = max_low_pfn - min_low_pfn; #ifdef CONFIG_HIGHMEM - zones_size[ZONE_HIGHMEM] = num_physpages - num_mappedpages; + zones_size[ZONE_HIGHMEM] = get_num_physpages() - num_mappedpages; #endif free_area_init(zones_size); @@ -114,45 +114,24 @@ void __init paging_init(void) */ void __init mem_init(void) { - unsigned long npages = (memory_end - memory_start) >> PAGE_SHIFT; - unsigned long tmp; -#ifdef CONFIG_MMU - unsigned long loop, pfn; - int datapages = 0; -#endif - int codek = 0, datak = 0; + unsigned long code_size = _etext - _stext; /* this will put all low memory onto the freelists */ free_all_bootmem(); +#if defined(CONFIG_MMU) && defined(CONFIG_HIGHMEM) + { + unsigned long pfn; -#ifdef CONFIG_MMU - for (loop = 0 ; loop < npages ; loop++) - if (PageReserved(&mem_map[loop])) - datapages++; - -#ifdef CONFIG_HIGHMEM - for (pfn = num_physpages - 1; pfn >= num_mappedpages; pfn--) - free_highmem_page(&mem_map[pfn]); -#endif - - codek = ((unsigned long) &_etext - (unsigned long) &_stext) >> 10; - datak = datapages << (PAGE_SHIFT - 10); - -#else - codek = (_etext - _stext) >> 10; - datak = 0; //(__bss_stop - _sdata) >> 10; + for (pfn = get_num_physpages() - 1; + pfn >= num_mappedpages; pfn--) + free_highmem_page(&mem_map[pfn]); + } #endif - tmp = nr_free_pages() << PAGE_SHIFT; - printk("Memory available: %luKiB/%luKiB RAM, %luKiB/%luKiB ROM (%dKiB kernel code, %dKiB data)\n", - tmp >> 10, - npages << (PAGE_SHIFT - 10), - (rom_length > 0) ? ((rom_length >> 10) - codek) : 0, - rom_length >> 10, - codek, - datak - ); - + mem_init_print_info(NULL); + if (rom_length > 0 && rom_length >= code_size) + printk("Memory available: %luKiB/%luKiB ROM\n", + (rom_length - code_size) >> 10, rom_length >> 10); } /* end mem_init() */ /*****************************************************************************/ -- cgit v1.2.3 From 27a59706e4df3d1ea7aa1803b1ee0c94f1a4f3cf Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:03:56 -0700 Subject: mm/h8300: prepare for removing num_physpages and simplify mem_init() Prepare for removing num_physpages and simplify mem_init(). Signed-off-by: Jiang Liu Cc: Yoshinori Sato Cc: Geert Uytterhoeven Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/h8300/mm/init.c | 36 ++++++++---------------------------- 1 file changed, 8 insertions(+), 28 deletions(-) diff --git a/arch/h8300/mm/init.c b/arch/h8300/mm/init.c index a506dd4724e0..6c1251e491af 100644 --- a/arch/h8300/mm/init.c +++ b/arch/h8300/mm/init.c @@ -121,40 +121,20 @@ void __init paging_init(void) void __init mem_init(void) { - int codek = 0, datak = 0, initk = 0; - /* DAVIDM look at setup memory map generically with reserved area */ - unsigned long tmp; - extern unsigned long _ramend, _ramstart; - unsigned long len = &_ramend - &_ramstart; - unsigned long start_mem = memory_start; /* DAVIDM - these must start at end of kernel */ - unsigned long end_mem = memory_end; /* DAVIDM - this must not include kernel stack at top */ + unsigned long codesize = _etext - _stext; -#ifdef DEBUG - printk(KERN_DEBUG "Mem_init: start=%lx, end=%lx\n", start_mem, end_mem); -#endif - - end_mem &= PAGE_MASK; - high_memory = (void *) end_mem; + pr_devel("Mem_init: start=%lx, end=%lx\n", memory_start, memory_end); - start_mem = PAGE_ALIGN(start_mem); - max_mapnr = num_physpages = MAP_NR(high_memory); + high_memory = (void *) (memory_end & PAGE_MASK); + max_mapnr = MAP_NR(high_memory); /* this will put all low memory onto the freelists */ free_all_bootmem(); - codek = (_etext - _stext) >> 10; - datak = (__bss_stop - _sdata) >> 10; - initk = (__init_begin - __init_end) >> 10; - - tmp = nr_free_pages() << PAGE_SHIFT; - printk(KERN_INFO "Memory available: %luk/%luk RAM, %luk/%luk ROM (%dk kernel code, %dk data)\n", - tmp >> 10, - len >> 10, - (rom_length > 0) ? ((rom_length >> 10) - codek) : 0, - rom_length >> 10, - codek, - datak - ); + mem_init_print_info(NULL); + if (rom_length > 0 && rom_length > codesize) + pr_info("Memory available: %luK/%luK ROM\n", + (rom_length - codesize) >> 10, rom_length >> 10); } -- cgit v1.2.3 From 5dc355c146689ac5a9d57dff349958585da21fe3 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:03:57 -0700 Subject: mm/hexagon: prepare for removing num_physpages and simplify mem_init() Prepare for removing num_physpages and simplify mem_init(). Signed-off-by: Jiang Liu Cc: Richard Kuo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/hexagon/mm/init.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/arch/hexagon/mm/init.c b/arch/hexagon/mm/init.c index 0ab5b4350e93..88977e42af0a 100644 --- a/arch/hexagon/mm/init.c +++ b/arch/hexagon/mm/init.c @@ -71,9 +71,7 @@ void __init mem_init(void) { /* No idea where this is actually declared. Seems to evade LXR. */ free_all_bootmem(); - num_physpages = bootmem_lastpg-ARCH_PFN_OFFSET; - - printk(KERN_INFO "totalram_pages = %ld\n", totalram_pages); + mem_init_print_info(NULL); /* * To-Do: someone somewhere should wipe out the bootmem map -- cgit v1.2.3 From de4bcddc13be31c669fc74cd2b400e1e7a1fdbcf Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:03:58 -0700 Subject: mm/IA64: prepare for removing num_physpages and simplify mem_init() Prepare for removing num_physpages and simplify mem_init(). Signed-off-by: Jiang Liu Cc: Tony Luck Cc: Fenghua Yu Cc: Zhang Yanfei Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/ia64/mm/contig.c | 11 ----------- arch/ia64/mm/discontig.c | 3 --- arch/ia64/mm/init.c | 27 +-------------------------- 3 files changed, 1 insertion(+), 40 deletions(-) diff --git a/arch/ia64/mm/contig.c b/arch/ia64/mm/contig.c index 67c59ebec899..e4a6a5366dea 100644 --- a/arch/ia64/mm/contig.c +++ b/arch/ia64/mm/contig.c @@ -295,14 +295,6 @@ find_memory (void) alloc_per_cpu_data(); } -static int count_pages(u64 start, u64 end, void *arg) -{ - unsigned long *count = arg; - - *count += (end - start) >> PAGE_SHIFT; - return 0; -} - /* * Set up the page tables. */ @@ -313,9 +305,6 @@ paging_init (void) unsigned long max_dma; unsigned long max_zone_pfns[MAX_NR_ZONES]; - num_physpages = 0; - efi_memmap_walk(count_pages, &num_physpages); - memset(max_zone_pfns, 0, sizeof(max_zone_pfns)); #ifdef CONFIG_ZONE_DMA max_dma = virt_to_phys((void *) MAX_DMA_ADDRESS) >> PAGE_SHIFT; diff --git a/arch/ia64/mm/discontig.c b/arch/ia64/mm/discontig.c index ae4db4bd6d97..58550b8f7d40 100644 --- a/arch/ia64/mm/discontig.c +++ b/arch/ia64/mm/discontig.c @@ -37,7 +37,6 @@ struct early_node_data { struct ia64_node_data *node_data; unsigned long pernode_addr; unsigned long pernode_size; - unsigned long num_physpages; #ifdef CONFIG_ZONE_DMA unsigned long num_dma_physpages; #endif @@ -732,7 +731,6 @@ static __init int count_node_pages(unsigned long start, unsigned long len, int n { unsigned long end = start + len; - mem_data[node].num_physpages += len >> PAGE_SHIFT; #ifdef CONFIG_ZONE_DMA if (start <= __pa(MAX_DMA_ADDRESS)) mem_data[node].num_dma_physpages += @@ -778,7 +776,6 @@ void __init paging_init(void) #endif for_each_online_node(node) { - num_physpages += mem_data[node].num_physpages; pfn_offset = mem_data[node].min_pfn; #ifdef CONFIG_VIRTUAL_MEM_MAP diff --git a/arch/ia64/mm/init.c b/arch/ia64/mm/init.c index d141f7ea0be5..2d372b4c5f70 100644 --- a/arch/ia64/mm/init.c +++ b/arch/ia64/mm/init.c @@ -545,19 +545,6 @@ int __init register_active_ranges(u64 start, u64 len, int nid) return 0; } -static int __init -count_reserved_pages(u64 start, u64 end, void *arg) -{ - unsigned long num_reserved = 0; - unsigned long *count = arg; - - for (; start < end; start += PAGE_SIZE) - if (PageReserved(virt_to_page(start))) - ++num_reserved; - *count += num_reserved; - return 0; -} - int find_max_min_low_pfn (u64 start, u64 end, void *arg) { @@ -596,7 +583,6 @@ __setup("nolwsys", nolwsys_setup); void __init mem_init (void) { - long reserved_pages, codesize, datasize, initsize; pg_data_t *pgdat; int i; @@ -624,18 +610,7 @@ mem_init (void) if (pgdat->bdata->node_bootmem_map) free_all_bootmem_node(pgdat); - reserved_pages = 0; - efi_memmap_walk(count_reserved_pages, &reserved_pages); - - codesize = (unsigned long) _etext - (unsigned long) _stext; - datasize = (unsigned long) _edata - (unsigned long) _etext; - initsize = (unsigned long) __init_end - (unsigned long) __init_begin; - - printk(KERN_INFO "Memory: %luk/%luk available (%luk code, %luk reserved, " - "%luk data, %luk init)\n", nr_free_pages() << (PAGE_SHIFT - 10), - num_physpages << (PAGE_SHIFT - 10), codesize >> 10, - reserved_pages << (PAGE_SHIFT - 10), datasize >> 10, initsize >> 10); - + mem_init_print_info(NULL); /* * For fsyscall entrpoints with no light-weight handler, use the ordinary -- cgit v1.2.3 From a0e7b805cd337d387373d197aa15d61a853e5ed7 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:03:59 -0700 Subject: mm/m32r: prepare for removing num_physpages and simplify mem_init() Prepare for removing num_physpages and simplify mem_init(). Signed-off-by: Jiang Liu Cc: Hirokazu Takata Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/m32r/mm/discontig.c | 6 +----- arch/m32r/mm/init.c | 49 +++++------------------------------------------- 2 files changed, 6 insertions(+), 49 deletions(-) diff --git a/arch/m32r/mm/discontig.c b/arch/m32r/mm/discontig.c index 2c468e8b5853..27196303ce36 100644 --- a/arch/m32r/mm/discontig.c +++ b/arch/m32r/mm/discontig.c @@ -129,11 +129,10 @@ unsigned long __init setup_memory(void) #define START_PFN(nid) (NODE_DATA(nid)->bdata->node_min_pfn) #define MAX_LOW_PFN(nid) (NODE_DATA(nid)->bdata->node_low_pfn) -unsigned long __init zone_sizes_init(void) +void __init zone_sizes_init(void) { unsigned long zones_size[MAX_NR_ZONES], zholes_size[MAX_NR_ZONES]; unsigned long low, start_pfn; - unsigned long holes = 0; int nid, i; mem_prof_t *mp; @@ -147,7 +146,6 @@ unsigned long __init zone_sizes_init(void) low = MAX_LOW_PFN(nid); zones_size[ZONE_DMA] = low - start_pfn; zholes_size[ZONE_DMA] = mp->holes; - holes += zholes_size[ZONE_DMA]; node_set_state(nid, N_NORMAL_MEMORY); free_area_init_node(nid, zones_size, start_pfn, zholes_size); @@ -161,6 +159,4 @@ unsigned long __init zone_sizes_init(void) NODE_DATA(1)->node_zones->watermark[WMARK_MIN] = 0; NODE_DATA(1)->node_zones->watermark[WMARK_LOW] = 0; NODE_DATA(1)->node_zones->watermark[WMARK_HIGH] = 0; - - return holes; } diff --git a/arch/m32r/mm/init.c b/arch/m32r/mm/init.c index a501838233ab..a4f8d934e25f 100644 --- a/arch/m32r/mm/init.c +++ b/arch/m32r/mm/init.c @@ -40,7 +40,6 @@ unsigned long mmu_context_cache_dat; #else unsigned long mmu_context_cache_dat[NR_CPUS]; #endif -static unsigned long hole_pages; /* * function prototype @@ -57,7 +56,7 @@ void free_initrd_mem(unsigned long, unsigned long); #define MAX_LOW_PFN(nid) (NODE_DATA(nid)->bdata->node_low_pfn) #ifndef CONFIG_DISCONTIGMEM -unsigned long __init zone_sizes_init(void) +void __init zone_sizes_init(void) { unsigned long zones_size[MAX_NR_ZONES] = {0, }; unsigned long max_dma; @@ -83,11 +82,9 @@ unsigned long __init zone_sizes_init(void) #endif /* CONFIG_MMU */ free_area_init_node(0, zones_size, start_pfn, 0); - - return 0; } #else /* CONFIG_DISCONTIGMEM */ -extern unsigned long zone_sizes_init(void); +extern void zone_sizes_init(void); #endif /* CONFIG_DISCONTIGMEM */ /*======================================================================* @@ -105,24 +102,7 @@ void __init paging_init(void) for (i = 0 ; i < USER_PTRS_PER_PGD * 2 ; i++) pgd_val(pg_dir[i]) = 0; #endif /* CONFIG_MMU */ - hole_pages = zone_sizes_init(); -} - -int __init reservedpages_count(void) -{ - int reservedpages, nid, i; - - reservedpages = 0; - for_each_online_node(nid) { - unsigned long flags; - pgdat_resize_lock(NODE_DATA(nid), &flags); - for (i = 0 ; i < MAX_LOW_PFN(nid) - START_PFN(nid) ; i++) - if (PageReserved(nid_page_nr(nid, i))) - reservedpages++; - pgdat_resize_unlock(NODE_DATA(nid), &flags); - } - - return reservedpages; + zone_sizes_init(); } /*======================================================================* @@ -131,20 +111,13 @@ int __init reservedpages_count(void) *======================================================================*/ void __init mem_init(void) { - int codesize, reservedpages, datasize, initsize; int nid; #ifndef CONFIG_MMU extern unsigned long memory_end; #endif - num_physpages = 0; - for_each_online_node(nid) - num_physpages += MAX_LOW_PFN(nid) - START_PFN(nid) + 1; - - num_physpages -= hole_pages; - #ifndef CONFIG_DISCONTIGMEM - max_mapnr = num_physpages; + max_mapnr = get_num_physpages(); #endif /* CONFIG_DISCONTIGMEM */ #ifdef CONFIG_MMU @@ -160,19 +133,7 @@ void __init mem_init(void) for_each_online_node(nid) free_all_bootmem_node(NODE_DATA(nid)); - reservedpages = reservedpages_count() - hole_pages; - codesize = (unsigned long) &_etext - (unsigned long)&_text; - datasize = (unsigned long) &_edata - (unsigned long)&_etext; - initsize = (unsigned long) &__init_end - (unsigned long)&__init_begin; - - printk(KERN_INFO "Memory: %luk/%luk available (%dk kernel code, " - "%dk reserved, %dk data, %dk init)\n", - nr_free_pages() << (PAGE_SHIFT-10), - num_physpages << (PAGE_SHIFT-10), - codesize >> 10, - reservedpages << (PAGE_SHIFT-10), - datasize >> 10, - initsize >> 10); + mem_init_print_info(NULL); } /*======================================================================* -- cgit v1.2.3 From 9671468f1e1e7ef67169f7fb518a9905f44b0dd6 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:04:00 -0700 Subject: mm/m68k: prepare for removing num_physpages and simplify mem_init() Prepare for removing num_physpages and simplify mem_init(). Signed-off-by: Jiang Liu Acked-by: Greg Ungerer Cc: Geert Uytterhoeven Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/m68k/mm/init.c | 31 ++----------------------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/arch/m68k/mm/init.c b/arch/m68k/mm/init.c index 614c60a04459..397a8848f067 100644 --- a/arch/m68k/mm/init.c +++ b/arch/m68k/mm/init.c @@ -149,33 +149,11 @@ void __init print_memmap(void) void __init mem_init(void) { pg_data_t *pgdat; - int codepages = 0; - int datapages = 0; - int initpages = 0; int i; /* this will put all memory onto the freelists */ - num_physpages = 0; - for_each_online_pgdat(pgdat) { - num_physpages += pgdat->node_present_pages; - + for_each_online_pgdat(pgdat) free_all_bootmem_node(pgdat); - for (i = 0; i < pgdat->node_spanned_pages; i++) { - struct page *page = pgdat->node_mem_map + i; - char *addr = page_to_virt(page); - - if (!PageReserved(page)) - continue; - if (addr >= _text && - addr < _etext) - codepages++; - else if (addr >= __init_begin && - addr < __init_end) - initpages++; - else - datapages++; - } - } #if defined(CONFIG_MMU) && !defined(CONFIG_SUN3) && !defined(CONFIG_COLDFIRE) /* insert pointer tables allocated so far into the tablelist */ @@ -190,12 +168,7 @@ void __init mem_init(void) init_pointer_table((unsigned long)zero_pgtable); #endif - pr_info("Memory: %luk/%luk available (%dk kernel code, %dk data, %dk init)\n", - nr_free_pages() << (PAGE_SHIFT-10), - totalram_pages << (PAGE_SHIFT-10), - codepages << (PAGE_SHIFT-10), - datapages << (PAGE_SHIFT-10), - initpages << (PAGE_SHIFT-10)); + mem_init_print_info(NULL); print_memmap(); } -- cgit v1.2.3 From 132de6717c47f6fb1d4d3ccd6033cd45aee8f779 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:04:01 -0700 Subject: mm/metag: prepare for removing num_physpages and simplify mem_init() Prepare for removing num_physpages and simplify mem_init(). Signed-off-by: Jiang Liu Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/metag/mm/init.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/arch/metag/mm/init.c b/arch/metag/mm/init.c index ce81d7c43983..e0862b7ecd6e 100644 --- a/arch/metag/mm/init.c +++ b/arch/metag/mm/init.c @@ -388,22 +388,16 @@ void __init mem_init(void) reset_all_zones_managed_pages(); for (tmp = highstart_pfn; tmp < highend_pfn; tmp++) free_highmem_page(pfn_to_page(tmp)); - num_physpages += totalhigh_pages; #endif /* CONFIG_HIGHMEM */ for_each_online_node(nid) { pg_data_t *pgdat = NODE_DATA(nid); - num_physpages += pgdat->node_present_pages; - if (pgdat->node_spanned_pages) free_all_bootmem_node(pgdat); } - pr_info("Memory: %luk/%luk available\n", - (unsigned long)nr_free_pages() << (PAGE_SHIFT - 10), - num_physpages << (PAGE_SHIFT - 10)); - + mem_init_print_info(NULL); show_mem(0); return; -- cgit v1.2.3 From 6879ea83c6d14e4f21bf48b5780ed549197c668b Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:04:02 -0700 Subject: mm/microblaze: prepare for removing num_physpages and simplify mem_init() Prepare for removing num_physpages and simplify mem_init(). Signed-off-by: Jiang Liu Cc: Michal Simek Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/arm64/mm/init.c | 2 +- arch/microblaze/mm/init.c | 51 ++++++----------------------------------------- 2 files changed, 7 insertions(+), 46 deletions(-) diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c index b16c778ea0de..67e8d7ce3fe7 100644 --- a/arch/arm64/mm/init.c +++ b/arch/arm64/mm/init.c @@ -282,7 +282,7 @@ void __init mem_init(void) /* this will put all unused low memory onto the freelists */ free_all_bootmem(); - mem_init_print_info(); + mem_init_print_info(NULL); #define MLK(b, t) b, t, ((t) - (b)) >> 10 #define MLM(b, t) b, t, ((t) - (b)) >> 20 diff --git a/arch/microblaze/mm/init.c b/arch/microblaze/mm/init.c index b384cbc2c8f2..74c7bcc1e82d 100644 --- a/arch/microblaze/mm/init.c +++ b/arch/microblaze/mm/init.c @@ -71,24 +71,17 @@ static void __init highmem_init(void) kmap_prot = PAGE_KERNEL; } -static unsigned long highmem_setup(void) +static void highmem_setup(void) { unsigned long pfn; - unsigned long reservedpages = 0; for (pfn = max_low_pfn; pfn < max_pfn; ++pfn) { struct page *page = pfn_to_page(pfn); /* FIXME not sure about */ - if (memblock_is_reserved(pfn << PAGE_SHIFT)) - continue; - free_highmem_page(page); - reservedpages++; + if (!memblock_is_reserved(pfn << PAGE_SHIFT)) + free_highmem_page(page); } - pr_info("High memory: %luk\n", - totalhigh_pages << (PAGE_SHIFT-10)); - - return reservedpages; } #endif /* CONFIG_HIGHMEM */ @@ -167,13 +160,12 @@ void __init setup_memory(void) * min_low_pfn - the first page (mm/bootmem.c - node_boot_start) * max_low_pfn * max_mapnr - the first unused page (mm/bootmem.c - node_low_pfn) - * num_physpages - number of all pages */ /* memory start is from the kernel end (aligned) to higher addr */ min_low_pfn = memory_start >> PAGE_SHIFT; /* minimum for allocation */ /* RAM is assumed contiguous */ - num_physpages = max_mapnr = memory_size >> PAGE_SHIFT; + max_mapnr = memory_size >> PAGE_SHIFT; max_low_pfn = ((u64)memory_start + (u64)lowmem_size) >> PAGE_SHIFT; max_pfn = ((u64)memory_start + (u64)memory_size) >> PAGE_SHIFT; @@ -246,46 +238,15 @@ void free_initmem(void) void __init mem_init(void) { - pg_data_t *pgdat; - unsigned long reservedpages = 0, codesize, initsize, datasize, bsssize; - high_memory = (void *)__va(memory_start + lowmem_size - 1); /* this will put all memory onto the freelists */ free_all_bootmem(); - - for_each_online_pgdat(pgdat) { - unsigned long i; - struct page *page; - - for (i = 0; i < pgdat->node_spanned_pages; i++) { - if (!pfn_valid(pgdat->node_start_pfn + i)) - continue; - page = pgdat_page_nr(pgdat, i); - if (PageReserved(page)) - reservedpages++; - } - } - #ifdef CONFIG_HIGHMEM - reservedpages -= highmem_setup(); + highmem_setup(); #endif - codesize = (unsigned long)&_sdata - (unsigned long)&_stext; - datasize = (unsigned long)&_edata - (unsigned long)&_sdata; - initsize = (unsigned long)&__init_end - (unsigned long)&__init_begin; - bsssize = (unsigned long)&__bss_stop - (unsigned long)&__bss_start; - - pr_info("Memory: %luk/%luk available (%luk kernel code, ", - nr_free_pages() << (PAGE_SHIFT-10), - num_physpages << (PAGE_SHIFT-10), - codesize >> 10); - pr_cont("%luk reserved, %luk data, %luk bss, %luk init)\n", - reservedpages << (PAGE_SHIFT-10), - datasize >> 10, - bsssize >> 10, - initsize >> 10); - + mem_init_print_info(NULL); #ifdef CONFIG_MMU pr_info("Kernel virtual memory layout:\n"); pr_info(" * 0x%08lx..0x%08lx : fixmap\n", FIXADDR_START, FIXADDR_TOP); -- cgit v1.2.3 From 1132137e87898d0b6786d85a99de35ce196ecbfb Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:04:04 -0700 Subject: mm/MIPS: prepare for removing num_physpages and simplify mem_init() Prepare for removing num_physpages and simplify mem_init(). Signed-off-by: Jiang Liu Cc: Ralf Baechle Cc: David Daney Cc: Arnd Bergmann Cc: Jiri Kosina Cc: John Crispin Cc: Greg Kroah-Hartman Cc: Minchan Kim Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/mips/mm/init.c | 57 +++++++++++++--------------------------- arch/mips/pci/pci-lantiq.c | 2 +- arch/mips/sgi-ip27/ip27-memory.c | 21 ++------------- 3 files changed, 21 insertions(+), 59 deletions(-) diff --git a/arch/mips/mm/init.c b/arch/mips/mm/init.c index e7333f15b1b7..4e73f10a7519 100644 --- a/arch/mips/mm/init.c +++ b/arch/mips/mm/init.c @@ -359,11 +359,24 @@ void __init paging_init(void) static struct kcore_list kcore_kseg0; #endif -void __init mem_init(void) +static inline void mem_init_free_highmem(void) { - unsigned long codesize, reservedpages, datasize, initsize; - unsigned long tmp, ram; +#ifdef CONFIG_HIGHMEM + unsigned long tmp; + for (tmp = highstart_pfn; tmp < highend_pfn; tmp++) { + struct page *page = pfn_to_page(tmp); + + if (!page_is_ram(tmp)) + SetPageReserved(page); + else + free_highmem_page(page); + } +#endif +} + +void __init mem_init(void) +{ #ifdef CONFIG_HIGHMEM #ifdef CONFIG_DISCONTIGMEM #error "CONFIG_HIGHMEM and CONFIG_DISCONTIGMEM dont work together yet" @@ -376,32 +389,8 @@ void __init mem_init(void) free_all_bootmem(); setup_zero_pages(); /* Setup zeroed pages. */ - - reservedpages = ram = 0; - for (tmp = 0; tmp < max_low_pfn; tmp++) - if (page_is_ram(tmp) && pfn_valid(tmp)) { - ram++; - if (PageReserved(pfn_to_page(tmp))) - reservedpages++; - } - num_physpages = ram; - -#ifdef CONFIG_HIGHMEM - for (tmp = highstart_pfn; tmp < highend_pfn; tmp++) { - struct page *page = pfn_to_page(tmp); - - if (!page_is_ram(tmp)) { - SetPageReserved(page); - continue; - } - free_highmem_page(page); - } - num_physpages += totalhigh_pages; -#endif - - codesize = (unsigned long) &_etext - (unsigned long) &_text; - datasize = (unsigned long) &_edata - (unsigned long) &_etext; - initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; + mem_init_free_highmem(); + mem_init_print_info(NULL); #ifdef CONFIG_64BIT if ((unsigned long) &_text > (unsigned long) CKSEG0) @@ -410,16 +399,6 @@ void __init mem_init(void) kclist_add(&kcore_kseg0, (void *) CKSEG0, 0x80000000 - 4, KCORE_TEXT); #endif - - printk(KERN_INFO "Memory: %luk/%luk available (%ldk kernel code, " - "%ldk reserved, %ldk data, %ldk init, %ldk highmem)\n", - nr_free_pages() << (PAGE_SHIFT-10), - ram << (PAGE_SHIFT-10), - codesize >> 10, - reservedpages << (PAGE_SHIFT-10), - datasize >> 10, - initsize >> 10, - totalhigh_pages << (PAGE_SHIFT-10)); } #endif /* !CONFIG_NEED_MULTIPLE_NODES */ diff --git a/arch/mips/pci/pci-lantiq.c b/arch/mips/pci/pci-lantiq.c index 879077b01155..cb1ef9984069 100644 --- a/arch/mips/pci/pci-lantiq.c +++ b/arch/mips/pci/pci-lantiq.c @@ -89,7 +89,7 @@ static inline u32 ltq_calc_bar11mask(void) u32 mem, bar11mask; /* BAR11MASK value depends on available memory on system. */ - mem = num_physpages * PAGE_SIZE; + mem = get_num_physpages() * PAGE_SIZE; bar11mask = (0x0ffffff0 & ~((1 << (fls(mem) - 1)) - 1)) | 8; return bar11mask; diff --git a/arch/mips/sgi-ip27/ip27-memory.c b/arch/mips/sgi-ip27/ip27-memory.c index aecac4a08360..a0c9e34147c2 100644 --- a/arch/mips/sgi-ip27/ip27-memory.c +++ b/arch/mips/sgi-ip27/ip27-memory.c @@ -357,8 +357,6 @@ static void __init szmem(void) int slot; cnodeid_t node; - num_physpages = 0; - for_each_online_node(node) { nodebytes = 0; for (slot = 0; slot < MAX_MEM_SLOTS; slot++) { @@ -381,7 +379,6 @@ static void __init szmem(void) slot = MAX_MEM_SLOTS; continue; } - num_physpages += slot_psize; memblock_add_node(PFN_PHYS(slot_getbasepfn(node, slot)), PFN_PHYS(slot_psize), node); } @@ -480,10 +477,9 @@ void __init paging_init(void) void __init mem_init(void) { - unsigned long codesize, datasize, initsize, tmp; unsigned node; - high_memory = (void *) __va(num_physpages << PAGE_SHIFT); + high_memory = (void *) __va(get_num_physpages() << PAGE_SHIFT); for_each_online_node(node) { /* @@ -494,18 +490,5 @@ void __init mem_init(void) setup_zero_pages(); /* This comes from node 0 */ - codesize = (unsigned long) &_etext - (unsigned long) &_text; - datasize = (unsigned long) &_edata - (unsigned long) &_etext; - initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; - - tmp = nr_free_pages(); - printk(KERN_INFO "Memory: %luk/%luk available (%ldk kernel code, " - "%ldk reserved, %ldk data, %ldk init, %ldk highmem)\n", - tmp << (PAGE_SHIFT-10), - num_physpages << (PAGE_SHIFT-10), - codesize >> 10, - (num_physpages - tmp) << (PAGE_SHIFT-10), - datasize >> 10, - initsize >> 10, - totalhigh_pages << (PAGE_SHIFT-10)); + mem_init_print_info(NULL); } -- cgit v1.2.3 From 76feaedeb9f33011c7048d4339d18c3fd342f984 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:04:05 -0700 Subject: mm/mn10300: prepare for removing num_physpages and simplify mem_init() Prepare for removing num_physpages and simplify mem_init(). Signed-off-by: Jiang Liu Cc: David Howells Cc: Koichi Yasutake Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/mn10300/mm/init.c | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/arch/mn10300/mm/init.c b/arch/mn10300/mm/init.c index 7590d91627f2..97a1ec0beeec 100644 --- a/arch/mn10300/mm/init.c +++ b/arch/mn10300/mm/init.c @@ -99,15 +99,12 @@ void __init paging_init(void) */ void __init mem_init(void) { - int codesize, reservedpages, datasize, initsize; - int tmp; - BUG_ON(!mem_map); #define START_PFN (contig_page_data.bdata->node_min_pfn) #define MAX_LOW_PFN (contig_page_data.bdata->node_low_pfn) - max_mapnr = num_physpages = MAX_LOW_PFN - START_PFN; + max_mapnr = MAX_LOW_PFN - START_PFN; high_memory = (void *) __va(MAX_LOW_PFN * PAGE_SIZE); /* clear the zero-page */ @@ -116,26 +113,7 @@ void __init mem_init(void) /* this will put all low memory onto the freelists */ free_all_bootmem(); - reservedpages = 0; - for (tmp = 0; tmp < num_physpages; tmp++) - if (PageReserved(&mem_map[tmp])) - reservedpages++; - - codesize = (unsigned long) &_etext - (unsigned long) &_stext; - datasize = (unsigned long) &_edata - (unsigned long) &_etext; - initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; - - printk(KERN_INFO - "Memory: %luk/%luk available" - " (%dk kernel code, %dk reserved, %dk data, %dk init," - " %ldk highmem)\n", - nr_free_pages() << (PAGE_SHIFT - 10), - max_mapnr << (PAGE_SHIFT - 10), - codesize >> 10, - reservedpages << (PAGE_SHIFT - 10), - datasize >> 10, - initsize >> 10, - totalhigh_pages << (PAGE_SHIFT - 10)); + mem_init_print_info(NULL); } /* -- cgit v1.2.3 From 1173db12bf145a0d0c6a2716e861de2fafde0522 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:04:06 -0700 Subject: mm/openrisc: prepare for removing num_physpages and simplify mem_init() Prepare for removing num_physpages and simplify mem_init(). Signed-off-by: Jiang Liu Acked-by: Jonas Bonn Cc: David Howells Cc: Arnd Bergmann Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/openrisc/mm/init.c | 44 ++++---------------------------------------- 1 file changed, 4 insertions(+), 40 deletions(-) diff --git a/arch/openrisc/mm/init.c b/arch/openrisc/mm/init.c index 16c1e135cf34..7f94652311d7 100644 --- a/arch/openrisc/mm/init.c +++ b/arch/openrisc/mm/init.c @@ -202,56 +202,20 @@ void __init paging_init(void) /* References to section boundaries */ -static int __init free_pages_init(void) -{ - int reservedpages, pfn; - - /* this will put all low memory onto the freelists */ - free_all_bootmem(); - - reservedpages = 0; - for (pfn = 0; pfn < max_low_pfn; pfn++) { - /* - * Only count reserved RAM pages - */ - if (PageReserved(mem_map + pfn)) - reservedpages++; - } - - return reservedpages; -} - -static void __init set_max_mapnr_init(void) -{ - max_mapnr = num_physpages = max_low_pfn; -} - void __init mem_init(void) { - int codesize, reservedpages, datasize, initsize; - BUG_ON(!mem_map); - set_max_mapnr_init(); - + max_mapnr = max_low_pfn; high_memory = (void *)__va(max_low_pfn * PAGE_SIZE); /* clear the zero-page */ memset((void *)empty_zero_page, 0, PAGE_SIZE); - reservedpages = free_pages_init(); - - codesize = (unsigned long)&_etext - (unsigned long)&_stext; - datasize = (unsigned long)&_edata - (unsigned long)&_etext; - initsize = (unsigned long)&__init_end - (unsigned long)&__init_begin; + /* this will put all low memory onto the freelists */ + free_all_bootmem(); - printk(KERN_INFO - "Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data, %dk init, %ldk highmem)\n", - (unsigned long)nr_free_pages() << (PAGE_SHIFT - 10), - max_mapnr << (PAGE_SHIFT - 10), codesize >> 10, - reservedpages << (PAGE_SHIFT - 10), datasize >> 10, - initsize >> 10, (unsigned long)(0 << (PAGE_SHIFT - 10)) - ); + mem_init_print_info(NULL); printk("mem_init_done ...........................................\n"); mem_init_done = 1; -- cgit v1.2.3 From 7d2c7747086354f7889e8d577c537f9ad8985230 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:04:07 -0700 Subject: mm/PARISC: prepare for removing num_physpages and simplify mem_init() Prepare for removing num_physpages and simplify mem_init(). Signed-off-by: Jiang Liu Cc: "James E.J. Bottomley" Cc: Helge Deller Cc: Thomas Gleixner Cc: Michal Hocko Cc: David Rientjes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/parisc/mm/init.c | 47 +++-------------------------------------------- 1 file changed, 3 insertions(+), 44 deletions(-) diff --git a/arch/parisc/mm/init.c b/arch/parisc/mm/init.c index d8aaaf06ede2..b4edc76c6f22 100644 --- a/arch/parisc/mm/init.c +++ b/arch/parisc/mm/init.c @@ -214,7 +214,6 @@ static void __init setup_bootmem(void) mem_limit_func(); /* check for "mem=" argument */ mem_max = 0; - num_physpages = 0; for (i = 0; i < npmem_ranges; i++) { unsigned long rsize; @@ -229,10 +228,8 @@ static void __init setup_bootmem(void) npmem_ranges = i + 1; mem_max = mem_limit; } - num_physpages += pmem_ranges[i].pages; break; } - num_physpages += pmem_ranges[i].pages; mem_max += rsize; } @@ -532,7 +529,7 @@ void free_initmem(void) * pages are no-longer executable */ flush_icache_range(init_begin, init_end); - num_physpages += free_initmem_default(-1); + free_initmem_default(-1); /* set up a new led state on systems shipped LED State panel */ pdc_chassis_send_status(PDC_CHASSIS_DIRECT_BCOMPLETE); @@ -580,8 +577,6 @@ unsigned long pcxl_dma_start __read_mostly; void __init mem_init(void) { - int codesize, reservedpages, datasize, initsize; - /* Do sanity checks on page table constants */ BUILD_BUG_ON(PTE_ENTRY_SIZE != sizeof(pte_t)); BUILD_BUG_ON(PMD_ENTRY_SIZE != sizeof(pmd_t)); @@ -603,33 +598,6 @@ void __init mem_init(void) } #endif - codesize = (unsigned long)_etext - (unsigned long)_text; - datasize = (unsigned long)_edata - (unsigned long)_etext; - initsize = (unsigned long)__init_end - (unsigned long)__init_begin; - - reservedpages = 0; -{ - unsigned long pfn; -#ifdef CONFIG_DISCONTIGMEM - int i; - - for (i = 0; i < npmem_ranges; i++) { - for (pfn = node_start_pfn(i); pfn < node_end_pfn(i); pfn++) { - if (PageReserved(pfn_to_page(pfn))) - reservedpages++; - } - } -#else /* !CONFIG_DISCONTIGMEM */ - for (pfn = 0; pfn < max_pfn; pfn++) { - /* - * Only count reserved RAM pages - */ - if (PageReserved(pfn_to_page(pfn))) - reservedpages++; - } -#endif -} - #ifdef CONFIG_PA11 if (hppa_dma_ops == &pcxl_dma_ops) { pcxl_dma_start = (unsigned long)SET_MAP_OFFSET(MAP_START); @@ -643,15 +611,7 @@ void __init mem_init(void) parisc_vmalloc_start = SET_MAP_OFFSET(MAP_START); #endif - printk(KERN_INFO "Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data, %dk init)\n", - nr_free_pages() << (PAGE_SHIFT-10), - num_physpages << (PAGE_SHIFT-10), - codesize >> 10, - reservedpages << (PAGE_SHIFT-10), - datasize >> 10, - initsize >> 10 - ); - + mem_init_print_info(NULL); #ifdef CONFIG_DEBUG_KERNEL /* double-sanity-check paranoia */ printk("virtual kernel memory layout:\n" " vmalloc : 0x%p - 0x%p (%4ld MB)\n" @@ -1101,7 +1061,6 @@ void flush_tlb_all(void) #ifdef CONFIG_BLK_DEV_INITRD void free_initrd_mem(unsigned long start, unsigned long end) { - num_physpages += free_reserved_area((void *)start, (void *)end, -1, - "initrd"); + free_reserved_area((void *)start, (void *)end, -1, "initrd"); } #endif -- cgit v1.2.3 From 369a9d8523dc7317eccb64b7aee6e9641d84cc8b Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:04:09 -0700 Subject: mm/ppc: prepare for removing num_physpages and simplify mem_init() Prepare for removing num_physpages and simplify mem_init(). Signed-off-by: Jiang Liu Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/powerpc/mm/mem.c | 56 +++++++++++---------------------------------------- 1 file changed, 12 insertions(+), 44 deletions(-) diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c index 3bcfc0d0d322..49c18b60bbb4 100644 --- a/arch/powerpc/mm/mem.c +++ b/arch/powerpc/mm/mem.c @@ -299,46 +299,27 @@ void __init paging_init(void) void __init mem_init(void) { -#ifdef CONFIG_NEED_MULTIPLE_NODES - int nid; -#endif - pg_data_t *pgdat; - unsigned long i; - struct page *page; - unsigned long reservedpages = 0, codesize, initsize, datasize, bsssize; - #ifdef CONFIG_SWIOTLB swiotlb_init(0); #endif - num_physpages = memblock_phys_mem_size() >> PAGE_SHIFT; high_memory = (void *) __va(max_low_pfn * PAGE_SIZE); #ifdef CONFIG_NEED_MULTIPLE_NODES - for_each_online_node(nid) { - if (NODE_DATA(nid)->node_spanned_pages != 0) { - printk("freeing bootmem node %d\n", nid); - free_all_bootmem_node(NODE_DATA(nid)); - } + { + pg_data_t *pgdat; + + for_each_online_pgdat(pgdat) + if (pgdat->node_spanned_pages != 0) { + printk("freeing bootmem node %d\n", + pgdat->node_id); + free_all_bootmem_node(pgdat); + } } #else max_mapnr = max_pfn; free_all_bootmem(); #endif - for_each_online_pgdat(pgdat) { - for (i = 0; i < pgdat->node_spanned_pages; i++) { - if (!pfn_valid(pgdat->node_start_pfn + i)) - continue; - page = pgdat_page_nr(pgdat, i); - if (PageReserved(page)) - reservedpages++; - } - } - - codesize = (unsigned long)&_sdata - (unsigned long)&_stext; - datasize = (unsigned long)&_edata - (unsigned long)&_sdata; - initsize = (unsigned long)&__init_end - (unsigned long)&__init_begin; - bsssize = (unsigned long)&__bss_stop - (unsigned long)&__bss_start; #ifdef CONFIG_HIGHMEM { @@ -348,13 +329,9 @@ void __init mem_init(void) for (pfn = highmem_mapnr; pfn < max_mapnr; ++pfn) { phys_addr_t paddr = (phys_addr_t)pfn << PAGE_SHIFT; struct page *page = pfn_to_page(pfn); - if (memblock_is_reserved(paddr)) - continue; - free_highmem_page(page); - reservedpages--; + if (!memblock_is_reserved(paddr)) + free_highmem_page(page); } - printk(KERN_DEBUG "High memory: %luk\n", - totalhigh_pages << (PAGE_SHIFT-10)); } #endif /* CONFIG_HIGHMEM */ @@ -367,16 +344,7 @@ void __init mem_init(void) (mfspr(SPRN_TLB1CFG) & TLBnCFG_N_ENTRY) - 1; #endif - printk(KERN_INFO "Memory: %luk/%luk available (%luk kernel code, " - "%luk reserved, %luk data, %luk bss, %luk init)\n", - nr_free_pages() << (PAGE_SHIFT-10), - num_physpages << (PAGE_SHIFT-10), - codesize >> 10, - reservedpages << (PAGE_SHIFT-10), - datasize >> 10, - bsssize >> 10, - initsize >> 10); - + mem_init_print_info(NULL); #ifdef CONFIG_PPC32 pr_info("Kernel virtual memory layout:\n"); pr_info(" * 0x%08lx..0x%08lx : fixmap\n", FIXADDR_START, FIXADDR_TOP); -- cgit v1.2.3 From a18d0e2d7097937e9f51b83eda4bc750d93eb34d Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:04:10 -0700 Subject: mm/s390: prepare for removing num_physpages and simplify mem_init() Prepare for removing num_physpages and simplify mem_init(). Signed-off-by: Jiang Liu Cc: Martin Schwidefsky Cc: Heiko Carstens Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/s390/mm/init.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/arch/s390/mm/init.c b/arch/s390/mm/init.c index a2aafe1b2300..ce36ea80e4f9 100644 --- a/arch/s390/mm/init.c +++ b/arch/s390/mm/init.c @@ -135,9 +135,7 @@ void __init paging_init(void) void __init mem_init(void) { - unsigned long codesize, reservedpages, datasize, initsize; - - max_mapnr = num_physpages = max_low_pfn; + max_mapnr = max_low_pfn; high_memory = (void *) __va(max_low_pfn * PAGE_SIZE); /* Setup guest page hinting */ @@ -147,18 +145,7 @@ void __init mem_init(void) free_all_bootmem(); setup_zero_pages(); /* Setup zeroed pages. */ - reservedpages = 0; - - codesize = (unsigned long) &_etext - (unsigned long) &_text; - datasize = (unsigned long) &_edata - (unsigned long) &_etext; - initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; - printk("Memory: %luk/%luk available (%ldk kernel code, %ldk reserved, %ldk data, %ldk init)\n", - nr_free_pages() << (PAGE_SHIFT-10), - max_mapnr << (PAGE_SHIFT-10), - codesize >> 10, - reservedpages << (PAGE_SHIFT-10), - datasize >>10, - initsize >> 10); + mem_init_print_info(NULL); printk("Write protected kernel read-only data: %#lx - %#lx\n", (unsigned long)&_stext, PFN_ALIGN((unsigned long)&_eshared) - 1); -- cgit v1.2.3 From ad941989d0cf2ef1320ecf46ee1188df85e94b43 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:04:11 -0700 Subject: mm/score: prepare for removing num_physpages and simplify mem_init() Prepare for removing num_physpages and simplify mem_init(). Signed-off-by: Jiang Liu Cc: Chen Liqin Cc: Lennox Wu Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/score/mm/init.c | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/arch/score/mm/init.c b/arch/score/mm/init.c index a8b917742dec..9fbce49ad3bd 100644 --- a/arch/score/mm/init.c +++ b/arch/score/mm/init.c @@ -75,33 +75,11 @@ void __init paging_init(void) void __init mem_init(void) { - unsigned long codesize, reservedpages, datasize, initsize; - unsigned long tmp, ram = 0; - high_memory = (void *) __va(max_low_pfn << PAGE_SHIFT); free_all_bootmem(); setup_zero_page(); /* Setup zeroed pages. */ - reservedpages = 0; - - for (tmp = 0; tmp < max_low_pfn; tmp++) - if (page_is_ram(tmp)) { - ram++; - if (PageReserved(pfn_to_page(tmp))) - reservedpages++; - } - - num_physpages = ram; - codesize = (unsigned long) &_etext - (unsigned long) &_text; - datasize = (unsigned long) &_edata - (unsigned long) &_etext; - initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; - - printk(KERN_INFO "Memory: %luk/%luk available (%ldk kernel code, " - "%ldk reserved, %ldk data, %ldk init, %ldk highmem)\n", - (unsigned long) nr_free_pages() << (PAGE_SHIFT-10), - ram << (PAGE_SHIFT-10), codesize >> 10, - reservedpages << (PAGE_SHIFT-10), datasize >> 10, - initsize >> 10, - totalhigh_pages << (PAGE_SHIFT-10)); + + mem_init_print_info(NULL); } #endif /* !CONFIG_NEED_MULTIPLE_NODES */ -- cgit v1.2.3 From da61efcfedabf16e994d67f983588683f3d59837 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:04:12 -0700 Subject: mm/SH: prepare for removing num_physpages and simplify mem_init() Prepare for removing num_physpages and simplify mem_init(). Signed-off-by: Jiang Liu Cc: Paul Mundt Cc: Wen Congyang Cc: Tang Chen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/sh/mm/init.c | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/arch/sh/mm/init.c b/arch/sh/mm/init.c index fc0c8e1c32a7..c9a517c8bbb6 100644 --- a/arch/sh/mm/init.c +++ b/arch/sh/mm/init.c @@ -407,24 +407,18 @@ unsigned int mem_init_done = 0; void __init mem_init(void) { - int codesize, datasize, initsize; - int nid; + pg_data_t *pgdat; iommu_init(); - num_physpages = 0; high_memory = NULL; - for_each_online_node(nid) { - pg_data_t *pgdat = NODE_DATA(nid); + for_each_online_pgdat(pgdat) { void *node_high_memory; - num_physpages += pgdat->node_present_pages; - if (pgdat->node_spanned_pages) free_all_bootmem_node(pgdat); - node_high_memory = (void *)__va((pgdat->node_start_pfn + pgdat->node_spanned_pages) << PAGE_SHIFT); @@ -441,19 +435,8 @@ void __init mem_init(void) vsyscall_init(); - codesize = (unsigned long) &_etext - (unsigned long) &_text; - datasize = (unsigned long) &_edata - (unsigned long) &_etext; - initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; - - printk(KERN_INFO "Memory: %luk/%luk available (%dk kernel code, " - "%dk data, %dk init)\n", - nr_free_pages() << (PAGE_SHIFT-10), - num_physpages << (PAGE_SHIFT-10), - codesize >> 10, - datasize >> 10, - initsize >> 10); - - printk(KERN_INFO "virtual kernel memory layout:\n" + mem_init_print_info(NULL); + pr_info("virtual kernel memory layout:\n" " fixmap : 0x%08lx - 0x%08lx (%4ld kB)\n" #ifdef CONFIG_HIGHMEM " pkmap : 0x%08lx - 0x%08lx (%4ld kB)\n" -- cgit v1.2.3 From dceccbe9209bded817f39c01a8a8bd1e69d6277a Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:04:14 -0700 Subject: mm/SPARC: prepare for removing num_physpages and simplify mem_init() Prepare for removing num_physpages and simplify mem_init(). Signed-off-by: Jiang Liu Acked-by: Sam Ravnborg Cc: "David S. Miller" Cc: Yasuaki Ishimatsu Cc: Tang Chen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/sparc/kernel/leon_smp.c | 3 --- arch/sparc/mm/init_32.c | 34 ++++------------------------------ arch/sparc/mm/init_64.c | 24 +++--------------------- 3 files changed, 7 insertions(+), 54 deletions(-) diff --git a/arch/sparc/kernel/leon_smp.c b/arch/sparc/kernel/leon_smp.c index 6cfc1b09ec25..d7aa524b7283 100644 --- a/arch/sparc/kernel/leon_smp.c +++ b/arch/sparc/kernel/leon_smp.c @@ -254,15 +254,12 @@ void __init leon_smp_done(void) /* Free unneeded trap tables */ if (!cpu_present(1)) { free_reserved_page(virt_to_page(&trapbase_cpu1)); - num_physpages++; } if (!cpu_present(2)) { free_reserved_page(virt_to_page(&trapbase_cpu2)); - num_physpages++; } if (!cpu_present(3)) { free_reserved_page(virt_to_page(&trapbase_cpu3)); - num_physpages++; } /* Ok, they are spinning and ready to go. */ smp_processors_ready = 1; diff --git a/arch/sparc/mm/init_32.c b/arch/sparc/mm/init_32.c index a438abb5495e..db6987082805 100644 --- a/arch/sparc/mm/init_32.c +++ b/arch/sparc/mm/init_32.c @@ -288,10 +288,6 @@ static void map_high_region(unsigned long start_pfn, unsigned long end_pfn) void __init mem_init(void) { - int codepages = 0; - int datapages = 0; - int initpages = 0; - int reservedpages = 0; int i; if (PKMAP_BASE+LAST_PKMAP*PAGE_SIZE >= FIXADDR_START) { @@ -329,8 +325,6 @@ void __init mem_init(void) unsigned long start_pfn = sp_banks[i].base_addr >> PAGE_SHIFT; unsigned long end_pfn = (sp_banks[i].base_addr + sp_banks[i].num_bytes) >> PAGE_SHIFT; - num_physpages += sp_banks[i].num_bytes >> PAGE_SHIFT; - if (end_pfn <= highstart_pfn) continue; @@ -340,39 +334,19 @@ void __init mem_init(void) map_high_region(start_pfn, end_pfn); } - codepages = (((unsigned long) &_etext) - ((unsigned long)&_start)); - codepages = PAGE_ALIGN(codepages) >> PAGE_SHIFT; - datapages = (((unsigned long) &_edata) - ((unsigned long)&_etext)); - datapages = PAGE_ALIGN(datapages) >> PAGE_SHIFT; - initpages = (((unsigned long) &__init_end) - ((unsigned long) &__init_begin)); - initpages = PAGE_ALIGN(initpages) >> PAGE_SHIFT; - - /* Ignore memory holes for the purpose of counting reserved pages */ - for (i=0; i < max_low_pfn; i++) - if (test_bit(i >> (20 - PAGE_SHIFT), sparc_valid_addr_bitmap) - && PageReserved(pfn_to_page(i))) - reservedpages++; - - printk(KERN_INFO "Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data, %dk init, %ldk highmem)\n", - nr_free_pages() << (PAGE_SHIFT-10), - num_physpages << (PAGE_SHIFT - 10), - codepages << (PAGE_SHIFT-10), - reservedpages << (PAGE_SHIFT - 10), - datapages << (PAGE_SHIFT-10), - initpages << (PAGE_SHIFT-10), - totalhigh_pages << (PAGE_SHIFT-10)); + mem_init_print_info(NULL); } void free_initmem (void) { - num_physpages += free_initmem_default(POISON_FREE_INITMEM); + free_initmem_default(POISON_FREE_INITMEM); } #ifdef CONFIG_BLK_DEV_INITRD void free_initrd_mem(unsigned long start, unsigned long end) { - num_physpages += free_reserved_area((void *)start, (void *)end, - POISON_FREE_INITMEM, "initrd"); + free_reserved_area((void *)start, (void *)end, POISON_FREE_INITMEM, + "initrd"); } #endif diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c index 752d73837f9e..a9c42a7ffb6a 100644 --- a/arch/sparc/mm/init_64.c +++ b/arch/sparc/mm/init_64.c @@ -2045,7 +2045,6 @@ static void __init register_page_bootmem_info(void) } void __init mem_init(void) { - unsigned long codepages, datapages, initpages; unsigned long addr, last; addr = PAGE_OFFSET + kern_base; @@ -2063,11 +2062,6 @@ void __init mem_init(void) register_page_bootmem_info(); free_all_bootmem(); - /* We subtract one to account for the mem_map_zero page - * allocated below. - */ - num_physpages = totalram_pages - 1; - /* * Set up the zero page, mark it reserved, so that page count * is not manipulated when freeing the page from user ptes. @@ -2079,19 +2073,7 @@ void __init mem_init(void) } mark_page_reserved(mem_map_zero); - codepages = (((unsigned long) _etext) - ((unsigned long) _start)); - codepages = PAGE_ALIGN(codepages) >> PAGE_SHIFT; - datapages = (((unsigned long) _edata) - ((unsigned long) _etext)); - datapages = PAGE_ALIGN(datapages) >> PAGE_SHIFT; - initpages = (((unsigned long) __init_end) - ((unsigned long) __init_begin)); - initpages = PAGE_ALIGN(initpages) >> PAGE_SHIFT; - - printk("Memory: %luk available (%ldk kernel code, %ldk data, %ldk init) [%016lx,%016lx]\n", - nr_free_pages() << (PAGE_SHIFT-10), - codepages << (PAGE_SHIFT-10), - datapages << (PAGE_SHIFT-10), - initpages << (PAGE_SHIFT-10), - PAGE_OFFSET, (last_valid_pfn << PAGE_SHIFT)); + mem_init_print_info(NULL); if (tlb_type == cheetah || tlb_type == cheetah_plus) cheetah_ecache_flush_init(); @@ -2131,8 +2113,8 @@ void free_initmem(void) #ifdef CONFIG_BLK_DEV_INITRD void free_initrd_mem(unsigned long start, unsigned long end) { - num_physpages += free_reserved_area((void *)start, (void *)end, - POISON_FREE_INITMEM, "initrd"); + free_reserved_area((void *)start, (void *)end, POISON_FREE_INITMEM, + "initrd"); } #endif -- cgit v1.2.3 From 3f29c33194d3b5fffdccc89665982a36b408b6f9 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:04:15 -0700 Subject: mm/tile: prepare for removing num_physpages and simplify mem_init() Prepare for removing num_physpages and simplify mem_init(). Signed-off-by: Jiang Liu Acked-by: Chris Metcalf Cc: Bjorn Helgaas Cc: "David S. Miller" Cc: Wen Congyang Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/tile/kernel/setup.c | 16 ++++++++-------- arch/tile/mm/init.c | 15 +-------------- 2 files changed, 9 insertions(+), 22 deletions(-) diff --git a/arch/tile/kernel/setup.c b/arch/tile/kernel/setup.c index 41818e3ce97e..68b542677f6a 100644 --- a/arch/tile/kernel/setup.c +++ b/arch/tile/kernel/setup.c @@ -329,6 +329,7 @@ static void __init setup_memory(void) #if defined(CONFIG_HIGHMEM) || defined(__tilegx__) long lowmem_pages; #endif + unsigned long physpages = 0; /* We are using a char to hold the cpu_2_node[] mapping */ BUILD_BUG_ON(MAX_NUMNODES > 127); @@ -388,8 +389,8 @@ static void __init setup_memory(void) continue; } } - if (num_physpages + PFN_DOWN(range.size) > maxmem_pfn) { - int max_size = maxmem_pfn - num_physpages; + if (physpages + PFN_DOWN(range.size) > maxmem_pfn) { + int max_size = maxmem_pfn - physpages; if (max_size > 0) { pr_err("Maxmem reduced node %d to %d pages\n", i, max_size); @@ -446,7 +447,7 @@ static void __init setup_memory(void) node_start_pfn[i] = start; node_end_pfn[i] = end; node_controller[i] = range.controller; - num_physpages += size; + physpages += size; max_pfn = end; /* Mark node as online */ @@ -465,7 +466,7 @@ static void __init setup_memory(void) * we're willing to use at 8 million pages (32GB of 4KB pages). */ cap = 8 * 1024 * 1024; /* 8 million pages */ - if (num_physpages > cap) { + if (physpages > cap) { int num_nodes = num_online_nodes(); int cap_each = cap / num_nodes; unsigned long dropped_pages = 0; @@ -476,10 +477,10 @@ static void __init setup_memory(void) node_end_pfn[i] = node_start_pfn[i] + cap_each; } } - num_physpages -= dropped_pages; + physpages -= dropped_pages; pr_warning("Only using %ldMB memory;" " ignoring %ldMB.\n", - num_physpages >> (20 - PAGE_SHIFT), + physpages >> (20 - PAGE_SHIFT), dropped_pages >> (20 - PAGE_SHIFT)); pr_warning("Consider using a larger page size.\n"); } @@ -497,7 +498,7 @@ static void __init setup_memory(void) lowmem_pages = (mappable_physpages > MAXMEM_PFN) ? MAXMEM_PFN : mappable_physpages; - highmem_pages = (long) (num_physpages - lowmem_pages); + highmem_pages = (long) (physpages - lowmem_pages); pr_notice("%ldMB HIGHMEM available.\n", pages_to_mb(highmem_pages > 0 ? highmem_pages : 0)); @@ -514,7 +515,6 @@ static void __init setup_memory(void) pr_warning("Use a HIGHMEM enabled kernel.\n"); max_low_pfn = MAXMEM_PFN; max_pfn = MAXMEM_PFN; - num_physpages = MAXMEM_PFN; node_end_pfn[0] = MAXMEM_PFN; } else { pr_notice("%ldMB memory available.\n", diff --git a/arch/tile/mm/init.c b/arch/tile/mm/init.c index f2ac2f405775..e182958c707d 100644 --- a/arch/tile/mm/init.c +++ b/arch/tile/mm/init.c @@ -821,7 +821,6 @@ static void __init set_max_mapnr_init(void) void __init mem_init(void) { - int codesize, datasize, initsize; int i; #ifndef __tilegx__ void *last; @@ -853,19 +852,7 @@ void __init mem_init(void) set_non_bootmem_pages_init(); #endif - codesize = (unsigned long)&_etext - (unsigned long)&_text; - datasize = (unsigned long)&_end - (unsigned long)&_sdata; - initsize = (unsigned long)&_einittext - (unsigned long)&_sinittext; - initsize += (unsigned long)&_einitdata - (unsigned long)&_sinitdata; - - pr_info("Memory: %luk/%luk available (%dk kernel code, %dk data, %dk init, %ldk highmem)\n", - (unsigned long) nr_free_pages() << (PAGE_SHIFT-10), - num_physpages << (PAGE_SHIFT-10), - codesize >> 10, - datasize >> 10, - initsize >> 10, - (unsigned long) (totalhigh_pages << (PAGE_SHIFT-10)) - ); + mem_init_print_info(NULL); /* * In debug mode, dump some interesting memory mappings. -- cgit v1.2.3 From 715ee356535e861c0c0890697fa48eeb8af94019 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:04:16 -0700 Subject: mm/um: prepare for removing num_physpages and simplify mem_init() Prepare for removing num_physpages and simplify mem_init(). Signed-off-by: Jiang Liu Cc: Jeff Dike Cc: Richard Weinberger Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/um/kernel/mem.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/arch/um/kernel/mem.c b/arch/um/kernel/mem.c index b0c763094ffb..7ddb64baf327 100644 --- a/arch/um/kernel/mem.c +++ b/arch/um/kernel/mem.c @@ -70,10 +70,8 @@ void __init mem_init(void) #ifdef CONFIG_HIGHMEM setup_highmem(end_iomem, highmem); #endif - num_physpages = totalram_pages; max_pfn = totalram_pages; - printk(KERN_INFO "Memory: %luk available\n", - nr_free_pages() << (PAGE_SHIFT-10)); + mem_init_print_info(NULL); kmalloc_ok = 1; } -- cgit v1.2.3 From 0d0b6d26ad52093452b1228325a99244e925582a Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:04:17 -0700 Subject: mm/unicore32: prepare for removing num_physpages and simplify mem_init() Prepare for removing num_physpages and simplify mem_init(). Signed-off-by: Jiang Liu Cc: Guan Xuetao Cc: Michal Hocko Cc: David Rientjes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/unicore32/mm/init.c | 49 ++---------------------------------------------- 1 file changed, 2 insertions(+), 47 deletions(-) diff --git a/arch/unicore32/mm/init.c b/arch/unicore32/mm/init.c index 7d1356c466b9..ae6bc036db92 100644 --- a/arch/unicore32/mm/init.c +++ b/arch/unicore32/mm/init.c @@ -383,10 +383,6 @@ static void __init free_unused_memmap(struct meminfo *mi) */ void __init mem_init(void) { - unsigned long reserved_pages, free_pages; - struct memblock_region *reg; - int i; - max_mapnr = pfn_to_page(max_pfn + PHYS_PFN_OFFSET) - mem_map; free_unused_memmap(&meminfo); @@ -394,48 +390,7 @@ void __init mem_init(void) /* this will put all unused low memory onto the freelists */ free_all_bootmem(); - reserved_pages = free_pages = 0; - - for_each_bank(i, &meminfo) { - struct membank *bank = &meminfo.bank[i]; - unsigned int pfn1, pfn2; - struct page *page, *end; - - pfn1 = bank_pfn_start(bank); - pfn2 = bank_pfn_end(bank); - - page = pfn_to_page(pfn1); - end = pfn_to_page(pfn2 - 1) + 1; - - do { - if (PageReserved(page)) - reserved_pages++; - else if (!page_count(page)) - free_pages++; - page++; - } while (page < end); - } - - /* - * Since our memory may not be contiguous, calculate the - * real number of pages we have in this system - */ - printk(KERN_INFO "Memory:"); - num_physpages = 0; - for_each_memblock(memory, reg) { - unsigned long pages = memblock_region_memory_end_pfn(reg) - - memblock_region_memory_base_pfn(reg); - num_physpages += pages; - printk(" %ldMB", pages >> (20 - PAGE_SHIFT)); - } - printk(" = %luMB total\n", num_physpages >> (20 - PAGE_SHIFT)); - - printk(KERN_NOTICE "Memory: %luk/%luk available, %luk reserved, %luK highmem\n", - nr_free_pages() << (PAGE_SHIFT-10), - free_pages << (PAGE_SHIFT-10), - reserved_pages << (PAGE_SHIFT-10), - totalhigh_pages << (PAGE_SHIFT-10)); - + mem_init_print_info(NULL); printk(KERN_NOTICE "Virtual kernel memory layout:\n" " vector : 0x%08lx - 0x%08lx (%4ld kB)\n" " vmalloc : 0x%08lx - 0x%08lx (%4ld MB)\n" @@ -464,7 +419,7 @@ void __init mem_init(void) BUILD_BUG_ON(TASK_SIZE > MODULES_VADDR); BUG_ON(TASK_SIZE > MODULES_VADDR); - if (PAGE_SIZE >= 16384 && num_physpages <= 128) { + if (PAGE_SIZE >= 16384 && get_num_physpages() <= 128) { /* * On a machine this small we won't get * anywhere without overcommit, so turn -- cgit v1.2.3 From 46a841329a6cd6298e131afd82e7d58130b19025 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:04:19 -0700 Subject: mm/x86: prepare for removing num_physpages and simplify mem_init() Prepare for removing num_physpages and simplify mem_init(). Signed-off-by: Jiang Liu Cc: Thomas Gleixner Cc: Ingo Molnar Cc: "H. Peter Anvin" Cc: Andreas Herrmann Cc: Tang Chen Cc: Wen Congyang Cc: Jianguo Wu Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/x86/kernel/cpu/amd.c | 2 +- arch/x86/kernel/setup.c | 2 -- arch/x86/mm/init_32.c | 30 ++---------------------------- arch/x86/mm/init_64.c | 20 +------------------- arch/x86/mm/numa_32.c | 2 -- 5 files changed, 4 insertions(+), 52 deletions(-) diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index 5013a48d1aff..c587a8757227 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c @@ -90,7 +90,7 @@ static void __cpuinit init_amd_k5(struct cpuinfo_x86 *c) static void __cpuinit init_amd_k6(struct cpuinfo_x86 *c) { u32 l, h; - int mbytes = num_physpages >> (20-PAGE_SHIFT); + int mbytes = get_num_physpages() >> (20-PAGE_SHIFT); if (c->x86_model < 6) { /* Based on AMD doc 20734R - June 2000 */ diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index 56f7fcfe7fa2..e68709da8251 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -1040,8 +1040,6 @@ void __init setup_arch(char **cmdline_p) /* max_low_pfn get updated here */ find_low_pfn_range(); #else - num_physpages = max_pfn; - check_x2apic(); /* How many end-of-memory variables you have, grandma! */ diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c index 9fa46baada27..4287f1ffba7e 100644 --- a/arch/x86/mm/init_32.c +++ b/arch/x86/mm/init_32.c @@ -660,10 +660,8 @@ void __init initmem_init(void) highstart_pfn = max_low_pfn; printk(KERN_NOTICE "%ldMB HIGHMEM available.\n", pages_to_mb(highend_pfn - highstart_pfn)); - num_physpages = highend_pfn; high_memory = (void *) __va(highstart_pfn * PAGE_SIZE - 1) + 1; #else - num_physpages = max_low_pfn; high_memory = (void *) __va(max_low_pfn * PAGE_SIZE - 1) + 1; #endif @@ -671,7 +669,7 @@ void __init initmem_init(void) sparse_memory_present_with_active_regions(0); #ifdef CONFIG_FLATMEM - max_mapnr = num_physpages; + max_mapnr = IS_ENABLED(CONFIG_HIGHMEM) ? highend_pfn : max_low_pfn; #endif __vmalloc_start_set = true; @@ -739,9 +737,6 @@ static void __init test_wp_bit(void) void __init mem_init(void) { - int codesize, reservedpages, datasize, initsize; - int tmp; - pci_iommu_alloc(); #ifdef CONFIG_FLATMEM @@ -761,30 +756,9 @@ void __init mem_init(void) /* this will put all low memory onto the freelists */ free_all_bootmem(); - reservedpages = 0; - for (tmp = 0; tmp < max_low_pfn; tmp++) - /* - * Only count reserved RAM pages: - */ - if (page_is_ram(tmp) && PageReserved(pfn_to_page(tmp))) - reservedpages++; - after_bootmem = 1; - codesize = (unsigned long) &_etext - (unsigned long) &_text; - datasize = (unsigned long) &_edata - (unsigned long) &_etext; - initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; - - printk(KERN_INFO "Memory: %luk/%luk available (%dk kernel code, " - "%dk reserved, %dk data, %dk init, %ldk highmem)\n", - nr_free_pages() << (PAGE_SHIFT-10), - num_physpages << (PAGE_SHIFT-10), - codesize >> 10, - reservedpages << (PAGE_SHIFT-10), - datasize >> 10, - initsize >> 10, - totalhigh_pages << (PAGE_SHIFT-10)); - + mem_init_print_info(NULL); printk(KERN_INFO "virtual kernel memory layout:\n" " fixmap : 0x%08lx - 0x%08lx (%4ld kB)\n" #ifdef CONFIG_HIGHMEM diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index 9577638f3ead..104d56a9245f 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -1044,9 +1044,6 @@ static void __init register_page_bootmem_info(void) void __init mem_init(void) { - long codesize, reservedpages, datasize, initsize; - unsigned long absent_pages; - pci_iommu_alloc(); /* clear_bss() already clear the empty_zero_page */ @@ -1055,28 +1052,13 @@ void __init mem_init(void) /* this will put all memory onto the freelists */ free_all_bootmem(); - - absent_pages = absent_pages_in_range(0, max_pfn); - reservedpages = max_pfn - totalram_pages - absent_pages; after_bootmem = 1; - codesize = (unsigned long) &_etext - (unsigned long) &_text; - datasize = (unsigned long) &_edata - (unsigned long) &_etext; - initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; - /* Register memory areas for /proc/kcore */ kclist_add(&kcore_vsyscall, (void *)VSYSCALL_START, VSYSCALL_END - VSYSCALL_START, KCORE_OTHER); - printk(KERN_INFO "Memory: %luk/%luk available (%ldk kernel code, " - "%ldk absent, %ldk reserved, %ldk data, %ldk init)\n", - nr_free_pages() << (PAGE_SHIFT-10), - max_pfn << (PAGE_SHIFT-10), - codesize >> 10, - absent_pages << (PAGE_SHIFT-10), - reservedpages << (PAGE_SHIFT-10), - datasize >> 10, - initsize >> 10); + mem_init_print_info(NULL); } #ifdef CONFIG_DEBUG_RODATA diff --git a/arch/x86/mm/numa_32.c b/arch/x86/mm/numa_32.c index 73a6d7395bd3..0342d27ca798 100644 --- a/arch/x86/mm/numa_32.c +++ b/arch/x86/mm/numa_32.c @@ -83,10 +83,8 @@ void __init initmem_init(void) highstart_pfn = max_low_pfn; printk(KERN_NOTICE "%ldMB HIGHMEM available.\n", pages_to_mb(highend_pfn - highstart_pfn)); - num_physpages = highend_pfn; high_memory = (void *) __va(highstart_pfn * PAGE_SIZE - 1) + 1; #else - num_physpages = max_low_pfn; high_memory = (void *) __va(max_low_pfn * PAGE_SIZE - 1) + 1; #endif printk(KERN_NOTICE "%ldMB LOWMEM available.\n", -- cgit v1.2.3 From 808c2c3745975714ecd4da4d68c915de9048b12f Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:04:20 -0700 Subject: mm/xtensa: prepare for removing num_physpages and simplify mem_init() Prepare for removing num_physpages and simplify mem_init(). Signed-off-by: Jiang Liu Cc: Chris Zankel Cc: Max Filippov Cc: Geert Uytterhoeven Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/xtensa/mm/init.c | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/arch/xtensa/mm/init.c b/arch/xtensa/mm/init.c index 663c1619562c..479d7537a32a 100644 --- a/arch/xtensa/mm/init.c +++ b/arch/xtensa/mm/init.c @@ -173,12 +173,8 @@ void __init zones_init(void) void __init mem_init(void) { - unsigned long codesize, reservedpages, datasize, initsize; - unsigned long highmemsize, tmp, ram; - - max_mapnr = num_physpages = max_low_pfn - ARCH_PFN_OFFSET; + max_mapnr = max_low_pfn - ARCH_PFN_OFFSET; high_memory = (void *) __va(max_low_pfn << PAGE_SHIFT); - highmemsize = 0; #ifdef CONFIG_HIGHMEM #error HIGHGMEM not implemented in init.c @@ -186,26 +182,7 @@ void __init mem_init(void) free_all_bootmem(); - reservedpages = ram = 0; - for (tmp = 0; tmp < max_mapnr; tmp++) { - ram++; - if (PageReserved(mem_map+tmp)) - reservedpages++; - } - - codesize = (unsigned long) _etext - (unsigned long) _stext; - datasize = (unsigned long) _edata - (unsigned long) _sdata; - initsize = (unsigned long) __init_end - (unsigned long) __init_begin; - - printk("Memory: %luk/%luk available (%ldk kernel code, %ldk reserved, " - "%ldk data, %ldk init %ldk highmem)\n", - nr_free_pages() << (PAGE_SHIFT-10), - ram << (PAGE_SHIFT-10), - codesize >> 10, - reservedpages << (PAGE_SHIFT-10), - datasize >> 10, - initsize >> 10, - highmemsize >> 10); + mem_init_print_info(NULL); } #ifdef CONFIG_BLK_DEV_INITRD -- cgit v1.2.3 From 1895418189e08c1d1eec4fbdb5fb41d793f57ba5 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:04:21 -0700 Subject: mm: kill global variable num_physpages Now all references to num_physpages have been removed, so kill it. Signed-off-by: Jiang Liu Cc: Mel Gorman Cc: Michel Lespinasse Cc: Rik van Riel Cc: Jiang Liu Cc: Hugh Dickins Cc: David Rientjes Cc: Al Viro Cc: Konstantin Khlebnikov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mm.h | 1 - mm/memory.c | 2 -- mm/nommu.c | 2 -- 3 files changed, 5 deletions(-) diff --git a/include/linux/mm.h b/include/linux/mm.h index 09c235301dbb..e550a1773a9e 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -29,7 +29,6 @@ struct writeback_control; extern unsigned long max_mapnr; #endif -extern unsigned long num_physpages; extern unsigned long totalram_pages; extern void * high_memory; extern int page_cluster; diff --git a/mm/memory.c b/mm/memory.c index 407533219673..b68812d682b6 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -82,7 +82,6 @@ EXPORT_SYMBOL(max_mapnr); EXPORT_SYMBOL(mem_map); #endif -unsigned long num_physpages; /* * A number of key systems in x86 including ioremap() rely on the assumption * that high_memory defines the upper bound on direct map memory, then end @@ -92,7 +91,6 @@ unsigned long num_physpages; */ void * high_memory; -EXPORT_SYMBOL(num_physpages); EXPORT_SYMBOL(high_memory); /* diff --git a/mm/nommu.c b/mm/nommu.c index 1898b2fe9da5..e44e6e0a125c 100644 --- a/mm/nommu.c +++ b/mm/nommu.c @@ -56,7 +56,6 @@ void *high_memory; struct page *mem_map; unsigned long max_mapnr; -unsigned long num_physpages; unsigned long highest_memmap_pfn; struct percpu_counter vm_committed_as; int sysctl_overcommit_memory = OVERCOMMIT_GUESS; /* heuristic overcommit */ @@ -85,7 +84,6 @@ unsigned long vm_memory_committed(void) EXPORT_SYMBOL_GPL(vm_memory_committed); EXPORT_SYMBOL(mem_map); -EXPORT_SYMBOL(num_physpages); /* list of mapped, potentially shareable regions */ static struct kmem_cache *vm_region_jar; -- cgit v1.2.3 From fccc998771043db1613509b26c18b3d7f071e668 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:04:23 -0700 Subject: mm: introduce helper function set_max_mapnr() Introduce a helper function set_max_mapnr() to set global variable max_mapnr. Also unify condition compilation for max_mapnr with CONFIG_NEED_MULTIPLE_NODES instead of CONFIG_DISCONTIGMEM. Signed-off-by: Jiang Liu Cc: Greg Kroah-Hartman Cc: Mauro Carvalho Chehab Cc: "David S. Miller" Cc: Mark Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mm.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/include/linux/mm.h b/include/linux/mm.h index e550a1773a9e..b87681adf0ba 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -25,8 +25,15 @@ struct file_ra_state; struct user_struct; struct writeback_control; -#ifndef CONFIG_DISCONTIGMEM /* Don't use mapnrs, do it properly */ +#ifndef CONFIG_NEED_MULTIPLE_NODES /* Don't use mapnrs, do it properly */ extern unsigned long max_mapnr; + +static inline void set_max_mapnr(unsigned long limit) +{ + max_mapnr = limit; +} +#else +static inline void set_max_mapnr(unsigned long limit) { } #endif extern unsigned long totalram_pages; -- cgit v1.2.3 From ce7549e1d824e08871beddb6c7945635dfc4341d Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:04:24 -0700 Subject: mm/AVR32: prepare for killing free_all_bootmem_node() Prepare for killing free_all_bootmem_node() by using free_all_bootmem() instead. Signed-off-by: Jiang Liu Cc: Haavard Skinnemoen Cc: Hans-Christian Egtvedt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/avr32/mm/init.c | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/arch/avr32/mm/init.c b/arch/avr32/mm/init.c index 0fc04b9323cf..def5391d927a 100644 --- a/arch/avr32/mm/init.c +++ b/arch/avr32/mm/init.c @@ -103,23 +103,12 @@ void __init mem_init(void) pg_data_t *pgdat; high_memory = NULL; + for_each_online_pgdat(pgdat) + high_memory = max_t(void *, high_memory, + __va(pgdat_end_pfn(pgdat) << PAGE_SHIFT)); - /* this will put all low memory onto the freelists */ - for_each_online_pgdat(pgdat) { - void *node_high_memory; - - if (pgdat->node_spanned_pages != 0) - free_all_bootmem_node(pgdat); - - node_high_memory = (void *)((pgdat->node_start_pfn - + pgdat->node_spanned_pages) - << PAGE_SHIFT); - if (node_high_memory > high_memory) - high_memory = node_high_memory; - } - - max_mapnr = MAP_NR(high_memory); - + set_max_mapnr(MAP_NR(high_memory)); + free_all_bootmem(); mem_init_print_info(NULL); } -- cgit v1.2.3 From b57b63a2ace84d09c6fc270150d0408ddef1efa7 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:04:25 -0700 Subject: mm/IA64: prepare for killing free_all_bootmem_node() Prepare for killing free_all_bootmem_node() by using free_all_bootmem(). Signed-off-by: Jiang Liu Cc: Tony Luck Cc: Fenghua Yu Cc: Tang Chen Cc: David Rientjes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/ia64/mm/init.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/arch/ia64/mm/init.c b/arch/ia64/mm/init.c index 2d372b4c5f70..b6f7f43424ec 100644 --- a/arch/ia64/mm/init.c +++ b/arch/ia64/mm/init.c @@ -583,7 +583,6 @@ __setup("nolwsys", nolwsys_setup); void __init mem_init (void) { - pg_data_t *pgdat; int i; BUG_ON(PTRS_PER_PGD * sizeof(pgd_t) != PAGE_SIZE); @@ -601,15 +600,11 @@ mem_init (void) #ifdef CONFIG_FLATMEM BUG_ON(!mem_map); - max_mapnr = max_low_pfn; #endif + set_max_mapnr(max_low_pfn); high_memory = __va(max_low_pfn * PAGE_SIZE); - - for_each_online_pgdat(pgdat) - if (pgdat->bdata->node_bootmem_map) - free_all_bootmem_node(pgdat); - + free_all_bootmem(); mem_init_print_info(NULL); /* -- cgit v1.2.3 From c5c009fbe753393dabc5d67b617c8188f81740a2 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:04:26 -0700 Subject: mm/m32r: prepare for killing free_all_bootmem_node() Prepare for killing free_all_bootmem_node() by using free_all_bootmem(). Signed-off-by: Jiang Liu Cc: Hirokazu Takata Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/m32r/mm/init.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/arch/m32r/mm/init.c b/arch/m32r/mm/init.c index a4f8d934e25f..0d4146f644dc 100644 --- a/arch/m32r/mm/init.c +++ b/arch/m32r/mm/init.c @@ -111,28 +111,19 @@ void __init paging_init(void) *======================================================================*/ void __init mem_init(void) { - int nid; #ifndef CONFIG_MMU extern unsigned long memory_end; -#endif -#ifndef CONFIG_DISCONTIGMEM - max_mapnr = get_num_physpages(); -#endif /* CONFIG_DISCONTIGMEM */ - -#ifdef CONFIG_MMU - high_memory = (void *)__va(PFN_PHYS(MAX_LOW_PFN(0))); -#else high_memory = (void *)(memory_end & PAGE_MASK); +#else + high_memory = (void *)__va(PFN_PHYS(MAX_LOW_PFN(0))); #endif /* CONFIG_MMU */ /* clear the zero-page */ memset(empty_zero_page, 0, PAGE_SIZE); - /* this will put all low memory onto the freelists */ - for_each_online_node(nid) - free_all_bootmem_node(NODE_DATA(nid)); - + set_max_mapnr(get_num_physpages()); + free_all_bootmem(); mem_init_print_info(NULL); } -- cgit v1.2.3 From b69a9787b1dfb2e5055fb4e0fea71f9e16c7ea01 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:04:27 -0700 Subject: mm/m68k: prepare for killing free_all_bootmem_node() Prepare for killing free_all_bootmem_node() by using free_all_bootmem(). Signed-off-by: Jiang Liu Cc: Geert Uytterhoeven Cc: Greg Ungerer Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/m68k/mm/init.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/arch/m68k/mm/init.c b/arch/m68k/mm/init.c index 397a8848f067..6e0a93869ec9 100644 --- a/arch/m68k/mm/init.c +++ b/arch/m68k/mm/init.c @@ -148,12 +148,10 @@ void __init print_memmap(void) void __init mem_init(void) { - pg_data_t *pgdat; int i; /* this will put all memory onto the freelists */ - for_each_online_pgdat(pgdat) - free_all_bootmem_node(pgdat); + free_all_bootmem(); #if defined(CONFIG_MMU) && !defined(CONFIG_SUN3) && !defined(CONFIG_COLDFIRE) /* insert pointer tables allocated so far into the tablelist */ -- cgit v1.2.3 From 5ad62f24ba22543a3b35b4f28fafd80e55eda269 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:04:28 -0700 Subject: mm/metag: prepare for killing free_all_bootmem_node() Prepare for killing free_all_bootmem_node() by using free_all_bootmem(). Signed-off-by: Jiang Liu Cc: James Hogan Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/metag/mm/init.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/arch/metag/mm/init.c b/arch/metag/mm/init.c index e0862b7ecd6e..28813f164730 100644 --- a/arch/metag/mm/init.c +++ b/arch/metag/mm/init.c @@ -376,31 +376,21 @@ void __init paging_init(unsigned long mem_end) void __init mem_init(void) { - int nid; - #ifdef CONFIG_HIGHMEM unsigned long tmp; /* * Explicitly reset zone->managed_pages because highmem pages are - * freed before calling free_all_bootmem_node(); + * freed before calling free_all_bootmem(); */ reset_all_zones_managed_pages(); for (tmp = highstart_pfn; tmp < highend_pfn; tmp++) free_highmem_page(pfn_to_page(tmp)); #endif /* CONFIG_HIGHMEM */ - for_each_online_node(nid) { - pg_data_t *pgdat = NODE_DATA(nid); - - if (pgdat->node_spanned_pages) - free_all_bootmem_node(pgdat); - } - + free_all_bootmem(); mem_init_print_info(NULL); show_mem(0); - - return; } void free_initmem(void) -- cgit v1.2.3 From 629e7b4c8051f88daa7d2d53283128a4b2a6fa72 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:04:30 -0700 Subject: mm/MIPS: prepare for killing free_all_bootmem_node() Prepare for killing free_all_bootmem_node() by using free_all_bootmem(). Signed-off-by: Jiang Liu Cc: Ralf Baechle Cc: Minchan Kim Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/mips/sgi-ip27/ip27-memory.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/arch/mips/sgi-ip27/ip27-memory.c b/arch/mips/sgi-ip27/ip27-memory.c index a0c9e34147c2..a95c00f5fb96 100644 --- a/arch/mips/sgi-ip27/ip27-memory.c +++ b/arch/mips/sgi-ip27/ip27-memory.c @@ -477,18 +477,8 @@ void __init paging_init(void) void __init mem_init(void) { - unsigned node; - high_memory = (void *) __va(get_num_physpages() << PAGE_SHIFT); - - for_each_online_node(node) { - /* - * This will free up the bootmem, ie, slot 0 memory. - */ - free_all_bootmem_node(NODE_DATA(node)); - } - + free_all_bootmem(); setup_zero_pages(); /* This comes from node 0 */ - mem_init_print_info(NULL); } -- cgit v1.2.3 From d5c017dde4ea0932f861d3ddf2b2ade92ee3d033 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:04:31 -0700 Subject: mm/PARISC: prepare for killing free_all_bootmem_node() Prepare for killing free_all_bootmem_node() by using free_all_bootmem(). Signed-off-by: Jiang Liu Cc: "James E.J. Bottomley" Cc: Helge Deller Cc: Michal Hocko Cc: David Rientjes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/parisc/mm/init.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/arch/parisc/mm/init.c b/arch/parisc/mm/init.c index b4edc76c6f22..b0f96c0e6316 100644 --- a/arch/parisc/mm/init.c +++ b/arch/parisc/mm/init.c @@ -585,18 +585,8 @@ void __init mem_init(void) > BITS_PER_LONG); high_memory = __va((max_pfn << PAGE_SHIFT)); - -#ifndef CONFIG_DISCONTIGMEM - max_mapnr = page_to_pfn(virt_to_page(high_memory - 1)) + 1; + set_max_mapnr(page_to_pfn(virt_to_page(high_memory - 1)) + 1); free_all_bootmem(); -#else - { - int i; - - for (i = 0; i < npmem_ranges; i++) - free_all_bootmem_node(NODE_DATA(i)); - } -#endif #ifdef CONFIG_PA11 if (hppa_dma_ops == &pcxl_dma_ops) { -- cgit v1.2.3 From 602ddc70ecf710e0cf512862473eac52524ec081 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:04:32 -0700 Subject: mm/PPC: prepare for killing free_all_bootmem_node() Prepare for killing free_all_bootmem_node() by using free_all_bootmem(). Signed-off-by: Jiang Liu Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Alexander Graf Cc: "Suzuki K. Poulose" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/powerpc/mm/mem.c | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c index 49c18b60bbb4..1cb1ea133a2c 100644 --- a/arch/powerpc/mm/mem.c +++ b/arch/powerpc/mm/mem.c @@ -304,22 +304,8 @@ void __init mem_init(void) #endif high_memory = (void *) __va(max_low_pfn * PAGE_SIZE); - -#ifdef CONFIG_NEED_MULTIPLE_NODES - { - pg_data_t *pgdat; - - for_each_online_pgdat(pgdat) - if (pgdat->node_spanned_pages != 0) { - printk("freeing bootmem node %d\n", - pgdat->node_id); - free_all_bootmem_node(pgdat); - } - } -#else - max_mapnr = max_pfn; + set_max_mapnr(max_pfn); free_all_bootmem(); -#endif #ifdef CONFIG_HIGHMEM { -- cgit v1.2.3 From e3a466b29fc3da335a5a5b695ca6da980d157070 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:04:33 -0700 Subject: mm/SH: prepare for killing free_all_bootmem_node() Prepare for killing free_all_bootmem_node() by using free_all_bootmem(). Signed-off-by: Jiang Liu Cc: Paul Mundt Cc: Wen Congyang Cc: Tang Chen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/sh/mm/init.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/arch/sh/mm/init.c b/arch/sh/mm/init.c index c9a517c8bbb6..33890fd267cb 100644 --- a/arch/sh/mm/init.c +++ b/arch/sh/mm/init.c @@ -412,19 +412,11 @@ void __init mem_init(void) iommu_init(); high_memory = NULL; + for_each_online_pgdat(pgdat) + high_memory = max_t(void *, high_memory, + __va(pgdat_end_pfn(pgdat) << PAGE_SHIFT)); - for_each_online_pgdat(pgdat) { - void *node_high_memory; - - if (pgdat->node_spanned_pages) - free_all_bootmem_node(pgdat); - - node_high_memory = (void *)__va((pgdat->node_start_pfn + - pgdat->node_spanned_pages) << - PAGE_SHIFT); - if (node_high_memory > high_memory) - high_memory = node_high_memory; - } + free_all_bootmem(); /* Set this up early, so we can take care of the zero page */ cpu_cache_init(); -- cgit v1.2.3 From e1280be0d8614be94e5bef48b6c830dfa03e82a7 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:04:34 -0700 Subject: mm: kill free_all_bootmem_node() Now nobody makes use of free_all_bootmem_node(), kill it. Signed-off-by: Jiang Liu Cc: Johannes Weiner Cc: "David S. Miller" Cc: Yinghai Lu Acked-by: Tejun Heo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/bootmem.h | 1 - mm/bootmem.c | 18 ------------------ 2 files changed, 19 deletions(-) diff --git a/include/linux/bootmem.h b/include/linux/bootmem.h index 0e48c3221d82..f1f07d31a3af 100644 --- a/include/linux/bootmem.h +++ b/include/linux/bootmem.h @@ -44,7 +44,6 @@ extern unsigned long init_bootmem_node(pg_data_t *pgdat, unsigned long endpfn); extern unsigned long init_bootmem(unsigned long addr, unsigned long memend); -extern unsigned long free_all_bootmem_node(pg_data_t *pgdat); extern unsigned long free_all_bootmem(void); extern void reset_all_zones_managed_pages(void); diff --git a/mm/bootmem.c b/mm/bootmem.c index 58609bbf584e..6ab7744e692e 100644 --- a/mm/bootmem.c +++ b/mm/bootmem.c @@ -263,24 +263,6 @@ void __init reset_all_zones_managed_pages(void) reset_managed_pages_done = 1; } -/** - * free_all_bootmem_node - release a node's free pages to the buddy allocator - * @pgdat: node to be released - * - * Returns the number of pages actually released. - */ -unsigned long __init free_all_bootmem_node(pg_data_t *pgdat) -{ - unsigned long pages; - - register_page_bootmem_info_node(pgdat); - reset_node_managed_pages(pgdat); - pages = free_all_bootmem_core(pgdat->bdata); - totalram_pages += pages; - - return pages; -} - /** * free_all_bootmem - release free pages to the buddy allocator * -- cgit v1.2.3 From 2fb1cd5a283a7c40457731415c6e6432f061edc2 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:04:36 -0700 Subject: mm/alpha: unify mem_init() for both UMA and NUMA architectures Now mem_init() for both Alpha UMA and Alpha NUMA are the same, so unify it to reduce duplicated code. Signed-off-by: Jiang Liu Cc: Richard Henderson Cc: Ivan Kokshaysky Cc: Matt Turner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/alpha/mm/init.c | 7 ++----- arch/alpha/mm/numa.c | 10 ---------- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/arch/alpha/mm/init.c b/arch/alpha/mm/init.c index af91010990b0..a1bea91df56a 100644 --- a/arch/alpha/mm/init.c +++ b/arch/alpha/mm/init.c @@ -276,17 +276,14 @@ srm_paging_stop (void) } #endif -#ifndef CONFIG_DISCONTIGMEM void __init mem_init(void) { - max_mapnr = max_low_pfn; - free_all_bootmem(); + set_max_mapnr(max_low_pfn); high_memory = (void *) __va(max_low_pfn * PAGE_SIZE); - + free_all_bootmem(); mem_init_print_info(NULL); } -#endif /* CONFIG_DISCONTIGMEM */ void free_initmem(void) diff --git a/arch/alpha/mm/numa.c b/arch/alpha/mm/numa.c index 0894b3a8b70f..d543d71c28b4 100644 --- a/arch/alpha/mm/numa.c +++ b/arch/alpha/mm/numa.c @@ -319,13 +319,3 @@ void __init paging_init(void) /* Initialize the kernel's ZERO_PGE. */ memset((void *)ZERO_PGE, 0, PAGE_SIZE); } - -void __init mem_init(void) -{ - high_memory = (void *) __va(max_low_pfn << PAGE_SHIFT); - free_all_bootmem(); - mem_init_print_info(NULL); -#if 0 - mem_stress(); -#endif -} -- cgit v1.2.3 From 3e548a9e8895318143a983681a8ef97312989880 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:04:37 -0700 Subject: mm/m68k: fix build warning of unused variable Fix build warning of unused variable: arch/m68k/mm/init.c: In function 'mem_init': arch/m68k/mm/init.c:151:6: warning: unused variable 'i' [-Wunused-variable] Signed-off-by: Jiang Liu Cc: Geert Uytterhoeven Cc: Greg Ungerer Cc: Thadeu Lima de Souza Cascardo Cc: Sergei Shtylyov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/m68k/mm/init.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/arch/m68k/mm/init.c b/arch/m68k/mm/init.c index 6e0a93869ec9..6b4baa6e4d31 100644 --- a/arch/m68k/mm/init.c +++ b/arch/m68k/mm/init.c @@ -146,14 +146,11 @@ void __init print_memmap(void) MLK_ROUNDUP(__bss_start, __bss_stop)); } -void __init mem_init(void) +static inline void init_pointer_tables(void) { +#if defined(CONFIG_MMU) && !defined(CONFIG_SUN3) && !defined(CONFIG_COLDFIRE) int i; - /* this will put all memory onto the freelists */ - free_all_bootmem(); - -#if defined(CONFIG_MMU) && !defined(CONFIG_SUN3) && !defined(CONFIG_COLDFIRE) /* insert pointer tables allocated so far into the tablelist */ init_pointer_table((unsigned long)kernel_pg_dir); for (i = 0; i < PTRS_PER_PGD; i++) { @@ -165,7 +162,13 @@ void __init mem_init(void) if (zero_pgtable) init_pointer_table((unsigned long)zero_pgtable); #endif +} +void __init mem_init(void) +{ + /* this will put all memory onto the freelists */ + free_all_bootmem(); + init_pointer_tables(); mem_init_print_info(NULL); print_memmap(); } -- cgit v1.2.3 From 922c1df068e69a5acf482c54e37d41e633a7d54a Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:04:38 -0700 Subject: mm/ALPHA: clean up unused VALID_PAGE() VALID_PAGE() has been removed from kernel long time ago, so clean up it. Signed-off-by: Jiang Liu Cc: Richard Henderson Cc: Ivan Kokshaysky Cc: Matt Turner Cc: Nadia Yvette Chambers Cc: Jiri Kosina Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/alpha/include/asm/mmzone.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/alpha/include/asm/mmzone.h b/arch/alpha/include/asm/mmzone.h index c5b5d6bac9ed..14ce27bccd24 100644 --- a/arch/alpha/include/asm/mmzone.h +++ b/arch/alpha/include/asm/mmzone.h @@ -71,8 +71,6 @@ PLAT_NODE_DATA_LOCALNR(unsigned long p, int n) #define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT) -#define VALID_PAGE(page) (((page) - mem_map) < max_mapnr) - #define pmd_page(pmd) (pfn_to_page(pmd_val(pmd) >> 32)) #define pgd_page(pgd) (pfn_to_page(pgd_val(pgd) >> 32)) #define pte_pfn(pte) (pte_val(pte) >> 32) -- cgit v1.2.3 From 5b84de34642bad442942ace0a57c4dee00266657 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:04:40 -0700 Subject: mm/ARM: fix stale comment about VALID_PAGE() VALID_PAGE() has been removed from kernel long time ago, so fix the comment. Signed-off-by: Jiang Liu Cc: Russell King Cc: Will Deacon Cc: Nicolas Pitre Cc: Stephen Boyd Cc: Giancarlo Asnaghi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/arm/include/asm/memory.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h index 584786f740f9..e750a938fd3c 100644 --- a/arch/arm/include/asm/memory.h +++ b/arch/arm/include/asm/memory.h @@ -276,12 +276,6 @@ static inline __deprecated void *bus_to_virt(unsigned long x) /* * Conversion between a struct page and a physical address. * - * Note: when converting an unknown physical address to a - * struct page, the resulting pointer must be validated - * using VALID_PAGE(). It must return an invalid struct page - * for any physical address not corresponding to a system - * RAM address. - * * page_to_pfn(page) convert a struct page * to a PFN number * pfn_to_page(pfn) convert a _valid_ PFN number to struct page * * -- cgit v1.2.3 From 35fa075f3ea432878ef50c0206df82f003a72222 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:04:41 -0700 Subject: mm/CRIS: clean up unused VALID_PAGE() VALID_PAGE() has been removed from kernel long time ago, so clean up it. Signed-off-by: Jiang Liu Acked-by: Jesper Nilsson Cc: Mikael Starvik Cc: Jiang Liu Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/cris/include/asm/page.h | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/cris/include/asm/page.h b/arch/cris/include/asm/page.h index be45ee366be9..dfc53f9b88ec 100644 --- a/arch/cris/include/asm/page.h +++ b/arch/cris/include/asm/page.h @@ -51,7 +51,6 @@ typedef struct page *pgtable_t; */ #define virt_to_page(kaddr) (mem_map + (((unsigned long)(kaddr) - PAGE_OFFSET) >> PAGE_SHIFT)) -#define VALID_PAGE(page) (((page) - mem_map) < max_mapnr) #define virt_addr_valid(kaddr) pfn_valid((unsigned)(kaddr) >> PAGE_SHIFT) /* convert a page (based on mem_map and forward) to a physical address -- cgit v1.2.3 From 924b387a72a02216b4899717c5d249c3ce5d11bd Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:04:42 -0700 Subject: mm/microblaze: clean up unused VALID_PAGE() VALID_PAGE() has been removed from kernel long time ago, so clean up it. Signed-off-by: Jiang Liu Cc: Michal Simek Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/microblaze/include/asm/page.h | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/microblaze/include/asm/page.h b/arch/microblaze/include/asm/page.h index 85a5ae8e9bd0..fd850879854d 100644 --- a/arch/microblaze/include/asm/page.h +++ b/arch/microblaze/include/asm/page.h @@ -168,7 +168,6 @@ extern int page_is_ram(unsigned long pfn); # else /* CONFIG_MMU */ # define ARCH_PFN_OFFSET (memory_start >> PAGE_SHIFT) # define pfn_valid(pfn) ((pfn) < (max_mapnr + ARCH_PFN_OFFSET)) -# define VALID_PAGE(page) ((page - mem_map) < max_mapnr) # endif /* CONFIG_MMU */ # endif /* __ASSEMBLY__ */ -- cgit v1.2.3 From b26a3dfd4c0b888303a5909ef37febeb582e190e Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Wed, 3 Jul 2013 15:04:43 -0700 Subject: mm/unicore32: fix stale comment about VALID_PAGE() VALID_PAGE() has been removed from kernel long time ago, so fix the comment. Signed-off-by: Jiang Liu Acked-by: Guan Xuetao Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/unicore32/include/asm/memory.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/arch/unicore32/include/asm/memory.h b/arch/unicore32/include/asm/memory.h index 5eddb997defe..debafc40200a 100644 --- a/arch/unicore32/include/asm/memory.h +++ b/arch/unicore32/include/asm/memory.h @@ -98,12 +98,6 @@ /* * Conversion between a struct page and a physical address. * - * Note: when converting an unknown physical address to a - * struct page, the resulting pointer must be validated - * using VALID_PAGE(). It must return an invalid struct page - * for any physical address not corresponding to a system - * RAM address. - * * page_to_pfn(page) convert a struct page * to a PFN number * pfn_to_page(pfn) convert a _valid_ PFN number to struct page * * -- cgit v1.2.3 From 55878e88c59221c3187e1c24ec3b15eb79c374c0 Mon Sep 17 00:00:00 2001 From: Cody P Schafer Date: Wed, 3 Jul 2013 15:04:44 -0700 Subject: sparsemem: add BUILD_BUG_ON when sizeof mem_section is non-power-of-2 Instead of leaving a hidden trap for the next person who comes along and wants to add something to mem_section, add a big fat warning about it needing to be a power-of-2, and insert a BUILD_BUG_ON() in sparse_init() to catch mistakes. Right now non-power-of-2 mem_sections cause a number of WARNs at boot (which don't clearly point to the size of mem_section as an issue), but the system limps on (temporarily, at least). This is based upon Dave Hansen's earlier RFC where he ran into the same issue: "sparsemem: fix boot when SECTIONS_PER_ROOT is not power-of-2" http://lkml.indiana.edu/hypermail/linux/kernel/1205.2/03077.html Signed-off-by: Cody P Schafer Acked-by: Dave Hansen Cc: Jiang Liu Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/mmzone.h | 4 ++++ mm/sparse.c | 3 +++ 2 files changed, 7 insertions(+) diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 09d381b71fd8..ae19af5ec02c 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -1137,6 +1137,10 @@ struct mem_section { struct page_cgroup *page_cgroup; unsigned long pad; #endif + /* + * WARNING: mem_section must be a power-of-2 in size for the + * calculation and use of SECTION_ROOT_MASK to make sense. + */ }; #ifdef CONFIG_SPARSEMEM_EXTREME diff --git a/mm/sparse.c b/mm/sparse.c index 1c91f0d3f6ab..3194ec414728 100644 --- a/mm/sparse.c +++ b/mm/sparse.c @@ -481,6 +481,9 @@ void __init sparse_init(void) struct page **map_map; #endif + /* see include/linux/mmzone.h 'struct mem_section' definition */ + BUILD_BUG_ON(!is_power_of_2(sizeof(struct mem_section))); + /* Setup pageblock_order for HUGETLB_PAGE_SIZE_VARIABLE */ set_pageblock_order(); -- cgit v1.2.3 From 26c0c5bf38159673f0ae28c38fc9f90dbeb4d4aa Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Wed, 3 Jul 2013 15:04:45 -0700 Subject: documentation: update address_space_operations The documentation for address_space_operations is partially out of date. Signed-off-by: Mel Gorman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/filesystems/vfs.txt | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index 1f0ba30ae47e..fc5d2a1d26c0 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt @@ -559,7 +559,6 @@ your filesystem. The following members are defined: struct address_space_operations { int (*writepage)(struct page *page, struct writeback_control *wbc); int (*readpage)(struct file *, struct page *); - int (*sync_page)(struct page *); int (*writepages)(struct address_space *, struct writeback_control *); int (*set_page_dirty)(struct page *page); int (*readpages)(struct file *filp, struct address_space *mapping, @@ -581,6 +580,8 @@ struct address_space_operations { /* migrate the contents of a page to the specified target */ int (*migratepage) (struct page *, struct page *); int (*launder_page) (struct page *); + int (*is_partially_uptodate) (struct page *, read_descriptor_t *, + unsigned long); int (*error_remove_page) (struct mapping *mapping, struct page *page); int (*swap_activate)(struct file *); int (*swap_deactivate)(struct file *); @@ -612,13 +613,6 @@ struct address_space_operations { In this case, the page will be relocated, relocked and if that all succeeds, ->readpage will be called again. - sync_page: called by the VM to notify the backing store to perform all - queued I/O operations for a page. I/O operations for other pages - associated with this address_space object may also be performed. - - This function is optional and is called only for pages with - PG_Writeback set while waiting for the writeback to complete. - writepages: called by the VM to write out pages associated with the address_space object. If wbc->sync_mode is WBC_SYNC_ALL, then the writeback_control will specify a range of pages that must be @@ -747,6 +741,11 @@ struct address_space_operations { prevent redirtying the page, it is kept locked during the whole operation. + is_partially_uptodate: Called by the VM when reading a file through the + pagecache when the underlying blocksize != pagesize. If the required + block is up to date then the read can complete without needing the IO + to bring the whole page up to date. + error_remove_page: normally set to generic_error_remove_page if truncation is ok for this address space. Used for memory failure handling. Setting this implies you deal with pages going away under you, -- cgit v1.2.3 From 543cc115339baa44fbea877b3d8673aca652622f Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Wed, 3 Jul 2013 15:04:46 -0700 Subject: documentation: document the is_dirty_writeback aops callback Signed-off-by: Mel Gorman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/filesystems/vfs.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index fc5d2a1d26c0..f93a88250a44 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt @@ -582,6 +582,7 @@ struct address_space_operations { int (*launder_page) (struct page *); int (*is_partially_uptodate) (struct page *, read_descriptor_t *, unsigned long); + void (*is_dirty_writeback) (struct page *, bool *, bool *); int (*error_remove_page) (struct mapping *mapping, struct page *page); int (*swap_activate)(struct file *); int (*swap_deactivate)(struct file *); @@ -746,6 +747,15 @@ struct address_space_operations { block is up to date then the read can complete without needing the IO to bring the whole page up to date. + is_dirty_writeback: Called by the VM when attempting to reclaim a page. + The VM uses dirty and writeback information to determine if it needs + to stall to allow flushers a chance to complete some IO. Ordinarily + it can use PageDirty and PageWriteback but some filesystems have + more complex state (unstable pages in NFS prevent reclaim) or + do not set those flags due to locking problems (jbd). This callback + allows a filesystem to indicate to the VM if a page should be + treated as dirty or writeback for the purposes of stalling. + error_remove_page: normally set to generic_error_remove_page if truncation is ok for this address space. Used for memory failure handling. Setting this implies you deal with pages going away under you, -- cgit v1.2.3 From d82b1d85760a8344d06272da67f0684243235fac Mon Sep 17 00:00:00 2001 From: Zhang Yanfei Date: Wed, 3 Jul 2013 15:04:47 -0700 Subject: mm, vmalloc: only call setup_vmalloc_vm() only in __get_vm_area_node() Now for insert_vmalloc_vm, it only calls the two functions: - setup_vmalloc_vm: fill vm_struct and vmap_area instances - clear_vm_unlist: clear VM_UNLIST bit in vm_struct->flags So in __get_vm_area_node(), if VM_UNLIST bit unset in flags, that is the else branch here, we don't need to clear VM_UNLIST bit for vm->flags since this bit is obviously not set. That is to say, we could only call setup_vmalloc_vm instead of insert_vmalloc_vm here. And then we could even remove the if test here. Signed-off-by: Zhang Yanfei Acked-by: Joonsoo Kim Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/vmalloc.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/mm/vmalloc.c b/mm/vmalloc.c index b7259906a806..d23e70ec45ad 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -1367,16 +1367,7 @@ static struct vm_struct *__get_vm_area_node(unsigned long size, return NULL; } - /* - * When this function is called from __vmalloc_node_range, - * we add VM_UNLIST flag to avoid accessing uninitialized - * members of vm_struct such as pages and nr_pages fields. - * They will be set later. - */ - if (flags & VM_UNLIST) - setup_vmalloc_vm(area, va, flags, caller); - else - insert_vmalloc_vm(area, va, flags, caller); + setup_vmalloc_vm(area, va, flags, caller); return area; } -- cgit v1.2.3 From 3645cb4a4eb2002dad17b314559badf8a20e55a7 Mon Sep 17 00:00:00 2001 From: Zhang Yanfei Date: Wed, 3 Jul 2013 15:04:48 -0700 Subject: mm, vmalloc: call setup_vmalloc_vm() instead of insert_vmalloc_vm() Here we pass flags with only VM_ALLOC bit set, it is unnecessary to call clear_vm_unlist to clear VM_UNLIST bit. So use setup_vmalloc_vm instead of insert_vmalloc_vm. Signed-off-by: Zhang Yanfei Acked-by: Joonsoo Kim Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/vmalloc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mm/vmalloc.c b/mm/vmalloc.c index d23e70ec45ad..db48d513598f 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -2526,8 +2526,8 @@ found: /* insert all vm's */ for (area = 0; area < nr_vms; area++) - insert_vmalloc_vm(vms[area], vas[area], VM_ALLOC, - pcpu_get_vm_areas); + setup_vmalloc_vm(vms[area], vas[area], VM_ALLOC, + pcpu_get_vm_areas); kfree(vas); return vms; -- cgit v1.2.3 From f6d480059bedaf4feb06466c770f5fcace9eca31 Mon Sep 17 00:00:00 2001 From: Zhang Yanfei Date: Wed, 3 Jul 2013 15:04:49 -0700 Subject: mm, vmalloc: remove insert_vmalloc_vm() Now this function is nowhere used, we can remove it directly. Signed-off-by: Zhang Yanfei Acked-by: Joonsoo Kim Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/vmalloc.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/mm/vmalloc.c b/mm/vmalloc.c index db48d513598f..bd60bffd9aef 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -1322,13 +1322,6 @@ static void clear_vm_unlist(struct vm_struct *vm) vm->flags &= ~VM_UNLIST; } -static void insert_vmalloc_vm(struct vm_struct *vm, struct vmap_area *va, - unsigned long flags, const void *caller) -{ - setup_vmalloc_vm(vm, va, flags, caller); - clear_vm_unlist(vm); -} - static struct vm_struct *__get_vm_area_node(unsigned long size, unsigned long align, unsigned long flags, unsigned long start, unsigned long end, int node, gfp_t gfp_mask, const void *caller) -- cgit v1.2.3 From 0f2d4a8e27108ad3b2555396b06392be590fe287 Mon Sep 17 00:00:00 2001 From: Zhang Yanfei Date: Wed, 3 Jul 2013 15:04:50 -0700 Subject: mm, vmalloc: use clamp() to simplify code Signed-off-by: Zhang Yanfei Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/vmalloc.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/mm/vmalloc.c b/mm/vmalloc.c index bd60bffd9aef..91a10472a39a 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -1330,16 +1330,8 @@ static struct vm_struct *__get_vm_area_node(unsigned long size, struct vm_struct *area; BUG_ON(in_interrupt()); - if (flags & VM_IOREMAP) { - int bit = fls(size); - - if (bit > IOREMAP_MAX_ORDER) - bit = IOREMAP_MAX_ORDER; - else if (bit < PAGE_SHIFT) - bit = PAGE_SHIFT; - - align = 1ul << bit; - } + if (flags & VM_IOREMAP) + align = 1ul << clamp(fls(size), PAGE_SHIFT, IOREMAP_MAX_ORDER); size = PAGE_ALIGN(size); if (unlikely(!size)) -- cgit v1.2.3 From 519ebea3bf6df45439e79c54bda1d9e29fe13a64 Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Wed, 3 Jul 2013 15:04:51 -0700 Subject: mm: memcontrol: factor out reclaim iterator loading and updating mem_cgroup_iter() is too hard to follow. Factor out the lockless reclaim iterator loading and updating so it's easier to follow the big picture. Also document the iterator invalidation mechanism a bit more extensively. Signed-off-by: Johannes Weiner Reported-by: Tejun Heo Reviewed-by: Tejun Heo Acked-by: Michal Hocko Cc: KAMEZAWA Hiroyuki Cc: Glauber Costa Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/memcontrol.c | 86 ++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 57 insertions(+), 29 deletions(-) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 4748966b1511..2e851f453814 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -1148,6 +1148,58 @@ skip_node: return NULL; } +static void mem_cgroup_iter_invalidate(struct mem_cgroup *root) +{ + /* + * When a group in the hierarchy below root is destroyed, the + * hierarchy iterator can no longer be trusted since it might + * have pointed to the destroyed group. Invalidate it. + */ + atomic_inc(&root->dead_count); +} + +static struct mem_cgroup * +mem_cgroup_iter_load(struct mem_cgroup_reclaim_iter *iter, + struct mem_cgroup *root, + int *sequence) +{ + struct mem_cgroup *position = NULL; + /* + * A cgroup destruction happens in two stages: offlining and + * release. They are separated by a RCU grace period. + * + * If the iterator is valid, we may still race with an + * offlining. The RCU lock ensures the object won't be + * released, tryget will fail if we lost the race. + */ + *sequence = atomic_read(&root->dead_count); + if (iter->last_dead_count == *sequence) { + smp_rmb(); + position = iter->last_visited; + if (position && !css_tryget(&position->css)) + position = NULL; + } + return position; +} + +static void mem_cgroup_iter_update(struct mem_cgroup_reclaim_iter *iter, + struct mem_cgroup *last_visited, + struct mem_cgroup *new_position, + int sequence) +{ + if (last_visited) + css_put(&last_visited->css); + /* + * We store the sequence count from the time @last_visited was + * loaded successfully instead of rereading it here so that we + * don't lose destruction events in between. We could have + * raced with the destruction of @new_position after all. + */ + iter->last_visited = new_position; + smp_wmb(); + iter->last_dead_count = sequence; +} + /** * mem_cgroup_iter - iterate over memory cgroup hierarchy * @root: hierarchy root @@ -1171,7 +1223,6 @@ struct mem_cgroup *mem_cgroup_iter(struct mem_cgroup *root, { struct mem_cgroup *memcg = NULL; struct mem_cgroup *last_visited = NULL; - unsigned long uninitialized_var(dead_count); if (mem_cgroup_disabled()) return NULL; @@ -1191,6 +1242,7 @@ struct mem_cgroup *mem_cgroup_iter(struct mem_cgroup *root, rcu_read_lock(); while (!memcg) { struct mem_cgroup_reclaim_iter *uninitialized_var(iter); + int uninitialized_var(seq); if (reclaim) { int nid = zone_to_nid(reclaim->zone); @@ -1204,37 +1256,13 @@ struct mem_cgroup *mem_cgroup_iter(struct mem_cgroup *root, goto out_unlock; } - /* - * If the dead_count mismatches, a destruction - * has happened or is happening concurrently. - * If the dead_count matches, a destruction - * might still happen concurrently, but since - * we checked under RCU, that destruction - * won't free the object until we release the - * RCU reader lock. Thus, the dead_count - * check verifies the pointer is still valid, - * css_tryget() verifies the cgroup pointed to - * is alive. - */ - dead_count = atomic_read(&root->dead_count); - if (dead_count == iter->last_dead_count) { - smp_rmb(); - last_visited = iter->last_visited; - if (last_visited && - !css_tryget(&last_visited->css)) - last_visited = NULL; - } + last_visited = mem_cgroup_iter_load(iter, root, &seq); } memcg = __mem_cgroup_iter_next(root, last_visited); if (reclaim) { - if (last_visited) - css_put(&last_visited->css); - - iter->last_visited = memcg; - smp_wmb(); - iter->last_dead_count = dead_count; + mem_cgroup_iter_update(iter, last_visited, memcg, seq); if (!memcg) iter->generation++; @@ -6318,14 +6346,14 @@ static void mem_cgroup_invalidate_reclaim_iterators(struct mem_cgroup *memcg) struct mem_cgroup *parent = memcg; while ((parent = parent_mem_cgroup(parent))) - atomic_inc(&parent->dead_count); + mem_cgroup_iter_invalidate(parent); /* * if the root memcg is not hierarchical we have to check it * explicitely. */ if (!root_mem_cgroup->use_hierarchy) - atomic_inc(&root_mem_cgroup->dead_count); + mem_cgroup_iter_invalidate(root_mem_cgroup); } static void mem_cgroup_css_offline(struct cgroup *cont) -- cgit v1.2.3 From 760033c45e8aa13c9e02530da7274edc624c7689 Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Wed, 3 Jul 2013 15:04:52 -0700 Subject: arch/frv/kernel/traps.c: using vsnprintf() instead of vsprintf() Since die_if_kernel() is an extern common used function, better always check the buffer length to avoid memory overflow by a long 'str'. Signed-off-by: Chen Gang Cc: David Howells Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/frv/kernel/traps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/frv/kernel/traps.c b/arch/frv/kernel/traps.c index 4bff48c19d29..a6d105d61b26 100644 --- a/arch/frv/kernel/traps.c +++ b/arch/frv/kernel/traps.c @@ -523,7 +523,7 @@ void die_if_kernel(const char *str, ...) return; va_start(va, str); - vsprintf(buffer, str, va); + vsnprintf(buffer, sizeof(buffer), str, va); va_end(va); console_verbose(); -- cgit v1.2.3 From 98bd8d05ea5798ff3179ee33f0ca2789722ecc5a Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Wed, 3 Jul 2013 15:04:53 -0700 Subject: arch/frv/kernel/setup.c: use strncmp() instead of memcmp() 'cmdline' is a NUL terminated string, when its length < 4, memcmp() will cause memory access out of boundary. So use strncmp() instead of memcmp(). Signed-off-by: Chen Gang Cc: David Howells Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/frv/kernel/setup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/frv/kernel/setup.c b/arch/frv/kernel/setup.c index f78f8cb44093..ae3a6706419b 100644 --- a/arch/frv/kernel/setup.c +++ b/arch/frv/kernel/setup.c @@ -735,7 +735,7 @@ static void __init parse_cmdline_early(char *cmdline) /* "mem=XXX[kKmM]" sets SDRAM size to , overriding the value we worked * out from the SDRAM controller mask register */ - if (!memcmp(cmdline, "mem=", 4)) { + if (!strncmp(cmdline, "mem=", 4)) { unsigned long long mem_size; mem_size = memparse(cmdline + 4, &cmdline); -- cgit v1.2.3 From e7152b97f38f1f5b915c719e6a3040697a700a16 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 3 Jul 2013 15:04:54 -0700 Subject: err.h: IS_ERR() can accept __user pointers Sparse generates a false positive when you pass a __user or __iomem pointer to the IS_ERR() functions. drivers/rtc/rtc-ds1286.c:344:36: sparse: incorrect type in argument 1 (different address spaces) drivers/rtc/rtc-ds1286.c:344:36: expected void const *ptr drivers/rtc/rtc-ds1286.c:344:36: got unsigned int [noderef] [usertype] *rtcregs We can silence these by adding a __force here and upgrading to Sparse v0.4.5-rc1 or later. This change has no effect when using current Sparse releases. Signed-off-by: Dan Carpenter Acked-by: Christopher Li Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/err.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/linux/err.h b/include/linux/err.h index f2edce25a76b..221fcfb676c4 100644 --- a/include/linux/err.h +++ b/include/linux/err.h @@ -24,17 +24,17 @@ static inline void * __must_check ERR_PTR(long error) return (void *) error; } -static inline long __must_check PTR_ERR(const void *ptr) +static inline long __must_check PTR_ERR(__force const void *ptr) { return (long) ptr; } -static inline long __must_check IS_ERR(const void *ptr) +static inline long __must_check IS_ERR(__force const void *ptr) { return IS_ERR_VALUE((unsigned long)ptr); } -static inline long __must_check IS_ERR_OR_NULL(const void *ptr) +static inline long __must_check IS_ERR_OR_NULL(__force const void *ptr) { return !ptr || IS_ERR_VALUE((unsigned long)ptr); } @@ -46,13 +46,13 @@ static inline long __must_check IS_ERR_OR_NULL(const void *ptr) * Explicitly cast an error-valued pointer to another pointer type in such a * way as to make it clear that's what's going on. */ -static inline void * __must_check ERR_CAST(const void *ptr) +static inline void * __must_check ERR_CAST(__force const void *ptr) { /* cast away the const */ return (void *) ptr; } -static inline int __must_check PTR_RET(const void *ptr) +static inline int __must_check PTR_RET(__force const void *ptr) { if (IS_ERR(ptr)) return PTR_ERR(ptr); -- cgit v1.2.3 From 096a8aac6bf4a5a0b2ef812ad76d056bbf3fb2af Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 3 Jul 2013 15:04:55 -0700 Subject: clean up scary strncpy(dst, src, strlen(src)) uses Fix various weird constructions of strncpy(dst, src, strlen(src)). Length limits should be about the space available in the destination, not repurposed as a method to either always include or always exclude a trailing NULL byte. Either the NULL should always be copied (using strlcpy), or it should not be copied (using something like memcpy). Readable code should not depend on the weird behavior of strncpy when it hits the length limit. Better to avoid the anti-pattern entirely. [akpm@linux-foundation.org: revert getdelays.c part due to missing bsd/string.h] Signed-off-by: Kees Cook Acked-by: Greg Kroah-Hartman [staging] Acked-by: Rafael J. Wysocki [acpi] Cc: Martin Schwidefsky Cc: Heiko Carstens Cc: Ursula Braun Cc: Frank Blaschka Cc: Richard Weinberger Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/acpi/sysfs.c | 3 +-- drivers/s390/net/qeth_l3_sys.c | 6 ++---- drivers/staging/tidspbridge/rmgr/drv_interface.c | 3 +-- fs/hppfs/hppfs.c | 11 ++++++----- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c index fcae5fa2e1b3..193745d96fcc 100644 --- a/drivers/acpi/sysfs.c +++ b/drivers/acpi/sysfs.c @@ -677,10 +677,9 @@ void acpi_irq_stats_init(void) else sprintf(buffer, "bug%02X", i); - name = kzalloc(strlen(buffer) + 1, GFP_KERNEL); + name = kstrdup(buffer, GFP_KERNEL); if (name == NULL) goto fail; - strncpy(name, buffer, strlen(buffer) + 1); sysfs_attr_init(&counter_attrs[i].attr); counter_attrs[i].attr.name = name; diff --git a/drivers/s390/net/qeth_l3_sys.c b/drivers/s390/net/qeth_l3_sys.c index e70af2406ff9..d1c8025b0b03 100644 --- a/drivers/s390/net/qeth_l3_sys.c +++ b/drivers/s390/net/qeth_l3_sys.c @@ -315,10 +315,8 @@ static ssize_t qeth_l3_dev_hsuid_store(struct device *dev, if (qeth_configure_cq(card, QETH_CQ_ENABLED)) return -EPERM; - for (i = 0; i < 8; i++) - card->options.hsuid[i] = ' '; - card->options.hsuid[8] = '\0'; - strncpy(card->options.hsuid, tmp, strlen(tmp)); + snprintf(card->options.hsuid, sizeof(card->options.hsuid), + "%-8s", tmp); ASCEBC(card->options.hsuid, 8); if (card->dev) memcpy(card->dev->perm_addr, card->options.hsuid, 9); diff --git a/drivers/staging/tidspbridge/rmgr/drv_interface.c b/drivers/staging/tidspbridge/rmgr/drv_interface.c index 9c020562c846..6d04eb48bfbc 100644 --- a/drivers/staging/tidspbridge/rmgr/drv_interface.c +++ b/drivers/staging/tidspbridge/rmgr/drv_interface.c @@ -421,12 +421,11 @@ static int omap3_bridge_startup(struct platform_device *pdev) drv_datap->tc_wordswapon = tc_wordswapon; if (base_img) { - drv_datap->base_img = kmalloc(strlen(base_img) + 1, GFP_KERNEL); + drv_datap->base_img = kstrdup(base_img, GFP_KERNEL); if (!drv_datap->base_img) { err = -ENOMEM; goto err2; } - strncpy(drv_datap->base_img, base_img, strlen(base_img) + 1); } dev_set_drvdata(bridge, drv_datap); diff --git a/fs/hppfs/hppfs.c b/fs/hppfs/hppfs.c index fc90ab11c340..4338ff32959d 100644 --- a/fs/hppfs/hppfs.c +++ b/fs/hppfs/hppfs.c @@ -69,7 +69,7 @@ static char *dentry_name(struct dentry *dentry, int extra) struct dentry *parent; char *root, *name; const char *seg_name; - int len, seg_len; + int len, seg_len, root_len; len = 0; parent = dentry; @@ -81,7 +81,8 @@ static char *dentry_name(struct dentry *dentry, int extra) } root = "proc"; - len += strlen(root); + root_len = strlen(root); + len += root_len; name = kmalloc(len + extra + 1, GFP_KERNEL); if (name == NULL) return NULL; @@ -91,7 +92,7 @@ static char *dentry_name(struct dentry *dentry, int extra) while (parent->d_parent != parent) { if (is_pid(parent)) { seg_name = "pid"; - seg_len = strlen("pid"); + seg_len = strlen(seg_name); } else { seg_name = parent->d_name.name; @@ -100,10 +101,10 @@ static char *dentry_name(struct dentry *dentry, int extra) len -= seg_len + 1; name[len] = '/'; - strncpy(&name[len + 1], seg_name, seg_len); + memcpy(&name[len + 1], seg_name, seg_len); parent = parent->d_parent; } - strncpy(name, root, strlen(root)); + memcpy(name, root, root_len); return name; } -- cgit v1.2.3 From 02aa2a37636c8fa4fb9322d91be46ff8225b7de0 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 3 Jul 2013 15:04:56 -0700 Subject: drivers: avoid format string in dev_set_name Calling dev_set_name with a single paramter causes it to be handled as a format string. Many callers are passing potentially dynamic string content, so use "%s" in those cases to avoid any potential accidents, including wrappers like device_create*() and bdi_register(). Signed-off-by: Kees Cook Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/base/attribute_container.c | 2 +- drivers/devfreq/devfreq.c | 2 +- drivers/extcon/extcon-class.c | 2 +- drivers/hsi/hsi.c | 2 +- drivers/ide/ide-cd.c | 2 +- drivers/ide/ide-gd.c | 2 +- drivers/ide/ide-probe.c | 4 ++-- drivers/ide/ide-tape.c | 2 +- drivers/infiniband/core/sysfs.c | 2 +- drivers/infiniband/hw/qib/qib_file_ops.c | 2 +- drivers/isdn/mISDN/dsp_pipeline.c | 2 +- drivers/mtd/mtdcore.c | 2 +- drivers/platform/x86/wmi.c | 2 +- drivers/scsi/sd.c | 2 +- drivers/staging/android/timed_output.c | 2 +- drivers/staging/dgrp/dgrp_sysfs.c | 2 +- drivers/uwb/lc-dev.c | 2 +- drivers/video/backlight/backlight.c | 2 +- drivers/video/backlight/lcd.c | 2 +- drivers/video/output.c | 2 +- drivers/xen/xenbus/xenbus_probe.c | 2 +- mm/backing-dev.c | 5 ++--- sound/sound_core.c | 2 +- 23 files changed, 25 insertions(+), 26 deletions(-) diff --git a/drivers/base/attribute_container.c b/drivers/base/attribute_container.c index d78b204e65c1..ecc1929d7f6a 100644 --- a/drivers/base/attribute_container.c +++ b/drivers/base/attribute_container.c @@ -167,7 +167,7 @@ attribute_container_add_device(struct device *dev, ic->classdev.parent = get_device(dev); ic->classdev.class = cont->class; cont->class->dev_release = attribute_container_release; - dev_set_name(&ic->classdev, dev_name(dev)); + dev_set_name(&ic->classdev, "%s", dev_name(dev)); if (fn) fn(cont, dev, &ic->classdev); else diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 3b367973a802..af8372feb587 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -477,7 +477,7 @@ struct devfreq *devfreq_add_device(struct device *dev, GFP_KERNEL); devfreq->last_stat_updated = jiffies; - dev_set_name(&devfreq->dev, dev_name(dev)); + dev_set_name(&devfreq->dev, "%s", dev_name(dev)); err = device_register(&devfreq->dev); if (err) { put_device(&devfreq->dev); diff --git a/drivers/extcon/extcon-class.c b/drivers/extcon/extcon-class.c index 8c69803558fe..18ccadef43fd 100644 --- a/drivers/extcon/extcon-class.c +++ b/drivers/extcon/extcon-class.c @@ -602,7 +602,7 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev) edev->dev->class = extcon_class; edev->dev->release = extcon_dev_release; - dev_set_name(edev->dev, edev->name ? edev->name : dev_name(dev)); + dev_set_name(edev->dev, "%s", edev->name ? edev->name : dev_name(dev)); if (edev->max_supported) { char buf[10]; diff --git a/drivers/hsi/hsi.c b/drivers/hsi/hsi.c index 833dd1afbf46..66d44581e1b1 100644 --- a/drivers/hsi/hsi.c +++ b/drivers/hsi/hsi.c @@ -75,7 +75,7 @@ static void hsi_new_client(struct hsi_port *port, struct hsi_board_info *info) cl->device.bus = &hsi_bus_type; cl->device.parent = &port->device; cl->device.release = hsi_client_release; - dev_set_name(&cl->device, info->name); + dev_set_name(&cl->device, "%s", info->name); cl->device.platform_data = info->platform_data; if (info->archdata) cl->device.archdata = *info->archdata; diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 2ff620444930..0b510bafd90e 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -1756,7 +1756,7 @@ static int ide_cd_probe(ide_drive_t *drive) info->dev.parent = &drive->gendev; info->dev.release = ide_cd_release; - dev_set_name(&info->dev, dev_name(&drive->gendev)); + dev_set_name(&info->dev, "%s", dev_name(&drive->gendev)); if (device_register(&info->dev)) goto out_free_disk; diff --git a/drivers/ide/ide-gd.c b/drivers/ide/ide-gd.c index de86631e767d..838996a0039e 100644 --- a/drivers/ide/ide-gd.c +++ b/drivers/ide/ide-gd.c @@ -392,7 +392,7 @@ static int ide_gd_probe(ide_drive_t *drive) idkp->dev.parent = &drive->gendev; idkp->dev.release = ide_disk_release; - dev_set_name(&idkp->dev, dev_name(&drive->gendev)); + dev_set_name(&idkp->dev, "%s", dev_name(&drive->gendev)); if (device_register(&idkp->dev)) goto out_free_disk; diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 068cef0a987a..2a744a91370e 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -545,7 +545,7 @@ static int ide_register_port(ide_hwif_t *hwif) int ret; /* register with global device tree */ - dev_set_name(&hwif->gendev, hwif->name); + dev_set_name(&hwif->gendev, "%s", hwif->name); dev_set_drvdata(&hwif->gendev, hwif); if (hwif->gendev.parent == NULL) hwif->gendev.parent = hwif->dev; @@ -559,7 +559,7 @@ static int ide_register_port(ide_hwif_t *hwif) } hwif->portdev = device_create(ide_port_class, &hwif->gendev, - MKDEV(0, 0), hwif, hwif->name); + MKDEV(0, 0), hwif, "%s", hwif->name); if (IS_ERR(hwif->portdev)) { ret = PTR_ERR(hwif->portdev); device_unregister(&hwif->gendev); diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index c6c574bd5f59..1793aea4a7d2 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -1985,7 +1985,7 @@ static int ide_tape_probe(ide_drive_t *drive) tape->dev.parent = &drive->gendev; tape->dev.release = ide_tape_release; - dev_set_name(&tape->dev, dev_name(&drive->gendev)); + dev_set_name(&tape->dev, "%s", dev_name(&drive->gendev)); if (device_register(&tape->dev)) goto out_free_disk; diff --git a/drivers/infiniband/core/sysfs.c b/drivers/infiniband/core/sysfs.c index 246fdc151652..99904f7d59e3 100644 --- a/drivers/infiniband/core/sysfs.c +++ b/drivers/infiniband/core/sysfs.c @@ -813,7 +813,7 @@ int ib_device_register_sysfs(struct ib_device *device, class_dev->class = &ib_class; class_dev->parent = device->dma_device; - dev_set_name(class_dev, device->name); + dev_set_name(class_dev, "%s", device->name); dev_set_drvdata(class_dev, device); INIT_LIST_HEAD(&device->port_list); diff --git a/drivers/infiniband/hw/qib/qib_file_ops.c b/drivers/infiniband/hw/qib/qib_file_ops.c index b56c9428f3c5..9dd0bc89c3aa 100644 --- a/drivers/infiniband/hw/qib/qib_file_ops.c +++ b/drivers/infiniband/hw/qib/qib_file_ops.c @@ -2208,7 +2208,7 @@ int qib_cdev_init(int minor, const char *name, goto err_cdev; } - device = device_create(qib_class, NULL, dev, NULL, name); + device = device_create(qib_class, NULL, dev, NULL, "%s", name); if (!IS_ERR(device)) goto done; ret = PTR_ERR(device); diff --git a/drivers/isdn/mISDN/dsp_pipeline.c b/drivers/isdn/mISDN/dsp_pipeline.c index 88305c9cbff5..8b1a66c6ca8a 100644 --- a/drivers/isdn/mISDN/dsp_pipeline.c +++ b/drivers/isdn/mISDN/dsp_pipeline.c @@ -102,7 +102,7 @@ int mISDN_dsp_element_register(struct mISDN_dsp_element *elem) entry->dev.class = elements_class; entry->dev.release = mISDN_dsp_dev_release; dev_set_drvdata(&entry->dev, elem); - dev_set_name(&entry->dev, elem->name); + dev_set_name(&entry->dev, "%s", elem->name); ret = device_register(&entry->dev); if (ret) { printk(KERN_ERR "%s: failed to register %s\n", diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index c400c57c394a..048c823f5c51 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -1151,7 +1151,7 @@ static int __init mtd_bdi_init(struct backing_dev_info *bdi, const char *name) ret = bdi_init(bdi); if (!ret) - ret = bdi_register(bdi, NULL, name); + ret = bdi_register(bdi, NULL, "%s", name); if (ret) bdi_destroy(bdi); diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index e4ac38aca580..b13344c59808 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -743,7 +743,7 @@ static int wmi_create_device(const struct guid_block *gblock, wblock->dev.class = &wmi_class; wmi_gtoa(gblock->guid, guid_string); - dev_set_name(&wblock->dev, guid_string); + dev_set_name(&wblock->dev, "%s", guid_string); dev_set_drvdata(&wblock->dev, wblock); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index c1c555242d0d..8fa3d0b73ad9 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -2931,7 +2931,7 @@ static int sd_probe(struct device *dev) device_initialize(&sdkp->dev); sdkp->dev.parent = dev; sdkp->dev.class = &sd_disk_class; - dev_set_name(&sdkp->dev, dev_name(dev)); + dev_set_name(&sdkp->dev, "%s", dev_name(dev)); if (device_add(&sdkp->dev)) goto out_free_index; diff --git a/drivers/staging/android/timed_output.c b/drivers/staging/android/timed_output.c index ec9e2ae2de0d..ee3a57f22832 100644 --- a/drivers/staging/android/timed_output.c +++ b/drivers/staging/android/timed_output.c @@ -78,7 +78,7 @@ int timed_output_dev_register(struct timed_output_dev *tdev) tdev->index = atomic_inc_return(&device_count); tdev->dev = device_create(timed_output_class, NULL, - MKDEV(0, tdev->index), NULL, tdev->name); + MKDEV(0, tdev->index), NULL, "%s", tdev->name); if (IS_ERR(tdev->dev)) return PTR_ERR(tdev->dev); diff --git a/drivers/staging/dgrp/dgrp_sysfs.c b/drivers/staging/dgrp/dgrp_sysfs.c index 7d1b36d1e75f..8cee9c8bc38b 100644 --- a/drivers/staging/dgrp/dgrp_sysfs.c +++ b/drivers/staging/dgrp/dgrp_sysfs.c @@ -273,7 +273,7 @@ void dgrp_create_node_class_sysfs_files(struct nd_struct *nd) sprintf(name, "node%ld", nd->nd_major); nd->nd_class_dev = device_create(dgrp_class, dgrp_class_nodes_dev, - MKDEV(0, nd->nd_major), NULL, name); + MKDEV(0, nd->nd_major), NULL, "%s", name); ret = sysfs_create_group(&nd->nd_class_dev->kobj, &dgrp_node_attribute_group); diff --git a/drivers/uwb/lc-dev.c b/drivers/uwb/lc-dev.c index 5241f1d0ef7a..9209eafc75b1 100644 --- a/drivers/uwb/lc-dev.c +++ b/drivers/uwb/lc-dev.c @@ -440,7 +440,7 @@ void uwbd_dev_onair(struct uwb_rc *rc, struct uwb_beca_e *bce) uwb_dev_init(uwb_dev); /* This sets refcnt to one, we own it */ uwb_dev->mac_addr = *bce->mac_addr; uwb_dev->dev_addr = bce->dev_addr; - dev_set_name(&uwb_dev->dev, macbuf); + dev_set_name(&uwb_dev->dev, "%s", macbuf); result = uwb_dev_add(uwb_dev, &rc->uwb_dev.dev, rc); if (result < 0) { dev_err(dev, "new device %s: cannot instantiate device\n", diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c index c74e7aa46731..e3c279083253 100644 --- a/drivers/video/backlight/backlight.c +++ b/drivers/video/backlight/backlight.c @@ -304,7 +304,7 @@ struct backlight_device *backlight_device_register(const char *name, new_bd->dev.class = backlight_class; new_bd->dev.parent = parent; new_bd->dev.release = bl_device_release; - dev_set_name(&new_bd->dev, name); + dev_set_name(&new_bd->dev, "%s", name); dev_set_drvdata(&new_bd->dev, devdata); /* Set default properties */ diff --git a/drivers/video/backlight/lcd.c b/drivers/video/backlight/lcd.c index 34fb6bd798c8..3649fd9ddb3a 100644 --- a/drivers/video/backlight/lcd.c +++ b/drivers/video/backlight/lcd.c @@ -219,7 +219,7 @@ struct lcd_device *lcd_device_register(const char *name, struct device *parent, new_ld->dev.class = lcd_class; new_ld->dev.parent = parent; new_ld->dev.release = lcd_device_release; - dev_set_name(&new_ld->dev, name); + dev_set_name(&new_ld->dev, "%s", name); dev_set_drvdata(&new_ld->dev, devdata); rc = device_register(&new_ld->dev); diff --git a/drivers/video/output.c b/drivers/video/output.c index 0d6f2cda9369..6285b9718451 100644 --- a/drivers/video/output.c +++ b/drivers/video/output.c @@ -97,7 +97,7 @@ struct output_device *video_output_register(const char *name, new_dev->props = op; new_dev->dev.class = &video_output_class; new_dev->dev.parent = dev; - dev_set_name(&new_dev->dev, name); + dev_set_name(&new_dev->dev, "%s", name); dev_set_drvdata(&new_dev->dev, devdata); ret_code = device_register(&new_dev->dev); if (ret_code) { diff --git a/drivers/xen/xenbus/xenbus_probe.c b/drivers/xen/xenbus/xenbus_probe.c index 56cfaaa9d006..8c9db16500e3 100644 --- a/drivers/xen/xenbus/xenbus_probe.c +++ b/drivers/xen/xenbus/xenbus_probe.c @@ -447,7 +447,7 @@ int xenbus_probe_node(struct xen_bus_type *bus, if (err) goto fail; - dev_set_name(&xendev->dev, devname); + dev_set_name(&xendev->dev, "%s", devname); /* Register with generic device framework. */ err = device_register(&xendev->dev); diff --git a/mm/backing-dev.c b/mm/backing-dev.c index 502517492258..d014ee5fcbbd 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -515,7 +515,6 @@ EXPORT_SYMBOL(bdi_destroy); int bdi_setup_and_register(struct backing_dev_info *bdi, char *name, unsigned int cap) { - char tmp[32]; int err; bdi->name = name; @@ -524,8 +523,8 @@ int bdi_setup_and_register(struct backing_dev_info *bdi, char *name, if (err) return err; - sprintf(tmp, "%.28s%s", name, "-%d"); - err = bdi_register(bdi, NULL, tmp, atomic_long_inc_return(&bdi_seq)); + err = bdi_register(bdi, NULL, "%.28s-%ld", name, + atomic_long_inc_return(&bdi_seq)); if (err) { bdi_destroy(bdi); return err; diff --git a/sound/sound_core.c b/sound/sound_core.c index 359753fc24e1..45759f4cca75 100644 --- a/sound/sound_core.c +++ b/sound/sound_core.c @@ -292,7 +292,7 @@ retry: } device_create(sound_class, dev, MKDEV(SOUND_MAJOR, s->unit_minor), - NULL, s->name+6); + NULL, "%s", s->name+6); return s->unit_minor; fail: -- cgit v1.2.3 From d8537548c924db3c44afde7646b6e220c7beb79d Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 3 Jul 2013 15:04:57 -0700 Subject: drivers: avoid format strings in names passed to alloc_workqueue() For the workqueue creation interfaces that do not expect format strings, make sure they cannot accidently be parsed that way. Additionally, clean up calls made with a single parameter that would be handled as a format string. Many callers are passing potentially dynamic string content, so use "%s" in those cases to avoid any potential accidents. Signed-off-by: Kees Cook Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- crypto/pcrypt.c | 4 ++-- drivers/media/pci/cx18/cx18-driver.c | 2 +- drivers/message/i2o/driver.c | 4 ++-- drivers/net/wireless/rt2x00/rt2x00dev.c | 2 +- drivers/net/wireless/rtlwifi/base.c | 2 +- drivers/pci/hotplug/pciehp_hpc.c | 4 +--- drivers/pci/hotplug/shpchp_core.c | 3 +-- drivers/scsi/be2iscsi/be_main.c | 2 +- drivers/scsi/qla4xxx/ql4_os.c | 4 ++-- drivers/scsi/scsi_transport_fc.c | 6 +++--- include/linux/workqueue.h | 7 ++++--- net/bluetooth/hci_core.c | 9 ++++----- net/mac80211/main.c | 2 +- 13 files changed, 24 insertions(+), 27 deletions(-) diff --git a/crypto/pcrypt.c b/crypto/pcrypt.c index b2c99dc1c5e2..f8c920cafe63 100644 --- a/crypto/pcrypt.c +++ b/crypto/pcrypt.c @@ -455,8 +455,8 @@ static int pcrypt_init_padata(struct padata_pcrypt *pcrypt, get_online_cpus(); - pcrypt->wq = alloc_workqueue(name, - WQ_MEM_RECLAIM | WQ_CPU_INTENSIVE, 1); + pcrypt->wq = alloc_workqueue("%s", WQ_MEM_RECLAIM | WQ_CPU_INTENSIVE, + 1, name); if (!pcrypt->wq) goto err; diff --git a/drivers/media/pci/cx18/cx18-driver.c b/drivers/media/pci/cx18/cx18-driver.c index 67b61cf3e03a..004d8ace5019 100644 --- a/drivers/media/pci/cx18/cx18-driver.c +++ b/drivers/media/pci/cx18/cx18-driver.c @@ -695,7 +695,7 @@ static int cx18_create_in_workq(struct cx18 *cx) { snprintf(cx->in_workq_name, sizeof(cx->in_workq_name), "%s-in", cx->v4l2_dev.name); - cx->in_work_queue = alloc_ordered_workqueue(cx->in_workq_name, 0); + cx->in_work_queue = alloc_ordered_workqueue("%s", 0, cx->in_workq_name); if (cx->in_work_queue == NULL) { CX18_ERR("Unable to create incoming mailbox handler thread\n"); return -ENOMEM; diff --git a/drivers/message/i2o/driver.c b/drivers/message/i2o/driver.c index 8a5b2d8f4daf..813eaa33fa14 100644 --- a/drivers/message/i2o/driver.c +++ b/drivers/message/i2o/driver.c @@ -84,8 +84,8 @@ int i2o_driver_register(struct i2o_driver *drv) osm_debug("Register driver %s\n", drv->name); if (drv->event) { - drv->event_queue = alloc_workqueue(drv->name, - WQ_MEM_RECLAIM, 1); + drv->event_queue = alloc_workqueue("%s", WQ_MEM_RECLAIM, 1, + drv->name); if (!drv->event_queue) { osm_err("Could not initialize event queue for driver " "%s\n", drv->name); diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 90dc14336980..c8b9ef0c21f8 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -1321,7 +1321,7 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) * Initialize work. */ rt2x00dev->workqueue = - alloc_ordered_workqueue(wiphy_name(rt2x00dev->hw->wiphy), 0); + alloc_ordered_workqueue("%s", 0, wiphy_name(rt2x00dev->hw->wiphy)); if (!rt2x00dev->workqueue) { retval = -ENOMEM; goto exit; diff --git a/drivers/net/wireless/rtlwifi/base.c b/drivers/net/wireless/rtlwifi/base.c index af59dd5718e1..a5f223145b0f 100644 --- a/drivers/net/wireless/rtlwifi/base.c +++ b/drivers/net/wireless/rtlwifi/base.c @@ -380,7 +380,7 @@ static void _rtl_init_deferred_work(struct ieee80211_hw *hw) /* <2> work queue */ rtlpriv->works.hw = hw; - rtlpriv->works.rtl_wq = alloc_workqueue(rtlpriv->cfg->name, 0, 0); + rtlpriv->works.rtl_wq = alloc_workqueue("%s", 0, 0, rtlpriv->cfg->name); INIT_DELAYED_WORK(&rtlpriv->works.watchdog_wq, (void *)rtl_watchdog_wq_callback); INIT_DELAYED_WORK(&rtlpriv->works.ips_nic_off_wq, diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 5127f3f41821..b2255736ac81 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -773,14 +773,12 @@ static void pcie_shutdown_notification(struct controller *ctrl) static int pcie_init_slot(struct controller *ctrl) { struct slot *slot; - char name[32]; slot = kzalloc(sizeof(*slot), GFP_KERNEL); if (!slot) return -ENOMEM; - snprintf(name, sizeof(name), "pciehp-%u", PSN(ctrl)); - slot->wq = alloc_workqueue(name, 0, 0); + slot->wq = alloc_workqueue("pciehp-%u", 0, 0, PSN(ctrl)); if (!slot->wq) goto abort; diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c index 3100c52c837c..d3f757df691c 100644 --- a/drivers/pci/hotplug/shpchp_core.c +++ b/drivers/pci/hotplug/shpchp_core.c @@ -128,8 +128,7 @@ static int init_slots(struct controller *ctrl) slot->hpc_ops = ctrl->hpc_ops; slot->number = ctrl->first_slot + (ctrl->slot_num_inc * i); - snprintf(name, sizeof(name), "shpchp-%d", slot->number); - slot->wq = alloc_workqueue(name, 0, 0); + slot->wq = alloc_workqueue("shpchp-%d", 0, 0, slot->number); if (!slot->wq) { retval = -ENOMEM; goto error_info; diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c index d24a2867bc21..a1f5ac7a9806 100644 --- a/drivers/scsi/be2iscsi/be_main.c +++ b/drivers/scsi/be2iscsi/be_main.c @@ -4996,7 +4996,7 @@ static int beiscsi_dev_probe(struct pci_dev *pcidev, snprintf(phba->wq_name, sizeof(phba->wq_name), "beiscsi_%02x_wq", phba->shost->host_no); - phba->wq = alloc_workqueue(phba->wq_name, WQ_MEM_RECLAIM, 1); + phba->wq = alloc_workqueue("%s", WQ_MEM_RECLAIM, 1, phba->wq_name); if (!phba->wq) { beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, "BM_%d : beiscsi_dev_probe-" diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index 4d231c12463e..b246b3c26912 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -7060,8 +7060,8 @@ skip_retry_init: } INIT_WORK(&ha->dpc_work, qla4xxx_do_dpc); - sprintf(buf, "qla4xxx_%lu_task", ha->host_no); - ha->task_wq = alloc_workqueue(buf, WQ_MEM_RECLAIM, 1); + ha->task_wq = alloc_workqueue("qla4xxx_%lu_task", WQ_MEM_RECLAIM, 1, + ha->host_no); if (!ha->task_wq) { ql4_printk(KERN_WARNING, ha, "Unable to start task thread!\n"); ret = -ENODEV; diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index e106c276aa00..4628fd5e0688 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -435,7 +435,7 @@ static int fc_host_setup(struct transport_container *tc, struct device *dev, snprintf(fc_host->work_q_name, sizeof(fc_host->work_q_name), "fc_wq_%d", shost->host_no); - fc_host->work_q = alloc_workqueue(fc_host->work_q_name, 0, 0); + fc_host->work_q = alloc_workqueue("%s", 0, 0, fc_host->work_q_name); if (!fc_host->work_q) return -ENOMEM; @@ -443,8 +443,8 @@ static int fc_host_setup(struct transport_container *tc, struct device *dev, snprintf(fc_host->devloss_work_q_name, sizeof(fc_host->devloss_work_q_name), "fc_dl_%d", shost->host_no); - fc_host->devloss_work_q = - alloc_workqueue(fc_host->devloss_work_q_name, 0, 0); + fc_host->devloss_work_q = alloc_workqueue("%s", 0, 0, + fc_host->devloss_work_q_name); if (!fc_host->devloss_work_q) { destroy_workqueue(fc_host->work_q); fc_host->work_q = NULL; diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index a9f4119c7e2e..a0ed78ab54d7 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -445,11 +445,12 @@ __alloc_workqueue_key(const char *fmt, unsigned int flags, int max_active, alloc_workqueue(fmt, WQ_UNBOUND | __WQ_ORDERED | (flags), 1, ##args) #define create_workqueue(name) \ - alloc_workqueue((name), WQ_MEM_RECLAIM, 1) + alloc_workqueue("%s", WQ_MEM_RECLAIM, 1, (name)) #define create_freezable_workqueue(name) \ - alloc_workqueue((name), WQ_FREEZABLE | WQ_UNBOUND | WQ_MEM_RECLAIM, 1) + alloc_workqueue("%s", WQ_FREEZABLE | WQ_UNBOUND | WQ_MEM_RECLAIM, \ + 1, (name)) #define create_singlethread_workqueue(name) \ - alloc_workqueue((name), WQ_UNBOUND | WQ_MEM_RECLAIM, 1) + alloc_workqueue("%s", WQ_UNBOUND | WQ_MEM_RECLAIM, 1, (name)) extern void destroy_workqueue(struct workqueue_struct *wq); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index ace5e55fe5a3..db7de80b88a2 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2211,16 +2211,15 @@ int hci_register_dev(struct hci_dev *hdev) list_add(&hdev->list, &hci_dev_list); write_unlock(&hci_dev_list_lock); - hdev->workqueue = alloc_workqueue(hdev->name, WQ_HIGHPRI | WQ_UNBOUND | - WQ_MEM_RECLAIM, 1); + hdev->workqueue = alloc_workqueue("%s", WQ_HIGHPRI | WQ_UNBOUND | + WQ_MEM_RECLAIM, 1, hdev->name); if (!hdev->workqueue) { error = -ENOMEM; goto err; } - hdev->req_workqueue = alloc_workqueue(hdev->name, - WQ_HIGHPRI | WQ_UNBOUND | - WQ_MEM_RECLAIM, 1); + hdev->req_workqueue = alloc_workqueue("%s", WQ_HIGHPRI | WQ_UNBOUND | + WQ_MEM_RECLAIM, 1, hdev->name); if (!hdev->req_workqueue) { destroy_workqueue(hdev->workqueue); error = -ENOMEM; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 8a7bfc47d577..8eae74ac4e1e 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -921,7 +921,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) hw->queues = IEEE80211_MAX_QUEUES; local->workqueue = - alloc_ordered_workqueue(wiphy_name(local->hw.wiphy), 0); + alloc_ordered_workqueue("%s", 0, wiphy_name(local->hw.wiphy)); if (!local->workqueue) { result = -ENOMEM; goto fail_workqueue; -- cgit v1.2.3 From f170168b9a0b61ea1e647b082b38f605f1d3de3e Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 3 Jul 2013 15:04:58 -0700 Subject: drivers: avoid parsing names as kthread_run() format strings Calling kthread_run with a single name parameter causes it to be handled as a format string. Many callers are passing potentially dynamic string content, so use "%s" in those cases to avoid any potential accidents. Signed-off-by: Kees Cook Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/aoe/aoecmd.c | 2 +- drivers/block/mtip32xx/mtip32xx.c | 3 ++- drivers/block/xen-blkback/xenbus.c | 2 +- drivers/hwmon/adt7470.c | 2 +- drivers/media/i2c/tvaudio.c | 3 ++- drivers/media/pci/ivtv/ivtv-driver.c | 2 +- drivers/media/platform/vivi.c | 3 ++- drivers/mtd/ubi/build.c | 2 +- drivers/net/wireless/airo.c | 3 ++- drivers/scsi/aacraid/commctrl.c | 3 ++- drivers/scsi/aacraid/commsup.c | 3 ++- drivers/spi/spi.c | 2 +- drivers/staging/rtl8712/os_intfs.c | 2 +- drivers/usb/atm/usbatm.c | 5 +++-- fs/lockd/svc.c | 2 +- fs/nfs/callback.c | 5 ++--- fs/nfs/nfs4state.c | 2 +- kernel/rcutree.c | 2 +- net/sunrpc/svc.c | 2 +- 19 files changed, 28 insertions(+), 22 deletions(-) diff --git a/drivers/block/aoe/aoecmd.c b/drivers/block/aoe/aoecmd.c index fc803ecbbce4..b75c7db16559 100644 --- a/drivers/block/aoe/aoecmd.c +++ b/drivers/block/aoe/aoecmd.c @@ -1340,7 +1340,7 @@ aoe_ktstart(struct ktstate *k) struct task_struct *task; init_completion(&k->rendez); - task = kthread_run(kthread, k, k->name); + task = kthread_run(kthread, k, "%s", k->name); if (task == NULL || IS_ERR(task)) return -ENOMEM; k->task = task; diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c index 20dd52a2f92f..952dbfe22126 100644 --- a/drivers/block/mtip32xx/mtip32xx.c +++ b/drivers/block/mtip32xx/mtip32xx.c @@ -4087,7 +4087,8 @@ skip_create_disk: start_service_thread: sprintf(thd_name, "mtip_svc_thd_%02d", index); dd->mtip_svc_handler = kthread_create_on_node(mtip_service_thread, - dd, dd->numa_node, thd_name); + dd, dd->numa_node, "%s", + thd_name); if (IS_ERR(dd->mtip_svc_handler)) { dev_err(&dd->pdev->dev, "service thread failed to start\n"); diff --git a/drivers/block/xen-blkback/xenbus.c b/drivers/block/xen-blkback/xenbus.c index 8bfd1bcf95ec..04608a6502d7 100644 --- a/drivers/block/xen-blkback/xenbus.c +++ b/drivers/block/xen-blkback/xenbus.c @@ -93,7 +93,7 @@ static void xen_update_blkif_status(struct xen_blkif *blkif) } invalidate_inode_pages2(blkif->vbd.bdev->bd_inode->i_mapping); - blkif->xenblkd = kthread_run(xen_blkif_schedule, blkif, name); + blkif->xenblkd = kthread_run(xen_blkif_schedule, blkif, "%s", name); if (IS_ERR(blkif->xenblkd)) { err = PTR_ERR(blkif->xenblkd); blkif->xenblkd = NULL; diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index b83bf4bb95eb..0f34bca9f5e5 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -1285,7 +1285,7 @@ static int adt7470_probe(struct i2c_client *client, } init_completion(&data->auto_update_stop); - data->auto_update = kthread_run(adt7470_update_thread, client, + data->auto_update = kthread_run(adt7470_update_thread, client, "%s", dev_name(data->hwmon_dev)); if (IS_ERR(data->auto_update)) { err = PTR_ERR(data->auto_update); diff --git a/drivers/media/i2c/tvaudio.c b/drivers/media/i2c/tvaudio.c index b72a59d3216a..e0634c8b7e0b 100644 --- a/drivers/media/i2c/tvaudio.c +++ b/drivers/media/i2c/tvaudio.c @@ -2020,7 +2020,8 @@ static int tvaudio_probe(struct i2c_client *client, const struct i2c_device_id * /* start async thread */ chip->wt.function = chip_thread_wake; chip->wt.data = (unsigned long)chip; - chip->thread = kthread_run(chip_thread, chip, client->name); + chip->thread = kthread_run(chip_thread, chip, "%s", + client->name); if (IS_ERR(chip->thread)) { v4l2_warn(sd, "failed to create kthread\n"); chip->thread = NULL; diff --git a/drivers/media/pci/ivtv/ivtv-driver.c b/drivers/media/pci/ivtv/ivtv-driver.c index 07b8460953b6..b809bc868a9f 100644 --- a/drivers/media/pci/ivtv/ivtv-driver.c +++ b/drivers/media/pci/ivtv/ivtv-driver.c @@ -753,7 +753,7 @@ static int ivtv_init_struct1(struct ivtv *itv) init_kthread_worker(&itv->irq_worker); itv->irq_worker_task = kthread_run(kthread_worker_fn, &itv->irq_worker, - itv->v4l2_dev.name); + "%s", itv->v4l2_dev.name); if (IS_ERR(itv->irq_worker_task)) { IVTV_ERR("Could not create ivtv task\n"); return -1; diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c index 85bc314382d3..1d3f11965196 100644 --- a/drivers/media/platform/vivi.c +++ b/drivers/media/platform/vivi.c @@ -768,7 +768,8 @@ static int vivi_start_generating(struct vivi_dev *dev) dma_q->frame = 0; dma_q->ini_jiffies = jiffies; - dma_q->kthread = kthread_run(vivi_thread, dev, dev->v4l2_dev.name); + dma_q->kthread = kthread_run(vivi_thread, dev, "%s", + dev->v4l2_dev.name); if (IS_ERR(dma_q->kthread)) { v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n"); diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index a56133585e92..0aaece9107c7 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -1005,7 +1005,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, if (err) goto out_uif; - ubi->bgt_thread = kthread_create(ubi_thread, ubi, ubi->bgt_name); + ubi->bgt_thread = kthread_create(ubi_thread, ubi, "%s", ubi->bgt_name); if (IS_ERR(ubi->bgt_thread)) { err = PTR_ERR(ubi->bgt_thread); ubi_err("cannot spawn \"%s\", error %d", ubi->bgt_name, diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c index 6125adb520a3..d0adbaf86186 100644 --- a/drivers/net/wireless/airo.c +++ b/drivers/net/wireless/airo.c @@ -1893,7 +1893,8 @@ static int airo_open(struct net_device *dev) { if (ai->wifidev != dev) { clear_bit(JOB_DIE, &ai->jobs); - ai->airo_thread_task = kthread_run(airo_thread, dev, dev->name); + ai->airo_thread_task = kthread_run(airo_thread, dev, "%s", + dev->name); if (IS_ERR(ai->airo_thread_task)) return (int)PTR_ERR(ai->airo_thread_task); diff --git a/drivers/scsi/aacraid/commctrl.c b/drivers/scsi/aacraid/commctrl.c index 1ef041bc60c8..d85ac1a9d2c0 100644 --- a/drivers/scsi/aacraid/commctrl.c +++ b/drivers/scsi/aacraid/commctrl.c @@ -318,7 +318,8 @@ return_fib: kthread_stop(dev->thread); ssleep(1); dev->aif_thread = 0; - dev->thread = kthread_run(aac_command_thread, dev, dev->name); + dev->thread = kthread_run(aac_command_thread, dev, + "%s", dev->name); ssleep(1); } if (f.wait) { diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c index 1be0776a80c4..cab190af6345 100644 --- a/drivers/scsi/aacraid/commsup.c +++ b/drivers/scsi/aacraid/commsup.c @@ -1336,7 +1336,8 @@ static int _aac_reset_adapter(struct aac_dev *aac, int forced) if ((retval = pci_set_dma_mask(aac->pdev, DMA_BIT_MASK(32)))) goto out; if (jafo) { - aac->thread = kthread_run(aac_command_thread, aac, aac->name); + aac->thread = kthread_run(aac_command_thread, aac, "%s", + aac->name); if (IS_ERR(aac->thread)) { retval = PTR_ERR(aac->thread); goto out; diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 32b7bb111eb6..085db8b2f2bc 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -601,7 +601,7 @@ static int spi_init_queue(struct spi_master *master) init_kthread_worker(&master->kworker); master->kworker_task = kthread_run(kthread_worker_fn, - &master->kworker, + &master->kworker, "%s", dev_name(&master->dev)); if (IS_ERR(master->kworker_task)) { dev_err(&master->dev, "failed to create message pump task\n"); diff --git a/drivers/staging/rtl8712/os_intfs.c b/drivers/staging/rtl8712/os_intfs.c index b65bf5e177a8..6e81ba0eaf1e 100644 --- a/drivers/staging/rtl8712/os_intfs.c +++ b/drivers/staging/rtl8712/os_intfs.c @@ -238,7 +238,7 @@ struct net_device *r8712_init_netdev(void) static u32 start_drv_threads(struct _adapter *padapter) { - padapter->cmdThread = kthread_run(r8712_cmd_thread, padapter, + padapter->cmdThread = kthread_run(r8712_cmd_thread, padapter, "%s", padapter->pnetdev->name); if (IS_ERR(padapter->cmdThread) < 0) return _FAIL; diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c index d3527dd8b90c..5e0d33a7da58 100644 --- a/drivers/usb/atm/usbatm.c +++ b/drivers/usb/atm/usbatm.c @@ -1020,7 +1020,7 @@ static int usbatm_heavy_init(struct usbatm_data *instance) { struct task_struct *t; - t = kthread_create(usbatm_do_heavy_init, instance, + t = kthread_create(usbatm_do_heavy_init, instance, "%s", instance->driver->driver_name); if (IS_ERR(t)) { usb_err(instance, "%s: failed to create kernel_thread (%ld)!\n", @@ -1076,7 +1076,8 @@ int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id, /* public fields */ instance->driver = driver; - snprintf(instance->driver_name, sizeof(instance->driver_name), driver->driver_name); + strlcpy(instance->driver_name, driver->driver_name, + sizeof(instance->driver_name)); instance->usb_dev = usb_dev; instance->usb_intf = intf; diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index a2aa97d45670..10d6c41aecad 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -305,7 +305,7 @@ static int lockd_start_svc(struct svc_serv *serv) svc_sock_update_bufs(serv); serv->sv_maxconn = nlm_max_connections; - nlmsvc_task = kthread_run(lockd, nlmsvc_rqst, serv->sv_name); + nlmsvc_task = kthread_run(lockd, nlmsvc_rqst, "%s", serv->sv_name); if (IS_ERR(nlmsvc_task)) { error = PTR_ERR(nlmsvc_task); printk(KERN_WARNING diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index cff089a412c7..da6a43d19aa3 100644 --- a/fs/nfs/callback.c +++ b/fs/nfs/callback.c @@ -211,7 +211,6 @@ static int nfs_callback_start_svc(int minorversion, struct rpc_xprt *xprt, struct svc_rqst *rqstp; int (*callback_svc)(void *vrqstp); struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion]; - char svc_name[12]; int ret; nfs_callback_bc_serv(minorversion, xprt, serv); @@ -235,10 +234,10 @@ static int nfs_callback_start_svc(int minorversion, struct rpc_xprt *xprt, svc_sock_update_bufs(serv); - sprintf(svc_name, "nfsv4.%u-svc", minorversion); cb_info->serv = serv; cb_info->rqst = rqstp; - cb_info->task = kthread_run(callback_svc, cb_info->rqst, svc_name); + cb_info->task = kthread_run(callback_svc, cb_info->rqst, + "nfsv4.%u-svc", minorversion); if (IS_ERR(cb_info->task)) { ret = PTR_ERR(cb_info->task); svc_exit_thread(cb_info->rqst); diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index ff10b4aa534c..55418811a55a 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -1194,7 +1194,7 @@ void nfs4_schedule_state_manager(struct nfs_client *clp) snprintf(buf, sizeof(buf), "%s-manager", rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR)); rcu_read_unlock(); - task = kthread_run(nfs4_run_state_manager, clp, buf); + task = kthread_run(nfs4_run_state_manager, clp, "%s", buf); if (IS_ERR(task)) { printk(KERN_ERR "%s: kthread_run: %ld\n", __func__, PTR_ERR(task)); diff --git a/kernel/rcutree.c b/kernel/rcutree.c index cf3adc6fe001..e08abb9461ac 100644 --- a/kernel/rcutree.c +++ b/kernel/rcutree.c @@ -3026,7 +3026,7 @@ static int __init rcu_spawn_gp_kthread(void) struct task_struct *t; for_each_rcu_flavor(rsp) { - t = kthread_run(rcu_gp_kthread, rsp, rsp->name); + t = kthread_run(rcu_gp_kthread, rsp, "%s", rsp->name); BUG_ON(IS_ERR(t)); rnp = rcu_get_root(rsp); raw_spin_lock_irqsave(&rnp->lock, flags); diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 89a588b4478b..b974571126fe 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -740,7 +740,7 @@ svc_set_num_threads(struct svc_serv *serv, struct svc_pool *pool, int nrservs) __module_get(serv->sv_module); task = kthread_create_on_node(serv->sv_function, rqstp, - node, serv->sv_name); + node, "%s", serv->sv_name); if (IS_ERR(task)) { error = PTR_ERR(task); module_put(serv->sv_module); -- cgit v1.2.3 From b58d977432c80ee60d6970042775a62e3f8d7675 Mon Sep 17 00:00:00 2001 From: Alex Thorlton Date: Wed, 3 Jul 2013 15:04:59 -0700 Subject: dump_stack: serialize the output from dump_stack() Add functionality to serialize the output from dump_stack() to avoid mangling of the output when dump_stack is called simultaneously from multiple cpus. [akpm@linux-foundation.org: fix comment indenting, avoid inclusion of files - use where possiblem fix uniprocessor build (__dump_stack undefined), remove unneeded ifdef around smp.h inclusion] Signed-off-by: Alex Thorlton Reported-by: Russ Anderson Reviewed-by: Robin Holt Cc: Vineet Gupta Cc: David S. Miller Cc: Richard Kuo Cc: Jesper Nilsson Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- lib/dump_stack.c | 47 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/lib/dump_stack.c b/lib/dump_stack.c index 53bad099ebd6..c03154173cc7 100644 --- a/lib/dump_stack.c +++ b/lib/dump_stack.c @@ -6,15 +6,58 @@ #include #include #include +#include +#include + +static void __dump_stack(void) +{ + dump_stack_print_info(KERN_DEFAULT); + show_stack(NULL, NULL); +} /** * dump_stack - dump the current task information and its stack trace * * Architectures can override this implementation by implementing its own. */ +#ifdef CONFIG_SMP +static atomic_t dump_lock = ATOMIC_INIT(-1); + void dump_stack(void) { - dump_stack_print_info(KERN_DEFAULT); - show_stack(NULL, NULL); + int was_locked; + int old; + int cpu; + + /* + * Permit this cpu to perform nested stack dumps while serialising + * against other CPUs + */ + preempt_disable(); + +retry: + cpu = smp_processor_id(); + old = atomic_cmpxchg(&dump_lock, -1, cpu); + if (old == -1) { + was_locked = 0; + } else if (old == cpu) { + was_locked = 1; + } else { + cpu_relax(); + goto retry; + } + + __dump_stack(); + + if (!was_locked) + atomic_set(&dump_lock, -1); + + preempt_enable(); +} +#else +void dump_stack(void) +{ + __dump_stack(); } +#endif EXPORT_SYMBOL(dump_stack); -- cgit v1.2.3 From 7ec75e1ca1bd35872a5c7b33da4b05395bc74364 Mon Sep 17 00:00:00 2001 From: liguang Date: Wed, 3 Jul 2013 15:05:00 -0700 Subject: kernel/sys.c: sys_reboot(): fix malformed panic message If LINUX_REBOOT_CMD_HALT for reboot failed, the message "cannot halt" will stay on the same line with the next message, so append a '\n'. Signed-off-by: liguang Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/sys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/sys.c b/kernel/sys.c index 2bbd9a73b54c..c1da757a97b0 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -511,7 +511,7 @@ SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, case LINUX_REBOOT_CMD_HALT: kernel_halt(); do_exit(0); - panic("cannot halt"); + panic("cannot halt.\n"); case LINUX_REBOOT_CMD_POWER_OFF: kernel_power_off(); -- cgit v1.2.3 From 45c64940c8bb64a042464ecec89d95eb4cce9b07 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 3 Jul 2013 15:05:01 -0700 Subject: kernel/sys.c:do_sysinfo(): use get_monotonic_boottime() Change do_sysinfo() to use get_monotonic_boottime() instead of do_posix_clock_monotonic_gettime() + monotonic_to_bootbased(). Signed-off-by: Oleg Nesterov Cc: "Eric W. Biederman" Acked-by: John Stultz Cc: Tomas Janousek Cc: Tomas Smetana Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/sys.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/kernel/sys.c b/kernel/sys.c index c1da757a97b0..7bf50dcc6d53 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -2355,8 +2355,7 @@ static int do_sysinfo(struct sysinfo *info) memset(info, 0, sizeof(struct sysinfo)); - ktime_get_ts(&tp); - monotonic_to_bootbased(&tp); + get_monotonic_boottime(&tp); info->uptime = tp.tv_sec + (tp.tv_nsec ? 1 : 0); get_avenrun(info->loads, 0, SI_LOAD_SHIFT - FSHIFT); -- cgit v1.2.3 From 5017b2851373ee15c7035151853bb1448800cae2 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 3 Jul 2013 15:05:02 -0700 Subject: dmi: add support for exact DMI matches in addition to substring matching dmi_match() considers a substring match to be a successful match. This is not always sufficient to distinguish between DMI data for different systems. Add support for exact string matching using strcmp() in addition to the substring matching using strstr(). The specific use case in the i915 driver is to allow us to use an exact match for D510MO, without also incorrectly matching D510MOV: { .ident = "Intel D510MO", .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "Intel"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "D510MO"), }, } Signed-off-by: Jani Nikula Cc: Cc: Chris Wilson Cc: Cornel Panceac Acked-by: Daniel Vetter Cc: Greg KH Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/firmware/dmi_scan.c | 12 +++++++++--- include/linux/mod_devicetable.h | 6 ++++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index b95159b33c39..eb760a218da4 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c @@ -551,9 +551,15 @@ static bool dmi_matches(const struct dmi_system_id *dmi) int s = dmi->matches[i].slot; if (s == DMI_NONE) break; - if (dmi_ident[s] - && strstr(dmi_ident[s], dmi->matches[i].substr)) - continue; + if (dmi_ident[s]) { + if (!dmi->matches[i].exact_match && + strstr(dmi_ident[s], dmi->matches[i].substr)) + continue; + else if (dmi->matches[i].exact_match && + !strcmp(dmi_ident[s], dmi->matches[i].substr)) + continue; + } + /* No match */ return false; } diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index b508016fb76d..b3bd7e737e8b 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -456,7 +456,8 @@ enum dmi_field { }; struct dmi_strmatch { - unsigned char slot; + unsigned char slot:7; + unsigned char exact_match:1; char substr[79]; }; @@ -474,7 +475,8 @@ struct dmi_system_id { */ #define dmi_device_id dmi_system_id -#define DMI_MATCH(a, b) { a, b } +#define DMI_MATCH(a, b) { .slot = a, .substr = b } +#define DMI_EXACT_MATCH(a, b) { .slot = a, .substr = b, .exact_match = 1 } #define PLATFORM_NAME_SIZE 20 #define PLATFORM_MODULE_PREFIX "platform:" -- cgit v1.2.3 From e5614f0c2d0f4d7f0b8ef745d34593baf2c5dbf8 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 3 Jul 2013 15:05:03 -0700 Subject: drm/i915: quirk away phantom LVDS on Intel's D510MO mainboard This replaceable mainboard only has a VGA-out, yet it claims to also have a connected LVDS header. Addresses https://bugs.freedesktop.org/show_bug.cgi?id=63860 [jani.nikula@intel.com: use DMI_EXACT_MATCH for board name.] Signed-off-by: Chris Wilson Signed-off-by: Jani Nikula Reported-by: Cc: Cornel Panceac Acked-by: Daniel Vetter Cc: Greg KH Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/gpu/drm/i915/intel_lvds.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 29412cc89c7a..5716cf340539 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -869,6 +869,14 @@ static const struct dmi_system_id intel_no_lvds[] = { DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Q900"), }, }, + { + .callback = intel_no_lvds_dmi_callback, + .ident = "Intel D510MO", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Intel"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "D510MO"), + }, + }, { } /* terminating entry */ }; -- cgit v1.2.3 From dcf6d294830d46b0e6901477fb4bf455281d90c8 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 3 Jul 2013 15:05:05 -0700 Subject: drm/i915: quirk away phantom LVDS on Intel's D525MW mainboard This replaceable mainboard only has a VGA-out, yet it claims to also have a connected LVDS header. Addresses https://bugs.freedesktop.org/show_bug.cgi?id=65256 Signed-off-by: Jani Nikula Reported-by: Cornel Panceac Cc: Chris Wilson Cc: Acked-by: Daniel Vetter Cc: Greg KH Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/gpu/drm/i915/intel_lvds.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 5716cf340539..817f936e2666 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -877,6 +877,14 @@ static const struct dmi_system_id intel_no_lvds[] = { DMI_EXACT_MATCH(DMI_BOARD_NAME, "D510MO"), }, }, + { + .callback = intel_no_lvds_dmi_callback, + .ident = "Intel D525MW", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Intel"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "D525MW"), + }, + }, { } /* terminating entry */ }; -- cgit v1.2.3 From 48a9db462d99494583dad829969616ac90a8df4e Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 3 Jul 2013 15:05:06 -0700 Subject: drivers/dma: remove unused support for MEMSET operations There have never been any real users of MEMSET operations since they have been introduced in January 2007 by commit 7405f74badf4 ("dmaengine: refactor dmaengine around dma_async_tx_descriptor"). Therefore remove support for them for now, it can be always brought back when needed. [sebastian.hesselbarth@gmail.com: fix drivers/dma/mv_xor] Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Kyungmin Park Signed-off-by: Sebastian Hesselbarth Cc: Vinod Koul Acked-by: Dan Williams Cc: Tomasz Figa Cc: Herbert Xu Cc: Olof Johansson Cc: Kevin Hilman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/crypto/async-tx-api.txt | 1 - arch/arm/mach-iop13xx/setup.c | 3 - arch/arm/plat-iop/adma.c | 2 - arch/arm/plat-orion/common.c | 10 --- crypto/async_tx/Kconfig | 4 -- crypto/async_tx/Makefile | 1 - crypto/async_tx/async_memset.c | 89 -------------------------- drivers/dma/dmaengine.c | 7 --- drivers/dma/ioat/dma.c | 3 +- drivers/dma/ioat/dma_v2.h | 1 - drivers/dma/ioat/dma_v3.c | 114 +--------------------------------- drivers/dma/ioat/hw.h | 27 -------- drivers/dma/iop-adma.c | 66 +------------------- drivers/dma/mv_xor.c | 85 ++----------------------- drivers/dma/mv_xor.h | 1 - drivers/dma/ppc4xx/adma.c | 47 -------------- include/linux/async_tx.h | 4 -- include/linux/dmaengine.h | 5 -- 18 files changed, 8 insertions(+), 462 deletions(-) delete mode 100644 crypto/async_tx/async_memset.c diff --git a/Documentation/crypto/async-tx-api.txt b/Documentation/crypto/async-tx-api.txt index ba046b8fa92f..7bf1be20d93a 100644 --- a/Documentation/crypto/async-tx-api.txt +++ b/Documentation/crypto/async-tx-api.txt @@ -222,5 +222,4 @@ drivers/dma/: location for offload engine drivers include/linux/async_tx.h: core header file for the async_tx api crypto/async_tx/async_tx.c: async_tx interface to dmaengine and common code crypto/async_tx/async_memcpy.c: copy offload -crypto/async_tx/async_memset.c: memory fill offload crypto/async_tx/async_xor.c: xor and xor zero sum offload diff --git a/arch/arm/mach-iop13xx/setup.c b/arch/arm/mach-iop13xx/setup.c index 3181f61ea63e..1c5bd7637b05 100644 --- a/arch/arm/mach-iop13xx/setup.c +++ b/arch/arm/mach-iop13xx/setup.c @@ -469,7 +469,6 @@ void __init iop13xx_platform_init(void) dma_cap_set(DMA_MEMCPY, plat_data->cap_mask); dma_cap_set(DMA_XOR, plat_data->cap_mask); dma_cap_set(DMA_XOR_VAL, plat_data->cap_mask); - dma_cap_set(DMA_MEMSET, plat_data->cap_mask); dma_cap_set(DMA_INTERRUPT, plat_data->cap_mask); break; case IOP13XX_INIT_ADMA_1: @@ -479,7 +478,6 @@ void __init iop13xx_platform_init(void) dma_cap_set(DMA_MEMCPY, plat_data->cap_mask); dma_cap_set(DMA_XOR, plat_data->cap_mask); dma_cap_set(DMA_XOR_VAL, plat_data->cap_mask); - dma_cap_set(DMA_MEMSET, plat_data->cap_mask); dma_cap_set(DMA_INTERRUPT, plat_data->cap_mask); break; case IOP13XX_INIT_ADMA_2: @@ -489,7 +487,6 @@ void __init iop13xx_platform_init(void) dma_cap_set(DMA_MEMCPY, plat_data->cap_mask); dma_cap_set(DMA_XOR, plat_data->cap_mask); dma_cap_set(DMA_XOR_VAL, plat_data->cap_mask); - dma_cap_set(DMA_MEMSET, plat_data->cap_mask); dma_cap_set(DMA_INTERRUPT, plat_data->cap_mask); dma_cap_set(DMA_PQ, plat_data->cap_mask); dma_cap_set(DMA_PQ_VAL, plat_data->cap_mask); diff --git a/arch/arm/plat-iop/adma.c b/arch/arm/plat-iop/adma.c index 1ff6a37e893c..a4d1f8de3b5b 100644 --- a/arch/arm/plat-iop/adma.c +++ b/arch/arm/plat-iop/adma.c @@ -192,12 +192,10 @@ static int __init iop3xx_adma_cap_init(void) #ifdef CONFIG_ARCH_IOP32X /* the 32x AAU does not perform zero sum */ dma_cap_set(DMA_XOR, iop3xx_aau_data.cap_mask); - dma_cap_set(DMA_MEMSET, iop3xx_aau_data.cap_mask); dma_cap_set(DMA_INTERRUPT, iop3xx_aau_data.cap_mask); #else dma_cap_set(DMA_XOR, iop3xx_aau_data.cap_mask); dma_cap_set(DMA_XOR_VAL, iop3xx_aau_data.cap_mask); - dma_cap_set(DMA_MEMSET, iop3xx_aau_data.cap_mask); dma_cap_set(DMA_INTERRUPT, iop3xx_aau_data.cap_mask); #endif diff --git a/arch/arm/plat-orion/common.c b/arch/arm/plat-orion/common.c index c019b7aaf776..c66d163d7a2a 100644 --- a/arch/arm/plat-orion/common.c +++ b/arch/arm/plat-orion/common.c @@ -666,14 +666,9 @@ void __init orion_xor0_init(unsigned long mapbase_low, orion_xor0_shared_resources[3].start = irq_1; orion_xor0_shared_resources[3].end = irq_1; - /* - * two engines can't do memset simultaneously, this limitation - * satisfied by removing memset support from one of the engines. - */ dma_cap_set(DMA_MEMCPY, orion_xor0_channels_data[0].cap_mask); dma_cap_set(DMA_XOR, orion_xor0_channels_data[0].cap_mask); - dma_cap_set(DMA_MEMSET, orion_xor0_channels_data[1].cap_mask); dma_cap_set(DMA_MEMCPY, orion_xor0_channels_data[1].cap_mask); dma_cap_set(DMA_XOR, orion_xor0_channels_data[1].cap_mask); @@ -732,14 +727,9 @@ void __init orion_xor1_init(unsigned long mapbase_low, orion_xor1_shared_resources[3].start = irq_1; orion_xor1_shared_resources[3].end = irq_1; - /* - * two engines can't do memset simultaneously, this limitation - * satisfied by removing memset support from one of the engines. - */ dma_cap_set(DMA_MEMCPY, orion_xor1_channels_data[0].cap_mask); dma_cap_set(DMA_XOR, orion_xor1_channels_data[0].cap_mask); - dma_cap_set(DMA_MEMSET, orion_xor1_channels_data[1].cap_mask); dma_cap_set(DMA_MEMCPY, orion_xor1_channels_data[1].cap_mask); dma_cap_set(DMA_XOR, orion_xor1_channels_data[1].cap_mask); diff --git a/crypto/async_tx/Kconfig b/crypto/async_tx/Kconfig index 1b11abbb5c91..f38a58aef3ec 100644 --- a/crypto/async_tx/Kconfig +++ b/crypto/async_tx/Kconfig @@ -10,10 +10,6 @@ config ASYNC_XOR select ASYNC_CORE select XOR_BLOCKS -config ASYNC_MEMSET - tristate - select ASYNC_CORE - config ASYNC_PQ tristate select ASYNC_CORE diff --git a/crypto/async_tx/Makefile b/crypto/async_tx/Makefile index d1e0e6f72bc1..462e4abbfe69 100644 --- a/crypto/async_tx/Makefile +++ b/crypto/async_tx/Makefile @@ -1,6 +1,5 @@ obj-$(CONFIG_ASYNC_CORE) += async_tx.o obj-$(CONFIG_ASYNC_MEMCPY) += async_memcpy.o -obj-$(CONFIG_ASYNC_MEMSET) += async_memset.o obj-$(CONFIG_ASYNC_XOR) += async_xor.o obj-$(CONFIG_ASYNC_PQ) += async_pq.o obj-$(CONFIG_ASYNC_RAID6_RECOV) += async_raid6_recov.o diff --git a/crypto/async_tx/async_memset.c b/crypto/async_tx/async_memset.c deleted file mode 100644 index 05a4d1e00148..000000000000 --- a/crypto/async_tx/async_memset.c +++ /dev/null @@ -1,89 +0,0 @@ -/* - * memory fill offload engine support - * - * Copyright © 2006, Intel Corporation. - * - * Dan Williams - * - * with architecture considerations by: - * Neil Brown - * Jeff Garzik - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - */ -#include -#include -#include -#include -#include -#include - -/** - * async_memset - attempt to fill memory with a dma engine. - * @dest: destination page - * @val: fill value - * @offset: offset in pages to start transaction - * @len: length in bytes - * - * honored flags: ASYNC_TX_ACK - */ -struct dma_async_tx_descriptor * -async_memset(struct page *dest, int val, unsigned int offset, size_t len, - struct async_submit_ctl *submit) -{ - struct dma_chan *chan = async_tx_find_channel(submit, DMA_MEMSET, - &dest, 1, NULL, 0, len); - struct dma_device *device = chan ? chan->device : NULL; - struct dma_async_tx_descriptor *tx = NULL; - - if (device && is_dma_fill_aligned(device, offset, 0, len)) { - dma_addr_t dma_dest; - unsigned long dma_prep_flags = 0; - - if (submit->cb_fn) - dma_prep_flags |= DMA_PREP_INTERRUPT; - if (submit->flags & ASYNC_TX_FENCE) - dma_prep_flags |= DMA_PREP_FENCE; - dma_dest = dma_map_page(device->dev, dest, offset, len, - DMA_FROM_DEVICE); - - tx = device->device_prep_dma_memset(chan, dma_dest, val, len, - dma_prep_flags); - } - - if (tx) { - pr_debug("%s: (async) len: %zu\n", __func__, len); - async_tx_submit(chan, tx, submit); - } else { /* run the memset synchronously */ - void *dest_buf; - pr_debug("%s: (sync) len: %zu\n", __func__, len); - - dest_buf = page_address(dest) + offset; - - /* wait for any prerequisite operations */ - async_tx_quiesce(&submit->depend_tx); - - memset(dest_buf, val, len); - - async_tx_sync_epilog(submit); - } - - return tx; -} -EXPORT_SYMBOL_GPL(async_memset); - -MODULE_AUTHOR("Intel Corporation"); -MODULE_DESCRIPTION("asynchronous memset api"); -MODULE_LICENSE("GPL"); diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 93f7992bee5c..9e56745f87bf 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -663,11 +663,6 @@ static bool device_has_all_tx_types(struct dma_device *device) return false; #endif - #if defined(CONFIG_ASYNC_MEMSET) || defined(CONFIG_ASYNC_MEMSET_MODULE) - if (!dma_has_cap(DMA_MEMSET, device->cap_mask)) - return false; - #endif - #if defined(CONFIG_ASYNC_XOR) || defined(CONFIG_ASYNC_XOR_MODULE) if (!dma_has_cap(DMA_XOR, device->cap_mask)) return false; @@ -729,8 +724,6 @@ int dma_async_device_register(struct dma_device *device) !device->device_prep_dma_pq); BUG_ON(dma_has_cap(DMA_PQ_VAL, device->cap_mask) && !device->device_prep_dma_pq_val); - BUG_ON(dma_has_cap(DMA_MEMSET, device->cap_mask) && - !device->device_prep_dma_memset); BUG_ON(dma_has_cap(DMA_INTERRUPT, device->cap_mask) && !device->device_prep_dma_interrupt); BUG_ON(dma_has_cap(DMA_SG, device->cap_mask) && diff --git a/drivers/dma/ioat/dma.c b/drivers/dma/ioat/dma.c index 17a2393b3e25..5ff6fc1819dc 100644 --- a/drivers/dma/ioat/dma.c +++ b/drivers/dma/ioat/dma.c @@ -1105,12 +1105,11 @@ static ssize_t cap_show(struct dma_chan *c, char *page) { struct dma_device *dma = c->device; - return sprintf(page, "copy%s%s%s%s%s%s\n", + return sprintf(page, "copy%s%s%s%s%s\n", dma_has_cap(DMA_PQ, dma->cap_mask) ? " pq" : "", dma_has_cap(DMA_PQ_VAL, dma->cap_mask) ? " pq_val" : "", dma_has_cap(DMA_XOR, dma->cap_mask) ? " xor" : "", dma_has_cap(DMA_XOR_VAL, dma->cap_mask) ? " xor_val" : "", - dma_has_cap(DMA_MEMSET, dma->cap_mask) ? " fill" : "", dma_has_cap(DMA_INTERRUPT, dma->cap_mask) ? " intr" : ""); } diff --git a/drivers/dma/ioat/dma_v2.h b/drivers/dma/ioat/dma_v2.h index 29bf9448035d..212d584fe427 100644 --- a/drivers/dma/ioat/dma_v2.h +++ b/drivers/dma/ioat/dma_v2.h @@ -123,7 +123,6 @@ static inline u16 ioat2_xferlen_to_descs(struct ioat2_dma_chan *ioat, size_t len struct ioat_ring_ent { union { struct ioat_dma_descriptor *hw; - struct ioat_fill_descriptor *fill; struct ioat_xor_descriptor *xor; struct ioat_xor_ext_descriptor *xor_ex; struct ioat_pq_descriptor *pq; diff --git a/drivers/dma/ioat/dma_v3.c b/drivers/dma/ioat/dma_v3.c index ca6ea9b3551b..b642e035579b 100644 --- a/drivers/dma/ioat/dma_v3.c +++ b/drivers/dma/ioat/dma_v3.c @@ -311,14 +311,6 @@ static void ioat3_dma_unmap(struct ioat2_dma_chan *ioat, if (!desc->hw->ctl_f.null) /* skip 'interrupt' ops */ ioat_dma_unmap(chan, flags, len, desc->hw); break; - case IOAT_OP_FILL: { - struct ioat_fill_descriptor *hw = desc->fill; - - if (!(flags & DMA_COMPL_SKIP_DEST_UNMAP)) - ioat_unmap(pdev, hw->dst_addr - offset, len, - PCI_DMA_FROMDEVICE, flags, 1); - break; - } case IOAT_OP_XOR_VAL: case IOAT_OP_XOR: { struct ioat_xor_descriptor *xor = desc->xor; @@ -823,51 +815,6 @@ ioat3_tx_status(struct dma_chan *c, dma_cookie_t cookie, return dma_cookie_status(c, cookie, txstate); } -static struct dma_async_tx_descriptor * -ioat3_prep_memset_lock(struct dma_chan *c, dma_addr_t dest, int value, - size_t len, unsigned long flags) -{ - struct ioat2_dma_chan *ioat = to_ioat2_chan(c); - struct ioat_ring_ent *desc; - size_t total_len = len; - struct ioat_fill_descriptor *fill; - u64 src_data = (0x0101010101010101ULL) * (value & 0xff); - int num_descs, idx, i; - - num_descs = ioat2_xferlen_to_descs(ioat, len); - if (likely(num_descs) && ioat2_check_space_lock(ioat, num_descs) == 0) - idx = ioat->head; - else - return NULL; - i = 0; - do { - size_t xfer_size = min_t(size_t, len, 1 << ioat->xfercap_log); - - desc = ioat2_get_ring_ent(ioat, idx + i); - fill = desc->fill; - - fill->size = xfer_size; - fill->src_data = src_data; - fill->dst_addr = dest; - fill->ctl = 0; - fill->ctl_f.op = IOAT_OP_FILL; - - len -= xfer_size; - dest += xfer_size; - dump_desc_dbg(ioat, desc); - } while (++i < num_descs); - - desc->txd.flags = flags; - desc->len = total_len; - fill->ctl_f.int_en = !!(flags & DMA_PREP_INTERRUPT); - fill->ctl_f.fence = !!(flags & DMA_PREP_FENCE); - fill->ctl_f.compl_write = 1; - dump_desc_dbg(ioat, desc); - - /* we leave the channel locked to ensure in order submission */ - return &desc->txd; -} - static struct dma_async_tx_descriptor * __ioat3_prep_xor_lock(struct dma_chan *c, enum sum_check_flags *result, dma_addr_t dest, dma_addr_t *src, unsigned int src_cnt, @@ -1431,7 +1378,7 @@ static int ioat_xor_val_self_test(struct ioatdma_device *device) struct page *xor_srcs[IOAT_NUM_SRC_TEST]; struct page *xor_val_srcs[IOAT_NUM_SRC_TEST + 1]; dma_addr_t dma_srcs[IOAT_NUM_SRC_TEST + 1]; - dma_addr_t dma_addr, dest_dma; + dma_addr_t dest_dma; struct dma_async_tx_descriptor *tx; struct dma_chan *dma_chan; dma_cookie_t cookie; @@ -1598,56 +1545,6 @@ static int ioat_xor_val_self_test(struct ioatdma_device *device) goto free_resources; } - /* skip memset if the capability is not present */ - if (!dma_has_cap(DMA_MEMSET, dma_chan->device->cap_mask)) - goto free_resources; - - /* test memset */ - op = IOAT_OP_FILL; - - dma_addr = dma_map_page(dev, dest, 0, - PAGE_SIZE, DMA_FROM_DEVICE); - tx = dma->device_prep_dma_memset(dma_chan, dma_addr, 0, PAGE_SIZE, - DMA_PREP_INTERRUPT | - DMA_COMPL_SKIP_SRC_UNMAP | - DMA_COMPL_SKIP_DEST_UNMAP); - if (!tx) { - dev_err(dev, "Self-test memset prep failed\n"); - err = -ENODEV; - goto dma_unmap; - } - - async_tx_ack(tx); - init_completion(&cmp); - tx->callback = ioat3_dma_test_callback; - tx->callback_param = &cmp; - cookie = tx->tx_submit(tx); - if (cookie < 0) { - dev_err(dev, "Self-test memset setup failed\n"); - err = -ENODEV; - goto dma_unmap; - } - dma->device_issue_pending(dma_chan); - - tmo = wait_for_completion_timeout(&cmp, msecs_to_jiffies(3000)); - - if (dma->device_tx_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { - dev_err(dev, "Self-test memset timed out\n"); - err = -ENODEV; - goto dma_unmap; - } - - dma_unmap_page(dev, dma_addr, PAGE_SIZE, DMA_FROM_DEVICE); - - for (i = 0; i < PAGE_SIZE/sizeof(u32); i++) { - u32 *ptr = page_address(dest); - if (ptr[i]) { - dev_err(dev, "Self-test memset failed compare\n"); - err = -ENODEV; - goto free_resources; - } - } - /* test for non-zero parity sum */ op = IOAT_OP_XOR_VAL; @@ -1706,8 +1603,7 @@ dma_unmap: for (i = 0; i < IOAT_NUM_SRC_TEST + 1; i++) dma_unmap_page(dev, dma_srcs[i], PAGE_SIZE, DMA_TO_DEVICE); - } else if (op == IOAT_OP_FILL) - dma_unmap_page(dev, dma_addr, PAGE_SIZE, DMA_FROM_DEVICE); + } free_resources: dma->device_free_chan_resources(dma_chan); out: @@ -1944,12 +1840,6 @@ int ioat3_dma_probe(struct ioatdma_device *device, int dca) } } - if (is_raid_device && (device->cap & IOAT_CAP_FILL_BLOCK)) { - dma_cap_set(DMA_MEMSET, dma->cap_mask); - dma->device_prep_dma_memset = ioat3_prep_memset_lock; - } - - dma->device_tx_status = ioat3_tx_status; device->cleanup_fn = ioat3_cleanup_event; device->timer_fn = ioat3_timer_event; diff --git a/drivers/dma/ioat/hw.h b/drivers/dma/ioat/hw.h index 5ee57d402a6e..62f83e983d8d 100644 --- a/drivers/dma/ioat/hw.h +++ b/drivers/dma/ioat/hw.h @@ -100,33 +100,6 @@ struct ioat_dma_descriptor { uint64_t user2; }; -struct ioat_fill_descriptor { - uint32_t size; - union { - uint32_t ctl; - struct { - unsigned int int_en:1; - unsigned int rsvd:1; - unsigned int dest_snoop_dis:1; - unsigned int compl_write:1; - unsigned int fence:1; - unsigned int rsvd2:2; - unsigned int dest_brk:1; - unsigned int bundle:1; - unsigned int rsvd4:15; - #define IOAT_OP_FILL 0x01 - unsigned int op:8; - } ctl_f; - }; - uint64_t src_data; - uint64_t dst_addr; - uint64_t next; - uint64_t rsv1; - uint64_t next_dst_addr; - uint64_t user1; - uint64_t user2; -}; - struct ioat_xor_descriptor { uint32_t size; union { diff --git a/drivers/dma/iop-adma.c b/drivers/dma/iop-adma.c index 7dafb9f3785f..c9cc08c2dbba 100644 --- a/drivers/dma/iop-adma.c +++ b/drivers/dma/iop-adma.c @@ -632,39 +632,6 @@ iop_adma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dma_dest, return sw_desc ? &sw_desc->async_tx : NULL; } -static struct dma_async_tx_descriptor * -iop_adma_prep_dma_memset(struct dma_chan *chan, dma_addr_t dma_dest, - int value, size_t len, unsigned long flags) -{ - struct iop_adma_chan *iop_chan = to_iop_adma_chan(chan); - struct iop_adma_desc_slot *sw_desc, *grp_start; - int slot_cnt, slots_per_op; - - if (unlikely(!len)) - return NULL; - BUG_ON(len > IOP_ADMA_MAX_BYTE_COUNT); - - dev_dbg(iop_chan->device->common.dev, "%s len: %u\n", - __func__, len); - - spin_lock_bh(&iop_chan->lock); - slot_cnt = iop_chan_memset_slot_count(len, &slots_per_op); - sw_desc = iop_adma_alloc_slots(iop_chan, slot_cnt, slots_per_op); - if (sw_desc) { - grp_start = sw_desc->group_head; - iop_desc_init_memset(grp_start, flags); - iop_desc_set_byte_count(grp_start, iop_chan, len); - iop_desc_set_block_fill_val(grp_start, value); - iop_desc_set_dest_addr(grp_start, iop_chan, dma_dest); - sw_desc->unmap_src_cnt = 1; - sw_desc->unmap_len = len; - sw_desc->async_tx.flags = flags; - } - spin_unlock_bh(&iop_chan->lock); - - return sw_desc ? &sw_desc->async_tx : NULL; -} - static struct dma_async_tx_descriptor * iop_adma_prep_dma_xor(struct dma_chan *chan, dma_addr_t dma_dest, dma_addr_t *dma_src, unsigned int src_cnt, size_t len, @@ -1176,33 +1143,6 @@ iop_adma_xor_val_self_test(struct iop_adma_device *device) goto free_resources; } - /* test memset */ - dma_addr = dma_map_page(dma_chan->device->dev, dest, 0, - PAGE_SIZE, DMA_FROM_DEVICE); - tx = iop_adma_prep_dma_memset(dma_chan, dma_addr, 0, PAGE_SIZE, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - - cookie = iop_adma_tx_submit(tx); - iop_adma_issue_pending(dma_chan); - msleep(8); - - if (iop_adma_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { - dev_err(dma_chan->device->dev, - "Self-test memset timed out, disabling\n"); - err = -ENODEV; - goto free_resources; - } - - for (i = 0; i < PAGE_SIZE/sizeof(u32); i++) { - u32 *ptr = page_address(dest); - if (ptr[i]) { - dev_err(dma_chan->device->dev, - "Self-test memset failed compare, disabling\n"); - err = -ENODEV; - goto free_resources; - } - } - /* test for non-zero parity sum */ zero_sum_result = 0; for (i = 0; i < IOP_ADMA_NUM_SRC_TEST + 1; i++) @@ -1487,8 +1427,6 @@ static int iop_adma_probe(struct platform_device *pdev) /* set prep routines based on capability */ if (dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask)) dma_dev->device_prep_dma_memcpy = iop_adma_prep_dma_memcpy; - if (dma_has_cap(DMA_MEMSET, dma_dev->cap_mask)) - dma_dev->device_prep_dma_memset = iop_adma_prep_dma_memset; if (dma_has_cap(DMA_XOR, dma_dev->cap_mask)) { dma_dev->max_xor = iop_adma_get_max_xor(); dma_dev->device_prep_dma_xor = iop_adma_prep_dma_xor; @@ -1556,8 +1494,7 @@ static int iop_adma_probe(struct platform_device *pdev) goto err_free_iop_chan; } - if (dma_has_cap(DMA_XOR, dma_dev->cap_mask) || - dma_has_cap(DMA_MEMSET, dma_dev->cap_mask)) { + if (dma_has_cap(DMA_XOR, dma_dev->cap_mask)) { ret = iop_adma_xor_val_self_test(adev); dev_dbg(&pdev->dev, "xor self test returned %d\n", ret); if (ret) @@ -1584,7 +1521,6 @@ static int iop_adma_probe(struct platform_device *pdev) dma_has_cap(DMA_PQ_VAL, dma_dev->cap_mask) ? "pq_val " : "", dma_has_cap(DMA_XOR, dma_dev->cap_mask) ? "xor " : "", dma_has_cap(DMA_XOR_VAL, dma_dev->cap_mask) ? "xor_val " : "", - dma_has_cap(DMA_MEMSET, dma_dev->cap_mask) ? "fill " : "", dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask) ? "cpy " : "", dma_has_cap(DMA_INTERRUPT, dma_dev->cap_mask) ? "intr " : ""); diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c index d64ae14f2706..200f1a3c9a44 100644 --- a/drivers/dma/mv_xor.c +++ b/drivers/dma/mv_xor.c @@ -89,11 +89,6 @@ static void mv_desc_clear_next_desc(struct mv_xor_desc_slot *desc) hw_desc->phy_next_desc = 0; } -static void mv_desc_set_block_fill_val(struct mv_xor_desc_slot *desc, u32 val) -{ - desc->value = val; -} - static void mv_desc_set_dest_addr(struct mv_xor_desc_slot *desc, dma_addr_t addr) { @@ -128,22 +123,6 @@ static void mv_chan_set_next_descriptor(struct mv_xor_chan *chan, __raw_writel(next_desc_addr, XOR_NEXT_DESC(chan)); } -static void mv_chan_set_dest_pointer(struct mv_xor_chan *chan, u32 desc_addr) -{ - __raw_writel(desc_addr, XOR_DEST_POINTER(chan)); -} - -static void mv_chan_set_block_size(struct mv_xor_chan *chan, u32 block_size) -{ - __raw_writel(block_size, XOR_BLOCK_SIZE(chan)); -} - -static void mv_chan_set_value(struct mv_xor_chan *chan, u32 value) -{ - __raw_writel(value, XOR_INIT_VALUE_LOW(chan)); - __raw_writel(value, XOR_INIT_VALUE_HIGH(chan)); -} - static void mv_chan_unmask_interrupts(struct mv_xor_chan *chan) { u32 val = __raw_readl(XOR_INTR_MASK(chan)); @@ -186,8 +165,6 @@ static int mv_can_chain(struct mv_xor_desc_slot *desc) if (chain_old_tail->type != desc->type) return 0; - if (desc->type == DMA_MEMSET) - return 0; return 1; } @@ -205,9 +182,6 @@ static void mv_set_mode(struct mv_xor_chan *chan, case DMA_MEMCPY: op_mode = XOR_OPERATION_MODE_MEMCPY; break; - case DMA_MEMSET: - op_mode = XOR_OPERATION_MODE_MEMSET; - break; default: dev_err(mv_chan_to_devp(chan), "error: unsupported operation %d\n", @@ -274,18 +248,9 @@ static void mv_xor_start_new_chain(struct mv_xor_chan *mv_chan, if (sw_desc->type != mv_chan->current_type) mv_set_mode(mv_chan, sw_desc->type); - if (sw_desc->type == DMA_MEMSET) { - /* for memset requests we need to program the engine, no - * descriptors used. - */ - struct mv_xor_desc *hw_desc = sw_desc->hw_desc; - mv_chan_set_dest_pointer(mv_chan, hw_desc->phy_dest_addr); - mv_chan_set_block_size(mv_chan, sw_desc->unmap_len); - mv_chan_set_value(mv_chan, sw_desc->value); - } else { - /* set the hardware chain */ - mv_chan_set_next_descriptor(mv_chan, sw_desc->async_tx.phys); - } + /* set the hardware chain */ + mv_chan_set_next_descriptor(mv_chan, sw_desc->async_tx.phys); + mv_chan->pending += sw_desc->slot_cnt; mv_xor_issue_pending(&mv_chan->dmachan); } @@ -687,43 +652,6 @@ mv_xor_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, return sw_desc ? &sw_desc->async_tx : NULL; } -static struct dma_async_tx_descriptor * -mv_xor_prep_dma_memset(struct dma_chan *chan, dma_addr_t dest, int value, - size_t len, unsigned long flags) -{ - struct mv_xor_chan *mv_chan = to_mv_xor_chan(chan); - struct mv_xor_desc_slot *sw_desc, *grp_start; - int slot_cnt; - - dev_dbg(mv_chan_to_devp(mv_chan), - "%s dest: %x len: %u flags: %ld\n", - __func__, dest, len, flags); - if (unlikely(len < MV_XOR_MIN_BYTE_COUNT)) - return NULL; - - BUG_ON(len > MV_XOR_MAX_BYTE_COUNT); - - spin_lock_bh(&mv_chan->lock); - slot_cnt = mv_chan_memset_slot_count(len); - sw_desc = mv_xor_alloc_slots(mv_chan, slot_cnt, 1); - if (sw_desc) { - sw_desc->type = DMA_MEMSET; - sw_desc->async_tx.flags = flags; - grp_start = sw_desc->group_head; - mv_desc_init(grp_start, flags); - mv_desc_set_byte_count(grp_start, len); - mv_desc_set_dest_addr(sw_desc->group_head, dest); - mv_desc_set_block_fill_val(grp_start, value); - sw_desc->unmap_src_cnt = 1; - sw_desc->unmap_len = len; - } - spin_unlock_bh(&mv_chan->lock); - dev_dbg(mv_chan_to_devp(mv_chan), - "%s sw_desc %p async_tx %p \n", - __func__, sw_desc, &sw_desc->async_tx); - return sw_desc ? &sw_desc->async_tx : NULL; -} - static struct dma_async_tx_descriptor * mv_xor_prep_dma_xor(struct dma_chan *chan, dma_addr_t dest, dma_addr_t *src, unsigned int src_cnt, size_t len, unsigned long flags) @@ -1137,8 +1065,6 @@ mv_xor_channel_add(struct mv_xor_device *xordev, /* set prep routines based on capability */ if (dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask)) dma_dev->device_prep_dma_memcpy = mv_xor_prep_dma_memcpy; - if (dma_has_cap(DMA_MEMSET, dma_dev->cap_mask)) - dma_dev->device_prep_dma_memset = mv_xor_prep_dma_memset; if (dma_has_cap(DMA_XOR, dma_dev->cap_mask)) { dma_dev->max_xor = 8; dma_dev->device_prep_dma_xor = mv_xor_prep_dma_xor; @@ -1187,9 +1113,8 @@ mv_xor_channel_add(struct mv_xor_device *xordev, goto err_free_irq; } - dev_info(&pdev->dev, "Marvell XOR: ( %s%s%s%s)\n", + dev_info(&pdev->dev, "Marvell XOR: ( %s%s%s)\n", dma_has_cap(DMA_XOR, dma_dev->cap_mask) ? "xor " : "", - dma_has_cap(DMA_MEMSET, dma_dev->cap_mask) ? "fill " : "", dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask) ? "cpy " : "", dma_has_cap(DMA_INTERRUPT, dma_dev->cap_mask) ? "intr " : ""); @@ -1298,8 +1223,6 @@ static int mv_xor_probe(struct platform_device *pdev) dma_cap_set(DMA_MEMCPY, cap_mask); if (of_property_read_bool(np, "dmacap,xor")) dma_cap_set(DMA_XOR, cap_mask); - if (of_property_read_bool(np, "dmacap,memset")) - dma_cap_set(DMA_MEMSET, cap_mask); if (of_property_read_bool(np, "dmacap,interrupt")) dma_cap_set(DMA_INTERRUPT, cap_mask); diff --git a/drivers/dma/mv_xor.h b/drivers/dma/mv_xor.h index c632a4761fcf..c619359cb7fe 100644 --- a/drivers/dma/mv_xor.h +++ b/drivers/dma/mv_xor.h @@ -31,7 +31,6 @@ #define XOR_OPERATION_MODE_XOR 0 #define XOR_OPERATION_MODE_MEMCPY 2 -#define XOR_OPERATION_MODE_MEMSET 4 #define XOR_CURR_DESC(chan) (chan->mmr_base + 0x210 + (chan->idx * 4)) #define XOR_NEXT_DESC(chan) (chan->mmr_base + 0x200 + (chan->idx * 4)) diff --git a/drivers/dma/ppc4xx/adma.c b/drivers/dma/ppc4xx/adma.c index 5d3d95569a1e..1e220f8dfd8c 100644 --- a/drivers/dma/ppc4xx/adma.c +++ b/drivers/dma/ppc4xx/adma.c @@ -2322,47 +2322,6 @@ static struct dma_async_tx_descriptor *ppc440spe_adma_prep_dma_memcpy( return sw_desc ? &sw_desc->async_tx : NULL; } -/** - * ppc440spe_adma_prep_dma_memset - prepare CDB for a MEMSET operation - */ -static struct dma_async_tx_descriptor *ppc440spe_adma_prep_dma_memset( - struct dma_chan *chan, dma_addr_t dma_dest, int value, - size_t len, unsigned long flags) -{ - struct ppc440spe_adma_chan *ppc440spe_chan; - struct ppc440spe_adma_desc_slot *sw_desc, *group_start; - int slot_cnt, slots_per_op; - - ppc440spe_chan = to_ppc440spe_adma_chan(chan); - - if (unlikely(!len)) - return NULL; - - BUG_ON(len > PPC440SPE_ADMA_DMA_MAX_BYTE_COUNT); - - spin_lock_bh(&ppc440spe_chan->lock); - - dev_dbg(ppc440spe_chan->device->common.dev, - "ppc440spe adma%d: %s cal: %u len: %u int_en %d\n", - ppc440spe_chan->device->id, __func__, value, len, - flags & DMA_PREP_INTERRUPT ? 1 : 0); - - slot_cnt = slots_per_op = 1; - sw_desc = ppc440spe_adma_alloc_slots(ppc440spe_chan, slot_cnt, - slots_per_op); - if (sw_desc) { - group_start = sw_desc->group_head; - ppc440spe_desc_init_memset(group_start, value, flags); - ppc440spe_adma_set_dest(group_start, dma_dest, 0); - ppc440spe_desc_set_byte_count(group_start, ppc440spe_chan, len); - sw_desc->unmap_len = len; - sw_desc->async_tx.flags = flags; - } - spin_unlock_bh(&ppc440spe_chan->lock); - - return sw_desc ? &sw_desc->async_tx : NULL; -} - /** * ppc440spe_adma_prep_dma_xor - prepare CDB for a XOR operation */ @@ -4125,7 +4084,6 @@ static void ppc440spe_adma_init_capabilities(struct ppc440spe_adma_device *adev) case PPC440SPE_DMA1_ID: dma_cap_set(DMA_MEMCPY, adev->common.cap_mask); dma_cap_set(DMA_INTERRUPT, adev->common.cap_mask); - dma_cap_set(DMA_MEMSET, adev->common.cap_mask); dma_cap_set(DMA_PQ, adev->common.cap_mask); dma_cap_set(DMA_PQ_VAL, adev->common.cap_mask); dma_cap_set(DMA_XOR_VAL, adev->common.cap_mask); @@ -4151,10 +4109,6 @@ static void ppc440spe_adma_init_capabilities(struct ppc440spe_adma_device *adev) adev->common.device_prep_dma_memcpy = ppc440spe_adma_prep_dma_memcpy; } - if (dma_has_cap(DMA_MEMSET, adev->common.cap_mask)) { - adev->common.device_prep_dma_memset = - ppc440spe_adma_prep_dma_memset; - } if (dma_has_cap(DMA_XOR, adev->common.cap_mask)) { adev->common.max_xor = XOR_MAX_OPS; adev->common.device_prep_dma_xor = @@ -4217,7 +4171,6 @@ static void ppc440spe_adma_init_capabilities(struct ppc440spe_adma_device *adev) dma_has_cap(DMA_XOR, adev->common.cap_mask) ? "xor " : "", dma_has_cap(DMA_XOR_VAL, adev->common.cap_mask) ? "xor_val " : "", dma_has_cap(DMA_MEMCPY, adev->common.cap_mask) ? "memcpy " : "", - dma_has_cap(DMA_MEMSET, adev->common.cap_mask) ? "memset " : "", dma_has_cap(DMA_INTERRUPT, adev->common.cap_mask) ? "intr " : ""); } diff --git a/include/linux/async_tx.h b/include/linux/async_tx.h index a1c486a88e88..179b38ffd351 100644 --- a/include/linux/async_tx.h +++ b/include/linux/async_tx.h @@ -182,10 +182,6 @@ async_memcpy(struct page *dest, struct page *src, unsigned int dest_offset, unsigned int src_offset, size_t len, struct async_submit_ctl *submit); -struct dma_async_tx_descriptor * -async_memset(struct page *dest, int val, unsigned int offset, - size_t len, struct async_submit_ctl *submit); - struct dma_async_tx_descriptor *async_trigger_callback(struct async_submit_ctl *submit); struct dma_async_tx_descriptor * diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 96d3e4ab11a9..cb286b1acdb6 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -66,7 +66,6 @@ enum dma_transaction_type { DMA_PQ, DMA_XOR_VAL, DMA_PQ_VAL, - DMA_MEMSET, DMA_INTERRUPT, DMA_SG, DMA_PRIVATE, @@ -520,7 +519,6 @@ struct dma_tx_state { * @device_prep_dma_xor_val: prepares a xor validation operation * @device_prep_dma_pq: prepares a pq operation * @device_prep_dma_pq_val: prepares a pqzero_sum operation - * @device_prep_dma_memset: prepares a memset operation * @device_prep_dma_interrupt: prepares an end of chain interrupt operation * @device_prep_slave_sg: prepares a slave dma operation * @device_prep_dma_cyclic: prepare a cyclic dma operation suitable for audio. @@ -573,9 +571,6 @@ struct dma_device { struct dma_chan *chan, dma_addr_t *pq, dma_addr_t *src, unsigned int src_cnt, const unsigned char *scf, size_t len, enum sum_check_flags *pqres, unsigned long flags); - struct dma_async_tx_descriptor *(*device_prep_dma_memset)( - struct dma_chan *chan, dma_addr_t dest, int value, size_t len, - unsigned long flags); struct dma_async_tx_descriptor *(*device_prep_dma_interrupt)( struct dma_chan *chan, unsigned long flags); struct dma_async_tx_descriptor *(*device_prep_dma_sg)( -- cgit v1.2.3 From a7d0dabb3e863a1d4018235c1384b4318322116a Mon Sep 17 00:00:00 2001 From: Dimitri Sivanich Date: Wed, 3 Jul 2013 15:05:07 -0700 Subject: drivers/misc/sgi-gru/grufault.c: fix a sanity test in gru_set_context_option() "req.val1 == -1" is valid but it doesn't make sense to check gru_base[-1]. gru_base[] is a global array. Signed-off-by: Dimitri Sivanich Cc: Dan Carpenter Cc: Robin Holt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/misc/sgi-gru/grufault.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/misc/sgi-gru/grufault.c b/drivers/misc/sgi-gru/grufault.c index c4acac74725c..f74fc0ca2ef9 100644 --- a/drivers/misc/sgi-gru/grufault.c +++ b/drivers/misc/sgi-gru/grufault.c @@ -876,8 +876,9 @@ int gru_set_context_option(unsigned long arg) switch (req.op) { case sco_blade_chiplet: /* Select blade/chiplet for GRU context */ - if (req.val1 < -1 || req.val1 >= GRU_MAX_BLADES || !gru_base[req.val1] || - req.val0 < -1 || req.val0 >= GRU_CHIPLETS_PER_HUB) { + if (req.val0 < -1 || req.val0 >= GRU_CHIPLETS_PER_HUB || + req.val1 < -1 || req.val1 >= GRU_MAX_BLADES || + (req.val1 >= 0 && !gru_base[req.val1])) { ret = -EINVAL; } else { gts->ts_user_blade_id = req.val1; -- cgit v1.2.3 From f7269cfc37d547a6e89b26caf30c62557450c196 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 3 Jul 2013 15:05:08 -0700 Subject: MAINTAINERS: fix tape driver file mappings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The masks for the st and osst tape drivers in MAINTAINERS are too broad and include unrelated files. Make the file list accurate so that maintainers of these drivers aren't bothered with unrelated work. Signed-off-by: Jean Delvare Cc: Willem Riede Cc: Kai Mäkisara Cc: "James E.J. Bottomley" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- MAINTAINERS | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 56de3827f5d5..2c64e6dbb4c8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5963,8 +5963,10 @@ M: Willem Riede L: osst-users@lists.sourceforge.net L: linux-scsi@vger.kernel.org S: Maintained -F: drivers/scsi/osst* -F: drivers/scsi/st* +F: Documentation/scsi/osst.txt +F: drivers/scsi/osst.* +F: drivers/scsi/osst_*.h +F: drivers/scsi/st.h OPENCORES I2C BUS DRIVER M: Peter Korsgaard @@ -7112,7 +7114,8 @@ M: Kai Mäkisara L: linux-scsi@vger.kernel.org S: Maintained F: Documentation/scsi/st.txt -F: drivers/scsi/st* +F: drivers/scsi/st.* +F: drivers/scsi/st_*.h SCTP PROTOCOL M: Vlad Yasevich -- cgit v1.2.3 From 29603af12096567299c0004975c1e18217732c31 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:05:09 -0700 Subject: backlight: atmel-pwm-bl: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d06310 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/backlight/atmel-pwm-bl.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/video/backlight/atmel-pwm-bl.c b/drivers/video/backlight/atmel-pwm-bl.c index a60d6afca97c..0393d827dd44 100644 --- a/drivers/video/backlight/atmel-pwm-bl.c +++ b/drivers/video/backlight/atmel-pwm-bl.c @@ -195,7 +195,6 @@ static int __init atmel_pwm_bl_probe(struct platform_device *pdev) return 0; err_free_bl_dev: - platform_set_drvdata(pdev, NULL); backlight_device_unregister(bldev); err_free_pwm: pwm_channel_free(&pwmbl->pwmc); @@ -212,7 +211,6 @@ static int __exit atmel_pwm_bl_remove(struct platform_device *pdev) pwm_channel_disable(&pwmbl->pwmc); pwm_channel_free(&pwmbl->pwmc); backlight_device_unregister(pwmbl->bldev); - platform_set_drvdata(pdev, NULL); return 0; } -- cgit v1.2.3 From 6a7c02f4edd6ce8ef96fc27701fa4eeb3fecc046 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:05:10 -0700 Subject: backlight: ep93xx: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/backlight/ep93xx_bl.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/video/backlight/ep93xx_bl.c b/drivers/video/backlight/ep93xx_bl.c index 33455821dd31..018368ba4124 100644 --- a/drivers/video/backlight/ep93xx_bl.c +++ b/drivers/video/backlight/ep93xx_bl.c @@ -111,7 +111,6 @@ static int ep93xxbl_remove(struct platform_device *dev) struct backlight_device *bl = platform_get_drvdata(dev); backlight_device_unregister(bl); - platform_set_drvdata(dev, NULL); return 0; } -- cgit v1.2.3 From 85f7f220fbcabec5ce76b8dcb065d200989f6a48 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:05:11 -0700 Subject: backlight: lp8788: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/backlight/lp8788_bl.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/video/backlight/lp8788_bl.c b/drivers/video/backlight/lp8788_bl.c index 4bb8b4f140cf..980855ec9bb1 100644 --- a/drivers/video/backlight/lp8788_bl.c +++ b/drivers/video/backlight/lp8788_bl.c @@ -312,7 +312,6 @@ static int lp8788_backlight_remove(struct platform_device *pdev) backlight_update_status(bl_dev); sysfs_remove_group(&pdev->dev.kobj, &lp8788_attr_group); lp8788_backlight_unregister(bl); - platform_set_drvdata(pdev, NULL); return 0; } -- cgit v1.2.3 From 9ed3936fd7e918bfea565f51aa4379967009d7dd Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:05:12 -0700 Subject: backlight: pcf50633: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/backlight/pcf50633-backlight.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/video/backlight/pcf50633-backlight.c b/drivers/video/backlight/pcf50633-backlight.c index e87c7a3394f3..6ed76be18f19 100644 --- a/drivers/video/backlight/pcf50633-backlight.c +++ b/drivers/video/backlight/pcf50633-backlight.c @@ -153,8 +153,6 @@ static int pcf50633_bl_remove(struct platform_device *pdev) backlight_device_unregister(pcf_bl->bl); - platform_set_drvdata(pdev, NULL); - return 0; } -- cgit v1.2.3 From 8318fde4ac78f6793b1cbaf57659902253a61617 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:05:13 -0700 Subject: backlight: add devm_backlight_device_{register,unregister}() These functions allow the driver core to automatically clean up any allocation made by backlight drivers. Thus it simplifies the error paths. Signed-off-by: Jingoo Han Cc: Tejun Heo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/backlight/backlight.c | 75 +++++++++++++++++++++++++++++++++++++ include/linux/backlight.h | 6 +++ 2 files changed, 81 insertions(+) diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c index e3c279083253..53c52fbc9395 100644 --- a/drivers/video/backlight/backlight.c +++ b/drivers/video/backlight/backlight.c @@ -370,6 +370,81 @@ void backlight_device_unregister(struct backlight_device *bd) } EXPORT_SYMBOL(backlight_device_unregister); +static void devm_backlight_device_release(struct device *dev, void *res) +{ + struct backlight_device *backlight = *(struct backlight_device **)res; + + backlight_device_unregister(backlight); +} + +static int devm_backlight_device_match(struct device *dev, void *res, + void *data) +{ + struct backlight_device **r = res; + + return *r == data; +} + +/** + * devm_backlight_device_register - resource managed backlight_device_register() + * @dev: the device to register + * @name: the name of the device + * @parent: a pointer to the parent device + * @devdata: an optional pointer to be stored for private driver use + * @ops: the backlight operations structure + * @props: the backlight properties + * + * @return a struct backlight on success, or an ERR_PTR on error + * + * Managed backlight_device_register(). The backlight_device returned + * from this function are automatically freed on driver detach. + * See backlight_device_register() for more information. + */ +struct backlight_device *devm_backlight_device_register(struct device *dev, + const char *name, struct device *parent, void *devdata, + const struct backlight_ops *ops, + const struct backlight_properties *props) +{ + struct backlight_device **ptr, *backlight; + + ptr = devres_alloc(devm_backlight_device_release, sizeof(*ptr), + GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + backlight = backlight_device_register(name, parent, devdata, ops, + props); + if (!IS_ERR(backlight)) { + *ptr = backlight; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return backlight; +} +EXPORT_SYMBOL(devm_backlight_device_register); + +/** + * devm_backlight_device_unregister - resource managed backlight_device_unregister() + * @dev: the device to unregister + * @bd: the backlight device to unregister + * + * Deallocated a backlight allocated with devm_backlight_device_register(). + * Normally this function will not need to be called and the resource management + * code will ensure that the resource is freed. + */ +void devm_backlight_device_unregister(struct device *dev, + struct backlight_device *bd) +{ + int rc; + + rc = devres_release(dev, devm_backlight_device_release, + devm_backlight_device_match, bd); + WARN_ON(rc); +} +EXPORT_SYMBOL(devm_backlight_device_unregister); + #ifdef CONFIG_OF static int of_parent_match(struct device *dev, const void *data) { diff --git a/include/linux/backlight.h b/include/linux/backlight.h index da9a0825e007..53b77949c79d 100644 --- a/include/linux/backlight.h +++ b/include/linux/backlight.h @@ -114,7 +114,13 @@ static inline void backlight_update_status(struct backlight_device *bd) extern struct backlight_device *backlight_device_register(const char *name, struct device *dev, void *devdata, const struct backlight_ops *ops, const struct backlight_properties *props); +extern struct backlight_device *devm_backlight_device_register( + struct device *dev, const char *name, struct device *parent, + void *devdata, const struct backlight_ops *ops, + const struct backlight_properties *props); extern void backlight_device_unregister(struct backlight_device *bd); +extern void devm_backlight_device_unregister(struct device *dev, + struct backlight_device *bd); extern void backlight_force_update(struct backlight_device *bd, enum backlight_update_reason reason); -- cgit v1.2.3 From 1d0c48e66b3f1cf40660f69a87f55af3df0b2ae3 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:05:14 -0700 Subject: lcd: add devm_lcd_device_{register,unregister}() These functions allow the driver core to automatically clean up any allocation made by lcd drivers. Thus it simplifies the error paths. Signed-off-by: Jingoo Han Cc: Tejun Heo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/backlight/lcd.c | 70 +++++++++++++++++++++++++++++++++++++++++++ include/linux/lcd.h | 5 ++++ 2 files changed, 75 insertions(+) diff --git a/drivers/video/backlight/lcd.c b/drivers/video/backlight/lcd.c index 3649fd9ddb3a..41964a71a036 100644 --- a/drivers/video/backlight/lcd.c +++ b/drivers/video/backlight/lcd.c @@ -260,6 +260,76 @@ void lcd_device_unregister(struct lcd_device *ld) } EXPORT_SYMBOL(lcd_device_unregister); +static void devm_lcd_device_release(struct device *dev, void *res) +{ + struct lcd_device *lcd = *(struct lcd_device **)res; + + lcd_device_unregister(lcd); +} + +static int devm_lcd_device_match(struct device *dev, void *res, void *data) +{ + struct lcd_device **r = res; + + return *r == data; +} + +/** + * devm_lcd_device_register - resource managed lcd_device_register() + * @dev: the device to register + * @name: the name of the device + * @parent: a pointer to the parent device + * @devdata: an optional pointer to be stored for private driver use + * @ops: the lcd operations structure + * + * @return a struct lcd on success, or an ERR_PTR on error + * + * Managed lcd_device_register(). The lcd_device returned from this function + * are automatically freed on driver detach. See lcd_device_register() + * for more information. + */ +struct lcd_device *devm_lcd_device_register(struct device *dev, + const char *name, struct device *parent, + void *devdata, struct lcd_ops *ops) +{ + struct lcd_device **ptr, *lcd; + + ptr = devres_alloc(devm_lcd_device_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + lcd = lcd_device_register(name, parent, devdata, ops); + if (!IS_ERR(lcd)) { + *ptr = lcd; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return lcd; +} +EXPORT_SYMBOL(devm_lcd_device_register); + +/** + * devm_lcd_device_unregister - resource managed lcd_device_unregister() + * @dev: the device to unregister + * @ld: the lcd device to unregister + * + * Deallocated a lcd allocated with devm_lcd_device_register(). Normally + * this function will not need to be called and the resource management + * code will ensure that the resource is freed. + */ +void devm_lcd_device_unregister(struct device *dev, struct lcd_device *ld) +{ + int rc; + + rc = devres_release(dev, devm_lcd_device_release, + devm_lcd_device_match, ld); + WARN_ON(rc); +} +EXPORT_SYMBOL(devm_lcd_device_unregister); + + static void __exit lcd_class_exit(void) { class_destroy(lcd_class); diff --git a/include/linux/lcd.h b/include/linux/lcd.h index e00c3b0ebc6b..504f6246f38f 100644 --- a/include/linux/lcd.h +++ b/include/linux/lcd.h @@ -112,7 +112,12 @@ static inline void lcd_set_power(struct lcd_device *ld, int power) extern struct lcd_device *lcd_device_register(const char *name, struct device *parent, void *devdata, struct lcd_ops *ops); +extern struct lcd_device *devm_lcd_device_register(struct device *dev, + const char *name, struct device *parent, + void *devdata, struct lcd_ops *ops); extern void lcd_device_unregister(struct lcd_device *ld); +extern void devm_lcd_device_unregister(struct device *dev, + struct lcd_device *ld); #define to_lcd_device(obj) container_of(obj, struct lcd_device, dev) -- cgit v1.2.3 From 6212de88f8a2f59e9be66674b3aa1f9399971a16 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:05:15 -0700 Subject: MAINTAINERS: add Backlight subsystem co-maintainer Add myself as co-maintainer for the backlight subsystem. Signed-off-by: Jingoo Han Cc: Richard Purdie Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 2c64e6dbb4c8..8e12f6908247 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1611,6 +1611,7 @@ F: drivers/net/wireless/b43legacy/ BACKLIGHT CLASS/SUBSYSTEM M: Richard Purdie +M: Jingoo Han S: Maintained F: drivers/video/backlight/ F: include/linux/backlight.h -- cgit v1.2.3 From 3601792e7b68150420ea8dc129e26e167c0484d8 Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Wed, 3 Jul 2013 15:05:16 -0700 Subject: backlight: convert from legacy pm ops to dev_pm_ops Convert drivers/video/backlight/class to use dev_pm_ops for power management and remove Legacy PM ops hooks. With this change, backlight class registers suspend/resume callbacks via class->pm (dev_pm_ops) instead of Legacy class->suspend/resume. When __device_suspend() runs call-backs, it will find class->pm ops for the backlight class. [jg1.han@samsung.com: add CONFIG_PM_SLEEP to suspend/resume functions] Signed-off-by: Shuah Khan Signed-off-by: Jingoo Han Cc: Shuah Khan Cc: Jingoo Han Cc: Shuah Khan Cc: Arnd Bergmann Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/backlight/backlight.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c index 53c52fbc9395..3fccb6d3c8c3 100644 --- a/drivers/video/backlight/backlight.c +++ b/drivers/video/backlight/backlight.c @@ -208,7 +208,8 @@ static ssize_t backlight_show_actual_brightness(struct device *dev, static struct class *backlight_class; -static int backlight_suspend(struct device *dev, pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int backlight_suspend(struct device *dev) { struct backlight_device *bd = to_backlight_device(dev); @@ -235,6 +236,10 @@ static int backlight_resume(struct device *dev) return 0; } +#endif + +static SIMPLE_DEV_PM_OPS(backlight_class_dev_pm_ops, backlight_suspend, + backlight_resume); static void bl_device_release(struct device *dev) { @@ -489,8 +494,7 @@ static int __init backlight_class_init(void) } backlight_class->dev_attrs = bl_device_attributes; - backlight_class->suspend = backlight_suspend; - backlight_class->resume = backlight_resume; + backlight_class->pm = &backlight_class_dev_pm_ops; return 0; } -- cgit v1.2.3 From a95681058e993bd059d0b6f70730964e15c22596 Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Wed, 3 Jul 2013 15:05:17 -0700 Subject: radeon: remove redundant __list_for_each definition from mkregtable.c Signed-off-by: Dave Jones Cc: Dave Airlie Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/gpu/drm/radeon/mkregtable.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/drivers/gpu/drm/radeon/mkregtable.c b/drivers/gpu/drm/radeon/mkregtable.c index 5a82b6b75849..af85299f2126 100644 --- a/drivers/gpu/drm/radeon/mkregtable.c +++ b/drivers/gpu/drm/radeon/mkregtable.c @@ -372,19 +372,6 @@ static inline void list_splice_tail_init(struct list_head *list, for (pos = (head)->next; prefetch(pos->next), pos != (head); \ pos = pos->next) -/** - * __list_for_each - iterate over a list - * @pos: the &struct list_head to use as a loop cursor. - * @head: the head for your list. - * - * This variant differs from list_for_each() in that it's the - * simplest possible list iteration code, no prefetching is done. - * Use this for code that knows the list to be very short (empty - * or 1 entry) most of the time. - */ -#define __list_for_each(pos, head) \ - for (pos = (head)->next; pos != (head); pos = pos->next) - /** * list_for_each_prev - iterate over a list backwards * @pos: the &struct list_head to use as a loop cursor. -- cgit v1.2.3 From 5cb0656b62ff1199763764e4f6b4c06d30d5d0f5 Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Wed, 3 Jul 2013 15:05:18 -0700 Subject: ipw2200: convert __list_for_each usage to list_for_each Signed-off-by: Dave Jones Cc: Stanislav Yakovlev Cc: "John W. Linville" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/net/wireless/ipw2x00/ipw2200.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ipw2x00/ipw2200.c b/drivers/net/wireless/ipw2x00/ipw2200.c index d96257b79a84..4ed5e45ca1e2 100644 --- a/drivers/net/wireless/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/ipw2x00/ipw2200.c @@ -8256,7 +8256,7 @@ static int is_duplicate_packet(struct ipw_priv *priv, u8 *mac = header->addr2; int index = mac[5] % IPW_IBSS_MAC_HASH_SIZE; - __list_for_each(p, &priv->ibss_mac_hash[index]) { + list_for_each(p, &priv->ibss_mac_hash[index]) { entry = list_entry(p, struct ipw_ibss_seq, list); if (!memcmp(entry->mac, mac, ETH_ALEN)) -- cgit v1.2.3 From 64df3071a97f20767f63b88c573791691a855b5c Mon Sep 17 00:00:00 2001 From: Fan Du Date: Wed, 3 Jul 2013 15:05:19 -0700 Subject: lib/percpu_counter.c: __this_cpu_write() doesn't need to be protected by spinlock __this_cpu_write doesn't need to be protected by spinlock, AS we are doing per cpu write with preempt disabled. And another reason to remove __this_cpu_write outside of spinlock: __percpu_counter_sum is not an accurate counter. Signed-off-by: Fan Du Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- lib/percpu_counter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/percpu_counter.c b/lib/percpu_counter.c index ba6085d9c741..1fc23a3277e1 100644 --- a/lib/percpu_counter.c +++ b/lib/percpu_counter.c @@ -80,8 +80,8 @@ void __percpu_counter_add(struct percpu_counter *fbc, s64 amount, s32 batch) if (count >= batch || count <= -batch) { raw_spin_lock(&fbc->lock); fbc->count += count; - __this_cpu_write(*fbc->counters, 0); raw_spin_unlock(&fbc->lock); + __this_cpu_write(*fbc->counters, 0); } else { __this_cpu_write(*fbc->counters, count); } -- cgit v1.2.3 From be79794bc116fc0c264be1a599433c92ec9e34f5 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 3 Jul 2013 15:05:20 -0700 Subject: checkpatch: change CamelCase test and make it --strict Do not bleat a message on nominally acceptable CamelCase uses that are separated by an _ like drm_core_has_MTRR. CamelCase tests are also a bit noisy against certain types of code acceptable to some kernel developers. Make the test applicable only with --strict. Signed-off-by: Joe Perches Cc: Andy Whitcroft Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/checkpatch.pl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index b954de58304f..f1aad19b475c 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -2938,12 +2938,12 @@ sub process { while ($line =~ m{($Constant|$Lval)}g) { my $var = $1; if ($var !~ /$Constant/ && - $var =~ /[A-Z]\w*[a-z]|[a-z]\w*[A-Z]/ && + $var =~ /[A-Z][a-z]|[a-z][A-Z]/ && $var !~ /"^(?:Clear|Set|TestClear|TestSet|)Page[A-Z]/ && !defined $camelcase{$var}) { $camelcase{$var} = 1; - WARN("CAMELCASE", - "Avoid CamelCase: <$var>\n" . $herecurr); + CHK("CAMELCASE", + "Avoid CamelCase: <$var>\n" . $herecurr); } } -- cgit v1.2.3 From 95e2c6023b0e4c8499fb521697f79215f69135fe Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 3 Jul 2013 15:05:20 -0700 Subject: checkpatch: warn when using gcc's binary constant ("0b") extension The gcc extension for binary constants that start with 0b is only supported with gcc version 4.3 or higher. The kernel can still be compiled with earlier versions of gcc, so have checkpatch emit a warning for these constants. Restructure checkpatch's constant finding code a bit to support finding these binary constants. Signed-off-by: Joe Perches Suggested-by: Andrew Morton Cc: Andy Whitcroft Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/checkpatch.pl | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index f1aad19b475c..517da26d9a2d 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -230,11 +230,15 @@ our $Inline = qr{inline|__always_inline|noinline}; our $Member = qr{->$Ident|\.$Ident|\[[^]]*\]}; our $Lval = qr{$Ident(?:$Member)*}; +our $Int_type = qr{(?i)llu|ull|ll|lu|ul|l|u}; +our $Binary = qr{(?i)0b[01]+$Int_type?}; +our $Hex = qr{(?i)0x[0-9a-f]+$Int_type?}; +our $Int = qr{[0-9]+$Int_type?}; our $Float_hex = qr{(?i)0x[0-9a-f]+p-?[0-9]+[fl]?}; our $Float_dec = qr{(?i)(?:[0-9]+\.[0-9]*|[0-9]*\.[0-9]+)(?:e-?[0-9]+)?[fl]?}; our $Float_int = qr{(?i)[0-9]+e-?[0-9]+[fl]?}; our $Float = qr{$Float_hex|$Float_dec|$Float_int}; -our $Constant = qr{$Float|(?i)(?:0x[0-9a-f]+|[0-9]+)[ul]*}; +our $Constant = qr{$Float|$Binary|$Hex|$Int}; our $Assignment = qr{\*\=|/=|%=|\+=|-=|<<=|>>=|&=|\^=|\|=|=}; our $Compare = qr{<=|>=|==|!=|<|>}; our $Operators = qr{ @@ -2934,9 +2938,17 @@ sub process { } } -#CamelCase +#Specific variable tests while ($line =~ m{($Constant|$Lval)}g) { my $var = $1; + +#gcc binary extension + if ($var =~ /^$Binary$/) { + WARN("GCC_BINARY_CONSTANT", + "Avoid gcc v4.3+ binary constant extension: <$var>\n" . $herecurr); + } + +#CamelCase if ($var !~ /$Constant/ && $var =~ /[A-Z][a-z]|[a-z][A-Z]/ && $var !~ /"^(?:Clear|Set|TestClear|TestSet|)Page[A-Z]/ && -- cgit v1.2.3 From a640d25cead66457ac14a878234f8b323ba8aade Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 3 Jul 2013 15:05:21 -0700 Subject: checkpatch: add --strict preference for p = kmalloc(sizeof(*p)... Add another test for memory allocation style to follow Documentation/CodingStyle: Chapter 14: Allocating memory The preferred form for passing a size of a struct is the following: p = kmalloc(sizeof(*p), ...); Signed-off-by: Joe Perches Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/checkpatch.pl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 517da26d9a2d..6185eb67df94 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -3507,6 +3507,14 @@ sub process { "unnecessary cast may hide bugs, see http://c-faq.com/malloc/mallocnocast.html\n" . $herecurr); } +# alloc style +# p = alloc(sizeof(struct foo), ...) should be p = alloc(sizeof(*p), ...) + if ($^V && $^V ge 5.10.0 && + $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*([kv][mz]alloc(?:_node)?)\s*\(\s*(sizeof\s*\(\s*struct\s+$Lval\s*\))/) { + CHK("ALLOC_SIZEOF_STRUCT", + "Prefer $3(sizeof(*$1)...) over $3($4...)\n" . $herecurr); + } + # check for krealloc arg reuse if ($^V && $^V ge 5.10.0 && $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*krealloc\s*\(\s*\1\s*,/) { -- cgit v1.2.3 From 807bd26c4c3e94aced4630ba8369c8941728636b Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 3 Jul 2013 15:05:22 -0700 Subject: checkpatch: remove quote from CamelCase test Commit be987d9f80 ("checkpatch: improve CamelCase test for Page") added it but it shouldn't be there. Must have been my fault. Make sure that the tested variable doesn't contain a constant. Signed-off-by: Joe Perches Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/checkpatch.pl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 6185eb67df94..2f61b5cc17e1 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -2949,9 +2949,9 @@ sub process { } #CamelCase - if ($var !~ /$Constant/ && + if ($var !~ /^$Constant$/ && $var =~ /[A-Z][a-z]|[a-z][A-Z]/ && - $var !~ /"^(?:Clear|Set|TestClear|TestSet|)Page[A-Z]/ && + $var !~ /^(?:Clear|Set|TestClear|TestSet|)Page[A-Z]/ && !defined $camelcase{$var}) { $camelcase{$var} = 1; CHK("CAMELCASE", -- cgit v1.2.3 From fdb4bcd6108602097b9a1ebd11f6e61f331c8dce Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 3 Jul 2013 15:05:23 -0700 Subject: checkpatch: improve network block comment test and message Show the first line of the comment after a line with just /* to better show where the defective comment style is in the file. Signed-off-by: Joe Perches Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/checkpatch.pl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 2f61b5cc17e1..a3922d0e4d25 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1891,8 +1891,8 @@ sub process { } if ($realfile =~ m@^(drivers/net/|net/)@ && - $rawline =~ /^\+[ \t]*\/\*[ \t]*$/ && - $prevrawline =~ /^\+[ \t]*$/) { + $prevrawline =~ /^\+[ \t]*\/\*[ \t]*$/ && + $rawline =~ /^\+[ \t]*\*/) { WARN("NETWORKING_BLOCK_COMMENT_STYLE", "networking block comments don't use an empty /* line, use /* Comment...\n" . $hereprev); } -- cgit v1.2.3 From a605e32ebde25dc31f943fecb30e3e28079ccd06 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 3 Jul 2013 15:05:24 -0700 Subject: checkpatch: warn when networking block comment lines don't start with * Some block comments in network are written as: /* block comment line 1 block comment line 2 */ Emit a warning on the "block comment line 2" because it should be /* block comment line 1 * block comment line 2 */ This warning is only emitted on the second line of a block comment. Signed-off-by: Joe Perches Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/checkpatch.pl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index a3922d0e4d25..576139a508a3 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1897,6 +1897,14 @@ sub process { "networking block comments don't use an empty /* line, use /* Comment...\n" . $hereprev); } + if ($realfile =~ m@^(drivers/net/|net/)@ && + $prevrawline =~ /^\+[ \t]*\/\*/ && #starting /* + $prevrawline !~ /\*\/[ \t]*$/ && #no trailing */ + $rawline !~ /^\+[ \t]*\*/) { #no leading * + WARN("NETWORKING_BLOCK_COMMENT_STYLE", + "networking block comments start with * on subsequent lines\n" . $hereprev); + } + if ($realfile =~ m@^(drivers/net/|net/)@ && $rawline !~ m@^\+[ \t]*\*/[ \t]*$@ && #trailing */ $rawline !~ m@^\+.*/\*.*\*/[ \t]*$@ && #inline /*...*/ -- cgit v1.2.3 From 36ec19390effc9131132901e8a66a071a7b74a88 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 3 Jul 2013 15:05:25 -0700 Subject: checkpatch: warn on comparisons to jiffies Comparing jiffies is almost always wrong and time_before and time_after should be used instead. Warn on any comparison to jiffies. Signed-off-by: Joe Perches Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/checkpatch.pl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 576139a508a3..c274e1dc1e67 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -3299,6 +3299,12 @@ sub process { } } +# check for comparisons of jiffies + if ($line =~ /\bjiffies\s*$Compare|$Compare\s*jiffies\b/) { + WARN("JIFFIES_COMPARISON", + "Comparing jiffies is almost always wrong; prefer time_after, time_before and friends\n" . $herecurr); + } + # warn about #ifdefs in C files # if ($line =~ /^.\s*\#\s*if(|n)def/ && ($realfile =~ /\.c$/)) { # print "#ifdef in C files should be avoided\n"; -- cgit v1.2.3 From 9d7a34a5135d29b840d074ba8fbb9c2fac63e508 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 3 Jul 2013 15:05:26 -0700 Subject: checkpatch: warn on comparisons to get_jiffies_64() Comparing get_jiffies_64() is almost always wrong and time_before64 and time_after64 should be used instead. Warn on any comparison to get_jiffies_64(). Signed-off-by: Joe Perches Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/checkpatch.pl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index c274e1dc1e67..f4e247b277de 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -3305,6 +3305,12 @@ sub process { "Comparing jiffies is almost always wrong; prefer time_after, time_before and friends\n" . $herecurr); } +# check for comparisons of get_jiffies_64() + if ($line =~ /\bget_jiffies_64\s*\(\s*\)\s*$Compare|$Compare\s*get_jiffies_64\s*\(\s*\)/) { + WARN("JIFFIES_COMPARISON", + "Comparing get_jiffies_64() is almost always wrong; prefer time_after64, time_before64 and friends\n" . $herecurr); + } + # warn about #ifdefs in C files # if ($line =~ /^.\s*\#\s*if(|n)def/ && ($realfile =~ /\.c$/)) { # print "#ifdef in C files should be avoided\n"; -- cgit v1.2.3 From 3cc4b1c3f0d283f4bb8d49059bd6df8e7af7558b Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 3 Jul 2013 15:05:27 -0700 Subject: checkpatch: reduce false positive rate of "complex macros" Allow "#define foo struct.member" without bleating a warning. This also allows "#define foo bar.baz->qux" and so on. Signed-off-by: Joe Perches Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/checkpatch.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index f4e247b277de..93b8e6693cb1 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -3041,7 +3041,7 @@ sub process { if ($dstat ne '' && $dstat !~ /^(?:$Ident|-?$Constant),$/ && # 10, // foo(), $dstat !~ /^(?:$Ident|-?$Constant);$/ && # foo(); - $dstat !~ /^[!~-]?(?:$Ident|$Constant)$/ && # 10 // foo() // !foo // ~foo // -foo + $dstat !~ /^[!~-]?(?:$Lval|$Constant)$/ && # 10 // foo() // !foo // ~foo // -foo // foo->bar // foo.bar->baz $dstat !~ /^'X'$/ && # character constants $dstat !~ /$exceptions/ && $dstat !~ /^\.$Ident\s*=/ && # .foo = -- cgit v1.2.3 From c4a62ef9102bfa39f3bb89be2ae1ae11a23ebe28 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 3 Jul 2013 15:05:28 -0700 Subject: checkpatch: add a placeholder to check blank lines before declarations Figure out first how to determine if this is in a struct declaration or in a function body before enabling this. Signed-off-by: Joe Perches Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/checkpatch.pl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 93b8e6693cb1..4ad40523b419 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -2750,6 +2750,14 @@ sub process { "space required before the open brace '{'\n" . $herecurr); } +## # check for blank lines before declarations +## if ($line =~ /^.\t+$Type\s+$Ident(?:\s*=.*)?;/ && +## $prevrawline =~ /^.\s*$/) { +## WARN("SPACING", +## "No blank lines before declarations\n" . $hereprev); +## } +## + # closing brace should have a space following it when it has anything # on the line if ($line =~ /}(?!(?:,|;|\)))\S/) { -- cgit v1.2.3 From 77b9a53a627491df83a75361440485629c35aa91 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 3 Jul 2013 15:05:29 -0700 Subject: checkpatch: don't warn on blank lines before/after braces as often Check to make sure the blank lines aren't comment lines like: bool foo(bool bar) { /* Don't warn on a leading comment */ return !bar; /* Don't warn on a trailing comment either */ } Signed-off-by: Joe Perches Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/checkpatch.pl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 4ad40523b419..c43be815cc71 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -3258,11 +3258,11 @@ sub process { } # check for unnecessary blank lines around braces - if (($line =~ /^.\s*}\s*$/ && $prevline =~ /^.\s*$/)) { + if (($line =~ /^.\s*}\s*$/ && $prevrawline =~ /^.\s*$/)) { CHK("BRACES", "Blank lines aren't necessary before a close brace '}'\n" . $hereprev); } - if (($line =~ /^.\s*$/ && $prevline =~ /^..*{\s*$/)) { + if (($rawline =~ /^.\s*$/ && $prevline =~ /^..*{\s*$/)) { CHK("BRACES", "Blank lines aren't necessary after an open brace '{'\n" . $hereprev); } -- cgit v1.2.3 From 179f8f40fc3ae7cd49e96b3a7d5182166c36bdab Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 3 Jul 2013 15:05:30 -0700 Subject: checkpatch: add a --strict test for comparison to true/false Comparing to true or false is error prone. Add tests for the various forms of (foo == true) && (false != bar) that are only reported with --strict. Signed-off-by: Joe Perches Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/checkpatch.pl | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index c43be815cc71..c2d223c406e8 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -3588,6 +3588,33 @@ sub process { "Using yield() is generally wrong. See yield() kernel-doc (sched/core.c)\n" . $herecurr); } +# check for comparisons against true and false + if ($line =~ /\+\s*(.*?)\b(true|false|$Lval)\s*(==|\!=)\s*(true|false|$Lval)\b(.*)$/i) { + my $lead = $1; + my $arg = $2; + my $test = $3; + my $otype = $4; + my $trail = $5; + my $op = "!"; + + ($arg, $otype) = ($otype, $arg) if ($arg =~ /^(?:true|false)$/i); + + my $type = lc($otype); + if ($type =~ /^(?:true|false)$/) { + if (("$test" eq "==" && "$type" eq "true") || + ("$test" eq "!=" && "$type" eq "false")) { + $op = ""; + } + + CHK("BOOL_COMPARISON", + "Using comparison to $otype is error prone\n" . $herecurr); + +## maybe suggesting a correct construct would better +## "Using comparison to $otype is error prone. Perhaps use '${lead}${op}${arg}${trail}'\n" . $herecurr); + + } + } + # check for semaphores initialized locked if ($line =~ /^.\s*sema_init.+,\W?0\W?\)/) { WARN("CONSIDER_COMPLETION", -- cgit v1.2.3 From 23f780c90496eb1cc158e862e7035c8468dfa052 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 3 Jul 2013 15:05:31 -0700 Subject: checkpatch: improve "no space after cast" test Some false positives exist on this test. For instance: *va_arg(args, signed char *) = val.s; or memset(foo, 0, sizeof(struct bar *) * baz)); Ignore lines that have an arithmetic operator or assignment after what appears to be a cast to a pointer "(foo *)". Add $Arithmetic convenience variable. Signed-off-by: Joe Perches Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/checkpatch.pl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index c2d223c406e8..ab39ceb38286 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -241,10 +241,11 @@ our $Float = qr{$Float_hex|$Float_dec|$Float_int}; our $Constant = qr{$Float|$Binary|$Hex|$Int}; our $Assignment = qr{\*\=|/=|%=|\+=|-=|<<=|>>=|&=|\^=|\|=|=}; our $Compare = qr{<=|>=|==|!=|<|>}; +our $Arithmetic = qr{\+|-|\*|\/|%}; our $Operators = qr{ <=|>=|==|!=| =>|->|<<|>>|<|>|!|~| - &&|\|\||,|\^|\+\+|--|&|\||\+|-|\*|\/|% + &&|\|\||,|\^|\+\+|--|&|\||$Arithmetic }x; our $NonptrType; @@ -1885,7 +1886,7 @@ sub process { } } - if ($line =~ /^\+.*\*[ \t]*\)[ \t]+/) { + if ($line =~ /^\+.*\*[ \t]*\)[ \t]+(?!$Assignment|$Arithmetic)/) { CHK("SPACING", "No space is necessary after a cast\n" . $hereprev); } -- cgit v1.2.3 From 3705ce5bcc1037b68e9d20f90ab50bc7f64edd00 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 3 Jul 2013 15:05:31 -0700 Subject: checkpatch: create an EXPERIMENTAL --fix option to correct patches Some patches have simple defects in whitespace and formatting that checkpatch could correct automatically. Attempt to do so. Add a --fix option to create a ".EXPERIMENTAL-checkpatch-fixes" file that tries to use normal kernel style for some of these formatting errors. Add warnings against using this file without verifying the changes. Signed-off-by: Joe Perches Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/checkpatch.pl | 442 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 354 insertions(+), 88 deletions(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index ab39ceb38286..9696be57ea42 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -27,6 +27,7 @@ my $summary = 1; my $mailback = 0; my $summary_file = 0; my $show_types = 0; +my $fix = 0; my $root; my %debug; my %ignore_type = (); @@ -63,6 +64,11 @@ Options: is all off) --test-only=WORD report only warnings/errors containing WORD literally + --fix EXPERIMENTAL - may create horrible results + If correctable single-line errors exist, create + ".EXPERIMENTAL-checkpatch-fixes" + with potential errors corrected to the preferred + checkpatch style -h, --help, --version display this help and exit When FILE is - read standard input. @@ -114,7 +120,7 @@ GetOptions( 'summary!' => \$summary, 'mailback!' => \$mailback, 'summary-file!' => \$summary_file, - + 'fix!' => \$fix, 'debug=s' => \%debug, 'test-only=s' => \$tst_only, 'h|help' => \$help, @@ -367,6 +373,7 @@ $chk_signoff = 0 if ($file); my @rawlines = (); my @lines = (); +my @fixed = (); my $vname; for my $filename (@ARGV) { my $FILE; @@ -394,6 +401,7 @@ for my $filename (@ARGV) { } @rawlines = (); @lines = (); + @fixed = (); } exit($exit); @@ -434,7 +442,7 @@ sub parse_email { $comment = $2 if defined $2; $formatted_email =~ s/$address.*$//; $name = $formatted_email; - $name =~ s/^\s+|\s+$//g; + $name = trim($name); $name =~ s/^\"|\"$//g; # If there's a name left after stripping spaces and # leading quotes, and the address doesn't have both @@ -449,9 +457,9 @@ sub parse_email { } } - $name =~ s/^\s+|\s+$//g; + $name = trim($name); $name =~ s/^\"|\"$//g; - $address =~ s/^\s+|\s+$//g; + $address = trim($address); $address =~ s/^\<|\>$//g; if ($name =~ /[^\w \-]/i) { ##has "must quote" chars @@ -467,9 +475,9 @@ sub format_email { my $formatted_email; - $name =~ s/^\s+|\s+$//g; + $name = trim($name); $name =~ s/^\"|\"$//g; - $address =~ s/^\s+|\s+$//g; + $address = trim($address); if ($name =~ /[^\w \-]/i) { ##has "must quote" chars $name =~ s/(?\n"; - my ($from, $to) = ($2, $2); + my ($ident, $from, $to) = ($1, $2, $2); # Should start with a space. $to =~ s/^(\S)/ $1/; @@ -2374,15 +2460,22 @@ sub process { while ($to =~ s/\*\s+\*/\*\*/) { } - #print "from<$from> to<$to>\n"; +## print "1: from<$from> to<$to> ident<$ident>\n"; if ($from ne $to) { - ERROR("POINTER_LOCATION", - "\"(foo$from)\" should be \"(foo$to)\"\n" . $herecurr); + if (ERROR("POINTER_LOCATION", + "\"(foo$from)\" should be \"(foo$to)\"\n" . $herecurr) && + $fix) { + my $sub_from = $ident; + my $sub_to = $ident; + $sub_to =~ s/\Q$from\E/$to/; + $fixed[$linenr - 1] =~ + s@\Q$sub_from\E@$sub_to@; + } } } while ($line =~ m{(\b$NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)($Ident))}g) { #print "BB<$1>\n"; - my ($from, $to, $ident) = ($2, $2, $3); + my ($match, $from, $to, $ident) = ($1, $2, $2, $3); # Should start with a space. $to =~ s/^(\S)/ $1/; @@ -2394,10 +2487,18 @@ sub process { # Modifiers should have spaces. $to =~ s/(\b$Modifier$)/$1 /; - #print "from<$from> to<$to> ident<$ident>\n"; +## print "2: from<$from> to<$to> ident<$ident>\n"; if ($from ne $to && $ident !~ /^$Modifier$/) { - ERROR("POINTER_LOCATION", - "\"foo${from}bar\" should be \"foo${to}bar\"\n" . $herecurr); + if (ERROR("POINTER_LOCATION", + "\"foo${from}bar\" should be \"foo${to}bar\"\n" . $herecurr) && + $fix) { + + my $sub_from = $match; + my $sub_to = $match; + $sub_to =~ s/\Q$from\E/$to/; + $fixed[$linenr - 1] =~ + s@\Q$sub_from\E@$sub_to@; + } } } @@ -2483,9 +2584,13 @@ sub process { } # missing space after union, struct or enum definition - if ($line =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident)?(?:\s+$Ident)?[=\{]/) { - WARN("SPACING", - "missing space after $1 definition\n" . $herecurr); + if ($line =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident){1,2}[=\{]/) { + if (WARN("SPACING", + "missing space after $1 definition\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ + s/^(.\s*(?:typedef\s+)?(?:enum|union|struct)(?:\s+$Ident){1,2})([=\{])/$1 $2/; + } } # check for spacing round square brackets; allowed: @@ -2497,8 +2602,12 @@ sub process { if ($prefix !~ /$Type\s+$/ && ($where != 0 || $prefix !~ /^.\s+$/) && $prefix !~ /[{,]\s+$/) { - ERROR("BRACKET_SPACE", - "space prohibited before open square bracket '['\n" . $herecurr); + if (ERROR("BRACKET_SPACE", + "space prohibited before open square bracket '['\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ + s/^(\+.*?)\s+\[/$1\[/; + } } } @@ -2515,7 +2624,6 @@ sub process { __attribute__|format|__extension__| asm|__asm__)$/x) { - # cpp #define statements have non-optional spaces, ie # if there is a space between the name and the open # parenthesis it is simply not a parameter group. @@ -2529,19 +2637,30 @@ sub process { } elsif ($ctx =~ /$Type$/) { } else { - WARN("SPACING", - "space prohibited between function name and open parenthesis '('\n" . $herecurr); + if (WARN("SPACING", + "space prohibited between function name and open parenthesis '('\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ + s/\b$name\s+\(/$name\(/; + } } } # check for whitespace before a non-naked semicolon if ($line =~ /^\+.*\S\s+;/) { - WARN("SPACING", - "space prohibited before semicolon\n" . $herecurr); + if (WARN("SPACING", + "space prohibited before semicolon\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ + s/^(\+.*\S)\s+;/$1;/; + } } # Check operator spacing. if (!($line=~/\#\s*include/)) { + my $fixed_line = ""; + my $line_fixed = 0; + my $ops = qr{ <<=|>>=|<=|>=|==|!=| \+=|-=|\*=|\/=|%=|\^=|\|=|&=| @@ -2550,11 +2669,30 @@ sub process { \?|: }x; my @elements = split(/($ops|;)/, $opline); + +## print("element count: <" . $#elements . ">\n"); +## foreach my $el (@elements) { +## print("el: <$el>\n"); +## } + + my @fix_elements = (); my $off = 0; + foreach my $el (@elements) { + push(@fix_elements, substr($rawline, $off, length($el))); + $off += length($el); + } + + $off = 0; + my $blank = copy_spacing($opline); for (my $n = 0; $n < $#elements; $n += 2) { + + my $good = $fix_elements[$n] . $fix_elements[$n + 1]; + +## print("n: <$n> good: <$good>\n"); + $off += length($elements[$n]); # Pick up the preceding and succeeding characters. @@ -2611,8 +2749,11 @@ sub process { } elsif ($op eq ';') { if ($ctx !~ /.x[WEBC]/ && $cc !~ /^\\/ && $cc !~ /^;/) { - ERROR("SPACING", - "space required after that '$op' $at\n" . $hereptr); + if (ERROR("SPACING", + "space required after that '$op' $at\n" . $hereptr)) { + $good = trim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " "; + $line_fixed = 1; + } } # // is a comment @@ -2623,15 +2764,24 @@ sub process { # : when part of a bitfield } elsif ($op eq '->' || $opv eq ':B') { if ($ctx =~ /Wx.|.xW/) { - ERROR("SPACING", - "spaces prohibited around that '$op' $at\n" . $hereptr); + if (ERROR("SPACING", + "spaces prohibited around that '$op' $at\n" . $hereptr)) { + $good = trim($fix_elements[$n]) . trim($fix_elements[$n + 1]); + $line_fixed = 1; + if (defined $fix_elements[$n + 2]) { + $fix_elements[$n + 2] =~ s/^\s+//; + } + } } # , must have a space on the right. } elsif ($op eq ',') { if ($ctx !~ /.x[WEC]/ && $cc !~ /^}/) { - ERROR("SPACING", - "space required after that '$op' $at\n" . $hereptr); + if (ERROR("SPACING", + "space required after that '$op' $at\n" . $hereptr)) { + $good = trim($fix_elements[$n]) . trim($fix_elements[$n + 1]) . " "; + $line_fixed = 1; + } } # '*' as part of a type definition -- reported already. @@ -2645,34 +2795,58 @@ sub process { $opv eq '*U' || $opv eq '-U' || $opv eq '&U' || $opv eq '&&U') { if ($ctx !~ /[WEBC]x./ && $ca !~ /(?:\)|!|~|\*|-|\&|\||\+\+|\-\-|\{)$/) { - ERROR("SPACING", - "space required before that '$op' $at\n" . $hereptr); + if (ERROR("SPACING", + "space required before that '$op' $at\n" . $hereptr)) { + $good = trim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]); + $line_fixed = 1; + } } if ($op eq '*' && $cc =~/\s*$Modifier\b/) { # A unary '*' may be const } elsif ($ctx =~ /.xW/) { - ERROR("SPACING", - "space prohibited after that '$op' $at\n" . $hereptr); + if (ERROR("SPACING", + "space prohibited after that '$op' $at\n" . $hereptr)) { + $fixed_line =~ s/\s+$//; + $good = trim($fix_elements[$n]) . trim($fix_elements[$n + 1]); + $line_fixed = 1; + if (defined $fix_elements[$n + 2]) { + $fix_elements[$n + 2] =~ s/^\s+//; + } + } } # unary ++ and unary -- are allowed no space on one side. } elsif ($op eq '++' or $op eq '--') { if ($ctx !~ /[WEOBC]x[^W]/ && $ctx !~ /[^W]x[WOBEC]/) { - ERROR("SPACING", - "space required one side of that '$op' $at\n" . $hereptr); + if (ERROR("SPACING", + "space required one side of that '$op' $at\n" . $hereptr)) { + $fixed_line =~ s/\s+$//; + $good = trim($fix_elements[$n]) . trim($fix_elements[$n + 1]) . " "; + $line_fixed = 1; + } } if ($ctx =~ /Wx[BE]/ || ($ctx =~ /Wx./ && $cc =~ /^;/)) { - ERROR("SPACING", - "space prohibited before that '$op' $at\n" . $hereptr); + if (ERROR("SPACING", + "space prohibited before that '$op' $at\n" . $hereptr)) { + $fixed_line =~ s/\s+$//; + $good = trim($fix_elements[$n]) . trim($fix_elements[$n + 1]); + $line_fixed = 1; + } } if ($ctx =~ /ExW/) { - ERROR("SPACING", - "space prohibited after that '$op' $at\n" . $hereptr); + if (ERROR("SPACING", + "space prohibited after that '$op' $at\n" . $hereptr)) { + $fixed_line =~ s/\s+$//; + $good = trim($fix_elements[$n]) . trim($fix_elements[$n + 1]); + $line_fixed = 1; + if (defined $fix_elements[$n + 2]) { + $fix_elements[$n + 2] =~ s/^\s+//; + } + } } - # << and >> may either have or not have spaces both sides } elsif ($op eq '<<' or $op eq '>>' or $op eq '&' or $op eq '^' or $op eq '|' or @@ -2681,17 +2855,23 @@ sub process { $op eq '%') { if ($ctx =~ /Wx[^WCE]|[^WCE]xW/) { - ERROR("SPACING", - "need consistent spacing around '$op' $at\n" . - $hereptr); + if (ERROR("SPACING", + "need consistent spacing around '$op' $at\n" . $hereptr)) { + $fixed_line =~ s/\s+$//; + $good = trim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " "; + $line_fixed = 1; + } } # A colon needs no spaces before when it is # terminating a case value or a label. } elsif ($opv eq ':C' || $opv eq ':L') { if ($ctx =~ /Wx./) { - ERROR("SPACING", - "space prohibited before that '$op' $at\n" . $hereptr); + if (ERROR("SPACING", + "space prohibited before that '$op' $at\n" . $hereptr)) { + $good = trim($fix_elements[$n]) . trim($fix_elements[$n + 1]); + $line_fixed = 1; + } } # All the others need spaces both sides. @@ -2714,12 +2894,30 @@ sub process { } if ($ok == 0) { - ERROR("SPACING", - "spaces required around that '$op' $at\n" . $hereptr); + if (ERROR("SPACING", + "spaces required around that '$op' $at\n" . $hereptr)) { + $good = trim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " "; + $good = $fix_elements[$n] . " " . trim($fix_elements[$n + 1]) . " "; + $line_fixed = 1; + } } } $off += length($elements[$n + 1]); + +## print("n: <$n> GOOD: <$good>\n"); + + $fixed_line = $fixed_line . $good; + } + + if (($#elements % 2) == 0) { + $fixed_line = $fixed_line . $fix_elements[$#elements]; } + + if ($fix && $line_fixed && $fixed_line ne $fixed[$linenr - 1]) { + $fixed[$linenr - 1] = $fixed_line; + } + + } # check for multiple assignments @@ -2747,8 +2945,12 @@ sub process { #need space before brace following if, while, etc if (($line =~ /\(.*\){/ && $line !~ /\($Type\){/) || $line =~ /do{/) { - ERROR("SPACING", - "space required before the open brace '{'\n" . $herecurr); + if (ERROR("SPACING", + "space required before the open brace '{'\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ + s/^(\+.*(?:do|\))){/$1 {/; + } } ## # check for blank lines before declarations @@ -2768,32 +2970,52 @@ sub process { # check spacing on square brackets if ($line =~ /\[\s/ && $line !~ /\[\s*$/) { - ERROR("SPACING", - "space prohibited after that open square bracket '['\n" . $herecurr); + if (ERROR("SPACING", + "space prohibited after that open square bracket '['\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ + s/\[\s+/\[/; + } } if ($line =~ /\s\]/) { - ERROR("SPACING", - "space prohibited before that close square bracket ']'\n" . $herecurr); + if (ERROR("SPACING", + "space prohibited before that close square bracket ']'\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ + s/\s+\]/\]/; + } } # check spacing on parentheses if ($line =~ /\(\s/ && $line !~ /\(\s*(?:\\)?$/ && $line !~ /for\s*\(\s+;/) { - ERROR("SPACING", - "space prohibited after that open parenthesis '('\n" . $herecurr); + if (ERROR("SPACING", + "space prohibited after that open parenthesis '('\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ + s/\(\s+/\(/; + } } if ($line =~ /(\s+)\)/ && $line !~ /^.\s*\)/ && $line !~ /for\s*\(.*;\s+\)/ && $line !~ /:\s+\)/) { - ERROR("SPACING", - "space prohibited before that close parenthesis ')'\n" . $herecurr); + if (ERROR("SPACING", + "space prohibited before that close parenthesis ')'\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ + s/\s+\)/\)/; + } } #goto labels aren't indented, allow a single space however if ($line=~/^.\s+[A-Za-z\d_]+:(?![0-9]+)/ and !($line=~/^. [A-Za-z\d_]+:/) and !($line=~/^.\s+default:/)) { - WARN("INDENTED_LABEL", - "labels should not be indented\n" . $herecurr); + if (WARN("INDENTED_LABEL", + "labels should not be indented\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ + s/^(.)\s+/$1/; + } } # Return is not a function. @@ -2830,8 +3052,13 @@ sub process { } # Need a space before open parenthesis after if, while etc - if ($line=~/\b(if|while|for|switch)\(/) { - ERROR("SPACING", "space required before the open parenthesis '('\n" . $herecurr); + if ($line =~ /\b(if|while|for|switch)\(/) { + if (ERROR("SPACING", + "space required before the open parenthesis '('\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ + s/\b(if|while|for|switch)\(/$1 \(/; + } } # Check for illegal assignment in if conditional -- and check for trailing @@ -3329,8 +3556,13 @@ sub process { # warn about spacing in #ifdefs if ($line =~ /^.\s*\#\s*(ifdef|ifndef|elif)\s\s+/) { - ERROR("SPACING", - "exactly one space required after that #$1\n" . $herecurr); + if (ERROR("SPACING", + "exactly one space required after that #$1\n" . $herecurr) && + $fix) { + $fixed[$linenr - 1] =~ + s/^(.\s*\#\s*(ifdef|ifndef|elif))\s{2,}/$1 /; + } + } # check for spinlock_t definitions without a comment. @@ -3793,6 +4025,40 @@ sub process { print "\n\n"; } + if ($clean == 0 && $fix && "@rawlines" ne "@fixed") { + my $newfile = $filename . ".EXPERIMENTAL-checkpatch-fixes"; + my $linecount = 0; + my $f; + + open($f, '>', $newfile) + or die "$P: Can't open $newfile for write\n"; + foreach my $fixed_line (@fixed) { + $linecount++; + if ($file) { + if ($linecount > 3) { + $fixed_line =~ s/^\+//; + print $f $fixed_line. "\n"; + } + } else { + print $f $fixed_line . "\n"; + } + } + close($f); + + if (!$quiet) { + print << "EOM"; +Wrote EXPERIMENTAL --fix correction(s) to '$newfile' + +Do _NOT_ trust the results written to this file. +Do _NOT_ submit these changes without inspecting them for correctness. + +This EXPERIMENTAL file is simply a convenience to help rewrite patches. +No warranties, expressed or implied... + +EOM + } + } + if ($clean == 1 && $quiet == 0) { print "$vname has no obvious style problems and is ready for submission.\n" } -- cgit v1.2.3 From 786b632622800d73d9b0355c9a79b3f3b5792c6c Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 3 Jul 2013 15:05:32 -0700 Subject: checkpatch: move test for space before semicolon after operator spacing Moving this test allows the --fix option to work better. Signed-off-by: Joe Perches Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/checkpatch.pl | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 9696be57ea42..5989415985ae 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -2646,16 +2646,6 @@ sub process { } } -# check for whitespace before a non-naked semicolon - if ($line =~ /^\+.*\S\s+;/) { - if (WARN("SPACING", - "space prohibited before semicolon\n" . $herecurr) && - $fix) { - $fixed[$linenr - 1] =~ - s/^(\+.*\S)\s+;/$1;/; - } - } - # Check operator spacing. if (!($line=~/\#\s*include/)) { my $fixed_line = ""; @@ -2920,6 +2910,16 @@ sub process { } +# check for whitespace before a non-naked semicolon + if ($line =~ /^\+.*\S\s+;/) { + if (WARN("SPACING", + "space prohibited before semicolon\n" . $herecurr) && + $fix) { + 1 while $fixed[$linenr - 1] =~ + s/^(\+.*\S)\s+;/$1;/; + } + } + # check for multiple assignments if ($line =~ /^.\s*$Lval\s*=\s*$Lval\s*=(?!=)/) { CHK("MULTIPLE_ASSIGNMENTS", -- cgit v1.2.3 From 22735ce857a2d9f4e6eec37c36be3fcf9d21d154 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 3 Jul 2013 15:05:33 -0700 Subject: checkpatch: ignore SI unit CamelCase variants like "_uV" Many existing variable names use SI like variants that should be otherwise obvious and acceptable. Whitelist them from the CamelCase message. Signed-off-by: Joe Perches Suggested-by: Phil Carmody Acked-by: Phil Carmody Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/checkpatch.pl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 5989415985ae..7e8aa1bb0721 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -3195,7 +3195,10 @@ sub process { #CamelCase if ($var !~ /^$Constant$/ && $var =~ /[A-Z][a-z]|[a-z][A-Z]/ && +#Ignore Page variants $var !~ /^(?:Clear|Set|TestClear|TestSet|)Page[A-Z]/ && +#Ignore SI style variants like nS, mV and dB (ie: max_uV, regulator_min_uA_show) + $var !~ /^(?:[a-z_]*?)_?[a-z][A-Z](?:_[a-z_]+)?$/ && !defined $camelcase{$var}) { $camelcase{$var} = 1; CHK("CAMELCASE", -- cgit v1.2.3 From 3445686af721534ac1086a9c6d48b3470dfb6946 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 3 Jul 2013 15:05:34 -0700 Subject: checkpatch: ignore existing CamelCase uses from include/... When using --strict, CamelCase uses are described with CHECK: messages. These CamelCase uses may be acceptable and should not generate these messages when the variable is already defined in a file from the include/... path. So, change checkpatch to read all the .h files in include/... and look for preexisting CamelCase #defines, typedefs and function prototypes. Add these to the existing camelcase hash so that any uses in the patch or file can be ignored. There are currently ~3500 files in include/. It takes about 10 cpu seconds on my little netbook to grep for and preseed these existing uses. That's about 4x the time for a similar git grep. This preseeding is only done once when using --strict and only when there is a CamelCase use found. If a .git directory is found, it uses 'git ls-files include' If not, it uses 'find $root/include -name "*.h" Signed-off-by: Joe Perches Cc: Andy Whitcroft Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/checkpatch.pl | 57 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 7e8aa1bb0721..70e7f7cdb452 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -31,6 +31,7 @@ my $fix = 0; my $root; my %debug; my %ignore_type = (); +my %camelcase = (); my @ignore = (); my $help = 0; my $configuration_file = ".checkpatch.conf"; @@ -349,7 +350,6 @@ sub build_types { } build_types(); - our $Typecast = qr{\s*(\(\s*$NonptrType\s*\)){0,1}\s*}; # Using $balanced_parens, $LvalOrFunc, or $FuncArg @@ -369,6 +369,48 @@ sub deparenthesize { return $string; } +sub seed_camelcase_file { + my ($file) = @_; + + return if (!(-f $file)); + + local $/; + + open(my $include_file, '<', "$file") + or warn "$P: Can't read '$file' $!\n"; + my $text = <$include_file>; + close($include_file); + + my @lines = split('\n', $text); + + foreach my $line (@lines) { + next if ($line !~ /(?:[A-Z][a-z]|[a-z][A-Z])/); + if ($line =~ /^[ \t]*(?:#[ \t]*define|typedef\s+$Type)\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)/) { + $camelcase{$1} = 1; + } + elsif ($line =~ /^\s*$Declare\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)\s*\(/) { + $camelcase{$1} = 1; + } + } +} + +my $camelcase_seeded = 0; +sub seed_camelcase_includes { + return if ($camelcase_seeded); + + my $files; + if (-d ".git") { + $files = `git ls-files include`; + } else { + $files = `find $root/include -name "*.h"`; + } + my @include_files = split('\n', $files); + foreach my $file (@include_files) { + seed_camelcase_file($file); + } + $camelcase_seeded = 1; +} + $chk_signoff = 0 if ($file); my @rawlines = (); @@ -1448,7 +1490,6 @@ sub process { my %suppress_export; my $suppress_statement = 0; - my %camelcase = (); # Pre-scan the patch sanitizing the lines. # Pre-scan the patch looking for any __setup documentation. @@ -3198,11 +3239,13 @@ sub process { #Ignore Page variants $var !~ /^(?:Clear|Set|TestClear|TestSet|)Page[A-Z]/ && #Ignore SI style variants like nS, mV and dB (ie: max_uV, regulator_min_uA_show) - $var !~ /^(?:[a-z_]*?)_?[a-z][A-Z](?:_[a-z_]+)?$/ && - !defined $camelcase{$var}) { - $camelcase{$var} = 1; - CHK("CAMELCASE", - "Avoid CamelCase: <$var>\n" . $herecurr); + $var !~ /^(?:[a-z_]*?)_?[a-z][A-Z](?:_[a-z_]+)?$/) { + seed_camelcase_includes() if ($check); + if (!defined $camelcase{$var}) { + $camelcase{$var} = 1; + CHK("CAMELCASE", + "Avoid CamelCase: <$var>\n" . $herecurr); + } } } -- cgit v1.2.3 From 7d0b6594e1055e3d4efcc28af11a8e42dd85ded4 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 3 Jul 2013 15:05:35 -0700 Subject: checkpatch: allow longer logging function names The current $logFunction regular expression allows names like dev_warn, e_dbg, netdev_info, etc, but some log functions are now written like e_dev_warn, so allow 1 or 2 word blocks with an underscore before the logging level. Signed-off-by: Jacob Keller Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher Signed-off-by: Joe Perches Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/checkpatch.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 70e7f7cdb452..0d94853079ec 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -281,7 +281,7 @@ our $typeTypedefs = qr{(?x: our $logFunctions = qr{(?x: printk(?:_ratelimited|_once|)| - [a-z0-9]+_(?:printk|emerg|alert|crit|err|warning|warn|notice|info|debug|dbg|vdbg|devel|cont|WARN)(?:_ratelimited|_once|)| + (?:[a-z0-9]+_){1,2}(?:printk|emerg|alert|crit|err|warning|warn|notice|info|debug|dbg|vdbg|devel|cont|WARN)(?:_ratelimited|_once|)| WARN(?:_RATELIMIT|_ONCE|)| panic| MODULE_[A-Z_]+ -- cgit v1.2.3 From 351b2a1fe2d06f44b4c06d377744b2ac408b7407 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 3 Jul 2013 15:05:36 -0700 Subject: checkpatch: cache last camelcase hash as .checkpatch-camelcase. Add a file to cache the CamelCase variables found by to reduce the time it takes to scan the include/ directory. Filename is '.checkpatch-camelcase.' and it is created only only if a .git directory exists. is determined by the last non-merge commit id in the include/ path. Reduces checkpatch run time by ~12 cpu seconds on my little netbook. Signed-off-by: Joe Perches Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/checkpatch.pl | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 0d94853079ec..6afcd1239ca5 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -399,7 +399,23 @@ sub seed_camelcase_includes { return if ($camelcase_seeded); my $files; + my $camelcase_git_file = ""; + if (-d ".git") { + my $git_last_include_commit = `git log --no-merges --pretty=format:"%h%n" -1 -- include`; + chomp $git_last_include_commit; + $camelcase_git_file = ".checkpatch-camelcase.$git_last_include_commit"; + if (-f $camelcase_git_file) { + open(my $camelcase_file, '<', "$camelcase_git_file") + or warn "$P: Can't read '$camelcase_git_file' $!\n"; + while (<$camelcase_file>) { + chomp; + $camelcase{$_} = 1; + } + close($camelcase_file); + + return; + } $files = `git ls-files include`; } else { $files = `find $root/include -name "*.h"`; @@ -409,6 +425,16 @@ sub seed_camelcase_includes { seed_camelcase_file($file); } $camelcase_seeded = 1; + + if ($camelcase_git_file ne "") { + unlink glob ".checkpatch-camelcase.*"; + open(my $camelcase_file, '>', "$camelcase_git_file") + or warn "$P: Can't write '$camelcase_git_file' $!\n"; + foreach (sort { lc($a) cmp lc($b) } keys(%camelcase)) { + print $camelcase_file ("$_\n"); + } + close($camelcase_file); + } } $chk_signoff = 0 if ($file); -- cgit v1.2.3 From ff1c8fac88ab8e7dac65236b277908c7c5c7ab09 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Wed, 3 Jul 2013 15:05:37 -0700 Subject: init: remove permanent string buffer from do_one_initcall() do_one_initcall() uses a 64 byte string buffer to save a message. This buffer is declared static and is only used at boot up and when a module is loaded. As 64 bytes is very small, and this function has very limited scope, there's no reason to waste permanent memory with this string and not just simply put it on the stack. Signed-off-by: Steven Rostedt Cc: Geert Uytterhoeven Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- init/main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/init/main.c b/init/main.c index ec549581d732..f2366533c922 100644 --- a/init/main.c +++ b/init/main.c @@ -655,8 +655,6 @@ static void __init do_ctors(void) bool initcall_debug; core_param(initcall_debug, initcall_debug, bool, 0644); -static char msgbuf[64]; - static int __init_or_module do_one_initcall_debug(initcall_t fn) { ktime_t calltime, delta, rettime; @@ -679,6 +677,7 @@ int __init_or_module do_one_initcall(initcall_t fn) { int count = preempt_count(); int ret; + char msgbuf[64]; if (initcall_debug) ret = do_one_initcall_debug(fn); -- cgit v1.2.3 From ca75b4d8799d46842350596fdc5fce7711610326 Mon Sep 17 00:00:00 2001 From: Toralf Förster Date: Wed, 3 Jul 2013 15:05:38 -0700 Subject: insert missing space in printk line of root_delay MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Trivial, but it really looks better. Signed-off-by: Toralf Förster Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- init/do_mounts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/init/do_mounts.c b/init/do_mounts.c index a2b49f2c1bd8..816014c4627e 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -536,7 +536,7 @@ void __init prepare_namespace(void) int is_floppy; if (root_delay) { - printk(KERN_INFO "Waiting %dsec before mounting root device...\n", + printk(KERN_INFO "Waiting %d sec before mounting root device...\n", root_delay); ssleep(root_delay); } -- cgit v1.2.3 From 10fb46d5f79147620d0afda7d3d51302a1e38191 Mon Sep 17 00:00:00 2001 From: Mathias Krause Date: Wed, 3 Jul 2013 15:05:39 -0700 Subject: kprobes: handle empty/invalid input to debugfs "enabled" file When writing invalid input to 'debug/kprobes/enabled' it'll silently be ignored. Even worse, when writing an empty string to this file, the outcome is purely random as the switch statement will make its decision based on the value of an uninitialized stack variable. Fix this by handling invalid/empty input as error returning -EINVAL. Signed-off-by: Mathias Krause Cc: Ananth N Mavinakayanahalli Cc: Anil S Keshavamurthy Cc: "David S. Miller" Cc: Masami Hiramatsu Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/kprobes.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kernel/kprobes.c b/kernel/kprobes.c index bddf3b201a48..6e33498d665c 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c @@ -2332,6 +2332,7 @@ static ssize_t write_enabled_file_bool(struct file *file, if (copy_from_user(buf, user_buf, buf_size)) return -EFAULT; + buf[buf_size] = '\0'; switch (buf[0]) { case 'y': case 'Y': @@ -2343,6 +2344,8 @@ static ssize_t write_enabled_file_bool(struct file *file, case '0': disarm_all_kprobes(); break; + default: + return -EINVAL; } return count; -- cgit v1.2.3 From ce0b348edfd803d4c2244449599799775591df3b Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:05:40 -0700 Subject: rtc: rtc-88pm80x: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-88pm80x.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/rtc/rtc-88pm80x.c b/drivers/rtc/rtc-88pm80x.c index f3742f364eb8..354c937a5866 100644 --- a/drivers/rtc/rtc-88pm80x.c +++ b/drivers/rtc/rtc-88pm80x.c @@ -345,7 +345,6 @@ out: static int pm80x_rtc_remove(struct platform_device *pdev) { struct pm80x_rtc_info *info = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); pm80x_free_irq(info->chip, info->irq, info); return 0; } -- cgit v1.2.3 From d8d5290a32d4a698db32f233f11bc2021a803746 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:05:41 -0700 Subject: drivers/rtc/rtc-v3020.c: remove redundant goto Remove a redundant goto statement left over during the conversion of this driver to use devm_* APIs. Signed-off-by: Sachin Kamat Cc: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-v3020.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/rtc/rtc-v3020.c b/drivers/rtc/rtc-v3020.c index 6e0cba8f47d5..4ee0e63ebba7 100644 --- a/drivers/rtc/rtc-v3020.c +++ b/drivers/rtc/rtc-v3020.c @@ -320,7 +320,7 @@ static int rtc_probe(struct platform_device *pdev) retval = chip->ops->map_io(chip, pdev, pdata); if (retval) - goto err_chip; + return retval; /* Make sure the v3020 expects a communication cycle * by reading 8 times */ @@ -364,7 +364,7 @@ static int rtc_probe(struct platform_device *pdev) err_io: chip->ops->unmap_io(chip); -err_chip: + return retval; } -- cgit v1.2.3 From 3ff2e13ce1b4172bf7d188799d3a106c0e2bb5cd Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:05:42 -0700 Subject: drivers/rtc/interface.c: fix checkpatch errors Fixes the following types of errors: ERROR: "foo* bar" should be "foo *bar" ERROR: else should follow close brace '}' WARNING: braces {} are not necessary for single statement blocks Signed-off-by: Sachin Kamat Cc: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/interface.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 42bd57da239d..14c1efdd0757 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -109,9 +109,9 @@ int rtc_set_mmss(struct rtc_device *rtc, unsigned long secs) err = rtc->ops->set_time(rtc->dev.parent, &new); } - } - else + } else { err = -EINVAL; + } mutex_unlock(&rtc->ops_lock); /* A timer might have just expired */ @@ -367,14 +367,14 @@ int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) err = mutex_lock_interruptible(&rtc->ops_lock); if (err) return err; - if (rtc->aie_timer.enabled) { + if (rtc->aie_timer.enabled) rtc_timer_remove(rtc, &rtc->aie_timer); - } + rtc->aie_timer.node.expires = rtc_tm_to_ktime(alarm->time); rtc->aie_timer.period = ktime_set(0, 0); - if (alarm->enabled) { + if (alarm->enabled) err = rtc_timer_enqueue(rtc, &rtc->aie_timer); - } + mutex_unlock(&rtc->ops_lock); return err; } @@ -891,7 +891,7 @@ again: * * Kernel interface to initializing an rtc_timer. */ -void rtc_timer_init(struct rtc_timer *timer, void (*f)(void* p), void* data) +void rtc_timer_init(struct rtc_timer *timer, void (*f)(void *p), void *data) { timerqueue_init(&timer->node); timer->enabled = 0; @@ -907,7 +907,7 @@ void rtc_timer_init(struct rtc_timer *timer, void (*f)(void* p), void* data) * * Kernel interface to set an rtc_timer */ -int rtc_timer_start(struct rtc_device *rtc, struct rtc_timer* timer, +int rtc_timer_start(struct rtc_device *rtc, struct rtc_timer *timer, ktime_t expires, ktime_t period) { int ret = 0; @@ -930,7 +930,7 @@ int rtc_timer_start(struct rtc_device *rtc, struct rtc_timer* timer, * * Kernel interface to cancel an rtc_timer */ -int rtc_timer_cancel(struct rtc_device *rtc, struct rtc_timer* timer) +int rtc_timer_cancel(struct rtc_device *rtc, struct rtc_timer *timer) { int ret = 0; mutex_lock(&rtc->ops_lock); -- cgit v1.2.3 From 365c411d812a67ae840c7fadd48a6813355b95c4 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:05:43 -0700 Subject: drivers/rtc/rtc-at32ap700x.c: fix checkpatch error Fixes the following error: ERROR: space required before the open parenthesis '(' Signed-off-by: Sachin Kamat Cc: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-at32ap700x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-at32ap700x.c b/drivers/rtc/rtc-at32ap700x.c index f47fbb5eee8b..128896da1a14 100644 --- a/drivers/rtc/rtc-at32ap700x.c +++ b/drivers/rtc/rtc-at32ap700x.c @@ -141,7 +141,7 @@ static int at32_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) spin_lock_irq(&rtc->lock); - if(enabled) { + if (enabled) { if (rtc_readl(rtc, VAL) > rtc->alarm_time) { ret = -EINVAL; goto out; -- cgit v1.2.3 From 8ecc0bf41a8e3ddd40e0fbb7b3045f553e8aad41 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:05:44 -0700 Subject: drivers/rtc/rtc-at91rm9200.c: include Silences the following checkpatch warning: WARNING: Use #include instead of Signed-off-by: Sachin Kamat Cc: Andrew Victor Cc: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-at91rm9200.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/rtc/rtc-at91rm9200.c b/drivers/rtc/rtc-at91rm9200.c index f296f3f7db9b..e87a81c8678e 100644 --- a/drivers/rtc/rtc-at91rm9200.c +++ b/drivers/rtc/rtc-at91rm9200.c @@ -31,8 +31,7 @@ #include #include #include - -#include +#include #include "rtc-at91rm9200.h" -- cgit v1.2.3 From 5e8599d21bdd8010e43f407c0f0cbef32f23bea8 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:05:45 -0700 Subject: drivers/rtc/rtc-cmos.c: fix whitespace related errors Fixes the following types of issues: ERROR: space required after that ',' (ctx:VxV) WARNING: please, no spaces at the start of a line Signed-off-by: Sachin Kamat Cc: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-cmos.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index f1cb706445c7..a6727d977330 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -326,7 +326,7 @@ static void cmos_irq_disable(struct cmos_rtc *cmos, unsigned char mask) static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t) { struct cmos_rtc *cmos = dev_get_drvdata(dev); - unsigned char mon, mday, hrs, min, sec, rtc_control; + unsigned char mon, mday, hrs, min, sec, rtc_control; if (!is_valid_irq(cmos->irq)) return -EIO; @@ -691,7 +691,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) /* FIXME: * doesn't know 12-hour mode either. */ - if (is_valid_irq(rtc_irq) && !(rtc_control & RTC_24H)) { + if (is_valid_irq(rtc_irq) && !(rtc_control & RTC_24H)) { dev_warn(dev, "only 24-hr supported\n"); retval = -ENXIO; goto cleanup1; @@ -991,7 +991,7 @@ static int cmos_pnp_probe(struct pnp_dev *pnp, const struct pnp_device_id *id) { cmos_wake_setup(&pnp->dev); - if (pnp_port_start(pnp,0) == 0x70 && !pnp_irq_valid(pnp,0)) + if (pnp_port_start(pnp, 0) == 0x70 && !pnp_irq_valid(pnp, 0)) /* Some machines contain a PNP entry for the RTC, but * don't define the IRQ. It should always be safe to * hardcode it in these cases -- cgit v1.2.3 From 48c48180de5a1292c5ce40d6ad8fc21e7d1c86a8 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:05:46 -0700 Subject: drivers/rtc/rtc-davinci.c: fix whitespace warning Silences the following warning: WARNING: please, no space before tabs Signed-off-by: Sachin Kamat Cc: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-davinci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-davinci.c b/drivers/rtc/rtc-davinci.c index a55048c3e26f..be5fc32d3cd7 100644 --- a/drivers/rtc/rtc-davinci.c +++ b/drivers/rtc/rtc-davinci.c @@ -117,7 +117,7 @@ static DEFINE_SPINLOCK(davinci_rtc_lock); struct davinci_rtc { - struct rtc_device *rtc; + struct rtc_device *rtc; void __iomem *base; resource_size_t pbase; size_t base_size; -- cgit v1.2.3 From 465008fa46dc73c54ee281b02047275539eb8ab4 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:05:47 -0700 Subject: drivers/rtc/rtc-ds1305.c: add missing braces around sizeof Silences the following type of warnings: WARNING: sizeof buf should be sizeof(buf) Signed-off-by: Sachin Kamat Cc: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-ds1305.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/rtc/rtc-ds1305.c b/drivers/rtc/rtc-ds1305.c index bb5f13f63630..dd6170acde95 100644 --- a/drivers/rtc/rtc-ds1305.c +++ b/drivers/rtc/rtc-ds1305.c @@ -158,7 +158,7 @@ static int ds1305_alarm_irq_enable(struct device *dev, unsigned int enabled) goto done; buf[1] &= ~DS1305_AEI0; } - err = spi_write_then_read(ds1305->spi, buf, sizeof buf, NULL, 0); + err = spi_write_then_read(ds1305->spi, buf, sizeof(buf), NULL, 0); if (err >= 0) ds1305->ctrl[0] = buf[1]; done: @@ -181,8 +181,8 @@ static int ds1305_get_time(struct device *dev, struct rtc_time *time) /* Use write-then-read to get all the date/time registers * since dma from stack is nonportable */ - status = spi_write_then_read(ds1305->spi, &addr, sizeof addr, - buf, sizeof buf); + status = spi_write_then_read(ds1305->spi, &addr, sizeof(addr), + buf, sizeof(buf)); if (status < 0) return status; @@ -237,7 +237,7 @@ static int ds1305_set_time(struct device *dev, struct rtc_time *time) buf[4], buf[5], buf[6], buf[7]); /* use write-then-read since dma from stack is nonportable */ - return spi_write_then_read(ds1305->spi, buf, sizeof buf, + return spi_write_then_read(ds1305->spi, buf, sizeof(buf), NULL, 0); } @@ -286,8 +286,8 @@ static int ds1305_get_alarm(struct device *dev, struct rtc_wkalrm *alm) * of EFI status is at best fragile anyway (given IRQ handlers). */ addr = DS1305_CONTROL; - status = spi_write_then_read(spi, &addr, sizeof addr, - ds1305->ctrl, sizeof ds1305->ctrl); + status = spi_write_then_read(spi, &addr, sizeof(addr), + ds1305->ctrl, sizeof(ds1305->ctrl)); if (status < 0) return status; @@ -296,8 +296,8 @@ static int ds1305_get_alarm(struct device *dev, struct rtc_wkalrm *alm) /* get and check ALM0 registers */ addr = DS1305_ALM0(DS1305_SEC); - status = spi_write_then_read(spi, &addr, sizeof addr, - buf, sizeof buf); + status = spi_write_then_read(spi, &addr, sizeof(addr), + buf, sizeof(buf)); if (status < 0) return status; @@ -381,7 +381,7 @@ static int ds1305_set_alarm(struct device *dev, struct rtc_wkalrm *alm) "alm0 write", buf[1 + DS1305_SEC], buf[1 + DS1305_MIN], buf[1 + DS1305_HOUR], buf[1 + DS1305_WDAY]); - status = spi_write_then_read(spi, buf, sizeof buf, NULL, 0); + status = spi_write_then_read(spi, buf, sizeof(buf), NULL, 0); if (status < 0) return status; @@ -474,7 +474,7 @@ static void ds1305_work(struct work_struct *work) buf[1] = ds1305->ctrl[0]; buf[2] = 0; - status = spi_write_then_read(spi, buf, sizeof buf, + status = spi_write_then_read(spi, buf, sizeof(buf), NULL, 0); if (status < 0) dev_dbg(&spi->dev, "clear irq --> %d\n", status); @@ -627,8 +627,8 @@ static int ds1305_probe(struct spi_device *spi) /* read and cache control registers */ addr = DS1305_CONTROL; - status = spi_write_then_read(spi, &addr, sizeof addr, - ds1305->ctrl, sizeof ds1305->ctrl); + status = spi_write_then_read(spi, &addr, sizeof(addr), + ds1305->ctrl, sizeof(ds1305->ctrl)); if (status < 0) { dev_dbg(&spi->dev, "can't %s, %d\n", "read", status); @@ -659,7 +659,7 @@ static int ds1305_probe(struct spi_device *spi) buf[0] = DS1305_WRITE | DS1305_CONTROL; buf[1] = ds1305->ctrl[0]; - status = spi_write_then_read(spi, buf, sizeof buf, NULL, 0); + status = spi_write_then_read(spi, buf, sizeof(buf), NULL, 0); dev_dbg(&spi->dev, "clear WP --> %d\n", status); if (status < 0) @@ -713,7 +713,7 @@ static int ds1305_probe(struct spi_device *spi) buf[1] = ds1305->ctrl[0]; buf[2] = ds1305->ctrl[1]; buf[3] = ds1305->ctrl[2]; - status = spi_write_then_read(spi, buf, sizeof buf, NULL, 0); + status = spi_write_then_read(spi, buf, sizeof(buf), NULL, 0); if (status < 0) { dev_dbg(&spi->dev, "can't %s, %d\n", "write", status); @@ -725,8 +725,8 @@ static int ds1305_probe(struct spi_device *spi) /* see if non-Linux software set up AM/PM mode */ addr = DS1305_HOUR; - status = spi_write_then_read(spi, &addr, sizeof addr, - &value, sizeof value); + status = spi_write_then_read(spi, &addr, sizeof(addr), + &value, sizeof(value)); if (status < 0) { dev_dbg(&spi->dev, "read HOUR --> %d\n", status); return status; -- cgit v1.2.3 From adc7b9b68dbd914199867327a0a6113492eda94b Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:05:48 -0700 Subject: drivers/rtc/rtc-ds1374.c: fix spacing related issues Fixes the following types of issues: ERROR: code indent should use tabs where possible WARNING: please, no spaces at the start of a line Signed-off-by: Sachin Kamat Cc: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-ds1374.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/rtc/rtc-ds1374.c b/drivers/rtc/rtc-ds1374.c index 94366e12f40f..9e6e14fb53d7 100644 --- a/drivers/rtc/rtc-ds1374.c +++ b/drivers/rtc/rtc-ds1374.c @@ -65,7 +65,7 @@ struct ds1374 { static struct i2c_driver ds1374_driver; static int ds1374_read_rtc(struct i2c_client *client, u32 *time, - int reg, int nbytes) + int reg, int nbytes) { u8 buf[4]; int ret; @@ -90,7 +90,7 @@ static int ds1374_read_rtc(struct i2c_client *client, u32 *time, } static int ds1374_write_rtc(struct i2c_client *client, u32 time, - int reg, int nbytes) + int reg, int nbytes) { u8 buf[4]; int i; @@ -119,8 +119,7 @@ static int ds1374_check_rtc_status(struct i2c_client *client) if (stat & DS1374_REG_SR_OSF) dev_warn(&client->dev, - "oscillator discontinuity flagged, " - "time unreliable\n"); + "oscillator discontinuity flagged, time unreliable\n"); stat &= ~(DS1374_REG_SR_OSF | DS1374_REG_SR_AF); @@ -363,7 +362,7 @@ static int ds1374_probe(struct i2c_client *client, if (client->irq > 0) { ret = devm_request_irq(&client->dev, client->irq, ds1374_irq, 0, - "ds1374", client); + "ds1374", client); if (ret) { dev_err(&client->dev, "unable to request IRQ\n"); return ret; @@ -373,7 +372,7 @@ static int ds1374_probe(struct i2c_client *client, } ds1374->rtc = devm_rtc_device_register(&client->dev, client->name, - &ds1374_rtc_ops, THIS_MODULE); + &ds1374_rtc_ops, THIS_MODULE); if (IS_ERR(ds1374->rtc)) { dev_err(&client->dev, "unable to register the class device\n"); return PTR_ERR(ds1374->rtc); -- cgit v1.2.3 From 7b2f0053b7dad07e1a18d0e46bc7ef4fb8ac564c Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:05:49 -0700 Subject: drivers/rtc/rtc-ds1511.c: fix issues related to spaces and braces Fixes the following types of issues: WARNING: please, no spaces at the start of a line WARNING: braces {} are not necessary for single statement blocks Signed-off-by: Sachin Kamat Cc: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-ds1511.c | 93 +++++++++++++++++++++++------------------------- 1 file changed, 44 insertions(+), 49 deletions(-) diff --git a/drivers/rtc/rtc-ds1511.c b/drivers/rtc/rtc-ds1511.c index 6ce8a997cf51..308a8fefe76f 100644 --- a/drivers/rtc/rtc-ds1511.c +++ b/drivers/rtc/rtc-ds1511.c @@ -104,31 +104,31 @@ static DEFINE_SPINLOCK(ds1511_lock); static __iomem char *ds1511_base; static u32 reg_spacing = 1; - static noinline void +static noinline void rtc_write(uint8_t val, uint32_t reg) { writeb(val, ds1511_base + (reg * reg_spacing)); } - static inline void +static inline void rtc_write_alarm(uint8_t val, enum ds1511reg reg) { rtc_write((val | 0x80), reg); } - static noinline uint8_t +static noinline uint8_t rtc_read(enum ds1511reg reg) { return readb(ds1511_base + (reg * reg_spacing)); } - static inline void +static inline void rtc_disable_update(void) { rtc_write((rtc_read(RTC_CMD) & ~RTC_TE), RTC_CMD); } - static void +static void rtc_enable_update(void) { rtc_write((rtc_read(RTC_CMD) | RTC_TE), RTC_CMD); @@ -145,7 +145,7 @@ rtc_enable_update(void) * just enough code to set the watchdog timer so that it * will reboot the system */ - void +void ds1511_wdog_set(unsigned long deciseconds) { /* @@ -163,7 +163,7 @@ ds1511_wdog_set(unsigned long deciseconds) rtc_write(DS1511_WDE | DS1511_WDS, RTC_CMD); } - void +void ds1511_wdog_disable(void) { /* @@ -191,13 +191,12 @@ static int ds1511_rtc_set_time(struct device *dev, struct rtc_time *rtc_tm) /* * won't have to change this for a while */ - if (rtc_tm->tm_year < 1900) { + if (rtc_tm->tm_year < 1900) rtc_tm->tm_year += 1900; - } - if (rtc_tm->tm_year < 1970) { + if (rtc_tm->tm_year < 1970) return -EINVAL; - } + yrs = rtc_tm->tm_year % 100; cen = rtc_tm->tm_year / 100; mon = rtc_tm->tm_mon + 1; /* tm_mon starts at zero */ @@ -207,17 +206,14 @@ static int ds1511_rtc_set_time(struct device *dev, struct rtc_time *rtc_tm) min = rtc_tm->tm_min; sec = rtc_tm->tm_sec; - if ((mon > 12) || (day == 0)) { + if ((mon > 12) || (day == 0)) return -EINVAL; - } - if (day > rtc_month_days(rtc_tm->tm_mon, rtc_tm->tm_year)) { + if (day > rtc_month_days(rtc_tm->tm_mon, rtc_tm->tm_year)) return -EINVAL; - } - if ((hrs >= 24) || (min >= 60) || (sec >= 60)) { + if ((hrs >= 24) || (min >= 60) || (sec >= 60)) return -EINVAL; - } /* * each register is a different number of valid bits @@ -299,7 +295,7 @@ static int ds1511_rtc_read_time(struct device *dev, struct rtc_time *rtc_tm) * date/hours/mins/secs matches. the ds1511 has many more * permutations, but the kernel doesn't. */ - static void +static void ds1511_rtc_update_alarm(struct rtc_plat_data *pdata) { unsigned long flags; @@ -322,7 +318,7 @@ ds1511_rtc_update_alarm(struct rtc_plat_data *pdata) spin_unlock_irqrestore(&pdata->lock, flags); } - static int +static int ds1511_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) { struct platform_device *pdev = to_platform_device(dev); @@ -335,14 +331,14 @@ ds1511_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) pdata->alrm_hour = alrm->time.tm_hour; pdata->alrm_min = alrm->time.tm_min; pdata->alrm_sec = alrm->time.tm_sec; - if (alrm->enabled) { + if (alrm->enabled) pdata->irqen |= RTC_AF; - } + ds1511_rtc_update_alarm(pdata); return 0; } - static int +static int ds1511_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) { struct platform_device *pdev = to_platform_device(dev); @@ -359,7 +355,7 @@ ds1511_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) return 0; } - static irqreturn_t +static irqreturn_t ds1511_interrupt(int irq, void *dev_id) { struct platform_device *pdev = dev_id; @@ -406,7 +402,7 @@ static const struct rtc_class_ops ds1511_rtc_ops = { .alarm_irq_enable = ds1511_rtc_alarm_irq_enable, }; - static ssize_t +static ssize_t ds1511_nvram_read(struct file *filp, struct kobject *kobj, struct bin_attribute *ba, char *buf, loff_t pos, size_t size) @@ -417,26 +413,26 @@ ds1511_nvram_read(struct file *filp, struct kobject *kobj, * if count is more than one, turn on "burst" mode * turn it off when you're done */ - if (size > 1) { + if (size > 1) rtc_write((rtc_read(RTC_CMD) | DS1511_BME), RTC_CMD); - } - if (pos > DS1511_RAM_MAX) { + + if (pos > DS1511_RAM_MAX) pos = DS1511_RAM_MAX; - } - if (size + pos > DS1511_RAM_MAX + 1) { + + if (size + pos > DS1511_RAM_MAX + 1) size = DS1511_RAM_MAX - pos + 1; - } + rtc_write(pos, DS1511_RAMADDR_LSB); - for (count = 0; size > 0; count++, size--) { + for (count = 0; size > 0; count++, size--) *buf++ = rtc_read(DS1511_RAMDATA); - } - if (count > 1) { + + if (count > 1) rtc_write((rtc_read(RTC_CMD) & ~DS1511_BME), RTC_CMD); - } + return count; } - static ssize_t +static ssize_t ds1511_nvram_write(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t size) @@ -447,22 +443,22 @@ ds1511_nvram_write(struct file *filp, struct kobject *kobj, * if count is more than one, turn on "burst" mode * turn it off when you're done */ - if (size > 1) { + if (size > 1) rtc_write((rtc_read(RTC_CMD) | DS1511_BME), RTC_CMD); - } - if (pos > DS1511_RAM_MAX) { + + if (pos > DS1511_RAM_MAX) pos = DS1511_RAM_MAX; - } - if (size + pos > DS1511_RAM_MAX + 1) { + + if (size + pos > DS1511_RAM_MAX + 1) size = DS1511_RAM_MAX - pos + 1; - } + rtc_write(pos, DS1511_RAMADDR_LSB); - for (count = 0; size > 0; count++, size--) { + for (count = 0; size > 0; count++, size--) rtc_write(*buf++, DS1511_RAMDATA); - } - if (count > 1) { + + if (count > 1) rtc_write((rtc_read(RTC_CMD) & ~DS1511_BME), RTC_CMD); - } + return count; } @@ -484,9 +480,9 @@ static int ds1511_rtc_probe(struct platform_device *pdev) int ret = 0; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { + if (!res) return -ENODEV; - } + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return -ENOMEM; @@ -518,9 +514,8 @@ static int ds1511_rtc_probe(struct platform_device *pdev) /* * check for a dying bat-tree */ - if (rtc_read(RTC_CMD1) & DS1511_BLF1) { + if (rtc_read(RTC_CMD1) & DS1511_BLF1) dev_warn(&pdev->dev, "voltage-low detected.\n"); - } spin_lock_init(&pdata->lock); platform_set_drvdata(pdev, pdata); -- cgit v1.2.3 From 97dd89693d1bc30060a5096c3c92274931e66f45 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:05:50 -0700 Subject: drivers/rtc/rtc-ds3234.c: fix whitespace issue Fixes the following warning: WARNING: please, no space before tabs Signed-off-by: Sachin Kamat Cc: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-ds3234.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-ds3234.c b/drivers/rtc/rtc-ds3234.c index ba98c0e9580d..67f1a80112e2 100644 --- a/drivers/rtc/rtc-ds3234.c +++ b/drivers/rtc/rtc-ds3234.c @@ -73,7 +73,7 @@ static int ds3234_read_time(struct device *dev, struct rtc_time *dt) dt->tm_wday = bcd2bin(buf[3]) - 1; /* 0 = Sun */ dt->tm_mday = bcd2bin(buf[4]); dt->tm_mon = bcd2bin(buf[5] & 0x1f) - 1; /* 0 = Jan */ - dt->tm_year = bcd2bin(buf[6] & 0xff) + 100; /* Assume 20YY */ + dt->tm_year = bcd2bin(buf[6] & 0xff) + 100; /* Assume 20YY */ return rtc_valid_tm(dt); } -- cgit v1.2.3 From c865e9220da35d484fb44c9f2c291593637d139c Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:05:51 -0700 Subject: drivers/rtc/rtc-fm3130.c: fix whitespace related issue Silences the following checkpatch warning: WARNING: please, no space before tabs Signed-off-by: Sachin Kamat Cc: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-fm3130.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-fm3130.c b/drivers/rtc/rtc-fm3130.c index 2835fb6c1965..8f053daf0f0c 100644 --- a/drivers/rtc/rtc-fm3130.c +++ b/drivers/rtc/rtc-fm3130.c @@ -47,7 +47,7 @@ struct fm3130 { u8 reg_addr_time; - u8 reg_addr_alarm; + u8 reg_addr_alarm; u8 regs[15]; struct i2c_msg msg[4]; struct i2c_client *client; -- cgit v1.2.3 From e46527d289a8e7dbdf9249c64a5245cc7a7ee699 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:05:52 -0700 Subject: drivers/rtc/rtc-m41t80.c: fix spacing related issue Silences the following checkpatch warning: WARNING: space prohibited before semicolon Signed-off-by: Sachin Kamat Cc: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-m41t80.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c index 89674b5e6efd..a5248aa1abf1 100644 --- a/drivers/rtc/rtc-m41t80.c +++ b/drivers/rtc/rtc-m41t80.c @@ -168,7 +168,7 @@ static int m41t80_set_datetime(struct i2c_client *client, struct rtc_time *tm) buf[M41T80_REG_MIN] = bin2bcd(tm->tm_min) | (buf[M41T80_REG_MIN] & ~0x7f); buf[M41T80_REG_HOUR] = - bin2bcd(tm->tm_hour) | (buf[M41T80_REG_HOUR] & ~0x3f) ; + bin2bcd(tm->tm_hour) | (buf[M41T80_REG_HOUR] & ~0x3f); buf[M41T80_REG_WDAY] = (tm->tm_wday & 0x07) | (buf[M41T80_REG_WDAY] & ~0x07); buf[M41T80_REG_DAY] = -- cgit v1.2.3 From 83246a168d65a28f98bf71bb4e267dc5f131b12c Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:05:54 -0700 Subject: drivers/rtc/rtc-max6902.c: remove unwanted spaces Silences the following type of warnings: WARNING: space prohibited between function name and open parenthesis '(' Signed-off-by: Sachin Kamat Cc: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-max6902.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/rtc/rtc-max6902.c b/drivers/rtc/rtc-max6902.c index e3aea00c3145..e9dd56c8ffe7 100644 --- a/drivers/rtc/rtc-max6902.c +++ b/drivers/rtc/rtc-max6902.c @@ -159,7 +159,7 @@ static struct spi_driver max6902_driver = { module_spi_driver(max6902_driver); -MODULE_DESCRIPTION ("max6902 spi RTC driver"); -MODULE_AUTHOR ("Raphael Assenat"); -MODULE_LICENSE ("GPL"); +MODULE_DESCRIPTION("max6902 spi RTC driver"); +MODULE_AUTHOR("Raphael Assenat"); +MODULE_LICENSE("GPL"); MODULE_ALIAS("spi:rtc-max6902"); -- cgit v1.2.3 From cdf5f4ac63785e48d93137566d003d6c839b1259 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:05:55 -0700 Subject: drivers/rtc/rtc-max77686.c: remove space before semicolon Fixes the following warning: WARNING: space prohibited before semicolon Signed-off-by: Sachin Kamat Cc: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-max77686.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-max77686.c b/drivers/rtc/rtc-max77686.c index 771812d62e6b..441c5c2e0bfc 100644 --- a/drivers/rtc/rtc-max77686.c +++ b/drivers/rtc/rtc-max77686.c @@ -119,7 +119,7 @@ static int max77686_rtc_tm_to_data(struct rtc_time *tm, u8 *data) data[RTC_WEEKDAY] = 1 << tm->tm_wday; data[RTC_DATE] = tm->tm_mday; data[RTC_MONTH] = tm->tm_mon + 1; - data[RTC_YEAR] = tm->tm_year > 100 ? (tm->tm_year - 100) : 0 ; + data[RTC_YEAR] = tm->tm_year > 100 ? (tm->tm_year - 100) : 0; if (tm->tm_year < 100) { pr_warn("%s: MAX77686 RTC cannot handle the year %d." -- cgit v1.2.3 From f887a9de5522487debc7601595f4ef791572c044 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:05:56 -0700 Subject: drivers/rtc/rtc-max8997.c: remove space before semicolon Fixes the following warning: WARNING: space prohibited before semicolon Signed-off-by: Sachin Kamat Cc: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-max8997.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-max8997.c b/drivers/rtc/rtc-max8997.c index dacf48db7925..168390428f86 100644 --- a/drivers/rtc/rtc-max8997.c +++ b/drivers/rtc/rtc-max8997.c @@ -104,7 +104,7 @@ static int max8997_rtc_tm_to_data(struct rtc_time *tm, u8 *data) data[RTC_WEEKDAY] = 1 << tm->tm_wday; data[RTC_DATE] = tm->tm_mday; data[RTC_MONTH] = tm->tm_mon + 1; - data[RTC_YEAR] = tm->tm_year > 100 ? (tm->tm_year - 100) : 0 ; + data[RTC_YEAR] = tm->tm_year > 100 ? (tm->tm_year - 100) : 0; if (tm->tm_year < 100) { pr_warn("%s: MAX8997 RTC cannot handle the year %d." -- cgit v1.2.3 From 07d27f2df56e6b8937fba13909f4eb5685211bb4 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:05:57 -0700 Subject: drivers/rtc/rtc-mpc5121.c: remove space before tab WARNING: please, no space before tabs Signed-off-by: Sachin Kamat Cc: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-mpc5121.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-mpc5121.c b/drivers/rtc/rtc-mpc5121.c index bdcc60830aec..213006bfc69a 100644 --- a/drivers/rtc/rtc-mpc5121.c +++ b/drivers/rtc/rtc-mpc5121.c @@ -68,7 +68,7 @@ struct mpc5121_rtc_regs { u32 target_time; /* RTC + 0x20 */ /* * actual_time: - * readonly time since VBAT_RTC was last connected + * readonly time since VBAT_RTC was last connected */ u32 actual_time; /* RTC + 0x24 */ u32 keep_alive; /* RTC + 0x28 */ -- cgit v1.2.3 From 2600f7154b5ab257d3e5c0a2990d5d951758ec78 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:05:58 -0700 Subject: drivers/rtc/rtc-msm6242.c: use pr_warn pr_warn is preferred to pr_warning. Signed-off-by: Sachin Kamat Cc: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-msm6242.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/rtc/rtc-msm6242.c b/drivers/rtc/rtc-msm6242.c index 771f86a05d14..f0e9893ae7be 100644 --- a/drivers/rtc/rtc-msm6242.c +++ b/drivers/rtc/rtc-msm6242.c @@ -111,8 +111,8 @@ static void msm6242_lock(struct msm6242_priv *priv) } if (!cnt) - pr_warning("msm6242: timed out waiting for RTC (0x%x)\n", - msm6242_read(priv, MSM6242_CD)); + pr_warn("msm6242: timed out waiting for RTC (0x%x)\n", + msm6242_read(priv, MSM6242_CD)); } static void msm6242_unlock(struct msm6242_priv *priv) -- cgit v1.2.3 From 4a8282d028b69b94ce86c3860be43a7608a0fd2c Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:05:59 -0700 Subject: drivers/rtc/rtc-mxc.c: fix checkpatch error Fixes the following error: ERROR: spaces required around that '>=' (ctx:WxV) Signed-off-by: Sachin Kamat Cc: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-mxc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-mxc.c b/drivers/rtc/rtc-mxc.c index 9a3895bc4f4d..bf00d6dbeadd 100644 --- a/drivers/rtc/rtc-mxc.c +++ b/drivers/rtc/rtc-mxc.c @@ -436,7 +436,7 @@ static int mxc_rtc_probe(struct platform_device *pdev) pdata->irq = -1; } - if (pdata->irq >=0) + if (pdata->irq >= 0) device_init_wakeup(&pdev->dev, 1); rtc = devm_rtc_device_register(&pdev->dev, pdev->name, &mxc_rtc_ops, -- cgit v1.2.3 From 4b30c9fca71b5f74fb53992791778b8c8ddf0ac5 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:06:00 -0700 Subject: drivers/rtc/rtc-omap.c: include instead of Use #include instead of as pointed out by checkpatch. Signed-off-by: Sachin Kamat Cc: George G. Davis Cc: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-omap.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c index b0ba3fc991ea..6f6ac033d5d9 100644 --- a/drivers/rtc/rtc-omap.c +++ b/drivers/rtc/rtc-omap.c @@ -23,9 +23,7 @@ #include #include #include - -#include - +#include /* The OMAP1 RTC is a year/month/day/hours/minutes/seconds BCD clock * with century-range alarm matching, driven by the 32kHz clock. -- cgit v1.2.3 From 369015fbd40655f86384a3e7d5452f9c61eb26e9 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:06:01 -0700 Subject: drivers/rtc/rtc-pcf2123.c: remove space before tabs Silences the following checkpatch warning: WARNING: please, no space before tabs Signed-off-by: Sachin Kamat Cc: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-pcf2123.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/rtc/rtc-pcf2123.c b/drivers/rtc/rtc-pcf2123.c index 796a6c5067dd..b2a78a02cf16 100644 --- a/drivers/rtc/rtc-pcf2123.c +++ b/drivers/rtc/rtc-pcf2123.c @@ -18,11 +18,11 @@ * should look something like: * * static struct spi_board_info ek_spi_devices[] = { - * ... - * { - * .modalias = "rtc-pcf2123", - * .chip_select = 1, - * .controller_data = (void *)AT91_PIN_PA10, + * ... + * { + * .modalias = "rtc-pcf2123", + * .chip_select = 1, + * .controller_data = (void *)AT91_PIN_PA10, * .max_speed_hz = 1000 * 1000, * .mode = SPI_CS_HIGH, * .bus_num = 0, -- cgit v1.2.3 From d1bda80afd36c5eeb79a8f5a53c9b3e23350d3a9 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:06:02 -0700 Subject: drivers/rtc/rtc-pcf8583.c: move assignment outside if condition Fixes the following checkpatch error: ERROR: do not use assignment in if condition Signed-off-by: Sachin Kamat Cc: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-pcf8583.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-pcf8583.c b/drivers/rtc/rtc-pcf8583.c index 95886dcf4a39..9971f7f7cac8 100644 --- a/drivers/rtc/rtc-pcf8583.c +++ b/drivers/rtc/rtc-pcf8583.c @@ -188,7 +188,8 @@ static int pcf8583_rtc_read_time(struct device *dev, struct rtc_time *tm) dev_warn(dev, "resetting control %02x -> %02x\n", ctrl, new_ctrl); - if ((err = pcf8583_set_ctrl(client, &new_ctrl)) < 0) + err = pcf8583_set_ctrl(client, &new_ctrl); + if (err < 0) return err; } -- cgit v1.2.3 From 91b80e4c3887fc26aa6c2c398b45478a09fac561 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:06:03 -0700 Subject: drivers/rtc/rtc-rs5c313.c: include instead of Use #include instead of as pointed out by checkpatch. Signed-off-by: Sachin Kamat Cc: Nobuhiro Iwamatsu Cc: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-rs5c313.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-rs5c313.c b/drivers/rtc/rtc-rs5c313.c index 8089fc63e403..b603cc41ea77 100644 --- a/drivers/rtc/rtc-rs5c313.c +++ b/drivers/rtc/rtc-rs5c313.c @@ -47,7 +47,7 @@ #include #include #include -#include +#include #define DRV_NAME "rs5c313" #define DRV_VERSION "1.13" -- cgit v1.2.3 From 675090fa84368d66f46acb521a3eff5a4318d9ff Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:06:04 -0700 Subject: drivers/rtc/rtc-rs5c313.c: fix spacing related issues Fixes the following types of checkpatch issues: WARNING: please, no space before tabs ERROR: space prohibited after that open parenthesis '(' ERROR: space prohibited before that close parenthesis ')' ERROR: need consistent spacing around '>>' (ctx:VxW) Signed-off-by: Sachin Kamat Cc: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-rs5c313.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/rtc/rtc-rs5c313.c b/drivers/rtc/rtc-rs5c313.c index b603cc41ea77..6af0f1f95f63 100644 --- a/drivers/rtc/rtc-rs5c313.c +++ b/drivers/rtc/rtc-rs5c313.c @@ -50,7 +50,7 @@ #include #define DRV_NAME "rs5c313" -#define DRV_VERSION "1.13" +#define DRV_VERSION "1.13" #ifdef CONFIG_SH_LANDISK /*****************************************************/ @@ -301,7 +301,7 @@ static int rs5c313_rtc_set_time(struct device *dev, struct rtc_time *tm) rs5c313_write_reg(RS5C313_ADDR_SEC10, (data >> 4)); data = bin2bcd(tm->tm_min); - rs5c313_write_reg(RS5C313_ADDR_MIN, data ); + rs5c313_write_reg(RS5C313_ADDR_MIN, data); rs5c313_write_reg(RS5C313_ADDR_MIN10, (data >> 4)); data = bin2bcd(tm->tm_hour); @@ -310,7 +310,7 @@ static int rs5c313_rtc_set_time(struct device *dev, struct rtc_time *tm) data = bin2bcd(tm->tm_mday); rs5c313_write_reg(RS5C313_ADDR_DAY, data); - rs5c313_write_reg(RS5C313_ADDR_DAY10, (data>> 4)); + rs5c313_write_reg(RS5C313_ADDR_DAY10, (data >> 4)); data = bin2bcd(tm->tm_mon + 1); rs5c313_write_reg(RS5C313_ADDR_MON, data); @@ -349,9 +349,9 @@ static void rs5c313_check_xstp_bit(void) } memset(&tm, 0, sizeof(struct rtc_time)); - tm.tm_mday = 1; - tm.tm_mon = 1 - 1; - tm.tm_year = 2000 - 1900; + tm.tm_mday = 1; + tm.tm_mon = 1 - 1; + tm.tm_year = 2000 - 1900; rs5c313_rtc_set_time(NULL, &tm); pr_err("invalid value, resetting to 1 Jan 2000\n"); @@ -388,7 +388,7 @@ static struct platform_driver rs5c313_rtc_platform_driver = { .name = DRV_NAME, .owner = THIS_MODULE, }, - .probe = rs5c313_rtc_probe, + .probe = rs5c313_rtc_probe, .remove = rs5c313_rtc_remove, }; @@ -408,7 +408,7 @@ static int __init rs5c313_rtc_init(void) static void __exit rs5c313_rtc_exit(void) { - platform_driver_unregister( &rs5c313_rtc_platform_driver ); + platform_driver_unregister(&rs5c313_rtc_platform_driver); } module_init(rs5c313_rtc_init); -- cgit v1.2.3 From de2edf32f174529bf172e6535f9e33d51240886a Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:06:05 -0700 Subject: drivers/rtc/rtc-v3020.c: fix spacing issues Fixes the following type of issues: WARNING: please, no space before tabs Signed-off-by: Sachin Kamat Cc: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-v3020.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/rtc/rtc-v3020.c b/drivers/rtc/rtc-v3020.c index 4ee0e63ebba7..d07d89823020 100644 --- a/drivers/rtc/rtc-v3020.c +++ b/drivers/rtc/rtc-v3020.c @@ -16,7 +16,7 @@ * - Use the generic rtc class * * ??-???-2004: Someone at Compulab - * - Initial driver creation. + * - Initial driver creation. * */ #include @@ -278,13 +278,13 @@ static int v3020_set_time(struct device *dev, struct rtc_time *dt) dev_dbg(dev, "tm_year: %i\n", dt->tm_year); /* Write all the values to ram... */ - v3020_set_reg(chip, V3020_SECONDS, bin2bcd(dt->tm_sec)); - v3020_set_reg(chip, V3020_MINUTES, bin2bcd(dt->tm_min)); - v3020_set_reg(chip, V3020_HOURS, bin2bcd(dt->tm_hour)); + v3020_set_reg(chip, V3020_SECONDS, bin2bcd(dt->tm_sec)); + v3020_set_reg(chip, V3020_MINUTES, bin2bcd(dt->tm_min)); + v3020_set_reg(chip, V3020_HOURS, bin2bcd(dt->tm_hour)); v3020_set_reg(chip, V3020_MONTH_DAY, bin2bcd(dt->tm_mday)); - v3020_set_reg(chip, V3020_MONTH, bin2bcd(dt->tm_mon + 1)); - v3020_set_reg(chip, V3020_WEEK_DAY, bin2bcd(dt->tm_wday)); - v3020_set_reg(chip, V3020_YEAR, bin2bcd(dt->tm_year % 100)); + v3020_set_reg(chip, V3020_MONTH, bin2bcd(dt->tm_mon + 1)); + v3020_set_reg(chip, V3020_WEEK_DAY, bin2bcd(dt->tm_wday)); + v3020_set_reg(chip, V3020_YEAR, bin2bcd(dt->tm_year % 100)); /* ...and set the clock. */ v3020_set_reg(chip, V3020_CMD_RAM2CLOCK, 0); -- cgit v1.2.3 From 0218bcf658d0faf98582856b7176e25bdf3fd129 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:06:06 -0700 Subject: drivers/rtc/rtc-vr41xx.c: fix spacing issues Fixes the following types of issues: ERROR: code indent should use tabs where possible Signed-off-by: Sachin Kamat Cc: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-vr41xx.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/rtc/rtc-vr41xx.c b/drivers/rtc/rtc-vr41xx.c index f91be04b9050..b940c2374904 100644 --- a/drivers/rtc/rtc-vr41xx.c +++ b/drivers/rtc/rtc-vr41xx.c @@ -103,7 +103,7 @@ static inline unsigned long read_elapsed_second(void) second_mid = rtc1_read(ETIMEMREG); second_high = rtc1_read(ETIMEHREG); } while (first_low != second_low || first_mid != second_mid || - first_high != second_high); + first_high != second_high); return (first_high << 17) | (first_mid << 1) | (first_low >> 15); } @@ -154,7 +154,7 @@ static int vr41xx_rtc_set_time(struct device *dev, struct rtc_time *time) epoch_sec = mktime(epoch, 1, 1, 0, 0, 0); current_sec = mktime(time->tm_year + 1900, time->tm_mon + 1, time->tm_mday, - time->tm_hour, time->tm_min, time->tm_sec); + time->tm_hour, time->tm_min, time->tm_sec); write_elapsed_second(current_sec - epoch_sec); @@ -186,7 +186,7 @@ static int vr41xx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) struct rtc_time *time = &wkalrm->time; alarm_sec = mktime(time->tm_year + 1900, time->tm_mon + 1, time->tm_mday, - time->tm_hour, time->tm_min, time->tm_sec); + time->tm_hour, time->tm_min, time->tm_sec); spin_lock_irq(&rtc_lock); @@ -334,7 +334,7 @@ static int rtc_probe(struct platform_device *pdev) } retval = request_irq(aie_irq, elapsedtime_interrupt, 0, - "elapsed_time", pdev); + "elapsed_time", pdev); if (retval < 0) goto err_device_unregister; @@ -343,7 +343,7 @@ static int rtc_probe(struct platform_device *pdev) goto err_free_irq; retval = request_irq(pie_irq, rtclong1_interrupt, 0, - "rtclong1", pdev); + "rtclong1", pdev); if (retval < 0) goto err_free_irq; -- cgit v1.2.3 From 66e3f10c993845e0118f4232911b40068ad91886 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:06:07 -0700 Subject: drivers/rtc/rtc-x1205.c: fix checkpatch issues Fixes the following types of issues: ERROR: do not use assignment in if condition ERROR: open brace '{' following struct go on the same line ERROR: else should follow close brace '}' WARNING: please, no space before tabs Signed-off-by: Sachin Kamat Cc: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-x1205.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/drivers/rtc/rtc-x1205.c b/drivers/rtc/rtc-x1205.c index fa9b0679fb60..365dc6505148 100644 --- a/drivers/rtc/rtc-x1205.c +++ b/drivers/rtc/rtc-x1205.c @@ -4,7 +4,7 @@ * Copyright 2005 Alessandro Zummo * * please send all reports to: - * Karen Spearel + * Karen Spearel * Alessandro Zummo * * based on a lot of other RTC drivers. @@ -215,12 +215,14 @@ static int x1205_set_datetime(struct i2c_client *client, struct rtc_time *tm, buf[i] |= 0x80; /* this sequence is required to unlock the chip */ - if ((xfer = i2c_master_send(client, wel, 3)) != 3) { + xfer = i2c_master_send(client, wel, 3); + if (xfer != 3) { dev_err(&client->dev, "%s: wel - %d\n", __func__, xfer); return -EIO; } - if ((xfer = i2c_master_send(client, rwel, 3)) != 3) { + xfer = i2c_master_send(client, rwel, 3); + if (xfer != 3) { dev_err(&client->dev, "%s: rwel - %d\n", __func__, xfer); return -EIO; } @@ -269,7 +271,8 @@ static int x1205_set_datetime(struct i2c_client *client, struct rtc_time *tm, } /* disable further writes */ - if ((xfer = i2c_master_send(client, diswe, 3)) != 3) { + xfer = i2c_master_send(client, diswe, 3); + if (xfer != 3) { dev_err(&client->dev, "%s: diswe - %d\n", __func__, xfer); return -EIO; } @@ -375,8 +378,7 @@ static int x1205_get_atrim(struct i2c_client *client, int *trim) return 0; } -struct x1205_limit -{ +struct x1205_limit { unsigned char reg, mask, min, max; }; @@ -430,7 +432,8 @@ static int x1205_validate_client(struct i2c_client *client) }, }; - if ((xfer = i2c_transfer(client->adapter, msgs, 2)) != 2) { + xfer = i2c_transfer(client->adapter, msgs, 2); + if (xfer != 2) { dev_err(&client->dev, "%s: could not read register %x\n", __func__, probe_zero_pattern[i]); @@ -467,7 +470,8 @@ static int x1205_validate_client(struct i2c_client *client) }, }; - if ((xfer = i2c_transfer(client->adapter, msgs, 2)) != 2) { + xfer = i2c_transfer(client->adapter, msgs, 2); + if (xfer != 2) { dev_err(&client->dev, "%s: could not read register %x\n", __func__, probe_limits_pattern[i].reg); @@ -548,10 +552,12 @@ static int x1205_rtc_proc(struct device *dev, struct seq_file *seq) { int err, dtrim, atrim; - if ((err = x1205_get_dtrim(to_i2c_client(dev), &dtrim)) == 0) + err = x1205_get_dtrim(to_i2c_client(dev), &dtrim); + if (!err) seq_printf(seq, "digital_trim\t: %d ppm\n", dtrim); - if ((err = x1205_get_atrim(to_i2c_client(dev), &atrim)) == 0) + err = x1205_get_atrim(to_i2c_client(dev), &atrim); + if (!err) seq_printf(seq, "analog_trim\t: %d.%02d pF\n", atrim / 1000, atrim % 1000); return 0; @@ -639,7 +645,8 @@ static int x1205_probe(struct i2c_client *client, i2c_set_clientdata(client, rtc); /* Check for power failures and eventually enable the osc */ - if ((err = x1205_get_status(client, &sr)) == 0) { + err = x1205_get_status(client, &sr); + if (!err) { if (sr & X1205_SR_RTCF) { dev_err(&client->dev, "power failure detected, " @@ -647,9 +654,9 @@ static int x1205_probe(struct i2c_client *client, udelay(50); x1205_fix_osc(client); } - } - else + } else { dev_err(&client->dev, "couldn't read status\n"); + } err = x1205_sysfs_register(&client->dev); if (err) -- cgit v1.2.3 From 90da0c0c1ee7f8fc5100de0f97dbb7c6796dc42f Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:08 -0700 Subject: rtc: rtc-88pm860x: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-88pm860x.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/rtc/rtc-88pm860x.c b/drivers/rtc/rtc-88pm860x.c index 0f2b91bfee37..4e30c85728e5 100644 --- a/drivers/rtc/rtc-88pm860x.c +++ b/drivers/rtc/rtc-88pm860x.c @@ -418,7 +418,6 @@ static int pm860x_rtc_remove(struct platform_device *pdev) pm860x_set_bits(info->i2c, PM8607_MEAS_EN2, MEAS2_VRTC, 0); #endif /* VRTC_CALIBRATION */ - platform_set_drvdata(pdev, NULL); return 0; } -- cgit v1.2.3 From 0ffb0c065707123ae19b3a8b37a3662261c421b5 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:09 -0700 Subject: rtc: rtc-ab3100: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Cc: Lars-Peter Clausen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-ab3100.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/rtc/rtc-ab3100.c b/drivers/rtc/rtc-ab3100.c index 47a4f2c4d30e..572208d67688 100644 --- a/drivers/rtc/rtc-ab3100.c +++ b/drivers/rtc/rtc-ab3100.c @@ -242,7 +242,6 @@ static int __init ab3100_rtc_probe(struct platform_device *pdev) static int __exit ab3100_rtc_remove(struct platform_device *pdev) { - platform_set_drvdata(pdev, NULL); return 0; } -- cgit v1.2.3 From 431296a4cf0e9c951457cf3f6786760f63dee7f5 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:10 -0700 Subject: rtc: rtc-ab8500: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Cc: Srinidhi Kasagar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-ab8500.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/rtc/rtc-ab8500.c b/drivers/rtc/rtc-ab8500.c index 63cfa314a39f..c5b62d4389ee 100644 --- a/drivers/rtc/rtc-ab8500.c +++ b/drivers/rtc/rtc-ab8500.c @@ -451,8 +451,6 @@ static int ab8500_rtc_remove(struct platform_device *pdev) { ab8500_sysfs_rtc_unregister(&pdev->dev); - platform_set_drvdata(pdev, NULL); - return 0; } -- cgit v1.2.3 From 46aee0c7829794cdc18ba96d4d0fb3e3cd646577 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:11 -0700 Subject: rtc: rtc-at32ap700x: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-at32ap700x.c | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/drivers/rtc/rtc-at32ap700x.c b/drivers/rtc/rtc-at32ap700x.c index 128896da1a14..3161ab5263ed 100644 --- a/drivers/rtc/rtc-at32ap700x.c +++ b/drivers/rtc/rtc-at32ap700x.c @@ -212,23 +212,20 @@ static int __init at32_rtc_probe(struct platform_device *pdev) regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!regs) { dev_dbg(&pdev->dev, "no mmio resource defined\n"); - ret = -ENXIO; - goto out; + return -ENXIO; } irq = platform_get_irq(pdev, 0); if (irq <= 0) { dev_dbg(&pdev->dev, "could not get irq\n"); - ret = -ENXIO; - goto out; + return -ENXIO; } rtc->irq = irq; rtc->regs = devm_ioremap(&pdev->dev, regs->start, resource_size(regs)); if (!rtc->regs) { - ret = -ENOMEM; dev_dbg(&pdev->dev, "could not map I/O memory\n"); - goto out; + return -ENOMEM; } spin_lock_init(&rtc->lock); @@ -249,7 +246,7 @@ static int __init at32_rtc_probe(struct platform_device *pdev) "rtc", rtc); if (ret) { dev_dbg(&pdev->dev, "could not request irq %d\n", irq); - goto out; + return ret; } platform_set_drvdata(pdev, rtc); @@ -258,8 +255,7 @@ static int __init at32_rtc_probe(struct platform_device *pdev) &at32_rtc_ops, THIS_MODULE); if (IS_ERR(rtc->rtc)) { dev_dbg(&pdev->dev, "could not register rtc device\n"); - ret = PTR_ERR(rtc->rtc); - goto out; + return PTR_ERR(rtc->rtc); } device_init_wakeup(&pdev->dev, 1); @@ -268,18 +264,12 @@ static int __init at32_rtc_probe(struct platform_device *pdev) (unsigned long)rtc->regs, rtc->irq); return 0; - -out: - platform_set_drvdata(pdev, NULL); - return ret; } static int __exit at32_rtc_remove(struct platform_device *pdev) { device_init_wakeup(&pdev->dev, 0); - platform_set_drvdata(pdev, NULL); - return 0; } -- cgit v1.2.3 From 3d772d34d18d6eeccb6cb39bbac1be932d063b0e Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:11 -0700 Subject: rtc: rtc-at91rm9200: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-at91rm9200.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/rtc/rtc-at91rm9200.c b/drivers/rtc/rtc-at91rm9200.c index e87a81c8678e..741892632ae0 100644 --- a/drivers/rtc/rtc-at91rm9200.c +++ b/drivers/rtc/rtc-at91rm9200.c @@ -438,7 +438,6 @@ static int __exit at91_rtc_remove(struct platform_device *pdev) rtc_device_unregister(rtc); iounmap(at91_rtc_regs); - platform_set_drvdata(pdev, NULL); return 0; } -- cgit v1.2.3 From 61cc483a6fbfbda9bcfa91b13cf062c851fec972 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:12 -0700 Subject: rtc: rtc-at91sam9: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-at91sam9.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/drivers/rtc/rtc-at91sam9.c b/drivers/rtc/rtc-at91sam9.c index b60a34cb145a..309b8b342d9c 100644 --- a/drivers/rtc/rtc-at91sam9.c +++ b/drivers/rtc/rtc-at91sam9.c @@ -324,16 +324,14 @@ static int at91_rtc_probe(struct platform_device *pdev) rtc->rtt = devm_ioremap(&pdev->dev, r->start, resource_size(r)); if (!rtc->rtt) { dev_err(&pdev->dev, "failed to map registers, aborting.\n"); - ret = -ENOMEM; - goto fail; + return -ENOMEM; } rtc->gpbr = devm_ioremap(&pdev->dev, r_gpbr->start, resource_size(r_gpbr)); if (!rtc->gpbr) { dev_err(&pdev->dev, "failed to map gpbr registers, aborting.\n"); - ret = -ENOMEM; - goto fail; + return -ENOMEM; } mr = rtt_readl(rtc, MR); @@ -350,17 +348,15 @@ static int at91_rtc_probe(struct platform_device *pdev) rtc->rtcdev = devm_rtc_device_register(&pdev->dev, pdev->name, &at91_rtc_ops, THIS_MODULE); - if (IS_ERR(rtc->rtcdev)) { - ret = PTR_ERR(rtc->rtcdev); - goto fail; - } + if (IS_ERR(rtc->rtcdev)) + return PTR_ERR(rtc->rtcdev); /* register irq handler after we know what name we'll use */ ret = devm_request_irq(&pdev->dev, rtc->irq, at91_rtc_interrupt, IRQF_SHARED, dev_name(&rtc->rtcdev->dev), rtc); if (ret) { dev_dbg(&pdev->dev, "can't share IRQ %d?\n", rtc->irq); - goto fail; + return ret; } /* NOTE: sam9260 rev A silicon has a ROM bug which resets the @@ -374,10 +370,6 @@ static int at91_rtc_probe(struct platform_device *pdev) dev_name(&rtc->rtcdev->dev)); return 0; - -fail: - platform_set_drvdata(pdev, NULL); - return ret; } /* @@ -391,7 +383,6 @@ static int at91_rtc_remove(struct platform_device *pdev) /* disable all interrupts */ rtt_writel(rtc, MR, mr & ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN)); - platform_set_drvdata(pdev, NULL); return 0; } -- cgit v1.2.3 From 0e3dee0e76e605249dc8eb68fda08b17188fc5b9 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:13 -0700 Subject: rtc: rtc-au1xxx: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-au1xxx.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/rtc/rtc-au1xxx.c b/drivers/rtc/rtc-au1xxx.c index 7995abc391fc..7c6e7bb4cf00 100644 --- a/drivers/rtc/rtc-au1xxx.c +++ b/drivers/rtc/rtc-au1xxx.c @@ -118,8 +118,6 @@ out_err: static int au1xtoy_rtc_remove(struct platform_device *pdev) { - platform_set_drvdata(pdev, NULL); - return 0; } -- cgit v1.2.3 From 22c87d633309364158911173ffaeb8de7aca6d96 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:14 -0700 Subject: rtc: rtc-bfin: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Acked-by: Mike Frysinger Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-bfin.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/rtc/rtc-bfin.c b/drivers/rtc/rtc-bfin.c index ad44ec5dc29a..0c53f452849d 100644 --- a/drivers/rtc/rtc-bfin.c +++ b/drivers/rtc/rtc-bfin.c @@ -391,7 +391,6 @@ static int bfin_rtc_remove(struct platform_device *pdev) struct device *dev = &pdev->dev; bfin_rtc_reset(dev, 0); - platform_set_drvdata(pdev, NULL); return 0; } -- cgit v1.2.3 From a8e23d7b50c9e90beca30a7eba333fec1dd88130 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:15 -0700 Subject: rtc: rtc-bq4802: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-bq4802.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/rtc/rtc-bq4802.c b/drivers/rtc/rtc-bq4802.c index af2886784a7b..b919168465f2 100644 --- a/drivers/rtc/rtc-bq4802.c +++ b/drivers/rtc/rtc-bq4802.c @@ -188,8 +188,6 @@ out: static int bq4802_remove(struct platform_device *pdev) { - platform_set_drvdata(pdev, NULL); - return 0; } -- cgit v1.2.3 From 7fcbf5fd5eb74eea9e136adc1e6364a8e7a98134 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:16 -0700 Subject: rtc: rtc-coh901331: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Acked-by: Linus Walleij Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-coh901331.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/rtc/rtc-coh901331.c b/drivers/rtc/rtc-coh901331.c index ad6863a76af9..c05b2021d25b 100644 --- a/drivers/rtc/rtc-coh901331.c +++ b/drivers/rtc/rtc-coh901331.c @@ -154,10 +154,8 @@ static int __exit coh901331_remove(struct platform_device *pdev) { struct coh901331_port *rtap = dev_get_drvdata(&pdev->dev); - if (rtap) { + if (rtap) clk_unprepare(rtap->clk); - platform_set_drvdata(pdev, NULL); - } return 0; } @@ -220,7 +218,6 @@ static int __init coh901331_probe(struct platform_device *pdev) return 0; out_no_rtc: - platform_set_drvdata(pdev, NULL); clk_unprepare(rtap->clk); return ret; } -- cgit v1.2.3 From 90566e1d35ceb4a83e007b25711afa6fcf615ed4 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:17 -0700 Subject: rtc: rtc-da9052: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-da9052.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/rtc/rtc-da9052.c b/drivers/rtc/rtc-da9052.c index 7286b279cf2d..a61ab7fa8a66 100644 --- a/drivers/rtc/rtc-da9052.c +++ b/drivers/rtc/rtc-da9052.c @@ -257,8 +257,6 @@ static int da9052_rtc_probe(struct platform_device *pdev) static int da9052_rtc_remove(struct platform_device *pdev) { - platform_set_drvdata(pdev, NULL); - return 0; } -- cgit v1.2.3 From 37dd5ffc35f41d85c1b8b9e1012c9fb891af3c05 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:18 -0700 Subject: rtc: rtc-da9055: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-da9055.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/rtc/rtc-da9055.c b/drivers/rtc/rtc-da9055.c index 73858ca9709a..df4cf16a967d 100644 --- a/drivers/rtc/rtc-da9055.c +++ b/drivers/rtc/rtc-da9055.c @@ -317,8 +317,6 @@ err_rtc: static int da9055_rtc_remove(struct platform_device *pdev) { - platform_set_drvdata(pdev, NULL); - return 0; } -- cgit v1.2.3 From 438831fc6ee66a34f27522ef3f0f9eac4b4da8bc Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:19 -0700 Subject: rtc: rtc-davinci: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-davinci.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/rtc/rtc-davinci.c b/drivers/rtc/rtc-davinci.c index be5fc32d3cd7..24677ef8c39a 100644 --- a/drivers/rtc/rtc-davinci.c +++ b/drivers/rtc/rtc-davinci.c @@ -526,10 +526,9 @@ static int __init davinci_rtc_probe(struct platform_device *pdev) davinci_rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, &davinci_rtc_ops, THIS_MODULE); if (IS_ERR(davinci_rtc->rtc)) { - ret = PTR_ERR(davinci_rtc->rtc); dev_err(dev, "unable to register RTC device, err %d\n", ret); - goto fail1; + return PTR_ERR(davinci_rtc->rtc); } rtcif_write(davinci_rtc, PRTCIF_INTFLG_RTCSS, PRTCIF_INTFLG); @@ -543,7 +542,7 @@ static int __init davinci_rtc_probe(struct platform_device *pdev) 0, "davinci_rtc", davinci_rtc); if (ret < 0) { dev_err(dev, "unable to register davinci RTC interrupt\n"); - goto fail1; + return ret; } /* Enable interrupts */ @@ -556,10 +555,6 @@ static int __init davinci_rtc_probe(struct platform_device *pdev) device_init_wakeup(&pdev->dev, 0); return 0; - -fail1: - platform_set_drvdata(pdev, NULL); - return ret; } static int __exit davinci_rtc_remove(struct platform_device *pdev) @@ -570,8 +565,6 @@ static int __exit davinci_rtc_remove(struct platform_device *pdev) rtcif_write(davinci_rtc, 0, PRTCIF_INTEN); - platform_set_drvdata(pdev, NULL); - return 0; } -- cgit v1.2.3 From 531d56fe7d4277367c8fb2c637f92915bca459d2 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:20 -0700 Subject: rtc: rtc-dm355evm: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-dm355evm.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/rtc/rtc-dm355evm.c b/drivers/rtc/rtc-dm355evm.c index 1e1ca63d58a9..1855581014f7 100644 --- a/drivers/rtc/rtc-dm355evm.c +++ b/drivers/rtc/rtc-dm355evm.c @@ -141,7 +141,6 @@ static int dm355evm_rtc_probe(struct platform_device *pdev) static int dm355evm_rtc_remove(struct platform_device *pdev) { - platform_set_drvdata(pdev, NULL); return 0; } -- cgit v1.2.3 From 844b8a6855bf08808fb211c4923dbd5062eeb0ad Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:21 -0700 Subject: rtc: rtc-ds1302: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-ds1302.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/rtc/rtc-ds1302.c b/drivers/rtc/rtc-ds1302.c index d13954346286..1f8ee639757b 100644 --- a/drivers/rtc/rtc-ds1302.c +++ b/drivers/rtc/rtc-ds1302.c @@ -236,8 +236,6 @@ static int __init ds1302_rtc_probe(struct platform_device *pdev) static int __exit ds1302_rtc_remove(struct platform_device *pdev) { - platform_set_drvdata(pdev, NULL); - return 0; } -- cgit v1.2.3 From 7b32b631fb30ef3982d1bdcbb9242873029b46aa Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:21 -0700 Subject: rtc: rtc-ep93xx: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-ep93xx.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/rtc/rtc-ep93xx.c b/drivers/rtc/rtc-ep93xx.c index 5807b77c444a..549b3c3792d2 100644 --- a/drivers/rtc/rtc-ep93xx.c +++ b/drivers/rtc/rtc-ep93xx.c @@ -167,7 +167,6 @@ static int ep93xx_rtc_probe(struct platform_device *pdev) return 0; exit: - platform_set_drvdata(pdev, NULL); pdev->dev.platform_data = NULL; return err; } @@ -175,7 +174,6 @@ exit: static int ep93xx_rtc_remove(struct platform_device *pdev) { sysfs_remove_group(&pdev->dev.kobj, &ep93xx_rtc_sysfs_files); - platform_set_drvdata(pdev, NULL); pdev->dev.platform_data = NULL; return 0; -- cgit v1.2.3 From 2ce5413d9ef3cf177b27edfaf7d7a3b056834fea Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:22 -0700 Subject: rtc: rtc-jz4740: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-jz4740.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c index 1e48686ca6d2..c6573585d028 100644 --- a/drivers/rtc/rtc-jz4740.c +++ b/drivers/rtc/rtc-jz4740.c @@ -287,7 +287,6 @@ err_free_irq: err_unregister_rtc: rtc_device_unregister(rtc->rtc); err_iounmap: - platform_set_drvdata(pdev, NULL); iounmap(rtc->base); err_release_mem_region: release_mem_region(rtc->mem->start, resource_size(rtc->mem)); @@ -310,8 +309,6 @@ static int jz4740_rtc_remove(struct platform_device *pdev) kfree(rtc); - platform_set_drvdata(pdev, NULL); - return 0; } -- cgit v1.2.3 From 70bfde832c772688cb12734b297d387c1037b994 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:23 -0700 Subject: rtc: rtc-lp8788: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-lp8788.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/rtc/rtc-lp8788.c b/drivers/rtc/rtc-lp8788.c index 9853ac15b296..76a82dc9e502 100644 --- a/drivers/rtc/rtc-lp8788.c +++ b/drivers/rtc/rtc-lp8788.c @@ -314,8 +314,6 @@ static int lp8788_rtc_probe(struct platform_device *pdev) static int lp8788_rtc_remove(struct platform_device *pdev) { - platform_set_drvdata(pdev, NULL); - return 0; } -- cgit v1.2.3 From 99c3e1c59ad96c6de3e59452f5183edc4d634f16 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:24 -0700 Subject: rtc: rtc-lpc32xx: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-lpc32xx.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/rtc/rtc-lpc32xx.c b/drivers/rtc/rtc-lpc32xx.c index 787550d756e9..8276ae94a2a9 100644 --- a/drivers/rtc/rtc-lpc32xx.c +++ b/drivers/rtc/rtc-lpc32xx.c @@ -277,7 +277,6 @@ static int lpc32xx_rtc_probe(struct platform_device *pdev) &lpc32xx_rtc_ops, THIS_MODULE); if (IS_ERR(rtc->rtc)) { dev_err(&pdev->dev, "Can't get RTC\n"); - platform_set_drvdata(pdev, NULL); return PTR_ERR(rtc->rtc); } @@ -306,8 +305,6 @@ static int lpc32xx_rtc_remove(struct platform_device *pdev) if (rtc->irq >= 0) device_init_wakeup(&pdev->dev, 0); - platform_set_drvdata(pdev, NULL); - return 0; } -- cgit v1.2.3 From 55d66d286d069f3cb65a0aaec9bf0889821a915a Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:25 -0700 Subject: rtc: rtc-ls1x: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-ls1x.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/rtc/rtc-ls1x.c b/drivers/rtc/rtc-ls1x.c index db82f91f4562..e913f38bd33d 100644 --- a/drivers/rtc/rtc-ls1x.c +++ b/drivers/rtc/rtc-ls1x.c @@ -187,8 +187,6 @@ err: static int ls1x_rtc_remove(struct platform_device *pdev) { - platform_set_drvdata(pdev, NULL); - return 0; } -- cgit v1.2.3 From 5f371582180572f06f74dcac55c54d2072e672bc Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:26 -0700 Subject: rtc: rtc-m48t59: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-m48t59.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/rtc/rtc-m48t59.c b/drivers/rtc/rtc-m48t59.c index 130f29af3869..d4d31fa19244 100644 --- a/drivers/rtc/rtc-m48t59.c +++ b/drivers/rtc/rtc-m48t59.c @@ -513,7 +513,6 @@ static int m48t59_rtc_remove(struct platform_device *pdev) iounmap(m48t59->ioaddr); if (m48t59->irq != NO_IRQ) free_irq(m48t59->irq, &pdev->dev); - platform_set_drvdata(pdev, NULL); kfree(m48t59); return 0; } -- cgit v1.2.3 From 28e1de09421b0e21dffbf2186b5ef14c1c8e033c Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:27 -0700 Subject: rtc: rtc-max8925: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-max8925.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/rtc/rtc-max8925.c b/drivers/rtc/rtc-max8925.c index 7c90f4e45e27..981b6544cf7c 100644 --- a/drivers/rtc/rtc-max8925.c +++ b/drivers/rtc/rtc-max8925.c @@ -268,7 +268,7 @@ static int max8925_rtc_probe(struct platform_device *pdev) if (ret < 0) { dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n", info->irq, ret); - goto err; + return ret; } dev_set_drvdata(&pdev->dev, info); @@ -282,13 +282,10 @@ static int max8925_rtc_probe(struct platform_device *pdev) ret = PTR_ERR(info->rtc_dev); if (IS_ERR(info->rtc_dev)) { dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret); - goto err; + return ret; } return 0; -err: - platform_set_drvdata(pdev, NULL); - return ret; } static int max8925_rtc_remove(struct platform_device *pdev) -- cgit v1.2.3 From 4802f224a50d841786f17a1d27dd48dc149a1e33 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:28 -0700 Subject: rtc: rtc-max8998: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-max8998.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/rtc/rtc-max8998.c b/drivers/rtc/rtc-max8998.c index d5af7baa48b5..738eecef4d90 100644 --- a/drivers/rtc/rtc-max8998.c +++ b/drivers/rtc/rtc-max8998.c @@ -274,7 +274,7 @@ static int max8998_rtc_probe(struct platform_device *pdev) if (IS_ERR(info->rtc_dev)) { ret = PTR_ERR(info->rtc_dev); dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret); - goto out_rtc; + return ret; } ret = devm_request_threaded_irq(&pdev->dev, info->irq, NULL, @@ -292,10 +292,6 @@ static int max8998_rtc_probe(struct platform_device *pdev) } return 0; - -out_rtc: - platform_set_drvdata(pdev, NULL); - return ret; } static int max8998_rtc_remove(struct platform_device *pdev) -- cgit v1.2.3 From d8f447877b1cd40267a66c6d9fcd11464c859346 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:29 -0700 Subject: rtc: rtc-mc13xxx: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-mc13xxx.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/rtc/rtc-mc13xxx.c b/drivers/rtc/rtc-mc13xxx.c index 7a8ed27a5f2e..77ea9896b5ba 100644 --- a/drivers/rtc/rtc-mc13xxx.c +++ b/drivers/rtc/rtc-mc13xxx.c @@ -370,8 +370,6 @@ err_reset_irq_status: err_reset_irq_request: mc13xxx_unlock(mc13xxx); - - platform_set_drvdata(pdev, NULL); } return ret; @@ -389,8 +387,6 @@ static int __exit mc13xxx_rtc_remove(struct platform_device *pdev) mc13xxx_unlock(priv->mc13xxx); - platform_set_drvdata(pdev, NULL); - return 0; } -- cgit v1.2.3 From e3aa7526fca757312732f88cd16f7a73a496b80d Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:29 -0700 Subject: rtc: rtc-msm6242: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-msm6242.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/rtc/rtc-msm6242.c b/drivers/rtc/rtc-msm6242.c index f0e9893ae7be..c014fa2ee5e9 100644 --- a/drivers/rtc/rtc-msm6242.c +++ b/drivers/rtc/rtc-msm6242.c @@ -199,7 +199,6 @@ static int __init msm6242_rtc_probe(struct platform_device *pdev) struct resource *res; struct msm6242_priv *priv; struct rtc_device *rtc; - int error; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) @@ -216,17 +215,11 @@ static int __init msm6242_rtc_probe(struct platform_device *pdev) rtc = devm_rtc_device_register(&pdev->dev, "rtc-msm6242", &msm6242_rtc_ops, THIS_MODULE); - if (IS_ERR(rtc)) { - error = PTR_ERR(rtc); - goto out_unmap; - } + if (IS_ERR(rtc)) + return PTR_ERR(rtc); priv->rtc = rtc; return 0; - -out_unmap: - platform_set_drvdata(pdev, NULL); - return error; } static int __exit msm6242_rtc_remove(struct platform_device *pdev) -- cgit v1.2.3 From d2f3a3984be5b154714a16aeafe195883b4cd233 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:30 -0700 Subject: rtc: rtc-mxc: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-mxc.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/rtc/rtc-mxc.c b/drivers/rtc/rtc-mxc.c index bf00d6dbeadd..ab87bacb8f88 100644 --- a/drivers/rtc/rtc-mxc.c +++ b/drivers/rtc/rtc-mxc.c @@ -443,15 +443,13 @@ static int mxc_rtc_probe(struct platform_device *pdev) THIS_MODULE); if (IS_ERR(rtc)) { ret = PTR_ERR(rtc); - goto exit_clr_drvdata; + goto exit_put_clk; } pdata->rtc = rtc; return 0; -exit_clr_drvdata: - platform_set_drvdata(pdev, NULL); exit_put_clk: clk_disable_unprepare(pdata->clk); @@ -465,7 +463,6 @@ static int mxc_rtc_remove(struct platform_device *pdev) struct rtc_plat_data *pdata = platform_get_drvdata(pdev); clk_disable_unprepare(pdata->clk); - platform_set_drvdata(pdev, NULL); return 0; } -- cgit v1.2.3 From 6d4a38cbbb311dc93c0f66a702f7d0ae5c5b4965 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:31 -0700 Subject: rtc: rtc-nuc900: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Acked-by: Wan Zongshun Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-nuc900.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/rtc/rtc-nuc900.c b/drivers/rtc/rtc-nuc900.c index d592e2fe43f7..994659b0f879 100644 --- a/drivers/rtc/rtc-nuc900.c +++ b/drivers/rtc/rtc-nuc900.c @@ -262,8 +262,6 @@ static int __init nuc900_rtc_probe(struct platform_device *pdev) static int __exit nuc900_rtc_remove(struct platform_device *pdev) { - platform_set_drvdata(pdev, NULL); - return 0; } -- cgit v1.2.3 From a1c805a9bdc55b244706b911e842d0649990953b Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:32 -0700 Subject: rtc: rtc-pcap: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-pcap.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/drivers/rtc/rtc-pcap.c b/drivers/rtc/rtc-pcap.c index 539a90b98bc5..40b5c630bc7d 100644 --- a/drivers/rtc/rtc-pcap.c +++ b/drivers/rtc/rtc-pcap.c @@ -156,10 +156,8 @@ static int __init pcap_rtc_probe(struct platform_device *pdev) pcap_rtc->rtc = devm_rtc_device_register(&pdev->dev, "pcap", &pcap_rtc_ops, THIS_MODULE); - if (IS_ERR(pcap_rtc->rtc)) { - err = PTR_ERR(pcap_rtc->rtc); - goto fail; - } + if (IS_ERR(pcap_rtc->rtc)) + return PTR_ERR(pcap_rtc->rtc); timer_irq = pcap_to_irq(pcap_rtc->pcap, PCAP_IRQ_1HZ); alarm_irq = pcap_to_irq(pcap_rtc->pcap, PCAP_IRQ_TODA); @@ -167,17 +165,14 @@ static int __init pcap_rtc_probe(struct platform_device *pdev) err = devm_request_irq(&pdev->dev, timer_irq, pcap_rtc_irq, 0, "RTC Timer", pcap_rtc); if (err) - goto fail; + return err; err = devm_request_irq(&pdev->dev, alarm_irq, pcap_rtc_irq, 0, "RTC Alarm", pcap_rtc); if (err) - goto fail; + return err; return 0; -fail: - platform_set_drvdata(pdev, NULL); - return err; } static int __exit pcap_rtc_remove(struct platform_device *pdev) -- cgit v1.2.3 From 253262a570e377bcf36c8d2643c6402644d7d363 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:33 -0700 Subject: rtc: rtc-pm8xxx: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-pm8xxx.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/rtc/rtc-pm8xxx.c b/drivers/rtc/rtc-pm8xxx.c index f1a6557261f3..14ee860d5a3f 100644 --- a/drivers/rtc/rtc-pm8xxx.c +++ b/drivers/rtc/rtc-pm8xxx.c @@ -480,7 +480,6 @@ static int pm8xxx_rtc_probe(struct platform_device *pdev) fail_req_irq: rtc_device_unregister(rtc_dd->rtc); fail_rtc_enable: - platform_set_drvdata(pdev, NULL); kfree(rtc_dd); return rc; } @@ -492,7 +491,6 @@ static int pm8xxx_rtc_remove(struct platform_device *pdev) device_init_wakeup(&pdev->dev, 0); free_irq(rtc_dd->rtc_alarm_irq, rtc_dd); rtc_device_unregister(rtc_dd->rtc); - platform_set_drvdata(pdev, NULL); kfree(rtc_dd); return 0; -- cgit v1.2.3 From fb9b525e7930822e6fbe6db1cce931686545410a Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:34 -0700 Subject: rtc: rtc-s3c: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-s3c.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index 0b495e8b8e66..7afd373b9595 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -421,8 +421,6 @@ static void s3c_rtc_enable(struct platform_device *pdev, int en) static int s3c_rtc_remove(struct platform_device *dev) { - platform_set_drvdata(dev, NULL); - s3c_rtc_setaie(&dev->dev, 0); clk_unprepare(rtc_clk); @@ -549,23 +547,20 @@ static int s3c_rtc_probe(struct platform_device *pdev) 0, "s3c2410-rtc alarm", rtc); if (ret) { dev_err(&pdev->dev, "IRQ%d error %d\n", s3c_rtc_alarmno, ret); - goto err_alarm_irq; + goto err_nortc; } ret = devm_request_irq(&pdev->dev, s3c_rtc_tickno, s3c_rtc_tickirq, 0, "s3c2410-rtc tick", rtc); if (ret) { dev_err(&pdev->dev, "IRQ%d error %d\n", s3c_rtc_tickno, ret); - goto err_alarm_irq; + goto err_nortc; } clk_disable(rtc_clk); return 0; - err_alarm_irq: - platform_set_drvdata(pdev, NULL); - err_nortc: s3c_rtc_enable(pdev, 0); clk_disable_unprepare(rtc_clk); -- cgit v1.2.3 From 66600bbef9c74c49b2a020a04897af86202b8d15 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:35 -0700 Subject: rtc: rtc-sa1100: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-sa1100.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/rtc/rtc-sa1100.c b/drivers/rtc/rtc-sa1100.c index 00605601dbf7..0f7adeb1944a 100644 --- a/drivers/rtc/rtc-sa1100.c +++ b/drivers/rtc/rtc-sa1100.c @@ -249,7 +249,7 @@ static int sa1100_rtc_probe(struct platform_device *pdev) ret = clk_prepare_enable(info->clk); if (ret) - goto err_enable_clk; + return ret; /* * According to the manual we should be able to let RTTR be zero * and then a default diviser for a 32.768KHz clock is used. @@ -303,8 +303,6 @@ static int sa1100_rtc_probe(struct platform_device *pdev) return 0; err_dev: clk_disable_unprepare(info->clk); -err_enable_clk: - platform_set_drvdata(pdev, NULL); return ret; } @@ -312,10 +310,8 @@ static int sa1100_rtc_remove(struct platform_device *pdev) { struct sa1100_rtc *info = platform_get_drvdata(pdev); - if (info) { + if (info) clk_disable_unprepare(info->clk); - platform_set_drvdata(pdev, NULL); - } return 0; } -- cgit v1.2.3 From f50c8bf73f74c304c213371de35f4a4d7fafcaf9 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:36 -0700 Subject: rtc: rtc-sh: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-sh.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c index 8d5bd2e36776..cb2f8399d692 100644 --- a/drivers/rtc/rtc-sh.c +++ b/drivers/rtc/rtc-sh.c @@ -770,8 +770,6 @@ static int __exit sh_rtc_remove(struct platform_device *pdev) clk_disable(rtc->clk); clk_put(rtc->clk); - platform_set_drvdata(pdev, NULL); - kfree(rtc); return 0; -- cgit v1.2.3 From 1cc622358745261dcfe4a7ebb5c37a5145fb3ee3 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:36 -0700 Subject: rtc: rtc-spear: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Acked-by: Viresh Kumar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-spear.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/rtc/rtc-spear.c b/drivers/rtc/rtc-spear.c index 574359c48f65..c492cf0ab8cd 100644 --- a/drivers/rtc/rtc-spear.c +++ b/drivers/rtc/rtc-spear.c @@ -417,7 +417,6 @@ static int spear_rtc_probe(struct platform_device *pdev) return 0; err_disable_clock: - platform_set_drvdata(pdev, NULL); clk_disable_unprepare(config->clk); return status; -- cgit v1.2.3 From dfd2a17809b787dea44eec76ab9f3de8b7fcb8d6 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:37 -0700 Subject: rtc: rtc-stmp3xxx: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-stmp3xxx.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/drivers/rtc/rtc-stmp3xxx.c b/drivers/rtc/rtc-stmp3xxx.c index 483ce086990b..90a3e864b8fe 100644 --- a/drivers/rtc/rtc-stmp3xxx.c +++ b/drivers/rtc/rtc-stmp3xxx.c @@ -225,7 +225,6 @@ static int stmp3xxx_rtc_remove(struct platform_device *pdev) writel(STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, rtc_data->io + STMP3XXX_RTC_CTRL_CLR); - platform_set_drvdata(pdev, NULL); return 0; } @@ -274,25 +273,19 @@ static int stmp3xxx_rtc_probe(struct platform_device *pdev) rtc_data->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, &stmp3xxx_rtc_ops, THIS_MODULE); - if (IS_ERR(rtc_data->rtc)) { - err = PTR_ERR(rtc_data->rtc); - goto out; - } + if (IS_ERR(rtc_data->rtc)) + return PTR_ERR(rtc_data->rtc); err = devm_request_irq(&pdev->dev, rtc_data->irq_alarm, stmp3xxx_rtc_interrupt, 0, "RTC alarm", &pdev->dev); if (err) { dev_err(&pdev->dev, "Cannot claim IRQ%d\n", rtc_data->irq_alarm); - goto out; + return err; } stmp3xxx_wdt_register(pdev); return 0; - -out: - platform_set_drvdata(pdev, NULL); - return err; } #ifdef CONFIG_PM_SLEEP -- cgit v1.2.3 From ca5f4389a81bdceb151ba7f806a6a807d0c8893f Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:38 -0700 Subject: rtc: rtc-twl: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-twl.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/rtc/rtc-twl.c b/drivers/rtc/rtc-twl.c index b2eab34f38d9..52843d0132be 100644 --- a/drivers/rtc/rtc-twl.c +++ b/drivers/rtc/rtc-twl.c @@ -556,7 +556,6 @@ static int twl_rtc_remove(struct platform_device *pdev) free_irq(irq, rtc); rtc_device_unregister(rtc); - platform_set_drvdata(pdev, NULL); return 0; } -- cgit v1.2.3 From fedd5f49618f11dd08f99c4106c5fc43951ba4b0 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:39 -0700 Subject: rtc: rtc-vr41xx: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-vr41xx.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/rtc/rtc-vr41xx.c b/drivers/rtc/rtc-vr41xx.c index b940c2374904..3b5b4fa9a6e9 100644 --- a/drivers/rtc/rtc-vr41xx.c +++ b/drivers/rtc/rtc-vr41xx.c @@ -381,8 +381,6 @@ static int rtc_remove(struct platform_device *pdev) if (rtc) rtc_device_unregister(rtc); - platform_set_drvdata(pdev, NULL); - free_irq(aie_irq, pdev); free_irq(pie_irq, pdev); if (rtc1_base) -- cgit v1.2.3 From 221ba4db9de567d84b891f8dad60d5efdea63af1 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:40 -0700 Subject: rtc: rtc-vt8500: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Acked-by: Tony Prisk Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-vt8500.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/rtc/rtc-vt8500.c b/drivers/rtc/rtc-vt8500.c index d89efee6d29e..c2d6331fc712 100644 --- a/drivers/rtc/rtc-vt8500.c +++ b/drivers/rtc/rtc-vt8500.c @@ -282,8 +282,6 @@ static int vt8500_rtc_remove(struct platform_device *pdev) /* Disable alarm matching */ writel(0, vt8500_rtc->regbase + VT8500_RTC_IS); - platform_set_drvdata(pdev, NULL); - return 0; } -- cgit v1.2.3 From d314fa3d27ad43c31ca1da91c7592d0df2461b86 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:41 -0700 Subject: rtc: rtc-m48t86: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-m48t86.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/rtc/rtc-m48t86.c b/drivers/rtc/rtc-m48t86.c index 33a91c484533..d1fe7f5cd20a 100644 --- a/drivers/rtc/rtc-m48t86.c +++ b/drivers/rtc/rtc-m48t86.c @@ -168,8 +168,6 @@ static int m48t86_rtc_probe(struct platform_device *dev) static int m48t86_rtc_remove(struct platform_device *dev) { - platform_set_drvdata(dev, NULL); - return 0; } -- cgit v1.2.3 From 7f1c2e824e10439ef716f2e0bda7609b0474d0f8 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:42 -0700 Subject: rtc: rtc-puv3: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Acked-by: Guan Xuetao Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-puv3.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/rtc/rtc-puv3.c b/drivers/rtc/rtc-puv3.c index 72f437170d2e..402732cfb32a 100644 --- a/drivers/rtc/rtc-puv3.c +++ b/drivers/rtc/rtc-puv3.c @@ -224,7 +224,6 @@ static int puv3_rtc_remove(struct platform_device *dev) { struct rtc_device *rtc = platform_get_drvdata(dev); - platform_set_drvdata(dev, NULL); rtc_device_unregister(rtc); puv3_rtc_setpie(&dev->dev, 0); -- cgit v1.2.3 From a81de2076c32f2b02c454cfb16fdba63050c30b7 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:43 -0700 Subject: rtc: rtc-rp5c01: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-rp5c01.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/rtc/rtc-rp5c01.c b/drivers/rtc/rtc-rp5c01.c index 873c689f01c3..89d073679267 100644 --- a/drivers/rtc/rtc-rp5c01.c +++ b/drivers/rtc/rtc-rp5c01.c @@ -251,21 +251,15 @@ static int __init rp5c01_rtc_probe(struct platform_device *dev) rtc = devm_rtc_device_register(&dev->dev, "rtc-rp5c01", &rp5c01_rtc_ops, THIS_MODULE); - if (IS_ERR(rtc)) { - error = PTR_ERR(rtc); - goto out; - } + if (IS_ERR(rtc)) + return PTR_ERR(rtc); priv->rtc = rtc; error = sysfs_create_bin_file(&dev->dev.kobj, &priv->nvram_attr); if (error) - goto out; + return error; return 0; - -out: - platform_set_drvdata(dev, NULL); - return error; } static int __exit rp5c01_rtc_remove(struct platform_device *dev) -- cgit v1.2.3 From 364589e359267b9878790db19c6b7130bcb7ac6b Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:06:44 -0700 Subject: rtc: rtc-tile: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d063100 ("device-core: Ensure drvdata = NULL when no driver is bound"). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-tile.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/rtc/rtc-tile.c b/drivers/rtc/rtc-tile.c index fc3dee95f166..b3bb32c43254 100644 --- a/drivers/rtc/rtc-tile.c +++ b/drivers/rtc/rtc-tile.c @@ -96,8 +96,6 @@ static int tile_rtc_probe(struct platform_device *dev) */ static int tile_rtc_remove(struct platform_device *dev) { - platform_set_drvdata(dev, NULL); - return 0; } -- cgit v1.2.3 From 29ecd78c0fd6ee05f2c6b07b23823a6ae43c13ff Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 3 Jul 2013 15:06:45 -0700 Subject: drivers/rtc/rtc-rv3029c2.c: fix disabling AIE irq In the disable AIE irq code path, current code passes "1" to enable parameter of rv3029c2_rtc_i2c_alarm_set_irq(). Thus it does not disable AIE irq. Signed-off-by: Axel Lin Acked-by: Heiko Schocher Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-rv3029c2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-rv3029c2.c b/drivers/rtc/rtc-rv3029c2.c index 5032c24ec159..9100a3401de1 100644 --- a/drivers/rtc/rtc-rv3029c2.c +++ b/drivers/rtc/rtc-rv3029c2.c @@ -310,7 +310,7 @@ static int rv3029c2_rtc_i2c_set_alarm(struct i2c_client *client, dev_dbg(&client->dev, "alarm IRQ armed\n"); } else { /* disable AIE irq */ - ret = rv3029c2_rtc_i2c_alarm_set_irq(client, 1); + ret = rv3029c2_rtc_i2c_alarm_set_irq(client, 0); if (ret) return ret; -- cgit v1.2.3 From d43fcab6b29b818a627501b21628967b5396b1f9 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:06:46 -0700 Subject: drivers/rtc/rtc-m48t86.c: remove empty function After the switch to devm_ functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-m48t86.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-m48t86.c b/drivers/rtc/rtc-m48t86.c index d1fe7f5cd20a..2d30314fa07f 100644 --- a/drivers/rtc/rtc-m48t86.c +++ b/drivers/rtc/rtc-m48t86.c @@ -166,18 +166,12 @@ static int m48t86_rtc_probe(struct platform_device *dev) return 0; } -static int m48t86_rtc_remove(struct platform_device *dev) -{ - return 0; -} - static struct platform_driver m48t86_rtc_platform_driver = { .driver = { .name = "rtc-m48t86", .owner = THIS_MODULE, }, .probe = m48t86_rtc_probe, - .remove = m48t86_rtc_remove, }; module_platform_driver(m48t86_rtc_platform_driver); -- cgit v1.2.3 From afdce3014002968fe5c3d97d36b71583337c5f06 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:06:47 -0700 Subject: drivers/rtc/rtc-tile.c: remove empty function After the switch to devm_ functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-tile.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/rtc/rtc-tile.c b/drivers/rtc/rtc-tile.c index b3bb32c43254..ff9632eb79f2 100644 --- a/drivers/rtc/rtc-tile.c +++ b/drivers/rtc/rtc-tile.c @@ -91,21 +91,12 @@ static int tile_rtc_probe(struct platform_device *dev) return 0; } -/* - * Device cleanup routine. - */ -static int tile_rtc_remove(struct platform_device *dev) -{ - return 0; -} - static struct platform_driver tile_rtc_platform_driver = { .driver = { .name = "rtc-tile", .owner = THIS_MODULE, }, .probe = tile_rtc_probe, - .remove = tile_rtc_remove, }; /* -- cgit v1.2.3 From 8bd941e7942e3f8bdb68df19e9c7fff57397a52d Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:06:48 -0700 Subject: drivers/rtc/rtc-nuc900.c: remove empty function After the switch to devm_ functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Cc: Wan ZongShun Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-nuc900.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-nuc900.c b/drivers/rtc/rtc-nuc900.c index 994659b0f879..22861c5e0c59 100644 --- a/drivers/rtc/rtc-nuc900.c +++ b/drivers/rtc/rtc-nuc900.c @@ -260,13 +260,7 @@ static int __init nuc900_rtc_probe(struct platform_device *pdev) return 0; } -static int __exit nuc900_rtc_remove(struct platform_device *pdev) -{ - return 0; -} - static struct platform_driver nuc900_rtc_driver = { - .remove = __exit_p(nuc900_rtc_remove), .driver = { .name = "nuc900-rtc", .owner = THIS_MODULE, -- cgit v1.2.3 From e7d5a628f2e6c8153922b20099cba0b7c35ac2ae Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:06:49 -0700 Subject: drivers/rtc/rtc-msm6242.c: remove empty function After the switch to devm_ functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Cc: Geert Uytterhoeven Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-msm6242.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-msm6242.c b/drivers/rtc/rtc-msm6242.c index c014fa2ee5e9..426cb5189daa 100644 --- a/drivers/rtc/rtc-msm6242.c +++ b/drivers/rtc/rtc-msm6242.c @@ -222,17 +222,11 @@ static int __init msm6242_rtc_probe(struct platform_device *pdev) return 0; } -static int __exit msm6242_rtc_remove(struct platform_device *pdev) -{ - return 0; -} - static struct platform_driver msm6242_rtc_driver = { .driver = { .name = "rtc-msm6242", .owner = THIS_MODULE, }, - .remove = __exit_p(msm6242_rtc_remove), }; module_platform_driver_probe(msm6242_rtc_driver, msm6242_rtc_probe); -- cgit v1.2.3 From 2fbbdb11f098d2c03677d1eb730bd55de18faa7a Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:06:50 -0700 Subject: drivers/rtc/rtc-max8998.c: remove empty function After the switch to devm_ functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Cc: Minkyu Kang Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-max8998.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-max8998.c b/drivers/rtc/rtc-max8998.c index 738eecef4d90..5388336a2c4c 100644 --- a/drivers/rtc/rtc-max8998.c +++ b/drivers/rtc/rtc-max8998.c @@ -294,11 +294,6 @@ static int max8998_rtc_probe(struct platform_device *pdev) return 0; } -static int max8998_rtc_remove(struct platform_device *pdev) -{ - return 0; -} - static const struct platform_device_id max8998_rtc_id[] = { { "max8998-rtc", TYPE_MAX8998 }, { "lp3974-rtc", TYPE_LP3974 }, @@ -311,7 +306,6 @@ static struct platform_driver max8998_rtc_driver = { .owner = THIS_MODULE, }, .probe = max8998_rtc_probe, - .remove = max8998_rtc_remove, .id_table = max8998_rtc_id, }; -- cgit v1.2.3 From ce06cc82d7cb5445d0c01c980433ada4ab0f2847 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:06:51 -0700 Subject: drivers/rtc/rtc-max8925.c: remove empty function After the switch to devm_ functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Cc: Haojian Zhuang Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-max8925.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-max8925.c b/drivers/rtc/rtc-max8925.c index 981b6544cf7c..951d1a78e190 100644 --- a/drivers/rtc/rtc-max8925.c +++ b/drivers/rtc/rtc-max8925.c @@ -288,11 +288,6 @@ static int max8925_rtc_probe(struct platform_device *pdev) return 0; } -static int max8925_rtc_remove(struct platform_device *pdev) -{ - return 0; -} - #ifdef CONFIG_PM_SLEEP static int max8925_rtc_suspend(struct device *dev) { @@ -323,7 +318,6 @@ static struct platform_driver max8925_rtc_driver = { .pm = &max8925_rtc_pm_ops, }, .probe = max8925_rtc_probe, - .remove = max8925_rtc_remove, }; module_platform_driver(max8925_rtc_driver); -- cgit v1.2.3 From 7f9833cdf642ade8a99d7979a7128b9288234d95 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:06:51 -0700 Subject: drivers/rtc/rtc-ls1x.c: remove empty function After the switch to devm_ functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Cc: zhao zhang Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-ls1x.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-ls1x.c b/drivers/rtc/rtc-ls1x.c index e913f38bd33d..682ecb094839 100644 --- a/drivers/rtc/rtc-ls1x.c +++ b/drivers/rtc/rtc-ls1x.c @@ -185,17 +185,11 @@ err: return ret; } -static int ls1x_rtc_remove(struct platform_device *pdev) -{ - return 0; -} - static struct platform_driver ls1x_rtc_driver = { .driver = { .name = "ls1x-rtc", .owner = THIS_MODULE, }, - .remove = ls1x_rtc_remove, .probe = ls1x_rtc_probe, }; -- cgit v1.2.3 From ffeb40515971c9860ff671bb074689db15e18831 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:06:53 -0700 Subject: drivers/rtc/rtc-lp8788.c: remove empty function After the switch to devm_ functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Acked-by: Milo Kim Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-lp8788.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-lp8788.c b/drivers/rtc/rtc-lp8788.c index 76a82dc9e502..4ff6c73253b3 100644 --- a/drivers/rtc/rtc-lp8788.c +++ b/drivers/rtc/rtc-lp8788.c @@ -312,14 +312,8 @@ static int lp8788_rtc_probe(struct platform_device *pdev) return 0; } -static int lp8788_rtc_remove(struct platform_device *pdev) -{ - return 0; -} - static struct platform_driver lp8788_rtc_driver = { .probe = lp8788_rtc_probe, - .remove = lp8788_rtc_remove, .driver = { .name = LP8788_DEV_RTC, .owner = THIS_MODULE, -- cgit v1.2.3 From 013f91e7bd98d6adb3b84763e2ab8ff86e64445f Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:06:54 -0700 Subject: drivers/rtc/rtc-ds1302.c: remove empty function After the switch to devm_ functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-ds1302.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-ds1302.c b/drivers/rtc/rtc-ds1302.c index 1f8ee639757b..7533b723082d 100644 --- a/drivers/rtc/rtc-ds1302.c +++ b/drivers/rtc/rtc-ds1302.c @@ -234,17 +234,11 @@ static int __init ds1302_rtc_probe(struct platform_device *pdev) return 0; } -static int __exit ds1302_rtc_remove(struct platform_device *pdev) -{ - return 0; -} - static struct platform_driver ds1302_platform_driver = { .driver = { .name = DRV_NAME, .owner = THIS_MODULE, }, - .remove = __exit_p(ds1302_rtc_remove), }; module_platform_driver_probe(ds1302_platform_driver, ds1302_rtc_probe); -- cgit v1.2.3 From 277048aa6a32ecd8a21241737287ff377a5501e0 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:06:55 -0700 Subject: drivers/rtc/rtc-dm355evm.c: remove empty function After the switch to devm_ functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-dm355evm.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-dm355evm.c b/drivers/rtc/rtc-dm355evm.c index 1855581014f7..1aca08394c47 100644 --- a/drivers/rtc/rtc-dm355evm.c +++ b/drivers/rtc/rtc-dm355evm.c @@ -139,18 +139,12 @@ static int dm355evm_rtc_probe(struct platform_device *pdev) return 0; } -static int dm355evm_rtc_remove(struct platform_device *pdev) -{ - return 0; -} - /* * I2C is used to talk to the MSP430, but this platform device is * exposed by an MFD driver that manages I2C communications. */ static struct platform_driver rtc_dm355evm_driver = { .probe = dm355evm_rtc_probe, - .remove = dm355evm_rtc_remove, .driver = { .owner = THIS_MODULE, .name = "rtc-dm355evm", -- cgit v1.2.3 From 67d326fa716f1521d435f18a4adae83a767cde69 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:06:56 -0700 Subject: drivers/rtc/rtc-da9055.c: remove empty function After the switch to devm_ functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Cc: David Dajun Chen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-da9055.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-da9055.c b/drivers/rtc/rtc-da9055.c index df4cf16a967d..e00642b61076 100644 --- a/drivers/rtc/rtc-da9055.c +++ b/drivers/rtc/rtc-da9055.c @@ -315,11 +315,6 @@ err_rtc: } -static int da9055_rtc_remove(struct platform_device *pdev) -{ - return 0; -} - #ifdef CONFIG_PM /* Turn off the alarm if it should not be a wake source. */ static int da9055_rtc_suspend(struct device *dev) @@ -392,7 +387,6 @@ static const struct dev_pm_ops da9055_rtc_pm_ops = { static struct platform_driver da9055_rtc_driver = { .probe = da9055_rtc_probe, - .remove = da9055_rtc_remove, .driver = { .name = "da9055-rtc", .owner = THIS_MODULE, -- cgit v1.2.3 From 8c0a4273306d2bf95d2448ff457847d7c59bec52 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:06:56 -0700 Subject: drivers/rtc/rtc-da9052.c: remove empty function After the switch to devm_ functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Cc: David Dajun Chen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-da9052.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-da9052.c b/drivers/rtc/rtc-da9052.c index a61ab7fa8a66..2f7fdd77a6f0 100644 --- a/drivers/rtc/rtc-da9052.c +++ b/drivers/rtc/rtc-da9052.c @@ -255,14 +255,8 @@ static int da9052_rtc_probe(struct platform_device *pdev) return 0; } -static int da9052_rtc_remove(struct platform_device *pdev) -{ - return 0; -} - static struct platform_driver da9052_rtc_driver = { .probe = da9052_rtc_probe, - .remove = da9052_rtc_remove, .driver = { .name = "da9052-rtc", .owner = THIS_MODULE, -- cgit v1.2.3 From d6c817586a9a5e1454c4e2f16f6f9968508168b6 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:06:57 -0700 Subject: drivers/rtc/rtc-bq4802.c: remove empty function After the switch to devm_ functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-bq4802.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-bq4802.c b/drivers/rtc/rtc-bq4802.c index b919168465f2..fc0ff87aa5df 100644 --- a/drivers/rtc/rtc-bq4802.c +++ b/drivers/rtc/rtc-bq4802.c @@ -186,11 +186,6 @@ out: } -static int bq4802_remove(struct platform_device *pdev) -{ - return 0; -} - /* work with hotplug and coldplug */ MODULE_ALIAS("platform:rtc-bq4802"); @@ -200,7 +195,6 @@ static struct platform_driver bq4802_driver = { .owner = THIS_MODULE, }, .probe = bq4802_probe, - .remove = bq4802_remove, }; module_platform_driver(bq4802_driver); -- cgit v1.2.3 From b3ecafd97be890fcfef5b2165a98c8eea01133c0 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:06:58 -0700 Subject: drivers/rtc/rtc-au1xxx.c: remove empty function After the switch to devm_ functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Cc: Manuel Lauss Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-au1xxx.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-au1xxx.c b/drivers/rtc/rtc-au1xxx.c index 7c6e7bb4cf00..ed526a192ce0 100644 --- a/drivers/rtc/rtc-au1xxx.c +++ b/drivers/rtc/rtc-au1xxx.c @@ -116,17 +116,11 @@ out_err: return ret; } -static int au1xtoy_rtc_remove(struct platform_device *pdev) -{ - return 0; -} - static struct platform_driver au1xrtc_driver = { .driver = { .name = "rtc-au1xxx", .owner = THIS_MODULE, }, - .remove = au1xtoy_rtc_remove, }; module_platform_driver_probe(au1xrtc_driver, au1xtoy_rtc_probe); -- cgit v1.2.3 From a2c0b85945e75901906d0aa82ef9a0bea96f85ce Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:06:59 -0700 Subject: drivers/rtc/rtc-ab3100.c: remove empty function After the switch to devm_ functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Acked-by: Linus Walleij Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-ab3100.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-ab3100.c b/drivers/rtc/rtc-ab3100.c index 572208d67688..ff435343ba9f 100644 --- a/drivers/rtc/rtc-ab3100.c +++ b/drivers/rtc/rtc-ab3100.c @@ -240,17 +240,11 @@ static int __init ab3100_rtc_probe(struct platform_device *pdev) return 0; } -static int __exit ab3100_rtc_remove(struct platform_device *pdev) -{ - return 0; -} - static struct platform_driver ab3100_rtc_driver = { .driver = { .name = "ab3100-rtc", .owner = THIS_MODULE, }, - .remove = __exit_p(ab3100_rtc_remove), }; module_platform_driver_probe(ab3100_rtc_driver, ab3100_rtc_probe); -- cgit v1.2.3 From 7e3c741abdae964b8f8aa9f46cf51143e2d0f686 Mon Sep 17 00:00:00 2001 From: Alexander Holler Date: Wed, 3 Jul 2013 15:07:00 -0700 Subject: rtc: rtc-hid-sensor-time: allow full years (16bit) in HID reports The draft for HID-sensors (HUTRR39) currently doesn't define the range for the attribute year. Asking one of the authors revealed that full years (e.g. 2013 instead of just 13) were meant. So we now allow both, 8 bit and 16 bit values for the attribute year and assuming full years when the value is 16 bits wide. We will still support 8 bit values until the specification gets final (and maybe defines a way to set the time too). Signed-off-by: Alexander Holler Cc: Alessandro Zummo Cc: Lars-Peter Clausen Cc: Jonathan Cameron Cc: Jiri Kosina Cc: John Stultz Cc: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-hid-sensor-time.c | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/drivers/rtc/rtc-hid-sensor-time.c b/drivers/rtc/rtc-hid-sensor-time.c index 63024505dddc..b8c1b106a0b7 100644 --- a/drivers/rtc/rtc-hid-sensor-time.c +++ b/drivers/rtc/rtc-hid-sensor-time.c @@ -85,10 +85,13 @@ static int hid_time_capture_sample(struct hid_sensor_hub_device *hsdev, switch (usage_id) { case HID_USAGE_SENSOR_TIME_YEAR: - time_buf->tm_year = *(u8 *)raw_data; - if (time_buf->tm_year < 70) - /* assume we are in 1970...2069 */ - time_buf->tm_year += 100; + if (raw_len == 1) { + time_buf->tm_year = *(u8 *)raw_data; + if (time_buf->tm_year < 70) + /* assume we are in 1970...2069 */ + time_buf->tm_year += 100; + } else + time_buf->tm_year = *(u16 *)raw_data-1900; break; case HID_USAGE_SENSOR_TIME_MONTH: /* sensor sending the month as 1-12, we need 0-11 */ @@ -151,11 +154,27 @@ static int hid_time_parse_report(struct platform_device *pdev, return -EINVAL; } if (time_state->info[i].size != 1) { - dev_err(&pdev->dev, - "attribute '%s' not 8 bits wide!\n", + /* + * The draft for HID-sensors (HUTRR39) currently + * doesn't define the range for the year attribute. + * Therefor we support both 8 bit (0-99) and 16 bit + * (full) as size for the year. + */ + if (time_state->info[i].attrib_id != + HID_USAGE_SENSOR_TIME_YEAR) { + dev_err(&pdev->dev, + "attribute '%s' not 8 bits wide!\n", hid_time_attrib_name( time_state->info[i].attrib_id)); - return -EINVAL; + return -EINVAL; + } + if (time_state->info[i].size != 2) { + dev_err(&pdev->dev, + "attribute '%s' not 8 or 16 bits wide!\n", + hid_time_attrib_name( + time_state->info[i].attrib_id)); + return -EINVAL; + } } if (time_state->info[i].units != HID_USAGE_SENSOR_UNITS_NOT_SPECIFIED && -- cgit v1.2.3 From c67d96e317510e63e8a24d3a5742dd47b21c3340 Mon Sep 17 00:00:00 2001 From: Alexander Holler Date: Wed, 3 Jul 2013 15:07:02 -0700 Subject: rtc: rtc-hid-sensor-time: allow 16 and 32 bit values for all attributes. There is no real reason to not support 16 or 32 bit values too. Signed-off-by: Alexander Holler Cc: Alessandro Zummo Cc: Lars-Peter Clausen Cc: Jonathan Cameron Cc: Jiri Kosina Cc: John Stultz Cc: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-hid-sensor-time.c | 59 +++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/drivers/rtc/rtc-hid-sensor-time.c b/drivers/rtc/rtc-hid-sensor-time.c index b8c1b106a0b7..7273b0139e5c 100644 --- a/drivers/rtc/rtc-hid-sensor-time.c +++ b/drivers/rtc/rtc-hid-sensor-time.c @@ -76,6 +76,20 @@ static int hid_time_proc_event(struct hid_sensor_hub_device *hsdev, return 0; } +static u32 hid_time_value(size_t raw_len, char *raw_data) +{ + switch (raw_len) { + case 1: + return *(u8 *)raw_data; + case 2: + return *(u16 *)raw_data; + case 4: + return *(u32 *)raw_data; + default: + return (u32)(~0U); /* 0xff... or -1 to denote an error */ + } +} + static int hid_time_capture_sample(struct hid_sensor_hub_device *hsdev, unsigned usage_id, size_t raw_len, char *raw_data, void *priv) @@ -85,29 +99,35 @@ static int hid_time_capture_sample(struct hid_sensor_hub_device *hsdev, switch (usage_id) { case HID_USAGE_SENSOR_TIME_YEAR: + /* + * The draft for HID-sensors (HUTRR39) currently doesn't define + * the range for the year attribute. Therefor we support + * 8 bit (0-99) and 16 or 32 bits (full) as size for the year. + */ if (raw_len == 1) { time_buf->tm_year = *(u8 *)raw_data; if (time_buf->tm_year < 70) /* assume we are in 1970...2069 */ time_buf->tm_year += 100; } else - time_buf->tm_year = *(u16 *)raw_data-1900; + time_buf->tm_year = + (int)hid_time_value(raw_len, raw_data)-1900; break; case HID_USAGE_SENSOR_TIME_MONTH: - /* sensor sending the month as 1-12, we need 0-11 */ - time_buf->tm_mon = *(u8 *)raw_data-1; + /* sensors are sending the month as 1-12, we need 0-11 */ + time_buf->tm_mon = (int)hid_time_value(raw_len, raw_data)-1; break; case HID_USAGE_SENSOR_TIME_DAY: - time_buf->tm_mday = *(u8 *)raw_data; + time_buf->tm_mday = (int)hid_time_value(raw_len, raw_data); break; case HID_USAGE_SENSOR_TIME_HOUR: - time_buf->tm_hour = *(u8 *)raw_data; + time_buf->tm_hour = (int)hid_time_value(raw_len, raw_data); break; case HID_USAGE_SENSOR_TIME_MINUTE: - time_buf->tm_min = *(u8 *)raw_data; + time_buf->tm_min = (int)hid_time_value(raw_len, raw_data); break; case HID_USAGE_SENSOR_TIME_SECOND: - time_buf->tm_sec = *(u8 *)raw_data; + time_buf->tm_sec = (int)hid_time_value(raw_len, raw_data); break; default: return -EINVAL; @@ -153,28 +173,13 @@ static int hid_time_parse_report(struct platform_device *pdev, "not all needed attributes inside the same report!\n"); return -EINVAL; } - if (time_state->info[i].size != 1) { - /* - * The draft for HID-sensors (HUTRR39) currently - * doesn't define the range for the year attribute. - * Therefor we support both 8 bit (0-99) and 16 bit - * (full) as size for the year. - */ - if (time_state->info[i].attrib_id != - HID_USAGE_SENSOR_TIME_YEAR) { - dev_err(&pdev->dev, - "attribute '%s' not 8 bits wide!\n", - hid_time_attrib_name( - time_state->info[i].attrib_id)); - return -EINVAL; - } - if (time_state->info[i].size != 2) { - dev_err(&pdev->dev, - "attribute '%s' not 8 or 16 bits wide!\n", + if (time_state->info[i].size == 3 || + time_state->info[i].size > 4) { + dev_err(&pdev->dev, + "attribute '%s' not 8, 16 or 32 bits wide!\n", hid_time_attrib_name( time_state->info[i].attrib_id)); - return -EINVAL; - } + return -EINVAL; } if (time_state->info[i].units != HID_USAGE_SENSOR_UNITS_NOT_SPECIFIED && -- cgit v1.2.3 From 1df0a4711f6e93c1073dd82f6e7905748842e2b3 Mon Sep 17 00:00:00 2001 From: Bernie Thompson Date: Wed, 3 Jul 2013 15:07:03 -0700 Subject: rtc: add ability to push out an existing wakealarm using sysfs This adds the ability for the rtc sysfs code to handle += characters at the beginning of a wakealarm setting string. This will allow the user to attempt to push out an existing wakealarm by a provided amount. In the case that the += characters are provided but the alarm is not active -EINVAL is returned. his is useful, at least for my purposes in suspend/resume testing. The basic test goes something like: 1. Set a wake alarm from userspace 5 seconds in the future 2. Start the suspend process (echo mem > /sys/power/state) 3. After ~2.5 seconds if userspace is still running (using another thread to check this), move the wake alarm 5 more seconds If the "move" involves an unset of the wakealarm then there's a period of time where the system is midway through suspending but has no wake alarm. It will get stuck. We'd rather not remove the "move" since the idea is to avoid a cancelled suspend when the alarm fires _during_ suspend. It is difficult for the test to tell the difference between a suspend that was cancelled because the alarm fired too early and a suspend that was Signed-off-by: Bernie Thompson Cc: Alessandro Zummo Cc: Doug Anderson Cc: "Rafael J. Wysocki" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/rtc.txt | 7 ++++--- drivers/rtc/rtc-sysfs.c | 20 +++++++++++++++----- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/Documentation/rtc.txt b/Documentation/rtc.txt index 32aa4002de4a..596b60c08b74 100644 --- a/Documentation/rtc.txt +++ b/Documentation/rtc.txt @@ -153,9 +153,10 @@ since_epoch: The number of seconds since the epoch according to the RTC time: RTC-provided time wakealarm: The time at which the clock will generate a system wakeup event. This is a one shot wakeup event, so must be reset - after wake if a daily wakeup is required. Format is either - seconds since the epoch or, if there's a leading +, seconds - in the future. + after wake if a daily wakeup is required. Format is seconds since + the epoch by default, or if there's a leading +, seconds in the + future, or if there is a leading +=, seconds ahead of the current + alarm. IOCTL INTERFACE --------------- diff --git a/drivers/rtc/rtc-sysfs.c b/drivers/rtc/rtc-sysfs.c index b70e2bb63645..4b26f8672b2d 100644 --- a/drivers/rtc/rtc-sysfs.c +++ b/drivers/rtc/rtc-sysfs.c @@ -164,6 +164,7 @@ rtc_sysfs_set_wakealarm(struct device *dev, struct device_attribute *attr, { ssize_t retval; unsigned long now, alarm; + unsigned long push = 0; struct rtc_wkalrm alm; struct rtc_device *rtc = to_rtc_device(dev); char *buf_ptr; @@ -180,13 +181,17 @@ rtc_sysfs_set_wakealarm(struct device *dev, struct device_attribute *attr, buf_ptr = (char *)buf; if (*buf_ptr == '+') { buf_ptr++; - adjust = 1; + if (*buf_ptr == '=') { + buf_ptr++; + push = 1; + } else + adjust = 1; } alarm = simple_strtoul(buf_ptr, NULL, 0); if (adjust) { alarm += now; } - if (alarm > now) { + if (alarm > now || push) { /* Avoid accidentally clobbering active alarms; we can't * entirely prevent that here, without even the minimal * locking from the /dev/rtcN api. @@ -194,9 +199,14 @@ rtc_sysfs_set_wakealarm(struct device *dev, struct device_attribute *attr, retval = rtc_read_alarm(rtc, &alm); if (retval < 0) return retval; - if (alm.enabled) - return -EBUSY; - + if (alm.enabled) { + if (push) { + rtc_tm_to_time(&alm.time, &push); + alarm += push; + } else + return -EBUSY; + } else if (push) + return -EINVAL; alm.enabled = 1; } else { alm.enabled = 0; -- cgit v1.2.3 From 812f147853a14812b260620b19e0764cdeb71b4c Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 3 Jul 2013 15:07:04 -0700 Subject: drivers/rtc/rtc-vr41xx.c: fix error return code in rtc_probe() Fix to return -EBUSY in the platform irq get error handling case instead of 0, as done elsewhere in this function. Signed-off-by: Wei Yongjun Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-vr41xx.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-vr41xx.c b/drivers/rtc/rtc-vr41xx.c index 3b5b4fa9a6e9..54e104e197e3 100644 --- a/drivers/rtc/rtc-vr41xx.c +++ b/drivers/rtc/rtc-vr41xx.c @@ -339,8 +339,10 @@ static int rtc_probe(struct platform_device *pdev) goto err_device_unregister; pie_irq = platform_get_irq(pdev, 1); - if (pie_irq <= 0) + if (pie_irq <= 0) { + retval = -EBUSY; goto err_free_irq; + } retval = request_irq(pie_irq, rtclong1_interrupt, 0, "rtclong1", pdev); -- cgit v1.2.3 From edca66d2ceae6ba426b9250ffcad5bca820b146f Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:07:05 -0700 Subject: rtc: rtc-ds1307: use devm_*() functions Use devm_*() functions to make cleanup paths simpler. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-ds1307.c | 43 +++++++++++++++++-------------------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index b53992ab3090..ca18fd1433b3 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -683,7 +683,7 @@ static int ds1307_probe(struct i2c_client *client, && !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) return -EIO; - ds1307 = kzalloc(sizeof(struct ds1307), GFP_KERNEL); + ds1307 = devm_kzalloc(&client->dev, sizeof(struct ds1307), GFP_KERNEL); if (!ds1307) return -ENOMEM; @@ -715,7 +715,7 @@ static int ds1307_probe(struct i2c_client *client, if (tmp != 2) { dev_dbg(&client->dev, "read error %d\n", tmp); err = -EIO; - goto exit_free; + goto exit; } /* oscillator off? turn it on, so clock can tick. */ @@ -754,7 +754,7 @@ static int ds1307_probe(struct i2c_client *client, if (tmp != 2) { dev_dbg(&client->dev, "read error %d\n", tmp); err = -EIO; - goto exit_free; + goto exit; } /* oscillator off? turn it on, so clock can tick. */ @@ -798,7 +798,7 @@ static int ds1307_probe(struct i2c_client *client, if (tmp != 2) { dev_dbg(&client->dev, "read error %d\n", tmp); err = -EIO; - goto exit_free; + goto exit; } /* correct hour */ @@ -826,7 +826,7 @@ read_rtc: if (tmp != 8) { dev_dbg(&client->dev, "read error %d\n", tmp); err = -EIO; - goto exit_free; + goto exit; } /* @@ -868,7 +868,7 @@ read_rtc: if (tmp < 0) { dev_dbg(&client->dev, "read error %d\n", tmp); err = -EIO; - goto exit_free; + goto exit; } /* oscillator fault? clear flag, and warn */ @@ -927,13 +927,13 @@ read_rtc: bin2bcd(tmp)); } - ds1307->rtc = rtc_device_register(client->name, &client->dev, + ds1307->rtc = devm_rtc_device_register(&client->dev, client->name, &ds13xx_rtc_ops, THIS_MODULE); if (IS_ERR(ds1307->rtc)) { err = PTR_ERR(ds1307->rtc); dev_err(&client->dev, "unable to register the class device\n"); - goto exit_free; + goto exit; } if (want_irq) { @@ -942,7 +942,7 @@ read_rtc: if (err) { dev_err(&client->dev, "unable to request IRQ!\n"); - goto exit_irq; + goto exit; } device_set_wakeup_capable(&client->dev, 1); @@ -951,11 +951,12 @@ read_rtc: } if (chip->nvram_size) { - ds1307->nvram = kzalloc(sizeof(struct bin_attribute), - GFP_KERNEL); + ds1307->nvram = devm_kzalloc(&client->dev, + sizeof(struct bin_attribute), + GFP_KERNEL); if (!ds1307->nvram) { err = -ENOMEM; - goto exit_nvram; + goto exit; } ds1307->nvram->attr.name = "nvram"; ds1307->nvram->attr.mode = S_IRUGO | S_IWUSR; @@ -965,21 +966,15 @@ read_rtc: ds1307->nvram->size = chip->nvram_size; ds1307->nvram_offset = chip->nvram_offset; err = sysfs_create_bin_file(&client->dev.kobj, ds1307->nvram); - if (err) { - kfree(ds1307->nvram); - goto exit_nvram; - } + if (err) + goto exit; set_bit(HAS_NVRAM, &ds1307->flags); dev_info(&client->dev, "%zu bytes nvram\n", ds1307->nvram->size); } return 0; -exit_nvram: -exit_irq: - rtc_device_unregister(ds1307->rtc); -exit_free: - kfree(ds1307); +exit: return err; } @@ -992,13 +987,9 @@ static int ds1307_remove(struct i2c_client *client) cancel_work_sync(&ds1307->work); } - if (test_and_clear_bit(HAS_NVRAM, &ds1307->flags)) { + if (test_and_clear_bit(HAS_NVRAM, &ds1307->flags)) sysfs_remove_bin_file(&client->dev.kobj, ds1307->nvram); - kfree(ds1307->nvram); - } - rtc_device_unregister(ds1307->rtc); - kfree(ds1307); return 0; } -- cgit v1.2.3 From c08ac4894c2079531f0310b1e9b7aba7a5b8b9be Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:07:06 -0700 Subject: rtc: rtc-jz4740: use devm_*() functions Use devm_*() functions to make cleanup paths simpler. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-jz4740.c | 65 ++++++++++++------------------------------------ 1 file changed, 16 insertions(+), 49 deletions(-) diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c index c6573585d028..1b126d2513de 100644 --- a/drivers/rtc/rtc-jz4740.c +++ b/drivers/rtc/rtc-jz4740.c @@ -14,6 +14,7 @@ * */ +#include #include #include #include @@ -216,37 +217,34 @@ static int jz4740_rtc_probe(struct platform_device *pdev) struct jz4740_rtc *rtc; uint32_t scratchpad; - rtc = kzalloc(sizeof(*rtc), GFP_KERNEL); + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); if (!rtc) return -ENOMEM; rtc->irq = platform_get_irq(pdev, 0); if (rtc->irq < 0) { - ret = -ENOENT; dev_err(&pdev->dev, "Failed to get platform irq\n"); - goto err_free; + return -ENOENT; } rtc->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!rtc->mem) { - ret = -ENOENT; dev_err(&pdev->dev, "Failed to get platform mmio memory\n"); - goto err_free; + return -ENOENT; } - rtc->mem = request_mem_region(rtc->mem->start, resource_size(rtc->mem), - pdev->name); + rtc->mem = devm_request_mem_region(&pdev->dev, rtc->mem->start, + resource_size(rtc->mem), pdev->name); if (!rtc->mem) { - ret = -EBUSY; dev_err(&pdev->dev, "Failed to request mmio memory region\n"); - goto err_free; + return -EBUSY; } - rtc->base = ioremap_nocache(rtc->mem->start, resource_size(rtc->mem)); + rtc->base = devm_ioremap_nocache(&pdev->dev, rtc->mem->start, + resource_size(rtc->mem)); if (!rtc->base) { - ret = -EBUSY; dev_err(&pdev->dev, "Failed to ioremap mmio memory\n"); - goto err_release_mem_region; + return -EBUSY; } spin_lock_init(&rtc->lock); @@ -255,19 +253,19 @@ static int jz4740_rtc_probe(struct platform_device *pdev) device_init_wakeup(&pdev->dev, 1); - rtc->rtc = rtc_device_register(pdev->name, &pdev->dev, &jz4740_rtc_ops, - THIS_MODULE); + rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, + &jz4740_rtc_ops, THIS_MODULE); if (IS_ERR(rtc->rtc)) { ret = PTR_ERR(rtc->rtc); dev_err(&pdev->dev, "Failed to register rtc device: %d\n", ret); - goto err_iounmap; + return ret; } - ret = request_irq(rtc->irq, jz4740_rtc_irq, 0, + ret = devm_request_irq(&pdev->dev, rtc->irq, jz4740_rtc_irq, 0, pdev->name, rtc); if (ret) { dev_err(&pdev->dev, "Failed to request rtc irq: %d\n", ret); - goto err_unregister_rtc; + return ret; } scratchpad = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SCRATCHPAD); @@ -276,43 +274,13 @@ static int jz4740_rtc_probe(struct platform_device *pdev) ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC, 0); if (ret) { dev_err(&pdev->dev, "Could not write write to RTC registers\n"); - goto err_free_irq; + return ret; } } return 0; - -err_free_irq: - free_irq(rtc->irq, rtc); -err_unregister_rtc: - rtc_device_unregister(rtc->rtc); -err_iounmap: - iounmap(rtc->base); -err_release_mem_region: - release_mem_region(rtc->mem->start, resource_size(rtc->mem)); -err_free: - kfree(rtc); - - return ret; } -static int jz4740_rtc_remove(struct platform_device *pdev) -{ - struct jz4740_rtc *rtc = platform_get_drvdata(pdev); - - free_irq(rtc->irq, rtc); - - rtc_device_unregister(rtc->rtc); - - iounmap(rtc->base); - release_mem_region(rtc->mem->start, resource_size(rtc->mem)); - - kfree(rtc); - - return 0; -} - - #ifdef CONFIG_PM static int jz4740_rtc_suspend(struct device *dev) { @@ -344,7 +312,6 @@ static const struct dev_pm_ops jz4740_pm_ops = { static struct platform_driver jz4740_rtc_driver = { .probe = jz4740_rtc_probe, - .remove = jz4740_rtc_remove, .driver = { .name = "jz4740-rtc", .owner = THIS_MODULE, -- cgit v1.2.3 From 073bf424602992427e3dbf83a1dca47ed119326a Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:07:07 -0700 Subject: rtc: rtc-mpc5121: use devm_*() functions Use devm_*() functions to make cleanup paths simpler. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-mpc5121.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/rtc/rtc-mpc5121.c b/drivers/rtc/rtc-mpc5121.c index 213006bfc69a..4c024974c69f 100644 --- a/drivers/rtc/rtc-mpc5121.c +++ b/drivers/rtc/rtc-mpc5121.c @@ -312,15 +312,14 @@ static int mpc5121_rtc_probe(struct platform_device *op) struct mpc5121_rtc_data *rtc; int err = 0; - rtc = kzalloc(sizeof(*rtc), GFP_KERNEL); + rtc = devm_kzalloc(&op->dev, sizeof(*rtc), GFP_KERNEL); if (!rtc) return -ENOMEM; rtc->regs = of_iomap(op->dev.of_node, 0); if (!rtc->regs) { dev_err(&op->dev, "%s: couldn't map io space\n", __func__); - err = -ENOSYS; - goto out_free; + return -ENOSYS; } device_init_wakeup(&op->dev, 1); @@ -354,10 +353,10 @@ static int mpc5121_rtc_probe(struct platform_device *op) out_be32(&rtc->regs->keep_alive, ka); } - rtc->rtc = rtc_device_register("mpc5121-rtc", &op->dev, + rtc->rtc = devm_rtc_device_register(&op->dev, "mpc5121-rtc", &mpc5121_rtc_ops, THIS_MODULE); } else { - rtc->rtc = rtc_device_register("mpc5200-rtc", &op->dev, + rtc->rtc = devm_rtc_device_register(&op->dev, "mpc5200-rtc", &mpc5200_rtc_ops, THIS_MODULE); } @@ -377,8 +376,6 @@ out_dispose2: out_dispose: irq_dispose_mapping(rtc->irq); iounmap(rtc->regs); -out_free: - kfree(rtc); return err; } @@ -392,14 +389,11 @@ static int mpc5121_rtc_remove(struct platform_device *op) out_8(®s->alm_enable, 0); out_8(®s->int_enable, in_8(®s->int_enable) & ~0x1); - rtc_device_unregister(rtc->rtc); iounmap(rtc->regs); free_irq(rtc->irq, &op->dev); free_irq(rtc->irq_periodic, &op->dev); irq_dispose_mapping(rtc->irq); irq_dispose_mapping(rtc->irq_periodic); - dev_set_drvdata(&op->dev, NULL); - kfree(rtc); return 0; } -- cgit v1.2.3 From 19b8d8875fd5f231135a55bfe53337859ec73a4a Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:07:08 -0700 Subject: rtc: rtc-m48t59: use devm_*() functions Use devm_*() functions to make cleanup paths simpler. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-m48t59.c | 53 ++++++++++++++++-------------------------------- 1 file changed, 17 insertions(+), 36 deletions(-) diff --git a/drivers/rtc/rtc-m48t59.c b/drivers/rtc/rtc-m48t59.c index d4d31fa19244..fcb03291f145 100644 --- a/drivers/rtc/rtc-m48t59.c +++ b/drivers/rtc/rtc-m48t59.c @@ -409,7 +409,8 @@ static int m48t59_rtc_probe(struct platform_device *pdev) } else if (res->flags & IORESOURCE_MEM) { /* we are memory-mapped */ if (!pdata) { - pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), + GFP_KERNEL); if (!pdata) return -ENOMEM; /* Ensure we only kmalloc platform data once */ @@ -425,7 +426,7 @@ static int m48t59_rtc_probe(struct platform_device *pdev) pdata->read_byte = m48t59_mem_readb; } - m48t59 = kzalloc(sizeof(*m48t59), GFP_KERNEL); + m48t59 = devm_kzalloc(&pdev->dev, sizeof(*m48t59), GFP_KERNEL); if (!m48t59) return -ENOMEM; @@ -433,9 +434,10 @@ static int m48t59_rtc_probe(struct platform_device *pdev) if (!m48t59->ioaddr) { /* ioaddr not mapped externally */ - m48t59->ioaddr = ioremap(res->start, resource_size(res)); + m48t59->ioaddr = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); if (!m48t59->ioaddr) - goto out; + return ret; } /* Try to get irq number. We also can work in @@ -446,10 +448,11 @@ static int m48t59_rtc_probe(struct platform_device *pdev) m48t59->irq = NO_IRQ; if (m48t59->irq != NO_IRQ) { - ret = request_irq(m48t59->irq, m48t59_rtc_interrupt, - IRQF_SHARED, "rtc-m48t59", &pdev->dev); + ret = devm_request_irq(&pdev->dev, m48t59->irq, + m48t59_rtc_interrupt, IRQF_SHARED, + "rtc-m48t59", &pdev->dev); if (ret) - goto out; + return ret; } switch (pdata->type) { case M48T59RTC_TYPE_M48T59: @@ -469,51 +472,29 @@ static int m48t59_rtc_probe(struct platform_device *pdev) break; default: dev_err(&pdev->dev, "Unknown RTC type\n"); - ret = -ENODEV; - goto out; + return -ENODEV; } spin_lock_init(&m48t59->lock); platform_set_drvdata(pdev, m48t59); - m48t59->rtc = rtc_device_register(name, &pdev->dev, ops, THIS_MODULE); - if (IS_ERR(m48t59->rtc)) { - ret = PTR_ERR(m48t59->rtc); - goto out; - } + m48t59->rtc = devm_rtc_device_register(&pdev->dev, name, ops, + THIS_MODULE); + if (IS_ERR(m48t59->rtc)) + return PTR_ERR(m48t59->rtc); m48t59_nvram_attr.size = pdata->offset; ret = sysfs_create_bin_file(&pdev->dev.kobj, &m48t59_nvram_attr); - if (ret) { - rtc_device_unregister(m48t59->rtc); - goto out; - } + if (ret) + return ret; return 0; - -out: - if (m48t59->irq != NO_IRQ) - free_irq(m48t59->irq, &pdev->dev); - if (m48t59->ioaddr) - iounmap(m48t59->ioaddr); - kfree(m48t59); - return ret; } static int m48t59_rtc_remove(struct platform_device *pdev) { - struct m48t59_private *m48t59 = platform_get_drvdata(pdev); - struct m48t59_plat_data *pdata = pdev->dev.platform_data; - sysfs_remove_bin_file(&pdev->dev.kobj, &m48t59_nvram_attr); - if (!IS_ERR(m48t59->rtc)) - rtc_device_unregister(m48t59->rtc); - if (m48t59->ioaddr && !pdata->ioaddr) - iounmap(m48t59->ioaddr); - if (m48t59->irq != NO_IRQ) - free_irq(m48t59->irq, &pdev->dev); - kfree(m48t59); return 0; } -- cgit v1.2.3 From c417299ce7d77521225f2aff471d3f7ee5f34b1e Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:07:09 -0700 Subject: rtc: rtc-pm8xxx: use devm_*() functions Use devm_*() functions to make cleanup paths simpler. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-pm8xxx.c | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/drivers/rtc/rtc-pm8xxx.c b/drivers/rtc/rtc-pm8xxx.c index 14ee860d5a3f..03f8f75d5af2 100644 --- a/drivers/rtc/rtc-pm8xxx.c +++ b/drivers/rtc/rtc-pm8xxx.c @@ -395,7 +395,7 @@ static int pm8xxx_rtc_probe(struct platform_device *pdev) if (pdata != NULL) rtc_write_enable = pdata->rtc_write_enable; - rtc_dd = kzalloc(sizeof(*rtc_dd), GFP_KERNEL); + rtc_dd = devm_kzalloc(&pdev->dev, sizeof(*rtc_dd), GFP_KERNEL); if (rtc_dd == NULL) { dev_err(&pdev->dev, "Unable to allocate memory!\n"); return -ENOMEM; @@ -407,16 +407,14 @@ static int pm8xxx_rtc_probe(struct platform_device *pdev) rtc_dd->rtc_alarm_irq = platform_get_irq(pdev, 0); if (rtc_dd->rtc_alarm_irq < 0) { dev_err(&pdev->dev, "Alarm IRQ resource absent!\n"); - rc = -ENXIO; - goto fail_rtc_enable; + return -ENXIO; } rtc_resource = platform_get_resource_byname(pdev, IORESOURCE_IO, "pmic_rtc_base"); if (!(rtc_resource && rtc_resource->start)) { dev_err(&pdev->dev, "RTC IO resource absent!\n"); - rc = -ENXIO; - goto fail_rtc_enable; + return -ENXIO; } rtc_dd->rtc_base = rtc_resource->start; @@ -432,7 +430,7 @@ static int pm8xxx_rtc_probe(struct platform_device *pdev) rc = pm8xxx_read_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base, 1); if (rc < 0) { dev_err(&pdev->dev, "RTC control register read failed!\n"); - goto fail_rtc_enable; + return rc; } if (!(ctrl_reg & PM8xxx_RTC_ENABLE)) { @@ -442,7 +440,7 @@ static int pm8xxx_rtc_probe(struct platform_device *pdev) if (rc < 0) { dev_err(&pdev->dev, "Write to RTC control register " "failed\n"); - goto fail_rtc_enable; + return rc; } } @@ -453,13 +451,12 @@ static int pm8xxx_rtc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, rtc_dd); /* Register the RTC device */ - rtc_dd->rtc = rtc_device_register("pm8xxx_rtc", &pdev->dev, + rtc_dd->rtc = devm_rtc_device_register(&pdev->dev, "pm8xxx_rtc", &pm8xxx_rtc_ops, THIS_MODULE); if (IS_ERR(rtc_dd->rtc)) { dev_err(&pdev->dev, "%s: RTC registration failed (%ld)\n", __func__, PTR_ERR(rtc_dd->rtc)); - rc = PTR_ERR(rtc_dd->rtc); - goto fail_rtc_enable; + return PTR_ERR(rtc_dd->rtc); } /* Request the alarm IRQ */ @@ -468,7 +465,7 @@ static int pm8xxx_rtc_probe(struct platform_device *pdev) "pm8xxx_rtc_alarm", rtc_dd); if (rc < 0) { dev_err(&pdev->dev, "Request IRQ failed (%d)\n", rc); - goto fail_req_irq; + return rc; } device_init_wakeup(&pdev->dev, 1); @@ -476,12 +473,6 @@ static int pm8xxx_rtc_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "Probe success !!\n"); return 0; - -fail_req_irq: - rtc_device_unregister(rtc_dd->rtc); -fail_rtc_enable: - kfree(rtc_dd); - return rc; } static int pm8xxx_rtc_remove(struct platform_device *pdev) @@ -490,8 +481,6 @@ static int pm8xxx_rtc_remove(struct platform_device *pdev) device_init_wakeup(&pdev->dev, 0); free_irq(rtc_dd->rtc_alarm_irq, rtc_dd); - rtc_device_unregister(rtc_dd->rtc); - kfree(rtc_dd); return 0; } -- cgit v1.2.3 From 618c300305c6ee7c71a1493da97c48c24205268c Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:07:09 -0700 Subject: rtc: rtc-pxa: use devm_*() functions Use devm_*() functions to make cleanup paths simpler. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-pxa.c | 43 ++++++++++++------------------------------- 1 file changed, 12 insertions(+), 31 deletions(-) diff --git a/drivers/rtc/rtc-pxa.c b/drivers/rtc/rtc-pxa.c index ed037ae91c5f..a355f2b82bb8 100644 --- a/drivers/rtc/rtc-pxa.c +++ b/drivers/rtc/rtc-pxa.c @@ -324,37 +324,35 @@ static int __init pxa_rtc_probe(struct platform_device *pdev) int ret; u32 rttr; - pxa_rtc = kzalloc(sizeof(struct pxa_rtc), GFP_KERNEL); + pxa_rtc = devm_kzalloc(dev, sizeof(*pxa_rtc), GFP_KERNEL); if (!pxa_rtc) return -ENOMEM; spin_lock_init(&pxa_rtc->lock); platform_set_drvdata(pdev, pxa_rtc); - ret = -ENXIO; pxa_rtc->ress = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!pxa_rtc->ress) { dev_err(dev, "No I/O memory resource defined\n"); - goto err_ress; + return -ENXIO; } pxa_rtc->irq_1Hz = platform_get_irq(pdev, 0); if (pxa_rtc->irq_1Hz < 0) { dev_err(dev, "No 1Hz IRQ resource defined\n"); - goto err_ress; + return -ENXIO; } pxa_rtc->irq_Alrm = platform_get_irq(pdev, 1); if (pxa_rtc->irq_Alrm < 0) { dev_err(dev, "No alarm IRQ resource defined\n"); - goto err_ress; + return -ENXIO; } pxa_rtc_open(dev); - ret = -ENOMEM; - pxa_rtc->base = ioremap(pxa_rtc->ress->start, + pxa_rtc->base = devm_ioremap(dev, pxa_rtc->ress->start, resource_size(pxa_rtc->ress)); if (!pxa_rtc->base) { - dev_err(&pdev->dev, "Unable to map pxa RTC I/O memory\n"); - goto err_map; + dev_err(dev, "Unable to map pxa RTC I/O memory\n"); + return -ENOMEM; } /* @@ -370,41 +368,24 @@ static int __init pxa_rtc_probe(struct platform_device *pdev) rtsr_clear_bits(pxa_rtc, RTSR_PIALE | RTSR_RDALE1 | RTSR_HZE); - pxa_rtc->rtc = rtc_device_register("pxa-rtc", &pdev->dev, &pxa_rtc_ops, - THIS_MODULE); - ret = PTR_ERR(pxa_rtc->rtc); + pxa_rtc->rtc = devm_rtc_device_register(&pdev->dev, "pxa-rtc", + &pxa_rtc_ops, THIS_MODULE); if (IS_ERR(pxa_rtc->rtc)) { + ret = PTR_ERR(pxa_rtc->rtc); dev_err(dev, "Failed to register RTC device -> %d\n", ret); - goto err_rtc_reg; + return ret; } device_init_wakeup(dev, 1); return 0; - -err_rtc_reg: - iounmap(pxa_rtc->base); -err_ress: -err_map: - kfree(pxa_rtc); - return ret; } static int __exit pxa_rtc_remove(struct platform_device *pdev) { - struct pxa_rtc *pxa_rtc = platform_get_drvdata(pdev); - struct device *dev = &pdev->dev; - pxa_rtc_release(dev); - - rtc_device_unregister(pxa_rtc->rtc); - - spin_lock_irq(&pxa_rtc->lock); - iounmap(pxa_rtc->base); - spin_unlock_irq(&pxa_rtc->lock); - - kfree(pxa_rtc); + pxa_rtc_release(dev); return 0; } -- cgit v1.2.3 From fac42b414a42e54f327ead67fa1804078b0db7be Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:07:10 -0700 Subject: rtc: rtc-rx8025: use devm_*() functions Use devm_*() functions to make cleanup paths simpler. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-rx8025.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/drivers/rtc/rtc-rx8025.c b/drivers/rtc/rtc-rx8025.c index 0722d36b9c9a..8fa23eabcb68 100644 --- a/drivers/rtc/rtc-rx8025.c +++ b/drivers/rtc/rtc-rx8025.c @@ -549,7 +549,7 @@ static int rx8025_probe(struct i2c_client *client, goto errout; } - rx8025 = kzalloc(sizeof(*rx8025), GFP_KERNEL); + rx8025 = devm_kzalloc(&client->dev, sizeof(*rx8025), GFP_KERNEL); if (!rx8025) { dev_err(&adapter->dev, "failed to alloc memory\n"); err = -ENOMEM; @@ -562,7 +562,7 @@ static int rx8025_probe(struct i2c_client *client, err = rx8025_init_client(client, &need_reset); if (err) - goto errout_free; + goto errout; if (need_reset) { struct rtc_time tm; @@ -572,12 +572,12 @@ static int rx8025_probe(struct i2c_client *client, rx8025_set_time(&client->dev, &tm); } - rx8025->rtc = rtc_device_register(client->name, &client->dev, + rx8025->rtc = devm_rtc_device_register(&client->dev, client->name, &rx8025_rtc_ops, THIS_MODULE); if (IS_ERR(rx8025->rtc)) { err = PTR_ERR(rx8025->rtc); dev_err(&client->dev, "unable to register the class device\n"); - goto errout_free; + goto errout; } if (client->irq > 0) { @@ -586,7 +586,7 @@ static int rx8025_probe(struct i2c_client *client, 0, "rx8025", client); if (err) { dev_err(&client->dev, "unable to request IRQ\n"); - goto errout_reg; + goto errout; } } @@ -603,12 +603,6 @@ errout_irq: if (client->irq > 0) free_irq(client->irq, client); -errout_reg: - rtc_device_unregister(rx8025->rtc); - -errout_free: - kfree(rx8025); - errout: dev_err(&adapter->dev, "probing for rx8025 failed\n"); return err; @@ -629,8 +623,6 @@ static int rx8025_remove(struct i2c_client *client) } rx8025_sysfs_unregister(&client->dev); - rtc_device_unregister(rx8025->rtc); - kfree(rx8025); return 0; } -- cgit v1.2.3 From 0209affa6eefb4bb8288f8a0e678b1b36facabd5 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:07:11 -0700 Subject: rtc: rtc-sh: use devm_*() functions Use devm_*() functions to make cleanup paths simpler. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-sh.c | 72 +++++++++++++++------------------------------------- 1 file changed, 21 insertions(+), 51 deletions(-) diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c index cb2f8399d692..6d87e26355a3 100644 --- a/drivers/rtc/rtc-sh.c +++ b/drivers/rtc/rtc-sh.c @@ -593,7 +593,7 @@ static int __init sh_rtc_probe(struct platform_device *pdev) char clk_name[6]; int clk_id, ret; - rtc = kzalloc(sizeof(struct sh_rtc), GFP_KERNEL); + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); if (unlikely(!rtc)) return -ENOMEM; @@ -602,9 +602,8 @@ static int __init sh_rtc_probe(struct platform_device *pdev) /* get periodic/carry/alarm irqs */ ret = platform_get_irq(pdev, 0); if (unlikely(ret <= 0)) { - ret = -ENOENT; dev_err(&pdev->dev, "No IRQ resource\n"); - goto err_badres; + return -ENOENT; } rtc->periodic_irq = ret; @@ -613,24 +612,21 @@ static int __init sh_rtc_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_IO, 0); if (unlikely(res == NULL)) { - ret = -ENOENT; dev_err(&pdev->dev, "No IO resource\n"); - goto err_badres; + return -ENOENT; } rtc->regsize = resource_size(res); - rtc->res = request_mem_region(res->start, rtc->regsize, pdev->name); - if (unlikely(!rtc->res)) { - ret = -EBUSY; - goto err_badres; - } + rtc->res = devm_request_mem_region(&pdev->dev, res->start, + rtc->regsize, pdev->name); + if (unlikely(!rtc->res)) + return -EBUSY; - rtc->regbase = ioremap_nocache(rtc->res->start, rtc->regsize); - if (unlikely(!rtc->regbase)) { - ret = -EINVAL; - goto err_badmap; - } + rtc->regbase = devm_ioremap_nocache(&pdev->dev, rtc->res->start, + rtc->regsize); + if (unlikely(!rtc->regbase)) + return -EINVAL; clk_id = pdev->id; /* With a single device, the clock id is still "rtc0" */ @@ -639,7 +635,7 @@ static int __init sh_rtc_probe(struct platform_device *pdev) snprintf(clk_name, sizeof(clk_name), "rtc%d", clk_id); - rtc->clk = clk_get(&pdev->dev, clk_name); + rtc->clk = devm_clk_get(&pdev->dev, clk_name); if (IS_ERR(rtc->clk)) { /* * No error handling for rtc->clk intentionally, not all @@ -665,8 +661,8 @@ static int __init sh_rtc_probe(struct platform_device *pdev) if (rtc->carry_irq <= 0) { /* register shared periodic/carry/alarm irq */ - ret = request_irq(rtc->periodic_irq, sh_rtc_shared, - 0, "sh-rtc", rtc); + ret = devm_request_irq(&pdev->dev, rtc->periodic_irq, + sh_rtc_shared, 0, "sh-rtc", rtc); if (unlikely(ret)) { dev_err(&pdev->dev, "request IRQ failed with %d, IRQ %d\n", ret, @@ -675,8 +671,8 @@ static int __init sh_rtc_probe(struct platform_device *pdev) } } else { /* register periodic/carry/alarm irqs */ - ret = request_irq(rtc->periodic_irq, sh_rtc_periodic, - 0, "sh-rtc period", rtc); + ret = devm_request_irq(&pdev->dev, rtc->periodic_irq, + sh_rtc_periodic, 0, "sh-rtc period", rtc); if (unlikely(ret)) { dev_err(&pdev->dev, "request period IRQ failed with %d, IRQ %d\n", @@ -684,24 +680,21 @@ static int __init sh_rtc_probe(struct platform_device *pdev) goto err_unmap; } - ret = request_irq(rtc->carry_irq, sh_rtc_interrupt, - 0, "sh-rtc carry", rtc); + ret = devm_request_irq(&pdev->dev, rtc->carry_irq, + sh_rtc_interrupt, 0, "sh-rtc carry", rtc); if (unlikely(ret)) { dev_err(&pdev->dev, "request carry IRQ failed with %d, IRQ %d\n", ret, rtc->carry_irq); - free_irq(rtc->periodic_irq, rtc); goto err_unmap; } - ret = request_irq(rtc->alarm_irq, sh_rtc_alarm, - 0, "sh-rtc alarm", rtc); + ret = devm_request_irq(&pdev->dev, rtc->alarm_irq, + sh_rtc_alarm, 0, "sh-rtc alarm", rtc); if (unlikely(ret)) { dev_err(&pdev->dev, "request alarm IRQ failed with %d, IRQ %d\n", ret, rtc->alarm_irq); - free_irq(rtc->carry_irq, rtc); - free_irq(rtc->periodic_irq, rtc); goto err_unmap; } } @@ -714,13 +707,10 @@ static int __init sh_rtc_probe(struct platform_device *pdev) sh_rtc_setaie(&pdev->dev, 0); sh_rtc_setcie(&pdev->dev, 0); - rtc->rtc_dev = rtc_device_register("sh", &pdev->dev, + rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, "sh", &sh_rtc_ops, THIS_MODULE); if (IS_ERR(rtc->rtc_dev)) { ret = PTR_ERR(rtc->rtc_dev); - free_irq(rtc->periodic_irq, rtc); - free_irq(rtc->carry_irq, rtc); - free_irq(rtc->alarm_irq, rtc); goto err_unmap; } @@ -737,12 +727,6 @@ static int __init sh_rtc_probe(struct platform_device *pdev) err_unmap: clk_disable(rtc->clk); - clk_put(rtc->clk); - iounmap(rtc->regbase); -err_badmap: - release_mem_region(rtc->res->start, rtc->regsize); -err_badres: - kfree(rtc); return ret; } @@ -751,26 +735,12 @@ static int __exit sh_rtc_remove(struct platform_device *pdev) { struct sh_rtc *rtc = platform_get_drvdata(pdev); - rtc_device_unregister(rtc->rtc_dev); sh_rtc_irq_set_state(&pdev->dev, 0); sh_rtc_setaie(&pdev->dev, 0); sh_rtc_setcie(&pdev->dev, 0); - free_irq(rtc->periodic_irq, rtc); - - if (rtc->carry_irq > 0) { - free_irq(rtc->carry_irq, rtc); - free_irq(rtc->alarm_irq, rtc); - } - - iounmap(rtc->regbase); - release_mem_region(rtc->res->start, rtc->regsize); - clk_disable(rtc->clk); - clk_put(rtc->clk); - - kfree(rtc); return 0; } -- cgit v1.2.3 From 3a02893f4e70725089b838053e916cc026ed93af Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:07:12 -0700 Subject: rtc: rtc-coh901331: use platform_{get,set}_drvdata() Use the wrapper functions for getting and setting the driver data using platform_device instead of using dev_{get,set}_drvdata() with &pdev->dev, so we can directly pass a struct platform_device. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-coh901331.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/rtc/rtc-coh901331.c b/drivers/rtc/rtc-coh901331.c index c05b2021d25b..73f157519dff 100644 --- a/drivers/rtc/rtc-coh901331.c +++ b/drivers/rtc/rtc-coh901331.c @@ -152,7 +152,7 @@ static struct rtc_class_ops coh901331_ops = { static int __exit coh901331_remove(struct platform_device *pdev) { - struct coh901331_port *rtap = dev_get_drvdata(&pdev->dev); + struct coh901331_port *rtap = platform_get_drvdata(pdev); if (rtap) clk_unprepare(rtap->clk); @@ -264,7 +264,7 @@ static SIMPLE_DEV_PM_OPS(coh901331_pm_ops, coh901331_suspend, coh901331_resume); static void coh901331_shutdown(struct platform_device *pdev) { - struct coh901331_port *rtap = dev_get_drvdata(&pdev->dev); + struct coh901331_port *rtap = platform_get_drvdata(pdev); clk_enable(rtap->clk); writel(0, rtap->virtbase + COH901331_IRQ_MASK); -- cgit v1.2.3 From e1c2f989e691e8e124b57fd7ba4e15e7873b91e5 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:07:13 -0700 Subject: rtc: rtc-rc5t583: use platform_{get,set}_drvdata() Use the wrapper functions for getting and setting the driver data using platform_device instead of using dev_{get,set}_drvdata() with &pdev->dev, so we can directly pass a struct platform_device. Signed-off-by: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-rc5t583.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-rc5t583.c b/drivers/rtc/rtc-rc5t583.c index 8eabcf51b35a..e53e9b1c69b3 100644 --- a/drivers/rtc/rtc-rc5t583.c +++ b/drivers/rtc/rtc-rc5t583.c @@ -273,7 +273,7 @@ static int rc5t583_rtc_probe(struct platform_device *pdev) */ static int rc5t583_rtc_remove(struct platform_device *pdev) { - struct rc5t583_rtc *rc5t583_rtc = dev_get_drvdata(&pdev->dev); + struct rc5t583_rtc *rc5t583_rtc = platform_get_drvdata(pdev); rc5t583_rtc_alarm_irq_enable(&rc5t583_rtc->rtc->dev, 0); return 0; -- cgit v1.2.3 From 52c1b7a74c6d1cebac4402beaa7c59bf5ea4b433 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:07:14 -0700 Subject: drivers/rtc/rtc-bq32k.c: remove empty function After the switch to devm_* functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-bq32k.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-bq32k.c b/drivers/rtc/rtc-bq32k.c index fea78bc713ca..c74bf0dc52cc 100644 --- a/drivers/rtc/rtc-bq32k.c +++ b/drivers/rtc/rtc-bq32k.c @@ -163,11 +163,6 @@ static int bq32k_probe(struct i2c_client *client, return 0; } -static int bq32k_remove(struct i2c_client *client) -{ - return 0; -} - static const struct i2c_device_id bq32k_id[] = { { "bq32000", 0 }, { } @@ -180,7 +175,6 @@ static struct i2c_driver bq32k_driver = { .owner = THIS_MODULE, }, .probe = bq32k_probe, - .remove = bq32k_remove, .id_table = bq32k_id, }; -- cgit v1.2.3 From 0df2c54e8e3e7befb7a484d55d781b4e6ba17d83 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:07:15 -0700 Subject: drivers/rtc/rtc-ds1216.c: remove empty function After the switch to devm_* functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Cc: Thomas Bogendoerfer Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-ds1216.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-ds1216.c b/drivers/rtc/rtc-ds1216.c index c7702b7269f7..1831c8461b90 100644 --- a/drivers/rtc/rtc-ds1216.c +++ b/drivers/rtc/rtc-ds1216.c @@ -167,17 +167,11 @@ static int __init ds1216_rtc_probe(struct platform_device *pdev) return 0; } -static int __exit ds1216_rtc_remove(struct platform_device *pdev) -{ - return 0; -} - static struct platform_driver ds1216_rtc_platform_driver = { .driver = { .name = "rtc-ds1216", .owner = THIS_MODULE, }, - .remove = __exit_p(ds1216_rtc_remove), }; static int __init ds1216_rtc_init(void) -- cgit v1.2.3 From b943abd37ea2209acb0076f2a2fc590a053d15c1 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:07:16 -0700 Subject: drivers/rtc/rtc-ds1286.c: remove empty function After the switch to devm_* functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Cc: Thomas Bogendoerfer Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-ds1286.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-ds1286.c b/drivers/rtc/rtc-ds1286.c index 398c96a98fc4..50e109b78252 100644 --- a/drivers/rtc/rtc-ds1286.c +++ b/drivers/rtc/rtc-ds1286.c @@ -353,18 +353,12 @@ static int ds1286_probe(struct platform_device *pdev) return 0; } -static int ds1286_remove(struct platform_device *pdev) -{ - return 0; -} - static struct platform_driver ds1286_platform_driver = { .driver = { .name = "rtc-ds1286", .owner = THIS_MODULE, }, .probe = ds1286_probe, - .remove = ds1286_remove, }; module_platform_driver(ds1286_platform_driver); -- cgit v1.2.3 From 53ac70aa4dbfa08005768aafeb1420ca101f0642 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:07:17 -0700 Subject: drivers/rtc/rtc-ds1672.c: remove empty function After the switch to devm_* functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-ds1672.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-ds1672.c b/drivers/rtc/rtc-ds1672.c index 3fc2a4738027..18e2d8471472 100644 --- a/drivers/rtc/rtc-ds1672.c +++ b/drivers/rtc/rtc-ds1672.c @@ -153,11 +153,6 @@ static const struct rtc_class_ops ds1672_rtc_ops = { .set_mmss = ds1672_rtc_set_mmss, }; -static int ds1672_remove(struct i2c_client *client) -{ - return 0; -} - static int ds1672_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -210,7 +205,6 @@ static struct i2c_driver ds1672_driver = { .name = "rtc-ds1672", }, .probe = &ds1672_probe, - .remove = &ds1672_remove, .id_table = ds1672_id, }; -- cgit v1.2.3 From 940bca371b53eb842299cd71f55880fec229885e Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:07:17 -0700 Subject: drivers/rtc/rtc-ds3234.c: remove empty function After the switch to devm_* functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Cc: Dennis Aberilla Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-ds3234.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-ds3234.c b/drivers/rtc/rtc-ds3234.c index 67f1a80112e2..4c9ba5368464 100644 --- a/drivers/rtc/rtc-ds3234.c +++ b/drivers/rtc/rtc-ds3234.c @@ -156,18 +156,12 @@ static int ds3234_probe(struct spi_device *spi) return 0; } -static int ds3234_remove(struct spi_device *spi) -{ - return 0; -} - static struct spi_driver ds3234_driver = { .driver = { .name = "ds3234", .owner = THIS_MODULE, }, .probe = ds3234_probe, - .remove = ds3234_remove, }; module_spi_driver(ds3234_driver); -- cgit v1.2.3 From f3828d5dd14af4c1d0a6ae4bf50895eefcf54f64 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:07:18 -0700 Subject: drivers/rtc/rtc-ds1390.c: remove empty function After the switch to devm_* functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Cc: Mark Jackson Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-ds1390.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-ds1390.c b/drivers/rtc/rtc-ds1390.c index 289af419dff4..be9d8c0a7e3a 100644 --- a/drivers/rtc/rtc-ds1390.c +++ b/drivers/rtc/rtc-ds1390.c @@ -154,18 +154,12 @@ static int ds1390_probe(struct spi_device *spi) return res; } -static int ds1390_remove(struct spi_device *spi) -{ - return 0; -} - static struct spi_driver ds1390_driver = { .driver = { .name = "rtc-ds1390", .owner = THIS_MODULE, }, .probe = ds1390_probe, - .remove = ds1390_remove, }; module_spi_driver(ds1390_driver); -- cgit v1.2.3 From e160419a05bc6c0948f646d46b2e91741a03ecf1 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:07:19 -0700 Subject: drivers/rtc/rtc-efi.c: remove empty function After the switch to devm_* functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Cc: dann frazier Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-efi.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c index b3c8c0b1709d..797aa0252ba9 100644 --- a/drivers/rtc/rtc-efi.c +++ b/drivers/rtc/rtc-efi.c @@ -201,17 +201,11 @@ static int __init efi_rtc_probe(struct platform_device *dev) return 0; } -static int __exit efi_rtc_remove(struct platform_device *dev) -{ - return 0; -} - static struct platform_driver efi_rtc_driver = { .driver = { .name = "rtc-efi", .owner = THIS_MODULE, }, - .remove = __exit_p(efi_rtc_remove), }; module_platform_driver_probe(efi_rtc_driver, efi_rtc_probe); -- cgit v1.2.3 From 2a7a145e787a900cca5939c52d2df78c20b7b4de Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:07:20 -0700 Subject: drivers/rtc/rtc-em3027.c: remove empty function After the switch to devm_* functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Cc: Mike Rapoport Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-em3027.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-em3027.c b/drivers/rtc/rtc-em3027.c index 3f9eb57d0486..fccf36699245 100644 --- a/drivers/rtc/rtc-em3027.c +++ b/drivers/rtc/rtc-em3027.c @@ -131,11 +131,6 @@ static int em3027_probe(struct i2c_client *client, return 0; } -static int em3027_remove(struct i2c_client *client) -{ - return 0; -} - static struct i2c_device_id em3027_id[] = { { "em3027", 0 }, { } @@ -146,7 +141,6 @@ static struct i2c_driver em3027_driver = { .name = "rtc-em3027", }, .probe = &em3027_probe, - .remove = &em3027_remove, .id_table = em3027_id, }; -- cgit v1.2.3 From 0814023b1c8b5f1f8ead68e0df161bcdedf83c1f Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:07:21 -0700 Subject: drivers/rtc/rtc-fm3130.c: remove empty function After the switch to devm_* functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Cc: Sergey Lapin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-fm3130.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-fm3130.c b/drivers/rtc/rtc-fm3130.c index 8f053daf0f0c..83c3b3029fa7 100644 --- a/drivers/rtc/rtc-fm3130.c +++ b/drivers/rtc/rtc-fm3130.c @@ -520,18 +520,12 @@ exit_free: return err; } -static int fm3130_remove(struct i2c_client *client) -{ - return 0; -} - static struct i2c_driver fm3130_driver = { .driver = { .name = "rtc-fm3130", .owner = THIS_MODULE, }, .probe = fm3130_probe, - .remove = fm3130_remove, .id_table = fm3130_id, }; -- cgit v1.2.3 From 2e5526fcd8762398fbef1eec3ae59849437f9765 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:07:22 -0700 Subject: drivers/rtc/rtc-isl12022.c: remove empty function After the switch to devm_* functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Cc: Roman Fietze Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-isl12022.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-isl12022.c b/drivers/rtc/rtc-isl12022.c index a1bbbb8de029..6f2de99e2616 100644 --- a/drivers/rtc/rtc-isl12022.c +++ b/drivers/rtc/rtc-isl12022.c @@ -273,11 +273,6 @@ static int isl12022_probe(struct i2c_client *client, return 0; } -static int isl12022_remove(struct i2c_client *client) -{ - return 0; -} - static const struct i2c_device_id isl12022_id[] = { { "isl12022", 0 }, { } @@ -289,7 +284,6 @@ static struct i2c_driver isl12022_driver = { .name = "rtc-isl12022", }, .probe = isl12022_probe, - .remove = isl12022_remove, .id_table = isl12022_id, }; -- cgit v1.2.3 From d95c616352e19e735f6d1578f64f586a87eb2004 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:07:23 -0700 Subject: drivers/rtc/rtc-m41t93.c: remove empty function After the switch to devm_* functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Cc: Nikolaus Voss Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-m41t93.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/rtc/rtc-m41t93.c b/drivers/rtc/rtc-m41t93.c index 9707d36e8b15..4698c7e344e4 100644 --- a/drivers/rtc/rtc-m41t93.c +++ b/drivers/rtc/rtc-m41t93.c @@ -194,19 +194,12 @@ static int m41t93_probe(struct spi_device *spi) return 0; } - -static int m41t93_remove(struct spi_device *spi) -{ - return 0; -} - static struct spi_driver m41t93_driver = { .driver = { .name = "rtc-m41t93", .owner = THIS_MODULE, }, .probe = m41t93_probe, - .remove = m41t93_remove, }; module_spi_driver(m41t93_driver); -- cgit v1.2.3 From 40ac8729ea4cc383d62046620f33b205a28391ba Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:07:24 -0700 Subject: drivers/rtc/rtc-m48t35.c: remove empty function After the switch to devm_* functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Cc: Thomas Bogendoerfer Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-m48t35.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-m48t35.c b/drivers/rtc/rtc-m48t35.c index 37444246e5e4..25678d4ee3b5 100644 --- a/drivers/rtc/rtc-m48t35.c +++ b/drivers/rtc/rtc-m48t35.c @@ -180,18 +180,12 @@ static int m48t35_probe(struct platform_device *pdev) return 0; } -static int m48t35_remove(struct platform_device *pdev) -{ - return 0; -} - static struct platform_driver m48t35_platform_driver = { .driver = { .name = "rtc-m48t35", .owner = THIS_MODULE, }, .probe = m48t35_probe, - .remove = m48t35_remove, }; module_platform_driver(m48t35_platform_driver); -- cgit v1.2.3 From c9e379f5af5b9e2db7f1713fa55bf27db771686a Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:07:25 -0700 Subject: drivers/rtc/rtc-generic.c: remove empty function After the switch to devm_* functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Cc: Kyle McMartin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-generic.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-generic.c b/drivers/rtc/rtc-generic.c index 06279ce6bff2..9b6725ebbfb2 100644 --- a/drivers/rtc/rtc-generic.c +++ b/drivers/rtc/rtc-generic.c @@ -48,17 +48,11 @@ static int __init generic_rtc_probe(struct platform_device *dev) return 0; } -static int __exit generic_rtc_remove(struct platform_device *dev) -{ - return 0; -} - static struct platform_driver generic_rtc_driver = { .driver = { .name = "rtc-generic", .owner = THIS_MODULE, }, - .remove = __exit_p(generic_rtc_remove), }; module_platform_driver_probe(generic_rtc_driver, generic_rtc_probe); -- cgit v1.2.3 From a29310497038410ad17ff5ec257d3c34568b96d4 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:07:26 -0700 Subject: drivers/rtc/rtc-m41t94.c: remove empty function After the switch to devm_* functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Cc: Kim B. Heino Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-m41t94.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-m41t94.c b/drivers/rtc/rtc-m41t94.c index 7454ef0a4cfa..8d800b1bf87b 100644 --- a/drivers/rtc/rtc-m41t94.c +++ b/drivers/rtc/rtc-m41t94.c @@ -134,18 +134,12 @@ static int m41t94_probe(struct spi_device *spi) return 0; } -static int m41t94_remove(struct spi_device *spi) -{ - return 0; -} - static struct spi_driver m41t94_driver = { .driver = { .name = "rtc-m41t94", .owner = THIS_MODULE, }, .probe = m41t94_probe, - .remove = m41t94_remove, }; module_spi_driver(m41t94_driver); -- cgit v1.2.3 From 060f55902057b9a46ee5d78a10f4d1371fca2838 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:07:27 -0700 Subject: drivers/rtc/rtc-max6902.c: remove empty function After the switch to devm_* functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-max6902.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-max6902.c b/drivers/rtc/rtc-max6902.c index e9dd56c8ffe7..ac3f4191864f 100644 --- a/drivers/rtc/rtc-max6902.c +++ b/drivers/rtc/rtc-max6902.c @@ -143,18 +143,12 @@ static int max6902_probe(struct spi_device *spi) return 0; } -static int max6902_remove(struct spi_device *spi) -{ - return 0; -} - static struct spi_driver max6902_driver = { .driver = { .name = "rtc-max6902", .owner = THIS_MODULE, }, .probe = max6902_probe, - .remove = max6902_remove, }; module_spi_driver(max6902_driver); -- cgit v1.2.3 From a1396d9f127952a09a40c8cd3285ff9d062282a0 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:07:28 -0700 Subject: drivers/rtc/rtc-max6900.c: remove empty function After the switch to devm_* functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Cc: Dale Farnsworth Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-max6900.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-max6900.c b/drivers/rtc/rtc-max6900.c index 8669d6d09a00..55969b1b771a 100644 --- a/drivers/rtc/rtc-max6900.c +++ b/drivers/rtc/rtc-max6900.c @@ -212,11 +212,6 @@ static int max6900_rtc_set_time(struct device *dev, struct rtc_time *tm) return max6900_i2c_set_time(to_i2c_client(dev), tm); } -static int max6900_remove(struct i2c_client *client) -{ - return 0; -} - static const struct rtc_class_ops max6900_rtc_ops = { .read_time = max6900_rtc_read_time, .set_time = max6900_rtc_set_time, @@ -252,7 +247,6 @@ static struct i2c_driver max6900_driver = { .name = "rtc-max6900", }, .probe = max6900_probe, - .remove = max6900_remove, .id_table = max6900_id, }; -- cgit v1.2.3 From 17d9e04c62c2980bf36c846541282204b22c1bf2 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:07:29 -0700 Subject: drivers/rtc/rtc-max8907.c: remove empty function After the switch to devm_* functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-max8907.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-max8907.c b/drivers/rtc/rtc-max8907.c index 86afb797125d..8e45b3c4aa2f 100644 --- a/drivers/rtc/rtc-max8907.c +++ b/drivers/rtc/rtc-max8907.c @@ -213,18 +213,12 @@ static int max8907_rtc_probe(struct platform_device *pdev) return ret; } -static int max8907_rtc_remove(struct platform_device *pdev) -{ - return 0; -} - static struct platform_driver max8907_rtc_driver = { .driver = { .name = "max8907-rtc", .owner = THIS_MODULE, }, .probe = max8907_rtc_probe, - .remove = max8907_rtc_remove, }; module_platform_driver(max8907_rtc_driver); -- cgit v1.2.3 From eda328fbe3e1f82a12d56ffae3112086e2f1c578 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:07:30 -0700 Subject: drivers/rtc/rtc-max77686.c: remove empty function After the switch to devm_* functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Cc: Chiwoong Byun Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-max77686.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-max77686.c b/drivers/rtc/rtc-max77686.c index 441c5c2e0bfc..9915cb96014b 100644 --- a/drivers/rtc/rtc-max77686.c +++ b/drivers/rtc/rtc-max77686.c @@ -567,11 +567,6 @@ err_rtc: return ret; } -static int max77686_rtc_remove(struct platform_device *pdev) -{ - return 0; -} - static void max77686_rtc_shutdown(struct platform_device *pdev) { #ifdef MAX77686_RTC_WTSR_SMPL @@ -610,7 +605,6 @@ static struct platform_driver max77686_rtc_driver = { .owner = THIS_MODULE, }, .probe = max77686_rtc_probe, - .remove = max77686_rtc_remove, .shutdown = max77686_rtc_shutdown, .id_table = rtc_id, }; -- cgit v1.2.3 From f7de2a1d6db0c510c1f23363325ee59cf84be548 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:07:31 -0700 Subject: drivers/rtc/rtc-max8997.c: remove empty function After the switch to devm_* functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-max8997.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-max8997.c b/drivers/rtc/rtc-max8997.c index 168390428f86..0777c01b58e0 100644 --- a/drivers/rtc/rtc-max8997.c +++ b/drivers/rtc/rtc-max8997.c @@ -507,11 +507,6 @@ err_out: return ret; } -static int max8997_rtc_remove(struct platform_device *pdev) -{ - return 0; -} - static void max8997_rtc_shutdown(struct platform_device *pdev) { struct max8997_rtc_info *info = platform_get_drvdata(pdev); @@ -531,7 +526,6 @@ static struct platform_driver max8997_rtc_driver = { .owner = THIS_MODULE, }, .probe = max8997_rtc_probe, - .remove = max8997_rtc_remove, .shutdown = max8997_rtc_shutdown, .id_table = rtc_id, }; -- cgit v1.2.3 From af99ef9ccc60c2ba1c43604259fee75eaa932c82 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:07:32 -0700 Subject: drivers/rtc/rtc-pcf8523.c: remove empty function After the switch to devm_* functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Acked-by: Thierry Reding Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-pcf8523.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-pcf8523.c b/drivers/rtc/rtc-pcf8523.c index 305c9515e5bb..5c8f8226c848 100644 --- a/drivers/rtc/rtc-pcf8523.c +++ b/drivers/rtc/rtc-pcf8523.c @@ -317,11 +317,6 @@ static int pcf8523_probe(struct i2c_client *client, return 0; } -static int pcf8523_remove(struct i2c_client *client) -{ - return 0; -} - static const struct i2c_device_id pcf8523_id[] = { { "pcf8523", 0 }, { } @@ -343,7 +338,6 @@ static struct i2c_driver pcf8523_driver = { .of_match_table = of_match_ptr(pcf8523_of_match), }, .probe = pcf8523_probe, - .remove = pcf8523_remove, .id_table = pcf8523_id, }; module_i2c_driver(pcf8523_driver); -- cgit v1.2.3 From b1fb593b782eeab2cf4282f17d58a4939dbef126 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:07:33 -0700 Subject: drivers/rtc/rtc-pcf8563.c: remove empty function After the switch to devm_* functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-pcf8563.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c index 97b354a26a44..9ea2d078ea4c 100644 --- a/drivers/rtc/rtc-pcf8563.c +++ b/drivers/rtc/rtc-pcf8563.c @@ -269,11 +269,6 @@ static int pcf8563_probe(struct i2c_client *client, return 0; } -static int pcf8563_remove(struct i2c_client *client) -{ - return 0; -} - static const struct i2c_device_id pcf8563_id[] = { { "pcf8563", 0 }, { "rtc8564", 0 }, @@ -296,7 +291,6 @@ static struct i2c_driver pcf8563_driver = { .of_match_table = of_match_ptr(pcf8563_of_match), }, .probe = pcf8563_probe, - .remove = pcf8563_remove, .id_table = pcf8563_id, }; -- cgit v1.2.3 From 54aa095ffcc52f9e81bc84159f4af223730d3681 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:07:34 -0700 Subject: drivers/rtc/rtc-pcf8583.c: remove empty function After the switch to devm_* functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-pcf8583.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-pcf8583.c b/drivers/rtc/rtc-pcf8583.c index 9971f7f7cac8..ab740cd63109 100644 --- a/drivers/rtc/rtc-pcf8583.c +++ b/drivers/rtc/rtc-pcf8583.c @@ -290,11 +290,6 @@ static int pcf8583_probe(struct i2c_client *client, return 0; } -static int pcf8583_remove(struct i2c_client *client) -{ - return 0; -} - static const struct i2c_device_id pcf8583_id[] = { { "pcf8583", 0 }, { } @@ -307,7 +302,6 @@ static struct i2c_driver pcf8583_driver = { .owner = THIS_MODULE, }, .probe = pcf8583_probe, - .remove = pcf8583_remove, .id_table = pcf8583_id, }; -- cgit v1.2.3 From 164b8649186c1746750d063c53b71c42646fe337 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:07:35 -0700 Subject: drivers/rtc/rtc-ps3.c: remove empty function After the switch to devm_* functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Acked-by: Geoff Levand Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-ps3.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-ps3.c b/drivers/rtc/rtc-ps3.c index 4bb825bb5804..554ada5e9b76 100644 --- a/drivers/rtc/rtc-ps3.c +++ b/drivers/rtc/rtc-ps3.c @@ -71,17 +71,11 @@ static int __init ps3_rtc_probe(struct platform_device *dev) return 0; } -static int __exit ps3_rtc_remove(struct platform_device *dev) -{ - return 0; -} - static struct platform_driver ps3_rtc_driver = { .driver = { .name = "rtc-ps3", .owner = THIS_MODULE, }, - .remove = __exit_p(ps3_rtc_remove), }; module_platform_driver_probe(ps3_rtc_driver, ps3_rtc_probe); -- cgit v1.2.3 From a87b0f802bbb89c83c1b7c921c3752e646827a22 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:07:36 -0700 Subject: drivers/rtc/rtc-rs5c313.c: remove empty function After the switch to devm_* functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Cc: Nobuhiro Iwamatsu Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-rs5c313.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-rs5c313.c b/drivers/rtc/rtc-rs5c313.c index 6af0f1f95f63..68f7856422f1 100644 --- a/drivers/rtc/rtc-rs5c313.c +++ b/drivers/rtc/rtc-rs5c313.c @@ -378,18 +378,12 @@ static int rs5c313_rtc_probe(struct platform_device *pdev) return 0; } -static int rs5c313_rtc_remove(struct platform_device *pdev) -{ - return 0; -} - static struct platform_driver rs5c313_rtc_platform_driver = { .driver = { .name = DRV_NAME, .owner = THIS_MODULE, }, .probe = rs5c313_rtc_probe, - .remove = rs5c313_rtc_remove, }; static int __init rs5c313_rtc_init(void) -- cgit v1.2.3 From 832ccd3fad7cadf9dbba8e28591be20a74b805a4 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:07:36 -0700 Subject: drivers/rtc/rtc-rv3029c2.c: remove empty function After the switch to devm_* functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Cc: Gregory Hermant Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-rv3029c2.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-rv3029c2.c b/drivers/rtc/rtc-rv3029c2.c index 9100a3401de1..1a779a67ff66 100644 --- a/drivers/rtc/rtc-rv3029c2.c +++ b/drivers/rtc/rtc-rv3029c2.c @@ -412,17 +412,11 @@ static int rv3029c2_probe(struct i2c_client *client, return 0; } -static int rv3029c2_remove(struct i2c_client *client) -{ - return 0; -} - static struct i2c_driver rv3029c2_driver = { .driver = { .name = "rtc-rv3029c2", }, .probe = rv3029c2_probe, - .remove = rv3029c2_remove, .id_table = rv3029c2_id, }; -- cgit v1.2.3 From c281735c52623e46cdef4b7cc74e81d117e944da Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:07:37 -0700 Subject: drivers/rtc/rtc-rx4581.c: remove empty function After the switch to devm_* functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Cc: Torben Hohn Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-rx4581.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-rx4581.c b/drivers/rtc/rtc-rx4581.c index 84eb08d65d30..6889222f9ed6 100644 --- a/drivers/rtc/rtc-rx4581.c +++ b/drivers/rtc/rtc-rx4581.c @@ -282,11 +282,6 @@ static int rx4581_probe(struct spi_device *spi) return 0; } -static int rx4581_remove(struct spi_device *spi) -{ - return 0; -} - static const struct spi_device_id rx4581_id[] = { { "rx4581", 0 }, { } @@ -299,7 +294,6 @@ static struct spi_driver rx4581_driver = { .owner = THIS_MODULE, }, .probe = rx4581_probe, - .remove = rx4581_remove, .id_table = rx4581_id, }; -- cgit v1.2.3 From 75a6cef3c6d2fe7b67e6860baa0a2c9c61058adf Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:07:38 -0700 Subject: drivers/rtc/rtc-rs5c348.c: remove empty function After the switch to devm_* functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Acked-by: Atsushi Nemoto Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-rs5c348.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-rs5c348.c b/drivers/rtc/rtc-rs5c348.c index 2c37df3586c7..f7a90a116a39 100644 --- a/drivers/rtc/rtc-rs5c348.c +++ b/drivers/rtc/rtc-rs5c348.c @@ -218,18 +218,12 @@ static int rs5c348_probe(struct spi_device *spi) return ret; } -static int rs5c348_remove(struct spi_device *spi) -{ - return 0; -} - static struct spi_driver rs5c348_driver = { .driver = { .name = "rtc-rs5c348", .owner = THIS_MODULE, }, .probe = rs5c348_probe, - .remove = rs5c348_remove, }; module_spi_driver(rs5c348_driver); -- cgit v1.2.3 From 753190928dd7e08f529f2a16a24fdb58080a481b Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:07:39 -0700 Subject: drivers/rtc/rtc-rx8581.c: remove empty function After the switch to devm_* functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Cc: Martyn Welch Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-rx8581.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-rx8581.c b/drivers/rtc/rtc-rx8581.c index 07f3037b18f4..00b0eb7fe166 100644 --- a/drivers/rtc/rtc-rx8581.c +++ b/drivers/rtc/rtc-rx8581.c @@ -251,11 +251,6 @@ static int rx8581_probe(struct i2c_client *client, return 0; } -static int rx8581_remove(struct i2c_client *client) -{ - return 0; -} - static const struct i2c_device_id rx8581_id[] = { { "rx8581", 0 }, { } @@ -268,7 +263,6 @@ static struct i2c_driver rx8581_driver = { .owner = THIS_MODULE, }, .probe = rx8581_probe, - .remove = rx8581_remove, .id_table = rx8581_id, }; -- cgit v1.2.3 From 0cb4b84fd3806d1cdad3f37ee606fd9f8d506264 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:07:40 -0700 Subject: drivers/rtc/rtc-snvs.c: remove empty function After the switch to devm_* functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-snvs.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-snvs.c b/drivers/rtc/rtc-snvs.c index b04f09a1df2a..316a342115b2 100644 --- a/drivers/rtc/rtc-snvs.c +++ b/drivers/rtc/rtc-snvs.c @@ -294,11 +294,6 @@ static int snvs_rtc_probe(struct platform_device *pdev) return 0; } -static int snvs_rtc_remove(struct platform_device *pdev) -{ - return 0; -} - #ifdef CONFIG_PM_SLEEP static int snvs_rtc_suspend(struct device *dev) { @@ -337,7 +332,6 @@ static struct platform_driver snvs_rtc_driver = { .of_match_table = of_match_ptr(snvs_dt_ids), }, .probe = snvs_rtc_probe, - .remove = snvs_rtc_remove, }; module_platform_driver(snvs_rtc_driver); -- cgit v1.2.3 From 15b005b7ef5fd56ca60bf734a480d87592bccd27 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:07:41 -0700 Subject: drivers/rtc/rtc-starfire.c: remove empty function After the switch to devm_* functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-starfire.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-starfire.c b/drivers/rtc/rtc-starfire.c index 987b5ec0ae56..f7d8a6db8078 100644 --- a/drivers/rtc/rtc-starfire.c +++ b/drivers/rtc/rtc-starfire.c @@ -51,17 +51,11 @@ static int __init starfire_rtc_probe(struct platform_device *pdev) return 0; } -static int __exit starfire_rtc_remove(struct platform_device *pdev) -{ - return 0; -} - static struct platform_driver starfire_rtc_driver = { .driver = { .name = "rtc-starfire", .owner = THIS_MODULE, }, - .remove = __exit_p(starfire_rtc_remove), }; module_platform_driver_probe(starfire_rtc_driver, starfire_rtc_probe); -- cgit v1.2.3 From d55b6643e54bdb0822c0db5c0b4dfc4d89c6b23d Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:07:42 -0700 Subject: drivers/rtc/rtc-sun4v.c: remove empty function After the switch to devm_* functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-sun4v.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-sun4v.c b/drivers/rtc/rtc-sun4v.c index ce42e5fa9e09..bc97ff91341d 100644 --- a/drivers/rtc/rtc-sun4v.c +++ b/drivers/rtc/rtc-sun4v.c @@ -92,17 +92,11 @@ static int __init sun4v_rtc_probe(struct platform_device *pdev) return 0; } -static int __exit sun4v_rtc_remove(struct platform_device *pdev) -{ - return 0; -} - static struct platform_driver sun4v_rtc_driver = { .driver = { .name = "rtc-sun4v", .owner = THIS_MODULE, }, - .remove = __exit_p(sun4v_rtc_remove), }; module_platform_driver_probe(sun4v_rtc_driver, sun4v_rtc_probe); -- cgit v1.2.3 From 7deef7f2983209b3bb34cbda91e1080f3e43d74a Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:07:43 -0700 Subject: drivers/rtc/rtc-tps80031.c: remove empty function After the switch to devm_* functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Cc: Laxman Dewangan Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-tps80031.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-tps80031.c b/drivers/rtc/rtc-tps80031.c index 72662eafb938..3e400dce2d06 100644 --- a/drivers/rtc/rtc-tps80031.c +++ b/drivers/rtc/rtc-tps80031.c @@ -298,11 +298,6 @@ static int tps80031_rtc_probe(struct platform_device *pdev) return 0; } -static int tps80031_rtc_remove(struct platform_device *pdev) -{ - return 0; -} - #ifdef CONFIG_PM_SLEEP static int tps80031_rtc_suspend(struct device *dev) { @@ -333,7 +328,6 @@ static struct platform_driver tps80031_rtc_driver = { .pm = &tps80031_pm_ops, }, .probe = tps80031_rtc_probe, - .remove = tps80031_rtc_remove, }; module_platform_driver(tps80031_rtc_driver); -- cgit v1.2.3 From 65ee88c9c68ad533463d1030d2abd859d7a4d9f9 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:07:44 -0700 Subject: drivers/rtc/rtc-wm831x.c: remove empty function After the switch to devm_* functions and the removal of rtc_device_unregister(), the 'remove' function does not do anything. Delete it. Signed-off-by: Sachin Kamat Acked-by: Mark Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-wm831x.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/rtc/rtc-wm831x.c b/drivers/rtc/rtc-wm831x.c index 8d65b94e5a7e..75aea4c4d334 100644 --- a/drivers/rtc/rtc-wm831x.c +++ b/drivers/rtc/rtc-wm831x.c @@ -460,11 +460,6 @@ err: return ret; } -static int wm831x_rtc_remove(struct platform_device *pdev) -{ - return 0; -} - static const struct dev_pm_ops wm831x_rtc_pm_ops = { .suspend = wm831x_rtc_suspend, .resume = wm831x_rtc_resume, @@ -478,7 +473,6 @@ static const struct dev_pm_ops wm831x_rtc_pm_ops = { static struct platform_driver wm831x_rtc_driver = { .probe = wm831x_rtc_probe, - .remove = wm831x_rtc_remove, .driver = { .name = "wm831x-rtc", .pm = &wm831x_rtc_pm_ops, -- cgit v1.2.3 From 25d053cf1040e6430fff679854b3710edb0b7fee Mon Sep 17 00:00:00 2001 From: Alexandre Torgue Date: Wed, 3 Jul 2013 15:07:45 -0700 Subject: drivers/rtc/rtc-ab8500.c: add second resolution to rtc driver Android expects the RTC to have second resolution. On ab8540 cut2 RTC block has a new register which allows setting seconds for wakeup alarms. Existing registers (minutes hi, mid and low) have seen their offsets changed. Here is the new mapping: * AlarmSec (A) 0x22 * AlarmMinLow (M) from 0x8 to 0x23 * AlarmMinMid (M) from 0x9 to 0x24 * AlarmMinHigh (M) from 0xA to 0x25 Signed-off-by: Julien Delacou Signed-off-by: Alexandre Torgue Acked-by: Lee Jones Acked-by: Linus Walleij Cc: Samuel Ortiz Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-ab8500.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-ab8500.c b/drivers/rtc/rtc-ab8500.c index c5b62d4389ee..727e2f5d14d9 100644 --- a/drivers/rtc/rtc-ab8500.c +++ b/drivers/rtc/rtc-ab8500.c @@ -35,6 +35,10 @@ #define AB8500_RTC_FORCE_BKUP_REG 0x0D #define AB8500_RTC_CALIB_REG 0x0E #define AB8500_RTC_SWITCH_STAT_REG 0x0F +#define AB8540_RTC_ALRM_SEC 0x22 +#define AB8540_RTC_ALRM_MIN_LOW_REG 0x23 +#define AB8540_RTC_ALRM_MIN_MID_REG 0x24 +#define AB8540_RTC_ALRM_MIN_HI_REG 0x25 /* RtcReadRequest bits */ #define RTC_READ_REQUEST 0x01 @@ -58,6 +62,11 @@ static const u8 ab8500_rtc_alarm_regs[] = { AB8500_RTC_ALRM_MIN_LOW_REG }; +static const u8 ab8540_rtc_alarm_regs[] = { + AB8540_RTC_ALRM_MIN_HI_REG, AB8540_RTC_ALRM_MIN_MID_REG, + AB8540_RTC_ALRM_MIN_LOW_REG, AB8540_RTC_ALRM_SEC +}; + /* Calculate the seconds from 1970 to 01-01-2000 00:00:00 */ static unsigned long get_elapsed_seconds(int year) { @@ -267,6 +276,42 @@ static int ab8500_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) return ab8500_rtc_irq_enable(dev, alarm->enabled); } +static int ab8540_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + int retval, i; + unsigned char buf[ARRAY_SIZE(ab8540_rtc_alarm_regs)]; + unsigned long mins, secs = 0; + + if (alarm->time.tm_year < (AB8500_RTC_EPOCH - 1900)) { + dev_dbg(dev, "year should be equal to or greater than %d\n", + AB8500_RTC_EPOCH); + return -EINVAL; + } + + /* Get the number of seconds since 1970 */ + rtc_tm_to_time(&alarm->time, &secs); + + /* + * Convert it to the number of seconds since 01-01-2000 00:00:00 + */ + secs -= get_elapsed_seconds(AB8500_RTC_EPOCH); + mins = secs / 60; + + buf[3] = secs % 60; + buf[2] = mins & 0xFF; + buf[1] = (mins >> 8) & 0xFF; + buf[0] = (mins >> 16) & 0xFF; + + /* Set the alarm time */ + for (i = 0; i < ARRAY_SIZE(ab8540_rtc_alarm_regs); i++) { + retval = abx500_set_register_interruptible(dev, AB8500_RTC, + ab8540_rtc_alarm_regs[i], buf[i]); + if (retval < 0) + return retval; + } + + return ab8500_rtc_irq_enable(dev, alarm->enabled); +} static int ab8500_rtc_set_calibration(struct device *dev, int calibration) { @@ -389,8 +434,22 @@ static const struct rtc_class_ops ab8500_rtc_ops = { .alarm_irq_enable = ab8500_rtc_irq_enable, }; +static const struct rtc_class_ops ab8540_rtc_ops = { + .read_time = ab8500_rtc_read_time, + .set_time = ab8500_rtc_set_time, + .read_alarm = ab8500_rtc_read_alarm, + .set_alarm = ab8540_rtc_set_alarm, + .alarm_irq_enable = ab8500_rtc_irq_enable, +}; + +static struct platform_device_id ab85xx_rtc_ids[] = { + { "ab8500-rtc", (kernel_ulong_t)&ab8500_rtc_ops, }, + { "ab8540-rtc", (kernel_ulong_t)&ab8540_rtc_ops, }, +}; + static int ab8500_rtc_probe(struct platform_device *pdev) { + const struct platform_device_id *platid = platform_get_device_id(pdev); int err; struct rtc_device *rtc; u8 rtc_ctrl; @@ -423,7 +482,8 @@ static int ab8500_rtc_probe(struct platform_device *pdev) device_init_wakeup(&pdev->dev, true); rtc = devm_rtc_device_register(&pdev->dev, "ab8500-rtc", - &ab8500_rtc_ops, THIS_MODULE); + (struct rtc_class_ops *)platid->driver_data, + THIS_MODULE); if (IS_ERR(rtc)) { dev_err(&pdev->dev, "Registration failed\n"); err = PTR_ERR(rtc); @@ -461,6 +521,7 @@ static struct platform_driver ab8500_rtc_driver = { }, .probe = ab8500_rtc_probe, .remove = ab8500_rtc_remove, + .id_table = ab85xx_rtc_ids, }; module_platform_driver(ab8500_rtc_driver); -- cgit v1.2.3 From dfc657b1330990213a3865aaef9d8af563ccad51 Mon Sep 17 00:00:00 2001 From: Sergey Yanovich Date: Wed, 3 Jul 2013 15:07:46 -0700 Subject: drivers/rtc/rtc-ds1302.c: handle write protection This chip has a control register and can prevent altering saved clock. Without this patch we could have: (arm)root@pac14:~# date Tue May 21 03:08:27 MSK 2013 (arm)root@pac14:~# /etc/init.d/hwclock.sh show Tue May 21 11:13:58 2013 -0.067322 seconds (arm)root@pac14:~# /etc/init.d/hwclock.sh stop [info] Saving the system clock. [info] Hardware Clock updated to Tue May 21 03:09:01 MSK 2013. (arm)root@pac14:~# /etc/init.d/hwclock.sh show Tue May 21 11:14:15 2013 -0.624272 seconds The patch enables write access to rtc before the driver tries to write time and re-disables when time data is written. Signed-off-by: Sergey Yanovich Acked-by: Marc Zyngier Cc: Alessandro Zummo Cc: Sachin Kamat Cc: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-ds1302.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/rtc/rtc-ds1302.c b/drivers/rtc/rtc-ds1302.c index 7533b723082d..07e8d79b4a09 100644 --- a/drivers/rtc/rtc-ds1302.c +++ b/drivers/rtc/rtc-ds1302.c @@ -23,8 +23,12 @@ #define RTC_CMD_READ 0x81 /* Read command */ #define RTC_CMD_WRITE 0x80 /* Write command */ +#define RTC_CMD_WRITE_ENABLE 0x00 /* Write enable */ +#define RTC_CMD_WRITE_DISABLE 0x80 /* Write disable */ + #define RTC_ADDR_RAM0 0x20 /* Address of RAM0 */ #define RTC_ADDR_TCR 0x08 /* Address of trickle charge register */ +#define RTC_ADDR_CTRL 0x07 /* Address of control register */ #define RTC_ADDR_YEAR 0x06 /* Address of year register */ #define RTC_ADDR_DAY 0x05 /* Address of day of week register */ #define RTC_ADDR_MON 0x04 /* Address of month register */ @@ -161,6 +165,7 @@ static int ds1302_rtc_read_time(struct device *dev, struct rtc_time *tm) static int ds1302_rtc_set_time(struct device *dev, struct rtc_time *tm) { + ds1302_writebyte(RTC_ADDR_CTRL, RTC_CMD_WRITE_ENABLE); /* Stop RTC */ ds1302_writebyte(RTC_ADDR_SEC, ds1302_readbyte(RTC_ADDR_SEC) | 0x80); @@ -175,6 +180,8 @@ static int ds1302_rtc_set_time(struct device *dev, struct rtc_time *tm) /* Start RTC */ ds1302_writebyte(RTC_ADDR_SEC, ds1302_readbyte(RTC_ADDR_SEC) & ~0x80); + ds1302_writebyte(RTC_ADDR_CTRL, RTC_CMD_WRITE_DISABLE); + return 0; } -- cgit v1.2.3 From ad87a766479d027f5966c781605bc9f7bf8bab67 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:07:47 -0700 Subject: drivers/rtc/rtc-mpc5121.c: use platform_{get,set}_drvdata() Use the wrapper functions for getting and setting the driver data using platform_device instead of using dev_{get,set}_drvdata() with &pdev->dev, so we can directly pass a struct platform_device. Signed-off-by: Jingoo Han Cc: Grant Likely Cc: Rob Herring Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-mpc5121.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/rtc/rtc-mpc5121.c b/drivers/rtc/rtc-mpc5121.c index 4c024974c69f..9c8f60903799 100644 --- a/drivers/rtc/rtc-mpc5121.c +++ b/drivers/rtc/rtc-mpc5121.c @@ -324,7 +324,7 @@ static int mpc5121_rtc_probe(struct platform_device *op) device_init_wakeup(&op->dev, 1); - dev_set_drvdata(&op->dev, rtc); + platform_set_drvdata(op, rtc); rtc->irq = irq_of_parse_and_map(op->dev.of_node, 1); err = request_irq(rtc->irq, mpc5121_rtc_handler, 0, @@ -382,7 +382,7 @@ out_dispose: static int mpc5121_rtc_remove(struct platform_device *op) { - struct mpc5121_rtc_data *rtc = dev_get_drvdata(&op->dev); + struct mpc5121_rtc_data *rtc = platform_get_drvdata(op); struct mpc5121_rtc_regs __iomem *regs = rtc->regs; /* disable interrupt, so there are no nasty surprises */ -- cgit v1.2.3 From 11f54d05865c009a468e9d7adaa7414749daa6ca Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:07:48 -0700 Subject: drivers/rtc/rtc-da9052.c: use PTR_RET() Use of PTR_RET() simplifies the code. Signed-off-by: Sachin Kamat Cc: David Dajun Chen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-da9052.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/rtc/rtc-da9052.c b/drivers/rtc/rtc-da9052.c index 2f7fdd77a6f0..9c8c19441cc6 100644 --- a/drivers/rtc/rtc-da9052.c +++ b/drivers/rtc/rtc-da9052.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -249,10 +250,7 @@ static int da9052_rtc_probe(struct platform_device *pdev) rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, &da9052_rtc_ops, THIS_MODULE); - if (IS_ERR(rtc->rtc)) - return PTR_ERR(rtc->rtc); - - return 0; + return PTR_RET(rtc->rtc); } static struct platform_driver da9052_rtc_driver = { -- cgit v1.2.3 From 17cc54b20e4a06c081dec6abc9317a69605db541 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:07:49 -0700 Subject: drivers/rtc/rtc-isl12022.c: use PTR_RET() Use of PTR_RET() simplifies the code. Signed-off-by: Sachin Kamat Cc: Roman Fietze Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-isl12022.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/rtc/rtc-isl12022.c b/drivers/rtc/rtc-isl12022.c index 6f2de99e2616..5dbdc4405718 100644 --- a/drivers/rtc/rtc-isl12022.c +++ b/drivers/rtc/rtc-isl12022.c @@ -16,6 +16,7 @@ #include #include #include +#include #define DRV_VERSION "0.1" @@ -267,10 +268,7 @@ static int isl12022_probe(struct i2c_client *client, isl12022->rtc = devm_rtc_device_register(&client->dev, isl12022_driver.driver.name, &isl12022_rtc_ops, THIS_MODULE); - if (IS_ERR(isl12022->rtc)) - return PTR_ERR(isl12022->rtc); - - return 0; + return PTR_RET(isl12022->rtc); } static const struct i2c_device_id isl12022_id[] = { -- cgit v1.2.3 From 8ff7b7d90ed3f2181ec8342758670891bba2f7bd Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:07:50 -0700 Subject: drivers/rtc/rtc-m48t35.c: use PTR_RET() Use of PTR_RET() simplifies the code. Signed-off-by: Sachin Kamat Cc: Thomas Bogendoerfer Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-m48t35.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/rtc/rtc-m48t35.c b/drivers/rtc/rtc-m48t35.c index 25678d4ee3b5..23c3779a5f2b 100644 --- a/drivers/rtc/rtc-m48t35.c +++ b/drivers/rtc/rtc-m48t35.c @@ -20,6 +20,7 @@ #include #include #include +#include #define DRV_VERSION "1.0" @@ -174,10 +175,7 @@ static int m48t35_probe(struct platform_device *pdev) priv->rtc = devm_rtc_device_register(&pdev->dev, "m48t35", &m48t35_ops, THIS_MODULE); - if (IS_ERR(priv->rtc)) - return PTR_ERR(priv->rtc); - - return 0; + return PTR_RET(priv->rtc); } static struct platform_driver m48t35_platform_driver = { -- cgit v1.2.3 From 23f809ee33f06877de4c4f28129c495d332cd500 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:07:51 -0700 Subject: drivers/rtc/rtc-pcf8563.c: use PTR_RET() Use of PTR_RET() simplifies the code. Signed-off-by: Sachin Kamat Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-pcf8563.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c index 9ea2d078ea4c..710c3a5aa6ff 100644 --- a/drivers/rtc/rtc-pcf8563.c +++ b/drivers/rtc/rtc-pcf8563.c @@ -20,6 +20,7 @@ #include #include #include +#include #define DRV_VERSION "0.4.3" @@ -263,10 +264,7 @@ static int pcf8563_probe(struct i2c_client *client, pcf8563_driver.driver.name, &pcf8563_rtc_ops, THIS_MODULE); - if (IS_ERR(pcf8563->rtc)) - return PTR_ERR(pcf8563->rtc); - - return 0; + return PTR_RET(pcf8563->rtc); } static const struct i2c_device_id pcf8563_id[] = { -- cgit v1.2.3 From ee605e0b2e12040dc1fe88eb8ab03bde0b8f92b8 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 3 Jul 2013 15:07:52 -0700 Subject: drivers/rtc/rtc-pcf8583.c: use PTR_RET() Use of PTR_RET() simplifies the code. Signed-off-by: Sachin Kamat Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-pcf8583.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/rtc/rtc-pcf8583.c b/drivers/rtc/rtc-pcf8583.c index ab740cd63109..843a745c42f3 100644 --- a/drivers/rtc/rtc-pcf8583.c +++ b/drivers/rtc/rtc-pcf8583.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -284,10 +285,7 @@ static int pcf8583_probe(struct i2c_client *client, pcf8583_driver.driver.name, &pcf8583_rtc_ops, THIS_MODULE); - if (IS_ERR(pcf8583->rtc)) - return PTR_ERR(pcf8583->rtc); - - return 0; + return PTR_RET(pcf8583->rtc); } static const struct i2c_device_id pcf8583_id[] = { -- cgit v1.2.3 From ae8458949a57346e17a07768fbdb626cbe993b89 Mon Sep 17 00:00:00 2001 From: Kevin Hilman Date: Wed, 3 Jul 2013 15:07:53 -0700 Subject: drivers/rtc/rtc-twl.c: ensure IRQ is wakeup enabled Currently, the RTC IRQ is never wakeup-enabled so is not capable of bringing the system out of suspend. On OMAP platforms, we have gotten by without this because the TWL RTC is on an I2C-connected chip which is capable of waking up the OMAP via the IO ring when the OMAP is in low-power states. However, if the OMAP suspends without hitting the low-power states (and the IO ring is not enabled), RTC wakeups will not work because the IRQ is not wakeup enabled. To fix, ensure the RTC IRQ is wakeup enabled whenever the RTC alarm is set. Signed-off-by: Kevin Hilman Cc: Alessandro Zummo Cc: Tony Lindgren Cc: Grygorii Strashko Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-twl.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/rtc/rtc-twl.c b/drivers/rtc/rtc-twl.c index 52843d0132be..3614874f7e3f 100644 --- a/drivers/rtc/rtc-twl.c +++ b/drivers/rtc/rtc-twl.c @@ -213,12 +213,24 @@ static int mask_rtc_irq_bit(unsigned char bit) static int twl_rtc_alarm_irq_enable(struct device *dev, unsigned enabled) { + struct platform_device *pdev = to_platform_device(dev); + int irq = platform_get_irq(pdev, 0); + static bool twl_rtc_wake_enabled; int ret; - if (enabled) + if (enabled) { ret = set_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_ALARM_M); - else + if (device_can_wakeup(dev) && !twl_rtc_wake_enabled) { + enable_irq_wake(irq); + twl_rtc_wake_enabled = true; + } + } else { ret = mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_ALARM_M); + if (twl_rtc_wake_enabled) { + disable_irq_wake(irq); + twl_rtc_wake_enabled = false; + } + } return ret; } -- cgit v1.2.3 From 998a06051afe6cba392eab66fa0ef1d7e7376f6c Mon Sep 17 00:00:00 2001 From: Derek Basehore Date: Wed, 3 Jul 2013 15:07:54 -0700 Subject: drivers/rtc/rtc-cmos.c: work around bios clearing rtc control The bios may clear the rtc control register when resuming the system. Since the cmos interrupt handler may now be run before the rtc_cmos is resumed, this can cause the interrupt handler to ignore an alarm since the alarm bit is not set in the rtc control register. To work around this, check if the rtc_cmos is suspended and use the stored value for the rtc control register. Signed-off-by: Derek Basehore Reviewed-by: Sameer Nanda Cc: Alessandro Zummo Cc: Jingoo Han Cc: Steven Rostedt Cc: John Stultz Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-cmos.c | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index a6727d977330..be06d7150de5 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -556,17 +556,24 @@ static irqreturn_t cmos_interrupt(int irq, void *p) rtc_control = CMOS_READ(RTC_CONTROL); if (is_hpet_enabled()) irqstat = (unsigned long)irq & 0xF0; - irqstat &= (rtc_control & RTC_IRQMASK) | RTC_IRQF; + + /* If we were suspended, RTC_CONTROL may not be accurate since the + * bios may have cleared it. + */ + if (!cmos_rtc.suspend_ctrl) + irqstat &= (rtc_control & RTC_IRQMASK) | RTC_IRQF; + else + irqstat &= (cmos_rtc.suspend_ctrl & RTC_IRQMASK) | RTC_IRQF; /* All Linux RTC alarms should be treated as if they were oneshot. * Similar code may be needed in system wakeup paths, in case the * alarm woke the system. */ if (irqstat & RTC_AIE) { + cmos_rtc.suspend_ctrl &= ~RTC_AIE; rtc_control &= ~RTC_AIE; CMOS_WRITE(rtc_control, RTC_CONTROL); hpet_mask_rtc_irq_bit(RTC_AIE); - CMOS_READ(RTC_INTR_FLAGS); } spin_unlock(&rtc_lock); @@ -839,21 +846,23 @@ static inline int cmos_poweroff(struct device *dev) static int cmos_resume(struct device *dev) { struct cmos_rtc *cmos = dev_get_drvdata(dev); - unsigned char tmp = cmos->suspend_ctrl; + unsigned char tmp; + + if (cmos->enabled_wake) { + if (cmos->wake_off) + cmos->wake_off(dev); + else + disable_irq_wake(cmos->irq); + cmos->enabled_wake = 0; + } + spin_lock_irq(&rtc_lock); + tmp = cmos->suspend_ctrl; + cmos->suspend_ctrl = 0; /* re-enable any irqs previously active */ if (tmp & RTC_IRQMASK) { unsigned char mask; - if (cmos->enabled_wake) { - if (cmos->wake_off) - cmos->wake_off(dev); - else - disable_irq_wake(cmos->irq); - cmos->enabled_wake = 0; - } - - spin_lock_irq(&rtc_lock); if (device_may_wakeup(dev)) hpet_rtc_timer_init(); @@ -873,8 +882,8 @@ static int cmos_resume(struct device *dev) tmp &= ~RTC_AIE; hpet_mask_rtc_irq_bit(RTC_AIE); } while (mask & RTC_AIE); - spin_unlock_irq(&rtc_lock); } + spin_unlock_irq(&rtc_lock); dev_dbg(dev, "resume, ctrl %02x\n", tmp); -- cgit v1.2.3 From d3869ff684cb96ccfaf3c3a9a302fe3b7e976b02 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 3 Jul 2013 15:07:55 -0700 Subject: drivers/rtc/rtc-twl.c: fix rtc_reg_map initialization Initialize the rtc_reg_map in platform_driver's probe function instead at module_init time. This way we can make sure that the twl-core has been already probed and initialized (twl_priv->twl_id is valid) since the platform device for the RTC driver will be created by the twl-core after it finished its init. Reported-by: Christoph Fritz Signed-off-by: Peter Ujfalusi Tested-by: Kevin Hilman Tested-by: Grygorii Strashko Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-twl.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/rtc/rtc-twl.c b/drivers/rtc/rtc-twl.c index 3614874f7e3f..8a04044a9c1c 100644 --- a/drivers/rtc/rtc-twl.c +++ b/drivers/rtc/rtc-twl.c @@ -481,6 +481,12 @@ static int twl_rtc_probe(struct platform_device *pdev) if (irq <= 0) goto out1; + /* Initialize the register map */ + if (twl_class_is_4030()) + rtc_reg_map = (u8 *)twl4030_rtc_reg_map; + else + rtc_reg_map = (u8 *)twl6030_rtc_reg_map; + ret = twl_rtc_read_u8(&rd_reg, REG_RTC_STATUS_REG); if (ret < 0) goto out1; @@ -622,11 +628,6 @@ static struct platform_driver twl4030rtc_driver = { static int __init twl_rtc_init(void) { - if (twl_class_is_4030()) - rtc_reg_map = (u8 *) twl4030_rtc_reg_map; - else - rtc_reg_map = (u8 *) twl6030_rtc_reg_map; - return platform_driver_register(&twl4030rtc_driver); } module_init(twl_rtc_init); -- cgit v1.2.3 From 5ee67484dede220d96c0b323c78d02c63fdfae44 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 3 Jul 2013 15:07:56 -0700 Subject: drivers/rtc/rtc-twl.c: cleanup with module_platform_driver() conversion Use module_platform_driver() to register the platform driver. Signed-off-by: Peter Ujfalusi Acked-by: Kevin Hilman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-twl.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/rtc/rtc-twl.c b/drivers/rtc/rtc-twl.c index 8a04044a9c1c..02faf3c4e0d5 100644 --- a/drivers/rtc/rtc-twl.c +++ b/drivers/rtc/rtc-twl.c @@ -626,17 +626,7 @@ static struct platform_driver twl4030rtc_driver = { }, }; -static int __init twl_rtc_init(void) -{ - return platform_driver_register(&twl4030rtc_driver); -} -module_init(twl_rtc_init); - -static void __exit twl_rtc_exit(void) -{ - platform_driver_unregister(&twl4030rtc_driver); -} -module_exit(twl_rtc_exit); +module_platform_driver(twl4030rtc_driver); MODULE_AUTHOR("Texas Instruments, MontaVista Software"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 0734e27f0befe9e88c2b5dad789b05b7bf86ce90 Mon Sep 17 00:00:00 2001 From: Chris Brand Date: Wed, 3 Jul 2013 15:07:57 -0700 Subject: drivers/rtc/interface.c: return -EBUSY, not -EACCES when device is busy If rtc->irq_task is non-NULL and task is NULL, they always rtc_irq_set_freq(), whenever err is set to -EBUSY it will then immediately be set to -EACCES, misleading the caller as to the underlying problem. Signed-off-by: Chris Brand Acked-by: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/interface.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 14c1efdd0757..72c5cdbe0791 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -698,9 +698,9 @@ retry: spin_lock_irqsave(&rtc->irq_task_lock, flags); if (rtc->irq_task != NULL && task == NULL) err = -EBUSY; - if (rtc->irq_task != task) + else if (rtc->irq_task != task) err = -EACCES; - if (!err) { + else { if (rtc_update_hrtimer(rtc, enabled) < 0) { spin_unlock_irqrestore(&rtc->irq_task_lock, flags); cpu_relax(); @@ -734,9 +734,9 @@ retry: spin_lock_irqsave(&rtc->irq_task_lock, flags); if (rtc->irq_task != NULL && task == NULL) err = -EBUSY; - if (rtc->irq_task != task) + else if (rtc->irq_task != task) err = -EACCES; - if (!err) { + else { rtc->irq_freq = freq; if (rtc->pie_enabled && rtc_update_hrtimer(rtc, 1) < 0) { spin_unlock_irqrestore(&rtc->irq_task_lock, flags); -- cgit v1.2.3 From 4c5591c1eee54b7775ea63635ae310a29f0207bb Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 3 Jul 2013 15:07:58 -0700 Subject: drivers/rtc/rtc-pcf2123.c: replace strict_strtoul() with kstrtoul() The usage of strict_strtoul() is not preferred, because strict_strtoul() is obsolete. Thus, kstrtoul() should be used. Signed-off-by: Jingoo Han Reviewed-by: Andy Shevchenko Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-pcf2123.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/rtc/rtc-pcf2123.c b/drivers/rtc/rtc-pcf2123.c index b2a78a02cf16..1725b5090e33 100644 --- a/drivers/rtc/rtc-pcf2123.c +++ b/drivers/rtc/rtc-pcf2123.c @@ -94,8 +94,9 @@ static ssize_t pcf2123_show(struct device *dev, struct device_attribute *attr, r = container_of(attr, struct pcf2123_sysfs_reg, attr); - if (strict_strtoul(r->name, 16, ®)) - return -EINVAL; + ret = kstrtoul(r->name, 16, ®); + if (ret) + return ret; txbuf[0] = PCF2123_READ | reg; ret = spi_write_then_read(spi, txbuf, 1, rxbuf, 1); @@ -117,9 +118,13 @@ static ssize_t pcf2123_store(struct device *dev, struct device_attribute *attr, r = container_of(attr, struct pcf2123_sysfs_reg, attr); - if (strict_strtoul(r->name, 16, ®) - || strict_strtoul(buffer, 10, &val)) - return -EINVAL; + ret = kstrtoul(r->name, 16, ®); + if (ret) + return ret; + + ret = kstrtoul(buffer, 10, &val); + if (ret) + return ret; txbuf[0] = PCF2123_WRITE | reg; txbuf[1] = val; -- cgit v1.2.3 From 92e7f04a7fcc9e7f5955f3337e26ca4ac2ae387b Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Wed, 3 Jul 2013 15:07:59 -0700 Subject: drivers/rtc/class: convert from Legacy pm ops to dev_pm_ops Convert drivers/rtc/class to use dev_pm_ops for power management and remove Legacy PM ops hooks. With this change, rtc class registers suspend/resume callbacks via class->pm (dev_pm_ops) instead of Legacy class->suspend/resume. When __device_suspend() runs call-backs, it will find class->pm ops for the rtc class. Signed-off-by: Shuah Khan Signed-off-by: Jingoo Han Cc: Shuah Khan Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/class.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index 66385402d20e..02426812bebc 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -38,7 +38,7 @@ static void rtc_device_release(struct device *dev) int rtc_hctosys_ret = -ENODEV; #endif -#if defined(CONFIG_PM) && defined(CONFIG_RTC_HCTOSYS_DEVICE) +#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_RTC_HCTOSYS_DEVICE) /* * On suspend(), measure the delta between one RTC and the * system's wall clock; restore it on resume(). @@ -47,7 +47,7 @@ int rtc_hctosys_ret = -ENODEV; static struct timespec old_rtc, old_system, old_delta; -static int rtc_suspend(struct device *dev, pm_message_t mesg) +static int rtc_suspend(struct device *dev) { struct rtc_device *rtc = to_rtc_device(dev); struct rtc_time tm; @@ -135,9 +135,10 @@ static int rtc_resume(struct device *dev) return 0; } +static SIMPLE_DEV_PM_OPS(rtc_class_dev_pm_ops, rtc_suspend, rtc_resume); +#define RTC_CLASS_DEV_PM_OPS (&rtc_class_dev_pm_ops) #else -#define rtc_suspend NULL -#define rtc_resume NULL +#define RTC_CLASS_DEV_PM_OPS NULL #endif @@ -336,8 +337,7 @@ static int __init rtc_init(void) pr_err("couldn't create class\n"); return PTR_ERR(rtc_class); } - rtc_class->suspend = rtc_suspend; - rtc_class->resume = rtc_resume; + rtc_class->pm = RTC_CLASS_DEV_PM_OPS; rtc_dev_init(); rtc_sysfs_init(rtc_class); return 0; -- cgit v1.2.3 From 2c5a5b30917b725ea95fa189e52fc8412f6a2814 Mon Sep 17 00:00:00 2001 From: Wei Ni Date: Wed, 3 Jul 2013 15:08:00 -0700 Subject: drivers/rtc/rtc-palmas.c: init wakeup before device register Enable dev as wakeup device before calling rtc_device_register(), so that it can create the "wakealarm" sysfs. Signed-off-by: Wei Ni Acked-by: Laxman Dewangan Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-palmas.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/rtc-palmas.c b/drivers/rtc/rtc-palmas.c index 50204d474eb7..a1fecc8d97fc 100644 --- a/drivers/rtc/rtc-palmas.c +++ b/drivers/rtc/rtc-palmas.c @@ -265,6 +265,7 @@ static int palmas_rtc_probe(struct platform_device *pdev) palmas_rtc->irq = platform_get_irq(pdev, 0); + device_init_wakeup(&pdev->dev, 1); palmas_rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, &palmas_rtc_ops, THIS_MODULE); if (IS_ERR(palmas_rtc->rtc)) { @@ -283,7 +284,6 @@ static int palmas_rtc_probe(struct platform_device *pdev) return ret; } - device_set_wakeup_capable(&pdev->dev, 1); return 0; } -- cgit v1.2.3 From 18cb6368f0b0fc6a28bd49ee547b4f655db97fc3 Mon Sep 17 00:00:00 2001 From: Renaud Cerrato Date: Wed, 3 Jul 2013 15:08:01 -0700 Subject: rtc: add NXP PCF2127 support (i2c) Added support for NXP PCF2127 RTC (i2c). [akpm@linux-foundation.org: fix typo, fix warnings] Signed-off-by: Renaud Cerrato Cc: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/Kconfig | 9 ++ drivers/rtc/Makefile | 1 + drivers/rtc/rtc-pcf2127.c | 241 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 251 insertions(+) create mode 100644 drivers/rtc/rtc-pcf2127.c diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index b9838130a7b0..8009e5d8776a 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -313,6 +313,15 @@ config RTC_DRV_PALMAS This driver can also be built as a module. If so, the module will be called rtc-palma. +config RTC_DRV_PCF2127 + tristate "NXP PCF2127" + help + If you say yes here you get support for the NXP PCF2127/29 RTC + chips. + + This driver can also be built as a module. If so, the module + will be called rtc-pcf2127. + config RTC_DRV_PCF8523 tristate "NXP PCF8523" help diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index c33f86f1a69b..994a2fac8277 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -83,6 +83,7 @@ obj-$(CONFIG_RTC_DRV_NUC900) += rtc-nuc900.o obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o obj-$(CONFIG_RTC_DRV_PALMAS) += rtc-palmas.o obj-$(CONFIG_RTC_DRV_PCAP) += rtc-pcap.o +obj-$(CONFIG_RTC_DRV_PCF2127) += rtc-pcf2127.o obj-$(CONFIG_RTC_DRV_PCF8523) += rtc-pcf8523.o obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o obj-$(CONFIG_RTC_DRV_PCF8583) += rtc-pcf8583.o diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c new file mode 100644 index 000000000000..205b9f7da1b8 --- /dev/null +++ b/drivers/rtc/rtc-pcf2127.c @@ -0,0 +1,241 @@ +/* + * An I2C driver for the NXP PCF2127 RTC + * Copyright 2013 Til-Technologies + * + * Author: Renaud Cerrato + * + * based on the other drivers in this same directory. + * + * http://www.nxp.com/documents/data_sheet/PCF2127AT.pdf + * + * 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 +#include +#include +#include +#include +#include + +#define DRV_VERSION "0.0.1" + +#define PCF2127_REG_CTRL1 (0x00) /* Control Register 1 */ +#define PCF2127_REG_CTRL2 (0x01) /* Control Register 2 */ +#define PCF2127_REG_CTRL3 (0x02) /* Control Register 3 */ +#define PCF2127_REG_SC (0x03) /* datetime */ +#define PCF2127_REG_MN (0x04) +#define PCF2127_REG_HR (0x05) +#define PCF2127_REG_DM (0x06) +#define PCF2127_REG_DW (0x07) +#define PCF2127_REG_MO (0x08) +#define PCF2127_REG_YR (0x09) + +static struct i2c_driver pcf2127_driver; + +struct pcf2127 { + struct rtc_device *rtc; + int voltage_low; /* indicates if a low_voltage was detected */ +}; + +/* + * In the routines that deal directly with the pcf2127 hardware, we use + * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch. + */ +static int pcf2127_get_datetime(struct i2c_client *client, struct rtc_time *tm) +{ + struct pcf2127 *pcf2127 = i2c_get_clientdata(client); + unsigned char buf[10] = { PCF2127_REG_CTRL1 }; + + /* read registers */ + if (i2c_master_send(client, buf, 1) != 1 || + i2c_master_recv(client, buf, sizeof(buf)) != sizeof(buf)) { + dev_err(&client->dev, "%s: read error\n", __func__); + return -EIO; + } + + if (buf[PCF2127_REG_CTRL3] & 0x04) { + pcf2127->voltage_low = 1; + dev_info(&client->dev, + "low voltage detected, date/time is not reliable.\n"); + } + + dev_dbg(&client->dev, + "%s: raw data is cr1=%02x, cr2=%02x, cr3=%02x, " + "sec=%02x, min=%02x, hr=%02x, " + "mday=%02x, wday=%02x, mon=%02x, year=%02x\n", + __func__, + buf[0], buf[1], buf[2], + buf[3], buf[4], buf[5], + buf[6], buf[7], buf[8], buf[9]); + + + tm->tm_sec = bcd2bin(buf[PCF2127_REG_SC] & 0x7F); + tm->tm_min = bcd2bin(buf[PCF2127_REG_MN] & 0x7F); + tm->tm_hour = bcd2bin(buf[PCF2127_REG_HR] & 0x3F); /* rtc hr 0-23 */ + tm->tm_mday = bcd2bin(buf[PCF2127_REG_DM] & 0x3F); + tm->tm_wday = buf[PCF2127_REG_DW] & 0x07; + tm->tm_mon = bcd2bin(buf[PCF2127_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */ + tm->tm_year = bcd2bin(buf[PCF2127_REG_YR]); + if (tm->tm_year < 70) + tm->tm_year += 100; /* assume we are in 1970...2069 */ + + dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, " + "mday=%d, mon=%d, year=%d, wday=%d\n", + __func__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + + /* the clock can give out invalid datetime, but we cannot return + * -EINVAL otherwise hwclock will refuse to set the time on bootup. + */ + if (rtc_valid_tm(tm) < 0) + dev_err(&client->dev, "retrieved date/time is not valid.\n"); + + return 0; +} + +static int pcf2127_set_datetime(struct i2c_client *client, struct rtc_time *tm) +{ + unsigned char buf[8]; + int i = 0, err; + + dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, " + "mday=%d, mon=%d, year=%d, wday=%d\n", + __func__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + + /* start register address */ + buf[i++] = PCF2127_REG_SC; + + /* hours, minutes and seconds */ + buf[i++] = bin2bcd(tm->tm_sec); + buf[i++] = bin2bcd(tm->tm_min); + buf[i++] = bin2bcd(tm->tm_hour); + buf[i++] = bin2bcd(tm->tm_mday); + buf[i++] = tm->tm_wday & 0x07; + + /* month, 1 - 12 */ + buf[i++] = bin2bcd(tm->tm_mon + 1); + + /* year */ + buf[i++] = bin2bcd(tm->tm_year % 100); + + /* write register's data */ + err = i2c_master_send(client, buf, i); + if (err != i) { + dev_err(&client->dev, + "%s: err=%d", __func__, err); + return -EIO; + } + + return 0; +} + +#ifdef CONFIG_RTC_INTF_DEV +static int pcf2127_rtc_ioctl(struct device *dev, + unsigned int cmd, unsigned long arg) +{ + struct pcf2127 *pcf2127 = i2c_get_clientdata(to_i2c_client(dev)); + + switch (cmd) { + case RTC_VL_READ: + if (pcf2127->voltage_low) + dev_info(dev, "low voltage detected, date/time is not reliable.\n"); + + if (copy_to_user((void __user *)arg, &pcf2127->voltage_low, + sizeof(int))) + return -EFAULT; + return 0; + default: + return -ENOIOCTLCMD; + } +} +#else +#define pcf2127_rtc_ioctl NULL +#endif + +static int pcf2127_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + return pcf2127_get_datetime(to_i2c_client(dev), tm); +} + +static int pcf2127_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + return pcf2127_set_datetime(to_i2c_client(dev), tm); +} + +static const struct rtc_class_ops pcf2127_rtc_ops = { + .ioctl = pcf2127_rtc_ioctl, + .read_time = pcf2127_rtc_read_time, + .set_time = pcf2127_rtc_set_time, +}; + +static int pcf2127_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct pcf2127 *pcf2127; + + dev_dbg(&client->dev, "%s\n", __func__); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + pcf2127 = devm_kzalloc(&client->dev, sizeof(struct pcf2127), + GFP_KERNEL); + if (!pcf2127) + return -ENOMEM; + + dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n"); + + i2c_set_clientdata(client, pcf2127); + + pcf2127->rtc = devm_rtc_device_register(&client->dev, + pcf2127_driver.driver.name, + &pcf2127_rtc_ops, THIS_MODULE); + + if (IS_ERR(pcf2127->rtc)) + return PTR_ERR(pcf2127->rtc); + + return 0; +} + +static int pcf2127_remove(struct i2c_client *client) +{ + return 0; +} + +static const struct i2c_device_id pcf2127_id[] = { + { "pcf2127", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pcf2127_id); + +#ifdef CONFIG_OF +static const struct of_device_id pcf2127_of_match[] = { + { .compatible = "nxp,pcf2127" }, + {} +}; +MODULE_DEVICE_TABLE(of, pcf2127_of_match); +#endif + +static struct i2c_driver pcf2127_driver = { + .driver = { + .name = "rtc-pcf2127", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(pcf2127_of_match), + }, + .probe = pcf2127_probe, + .remove = pcf2127_remove, + .id_table = pcf2127_id, +}; + +module_i2c_driver(pcf2127_driver); + +MODULE_AUTHOR("Renaud Cerrato "); +MODULE_DESCRIPTION("NXP PCF2127 RTC driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); -- cgit v1.2.3 From 1d2e2b65d098c0a321e16b0997eb760439d99500 Mon Sep 17 00:00:00 2001 From: Hebbar Gururaja Date: Wed, 3 Jul 2013 15:08:02 -0700 Subject: rtc: omap: restore back (hard-code) wakeup support rtc-omap driver modules is used both by OMAP1/2, Davinci SoC platforms. However, rtc wake support on OMAP1 is broken. Hence the device_init_wakeup() was removed from rtc-omap driver and moved to platform board files that supported it (DA850/OMAP-L138). [1] However, recently [2] it was suggested that driver should always do a device_init_wakeup(dev, true). Platforms that don't want/need wakeup support can disable it from userspace via: echo disabled > /sys/devices/.../power/wakeup Also, with the new DT boot-up, board file doesn't exist and hence there is no way to have device wakeup support rtc. The fix for above issues, is to hard code device_init_wakeup() inside driver and let platforms that don't need this, handle it through the sysfs power entry. [1] https://patchwork.kernel.org/patch/136731/ [2] http://www.mail-archive.com/davinci-linux-open-source@linux. davincidsp.com/msg26077.html Signed-off-by: Hebbar Gururaja Cc: Alessandro Zummo Acked-by: Kevin Hilman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-omap.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c index 6f6ac033d5d9..c6ffbaec32a4 100644 --- a/drivers/rtc/rtc-omap.c +++ b/drivers/rtc/rtc-omap.c @@ -421,6 +421,8 @@ static int __init omap_rtc_probe(struct platform_device *pdev) * is write-only, and always reads as zero...) */ + device_init_wakeup(&pdev->dev, true); + if (new_ctrl & (u8) OMAP_RTC_CTRL_SPLIT) pr_info("%s: split power mode\n", pdev->name); -- cgit v1.2.3 From 061d2a3e39c4686039519509d69cd31c7841f666 Mon Sep 17 00:00:00 2001 From: Fabio Porcedda Date: Wed, 3 Jul 2013 15:08:03 -0700 Subject: drivers/rtc/rtc-ds1216.c: use module_platform_driver_probe() Use module_platform_driver_probe() macro which makes the code smaller and simpler. Signed-off-by: Fabio Porcedda Cc: Alessandro Zummo Cc: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-ds1216.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/rtc/rtc-ds1216.c b/drivers/rtc/rtc-ds1216.c index 1831c8461b90..9c04fd2bc209 100644 --- a/drivers/rtc/rtc-ds1216.c +++ b/drivers/rtc/rtc-ds1216.c @@ -174,21 +174,10 @@ static struct platform_driver ds1216_rtc_platform_driver = { }, }; -static int __init ds1216_rtc_init(void) -{ - return platform_driver_probe(&ds1216_rtc_platform_driver, ds1216_rtc_probe); -} - -static void __exit ds1216_rtc_exit(void) -{ - platform_driver_unregister(&ds1216_rtc_platform_driver); -} +module_platform_driver_probe(ds1216_rtc_platform_driver, ds1216_rtc_probe); MODULE_AUTHOR("Thomas Bogendoerfer "); MODULE_DESCRIPTION("DS1216 RTC driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); MODULE_ALIAS("platform:rtc-ds1216"); - -module_init(ds1216_rtc_init); -module_exit(ds1216_rtc_exit); -- cgit v1.2.3 From e88b815e011bcacb7066f1156ce390f6b0adc9df Mon Sep 17 00:00:00 2001 From: Xianglong Du Date: Wed, 3 Jul 2013 15:08:04 -0700 Subject: drivers/rtc/rtc-sirfsoc.c: add rtc drivers for CSR SiRFprimaII and SiRFatlasVI On CSR SiRFprimaII/atlasVI, there is a programmable 16-bit divider (RTC_DIV) that divides the input 32.768KHz clock to the frequency that users need (E.g. 1 Hz). The divided real-time clock will be used to drive a 32-bit counter (RTC_COUNTER) that provides users with the actual time. In each cycle of the divided real-time clock, there is a Hertz interrupt generated to the RISC. Users can also configure an alarm (RTC_ALARM). When RTC_COUNTER matches the alarm, there will be an alarm interrupt generated to the RISC. The system RTC can generate an alarm wake-up signal to notify the power controller to wake up from power saving mode. Signed-off-by: Xianglong Du Signed-off-by: Barry Song Cc: Jingoo Han Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/arm/boot/dts/atlas6.dtsi | 2 +- arch/arm/boot/dts/prima2.dtsi | 2 +- drivers/rtc/Kconfig | 7 + drivers/rtc/Makefile | 1 + drivers/rtc/rtc-sirfsoc.c | 475 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 485 insertions(+), 2 deletions(-) create mode 100644 drivers/rtc/rtc-sirfsoc.c diff --git a/arch/arm/boot/dts/atlas6.dtsi b/arch/arm/boot/dts/atlas6.dtsi index 7d1a27949c13..9866cd736dee 100644 --- a/arch/arm/boot/dts/atlas6.dtsi +++ b/arch/arm/boot/dts/atlas6.dtsi @@ -613,7 +613,7 @@ }; rtc-iobg { - compatible = "sirf,prima2-rtciobg", "sirf-prima2-rtciobg-bus"; + compatible = "sirf,prima2-rtciobg", "sirf-prima2-rtciobg-bus", "simple-bus"; #address-cells = <1>; #size-cells = <1>; reg = <0x80030000 0x10000>; diff --git a/arch/arm/boot/dts/prima2.dtsi b/arch/arm/boot/dts/prima2.dtsi index 02edd8965f8a..05e9489cf95c 100644 --- a/arch/arm/boot/dts/prima2.dtsi +++ b/arch/arm/boot/dts/prima2.dtsi @@ -610,7 +610,7 @@ }; rtc-iobg { - compatible = "sirf,prima2-rtciobg", "sirf-prima2-rtciobg-bus"; + compatible = "sirf,prima2-rtciobg", "sirf-prima2-rtciobg-bus", "simple-bus"; #address-cells = <1>; #size-cells = <1>; reg = <0x80030000 0x10000>; diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 8009e5d8776a..9e3498bf302b 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1242,6 +1242,13 @@ config RTC_DRV_SNVS This driver can also be built as a module, if so, the module will be called "rtc-snvs". +config RTC_DRV_SIRFSOC + tristate "SiRFSOC RTC" + depends on ARCH_SIRF + help + Say "yes" here to support the real time clock on SiRF SOC chips. + This driver can also be built as a module called rtc-sirfsoc. + comment "HID Sensor RTC drivers" config RTC_DRV_HID_SENSOR_TIME diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 994a2fac8277..d3b4488f48f2 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -129,3 +129,4 @@ obj-$(CONFIG_RTC_DRV_VT8500) += rtc-vt8500.o obj-$(CONFIG_RTC_DRV_WM831X) += rtc-wm831x.o obj-$(CONFIG_RTC_DRV_WM8350) += rtc-wm8350.o obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o +obj-$(CONFIG_RTC_DRV_SIRFSOC) += rtc-sirfsoc.o diff --git a/drivers/rtc/rtc-sirfsoc.c b/drivers/rtc/rtc-sirfsoc.c new file mode 100644 index 000000000000..aa7ed4b5f7f0 --- /dev/null +++ b/drivers/rtc/rtc-sirfsoc.c @@ -0,0 +1,475 @@ +/* + * SiRFSoC Real Time Clock interface for Linux + * + * Copyright (c) 2013 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + +#define RTC_CN 0x00 +#define RTC_ALARM0 0x04 +#define RTC_ALARM1 0x18 +#define RTC_STATUS 0x08 +#define RTC_SW_VALUE 0x40 +#define SIRFSOC_RTC_AL1E (1<<6) +#define SIRFSOC_RTC_AL1 (1<<4) +#define SIRFSOC_RTC_HZE (1<<3) +#define SIRFSOC_RTC_AL0E (1<<2) +#define SIRFSOC_RTC_HZ (1<<1) +#define SIRFSOC_RTC_AL0 (1<<0) +#define RTC_DIV 0x0c +#define RTC_DEEP_CTRL 0x14 +#define RTC_CLOCK_SWITCH 0x1c +#define SIRFSOC_RTC_CLK 0x03 /* others are reserved */ + +/* Refer to RTC DIV switch */ +#define RTC_HZ 16 + +/* This macro is also defined in arch/arm/plat-sirfsoc/cpu.c */ +#define RTC_SHIFT 4 + +#define INTR_SYSRTC_CN 0x48 + +struct sirfsoc_rtc_drv { + struct rtc_device *rtc; + u32 rtc_base; + u32 irq; + /* Overflow for every 8 years extra time */ + u32 overflow_rtc; +#ifdef CONFIG_PM + u32 saved_counter; + u32 saved_overflow_rtc; +#endif +}; + +static int sirfsoc_rtc_read_alarm(struct device *dev, + struct rtc_wkalrm *alrm) +{ + unsigned long rtc_alarm, rtc_count; + struct sirfsoc_rtc_drv *rtcdrv; + + rtcdrv = (struct sirfsoc_rtc_drv *)dev_get_drvdata(dev); + + local_irq_disable(); + + rtc_count = sirfsoc_rtc_iobrg_readl(rtcdrv->rtc_base + RTC_CN); + + rtc_alarm = sirfsoc_rtc_iobrg_readl(rtcdrv->rtc_base + RTC_ALARM0); + memset(alrm, 0, sizeof(struct rtc_wkalrm)); + + /* + * assume alarm interval not beyond one round counter overflow_rtc: + * 0->0xffffffff + */ + /* if alarm is in next overflow cycle */ + if (rtc_count > rtc_alarm) + rtc_time_to_tm((rtcdrv->overflow_rtc + 1) + << (BITS_PER_LONG - RTC_SHIFT) + | rtc_alarm >> RTC_SHIFT, &(alrm->time)); + else + rtc_time_to_tm(rtcdrv->overflow_rtc + << (BITS_PER_LONG - RTC_SHIFT) + | rtc_alarm >> RTC_SHIFT, &(alrm->time)); + if (sirfsoc_rtc_iobrg_readl( + rtcdrv->rtc_base + RTC_STATUS) & SIRFSOC_RTC_AL0E) + alrm->enabled = 1; + local_irq_enable(); + + return 0; +} + +static int sirfsoc_rtc_set_alarm(struct device *dev, + struct rtc_wkalrm *alrm) +{ + unsigned long rtc_status_reg, rtc_alarm; + struct sirfsoc_rtc_drv *rtcdrv; + rtcdrv = (struct sirfsoc_rtc_drv *)dev_get_drvdata(dev); + + if (alrm->enabled) { + rtc_tm_to_time(&(alrm->time), &rtc_alarm); + + local_irq_disable(); + + rtc_status_reg = sirfsoc_rtc_iobrg_readl( + rtcdrv->rtc_base + RTC_STATUS); + if (rtc_status_reg & SIRFSOC_RTC_AL0E) { + /* + * An ongoing alarm in progress - ingore it and not + * to return EBUSY + */ + dev_info(dev, "An old alarm was set, will be replaced by a new one\n"); + } + + sirfsoc_rtc_iobrg_writel( + rtc_alarm << RTC_SHIFT, rtcdrv->rtc_base + RTC_ALARM0); + rtc_status_reg &= ~0x07; /* mask out the lower status bits */ + /* + * This bit RTC_AL sets it as a wake-up source for Sleep Mode + * Writing 1 into this bit will clear it + */ + rtc_status_reg |= SIRFSOC_RTC_AL0; + /* enable the RTC alarm interrupt */ + rtc_status_reg |= SIRFSOC_RTC_AL0E; + sirfsoc_rtc_iobrg_writel( + rtc_status_reg, rtcdrv->rtc_base + RTC_STATUS); + local_irq_enable(); + } else { + /* + * if this function was called with enabled=0 + * then it could mean that the application is + * trying to cancel an ongoing alarm + */ + local_irq_disable(); + + rtc_status_reg = sirfsoc_rtc_iobrg_readl( + rtcdrv->rtc_base + RTC_STATUS); + if (rtc_status_reg & SIRFSOC_RTC_AL0E) { + /* clear the RTC status register's alarm bit */ + rtc_status_reg &= ~0x07; + /* write 1 into SIRFSOC_RTC_AL0 to force a clear */ + rtc_status_reg |= (SIRFSOC_RTC_AL0); + /* Clear the Alarm enable bit */ + rtc_status_reg &= ~(SIRFSOC_RTC_AL0E); + + sirfsoc_rtc_iobrg_writel(rtc_status_reg, + rtcdrv->rtc_base + RTC_STATUS); + } + + local_irq_enable(); + } + + return 0; +} + +static int sirfsoc_rtc_read_time(struct device *dev, + struct rtc_time *tm) +{ + unsigned long tmp_rtc = 0; + struct sirfsoc_rtc_drv *rtcdrv; + rtcdrv = (struct sirfsoc_rtc_drv *)dev_get_drvdata(dev); + /* + * This patch is taken from WinCE - Need to validate this for + * correctness. To work around sirfsoc RTC counter double sync logic + * fail, read several times to make sure get stable value. + */ + do { + tmp_rtc = sirfsoc_rtc_iobrg_readl(rtcdrv->rtc_base + RTC_CN); + cpu_relax(); + } while (tmp_rtc != sirfsoc_rtc_iobrg_readl(rtcdrv->rtc_base + RTC_CN)); + + rtc_time_to_tm(rtcdrv->overflow_rtc << (BITS_PER_LONG - RTC_SHIFT) | + tmp_rtc >> RTC_SHIFT, tm); + return 0; +} + +static int sirfsoc_rtc_set_time(struct device *dev, + struct rtc_time *tm) +{ + unsigned long rtc_time; + struct sirfsoc_rtc_drv *rtcdrv; + rtcdrv = (struct sirfsoc_rtc_drv *)dev_get_drvdata(dev); + + rtc_tm_to_time(tm, &rtc_time); + + rtcdrv->overflow_rtc = rtc_time >> (BITS_PER_LONG - RTC_SHIFT); + + sirfsoc_rtc_iobrg_writel(rtcdrv->overflow_rtc, + rtcdrv->rtc_base + RTC_SW_VALUE); + sirfsoc_rtc_iobrg_writel( + rtc_time << RTC_SHIFT, rtcdrv->rtc_base + RTC_CN); + + return 0; +} + +static int sirfsoc_rtc_ioctl(struct device *dev, unsigned int cmd, + unsigned long arg) +{ + switch (cmd) { + case RTC_PIE_ON: + case RTC_PIE_OFF: + case RTC_UIE_ON: + case RTC_UIE_OFF: + case RTC_AIE_ON: + case RTC_AIE_OFF: + return 0; + + default: + return -ENOIOCTLCMD; + } +} + +static const struct rtc_class_ops sirfsoc_rtc_ops = { + .read_time = sirfsoc_rtc_read_time, + .set_time = sirfsoc_rtc_set_time, + .read_alarm = sirfsoc_rtc_read_alarm, + .set_alarm = sirfsoc_rtc_set_alarm, + .ioctl = sirfsoc_rtc_ioctl +}; + +static irqreturn_t sirfsoc_rtc_irq_handler(int irq, void *pdata) +{ + struct sirfsoc_rtc_drv *rtcdrv = pdata; + unsigned long rtc_status_reg = 0x0; + unsigned long events = 0x0; + + rtc_status_reg = sirfsoc_rtc_iobrg_readl(rtcdrv->rtc_base + RTC_STATUS); + /* this bit will be set ONLY if an alarm was active + * and it expired NOW + * So this is being used as an ASSERT + */ + if (rtc_status_reg & SIRFSOC_RTC_AL0) { + /* + * clear the RTC status register's alarm bit + * mask out the lower status bits + */ + rtc_status_reg &= ~0x07; + /* write 1 into SIRFSOC_RTC_AL0 to ACK the alarm interrupt */ + rtc_status_reg |= (SIRFSOC_RTC_AL0); + /* Clear the Alarm enable bit */ + rtc_status_reg &= ~(SIRFSOC_RTC_AL0E); + } + sirfsoc_rtc_iobrg_writel(rtc_status_reg, rtcdrv->rtc_base + RTC_STATUS); + /* this should wake up any apps polling/waiting on the read + * after setting the alarm + */ + events |= RTC_IRQF | RTC_AF; + rtc_update_irq(rtcdrv->rtc, 1, events); + + return IRQ_HANDLED; +} + +static const struct of_device_id sirfsoc_rtc_of_match[] = { + { .compatible = "sirf,prima2-sysrtc"}, + {}, +}; +MODULE_DEVICE_TABLE(of, sirfsoc_rtc_of_match); + +static int sirfsoc_rtc_probe(struct platform_device *pdev) +{ + int err; + unsigned long rtc_div; + struct sirfsoc_rtc_drv *rtcdrv; + struct device_node *np = pdev->dev.of_node; + + rtcdrv = devm_kzalloc(&pdev->dev, + sizeof(struct sirfsoc_rtc_drv), GFP_KERNEL); + if (rtcdrv == NULL) { + dev_err(&pdev->dev, + "%s: can't alloc mem for drv struct\n", + pdev->name); + return -ENOMEM; + } + + err = of_property_read_u32(np, "reg", &rtcdrv->rtc_base); + if (err) { + dev_err(&pdev->dev, "unable to find base address of rtc node in dtb\n"); + goto error; + } + + platform_set_drvdata(pdev, rtcdrv); + + /* Register rtc alarm as a wakeup source */ + device_init_wakeup(&pdev->dev, 1); + + /* + * Set SYS_RTC counter in RTC_HZ HZ Units + * We are using 32K RTC crystal (32768 / RTC_HZ / 2) -1 + * If 16HZ, therefore RTC_DIV = 1023; + */ + rtc_div = ((32768 / RTC_HZ) / 2) - 1; + sirfsoc_rtc_iobrg_writel(rtc_div, rtcdrv->rtc_base + RTC_DIV); + + rtcdrv->rtc = rtc_device_register(pdev->name, &(pdev->dev), + &sirfsoc_rtc_ops, THIS_MODULE); + if (IS_ERR(rtcdrv->rtc)) { + err = PTR_ERR(rtcdrv->rtc); + dev_err(&pdev->dev, "can't register RTC device\n"); + return err; + } + + /* 0x3 -> RTC_CLK */ + sirfsoc_rtc_iobrg_writel(SIRFSOC_RTC_CLK, + rtcdrv->rtc_base + RTC_CLOCK_SWITCH); + + /* reset SYS RTC ALARM0 */ + sirfsoc_rtc_iobrg_writel(0x0, rtcdrv->rtc_base + RTC_ALARM0); + + /* reset SYS RTC ALARM1 */ + sirfsoc_rtc_iobrg_writel(0x0, rtcdrv->rtc_base + RTC_ALARM1); + + /* Restore RTC Overflow From Register After Command Reboot */ + rtcdrv->overflow_rtc = + sirfsoc_rtc_iobrg_readl(rtcdrv->rtc_base + RTC_SW_VALUE); + + rtcdrv->irq = platform_get_irq(pdev, 0); + err = devm_request_irq( + &pdev->dev, + rtcdrv->irq, + sirfsoc_rtc_irq_handler, + IRQF_SHARED, + pdev->name, + rtcdrv); + if (err) { + dev_err(&pdev->dev, "Unable to register for the SiRF SOC RTC IRQ\n"); + goto error; + } + + return 0; + +error: + if (rtcdrv->rtc) + rtc_device_unregister(rtcdrv->rtc); + + return err; +} + +static int sirfsoc_rtc_remove(struct platform_device *pdev) +{ + struct sirfsoc_rtc_drv *rtcdrv = platform_get_drvdata(pdev); + + device_init_wakeup(&pdev->dev, 0); + rtc_device_unregister(rtcdrv->rtc); + + return 0; +} + +#ifdef CONFIG_PM + +static int sirfsoc_rtc_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct sirfsoc_rtc_drv *rtcdrv = platform_get_drvdata(pdev); + rtcdrv->overflow_rtc = + sirfsoc_rtc_iobrg_readl(rtcdrv->rtc_base + RTC_SW_VALUE); + + rtcdrv->saved_counter = + sirfsoc_rtc_iobrg_readl(rtcdrv->rtc_base + RTC_CN); + rtcdrv->saved_overflow_rtc = rtcdrv->overflow_rtc; + if (device_may_wakeup(&pdev->dev)) + enable_irq_wake(rtcdrv->irq); + + return 0; +} + +static int sirfsoc_rtc_freeze(struct device *dev) +{ + sirfsoc_rtc_suspend(dev); + + return 0; +} + +static int sirfsoc_rtc_thaw(struct device *dev) +{ + u32 tmp; + struct sirfsoc_rtc_drv *rtcdrv; + rtcdrv = (struct sirfsoc_rtc_drv *)dev_get_drvdata(dev); + + /* + * if resume from snapshot and the rtc power is losed, + * restroe the rtc settings + */ + if (SIRFSOC_RTC_CLK != sirfsoc_rtc_iobrg_readl( + rtcdrv->rtc_base + RTC_CLOCK_SWITCH)) { + u32 rtc_div; + /* 0x3 -> RTC_CLK */ + sirfsoc_rtc_iobrg_writel(SIRFSOC_RTC_CLK, + rtcdrv->rtc_base + RTC_CLOCK_SWITCH); + /* + * Set SYS_RTC counter in RTC_HZ HZ Units + * We are using 32K RTC crystal (32768 / RTC_HZ / 2) -1 + * If 16HZ, therefore RTC_DIV = 1023; + */ + rtc_div = ((32768 / RTC_HZ) / 2) - 1; + + sirfsoc_rtc_iobrg_writel(rtc_div, rtcdrv->rtc_base + RTC_DIV); + + /* reset SYS RTC ALARM0 */ + sirfsoc_rtc_iobrg_writel(0x0, rtcdrv->rtc_base + RTC_ALARM0); + + /* reset SYS RTC ALARM1 */ + sirfsoc_rtc_iobrg_writel(0x0, rtcdrv->rtc_base + RTC_ALARM1); + } + rtcdrv->overflow_rtc = rtcdrv->saved_overflow_rtc; + + /* + * if current counter is small than previous, + * it means overflow in sleep + */ + tmp = sirfsoc_rtc_iobrg_readl(rtcdrv->rtc_base + RTC_CN); + if (tmp <= rtcdrv->saved_counter) + rtcdrv->overflow_rtc++; + /* + *PWRC Value Be Changed When Suspend, Restore Overflow + * In Memory To Register + */ + sirfsoc_rtc_iobrg_writel(rtcdrv->overflow_rtc, + rtcdrv->rtc_base + RTC_SW_VALUE); + + return 0; +} + +static int sirfsoc_rtc_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct sirfsoc_rtc_drv *rtcdrv = platform_get_drvdata(pdev); + sirfsoc_rtc_thaw(dev); + if (device_may_wakeup(&pdev->dev)) + disable_irq_wake(rtcdrv->irq); + + return 0; +} + +static int sirfsoc_rtc_restore(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct sirfsoc_rtc_drv *rtcdrv = platform_get_drvdata(pdev); + + if (device_may_wakeup(&pdev->dev)) + disable_irq_wake(rtcdrv->irq); + return 0; +} + +#else +#define sirfsoc_rtc_suspend NULL +#define sirfsoc_rtc_resume NULL +#define sirfsoc_rtc_freeze NULL +#define sirfsoc_rtc_thaw NULL +#define sirfsoc_rtc_restore NULL +#endif + +static const struct dev_pm_ops sirfsoc_rtc_pm_ops = { + .suspend = sirfsoc_rtc_suspend, + .resume = sirfsoc_rtc_resume, + .freeze = sirfsoc_rtc_freeze, + .thaw = sirfsoc_rtc_thaw, + .restore = sirfsoc_rtc_restore, +}; + +static struct platform_driver sirfsoc_rtc_driver = { + .driver = { + .name = "sirfsoc-rtc", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &sirfsoc_rtc_pm_ops, +#endif + .of_match_table = of_match_ptr(sirfsoc_rtc_of_match), + }, + .probe = sirfsoc_rtc_probe, + .remove = sirfsoc_rtc_remove, +}; +module_platform_driver(sirfsoc_rtc_driver); + +MODULE_DESCRIPTION("SiRF SoC rtc driver"); +MODULE_AUTHOR("Xianglong Du "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:sirfsoc-rtc"); -- cgit v1.2.3 From c7ef972c440fc9f1eda28b450cd30ad15c4d60cf Mon Sep 17 00:00:00 2001 From: Vyacheslav Dubeyko Date: Wed, 3 Jul 2013 15:08:05 -0700 Subject: nilfs2: implement calculation of free inodes count Currently, NILFS2 returns 0 as free inodes count (f_ffree) and current used inodes count as total file nodes in file system (f_files): df -i Filesystem Inodes IUsed IFree IUse% Mounted on /dev/loop0 2 2 0 100% /mnt/nilfs2 This patch implements real calculation of free inodes count. First of all, it is calculated total file nodes in file system as (desc_blocks_count * groups_per_desc_block * entries_per_group). Then, it is calculated free inodes count as difference the total file nodes and used inodes count. As a result, we have such output for NILFS2: df -i Filesystem Inodes IUsed IFree IUse% Mounted on /dev/loop0 4194304 2114701 2079603 51% /mnt/nilfs2 Reported-by: Clemens Eisserer Signed-off-by: Vyacheslav Dubeyko Signed-off-by: Ryusuke Konishi Tested-by: Vyacheslav Dubeyko Cc: Joern Engel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/nilfs2/alloc.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/nilfs2/alloc.h | 2 ++ fs/nilfs2/ifile.c | 22 +++++++++++++++++++ fs/nilfs2/ifile.h | 2 ++ fs/nilfs2/super.c | 25 ++++++++++++++++++++-- 5 files changed, 112 insertions(+), 2 deletions(-) diff --git a/fs/nilfs2/alloc.c b/fs/nilfs2/alloc.c index eed4d7b26249..741fd02e0444 100644 --- a/fs/nilfs2/alloc.c +++ b/fs/nilfs2/alloc.c @@ -397,6 +397,69 @@ nilfs_palloc_rest_groups_in_desc_block(const struct inode *inode, max - curr + 1); } +/** + * nilfs_palloc_count_desc_blocks - count descriptor blocks number + * @inode: inode of metadata file using this allocator + * @desc_blocks: descriptor blocks number [out] + */ +static int nilfs_palloc_count_desc_blocks(struct inode *inode, + unsigned long *desc_blocks) +{ + unsigned long blknum; + int ret; + + ret = nilfs_bmap_last_key(NILFS_I(inode)->i_bmap, &blknum); + if (likely(!ret)) + *desc_blocks = DIV_ROUND_UP( + blknum, NILFS_MDT(inode)->mi_blocks_per_desc_block); + return ret; +} + +/** + * nilfs_palloc_mdt_file_can_grow - check potential opportunity for + * MDT file growing + * @inode: inode of metadata file using this allocator + * @desc_blocks: known current descriptor blocks count + */ +static inline bool nilfs_palloc_mdt_file_can_grow(struct inode *inode, + unsigned long desc_blocks) +{ + return (nilfs_palloc_groups_per_desc_block(inode) * desc_blocks) < + nilfs_palloc_groups_count(inode); +} + +/** + * nilfs_palloc_count_max_entries - count max number of entries that can be + * described by descriptor blocks count + * @inode: inode of metadata file using this allocator + * @nused: current number of used entries + * @nmaxp: max number of entries [out] + */ +int nilfs_palloc_count_max_entries(struct inode *inode, u64 nused, u64 *nmaxp) +{ + unsigned long desc_blocks = 0; + u64 entries_per_desc_block, nmax; + int err; + + err = nilfs_palloc_count_desc_blocks(inode, &desc_blocks); + if (unlikely(err)) + return err; + + entries_per_desc_block = (u64)nilfs_palloc_entries_per_group(inode) * + nilfs_palloc_groups_per_desc_block(inode); + nmax = entries_per_desc_block * desc_blocks; + + if (nused == nmax && + nilfs_palloc_mdt_file_can_grow(inode, desc_blocks)) + nmax += entries_per_desc_block; + + if (nused > nmax) + return -ERANGE; + + *nmaxp = nmax; + return 0; +} + /** * nilfs_palloc_prepare_alloc_entry - prepare to allocate a persistent object * @inode: inode of metadata file using this allocator diff --git a/fs/nilfs2/alloc.h b/fs/nilfs2/alloc.h index fb7238100548..4bd6451b5703 100644 --- a/fs/nilfs2/alloc.h +++ b/fs/nilfs2/alloc.h @@ -48,6 +48,8 @@ int nilfs_palloc_get_entry_block(struct inode *, __u64, int, void *nilfs_palloc_block_get_entry(const struct inode *, __u64, const struct buffer_head *, void *); +int nilfs_palloc_count_max_entries(struct inode *, u64, u64 *); + /** * nilfs_palloc_req - persistent allocator request and reply * @pr_entry_nr: entry number (vblocknr or inode number) diff --git a/fs/nilfs2/ifile.c b/fs/nilfs2/ifile.c index d8e65bde083c..d788a5928351 100644 --- a/fs/nilfs2/ifile.c +++ b/fs/nilfs2/ifile.c @@ -159,6 +159,28 @@ int nilfs_ifile_get_inode_block(struct inode *ifile, ino_t ino, return err; } +/** + * nilfs_ifile_count_free_inodes - calculate free inodes count + * @ifile: ifile inode + * @nmaxinodes: current maximum of available inodes count [out] + * @nfreeinodes: free inodes count [out] + */ +int nilfs_ifile_count_free_inodes(struct inode *ifile, + u64 *nmaxinodes, u64 *nfreeinodes) +{ + u64 nused; + int err; + + *nmaxinodes = 0; + *nfreeinodes = 0; + + nused = atomic_read(&NILFS_I(ifile)->i_root->inodes_count); + err = nilfs_palloc_count_max_entries(ifile, nused, nmaxinodes); + if (likely(!err)) + *nfreeinodes = *nmaxinodes - nused; + return err; +} + /** * nilfs_ifile_read - read or get ifile inode * @sb: super block instance diff --git a/fs/nilfs2/ifile.h b/fs/nilfs2/ifile.h index 59b6f2b51df6..679674d13372 100644 --- a/fs/nilfs2/ifile.h +++ b/fs/nilfs2/ifile.h @@ -49,6 +49,8 @@ int nilfs_ifile_create_inode(struct inode *, ino_t *, struct buffer_head **); int nilfs_ifile_delete_inode(struct inode *, ino_t); int nilfs_ifile_get_inode_block(struct inode *, ino_t, struct buffer_head **); +int nilfs_ifile_count_free_inodes(struct inode *, u64 *, u64 *); + int nilfs_ifile_read(struct super_block *sb, struct nilfs_root *root, size_t inode_size, struct nilfs_inode *raw_inode, struct inode **inodep); diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index c7d1f9f18b09..7d257e78fbad 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -609,6 +609,7 @@ static int nilfs_statfs(struct dentry *dentry, struct kstatfs *buf) unsigned long overhead; unsigned long nrsvblocks; sector_t nfreeblocks; + u64 nmaxinodes, nfreeinodes; int err; /* @@ -633,14 +634,34 @@ static int nilfs_statfs(struct dentry *dentry, struct kstatfs *buf) if (unlikely(err)) return err; + err = nilfs_ifile_count_free_inodes(root->ifile, + &nmaxinodes, &nfreeinodes); + if (unlikely(err)) { + printk(KERN_WARNING + "NILFS warning: fail to count free inodes: err %d.\n", + err); + if (err == -ERANGE) { + /* + * If nilfs_palloc_count_max_entries() returns + * -ERANGE error code then we simply treat + * curent inodes count as maximum possible and + * zero as free inodes value. + */ + nmaxinodes = atomic_read(&root->inodes_count); + nfreeinodes = 0; + err = 0; + } else + return err; + } + buf->f_type = NILFS_SUPER_MAGIC; buf->f_bsize = sb->s_blocksize; buf->f_blocks = blocks - overhead; buf->f_bfree = nfreeblocks; buf->f_bavail = (buf->f_bfree >= nrsvblocks) ? (buf->f_bfree - nrsvblocks) : 0; - buf->f_files = atomic_read(&root->inodes_count); - buf->f_ffree = 0; /* nilfs_count_free_inodes(sb); */ + buf->f_files = nmaxinodes; + buf->f_ffree = nfreeinodes; buf->f_namelen = NILFS_NAME_LEN; buf->f_fsid.val[0] = (u32)id; buf->f_fsid.val[1] = (u32)(id >> 32); -- cgit v1.2.3 From e5f7f84843154db8b6ef5b2ac5e286f72212f54e Mon Sep 17 00:00:00 2001 From: Vyacheslav Dubeyko Date: Wed, 3 Jul 2013 15:08:06 -0700 Subject: ] nilfs2: use atomic64_t type for inodes_count and blocks_count fields in nilfs_root struct The cp_inodes_count and cp_blocks_count are represented as __le64 type in on-disk structure (struct nilfs_checkpoint). But analogous fields in in-core structure (struct nilfs_root) are represented by atomic_t type. This patch replaces atomic_t on atomic64_t type in representation of inodes_count and blocks_count fields in struct nilfs_root. Signed-off-by: Vyacheslav Dubeyko Acked-by: Ryusuke Konishi Acked-by: Joern Engel Cc: Clemens Eisserer Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/nilfs2/ifile.c | 2 +- fs/nilfs2/inode.c | 8 ++++---- fs/nilfs2/segment.c | 4 ++-- fs/nilfs2/super.c | 8 +++++--- fs/nilfs2/the_nilfs.c | 4 ++-- fs/nilfs2/the_nilfs.h | 4 ++-- 6 files changed, 16 insertions(+), 14 deletions(-) diff --git a/fs/nilfs2/ifile.c b/fs/nilfs2/ifile.c index d788a5928351..6548c7851b48 100644 --- a/fs/nilfs2/ifile.c +++ b/fs/nilfs2/ifile.c @@ -174,7 +174,7 @@ int nilfs_ifile_count_free_inodes(struct inode *ifile, *nmaxinodes = 0; *nfreeinodes = 0; - nused = atomic_read(&NILFS_I(ifile)->i_root->inodes_count); + nused = atomic64_read(&NILFS_I(ifile)->i_root->inodes_count); err = nilfs_palloc_count_max_entries(ifile, nused, nmaxinodes); if (likely(!err)) *nfreeinodes = *nmaxinodes - nused; diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c index bccfec8343c5..b1a5277cfd18 100644 --- a/fs/nilfs2/inode.c +++ b/fs/nilfs2/inode.c @@ -54,7 +54,7 @@ void nilfs_inode_add_blocks(struct inode *inode, int n) inode_add_bytes(inode, (1 << inode->i_blkbits) * n); if (root) - atomic_add(n, &root->blocks_count); + atomic64_add(n, &root->blocks_count); } void nilfs_inode_sub_blocks(struct inode *inode, int n) @@ -63,7 +63,7 @@ void nilfs_inode_sub_blocks(struct inode *inode, int n) inode_sub_bytes(inode, (1 << inode->i_blkbits) * n); if (root) - atomic_sub(n, &root->blocks_count); + atomic64_sub(n, &root->blocks_count); } /** @@ -369,7 +369,7 @@ struct inode *nilfs_new_inode(struct inode *dir, umode_t mode) goto failed_ifile_create_inode; /* reference count of i_bh inherits from nilfs_mdt_read_block() */ - atomic_inc(&root->inodes_count); + atomic64_inc(&root->inodes_count); inode_init_owner(inode, dir, mode); inode->i_ino = ino; inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; @@ -801,7 +801,7 @@ void nilfs_evict_inode(struct inode *inode) ret = nilfs_ifile_delete_inode(ii->i_root->ifile, inode->i_ino); if (!ret) - atomic_dec(&ii->i_root->inodes_count); + atomic64_dec(&ii->i_root->inodes_count); nilfs_clear_inode(inode); diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c index a5752a589932..bd88a7461063 100644 --- a/fs/nilfs2/segment.c +++ b/fs/nilfs2/segment.c @@ -835,9 +835,9 @@ static int nilfs_segctor_fill_in_checkpoint(struct nilfs_sc_info *sci) raw_cp->cp_snapshot_list.ssl_next = 0; raw_cp->cp_snapshot_list.ssl_prev = 0; raw_cp->cp_inodes_count = - cpu_to_le64(atomic_read(&sci->sc_root->inodes_count)); + cpu_to_le64(atomic64_read(&sci->sc_root->inodes_count)); raw_cp->cp_blocks_count = - cpu_to_le64(atomic_read(&sci->sc_root->blocks_count)); + cpu_to_le64(atomic64_read(&sci->sc_root->blocks_count)); raw_cp->cp_nblk_inc = cpu_to_le64(sci->sc_nblk_inc + sci->sc_nblk_this_inc); raw_cp->cp_create = cpu_to_le64(sci->sc_seg_ctime); diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index 7d257e78fbad..1427de5ebf4d 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -554,8 +554,10 @@ int nilfs_attach_checkpoint(struct super_block *sb, __u64 cno, int curr_mnt, if (err) goto failed_bh; - atomic_set(&root->inodes_count, le64_to_cpu(raw_cp->cp_inodes_count)); - atomic_set(&root->blocks_count, le64_to_cpu(raw_cp->cp_blocks_count)); + atomic64_set(&root->inodes_count, + le64_to_cpu(raw_cp->cp_inodes_count)); + atomic64_set(&root->blocks_count, + le64_to_cpu(raw_cp->cp_blocks_count)); nilfs_cpfile_put_checkpoint(nilfs->ns_cpfile, cno, bh_cp); @@ -647,7 +649,7 @@ static int nilfs_statfs(struct dentry *dentry, struct kstatfs *buf) * curent inodes count as maximum possible and * zero as free inodes value. */ - nmaxinodes = atomic_read(&root->inodes_count); + nmaxinodes = atomic64_read(&root->inodes_count); nfreeinodes = 0; err = 0; } else diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c index 41e6a04a561f..94c451ce6d24 100644 --- a/fs/nilfs2/the_nilfs.c +++ b/fs/nilfs2/the_nilfs.c @@ -764,8 +764,8 @@ nilfs_find_or_create_root(struct the_nilfs *nilfs, __u64 cno) new->ifile = NULL; new->nilfs = nilfs; atomic_set(&new->count, 1); - atomic_set(&new->inodes_count, 0); - atomic_set(&new->blocks_count, 0); + atomic64_set(&new->inodes_count, 0); + atomic64_set(&new->blocks_count, 0); rb_link_node(&new->rb_node, parent, p); rb_insert_color(&new->rb_node, &nilfs->ns_cptree); diff --git a/fs/nilfs2/the_nilfs.h b/fs/nilfs2/the_nilfs.h index be1267a34cea..de8cc53b4a5c 100644 --- a/fs/nilfs2/the_nilfs.h +++ b/fs/nilfs2/the_nilfs.h @@ -241,8 +241,8 @@ struct nilfs_root { struct the_nilfs *nilfs; struct inode *ifile; - atomic_t inodes_count; - atomic_t blocks_count; + atomic64_t inodes_count; + atomic64_t blocks_count; }; /* Special checkpoint number */ -- cgit v1.2.3 From e68e96d2a7b2391cc1f70e207ec9cbe9d092adc9 Mon Sep 17 00:00:00 2001 From: Gu Zheng Date: Wed, 3 Jul 2013 15:08:08 -0700 Subject: fs/fat: use fat_msg() to replace printk() in __fat_fs_error() Signed-off-by: Gu Zheng Acked-by: OGAWA Hirofumi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/fat/misc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fs/fat/misc.c b/fs/fat/misc.c index 359d307b5507..628e22a5a543 100644 --- a/fs/fat/misc.c +++ b/fs/fat/misc.c @@ -30,7 +30,7 @@ void __fat_fs_error(struct super_block *sb, int report, const char *fmt, ...) va_start(args, fmt); vaf.fmt = fmt; vaf.va = &args; - printk(KERN_ERR "FAT-fs (%s): error, %pV\n", sb->s_id, &vaf); + fat_msg(sb, KERN_ERR, "error, %pV", &vaf); va_end(args); } @@ -38,8 +38,7 @@ void __fat_fs_error(struct super_block *sb, int report, const char *fmt, ...) panic("FAT-fs (%s): fs panic from previous error\n", sb->s_id); else if (opts->errors == FAT_ERRORS_RO && !(sb->s_flags & MS_RDONLY)) { sb->s_flags |= MS_RDONLY; - printk(KERN_ERR "FAT-fs (%s): Filesystem has been " - "set read-only\n", sb->s_id); + fat_msg(sb, KERN_ERR, "Filesystem has been set read-only"); } } EXPORT_SYMBOL_GPL(__fat_fs_error); -- cgit v1.2.3 From b57a0505e750b3d7b2d39e9823b276d8ca1a08fe Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 3 Jul 2013 15:08:08 -0700 Subject: Documentation/CodingStyle: allow multiple return statements per function A surprising number of newbies interpret this section to mean that only one return statement is allowed per function. Part of the problem is that the "one return statement per function" rule is an actual style guideline that people are used to from other projects. Signed-off-by: Dan Carpenter Cc: Eduardo Valentin Cc: Rob Landley Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/CodingStyle | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/CodingStyle b/Documentation/CodingStyle index e00b8f0dde52..7fe0546c504a 100644 --- a/Documentation/CodingStyle +++ b/Documentation/CodingStyle @@ -389,7 +389,8 @@ Albeit deprecated by some people, the equivalent of the goto statement is used frequently by compilers in form of the unconditional jump instruction. The goto statement comes in handy when a function exits from multiple -locations and some common work such as cleanup has to be done. +locations and some common work such as cleanup has to be done. If there is no +cleanup needed then just return directly. The rationale is: -- cgit v1.2.3 From 4d8eaaae7629074b6d3455cb71632f5969f34de7 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 3 Jul 2013 15:08:10 -0700 Subject: docbook: add futexes to kernel-locking docbook Add Fast User Mutexes (futexes) to kernel-locking docbook. Signed-off-by: Randy Dunlap Acked-by: Rob Landley Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/DocBook/kernel-locking.tmpl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Documentation/DocBook/kernel-locking.tmpl b/Documentation/DocBook/kernel-locking.tmpl index 67e7ab41c0a6..09e884e5b9f5 100644 --- a/Documentation/DocBook/kernel-locking.tmpl +++ b/Documentation/DocBook/kernel-locking.tmpl @@ -1955,12 +1955,17 @@ machines due to caching. - + Mutex API reference !Iinclude/linux/mutex.h !Ekernel/mutex.c + + Futex API reference +!Ikernel/futex.c + + Further reading -- cgit v1.2.3 From 37f07655522042399db7a4b54aee830bdb52ce2c Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 3 Jul 2013 15:08:11 -0700 Subject: x86: kill TIF_DEBUG Because it is not used. Signed-off-by: Oleg Nesterov Cc: Benjamin Herrenschmidt Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jan Kratochvil Cc: Michael Neuling Cc: Paul Mackerras Cc: Paul Mundt Cc: Will Deacon Cc: Prasad Cc: Russell King Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/x86/include/asm/thread_info.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h index a1df6e84691f..27811190cbd7 100644 --- a/arch/x86/include/asm/thread_info.h +++ b/arch/x86/include/asm/thread_info.h @@ -89,7 +89,6 @@ struct thread_info { #define TIF_FORK 18 /* ret_from_fork */ #define TIF_NOHZ 19 /* in adaptive nohz mode */ #define TIF_MEMDIE 20 /* is terminating due to OOM killer */ -#define TIF_DEBUG 21 /* uses debug registers */ #define TIF_IO_BITMAP 22 /* uses I/O bitmap */ #define TIF_FORCED_TF 24 /* true if TF in eflags artificially */ #define TIF_BLOCKSTEP 25 /* set when we want DEBUGCTLMSR_BTF */ @@ -113,7 +112,6 @@ struct thread_info { #define _TIF_IA32 (1 << TIF_IA32) #define _TIF_FORK (1 << TIF_FORK) #define _TIF_NOHZ (1 << TIF_NOHZ) -#define _TIF_DEBUG (1 << TIF_DEBUG) #define _TIF_IO_BITMAP (1 << TIF_IO_BITMAP) #define _TIF_FORCED_TF (1 << TIF_FORCED_TF) #define _TIF_BLOCKSTEP (1 << TIF_BLOCKSTEP) @@ -154,7 +152,7 @@ struct thread_info { (_TIF_IO_BITMAP|_TIF_NOTSC|_TIF_BLOCKSTEP) #define _TIF_WORK_CTXSW_PREV (_TIF_WORK_CTXSW|_TIF_USER_RETURN_NOTIFY) -#define _TIF_WORK_CTXSW_NEXT (_TIF_WORK_CTXSW|_TIF_DEBUG) +#define _TIF_WORK_CTXSW_NEXT (_TIF_WORK_CTXSW) #define PREEMPT_ACTIVE 0x10000000 -- cgit v1.2.3 From 29000caecbe87b6b66f144f72111f0d02fbbf0c1 Mon Sep 17 00:00:00 2001 From: Andrey Vagin Date: Wed, 3 Jul 2013 15:08:12 -0700 Subject: ptrace: add ability to get/set signal-blocked mask crtools uses a parasite code for dumping processes. The parasite code is injected into a process with help PTRACE_SEIZE. Currently crtools blocks signals from a parasite code. If a process has pending signals, crtools wait while a process handles these signals. This method is not suitable for stopped tasks. A stopped task can have a few pending signals, when we will try to execute a parasite code, we will need to drop SIGSTOP, but all other signals must remain pending, because a state of processes must not be changed during checkpointing. This patch adds two ptrace commands to set/get signal-blocked mask. I think gdb can use this commands too. [akpm@linux-foundation.org: be consistent with brace layout] Signed-off-by: Andrey Vagin Reviewed-by: Oleg Nesterov Cc: Roland McGrath Cc: Michael Kerrisk Cc: Pavel Emelyanov Cc: Cyrill Gorcunov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/uapi/linux/ptrace.h | 3 +++ kernel/ptrace.c | 44 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/include/uapi/linux/ptrace.h b/include/uapi/linux/ptrace.h index 52ebcc89f306..cf1019e15f5b 100644 --- a/include/uapi/linux/ptrace.h +++ b/include/uapi/linux/ptrace.h @@ -61,6 +61,9 @@ struct ptrace_peeksiginfo_args { __s32 nr; /* how may siginfos to take */ }; +#define PTRACE_GETSIGMASK 0x420a +#define PTRACE_SETSIGMASK 0x420b + /* Read signals from a shared (process wide) queue */ #define PTRACE_PEEKSIGINFO_SHARED (1 << 0) diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 335a7ae697f5..ba5e6cea181a 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -844,6 +844,47 @@ int ptrace_request(struct task_struct *child, long request, ret = ptrace_setsiginfo(child, &siginfo); break; + case PTRACE_GETSIGMASK: + if (addr != sizeof(sigset_t)) { + ret = -EINVAL; + break; + } + + if (copy_to_user(datavp, &child->blocked, sizeof(sigset_t))) + ret = -EFAULT; + else + ret = 0; + + break; + + case PTRACE_SETSIGMASK: { + sigset_t new_set; + + if (addr != sizeof(sigset_t)) { + ret = -EINVAL; + break; + } + + if (copy_from_user(&new_set, datavp, sizeof(sigset_t))) { + ret = -EFAULT; + break; + } + + sigdelsetmask(&new_set, sigmask(SIGKILL)|sigmask(SIGSTOP)); + + /* + * Every thread does recalc_sigpending() after resume, so + * retarget_shared_pending() and recalc_sigpending() are not + * called here. + */ + spin_lock_irq(&child->sighand->siglock); + child->blocked = new_set; + spin_unlock_irq(&child->sighand->siglock); + + ret = 0; + break; + } + case PTRACE_INTERRUPT: /* * Stop tracee without any side-effect on signal or job @@ -948,8 +989,7 @@ int ptrace_request(struct task_struct *child, long request, #ifdef CONFIG_HAVE_ARCH_TRACEHOOK case PTRACE_GETREGSET: - case PTRACE_SETREGSET: - { + case PTRACE_SETREGSET: { struct iovec kiov; struct iovec __user *uiov = datavp; -- cgit v1.2.3 From 77d55918029ef4626bfd6ba716728dcc7d919de3 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 3 Jul 2013 15:08:14 -0700 Subject: signals: eventpoll: do not use sigprocmask() sigprocmask() should die. None of the current callers actually need this strange interface. Change fs/eventpoll.c to use set_current_blocked(). This also means we should not worry about SIGKILL/SIGSTOP. Signed-off-by: Oleg Nesterov Cc: Al Viro Cc: Denys Vlasenko Cc: Eric Wong Cc: Jason Baron Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/eventpoll.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fs/eventpoll.c b/fs/eventpoll.c index deecc7294a67..0f0f73624a88 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -1975,8 +1975,8 @@ SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events, return -EINVAL; if (copy_from_user(&ksigmask, sigmask, sizeof(ksigmask))) return -EFAULT; - sigdelsetmask(&ksigmask, sigmask(SIGKILL) | sigmask(SIGSTOP)); - sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved); + sigsaved = current->blocked; + set_current_blocked(&ksigmask); } error = sys_epoll_wait(epfd, events, maxevents, timeout); @@ -1993,7 +1993,7 @@ SYSCALL_DEFINE6(epoll_pwait, int, epfd, struct epoll_event __user *, events, sizeof(sigsaved)); set_restore_sigmask(); } else - sigprocmask(SIG_SETMASK, &sigsaved, NULL); + set_current_blocked(&sigsaved); } return error; @@ -2020,8 +2020,8 @@ COMPAT_SYSCALL_DEFINE6(epoll_pwait, int, epfd, if (copy_from_user(&csigmask, sigmask, sizeof(csigmask))) return -EFAULT; sigset_from_compat(&ksigmask, &csigmask); - sigdelsetmask(&ksigmask, sigmask(SIGKILL) | sigmask(SIGSTOP)); - sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved); + sigsaved = current->blocked; + set_current_blocked(&ksigmask); } err = sys_epoll_wait(epfd, events, maxevents, timeout); @@ -2038,7 +2038,7 @@ COMPAT_SYSCALL_DEFINE6(epoll_pwait, int, epfd, sizeof(sigsaved)); set_restore_sigmask(); } else - sigprocmask(SIG_SETMASK, &sigsaved, NULL); + set_current_blocked(&sigsaved); } return err; -- cgit v1.2.3 From 7f57cfa4e2aa29fabe69e41529fd26578adc9b58 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 3 Jul 2013 15:08:15 -0700 Subject: usermodehelper: kill the sub_info->path[0] check call_usermodehelper_exec() does nothing but returns success if path[0] == 0. The only user which needs this strange feature is request_module(), it can check modprobe_path[0] itself like other users do if they want to detect the "disabled by admin" case. Kill it. Not only it looks strange, it can confuse other callers. And this allows us to revert 264b83c0 ("usermodehelper: check subprocess_info->path != NULL"), do_execve(NULL) is safe. Signed-off-by: Oleg Nesterov Acked-by: Rusty Russell Cc: Lucas De Marchi Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/kmod.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/kernel/kmod.c b/kernel/kmod.c index 8241906c4b61..fb326365b694 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c @@ -147,6 +147,9 @@ int __request_module(bool wait, const char *fmt, ...) */ WARN_ON_ONCE(wait && current_is_async()); + if (!modprobe_path[0]) + return 0; + va_start(args, fmt); ret = vsnprintf(module_name, MODULE_NAME_LEN, fmt, args); va_end(args); @@ -569,14 +572,6 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info, int wait) int retval = 0; helper_lock(); - if (!sub_info->path) { - retval = -EINVAL; - goto out; - } - - if (sub_info->path[0] == '\0') - goto out; - if (!khelper_wq || usermodehelper_disabled) { retval = -EBUSY; goto out; -- cgit v1.2.3 From e7fd1549aeb83e34ee0955cdf5dee5d4088508f3 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 3 Jul 2013 15:08:16 -0700 Subject: coredump: format_corename() can leak cn->corename do_coredump() assumes that format_corename() can only fail if expand_corename() fails and frees cn->corename. This is not true, for example cn_print_exe_file() can fail and in this case nobody frees cn->corename. Change do_coredump() to always do kfree(cn->corename) after it calls format_corename() (NULL is fine), change expand_corename() to do nothing if kmalloc() fails. Signed-off-by: Oleg Nesterov Cc: Andi Kleen Cc: Colin Walters Cc: Denys Vlasenko Cc: Jiri Slaby Cc: Lennart Poettering Cc: Lucas De Marchi Acked-by: Neil Horman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/coredump.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/fs/coredump.c b/fs/coredump.c index dafafbafa731..11bc368e0017 100644 --- a/fs/coredump.c +++ b/fs/coredump.c @@ -58,16 +58,14 @@ static atomic_t call_count = ATOMIC_INIT(1); static int expand_corename(struct core_name *cn) { - char *old_corename = cn->corename; + int size = CORENAME_MAX_SIZE * atomic_inc_return(&call_count); + char *corename = krealloc(cn->corename, size, GFP_KERNEL); - cn->size = CORENAME_MAX_SIZE * atomic_inc_return(&call_count); - cn->corename = krealloc(old_corename, cn->size, GFP_KERNEL); - - if (!cn->corename) { - kfree(old_corename); + if (!corename) return -ENOMEM; - } + cn->size = size; + cn->corename = corename; return 0; } @@ -157,10 +155,9 @@ static int format_corename(struct core_name *cn, struct coredump_params *cprm) int pid_in_pattern = 0; int err = 0; + cn->used = 0; cn->size = CORENAME_MAX_SIZE * atomic_read(&call_count); cn->corename = kmalloc(cn->size, GFP_KERNEL); - cn->used = 0; - if (!cn->corename) return -ENOMEM; @@ -549,7 +546,7 @@ void do_coredump(siginfo_t *siginfo) if (ispipe < 0) { printk(KERN_WARNING "format_corename failed\n"); printk(KERN_WARNING "Aborting core\n"); - goto fail_corename; + goto fail_unlock; } if (cprm.limit == 1) { @@ -669,7 +666,6 @@ fail_dropcount: atomic_dec(&core_dump_count); fail_unlock: kfree(cn.corename); -fail_corename: coredump_finish(mm, core_dumped); revert_creds(old_cred); fail_creds: -- cgit v1.2.3 From bc03c691aa86948af4e272ebdcdd4203018210f3 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 3 Jul 2013 15:08:17 -0700 Subject: coredump: introduce cn_vprintf() Turn cn_printf(...) into cn_vprintf(va_list args), reintroduce cn_printf() as a trivial wrapper. This simplifies the next change and cn_vprintf() will have more callers. Signed-off-by: Oleg Nesterov Cc: Andi Kleen Cc: Colin Walters Cc: Denys Vlasenko Cc: Jiri Slaby Cc: Lennart Poettering Cc: Lucas De Marchi Acked-by: Neil Horman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/coredump.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/fs/coredump.c b/fs/coredump.c index 11bc368e0017..c10a43aae220 100644 --- a/fs/coredump.c +++ b/fs/coredump.c @@ -69,17 +69,13 @@ static int expand_corename(struct core_name *cn) return 0; } -static int cn_printf(struct core_name *cn, const char *fmt, ...) +static int cn_vprintf(struct core_name *cn, const char *fmt, va_list arg) { char *cur; int need; int ret; - va_list arg; - va_start(arg, fmt); need = vsnprintf(NULL, 0, fmt, arg); - va_end(arg); - if (likely(need < cn->size - cn->used - 1)) goto out_printf; @@ -89,9 +85,7 @@ static int cn_printf(struct core_name *cn, const char *fmt, ...) out_printf: cur = cn->corename + cn->used; - va_start(arg, fmt); vsnprintf(cur, need + 1, fmt, arg); - va_end(arg); cn->used += need; return 0; @@ -99,6 +93,18 @@ expand_fail: return ret; } +static int cn_printf(struct core_name *cn, const char *fmt, ...) +{ + va_list arg; + int ret; + + va_start(arg, fmt); + ret = cn_vprintf(cn, fmt, arg); + va_end(arg); + + return ret; +} + static void cn_escape(char *str) { for (; *str; str++) -- cgit v1.2.3 From 5fe9d8ca21cc1517258fe448639392d5d542eec6 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 3 Jul 2013 15:08:19 -0700 Subject: coredump: cn_vprintf() has no reason to call vsnprintf() twice cn_vprintf() looks really overcomplicated and sub-optimal. We do not need vsnprintf(NULL) to calculate the size we need, we can simply try to print into the current buffer and expand/retry only if necessary. Signed-off-by: Oleg Nesterov Cc: Andi Kleen Cc: Colin Walters Cc: Denys Vlasenko Cc: Jiri Slaby Cc: Lennart Poettering Cc: Lucas De Marchi Acked-by: Neil Horman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/coredump.c | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/fs/coredump.c b/fs/coredump.c index c10a43aae220..2b1d1f54e630 100644 --- a/fs/coredump.c +++ b/fs/coredump.c @@ -71,26 +71,20 @@ static int expand_corename(struct core_name *cn) static int cn_vprintf(struct core_name *cn, const char *fmt, va_list arg) { - char *cur; - int need; - int ret; - - need = vsnprintf(NULL, 0, fmt, arg); - if (likely(need < cn->size - cn->used - 1)) - goto out_printf; - - ret = expand_corename(cn); - if (ret) - goto expand_fail; + int free, need; + +again: + free = cn->size - cn->used; + need = vsnprintf(cn->corename + cn->used, free, fmt, arg); + if (need < free) { + cn->used += need; + return 0; + } -out_printf: - cur = cn->corename + cn->used; - vsnprintf(cur, need + 1, fmt, arg); - cn->used += need; - return 0; + if (!expand_corename(cn)) + goto again; -expand_fail: - return ret; + return -ENOMEM; } static int cn_printf(struct core_name *cn, const char *fmt, ...) -- cgit v1.2.3 From 923bed030ff6e20b5176e10da151fade83097891 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 3 Jul 2013 15:08:20 -0700 Subject: coredump: kill cn_escape(), introduce cn_esc_printf() The usage of cn_escape() looks really annoying, imho this sequence needs a wrapper. And it is buggy. If cn_printf() does expand_corename() cn_escape() writes to the freed memory. Introduce cn_esc_printf() which hopefully does this all right. It records the index before cn_vprintf(), not "char *" which is no longer valid (in general) after krealloc(). Signed-off-by: Oleg Nesterov Cc: Andi Kleen Cc: Colin Walters Cc: Denys Vlasenko Cc: Jiri Slaby Cc: Lennart Poettering Cc: Lucas De Marchi Acked-by: Neil Horman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/coredump.c | 44 +++++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/fs/coredump.c b/fs/coredump.c index 2b1d1f54e630..90d7cee54347 100644 --- a/fs/coredump.c +++ b/fs/coredump.c @@ -99,11 +99,21 @@ static int cn_printf(struct core_name *cn, const char *fmt, ...) return ret; } -static void cn_escape(char *str) +static int cn_esc_printf(struct core_name *cn, const char *fmt, ...) { - for (; *str; str++) - if (*str == '/') - *str = '!'; + int cur = cn->used; + va_list arg; + int ret; + + va_start(arg, fmt); + ret = cn_vprintf(cn, fmt, arg); + va_end(arg); + + for (; cur < cn->used; ++cur) { + if (cn->corename[cur] == '/') + cn->corename[cur] = '!'; + } + return ret; } static int cn_print_exe_file(struct core_name *cn) @@ -113,12 +123,8 @@ static int cn_print_exe_file(struct core_name *cn) int ret; exe_file = get_mm_exe_file(current->mm); - if (!exe_file) { - char *commstart = cn->corename + cn->used; - ret = cn_printf(cn, "%s (path unknown)", current->comm); - cn_escape(commstart); - return ret; - } + if (!exe_file) + return cn_esc_printf(cn, "%s (path unknown)", current->comm); pathbuf = kmalloc(PATH_MAX, GFP_TEMPORARY); if (!pathbuf) { @@ -132,9 +138,7 @@ static int cn_print_exe_file(struct core_name *cn) goto free_buf; } - cn_escape(path); - - ret = cn_printf(cn, "%s", path); + ret = cn_esc_printf(cn, "%s", path); free_buf: kfree(pathbuf); @@ -207,22 +211,16 @@ static int format_corename(struct core_name *cn, struct coredump_params *cprm) break; } /* hostname */ - case 'h': { - char *namestart = cn->corename + cn->used; + case 'h': down_read(&uts_sem); - err = cn_printf(cn, "%s", + err = cn_esc_printf(cn, "%s", utsname()->nodename); up_read(&uts_sem); - cn_escape(namestart); break; - } /* executable */ - case 'e': { - char *commstart = cn->corename + cn->used; - err = cn_printf(cn, "%s", current->comm); - cn_escape(commstart); + case 'e': + err = cn_esc_printf(cn, "%s", current->comm); break; - } case 'E': err = cn_print_exe_file(cn); break; -- cgit v1.2.3 From 3ceadcf6d489650ade673b7197c11c521aecb038 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 3 Jul 2013 15:08:22 -0700 Subject: coredump: kill call_count, add core_name_size Imho, "atomic_t call_count" is ugly and should die. It buys nothing and in fact it can grow more than necessary, expand doesn't check if it was already incremented by another task. Kill it, and introduce "static int core_name_size" updated by expand_corename(). This is obviously racy too but harmless, and core_name_size never grows for no reason. We do not bother to to calculate the "right" new size, we simply do kmalloc(size_we_need) and use ksize() to rely on kmalloc_index's decision. Finally change format_corename() to use expand_corename(), krealloc(NULL) is fine. Signed-off-by: Oleg Nesterov Cc: Andi Kleen Cc: Colin Walters Cc: Denys Vlasenko Cc: Jiri Slaby Cc: Lennart Poettering Cc: Lucas De Marchi Acked-by: Neil Horman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/coredump.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/fs/coredump.c b/fs/coredump.c index 90d7cee54347..56a9ab963a40 100644 --- a/fs/coredump.c +++ b/fs/coredump.c @@ -45,26 +45,28 @@ #include int core_uses_pid; -char core_pattern[CORENAME_MAX_SIZE] = "core"; unsigned int core_pipe_limit; +char core_pattern[CORENAME_MAX_SIZE] = "core"; +static int core_name_size = CORENAME_MAX_SIZE; struct core_name { char *corename; int used, size; }; -static atomic_t call_count = ATOMIC_INIT(1); /* The maximal length of core_pattern is also specified in sysctl.c */ -static int expand_corename(struct core_name *cn) +static int expand_corename(struct core_name *cn, int size) { - int size = CORENAME_MAX_SIZE * atomic_inc_return(&call_count); char *corename = krealloc(cn->corename, size, GFP_KERNEL); if (!corename) return -ENOMEM; - cn->size = size; + if (size > core_name_size) /* racy but harmless */ + core_name_size = size; + + cn->size = ksize(corename); cn->corename = corename; return 0; } @@ -81,7 +83,7 @@ again: return 0; } - if (!expand_corename(cn)) + if (!expand_corename(cn, cn->size + need - free + 1)) goto again; return -ENOMEM; @@ -160,9 +162,8 @@ static int format_corename(struct core_name *cn, struct coredump_params *cprm) int err = 0; cn->used = 0; - cn->size = CORENAME_MAX_SIZE * atomic_read(&call_count); - cn->corename = kmalloc(cn->size, GFP_KERNEL); - if (!cn->corename) + cn->corename = NULL; + if (expand_corename(cn, core_name_size)) return -ENOMEM; /* Repeat as long as we have more pattern to process and more output -- cgit v1.2.3 From 888ffc5923e4343a78575918ab781e85fa22d244 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 3 Jul 2013 15:08:23 -0700 Subject: coredump: '% at the end' shouldn't bypass core_uses_pid logic "goto end" should not bypass the "Backward compatibility with core_uses_pid" code, move this label up. While at it, - It is ugly to copy '|' into cn->corename and then inc the pointer for argv_split(). Change format_corename() to increment pat_ptr instead. - Remove the dead "if (*pat_ptr == 0)" in format_corename(), we already checked it is not zero. Signed-off-by: Oleg Nesterov Cc: Andi Kleen Cc: Colin Walters Cc: Denys Vlasenko Cc: Jiri Slaby Cc: Lennart Poettering Cc: Lucas De Marchi Acked-by: Neil Horman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/coredump.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/fs/coredump.c b/fs/coredump.c index 56a9ab963a40..72f816d6cad9 100644 --- a/fs/coredump.c +++ b/fs/coredump.c @@ -165,13 +165,15 @@ static int format_corename(struct core_name *cn, struct coredump_params *cprm) cn->corename = NULL; if (expand_corename(cn, core_name_size)) return -ENOMEM; + cn->corename[0] = '\0'; + + if (ispipe) + ++pat_ptr; /* Repeat as long as we have more pattern to process and more output space */ while (*pat_ptr) { if (*pat_ptr != '%') { - if (*pat_ptr == 0) - goto out; err = cn_printf(cn, "%c", *pat_ptr++); } else { switch (*++pat_ptr) { @@ -240,6 +242,7 @@ static int format_corename(struct core_name *cn, struct coredump_params *cprm) return err; } +out: /* Backward compatibility with core_uses_pid: * * If core_pattern does not include a %p (as is the default) @@ -250,7 +253,6 @@ static int format_corename(struct core_name *cn, struct coredump_params *cprm) if (err) return err; } -out: return ispipe; } @@ -580,7 +582,7 @@ void do_coredump(siginfo_t *siginfo) goto fail_dropcount; } - helper_argv = argv_split(GFP_KERNEL, cn.corename+1, NULL); + helper_argv = argv_split(GFP_KERNEL, cn.corename, NULL); if (!helper_argv) { printk(KERN_WARNING "%s failed to allocate memory\n", __func__); @@ -597,7 +599,7 @@ void do_coredump(siginfo_t *siginfo) argv_free(helper_argv); if (retval) { - printk(KERN_INFO "Core dump to %s pipe failed\n", + printk(KERN_INFO "Core dump to |%s pipe failed\n", cn.corename); goto close_fail; } -- cgit v1.2.3 From 3f4185483832ccf3d2977923db576fa689c2abce Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 3 Jul 2013 15:08:25 -0700 Subject: fs/exec.c:de_thread(): use change_pid() rather than detach_pid/attach_pid de_thread() can use change_pid() instead of detach + attach. This looks better and this ensures that, say, next_thread() can never see a task with ->pid == NULL. Signed-off-by: Oleg Nesterov Acked-by: "Eric W. Biederman" Cc: Michal Hocko Cc: Pavel Emelyanov Cc: Sergey Dyasly Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/exec.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/exec.c b/fs/exec.c index 03b907cfd765..7619dddd5622 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -947,9 +947,8 @@ static int de_thread(struct task_struct *tsk) * Note: The old leader also uses this pid until release_task * is called. Odd but simple and correct. */ - detach_pid(tsk, PIDTYPE_PID); tsk->pid = leader->pid; - attach_pid(tsk, PIDTYPE_PID, task_pid(leader)); + change_pid(tsk, PIDTYPE_PID, task_pid(leader)); transfer_pid(leader, tsk, PIDTYPE_PGID); transfer_pid(leader, tsk, PIDTYPE_SID); -- cgit v1.2.3 From 81dabb464139324c005159f5afba377104d8828d Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 3 Jul 2013 15:08:26 -0700 Subject: exit.c: unexport __set_special_pids() Move __set_special_pids() from exit.c to sys.c close to its single caller and make it static. And rename it to set_special_pids(), another helper with this name has gone away. Signed-off-by: Oleg Nesterov Cc: "Eric W. Biederman" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/sched.h | 2 -- kernel/exit.c | 11 ----------- kernel/sys.c | 13 ++++++++++++- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/include/linux/sched.h b/include/linux/sched.h index ec80684a0127..cdd5407b37e2 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1950,8 +1950,6 @@ extern struct task_struct *find_task_by_vpid(pid_t nr); extern struct task_struct *find_task_by_pid_ns(pid_t nr, struct pid_namespace *ns); -extern void __set_special_pids(struct pid *pid); - /* per-UID process charging. */ extern struct user_struct * alloc_uid(kuid_t); static inline struct user_struct *get_uid(struct user_struct *u) diff --git a/kernel/exit.c b/kernel/exit.c index 7bb73f9d09db..3a77cd9390a1 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -312,17 +312,6 @@ kill_orphaned_pgrp(struct task_struct *tsk, struct task_struct *parent) } } -void __set_special_pids(struct pid *pid) -{ - struct task_struct *curr = current->group_leader; - - if (task_session(curr) != pid) - change_pid(curr, PIDTYPE_SID, pid); - - if (task_pgrp(curr) != pid) - change_pid(curr, PIDTYPE_PGID, pid); -} - /* * Let kernel threads use this to say that they allow a certain signal. * Must not be used if kthread was cloned with CLONE_SIGHAND. diff --git a/kernel/sys.c b/kernel/sys.c index 7bf50dcc6d53..071de900c824 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -1309,6 +1309,17 @@ out: return retval; } +static void set_special_pids(struct pid *pid) +{ + struct task_struct *curr = current->group_leader; + + if (task_session(curr) != pid) + change_pid(curr, PIDTYPE_SID, pid); + + if (task_pgrp(curr) != pid) + change_pid(curr, PIDTYPE_PGID, pid); +} + SYSCALL_DEFINE0(setsid) { struct task_struct *group_leader = current->group_leader; @@ -1328,7 +1339,7 @@ SYSCALL_DEFINE0(setsid) goto out; group_leader->signal->leader = 1; - __set_special_pids(sid); + set_special_pids(sid); proc_clear_tty(group_leader); -- cgit v1.2.3 From 1d98a5fa11e9a66a7cf7b03f73cab60184781065 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 3 Jul 2013 15:08:27 -0700 Subject: fs/proc/uptime.c:uptime_proc_show(): use get_monotonic_boottime() Change uptime_proc_show() to use get_monotonic_boottime() instead of do_posix_clock_monotonic_gettime() + monotonic_to_bootbased(). Signed-off-by: Oleg Nesterov Cc: "Eric W. Biederman" Acked-by: John Stultz Cc: Tomas Janousek Cc: Tomas Smetana Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/proc/uptime.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/proc/uptime.c b/fs/proc/uptime.c index 9610ac772d7e..061894625903 100644 --- a/fs/proc/uptime.c +++ b/fs/proc/uptime.c @@ -20,8 +20,7 @@ static int uptime_proc_show(struct seq_file *m, void *v) for_each_possible_cpu(i) idletime += (__force u64) kcpustat_cpu(i).cpustat[CPUTIME_IDLE]; - do_posix_clock_monotonic_gettime(&uptime); - monotonic_to_bootbased(&uptime); + get_monotonic_boottime(&uptime); nsec = cputime64_to_jiffies64(idletime) * TICK_NSEC; idle.tv_sec = div_u64_rem(nsec, NSEC_PER_SEC, &rem); idle.tv_nsec = rem; -- cgit v1.2.3 From 30bc30df102b2d0c003d93477e04b97e6c528573 Mon Sep 17 00:00:00 2001 From: Zhao Hongjiang Date: Wed, 3 Jul 2013 15:08:28 -0700 Subject: fs/proc/kcore.c: using strlcpy() instead of strncpy() For NUL terminated string, set '\0' at the end. Signed-off-by: Zhao Hongjiang Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/proc/kcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/proc/kcore.c b/fs/proc/kcore.c index 0a22194e5d58..06ea155e1a59 100644 --- a/fs/proc/kcore.c +++ b/fs/proc/kcore.c @@ -408,7 +408,7 @@ static void elf_kcore_store_hdr(char *bufp, int nphdr, int dataoff) prpsinfo.pr_zomb = 0; strcpy(prpsinfo.pr_fname, "vmlinux"); - strncpy(prpsinfo.pr_psargs, saved_command_line, ELF_PRARGSZ); + strlcpy(prpsinfo.pr_psargs, saved_command_line, sizeof(prpsinfo.pr_psargs)); nhdr->p_filesz += notesize(¬es[1]); bufp = storenote(¬es[1], bufp); -- cgit v1.2.3 From b57922b6c76c3ee401bb32fd3f298409dd6e6a53 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 3 Jul 2013 15:08:29 -0700 Subject: fork: reorder permissions when violating number of processes limits When a task is attempting to violate the RLIMIT_NPROC limit we have a check to see if the task is sufficiently priviledged. The check first looks at CAP_SYS_ADMIN, then CAP_SYS_RESOURCE, then if the task is uid=0. A result is that tasks which are allowed by the uid=0 check are first checked against the security subsystem. This results in the security subsystem auditting a denial for sys_admin and sys_resource and then the task passing the uid=0 check. This patch rearranges the code to first check uid=0, since if we pass that we shouldn't hit the security system at all. We then check sys_resource, since it is the smallest capability which will solve the problem. Lastly we check the fallback everything cap_sysadmin. We don't want to give this capability many places since it is so powerful. This will eliminate many of the false positive/needless denial messages we get when a root task tries to violate the nproc limit. (note that kthreads count against root, so on a sufficiently large machine we can actually get past the default limits before any userspace tasks are launched.) Signed-off-by: Eric Paris Cc: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/fork.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/fork.c b/kernel/fork.c index 987b28a1f01b..09dbda38a54b 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1199,8 +1199,8 @@ static struct task_struct *copy_process(unsigned long clone_flags, retval = -EAGAIN; if (atomic_read(&p->real_cred->user->processes) >= task_rlimit(p, RLIMIT_NPROC)) { - if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE) && - p->real_cred->user != INIT_USER) + if (p->real_cred->user != INIT_USER && + !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN)) goto bad_fork_free; } current->flags &= ~PF_NPROC_EXCEEDED; -- cgit v1.2.3 From 80628ca06c5d42929de6bc22c0a41589a834d151 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 3 Jul 2013 15:08:30 -0700 Subject: kernel/fork.c:copy_process(): unify CLONE_THREAD-or-thread_group_leader code Cleanup and preparation for the next changes. Move the "if (clone_flags & CLONE_THREAD)" code down under "if (likely(p->pid))" and turn it into into the "else" branch. This makes the process/thread initialization more symmetrical and removes one check. Signed-off-by: Oleg Nesterov Cc: "Eric W. Biederman" Cc: Michal Hocko Cc: Pavel Emelyanov Cc: Sergey Dyasly Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/fork.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/kernel/fork.c b/kernel/fork.c index 09dbda38a54b..417cb864e20c 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1446,14 +1446,6 @@ static struct task_struct *copy_process(unsigned long clone_flags, goto bad_fork_free_pid; } - if (clone_flags & CLONE_THREAD) { - current->signal->nr_threads++; - atomic_inc(¤t->signal->live); - atomic_inc(¤t->signal->sigcnt); - p->group_leader = current->group_leader; - list_add_tail_rcu(&p->thread_group, &p->group_leader->thread_group); - } - if (likely(p->pid)) { ptrace_init_task(p, (clone_flags & CLONE_PTRACE) || trace); @@ -1470,6 +1462,13 @@ static struct task_struct *copy_process(unsigned long clone_flags, list_add_tail(&p->sibling, &p->real_parent->children); list_add_tail_rcu(&p->tasks, &init_task.tasks); __this_cpu_inc(process_counts); + } else { + current->signal->nr_threads++; + atomic_inc(¤t->signal->live); + atomic_inc(¤t->signal->sigcnt); + p->group_leader = current->group_leader; + list_add_tail_rcu(&p->thread_group, + &p->group_leader->thread_group); } attach_pid(p, PIDTYPE_PID, pid); nr_threads++; -- cgit v1.2.3 From 8190773985141f063e1d6dc10200527c655abfb5 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 3 Jul 2013 15:08:31 -0700 Subject: kernel/fork.c:copy_process(): don't add the uninitialized child to thread/task/pid lists copy_process() adds the new child to thread_group/init_task.tasks list and then does attach_pid(child, PIDTYPE_PID). This means that the lockless next_thread() or next_task() can see this thread with the wrong pid. Say, "ls /proc/pid/task" can list the same inode twice. We could move attach_pid(child, PIDTYPE_PID) up, but in this case find_task_by_vpid() can find the new thread before it was fully initialized. And this is already true for PIDTYPE_PGID/PIDTYPE_SID, With this patch copy_process() initializes child->pids[*].pid first, then calls attach_pid() to insert the task into the pid->tasks list. attach_pid() no longer need the "struct pid*" argument, it is always called after pid_link->pid was already set. Signed-off-by: Oleg Nesterov Cc: "Eric W. Biederman" Cc: Michal Hocko Cc: Pavel Emelyanov Cc: Sergey Dyasly Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/pid.h | 6 ++---- kernel/fork.c | 16 +++++++++++++--- kernel/pid.c | 12 ++++-------- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/include/linux/pid.h b/include/linux/pid.h index a089a3c447fc..23705a53abba 100644 --- a/include/linux/pid.h +++ b/include/linux/pid.h @@ -86,11 +86,9 @@ extern struct task_struct *get_pid_task(struct pid *pid, enum pid_type); extern struct pid *get_task_pid(struct task_struct *task, enum pid_type type); /* - * attach_pid() and detach_pid() must be called with the tasklist_lock - * write-held. + * these helpers must be called with the tasklist_lock write-held. */ -extern void attach_pid(struct task_struct *task, enum pid_type type, - struct pid *pid); +extern void attach_pid(struct task_struct *task, enum pid_type); extern void detach_pid(struct task_struct *task, enum pid_type); extern void change_pid(struct task_struct *task, enum pid_type, struct pid *pid); diff --git a/kernel/fork.c b/kernel/fork.c index 417cb864e20c..7d6962fb6156 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1121,6 +1121,12 @@ static void posix_cpu_timers_init(struct task_struct *tsk) INIT_LIST_HEAD(&tsk->cpu_timers[2]); } +static inline void +init_task_pid(struct task_struct *task, enum pid_type type, struct pid *pid) +{ + task->pids[type].pid = pid; +} + /* * This creates a new process as a copy of the old one, * but does not actually start it yet. @@ -1449,7 +1455,11 @@ static struct task_struct *copy_process(unsigned long clone_flags, if (likely(p->pid)) { ptrace_init_task(p, (clone_flags & CLONE_PTRACE) || trace); + init_task_pid(p, PIDTYPE_PID, pid); if (thread_group_leader(p)) { + init_task_pid(p, PIDTYPE_PGID, task_pgrp(current)); + init_task_pid(p, PIDTYPE_SID, task_session(current)); + if (is_child_reaper(pid)) { ns_of_pid(pid)->child_reaper = p; p->signal->flags |= SIGNAL_UNKILLABLE; @@ -1457,10 +1467,10 @@ static struct task_struct *copy_process(unsigned long clone_flags, p->signal->leader_pid = pid; p->signal->tty = tty_kref_get(current->signal->tty); - attach_pid(p, PIDTYPE_PGID, task_pgrp(current)); - attach_pid(p, PIDTYPE_SID, task_session(current)); list_add_tail(&p->sibling, &p->real_parent->children); list_add_tail_rcu(&p->tasks, &init_task.tasks); + attach_pid(p, PIDTYPE_PGID); + attach_pid(p, PIDTYPE_SID); __this_cpu_inc(process_counts); } else { current->signal->nr_threads++; @@ -1470,7 +1480,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, list_add_tail_rcu(&p->thread_group, &p->group_leader->thread_group); } - attach_pid(p, PIDTYPE_PID, pid); + attach_pid(p, PIDTYPE_PID); nr_threads++; } diff --git a/kernel/pid.c b/kernel/pid.c index 0db3e791a06d..61980cefb1f5 100644 --- a/kernel/pid.c +++ b/kernel/pid.c @@ -373,14 +373,10 @@ EXPORT_SYMBOL_GPL(find_vpid); /* * attach_pid() must be called with the tasklist_lock write-held. */ -void attach_pid(struct task_struct *task, enum pid_type type, - struct pid *pid) +void attach_pid(struct task_struct *task, enum pid_type type) { - struct pid_link *link; - - link = &task->pids[type]; - link->pid = pid; - hlist_add_head_rcu(&link->node, &pid->tasks[type]); + struct pid_link *link = &task->pids[type]; + hlist_add_head_rcu(&link->node, &link->pid->tasks[type]); } static void __change_pid(struct task_struct *task, enum pid_type type, @@ -412,7 +408,7 @@ void change_pid(struct task_struct *task, enum pid_type type, struct pid *pid) { __change_pid(task, type, pid); - attach_pid(task, type, pid); + attach_pid(task, type); } /* transfer_pid is an optimization of attach_pid(new), detach_pid(old) */ -- cgit v1.2.3 From 18c830df771f2ba8b4699fea9af1492275ae627b Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 3 Jul 2013 15:08:32 -0700 Subject: kernel/fork.c:copy_process(): consolidate the lockless CLONE_THREAD checks copy_process() does a lot of "chaotic" initializations and checks CLONE_THREAD twice before it takes tasklist. In particular it sets "p->group_leader = p" and then changes it again under tasklist if !thread_group_leader(p). This looks a bit confusing, lets create a single "if (CLONE_THREAD)" block which initializes ->exit_signal, ->group_leader, and ->tgid. Signed-off-by: Oleg Nesterov Cc: "Eric W. Biederman" Cc: Michal Hocko Cc: Pavel Emelyanov Cc: Sergey Dyasly Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/fork.c | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/kernel/fork.c b/kernel/fork.c index 7d6962fb6156..6e6a1c11b3e5 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1360,11 +1360,6 @@ static struct task_struct *copy_process(unsigned long clone_flags, goto bad_fork_cleanup_io; } - p->pid = pid_nr(pid); - p->tgid = p->pid; - if (clone_flags & CLONE_THREAD) - p->tgid = current->tgid; - p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? child_tidptr : NULL; /* * Clear TID on mm_release()? @@ -1400,12 +1395,19 @@ static struct task_struct *copy_process(unsigned long clone_flags, clear_all_latency_tracing(p); /* ok, now we should be set up.. */ - if (clone_flags & CLONE_THREAD) + p->pid = pid_nr(pid); + if (clone_flags & CLONE_THREAD) { p->exit_signal = -1; - else if (clone_flags & CLONE_PARENT) - p->exit_signal = current->group_leader->exit_signal; - else - p->exit_signal = (clone_flags & CSIGNAL); + p->group_leader = current->group_leader; + p->tgid = current->tgid; + } else { + if (clone_flags & CLONE_PARENT) + p->exit_signal = current->group_leader->exit_signal; + else + p->exit_signal = (clone_flags & CSIGNAL); + p->group_leader = p; + p->tgid = p->pid; + } p->pdeath_signal = 0; p->exit_state = 0; @@ -1414,15 +1416,13 @@ static struct task_struct *copy_process(unsigned long clone_flags, p->nr_dirtied_pause = 128 >> (PAGE_SHIFT - 10); p->dirty_paused_when = 0; - /* - * Ok, make it visible to the rest of the system. - * We dont wake it up yet. - */ - p->group_leader = p; INIT_LIST_HEAD(&p->thread_group); p->task_works = NULL; - /* Need tasklist lock for parent etc handling! */ + /* + * Make it visible to the rest of the system, but dont wake it up yet. + * Need tasklist lock for parent etc handling! + */ write_lock_irq(&tasklist_lock); /* CLONE_PARENT re-uses the old parent */ @@ -1476,7 +1476,6 @@ static struct task_struct *copy_process(unsigned long clone_flags, current->signal->nr_threads++; atomic_inc(¤t->signal->live); atomic_inc(¤t->signal->sigcnt); - p->group_leader = current->group_leader; list_add_tail_rcu(&p->thread_group, &p->group_leader->thread_group); } -- cgit v1.2.3 From bd9d43f47d6944019918a801398c587ee86c4b52 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 3 Jul 2013 15:08:34 -0700 Subject: fs/exec.c: do_execve_common(): use current_user() Trivial cleanup. do_execve_common() can use current_user() and avoid the unnecessary "struct cred *cred" var. Signed-off-by: Oleg Nesterov Cc: Vasiliy Kulikov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/exec.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/exec.c b/fs/exec.c index 7619dddd5622..396aec2dae90 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1464,7 +1464,6 @@ static int do_execve_common(const char *filename, struct files_struct *displaced; bool clear_in_exec; int retval; - const struct cred *cred = current_cred(); /* * We move the actual failure in case of RLIMIT_NPROC excess from @@ -1473,7 +1472,7 @@ static int do_execve_common(const char *filename, * whether NPROC limit is still exceeded. */ if ((current->flags & PF_NPROC_EXCEEDED) && - atomic_read(&cred->user->processes) > rlimit(RLIMIT_NPROC)) { + atomic_read(¤t_user()->processes) > rlimit(RLIMIT_NPROC)) { retval = -EAGAIN; goto out_ret; } -- cgit v1.2.3 From 266b7a021f7dcc4d4531961a47f4ef74c3c4ab6b Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 3 Jul 2013 15:08:35 -0700 Subject: fs/exec.c:de_thread: mt-exec should update ->real_start_time 924b42d5 ("Use boot based time for process start time and boot time in /proc") updated copy_process/do_task_stat but forgot about de_thread(). This breaks "ps axOT" if a sub-thread execs. Note: I think that task->start_time should die. Signed-off-by: Oleg Nesterov Cc: "Eric W. Biederman" Acked-by: John Stultz Cc: Tomas Janousek Cc: Tomas Smetana Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/exec.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/exec.c b/fs/exec.c index 396aec2dae90..9c73def87642 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -932,6 +932,7 @@ static int de_thread(struct task_struct *tsk) * also take its birthdate (always earlier than our own). */ tsk->start_time = leader->start_time; + tsk->real_start_time = leader->real_start_time; BUG_ON(!same_thread_group(leader, tsk)); BUG_ON(has_group_leader_pid(tsk)); -- cgit v1.2.3 From a11edb59a05d8d5195419bd1fc28d82752324158 Mon Sep 17 00:00:00 2001 From: Zhang Yanfei Date: Wed, 3 Jul 2013 15:08:36 -0700 Subject: /dev/oldmem: Remove the interface /dev/oldmem provides the interface for us to access the "old memory" in the dump-capture kernel. Unfortunately, no one actually uses this interface. And this interface could actually cause some real problems if used on ia64 where the cached/uncached accesses are mixed. See the discussion from the link: https://lkml.org/lkml/2013/4/12/386. So Eric suggested that we should remove /dev/oldmem as an unused piece of code. [akpm@linux-foundation.org: mention /dev/oldmem obsolescence in devices.txt] Suggested-by: "Eric W. Biederman" Signed-off-by: Zhang Yanfei Cc: Vivek Goyal Cc: Dave Hansen Cc: "H. Peter Anvin" Cc: "H. Peter Anvin" Cc: Benjamin Herrenschmidt Cc: Fenghua Yu Cc: Heiko Carstens Cc: Martin Schwidefsky Cc: Matt Fleming Cc: Michael Holzheu Cc: Paul Mackerras Cc: Ralf Baechle Cc: Tony Luck Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/devices.txt | 3 +-- drivers/char/mem.c | 47 ----------------------------------------------- 2 files changed, 1 insertion(+), 49 deletions(-) diff --git a/Documentation/devices.txt b/Documentation/devices.txt index b9015912bca6..23721d3be3e6 100644 --- a/Documentation/devices.txt +++ b/Documentation/devices.txt @@ -100,8 +100,7 @@ Your cooperation is appreciated. 10 = /dev/aio Asynchronous I/O notification interface 11 = /dev/kmsg Writes to this come out as printk's, reads export the buffered printk records. - 12 = /dev/oldmem Used by crashdump kernels to access - the memory of the kernel that crashed. + 12 = /dev/oldmem OBSOLETE - replaced by /proc/vmcore 1 block RAM disk 0 = /dev/ram0 First RAM disk diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 2ca6d7844ad9..f895a8c8a244 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -357,40 +356,6 @@ static int mmap_kmem(struct file *file, struct vm_area_struct *vma) } #endif -#ifdef CONFIG_CRASH_DUMP -/* - * Read memory corresponding to the old kernel. - */ -static ssize_t read_oldmem(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - unsigned long pfn, offset; - size_t read = 0, csize; - int rc = 0; - - while (count) { - pfn = *ppos / PAGE_SIZE; - if (pfn > saved_max_pfn) - return read; - - offset = (unsigned long)(*ppos % PAGE_SIZE); - if (count > PAGE_SIZE - offset) - csize = PAGE_SIZE - offset; - else - csize = count; - - rc = copy_oldmem_page(pfn, buf, csize, offset, 1); - if (rc < 0) - return rc; - buf += csize; - *ppos += csize; - read += csize; - count -= csize; - } - return read; -} -#endif - #ifdef CONFIG_DEVKMEM /* * This function reads the *virtual* memory as seen by the kernel. @@ -772,7 +737,6 @@ static int open_port(struct inode *inode, struct file *filp) #define aio_write_zero aio_write_null #define open_mem open_port #define open_kmem open_mem -#define open_oldmem open_mem static const struct file_operations mem_fops = { .llseek = memory_lseek, @@ -837,14 +801,6 @@ static const struct file_operations full_fops = { .write = write_full, }; -#ifdef CONFIG_CRASH_DUMP -static const struct file_operations oldmem_fops = { - .read = read_oldmem, - .open = open_oldmem, - .llseek = default_llseek, -}; -#endif - static const struct memdev { const char *name; umode_t mode; @@ -866,9 +822,6 @@ static const struct memdev { #ifdef CONFIG_PRINTK [11] = { "kmsg", 0644, &kmsg_fops, NULL }, #endif -#ifdef CONFIG_CRASH_DUMP - [12] = { "oldmem", 0, &oldmem_fops, NULL }, -#endif }; static int memory_open(struct inode *inode, struct file *filp) -- cgit v1.2.3 From 987bf6fe3b1409b1cdf4352b3c421260c95d52f2 Mon Sep 17 00:00:00 2001 From: Zhang Yanfei Date: Wed, 3 Jul 2013 15:08:38 -0700 Subject: Documentation/kdump/kdump.txt: remove /dev/oldmem description Signed-off-by: Zhang Yanfei Cc: Vivek Goyal Cc: "Eric W. Biederman" Cc: "H. Peter Anvin" Cc: Benjamin Herrenschmidt Cc: Dave Hansen Cc: Fenghua Yu Cc: Heiko Carstens Cc: Martin Schwidefsky Cc: Matt Fleming Cc: Michael Holzheu Cc: Paul Mackerras Cc: Ralf Baechle Cc: Tony Luck Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/kdump/kdump.txt | 31 ++++++------------------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/Documentation/kdump/kdump.txt b/Documentation/kdump/kdump.txt index 9c7fd988e299..bec123e466ae 100644 --- a/Documentation/kdump/kdump.txt +++ b/Documentation/kdump/kdump.txt @@ -47,19 +47,12 @@ parameter. Optionally the size of the ELF header can also be passed when using the elfcorehdr=[size[KMG]@]offset[KMG] syntax. -With the dump-capture kernel, you can access the memory image, or "old -memory," in two ways: - -- Through a /dev/oldmem device interface. A capture utility can read the - device file and write out the memory in raw format. This is a raw dump - of memory. Analysis and capture tools must be intelligent enough to - determine where to look for the right information. - -- Through /proc/vmcore. This exports the dump as an ELF-format file that - you can write out using file copy commands such as cp or scp. Further, - you can use analysis tools such as the GNU Debugger (GDB) and the Crash - tool to debug the dump file. This method ensures that the dump pages are - correctly ordered. +With the dump-capture kernel, you can access the memory image through +/proc/vmcore. This exports the dump as an ELF-format file that you can +write out using file copy commands such as cp or scp. Further, you can +use analysis tools such as the GNU Debugger (GDB) and the Crash tool to +debug the dump file. This method ensures that the dump pages are correctly +ordered. Setup and Installation @@ -423,18 +416,6 @@ the following command: cp /proc/vmcore -You can also access dumped memory as a /dev/oldmem device for a linear -and raw view. To create the device, use the following command: - - mknod /dev/oldmem c 1 12 - -Use the dd command with suitable options for count, bs, and skip to -access specific portions of the dump. - -To see the entire memory, use the following command: - - dd if=/dev/oldmem of=oldmem.001 - Analysis ======== -- cgit v1.2.3 From db9ab97d9df678b6039e79de3516db83ec71b1f0 Mon Sep 17 00:00:00 2001 From: Zhang Yanfei Date: Wed, 3 Jul 2013 15:08:40 -0700 Subject: mips: remove savemaxmem parameter setup saved_max_pfn is used to know the amount of memory that the previous kernel used. And for powerpc, we set saved_max_pfn by passing the kernel commandline parameter "savemaxmem=". The only user of saved_max_pfn in mips is read_oldmem interface. Since we have removed read_oldmem, so we don't need this parameter anymore. Signed-off-by: Zhang Yanfei Cc: Ralf Baechle Cc: "Eric W. Biederman" Cc: "H. Peter Anvin" Cc: Benjamin Herrenschmidt Cc: Dave Hansen Cc: Fenghua Yu Cc: Heiko Carstens Cc: Martin Schwidefsky Cc: Matt Fleming Cc: Michael Holzheu Cc: Paul Mackerras Cc: Tony Luck Cc: Vivek Goyal Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/mips/kernel/crash_dump.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/arch/mips/kernel/crash_dump.c b/arch/mips/kernel/crash_dump.c index 3be9e7bb30ff..f291cf99b03a 100644 --- a/arch/mips/kernel/crash_dump.c +++ b/arch/mips/kernel/crash_dump.c @@ -4,16 +4,6 @@ #include #include -static int __init parse_savemaxmem(char *p) -{ - if (p) - saved_max_pfn = (memparse(p, &p) >> PAGE_SHIFT) - 1; - - return 1; -} -__setup("savemaxmem=", parse_savemaxmem); - - static void *kdump_buf_page; /** -- cgit v1.2.3 From 427fcd23b56598fd0dda7b8a5925c8479aa197db Mon Sep 17 00:00:00 2001 From: Zhang Yanfei Date: Wed, 3 Jul 2013 15:08:42 -0700 Subject: powerpc: Remove savemaxmem parameter setup saved_max_pfn is used to know the amount of memory that the previous kernel used. And for powerpc, we set saved_max_pfn by passing the kernel commandline parameter "savemaxmem=". The only user of saved_max_pfn in powerpc is read_oldmem interface. Since we have removed read_oldmem, we don't need this parameter anymore. Signed-off-by: Zhang Yanfei Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: "Eric W. Biederman" Cc: "H. Peter Anvin" Cc: Dave Hansen Cc: Fenghua Yu Cc: Heiko Carstens Cc: Martin Schwidefsky Cc: Matt Fleming Cc: Michael Holzheu Cc: Ralf Baechle Cc: Tony Luck Cc: Vivek Goyal Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/powerpc/kernel/crash_dump.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/arch/powerpc/kernel/crash_dump.c b/arch/powerpc/kernel/crash_dump.c index 9ec3fe174cba..779a78c26435 100644 --- a/arch/powerpc/kernel/crash_dump.c +++ b/arch/powerpc/kernel/crash_dump.c @@ -69,16 +69,6 @@ void __init setup_kdump_trampoline(void) } #endif /* CONFIG_NONSTATIC_KERNEL */ -static int __init parse_savemaxmem(char *p) -{ - if (p) - saved_max_pfn = (memparse(p, &p) >> PAGE_SHIFT) - 1; - - return 1; -} -__setup("savemaxmem=", parse_savemaxmem); - - static size_t copy_oldmem_vaddr(void *vaddr, char *buf, size_t csize, unsigned long offset, int userbuf) { -- cgit v1.2.3 From eeb407a57441e1178bb3ce1c8e1c23b877411f24 Mon Sep 17 00:00:00 2001 From: Zhang Yanfei Date: Wed, 3 Jul 2013 15:08:44 -0700 Subject: ia64: remove setting for saved_max_pfn The only user of saved_max_pfn in ia64 is read_oldmem interface but we have removed that interface, so saved_max_pfn is now unneeded in ia64, and we needn't set it anymore. Signed-off-by: Zhang Yanfei Cc: Matt Fleming Cc: Tony Luck Cc: Fenghua Yu Cc: "Eric W. Biederman" Cc: "H. Peter Anvin" Cc: Benjamin Herrenschmidt Cc: Dave Hansen Cc: Heiko Carstens Cc: Martin Schwidefsky Cc: Michael Holzheu Cc: Paul Mackerras Cc: Ralf Baechle Cc: Vivek Goyal Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/ia64/kernel/efi.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/arch/ia64/kernel/efi.c b/arch/ia64/kernel/efi.c index f034563aeae5..51bce594eb83 100644 --- a/arch/ia64/kernel/efi.c +++ b/arch/ia64/kernel/efi.c @@ -1116,11 +1116,6 @@ efi_memmap_init(u64 *s, u64 *e) if (!is_memory_available(md)) continue; -#ifdef CONFIG_CRASH_DUMP - /* saved_max_pfn should ignore max_addr= command line arg */ - if (saved_max_pfn < (efi_md_end(md) >> PAGE_SHIFT)) - saved_max_pfn = (efi_md_end(md) >> PAGE_SHIFT); -#endif /* * Round ends inward to granule boundaries * Give trimmings to uncached allocator -- cgit v1.2.3 From 8bdc237ac113dd42a1c977c8cd3a65a82f774d5e Mon Sep 17 00:00:00 2001 From: Zhang Yanfei Date: Wed, 3 Jul 2013 15:08:45 -0700 Subject: s390: remove setting for saved_max_pfn The only user of saved_max_pfn in s390 is read_oldmem interface but we have removed that interface, so saved_max_pfn is now unneeded in s390, and we needn't set it anymore. Signed-off-by: Zhang Yanfei Cc: Martin Schwidefsky Cc: Heiko Carstens Cc: Michael Holzheu Cc: "Eric W. Biederman" Cc: "H. Peter Anvin" Cc: Benjamin Herrenschmidt Cc: Dave Hansen Cc: Fenghua Yu Cc: Matt Fleming Cc: Paul Mackerras Cc: Ralf Baechle Cc: Tony Luck Cc: Vivek Goyal Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/s390/kernel/setup.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index 0a49095104c9..497451ec5e26 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -719,10 +719,6 @@ static void reserve_oldmem(void) } create_mem_hole(memory_chunk, OLDMEM_BASE, OLDMEM_SIZE); create_mem_hole(memory_chunk, OLDMEM_SIZE, real_size - OLDMEM_SIZE); - if (OLDMEM_BASE + OLDMEM_SIZE == real_size) - saved_max_pfn = PFN_DOWN(OLDMEM_BASE) - 1; - else - saved_max_pfn = PFN_DOWN(real_size) - 1; #endif } -- cgit v1.2.3 From dd04b452f532ca100f7c557295ffcbc049c77171 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 3 Jul 2013 15:08:47 -0700 Subject: idr: print a stack dump after ida_remove warning We print a dump stack after idr_remove warning. This is useful to find the faulty piece of code. Let's do the same for ida_remove, as it would be equally useful there. [akpm@linux-foundation.org: convert the open-coded printk+dump_stack into WARN()] Signed-off-by: Jean Delvare Cc: Tejun Heo Cc: Takashi Iwai Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- lib/idr.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/idr.c b/lib/idr.c index cca4b9302a71..bfe4db4e165f 100644 --- a/lib/idr.c +++ b/lib/idr.c @@ -524,9 +524,7 @@ EXPORT_SYMBOL(idr_alloc_cyclic); static void idr_remove_warning(int id) { - printk(KERN_WARNING - "idr_remove called for id=%d which is not allocated.\n", id); - dump_stack(); + WARN(1, "idr_remove called for id=%d which is not allocated.\n", id); } static void sub_remove(struct idr *idp, int shift, int id) @@ -1064,8 +1062,7 @@ void ida_remove(struct ida *ida, int id) return; err: - printk(KERN_WARNING - "ida_remove called for id=%d which is not allocated.\n", id); + WARN(1, "ida_remove called for id=%d which is not allocated.\n", id); } EXPORT_SYMBOL(ida_remove); -- cgit v1.2.3 From e350cd1d9308b67363ebb870a678a1fd807df1a6 Mon Sep 17 00:00:00 2001 From: Alexandre Bounine Date: Wed, 3 Jul 2013 15:08:48 -0700 Subject: rapidio/switches: remove tsi500 driver Remove the driver for Tsi500 Parallel RapidIO switch because this device has not been available for several years. Since the first introduction of Tsi500, the parallel RapidIO interface was replaced by the serial RapidIO (sRIO) and therefore there is no value in keeping this driver. Signed-off-by: Alexandre Bounine Cc: Matt Porter Cc: Li Yang Cc: Kumar Gala Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rapidio/switches/Kconfig | 7 ---- drivers/rapidio/switches/Makefile | 1 - drivers/rapidio/switches/tsi500.c | 78 --------------------------------------- 3 files changed, 86 deletions(-) delete mode 100644 drivers/rapidio/switches/tsi500.c diff --git a/drivers/rapidio/switches/Kconfig b/drivers/rapidio/switches/Kconfig index f47fee5d4563..62d4a065230f 100644 --- a/drivers/rapidio/switches/Kconfig +++ b/drivers/rapidio/switches/Kconfig @@ -26,10 +26,3 @@ config RAPIDIO_CPS_GEN2 default n ---help--- Includes support for ITD CPS Gen.2 serial RapidIO switches. - -config RAPIDIO_TSI500 - bool "Tsi500 Parallel RapidIO switch support" - depends on RAPIDIO - default n - ---help--- - Includes support for IDT Tsi500 parallel RapidIO switch. diff --git a/drivers/rapidio/switches/Makefile b/drivers/rapidio/switches/Makefile index c4d3acc3c715..051cc6b38188 100644 --- a/drivers/rapidio/switches/Makefile +++ b/drivers/rapidio/switches/Makefile @@ -5,5 +5,4 @@ obj-$(CONFIG_RAPIDIO_TSI57X) += tsi57x.o obj-$(CONFIG_RAPIDIO_CPS_XX) += idtcps.o obj-$(CONFIG_RAPIDIO_TSI568) += tsi568.o -obj-$(CONFIG_RAPIDIO_TSI500) += tsi500.o obj-$(CONFIG_RAPIDIO_CPS_GEN2) += idt_gen2.o diff --git a/drivers/rapidio/switches/tsi500.c b/drivers/rapidio/switches/tsi500.c deleted file mode 100644 index 914eddd5aa42..000000000000 --- a/drivers/rapidio/switches/tsi500.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - * RapidIO Tsi500 switch support - * - * Copyright 2009-2010 Integrated Device Technology, Inc. - * Alexandre Bounine - * - Modified switch operations initialization. - * - * Copyright 2005 MontaVista Software, Inc. - * Matt Porter - * - * 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 -#include -#include -#include "../rio.h" - -static int -tsi500_route_add_entry(struct rio_mport *mport, u16 destid, u8 hopcount, u16 table, u16 route_destid, u8 route_port) -{ - int i; - u32 offset = 0x10000 + 0xa00 + ((route_destid / 2)&~0x3); - u32 result; - - if (table == 0xff) { - rio_mport_read_config_32(mport, destid, hopcount, offset, &result); - result &= ~(0xf << (4*(route_destid & 0x7))); - for (i=0;i<4;i++) - rio_mport_write_config_32(mport, destid, hopcount, offset + (0x20000*i), result | (route_port << (4*(route_destid & 0x7)))); - } - else { - rio_mport_read_config_32(mport, destid, hopcount, offset + (0x20000*table), &result); - result &= ~(0xf << (4*(route_destid & 0x7))); - rio_mport_write_config_32(mport, destid, hopcount, offset + (0x20000*table), result | (route_port << (4*(route_destid & 0x7)))); - } - - return 0; -} - -static int -tsi500_route_get_entry(struct rio_mport *mport, u16 destid, u8 hopcount, u16 table, u16 route_destid, u8 *route_port) -{ - int ret = 0; - u32 offset = 0x10000 + 0xa00 + ((route_destid / 2)&~0x3); - u32 result; - - if (table == 0xff) - rio_mport_read_config_32(mport, destid, hopcount, offset, &result); - else - rio_mport_read_config_32(mport, destid, hopcount, offset + (0x20000*table), &result); - - result &= 0xf << (4*(route_destid & 0x7)); - *route_port = result >> (4*(route_destid & 0x7)); - if (*route_port > 3) - ret = -1; - - return ret; -} - -static int tsi500_switch_init(struct rio_dev *rdev, int do_enum) -{ - pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev)); - rdev->rswitch->add_entry = tsi500_route_add_entry; - rdev->rswitch->get_entry = tsi500_route_get_entry; - rdev->rswitch->clr_table = NULL; - rdev->rswitch->set_domain = NULL; - rdev->rswitch->get_domain = NULL; - rdev->rswitch->em_init = NULL; - rdev->rswitch->em_handle = NULL; - - return 0; -} - -DECLARE_RIO_SWITCH_INIT(RIO_VID_TUNDRA, RIO_DID_TSI500, tsi500_switch_init); -- cgit v1.2.3 From 36f0efbbe8e21c153dfc2f94c91f89ab06fd64c5 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Wed, 3 Jul 2013 15:08:49 -0700 Subject: drivers/rapidio/rio-scan.c: make functions static sparse warnings: drivers/rapidio/rio-scan.c:1143:5: sparse: symbol 'rio_enum_mport' was not declared. Should it be static? drivers/rapidio/rio-scan.c:1246:5: sparse: symbol 'rio_disc_mport' was not declared. Should it be static? Signed-off-by: Fengguang Wu Cc: "Bounine, Alexandre" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rapidio/rio-scan.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/rapidio/rio-scan.c b/drivers/rapidio/rio-scan.c index 4c15dbf81087..4b9b15ee8596 100644 --- a/drivers/rapidio/rio-scan.c +++ b/drivers/rapidio/rio-scan.c @@ -1141,7 +1141,7 @@ static void rio_pw_enable(struct rio_mport *port, int enable) * link, then start recursive peer enumeration. Returns %0 if * enumeration succeeds or %-EBUSY if enumeration fails. */ -int rio_enum_mport(struct rio_mport *mport, u32 flags) +static int rio_enum_mport(struct rio_mport *mport, u32 flags) { struct rio_net *net = NULL; int rc = 0; @@ -1256,7 +1256,7 @@ static void rio_build_route_tables(struct rio_net *net) * peer discovery. Returns %0 if discovery succeeds or %-EBUSY * on failure. */ -int rio_disc_mport(struct rio_mport *mport, u32 flags) +static int rio_disc_mport(struct rio_mport *mport, u32 flags) { struct rio_net *net = NULL; unsigned long to_end; -- cgit v1.2.3 From 2ec3ba69faf301fb599e3651515e808e8efa533e Mon Sep 17 00:00:00 2001 From: Alexandre Bounine Date: Wed, 3 Jul 2013 15:08:50 -0700 Subject: rapidio: convert switch drivers to modules Rework RapidIO switch drivers to add an option to build them as loadable kernel modules. This patch removes RapidIO-specific vmlinux section and converts switch drivers to be compatible with LDM driver registration method. To simplify registration of device-specific callback routines this patch introduces rio_switch_ops data structure. The sw_sysfs() callback is removed from the list of device-specific operations because under the new structure its functions can be handled by switch driver's probe() and remove() routines. If a specific switch device driver is not loaded the RapidIO subsystem core will use default standard-based operations to configure a switch. Because the current implementation of RapidIO enumeration/discovery method relies on availability of device-specific operations for error management, switch device drivers must be loaded before the RapidIO enumeration/discovery starts. This patch also moves several common routines from enumeration/discovery module into the RapidIO core code to make switch-specific operations accessible to all components of RapidIO subsystem. Signed-off-by: Alexandre Bounine Cc: Matt Porter Cc: Li Yang Cc: Kumar Gala Cc: Andre van Herk Cc: Micha Nelissen Cc: Stef van Os Cc: Jean Delvare Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rapidio/Kconfig | 5 + drivers/rapidio/rio-scan.c | 171 ++------------------- drivers/rapidio/rio-sysfs.c | 4 - drivers/rapidio/rio.c | 286 ++++++++++++++++++++++++++++++------ drivers/rapidio/rio.h | 41 +----- drivers/rapidio/switches/Kconfig | 12 +- drivers/rapidio/switches/idt_gen2.c | 98 +++++++++--- drivers/rapidio/switches/idtcps.c | 86 +++++++++-- drivers/rapidio/switches/tsi568.c | 71 +++++++-- drivers/rapidio/switches/tsi57x.c | 81 ++++++++-- include/asm-generic/vmlinux.lds.h | 7 - include/linux/rio.h | 51 +++---- 12 files changed, 568 insertions(+), 345 deletions(-) diff --git a/drivers/rapidio/Kconfig b/drivers/rapidio/Kconfig index 5ab056494bbe..3e3be57e9a1a 100644 --- a/drivers/rapidio/Kconfig +++ b/drivers/rapidio/Kconfig @@ -67,4 +67,9 @@ config RAPIDIO_ENUM_BASIC endchoice +menu "RapidIO Switch drivers" + depends on RAPIDIO + source "drivers/rapidio/switches/Kconfig" + +endmenu diff --git a/drivers/rapidio/rio-scan.c b/drivers/rapidio/rio-scan.c index 4b9b15ee8596..913950212605 100644 --- a/drivers/rapidio/rio-scan.c +++ b/drivers/rapidio/rio-scan.c @@ -406,6 +406,7 @@ static struct rio_dev *rio_setup_device(struct rio_net *net, rio_mport_write_config_32(port, destid, hopcount, RIO_COMPONENT_TAG_CSR, next_comptag); rdev->comp_tag = next_comptag++; + rdev->do_enum = true; } else { rio_mport_read_config_32(port, destid, hopcount, RIO_COMPONENT_TAG_CSR, @@ -434,6 +435,7 @@ static struct rio_dev *rio_setup_device(struct rio_net *net, rswitch = rdev->rswitch; rswitch->switchid = rdev->comp_tag & RIO_CTAG_UDEVID; rswitch->port_ok = 0; + spin_lock_init(&rswitch->lock); rswitch->route_table = kzalloc(sizeof(u8)* RIO_MAX_ROUTE_ENTRIES(port->sys_size), GFP_KERNEL); @@ -445,11 +447,9 @@ static struct rio_dev *rio_setup_device(struct rio_net *net, rswitch->route_table[rdid] = RIO_INVALID_ROUTE; dev_set_name(&rdev->dev, "%02x:s:%04x", rdev->net->id, rswitch->switchid); - rio_switch_init(rdev, do_enum); - if (do_enum && rswitch->clr_table) - rswitch->clr_table(port, destid, hopcount, - RIO_GLOBAL_TABLE); + if (do_enum) + rio_route_clr_table(rdev, RIO_GLOBAL_TABLE, 0); list_add_tail(&rswitch->node, &net->switches); @@ -532,156 +532,6 @@ rio_sport_is_active(struct rio_mport *port, u16 destid, u8 hopcount, int sport) return result & RIO_PORT_N_ERR_STS_PORT_OK; } -/** - * rio_lock_device - Acquires host device lock for specified device - * @port: Master port to send transaction - * @destid: Destination ID for device/switch - * @hopcount: Hopcount to reach switch - * @wait_ms: Max wait time in msec (0 = no timeout) - * - * Attepts to acquire host device lock for specified device - * Returns 0 if device lock acquired or EINVAL if timeout expires. - */ -static int -rio_lock_device(struct rio_mport *port, u16 destid, u8 hopcount, int wait_ms) -{ - u32 result; - int tcnt = 0; - - /* Attempt to acquire device lock */ - rio_mport_write_config_32(port, destid, hopcount, - RIO_HOST_DID_LOCK_CSR, port->host_deviceid); - rio_mport_read_config_32(port, destid, hopcount, - RIO_HOST_DID_LOCK_CSR, &result); - - while (result != port->host_deviceid) { - if (wait_ms != 0 && tcnt == wait_ms) { - pr_debug("RIO: timeout when locking device %x:%x\n", - destid, hopcount); - return -EINVAL; - } - - /* Delay a bit */ - mdelay(1); - tcnt++; - /* Try to acquire device lock again */ - rio_mport_write_config_32(port, destid, - hopcount, - RIO_HOST_DID_LOCK_CSR, - port->host_deviceid); - rio_mport_read_config_32(port, destid, - hopcount, - RIO_HOST_DID_LOCK_CSR, &result); - } - - return 0; -} - -/** - * rio_unlock_device - Releases host device lock for specified device - * @port: Master port to send transaction - * @destid: Destination ID for device/switch - * @hopcount: Hopcount to reach switch - * - * Returns 0 if device lock released or EINVAL if fails. - */ -static int -rio_unlock_device(struct rio_mport *port, u16 destid, u8 hopcount) -{ - u32 result; - - /* Release device lock */ - rio_mport_write_config_32(port, destid, - hopcount, - RIO_HOST_DID_LOCK_CSR, - port->host_deviceid); - rio_mport_read_config_32(port, destid, hopcount, - RIO_HOST_DID_LOCK_CSR, &result); - if ((result & 0xffff) != 0xffff) { - pr_debug("RIO: badness when releasing device lock %x:%x\n", - destid, hopcount); - return -EINVAL; - } - - return 0; -} - -/** - * rio_route_add_entry- Add a route entry to a switch routing table - * @rdev: RIO device - * @table: Routing table ID - * @route_destid: Destination ID to be routed - * @route_port: Port number to be routed - * @lock: lock switch device flag - * - * Calls the switch specific add_entry() method to add a route entry - * on a switch. The route table can be specified using the @table - * argument if a switch has per port routing tables or the normal - * use is to specific all tables (or the global table) by passing - * %RIO_GLOBAL_TABLE in @table. Returns %0 on success or %-EINVAL - * on failure. - */ -static int -rio_route_add_entry(struct rio_dev *rdev, - u16 table, u16 route_destid, u8 route_port, int lock) -{ - int rc; - - if (lock) { - rc = rio_lock_device(rdev->net->hport, rdev->destid, - rdev->hopcount, 1000); - if (rc) - return rc; - } - - rc = rdev->rswitch->add_entry(rdev->net->hport, rdev->destid, - rdev->hopcount, table, - route_destid, route_port); - if (lock) - rio_unlock_device(rdev->net->hport, rdev->destid, - rdev->hopcount); - - return rc; -} - -/** - * rio_route_get_entry- Read a route entry in a switch routing table - * @rdev: RIO device - * @table: Routing table ID - * @route_destid: Destination ID to be routed - * @route_port: Pointer to read port number into - * @lock: lock switch device flag - * - * Calls the switch specific get_entry() method to read a route entry - * in a switch. The route table can be specified using the @table - * argument if a switch has per port routing tables or the normal - * use is to specific all tables (or the global table) by passing - * %RIO_GLOBAL_TABLE in @table. Returns %0 on success or %-EINVAL - * on failure. - */ -static int -rio_route_get_entry(struct rio_dev *rdev, u16 table, - u16 route_destid, u8 *route_port, int lock) -{ - int rc; - - if (lock) { - rc = rio_lock_device(rdev->net->hport, rdev->destid, - rdev->hopcount, 1000); - if (rc) - return rc; - } - - rc = rdev->rswitch->get_entry(rdev->net->hport, rdev->destid, - rdev->hopcount, table, - route_destid, route_port); - if (lock) - rio_unlock_device(rdev->net->hport, rdev->destid, - rdev->hopcount); - - return rc; -} - /** * rio_get_host_deviceid_lock- Reads the Host Device ID Lock CSR on a device * @port: Master port to send transaction @@ -1094,12 +944,9 @@ static void rio_update_route_tables(struct rio_net *net) sport = RIO_GET_PORT_NUM(swrdev->swpinfo); - if (rswitch->add_entry) { - rio_route_add_entry(swrdev, - RIO_GLOBAL_TABLE, destid, - sport, 0); - rswitch->route_table[destid] = sport; - } + rio_route_add_entry(swrdev, RIO_GLOBAL_TABLE, + destid, sport, 0); + rswitch->route_table[destid] = sport; } } } @@ -1115,8 +962,8 @@ static void rio_update_route_tables(struct rio_net *net) static void rio_init_em(struct rio_dev *rdev) { if (rio_is_switch(rdev) && (rdev->em_efptr) && - (rdev->rswitch->em_init)) { - rdev->rswitch->em_init(rdev); + rdev->rswitch->ops && rdev->rswitch->ops->em_init) { + rdev->rswitch->ops->em_init(rdev); } } diff --git a/drivers/rapidio/rio-sysfs.c b/drivers/rapidio/rio-sysfs.c index 66d4acd5e18f..864e52f193e1 100644 --- a/drivers/rapidio/rio-sysfs.c +++ b/drivers/rapidio/rio-sysfs.c @@ -257,8 +257,6 @@ int rio_create_sysfs_dev_files(struct rio_dev *rdev) err |= device_create_file(&rdev->dev, &dev_attr_routes); err |= device_create_file(&rdev->dev, &dev_attr_lnext); err |= device_create_file(&rdev->dev, &dev_attr_hopcount); - if (!err && rdev->rswitch->sw_sysfs) - err = rdev->rswitch->sw_sysfs(rdev, RIO_SW_SYSFS_CREATE); } if (err) @@ -281,8 +279,6 @@ void rio_remove_sysfs_dev_files(struct rio_dev *rdev) device_remove_file(&rdev->dev, &dev_attr_routes); device_remove_file(&rdev->dev, &dev_attr_lnext); device_remove_file(&rdev->dev, &dev_attr_hopcount); - if (rdev->rswitch->sw_sysfs) - rdev->rswitch->sw_sysfs(rdev, RIO_SW_SYSFS_REMOVE); } } diff --git a/drivers/rapidio/rio.c b/drivers/rapidio/rio.c index cb1c08996fbb..b17d5218005e 100644 --- a/drivers/rapidio/rio.c +++ b/drivers/rapidio/rio.c @@ -7,7 +7,6 @@ * * Copyright 2009 Integrated Device Technology, Inc. * Alex Bounine - * - Added Port-Write/Error Management initialization and handling * * 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 @@ -579,44 +578,6 @@ int rio_set_port_lockout(struct rio_dev *rdev, u32 pnum, int lock) } EXPORT_SYMBOL_GPL(rio_set_port_lockout); -/** - * rio_switch_init - Sets switch operations for a particular vendor switch - * @rdev: RIO device - * @do_enum: Enumeration/Discovery mode flag - * - * Searches the RIO switch ops table for known switch types. If the vid - * and did match a switch table entry, then call switch initialization - * routine to setup switch-specific routines. - */ -void rio_switch_init(struct rio_dev *rdev, int do_enum) -{ - struct rio_switch_ops *cur = __start_rio_switch_ops; - struct rio_switch_ops *end = __end_rio_switch_ops; - - while (cur < end) { - if ((cur->vid == rdev->vid) && (cur->did == rdev->did)) { - pr_debug("RIO: calling init routine for %s\n", - rio_name(rdev)); - cur->init_hook(rdev, do_enum); - break; - } - cur++; - } - - if ((cur >= end) && (rdev->pef & RIO_PEF_STD_RT)) { - pr_debug("RIO: adding STD routing ops for %s\n", - rio_name(rdev)); - rdev->rswitch->add_entry = rio_std_route_add_entry; - rdev->rswitch->get_entry = rio_std_route_get_entry; - rdev->rswitch->clr_table = rio_std_route_clr_table; - } - - if (!rdev->rswitch->add_entry || !rdev->rswitch->get_entry) - printk(KERN_ERR "RIO: missing routing ops for %s\n", - rio_name(rdev)); -} -EXPORT_SYMBOL_GPL(rio_switch_init); - /** * rio_enable_rx_tx_port - enable input receiver and output transmitter of * given port @@ -970,8 +931,8 @@ int rio_inb_pwrite_handler(union rio_pw_msg *pw_msg) /* * Process the port-write notification from switch */ - if (rdev->rswitch->em_handle) - rdev->rswitch->em_handle(rdev, portnum); + if (rdev->rswitch->ops && rdev->rswitch->ops->em_handle) + rdev->rswitch->ops->em_handle(rdev, portnum); rio_read_config_32(rdev, rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(portnum), @@ -1207,8 +1168,9 @@ struct rio_dev *rio_get_device(u16 vid, u16 did, struct rio_dev *from) * @route_destid: destID entry in the RT * @route_port: destination port for specified destID */ -int rio_std_route_add_entry(struct rio_mport *mport, u16 destid, u8 hopcount, - u16 table, u16 route_destid, u8 route_port) +static int +rio_std_route_add_entry(struct rio_mport *mport, u16 destid, u8 hopcount, + u16 table, u16 route_destid, u8 route_port) { if (table == RIO_GLOBAL_TABLE) { rio_mport_write_config_32(mport, destid, hopcount, @@ -1234,8 +1196,9 @@ int rio_std_route_add_entry(struct rio_mport *mport, u16 destid, u8 hopcount, * @route_destid: destID entry in the RT * @route_port: returned destination port for specified destID */ -int rio_std_route_get_entry(struct rio_mport *mport, u16 destid, u8 hopcount, - u16 table, u16 route_destid, u8 *route_port) +static int +rio_std_route_get_entry(struct rio_mport *mport, u16 destid, u8 hopcount, + u16 table, u16 route_destid, u8 *route_port) { u32 result; @@ -1259,8 +1222,9 @@ int rio_std_route_get_entry(struct rio_mport *mport, u16 destid, u8 hopcount, * @hopcount: Number of switch hops to the device * @table: routing table ID (global or port-specific) */ -int rio_std_route_clr_table(struct rio_mport *mport, u16 destid, u8 hopcount, - u16 table) +static int +rio_std_route_clr_table(struct rio_mport *mport, u16 destid, u8 hopcount, + u16 table) { u32 max_destid = 0xff; u32 i, pef, id_inc = 1, ext_cfg = 0; @@ -1301,6 +1265,234 @@ int rio_std_route_clr_table(struct rio_mport *mport, u16 destid, u8 hopcount, return 0; } +/** + * rio_lock_device - Acquires host device lock for specified device + * @port: Master port to send transaction + * @destid: Destination ID for device/switch + * @hopcount: Hopcount to reach switch + * @wait_ms: Max wait time in msec (0 = no timeout) + * + * Attepts to acquire host device lock for specified device + * Returns 0 if device lock acquired or EINVAL if timeout expires. + */ +int rio_lock_device(struct rio_mport *port, u16 destid, + u8 hopcount, int wait_ms) +{ + u32 result; + int tcnt = 0; + + /* Attempt to acquire device lock */ + rio_mport_write_config_32(port, destid, hopcount, + RIO_HOST_DID_LOCK_CSR, port->host_deviceid); + rio_mport_read_config_32(port, destid, hopcount, + RIO_HOST_DID_LOCK_CSR, &result); + + while (result != port->host_deviceid) { + if (wait_ms != 0 && tcnt == wait_ms) { + pr_debug("RIO: timeout when locking device %x:%x\n", + destid, hopcount); + return -EINVAL; + } + + /* Delay a bit */ + mdelay(1); + tcnt++; + /* Try to acquire device lock again */ + rio_mport_write_config_32(port, destid, + hopcount, + RIO_HOST_DID_LOCK_CSR, + port->host_deviceid); + rio_mport_read_config_32(port, destid, + hopcount, + RIO_HOST_DID_LOCK_CSR, &result); + } + + return 0; +} +EXPORT_SYMBOL_GPL(rio_lock_device); + +/** + * rio_unlock_device - Releases host device lock for specified device + * @port: Master port to send transaction + * @destid: Destination ID for device/switch + * @hopcount: Hopcount to reach switch + * + * Returns 0 if device lock released or EINVAL if fails. + */ +int rio_unlock_device(struct rio_mport *port, u16 destid, u8 hopcount) +{ + u32 result; + + /* Release device lock */ + rio_mport_write_config_32(port, destid, + hopcount, + RIO_HOST_DID_LOCK_CSR, + port->host_deviceid); + rio_mport_read_config_32(port, destid, hopcount, + RIO_HOST_DID_LOCK_CSR, &result); + if ((result & 0xffff) != 0xffff) { + pr_debug("RIO: badness when releasing device lock %x:%x\n", + destid, hopcount); + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(rio_unlock_device); + +/** + * rio_route_add_entry- Add a route entry to a switch routing table + * @rdev: RIO device + * @table: Routing table ID + * @route_destid: Destination ID to be routed + * @route_port: Port number to be routed + * @lock: apply a hardware lock on switch device flag (1=lock, 0=no_lock) + * + * If available calls the switch specific add_entry() method to add a route + * entry into a switch routing table. Otherwise uses standard RT update method + * as defined by RapidIO specification. A specific routing table can be selected + * using the @table argument if a switch has per port routing tables or + * the standard (or global) table may be used by passing + * %RIO_GLOBAL_TABLE in @table. + * + * Returns %0 on success or %-EINVAL on failure. + */ +int rio_route_add_entry(struct rio_dev *rdev, + u16 table, u16 route_destid, u8 route_port, int lock) +{ + int rc = -EINVAL; + struct rio_switch_ops *ops = rdev->rswitch->ops; + + if (lock) { + rc = rio_lock_device(rdev->net->hport, rdev->destid, + rdev->hopcount, 1000); + if (rc) + return rc; + } + + spin_lock(&rdev->rswitch->lock); + + if (ops == NULL || ops->add_entry == NULL) { + rc = rio_std_route_add_entry(rdev->net->hport, rdev->destid, + rdev->hopcount, table, + route_destid, route_port); + } else if (try_module_get(ops->owner)) { + rc = ops->add_entry(rdev->net->hport, rdev->destid, + rdev->hopcount, table, route_destid, + route_port); + module_put(ops->owner); + } + + spin_unlock(&rdev->rswitch->lock); + + if (lock) + rio_unlock_device(rdev->net->hport, rdev->destid, + rdev->hopcount); + + return rc; +} +EXPORT_SYMBOL_GPL(rio_route_add_entry); + +/** + * rio_route_get_entry- Read an entry from a switch routing table + * @rdev: RIO device + * @table: Routing table ID + * @route_destid: Destination ID to be routed + * @route_port: Pointer to read port number into + * @lock: apply a hardware lock on switch device flag (1=lock, 0=no_lock) + * + * If available calls the switch specific get_entry() method to fetch a route + * entry from a switch routing table. Otherwise uses standard RT read method + * as defined by RapidIO specification. A specific routing table can be selected + * using the @table argument if a switch has per port routing tables or + * the standard (or global) table may be used by passing + * %RIO_GLOBAL_TABLE in @table. + * + * Returns %0 on success or %-EINVAL on failure. + */ +int rio_route_get_entry(struct rio_dev *rdev, u16 table, + u16 route_destid, u8 *route_port, int lock) +{ + int rc = -EINVAL; + struct rio_switch_ops *ops = rdev->rswitch->ops; + + if (lock) { + rc = rio_lock_device(rdev->net->hport, rdev->destid, + rdev->hopcount, 1000); + if (rc) + return rc; + } + + spin_lock(&rdev->rswitch->lock); + + if (ops == NULL || ops->get_entry == NULL) { + rc = rio_std_route_get_entry(rdev->net->hport, rdev->destid, + rdev->hopcount, table, + route_destid, route_port); + } else if (try_module_get(ops->owner)) { + rc = ops->get_entry(rdev->net->hport, rdev->destid, + rdev->hopcount, table, route_destid, + route_port); + module_put(ops->owner); + } + + spin_unlock(&rdev->rswitch->lock); + + if (lock) + rio_unlock_device(rdev->net->hport, rdev->destid, + rdev->hopcount); + return rc; +} +EXPORT_SYMBOL_GPL(rio_route_get_entry); + +/** + * rio_route_clr_table - Clear a switch routing table + * @rdev: RIO device + * @table: Routing table ID + * @lock: apply a hardware lock on switch device flag (1=lock, 0=no_lock) + * + * If available calls the switch specific clr_table() method to clear a switch + * routing table. Otherwise uses standard RT write method as defined by RapidIO + * specification. A specific routing table can be selected using the @table + * argument if a switch has per port routing tables or the standard (or global) + * table may be used by passing %RIO_GLOBAL_TABLE in @table. + * + * Returns %0 on success or %-EINVAL on failure. + */ +int rio_route_clr_table(struct rio_dev *rdev, u16 table, int lock) +{ + int rc = -EINVAL; + struct rio_switch_ops *ops = rdev->rswitch->ops; + + if (lock) { + rc = rio_lock_device(rdev->net->hport, rdev->destid, + rdev->hopcount, 1000); + if (rc) + return rc; + } + + spin_lock(&rdev->rswitch->lock); + + if (ops == NULL || ops->clr_table == NULL) { + rc = rio_std_route_clr_table(rdev->net->hport, rdev->destid, + rdev->hopcount, table); + } else if (try_module_get(ops->owner)) { + rc = ops->clr_table(rdev->net->hport, rdev->destid, + rdev->hopcount, table); + + module_put(ops->owner); + } + + spin_unlock(&rdev->rswitch->lock); + + if (lock) + rio_unlock_device(rdev->net->hport, rdev->destid, + rdev->hopcount); + + return rc; +} +EXPORT_SYMBOL_GPL(rio_route_clr_table); + #ifdef CONFIG_RAPIDIO_DMA_ENGINE static bool rio_chan_filter(struct dma_chan *chan, void *arg) diff --git a/drivers/rapidio/rio.h b/drivers/rapidio/rio.h index c14f864dea5c..d59587762c76 100644 --- a/drivers/rapidio/rio.h +++ b/drivers/rapidio/rio.h @@ -28,18 +28,17 @@ extern u32 rio_mport_get_efb(struct rio_mport *port, int local, u16 destid, extern int rio_mport_chk_dev_access(struct rio_mport *mport, u16 destid, u8 hopcount); extern int rio_create_sysfs_dev_files(struct rio_dev *rdev); -extern int rio_std_route_add_entry(struct rio_mport *mport, u16 destid, - u8 hopcount, u16 table, u16 route_destid, - u8 route_port); -extern int rio_std_route_get_entry(struct rio_mport *mport, u16 destid, - u8 hopcount, u16 table, u16 route_destid, - u8 *route_port); -extern int rio_std_route_clr_table(struct rio_mport *mport, u16 destid, - u8 hopcount, u16 table); +extern int rio_lock_device(struct rio_mport *port, u16 destid, + u8 hopcount, int wait_ms); +extern int rio_unlock_device(struct rio_mport *port, u16 destid, u8 hopcount); +extern int rio_route_add_entry(struct rio_dev *rdev, + u16 table, u16 route_destid, u8 route_port, int lock); +extern int rio_route_get_entry(struct rio_dev *rdev, u16 table, + u16 route_destid, u8 *route_port, int lock); +extern int rio_route_clr_table(struct rio_dev *rdev, u16 table, int lock); extern int rio_set_port_lockout(struct rio_dev *rdev, u32 pnum, int lock); extern struct rio_dev *rio_get_comptag(u32 comp_tag, struct rio_dev *from); extern int rio_add_device(struct rio_dev *rdev); -extern void rio_switch_init(struct rio_dev *rdev, int do_enum); extern int rio_enable_rx_tx_port(struct rio_mport *port, int local, u16 destid, u8 hopcount, u8 port_num); extern int rio_register_scan(int mport_id, struct rio_scan *scan_ops); @@ -51,29 +50,5 @@ extern struct rio_mport *rio_find_mport(int mport_id); extern struct device_attribute rio_dev_attrs[]; extern struct bus_attribute rio_bus_attrs[]; -extern struct rio_switch_ops __start_rio_switch_ops[]; -extern struct rio_switch_ops __end_rio_switch_ops[]; - -/* Helpers internal to the RIO core code */ -#define DECLARE_RIO_SWITCH_SECTION(section, name, vid, did, init_hook) \ - static const struct rio_switch_ops __rio_switch_##name __used \ - __section(section) = { vid, did, init_hook }; - -/** - * DECLARE_RIO_SWITCH_INIT - Registers switch initialization routine - * @vid: RIO vendor ID - * @did: RIO device ID - * @init_hook: Callback that performs switch-specific initialization - * - * Manipulating switch route tables and error management in RIO - * is switch specific. This registers a switch by vendor and device ID with - * initialization callback for setting up switch operations and (if required) - * hardware initialization. A &struct rio_switch_ops is initialized with - * pointer to the init routine and placed into a RIO-specific kernel section. - */ -#define DECLARE_RIO_SWITCH_INIT(vid, did, init_hook) \ - DECLARE_RIO_SWITCH_SECTION(.rio_switch_ops, vid##did, \ - vid, did, init_hook) - #define RIO_GET_DID(size, x) (size ? (x & 0xffff) : ((x & 0x00ff0000) >> 16)) #define RIO_SET_DID(size, x) (size ? (x & 0xffff) : ((x & 0x000000ff) << 16)) diff --git a/drivers/rapidio/switches/Kconfig b/drivers/rapidio/switches/Kconfig index 62d4a065230f..345841562f95 100644 --- a/drivers/rapidio/switches/Kconfig +++ b/drivers/rapidio/switches/Kconfig @@ -2,27 +2,23 @@ # RapidIO switches configuration # config RAPIDIO_TSI57X - bool "IDT Tsi57x SRIO switches support" - depends on RAPIDIO + tristate "IDT Tsi57x SRIO switches support" ---help--- Includes support for IDT Tsi57x family of serial RapidIO switches. config RAPIDIO_CPS_XX - bool "IDT CPS-xx SRIO switches support" - depends on RAPIDIO + tristate "IDT CPS-xx SRIO switches support" ---help--- Includes support for IDT CPS-16/12/10/8 serial RapidIO switches. config RAPIDIO_TSI568 - bool "Tsi568 SRIO switch support" - depends on RAPIDIO + tristate "Tsi568 SRIO switch support" default n ---help--- Includes support for IDT Tsi568 serial RapidIO switch. config RAPIDIO_CPS_GEN2 - bool "IDT CPS Gen.2 SRIO switch support" - depends on RAPIDIO + tristate "IDT CPS Gen.2 SRIO switch support" default n ---help--- Includes support for ITD CPS Gen.2 serial RapidIO switches. diff --git a/drivers/rapidio/switches/idt_gen2.c b/drivers/rapidio/switches/idt_gen2.c index 809b7a3336ba..00a71ebb5cac 100644 --- a/drivers/rapidio/switches/idt_gen2.c +++ b/drivers/rapidio/switches/idt_gen2.c @@ -11,6 +11,7 @@ */ #include +#include #include #include #include @@ -387,12 +388,12 @@ idtg2_show_errlog(struct device *dev, struct device_attribute *attr, char *buf) static DEVICE_ATTR(errlog, S_IRUGO, idtg2_show_errlog, NULL); -static int idtg2_sysfs(struct rio_dev *rdev, int create) +static int idtg2_sysfs(struct rio_dev *rdev, bool create) { struct device *dev = &rdev->dev; int err = 0; - if (create == RIO_SW_SYSFS_CREATE) { + if (create) { /* Initialize sysfs entries */ err = device_create_file(dev, &dev_attr_errlog); if (err) @@ -403,29 +404,90 @@ static int idtg2_sysfs(struct rio_dev *rdev, int create) return err; } -static int idtg2_switch_init(struct rio_dev *rdev, int do_enum) +static struct rio_switch_ops idtg2_switch_ops = { + .owner = THIS_MODULE, + .add_entry = idtg2_route_add_entry, + .get_entry = idtg2_route_get_entry, + .clr_table = idtg2_route_clr_table, + .set_domain = idtg2_set_domain, + .get_domain = idtg2_get_domain, + .em_init = idtg2_em_init, + .em_handle = idtg2_em_handler, +}; + +static int idtg2_probe(struct rio_dev *rdev, const struct rio_device_id *id) { pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev)); - rdev->rswitch->add_entry = idtg2_route_add_entry; - rdev->rswitch->get_entry = idtg2_route_get_entry; - rdev->rswitch->clr_table = idtg2_route_clr_table; - rdev->rswitch->set_domain = idtg2_set_domain; - rdev->rswitch->get_domain = idtg2_get_domain; - rdev->rswitch->em_init = idtg2_em_init; - rdev->rswitch->em_handle = idtg2_em_handler; - rdev->rswitch->sw_sysfs = idtg2_sysfs; - - if (do_enum) { + + spin_lock(&rdev->rswitch->lock); + + if (rdev->rswitch->ops) { + spin_unlock(&rdev->rswitch->lock); + return -EINVAL; + } + + rdev->rswitch->ops = &idtg2_switch_ops; + + if (rdev->do_enum) { /* Ensure that default routing is disabled on startup */ rio_write_config_32(rdev, RIO_STD_RTE_DEFAULT_PORT, IDT_NO_ROUTE); } + /* Create device-specific sysfs attributes */ + idtg2_sysfs(rdev, true); + + spin_unlock(&rdev->rswitch->lock); return 0; } -DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTCPS1848, idtg2_switch_init); -DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTCPS1616, idtg2_switch_init); -DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTVPS1616, idtg2_switch_init); -DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTSPS1616, idtg2_switch_init); -DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTCPS1432, idtg2_switch_init); +static void idtg2_remove(struct rio_dev *rdev) +{ + pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev)); + spin_lock(&rdev->rswitch->lock); + if (rdev->rswitch->ops != &idtg2_switch_ops) { + spin_unlock(&rdev->rswitch->lock); + return; + } + rdev->rswitch->ops = NULL; + + /* Remove device-specific sysfs attributes */ + idtg2_sysfs(rdev, false); + + spin_unlock(&rdev->rswitch->lock); +} + +static struct rio_device_id idtg2_id_table[] = { + {RIO_DEVICE(RIO_DID_IDTCPS1848, RIO_VID_IDT)}, + {RIO_DEVICE(RIO_DID_IDTCPS1616, RIO_VID_IDT)}, + {RIO_DEVICE(RIO_DID_IDTVPS1616, RIO_VID_IDT)}, + {RIO_DEVICE(RIO_DID_IDTSPS1616, RIO_VID_IDT)}, + {RIO_DEVICE(RIO_DID_IDTCPS1432, RIO_VID_IDT)}, + { 0, } /* terminate list */ +}; + +static struct rio_driver idtg2_driver = { + .name = "idt_gen2", + .id_table = idtg2_id_table, + .probe = idtg2_probe, + .remove = idtg2_remove, +}; + +static int __init idtg2_init(void) +{ + return rio_register_driver(&idtg2_driver); +} + +static void __exit idtg2_exit(void) +{ + pr_debug("RIO: %s\n", __func__); + rio_unregister_driver(&idtg2_driver); + pr_debug("RIO: %s done\n", __func__); +} + +device_initcall(idtg2_init); +module_exit(idtg2_exit); + +MODULE_DESCRIPTION("IDT CPS Gen.2 Serial RapidIO switch family driver"); +MODULE_AUTHOR("Integrated Device Technology, Inc."); +MODULE_LICENSE("GPL"); diff --git a/drivers/rapidio/switches/idtcps.c b/drivers/rapidio/switches/idtcps.c index d06ee2d44b44..7fbb60d31796 100644 --- a/drivers/rapidio/switches/idtcps.c +++ b/drivers/rapidio/switches/idtcps.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "../rio.h" #define CPS_DEFAULT_ROUTE 0xde @@ -118,18 +119,31 @@ idtcps_get_domain(struct rio_mport *mport, u16 destid, u8 hopcount, return 0; } -static int idtcps_switch_init(struct rio_dev *rdev, int do_enum) +static struct rio_switch_ops idtcps_switch_ops = { + .owner = THIS_MODULE, + .add_entry = idtcps_route_add_entry, + .get_entry = idtcps_route_get_entry, + .clr_table = idtcps_route_clr_table, + .set_domain = idtcps_set_domain, + .get_domain = idtcps_get_domain, + .em_init = NULL, + .em_handle = NULL, +}; + +static int idtcps_probe(struct rio_dev *rdev, const struct rio_device_id *id) { pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev)); - rdev->rswitch->add_entry = idtcps_route_add_entry; - rdev->rswitch->get_entry = idtcps_route_get_entry; - rdev->rswitch->clr_table = idtcps_route_clr_table; - rdev->rswitch->set_domain = idtcps_set_domain; - rdev->rswitch->get_domain = idtcps_get_domain; - rdev->rswitch->em_init = NULL; - rdev->rswitch->em_handle = NULL; - - if (do_enum) { + + spin_lock(&rdev->rswitch->lock); + + if (rdev->rswitch->ops) { + spin_unlock(&rdev->rswitch->lock); + return -EINVAL; + } + + rdev->rswitch->ops = &idtcps_switch_ops; + + if (rdev->do_enum) { /* set TVAL = ~50us */ rio_write_config_32(rdev, rdev->phys_efptr + RIO_PORT_LINKTO_CTL_CSR, 0x8e << 8); @@ -138,12 +152,52 @@ static int idtcps_switch_init(struct rio_dev *rdev, int do_enum) RIO_STD_RTE_DEFAULT_PORT, CPS_NO_ROUTE); } + spin_unlock(&rdev->rswitch->lock); return 0; } -DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTCPS6Q, idtcps_switch_init); -DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTCPS8, idtcps_switch_init); -DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTCPS10Q, idtcps_switch_init); -DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTCPS12, idtcps_switch_init); -DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDTCPS16, idtcps_switch_init); -DECLARE_RIO_SWITCH_INIT(RIO_VID_IDT, RIO_DID_IDT70K200, idtcps_switch_init); +static void idtcps_remove(struct rio_dev *rdev) +{ + pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev)); + spin_lock(&rdev->rswitch->lock); + if (rdev->rswitch->ops != &idtcps_switch_ops) { + spin_unlock(&rdev->rswitch->lock); + return; + } + rdev->rswitch->ops = NULL; + spin_unlock(&rdev->rswitch->lock); +} + +static struct rio_device_id idtcps_id_table[] = { + {RIO_DEVICE(RIO_DID_IDTCPS6Q, RIO_VID_IDT)}, + {RIO_DEVICE(RIO_DID_IDTCPS8, RIO_VID_IDT)}, + {RIO_DEVICE(RIO_DID_IDTCPS10Q, RIO_VID_IDT)}, + {RIO_DEVICE(RIO_DID_IDTCPS12, RIO_VID_IDT)}, + {RIO_DEVICE(RIO_DID_IDTCPS16, RIO_VID_IDT)}, + {RIO_DEVICE(RIO_DID_IDT70K200, RIO_VID_IDT)}, + { 0, } /* terminate list */ +}; + +static struct rio_driver idtcps_driver = { + .name = "idtcps", + .id_table = idtcps_id_table, + .probe = idtcps_probe, + .remove = idtcps_remove, +}; + +static int __init idtcps_init(void) +{ + return rio_register_driver(&idtcps_driver); +} + +static void __exit idtcps_exit(void) +{ + rio_unregister_driver(&idtcps_driver); +} + +device_initcall(idtcps_init); +module_exit(idtcps_exit); + +MODULE_DESCRIPTION("IDT CPS Gen.1 Serial RapidIO switch family driver"); +MODULE_AUTHOR("Integrated Device Technology, Inc."); +MODULE_LICENSE("GPL"); diff --git a/drivers/rapidio/switches/tsi568.c b/drivers/rapidio/switches/tsi568.c index 3994c00aa01f..8a43561b9d17 100644 --- a/drivers/rapidio/switches/tsi568.c +++ b/drivers/rapidio/switches/tsi568.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "../rio.h" /* Global (broadcast) route registers */ @@ -129,18 +130,70 @@ tsi568_em_init(struct rio_dev *rdev) return 0; } -static int tsi568_switch_init(struct rio_dev *rdev, int do_enum) +static struct rio_switch_ops tsi568_switch_ops = { + .owner = THIS_MODULE, + .add_entry = tsi568_route_add_entry, + .get_entry = tsi568_route_get_entry, + .clr_table = tsi568_route_clr_table, + .set_domain = NULL, + .get_domain = NULL, + .em_init = tsi568_em_init, + .em_handle = NULL, +}; + +static int tsi568_probe(struct rio_dev *rdev, const struct rio_device_id *id) { pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev)); - rdev->rswitch->add_entry = tsi568_route_add_entry; - rdev->rswitch->get_entry = tsi568_route_get_entry; - rdev->rswitch->clr_table = tsi568_route_clr_table; - rdev->rswitch->set_domain = NULL; - rdev->rswitch->get_domain = NULL; - rdev->rswitch->em_init = tsi568_em_init; - rdev->rswitch->em_handle = NULL; + spin_lock(&rdev->rswitch->lock); + + if (rdev->rswitch->ops) { + spin_unlock(&rdev->rswitch->lock); + return -EINVAL; + } + + rdev->rswitch->ops = &tsi568_switch_ops; + spin_unlock(&rdev->rswitch->lock); return 0; } -DECLARE_RIO_SWITCH_INIT(RIO_VID_TUNDRA, RIO_DID_TSI568, tsi568_switch_init); +static void tsi568_remove(struct rio_dev *rdev) +{ + pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev)); + spin_lock(&rdev->rswitch->lock); + if (rdev->rswitch->ops != &tsi568_switch_ops) { + spin_unlock(&rdev->rswitch->lock); + return; + } + rdev->rswitch->ops = NULL; + spin_unlock(&rdev->rswitch->lock); +} + +static struct rio_device_id tsi568_id_table[] = { + {RIO_DEVICE(RIO_DID_TSI568, RIO_VID_TUNDRA)}, + { 0, } /* terminate list */ +}; + +static struct rio_driver tsi568_driver = { + .name = "tsi568", + .id_table = tsi568_id_table, + .probe = tsi568_probe, + .remove = tsi568_remove, +}; + +static int __init tsi568_init(void) +{ + return rio_register_driver(&tsi568_driver); +} + +static void __exit tsi568_exit(void) +{ + rio_unregister_driver(&tsi568_driver); +} + +device_initcall(tsi568_init); +module_exit(tsi568_exit); + +MODULE_DESCRIPTION("IDT Tsi568 Serial RapidIO switch driver"); +MODULE_AUTHOR("Integrated Device Technology, Inc."); +MODULE_LICENSE("GPL"); diff --git a/drivers/rapidio/switches/tsi57x.c b/drivers/rapidio/switches/tsi57x.c index db8b8028988d..42c8b014fe15 100644 --- a/drivers/rapidio/switches/tsi57x.c +++ b/drivers/rapidio/switches/tsi57x.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "../rio.h" /* Global (broadcast) route registers */ @@ -292,27 +293,79 @@ exit_es: return 0; } -static int tsi57x_switch_init(struct rio_dev *rdev, int do_enum) +static struct rio_switch_ops tsi57x_switch_ops = { + .owner = THIS_MODULE, + .add_entry = tsi57x_route_add_entry, + .get_entry = tsi57x_route_get_entry, + .clr_table = tsi57x_route_clr_table, + .set_domain = tsi57x_set_domain, + .get_domain = tsi57x_get_domain, + .em_init = tsi57x_em_init, + .em_handle = tsi57x_em_handler, +}; + +static int tsi57x_probe(struct rio_dev *rdev, const struct rio_device_id *id) { pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev)); - rdev->rswitch->add_entry = tsi57x_route_add_entry; - rdev->rswitch->get_entry = tsi57x_route_get_entry; - rdev->rswitch->clr_table = tsi57x_route_clr_table; - rdev->rswitch->set_domain = tsi57x_set_domain; - rdev->rswitch->get_domain = tsi57x_get_domain; - rdev->rswitch->em_init = tsi57x_em_init; - rdev->rswitch->em_handle = tsi57x_em_handler; - - if (do_enum) { + + spin_lock(&rdev->rswitch->lock); + + if (rdev->rswitch->ops) { + spin_unlock(&rdev->rswitch->lock); + return -EINVAL; + } + rdev->rswitch->ops = &tsi57x_switch_ops; + + if (rdev->do_enum) { /* Ensure that default routing is disabled on startup */ rio_write_config_32(rdev, RIO_STD_RTE_DEFAULT_PORT, RIO_INVALID_ROUTE); } + spin_unlock(&rdev->rswitch->lock); return 0; } -DECLARE_RIO_SWITCH_INIT(RIO_VID_TUNDRA, RIO_DID_TSI572, tsi57x_switch_init); -DECLARE_RIO_SWITCH_INIT(RIO_VID_TUNDRA, RIO_DID_TSI574, tsi57x_switch_init); -DECLARE_RIO_SWITCH_INIT(RIO_VID_TUNDRA, RIO_DID_TSI577, tsi57x_switch_init); -DECLARE_RIO_SWITCH_INIT(RIO_VID_TUNDRA, RIO_DID_TSI578, tsi57x_switch_init); +static void tsi57x_remove(struct rio_dev *rdev) +{ + pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev)); + spin_lock(&rdev->rswitch->lock); + if (rdev->rswitch->ops != &tsi57x_switch_ops) { + spin_unlock(&rdev->rswitch->lock); + return; + } + rdev->rswitch->ops = NULL; + spin_unlock(&rdev->rswitch->lock); +} + +static struct rio_device_id tsi57x_id_table[] = { + {RIO_DEVICE(RIO_DID_TSI572, RIO_VID_TUNDRA)}, + {RIO_DEVICE(RIO_DID_TSI574, RIO_VID_TUNDRA)}, + {RIO_DEVICE(RIO_DID_TSI577, RIO_VID_TUNDRA)}, + {RIO_DEVICE(RIO_DID_TSI578, RIO_VID_TUNDRA)}, + { 0, } /* terminate list */ +}; + +static struct rio_driver tsi57x_driver = { + .name = "tsi57x", + .id_table = tsi57x_id_table, + .probe = tsi57x_probe, + .remove = tsi57x_remove, +}; + +static int __init tsi57x_init(void) +{ + return rio_register_driver(&tsi57x_driver); +} + +static void __exit tsi57x_exit(void) +{ + rio_unregister_driver(&tsi57x_driver); +} + +device_initcall(tsi57x_init); +module_exit(tsi57x_exit); + +MODULE_DESCRIPTION("IDT Tsi57x Serial RapidIO switch family driver"); +MODULE_AUTHOR("Integrated Device Technology, Inc."); +MODULE_LICENSE("GPL"); diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 4f2737208c42..c74d88baea60 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -275,13 +275,6 @@ VMLINUX_SYMBOL(__end_builtin_fw) = .; \ } \ \ - /* RapidIO route ops */ \ - .rio_ops : AT(ADDR(.rio_ops) - LOAD_OFFSET) { \ - VMLINUX_SYMBOL(__start_rio_switch_ops) = .; \ - *(.rio_switch_ops) \ - VMLINUX_SYMBOL(__end_rio_switch_ops) = .; \ - } \ - \ TRACEDATA \ \ /* Kernel symbol table: Normal symbols */ \ diff --git a/include/linux/rio.h b/include/linux/rio.h index 18e099342e6f..fcd492e7aff4 100644 --- a/include/linux/rio.h +++ b/include/linux/rio.h @@ -94,6 +94,23 @@ union rio_pw_msg; * @switchid: Switch ID that is unique across a network * @route_table: Copy of switch routing table * @port_ok: Status of each port (one bit per port) - OK=1 or UNINIT=0 + * @ops: pointer to switch-specific operations + * @lock: lock to serialize operations updates + * @nextdev: Array of per-port pointers to the next attached device + */ +struct rio_switch { + struct list_head node; + u16 switchid; + u8 *route_table; + u32 port_ok; + struct rio_switch_ops *ops; + spinlock_t lock; + struct rio_dev *nextdev[0]; +}; + +/** + * struct rio_switch_ops - Per-switch operations + * @owner: The module owner of this structure * @add_entry: Callback for switch-specific route add function * @get_entry: Callback for switch-specific route get function * @clr_table: Callback for switch-specific clear route table function @@ -101,14 +118,12 @@ union rio_pw_msg; * @get_domain: Callback for switch-specific domain get function * @em_init: Callback for switch-specific error management init function * @em_handle: Callback for switch-specific error management handler function - * @sw_sysfs: Callback that initializes switch-specific sysfs attributes - * @nextdev: Array of per-port pointers to the next attached device + * + * Defines the operations that are necessary to initialize/control + * a particular RIO switch device. */ -struct rio_switch { - struct list_head node; - u16 switchid; - u8 *route_table; - u32 port_ok; +struct rio_switch_ops { + struct module *owner; int (*add_entry) (struct rio_mport *mport, u16 destid, u8 hopcount, u16 table, u16 route_destid, u8 route_port); int (*get_entry) (struct rio_mport *mport, u16 destid, u8 hopcount, @@ -121,8 +136,6 @@ struct rio_switch { u8 *sw_domain); int (*em_init) (struct rio_dev *dev); int (*em_handle) (struct rio_dev *dev, u8 swport); - int (*sw_sysfs) (struct rio_dev *dev, int create); - struct rio_dev *nextdev[0]; }; /** @@ -130,6 +143,7 @@ struct rio_switch { * @global_list: Node in list of all RIO devices * @net_list: Node in list of RIO devices in a network * @net: Network this device is a part of + * @do_enum: Enumeration flag * @did: Device ID * @vid: Vendor ID * @device_rev: Device revision @@ -158,6 +172,7 @@ struct rio_dev { struct list_head global_list; /* node in list of all RIO devices */ struct list_head net_list; /* node in per net list */ struct rio_net *net; /* RIO net this device resides in */ + bool do_enum; u16 did; u16 vid; u32 device_rev; @@ -297,10 +312,6 @@ struct rio_net { struct rio_id_table destid_table; /* destID allocation table */ }; -/* Definitions used by switch sysfs initialization callback */ -#define RIO_SW_SYSFS_CREATE 1 /* Create switch attributes */ -#define RIO_SW_SYSFS_REMOVE 0 /* Remove switch attributes */ - /* Low-level architecture-dependent routines */ /** @@ -400,20 +411,6 @@ struct rio_device_id { u16 asm_did, asm_vid; }; -/** - * struct rio_switch_ops - Per-switch operations - * @vid: RIO vendor ID - * @did: RIO device ID - * @init_hook: Callback that performs switch device initialization - * - * Defines the operations that are necessary to initialize/control - * a particular RIO switch device. - */ -struct rio_switch_ops { - u16 vid, did; - int (*init_hook) (struct rio_dev *rdev, int do_enum); -}; - union rio_pw_msg { struct { u32 comptag; /* Component Tag CSR */ -- cgit v1.2.3 From e6161d64263ee7a903acdde1a8ab7d4221d5512f Mon Sep 17 00:00:00 2001 From: Alexandre Bounine Date: Wed, 3 Jul 2013 15:08:52 -0700 Subject: rapidio/rionet: rework driver initialization and removal Rework probe/remove routines to prevent rionet driver from monopolizing target RapidIO devices. Fix conflict with modular RapidIO switch drivers. Using one of RapidIO messaging channels rionet driver provides a service layer common to all endpoint devices in a system's RapidIO network. These devices may also require their own specific device driver which will be blocked from attaching to the target device by rionet (or block rionet if loaded earlier). To avoid conflict with device-specific drivers, the rionet driver is reworked to be registered as a subsystem interface on the RapidIO bus. The reworked rio_remove_dev() and rionet_exit() routines also include handling of individual rionet peer device removal which was not supported before. Signed-off-by: Alexandre Bounine Cc: Matt Porter Cc: Li Yang Cc: Kumar Gala Cc: "David S. Miller" Cc: Andre van Herk Cc: Micha Nelissen Cc: Stef van Os Cc: Jean Delvare Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/net/rionet.c | 103 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 78 insertions(+), 25 deletions(-) diff --git a/drivers/net/rionet.c b/drivers/net/rionet.c index f433b594388e..6d1f6ed3113f 100644 --- a/drivers/net/rionet.c +++ b/drivers/net/rionet.c @@ -208,6 +208,17 @@ static int rionet_start_xmit(struct sk_buff *skb, struct net_device *ndev) if (nets[rnet->mport->id].active[destid]) rionet_queue_tx_msg(skb, ndev, nets[rnet->mport->id].active[destid]); + else { + /* + * If the target device was removed from the list of + * active peers but we still have TX packets targeting + * it just report sending a packet to the target + * (without actual packet transfer). + */ + dev_kfree_skb_any(skb); + ndev->stats.tx_packets++; + ndev->stats.tx_bytes += skb->len; + } } spin_unlock_irqrestore(&rnet->tx_lock, flags); @@ -385,24 +396,28 @@ static int rionet_close(struct net_device *ndev) return 0; } -static void rionet_remove(struct rio_dev *rdev) +static int rionet_remove_dev(struct device *dev, struct subsys_interface *sif) { - struct net_device *ndev = rio_get_drvdata(rdev); + struct rio_dev *rdev = to_rio_dev(dev); unsigned char netid = rdev->net->hport->id; struct rionet_peer *peer, *tmp; - unregister_netdev(ndev); - - free_pages((unsigned long)nets[netid].active, get_order(sizeof(void *) * - RIO_MAX_ROUTE_ENTRIES(rdev->net->hport->sys_size))); - nets[netid].active = NULL; + if (dev_rionet_capable(rdev)) { + list_for_each_entry_safe(peer, tmp, &nets[netid].peers, node) { + if (peer->rdev == rdev) { + if (nets[netid].active[rdev->destid]) { + nets[netid].active[rdev->destid] = NULL; + nets[netid].nact--; + } - list_for_each_entry_safe(peer, tmp, &nets[netid].peers, node) { - list_del(&peer->node); - kfree(peer); + list_del(&peer->node); + kfree(peer); + break; + } + } } - free_netdev(ndev); + return 0; } static void rionet_get_drvinfo(struct net_device *ndev, @@ -503,12 +518,13 @@ static int rionet_setup_netdev(struct rio_mport *mport, struct net_device *ndev) static unsigned long net_table[RIONET_MAX_NETS/sizeof(unsigned long) + 1]; -static int rionet_probe(struct rio_dev *rdev, const struct rio_device_id *id) +static int rionet_add_dev(struct device *dev, struct subsys_interface *sif) { int rc = -ENODEV; u32 lsrc_ops, ldst_ops; struct rionet_peer *peer; struct net_device *ndev = NULL; + struct rio_dev *rdev = to_rio_dev(dev); unsigned char netid = rdev->net->hport->id; int oldnet; @@ -518,8 +534,9 @@ static int rionet_probe(struct rio_dev *rdev, const struct rio_device_id *id) oldnet = test_and_set_bit(netid, net_table); /* - * First time through, make sure local device is rionet - * capable, setup netdev (will be skipped on later probes) + * If first time through this net, make sure local device is rionet + * capable and setup netdev (this step will be skipped in later probes + * on the same net). */ if (!oldnet) { rio_local_read_config_32(rdev->net->hport, RIO_SRC_OPS_CAR, @@ -541,6 +558,12 @@ static int rionet_probe(struct rio_dev *rdev, const struct rio_device_id *id) } nets[netid].ndev = ndev; rc = rionet_setup_netdev(rdev->net->hport, ndev); + if (rc) { + printk(KERN_ERR "%s: failed to setup netdev (rc=%d)\n", + DRV_NAME, rc); + goto out; + } + INIT_LIST_HEAD(&nets[netid].peers); nets[netid].nact = 0; } else if (nets[netid].ndev == NULL) @@ -559,31 +582,61 @@ static int rionet_probe(struct rio_dev *rdev, const struct rio_device_id *id) list_add_tail(&peer->node, &nets[netid].peers); } - rio_set_drvdata(rdev, nets[netid].ndev); - - out: + return 0; +out: return rc; } +#ifdef MODULE static struct rio_device_id rionet_id_table[] = { - {RIO_DEVICE(RIO_ANY_ID, RIO_ANY_ID)} + {RIO_DEVICE(RIO_ANY_ID, RIO_ANY_ID)}, + { 0, } /* terminate list */ }; -static struct rio_driver rionet_driver = { - .name = "rionet", - .id_table = rionet_id_table, - .probe = rionet_probe, - .remove = rionet_remove, +MODULE_DEVICE_TABLE(rapidio, rionet_id_table); +#endif + +static struct subsys_interface rionet_interface = { + .name = "rionet", + .subsys = &rio_bus_type, + .add_dev = rionet_add_dev, + .remove_dev = rionet_remove_dev, }; static int __init rionet_init(void) { - return rio_register_driver(&rionet_driver); + return subsys_interface_register(&rionet_interface); } static void __exit rionet_exit(void) { - rio_unregister_driver(&rionet_driver); + struct rionet_private *rnet; + struct net_device *ndev; + struct rionet_peer *peer, *tmp; + int i; + + for (i = 0; i < RIONET_MAX_NETS; i++) { + if (nets[i].ndev != NULL) { + ndev = nets[i].ndev; + rnet = netdev_priv(ndev); + unregister_netdev(ndev); + + list_for_each_entry_safe(peer, + tmp, &nets[i].peers, node) { + list_del(&peer->node); + kfree(peer); + } + + free_pages((unsigned long)nets[i].active, + get_order(sizeof(void *) * + RIO_MAX_ROUTE_ENTRIES(rnet->mport->sys_size))); + nets[i].active = NULL; + + free_netdev(ndev); + } + } + + subsys_interface_unregister(&rionet_interface); } late_initcall(rionet_init); -- cgit v1.2.3 From 9edbc30b434f56258d03faac5daf37a555384db3 Mon Sep 17 00:00:00 2001 From: Alexandre Bounine Date: Wed, 3 Jul 2013 15:08:53 -0700 Subject: rapidio: update enumerator registration mechanism Update enumeration/discovery method registration mechanism to allow loading enumeration/discovery methods before all mports are registered. Existing statically linked RapidIO subsystem expects that all available RapidIO mport devices are initialized and registered before the enumeration/discovery method is registered. Switching to loadable mport device drivers creates situation when mport device driver can be loaded after enumeration/discovery method is attached (e.g., loadable mport driver in a system with statically linked RapidIO core and enumerator). This also will happen in a system with hot-pluggable RapidIO controllers. To remove the dependency on the initialization/registration order this patch introduces enumeration/discovery registration mechanism that supports arbitrary registration order of mports and enumerator/discovery methods. The following registration rules are implemented: - only one enumeration/discovery method can be registered for given mport ID (including RIO_MPORT_ANY); - when new enumeration/discovery methods tries to attach to the registered mport device, method with matching mport ID will replace a default method previously registered for given mport (if any); - enumeration/discovery method with target ID=RIO_MPORT_ANY will be attached only to mports that do not have another enumerator attached to them; - when new mport device is registered with RapidIO subsystem, registration routine searches for the enumeration/discovery method with the best matching mport ID; Signed-off-by: Alexandre Bounine Cc: Matt Porter Cc: Li Yang Cc: Kumar Gala Cc: Andre van Herk Cc: Micha Nelissen Cc: Stef van Os Cc: Jean Delvare Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rapidio/rio-scan.c | 1 + drivers/rapidio/rio-sysfs.c | 17 +---- drivers/rapidio/rio.c | 176 ++++++++++++++++++++++++++++++++++++-------- drivers/rapidio/rio.h | 3 +- include/linux/rio.h | 15 ++++ 5 files changed, 164 insertions(+), 48 deletions(-) diff --git a/drivers/rapidio/rio-scan.c b/drivers/rapidio/rio-scan.c index 913950212605..ab837fdb2383 100644 --- a/drivers/rapidio/rio-scan.c +++ b/drivers/rapidio/rio-scan.c @@ -1162,6 +1162,7 @@ bail: } static struct rio_scan rio_scan_ops = { + .owner = THIS_MODULE, .enumerate = rio_enum_mport, .discover = rio_disc_mport, }; diff --git a/drivers/rapidio/rio-sysfs.c b/drivers/rapidio/rio-sysfs.c index 864e52f193e1..0c4473e54f86 100644 --- a/drivers/rapidio/rio-sysfs.c +++ b/drivers/rapidio/rio-sysfs.c @@ -286,7 +286,6 @@ static ssize_t bus_scan_store(struct bus_type *bus, const char *buf, size_t count) { long val; - struct rio_mport *port = NULL; int rc; if (kstrtol(buf, 0, &val) < 0) @@ -300,21 +299,7 @@ static ssize_t bus_scan_store(struct bus_type *bus, const char *buf, if (val < 0 || val >= RIO_MAX_MPORTS) return -EINVAL; - port = rio_find_mport((int)val); - - if (!port) { - pr_debug("RIO: %s: mport_%d not available\n", - __func__, (int)val); - return -EINVAL; - } - - if (!port->nscan) - return -EINVAL; - - if (port->host_deviceid >= 0) - rc = port->nscan->enumerate(port, 0); - else - rc = port->nscan->discover(port, RIO_SCAN_ENUM_NO_WAIT); + rc = rio_mport_scan((int)val); exit: if (!rc) rc = count; diff --git a/drivers/rapidio/rio.c b/drivers/rapidio/rio.c index b17d5218005e..5eb727cd0652 100644 --- a/drivers/rapidio/rio.c +++ b/drivers/rapidio/rio.c @@ -34,6 +34,7 @@ static LIST_HEAD(rio_devices); static DEFINE_SPINLOCK(rio_global_list_lock); static LIST_HEAD(rio_mports); +static LIST_HEAD(rio_scans); static DEFINE_MUTEX(rio_mport_list_lock); static unsigned char next_portid; static DEFINE_SPINLOCK(rio_mmap_lock); @@ -1602,34 +1603,73 @@ found: * rio_register_scan - enumeration/discovery method registration interface * @mport_id: mport device ID for which fabric scan routine has to be set * (RIO_MPORT_ANY = set for all available mports) - * @scan_ops: enumeration/discovery control structure + * @scan_ops: enumeration/discovery operations structure + * + * Registers enumeration/discovery operations with RapidIO subsystem and + * attaches it to the specified mport device (or all available mports + * if RIO_MPORT_ANY is specified). * - * Assigns enumeration or discovery method to the specified mport device (or all - * available mports if RIO_MPORT_ANY is specified). * Returns error if the mport already has an enumerator attached to it. - * In case of RIO_MPORT_ANY ignores ports with valid scan routines and returns - * an error if was unable to find at least one available mport. + * In case of RIO_MPORT_ANY skips mports with valid scan routines (no error). */ int rio_register_scan(int mport_id, struct rio_scan *scan_ops) { struct rio_mport *port; - int rc = -EBUSY; + struct rio_scan_node *scan; + int rc = 0; - mutex_lock(&rio_mport_list_lock); - list_for_each_entry(port, &rio_mports, node) { - if (port->id == mport_id || mport_id == RIO_MPORT_ANY) { - if (port->nscan && mport_id == RIO_MPORT_ANY) - continue; - else if (port->nscan) - break; + pr_debug("RIO: %s for mport_id=%d\n", __func__, mport_id); - port->nscan = scan_ops; - rc = 0; + if ((mport_id != RIO_MPORT_ANY && mport_id >= RIO_MAX_MPORTS) || + !scan_ops) + return -EINVAL; - if (mport_id != RIO_MPORT_ANY) - break; + mutex_lock(&rio_mport_list_lock); + + /* + * Check if there is another enumerator already registered for + * the same mport ID (including RIO_MPORT_ANY). Multiple enumerators + * for the same mport ID are not supported. + */ + list_for_each_entry(scan, &rio_scans, node) { + if (scan->mport_id == mport_id) { + rc = -EBUSY; + goto err_out; } } + + /* + * Allocate and initialize new scan registration node. + */ + scan = kzalloc(sizeof(*scan), GFP_KERNEL); + if (!scan) { + rc = -ENOMEM; + goto err_out; + } + + scan->mport_id = mport_id; + scan->ops = scan_ops; + + /* + * Traverse the list of registered mports to attach this new scan. + * + * The new scan with matching mport ID overrides any previously attached + * scan assuming that old scan (if any) is the default one (based on the + * enumerator registration check above). + * If the new scan is the global one, it will be attached only to mports + * that do not have their own individual operations already attached. + */ + list_for_each_entry(port, &rio_mports, node) { + if (port->id == mport_id) { + port->nscan = scan_ops; + break; + } else if (mport_id == RIO_MPORT_ANY && !port->nscan) + port->nscan = scan_ops; + } + + list_add_tail(&scan->node, &rio_scans); + +err_out: mutex_unlock(&rio_mport_list_lock); return rc; @@ -1639,30 +1679,81 @@ EXPORT_SYMBOL_GPL(rio_register_scan); /** * rio_unregister_scan - removes enumeration/discovery method from mport * @mport_id: mport device ID for which fabric scan routine has to be - * unregistered (RIO_MPORT_ANY = set for all available mports) + * unregistered (RIO_MPORT_ANY = apply to all mports that use + * the specified scan_ops) + * @scan_ops: enumeration/discovery operations structure * * Removes enumeration or discovery method assigned to the specified mport - * device (or all available mports if RIO_MPORT_ANY is specified). + * device. If RIO_MPORT_ANY is specified, removes the specified operations from + * all mports that have them attached. */ -int rio_unregister_scan(int mport_id) +int rio_unregister_scan(int mport_id, struct rio_scan *scan_ops) { struct rio_mport *port; + struct rio_scan_node *scan; + + pr_debug("RIO: %s for mport_id=%d\n", __func__, mport_id); + + if (mport_id != RIO_MPORT_ANY && mport_id >= RIO_MAX_MPORTS) + return -EINVAL; mutex_lock(&rio_mport_list_lock); - list_for_each_entry(port, &rio_mports, node) { - if (port->id == mport_id || mport_id == RIO_MPORT_ANY) { - if (port->nscan) - port->nscan = NULL; - if (mport_id != RIO_MPORT_ANY) - break; + + list_for_each_entry(port, &rio_mports, node) + if (port->id == mport_id || + (mport_id == RIO_MPORT_ANY && port->nscan == scan_ops)) + port->nscan = NULL; + + list_for_each_entry(scan, &rio_scans, node) + if (scan->mport_id == mport_id) { + list_del(&scan->node); + kfree(scan); } - } + mutex_unlock(&rio_mport_list_lock); return 0; } EXPORT_SYMBOL_GPL(rio_unregister_scan); +/** + * rio_mport_scan - execute enumeration/discovery on the specified mport + * @mport_id: number (ID) of mport device + */ +int rio_mport_scan(int mport_id) +{ + struct rio_mport *port = NULL; + int rc; + + mutex_lock(&rio_mport_list_lock); + list_for_each_entry(port, &rio_mports, node) { + if (port->id == mport_id) + goto found; + } + mutex_unlock(&rio_mport_list_lock); + return -ENODEV; +found: + if (!port->nscan) { + mutex_unlock(&rio_mport_list_lock); + return -EINVAL; + } + + if (!try_module_get(port->nscan->owner)) { + mutex_unlock(&rio_mport_list_lock); + return -ENODEV; + } + + mutex_unlock(&rio_mport_list_lock); + + if (port->host_deviceid >= 0) + rc = port->nscan->enumerate(port, 0); + else + rc = port->nscan->discover(port, RIO_SCAN_ENUM_NO_WAIT); + + module_put(port->nscan->owner); + return rc; +} + static void rio_fixup_device(struct rio_dev *dev) { } @@ -1691,7 +1782,10 @@ static void disc_work_handler(struct work_struct *_work) work = container_of(_work, struct rio_disc_work, work); pr_debug("RIO: discovery work for mport %d %s\n", work->mport->id, work->mport->name); - work->mport->nscan->discover(work->mport, 0); + if (try_module_get(work->mport->nscan->owner)) { + work->mport->nscan->discover(work->mport, 0); + module_put(work->mport->nscan->owner); + } } int rio_init_mports(void) @@ -1710,8 +1804,10 @@ int rio_init_mports(void) mutex_lock(&rio_mport_list_lock); list_for_each_entry(port, &rio_mports, node) { if (port->host_deviceid >= 0) { - if (port->nscan) + if (port->nscan && try_module_get(port->nscan->owner)) { port->nscan->enumerate(port, 0); + module_put(port->nscan->owner); + } } else n++; } @@ -1725,7 +1821,7 @@ int rio_init_mports(void) * for each of them. If the code below fails to allocate needed * resources, exit without error to keep results of enumeration * process (if any). - * TODO: Implement restart of dicovery process for all or + * TODO: Implement restart of discovery process for all or * individual discovering mports. */ rio_wq = alloc_workqueue("riodisc", 0, 0); @@ -1751,9 +1847,9 @@ int rio_init_mports(void) n++; } } - mutex_unlock(&rio_mport_list_lock); flush_workqueue(rio_wq); + mutex_unlock(&rio_mport_list_lock); pr_debug("RIO: destroy discovery workqueue\n"); destroy_workqueue(rio_wq); kfree(work); @@ -1784,6 +1880,8 @@ __setup("riohdid=", rio_hdid_setup); int rio_register_mport(struct rio_mport *port) { + struct rio_scan_node *scan = NULL; + if (next_portid >= RIO_MAX_MPORTS) { pr_err("RIO: reached specified max number of mports\n"); return 1; @@ -1792,9 +1890,25 @@ int rio_register_mport(struct rio_mport *port) port->id = next_portid++; port->host_deviceid = rio_get_hdid(port->id); port->nscan = NULL; + mutex_lock(&rio_mport_list_lock); list_add_tail(&port->node, &rio_mports); + + /* + * Check if there are any registered enumeration/discovery operations + * that have to be attached to the added mport. + */ + list_for_each_entry(scan, &rio_scans, node) { + if (port->id == scan->mport_id || + scan->mport_id == RIO_MPORT_ANY) { + port->nscan = scan->ops; + if (port->id == scan->mport_id) + break; + } + } mutex_unlock(&rio_mport_list_lock); + + pr_debug("RIO: %s %s id=%d\n", __func__, port->name, port->id); return 0; } diff --git a/drivers/rapidio/rio.h b/drivers/rapidio/rio.h index d59587762c76..085215cd8502 100644 --- a/drivers/rapidio/rio.h +++ b/drivers/rapidio/rio.h @@ -42,9 +42,10 @@ extern int rio_add_device(struct rio_dev *rdev); extern int rio_enable_rx_tx_port(struct rio_mport *port, int local, u16 destid, u8 hopcount, u8 port_num); extern int rio_register_scan(int mport_id, struct rio_scan *scan_ops); -extern int rio_unregister_scan(int mport_id); +extern int rio_unregister_scan(int mport_id, struct rio_scan *scan_ops); extern void rio_attach_device(struct rio_dev *rdev); extern struct rio_mport *rio_find_mport(int mport_id); +extern int rio_mport_scan(int mport_id); /* Structures internal to the RIO core code */ extern struct device_attribute rio_dev_attrs[]; diff --git a/include/linux/rio.h b/include/linux/rio.h index fcd492e7aff4..8d3db1b7b998 100644 --- a/include/linux/rio.h +++ b/include/linux/rio.h @@ -465,14 +465,29 @@ static inline struct rio_mport *dma_to_mport(struct dma_device *ddev) /** * struct rio_scan - RIO enumeration and discovery operations + * @owner: The module owner of this structure * @enumerate: Callback to perform RapidIO fabric enumeration. * @discover: Callback to perform RapidIO fabric discovery. */ struct rio_scan { + struct module *owner; int (*enumerate)(struct rio_mport *mport, u32 flags); int (*discover)(struct rio_mport *mport, u32 flags); }; +/** + * struct rio_scan_node - list node to register RapidIO enumeration and + * discovery methods with RapidIO core. + * @mport_id: ID of an mport (net) serviced by this enumerator + * @node: node in global list of registered enumerators + * @ops: RIO enumeration and discovery operations + */ +struct rio_scan_node { + int mport_id; + struct list_head node; + struct rio_scan *ops; +}; + /* Architecture and hardware-specific functions */ extern int rio_register_mport(struct rio_mport *); extern int rio_open_inb_mbox(struct rio_mport *, void *, int, int); -- cgit v1.2.3 From 94d9bd4576fcd340c344bffbbe0526227535a95f Mon Sep 17 00:00:00 2001 From: Alexandre Bounine Date: Wed, 3 Jul 2013 15:08:55 -0700 Subject: rapidio/tsi721: convert to modular mport driver This patch adds an option to build device driver for Tsi721 PCIe-to-SRIO bridge device as a kernel module. Currently this module cannot be unloaded because the existing RapidIO subsystem code does not support dynamic removal of local RapidIO controllers (TODO). Signed-off-by: Alexandre Bounine Cc: Matt Porter Cc: Li Yang Cc: Kumar Gala Cc: Andre van Herk Cc: Micha Nelissen Cc: Stef van Os Cc: Jean Delvare Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rapidio/devices/Kconfig | 2 +- drivers/rapidio/devices/Makefile | 7 +++---- drivers/rapidio/devices/tsi721.c | 9 ++++----- drivers/rapidio/rio.c | 1 + 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/drivers/rapidio/devices/Kconfig b/drivers/rapidio/devices/Kconfig index 12a9d7f7040b..c4cb0877592b 100644 --- a/drivers/rapidio/devices/Kconfig +++ b/drivers/rapidio/devices/Kconfig @@ -3,7 +3,7 @@ # config RAPIDIO_TSI721 - bool "IDT Tsi721 PCI Express SRIO Controller support" + tristate "IDT Tsi721 PCI Express SRIO Controller support" depends on RAPIDIO && PCIEPORTBUS default "n" ---help--- diff --git a/drivers/rapidio/devices/Makefile b/drivers/rapidio/devices/Makefile index 7b62860f34f8..9432c494cf57 100644 --- a/drivers/rapidio/devices/Makefile +++ b/drivers/rapidio/devices/Makefile @@ -2,7 +2,6 @@ # Makefile for RapidIO devices # -obj-$(CONFIG_RAPIDIO_TSI721) += tsi721.o -ifeq ($(CONFIG_RAPIDIO_DMA_ENGINE),y) -obj-$(CONFIG_RAPIDIO_TSI721) += tsi721_dma.o -endif +obj-$(CONFIG_RAPIDIO_TSI721) += tsi721_mport.o +tsi721_mport-y := tsi721.o +tsi721_mport-$(CONFIG_RAPIDIO_DMA_ENGINE) += tsi721_dma.o diff --git a/drivers/rapidio/devices/tsi721.c b/drivers/rapidio/devices/tsi721.c index a8b2c23a7ef4..ff7cbf2d28e3 100644 --- a/drivers/rapidio/devices/tsi721.c +++ b/drivers/rapidio/devices/tsi721.c @@ -2515,9 +2515,8 @@ static int __init tsi721_init(void) return pci_register_driver(&tsi721_driver); } -static void __exit tsi721_exit(void) -{ - pci_unregister_driver(&tsi721_driver); -} - device_initcall(tsi721_init); + +MODULE_DESCRIPTION("IDT Tsi721 PCIExpress-to-SRIO bridge driver"); +MODULE_AUTHOR("Integrated Device Technology, Inc."); +MODULE_LICENSE("GPL"); diff --git a/drivers/rapidio/rio.c b/drivers/rapidio/rio.c index 5eb727cd0652..2054b6208823 100644 --- a/drivers/rapidio/rio.c +++ b/drivers/rapidio/rio.c @@ -1911,6 +1911,7 @@ int rio_register_mport(struct rio_mport *port) pr_debug("RIO: %s %s id=%d\n", __func__, port->name, port->id); return 0; } +EXPORT_SYMBOL_GPL(rio_register_mport); EXPORT_SYMBOL_GPL(rio_local_get_device_id); EXPORT_SYMBOL_GPL(rio_get_device); -- cgit v1.2.3 From fdf90abc00979fb2d61dbdba9e855200e236142b Mon Sep 17 00:00:00 2001 From: Alexandre Bounine Date: Wed, 3 Jul 2013 15:08:56 -0700 Subject: rapidio: add modular build option for the subsystem core Add a configuration option to build RapidIO subsystem core code as a loadable kernel module. Currently this option is available only for x86-based platforms, with the additional patch for PowerPC planned to be provided later. This patch replaces kernel command line parameter "riohdid=" with its module-specific analog "rapidio.hdid=". Signed-off-by: Alexandre Bounine Cc: Matt Porter Cc: Li Yang Cc: Kumar Gala Cc: Andre van Herk Cc: Micha Nelissen Cc: Stef van Os Cc: Jean Delvare Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/x86/Kconfig | 4 ++-- drivers/rapidio/Makefile | 4 +++- drivers/rapidio/rio.c | 27 ++++++++++++++------------- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 10764a3d62cc..2775023a0744 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -2259,11 +2259,11 @@ source "drivers/pcmcia/Kconfig" source "drivers/pci/hotplug/Kconfig" config RAPIDIO - bool "RapidIO support" + tristate "RapidIO support" depends on PCI default n help - If you say Y here, the kernel will include drivers and + If enabled this option will include drivers and the core infrastructure code to support RapidIO interconnect devices. source "drivers/rapidio/Kconfig" diff --git a/drivers/rapidio/Makefile b/drivers/rapidio/Makefile index 3036702ffe8b..6271ada6993f 100644 --- a/drivers/rapidio/Makefile +++ b/drivers/rapidio/Makefile @@ -1,7 +1,9 @@ # # Makefile for RapidIO interconnect services # -obj-y += rio.o rio-access.o rio-driver.o rio-sysfs.o +obj-$(CONFIG_RAPIDIO) += rapidio.o +rapidio-y := rio.o rio-access.o rio-driver.o rio-sysfs.o + obj-$(CONFIG_RAPIDIO_ENUM_BASIC) += rio-scan.o obj-$(CONFIG_RAPIDIO) += switches/ diff --git a/drivers/rapidio/rio.c b/drivers/rapidio/rio.c index 2054b6208823..f4f30af2df68 100644 --- a/drivers/rapidio/rio.c +++ b/drivers/rapidio/rio.c @@ -5,7 +5,7 @@ * Copyright 2005 MontaVista Software, Inc. * Matt Porter * - * Copyright 2009 Integrated Device Technology, Inc. + * Copyright 2009 - 2013 Integrated Device Technology, Inc. * Alex Bounine * * This program is free software; you can redistribute it and/or modify it @@ -30,6 +30,17 @@ #include "rio.h" +MODULE_DESCRIPTION("RapidIO Subsystem Core"); +MODULE_AUTHOR("Matt Porter "); +MODULE_AUTHOR("Alexandre Bounine "); +MODULE_LICENSE("GPL"); + +static int hdid[RIO_MAX_MPORTS]; +static int ids_num; +module_param_array(hdid, int, &ids_num, 0); +MODULE_PARM_DESC(hdid, + "Destination ID assignment to local RapidIO controllers"); + static LIST_HEAD(rio_devices); static DEFINE_SPINLOCK(rio_global_list_lock); @@ -1860,24 +1871,14 @@ no_disc: return 0; } -static int hdids[RIO_MAX_MPORTS + 1]; - static int rio_get_hdid(int index) { - if (!hdids[0] || hdids[0] <= index || index >= RIO_MAX_MPORTS) + if (ids_num == 0 || ids_num <= index || index >= RIO_MAX_MPORTS) return -1; - return hdids[index + 1]; + return hdid[index]; } -static int rio_hdid_setup(char *str) -{ - (void)get_options(str, ARRAY_SIZE(hdids), hdids); - return 1; -} - -__setup("riohdid=", rio_hdid_setup); - int rio_register_mport(struct rio_mport *port) { struct rio_scan_node *scan = NULL; -- cgit v1.2.3 From 3bdbb62fe97537252b22f700009863eeb51aa750 Mon Sep 17 00:00:00 2001 From: Alexandre Bounine Date: Wed, 3 Jul 2013 15:08:58 -0700 Subject: rapidio: add udev notification Add RapidIO-specific modalias generation to enable udev notifications about RapidIO-specific events. The RapidIO modalias string format is shown below: "rapidio:vNNNNdNNNNavNNNNadNNNN" Where: v - Device Vendor ID (16 bit), d - Device ID (16 bit), av - Assembly Vendor ID (16 bit), ad - Assembly ID (16 bit), as they are reported in corresponding Capability Registers (CARs) of each RapidIO device. Signed-off-by: Alexandre Bounine Cc: Matt Porter Cc: Li Yang Cc: Kumar Gala Cc: Andre van Herk Cc: Micha Nelissen Cc: Stef van Os Cc: Jean Delvare Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rapidio/rio-driver.c | 18 ++++++++++++++++++ drivers/rapidio/rio-sysfs.c | 10 ++++++++++ include/linux/mod_devicetable.h | 19 +++++++++++++++++++ include/linux/rio.h | 16 +--------------- include/linux/rio_ids.h | 2 -- scripts/mod/devicetable-offsets.c | 6 ++++++ scripts/mod/file2alias.c | 20 ++++++++++++++++++++ 7 files changed, 74 insertions(+), 17 deletions(-) diff --git a/drivers/rapidio/rio-driver.c b/drivers/rapidio/rio-driver.c index a0c875563d76..3e9b6a78ad18 100644 --- a/drivers/rapidio/rio-driver.c +++ b/drivers/rapidio/rio-driver.c @@ -199,6 +199,23 @@ static int rio_match_bus(struct device *dev, struct device_driver *drv) out:return 0; } +static int rio_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct rio_dev *rdev; + + if (!dev) + return -ENODEV; + + rdev = to_rio_dev(dev); + if (!rdev) + return -ENODEV; + + if (add_uevent_var(env, "MODALIAS=rapidio:v%04Xd%04Xav%04Xad%04X", + rdev->vid, rdev->did, rdev->asm_vid, rdev->asm_did)) + return -ENOMEM; + return 0; +} + struct device rio_bus = { .init_name = "rapidio", }; @@ -210,6 +227,7 @@ struct bus_type rio_bus_type = { .bus_attrs = rio_bus_attrs, .probe = rio_device_probe, .remove = rio_device_remove, + .uevent = rio_uevent, }; /** diff --git a/drivers/rapidio/rio-sysfs.c b/drivers/rapidio/rio-sysfs.c index 0c4473e54f86..9331be646dc3 100644 --- a/drivers/rapidio/rio-sysfs.c +++ b/drivers/rapidio/rio-sysfs.c @@ -84,6 +84,15 @@ static ssize_t lnext_show(struct device *dev, return str - buf; } +static ssize_t modalias_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rio_dev *rdev = to_rio_dev(dev); + + return sprintf(buf, "rapidio:v%04Xd%04Xav%04Xad%04X\n", + rdev->vid, rdev->did, rdev->asm_vid, rdev->asm_did); +} + struct device_attribute rio_dev_attrs[] = { __ATTR_RO(did), __ATTR_RO(vid), @@ -93,6 +102,7 @@ struct device_attribute rio_dev_attrs[] = { __ATTR_RO(asm_rev), __ATTR_RO(lprev), __ATTR_RO(destid), + __ATTR_RO(modalias), __ATTR_NULL, }; diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index b3bd7e737e8b..b62d4af6c667 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -579,4 +579,23 @@ struct mei_cl_device_id { kernel_ulong_t driver_info; }; +/* RapidIO */ + +#define RIO_ANY_ID 0xffff + +/** + * struct rio_device_id - RIO device identifier + * @did: RapidIO device ID + * @vid: RapidIO vendor ID + * @asm_did: RapidIO assembly device ID + * @asm_vid: RapidIO assembly vendor ID + * + * Identifies a RapidIO device based on both the device/vendor IDs and + * the assembly device/vendor IDs. + */ +struct rio_device_id { + __u16 did, vid; + __u16 asm_did, asm_vid; +}; + #endif /* LINUX_MOD_DEVICETABLE_H */ diff --git a/include/linux/rio.h b/include/linux/rio.h index 8d3db1b7b998..e2faf7b8fe80 100644 --- a/include/linux/rio.h +++ b/include/linux/rio.h @@ -20,6 +20,7 @@ #include #include #include +#include #ifdef CONFIG_RAPIDIO_DMA_ENGINE #include #endif @@ -396,21 +397,6 @@ struct rio_driver { #define to_rio_driver(drv) container_of(drv,struct rio_driver, driver) -/** - * struct rio_device_id - RIO device identifier - * @did: RIO device ID - * @vid: RIO vendor ID - * @asm_did: RIO assembly device ID - * @asm_vid: RIO assembly vendor ID - * - * Identifies a RIO device based on both the device/vendor IDs and - * the assembly device/vendor IDs. - */ -struct rio_device_id { - u16 did, vid; - u16 asm_did, asm_vid; -}; - union rio_pw_msg { struct { u32 comptag; /* Component Tag CSR */ diff --git a/include/linux/rio_ids.h b/include/linux/rio_ids.h index b66d13d1bdc0..2543bc163d54 100644 --- a/include/linux/rio_ids.h +++ b/include/linux/rio_ids.h @@ -13,8 +13,6 @@ #ifndef LINUX_RIO_IDS_H #define LINUX_RIO_IDS_H -#define RIO_ANY_ID 0xffff - #define RIO_VID_FREESCALE 0x0002 #define RIO_DID_MPC8560 0x0003 diff --git a/scripts/mod/devicetable-offsets.c b/scripts/mod/devicetable-offsets.c index e66d4d258e1a..bb5d115ca671 100644 --- a/scripts/mod/devicetable-offsets.c +++ b/scripts/mod/devicetable-offsets.c @@ -177,5 +177,11 @@ int main(void) DEVID(mei_cl_device_id); DEVID_FIELD(mei_cl_device_id, name); + DEVID(rio_device_id); + DEVID_FIELD(rio_device_id, did); + DEVID_FIELD(rio_device_id, vid); + DEVID_FIELD(rio_device_id, asm_did); + DEVID_FIELD(rio_device_id, asm_vid); + return 0; } diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 45f9a3377dcd..d9e67b719f08 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -1145,6 +1145,26 @@ static int do_mei_entry(const char *filename, void *symval, } ADD_TO_DEVTABLE("mei", mei_cl_device_id, do_mei_entry); +/* Looks like: rapidio:vNdNavNadN */ +static int do_rio_entry(const char *filename, + void *symval, char *alias) +{ + DEF_FIELD(symval, rio_device_id, did); + DEF_FIELD(symval, rio_device_id, vid); + DEF_FIELD(symval, rio_device_id, asm_did); + DEF_FIELD(symval, rio_device_id, asm_vid); + + strcpy(alias, "rapidio:"); + ADD(alias, "v", vid != RIO_ANY_ID, vid); + ADD(alias, "d", did != RIO_ANY_ID, did); + ADD(alias, "av", asm_vid != RIO_ANY_ID, asm_vid); + ADD(alias, "ad", asm_did != RIO_ANY_ID, asm_did); + + add_wildcard(alias); + return 1; +} +ADD_TO_DEVTABLE("rapidio", rio_device_id, do_rio_entry); + /* Does namelen bytes of name exactly match the symbol? */ static bool sym_is(const char *name, unsigned namelen, const char *symbol) { -- cgit v1.2.3 From ed5edee2f8547d5c2b28de21cb1471aaea71ee0a Mon Sep 17 00:00:00 2001 From: Alexandre Bounine Date: Wed, 3 Jul 2013 15:08:59 -0700 Subject: rapidio: documentation update Update RapidIO documentation files to reflect modularization changes. Signed-off-by: Alexandre Bounine Cc: Matt Porter Cc: Li Yang Cc: Kumar Gala Cc: Andre van Herk Cc: Micha Nelissen Cc: Stef van Os Cc: Jean Delvare Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/rapidio/rapidio.txt | 98 +++++++++++++++++++++++++++++++++------ Documentation/rapidio/sysfs.txt | 1 + 2 files changed, 86 insertions(+), 13 deletions(-) diff --git a/Documentation/rapidio/rapidio.txt b/Documentation/rapidio/rapidio.txt index a9c16c979da2..717f5aa388b1 100644 --- a/Documentation/rapidio/rapidio.txt +++ b/Documentation/rapidio/rapidio.txt @@ -73,28 +73,44 @@ data structure. This structure includes lists of all devices and local master ports that form the same network. It also contains a pointer to the default master port that is used to communicate with devices within the network. +2.5 Device Drivers + +RapidIO device-specific drivers follow Linux Kernel Driver Model and are +intended to support specific RapidIO devices attached to the RapidIO network. + +2.6 Subsystem Interfaces + +RapidIO interconnect specification defines features that may be used to provide +one or more common service layers for all participating RapidIO devices. These +common services may act separately from device-specific drivers or be used by +device-specific drivers. Example of such service provider is the RIONET driver +which implements Ethernet-over-RapidIO interface. Because only one driver can be +registered for a device, all common RapidIO services have to be registered as +subsystem interfaces. This allows to have multiple common services attached to +the same device without blocking attachment of a device-specific driver. + 3. Subsystem Initialization --------------------------- In order to initialize the RapidIO subsystem, a platform must initialize and register at least one master port within the RapidIO network. To register mport -within the subsystem controller driver initialization code calls function +within the subsystem controller driver's initialization code calls function rio_register_mport() for each available master port. -RapidIO subsystem uses subsys_initcall() or device_initcall() to perform -controller initialization (depending on controller device type). - After all active master ports are registered with a RapidIO subsystem, an enumeration and/or discovery routine may be called automatically or by user-space command. +RapidIO subsystem can be configured to be built as a statically linked or +modular component of the kernel (see details below). + 4. Enumeration and Discovery ---------------------------- 4.1 Overview ------------ -RapidIO subsystem configuration options allow users to specify enumeration and +RapidIO subsystem configuration options allow users to build enumeration and discovery methods as statically linked components or loadable modules. An enumeration/discovery method implementation and available input parameters define how any given method can be attached to available RapidIO mports: @@ -115,8 +131,8 @@ several methods to initiate an enumeration and/or discovery process: endpoint waits for enumeration to be completed. If the specified timeout expires the discovery process is terminated without obtaining RapidIO network information. NOTE: a timed out discovery process may be restarted later using - a user-space command as it is described later if the given endpoint was - enumerated successfully. + a user-space command as it is described below (if the given endpoint was + enumerated successfully). (b) Statically linked enumeration and discovery process can be started by a command from user space. This initiation method provides more flexibility @@ -138,15 +154,42 @@ When a network scan process is started it calls an enumeration or discovery routine depending on the configured role of a master port: host or agent. Enumeration is performed by a master port if it is configured as a host port by -assigning a host device ID greater than or equal to zero. A host device ID is -assigned to a master port through the kernel command line parameter "riohdid=", -or can be configured in a platform-specific manner. If the host device ID for -a specific master port is set to -1, the discovery process will be performed -for it. +assigning a host destination ID greater than or equal to zero. The host +destination ID can be assigned to a master port using various methods depending +on RapidIO subsystem build configuration: + + (a) For a statically linked RapidIO subsystem core use command line parameter + "rapidio.hdid=" with a list of destination ID assignments in order of mport + device registration. For example, in a system with two RapidIO controllers + the command line parameter "rapidio.hdid=-1,7" will result in assignment of + the host destination ID=7 to the second RapidIO controller, while the first + one will be assigned destination ID=-1. + + (b) If the RapidIO subsystem core is built as a loadable module, in addition + to the method shown above, the host destination ID(s) can be specified using + traditional methods of passing module parameter "hdid=" during its loading: + - from command line: "modprobe rapidio hdid=-1,7", or + - from modprobe configuration file using configuration command "options", + like in this example: "options rapidio hdid=-1,7". An example of modprobe + configuration file is provided in the section below. + + NOTES: + (i) if "hdid=" parameter is omitted all available mport will be assigned + destination ID = -1; + (ii) the "hdid=" parameter in systems with multiple mports can have + destination ID assignments omitted from the end of list (default = -1). + +If the host device ID for a specific master port is set to -1, the discovery +process will be performed for it. The enumeration and discovery routines use RapidIO maintenance transactions to access the configuration space of devices. +NOTE: If RapidIO switch-specific device drivers are built as loadable modules +they must be loaded before enumeration/discovery process starts. +This requirement is cased by the fact that enumeration/discovery methods invoke +vendor-specific callbacks on early stages. + 4.2 Automatic Start of Enumeration and Discovery ------------------------------------------------ @@ -266,7 +309,36 @@ method's module initialization routine calls rio_register_scan() to attach an enumerator to a specified mport device (or devices). The basic enumerator implementation demonstrates this process. -5. References +4.6 Using Loadable RapidIO Switch Drivers +----------------------------------------- + +In the case when RapidIO switch drivers are built as loadable modules a user +must ensure that they are loaded before the enumeration/discovery starts. +This process can be automated by specifying pre- or post- dependencies in the +RapidIO-specific modprobe configuration file as shown in the example below. + + File /etc/modprobe.d/rapidio.conf: + ---------------------------------- + + # Configure RapidIO subsystem modules + + # Set enumerator host destination ID (overrides kernel command line option) + options rapidio hdid=-1,2 + + # Load RapidIO switch drivers immediately after rapidio core module was loaded + softdep rapidio post: idt_gen2 idtcps tsi57x + + # OR : + + # Load RapidIO switch drivers just before rio-scan enumerator module is loaded + softdep rio-scan pre: idt_gen2 idtcps tsi57x + + -------------------------- + +NOTE: In the example above, one of "softdep" commands must be removed or +commented out to keep required module loading sequence. + +A. References ------------- [1] RapidIO Trade Association. RapidIO Interconnect Specifications. diff --git a/Documentation/rapidio/sysfs.txt b/Documentation/rapidio/sysfs.txt index 19878179da4c..271438c0617f 100644 --- a/Documentation/rapidio/sysfs.txt +++ b/Documentation/rapidio/sysfs.txt @@ -40,6 +40,7 @@ device_rev - returns the device revision level (see 4.1 for switch specific details) lprev - returns name of previous device (switch) on the path to the device that that owns this attribute + modalias - returns the device modalias In addition to the files listed above, each device has a binary attribute file that allows read/write access to the device configuration registers using -- cgit v1.2.3 From 6ca40c2565fc617534d20d10a5848b626213608c Mon Sep 17 00:00:00 2001 From: Alexandre Bounine Date: Wed, 3 Jul 2013 15:09:01 -0700 Subject: rapidio: change endpoint device name format Change endpoint device name format to use a component tag value instead of device destination ID. RapidIO specification defines a component tag to be a unique identifier for devices in a network. RapidIO switches already use component tag as part of their device name and also use it for device identification when processing error management event notifications. Forming an endpoint's device name using its component tag instead of destination ID allows to keep sysfs device directories unchanged in case if a routing process dynamically changes endpoint's destination ID as a result of route optimization. This change should not affect any existing users because a valid device destination ID always should be obtained by reading "destid" attribute and not by parsing device name. This patch also removes switchid member from struct rio_switch because it simply duplicates the component tag and does not have other use than in device name generation. Signed-off-by: Alexandre Bounine Cc: Matt Porter Cc: Li Yang Cc: Kumar Gala Cc: Andre van Herk Cc: Micha Nelissen Cc: Stef van Os Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rapidio/rio-scan.c | 5 ++--- include/linux/rio.h | 2 -- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/rapidio/rio-scan.c b/drivers/rapidio/rio-scan.c index ab837fdb2383..d3a6539a77cc 100644 --- a/drivers/rapidio/rio-scan.c +++ b/drivers/rapidio/rio-scan.c @@ -433,7 +433,6 @@ static struct rio_dev *rio_setup_device(struct rio_net *net, /* If a PE has both switch and other functions, show it as a switch */ if (rio_is_switch(rdev)) { rswitch = rdev->rswitch; - rswitch->switchid = rdev->comp_tag & RIO_CTAG_UDEVID; rswitch->port_ok = 0; spin_lock_init(&rswitch->lock); rswitch->route_table = kzalloc(sizeof(u8)* @@ -446,7 +445,7 @@ static struct rio_dev *rio_setup_device(struct rio_net *net, rdid++) rswitch->route_table[rdid] = RIO_INVALID_ROUTE; dev_set_name(&rdev->dev, "%02x:s:%04x", rdev->net->id, - rswitch->switchid); + rdev->comp_tag & RIO_CTAG_UDEVID); if (do_enum) rio_route_clr_table(rdev, RIO_GLOBAL_TABLE, 0); @@ -459,7 +458,7 @@ static struct rio_dev *rio_setup_device(struct rio_net *net, rio_enable_rx_tx_port(port, 0, destid, hopcount, 0); dev_set_name(&rdev->dev, "%02x:e:%04x", rdev->net->id, - rdev->destid); + rdev->comp_tag & RIO_CTAG_UDEVID); } rio_attach_device(rdev); diff --git a/include/linux/rio.h b/include/linux/rio.h index e2faf7b8fe80..b71d5738e683 100644 --- a/include/linux/rio.h +++ b/include/linux/rio.h @@ -92,7 +92,6 @@ union rio_pw_msg; /** * struct rio_switch - RIO switch info * @node: Node in global list of switches - * @switchid: Switch ID that is unique across a network * @route_table: Copy of switch routing table * @port_ok: Status of each port (one bit per port) - OK=1 or UNINIT=0 * @ops: pointer to switch-specific operations @@ -101,7 +100,6 @@ union rio_pw_msg; */ struct rio_switch { struct list_head node; - u16 switchid; u8 *route_table; u32 port_ok; struct rio_switch_ops *ops; -- cgit v1.2.3 From 8f75af44eed0c81f818b5b345023cebfe5209400 Mon Sep 17 00:00:00 2001 From: "Raphael S. Carvalho" Date: Wed, 3 Jul 2013 15:09:02 -0700 Subject: kernel/pid.c: move statement Move statement to static initilization of init_pid_ns. Signed-off-by: Raphael S. Carvalho Cc: "Eric W. Biederman" Acked-by: Serge Hallyn Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/pid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/pid.c b/kernel/pid.c index 61980cefb1f5..66505c1dfc51 100644 --- a/kernel/pid.c +++ b/kernel/pid.c @@ -75,6 +75,7 @@ struct pid_namespace init_pid_ns = { [ 0 ... PIDMAP_ENTRIES-1] = { ATOMIC_INIT(BITS_PER_PAGE), NULL } }, .last_pid = 0, + .nr_hashed = PIDNS_HASH_ADDING, .level = 0, .child_reaper = &init_task, .user_ns = &init_user_ns, @@ -590,7 +591,6 @@ void __init pidmap_init(void) /* Reserve PID 0. We never call free_pidmap(0) */ set_bit(0, init_pid_ns.pidmap[0].page); atomic_dec(&init_pid_ns.pidmap[0].nr_free); - init_pid_ns.nr_hashed = PIDNS_HASH_ADDING; init_pid_ns.pid_cachep = KMEM_CACHE(pid, SLAB_HWCACHE_ALIGN | SLAB_PANIC); -- cgit v1.2.3 From 9532f149ee4532b5efc7e4a76381e1742805e1af Mon Sep 17 00:00:00 2001 From: Michal Belczyk Date: Wed, 3 Jul 2013 15:09:03 -0700 Subject: nbd: remove bogus BUG_ON in NBD_CLEAR_QUE The NBD_CLEAR_QUE ioctl has been deprecated for quite some time (its job is now done by two other ioctls). We should stop trying to make bogus assertions in it. Also, user-level code should remove calls to NBD_CLEAR_QUE, ASAP. Signed-off-by: Michal Belczyk Signed-off-by: Paul Clements Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/nbd.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 46b35f7acfde..7ed1308c42d3 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -751,7 +751,6 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd, * This is for compatibility only. The queue is always cleared * by NBD_DO_IT or NBD_CLEAR_SOCK. */ - BUG_ON(!nbd->sock && !list_empty(&nbd->queue_head)); return 0; case NBD_PRINT_DEBUG: -- cgit v1.2.3 From c378f70adbc1bbecd9e6db145019f14b2f688c7c Mon Sep 17 00:00:00 2001 From: Paul Clements Date: Wed, 3 Jul 2013 15:09:04 -0700 Subject: nbd: correct disconnect behavior Currently, when a disconnect is requested by the user (via NBD_DISCONNECT ioctl) the return from NBD_DO_IT is undefined (it is usually one of several error codes). This means that nbd-client does not know if a manual disconnect was performed or whether a network error occurred. Because of this, nbd-client's persist mode (which tries to reconnect after error, but not after manual disconnect) does not always work correctly. This change fixes this by causing NBD_DO_IT to always return 0 if a user requests a disconnect. This means that nbd-client can correctly either persist the connection (if an error occurred) or disconnect (if the user requested it). Signed-off-by: Paul Clements Acked-by: Rob Landley Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/nbd.c | 7 ++++++- include/linux/nbd.h | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 7ed1308c42d3..2dc3b5153f0d 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -623,8 +623,10 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd, if (!nbd->sock) return -EINVAL; + nbd->disconnect = 1; + nbd_send_req(nbd, &sreq); - return 0; + return 0; } case NBD_CLEAR_SOCK: { @@ -654,6 +656,7 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd, nbd->sock = SOCKET_I(inode); if (max_part > 0) bdev->bd_invalidated = 1; + nbd->disconnect = 0; /* we're connected now */ return 0; } else { fput(file); @@ -743,6 +746,8 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd, set_capacity(nbd->disk, 0); if (max_part > 0) ioctl_by_bdev(bdev, BLKRRPART, 0); + if (nbd->disconnect) /* user requested, ignore socket errors */ + return 0; return nbd->harderror; } diff --git a/include/linux/nbd.h b/include/linux/nbd.h index 4871170a04a0..ae4981ebd18e 100644 --- a/include/linux/nbd.h +++ b/include/linux/nbd.h @@ -41,6 +41,7 @@ struct nbd_device { u64 bytesize; pid_t pid; /* pid of nbd-client, if attached */ int xmit_timeout; + int disconnect; /* a disconnect has been requested by user */ }; #endif -- cgit v1.2.3 From 8030d34397e066deecb5ee9d17387fa767b12de2 Mon Sep 17 00:00:00 2001 From: Ed Cashin Date: Wed, 3 Jul 2013 15:09:05 -0700 Subject: aoe: perform I/O completions in parallel Some users have a large AoE target while others like to use many AoE targets at the same time. In the latter case, there is an opportunity to greatly improve aggregate throughput by allowing different threads to complete the I/O associated with each target. For 36 targets, 4 KiB read throughput roughly doubles, for example, with these changes in place. Signed-off-by: Ed Cashin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/aoe/aoe.h | 7 ++- drivers/block/aoe/aoecmd.c | 152 +++++++++++++++++++++++++++++++++++++-------- drivers/block/aoe/aoedev.c | 1 - drivers/block/aoe/aoenet.c | 5 +- 4 files changed, 134 insertions(+), 31 deletions(-) diff --git a/drivers/block/aoe/aoe.h b/drivers/block/aoe/aoe.h index 175649468c95..c9698529df51 100644 --- a/drivers/block/aoe/aoe.h +++ b/drivers/block/aoe/aoe.h @@ -196,9 +196,11 @@ struct ktstate { struct completion rendez; struct task_struct *task; wait_queue_head_t *waitq; - int (*fn) (void); - char *name; + int (*fn) (int); + char name[12]; spinlock_t *lock; + int id; + int active; }; int aoeblk_init(void); @@ -222,6 +224,7 @@ int aoecmd_init(void); struct sk_buff *aoecmd_ata_id(struct aoedev *); void aoe_freetframe(struct frame *); void aoe_flush_iocq(void); +void aoe_flush_iocq_by_index(int); void aoe_end_request(struct aoedev *, struct request *, int); int aoe_ktstart(struct ktstate *k); void aoe_ktstop(struct ktstate *k); diff --git a/drivers/block/aoe/aoecmd.c b/drivers/block/aoe/aoecmd.c index b75c7db16559..19955dd72eee 100644 --- a/drivers/block/aoe/aoecmd.c +++ b/drivers/block/aoe/aoecmd.c @@ -35,14 +35,27 @@ module_param(aoe_maxout, int, 0644); MODULE_PARM_DESC(aoe_maxout, "Only aoe_maxout outstanding packets for every MAC on eX.Y."); -static wait_queue_head_t ktiowq; -static struct ktstate kts; +/* The number of online cpus during module initialization gives us a + * convenient heuristic cap on the parallelism used for ktio threads + * doing I/O completion. It is not important that the cap equal the + * actual number of running CPUs at any given time, but because of CPU + * hotplug, we take care to use ncpus instead of using + * num_online_cpus() after module initialization. + */ +static int ncpus; + +/* mutex lock used for synchronization while thread spawning */ +static DEFINE_MUTEX(ktio_spawn_lock); + +static wait_queue_head_t *ktiowq; +static struct ktstate *kts; /* io completion queue */ -static struct { +struct iocq_ktio { struct list_head head; spinlock_t lock; -} iocq; +}; +static struct iocq_ktio *iocq; static struct page *empty_page; @@ -1278,23 +1291,36 @@ out: * Returns true iff responses needing processing remain. */ static int -ktio(void) +ktio(int id) { struct frame *f; struct list_head *pos; int i; + int actual_id; for (i = 0; ; ++i) { if (i == MAXIOC) return 1; - if (list_empty(&iocq.head)) + if (list_empty(&iocq[id].head)) return 0; - pos = iocq.head.next; + pos = iocq[id].head.next; list_del(pos); - spin_unlock_irq(&iocq.lock); f = list_entry(pos, struct frame, head); + spin_unlock_irq(&iocq[id].lock); ktiocomplete(f); - spin_lock_irq(&iocq.lock); + + /* Figure out if extra threads are required. */ + actual_id = f->t->d->aoeminor % ncpus; + + if (!kts[actual_id].active) { + BUG_ON(id != 0); + mutex_lock(&ktio_spawn_lock); + if (!kts[actual_id].active + && aoe_ktstart(&kts[actual_id]) == 0) + kts[actual_id].active = 1; + mutex_unlock(&ktio_spawn_lock); + } + spin_lock_irq(&iocq[id].lock); } } @@ -1311,7 +1337,7 @@ kthread(void *vp) complete(&k->rendez); /* tell spawner we're running */ do { spin_lock_irq(k->lock); - more = k->fn(); + more = k->fn(k->id); if (!more) { add_wait_queue(k->waitq, &wait); __set_current_state(TASK_INTERRUPTIBLE); @@ -1353,13 +1379,24 @@ aoe_ktstart(struct ktstate *k) static void ktcomplete(struct frame *f, struct sk_buff *skb) { + int id; ulong flags; f->r_skb = skb; - spin_lock_irqsave(&iocq.lock, flags); - list_add_tail(&f->head, &iocq.head); - spin_unlock_irqrestore(&iocq.lock, flags); - wake_up(&ktiowq); + id = f->t->d->aoeminor % ncpus; + spin_lock_irqsave(&iocq[id].lock, flags); + if (!kts[id].active) { + spin_unlock_irqrestore(&iocq[id].lock, flags); + /* The thread with id has not been spawned yet, + * so delegate the work to the main thread and + * try spawning a new thread. + */ + id = 0; + spin_lock_irqsave(&iocq[id].lock, flags); + } + list_add_tail(&f->head, &iocq[id].head); + spin_unlock_irqrestore(&iocq[id].lock, flags); + wake_up(&ktiowq[id]); } struct sk_buff * @@ -1705,6 +1742,17 @@ aoe_failbuf(struct aoedev *d, struct buf *buf) void aoe_flush_iocq(void) +{ + int i; + + for (i = 0; i < ncpus; i++) { + if (kts[i].active) + aoe_flush_iocq_by_index(i); + } +} + +void +aoe_flush_iocq_by_index(int id) { struct frame *f; struct aoedev *d; @@ -1713,9 +1761,9 @@ aoe_flush_iocq(void) struct sk_buff *skb; ulong flags; - spin_lock_irqsave(&iocq.lock, flags); - list_splice_init(&iocq.head, &flist); - spin_unlock_irqrestore(&iocq.lock, flags); + spin_lock_irqsave(&iocq[id].lock, flags); + list_splice_init(&iocq[id].head, &flist); + spin_unlock_irqrestore(&iocq[id].lock, flags); while (!list_empty(&flist)) { pos = flist.next; list_del(pos); @@ -1738,6 +1786,8 @@ int __init aoecmd_init(void) { void *p; + int i; + int ret; /* get_zeroed_page returns page with ref count 1 */ p = (void *) get_zeroed_page(GFP_KERNEL | __GFP_REPEAT); @@ -1745,22 +1795,72 @@ aoecmd_init(void) return -ENOMEM; empty_page = virt_to_page(p); - INIT_LIST_HEAD(&iocq.head); - spin_lock_init(&iocq.lock); - init_waitqueue_head(&ktiowq); - kts.name = "aoe_ktio"; - kts.fn = ktio; - kts.waitq = &ktiowq; - kts.lock = &iocq.lock; - return aoe_ktstart(&kts); + ncpus = num_online_cpus(); + + iocq = kcalloc(ncpus, sizeof(struct iocq_ktio), GFP_KERNEL); + if (!iocq) + return -ENOMEM; + + kts = kcalloc(ncpus, sizeof(struct ktstate), GFP_KERNEL); + if (!kts) { + ret = -ENOMEM; + goto kts_fail; + } + + ktiowq = kcalloc(ncpus, sizeof(wait_queue_head_t), GFP_KERNEL); + if (!ktiowq) { + ret = -ENOMEM; + goto ktiowq_fail; + } + + mutex_init(&ktio_spawn_lock); + + for (i = 0; i < ncpus; i++) { + INIT_LIST_HEAD(&iocq[i].head); + spin_lock_init(&iocq[i].lock); + init_waitqueue_head(&ktiowq[i]); + snprintf(kts[i].name, sizeof(kts[i].name), "aoe_ktio%d", i); + kts[i].fn = ktio; + kts[i].waitq = &ktiowq[i]; + kts[i].lock = &iocq[i].lock; + kts[i].id = i; + kts[i].active = 0; + } + kts[0].active = 1; + if (aoe_ktstart(&kts[0])) { + ret = -ENOMEM; + goto ktstart_fail; + } + return 0; + +ktstart_fail: + kfree(ktiowq); +ktiowq_fail: + kfree(kts); +kts_fail: + kfree(iocq); + + return ret; } void aoecmd_exit(void) { - aoe_ktstop(&kts); + int i; + + for (i = 0; i < ncpus; i++) + if (kts[i].active) + aoe_ktstop(&kts[i]); + aoe_flush_iocq(); + /* Free up the iocq and thread speicific configuration + * allocated during startup. + */ + kfree(iocq); + kfree(kts); + kfree(ktiowq); + free_page((unsigned long) page_address(empty_page)); empty_page = NULL; } diff --git a/drivers/block/aoe/aoedev.c b/drivers/block/aoe/aoedev.c index 98f2965778b9..92201b6334c2 100644 --- a/drivers/block/aoe/aoedev.c +++ b/drivers/block/aoe/aoedev.c @@ -518,7 +518,6 @@ void aoedev_exit(void) { flush_scheduled_work(); - aoe_flush_iocq(); flush(NULL, 0, EXITING); } diff --git a/drivers/block/aoe/aoenet.c b/drivers/block/aoe/aoenet.c index 71d3ea8d3006..4af5f06c467b 100644 --- a/drivers/block/aoe/aoenet.c +++ b/drivers/block/aoe/aoenet.c @@ -52,7 +52,7 @@ static struct sk_buff_head skbtxq; /* enters with txlock held */ static int -tx(void) __must_hold(&txlock) +tx(int id) __must_hold(&txlock) { struct sk_buff *skb; struct net_device *ifp; @@ -205,7 +205,8 @@ aoenet_init(void) kts.lock = &txlock; kts.fn = tx; kts.waitq = &txwq; - kts.name = "aoe_tx"; + kts.id = 0; + snprintf(kts.name, sizeof(kts.name), "aoe_tx%d", kts.id); if (aoe_ktstart(&kts)) return -EAGAIN; dev_add_pack(&aoe_pt); -- cgit v1.2.3 From ca47bbd93c1cc75b9b2736b0ac49129718f32342 Mon Sep 17 00:00:00 2001 From: Ed Cashin Date: Wed, 3 Jul 2013 15:09:06 -0700 Subject: aoe: update copyright date Signed-off-by: Ed Cashin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/aoe/aoe.h | 2 +- drivers/block/aoe/aoecmd.c | 2 +- drivers/block/aoe/aoedev.c | 2 +- drivers/block/aoe/aoenet.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/block/aoe/aoe.h b/drivers/block/aoe/aoe.h index c9698529df51..2284f892c0bc 100644 --- a/drivers/block/aoe/aoe.h +++ b/drivers/block/aoe/aoe.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012 Coraid, Inc. See COPYING for GPL terms. */ +/* Copyright (c) 2013 Coraid, Inc. See COPYING for GPL terms. */ #define VERSION "81" #define AOE_MAJOR 152 #define DEVICE_NAME "aoe" diff --git a/drivers/block/aoe/aoecmd.c b/drivers/block/aoe/aoecmd.c index 19955dd72eee..99cb944a002d 100644 --- a/drivers/block/aoe/aoecmd.c +++ b/drivers/block/aoe/aoecmd.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012 Coraid, Inc. See COPYING for GPL terms. */ +/* Copyright (c) 2013 Coraid, Inc. See COPYING for GPL terms. */ /* * aoecmd.c * Filesystem request handling methods diff --git a/drivers/block/aoe/aoedev.c b/drivers/block/aoe/aoedev.c index 92201b6334c2..784c92e038d1 100644 --- a/drivers/block/aoe/aoedev.c +++ b/drivers/block/aoe/aoedev.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012 Coraid, Inc. See COPYING for GPL terms. */ +/* Copyright (c) 2013 Coraid, Inc. See COPYING for GPL terms. */ /* * aoedev.c * AoE device utility functions; maintains device list. diff --git a/drivers/block/aoe/aoenet.c b/drivers/block/aoe/aoenet.c index 4af5f06c467b..63773a90581d 100644 --- a/drivers/block/aoe/aoenet.c +++ b/drivers/block/aoe/aoenet.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012 Coraid, Inc. See COPYING for GPL terms. */ +/* Copyright (c) 2013 Coraid, Inc. See COPYING for GPL terms. */ /* * aoenet.c * Ethernet portion of AoE driver -- cgit v1.2.3 From 94ac11833fc46fcde6eec7d97893a44c7673967b Mon Sep 17 00:00:00 2001 From: Ed Cashin Date: Wed, 3 Jul 2013 15:09:07 -0700 Subject: aoe: update internal version number to v83 Signed-off-by: Ed Cashin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/aoe/aoe.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/block/aoe/aoe.h b/drivers/block/aoe/aoe.h index 2284f892c0bc..025c41d3cb33 100644 --- a/drivers/block/aoe/aoe.h +++ b/drivers/block/aoe/aoe.h @@ -1,5 +1,5 @@ /* Copyright (c) 2013 Coraid, Inc. See COPYING for GPL terms. */ -#define VERSION "81" +#define VERSION "83" #define AOE_MAJOR 152 #define DEVICE_NAME "aoe" -- cgit v1.2.3 From 8da01af45e197110cbce84fb5ccb54645f051ac6 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 3 Jul 2013 15:09:08 -0700 Subject: Documentation/accounting/getdelays.c: avoid strncpy in accounting tool Avoid strncpy anti-pattern. [akpm@linux-foundation.org: remove the str[cpy|dup] altogether] Signed-off-by: Kees Cook Cc: Andreas Schwab Cc: Rob Landley Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/accounting/getdelays.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/accounting/getdelays.c b/Documentation/accounting/getdelays.c index f8ebcde43b17..c6a06b71594d 100644 --- a/Documentation/accounting/getdelays.c +++ b/Documentation/accounting/getdelays.c @@ -272,7 +272,7 @@ int main(int argc, char *argv[]) char *logfile = NULL; int loop = 0; int containerset = 0; - char containerpath[1024]; + char *containerpath = NULL; int cfd = 0; int forking = 0; sigset_t sigset; @@ -299,7 +299,7 @@ int main(int argc, char *argv[]) break; case 'C': containerset = 1; - strncpy(containerpath, optarg, strlen(optarg) + 1); + containerpath = optarg; break; case 'w': logfile = strdup(optarg); -- cgit v1.2.3 From c32df4e182e5bf40edde45da247318986d3cbf91 Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghiu Date: Wed, 3 Jul 2013 15:09:09 -0700 Subject: drivers/parport/share.c: use kzalloc Replaced calls to kmalloc and memset with kzalloc. Patch found using coccinelle. Signed-off-by: Alexandru Gheorghiu Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/parport/share.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/parport/share.c b/drivers/parport/share.c index a848e02e6be3..6a83ee1e9178 100644 --- a/drivers/parport/share.c +++ b/drivers/parport/share.c @@ -282,14 +282,13 @@ struct parport *parport_register_port(unsigned long base, int irq, int dma, int device; char *name; - tmp = kmalloc(sizeof(struct parport), GFP_KERNEL); + tmp = kzalloc(sizeof(struct parport), GFP_KERNEL); if (!tmp) { printk(KERN_WARNING "parport: memory squeeze\n"); return NULL; } /* Init our structure */ - memset(tmp, 0, sizeof(struct parport)); tmp->base = base; tmp->irq = irq; tmp->dma = dma; -- cgit v1.2.3 From 2a65182235790fc9a6abb9f41c1006c95f506545 Mon Sep 17 00:00:00 2001 From: Jan Luebbe Date: Wed, 3 Jul 2013 15:09:10 -0700 Subject: drivers/pps/clients/pps-gpio.c: convert to devm_* helpers Signed-off-by: Jan Luebbe Acked-by: Rodolfo Giometti Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/pps/clients/pps-gpio.c | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/drivers/pps/clients/pps-gpio.c b/drivers/pps/clients/pps-gpio.c index d3db26e46489..54c2809021a3 100644 --- a/drivers/pps/clients/pps-gpio.c +++ b/drivers/pps/clients/pps-gpio.c @@ -74,7 +74,7 @@ static int pps_gpio_setup(struct platform_device *pdev) int ret; const struct pps_gpio_platform_data *pdata = pdev->dev.platform_data; - ret = gpio_request(pdata->gpio_pin, pdata->gpio_label); + ret = devm_gpio_request(&pdev->dev, pdata->gpio_pin, pdata->gpio_label); if (ret) { pr_warning("failed to request GPIO %u\n", pdata->gpio_pin); return -EINVAL; @@ -83,7 +83,6 @@ static int pps_gpio_setup(struct platform_device *pdev) ret = gpio_direction_input(pdata->gpio_pin); if (ret) { pr_warning("failed to set pin direction\n"); - gpio_free(pdata->gpio_pin); return -EINVAL; } @@ -109,7 +108,6 @@ static int pps_gpio_probe(struct platform_device *pdev) struct pps_gpio_device_data *data; int irq; int ret; - int err; int pps_default_params; const struct pps_gpio_platform_data *pdata = pdev->dev.platform_data; @@ -123,17 +121,14 @@ static int pps_gpio_probe(struct platform_device *pdev) irq = gpio_to_irq(pdata->gpio_pin); if (irq < 0) { pr_err("failed to map GPIO to IRQ: %d\n", irq); - err = -EINVAL; - goto return_error; + return -EINVAL; } /* allocate space for device info */ data = devm_kzalloc(&pdev->dev, sizeof(struct pps_gpio_device_data), GFP_KERNEL); - if (data == NULL) { - err = -ENOMEM; - goto return_error; - } + if (data == NULL) + return -ENOMEM; /* initialize PPS specific parts of the bookkeeping data structure. */ data->info.mode = PPS_CAPTUREASSERT | PPS_OFFSETASSERT | @@ -152,41 +147,32 @@ static int pps_gpio_probe(struct platform_device *pdev) data->pps = pps_register_source(&data->info, pps_default_params); if (data->pps == NULL) { pr_err("failed to register IRQ %d as PPS source\n", irq); - err = -EINVAL; - goto return_error; + return -EINVAL; } data->irq = irq; data->pdata = pdata; /* register IRQ interrupt handler */ - ret = request_irq(irq, pps_gpio_irq_handler, + ret = devm_request_irq(&pdev->dev, irq, pps_gpio_irq_handler, get_irqf_trigger_flags(pdata), data->info.name, data); if (ret) { pps_unregister_source(data->pps); pr_err("failed to acquire IRQ %d\n", irq); - err = -EINVAL; - goto return_error; + return -EINVAL; } platform_set_drvdata(pdev, data); dev_info(data->pps->dev, "Registered IRQ %d as PPS source\n", irq); return 0; - -return_error: - gpio_free(pdata->gpio_pin); - return err; } static int pps_gpio_remove(struct platform_device *pdev) { struct pps_gpio_device_data *data = platform_get_drvdata(pdev); - const struct pps_gpio_platform_data *pdata = data->pdata; platform_set_drvdata(pdev, NULL); - free_irq(data->irq, data); - gpio_free(pdata->gpio_pin); pps_unregister_source(data->pps); pr_info("removed IRQ %d as PPS source\n", data->irq); return 0; -- cgit v1.2.3 From 05212be363363efbc003d4fa1eaf8e48dfbe425a Mon Sep 17 00:00:00 2001 From: Jan Luebbe Date: Wed, 3 Jul 2013 15:09:10 -0700 Subject: drivers/pps/clients/pps-gpio.c: convert to module_platform_driver This removes some boilerplate code (no functional changes). Signed-off-by: Jan Luebbe Acked-by: Rodolfo Giometti Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/pps/clients/pps-gpio.c | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/drivers/pps/clients/pps-gpio.c b/drivers/pps/clients/pps-gpio.c index 54c2809021a3..5bd3bf093b54 100644 --- a/drivers/pps/clients/pps-gpio.c +++ b/drivers/pps/clients/pps-gpio.c @@ -187,23 +187,7 @@ static struct platform_driver pps_gpio_driver = { }, }; -static int __init pps_gpio_init(void) -{ - int ret = platform_driver_register(&pps_gpio_driver); - if (ret < 0) - pr_err("failed to register platform driver\n"); - return ret; -} - -static void __exit pps_gpio_exit(void) -{ - platform_driver_unregister(&pps_gpio_driver); - pr_debug("unregistered platform driver\n"); -} - -module_init(pps_gpio_init); -module_exit(pps_gpio_exit); - +module_platform_driver(pps_gpio_driver); MODULE_AUTHOR("Ricardo Martins "); MODULE_AUTHOR("James Nuss "); MODULE_DESCRIPTION("Use GPIO pin as PPS source"); -- cgit v1.2.3 From c5dbcf8b70b50b1f6ef4850f61d79204ea46d761 Mon Sep 17 00:00:00 2001 From: Jan Luebbe Date: Wed, 3 Jul 2013 15:09:12 -0700 Subject: pps-gpio: add device-tree binding and support Instead of allocating a struct pps_gpio_platform_data in the DT case, store the necessary information in struct pps_gpio_device_data itself. This avoids an additional allocation and the ifdef. It also gets rid of some indirection. Also use dev_err instead of pr_err in the changed code. Signed-off-by: Jan Luebbe Acked-by: Arnd Bergmann Acked-by: Rodolfo Giometti Cc: Grant Likely Cc: Rob Herring Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/devicetree/bindings/pps/pps-gpio.txt | 20 ++++ drivers/pps/clients/pps-gpio.c | 127 ++++++++++++--------- 2 files changed, 93 insertions(+), 54 deletions(-) create mode 100644 Documentation/devicetree/bindings/pps/pps-gpio.txt diff --git a/Documentation/devicetree/bindings/pps/pps-gpio.txt b/Documentation/devicetree/bindings/pps/pps-gpio.txt new file mode 100644 index 000000000000..40bf9c3564a5 --- /dev/null +++ b/Documentation/devicetree/bindings/pps/pps-gpio.txt @@ -0,0 +1,20 @@ +Device-Tree Bindings for a PPS Signal on GPIO + +These properties describe a PPS (pulse-per-second) signal connected to +a GPIO pin. + +Required properties: +- compatible: should be "pps-gpio" +- gpios: one PPS GPIO in the format described by ../gpio/gpio.txt + +Optional properties: +- assert-falling-edge: when present, assert is indicated by a falling edge + (instead of by a rising edge) + +Example: + pps { + compatible = "pps-gpio"; + gpios = <&gpio2 6 0>; + + assert-falling-edge; + }; diff --git a/drivers/pps/clients/pps-gpio.c b/drivers/pps/clients/pps-gpio.c index 5bd3bf093b54..eae0eda9ff39 100644 --- a/drivers/pps/clients/pps-gpio.c +++ b/drivers/pps/clients/pps-gpio.c @@ -33,13 +33,17 @@ #include #include #include +#include +#include /* Info for each registered platform device */ struct pps_gpio_device_data { int irq; /* IRQ used as PPS source */ struct pps_device *pps; /* PPS source device */ struct pps_source_info info; /* PPS source information */ - const struct pps_gpio_platform_data *pdata; + bool assert_falling_edge; + bool capture_clear; + unsigned int gpio_pin; }; /* @@ -57,45 +61,25 @@ static irqreturn_t pps_gpio_irq_handler(int irq, void *data) info = data; - rising_edge = gpio_get_value(info->pdata->gpio_pin); - if ((rising_edge && !info->pdata->assert_falling_edge) || - (!rising_edge && info->pdata->assert_falling_edge)) + rising_edge = gpio_get_value(info->gpio_pin); + if ((rising_edge && !info->assert_falling_edge) || + (!rising_edge && info->assert_falling_edge)) pps_event(info->pps, &ts, PPS_CAPTUREASSERT, NULL); - else if (info->pdata->capture_clear && - ((rising_edge && info->pdata->assert_falling_edge) || - (!rising_edge && !info->pdata->assert_falling_edge))) + else if (info->capture_clear && + ((rising_edge && info->assert_falling_edge) || + (!rising_edge && !info->assert_falling_edge))) pps_event(info->pps, &ts, PPS_CAPTURECLEAR, NULL); return IRQ_HANDLED; } -static int pps_gpio_setup(struct platform_device *pdev) -{ - int ret; - const struct pps_gpio_platform_data *pdata = pdev->dev.platform_data; - - ret = devm_gpio_request(&pdev->dev, pdata->gpio_pin, pdata->gpio_label); - if (ret) { - pr_warning("failed to request GPIO %u\n", pdata->gpio_pin); - return -EINVAL; - } - - ret = gpio_direction_input(pdata->gpio_pin); - if (ret) { - pr_warning("failed to set pin direction\n"); - return -EINVAL; - } - - return 0; -} - static unsigned long -get_irqf_trigger_flags(const struct pps_gpio_platform_data *pdata) +get_irqf_trigger_flags(const struct pps_gpio_device_data *data) { - unsigned long flags = pdata->assert_falling_edge ? + unsigned long flags = data->assert_falling_edge ? IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING; - if (pdata->capture_clear) { + if (data->capture_clear) { flags |= ((flags & IRQF_TRIGGER_RISING) ? IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING); } @@ -106,34 +90,63 @@ get_irqf_trigger_flags(const struct pps_gpio_platform_data *pdata) static int pps_gpio_probe(struct platform_device *pdev) { struct pps_gpio_device_data *data; - int irq; + const char *gpio_label; int ret; int pps_default_params; const struct pps_gpio_platform_data *pdata = pdev->dev.platform_data; + struct device_node *np = pdev->dev.of_node; + + /* allocate space for device info */ + data = devm_kzalloc(&pdev->dev, sizeof(struct pps_gpio_device_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + if (pdata) { + data->gpio_pin = pdata->gpio_pin; + gpio_label = pdata->gpio_label; + + data->assert_falling_edge = pdata->assert_falling_edge; + data->capture_clear = pdata->capture_clear; + } else { + ret = of_get_gpio(np, 0); + if (ret < 0) { + dev_err(&pdev->dev, "failed to get GPIO from device tree\n"); + return ret; + } + data->gpio_pin = ret; + gpio_label = PPS_GPIO_NAME; + + if (of_get_property(np, "assert-falling-edge", NULL)) + data->assert_falling_edge = true; + } /* GPIO setup */ - ret = pps_gpio_setup(pdev); - if (ret) + ret = devm_gpio_request(&pdev->dev, data->gpio_pin, gpio_label); + if (ret) { + dev_err(&pdev->dev, "failed to request GPIO %u\n", + data->gpio_pin); + return ret; + } + + ret = gpio_direction_input(data->gpio_pin); + if (ret) { + dev_err(&pdev->dev, "failed to set pin direction\n"); return -EINVAL; + } /* IRQ setup */ - irq = gpio_to_irq(pdata->gpio_pin); - if (irq < 0) { - pr_err("failed to map GPIO to IRQ: %d\n", irq); + ret = gpio_to_irq(data->gpio_pin); + if (ret < 0) { + dev_err(&pdev->dev, "failed to map GPIO to IRQ: %d\n", ret); return -EINVAL; } - - /* allocate space for device info */ - data = devm_kzalloc(&pdev->dev, sizeof(struct pps_gpio_device_data), - GFP_KERNEL); - if (data == NULL) - return -ENOMEM; + data->irq = ret; /* initialize PPS specific parts of the bookkeeping data structure. */ data->info.mode = PPS_CAPTUREASSERT | PPS_OFFSETASSERT | PPS_ECHOASSERT | PPS_CANWAIT | PPS_TSFMT_TSPEC; - if (pdata->capture_clear) + if (data->capture_clear) data->info.mode |= PPS_CAPTURECLEAR | PPS_OFFSETCLEAR | PPS_ECHOCLEAR; data->info.owner = THIS_MODULE; @@ -142,28 +155,27 @@ static int pps_gpio_probe(struct platform_device *pdev) /* register PPS source */ pps_default_params = PPS_CAPTUREASSERT | PPS_OFFSETASSERT; - if (pdata->capture_clear) + if (data->capture_clear) pps_default_params |= PPS_CAPTURECLEAR | PPS_OFFSETCLEAR; data->pps = pps_register_source(&data->info, pps_default_params); if (data->pps == NULL) { - pr_err("failed to register IRQ %d as PPS source\n", irq); + dev_err(&pdev->dev, "failed to register IRQ %d as PPS source\n", + data->irq); return -EINVAL; } - data->irq = irq; - data->pdata = pdata; - /* register IRQ interrupt handler */ - ret = devm_request_irq(&pdev->dev, irq, pps_gpio_irq_handler, - get_irqf_trigger_flags(pdata), data->info.name, data); + ret = devm_request_irq(&pdev->dev, data->irq, pps_gpio_irq_handler, + get_irqf_trigger_flags(data), data->info.name, data); if (ret) { pps_unregister_source(data->pps); - pr_err("failed to acquire IRQ %d\n", irq); + dev_err(&pdev->dev, "failed to acquire IRQ %d\n", data->irq); return -EINVAL; } platform_set_drvdata(pdev, data); - dev_info(data->pps->dev, "Registered IRQ %d as PPS source\n", irq); + dev_info(data->pps->dev, "Registered IRQ %d as PPS source\n", + data->irq); return 0; } @@ -174,16 +186,23 @@ static int pps_gpio_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); pps_unregister_source(data->pps); - pr_info("removed IRQ %d as PPS source\n", data->irq); + dev_info(&pdev->dev, "removed IRQ %d as PPS source\n", data->irq); return 0; } +static const struct of_device_id pps_gpio_dt_ids[] = { + { .compatible = "pps-gpio", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, pps_gpio_dt_ids); + static struct platform_driver pps_gpio_driver = { .probe = pps_gpio_probe, .remove = pps_gpio_remove, .driver = { .name = PPS_GPIO_NAME, - .owner = THIS_MODULE + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(pps_gpio_dt_ids), }, }; -- cgit v1.2.3 From 10560490d9555b3add7e9b241594dddd07b160ec Mon Sep 17 00:00:00 2001 From: Libo Chen Date: Wed, 3 Jul 2013 15:09:13 -0700 Subject: drivers/memstick/host/jmb38x_ms: convert to module_pci_driver Use module_pci_driver instead of init/exit, make code clean. Signed-off-by: Libo Chen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/memstick/host/jmb38x_ms.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/memstick/host/jmb38x_ms.c b/drivers/memstick/host/jmb38x_ms.c index c37d3756d8d2..aeabaa5aedf7 100644 --- a/drivers/memstick/host/jmb38x_ms.c +++ b/drivers/memstick/host/jmb38x_ms.c @@ -1046,20 +1046,9 @@ static struct pci_driver jmb38x_ms_driver = { .resume = jmb38x_ms_resume }; -static int __init jmb38x_ms_init(void) -{ - return pci_register_driver(&jmb38x_ms_driver); -} - -static void __exit jmb38x_ms_exit(void) -{ - pci_unregister_driver(&jmb38x_ms_driver); -} +module_pci_driver(jmb38x_ms_driver); MODULE_AUTHOR("Alex Dubov"); MODULE_DESCRIPTION("JMicron jmb38x MemoryStick driver"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, jmb38x_ms_id_tbl); - -module_init(jmb38x_ms_init); -module_exit(jmb38x_ms_exit); -- cgit v1.2.3 From 5af1a9ce3d114bbd49b5a06bfc5f5404d73709e3 Mon Sep 17 00:00:00 2001 From: Libo Chen Date: Wed, 3 Jul 2013 15:09:14 -0700 Subject: drivers/memstick/host/r592.c: convert to module_pci_driver Signed-off-by: Libo Chen Cc: Maxim Levitsky Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/memstick/host/r592.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/memstick/host/r592.c b/drivers/memstick/host/r592.c index 9718661c1fb6..1b6e91345222 100644 --- a/drivers/memstick/host/r592.c +++ b/drivers/memstick/host/r592.c @@ -884,18 +884,7 @@ static struct pci_driver r852_pci_driver = { .driver.pm = &r592_pm_ops, }; -static __init int r592_module_init(void) -{ - return pci_register_driver(&r852_pci_driver); -} - -static void __exit r592_module_exit(void) -{ - pci_unregister_driver(&r852_pci_driver); -} - -module_init(r592_module_init); -module_exit(r592_module_exit); +module_pci_driver(r852_pci_driver); module_param_named(enable_dma, r592_enable_dma, bool, S_IRUGO); MODULE_PARM_DESC(enable_dma, "Enable usage of the DMA (default)"); -- cgit v1.2.3 From d5528773e68e35512f69dd574e25d8c81e7d3105 Mon Sep 17 00:00:00 2001 From: Jean-Francois Dagenais Date: Wed, 3 Jul 2013 15:09:15 -0700 Subject: drivers/w1/slaves/w1_ds2408.c: add magic sequence to disable P0 test mode Power-up timing The DS2408 is sensitive to the power-on slew rate and can inadvertently power up with a test mode feature enabled. When this occurs, the P0 port does not respond to the Channel Access Write command. For most reliable operation, it is recommended to disable the test mode after every power-on reset using the Disable Test Mode sequence shown below. The 64-bit ROM code must be transmitted in the same bit sequence as with the Match ROM command, i.e., least significant bit first. This precaution is recommended in parasite power mode (VCC pin connected to GND) as well as with VCC power. Disable Test Mode: RST,PD,96h,<64-bit DS2408 ROM Code>,3Ch,RST,PD [akpm@linux-foundation.org: don't use kerenldoc token to introduce a non-kerneldoc comment, tweak whitespace] Signed-off-by: Jean-Francois Dagenais Cc: Evgeniy Polyakov Cc: Greg KH Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/w1/slaves/w1_ds2408.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/w1/slaves/w1_ds2408.c b/drivers/w1/slaves/w1_ds2408.c index 91cc2cdf02c0..cb8a8e5d9573 100644 --- a/drivers/w1/slaves/w1_ds2408.c +++ b/drivers/w1/slaves/w1_ds2408.c @@ -302,7 +302,33 @@ error: return -EIO; } +/* + * This is a special sequence we must do to ensure the P0 output is not stuck + * in test mode. This is described in rev 2 of the ds2408's datasheet + * (http://datasheets.maximintegrated.com/en/ds/DS2408.pdf) under + * "APPLICATION INFORMATION/Power-up timing". + */ +static int w1_f29_disable_test_mode(struct w1_slave *sl) +{ + int res; + u8 magic[10] = {0x96, }; + u64 rn = le64_to_cpu(*((u64*)&sl->reg_num)); + + memcpy(&magic[1], &rn, 8); + magic[9] = 0x3C; + + mutex_lock(&sl->master->bus_mutex); + res = w1_reset_bus(sl->master); + if (res) + goto out; + w1_write_block(sl->master, magic, ARRAY_SIZE(magic)); + + res = w1_reset_bus(sl->master); +out: + mutex_unlock(&sl->master->bus_mutex); + return res; +} static struct bin_attribute w1_f29_sysfs_bin_files[] = { { @@ -363,6 +389,10 @@ static int w1_f29_add_slave(struct w1_slave *sl) int err = 0; int i; + err = w1_f29_disable_test_mode(sl); + if (err) + return err; + for (i = 0; i < ARRAY_SIZE(w1_f29_sysfs_bin_files) && !err; ++i) err = sysfs_create_bin_file( &sl->dev.kobj, -- cgit v1.2.3 From 4b30f07e74d2df673925019ad58bc59a719df3bb Mon Sep 17 00:00:00 2001 From: Tang Chen Date: Wed, 3 Jul 2013 15:09:16 -0700 Subject: aio: fix wrong comment in aio_complete() ctx->ctx_lock should be ctx->completion_lock. Signed-off-by: Tang Chen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/aio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/aio.c b/fs/aio.c index a8ecc8313fb0..9b5ca1137419 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -625,7 +625,7 @@ void aio_complete(struct kiocb *iocb, long res, long res2) /* * Add a completion event to the ring buffer. Must be done holding - * ctx->ctx_lock to prevent other code from messing with the tail + * ctx->completion_lock to prevent other code from messing with the tail * pointer since we might be called from irq context. */ spin_lock_irqsave(&ctx->completion_lock, flags); -- cgit v1.2.3 From 0786f7b225ba1edd801dc4bfbf6191d058b943a2 Mon Sep 17 00:00:00 2001 From: Kevin Hao Date: Wed, 3 Jul 2013 15:09:16 -0700 Subject: kernel/resource.c: remove the unneeded assignment in function __find_resource This line was introduced by fcb11918 ("resources: add arch hook for preventing allocation in reserved areas"). But the struct tmp was already assigned to *new in the above line, so this seems superfluous. Just remove it. Signed-off-by: Kevin Hao Cc: Bjorn Helgaas Cc: Jesse Barnes Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/resource.c | 1 - 1 file changed, 1 deletion(-) diff --git a/kernel/resource.c b/kernel/resource.c index 77bf11a86c7d..3f285dce9347 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -449,7 +449,6 @@ static int __find_resource(struct resource *root, struct resource *old, struct resource *this = root->child; struct resource tmp = *new, avail, alloc; - tmp.flags = new->flags; tmp.start = root->start; /* * Skip past an allocated resource that starts at 0, since the assignment -- cgit v1.2.3 From 51a1d16563fb6488ae1f30d31f62abc6aa50b268 Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Wed, 3 Jul 2013 15:09:17 -0700 Subject: selftests: exit 1 on failure In case this ever gets scripted, it should return 0 on success and 1 on failure. Parsing the output should be left to meatbags. Signed-off-by: Joern Engel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- tools/testing/selftests/vm/Makefile | 2 +- tools/testing/selftests/vm/run_vmtests | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/vm/Makefile b/tools/testing/selftests/vm/Makefile index 436d2e81868b..7d4792799043 100644 --- a/tools/testing/selftests/vm/Makefile +++ b/tools/testing/selftests/vm/Makefile @@ -8,7 +8,7 @@ all: hugepage-mmap hugepage-shm map_hugetlb thuge-gen $(CC) $(CFLAGS) -o $@ $^ run_tests: all - @/bin/sh ./run_vmtests || echo "vmtests: [FAIL]" + @/bin/sh ./run_vmtests || (echo "vmtests: [FAIL]"; exit 1) clean: $(RM) hugepage-mmap hugepage-shm map_hugetlb diff --git a/tools/testing/selftests/vm/run_vmtests b/tools/testing/selftests/vm/run_vmtests index 4c53cae6c273..7a9072d52e70 100644 --- a/tools/testing/selftests/vm/run_vmtests +++ b/tools/testing/selftests/vm/run_vmtests @@ -4,6 +4,7 @@ #we need 256M, below is the size in kB needmem=262144 mnt=./huge +exitcode=0 #get pagesize and freepages from /proc/meminfo while read name size unit; do @@ -41,6 +42,7 @@ echo "--------------------" ./hugepage-mmap if [ $? -ne 0 ]; then echo "[FAIL]" + exitcode=1 else echo "[PASS]" fi @@ -55,6 +57,7 @@ echo "--------------------" ./hugepage-shm if [ $? -ne 0 ]; then echo "[FAIL]" + exitcode=1 else echo "[PASS]" fi @@ -67,6 +70,7 @@ echo "--------------------" ./map_hugetlb if [ $? -ne 0 ]; then echo "[FAIL]" + exitcode=1 else echo "[PASS]" fi @@ -75,3 +79,4 @@ fi umount $mnt rm -rf $mnt echo $nr_hugepgs > /proc/sys/vm/nr_hugepages +exit $exitcode -- cgit v1.2.3 From fc256f04eb82abcd2bd828fee4af0c36763ffee0 Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Wed, 3 Jul 2013 15:09:18 -0700 Subject: self-test: fix make clean thuge-gen was forgotten. Fix it by removing the duplication, so we don't get too many repeats. Signed-off-by: Joern Engel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- tools/testing/selftests/vm/Makefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/vm/Makefile b/tools/testing/selftests/vm/Makefile index 7d4792799043..cb3f5f2e0f5f 100644 --- a/tools/testing/selftests/vm/Makefile +++ b/tools/testing/selftests/vm/Makefile @@ -2,8 +2,9 @@ CC = $(CROSS_COMPILE)gcc CFLAGS = -Wall +BINARIES = hugepage-mmap hugepage-shm map_hugetlb thuge-gen -all: hugepage-mmap hugepage-shm map_hugetlb thuge-gen +all: $(BINARIES) %: %.c $(CC) $(CFLAGS) -o $@ $^ @@ -11,4 +12,4 @@ run_tests: all @/bin/sh ./run_vmtests || (echo "vmtests: [FAIL]"; exit 1) clean: - $(RM) hugepage-mmap hugepage-shm map_hugetlb + $(RM) $(BINARIES) -- cgit v1.2.3 From 7e50533d4b84289e4f01de56d6f98e9c64e2229e Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Wed, 3 Jul 2013 15:09:19 -0700 Subject: selftests: add hugetlbfstest As the confusing naming indicates, this test has some overlap with pre-existing tests. Would be nice to merge them eventually. But since it is only test code, cleanliness is much less important than mere existence. Signed-off-by: Joern Engel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- tools/testing/selftests/vm/Makefile | 2 +- tools/testing/selftests/vm/hugetlbfstest.c | 84 ++++++++++++++++++++++++++++++ tools/testing/selftests/vm/run_vmtests | 11 ++++ 3 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/vm/hugetlbfstest.c diff --git a/tools/testing/selftests/vm/Makefile b/tools/testing/selftests/vm/Makefile index cb3f5f2e0f5f..3f94e1afd6cf 100644 --- a/tools/testing/selftests/vm/Makefile +++ b/tools/testing/selftests/vm/Makefile @@ -2,7 +2,7 @@ CC = $(CROSS_COMPILE)gcc CFLAGS = -Wall -BINARIES = hugepage-mmap hugepage-shm map_hugetlb thuge-gen +BINARIES = hugepage-mmap hugepage-shm map_hugetlb thuge-gen hugetlbfstest all: $(BINARIES) %: %.c diff --git a/tools/testing/selftests/vm/hugetlbfstest.c b/tools/testing/selftests/vm/hugetlbfstest.c new file mode 100644 index 000000000000..ea40ff8c2391 --- /dev/null +++ b/tools/testing/selftests/vm/hugetlbfstest.c @@ -0,0 +1,84 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef unsigned long long u64; + +static size_t length = 1 << 24; + +static u64 read_rss(void) +{ + char buf[4096], *s = buf; + int i, fd; + u64 rss; + + fd = open("/proc/self/statm", O_RDONLY); + assert(fd > 2); + memset(buf, 0, sizeof(buf)); + read(fd, buf, sizeof(buf) - 1); + for (i = 0; i < 1; i++) + s = strchr(s, ' ') + 1; + rss = strtoull(s, NULL, 10); + return rss << 12; /* assumes 4k pagesize */ +} + +static void do_mmap(int fd, int extra_flags, int unmap) +{ + int *p; + int flags = MAP_PRIVATE | MAP_POPULATE | extra_flags; + u64 before, after; + + before = read_rss(); + p = mmap(NULL, length, PROT_READ | PROT_WRITE, flags, fd, 0); + assert(p != MAP_FAILED || + !"mmap returned an unexpected error"); + after = read_rss(); + assert(llabs(after - before - length) < 0x40000 || + !"rss didn't grow as expected"); + if (!unmap) + return; + munmap(p, length); + after = read_rss(); + assert(llabs(after - before) < 0x40000 || + !"rss didn't shrink as expected"); +} + +static int open_file(const char *path) +{ + int fd, err; + + unlink(path); + fd = open(path, O_CREAT | O_RDWR | O_TRUNC | O_EXCL + | O_LARGEFILE | O_CLOEXEC, 0600); + assert(fd > 2); + unlink(path); + err = ftruncate(fd, length); + assert(!err); + return fd; +} + +int main(void) +{ + int hugefd, fd; + + fd = open_file("/dev/shm/hugetlbhog"); + hugefd = open_file("/hugepages/hugetlbhog"); + + system("echo 100 > /proc/sys/vm/nr_hugepages"); + do_mmap(-1, MAP_ANONYMOUS, 1); + do_mmap(fd, 0, 1); + do_mmap(-1, MAP_ANONYMOUS | MAP_HUGETLB, 1); + do_mmap(hugefd, 0, 1); + do_mmap(hugefd, MAP_HUGETLB, 1); + /* Leak the last one to test do_exit() */ + do_mmap(-1, MAP_ANONYMOUS | MAP_HUGETLB, 0); + printf("oll korrekt.\n"); + return 0; +} diff --git a/tools/testing/selftests/vm/run_vmtests b/tools/testing/selftests/vm/run_vmtests index 7a9072d52e70..c87b6812300d 100644 --- a/tools/testing/selftests/vm/run_vmtests +++ b/tools/testing/selftests/vm/run_vmtests @@ -75,6 +75,17 @@ else echo "[PASS]" fi +echo "--------------------" +echo "running hugetlbfstest" +echo "--------------------" +./hugetlbfstest +if [ $? -ne 0 ]; then + echo "[FAIL]" + exitcode=1 +else + echo "[PASS]" +fi + #cleanup umount $mnt rm -rf $mnt -- cgit v1.2.3 From c7fed9cf614aa1dc561ad3334b02e896d042068f Mon Sep 17 00:00:00 2001 From: Ramkumar Ramachandra Date: Wed, 3 Jul 2013 15:09:20 -0700 Subject: selftests: add .gitignore for vm Signed-off-by: Ramkumar Ramachandra Cc: Dave Young Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- tools/testing/selftests/vm/.gitignore | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 tools/testing/selftests/vm/.gitignore diff --git a/tools/testing/selftests/vm/.gitignore b/tools/testing/selftests/vm/.gitignore new file mode 100644 index 000000000000..ff1bb16cec4f --- /dev/null +++ b/tools/testing/selftests/vm/.gitignore @@ -0,0 +1,4 @@ +hugepage-mmap +hugepage-shm +map_hugetlb +thuge-gen -- cgit v1.2.3 From 1b7871550330c8777c45e07b84781529f27e2b3c Mon Sep 17 00:00:00 2001 From: Ramkumar Ramachandra Date: Wed, 3 Jul 2013 15:09:21 -0700 Subject: selftests: fix clean target in kcmp Makefile Signed-off-by: Ramkumar Ramachandra Cc: Dave Young Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- tools/testing/selftests/kcmp/Makefile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/testing/selftests/kcmp/Makefile b/tools/testing/selftests/kcmp/Makefile index 56eb5523dbb8..d7d6bbeeff2f 100644 --- a/tools/testing/selftests/kcmp/Makefile +++ b/tools/testing/selftests/kcmp/Makefile @@ -25,5 +25,4 @@ run_tests: all @./kcmp_test || echo "kcmp_test: [FAIL]" clean: - rm -fr ./run_test - rm -fr ./test-file + $(RM) kcmp_test kcmp-test-file -- cgit v1.2.3 From 94d090013e82ba7e8da74f042e3b88406479706c Mon Sep 17 00:00:00 2001 From: Ramkumar Ramachandra Date: Wed, 3 Jul 2013 15:09:22 -0700 Subject: selftests: add .gitignore for kcmp Signed-off-by: Ramkumar Ramachandra Cc: Dave Young Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- tools/testing/selftests/kcmp/.gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 tools/testing/selftests/kcmp/.gitignore diff --git a/tools/testing/selftests/kcmp/.gitignore b/tools/testing/selftests/kcmp/.gitignore new file mode 100644 index 000000000000..5a9b3732b2de --- /dev/null +++ b/tools/testing/selftests/kcmp/.gitignore @@ -0,0 +1,2 @@ +kcmp_test +kcmp-test-file -- cgit v1.2.3 From 9307c29524502c21f0e8a6d96d850b2f5bc0bd9a Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Wed, 3 Jul 2013 15:09:23 -0700 Subject: tools/testing/selftests: don't assume the x bit is set on scripts The x bit can easily get lost (patch(1) loses it, for example). Reported-by: Ramkumar Ramachandra Cc: Dave Young Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- tools/testing/selftests/cpu-hotplug/Makefile | 2 +- tools/testing/selftests/memory-hotplug/Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/cpu-hotplug/Makefile b/tools/testing/selftests/cpu-hotplug/Makefile index 12657a5e4bf9..ae5faf9aade2 100644 --- a/tools/testing/selftests/cpu-hotplug/Makefile +++ b/tools/testing/selftests/cpu-hotplug/Makefile @@ -1,6 +1,6 @@ all: run_tests: - @./on-off-test.sh || echo "cpu-hotplug selftests: [FAIL]" + @/bin/sh ./on-off-test.sh || echo "cpu-hotplug selftests: [FAIL]" clean: diff --git a/tools/testing/selftests/memory-hotplug/Makefile b/tools/testing/selftests/memory-hotplug/Makefile index 0f49c3f5f58d..350bfeda3aa8 100644 --- a/tools/testing/selftests/memory-hotplug/Makefile +++ b/tools/testing/selftests/memory-hotplug/Makefile @@ -1,6 +1,6 @@ all: run_tests: - @./on-off-test.sh || echo "memory-hotplug selftests: [FAIL]" + @/bin/sh ./on-off-test.sh || echo "memory-hotplug selftests: [FAIL]" clean: -- cgit v1.2.3 From bc2bebe8de8ed4ba6482c9cc370b0dd72ffe8cd2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 3 Jul 2013 21:48:11 +0200 Subject: alx: remove WoL support Unfortunately, WoL is broken and the system will immediately resume after suspending, and I can't seem to figure out why. Remove WoL support until the issue can be found. Signed-off-by: Johannes Berg Signed-off-by: David S. Miller --- drivers/net/ethernet/atheros/alx/ethtool.c | 36 ------- drivers/net/ethernet/atheros/alx/hw.c | 155 ----------------------------- drivers/net/ethernet/atheros/alx/hw.h | 5 - drivers/net/ethernet/atheros/alx/main.c | 142 +++----------------------- 4 files changed, 15 insertions(+), 323 deletions(-) diff --git a/drivers/net/ethernet/atheros/alx/ethtool.c b/drivers/net/ethernet/atheros/alx/ethtool.c index 926100626d60..45b36507abc1 100644 --- a/drivers/net/ethernet/atheros/alx/ethtool.c +++ b/drivers/net/ethernet/atheros/alx/ethtool.c @@ -201,40 +201,6 @@ static void alx_set_msglevel(struct net_device *netdev, u32 data) alx->msg_enable = data; } -static void alx_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) -{ - struct alx_priv *alx = netdev_priv(netdev); - struct alx_hw *hw = &alx->hw; - - wol->supported = WAKE_MAGIC | WAKE_PHY; - wol->wolopts = 0; - - if (hw->sleep_ctrl & ALX_SLEEP_WOL_MAGIC) - wol->wolopts |= WAKE_MAGIC; - if (hw->sleep_ctrl & ALX_SLEEP_WOL_PHY) - wol->wolopts |= WAKE_PHY; -} - -static int alx_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) -{ - struct alx_priv *alx = netdev_priv(netdev); - struct alx_hw *hw = &alx->hw; - - if (wol->wolopts & ~(WAKE_MAGIC | WAKE_PHY)) - return -EOPNOTSUPP; - - hw->sleep_ctrl = 0; - - if (wol->wolopts & WAKE_MAGIC) - hw->sleep_ctrl |= ALX_SLEEP_WOL_MAGIC; - if (wol->wolopts & WAKE_PHY) - hw->sleep_ctrl |= ALX_SLEEP_WOL_PHY; - - device_set_wakeup_enable(&alx->hw.pdev->dev, hw->sleep_ctrl); - - return 0; -} - const struct ethtool_ops alx_ethtool_ops = { .get_settings = alx_get_settings, .set_settings = alx_set_settings, @@ -242,7 +208,5 @@ const struct ethtool_ops alx_ethtool_ops = { .set_pauseparam = alx_set_pauseparam, .get_msglevel = alx_get_msglevel, .set_msglevel = alx_set_msglevel, - .get_wol = alx_get_wol, - .set_wol = alx_set_wol, .get_link = ethtool_op_get_link, }; diff --git a/drivers/net/ethernet/atheros/alx/hw.c b/drivers/net/ethernet/atheros/alx/hw.c index ea99e5d8743f..1e8c24a3cb4e 100644 --- a/drivers/net/ethernet/atheros/alx/hw.c +++ b/drivers/net/ethernet/atheros/alx/hw.c @@ -332,16 +332,6 @@ void alx_set_macaddr(struct alx_hw *hw, const u8 *addr) alx_write_mem32(hw, ALX_STAD1, val); } -static void alx_enable_osc(struct alx_hw *hw) -{ - u32 val; - - /* rising edge */ - val = alx_read_mem32(hw, ALX_MISC); - alx_write_mem32(hw, ALX_MISC, val & ~ALX_MISC_INTNLOSC_OPEN); - alx_write_mem32(hw, ALX_MISC, val | ALX_MISC_INTNLOSC_OPEN); -} - static void alx_reset_osc(struct alx_hw *hw, u8 rev) { u32 val, val2; @@ -858,66 +848,6 @@ void alx_post_phy_link(struct alx_hw *hw) } } - -/* NOTE: - * 1. phy link must be established before calling this function - * 2. wol option (pattern,magic,link,etc.) is configed before call it. - */ -int alx_pre_suspend(struct alx_hw *hw, int speed, u8 duplex) -{ - u32 master, mac, phy, val; - int err = 0; - - master = alx_read_mem32(hw, ALX_MASTER); - master &= ~ALX_MASTER_PCLKSEL_SRDS; - mac = hw->rx_ctrl; - /* 10/100 half */ - ALX_SET_FIELD(mac, ALX_MAC_CTRL_SPEED, ALX_MAC_CTRL_SPEED_10_100); - mac &= ~(ALX_MAC_CTRL_FULLD | ALX_MAC_CTRL_RX_EN | ALX_MAC_CTRL_TX_EN); - - phy = alx_read_mem32(hw, ALX_PHY_CTRL); - phy &= ~(ALX_PHY_CTRL_DSPRST_OUT | ALX_PHY_CTRL_CLS); - phy |= ALX_PHY_CTRL_RST_ANALOG | ALX_PHY_CTRL_HIB_PULSE | - ALX_PHY_CTRL_HIB_EN; - - /* without any activity */ - if (!(hw->sleep_ctrl & ALX_SLEEP_ACTIVE)) { - err = alx_write_phy_reg(hw, ALX_MII_IER, 0); - if (err) - return err; - phy |= ALX_PHY_CTRL_IDDQ | ALX_PHY_CTRL_POWER_DOWN; - } else { - if (hw->sleep_ctrl & (ALX_SLEEP_WOL_MAGIC | ALX_SLEEP_CIFS)) - mac |= ALX_MAC_CTRL_RX_EN | ALX_MAC_CTRL_BRD_EN; - if (hw->sleep_ctrl & ALX_SLEEP_CIFS) - mac |= ALX_MAC_CTRL_TX_EN; - if (duplex == DUPLEX_FULL) - mac |= ALX_MAC_CTRL_FULLD; - if (speed == SPEED_1000) - ALX_SET_FIELD(mac, ALX_MAC_CTRL_SPEED, - ALX_MAC_CTRL_SPEED_1000); - phy |= ALX_PHY_CTRL_DSPRST_OUT; - err = alx_write_phy_ext(hw, ALX_MIIEXT_ANEG, - ALX_MIIEXT_S3DIG10, - ALX_MIIEXT_S3DIG10_SL); - if (err) - return err; - } - - alx_enable_osc(hw); - hw->rx_ctrl = mac; - alx_write_mem32(hw, ALX_MASTER, master); - alx_write_mem32(hw, ALX_MAC_CTRL, mac); - alx_write_mem32(hw, ALX_PHY_CTRL, phy); - - /* set val of PDLL D3PLLOFF */ - val = alx_read_mem32(hw, ALX_PDLL_TRNS1); - val |= ALX_PDLL_TRNS1_D3PLLOFF_EN; - alx_write_mem32(hw, ALX_PDLL_TRNS1, val); - - return 0; -} - bool alx_phy_configured(struct alx_hw *hw) { u32 cfg, hw_cfg; @@ -990,26 +920,6 @@ int alx_clear_phy_intr(struct alx_hw *hw) return alx_read_phy_reg(hw, ALX_MII_ISR, &isr); } -int alx_config_wol(struct alx_hw *hw) -{ - u32 wol = 0; - int err = 0; - - /* turn on magic packet event */ - if (hw->sleep_ctrl & ALX_SLEEP_WOL_MAGIC) - wol |= ALX_WOL0_MAGIC_EN | ALX_WOL0_PME_MAGIC_EN; - - /* turn on link up event */ - if (hw->sleep_ctrl & ALX_SLEEP_WOL_PHY) { - wol |= ALX_WOL0_LINK_EN | ALX_WOL0_PME_LINK; - /* only link up can wake up */ - err = alx_write_phy_reg(hw, ALX_MII_IER, ALX_IER_LINK_UP); - } - alx_write_mem32(hw, ALX_WOL0, wol); - - return err; -} - void alx_disable_rss(struct alx_hw *hw) { u32 ctrl = alx_read_mem32(hw, ALX_RXQ0); @@ -1121,71 +1031,6 @@ void alx_configure_basic(struct alx_hw *hw) alx_write_mem32(hw, ALX_WRR, val); } -int alx_select_powersaving_speed(struct alx_hw *hw, int *speed, u8 *duplex) -{ - int i, err; - u16 lpa; - - err = alx_read_phy_link(hw); - if (err) - return err; - - if (hw->link_speed == SPEED_UNKNOWN) { - *speed = SPEED_UNKNOWN; - *duplex = DUPLEX_UNKNOWN; - return 0; - } - - err = alx_read_phy_reg(hw, MII_LPA, &lpa); - if (err) - return err; - - if (!(lpa & LPA_LPACK)) { - *speed = hw->link_speed; - return 0; - } - - if (lpa & LPA_10FULL) { - *speed = SPEED_10; - *duplex = DUPLEX_FULL; - } else if (lpa & LPA_10HALF) { - *speed = SPEED_10; - *duplex = DUPLEX_HALF; - } else if (lpa & LPA_100FULL) { - *speed = SPEED_100; - *duplex = DUPLEX_FULL; - } else { - *speed = SPEED_100; - *duplex = DUPLEX_HALF; - } - - if (*speed == hw->link_speed && *duplex == hw->duplex) - return 0; - err = alx_write_phy_reg(hw, ALX_MII_IER, 0); - if (err) - return err; - err = alx_setup_speed_duplex(hw, alx_speed_to_ethadv(*speed, *duplex) | - ADVERTISED_Autoneg, ALX_FC_ANEG | - ALX_FC_RX | ALX_FC_TX); - if (err) - return err; - - /* wait for linkup */ - for (i = 0; i < ALX_MAX_SETUP_LNK_CYCLE; i++) { - msleep(100); - - err = alx_read_phy_link(hw); - if (err < 0) - return err; - if (hw->link_speed != SPEED_UNKNOWN) - break; - } - if (i == ALX_MAX_SETUP_LNK_CYCLE) - return -ETIMEDOUT; - - return 0; -} - bool alx_get_phy_info(struct alx_hw *hw) { u16 devs1, devs2; diff --git a/drivers/net/ethernet/atheros/alx/hw.h b/drivers/net/ethernet/atheros/alx/hw.h index a60e35c1c1b5..96f3b4381e17 100644 --- a/drivers/net/ethernet/atheros/alx/hw.h +++ b/drivers/net/ethernet/atheros/alx/hw.h @@ -418,8 +418,6 @@ struct alx_hw { u8 flowctrl; u32 adv_cfg; - u32 sleep_ctrl; - spinlock_t mdio_lock; struct mdio_if_info mdio; u16 phy_id[2]; @@ -479,14 +477,12 @@ void alx_reset_pcie(struct alx_hw *hw); void alx_enable_aspm(struct alx_hw *hw, bool l0s_en, bool l1_en); int alx_setup_speed_duplex(struct alx_hw *hw, u32 ethadv, u8 flowctrl); void alx_post_phy_link(struct alx_hw *hw); -int alx_pre_suspend(struct alx_hw *hw, int speed, u8 duplex); int alx_read_phy_reg(struct alx_hw *hw, u16 reg, u16 *phy_data); int alx_write_phy_reg(struct alx_hw *hw, u16 reg, u16 phy_data); int alx_read_phy_ext(struct alx_hw *hw, u8 dev, u16 reg, u16 *pdata); int alx_write_phy_ext(struct alx_hw *hw, u8 dev, u16 reg, u16 data); int alx_read_phy_link(struct alx_hw *hw); int alx_clear_phy_intr(struct alx_hw *hw); -int alx_config_wol(struct alx_hw *hw); void alx_cfg_mac_flowcontrol(struct alx_hw *hw, u8 fc); void alx_start_mac(struct alx_hw *hw); int alx_reset_mac(struct alx_hw *hw); @@ -494,7 +490,6 @@ void alx_set_macaddr(struct alx_hw *hw, const u8 *addr); bool alx_phy_configured(struct alx_hw *hw); void alx_configure_basic(struct alx_hw *hw); void alx_disable_rss(struct alx_hw *hw); -int alx_select_powersaving_speed(struct alx_hw *hw, int *speed, u8 *duplex); bool alx_get_phy_info(struct alx_hw *hw); static inline u32 alx_speed_to_ethadv(int speed, u8 duplex) diff --git a/drivers/net/ethernet/atheros/alx/main.c b/drivers/net/ethernet/atheros/alx/main.c index 148b4b976474..0e0b242a9dd4 100644 --- a/drivers/net/ethernet/atheros/alx/main.c +++ b/drivers/net/ethernet/atheros/alx/main.c @@ -706,7 +706,6 @@ static int alx_init_sw(struct alx_priv *alx) alx->rxbuf_size = ALIGN(ALX_RAW_MTU(hw->mtu), 8); alx->tx_ringsz = 256; alx->rx_ringsz = 512; - hw->sleep_ctrl = ALX_SLEEP_WOL_MAGIC | ALX_SLEEP_WOL_PHY; hw->imt = 200; alx->int_mask = ALX_ISR_MISC; hw->dma_chnl = hw->max_dma_chnl; @@ -961,66 +960,6 @@ static int alx_stop(struct net_device *netdev) return 0; } -static int __alx_shutdown(struct pci_dev *pdev, bool *wol_en) -{ - struct alx_priv *alx = pci_get_drvdata(pdev); - struct net_device *netdev = alx->dev; - struct alx_hw *hw = &alx->hw; - int err, speed; - u8 duplex; - - netif_device_detach(netdev); - - if (netif_running(netdev)) - __alx_stop(alx); - -#ifdef CONFIG_PM_SLEEP - err = pci_save_state(pdev); - if (err) - return err; -#endif - - err = alx_select_powersaving_speed(hw, &speed, &duplex); - if (err) - return err; - err = alx_clear_phy_intr(hw); - if (err) - return err; - err = alx_pre_suspend(hw, speed, duplex); - if (err) - return err; - err = alx_config_wol(hw); - if (err) - return err; - - *wol_en = false; - if (hw->sleep_ctrl & ALX_SLEEP_ACTIVE) { - netif_info(alx, wol, netdev, - "wol: ctrl=%X, speed=%X\n", - hw->sleep_ctrl, speed); - device_set_wakeup_enable(&pdev->dev, true); - *wol_en = true; - } - - pci_disable_device(pdev); - - return 0; -} - -static void alx_shutdown(struct pci_dev *pdev) -{ - int err; - bool wol_en; - - err = __alx_shutdown(pdev, &wol_en); - if (!err) { - pci_wake_from_d3(pdev, wol_en); - pci_set_power_state(pdev, PCI_D3hot); - } else { - dev_err(&pdev->dev, "shutdown fail %d\n", err); - } -} - static void alx_link_check(struct work_struct *work) { struct alx_priv *alx; @@ -1399,8 +1338,6 @@ static int alx_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto out_unmap; } - device_set_wakeup_enable(&pdev->dev, hw->sleep_ctrl); - netdev_info(netdev, "Qualcomm Atheros AR816x/AR817x Ethernet [%pM]\n", netdev->dev_addr); @@ -1445,22 +1382,12 @@ static void alx_remove(struct pci_dev *pdev) static int alx_suspend(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); - int err; - bool wol_en; - - err = __alx_shutdown(pdev, &wol_en); - if (err) { - dev_err(&pdev->dev, "shutdown fail in suspend %d\n", err); - return err; - } - - if (wol_en) { - pci_prepare_to_sleep(pdev); - } else { - pci_wake_from_d3(pdev, false); - pci_set_power_state(pdev, PCI_D3hot); - } + struct alx_priv *alx = pci_get_drvdata(pdev); + if (!netif_running(alx->dev)) + return 0; + netif_device_detach(alx->dev); + __alx_stop(alx); return 0; } @@ -1468,49 +1395,20 @@ static int alx_resume(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); struct alx_priv *alx = pci_get_drvdata(pdev); - struct net_device *netdev = alx->dev; - struct alx_hw *hw = &alx->hw; - int err; - - pci_set_power_state(pdev, PCI_D0); - pci_restore_state(pdev); - pci_save_state(pdev); - - pci_enable_wake(pdev, PCI_D3hot, 0); - pci_enable_wake(pdev, PCI_D3cold, 0); - - hw->link_speed = SPEED_UNKNOWN; - alx->int_mask = ALX_ISR_MISC; - - alx_reset_pcie(hw); - alx_reset_phy(hw); - - err = alx_reset_mac(hw); - if (err) { - netif_err(alx, hw, alx->dev, - "resume:reset_mac fail %d\n", err); - return -EIO; - } - - err = alx_setup_speed_duplex(hw, hw->adv_cfg, hw->flowctrl); - if (err) { - netif_err(alx, hw, alx->dev, - "resume:setup_speed_duplex fail %d\n", err); - return -EIO; - } - - if (netif_running(netdev)) { - err = __alx_open(alx, true); - if (err) - return err; - } - - netif_device_attach(netdev); - return err; + if (!netif_running(alx->dev)) + return 0; + netif_device_attach(alx->dev); + return __alx_open(alx, true); } + +static SIMPLE_DEV_PM_OPS(alx_pm_ops, alx_suspend, alx_resume); +#define ALX_PM_OPS (&alx_pm_ops) +#else +#define ALX_PM_OPS NULL #endif + static pci_ers_result_t alx_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state) { @@ -1553,8 +1451,6 @@ static pci_ers_result_t alx_pci_error_slot_reset(struct pci_dev *pdev) } pci_set_master(pdev); - pci_enable_wake(pdev, PCI_D3hot, 0); - pci_enable_wake(pdev, PCI_D3cold, 0); alx_reset_pcie(hw); if (!alx_reset_mac(hw)) @@ -1590,13 +1486,6 @@ static const struct pci_error_handlers alx_err_handlers = { .resume = alx_pci_error_resume, }; -#ifdef CONFIG_PM_SLEEP -static SIMPLE_DEV_PM_OPS(alx_pm_ops, alx_suspend, alx_resume); -#define ALX_PM_OPS (&alx_pm_ops) -#else -#define ALX_PM_OPS NULL -#endif - static DEFINE_PCI_DEVICE_TABLE(alx_pci_tbl) = { { PCI_VDEVICE(ATTANSIC, ALX_DEV_ID_AR8161), .driver_data = ALX_DEV_QUIRK_MSI_INTX_DISABLE_BUG }, @@ -1614,7 +1503,6 @@ static struct pci_driver alx_driver = { .id_table = alx_pci_tbl, .probe = alx_probe, .remove = alx_remove, - .shutdown = alx_shutdown, .err_handler = &alx_err_handlers, .driver.pm = ALX_PM_OPS, }; -- cgit v1.2.3 From cdffcf1bc721032d261a7a9da353b41f0d830410 Mon Sep 17 00:00:00 2001 From: Jim Baxter Date: Tue, 2 Jul 2013 22:52:56 +0100 Subject: net: fec: Add VLAN receive HW support. This enables the driver to take advantage of the FEC VLAN indicator to improve performance. Signed-off-by: Jim Baxter Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fec.h | 3 ++ drivers/net/ethernet/freescale/fec_main.c | 62 ++++++++++++++++++++++++++----- 2 files changed, 55 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h index 8362a0399afb..2b0a0ea4f8e7 100644 --- a/drivers/net/ethernet/freescale/fec.h +++ b/drivers/net/ethernet/freescale/fec.h @@ -203,6 +203,9 @@ struct bufdesc_ex { #define BD_ENET_RX_CL ((ushort)0x0001) #define BD_ENET_RX_STATS ((ushort)0x013f) /* All status bits */ +/* Enhanced buffer descriptor control/status used by Ethernet receive */ +#define BD_ENET_RX_VLAN 0x00000004 + /* Buffer descriptor control/status used by Ethernet transmit. */ #define BD_ENET_TX_READY ((ushort)0x8000) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 1f7ff2268bd0..d3ad5ea711d3 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -54,6 +54,7 @@ #include #include #include +#include #include @@ -90,6 +91,8 @@ static void set_multicast_list(struct net_device *ndev); #define FEC_QUIRK_HAS_BUFDESC_EX (1 << 4) /* Controller has hardware checksum support */ #define FEC_QUIRK_HAS_CSUM (1 << 5) +/* Controller has hardware vlan support */ +#define FEC_QUIRK_HAS_VLAN (1 << 6) static struct platform_device_id fec_devtype[] = { { @@ -108,7 +111,8 @@ static struct platform_device_id fec_devtype[] = { }, { .name = "imx6q-fec", .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | - FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM, + FEC_QUIRK_HAS_BUFDESC_EX | FEC_QUIRK_HAS_CSUM | + FEC_QUIRK_HAS_VLAN, }, { .name = "mvf600-fec", .driver_data = FEC_QUIRK_ENET_MAC, @@ -179,11 +183,11 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); #define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII) #define FEC_RX_DISABLED_IMASK (FEC_DEFAULT_IMASK & (~FEC_ENET_RXF)) -/* The FEC stores dest/src/type, data, and checksum for receive packets. +/* The FEC stores dest/src/type/vlan, data, and checksum for receive packets. */ -#define PKT_MAXBUF_SIZE 1518 +#define PKT_MAXBUF_SIZE 1522 #define PKT_MINBUF_SIZE 64 -#define PKT_MAXBLR_SIZE 1520 +#define PKT_MAXBLR_SIZE 1536 /* FEC receive acceleration */ #define FEC_RACC_IPDIS (1 << 1) @@ -806,6 +810,9 @@ fec_enet_rx(struct net_device *ndev, int budget) ushort pkt_len; __u8 *data; int pkt_received = 0; + struct bufdesc_ex *ebdp = NULL; + bool vlan_packet_rcvd = false; + u16 vlan_tag; #ifdef CONFIG_M532x flush_cache_all(); @@ -869,6 +876,24 @@ fec_enet_rx(struct net_device *ndev, int budget) if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME) swap_buffer(data, pkt_len); + /* Extract the enhanced buffer descriptor */ + ebdp = NULL; + if (fep->bufdesc_ex) + ebdp = (struct bufdesc_ex *)bdp; + + /* If this is a VLAN packet remove the VLAN Tag */ + vlan_packet_rcvd = false; + if ((ndev->features & NETIF_F_HW_VLAN_CTAG_RX) && + fep->bufdesc_ex && (ebdp->cbd_esc & BD_ENET_RX_VLAN)) { + /* Push and remove the vlan tag */ + struct vlan_hdr *vlan_header = + (struct vlan_hdr *) (data + ETH_HLEN); + vlan_tag = ntohs(vlan_header->h_vlan_TCI); + pkt_len -= VLAN_HLEN; + + vlan_packet_rcvd = true; + } + /* This does 16 byte alignment, exactly what we need. * The packet length includes FCS, but we don't want to * include that when passing upstream as it messes up @@ -879,9 +904,18 @@ fec_enet_rx(struct net_device *ndev, int budget) if (unlikely(!skb)) { ndev->stats.rx_dropped++; } else { + int payload_offset = (2 * ETH_ALEN); skb_reserve(skb, NET_IP_ALIGN); skb_put(skb, pkt_len - 4); /* Make room */ - skb_copy_to_linear_data(skb, data, pkt_len - 4); + + /* Extract the frame data without the VLAN header. */ + skb_copy_to_linear_data(skb, data, (2 * ETH_ALEN)); + if (vlan_packet_rcvd) + payload_offset = (2 * ETH_ALEN) + VLAN_HLEN; + skb_copy_to_linear_data_offset(skb, (2 * ETH_ALEN), + data + payload_offset, + pkt_len - 4 - (2 * ETH_ALEN)); + skb->protocol = eth_type_trans(skb, ndev); /* Get receive timestamp from the skb */ @@ -889,8 +923,6 @@ fec_enet_rx(struct net_device *ndev, int budget) struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb); unsigned long flags; - struct bufdesc_ex *ebdp = - (struct bufdesc_ex *)bdp; memset(shhwtstamps, 0, sizeof(*shhwtstamps)); @@ -901,9 +933,7 @@ fec_enet_rx(struct net_device *ndev, int budget) } if (fep->bufdesc_ex && - (fep->csum_flags & FLAG_RX_CSUM_ENABLED)) { - struct bufdesc_ex *ebdp = - (struct bufdesc_ex *)bdp; + (fep->csum_flags & FLAG_RX_CSUM_ENABLED)) { if (!(ebdp->cbd_esc & FLAG_RX_CSUM_ERROR)) { /* don't check it */ skb->ip_summed = CHECKSUM_UNNECESSARY; @@ -912,6 +942,12 @@ fec_enet_rx(struct net_device *ndev, int budget) } } + /* Handle received VLAN packets */ + if (vlan_packet_rcvd) + __vlan_hwaccel_put_tag(skb, + htons(ETH_P_8021Q), + vlan_tag); + if (!skb_defer_rx_timestamp(skb)) napi_gro_receive(&fep->napi, skb); } @@ -1924,6 +1960,12 @@ static int fec_enet_init(struct net_device *ndev) writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK); netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi, FEC_NAPI_WEIGHT); + if (id_entry->driver_data & FEC_QUIRK_HAS_VLAN) { + /* enable hw VLAN support */ + ndev->features |= NETIF_F_HW_VLAN_CTAG_RX; + ndev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX; + } + if (id_entry->driver_data & FEC_QUIRK_HAS_CSUM) { /* enable hw accelerator */ ndev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM -- cgit v1.2.3 From 4bc41b84e9b4d904f68cba2dbe0c60a5428c27c4 Mon Sep 17 00:00:00 2001 From: Joe Stringer Date: Wed, 3 Jul 2013 16:04:25 +0900 Subject: core: Copy inner_protocol in copy_skb_header() inner_protocol was added to struct sk_buff in 0d89d2035fe063461a5ddb609b2c12e7fb006e44 ("MPLS: Add limited GSO support"), which is scheduled to be included in v3.11. That patch did not update __copy_skb_header to copy the inner_protocol. Signed-off-by: Joe Stringer Signed-off-by: Simon Horman Signed-off-by: David S. Miller --- net/core/skbuff.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 77971a35d6e1..724bb7cb173f 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -697,6 +697,7 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old) new->transport_header = old->transport_header; new->network_header = old->network_header; new->mac_header = old->mac_header; + new->inner_protocol = old->inner_protocol; new->inner_transport_header = old->inner_transport_header; new->inner_network_header = old->inner_network_header; new->inner_mac_header = old->inner_mac_header; -- cgit v1.2.3 From 9caf83c32be1dabf000ab7dc8430fba4f7d89e65 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 3 Jul 2013 12:37:46 +0300 Subject: net/mlx4: fix small memory leak on error "work" needs to be freed before returning on this error path. Signed-off-by: Dan Carpenter Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/cmd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/mellanox/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c index 707a7d0d08e1..299d0184f983 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c @@ -1572,6 +1572,7 @@ int mlx4_master_immediate_activate_vlan_qos(struct mlx4_priv *priv, vp_admin->default_vlan, &admin_vlan_ix); if (err) { + kfree(work); mlx4_warn((&priv->dev), "No vlan resources slave %d, port %d\n", slave, port); -- cgit v1.2.3 From 9eb5bf838d06aa6ddebe4aca6b5cedcf2eb53b86 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 3 Jul 2013 05:02:22 -0700 Subject: net: sock: fix TCP_SKB_MIN_TRUESIZE commit eea86af6b1e18d ("net: sock: adapt SOCK_MIN_RCVBUF and SOCK_MIN_SNDBUF") forgot the sk_buff alignment taken into account in __alloc_skb() : skb->truesize = SKB_TRUESIZE(size); While above commit fixed the sender issue, the receiver is still dropping the second packet (on loopback device), because the receiver socket can not really hold two skbs : First packet truesize already is above sk_rcvbuf, so even TCP coalescing cannot help. On a typical 64bit build, each tcp skb truesize is 2304, instead of 2272 Signed-off-by: Eric Dumazet Cc: Daniel Borkmann Cc: Neal Cardwell Acked-by: Neal Cardwell Tested-by: Neal Cardwell Signed-off-by: David S. Miller --- include/net/sock.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/net/sock.h b/include/net/sock.h index ea6206ccc896..95a5a2c6925a 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -2052,7 +2052,7 @@ static inline void sk_wake_async(struct sock *sk, int how, int band) * Note: for send buffers, TCP works better if we can build two skbs at * minimum. */ -#define TCP_SKB_MIN_TRUESIZE (2048 + sizeof(struct sk_buff)) +#define TCP_SKB_MIN_TRUESIZE (2048 + SKB_DATA_ALIGN(sizeof(struct sk_buff))) #define SOCK_MIN_SNDBUF (TCP_SKB_MIN_TRUESIZE * 2) #define SOCK_MIN_RCVBUF TCP_SKB_MIN_TRUESIZE -- cgit v1.2.3 From 36b7bfe09b6deb71bf387852465245783c9a6208 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 3 Jul 2013 14:04:14 -0700 Subject: netem: fix possible NULL deref in netem_dequeue() commit aec0a40a6f7884 ("netem: use rb tree to implement the time queue") added a regression if a child qdisc is attached to netem, as we perform a NULL dereference. Fix this by adding a temporary variable to cache netem_skb_cb(skb)->time_to_send. Reported-by: Dan Carpenter Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/sched/sch_netem.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index ed0082cf8eff..82f6016d89ab 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -554,10 +554,13 @@ deliver: } p = rb_first(&q->t_root); if (p) { + psched_time_t time_to_send; + skb = netem_rb_to_skb(p); /* if more time remaining? */ - if (netem_skb_cb(skb)->time_to_send <= psched_get_time()) { + time_to_send = netem_skb_cb(skb)->time_to_send; + if (time_to_send <= psched_get_time()) { rb_erase(p, &q->t_root); sch->q.qlen--; @@ -593,8 +596,7 @@ deliver: if (skb) goto deliver; } - qdisc_watchdog_schedule(&q->watchdog, - netem_skb_cb(skb)->time_to_send); + qdisc_watchdog_schedule(&q->watchdog, time_to_send); } if (q->qdisc) { -- cgit v1.2.3 From 4a88f73f14f6d6c94616538427e1235a6d0a5885 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 2 Jul 2013 09:18:39 +0200 Subject: drm/prime: fix up handle_to_fd ioctl return value In commit da34242e5e0638312130f5bd5d2d277afbc6f806 Author: YoungJun Cho Date: Wed Jun 26 10:21:42 2013 +0900 drm/prime: add return check for dma_buf_fd the failure case handling was fixed up. But in the case when we already had the buffer exported it changed the return value: Previously we've return 0 on success, now we return the fd. This ABI change has been caught by i-g-t/prime_self_import/with_one_bo. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=66436 Cc: YoungJun Cho Cc: Seung-Woo Kim Cc: Kyungmin Park Tested-by: lu hua Signed-off-by: Daniel Vetter Reviewed-by: YoungJun Cho Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_prime.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index 52709f2171fb..1e0de41f085c 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -347,10 +347,13 @@ int drm_gem_prime_handle_to_fd(struct drm_device *dev, out_have_obj: get_dma_buf(dmabuf); ret = dma_buf_fd(dmabuf, flags); - if (ret < 0) + if (ret < 0) { dma_buf_put(dmabuf); - else + } else { *prime_fd = ret; + ret = 0; + } + goto out; fail_rm_handle: -- cgit v1.2.3 From 2c54b133576fefaa54a0ab939a94af348ec54499 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 1 Jul 2013 22:01:02 +0200 Subject: drm/mm: fix debug table BUG In commit 3a359f0b21ab218c1bf7a6a1b638b6fd143d0b99 Author: Daniel Vetter Date: Sat Apr 20 12:08:11 2013 +0200 drm/mm: fix dump table BUG I've failed to fix both instances of the regression introduced in commit 9e8944ab564f2e3dde90a518cd32048c58918608 Author: Chris Wilson Date: Thu Nov 15 11:32:17 2012 +0000 drm: Introduce an iterator over holes in the drm_mm range manager Patch this up in the same way by extracting the hole debug logic into it's own function, since that'll also clarify the logic a bit. Cc: Chris Wilson Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_mm.c | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index 7917729ee61d..e05c3cea322c 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c @@ -714,36 +714,37 @@ void drm_mm_takedown(struct drm_mm * mm) } EXPORT_SYMBOL(drm_mm_takedown); -void drm_mm_debug_table(struct drm_mm *mm, const char *prefix) +static unsigned long drm_mm_debug_hole(struct drm_mm_node *entry, + const char *prefix) { - struct drm_mm_node *entry; - unsigned long total_used = 0, total_free = 0, total = 0; unsigned long hole_start, hole_end, hole_size; - hole_start = drm_mm_hole_node_start(&mm->head_node); - hole_end = drm_mm_hole_node_end(&mm->head_node); - hole_size = hole_end - hole_start; - if (hole_size) + if (entry->hole_follows) { + hole_start = drm_mm_hole_node_start(entry); + hole_end = drm_mm_hole_node_end(entry); + hole_size = hole_end - hole_start; printk(KERN_DEBUG "%s 0x%08lx-0x%08lx: %8lu: free\n", prefix, hole_start, hole_end, hole_size); - total_free += hole_size; + return hole_size; + } + + return 0; +} + +void drm_mm_debug_table(struct drm_mm *mm, const char *prefix) +{ + struct drm_mm_node *entry; + unsigned long total_used = 0, total_free = 0, total = 0; + + total_free += drm_mm_debug_hole(&mm->head_node, prefix); drm_mm_for_each_node(entry, mm) { printk(KERN_DEBUG "%s 0x%08lx-0x%08lx: %8lu: used\n", prefix, entry->start, entry->start + entry->size, entry->size); total_used += entry->size; - - if (entry->hole_follows) { - hole_start = drm_mm_hole_node_start(entry); - hole_end = drm_mm_hole_node_end(entry); - hole_size = hole_end - hole_start; - printk(KERN_DEBUG "%s 0x%08lx-0x%08lx: %8lu: free\n", - prefix, hole_start, hole_end, - hole_size); - total_free += hole_size; - } + total_free += drm_mm_debug_hole(entry, prefix); } total = total_free + total_used; -- cgit v1.2.3 From e5ad449be684492f4f58dc169f20926670da7cae Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 1 Jul 2013 22:01:04 +0200 Subject: drm/mm: WARN for unclean mm takedown The usual drm driver has tons of different drm_mm memory managers so the drm error message in dmesg is pretty useless. WARN instead so that we have the full backtrace. Signed-off-by: Daniel Vetter Reviewed-by: Ben Widawsky Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_mm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index e05c3cea322c..543b9b3171d3 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c @@ -697,8 +697,8 @@ void drm_mm_takedown(struct drm_mm * mm) { struct drm_mm_node *entry, *next; - if (!list_empty(&mm->head_node.node_list)) { - DRM_ERROR("Memory manager not clean. Delaying takedown\n"); + if (WARN(!list_empty(&mm->head_node.node_list), + "Memory manager not clean. Delaying takedown\n")) { return; } -- cgit v1.2.3 From a1bdc45580fc19e968b32ad27cd7e476a4aa58f6 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Thu, 4 Jul 2013 00:52:49 +0900 Subject: net: ipv6: add missing lock in ping_v6_sendmsg Signed-off-by: Lorenzo Colitti Signed-off-by: David S. Miller --- net/ipv6/ping.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c index 2b52046e1263..10b975577e91 100644 --- a/net/ipv6/ping.c +++ b/net/ipv6/ping.c @@ -174,6 +174,7 @@ int ping_v6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, if (hlimit < 0) hlimit = ip6_dst_hoplimit(dst); + lock_sock(sk); err = ip6_append_data(sk, ping_getfrag, &pfh, len, 0, hlimit, np->tclass, NULL, &fl6, rt, @@ -188,6 +189,7 @@ int ping_v6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, (struct icmp6hdr *) &pfh.icmph, len); } + release_sock(sk); return err; } -- cgit v1.2.3 From fbfe80c890a1dc521d0b629b870e32fcffff0da5 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Thu, 4 Jul 2013 00:12:40 +0900 Subject: net: ipv6: fix wrong ping_v6_sendmsg return value ping_v6_sendmsg currently returns 0 on success. It should return the number of bytes written instead. Signed-off-by: Lorenzo Colitti Signed-off-by: David S. Miller --- net/ipv6/ping.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c index 10b975577e91..18f19df4189f 100644 --- a/net/ipv6/ping.c +++ b/net/ipv6/ping.c @@ -191,7 +191,10 @@ int ping_v6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, } release_sock(sk); - return err; + if (err) + return err; + + return len; } #ifdef CONFIG_PROC_FS -- cgit v1.2.3 From cf4b91f2d9de7ccc7ca0fb0931d55aaf8569fbc0 Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Mon, 1 Jul 2013 16:06:02 -0600 Subject: drm: Convert drm class driver from legacy pm ops to dev_pm_ops Convert drivers/gpu/drm class to use dev_pm_ops for power management and remove Legacy PM ops hooks. With this change, drm class registers suspend/resume callbacks via class->pm (dev_pm_ops) instead of Legacy class->suspend/resume. When __device_suspend() runs call-backs, it will find class->pm ops for the drm class. drm_class_suspend() hook calls driver legacy ops with the state information. e.g: drm_class_suspend() calls into driver suspend routines via drm_dev->driver->suspend(drm_dev, state). Once drm_class_suspend() is converted to dev_pm_ops, it will no longer have access to pm_transition which it has to pass into driver legacy suspend calls. A new freeze and suspend hooks are added to address the not having access to the state information. The new freeze and suspend hooks simply call __drm_class_suspend() with the appropriate pm state information. __drm_class_suspend() is the original suspend hook with a new name. Signed-off-by: Shuah Khan Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_sysfs.c | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c index 02296653a058..2290b3b73832 100644 --- a/drivers/gpu/drm/drm_sysfs.c +++ b/drivers/gpu/drm/drm_sysfs.c @@ -30,14 +30,14 @@ static struct device_type drm_sysfs_device_minor = { }; /** - * drm_class_suspend - DRM class suspend hook + * __drm_class_suspend - internal DRM class suspend routine * @dev: Linux device to suspend * @state: power state to enter * * Just figures out what the actual struct drm_device associated with * @dev is and calls its suspend hook, if present. */ -static int drm_class_suspend(struct device *dev, pm_message_t state) +static int __drm_class_suspend(struct device *dev, pm_message_t state) { if (dev->type == &drm_sysfs_device_minor) { struct drm_minor *drm_minor = to_drm_minor(dev); @@ -51,6 +51,26 @@ static int drm_class_suspend(struct device *dev, pm_message_t state) return 0; } +/** + * drm_class_suspend - internal DRM class suspend hook. Simply calls + * __drm_class_suspend() with the correct pm state. + * @dev: Linux device to suspend + */ +static int drm_class_suspend(struct device *dev) +{ + return __drm_class_suspend(dev, PMSG_SUSPEND); +} + +/** + * drm_class_freeze - internal DRM class freeze hook. Simply calls + * __drm_class_suspend() with the correct pm state. + * @dev: Linux device to freeze + */ +static int drm_class_freeze(struct device *dev) +{ + return __drm_class_suspend(dev, PMSG_FREEZE); +} + /** * drm_class_resume - DRM class resume hook * @dev: Linux device to resume @@ -72,6 +92,12 @@ static int drm_class_resume(struct device *dev) return 0; } +static const struct dev_pm_ops drm_class_dev_pm_ops = { + .suspend = drm_class_suspend, + .resume = drm_class_resume, + .freeze = drm_class_freeze, +}; + static char *drm_devnode(struct device *dev, umode_t *mode) { return kasprintf(GFP_KERNEL, "dri/%s", dev_name(dev)); @@ -106,8 +132,7 @@ struct class *drm_sysfs_create(struct module *owner, char *name) goto err_out; } - class->suspend = drm_class_suspend; - class->resume = drm_class_resume; + class->pm = &drm_class_dev_pm_ops; err = class_create_file(class, &class_attr_version.attr); if (err) -- cgit v1.2.3 From 3630d40067a21d4dfbadc6002bb469ce26ac5d52 Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Wed, 3 Jul 2013 20:45:04 +0200 Subject: ipv6: rt6_check_neigh should successfully verify neigh if no NUD information are available After the removal of rt->n we do not create a neighbour entry at route insertion time (rt6_bind_neighbour is gone). As long as no neighbour is created because of "useful traffic" we skip this routing entry because rt6_check_neigh cannot pick up a valid neighbour (neigh == NULL) and thus returns false. This change was introduced by commit 887c95cc1da53f66a5890fdeab13414613010097 ("ipv6: Complete neighbour entry removal from dst_entry.") To quote RFC4191: "If the host has no information about the router's reachability, then the host assumes the router is reachable." and also: "A host MUST NOT probe a router's reachability in the absence of useful traffic that the host would have sent to the router if it were reachable." So, just assume the router is reachable and let's rt6_probe do the rest. We don't need to create a neighbour on route insertion time. If we don't compile with CONFIG_IPV6_ROUTER_PREF (RFC4191 support) a neighbour is only valid if its nud_state is NUD_VALID. I did not find any references that we should probe the router on route insertion time via the other RFCs. So skip this route in that case. v2: a) use IS_ENABLED instead of #ifdefs (thanks to Sergei Shtylyov) Reported-by: Pierre Emeriaud Cc: YOSHIFUJI Hideaki Signed-off-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- net/ipv6/route.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 9ff0b78a9c15..bd5fd7054031 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -551,6 +551,8 @@ static inline bool rt6_check_neigh(struct rt6_info *rt) ret = true; #endif read_unlock(&neigh->lock); + } else if (IS_ENABLED(CONFIG_IPV6_ROUTER_PREF)) { + ret = true; } rcu_read_unlock_bh(); -- cgit v1.2.3 From fe2ef780669d9bbd2f921b247dc79266b00b99ab Mon Sep 17 00:00:00 2001 From: Seung-Woo Kim Date: Tue, 2 Jul 2013 17:57:04 +0900 Subject: drm: add assertion for checking null edid to drm_edid_block_valid If raw_edid of drm_edid_block_vaild() is null, it will crash, so checking in bad label is removed and instead assertion is added at the top of the function. The type of return for the function is bool, so it fixes to return true and false instead of 1 and 0. Signed-off-by: Seung-Woo Kim Signed-off-by: Kyungmin Park Reviewed-by: Chris Wilson Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_edid.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 2dc1a60a867d..95d6f4b6967c 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -968,6 +968,9 @@ bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid) u8 csum = 0; struct edid *edid = (struct edid *)raw_edid; + if (WARN_ON(!raw_edid)) + return false; + if (edid_fixup > 8 || edid_fixup < 0) edid_fixup = 6; @@ -1010,15 +1013,15 @@ bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid) break; } - return 1; + return true; bad: - if (raw_edid && print_bad_edid) { + if (print_bad_edid) { printk(KERN_ERR "Raw EDID:\n"); print_hex_dump(KERN_ERR, " \t", DUMP_PREFIX_NONE, 16, 1, raw_edid, EDID_LENGTH, false); } - return 0; + return false; } EXPORT_SYMBOL(drm_edid_block_valid); -- cgit v1.2.3 From df9b6a9c3333a99d4483e92ca6b225b335567313 Mon Sep 17 00:00:00 2001 From: Seung-Woo Kim Date: Tue, 2 Jul 2013 09:53:28 +0900 Subject: drm: fix error routines in drm_open_helper There are missing parts to handle error in drm_open_helper(). The priv->minor, assigned by idr_find() which can return NULL, should be checked whether it is NULL or not before referencing it. put_pid(), drm_gem_release(), and drm_prime_destory_file_private() should be called when error happens after their pair functions are called. If an error occurs after executing dev->driver->open() which allocates driver specific per-file private data, then the private data should be released. Signed-off-by: YoungJun Cho Signed-off-by: Seung-Woo Kim Signed-off-by: Kyungmin Park Reviewed-by: Chris Wilson Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_fops.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 429e07d0b0f1..3a24385e0368 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -271,6 +271,11 @@ static int drm_open_helper(struct inode *inode, struct file *filp, priv->uid = current_euid(); priv->pid = get_pid(task_pid(current)); priv->minor = idr_find(&drm_minors_idr, minor_id); + if (!priv->minor) { + ret = -ENODEV; + goto out_put_pid; + } + priv->ioctl_count = 0; /* for compatibility root is always authenticated */ priv->authenticated = capable(CAP_SYS_ADMIN); @@ -292,7 +297,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp, if (dev->driver->open) { ret = dev->driver->open(dev, priv); if (ret < 0) - goto out_free; + goto out_prime_destroy; } @@ -304,7 +309,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp, if (!priv->minor->master) { mutex_unlock(&dev->struct_mutex); ret = -ENOMEM; - goto out_free; + goto out_close; } priv->is_master = 1; @@ -322,7 +327,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp, drm_master_put(&priv->minor->master); drm_master_put(&priv->master); mutex_unlock(&dev->struct_mutex); - goto out_free; + goto out_close; } } mutex_lock(&dev->struct_mutex); @@ -333,7 +338,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp, drm_master_put(&priv->minor->master); drm_master_put(&priv->master); mutex_unlock(&dev->struct_mutex); - goto out_free; + goto out_close; } } mutex_unlock(&dev->struct_mutex); @@ -367,7 +372,17 @@ static int drm_open_helper(struct inode *inode, struct file *filp, #endif return 0; - out_free: + +out_close: + if (dev->driver->postclose) + dev->driver->postclose(dev, priv); +out_prime_destroy: + if (drm_core_check_feature(dev, DRIVER_PRIME)) + drm_prime_destroy_file_private(&priv->prime); + if (dev->driver->driver_features & DRIVER_GEM) + drm_gem_release(dev, priv); +out_put_pid: + put_pid(priv->pid); kfree(priv); filp->private_data = NULL; return ret; -- cgit v1.2.3 From 04274cd058193cefc22f1ebda1e634d96cb59fa5 Mon Sep 17 00:00:00 2001 From: Seung-Woo Kim Date: Mon, 1 Jul 2013 19:44:14 +0900 Subject: drm: fix print format of sequence in trace point seq of a trace point is unsigned int but print format was %d. So it fixes the format as %u. Signed-off-by: Seung-Woo Kim Signed-off-by: Kyungmin Park Reviewed-by: Chris Wilson Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_trace.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/drm_trace.h b/drivers/gpu/drm/drm_trace.h index 03ea964aa604..27cc95f36381 100644 --- a/drivers/gpu/drm/drm_trace.h +++ b/drivers/gpu/drm/drm_trace.h @@ -21,7 +21,7 @@ TRACE_EVENT(drm_vblank_event, __entry->crtc = crtc; __entry->seq = seq; ), - TP_printk("crtc=%d, seq=%d", __entry->crtc, __entry->seq) + TP_printk("crtc=%d, seq=%u", __entry->crtc, __entry->seq) ); TRACE_EVENT(drm_vblank_event_queued, @@ -37,7 +37,7 @@ TRACE_EVENT(drm_vblank_event_queued, __entry->crtc = crtc; __entry->seq = seq; ), - TP_printk("pid=%d, crtc=%d, seq=%d", __entry->pid, __entry->crtc, \ + TP_printk("pid=%d, crtc=%d, seq=%u", __entry->pid, __entry->crtc, \ __entry->seq) ); @@ -54,7 +54,7 @@ TRACE_EVENT(drm_vblank_event_delivered, __entry->crtc = crtc; __entry->seq = seq; ), - TP_printk("pid=%d, crtc=%d, seq=%d", __entry->pid, __entry->crtc, \ + TP_printk("pid=%d, crtc=%d, seq=%u", __entry->pid, __entry->crtc, \ __entry->seq) ); -- cgit v1.2.3 From 69163ea82732894e8c1e17df4010372ed078efdd Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 1 Jul 2013 22:05:53 +0200 Subject: drm/mm: kill color_search_free/get_block drm/i915 is the only user of the color allocation handling and switched to insert_node a while ago. So we can ditch this. Signed-off-by: Daniel Vetter Reviewed-by: Chris Wilson Signed-off-by: Dave Airlie --- include/drm/drm_mm.h | 32 +------------------------------- 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/include/drm/drm_mm.h b/include/drm/drm_mm.h index de9242542f05..4d06edb56d5f 100644 --- a/include/drm/drm_mm.h +++ b/include/drm/drm_mm.h @@ -177,17 +177,6 @@ static inline struct drm_mm_node *drm_mm_get_block_range( return drm_mm_get_block_range_generic(parent, size, alignment, 0, start, end, 0); } -static inline struct drm_mm_node *drm_mm_get_color_block_range( - struct drm_mm_node *parent, - unsigned long size, - unsigned alignment, - unsigned long color, - unsigned long start, - unsigned long end) -{ - return drm_mm_get_block_range_generic(parent, size, alignment, color, - start, end, 0); -} static inline struct drm_mm_node *drm_mm_get_block_atomic_range( struct drm_mm_node *parent, unsigned long size, @@ -255,26 +244,7 @@ static inline struct drm_mm_node *drm_mm_search_free_in_range( return drm_mm_search_free_in_range_generic(mm, size, alignment, 0, start, end, best_match); } -static inline struct drm_mm_node *drm_mm_search_free_color(const struct drm_mm *mm, - unsigned long size, - unsigned alignment, - unsigned long color, - bool best_match) -{ - return drm_mm_search_free_generic(mm,size, alignment, color, best_match); -} -static inline struct drm_mm_node *drm_mm_search_free_in_range_color( - const struct drm_mm *mm, - unsigned long size, - unsigned alignment, - unsigned long color, - unsigned long start, - unsigned long end, - bool best_match) -{ - return drm_mm_search_free_in_range_generic(mm, size, alignment, color, - start, end, best_match); -} + extern void drm_mm_init(struct drm_mm *mm, unsigned long start, unsigned long size); -- cgit v1.2.3 From 9b9cd8024a2882e896c65222aa421d461354e3f2 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Thu, 4 Jul 2013 11:22:57 +0930 Subject: virtio-net: fix the race between channels setting and refill Commit 55257d72bd1c51f25106350f4983ec19f62ed1fa (virtio-net: fill only rx queues which are being used) tries to refill on demand when changing the number of channels by call try_refill_recv() directly, this may race: - the refill work who may do the refill in the same time - the try_refill_recv() called in bh since napi was not disabled Which may led guest complain during setting channels: virtio_net virtio0: input.1:id 0 is not a head! Solve this issue by scheduling a refill work which can guarantee the serialization of refill. Cc: Sasha Levin Cc: Rusty Russell Cc: Michael S. Tsirkin Signed-off-by: Jason Wang Signed-off-by: Rusty Russell --- drivers/net/virtio_net.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 655bb25eed2b..6ddd77ece5d5 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -900,7 +900,6 @@ static int virtnet_set_queues(struct virtnet_info *vi, u16 queue_pairs) struct scatterlist sg; struct virtio_net_ctrl_mq s; struct net_device *dev = vi->dev; - int i; if (!vi->has_cvq || !virtio_has_feature(vi->vdev, VIRTIO_NET_F_MQ)) return 0; @@ -914,10 +913,8 @@ static int virtnet_set_queues(struct virtnet_info *vi, u16 queue_pairs) queue_pairs); return -EINVAL; } else { - for (i = vi->curr_queue_pairs; i < queue_pairs; i++) - if (!try_fill_recv(&vi->rq[i], GFP_KERNEL)) - schedule_delayed_work(&vi->refill, 0); vi->curr_queue_pairs = queue_pairs; + schedule_delayed_work(&vi->refill, 0); } return 0; -- cgit v1.2.3 From e6dc0418bcb96e3cccb80571f35934ed72e9c99a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 4 Jul 2013 11:22:58 +0930 Subject: lguest: fix example launcher compilation for broken glibc headers. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Somehow a naked u16 slipped into the glibc headers on my Ubuntu machine (i386 2.17-0ubuntu5), breaking compile: In file included from lguest.c:46:0: /usr/include/linux/virtio_net.h:188:2: error: unknown type name ‘u16’ We use the kernel-style types anyway, just define them before the includes. Also remove the advice on adding missing headers: that no longer works. Signed-off-by: Rusty Russell --- tools/lguest/Makefile | 1 - tools/lguest/lguest.c | 17 +++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tools/lguest/Makefile b/tools/lguest/Makefile index 0ac34206f7a7..97bca4871ea3 100644 --- a/tools/lguest/Makefile +++ b/tools/lguest/Makefile @@ -1,5 +1,4 @@ # This creates the demonstration utility "lguest" which runs a Linux guest. -# Missing headers? Add "-I../../../include -I../../../arch/x86/include" CFLAGS:=-m32 -Wall -Wmissing-declarations -Wmissing-prototypes -O3 -U_FORTIFY_SOURCE all: lguest diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c index a64f5cb0c15b..68f67cf3d318 100644 --- a/tools/lguest/lguest.c +++ b/tools/lguest/lguest.c @@ -42,14 +42,6 @@ #include #include -#include -#include -#include -#include -#include -#include -#include -#include "../../include/linux/lguest_launcher.h" /*L:110 * We can ignore the 43 include files we need for this program, but I do want * to draw attention to the use of kernel-style types. @@ -65,6 +57,15 @@ typedef uint16_t u16; typedef uint8_t u8; /*:*/ +#include +#include +#include +#include +#include +#include +#include +#include "../../include/linux/lguest_launcher.h" + #define BRIDGE_PFX "bridge:" #ifndef SIOCBRADDIF #define SIOCBRADDIF 0x89a2 /* add interface to bridge */ -- cgit v1.2.3 From dbf738a1a6f93c634e368e74a1943acb93696b22 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Wed, 19 Jun 2013 23:21:20 -0700 Subject: iscsi-target: Allow ->MaxXmitDataSegmentLength assignment for iser discovery This patch changes iscsi_set_connection_parameters() to allow conn_ops->MaxXmitDataSegmentLength assignement to occur during in-band iser send-targets discovery, as this value is required by TEXT response processing code. Reported-by: Or Gerlitz Cc: Or Gerlitz Cc: Mike Christie Signed-off-by: Nicholas Bellinger --- drivers/target/iscsi/iscsi_target_parameters.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/target/iscsi/iscsi_target_parameters.c b/drivers/target/iscsi/iscsi_target_parameters.c index e38222191a33..35fd6439eb01 100644 --- a/drivers/target/iscsi/iscsi_target_parameters.c +++ b/drivers/target/iscsi/iscsi_target_parameters.c @@ -1799,9 +1799,6 @@ void iscsi_set_connection_parameters( * this key is not sent over the wire. */ if (!strcmp(param->name, MAXXMITDATASEGMENTLENGTH)) { - if (param_list->iser == true) - continue; - ops->MaxXmitDataSegmentLength = simple_strtoul(param->value, &tmpptr, 0); pr_debug("MaxXmitDataSegmentLength: %s\n", -- cgit v1.2.3 From 9864ca9d27f75d2716d09dd02b3d62d241194576 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Wed, 19 Jun 2013 22:43:11 -0700 Subject: iscsi-target: Move sendtargets parsing into iscsit_process_text_cmd This patch moves ISCSI_OP_TEXT PDU buffer sanity checks to iscsit_process_text_cmd() code, so that it can be shared with iser-target code. It adds IFC_SENDTARGETS_ALL + iscsi_cmd->text_in_ptr in order to save text payload for ISCSI_OP_TEXT_RSP, and updates iscsit_release_cmd() to assigned memory. Cc: Or Gerlitz Cc: Mike Christie Signed-off-by: Nicholas Bellinger --- drivers/target/iscsi/iscsi_target.c | 51 +++++++++++++++++++------------- drivers/target/iscsi/iscsi_target_core.h | 3 ++ drivers/target/iscsi/iscsi_target_util.c | 1 + 3 files changed, 35 insertions(+), 20 deletions(-) diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 1f79a168f1c1..30ca1887516b 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -1995,8 +1995,32 @@ int iscsit_process_text_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, struct iscsi_text *hdr) { + unsigned char *text_in = cmd->text_in_ptr, *text_ptr; int cmdsn_ret; + if (!text_in) { + pr_err("Unable to locate text_in buffer for sendtargets" + " discovery\n"); + goto reject; + } + if (strncmp("SendTargets", text_in, 11) != 0) { + pr_err("Received Text Data that is not" + " SendTargets, cannot continue.\n"); + goto reject; + } + text_ptr = strchr(text_in, '='); + if (!text_ptr) { + pr_err("No \"=\" separator found in Text Data," + " cannot continue.\n"); + goto reject; + } + if (!strncmp("=All", text_ptr, 4)) { + cmd->cmd_flags |= IFC_SENDTARGETS_ALL; + } else { + pr_err("Unable to locate valid SendTargets=%s value\n", text_ptr); + goto reject; + } + spin_lock_bh(&conn->cmd_lock); list_add_tail(&cmd->i_conn_node, &conn->conn_cmd_list); spin_unlock_bh(&conn->cmd_lock); @@ -2013,6 +2037,10 @@ iscsit_process_text_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, } return iscsit_execute_cmd(cmd, 0); + +reject: + return iscsit_add_reject_from_cmd(ISCSI_REASON_PROTOCOL_ERROR, + 0, 0, (unsigned char *)hdr, cmd); } EXPORT_SYMBOL(iscsit_process_text_cmd); @@ -2031,7 +2059,6 @@ iscsit_handle_text_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, rx_size = payload_length; if (payload_length) { - char *text_ptr; u32 checksum = 0, data_crc = 0; u32 padding = 0, pad_bytes = 0; int niov = 0, rx_got; @@ -2043,6 +2070,7 @@ iscsit_handle_text_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, " incoming text parameters\n"); goto reject; } + cmd->text_in_ptr = text_in; memset(iov, 0, 3 * sizeof(struct kvec)); iov[niov].iov_base = text_in; @@ -2101,30 +2129,13 @@ iscsit_handle_text_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, text_in[payload_length - 1] = '\0'; pr_debug("Successfully read %d bytes of text" " data.\n", payload_length); - - if (strncmp("SendTargets", text_in, 11) != 0) { - pr_err("Received Text Data that is not" - " SendTargets, cannot continue.\n"); - goto reject; - } - text_ptr = strchr(text_in, '='); - if (!text_ptr) { - pr_err("No \"=\" separator found in Text Data," - " cannot continue.\n"); - goto reject; - } - if (strncmp("=All", text_ptr, 4) != 0) { - pr_err("Unable to locate All value for" - " SendTargets key, cannot continue.\n"); - goto reject; - } - kfree(text_in); } return iscsit_process_text_cmd(conn, cmd, hdr); reject: - kfree(text_in); + kfree(cmd->text_in_ptr); + cmd->text_in_ptr = NULL; return iscsit_add_reject_from_cmd(ISCSI_REASON_PROTOCOL_ERROR, 0, 0, buf, cmd); } diff --git a/drivers/target/iscsi/iscsi_target_core.h b/drivers/target/iscsi/iscsi_target_core.h index 60ec4b92be03..ad46e73dab4c 100644 --- a/drivers/target/iscsi/iscsi_target_core.h +++ b/drivers/target/iscsi/iscsi_target_core.h @@ -133,6 +133,7 @@ enum cmd_flags_table { ICF_ATTACHED_TO_RQUEUE = 0x00000040, ICF_OOO_CMDSN = 0x00000080, ICF_REJECT_FAIL_CONN = 0x00000100, + IFC_SENDTARGETS_ALL = 0x00000200, }; /* struct iscsi_cmd->i_state */ @@ -427,6 +428,8 @@ struct iscsi_cmd { u32 tx_size; /* Buffer used for various purposes */ void *buf_ptr; + /* Used by SendTargets=[iqn.,eui.] discovery */ + void *text_in_ptr; /* See include/linux/dma-mapping.h */ enum dma_data_direction data_direction; /* iSCSI PDU Header + CRC */ diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c index 08a3bacef0c5..fe712d6cc478 100644 --- a/drivers/target/iscsi/iscsi_target_util.c +++ b/drivers/target/iscsi/iscsi_target_util.c @@ -681,6 +681,7 @@ void iscsit_release_cmd(struct iscsi_cmd *cmd) kfree(cmd->seq_list); kfree(cmd->tmr_req); kfree(cmd->iov_data); + kfree(cmd->text_in_ptr); kmem_cache_free(lio_cmd_cache, cmd); } -- cgit v1.2.3 From 6665889c843c774cd35309cf995ba0d302fa6dba Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Wed, 19 Jun 2013 22:45:42 -0700 Subject: iscsi-target: Add IFC_SENDTARGETS_SINGLE support This patch changes ISCSI_OP_TEXT handling of SendTargets=[iqn.,eui.] payloads to return explicit discovery information. It adds checks to iscsit_process_text_cmd() and adds the special single $TARGETNAME discovery case in iscsit_build_sendtargets_response() code. Cc: Or Gerlitz Cc: Mike Christie Signed-off-by: Nicholas Bellinger --- drivers/target/iscsi/iscsi_target.c | 28 ++++++++++++++++++++++++++++ drivers/target/iscsi/iscsi_target_core.h | 1 + 2 files changed, 29 insertions(+) diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 30ca1887516b..f2c3d4abfe70 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -2016,6 +2016,9 @@ iscsit_process_text_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, } if (!strncmp("=All", text_ptr, 4)) { cmd->cmd_flags |= IFC_SENDTARGETS_ALL; + } else if (!strncmp("=iqn.", text_ptr, 5) || + !strncmp("=eui.", text_ptr, 5)) { + cmd->cmd_flags |= IFC_SENDTARGETS_SINGLE; } else { pr_err("Unable to locate valid SendTargets=%s value\n", text_ptr); goto reject; @@ -3398,6 +3401,7 @@ static int iscsit_build_sendtargets_response(struct iscsi_cmd *cmd) struct iscsi_tpg_np *tpg_np; int buffer_len, end_of_buf = 0, len = 0, payload_len = 0; unsigned char buf[ISCSI_IQN_LEN+12]; /* iqn + "TargetName=" + \0 */ + unsigned char *text_in = cmd->text_in_ptr, *text_ptr = NULL; buffer_len = max(conn->conn_ops->MaxRecvDataSegmentLength, SENDTARGETS_BUF_LIMIT); @@ -3408,9 +3412,30 @@ static int iscsit_build_sendtargets_response(struct iscsi_cmd *cmd) " response.\n"); return -ENOMEM; } + /* + * Locate pointer to iqn./eui. string for IFC_SENDTARGETS_SINGLE + * explicit case.. + */ + if (cmd->cmd_flags & IFC_SENDTARGETS_SINGLE) { + text_ptr = strchr(text_in, '='); + if (!text_ptr) { + pr_err("Unable to locate '=' string in text_in:" + " %s\n", text_in); + return -EINVAL; + } + /* + * Skip over '=' character.. + */ + text_ptr += 1; + } spin_lock(&tiqn_lock); list_for_each_entry(tiqn, &g_tiqn_list, tiqn_list) { + if ((cmd->cmd_flags & IFC_SENDTARGETS_SINGLE) && + strcmp(tiqn->tiqn, text_ptr)) { + continue; + } + len = sprintf(buf, "TargetName=%s", tiqn->tiqn); len += 1; @@ -3464,6 +3489,9 @@ static int iscsit_build_sendtargets_response(struct iscsi_cmd *cmd) eob: if (end_of_buf) break; + + if (cmd->cmd_flags & IFC_SENDTARGETS_SINGLE) + break; } spin_unlock(&tiqn_lock); diff --git a/drivers/target/iscsi/iscsi_target_core.h b/drivers/target/iscsi/iscsi_target_core.h index ad46e73dab4c..3436a2cc1d35 100644 --- a/drivers/target/iscsi/iscsi_target_core.h +++ b/drivers/target/iscsi/iscsi_target_core.h @@ -134,6 +134,7 @@ enum cmd_flags_table { ICF_OOO_CMDSN = 0x00000080, ICF_REJECT_FAIL_CONN = 0x00000100, IFC_SENDTARGETS_ALL = 0x00000200, + IFC_SENDTARGETS_SINGLE = 0x00000400, }; /* struct iscsi_cmd->i_state */ -- cgit v1.2.3 From e4b512e7133f5243f080db8238c5be8434cbcdfd Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Wed, 19 Jun 2013 18:37:00 -0700 Subject: target: Add se_portal_group->tpg_auth_group This patch adds an optional /auth/ configfs group to TPG context that can be used by fabrics like iscsi-target for TPG demo-mode authentication. Cc: Dax Kelson Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_fabric_configfs.c | 21 +++++++++++++++++++-- include/target/target_core_base.h | 3 ++- include/target/target_core_configfs.h | 1 + include/target/target_core_fabric_configfs.h | 11 +++++++++++ 4 files changed, 33 insertions(+), 3 deletions(-) diff --git a/drivers/target/target_core_fabric_configfs.c b/drivers/target/target_core_fabric_configfs.c index 04c775cb3e65..eb56eb129563 100644 --- a/drivers/target/target_core_fabric_configfs.c +++ b/drivers/target/target_core_fabric_configfs.c @@ -965,6 +965,19 @@ TF_CIT_SETUP(tpg_attrib, &target_fabric_tpg_attrib_item_ops, NULL, NULL); /* End of tfc_tpg_attrib_cit */ +/* Start of tfc_tpg_auth_cit */ + +CONFIGFS_EATTR_OPS(target_fabric_tpg_auth, se_portal_group, tpg_auth_group); + +static struct configfs_item_operations target_fabric_tpg_auth_item_ops = { + .show_attribute = target_fabric_tpg_auth_attr_show, + .store_attribute = target_fabric_tpg_auth_attr_store, +}; + +TF_CIT_SETUP(tpg_auth, &target_fabric_tpg_auth_item_ops, NULL, NULL); + +/* End of tfc_tpg_attrib_cit */ + /* Start of tfc_tpg_param_cit */ CONFIGFS_EATTR_OPS(target_fabric_tpg_param, se_portal_group, tpg_param_group); @@ -1030,8 +1043,9 @@ static struct config_group *target_fabric_make_tpg( se_tpg->tpg_group.default_groups[1] = &se_tpg->tpg_np_group; se_tpg->tpg_group.default_groups[2] = &se_tpg->tpg_acl_group; se_tpg->tpg_group.default_groups[3] = &se_tpg->tpg_attrib_group; - se_tpg->tpg_group.default_groups[4] = &se_tpg->tpg_param_group; - se_tpg->tpg_group.default_groups[5] = NULL; + se_tpg->tpg_group.default_groups[4] = &se_tpg->tpg_auth_group; + se_tpg->tpg_group.default_groups[5] = &se_tpg->tpg_param_group; + se_tpg->tpg_group.default_groups[6] = NULL; config_group_init_type_name(&se_tpg->tpg_group, name, &TF_CIT_TMPL(tf)->tfc_tpg_base_cit); @@ -1043,6 +1057,8 @@ static struct config_group *target_fabric_make_tpg( &TF_CIT_TMPL(tf)->tfc_tpg_nacl_cit); config_group_init_type_name(&se_tpg->tpg_attrib_group, "attrib", &TF_CIT_TMPL(tf)->tfc_tpg_attrib_cit); + config_group_init_type_name(&se_tpg->tpg_auth_group, "auth", + &TF_CIT_TMPL(tf)->tfc_tpg_auth_cit); config_group_init_type_name(&se_tpg->tpg_param_group, "param", &TF_CIT_TMPL(tf)->tfc_tpg_param_cit); @@ -1202,6 +1218,7 @@ int target_fabric_setup_cits(struct target_fabric_configfs *tf) target_fabric_setup_tpg_np_cit(tf); target_fabric_setup_tpg_np_base_cit(tf); target_fabric_setup_tpg_attrib_cit(tf); + target_fabric_setup_tpg_auth_cit(tf); target_fabric_setup_tpg_param_cit(tf); target_fabric_setup_tpg_nacl_cit(tf); target_fabric_setup_tpg_nacl_base_cit(tf); diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index 9fd7a60f47f0..d92ec6765f5d 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -795,11 +795,12 @@ struct se_portal_group { struct target_core_fabric_ops *se_tpg_tfo; struct se_wwn *se_tpg_wwn; struct config_group tpg_group; - struct config_group *tpg_default_groups[6]; + struct config_group *tpg_default_groups[7]; struct config_group tpg_lun_group; struct config_group tpg_np_group; struct config_group tpg_acl_group; struct config_group tpg_attrib_group; + struct config_group tpg_auth_group; struct config_group tpg_param_group; }; diff --git a/include/target/target_core_configfs.h b/include/target/target_core_configfs.h index 612509592ffd..713c5004f4ae 100644 --- a/include/target/target_core_configfs.h +++ b/include/target/target_core_configfs.h @@ -23,6 +23,7 @@ struct target_fabric_configfs_template { struct config_item_type tfc_tpg_np_cit; struct config_item_type tfc_tpg_np_base_cit; struct config_item_type tfc_tpg_attrib_cit; + struct config_item_type tfc_tpg_auth_cit; struct config_item_type tfc_tpg_param_cit; struct config_item_type tfc_tpg_nacl_cit; struct config_item_type tfc_tpg_nacl_base_cit; diff --git a/include/target/target_core_fabric_configfs.h b/include/target/target_core_fabric_configfs.h index a26fb7586a09..b32a14905cfa 100644 --- a/include/target/target_core_fabric_configfs.h +++ b/include/target/target_core_fabric_configfs.h @@ -62,6 +62,17 @@ static struct target_fabric_tpg_attrib_attribute _fabric##_tpg_attrib_##_name = _fabric##_tpg_attrib_show_##_name, \ _fabric##_tpg_attrib_store_##_name); +CONFIGFS_EATTR_STRUCT(target_fabric_tpg_auth, se_portal_group); +#define TF_TPG_AUTH_ATTR(_fabric, _name, _mode) \ +static struct target_fabric_tpg_auth_attribute _fabric##_tpg_auth_##_name = \ + __CONFIGFS_EATTR(_name, _mode, \ + _fabric##_tpg_auth_show_##_name, \ + _fabric##_tpg_auth_store_##_name); + +#define TF_TPG_AUTH_ATTR_RO(_fabric, _name) \ +static struct target_fabric_tpg_auth_attribute _fabric##_tpg_auth_##_name = \ + __CONFIGFS_EATTR_RO(_name, \ + _fabric##_tpg_auth_show_##_name); CONFIGFS_EATTR_STRUCT(target_fabric_tpg_param, se_portal_group); #define TF_TPG_PARAM_ATTR(_fabric, _name, _mode) \ -- cgit v1.2.3 From c3e51442711d20ea1245bb6d260aa05593849e82 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Wed, 19 Jun 2013 18:48:51 -0700 Subject: iscsi-target: Add demo-mode TPG authentication context support This patch adds a auth configfs group context following existing explict NodeACL and discovery auth within: /sys/kernel/config/target/iscsi/$TARGETNAME/$TPGT/auth/ This patch allows these attributes to be used for CHAP authentication an TPG is configured in demo-mode (generate_node_acl=1). Note this authentication information takes precedence over NodeACL authentication when struct se_node_acl->dynamic_node_acl is present. Cc: Dax Kelson Signed-off-by: Nicholas Bellinger --- drivers/target/iscsi/iscsi_target_configfs.c | 126 +++++++++++++++++++++++++++ drivers/target/iscsi/iscsi_target_core.h | 1 + drivers/target/iscsi/iscsi_target_nego.c | 13 ++- 3 files changed, 139 insertions(+), 1 deletion(-) diff --git a/drivers/target/iscsi/iscsi_target_configfs.c b/drivers/target/iscsi/iscsi_target_configfs.c index 13e9e715ad2e..e251849a6140 100644 --- a/drivers/target/iscsi/iscsi_target_configfs.c +++ b/drivers/target/iscsi/iscsi_target_configfs.c @@ -1052,6 +1052,131 @@ static struct configfs_attribute *lio_target_tpg_attrib_attrs[] = { /* End items for lio_target_tpg_attrib_cit */ +/* Start items for lio_target_tpg_auth_cit */ + +#define __DEF_TPG_AUTH_STR(prefix, name, flags) \ +static ssize_t __iscsi_##prefix##_show_##name( \ + struct se_portal_group *se_tpg, \ + char *page) \ +{ \ + struct iscsi_portal_group *tpg = container_of(se_tpg, \ + struct iscsi_portal_group, tpg_se_tpg); \ + struct iscsi_node_auth *auth = &tpg->tpg_demo_auth; \ + \ + if (!capable(CAP_SYS_ADMIN)) \ + return -EPERM; \ + \ + return snprintf(page, PAGE_SIZE, "%s\n", auth->name); \ +} \ + \ +static ssize_t __iscsi_##prefix##_store_##name( \ + struct se_portal_group *se_tpg, \ + const char *page, \ + size_t count) \ +{ \ + struct iscsi_portal_group *tpg = container_of(se_tpg, \ + struct iscsi_portal_group, tpg_se_tpg); \ + struct iscsi_node_auth *auth = &tpg->tpg_demo_auth; \ + \ + if (!capable(CAP_SYS_ADMIN)) \ + return -EPERM; \ + \ + snprintf(auth->name, PAGE_SIZE, "%s", page); \ + if (!(strncmp("NULL", auth->name, 4))) \ + auth->naf_flags &= ~flags; \ + else \ + auth->naf_flags |= flags; \ + \ + if ((auth->naf_flags & NAF_USERID_IN_SET) && \ + (auth->naf_flags & NAF_PASSWORD_IN_SET)) \ + auth->authenticate_target = 1; \ + else \ + auth->authenticate_target = 0; \ + \ + return count; \ +} + +#define __DEF_TPG_AUTH_INT(prefix, name) \ +static ssize_t __iscsi_##prefix##_show_##name( \ + struct se_portal_group *se_tpg, \ + char *page) \ +{ \ + struct iscsi_portal_group *tpg = container_of(se_tpg, \ + struct iscsi_portal_group, tpg_se_tpg); \ + struct iscsi_node_auth *auth = &tpg->tpg_demo_auth; \ + \ + if (!capable(CAP_SYS_ADMIN)) \ + return -EPERM; \ + \ + return snprintf(page, PAGE_SIZE, "%d\n", auth->name); \ +} + +#define DEF_TPG_AUTH_STR(name, flags) \ + __DEF_TPG_AUTH_STR(tpg_auth, name, flags) \ +static ssize_t iscsi_tpg_auth_show_##name( \ + struct se_portal_group *se_tpg, \ + char *page) \ +{ \ + return __iscsi_tpg_auth_show_##name(se_tpg, page); \ +} \ + \ +static ssize_t iscsi_tpg_auth_store_##name( \ + struct se_portal_group *se_tpg, \ + const char *page, \ + size_t count) \ +{ \ + return __iscsi_tpg_auth_store_##name(se_tpg, page, count); \ +} + +#define DEF_TPG_AUTH_INT(name) \ + __DEF_TPG_AUTH_INT(tpg_auth, name) \ +static ssize_t iscsi_tpg_auth_show_##name( \ + struct se_portal_group *se_tpg, \ + char *page) \ +{ \ + return __iscsi_tpg_auth_show_##name(se_tpg, page); \ +} + +#define TPG_AUTH_ATTR(_name, _mode) TF_TPG_AUTH_ATTR(iscsi, _name, _mode); +#define TPG_AUTH_ATTR_RO(_name) TF_TPG_AUTH_ATTR_RO(iscsi, _name); + +/* + * * One-way authentication userid + * */ +DEF_TPG_AUTH_STR(userid, NAF_USERID_SET); +TPG_AUTH_ATTR(userid, S_IRUGO | S_IWUSR); +/* + * * One-way authentication password + * */ +DEF_TPG_AUTH_STR(password, NAF_PASSWORD_SET); +TPG_AUTH_ATTR(password, S_IRUGO | S_IWUSR); +/* + * * Enforce mutual authentication + * */ +DEF_TPG_AUTH_INT(authenticate_target); +TPG_AUTH_ATTR_RO(authenticate_target); +/* + * * Mutual authentication userid + * */ +DEF_TPG_AUTH_STR(userid_mutual, NAF_USERID_IN_SET); +TPG_AUTH_ATTR(userid_mutual, S_IRUGO | S_IWUSR); +/* + * * Mutual authentication password + * */ +DEF_TPG_AUTH_STR(password_mutual, NAF_PASSWORD_IN_SET); +TPG_AUTH_ATTR(password_mutual, S_IRUGO | S_IWUSR); + +static struct configfs_attribute *lio_target_tpg_auth_attrs[] = { + &iscsi_tpg_auth_userid.attr, + &iscsi_tpg_auth_password.attr, + &iscsi_tpg_auth_authenticate_target.attr, + &iscsi_tpg_auth_userid_mutual.attr, + &iscsi_tpg_auth_password_mutual.attr, + NULL, +}; + +/* End items for lio_target_tpg_auth_cit */ + /* Start items for lio_target_tpg_param_cit */ #define DEF_TPG_PARAM(name) \ @@ -1865,6 +1990,7 @@ int iscsi_target_register_configfs(void) TF_CIT_TMPL(fabric)->tfc_wwn_cit.ct_attrs = lio_target_wwn_attrs; TF_CIT_TMPL(fabric)->tfc_tpg_base_cit.ct_attrs = lio_target_tpg_attrs; TF_CIT_TMPL(fabric)->tfc_tpg_attrib_cit.ct_attrs = lio_target_tpg_attrib_attrs; + TF_CIT_TMPL(fabric)->tfc_tpg_auth_cit.ct_attrs = lio_target_tpg_auth_attrs; TF_CIT_TMPL(fabric)->tfc_tpg_param_cit.ct_attrs = lio_target_tpg_param_attrs; TF_CIT_TMPL(fabric)->tfc_tpg_np_base_cit.ct_attrs = lio_target_portal_attrs; TF_CIT_TMPL(fabric)->tfc_tpg_nacl_base_cit.ct_attrs = lio_target_initiator_attrs; diff --git a/drivers/target/iscsi/iscsi_target_core.h b/drivers/target/iscsi/iscsi_target_core.h index 3436a2cc1d35..391283c85313 100644 --- a/drivers/target/iscsi/iscsi_target_core.h +++ b/drivers/target/iscsi/iscsi_target_core.h @@ -813,6 +813,7 @@ struct iscsi_portal_group { struct mutex tpg_access_lock; struct mutex np_login_lock; struct iscsi_tpg_attrib tpg_attrib; + struct iscsi_node_auth tpg_demo_auth; /* Pointer to default list of iSCSI parameters for TPG */ struct iscsi_param_list *param_list; struct iscsi_tiqn *tpg_tiqn; diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c index 7ad912060e21..6b5fc27a770d 100644 --- a/drivers/target/iscsi/iscsi_target_nego.c +++ b/drivers/target/iscsi/iscsi_target_nego.c @@ -112,6 +112,7 @@ static u32 iscsi_handle_authentication( struct iscsi_session *sess = conn->sess; struct iscsi_node_auth *auth; struct iscsi_node_acl *iscsi_nacl; + struct iscsi_portal_group *iscsi_tpg; struct se_node_acl *se_nacl; if (!sess->sess_ops->SessionType) { @@ -132,7 +133,17 @@ static u32 iscsi_handle_authentication( return -1; } - auth = ISCSI_NODE_AUTH(iscsi_nacl); + if (se_nacl->dynamic_node_acl) { + iscsi_tpg = container_of(se_nacl->se_tpg, + struct iscsi_portal_group, tpg_se_tpg); + + auth = &iscsi_tpg->tpg_demo_auth; + } else { + iscsi_nacl = container_of(se_nacl, struct iscsi_node_acl, + se_node_acl); + + auth = ISCSI_NODE_AUTH(iscsi_nacl); + } } else { /* * For SessionType=Discovery -- cgit v1.2.3 From 8a3918571a4eb3ae10ddd4aaec591b4af80d1172 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Mon, 24 Jun 2013 22:18:40 -0700 Subject: target: Make core_scsi3_update_and_write_aptpl return sense_reason_t Fix up sense_reason_t breakage in core_scsi3_update_and_write_aptpl() from recent conversion to use local scope memory allocation. Reported as sparse warnings: (new ones prefixed by >>) by Fengguang: >> drivers/target/target_core_pr.c:2069:57: sparse: incorrect type in >> return expression (different base types) drivers/target/target_core_pr.c:2069:57: expected restricted sense_reason_t drivers/target/target_core_pr.c:2069:57: got int >> drivers/target/target_core_pr.c:2179:21: sparse: incorrect type in >> assignment (different base types) drivers/target/target_core_pr.c:2179:21: expected restricted sense_reason_t [assigned] [usertype] ret drivers/target/target_core_pr.c:2179:21: got int >> drivers/target/target_core_pr.c:2197:13: sparse: incorrect type in >> assignment (different base types) drivers/target/target_core_pr.c:2197:13: expected restricted sense_reason_t [assigned] [usertype] ret drivers/target/target_core_pr.c:2197:13: got int drivers/target/target_core_pr.c:1245:28: sparse: context imbalance in '__core_scsi3_free_registration' - unexpected unlock Reported-by: kbuild test robot Cc: Andy Grover Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_pr.c | 47 ++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index 05c3f426728a..bd78faf67c6b 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -1956,41 +1956,44 @@ static int __core_scsi3_write_aptpl_to_file( * Clear the APTPL metadata if APTPL has been disabled, otherwise * write out the updated metadata to struct file for this SCSI device. */ -static int core_scsi3_update_and_write_aptpl(struct se_device *dev, bool aptpl) +static sense_reason_t core_scsi3_update_and_write_aptpl(struct se_device *dev, bool aptpl) { - int ret = 0; + unsigned char *buf; + int rc; if (!aptpl) { char *null_buf = "No Registrations or Reservations\n"; - ret = __core_scsi3_write_aptpl_to_file(dev, null_buf); + rc = __core_scsi3_write_aptpl_to_file(dev, null_buf); dev->t10_pr.pr_aptpl_active = 0; pr_debug("SPC-3 PR: Set APTPL Bit Deactivated\n"); - } else { - int ret; - unsigned char *buf; - buf = kzalloc(PR_APTPL_BUF_LEN, GFP_KERNEL); - if (!buf) - return -ENOMEM; + if (rc) + return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; - ret = core_scsi3_update_aptpl_buf(dev, buf, PR_APTPL_BUF_LEN); - if (ret < 0) { - kfree(buf); - return ret; - } + return 0; + } - ret = __core_scsi3_write_aptpl_to_file(dev, buf); - if (ret != 0) { - pr_err("SPC-3 PR: Could not update APTPL\n"); - } else { - dev->t10_pr.pr_aptpl_active = 1; - pr_debug("SPC-3 PR: Set APTPL Bit Activated\n"); - } + buf = kzalloc(PR_APTPL_BUF_LEN, GFP_KERNEL); + if (!buf) + return TCM_OUT_OF_RESOURCES; + + rc = core_scsi3_update_aptpl_buf(dev, buf, PR_APTPL_BUF_LEN); + if (rc < 0) { kfree(buf); + return TCM_OUT_OF_RESOURCES; } - return ret; + rc = __core_scsi3_write_aptpl_to_file(dev, buf); + if (rc != 0) { + pr_err("SPC-3 PR: Could not update APTPL\n"); + kfree(buf); + return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; + } + dev->t10_pr.pr_aptpl_active = 1; + kfree(buf); + pr_debug("SPC-3 PR: Set APTPL Bit Activated\n"); + return 0; } static sense_reason_t -- cgit v1.2.3 From 3e23d025bc19940979c4f0c67a39d64af7c893c6 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Mon, 24 Jun 2013 22:26:02 -0700 Subject: iscsi-target: Drop left-over iscsi_conn->bad_hdr All REJECT response setup of the rejected payload is now done using on-demand cmd->buf_ptr allocations. Go ahead and remove dead iscsi_conn->bad_hdr usage rx_opcode path Signed-off-by: Nicholas Bellinger --- drivers/target/iscsi/iscsi_target.c | 5 ----- drivers/target/iscsi/iscsi_target_core.h | 2 -- 2 files changed, 7 deletions(-) diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index f2c3d4abfe70..dc2c0565736d 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -4105,11 +4105,6 @@ restart: goto transport_err; } - /* - * Set conn->bad_hdr for use with REJECT PDUs. - */ - memcpy(&conn->bad_hdr, &buffer, ISCSI_HDR_LEN); - if (conn->conn_ops->HeaderDigest) { iov.iov_base = &digest; iov.iov_len = ISCSI_CRC_LEN; diff --git a/drivers/target/iscsi/iscsi_target_core.h b/drivers/target/iscsi/iscsi_target_core.h index 391283c85313..caa0ad9a778b 100644 --- a/drivers/target/iscsi/iscsi_target_core.h +++ b/drivers/target/iscsi/iscsi_target_core.h @@ -532,8 +532,6 @@ struct iscsi_conn { u32 of_marker; /* Used for calculating OFMarker offset to next PDU */ u32 of_marker_offset; - /* Complete Bad PDU for sending reject */ - unsigned char bad_hdr[ISCSI_HDR_LEN]; #define IPV6_ADDRESS_SPACE 48 unsigned char login_ip[IPV6_ADDRESS_SPACE]; unsigned char local_ip[IPV6_ADDRESS_SPACE]; -- cgit v1.2.3 From 4f45d320ba97ad2f1107a56e8b2af0dd7e764502 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 24 Jun 2013 18:46:57 +0300 Subject: iscsi-target: missing kfree() on error path Fix-up breakage in iscsit_build_sendtargets_response() from v3.11 changes, and free "payload" before returning. Signed-off-by: Dan Carpenter Signed-off-by: Nicholas Bellinger --- drivers/target/iscsi/iscsi_target.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index dc2c0565736d..19a31f9bb7d5 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -3421,6 +3421,7 @@ static int iscsit_build_sendtargets_response(struct iscsi_cmd *cmd) if (!text_ptr) { pr_err("Unable to locate '=' string in text_in:" " %s\n", text_in); + kfree(payload); return -EINVAL; } /* -- cgit v1.2.3 From fdcfbbb653b27964c4daa4d2bcb364259c257e7d Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Thu, 4 Jul 2013 16:38:16 +1000 Subject: md/raid5: allow 5-device RAID6 to be reshaped to 4-device. There is a bug in 'check_reshape' for raid5.c To checks that the new minimum number of devices is large enough (which is good), but it does so also after the reshape has started (bad). This is bad because - the calculation is now wrong as mddev->raid_disks has changed already, and - it is pointless because it is now too late to stop. So only perform that test when reshape has not been committed to. Signed-off-by: NeilBrown --- drivers/md/raid5.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index cd9aab9329fd..2bf094a587cb 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -5914,7 +5914,7 @@ static int check_reshape(struct mddev *mddev) return 0; /* nothing to do */ if (has_failed(conf)) return -EINVAL; - if (mddev->delta_disks < 0) { + if (mddev->delta_disks < 0 && mddev->reshape_position == MaxSector) { /* We might be able to shrink, but the devices must * be made bigger first. * For raid6, 4 is the minimum size. -- cgit v1.2.3 From 1376512065b23f39d5f9a160948f313397dde972 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Thu, 4 Jul 2013 16:41:53 +1000 Subject: md/raid10: fix bug which causes all RAID10 reshapes to move no data. The recent comment: commit 7e83ccbecd608b971f340e951c9e84cd0343002f md/raid10: Allow skipping recovery when clean arrays are assembled Causes raid10 to skip a recovery in certain cases where it is safe to do so. Unfortunately it also causes a reshape to be skipped which is never safe. The result is that an attempt to reshape a RAID10 will appear to complete instantly, but no data will have been moves so the array will now contain garbage. (If nothing is written, you can recovery by simple performing the reverse reshape which will also complete instantly). Bug was introduced in 3.10, so this is suitable for 3.10-stable. Cc: stable@vger.kernel.org (3.10) Cc: Martin Wilck Signed-off-by: NeilBrown --- drivers/md/raid10.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 3480bf7c20d4..cd066b63bdaf 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -2931,14 +2931,13 @@ static sector_t sync_request(struct mddev *mddev, sector_t sector_nr, */ if (mddev->bitmap == NULL && mddev->recovery_cp == MaxSector && + mddev->reshape_position == MaxSector && + !test_bit(MD_RECOVERY_SYNC, &mddev->recovery) && !test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery) && + !test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery) && conf->fullsync == 0) { *skipped = 1; - max_sector = mddev->dev_sectors; - if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery) || - test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery)) - max_sector = mddev->resync_max_sectors; - return max_sector - sector_nr; + return mddev->dev_sectors - sector_nr; } skipped: -- cgit v1.2.3 From 8eed2641df55f7a3e69699b50a63f5705533886f Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 1 Jul 2013 15:15:36 +0900 Subject: of/documentation: Update hpd gpio property for exynos_hdmi Signed-off-by: Sachin Kamat Signed-off-by: Rahul Sharma Signed-off-by: Inki Dae --- Documentation/devicetree/bindings/video/exynos_hdmi.txt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/video/exynos_hdmi.txt b/Documentation/devicetree/bindings/video/exynos_hdmi.txt index c71d0f0b750a..323983be3c30 100644 --- a/Documentation/devicetree/bindings/video/exynos_hdmi.txt +++ b/Documentation/devicetree/bindings/video/exynos_hdmi.txt @@ -11,9 +11,7 @@ Required properties: - hpd-gpio: following information about the hotplug gpio pin. a) phandle of the gpio controller node. b) pin number within the gpio controller. - c) pin function mode. - d) optional flags and pull up/down. - e) drive strength. + c) optional flags and pull up/down. Example: @@ -21,5 +19,5 @@ Example: compatible = "samsung,exynos4212-hdmi"; reg = <0x14530000 0x100000>; interrupts = <0 95 0>; - hpd-gpio = <&gpx3 7 0xf 1 3>; + hpd-gpio = <&gpx3 7 1>; }; -- cgit v1.2.3 From c58c1599cd5c97977c44047272b56b4db455bbba Mon Sep 17 00:00:00 2001 From: YoungJun Cho Date: Mon, 1 Jul 2013 11:17:12 +0900 Subject: drm/exynos: fix not to remain exynos_gem_obj as a leak The exynos_drm_gem_create() only calls drm_gem_object_release() when exynos_drm_alloc_buf() is failed, and exynos_gem_obj remains as a leak, which is allocated in exynos_drm_gem_init(). So this patch fixes it not to remain as a leak. Signed-off-by: YoungJun Cho Signed-off-by: Seung-Woo Kim Signed-off-by: Kyungmin Park Signed-off-by: Inki Dae --- drivers/gpu/drm/exynos/exynos_drm_gem.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c index c3f15e7646d5..24c22a8c3364 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c @@ -246,13 +246,14 @@ struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev, exynos_gem_obj->flags = flags; ret = exynos_drm_alloc_buf(dev, buf, flags); - if (ret < 0) { - drm_gem_object_release(&exynos_gem_obj->base); - goto err_fini_buf; - } + if (ret < 0) + goto err_gem_fini; return exynos_gem_obj; +err_gem_fini: + drm_gem_object_release(&exynos_gem_obj->base); + kfree(exynos_gem_obj); err_fini_buf: exynos_drm_fini_buf(dev, buf); return ERR_PTR(ret); -- cgit v1.2.3 From cbb28bb09d41872909fdbd4ba56eb1ba55146c03 Mon Sep 17 00:00:00 2001 From: YoungJun Cho Date: Mon, 1 Jul 2013 13:03:39 +0900 Subject: drm/exynos: remove dead code in vidi_power_on The type of input parameter enable is bool, so it does not need to check whether true or false. Signed-off-by: YoungJun Cho Signed-off-by: Kyungmin Park Signed-off-by: Inki Dae --- drivers/gpu/drm/exynos/exynos_drm_vidi.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c index 784bbce0741a..41cc74d83e4e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c @@ -413,9 +413,6 @@ static int vidi_power_on(struct vidi_context *ctx, bool enable) struct exynos_drm_subdrv *subdrv = &ctx->subdrv; struct device *dev = subdrv->dev; - if (enable != false && enable != true) - return -EINVAL; - if (enable) { ctx->suspended = false; -- cgit v1.2.3 From 782953ece3d102ccb31df87ecfeeb96064126afb Mon Sep 17 00:00:00 2001 From: YoungJun Cho Date: Mon, 1 Jul 2013 13:04:12 +0900 Subject: drm/exynos: initialize the buf_num in vp_video_buffer The buf_num in vp_video_buffer() should be 1 or 2, but it is not initialized, and only set to 2 in NV12M or NV12MT cases. So this patch initializes the buf_num with 1 as default. Signed-off-by: YoungJun Cho Signed-off-by: Seung-Woo Kim Signed-off-by: Kyungmin Park Signed-off-by: Inki Dae --- drivers/gpu/drm/exynos/exynos_mixer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index b1280b43931c..42ffb71c63bc 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -379,7 +379,7 @@ static void vp_video_buffer(struct mixer_context *ctx, int win) unsigned long flags; struct hdmi_win_data *win_data; unsigned int x_ratio, y_ratio; - unsigned int buf_num; + unsigned int buf_num = 1; dma_addr_t luma_addr[2], chroma_addr[2]; bool tiled_mode = false; bool crcb_mode = false; -- cgit v1.2.3 From ba3706c0f19ab77593b8b3be6649746ac56905d3 Mon Sep 17 00:00:00 2001 From: YoungJun Cho Date: Mon, 1 Jul 2013 17:00:47 +0900 Subject: drm/exynos: add error check routine in exynos_drm_open When the exynos_drm_subdrv_open() returns error, the file_priv should be released and file->driver_priv set to NULL. Signed-off-by: YoungJun Cho Signed-off-by: Kyungmin Park Signed-off-by: Inki Dae --- drivers/gpu/drm/exynos/exynos_drm_drv.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 276237348d1e..ca2729a85129 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -155,6 +155,7 @@ static int exynos_drm_unload(struct drm_device *dev) static int exynos_drm_open(struct drm_device *dev, struct drm_file *file) { struct drm_exynos_file_private *file_priv; + int ret; file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL); if (!file_priv) @@ -162,7 +163,13 @@ static int exynos_drm_open(struct drm_device *dev, struct drm_file *file) file->driver_priv = file_priv; - return exynos_drm_subdrv_open(dev, file); + ret = exynos_drm_subdrv_open(dev, file); + if (ret) { + kfree(file_priv); + file->driver_priv = NULL; + } + + return ret; } static void exynos_drm_preclose(struct drm_device *dev, -- cgit v1.2.3 From af51a5e7068826fe5016ab38149243bacc449233 Mon Sep 17 00:00:00 2001 From: YoungJun Cho Date: Wed, 3 Jul 2013 17:09:19 +0900 Subject: drm/exynos: use drm_calloc_large when allocates pointer array If the type of object is pointer array, the drm_calloc_large() is more suitable than kzalloc() for its allocation function. And uses drm_free_large() instead of kfree() also. Signed-off-by: YoungJun Cho Signed-off-by: Seung-Woo Kim Signed-off-by: Kyungmin Park Signed-off-by: Inki Dae --- drivers/gpu/drm/exynos/exynos_drm_buf.c | 9 ++++----- drivers/gpu/drm/exynos/exynos_drm_g2d.c | 6 +++--- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos_drm_buf.c b/drivers/gpu/drm/exynos/exynos_drm_buf.c index 22865baa03b1..245c9ae187a1 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_buf.c +++ b/drivers/gpu/drm/exynos/exynos_drm_buf.c @@ -57,8 +57,7 @@ static int lowlevel_buffer_allocate(struct drm_device *dev, dma_addr_t start_addr; unsigned int i = 0; - buf->pages = kzalloc(sizeof(struct page) * nr_pages, - GFP_KERNEL); + buf->pages = drm_calloc_large(nr_pages, sizeof(struct page)); if (!buf->pages) { DRM_ERROR("failed to allocate pages.\n"); return -ENOMEM; @@ -69,7 +68,7 @@ static int lowlevel_buffer_allocate(struct drm_device *dev, &buf->dma_attrs); if (!buf->kvaddr) { DRM_ERROR("failed to allocate buffer.\n"); - kfree(buf->pages); + drm_free_large(buf->pages); return -ENOMEM; } @@ -109,7 +108,7 @@ err_free_attrs: buf->dma_addr = (dma_addr_t)NULL; if (!is_drm_iommu_supported(dev)) - kfree(buf->pages); + drm_free_large(buf->pages); return ret; } @@ -134,7 +133,7 @@ static void lowlevel_buffer_deallocate(struct drm_device *dev, if (!is_drm_iommu_supported(dev)) { dma_free_attrs(dev->dev, buf->size, buf->kvaddr, (dma_addr_t)buf->dma_addr, &buf->dma_attrs); - kfree(buf->pages); + drm_free_large(buf->pages); } else dma_free_attrs(dev->dev, buf->size, buf->pages, (dma_addr_t)buf->dma_addr, &buf->dma_attrs); diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c index af75434ee4d7..fb19ee53ecbb 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c @@ -390,7 +390,7 @@ out: kfree(g2d_userptr->sgt); g2d_userptr->sgt = NULL; - kfree(g2d_userptr->pages); + drm_free_large(g2d_userptr->pages); g2d_userptr->pages = NULL; kfree(g2d_userptr); g2d_userptr = NULL; @@ -463,7 +463,7 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev, npages = (end - start) >> PAGE_SHIFT; g2d_userptr->npages = npages; - pages = kzalloc(npages * sizeof(struct page *), GFP_KERNEL); + pages = drm_calloc_large(npages, sizeof(struct page *)); if (!pages) { DRM_ERROR("failed to allocate pages.\n"); kfree(g2d_userptr); @@ -554,7 +554,7 @@ err_put_vma: exynos_gem_put_vma(g2d_userptr->vma); err_free_pages: - kfree(pages); + drm_free_large(pages); kfree(g2d_userptr); pages = NULL; g2d_userptr = NULL; -- cgit v1.2.3 From 42ac99a72041a3515bd2b205adb9a239b49c6741 Mon Sep 17 00:00:00 2001 From: YoungJun Cho Date: Wed, 3 Jul 2013 17:09:20 +0900 Subject: drm/exynos: fix pages allocation size in lowlevel_buffer_allocate When IOMMU is not supported, buf->pages has to be allocated to assign the result of phys_to_page() which return type is struct page *. So it is sufficient to allocate buf->pages with the size of multiple struct page pointers. Signed-off-by: YoungJun Cho Signed-off-by: Seung-Woo Kim Signed-off-by: Kyungmin Park Signed-off-by: Inki Dae --- drivers/gpu/drm/exynos/exynos_drm_buf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/exynos/exynos_drm_buf.c b/drivers/gpu/drm/exynos/exynos_drm_buf.c index 245c9ae187a1..518b6d8e062b 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_buf.c +++ b/drivers/gpu/drm/exynos/exynos_drm_buf.c @@ -57,7 +57,7 @@ static int lowlevel_buffer_allocate(struct drm_device *dev, dma_addr_t start_addr; unsigned int i = 0; - buf->pages = drm_calloc_large(nr_pages, sizeof(struct page)); + buf->pages = drm_calloc_large(nr_pages, sizeof(struct page *)); if (!buf->pages) { DRM_ERROR("failed to allocate pages.\n"); return -ENOMEM; -- cgit v1.2.3 From 4bb615c5fbb4f3ea0f197dfe4fb07a9e4ec2a755 Mon Sep 17 00:00:00 2001 From: Seung-Woo Kim Date: Wed, 3 Jul 2013 17:09:21 +0900 Subject: drm/exynos: remove duplicated error routine and unnecessary assign There were duplicated error handling routines during allocating pages in lowlevel_buffer_allocate() and g2d_userptr_get_dma_addr(). Also unnecessary NULL assignments for variable used not any more are removed from g2d_userptr_get_dma_addr() and g2d_userptr_put_dma_addr(). Signed-off-by: Seung-Woo Kim Signed-off-by: YoungJun Cho Signed-off-by: Kyungmin Park Signed-off-by: Inki Dae --- drivers/gpu/drm/exynos/exynos_drm_buf.c | 6 +++--- drivers/gpu/drm/exynos/exynos_drm_g2d.c | 12 ++++-------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/exynos/exynos_drm_buf.c b/drivers/gpu/drm/exynos/exynos_drm_buf.c index 518b6d8e062b..b8ac06d92fbf 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_buf.c +++ b/drivers/gpu/drm/exynos/exynos_drm_buf.c @@ -68,8 +68,8 @@ static int lowlevel_buffer_allocate(struct drm_device *dev, &buf->dma_attrs); if (!buf->kvaddr) { DRM_ERROR("failed to allocate buffer.\n"); - drm_free_large(buf->pages); - return -ENOMEM; + ret = -ENOMEM; + goto err_free; } start_addr = buf->dma_addr; @@ -106,7 +106,7 @@ err_free_attrs: dma_free_attrs(dev->dev, buf->size, buf->pages, (dma_addr_t)buf->dma_addr, &buf->dma_attrs); buf->dma_addr = (dma_addr_t)NULL; - +err_free: if (!is_drm_iommu_supported(dev)) drm_free_large(buf->pages); diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c index fb19ee53ecbb..42a5a5466075 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c @@ -388,12 +388,9 @@ out: sg_free_table(g2d_userptr->sgt); kfree(g2d_userptr->sgt); - g2d_userptr->sgt = NULL; drm_free_large(g2d_userptr->pages); - g2d_userptr->pages = NULL; kfree(g2d_userptr); - g2d_userptr = NULL; } static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev, @@ -466,8 +463,8 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev, pages = drm_calloc_large(npages, sizeof(struct page *)); if (!pages) { DRM_ERROR("failed to allocate pages.\n"); - kfree(g2d_userptr); - return ERR_PTR(-ENOMEM); + ret = -ENOMEM; + goto err_free; } vma = find_vma(current->mm, userptr); @@ -543,7 +540,6 @@ err_sg_free_table: err_free_sgt: kfree(sgt); - sgt = NULL; err_free_userptr: exynos_gem_put_pages_to_userptr(g2d_userptr->pages, @@ -555,9 +551,9 @@ err_put_vma: err_free_pages: drm_free_large(pages); + +err_free: kfree(g2d_userptr); - pages = NULL; - g2d_userptr = NULL; return ERR_PTR(ret); } -- cgit v1.2.3 From c20eb0f1d0918c019fb2674a104e6ab59d8e62ac Mon Sep 17 00:00:00 2001 From: James Hogan Date: Thu, 4 Jul 2013 09:47:45 +0100 Subject: metag: move EXPORT_SYMBOL(csum_partial) to metag_ksyms.c Move EXPORT_SYMBOL(csum_partial) from lib/checksum.c into metag_ksyms.c so that it doesn't get omitted by the static linker if it's not used by any other statically linked code, which can result in undefined symbols when building modules. For example a randconfig caused the following error: ERROR: "csum_partial" [fs/reiserfs/reiserfs.ko] undefined! Signed-off-by: James Hogan --- arch/metag/kernel/metag_ksyms.c | 5 +++++ arch/metag/lib/checksum.c | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/metag/kernel/metag_ksyms.c b/arch/metag/kernel/metag_ksyms.c index ec872ef14eb1..215c94ad63ac 100644 --- a/arch/metag/kernel/metag_ksyms.c +++ b/arch/metag/kernel/metag_ksyms.c @@ -1,5 +1,7 @@ #include +#include +#include #include #include #include @@ -15,6 +17,9 @@ EXPORT_SYMBOL(max_pfn); EXPORT_SYMBOL(min_low_pfn); #endif +/* Network checksum functions */ +EXPORT_SYMBOL(csum_partial); + /* TBI symbols */ EXPORT_SYMBOL(__TBI); EXPORT_SYMBOL(__TBIFindSeg); diff --git a/arch/metag/lib/checksum.c b/arch/metag/lib/checksum.c index 44d2e1913560..5d6a98a05e9d 100644 --- a/arch/metag/lib/checksum.c +++ b/arch/metag/lib/checksum.c @@ -124,7 +124,6 @@ __wsum csum_partial(const void *buff, int len, __wsum wsum) result += 1; return (__force __wsum)result; } -EXPORT_SYMBOL(csum_partial); /* * this routine is used for miscellaneous IP-like checksums, mainly -- cgit v1.2.3 From 1baa3b4e06f1e03f4fbbbc3734ea3b9a7a1134ec Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 4 Jul 2013 11:48:38 +0530 Subject: regulator: s5m8767: Fix a trivial typo in documentation Changed volatage to voltage. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/regulator/s5m8767-regulator.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/regulator/s5m8767-regulator.txt b/Documentation/devicetree/bindings/regulator/s5m8767-regulator.txt index 7364f7114fbd..d1660a90fc06 100644 --- a/Documentation/devicetree/bindings/regulator/s5m8767-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/s5m8767-regulator.txt @@ -1,6 +1,6 @@ * Samsung S5M8767 Voltage and Current Regulator -The Samsung S5M8767 is a multi-function device which includes volatage and +The Samsung S5M8767 is a multi-function device which includes voltage and current regulators, rtc, charger controller and other sub-blocks. It is interfaced to the host controller using a i2c interface. Each sub-block is addressed by the host system using different i2c slave address. This document -- cgit v1.2.3 From 489cceeeb808199f55dbff2adaddd7394fb88de6 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 4 Jul 2013 11:48:39 +0530 Subject: regulator: max8997: Fix a trivial typo in documentation Changed volatage to voltage. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/regulator/max8997-regulator.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/regulator/max8997-regulator.txt b/Documentation/devicetree/bindings/regulator/max8997-regulator.txt index 9e5e51d78868..5c186a7a77ba 100644 --- a/Documentation/devicetree/bindings/regulator/max8997-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/max8997-regulator.txt @@ -1,6 +1,6 @@ * Maxim MAX8997 Voltage and Current Regulator -The Maxim MAX8997 is a multi-function device which includes volatage and +The Maxim MAX8997 is a multi-function device which includes voltage and current regulators, rtc, charger controller and other sub-blocks. It is interfaced to the host controller using a i2c interface. Each sub-block is addressed by the host system using different i2c slave address. This document -- cgit v1.2.3 From 6febb5abc597849b3fe918cc69432dc9cf5ab025 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 4 Jul 2013 12:08:50 +0530 Subject: MAINTAINERS: Update git repository The current git repository hasn't been updated since quite a long time. Update it with Mark's git repo which is the currently maintained one. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index ad7e322ad17b..199e8078e622 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8895,7 +8895,7 @@ M: Liam Girdwood M: Mark Brown W: http://opensource.wolfsonmicro.com/node/15 W: http://www.slimlogic.co.uk/?p=48 -T: git git://git.kernel.org/pub/scm/linux/kernel/git/lrg/regulator.git +T: git git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator.git S: Supported F: drivers/regulator/ F: include/linux/regulator/ -- cgit v1.2.3 From 8f0b3b7e222383a21f7d58bd97d5552b3a5dbced Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 4 Jul 2013 12:54:22 +0200 Subject: ALSA: hda - Fix EAPD vmaster hook for AD1884 & co ad1884_fixup_hp_eapd() tries to set the NID for controlling the speaker EAPD from the pin configuration. But the current code can't work expectedly since it sets spec->eapd_nid before calling the generic parser where the autocfg pins are set up. This patch changes the function to set spec->eapd_nid after the generic parser call while it sets vmaster hook unconditionally. The spec->eapd_nid check is moved in the hook function itself instead. Cc: [v3.9+] Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_analog.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 977b0d878dae..d97f0d61a15b 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -2112,6 +2112,9 @@ static void ad_vmaster_eapd_hook(void *private_data, int enabled) { struct hda_codec *codec = private_data; struct ad198x_spec *spec = codec->spec; + + if (!spec->eapd_nid) + return; snd_hda_codec_update_cache(codec, spec->eapd_nid, 0, AC_VERB_SET_EAPD_BTLENABLE, enabled ? 0x02 : 0x00); @@ -3601,13 +3604,16 @@ static void ad1884_fixup_hp_eapd(struct hda_codec *codec, { struct ad198x_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PRE_PROBE) { + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook; + break; + case HDA_FIXUP_ACT_PROBE: if (spec->gen.autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) spec->eapd_nid = spec->gen.autocfg.line_out_pins[0]; else spec->eapd_nid = spec->gen.autocfg.speaker_pins[0]; - if (spec->eapd_nid) - spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook; + break; } } -- cgit v1.2.3 From 266c13d767be61a17d8e6f2310b9b7c46278273b Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 2 Jul 2013 16:36:28 +0530 Subject: cpufreq: Fix serialization of frequency transitions Commit 7c30ed ("cpufreq: make sure frequency transitions are serialized") interacts poorly with systems that have a single core freqency for all cores. On such systems we have a single policy for all cores with several CPUs. When we do a frequency transition the governor calls the pre and post change notifiers which causes cpufreq_notify_transition() per CPU. Since the policy is the same for all of them all CPUs after the first and the warnings added are generated by checking a per-policy flag the warnings will be triggered for all cores after the first. Fix this by allowing notifier to be called for n times. Where n is the number of cpus in policy->cpus. Reported-and-tested-by: Mark Brown Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq.c | 7 ++++--- include/linux/cpufreq.h | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 6a015ada5285..0937b8d6c2a4 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -312,11 +312,12 @@ static void __cpufreq_notify_transition(struct cpufreq_policy *policy, switch (state) { case CPUFREQ_PRECHANGE: - if (WARN(policy->transition_ongoing, + if (WARN(policy->transition_ongoing == + cpumask_weight(policy->cpus), "In middle of another frequency transition\n")) return; - policy->transition_ongoing = true; + policy->transition_ongoing++; /* detect if the driver reported a value as "old frequency" * which is not equal to what the cpufreq core thinks is @@ -341,7 +342,7 @@ static void __cpufreq_notify_transition(struct cpufreq_policy *policy, "No frequency transition in progress\n")) return; - policy->transition_ongoing = false; + policy->transition_ongoing--; adjust_jiffies(CPUFREQ_POSTCHANGE, freqs); pr_debug("FREQ: %lu - CPU: %lu", (unsigned long)freqs->new, diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 4d7390bc1727..90d5a15120d5 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -119,7 +119,7 @@ struct cpufreq_policy { struct kobject kobj; struct completion kobj_unregister; - bool transition_ongoing; /* Tracks transition status */ + int transition_ongoing; /* Tracks transition status */ }; #define CPUFREQ_ADJUST (0) -- cgit v1.2.3 From 91bdad0b6237c25a7bf8fd4604d0cc64a2005a23 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 4 Jul 2013 13:22:11 +0200 Subject: ACPI / PM: Fix corner case in acpi_bus_update_power() The role of acpi_bus_update_power() is to update the given ACPI device object's power.state field to reflect the current physical state of the device (as inferred from the configuration of power resources and _PSC, if available). For this purpose it calls acpi_device_set_power() that should update the power resources' reference counters and set power.state as appropriate. However, that doesn't work if the "new" state is D1, D2 or D3hot and the the current value of power.state means D3cold, because in that case acpi_device_set_power() will refuse to transition the device from D3cold to non-D0. To address this problem, make acpi_bus_update_power() call acpi_power_transition() directly to update the power resources' reference counters and only use acpi_device_set_power() to put the device into D0 if the current physical state of it cannot be determined. Signed-off-by: Rafael J. Wysocki Cc: 3.9+ --- drivers/acpi/device_pm.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index e9e8bb24785b..4ab807dc8518 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -324,14 +324,27 @@ int acpi_bus_update_power(acpi_handle handle, int *state_p) if (result) return result; - if (state == ACPI_STATE_UNKNOWN) + if (state == ACPI_STATE_UNKNOWN) { state = ACPI_STATE_D0; - - result = acpi_device_set_power(device, state); - if (!result && state_p) + result = acpi_device_set_power(device, state); + if (result) + return result; + } else { + if (device->power.flags.power_resources) { + /* + * We don't need to really switch the state, bu we need + * to update the power resources' reference counters. + */ + result = acpi_power_transition(device, state); + if (result) + return result; + } + device->power.state = state; + } + if (state_p) *state_p = state; - return result; + return 0; } EXPORT_SYMBOL_GPL(acpi_bus_update_power); -- cgit v1.2.3 From 2ce65fe89153b6091393541de7e211d505436ff7 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 4 Jul 2013 13:25:04 +0200 Subject: ACPI / dock: Actually define acpi_dock_init() as void Commit 94add0f (ACPI / dock: Initialize ACPI dock subsystem upfront) changed the header of acpi_dock_init() in internal.h so that it is supposed to be a void function now, but it forgot to update its actual definition in dock.c according to which it still is supposed to return int. Although that didn't cause any visible breakage or even a compiler warning to be thrown, which is odd enough, fix it. Signed-off-by: Rafael J. Wysocki Cc: 3.10+ --- drivers/acpi/dock.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 14de9f46972e..826560753389 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -1064,10 +1064,10 @@ find_dock_and_bay(acpi_handle handle, u32 lvl, void *context, void **rv) return AE_OK; } -int __init acpi_dock_init(void) +void __init acpi_dock_init(void) { if (acpi_disabled) - return 0; + return; /* look for dock stations and bays */ acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, @@ -1075,11 +1075,10 @@ int __init acpi_dock_init(void) if (!dock_station_count) { pr_info(PREFIX "No dock devices found.\n"); - return 0; + return; } register_acpi_bus_notifier(&dock_acpi_notifier); pr_info(PREFIX "%s: %d docks/bays found\n", ACPI_DOCK_DRIVER_DESCRIPTION, dock_station_count); - return 0; } -- cgit v1.2.3 From b67cf7c44c7e6b485f1c255ac3c1fe98cc99b677 Mon Sep 17 00:00:00 2001 From: Haicheng Li Date: Thu, 4 Jul 2013 12:07:11 +0800 Subject: ACPI / scan: remove unused LIST_HEAD(acpi_device_list) The acpi_device_list list is not used, so removed it. [rjw: Changelog] Signed-off-by: Haicheng Li Signed-off-by: Rafael J. Wysocki --- drivers/acpi/scan.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index dfe76f17cfc4..10985573aaa7 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -35,7 +35,6 @@ bool acpi_force_hot_remove; static const char *dummy_hid = "device"; -static LIST_HEAD(acpi_device_list); static LIST_HEAD(acpi_bus_id_list); static DEFINE_MUTEX(acpi_scan_lock); static LIST_HEAD(acpi_scan_handlers_list); -- cgit v1.2.3 From 85eb98274fd81c44e721c275ced54661ee9d5b05 Mon Sep 17 00:00:00 2001 From: Naresh Bhat Date: Mon, 1 Jul 2013 19:51:00 +0530 Subject: ACPI / fan: Initialize acpi_state variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the following compiler warning go away: CC drivers/acpi/fan.o drivers/acpi/fan.c: In function ‘fan_get_cur_state’: drivers/acpi/fan.c:96:9: warning: ‘acpi_state’ may be used uninitialized in this function [-Wuninitialized] by initializing the local variable acpi_state in fan_get_cur_state(). [rjw: Changelog] Signed-off-by: Naresh Bhat Signed-off-by: Rafael J. Wysocki --- drivers/acpi/fan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index 8d1c0105e113..5b02a0aa540c 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan.c @@ -84,7 +84,7 @@ static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long { struct acpi_device *device = cdev->devdata; int result; - int acpi_state; + int acpi_state = ACPI_STATE_D0; if (!device) return -EINVAL; -- cgit v1.2.3 From 176455e9a9e2a37a0b3eec6f6d7883de720c02c7 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Tue, 2 Jul 2013 10:42:40 +0000 Subject: xen/arm and xen/arm64: implement HYPERVISOR_tmem_op Signed-off-by: Stefano Stabellini --- arch/arm/include/asm/xen/hypercall.h | 1 + arch/arm/xen/enlighten.c | 1 + arch/arm/xen/hypercall.S | 1 + arch/arm64/xen/hypercall.S | 1 + 4 files changed, 4 insertions(+) diff --git a/arch/arm/include/asm/xen/hypercall.h b/arch/arm/include/asm/xen/hypercall.h index 799f42ecca63..7704e28c3483 100644 --- a/arch/arm/include/asm/xen/hypercall.h +++ b/arch/arm/include/asm/xen/hypercall.h @@ -47,6 +47,7 @@ unsigned long HYPERVISOR_hvm_op(int op, void *arg); int HYPERVISOR_memory_op(unsigned int cmd, void *arg); int HYPERVISOR_physdev_op(int cmd, void *arg); int HYPERVISOR_vcpu_op(int cmd, int vcpuid, void *extra_args); +int HYPERVISOR_tmem_op(void *arg); static inline void MULTI_update_va_mapping(struct multicall_entry *mcl, unsigned long va, diff --git a/arch/arm/xen/enlighten.c b/arch/arm/xen/enlighten.c index 13609e01f4b7..f71c37edca26 100644 --- a/arch/arm/xen/enlighten.c +++ b/arch/arm/xen/enlighten.c @@ -314,4 +314,5 @@ EXPORT_SYMBOL_GPL(HYPERVISOR_hvm_op); EXPORT_SYMBOL_GPL(HYPERVISOR_memory_op); EXPORT_SYMBOL_GPL(HYPERVISOR_physdev_op); EXPORT_SYMBOL_GPL(HYPERVISOR_vcpu_op); +EXPORT_SYMBOL_GPL(HYPERVISOR_tmem_op); EXPORT_SYMBOL_GPL(privcmd_call); diff --git a/arch/arm/xen/hypercall.S b/arch/arm/xen/hypercall.S index 199cb2da7663..d1cf7b7c2200 100644 --- a/arch/arm/xen/hypercall.S +++ b/arch/arm/xen/hypercall.S @@ -88,6 +88,7 @@ HYPERCALL2(hvm_op); HYPERCALL2(memory_op); HYPERCALL2(physdev_op); HYPERCALL3(vcpu_op); +HYPERCALL1(tmem_op); ENTRY(privcmd_call) stmdb sp!, {r4} diff --git a/arch/arm64/xen/hypercall.S b/arch/arm64/xen/hypercall.S index 2816c479cd49..531342ec4bcf 100644 --- a/arch/arm64/xen/hypercall.S +++ b/arch/arm64/xen/hypercall.S @@ -79,6 +79,7 @@ HYPERCALL2(hvm_op); HYPERCALL2(memory_op); HYPERCALL2(physdev_op); HYPERCALL3(vcpu_op); +HYPERCALL1(tmem_op); ENTRY(privcmd_call) mov x16, x0 -- cgit v1.2.3 From c3eb5b14449a0949e9764d39374a2ea63faae14f Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Thu, 4 Jul 2013 13:34:32 +0100 Subject: arm64: KVM: Kconfig integration Finally plug KVM/arm64 into the config system, making it possible to enable KVM support on AArch64 CPUs. Signed-off-by: Marc Zyngier Signed-off-by: Paolo Bonzini --- arch/arm64/Kconfig | 2 ++ arch/arm64/kernel/asm-offsets.c | 1 + arch/arm64/kvm/Kconfig | 51 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 arch/arm64/kvm/Kconfig diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 4143d9b0d87a..9737e97f9f38 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -270,6 +270,8 @@ source "drivers/Kconfig" source "fs/Kconfig" +source "arch/arm64/kvm/Kconfig" + source "arch/arm64/Kconfig.debug" source "security/Kconfig" diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c index 49c162c03b69..666e231d410b 100644 --- a/arch/arm64/kernel/asm-offsets.c +++ b/arch/arm64/kernel/asm-offsets.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/arm64/kvm/Kconfig b/arch/arm64/kvm/Kconfig new file mode 100644 index 000000000000..21e90820bd23 --- /dev/null +++ b/arch/arm64/kvm/Kconfig @@ -0,0 +1,51 @@ +# +# KVM configuration +# + +source "virt/kvm/Kconfig" + +menuconfig VIRTUALIZATION + bool "Virtualization" + ---help--- + Say Y here to get to see options for using your Linux host to run + other operating systems inside virtual machines (guests). + This option alone does not add any kernel code. + + If you say N, all options in this submenu will be skipped and + disabled. + +if VIRTUALIZATION + +config KVM + bool "Kernel-based Virtual Machine (KVM) support" + select MMU_NOTIFIER + select PREEMPT_NOTIFIERS + select ANON_INODES + select KVM_MMIO + select KVM_ARM_HOST + select KVM_ARM_VGIC + select KVM_ARM_TIMER + ---help--- + Support hosting virtualized guest machines. + + If unsure, say N. + +config KVM_ARM_HOST + bool + ---help--- + Provides host support for ARM processors. + +config KVM_ARM_VGIC + bool + depends on KVM_ARM_HOST && OF + select HAVE_KVM_IRQCHIP + ---help--- + Adds support for a hardware assisted, in-kernel GIC emulation. + +config KVM_ARM_TIMER + bool + depends on KVM_ARM_VGIC + ---help--- + Adds support for the Architected Timers in virtual machines. + +endif # VIRTUALIZATION -- cgit v1.2.3 From 03617c188f41eeeb4223c919ee7e66e5a114f2c6 Mon Sep 17 00:00:00 2001 From: Gleb Natapov Date: Fri, 28 Jun 2013 13:17:18 +0300 Subject: KVM: VMX: mark unusable segment as nonpresent Some userspaces do not preserve unusable property. Since usable segment has to be present according to VMX spec we can use present property to amend userspace bug by making unusable segment always nonpresent. vmx_segment_access_rights() already marks nonpresent segment as unusable. Cc: stable@vger.kernel.org # 3.9+ Reported-by: Stefan Pietsch Tested-by: Stefan Pietsch Signed-off-by: Gleb Natapov Signed-off-by: Paolo Bonzini --- arch/x86/kvm/vmx.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index a7e18551c968..064d0be67ecc 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -3404,15 +3404,22 @@ static void vmx_get_segment(struct kvm_vcpu *vcpu, var->limit = vmx_read_guest_seg_limit(vmx, seg); var->selector = vmx_read_guest_seg_selector(vmx, seg); ar = vmx_read_guest_seg_ar(vmx, seg); + var->unusable = (ar >> 16) & 1; var->type = ar & 15; var->s = (ar >> 4) & 1; var->dpl = (ar >> 5) & 3; - var->present = (ar >> 7) & 1; + /* + * Some userspaces do not preserve unusable property. Since usable + * segment has to be present according to VMX spec we can use present + * property to amend userspace bug by making unusable segment always + * nonpresent. vmx_segment_access_rights() already marks nonpresent + * segment as unusable. + */ + var->present = !var->unusable; var->avl = (ar >> 12) & 1; var->l = (ar >> 13) & 1; var->db = (ar >> 14) & 1; var->g = (ar >> 15) & 1; - var->unusable = (ar >> 16) & 1; } static u64 vmx_get_segment_base(struct kvm_vcpu *vcpu, int seg) -- cgit v1.2.3 From b3ff04668c5a36c290e5b2849b6c86ceb3aec2c3 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 4 Jul 2013 16:24:37 +0530 Subject: ASoC: Samsung: Remove redundant comment There is a typo in the filename (i2c mentioned instead of i2s). However, this is a redundant piece of information. Delete it altogether. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown --- sound/soc/samsung/s3c-i2s-v2.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sound/soc/samsung/s3c-i2s-v2.c b/sound/soc/samsung/s3c-i2s-v2.c index 20e98d1dded2..e5e81b111001 100644 --- a/sound/soc/samsung/s3c-i2s-v2.c +++ b/sound/soc/samsung/s3c-i2s-v2.c @@ -1,6 +1,4 @@ -/* sound/soc/samsung/s3c-i2c-v2.c - * - * ALSA Soc Audio Layer - I2S core for newer Samsung SoCs. +/* ALSA Soc Audio Layer - I2S core for newer Samsung SoCs. * * Copyright (c) 2006 Wolfson Microelectronics PLC. * Graeme Gregory graeme.gregory@wolfsonmicro.com -- cgit v1.2.3 From 811adb9622de310efbb661531c3ec0ae5d2b2bc0 Mon Sep 17 00:00:00 2001 From: Andrew Duggan Date: Mon, 17 Jun 2013 16:15:06 -0700 Subject: HID: i2c-hid: support sending HID output reports using the output register The current i2c hid driver does not support sending HID output reports using the output register for devices which support receiving reports through this method. This patch determines which method to use to send output reports based on the value of wMaxOutputLength in the device's HID descriptor. Signed-off-by: Andrew Duggan Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index 2b1799a3b212..879b0ed701a3 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -108,6 +108,7 @@ static const struct i2c_hid_cmd hid_reset_cmd = { I2C_HID_CMD(0x01), static const struct i2c_hid_cmd hid_get_report_cmd = { I2C_HID_CMD(0x02) }; static const struct i2c_hid_cmd hid_set_report_cmd = { I2C_HID_CMD(0x03) }; static const struct i2c_hid_cmd hid_set_power_cmd = { I2C_HID_CMD(0x08) }; +static const struct i2c_hid_cmd hid_no_cmd = { .length = 0 }; /* * These definitions are not used here, but are defined by the spec. @@ -259,8 +260,11 @@ static int i2c_hid_set_report(struct i2c_client *client, u8 reportType, { struct i2c_hid *ihid = i2c_get_clientdata(client); u8 *args = ihid->argsbuf; + const struct i2c_hid_cmd * hidcmd = &hid_set_report_cmd; int ret; u16 dataRegister = le16_to_cpu(ihid->hdesc.wDataRegister); + u16 outputRegister = le16_to_cpu(ihid->hdesc.wOutputRegister); + u16 maxOutputLength = le16_to_cpu(ihid->hdesc.wMaxOutputLength); /* hidraw already checked that data_len < HID_MAX_BUFFER_SIZE */ u16 size = 2 /* size */ + @@ -278,8 +282,18 @@ static int i2c_hid_set_report(struct i2c_client *client, u8 reportType, reportID = 0x0F; } - args[index++] = dataRegister & 0xFF; - args[index++] = dataRegister >> 8; + /* + * use the data register for feature reports or if the device does not + * support the output register + */ + if (reportType == 0x03 || maxOutputLength == 0) { + args[index++] = dataRegister & 0xFF; + args[index++] = dataRegister >> 8; + } else { + args[index++] = outputRegister & 0xFF; + args[index++] = outputRegister >> 8; + hidcmd = &hid_no_cmd; + } args[index++] = size & 0xFF; args[index++] = size >> 8; @@ -289,7 +303,7 @@ static int i2c_hid_set_report(struct i2c_client *client, u8 reportType, memcpy(&args[index], buf, data_len); - ret = __i2c_hid_command(client, &hid_set_report_cmd, reportID, + ret = __i2c_hid_command(client, hidcmd, reportID, reportType, args, args_len, NULL, 0); if (ret) { dev_err(&client->dev, "failed to set a report to device.\n"); -- cgit v1.2.3 From 9d157624035214793c6d06b0512c6ab1a7b39e05 Mon Sep 17 00:00:00 2001 From: Przemo Firszt Date: Sat, 29 Jun 2013 10:57:13 +0100 Subject: HID: wacom: Intuos4 battery charging changes Intuos4 WL is separately reporting power supply and battery charging status - now hid-wacom is using that information. Previously hid-wacom was wrongly treating "battery charging" bit as "power supply connected". Now it should report battery charging, battery discharging, battery full and power supply status. Intuos4 WL sends reports when is in use (obvious) and when unplugging power supply. If means that if the device is being charged, but it's not being used it will never report "battery full". The same problem happens after the device has been connected, but it's not in use - the battery/ac status will be incorrect. Currently there is no mechanism to ask the device to send a report containing battery/ac status. Signed-off-by: Przemo Firszt Signed-off-by: Jiri Kosina --- drivers/hid/hid-wacom.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c index a4a8bb0da688..60c75dcbbdb8 100644 --- a/drivers/hid/hid-wacom.c +++ b/drivers/hid/hid-wacom.c @@ -46,6 +46,7 @@ struct wacom_data { __u8 battery_capacity; __u8 power_raw; __u8 ps_connected; + __u8 bat_charging; struct power_supply battery; struct power_supply ac; __u8 led_selector; @@ -62,6 +63,7 @@ static enum power_supply_property wacom_battery_props[] = { POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_SCOPE, + POWER_SUPPLY_PROP_STATUS, }; static enum power_supply_property wacom_ac_props[] = { @@ -287,6 +289,15 @@ static int wacom_battery_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CAPACITY: val->intval = wdata->battery_capacity; break; + case POWER_SUPPLY_PROP_STATUS: + if (wdata->bat_charging) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else + if (wdata->battery_capacity == 100 && wdata->ps_connected) + val->intval = POWER_SUPPLY_STATUS_FULL; + else + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + break; default: ret = -EINVAL; break; @@ -727,7 +738,8 @@ static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report, if (power_raw != wdata->power_raw) { wdata->power_raw = power_raw; wdata->battery_capacity = batcap_i4[power_raw & 0x07]; - wdata->ps_connected = power_raw & 0x08; + wdata->bat_charging = (power_raw & 0x08) ? 1 : 0; + wdata->ps_connected = (power_raw & 0x10) ? 1 : 0; } break; -- cgit v1.2.3 From 28e5ca73ef9072ed58dbb81cfff6f908be8e3cd4 Mon Sep 17 00:00:00 2001 From: "Arnaud Patard (Rtp)" Date: Thu, 20 Jun 2013 23:20:49 +0200 Subject: ASoC: imx-sgtl5000: return E_PROBE_DEFER if ssi/codec not found If the ssi or codec drivers are not loaded (for instance, because spi or i2c bus drivers are not loaded), returning -EINVAL will for people to unload and then reload the module to get sound working. Returning E_PROBE_DEFER will mitigate this. Signed-off-by: Arnaud Patard Signed-off-by: Mark Brown --- sound/soc/fsl/imx-sgtl5000.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/fsl/imx-sgtl5000.c b/sound/soc/fsl/imx-sgtl5000.c index 7a8bc1220b2e..3f726e4f88db 100644 --- a/sound/soc/fsl/imx-sgtl5000.c +++ b/sound/soc/fsl/imx-sgtl5000.c @@ -113,13 +113,13 @@ static int imx_sgtl5000_probe(struct platform_device *pdev) ssi_pdev = of_find_device_by_node(ssi_np); if (!ssi_pdev) { dev_err(&pdev->dev, "failed to find SSI platform device\n"); - ret = -EINVAL; + ret = -EPROBE_DEFER; goto fail; } codec_dev = of_find_i2c_device_by_node(codec_np); if (!codec_dev) { dev_err(&pdev->dev, "failed to find codec platform device\n"); - return -EINVAL; + return -EPROBE_DEFER; } data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); -- cgit v1.2.3 From 6dd18e4684f3d188277bbbc27545248487472108 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 3 Jul 2013 16:01:10 +1000 Subject: of: Fix address decoding on Bimini and js2x machines Commit: e38c0a1fbc5803cbacdaac0557c70ac8ca5152e7 of/address: Handle #address-cells > 2 specially broke real time clock access on Bimini, js2x, and similar powerpc machines using the "maple" platform. That code was indirectly relying on the old (broken) behaviour of the translation for the hypertransport to ISA bridge. This fixes it by treating hypertransport as a PCI bus Signed-off-by: Benjamin Herrenschmidt Acked-by: Rob Herring Signed-off-by: Grant Likely --- drivers/of/address.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/of/address.c b/drivers/of/address.c index 04da786c84d2..7c8221d36329 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -106,8 +106,12 @@ static unsigned int of_bus_default_get_flags(const __be32 *addr) static int of_bus_pci_match(struct device_node *np) { - /* "vci" is for the /chaos bridge on 1st-gen PCI powermacs */ - return !strcmp(np->type, "pci") || !strcmp(np->type, "vci"); + /* + * "vci" is for the /chaos bridge on 1st-gen PCI powermacs + * "ht" is hypertransport + */ + return !strcmp(np->type, "pci") || !strcmp(np->type, "vci") || + !strcmp(np->type, "ht"); } static void of_bus_pci_count_cells(struct device_node *np, -- cgit v1.2.3 From 4642aabd21d0491a7d9dcbe789bdf93a596f771c Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Thu, 4 Jul 2013 23:14:00 +0800 Subject: ASoC: wm8962: fix NULL pdata pointer There is an error in merge commit 384b834 on conflict resolution which causes the following NULL pdata pointer bug. wm8962 0-001a: customer id 0 revision D Unable to handle kernel NULL pointer dereference at virtual address 00000004 pgd = 80004000 [00000004] *pgd=00000000 Internal error: Oops: 5 [#1] SMP ARM Modules linked in: CPU: 0 PID: 1 Comm: swapper/0 Not tainted 3.10.0+ #1 task: bf870000 ti: bf874000 task.ti: bf874000 PC is at wm8962_probe+0x134/0x6c8 LR is at regmap_unlock_mutex+0x10/0x14 pc : [<80452100>] lr : [<80304cf4>] psr: a0000113 sp : bf875c98 ip : 00000000 fp : bf875cd4 r10: 00000000 r9 : bfb1830c r8 : 80779bc4 r7 : 00000000 r6 : 00000001 r5 : bfbac010 r4 : bfb33e00 r3 : 80304ce4 r2 : 00000000 r1 : 00000001 r0 : fffffffb Flags: NzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment kernel Control: 10c53c7d Table: 1000404a DAC: 00000017 Process swapper/0 (pid: 1, stack limit = 0xbf874238) Stack: (0xbf875c98 to 0xbf876000) ... Fix the error by assigning pdata a correct pointer. Signed-off-by: Shawn Guo Signed-off-by: Mark Brown --- sound/soc/codecs/wm8962.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index b1dc7d426438..e2de9ecfd641 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -3377,7 +3377,7 @@ static int wm8962_probe(struct snd_soc_codec *codec) { int ret; struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); - struct wm8962_pdata *pdata = dev_get_platdata(codec->dev); + struct wm8962_pdata *pdata = &wm8962->pdata; int i, trigger, irq_pol; bool dmicclk, dmicdat; -- cgit v1.2.3 From fa18f7bde3ad4568d1d343b60d963bfbd8dc3991 Mon Sep 17 00:00:00 2001 From: KOSAKI Motohiro Date: Sun, 26 May 2013 17:35:41 -0400 Subject: posix-cpu-timers: don't account cpu timer after stopped thread runtime accounting When tsk->signal->cputimer->running is 1, signal->cputimer (i.e. per process timer account) and tsk->sum_sched_runtime (i.e. per thread timer account) increase at the same pace because update_curr() increases both accounting. However, there is one exception. When thread exiting, __exit_signal() turns over task's sum_shced_runtime to sig->sum_sched_runtime, but it doesn't stop signal->cputimer accounting. This inconsistency makes POSIX timer wake up too early. This patch fixes it. Original-patch-by: Olivier Langlois Cc: Thomas Gleixner Cc: Ingo Molnar Cc: Peter Zijlstra Acked-by: Peter Zijlstra Signed-off-by: Olivier Langlois Signed-off-by: KOSAKI Motohiro Signed-off-by: Frederic Weisbecker --- kernel/sched/stats.h | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/kernel/sched/stats.h b/kernel/sched/stats.h index 2ef90a51ec5e..71bac979d5ee 100644 --- a/kernel/sched/stats.h +++ b/kernel/sched/stats.h @@ -161,6 +161,39 @@ sched_info_switch(struct task_struct *prev, struct task_struct *next) * on CONFIG_SCHEDSTATS. */ +/** + * cputimer_running - return true if cputimer is running + * + * @tsk: Pointer to target task. + */ +static inline bool cputimer_running(struct task_struct *tsk) + +{ + struct thread_group_cputimer *cputimer = &tsk->signal->cputimer; + + if (!cputimer->running) + return false; + + /* + * After we flush the task's sum_exec_runtime to sig->sum_sched_runtime + * in __exit_signal(), we won't account to the signal struct further + * cputime consumed by that task, even though the task can still be + * ticking after __exit_signal(). + * + * In order to keep a consistent behaviour between thread group cputime + * and thread group cputimer accounting, lets also ignore the cputime + * elapsing after __exit_signal() in any thread group timer running. + * + * This makes sure that POSIX CPU clocks and timers are synchronized, so + * that a POSIX CPU timer won't expire while the corresponding POSIX CPU + * clock delta is behind the expiring timer value. + */ + if (unlikely(!tsk->sighand)) + return false; + + return true; +} + /** * account_group_user_time - Maintain utime for a thread group. * @@ -176,7 +209,7 @@ static inline void account_group_user_time(struct task_struct *tsk, { struct thread_group_cputimer *cputimer = &tsk->signal->cputimer; - if (!cputimer->running) + if (!cputimer_running(tsk)) return; raw_spin_lock(&cputimer->lock); @@ -199,7 +232,7 @@ static inline void account_group_system_time(struct task_struct *tsk, { struct thread_group_cputimer *cputimer = &tsk->signal->cputimer; - if (!cputimer->running) + if (!cputimer_running(tsk)) return; raw_spin_lock(&cputimer->lock); @@ -222,7 +255,7 @@ static inline void account_group_exec_runtime(struct task_struct *tsk, { struct thread_group_cputimer *cputimer = &tsk->signal->cputimer; - if (!cputimer->running) + if (!cputimer_running(tsk)) return; raw_spin_lock(&cputimer->lock); -- cgit v1.2.3 From 3df32196519d1556b8851b610ca1aa68c5b673d3 Mon Sep 17 00:00:00 2001 From: Dmitry Monakhov Date: Tue, 28 May 2013 13:19:01 +0400 Subject: ext3: Fix fsync error handling after filesystem abort. If filesystem was aborted we will return success due to (sb->s_flags & MS_RDONLY) which is incorrect and results in data loss. In order to handle fs abort correctly we have to check fs state once we discover that it is in MS_RDONLY state Test case: http://patchwork.ozlabs.org/patch/244297/ Changes from V1: - fix spelling - fix smp_rmb()/debug order Signed-off-by: Dmitry Monakhov Signed-off-by: Jan Kara --- fs/ext3/fsync.c | 8 ++++++-- fs/ext3/super.c | 13 ++++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/fs/ext3/fsync.c b/fs/ext3/fsync.c index b31dbd4c46ad..1cb9c7e10c6f 100644 --- a/fs/ext3/fsync.c +++ b/fs/ext3/fsync.c @@ -48,9 +48,13 @@ int ext3_sync_file(struct file *file, loff_t start, loff_t end, int datasync) trace_ext3_sync_file_enter(file, datasync); - if (inode->i_sb->s_flags & MS_RDONLY) + if (inode->i_sb->s_flags & MS_RDONLY) { + /* Make sure that we read updated state */ + smp_rmb(); + if (EXT3_SB(inode->i_sb)->s_mount_state & EXT3_ERROR_FS) + return -EROFS; return 0; - + } ret = filemap_write_and_wait_range(inode->i_mapping, start, end); if (ret) goto out; diff --git a/fs/ext3/super.c b/fs/ext3/super.c index 6356665a74bb..c47f14750722 100644 --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -174,6 +174,11 @@ static void ext3_handle_error(struct super_block *sb) if (test_opt (sb, ERRORS_RO)) { ext3_msg(sb, KERN_CRIT, "error: remounting filesystem read-only"); + /* + * Make sure updated value of ->s_mount_state will be visible + * before ->s_flags update. + */ + smp_wmb(); sb->s_flags |= MS_RDONLY; } ext3_commit_super(sb, es, 1); @@ -291,8 +296,14 @@ void ext3_abort(struct super_block *sb, const char *function, ext3_msg(sb, KERN_CRIT, "error: remounting filesystem read-only"); EXT3_SB(sb)->s_mount_state |= EXT3_ERROR_FS; - sb->s_flags |= MS_RDONLY; set_opt(EXT3_SB(sb)->s_mount_opt, ABORT); + /* + * Make sure updated value of ->s_mount_state will be visible + * before ->s_flags update. + */ + smp_wmb(); + sb->s_flags |= MS_RDONLY; + if (EXT3_SB(sb)->s_journal) journal_abort(EXT3_SB(sb)->s_journal, -EIO); } -- cgit v1.2.3 From e628753bf925b058b6811527492f7529f4d27691 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 13 Jun 2013 19:37:49 -0700 Subject: quota: Convert use of typedef ctl_table to struct ctl_table This typedef is unnecessary and should just be removed. Signed-off-by: Joe Perches Signed-off-by: Jan Kara --- fs/quota/dquot.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index 3e64169ef527..fbad622841f9 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -2585,7 +2585,7 @@ static int do_proc_dqstats(struct ctl_table *table, int write, return proc_dointvec(table, write, buffer, lenp, ppos); } -static ctl_table fs_dqstats_table[] = { +static struct ctl_table fs_dqstats_table[] = { { .procname = "lookups", .data = &dqstats.stat[DQST_LOOKUPS], @@ -2654,7 +2654,7 @@ static ctl_table fs_dqstats_table[] = { { }, }; -static ctl_table fs_table[] = { +static struct ctl_table fs_table[] = { { .procname = "quota", .mode = 0555, @@ -2663,7 +2663,7 @@ static ctl_table fs_table[] = { { }, }; -static ctl_table sys_table[] = { +static struct ctl_table sys_table[] = { { .procname = "fs", .mode = 0555, -- cgit v1.2.3 From 3ebacb05044f82c5f0bb456a894eb9dc57d0ed90 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 4 Jul 2013 18:42:29 +0200 Subject: hpfs: better test for errors The test if bitmap access is out of bound could errorneously pass if the device size is divisible by 16384 sectors and we are asking for one bitmap after the end. Check for invalid size in the superblock. Invalid size could cause integer overflows in the rest of the code. Signed-off-by: Mikulas Patocka Cc: stable@kernel.org Signed-off-by: Linus Torvalds --- fs/hpfs/map.c | 3 ++- fs/hpfs/super.c | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/fs/hpfs/map.c b/fs/hpfs/map.c index 4acb19d78359..803d3da3a0fe 100644 --- a/fs/hpfs/map.c +++ b/fs/hpfs/map.c @@ -17,7 +17,8 @@ __le32 *hpfs_map_bitmap(struct super_block *s, unsigned bmp_block, struct quad_buffer_head *qbh, char *id) { secno sec; - if (hpfs_sb(s)->sb_chk) if (bmp_block * 16384 > hpfs_sb(s)->sb_fs_size) { + unsigned n_bands = (hpfs_sb(s)->sb_fs_size + 0x3fff) >> 14; + if (hpfs_sb(s)->sb_chk) if (bmp_block >= n_bands) { hpfs_error(s, "hpfs_map_bitmap called with bad parameter: %08x at %s", bmp_block, id); return NULL; } diff --git a/fs/hpfs/super.c b/fs/hpfs/super.c index a0617e706957..962e90c37aec 100644 --- a/fs/hpfs/super.c +++ b/fs/hpfs/super.c @@ -558,7 +558,13 @@ static int hpfs_fill_super(struct super_block *s, void *options, int silent) sbi->sb_cp_table = NULL; sbi->sb_c_bitmap = -1; sbi->sb_max_fwd_alloc = 0xffffff; - + + if (sbi->sb_fs_size >= 0x80000000) { + hpfs_error(s, "invalid size in superblock: %08x", + (unsigned)sbi->sb_fs_size); + goto bail4; + } + /* Load bitmap directory */ if (!(sbi->sb_bmp_dir = hpfs_load_bitmap_directory(s, le32_to_cpu(superblock->bitmaps)))) goto bail4; -- cgit v1.2.3 From a0c1b7596323a2323d5f5c7d2404af7b58a1ef4e Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 4 Jul 2013 18:44:27 +0200 Subject: hpfs: use mpage Use the mpage interface to improve performance. Signed-off-by: Mikulas Patocka Signed-off-by: Linus Torvalds --- fs/hpfs/file.c | 40 ++++++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/fs/hpfs/file.c b/fs/hpfs/file.c index e4ba5fe4c3b5..4e9dabcf1f4c 100644 --- a/fs/hpfs/file.c +++ b/fs/hpfs/file.c @@ -7,6 +7,7 @@ */ #include "hpfs_fn.h" +#include #define BLOCKS(size) (((size) + 511) >> 9) @@ -34,7 +35,7 @@ int hpfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync) * so we must ignore such errors. */ -static secno hpfs_bmap(struct inode *inode, unsigned file_secno) +static secno hpfs_bmap(struct inode *inode, unsigned file_secno, unsigned *n_secs) { struct hpfs_inode_info *hpfs_inode = hpfs_i(inode); unsigned n, disk_secno; @@ -42,11 +43,20 @@ static secno hpfs_bmap(struct inode *inode, unsigned file_secno) struct buffer_head *bh; if (BLOCKS(hpfs_i(inode)->mmu_private) <= file_secno) return 0; n = file_secno - hpfs_inode->i_file_sec; - if (n < hpfs_inode->i_n_secs) return hpfs_inode->i_disk_sec + n; + if (n < hpfs_inode->i_n_secs) { + *n_secs = hpfs_inode->i_n_secs - n; + return hpfs_inode->i_disk_sec + n; + } if (!(fnode = hpfs_map_fnode(inode->i_sb, inode->i_ino, &bh))) return 0; disk_secno = hpfs_bplus_lookup(inode->i_sb, inode, &fnode->btree, file_secno, bh); if (disk_secno == -1) return 0; if (hpfs_chk_sectors(inode->i_sb, disk_secno, 1, "bmap")) return 0; + n = file_secno - hpfs_inode->i_file_sec; + if (n < hpfs_inode->i_n_secs) { + *n_secs = hpfs_inode->i_n_secs - n; + return hpfs_inode->i_disk_sec + n; + } + *n_secs = 1; return disk_secno; } @@ -67,10 +77,14 @@ static int hpfs_get_block(struct inode *inode, sector_t iblock, struct buffer_he { int r; secno s; + unsigned n_secs; hpfs_lock(inode->i_sb); - s = hpfs_bmap(inode, iblock); + s = hpfs_bmap(inode, iblock, &n_secs); if (s) { + if (bh_result->b_size >> 9 < n_secs) + n_secs = bh_result->b_size >> 9; map_bh(bh_result, inode->i_sb, s); + bh_result->b_size = n_secs << 9; goto ret_0; } if (!create) goto ret_0; @@ -95,14 +109,26 @@ static int hpfs_get_block(struct inode *inode, sector_t iblock, struct buffer_he return r; } +static int hpfs_readpage(struct file *file, struct page *page) +{ + return mpage_readpage(page, hpfs_get_block); +} + static int hpfs_writepage(struct page *page, struct writeback_control *wbc) { - return block_write_full_page(page,hpfs_get_block, wbc); + return block_write_full_page(page, hpfs_get_block, wbc); } -static int hpfs_readpage(struct file *file, struct page *page) +static int hpfs_readpages(struct file *file, struct address_space *mapping, + struct list_head *pages, unsigned nr_pages) +{ + return mpage_readpages(mapping, pages, nr_pages, hpfs_get_block); +} + +static int hpfs_writepages(struct address_space *mapping, + struct writeback_control *wbc) { - return block_read_full_page(page,hpfs_get_block); + return mpage_writepages(mapping, wbc, hpfs_get_block); } static void hpfs_write_failed(struct address_space *mapping, loff_t to) @@ -161,6 +187,8 @@ static sector_t _hpfs_bmap(struct address_space *mapping, sector_t block) const struct address_space_operations hpfs_aops = { .readpage = hpfs_readpage, .writepage = hpfs_writepage, + .readpages = hpfs_readpages, + .writepages = hpfs_writepages, .write_begin = hpfs_write_begin, .write_end = hpfs_write_end, .bmap = _hpfs_bmap -- cgit v1.2.3 From 275f495dbe34300d793466a7d96c70f83fbae1bc Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Thu, 4 Jul 2013 19:04:01 +0200 Subject: hpfs: implement prefetch to improve performance This patch implements prefetch to improve performance. It helps mostly when scanning the bitmaps to calculate free space. Signed-off-by: Mikulas Patocka Signed-off-by: Linus Torvalds --- fs/hpfs/buffer.c | 33 +++++++++++++++++++++++++++++++++ fs/hpfs/hpfs_fn.h | 7 +++++-- fs/hpfs/map.c | 19 ++++++++++++++++++- fs/hpfs/super.c | 9 +++++++-- 4 files changed, 63 insertions(+), 5 deletions(-) diff --git a/fs/hpfs/buffer.c b/fs/hpfs/buffer.c index f49d1498aa2e..4d0a1afa058c 100644 --- a/fs/hpfs/buffer.c +++ b/fs/hpfs/buffer.c @@ -7,8 +7,37 @@ */ #include #include +#include #include "hpfs_fn.h" +void hpfs_prefetch_sectors(struct super_block *s, unsigned secno, int n) +{ + struct buffer_head *bh; + struct blk_plug plug; + + if (n <= 0 || unlikely(secno >= hpfs_sb(s)->sb_fs_size)) + return; + + bh = sb_find_get_block(s, secno); + if (bh) { + if (buffer_uptodate(bh)) { + brelse(bh); + return; + } + brelse(bh); + }; + + blk_start_plug(&plug); + while (n > 0) { + if (unlikely(secno >= hpfs_sb(s)->sb_fs_size)) + break; + sb_breadahead(s, secno); + secno++; + n--; + } + blk_finish_plug(&plug); +} + /* Map a sector into a buffer and return pointers to it and to the buffer. */ void *hpfs_map_sector(struct super_block *s, unsigned secno, struct buffer_head **bhp, @@ -18,6 +47,8 @@ void *hpfs_map_sector(struct super_block *s, unsigned secno, struct buffer_head hpfs_lock_assert(s); + hpfs_prefetch_sectors(s, secno, ahead); + cond_resched(); *bhp = bh = sb_bread(s, secno); @@ -67,6 +98,8 @@ void *hpfs_map_4sectors(struct super_block *s, unsigned secno, struct quad_buffe return NULL; } + hpfs_prefetch_sectors(s, secno, 4 + ahead); + qbh->data = data = kmalloc(2048, GFP_NOFS); if (!data) { printk("HPFS: hpfs_map_4sectors: out of memory\n"); diff --git a/fs/hpfs/hpfs_fn.h b/fs/hpfs/hpfs_fn.h index b7ae286646b5..1b398636e990 100644 --- a/fs/hpfs/hpfs_fn.h +++ b/fs/hpfs/hpfs_fn.h @@ -27,8 +27,9 @@ #define ALLOC_FWD_MAX 128 #define ALLOC_M 1 #define FNODE_RD_AHEAD 16 -#define ANODE_RD_AHEAD 16 -#define DNODE_RD_AHEAD 4 +#define ANODE_RD_AHEAD 0 +#define DNODE_RD_AHEAD 72 +#define COUNT_RD_AHEAD 62 #define FREE_DNODES_ADD 58 #define FREE_DNODES_DEL 29 @@ -207,6 +208,7 @@ void hpfs_remove_fnode(struct super_block *, fnode_secno fno); /* buffer.c */ +void hpfs_prefetch_sectors(struct super_block *, unsigned, int); void *hpfs_map_sector(struct super_block *, unsigned, struct buffer_head **, int); void *hpfs_get_sector(struct super_block *, unsigned, struct buffer_head **); void *hpfs_map_4sectors(struct super_block *, unsigned, struct quad_buffer_head *, int); @@ -271,6 +273,7 @@ void hpfs_evict_inode(struct inode *); __le32 *hpfs_map_dnode_bitmap(struct super_block *, struct quad_buffer_head *); __le32 *hpfs_map_bitmap(struct super_block *, unsigned, struct quad_buffer_head *, char *); +void hpfs_prefetch_bitmap(struct super_block *, unsigned); unsigned char *hpfs_load_code_page(struct super_block *, secno); __le32 *hpfs_load_bitmap_directory(struct super_block *, secno bmp); struct fnode *hpfs_map_fnode(struct super_block *s, ino_t, struct buffer_head **); diff --git a/fs/hpfs/map.c b/fs/hpfs/map.c index 803d3da3a0fe..3aa66ae1031e 100644 --- a/fs/hpfs/map.c +++ b/fs/hpfs/map.c @@ -17,6 +17,7 @@ __le32 *hpfs_map_bitmap(struct super_block *s, unsigned bmp_block, struct quad_buffer_head *qbh, char *id) { secno sec; + __le32 *ret; unsigned n_bands = (hpfs_sb(s)->sb_fs_size + 0x3fff) >> 14; if (hpfs_sb(s)->sb_chk) if (bmp_block >= n_bands) { hpfs_error(s, "hpfs_map_bitmap called with bad parameter: %08x at %s", bmp_block, id); @@ -27,7 +28,23 @@ __le32 *hpfs_map_bitmap(struct super_block *s, unsigned bmp_block, hpfs_error(s, "invalid bitmap block pointer %08x -> %08x at %s", bmp_block, sec, id); return NULL; } - return hpfs_map_4sectors(s, sec, qbh, 4); + ret = hpfs_map_4sectors(s, sec, qbh, 4); + if (ret) hpfs_prefetch_bitmap(s, bmp_block + 1); + return ret; +} + +void hpfs_prefetch_bitmap(struct super_block *s, unsigned bmp_block) +{ + unsigned to_prefetch, next_prefetch; + unsigned n_bands = (hpfs_sb(s)->sb_fs_size + 0x3fff) >> 14; + if (unlikely(bmp_block >= n_bands)) + return; + to_prefetch = le32_to_cpu(hpfs_sb(s)->sb_bmp_dir[bmp_block]); + if (unlikely(bmp_block + 1 >= n_bands)) + next_prefetch = 0; + else + next_prefetch = le32_to_cpu(hpfs_sb(s)->sb_bmp_dir[bmp_block + 1]); + hpfs_prefetch_sectors(s, to_prefetch, 4 + 4 * (to_prefetch + 4 == next_prefetch)); } /* diff --git a/fs/hpfs/super.c b/fs/hpfs/super.c index 962e90c37aec..4334cda8dba1 100644 --- a/fs/hpfs/super.c +++ b/fs/hpfs/super.c @@ -121,7 +121,7 @@ unsigned hpfs_count_one_bitmap(struct super_block *s, secno secno) unsigned long *bits; unsigned count; - bits = hpfs_map_4sectors(s, secno, &qbh, 4); + bits = hpfs_map_4sectors(s, secno, &qbh, 0); if (!bits) return 0; count = bitmap_weight(bits, 2048 * BITS_PER_BYTE); @@ -134,8 +134,13 @@ static unsigned count_bitmaps(struct super_block *s) unsigned n, count, n_bands; n_bands = (hpfs_sb(s)->sb_fs_size + 0x3fff) >> 14; count = 0; - for (n = 0; n < n_bands; n++) + for (n = 0; n < COUNT_RD_AHEAD; n++) { + hpfs_prefetch_bitmap(s, n); + } + for (n = 0; n < n_bands; n++) { + hpfs_prefetch_bitmap(s, n + COUNT_RD_AHEAD); count += hpfs_count_one_bitmap(s, le32_to_cpu(hpfs_sb(s)->sb_bmp_dir[n])); + } return count; } -- cgit v1.2.3 From d1a1dc0be866219f7a613c6368c6a036b8eefe03 Mon Sep 17 00:00:00 2001 From: Dave Hansen Date: Mon, 1 Jul 2013 13:04:42 -0700 Subject: consolidate per-arch stack overflow debugging options Original posting: http://lkml.kernel.org/r/20121214184202.F54094D9@kernel.stglabs.ibm.com Several architectures have similar stack debugging config options. They all pretty much do the same thing, some with slightly differing help text. This patch changes the architectures to instead enable a Kconfig boolean, and then use that boolean in the generic Kconfig.debug to present the actual menu option. This removes a bunch of duplication and adds consistency across arches. Signed-off-by: Dave Hansen Reviewed-by: H. Peter Anvin Reviewed-by: James Hogan Acked-by: Chris Metcalf [for tile] Signed-off-by: Dave Hansen Signed-off-by: Linus Torvalds --- arch/arc/Kconfig | 1 + arch/arc/Kconfig.debug | 7 ------- arch/blackfin/Kconfig | 1 + arch/blackfin/Kconfig.debug | 7 ------- arch/frv/Kconfig | 1 + arch/frv/Kconfig.debug | 4 ---- arch/m32r/Kconfig | 1 + arch/m32r/Kconfig.debug | 7 ------- arch/metag/Kconfig | 1 + arch/metag/Kconfig.debug | 7 ------- arch/mips/Kconfig | 1 + arch/mips/Kconfig.debug | 9 --------- arch/mn10300/Kconfig | 1 + arch/mn10300/Kconfig.debug | 4 ---- arch/openrisc/Kconfig | 11 +---------- arch/parisc/Kconfig | 1 + arch/parisc/Kconfig.debug | 11 ----------- arch/powerpc/Kconfig | 1 + arch/powerpc/Kconfig.debug | 7 ------- arch/tile/Kconfig | 1 + arch/tile/Kconfig.debug | 7 ------- arch/x86/Kconfig | 1 + arch/x86/Kconfig.debug | 10 ---------- lib/Kconfig.debug | 21 +++++++++++++++++++++ 24 files changed, 33 insertions(+), 90 deletions(-) diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index 5917099470ea..cff2aad31041 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig @@ -34,6 +34,7 @@ config ARC select OF select OF_EARLY_FLATTREE select PERF_USE_VMALLOC + select HAVE_DEBUG_STACKOVERFLOW config SCHED_OMIT_FRAME_POINTER def_bool y diff --git a/arch/arc/Kconfig.debug b/arch/arc/Kconfig.debug index 962c6099659e..a7fc0da25650 100644 --- a/arch/arc/Kconfig.debug +++ b/arch/arc/Kconfig.debug @@ -15,13 +15,6 @@ config EARLY_PRINTK with klogd/syslogd or the X server. You should normally N here, unless you want to debug such a crash. -config DEBUG_STACKOVERFLOW - bool "Check for stack overflows" - depends on DEBUG_KERNEL - help - This option will cause messages to be printed if free stack space - drops below a certain limit. - config 16KSTACKS bool "Use 16Kb for kernel stacks instead of 8Kb" help diff --git a/arch/blackfin/Kconfig b/arch/blackfin/Kconfig index a117652b5fea..82707a77aa73 100644 --- a/arch/blackfin/Kconfig +++ b/arch/blackfin/Kconfig @@ -41,6 +41,7 @@ config BLACKFIN select ARCH_USES_GETTIMEOFFSET if !GENERIC_CLOCKEVENTS select HAVE_MOD_ARCH_SPECIFIC select MODULES_USE_ELF_RELA + select HAVE_DEBUG_STACKOVERFLOW config GENERIC_CSUM def_bool y diff --git a/arch/blackfin/Kconfig.debug b/arch/blackfin/Kconfig.debug index 79594694ee90..f3337ee03621 100644 --- a/arch/blackfin/Kconfig.debug +++ b/arch/blackfin/Kconfig.debug @@ -2,13 +2,6 @@ menu "Kernel hacking" source "lib/Kconfig.debug" -config DEBUG_STACKOVERFLOW - bool "Check for stack overflows" - depends on DEBUG_KERNEL - help - This option will cause messages to be printed if free stack space - drops below a certain limit. - config DEBUG_VERBOSE bool "Verbose fault messages" default y diff --git a/arch/frv/Kconfig b/arch/frv/Kconfig index 2ce731f9aa4d..4b6628ea381e 100644 --- a/arch/frv/Kconfig +++ b/arch/frv/Kconfig @@ -14,6 +14,7 @@ config FRV select ARCH_WANT_IPC_PARSE_VERSION select OLD_SIGSUSPEND3 select OLD_SIGACTION + select HAVE_DEBUG_STACKOVERFLOW config ZONE_DMA bool diff --git a/arch/frv/Kconfig.debug b/arch/frv/Kconfig.debug index 211f01bc4caa..98c99a3ed2be 100644 --- a/arch/frv/Kconfig.debug +++ b/arch/frv/Kconfig.debug @@ -2,10 +2,6 @@ menu "Kernel hacking" source "lib/Kconfig.debug" -config DEBUG_STACKOVERFLOW - bool "Check for stack overflows" - depends on DEBUG_KERNEL - config GDBSTUB bool "Remote GDB kernel debugging" depends on DEBUG_KERNEL diff --git a/arch/m32r/Kconfig b/arch/m32r/Kconfig index bcd17b206571..29a7ef4e448b 100644 --- a/arch/m32r/Kconfig +++ b/arch/m32r/Kconfig @@ -16,6 +16,7 @@ config M32R select GENERIC_ATOMIC64 select ARCH_USES_GETTIMEOFFSET select MODULES_USE_ELF_RELA + select HAVE_DEBUG_STACKOVERFLOW config SBUS bool diff --git a/arch/m32r/Kconfig.debug b/arch/m32r/Kconfig.debug index bb1afc1a31cc..6c612b7691b0 100644 --- a/arch/m32r/Kconfig.debug +++ b/arch/m32r/Kconfig.debug @@ -2,13 +2,6 @@ menu "Kernel hacking" source "lib/Kconfig.debug" -config DEBUG_STACKOVERFLOW - bool "Check for stack overflows" - depends on DEBUG_KERNEL - help - This option will cause messages to be printed if free stack space - drops below a certain limit. - config DEBUG_PAGEALLOC bool "Debug page memory allocations" depends on DEBUG_KERNEL && BROKEN diff --git a/arch/metag/Kconfig b/arch/metag/Kconfig index dcd94406030e..cfd831c29824 100644 --- a/arch/metag/Kconfig +++ b/arch/metag/Kconfig @@ -30,6 +30,7 @@ config METAG select OF select OF_EARLY_FLATTREE select SPARSE_IRQ + select HAVE_DEBUG_STACKOVERFLOW config STACKTRACE_SUPPORT def_bool y diff --git a/arch/metag/Kconfig.debug b/arch/metag/Kconfig.debug index e45bbf6a7a5d..cb5c92860540 100644 --- a/arch/metag/Kconfig.debug +++ b/arch/metag/Kconfig.debug @@ -6,13 +6,6 @@ config TRACE_IRQFLAGS_SUPPORT source "lib/Kconfig.debug" -config DEBUG_STACKOVERFLOW - bool "Check for stack overflows" - depends on DEBUG_KERNEL - help - This option will cause messages to be printed if free stack space - drops below a certain limit. - config 4KSTACKS bool "Use 4Kb for kernel stacks instead of 8Kb" depends on DEBUG_KERNEL diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 7a58ab933b20..7aae1ef096ea 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -42,6 +42,7 @@ config MIPS select MODULES_USE_ELF_REL if MODULES select MODULES_USE_ELF_RELA if MODULES && 64BIT select CLONE_BACKWARDS + select HAVE_DEBUG_STACKOVERFLOW menu "Machine selection" diff --git a/arch/mips/Kconfig.debug b/arch/mips/Kconfig.debug index 5a43aa0798ca..37871f0de15e 100644 --- a/arch/mips/Kconfig.debug +++ b/arch/mips/Kconfig.debug @@ -67,15 +67,6 @@ config CMDLINE_OVERRIDE Normally, you will choose 'N' here. -config DEBUG_STACKOVERFLOW - bool "Check for stack overflows" - depends on DEBUG_KERNEL - help - This option will cause messages to be printed if free stack space - drops below a certain limit(2GB on MIPS). The debugging option - provides another way to check stack overflow happened on kernel mode - stack usually caused by nested interruption. - config SMTC_IDLE_HOOK_DEBUG bool "Enable additional debug checks before going into CPU idle loop" depends on DEBUG_KERNEL && MIPS_MT_SMTC diff --git a/arch/mn10300/Kconfig b/arch/mn10300/Kconfig index 428da175d073..70e4f663ebd2 100644 --- a/arch/mn10300/Kconfig +++ b/arch/mn10300/Kconfig @@ -13,6 +13,7 @@ config MN10300 select MODULES_USE_ELF_RELA select OLD_SIGSUSPEND3 select OLD_SIGACTION + select HAVE_DEBUG_STACKOVERFLOW config AM33_2 def_bool n diff --git a/arch/mn10300/Kconfig.debug b/arch/mn10300/Kconfig.debug index bdbfd444a9ff..94efb3ed223f 100644 --- a/arch/mn10300/Kconfig.debug +++ b/arch/mn10300/Kconfig.debug @@ -2,10 +2,6 @@ menu "Kernel hacking" source "lib/Kconfig.debug" -config DEBUG_STACKOVERFLOW - bool "Check for stack overflows" - depends on DEBUG_KERNEL - config DEBUG_DECOMPRESS_KERNEL bool "Using serial port during decompressing kernel" depends on DEBUG_KERNEL diff --git a/arch/openrisc/Kconfig b/arch/openrisc/Kconfig index 1072bfd18c50..99dbab1c59ac 100644 --- a/arch/openrisc/Kconfig +++ b/arch/openrisc/Kconfig @@ -22,6 +22,7 @@ config OPENRISC select GENERIC_STRNCPY_FROM_USER select GENERIC_STRNLEN_USER select MODULES_USE_ELF_RELA + select HAVE_DEBUG_STACKOVERFLOW config MMU def_bool y @@ -128,16 +129,6 @@ config CMDLINE menu "Debugging options" -config DEBUG_STACKOVERFLOW - bool "Check for kernel stack overflow" - default y - help - Make extra checks for space available on stack in some - critical functions. This will cause kernel to run a bit slower, - but will catch most of kernel stack overruns and exit gracefully. - - Say Y if you are unsure. - config JUMP_UPON_UNHANDLED_EXCEPTION bool "Try to die gracefully" default y diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig index 6507dabdd5dd..ffcd557f8cfd 100644 --- a/arch/parisc/Kconfig +++ b/arch/parisc/Kconfig @@ -27,6 +27,7 @@ config PARISC select MODULES_USE_ELF_RELA select CLONE_BACKWARDS select TTY # Needed for pdc_cons.c + select HAVE_DEBUG_STACKOVERFLOW help The PA-RISC microprocessor is designed by Hewlett-Packard and used diff --git a/arch/parisc/Kconfig.debug b/arch/parisc/Kconfig.debug index 08a332f6ee87..bc989e522a04 100644 --- a/arch/parisc/Kconfig.debug +++ b/arch/parisc/Kconfig.debug @@ -13,14 +13,3 @@ config DEBUG_RODATA If in doubt, say "N". endmenu - -config DEBUG_STACKOVERFLOW - bool "Check for stack overflows" - default y - depends on DEBUG_KERNEL - ---help--- - Say Y here if you want to check the overflows of kernel, IRQ - and exception stacks. This option will cause messages of the - stacks in detail when free stack space drops below a certain - limit. - If in doubt, say "N". diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index c33e3ad2c8fd..a4f5959ed9b0 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -138,6 +138,7 @@ config PPC select ARCH_USE_BUILTIN_BSWAP select OLD_SIGSUSPEND select OLD_SIGACTION if PPC32 + select HAVE_DEBUG_STACKOVERFLOW config EARLY_PRINTK bool diff --git a/arch/powerpc/Kconfig.debug b/arch/powerpc/Kconfig.debug index 863d877e0b5f..a4914f373958 100644 --- a/arch/powerpc/Kconfig.debug +++ b/arch/powerpc/Kconfig.debug @@ -28,13 +28,6 @@ config PRINT_STACK_DEPTH too small and stack traces cause important information to scroll off the screen. -config DEBUG_STACKOVERFLOW - bool "Check for stack overflows" - depends on DEBUG_KERNEL - help - This option will cause messages to be printed if free stack space - drops below a certain limit. - config HCALL_STATS bool "Hypervisor call instrumentation" depends on PPC_PSERIES && DEBUG_FS && TRACEPOINTS diff --git a/arch/tile/Kconfig b/arch/tile/Kconfig index 3aa37669ff8c..24565a7ffe6d 100644 --- a/arch/tile/Kconfig +++ b/arch/tile/Kconfig @@ -25,6 +25,7 @@ config TILE select HAVE_ARCH_TRACEHOOK select HAVE_SYSCALL_TRACEPOINTS select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE + select HAVE_DEBUG_STACKOVERFLOW # FIXME: investigate whether we need/want these options. # select HAVE_IOREMAP_PROT diff --git a/arch/tile/Kconfig.debug b/arch/tile/Kconfig.debug index ddbfc3322d7f..9165ea979e85 100644 --- a/arch/tile/Kconfig.debug +++ b/arch/tile/Kconfig.debug @@ -14,13 +14,6 @@ config EARLY_PRINTK with klogd/syslogd. You should normally N here, unless you want to debug such a crash. -config DEBUG_STACKOVERFLOW - bool "Check for stack overflows" - depends on DEBUG_KERNEL - help - This option will cause messages to be printed if free stack space - drops below a certain limit. - config DEBUG_EXTRA_FLAGS string "Additional compiler arguments when building with '-g'" depends on DEBUG_INFO diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index fe120da25625..22f30337a18e 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -121,6 +121,7 @@ config X86 select OLD_SIGACTION if X86_32 select COMPAT_OLD_SIGACTION if IA32_EMULATION select RTC_LIB + select HAVE_DEBUG_STACKOVERFLOW config INSTRUCTION_DECODER def_bool y diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug index c198b7e13e7b..c1665a8989da 100644 --- a/arch/x86/Kconfig.debug +++ b/arch/x86/Kconfig.debug @@ -59,16 +59,6 @@ config EARLY_PRINTK_DBGP with klogd/syslogd or the X server. You should normally N here, unless you want to debug such a crash. You need usb debug device. -config DEBUG_STACKOVERFLOW - bool "Check for stack overflows" - depends on DEBUG_KERNEL - ---help--- - Say Y here if you want to check the overflows of kernel, IRQ - and exception stacks. This option will cause messages of the - stacks in detail when free stack space drops below a certain - limit. - If in doubt, say "N". - config X86_PTDUMP bool "Export kernel pagetable layout to userspace via debugfs" depends on DEBUG_KERNEL diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 566cf2bc08ea..cc443bc6edfc 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -1475,6 +1475,27 @@ config ASYNC_RAID6_TEST If unsure, say N. +config HAVE_DEBUG_STACKOVERFLOW + bool + +config DEBUG_STACKOVERFLOW + bool "Check for stack overflows" + depends on DEBUG_KERNEL && HAVE_DEBUG_STACKOVERFLOW + ---help--- + Say Y here if you want to check for overflows of kernel, IRQ + and exception stacks (if your archicture uses them). This + option will show detailed messages if free stack space drops + below a certain limit. + + These kinds of bugs usually occur when call-chains in the + kernel get too deep, especially when interrupts are + involved. + + Use this in cases where you see apparently random memory + corruption, especially if it appears in 'struct thread_info' + + If in doubt, say "N". + source "samples/Kconfig" source "lib/Kconfig.kgdb" -- cgit v1.2.3 From 0610c8a8a258e869a542e5fdb964c6816336559e Mon Sep 17 00:00:00 2001 From: Dave Hansen Date: Mon, 1 Jul 2013 13:04:43 -0700 Subject: order memory debugging Kconfig options Original posting: http://lkml.kernel.org/r/20121214184203.37E6C724@kernel.stglabs.ibm.com There are a *LOT* of memory debugging options. They are just scattered all over the "Kernel Hacking" menu. Sure, "memory debugging" is a very vague term and it's going to be hard to make absolute rules about what goes in here, but this has to be better than what we had before. This does, however, leave out the architecture-specific memory debugging options (like x86's DEBUG_SET_MODULE_RONX). There would need to be some substantial changes to move those in here. Kconfig can not easily mix arch-specific and generic options together: it really requires a file per-architecture, and I think having an arch/foo/Kconfig.debug-memory might be taking things a bit too far Signed-off-by: Dave Hansen Signed-off-by: Dave Hansen Signed-off-by: Linus Torvalds --- lib/Kconfig.debug | 685 +++++++++++++++++++++++++++--------------------------- 1 file changed, 345 insertions(+), 340 deletions(-) diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index cc443bc6edfc..57e18ac6ca3c 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -162,6 +162,283 @@ config DEBUG_KERNEL Say Y here if you are developing drivers or trying to debug and identify kernel problems. +menu "Memory Debugging" + +source mm/Kconfig.debug + +config DEBUG_OBJECTS + bool "Debug object operations" + depends on DEBUG_KERNEL + help + If you say Y here, additional code will be inserted into the + kernel to track the life time of various objects and validate + the operations on those objects. + +config DEBUG_OBJECTS_SELFTEST + bool "Debug objects selftest" + depends on DEBUG_OBJECTS + help + This enables the selftest of the object debug code. + +config DEBUG_OBJECTS_FREE + bool "Debug objects in freed memory" + depends on DEBUG_OBJECTS + help + This enables checks whether a k/v free operation frees an area + which contains an object which has not been deactivated + properly. This can make kmalloc/kfree-intensive workloads + much slower. + +config DEBUG_OBJECTS_TIMERS + bool "Debug timer objects" + depends on DEBUG_OBJECTS + help + If you say Y here, additional code will be inserted into the + timer routines to track the life time of timer objects and + validate the timer operations. + +config DEBUG_OBJECTS_WORK + bool "Debug work objects" + depends on DEBUG_OBJECTS + help + If you say Y here, additional code will be inserted into the + work queue routines to track the life time of work objects and + validate the work operations. + +config DEBUG_OBJECTS_RCU_HEAD + bool "Debug RCU callbacks objects" + depends on DEBUG_OBJECTS + help + Enable this to turn on debugging of RCU list heads (call_rcu() usage). + +config DEBUG_OBJECTS_PERCPU_COUNTER + bool "Debug percpu counter objects" + depends on DEBUG_OBJECTS + help + If you say Y here, additional code will be inserted into the + percpu counter routines to track the life time of percpu counter + objects and validate the percpu counter operations. + +config DEBUG_OBJECTS_ENABLE_DEFAULT + int "debug_objects bootup default value (0-1)" + range 0 1 + default "1" + depends on DEBUG_OBJECTS + help + Debug objects boot parameter default value + +config DEBUG_SLAB + bool "Debug slab memory allocations" + depends on DEBUG_KERNEL && SLAB && !KMEMCHECK + help + Say Y here to have the kernel do limited verification on memory + allocation as well as poisoning memory on free to catch use of freed + memory. This can make kmalloc/kfree-intensive workloads much slower. + +config DEBUG_SLAB_LEAK + bool "Memory leak debugging" + depends on DEBUG_SLAB + +config SLUB_DEBUG_ON + bool "SLUB debugging on by default" + depends on SLUB && SLUB_DEBUG && !KMEMCHECK + default n + help + Boot with debugging on by default. SLUB boots by default with + the runtime debug capabilities switched off. Enabling this is + equivalent to specifying the "slub_debug" parameter on boot. + There is no support for more fine grained debug control like + possible with slub_debug=xxx. SLUB debugging may be switched + off in a kernel built with CONFIG_SLUB_DEBUG_ON by specifying + "slub_debug=-". + +config SLUB_STATS + default n + bool "Enable SLUB performance statistics" + depends on SLUB && SYSFS + help + SLUB statistics are useful to debug SLUBs allocation behavior in + order find ways to optimize the allocator. This should never be + enabled for production use since keeping statistics slows down + the allocator by a few percentage points. The slabinfo command + supports the determination of the most active slabs to figure + out which slabs are relevant to a particular load. + Try running: slabinfo -DA + +config HAVE_DEBUG_KMEMLEAK + bool + +config DEBUG_KMEMLEAK + bool "Kernel memory leak detector" + depends on DEBUG_KERNEL && HAVE_DEBUG_KMEMLEAK + select DEBUG_FS + select STACKTRACE if STACKTRACE_SUPPORT + select KALLSYMS + select CRC32 + help + Say Y here if you want to enable the memory leak + detector. The memory allocation/freeing is traced in a way + similar to the Boehm's conservative garbage collector, the + difference being that the orphan objects are not freed but + only shown in /sys/kernel/debug/kmemleak. Enabling this + feature will introduce an overhead to memory + allocations. See Documentation/kmemleak.txt for more + details. + + Enabling DEBUG_SLAB or SLUB_DEBUG may increase the chances + of finding leaks due to the slab objects poisoning. + + In order to access the kmemleak file, debugfs needs to be + mounted (usually at /sys/kernel/debug). + +config DEBUG_KMEMLEAK_EARLY_LOG_SIZE + int "Maximum kmemleak early log entries" + depends on DEBUG_KMEMLEAK + range 200 40000 + default 400 + help + Kmemleak must track all the memory allocations to avoid + reporting false positives. Since memory may be allocated or + freed before kmemleak is initialised, an early log buffer is + used to store these actions. If kmemleak reports "early log + buffer exceeded", please increase this value. + +config DEBUG_KMEMLEAK_TEST + tristate "Simple test for the kernel memory leak detector" + depends on DEBUG_KMEMLEAK && m + help + This option enables a module that explicitly leaks memory. + + If unsure, say N. + +config DEBUG_KMEMLEAK_DEFAULT_OFF + bool "Default kmemleak to off" + depends on DEBUG_KMEMLEAK + help + Say Y here to disable kmemleak by default. It can then be enabled + on the command line via kmemleak=on. + +config DEBUG_STACK_USAGE + bool "Stack utilization instrumentation" + depends on DEBUG_KERNEL && !IA64 && !PARISC && !METAG + help + Enables the display of the minimum amount of free stack which each + task has ever had available in the sysrq-T and sysrq-P debug output. + + This option will slow down process creation somewhat. + +config DEBUG_VM + bool "Debug VM" + depends on DEBUG_KERNEL + help + Enable this to turn on extended checks in the virtual-memory system + that may impact performance. + + If unsure, say N. + +config DEBUG_VM_RB + bool "Debug VM red-black trees" + depends on DEBUG_VM + help + Enable this to turn on more extended checks in the virtual-memory + system that may impact performance. + + If unsure, say N. + +config DEBUG_VIRTUAL + bool "Debug VM translations" + depends on DEBUG_KERNEL && X86 + help + Enable some costly sanity checks in virtual to page code. This can + catch mistakes with virt_to_page() and friends. + + If unsure, say N. + +config DEBUG_NOMMU_REGIONS + bool "Debug the global anon/private NOMMU mapping region tree" + depends on DEBUG_KERNEL && !MMU + help + This option causes the global tree of anonymous and private mapping + regions to be regularly checked for invalid topology. + +config DEBUG_MEMORY_INIT + bool "Debug memory initialisation" if EXPERT + default !EXPERT + help + Enable this for additional checks during memory initialisation. + The sanity checks verify aspects of the VM such as the memory model + and other information provided by the architecture. Verbose + information will be printed at KERN_DEBUG loglevel depending + on the mminit_loglevel= command-line option. + + If unsure, say Y + +config MEMORY_NOTIFIER_ERROR_INJECT + tristate "Memory hotplug notifier error injection module" + depends on MEMORY_HOTPLUG_SPARSE && NOTIFIER_ERROR_INJECTION + help + This option provides the ability to inject artificial errors to + memory hotplug notifier chain callbacks. It is controlled through + debugfs interface under /sys/kernel/debug/notifier-error-inject/memory + + If the notifier call chain should be failed with some events + notified, write the error code to "actions//error". + + Example: Inject memory hotplug offline error (-12 == -ENOMEM) + + # cd /sys/kernel/debug/notifier-error-inject/memory + # echo -12 > actions/MEM_GOING_OFFLINE/error + # echo offline > /sys/devices/system/memory/memoryXXX/state + bash: echo: write error: Cannot allocate memory + + To compile this code as a module, choose M here: the module will + be called memory-notifier-error-inject. + + If unsure, say N. + +config DEBUG_PER_CPU_MAPS + bool "Debug access to per_cpu maps" + depends on DEBUG_KERNEL + depends on SMP + help + Say Y to verify that the per_cpu map being accessed has + been set up. This adds a fair amount of code to kernel memory + and decreases performance. + + Say N if unsure. + +config DEBUG_HIGHMEM + bool "Highmem debugging" + depends on DEBUG_KERNEL && HIGHMEM + help + This options enables addition error checking for high memory systems. + Disable for production systems. + +config HAVE_DEBUG_STACKOVERFLOW + bool + +config DEBUG_STACKOVERFLOW + bool "Check for stack overflows" + depends on DEBUG_KERNEL && HAVE_DEBUG_STACKOVERFLOW + ---help--- + Say Y here if you want to check for overflows of kernel, IRQ + and exception stacks (if your archicture uses them). This + option will show detailed messages if free stack space drops + below a certain limit. + + These kinds of bugs usually occur when call-chains in the + kernel get too deep, especially when interrupts are + involved. + + Use this in cases where you see apparently random memory + corruption, especially if it appears in 'struct thread_info' + + If in doubt, say "N". + +source "lib/Kconfig.kmemcheck" + +endmenu # "Memory Debugging" + config DEBUG_SHIRQ bool "Debug shared IRQ handlers" depends on DEBUG_KERNEL && GENERIC_HARDIRQS @@ -266,241 +543,89 @@ config DETECT_HUNG_TASK depends on DEBUG_KERNEL default LOCKUP_DETECTOR help - Say Y here to enable the kernel to detect "hung tasks", - which are bugs that cause the task to be stuck in - uninterruptible "D" state indefinitiley. - - When a hung task is detected, the kernel will print the - current stack trace (which you should report), but the - task will stay in uninterruptible state. If lockdep is - enabled then all held locks will also be reported. This - feature has negligible overhead. - -config DEFAULT_HUNG_TASK_TIMEOUT - int "Default timeout for hung task detection (in seconds)" - depends on DETECT_HUNG_TASK - default 120 - help - This option controls the default timeout (in seconds) used - to determine when a task has become non-responsive and should - be considered hung. - - It can be adjusted at runtime via the kernel.hung_task_timeout_secs - sysctl or by writing a value to - /proc/sys/kernel/hung_task_timeout_secs. - - A timeout of 0 disables the check. The default is two minutes. - Keeping the default should be fine in most cases. - -config BOOTPARAM_HUNG_TASK_PANIC - bool "Panic (Reboot) On Hung Tasks" - depends on DETECT_HUNG_TASK - help - Say Y here to enable the kernel to panic on "hung tasks", - which are bugs that cause the kernel to leave a task stuck - in uninterruptible "D" state. - - The panic can be used in combination with panic_timeout, - to cause the system to reboot automatically after a - hung task has been detected. This feature is useful for - high-availability systems that have uptime guarantees and - where a hung tasks must be resolved ASAP. - - Say N if unsure. - -config BOOTPARAM_HUNG_TASK_PANIC_VALUE - int - depends on DETECT_HUNG_TASK - range 0 1 - default 0 if !BOOTPARAM_HUNG_TASK_PANIC - default 1 if BOOTPARAM_HUNG_TASK_PANIC - -config SCHED_DEBUG - bool "Collect scheduler debugging info" - depends on DEBUG_KERNEL && PROC_FS - default y - help - If you say Y here, the /proc/sched_debug file will be provided - that can help debug the scheduler. The runtime overhead of this - option is minimal. - -config SCHEDSTATS - bool "Collect scheduler statistics" - depends on DEBUG_KERNEL && PROC_FS - help - If you say Y here, additional code will be inserted into the - scheduler and related routines to collect statistics about - scheduler behavior and provide them in /proc/schedstat. These - stats may be useful for both tuning and debugging the scheduler - If you aren't debugging the scheduler or trying to tune a specific - application, you can say N to avoid the very slight overhead - this adds. - -config TIMER_STATS - bool "Collect kernel timers statistics" - depends on DEBUG_KERNEL && PROC_FS - help - If you say Y here, additional code will be inserted into the - timer routines to collect statistics about kernel timers being - reprogrammed. The statistics can be read from /proc/timer_stats. - The statistics collection is started by writing 1 to /proc/timer_stats, - writing 0 stops it. This feature is useful to collect information - about timer usage patterns in kernel and userspace. This feature - is lightweight if enabled in the kernel config but not activated - (it defaults to deactivated on bootup and will only be activated - if some application like powertop activates it explicitly). - -config DEBUG_OBJECTS - bool "Debug object operations" - depends on DEBUG_KERNEL - help - If you say Y here, additional code will be inserted into the - kernel to track the life time of various objects and validate - the operations on those objects. - -config DEBUG_OBJECTS_SELFTEST - bool "Debug objects selftest" - depends on DEBUG_OBJECTS - help - This enables the selftest of the object debug code. - -config DEBUG_OBJECTS_FREE - bool "Debug objects in freed memory" - depends on DEBUG_OBJECTS - help - This enables checks whether a k/v free operation frees an area - which contains an object which has not been deactivated - properly. This can make kmalloc/kfree-intensive workloads - much slower. - -config DEBUG_OBJECTS_TIMERS - bool "Debug timer objects" - depends on DEBUG_OBJECTS - help - If you say Y here, additional code will be inserted into the - timer routines to track the life time of timer objects and - validate the timer operations. - -config DEBUG_OBJECTS_WORK - bool "Debug work objects" - depends on DEBUG_OBJECTS - help - If you say Y here, additional code will be inserted into the - work queue routines to track the life time of work objects and - validate the work operations. - -config DEBUG_OBJECTS_RCU_HEAD - bool "Debug RCU callbacks objects" - depends on DEBUG_OBJECTS - help - Enable this to turn on debugging of RCU list heads (call_rcu() usage). - -config DEBUG_OBJECTS_PERCPU_COUNTER - bool "Debug percpu counter objects" - depends on DEBUG_OBJECTS - help - If you say Y here, additional code will be inserted into the - percpu counter routines to track the life time of percpu counter - objects and validate the percpu counter operations. - -config DEBUG_OBJECTS_ENABLE_DEFAULT - int "debug_objects bootup default value (0-1)" - range 0 1 - default "1" - depends on DEBUG_OBJECTS - help - Debug objects boot parameter default value - -config DEBUG_SLAB - bool "Debug slab memory allocations" - depends on DEBUG_KERNEL && SLAB && !KMEMCHECK - help - Say Y here to have the kernel do limited verification on memory - allocation as well as poisoning memory on free to catch use of freed - memory. This can make kmalloc/kfree-intensive workloads much slower. + Say Y here to enable the kernel to detect "hung tasks", + which are bugs that cause the task to be stuck in + uninterruptible "D" state indefinitiley. -config DEBUG_SLAB_LEAK - bool "Memory leak debugging" - depends on DEBUG_SLAB + When a hung task is detected, the kernel will print the + current stack trace (which you should report), but the + task will stay in uninterruptible state. If lockdep is + enabled then all held locks will also be reported. This + feature has negligible overhead. -config SLUB_DEBUG_ON - bool "SLUB debugging on by default" - depends on SLUB && SLUB_DEBUG && !KMEMCHECK - default n +config DEFAULT_HUNG_TASK_TIMEOUT + int "Default timeout for hung task detection (in seconds)" + depends on DETECT_HUNG_TASK + default 120 help - Boot with debugging on by default. SLUB boots by default with - the runtime debug capabilities switched off. Enabling this is - equivalent to specifying the "slub_debug" parameter on boot. - There is no support for more fine grained debug control like - possible with slub_debug=xxx. SLUB debugging may be switched - off in a kernel built with CONFIG_SLUB_DEBUG_ON by specifying - "slub_debug=-". + This option controls the default timeout (in seconds) used + to determine when a task has become non-responsive and should + be considered hung. -config SLUB_STATS - default n - bool "Enable SLUB performance statistics" - depends on SLUB && SYSFS - help - SLUB statistics are useful to debug SLUBs allocation behavior in - order find ways to optimize the allocator. This should never be - enabled for production use since keeping statistics slows down - the allocator by a few percentage points. The slabinfo command - supports the determination of the most active slabs to figure - out which slabs are relevant to a particular load. - Try running: slabinfo -DA + It can be adjusted at runtime via the kernel.hung_task_timeout_secs + sysctl or by writing a value to + /proc/sys/kernel/hung_task_timeout_secs. -config HAVE_DEBUG_KMEMLEAK - bool + A timeout of 0 disables the check. The default is two minutes. + Keeping the default should be fine in most cases. -config DEBUG_KMEMLEAK - bool "Kernel memory leak detector" - depends on DEBUG_KERNEL && HAVE_DEBUG_KMEMLEAK - select DEBUG_FS - select STACKTRACE if STACKTRACE_SUPPORT - select KALLSYMS - select CRC32 +config BOOTPARAM_HUNG_TASK_PANIC + bool "Panic (Reboot) On Hung Tasks" + depends on DETECT_HUNG_TASK help - Say Y here if you want to enable the memory leak - detector. The memory allocation/freeing is traced in a way - similar to the Boehm's conservative garbage collector, the - difference being that the orphan objects are not freed but - only shown in /sys/kernel/debug/kmemleak. Enabling this - feature will introduce an overhead to memory - allocations. See Documentation/kmemleak.txt for more - details. + Say Y here to enable the kernel to panic on "hung tasks", + which are bugs that cause the kernel to leave a task stuck + in uninterruptible "D" state. - Enabling DEBUG_SLAB or SLUB_DEBUG may increase the chances - of finding leaks due to the slab objects poisoning. + The panic can be used in combination with panic_timeout, + to cause the system to reboot automatically after a + hung task has been detected. This feature is useful for + high-availability systems that have uptime guarantees and + where a hung tasks must be resolved ASAP. - In order to access the kmemleak file, debugfs needs to be - mounted (usually at /sys/kernel/debug). + Say N if unsure. -config DEBUG_KMEMLEAK_EARLY_LOG_SIZE - int "Maximum kmemleak early log entries" - depends on DEBUG_KMEMLEAK - range 200 40000 - default 400 - help - Kmemleak must track all the memory allocations to avoid - reporting false positives. Since memory may be allocated or - freed before kmemleak is initialised, an early log buffer is - used to store these actions. If kmemleak reports "early log - buffer exceeded", please increase this value. +config BOOTPARAM_HUNG_TASK_PANIC_VALUE + int + depends on DETECT_HUNG_TASK + range 0 1 + default 0 if !BOOTPARAM_HUNG_TASK_PANIC + default 1 if BOOTPARAM_HUNG_TASK_PANIC -config DEBUG_KMEMLEAK_TEST - tristate "Simple test for the kernel memory leak detector" - depends on DEBUG_KMEMLEAK && m +config SCHED_DEBUG + bool "Collect scheduler debugging info" + depends on DEBUG_KERNEL && PROC_FS + default y help - This option enables a module that explicitly leaks memory. + If you say Y here, the /proc/sched_debug file will be provided + that can help debug the scheduler. The runtime overhead of this + option is minimal. - If unsure, say N. +config SCHEDSTATS + bool "Collect scheduler statistics" + depends on DEBUG_KERNEL && PROC_FS + help + If you say Y here, additional code will be inserted into the + scheduler and related routines to collect statistics about + scheduler behavior and provide them in /proc/schedstat. These + stats may be useful for both tuning and debugging the scheduler + If you aren't debugging the scheduler or trying to tune a specific + application, you can say N to avoid the very slight overhead + this adds. -config DEBUG_KMEMLEAK_DEFAULT_OFF - bool "Default kmemleak to off" - depends on DEBUG_KMEMLEAK +config TIMER_STATS + bool "Collect kernel timers statistics" + depends on DEBUG_KERNEL && PROC_FS help - Say Y here to disable kmemleak by default. It can then be enabled - on the command line via kmemleak=on. + If you say Y here, additional code will be inserted into the + timer routines to collect statistics about kernel timers being + reprogrammed. The statistics can be read from /proc/timer_stats. + The statistics collection is started by writing 1 to /proc/timer_stats, + writing 0 stops it. This feature is useful to collect information + about timer usage patterns in kernel and userspace. This feature + is lightweight if enabled in the kernel config but not activated + (it defaults to deactivated on bootup and will only be activated + if some application like powertop activates it explicitly). config DEBUG_PREEMPT bool "Debug preemptible kernel" @@ -672,15 +797,6 @@ config STACKTRACE bool depends on STACKTRACE_SUPPORT -config DEBUG_STACK_USAGE - bool "Stack utilization instrumentation" - depends on DEBUG_KERNEL && !IA64 && !PARISC && !METAG - help - Enables the display of the minimum amount of free stack which each - task has ever had available in the sysrq-T and sysrq-P debug output. - - This option will slow down process creation somewhat. - config DEBUG_KOBJECT bool "kobject debugging" depends on DEBUG_KERNEL @@ -688,13 +804,6 @@ config DEBUG_KOBJECT If you say Y here, some extra kobject debugging messages will be sent to the syslog. -config DEBUG_HIGHMEM - bool "Highmem debugging" - depends on DEBUG_KERNEL && HIGHMEM - help - This options enables addition error checking for high memory systems. - Disable for production systems. - config HAVE_DEBUG_BUGVERBOSE bool @@ -733,40 +842,6 @@ config DEBUG_INFO_REDUCED DEBUG_INFO build and compile times are reduced too. Only works with newer gcc versions. -config DEBUG_VM - bool "Debug VM" - depends on DEBUG_KERNEL - help - Enable this to turn on extended checks in the virtual-memory system - that may impact performance. - - If unsure, say N. - -config DEBUG_VM_RB - bool "Debug VM red-black trees" - depends on DEBUG_VM - help - Enable this to turn on more extended checks in the virtual-memory - system that may impact performance. - - If unsure, say N. - -config DEBUG_VIRTUAL - bool "Debug VM translations" - depends on DEBUG_KERNEL && X86 - help - Enable some costly sanity checks in virtual to page code. This can - catch mistakes with virt_to_page() and friends. - - If unsure, say N. - -config DEBUG_NOMMU_REGIONS - bool "Debug the global anon/private NOMMU mapping region tree" - depends on DEBUG_KERNEL && !MMU - help - This option causes the global tree of anonymous and private mapping - regions to be regularly checked for invalid topology. - config DEBUG_WRITECOUNT bool "Debug filesystem writers count" depends on DEBUG_KERNEL @@ -777,18 +852,6 @@ config DEBUG_WRITECOUNT If unsure, say N. -config DEBUG_MEMORY_INIT - bool "Debug memory initialisation" if EXPERT - default !EXPERT - help - Enable this for additional checks during memory initialisation. - The sanity checks verify aspects of the VM such as the memory model - and other information provided by the architecture. Verbose - information will be printed at KERN_DEBUG loglevel depending - on the mminit_loglevel= command-line option. - - If unsure, say Y - config DEBUG_LIST bool "Debug linked list manipulation" depends on DEBUG_KERNEL @@ -1088,17 +1151,6 @@ config DEBUG_FORCE_WEAK_PER_CPU To ensure that generic code follows the above rules, this option forces all percpu variables to be defined as weak. -config DEBUG_PER_CPU_MAPS - bool "Debug access to per_cpu maps" - depends on DEBUG_KERNEL - depends on SMP - help - Say Y to verify that the per_cpu map being accessed has - been set up. This adds a fair amount of code to kernel memory - and decreases performance. - - Say N if unsure. - config LKDTM tristate "Linux Kernel Dump Test Tool Module" depends on DEBUG_FS @@ -1173,29 +1225,6 @@ config PM_NOTIFIER_ERROR_INJECT If unsure, say N. -config MEMORY_NOTIFIER_ERROR_INJECT - tristate "Memory hotplug notifier error injection module" - depends on MEMORY_HOTPLUG_SPARSE && NOTIFIER_ERROR_INJECTION - help - This option provides the ability to inject artificial errors to - memory hotplug notifier chain callbacks. It is controlled through - debugfs interface under /sys/kernel/debug/notifier-error-inject/memory - - If the notifier call chain should be failed with some events - notified, write the error code to "actions//error". - - Example: Inject memory hotplug offline error (-12 == -ENOMEM) - - # cd /sys/kernel/debug/notifier-error-inject/memory - # echo -12 > actions/MEM_GOING_OFFLINE/error - # echo offline > /sys/devices/system/memory/memoryXXX/state - bash: echo: write error: Cannot allocate memory - - To compile this code as a module, choose M here: the module will - be called memory-notifier-error-inject. - - If unsure, say N. - config OF_RECONFIG_NOTIFIER_ERROR_INJECT tristate "OF reconfig notifier error injection module" depends on OF_DYNAMIC && NOTIFIER_ERROR_INJECTION @@ -1310,7 +1339,6 @@ config DEBUG_STRICT_USER_COPY_CHECKS If unsure, say N. -source mm/Kconfig.debug source kernel/trace/Kconfig config RBTREE_TEST @@ -1475,33 +1503,10 @@ config ASYNC_RAID6_TEST If unsure, say N. -config HAVE_DEBUG_STACKOVERFLOW - bool - -config DEBUG_STACKOVERFLOW - bool "Check for stack overflows" - depends on DEBUG_KERNEL && HAVE_DEBUG_STACKOVERFLOW - ---help--- - Say Y here if you want to check for overflows of kernel, IRQ - and exception stacks (if your archicture uses them). This - option will show detailed messages if free stack space drops - below a certain limit. - - These kinds of bugs usually occur when call-chains in the - kernel get too deep, especially when interrupts are - involved. - - Use this in cases where you see apparently random memory - corruption, especially if it appears in 'struct thread_info' - - If in doubt, say "N". - source "samples/Kconfig" source "lib/Kconfig.kgdb" -source "lib/Kconfig.kmemcheck" - config TEST_STRING_HELPERS tristate "Test functions located in the string_helpers module at runtime" -- cgit v1.2.3 From 881c5149543a3a391ab6ce2ebe70235924f15d9c Mon Sep 17 00:00:00 2001 From: Dave Hansen Date: Mon, 1 Jul 2013 13:04:44 -0700 Subject: consolidate runtime testing configs Original posting: http://lkml.kernel.org/r/20121214184206.FC11422F@kernel.stglabs.ibm.com These runtime tests are great, except that there are a lot of them, and they are very rarely needed. Give them their own menu so that only the folks who need them will have to go looking for them. Note that there are some other runtime tests that are not in here, like for RCU or locking. This menu should only be used for tests that do not have a more appropriate home. Signed-off-by: Dave Hansen Signed-off-by: Dave Hansen Signed-off-by: Linus Torvalds --- lib/Kconfig.debug | 157 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 81 insertions(+), 76 deletions(-) diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 57e18ac6ca3c..6fdead79ee15 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -861,15 +861,6 @@ config DEBUG_LIST If unsure, say N. -config TEST_LIST_SORT - bool "Linked list sorting test" - depends on DEBUG_KERNEL - help - Enable this to turn on 'list_sort()' function test. This test is - executed only once during system boot, so affects only boot time. - - If unsure, say N. - config DEBUG_SG bool "Debug SG table operations" depends on DEBUG_KERNEL @@ -1082,33 +1073,6 @@ config RCU_TRACE endmenu # "RCU Debugging" -config KPROBES_SANITY_TEST - bool "Kprobes sanity tests" - depends on DEBUG_KERNEL - depends on KPROBES - default n - help - This option provides for testing basic kprobes functionality on - boot. A sample kprobe, jprobe and kretprobe are inserted and - verified for functionality. - - Say N if you are unsure. - -config BACKTRACE_SELF_TEST - tristate "Self test for the backtrace code" - depends on DEBUG_KERNEL - default n - help - This option provides a kernel module that can be used to test - the kernel stack backtrace code. This option is not useful - for distributions or general kernels, but only for kernel - developers working on architecture code. - - Note that if you want to also test saved backtraces, you will - have to enable STACKTRACE as well. - - Say N if you are unsure. - config DEBUG_BLOCK_EXT_DEVT bool "Force extended block device numbers and spread them" depends on DEBUG_KERNEL @@ -1151,21 +1115,6 @@ config DEBUG_FORCE_WEAK_PER_CPU To ensure that generic code follows the above rules, this option forces all percpu variables to be defined as weak. -config LKDTM - tristate "Linux Kernel Dump Test Tool Module" - depends on DEBUG_FS - depends on BLOCK - default n - help - This module enables testing of the different dumping mechanisms by - inducing system failures at predefined crash points. - If you don't need it: say N - Choose M here to compile this code as a module. The module will be - called lkdtm. - - Documentation on how to use the module can be found in - Documentation/fault-injection/provoke-crashes.txt - config NOTIFIER_ERROR_INJECTION tristate "Notifier error injection" depends on DEBUG_KERNEL @@ -1341,6 +1290,59 @@ config DEBUG_STRICT_USER_COPY_CHECKS source kernel/trace/Kconfig +menu "Runtime Testing" + +config LKDTM + tristate "Linux Kernel Dump Test Tool Module" + depends on DEBUG_FS + depends on BLOCK + default n + help + This module enables testing of the different dumping mechanisms by + inducing system failures at predefined crash points. + If you don't need it: say N + Choose M here to compile this code as a module. The module will be + called lkdtm. + + Documentation on how to use the module can be found in + Documentation/fault-injection/provoke-crashes.txt + +config TEST_LIST_SORT + bool "Linked list sorting test" + depends on DEBUG_KERNEL + help + Enable this to turn on 'list_sort()' function test. This test is + executed only once during system boot, so affects only boot time. + + If unsure, say N. + +config KPROBES_SANITY_TEST + bool "Kprobes sanity tests" + depends on DEBUG_KERNEL + depends on KPROBES + default n + help + This option provides for testing basic kprobes functionality on + boot. A sample kprobe, jprobe and kretprobe are inserted and + verified for functionality. + + Say N if you are unsure. + +config BACKTRACE_SELF_TEST + tristate "Self test for the backtrace code" + depends on DEBUG_KERNEL + default n + help + This option provides a kernel module that can be used to test + the kernel stack backtrace code. This option is not useful + for distributions or general kernels, but only for kernel + developers working on architecture code. + + Note that if you want to also test saved backtraces, you will + have to enable STACKTRACE as well. + + Say N if you are unsure. + config RBTREE_TEST tristate "Red-Black tree test" depends on m && DEBUG_KERNEL @@ -1354,6 +1356,34 @@ config INTERVAL_TREE_TEST help A benchmark measuring the performance of the interval tree library +config ATOMIC64_SELFTEST + bool "Perform an atomic64_t self-test at boot" + help + Enable this option to test the atomic64_t functions at boot. + + If unsure, say N. + +config ASYNC_RAID6_TEST + tristate "Self test for hardware accelerated raid6 recovery" + depends on ASYNC_RAID6_RECOV + select ASYNC_MEMCPY + ---help--- + This is a one-shot self test that permutes through the + recovery of all the possible two disk failure scenarios for a + N-disk array. Recovery is performed with the asynchronous + raid6 recovery routines, and will optionally use an offload + engine if one is available. + + If unsure, say N. + +config TEST_STRING_HELPERS + tristate "Test functions located in the string_helpers module at runtime" + +config TEST_KSTRTOX + tristate "Test kstrto*() family of functions at runtime" + +endmenu # runtime tests + config PROVIDE_OHCI1394_DMA_INIT bool "Remote debugging over FireWire early on boot" depends on PCI && X86 @@ -1483,32 +1513,7 @@ config DMA_API_DEBUG This option causes a performance degredation. Use only if you want to debug device drivers. If unsure, say N. -config ATOMIC64_SELFTEST - bool "Perform an atomic64_t self-test at boot" - help - Enable this option to test the atomic64_t functions at boot. - - If unsure, say N. - -config ASYNC_RAID6_TEST - tristate "Self test for hardware accelerated raid6 recovery" - depends on ASYNC_RAID6_RECOV - select ASYNC_MEMCPY - ---help--- - This is a one-shot self test that permutes through the - recovery of all the possible two disk failure scenarios for a - N-disk array. Recovery is performed with the asynchronous - raid6 recovery routines, and will optionally use an offload - engine if one is available. - - If unsure, say N. - source "samples/Kconfig" source "lib/Kconfig.kgdb" -config TEST_STRING_HELPERS - tristate "Test functions located in the string_helpers module at runtime" - -config TEST_KSTRTOX - tristate "Test kstrto*() family of functions at runtime" -- cgit v1.2.3 From 6dfc06651b3d29aa07ed99b8075e5d324f7a953a Mon Sep 17 00:00:00 2001 From: Dave Hansen Date: Mon, 1 Jul 2013 13:04:46 -0700 Subject: consolidate compilation option configs Original Post: http://lkml.kernel.org/r/20121214184207.6E00DDEC@kernel.stglabs.ibm.com Again, trying to come up with some common themes of the stuff in the kernel hacking menu... There are quite a few options to tweak compilation in some way, or perform extra compile-time checks. Give them their own menu. The diff here looks a bit funny... makes it look like I'm moving debugfs even though I'm actually moving the options on either side of it. Signed-off-by: Dave Hansen Signed-off-by: Dave Hansen Signed-off-by: Linus Torvalds --- lib/Kconfig.debug | 156 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 80 insertions(+), 76 deletions(-) diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 6fdead79ee15..70dfda31c8aa 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -25,6 +25,34 @@ config DEFAULT_MESSAGE_LOGLEVEL that are auditing their logs closely may want to set it to a lower priority. +menu "Compile-time checks and compiler options" + +config DEBUG_INFO + bool "Compile the kernel with debug info" + depends on DEBUG_KERNEL + help + If you say Y here the resulting kernel image will include + debugging info resulting in a larger kernel image. + This adds debug symbols to the kernel and modules (gcc -g), and + is needed if you intend to use kernel crashdump or binary object + tools like crash, kgdb, LKCD, gdb, etc on the kernel. + Say Y here only if you plan to debug the kernel. + + If unsure, say N. + +config DEBUG_INFO_REDUCED + bool "Reduce debugging information" + depends on DEBUG_INFO + help + If you say Y here gcc is instructed to generate less debugging + information for structure types. This means that tools that + need full debugging information (like kgdb or systemtap) won't + be happy. But if you merely need debugging information to + resolve line numbers there is no loss. Advantage is that + build directory object sizes shrink dramatically over a full + DEBUG_INFO build and compile times are reduced too. + Only works with newer gcc versions. + config ENABLE_WARN_DEPRECATED bool "Enable __deprecated logic" default y @@ -52,20 +80,6 @@ config FRAME_WARN Setting it to 0 disables the warning. Requires gcc 4.4 -config MAGIC_SYSRQ - bool "Magic SysRq key" - depends on !UML - help - If you say Y here, you will have some control over the system even - if the system crashes for example during kernel debugging (e.g., you - will be able to flush the buffer cache to disk, reboot the system - immediately or dump some status information). This is accomplished - by pressing various keys while holding SysRq (Alt+PrintScreen). It - also works on a serial console (on PC hardware at least), if you - send a BREAK and then within 5 seconds a command keypress. The - keys are documented in . Don't say Y - unless you really know what this hack does. - config STRIP_ASM_SYMS bool "Strip assembler-generated symbols during link" default n @@ -156,6 +170,58 @@ config DEBUG_SECTION_MISMATCH - Enable verbose reporting from modpost in order to help resolve the section mismatches that are reported. +# +# Select this config option from the architecture Kconfig, if it +# is preferred to always offer frame pointers as a config +# option on the architecture (regardless of KERNEL_DEBUG): +# +config ARCH_WANT_FRAME_POINTERS + bool + help + +config FRAME_POINTER + bool "Compile the kernel with frame pointers" + depends on DEBUG_KERNEL && \ + (CRIS || M68K || FRV || UML || \ + AVR32 || SUPERH || BLACKFIN || MN10300 || METAG) || \ + ARCH_WANT_FRAME_POINTERS + default y if (DEBUG_INFO && UML) || ARCH_WANT_FRAME_POINTERS + help + If you say Y here the resulting kernel image will be slightly + larger and slower, but it gives very useful debugging information + in case of kernel bugs. (precise oopses/stacktraces/warnings) + +config DEBUG_FORCE_WEAK_PER_CPU + bool "Force weak per-cpu definitions" + depends on DEBUG_KERNEL + help + s390 and alpha require percpu variables in modules to be + defined weak to work around addressing range issue which + puts the following two restrictions on percpu variable + definitions. + + 1. percpu symbols must be unique whether static or not + 2. percpu variables can't be defined inside a function + + To ensure that generic code follows the above rules, this + option forces all percpu variables to be defined as weak. + +endmenu # "Compiler options" + +config MAGIC_SYSRQ + bool "Magic SysRq key" + depends on !UML + help + If you say Y here, you will have some control over the system even + if the system crashes for example during kernel debugging (e.g., you + will be able to flush the buffer cache to disk, reboot the system + immediately or dump some status information). This is accomplished + by pressing various keys while holding SysRq (Alt+PrintScreen). It + also works on a serial console (on PC hardware at least), if you + send a BREAK and then within 5 seconds a command keypress. The + keys are documented in . Don't say Y + unless you really know what this hack does. + config DEBUG_KERNEL bool "Kernel debugging" help @@ -816,32 +882,6 @@ config DEBUG_BUGVERBOSE of the BUG call as well as the EIP and oops trace. This aids debugging but costs about 70-100K of memory. -config DEBUG_INFO - bool "Compile the kernel with debug info" - depends on DEBUG_KERNEL - help - If you say Y here the resulting kernel image will include - debugging info resulting in a larger kernel image. - This adds debug symbols to the kernel and modules (gcc -g), and - is needed if you intend to use kernel crashdump or binary object - tools like crash, kgdb, LKCD, gdb, etc on the kernel. - Say Y here only if you plan to debug the kernel. - - If unsure, say N. - -config DEBUG_INFO_REDUCED - bool "Reduce debugging information" - depends on DEBUG_INFO - help - If you say Y here gcc is instructed to generate less debugging - information for structure types. This means that tools that - need full debugging information (like kgdb or systemtap) won't - be happy. But if you merely need debugging information to - resolve line numbers there is no loss. Advantage is that - build directory object sizes shrink dramatically over a full - DEBUG_INFO build and compile times are reduced too. - Only works with newer gcc versions. - config DEBUG_WRITECOUNT bool "Debug filesystem writers count" depends on DEBUG_KERNEL @@ -896,27 +936,6 @@ config DEBUG_CREDENTIALS If unsure, say N. -# -# Select this config option from the architecture Kconfig, if it -# is preferred to always offer frame pointers as a config -# option on the architecture (regardless of KERNEL_DEBUG): -# -config ARCH_WANT_FRAME_POINTERS - bool - help - -config FRAME_POINTER - bool "Compile the kernel with frame pointers" - depends on DEBUG_KERNEL && \ - (CRIS || M68K || FRV || UML || \ - AVR32 || SUPERH || BLACKFIN || MN10300 || METAG) || \ - ARCH_WANT_FRAME_POINTERS - default y if (DEBUG_INFO && UML) || ARCH_WANT_FRAME_POINTERS - help - If you say Y here the resulting kernel image will be slightly - larger and slower, but it gives very useful debugging information - in case of kernel bugs. (precise oopses/stacktraces/warnings) - config BOOT_PRINTK_DELAY bool "Delay each boot printk message by N milliseconds" depends on DEBUG_KERNEL && PRINTK && GENERIC_CALIBRATE_DELAY @@ -1100,21 +1119,6 @@ config DEBUG_BLOCK_EXT_DEVT Say N if you are unsure. -config DEBUG_FORCE_WEAK_PER_CPU - bool "Force weak per-cpu definitions" - depends on DEBUG_KERNEL - help - s390 and alpha require percpu variables in modules to be - defined weak to work around addressing range issue which - puts the following two restrictions on percpu variable - definitions. - - 1. percpu symbols must be unique whether static or not - 2. percpu variables can't be defined inside a function - - To ensure that generic code follows the above rules, this - option forces all percpu variables to be defined as weak. - config NOTIFIER_ERROR_INJECTION tristate "Notifier error injection" depends on DEBUG_KERNEL -- cgit v1.2.3 From 9eade16b4151ec35a9e03bbc7cc890e640a536c1 Mon Sep 17 00:00:00 2001 From: Dave Hansen Date: Mon, 1 Jul 2013 13:04:47 -0700 Subject: group locking debugging options Original posting: http://lkml.kernel.org/r/20121214184208.D9E5804D@kernel.stglabs.ibm.com There are quite a few of these, and we want to make sure that there is one-stop-shopping for lock debugging. Signed-off-by: Dave Hansen Signed-off-by: Dave Hansen Signed-off-by: Linus Torvalds --- lib/Kconfig.debug | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 70dfda31c8aa..c0d41dfadc2d 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -703,6 +703,8 @@ config DEBUG_PREEMPT if kernel code uses it in a preemption-unsafe way. Also, the kernel will detect preemption count underflows. +menu "Lock Debugging (spinlocks, mutexes, etc...)" + config DEBUG_RT_MUTEXES bool "RT Mutex debugging, deadlock detection" depends on DEBUG_KERNEL && RT_MUTEXES @@ -832,12 +834,6 @@ config DEBUG_LOCKDEP additional runtime checks to debug itself, at the price of more runtime overhead. -config TRACE_IRQFLAGS - bool - help - Enables hooks to interrupt enabling and disabling for - either tracing or lock debugging. - config DEBUG_ATOMIC_SLEEP bool "Sleep inside atomic section checking" select PREEMPT_COUNT @@ -859,6 +855,14 @@ config DEBUG_LOCKING_API_SELFTESTS The following locking APIs are covered: spinlocks, rwlocks, mutexes and rwsems. +endmenu # lock debugging + +config TRACE_IRQFLAGS + bool + help + Enables hooks to interrupt enabling and disabling for + either tracing or lock debugging. + config STACKTRACE bool depends on STACKTRACE_SUPPORT -- cgit v1.2.3 From 604ff0dceb2aca049a25a181c68f8db5c0798f7f Mon Sep 17 00:00:00 2001 From: Dave Hansen Date: Mon, 1 Jul 2013 13:04:49 -0700 Subject: kconfig: consolidate printk options Same deal, take the printk-related things and hide them in a menu. This takes another 4 items out of the top-level menu. Signed-off-by: Dave Hansen Signed-off-by: Linus Torvalds --- lib/Kconfig.debug | 177 +++++++++++++++++++++++++++--------------------------- 1 file changed, 90 insertions(+), 87 deletions(-) diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index c0d41dfadc2d..a6c2c5c1a5f8 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -1,3 +1,4 @@ +menu "printk and dmesg options" config PRINTK_TIME bool "Show timing information on printks" @@ -25,6 +26,95 @@ config DEFAULT_MESSAGE_LOGLEVEL that are auditing their logs closely may want to set it to a lower priority. +config BOOT_PRINTK_DELAY + bool "Delay each boot printk message by N milliseconds" + depends on DEBUG_KERNEL && PRINTK && GENERIC_CALIBRATE_DELAY + help + This build option allows you to read kernel boot messages + by inserting a short delay after each one. The delay is + specified in milliseconds on the kernel command line, + using "boot_delay=N". + + It is likely that you would also need to use "lpj=M" to preset + the "loops per jiffie" value. + See a previous boot log for the "lpj" value to use for your + system, and then set "lpj=M" before setting "boot_delay=N". + NOTE: Using this option may adversely affect SMP systems. + I.e., processors other than the first one may not boot up. + BOOT_PRINTK_DELAY also may cause LOCKUP_DETECTOR to detect + what it believes to be lockup conditions. + +config DYNAMIC_DEBUG + bool "Enable dynamic printk() support" + default n + depends on PRINTK + depends on DEBUG_FS + help + + Compiles debug level messages into the kernel, which would not + otherwise be available at runtime. These messages can then be + enabled/disabled based on various levels of scope - per source file, + function, module, format string, and line number. This mechanism + implicitly compiles in all pr_debug() and dev_dbg() calls, which + enlarges the kernel text size by about 2%. + + If a source file is compiled with DEBUG flag set, any + pr_debug() calls in it are enabled by default, but can be + disabled at runtime as below. Note that DEBUG flag is + turned on by many CONFIG_*DEBUG* options. + + Usage: + + Dynamic debugging is controlled via the 'dynamic_debug/control' file, + which is contained in the 'debugfs' filesystem. Thus, the debugfs + filesystem must first be mounted before making use of this feature. + We refer the control file as: /dynamic_debug/control. This + file contains a list of the debug statements that can be enabled. The + format for each line of the file is: + + filename:lineno [module]function flags format + + filename : source file of the debug statement + lineno : line number of the debug statement + module : module that contains the debug statement + function : function that contains the debug statement + flags : '=p' means the line is turned 'on' for printing + format : the format used for the debug statement + + From a live system: + + nullarbor:~ # cat /dynamic_debug/control + # filename:lineno [module]function flags format + fs/aio.c:222 [aio]__put_ioctx =_ "__put_ioctx:\040freeing\040%p\012" + fs/aio.c:248 [aio]ioctx_alloc =_ "ENOMEM:\040nr_events\040too\040high\012" + fs/aio.c:1770 [aio]sys_io_cancel =_ "calling\040cancel\012" + + Example usage: + + // enable the message at line 1603 of file svcsock.c + nullarbor:~ # echo -n 'file svcsock.c line 1603 +p' > + /dynamic_debug/control + + // enable all the messages in file svcsock.c + nullarbor:~ # echo -n 'file svcsock.c +p' > + /dynamic_debug/control + + // enable all the messages in the NFS server module + nullarbor:~ # echo -n 'module nfsd +p' > + /dynamic_debug/control + + // enable all 12 messages in the function svc_process() + nullarbor:~ # echo -n 'func svc_process +p' > + /dynamic_debug/control + + // disable all 12 messages in the function svc_process() + nullarbor:~ # echo -n 'func svc_process -p' > + /dynamic_debug/control + + See Documentation/dynamic-debug-howto.txt for additional information. + +endmenu # "printk and dmesg options" + menu "Compile-time checks and compiler options" config DEBUG_INFO @@ -940,24 +1030,6 @@ config DEBUG_CREDENTIALS If unsure, say N. -config BOOT_PRINTK_DELAY - bool "Delay each boot printk message by N milliseconds" - depends on DEBUG_KERNEL && PRINTK && GENERIC_CALIBRATE_DELAY - help - This build option allows you to read kernel boot messages - by inserting a short delay after each one. The delay is - specified in milliseconds on the kernel command line, - using "boot_delay=N". - - It is likely that you would also need to use "lpj=M" to preset - the "loops per jiffie" value. - See a previous boot log for the "lpj" value to use for your - system, and then set "lpj=M" before setting "boot_delay=N". - NOTE: Using this option may adversely affect SMP systems. - I.e., processors other than the first one may not boot up. - BOOT_PRINTK_DELAY also may cause LOCKUP_DETECTOR to detect - what it believes to be lockup conditions. - menu "RCU Debugging" config PROVE_RCU @@ -1441,75 +1513,6 @@ config BUILD_DOCSRC Say N if you are unsure. -config DYNAMIC_DEBUG - bool "Enable dynamic printk() support" - default n - depends on PRINTK - depends on DEBUG_FS - help - - Compiles debug level messages into the kernel, which would not - otherwise be available at runtime. These messages can then be - enabled/disabled based on various levels of scope - per source file, - function, module, format string, and line number. This mechanism - implicitly compiles in all pr_debug() and dev_dbg() calls, which - enlarges the kernel text size by about 2%. - - If a source file is compiled with DEBUG flag set, any - pr_debug() calls in it are enabled by default, but can be - disabled at runtime as below. Note that DEBUG flag is - turned on by many CONFIG_*DEBUG* options. - - Usage: - - Dynamic debugging is controlled via the 'dynamic_debug/control' file, - which is contained in the 'debugfs' filesystem. Thus, the debugfs - filesystem must first be mounted before making use of this feature. - We refer the control file as: /dynamic_debug/control. This - file contains a list of the debug statements that can be enabled. The - format for each line of the file is: - - filename:lineno [module]function flags format - - filename : source file of the debug statement - lineno : line number of the debug statement - module : module that contains the debug statement - function : function that contains the debug statement - flags : '=p' means the line is turned 'on' for printing - format : the format used for the debug statement - - From a live system: - - nullarbor:~ # cat /dynamic_debug/control - # filename:lineno [module]function flags format - fs/aio.c:222 [aio]__put_ioctx =_ "__put_ioctx:\040freeing\040%p\012" - fs/aio.c:248 [aio]ioctx_alloc =_ "ENOMEM:\040nr_events\040too\040high\012" - fs/aio.c:1770 [aio]sys_io_cancel =_ "calling\040cancel\012" - - Example usage: - - // enable the message at line 1603 of file svcsock.c - nullarbor:~ # echo -n 'file svcsock.c line 1603 +p' > - /dynamic_debug/control - - // enable all the messages in file svcsock.c - nullarbor:~ # echo -n 'file svcsock.c +p' > - /dynamic_debug/control - - // enable all the messages in the NFS server module - nullarbor:~ # echo -n 'module nfsd +p' > - /dynamic_debug/control - - // enable all 12 messages in the function svc_process() - nullarbor:~ # echo -n 'func svc_process +p' > - /dynamic_debug/control - - // disable all 12 messages in the function svc_process() - nullarbor:~ # echo -n 'func svc_process -p' > - /dynamic_debug/control - - See Documentation/dynamic-debug-howto.txt for additional information. - config DMA_API_DEBUG bool "Enable debugging of DMA-API usage" depends on HAVE_DMA_API_DEBUG -- cgit v1.2.3 From 92aef8fbabb4cc8616777ab27d6499a3719c3e4c Mon Sep 17 00:00:00 2001 From: Dave Hansen Date: Mon, 1 Jul 2013 13:04:50 -0700 Subject: hang and lockup detection menu The hard/softlockup and hung-task entries take up 6 lines of screen real-estate when enabled. I bet folks don't mess with these _that_ often, so move them in a group down a level. Signed-off-by: Dave Hansen Signed-off-by: Linus Torvalds --- lib/Kconfig.debug | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index a6c2c5c1a5f8..65c8e4daee4a 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -604,6 +604,8 @@ config DEBUG_SHIRQ Drivers ought to be able to handle interrupts coming in at those points; some don't and need to be caught. +menu "Debug Lockups and Hangs" + config LOCKUP_DETECTOR bool "Detect Hard and Soft Lockups" depends on DEBUG_KERNEL && !S390 @@ -675,25 +677,6 @@ config BOOTPARAM_SOFTLOCKUP_PANIC_VALUE default 0 if !BOOTPARAM_SOFTLOCKUP_PANIC default 1 if BOOTPARAM_SOFTLOCKUP_PANIC -config PANIC_ON_OOPS - bool "Panic on Oops" - help - Say Y here to enable the kernel to panic when it oopses. This - has the same effect as setting oops=panic on the kernel command - line. - - This feature is useful to ensure that the kernel does not do - anything erroneous after an oops which could result in data - corruption or other issues. - - Say N if unsure. - -config PANIC_ON_OOPS_VALUE - int - range 0 1 - default 0 if !PANIC_ON_OOPS - default 1 if PANIC_ON_OOPS - config DETECT_HUNG_TASK bool "Detect Hung Tasks" depends on DEBUG_KERNEL @@ -748,6 +731,27 @@ config BOOTPARAM_HUNG_TASK_PANIC_VALUE default 0 if !BOOTPARAM_HUNG_TASK_PANIC default 1 if BOOTPARAM_HUNG_TASK_PANIC +endmenu # "Debug lockups and hangs" + +config PANIC_ON_OOPS + bool "Panic on Oops" + help + Say Y here to enable the kernel to panic when it oopses. This + has the same effect as setting oops=panic on the kernel command + line. + + This feature is useful to ensure that the kernel does not do + anything erroneous after an oops which could result in data + corruption or other issues. + + Say N if unsure. + +config PANIC_ON_OOPS_VALUE + int + range 0 1 + default 0 if !PANIC_ON_OOPS + default 1 if PANIC_ON_OOPS + config SCHED_DEBUG bool "Collect scheduler debugging info" depends on DEBUG_KERNEL && PROC_FS -- cgit v1.2.3 From 57961e3ba72f4a8a1aa52e978020ecc2ca03a79f Mon Sep 17 00:00:00 2001 From: Ferruh Yigit Date: Thu, 4 Jul 2013 14:02:45 -0700 Subject: Input: cyttsp4 - kfree xfer_buf on error path in probe() If probe() fails after cd->xfer_buf allocated, it will not freed. Added kfree(cd->xfer_buf) with and error label. Signed-off-by: Ferruh Yigit Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/cyttsp4_core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/cyttsp4_core.c b/drivers/input/touchscreen/cyttsp4_core.c index a7987e170071..edcf7993034b 100644 --- a/drivers/input/touchscreen/cyttsp4_core.c +++ b/drivers/input/touchscreen/cyttsp4_core.c @@ -2049,7 +2049,7 @@ struct cyttsp4 *cyttsp4_probe(const struct cyttsp4_bus_ops *ops, cd->irq = gpio_to_irq(cd->cpdata->irq_gpio); if (cd->irq < 0) { rc = -EINVAL; - goto error_free_cd; + goto error_free_xfer; } dev_set_drvdata(dev, cd); @@ -2117,6 +2117,8 @@ error_request_irq: if (cd->cpdata->init) cd->cpdata->init(cd->cpdata, 0, dev); dev_set_drvdata(dev, NULL); +error_free_xfer: + kfree(cd->xfer_buf); error_free_cd: kfree(cd); error_alloc_data: -- cgit v1.2.3 From c9324d1870f3a7d13297ae6b787c567606ab3c89 Mon Sep 17 00:00:00 2001 From: Giuseppe CAVALLARO Date: Thu, 4 Jul 2013 06:18:07 +0200 Subject: net:stmmac: fix memleak in the open method This patch is to fix a memory leak in the open method, it reviews error conditions freeing the resources previously allocated and not freed in cased of DMA failure. Signed-off-by: Giuseppe Cavallaro Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index f2d283d2528f..f2ccb36e8685 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1552,7 +1552,7 @@ static int stmmac_open(struct net_device *dev) if (ret) { pr_err("%s: Cannot attach to PHY (error: %d)\n", __func__, ret); - goto open_error; + goto phy_error; } } @@ -1566,7 +1566,7 @@ static int stmmac_open(struct net_device *dev) ret = stmmac_init_dma_engine(priv); if (ret < 0) { pr_err("%s: DMA initialization failed\n", __func__); - goto open_error; + goto init_error; } /* Copy the MAC addr into the HW */ @@ -1585,7 +1585,7 @@ static int stmmac_open(struct net_device *dev) if (unlikely(ret < 0)) { pr_err("%s: ERROR: allocating the IRQ %d (error: %d)\n", __func__, dev->irq, ret); - goto open_error; + goto init_error; } /* Request the Wake IRQ in case of another line is used for WoL */ @@ -1595,7 +1595,7 @@ static int stmmac_open(struct net_device *dev) if (unlikely(ret < 0)) { pr_err("%s: ERROR: allocating the WoL IRQ %d (%d)\n", __func__, priv->wol_irq, ret); - goto open_error_wolirq; + goto wolirq_error; } } @@ -1606,7 +1606,7 @@ static int stmmac_open(struct net_device *dev) if (unlikely(ret < 0)) { pr_err("%s: ERROR: allocating the LPI IRQ %d (%d)\n", __func__, priv->lpi_irq, ret); - goto open_error_lpiirq; + goto lpiirq_error; } } @@ -1664,17 +1664,17 @@ static int stmmac_open(struct net_device *dev) return 0; -open_error_lpiirq: +lpiirq_error: if (priv->wol_irq != dev->irq) free_irq(priv->wol_irq, dev); - -open_error_wolirq: +wolirq_error: free_irq(dev->irq, dev); -open_error: +init_error: + free_dma_desc_resources(priv); if (priv->phydev) phy_disconnect(priv->phydev); - +phy_error: clk_disable_unprepare(priv->stmmac_clk); return ret; -- cgit v1.2.3 From d741434c3e0ae5592b72a0f4833a42b4be1b2fa1 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 4 Jul 2013 10:35:35 +0100 Subject: dt:net:stmmac: Allocate platform data only if its NULL. In some DT use-cases platform data might be already allocated and passed via AUXDATA. These are the cases where machine level code populates few callbacks in the platform data. This patch adds check and reuses platform_data if its valid, before allocating a new one. Signed-off-by: Srinivas Kandagatla Acked-by: Giuseppe Cavallaro Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index 17bc7827e7ca..adfb9a3ce6c5 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -92,8 +92,10 @@ static int stmmac_pltfr_probe(struct platform_device *pdev) if (IS_ERR(addr)) return PTR_ERR(addr); + plat_dat = pdev->dev.platform_data; if (pdev->dev.of_node) { - plat_dat = devm_kzalloc(&pdev->dev, + if (!plat_dat) + plat_dat = devm_kzalloc(&pdev->dev, sizeof(struct plat_stmmacenet_data), GFP_KERNEL); if (!plat_dat) { @@ -106,8 +108,6 @@ static int stmmac_pltfr_probe(struct platform_device *pdev) pr_err("%s: main dt probe failed", __func__); return ret; } - } else { - plat_dat = pdev->dev.platform_data; } /* Custom initialisation (if needed)*/ -- cgit v1.2.3 From 25c83b5c2e82560406d5265f42cd147f1eb441d0 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 4 Jul 2013 10:35:41 +0100 Subject: dt:net:stmmac: Add support to dwmac version 3.610 and 3.710 This patch adds dt support to dwmac version 3.610 and 3.710 these versions are integrated in STiH415 and STiH416 ARM A9 SOCs. To support these IP version, some of the device tree properties are extended. Signed-off-by: Srinivas Kandagatla Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/stmmac.txt | 4 ++++ .../net/ethernet/stmicro/stmmac/stmmac_platform.c | 26 ++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/Documentation/devicetree/bindings/net/stmmac.txt b/Documentation/devicetree/bindings/net/stmmac.txt index 060bbf098ef3..e1ddfcc46a4e 100644 --- a/Documentation/devicetree/bindings/net/stmmac.txt +++ b/Documentation/devicetree/bindings/net/stmmac.txt @@ -12,6 +12,10 @@ Required properties: property - phy-mode: String, operation mode of the PHY interface. Supported values are: "mii", "rmii", "gmii", "rgmii". +- snps,phy-addr phy address to connect to. +- snps,pbl Programmable Burst Length +- snps,fixed-burst Program the DMA to use the fixed burst mode +- snps,mixed-burst Program the DMA to use the mixed burst mode Optional properties: - mac-address: 6 bytes, mac address diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index adfb9a3ce6c5..03de76c7a177 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -34,12 +34,20 @@ static int stmmac_probe_config_dt(struct platform_device *pdev, const char **mac) { struct device_node *np = pdev->dev.of_node; + struct stmmac_dma_cfg *dma_cfg; if (!np) return -ENODEV; *mac = of_get_mac_address(np); plat->interface = of_get_phy_mode(np); + + plat->bus_id = of_alias_get_id(np, "ethernet"); + if (plat->bus_id < 0) + plat->bus_id = 0; + + of_property_read_u32(np, "snps,phy-addr", &plat->phy_addr); + plat->mdio_bus_data = devm_kzalloc(&pdev->dev, sizeof(struct stmmac_mdio_bus_data), GFP_KERNEL); @@ -56,6 +64,22 @@ static int stmmac_probe_config_dt(struct platform_device *pdev, plat->pmt = 1; } + if (of_device_is_compatible(np, "snps,dwmac-3.610") || + of_device_is_compatible(np, "snps,dwmac-3.710")) { + plat->enh_desc = 1; + plat->bugged_jumbo = 1; + plat->force_sf_dma_mode = 1; + } + + dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*dma_cfg), GFP_KERNEL); + if (!dma_cfg) + return -ENOMEM; + + plat->dma_cfg = dma_cfg; + of_property_read_u32(np, "snps,pbl", &dma_cfg->pbl); + dma_cfg->fixed_burst = of_property_read_bool(np, "snps,fixed-burst"); + dma_cfg->mixed_burst = of_property_read_bool(np, "snps,mixed-burst"); + return 0; } #else @@ -228,7 +252,9 @@ static const struct dev_pm_ops stmmac_pltfr_pm_ops; static const struct of_device_id stmmac_dt_ids[] = { { .compatible = "st,spear600-gmac"}, + { .compatible = "snps,dwmac-3.610"}, { .compatible = "snps,dwmac-3.70a"}, + { .compatible = "snps,dwmac-3.710"}, { .compatible = "snps,dwmac"}, { /* sentinel */ } }; -- cgit v1.2.3 From 0e0764715d8116484d808f5b3985ca043080788e Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 4 Jul 2013 10:35:48 +0100 Subject: dt:net:stmmac: Add dt specific phy reset callback support. This patch adds phy reset callback support for stmmac driver via device trees. It adds three new properties to gmac device tree bindings to define the reset signal via gpio. With this patch users can conveniently pass reset gpio number with pre, pulse and post delay in micro secs via DTs. active low: _________ ____________ | | | | |_______________| active high: ________________ | | | | ________| |___________ Signed-off-by: Srinivas Kandagatla Signed-off-by: David S. Miller --- Documentation/devicetree/bindings/net/stmmac.txt | 6 +++ drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c | 48 ++++++++++++++++++++++- include/linux/stmmac.h | 4 ++ 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/net/stmmac.txt b/Documentation/devicetree/bindings/net/stmmac.txt index e1ddfcc46a4e..261c563b5f06 100644 --- a/Documentation/devicetree/bindings/net/stmmac.txt +++ b/Documentation/devicetree/bindings/net/stmmac.txt @@ -13,6 +13,12 @@ Required properties: - phy-mode: String, operation mode of the PHY interface. Supported values are: "mii", "rmii", "gmii", "rgmii". - snps,phy-addr phy address to connect to. +- snps,reset-gpio gpio number for phy reset. +- snps,reset-active-low boolean flag to indicate if phy reset is active low. +- snps,reset-delays-us is triplet of delays + The 1st cell is reset pre-delay in micro seconds. + The 2nd cell is reset pulse in micro seconds. + The 3rd cell is reset post-delay in micro seconds. - snps,pbl Programmable Burst Length - snps,fixed-burst Program the DMA to use the fixed burst mode - snps,mixed-burst Program the DMA to use the mixed burst mode diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c index cc15039eaa47..fe7bc9903867 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c @@ -27,6 +27,9 @@ #include #include #include +#include +#include + #include #include "stmmac.h" @@ -131,10 +134,46 @@ static int stmmac_mdio_reset(struct mii_bus *bus) struct net_device *ndev = bus->priv; struct stmmac_priv *priv = netdev_priv(ndev); unsigned int mii_address = priv->hw->mii.addr; + struct stmmac_mdio_bus_data *data = priv->plat->mdio_bus_data; + +#ifdef CONFIG_OF + if (priv->device->of_node) { + int reset_gpio, active_low; + + if (data->reset_gpio < 0) { + struct device_node *np = priv->device->of_node; + if (!np) + return 0; + + data->reset_gpio = of_get_named_gpio(np, + "snps,reset-gpio", 0); + if (data->reset_gpio < 0) + return 0; + + data->active_low = of_property_read_bool(np, + "snps,reset-active-low"); + of_property_read_u32_array(np, + "snps,reset-delays-us", data->delays, 3); + } + + reset_gpio = data->reset_gpio; + active_low = data->active_low; + + if (!gpio_request(reset_gpio, "mdio-reset")) { + gpio_direction_output(reset_gpio, active_low ? 1 : 0); + udelay(data->delays[0]); + gpio_set_value(reset_gpio, active_low ? 0 : 1); + udelay(data->delays[1]); + gpio_set_value(reset_gpio, active_low ? 1 : 0); + udelay(data->delays[2]); + gpio_free(reset_gpio); + } + } +#endif - if (priv->plat->mdio_bus_data->phy_reset) { + if (data->phy_reset) { pr_debug("stmmac_mdio_reset: calling phy_reset\n"); - priv->plat->mdio_bus_data->phy_reset(priv->plat->bsp_priv); + data->phy_reset(priv->plat->bsp_priv); } /* This is a workaround for problems with the STE101P PHY. @@ -172,6 +211,11 @@ int stmmac_mdio_register(struct net_device *ndev) else irqlist = priv->mii_irq; +#ifdef CONFIG_OF + if (priv->device->of_node) + mdio_bus_data->reset_gpio = -1; +#endif + new_bus->name = "stmmac"; new_bus->read = &stmmac_mdio_read; new_bus->write = &stmmac_mdio_write; diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h index c1b3ed3fb787..9e495d31516e 100644 --- a/include/linux/stmmac.h +++ b/include/linux/stmmac.h @@ -80,6 +80,10 @@ struct stmmac_mdio_bus_data { unsigned int phy_mask; int *irqs; int probed_phy_irq; +#ifdef CONFIG_OF + int reset_gpio, active_low; + u32 delays[3]; +#endif }; struct stmmac_dma_cfg { -- cgit v1.2.3 From 86bd68bfd75941d4cf3b874468791c3e73eef23d Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Wed, 3 Jul 2013 17:00:34 +0200 Subject: sit: fix tunnel update via netlink The device can stand in another netns, hence we need to do the lookup in netns tunnel->net. Signed-off-by: Nicolas Dichtel Signed-off-by: David S. Miller --- net/ipv6/sit.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 85ff37b1ce02..a3437a4cd07e 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -1426,9 +1426,9 @@ static int ipip6_newlink(struct net *src_net, struct net_device *dev, static int ipip6_changelink(struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { - struct ip_tunnel *t; + struct ip_tunnel *t = netdev_priv(dev); struct ip_tunnel_parm p; - struct net *net = dev_net(dev); + struct net *net = t->net; struct sit_net *sitn = net_generic(net, sit_net_id); #ifdef CONFIG_IPV6_SIT_6RD struct ip_tunnel_6rd ip6rd; -- cgit v1.2.3 From f605181abd95a109031a23c67a824eb8e5dcfe67 Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Fri, 28 Jun 2013 15:34:29 +0200 Subject: cpupower: Make idlestate usage unsigned Use unsigned int as the data type for some variables related to CPU idle states which allows the code to be simplified slightly. [rjw: Changelog] Signed-off-by: Thomas Renninger Signed-off-by: Rafael J. Wysocki --- tools/power/cpupower/utils/cpuidle-info.c | 18 ++++-------------- tools/power/cpupower/utils/helpers/sysfs.c | 2 +- tools/power/cpupower/utils/helpers/sysfs.h | 2 +- 3 files changed, 6 insertions(+), 16 deletions(-) diff --git a/tools/power/cpupower/utils/cpuidle-info.c b/tools/power/cpupower/utils/cpuidle-info.c index 8145af5f93a6..edd5dba3e249 100644 --- a/tools/power/cpupower/utils/cpuidle-info.c +++ b/tools/power/cpupower/utils/cpuidle-info.c @@ -22,7 +22,7 @@ static void cpuidle_cpu_output(unsigned int cpu, int verbose) { - int idlestates, idlestate; + unsigned int idlestates, idlestate; char *tmp; printf(_ ("Analyzing CPU %d:\n"), cpu); @@ -31,10 +31,8 @@ static void cpuidle_cpu_output(unsigned int cpu, int verbose) if (idlestates == 0) { printf(_("CPU %u: No idle states\n"), cpu); return; - } else if (idlestates <= 0) { - printf(_("CPU %u: Can't read idle state info\n"), cpu); - return; } + printf(_("Number of idle states: %d\n"), idlestates); printf(_("Available idle states:")); for (idlestate = 0; idlestate < idlestates; idlestate++) { @@ -98,21 +96,13 @@ static void cpuidle_general_output(void) static void proc_cpuidle_cpu_output(unsigned int cpu) { long max_allowed_cstate = 2000000000; - int cstates, cstate; + unsigned int cstate, cstates; cstates = sysfs_get_idlestate_count(cpu); if (cstates == 0) { - /* - * Go on and print same useless info as you'd see with - * cat /proc/acpi/processor/../power - * printf(_("CPU %u: No C-states available\n"), cpu); - * return; - */ - } else if (cstates <= 0) { - printf(_("CPU %u: Can't read C-state info\n"), cpu); + printf(_("CPU %u: No C-states info\n"), cpu); return; } - /* printf("Cstates: %d\n", cstates); */ printf(_("active state: C0\n")); printf(_("max_cstate: C%u\n"), cstates-1); diff --git a/tools/power/cpupower/utils/helpers/sysfs.c b/tools/power/cpupower/utils/helpers/sysfs.c index 38ab91629463..891f6710d026 100644 --- a/tools/power/cpupower/utils/helpers/sysfs.c +++ b/tools/power/cpupower/utils/helpers/sysfs.c @@ -238,7 +238,7 @@ char *sysfs_get_idlestate_desc(unsigned int cpu, unsigned int idlestate) * Negativ in error case * Zero if cpuidle does not export any C-states */ -int sysfs_get_idlestate_count(unsigned int cpu) +unsigned int sysfs_get_idlestate_count(unsigned int cpu) { char file[SYSFS_PATH_MAX]; struct stat statbuf; diff --git a/tools/power/cpupower/utils/helpers/sysfs.h b/tools/power/cpupower/utils/helpers/sysfs.h index 8cb797bbceb0..0401a97a4cab 100644 --- a/tools/power/cpupower/utils/helpers/sysfs.h +++ b/tools/power/cpupower/utils/helpers/sysfs.h @@ -19,7 +19,7 @@ extern char *sysfs_get_idlestate_name(unsigned int cpu, unsigned int idlestate); extern char *sysfs_get_idlestate_desc(unsigned int cpu, unsigned int idlestate); -extern int sysfs_get_idlestate_count(unsigned int cpu); +extern unsigned int sysfs_get_idlestate_count(unsigned int cpu); extern char *sysfs_get_cpuidle_governor(void); extern char *sysfs_get_cpuidle_driver(void); -- cgit v1.2.3 From 0924c369bc5492cf181a066fc2d459aa18ffa5ac Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Fri, 28 Jun 2013 15:34:30 +0200 Subject: cpupower: Implement disabling of cstate interface Latest kernel allows to disable C-states via: /sys/devices/system/cpu/cpuX/cpuidle/stateY/disable This patch provides lower level sysfs access functions to make use of this interface. A later patch will implement the higher level stuff. Signed-off-by: Thomas Renninger Signed-off-by: Rafael J. Wysocki --- tools/power/cpupower/utils/helpers/sysfs.c | 116 ++++++++++++++++++++++++++++- tools/power/cpupower/utils/helpers/sysfs.h | 8 ++ 2 files changed, 123 insertions(+), 1 deletion(-) diff --git a/tools/power/cpupower/utils/helpers/sysfs.c b/tools/power/cpupower/utils/helpers/sysfs.c index 891f6710d026..5cdc600e8152 100644 --- a/tools/power/cpupower/utils/helpers/sysfs.c +++ b/tools/power/cpupower/utils/helpers/sysfs.c @@ -87,8 +87,35 @@ int sysfs_is_cpu_online(unsigned int cpu) return value; } +/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */ + + /* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */ +/* + * helper function to check whether a file under "../cpuX/cpuidle/stateX/" dir + * exists. + * For example the functionality to disable c-states was introduced in later + * kernel versions, this function can be used to explicitly check for this + * feature. + * + * returns 1 if the file exists, 0 otherwise. + */ +unsigned int sysfs_idlestate_file_exists(unsigned int cpu, + unsigned int idlestate, + const char *fname) +{ + char path[SYSFS_PATH_MAX]; + struct stat statbuf; + + + snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s", + cpu, idlestate, fname); + if (stat(path, &statbuf) != 0) + return 0; + return 1; +} + /* * helper function to read file from /sys into given buffer * fname is a relative path under "cpuX/cpuidle/stateX/" dir @@ -121,6 +148,40 @@ unsigned int sysfs_idlestate_read_file(unsigned int cpu, unsigned int idlestate, return (unsigned int) numread; } +/* + * helper function to write a new value to a /sys file + * fname is a relative path under "../cpuX/cpuidle/cstateY/" dir + * + * Returns the number of bytes written or 0 on error + */ +static +unsigned int sysfs_idlestate_write_file(unsigned int cpu, + unsigned int idlestate, + const char *fname, + const char *value, size_t len) +{ + char path[SYSFS_PATH_MAX]; + int fd; + ssize_t numwrite; + + snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s", + cpu, idlestate, fname); + + fd = open(path, O_WRONLY); + if (fd == -1) + return 0; + + numwrite = write(fd, value, len); + if (numwrite < 1) { + close(fd); + return 0; + } + + close(fd); + + return (unsigned int) numwrite; +} + /* read access to files which contain one numeric value */ enum idlestate_value { @@ -128,6 +189,7 @@ enum idlestate_value { IDLESTATE_POWER, IDLESTATE_LATENCY, IDLESTATE_TIME, + IDLESTATE_DISABLE, MAX_IDLESTATE_VALUE_FILES }; @@ -136,6 +198,7 @@ static const char *idlestate_value_files[MAX_IDLESTATE_VALUE_FILES] = { [IDLESTATE_POWER] = "power", [IDLESTATE_LATENCY] = "latency", [IDLESTATE_TIME] = "time", + [IDLESTATE_DISABLE] = "disable", }; static unsigned long long sysfs_idlestate_get_one_value(unsigned int cpu, @@ -205,8 +268,59 @@ static char *sysfs_idlestate_get_one_string(unsigned int cpu, return result; } +/* + * Returns: + * 1 if disabled + * 0 if enabled + * -1 if idlestate is not available + * -2 if disabling is not supported by the kernel + */ +int sysfs_is_idlestate_disabled(unsigned int cpu, + unsigned int idlestate) +{ + if (sysfs_get_idlestate_count(cpu) < idlestate) + return -1; + + if (!sysfs_idlestate_file_exists(cpu, idlestate, + idlestate_value_files[IDLESTATE_DISABLE])) + return -2; + return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_DISABLE); +} + +/* + * Pass 1 as last argument to disable or 0 to enable the state + * Returns: + * 0 on success + * negative values on error, for example: + * -1 if idlestate is not available + * -2 if disabling is not supported by the kernel + * -3 No write access to disable/enable C-states + */ +int sysfs_idlestate_disable(unsigned int cpu, + unsigned int idlestate, + unsigned int disable) +{ + char value[SYSFS_PATH_MAX]; + int bytes_written; + + if (sysfs_get_idlestate_count(cpu) < idlestate) + return -1; + + if (!sysfs_idlestate_file_exists(cpu, idlestate, + idlestate_value_files[IDLESTATE_DISABLE])) + return -2; + + snprintf(value, SYSFS_PATH_MAX, "%u", disable); + + bytes_written = sysfs_idlestate_write_file(cpu, idlestate, "disable", + value, sizeof(disable)); + if (bytes_written) + return 0; + return -3; +} + unsigned long sysfs_get_idlestate_latency(unsigned int cpu, - unsigned int idlestate) + unsigned int idlestate) { return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_LATENCY); } diff --git a/tools/power/cpupower/utils/helpers/sysfs.h b/tools/power/cpupower/utils/helpers/sysfs.h index 0401a97a4cab..d28f11fedbda 100644 --- a/tools/power/cpupower/utils/helpers/sysfs.h +++ b/tools/power/cpupower/utils/helpers/sysfs.h @@ -7,8 +7,16 @@ extern unsigned int sysfs_read_file(const char *path, char *buf, size_t buflen); +extern unsigned int sysfs_idlestate_file_exists(unsigned int cpu, + unsigned int idlestate, + const char *fname); + extern int sysfs_is_cpu_online(unsigned int cpu); +extern int sysfs_is_idlestate_disabled(unsigned int cpu, + unsigned int idlestate); +extern int sysfs_idlestate_disable(unsigned int cpu, unsigned int idlestate, + unsigned int disable); extern unsigned long sysfs_get_idlestate_latency(unsigned int cpu, unsigned int idlestate); extern unsigned long sysfs_get_idlestate_usage(unsigned int cpu, -- cgit v1.2.3 From c4f3610eba69321b9cf35779cd67e68b5138cc16 Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Fri, 28 Jun 2013 15:34:31 +0200 Subject: cpupower: Introduce idle-set subcommand and C-state enabling/disabling Example: cpupower idle-set -d 3 will disable C-state 3 on all processors (set commands are active on all CPUs by default), same as: cpupower -c all idle-set -d 3 Signed-off-by: Thomas Renninger Signed-off-by: Rafael J. Wysocki --- tools/power/cpupower/Makefile | 3 +- tools/power/cpupower/man/cpupower-monitor.1 | 10 ++- tools/power/cpupower/utils/builtin.h | 1 + tools/power/cpupower/utils/cpuidle-info.c | 6 +- tools/power/cpupower/utils/cpuidle-set.c | 118 ++++++++++++++++++++++++++++ tools/power/cpupower/utils/cpupower.c | 13 +-- 6 files changed, 142 insertions(+), 9 deletions(-) create mode 100644 tools/power/cpupower/utils/cpuidle-set.c diff --git a/tools/power/cpupower/Makefile b/tools/power/cpupower/Makefile index d875a74a3bdf..ce17f30f04ba 100644 --- a/tools/power/cpupower/Makefile +++ b/tools/power/cpupower/Makefile @@ -131,7 +131,8 @@ UTIL_OBJS = utils/helpers/amd.o utils/helpers/topology.o utils/helpers/msr.o \ utils/idle_monitor/amd_fam14h_idle.o utils/idle_monitor/cpuidle_sysfs.o \ utils/idle_monitor/mperf_monitor.o utils/idle_monitor/cpupower-monitor.o \ utils/cpupower.o utils/cpufreq-info.o utils/cpufreq-set.o \ - utils/cpupower-set.o utils/cpupower-info.o utils/cpuidle-info.o + utils/cpupower-set.o utils/cpupower-info.o utils/cpuidle-info.o \ + utils/cpuidle-set.o UTIL_SRC := $(UTIL_OBJS:.o=.c) diff --git a/tools/power/cpupower/man/cpupower-monitor.1 b/tools/power/cpupower/man/cpupower-monitor.1 index e01c35d13b6e..914cbb9d9cd0 100644 --- a/tools/power/cpupower/man/cpupower-monitor.1 +++ b/tools/power/cpupower/man/cpupower-monitor.1 @@ -110,13 +110,21 @@ May work poorly on Linux-2.6.20 through 2.6.29, as the \fBacpi-cpufreq \fP kernel frequency driver periodically cleared aperf/mperf registers in those kernels. -.SS "Nehalem" "SandyBridge" +.SS "Nehalem" "SandyBridge" "HaswellExtended" Intel Core and Package sleep state counters. Threads (hyperthreaded cores) may not be able to enter deeper core states if its sibling is utilized. Deepest package sleep states may in reality show up as machine/platform wide sleep states and can only be entered if all cores are idle. Look up Intel manuals (some are provided in the References section) for further details. +The monitors are named after the CPU family where the sleep state capabilities +got introduced and may not match exactly the CPU name of the platform. +For example an IvyBridge processor has sleep state capabilities which got +introduced in Nehalem and SandyBridge processor families. +Thus on an IvyBridge processor one will get Nehalem and SandyBridge sleep +state monitors. +HaswellExtended extra package sleep state capabilities are available only in a +specific Haswell (family 0x45) and probably also other future processors. .SS "Fam_12h" "Fam_14h" AMD laptop and desktop processor (family 12h and 14h) sleep state counters. diff --git a/tools/power/cpupower/utils/builtin.h b/tools/power/cpupower/utils/builtin.h index c10496fbe3c6..2284c8ea4e2a 100644 --- a/tools/power/cpupower/utils/builtin.h +++ b/tools/power/cpupower/utils/builtin.h @@ -5,6 +5,7 @@ extern int cmd_set(int argc, const char **argv); extern int cmd_info(int argc, const char **argv); extern int cmd_freq_set(int argc, const char **argv); extern int cmd_freq_info(int argc, const char **argv); +extern int cmd_idle_set(int argc, const char **argv); extern int cmd_idle_info(int argc, const char **argv); extern int cmd_monitor(int argc, const char **argv); diff --git a/tools/power/cpupower/utils/cpuidle-info.c b/tools/power/cpupower/utils/cpuidle-info.c index edd5dba3e249..75e66de7e7a7 100644 --- a/tools/power/cpupower/utils/cpuidle-info.c +++ b/tools/power/cpupower/utils/cpuidle-info.c @@ -48,10 +48,14 @@ static void cpuidle_cpu_output(unsigned int cpu, int verbose) return; for (idlestate = 0; idlestate < idlestates; idlestate++) { + int disabled = sysfs_is_idlestate_disabled(cpu, idlestate); + /* Disabled interface not supported on older kernels */ + if (disabled < 0) + disabled = 0; tmp = sysfs_get_idlestate_name(cpu, idlestate); if (!tmp) continue; - printf("%s:\n", tmp); + printf("%s%s:\n", tmp, (disabled) ? " (DISABLED) " : ""); free(tmp); tmp = sysfs_get_idlestate_desc(cpu, idlestate); diff --git a/tools/power/cpupower/utils/cpuidle-set.c b/tools/power/cpupower/utils/cpuidle-set.c new file mode 100644 index 000000000000..c78141c5dfac --- /dev/null +++ b/tools/power/cpupower/utils/cpuidle-set.c @@ -0,0 +1,118 @@ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "cpufreq.h" +#include "helpers/helpers.h" +#include "helpers/sysfs.h" + +static struct option info_opts[] = { + { .name = "disable", .has_arg = required_argument, .flag = NULL, .val = 'd'}, + { .name = "enable", .has_arg = required_argument, .flag = NULL, .val = 'e'}, + { }, +}; + + +int cmd_idle_set(int argc, char **argv) +{ + extern char *optarg; + extern int optind, opterr, optopt; + int ret = 0, cont = 1, param = 0, idlestate = 0; + unsigned int cpu = 0; + + do { + ret = getopt_long(argc, argv, "d:e:", info_opts, NULL); + if (ret == -1) + break; + switch (ret) { + case '?': + param = '?'; + cont = 0; + break; + case 'd': + if (param) { + param = -1; + cont = 0; + break; + } + param = ret; + idlestate = atoi(optarg); + break; + case 'e': + if (param) { + param = -1; + cont = 0; + break; + } + param = ret; + idlestate = atoi(optarg); + break; + case -1: + cont = 0; + break; + } + } while (cont); + + switch (param) { + case -1: + printf(_("You can't specify more than one " + "output-specific argument\n")); + exit(EXIT_FAILURE); + case '?': + printf(_("invalid or unknown argument\n")); + exit(EXIT_FAILURE); + } + + /* Default is: set all CPUs */ + if (bitmask_isallclear(cpus_chosen)) + bitmask_setall(cpus_chosen); + + for (cpu = bitmask_first(cpus_chosen); + cpu <= bitmask_last(cpus_chosen); cpu++) { + + if (!bitmask_isbitset(cpus_chosen, cpu)) + continue; + + switch (param) { + + case 'd': + ret = sysfs_idlestate_disable(cpu, idlestate, 1); + if (ret == 0) + printf(_("Idlestate %u disabled on CPU %u\n"), idlestate, cpu); + else if (ret == -1) + printf(_("Idlestate %u not available on CPU %u\n"), + idlestate, cpu); + else if (ret == -2) + printf(_("Idlestate disabling not supported by kernel\n")); + else + printf(_("Idlestate %u not disabled on CPU %u\n"), + idlestate, cpu); + break; + case 'e': + ret = sysfs_idlestate_disable(cpu, idlestate, 0); + if (ret == 0) + printf(_("Idlestate %u enabled on CPU %u\n"), idlestate, cpu); + else if (ret == -1) + printf(_("Idlestate %u not available on CPU %u\n"), + idlestate, cpu); + else if (ret == -2) + printf(_("Idlestate enabling not supported by kernel\n")); + else + printf(_("Idlestate %u not enabled on CPU %u\n"), + idlestate, cpu); + break; + default: + /* Not reachable with proper args checking */ + printf(_("Invalid or unknown argument\n")); + exit(EXIT_FAILURE); + break; + } + } + return EXIT_SUCCESS; +} diff --git a/tools/power/cpupower/utils/cpupower.c b/tools/power/cpupower/utils/cpupower.c index 52bee591c1c5..7efc570ffbaa 100644 --- a/tools/power/cpupower/utils/cpupower.c +++ b/tools/power/cpupower/utils/cpupower.c @@ -17,12 +17,6 @@ #include "helpers/helpers.h" #include "helpers/bitmask.h" -struct cmd_struct { - const char *cmd; - int (*main)(int, const char **); - int needs_root; -}; - #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) static int cmd_help(int argc, const char **argv); @@ -43,10 +37,17 @@ int be_verbose; static void print_help(void); +struct cmd_struct { + const char *cmd; + int (*main)(int, const char **); + int needs_root; +}; + static struct cmd_struct commands[] = { { "frequency-info", cmd_freq_info, 0 }, { "frequency-set", cmd_freq_set, 1 }, { "idle-info", cmd_idle_info, 0 }, + { "idle-set", cmd_idle_set, 1 }, { "set", cmd_set, 1 }, { "info", cmd_info, 0 }, { "monitor", cmd_monitor, 0 }, -- cgit v1.2.3 From 2aa1ca75c48f1da2c9bdc81ec4403eaf89a410d5 Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Fri, 28 Jun 2013 15:34:32 +0200 Subject: cpupower: Haswell also supports the C-states introduced with SandyBridge Add Haswell model numbers to snb_register() as it also supports the C-states introduced in SandyBridge processors. [rjw: Changelog] Signed-off-by: Thomas Renninger Signed-off-by: Rafael J. Wysocki --- tools/power/cpupower/utils/idle_monitor/snb_idle.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/power/cpupower/utils/idle_monitor/snb_idle.c b/tools/power/cpupower/utils/idle_monitor/snb_idle.c index a99b43b97d6d..efc8a69c9aba 100644 --- a/tools/power/cpupower/utils/idle_monitor/snb_idle.c +++ b/tools/power/cpupower/utils/idle_monitor/snb_idle.c @@ -155,6 +155,10 @@ static struct cpuidle_monitor *snb_register(void) case 0x2D: /* SNB Xeon */ case 0x3A: /* IVB */ case 0x3E: /* IVB Xeon */ + case 0x3C: /* HSW */ + case 0x3F: /* HSW */ + case 0x45: /* HSW */ + case 0x46: /* HSW */ break; default: return NULL; -- cgit v1.2.3 From 7ee767b69b6885dd81bafaf1881c5028033a6177 Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Fri, 28 Jun 2013 15:34:33 +0200 Subject: cpupower: Add Haswell family 0x45 specific idle monitor to show PC8,9,10 states This specific processor supports 3 new package sleep states. Provide a monitor, so that the user can see their usage. Signed-off-by: Thomas Renninger Signed-off-by: Rafael J. Wysocki --- tools/power/cpupower/Makefile | 1 + .../cpupower/utils/idle_monitor/hsw_ext_idle.c | 196 +++++++++++++++++++++ .../cpupower/utils/idle_monitor/idle_monitors.def | 1 + 3 files changed, 198 insertions(+) create mode 100644 tools/power/cpupower/utils/idle_monitor/hsw_ext_idle.c diff --git a/tools/power/cpupower/Makefile b/tools/power/cpupower/Makefile index ce17f30f04ba..cbfec92af327 100644 --- a/tools/power/cpupower/Makefile +++ b/tools/power/cpupower/Makefile @@ -128,6 +128,7 @@ UTIL_OBJS = utils/helpers/amd.o utils/helpers/topology.o utils/helpers/msr.o \ utils/helpers/sysfs.o utils/helpers/misc.o utils/helpers/cpuid.o \ utils/helpers/pci.o utils/helpers/bitmask.o \ utils/idle_monitor/nhm_idle.o utils/idle_monitor/snb_idle.o \ + utils/idle_monitor/hsw_ext_idle.o \ utils/idle_monitor/amd_fam14h_idle.o utils/idle_monitor/cpuidle_sysfs.o \ utils/idle_monitor/mperf_monitor.o utils/idle_monitor/cpupower-monitor.o \ utils/cpupower.o utils/cpufreq-info.o utils/cpufreq-set.o \ diff --git a/tools/power/cpupower/utils/idle_monitor/hsw_ext_idle.c b/tools/power/cpupower/utils/idle_monitor/hsw_ext_idle.c new file mode 100644 index 000000000000..ebeaba6571a3 --- /dev/null +++ b/tools/power/cpupower/utils/idle_monitor/hsw_ext_idle.c @@ -0,0 +1,196 @@ +/* + * (C) 2010,2011 Thomas Renninger , Novell Inc. + * + * Licensed under the terms of the GNU GPL License version 2. + * + * Based on SandyBridge monitor. Implements the new package C-states + * (PC8, PC9, PC10) coming with a specific Haswell (family 0x45) CPU. + */ + +#if defined(__i386__) || defined(__x86_64__) + +#include +#include +#include +#include + +#include "helpers/helpers.h" +#include "idle_monitor/cpupower-monitor.h" + +#define MSR_PKG_C8_RESIDENCY 0x00000630 +#define MSR_PKG_C9_RESIDENCY 0x00000631 +#define MSR_PKG_C10_RESIDENCY 0x00000632 + +#define MSR_TSC 0x10 + +enum intel_hsw_ext_id { PC8 = 0, PC9, PC10, HSW_EXT_CSTATE_COUNT, + TSC = 0xFFFF }; + +static int hsw_ext_get_count_percent(unsigned int self_id, double *percent, + unsigned int cpu); + +static cstate_t hsw_ext_cstates[HSW_EXT_CSTATE_COUNT] = { + { + .name = "PC8", + .desc = N_("Processor Package C8"), + .id = PC8, + .range = RANGE_PACKAGE, + .get_count_percent = hsw_ext_get_count_percent, + }, + { + .name = "PC9", + .desc = N_("Processor Package C9"), + .desc = N_("Processor Package C2"), + .id = PC9, + .range = RANGE_PACKAGE, + .get_count_percent = hsw_ext_get_count_percent, + }, + { + .name = "PC10", + .desc = N_("Processor Package C10"), + .id = PC10, + .range = RANGE_PACKAGE, + .get_count_percent = hsw_ext_get_count_percent, + }, +}; + +static unsigned long long tsc_at_measure_start; +static unsigned long long tsc_at_measure_end; +static unsigned long long *previous_count[HSW_EXT_CSTATE_COUNT]; +static unsigned long long *current_count[HSW_EXT_CSTATE_COUNT]; +/* valid flag for all CPUs. If a MSR read failed it will be zero */ +static int *is_valid; + +static int hsw_ext_get_count(enum intel_hsw_ext_id id, unsigned long long *val, + unsigned int cpu) +{ + int msr; + + switch (id) { + case PC8: + msr = MSR_PKG_C8_RESIDENCY; + break; + case PC9: + msr = MSR_PKG_C9_RESIDENCY; + break; + case PC10: + msr = MSR_PKG_C10_RESIDENCY; + break; + case TSC: + msr = MSR_TSC; + break; + default: + return -1; + }; + if (read_msr(cpu, msr, val)) + return -1; + return 0; +} + +static int hsw_ext_get_count_percent(unsigned int id, double *percent, + unsigned int cpu) +{ + *percent = 0.0; + + if (!is_valid[cpu]) + return -1; + + *percent = (100.0 * + (current_count[id][cpu] - previous_count[id][cpu])) / + (tsc_at_measure_end - tsc_at_measure_start); + + dprint("%s: previous: %llu - current: %llu - (%u)\n", + hsw_ext_cstates[id].name, previous_count[id][cpu], + current_count[id][cpu], cpu); + + dprint("%s: tsc_diff: %llu - count_diff: %llu - percent: %2.f (%u)\n", + hsw_ext_cstates[id].name, + (unsigned long long) tsc_at_measure_end - tsc_at_measure_start, + current_count[id][cpu] - previous_count[id][cpu], + *percent, cpu); + + return 0; +} + +static int hsw_ext_start(void) +{ + int num, cpu; + unsigned long long val; + + for (num = 0; num < HSW_EXT_CSTATE_COUNT; num++) { + for (cpu = 0; cpu < cpu_count; cpu++) { + hsw_ext_get_count(num, &val, cpu); + previous_count[num][cpu] = val; + } + } + hsw_ext_get_count(TSC, &tsc_at_measure_start, 0); + return 0; +} + +static int hsw_ext_stop(void) +{ + unsigned long long val; + int num, cpu; + + hsw_ext_get_count(TSC, &tsc_at_measure_end, 0); + + for (num = 0; num < HSW_EXT_CSTATE_COUNT; num++) { + for (cpu = 0; cpu < cpu_count; cpu++) { + is_valid[cpu] = !hsw_ext_get_count(num, &val, cpu); + current_count[num][cpu] = val; + } + } + return 0; +} + +struct cpuidle_monitor intel_hsw_ext_monitor; + +static struct cpuidle_monitor *hsw_ext_register(void) +{ + int num; + + if (cpupower_cpu_info.vendor != X86_VENDOR_INTEL + || cpupower_cpu_info.family != 6) + return NULL; + + switch (cpupower_cpu_info.model) { + case 0x45: /* HSW */ + break; + default: + return NULL; + } + + is_valid = calloc(cpu_count, sizeof(int)); + for (num = 0; num < HSW_EXT_CSTATE_COUNT; num++) { + previous_count[num] = calloc(cpu_count, + sizeof(unsigned long long)); + current_count[num] = calloc(cpu_count, + sizeof(unsigned long long)); + } + intel_hsw_ext_monitor.name_len = strlen(intel_hsw_ext_monitor.name); + return &intel_hsw_ext_monitor; +} + +void hsw_ext_unregister(void) +{ + int num; + free(is_valid); + for (num = 0; num < HSW_EXT_CSTATE_COUNT; num++) { + free(previous_count[num]); + free(current_count[num]); + } +} + +struct cpuidle_monitor intel_hsw_ext_monitor = { + .name = "HaswellExtended", + .hw_states = hsw_ext_cstates, + .hw_states_num = HSW_EXT_CSTATE_COUNT, + .start = hsw_ext_start, + .stop = hsw_ext_stop, + .do_register = hsw_ext_register, + .unregister = hsw_ext_unregister, + .needs_root = 1, + .overflow_s = 922000000 /* 922337203 seconds TSC overflow + at 20GHz */ +}; +#endif /* defined(__i386__) || defined(__x86_64__) */ diff --git a/tools/power/cpupower/utils/idle_monitor/idle_monitors.def b/tools/power/cpupower/utils/idle_monitor/idle_monitors.def index e3f8d9b2b18f..0d6ba4dbb9c7 100644 --- a/tools/power/cpupower/utils/idle_monitor/idle_monitors.def +++ b/tools/power/cpupower/utils/idle_monitor/idle_monitors.def @@ -2,6 +2,7 @@ DEF(amd_fam14h) DEF(intel_nhm) DEF(intel_snb) +DEF(intel_hsw_ext) DEF(mperf) #endif DEF(cpuidle_sysfs) -- cgit v1.2.3 From 5b8788c1740fae8416e7e045301d99676d20bd64 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Mon, 1 Jul 2013 14:14:38 +1000 Subject: drm/qxl: make dynamic resizing work properly. qxl has a feature to allow the userspace driver do arbitrary resizes when the viewer resizes, this fixes it by removing unnecessary code from the kernel side. Signed-off-by: Dave Airlie --- drivers/gpu/drm/qxl/qxl_display.c | 58 ++------------------------------------- 1 file changed, 3 insertions(+), 55 deletions(-) diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c index 686a937675cb..df5ca7e72e67 100644 --- a/drivers/gpu/drm/qxl/qxl_display.c +++ b/drivers/gpu/drm/qxl/qxl_display.c @@ -30,55 +30,6 @@ #include "qxl_object.h" #include "drm_crtc_helper.h" -static void qxl_crtc_set_to_mode(struct qxl_device *qdev, - struct drm_connector *connector, - struct qxl_head *head) -{ - struct drm_device *dev = connector->dev; - struct drm_display_mode *mode, *t; - int width = head->width; - int height = head->height; - - if (width < 320 || height < 240) { - qxl_io_log(qdev, "%s: bad head: %dx%d", width, height); - width = 1024; - height = 768; - } - if (width * height * 4 > 16*1024*1024) { - width = 1024; - height = 768; - } - /* TODO: go over regular modes and removed preferred? */ - list_for_each_entry_safe(mode, t, &connector->probed_modes, head) - drm_mode_remove(connector, mode); - mode = drm_cvt_mode(dev, width, height, 60, false, false, false); - mode->type |= DRM_MODE_TYPE_PREFERRED; - mode->status = MODE_OK; - drm_mode_probed_add(connector, mode); - qxl_io_log(qdev, "%s: %d x %d\n", __func__, width, height); -} - -void qxl_crtc_set_from_monitors_config(struct qxl_device *qdev) -{ - struct drm_connector *connector; - int i; - struct drm_device *dev = qdev->ddev; - - i = 0; - qxl_io_log(qdev, "%s: %d, %d\n", __func__, - dev->mode_config.num_connector, - qdev->monitors_config->count); - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - if (i > qdev->monitors_config->count) { - /* crtc will be reported as disabled */ - continue; - } - qxl_crtc_set_to_mode(qdev, connector, - &qdev->monitors_config->heads[i]); - ++i; - } -} - void qxl_alloc_client_monitors_config(struct qxl_device *qdev, unsigned count) { if (qdev->client_monitors_config && @@ -117,8 +68,8 @@ static int qxl_display_copy_rom_client_monitors_config(struct qxl_device *qdev) return 1; } if (num_monitors > qdev->monitors_config->max_allowed) { - DRM_INFO("client monitors list will be truncated: %d < %d\n", - qdev->monitors_config->max_allowed, num_monitors); + DRM_DEBUG_KMS("client monitors list will be truncated: %d < %d\n", + qdev->monitors_config->max_allowed, num_monitors); num_monitors = qdev->monitors_config->max_allowed; } else { num_monitors = qdev->rom->client_monitors_config.count; @@ -142,7 +93,7 @@ static int qxl_display_copy_rom_client_monitors_config(struct qxl_device *qdev) client_head->surface_id = head->surface_id = 0; client_head->id = head->id = i; client_head->flags = head->flags = 0; - QXL_DEBUG(qdev, "read %dx%d+%d+%d\n", head->width, head->height, + DRM_DEBUG_KMS("read %dx%d+%d+%d\n", head->width, head->height, head->x, head->y); } return 0; @@ -155,9 +106,6 @@ void qxl_display_read_client_monitors_config(struct qxl_device *qdev) qxl_io_log(qdev, "failed crc check for client_monitors_config," " retrying\n"); } - qxl_crtc_set_from_monitors_config(qdev); - /* fire off a uevent and let userspace tell us what to do */ - qxl_io_log(qdev, "calling drm_sysfs_hotplug_event\n"); drm_sysfs_hotplug_event(qdev->ddev); } -- cgit v1.2.3 From 07f8d9bdb235836d0a255d20f387bc3afa99180f Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Tue, 2 Jul 2013 06:37:13 +0100 Subject: drm/qxl: add support for > 1 output This adds support for a default of 4 heads, with a command line parameter to change the default number. It also overhauls the modesetting code to handle this case properly, and send the correct things to the hardware at the right time. Signed-off-by: Dave Airlie --- drivers/gpu/drm/qxl/qxl_cmd.c | 8 +-- drivers/gpu/drm/qxl/qxl_display.c | 121 +++++++++++++++++++++++--------------- drivers/gpu/drm/qxl/qxl_drv.c | 4 ++ drivers/gpu/drm/qxl/qxl_drv.h | 8 +-- drivers/gpu/drm/qxl/qxl_fb.c | 2 +- 5 files changed, 85 insertions(+), 58 deletions(-) diff --git a/drivers/gpu/drm/qxl/qxl_cmd.c b/drivers/gpu/drm/qxl/qxl_cmd.c index f86771481317..92cf1afd60e5 100644 --- a/drivers/gpu/drm/qxl/qxl_cmd.c +++ b/drivers/gpu/drm/qxl/qxl_cmd.c @@ -375,8 +375,8 @@ void qxl_io_destroy_primary(struct qxl_device *qdev) wait_for_io_cmd(qdev, 0, QXL_IO_DESTROY_PRIMARY_ASYNC); } -void qxl_io_create_primary(struct qxl_device *qdev, unsigned width, - unsigned height, unsigned offset, struct qxl_bo *bo) +void qxl_io_create_primary(struct qxl_device *qdev, + unsigned offset, struct qxl_bo *bo) { struct qxl_surface_create *create; @@ -384,8 +384,8 @@ void qxl_io_create_primary(struct qxl_device *qdev, unsigned width, qdev->ram_header); create = &qdev->ram_header->create_surface; create->format = bo->surf.format; - create->width = width; - create->height = height; + create->width = bo->surf.width; + create->height = bo->surf.height; create->stride = bo->surf.stride; create->mem = qxl_bo_physical_address(qdev, bo, offset); diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c index df5ca7e72e67..d3b92618246e 100644 --- a/drivers/gpu/drm/qxl/qxl_display.c +++ b/drivers/gpu/drm/qxl/qxl_display.c @@ -30,6 +30,11 @@ #include "qxl_object.h" #include "drm_crtc_helper.h" +static bool qxl_head_enabled(struct qxl_head *head) +{ + return head->width && head->height; +} + void qxl_alloc_client_monitors_config(struct qxl_device *qdev, unsigned count) { if (qdev->client_monitors_config && @@ -57,7 +62,6 @@ static int qxl_display_copy_rom_client_monitors_config(struct qxl_device *qdev) int num_monitors; uint32_t crc; - BUG_ON(!qdev->monitors_config); num_monitors = qdev->rom->client_monitors_config.count; crc = crc32(0, (const uint8_t *)&qdev->rom->client_monitors_config, sizeof(qdev->rom->client_monitors_config)); @@ -83,18 +87,15 @@ static int qxl_display_copy_rom_client_monitors_config(struct qxl_device *qdev) &qdev->rom->client_monitors_config.heads[i]; struct qxl_head *client_head = &qdev->client_monitors_config->heads[i]; - struct qxl_head *head = &qdev->monitors_config->heads[i]; - client_head->x = head->x = c_rect->left; - client_head->y = head->y = c_rect->top; - client_head->width = head->width = - c_rect->right - c_rect->left; - client_head->height = head->height = - c_rect->bottom - c_rect->top; - client_head->surface_id = head->surface_id = 0; - client_head->id = head->id = i; - client_head->flags = head->flags = 0; - DRM_DEBUG_KMS("read %dx%d+%d+%d\n", head->width, head->height, - head->x, head->y); + client_head->x = c_rect->left; + client_head->y = c_rect->top; + client_head->width = c_rect->right - c_rect->left; + client_head->height = c_rect->bottom - c_rect->top; + client_head->surface_id = 0; + client_head->id = i; + client_head->flags = 0; + DRM_DEBUG_KMS("read %dx%d+%d+%d\n", client_head->width, client_head->height, + client_head->x, client_head->y); } return 0; } @@ -118,9 +119,9 @@ static int qxl_add_monitors_config_modes(struct drm_connector *connector) struct drm_display_mode *mode = NULL; struct qxl_head *head; - if (!qdev->monitors_config) + if (!qdev->client_monitors_config) return 0; - head = &qdev->monitors_config->heads[h]; + head = &qdev->client_monitors_config->heads[h]; mode = drm_cvt_mode(dev, head->width, head->height, 60, false, false, false); @@ -447,7 +448,7 @@ qxl_send_monitors_config(struct qxl_device *qdev) for (i = 0 ; i < qdev->monitors_config->count ; ++i) { struct qxl_head *head = &qdev->monitors_config->heads[i]; - if (head->y > 8192 || head->y < head->x || + if (head->y > 8192 || head->x > 8192 || head->width > 8192 || head->height > 8192) { DRM_ERROR("head %d wrong: %dx%d+%d+%d\n", i, head->width, head->height, @@ -458,16 +459,19 @@ qxl_send_monitors_config(struct qxl_device *qdev) qxl_io_monitors_config(qdev); } -static void qxl_monitors_config_set_single(struct qxl_device *qdev, - unsigned x, unsigned y, - unsigned width, unsigned height) +static void qxl_monitors_config_set(struct qxl_device *qdev, + int index, + unsigned x, unsigned y, + unsigned width, unsigned height, + unsigned surf_id) { - DRM_DEBUG("%dx%d+%d+%d\n", width, height, x, y); - qdev->monitors_config->count = 1; - qdev->monitors_config->heads[0].x = x; - qdev->monitors_config->heads[0].y = y; - qdev->monitors_config->heads[0].width = width; - qdev->monitors_config->heads[0].height = height; + DRM_DEBUG_KMS("%d:%dx%d+%d+%d\n", index, width, height, x, y); + qdev->monitors_config->heads[index].x = x; + qdev->monitors_config->heads[index].y = y; + qdev->monitors_config->heads[index].width = width; + qdev->monitors_config->heads[index].height = height; + qdev->monitors_config->heads[index].surface_id = surf_id; + } static int qxl_crtc_mode_set(struct drm_crtc *crtc, @@ -481,10 +485,11 @@ static int qxl_crtc_mode_set(struct drm_crtc *crtc, struct qxl_mode *m = (void *)mode->private; struct qxl_framebuffer *qfb; struct qxl_bo *bo, *old_bo = NULL; + struct qxl_crtc *qcrtc = to_qxl_crtc(crtc); uint32_t width, height, base_offset; bool recreate_primary = false; int ret; - + int surf_id; if (!crtc->fb) { DRM_DEBUG_KMS("No FB bound\n"); return 0; @@ -508,7 +513,8 @@ static int qxl_crtc_mode_set(struct drm_crtc *crtc, adjusted_mode->hdisplay, adjusted_mode->vdisplay); - recreate_primary = true; + if (qcrtc->index == 0) + recreate_primary = true; width = mode->hdisplay; height = mode->vdisplay; @@ -529,8 +535,11 @@ static int qxl_crtc_mode_set(struct drm_crtc *crtc, "recreate primary: %dx%d (was %dx%d,%d,%d)\n", width, height, bo->surf.width, bo->surf.height, bo->surf.stride, bo->surf.format); - qxl_io_create_primary(qdev, width, height, base_offset, bo); + qxl_io_create_primary(qdev, base_offset, bo); bo->is_primary = true; + surf_id = 0; + } else { + surf_id = bo->surface_id; } if (old_bo && old_bo != bo) { @@ -540,11 +549,9 @@ static int qxl_crtc_mode_set(struct drm_crtc *crtc, qxl_bo_unreserve(old_bo); } - if (qdev->monitors_config->count == 0) { - qxl_monitors_config_set_single(qdev, x, y, - mode->hdisplay, - mode->vdisplay); - } + qxl_monitors_config_set(qdev, qcrtc->index, x, y, + mode->hdisplay, + mode->vdisplay, surf_id); return 0; } @@ -560,15 +567,36 @@ static void qxl_crtc_commit(struct drm_crtc *crtc) DRM_DEBUG("\n"); } +static void qxl_crtc_disable(struct drm_crtc *crtc) +{ + struct qxl_crtc *qcrtc = to_qxl_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct qxl_device *qdev = dev->dev_private; + if (crtc->fb) { + struct qxl_framebuffer *qfb = to_qxl_framebuffer(crtc->fb); + struct qxl_bo *bo = gem_to_qxl_bo(qfb->obj); + int ret; + ret = qxl_bo_reserve(bo, false); + qxl_bo_unpin(bo); + qxl_bo_unreserve(bo); + crtc->fb = NULL; + } + + qxl_monitors_config_set(qdev, qcrtc->index, 0, 0, 0, 0, 0); + + qxl_send_monitors_config(qdev); +} + static const struct drm_crtc_helper_funcs qxl_crtc_helper_funcs = { .dpms = qxl_crtc_dpms, + .disable = qxl_crtc_disable, .mode_fixup = qxl_crtc_mode_fixup, .mode_set = qxl_crtc_mode_set, .prepare = qxl_crtc_prepare, .commit = qxl_crtc_commit, }; -static int qdev_crtc_init(struct drm_device *dev, int num_crtc) +static int qdev_crtc_init(struct drm_device *dev, int crtc_id) { struct qxl_crtc *qxl_crtc; @@ -577,7 +605,7 @@ static int qdev_crtc_init(struct drm_device *dev, int num_crtc) return -ENOMEM; drm_crtc_init(dev, &qxl_crtc->base, &qxl_crtc_funcs); - + qxl_crtc->index = crtc_id; drm_mode_crtc_set_gamma_size(&qxl_crtc->base, 256); drm_crtc_helper_add(&qxl_crtc->base, &qxl_crtc_helper_funcs); return 0; @@ -605,18 +633,13 @@ static void qxl_write_monitors_config_for_encoder(struct qxl_device *qdev, struct drm_encoder *encoder) { int i; + struct qxl_output *output = drm_encoder_to_qxl_output(encoder); struct qxl_head *head; struct drm_display_mode *mode; BUG_ON(!encoder); /* TODO: ugly, do better */ - for (i = 0 ; (encoder->possible_crtcs != (1 << i)) && i < 32; ++i) - ; - if (encoder->possible_crtcs != (1 << i)) { - DRM_ERROR("encoder has wrong possible_crtcs: %x\n", - encoder->possible_crtcs); - return; - } + i = output->index; if (!qdev->monitors_config || qdev->monitors_config->max_allowed <= i) { DRM_ERROR( @@ -634,7 +657,6 @@ static void qxl_write_monitors_config_for_encoder(struct qxl_device *qdev, DRM_DEBUG("missing for multiple monitors: no head holes\n"); head = &qdev->monitors_config->heads[i]; head->id = i; - head->surface_id = 0; if (encoder->crtc->enabled) { mode = &encoder->crtc->mode; head->width = mode->hdisplay; @@ -649,8 +671,8 @@ static void qxl_write_monitors_config_for_encoder(struct qxl_device *qdev, head->x = 0; head->y = 0; } - DRM_DEBUG("setting head %d to +%d+%d %dx%d\n", - i, head->x, head->y, head->width, head->height); + DRM_DEBUG_KMS("setting head %d to +%d+%d %dx%d out of %d\n", + i, head->x, head->y, head->width, head->height, qdev->monitors_config->count); head->flags = 0; /* TODO - somewhere else to call this for multiple monitors * (config_commit?) */ @@ -745,8 +767,9 @@ static enum drm_connector_status qxl_conn_detect( /* The first monitor is always connected */ connected = (output->index == 0) || - (qdev->monitors_config && - qdev->monitors_config->count > output->index); + (qdev->client_monitors_config && + qdev->client_monitors_config->count > output->index && + qxl_head_enabled(&qdev->client_monitors_config->heads[output->index])); DRM_DEBUG("\n"); return connected ? connector_status_connected @@ -854,7 +877,7 @@ int qxl_modeset_init(struct qxl_device *qdev) int i; int ret; struct drm_gem_object *gobj; - int max_allowed = QXL_NUM_OUTPUTS; + int max_allowed = qxl_num_crtc; int monitors_config_size = sizeof(struct qxl_monitors_config) + max_allowed * sizeof(struct qxl_head); @@ -884,7 +907,7 @@ int qxl_modeset_init(struct qxl_device *qdev) qdev->ddev->mode_config.max_height = 8192; qdev->ddev->mode_config.fb_base = qdev->vram_base; - for (i = 0 ; i < QXL_NUM_OUTPUTS; ++i) { + for (i = 0 ; i < qxl_num_crtc; ++i) { qdev_crtc_init(qdev->ddev, i); qdev_output_init(qdev->ddev, i); } diff --git a/drivers/gpu/drm/qxl/qxl_drv.c b/drivers/gpu/drm/qxl/qxl_drv.c index aa291d8a98a2..00e57b76f4f5 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.c +++ b/drivers/gpu/drm/qxl/qxl_drv.c @@ -47,10 +47,14 @@ static DEFINE_PCI_DEVICE_TABLE(pciidlist) = { MODULE_DEVICE_TABLE(pci, pciidlist); static int qxl_modeset = -1; +int qxl_num_crtc = 4; MODULE_PARM_DESC(modeset, "Disable/Enable modesetting"); module_param_named(modeset, qxl_modeset, int, 0400); +MODULE_PARM_DESC(num_heads, "Number of virtual crtcs to expose (default 4)"); +module_param_named(num_heads, qxl_num_crtc, int, 0400); + static struct drm_driver qxl_driver; static struct pci_driver qxl_pci_driver; diff --git a/drivers/gpu/drm/qxl/qxl_drv.h b/drivers/gpu/drm/qxl/qxl_drv.h index 43d06ab28a21..42ef0e2b5094 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.h +++ b/drivers/gpu/drm/qxl/qxl_drv.h @@ -55,11 +55,10 @@ #define DRIVER_MINOR 1 #define DRIVER_PATCHLEVEL 0 -#define QXL_NUM_OUTPUTS 1 - #define QXL_DEBUGFS_MAX_COMPONENTS 32 extern int qxl_log_level; +extern int qxl_num_crtc; enum { QXL_INFO_LEVEL = 1, @@ -139,6 +138,7 @@ struct qxl_reloc_list { struct qxl_crtc { struct drm_crtc base; + int index; int cur_x; int cur_y; }; @@ -156,7 +156,7 @@ struct qxl_framebuffer { #define to_qxl_crtc(x) container_of(x, struct qxl_crtc, base) #define drm_connector_to_qxl_output(x) container_of(x, struct qxl_output, base) -#define drm_encoder_to_qxl_output(x) container_of(x, struct qxl_output, base) +#define drm_encoder_to_qxl_output(x) container_of(x, struct qxl_output, enc) #define to_qxl_framebuffer(x) container_of(x, struct qxl_framebuffer, base) struct qxl_mman { @@ -435,7 +435,7 @@ void qxl_update_screen(struct qxl_device *qxl); /* qxl io operations (qxl_cmd.c) */ void qxl_io_create_primary(struct qxl_device *qdev, - unsigned width, unsigned height, unsigned offset, + unsigned offset, struct qxl_bo *bo); void qxl_io_destroy_primary(struct qxl_device *qdev); void qxl_io_memslot_add(struct qxl_device *qdev, uint8_t id); diff --git a/drivers/gpu/drm/qxl/qxl_fb.c b/drivers/gpu/drm/qxl/qxl_fb.c index 4b955b04ce1e..c08e12886d6c 100644 --- a/drivers/gpu/drm/qxl/qxl_fb.c +++ b/drivers/gpu/drm/qxl/qxl_fb.c @@ -538,7 +538,7 @@ int qxl_fbdev_init(struct qxl_device *qdev) qfbdev->helper.funcs = &qxl_fb_helper_funcs; ret = drm_fb_helper_init(qdev->ddev, &qfbdev->helper, - 1 /* num_crtc - QXL supports just 1 */, + qxl_num_crtc /* num_crtc - QXL supports just 1 */, QXLFB_CONN_LIMIT); if (ret) { kfree(qfbdev); -- cgit v1.2.3 From c927215543eb8f67c2c0102db147c299189c9957 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Tue, 2 Jul 2013 10:44:50 +0100 Subject: drm/qxl: set time on drawables from userspace This just sets the qxl time on the drawables. Signed-off-by: Dave Airlie --- drivers/gpu/drm/qxl/qxl_ioctl.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/qxl/qxl_ioctl.c b/drivers/gpu/drm/qxl/qxl_ioctl.c index a4b71b25fa53..6ba49d9922f2 100644 --- a/drivers/gpu/drm/qxl/qxl_ioctl.c +++ b/drivers/gpu/drm/qxl/qxl_ioctl.c @@ -183,6 +183,12 @@ static int qxl_execbuffer_ioctl(struct drm_device *dev, void *data, /* TODO copy slow path code from i915 */ fb_cmd = qxl_bo_kmap_atomic_page(qdev, cmd_bo, (release->release_offset & PAGE_SIZE)); unwritten = __copy_from_user_inatomic_nocache(fb_cmd + sizeof(union qxl_release_info) + (release->release_offset & ~PAGE_SIZE), (void *)(unsigned long)user_cmd.command, user_cmd.command_size); + + { + struct qxl_drawable *draw = fb_cmd; + + draw->mm_time = qdev->rom->mm_clock; + } qxl_bo_kunmap_atomic_page(qdev, cmd_bo, fb_cmd); if (unwritten) { DRM_ERROR("got unwritten %d\n", unwritten); -- cgit v1.2.3 From 2bd6ce84e1b4799be1f328a165d0b8a4fdfd2141 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 4 Jul 2013 14:46:46 +1000 Subject: qxl: split monitors_config object creation out. This splits the creation of the monitors config object out so we can re-use it across suspend/resume later. Signed-off-by: Dave Airlie --- drivers/gpu/drm/qxl/qxl_display.c | 54 ++++++++++++++++++++++++++++++++++++--- drivers/gpu/drm/qxl/qxl_drv.h | 2 ++ 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c index d3b92618246e..61714fdfc782 100644 --- a/drivers/gpu/drm/qxl/qxl_display.c +++ b/drivers/gpu/drm/qxl/qxl_display.c @@ -872,16 +872,14 @@ static const struct drm_mode_config_funcs qxl_mode_funcs = { .fb_create = qxl_user_framebuffer_create, }; -int qxl_modeset_init(struct qxl_device *qdev) +int qxl_create_monitors_object(struct qxl_device *qdev) { - int i; int ret; struct drm_gem_object *gobj; int max_allowed = qxl_num_crtc; int monitors_config_size = sizeof(struct qxl_monitors_config) + - max_allowed * sizeof(struct qxl_head); + max_allowed * sizeof(struct qxl_head); - drm_mode_config_init(qdev->ddev); ret = qxl_gem_object_create(qdev, monitors_config_size, 0, QXL_GEM_DOMAIN_VRAM, false, false, NULL, &gobj); @@ -890,13 +888,59 @@ int qxl_modeset_init(struct qxl_device *qdev) return -ENOMEM; } qdev->monitors_config_bo = gem_to_qxl_bo(gobj); + + ret = qxl_bo_reserve(qdev->monitors_config_bo, false); + if (ret) + return ret; + + ret = qxl_bo_pin(qdev->monitors_config_bo, QXL_GEM_DOMAIN_VRAM, NULL); + if (ret) { + qxl_bo_unreserve(qdev->monitors_config_bo); + return ret; + } + + qxl_bo_unreserve(qdev->monitors_config_bo); + qxl_bo_kmap(qdev->monitors_config_bo, NULL); + qdev->monitors_config = qdev->monitors_config_bo->kptr; qdev->ram_header->monitors_config = qxl_bo_physical_address(qdev, qdev->monitors_config_bo, 0); memset(qdev->monitors_config, 0, monitors_config_size); qdev->monitors_config->max_allowed = max_allowed; + return 0; +} + +int qxl_destroy_monitors_object(struct qxl_device *qdev) +{ + int ret; + + qdev->monitors_config = NULL; + qdev->ram_header->monitors_config = 0; + + qxl_bo_kunmap(qdev->monitors_config_bo); + ret = qxl_bo_reserve(qdev->monitors_config_bo, false); + if (ret) + return ret; + + qxl_bo_unpin(qdev->monitors_config_bo); + qxl_bo_unreserve(qdev->monitors_config_bo); + + qxl_bo_unref(&qdev->monitors_config_bo); + return 0; +} + +int qxl_modeset_init(struct qxl_device *qdev) +{ + int i; + int ret; + + drm_mode_config_init(qdev->ddev); + + ret = qxl_create_monitors_object(qdev); + if (ret) + return ret; qdev->ddev->mode_config.funcs = (void *)&qxl_mode_funcs; @@ -924,6 +968,8 @@ int qxl_modeset_init(struct qxl_device *qdev) void qxl_modeset_fini(struct qxl_device *qdev) { qxl_fbdev_fini(qdev); + + qxl_destroy_monitors_object(qdev); if (qdev->mode_info.mode_config_initialized) { drm_mode_config_cleanup(qdev->ddev); qdev->mode_info.mode_config_initialized = false; diff --git a/drivers/gpu/drm/qxl/qxl_drv.h b/drivers/gpu/drm/qxl/qxl_drv.h index 42ef0e2b5094..6a929258fff9 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.h +++ b/drivers/gpu/drm/qxl/qxl_drv.h @@ -374,6 +374,8 @@ qxl_framebuffer_init(struct drm_device *dev, struct drm_gem_object *obj); void qxl_display_read_client_monitors_config(struct qxl_device *qdev); void qxl_send_monitors_config(struct qxl_device *qdev); +int qxl_create_monitors_object(struct qxl_device *qdev); +int qxl_destroy_monitors_object(struct qxl_device *qdev); /* used by qxl_debugfs only */ void qxl_crtc_set_from_monitors_config(struct qxl_device *qdev); -- cgit v1.2.3 From c9fdda2a2b7a8875db3eebd89e428aa760afb897 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 4 Jul 2013 14:57:58 +1000 Subject: qxl: prepare memslot code for suspend/resume this splits out initing the hw memslots from the guest info, and creates an entrypoint for s/r to use. Signed-off-by: Dave Airlie --- drivers/gpu/drm/qxl/qxl_drv.h | 2 ++ drivers/gpu/drm/qxl/qxl_kms.c | 21 +++++++++++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/qxl/qxl_drv.h b/drivers/gpu/drm/qxl/qxl_drv.h index 6a929258fff9..056330e5fecf 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.h +++ b/drivers/gpu/drm/qxl/qxl_drv.h @@ -331,6 +331,8 @@ void qxl_modeset_fini(struct qxl_device *qdev); int qxl_bo_init(struct qxl_device *qdev); void qxl_bo_fini(struct qxl_device *qdev); +void qxl_reinit_memslots(struct qxl_device *qdev); + struct qxl_ring *qxl_ring_create(struct qxl_ring_header *header, int element_size, int n_elements, diff --git a/drivers/gpu/drm/qxl/qxl_kms.c b/drivers/gpu/drm/qxl/qxl_kms.c index e27ce2a907cf..2c6f9219e9d9 100644 --- a/drivers/gpu/drm/qxl/qxl_kms.c +++ b/drivers/gpu/drm/qxl/qxl_kms.c @@ -72,21 +72,28 @@ static bool qxl_check_device(struct qxl_device *qdev) return true; } +static void setup_hw_slot(struct qxl_device *qdev, int slot_index, + struct qxl_memslot *slot) +{ + qdev->ram_header->mem_slot.mem_start = slot->start_phys_addr; + qdev->ram_header->mem_slot.mem_end = slot->end_phys_addr; + qxl_io_memslot_add(qdev, slot_index); +} + static uint8_t setup_slot(struct qxl_device *qdev, uint8_t slot_index_offset, unsigned long start_phys_addr, unsigned long end_phys_addr) { uint64_t high_bits; struct qxl_memslot *slot; uint8_t slot_index; - struct qxl_ram_header *ram_header = qdev->ram_header; slot_index = qdev->rom->slots_start + slot_index_offset; slot = &qdev->mem_slots[slot_index]; slot->start_phys_addr = start_phys_addr; slot->end_phys_addr = end_phys_addr; - ram_header->mem_slot.mem_start = slot->start_phys_addr; - ram_header->mem_slot.mem_end = slot->end_phys_addr; - qxl_io_memslot_add(qdev, slot_index); + + setup_hw_slot(qdev, slot_index, slot); + slot->generation = qdev->rom->slot_generation; high_bits = slot_index << qdev->slot_gen_bits; high_bits |= slot->generation; @@ -95,6 +102,12 @@ static uint8_t setup_slot(struct qxl_device *qdev, uint8_t slot_index_offset, return slot_index; } +void qxl_reinit_memslots(struct qxl_device *qdev) +{ + setup_hw_slot(qdev, qdev->main_mem_slot, &qdev->mem_slots[qdev->main_mem_slot]); + setup_hw_slot(qdev, qdev->surfaces_mem_slot, &qdev->mem_slots[qdev->surfaces_mem_slot]); +} + static void qxl_gc_work(struct work_struct *work) { struct qxl_device *qdev = container_of(work, struct qxl_device, gc_work); -- cgit v1.2.3 From 1e209117dbe00d3d87db1c5266f177eaa60451c8 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 4 Jul 2013 14:58:45 +1000 Subject: qxl: add ring prep code for s/r This prepare the ring code for s/r additions, the release ring will need reinitialising. Signed-off-by: Dave Airlie --- drivers/gpu/drm/qxl/qxl_cmd.c | 9 +++++++-- drivers/gpu/drm/qxl/qxl_drv.h | 2 ++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/qxl/qxl_cmd.c b/drivers/gpu/drm/qxl/qxl_cmd.c index 92cf1afd60e5..93c2f2cceb51 100644 --- a/drivers/gpu/drm/qxl/qxl_cmd.c +++ b/drivers/gpu/drm/qxl/qxl_cmd.c @@ -49,6 +49,11 @@ void qxl_ring_free(struct qxl_ring *ring) kfree(ring); } +void qxl_ring_init_hdr(struct qxl_ring *ring) +{ + ring->ring->header.notify_on_prod = ring->n_elements; +} + struct qxl_ring * qxl_ring_create(struct qxl_ring_header *header, int element_size, @@ -69,7 +74,7 @@ qxl_ring_create(struct qxl_ring_header *header, ring->prod_notify = prod_notify; ring->push_event = push_event; if (set_prod_notify) - header->notify_on_prod = ring->n_elements; + qxl_ring_init_hdr(ring); spin_lock_init(&ring->lock); return ring; } @@ -87,7 +92,7 @@ static int qxl_check_header(struct qxl_ring *ring) return ret; } -static int qxl_check_idle(struct qxl_ring *ring) +int qxl_check_idle(struct qxl_ring *ring) { int ret; struct qxl_ring_header *header = &(ring->ring->header); diff --git a/drivers/gpu/drm/qxl/qxl_drv.h b/drivers/gpu/drm/qxl/qxl_drv.h index 056330e5fecf..aec9f1f9c814 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.h +++ b/drivers/gpu/drm/qxl/qxl_drv.h @@ -340,6 +340,8 @@ struct qxl_ring *qxl_ring_create(struct qxl_ring_header *header, bool set_prod_notify, wait_queue_head_t *push_event); void qxl_ring_free(struct qxl_ring *ring); +void qxl_ring_init_hdr(struct qxl_ring *ring); +int qxl_check_idle(struct qxl_ring *ring); static inline void * qxl_fb_virtual_address(struct qxl_device *qdev, unsigned long physical) -- cgit v1.2.3 From b86487a6b671ff7107fbf6d3ff10c2da970cd1c3 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 4 Jul 2013 14:59:34 +1000 Subject: qxl: add fb and ttm entry points for use by suspend/resume. This just ports some APIs like radeon uses to provide hooks for s/r to call. Signed-off-by: Dave Airlie --- drivers/gpu/drm/qxl/qxl_drv.h | 3 +++ drivers/gpu/drm/qxl/qxl_fb.c | 10 ++++++++++ drivers/gpu/drm/qxl/qxl_object.c | 5 +++++ 3 files changed, 18 insertions(+) diff --git a/drivers/gpu/drm/qxl/qxl_drv.h b/drivers/gpu/drm/qxl/qxl_drv.h index aec9f1f9c814..70a67862673a 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.h +++ b/drivers/gpu/drm/qxl/qxl_drv.h @@ -332,6 +332,7 @@ int qxl_bo_init(struct qxl_device *qdev); void qxl_bo_fini(struct qxl_device *qdev); void qxl_reinit_memslots(struct qxl_device *qdev); +int qxl_surf_evict(struct qxl_device *qdev); struct qxl_ring *qxl_ring_create(struct qxl_ring_header *header, int element_size, @@ -369,6 +370,7 @@ void qxl_fbdev_fini(struct qxl_device *qdev); int qxl_get_handle_for_primary_fb(struct qxl_device *qdev, struct drm_file *file_priv, uint32_t *handle); +void qxl_fbdev_set_suspend(struct qxl_device *qdev, int state); /* qxl_display.c */ int @@ -534,6 +536,7 @@ irqreturn_t qxl_irq_handler(DRM_IRQ_ARGS); /* qxl_fb.c */ int qxl_fb_init(struct qxl_device *qdev); +bool qxl_fbdev_qobj_is_fb(struct qxl_device *qdev, struct qxl_bo *qobj); int qxl_debugfs_add_files(struct qxl_device *qdev, struct drm_info_list *files, diff --git a/drivers/gpu/drm/qxl/qxl_fb.c b/drivers/gpu/drm/qxl/qxl_fb.c index c08e12886d6c..76f39d88d684 100644 --- a/drivers/gpu/drm/qxl/qxl_fb.c +++ b/drivers/gpu/drm/qxl/qxl_fb.c @@ -560,4 +560,14 @@ void qxl_fbdev_fini(struct qxl_device *qdev) qdev->mode_info.qfbdev = NULL; } +void qxl_fbdev_set_suspend(struct qxl_device *qdev, int state) +{ + fb_set_suspend(qdev->mode_info.qfbdev->helper.fbdev, state); +} +bool qxl_fbdev_qobj_is_fb(struct qxl_device *qdev, struct qxl_bo *qobj) +{ + if (qobj == gem_to_qxl_bo(qdev->mode_info.qfbdev->qfb.obj)) + return true; + return false; +} diff --git a/drivers/gpu/drm/qxl/qxl_object.c b/drivers/gpu/drm/qxl/qxl_object.c index d9b12e7bc6e1..62a046e4a036 100644 --- a/drivers/gpu/drm/qxl/qxl_object.c +++ b/drivers/gpu/drm/qxl/qxl_object.c @@ -363,3 +363,8 @@ int qxl_bo_list_add(struct qxl_reloc_list *reloc_list, struct qxl_bo *bo) return ret; return 0; } + +int qxl_surf_evict(struct qxl_device *qdev) +{ + return ttm_bo_evict_mm(&qdev->mman.bdev, TTM_PL_PRIV0); +} -- cgit v1.2.3 From d84300bf793471cc20c7553601c45d6f70dd2b1e Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 4 Jul 2013 15:02:33 +1000 Subject: qxl: add suspend/resume/hibernate support. This adds suspend/resume and hibernate support for the KMS driver. it evicts all the objects, turns off the outputs, and waits for the hw to go idle, On resume, it resets the memslots, rings, monitors object and forces modeset. Signed-off-by: Dave Airlie --- drivers/gpu/drm/qxl/qxl_drv.c | 134 ++++++++++++++++++++++++++++++++++++--- drivers/gpu/drm/qxl/qxl_drv.h | 1 + drivers/gpu/drm/qxl/qxl_object.c | 5 ++ 3 files changed, 132 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/qxl/qxl_drv.c b/drivers/gpu/drm/qxl/qxl_drv.c index 00e57b76f4f5..df0b577a6608 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.c +++ b/drivers/gpu/drm/qxl/qxl_drv.c @@ -33,8 +33,9 @@ #include "drmP.h" #include "drm/drm.h" - +#include "drm_crtc_helper.h" #include "qxl_drv.h" +#include "qxl_object.h" extern int qxl_max_ioctls; static DEFINE_PCI_DEVICE_TABLE(pciidlist) = { @@ -77,13 +78,6 @@ qxl_pci_remove(struct pci_dev *pdev) drm_put_dev(dev); } -static struct pci_driver qxl_pci_driver = { - .name = DRIVER_NAME, - .id_table = pciidlist, - .probe = qxl_pci_probe, - .remove = qxl_pci_remove, -}; - static const struct file_operations qxl_fops = { .owner = THIS_MODULE, .open = drm_open, @@ -94,6 +88,130 @@ static const struct file_operations qxl_fops = { .mmap = qxl_mmap, }; +static int qxl_drm_freeze(struct drm_device *dev) +{ + struct pci_dev *pdev = dev->pdev; + struct qxl_device *qdev = dev->dev_private; + struct drm_crtc *crtc; + + drm_kms_helper_poll_disable(dev); + + console_lock(); + qxl_fbdev_set_suspend(qdev, 1); + console_unlock(); + + /* unpin the front buffers */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + if (crtc->enabled) + (*crtc_funcs->disable)(crtc); + } + + qxl_destroy_monitors_object(qdev); + qxl_surf_evict(qdev); + qxl_vram_evict(qdev); + + while (!qxl_check_idle(qdev->command_ring)); + while (!qxl_check_idle(qdev->release_ring)) + qxl_queue_garbage_collect(qdev, 1); + + pci_save_state(pdev); + + return 0; +} + +static int qxl_drm_resume(struct drm_device *dev, bool thaw) +{ + struct qxl_device *qdev = dev->dev_private; + + qdev->ram_header->int_mask = QXL_INTERRUPT_MASK; + if (!thaw) { + qxl_reinit_memslots(qdev); + qxl_ring_init_hdr(qdev->release_ring); + } + + qxl_create_monitors_object(qdev); + drm_helper_resume_force_mode(dev); + + console_lock(); + qxl_fbdev_set_suspend(qdev, 0); + console_unlock(); + + drm_kms_helper_poll_enable(dev); + return 0; +} + +static int qxl_pm_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct drm_device *drm_dev = pci_get_drvdata(pdev); + int error; + + error = qxl_drm_freeze(drm_dev); + if (error) + return error; + + pci_disable_device(pdev); + pci_set_power_state(pdev, PCI_D3hot); + return 0; +} + +static int qxl_pm_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct drm_device *drm_dev = pci_get_drvdata(pdev); + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + if (pci_enable_device(pdev)) { + return -EIO; + } + + return qxl_drm_resume(drm_dev, false); +} + +static int qxl_pm_thaw(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct drm_device *drm_dev = pci_get_drvdata(pdev); + + return qxl_drm_resume(drm_dev, true); +} + +static int qxl_pm_freeze(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct drm_device *drm_dev = pci_get_drvdata(pdev); + + return qxl_drm_freeze(drm_dev); +} + +static int qxl_pm_restore(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct drm_device *drm_dev = pci_get_drvdata(pdev); + struct qxl_device *qdev = drm_dev->dev_private; + + qxl_io_reset(qdev); + return qxl_drm_resume(drm_dev, false); +} + +static const struct dev_pm_ops qxl_pm_ops = { + .suspend = qxl_pm_suspend, + .resume = qxl_pm_resume, + .freeze = qxl_pm_freeze, + .thaw = qxl_pm_thaw, + .poweroff = qxl_pm_freeze, + .restore = qxl_pm_restore, +}; +static struct pci_driver qxl_pci_driver = { + .name = DRIVER_NAME, + .id_table = pciidlist, + .probe = qxl_pci_probe, + .remove = qxl_pci_remove, + .driver.pm = &qxl_pm_ops, +}; + static struct drm_driver qxl_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED, diff --git a/drivers/gpu/drm/qxl/qxl_drv.h b/drivers/gpu/drm/qxl/qxl_drv.h index 70a67862673a..aacb791464a3 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.h +++ b/drivers/gpu/drm/qxl/qxl_drv.h @@ -333,6 +333,7 @@ void qxl_bo_fini(struct qxl_device *qdev); void qxl_reinit_memslots(struct qxl_device *qdev); int qxl_surf_evict(struct qxl_device *qdev); +int qxl_vram_evict(struct qxl_device *qdev); struct qxl_ring *qxl_ring_create(struct qxl_ring_header *header, int element_size, diff --git a/drivers/gpu/drm/qxl/qxl_object.c b/drivers/gpu/drm/qxl/qxl_object.c index 62a046e4a036..1191fe7788c9 100644 --- a/drivers/gpu/drm/qxl/qxl_object.c +++ b/drivers/gpu/drm/qxl/qxl_object.c @@ -368,3 +368,8 @@ int qxl_surf_evict(struct qxl_device *qdev) { return ttm_bo_evict_mm(&qdev->mman.bdev, TTM_PL_PRIV0); } + +int qxl_vram_evict(struct qxl_device *qdev) +{ + return ttm_bo_evict_mm(&qdev->mman.bdev, TTM_PL_VRAM); +} -- cgit v1.2.3 From 5ff91e442652ec33a648c3b9ae5025faaff1e813 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Fri, 5 Jul 2013 10:20:33 +1000 Subject: qxl: use drm helper hotplug support This uses the helper to deal with hotplug so fbdev gets included. Signed-off-by: Dave Airlie --- drivers/gpu/drm/qxl/qxl_display.c | 4 +++- drivers/gpu/drm/qxl/qxl_kms.c | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c index 61714fdfc782..f76f5dd7bfc4 100644 --- a/drivers/gpu/drm/qxl/qxl_display.c +++ b/drivers/gpu/drm/qxl/qxl_display.c @@ -107,7 +107,7 @@ void qxl_display_read_client_monitors_config(struct qxl_device *qdev) qxl_io_log(qdev, "failed crc check for client_monitors_config," " retrying\n"); } - drm_sysfs_hotplug_event(qdev->ddev); + drm_helper_hpd_irq_event(qdev->ddev); } static int qxl_add_monitors_config_modes(struct drm_connector *connector) @@ -833,6 +833,8 @@ static int qdev_output_init(struct drm_device *dev, int num_output) drm_encoder_init(dev, &qxl_output->enc, &qxl_enc_funcs, DRM_MODE_ENCODER_VIRTUAL); + /* we get HPD via client monitors config */ + connector->polled = DRM_CONNECTOR_POLL_HPD; encoder->possible_crtcs = 1 << num_output; drm_mode_connector_attach_encoder(&qxl_output->base, &qxl_output->enc); diff --git a/drivers/gpu/drm/qxl/qxl_kms.c b/drivers/gpu/drm/qxl/qxl_kms.c index 2c6f9219e9d9..9e8da9ee9731 100644 --- a/drivers/gpu/drm/qxl/qxl_kms.c +++ b/drivers/gpu/drm/qxl/qxl_kms.c @@ -26,6 +26,7 @@ #include "qxl_drv.h" #include "qxl_object.h" +#include #include int qxl_log_level; @@ -307,6 +308,8 @@ int qxl_driver_load(struct drm_device *dev, unsigned long flags) goto out; } + drm_kms_helper_poll_init(qdev->ddev); + return 0; out: kfree(qdev); -- cgit v1.2.3 From 30f4e0870d1726f31aa59804337cfd5e0a3f2ec7 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Sun, 9 Jun 2013 16:08:22 +1000 Subject: drm/nvc0-/gr: make register lists from initvals functions Generated context verified to be the same for all supported chipsets. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/Makefile | 14 +- drivers/gpu/drm/nouveau/core/engine/device/nvc0.c | 18 +- drivers/gpu/drm/nouveau/core/engine/device/nve0.c | 8 +- .../gpu/drm/nouveau/core/engine/graph/ctxnvc0.c | 4498 +++++--------------- .../gpu/drm/nouveau/core/engine/graph/ctxnvc1.c | 804 ++++ .../gpu/drm/nouveau/core/engine/graph/ctxnvc3.c | 91 + .../gpu/drm/nouveau/core/engine/graph/ctxnvc8.c | 362 ++ .../gpu/drm/nouveau/core/engine/graph/ctxnvd9.c | 531 +++ .../gpu/drm/nouveau/core/engine/graph/ctxnve0.c | 2924 ------------- .../gpu/drm/nouveau/core/engine/graph/ctxnve4.c | 1033 +++++ .../gpu/drm/nouveau/core/engine/graph/ctxnvf0.c | 288 ++ drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c | 1648 +++---- drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h | 214 +- drivers/gpu/drm/nouveau/core/engine/graph/nvc1.c | 143 + drivers/gpu/drm/nouveau/core/engine/graph/nvc3.c | 109 + drivers/gpu/drm/nouveau/core/engine/graph/nvc8.c | 140 + drivers/gpu/drm/nouveau/core/engine/graph/nvd9.c | 164 + drivers/gpu/drm/nouveau/core/engine/graph/nve0.c | 1105 ----- drivers/gpu/drm/nouveau/core/engine/graph/nve4.c | 354 ++ drivers/gpu/drm/nouveau/core/engine/graph/nvf0.c | 176 + .../gpu/drm/nouveau/core/include/engine/graph.h | 9 +- 21 files changed, 6167 insertions(+), 8466 deletions(-) create mode 100644 drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc1.c create mode 100644 drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc3.c create mode 100644 drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc8.c create mode 100644 drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd9.c delete mode 100644 drivers/gpu/drm/nouveau/core/engine/graph/ctxnve0.c create mode 100644 drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c create mode 100644 drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c create mode 100644 drivers/gpu/drm/nouveau/core/engine/graph/nvc1.c create mode 100644 drivers/gpu/drm/nouveau/core/engine/graph/nvc3.c create mode 100644 drivers/gpu/drm/nouveau/core/engine/graph/nvc8.c create mode 100644 drivers/gpu/drm/nouveau/core/engine/graph/nvd9.c delete mode 100644 drivers/gpu/drm/nouveau/core/engine/graph/nve0.c create mode 100644 drivers/gpu/drm/nouveau/core/engine/graph/nve4.c create mode 100644 drivers/gpu/drm/nouveau/core/engine/graph/nvf0.c diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index b59cfd79bd79..4c3d29d67436 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile @@ -200,7 +200,12 @@ nouveau-y += core/engine/fifo/nve0.o nouveau-y += core/engine/graph/ctxnv40.o nouveau-y += core/engine/graph/ctxnv50.o nouveau-y += core/engine/graph/ctxnvc0.o -nouveau-y += core/engine/graph/ctxnve0.o +nouveau-y += core/engine/graph/ctxnvc1.o +nouveau-y += core/engine/graph/ctxnvc3.o +nouveau-y += core/engine/graph/ctxnvc8.o +nouveau-y += core/engine/graph/ctxnvd9.o +nouveau-y += core/engine/graph/ctxnve4.o +nouveau-y += core/engine/graph/ctxnvf0.o nouveau-y += core/engine/graph/nv04.o nouveau-y += core/engine/graph/nv10.o nouveau-y += core/engine/graph/nv20.o @@ -212,7 +217,12 @@ nouveau-y += core/engine/graph/nv35.o nouveau-y += core/engine/graph/nv40.o nouveau-y += core/engine/graph/nv50.o nouveau-y += core/engine/graph/nvc0.o -nouveau-y += core/engine/graph/nve0.o +nouveau-y += core/engine/graph/nvc1.o +nouveau-y += core/engine/graph/nvc3.o +nouveau-y += core/engine/graph/nvc8.o +nouveau-y += core/engine/graph/nvd9.o +nouveau-y += core/engine/graph/nve4.o +nouveau-y += core/engine/graph/nvf0.o nouveau-y += core/engine/mpeg/nv31.o nouveau-y += core/engine/mpeg/nv40.o nouveau-y += core/engine/mpeg/nv50.o diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c index 1df3578a5315..6637eecd831e 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c @@ -75,7 +75,7 @@ nvc0_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvc0_dmaeng_oclass; device->oclass[NVDEV_ENGINE_FIFO ] = &nvc0_fifo_oclass; device->oclass[NVDEV_ENGINE_SW ] = &nvc0_software_oclass; - device->oclass[NVDEV_ENGINE_GR ] = &nvc0_graph_oclass; + device->oclass[NVDEV_ENGINE_GR ] = nvc0_graph_oclass; device->oclass[NVDEV_ENGINE_VP ] = &nvc0_vp_oclass; device->oclass[NVDEV_ENGINE_BSP ] = &nvc0_bsp_oclass; device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass; @@ -104,7 +104,7 @@ nvc0_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvc0_dmaeng_oclass; device->oclass[NVDEV_ENGINE_FIFO ] = &nvc0_fifo_oclass; device->oclass[NVDEV_ENGINE_SW ] = &nvc0_software_oclass; - device->oclass[NVDEV_ENGINE_GR ] = &nvc0_graph_oclass; + device->oclass[NVDEV_ENGINE_GR ] = nvc3_graph_oclass; device->oclass[NVDEV_ENGINE_VP ] = &nvc0_vp_oclass; device->oclass[NVDEV_ENGINE_BSP ] = &nvc0_bsp_oclass; device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass; @@ -133,7 +133,7 @@ nvc0_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvc0_dmaeng_oclass; device->oclass[NVDEV_ENGINE_FIFO ] = &nvc0_fifo_oclass; device->oclass[NVDEV_ENGINE_SW ] = &nvc0_software_oclass; - device->oclass[NVDEV_ENGINE_GR ] = &nvc0_graph_oclass; + device->oclass[NVDEV_ENGINE_GR ] = nvc3_graph_oclass; device->oclass[NVDEV_ENGINE_VP ] = &nvc0_vp_oclass; device->oclass[NVDEV_ENGINE_BSP ] = &nvc0_bsp_oclass; device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass; @@ -161,7 +161,7 @@ nvc0_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvc0_dmaeng_oclass; device->oclass[NVDEV_ENGINE_FIFO ] = &nvc0_fifo_oclass; device->oclass[NVDEV_ENGINE_SW ] = &nvc0_software_oclass; - device->oclass[NVDEV_ENGINE_GR ] = &nvc0_graph_oclass; + device->oclass[NVDEV_ENGINE_GR ] = nvc3_graph_oclass; device->oclass[NVDEV_ENGINE_VP ] = &nvc0_vp_oclass; device->oclass[NVDEV_ENGINE_BSP ] = &nvc0_bsp_oclass; device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass; @@ -190,7 +190,7 @@ nvc0_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvc0_dmaeng_oclass; device->oclass[NVDEV_ENGINE_FIFO ] = &nvc0_fifo_oclass; device->oclass[NVDEV_ENGINE_SW ] = &nvc0_software_oclass; - device->oclass[NVDEV_ENGINE_GR ] = &nvc0_graph_oclass; + device->oclass[NVDEV_ENGINE_GR ] = nvc3_graph_oclass; device->oclass[NVDEV_ENGINE_VP ] = &nvc0_vp_oclass; device->oclass[NVDEV_ENGINE_BSP ] = &nvc0_bsp_oclass; device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass; @@ -219,7 +219,7 @@ nvc0_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvc0_dmaeng_oclass; device->oclass[NVDEV_ENGINE_FIFO ] = &nvc0_fifo_oclass; device->oclass[NVDEV_ENGINE_SW ] = &nvc0_software_oclass; - device->oclass[NVDEV_ENGINE_GR ] = &nvc0_graph_oclass; + device->oclass[NVDEV_ENGINE_GR ] = nvc1_graph_oclass; device->oclass[NVDEV_ENGINE_VP ] = &nvc0_vp_oclass; device->oclass[NVDEV_ENGINE_BSP ] = &nvc0_bsp_oclass; device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass; @@ -247,7 +247,7 @@ nvc0_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvc0_dmaeng_oclass; device->oclass[NVDEV_ENGINE_FIFO ] = &nvc0_fifo_oclass; device->oclass[NVDEV_ENGINE_SW ] = &nvc0_software_oclass; - device->oclass[NVDEV_ENGINE_GR ] = &nvc0_graph_oclass; + device->oclass[NVDEV_ENGINE_GR ] = nvc8_graph_oclass; device->oclass[NVDEV_ENGINE_VP ] = &nvc0_vp_oclass; device->oclass[NVDEV_ENGINE_BSP ] = &nvc0_bsp_oclass; device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass; @@ -276,7 +276,7 @@ nvc0_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvd0_dmaeng_oclass; device->oclass[NVDEV_ENGINE_FIFO ] = &nvc0_fifo_oclass; device->oclass[NVDEV_ENGINE_SW ] = &nvc0_software_oclass; - device->oclass[NVDEV_ENGINE_GR ] = &nvc0_graph_oclass; + device->oclass[NVDEV_ENGINE_GR ] = nvd9_graph_oclass; device->oclass[NVDEV_ENGINE_VP ] = &nvc0_vp_oclass; device->oclass[NVDEV_ENGINE_BSP ] = &nvc0_bsp_oclass; device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass; @@ -304,7 +304,7 @@ nvc0_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvd0_dmaeng_oclass; device->oclass[NVDEV_ENGINE_FIFO ] = &nvc0_fifo_oclass; device->oclass[NVDEV_ENGINE_SW ] = &nvc0_software_oclass; - device->oclass[NVDEV_ENGINE_GR ] = &nvc0_graph_oclass; + device->oclass[NVDEV_ENGINE_GR ] = nvd9_graph_oclass; device->oclass[NVDEV_ENGINE_VP ] = &nvc0_vp_oclass; device->oclass[NVDEV_ENGINE_BSP ] = &nvc0_bsp_oclass; device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass; diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nve0.c b/drivers/gpu/drm/nouveau/core/engine/device/nve0.c index 4e6ef62e88c8..78ae397c3eee 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/nve0.c @@ -75,7 +75,7 @@ nve0_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvd0_dmaeng_oclass; device->oclass[NVDEV_ENGINE_FIFO ] = &nve0_fifo_oclass; device->oclass[NVDEV_ENGINE_SW ] = &nvc0_software_oclass; - device->oclass[NVDEV_ENGINE_GR ] = &nve0_graph_oclass; + device->oclass[NVDEV_ENGINE_GR ] = nve4_graph_oclass; device->oclass[NVDEV_ENGINE_DISP ] = &nve0_disp_oclass; device->oclass[NVDEV_ENGINE_COPY0 ] = &nve0_copy0_oclass; device->oclass[NVDEV_ENGINE_COPY1 ] = &nve0_copy1_oclass; @@ -105,7 +105,7 @@ nve0_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvd0_dmaeng_oclass; device->oclass[NVDEV_ENGINE_FIFO ] = &nve0_fifo_oclass; device->oclass[NVDEV_ENGINE_SW ] = &nvc0_software_oclass; - device->oclass[NVDEV_ENGINE_GR ] = &nve0_graph_oclass; + device->oclass[NVDEV_ENGINE_GR ] = nve4_graph_oclass; device->oclass[NVDEV_ENGINE_DISP ] = &nve0_disp_oclass; device->oclass[NVDEV_ENGINE_COPY0 ] = &nve0_copy0_oclass; device->oclass[NVDEV_ENGINE_COPY1 ] = &nve0_copy1_oclass; @@ -135,7 +135,7 @@ nve0_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvd0_dmaeng_oclass; device->oclass[NVDEV_ENGINE_FIFO ] = &nve0_fifo_oclass; device->oclass[NVDEV_ENGINE_SW ] = &nvc0_software_oclass; - device->oclass[NVDEV_ENGINE_GR ] = &nve0_graph_oclass; + device->oclass[NVDEV_ENGINE_GR ] = nve4_graph_oclass; device->oclass[NVDEV_ENGINE_DISP ] = &nve0_disp_oclass; device->oclass[NVDEV_ENGINE_COPY0 ] = &nve0_copy0_oclass; device->oclass[NVDEV_ENGINE_COPY1 ] = &nve0_copy1_oclass; @@ -166,7 +166,7 @@ nve0_identify(struct nouveau_device *device) #if 0 device->oclass[NVDEV_ENGINE_FIFO ] = &nve0_fifo_oclass; device->oclass[NVDEV_ENGINE_SW ] = &nvc0_software_oclass; - device->oclass[NVDEV_ENGINE_GR ] = &nve0_graph_oclass; + device->oclass[NVDEV_ENGINE_GR ] = nvf0_graph_oclass; #endif device->oclass[NVDEV_ENGINE_DISP ] = &nvf0_disp_oclass; #if 0 diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c index 3be7b950eece..a09ee65b0f99 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c @@ -24,28 +24,1067 @@ #include "nvc0.h" +struct nvc0_graph_init +nvc0_grctx_init_icmd[] = { + { 0x001000, 1, 0x01, 0x00000004 }, + { 0x0000a9, 1, 0x01, 0x0000ffff }, + { 0x000038, 1, 0x01, 0x0fac6881 }, + { 0x00003d, 1, 0x01, 0x00000001 }, + { 0x0000e8, 8, 0x01, 0x00000400 }, + { 0x000078, 8, 0x01, 0x00000300 }, + { 0x000050, 1, 0x01, 0x00000011 }, + { 0x000058, 8, 0x01, 0x00000008 }, + { 0x000208, 8, 0x01, 0x00000001 }, + { 0x000081, 1, 0x01, 0x00000001 }, + { 0x000085, 1, 0x01, 0x00000004 }, + { 0x000088, 1, 0x01, 0x00000400 }, + { 0x000090, 1, 0x01, 0x00000300 }, + { 0x000098, 1, 0x01, 0x00001001 }, + { 0x0000e3, 1, 0x01, 0x00000001 }, + { 0x0000da, 1, 0x01, 0x00000001 }, + { 0x0000f8, 1, 0x01, 0x00000003 }, + { 0x0000fa, 1, 0x01, 0x00000001 }, + { 0x00009f, 4, 0x01, 0x0000ffff }, + { 0x0000b1, 1, 0x01, 0x00000001 }, + { 0x0000b2, 40, 0x01, 0x00000000 }, + { 0x000210, 8, 0x01, 0x00000040 }, + { 0x000218, 8, 0x01, 0x0000c080 }, + { 0x0000ad, 1, 0x01, 0x0000013e }, + { 0x0000e1, 1, 0x01, 0x00000010 }, + { 0x000290, 16, 0x01, 0x00000000 }, + { 0x0003b0, 16, 0x01, 0x00000000 }, + { 0x0002a0, 16, 0x01, 0x00000000 }, + { 0x000420, 16, 0x01, 0x00000000 }, + { 0x0002b0, 16, 0x01, 0x00000000 }, + { 0x000430, 16, 0x01, 0x00000000 }, + { 0x0002c0, 16, 0x01, 0x00000000 }, + { 0x0004d0, 16, 0x01, 0x00000000 }, + { 0x000720, 16, 0x01, 0x00000000 }, + { 0x0008c0, 16, 0x01, 0x00000000 }, + { 0x000890, 16, 0x01, 0x00000000 }, + { 0x0008e0, 16, 0x01, 0x00000000 }, + { 0x0008a0, 16, 0x01, 0x00000000 }, + { 0x0008f0, 16, 0x01, 0x00000000 }, + { 0x00094c, 1, 0x01, 0x000000ff }, + { 0x00094d, 1, 0x01, 0xffffffff }, + { 0x00094e, 1, 0x01, 0x00000002 }, + { 0x0002ec, 1, 0x01, 0x00000001 }, + { 0x000303, 1, 0x01, 0x00000001 }, + { 0x0002e6, 1, 0x01, 0x00000001 }, + { 0x000466, 1, 0x01, 0x00000052 }, + { 0x000301, 1, 0x01, 0x3f800000 }, + { 0x000304, 1, 0x01, 0x30201000 }, + { 0x000305, 1, 0x01, 0x70605040 }, + { 0x000306, 1, 0x01, 0xb8a89888 }, + { 0x000307, 1, 0x01, 0xf8e8d8c8 }, + { 0x00030a, 1, 0x01, 0x00ffff00 }, + { 0x00030b, 1, 0x01, 0x0000001a }, + { 0x00030c, 1, 0x01, 0x00000001 }, + { 0x000318, 1, 0x01, 0x00000001 }, + { 0x000340, 1, 0x01, 0x00000000 }, + { 0x000375, 1, 0x01, 0x00000001 }, + { 0x000351, 1, 0x01, 0x00000100 }, + { 0x00037d, 1, 0x01, 0x00000006 }, + { 0x0003a0, 1, 0x01, 0x00000002 }, + { 0x0003aa, 1, 0x01, 0x00000001 }, + { 0x0003a9, 1, 0x01, 0x00000001 }, + { 0x000380, 1, 0x01, 0x00000001 }, + { 0x000360, 1, 0x01, 0x00000040 }, + { 0x000366, 2, 0x01, 0x00000000 }, + { 0x000368, 1, 0x01, 0x00001fff }, + { 0x000370, 2, 0x01, 0x00000000 }, + { 0x000372, 1, 0x01, 0x003fffff }, + { 0x00037a, 1, 0x01, 0x00000012 }, + { 0x0005e0, 5, 0x01, 0x00000022 }, + { 0x000619, 1, 0x01, 0x00000003 }, + { 0x000811, 1, 0x01, 0x00000003 }, + { 0x000812, 1, 0x01, 0x00000004 }, + { 0x000813, 1, 0x01, 0x00000006 }, + { 0x000814, 1, 0x01, 0x00000008 }, + { 0x000815, 1, 0x01, 0x0000000b }, + { 0x000800, 6, 0x01, 0x00000001 }, + { 0x000632, 1, 0x01, 0x00000001 }, + { 0x000633, 1, 0x01, 0x00000002 }, + { 0x000634, 1, 0x01, 0x00000003 }, + { 0x000635, 1, 0x01, 0x00000004 }, + { 0x000654, 1, 0x01, 0x3f800000 }, + { 0x000657, 1, 0x01, 0x3f800000 }, + { 0x000655, 2, 0x01, 0x3f800000 }, + { 0x0006cd, 1, 0x01, 0x3f800000 }, + { 0x0007f5, 1, 0x01, 0x3f800000 }, + { 0x0007dc, 1, 0x01, 0x39291909 }, + { 0x0007dd, 1, 0x01, 0x79695949 }, + { 0x0007de, 1, 0x01, 0xb9a99989 }, + { 0x0007df, 1, 0x01, 0xf9e9d9c9 }, + { 0x0007e8, 1, 0x01, 0x00003210 }, + { 0x0007e9, 1, 0x01, 0x00007654 }, + { 0x0007ea, 1, 0x01, 0x00000098 }, + { 0x0007ec, 1, 0x01, 0x39291909 }, + { 0x0007ed, 1, 0x01, 0x79695949 }, + { 0x0007ee, 1, 0x01, 0xb9a99989 }, + { 0x0007ef, 1, 0x01, 0xf9e9d9c9 }, + { 0x0007f0, 1, 0x01, 0x00003210 }, + { 0x0007f1, 1, 0x01, 0x00007654 }, + { 0x0007f2, 1, 0x01, 0x00000098 }, + { 0x0005a5, 1, 0x01, 0x00000001 }, + { 0x000980, 128, 0x01, 0x00000000 }, + { 0x000468, 1, 0x01, 0x00000004 }, + { 0x00046c, 1, 0x01, 0x00000001 }, + { 0x000470, 96, 0x01, 0x00000000 }, + { 0x000510, 16, 0x01, 0x3f800000 }, + { 0x000520, 1, 0x01, 0x000002b6 }, + { 0x000529, 1, 0x01, 0x00000001 }, + { 0x000530, 16, 0x01, 0xffff0000 }, + { 0x000585, 1, 0x01, 0x0000003f }, + { 0x000576, 1, 0x01, 0x00000003 }, + { 0x000586, 1, 0x01, 0x00000040 }, + { 0x000582, 2, 0x01, 0x00000080 }, + { 0x0005c2, 1, 0x01, 0x00000001 }, + { 0x000638, 1, 0x01, 0x00000001 }, + { 0x000639, 1, 0x01, 0x00000001 }, + { 0x00063a, 1, 0x01, 0x00000002 }, + { 0x00063b, 2, 0x01, 0x00000001 }, + { 0x00063d, 1, 0x01, 0x00000002 }, + { 0x00063e, 1, 0x01, 0x00000001 }, + { 0x0008b8, 8, 0x01, 0x00000001 }, + { 0x000900, 8, 0x01, 0x00000001 }, + { 0x000908, 8, 0x01, 0x00000002 }, + { 0x000910, 16, 0x01, 0x00000001 }, + { 0x000920, 8, 0x01, 0x00000002 }, + { 0x000928, 8, 0x01, 0x00000001 }, + { 0x000648, 9, 0x01, 0x00000001 }, + { 0x000658, 1, 0x01, 0x0000000f }, + { 0x0007ff, 1, 0x01, 0x0000000a }, + { 0x00066a, 1, 0x01, 0x40000000 }, + { 0x00066b, 1, 0x01, 0x10000000 }, + { 0x00066c, 2, 0x01, 0xffff0000 }, + { 0x0007af, 2, 0x01, 0x00000008 }, + { 0x0007f6, 1, 0x01, 0x00000001 }, + { 0x0006b2, 1, 0x01, 0x00000055 }, + { 0x0007ad, 1, 0x01, 0x00000003 }, + { 0x000937, 1, 0x01, 0x00000001 }, + { 0x000971, 1, 0x01, 0x00000008 }, + { 0x000972, 1, 0x01, 0x00000040 }, + { 0x000973, 1, 0x01, 0x0000012c }, + { 0x00097c, 1, 0x01, 0x00000040 }, + { 0x000979, 1, 0x01, 0x00000003 }, + { 0x000975, 1, 0x01, 0x00000020 }, + { 0x000976, 1, 0x01, 0x00000001 }, + { 0x000977, 1, 0x01, 0x00000020 }, + { 0x000978, 1, 0x01, 0x00000001 }, + { 0x000957, 1, 0x01, 0x00000003 }, + { 0x00095e, 1, 0x01, 0x20164010 }, + { 0x00095f, 1, 0x01, 0x00000020 }, + { 0x000683, 1, 0x01, 0x00000006 }, + { 0x000685, 1, 0x01, 0x003fffff }, + { 0x000687, 1, 0x01, 0x00000c48 }, + { 0x0006a0, 1, 0x01, 0x00000005 }, + { 0x000840, 1, 0x01, 0x00300008 }, + { 0x000841, 1, 0x01, 0x04000080 }, + { 0x000842, 1, 0x01, 0x00300008 }, + { 0x000843, 1, 0x01, 0x04000080 }, + { 0x000818, 8, 0x01, 0x00000000 }, + { 0x000848, 16, 0x01, 0x00000000 }, + { 0x000738, 1, 0x01, 0x00000000 }, + { 0x0006aa, 1, 0x01, 0x00000001 }, + { 0x0006ab, 1, 0x01, 0x00000002 }, + { 0x0006ac, 1, 0x01, 0x00000080 }, + { 0x0006ad, 2, 0x01, 0x00000100 }, + { 0x0006b1, 1, 0x01, 0x00000011 }, + { 0x0006bb, 1, 0x01, 0x000000cf }, + { 0x0006ce, 1, 0x01, 0x2a712488 }, + { 0x000739, 1, 0x01, 0x4085c000 }, + { 0x00073a, 1, 0x01, 0x00000080 }, + { 0x000786, 1, 0x01, 0x80000100 }, + { 0x00073c, 1, 0x01, 0x00010100 }, + { 0x00073d, 1, 0x01, 0x02800000 }, + { 0x000787, 1, 0x01, 0x000000cf }, + { 0x00078c, 1, 0x01, 0x00000008 }, + { 0x000792, 1, 0x01, 0x00000001 }, + { 0x000794, 1, 0x01, 0x00000001 }, + { 0x000795, 2, 0x01, 0x00000001 }, + { 0x000797, 1, 0x01, 0x000000cf }, + { 0x000836, 1, 0x01, 0x00000001 }, + { 0x00079a, 1, 0x01, 0x00000002 }, + { 0x000833, 1, 0x01, 0x04444480 }, + { 0x0007a1, 1, 0x01, 0x00000001 }, + { 0x0007a3, 1, 0x01, 0x00000001 }, + { 0x0007a4, 2, 0x01, 0x00000001 }, + { 0x000831, 1, 0x01, 0x00000004 }, + { 0x00080c, 1, 0x01, 0x00000002 }, + { 0x00080d, 2, 0x01, 0x00000100 }, + { 0x00080f, 1, 0x01, 0x00000001 }, + { 0x000823, 1, 0x01, 0x00000002 }, + { 0x000824, 2, 0x01, 0x00000100 }, + { 0x000826, 1, 0x01, 0x00000001 }, + { 0x00095d, 1, 0x01, 0x00000001 }, + { 0x00082b, 1, 0x01, 0x00000004 }, + { 0x000942, 1, 0x01, 0x00010001 }, + { 0x000943, 1, 0x01, 0x00000001 }, + { 0x000944, 1, 0x01, 0x00000022 }, + { 0x0007c5, 1, 0x01, 0x00010001 }, + { 0x000834, 1, 0x01, 0x00000001 }, + { 0x0007c7, 1, 0x01, 0x00000001 }, + { 0x00c1b0, 8, 0x01, 0x0000000f }, + { 0x00c1b8, 1, 0x01, 0x0fac6881 }, + { 0x00c1b9, 1, 0x01, 0x00fac688 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + { 0x001000, 1, 0x01, 0x00000002 }, + { 0x0006aa, 1, 0x01, 0x00000001 }, + { 0x0006ad, 2, 0x01, 0x00000100 }, + { 0x0006b1, 1, 0x01, 0x00000011 }, + { 0x00078c, 1, 0x01, 0x00000008 }, + { 0x000792, 1, 0x01, 0x00000001 }, + { 0x000794, 1, 0x01, 0x00000001 }, + { 0x000795, 2, 0x01, 0x00000001 }, + { 0x000797, 1, 0x01, 0x000000cf }, + { 0x00079a, 1, 0x01, 0x00000002 }, + { 0x000833, 1, 0x01, 0x04444480 }, + { 0x0007a1, 1, 0x01, 0x00000001 }, + { 0x0007a3, 1, 0x01, 0x00000001 }, + { 0x0007a4, 2, 0x01, 0x00000001 }, + { 0x000831, 1, 0x01, 0x00000004 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + { 0x001000, 1, 0x01, 0x00000014 }, + { 0x000351, 1, 0x01, 0x00000100 }, + { 0x000957, 1, 0x01, 0x00000003 }, + { 0x00095d, 1, 0x01, 0x00000001 }, + { 0x00082b, 1, 0x01, 0x00000004 }, + { 0x000942, 1, 0x01, 0x00010001 }, + { 0x000943, 1, 0x01, 0x00000001 }, + { 0x0007c5, 1, 0x01, 0x00010001 }, + { 0x000834, 1, 0x01, 0x00000001 }, + { 0x0007c7, 1, 0x01, 0x00000001 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + { 0x001000, 1, 0x01, 0x00000001 }, + { 0x00080c, 1, 0x01, 0x00000002 }, + { 0x00080d, 2, 0x01, 0x00000100 }, + { 0x00080f, 1, 0x01, 0x00000001 }, + { 0x000823, 1, 0x01, 0x00000002 }, + { 0x000824, 2, 0x01, 0x00000100 }, + { 0x000826, 1, 0x01, 0x00000001 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + {} +}; + +struct nvc0_graph_init +nvc0_grctx_init_9097[] = { + { 0x000800, 8, 0x40, 0x00000000 }, + { 0x000804, 8, 0x40, 0x00000000 }, + { 0x000808, 8, 0x40, 0x00000400 }, + { 0x00080c, 8, 0x40, 0x00000300 }, + { 0x000810, 1, 0x04, 0x000000cf }, + { 0x000850, 7, 0x40, 0x00000000 }, + { 0x000814, 8, 0x40, 0x00000040 }, + { 0x000818, 8, 0x40, 0x00000001 }, + { 0x00081c, 8, 0x40, 0x00000000 }, + { 0x000820, 8, 0x40, 0x00000000 }, + { 0x002700, 8, 0x20, 0x00000000 }, + { 0x002704, 8, 0x20, 0x00000000 }, + { 0x002708, 8, 0x20, 0x00000000 }, + { 0x00270c, 8, 0x20, 0x00000000 }, + { 0x002710, 8, 0x20, 0x00014000 }, + { 0x002714, 8, 0x20, 0x00000040 }, + { 0x001c00, 16, 0x10, 0x00000000 }, + { 0x001c04, 16, 0x10, 0x00000000 }, + { 0x001c08, 16, 0x10, 0x00000000 }, + { 0x001c0c, 16, 0x10, 0x00000000 }, + { 0x001d00, 16, 0x10, 0x00000000 }, + { 0x001d04, 16, 0x10, 0x00000000 }, + { 0x001d08, 16, 0x10, 0x00000000 }, + { 0x001d0c, 16, 0x10, 0x00000000 }, + { 0x001f00, 16, 0x08, 0x00000000 }, + { 0x001f04, 16, 0x08, 0x00000000 }, + { 0x001f80, 16, 0x08, 0x00000000 }, + { 0x001f84, 16, 0x08, 0x00000000 }, + { 0x002200, 5, 0x10, 0x00000022 }, + { 0x002000, 1, 0x04, 0x00000000 }, + { 0x002040, 1, 0x04, 0x00000011 }, + { 0x002080, 1, 0x04, 0x00000020 }, + { 0x0020c0, 1, 0x04, 0x00000030 }, + { 0x002100, 1, 0x04, 0x00000040 }, + { 0x002140, 1, 0x04, 0x00000051 }, + { 0x00200c, 6, 0x40, 0x00000001 }, + { 0x002010, 1, 0x04, 0x00000000 }, + { 0x002050, 1, 0x04, 0x00000000 }, + { 0x002090, 1, 0x04, 0x00000001 }, + { 0x0020d0, 1, 0x04, 0x00000002 }, + { 0x002110, 1, 0x04, 0x00000003 }, + { 0x002150, 1, 0x04, 0x00000004 }, + { 0x000380, 4, 0x20, 0x00000000 }, + { 0x000384, 4, 0x20, 0x00000000 }, + { 0x000388, 4, 0x20, 0x00000000 }, + { 0x00038c, 4, 0x20, 0x00000000 }, + { 0x000700, 4, 0x10, 0x00000000 }, + { 0x000704, 4, 0x10, 0x00000000 }, + { 0x000708, 4, 0x10, 0x00000000 }, + { 0x002800, 128, 0x04, 0x00000000 }, + { 0x000a00, 16, 0x20, 0x00000000 }, + { 0x000a04, 16, 0x20, 0x00000000 }, + { 0x000a08, 16, 0x20, 0x00000000 }, + { 0x000a0c, 16, 0x20, 0x00000000 }, + { 0x000a10, 16, 0x20, 0x00000000 }, + { 0x000a14, 16, 0x20, 0x00000000 }, + { 0x000c00, 16, 0x10, 0x00000000 }, + { 0x000c04, 16, 0x10, 0x00000000 }, + { 0x000c08, 16, 0x10, 0x00000000 }, + { 0x000c0c, 16, 0x10, 0x3f800000 }, + { 0x000d00, 8, 0x08, 0xffff0000 }, + { 0x000d04, 8, 0x08, 0xffff0000 }, + { 0x000e00, 16, 0x10, 0x00000000 }, + { 0x000e04, 16, 0x10, 0xffff0000 }, + { 0x000e08, 16, 0x10, 0xffff0000 }, + { 0x000d40, 4, 0x08, 0x00000000 }, + { 0x000d44, 4, 0x08, 0x00000000 }, + { 0x001e00, 8, 0x20, 0x00000001 }, + { 0x001e04, 8, 0x20, 0x00000001 }, + { 0x001e08, 8, 0x20, 0x00000002 }, + { 0x001e0c, 8, 0x20, 0x00000001 }, + { 0x001e10, 8, 0x20, 0x00000001 }, + { 0x001e14, 8, 0x20, 0x00000002 }, + { 0x001e18, 8, 0x20, 0x00000001 }, + { 0x003400, 128, 0x04, 0x00000000 }, + { 0x00030c, 1, 0x04, 0x00000001 }, + { 0x001944, 1, 0x04, 0x00000000 }, + { 0x001514, 1, 0x04, 0x00000000 }, + { 0x000d68, 1, 0x04, 0x0000ffff }, + { 0x00121c, 1, 0x04, 0x0fac6881 }, + { 0x000fac, 1, 0x04, 0x00000001 }, + { 0x001538, 1, 0x04, 0x00000001 }, + { 0x000fe0, 2, 0x04, 0x00000000 }, + { 0x000fe8, 1, 0x04, 0x00000014 }, + { 0x000fec, 1, 0x04, 0x00000040 }, + { 0x000ff0, 1, 0x04, 0x00000000 }, + { 0x00179c, 1, 0x04, 0x00000000 }, + { 0x001228, 1, 0x04, 0x00000400 }, + { 0x00122c, 1, 0x04, 0x00000300 }, + { 0x001230, 1, 0x04, 0x00010001 }, + { 0x0007f8, 1, 0x04, 0x00000000 }, + { 0x0015b4, 1, 0x04, 0x00000001 }, + { 0x0015cc, 1, 0x04, 0x00000000 }, + { 0x001534, 1, 0x04, 0x00000000 }, + { 0x000fb0, 1, 0x04, 0x00000000 }, + { 0x0015d0, 1, 0x04, 0x00000000 }, + { 0x00153c, 1, 0x04, 0x00000000 }, + { 0x0016b4, 1, 0x04, 0x00000003 }, + { 0x000fbc, 4, 0x04, 0x0000ffff }, + { 0x000df8, 2, 0x04, 0x00000000 }, + { 0x001948, 1, 0x04, 0x00000000 }, + { 0x001970, 1, 0x04, 0x00000001 }, + { 0x00161c, 1, 0x04, 0x000009f0 }, + { 0x000dcc, 1, 0x04, 0x00000010 }, + { 0x00163c, 1, 0x04, 0x00000000 }, + { 0x0015e4, 1, 0x04, 0x00000000 }, + { 0x001160, 32, 0x04, 0x25e00040 }, + { 0x001880, 32, 0x04, 0x00000000 }, + { 0x000f84, 2, 0x04, 0x00000000 }, + { 0x0017c8, 2, 0x04, 0x00000000 }, + { 0x0017d0, 1, 0x04, 0x000000ff }, + { 0x0017d4, 1, 0x04, 0xffffffff }, + { 0x0017d8, 1, 0x04, 0x00000002 }, + { 0x0017dc, 1, 0x04, 0x00000000 }, + { 0x0015f4, 2, 0x04, 0x00000000 }, + { 0x001434, 2, 0x04, 0x00000000 }, + { 0x000d74, 1, 0x04, 0x00000000 }, + { 0x000dec, 1, 0x04, 0x00000001 }, + { 0x0013a4, 1, 0x04, 0x00000000 }, + { 0x001318, 1, 0x04, 0x00000001 }, + { 0x001644, 1, 0x04, 0x00000000 }, + { 0x000748, 1, 0x04, 0x00000000 }, + { 0x000de8, 1, 0x04, 0x00000000 }, + { 0x001648, 1, 0x04, 0x00000000 }, + { 0x0012a4, 1, 0x04, 0x00000000 }, + { 0x001120, 4, 0x04, 0x00000000 }, + { 0x001118, 1, 0x04, 0x00000000 }, + { 0x00164c, 1, 0x04, 0x00000000 }, + { 0x001658, 1, 0x04, 0x00000000 }, + { 0x001910, 1, 0x04, 0x00000290 }, + { 0x001518, 1, 0x04, 0x00000000 }, + { 0x00165c, 1, 0x04, 0x00000001 }, + { 0x001520, 1, 0x04, 0x00000000 }, + { 0x001604, 1, 0x04, 0x00000000 }, + { 0x001570, 1, 0x04, 0x00000000 }, + { 0x0013b0, 2, 0x04, 0x3f800000 }, + { 0x00020c, 1, 0x04, 0x00000000 }, + { 0x001670, 1, 0x04, 0x30201000 }, + { 0x001674, 1, 0x04, 0x70605040 }, + { 0x001678, 1, 0x04, 0xb8a89888 }, + { 0x00167c, 1, 0x04, 0xf8e8d8c8 }, + { 0x00166c, 1, 0x04, 0x00000000 }, + { 0x001680, 1, 0x04, 0x00ffff00 }, + { 0x0012d0, 1, 0x04, 0x00000003 }, + { 0x0012d4, 1, 0x04, 0x00000002 }, + { 0x001684, 2, 0x04, 0x00000000 }, + { 0x000dac, 2, 0x04, 0x00001b02 }, + { 0x000db4, 1, 0x04, 0x00000000 }, + { 0x00168c, 1, 0x04, 0x00000000 }, + { 0x0015bc, 1, 0x04, 0x00000000 }, + { 0x00156c, 1, 0x04, 0x00000000 }, + { 0x00187c, 1, 0x04, 0x00000000 }, + { 0x001110, 1, 0x04, 0x00000001 }, + { 0x000dc0, 3, 0x04, 0x00000000 }, + { 0x001234, 1, 0x04, 0x00000000 }, + { 0x001690, 1, 0x04, 0x00000000 }, + { 0x0012ac, 1, 0x04, 0x00000001 }, + { 0x0002c4, 1, 0x04, 0x00000000 }, + { 0x000790, 5, 0x04, 0x00000000 }, + { 0x00077c, 1, 0x04, 0x00000000 }, + { 0x001000, 1, 0x04, 0x00000010 }, + { 0x0010fc, 1, 0x04, 0x00000000 }, + { 0x001290, 1, 0x04, 0x00000000 }, + { 0x000218, 1, 0x04, 0x00000010 }, + { 0x0012d8, 1, 0x04, 0x00000000 }, + { 0x0012dc, 1, 0x04, 0x00000010 }, + { 0x000d94, 1, 0x04, 0x00000001 }, + { 0x00155c, 2, 0x04, 0x00000000 }, + { 0x001564, 1, 0x04, 0x00001fff }, + { 0x001574, 2, 0x04, 0x00000000 }, + { 0x00157c, 1, 0x04, 0x003fffff }, + { 0x001354, 1, 0x04, 0x00000000 }, + { 0x001664, 1, 0x04, 0x00000000 }, + { 0x001610, 1, 0x04, 0x00000012 }, + { 0x001608, 2, 0x04, 0x00000000 }, + { 0x00162c, 1, 0x04, 0x00000003 }, + { 0x000210, 1, 0x04, 0x00000000 }, + { 0x000320, 1, 0x04, 0x00000000 }, + { 0x000324, 6, 0x04, 0x3f800000 }, + { 0x000750, 1, 0x04, 0x00000000 }, + { 0x000760, 1, 0x04, 0x39291909 }, + { 0x000764, 1, 0x04, 0x79695949 }, + { 0x000768, 1, 0x04, 0xb9a99989 }, + { 0x00076c, 1, 0x04, 0xf9e9d9c9 }, + { 0x000770, 1, 0x04, 0x30201000 }, + { 0x000774, 1, 0x04, 0x70605040 }, + { 0x000778, 1, 0x04, 0x00009080 }, + { 0x000780, 1, 0x04, 0x39291909 }, + { 0x000784, 1, 0x04, 0x79695949 }, + { 0x000788, 1, 0x04, 0xb9a99989 }, + { 0x00078c, 1, 0x04, 0xf9e9d9c9 }, + { 0x0007d0, 1, 0x04, 0x30201000 }, + { 0x0007d4, 1, 0x04, 0x70605040 }, + { 0x0007d8, 1, 0x04, 0x00009080 }, + { 0x00037c, 1, 0x04, 0x00000001 }, + { 0x000740, 2, 0x04, 0x00000000 }, + { 0x002600, 1, 0x04, 0x00000000 }, + { 0x001918, 1, 0x04, 0x00000000 }, + { 0x00191c, 1, 0x04, 0x00000900 }, + { 0x001920, 1, 0x04, 0x00000405 }, + { 0x001308, 1, 0x04, 0x00000001 }, + { 0x001924, 1, 0x04, 0x00000000 }, + { 0x0013ac, 1, 0x04, 0x00000000 }, + { 0x00192c, 1, 0x04, 0x00000001 }, + { 0x00193c, 1, 0x04, 0x00002c1c }, + { 0x000d7c, 1, 0x04, 0x00000000 }, + { 0x000f8c, 1, 0x04, 0x00000000 }, + { 0x0002c0, 1, 0x04, 0x00000001 }, + { 0x001510, 1, 0x04, 0x00000000 }, + { 0x001940, 1, 0x04, 0x00000000 }, + { 0x000ff4, 2, 0x04, 0x00000000 }, + { 0x00194c, 2, 0x04, 0x00000000 }, + { 0x001968, 1, 0x04, 0x00000000 }, + { 0x001590, 1, 0x04, 0x0000003f }, + { 0x0007e8, 4, 0x04, 0x00000000 }, + { 0x00196c, 1, 0x04, 0x00000011 }, + { 0x00197c, 1, 0x04, 0x00000000 }, + { 0x000fcc, 2, 0x04, 0x00000000 }, + { 0x0002d8, 1, 0x04, 0x00000040 }, + { 0x001980, 1, 0x04, 0x00000080 }, + { 0x001504, 1, 0x04, 0x00000080 }, + { 0x001984, 1, 0x04, 0x00000000 }, + { 0x000300, 1, 0x04, 0x00000001 }, + { 0x0013a8, 1, 0x04, 0x00000000 }, + { 0x0012ec, 1, 0x04, 0x00000000 }, + { 0x001310, 1, 0x04, 0x00000000 }, + { 0x001314, 1, 0x04, 0x00000001 }, + { 0x001380, 1, 0x04, 0x00000000 }, + { 0x001384, 4, 0x04, 0x00000001 }, + { 0x001394, 1, 0x04, 0x00000000 }, + { 0x00139c, 1, 0x04, 0x00000000 }, + { 0x001398, 1, 0x04, 0x00000000 }, + { 0x001594, 1, 0x04, 0x00000000 }, + { 0x001598, 4, 0x04, 0x00000001 }, + { 0x000f54, 3, 0x04, 0x00000000 }, + { 0x0019bc, 1, 0x04, 0x00000000 }, + { 0x000f9c, 2, 0x04, 0x00000000 }, + { 0x0012cc, 1, 0x04, 0x00000000 }, + { 0x0012e8, 1, 0x04, 0x00000000 }, + { 0x00130c, 1, 0x04, 0x00000001 }, + { 0x001360, 8, 0x04, 0x00000000 }, + { 0x00133c, 2, 0x04, 0x00000001 }, + { 0x001344, 1, 0x04, 0x00000002 }, + { 0x001348, 2, 0x04, 0x00000001 }, + { 0x001350, 1, 0x04, 0x00000002 }, + { 0x001358, 1, 0x04, 0x00000001 }, + { 0x0012e4, 1, 0x04, 0x00000000 }, + { 0x00131c, 1, 0x04, 0x00000000 }, + { 0x001320, 3, 0x04, 0x00000000 }, + { 0x0019c0, 1, 0x04, 0x00000000 }, + { 0x001140, 1, 0x04, 0x00000000 }, + { 0x0019c4, 1, 0x04, 0x00000000 }, + { 0x0019c8, 1, 0x04, 0x00001500 }, + { 0x00135c, 1, 0x04, 0x00000000 }, + { 0x000f90, 1, 0x04, 0x00000000 }, + { 0x0019e0, 8, 0x04, 0x00000001 }, + { 0x0019cc, 1, 0x04, 0x00000001 }, + { 0x0015b8, 1, 0x04, 0x00000000 }, + { 0x001a00, 1, 0x04, 0x00001111 }, + { 0x001a04, 7, 0x04, 0x00000000 }, + { 0x000d6c, 2, 0x04, 0xffff0000 }, + { 0x0010f8, 1, 0x04, 0x00001010 }, + { 0x000d80, 5, 0x04, 0x00000000 }, + { 0x000da0, 1, 0x04, 0x00000000 }, + { 0x001508, 1, 0x04, 0x80000000 }, + { 0x00150c, 1, 0x04, 0x40000000 }, + { 0x001668, 1, 0x04, 0x00000000 }, + { 0x000318, 2, 0x04, 0x00000008 }, + { 0x000d9c, 1, 0x04, 0x00000001 }, + { 0x0007dc, 1, 0x04, 0x00000000 }, + { 0x00074c, 1, 0x04, 0x00000055 }, + { 0x001420, 1, 0x04, 0x00000003 }, + { 0x0017bc, 2, 0x04, 0x00000000 }, + { 0x0017c4, 1, 0x04, 0x00000001 }, + { 0x001008, 1, 0x04, 0x00000008 }, + { 0x00100c, 1, 0x04, 0x00000040 }, + { 0x001010, 1, 0x04, 0x0000012c }, + { 0x000d60, 1, 0x04, 0x00000040 }, + { 0x00075c, 1, 0x04, 0x00000003 }, + { 0x001018, 1, 0x04, 0x00000020 }, + { 0x00101c, 1, 0x04, 0x00000001 }, + { 0x001020, 1, 0x04, 0x00000020 }, + { 0x001024, 1, 0x04, 0x00000001 }, + { 0x001444, 3, 0x04, 0x00000000 }, + { 0x000360, 1, 0x04, 0x20164010 }, + { 0x000364, 1, 0x04, 0x00000020 }, + { 0x000368, 1, 0x04, 0x00000000 }, + { 0x000de4, 1, 0x04, 0x00000000 }, + { 0x000204, 1, 0x04, 0x00000006 }, + { 0x000208, 1, 0x04, 0x00000000 }, + { 0x0002cc, 1, 0x04, 0x003fffff }, + { 0x0002d0, 1, 0x04, 0x00000c48 }, + { 0x001220, 1, 0x04, 0x00000005 }, + { 0x000fdc, 1, 0x04, 0x00000000 }, + { 0x000f98, 1, 0x04, 0x00300008 }, + { 0x001284, 1, 0x04, 0x04000080 }, + { 0x001450, 1, 0x04, 0x00300008 }, + { 0x001454, 1, 0x04, 0x04000080 }, + { 0x000214, 1, 0x04, 0x00000000 }, + {} +}; + +struct nvc0_graph_init +nvc0_grctx_init_902d[] = { + { 0x000200, 1, 0x04, 0x000000cf }, + { 0x000204, 1, 0x04, 0x00000001 }, + { 0x000208, 1, 0x04, 0x00000020 }, + { 0x00020c, 1, 0x04, 0x00000001 }, + { 0x000210, 1, 0x04, 0x00000000 }, + { 0x000214, 1, 0x04, 0x00000080 }, + { 0x000218, 2, 0x04, 0x00000100 }, + { 0x000220, 2, 0x04, 0x00000000 }, + { 0x000230, 1, 0x04, 0x000000cf }, + { 0x000234, 1, 0x04, 0x00000001 }, + { 0x000238, 1, 0x04, 0x00000020 }, + { 0x00023c, 1, 0x04, 0x00000001 }, + { 0x000244, 1, 0x04, 0x00000080 }, + { 0x000248, 2, 0x04, 0x00000100 }, + {} +}; + +struct nvc0_graph_init +nvc0_grctx_init_9039[] = { + { 0x00030c, 3, 0x04, 0x00000000 }, + { 0x000320, 1, 0x04, 0x00000000 }, + { 0x000238, 2, 0x04, 0x00000000 }, + { 0x000318, 2, 0x04, 0x00000000 }, + {} +}; + +struct nvc0_graph_init +nvc0_grctx_init_90c0[] = { + { 0x00270c, 8, 0x20, 0x00000000 }, + { 0x00030c, 1, 0x04, 0x00000001 }, + { 0x001944, 1, 0x04, 0x00000000 }, + { 0x000758, 1, 0x04, 0x00000100 }, + { 0x0002c4, 1, 0x04, 0x00000000 }, + { 0x000790, 5, 0x04, 0x00000000 }, + { 0x00077c, 1, 0x04, 0x00000000 }, + { 0x000204, 3, 0x04, 0x00000000 }, + { 0x000214, 1, 0x04, 0x00000000 }, + { 0x00024c, 1, 0x04, 0x00000000 }, + { 0x000d94, 1, 0x04, 0x00000001 }, + { 0x001608, 2, 0x04, 0x00000000 }, + { 0x001664, 1, 0x04, 0x00000000 }, + {} +}; + +struct nvc0_graph_init +nvc0_grctx_init_base[] = { + { 0x400204, 2, 0x04, 0x00000000 }, + {} +}; + +struct nvc0_graph_init +nvc0_grctx_init_unk40xx[] = { + { 0x404004, 10, 0x04, 0x00000000 }, + { 0x404044, 1, 0x04, 0x00000000 }, + { 0x404094, 1, 0x04, 0x00000000 }, + { 0x404098, 12, 0x04, 0x00000000 }, + { 0x4040c8, 1, 0x04, 0xf0000087 }, + { 0x4040d0, 6, 0x04, 0x00000000 }, + { 0x4040e8, 1, 0x04, 0x00001000 }, + { 0x4040f8, 1, 0x04, 0x00000000 }, + { 0x404130, 1, 0x04, 0x00000000 }, + { 0x404134, 1, 0x04, 0x00000000 }, + { 0x404138, 1, 0x04, 0x20000040 }, + { 0x404150, 1, 0x04, 0x0000002e }, + { 0x404154, 1, 0x04, 0x00000400 }, + { 0x404158, 1, 0x04, 0x00000200 }, + { 0x404164, 1, 0x04, 0x00000055 }, + { 0x404168, 1, 0x04, 0x00000000 }, + { 0x404174, 1, 0x04, 0x00000000 }, + { 0x404178, 2, 0x04, 0x00000000 }, + { 0x404200, 8, 0x04, 0x00000000 }, + {} +}; + +struct nvc0_graph_init +nvc0_grctx_init_unk44xx[] = { + { 0x404404, 14, 0x04, 0x00000000 }, + { 0x404460, 2, 0x04, 0x00000000 }, + { 0x404468, 1, 0x04, 0x00ffffff }, + { 0x40446c, 1, 0x04, 0x00000000 }, + { 0x404480, 1, 0x04, 0x00000001 }, + { 0x404498, 1, 0x04, 0x00000001 }, + {} +}; + +struct nvc0_graph_init +nvc0_grctx_init_unk46xx[] = { + { 0x404604, 1, 0x04, 0x00000015 }, + { 0x404608, 1, 0x04, 0x00000000 }, + { 0x40460c, 1, 0x04, 0x00002e00 }, + { 0x404610, 1, 0x04, 0x00000100 }, + { 0x404618, 8, 0x04, 0x00000000 }, + { 0x404638, 1, 0x04, 0x00000004 }, + { 0x40463c, 8, 0x04, 0x00000000 }, + { 0x40465c, 1, 0x04, 0x007f0100 }, + { 0x404660, 7, 0x04, 0x00000000 }, + { 0x40467c, 1, 0x04, 0x00000002 }, + { 0x404680, 8, 0x04, 0x00000000 }, + { 0x4046a0, 1, 0x04, 0x007f0080 }, + { 0x4046a4, 18, 0x04, 0x00000000 }, + { 0x4046f0, 2, 0x04, 0x00000000 }, + {} +}; + +struct nvc0_graph_init +nvc0_grctx_init_unk47xx[] = { + { 0x404700, 13, 0x04, 0x00000000 }, + { 0x404734, 1, 0x04, 0x00000100 }, + { 0x404738, 8, 0x04, 0x00000000 }, + {} +}; + +struct nvc0_graph_init +nvc0_grctx_init_unk58xx[] = { + { 0x405800, 1, 0x04, 0x078000bf }, + { 0x405830, 1, 0x04, 0x02180000 }, + { 0x405834, 2, 0x04, 0x00000000 }, + { 0x405854, 1, 0x04, 0x00000000 }, + { 0x405870, 4, 0x04, 0x00000001 }, + { 0x405a00, 2, 0x04, 0x00000000 }, + { 0x405a18, 1, 0x04, 0x00000000 }, + {} +}; + +struct nvc0_graph_init +nvc0_grctx_init_unk60xx[] = { + { 0x406020, 1, 0x04, 0x000103c1 }, + { 0x406028, 4, 0x04, 0x00000001 }, + {} +}; + +struct nvc0_graph_init +nvc0_grctx_init_unk64xx[] = { + { 0x4064a8, 1, 0x04, 0x00000000 }, + { 0x4064ac, 1, 0x04, 0x00003fff }, + { 0x4064b4, 2, 0x04, 0x00000000 }, + {} +}; + +struct nvc0_graph_init +nvc0_grctx_init_unk78xx[] = { + { 0x407804, 1, 0x04, 0x00000023 }, + { 0x40780c, 1, 0x04, 0x0a418820 }, + { 0x407810, 1, 0x04, 0x062080e6 }, + { 0x407814, 1, 0x04, 0x020398a4 }, + { 0x407818, 1, 0x04, 0x0e629062 }, + { 0x40781c, 1, 0x04, 0x0a418820 }, + { 0x407820, 1, 0x04, 0x000000e6 }, + { 0x4078bc, 1, 0x04, 0x00000103 }, + {} +}; + +struct nvc0_graph_init +nvc0_grctx_init_unk80xx[] = { + { 0x408000, 2, 0x04, 0x00000000 }, + { 0x408008, 1, 0x04, 0x00000018 }, + { 0x40800c, 2, 0x04, 0x00000000 }, + { 0x408014, 1, 0x04, 0x00000069 }, + { 0x408018, 1, 0x04, 0xe100e100 }, + { 0x408064, 1, 0x04, 0x00000000 }, + {} +}; + +struct nvc0_graph_init +nvc0_grctx_init_rop[] = { + { 0x408800, 1, 0x04, 0x02802a3c }, + { 0x408804, 1, 0x04, 0x00000040 }, + { 0x408808, 1, 0x04, 0x0003e00d }, + { 0x408900, 1, 0x04, 0x3080b801 }, + { 0x408904, 1, 0x04, 0x02000001 }, + { 0x408908, 1, 0x04, 0x00c80929 }, + { 0x408980, 1, 0x04, 0x0000011d }, + {} +}; + +struct nvc0_graph_init +nvc0_grctx_init_gpc[] = { + { 0x418380, 1, 0x04, 0x00000016 }, + { 0x418400, 1, 0x04, 0x38004e00 }, + { 0x418404, 1, 0x04, 0x71e0ffff }, + { 0x418408, 1, 0x04, 0x00000000 }, + { 0x41840c, 1, 0x04, 0x00001008 }, + { 0x418410, 1, 0x04, 0x0fff0fff }, + { 0x418414, 1, 0x04, 0x00200fff }, + { 0x418450, 6, 0x04, 0x00000000 }, + { 0x418468, 1, 0x04, 0x00000001 }, + { 0x41846c, 2, 0x04, 0x00000000 }, + { 0x418600, 1, 0x04, 0x0000001f }, + { 0x418684, 1, 0x04, 0x0000000f }, + { 0x418700, 1, 0x04, 0x00000002 }, + { 0x418704, 1, 0x04, 0x00000080 }, + { 0x418708, 1, 0x04, 0x00000000 }, + { 0x41870c, 1, 0x04, 0x07c80000 }, + { 0x418710, 1, 0x04, 0x00000000 }, + { 0x418800, 1, 0x04, 0x0006860a }, + { 0x418808, 3, 0x04, 0x00000000 }, + { 0x418828, 1, 0x04, 0x00008442 }, + { 0x418830, 1, 0x04, 0x00000001 }, + { 0x4188d8, 1, 0x04, 0x00000008 }, + { 0x4188e0, 1, 0x04, 0x01000000 }, + { 0x4188e8, 5, 0x04, 0x00000000 }, + { 0x4188fc, 1, 0x04, 0x00100000 }, + { 0x41891c, 1, 0x04, 0x00ff00ff }, + { 0x418924, 1, 0x04, 0x00000000 }, + { 0x418928, 1, 0x04, 0x00ffff00 }, + { 0x41892c, 1, 0x04, 0x0000ff00 }, + { 0x418a00, 3, 0x04, 0x00000000 }, + { 0x418a0c, 1, 0x04, 0x00010000 }, + { 0x418a10, 3, 0x04, 0x00000000 }, + { 0x418a20, 3, 0x04, 0x00000000 }, + { 0x418a2c, 1, 0x04, 0x00010000 }, + { 0x418a30, 3, 0x04, 0x00000000 }, + { 0x418a40, 3, 0x04, 0x00000000 }, + { 0x418a4c, 1, 0x04, 0x00010000 }, + { 0x418a50, 3, 0x04, 0x00000000 }, + { 0x418a60, 3, 0x04, 0x00000000 }, + { 0x418a6c, 1, 0x04, 0x00010000 }, + { 0x418a70, 3, 0x04, 0x00000000 }, + { 0x418a80, 3, 0x04, 0x00000000 }, + { 0x418a8c, 1, 0x04, 0x00010000 }, + { 0x418a90, 3, 0x04, 0x00000000 }, + { 0x418aa0, 3, 0x04, 0x00000000 }, + { 0x418aac, 1, 0x04, 0x00010000 }, + { 0x418ab0, 3, 0x04, 0x00000000 }, + { 0x418ac0, 3, 0x04, 0x00000000 }, + { 0x418acc, 1, 0x04, 0x00010000 }, + { 0x418ad0, 3, 0x04, 0x00000000 }, + { 0x418ae0, 3, 0x04, 0x00000000 }, + { 0x418aec, 1, 0x04, 0x00010000 }, + { 0x418af0, 3, 0x04, 0x00000000 }, + { 0x418b00, 1, 0x04, 0x00000000 }, + { 0x418b08, 1, 0x04, 0x0a418820 }, + { 0x418b0c, 1, 0x04, 0x062080e6 }, + { 0x418b10, 1, 0x04, 0x020398a4 }, + { 0x418b14, 1, 0x04, 0x0e629062 }, + { 0x418b18, 1, 0x04, 0x0a418820 }, + { 0x418b1c, 1, 0x04, 0x000000e6 }, + { 0x418bb8, 1, 0x04, 0x00000103 }, + { 0x418c08, 1, 0x04, 0x00000001 }, + { 0x418c10, 8, 0x04, 0x00000000 }, + { 0x418c80, 1, 0x04, 0x20200004 }, + { 0x418c8c, 1, 0x04, 0x00000001 }, + { 0x419000, 1, 0x04, 0x00000780 }, + { 0x419004, 2, 0x04, 0x00000000 }, + { 0x419014, 1, 0x04, 0x00000004 }, + {} +}; + +struct nvc0_graph_init +nvc0_grctx_init_tpc[] = { + { 0x419818, 1, 0x04, 0x00000000 }, + { 0x41983c, 1, 0x04, 0x00038bc7 }, + { 0x419848, 1, 0x04, 0x00000000 }, + { 0x419864, 1, 0x04, 0x0000012a }, + { 0x419888, 1, 0x04, 0x00000000 }, + { 0x419a00, 1, 0x04, 0x000001f0 }, + { 0x419a04, 1, 0x04, 0x00000001 }, + { 0x419a08, 1, 0x04, 0x00000023 }, + { 0x419a0c, 1, 0x04, 0x00020000 }, + { 0x419a10, 1, 0x04, 0x00000000 }, + { 0x419a14, 1, 0x04, 0x00000200 }, + { 0x419b00, 1, 0x04, 0x0a418820 }, + { 0x419b04, 1, 0x04, 0x062080e6 }, + { 0x419b08, 1, 0x04, 0x020398a4 }, + { 0x419b0c, 1, 0x04, 0x0e629062 }, + { 0x419b10, 1, 0x04, 0x0a418820 }, + { 0x419b14, 1, 0x04, 0x000000e6 }, + { 0x419bd0, 1, 0x04, 0x00900103 }, + { 0x419be0, 1, 0x04, 0x00000001 }, + { 0x419be4, 1, 0x04, 0x00000000 }, + { 0x419c00, 1, 0x04, 0x00000002 }, + { 0x419c04, 1, 0x04, 0x00000006 }, + { 0x419c08, 1, 0x04, 0x00000002 }, + { 0x419c20, 1, 0x04, 0x00000000 }, + { 0x419cb0, 1, 0x04, 0x00060048 }, + { 0x419ce8, 1, 0x04, 0x00000000 }, + { 0x419cf4, 1, 0x04, 0x00000183 }, + { 0x419d20, 1, 0x04, 0x02180000 }, + { 0x419d24, 1, 0x04, 0x00001fff }, + { 0x419e04, 3, 0x04, 0x00000000 }, + { 0x419e10, 1, 0x04, 0x00000002 }, + { 0x419e44, 1, 0x04, 0x001beff2 }, + { 0x419e48, 1, 0x04, 0x00000000 }, + { 0x419e4c, 1, 0x04, 0x0000000f }, + { 0x419e50, 17, 0x04, 0x00000000 }, + { 0x419e98, 1, 0x04, 0x00000000 }, + { 0x419f50, 2, 0x04, 0x00000000 }, + {} +}; + +void +nvc0_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info) +{ + int gpc, tpc; + u32 offset; + + mmio_data(0x002000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS); + mmio_data(0x008000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS); + mmio_data(0x060000, 0x1000, NV_MEM_ACCESS_RW); + + mmio_list(0x408004, 0x00000000, 8, 0); + mmio_list(0x408008, 0x80000018, 0, 0); + mmio_list(0x40800c, 0x00000000, 8, 1); + mmio_list(0x408010, 0x80000000, 0, 0); + mmio_list(0x418810, 0x80000000, 12, 2); + mmio_list(0x419848, 0x10000000, 12, 2); + mmio_list(0x419004, 0x00000000, 8, 1); + mmio_list(0x419008, 0x00000000, 0, 0); + mmio_list(0x418808, 0x00000000, 8, 0); + mmio_list(0x41880c, 0x80000018, 0, 0); + + mmio_list(0x405830, 0x02180000, 0, 0); + + for (gpc = 0, offset = 0; gpc < priv->gpc_nr; gpc++) { + for (tpc = 0; tpc < priv->tpc_nr[gpc]; tpc++) { + u32 addr = TPC_UNIT(gpc, tpc, 0x0520); + mmio_list(addr, 0x02180000 | offset, 0, 0); + offset += 0x0324; + } + } +} + void -nv_icmd(struct nvc0_graph_priv *priv, u32 icmd, u32 data) +nvc0_grctx_generate_tpcid(struct nvc0_graph_priv *priv) { - nv_wr32(priv, 0x400204, data); - nv_wr32(priv, 0x400200, icmd); - while (nv_rd32(priv, 0x400700) & 2) {} + int gpc, tpc, id; + + for (tpc = 0, id = 0; tpc < 4; tpc++) { + for (gpc = 0; gpc < priv->gpc_nr; gpc++) { + if (tpc < priv->tpc_nr[gpc]) { + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x698), id); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x4e8), id); + nv_wr32(priv, GPC_UNIT(gpc, 0x0c10 + tpc * 4), id); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x088), id); + id++; + } + + nv_wr32(priv, GPC_UNIT(gpc, 0x0c08), priv->tpc_nr[gpc]); + nv_wr32(priv, GPC_UNIT(gpc, 0x0c8c), priv->tpc_nr[gpc]); + } + } +} + +void +nvc0_grctx_generate_r406028(struct nvc0_graph_priv *priv) +{ + u32 tmp = 0, i; + for (i = 0; i < priv->gpc_nr; i++) + tmp |= priv->tpc_nr[i] << (i * 4); + nv_wr32(priv, 0x406028, tmp); + nv_wr32(priv, 0x405870, tmp); +} + +void +nvc0_grctx_generate_r4060a8(struct nvc0_graph_priv *priv) +{ + u8 tpcnr[GPC_MAX], data[TPC_MAX]; + int gpc, tpc, i; + + memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr)); + memset(data, 0x1f, sizeof(data)); + + gpc = -1; + for (tpc = 0; tpc < priv->tpc_total; tpc++) { + do { + gpc = (gpc + 1) % priv->gpc_nr; + } while (!tpcnr[gpc]); + tpcnr[gpc]--; + data[tpc] = gpc; + } + + for (i = 0; i < 4; i++) + nv_wr32(priv, 0x4060a8 + (i * 4), ((u32 *)data)[i]); +} + +void +nvc0_grctx_generate_r418bb8(struct nvc0_graph_priv *priv) +{ + u32 data[6] = {}, data2[2] = {}; + u8 tpcnr[GPC_MAX]; + u8 shift, ntpcv; + int gpc, tpc, i; + + /* calculate first set of magics */ + memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr)); + + gpc = -1; + for (tpc = 0; tpc < priv->tpc_total; tpc++) { + do { + gpc = (gpc + 1) % priv->gpc_nr; + } while (!tpcnr[gpc]); + tpcnr[gpc]--; + + data[tpc / 6] |= gpc << ((tpc % 6) * 5); + } + + for (; tpc < 32; tpc++) + data[tpc / 6] |= 7 << ((tpc % 6) * 5); + + /* and the second... */ + shift = 0; + ntpcv = priv->tpc_total; + while (!(ntpcv & (1 << 4))) { + ntpcv <<= 1; + shift++; + } + + data2[0] = (ntpcv << 16); + data2[0] |= (shift << 21); + data2[0] |= (((1 << (0 + 5)) % ntpcv) << 24); + for (i = 1; i < 7; i++) + data2[1] |= ((1 << (i + 5)) % ntpcv) << ((i - 1) * 5); + + /* GPC_BROADCAST */ + nv_wr32(priv, 0x418bb8, (priv->tpc_total << 8) | + priv->magic_not_rop_nr); + for (i = 0; i < 6; i++) + nv_wr32(priv, 0x418b08 + (i * 4), data[i]); + + /* GPC_BROADCAST.TP_BROADCAST */ + nv_wr32(priv, 0x419bd0, (priv->tpc_total << 8) | + priv->magic_not_rop_nr | data2[0]); + nv_wr32(priv, 0x419be4, data2[1]); + for (i = 0; i < 6; i++) + nv_wr32(priv, 0x419b00 + (i * 4), data[i]); + + /* UNK78xx */ + nv_wr32(priv, 0x4078bc, (priv->tpc_total << 8) | + priv->magic_not_rop_nr); + for (i = 0; i < 6; i++) + nv_wr32(priv, 0x40780c + (i * 4), data[i]); +} + +void +nvc0_grctx_generate_r406800(struct nvc0_graph_priv *priv) +{ + u32 tpc_mask = 0, tpc_set = 0; + u8 tpcnr[GPC_MAX], a, b; + int gpc, tpc, i; + + memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr)); + for (gpc = 0; gpc < priv->gpc_nr; gpc++) + tpc_mask |= ((1 << priv->tpc_nr[gpc]) - 1) << (gpc * 8); + + for (i = 0, gpc = -1, b = -1; i < 32; i++) { + a = (i * (priv->tpc_total - 1)) / 32; + if (a != b) { + b = a; + do { + gpc = (gpc + 1) % priv->gpc_nr; + } while (!tpcnr[gpc]); + tpc = priv->tpc_nr[gpc] - tpcnr[gpc]--; + + tpc_set |= 1 << ((gpc * 8) + tpc); + } + + nv_wr32(priv, 0x406800 + (i * 0x20), tpc_set); + nv_wr32(priv, 0x406c00 + (i * 0x20), tpc_set ^ tpc_mask); + } +} + +void +nvc0_grctx_generate_main(struct nvc0_graph_priv *priv, struct nvc0_grctx *info) +{ + struct nvc0_grctx_oclass *oclass = (void *)nv_engine(priv)->cclass; + int i; + + nv_mask(priv, 0x000260, 0x00000001, 0x00000000); + + for (i = 0; oclass->mmio[i]; i++) + nvc0_graph_mmio(priv, oclass->mmio[i]); + nvc0_graph_mmio(priv, oclass->gpc); + nvc0_graph_mmio(priv, oclass->tpc); + + nv_wr32(priv, 0x404154, 0x00000000); + + oclass->mods(priv, info); + + nvc0_grctx_generate_tpcid(priv); + nvc0_grctx_generate_r406028(priv); + + nv_wr32(priv, 0x40602c, 0x00000000); + nv_wr32(priv, 0x405874, 0x00000000); + nv_wr32(priv, 0x406030, 0x00000000); + nv_wr32(priv, 0x405878, 0x00000000); + nv_wr32(priv, 0x406034, 0x00000000); + nv_wr32(priv, 0x40587c, 0x00000000); + + nvc0_grctx_generate_r4060a8(priv); + nvc0_grctx_generate_r418bb8(priv); + nvc0_grctx_generate_r406800(priv); + + nvc0_graph_icmd(priv, oclass->icmd); + nv_wr32(priv, 0x404154, 0x00000400); + nvc0_graph_mthd(priv, oclass->mthd); + nv_mask(priv, 0x000260, 0x00000001, 0x00000001); } int -nvc0_grctx_init(struct nvc0_graph_priv *priv, struct nvc0_grctx *info) +nvc0_grctx_generate(struct nvc0_graph_priv *priv) { + struct nvc0_grctx_oclass *oclass = (void *)nv_engine(priv)->cclass; struct nouveau_bar *bar = nouveau_bar(priv); struct nouveau_gpuobj *chan; - u32 size = (0x80000 + priv->size + 4095) & ~4095; + struct nvc0_grctx info; int ret, i; /* allocate memory to for a "channel", which we'll use to generate * the default context values */ - ret = nouveau_gpuobj_new(nv_object(priv), NULL, size, 0x1000, - NVOBJ_FLAG_ZERO_ALLOC, &info->chan); - chan = info->chan; + ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x80000 + priv->size, + 0x1000, NVOBJ_FLAG_ZERO_ALLOC, &chan); if (ret) { nv_error(priv, "failed to allocate channel memory, %d\n", ret); return ret; @@ -62,7 +1101,7 @@ nvc0_grctx_init(struct nvc0_graph_priv *priv, struct nvc0_grctx *info) nv_wo32(chan, 0x1004, 0x00000001 | (chan->addr + 0x2000) >> 8); /* identity-map the whole "channel" into its own vm */ - for (i = 0; i < size / 4096; i++) { + for (i = 0; i < chan->size / 4096; i++) { u64 addr = ((chan->addr + (i * 4096)) >> 8) | 1; nv_wo32(chan, 0x2000 + (i * 8), lower_32_bits(addr)); nv_wo32(chan, 0x2004 + (i * 8), upper_32_bits(addr)); @@ -79,12 +1118,13 @@ nvc0_grctx_init(struct nvc0_graph_priv *priv, struct nvc0_grctx *info) nv_wait(priv, 0x100c80, 0x00008000, 0x00008000); /* setup default state for mmio list construction */ - info->data = priv->mmio_data; - info->mmio = priv->mmio_list; - info->addr = 0x2000 + (i * 8); - info->priv = priv; - info->buffer_nr = 0; + info.priv = priv; + info.data = priv->mmio_data; + info.mmio = priv->mmio_list; + info.addr = 0x2000 + (i * 8); + info.buffer_nr = 0; + /* make channel current */ if (priv->firmware) { nv_wr32(priv, 0x409840, 0x00000030); nv_wr32(priv, 0x409500, 0x80000000 | chan->addr >> 12); @@ -97,58 +1137,15 @@ nvc0_grctx_init(struct nvc0_graph_priv *priv, struct nvc0_grctx *info) nv_wo32(chan, 0x80028, 0); nv_wo32(chan, 0x8002c, 0); bar->flush(bar); - return 0; - } - - /* HUB_FUC(SET_CHAN) */ - nv_wr32(priv, 0x409840, 0x80000000); - nv_wr32(priv, 0x409500, 0x80000000 | chan->addr >> 12); - nv_wr32(priv, 0x409504, 0x00000001); - if (!nv_wait(priv, 0x409800, 0x80000000, 0x80000000)) { - nv_error(priv, "HUB_SET_CHAN timeout\n"); - nvc0_graph_ctxctl_debug(priv); - nouveau_gpuobj_ref(NULL, &info->chan); - return -EBUSY; + } else { + nv_wr32(priv, 0x409840, 0x80000000); + nv_wr32(priv, 0x409500, 0x80000000 | chan->addr >> 12); + nv_wr32(priv, 0x409504, 0x00000001); + if (!nv_wait(priv, 0x409800, 0x80000000, 0x80000000)) + nv_error(priv, "HUB_SET_CHAN timeout\n"); } - return 0; -} - -void -nvc0_grctx_data(struct nvc0_grctx *info, u32 size, u32 align, u32 access) -{ - info->buffer[info->buffer_nr] = info->addr; - info->buffer[info->buffer_nr] += (align - 1); - info->buffer[info->buffer_nr] &= ~(align - 1); - info->addr = info->buffer[info->buffer_nr++] + size; - - info->data->size = size; - info->data->align = align; - info->data->access = access; - info->data++; -} - -void -nvc0_grctx_mmio(struct nvc0_grctx *info, u32 addr, u32 data, u32 shift, u32 buf) -{ - struct nvc0_graph_priv *priv = info->priv; - - info->mmio->addr = addr; - info->mmio->data = data; - info->mmio->shift = shift; - info->mmio->buffer = buf; - info->mmio++; - - if (shift) - data |= info->buffer[buf] >> shift; - nv_wr32(priv, addr, data); -} - -int -nvc0_grctx_fini(struct nvc0_grctx *info) -{ - struct nvc0_graph_priv *priv = info->priv; - int i; + oclass->main(priv, &info); /* trigger a context unload by unsetting the "next channel valid" bit * and faking a context switch interrupt @@ -157,3323 +1154,72 @@ nvc0_grctx_fini(struct nvc0_grctx *info) nv_wr32(priv, 0x409000, 0x00000100); if (!nv_wait(priv, 0x409b00, 0x80000000, 0x00000000)) { nv_error(priv, "grctx template channel unload timeout\n"); - return -EBUSY; + ret = -EBUSY; + goto done; } priv->data = kmalloc(priv->size, GFP_KERNEL); if (priv->data) { for (i = 0; i < priv->size; i += 4) - priv->data[i / 4] = nv_ro32(info->chan, 0x80000 + i); + priv->data[i / 4] = nv_ro32(chan, 0x80000 + i); + ret = 0; + } else { + ret = -ENOMEM; } - nouveau_gpuobj_ref(NULL, &info->chan); - return priv->data ? 0 : -ENOMEM; -} - -static void -nvc0_grctx_generate_9097(struct nvc0_graph_priv *priv) -{ - u32 fermi = nvc0_graph_class(priv); - u32 mthd; - - nv_mthd(priv, 0x9097, 0x0800, 0x00000000); - nv_mthd(priv, 0x9097, 0x0840, 0x00000000); - nv_mthd(priv, 0x9097, 0x0880, 0x00000000); - nv_mthd(priv, 0x9097, 0x08c0, 0x00000000); - nv_mthd(priv, 0x9097, 0x0900, 0x00000000); - nv_mthd(priv, 0x9097, 0x0940, 0x00000000); - nv_mthd(priv, 0x9097, 0x0980, 0x00000000); - nv_mthd(priv, 0x9097, 0x09c0, 0x00000000); - nv_mthd(priv, 0x9097, 0x0804, 0x00000000); - nv_mthd(priv, 0x9097, 0x0844, 0x00000000); - nv_mthd(priv, 0x9097, 0x0884, 0x00000000); - nv_mthd(priv, 0x9097, 0x08c4, 0x00000000); - nv_mthd(priv, 0x9097, 0x0904, 0x00000000); - nv_mthd(priv, 0x9097, 0x0944, 0x00000000); - nv_mthd(priv, 0x9097, 0x0984, 0x00000000); - nv_mthd(priv, 0x9097, 0x09c4, 0x00000000); - nv_mthd(priv, 0x9097, 0x0808, 0x00000400); - nv_mthd(priv, 0x9097, 0x0848, 0x00000400); - nv_mthd(priv, 0x9097, 0x0888, 0x00000400); - nv_mthd(priv, 0x9097, 0x08c8, 0x00000400); - nv_mthd(priv, 0x9097, 0x0908, 0x00000400); - nv_mthd(priv, 0x9097, 0x0948, 0x00000400); - nv_mthd(priv, 0x9097, 0x0988, 0x00000400); - nv_mthd(priv, 0x9097, 0x09c8, 0x00000400); - nv_mthd(priv, 0x9097, 0x080c, 0x00000300); - nv_mthd(priv, 0x9097, 0x084c, 0x00000300); - nv_mthd(priv, 0x9097, 0x088c, 0x00000300); - nv_mthd(priv, 0x9097, 0x08cc, 0x00000300); - nv_mthd(priv, 0x9097, 0x090c, 0x00000300); - nv_mthd(priv, 0x9097, 0x094c, 0x00000300); - nv_mthd(priv, 0x9097, 0x098c, 0x00000300); - nv_mthd(priv, 0x9097, 0x09cc, 0x00000300); - nv_mthd(priv, 0x9097, 0x0810, 0x000000cf); - nv_mthd(priv, 0x9097, 0x0850, 0x00000000); - nv_mthd(priv, 0x9097, 0x0890, 0x00000000); - nv_mthd(priv, 0x9097, 0x08d0, 0x00000000); - nv_mthd(priv, 0x9097, 0x0910, 0x00000000); - nv_mthd(priv, 0x9097, 0x0950, 0x00000000); - nv_mthd(priv, 0x9097, 0x0990, 0x00000000); - nv_mthd(priv, 0x9097, 0x09d0, 0x00000000); - nv_mthd(priv, 0x9097, 0x0814, 0x00000040); - nv_mthd(priv, 0x9097, 0x0854, 0x00000040); - nv_mthd(priv, 0x9097, 0x0894, 0x00000040); - nv_mthd(priv, 0x9097, 0x08d4, 0x00000040); - nv_mthd(priv, 0x9097, 0x0914, 0x00000040); - nv_mthd(priv, 0x9097, 0x0954, 0x00000040); - nv_mthd(priv, 0x9097, 0x0994, 0x00000040); - nv_mthd(priv, 0x9097, 0x09d4, 0x00000040); - nv_mthd(priv, 0x9097, 0x0818, 0x00000001); - nv_mthd(priv, 0x9097, 0x0858, 0x00000001); - nv_mthd(priv, 0x9097, 0x0898, 0x00000001); - nv_mthd(priv, 0x9097, 0x08d8, 0x00000001); - nv_mthd(priv, 0x9097, 0x0918, 0x00000001); - nv_mthd(priv, 0x9097, 0x0958, 0x00000001); - nv_mthd(priv, 0x9097, 0x0998, 0x00000001); - nv_mthd(priv, 0x9097, 0x09d8, 0x00000001); - nv_mthd(priv, 0x9097, 0x081c, 0x00000000); - nv_mthd(priv, 0x9097, 0x085c, 0x00000000); - nv_mthd(priv, 0x9097, 0x089c, 0x00000000); - nv_mthd(priv, 0x9097, 0x08dc, 0x00000000); - nv_mthd(priv, 0x9097, 0x091c, 0x00000000); - nv_mthd(priv, 0x9097, 0x095c, 0x00000000); - nv_mthd(priv, 0x9097, 0x099c, 0x00000000); - nv_mthd(priv, 0x9097, 0x09dc, 0x00000000); - nv_mthd(priv, 0x9097, 0x0820, 0x00000000); - nv_mthd(priv, 0x9097, 0x0860, 0x00000000); - nv_mthd(priv, 0x9097, 0x08a0, 0x00000000); - nv_mthd(priv, 0x9097, 0x08e0, 0x00000000); - nv_mthd(priv, 0x9097, 0x0920, 0x00000000); - nv_mthd(priv, 0x9097, 0x0960, 0x00000000); - nv_mthd(priv, 0x9097, 0x09a0, 0x00000000); - nv_mthd(priv, 0x9097, 0x09e0, 0x00000000); - nv_mthd(priv, 0x9097, 0x2700, 0x00000000); - nv_mthd(priv, 0x9097, 0x2720, 0x00000000); - nv_mthd(priv, 0x9097, 0x2740, 0x00000000); - nv_mthd(priv, 0x9097, 0x2760, 0x00000000); - nv_mthd(priv, 0x9097, 0x2780, 0x00000000); - nv_mthd(priv, 0x9097, 0x27a0, 0x00000000); - nv_mthd(priv, 0x9097, 0x27c0, 0x00000000); - nv_mthd(priv, 0x9097, 0x27e0, 0x00000000); - nv_mthd(priv, 0x9097, 0x2704, 0x00000000); - nv_mthd(priv, 0x9097, 0x2724, 0x00000000); - nv_mthd(priv, 0x9097, 0x2744, 0x00000000); - nv_mthd(priv, 0x9097, 0x2764, 0x00000000); - nv_mthd(priv, 0x9097, 0x2784, 0x00000000); - nv_mthd(priv, 0x9097, 0x27a4, 0x00000000); - nv_mthd(priv, 0x9097, 0x27c4, 0x00000000); - nv_mthd(priv, 0x9097, 0x27e4, 0x00000000); - nv_mthd(priv, 0x9097, 0x2708, 0x00000000); - nv_mthd(priv, 0x9097, 0x2728, 0x00000000); - nv_mthd(priv, 0x9097, 0x2748, 0x00000000); - nv_mthd(priv, 0x9097, 0x2768, 0x00000000); - nv_mthd(priv, 0x9097, 0x2788, 0x00000000); - nv_mthd(priv, 0x9097, 0x27a8, 0x00000000); - nv_mthd(priv, 0x9097, 0x27c8, 0x00000000); - nv_mthd(priv, 0x9097, 0x27e8, 0x00000000); - nv_mthd(priv, 0x9097, 0x270c, 0x00000000); - nv_mthd(priv, 0x9097, 0x272c, 0x00000000); - nv_mthd(priv, 0x9097, 0x274c, 0x00000000); - nv_mthd(priv, 0x9097, 0x276c, 0x00000000); - nv_mthd(priv, 0x9097, 0x278c, 0x00000000); - nv_mthd(priv, 0x9097, 0x27ac, 0x00000000); - nv_mthd(priv, 0x9097, 0x27cc, 0x00000000); - nv_mthd(priv, 0x9097, 0x27ec, 0x00000000); - nv_mthd(priv, 0x9097, 0x2710, 0x00014000); - nv_mthd(priv, 0x9097, 0x2730, 0x00014000); - nv_mthd(priv, 0x9097, 0x2750, 0x00014000); - nv_mthd(priv, 0x9097, 0x2770, 0x00014000); - nv_mthd(priv, 0x9097, 0x2790, 0x00014000); - nv_mthd(priv, 0x9097, 0x27b0, 0x00014000); - nv_mthd(priv, 0x9097, 0x27d0, 0x00014000); - nv_mthd(priv, 0x9097, 0x27f0, 0x00014000); - nv_mthd(priv, 0x9097, 0x2714, 0x00000040); - nv_mthd(priv, 0x9097, 0x2734, 0x00000040); - nv_mthd(priv, 0x9097, 0x2754, 0x00000040); - nv_mthd(priv, 0x9097, 0x2774, 0x00000040); - nv_mthd(priv, 0x9097, 0x2794, 0x00000040); - nv_mthd(priv, 0x9097, 0x27b4, 0x00000040); - nv_mthd(priv, 0x9097, 0x27d4, 0x00000040); - nv_mthd(priv, 0x9097, 0x27f4, 0x00000040); - nv_mthd(priv, 0x9097, 0x1c00, 0x00000000); - nv_mthd(priv, 0x9097, 0x1c10, 0x00000000); - nv_mthd(priv, 0x9097, 0x1c20, 0x00000000); - nv_mthd(priv, 0x9097, 0x1c30, 0x00000000); - nv_mthd(priv, 0x9097, 0x1c40, 0x00000000); - nv_mthd(priv, 0x9097, 0x1c50, 0x00000000); - nv_mthd(priv, 0x9097, 0x1c60, 0x00000000); - nv_mthd(priv, 0x9097, 0x1c70, 0x00000000); - nv_mthd(priv, 0x9097, 0x1c80, 0x00000000); - nv_mthd(priv, 0x9097, 0x1c90, 0x00000000); - nv_mthd(priv, 0x9097, 0x1ca0, 0x00000000); - nv_mthd(priv, 0x9097, 0x1cb0, 0x00000000); - nv_mthd(priv, 0x9097, 0x1cc0, 0x00000000); - nv_mthd(priv, 0x9097, 0x1cd0, 0x00000000); - nv_mthd(priv, 0x9097, 0x1ce0, 0x00000000); - nv_mthd(priv, 0x9097, 0x1cf0, 0x00000000); - nv_mthd(priv, 0x9097, 0x1c04, 0x00000000); - nv_mthd(priv, 0x9097, 0x1c14, 0x00000000); - nv_mthd(priv, 0x9097, 0x1c24, 0x00000000); - nv_mthd(priv, 0x9097, 0x1c34, 0x00000000); - nv_mthd(priv, 0x9097, 0x1c44, 0x00000000); - nv_mthd(priv, 0x9097, 0x1c54, 0x00000000); - nv_mthd(priv, 0x9097, 0x1c64, 0x00000000); - nv_mthd(priv, 0x9097, 0x1c74, 0x00000000); - nv_mthd(priv, 0x9097, 0x1c84, 0x00000000); - nv_mthd(priv, 0x9097, 0x1c94, 0x00000000); - nv_mthd(priv, 0x9097, 0x1ca4, 0x00000000); - nv_mthd(priv, 0x9097, 0x1cb4, 0x00000000); - nv_mthd(priv, 0x9097, 0x1cc4, 0x00000000); - nv_mthd(priv, 0x9097, 0x1cd4, 0x00000000); - nv_mthd(priv, 0x9097, 0x1ce4, 0x00000000); - nv_mthd(priv, 0x9097, 0x1cf4, 0x00000000); - nv_mthd(priv, 0x9097, 0x1c08, 0x00000000); - nv_mthd(priv, 0x9097, 0x1c18, 0x00000000); - nv_mthd(priv, 0x9097, 0x1c28, 0x00000000); - nv_mthd(priv, 0x9097, 0x1c38, 0x00000000); - nv_mthd(priv, 0x9097, 0x1c48, 0x00000000); - nv_mthd(priv, 0x9097, 0x1c58, 0x00000000); - nv_mthd(priv, 0x9097, 0x1c68, 0x00000000); - nv_mthd(priv, 0x9097, 0x1c78, 0x00000000); - nv_mthd(priv, 0x9097, 0x1c88, 0x00000000); - nv_mthd(priv, 0x9097, 0x1c98, 0x00000000); - nv_mthd(priv, 0x9097, 0x1ca8, 0x00000000); - nv_mthd(priv, 0x9097, 0x1cb8, 0x00000000); - nv_mthd(priv, 0x9097, 0x1cc8, 0x00000000); - nv_mthd(priv, 0x9097, 0x1cd8, 0x00000000); - nv_mthd(priv, 0x9097, 0x1ce8, 0x00000000); - nv_mthd(priv, 0x9097, 0x1cf8, 0x00000000); - nv_mthd(priv, 0x9097, 0x1c0c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1c1c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1c2c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1c3c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1c4c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1c5c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1c6c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1c7c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1c8c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1c9c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1cac, 0x00000000); - nv_mthd(priv, 0x9097, 0x1cbc, 0x00000000); - nv_mthd(priv, 0x9097, 0x1ccc, 0x00000000); - nv_mthd(priv, 0x9097, 0x1cdc, 0x00000000); - nv_mthd(priv, 0x9097, 0x1cec, 0x00000000); - nv_mthd(priv, 0x9097, 0x1cfc, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d00, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d10, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d20, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d30, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d40, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d50, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d60, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d70, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d80, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d90, 0x00000000); - nv_mthd(priv, 0x9097, 0x1da0, 0x00000000); - nv_mthd(priv, 0x9097, 0x1db0, 0x00000000); - nv_mthd(priv, 0x9097, 0x1dc0, 0x00000000); - nv_mthd(priv, 0x9097, 0x1dd0, 0x00000000); - nv_mthd(priv, 0x9097, 0x1de0, 0x00000000); - nv_mthd(priv, 0x9097, 0x1df0, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d04, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d14, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d24, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d34, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d44, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d54, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d64, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d74, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d84, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d94, 0x00000000); - nv_mthd(priv, 0x9097, 0x1da4, 0x00000000); - nv_mthd(priv, 0x9097, 0x1db4, 0x00000000); - nv_mthd(priv, 0x9097, 0x1dc4, 0x00000000); - nv_mthd(priv, 0x9097, 0x1dd4, 0x00000000); - nv_mthd(priv, 0x9097, 0x1de4, 0x00000000); - nv_mthd(priv, 0x9097, 0x1df4, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d08, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d18, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d28, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d38, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d48, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d58, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d68, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d78, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d88, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d98, 0x00000000); - nv_mthd(priv, 0x9097, 0x1da8, 0x00000000); - nv_mthd(priv, 0x9097, 0x1db8, 0x00000000); - nv_mthd(priv, 0x9097, 0x1dc8, 0x00000000); - nv_mthd(priv, 0x9097, 0x1dd8, 0x00000000); - nv_mthd(priv, 0x9097, 0x1de8, 0x00000000); - nv_mthd(priv, 0x9097, 0x1df8, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d0c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d1c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d2c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d3c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d4c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d5c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d6c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d7c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d8c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1d9c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1dac, 0x00000000); - nv_mthd(priv, 0x9097, 0x1dbc, 0x00000000); - nv_mthd(priv, 0x9097, 0x1dcc, 0x00000000); - nv_mthd(priv, 0x9097, 0x1ddc, 0x00000000); - nv_mthd(priv, 0x9097, 0x1dec, 0x00000000); - nv_mthd(priv, 0x9097, 0x1dfc, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f00, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f08, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f10, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f18, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f20, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f28, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f30, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f38, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f40, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f48, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f50, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f58, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f60, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f68, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f70, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f78, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f04, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f0c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f14, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f1c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f24, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f2c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f34, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f3c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f44, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f4c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f54, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f5c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f64, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f6c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f74, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f7c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f80, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f88, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f90, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f98, 0x00000000); - nv_mthd(priv, 0x9097, 0x1fa0, 0x00000000); - nv_mthd(priv, 0x9097, 0x1fa8, 0x00000000); - nv_mthd(priv, 0x9097, 0x1fb0, 0x00000000); - nv_mthd(priv, 0x9097, 0x1fb8, 0x00000000); - nv_mthd(priv, 0x9097, 0x1fc0, 0x00000000); - nv_mthd(priv, 0x9097, 0x1fc8, 0x00000000); - nv_mthd(priv, 0x9097, 0x1fd0, 0x00000000); - nv_mthd(priv, 0x9097, 0x1fd8, 0x00000000); - nv_mthd(priv, 0x9097, 0x1fe0, 0x00000000); - nv_mthd(priv, 0x9097, 0x1fe8, 0x00000000); - nv_mthd(priv, 0x9097, 0x1ff0, 0x00000000); - nv_mthd(priv, 0x9097, 0x1ff8, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f84, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f8c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f94, 0x00000000); - nv_mthd(priv, 0x9097, 0x1f9c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1fa4, 0x00000000); - nv_mthd(priv, 0x9097, 0x1fac, 0x00000000); - nv_mthd(priv, 0x9097, 0x1fb4, 0x00000000); - nv_mthd(priv, 0x9097, 0x1fbc, 0x00000000); - nv_mthd(priv, 0x9097, 0x1fc4, 0x00000000); - nv_mthd(priv, 0x9097, 0x1fcc, 0x00000000); - nv_mthd(priv, 0x9097, 0x1fd4, 0x00000000); - nv_mthd(priv, 0x9097, 0x1fdc, 0x00000000); - nv_mthd(priv, 0x9097, 0x1fe4, 0x00000000); - nv_mthd(priv, 0x9097, 0x1fec, 0x00000000); - nv_mthd(priv, 0x9097, 0x1ff4, 0x00000000); - nv_mthd(priv, 0x9097, 0x1ffc, 0x00000000); - nv_mthd(priv, 0x9097, 0x2200, 0x00000022); - nv_mthd(priv, 0x9097, 0x2210, 0x00000022); - nv_mthd(priv, 0x9097, 0x2220, 0x00000022); - nv_mthd(priv, 0x9097, 0x2230, 0x00000022); - nv_mthd(priv, 0x9097, 0x2240, 0x00000022); - nv_mthd(priv, 0x9097, 0x2000, 0x00000000); - nv_mthd(priv, 0x9097, 0x2040, 0x00000011); - nv_mthd(priv, 0x9097, 0x2080, 0x00000020); - nv_mthd(priv, 0x9097, 0x20c0, 0x00000030); - nv_mthd(priv, 0x9097, 0x2100, 0x00000040); - nv_mthd(priv, 0x9097, 0x2140, 0x00000051); - nv_mthd(priv, 0x9097, 0x200c, 0x00000001); - nv_mthd(priv, 0x9097, 0x204c, 0x00000001); - nv_mthd(priv, 0x9097, 0x208c, 0x00000001); - nv_mthd(priv, 0x9097, 0x20cc, 0x00000001); - nv_mthd(priv, 0x9097, 0x210c, 0x00000001); - nv_mthd(priv, 0x9097, 0x214c, 0x00000001); - nv_mthd(priv, 0x9097, 0x2010, 0x00000000); - nv_mthd(priv, 0x9097, 0x2050, 0x00000000); - nv_mthd(priv, 0x9097, 0x2090, 0x00000001); - nv_mthd(priv, 0x9097, 0x20d0, 0x00000002); - nv_mthd(priv, 0x9097, 0x2110, 0x00000003); - nv_mthd(priv, 0x9097, 0x2150, 0x00000004); - nv_mthd(priv, 0x9097, 0x0380, 0x00000000); - nv_mthd(priv, 0x9097, 0x03a0, 0x00000000); - nv_mthd(priv, 0x9097, 0x03c0, 0x00000000); - nv_mthd(priv, 0x9097, 0x03e0, 0x00000000); - nv_mthd(priv, 0x9097, 0x0384, 0x00000000); - nv_mthd(priv, 0x9097, 0x03a4, 0x00000000); - nv_mthd(priv, 0x9097, 0x03c4, 0x00000000); - nv_mthd(priv, 0x9097, 0x03e4, 0x00000000); - nv_mthd(priv, 0x9097, 0x0388, 0x00000000); - nv_mthd(priv, 0x9097, 0x03a8, 0x00000000); - nv_mthd(priv, 0x9097, 0x03c8, 0x00000000); - nv_mthd(priv, 0x9097, 0x03e8, 0x00000000); - nv_mthd(priv, 0x9097, 0x038c, 0x00000000); - nv_mthd(priv, 0x9097, 0x03ac, 0x00000000); - nv_mthd(priv, 0x9097, 0x03cc, 0x00000000); - nv_mthd(priv, 0x9097, 0x03ec, 0x00000000); - nv_mthd(priv, 0x9097, 0x0700, 0x00000000); - nv_mthd(priv, 0x9097, 0x0710, 0x00000000); - nv_mthd(priv, 0x9097, 0x0720, 0x00000000); - nv_mthd(priv, 0x9097, 0x0730, 0x00000000); - nv_mthd(priv, 0x9097, 0x0704, 0x00000000); - nv_mthd(priv, 0x9097, 0x0714, 0x00000000); - nv_mthd(priv, 0x9097, 0x0724, 0x00000000); - nv_mthd(priv, 0x9097, 0x0734, 0x00000000); - nv_mthd(priv, 0x9097, 0x0708, 0x00000000); - nv_mthd(priv, 0x9097, 0x0718, 0x00000000); - nv_mthd(priv, 0x9097, 0x0728, 0x00000000); - nv_mthd(priv, 0x9097, 0x0738, 0x00000000); - nv_mthd(priv, 0x9097, 0x2800, 0x00000000); - nv_mthd(priv, 0x9097, 0x2804, 0x00000000); - nv_mthd(priv, 0x9097, 0x2808, 0x00000000); - nv_mthd(priv, 0x9097, 0x280c, 0x00000000); - nv_mthd(priv, 0x9097, 0x2810, 0x00000000); - nv_mthd(priv, 0x9097, 0x2814, 0x00000000); - nv_mthd(priv, 0x9097, 0x2818, 0x00000000); - nv_mthd(priv, 0x9097, 0x281c, 0x00000000); - nv_mthd(priv, 0x9097, 0x2820, 0x00000000); - nv_mthd(priv, 0x9097, 0x2824, 0x00000000); - nv_mthd(priv, 0x9097, 0x2828, 0x00000000); - nv_mthd(priv, 0x9097, 0x282c, 0x00000000); - nv_mthd(priv, 0x9097, 0x2830, 0x00000000); - nv_mthd(priv, 0x9097, 0x2834, 0x00000000); - nv_mthd(priv, 0x9097, 0x2838, 0x00000000); - nv_mthd(priv, 0x9097, 0x283c, 0x00000000); - nv_mthd(priv, 0x9097, 0x2840, 0x00000000); - nv_mthd(priv, 0x9097, 0x2844, 0x00000000); - nv_mthd(priv, 0x9097, 0x2848, 0x00000000); - nv_mthd(priv, 0x9097, 0x284c, 0x00000000); - nv_mthd(priv, 0x9097, 0x2850, 0x00000000); - nv_mthd(priv, 0x9097, 0x2854, 0x00000000); - nv_mthd(priv, 0x9097, 0x2858, 0x00000000); - nv_mthd(priv, 0x9097, 0x285c, 0x00000000); - nv_mthd(priv, 0x9097, 0x2860, 0x00000000); - nv_mthd(priv, 0x9097, 0x2864, 0x00000000); - nv_mthd(priv, 0x9097, 0x2868, 0x00000000); - nv_mthd(priv, 0x9097, 0x286c, 0x00000000); - nv_mthd(priv, 0x9097, 0x2870, 0x00000000); - nv_mthd(priv, 0x9097, 0x2874, 0x00000000); - nv_mthd(priv, 0x9097, 0x2878, 0x00000000); - nv_mthd(priv, 0x9097, 0x287c, 0x00000000); - nv_mthd(priv, 0x9097, 0x2880, 0x00000000); - nv_mthd(priv, 0x9097, 0x2884, 0x00000000); - nv_mthd(priv, 0x9097, 0x2888, 0x00000000); - nv_mthd(priv, 0x9097, 0x288c, 0x00000000); - nv_mthd(priv, 0x9097, 0x2890, 0x00000000); - nv_mthd(priv, 0x9097, 0x2894, 0x00000000); - nv_mthd(priv, 0x9097, 0x2898, 0x00000000); - nv_mthd(priv, 0x9097, 0x289c, 0x00000000); - nv_mthd(priv, 0x9097, 0x28a0, 0x00000000); - nv_mthd(priv, 0x9097, 0x28a4, 0x00000000); - nv_mthd(priv, 0x9097, 0x28a8, 0x00000000); - nv_mthd(priv, 0x9097, 0x28ac, 0x00000000); - nv_mthd(priv, 0x9097, 0x28b0, 0x00000000); - nv_mthd(priv, 0x9097, 0x28b4, 0x00000000); - nv_mthd(priv, 0x9097, 0x28b8, 0x00000000); - nv_mthd(priv, 0x9097, 0x28bc, 0x00000000); - nv_mthd(priv, 0x9097, 0x28c0, 0x00000000); - nv_mthd(priv, 0x9097, 0x28c4, 0x00000000); - nv_mthd(priv, 0x9097, 0x28c8, 0x00000000); - nv_mthd(priv, 0x9097, 0x28cc, 0x00000000); - nv_mthd(priv, 0x9097, 0x28d0, 0x00000000); - nv_mthd(priv, 0x9097, 0x28d4, 0x00000000); - nv_mthd(priv, 0x9097, 0x28d8, 0x00000000); - nv_mthd(priv, 0x9097, 0x28dc, 0x00000000); - nv_mthd(priv, 0x9097, 0x28e0, 0x00000000); - nv_mthd(priv, 0x9097, 0x28e4, 0x00000000); - nv_mthd(priv, 0x9097, 0x28e8, 0x00000000); - nv_mthd(priv, 0x9097, 0x28ec, 0x00000000); - nv_mthd(priv, 0x9097, 0x28f0, 0x00000000); - nv_mthd(priv, 0x9097, 0x28f4, 0x00000000); - nv_mthd(priv, 0x9097, 0x28f8, 0x00000000); - nv_mthd(priv, 0x9097, 0x28fc, 0x00000000); - nv_mthd(priv, 0x9097, 0x2900, 0x00000000); - nv_mthd(priv, 0x9097, 0x2904, 0x00000000); - nv_mthd(priv, 0x9097, 0x2908, 0x00000000); - nv_mthd(priv, 0x9097, 0x290c, 0x00000000); - nv_mthd(priv, 0x9097, 0x2910, 0x00000000); - nv_mthd(priv, 0x9097, 0x2914, 0x00000000); - nv_mthd(priv, 0x9097, 0x2918, 0x00000000); - nv_mthd(priv, 0x9097, 0x291c, 0x00000000); - nv_mthd(priv, 0x9097, 0x2920, 0x00000000); - nv_mthd(priv, 0x9097, 0x2924, 0x00000000); - nv_mthd(priv, 0x9097, 0x2928, 0x00000000); - nv_mthd(priv, 0x9097, 0x292c, 0x00000000); - nv_mthd(priv, 0x9097, 0x2930, 0x00000000); - nv_mthd(priv, 0x9097, 0x2934, 0x00000000); - nv_mthd(priv, 0x9097, 0x2938, 0x00000000); - nv_mthd(priv, 0x9097, 0x293c, 0x00000000); - nv_mthd(priv, 0x9097, 0x2940, 0x00000000); - nv_mthd(priv, 0x9097, 0x2944, 0x00000000); - nv_mthd(priv, 0x9097, 0x2948, 0x00000000); - nv_mthd(priv, 0x9097, 0x294c, 0x00000000); - nv_mthd(priv, 0x9097, 0x2950, 0x00000000); - nv_mthd(priv, 0x9097, 0x2954, 0x00000000); - nv_mthd(priv, 0x9097, 0x2958, 0x00000000); - nv_mthd(priv, 0x9097, 0x295c, 0x00000000); - nv_mthd(priv, 0x9097, 0x2960, 0x00000000); - nv_mthd(priv, 0x9097, 0x2964, 0x00000000); - nv_mthd(priv, 0x9097, 0x2968, 0x00000000); - nv_mthd(priv, 0x9097, 0x296c, 0x00000000); - nv_mthd(priv, 0x9097, 0x2970, 0x00000000); - nv_mthd(priv, 0x9097, 0x2974, 0x00000000); - nv_mthd(priv, 0x9097, 0x2978, 0x00000000); - nv_mthd(priv, 0x9097, 0x297c, 0x00000000); - nv_mthd(priv, 0x9097, 0x2980, 0x00000000); - nv_mthd(priv, 0x9097, 0x2984, 0x00000000); - nv_mthd(priv, 0x9097, 0x2988, 0x00000000); - nv_mthd(priv, 0x9097, 0x298c, 0x00000000); - nv_mthd(priv, 0x9097, 0x2990, 0x00000000); - nv_mthd(priv, 0x9097, 0x2994, 0x00000000); - nv_mthd(priv, 0x9097, 0x2998, 0x00000000); - nv_mthd(priv, 0x9097, 0x299c, 0x00000000); - nv_mthd(priv, 0x9097, 0x29a0, 0x00000000); - nv_mthd(priv, 0x9097, 0x29a4, 0x00000000); - nv_mthd(priv, 0x9097, 0x29a8, 0x00000000); - nv_mthd(priv, 0x9097, 0x29ac, 0x00000000); - nv_mthd(priv, 0x9097, 0x29b0, 0x00000000); - nv_mthd(priv, 0x9097, 0x29b4, 0x00000000); - nv_mthd(priv, 0x9097, 0x29b8, 0x00000000); - nv_mthd(priv, 0x9097, 0x29bc, 0x00000000); - nv_mthd(priv, 0x9097, 0x29c0, 0x00000000); - nv_mthd(priv, 0x9097, 0x29c4, 0x00000000); - nv_mthd(priv, 0x9097, 0x29c8, 0x00000000); - nv_mthd(priv, 0x9097, 0x29cc, 0x00000000); - nv_mthd(priv, 0x9097, 0x29d0, 0x00000000); - nv_mthd(priv, 0x9097, 0x29d4, 0x00000000); - nv_mthd(priv, 0x9097, 0x29d8, 0x00000000); - nv_mthd(priv, 0x9097, 0x29dc, 0x00000000); - nv_mthd(priv, 0x9097, 0x29e0, 0x00000000); - nv_mthd(priv, 0x9097, 0x29e4, 0x00000000); - nv_mthd(priv, 0x9097, 0x29e8, 0x00000000); - nv_mthd(priv, 0x9097, 0x29ec, 0x00000000); - nv_mthd(priv, 0x9097, 0x29f0, 0x00000000); - nv_mthd(priv, 0x9097, 0x29f4, 0x00000000); - nv_mthd(priv, 0x9097, 0x29f8, 0x00000000); - nv_mthd(priv, 0x9097, 0x29fc, 0x00000000); - nv_mthd(priv, 0x9097, 0x0a00, 0x00000000); - nv_mthd(priv, 0x9097, 0x0a20, 0x00000000); - nv_mthd(priv, 0x9097, 0x0a40, 0x00000000); - nv_mthd(priv, 0x9097, 0x0a60, 0x00000000); - nv_mthd(priv, 0x9097, 0x0a80, 0x00000000); - nv_mthd(priv, 0x9097, 0x0aa0, 0x00000000); - nv_mthd(priv, 0x9097, 0x0ac0, 0x00000000); - nv_mthd(priv, 0x9097, 0x0ae0, 0x00000000); - nv_mthd(priv, 0x9097, 0x0b00, 0x00000000); - nv_mthd(priv, 0x9097, 0x0b20, 0x00000000); - nv_mthd(priv, 0x9097, 0x0b40, 0x00000000); - nv_mthd(priv, 0x9097, 0x0b60, 0x00000000); - nv_mthd(priv, 0x9097, 0x0b80, 0x00000000); - nv_mthd(priv, 0x9097, 0x0ba0, 0x00000000); - nv_mthd(priv, 0x9097, 0x0bc0, 0x00000000); - nv_mthd(priv, 0x9097, 0x0be0, 0x00000000); - nv_mthd(priv, 0x9097, 0x0a04, 0x00000000); - nv_mthd(priv, 0x9097, 0x0a24, 0x00000000); - nv_mthd(priv, 0x9097, 0x0a44, 0x00000000); - nv_mthd(priv, 0x9097, 0x0a64, 0x00000000); - nv_mthd(priv, 0x9097, 0x0a84, 0x00000000); - nv_mthd(priv, 0x9097, 0x0aa4, 0x00000000); - nv_mthd(priv, 0x9097, 0x0ac4, 0x00000000); - nv_mthd(priv, 0x9097, 0x0ae4, 0x00000000); - nv_mthd(priv, 0x9097, 0x0b04, 0x00000000); - nv_mthd(priv, 0x9097, 0x0b24, 0x00000000); - nv_mthd(priv, 0x9097, 0x0b44, 0x00000000); - nv_mthd(priv, 0x9097, 0x0b64, 0x00000000); - nv_mthd(priv, 0x9097, 0x0b84, 0x00000000); - nv_mthd(priv, 0x9097, 0x0ba4, 0x00000000); - nv_mthd(priv, 0x9097, 0x0bc4, 0x00000000); - nv_mthd(priv, 0x9097, 0x0be4, 0x00000000); - nv_mthd(priv, 0x9097, 0x0a08, 0x00000000); - nv_mthd(priv, 0x9097, 0x0a28, 0x00000000); - nv_mthd(priv, 0x9097, 0x0a48, 0x00000000); - nv_mthd(priv, 0x9097, 0x0a68, 0x00000000); - nv_mthd(priv, 0x9097, 0x0a88, 0x00000000); - nv_mthd(priv, 0x9097, 0x0aa8, 0x00000000); - nv_mthd(priv, 0x9097, 0x0ac8, 0x00000000); - nv_mthd(priv, 0x9097, 0x0ae8, 0x00000000); - nv_mthd(priv, 0x9097, 0x0b08, 0x00000000); - nv_mthd(priv, 0x9097, 0x0b28, 0x00000000); - nv_mthd(priv, 0x9097, 0x0b48, 0x00000000); - nv_mthd(priv, 0x9097, 0x0b68, 0x00000000); - nv_mthd(priv, 0x9097, 0x0b88, 0x00000000); - nv_mthd(priv, 0x9097, 0x0ba8, 0x00000000); - nv_mthd(priv, 0x9097, 0x0bc8, 0x00000000); - nv_mthd(priv, 0x9097, 0x0be8, 0x00000000); - nv_mthd(priv, 0x9097, 0x0a0c, 0x00000000); - nv_mthd(priv, 0x9097, 0x0a2c, 0x00000000); - nv_mthd(priv, 0x9097, 0x0a4c, 0x00000000); - nv_mthd(priv, 0x9097, 0x0a6c, 0x00000000); - nv_mthd(priv, 0x9097, 0x0a8c, 0x00000000); - nv_mthd(priv, 0x9097, 0x0aac, 0x00000000); - nv_mthd(priv, 0x9097, 0x0acc, 0x00000000); - nv_mthd(priv, 0x9097, 0x0aec, 0x00000000); - nv_mthd(priv, 0x9097, 0x0b0c, 0x00000000); - nv_mthd(priv, 0x9097, 0x0b2c, 0x00000000); - nv_mthd(priv, 0x9097, 0x0b4c, 0x00000000); - nv_mthd(priv, 0x9097, 0x0b6c, 0x00000000); - nv_mthd(priv, 0x9097, 0x0b8c, 0x00000000); - nv_mthd(priv, 0x9097, 0x0bac, 0x00000000); - nv_mthd(priv, 0x9097, 0x0bcc, 0x00000000); - nv_mthd(priv, 0x9097, 0x0bec, 0x00000000); - nv_mthd(priv, 0x9097, 0x0a10, 0x00000000); - nv_mthd(priv, 0x9097, 0x0a30, 0x00000000); - nv_mthd(priv, 0x9097, 0x0a50, 0x00000000); - nv_mthd(priv, 0x9097, 0x0a70, 0x00000000); - nv_mthd(priv, 0x9097, 0x0a90, 0x00000000); - nv_mthd(priv, 0x9097, 0x0ab0, 0x00000000); - nv_mthd(priv, 0x9097, 0x0ad0, 0x00000000); - nv_mthd(priv, 0x9097, 0x0af0, 0x00000000); - nv_mthd(priv, 0x9097, 0x0b10, 0x00000000); - nv_mthd(priv, 0x9097, 0x0b30, 0x00000000); - nv_mthd(priv, 0x9097, 0x0b50, 0x00000000); - nv_mthd(priv, 0x9097, 0x0b70, 0x00000000); - nv_mthd(priv, 0x9097, 0x0b90, 0x00000000); - nv_mthd(priv, 0x9097, 0x0bb0, 0x00000000); - nv_mthd(priv, 0x9097, 0x0bd0, 0x00000000); - nv_mthd(priv, 0x9097, 0x0bf0, 0x00000000); - nv_mthd(priv, 0x9097, 0x0a14, 0x00000000); - nv_mthd(priv, 0x9097, 0x0a34, 0x00000000); - nv_mthd(priv, 0x9097, 0x0a54, 0x00000000); - nv_mthd(priv, 0x9097, 0x0a74, 0x00000000); - nv_mthd(priv, 0x9097, 0x0a94, 0x00000000); - nv_mthd(priv, 0x9097, 0x0ab4, 0x00000000); - nv_mthd(priv, 0x9097, 0x0ad4, 0x00000000); - nv_mthd(priv, 0x9097, 0x0af4, 0x00000000); - nv_mthd(priv, 0x9097, 0x0b14, 0x00000000); - nv_mthd(priv, 0x9097, 0x0b34, 0x00000000); - nv_mthd(priv, 0x9097, 0x0b54, 0x00000000); - nv_mthd(priv, 0x9097, 0x0b74, 0x00000000); - nv_mthd(priv, 0x9097, 0x0b94, 0x00000000); - nv_mthd(priv, 0x9097, 0x0bb4, 0x00000000); - nv_mthd(priv, 0x9097, 0x0bd4, 0x00000000); - nv_mthd(priv, 0x9097, 0x0bf4, 0x00000000); - nv_mthd(priv, 0x9097, 0x0c00, 0x00000000); - nv_mthd(priv, 0x9097, 0x0c10, 0x00000000); - nv_mthd(priv, 0x9097, 0x0c20, 0x00000000); - nv_mthd(priv, 0x9097, 0x0c30, 0x00000000); - nv_mthd(priv, 0x9097, 0x0c40, 0x00000000); - nv_mthd(priv, 0x9097, 0x0c50, 0x00000000); - nv_mthd(priv, 0x9097, 0x0c60, 0x00000000); - nv_mthd(priv, 0x9097, 0x0c70, 0x00000000); - nv_mthd(priv, 0x9097, 0x0c80, 0x00000000); - nv_mthd(priv, 0x9097, 0x0c90, 0x00000000); - nv_mthd(priv, 0x9097, 0x0ca0, 0x00000000); - nv_mthd(priv, 0x9097, 0x0cb0, 0x00000000); - nv_mthd(priv, 0x9097, 0x0cc0, 0x00000000); - nv_mthd(priv, 0x9097, 0x0cd0, 0x00000000); - nv_mthd(priv, 0x9097, 0x0ce0, 0x00000000); - nv_mthd(priv, 0x9097, 0x0cf0, 0x00000000); - nv_mthd(priv, 0x9097, 0x0c04, 0x00000000); - nv_mthd(priv, 0x9097, 0x0c14, 0x00000000); - nv_mthd(priv, 0x9097, 0x0c24, 0x00000000); - nv_mthd(priv, 0x9097, 0x0c34, 0x00000000); - nv_mthd(priv, 0x9097, 0x0c44, 0x00000000); - nv_mthd(priv, 0x9097, 0x0c54, 0x00000000); - nv_mthd(priv, 0x9097, 0x0c64, 0x00000000); - nv_mthd(priv, 0x9097, 0x0c74, 0x00000000); - nv_mthd(priv, 0x9097, 0x0c84, 0x00000000); - nv_mthd(priv, 0x9097, 0x0c94, 0x00000000); - nv_mthd(priv, 0x9097, 0x0ca4, 0x00000000); - nv_mthd(priv, 0x9097, 0x0cb4, 0x00000000); - nv_mthd(priv, 0x9097, 0x0cc4, 0x00000000); - nv_mthd(priv, 0x9097, 0x0cd4, 0x00000000); - nv_mthd(priv, 0x9097, 0x0ce4, 0x00000000); - nv_mthd(priv, 0x9097, 0x0cf4, 0x00000000); - nv_mthd(priv, 0x9097, 0x0c08, 0x00000000); - nv_mthd(priv, 0x9097, 0x0c18, 0x00000000); - nv_mthd(priv, 0x9097, 0x0c28, 0x00000000); - nv_mthd(priv, 0x9097, 0x0c38, 0x00000000); - nv_mthd(priv, 0x9097, 0x0c48, 0x00000000); - nv_mthd(priv, 0x9097, 0x0c58, 0x00000000); - nv_mthd(priv, 0x9097, 0x0c68, 0x00000000); - nv_mthd(priv, 0x9097, 0x0c78, 0x00000000); - nv_mthd(priv, 0x9097, 0x0c88, 0x00000000); - nv_mthd(priv, 0x9097, 0x0c98, 0x00000000); - nv_mthd(priv, 0x9097, 0x0ca8, 0x00000000); - nv_mthd(priv, 0x9097, 0x0cb8, 0x00000000); - nv_mthd(priv, 0x9097, 0x0cc8, 0x00000000); - nv_mthd(priv, 0x9097, 0x0cd8, 0x00000000); - nv_mthd(priv, 0x9097, 0x0ce8, 0x00000000); - nv_mthd(priv, 0x9097, 0x0cf8, 0x00000000); - nv_mthd(priv, 0x9097, 0x0c0c, 0x3f800000); - nv_mthd(priv, 0x9097, 0x0c1c, 0x3f800000); - nv_mthd(priv, 0x9097, 0x0c2c, 0x3f800000); - nv_mthd(priv, 0x9097, 0x0c3c, 0x3f800000); - nv_mthd(priv, 0x9097, 0x0c4c, 0x3f800000); - nv_mthd(priv, 0x9097, 0x0c5c, 0x3f800000); - nv_mthd(priv, 0x9097, 0x0c6c, 0x3f800000); - nv_mthd(priv, 0x9097, 0x0c7c, 0x3f800000); - nv_mthd(priv, 0x9097, 0x0c8c, 0x3f800000); - nv_mthd(priv, 0x9097, 0x0c9c, 0x3f800000); - nv_mthd(priv, 0x9097, 0x0cac, 0x3f800000); - nv_mthd(priv, 0x9097, 0x0cbc, 0x3f800000); - nv_mthd(priv, 0x9097, 0x0ccc, 0x3f800000); - nv_mthd(priv, 0x9097, 0x0cdc, 0x3f800000); - nv_mthd(priv, 0x9097, 0x0cec, 0x3f800000); - nv_mthd(priv, 0x9097, 0x0cfc, 0x3f800000); - nv_mthd(priv, 0x9097, 0x0d00, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0d08, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0d10, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0d18, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0d20, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0d28, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0d30, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0d38, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0d04, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0d0c, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0d14, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0d1c, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0d24, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0d2c, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0d34, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0d3c, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0e00, 0x00000000); - nv_mthd(priv, 0x9097, 0x0e10, 0x00000000); - nv_mthd(priv, 0x9097, 0x0e20, 0x00000000); - nv_mthd(priv, 0x9097, 0x0e30, 0x00000000); - nv_mthd(priv, 0x9097, 0x0e40, 0x00000000); - nv_mthd(priv, 0x9097, 0x0e50, 0x00000000); - nv_mthd(priv, 0x9097, 0x0e60, 0x00000000); - nv_mthd(priv, 0x9097, 0x0e70, 0x00000000); - nv_mthd(priv, 0x9097, 0x0e80, 0x00000000); - nv_mthd(priv, 0x9097, 0x0e90, 0x00000000); - nv_mthd(priv, 0x9097, 0x0ea0, 0x00000000); - nv_mthd(priv, 0x9097, 0x0eb0, 0x00000000); - nv_mthd(priv, 0x9097, 0x0ec0, 0x00000000); - nv_mthd(priv, 0x9097, 0x0ed0, 0x00000000); - nv_mthd(priv, 0x9097, 0x0ee0, 0x00000000); - nv_mthd(priv, 0x9097, 0x0ef0, 0x00000000); - nv_mthd(priv, 0x9097, 0x0e04, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0e14, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0e24, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0e34, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0e44, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0e54, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0e64, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0e74, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0e84, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0e94, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0ea4, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0eb4, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0ec4, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0ed4, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0ee4, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0ef4, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0e08, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0e18, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0e28, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0e38, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0e48, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0e58, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0e68, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0e78, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0e88, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0e98, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0ea8, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0eb8, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0ec8, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0ed8, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0ee8, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0ef8, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0d40, 0x00000000); - nv_mthd(priv, 0x9097, 0x0d48, 0x00000000); - nv_mthd(priv, 0x9097, 0x0d50, 0x00000000); - nv_mthd(priv, 0x9097, 0x0d58, 0x00000000); - nv_mthd(priv, 0x9097, 0x0d44, 0x00000000); - nv_mthd(priv, 0x9097, 0x0d4c, 0x00000000); - nv_mthd(priv, 0x9097, 0x0d54, 0x00000000); - nv_mthd(priv, 0x9097, 0x0d5c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1e00, 0x00000001); - nv_mthd(priv, 0x9097, 0x1e20, 0x00000001); - nv_mthd(priv, 0x9097, 0x1e40, 0x00000001); - nv_mthd(priv, 0x9097, 0x1e60, 0x00000001); - nv_mthd(priv, 0x9097, 0x1e80, 0x00000001); - nv_mthd(priv, 0x9097, 0x1ea0, 0x00000001); - nv_mthd(priv, 0x9097, 0x1ec0, 0x00000001); - nv_mthd(priv, 0x9097, 0x1ee0, 0x00000001); - nv_mthd(priv, 0x9097, 0x1e04, 0x00000001); - nv_mthd(priv, 0x9097, 0x1e24, 0x00000001); - nv_mthd(priv, 0x9097, 0x1e44, 0x00000001); - nv_mthd(priv, 0x9097, 0x1e64, 0x00000001); - nv_mthd(priv, 0x9097, 0x1e84, 0x00000001); - nv_mthd(priv, 0x9097, 0x1ea4, 0x00000001); - nv_mthd(priv, 0x9097, 0x1ec4, 0x00000001); - nv_mthd(priv, 0x9097, 0x1ee4, 0x00000001); - nv_mthd(priv, 0x9097, 0x1e08, 0x00000002); - nv_mthd(priv, 0x9097, 0x1e28, 0x00000002); - nv_mthd(priv, 0x9097, 0x1e48, 0x00000002); - nv_mthd(priv, 0x9097, 0x1e68, 0x00000002); - nv_mthd(priv, 0x9097, 0x1e88, 0x00000002); - nv_mthd(priv, 0x9097, 0x1ea8, 0x00000002); - nv_mthd(priv, 0x9097, 0x1ec8, 0x00000002); - nv_mthd(priv, 0x9097, 0x1ee8, 0x00000002); - nv_mthd(priv, 0x9097, 0x1e0c, 0x00000001); - nv_mthd(priv, 0x9097, 0x1e2c, 0x00000001); - nv_mthd(priv, 0x9097, 0x1e4c, 0x00000001); - nv_mthd(priv, 0x9097, 0x1e6c, 0x00000001); - nv_mthd(priv, 0x9097, 0x1e8c, 0x00000001); - nv_mthd(priv, 0x9097, 0x1eac, 0x00000001); - nv_mthd(priv, 0x9097, 0x1ecc, 0x00000001); - nv_mthd(priv, 0x9097, 0x1eec, 0x00000001); - nv_mthd(priv, 0x9097, 0x1e10, 0x00000001); - nv_mthd(priv, 0x9097, 0x1e30, 0x00000001); - nv_mthd(priv, 0x9097, 0x1e50, 0x00000001); - nv_mthd(priv, 0x9097, 0x1e70, 0x00000001); - nv_mthd(priv, 0x9097, 0x1e90, 0x00000001); - nv_mthd(priv, 0x9097, 0x1eb0, 0x00000001); - nv_mthd(priv, 0x9097, 0x1ed0, 0x00000001); - nv_mthd(priv, 0x9097, 0x1ef0, 0x00000001); - nv_mthd(priv, 0x9097, 0x1e14, 0x00000002); - nv_mthd(priv, 0x9097, 0x1e34, 0x00000002); - nv_mthd(priv, 0x9097, 0x1e54, 0x00000002); - nv_mthd(priv, 0x9097, 0x1e74, 0x00000002); - nv_mthd(priv, 0x9097, 0x1e94, 0x00000002); - nv_mthd(priv, 0x9097, 0x1eb4, 0x00000002); - nv_mthd(priv, 0x9097, 0x1ed4, 0x00000002); - nv_mthd(priv, 0x9097, 0x1ef4, 0x00000002); - nv_mthd(priv, 0x9097, 0x1e18, 0x00000001); - nv_mthd(priv, 0x9097, 0x1e38, 0x00000001); - nv_mthd(priv, 0x9097, 0x1e58, 0x00000001); - nv_mthd(priv, 0x9097, 0x1e78, 0x00000001); - nv_mthd(priv, 0x9097, 0x1e98, 0x00000001); - nv_mthd(priv, 0x9097, 0x1eb8, 0x00000001); - nv_mthd(priv, 0x9097, 0x1ed8, 0x00000001); - nv_mthd(priv, 0x9097, 0x1ef8, 0x00000001); - if (fermi == 0x9097) { - for (mthd = 0x3400; mthd <= 0x35fc; mthd += 4) - nv_mthd(priv, 0x9097, mthd, 0x00000000); - } - nv_mthd(priv, 0x9097, 0x030c, 0x00000001); - nv_mthd(priv, 0x9097, 0x1944, 0x00000000); - nv_mthd(priv, 0x9097, 0x1514, 0x00000000); - nv_mthd(priv, 0x9097, 0x0d68, 0x0000ffff); - nv_mthd(priv, 0x9097, 0x121c, 0x0fac6881); - nv_mthd(priv, 0x9097, 0x0fac, 0x00000001); - nv_mthd(priv, 0x9097, 0x1538, 0x00000001); - nv_mthd(priv, 0x9097, 0x0fe0, 0x00000000); - nv_mthd(priv, 0x9097, 0x0fe4, 0x00000000); - nv_mthd(priv, 0x9097, 0x0fe8, 0x00000014); - nv_mthd(priv, 0x9097, 0x0fec, 0x00000040); - nv_mthd(priv, 0x9097, 0x0ff0, 0x00000000); - nv_mthd(priv, 0x9097, 0x179c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1228, 0x00000400); - nv_mthd(priv, 0x9097, 0x122c, 0x00000300); - nv_mthd(priv, 0x9097, 0x1230, 0x00010001); - nv_mthd(priv, 0x9097, 0x07f8, 0x00000000); - nv_mthd(priv, 0x9097, 0x15b4, 0x00000001); - nv_mthd(priv, 0x9097, 0x15cc, 0x00000000); - nv_mthd(priv, 0x9097, 0x1534, 0x00000000); - nv_mthd(priv, 0x9097, 0x0fb0, 0x00000000); - nv_mthd(priv, 0x9097, 0x15d0, 0x00000000); - nv_mthd(priv, 0x9097, 0x153c, 0x00000000); - nv_mthd(priv, 0x9097, 0x16b4, 0x00000003); - nv_mthd(priv, 0x9097, 0x0fbc, 0x0000ffff); - nv_mthd(priv, 0x9097, 0x0fc0, 0x0000ffff); - nv_mthd(priv, 0x9097, 0x0fc4, 0x0000ffff); - nv_mthd(priv, 0x9097, 0x0fc8, 0x0000ffff); - nv_mthd(priv, 0x9097, 0x0df8, 0x00000000); - nv_mthd(priv, 0x9097, 0x0dfc, 0x00000000); - nv_mthd(priv, 0x9097, 0x1948, 0x00000000); - nv_mthd(priv, 0x9097, 0x1970, 0x00000001); - nv_mthd(priv, 0x9097, 0x161c, 0x000009f0); - nv_mthd(priv, 0x9097, 0x0dcc, 0x00000010); - nv_mthd(priv, 0x9097, 0x163c, 0x00000000); - nv_mthd(priv, 0x9097, 0x15e4, 0x00000000); - nv_mthd(priv, 0x9097, 0x1160, 0x25e00040); - nv_mthd(priv, 0x9097, 0x1164, 0x25e00040); - nv_mthd(priv, 0x9097, 0x1168, 0x25e00040); - nv_mthd(priv, 0x9097, 0x116c, 0x25e00040); - nv_mthd(priv, 0x9097, 0x1170, 0x25e00040); - nv_mthd(priv, 0x9097, 0x1174, 0x25e00040); - nv_mthd(priv, 0x9097, 0x1178, 0x25e00040); - nv_mthd(priv, 0x9097, 0x117c, 0x25e00040); - nv_mthd(priv, 0x9097, 0x1180, 0x25e00040); - nv_mthd(priv, 0x9097, 0x1184, 0x25e00040); - nv_mthd(priv, 0x9097, 0x1188, 0x25e00040); - nv_mthd(priv, 0x9097, 0x118c, 0x25e00040); - nv_mthd(priv, 0x9097, 0x1190, 0x25e00040); - nv_mthd(priv, 0x9097, 0x1194, 0x25e00040); - nv_mthd(priv, 0x9097, 0x1198, 0x25e00040); - nv_mthd(priv, 0x9097, 0x119c, 0x25e00040); - nv_mthd(priv, 0x9097, 0x11a0, 0x25e00040); - nv_mthd(priv, 0x9097, 0x11a4, 0x25e00040); - nv_mthd(priv, 0x9097, 0x11a8, 0x25e00040); - nv_mthd(priv, 0x9097, 0x11ac, 0x25e00040); - nv_mthd(priv, 0x9097, 0x11b0, 0x25e00040); - nv_mthd(priv, 0x9097, 0x11b4, 0x25e00040); - nv_mthd(priv, 0x9097, 0x11b8, 0x25e00040); - nv_mthd(priv, 0x9097, 0x11bc, 0x25e00040); - nv_mthd(priv, 0x9097, 0x11c0, 0x25e00040); - nv_mthd(priv, 0x9097, 0x11c4, 0x25e00040); - nv_mthd(priv, 0x9097, 0x11c8, 0x25e00040); - nv_mthd(priv, 0x9097, 0x11cc, 0x25e00040); - nv_mthd(priv, 0x9097, 0x11d0, 0x25e00040); - nv_mthd(priv, 0x9097, 0x11d4, 0x25e00040); - nv_mthd(priv, 0x9097, 0x11d8, 0x25e00040); - nv_mthd(priv, 0x9097, 0x11dc, 0x25e00040); - nv_mthd(priv, 0x9097, 0x1880, 0x00000000); - nv_mthd(priv, 0x9097, 0x1884, 0x00000000); - nv_mthd(priv, 0x9097, 0x1888, 0x00000000); - nv_mthd(priv, 0x9097, 0x188c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1890, 0x00000000); - nv_mthd(priv, 0x9097, 0x1894, 0x00000000); - nv_mthd(priv, 0x9097, 0x1898, 0x00000000); - nv_mthd(priv, 0x9097, 0x189c, 0x00000000); - nv_mthd(priv, 0x9097, 0x18a0, 0x00000000); - nv_mthd(priv, 0x9097, 0x18a4, 0x00000000); - nv_mthd(priv, 0x9097, 0x18a8, 0x00000000); - nv_mthd(priv, 0x9097, 0x18ac, 0x00000000); - nv_mthd(priv, 0x9097, 0x18b0, 0x00000000); - nv_mthd(priv, 0x9097, 0x18b4, 0x00000000); - nv_mthd(priv, 0x9097, 0x18b8, 0x00000000); - nv_mthd(priv, 0x9097, 0x18bc, 0x00000000); - nv_mthd(priv, 0x9097, 0x18c0, 0x00000000); - nv_mthd(priv, 0x9097, 0x18c4, 0x00000000); - nv_mthd(priv, 0x9097, 0x18c8, 0x00000000); - nv_mthd(priv, 0x9097, 0x18cc, 0x00000000); - nv_mthd(priv, 0x9097, 0x18d0, 0x00000000); - nv_mthd(priv, 0x9097, 0x18d4, 0x00000000); - nv_mthd(priv, 0x9097, 0x18d8, 0x00000000); - nv_mthd(priv, 0x9097, 0x18dc, 0x00000000); - nv_mthd(priv, 0x9097, 0x18e0, 0x00000000); - nv_mthd(priv, 0x9097, 0x18e4, 0x00000000); - nv_mthd(priv, 0x9097, 0x18e8, 0x00000000); - nv_mthd(priv, 0x9097, 0x18ec, 0x00000000); - nv_mthd(priv, 0x9097, 0x18f0, 0x00000000); - nv_mthd(priv, 0x9097, 0x18f4, 0x00000000); - nv_mthd(priv, 0x9097, 0x18f8, 0x00000000); - nv_mthd(priv, 0x9097, 0x18fc, 0x00000000); - nv_mthd(priv, 0x9097, 0x0f84, 0x00000000); - nv_mthd(priv, 0x9097, 0x0f88, 0x00000000); - nv_mthd(priv, 0x9097, 0x17c8, 0x00000000); - nv_mthd(priv, 0x9097, 0x17cc, 0x00000000); - nv_mthd(priv, 0x9097, 0x17d0, 0x000000ff); - nv_mthd(priv, 0x9097, 0x17d4, 0xffffffff); - nv_mthd(priv, 0x9097, 0x17d8, 0x00000002); - nv_mthd(priv, 0x9097, 0x17dc, 0x00000000); - nv_mthd(priv, 0x9097, 0x15f4, 0x00000000); - nv_mthd(priv, 0x9097, 0x15f8, 0x00000000); - nv_mthd(priv, 0x9097, 0x1434, 0x00000000); - nv_mthd(priv, 0x9097, 0x1438, 0x00000000); - nv_mthd(priv, 0x9097, 0x0d74, 0x00000000); - nv_mthd(priv, 0x9097, 0x0dec, 0x00000001); - nv_mthd(priv, 0x9097, 0x13a4, 0x00000000); - nv_mthd(priv, 0x9097, 0x1318, 0x00000001); - nv_mthd(priv, 0x9097, 0x1644, 0x00000000); - nv_mthd(priv, 0x9097, 0x0748, 0x00000000); - nv_mthd(priv, 0x9097, 0x0de8, 0x00000000); - nv_mthd(priv, 0x9097, 0x1648, 0x00000000); - nv_mthd(priv, 0x9097, 0x12a4, 0x00000000); - nv_mthd(priv, 0x9097, 0x1120, 0x00000000); - nv_mthd(priv, 0x9097, 0x1124, 0x00000000); - nv_mthd(priv, 0x9097, 0x1128, 0x00000000); - nv_mthd(priv, 0x9097, 0x112c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1118, 0x00000000); - nv_mthd(priv, 0x9097, 0x164c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1658, 0x00000000); - nv_mthd(priv, 0x9097, 0x1910, 0x00000290); - nv_mthd(priv, 0x9097, 0x1518, 0x00000000); - nv_mthd(priv, 0x9097, 0x165c, 0x00000001); - nv_mthd(priv, 0x9097, 0x1520, 0x00000000); - nv_mthd(priv, 0x9097, 0x1604, 0x00000000); - nv_mthd(priv, 0x9097, 0x1570, 0x00000000); - nv_mthd(priv, 0x9097, 0x13b0, 0x3f800000); - nv_mthd(priv, 0x9097, 0x13b4, 0x3f800000); - nv_mthd(priv, 0x9097, 0x020c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1670, 0x30201000); - nv_mthd(priv, 0x9097, 0x1674, 0x70605040); - nv_mthd(priv, 0x9097, 0x1678, 0xb8a89888); - nv_mthd(priv, 0x9097, 0x167c, 0xf8e8d8c8); - nv_mthd(priv, 0x9097, 0x166c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1680, 0x00ffff00); - nv_mthd(priv, 0x9097, 0x12d0, 0x00000003); - nv_mthd(priv, 0x9097, 0x12d4, 0x00000002); - nv_mthd(priv, 0x9097, 0x1684, 0x00000000); - nv_mthd(priv, 0x9097, 0x1688, 0x00000000); - nv_mthd(priv, 0x9097, 0x0dac, 0x00001b02); - nv_mthd(priv, 0x9097, 0x0db0, 0x00001b02); - nv_mthd(priv, 0x9097, 0x0db4, 0x00000000); - nv_mthd(priv, 0x9097, 0x168c, 0x00000000); - nv_mthd(priv, 0x9097, 0x15bc, 0x00000000); - nv_mthd(priv, 0x9097, 0x156c, 0x00000000); - nv_mthd(priv, 0x9097, 0x187c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1110, 0x00000001); - nv_mthd(priv, 0x9097, 0x0dc0, 0x00000000); - nv_mthd(priv, 0x9097, 0x0dc4, 0x00000000); - nv_mthd(priv, 0x9097, 0x0dc8, 0x00000000); - nv_mthd(priv, 0x9097, 0x1234, 0x00000000); - nv_mthd(priv, 0x9097, 0x1690, 0x00000000); - nv_mthd(priv, 0x9097, 0x12ac, 0x00000001); - nv_mthd(priv, 0x9097, 0x02c4, 0x00000000); - nv_mthd(priv, 0x9097, 0x0790, 0x00000000); - nv_mthd(priv, 0x9097, 0x0794, 0x00000000); - nv_mthd(priv, 0x9097, 0x0798, 0x00000000); - nv_mthd(priv, 0x9097, 0x079c, 0x00000000); - nv_mthd(priv, 0x9097, 0x07a0, 0x00000000); - nv_mthd(priv, 0x9097, 0x077c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1000, 0x00000010); - nv_mthd(priv, 0x9097, 0x10fc, 0x00000000); - nv_mthd(priv, 0x9097, 0x1290, 0x00000000); - nv_mthd(priv, 0x9097, 0x0218, 0x00000010); - nv_mthd(priv, 0x9097, 0x12d8, 0x00000000); - nv_mthd(priv, 0x9097, 0x12dc, 0x00000010); - nv_mthd(priv, 0x9097, 0x0d94, 0x00000001); - nv_mthd(priv, 0x9097, 0x155c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1560, 0x00000000); - nv_mthd(priv, 0x9097, 0x1564, 0x00001fff); - nv_mthd(priv, 0x9097, 0x1574, 0x00000000); - nv_mthd(priv, 0x9097, 0x1578, 0x00000000); - nv_mthd(priv, 0x9097, 0x157c, 0x003fffff); - nv_mthd(priv, 0x9097, 0x1354, 0x00000000); - nv_mthd(priv, 0x9097, 0x1664, 0x00000000); - nv_mthd(priv, 0x9097, 0x1610, 0x00000012); - nv_mthd(priv, 0x9097, 0x1608, 0x00000000); - nv_mthd(priv, 0x9097, 0x160c, 0x00000000); - nv_mthd(priv, 0x9097, 0x162c, 0x00000003); - nv_mthd(priv, 0x9097, 0x0210, 0x00000000); - nv_mthd(priv, 0x9097, 0x0320, 0x00000000); - nv_mthd(priv, 0x9097, 0x0324, 0x3f800000); - nv_mthd(priv, 0x9097, 0x0328, 0x3f800000); - nv_mthd(priv, 0x9097, 0x032c, 0x3f800000); - nv_mthd(priv, 0x9097, 0x0330, 0x3f800000); - nv_mthd(priv, 0x9097, 0x0334, 0x3f800000); - nv_mthd(priv, 0x9097, 0x0338, 0x3f800000); - nv_mthd(priv, 0x9097, 0x0750, 0x00000000); - nv_mthd(priv, 0x9097, 0x0760, 0x39291909); - nv_mthd(priv, 0x9097, 0x0764, 0x79695949); - nv_mthd(priv, 0x9097, 0x0768, 0xb9a99989); - nv_mthd(priv, 0x9097, 0x076c, 0xf9e9d9c9); - nv_mthd(priv, 0x9097, 0x0770, 0x30201000); - nv_mthd(priv, 0x9097, 0x0774, 0x70605040); - nv_mthd(priv, 0x9097, 0x0778, 0x00009080); - nv_mthd(priv, 0x9097, 0x0780, 0x39291909); - nv_mthd(priv, 0x9097, 0x0784, 0x79695949); - nv_mthd(priv, 0x9097, 0x0788, 0xb9a99989); - nv_mthd(priv, 0x9097, 0x078c, 0xf9e9d9c9); - nv_mthd(priv, 0x9097, 0x07d0, 0x30201000); - nv_mthd(priv, 0x9097, 0x07d4, 0x70605040); - nv_mthd(priv, 0x9097, 0x07d8, 0x00009080); - nv_mthd(priv, 0x9097, 0x037c, 0x00000001); - nv_mthd(priv, 0x9097, 0x0740, 0x00000000); - nv_mthd(priv, 0x9097, 0x0744, 0x00000000); - nv_mthd(priv, 0x9097, 0x2600, 0x00000000); - nv_mthd(priv, 0x9097, 0x1918, 0x00000000); - nv_mthd(priv, 0x9097, 0x191c, 0x00000900); - nv_mthd(priv, 0x9097, 0x1920, 0x00000405); - nv_mthd(priv, 0x9097, 0x1308, 0x00000001); - nv_mthd(priv, 0x9097, 0x1924, 0x00000000); - nv_mthd(priv, 0x9097, 0x13ac, 0x00000000); - nv_mthd(priv, 0x9097, 0x192c, 0x00000001); - nv_mthd(priv, 0x9097, 0x193c, 0x00002c1c); - nv_mthd(priv, 0x9097, 0x0d7c, 0x00000000); - nv_mthd(priv, 0x9097, 0x0f8c, 0x00000000); - nv_mthd(priv, 0x9097, 0x02c0, 0x00000001); - nv_mthd(priv, 0x9097, 0x1510, 0x00000000); - nv_mthd(priv, 0x9097, 0x1940, 0x00000000); - nv_mthd(priv, 0x9097, 0x0ff4, 0x00000000); - nv_mthd(priv, 0x9097, 0x0ff8, 0x00000000); - nv_mthd(priv, 0x9097, 0x194c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1950, 0x00000000); - nv_mthd(priv, 0x9097, 0x1968, 0x00000000); - nv_mthd(priv, 0x9097, 0x1590, 0x0000003f); - nv_mthd(priv, 0x9097, 0x07e8, 0x00000000); - nv_mthd(priv, 0x9097, 0x07ec, 0x00000000); - nv_mthd(priv, 0x9097, 0x07f0, 0x00000000); - nv_mthd(priv, 0x9097, 0x07f4, 0x00000000); - nv_mthd(priv, 0x9097, 0x196c, 0x00000011); - nv_mthd(priv, 0x9097, 0x197c, 0x00000000); - nv_mthd(priv, 0x9097, 0x0fcc, 0x00000000); - nv_mthd(priv, 0x9097, 0x0fd0, 0x00000000); - nv_mthd(priv, 0x9097, 0x02d8, 0x00000040); - nv_mthd(priv, 0x9097, 0x1980, 0x00000080); - nv_mthd(priv, 0x9097, 0x1504, 0x00000080); - nv_mthd(priv, 0x9097, 0x1984, 0x00000000); - nv_mthd(priv, 0x9097, 0x0300, 0x00000001); - nv_mthd(priv, 0x9097, 0x13a8, 0x00000000); - nv_mthd(priv, 0x9097, 0x12ec, 0x00000000); - nv_mthd(priv, 0x9097, 0x1310, 0x00000000); - nv_mthd(priv, 0x9097, 0x1314, 0x00000001); - nv_mthd(priv, 0x9097, 0x1380, 0x00000000); - nv_mthd(priv, 0x9097, 0x1384, 0x00000001); - nv_mthd(priv, 0x9097, 0x1388, 0x00000001); - nv_mthd(priv, 0x9097, 0x138c, 0x00000001); - nv_mthd(priv, 0x9097, 0x1390, 0x00000001); - nv_mthd(priv, 0x9097, 0x1394, 0x00000000); - nv_mthd(priv, 0x9097, 0x139c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1398, 0x00000000); - nv_mthd(priv, 0x9097, 0x1594, 0x00000000); - nv_mthd(priv, 0x9097, 0x1598, 0x00000001); - nv_mthd(priv, 0x9097, 0x159c, 0x00000001); - nv_mthd(priv, 0x9097, 0x15a0, 0x00000001); - nv_mthd(priv, 0x9097, 0x15a4, 0x00000001); - nv_mthd(priv, 0x9097, 0x0f54, 0x00000000); - nv_mthd(priv, 0x9097, 0x0f58, 0x00000000); - nv_mthd(priv, 0x9097, 0x0f5c, 0x00000000); - nv_mthd(priv, 0x9097, 0x19bc, 0x00000000); - nv_mthd(priv, 0x9097, 0x0f9c, 0x00000000); - nv_mthd(priv, 0x9097, 0x0fa0, 0x00000000); - nv_mthd(priv, 0x9097, 0x12cc, 0x00000000); - nv_mthd(priv, 0x9097, 0x12e8, 0x00000000); - nv_mthd(priv, 0x9097, 0x130c, 0x00000001); - nv_mthd(priv, 0x9097, 0x1360, 0x00000000); - nv_mthd(priv, 0x9097, 0x1364, 0x00000000); - nv_mthd(priv, 0x9097, 0x1368, 0x00000000); - nv_mthd(priv, 0x9097, 0x136c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1370, 0x00000000); - nv_mthd(priv, 0x9097, 0x1374, 0x00000000); - nv_mthd(priv, 0x9097, 0x1378, 0x00000000); - nv_mthd(priv, 0x9097, 0x137c, 0x00000000); - nv_mthd(priv, 0x9097, 0x133c, 0x00000001); - nv_mthd(priv, 0x9097, 0x1340, 0x00000001); - nv_mthd(priv, 0x9097, 0x1344, 0x00000002); - nv_mthd(priv, 0x9097, 0x1348, 0x00000001); - nv_mthd(priv, 0x9097, 0x134c, 0x00000001); - nv_mthd(priv, 0x9097, 0x1350, 0x00000002); - nv_mthd(priv, 0x9097, 0x1358, 0x00000001); - nv_mthd(priv, 0x9097, 0x12e4, 0x00000000); - nv_mthd(priv, 0x9097, 0x131c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1320, 0x00000000); - nv_mthd(priv, 0x9097, 0x1324, 0x00000000); - nv_mthd(priv, 0x9097, 0x1328, 0x00000000); - nv_mthd(priv, 0x9097, 0x19c0, 0x00000000); - nv_mthd(priv, 0x9097, 0x1140, 0x00000000); - nv_mthd(priv, 0x9097, 0x19c4, 0x00000000); - nv_mthd(priv, 0x9097, 0x19c8, 0x00001500); - nv_mthd(priv, 0x9097, 0x135c, 0x00000000); - nv_mthd(priv, 0x9097, 0x0f90, 0x00000000); - nv_mthd(priv, 0x9097, 0x19e0, 0x00000001); - nv_mthd(priv, 0x9097, 0x19e4, 0x00000001); - nv_mthd(priv, 0x9097, 0x19e8, 0x00000001); - nv_mthd(priv, 0x9097, 0x19ec, 0x00000001); - nv_mthd(priv, 0x9097, 0x19f0, 0x00000001); - nv_mthd(priv, 0x9097, 0x19f4, 0x00000001); - nv_mthd(priv, 0x9097, 0x19f8, 0x00000001); - nv_mthd(priv, 0x9097, 0x19fc, 0x00000001); - nv_mthd(priv, 0x9097, 0x19cc, 0x00000001); - nv_mthd(priv, 0x9097, 0x15b8, 0x00000000); - nv_mthd(priv, 0x9097, 0x1a00, 0x00001111); - nv_mthd(priv, 0x9097, 0x1a04, 0x00000000); - nv_mthd(priv, 0x9097, 0x1a08, 0x00000000); - nv_mthd(priv, 0x9097, 0x1a0c, 0x00000000); - nv_mthd(priv, 0x9097, 0x1a10, 0x00000000); - nv_mthd(priv, 0x9097, 0x1a14, 0x00000000); - nv_mthd(priv, 0x9097, 0x1a18, 0x00000000); - nv_mthd(priv, 0x9097, 0x1a1c, 0x00000000); - nv_mthd(priv, 0x9097, 0x0d6c, 0xffff0000); - nv_mthd(priv, 0x9097, 0x0d70, 0xffff0000); - nv_mthd(priv, 0x9097, 0x10f8, 0x00001010); - nv_mthd(priv, 0x9097, 0x0d80, 0x00000000); - nv_mthd(priv, 0x9097, 0x0d84, 0x00000000); - nv_mthd(priv, 0x9097, 0x0d88, 0x00000000); - nv_mthd(priv, 0x9097, 0x0d8c, 0x00000000); - nv_mthd(priv, 0x9097, 0x0d90, 0x00000000); - nv_mthd(priv, 0x9097, 0x0da0, 0x00000000); - nv_mthd(priv, 0x9097, 0x1508, 0x80000000); - nv_mthd(priv, 0x9097, 0x150c, 0x40000000); - nv_mthd(priv, 0x9097, 0x1668, 0x00000000); - nv_mthd(priv, 0x9097, 0x0318, 0x00000008); - nv_mthd(priv, 0x9097, 0x031c, 0x00000008); - nv_mthd(priv, 0x9097, 0x0d9c, 0x00000001); - nv_mthd(priv, 0x9097, 0x07dc, 0x00000000); - nv_mthd(priv, 0x9097, 0x074c, 0x00000055); - nv_mthd(priv, 0x9097, 0x1420, 0x00000003); - nv_mthd(priv, 0x9097, 0x17bc, 0x00000000); - nv_mthd(priv, 0x9097, 0x17c0, 0x00000000); - nv_mthd(priv, 0x9097, 0x17c4, 0x00000001); - nv_mthd(priv, 0x9097, 0x1008, 0x00000008); - nv_mthd(priv, 0x9097, 0x100c, 0x00000040); - nv_mthd(priv, 0x9097, 0x1010, 0x0000012c); - nv_mthd(priv, 0x9097, 0x0d60, 0x00000040); - nv_mthd(priv, 0x9097, 0x075c, 0x00000003); - nv_mthd(priv, 0x9097, 0x1018, 0x00000020); - nv_mthd(priv, 0x9097, 0x101c, 0x00000001); - nv_mthd(priv, 0x9097, 0x1020, 0x00000020); - nv_mthd(priv, 0x9097, 0x1024, 0x00000001); - nv_mthd(priv, 0x9097, 0x1444, 0x00000000); - nv_mthd(priv, 0x9097, 0x1448, 0x00000000); - nv_mthd(priv, 0x9097, 0x144c, 0x00000000); - nv_mthd(priv, 0x9097, 0x0360, 0x20164010); - nv_mthd(priv, 0x9097, 0x0364, 0x00000020); - nv_mthd(priv, 0x9097, 0x0368, 0x00000000); - nv_mthd(priv, 0x9097, 0x0de4, 0x00000000); - nv_mthd(priv, 0x9097, 0x0204, 0x00000006); - nv_mthd(priv, 0x9097, 0x0208, 0x00000000); - nv_mthd(priv, 0x9097, 0x02cc, 0x003fffff); - nv_mthd(priv, 0x9097, 0x02d0, 0x00000c48); - nv_mthd(priv, 0x9097, 0x1220, 0x00000005); - nv_mthd(priv, 0x9097, 0x0fdc, 0x00000000); - nv_mthd(priv, 0x9097, 0x0f98, 0x00300008); - nv_mthd(priv, 0x9097, 0x1284, 0x04000080); - nv_mthd(priv, 0x9097, 0x1450, 0x00300008); - nv_mthd(priv, 0x9097, 0x1454, 0x04000080); - nv_mthd(priv, 0x9097, 0x0214, 0x00000000); -} - -static void -nvc0_grctx_generate_9197(struct nvc0_graph_priv *priv) -{ - u32 fermi = nvc0_graph_class(priv); - u32 mthd; - - if (fermi == 0x9197) { - for (mthd = 0x3400; mthd <= 0x35fc; mthd += 4) - nv_mthd(priv, 0x9197, mthd, 0x00000000); - } - nv_mthd(priv, 0x9197, 0x02e4, 0x0000b001); -} - -static void -nvc0_grctx_generate_9297(struct nvc0_graph_priv *priv) -{ - u32 fermi = nvc0_graph_class(priv); - u32 mthd; - - if (fermi == 0x9297) { - for (mthd = 0x3400; mthd <= 0x35fc; mthd += 4) - nv_mthd(priv, 0x9297, mthd, 0x00000000); - } - nv_mthd(priv, 0x9297, 0x036c, 0x00000000); - nv_mthd(priv, 0x9297, 0x0370, 0x00000000); - nv_mthd(priv, 0x9297, 0x07a4, 0x00000000); - nv_mthd(priv, 0x9297, 0x07a8, 0x00000000); - nv_mthd(priv, 0x9297, 0x0374, 0x00000000); - nv_mthd(priv, 0x9297, 0x0378, 0x00000020); -} - -static void -nvc0_grctx_generate_902d(struct nvc0_graph_priv *priv) -{ - nv_mthd(priv, 0x902d, 0x0200, 0x000000cf); - nv_mthd(priv, 0x902d, 0x0204, 0x00000001); - nv_mthd(priv, 0x902d, 0x0208, 0x00000020); - nv_mthd(priv, 0x902d, 0x020c, 0x00000001); - nv_mthd(priv, 0x902d, 0x0210, 0x00000000); - nv_mthd(priv, 0x902d, 0x0214, 0x00000080); - nv_mthd(priv, 0x902d, 0x0218, 0x00000100); - nv_mthd(priv, 0x902d, 0x021c, 0x00000100); - nv_mthd(priv, 0x902d, 0x0220, 0x00000000); - nv_mthd(priv, 0x902d, 0x0224, 0x00000000); - nv_mthd(priv, 0x902d, 0x0230, 0x000000cf); - nv_mthd(priv, 0x902d, 0x0234, 0x00000001); - nv_mthd(priv, 0x902d, 0x0238, 0x00000020); - nv_mthd(priv, 0x902d, 0x023c, 0x00000001); - nv_mthd(priv, 0x902d, 0x0244, 0x00000080); - nv_mthd(priv, 0x902d, 0x0248, 0x00000100); - nv_mthd(priv, 0x902d, 0x024c, 0x00000100); -} - -static void -nvc0_grctx_generate_9039(struct nvc0_graph_priv *priv) -{ - nv_mthd(priv, 0x9039, 0x030c, 0x00000000); - nv_mthd(priv, 0x9039, 0x0310, 0x00000000); - nv_mthd(priv, 0x9039, 0x0314, 0x00000000); - nv_mthd(priv, 0x9039, 0x0320, 0x00000000); - nv_mthd(priv, 0x9039, 0x0238, 0x00000000); - nv_mthd(priv, 0x9039, 0x023c, 0x00000000); - nv_mthd(priv, 0x9039, 0x0318, 0x00000000); - nv_mthd(priv, 0x9039, 0x031c, 0x00000000); -} - -static void -nvc0_grctx_generate_90c0(struct nvc0_graph_priv *priv) -{ - int i; - - for (i = 0; nv_device(priv)->chipset >= 0xd0 && i < 4; i++) { - nv_mthd(priv, 0x90c0, 0x2700 + (i * 0x40), 0x00000000); - nv_mthd(priv, 0x90c0, 0x2720 + (i * 0x40), 0x00000000); - nv_mthd(priv, 0x90c0, 0x2704 + (i * 0x40), 0x00000000); - nv_mthd(priv, 0x90c0, 0x2724 + (i * 0x40), 0x00000000); - nv_mthd(priv, 0x90c0, 0x2708 + (i * 0x40), 0x00000000); - nv_mthd(priv, 0x90c0, 0x2728 + (i * 0x40), 0x00000000); - } - nv_mthd(priv, 0x90c0, 0x270c, 0x00000000); - nv_mthd(priv, 0x90c0, 0x272c, 0x00000000); - nv_mthd(priv, 0x90c0, 0x274c, 0x00000000); - nv_mthd(priv, 0x90c0, 0x276c, 0x00000000); - nv_mthd(priv, 0x90c0, 0x278c, 0x00000000); - nv_mthd(priv, 0x90c0, 0x27ac, 0x00000000); - nv_mthd(priv, 0x90c0, 0x27cc, 0x00000000); - nv_mthd(priv, 0x90c0, 0x27ec, 0x00000000); - for (i = 0; nv_device(priv)->chipset >= 0xd0 && i < 4; i++) { - nv_mthd(priv, 0x90c0, 0x2710 + (i * 0x40), 0x00014000); - nv_mthd(priv, 0x90c0, 0x2730 + (i * 0x40), 0x00014000); - } - for (i = 0; nv_device(priv)->chipset >= 0xd0 && i < 4; i++) { - nv_mthd(priv, 0x90c0, 0x2714 + (i * 0x40), 0x00000040); - nv_mthd(priv, 0x90c0, 0x2734 + (i * 0x40), 0x00000040); - } - nv_mthd(priv, 0x90c0, 0x030c, 0x00000001); - nv_mthd(priv, 0x90c0, 0x1944, 0x00000000); - nv_mthd(priv, 0x90c0, 0x0758, 0x00000100); - nv_mthd(priv, 0x90c0, 0x02c4, 0x00000000); - nv_mthd(priv, 0x90c0, 0x0790, 0x00000000); - nv_mthd(priv, 0x90c0, 0x0794, 0x00000000); - nv_mthd(priv, 0x90c0, 0x0798, 0x00000000); - nv_mthd(priv, 0x90c0, 0x079c, 0x00000000); - nv_mthd(priv, 0x90c0, 0x07a0, 0x00000000); - nv_mthd(priv, 0x90c0, 0x077c, 0x00000000); - nv_mthd(priv, 0x90c0, 0x0204, 0x00000000); - nv_mthd(priv, 0x90c0, 0x0208, 0x00000000); - nv_mthd(priv, 0x90c0, 0x020c, 0x00000000); - nv_mthd(priv, 0x90c0, 0x0214, 0x00000000); - nv_mthd(priv, 0x90c0, 0x024c, 0x00000000); - nv_mthd(priv, 0x90c0, 0x0d94, 0x00000001); - nv_mthd(priv, 0x90c0, 0x1608, 0x00000000); - nv_mthd(priv, 0x90c0, 0x160c, 0x00000000); - nv_mthd(priv, 0x90c0, 0x1664, 0x00000000); -} - -static void -nvc0_grctx_generate_dispatch(struct nvc0_graph_priv *priv) -{ - int i; - - nv_wr32(priv, 0x404004, 0x00000000); - nv_wr32(priv, 0x404008, 0x00000000); - nv_wr32(priv, 0x40400c, 0x00000000); - nv_wr32(priv, 0x404010, 0x00000000); - nv_wr32(priv, 0x404014, 0x00000000); - nv_wr32(priv, 0x404018, 0x00000000); - nv_wr32(priv, 0x40401c, 0x00000000); - nv_wr32(priv, 0x404020, 0x00000000); - nv_wr32(priv, 0x404024, 0x00000000); - nv_wr32(priv, 0x404028, 0x00000000); - switch (nv_device(priv)->chipset) { - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x40402c, 0x00000000); - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc1: - case 0xc8: - case 0xce: - case 0xcf: - break; - default: - BUG_ON(1); - break; - } - nv_wr32(priv, 0x404044, 0x00000000); - nv_wr32(priv, 0x404094, 0x00000000); - nv_wr32(priv, 0x404098, 0x00000000); - nv_wr32(priv, 0x40409c, 0x00000000); - nv_wr32(priv, 0x4040a0, 0x00000000); - nv_wr32(priv, 0x4040a4, 0x00000000); - nv_wr32(priv, 0x4040a8, 0x00000000); - nv_wr32(priv, 0x4040ac, 0x00000000); - nv_wr32(priv, 0x4040b0, 0x00000000); - nv_wr32(priv, 0x4040b4, 0x00000000); - nv_wr32(priv, 0x4040b8, 0x00000000); - nv_wr32(priv, 0x4040bc, 0x00000000); - nv_wr32(priv, 0x4040c0, 0x00000000); - nv_wr32(priv, 0x4040c4, 0x00000000); - nv_wr32(priv, 0x4040c8, 0xf0000087); - nv_wr32(priv, 0x4040d0, 0x00000000); - nv_wr32(priv, 0x4040d4, 0x00000000); - nv_wr32(priv, 0x4040d8, 0x00000000); - nv_wr32(priv, 0x4040dc, 0x00000000); - nv_wr32(priv, 0x4040e0, 0x00000000); - nv_wr32(priv, 0x4040e4, 0x00000000); - nv_wr32(priv, 0x4040e8, 0x00001000); - nv_wr32(priv, 0x4040f8, 0x00000000); - nv_wr32(priv, 0x404130, 0x00000000); - nv_wr32(priv, 0x404134, 0x00000000); - nv_wr32(priv, 0x404138, 0x20000040); - nv_wr32(priv, 0x404150, 0x0000002e); - nv_wr32(priv, 0x404154, 0x00000400); - nv_wr32(priv, 0x404158, 0x00000200); - nv_wr32(priv, 0x404164, 0x00000055); - nv_wr32(priv, 0x404168, 0x00000000); - switch (nv_device(priv)->chipset) { - case 0xd9: - case 0xd7: - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc1: - case 0xc8: - case 0xce: - case 0xcf: - nv_wr32(priv, 0x404174, 0x00000000); - break; - default: - BUG_ON(1); - break; - } - nv_wr32(priv, 0x404178, 0x00000000); - nv_wr32(priv, 0x40417c, 0x00000000); - for (i = 0; i < 8; i++) - nv_wr32(priv, 0x404200 + (i * 4), 0x00000000); /* subc */ -} - -static void -nvc0_grctx_generate_macro(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x404404, 0x00000000); - nv_wr32(priv, 0x404408, 0x00000000); - nv_wr32(priv, 0x40440c, 0x00000000); - nv_wr32(priv, 0x404410, 0x00000000); - nv_wr32(priv, 0x404414, 0x00000000); - nv_wr32(priv, 0x404418, 0x00000000); - nv_wr32(priv, 0x40441c, 0x00000000); - nv_wr32(priv, 0x404420, 0x00000000); - nv_wr32(priv, 0x404424, 0x00000000); - nv_wr32(priv, 0x404428, 0x00000000); - nv_wr32(priv, 0x40442c, 0x00000000); - nv_wr32(priv, 0x404430, 0x00000000); - nv_wr32(priv, 0x404434, 0x00000000); - nv_wr32(priv, 0x404438, 0x00000000); - nv_wr32(priv, 0x404460, 0x00000000); - nv_wr32(priv, 0x404464, 0x00000000); - nv_wr32(priv, 0x404468, 0x00ffffff); - nv_wr32(priv, 0x40446c, 0x00000000); - nv_wr32(priv, 0x404480, 0x00000001); - nv_wr32(priv, 0x404498, 0x00000001); +done: + nouveau_gpuobj_ref(NULL, &chan); + return ret; } -static void -nvc0_grctx_generate_m2mf(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x404604, 0x00000015); - nv_wr32(priv, 0x404608, 0x00000000); - nv_wr32(priv, 0x40460c, 0x00002e00); - nv_wr32(priv, 0x404610, 0x00000100); - nv_wr32(priv, 0x404618, 0x00000000); - nv_wr32(priv, 0x40461c, 0x00000000); - nv_wr32(priv, 0x404620, 0x00000000); - nv_wr32(priv, 0x404624, 0x00000000); - nv_wr32(priv, 0x404628, 0x00000000); - nv_wr32(priv, 0x40462c, 0x00000000); - nv_wr32(priv, 0x404630, 0x00000000); - nv_wr32(priv, 0x404634, 0x00000000); - nv_wr32(priv, 0x404638, 0x00000004); - nv_wr32(priv, 0x40463c, 0x00000000); - nv_wr32(priv, 0x404640, 0x00000000); - nv_wr32(priv, 0x404644, 0x00000000); - nv_wr32(priv, 0x404648, 0x00000000); - nv_wr32(priv, 0x40464c, 0x00000000); - nv_wr32(priv, 0x404650, 0x00000000); - nv_wr32(priv, 0x404654, 0x00000000); - nv_wr32(priv, 0x404658, 0x00000000); - nv_wr32(priv, 0x40465c, 0x007f0100); - nv_wr32(priv, 0x404660, 0x00000000); - nv_wr32(priv, 0x404664, 0x00000000); - nv_wr32(priv, 0x404668, 0x00000000); - nv_wr32(priv, 0x40466c, 0x00000000); - nv_wr32(priv, 0x404670, 0x00000000); - nv_wr32(priv, 0x404674, 0x00000000); - nv_wr32(priv, 0x404678, 0x00000000); - nv_wr32(priv, 0x40467c, 0x00000002); - nv_wr32(priv, 0x404680, 0x00000000); - nv_wr32(priv, 0x404684, 0x00000000); - nv_wr32(priv, 0x404688, 0x00000000); - nv_wr32(priv, 0x40468c, 0x00000000); - nv_wr32(priv, 0x404690, 0x00000000); - nv_wr32(priv, 0x404694, 0x00000000); - nv_wr32(priv, 0x404698, 0x00000000); - nv_wr32(priv, 0x40469c, 0x00000000); - nv_wr32(priv, 0x4046a0, 0x007f0080); - nv_wr32(priv, 0x4046a4, 0x00000000); - nv_wr32(priv, 0x4046a8, 0x00000000); - nv_wr32(priv, 0x4046ac, 0x00000000); - nv_wr32(priv, 0x4046b0, 0x00000000); - nv_wr32(priv, 0x4046b4, 0x00000000); - nv_wr32(priv, 0x4046b8, 0x00000000); - nv_wr32(priv, 0x4046bc, 0x00000000); - nv_wr32(priv, 0x4046c0, 0x00000000); - nv_wr32(priv, 0x4046c4, 0x00000000); - nv_wr32(priv, 0x4046c8, 0x00000000); - nv_wr32(priv, 0x4046cc, 0x00000000); - nv_wr32(priv, 0x4046d0, 0x00000000); - nv_wr32(priv, 0x4046d4, 0x00000000); - nv_wr32(priv, 0x4046d8, 0x00000000); - nv_wr32(priv, 0x4046dc, 0x00000000); - nv_wr32(priv, 0x4046e0, 0x00000000); - nv_wr32(priv, 0x4046e4, 0x00000000); - nv_wr32(priv, 0x4046e8, 0x00000000); - nv_wr32(priv, 0x4046f0, 0x00000000); - nv_wr32(priv, 0x4046f4, 0x00000000); -} - -static void -nvc0_grctx_generate_unk47xx(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x404700, 0x00000000); - nv_wr32(priv, 0x404704, 0x00000000); - nv_wr32(priv, 0x404708, 0x00000000); - nv_wr32(priv, 0x40470c, 0x00000000); - nv_wr32(priv, 0x404710, 0x00000000); - nv_wr32(priv, 0x404714, 0x00000000); - nv_wr32(priv, 0x404718, 0x00000000); - nv_wr32(priv, 0x40471c, 0x00000000); - nv_wr32(priv, 0x404720, 0x00000000); - nv_wr32(priv, 0x404724, 0x00000000); - nv_wr32(priv, 0x404728, 0x00000000); - nv_wr32(priv, 0x40472c, 0x00000000); - nv_wr32(priv, 0x404730, 0x00000000); - nv_wr32(priv, 0x404734, 0x00000100); - nv_wr32(priv, 0x404738, 0x00000000); - nv_wr32(priv, 0x40473c, 0x00000000); - nv_wr32(priv, 0x404740, 0x00000000); - nv_wr32(priv, 0x404744, 0x00000000); - nv_wr32(priv, 0x404748, 0x00000000); - nv_wr32(priv, 0x40474c, 0x00000000); - nv_wr32(priv, 0x404750, 0x00000000); - nv_wr32(priv, 0x404754, 0x00000000); -} - -static void -nvc0_grctx_generate_shaders(struct nvc0_graph_priv *priv) -{ - switch (nv_device(priv)->chipset) { - case 0xc1: - nv_wr32(priv, 0x405800, 0x0f8000bf); - nv_wr32(priv, 0x405830, 0x02180218); - nv_wr32(priv, 0x405834, 0x00000000); - break; - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x405800, 0x0f8000bf); - nv_wr32(priv, 0x405830, 0x02180218); - nv_wr32(priv, 0x405834, 0x08000000); - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc8: - case 0xce: - case 0xcf: - nv_wr32(priv, 0x405800, 0x078000bf); - nv_wr32(priv, 0x405830, 0x02180000); - nv_wr32(priv, 0x405834, 0x00000000); - break; - default: - BUG_ON(1); - break; - } - nv_wr32(priv, 0x405838, 0x00000000); - nv_wr32(priv, 0x405854, 0x00000000); - nv_wr32(priv, 0x405870, 0x00000001); - nv_wr32(priv, 0x405874, 0x00000001); - nv_wr32(priv, 0x405878, 0x00000001); - nv_wr32(priv, 0x40587c, 0x00000001); - nv_wr32(priv, 0x405a00, 0x00000000); - nv_wr32(priv, 0x405a04, 0x00000000); - nv_wr32(priv, 0x405a18, 0x00000000); -} - -static void -nvc0_grctx_generate_unk60xx(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x406020, 0x000103c1); - nv_wr32(priv, 0x406028, 0x00000001); - nv_wr32(priv, 0x40602c, 0x00000001); - nv_wr32(priv, 0x406030, 0x00000001); - nv_wr32(priv, 0x406034, 0x00000001); -} - -static void -nvc0_grctx_generate_unk64xx(struct nvc0_graph_priv *priv) -{ - - nv_wr32(priv, 0x4064a8, 0x00000000); - nv_wr32(priv, 0x4064ac, 0x00003fff); - nv_wr32(priv, 0x4064b4, 0x00000000); - nv_wr32(priv, 0x4064b8, 0x00000000); - switch (nv_device(priv)->chipset) { - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x4064bc, 0x00000000); - nv_wr32(priv, 0x4064c0, 0x80140078); - nv_wr32(priv, 0x4064c4, 0x0086ffff); - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc1: - case 0xc8: - case 0xce: - case 0xcf: - break; - default: - BUG_ON(1); - break; - } -} - -static void -nvc0_grctx_generate_tpbus(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x407804, 0x00000023); - nv_wr32(priv, 0x40780c, 0x0a418820); - nv_wr32(priv, 0x407810, 0x062080e6); - nv_wr32(priv, 0x407814, 0x020398a4); - nv_wr32(priv, 0x407818, 0x0e629062); - nv_wr32(priv, 0x40781c, 0x0a418820); - nv_wr32(priv, 0x407820, 0x000000e6); - nv_wr32(priv, 0x4078bc, 0x00000103); -} - -static void -nvc0_grctx_generate_ccache(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x408000, 0x00000000); - nv_wr32(priv, 0x408004, 0x00000000); - nv_wr32(priv, 0x408008, 0x00000018); - nv_wr32(priv, 0x40800c, 0x00000000); - nv_wr32(priv, 0x408010, 0x00000000); - nv_wr32(priv, 0x408014, 0x00000069); - nv_wr32(priv, 0x408018, 0xe100e100); - nv_wr32(priv, 0x408064, 0x00000000); -} - -static void -nvc0_grctx_generate_rop(struct nvc0_graph_priv *priv) -{ - /* ROPC_BROADCAST */ - nv_wr32(priv, 0x408800, 0x02802a3c); - nv_wr32(priv, 0x408804, 0x00000040); - switch (nv_device(priv)->chipset) { - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc8: - case 0xce: - case 0xcf: - nv_wr32(priv, 0x408808, 0x0003e00d); - nv_wr32(priv, 0x408900, 0x3080b801); - nv_wr32(priv, 0x408904, 0x02000001); - nv_wr32(priv, 0x408908, 0x00c80929); - break; - case 0xc1: - nv_wr32(priv, 0x408808, 0x1003e005); - nv_wr32(priv, 0x408900, 0x3080b801); - nv_wr32(priv, 0x408904, 0x62000001); - nv_wr32(priv, 0x408908, 0x00c80929); - break; - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x408808, 0x1043e005); - nv_wr32(priv, 0x408900, 0x3080b801); - nv_wr32(priv, 0x408904, 0x1043e005); - nv_wr32(priv, 0x408908, 0x00c8102f); - break; - default: - BUG_ON(1); - break; - } - nv_wr32(priv, 0x408980, 0x0000011d); -} - -static void -nvc0_grctx_generate_gpc(struct nvc0_graph_priv *priv) -{ - int i; - - /* GPC_BROADCAST */ - nv_wr32(priv, 0x418380, 0x00000016); - nv_wr32(priv, 0x418400, 0x38004e00); - nv_wr32(priv, 0x418404, 0x71e0ffff); - switch (nv_device(priv)->chipset) { - case 0xd9: - case 0xd7: - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc1: - case 0xc8: - case 0xce: - case 0xcf: - nv_wr32(priv, 0x418408, 0x00000000); - break; - default: - BUG_ON(1); - break; - } - nv_wr32(priv, 0x41840c, 0x00001008); - nv_wr32(priv, 0x418410, 0x0fff0fff); - switch (nv_device(priv)->chipset) { - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x418414, 0x02200fff); - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc1: - case 0xc8: - case 0xce: - case 0xcf: - nv_wr32(priv, 0x418414, 0x00200fff); - break; - default: - BUG_ON(1); - break; - } - nv_wr32(priv, 0x418450, 0x00000000); - nv_wr32(priv, 0x418454, 0x00000000); - nv_wr32(priv, 0x418458, 0x00000000); - nv_wr32(priv, 0x41845c, 0x00000000); - nv_wr32(priv, 0x418460, 0x00000000); - nv_wr32(priv, 0x418464, 0x00000000); - nv_wr32(priv, 0x418468, 0x00000001); - nv_wr32(priv, 0x41846c, 0x00000000); - nv_wr32(priv, 0x418470, 0x00000000); - nv_wr32(priv, 0x418600, 0x0000001f); - nv_wr32(priv, 0x418684, 0x0000000f); - nv_wr32(priv, 0x418700, 0x00000002); - nv_wr32(priv, 0x418704, 0x00000080); - nv_wr32(priv, 0x418708, 0x00000000); - switch (nv_device(priv)->chipset) { - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x41870c, 0x00000000); - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc1: - case 0xc8: - case 0xce: - case 0xcf: - nv_wr32(priv, 0x41870c, 0x07c80000); - break; - default: - BUG_ON(1); - break; - } - nv_wr32(priv, 0x418710, 0x00000000); - switch (nv_device(priv)->chipset) { - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x418800, 0x7006860a); - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc1: - case 0xc8: - case 0xce: - case 0xcf: - nv_wr32(priv, 0x418800, 0x0006860a); - break; - default: - BUG_ON(1); - break; - } - nv_wr32(priv, 0x418808, 0x00000000); - nv_wr32(priv, 0x41880c, 0x00000000); - nv_wr32(priv, 0x418810, 0x00000000); - nv_wr32(priv, 0x418828, 0x00008442); - switch (nv_device(priv)->chipset) { - case 0xc1: - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x418830, 0x10000001); - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc8: - case 0xce: - case 0xcf: - nv_wr32(priv, 0x418830, 0x00000001); - break; - default: - BUG_ON(1); - break; - } - nv_wr32(priv, 0x4188d8, 0x00000008); - nv_wr32(priv, 0x4188e0, 0x01000000); - nv_wr32(priv, 0x4188e8, 0x00000000); - nv_wr32(priv, 0x4188ec, 0x00000000); - nv_wr32(priv, 0x4188f0, 0x00000000); - nv_wr32(priv, 0x4188f4, 0x00000000); - nv_wr32(priv, 0x4188f8, 0x00000000); - switch (nv_device(priv)->chipset) { - case 0xc1: - nv_wr32(priv, 0x4188fc, 0x00100018); - break; - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x4188fc, 0x20100008); - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc8: - case 0xce: - case 0xcf: - nv_wr32(priv, 0x4188fc, 0x00100000); - break; - default: - BUG_ON(1); - break; - } - nv_wr32(priv, 0x41891c, 0x00ff00ff); - nv_wr32(priv, 0x418924, 0x00000000); - nv_wr32(priv, 0x418928, 0x00ffff00); - nv_wr32(priv, 0x41892c, 0x0000ff00); - for (i = 0; i < 8; i++) { - nv_wr32(priv, 0x418a00 + (i * 0x20), 0x00000000); - nv_wr32(priv, 0x418a04 + (i * 0x20), 0x00000000); - nv_wr32(priv, 0x418a08 + (i * 0x20), 0x00000000); - nv_wr32(priv, 0x418a0c + (i * 0x20), 0x00010000); - nv_wr32(priv, 0x418a10 + (i * 0x20), 0x00000000); - nv_wr32(priv, 0x418a14 + (i * 0x20), 0x00000000); - nv_wr32(priv, 0x418a18 + (i * 0x20), 0x00000000); - } - switch (nv_device(priv)->chipset) { - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x418b00, 0x00000006); - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc1: - case 0xc8: - case 0xce: - case 0xcf: - nv_wr32(priv, 0x418b00, 0x00000000); - break; - default: - BUG_ON(1); - break; - } - nv_wr32(priv, 0x418b08, 0x0a418820); - nv_wr32(priv, 0x418b0c, 0x062080e6); - nv_wr32(priv, 0x418b10, 0x020398a4); - nv_wr32(priv, 0x418b14, 0x0e629062); - nv_wr32(priv, 0x418b18, 0x0a418820); - nv_wr32(priv, 0x418b1c, 0x000000e6); - nv_wr32(priv, 0x418bb8, 0x00000103); - nv_wr32(priv, 0x418c08, 0x00000001); - nv_wr32(priv, 0x418c10, 0x00000000); - nv_wr32(priv, 0x418c14, 0x00000000); - nv_wr32(priv, 0x418c18, 0x00000000); - nv_wr32(priv, 0x418c1c, 0x00000000); - nv_wr32(priv, 0x418c20, 0x00000000); - nv_wr32(priv, 0x418c24, 0x00000000); - nv_wr32(priv, 0x418c28, 0x00000000); - nv_wr32(priv, 0x418c2c, 0x00000000); - switch (nv_device(priv)->chipset) { - case 0xc1: - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x418c6c, 0x00000001); - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc8: - case 0xce: - case 0xcf: - break; - default: - BUG_ON(1); - break; - } - nv_wr32(priv, 0x418c80, 0x20200004); - nv_wr32(priv, 0x418c8c, 0x00000001); - nv_wr32(priv, 0x419000, 0x00000780); - nv_wr32(priv, 0x419004, 0x00000000); - nv_wr32(priv, 0x419008, 0x00000000); - nv_wr32(priv, 0x419014, 0x00000004); -} - -static void -nvc0_grctx_generate_tp(struct nvc0_graph_priv *priv) -{ - /* GPC_BROADCAST.TP_BROADCAST */ - nv_wr32(priv, 0x419818, 0x00000000); - nv_wr32(priv, 0x41983c, 0x00038bc7); - nv_wr32(priv, 0x419848, 0x00000000); - switch (nv_device(priv)->chipset) { - case 0xc1: - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x419864, 0x00000129); - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc8: - case 0xce: - case 0xcf: - nv_wr32(priv, 0x419864, 0x0000012a); - break; - default: - BUG_ON(1); - break; - } - nv_wr32(priv, 0x419888, 0x00000000); - nv_wr32(priv, 0x419a00, 0x000001f0); - nv_wr32(priv, 0x419a04, 0x00000001); - nv_wr32(priv, 0x419a08, 0x00000023); - nv_wr32(priv, 0x419a0c, 0x00020000); - nv_wr32(priv, 0x419a10, 0x00000000); - nv_wr32(priv, 0x419a14, 0x00000200); - switch (nv_device(priv)->chipset) { - case 0xc0: - break; - case 0xc3: - case 0xc4: - case 0xc1: - case 0xc8: - case 0xce: - case 0xcf: - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x419a1c, 0x00000000); - nv_wr32(priv, 0x419a20, 0x00000800); - break; - default: - BUG_ON(1); - break; - } - switch (nv_device(priv)->chipset) { - case 0xc0: - case 0xc8: - break; - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x00419ac4, 0x0017f440); - break; - case 0xc3: - case 0xc4: - case 0xc1: - case 0xce: - case 0xcf: - nv_wr32(priv, 0x00419ac4, 0x0007f440); - break; - default: - BUG_ON(1); - break; - } - nv_wr32(priv, 0x419b00, 0x0a418820); - nv_wr32(priv, 0x419b04, 0x062080e6); - nv_wr32(priv, 0x419b08, 0x020398a4); - nv_wr32(priv, 0x419b0c, 0x0e629062); - nv_wr32(priv, 0x419b10, 0x0a418820); - nv_wr32(priv, 0x419b14, 0x000000e6); - nv_wr32(priv, 0x419bd0, 0x00900103); - switch (nv_device(priv)->chipset) { - case 0xc1: - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x419be0, 0x00400001); - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc8: - case 0xce: - case 0xcf: - nv_wr32(priv, 0x419be0, 0x00000001); - break; - default: - BUG_ON(1); - break; - } - nv_wr32(priv, 0x419be4, 0x00000000); - switch (nv_device(priv)->chipset) { - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x419c00, 0x0000000a); - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc1: - case 0xc8: - case 0xce: - case 0xcf: - nv_wr32(priv, 0x419c00, 0x00000002); - break; - default: - BUG_ON(1); - break; - } - nv_wr32(priv, 0x419c04, 0x00000006); - nv_wr32(priv, 0x419c08, 0x00000002); - nv_wr32(priv, 0x419c20, 0x00000000); - switch (nv_device(priv)->chipset) { - case 0xc3: - case 0xc4: - case 0xc1: - case 0xce: - case 0xcf: - nv_wr32(priv, 0x419cb0, 0x00020048); - break; - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x419c24, 0x00084210); - nv_wr32(priv, 0x419c28, 0x3cf3cf3c); - nv_wr32(priv, 0x419cb0, 0x00020048); - break; - case 0xc0: - case 0xc8: - nv_wr32(priv, 0x419cb0, 0x00060048); - break; - default: - BUG_ON(1); - break; - } - nv_wr32(priv, 0x419ce8, 0x00000000); - nv_wr32(priv, 0x419cf4, 0x00000183); - switch (nv_device(priv)->chipset) { - case 0xc1: - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x419d20, 0x12180000); - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc8: - case 0xce: - case 0xcf: - nv_wr32(priv, 0x419d20, 0x02180000); - break; - default: - BUG_ON(1); - break; - } - nv_wr32(priv, 0x419d24, 0x00001fff); - switch (nv_device(priv)->chipset) { - case 0xc1: - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x419d44, 0x02180218); - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc8: - case 0xce: - case 0xcf: - break; - default: - BUG_ON(1); - break; - } - nv_wr32(priv, 0x419e04, 0x00000000); - nv_wr32(priv, 0x419e08, 0x00000000); - nv_wr32(priv, 0x419e0c, 0x00000000); - nv_wr32(priv, 0x419e10, 0x00000002); - nv_wr32(priv, 0x419e44, 0x001beff2); - nv_wr32(priv, 0x419e48, 0x00000000); - nv_wr32(priv, 0x419e4c, 0x0000000f); - nv_wr32(priv, 0x419e50, 0x00000000); - nv_wr32(priv, 0x419e54, 0x00000000); - nv_wr32(priv, 0x419e58, 0x00000000); - nv_wr32(priv, 0x419e5c, 0x00000000); - nv_wr32(priv, 0x419e60, 0x00000000); - nv_wr32(priv, 0x419e64, 0x00000000); - nv_wr32(priv, 0x419e68, 0x00000000); - nv_wr32(priv, 0x419e6c, 0x00000000); - nv_wr32(priv, 0x419e70, 0x00000000); - nv_wr32(priv, 0x419e74, 0x00000000); - nv_wr32(priv, 0x419e78, 0x00000000); - nv_wr32(priv, 0x419e7c, 0x00000000); - nv_wr32(priv, 0x419e80, 0x00000000); - nv_wr32(priv, 0x419e84, 0x00000000); - nv_wr32(priv, 0x419e88, 0x00000000); - nv_wr32(priv, 0x419e8c, 0x00000000); - nv_wr32(priv, 0x419e90, 0x00000000); - nv_wr32(priv, 0x419e98, 0x00000000); - switch (nv_device(priv)->chipset) { - case 0xc0: - case 0xc8: - break; - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x419ee0, 0x00010110); - break; - case 0xc3: - case 0xc4: - case 0xc1: - case 0xce: - case 0xcf: - nv_wr32(priv, 0x419ee0, 0x00011110); - break; - default: - BUG_ON(1); - break; - } - switch (nv_device(priv)->chipset) { - case 0xc0: - case 0xc8: - nv_wr32(priv, 0x419f50, 0x00000000); - nv_wr32(priv, 0x419f54, 0x00000000); - break; - case 0xc3: - case 0xc4: - case 0xc1: - case 0xce: - case 0xcf: - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x419f30, 0x00000000); - nv_wr32(priv, 0x419f34, 0x00000000); - nv_wr32(priv, 0x419f38, 0x00000000); - nv_wr32(priv, 0x419f3c, 0x00000000); - nv_wr32(priv, 0x419f40, 0x00000000); - nv_wr32(priv, 0x419f44, 0x00000000); - nv_wr32(priv, 0x419f48, 0x00000000); - nv_wr32(priv, 0x419f4c, 0x00000000); - nv_wr32(priv, 0x419f50, 0x00000000); - nv_wr32(priv, 0x419f54, 0x00000000); - nv_wr32(priv, 0x419f58, 0x00000000); - break; - break; - default: - BUG_ON(1); - break; - } -} - -int -nvc0_grctx_generate(struct nvc0_graph_priv *priv) -{ - struct nvc0_grctx info; - int ret, i, gpc, tpc, id; - u32 fermi = nvc0_graph_class(priv); - u32 r000260, tmp; - - ret = nvc0_grctx_init(priv, &info); - if (ret) - return ret; - - r000260 = nv_rd32(priv, 0x000260); - nv_wr32(priv, 0x000260, r000260 & ~1); - nv_wr32(priv, 0x400208, 0x00000000); - - nvc0_grctx_generate_dispatch(priv); - nvc0_grctx_generate_macro(priv); - nvc0_grctx_generate_m2mf(priv); - nvc0_grctx_generate_unk47xx(priv); - nvc0_grctx_generate_shaders(priv); - nvc0_grctx_generate_unk60xx(priv); - nvc0_grctx_generate_unk64xx(priv); - nvc0_grctx_generate_tpbus(priv); - nvc0_grctx_generate_ccache(priv); - nvc0_grctx_generate_rop(priv); - nvc0_grctx_generate_gpc(priv); - nvc0_grctx_generate_tp(priv); - - nv_wr32(priv, 0x404154, 0x00000000); - - /* generate per-context mmio list data */ - mmio_data(0x002000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS); - mmio_data(0x008000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS); - mmio_data(0x060000, 0x1000, NV_MEM_ACCESS_RW); - mmio_list(0x408004, 0x00000000, 8, 0); - mmio_list(0x408008, 0x80000018, 0, 0); - mmio_list(0x40800c, 0x00000000, 8, 1); - mmio_list(0x408010, 0x80000000, 0, 0); - mmio_list(0x418810, 0x80000000, 12, 2); - mmio_list(0x419848, 0x10000000, 12, 2); - mmio_list(0x419004, 0x00000000, 8, 1); - mmio_list(0x419008, 0x00000000, 0, 0); - mmio_list(0x418808, 0x00000000, 8, 0); - mmio_list(0x41880c, 0x80000018, 0, 0); - switch (nv_device(priv)->chipset) { - case 0xc1: - case 0xd9: - case 0xd7: - tmp = 0x02180000; - mmio_list(0x405830, 0x00000218 | tmp, 0, 0); - mmio_list(0x4064c4, 0x0086ffff, 0, 0); - for (gpc = 0; gpc < priv->gpc_nr; gpc++) { - for (tpc = 0; tpc < priv->tpc_nr[gpc]; tpc++) { - u32 reg = TPC_UNIT(gpc, tpc, 0x0520); - mmio_list(reg, 0x10000000 | tmp, 0, 0); - tmp += 0x0324; - } - for (tpc = 0; tpc < priv->tpc_nr[gpc]; tpc++) { - u32 reg = TPC_UNIT(gpc, tpc, 0x0544); - mmio_list(reg, tmp, 0, 0); - tmp += 0x0324; - } - } - break; - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc8: - case 0xce: - case 0xcf: - tmp = 0x02180000; - mmio_list(0x405830, tmp, 0, 0); - for (gpc = 0; gpc < priv->gpc_nr; gpc++) { - for (tpc = 0; tpc < priv->tpc_nr[gpc]; tpc++) { - u32 reg = TPC_UNIT(gpc, tpc, 0x0520); - mmio_list(reg, tmp, 0, 0); - tmp += 0x0324; - } - } - break; - default: - BUG_ON(1); - break; - } - - for (tpc = 0, id = 0; tpc < 4; tpc++) { - for (gpc = 0; gpc < priv->gpc_nr; gpc++) { - if (tpc < priv->tpc_nr[gpc]) { - nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x698), id); - nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x4e8), id); - nv_wr32(priv, GPC_UNIT(gpc, 0x0c10 + tpc * 4), id); - nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x088), id); - id++; - } - - nv_wr32(priv, GPC_UNIT(gpc, 0x0c08), priv->tpc_nr[gpc]); - nv_wr32(priv, GPC_UNIT(gpc, 0x0c8c), priv->tpc_nr[gpc]); - } - } - - tmp = 0; - for (i = 0; i < priv->gpc_nr; i++) - tmp |= priv->tpc_nr[i] << (i * 4); - nv_wr32(priv, 0x406028, tmp); - nv_wr32(priv, 0x405870, tmp); - - nv_wr32(priv, 0x40602c, 0x00000000); - nv_wr32(priv, 0x405874, 0x00000000); - nv_wr32(priv, 0x406030, 0x00000000); - nv_wr32(priv, 0x405878, 0x00000000); - nv_wr32(priv, 0x406034, 0x00000000); - nv_wr32(priv, 0x40587c, 0x00000000); - - if (1) { - u8 tpcnr[GPC_MAX], data[TPC_MAX]; - - memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr)); - memset(data, 0x1f, sizeof(data)); - - gpc = -1; - for (tpc = 0; tpc < priv->tpc_total; tpc++) { - do { - gpc = (gpc + 1) % priv->gpc_nr; - } while (!tpcnr[gpc]); - tpcnr[gpc]--; - data[tpc] = gpc; - } - - for (i = 0; i < 4; i++) - nv_wr32(priv, 0x4060a8 + (i * 4), ((u32 *)data)[i]); - } - - if (1) { - u32 data[6] = {}, data2[2] = {}; - u8 tpcnr[GPC_MAX]; - u8 shift, ntpcv; - - /* calculate first set of magics */ - memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr)); - - gpc = -1; - for (tpc = 0; tpc < priv->tpc_total; tpc++) { - do { - gpc = (gpc + 1) % priv->gpc_nr; - } while (!tpcnr[gpc]); - tpcnr[gpc]--; - - data[tpc / 6] |= gpc << ((tpc % 6) * 5); - } - - for (; tpc < 32; tpc++) - data[tpc / 6] |= 7 << ((tpc % 6) * 5); - - /* and the second... */ - shift = 0; - ntpcv = priv->tpc_total; - while (!(ntpcv & (1 << 4))) { - ntpcv <<= 1; - shift++; - } - - data2[0] = (ntpcv << 16); - data2[0] |= (shift << 21); - data2[0] |= (((1 << (0 + 5)) % ntpcv) << 24); - for (i = 1; i < 7; i++) - data2[1] |= ((1 << (i + 5)) % ntpcv) << ((i - 1) * 5); - - /* GPC_BROADCAST */ - nv_wr32(priv, 0x418bb8, (priv->tpc_total << 8) | - priv->magic_not_rop_nr); - for (i = 0; i < 6; i++) - nv_wr32(priv, 0x418b08 + (i * 4), data[i]); - - /* GPC_BROADCAST.TP_BROADCAST */ - nv_wr32(priv, 0x419bd0, (priv->tpc_total << 8) | - priv->magic_not_rop_nr | - data2[0]); - nv_wr32(priv, 0x419be4, data2[1]); - for (i = 0; i < 6; i++) - nv_wr32(priv, 0x419b00 + (i * 4), data[i]); - - /* UNK78xx */ - nv_wr32(priv, 0x4078bc, (priv->tpc_total << 8) | - priv->magic_not_rop_nr); - for (i = 0; i < 6; i++) - nv_wr32(priv, 0x40780c + (i * 4), data[i]); - } - - if (1) { - u32 tpc_mask = 0, tpc_set = 0; - u8 tpcnr[GPC_MAX], a, b; - - memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr)); - for (gpc = 0; gpc < priv->gpc_nr; gpc++) - tpc_mask |= ((1 << priv->tpc_nr[gpc]) - 1) << (gpc * 8); - - for (i = 0, gpc = -1, b = -1; i < 32; i++) { - a = (i * (priv->tpc_total - 1)) / 32; - if (a != b) { - b = a; - do { - gpc = (gpc + 1) % priv->gpc_nr; - } while (!tpcnr[gpc]); - tpc = priv->tpc_nr[gpc] - tpcnr[gpc]--; - - tpc_set |= 1 << ((gpc * 8) + tpc); - } - - nv_wr32(priv, 0x406800 + (i * 0x20), tpc_set); - nv_wr32(priv, 0x406c00 + (i * 0x20), tpc_set ^ tpc_mask); - } - } - - nv_wr32(priv, 0x400208, 0x80000000); - - nv_icmd(priv, 0x00001000, 0x00000004); - nv_icmd(priv, 0x000000a9, 0x0000ffff); - nv_icmd(priv, 0x00000038, 0x0fac6881); - nv_icmd(priv, 0x0000003d, 0x00000001); - nv_icmd(priv, 0x000000e8, 0x00000400); - nv_icmd(priv, 0x000000e9, 0x00000400); - nv_icmd(priv, 0x000000ea, 0x00000400); - nv_icmd(priv, 0x000000eb, 0x00000400); - nv_icmd(priv, 0x000000ec, 0x00000400); - nv_icmd(priv, 0x000000ed, 0x00000400); - nv_icmd(priv, 0x000000ee, 0x00000400); - nv_icmd(priv, 0x000000ef, 0x00000400); - nv_icmd(priv, 0x00000078, 0x00000300); - nv_icmd(priv, 0x00000079, 0x00000300); - nv_icmd(priv, 0x0000007a, 0x00000300); - nv_icmd(priv, 0x0000007b, 0x00000300); - nv_icmd(priv, 0x0000007c, 0x00000300); - nv_icmd(priv, 0x0000007d, 0x00000300); - nv_icmd(priv, 0x0000007e, 0x00000300); - nv_icmd(priv, 0x0000007f, 0x00000300); - nv_icmd(priv, 0x00000050, 0x00000011); - nv_icmd(priv, 0x00000058, 0x00000008); - nv_icmd(priv, 0x00000059, 0x00000008); - nv_icmd(priv, 0x0000005a, 0x00000008); - nv_icmd(priv, 0x0000005b, 0x00000008); - nv_icmd(priv, 0x0000005c, 0x00000008); - nv_icmd(priv, 0x0000005d, 0x00000008); - nv_icmd(priv, 0x0000005e, 0x00000008); - nv_icmd(priv, 0x0000005f, 0x00000008); - nv_icmd(priv, 0x00000208, 0x00000001); - nv_icmd(priv, 0x00000209, 0x00000001); - nv_icmd(priv, 0x0000020a, 0x00000001); - nv_icmd(priv, 0x0000020b, 0x00000001); - nv_icmd(priv, 0x0000020c, 0x00000001); - nv_icmd(priv, 0x0000020d, 0x00000001); - nv_icmd(priv, 0x0000020e, 0x00000001); - nv_icmd(priv, 0x0000020f, 0x00000001); - nv_icmd(priv, 0x00000081, 0x00000001); - nv_icmd(priv, 0x00000085, 0x00000004); - nv_icmd(priv, 0x00000088, 0x00000400); - nv_icmd(priv, 0x00000090, 0x00000300); - nv_icmd(priv, 0x00000098, 0x00001001); - nv_icmd(priv, 0x000000e3, 0x00000001); - nv_icmd(priv, 0x000000da, 0x00000001); - nv_icmd(priv, 0x000000f8, 0x00000003); - nv_icmd(priv, 0x000000fa, 0x00000001); - nv_icmd(priv, 0x0000009f, 0x0000ffff); - nv_icmd(priv, 0x000000a0, 0x0000ffff); - nv_icmd(priv, 0x000000a1, 0x0000ffff); - nv_icmd(priv, 0x000000a2, 0x0000ffff); - nv_icmd(priv, 0x000000b1, 0x00000001); - nv_icmd(priv, 0x000000b2, 0x00000000); - nv_icmd(priv, 0x000000b3, 0x00000000); - nv_icmd(priv, 0x000000b4, 0x00000000); - nv_icmd(priv, 0x000000b5, 0x00000000); - nv_icmd(priv, 0x000000b6, 0x00000000); - nv_icmd(priv, 0x000000b7, 0x00000000); - nv_icmd(priv, 0x000000b8, 0x00000000); - nv_icmd(priv, 0x000000b9, 0x00000000); - nv_icmd(priv, 0x000000ba, 0x00000000); - nv_icmd(priv, 0x000000bb, 0x00000000); - nv_icmd(priv, 0x000000bc, 0x00000000); - nv_icmd(priv, 0x000000bd, 0x00000000); - nv_icmd(priv, 0x000000be, 0x00000000); - nv_icmd(priv, 0x000000bf, 0x00000000); - nv_icmd(priv, 0x000000c0, 0x00000000); - nv_icmd(priv, 0x000000c1, 0x00000000); - nv_icmd(priv, 0x000000c2, 0x00000000); - nv_icmd(priv, 0x000000c3, 0x00000000); - nv_icmd(priv, 0x000000c4, 0x00000000); - nv_icmd(priv, 0x000000c5, 0x00000000); - nv_icmd(priv, 0x000000c6, 0x00000000); - nv_icmd(priv, 0x000000c7, 0x00000000); - nv_icmd(priv, 0x000000c8, 0x00000000); - nv_icmd(priv, 0x000000c9, 0x00000000); - nv_icmd(priv, 0x000000ca, 0x00000000); - nv_icmd(priv, 0x000000cb, 0x00000000); - nv_icmd(priv, 0x000000cc, 0x00000000); - nv_icmd(priv, 0x000000cd, 0x00000000); - nv_icmd(priv, 0x000000ce, 0x00000000); - nv_icmd(priv, 0x000000cf, 0x00000000); - nv_icmd(priv, 0x000000d0, 0x00000000); - nv_icmd(priv, 0x000000d1, 0x00000000); - nv_icmd(priv, 0x000000d2, 0x00000000); - nv_icmd(priv, 0x000000d3, 0x00000000); - nv_icmd(priv, 0x000000d4, 0x00000000); - nv_icmd(priv, 0x000000d5, 0x00000000); - nv_icmd(priv, 0x000000d6, 0x00000000); - nv_icmd(priv, 0x000000d7, 0x00000000); - nv_icmd(priv, 0x000000d8, 0x00000000); - nv_icmd(priv, 0x000000d9, 0x00000000); - nv_icmd(priv, 0x00000210, 0x00000040); - nv_icmd(priv, 0x00000211, 0x00000040); - nv_icmd(priv, 0x00000212, 0x00000040); - nv_icmd(priv, 0x00000213, 0x00000040); - nv_icmd(priv, 0x00000214, 0x00000040); - nv_icmd(priv, 0x00000215, 0x00000040); - nv_icmd(priv, 0x00000216, 0x00000040); - nv_icmd(priv, 0x00000217, 0x00000040); - switch (nv_device(priv)->chipset) { - case 0xd9: - case 0xd7: - for (i = 0x400; i <= 0x417; i++) - nv_icmd(priv, i, 0x00000040); - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc1: - case 0xc8: - case 0xce: - case 0xcf: - break; - default: - BUG_ON(1); - break; - } - nv_icmd(priv, 0x00000218, 0x0000c080); - nv_icmd(priv, 0x00000219, 0x0000c080); - nv_icmd(priv, 0x0000021a, 0x0000c080); - nv_icmd(priv, 0x0000021b, 0x0000c080); - nv_icmd(priv, 0x0000021c, 0x0000c080); - nv_icmd(priv, 0x0000021d, 0x0000c080); - nv_icmd(priv, 0x0000021e, 0x0000c080); - nv_icmd(priv, 0x0000021f, 0x0000c080); - switch (nv_device(priv)->chipset) { - case 0xd9: - case 0xd7: - for (i = 0x440; i <= 0x457; i++) - nv_icmd(priv, i, 0x0000c080); - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc1: - case 0xc8: - case 0xce: - case 0xcf: - break; - default: - BUG_ON(1); - break; - } - nv_icmd(priv, 0x000000ad, 0x0000013e); - nv_icmd(priv, 0x000000e1, 0x00000010); - nv_icmd(priv, 0x00000290, 0x00000000); - nv_icmd(priv, 0x00000291, 0x00000000); - nv_icmd(priv, 0x00000292, 0x00000000); - nv_icmd(priv, 0x00000293, 0x00000000); - nv_icmd(priv, 0x00000294, 0x00000000); - nv_icmd(priv, 0x00000295, 0x00000000); - nv_icmd(priv, 0x00000296, 0x00000000); - nv_icmd(priv, 0x00000297, 0x00000000); - nv_icmd(priv, 0x00000298, 0x00000000); - nv_icmd(priv, 0x00000299, 0x00000000); - nv_icmd(priv, 0x0000029a, 0x00000000); - nv_icmd(priv, 0x0000029b, 0x00000000); - nv_icmd(priv, 0x0000029c, 0x00000000); - nv_icmd(priv, 0x0000029d, 0x00000000); - nv_icmd(priv, 0x0000029e, 0x00000000); - nv_icmd(priv, 0x0000029f, 0x00000000); - nv_icmd(priv, 0x000003b0, 0x00000000); - nv_icmd(priv, 0x000003b1, 0x00000000); - nv_icmd(priv, 0x000003b2, 0x00000000); - nv_icmd(priv, 0x000003b3, 0x00000000); - nv_icmd(priv, 0x000003b4, 0x00000000); - nv_icmd(priv, 0x000003b5, 0x00000000); - nv_icmd(priv, 0x000003b6, 0x00000000); - nv_icmd(priv, 0x000003b7, 0x00000000); - nv_icmd(priv, 0x000003b8, 0x00000000); - nv_icmd(priv, 0x000003b9, 0x00000000); - nv_icmd(priv, 0x000003ba, 0x00000000); - nv_icmd(priv, 0x000003bb, 0x00000000); - nv_icmd(priv, 0x000003bc, 0x00000000); - nv_icmd(priv, 0x000003bd, 0x00000000); - nv_icmd(priv, 0x000003be, 0x00000000); - nv_icmd(priv, 0x000003bf, 0x00000000); - nv_icmd(priv, 0x000002a0, 0x00000000); - nv_icmd(priv, 0x000002a1, 0x00000000); - nv_icmd(priv, 0x000002a2, 0x00000000); - nv_icmd(priv, 0x000002a3, 0x00000000); - nv_icmd(priv, 0x000002a4, 0x00000000); - nv_icmd(priv, 0x000002a5, 0x00000000); - nv_icmd(priv, 0x000002a6, 0x00000000); - nv_icmd(priv, 0x000002a7, 0x00000000); - nv_icmd(priv, 0x000002a8, 0x00000000); - nv_icmd(priv, 0x000002a9, 0x00000000); - nv_icmd(priv, 0x000002aa, 0x00000000); - nv_icmd(priv, 0x000002ab, 0x00000000); - nv_icmd(priv, 0x000002ac, 0x00000000); - nv_icmd(priv, 0x000002ad, 0x00000000); - nv_icmd(priv, 0x000002ae, 0x00000000); - nv_icmd(priv, 0x000002af, 0x00000000); - nv_icmd(priv, 0x00000420, 0x00000000); - nv_icmd(priv, 0x00000421, 0x00000000); - nv_icmd(priv, 0x00000422, 0x00000000); - nv_icmd(priv, 0x00000423, 0x00000000); - nv_icmd(priv, 0x00000424, 0x00000000); - nv_icmd(priv, 0x00000425, 0x00000000); - nv_icmd(priv, 0x00000426, 0x00000000); - nv_icmd(priv, 0x00000427, 0x00000000); - nv_icmd(priv, 0x00000428, 0x00000000); - nv_icmd(priv, 0x00000429, 0x00000000); - nv_icmd(priv, 0x0000042a, 0x00000000); - nv_icmd(priv, 0x0000042b, 0x00000000); - nv_icmd(priv, 0x0000042c, 0x00000000); - nv_icmd(priv, 0x0000042d, 0x00000000); - nv_icmd(priv, 0x0000042e, 0x00000000); - nv_icmd(priv, 0x0000042f, 0x00000000); - nv_icmd(priv, 0x000002b0, 0x00000000); - nv_icmd(priv, 0x000002b1, 0x00000000); - nv_icmd(priv, 0x000002b2, 0x00000000); - nv_icmd(priv, 0x000002b3, 0x00000000); - nv_icmd(priv, 0x000002b4, 0x00000000); - nv_icmd(priv, 0x000002b5, 0x00000000); - nv_icmd(priv, 0x000002b6, 0x00000000); - nv_icmd(priv, 0x000002b7, 0x00000000); - nv_icmd(priv, 0x000002b8, 0x00000000); - nv_icmd(priv, 0x000002b9, 0x00000000); - nv_icmd(priv, 0x000002ba, 0x00000000); - nv_icmd(priv, 0x000002bb, 0x00000000); - nv_icmd(priv, 0x000002bc, 0x00000000); - nv_icmd(priv, 0x000002bd, 0x00000000); - nv_icmd(priv, 0x000002be, 0x00000000); - nv_icmd(priv, 0x000002bf, 0x00000000); - nv_icmd(priv, 0x00000430, 0x00000000); - nv_icmd(priv, 0x00000431, 0x00000000); - nv_icmd(priv, 0x00000432, 0x00000000); - nv_icmd(priv, 0x00000433, 0x00000000); - nv_icmd(priv, 0x00000434, 0x00000000); - nv_icmd(priv, 0x00000435, 0x00000000); - nv_icmd(priv, 0x00000436, 0x00000000); - nv_icmd(priv, 0x00000437, 0x00000000); - nv_icmd(priv, 0x00000438, 0x00000000); - nv_icmd(priv, 0x00000439, 0x00000000); - nv_icmd(priv, 0x0000043a, 0x00000000); - nv_icmd(priv, 0x0000043b, 0x00000000); - nv_icmd(priv, 0x0000043c, 0x00000000); - nv_icmd(priv, 0x0000043d, 0x00000000); - nv_icmd(priv, 0x0000043e, 0x00000000); - nv_icmd(priv, 0x0000043f, 0x00000000); - nv_icmd(priv, 0x000002c0, 0x00000000); - nv_icmd(priv, 0x000002c1, 0x00000000); - nv_icmd(priv, 0x000002c2, 0x00000000); - nv_icmd(priv, 0x000002c3, 0x00000000); - nv_icmd(priv, 0x000002c4, 0x00000000); - nv_icmd(priv, 0x000002c5, 0x00000000); - nv_icmd(priv, 0x000002c6, 0x00000000); - nv_icmd(priv, 0x000002c7, 0x00000000); - nv_icmd(priv, 0x000002c8, 0x00000000); - nv_icmd(priv, 0x000002c9, 0x00000000); - nv_icmd(priv, 0x000002ca, 0x00000000); - nv_icmd(priv, 0x000002cb, 0x00000000); - nv_icmd(priv, 0x000002cc, 0x00000000); - nv_icmd(priv, 0x000002cd, 0x00000000); - nv_icmd(priv, 0x000002ce, 0x00000000); - nv_icmd(priv, 0x000002cf, 0x00000000); - nv_icmd(priv, 0x000004d0, 0x00000000); - nv_icmd(priv, 0x000004d1, 0x00000000); - nv_icmd(priv, 0x000004d2, 0x00000000); - nv_icmd(priv, 0x000004d3, 0x00000000); - nv_icmd(priv, 0x000004d4, 0x00000000); - nv_icmd(priv, 0x000004d5, 0x00000000); - nv_icmd(priv, 0x000004d6, 0x00000000); - nv_icmd(priv, 0x000004d7, 0x00000000); - nv_icmd(priv, 0x000004d8, 0x00000000); - nv_icmd(priv, 0x000004d9, 0x00000000); - nv_icmd(priv, 0x000004da, 0x00000000); - nv_icmd(priv, 0x000004db, 0x00000000); - nv_icmd(priv, 0x000004dc, 0x00000000); - nv_icmd(priv, 0x000004dd, 0x00000000); - nv_icmd(priv, 0x000004de, 0x00000000); - nv_icmd(priv, 0x000004df, 0x00000000); - nv_icmd(priv, 0x00000720, 0x00000000); - nv_icmd(priv, 0x00000721, 0x00000000); - nv_icmd(priv, 0x00000722, 0x00000000); - nv_icmd(priv, 0x00000723, 0x00000000); - nv_icmd(priv, 0x00000724, 0x00000000); - nv_icmd(priv, 0x00000725, 0x00000000); - nv_icmd(priv, 0x00000726, 0x00000000); - nv_icmd(priv, 0x00000727, 0x00000000); - nv_icmd(priv, 0x00000728, 0x00000000); - nv_icmd(priv, 0x00000729, 0x00000000); - nv_icmd(priv, 0x0000072a, 0x00000000); - nv_icmd(priv, 0x0000072b, 0x00000000); - nv_icmd(priv, 0x0000072c, 0x00000000); - nv_icmd(priv, 0x0000072d, 0x00000000); - nv_icmd(priv, 0x0000072e, 0x00000000); - nv_icmd(priv, 0x0000072f, 0x00000000); - nv_icmd(priv, 0x000008c0, 0x00000000); - nv_icmd(priv, 0x000008c1, 0x00000000); - nv_icmd(priv, 0x000008c2, 0x00000000); - nv_icmd(priv, 0x000008c3, 0x00000000); - nv_icmd(priv, 0x000008c4, 0x00000000); - nv_icmd(priv, 0x000008c5, 0x00000000); - nv_icmd(priv, 0x000008c6, 0x00000000); - nv_icmd(priv, 0x000008c7, 0x00000000); - nv_icmd(priv, 0x000008c8, 0x00000000); - nv_icmd(priv, 0x000008c9, 0x00000000); - nv_icmd(priv, 0x000008ca, 0x00000000); - nv_icmd(priv, 0x000008cb, 0x00000000); - nv_icmd(priv, 0x000008cc, 0x00000000); - nv_icmd(priv, 0x000008cd, 0x00000000); - nv_icmd(priv, 0x000008ce, 0x00000000); - nv_icmd(priv, 0x000008cf, 0x00000000); - nv_icmd(priv, 0x00000890, 0x00000000); - nv_icmd(priv, 0x00000891, 0x00000000); - nv_icmd(priv, 0x00000892, 0x00000000); - nv_icmd(priv, 0x00000893, 0x00000000); - nv_icmd(priv, 0x00000894, 0x00000000); - nv_icmd(priv, 0x00000895, 0x00000000); - nv_icmd(priv, 0x00000896, 0x00000000); - nv_icmd(priv, 0x00000897, 0x00000000); - nv_icmd(priv, 0x00000898, 0x00000000); - nv_icmd(priv, 0x00000899, 0x00000000); - nv_icmd(priv, 0x0000089a, 0x00000000); - nv_icmd(priv, 0x0000089b, 0x00000000); - nv_icmd(priv, 0x0000089c, 0x00000000); - nv_icmd(priv, 0x0000089d, 0x00000000); - nv_icmd(priv, 0x0000089e, 0x00000000); - nv_icmd(priv, 0x0000089f, 0x00000000); - nv_icmd(priv, 0x000008e0, 0x00000000); - nv_icmd(priv, 0x000008e1, 0x00000000); - nv_icmd(priv, 0x000008e2, 0x00000000); - nv_icmd(priv, 0x000008e3, 0x00000000); - nv_icmd(priv, 0x000008e4, 0x00000000); - nv_icmd(priv, 0x000008e5, 0x00000000); - nv_icmd(priv, 0x000008e6, 0x00000000); - nv_icmd(priv, 0x000008e7, 0x00000000); - nv_icmd(priv, 0x000008e8, 0x00000000); - nv_icmd(priv, 0x000008e9, 0x00000000); - nv_icmd(priv, 0x000008ea, 0x00000000); - nv_icmd(priv, 0x000008eb, 0x00000000); - nv_icmd(priv, 0x000008ec, 0x00000000); - nv_icmd(priv, 0x000008ed, 0x00000000); - nv_icmd(priv, 0x000008ee, 0x00000000); - nv_icmd(priv, 0x000008ef, 0x00000000); - nv_icmd(priv, 0x000008a0, 0x00000000); - nv_icmd(priv, 0x000008a1, 0x00000000); - nv_icmd(priv, 0x000008a2, 0x00000000); - nv_icmd(priv, 0x000008a3, 0x00000000); - nv_icmd(priv, 0x000008a4, 0x00000000); - nv_icmd(priv, 0x000008a5, 0x00000000); - nv_icmd(priv, 0x000008a6, 0x00000000); - nv_icmd(priv, 0x000008a7, 0x00000000); - nv_icmd(priv, 0x000008a8, 0x00000000); - nv_icmd(priv, 0x000008a9, 0x00000000); - nv_icmd(priv, 0x000008aa, 0x00000000); - nv_icmd(priv, 0x000008ab, 0x00000000); - nv_icmd(priv, 0x000008ac, 0x00000000); - nv_icmd(priv, 0x000008ad, 0x00000000); - nv_icmd(priv, 0x000008ae, 0x00000000); - nv_icmd(priv, 0x000008af, 0x00000000); - nv_icmd(priv, 0x000008f0, 0x00000000); - nv_icmd(priv, 0x000008f1, 0x00000000); - nv_icmd(priv, 0x000008f2, 0x00000000); - nv_icmd(priv, 0x000008f3, 0x00000000); - nv_icmd(priv, 0x000008f4, 0x00000000); - nv_icmd(priv, 0x000008f5, 0x00000000); - nv_icmd(priv, 0x000008f6, 0x00000000); - nv_icmd(priv, 0x000008f7, 0x00000000); - nv_icmd(priv, 0x000008f8, 0x00000000); - nv_icmd(priv, 0x000008f9, 0x00000000); - nv_icmd(priv, 0x000008fa, 0x00000000); - nv_icmd(priv, 0x000008fb, 0x00000000); - nv_icmd(priv, 0x000008fc, 0x00000000); - nv_icmd(priv, 0x000008fd, 0x00000000); - nv_icmd(priv, 0x000008fe, 0x00000000); - nv_icmd(priv, 0x000008ff, 0x00000000); - nv_icmd(priv, 0x0000094c, 0x000000ff); - nv_icmd(priv, 0x0000094d, 0xffffffff); - nv_icmd(priv, 0x0000094e, 0x00000002); - nv_icmd(priv, 0x000002ec, 0x00000001); - nv_icmd(priv, 0x00000303, 0x00000001); - nv_icmd(priv, 0x000002e6, 0x00000001); - nv_icmd(priv, 0x00000466, 0x00000052); - nv_icmd(priv, 0x00000301, 0x3f800000); - nv_icmd(priv, 0x00000304, 0x30201000); - nv_icmd(priv, 0x00000305, 0x70605040); - nv_icmd(priv, 0x00000306, 0xb8a89888); - nv_icmd(priv, 0x00000307, 0xf8e8d8c8); - nv_icmd(priv, 0x0000030a, 0x00ffff00); - nv_icmd(priv, 0x0000030b, 0x0000001a); - nv_icmd(priv, 0x0000030c, 0x00000001); - nv_icmd(priv, 0x00000318, 0x00000001); - nv_icmd(priv, 0x00000340, 0x00000000); - nv_icmd(priv, 0x00000375, 0x00000001); - nv_icmd(priv, 0x00000351, 0x00000100); - nv_icmd(priv, 0x0000037d, 0x00000006); - nv_icmd(priv, 0x000003a0, 0x00000002); - nv_icmd(priv, 0x000003aa, 0x00000001); - nv_icmd(priv, 0x000003a9, 0x00000001); - nv_icmd(priv, 0x00000380, 0x00000001); - nv_icmd(priv, 0x00000360, 0x00000040); - nv_icmd(priv, 0x00000366, 0x00000000); - nv_icmd(priv, 0x00000367, 0x00000000); - nv_icmd(priv, 0x00000368, 0x00001fff); - nv_icmd(priv, 0x00000370, 0x00000000); - nv_icmd(priv, 0x00000371, 0x00000000); - nv_icmd(priv, 0x00000372, 0x003fffff); - nv_icmd(priv, 0x0000037a, 0x00000012); - nv_icmd(priv, 0x000005e0, 0x00000022); - nv_icmd(priv, 0x000005e1, 0x00000022); - nv_icmd(priv, 0x000005e2, 0x00000022); - nv_icmd(priv, 0x000005e3, 0x00000022); - nv_icmd(priv, 0x000005e4, 0x00000022); - nv_icmd(priv, 0x00000619, 0x00000003); - nv_icmd(priv, 0x00000811, 0x00000003); - nv_icmd(priv, 0x00000812, 0x00000004); - nv_icmd(priv, 0x00000813, 0x00000006); - nv_icmd(priv, 0x00000814, 0x00000008); - nv_icmd(priv, 0x00000815, 0x0000000b); - nv_icmd(priv, 0x00000800, 0x00000001); - nv_icmd(priv, 0x00000801, 0x00000001); - nv_icmd(priv, 0x00000802, 0x00000001); - nv_icmd(priv, 0x00000803, 0x00000001); - nv_icmd(priv, 0x00000804, 0x00000001); - nv_icmd(priv, 0x00000805, 0x00000001); - nv_icmd(priv, 0x00000632, 0x00000001); - nv_icmd(priv, 0x00000633, 0x00000002); - nv_icmd(priv, 0x00000634, 0x00000003); - nv_icmd(priv, 0x00000635, 0x00000004); - nv_icmd(priv, 0x00000654, 0x3f800000); - nv_icmd(priv, 0x00000657, 0x3f800000); - nv_icmd(priv, 0x00000655, 0x3f800000); - nv_icmd(priv, 0x00000656, 0x3f800000); - nv_icmd(priv, 0x000006cd, 0x3f800000); - nv_icmd(priv, 0x000007f5, 0x3f800000); - nv_icmd(priv, 0x000007dc, 0x39291909); - nv_icmd(priv, 0x000007dd, 0x79695949); - nv_icmd(priv, 0x000007de, 0xb9a99989); - nv_icmd(priv, 0x000007df, 0xf9e9d9c9); - nv_icmd(priv, 0x000007e8, 0x00003210); - nv_icmd(priv, 0x000007e9, 0x00007654); - nv_icmd(priv, 0x000007ea, 0x00000098); - nv_icmd(priv, 0x000007ec, 0x39291909); - nv_icmd(priv, 0x000007ed, 0x79695949); - nv_icmd(priv, 0x000007ee, 0xb9a99989); - nv_icmd(priv, 0x000007ef, 0xf9e9d9c9); - nv_icmd(priv, 0x000007f0, 0x00003210); - nv_icmd(priv, 0x000007f1, 0x00007654); - nv_icmd(priv, 0x000007f2, 0x00000098); - nv_icmd(priv, 0x000005a5, 0x00000001); - nv_icmd(priv, 0x00000980, 0x00000000); - nv_icmd(priv, 0x00000981, 0x00000000); - nv_icmd(priv, 0x00000982, 0x00000000); - nv_icmd(priv, 0x00000983, 0x00000000); - nv_icmd(priv, 0x00000984, 0x00000000); - nv_icmd(priv, 0x00000985, 0x00000000); - nv_icmd(priv, 0x00000986, 0x00000000); - nv_icmd(priv, 0x00000987, 0x00000000); - nv_icmd(priv, 0x00000988, 0x00000000); - nv_icmd(priv, 0x00000989, 0x00000000); - nv_icmd(priv, 0x0000098a, 0x00000000); - nv_icmd(priv, 0x0000098b, 0x00000000); - nv_icmd(priv, 0x0000098c, 0x00000000); - nv_icmd(priv, 0x0000098d, 0x00000000); - nv_icmd(priv, 0x0000098e, 0x00000000); - nv_icmd(priv, 0x0000098f, 0x00000000); - nv_icmd(priv, 0x00000990, 0x00000000); - nv_icmd(priv, 0x00000991, 0x00000000); - nv_icmd(priv, 0x00000992, 0x00000000); - nv_icmd(priv, 0x00000993, 0x00000000); - nv_icmd(priv, 0x00000994, 0x00000000); - nv_icmd(priv, 0x00000995, 0x00000000); - nv_icmd(priv, 0x00000996, 0x00000000); - nv_icmd(priv, 0x00000997, 0x00000000); - nv_icmd(priv, 0x00000998, 0x00000000); - nv_icmd(priv, 0x00000999, 0x00000000); - nv_icmd(priv, 0x0000099a, 0x00000000); - nv_icmd(priv, 0x0000099b, 0x00000000); - nv_icmd(priv, 0x0000099c, 0x00000000); - nv_icmd(priv, 0x0000099d, 0x00000000); - nv_icmd(priv, 0x0000099e, 0x00000000); - nv_icmd(priv, 0x0000099f, 0x00000000); - nv_icmd(priv, 0x000009a0, 0x00000000); - nv_icmd(priv, 0x000009a1, 0x00000000); - nv_icmd(priv, 0x000009a2, 0x00000000); - nv_icmd(priv, 0x000009a3, 0x00000000); - nv_icmd(priv, 0x000009a4, 0x00000000); - nv_icmd(priv, 0x000009a5, 0x00000000); - nv_icmd(priv, 0x000009a6, 0x00000000); - nv_icmd(priv, 0x000009a7, 0x00000000); - nv_icmd(priv, 0x000009a8, 0x00000000); - nv_icmd(priv, 0x000009a9, 0x00000000); - nv_icmd(priv, 0x000009aa, 0x00000000); - nv_icmd(priv, 0x000009ab, 0x00000000); - nv_icmd(priv, 0x000009ac, 0x00000000); - nv_icmd(priv, 0x000009ad, 0x00000000); - nv_icmd(priv, 0x000009ae, 0x00000000); - nv_icmd(priv, 0x000009af, 0x00000000); - nv_icmd(priv, 0x000009b0, 0x00000000); - nv_icmd(priv, 0x000009b1, 0x00000000); - nv_icmd(priv, 0x000009b2, 0x00000000); - nv_icmd(priv, 0x000009b3, 0x00000000); - nv_icmd(priv, 0x000009b4, 0x00000000); - nv_icmd(priv, 0x000009b5, 0x00000000); - nv_icmd(priv, 0x000009b6, 0x00000000); - nv_icmd(priv, 0x000009b7, 0x00000000); - nv_icmd(priv, 0x000009b8, 0x00000000); - nv_icmd(priv, 0x000009b9, 0x00000000); - nv_icmd(priv, 0x000009ba, 0x00000000); - nv_icmd(priv, 0x000009bb, 0x00000000); - nv_icmd(priv, 0x000009bc, 0x00000000); - nv_icmd(priv, 0x000009bd, 0x00000000); - nv_icmd(priv, 0x000009be, 0x00000000); - nv_icmd(priv, 0x000009bf, 0x00000000); - nv_icmd(priv, 0x000009c0, 0x00000000); - nv_icmd(priv, 0x000009c1, 0x00000000); - nv_icmd(priv, 0x000009c2, 0x00000000); - nv_icmd(priv, 0x000009c3, 0x00000000); - nv_icmd(priv, 0x000009c4, 0x00000000); - nv_icmd(priv, 0x000009c5, 0x00000000); - nv_icmd(priv, 0x000009c6, 0x00000000); - nv_icmd(priv, 0x000009c7, 0x00000000); - nv_icmd(priv, 0x000009c8, 0x00000000); - nv_icmd(priv, 0x000009c9, 0x00000000); - nv_icmd(priv, 0x000009ca, 0x00000000); - nv_icmd(priv, 0x000009cb, 0x00000000); - nv_icmd(priv, 0x000009cc, 0x00000000); - nv_icmd(priv, 0x000009cd, 0x00000000); - nv_icmd(priv, 0x000009ce, 0x00000000); - nv_icmd(priv, 0x000009cf, 0x00000000); - nv_icmd(priv, 0x000009d0, 0x00000000); - nv_icmd(priv, 0x000009d1, 0x00000000); - nv_icmd(priv, 0x000009d2, 0x00000000); - nv_icmd(priv, 0x000009d3, 0x00000000); - nv_icmd(priv, 0x000009d4, 0x00000000); - nv_icmd(priv, 0x000009d5, 0x00000000); - nv_icmd(priv, 0x000009d6, 0x00000000); - nv_icmd(priv, 0x000009d7, 0x00000000); - nv_icmd(priv, 0x000009d8, 0x00000000); - nv_icmd(priv, 0x000009d9, 0x00000000); - nv_icmd(priv, 0x000009da, 0x00000000); - nv_icmd(priv, 0x000009db, 0x00000000); - nv_icmd(priv, 0x000009dc, 0x00000000); - nv_icmd(priv, 0x000009dd, 0x00000000); - nv_icmd(priv, 0x000009de, 0x00000000); - nv_icmd(priv, 0x000009df, 0x00000000); - nv_icmd(priv, 0x000009e0, 0x00000000); - nv_icmd(priv, 0x000009e1, 0x00000000); - nv_icmd(priv, 0x000009e2, 0x00000000); - nv_icmd(priv, 0x000009e3, 0x00000000); - nv_icmd(priv, 0x000009e4, 0x00000000); - nv_icmd(priv, 0x000009e5, 0x00000000); - nv_icmd(priv, 0x000009e6, 0x00000000); - nv_icmd(priv, 0x000009e7, 0x00000000); - nv_icmd(priv, 0x000009e8, 0x00000000); - nv_icmd(priv, 0x000009e9, 0x00000000); - nv_icmd(priv, 0x000009ea, 0x00000000); - nv_icmd(priv, 0x000009eb, 0x00000000); - nv_icmd(priv, 0x000009ec, 0x00000000); - nv_icmd(priv, 0x000009ed, 0x00000000); - nv_icmd(priv, 0x000009ee, 0x00000000); - nv_icmd(priv, 0x000009ef, 0x00000000); - nv_icmd(priv, 0x000009f0, 0x00000000); - nv_icmd(priv, 0x000009f1, 0x00000000); - nv_icmd(priv, 0x000009f2, 0x00000000); - nv_icmd(priv, 0x000009f3, 0x00000000); - nv_icmd(priv, 0x000009f4, 0x00000000); - nv_icmd(priv, 0x000009f5, 0x00000000); - nv_icmd(priv, 0x000009f6, 0x00000000); - nv_icmd(priv, 0x000009f7, 0x00000000); - nv_icmd(priv, 0x000009f8, 0x00000000); - nv_icmd(priv, 0x000009f9, 0x00000000); - nv_icmd(priv, 0x000009fa, 0x00000000); - nv_icmd(priv, 0x000009fb, 0x00000000); - nv_icmd(priv, 0x000009fc, 0x00000000); - nv_icmd(priv, 0x000009fd, 0x00000000); - nv_icmd(priv, 0x000009fe, 0x00000000); - nv_icmd(priv, 0x000009ff, 0x00000000); - nv_icmd(priv, 0x00000468, 0x00000004); - nv_icmd(priv, 0x0000046c, 0x00000001); - nv_icmd(priv, 0x00000470, 0x00000000); - nv_icmd(priv, 0x00000471, 0x00000000); - nv_icmd(priv, 0x00000472, 0x00000000); - nv_icmd(priv, 0x00000473, 0x00000000); - nv_icmd(priv, 0x00000474, 0x00000000); - nv_icmd(priv, 0x00000475, 0x00000000); - nv_icmd(priv, 0x00000476, 0x00000000); - nv_icmd(priv, 0x00000477, 0x00000000); - nv_icmd(priv, 0x00000478, 0x00000000); - nv_icmd(priv, 0x00000479, 0x00000000); - nv_icmd(priv, 0x0000047a, 0x00000000); - nv_icmd(priv, 0x0000047b, 0x00000000); - nv_icmd(priv, 0x0000047c, 0x00000000); - nv_icmd(priv, 0x0000047d, 0x00000000); - nv_icmd(priv, 0x0000047e, 0x00000000); - nv_icmd(priv, 0x0000047f, 0x00000000); - nv_icmd(priv, 0x00000480, 0x00000000); - nv_icmd(priv, 0x00000481, 0x00000000); - nv_icmd(priv, 0x00000482, 0x00000000); - nv_icmd(priv, 0x00000483, 0x00000000); - nv_icmd(priv, 0x00000484, 0x00000000); - nv_icmd(priv, 0x00000485, 0x00000000); - nv_icmd(priv, 0x00000486, 0x00000000); - nv_icmd(priv, 0x00000487, 0x00000000); - nv_icmd(priv, 0x00000488, 0x00000000); - nv_icmd(priv, 0x00000489, 0x00000000); - nv_icmd(priv, 0x0000048a, 0x00000000); - nv_icmd(priv, 0x0000048b, 0x00000000); - nv_icmd(priv, 0x0000048c, 0x00000000); - nv_icmd(priv, 0x0000048d, 0x00000000); - nv_icmd(priv, 0x0000048e, 0x00000000); - nv_icmd(priv, 0x0000048f, 0x00000000); - nv_icmd(priv, 0x00000490, 0x00000000); - nv_icmd(priv, 0x00000491, 0x00000000); - nv_icmd(priv, 0x00000492, 0x00000000); - nv_icmd(priv, 0x00000493, 0x00000000); - nv_icmd(priv, 0x00000494, 0x00000000); - nv_icmd(priv, 0x00000495, 0x00000000); - nv_icmd(priv, 0x00000496, 0x00000000); - nv_icmd(priv, 0x00000497, 0x00000000); - nv_icmd(priv, 0x00000498, 0x00000000); - nv_icmd(priv, 0x00000499, 0x00000000); - nv_icmd(priv, 0x0000049a, 0x00000000); - nv_icmd(priv, 0x0000049b, 0x00000000); - nv_icmd(priv, 0x0000049c, 0x00000000); - nv_icmd(priv, 0x0000049d, 0x00000000); - nv_icmd(priv, 0x0000049e, 0x00000000); - nv_icmd(priv, 0x0000049f, 0x00000000); - nv_icmd(priv, 0x000004a0, 0x00000000); - nv_icmd(priv, 0x000004a1, 0x00000000); - nv_icmd(priv, 0x000004a2, 0x00000000); - nv_icmd(priv, 0x000004a3, 0x00000000); - nv_icmd(priv, 0x000004a4, 0x00000000); - nv_icmd(priv, 0x000004a5, 0x00000000); - nv_icmd(priv, 0x000004a6, 0x00000000); - nv_icmd(priv, 0x000004a7, 0x00000000); - nv_icmd(priv, 0x000004a8, 0x00000000); - nv_icmd(priv, 0x000004a9, 0x00000000); - nv_icmd(priv, 0x000004aa, 0x00000000); - nv_icmd(priv, 0x000004ab, 0x00000000); - nv_icmd(priv, 0x000004ac, 0x00000000); - nv_icmd(priv, 0x000004ad, 0x00000000); - nv_icmd(priv, 0x000004ae, 0x00000000); - nv_icmd(priv, 0x000004af, 0x00000000); - nv_icmd(priv, 0x000004b0, 0x00000000); - nv_icmd(priv, 0x000004b1, 0x00000000); - nv_icmd(priv, 0x000004b2, 0x00000000); - nv_icmd(priv, 0x000004b3, 0x00000000); - nv_icmd(priv, 0x000004b4, 0x00000000); - nv_icmd(priv, 0x000004b5, 0x00000000); - nv_icmd(priv, 0x000004b6, 0x00000000); - nv_icmd(priv, 0x000004b7, 0x00000000); - nv_icmd(priv, 0x000004b8, 0x00000000); - nv_icmd(priv, 0x000004b9, 0x00000000); - nv_icmd(priv, 0x000004ba, 0x00000000); - nv_icmd(priv, 0x000004bb, 0x00000000); - nv_icmd(priv, 0x000004bc, 0x00000000); - nv_icmd(priv, 0x000004bd, 0x00000000); - nv_icmd(priv, 0x000004be, 0x00000000); - nv_icmd(priv, 0x000004bf, 0x00000000); - nv_icmd(priv, 0x000004c0, 0x00000000); - nv_icmd(priv, 0x000004c1, 0x00000000); - nv_icmd(priv, 0x000004c2, 0x00000000); - nv_icmd(priv, 0x000004c3, 0x00000000); - nv_icmd(priv, 0x000004c4, 0x00000000); - nv_icmd(priv, 0x000004c5, 0x00000000); - nv_icmd(priv, 0x000004c6, 0x00000000); - nv_icmd(priv, 0x000004c7, 0x00000000); - nv_icmd(priv, 0x000004c8, 0x00000000); - nv_icmd(priv, 0x000004c9, 0x00000000); - nv_icmd(priv, 0x000004ca, 0x00000000); - nv_icmd(priv, 0x000004cb, 0x00000000); - nv_icmd(priv, 0x000004cc, 0x00000000); - nv_icmd(priv, 0x000004cd, 0x00000000); - nv_icmd(priv, 0x000004ce, 0x00000000); - nv_icmd(priv, 0x000004cf, 0x00000000); - nv_icmd(priv, 0x00000510, 0x3f800000); - nv_icmd(priv, 0x00000511, 0x3f800000); - nv_icmd(priv, 0x00000512, 0x3f800000); - nv_icmd(priv, 0x00000513, 0x3f800000); - nv_icmd(priv, 0x00000514, 0x3f800000); - nv_icmd(priv, 0x00000515, 0x3f800000); - nv_icmd(priv, 0x00000516, 0x3f800000); - nv_icmd(priv, 0x00000517, 0x3f800000); - nv_icmd(priv, 0x00000518, 0x3f800000); - nv_icmd(priv, 0x00000519, 0x3f800000); - nv_icmd(priv, 0x0000051a, 0x3f800000); - nv_icmd(priv, 0x0000051b, 0x3f800000); - nv_icmd(priv, 0x0000051c, 0x3f800000); - nv_icmd(priv, 0x0000051d, 0x3f800000); - nv_icmd(priv, 0x0000051e, 0x3f800000); - nv_icmd(priv, 0x0000051f, 0x3f800000); - nv_icmd(priv, 0x00000520, 0x000002b6); - nv_icmd(priv, 0x00000529, 0x00000001); - nv_icmd(priv, 0x00000530, 0xffff0000); - nv_icmd(priv, 0x00000531, 0xffff0000); - nv_icmd(priv, 0x00000532, 0xffff0000); - nv_icmd(priv, 0x00000533, 0xffff0000); - nv_icmd(priv, 0x00000534, 0xffff0000); - nv_icmd(priv, 0x00000535, 0xffff0000); - nv_icmd(priv, 0x00000536, 0xffff0000); - nv_icmd(priv, 0x00000537, 0xffff0000); - nv_icmd(priv, 0x00000538, 0xffff0000); - nv_icmd(priv, 0x00000539, 0xffff0000); - nv_icmd(priv, 0x0000053a, 0xffff0000); - nv_icmd(priv, 0x0000053b, 0xffff0000); - nv_icmd(priv, 0x0000053c, 0xffff0000); - nv_icmd(priv, 0x0000053d, 0xffff0000); - nv_icmd(priv, 0x0000053e, 0xffff0000); - nv_icmd(priv, 0x0000053f, 0xffff0000); - nv_icmd(priv, 0x00000585, 0x0000003f); - nv_icmd(priv, 0x00000576, 0x00000003); - switch (nv_device(priv)->chipset) { - case 0xc1: - case 0xc8: - case 0xd9: - case 0xd7: - nv_icmd(priv, 0x0000057b, 0x00000059); - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xce: - case 0xcf: - break; - default: - BUG_ON(1); - break; - } - nv_icmd(priv, 0x00000586, 0x00000040); - nv_icmd(priv, 0x00000582, 0x00000080); - nv_icmd(priv, 0x00000583, 0x00000080); - nv_icmd(priv, 0x000005c2, 0x00000001); - nv_icmd(priv, 0x00000638, 0x00000001); - nv_icmd(priv, 0x00000639, 0x00000001); - nv_icmd(priv, 0x0000063a, 0x00000002); - nv_icmd(priv, 0x0000063b, 0x00000001); - nv_icmd(priv, 0x0000063c, 0x00000001); - nv_icmd(priv, 0x0000063d, 0x00000002); - nv_icmd(priv, 0x0000063e, 0x00000001); - nv_icmd(priv, 0x000008b8, 0x00000001); - nv_icmd(priv, 0x000008b9, 0x00000001); - nv_icmd(priv, 0x000008ba, 0x00000001); - nv_icmd(priv, 0x000008bb, 0x00000001); - nv_icmd(priv, 0x000008bc, 0x00000001); - nv_icmd(priv, 0x000008bd, 0x00000001); - nv_icmd(priv, 0x000008be, 0x00000001); - nv_icmd(priv, 0x000008bf, 0x00000001); - nv_icmd(priv, 0x00000900, 0x00000001); - nv_icmd(priv, 0x00000901, 0x00000001); - nv_icmd(priv, 0x00000902, 0x00000001); - nv_icmd(priv, 0x00000903, 0x00000001); - nv_icmd(priv, 0x00000904, 0x00000001); - nv_icmd(priv, 0x00000905, 0x00000001); - nv_icmd(priv, 0x00000906, 0x00000001); - nv_icmd(priv, 0x00000907, 0x00000001); - nv_icmd(priv, 0x00000908, 0x00000002); - nv_icmd(priv, 0x00000909, 0x00000002); - nv_icmd(priv, 0x0000090a, 0x00000002); - nv_icmd(priv, 0x0000090b, 0x00000002); - nv_icmd(priv, 0x0000090c, 0x00000002); - nv_icmd(priv, 0x0000090d, 0x00000002); - nv_icmd(priv, 0x0000090e, 0x00000002); - nv_icmd(priv, 0x0000090f, 0x00000002); - nv_icmd(priv, 0x00000910, 0x00000001); - nv_icmd(priv, 0x00000911, 0x00000001); - nv_icmd(priv, 0x00000912, 0x00000001); - nv_icmd(priv, 0x00000913, 0x00000001); - nv_icmd(priv, 0x00000914, 0x00000001); - nv_icmd(priv, 0x00000915, 0x00000001); - nv_icmd(priv, 0x00000916, 0x00000001); - nv_icmd(priv, 0x00000917, 0x00000001); - nv_icmd(priv, 0x00000918, 0x00000001); - nv_icmd(priv, 0x00000919, 0x00000001); - nv_icmd(priv, 0x0000091a, 0x00000001); - nv_icmd(priv, 0x0000091b, 0x00000001); - nv_icmd(priv, 0x0000091c, 0x00000001); - nv_icmd(priv, 0x0000091d, 0x00000001); - nv_icmd(priv, 0x0000091e, 0x00000001); - nv_icmd(priv, 0x0000091f, 0x00000001); - nv_icmd(priv, 0x00000920, 0x00000002); - nv_icmd(priv, 0x00000921, 0x00000002); - nv_icmd(priv, 0x00000922, 0x00000002); - nv_icmd(priv, 0x00000923, 0x00000002); - nv_icmd(priv, 0x00000924, 0x00000002); - nv_icmd(priv, 0x00000925, 0x00000002); - nv_icmd(priv, 0x00000926, 0x00000002); - nv_icmd(priv, 0x00000927, 0x00000002); - nv_icmd(priv, 0x00000928, 0x00000001); - nv_icmd(priv, 0x00000929, 0x00000001); - nv_icmd(priv, 0x0000092a, 0x00000001); - nv_icmd(priv, 0x0000092b, 0x00000001); - nv_icmd(priv, 0x0000092c, 0x00000001); - nv_icmd(priv, 0x0000092d, 0x00000001); - nv_icmd(priv, 0x0000092e, 0x00000001); - nv_icmd(priv, 0x0000092f, 0x00000001); - nv_icmd(priv, 0x00000648, 0x00000001); - nv_icmd(priv, 0x00000649, 0x00000001); - nv_icmd(priv, 0x0000064a, 0x00000001); - nv_icmd(priv, 0x0000064b, 0x00000001); - nv_icmd(priv, 0x0000064c, 0x00000001); - nv_icmd(priv, 0x0000064d, 0x00000001); - nv_icmd(priv, 0x0000064e, 0x00000001); - nv_icmd(priv, 0x0000064f, 0x00000001); - nv_icmd(priv, 0x00000650, 0x00000001); - nv_icmd(priv, 0x00000658, 0x0000000f); - nv_icmd(priv, 0x000007ff, 0x0000000a); - nv_icmd(priv, 0x0000066a, 0x40000000); - nv_icmd(priv, 0x0000066b, 0x10000000); - nv_icmd(priv, 0x0000066c, 0xffff0000); - nv_icmd(priv, 0x0000066d, 0xffff0000); - nv_icmd(priv, 0x000007af, 0x00000008); - nv_icmd(priv, 0x000007b0, 0x00000008); - nv_icmd(priv, 0x000007f6, 0x00000001); - nv_icmd(priv, 0x000006b2, 0x00000055); - nv_icmd(priv, 0x000007ad, 0x00000003); - nv_icmd(priv, 0x00000937, 0x00000001); - nv_icmd(priv, 0x00000971, 0x00000008); - nv_icmd(priv, 0x00000972, 0x00000040); - nv_icmd(priv, 0x00000973, 0x0000012c); - nv_icmd(priv, 0x0000097c, 0x00000040); - nv_icmd(priv, 0x00000979, 0x00000003); - nv_icmd(priv, 0x00000975, 0x00000020); - nv_icmd(priv, 0x00000976, 0x00000001); - nv_icmd(priv, 0x00000977, 0x00000020); - nv_icmd(priv, 0x00000978, 0x00000001); - nv_icmd(priv, 0x00000957, 0x00000003); - nv_icmd(priv, 0x0000095e, 0x20164010); - nv_icmd(priv, 0x0000095f, 0x00000020); - switch (nv_device(priv)->chipset) { - case 0xd9: - case 0xd7: - case 0xc8: - nv_icmd(priv, 0x0000097d, 0x00000020); - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc1: - case 0xce: - case 0xcf: - break; - default: - BUG_ON(1); - break; - } - nv_icmd(priv, 0x00000683, 0x00000006); - nv_icmd(priv, 0x00000685, 0x003fffff); - nv_icmd(priv, 0x00000687, 0x00000c48); - nv_icmd(priv, 0x000006a0, 0x00000005); - nv_icmd(priv, 0x00000840, 0x00300008); - nv_icmd(priv, 0x00000841, 0x04000080); - nv_icmd(priv, 0x00000842, 0x00300008); - nv_icmd(priv, 0x00000843, 0x04000080); - nv_icmd(priv, 0x00000818, 0x00000000); - nv_icmd(priv, 0x00000819, 0x00000000); - nv_icmd(priv, 0x0000081a, 0x00000000); - nv_icmd(priv, 0x0000081b, 0x00000000); - nv_icmd(priv, 0x0000081c, 0x00000000); - nv_icmd(priv, 0x0000081d, 0x00000000); - nv_icmd(priv, 0x0000081e, 0x00000000); - nv_icmd(priv, 0x0000081f, 0x00000000); - nv_icmd(priv, 0x00000848, 0x00000000); - nv_icmd(priv, 0x00000849, 0x00000000); - nv_icmd(priv, 0x0000084a, 0x00000000); - nv_icmd(priv, 0x0000084b, 0x00000000); - nv_icmd(priv, 0x0000084c, 0x00000000); - nv_icmd(priv, 0x0000084d, 0x00000000); - nv_icmd(priv, 0x0000084e, 0x00000000); - nv_icmd(priv, 0x0000084f, 0x00000000); - nv_icmd(priv, 0x00000850, 0x00000000); - nv_icmd(priv, 0x00000851, 0x00000000); - nv_icmd(priv, 0x00000852, 0x00000000); - nv_icmd(priv, 0x00000853, 0x00000000); - nv_icmd(priv, 0x00000854, 0x00000000); - nv_icmd(priv, 0x00000855, 0x00000000); - nv_icmd(priv, 0x00000856, 0x00000000); - nv_icmd(priv, 0x00000857, 0x00000000); - nv_icmd(priv, 0x00000738, 0x00000000); - nv_icmd(priv, 0x000006aa, 0x00000001); - nv_icmd(priv, 0x000006ab, 0x00000002); - nv_icmd(priv, 0x000006ac, 0x00000080); - nv_icmd(priv, 0x000006ad, 0x00000100); - nv_icmd(priv, 0x000006ae, 0x00000100); - nv_icmd(priv, 0x000006b1, 0x00000011); - nv_icmd(priv, 0x000006bb, 0x000000cf); - nv_icmd(priv, 0x000006ce, 0x2a712488); - nv_icmd(priv, 0x00000739, 0x4085c000); - nv_icmd(priv, 0x0000073a, 0x00000080); - nv_icmd(priv, 0x00000786, 0x80000100); - nv_icmd(priv, 0x0000073c, 0x00010100); - nv_icmd(priv, 0x0000073d, 0x02800000); - nv_icmd(priv, 0x00000787, 0x000000cf); - nv_icmd(priv, 0x0000078c, 0x00000008); - nv_icmd(priv, 0x00000792, 0x00000001); - nv_icmd(priv, 0x00000794, 0x00000001); - nv_icmd(priv, 0x00000795, 0x00000001); - nv_icmd(priv, 0x00000796, 0x00000001); - nv_icmd(priv, 0x00000797, 0x000000cf); - nv_icmd(priv, 0x00000836, 0x00000001); - nv_icmd(priv, 0x0000079a, 0x00000002); - nv_icmd(priv, 0x00000833, 0x04444480); - nv_icmd(priv, 0x000007a1, 0x00000001); - nv_icmd(priv, 0x000007a3, 0x00000001); - nv_icmd(priv, 0x000007a4, 0x00000001); - nv_icmd(priv, 0x000007a5, 0x00000001); - nv_icmd(priv, 0x00000831, 0x00000004); - nv_icmd(priv, 0x0000080c, 0x00000002); - nv_icmd(priv, 0x0000080d, 0x00000100); - nv_icmd(priv, 0x0000080e, 0x00000100); - nv_icmd(priv, 0x0000080f, 0x00000001); - nv_icmd(priv, 0x00000823, 0x00000002); - nv_icmd(priv, 0x00000824, 0x00000100); - nv_icmd(priv, 0x00000825, 0x00000100); - nv_icmd(priv, 0x00000826, 0x00000001); - nv_icmd(priv, 0x0000095d, 0x00000001); - nv_icmd(priv, 0x0000082b, 0x00000004); - nv_icmd(priv, 0x00000942, 0x00010001); - nv_icmd(priv, 0x00000943, 0x00000001); - nv_icmd(priv, 0x00000944, 0x00000022); - nv_icmd(priv, 0x000007c5, 0x00010001); - nv_icmd(priv, 0x00000834, 0x00000001); - nv_icmd(priv, 0x000007c7, 0x00000001); - nv_icmd(priv, 0x0000c1b0, 0x0000000f); - nv_icmd(priv, 0x0000c1b1, 0x0000000f); - nv_icmd(priv, 0x0000c1b2, 0x0000000f); - nv_icmd(priv, 0x0000c1b3, 0x0000000f); - nv_icmd(priv, 0x0000c1b4, 0x0000000f); - nv_icmd(priv, 0x0000c1b5, 0x0000000f); - nv_icmd(priv, 0x0000c1b6, 0x0000000f); - nv_icmd(priv, 0x0000c1b7, 0x0000000f); - nv_icmd(priv, 0x0000c1b8, 0x0fac6881); - nv_icmd(priv, 0x0000c1b9, 0x00fac688); - nv_icmd(priv, 0x0001e100, 0x00000001); - nv_icmd(priv, 0x00001000, 0x00000002); - nv_icmd(priv, 0x000006aa, 0x00000001); - nv_icmd(priv, 0x000006ad, 0x00000100); - nv_icmd(priv, 0x000006ae, 0x00000100); - nv_icmd(priv, 0x000006b1, 0x00000011); - nv_icmd(priv, 0x0000078c, 0x00000008); - nv_icmd(priv, 0x00000792, 0x00000001); - nv_icmd(priv, 0x00000794, 0x00000001); - nv_icmd(priv, 0x00000795, 0x00000001); - nv_icmd(priv, 0x00000796, 0x00000001); - nv_icmd(priv, 0x00000797, 0x000000cf); - nv_icmd(priv, 0x0000079a, 0x00000002); - nv_icmd(priv, 0x00000833, 0x04444480); - nv_icmd(priv, 0x000007a1, 0x00000001); - nv_icmd(priv, 0x000007a3, 0x00000001); - nv_icmd(priv, 0x000007a4, 0x00000001); - nv_icmd(priv, 0x000007a5, 0x00000001); - nv_icmd(priv, 0x00000831, 0x00000004); - nv_icmd(priv, 0x0001e100, 0x00000001); - nv_icmd(priv, 0x00001000, 0x00000014); - nv_icmd(priv, 0x00000351, 0x00000100); - nv_icmd(priv, 0x00000957, 0x00000003); - nv_icmd(priv, 0x0000095d, 0x00000001); - nv_icmd(priv, 0x0000082b, 0x00000004); - nv_icmd(priv, 0x00000942, 0x00010001); - nv_icmd(priv, 0x00000943, 0x00000001); - nv_icmd(priv, 0x000007c5, 0x00010001); - nv_icmd(priv, 0x00000834, 0x00000001); - nv_icmd(priv, 0x000007c7, 0x00000001); - nv_icmd(priv, 0x0001e100, 0x00000001); - nv_icmd(priv, 0x00001000, 0x00000001); - nv_icmd(priv, 0x0000080c, 0x00000002); - nv_icmd(priv, 0x0000080d, 0x00000100); - nv_icmd(priv, 0x0000080e, 0x00000100); - nv_icmd(priv, 0x0000080f, 0x00000001); - nv_icmd(priv, 0x00000823, 0x00000002); - nv_icmd(priv, 0x00000824, 0x00000100); - nv_icmd(priv, 0x00000825, 0x00000100); - nv_icmd(priv, 0x00000826, 0x00000001); - nv_icmd(priv, 0x0001e100, 0x00000001); - - - nv_wr32(priv, 0x400208, 0x00000000); - nv_wr32(priv, 0x404154, 0x00000400); - - nvc0_grctx_generate_9097(priv); - if (fermi >= 0x9197) - nvc0_grctx_generate_9197(priv); - if (fermi >= 0x9297) - nvc0_grctx_generate_9297(priv); - nvc0_grctx_generate_902d(priv); - nvc0_grctx_generate_9039(priv); - nvc0_grctx_generate_90c0(priv); - - switch (nv_device(priv)->chipset) { - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc1: - case 0xc8: - case 0xce: - case 0xcf: - nv_mthd(priv, 0x902d, 0x3410, 0x00000000); - break; - case 0xd9: - case 0xd7: - nv_mthd(priv, 0x902d, 0x3410, 0x80002006); - break; - default: - BUG_ON(1); - break; - } - - nv_wr32(priv, 0x000260, r000260); - - return nvc0_grctx_fini(&info); -} +struct nvc0_graph_init * +nvc0_grctx_init_mmio[] = { + nvc0_grctx_init_base, + nvc0_grctx_init_unk40xx, + nvc0_grctx_init_unk44xx, + nvc0_grctx_init_unk46xx, + nvc0_grctx_init_unk47xx, + nvc0_grctx_init_unk58xx, + nvc0_grctx_init_unk60xx, + nvc0_grctx_init_unk64xx, + nvc0_grctx_init_unk78xx, + nvc0_grctx_init_unk80xx, + nvc0_grctx_init_rop, + NULL +}; + +struct nvc0_graph_init +nvc0_grctx_init_mthd_magic[] = { + { 0x3410, 1, 0x04, 0x00000000 }, + {} +}; + +struct nvc0_graph_mthd +nvc0_grctx_init_mthd[] = { + { 0x9097, nvc0_grctx_init_9097, }, + { 0x902d, nvc0_grctx_init_902d, }, + { 0x9039, nvc0_grctx_init_9039, }, + { 0x90c0, nvc0_grctx_init_90c0, }, + { 0x902d, nvc0_grctx_init_mthd_magic, }, + {} +}; + +struct nouveau_oclass * +nvc0_grctx_oclass = &(struct nvc0_grctx_oclass) { + .base.handle = NV_ENGCTX(GR, 0xc0), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = nvc0_graph_context_ctor, + .dtor = nvc0_graph_context_dtor, + .init = _nouveau_graph_context_init, + .fini = _nouveau_graph_context_fini, + .rd32 = _nouveau_graph_context_rd32, + .wr32 = _nouveau_graph_context_wr32, + }, + .main = nvc0_grctx_generate_main, + .mods = nvc0_grctx_generate_mods, + .mmio = nvc0_grctx_init_mmio, + .gpc = nvc0_grctx_init_gpc, + .tpc = nvc0_grctx_init_tpc, + .icmd = nvc0_grctx_init_icmd, + .mthd = nvc0_grctx_init_mthd, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc1.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc1.c new file mode 100644 index 000000000000..75047154b74d --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc1.c @@ -0,0 +1,804 @@ +/* + * Copyright 2013 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 "nvc0.h" + +static struct nvc0_graph_init +nvc1_grctx_init_icmd[] = { + { 0x001000, 1, 0x01, 0x00000004 }, + { 0x0000a9, 1, 0x01, 0x0000ffff }, + { 0x000038, 1, 0x01, 0x0fac6881 }, + { 0x00003d, 1, 0x01, 0x00000001 }, + { 0x0000e8, 8, 0x01, 0x00000400 }, + { 0x000078, 8, 0x01, 0x00000300 }, + { 0x000050, 1, 0x01, 0x00000011 }, + { 0x000058, 8, 0x01, 0x00000008 }, + { 0x000208, 8, 0x01, 0x00000001 }, + { 0x000081, 1, 0x01, 0x00000001 }, + { 0x000085, 1, 0x01, 0x00000004 }, + { 0x000088, 1, 0x01, 0x00000400 }, + { 0x000090, 1, 0x01, 0x00000300 }, + { 0x000098, 1, 0x01, 0x00001001 }, + { 0x0000e3, 1, 0x01, 0x00000001 }, + { 0x0000da, 1, 0x01, 0x00000001 }, + { 0x0000f8, 1, 0x01, 0x00000003 }, + { 0x0000fa, 1, 0x01, 0x00000001 }, + { 0x00009f, 4, 0x01, 0x0000ffff }, + { 0x0000b1, 1, 0x01, 0x00000001 }, + { 0x0000b2, 40, 0x01, 0x00000000 }, + { 0x000210, 8, 0x01, 0x00000040 }, + { 0x000218, 8, 0x01, 0x0000c080 }, + { 0x0000ad, 1, 0x01, 0x0000013e }, + { 0x0000e1, 1, 0x01, 0x00000010 }, + { 0x000290, 16, 0x01, 0x00000000 }, + { 0x0003b0, 16, 0x01, 0x00000000 }, + { 0x0002a0, 16, 0x01, 0x00000000 }, + { 0x000420, 16, 0x01, 0x00000000 }, + { 0x0002b0, 16, 0x01, 0x00000000 }, + { 0x000430, 16, 0x01, 0x00000000 }, + { 0x0002c0, 16, 0x01, 0x00000000 }, + { 0x0004d0, 16, 0x01, 0x00000000 }, + { 0x000720, 16, 0x01, 0x00000000 }, + { 0x0008c0, 16, 0x01, 0x00000000 }, + { 0x000890, 16, 0x01, 0x00000000 }, + { 0x0008e0, 16, 0x01, 0x00000000 }, + { 0x0008a0, 16, 0x01, 0x00000000 }, + { 0x0008f0, 16, 0x01, 0x00000000 }, + { 0x00094c, 1, 0x01, 0x000000ff }, + { 0x00094d, 1, 0x01, 0xffffffff }, + { 0x00094e, 1, 0x01, 0x00000002 }, + { 0x0002ec, 1, 0x01, 0x00000001 }, + { 0x000303, 1, 0x01, 0x00000001 }, + { 0x0002e6, 1, 0x01, 0x00000001 }, + { 0x000466, 1, 0x01, 0x00000052 }, + { 0x000301, 1, 0x01, 0x3f800000 }, + { 0x000304, 1, 0x01, 0x30201000 }, + { 0x000305, 1, 0x01, 0x70605040 }, + { 0x000306, 1, 0x01, 0xb8a89888 }, + { 0x000307, 1, 0x01, 0xf8e8d8c8 }, + { 0x00030a, 1, 0x01, 0x00ffff00 }, + { 0x00030b, 1, 0x01, 0x0000001a }, + { 0x00030c, 1, 0x01, 0x00000001 }, + { 0x000318, 1, 0x01, 0x00000001 }, + { 0x000340, 1, 0x01, 0x00000000 }, + { 0x000375, 1, 0x01, 0x00000001 }, + { 0x000351, 1, 0x01, 0x00000100 }, + { 0x00037d, 1, 0x01, 0x00000006 }, + { 0x0003a0, 1, 0x01, 0x00000002 }, + { 0x0003aa, 1, 0x01, 0x00000001 }, + { 0x0003a9, 1, 0x01, 0x00000001 }, + { 0x000380, 1, 0x01, 0x00000001 }, + { 0x000360, 1, 0x01, 0x00000040 }, + { 0x000366, 2, 0x01, 0x00000000 }, + { 0x000368, 1, 0x01, 0x00001fff }, + { 0x000370, 2, 0x01, 0x00000000 }, + { 0x000372, 1, 0x01, 0x003fffff }, + { 0x00037a, 1, 0x01, 0x00000012 }, + { 0x0005e0, 5, 0x01, 0x00000022 }, + { 0x000619, 1, 0x01, 0x00000003 }, + { 0x000811, 1, 0x01, 0x00000003 }, + { 0x000812, 1, 0x01, 0x00000004 }, + { 0x000813, 1, 0x01, 0x00000006 }, + { 0x000814, 1, 0x01, 0x00000008 }, + { 0x000815, 1, 0x01, 0x0000000b }, + { 0x000800, 6, 0x01, 0x00000001 }, + { 0x000632, 1, 0x01, 0x00000001 }, + { 0x000633, 1, 0x01, 0x00000002 }, + { 0x000634, 1, 0x01, 0x00000003 }, + { 0x000635, 1, 0x01, 0x00000004 }, + { 0x000654, 1, 0x01, 0x3f800000 }, + { 0x000657, 1, 0x01, 0x3f800000 }, + { 0x000655, 2, 0x01, 0x3f800000 }, + { 0x0006cd, 1, 0x01, 0x3f800000 }, + { 0x0007f5, 1, 0x01, 0x3f800000 }, + { 0x0007dc, 1, 0x01, 0x39291909 }, + { 0x0007dd, 1, 0x01, 0x79695949 }, + { 0x0007de, 1, 0x01, 0xb9a99989 }, + { 0x0007df, 1, 0x01, 0xf9e9d9c9 }, + { 0x0007e8, 1, 0x01, 0x00003210 }, + { 0x0007e9, 1, 0x01, 0x00007654 }, + { 0x0007ea, 1, 0x01, 0x00000098 }, + { 0x0007ec, 1, 0x01, 0x39291909 }, + { 0x0007ed, 1, 0x01, 0x79695949 }, + { 0x0007ee, 1, 0x01, 0xb9a99989 }, + { 0x0007ef, 1, 0x01, 0xf9e9d9c9 }, + { 0x0007f0, 1, 0x01, 0x00003210 }, + { 0x0007f1, 1, 0x01, 0x00007654 }, + { 0x0007f2, 1, 0x01, 0x00000098 }, + { 0x0005a5, 1, 0x01, 0x00000001 }, + { 0x000980, 128, 0x01, 0x00000000 }, + { 0x000468, 1, 0x01, 0x00000004 }, + { 0x00046c, 1, 0x01, 0x00000001 }, + { 0x000470, 96, 0x01, 0x00000000 }, + { 0x000510, 16, 0x01, 0x3f800000 }, + { 0x000520, 1, 0x01, 0x000002b6 }, + { 0x000529, 1, 0x01, 0x00000001 }, + { 0x000530, 16, 0x01, 0xffff0000 }, + { 0x000585, 1, 0x01, 0x0000003f }, + { 0x000576, 1, 0x01, 0x00000003 }, + { 0x00057b, 1, 0x01, 0x00000059 }, + { 0x000586, 1, 0x01, 0x00000040 }, + { 0x000582, 2, 0x01, 0x00000080 }, + { 0x0005c2, 1, 0x01, 0x00000001 }, + { 0x000638, 1, 0x01, 0x00000001 }, + { 0x000639, 1, 0x01, 0x00000001 }, + { 0x00063a, 1, 0x01, 0x00000002 }, + { 0x00063b, 2, 0x01, 0x00000001 }, + { 0x00063d, 1, 0x01, 0x00000002 }, + { 0x00063e, 1, 0x01, 0x00000001 }, + { 0x0008b8, 8, 0x01, 0x00000001 }, + { 0x000900, 8, 0x01, 0x00000001 }, + { 0x000908, 8, 0x01, 0x00000002 }, + { 0x000910, 16, 0x01, 0x00000001 }, + { 0x000920, 8, 0x01, 0x00000002 }, + { 0x000928, 8, 0x01, 0x00000001 }, + { 0x000648, 9, 0x01, 0x00000001 }, + { 0x000658, 1, 0x01, 0x0000000f }, + { 0x0007ff, 1, 0x01, 0x0000000a }, + { 0x00066a, 1, 0x01, 0x40000000 }, + { 0x00066b, 1, 0x01, 0x10000000 }, + { 0x00066c, 2, 0x01, 0xffff0000 }, + { 0x0007af, 2, 0x01, 0x00000008 }, + { 0x0007f6, 1, 0x01, 0x00000001 }, + { 0x0006b2, 1, 0x01, 0x00000055 }, + { 0x0007ad, 1, 0x01, 0x00000003 }, + { 0x000937, 1, 0x01, 0x00000001 }, + { 0x000971, 1, 0x01, 0x00000008 }, + { 0x000972, 1, 0x01, 0x00000040 }, + { 0x000973, 1, 0x01, 0x0000012c }, + { 0x00097c, 1, 0x01, 0x00000040 }, + { 0x000979, 1, 0x01, 0x00000003 }, + { 0x000975, 1, 0x01, 0x00000020 }, + { 0x000976, 1, 0x01, 0x00000001 }, + { 0x000977, 1, 0x01, 0x00000020 }, + { 0x000978, 1, 0x01, 0x00000001 }, + { 0x000957, 1, 0x01, 0x00000003 }, + { 0x00095e, 1, 0x01, 0x20164010 }, + { 0x00095f, 1, 0x01, 0x00000020 }, + { 0x000683, 1, 0x01, 0x00000006 }, + { 0x000685, 1, 0x01, 0x003fffff }, + { 0x000687, 1, 0x01, 0x00000c48 }, + { 0x0006a0, 1, 0x01, 0x00000005 }, + { 0x000840, 1, 0x01, 0x00300008 }, + { 0x000841, 1, 0x01, 0x04000080 }, + { 0x000842, 1, 0x01, 0x00300008 }, + { 0x000843, 1, 0x01, 0x04000080 }, + { 0x000818, 8, 0x01, 0x00000000 }, + { 0x000848, 16, 0x01, 0x00000000 }, + { 0x000738, 1, 0x01, 0x00000000 }, + { 0x0006aa, 1, 0x01, 0x00000001 }, + { 0x0006ab, 1, 0x01, 0x00000002 }, + { 0x0006ac, 1, 0x01, 0x00000080 }, + { 0x0006ad, 2, 0x01, 0x00000100 }, + { 0x0006b1, 1, 0x01, 0x00000011 }, + { 0x0006bb, 1, 0x01, 0x000000cf }, + { 0x0006ce, 1, 0x01, 0x2a712488 }, + { 0x000739, 1, 0x01, 0x4085c000 }, + { 0x00073a, 1, 0x01, 0x00000080 }, + { 0x000786, 1, 0x01, 0x80000100 }, + { 0x00073c, 1, 0x01, 0x00010100 }, + { 0x00073d, 1, 0x01, 0x02800000 }, + { 0x000787, 1, 0x01, 0x000000cf }, + { 0x00078c, 1, 0x01, 0x00000008 }, + { 0x000792, 1, 0x01, 0x00000001 }, + { 0x000794, 1, 0x01, 0x00000001 }, + { 0x000795, 2, 0x01, 0x00000001 }, + { 0x000797, 1, 0x01, 0x000000cf }, + { 0x000836, 1, 0x01, 0x00000001 }, + { 0x00079a, 1, 0x01, 0x00000002 }, + { 0x000833, 1, 0x01, 0x04444480 }, + { 0x0007a1, 1, 0x01, 0x00000001 }, + { 0x0007a3, 1, 0x01, 0x00000001 }, + { 0x0007a4, 2, 0x01, 0x00000001 }, + { 0x000831, 1, 0x01, 0x00000004 }, + { 0x00080c, 1, 0x01, 0x00000002 }, + { 0x00080d, 2, 0x01, 0x00000100 }, + { 0x00080f, 1, 0x01, 0x00000001 }, + { 0x000823, 1, 0x01, 0x00000002 }, + { 0x000824, 2, 0x01, 0x00000100 }, + { 0x000826, 1, 0x01, 0x00000001 }, + { 0x00095d, 1, 0x01, 0x00000001 }, + { 0x00082b, 1, 0x01, 0x00000004 }, + { 0x000942, 1, 0x01, 0x00010001 }, + { 0x000943, 1, 0x01, 0x00000001 }, + { 0x000944, 1, 0x01, 0x00000022 }, + { 0x0007c5, 1, 0x01, 0x00010001 }, + { 0x000834, 1, 0x01, 0x00000001 }, + { 0x0007c7, 1, 0x01, 0x00000001 }, + { 0x00c1b0, 8, 0x01, 0x0000000f }, + { 0x00c1b8, 1, 0x01, 0x0fac6881 }, + { 0x00c1b9, 1, 0x01, 0x00fac688 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + { 0x001000, 1, 0x01, 0x00000002 }, + { 0x0006aa, 1, 0x01, 0x00000001 }, + { 0x0006ad, 2, 0x01, 0x00000100 }, + { 0x0006b1, 1, 0x01, 0x00000011 }, + { 0x00078c, 1, 0x01, 0x00000008 }, + { 0x000792, 1, 0x01, 0x00000001 }, + { 0x000794, 1, 0x01, 0x00000001 }, + { 0x000795, 2, 0x01, 0x00000001 }, + { 0x000797, 1, 0x01, 0x000000cf }, + { 0x00079a, 1, 0x01, 0x00000002 }, + { 0x000833, 1, 0x01, 0x04444480 }, + { 0x0007a1, 1, 0x01, 0x00000001 }, + { 0x0007a3, 1, 0x01, 0x00000001 }, + { 0x0007a4, 2, 0x01, 0x00000001 }, + { 0x000831, 1, 0x01, 0x00000004 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + { 0x001000, 1, 0x01, 0x00000014 }, + { 0x000351, 1, 0x01, 0x00000100 }, + { 0x000957, 1, 0x01, 0x00000003 }, + { 0x00095d, 1, 0x01, 0x00000001 }, + { 0x00082b, 1, 0x01, 0x00000004 }, + { 0x000942, 1, 0x01, 0x00010001 }, + { 0x000943, 1, 0x01, 0x00000001 }, + { 0x0007c5, 1, 0x01, 0x00010001 }, + { 0x000834, 1, 0x01, 0x00000001 }, + { 0x0007c7, 1, 0x01, 0x00000001 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + { 0x001000, 1, 0x01, 0x00000001 }, + { 0x00080c, 1, 0x01, 0x00000002 }, + { 0x00080d, 2, 0x01, 0x00000100 }, + { 0x00080f, 1, 0x01, 0x00000001 }, + { 0x000823, 1, 0x01, 0x00000002 }, + { 0x000824, 2, 0x01, 0x00000100 }, + { 0x000826, 1, 0x01, 0x00000001 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + {} +}; + +struct nvc0_graph_init +nvc1_grctx_init_9097[] = { + { 0x000800, 8, 0x40, 0x00000000 }, + { 0x000804, 8, 0x40, 0x00000000 }, + { 0x000808, 8, 0x40, 0x00000400 }, + { 0x00080c, 8, 0x40, 0x00000300 }, + { 0x000810, 1, 0x04, 0x000000cf }, + { 0x000850, 7, 0x40, 0x00000000 }, + { 0x000814, 8, 0x40, 0x00000040 }, + { 0x000818, 8, 0x40, 0x00000001 }, + { 0x00081c, 8, 0x40, 0x00000000 }, + { 0x000820, 8, 0x40, 0x00000000 }, + { 0x002700, 8, 0x20, 0x00000000 }, + { 0x002704, 8, 0x20, 0x00000000 }, + { 0x002708, 8, 0x20, 0x00000000 }, + { 0x00270c, 8, 0x20, 0x00000000 }, + { 0x002710, 8, 0x20, 0x00014000 }, + { 0x002714, 8, 0x20, 0x00000040 }, + { 0x001c00, 16, 0x10, 0x00000000 }, + { 0x001c04, 16, 0x10, 0x00000000 }, + { 0x001c08, 16, 0x10, 0x00000000 }, + { 0x001c0c, 16, 0x10, 0x00000000 }, + { 0x001d00, 16, 0x10, 0x00000000 }, + { 0x001d04, 16, 0x10, 0x00000000 }, + { 0x001d08, 16, 0x10, 0x00000000 }, + { 0x001d0c, 16, 0x10, 0x00000000 }, + { 0x001f00, 16, 0x08, 0x00000000 }, + { 0x001f04, 16, 0x08, 0x00000000 }, + { 0x001f80, 16, 0x08, 0x00000000 }, + { 0x001f84, 16, 0x08, 0x00000000 }, + { 0x002200, 5, 0x10, 0x00000022 }, + { 0x002000, 1, 0x04, 0x00000000 }, + { 0x002040, 1, 0x04, 0x00000011 }, + { 0x002080, 1, 0x04, 0x00000020 }, + { 0x0020c0, 1, 0x04, 0x00000030 }, + { 0x002100, 1, 0x04, 0x00000040 }, + { 0x002140, 1, 0x04, 0x00000051 }, + { 0x00200c, 6, 0x40, 0x00000001 }, + { 0x002010, 1, 0x04, 0x00000000 }, + { 0x002050, 1, 0x04, 0x00000000 }, + { 0x002090, 1, 0x04, 0x00000001 }, + { 0x0020d0, 1, 0x04, 0x00000002 }, + { 0x002110, 1, 0x04, 0x00000003 }, + { 0x002150, 1, 0x04, 0x00000004 }, + { 0x000380, 4, 0x20, 0x00000000 }, + { 0x000384, 4, 0x20, 0x00000000 }, + { 0x000388, 4, 0x20, 0x00000000 }, + { 0x00038c, 4, 0x20, 0x00000000 }, + { 0x000700, 4, 0x10, 0x00000000 }, + { 0x000704, 4, 0x10, 0x00000000 }, + { 0x000708, 4, 0x10, 0x00000000 }, + { 0x002800, 128, 0x04, 0x00000000 }, + { 0x000a00, 16, 0x20, 0x00000000 }, + { 0x000a04, 16, 0x20, 0x00000000 }, + { 0x000a08, 16, 0x20, 0x00000000 }, + { 0x000a0c, 16, 0x20, 0x00000000 }, + { 0x000a10, 16, 0x20, 0x00000000 }, + { 0x000a14, 16, 0x20, 0x00000000 }, + { 0x000c00, 16, 0x10, 0x00000000 }, + { 0x000c04, 16, 0x10, 0x00000000 }, + { 0x000c08, 16, 0x10, 0x00000000 }, + { 0x000c0c, 16, 0x10, 0x3f800000 }, + { 0x000d00, 8, 0x08, 0xffff0000 }, + { 0x000d04, 8, 0x08, 0xffff0000 }, + { 0x000e00, 16, 0x10, 0x00000000 }, + { 0x000e04, 16, 0x10, 0xffff0000 }, + { 0x000e08, 16, 0x10, 0xffff0000 }, + { 0x000d40, 4, 0x08, 0x00000000 }, + { 0x000d44, 4, 0x08, 0x00000000 }, + { 0x001e00, 8, 0x20, 0x00000001 }, + { 0x001e04, 8, 0x20, 0x00000001 }, + { 0x001e08, 8, 0x20, 0x00000002 }, + { 0x001e0c, 8, 0x20, 0x00000001 }, + { 0x001e10, 8, 0x20, 0x00000001 }, + { 0x001e14, 8, 0x20, 0x00000002 }, + { 0x001e18, 8, 0x20, 0x00000001 }, + { 0x00030c, 1, 0x04, 0x00000001 }, + { 0x001944, 1, 0x04, 0x00000000 }, + { 0x001514, 1, 0x04, 0x00000000 }, + { 0x000d68, 1, 0x04, 0x0000ffff }, + { 0x00121c, 1, 0x04, 0x0fac6881 }, + { 0x000fac, 1, 0x04, 0x00000001 }, + { 0x001538, 1, 0x04, 0x00000001 }, + { 0x000fe0, 2, 0x04, 0x00000000 }, + { 0x000fe8, 1, 0x04, 0x00000014 }, + { 0x000fec, 1, 0x04, 0x00000040 }, + { 0x000ff0, 1, 0x04, 0x00000000 }, + { 0x00179c, 1, 0x04, 0x00000000 }, + { 0x001228, 1, 0x04, 0x00000400 }, + { 0x00122c, 1, 0x04, 0x00000300 }, + { 0x001230, 1, 0x04, 0x00010001 }, + { 0x0007f8, 1, 0x04, 0x00000000 }, + { 0x0015b4, 1, 0x04, 0x00000001 }, + { 0x0015cc, 1, 0x04, 0x00000000 }, + { 0x001534, 1, 0x04, 0x00000000 }, + { 0x000fb0, 1, 0x04, 0x00000000 }, + { 0x0015d0, 1, 0x04, 0x00000000 }, + { 0x00153c, 1, 0x04, 0x00000000 }, + { 0x0016b4, 1, 0x04, 0x00000003 }, + { 0x000fbc, 4, 0x04, 0x0000ffff }, + { 0x000df8, 2, 0x04, 0x00000000 }, + { 0x001948, 1, 0x04, 0x00000000 }, + { 0x001970, 1, 0x04, 0x00000001 }, + { 0x00161c, 1, 0x04, 0x000009f0 }, + { 0x000dcc, 1, 0x04, 0x00000010 }, + { 0x00163c, 1, 0x04, 0x00000000 }, + { 0x0015e4, 1, 0x04, 0x00000000 }, + { 0x001160, 32, 0x04, 0x25e00040 }, + { 0x001880, 32, 0x04, 0x00000000 }, + { 0x000f84, 2, 0x04, 0x00000000 }, + { 0x0017c8, 2, 0x04, 0x00000000 }, + { 0x0017d0, 1, 0x04, 0x000000ff }, + { 0x0017d4, 1, 0x04, 0xffffffff }, + { 0x0017d8, 1, 0x04, 0x00000002 }, + { 0x0017dc, 1, 0x04, 0x00000000 }, + { 0x0015f4, 2, 0x04, 0x00000000 }, + { 0x001434, 2, 0x04, 0x00000000 }, + { 0x000d74, 1, 0x04, 0x00000000 }, + { 0x000dec, 1, 0x04, 0x00000001 }, + { 0x0013a4, 1, 0x04, 0x00000000 }, + { 0x001318, 1, 0x04, 0x00000001 }, + { 0x001644, 1, 0x04, 0x00000000 }, + { 0x000748, 1, 0x04, 0x00000000 }, + { 0x000de8, 1, 0x04, 0x00000000 }, + { 0x001648, 1, 0x04, 0x00000000 }, + { 0x0012a4, 1, 0x04, 0x00000000 }, + { 0x001120, 4, 0x04, 0x00000000 }, + { 0x001118, 1, 0x04, 0x00000000 }, + { 0x00164c, 1, 0x04, 0x00000000 }, + { 0x001658, 1, 0x04, 0x00000000 }, + { 0x001910, 1, 0x04, 0x00000290 }, + { 0x001518, 1, 0x04, 0x00000000 }, + { 0x00165c, 1, 0x04, 0x00000001 }, + { 0x001520, 1, 0x04, 0x00000000 }, + { 0x001604, 1, 0x04, 0x00000000 }, + { 0x001570, 1, 0x04, 0x00000000 }, + { 0x0013b0, 2, 0x04, 0x3f800000 }, + { 0x00020c, 1, 0x04, 0x00000000 }, + { 0x001670, 1, 0x04, 0x30201000 }, + { 0x001674, 1, 0x04, 0x70605040 }, + { 0x001678, 1, 0x04, 0xb8a89888 }, + { 0x00167c, 1, 0x04, 0xf8e8d8c8 }, + { 0x00166c, 1, 0x04, 0x00000000 }, + { 0x001680, 1, 0x04, 0x00ffff00 }, + { 0x0012d0, 1, 0x04, 0x00000003 }, + { 0x0012d4, 1, 0x04, 0x00000002 }, + { 0x001684, 2, 0x04, 0x00000000 }, + { 0x000dac, 2, 0x04, 0x00001b02 }, + { 0x000db4, 1, 0x04, 0x00000000 }, + { 0x00168c, 1, 0x04, 0x00000000 }, + { 0x0015bc, 1, 0x04, 0x00000000 }, + { 0x00156c, 1, 0x04, 0x00000000 }, + { 0x00187c, 1, 0x04, 0x00000000 }, + { 0x001110, 1, 0x04, 0x00000001 }, + { 0x000dc0, 3, 0x04, 0x00000000 }, + { 0x001234, 1, 0x04, 0x00000000 }, + { 0x001690, 1, 0x04, 0x00000000 }, + { 0x0012ac, 1, 0x04, 0x00000001 }, + { 0x0002c4, 1, 0x04, 0x00000000 }, + { 0x000790, 5, 0x04, 0x00000000 }, + { 0x00077c, 1, 0x04, 0x00000000 }, + { 0x001000, 1, 0x04, 0x00000010 }, + { 0x0010fc, 1, 0x04, 0x00000000 }, + { 0x001290, 1, 0x04, 0x00000000 }, + { 0x000218, 1, 0x04, 0x00000010 }, + { 0x0012d8, 1, 0x04, 0x00000000 }, + { 0x0012dc, 1, 0x04, 0x00000010 }, + { 0x000d94, 1, 0x04, 0x00000001 }, + { 0x00155c, 2, 0x04, 0x00000000 }, + { 0x001564, 1, 0x04, 0x00001fff }, + { 0x001574, 2, 0x04, 0x00000000 }, + { 0x00157c, 1, 0x04, 0x003fffff }, + { 0x001354, 1, 0x04, 0x00000000 }, + { 0x001664, 1, 0x04, 0x00000000 }, + { 0x001610, 1, 0x04, 0x00000012 }, + { 0x001608, 2, 0x04, 0x00000000 }, + { 0x00162c, 1, 0x04, 0x00000003 }, + { 0x000210, 1, 0x04, 0x00000000 }, + { 0x000320, 1, 0x04, 0x00000000 }, + { 0x000324, 6, 0x04, 0x3f800000 }, + { 0x000750, 1, 0x04, 0x00000000 }, + { 0x000760, 1, 0x04, 0x39291909 }, + { 0x000764, 1, 0x04, 0x79695949 }, + { 0x000768, 1, 0x04, 0xb9a99989 }, + { 0x00076c, 1, 0x04, 0xf9e9d9c9 }, + { 0x000770, 1, 0x04, 0x30201000 }, + { 0x000774, 1, 0x04, 0x70605040 }, + { 0x000778, 1, 0x04, 0x00009080 }, + { 0x000780, 1, 0x04, 0x39291909 }, + { 0x000784, 1, 0x04, 0x79695949 }, + { 0x000788, 1, 0x04, 0xb9a99989 }, + { 0x00078c, 1, 0x04, 0xf9e9d9c9 }, + { 0x0007d0, 1, 0x04, 0x30201000 }, + { 0x0007d4, 1, 0x04, 0x70605040 }, + { 0x0007d8, 1, 0x04, 0x00009080 }, + { 0x00037c, 1, 0x04, 0x00000001 }, + { 0x000740, 2, 0x04, 0x00000000 }, + { 0x002600, 1, 0x04, 0x00000000 }, + { 0x001918, 1, 0x04, 0x00000000 }, + { 0x00191c, 1, 0x04, 0x00000900 }, + { 0x001920, 1, 0x04, 0x00000405 }, + { 0x001308, 1, 0x04, 0x00000001 }, + { 0x001924, 1, 0x04, 0x00000000 }, + { 0x0013ac, 1, 0x04, 0x00000000 }, + { 0x00192c, 1, 0x04, 0x00000001 }, + { 0x00193c, 1, 0x04, 0x00002c1c }, + { 0x000d7c, 1, 0x04, 0x00000000 }, + { 0x000f8c, 1, 0x04, 0x00000000 }, + { 0x0002c0, 1, 0x04, 0x00000001 }, + { 0x001510, 1, 0x04, 0x00000000 }, + { 0x001940, 1, 0x04, 0x00000000 }, + { 0x000ff4, 2, 0x04, 0x00000000 }, + { 0x00194c, 2, 0x04, 0x00000000 }, + { 0x001968, 1, 0x04, 0x00000000 }, + { 0x001590, 1, 0x04, 0x0000003f }, + { 0x0007e8, 4, 0x04, 0x00000000 }, + { 0x00196c, 1, 0x04, 0x00000011 }, + { 0x00197c, 1, 0x04, 0x00000000 }, + { 0x000fcc, 2, 0x04, 0x00000000 }, + { 0x0002d8, 1, 0x04, 0x00000040 }, + { 0x001980, 1, 0x04, 0x00000080 }, + { 0x001504, 1, 0x04, 0x00000080 }, + { 0x001984, 1, 0x04, 0x00000000 }, + { 0x000300, 1, 0x04, 0x00000001 }, + { 0x0013a8, 1, 0x04, 0x00000000 }, + { 0x0012ec, 1, 0x04, 0x00000000 }, + { 0x001310, 1, 0x04, 0x00000000 }, + { 0x001314, 1, 0x04, 0x00000001 }, + { 0x001380, 1, 0x04, 0x00000000 }, + { 0x001384, 4, 0x04, 0x00000001 }, + { 0x001394, 1, 0x04, 0x00000000 }, + { 0x00139c, 1, 0x04, 0x00000000 }, + { 0x001398, 1, 0x04, 0x00000000 }, + { 0x001594, 1, 0x04, 0x00000000 }, + { 0x001598, 4, 0x04, 0x00000001 }, + { 0x000f54, 3, 0x04, 0x00000000 }, + { 0x0019bc, 1, 0x04, 0x00000000 }, + { 0x000f9c, 2, 0x04, 0x00000000 }, + { 0x0012cc, 1, 0x04, 0x00000000 }, + { 0x0012e8, 1, 0x04, 0x00000000 }, + { 0x00130c, 1, 0x04, 0x00000001 }, + { 0x001360, 8, 0x04, 0x00000000 }, + { 0x00133c, 2, 0x04, 0x00000001 }, + { 0x001344, 1, 0x04, 0x00000002 }, + { 0x001348, 2, 0x04, 0x00000001 }, + { 0x001350, 1, 0x04, 0x00000002 }, + { 0x001358, 1, 0x04, 0x00000001 }, + { 0x0012e4, 1, 0x04, 0x00000000 }, + { 0x00131c, 1, 0x04, 0x00000000 }, + { 0x001320, 3, 0x04, 0x00000000 }, + { 0x0019c0, 1, 0x04, 0x00000000 }, + { 0x001140, 1, 0x04, 0x00000000 }, + { 0x0019c4, 1, 0x04, 0x00000000 }, + { 0x0019c8, 1, 0x04, 0x00001500 }, + { 0x00135c, 1, 0x04, 0x00000000 }, + { 0x000f90, 1, 0x04, 0x00000000 }, + { 0x0019e0, 8, 0x04, 0x00000001 }, + { 0x0019cc, 1, 0x04, 0x00000001 }, + { 0x0015b8, 1, 0x04, 0x00000000 }, + { 0x001a00, 1, 0x04, 0x00001111 }, + { 0x001a04, 7, 0x04, 0x00000000 }, + { 0x000d6c, 2, 0x04, 0xffff0000 }, + { 0x0010f8, 1, 0x04, 0x00001010 }, + { 0x000d80, 5, 0x04, 0x00000000 }, + { 0x000da0, 1, 0x04, 0x00000000 }, + { 0x001508, 1, 0x04, 0x80000000 }, + { 0x00150c, 1, 0x04, 0x40000000 }, + { 0x001668, 1, 0x04, 0x00000000 }, + { 0x000318, 2, 0x04, 0x00000008 }, + { 0x000d9c, 1, 0x04, 0x00000001 }, + { 0x0007dc, 1, 0x04, 0x00000000 }, + { 0x00074c, 1, 0x04, 0x00000055 }, + { 0x001420, 1, 0x04, 0x00000003 }, + { 0x0017bc, 2, 0x04, 0x00000000 }, + { 0x0017c4, 1, 0x04, 0x00000001 }, + { 0x001008, 1, 0x04, 0x00000008 }, + { 0x00100c, 1, 0x04, 0x00000040 }, + { 0x001010, 1, 0x04, 0x0000012c }, + { 0x000d60, 1, 0x04, 0x00000040 }, + { 0x00075c, 1, 0x04, 0x00000003 }, + { 0x001018, 1, 0x04, 0x00000020 }, + { 0x00101c, 1, 0x04, 0x00000001 }, + { 0x001020, 1, 0x04, 0x00000020 }, + { 0x001024, 1, 0x04, 0x00000001 }, + { 0x001444, 3, 0x04, 0x00000000 }, + { 0x000360, 1, 0x04, 0x20164010 }, + { 0x000364, 1, 0x04, 0x00000020 }, + { 0x000368, 1, 0x04, 0x00000000 }, + { 0x000de4, 1, 0x04, 0x00000000 }, + { 0x000204, 1, 0x04, 0x00000006 }, + { 0x000208, 1, 0x04, 0x00000000 }, + { 0x0002cc, 1, 0x04, 0x003fffff }, + { 0x0002d0, 1, 0x04, 0x00000c48 }, + { 0x001220, 1, 0x04, 0x00000005 }, + { 0x000fdc, 1, 0x04, 0x00000000 }, + { 0x000f98, 1, 0x04, 0x00300008 }, + { 0x001284, 1, 0x04, 0x04000080 }, + { 0x001450, 1, 0x04, 0x00300008 }, + { 0x001454, 1, 0x04, 0x04000080 }, + { 0x000214, 1, 0x04, 0x00000000 }, + {} +}; + +static struct nvc0_graph_init +nvc1_grctx_init_9197[] = { + { 0x003400, 128, 0x04, 0x00000000 }, + { 0x0002e4, 1, 0x04, 0x0000b001 }, + {} +}; + +static struct nvc0_graph_init +nvc1_grctx_init_unk58xx[] = { + { 0x405800, 1, 0x04, 0x0f8000bf }, + { 0x405830, 1, 0x04, 0x02180218 }, + { 0x405834, 2, 0x04, 0x00000000 }, + { 0x405854, 1, 0x04, 0x00000000 }, + { 0x405870, 4, 0x04, 0x00000001 }, + { 0x405a00, 2, 0x04, 0x00000000 }, + { 0x405a18, 1, 0x04, 0x00000000 }, +}; + +static struct nvc0_graph_init +nvc1_grctx_init_rop[] = { + { 0x408800, 1, 0x04, 0x02802a3c }, + { 0x408804, 1, 0x04, 0x00000040 }, + { 0x408808, 1, 0x04, 0x1003e005 }, + { 0x408900, 1, 0x04, 0x3080b801 }, + { 0x408904, 1, 0x04, 0x62000001 }, + { 0x408908, 1, 0x04, 0x00c80929 }, + { 0x408980, 1, 0x04, 0x0000011d }, +}; + +static struct nvc0_graph_init +nvc1_grctx_init_gpc[] = { + { 0x418380, 1, 0x04, 0x00000016 }, + { 0x418400, 1, 0x04, 0x38004e00 }, + { 0x418404, 1, 0x04, 0x71e0ffff }, + { 0x418408, 1, 0x04, 0x00000000 }, + { 0x41840c, 1, 0x04, 0x00001008 }, + { 0x418410, 1, 0x04, 0x0fff0fff }, + { 0x418414, 1, 0x04, 0x00200fff }, + { 0x418450, 6, 0x04, 0x00000000 }, + { 0x418468, 1, 0x04, 0x00000001 }, + { 0x41846c, 2, 0x04, 0x00000000 }, + { 0x418600, 1, 0x04, 0x0000001f }, + { 0x418684, 1, 0x04, 0x0000000f }, + { 0x418700, 1, 0x04, 0x00000002 }, + { 0x418704, 1, 0x04, 0x00000080 }, + { 0x418708, 1, 0x04, 0x00000000 }, + { 0x41870c, 1, 0x04, 0x07c80000 }, + { 0x418710, 1, 0x04, 0x00000000 }, + { 0x418800, 1, 0x04, 0x0006860a }, + { 0x418808, 3, 0x04, 0x00000000 }, + { 0x418828, 1, 0x04, 0x00008442 }, + { 0x418830, 1, 0x04, 0x10000001 }, + { 0x4188d8, 1, 0x04, 0x00000008 }, + { 0x4188e0, 1, 0x04, 0x01000000 }, + { 0x4188e8, 5, 0x04, 0x00000000 }, + { 0x4188fc, 1, 0x04, 0x00100018 }, + { 0x41891c, 1, 0x04, 0x00ff00ff }, + { 0x418924, 1, 0x04, 0x00000000 }, + { 0x418928, 1, 0x04, 0x00ffff00 }, + { 0x41892c, 1, 0x04, 0x0000ff00 }, + { 0x418a00, 3, 0x04, 0x00000000 }, + { 0x418a0c, 1, 0x04, 0x00010000 }, + { 0x418a10, 3, 0x04, 0x00000000 }, + { 0x418a20, 3, 0x04, 0x00000000 }, + { 0x418a2c, 1, 0x04, 0x00010000 }, + { 0x418a30, 3, 0x04, 0x00000000 }, + { 0x418a40, 3, 0x04, 0x00000000 }, + { 0x418a4c, 1, 0x04, 0x00010000 }, + { 0x418a50, 3, 0x04, 0x00000000 }, + { 0x418a60, 3, 0x04, 0x00000000 }, + { 0x418a6c, 1, 0x04, 0x00010000 }, + { 0x418a70, 3, 0x04, 0x00000000 }, + { 0x418a80, 3, 0x04, 0x00000000 }, + { 0x418a8c, 1, 0x04, 0x00010000 }, + { 0x418a90, 3, 0x04, 0x00000000 }, + { 0x418aa0, 3, 0x04, 0x00000000 }, + { 0x418aac, 1, 0x04, 0x00010000 }, + { 0x418ab0, 3, 0x04, 0x00000000 }, + { 0x418ac0, 3, 0x04, 0x00000000 }, + { 0x418acc, 1, 0x04, 0x00010000 }, + { 0x418ad0, 3, 0x04, 0x00000000 }, + { 0x418ae0, 3, 0x04, 0x00000000 }, + { 0x418aec, 1, 0x04, 0x00010000 }, + { 0x418af0, 3, 0x04, 0x00000000 }, + { 0x418b00, 1, 0x04, 0x00000000 }, + { 0x418b08, 1, 0x04, 0x0a418820 }, + { 0x418b0c, 1, 0x04, 0x062080e6 }, + { 0x418b10, 1, 0x04, 0x020398a4 }, + { 0x418b14, 1, 0x04, 0x0e629062 }, + { 0x418b18, 1, 0x04, 0x0a418820 }, + { 0x418b1c, 1, 0x04, 0x000000e6 }, + { 0x418bb8, 1, 0x04, 0x00000103 }, + { 0x418c08, 1, 0x04, 0x00000001 }, + { 0x418c10, 8, 0x04, 0x00000000 }, + { 0x418c6c, 1, 0x04, 0x00000001 }, + { 0x418c80, 1, 0x04, 0x20200004 }, + { 0x418c8c, 1, 0x04, 0x00000001 }, + { 0x419000, 1, 0x04, 0x00000780 }, + { 0x419004, 2, 0x04, 0x00000000 }, + { 0x419014, 1, 0x04, 0x00000004 }, +}; + +static struct nvc0_graph_init +nvc1_grctx_init_tpc[] = { + { 0x419818, 1, 0x04, 0x00000000 }, + { 0x41983c, 1, 0x04, 0x00038bc7 }, + { 0x419848, 1, 0x04, 0x00000000 }, + { 0x419864, 1, 0x04, 0x00000129 }, + { 0x419888, 1, 0x04, 0x00000000 }, + { 0x419a00, 1, 0x04, 0x000001f0 }, + { 0x419a04, 1, 0x04, 0x00000001 }, + { 0x419a08, 1, 0x04, 0x00000023 }, + { 0x419a0c, 1, 0x04, 0x00020000 }, + { 0x419a10, 1, 0x04, 0x00000000 }, + { 0x419a14, 1, 0x04, 0x00000200 }, + { 0x419a1c, 1, 0x04, 0x00000000 }, + { 0x419a20, 1, 0x04, 0x00000800 }, + { 0x419ac4, 1, 0x04, 0x0007f440 }, + { 0x419b00, 1, 0x04, 0x0a418820 }, + { 0x419b04, 1, 0x04, 0x062080e6 }, + { 0x419b08, 1, 0x04, 0x020398a4 }, + { 0x419b0c, 1, 0x04, 0x0e629062 }, + { 0x419b10, 1, 0x04, 0x0a418820 }, + { 0x419b14, 1, 0x04, 0x000000e6 }, + { 0x419bd0, 1, 0x04, 0x00900103 }, + { 0x419be0, 1, 0x04, 0x00400001 }, + { 0x419be4, 1, 0x04, 0x00000000 }, + { 0x419c00, 1, 0x04, 0x00000002 }, + { 0x419c04, 1, 0x04, 0x00000006 }, + { 0x419c08, 1, 0x04, 0x00000002 }, + { 0x419c20, 1, 0x04, 0x00000000 }, + { 0x419cb0, 1, 0x04, 0x00020048 }, + { 0x419ce8, 1, 0x04, 0x00000000 }, + { 0x419cf4, 1, 0x04, 0x00000183 }, + { 0x419d20, 1, 0x04, 0x12180000 }, + { 0x419d24, 1, 0x04, 0x00001fff }, + { 0x419d44, 1, 0x04, 0x02180218 }, + { 0x419e04, 3, 0x04, 0x00000000 }, + { 0x419e10, 1, 0x04, 0x00000002 }, + { 0x419e44, 1, 0x04, 0x001beff2 }, + { 0x419e48, 1, 0x04, 0x00000000 }, + { 0x419e4c, 1, 0x04, 0x0000000f }, + { 0x419e50, 17, 0x04, 0x00000000 }, + { 0x419e98, 1, 0x04, 0x00000000 }, + { 0x419ee0, 1, 0x04, 0x00011110 }, + { 0x419f30, 11, 0x04, 0x00000000 }, +}; + +void +nvc1_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info) +{ + int gpc, tpc; + u32 offset; + + mmio_data(0x002000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS); + mmio_data(0x008000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS); + mmio_data(0x060000, 0x1000, NV_MEM_ACCESS_RW); + mmio_list(0x408004, 0x00000000, 8, 0); + mmio_list(0x408008, 0x80000018, 0, 0); + mmio_list(0x40800c, 0x00000000, 8, 1); + mmio_list(0x408010, 0x80000000, 0, 0); + mmio_list(0x418810, 0x80000000, 12, 2); + mmio_list(0x419848, 0x10000000, 12, 2); + mmio_list(0x419004, 0x00000000, 8, 1); + mmio_list(0x419008, 0x00000000, 0, 0); + mmio_list(0x418808, 0x00000000, 8, 0); + mmio_list(0x41880c, 0x80000018, 0, 0); + + mmio_list(0x405830, 0x02180218, 0, 0); + mmio_list(0x4064c4, 0x0086ffff, 0, 0); + + for (gpc = 0, offset = 0; gpc < priv->gpc_nr; gpc++) { + for (tpc = 0; tpc < priv->tpc_nr[gpc]; tpc++) { + u32 addr = TPC_UNIT(gpc, tpc, 0x0520); + mmio_list(addr, 0x12180000 | offset, 0, 0); + offset += 0x0324; + } + for (tpc = 0; tpc < priv->tpc_nr[gpc]; tpc++) { + u32 addr = TPC_UNIT(gpc, tpc, 0x0544); + mmio_list(addr, 0x02180000 | offset, 0, 0); + offset += 0x0324; + } + } +} + +static struct nvc0_graph_init * +nvc1_grctx_init_mmio[] = { + nvc0_grctx_init_base, + nvc0_grctx_init_unk40xx, + nvc0_grctx_init_unk44xx, + nvc0_grctx_init_unk46xx, + nvc0_grctx_init_unk47xx, + nvc1_grctx_init_unk58xx, + nvc0_grctx_init_unk60xx, + nvc0_grctx_init_unk64xx, + nvc0_grctx_init_unk78xx, + nvc0_grctx_init_unk80xx, + nvc1_grctx_init_rop, + NULL +}; + +static struct nvc0_graph_mthd +nvc1_grctx_init_mthd[] = { + { 0x9097, nvc1_grctx_init_9097, }, + { 0x9197, nvc1_grctx_init_9197, }, + { 0x902d, nvc0_grctx_init_902d, }, + { 0x9039, nvc0_grctx_init_9039, }, + { 0x90c0, nvc0_grctx_init_90c0, }, + { 0x902d, nvc0_grctx_init_mthd_magic, }, + {} +}; + +struct nouveau_oclass * +nvc1_grctx_oclass = &(struct nvc0_grctx_oclass) { + .base.handle = NV_ENGCTX(GR, 0xc1), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = nvc0_graph_context_ctor, + .dtor = nvc0_graph_context_dtor, + .init = _nouveau_graph_context_init, + .fini = _nouveau_graph_context_fini, + .rd32 = _nouveau_graph_context_rd32, + .wr32 = _nouveau_graph_context_wr32, + }, + .main = nvc0_grctx_generate_main, + .mods = nvc1_grctx_generate_mods, + .mmio = nvc1_grctx_init_mmio, + .gpc = nvc1_grctx_init_gpc, + .tpc = nvc1_grctx_init_tpc, + .icmd = nvc1_grctx_init_icmd, + .mthd = nvc1_grctx_init_mthd, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc3.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc3.c new file mode 100644 index 000000000000..88eceefcd30d --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc3.c @@ -0,0 +1,91 @@ +/* + * Copyright 2013 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 "nvc0.h" + +static struct nvc0_graph_init +nvc3_grctx_init_tpc[] = { + { 0x419818, 1, 0x04, 0x00000000 }, + { 0x41983c, 1, 0x04, 0x00038bc7 }, + { 0x419848, 1, 0x04, 0x00000000 }, + { 0x419864, 1, 0x04, 0x0000012a }, + { 0x419888, 1, 0x04, 0x00000000 }, + { 0x419a00, 1, 0x04, 0x000001f0 }, + { 0x419a04, 1, 0x04, 0x00000001 }, + { 0x419a08, 1, 0x04, 0x00000023 }, + { 0x419a0c, 1, 0x04, 0x00020000 }, + { 0x419a10, 1, 0x04, 0x00000000 }, + { 0x419a14, 1, 0x04, 0x00000200 }, + { 0x419a1c, 1, 0x04, 0x00000000 }, + { 0x419a20, 1, 0x04, 0x00000800 }, + { 0x419ac4, 1, 0x04, 0x0007f440 }, + { 0x419b00, 1, 0x04, 0x0a418820 }, + { 0x419b04, 1, 0x04, 0x062080e6 }, + { 0x419b08, 1, 0x04, 0x020398a4 }, + { 0x419b0c, 1, 0x04, 0x0e629062 }, + { 0x419b10, 1, 0x04, 0x0a418820 }, + { 0x419b14, 1, 0x04, 0x000000e6 }, + { 0x419bd0, 1, 0x04, 0x00900103 }, + { 0x419be0, 1, 0x04, 0x00000001 }, + { 0x419be4, 1, 0x04, 0x00000000 }, + { 0x419c00, 1, 0x04, 0x00000002 }, + { 0x419c04, 1, 0x04, 0x00000006 }, + { 0x419c08, 1, 0x04, 0x00000002 }, + { 0x419c20, 1, 0x04, 0x00000000 }, + { 0x419cb0, 1, 0x04, 0x00020048 }, + { 0x419ce8, 1, 0x04, 0x00000000 }, + { 0x419cf4, 1, 0x04, 0x00000183 }, + { 0x419d20, 1, 0x04, 0x02180000 }, + { 0x419d24, 1, 0x04, 0x00001fff }, + { 0x419e04, 3, 0x04, 0x00000000 }, + { 0x419e10, 1, 0x04, 0x00000002 }, + { 0x419e44, 1, 0x04, 0x001beff2 }, + { 0x419e48, 1, 0x04, 0x00000000 }, + { 0x419e4c, 1, 0x04, 0x0000000f }, + { 0x419e50, 17, 0x04, 0x00000000 }, + { 0x419e98, 1, 0x04, 0x00000000 }, + { 0x419ee0, 1, 0x04, 0x00011110 }, + { 0x419f30, 11, 0x04, 0x00000000 }, + {} +}; + +struct nouveau_oclass * +nvc3_grctx_oclass = &(struct nvc0_grctx_oclass) { + .base.handle = NV_ENGCTX(GR, 0xc3), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = nvc0_graph_context_ctor, + .dtor = nvc0_graph_context_dtor, + .init = _nouveau_graph_context_init, + .fini = _nouveau_graph_context_fini, + .rd32 = _nouveau_graph_context_rd32, + .wr32 = _nouveau_graph_context_wr32, + }, + .main = nvc0_grctx_generate_main, + .mods = nvc0_grctx_generate_mods, + .mmio = nvc0_grctx_init_mmio, + .gpc = nvc0_grctx_init_gpc, + .tpc = nvc3_grctx_init_tpc, + .icmd = nvc0_grctx_init_icmd, + .mthd = nvc0_grctx_init_mthd, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc8.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc8.c new file mode 100644 index 000000000000..aa766813699b --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc8.c @@ -0,0 +1,362 @@ +/* + * Copyright 2013 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 "nvc0.h" + +static struct nvc0_graph_init +nvc8_grctx_init_icmd[] = { + { 0x001000, 1, 0x01, 0x00000004 }, + { 0x0000a9, 1, 0x01, 0x0000ffff }, + { 0x000038, 1, 0x01, 0x0fac6881 }, + { 0x00003d, 1, 0x01, 0x00000001 }, + { 0x0000e8, 8, 0x01, 0x00000400 }, + { 0x000078, 8, 0x01, 0x00000300 }, + { 0x000050, 1, 0x01, 0x00000011 }, + { 0x000058, 8, 0x01, 0x00000008 }, + { 0x000208, 8, 0x01, 0x00000001 }, + { 0x000081, 1, 0x01, 0x00000001 }, + { 0x000085, 1, 0x01, 0x00000004 }, + { 0x000088, 1, 0x01, 0x00000400 }, + { 0x000090, 1, 0x01, 0x00000300 }, + { 0x000098, 1, 0x01, 0x00001001 }, + { 0x0000e3, 1, 0x01, 0x00000001 }, + { 0x0000da, 1, 0x01, 0x00000001 }, + { 0x0000f8, 1, 0x01, 0x00000003 }, + { 0x0000fa, 1, 0x01, 0x00000001 }, + { 0x00009f, 4, 0x01, 0x0000ffff }, + { 0x0000b1, 1, 0x01, 0x00000001 }, + { 0x0000b2, 40, 0x01, 0x00000000 }, + { 0x000210, 8, 0x01, 0x00000040 }, + { 0x000218, 8, 0x01, 0x0000c080 }, + { 0x0000ad, 1, 0x01, 0x0000013e }, + { 0x0000e1, 1, 0x01, 0x00000010 }, + { 0x000290, 16, 0x01, 0x00000000 }, + { 0x0003b0, 16, 0x01, 0x00000000 }, + { 0x0002a0, 16, 0x01, 0x00000000 }, + { 0x000420, 16, 0x01, 0x00000000 }, + { 0x0002b0, 16, 0x01, 0x00000000 }, + { 0x000430, 16, 0x01, 0x00000000 }, + { 0x0002c0, 16, 0x01, 0x00000000 }, + { 0x0004d0, 16, 0x01, 0x00000000 }, + { 0x000720, 16, 0x01, 0x00000000 }, + { 0x0008c0, 16, 0x01, 0x00000000 }, + { 0x000890, 16, 0x01, 0x00000000 }, + { 0x0008e0, 16, 0x01, 0x00000000 }, + { 0x0008a0, 16, 0x01, 0x00000000 }, + { 0x0008f0, 16, 0x01, 0x00000000 }, + { 0x00094c, 1, 0x01, 0x000000ff }, + { 0x00094d, 1, 0x01, 0xffffffff }, + { 0x00094e, 1, 0x01, 0x00000002 }, + { 0x0002ec, 1, 0x01, 0x00000001 }, + { 0x000303, 1, 0x01, 0x00000001 }, + { 0x0002e6, 1, 0x01, 0x00000001 }, + { 0x000466, 1, 0x01, 0x00000052 }, + { 0x000301, 1, 0x01, 0x3f800000 }, + { 0x000304, 1, 0x01, 0x30201000 }, + { 0x000305, 1, 0x01, 0x70605040 }, + { 0x000306, 1, 0x01, 0xb8a89888 }, + { 0x000307, 1, 0x01, 0xf8e8d8c8 }, + { 0x00030a, 1, 0x01, 0x00ffff00 }, + { 0x00030b, 1, 0x01, 0x0000001a }, + { 0x00030c, 1, 0x01, 0x00000001 }, + { 0x000318, 1, 0x01, 0x00000001 }, + { 0x000340, 1, 0x01, 0x00000000 }, + { 0x000375, 1, 0x01, 0x00000001 }, + { 0x000351, 1, 0x01, 0x00000100 }, + { 0x00037d, 1, 0x01, 0x00000006 }, + { 0x0003a0, 1, 0x01, 0x00000002 }, + { 0x0003aa, 1, 0x01, 0x00000001 }, + { 0x0003a9, 1, 0x01, 0x00000001 }, + { 0x000380, 1, 0x01, 0x00000001 }, + { 0x000360, 1, 0x01, 0x00000040 }, + { 0x000366, 2, 0x01, 0x00000000 }, + { 0x000368, 1, 0x01, 0x00001fff }, + { 0x000370, 2, 0x01, 0x00000000 }, + { 0x000372, 1, 0x01, 0x003fffff }, + { 0x00037a, 1, 0x01, 0x00000012 }, + { 0x0005e0, 5, 0x01, 0x00000022 }, + { 0x000619, 1, 0x01, 0x00000003 }, + { 0x000811, 1, 0x01, 0x00000003 }, + { 0x000812, 1, 0x01, 0x00000004 }, + { 0x000813, 1, 0x01, 0x00000006 }, + { 0x000814, 1, 0x01, 0x00000008 }, + { 0x000815, 1, 0x01, 0x0000000b }, + { 0x000800, 6, 0x01, 0x00000001 }, + { 0x000632, 1, 0x01, 0x00000001 }, + { 0x000633, 1, 0x01, 0x00000002 }, + { 0x000634, 1, 0x01, 0x00000003 }, + { 0x000635, 1, 0x01, 0x00000004 }, + { 0x000654, 1, 0x01, 0x3f800000 }, + { 0x000657, 1, 0x01, 0x3f800000 }, + { 0x000655, 2, 0x01, 0x3f800000 }, + { 0x0006cd, 1, 0x01, 0x3f800000 }, + { 0x0007f5, 1, 0x01, 0x3f800000 }, + { 0x0007dc, 1, 0x01, 0x39291909 }, + { 0x0007dd, 1, 0x01, 0x79695949 }, + { 0x0007de, 1, 0x01, 0xb9a99989 }, + { 0x0007df, 1, 0x01, 0xf9e9d9c9 }, + { 0x0007e8, 1, 0x01, 0x00003210 }, + { 0x0007e9, 1, 0x01, 0x00007654 }, + { 0x0007ea, 1, 0x01, 0x00000098 }, + { 0x0007ec, 1, 0x01, 0x39291909 }, + { 0x0007ed, 1, 0x01, 0x79695949 }, + { 0x0007ee, 1, 0x01, 0xb9a99989 }, + { 0x0007ef, 1, 0x01, 0xf9e9d9c9 }, + { 0x0007f0, 1, 0x01, 0x00003210 }, + { 0x0007f1, 1, 0x01, 0x00007654 }, + { 0x0007f2, 1, 0x01, 0x00000098 }, + { 0x0005a5, 1, 0x01, 0x00000001 }, + { 0x000980, 128, 0x01, 0x00000000 }, + { 0x000468, 1, 0x01, 0x00000004 }, + { 0x00046c, 1, 0x01, 0x00000001 }, + { 0x000470, 96, 0x01, 0x00000000 }, + { 0x000510, 16, 0x01, 0x3f800000 }, + { 0x000520, 1, 0x01, 0x000002b6 }, + { 0x000529, 1, 0x01, 0x00000001 }, + { 0x000530, 16, 0x01, 0xffff0000 }, + { 0x000585, 1, 0x01, 0x0000003f }, + { 0x000576, 1, 0x01, 0x00000003 }, + { 0x00057b, 1, 0x01, 0x00000059 }, + { 0x000586, 1, 0x01, 0x00000040 }, + { 0x000582, 2, 0x01, 0x00000080 }, + { 0x0005c2, 1, 0x01, 0x00000001 }, + { 0x000638, 1, 0x01, 0x00000001 }, + { 0x000639, 1, 0x01, 0x00000001 }, + { 0x00063a, 1, 0x01, 0x00000002 }, + { 0x00063b, 2, 0x01, 0x00000001 }, + { 0x00063d, 1, 0x01, 0x00000002 }, + { 0x00063e, 1, 0x01, 0x00000001 }, + { 0x0008b8, 8, 0x01, 0x00000001 }, + { 0x000900, 8, 0x01, 0x00000001 }, + { 0x000908, 8, 0x01, 0x00000002 }, + { 0x000910, 16, 0x01, 0x00000001 }, + { 0x000920, 8, 0x01, 0x00000002 }, + { 0x000928, 8, 0x01, 0x00000001 }, + { 0x000648, 9, 0x01, 0x00000001 }, + { 0x000658, 1, 0x01, 0x0000000f }, + { 0x0007ff, 1, 0x01, 0x0000000a }, + { 0x00066a, 1, 0x01, 0x40000000 }, + { 0x00066b, 1, 0x01, 0x10000000 }, + { 0x00066c, 2, 0x01, 0xffff0000 }, + { 0x0007af, 2, 0x01, 0x00000008 }, + { 0x0007f6, 1, 0x01, 0x00000001 }, + { 0x0006b2, 1, 0x01, 0x00000055 }, + { 0x0007ad, 1, 0x01, 0x00000003 }, + { 0x000937, 1, 0x01, 0x00000001 }, + { 0x000971, 1, 0x01, 0x00000008 }, + { 0x000972, 1, 0x01, 0x00000040 }, + { 0x000973, 1, 0x01, 0x0000012c }, + { 0x00097c, 1, 0x01, 0x00000040 }, + { 0x000979, 1, 0x01, 0x00000003 }, + { 0x000975, 1, 0x01, 0x00000020 }, + { 0x000976, 1, 0x01, 0x00000001 }, + { 0x000977, 1, 0x01, 0x00000020 }, + { 0x000978, 1, 0x01, 0x00000001 }, + { 0x000957, 1, 0x01, 0x00000003 }, + { 0x00095e, 1, 0x01, 0x20164010 }, + { 0x00095f, 1, 0x01, 0x00000020 }, + { 0x00097d, 1, 0x01, 0x00000020 }, + { 0x000683, 1, 0x01, 0x00000006 }, + { 0x000685, 1, 0x01, 0x003fffff }, + { 0x000687, 1, 0x01, 0x00000c48 }, + { 0x0006a0, 1, 0x01, 0x00000005 }, + { 0x000840, 1, 0x01, 0x00300008 }, + { 0x000841, 1, 0x01, 0x04000080 }, + { 0x000842, 1, 0x01, 0x00300008 }, + { 0x000843, 1, 0x01, 0x04000080 }, + { 0x000818, 8, 0x01, 0x00000000 }, + { 0x000848, 16, 0x01, 0x00000000 }, + { 0x000738, 1, 0x01, 0x00000000 }, + { 0x0006aa, 1, 0x01, 0x00000001 }, + { 0x0006ab, 1, 0x01, 0x00000002 }, + { 0x0006ac, 1, 0x01, 0x00000080 }, + { 0x0006ad, 2, 0x01, 0x00000100 }, + { 0x0006b1, 1, 0x01, 0x00000011 }, + { 0x0006bb, 1, 0x01, 0x000000cf }, + { 0x0006ce, 1, 0x01, 0x2a712488 }, + { 0x000739, 1, 0x01, 0x4085c000 }, + { 0x00073a, 1, 0x01, 0x00000080 }, + { 0x000786, 1, 0x01, 0x80000100 }, + { 0x00073c, 1, 0x01, 0x00010100 }, + { 0x00073d, 1, 0x01, 0x02800000 }, + { 0x000787, 1, 0x01, 0x000000cf }, + { 0x00078c, 1, 0x01, 0x00000008 }, + { 0x000792, 1, 0x01, 0x00000001 }, + { 0x000794, 1, 0x01, 0x00000001 }, + { 0x000795, 2, 0x01, 0x00000001 }, + { 0x000797, 1, 0x01, 0x000000cf }, + { 0x000836, 1, 0x01, 0x00000001 }, + { 0x00079a, 1, 0x01, 0x00000002 }, + { 0x000833, 1, 0x01, 0x04444480 }, + { 0x0007a1, 1, 0x01, 0x00000001 }, + { 0x0007a3, 1, 0x01, 0x00000001 }, + { 0x0007a4, 2, 0x01, 0x00000001 }, + { 0x000831, 1, 0x01, 0x00000004 }, + { 0x00080c, 1, 0x01, 0x00000002 }, + { 0x00080d, 2, 0x01, 0x00000100 }, + { 0x00080f, 1, 0x01, 0x00000001 }, + { 0x000823, 1, 0x01, 0x00000002 }, + { 0x000824, 2, 0x01, 0x00000100 }, + { 0x000826, 1, 0x01, 0x00000001 }, + { 0x00095d, 1, 0x01, 0x00000001 }, + { 0x00082b, 1, 0x01, 0x00000004 }, + { 0x000942, 1, 0x01, 0x00010001 }, + { 0x000943, 1, 0x01, 0x00000001 }, + { 0x000944, 1, 0x01, 0x00000022 }, + { 0x0007c5, 1, 0x01, 0x00010001 }, + { 0x000834, 1, 0x01, 0x00000001 }, + { 0x0007c7, 1, 0x01, 0x00000001 }, + { 0x00c1b0, 8, 0x01, 0x0000000f }, + { 0x00c1b8, 1, 0x01, 0x0fac6881 }, + { 0x00c1b9, 1, 0x01, 0x00fac688 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + { 0x001000, 1, 0x01, 0x00000002 }, + { 0x0006aa, 1, 0x01, 0x00000001 }, + { 0x0006ad, 2, 0x01, 0x00000100 }, + { 0x0006b1, 1, 0x01, 0x00000011 }, + { 0x00078c, 1, 0x01, 0x00000008 }, + { 0x000792, 1, 0x01, 0x00000001 }, + { 0x000794, 1, 0x01, 0x00000001 }, + { 0x000795, 2, 0x01, 0x00000001 }, + { 0x000797, 1, 0x01, 0x000000cf }, + { 0x00079a, 1, 0x01, 0x00000002 }, + { 0x000833, 1, 0x01, 0x04444480 }, + { 0x0007a1, 1, 0x01, 0x00000001 }, + { 0x0007a3, 1, 0x01, 0x00000001 }, + { 0x0007a4, 2, 0x01, 0x00000001 }, + { 0x000831, 1, 0x01, 0x00000004 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + { 0x001000, 1, 0x01, 0x00000014 }, + { 0x000351, 1, 0x01, 0x00000100 }, + { 0x000957, 1, 0x01, 0x00000003 }, + { 0x00095d, 1, 0x01, 0x00000001 }, + { 0x00082b, 1, 0x01, 0x00000004 }, + { 0x000942, 1, 0x01, 0x00010001 }, + { 0x000943, 1, 0x01, 0x00000001 }, + { 0x0007c5, 1, 0x01, 0x00010001 }, + { 0x000834, 1, 0x01, 0x00000001 }, + { 0x0007c7, 1, 0x01, 0x00000001 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + { 0x001000, 1, 0x01, 0x00000001 }, + { 0x00080c, 1, 0x01, 0x00000002 }, + { 0x00080d, 2, 0x01, 0x00000100 }, + { 0x00080f, 1, 0x01, 0x00000001 }, + { 0x000823, 1, 0x01, 0x00000002 }, + { 0x000824, 2, 0x01, 0x00000100 }, + { 0x000826, 1, 0x01, 0x00000001 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + {} +}; + +static struct nvc0_graph_init +nvc8_grctx_init_tpc[] = { + { 0x419818, 1, 0x04, 0x00000000 }, + { 0x41983c, 1, 0x04, 0x00038bc7 }, + { 0x419848, 1, 0x04, 0x00000000 }, + { 0x419864, 1, 0x04, 0x0000012a }, + { 0x419888, 1, 0x04, 0x00000000 }, + { 0x419a00, 1, 0x04, 0x000001f0 }, + { 0x419a04, 1, 0x04, 0x00000001 }, + { 0x419a08, 1, 0x04, 0x00000023 }, + { 0x419a0c, 1, 0x04, 0x00020000 }, + { 0x419a10, 1, 0x04, 0x00000000 }, + { 0x419a14, 1, 0x04, 0x00000200 }, + { 0x419a1c, 1, 0x04, 0x00000000 }, + { 0x419a20, 1, 0x04, 0x00000800 }, + { 0x419b00, 1, 0x04, 0x0a418820 }, + { 0x419b04, 1, 0x04, 0x062080e6 }, + { 0x419b08, 1, 0x04, 0x020398a4 }, + { 0x419b0c, 1, 0x04, 0x0e629062 }, + { 0x419b10, 1, 0x04, 0x0a418820 }, + { 0x419b14, 1, 0x04, 0x000000e6 }, + { 0x419bd0, 1, 0x04, 0x00900103 }, + { 0x419be0, 1, 0x04, 0x00000001 }, + { 0x419be4, 1, 0x04, 0x00000000 }, + { 0x419c00, 1, 0x04, 0x00000002 }, + { 0x419c04, 1, 0x04, 0x00000006 }, + { 0x419c08, 1, 0x04, 0x00000002 }, + { 0x419c20, 1, 0x04, 0x00000000 }, + { 0x419cb0, 1, 0x04, 0x00060048 }, + { 0x419ce8, 1, 0x04, 0x00000000 }, + { 0x419cf4, 1, 0x04, 0x00000183 }, + { 0x419d20, 1, 0x04, 0x02180000 }, + { 0x419d24, 1, 0x04, 0x00001fff }, + { 0x419e04, 3, 0x04, 0x00000000 }, + { 0x419e10, 1, 0x04, 0x00000002 }, + { 0x419e44, 1, 0x04, 0x001beff2 }, + { 0x419e48, 1, 0x04, 0x00000000 }, + { 0x419e4c, 1, 0x04, 0x0000000f }, + { 0x419e50, 17, 0x04, 0x00000000 }, + { 0x419e98, 1, 0x04, 0x00000000 }, + { 0x419f50, 2, 0x04, 0x00000000 }, + {} +}; + +struct nvc0_graph_init +nvc8_grctx_init_9197[] = { + { 0x0002e4, 1, 0x04, 0x0000b001 }, + {} +}; + +struct nvc0_graph_init +nvc8_grctx_init_9297[] = { + { 0x003400, 128, 0x04, 0x00000000 }, + { 0x00036c, 2, 0x04, 0x00000000 }, + { 0x0007a4, 2, 0x04, 0x00000000 }, + { 0x000374, 1, 0x04, 0x00000000 }, + { 0x000378, 1, 0x04, 0x00000020 }, + {} +}; + +static struct nvc0_graph_mthd +nvc8_grctx_init_mthd[] = { + { 0x9097, nvc1_grctx_init_9097, }, + { 0x9197, nvc8_grctx_init_9197, }, + { 0x9297, nvc8_grctx_init_9297, }, + { 0x902d, nvc0_grctx_init_902d, }, + { 0x9039, nvc0_grctx_init_9039, }, + { 0x90c0, nvc0_grctx_init_90c0, }, + { 0x902d, nvc0_grctx_init_mthd_magic, }, + {} +}; + +struct nouveau_oclass * +nvc8_grctx_oclass = &(struct nvc0_grctx_oclass) { + .base.handle = NV_ENGCTX(GR, 0xc8), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = nvc0_graph_context_ctor, + .dtor = nvc0_graph_context_dtor, + .init = _nouveau_graph_context_init, + .fini = _nouveau_graph_context_fini, + .rd32 = _nouveau_graph_context_rd32, + .wr32 = _nouveau_graph_context_wr32, + }, + .main = nvc0_grctx_generate_main, + .mods = nvc0_grctx_generate_mods, + .mmio = nvc0_grctx_init_mmio, + .gpc = nvc0_grctx_init_gpc, + .tpc = nvc8_grctx_init_tpc, + .icmd = nvc8_grctx_init_icmd, + .mthd = nvc8_grctx_init_mthd, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd9.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd9.c new file mode 100644 index 000000000000..4be543e411cd --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd9.c @@ -0,0 +1,531 @@ +/* + * Copyright 2013 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 "nvc0.h" + +struct nvc0_graph_init +nvd9_grctx_init_90c0[] = { + { 0x002700, 4, 0x40, 0x00000000 }, + { 0x002720, 4, 0x40, 0x00000000 }, + { 0x002704, 4, 0x40, 0x00000000 }, + { 0x002724, 4, 0x40, 0x00000000 }, + { 0x002708, 4, 0x40, 0x00000000 }, + { 0x002728, 4, 0x40, 0x00000000 }, + { 0x00270c, 8, 0x20, 0x00000000 }, + { 0x002710, 4, 0x40, 0x00014000 }, + { 0x002730, 4, 0x40, 0x00014000 }, + { 0x002714, 4, 0x40, 0x00000040 }, + { 0x002734, 4, 0x40, 0x00000040 }, + { 0x00030c, 1, 0x04, 0x00000001 }, + { 0x001944, 1, 0x04, 0x00000000 }, + { 0x000758, 1, 0x04, 0x00000100 }, + { 0x0002c4, 1, 0x04, 0x00000000 }, + { 0x000790, 5, 0x04, 0x00000000 }, + { 0x00077c, 1, 0x04, 0x00000000 }, + { 0x000204, 3, 0x04, 0x00000000 }, + { 0x000214, 1, 0x04, 0x00000000 }, + { 0x00024c, 1, 0x04, 0x00000000 }, + { 0x000d94, 1, 0x04, 0x00000001 }, + { 0x001608, 2, 0x04, 0x00000000 }, + { 0x001664, 1, 0x04, 0x00000000 }, + {} +}; + +struct nvc0_graph_init +nvd9_grctx_init_icmd[] = { + { 0x001000, 1, 0x01, 0x00000004 }, + { 0x0000a9, 1, 0x01, 0x0000ffff }, + { 0x000038, 1, 0x01, 0x0fac6881 }, + { 0x00003d, 1, 0x01, 0x00000001 }, + { 0x0000e8, 8, 0x01, 0x00000400 }, + { 0x000078, 8, 0x01, 0x00000300 }, + { 0x000050, 1, 0x01, 0x00000011 }, + { 0x000058, 8, 0x01, 0x00000008 }, + { 0x000208, 8, 0x01, 0x00000001 }, + { 0x000081, 1, 0x01, 0x00000001 }, + { 0x000085, 1, 0x01, 0x00000004 }, + { 0x000088, 1, 0x01, 0x00000400 }, + { 0x000090, 1, 0x01, 0x00000300 }, + { 0x000098, 1, 0x01, 0x00001001 }, + { 0x0000e3, 1, 0x01, 0x00000001 }, + { 0x0000da, 1, 0x01, 0x00000001 }, + { 0x0000f8, 1, 0x01, 0x00000003 }, + { 0x0000fa, 1, 0x01, 0x00000001 }, + { 0x00009f, 4, 0x01, 0x0000ffff }, + { 0x0000b1, 1, 0x01, 0x00000001 }, + { 0x0000b2, 40, 0x01, 0x00000000 }, + { 0x000210, 8, 0x01, 0x00000040 }, + { 0x000400, 24, 0x01, 0x00000040 }, + { 0x000218, 8, 0x01, 0x0000c080 }, + { 0x000440, 24, 0x01, 0x0000c080 }, + { 0x0000ad, 1, 0x01, 0x0000013e }, + { 0x0000e1, 1, 0x01, 0x00000010 }, + { 0x000290, 16, 0x01, 0x00000000 }, + { 0x0003b0, 16, 0x01, 0x00000000 }, + { 0x0002a0, 16, 0x01, 0x00000000 }, + { 0x000420, 16, 0x01, 0x00000000 }, + { 0x0002b0, 16, 0x01, 0x00000000 }, + { 0x000430, 16, 0x01, 0x00000000 }, + { 0x0002c0, 16, 0x01, 0x00000000 }, + { 0x0004d0, 16, 0x01, 0x00000000 }, + { 0x000720, 16, 0x01, 0x00000000 }, + { 0x0008c0, 16, 0x01, 0x00000000 }, + { 0x000890, 16, 0x01, 0x00000000 }, + { 0x0008e0, 16, 0x01, 0x00000000 }, + { 0x0008a0, 16, 0x01, 0x00000000 }, + { 0x0008f0, 16, 0x01, 0x00000000 }, + { 0x00094c, 1, 0x01, 0x000000ff }, + { 0x00094d, 1, 0x01, 0xffffffff }, + { 0x00094e, 1, 0x01, 0x00000002 }, + { 0x0002ec, 1, 0x01, 0x00000001 }, + { 0x000303, 1, 0x01, 0x00000001 }, + { 0x0002e6, 1, 0x01, 0x00000001 }, + { 0x000466, 1, 0x01, 0x00000052 }, + { 0x000301, 1, 0x01, 0x3f800000 }, + { 0x000304, 1, 0x01, 0x30201000 }, + { 0x000305, 1, 0x01, 0x70605040 }, + { 0x000306, 1, 0x01, 0xb8a89888 }, + { 0x000307, 1, 0x01, 0xf8e8d8c8 }, + { 0x00030a, 1, 0x01, 0x00ffff00 }, + { 0x00030b, 1, 0x01, 0x0000001a }, + { 0x00030c, 1, 0x01, 0x00000001 }, + { 0x000318, 1, 0x01, 0x00000001 }, + { 0x000340, 1, 0x01, 0x00000000 }, + { 0x000375, 1, 0x01, 0x00000001 }, + { 0x000351, 1, 0x01, 0x00000100 }, + { 0x00037d, 1, 0x01, 0x00000006 }, + { 0x0003a0, 1, 0x01, 0x00000002 }, + { 0x0003aa, 1, 0x01, 0x00000001 }, + { 0x0003a9, 1, 0x01, 0x00000001 }, + { 0x000380, 1, 0x01, 0x00000001 }, + { 0x000360, 1, 0x01, 0x00000040 }, + { 0x000366, 2, 0x01, 0x00000000 }, + { 0x000368, 1, 0x01, 0x00001fff }, + { 0x000370, 2, 0x01, 0x00000000 }, + { 0x000372, 1, 0x01, 0x003fffff }, + { 0x00037a, 1, 0x01, 0x00000012 }, + { 0x0005e0, 5, 0x01, 0x00000022 }, + { 0x000619, 1, 0x01, 0x00000003 }, + { 0x000811, 1, 0x01, 0x00000003 }, + { 0x000812, 1, 0x01, 0x00000004 }, + { 0x000813, 1, 0x01, 0x00000006 }, + { 0x000814, 1, 0x01, 0x00000008 }, + { 0x000815, 1, 0x01, 0x0000000b }, + { 0x000800, 6, 0x01, 0x00000001 }, + { 0x000632, 1, 0x01, 0x00000001 }, + { 0x000633, 1, 0x01, 0x00000002 }, + { 0x000634, 1, 0x01, 0x00000003 }, + { 0x000635, 1, 0x01, 0x00000004 }, + { 0x000654, 1, 0x01, 0x3f800000 }, + { 0x000657, 1, 0x01, 0x3f800000 }, + { 0x000655, 2, 0x01, 0x3f800000 }, + { 0x0006cd, 1, 0x01, 0x3f800000 }, + { 0x0007f5, 1, 0x01, 0x3f800000 }, + { 0x0007dc, 1, 0x01, 0x39291909 }, + { 0x0007dd, 1, 0x01, 0x79695949 }, + { 0x0007de, 1, 0x01, 0xb9a99989 }, + { 0x0007df, 1, 0x01, 0xf9e9d9c9 }, + { 0x0007e8, 1, 0x01, 0x00003210 }, + { 0x0007e9, 1, 0x01, 0x00007654 }, + { 0x0007ea, 1, 0x01, 0x00000098 }, + { 0x0007ec, 1, 0x01, 0x39291909 }, + { 0x0007ed, 1, 0x01, 0x79695949 }, + { 0x0007ee, 1, 0x01, 0xb9a99989 }, + { 0x0007ef, 1, 0x01, 0xf9e9d9c9 }, + { 0x0007f0, 1, 0x01, 0x00003210 }, + { 0x0007f1, 1, 0x01, 0x00007654 }, + { 0x0007f2, 1, 0x01, 0x00000098 }, + { 0x0005a5, 1, 0x01, 0x00000001 }, + { 0x000980, 128, 0x01, 0x00000000 }, + { 0x000468, 1, 0x01, 0x00000004 }, + { 0x00046c, 1, 0x01, 0x00000001 }, + { 0x000470, 96, 0x01, 0x00000000 }, + { 0x000510, 16, 0x01, 0x3f800000 }, + { 0x000520, 1, 0x01, 0x000002b6 }, + { 0x000529, 1, 0x01, 0x00000001 }, + { 0x000530, 16, 0x01, 0xffff0000 }, + { 0x000585, 1, 0x01, 0x0000003f }, + { 0x000576, 1, 0x01, 0x00000003 }, + { 0x00057b, 1, 0x01, 0x00000059 }, + { 0x000586, 1, 0x01, 0x00000040 }, + { 0x000582, 2, 0x01, 0x00000080 }, + { 0x0005c2, 1, 0x01, 0x00000001 }, + { 0x000638, 1, 0x01, 0x00000001 }, + { 0x000639, 1, 0x01, 0x00000001 }, + { 0x00063a, 1, 0x01, 0x00000002 }, + { 0x00063b, 2, 0x01, 0x00000001 }, + { 0x00063d, 1, 0x01, 0x00000002 }, + { 0x00063e, 1, 0x01, 0x00000001 }, + { 0x0008b8, 8, 0x01, 0x00000001 }, + { 0x000900, 8, 0x01, 0x00000001 }, + { 0x000908, 8, 0x01, 0x00000002 }, + { 0x000910, 16, 0x01, 0x00000001 }, + { 0x000920, 8, 0x01, 0x00000002 }, + { 0x000928, 8, 0x01, 0x00000001 }, + { 0x000648, 9, 0x01, 0x00000001 }, + { 0x000658, 1, 0x01, 0x0000000f }, + { 0x0007ff, 1, 0x01, 0x0000000a }, + { 0x00066a, 1, 0x01, 0x40000000 }, + { 0x00066b, 1, 0x01, 0x10000000 }, + { 0x00066c, 2, 0x01, 0xffff0000 }, + { 0x0007af, 2, 0x01, 0x00000008 }, + { 0x0007f6, 1, 0x01, 0x00000001 }, + { 0x0006b2, 1, 0x01, 0x00000055 }, + { 0x0007ad, 1, 0x01, 0x00000003 }, + { 0x000937, 1, 0x01, 0x00000001 }, + { 0x000971, 1, 0x01, 0x00000008 }, + { 0x000972, 1, 0x01, 0x00000040 }, + { 0x000973, 1, 0x01, 0x0000012c }, + { 0x00097c, 1, 0x01, 0x00000040 }, + { 0x000979, 1, 0x01, 0x00000003 }, + { 0x000975, 1, 0x01, 0x00000020 }, + { 0x000976, 1, 0x01, 0x00000001 }, + { 0x000977, 1, 0x01, 0x00000020 }, + { 0x000978, 1, 0x01, 0x00000001 }, + { 0x000957, 1, 0x01, 0x00000003 }, + { 0x00095e, 1, 0x01, 0x20164010 }, + { 0x00095f, 1, 0x01, 0x00000020 }, + { 0x00097d, 1, 0x01, 0x00000020 }, + { 0x000683, 1, 0x01, 0x00000006 }, + { 0x000685, 1, 0x01, 0x003fffff }, + { 0x000687, 1, 0x01, 0x00000c48 }, + { 0x0006a0, 1, 0x01, 0x00000005 }, + { 0x000840, 1, 0x01, 0x00300008 }, + { 0x000841, 1, 0x01, 0x04000080 }, + { 0x000842, 1, 0x01, 0x00300008 }, + { 0x000843, 1, 0x01, 0x04000080 }, + { 0x000818, 8, 0x01, 0x00000000 }, + { 0x000848, 16, 0x01, 0x00000000 }, + { 0x000738, 1, 0x01, 0x00000000 }, + { 0x0006aa, 1, 0x01, 0x00000001 }, + { 0x0006ab, 1, 0x01, 0x00000002 }, + { 0x0006ac, 1, 0x01, 0x00000080 }, + { 0x0006ad, 2, 0x01, 0x00000100 }, + { 0x0006b1, 1, 0x01, 0x00000011 }, + { 0x0006bb, 1, 0x01, 0x000000cf }, + { 0x0006ce, 1, 0x01, 0x2a712488 }, + { 0x000739, 1, 0x01, 0x4085c000 }, + { 0x00073a, 1, 0x01, 0x00000080 }, + { 0x000786, 1, 0x01, 0x80000100 }, + { 0x00073c, 1, 0x01, 0x00010100 }, + { 0x00073d, 1, 0x01, 0x02800000 }, + { 0x000787, 1, 0x01, 0x000000cf }, + { 0x00078c, 1, 0x01, 0x00000008 }, + { 0x000792, 1, 0x01, 0x00000001 }, + { 0x000794, 1, 0x01, 0x00000001 }, + { 0x000795, 2, 0x01, 0x00000001 }, + { 0x000797, 1, 0x01, 0x000000cf }, + { 0x000836, 1, 0x01, 0x00000001 }, + { 0x00079a, 1, 0x01, 0x00000002 }, + { 0x000833, 1, 0x01, 0x04444480 }, + { 0x0007a1, 1, 0x01, 0x00000001 }, + { 0x0007a3, 1, 0x01, 0x00000001 }, + { 0x0007a4, 2, 0x01, 0x00000001 }, + { 0x000831, 1, 0x01, 0x00000004 }, + { 0x00080c, 1, 0x01, 0x00000002 }, + { 0x00080d, 2, 0x01, 0x00000100 }, + { 0x00080f, 1, 0x01, 0x00000001 }, + { 0x000823, 1, 0x01, 0x00000002 }, + { 0x000824, 2, 0x01, 0x00000100 }, + { 0x000826, 1, 0x01, 0x00000001 }, + { 0x00095d, 1, 0x01, 0x00000001 }, + { 0x00082b, 1, 0x01, 0x00000004 }, + { 0x000942, 1, 0x01, 0x00010001 }, + { 0x000943, 1, 0x01, 0x00000001 }, + { 0x000944, 1, 0x01, 0x00000022 }, + { 0x0007c5, 1, 0x01, 0x00010001 }, + { 0x000834, 1, 0x01, 0x00000001 }, + { 0x0007c7, 1, 0x01, 0x00000001 }, + { 0x00c1b0, 8, 0x01, 0x0000000f }, + { 0x00c1b8, 1, 0x01, 0x0fac6881 }, + { 0x00c1b9, 1, 0x01, 0x00fac688 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + { 0x001000, 1, 0x01, 0x00000002 }, + { 0x0006aa, 1, 0x01, 0x00000001 }, + { 0x0006ad, 2, 0x01, 0x00000100 }, + { 0x0006b1, 1, 0x01, 0x00000011 }, + { 0x00078c, 1, 0x01, 0x00000008 }, + { 0x000792, 1, 0x01, 0x00000001 }, + { 0x000794, 1, 0x01, 0x00000001 }, + { 0x000795, 2, 0x01, 0x00000001 }, + { 0x000797, 1, 0x01, 0x000000cf }, + { 0x00079a, 1, 0x01, 0x00000002 }, + { 0x000833, 1, 0x01, 0x04444480 }, + { 0x0007a1, 1, 0x01, 0x00000001 }, + { 0x0007a3, 1, 0x01, 0x00000001 }, + { 0x0007a4, 2, 0x01, 0x00000001 }, + { 0x000831, 1, 0x01, 0x00000004 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + { 0x001000, 1, 0x01, 0x00000014 }, + { 0x000351, 1, 0x01, 0x00000100 }, + { 0x000957, 1, 0x01, 0x00000003 }, + { 0x00095d, 1, 0x01, 0x00000001 }, + { 0x00082b, 1, 0x01, 0x00000004 }, + { 0x000942, 1, 0x01, 0x00010001 }, + { 0x000943, 1, 0x01, 0x00000001 }, + { 0x0007c5, 1, 0x01, 0x00010001 }, + { 0x000834, 1, 0x01, 0x00000001 }, + { 0x0007c7, 1, 0x01, 0x00000001 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + { 0x001000, 1, 0x01, 0x00000001 }, + { 0x00080c, 1, 0x01, 0x00000002 }, + { 0x00080d, 2, 0x01, 0x00000100 }, + { 0x00080f, 1, 0x01, 0x00000001 }, + { 0x000823, 1, 0x01, 0x00000002 }, + { 0x000824, 2, 0x01, 0x00000100 }, + { 0x000826, 1, 0x01, 0x00000001 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + {} +}; + +struct nvc0_graph_init +nvd9_grctx_init_unk40xx[] = { + { 0x404004, 11, 0x04, 0x00000000 }, + { 0x404044, 1, 0x04, 0x00000000 }, + { 0x404094, 1, 0x04, 0x00000000 }, + { 0x404098, 12, 0x04, 0x00000000 }, + { 0x4040c8, 1, 0x04, 0xf0000087 }, + { 0x4040d0, 6, 0x04, 0x00000000 }, + { 0x4040e8, 1, 0x04, 0x00001000 }, + { 0x4040f8, 1, 0x04, 0x00000000 }, + { 0x404130, 1, 0x04, 0x00000000 }, + { 0x404134, 1, 0x04, 0x00000000 }, + { 0x404138, 1, 0x04, 0x20000040 }, + { 0x404150, 1, 0x04, 0x0000002e }, + { 0x404154, 1, 0x04, 0x00000400 }, + { 0x404158, 1, 0x04, 0x00000200 }, + { 0x404164, 1, 0x04, 0x00000055 }, + { 0x404168, 1, 0x04, 0x00000000 }, + { 0x404178, 2, 0x04, 0x00000000 }, + { 0x404200, 8, 0x04, 0x00000000 }, + {} +}; + +static struct nvc0_graph_init +nvd9_grctx_init_unk58xx[] = { + { 0x405800, 1, 0x04, 0x0f8000bf }, + { 0x405830, 1, 0x04, 0x02180218 }, + { 0x405834, 1, 0x04, 0x08000000 }, + { 0x405838, 1, 0x04, 0x00000000 }, + { 0x405854, 1, 0x04, 0x00000000 }, + { 0x405870, 4, 0x04, 0x00000001 }, + { 0x405a00, 2, 0x04, 0x00000000 }, + { 0x405a18, 1, 0x04, 0x00000000 }, + {} +}; + +static struct nvc0_graph_init +nvd9_grctx_init_unk64xx[] = { + { 0x4064a8, 1, 0x04, 0x00000000 }, + { 0x4064ac, 1, 0x04, 0x00003fff }, + { 0x4064b4, 3, 0x04, 0x00000000 }, + { 0x4064c0, 1, 0x04, 0x80140078 }, + { 0x4064c4, 1, 0x04, 0x0086ffff }, + {} +}; + +struct nvc0_graph_init +nvd9_grctx_init_rop[] = { + { 0x408800, 1, 0x04, 0x02802a3c }, + { 0x408804, 1, 0x04, 0x00000040 }, + { 0x408808, 1, 0x04, 0x1043e005 }, + { 0x408900, 1, 0x04, 0x3080b801 }, + { 0x408904, 1, 0x04, 0x1043e005 }, + { 0x408908, 1, 0x04, 0x00c8102f }, + { 0x408980, 1, 0x04, 0x0000011d }, + {} +}; + +static struct nvc0_graph_init +nvd9_grctx_init_gpc[] = { + { 0x418380, 1, 0x04, 0x00000016 }, + { 0x418400, 1, 0x04, 0x38004e00 }, + { 0x418404, 1, 0x04, 0x71e0ffff }, + { 0x41840c, 1, 0x04, 0x00001008 }, + { 0x418410, 1, 0x04, 0x0fff0fff }, + { 0x418414, 1, 0x04, 0x02200fff }, + { 0x418450, 6, 0x04, 0x00000000 }, + { 0x418468, 1, 0x04, 0x00000001 }, + { 0x41846c, 2, 0x04, 0x00000000 }, + { 0x418600, 1, 0x04, 0x0000001f }, + { 0x418684, 1, 0x04, 0x0000000f }, + { 0x418700, 1, 0x04, 0x00000002 }, + { 0x418704, 1, 0x04, 0x00000080 }, + { 0x418708, 3, 0x04, 0x00000000 }, + { 0x418800, 1, 0x04, 0x7006860a }, + { 0x418808, 3, 0x04, 0x00000000 }, + { 0x418828, 1, 0x04, 0x00008442 }, + { 0x418830, 1, 0x04, 0x10000001 }, + { 0x4188d8, 1, 0x04, 0x00000008 }, + { 0x4188e0, 1, 0x04, 0x01000000 }, + { 0x4188e8, 5, 0x04, 0x00000000 }, + { 0x4188fc, 1, 0x04, 0x20100008 }, + { 0x41891c, 1, 0x04, 0x00ff00ff }, + { 0x418924, 1, 0x04, 0x00000000 }, + { 0x418928, 1, 0x04, 0x00ffff00 }, + { 0x41892c, 1, 0x04, 0x0000ff00 }, + { 0x418a00, 3, 0x04, 0x00000000 }, + { 0x418a0c, 1, 0x04, 0x00010000 }, + { 0x418a10, 3, 0x04, 0x00000000 }, + { 0x418a20, 3, 0x04, 0x00000000 }, + { 0x418a2c, 1, 0x04, 0x00010000 }, + { 0x418a30, 3, 0x04, 0x00000000 }, + { 0x418a40, 3, 0x04, 0x00000000 }, + { 0x418a4c, 1, 0x04, 0x00010000 }, + { 0x418a50, 3, 0x04, 0x00000000 }, + { 0x418a60, 3, 0x04, 0x00000000 }, + { 0x418a6c, 1, 0x04, 0x00010000 }, + { 0x418a70, 3, 0x04, 0x00000000 }, + { 0x418a80, 3, 0x04, 0x00000000 }, + { 0x418a8c, 1, 0x04, 0x00010000 }, + { 0x418a90, 3, 0x04, 0x00000000 }, + { 0x418aa0, 3, 0x04, 0x00000000 }, + { 0x418aac, 1, 0x04, 0x00010000 }, + { 0x418ab0, 3, 0x04, 0x00000000 }, + { 0x418ac0, 3, 0x04, 0x00000000 }, + { 0x418acc, 1, 0x04, 0x00010000 }, + { 0x418ad0, 3, 0x04, 0x00000000 }, + { 0x418ae0, 3, 0x04, 0x00000000 }, + { 0x418aec, 1, 0x04, 0x00010000 }, + { 0x418af0, 3, 0x04, 0x00000000 }, + { 0x418b00, 1, 0x04, 0x00000006 }, + { 0x418b08, 1, 0x04, 0x0a418820 }, + { 0x418b0c, 1, 0x04, 0x062080e6 }, + { 0x418b10, 1, 0x04, 0x020398a4 }, + { 0x418b14, 1, 0x04, 0x0e629062 }, + { 0x418b18, 1, 0x04, 0x0a418820 }, + { 0x418b1c, 1, 0x04, 0x000000e6 }, + { 0x418bb8, 1, 0x04, 0x00000103 }, + { 0x418c08, 1, 0x04, 0x00000001 }, + { 0x418c10, 8, 0x04, 0x00000000 }, + { 0x418c6c, 1, 0x04, 0x00000001 }, + { 0x418c80, 1, 0x04, 0x20200004 }, + { 0x418c8c, 1, 0x04, 0x00000001 }, + { 0x419000, 1, 0x04, 0x00000780 }, + { 0x419004, 2, 0x04, 0x00000000 }, + { 0x419014, 1, 0x04, 0x00000004 }, + {} +}; + +static struct nvc0_graph_init +nvd9_grctx_init_tpc[] = { + { 0x419818, 1, 0x04, 0x00000000 }, + { 0x41983c, 1, 0x04, 0x00038bc7 }, + { 0x419848, 1, 0x04, 0x00000000 }, + { 0x419864, 1, 0x04, 0x00000129 }, + { 0x419888, 1, 0x04, 0x00000000 }, + { 0x419a00, 1, 0x04, 0x000001f0 }, + { 0x419a04, 1, 0x04, 0x00000001 }, + { 0x419a08, 1, 0x04, 0x00000023 }, + { 0x419a0c, 1, 0x04, 0x00020000 }, + { 0x419a10, 1, 0x04, 0x00000000 }, + { 0x419a14, 1, 0x04, 0x00000200 }, + { 0x419a1c, 1, 0x04, 0x00000000 }, + { 0x419a20, 1, 0x04, 0x00000800 }, + { 0x419ac4, 1, 0x04, 0x0017f440 }, + { 0x419b00, 1, 0x04, 0x0a418820 }, + { 0x419b04, 1, 0x04, 0x062080e6 }, + { 0x419b08, 1, 0x04, 0x020398a4 }, + { 0x419b0c, 1, 0x04, 0x0e629062 }, + { 0x419b10, 1, 0x04, 0x0a418820 }, + { 0x419b14, 1, 0x04, 0x000000e6 }, + { 0x419bd0, 1, 0x04, 0x00900103 }, + { 0x419be0, 1, 0x04, 0x00400001 }, + { 0x419be4, 1, 0x04, 0x00000000 }, + { 0x419c00, 1, 0x04, 0x0000000a }, + { 0x419c04, 1, 0x04, 0x00000006 }, + { 0x419c08, 1, 0x04, 0x00000002 }, + { 0x419c20, 1, 0x04, 0x00000000 }, + { 0x419c24, 1, 0x04, 0x00084210 }, + { 0x419c28, 1, 0x04, 0x3cf3cf3c }, + { 0x419cb0, 1, 0x04, 0x00020048 }, + { 0x419ce8, 1, 0x04, 0x00000000 }, + { 0x419cf4, 1, 0x04, 0x00000183 }, + { 0x419d20, 1, 0x04, 0x12180000 }, + { 0x419d24, 1, 0x04, 0x00001fff }, + { 0x419d44, 1, 0x04, 0x02180218 }, + { 0x419e04, 3, 0x04, 0x00000000 }, + { 0x419e10, 1, 0x04, 0x00000002 }, + { 0x419e44, 1, 0x04, 0x001beff2 }, + { 0x419e48, 1, 0x04, 0x00000000 }, + { 0x419e4c, 1, 0x04, 0x0000000f }, + { 0x419e50, 17, 0x04, 0x00000000 }, + { 0x419e98, 1, 0x04, 0x00000000 }, + { 0x419ee0, 1, 0x04, 0x00010110 }, + { 0x419f30, 11, 0x04, 0x00000000 }, + {} +}; + +static struct nvc0_graph_init * +nvd9_grctx_init_mmio[] = { + nvc0_grctx_init_base, + nvd9_grctx_init_unk40xx, + nvc0_grctx_init_unk44xx, + nvc0_grctx_init_unk46xx, + nvc0_grctx_init_unk47xx, + nvd9_grctx_init_unk58xx, + nvc0_grctx_init_unk60xx, + nvd9_grctx_init_unk64xx, + nvc0_grctx_init_unk78xx, + nvc0_grctx_init_unk80xx, + nvd9_grctx_init_rop, +}; + +struct nvc0_graph_init +nvd9_grctx_init_mthd_magic[] = { + { 0x3410, 1, 0x04, 0x80002006 }, + {} +}; + +struct nvc0_graph_mthd +nvd9_grctx_init_mthd[] = { + { 0x9097, nvc1_grctx_init_9097, }, + { 0x9197, nvc8_grctx_init_9197, }, + { 0x9297, nvc8_grctx_init_9297, }, + { 0x902d, nvc0_grctx_init_902d, }, + { 0x9039, nvc0_grctx_init_9039, }, + { 0x90c0, nvd9_grctx_init_90c0, }, + { 0x902d, nvd9_grctx_init_mthd_magic, }, + {} +}; + +struct nouveau_oclass * +nvd9_grctx_oclass = &(struct nvc0_grctx_oclass) { + .base.handle = NV_ENGCTX(GR, 0xd9), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = nvc0_graph_context_ctor, + .dtor = nvc0_graph_context_dtor, + .init = _nouveau_graph_context_init, + .fini = _nouveau_graph_context_fini, + .rd32 = _nouveau_graph_context_rd32, + .wr32 = _nouveau_graph_context_wr32, + }, + .main = nvc0_grctx_generate_main, + .mods = nvc1_grctx_generate_mods, + .mmio = nvd9_grctx_init_mmio, + .gpc = nvd9_grctx_init_gpc, + .tpc = nvd9_grctx_init_tpc, + .icmd = nvd9_grctx_init_icmd, + .mthd = nvd9_grctx_init_mthd, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve0.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve0.c deleted file mode 100644 index 848570b4c519..000000000000 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve0.c +++ /dev/null @@ -1,2924 +0,0 @@ -/* - * 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 "nvc0.h" - -static void -nve0_grctx_generate_icmd(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x400208, 0x80000000); - nv_icmd(priv, 0x001000, 0x00000004); - nv_icmd(priv, 0x000039, 0x00000000); - nv_icmd(priv, 0x00003a, 0x00000000); - nv_icmd(priv, 0x00003b, 0x00000000); - nv_icmd(priv, 0x0000a9, 0x0000ffff); - nv_icmd(priv, 0x000038, 0x0fac6881); - nv_icmd(priv, 0x00003d, 0x00000001); - nv_icmd(priv, 0x0000e8, 0x00000400); - nv_icmd(priv, 0x0000e9, 0x00000400); - nv_icmd(priv, 0x0000ea, 0x00000400); - nv_icmd(priv, 0x0000eb, 0x00000400); - nv_icmd(priv, 0x0000ec, 0x00000400); - nv_icmd(priv, 0x0000ed, 0x00000400); - nv_icmd(priv, 0x0000ee, 0x00000400); - nv_icmd(priv, 0x0000ef, 0x00000400); - nv_icmd(priv, 0x000078, 0x00000300); - nv_icmd(priv, 0x000079, 0x00000300); - nv_icmd(priv, 0x00007a, 0x00000300); - nv_icmd(priv, 0x00007b, 0x00000300); - nv_icmd(priv, 0x00007c, 0x00000300); - nv_icmd(priv, 0x00007d, 0x00000300); - nv_icmd(priv, 0x00007e, 0x00000300); - nv_icmd(priv, 0x00007f, 0x00000300); - nv_icmd(priv, 0x000050, 0x00000011); - nv_icmd(priv, 0x000058, 0x00000008); - nv_icmd(priv, 0x000059, 0x00000008); - nv_icmd(priv, 0x00005a, 0x00000008); - nv_icmd(priv, 0x00005b, 0x00000008); - nv_icmd(priv, 0x00005c, 0x00000008); - nv_icmd(priv, 0x00005d, 0x00000008); - nv_icmd(priv, 0x00005e, 0x00000008); - nv_icmd(priv, 0x00005f, 0x00000008); - nv_icmd(priv, 0x000208, 0x00000001); - nv_icmd(priv, 0x000209, 0x00000001); - nv_icmd(priv, 0x00020a, 0x00000001); - nv_icmd(priv, 0x00020b, 0x00000001); - nv_icmd(priv, 0x00020c, 0x00000001); - nv_icmd(priv, 0x00020d, 0x00000001); - nv_icmd(priv, 0x00020e, 0x00000001); - nv_icmd(priv, 0x00020f, 0x00000001); - nv_icmd(priv, 0x000081, 0x00000001); - nv_icmd(priv, 0x000085, 0x00000004); - nv_icmd(priv, 0x000088, 0x00000400); - nv_icmd(priv, 0x000090, 0x00000300); - nv_icmd(priv, 0x000098, 0x00001001); - nv_icmd(priv, 0x0000e3, 0x00000001); - nv_icmd(priv, 0x0000da, 0x00000001); - nv_icmd(priv, 0x0000f8, 0x00000003); - nv_icmd(priv, 0x0000fa, 0x00000001); - nv_icmd(priv, 0x00009f, 0x0000ffff); - nv_icmd(priv, 0x0000a0, 0x0000ffff); - nv_icmd(priv, 0x0000a1, 0x0000ffff); - nv_icmd(priv, 0x0000a2, 0x0000ffff); - nv_icmd(priv, 0x0000b1, 0x00000001); - nv_icmd(priv, 0x0000ad, 0x0000013e); - nv_icmd(priv, 0x0000e1, 0x00000010); - nv_icmd(priv, 0x000290, 0x00000000); - nv_icmd(priv, 0x000291, 0x00000000); - nv_icmd(priv, 0x000292, 0x00000000); - nv_icmd(priv, 0x000293, 0x00000000); - nv_icmd(priv, 0x000294, 0x00000000); - nv_icmd(priv, 0x000295, 0x00000000); - nv_icmd(priv, 0x000296, 0x00000000); - nv_icmd(priv, 0x000297, 0x00000000); - nv_icmd(priv, 0x000298, 0x00000000); - nv_icmd(priv, 0x000299, 0x00000000); - nv_icmd(priv, 0x00029a, 0x00000000); - nv_icmd(priv, 0x00029b, 0x00000000); - nv_icmd(priv, 0x00029c, 0x00000000); - nv_icmd(priv, 0x00029d, 0x00000000); - nv_icmd(priv, 0x00029e, 0x00000000); - nv_icmd(priv, 0x00029f, 0x00000000); - nv_icmd(priv, 0x0003b0, 0x00000000); - nv_icmd(priv, 0x0003b1, 0x00000000); - nv_icmd(priv, 0x0003b2, 0x00000000); - nv_icmd(priv, 0x0003b3, 0x00000000); - nv_icmd(priv, 0x0003b4, 0x00000000); - nv_icmd(priv, 0x0003b5, 0x00000000); - nv_icmd(priv, 0x0003b6, 0x00000000); - nv_icmd(priv, 0x0003b7, 0x00000000); - nv_icmd(priv, 0x0003b8, 0x00000000); - nv_icmd(priv, 0x0003b9, 0x00000000); - nv_icmd(priv, 0x0003ba, 0x00000000); - nv_icmd(priv, 0x0003bb, 0x00000000); - nv_icmd(priv, 0x0003bc, 0x00000000); - nv_icmd(priv, 0x0003bd, 0x00000000); - nv_icmd(priv, 0x0003be, 0x00000000); - nv_icmd(priv, 0x0003bf, 0x00000000); - nv_icmd(priv, 0x0002a0, 0x00000000); - nv_icmd(priv, 0x0002a1, 0x00000000); - nv_icmd(priv, 0x0002a2, 0x00000000); - nv_icmd(priv, 0x0002a3, 0x00000000); - nv_icmd(priv, 0x0002a4, 0x00000000); - nv_icmd(priv, 0x0002a5, 0x00000000); - nv_icmd(priv, 0x0002a6, 0x00000000); - nv_icmd(priv, 0x0002a7, 0x00000000); - nv_icmd(priv, 0x0002a8, 0x00000000); - nv_icmd(priv, 0x0002a9, 0x00000000); - nv_icmd(priv, 0x0002aa, 0x00000000); - nv_icmd(priv, 0x0002ab, 0x00000000); - nv_icmd(priv, 0x0002ac, 0x00000000); - nv_icmd(priv, 0x0002ad, 0x00000000); - nv_icmd(priv, 0x0002ae, 0x00000000); - nv_icmd(priv, 0x0002af, 0x00000000); - nv_icmd(priv, 0x000420, 0x00000000); - nv_icmd(priv, 0x000421, 0x00000000); - nv_icmd(priv, 0x000422, 0x00000000); - nv_icmd(priv, 0x000423, 0x00000000); - nv_icmd(priv, 0x000424, 0x00000000); - nv_icmd(priv, 0x000425, 0x00000000); - nv_icmd(priv, 0x000426, 0x00000000); - nv_icmd(priv, 0x000427, 0x00000000); - nv_icmd(priv, 0x000428, 0x00000000); - nv_icmd(priv, 0x000429, 0x00000000); - nv_icmd(priv, 0x00042a, 0x00000000); - nv_icmd(priv, 0x00042b, 0x00000000); - nv_icmd(priv, 0x00042c, 0x00000000); - nv_icmd(priv, 0x00042d, 0x00000000); - nv_icmd(priv, 0x00042e, 0x00000000); - nv_icmd(priv, 0x00042f, 0x00000000); - nv_icmd(priv, 0x0002b0, 0x00000000); - nv_icmd(priv, 0x0002b1, 0x00000000); - nv_icmd(priv, 0x0002b2, 0x00000000); - nv_icmd(priv, 0x0002b3, 0x00000000); - nv_icmd(priv, 0x0002b4, 0x00000000); - nv_icmd(priv, 0x0002b5, 0x00000000); - nv_icmd(priv, 0x0002b6, 0x00000000); - nv_icmd(priv, 0x0002b7, 0x00000000); - nv_icmd(priv, 0x0002b8, 0x00000000); - nv_icmd(priv, 0x0002b9, 0x00000000); - nv_icmd(priv, 0x0002ba, 0x00000000); - nv_icmd(priv, 0x0002bb, 0x00000000); - nv_icmd(priv, 0x0002bc, 0x00000000); - nv_icmd(priv, 0x0002bd, 0x00000000); - nv_icmd(priv, 0x0002be, 0x00000000); - nv_icmd(priv, 0x0002bf, 0x00000000); - nv_icmd(priv, 0x000430, 0x00000000); - nv_icmd(priv, 0x000431, 0x00000000); - nv_icmd(priv, 0x000432, 0x00000000); - nv_icmd(priv, 0x000433, 0x00000000); - nv_icmd(priv, 0x000434, 0x00000000); - nv_icmd(priv, 0x000435, 0x00000000); - nv_icmd(priv, 0x000436, 0x00000000); - nv_icmd(priv, 0x000437, 0x00000000); - nv_icmd(priv, 0x000438, 0x00000000); - nv_icmd(priv, 0x000439, 0x00000000); - nv_icmd(priv, 0x00043a, 0x00000000); - nv_icmd(priv, 0x00043b, 0x00000000); - nv_icmd(priv, 0x00043c, 0x00000000); - nv_icmd(priv, 0x00043d, 0x00000000); - nv_icmd(priv, 0x00043e, 0x00000000); - nv_icmd(priv, 0x00043f, 0x00000000); - nv_icmd(priv, 0x0002c0, 0x00000000); - nv_icmd(priv, 0x0002c1, 0x00000000); - nv_icmd(priv, 0x0002c2, 0x00000000); - nv_icmd(priv, 0x0002c3, 0x00000000); - nv_icmd(priv, 0x0002c4, 0x00000000); - nv_icmd(priv, 0x0002c5, 0x00000000); - nv_icmd(priv, 0x0002c6, 0x00000000); - nv_icmd(priv, 0x0002c7, 0x00000000); - nv_icmd(priv, 0x0002c8, 0x00000000); - nv_icmd(priv, 0x0002c9, 0x00000000); - nv_icmd(priv, 0x0002ca, 0x00000000); - nv_icmd(priv, 0x0002cb, 0x00000000); - nv_icmd(priv, 0x0002cc, 0x00000000); - nv_icmd(priv, 0x0002cd, 0x00000000); - nv_icmd(priv, 0x0002ce, 0x00000000); - nv_icmd(priv, 0x0002cf, 0x00000000); - nv_icmd(priv, 0x0004d0, 0x00000000); - nv_icmd(priv, 0x0004d1, 0x00000000); - nv_icmd(priv, 0x0004d2, 0x00000000); - nv_icmd(priv, 0x0004d3, 0x00000000); - nv_icmd(priv, 0x0004d4, 0x00000000); - nv_icmd(priv, 0x0004d5, 0x00000000); - nv_icmd(priv, 0x0004d6, 0x00000000); - nv_icmd(priv, 0x0004d7, 0x00000000); - nv_icmd(priv, 0x0004d8, 0x00000000); - nv_icmd(priv, 0x0004d9, 0x00000000); - nv_icmd(priv, 0x0004da, 0x00000000); - nv_icmd(priv, 0x0004db, 0x00000000); - nv_icmd(priv, 0x0004dc, 0x00000000); - nv_icmd(priv, 0x0004dd, 0x00000000); - nv_icmd(priv, 0x0004de, 0x00000000); - nv_icmd(priv, 0x0004df, 0x00000000); - nv_icmd(priv, 0x000720, 0x00000000); - nv_icmd(priv, 0x000721, 0x00000000); - nv_icmd(priv, 0x000722, 0x00000000); - nv_icmd(priv, 0x000723, 0x00000000); - nv_icmd(priv, 0x000724, 0x00000000); - nv_icmd(priv, 0x000725, 0x00000000); - nv_icmd(priv, 0x000726, 0x00000000); - nv_icmd(priv, 0x000727, 0x00000000); - nv_icmd(priv, 0x000728, 0x00000000); - nv_icmd(priv, 0x000729, 0x00000000); - nv_icmd(priv, 0x00072a, 0x00000000); - nv_icmd(priv, 0x00072b, 0x00000000); - nv_icmd(priv, 0x00072c, 0x00000000); - nv_icmd(priv, 0x00072d, 0x00000000); - nv_icmd(priv, 0x00072e, 0x00000000); - nv_icmd(priv, 0x00072f, 0x00000000); - nv_icmd(priv, 0x0008c0, 0x00000000); - nv_icmd(priv, 0x0008c1, 0x00000000); - nv_icmd(priv, 0x0008c2, 0x00000000); - nv_icmd(priv, 0x0008c3, 0x00000000); - nv_icmd(priv, 0x0008c4, 0x00000000); - nv_icmd(priv, 0x0008c5, 0x00000000); - nv_icmd(priv, 0x0008c6, 0x00000000); - nv_icmd(priv, 0x0008c7, 0x00000000); - nv_icmd(priv, 0x0008c8, 0x00000000); - nv_icmd(priv, 0x0008c9, 0x00000000); - nv_icmd(priv, 0x0008ca, 0x00000000); - nv_icmd(priv, 0x0008cb, 0x00000000); - nv_icmd(priv, 0x0008cc, 0x00000000); - nv_icmd(priv, 0x0008cd, 0x00000000); - nv_icmd(priv, 0x0008ce, 0x00000000); - nv_icmd(priv, 0x0008cf, 0x00000000); - nv_icmd(priv, 0x000890, 0x00000000); - nv_icmd(priv, 0x000891, 0x00000000); - nv_icmd(priv, 0x000892, 0x00000000); - nv_icmd(priv, 0x000893, 0x00000000); - nv_icmd(priv, 0x000894, 0x00000000); - nv_icmd(priv, 0x000895, 0x00000000); - nv_icmd(priv, 0x000896, 0x00000000); - nv_icmd(priv, 0x000897, 0x00000000); - nv_icmd(priv, 0x000898, 0x00000000); - nv_icmd(priv, 0x000899, 0x00000000); - nv_icmd(priv, 0x00089a, 0x00000000); - nv_icmd(priv, 0x00089b, 0x00000000); - nv_icmd(priv, 0x00089c, 0x00000000); - nv_icmd(priv, 0x00089d, 0x00000000); - nv_icmd(priv, 0x00089e, 0x00000000); - nv_icmd(priv, 0x00089f, 0x00000000); - nv_icmd(priv, 0x0008e0, 0x00000000); - nv_icmd(priv, 0x0008e1, 0x00000000); - nv_icmd(priv, 0x0008e2, 0x00000000); - nv_icmd(priv, 0x0008e3, 0x00000000); - nv_icmd(priv, 0x0008e4, 0x00000000); - nv_icmd(priv, 0x0008e5, 0x00000000); - nv_icmd(priv, 0x0008e6, 0x00000000); - nv_icmd(priv, 0x0008e7, 0x00000000); - nv_icmd(priv, 0x0008e8, 0x00000000); - nv_icmd(priv, 0x0008e9, 0x00000000); - nv_icmd(priv, 0x0008ea, 0x00000000); - nv_icmd(priv, 0x0008eb, 0x00000000); - nv_icmd(priv, 0x0008ec, 0x00000000); - nv_icmd(priv, 0x0008ed, 0x00000000); - nv_icmd(priv, 0x0008ee, 0x00000000); - nv_icmd(priv, 0x0008ef, 0x00000000); - nv_icmd(priv, 0x0008a0, 0x00000000); - nv_icmd(priv, 0x0008a1, 0x00000000); - nv_icmd(priv, 0x0008a2, 0x00000000); - nv_icmd(priv, 0x0008a3, 0x00000000); - nv_icmd(priv, 0x0008a4, 0x00000000); - nv_icmd(priv, 0x0008a5, 0x00000000); - nv_icmd(priv, 0x0008a6, 0x00000000); - nv_icmd(priv, 0x0008a7, 0x00000000); - nv_icmd(priv, 0x0008a8, 0x00000000); - nv_icmd(priv, 0x0008a9, 0x00000000); - nv_icmd(priv, 0x0008aa, 0x00000000); - nv_icmd(priv, 0x0008ab, 0x00000000); - nv_icmd(priv, 0x0008ac, 0x00000000); - nv_icmd(priv, 0x0008ad, 0x00000000); - nv_icmd(priv, 0x0008ae, 0x00000000); - nv_icmd(priv, 0x0008af, 0x00000000); - nv_icmd(priv, 0x0008f0, 0x00000000); - nv_icmd(priv, 0x0008f1, 0x00000000); - nv_icmd(priv, 0x0008f2, 0x00000000); - nv_icmd(priv, 0x0008f3, 0x00000000); - nv_icmd(priv, 0x0008f4, 0x00000000); - nv_icmd(priv, 0x0008f5, 0x00000000); - nv_icmd(priv, 0x0008f6, 0x00000000); - nv_icmd(priv, 0x0008f7, 0x00000000); - nv_icmd(priv, 0x0008f8, 0x00000000); - nv_icmd(priv, 0x0008f9, 0x00000000); - nv_icmd(priv, 0x0008fa, 0x00000000); - nv_icmd(priv, 0x0008fb, 0x00000000); - nv_icmd(priv, 0x0008fc, 0x00000000); - nv_icmd(priv, 0x0008fd, 0x00000000); - nv_icmd(priv, 0x0008fe, 0x00000000); - nv_icmd(priv, 0x0008ff, 0x00000000); - nv_icmd(priv, 0x00094c, 0x000000ff); - nv_icmd(priv, 0x00094d, 0xffffffff); - nv_icmd(priv, 0x00094e, 0x00000002); - nv_icmd(priv, 0x0002ec, 0x00000001); - nv_icmd(priv, 0x000303, 0x00000001); - nv_icmd(priv, 0x0002e6, 0x00000001); - nv_icmd(priv, 0x000466, 0x00000052); - nv_icmd(priv, 0x000301, 0x3f800000); - nv_icmd(priv, 0x000304, 0x30201000); - nv_icmd(priv, 0x000305, 0x70605040); - nv_icmd(priv, 0x000306, 0xb8a89888); - nv_icmd(priv, 0x000307, 0xf8e8d8c8); - nv_icmd(priv, 0x00030a, 0x00ffff00); - nv_icmd(priv, 0x00030b, 0x0000001a); - nv_icmd(priv, 0x00030c, 0x00000001); - nv_icmd(priv, 0x000318, 0x00000001); - nv_icmd(priv, 0x000340, 0x00000000); - nv_icmd(priv, 0x000375, 0x00000001); - nv_icmd(priv, 0x00037d, 0x00000006); - nv_icmd(priv, 0x0003a0, 0x00000002); - nv_icmd(priv, 0x0003aa, 0x00000001); - nv_icmd(priv, 0x0003a9, 0x00000001); - nv_icmd(priv, 0x000380, 0x00000001); - nv_icmd(priv, 0x000383, 0x00000011); - nv_icmd(priv, 0x000360, 0x00000040); - nv_icmd(priv, 0x000366, 0x00000000); - nv_icmd(priv, 0x000367, 0x00000000); - nv_icmd(priv, 0x000368, 0x00000fff); - nv_icmd(priv, 0x000370, 0x00000000); - nv_icmd(priv, 0x000371, 0x00000000); - nv_icmd(priv, 0x000372, 0x000fffff); - nv_icmd(priv, 0x00037a, 0x00000012); - nv_icmd(priv, 0x000619, 0x00000003); - nv_icmd(priv, 0x000811, 0x00000003); - nv_icmd(priv, 0x000812, 0x00000004); - nv_icmd(priv, 0x000813, 0x00000006); - nv_icmd(priv, 0x000814, 0x00000008); - nv_icmd(priv, 0x000815, 0x0000000b); - nv_icmd(priv, 0x000800, 0x00000001); - nv_icmd(priv, 0x000801, 0x00000001); - nv_icmd(priv, 0x000802, 0x00000001); - nv_icmd(priv, 0x000803, 0x00000001); - nv_icmd(priv, 0x000804, 0x00000001); - nv_icmd(priv, 0x000805, 0x00000001); - nv_icmd(priv, 0x000632, 0x00000001); - nv_icmd(priv, 0x000633, 0x00000002); - nv_icmd(priv, 0x000634, 0x00000003); - nv_icmd(priv, 0x000635, 0x00000004); - nv_icmd(priv, 0x000654, 0x3f800000); - nv_icmd(priv, 0x000657, 0x3f800000); - nv_icmd(priv, 0x000655, 0x3f800000); - nv_icmd(priv, 0x000656, 0x3f800000); - nv_icmd(priv, 0x0006cd, 0x3f800000); - nv_icmd(priv, 0x0007f5, 0x3f800000); - nv_icmd(priv, 0x0007dc, 0x39291909); - nv_icmd(priv, 0x0007dd, 0x79695949); - nv_icmd(priv, 0x0007de, 0xb9a99989); - nv_icmd(priv, 0x0007df, 0xf9e9d9c9); - nv_icmd(priv, 0x0007e8, 0x00003210); - nv_icmd(priv, 0x0007e9, 0x00007654); - nv_icmd(priv, 0x0007ea, 0x00000098); - nv_icmd(priv, 0x0007ec, 0x39291909); - nv_icmd(priv, 0x0007ed, 0x79695949); - nv_icmd(priv, 0x0007ee, 0xb9a99989); - nv_icmd(priv, 0x0007ef, 0xf9e9d9c9); - nv_icmd(priv, 0x0007f0, 0x00003210); - nv_icmd(priv, 0x0007f1, 0x00007654); - nv_icmd(priv, 0x0007f2, 0x00000098); - nv_icmd(priv, 0x0005a5, 0x00000001); - nv_icmd(priv, 0x000980, 0x00000000); - nv_icmd(priv, 0x000981, 0x00000000); - nv_icmd(priv, 0x000982, 0x00000000); - nv_icmd(priv, 0x000983, 0x00000000); - nv_icmd(priv, 0x000984, 0x00000000); - nv_icmd(priv, 0x000985, 0x00000000); - nv_icmd(priv, 0x000986, 0x00000000); - nv_icmd(priv, 0x000987, 0x00000000); - nv_icmd(priv, 0x000988, 0x00000000); - nv_icmd(priv, 0x000989, 0x00000000); - nv_icmd(priv, 0x00098a, 0x00000000); - nv_icmd(priv, 0x00098b, 0x00000000); - nv_icmd(priv, 0x00098c, 0x00000000); - nv_icmd(priv, 0x00098d, 0x00000000); - nv_icmd(priv, 0x00098e, 0x00000000); - nv_icmd(priv, 0x00098f, 0x00000000); - nv_icmd(priv, 0x000990, 0x00000000); - nv_icmd(priv, 0x000991, 0x00000000); - nv_icmd(priv, 0x000992, 0x00000000); - nv_icmd(priv, 0x000993, 0x00000000); - nv_icmd(priv, 0x000994, 0x00000000); - nv_icmd(priv, 0x000995, 0x00000000); - nv_icmd(priv, 0x000996, 0x00000000); - nv_icmd(priv, 0x000997, 0x00000000); - nv_icmd(priv, 0x000998, 0x00000000); - nv_icmd(priv, 0x000999, 0x00000000); - nv_icmd(priv, 0x00099a, 0x00000000); - nv_icmd(priv, 0x00099b, 0x00000000); - nv_icmd(priv, 0x00099c, 0x00000000); - nv_icmd(priv, 0x00099d, 0x00000000); - nv_icmd(priv, 0x00099e, 0x00000000); - nv_icmd(priv, 0x00099f, 0x00000000); - nv_icmd(priv, 0x0009a0, 0x00000000); - nv_icmd(priv, 0x0009a1, 0x00000000); - nv_icmd(priv, 0x0009a2, 0x00000000); - nv_icmd(priv, 0x0009a3, 0x00000000); - nv_icmd(priv, 0x0009a4, 0x00000000); - nv_icmd(priv, 0x0009a5, 0x00000000); - nv_icmd(priv, 0x0009a6, 0x00000000); - nv_icmd(priv, 0x0009a7, 0x00000000); - nv_icmd(priv, 0x0009a8, 0x00000000); - nv_icmd(priv, 0x0009a9, 0x00000000); - nv_icmd(priv, 0x0009aa, 0x00000000); - nv_icmd(priv, 0x0009ab, 0x00000000); - nv_icmd(priv, 0x0009ac, 0x00000000); - nv_icmd(priv, 0x0009ad, 0x00000000); - nv_icmd(priv, 0x0009ae, 0x00000000); - nv_icmd(priv, 0x0009af, 0x00000000); - nv_icmd(priv, 0x0009b0, 0x00000000); - nv_icmd(priv, 0x0009b1, 0x00000000); - nv_icmd(priv, 0x0009b2, 0x00000000); - nv_icmd(priv, 0x0009b3, 0x00000000); - nv_icmd(priv, 0x0009b4, 0x00000000); - nv_icmd(priv, 0x0009b5, 0x00000000); - nv_icmd(priv, 0x0009b6, 0x00000000); - nv_icmd(priv, 0x0009b7, 0x00000000); - nv_icmd(priv, 0x0009b8, 0x00000000); - nv_icmd(priv, 0x0009b9, 0x00000000); - nv_icmd(priv, 0x0009ba, 0x00000000); - nv_icmd(priv, 0x0009bb, 0x00000000); - nv_icmd(priv, 0x0009bc, 0x00000000); - nv_icmd(priv, 0x0009bd, 0x00000000); - nv_icmd(priv, 0x0009be, 0x00000000); - nv_icmd(priv, 0x0009bf, 0x00000000); - nv_icmd(priv, 0x0009c0, 0x00000000); - nv_icmd(priv, 0x0009c1, 0x00000000); - nv_icmd(priv, 0x0009c2, 0x00000000); - nv_icmd(priv, 0x0009c3, 0x00000000); - nv_icmd(priv, 0x0009c4, 0x00000000); - nv_icmd(priv, 0x0009c5, 0x00000000); - nv_icmd(priv, 0x0009c6, 0x00000000); - nv_icmd(priv, 0x0009c7, 0x00000000); - nv_icmd(priv, 0x0009c8, 0x00000000); - nv_icmd(priv, 0x0009c9, 0x00000000); - nv_icmd(priv, 0x0009ca, 0x00000000); - nv_icmd(priv, 0x0009cb, 0x00000000); - nv_icmd(priv, 0x0009cc, 0x00000000); - nv_icmd(priv, 0x0009cd, 0x00000000); - nv_icmd(priv, 0x0009ce, 0x00000000); - nv_icmd(priv, 0x0009cf, 0x00000000); - nv_icmd(priv, 0x0009d0, 0x00000000); - nv_icmd(priv, 0x0009d1, 0x00000000); - nv_icmd(priv, 0x0009d2, 0x00000000); - nv_icmd(priv, 0x0009d3, 0x00000000); - nv_icmd(priv, 0x0009d4, 0x00000000); - nv_icmd(priv, 0x0009d5, 0x00000000); - nv_icmd(priv, 0x0009d6, 0x00000000); - nv_icmd(priv, 0x0009d7, 0x00000000); - nv_icmd(priv, 0x0009d8, 0x00000000); - nv_icmd(priv, 0x0009d9, 0x00000000); - nv_icmd(priv, 0x0009da, 0x00000000); - nv_icmd(priv, 0x0009db, 0x00000000); - nv_icmd(priv, 0x0009dc, 0x00000000); - nv_icmd(priv, 0x0009dd, 0x00000000); - nv_icmd(priv, 0x0009de, 0x00000000); - nv_icmd(priv, 0x0009df, 0x00000000); - nv_icmd(priv, 0x0009e0, 0x00000000); - nv_icmd(priv, 0x0009e1, 0x00000000); - nv_icmd(priv, 0x0009e2, 0x00000000); - nv_icmd(priv, 0x0009e3, 0x00000000); - nv_icmd(priv, 0x0009e4, 0x00000000); - nv_icmd(priv, 0x0009e5, 0x00000000); - nv_icmd(priv, 0x0009e6, 0x00000000); - nv_icmd(priv, 0x0009e7, 0x00000000); - nv_icmd(priv, 0x0009e8, 0x00000000); - nv_icmd(priv, 0x0009e9, 0x00000000); - nv_icmd(priv, 0x0009ea, 0x00000000); - nv_icmd(priv, 0x0009eb, 0x00000000); - nv_icmd(priv, 0x0009ec, 0x00000000); - nv_icmd(priv, 0x0009ed, 0x00000000); - nv_icmd(priv, 0x0009ee, 0x00000000); - nv_icmd(priv, 0x0009ef, 0x00000000); - nv_icmd(priv, 0x0009f0, 0x00000000); - nv_icmd(priv, 0x0009f1, 0x00000000); - nv_icmd(priv, 0x0009f2, 0x00000000); - nv_icmd(priv, 0x0009f3, 0x00000000); - nv_icmd(priv, 0x0009f4, 0x00000000); - nv_icmd(priv, 0x0009f5, 0x00000000); - nv_icmd(priv, 0x0009f6, 0x00000000); - nv_icmd(priv, 0x0009f7, 0x00000000); - nv_icmd(priv, 0x0009f8, 0x00000000); - nv_icmd(priv, 0x0009f9, 0x00000000); - nv_icmd(priv, 0x0009fa, 0x00000000); - nv_icmd(priv, 0x0009fb, 0x00000000); - nv_icmd(priv, 0x0009fc, 0x00000000); - nv_icmd(priv, 0x0009fd, 0x00000000); - nv_icmd(priv, 0x0009fe, 0x00000000); - nv_icmd(priv, 0x0009ff, 0x00000000); - nv_icmd(priv, 0x000468, 0x00000004); - nv_icmd(priv, 0x00046c, 0x00000001); - nv_icmd(priv, 0x000470, 0x00000000); - nv_icmd(priv, 0x000471, 0x00000000); - nv_icmd(priv, 0x000472, 0x00000000); - nv_icmd(priv, 0x000473, 0x00000000); - nv_icmd(priv, 0x000474, 0x00000000); - nv_icmd(priv, 0x000475, 0x00000000); - nv_icmd(priv, 0x000476, 0x00000000); - nv_icmd(priv, 0x000477, 0x00000000); - nv_icmd(priv, 0x000478, 0x00000000); - nv_icmd(priv, 0x000479, 0x00000000); - nv_icmd(priv, 0x00047a, 0x00000000); - nv_icmd(priv, 0x00047b, 0x00000000); - nv_icmd(priv, 0x00047c, 0x00000000); - nv_icmd(priv, 0x00047d, 0x00000000); - nv_icmd(priv, 0x00047e, 0x00000000); - nv_icmd(priv, 0x00047f, 0x00000000); - nv_icmd(priv, 0x000480, 0x00000000); - nv_icmd(priv, 0x000481, 0x00000000); - nv_icmd(priv, 0x000482, 0x00000000); - nv_icmd(priv, 0x000483, 0x00000000); - nv_icmd(priv, 0x000484, 0x00000000); - nv_icmd(priv, 0x000485, 0x00000000); - nv_icmd(priv, 0x000486, 0x00000000); - nv_icmd(priv, 0x000487, 0x00000000); - nv_icmd(priv, 0x000488, 0x00000000); - nv_icmd(priv, 0x000489, 0x00000000); - nv_icmd(priv, 0x00048a, 0x00000000); - nv_icmd(priv, 0x00048b, 0x00000000); - nv_icmd(priv, 0x00048c, 0x00000000); - nv_icmd(priv, 0x00048d, 0x00000000); - nv_icmd(priv, 0x00048e, 0x00000000); - nv_icmd(priv, 0x00048f, 0x00000000); - nv_icmd(priv, 0x000490, 0x00000000); - nv_icmd(priv, 0x000491, 0x00000000); - nv_icmd(priv, 0x000492, 0x00000000); - nv_icmd(priv, 0x000493, 0x00000000); - nv_icmd(priv, 0x000494, 0x00000000); - nv_icmd(priv, 0x000495, 0x00000000); - nv_icmd(priv, 0x000496, 0x00000000); - nv_icmd(priv, 0x000497, 0x00000000); - nv_icmd(priv, 0x000498, 0x00000000); - nv_icmd(priv, 0x000499, 0x00000000); - nv_icmd(priv, 0x00049a, 0x00000000); - nv_icmd(priv, 0x00049b, 0x00000000); - nv_icmd(priv, 0x00049c, 0x00000000); - nv_icmd(priv, 0x00049d, 0x00000000); - nv_icmd(priv, 0x00049e, 0x00000000); - nv_icmd(priv, 0x00049f, 0x00000000); - nv_icmd(priv, 0x0004a0, 0x00000000); - nv_icmd(priv, 0x0004a1, 0x00000000); - nv_icmd(priv, 0x0004a2, 0x00000000); - nv_icmd(priv, 0x0004a3, 0x00000000); - nv_icmd(priv, 0x0004a4, 0x00000000); - nv_icmd(priv, 0x0004a5, 0x00000000); - nv_icmd(priv, 0x0004a6, 0x00000000); - nv_icmd(priv, 0x0004a7, 0x00000000); - nv_icmd(priv, 0x0004a8, 0x00000000); - nv_icmd(priv, 0x0004a9, 0x00000000); - nv_icmd(priv, 0x0004aa, 0x00000000); - nv_icmd(priv, 0x0004ab, 0x00000000); - nv_icmd(priv, 0x0004ac, 0x00000000); - nv_icmd(priv, 0x0004ad, 0x00000000); - nv_icmd(priv, 0x0004ae, 0x00000000); - nv_icmd(priv, 0x0004af, 0x00000000); - nv_icmd(priv, 0x0004b0, 0x00000000); - nv_icmd(priv, 0x0004b1, 0x00000000); - nv_icmd(priv, 0x0004b2, 0x00000000); - nv_icmd(priv, 0x0004b3, 0x00000000); - nv_icmd(priv, 0x0004b4, 0x00000000); - nv_icmd(priv, 0x0004b5, 0x00000000); - nv_icmd(priv, 0x0004b6, 0x00000000); - nv_icmd(priv, 0x0004b7, 0x00000000); - nv_icmd(priv, 0x0004b8, 0x00000000); - nv_icmd(priv, 0x0004b9, 0x00000000); - nv_icmd(priv, 0x0004ba, 0x00000000); - nv_icmd(priv, 0x0004bb, 0x00000000); - nv_icmd(priv, 0x0004bc, 0x00000000); - nv_icmd(priv, 0x0004bd, 0x00000000); - nv_icmd(priv, 0x0004be, 0x00000000); - nv_icmd(priv, 0x0004bf, 0x00000000); - nv_icmd(priv, 0x0004c0, 0x00000000); - nv_icmd(priv, 0x0004c1, 0x00000000); - nv_icmd(priv, 0x0004c2, 0x00000000); - nv_icmd(priv, 0x0004c3, 0x00000000); - nv_icmd(priv, 0x0004c4, 0x00000000); - nv_icmd(priv, 0x0004c5, 0x00000000); - nv_icmd(priv, 0x0004c6, 0x00000000); - nv_icmd(priv, 0x0004c7, 0x00000000); - nv_icmd(priv, 0x0004c8, 0x00000000); - nv_icmd(priv, 0x0004c9, 0x00000000); - nv_icmd(priv, 0x0004ca, 0x00000000); - nv_icmd(priv, 0x0004cb, 0x00000000); - nv_icmd(priv, 0x0004cc, 0x00000000); - nv_icmd(priv, 0x0004cd, 0x00000000); - nv_icmd(priv, 0x0004ce, 0x00000000); - nv_icmd(priv, 0x0004cf, 0x00000000); - nv_icmd(priv, 0x000510, 0x3f800000); - nv_icmd(priv, 0x000511, 0x3f800000); - nv_icmd(priv, 0x000512, 0x3f800000); - nv_icmd(priv, 0x000513, 0x3f800000); - nv_icmd(priv, 0x000514, 0x3f800000); - nv_icmd(priv, 0x000515, 0x3f800000); - nv_icmd(priv, 0x000516, 0x3f800000); - nv_icmd(priv, 0x000517, 0x3f800000); - nv_icmd(priv, 0x000518, 0x3f800000); - nv_icmd(priv, 0x000519, 0x3f800000); - nv_icmd(priv, 0x00051a, 0x3f800000); - nv_icmd(priv, 0x00051b, 0x3f800000); - nv_icmd(priv, 0x00051c, 0x3f800000); - nv_icmd(priv, 0x00051d, 0x3f800000); - nv_icmd(priv, 0x00051e, 0x3f800000); - nv_icmd(priv, 0x00051f, 0x3f800000); - nv_icmd(priv, 0x000520, 0x000002b6); - nv_icmd(priv, 0x000529, 0x00000001); - nv_icmd(priv, 0x000530, 0xffff0000); - nv_icmd(priv, 0x000531, 0xffff0000); - nv_icmd(priv, 0x000532, 0xffff0000); - nv_icmd(priv, 0x000533, 0xffff0000); - nv_icmd(priv, 0x000534, 0xffff0000); - nv_icmd(priv, 0x000535, 0xffff0000); - nv_icmd(priv, 0x000536, 0xffff0000); - nv_icmd(priv, 0x000537, 0xffff0000); - nv_icmd(priv, 0x000538, 0xffff0000); - nv_icmd(priv, 0x000539, 0xffff0000); - nv_icmd(priv, 0x00053a, 0xffff0000); - nv_icmd(priv, 0x00053b, 0xffff0000); - nv_icmd(priv, 0x00053c, 0xffff0000); - nv_icmd(priv, 0x00053d, 0xffff0000); - nv_icmd(priv, 0x00053e, 0xffff0000); - nv_icmd(priv, 0x00053f, 0xffff0000); - nv_icmd(priv, 0x000585, 0x0000003f); - nv_icmd(priv, 0x000576, 0x00000003); - nv_icmd(priv, 0x00057b, 0x00000059); - nv_icmd(priv, 0x000586, 0x00000040); - nv_icmd(priv, 0x000582, 0x00000080); - nv_icmd(priv, 0x000583, 0x00000080); - nv_icmd(priv, 0x0005c2, 0x00000001); - nv_icmd(priv, 0x000638, 0x00000001); - nv_icmd(priv, 0x000639, 0x00000001); - nv_icmd(priv, 0x00063a, 0x00000002); - nv_icmd(priv, 0x00063b, 0x00000001); - nv_icmd(priv, 0x00063c, 0x00000001); - nv_icmd(priv, 0x00063d, 0x00000002); - nv_icmd(priv, 0x00063e, 0x00000001); - nv_icmd(priv, 0x0008b8, 0x00000001); - nv_icmd(priv, 0x0008b9, 0x00000001); - nv_icmd(priv, 0x0008ba, 0x00000001); - nv_icmd(priv, 0x0008bb, 0x00000001); - nv_icmd(priv, 0x0008bc, 0x00000001); - nv_icmd(priv, 0x0008bd, 0x00000001); - nv_icmd(priv, 0x0008be, 0x00000001); - nv_icmd(priv, 0x0008bf, 0x00000001); - nv_icmd(priv, 0x000900, 0x00000001); - nv_icmd(priv, 0x000901, 0x00000001); - nv_icmd(priv, 0x000902, 0x00000001); - nv_icmd(priv, 0x000903, 0x00000001); - nv_icmd(priv, 0x000904, 0x00000001); - nv_icmd(priv, 0x000905, 0x00000001); - nv_icmd(priv, 0x000906, 0x00000001); - nv_icmd(priv, 0x000907, 0x00000001); - nv_icmd(priv, 0x000908, 0x00000002); - nv_icmd(priv, 0x000909, 0x00000002); - nv_icmd(priv, 0x00090a, 0x00000002); - nv_icmd(priv, 0x00090b, 0x00000002); - nv_icmd(priv, 0x00090c, 0x00000002); - nv_icmd(priv, 0x00090d, 0x00000002); - nv_icmd(priv, 0x00090e, 0x00000002); - nv_icmd(priv, 0x00090f, 0x00000002); - nv_icmd(priv, 0x000910, 0x00000001); - nv_icmd(priv, 0x000911, 0x00000001); - nv_icmd(priv, 0x000912, 0x00000001); - nv_icmd(priv, 0x000913, 0x00000001); - nv_icmd(priv, 0x000914, 0x00000001); - nv_icmd(priv, 0x000915, 0x00000001); - nv_icmd(priv, 0x000916, 0x00000001); - nv_icmd(priv, 0x000917, 0x00000001); - nv_icmd(priv, 0x000918, 0x00000001); - nv_icmd(priv, 0x000919, 0x00000001); - nv_icmd(priv, 0x00091a, 0x00000001); - nv_icmd(priv, 0x00091b, 0x00000001); - nv_icmd(priv, 0x00091c, 0x00000001); - nv_icmd(priv, 0x00091d, 0x00000001); - nv_icmd(priv, 0x00091e, 0x00000001); - nv_icmd(priv, 0x00091f, 0x00000001); - nv_icmd(priv, 0x000920, 0x00000002); - nv_icmd(priv, 0x000921, 0x00000002); - nv_icmd(priv, 0x000922, 0x00000002); - nv_icmd(priv, 0x000923, 0x00000002); - nv_icmd(priv, 0x000924, 0x00000002); - nv_icmd(priv, 0x000925, 0x00000002); - nv_icmd(priv, 0x000926, 0x00000002); - nv_icmd(priv, 0x000927, 0x00000002); - nv_icmd(priv, 0x000928, 0x00000001); - nv_icmd(priv, 0x000929, 0x00000001); - nv_icmd(priv, 0x00092a, 0x00000001); - nv_icmd(priv, 0x00092b, 0x00000001); - nv_icmd(priv, 0x00092c, 0x00000001); - nv_icmd(priv, 0x00092d, 0x00000001); - nv_icmd(priv, 0x00092e, 0x00000001); - nv_icmd(priv, 0x00092f, 0x00000001); - nv_icmd(priv, 0x000648, 0x00000001); - nv_icmd(priv, 0x000649, 0x00000001); - nv_icmd(priv, 0x00064a, 0x00000001); - nv_icmd(priv, 0x00064b, 0x00000001); - nv_icmd(priv, 0x00064c, 0x00000001); - nv_icmd(priv, 0x00064d, 0x00000001); - nv_icmd(priv, 0x00064e, 0x00000001); - nv_icmd(priv, 0x00064f, 0x00000001); - nv_icmd(priv, 0x000650, 0x00000001); - nv_icmd(priv, 0x000658, 0x0000000f); - nv_icmd(priv, 0x0007ff, 0x0000000a); - nv_icmd(priv, 0x00066a, 0x40000000); - nv_icmd(priv, 0x00066b, 0x10000000); - nv_icmd(priv, 0x00066c, 0xffff0000); - nv_icmd(priv, 0x00066d, 0xffff0000); - nv_icmd(priv, 0x0007af, 0x00000008); - nv_icmd(priv, 0x0007b0, 0x00000008); - nv_icmd(priv, 0x0007f6, 0x00000001); - nv_icmd(priv, 0x0006b2, 0x00000055); - nv_icmd(priv, 0x0007ad, 0x00000003); - nv_icmd(priv, 0x000937, 0x00000001); - nv_icmd(priv, 0x000971, 0x00000008); - nv_icmd(priv, 0x000972, 0x00000040); - nv_icmd(priv, 0x000973, 0x0000012c); - nv_icmd(priv, 0x00097c, 0x00000040); - nv_icmd(priv, 0x000979, 0x00000003); - nv_icmd(priv, 0x000975, 0x00000020); - nv_icmd(priv, 0x000976, 0x00000001); - nv_icmd(priv, 0x000977, 0x00000020); - nv_icmd(priv, 0x000978, 0x00000001); - nv_icmd(priv, 0x000957, 0x00000003); - nv_icmd(priv, 0x00095e, 0x20164010); - nv_icmd(priv, 0x00095f, 0x00000020); - nv_icmd(priv, 0x00097d, 0x00000020); - nv_icmd(priv, 0x000683, 0x00000006); - nv_icmd(priv, 0x000685, 0x003fffff); - nv_icmd(priv, 0x000687, 0x003fffff); - nv_icmd(priv, 0x0006a0, 0x00000005); - nv_icmd(priv, 0x000840, 0x00400008); - nv_icmd(priv, 0x000841, 0x08000080); - nv_icmd(priv, 0x000842, 0x00400008); - nv_icmd(priv, 0x000843, 0x08000080); - nv_icmd(priv, 0x0006aa, 0x00000001); - nv_icmd(priv, 0x0006ab, 0x00000002); - nv_icmd(priv, 0x0006ac, 0x00000080); - nv_icmd(priv, 0x0006ad, 0x00000100); - nv_icmd(priv, 0x0006ae, 0x00000100); - nv_icmd(priv, 0x0006b1, 0x00000011); - nv_icmd(priv, 0x0006bb, 0x000000cf); - nv_icmd(priv, 0x0006ce, 0x2a712488); - nv_icmd(priv, 0x000739, 0x4085c000); - nv_icmd(priv, 0x00073a, 0x00000080); - nv_icmd(priv, 0x000786, 0x80000100); - nv_icmd(priv, 0x00073c, 0x00010100); - nv_icmd(priv, 0x00073d, 0x02800000); - nv_icmd(priv, 0x000787, 0x000000cf); - nv_icmd(priv, 0x00078c, 0x00000008); - nv_icmd(priv, 0x000792, 0x00000001); - nv_icmd(priv, 0x000794, 0x00000001); - nv_icmd(priv, 0x000795, 0x00000001); - nv_icmd(priv, 0x000796, 0x00000001); - nv_icmd(priv, 0x000797, 0x000000cf); - nv_icmd(priv, 0x000836, 0x00000001); - nv_icmd(priv, 0x00079a, 0x00000002); - nv_icmd(priv, 0x000833, 0x04444480); - nv_icmd(priv, 0x0007a1, 0x00000001); - nv_icmd(priv, 0x0007a3, 0x00000001); - nv_icmd(priv, 0x0007a4, 0x00000001); - nv_icmd(priv, 0x0007a5, 0x00000001); - nv_icmd(priv, 0x000831, 0x00000004); - nv_icmd(priv, 0x000b07, 0x00000002); - nv_icmd(priv, 0x000b08, 0x00000100); - nv_icmd(priv, 0x000b09, 0x00000100); - nv_icmd(priv, 0x000b0a, 0x00000001); - nv_icmd(priv, 0x000a04, 0x000000ff); - nv_icmd(priv, 0x000a0b, 0x00000040); - nv_icmd(priv, 0x00097f, 0x00000100); - nv_icmd(priv, 0x000a02, 0x00000001); - nv_icmd(priv, 0x000809, 0x00000007); - nv_icmd(priv, 0x00c221, 0x00000040); - nv_icmd(priv, 0x00c1b0, 0x0000000f); - nv_icmd(priv, 0x00c1b1, 0x0000000f); - nv_icmd(priv, 0x00c1b2, 0x0000000f); - nv_icmd(priv, 0x00c1b3, 0x0000000f); - nv_icmd(priv, 0x00c1b4, 0x0000000f); - nv_icmd(priv, 0x00c1b5, 0x0000000f); - nv_icmd(priv, 0x00c1b6, 0x0000000f); - nv_icmd(priv, 0x00c1b7, 0x0000000f); - nv_icmd(priv, 0x00c1b8, 0x0fac6881); - nv_icmd(priv, 0x00c1b9, 0x00fac688); - nv_icmd(priv, 0x00c401, 0x00000001); - nv_icmd(priv, 0x00c402, 0x00010001); - nv_icmd(priv, 0x00c403, 0x00000001); - nv_icmd(priv, 0x00c404, 0x00000001); - nv_icmd(priv, 0x00c40e, 0x00000020); - nv_icmd(priv, 0x00c500, 0x00000003); - nv_icmd(priv, 0x01e100, 0x00000001); - nv_icmd(priv, 0x001000, 0x00000002); - nv_icmd(priv, 0x0006aa, 0x00000001); - nv_icmd(priv, 0x0006ad, 0x00000100); - nv_icmd(priv, 0x0006ae, 0x00000100); - nv_icmd(priv, 0x0006b1, 0x00000011); - nv_icmd(priv, 0x00078c, 0x00000008); - nv_icmd(priv, 0x000792, 0x00000001); - nv_icmd(priv, 0x000794, 0x00000001); - nv_icmd(priv, 0x000795, 0x00000001); - nv_icmd(priv, 0x000796, 0x00000001); - nv_icmd(priv, 0x000797, 0x000000cf); - nv_icmd(priv, 0x00079a, 0x00000002); - nv_icmd(priv, 0x000833, 0x04444480); - nv_icmd(priv, 0x0007a1, 0x00000001); - nv_icmd(priv, 0x0007a3, 0x00000001); - nv_icmd(priv, 0x0007a4, 0x00000001); - nv_icmd(priv, 0x0007a5, 0x00000001); - nv_icmd(priv, 0x000831, 0x00000004); - nv_icmd(priv, 0x01e100, 0x00000001); - nv_icmd(priv, 0x001000, 0x00000008); - nv_icmd(priv, 0x000039, 0x00000000); - nv_icmd(priv, 0x00003a, 0x00000000); - nv_icmd(priv, 0x00003b, 0x00000000); - nv_icmd(priv, 0x000380, 0x00000001); - nv_icmd(priv, 0x000366, 0x00000000); - nv_icmd(priv, 0x000367, 0x00000000); - nv_icmd(priv, 0x000368, 0x00000fff); - nv_icmd(priv, 0x000370, 0x00000000); - nv_icmd(priv, 0x000371, 0x00000000); - nv_icmd(priv, 0x000372, 0x000fffff); - nv_icmd(priv, 0x000813, 0x00000006); - nv_icmd(priv, 0x000814, 0x00000008); - nv_icmd(priv, 0x000957, 0x00000003); - nv_icmd(priv, 0x000b07, 0x00000002); - nv_icmd(priv, 0x000b08, 0x00000100); - nv_icmd(priv, 0x000b09, 0x00000100); - nv_icmd(priv, 0x000b0a, 0x00000001); - nv_icmd(priv, 0x000a04, 0x000000ff); - nv_icmd(priv, 0x00097f, 0x00000100); - nv_icmd(priv, 0x000a02, 0x00000001); - nv_icmd(priv, 0x000809, 0x00000007); - nv_icmd(priv, 0x00c221, 0x00000040); - nv_icmd(priv, 0x00c401, 0x00000001); - nv_icmd(priv, 0x00c402, 0x00010001); - nv_icmd(priv, 0x00c403, 0x00000001); - nv_icmd(priv, 0x00c404, 0x00000001); - nv_icmd(priv, 0x00c40e, 0x00000020); - nv_icmd(priv, 0x00c500, 0x00000003); - nv_icmd(priv, 0x01e100, 0x00000001); - nv_icmd(priv, 0x001000, 0x00000001); - nv_icmd(priv, 0x000b07, 0x00000002); - nv_icmd(priv, 0x000b08, 0x00000100); - nv_icmd(priv, 0x000b09, 0x00000100); - nv_icmd(priv, 0x000b0a, 0x00000001); - nv_icmd(priv, 0x01e100, 0x00000001); - nv_wr32(priv, 0x400208, 0x00000000); -} - -static void -nve0_grctx_generate_a097(struct nvc0_graph_priv *priv) -{ - nv_mthd(priv, 0xa097, 0x0800, 0x00000000); - nv_mthd(priv, 0xa097, 0x0840, 0x00000000); - nv_mthd(priv, 0xa097, 0x0880, 0x00000000); - nv_mthd(priv, 0xa097, 0x08c0, 0x00000000); - nv_mthd(priv, 0xa097, 0x0900, 0x00000000); - nv_mthd(priv, 0xa097, 0x0940, 0x00000000); - nv_mthd(priv, 0xa097, 0x0980, 0x00000000); - nv_mthd(priv, 0xa097, 0x09c0, 0x00000000); - nv_mthd(priv, 0xa097, 0x0804, 0x00000000); - nv_mthd(priv, 0xa097, 0x0844, 0x00000000); - nv_mthd(priv, 0xa097, 0x0884, 0x00000000); - nv_mthd(priv, 0xa097, 0x08c4, 0x00000000); - nv_mthd(priv, 0xa097, 0x0904, 0x00000000); - nv_mthd(priv, 0xa097, 0x0944, 0x00000000); - nv_mthd(priv, 0xa097, 0x0984, 0x00000000); - nv_mthd(priv, 0xa097, 0x09c4, 0x00000000); - nv_mthd(priv, 0xa097, 0x0808, 0x00000400); - nv_mthd(priv, 0xa097, 0x0848, 0x00000400); - nv_mthd(priv, 0xa097, 0x0888, 0x00000400); - nv_mthd(priv, 0xa097, 0x08c8, 0x00000400); - nv_mthd(priv, 0xa097, 0x0908, 0x00000400); - nv_mthd(priv, 0xa097, 0x0948, 0x00000400); - nv_mthd(priv, 0xa097, 0x0988, 0x00000400); - nv_mthd(priv, 0xa097, 0x09c8, 0x00000400); - nv_mthd(priv, 0xa097, 0x080c, 0x00000300); - nv_mthd(priv, 0xa097, 0x084c, 0x00000300); - nv_mthd(priv, 0xa097, 0x088c, 0x00000300); - nv_mthd(priv, 0xa097, 0x08cc, 0x00000300); - nv_mthd(priv, 0xa097, 0x090c, 0x00000300); - nv_mthd(priv, 0xa097, 0x094c, 0x00000300); - nv_mthd(priv, 0xa097, 0x098c, 0x00000300); - nv_mthd(priv, 0xa097, 0x09cc, 0x00000300); - nv_mthd(priv, 0xa097, 0x0810, 0x000000cf); - nv_mthd(priv, 0xa097, 0x0850, 0x00000000); - nv_mthd(priv, 0xa097, 0x0890, 0x00000000); - nv_mthd(priv, 0xa097, 0x08d0, 0x00000000); - nv_mthd(priv, 0xa097, 0x0910, 0x00000000); - nv_mthd(priv, 0xa097, 0x0950, 0x00000000); - nv_mthd(priv, 0xa097, 0x0990, 0x00000000); - nv_mthd(priv, 0xa097, 0x09d0, 0x00000000); - nv_mthd(priv, 0xa097, 0x0814, 0x00000040); - nv_mthd(priv, 0xa097, 0x0854, 0x00000040); - nv_mthd(priv, 0xa097, 0x0894, 0x00000040); - nv_mthd(priv, 0xa097, 0x08d4, 0x00000040); - nv_mthd(priv, 0xa097, 0x0914, 0x00000040); - nv_mthd(priv, 0xa097, 0x0954, 0x00000040); - nv_mthd(priv, 0xa097, 0x0994, 0x00000040); - nv_mthd(priv, 0xa097, 0x09d4, 0x00000040); - nv_mthd(priv, 0xa097, 0x0818, 0x00000001); - nv_mthd(priv, 0xa097, 0x0858, 0x00000001); - nv_mthd(priv, 0xa097, 0x0898, 0x00000001); - nv_mthd(priv, 0xa097, 0x08d8, 0x00000001); - nv_mthd(priv, 0xa097, 0x0918, 0x00000001); - nv_mthd(priv, 0xa097, 0x0958, 0x00000001); - nv_mthd(priv, 0xa097, 0x0998, 0x00000001); - nv_mthd(priv, 0xa097, 0x09d8, 0x00000001); - nv_mthd(priv, 0xa097, 0x081c, 0x00000000); - nv_mthd(priv, 0xa097, 0x085c, 0x00000000); - nv_mthd(priv, 0xa097, 0x089c, 0x00000000); - nv_mthd(priv, 0xa097, 0x08dc, 0x00000000); - nv_mthd(priv, 0xa097, 0x091c, 0x00000000); - nv_mthd(priv, 0xa097, 0x095c, 0x00000000); - nv_mthd(priv, 0xa097, 0x099c, 0x00000000); - nv_mthd(priv, 0xa097, 0x09dc, 0x00000000); - nv_mthd(priv, 0xa097, 0x0820, 0x00000000); - nv_mthd(priv, 0xa097, 0x0860, 0x00000000); - nv_mthd(priv, 0xa097, 0x08a0, 0x00000000); - nv_mthd(priv, 0xa097, 0x08e0, 0x00000000); - nv_mthd(priv, 0xa097, 0x0920, 0x00000000); - nv_mthd(priv, 0xa097, 0x0960, 0x00000000); - nv_mthd(priv, 0xa097, 0x09a0, 0x00000000); - nv_mthd(priv, 0xa097, 0x09e0, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c00, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c10, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c20, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c30, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c40, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c50, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c60, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c70, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c80, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c90, 0x00000000); - nv_mthd(priv, 0xa097, 0x1ca0, 0x00000000); - nv_mthd(priv, 0xa097, 0x1cb0, 0x00000000); - nv_mthd(priv, 0xa097, 0x1cc0, 0x00000000); - nv_mthd(priv, 0xa097, 0x1cd0, 0x00000000); - nv_mthd(priv, 0xa097, 0x1ce0, 0x00000000); - nv_mthd(priv, 0xa097, 0x1cf0, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c04, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c14, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c24, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c34, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c44, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c54, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c64, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c74, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c84, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c94, 0x00000000); - nv_mthd(priv, 0xa097, 0x1ca4, 0x00000000); - nv_mthd(priv, 0xa097, 0x1cb4, 0x00000000); - nv_mthd(priv, 0xa097, 0x1cc4, 0x00000000); - nv_mthd(priv, 0xa097, 0x1cd4, 0x00000000); - nv_mthd(priv, 0xa097, 0x1ce4, 0x00000000); - nv_mthd(priv, 0xa097, 0x1cf4, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c08, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c18, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c28, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c38, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c48, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c58, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c68, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c78, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c88, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c98, 0x00000000); - nv_mthd(priv, 0xa097, 0x1ca8, 0x00000000); - nv_mthd(priv, 0xa097, 0x1cb8, 0x00000000); - nv_mthd(priv, 0xa097, 0x1cc8, 0x00000000); - nv_mthd(priv, 0xa097, 0x1cd8, 0x00000000); - nv_mthd(priv, 0xa097, 0x1ce8, 0x00000000); - nv_mthd(priv, 0xa097, 0x1cf8, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c0c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c1c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c2c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c3c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c4c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c5c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c6c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c7c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c8c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1c9c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1cac, 0x00000000); - nv_mthd(priv, 0xa097, 0x1cbc, 0x00000000); - nv_mthd(priv, 0xa097, 0x1ccc, 0x00000000); - nv_mthd(priv, 0xa097, 0x1cdc, 0x00000000); - nv_mthd(priv, 0xa097, 0x1cec, 0x00000000); - nv_mthd(priv, 0xa097, 0x1cfc, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d00, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d10, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d20, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d30, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d40, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d50, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d60, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d70, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d80, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d90, 0x00000000); - nv_mthd(priv, 0xa097, 0x1da0, 0x00000000); - nv_mthd(priv, 0xa097, 0x1db0, 0x00000000); - nv_mthd(priv, 0xa097, 0x1dc0, 0x00000000); - nv_mthd(priv, 0xa097, 0x1dd0, 0x00000000); - nv_mthd(priv, 0xa097, 0x1de0, 0x00000000); - nv_mthd(priv, 0xa097, 0x1df0, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d04, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d14, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d24, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d34, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d44, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d54, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d64, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d74, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d84, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d94, 0x00000000); - nv_mthd(priv, 0xa097, 0x1da4, 0x00000000); - nv_mthd(priv, 0xa097, 0x1db4, 0x00000000); - nv_mthd(priv, 0xa097, 0x1dc4, 0x00000000); - nv_mthd(priv, 0xa097, 0x1dd4, 0x00000000); - nv_mthd(priv, 0xa097, 0x1de4, 0x00000000); - nv_mthd(priv, 0xa097, 0x1df4, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d08, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d18, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d28, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d38, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d48, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d58, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d68, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d78, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d88, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d98, 0x00000000); - nv_mthd(priv, 0xa097, 0x1da8, 0x00000000); - nv_mthd(priv, 0xa097, 0x1db8, 0x00000000); - nv_mthd(priv, 0xa097, 0x1dc8, 0x00000000); - nv_mthd(priv, 0xa097, 0x1dd8, 0x00000000); - nv_mthd(priv, 0xa097, 0x1de8, 0x00000000); - nv_mthd(priv, 0xa097, 0x1df8, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d0c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d1c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d2c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d3c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d4c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d5c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d6c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d7c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d8c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1d9c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1dac, 0x00000000); - nv_mthd(priv, 0xa097, 0x1dbc, 0x00000000); - nv_mthd(priv, 0xa097, 0x1dcc, 0x00000000); - nv_mthd(priv, 0xa097, 0x1ddc, 0x00000000); - nv_mthd(priv, 0xa097, 0x1dec, 0x00000000); - nv_mthd(priv, 0xa097, 0x1dfc, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f00, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f08, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f10, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f18, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f20, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f28, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f30, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f38, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f40, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f48, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f50, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f58, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f60, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f68, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f70, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f78, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f04, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f0c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f14, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f1c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f24, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f2c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f34, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f3c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f44, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f4c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f54, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f5c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f64, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f6c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f74, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f7c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f80, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f88, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f90, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f98, 0x00000000); - nv_mthd(priv, 0xa097, 0x1fa0, 0x00000000); - nv_mthd(priv, 0xa097, 0x1fa8, 0x00000000); - nv_mthd(priv, 0xa097, 0x1fb0, 0x00000000); - nv_mthd(priv, 0xa097, 0x1fb8, 0x00000000); - nv_mthd(priv, 0xa097, 0x1fc0, 0x00000000); - nv_mthd(priv, 0xa097, 0x1fc8, 0x00000000); - nv_mthd(priv, 0xa097, 0x1fd0, 0x00000000); - nv_mthd(priv, 0xa097, 0x1fd8, 0x00000000); - nv_mthd(priv, 0xa097, 0x1fe0, 0x00000000); - nv_mthd(priv, 0xa097, 0x1fe8, 0x00000000); - nv_mthd(priv, 0xa097, 0x1ff0, 0x00000000); - nv_mthd(priv, 0xa097, 0x1ff8, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f84, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f8c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f94, 0x00000000); - nv_mthd(priv, 0xa097, 0x1f9c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1fa4, 0x00000000); - nv_mthd(priv, 0xa097, 0x1fac, 0x00000000); - nv_mthd(priv, 0xa097, 0x1fb4, 0x00000000); - nv_mthd(priv, 0xa097, 0x1fbc, 0x00000000); - nv_mthd(priv, 0xa097, 0x1fc4, 0x00000000); - nv_mthd(priv, 0xa097, 0x1fcc, 0x00000000); - nv_mthd(priv, 0xa097, 0x1fd4, 0x00000000); - nv_mthd(priv, 0xa097, 0x1fdc, 0x00000000); - nv_mthd(priv, 0xa097, 0x1fe4, 0x00000000); - nv_mthd(priv, 0xa097, 0x1fec, 0x00000000); - nv_mthd(priv, 0xa097, 0x1ff4, 0x00000000); - nv_mthd(priv, 0xa097, 0x1ffc, 0x00000000); - nv_mthd(priv, 0xa097, 0x2000, 0x00000000); - nv_mthd(priv, 0xa097, 0x2040, 0x00000011); - nv_mthd(priv, 0xa097, 0x2080, 0x00000020); - nv_mthd(priv, 0xa097, 0x20c0, 0x00000030); - nv_mthd(priv, 0xa097, 0x2100, 0x00000040); - nv_mthd(priv, 0xa097, 0x2140, 0x00000051); - nv_mthd(priv, 0xa097, 0x200c, 0x00000001); - nv_mthd(priv, 0xa097, 0x204c, 0x00000001); - nv_mthd(priv, 0xa097, 0x208c, 0x00000001); - nv_mthd(priv, 0xa097, 0x20cc, 0x00000001); - nv_mthd(priv, 0xa097, 0x210c, 0x00000001); - nv_mthd(priv, 0xa097, 0x214c, 0x00000001); - nv_mthd(priv, 0xa097, 0x2010, 0x00000000); - nv_mthd(priv, 0xa097, 0x2050, 0x00000000); - nv_mthd(priv, 0xa097, 0x2090, 0x00000001); - nv_mthd(priv, 0xa097, 0x20d0, 0x00000002); - nv_mthd(priv, 0xa097, 0x2110, 0x00000003); - nv_mthd(priv, 0xa097, 0x2150, 0x00000004); - nv_mthd(priv, 0xa097, 0x0380, 0x00000000); - nv_mthd(priv, 0xa097, 0x03a0, 0x00000000); - nv_mthd(priv, 0xa097, 0x03c0, 0x00000000); - nv_mthd(priv, 0xa097, 0x03e0, 0x00000000); - nv_mthd(priv, 0xa097, 0x0384, 0x00000000); - nv_mthd(priv, 0xa097, 0x03a4, 0x00000000); - nv_mthd(priv, 0xa097, 0x03c4, 0x00000000); - nv_mthd(priv, 0xa097, 0x03e4, 0x00000000); - nv_mthd(priv, 0xa097, 0x0388, 0x00000000); - nv_mthd(priv, 0xa097, 0x03a8, 0x00000000); - nv_mthd(priv, 0xa097, 0x03c8, 0x00000000); - nv_mthd(priv, 0xa097, 0x03e8, 0x00000000); - nv_mthd(priv, 0xa097, 0x038c, 0x00000000); - nv_mthd(priv, 0xa097, 0x03ac, 0x00000000); - nv_mthd(priv, 0xa097, 0x03cc, 0x00000000); - nv_mthd(priv, 0xa097, 0x03ec, 0x00000000); - nv_mthd(priv, 0xa097, 0x0700, 0x00000000); - nv_mthd(priv, 0xa097, 0x0710, 0x00000000); - nv_mthd(priv, 0xa097, 0x0720, 0x00000000); - nv_mthd(priv, 0xa097, 0x0730, 0x00000000); - nv_mthd(priv, 0xa097, 0x0704, 0x00000000); - nv_mthd(priv, 0xa097, 0x0714, 0x00000000); - nv_mthd(priv, 0xa097, 0x0724, 0x00000000); - nv_mthd(priv, 0xa097, 0x0734, 0x00000000); - nv_mthd(priv, 0xa097, 0x0708, 0x00000000); - nv_mthd(priv, 0xa097, 0x0718, 0x00000000); - nv_mthd(priv, 0xa097, 0x0728, 0x00000000); - nv_mthd(priv, 0xa097, 0x0738, 0x00000000); - nv_mthd(priv, 0xa097, 0x2800, 0x00000000); - nv_mthd(priv, 0xa097, 0x2804, 0x00000000); - nv_mthd(priv, 0xa097, 0x2808, 0x00000000); - nv_mthd(priv, 0xa097, 0x280c, 0x00000000); - nv_mthd(priv, 0xa097, 0x2810, 0x00000000); - nv_mthd(priv, 0xa097, 0x2814, 0x00000000); - nv_mthd(priv, 0xa097, 0x2818, 0x00000000); - nv_mthd(priv, 0xa097, 0x281c, 0x00000000); - nv_mthd(priv, 0xa097, 0x2820, 0x00000000); - nv_mthd(priv, 0xa097, 0x2824, 0x00000000); - nv_mthd(priv, 0xa097, 0x2828, 0x00000000); - nv_mthd(priv, 0xa097, 0x282c, 0x00000000); - nv_mthd(priv, 0xa097, 0x2830, 0x00000000); - nv_mthd(priv, 0xa097, 0x2834, 0x00000000); - nv_mthd(priv, 0xa097, 0x2838, 0x00000000); - nv_mthd(priv, 0xa097, 0x283c, 0x00000000); - nv_mthd(priv, 0xa097, 0x2840, 0x00000000); - nv_mthd(priv, 0xa097, 0x2844, 0x00000000); - nv_mthd(priv, 0xa097, 0x2848, 0x00000000); - nv_mthd(priv, 0xa097, 0x284c, 0x00000000); - nv_mthd(priv, 0xa097, 0x2850, 0x00000000); - nv_mthd(priv, 0xa097, 0x2854, 0x00000000); - nv_mthd(priv, 0xa097, 0x2858, 0x00000000); - nv_mthd(priv, 0xa097, 0x285c, 0x00000000); - nv_mthd(priv, 0xa097, 0x2860, 0x00000000); - nv_mthd(priv, 0xa097, 0x2864, 0x00000000); - nv_mthd(priv, 0xa097, 0x2868, 0x00000000); - nv_mthd(priv, 0xa097, 0x286c, 0x00000000); - nv_mthd(priv, 0xa097, 0x2870, 0x00000000); - nv_mthd(priv, 0xa097, 0x2874, 0x00000000); - nv_mthd(priv, 0xa097, 0x2878, 0x00000000); - nv_mthd(priv, 0xa097, 0x287c, 0x00000000); - nv_mthd(priv, 0xa097, 0x2880, 0x00000000); - nv_mthd(priv, 0xa097, 0x2884, 0x00000000); - nv_mthd(priv, 0xa097, 0x2888, 0x00000000); - nv_mthd(priv, 0xa097, 0x288c, 0x00000000); - nv_mthd(priv, 0xa097, 0x2890, 0x00000000); - nv_mthd(priv, 0xa097, 0x2894, 0x00000000); - nv_mthd(priv, 0xa097, 0x2898, 0x00000000); - nv_mthd(priv, 0xa097, 0x289c, 0x00000000); - nv_mthd(priv, 0xa097, 0x28a0, 0x00000000); - nv_mthd(priv, 0xa097, 0x28a4, 0x00000000); - nv_mthd(priv, 0xa097, 0x28a8, 0x00000000); - nv_mthd(priv, 0xa097, 0x28ac, 0x00000000); - nv_mthd(priv, 0xa097, 0x28b0, 0x00000000); - nv_mthd(priv, 0xa097, 0x28b4, 0x00000000); - nv_mthd(priv, 0xa097, 0x28b8, 0x00000000); - nv_mthd(priv, 0xa097, 0x28bc, 0x00000000); - nv_mthd(priv, 0xa097, 0x28c0, 0x00000000); - nv_mthd(priv, 0xa097, 0x28c4, 0x00000000); - nv_mthd(priv, 0xa097, 0x28c8, 0x00000000); - nv_mthd(priv, 0xa097, 0x28cc, 0x00000000); - nv_mthd(priv, 0xa097, 0x28d0, 0x00000000); - nv_mthd(priv, 0xa097, 0x28d4, 0x00000000); - nv_mthd(priv, 0xa097, 0x28d8, 0x00000000); - nv_mthd(priv, 0xa097, 0x28dc, 0x00000000); - nv_mthd(priv, 0xa097, 0x28e0, 0x00000000); - nv_mthd(priv, 0xa097, 0x28e4, 0x00000000); - nv_mthd(priv, 0xa097, 0x28e8, 0x00000000); - nv_mthd(priv, 0xa097, 0x28ec, 0x00000000); - nv_mthd(priv, 0xa097, 0x28f0, 0x00000000); - nv_mthd(priv, 0xa097, 0x28f4, 0x00000000); - nv_mthd(priv, 0xa097, 0x28f8, 0x00000000); - nv_mthd(priv, 0xa097, 0x28fc, 0x00000000); - nv_mthd(priv, 0xa097, 0x2900, 0x00000000); - nv_mthd(priv, 0xa097, 0x2904, 0x00000000); - nv_mthd(priv, 0xa097, 0x2908, 0x00000000); - nv_mthd(priv, 0xa097, 0x290c, 0x00000000); - nv_mthd(priv, 0xa097, 0x2910, 0x00000000); - nv_mthd(priv, 0xa097, 0x2914, 0x00000000); - nv_mthd(priv, 0xa097, 0x2918, 0x00000000); - nv_mthd(priv, 0xa097, 0x291c, 0x00000000); - nv_mthd(priv, 0xa097, 0x2920, 0x00000000); - nv_mthd(priv, 0xa097, 0x2924, 0x00000000); - nv_mthd(priv, 0xa097, 0x2928, 0x00000000); - nv_mthd(priv, 0xa097, 0x292c, 0x00000000); - nv_mthd(priv, 0xa097, 0x2930, 0x00000000); - nv_mthd(priv, 0xa097, 0x2934, 0x00000000); - nv_mthd(priv, 0xa097, 0x2938, 0x00000000); - nv_mthd(priv, 0xa097, 0x293c, 0x00000000); - nv_mthd(priv, 0xa097, 0x2940, 0x00000000); - nv_mthd(priv, 0xa097, 0x2944, 0x00000000); - nv_mthd(priv, 0xa097, 0x2948, 0x00000000); - nv_mthd(priv, 0xa097, 0x294c, 0x00000000); - nv_mthd(priv, 0xa097, 0x2950, 0x00000000); - nv_mthd(priv, 0xa097, 0x2954, 0x00000000); - nv_mthd(priv, 0xa097, 0x2958, 0x00000000); - nv_mthd(priv, 0xa097, 0x295c, 0x00000000); - nv_mthd(priv, 0xa097, 0x2960, 0x00000000); - nv_mthd(priv, 0xa097, 0x2964, 0x00000000); - nv_mthd(priv, 0xa097, 0x2968, 0x00000000); - nv_mthd(priv, 0xa097, 0x296c, 0x00000000); - nv_mthd(priv, 0xa097, 0x2970, 0x00000000); - nv_mthd(priv, 0xa097, 0x2974, 0x00000000); - nv_mthd(priv, 0xa097, 0x2978, 0x00000000); - nv_mthd(priv, 0xa097, 0x297c, 0x00000000); - nv_mthd(priv, 0xa097, 0x2980, 0x00000000); - nv_mthd(priv, 0xa097, 0x2984, 0x00000000); - nv_mthd(priv, 0xa097, 0x2988, 0x00000000); - nv_mthd(priv, 0xa097, 0x298c, 0x00000000); - nv_mthd(priv, 0xa097, 0x2990, 0x00000000); - nv_mthd(priv, 0xa097, 0x2994, 0x00000000); - nv_mthd(priv, 0xa097, 0x2998, 0x00000000); - nv_mthd(priv, 0xa097, 0x299c, 0x00000000); - nv_mthd(priv, 0xa097, 0x29a0, 0x00000000); - nv_mthd(priv, 0xa097, 0x29a4, 0x00000000); - nv_mthd(priv, 0xa097, 0x29a8, 0x00000000); - nv_mthd(priv, 0xa097, 0x29ac, 0x00000000); - nv_mthd(priv, 0xa097, 0x29b0, 0x00000000); - nv_mthd(priv, 0xa097, 0x29b4, 0x00000000); - nv_mthd(priv, 0xa097, 0x29b8, 0x00000000); - nv_mthd(priv, 0xa097, 0x29bc, 0x00000000); - nv_mthd(priv, 0xa097, 0x29c0, 0x00000000); - nv_mthd(priv, 0xa097, 0x29c4, 0x00000000); - nv_mthd(priv, 0xa097, 0x29c8, 0x00000000); - nv_mthd(priv, 0xa097, 0x29cc, 0x00000000); - nv_mthd(priv, 0xa097, 0x29d0, 0x00000000); - nv_mthd(priv, 0xa097, 0x29d4, 0x00000000); - nv_mthd(priv, 0xa097, 0x29d8, 0x00000000); - nv_mthd(priv, 0xa097, 0x29dc, 0x00000000); - nv_mthd(priv, 0xa097, 0x29e0, 0x00000000); - nv_mthd(priv, 0xa097, 0x29e4, 0x00000000); - nv_mthd(priv, 0xa097, 0x29e8, 0x00000000); - nv_mthd(priv, 0xa097, 0x29ec, 0x00000000); - nv_mthd(priv, 0xa097, 0x29f0, 0x00000000); - nv_mthd(priv, 0xa097, 0x29f4, 0x00000000); - nv_mthd(priv, 0xa097, 0x29f8, 0x00000000); - nv_mthd(priv, 0xa097, 0x29fc, 0x00000000); - nv_mthd(priv, 0xa097, 0x0a00, 0x00000000); - nv_mthd(priv, 0xa097, 0x0a20, 0x00000000); - nv_mthd(priv, 0xa097, 0x0a40, 0x00000000); - nv_mthd(priv, 0xa097, 0x0a60, 0x00000000); - nv_mthd(priv, 0xa097, 0x0a80, 0x00000000); - nv_mthd(priv, 0xa097, 0x0aa0, 0x00000000); - nv_mthd(priv, 0xa097, 0x0ac0, 0x00000000); - nv_mthd(priv, 0xa097, 0x0ae0, 0x00000000); - nv_mthd(priv, 0xa097, 0x0b00, 0x00000000); - nv_mthd(priv, 0xa097, 0x0b20, 0x00000000); - nv_mthd(priv, 0xa097, 0x0b40, 0x00000000); - nv_mthd(priv, 0xa097, 0x0b60, 0x00000000); - nv_mthd(priv, 0xa097, 0x0b80, 0x00000000); - nv_mthd(priv, 0xa097, 0x0ba0, 0x00000000); - nv_mthd(priv, 0xa097, 0x0bc0, 0x00000000); - nv_mthd(priv, 0xa097, 0x0be0, 0x00000000); - nv_mthd(priv, 0xa097, 0x0a04, 0x00000000); - nv_mthd(priv, 0xa097, 0x0a24, 0x00000000); - nv_mthd(priv, 0xa097, 0x0a44, 0x00000000); - nv_mthd(priv, 0xa097, 0x0a64, 0x00000000); - nv_mthd(priv, 0xa097, 0x0a84, 0x00000000); - nv_mthd(priv, 0xa097, 0x0aa4, 0x00000000); - nv_mthd(priv, 0xa097, 0x0ac4, 0x00000000); - nv_mthd(priv, 0xa097, 0x0ae4, 0x00000000); - nv_mthd(priv, 0xa097, 0x0b04, 0x00000000); - nv_mthd(priv, 0xa097, 0x0b24, 0x00000000); - nv_mthd(priv, 0xa097, 0x0b44, 0x00000000); - nv_mthd(priv, 0xa097, 0x0b64, 0x00000000); - nv_mthd(priv, 0xa097, 0x0b84, 0x00000000); - nv_mthd(priv, 0xa097, 0x0ba4, 0x00000000); - nv_mthd(priv, 0xa097, 0x0bc4, 0x00000000); - nv_mthd(priv, 0xa097, 0x0be4, 0x00000000); - nv_mthd(priv, 0xa097, 0x0a08, 0x00000000); - nv_mthd(priv, 0xa097, 0x0a28, 0x00000000); - nv_mthd(priv, 0xa097, 0x0a48, 0x00000000); - nv_mthd(priv, 0xa097, 0x0a68, 0x00000000); - nv_mthd(priv, 0xa097, 0x0a88, 0x00000000); - nv_mthd(priv, 0xa097, 0x0aa8, 0x00000000); - nv_mthd(priv, 0xa097, 0x0ac8, 0x00000000); - nv_mthd(priv, 0xa097, 0x0ae8, 0x00000000); - nv_mthd(priv, 0xa097, 0x0b08, 0x00000000); - nv_mthd(priv, 0xa097, 0x0b28, 0x00000000); - nv_mthd(priv, 0xa097, 0x0b48, 0x00000000); - nv_mthd(priv, 0xa097, 0x0b68, 0x00000000); - nv_mthd(priv, 0xa097, 0x0b88, 0x00000000); - nv_mthd(priv, 0xa097, 0x0ba8, 0x00000000); - nv_mthd(priv, 0xa097, 0x0bc8, 0x00000000); - nv_mthd(priv, 0xa097, 0x0be8, 0x00000000); - nv_mthd(priv, 0xa097, 0x0a0c, 0x00000000); - nv_mthd(priv, 0xa097, 0x0a2c, 0x00000000); - nv_mthd(priv, 0xa097, 0x0a4c, 0x00000000); - nv_mthd(priv, 0xa097, 0x0a6c, 0x00000000); - nv_mthd(priv, 0xa097, 0x0a8c, 0x00000000); - nv_mthd(priv, 0xa097, 0x0aac, 0x00000000); - nv_mthd(priv, 0xa097, 0x0acc, 0x00000000); - nv_mthd(priv, 0xa097, 0x0aec, 0x00000000); - nv_mthd(priv, 0xa097, 0x0b0c, 0x00000000); - nv_mthd(priv, 0xa097, 0x0b2c, 0x00000000); - nv_mthd(priv, 0xa097, 0x0b4c, 0x00000000); - nv_mthd(priv, 0xa097, 0x0b6c, 0x00000000); - nv_mthd(priv, 0xa097, 0x0b8c, 0x00000000); - nv_mthd(priv, 0xa097, 0x0bac, 0x00000000); - nv_mthd(priv, 0xa097, 0x0bcc, 0x00000000); - nv_mthd(priv, 0xa097, 0x0bec, 0x00000000); - nv_mthd(priv, 0xa097, 0x0a10, 0x00000000); - nv_mthd(priv, 0xa097, 0x0a30, 0x00000000); - nv_mthd(priv, 0xa097, 0x0a50, 0x00000000); - nv_mthd(priv, 0xa097, 0x0a70, 0x00000000); - nv_mthd(priv, 0xa097, 0x0a90, 0x00000000); - nv_mthd(priv, 0xa097, 0x0ab0, 0x00000000); - nv_mthd(priv, 0xa097, 0x0ad0, 0x00000000); - nv_mthd(priv, 0xa097, 0x0af0, 0x00000000); - nv_mthd(priv, 0xa097, 0x0b10, 0x00000000); - nv_mthd(priv, 0xa097, 0x0b30, 0x00000000); - nv_mthd(priv, 0xa097, 0x0b50, 0x00000000); - nv_mthd(priv, 0xa097, 0x0b70, 0x00000000); - nv_mthd(priv, 0xa097, 0x0b90, 0x00000000); - nv_mthd(priv, 0xa097, 0x0bb0, 0x00000000); - nv_mthd(priv, 0xa097, 0x0bd0, 0x00000000); - nv_mthd(priv, 0xa097, 0x0bf0, 0x00000000); - nv_mthd(priv, 0xa097, 0x0a14, 0x00000000); - nv_mthd(priv, 0xa097, 0x0a34, 0x00000000); - nv_mthd(priv, 0xa097, 0x0a54, 0x00000000); - nv_mthd(priv, 0xa097, 0x0a74, 0x00000000); - nv_mthd(priv, 0xa097, 0x0a94, 0x00000000); - nv_mthd(priv, 0xa097, 0x0ab4, 0x00000000); - nv_mthd(priv, 0xa097, 0x0ad4, 0x00000000); - nv_mthd(priv, 0xa097, 0x0af4, 0x00000000); - nv_mthd(priv, 0xa097, 0x0b14, 0x00000000); - nv_mthd(priv, 0xa097, 0x0b34, 0x00000000); - nv_mthd(priv, 0xa097, 0x0b54, 0x00000000); - nv_mthd(priv, 0xa097, 0x0b74, 0x00000000); - nv_mthd(priv, 0xa097, 0x0b94, 0x00000000); - nv_mthd(priv, 0xa097, 0x0bb4, 0x00000000); - nv_mthd(priv, 0xa097, 0x0bd4, 0x00000000); - nv_mthd(priv, 0xa097, 0x0bf4, 0x00000000); - nv_mthd(priv, 0xa097, 0x0c00, 0x00000000); - nv_mthd(priv, 0xa097, 0x0c10, 0x00000000); - nv_mthd(priv, 0xa097, 0x0c20, 0x00000000); - nv_mthd(priv, 0xa097, 0x0c30, 0x00000000); - nv_mthd(priv, 0xa097, 0x0c40, 0x00000000); - nv_mthd(priv, 0xa097, 0x0c50, 0x00000000); - nv_mthd(priv, 0xa097, 0x0c60, 0x00000000); - nv_mthd(priv, 0xa097, 0x0c70, 0x00000000); - nv_mthd(priv, 0xa097, 0x0c80, 0x00000000); - nv_mthd(priv, 0xa097, 0x0c90, 0x00000000); - nv_mthd(priv, 0xa097, 0x0ca0, 0x00000000); - nv_mthd(priv, 0xa097, 0x0cb0, 0x00000000); - nv_mthd(priv, 0xa097, 0x0cc0, 0x00000000); - nv_mthd(priv, 0xa097, 0x0cd0, 0x00000000); - nv_mthd(priv, 0xa097, 0x0ce0, 0x00000000); - nv_mthd(priv, 0xa097, 0x0cf0, 0x00000000); - nv_mthd(priv, 0xa097, 0x0c04, 0x00000000); - nv_mthd(priv, 0xa097, 0x0c14, 0x00000000); - nv_mthd(priv, 0xa097, 0x0c24, 0x00000000); - nv_mthd(priv, 0xa097, 0x0c34, 0x00000000); - nv_mthd(priv, 0xa097, 0x0c44, 0x00000000); - nv_mthd(priv, 0xa097, 0x0c54, 0x00000000); - nv_mthd(priv, 0xa097, 0x0c64, 0x00000000); - nv_mthd(priv, 0xa097, 0x0c74, 0x00000000); - nv_mthd(priv, 0xa097, 0x0c84, 0x00000000); - nv_mthd(priv, 0xa097, 0x0c94, 0x00000000); - nv_mthd(priv, 0xa097, 0x0ca4, 0x00000000); - nv_mthd(priv, 0xa097, 0x0cb4, 0x00000000); - nv_mthd(priv, 0xa097, 0x0cc4, 0x00000000); - nv_mthd(priv, 0xa097, 0x0cd4, 0x00000000); - nv_mthd(priv, 0xa097, 0x0ce4, 0x00000000); - nv_mthd(priv, 0xa097, 0x0cf4, 0x00000000); - nv_mthd(priv, 0xa097, 0x0c08, 0x00000000); - nv_mthd(priv, 0xa097, 0x0c18, 0x00000000); - nv_mthd(priv, 0xa097, 0x0c28, 0x00000000); - nv_mthd(priv, 0xa097, 0x0c38, 0x00000000); - nv_mthd(priv, 0xa097, 0x0c48, 0x00000000); - nv_mthd(priv, 0xa097, 0x0c58, 0x00000000); - nv_mthd(priv, 0xa097, 0x0c68, 0x00000000); - nv_mthd(priv, 0xa097, 0x0c78, 0x00000000); - nv_mthd(priv, 0xa097, 0x0c88, 0x00000000); - nv_mthd(priv, 0xa097, 0x0c98, 0x00000000); - nv_mthd(priv, 0xa097, 0x0ca8, 0x00000000); - nv_mthd(priv, 0xa097, 0x0cb8, 0x00000000); - nv_mthd(priv, 0xa097, 0x0cc8, 0x00000000); - nv_mthd(priv, 0xa097, 0x0cd8, 0x00000000); - nv_mthd(priv, 0xa097, 0x0ce8, 0x00000000); - nv_mthd(priv, 0xa097, 0x0cf8, 0x00000000); - nv_mthd(priv, 0xa097, 0x0c0c, 0x3f800000); - nv_mthd(priv, 0xa097, 0x0c1c, 0x3f800000); - nv_mthd(priv, 0xa097, 0x0c2c, 0x3f800000); - nv_mthd(priv, 0xa097, 0x0c3c, 0x3f800000); - nv_mthd(priv, 0xa097, 0x0c4c, 0x3f800000); - nv_mthd(priv, 0xa097, 0x0c5c, 0x3f800000); - nv_mthd(priv, 0xa097, 0x0c6c, 0x3f800000); - nv_mthd(priv, 0xa097, 0x0c7c, 0x3f800000); - nv_mthd(priv, 0xa097, 0x0c8c, 0x3f800000); - nv_mthd(priv, 0xa097, 0x0c9c, 0x3f800000); - nv_mthd(priv, 0xa097, 0x0cac, 0x3f800000); - nv_mthd(priv, 0xa097, 0x0cbc, 0x3f800000); - nv_mthd(priv, 0xa097, 0x0ccc, 0x3f800000); - nv_mthd(priv, 0xa097, 0x0cdc, 0x3f800000); - nv_mthd(priv, 0xa097, 0x0cec, 0x3f800000); - nv_mthd(priv, 0xa097, 0x0cfc, 0x3f800000); - nv_mthd(priv, 0xa097, 0x0d00, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0d08, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0d10, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0d18, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0d20, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0d28, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0d30, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0d38, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0d04, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0d0c, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0d14, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0d1c, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0d24, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0d2c, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0d34, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0d3c, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0e00, 0x00000000); - nv_mthd(priv, 0xa097, 0x0e10, 0x00000000); - nv_mthd(priv, 0xa097, 0x0e20, 0x00000000); - nv_mthd(priv, 0xa097, 0x0e30, 0x00000000); - nv_mthd(priv, 0xa097, 0x0e40, 0x00000000); - nv_mthd(priv, 0xa097, 0x0e50, 0x00000000); - nv_mthd(priv, 0xa097, 0x0e60, 0x00000000); - nv_mthd(priv, 0xa097, 0x0e70, 0x00000000); - nv_mthd(priv, 0xa097, 0x0e80, 0x00000000); - nv_mthd(priv, 0xa097, 0x0e90, 0x00000000); - nv_mthd(priv, 0xa097, 0x0ea0, 0x00000000); - nv_mthd(priv, 0xa097, 0x0eb0, 0x00000000); - nv_mthd(priv, 0xa097, 0x0ec0, 0x00000000); - nv_mthd(priv, 0xa097, 0x0ed0, 0x00000000); - nv_mthd(priv, 0xa097, 0x0ee0, 0x00000000); - nv_mthd(priv, 0xa097, 0x0ef0, 0x00000000); - nv_mthd(priv, 0xa097, 0x0e04, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0e14, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0e24, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0e34, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0e44, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0e54, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0e64, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0e74, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0e84, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0e94, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0ea4, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0eb4, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0ec4, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0ed4, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0ee4, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0ef4, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0e08, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0e18, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0e28, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0e38, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0e48, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0e58, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0e68, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0e78, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0e88, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0e98, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0ea8, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0eb8, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0ec8, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0ed8, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0ee8, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0ef8, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0d40, 0x00000000); - nv_mthd(priv, 0xa097, 0x0d48, 0x00000000); - nv_mthd(priv, 0xa097, 0x0d50, 0x00000000); - nv_mthd(priv, 0xa097, 0x0d58, 0x00000000); - nv_mthd(priv, 0xa097, 0x0d44, 0x00000000); - nv_mthd(priv, 0xa097, 0x0d4c, 0x00000000); - nv_mthd(priv, 0xa097, 0x0d54, 0x00000000); - nv_mthd(priv, 0xa097, 0x0d5c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1e00, 0x00000001); - nv_mthd(priv, 0xa097, 0x1e20, 0x00000001); - nv_mthd(priv, 0xa097, 0x1e40, 0x00000001); - nv_mthd(priv, 0xa097, 0x1e60, 0x00000001); - nv_mthd(priv, 0xa097, 0x1e80, 0x00000001); - nv_mthd(priv, 0xa097, 0x1ea0, 0x00000001); - nv_mthd(priv, 0xa097, 0x1ec0, 0x00000001); - nv_mthd(priv, 0xa097, 0x1ee0, 0x00000001); - nv_mthd(priv, 0xa097, 0x1e04, 0x00000001); - nv_mthd(priv, 0xa097, 0x1e24, 0x00000001); - nv_mthd(priv, 0xa097, 0x1e44, 0x00000001); - nv_mthd(priv, 0xa097, 0x1e64, 0x00000001); - nv_mthd(priv, 0xa097, 0x1e84, 0x00000001); - nv_mthd(priv, 0xa097, 0x1ea4, 0x00000001); - nv_mthd(priv, 0xa097, 0x1ec4, 0x00000001); - nv_mthd(priv, 0xa097, 0x1ee4, 0x00000001); - nv_mthd(priv, 0xa097, 0x1e08, 0x00000002); - nv_mthd(priv, 0xa097, 0x1e28, 0x00000002); - nv_mthd(priv, 0xa097, 0x1e48, 0x00000002); - nv_mthd(priv, 0xa097, 0x1e68, 0x00000002); - nv_mthd(priv, 0xa097, 0x1e88, 0x00000002); - nv_mthd(priv, 0xa097, 0x1ea8, 0x00000002); - nv_mthd(priv, 0xa097, 0x1ec8, 0x00000002); - nv_mthd(priv, 0xa097, 0x1ee8, 0x00000002); - nv_mthd(priv, 0xa097, 0x1e0c, 0x00000001); - nv_mthd(priv, 0xa097, 0x1e2c, 0x00000001); - nv_mthd(priv, 0xa097, 0x1e4c, 0x00000001); - nv_mthd(priv, 0xa097, 0x1e6c, 0x00000001); - nv_mthd(priv, 0xa097, 0x1e8c, 0x00000001); - nv_mthd(priv, 0xa097, 0x1eac, 0x00000001); - nv_mthd(priv, 0xa097, 0x1ecc, 0x00000001); - nv_mthd(priv, 0xa097, 0x1eec, 0x00000001); - nv_mthd(priv, 0xa097, 0x1e10, 0x00000001); - nv_mthd(priv, 0xa097, 0x1e30, 0x00000001); - nv_mthd(priv, 0xa097, 0x1e50, 0x00000001); - nv_mthd(priv, 0xa097, 0x1e70, 0x00000001); - nv_mthd(priv, 0xa097, 0x1e90, 0x00000001); - nv_mthd(priv, 0xa097, 0x1eb0, 0x00000001); - nv_mthd(priv, 0xa097, 0x1ed0, 0x00000001); - nv_mthd(priv, 0xa097, 0x1ef0, 0x00000001); - nv_mthd(priv, 0xa097, 0x1e14, 0x00000002); - nv_mthd(priv, 0xa097, 0x1e34, 0x00000002); - nv_mthd(priv, 0xa097, 0x1e54, 0x00000002); - nv_mthd(priv, 0xa097, 0x1e74, 0x00000002); - nv_mthd(priv, 0xa097, 0x1e94, 0x00000002); - nv_mthd(priv, 0xa097, 0x1eb4, 0x00000002); - nv_mthd(priv, 0xa097, 0x1ed4, 0x00000002); - nv_mthd(priv, 0xa097, 0x1ef4, 0x00000002); - nv_mthd(priv, 0xa097, 0x1e18, 0x00000001); - nv_mthd(priv, 0xa097, 0x1e38, 0x00000001); - nv_mthd(priv, 0xa097, 0x1e58, 0x00000001); - nv_mthd(priv, 0xa097, 0x1e78, 0x00000001); - nv_mthd(priv, 0xa097, 0x1e98, 0x00000001); - nv_mthd(priv, 0xa097, 0x1eb8, 0x00000001); - nv_mthd(priv, 0xa097, 0x1ed8, 0x00000001); - nv_mthd(priv, 0xa097, 0x1ef8, 0x00000001); - nv_mthd(priv, 0xa097, 0x3400, 0x00000000); - nv_mthd(priv, 0xa097, 0x3404, 0x00000000); - nv_mthd(priv, 0xa097, 0x3408, 0x00000000); - nv_mthd(priv, 0xa097, 0x340c, 0x00000000); - nv_mthd(priv, 0xa097, 0x3410, 0x00000000); - nv_mthd(priv, 0xa097, 0x3414, 0x00000000); - nv_mthd(priv, 0xa097, 0x3418, 0x00000000); - nv_mthd(priv, 0xa097, 0x341c, 0x00000000); - nv_mthd(priv, 0xa097, 0x3420, 0x00000000); - nv_mthd(priv, 0xa097, 0x3424, 0x00000000); - nv_mthd(priv, 0xa097, 0x3428, 0x00000000); - nv_mthd(priv, 0xa097, 0x342c, 0x00000000); - nv_mthd(priv, 0xa097, 0x3430, 0x00000000); - nv_mthd(priv, 0xa097, 0x3434, 0x00000000); - nv_mthd(priv, 0xa097, 0x3438, 0x00000000); - nv_mthd(priv, 0xa097, 0x343c, 0x00000000); - nv_mthd(priv, 0xa097, 0x3440, 0x00000000); - nv_mthd(priv, 0xa097, 0x3444, 0x00000000); - nv_mthd(priv, 0xa097, 0x3448, 0x00000000); - nv_mthd(priv, 0xa097, 0x344c, 0x00000000); - nv_mthd(priv, 0xa097, 0x3450, 0x00000000); - nv_mthd(priv, 0xa097, 0x3454, 0x00000000); - nv_mthd(priv, 0xa097, 0x3458, 0x00000000); - nv_mthd(priv, 0xa097, 0x345c, 0x00000000); - nv_mthd(priv, 0xa097, 0x3460, 0x00000000); - nv_mthd(priv, 0xa097, 0x3464, 0x00000000); - nv_mthd(priv, 0xa097, 0x3468, 0x00000000); - nv_mthd(priv, 0xa097, 0x346c, 0x00000000); - nv_mthd(priv, 0xa097, 0x3470, 0x00000000); - nv_mthd(priv, 0xa097, 0x3474, 0x00000000); - nv_mthd(priv, 0xa097, 0x3478, 0x00000000); - nv_mthd(priv, 0xa097, 0x347c, 0x00000000); - nv_mthd(priv, 0xa097, 0x3480, 0x00000000); - nv_mthd(priv, 0xa097, 0x3484, 0x00000000); - nv_mthd(priv, 0xa097, 0x3488, 0x00000000); - nv_mthd(priv, 0xa097, 0x348c, 0x00000000); - nv_mthd(priv, 0xa097, 0x3490, 0x00000000); - nv_mthd(priv, 0xa097, 0x3494, 0x00000000); - nv_mthd(priv, 0xa097, 0x3498, 0x00000000); - nv_mthd(priv, 0xa097, 0x349c, 0x00000000); - nv_mthd(priv, 0xa097, 0x34a0, 0x00000000); - nv_mthd(priv, 0xa097, 0x34a4, 0x00000000); - nv_mthd(priv, 0xa097, 0x34a8, 0x00000000); - nv_mthd(priv, 0xa097, 0x34ac, 0x00000000); - nv_mthd(priv, 0xa097, 0x34b0, 0x00000000); - nv_mthd(priv, 0xa097, 0x34b4, 0x00000000); - nv_mthd(priv, 0xa097, 0x34b8, 0x00000000); - nv_mthd(priv, 0xa097, 0x34bc, 0x00000000); - nv_mthd(priv, 0xa097, 0x34c0, 0x00000000); - nv_mthd(priv, 0xa097, 0x34c4, 0x00000000); - nv_mthd(priv, 0xa097, 0x34c8, 0x00000000); - nv_mthd(priv, 0xa097, 0x34cc, 0x00000000); - nv_mthd(priv, 0xa097, 0x34d0, 0x00000000); - nv_mthd(priv, 0xa097, 0x34d4, 0x00000000); - nv_mthd(priv, 0xa097, 0x34d8, 0x00000000); - nv_mthd(priv, 0xa097, 0x34dc, 0x00000000); - nv_mthd(priv, 0xa097, 0x34e0, 0x00000000); - nv_mthd(priv, 0xa097, 0x34e4, 0x00000000); - nv_mthd(priv, 0xa097, 0x34e8, 0x00000000); - nv_mthd(priv, 0xa097, 0x34ec, 0x00000000); - nv_mthd(priv, 0xa097, 0x34f0, 0x00000000); - nv_mthd(priv, 0xa097, 0x34f4, 0x00000000); - nv_mthd(priv, 0xa097, 0x34f8, 0x00000000); - nv_mthd(priv, 0xa097, 0x34fc, 0x00000000); - nv_mthd(priv, 0xa097, 0x3500, 0x00000000); - nv_mthd(priv, 0xa097, 0x3504, 0x00000000); - nv_mthd(priv, 0xa097, 0x3508, 0x00000000); - nv_mthd(priv, 0xa097, 0x350c, 0x00000000); - nv_mthd(priv, 0xa097, 0x3510, 0x00000000); - nv_mthd(priv, 0xa097, 0x3514, 0x00000000); - nv_mthd(priv, 0xa097, 0x3518, 0x00000000); - nv_mthd(priv, 0xa097, 0x351c, 0x00000000); - nv_mthd(priv, 0xa097, 0x3520, 0x00000000); - nv_mthd(priv, 0xa097, 0x3524, 0x00000000); - nv_mthd(priv, 0xa097, 0x3528, 0x00000000); - nv_mthd(priv, 0xa097, 0x352c, 0x00000000); - nv_mthd(priv, 0xa097, 0x3530, 0x00000000); - nv_mthd(priv, 0xa097, 0x3534, 0x00000000); - nv_mthd(priv, 0xa097, 0x3538, 0x00000000); - nv_mthd(priv, 0xa097, 0x353c, 0x00000000); - nv_mthd(priv, 0xa097, 0x3540, 0x00000000); - nv_mthd(priv, 0xa097, 0x3544, 0x00000000); - nv_mthd(priv, 0xa097, 0x3548, 0x00000000); - nv_mthd(priv, 0xa097, 0x354c, 0x00000000); - nv_mthd(priv, 0xa097, 0x3550, 0x00000000); - nv_mthd(priv, 0xa097, 0x3554, 0x00000000); - nv_mthd(priv, 0xa097, 0x3558, 0x00000000); - nv_mthd(priv, 0xa097, 0x355c, 0x00000000); - nv_mthd(priv, 0xa097, 0x3560, 0x00000000); - nv_mthd(priv, 0xa097, 0x3564, 0x00000000); - nv_mthd(priv, 0xa097, 0x3568, 0x00000000); - nv_mthd(priv, 0xa097, 0x356c, 0x00000000); - nv_mthd(priv, 0xa097, 0x3570, 0x00000000); - nv_mthd(priv, 0xa097, 0x3574, 0x00000000); - nv_mthd(priv, 0xa097, 0x3578, 0x00000000); - nv_mthd(priv, 0xa097, 0x357c, 0x00000000); - nv_mthd(priv, 0xa097, 0x3580, 0x00000000); - nv_mthd(priv, 0xa097, 0x3584, 0x00000000); - nv_mthd(priv, 0xa097, 0x3588, 0x00000000); - nv_mthd(priv, 0xa097, 0x358c, 0x00000000); - nv_mthd(priv, 0xa097, 0x3590, 0x00000000); - nv_mthd(priv, 0xa097, 0x3594, 0x00000000); - nv_mthd(priv, 0xa097, 0x3598, 0x00000000); - nv_mthd(priv, 0xa097, 0x359c, 0x00000000); - nv_mthd(priv, 0xa097, 0x35a0, 0x00000000); - nv_mthd(priv, 0xa097, 0x35a4, 0x00000000); - nv_mthd(priv, 0xa097, 0x35a8, 0x00000000); - nv_mthd(priv, 0xa097, 0x35ac, 0x00000000); - nv_mthd(priv, 0xa097, 0x35b0, 0x00000000); - nv_mthd(priv, 0xa097, 0x35b4, 0x00000000); - nv_mthd(priv, 0xa097, 0x35b8, 0x00000000); - nv_mthd(priv, 0xa097, 0x35bc, 0x00000000); - nv_mthd(priv, 0xa097, 0x35c0, 0x00000000); - nv_mthd(priv, 0xa097, 0x35c4, 0x00000000); - nv_mthd(priv, 0xa097, 0x35c8, 0x00000000); - nv_mthd(priv, 0xa097, 0x35cc, 0x00000000); - nv_mthd(priv, 0xa097, 0x35d0, 0x00000000); - nv_mthd(priv, 0xa097, 0x35d4, 0x00000000); - nv_mthd(priv, 0xa097, 0x35d8, 0x00000000); - nv_mthd(priv, 0xa097, 0x35dc, 0x00000000); - nv_mthd(priv, 0xa097, 0x35e0, 0x00000000); - nv_mthd(priv, 0xa097, 0x35e4, 0x00000000); - nv_mthd(priv, 0xa097, 0x35e8, 0x00000000); - nv_mthd(priv, 0xa097, 0x35ec, 0x00000000); - nv_mthd(priv, 0xa097, 0x35f0, 0x00000000); - nv_mthd(priv, 0xa097, 0x35f4, 0x00000000); - nv_mthd(priv, 0xa097, 0x35f8, 0x00000000); - nv_mthd(priv, 0xa097, 0x35fc, 0x00000000); - nv_mthd(priv, 0xa097, 0x030c, 0x00000001); - nv_mthd(priv, 0xa097, 0x1944, 0x00000000); - nv_mthd(priv, 0xa097, 0x1514, 0x00000000); - nv_mthd(priv, 0xa097, 0x0d68, 0x0000ffff); - nv_mthd(priv, 0xa097, 0x121c, 0x0fac6881); - nv_mthd(priv, 0xa097, 0x0fac, 0x00000001); - nv_mthd(priv, 0xa097, 0x1538, 0x00000001); - nv_mthd(priv, 0xa097, 0x0fe0, 0x00000000); - nv_mthd(priv, 0xa097, 0x0fe4, 0x00000000); - nv_mthd(priv, 0xa097, 0x0fe8, 0x00000014); - nv_mthd(priv, 0xa097, 0x0fec, 0x00000040); - nv_mthd(priv, 0xa097, 0x0ff0, 0x00000000); - nv_mthd(priv, 0xa097, 0x179c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1228, 0x00000400); - nv_mthd(priv, 0xa097, 0x122c, 0x00000300); - nv_mthd(priv, 0xa097, 0x1230, 0x00010001); - nv_mthd(priv, 0xa097, 0x07f8, 0x00000000); - nv_mthd(priv, 0xa097, 0x15b4, 0x00000001); - nv_mthd(priv, 0xa097, 0x15cc, 0x00000000); - nv_mthd(priv, 0xa097, 0x1534, 0x00000000); - nv_mthd(priv, 0xa097, 0x0fb0, 0x00000000); - nv_mthd(priv, 0xa097, 0x15d0, 0x00000000); - nv_mthd(priv, 0xa097, 0x153c, 0x00000000); - nv_mthd(priv, 0xa097, 0x16b4, 0x00000003); - nv_mthd(priv, 0xa097, 0x0fbc, 0x0000ffff); - nv_mthd(priv, 0xa097, 0x0fc0, 0x0000ffff); - nv_mthd(priv, 0xa097, 0x0fc4, 0x0000ffff); - nv_mthd(priv, 0xa097, 0x0fc8, 0x0000ffff); - nv_mthd(priv, 0xa097, 0x0df8, 0x00000000); - nv_mthd(priv, 0xa097, 0x0dfc, 0x00000000); - nv_mthd(priv, 0xa097, 0x1948, 0x00000000); - nv_mthd(priv, 0xa097, 0x1970, 0x00000001); - nv_mthd(priv, 0xa097, 0x161c, 0x000009f0); - nv_mthd(priv, 0xa097, 0x0dcc, 0x00000010); - nv_mthd(priv, 0xa097, 0x163c, 0x00000000); - nv_mthd(priv, 0xa097, 0x15e4, 0x00000000); - nv_mthd(priv, 0xa097, 0x1160, 0x25e00040); - nv_mthd(priv, 0xa097, 0x1164, 0x25e00040); - nv_mthd(priv, 0xa097, 0x1168, 0x25e00040); - nv_mthd(priv, 0xa097, 0x116c, 0x25e00040); - nv_mthd(priv, 0xa097, 0x1170, 0x25e00040); - nv_mthd(priv, 0xa097, 0x1174, 0x25e00040); - nv_mthd(priv, 0xa097, 0x1178, 0x25e00040); - nv_mthd(priv, 0xa097, 0x117c, 0x25e00040); - nv_mthd(priv, 0xa097, 0x1180, 0x25e00040); - nv_mthd(priv, 0xa097, 0x1184, 0x25e00040); - nv_mthd(priv, 0xa097, 0x1188, 0x25e00040); - nv_mthd(priv, 0xa097, 0x118c, 0x25e00040); - nv_mthd(priv, 0xa097, 0x1190, 0x25e00040); - nv_mthd(priv, 0xa097, 0x1194, 0x25e00040); - nv_mthd(priv, 0xa097, 0x1198, 0x25e00040); - nv_mthd(priv, 0xa097, 0x119c, 0x25e00040); - nv_mthd(priv, 0xa097, 0x11a0, 0x25e00040); - nv_mthd(priv, 0xa097, 0x11a4, 0x25e00040); - nv_mthd(priv, 0xa097, 0x11a8, 0x25e00040); - nv_mthd(priv, 0xa097, 0x11ac, 0x25e00040); - nv_mthd(priv, 0xa097, 0x11b0, 0x25e00040); - nv_mthd(priv, 0xa097, 0x11b4, 0x25e00040); - nv_mthd(priv, 0xa097, 0x11b8, 0x25e00040); - nv_mthd(priv, 0xa097, 0x11bc, 0x25e00040); - nv_mthd(priv, 0xa097, 0x11c0, 0x25e00040); - nv_mthd(priv, 0xa097, 0x11c4, 0x25e00040); - nv_mthd(priv, 0xa097, 0x11c8, 0x25e00040); - nv_mthd(priv, 0xa097, 0x11cc, 0x25e00040); - nv_mthd(priv, 0xa097, 0x11d0, 0x25e00040); - nv_mthd(priv, 0xa097, 0x11d4, 0x25e00040); - nv_mthd(priv, 0xa097, 0x11d8, 0x25e00040); - nv_mthd(priv, 0xa097, 0x11dc, 0x25e00040); - nv_mthd(priv, 0xa097, 0x1880, 0x00000000); - nv_mthd(priv, 0xa097, 0x1884, 0x00000000); - nv_mthd(priv, 0xa097, 0x1888, 0x00000000); - nv_mthd(priv, 0xa097, 0x188c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1890, 0x00000000); - nv_mthd(priv, 0xa097, 0x1894, 0x00000000); - nv_mthd(priv, 0xa097, 0x1898, 0x00000000); - nv_mthd(priv, 0xa097, 0x189c, 0x00000000); - nv_mthd(priv, 0xa097, 0x18a0, 0x00000000); - nv_mthd(priv, 0xa097, 0x18a4, 0x00000000); - nv_mthd(priv, 0xa097, 0x18a8, 0x00000000); - nv_mthd(priv, 0xa097, 0x18ac, 0x00000000); - nv_mthd(priv, 0xa097, 0x18b0, 0x00000000); - nv_mthd(priv, 0xa097, 0x18b4, 0x00000000); - nv_mthd(priv, 0xa097, 0x18b8, 0x00000000); - nv_mthd(priv, 0xa097, 0x18bc, 0x00000000); - nv_mthd(priv, 0xa097, 0x18c0, 0x00000000); - nv_mthd(priv, 0xa097, 0x18c4, 0x00000000); - nv_mthd(priv, 0xa097, 0x18c8, 0x00000000); - nv_mthd(priv, 0xa097, 0x18cc, 0x00000000); - nv_mthd(priv, 0xa097, 0x18d0, 0x00000000); - nv_mthd(priv, 0xa097, 0x18d4, 0x00000000); - nv_mthd(priv, 0xa097, 0x18d8, 0x00000000); - nv_mthd(priv, 0xa097, 0x18dc, 0x00000000); - nv_mthd(priv, 0xa097, 0x18e0, 0x00000000); - nv_mthd(priv, 0xa097, 0x18e4, 0x00000000); - nv_mthd(priv, 0xa097, 0x18e8, 0x00000000); - nv_mthd(priv, 0xa097, 0x18ec, 0x00000000); - nv_mthd(priv, 0xa097, 0x18f0, 0x00000000); - nv_mthd(priv, 0xa097, 0x18f4, 0x00000000); - nv_mthd(priv, 0xa097, 0x18f8, 0x00000000); - nv_mthd(priv, 0xa097, 0x18fc, 0x00000000); - nv_mthd(priv, 0xa097, 0x0f84, 0x00000000); - nv_mthd(priv, 0xa097, 0x0f88, 0x00000000); - nv_mthd(priv, 0xa097, 0x17c8, 0x00000000); - nv_mthd(priv, 0xa097, 0x17cc, 0x00000000); - nv_mthd(priv, 0xa097, 0x17d0, 0x000000ff); - nv_mthd(priv, 0xa097, 0x17d4, 0xffffffff); - nv_mthd(priv, 0xa097, 0x17d8, 0x00000002); - nv_mthd(priv, 0xa097, 0x17dc, 0x00000000); - nv_mthd(priv, 0xa097, 0x15f4, 0x00000000); - nv_mthd(priv, 0xa097, 0x15f8, 0x00000000); - nv_mthd(priv, 0xa097, 0x1434, 0x00000000); - nv_mthd(priv, 0xa097, 0x1438, 0x00000000); - nv_mthd(priv, 0xa097, 0x0d74, 0x00000000); - nv_mthd(priv, 0xa097, 0x0dec, 0x00000001); - nv_mthd(priv, 0xa097, 0x13a4, 0x00000000); - nv_mthd(priv, 0xa097, 0x1318, 0x00000001); - nv_mthd(priv, 0xa097, 0x1644, 0x00000000); - nv_mthd(priv, 0xa097, 0x0748, 0x00000000); - nv_mthd(priv, 0xa097, 0x0de8, 0x00000000); - nv_mthd(priv, 0xa097, 0x1648, 0x00000000); - nv_mthd(priv, 0xa097, 0x12a4, 0x00000000); - nv_mthd(priv, 0xa097, 0x1120, 0x00000000); - nv_mthd(priv, 0xa097, 0x1124, 0x00000000); - nv_mthd(priv, 0xa097, 0x1128, 0x00000000); - nv_mthd(priv, 0xa097, 0x112c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1118, 0x00000000); - nv_mthd(priv, 0xa097, 0x164c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1658, 0x00000000); - nv_mthd(priv, 0xa097, 0x1910, 0x00000290); - nv_mthd(priv, 0xa097, 0x1518, 0x00000000); - nv_mthd(priv, 0xa097, 0x165c, 0x00000001); - nv_mthd(priv, 0xa097, 0x1520, 0x00000000); - nv_mthd(priv, 0xa097, 0x1604, 0x00000000); - nv_mthd(priv, 0xa097, 0x1570, 0x00000000); - nv_mthd(priv, 0xa097, 0x13b0, 0x3f800000); - nv_mthd(priv, 0xa097, 0x13b4, 0x3f800000); - nv_mthd(priv, 0xa097, 0x020c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1670, 0x30201000); - nv_mthd(priv, 0xa097, 0x1674, 0x70605040); - nv_mthd(priv, 0xa097, 0x1678, 0xb8a89888); - nv_mthd(priv, 0xa097, 0x167c, 0xf8e8d8c8); - nv_mthd(priv, 0xa097, 0x166c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1680, 0x00ffff00); - nv_mthd(priv, 0xa097, 0x12d0, 0x00000003); - nv_mthd(priv, 0xa097, 0x12d4, 0x00000002); - nv_mthd(priv, 0xa097, 0x1684, 0x00000000); - nv_mthd(priv, 0xa097, 0x1688, 0x00000000); - nv_mthd(priv, 0xa097, 0x0dac, 0x00001b02); - nv_mthd(priv, 0xa097, 0x0db0, 0x00001b02); - nv_mthd(priv, 0xa097, 0x0db4, 0x00000000); - nv_mthd(priv, 0xa097, 0x168c, 0x00000000); - nv_mthd(priv, 0xa097, 0x15bc, 0x00000000); - nv_mthd(priv, 0xa097, 0x156c, 0x00000000); - nv_mthd(priv, 0xa097, 0x187c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1110, 0x00000001); - nv_mthd(priv, 0xa097, 0x0dc0, 0x00000000); - nv_mthd(priv, 0xa097, 0x0dc4, 0x00000000); - nv_mthd(priv, 0xa097, 0x0dc8, 0x00000000); - nv_mthd(priv, 0xa097, 0x1234, 0x00000000); - nv_mthd(priv, 0xa097, 0x1690, 0x00000000); - nv_mthd(priv, 0xa097, 0x12ac, 0x00000001); - nv_mthd(priv, 0xa097, 0x0790, 0x00000000); - nv_mthd(priv, 0xa097, 0x0794, 0x00000000); - nv_mthd(priv, 0xa097, 0x0798, 0x00000000); - nv_mthd(priv, 0xa097, 0x079c, 0x00000000); - nv_mthd(priv, 0xa097, 0x07a0, 0x00000000); - nv_mthd(priv, 0xa097, 0x077c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1000, 0x00000010); - nv_mthd(priv, 0xa097, 0x10fc, 0x00000000); - nv_mthd(priv, 0xa097, 0x1290, 0x00000000); - nv_mthd(priv, 0xa097, 0x0218, 0x00000010); - nv_mthd(priv, 0xa097, 0x12d8, 0x00000000); - nv_mthd(priv, 0xa097, 0x12dc, 0x00000010); - nv_mthd(priv, 0xa097, 0x0d94, 0x00000001); - nv_mthd(priv, 0xa097, 0x155c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1560, 0x00000000); - nv_mthd(priv, 0xa097, 0x1564, 0x00000fff); - nv_mthd(priv, 0xa097, 0x1574, 0x00000000); - nv_mthd(priv, 0xa097, 0x1578, 0x00000000); - nv_mthd(priv, 0xa097, 0x157c, 0x000fffff); - nv_mthd(priv, 0xa097, 0x1354, 0x00000000); - nv_mthd(priv, 0xa097, 0x1610, 0x00000012); - nv_mthd(priv, 0xa097, 0x1608, 0x00000000); - nv_mthd(priv, 0xa097, 0x160c, 0x00000000); - nv_mthd(priv, 0xa097, 0x260c, 0x00000000); - nv_mthd(priv, 0xa097, 0x07ac, 0x00000000); - nv_mthd(priv, 0xa097, 0x162c, 0x00000003); - nv_mthd(priv, 0xa097, 0x0210, 0x00000000); - nv_mthd(priv, 0xa097, 0x0320, 0x00000000); - nv_mthd(priv, 0xa097, 0x0324, 0x3f800000); - nv_mthd(priv, 0xa097, 0x0328, 0x3f800000); - nv_mthd(priv, 0xa097, 0x032c, 0x3f800000); - nv_mthd(priv, 0xa097, 0x0330, 0x3f800000); - nv_mthd(priv, 0xa097, 0x0334, 0x3f800000); - nv_mthd(priv, 0xa097, 0x0338, 0x3f800000); - nv_mthd(priv, 0xa097, 0x0750, 0x00000000); - nv_mthd(priv, 0xa097, 0x0760, 0x39291909); - nv_mthd(priv, 0xa097, 0x0764, 0x79695949); - nv_mthd(priv, 0xa097, 0x0768, 0xb9a99989); - nv_mthd(priv, 0xa097, 0x076c, 0xf9e9d9c9); - nv_mthd(priv, 0xa097, 0x0770, 0x30201000); - nv_mthd(priv, 0xa097, 0x0774, 0x70605040); - nv_mthd(priv, 0xa097, 0x0778, 0x00009080); - nv_mthd(priv, 0xa097, 0x0780, 0x39291909); - nv_mthd(priv, 0xa097, 0x0784, 0x79695949); - nv_mthd(priv, 0xa097, 0x0788, 0xb9a99989); - nv_mthd(priv, 0xa097, 0x078c, 0xf9e9d9c9); - nv_mthd(priv, 0xa097, 0x07d0, 0x30201000); - nv_mthd(priv, 0xa097, 0x07d4, 0x70605040); - nv_mthd(priv, 0xa097, 0x07d8, 0x00009080); - nv_mthd(priv, 0xa097, 0x037c, 0x00000001); - nv_mthd(priv, 0xa097, 0x0740, 0x00000000); - nv_mthd(priv, 0xa097, 0x0744, 0x00000000); - nv_mthd(priv, 0xa097, 0x2600, 0x00000000); - nv_mthd(priv, 0xa097, 0x1918, 0x00000000); - nv_mthd(priv, 0xa097, 0x191c, 0x00000900); - nv_mthd(priv, 0xa097, 0x1920, 0x00000405); - nv_mthd(priv, 0xa097, 0x1308, 0x00000001); - nv_mthd(priv, 0xa097, 0x1924, 0x00000000); - nv_mthd(priv, 0xa097, 0x13ac, 0x00000000); - nv_mthd(priv, 0xa097, 0x192c, 0x00000001); - nv_mthd(priv, 0xa097, 0x193c, 0x00002c1c); - nv_mthd(priv, 0xa097, 0x0d7c, 0x00000000); - nv_mthd(priv, 0xa097, 0x0f8c, 0x00000000); - nv_mthd(priv, 0xa097, 0x02c0, 0x00000001); - nv_mthd(priv, 0xa097, 0x1510, 0x00000000); - nv_mthd(priv, 0xa097, 0x1940, 0x00000000); - nv_mthd(priv, 0xa097, 0x0ff4, 0x00000000); - nv_mthd(priv, 0xa097, 0x0ff8, 0x00000000); - nv_mthd(priv, 0xa097, 0x194c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1950, 0x00000000); - nv_mthd(priv, 0xa097, 0x1968, 0x00000000); - nv_mthd(priv, 0xa097, 0x1590, 0x0000003f); - nv_mthd(priv, 0xa097, 0x07e8, 0x00000000); - nv_mthd(priv, 0xa097, 0x07ec, 0x00000000); - nv_mthd(priv, 0xa097, 0x07f0, 0x00000000); - nv_mthd(priv, 0xa097, 0x07f4, 0x00000000); - nv_mthd(priv, 0xa097, 0x196c, 0x00000011); - nv_mthd(priv, 0xa097, 0x02e4, 0x0000b001); - nv_mthd(priv, 0xa097, 0x036c, 0x00000000); - nv_mthd(priv, 0xa097, 0x0370, 0x00000000); - nv_mthd(priv, 0xa097, 0x197c, 0x00000000); - nv_mthd(priv, 0xa097, 0x0fcc, 0x00000000); - nv_mthd(priv, 0xa097, 0x0fd0, 0x00000000); - nv_mthd(priv, 0xa097, 0x02d8, 0x00000040); - nv_mthd(priv, 0xa097, 0x1980, 0x00000080); - nv_mthd(priv, 0xa097, 0x1504, 0x00000080); - nv_mthd(priv, 0xa097, 0x1984, 0x00000000); - nv_mthd(priv, 0xa097, 0x0300, 0x00000001); - nv_mthd(priv, 0xa097, 0x13a8, 0x00000000); - nv_mthd(priv, 0xa097, 0x12ec, 0x00000000); - nv_mthd(priv, 0xa097, 0x1310, 0x00000000); - nv_mthd(priv, 0xa097, 0x1314, 0x00000001); - nv_mthd(priv, 0xa097, 0x1380, 0x00000000); - nv_mthd(priv, 0xa097, 0x1384, 0x00000001); - nv_mthd(priv, 0xa097, 0x1388, 0x00000001); - nv_mthd(priv, 0xa097, 0x138c, 0x00000001); - nv_mthd(priv, 0xa097, 0x1390, 0x00000001); - nv_mthd(priv, 0xa097, 0x1394, 0x00000000); - nv_mthd(priv, 0xa097, 0x139c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1398, 0x00000000); - nv_mthd(priv, 0xa097, 0x1594, 0x00000000); - nv_mthd(priv, 0xa097, 0x1598, 0x00000001); - nv_mthd(priv, 0xa097, 0x159c, 0x00000001); - nv_mthd(priv, 0xa097, 0x15a0, 0x00000001); - nv_mthd(priv, 0xa097, 0x15a4, 0x00000001); - nv_mthd(priv, 0xa097, 0x0f54, 0x00000000); - nv_mthd(priv, 0xa097, 0x0f58, 0x00000000); - nv_mthd(priv, 0xa097, 0x0f5c, 0x00000000); - nv_mthd(priv, 0xa097, 0x19bc, 0x00000000); - nv_mthd(priv, 0xa097, 0x0f9c, 0x00000000); - nv_mthd(priv, 0xa097, 0x0fa0, 0x00000000); - nv_mthd(priv, 0xa097, 0x12cc, 0x00000000); - nv_mthd(priv, 0xa097, 0x12e8, 0x00000000); - nv_mthd(priv, 0xa097, 0x130c, 0x00000001); - nv_mthd(priv, 0xa097, 0x1360, 0x00000000); - nv_mthd(priv, 0xa097, 0x1364, 0x00000000); - nv_mthd(priv, 0xa097, 0x1368, 0x00000000); - nv_mthd(priv, 0xa097, 0x136c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1370, 0x00000000); - nv_mthd(priv, 0xa097, 0x1374, 0x00000000); - nv_mthd(priv, 0xa097, 0x1378, 0x00000000); - nv_mthd(priv, 0xa097, 0x137c, 0x00000000); - nv_mthd(priv, 0xa097, 0x133c, 0x00000001); - nv_mthd(priv, 0xa097, 0x1340, 0x00000001); - nv_mthd(priv, 0xa097, 0x1344, 0x00000002); - nv_mthd(priv, 0xa097, 0x1348, 0x00000001); - nv_mthd(priv, 0xa097, 0x134c, 0x00000001); - nv_mthd(priv, 0xa097, 0x1350, 0x00000002); - nv_mthd(priv, 0xa097, 0x1358, 0x00000001); - nv_mthd(priv, 0xa097, 0x12e4, 0x00000000); - nv_mthd(priv, 0xa097, 0x131c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1320, 0x00000000); - nv_mthd(priv, 0xa097, 0x1324, 0x00000000); - nv_mthd(priv, 0xa097, 0x1328, 0x00000000); - nv_mthd(priv, 0xa097, 0x19c0, 0x00000000); - nv_mthd(priv, 0xa097, 0x1140, 0x00000000); - nv_mthd(priv, 0xa097, 0x19c4, 0x00000000); - nv_mthd(priv, 0xa097, 0x19c8, 0x00001500); - nv_mthd(priv, 0xa097, 0x135c, 0x00000000); - nv_mthd(priv, 0xa097, 0x0f90, 0x00000000); - nv_mthd(priv, 0xa097, 0x19e0, 0x00000001); - nv_mthd(priv, 0xa097, 0x19e4, 0x00000001); - nv_mthd(priv, 0xa097, 0x19e8, 0x00000001); - nv_mthd(priv, 0xa097, 0x19ec, 0x00000001); - nv_mthd(priv, 0xa097, 0x19f0, 0x00000001); - nv_mthd(priv, 0xa097, 0x19f4, 0x00000001); - nv_mthd(priv, 0xa097, 0x19f8, 0x00000001); - nv_mthd(priv, 0xa097, 0x19fc, 0x00000001); - nv_mthd(priv, 0xa097, 0x19cc, 0x00000001); - nv_mthd(priv, 0xa097, 0x15b8, 0x00000000); - nv_mthd(priv, 0xa097, 0x1a00, 0x00001111); - nv_mthd(priv, 0xa097, 0x1a04, 0x00000000); - nv_mthd(priv, 0xa097, 0x1a08, 0x00000000); - nv_mthd(priv, 0xa097, 0x1a0c, 0x00000000); - nv_mthd(priv, 0xa097, 0x1a10, 0x00000000); - nv_mthd(priv, 0xa097, 0x1a14, 0x00000000); - nv_mthd(priv, 0xa097, 0x1a18, 0x00000000); - nv_mthd(priv, 0xa097, 0x1a1c, 0x00000000); - nv_mthd(priv, 0xa097, 0x0d6c, 0xffff0000); - nv_mthd(priv, 0xa097, 0x0d70, 0xffff0000); - nv_mthd(priv, 0xa097, 0x10f8, 0x00001010); - nv_mthd(priv, 0xa097, 0x0d80, 0x00000000); - nv_mthd(priv, 0xa097, 0x0d84, 0x00000000); - nv_mthd(priv, 0xa097, 0x0d88, 0x00000000); - nv_mthd(priv, 0xa097, 0x0d8c, 0x00000000); - nv_mthd(priv, 0xa097, 0x0d90, 0x00000000); - nv_mthd(priv, 0xa097, 0x0da0, 0x00000000); - nv_mthd(priv, 0xa097, 0x07a4, 0x00000000); - nv_mthd(priv, 0xa097, 0x07a8, 0x00000000); - nv_mthd(priv, 0xa097, 0x1508, 0x80000000); - nv_mthd(priv, 0xa097, 0x150c, 0x40000000); - nv_mthd(priv, 0xa097, 0x1668, 0x00000000); - nv_mthd(priv, 0xa097, 0x0318, 0x00000008); - nv_mthd(priv, 0xa097, 0x031c, 0x00000008); - nv_mthd(priv, 0xa097, 0x0d9c, 0x00000001); - nv_mthd(priv, 0xa097, 0x0374, 0x00000000); - nv_mthd(priv, 0xa097, 0x0378, 0x00000020); - nv_mthd(priv, 0xa097, 0x07dc, 0x00000000); - nv_mthd(priv, 0xa097, 0x074c, 0x00000055); - nv_mthd(priv, 0xa097, 0x1420, 0x00000003); - nv_mthd(priv, 0xa097, 0x17bc, 0x00000000); - nv_mthd(priv, 0xa097, 0x17c0, 0x00000000); - nv_mthd(priv, 0xa097, 0x17c4, 0x00000001); - nv_mthd(priv, 0xa097, 0x1008, 0x00000008); - nv_mthd(priv, 0xa097, 0x100c, 0x00000040); - nv_mthd(priv, 0xa097, 0x1010, 0x0000012c); - nv_mthd(priv, 0xa097, 0x0d60, 0x00000040); - nv_mthd(priv, 0xa097, 0x075c, 0x00000003); - nv_mthd(priv, 0xa097, 0x1018, 0x00000020); - nv_mthd(priv, 0xa097, 0x101c, 0x00000001); - nv_mthd(priv, 0xa097, 0x1020, 0x00000020); - nv_mthd(priv, 0xa097, 0x1024, 0x00000001); - nv_mthd(priv, 0xa097, 0x1444, 0x00000000); - nv_mthd(priv, 0xa097, 0x1448, 0x00000000); - nv_mthd(priv, 0xa097, 0x144c, 0x00000000); - nv_mthd(priv, 0xa097, 0x0360, 0x20164010); - nv_mthd(priv, 0xa097, 0x0364, 0x00000020); - nv_mthd(priv, 0xa097, 0x0368, 0x00000000); - nv_mthd(priv, 0xa097, 0x0de4, 0x00000000); - nv_mthd(priv, 0xa097, 0x0204, 0x00000006); - nv_mthd(priv, 0xa097, 0x0208, 0x00000000); - nv_mthd(priv, 0xa097, 0x02cc, 0x003fffff); - nv_mthd(priv, 0xa097, 0x02d0, 0x003fffff); - nv_mthd(priv, 0xa097, 0x1220, 0x00000005); - nv_mthd(priv, 0xa097, 0x0fdc, 0x00000000); - nv_mthd(priv, 0xa097, 0x0f98, 0x00400008); - nv_mthd(priv, 0xa097, 0x1284, 0x08000080); - nv_mthd(priv, 0xa097, 0x1450, 0x00400008); - nv_mthd(priv, 0xa097, 0x1454, 0x08000080); - nv_mthd(priv, 0xa097, 0x0214, 0x00000000); -} - -static void -nve0_grctx_generate_902d(struct nvc0_graph_priv *priv) -{ - nv_mthd(priv, 0x902d, 0x0200, 0x000000cf); - nv_mthd(priv, 0x902d, 0x0204, 0x00000001); - nv_mthd(priv, 0x902d, 0x0208, 0x00000020); - nv_mthd(priv, 0x902d, 0x020c, 0x00000001); - nv_mthd(priv, 0x902d, 0x0210, 0x00000000); - nv_mthd(priv, 0x902d, 0x0214, 0x00000080); - nv_mthd(priv, 0x902d, 0x0218, 0x00000100); - nv_mthd(priv, 0x902d, 0x021c, 0x00000100); - nv_mthd(priv, 0x902d, 0x0220, 0x00000000); - nv_mthd(priv, 0x902d, 0x0224, 0x00000000); - nv_mthd(priv, 0x902d, 0x0230, 0x000000cf); - nv_mthd(priv, 0x902d, 0x0234, 0x00000001); - nv_mthd(priv, 0x902d, 0x0238, 0x00000020); - nv_mthd(priv, 0x902d, 0x023c, 0x00000001); - nv_mthd(priv, 0x902d, 0x0244, 0x00000080); - nv_mthd(priv, 0x902d, 0x0248, 0x00000100); - nv_mthd(priv, 0x902d, 0x024c, 0x00000100); - switch (nv_device(priv)->chipset) { - case 0xe6: - nv_mthd(priv, 0x902d, 0x3410, 0x80002006); - break; - case 0xe4: - case 0xe7: - default: - nv_mthd(priv, 0x902d, 0x3410, 0x00000000); - break; - } -} - -static void -nve0_graph_generate_unk40xx(struct nvc0_graph_priv *priv) -{ - switch (nv_device(priv)->chipset) { - case 0xf0: - nv_wr32(priv, 0x404004, 0x00000000); - nv_wr32(priv, 0x404008, 0x00000000); - nv_wr32(priv, 0x40400c, 0x00000000); - break; - default: - break; - } - nv_wr32(priv, 0x404010, 0x0); - nv_wr32(priv, 0x404014, 0x0); - nv_wr32(priv, 0x404018, 0x0); - nv_wr32(priv, 0x40401c, 0x0); - nv_wr32(priv, 0x404020, 0x0); - nv_wr32(priv, 0x404024, 0xe000); - nv_wr32(priv, 0x404028, 0x0); - switch (nv_device(priv)->chipset) { - case 0xf0: - nv_wr32(priv, 0x40402c, 0x00000000); - nv_wr32(priv, 0x404030, 0x00000000); - nv_wr32(priv, 0x404034, 0x00000000); - nv_wr32(priv, 0x404038, 0x00000000); - nv_wr32(priv, 0x40403c, 0x00000000); - nv_wr32(priv, 0x404040, 0x00000000); - nv_wr32(priv, 0x404044, 0x00000000); - break; - default: - break; - } - nv_wr32(priv, 0x4040a8, 0x0); - nv_wr32(priv, 0x4040ac, 0x0); - nv_wr32(priv, 0x4040b0, 0x0); - nv_wr32(priv, 0x4040b4, 0x0); - nv_wr32(priv, 0x4040b8, 0x0); - nv_wr32(priv, 0x4040bc, 0x0); - nv_wr32(priv, 0x4040c0, 0x0); - nv_wr32(priv, 0x4040c4, 0x0); - nv_wr32(priv, 0x4040c8, 0xf800008f); - nv_wr32(priv, 0x4040d0, 0x0); - nv_wr32(priv, 0x4040d4, 0x0); - nv_wr32(priv, 0x4040d8, 0x0); - nv_wr32(priv, 0x4040dc, 0x0); - nv_wr32(priv, 0x4040e0, 0x0); - nv_wr32(priv, 0x4040e4, 0x0); - nv_wr32(priv, 0x4040e8, 0x1000); - nv_wr32(priv, 0x4040f8, 0x0); - switch (nv_device(priv)->chipset) { - case 0xf0: - nv_wr32(priv, 0x404100, 0x00000000); - nv_wr32(priv, 0x404104, 0x00000000); - nv_wr32(priv, 0x404108, 0x00000000); - nv_wr32(priv, 0x40410c, 0x00000000); - nv_wr32(priv, 0x404110, 0x00000000); - nv_wr32(priv, 0x404114, 0x00000000); - nv_wr32(priv, 0x404118, 0x00000000); - nv_wr32(priv, 0x40411c, 0x00000000); - nv_wr32(priv, 0x404120, 0x00000000); - nv_wr32(priv, 0x404124, 0x00000000); - break; - default: - break; - } - nv_wr32(priv, 0x404130, 0x0); - nv_wr32(priv, 0x404134, 0x0); - nv_wr32(priv, 0x404138, 0x20000040); - nv_wr32(priv, 0x404150, 0x2e); - nv_wr32(priv, 0x404154, 0x400); - nv_wr32(priv, 0x404158, 0x200); - nv_wr32(priv, 0x404164, 0x55); - switch (nv_device(priv)->chipset) { - case 0xf0: - nv_wr32(priv, 0x40417c, 0x00000000); - nv_wr32(priv, 0x404180, 0x00000000); - break; - default: - break; - } - nv_wr32(priv, 0x4041a0, 0x0); - nv_wr32(priv, 0x4041a4, 0x0); - nv_wr32(priv, 0x4041a8, 0x0); - nv_wr32(priv, 0x4041ac, 0x0); - switch (nv_device(priv)->chipset) { - case 0xf0: - nv_wr32(priv, 0x404200, 0xa197); - nv_wr32(priv, 0x404204, 0xa1c0); - nv_wr32(priv, 0x404208, 0xa140); - nv_wr32(priv, 0x40420c, 0x902d); - break; - default: - nv_wr32(priv, 0x404200, 0x0); - nv_wr32(priv, 0x404204, 0x0); - nv_wr32(priv, 0x404208, 0x0); - nv_wr32(priv, 0x40420c, 0x0); - break; - } -} - -static void -nve0_graph_generate_unk44xx(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x404404, 0x0); - nv_wr32(priv, 0x404408, 0x0); - nv_wr32(priv, 0x40440c, 0x0); - nv_wr32(priv, 0x404410, 0x0); - nv_wr32(priv, 0x404414, 0x0); - nv_wr32(priv, 0x404418, 0x0); - nv_wr32(priv, 0x40441c, 0x0); - nv_wr32(priv, 0x404420, 0x0); - nv_wr32(priv, 0x404424, 0x0); - nv_wr32(priv, 0x404428, 0x0); - nv_wr32(priv, 0x40442c, 0x0); - nv_wr32(priv, 0x404430, 0x0); - switch (nv_device(priv)->chipset) { - case 0xf0: - break; - default: - nv_wr32(priv, 0x404434, 0x0); - break; - } - nv_wr32(priv, 0x404438, 0x0); - nv_wr32(priv, 0x404460, 0x0); - nv_wr32(priv, 0x404464, 0x0); - nv_wr32(priv, 0x404468, 0xffffff); - nv_wr32(priv, 0x40446c, 0x0); - nv_wr32(priv, 0x404480, 0x1); - nv_wr32(priv, 0x404498, 0x1); -} - -static void -nve0_graph_generate_unk46xx(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x404604, 0x14); - nv_wr32(priv, 0x404608, 0x0); - nv_wr32(priv, 0x40460c, 0x3fff); - nv_wr32(priv, 0x404610, 0x100); - nv_wr32(priv, 0x404618, 0x0); - nv_wr32(priv, 0x40461c, 0x0); - nv_wr32(priv, 0x404620, 0x0); - nv_wr32(priv, 0x404624, 0x0); - nv_wr32(priv, 0x40462c, 0x0); - nv_wr32(priv, 0x404630, 0x0); - nv_wr32(priv, 0x404640, 0x0); - nv_wr32(priv, 0x404654, 0x0); - nv_wr32(priv, 0x404660, 0x0); - nv_wr32(priv, 0x404678, 0x0); - nv_wr32(priv, 0x40467c, 0x2); - nv_wr32(priv, 0x404680, 0x0); - nv_wr32(priv, 0x404684, 0x0); - nv_wr32(priv, 0x404688, 0x0); - nv_wr32(priv, 0x40468c, 0x0); - nv_wr32(priv, 0x404690, 0x0); - nv_wr32(priv, 0x404694, 0x0); - nv_wr32(priv, 0x404698, 0x0); - nv_wr32(priv, 0x40469c, 0x0); - nv_wr32(priv, 0x4046a0, 0x7f0080); - nv_wr32(priv, 0x4046a4, 0x0); - nv_wr32(priv, 0x4046a8, 0x0); - nv_wr32(priv, 0x4046ac, 0x0); - nv_wr32(priv, 0x4046b0, 0x0); - nv_wr32(priv, 0x4046b4, 0x0); - nv_wr32(priv, 0x4046b8, 0x0); - nv_wr32(priv, 0x4046bc, 0x0); - nv_wr32(priv, 0x4046c0, 0x0); - nv_wr32(priv, 0x4046c8, 0x0); - nv_wr32(priv, 0x4046cc, 0x0); - nv_wr32(priv, 0x4046d0, 0x0); -} - -static void -nve0_graph_generate_unk47xx(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x404700, 0x0); - nv_wr32(priv, 0x404704, 0x0); - nv_wr32(priv, 0x404708, 0x0); - nv_wr32(priv, 0x404718, 0x0); - nv_wr32(priv, 0x40471c, 0x0); - nv_wr32(priv, 0x404720, 0x0); - nv_wr32(priv, 0x404724, 0x0); - nv_wr32(priv, 0x404728, 0x0); - nv_wr32(priv, 0x40472c, 0x0); - nv_wr32(priv, 0x404730, 0x0); - nv_wr32(priv, 0x404734, 0x100); - nv_wr32(priv, 0x404738, 0x0); - nv_wr32(priv, 0x40473c, 0x0); - nv_wr32(priv, 0x404744, 0x0); - nv_wr32(priv, 0x404748, 0x0); - nv_wr32(priv, 0x404754, 0x0); -} - -static void -nve0_graph_generate_unk58xx(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x405800, 0xf8000bf); - nv_wr32(priv, 0x405830, 0x2180648); - nv_wr32(priv, 0x405834, 0x8000000); - nv_wr32(priv, 0x405838, 0x0); - nv_wr32(priv, 0x405854, 0x0); - nv_wr32(priv, 0x405870, 0x1); - nv_wr32(priv, 0x405874, 0x1); - nv_wr32(priv, 0x405878, 0x1); - nv_wr32(priv, 0x40587c, 0x1); - nv_wr32(priv, 0x405a00, 0x0); - nv_wr32(priv, 0x405a04, 0x0); - nv_wr32(priv, 0x405a18, 0x0); -} - -static void -nve0_graph_generate_unk5bxx(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x405b00, 0x0); - nv_wr32(priv, 0x405b10, 0x1000); - switch (nv_device(priv)->chipset) { - case 0xf0: - nv_wr32(priv, 0x405b20, 0x04000000); - break; - default: - break; - } -} - -static void -nve0_graph_generate_unk60xx(struct nvc0_graph_priv *priv) -{ - switch (nv_device(priv)->chipset) { - case 0xf0: - nv_wr32(priv, 0x406020, 0x34103c1); - break; - default: - nv_wr32(priv, 0x406020, 0x4103c1); - break; - } - nv_wr32(priv, 0x406028, 0x1); - nv_wr32(priv, 0x40602c, 0x1); - nv_wr32(priv, 0x406030, 0x1); - nv_wr32(priv, 0x406034, 0x1); -} - -static void -nve0_graph_generate_unk64xx(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x4064a8, 0x0); - nv_wr32(priv, 0x4064ac, 0x3fff); - switch (nv_device(priv)->chipset) { - case 0xf0: - nv_wr32(priv, 0x4064b0, 0x0); - break; - default: - break; - } - nv_wr32(priv, 0x4064b4, 0x0); - nv_wr32(priv, 0x4064b8, 0x0); - switch (nv_device(priv)->chipset) { - case 0xf0: - nv_wr32(priv, 0x4064c0, 0x802000f0); - nv_wr32(priv, 0x4064c4, 0x192ffff); - nv_wr32(priv, 0x4064c8, 0x18007c0); - break; - default: - nv_wr32(priv, 0x4064c0, 0x801a00f0); - nv_wr32(priv, 0x4064c4, 0x192ffff); - nv_wr32(priv, 0x4064c8, 0x1800600); - break; - } - nv_wr32(priv, 0x4064cc, 0x0); - nv_wr32(priv, 0x4064d0, 0x0); - nv_wr32(priv, 0x4064d4, 0x0); - nv_wr32(priv, 0x4064d8, 0x0); - nv_wr32(priv, 0x4064dc, 0x0); - nv_wr32(priv, 0x4064e0, 0x0); - nv_wr32(priv, 0x4064e4, 0x0); - nv_wr32(priv, 0x4064e8, 0x0); - nv_wr32(priv, 0x4064ec, 0x0); - nv_wr32(priv, 0x4064fc, 0x22a); -} - -static void -nve0_graph_generate_unk70xx(struct nvc0_graph_priv *priv) -{ - switch (nv_device(priv)->chipset) { - case 0xf0: - break; - default: - nv_wr32(priv, 0x407040, 0x0); - break; - } -} - -static void -nve0_graph_generate_unk78xx(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x407804, 0x23); - nv_wr32(priv, 0x40780c, 0xa418820); - nv_wr32(priv, 0x407810, 0x62080e6); - nv_wr32(priv, 0x407814, 0x20398a4); - nv_wr32(priv, 0x407818, 0xe629062); - nv_wr32(priv, 0x40781c, 0xa418820); - nv_wr32(priv, 0x407820, 0xe6); - nv_wr32(priv, 0x4078bc, 0x103); -} - -static void -nve0_graph_generate_unk80xx(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x408000, 0x0); - nv_wr32(priv, 0x408004, 0x0); - nv_wr32(priv, 0x408008, 0x30); - nv_wr32(priv, 0x40800c, 0x0); - nv_wr32(priv, 0x408010, 0x0); - nv_wr32(priv, 0x408014, 0x69); - nv_wr32(priv, 0x408018, 0xe100e100); - nv_wr32(priv, 0x408064, 0x0); -} - -static void -nve0_graph_generate_unk88xx(struct nvc0_graph_priv *priv) -{ - switch (nv_device(priv)->chipset) { - case 0xf0: - nv_wr32(priv, 0x408800, 0x12802a3c); - break; - default: - nv_wr32(priv, 0x408800, 0x2802a3c); - break; - } - nv_wr32(priv, 0x408804, 0x40); - switch (nv_device(priv)->chipset) { - case 0xf0: - nv_wr32(priv, 0x408808, 0x1003e005); - break; - default: - nv_wr32(priv, 0x408808, 0x1043e005); - break; - } - nv_wr32(priv, 0x408840, 0xb); - nv_wr32(priv, 0x408900, 0x3080b801); - nv_wr32(priv, 0x408904, 0x62000001); - nv_wr32(priv, 0x408908, 0xc8102f); - nv_wr32(priv, 0x408980, 0x11d); -} - -static void -nve0_graph_generate_gpc(struct nvc0_graph_priv *priv) -{ - int i; - - nv_wr32(priv, 0x418380, 0x16); - nv_wr32(priv, 0x418400, 0x38004e00); - nv_wr32(priv, 0x418404, 0x71e0ffff); - nv_wr32(priv, 0x41840c, 0x1008); - nv_wr32(priv, 0x418410, 0xfff0fff); - nv_wr32(priv, 0x418414, 0x2200fff); - nv_wr32(priv, 0x418450, 0x0); - nv_wr32(priv, 0x418454, 0x0); - nv_wr32(priv, 0x418458, 0x0); - nv_wr32(priv, 0x41845c, 0x0); - nv_wr32(priv, 0x418460, 0x0); - nv_wr32(priv, 0x418464, 0x0); - nv_wr32(priv, 0x418468, 0x1); - nv_wr32(priv, 0x41846c, 0x0); - nv_wr32(priv, 0x418470, 0x0); - nv_wr32(priv, 0x418600, 0x1f); - nv_wr32(priv, 0x418684, 0xf); - nv_wr32(priv, 0x418700, 0x2); - nv_wr32(priv, 0x418704, 0x80); - nv_wr32(priv, 0x418708, 0x0); - nv_wr32(priv, 0x41870c, 0x0); - nv_wr32(priv, 0x418710, 0x0); - nv_wr32(priv, 0x418800, 0x7006860a); - nv_wr32(priv, 0x418808, 0x0); - switch (nv_device(priv)->chipset) { - case 0xf0: - nv_wr32(priv, 0x41880c, 0x30); - break; - default: - nv_wr32(priv, 0x41880c, 0x0); - break; - } - nv_wr32(priv, 0x418810, 0x0); - nv_wr32(priv, 0x418828, 0x44); - nv_wr32(priv, 0x418830, 0x10000001); - nv_wr32(priv, 0x4188d8, 0x8); - nv_wr32(priv, 0x4188e0, 0x1000000); - nv_wr32(priv, 0x4188e8, 0x0); - nv_wr32(priv, 0x4188ec, 0x0); - nv_wr32(priv, 0x4188f0, 0x0); - nv_wr32(priv, 0x4188f4, 0x0); - nv_wr32(priv, 0x4188f8, 0x0); - nv_wr32(priv, 0x4188fc, 0x20100018); - nv_wr32(priv, 0x41891c, 0xff00ff); - nv_wr32(priv, 0x418924, 0x0); - nv_wr32(priv, 0x418928, 0xffff00); - nv_wr32(priv, 0x41892c, 0xff00); - for (i = 0; i < 8; i++) { - nv_wr32(priv, 0x418a00 + (i * 0x20), 0x0); - nv_wr32(priv, 0x418a04 + (i * 0x20), 0x0); - nv_wr32(priv, 0x418a08 + (i * 0x20), 0x0); - nv_wr32(priv, 0x418a0c + (i * 0x20), 0x10000); - nv_wr32(priv, 0x418a10 + (i * 0x20), 0x0); - nv_wr32(priv, 0x418a14 + (i * 0x20), 0x0); - nv_wr32(priv, 0x418a18 + (i * 0x20), 0x0); - } - nv_wr32(priv, 0x418b00, 0x6); - nv_wr32(priv, 0x418b08, 0xa418820); - nv_wr32(priv, 0x418b0c, 0x62080e6); - nv_wr32(priv, 0x418b10, 0x20398a4); - nv_wr32(priv, 0x418b14, 0xe629062); - nv_wr32(priv, 0x418b18, 0xa418820); - nv_wr32(priv, 0x418b1c, 0xe6); - nv_wr32(priv, 0x418bb8, 0x103); - nv_wr32(priv, 0x418c08, 0x1); - nv_wr32(priv, 0x418c10, 0x0); - nv_wr32(priv, 0x418c14, 0x0); - nv_wr32(priv, 0x418c18, 0x0); - nv_wr32(priv, 0x418c1c, 0x0); - nv_wr32(priv, 0x418c20, 0x0); - nv_wr32(priv, 0x418c24, 0x0); - nv_wr32(priv, 0x418c28, 0x0); - nv_wr32(priv, 0x418c2c, 0x0); - nv_wr32(priv, 0x418c40, 0xffffffff); - nv_wr32(priv, 0x418c6c, 0x1); - nv_wr32(priv, 0x418c80, 0x20200004); - nv_wr32(priv, 0x418c8c, 0x1); - switch (nv_device(priv)->chipset) { - case 0xf0: - nv_wr32(priv, 0x418d24, 0x0); - break; - default: - break; - } - nv_wr32(priv, 0x419000, 0x780); - nv_wr32(priv, 0x419004, 0x0); - nv_wr32(priv, 0x419008, 0x0); - nv_wr32(priv, 0x419014, 0x4); -} - -static void -nve0_graph_generate_tpc(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x419848, 0x0); - nv_wr32(priv, 0x419864, 0x129); - nv_wr32(priv, 0x419888, 0x0); - nv_wr32(priv, 0x419a00, 0xf0); - nv_wr32(priv, 0x419a04, 0x1); - nv_wr32(priv, 0x419a08, 0x21); - nv_wr32(priv, 0x419a0c, 0x20000); - nv_wr32(priv, 0x419a10, 0x0); - nv_wr32(priv, 0x419a14, 0x200); - nv_wr32(priv, 0x419a1c, 0xc000); - switch (nv_device(priv)->chipset) { - case 0xf0: - nv_wr32(priv, 0x419a20, 0x20800); - break; - default: - nv_wr32(priv, 0x419a20, 0x800); - break; - } - nv_wr32(priv, 0x419a30, 0x1); - nv_wr32(priv, 0x419ac4, 0x37f440); - switch (nv_device(priv)->chipset) { - case 0xf0: - nv_wr32(priv, 0x419c00, 0x1a); - break; - default: - nv_wr32(priv, 0x419c00, 0xa); - break; - } - nv_wr32(priv, 0x419c04, 0x80000006); - nv_wr32(priv, 0x419c08, 0x2); - nv_wr32(priv, 0x419c20, 0x0); - nv_wr32(priv, 0x419c24, 0x84210); - nv_wr32(priv, 0x419c28, 0x3efbefbe); - nv_wr32(priv, 0x419ce8, 0x0); - switch (nv_device(priv)->chipset) { - case 0xf0: - nv_wr32(priv, 0x419cf4, 0x203); - nv_wr32(priv, 0x419e04, 0x0); - nv_wr32(priv, 0x419e08, 0x1d); - nv_wr32(priv, 0x419e0c, 0x0); - nv_wr32(priv, 0x419e10, 0x1c02); - - break; - default: - nv_wr32(priv, 0x419cf4, 0x3203); - nv_wr32(priv, 0x419e04, 0x0); - nv_wr32(priv, 0x419e08, 0x0); - nv_wr32(priv, 0x419e0c, 0x0); - nv_wr32(priv, 0x419e10, 0x402); - break; - } - nv_wr32(priv, 0x419e44, 0x13eff2); - nv_wr32(priv, 0x419e48, 0x0); - nv_wr32(priv, 0x419e4c, 0x7f); - nv_wr32(priv, 0x419e50, 0x0); - nv_wr32(priv, 0x419e54, 0x0); - switch (nv_device(priv)->chipset) { - case 0xf0: - nv_wr32(priv, 0x419e58, 0x1); - break; - default: - nv_wr32(priv, 0x419e58, 0x0); - break; - } - nv_wr32(priv, 0x419e5c, 0x0); - nv_wr32(priv, 0x419e60, 0x0); - nv_wr32(priv, 0x419e64, 0x0); - switch (nv_device(priv)->chipset) { - case 0xf0: - nv_wr32(priv, 0x419e68, 0x2); - break; - default: - nv_wr32(priv, 0x419e68, 0x0); - break; - } - nv_wr32(priv, 0x419e6c, 0x0); - nv_wr32(priv, 0x419e70, 0x0); - nv_wr32(priv, 0x419e74, 0x0); - nv_wr32(priv, 0x419e78, 0x0); - nv_wr32(priv, 0x419e7c, 0x0); - nv_wr32(priv, 0x419e80, 0x0); - nv_wr32(priv, 0x419e84, 0x0); - nv_wr32(priv, 0x419e88, 0x0); - nv_wr32(priv, 0x419e8c, 0x0); - nv_wr32(priv, 0x419e90, 0x0); - nv_wr32(priv, 0x419e94, 0x0); - nv_wr32(priv, 0x419e98, 0x0); - switch (nv_device(priv)->chipset) { - case 0xe4: - case 0xe7: - case 0xe6: - nv_wr32(priv, 0x419eac, 0x1f8f); - nv_wr32(priv, 0x419eb0, 0xd3f); - break; - case 0xf0: - nv_wr32(priv, 0x419eac, 0x1fcf); - nv_wr32(priv, 0x419eb0, 0xdb00da0); - nv_wr32(priv, 0x419eb8, 0x0); - break; - } - nv_wr32(priv, 0x419ec8, 0x1304f); - nv_wr32(priv, 0x419f30, 0x0); - nv_wr32(priv, 0x419f34, 0x0); - nv_wr32(priv, 0x419f38, 0x0); - nv_wr32(priv, 0x419f3c, 0x0); - switch (nv_device(priv)->chipset) { - case 0xf0: - nv_wr32(priv, 0x419f40, 0x18); - break; - default: - nv_wr32(priv, 0x419f40, 0x0); - break; - } - nv_wr32(priv, 0x419f44, 0x0); - nv_wr32(priv, 0x419f48, 0x0); - nv_wr32(priv, 0x419f4c, 0x0); - nv_wr32(priv, 0x419f58, 0x0); - switch (nv_device(priv)->chipset) { - case 0xe4: - case 0xe7: - case 0xe6: - nv_wr32(priv, 0x419f70, 0x0); - nv_wr32(priv, 0x419f78, 0xb); - nv_wr32(priv, 0x419f7c, 0x27a); - break; - case 0xf0: - nv_wr32(priv, 0x419f70, 0x7300); - nv_wr32(priv, 0x419f78, 0xeb); - nv_wr32(priv, 0x419f7c, 0x404); - break; - } -} - -static void -nve0_graph_generate_tpcunk(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x41be24, 0x6); - switch (nv_device(priv)->chipset) { - case 0xf0: - nv_wr32(priv, 0x41bec0, 0x10000000); - break; - default: - nv_wr32(priv, 0x41bec0, 0x12180000); - break; - } - nv_wr32(priv, 0x41bec4, 0x37f7f); - switch (nv_device(priv)->chipset) { - case 0xf0: - nv_wr32(priv, 0x41bee4, 0x0); - break; - default: - nv_wr32(priv, 0x41bee4, 0x6480430); - break; - } - nv_wr32(priv, 0x41bf00, 0xa418820); - nv_wr32(priv, 0x41bf04, 0x62080e6); - nv_wr32(priv, 0x41bf08, 0x20398a4); - nv_wr32(priv, 0x41bf0c, 0xe629062); - nv_wr32(priv, 0x41bf10, 0xa418820); - nv_wr32(priv, 0x41bf14, 0xe6); - nv_wr32(priv, 0x41bfd0, 0x900103); - nv_wr32(priv, 0x41bfe0, 0x400001); - nv_wr32(priv, 0x41bfe4, 0x0); -} - -int -nve0_grctx_generate(struct nvc0_graph_priv *priv) -{ - struct nvc0_grctx info; - int ret, i, gpc, tpc, id; - u32 data[6] = {}, data2[2] = {}, tmp; - u32 tpc_set = 0, tpc_mask = 0; - u32 magic[GPC_MAX][2], offset; - u8 tpcnr[GPC_MAX], a, b; - u8 shift, ntpcv; - - ret = nvc0_grctx_init(priv, &info); - if (ret) - return ret; - - nv_mask(priv, 0x000260, 0x00000001, 0x00000000); - nv_wr32(priv, 0x400204, 0x00000000); - nv_wr32(priv, 0x400208, 0x00000000); - - nve0_graph_generate_unk40xx(priv); - nve0_graph_generate_unk44xx(priv); - nve0_graph_generate_unk46xx(priv); - nve0_graph_generate_unk47xx(priv); - nve0_graph_generate_unk58xx(priv); - nve0_graph_generate_unk5bxx(priv); - nve0_graph_generate_unk60xx(priv); - nve0_graph_generate_unk64xx(priv); - nve0_graph_generate_unk70xx(priv); - nve0_graph_generate_unk78xx(priv); - nve0_graph_generate_unk80xx(priv); - nve0_graph_generate_unk88xx(priv); - nve0_graph_generate_gpc(priv); - nve0_graph_generate_tpc(priv); - nve0_graph_generate_tpcunk(priv); - - nv_wr32(priv, 0x404154, 0x0); - - mmio_data(0x003000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS); - mmio_data(0x008000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS); - mmio_data(0x060000, 0x1000, NV_MEM_ACCESS_RW); - mmio_list(0x40800c, 0x00000000, 8, 1); - mmio_list(0x408010, 0x80000000, 0, 0); - mmio_list(0x419004, 0x00000000, 8, 1); - mmio_list(0x419008, 0x00000000, 0, 0); - mmio_list(0x4064cc, 0x80000000, 0, 0); - mmio_list(0x408004, 0x00000000, 8, 0); - mmio_list(0x408008, 0x80000030, 0, 0); - mmio_list(0x418808, 0x00000000, 8, 0); - mmio_list(0x41880c, 0x80000030, 0, 0); - mmio_list(0x4064c8, 0x01800600, 0, 0); - mmio_list(0x418810, 0x80000000, 12, 2); - mmio_list(0x419848, 0x10000000, 12, 2); - mmio_list(0x405830, 0x02180648, 0, 0); - mmio_list(0x4064c4, 0x0192ffff, 0, 0); - for (gpc = 0, offset = 0; gpc < priv->gpc_nr; gpc++) { - u16 magic0 = 0x0218 * priv->tpc_nr[gpc]; - u16 magic1 = 0x0648 * priv->tpc_nr[gpc]; - magic[gpc][0] = 0x10000000 | (magic0 << 16) | offset; - magic[gpc][1] = 0x00000000 | (magic1 << 16); - offset += 0x0324 * priv->tpc_nr[gpc]; - } - for (gpc = 0; gpc < priv->gpc_nr; gpc++) { - mmio_list(GPC_UNIT(gpc, 0x30c0), magic[gpc][0], 0, 0); - mmio_list(GPC_UNIT(gpc, 0x30e4), magic[gpc][1] | offset, 0, 0); - offset += 0x07ff * priv->tpc_nr[gpc]; - } - mmio_list(0x17e91c, 0x06060609, 0, 0); - mmio_list(0x17e920, 0x00090a05, 0, 0); - - nv_wr32(priv, 0x418c6c, 0x1); - nv_wr32(priv, 0x41980c, 0x10); - nv_wr32(priv, 0x41be08, 0x4); - nv_wr32(priv, 0x4064c0, 0x801a00f0); - nv_wr32(priv, 0x405800, 0xf8000bf); - nv_wr32(priv, 0x419c00, 0xa); - - for (tpc = 0, id = 0; tpc < 4; tpc++) { - for (gpc = 0; gpc < priv->gpc_nr; gpc++) { - if (tpc < priv->tpc_nr[gpc]) { - nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x0698), id); - nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x04e8), id); - nv_wr32(priv, GPC_UNIT(gpc, 0x0c10 + tpc * 4), id); - nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x0088), id++); - } - - nv_wr32(priv, GPC_UNIT(gpc, 0x0c08), priv->tpc_nr[gpc]); - nv_wr32(priv, GPC_UNIT(gpc, 0x0c8c), priv->tpc_nr[gpc]); - } - } - - tmp = 0; - for (i = 0; i < priv->gpc_nr; i++) - tmp |= priv->tpc_nr[i] << (i * 4); - nv_wr32(priv, 0x406028, tmp); - nv_wr32(priv, 0x405870, tmp); - - nv_wr32(priv, 0x40602c, 0x0); - nv_wr32(priv, 0x405874, 0x0); - nv_wr32(priv, 0x406030, 0x0); - nv_wr32(priv, 0x405878, 0x0); - nv_wr32(priv, 0x406034, 0x0); - nv_wr32(priv, 0x40587c, 0x0); - - /* calculate first set of magics */ - memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr)); - - gpc = -1; - for (tpc = 0; tpc < priv->tpc_total; tpc++) { - do { - gpc = (gpc + 1) % priv->gpc_nr; - } while (!tpcnr[gpc]); - tpcnr[gpc]--; - - data[tpc / 6] |= gpc << ((tpc % 6) * 5); - } - - for (; tpc < 32; tpc++) - data[tpc / 6] |= 7 << ((tpc % 6) * 5); - - /* and the second... */ - shift = 0; - ntpcv = priv->tpc_total; - while (!(ntpcv & (1 << 4))) { - ntpcv <<= 1; - shift++; - } - - data2[0] = ntpcv << 16; - data2[0] |= shift << 21; - data2[0] |= (((1 << (0 + 5)) % ntpcv) << 24); - data2[0] |= priv->tpc_total << 8; - data2[0] |= priv->magic_not_rop_nr; - for (i = 1; i < 7; i++) - data2[1] |= ((1 << (i + 5)) % ntpcv) << ((i - 1) * 5); - - /* and write it all the various parts of PGRAPH */ - nv_wr32(priv, 0x418bb8, (priv->tpc_total << 8) | priv->magic_not_rop_nr); - for (i = 0; i < 6; i++) - nv_wr32(priv, 0x418b08 + (i * 4), data[i]); - - nv_wr32(priv, 0x41bfd0, data2[0]); - nv_wr32(priv, 0x41bfe4, data2[1]); - for (i = 0; i < 6; i++) - nv_wr32(priv, 0x41bf00 + (i * 4), data[i]); - - nv_wr32(priv, 0x4078bc, (priv->tpc_total << 8) | priv->magic_not_rop_nr); - for (i = 0; i < 6; i++) - nv_wr32(priv, 0x40780c + (i * 4), data[i]); - - - memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr)); - for (gpc = 0; gpc < priv->gpc_nr; gpc++) - tpc_mask |= ((1 << priv->tpc_nr[gpc]) - 1) << (gpc * 8); - - for (i = 0, gpc = -1, b = -1; i < 32; i++) { - a = (i * (priv->tpc_total - 1)) / 32; - if (a != b) { - b = a; - do { - gpc = (gpc + 1) % priv->gpc_nr; - } while (!tpcnr[gpc]); - tpc = priv->tpc_nr[gpc] - tpcnr[gpc]--; - - tpc_set |= 1 << ((gpc * 8) + tpc); - } - - nv_wr32(priv, 0x406800 + (i * 0x20), tpc_set); - nv_wr32(priv, 0x406c00 + (i * 0x20), tpc_set ^ tpc_mask); - } - - for (i = 0; i < 8; i++) - nv_wr32(priv, 0x4064d0 + (i * 0x04), 0x00000000); - - nv_wr32(priv, 0x405b00, (priv->tpc_total << 8) | priv->gpc_nr); - if (priv->gpc_nr == 1) { - nv_mask(priv, 0x408850, 0x0000000f, priv->tpc_nr[0]); - nv_mask(priv, 0x408958, 0x0000000f, priv->tpc_nr[0]); - } else { - nv_mask(priv, 0x408850, 0x0000000f, priv->gpc_nr); - nv_mask(priv, 0x408958, 0x0000000f, priv->gpc_nr); - } - nv_mask(priv, 0x419f78, 0x00000001, 0x00000000); - - nve0_grctx_generate_icmd(priv); - nve0_grctx_generate_a097(priv); - nve0_grctx_generate_902d(priv); - - nv_mask(priv, 0x000260, 0x00000001, 0x00000001); - nv_wr32(priv, 0x418800, 0x7026860a); //XXX - nv_wr32(priv, 0x41be10, 0x00bb8bc7); //XXX - return nvc0_grctx_fini(&info); -} diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c new file mode 100644 index 000000000000..4f6041490986 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c @@ -0,0 +1,1033 @@ +/* + * Copyright 2013 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 "nvc0.h" + +struct nvc0_graph_init +nve4_grctx_init_icmd[] = { + { 0x001000, 1, 0x01, 0x00000004 }, + { 0x000039, 3, 0x01, 0x00000000 }, + { 0x0000a9, 1, 0x01, 0x0000ffff }, + { 0x000038, 1, 0x01, 0x0fac6881 }, + { 0x00003d, 1, 0x01, 0x00000001 }, + { 0x0000e8, 8, 0x01, 0x00000400 }, + { 0x000078, 8, 0x01, 0x00000300 }, + { 0x000050, 1, 0x01, 0x00000011 }, + { 0x000058, 8, 0x01, 0x00000008 }, + { 0x000208, 8, 0x01, 0x00000001 }, + { 0x000081, 1, 0x01, 0x00000001 }, + { 0x000085, 1, 0x01, 0x00000004 }, + { 0x000088, 1, 0x01, 0x00000400 }, + { 0x000090, 1, 0x01, 0x00000300 }, + { 0x000098, 1, 0x01, 0x00001001 }, + { 0x0000e3, 1, 0x01, 0x00000001 }, + { 0x0000da, 1, 0x01, 0x00000001 }, + { 0x0000f8, 1, 0x01, 0x00000003 }, + { 0x0000fa, 1, 0x01, 0x00000001 }, + { 0x00009f, 4, 0x01, 0x0000ffff }, + { 0x0000b1, 1, 0x01, 0x00000001 }, + { 0x0000ad, 1, 0x01, 0x0000013e }, + { 0x0000e1, 1, 0x01, 0x00000010 }, + { 0x000290, 16, 0x01, 0x00000000 }, + { 0x0003b0, 16, 0x01, 0x00000000 }, + { 0x0002a0, 16, 0x01, 0x00000000 }, + { 0x000420, 16, 0x01, 0x00000000 }, + { 0x0002b0, 16, 0x01, 0x00000000 }, + { 0x000430, 16, 0x01, 0x00000000 }, + { 0x0002c0, 16, 0x01, 0x00000000 }, + { 0x0004d0, 16, 0x01, 0x00000000 }, + { 0x000720, 16, 0x01, 0x00000000 }, + { 0x0008c0, 16, 0x01, 0x00000000 }, + { 0x000890, 16, 0x01, 0x00000000 }, + { 0x0008e0, 16, 0x01, 0x00000000 }, + { 0x0008a0, 16, 0x01, 0x00000000 }, + { 0x0008f0, 16, 0x01, 0x00000000 }, + { 0x00094c, 1, 0x01, 0x000000ff }, + { 0x00094d, 1, 0x01, 0xffffffff }, + { 0x00094e, 1, 0x01, 0x00000002 }, + { 0x0002ec, 1, 0x01, 0x00000001 }, + { 0x000303, 1, 0x01, 0x00000001 }, + { 0x0002e6, 1, 0x01, 0x00000001 }, + { 0x000466, 1, 0x01, 0x00000052 }, + { 0x000301, 1, 0x01, 0x3f800000 }, + { 0x000304, 1, 0x01, 0x30201000 }, + { 0x000305, 1, 0x01, 0x70605040 }, + { 0x000306, 1, 0x01, 0xb8a89888 }, + { 0x000307, 1, 0x01, 0xf8e8d8c8 }, + { 0x00030a, 1, 0x01, 0x00ffff00 }, + { 0x00030b, 1, 0x01, 0x0000001a }, + { 0x00030c, 1, 0x01, 0x00000001 }, + { 0x000318, 1, 0x01, 0x00000001 }, + { 0x000340, 1, 0x01, 0x00000000 }, + { 0x000375, 1, 0x01, 0x00000001 }, + { 0x00037d, 1, 0x01, 0x00000006 }, + { 0x0003a0, 1, 0x01, 0x00000002 }, + { 0x0003aa, 1, 0x01, 0x00000001 }, + { 0x0003a9, 1, 0x01, 0x00000001 }, + { 0x000380, 1, 0x01, 0x00000001 }, + { 0x000383, 1, 0x01, 0x00000011 }, + { 0x000360, 1, 0x01, 0x00000040 }, + { 0x000366, 2, 0x01, 0x00000000 }, + { 0x000368, 1, 0x01, 0x00000fff }, + { 0x000370, 2, 0x01, 0x00000000 }, + { 0x000372, 1, 0x01, 0x000fffff }, + { 0x00037a, 1, 0x01, 0x00000012 }, + { 0x000619, 1, 0x01, 0x00000003 }, + { 0x000811, 1, 0x01, 0x00000003 }, + { 0x000812, 1, 0x01, 0x00000004 }, + { 0x000813, 1, 0x01, 0x00000006 }, + { 0x000814, 1, 0x01, 0x00000008 }, + { 0x000815, 1, 0x01, 0x0000000b }, + { 0x000800, 6, 0x01, 0x00000001 }, + { 0x000632, 1, 0x01, 0x00000001 }, + { 0x000633, 1, 0x01, 0x00000002 }, + { 0x000634, 1, 0x01, 0x00000003 }, + { 0x000635, 1, 0x01, 0x00000004 }, + { 0x000654, 1, 0x01, 0x3f800000 }, + { 0x000657, 1, 0x01, 0x3f800000 }, + { 0x000655, 2, 0x01, 0x3f800000 }, + { 0x0006cd, 1, 0x01, 0x3f800000 }, + { 0x0007f5, 1, 0x01, 0x3f800000 }, + { 0x0007dc, 1, 0x01, 0x39291909 }, + { 0x0007dd, 1, 0x01, 0x79695949 }, + { 0x0007de, 1, 0x01, 0xb9a99989 }, + { 0x0007df, 1, 0x01, 0xf9e9d9c9 }, + { 0x0007e8, 1, 0x01, 0x00003210 }, + { 0x0007e9, 1, 0x01, 0x00007654 }, + { 0x0007ea, 1, 0x01, 0x00000098 }, + { 0x0007ec, 1, 0x01, 0x39291909 }, + { 0x0007ed, 1, 0x01, 0x79695949 }, + { 0x0007ee, 1, 0x01, 0xb9a99989 }, + { 0x0007ef, 1, 0x01, 0xf9e9d9c9 }, + { 0x0007f0, 1, 0x01, 0x00003210 }, + { 0x0007f1, 1, 0x01, 0x00007654 }, + { 0x0007f2, 1, 0x01, 0x00000098 }, + { 0x0005a5, 1, 0x01, 0x00000001 }, + { 0x000980, 128, 0x01, 0x00000000 }, + { 0x000468, 1, 0x01, 0x00000004 }, + { 0x00046c, 1, 0x01, 0x00000001 }, + { 0x000470, 96, 0x01, 0x00000000 }, + { 0x000510, 16, 0x01, 0x3f800000 }, + { 0x000520, 1, 0x01, 0x000002b6 }, + { 0x000529, 1, 0x01, 0x00000001 }, + { 0x000530, 16, 0x01, 0xffff0000 }, + { 0x000585, 1, 0x01, 0x0000003f }, + { 0x000576, 1, 0x01, 0x00000003 }, + { 0x00057b, 1, 0x01, 0x00000059 }, + { 0x000586, 1, 0x01, 0x00000040 }, + { 0x000582, 2, 0x01, 0x00000080 }, + { 0x0005c2, 1, 0x01, 0x00000001 }, + { 0x000638, 1, 0x01, 0x00000001 }, + { 0x000639, 1, 0x01, 0x00000001 }, + { 0x00063a, 1, 0x01, 0x00000002 }, + { 0x00063b, 2, 0x01, 0x00000001 }, + { 0x00063d, 1, 0x01, 0x00000002 }, + { 0x00063e, 1, 0x01, 0x00000001 }, + { 0x0008b8, 8, 0x01, 0x00000001 }, + { 0x000900, 8, 0x01, 0x00000001 }, + { 0x000908, 8, 0x01, 0x00000002 }, + { 0x000910, 16, 0x01, 0x00000001 }, + { 0x000920, 8, 0x01, 0x00000002 }, + { 0x000928, 8, 0x01, 0x00000001 }, + { 0x000648, 9, 0x01, 0x00000001 }, + { 0x000658, 1, 0x01, 0x0000000f }, + { 0x0007ff, 1, 0x01, 0x0000000a }, + { 0x00066a, 1, 0x01, 0x40000000 }, + { 0x00066b, 1, 0x01, 0x10000000 }, + { 0x00066c, 2, 0x01, 0xffff0000 }, + { 0x0007af, 2, 0x01, 0x00000008 }, + { 0x0007f6, 1, 0x01, 0x00000001 }, + { 0x0006b2, 1, 0x01, 0x00000055 }, + { 0x0007ad, 1, 0x01, 0x00000003 }, + { 0x000937, 1, 0x01, 0x00000001 }, + { 0x000971, 1, 0x01, 0x00000008 }, + { 0x000972, 1, 0x01, 0x00000040 }, + { 0x000973, 1, 0x01, 0x0000012c }, + { 0x00097c, 1, 0x01, 0x00000040 }, + { 0x000979, 1, 0x01, 0x00000003 }, + { 0x000975, 1, 0x01, 0x00000020 }, + { 0x000976, 1, 0x01, 0x00000001 }, + { 0x000977, 1, 0x01, 0x00000020 }, + { 0x000978, 1, 0x01, 0x00000001 }, + { 0x000957, 1, 0x01, 0x00000003 }, + { 0x00095e, 1, 0x01, 0x20164010 }, + { 0x00095f, 1, 0x01, 0x00000020 }, + { 0x00097d, 1, 0x01, 0x00000020 }, + { 0x000683, 1, 0x01, 0x00000006 }, + { 0x000685, 1, 0x01, 0x003fffff }, + { 0x000687, 1, 0x01, 0x003fffff }, + { 0x0006a0, 1, 0x01, 0x00000005 }, + { 0x000840, 1, 0x01, 0x00400008 }, + { 0x000841, 1, 0x01, 0x08000080 }, + { 0x000842, 1, 0x01, 0x00400008 }, + { 0x000843, 1, 0x01, 0x08000080 }, + { 0x0006aa, 1, 0x01, 0x00000001 }, + { 0x0006ab, 1, 0x01, 0x00000002 }, + { 0x0006ac, 1, 0x01, 0x00000080 }, + { 0x0006ad, 2, 0x01, 0x00000100 }, + { 0x0006b1, 1, 0x01, 0x00000011 }, + { 0x0006bb, 1, 0x01, 0x000000cf }, + { 0x0006ce, 1, 0x01, 0x2a712488 }, + { 0x000739, 1, 0x01, 0x4085c000 }, + { 0x00073a, 1, 0x01, 0x00000080 }, + { 0x000786, 1, 0x01, 0x80000100 }, + { 0x00073c, 1, 0x01, 0x00010100 }, + { 0x00073d, 1, 0x01, 0x02800000 }, + { 0x000787, 1, 0x01, 0x000000cf }, + { 0x00078c, 1, 0x01, 0x00000008 }, + { 0x000792, 1, 0x01, 0x00000001 }, + { 0x000794, 1, 0x01, 0x00000001 }, + { 0x000795, 2, 0x01, 0x00000001 }, + { 0x000797, 1, 0x01, 0x000000cf }, + { 0x000836, 1, 0x01, 0x00000001 }, + { 0x00079a, 1, 0x01, 0x00000002 }, + { 0x000833, 1, 0x01, 0x04444480 }, + { 0x0007a1, 1, 0x01, 0x00000001 }, + { 0x0007a3, 1, 0x01, 0x00000001 }, + { 0x0007a4, 2, 0x01, 0x00000001 }, + { 0x000831, 1, 0x01, 0x00000004 }, + { 0x000b07, 1, 0x01, 0x00000002 }, + { 0x000b08, 2, 0x01, 0x00000100 }, + { 0x000b0a, 1, 0x01, 0x00000001 }, + { 0x000a04, 1, 0x01, 0x000000ff }, + { 0x000a0b, 1, 0x01, 0x00000040 }, + { 0x00097f, 1, 0x01, 0x00000100 }, + { 0x000a02, 1, 0x01, 0x00000001 }, + { 0x000809, 1, 0x01, 0x00000007 }, + { 0x00c221, 1, 0x01, 0x00000040 }, + { 0x00c1b0, 8, 0x01, 0x0000000f }, + { 0x00c1b8, 1, 0x01, 0x0fac6881 }, + { 0x00c1b9, 1, 0x01, 0x00fac688 }, + { 0x00c401, 1, 0x01, 0x00000001 }, + { 0x00c402, 1, 0x01, 0x00010001 }, + { 0x00c403, 2, 0x01, 0x00000001 }, + { 0x00c40e, 1, 0x01, 0x00000020 }, + { 0x00c500, 1, 0x01, 0x00000003 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + { 0x001000, 1, 0x01, 0x00000002 }, + { 0x0006aa, 1, 0x01, 0x00000001 }, + { 0x0006ad, 2, 0x01, 0x00000100 }, + { 0x0006b1, 1, 0x01, 0x00000011 }, + { 0x00078c, 1, 0x01, 0x00000008 }, + { 0x000792, 1, 0x01, 0x00000001 }, + { 0x000794, 1, 0x01, 0x00000001 }, + { 0x000795, 2, 0x01, 0x00000001 }, + { 0x000797, 1, 0x01, 0x000000cf }, + { 0x00079a, 1, 0x01, 0x00000002 }, + { 0x000833, 1, 0x01, 0x04444480 }, + { 0x0007a1, 1, 0x01, 0x00000001 }, + { 0x0007a3, 1, 0x01, 0x00000001 }, + { 0x0007a4, 2, 0x01, 0x00000001 }, + { 0x000831, 1, 0x01, 0x00000004 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + { 0x001000, 1, 0x01, 0x00000008 }, + { 0x000039, 3, 0x01, 0x00000000 }, + { 0x000380, 1, 0x01, 0x00000001 }, + { 0x000366, 2, 0x01, 0x00000000 }, + { 0x000368, 1, 0x01, 0x00000fff }, + { 0x000370, 2, 0x01, 0x00000000 }, + { 0x000372, 1, 0x01, 0x000fffff }, + { 0x000813, 1, 0x01, 0x00000006 }, + { 0x000814, 1, 0x01, 0x00000008 }, + { 0x000957, 1, 0x01, 0x00000003 }, + { 0x000b07, 1, 0x01, 0x00000002 }, + { 0x000b08, 2, 0x01, 0x00000100 }, + { 0x000b0a, 1, 0x01, 0x00000001 }, + { 0x000a04, 1, 0x01, 0x000000ff }, + { 0x00097f, 1, 0x01, 0x00000100 }, + { 0x000a02, 1, 0x01, 0x00000001 }, + { 0x000809, 1, 0x01, 0x00000007 }, + { 0x00c221, 1, 0x01, 0x00000040 }, + { 0x00c401, 1, 0x01, 0x00000001 }, + { 0x00c402, 1, 0x01, 0x00010001 }, + { 0x00c403, 2, 0x01, 0x00000001 }, + { 0x00c40e, 1, 0x01, 0x00000020 }, + { 0x00c500, 1, 0x01, 0x00000003 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + { 0x001000, 1, 0x01, 0x00000001 }, + { 0x000b07, 1, 0x01, 0x00000002 }, + { 0x000b08, 2, 0x01, 0x00000100 }, + { 0x000b0a, 1, 0x01, 0x00000001 }, + { 0x01e100, 1, 0x01, 0x00000001 }, + {} +}; + +struct nvc0_graph_init +nve4_grctx_init_a097[] = { + { 0x000800, 8, 0x40, 0x00000000 }, + { 0x000804, 8, 0x40, 0x00000000 }, + { 0x000808, 8, 0x40, 0x00000400 }, + { 0x00080c, 8, 0x40, 0x00000300 }, + { 0x000810, 1, 0x04, 0x000000cf }, + { 0x000850, 7, 0x40, 0x00000000 }, + { 0x000814, 8, 0x40, 0x00000040 }, + { 0x000818, 8, 0x40, 0x00000001 }, + { 0x00081c, 8, 0x40, 0x00000000 }, + { 0x000820, 8, 0x40, 0x00000000 }, + { 0x001c00, 16, 0x10, 0x00000000 }, + { 0x001c04, 16, 0x10, 0x00000000 }, + { 0x001c08, 16, 0x10, 0x00000000 }, + { 0x001c0c, 16, 0x10, 0x00000000 }, + { 0x001d00, 16, 0x10, 0x00000000 }, + { 0x001d04, 16, 0x10, 0x00000000 }, + { 0x001d08, 16, 0x10, 0x00000000 }, + { 0x001d0c, 16, 0x10, 0x00000000 }, + { 0x001f00, 16, 0x08, 0x00000000 }, + { 0x001f04, 16, 0x08, 0x00000000 }, + { 0x001f80, 16, 0x08, 0x00000000 }, + { 0x001f84, 16, 0x08, 0x00000000 }, + { 0x002000, 1, 0x04, 0x00000000 }, + { 0x002040, 1, 0x04, 0x00000011 }, + { 0x002080, 1, 0x04, 0x00000020 }, + { 0x0020c0, 1, 0x04, 0x00000030 }, + { 0x002100, 1, 0x04, 0x00000040 }, + { 0x002140, 1, 0x04, 0x00000051 }, + { 0x00200c, 6, 0x40, 0x00000001 }, + { 0x002010, 1, 0x04, 0x00000000 }, + { 0x002050, 1, 0x04, 0x00000000 }, + { 0x002090, 1, 0x04, 0x00000001 }, + { 0x0020d0, 1, 0x04, 0x00000002 }, + { 0x002110, 1, 0x04, 0x00000003 }, + { 0x002150, 1, 0x04, 0x00000004 }, + { 0x000380, 4, 0x20, 0x00000000 }, + { 0x000384, 4, 0x20, 0x00000000 }, + { 0x000388, 4, 0x20, 0x00000000 }, + { 0x00038c, 4, 0x20, 0x00000000 }, + { 0x000700, 4, 0x10, 0x00000000 }, + { 0x000704, 4, 0x10, 0x00000000 }, + { 0x000708, 4, 0x10, 0x00000000 }, + { 0x002800, 128, 0x04, 0x00000000 }, + { 0x000a00, 16, 0x20, 0x00000000 }, + { 0x000a04, 16, 0x20, 0x00000000 }, + { 0x000a08, 16, 0x20, 0x00000000 }, + { 0x000a0c, 16, 0x20, 0x00000000 }, + { 0x000a10, 16, 0x20, 0x00000000 }, + { 0x000a14, 16, 0x20, 0x00000000 }, + { 0x000c00, 16, 0x10, 0x00000000 }, + { 0x000c04, 16, 0x10, 0x00000000 }, + { 0x000c08, 16, 0x10, 0x00000000 }, + { 0x000c0c, 16, 0x10, 0x3f800000 }, + { 0x000d00, 8, 0x08, 0xffff0000 }, + { 0x000d04, 8, 0x08, 0xffff0000 }, + { 0x000e00, 16, 0x10, 0x00000000 }, + { 0x000e04, 16, 0x10, 0xffff0000 }, + { 0x000e08, 16, 0x10, 0xffff0000 }, + { 0x000d40, 4, 0x08, 0x00000000 }, + { 0x000d44, 4, 0x08, 0x00000000 }, + { 0x001e00, 8, 0x20, 0x00000001 }, + { 0x001e04, 8, 0x20, 0x00000001 }, + { 0x001e08, 8, 0x20, 0x00000002 }, + { 0x001e0c, 8, 0x20, 0x00000001 }, + { 0x001e10, 8, 0x20, 0x00000001 }, + { 0x001e14, 8, 0x20, 0x00000002 }, + { 0x001e18, 8, 0x20, 0x00000001 }, + { 0x003400, 128, 0x04, 0x00000000 }, + { 0x00030c, 1, 0x04, 0x00000001 }, + { 0x001944, 1, 0x04, 0x00000000 }, + { 0x001514, 1, 0x04, 0x00000000 }, + { 0x000d68, 1, 0x04, 0x0000ffff }, + { 0x00121c, 1, 0x04, 0x0fac6881 }, + { 0x000fac, 1, 0x04, 0x00000001 }, + { 0x001538, 1, 0x04, 0x00000001 }, + { 0x000fe0, 2, 0x04, 0x00000000 }, + { 0x000fe8, 1, 0x04, 0x00000014 }, + { 0x000fec, 1, 0x04, 0x00000040 }, + { 0x000ff0, 1, 0x04, 0x00000000 }, + { 0x00179c, 1, 0x04, 0x00000000 }, + { 0x001228, 1, 0x04, 0x00000400 }, + { 0x00122c, 1, 0x04, 0x00000300 }, + { 0x001230, 1, 0x04, 0x00010001 }, + { 0x0007f8, 1, 0x04, 0x00000000 }, + { 0x0015b4, 1, 0x04, 0x00000001 }, + { 0x0015cc, 1, 0x04, 0x00000000 }, + { 0x001534, 1, 0x04, 0x00000000 }, + { 0x000fb0, 1, 0x04, 0x00000000 }, + { 0x0015d0, 1, 0x04, 0x00000000 }, + { 0x00153c, 1, 0x04, 0x00000000 }, + { 0x0016b4, 1, 0x04, 0x00000003 }, + { 0x000fbc, 4, 0x04, 0x0000ffff }, + { 0x000df8, 2, 0x04, 0x00000000 }, + { 0x001948, 1, 0x04, 0x00000000 }, + { 0x001970, 1, 0x04, 0x00000001 }, + { 0x00161c, 1, 0x04, 0x000009f0 }, + { 0x000dcc, 1, 0x04, 0x00000010 }, + { 0x00163c, 1, 0x04, 0x00000000 }, + { 0x0015e4, 1, 0x04, 0x00000000 }, + { 0x001160, 32, 0x04, 0x25e00040 }, + { 0x001880, 32, 0x04, 0x00000000 }, + { 0x000f84, 2, 0x04, 0x00000000 }, + { 0x0017c8, 2, 0x04, 0x00000000 }, + { 0x0017d0, 1, 0x04, 0x000000ff }, + { 0x0017d4, 1, 0x04, 0xffffffff }, + { 0x0017d8, 1, 0x04, 0x00000002 }, + { 0x0017dc, 1, 0x04, 0x00000000 }, + { 0x0015f4, 2, 0x04, 0x00000000 }, + { 0x001434, 2, 0x04, 0x00000000 }, + { 0x000d74, 1, 0x04, 0x00000000 }, + { 0x000dec, 1, 0x04, 0x00000001 }, + { 0x0013a4, 1, 0x04, 0x00000000 }, + { 0x001318, 1, 0x04, 0x00000001 }, + { 0x001644, 1, 0x04, 0x00000000 }, + { 0x000748, 1, 0x04, 0x00000000 }, + { 0x000de8, 1, 0x04, 0x00000000 }, + { 0x001648, 1, 0x04, 0x00000000 }, + { 0x0012a4, 1, 0x04, 0x00000000 }, + { 0x001120, 4, 0x04, 0x00000000 }, + { 0x001118, 1, 0x04, 0x00000000 }, + { 0x00164c, 1, 0x04, 0x00000000 }, + { 0x001658, 1, 0x04, 0x00000000 }, + { 0x001910, 1, 0x04, 0x00000290 }, + { 0x001518, 1, 0x04, 0x00000000 }, + { 0x00165c, 1, 0x04, 0x00000001 }, + { 0x001520, 1, 0x04, 0x00000000 }, + { 0x001604, 1, 0x04, 0x00000000 }, + { 0x001570, 1, 0x04, 0x00000000 }, + { 0x0013b0, 2, 0x04, 0x3f800000 }, + { 0x00020c, 1, 0x04, 0x00000000 }, + { 0x001670, 1, 0x04, 0x30201000 }, + { 0x001674, 1, 0x04, 0x70605040 }, + { 0x001678, 1, 0x04, 0xb8a89888 }, + { 0x00167c, 1, 0x04, 0xf8e8d8c8 }, + { 0x00166c, 1, 0x04, 0x00000000 }, + { 0x001680, 1, 0x04, 0x00ffff00 }, + { 0x0012d0, 1, 0x04, 0x00000003 }, + { 0x0012d4, 1, 0x04, 0x00000002 }, + { 0x001684, 2, 0x04, 0x00000000 }, + { 0x000dac, 2, 0x04, 0x00001b02 }, + { 0x000db4, 1, 0x04, 0x00000000 }, + { 0x00168c, 1, 0x04, 0x00000000 }, + { 0x0015bc, 1, 0x04, 0x00000000 }, + { 0x00156c, 1, 0x04, 0x00000000 }, + { 0x00187c, 1, 0x04, 0x00000000 }, + { 0x001110, 1, 0x04, 0x00000001 }, + { 0x000dc0, 3, 0x04, 0x00000000 }, + { 0x001234, 1, 0x04, 0x00000000 }, + { 0x001690, 1, 0x04, 0x00000000 }, + { 0x0012ac, 1, 0x04, 0x00000001 }, + { 0x000790, 5, 0x04, 0x00000000 }, + { 0x00077c, 1, 0x04, 0x00000000 }, + { 0x001000, 1, 0x04, 0x00000010 }, + { 0x0010fc, 1, 0x04, 0x00000000 }, + { 0x001290, 1, 0x04, 0x00000000 }, + { 0x000218, 1, 0x04, 0x00000010 }, + { 0x0012d8, 1, 0x04, 0x00000000 }, + { 0x0012dc, 1, 0x04, 0x00000010 }, + { 0x000d94, 1, 0x04, 0x00000001 }, + { 0x00155c, 2, 0x04, 0x00000000 }, + { 0x001564, 1, 0x04, 0x00000fff }, + { 0x001574, 2, 0x04, 0x00000000 }, + { 0x00157c, 1, 0x04, 0x000fffff }, + { 0x001354, 1, 0x04, 0x00000000 }, + { 0x001610, 1, 0x04, 0x00000012 }, + { 0x001608, 2, 0x04, 0x00000000 }, + { 0x00260c, 1, 0x04, 0x00000000 }, + { 0x0007ac, 1, 0x04, 0x00000000 }, + { 0x00162c, 1, 0x04, 0x00000003 }, + { 0x000210, 1, 0x04, 0x00000000 }, + { 0x000320, 1, 0x04, 0x00000000 }, + { 0x000324, 6, 0x04, 0x3f800000 }, + { 0x000750, 1, 0x04, 0x00000000 }, + { 0x000760, 1, 0x04, 0x39291909 }, + { 0x000764, 1, 0x04, 0x79695949 }, + { 0x000768, 1, 0x04, 0xb9a99989 }, + { 0x00076c, 1, 0x04, 0xf9e9d9c9 }, + { 0x000770, 1, 0x04, 0x30201000 }, + { 0x000774, 1, 0x04, 0x70605040 }, + { 0x000778, 1, 0x04, 0x00009080 }, + { 0x000780, 1, 0x04, 0x39291909 }, + { 0x000784, 1, 0x04, 0x79695949 }, + { 0x000788, 1, 0x04, 0xb9a99989 }, + { 0x00078c, 1, 0x04, 0xf9e9d9c9 }, + { 0x0007d0, 1, 0x04, 0x30201000 }, + { 0x0007d4, 1, 0x04, 0x70605040 }, + { 0x0007d8, 1, 0x04, 0x00009080 }, + { 0x00037c, 1, 0x04, 0x00000001 }, + { 0x000740, 2, 0x04, 0x00000000 }, + { 0x002600, 1, 0x04, 0x00000000 }, + { 0x001918, 1, 0x04, 0x00000000 }, + { 0x00191c, 1, 0x04, 0x00000900 }, + { 0x001920, 1, 0x04, 0x00000405 }, + { 0x001308, 1, 0x04, 0x00000001 }, + { 0x001924, 1, 0x04, 0x00000000 }, + { 0x0013ac, 1, 0x04, 0x00000000 }, + { 0x00192c, 1, 0x04, 0x00000001 }, + { 0x00193c, 1, 0x04, 0x00002c1c }, + { 0x000d7c, 1, 0x04, 0x00000000 }, + { 0x000f8c, 1, 0x04, 0x00000000 }, + { 0x0002c0, 1, 0x04, 0x00000001 }, + { 0x001510, 1, 0x04, 0x00000000 }, + { 0x001940, 1, 0x04, 0x00000000 }, + { 0x000ff4, 2, 0x04, 0x00000000 }, + { 0x00194c, 2, 0x04, 0x00000000 }, + { 0x001968, 1, 0x04, 0x00000000 }, + { 0x001590, 1, 0x04, 0x0000003f }, + { 0x0007e8, 4, 0x04, 0x00000000 }, + { 0x00196c, 1, 0x04, 0x00000011 }, + { 0x0002e4, 1, 0x04, 0x0000b001 }, + { 0x00036c, 2, 0x04, 0x00000000 }, + { 0x00197c, 1, 0x04, 0x00000000 }, + { 0x000fcc, 2, 0x04, 0x00000000 }, + { 0x0002d8, 1, 0x04, 0x00000040 }, + { 0x001980, 1, 0x04, 0x00000080 }, + { 0x001504, 1, 0x04, 0x00000080 }, + { 0x001984, 1, 0x04, 0x00000000 }, + { 0x000300, 1, 0x04, 0x00000001 }, + { 0x0013a8, 1, 0x04, 0x00000000 }, + { 0x0012ec, 1, 0x04, 0x00000000 }, + { 0x001310, 1, 0x04, 0x00000000 }, + { 0x001314, 1, 0x04, 0x00000001 }, + { 0x001380, 1, 0x04, 0x00000000 }, + { 0x001384, 4, 0x04, 0x00000001 }, + { 0x001394, 1, 0x04, 0x00000000 }, + { 0x00139c, 1, 0x04, 0x00000000 }, + { 0x001398, 1, 0x04, 0x00000000 }, + { 0x001594, 1, 0x04, 0x00000000 }, + { 0x001598, 4, 0x04, 0x00000001 }, + { 0x000f54, 3, 0x04, 0x00000000 }, + { 0x0019bc, 1, 0x04, 0x00000000 }, + { 0x000f9c, 2, 0x04, 0x00000000 }, + { 0x0012cc, 1, 0x04, 0x00000000 }, + { 0x0012e8, 1, 0x04, 0x00000000 }, + { 0x00130c, 1, 0x04, 0x00000001 }, + { 0x001360, 8, 0x04, 0x00000000 }, + { 0x00133c, 2, 0x04, 0x00000001 }, + { 0x001344, 1, 0x04, 0x00000002 }, + { 0x001348, 2, 0x04, 0x00000001 }, + { 0x001350, 1, 0x04, 0x00000002 }, + { 0x001358, 1, 0x04, 0x00000001 }, + { 0x0012e4, 1, 0x04, 0x00000000 }, + { 0x00131c, 1, 0x04, 0x00000000 }, + { 0x001320, 3, 0x04, 0x00000000 }, + { 0x0019c0, 1, 0x04, 0x00000000 }, + { 0x001140, 1, 0x04, 0x00000000 }, + { 0x0019c4, 1, 0x04, 0x00000000 }, + { 0x0019c8, 1, 0x04, 0x00001500 }, + { 0x00135c, 1, 0x04, 0x00000000 }, + { 0x000f90, 1, 0x04, 0x00000000 }, + { 0x0019e0, 8, 0x04, 0x00000001 }, + { 0x0019cc, 1, 0x04, 0x00000001 }, + { 0x0015b8, 1, 0x04, 0x00000000 }, + { 0x001a00, 1, 0x04, 0x00001111 }, + { 0x001a04, 7, 0x04, 0x00000000 }, + { 0x000d6c, 2, 0x04, 0xffff0000 }, + { 0x0010f8, 1, 0x04, 0x00001010 }, + { 0x000d80, 5, 0x04, 0x00000000 }, + { 0x000da0, 1, 0x04, 0x00000000 }, + { 0x0007a4, 2, 0x04, 0x00000000 }, + { 0x001508, 1, 0x04, 0x80000000 }, + { 0x00150c, 1, 0x04, 0x40000000 }, + { 0x001668, 1, 0x04, 0x00000000 }, + { 0x000318, 2, 0x04, 0x00000008 }, + { 0x000d9c, 1, 0x04, 0x00000001 }, + { 0x000374, 1, 0x04, 0x00000000 }, + { 0x000378, 1, 0x04, 0x00000020 }, + { 0x0007dc, 1, 0x04, 0x00000000 }, + { 0x00074c, 1, 0x04, 0x00000055 }, + { 0x001420, 1, 0x04, 0x00000003 }, + { 0x0017bc, 2, 0x04, 0x00000000 }, + { 0x0017c4, 1, 0x04, 0x00000001 }, + { 0x001008, 1, 0x04, 0x00000008 }, + { 0x00100c, 1, 0x04, 0x00000040 }, + { 0x001010, 1, 0x04, 0x0000012c }, + { 0x000d60, 1, 0x04, 0x00000040 }, + { 0x00075c, 1, 0x04, 0x00000003 }, + { 0x001018, 1, 0x04, 0x00000020 }, + { 0x00101c, 1, 0x04, 0x00000001 }, + { 0x001020, 1, 0x04, 0x00000020 }, + { 0x001024, 1, 0x04, 0x00000001 }, + { 0x001444, 3, 0x04, 0x00000000 }, + { 0x000360, 1, 0x04, 0x20164010 }, + { 0x000364, 1, 0x04, 0x00000020 }, + { 0x000368, 1, 0x04, 0x00000000 }, + { 0x000de4, 1, 0x04, 0x00000000 }, + { 0x000204, 1, 0x04, 0x00000006 }, + { 0x000208, 1, 0x04, 0x00000000 }, + { 0x0002cc, 2, 0x04, 0x003fffff }, + { 0x001220, 1, 0x04, 0x00000005 }, + { 0x000fdc, 1, 0x04, 0x00000000 }, + { 0x000f98, 1, 0x04, 0x00400008 }, + { 0x001284, 1, 0x04, 0x08000080 }, + { 0x001450, 1, 0x04, 0x00400008 }, + { 0x001454, 1, 0x04, 0x08000080 }, + { 0x000214, 1, 0x04, 0x00000000 }, + {} +}; + +static struct nvc0_graph_init +nve4_grctx_init_unk40xx[] = { + { 0x404010, 5, 0x04, 0x00000000 }, + { 0x404024, 1, 0x04, 0x0000e000 }, + { 0x404028, 1, 0x04, 0x00000000 }, + { 0x4040a8, 1, 0x04, 0x00000000 }, + { 0x4040ac, 7, 0x04, 0x00000000 }, + { 0x4040c8, 1, 0x04, 0xf800008f }, + { 0x4040d0, 6, 0x04, 0x00000000 }, + { 0x4040e8, 1, 0x04, 0x00001000 }, + { 0x4040f8, 1, 0x04, 0x00000000 }, + { 0x404130, 1, 0x04, 0x00000000 }, + { 0x404134, 1, 0x04, 0x00000000 }, + { 0x404138, 1, 0x04, 0x20000040 }, + { 0x404150, 1, 0x04, 0x0000002e }, + { 0x404154, 1, 0x04, 0x00000400 }, + { 0x404158, 1, 0x04, 0x00000200 }, + { 0x404164, 1, 0x04, 0x00000055 }, + { 0x4041a0, 4, 0x04, 0x00000000 }, + { 0x404200, 4, 0x04, 0x00000000 }, + {} +}; + +struct nvc0_graph_init +nve4_grctx_init_unk46xx[] = { + { 0x404604, 1, 0x04, 0x00000014 }, + { 0x404608, 1, 0x04, 0x00000000 }, + { 0x40460c, 1, 0x04, 0x00003fff }, + { 0x404610, 1, 0x04, 0x00000100 }, + { 0x404618, 4, 0x04, 0x00000000 }, + { 0x40462c, 2, 0x04, 0x00000000 }, + { 0x404640, 1, 0x04, 0x00000000 }, + { 0x404654, 1, 0x04, 0x00000000 }, + { 0x404660, 1, 0x04, 0x00000000 }, + { 0x404678, 1, 0x04, 0x00000000 }, + { 0x40467c, 1, 0x04, 0x00000002 }, + { 0x404680, 8, 0x04, 0x00000000 }, + { 0x4046a0, 1, 0x04, 0x007f0080 }, + { 0x4046a4, 8, 0x04, 0x00000000 }, + { 0x4046c8, 3, 0x04, 0x00000000 }, + {} +}; + +struct nvc0_graph_init +nve4_grctx_init_unk47xx[] = { + { 0x404700, 3, 0x04, 0x00000000 }, + { 0x404718, 7, 0x04, 0x00000000 }, + { 0x404734, 1, 0x04, 0x00000100 }, + { 0x404738, 2, 0x04, 0x00000000 }, + { 0x404744, 2, 0x04, 0x00000000 }, + { 0x404754, 1, 0x04, 0x00000000 }, + {} +}; + +struct nvc0_graph_init +nve4_grctx_init_unk58xx[] = { + { 0x405800, 1, 0x04, 0x0f8000bf }, + { 0x405830, 1, 0x04, 0x02180648 }, + { 0x405834, 1, 0x04, 0x08000000 }, + { 0x405838, 1, 0x04, 0x00000000 }, + { 0x405854, 1, 0x04, 0x00000000 }, + { 0x405870, 4, 0x04, 0x00000001 }, + { 0x405a00, 2, 0x04, 0x00000000 }, + { 0x405a18, 1, 0x04, 0x00000000 }, + {} +}; + +static struct nvc0_graph_init +nve4_grctx_init_unk5bxx[] = { + { 0x405b00, 1, 0x04, 0x00000000 }, + { 0x405b10, 1, 0x04, 0x00001000 }, + {} +}; + +static struct nvc0_graph_init +nve4_grctx_init_unk60xx[] = { + { 0x406020, 1, 0x04, 0x004103c1 }, + { 0x406028, 4, 0x04, 0x00000001 }, + {} +}; + +static struct nvc0_graph_init +nve4_grctx_init_unk64xx[] = { + { 0x4064a8, 1, 0x04, 0x00000000 }, + { 0x4064ac, 1, 0x04, 0x00003fff }, + { 0x4064b4, 2, 0x04, 0x00000000 }, + { 0x4064c0, 1, 0x04, 0x801a00f0 }, + { 0x4064c4, 1, 0x04, 0x0192ffff }, + { 0x4064c8, 1, 0x04, 0x01800600 }, + { 0x4064cc, 9, 0x04, 0x00000000 }, + { 0x4064fc, 1, 0x04, 0x0000022a }, + {} +}; + +static struct nvc0_graph_init +nve4_grctx_init_unk70xx[] = { + { 0x407040, 1, 0x04, 0x00000000 }, + {} +}; + +struct nvc0_graph_init +nve4_grctx_init_unk80xx[] = { + { 0x408000, 2, 0x04, 0x00000000 }, + { 0x408008, 1, 0x04, 0x00000030 }, + { 0x40800c, 2, 0x04, 0x00000000 }, + { 0x408014, 1, 0x04, 0x00000069 }, + { 0x408018, 1, 0x04, 0xe100e100 }, + { 0x408064, 1, 0x04, 0x00000000 }, + {} +}; + +static struct nvc0_graph_init +nve4_grctx_init_rop[] = { + { 0x408800, 1, 0x04, 0x02802a3c }, + { 0x408804, 1, 0x04, 0x00000040 }, + { 0x408808, 1, 0x04, 0x1043e005 }, + { 0x408840, 1, 0x04, 0x0000000b }, + { 0x408900, 1, 0x04, 0x3080b801 }, + { 0x408904, 1, 0x04, 0x62000001 }, + { 0x408908, 1, 0x04, 0x00c8102f }, + { 0x408980, 1, 0x04, 0x0000011d }, + {} +}; + +static struct nvc0_graph_init +nve4_grctx_init_gpc[] = { + { 0x418380, 1, 0x04, 0x00000016 }, + { 0x418400, 1, 0x04, 0x38004e00 }, + { 0x418404, 1, 0x04, 0x71e0ffff }, + { 0x41840c, 1, 0x04, 0x00001008 }, + { 0x418410, 1, 0x04, 0x0fff0fff }, + { 0x418414, 1, 0x04, 0x02200fff }, + { 0x418450, 6, 0x04, 0x00000000 }, + { 0x418468, 1, 0x04, 0x00000001 }, + { 0x41846c, 2, 0x04, 0x00000000 }, + { 0x418600, 1, 0x04, 0x0000001f }, + { 0x418684, 1, 0x04, 0x0000000f }, + { 0x418700, 1, 0x04, 0x00000002 }, + { 0x418704, 1, 0x04, 0x00000080 }, + { 0x418708, 3, 0x04, 0x00000000 }, + { 0x418800, 1, 0x04, 0x7006860a }, + { 0x418808, 3, 0x04, 0x00000000 }, + { 0x418828, 1, 0x04, 0x00000044 }, + { 0x418830, 1, 0x04, 0x10000001 }, + { 0x4188d8, 1, 0x04, 0x00000008 }, + { 0x4188e0, 1, 0x04, 0x01000000 }, + { 0x4188e8, 5, 0x04, 0x00000000 }, + { 0x4188fc, 1, 0x04, 0x20100018 }, + { 0x41891c, 1, 0x04, 0x00ff00ff }, + { 0x418924, 1, 0x04, 0x00000000 }, + { 0x418928, 1, 0x04, 0x00ffff00 }, + { 0x41892c, 1, 0x04, 0x0000ff00 }, + { 0x418a00, 3, 0x04, 0x00000000 }, + { 0x418a0c, 1, 0x04, 0x00010000 }, + { 0x418a10, 3, 0x04, 0x00000000 }, + { 0x418a20, 3, 0x04, 0x00000000 }, + { 0x418a2c, 1, 0x04, 0x00010000 }, + { 0x418a30, 3, 0x04, 0x00000000 }, + { 0x418a40, 3, 0x04, 0x00000000 }, + { 0x418a4c, 1, 0x04, 0x00010000 }, + { 0x418a50, 3, 0x04, 0x00000000 }, + { 0x418a60, 3, 0x04, 0x00000000 }, + { 0x418a6c, 1, 0x04, 0x00010000 }, + { 0x418a70, 3, 0x04, 0x00000000 }, + { 0x418a80, 3, 0x04, 0x00000000 }, + { 0x418a8c, 1, 0x04, 0x00010000 }, + { 0x418a90, 3, 0x04, 0x00000000 }, + { 0x418aa0, 3, 0x04, 0x00000000 }, + { 0x418aac, 1, 0x04, 0x00010000 }, + { 0x418ab0, 3, 0x04, 0x00000000 }, + { 0x418ac0, 3, 0x04, 0x00000000 }, + { 0x418acc, 1, 0x04, 0x00010000 }, + { 0x418ad0, 3, 0x04, 0x00000000 }, + { 0x418ae0, 3, 0x04, 0x00000000 }, + { 0x418aec, 1, 0x04, 0x00010000 }, + { 0x418af0, 3, 0x04, 0x00000000 }, + { 0x418b00, 1, 0x04, 0x00000006 }, + { 0x418b08, 1, 0x04, 0x0a418820 }, + { 0x418b0c, 1, 0x04, 0x062080e6 }, + { 0x418b10, 1, 0x04, 0x020398a4 }, + { 0x418b14, 1, 0x04, 0x0e629062 }, + { 0x418b18, 1, 0x04, 0x0a418820 }, + { 0x418b1c, 1, 0x04, 0x000000e6 }, + { 0x418bb8, 1, 0x04, 0x00000103 }, + { 0x418c08, 1, 0x04, 0x00000001 }, + { 0x418c10, 8, 0x04, 0x00000000 }, + { 0x418c40, 1, 0x04, 0xffffffff }, + { 0x418c6c, 1, 0x04, 0x00000001 }, + { 0x418c80, 1, 0x04, 0x20200004 }, + { 0x418c8c, 1, 0x04, 0x00000001 }, + { 0x419000, 1, 0x04, 0x00000780 }, + { 0x419004, 2, 0x04, 0x00000000 }, + { 0x419014, 1, 0x04, 0x00000004 }, + {} +}; + +static struct nvc0_graph_init +nve4_grctx_init_tpc[] = { + { 0x419848, 1, 0x04, 0x00000000 }, + { 0x419864, 1, 0x04, 0x00000129 }, + { 0x419888, 1, 0x04, 0x00000000 }, + { 0x419a00, 1, 0x04, 0x000000f0 }, + { 0x419a04, 1, 0x04, 0x00000001 }, + { 0x419a08, 1, 0x04, 0x00000021 }, + { 0x419a0c, 1, 0x04, 0x00020000 }, + { 0x419a10, 1, 0x04, 0x00000000 }, + { 0x419a14, 1, 0x04, 0x00000200 }, + { 0x419a1c, 1, 0x04, 0x0000c000 }, + { 0x419a20, 1, 0x04, 0x00000800 }, + { 0x419a30, 1, 0x04, 0x00000001 }, + { 0x419ac4, 1, 0x04, 0x0037f440 }, + { 0x419c00, 1, 0x04, 0x0000000a }, + { 0x419c04, 1, 0x04, 0x80000006 }, + { 0x419c08, 1, 0x04, 0x00000002 }, + { 0x419c20, 1, 0x04, 0x00000000 }, + { 0x419c24, 1, 0x04, 0x00084210 }, + { 0x419c28, 1, 0x04, 0x3efbefbe }, + { 0x419ce8, 1, 0x04, 0x00000000 }, + { 0x419cf4, 1, 0x04, 0x00003203 }, + { 0x419e04, 3, 0x04, 0x00000000 }, + { 0x419e10, 1, 0x04, 0x00000402 }, + { 0x419e44, 1, 0x04, 0x0013eff2 }, + { 0x419e48, 1, 0x04, 0x00000000 }, + { 0x419e4c, 1, 0x04, 0x0000007f }, + { 0x419e50, 19, 0x04, 0x00000000 }, + { 0x419eac, 1, 0x04, 0x00001f8f }, + { 0x419eb0, 1, 0x04, 0x00000d3f }, + { 0x419ec8, 1, 0x04, 0x0001304f }, + { 0x419f30, 8, 0x04, 0x00000000 }, + { 0x419f58, 1, 0x04, 0x00000000 }, + { 0x419f70, 1, 0x04, 0x00000000 }, + { 0x419f78, 1, 0x04, 0x0000000b }, + { 0x419f7c, 1, 0x04, 0x0000027a }, + + { 0x41be24, 1, 0x04, 0x00000006 }, + { 0x41bec0, 1, 0x04, 0x12180000 }, + { 0x41bec4, 1, 0x04, 0x00037f7f }, + { 0x41bee4, 1, 0x04, 0x06480430 }, + { 0x41bf00, 1, 0x04, 0x0a418820 }, + { 0x41bf04, 1, 0x04, 0x062080e6 }, + { 0x41bf08, 1, 0x04, 0x020398a4 }, + { 0x41bf0c, 1, 0x04, 0x0e629062 }, + { 0x41bf10, 1, 0x04, 0x0a418820 }, + { 0x41bf14, 1, 0x04, 0x000000e6 }, + { 0x41bfd0, 1, 0x04, 0x00900103 }, + { 0x41bfe0, 1, 0x04, 0x00400001 }, + { 0x41bfe4, 1, 0x04, 0x00000000 }, + + {} +}; + +void +nve4_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info) +{ + u32 magic[GPC_MAX][2]; + u32 offset; + int gpc; + + mmio_data(0x003000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS); + mmio_data(0x008000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS); + mmio_data(0x060000, 0x1000, NV_MEM_ACCESS_RW); + mmio_list(0x40800c, 0x00000000, 8, 1); + mmio_list(0x408010, 0x80000000, 0, 0); + mmio_list(0x419004, 0x00000000, 8, 1); + mmio_list(0x419008, 0x00000000, 0, 0); + mmio_list(0x4064cc, 0x80000000, 0, 0); + mmio_list(0x408004, 0x00000000, 8, 0); + mmio_list(0x408008, 0x80000030, 0, 0); + mmio_list(0x418808, 0x00000000, 8, 0); + mmio_list(0x41880c, 0x80000030, 0, 0); + mmio_list(0x4064c8, 0x01800600, 0, 0); + mmio_list(0x418810, 0x80000000, 12, 2); + mmio_list(0x419848, 0x10000000, 12, 2); + + mmio_list(0x405830, 0x02180648, 0, 0); + mmio_list(0x4064c4, 0x0192ffff, 0, 0); + + for (gpc = 0, offset = 0; gpc < priv->gpc_nr; gpc++) { + u16 magic0 = 0x0218 * priv->tpc_nr[gpc]; + u16 magic1 = 0x0648 * priv->tpc_nr[gpc]; + magic[gpc][0] = 0x10000000 | (magic0 << 16) | offset; + magic[gpc][1] = 0x00000000 | (magic1 << 16); + offset += 0x0324 * priv->tpc_nr[gpc]; + } + + for (gpc = 0; gpc < priv->gpc_nr; gpc++) { + mmio_list(GPC_UNIT(gpc, 0x30c0), magic[gpc][0], 0, 0); + mmio_list(GPC_UNIT(gpc, 0x30e4), magic[gpc][1] | offset, 0, 0); + offset += 0x07ff * priv->tpc_nr[gpc]; + } + + mmio_list(0x17e91c, 0x06060609, 0, 0); + mmio_list(0x17e920, 0x00090a05, 0, 0); +} + +void +nve4_grctx_generate_r418bb8(struct nvc0_graph_priv *priv) +{ + u32 data[6] = {}, data2[2] = {}; + u8 tpcnr[GPC_MAX]; + u8 shift, ntpcv; + int gpc, tpc, i; + + /* calculate first set of magics */ + memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr)); + + gpc = -1; + for (tpc = 0; tpc < priv->tpc_total; tpc++) { + do { + gpc = (gpc + 1) % priv->gpc_nr; + } while (!tpcnr[gpc]); + tpcnr[gpc]--; + + data[tpc / 6] |= gpc << ((tpc % 6) * 5); + } + + for (; tpc < 32; tpc++) + data[tpc / 6] |= 7 << ((tpc % 6) * 5); + + /* and the second... */ + shift = 0; + ntpcv = priv->tpc_total; + while (!(ntpcv & (1 << 4))) { + ntpcv <<= 1; + shift++; + } + + data2[0] = (ntpcv << 16); + data2[0] |= (shift << 21); + data2[0] |= (((1 << (0 + 5)) % ntpcv) << 24); + for (i = 1; i < 7; i++) + data2[1] |= ((1 << (i + 5)) % ntpcv) << ((i - 1) * 5); + + /* GPC_BROADCAST */ + nv_wr32(priv, 0x418bb8, (priv->tpc_total << 8) | + priv->magic_not_rop_nr); + for (i = 0; i < 6; i++) + nv_wr32(priv, 0x418b08 + (i * 4), data[i]); + + /* GPC_BROADCAST.TP_BROADCAST */ + nv_wr32(priv, 0x41bfd0, (priv->tpc_total << 8) | + priv->magic_not_rop_nr | data2[0]); + nv_wr32(priv, 0x41bfe4, data2[1]); + for (i = 0; i < 6; i++) + nv_wr32(priv, 0x41bf00 + (i * 4), data[i]); + + /* UNK78xx */ + nv_wr32(priv, 0x4078bc, (priv->tpc_total << 8) | + priv->magic_not_rop_nr); + for (i = 0; i < 6; i++) + nv_wr32(priv, 0x40780c + (i * 4), data[i]); +} + +void +nve4_grctx_generate_main(struct nvc0_graph_priv *priv, struct nvc0_grctx *info) +{ + struct nvc0_grctx_oclass *oclass = (void *)nv_engine(priv)->cclass; + int i; + + nv_mask(priv, 0x000260, 0x00000001, 0x00000000); + + for (i = 0; oclass->mmio[i]; i++) + nvc0_graph_mmio(priv, oclass->mmio[i]); + nvc0_graph_mmio(priv, oclass->gpc); + nvc0_graph_mmio(priv, oclass->tpc); + + nv_wr32(priv, 0x404154, 0x00000000); + + oclass->mods(priv, info); + + nv_wr32(priv, 0x418c6c, 0x1); + nv_wr32(priv, 0x41980c, 0x10); + nv_wr32(priv, 0x41be08, 0x4); + nv_wr32(priv, 0x4064c0, 0x801a00f0); + nv_wr32(priv, 0x405800, 0xf8000bf); + nv_wr32(priv, 0x419c00, 0xa); + + nvc0_grctx_generate_tpcid(priv); + nvc0_grctx_generate_r406028(priv); + + nv_wr32(priv, 0x40602c, 0x00000000); + nv_wr32(priv, 0x405874, 0x00000000); + nv_wr32(priv, 0x406030, 0x00000000); + nv_wr32(priv, 0x405878, 0x00000000); + nv_wr32(priv, 0x406034, 0x00000000); + nv_wr32(priv, 0x40587c, 0x00000000); + + nve4_grctx_generate_r418bb8(priv); + nvc0_grctx_generate_r406800(priv); + + for (i = 0; i < 8; i++) + nv_wr32(priv, 0x4064d0 + (i * 0x04), 0x00000000); + + nv_wr32(priv, 0x405b00, (priv->tpc_total << 8) | priv->gpc_nr); + if (priv->gpc_nr == 1) { + nv_mask(priv, 0x408850, 0x0000000f, priv->tpc_nr[0]); + nv_mask(priv, 0x408958, 0x0000000f, priv->tpc_nr[0]); + } else { + nv_mask(priv, 0x408850, 0x0000000f, priv->gpc_nr); + nv_mask(priv, 0x408958, 0x0000000f, priv->gpc_nr); + } + nv_mask(priv, 0x419f78, 0x00000001, 0x00000000); + + nvc0_graph_icmd(priv, oclass->icmd); + nv_wr32(priv, 0x404154, 0x00000400); + nvc0_graph_mthd(priv, oclass->mthd); + nv_mask(priv, 0x000260, 0x00000001, 0x00000001); + + nv_mask(priv, 0x418800, 0x00200000, 0x00200000); + nv_mask(priv, 0x41be10, 0x00800000, 0x00800000); +} + +static struct nvc0_graph_init * +nve4_grctx_init_mmio[] = { + nvc0_grctx_init_base, + nve4_grctx_init_unk40xx, + nvc0_grctx_init_unk44xx, + nve4_grctx_init_unk46xx, + nve4_grctx_init_unk47xx, + nve4_grctx_init_unk58xx, + nve4_grctx_init_unk5bxx, + nve4_grctx_init_unk60xx, + nve4_grctx_init_unk64xx, + nve4_grctx_init_unk70xx, + nvc0_grctx_init_unk78xx, + nve4_grctx_init_unk80xx, + nve4_grctx_init_rop, + NULL +}; + +static struct nvc0_graph_mthd +nve4_grctx_init_mthd[] = { + { 0xa097, nve4_grctx_init_a097, }, + { 0x902d, nvc0_grctx_init_902d, }, + { 0x902d, nvc0_grctx_init_mthd_magic, }, + {} +}; + +struct nouveau_oclass * +nve4_grctx_oclass = &(struct nvc0_grctx_oclass) { + .base.handle = NV_ENGCTX(GR, 0xe4), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = nvc0_graph_context_ctor, + .dtor = nvc0_graph_context_dtor, + .init = _nouveau_graph_context_init, + .fini = _nouveau_graph_context_fini, + .rd32 = _nouveau_graph_context_rd32, + .wr32 = _nouveau_graph_context_wr32, + }, + .main = nve4_grctx_generate_main, + .mods = nve4_grctx_generate_mods, + .mmio = nve4_grctx_init_mmio, + .gpc = nve4_grctx_init_gpc, + .tpc = nve4_grctx_init_tpc, + .icmd = nve4_grctx_init_icmd, + .mthd = nve4_grctx_init_mthd, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c new file mode 100644 index 000000000000..8aae1f3415f5 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c @@ -0,0 +1,288 @@ +/* + * Copyright 2013 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 "nvc0.h" + +static struct nvc0_graph_init +nvf0_grctx_init_unk40xx[] = { + { 0x404004, 8, 0x04, 0x00000000 }, + { 0x404024, 1, 0x04, 0x0000e000 }, + { 0x404028, 8, 0x04, 0x00000000 }, + { 0x4040a8, 8, 0x04, 0x00000000 }, + { 0x4040c8, 1, 0x04, 0xf800008f }, + { 0x4040d0, 6, 0x04, 0x00000000 }, + { 0x4040e8, 1, 0x04, 0x00001000 }, + { 0x4040f8, 1, 0x04, 0x00000000 }, + { 0x404100, 10, 0x04, 0x00000000 }, + { 0x404130, 2, 0x04, 0x00000000 }, + { 0x404138, 1, 0x04, 0x20000040 }, + { 0x404150, 1, 0x04, 0x0000002e }, + { 0x404154, 1, 0x04, 0x00000400 }, + { 0x404158, 1, 0x04, 0x00000200 }, + { 0x404164, 1, 0x04, 0x00000055 }, + { 0x40417c, 2, 0x04, 0x00000000 }, + { 0x4041a0, 4, 0x04, 0x00000000 }, + { 0x404200, 1, 0x04, 0x0000a197 }, + { 0x404204, 1, 0x04, 0x0000a1c0 }, + { 0x404208, 1, 0x04, 0x0000a140 }, + { 0x40420c, 1, 0x04, 0x0000902d }, + {} +}; + +static struct nvc0_graph_init +nvf0_grctx_init_unk44xx[] = { + { 0x404404, 12, 0x04, 0x00000000 }, + { 0x404438, 1, 0x04, 0x00000000 }, + { 0x404460, 2, 0x04, 0x00000000 }, + { 0x404468, 1, 0x04, 0x00ffffff }, + { 0x40446c, 1, 0x04, 0x00000000 }, + { 0x404480, 1, 0x04, 0x00000001 }, + { 0x404498, 1, 0x04, 0x00000001 }, + {} +}; + +static struct nvc0_graph_init +nvf0_grctx_init_unk5bxx[] = { + { 0x405b00, 1, 0x04, 0x00000000 }, + { 0x405b10, 1, 0x04, 0x00001000 }, + { 0x405b20, 1, 0x04, 0x04000000 }, + {} +}; + +static struct nvc0_graph_init +nvf0_grctx_init_unk60xx[] = { + { 0x406020, 1, 0x04, 0x034103c1 }, + { 0x406028, 4, 0x04, 0x00000001 }, + {} +}; + +static struct nvc0_graph_init +nvf0_grctx_init_unk64xx[] = { + { 0x4064a8, 1, 0x04, 0x00000000 }, + { 0x4064ac, 1, 0x04, 0x00003fff }, + { 0x4064b0, 3, 0x04, 0x00000000 }, + { 0x4064c0, 1, 0x04, 0x802000f0 }, + { 0x4064c4, 1, 0x04, 0x0192ffff }, + { 0x4064c8, 1, 0x04, 0x018007c0 }, + { 0x4064cc, 9, 0x04, 0x00000000 }, + { 0x4064fc, 1, 0x04, 0x0000022a }, + {} +}; + +static struct nvc0_graph_init +nvf0_grctx_init_unk88xx[] = { + { 0x408800, 1, 0x04, 0x12802a3c }, + { 0x408804, 1, 0x04, 0x00000040 }, + { 0x408808, 1, 0x04, 0x1003e005 }, + { 0x408840, 1, 0x04, 0x0000000b }, + { 0x408900, 1, 0x04, 0x3080b801 }, + { 0x408904, 1, 0x04, 0x62000001 }, + { 0x408908, 1, 0x04, 0x00c8102f }, + { 0x408980, 1, 0x04, 0x0000011d }, + {} +}; + +static struct nvc0_graph_init +nvf0_grctx_init_gpc[] = { + { 0x418380, 1, 0x04, 0x00000016 }, + { 0x418400, 1, 0x04, 0x38004e00 }, + { 0x418404, 1, 0x04, 0x71e0ffff }, + { 0x41840c, 1, 0x04, 0x00001008 }, + { 0x418410, 1, 0x04, 0x0fff0fff }, + { 0x418414, 1, 0x04, 0x02200fff }, + { 0x418450, 6, 0x04, 0x00000000 }, + { 0x418468, 1, 0x04, 0x00000001 }, + { 0x41846c, 2, 0x04, 0x00000000 }, + { 0x418600, 1, 0x04, 0x0000001f }, + { 0x418684, 1, 0x04, 0x0000000f }, + { 0x418700, 1, 0x04, 0x00000002 }, + { 0x418704, 1, 0x04, 0x00000080 }, + { 0x418708, 3, 0x04, 0x00000000 }, + { 0x418800, 1, 0x04, 0x7006860a }, + { 0x418808, 1, 0x04, 0x00000000 }, + { 0x41880c, 1, 0x04, 0x00000030 }, + { 0x418810, 1, 0x04, 0x00000000 }, + { 0x418828, 1, 0x04, 0x00000044 }, + { 0x418830, 1, 0x04, 0x10000001 }, + { 0x4188d8, 1, 0x04, 0x00000008 }, + { 0x4188e0, 1, 0x04, 0x01000000 }, + { 0x4188e8, 5, 0x04, 0x00000000 }, + { 0x4188fc, 1, 0x04, 0x20100018 }, + { 0x41891c, 1, 0x04, 0x00ff00ff }, + { 0x418924, 1, 0x04, 0x00000000 }, + { 0x418928, 1, 0x04, 0x00ffff00 }, + { 0x41892c, 1, 0x04, 0x0000ff00 }, + { 0x418a00, 3, 0x04, 0x00000000 }, + { 0x418a0c, 1, 0x04, 0x00010000 }, + { 0x418a10, 3, 0x04, 0x00000000 }, + { 0x418a20, 3, 0x04, 0x00000000 }, + { 0x418a2c, 1, 0x04, 0x00010000 }, + { 0x418a30, 3, 0x04, 0x00000000 }, + { 0x418a40, 3, 0x04, 0x00000000 }, + { 0x418a4c, 1, 0x04, 0x00010000 }, + { 0x418a50, 3, 0x04, 0x00000000 }, + { 0x418a60, 3, 0x04, 0x00000000 }, + { 0x418a6c, 1, 0x04, 0x00010000 }, + { 0x418a70, 3, 0x04, 0x00000000 }, + { 0x418a80, 3, 0x04, 0x00000000 }, + { 0x418a8c, 1, 0x04, 0x00010000 }, + { 0x418a90, 3, 0x04, 0x00000000 }, + { 0x418aa0, 3, 0x04, 0x00000000 }, + { 0x418aac, 1, 0x04, 0x00010000 }, + { 0x418ab0, 3, 0x04, 0x00000000 }, + { 0x418ac0, 3, 0x04, 0x00000000 }, + { 0x418acc, 1, 0x04, 0x00010000 }, + { 0x418ad0, 3, 0x04, 0x00000000 }, + { 0x418ae0, 3, 0x04, 0x00000000 }, + { 0x418aec, 1, 0x04, 0x00010000 }, + { 0x418af0, 3, 0x04, 0x00000000 }, + { 0x418b00, 1, 0x04, 0x00000006 }, + { 0x418b08, 1, 0x04, 0x0a418820 }, + { 0x418b0c, 1, 0x04, 0x062080e6 }, + { 0x418b10, 1, 0x04, 0x020398a4 }, + { 0x418b14, 1, 0x04, 0x0e629062 }, + { 0x418b18, 1, 0x04, 0x0a418820 }, + { 0x418b1c, 1, 0x04, 0x000000e6 }, + { 0x418bb8, 1, 0x04, 0x00000103 }, + { 0x418c08, 1, 0x04, 0x00000001 }, + { 0x418c10, 8, 0x04, 0x00000000 }, + { 0x418c40, 1, 0x04, 0xffffffff }, + { 0x418c6c, 1, 0x04, 0x00000001 }, + { 0x418c80, 1, 0x04, 0x20200004 }, + { 0x418c8c, 1, 0x04, 0x00000001 }, + { 0x418d24, 1, 0x04, 0x00000000 }, + { 0x419000, 1, 0x04, 0x00000780 }, + { 0x419004, 2, 0x04, 0x00000000 }, + { 0x419014, 1, 0x04, 0x00000004 }, + {} +}; + +static struct nvc0_graph_init +nvf0_grctx_init_tpc[] = { + { 0x419848, 1, 0x04, 0x00000000 }, + { 0x419864, 1, 0x04, 0x00000129 }, + { 0x419888, 1, 0x04, 0x00000000 }, + { 0x419a00, 1, 0x04, 0x000000f0 }, + { 0x419a04, 1, 0x04, 0x00000001 }, + { 0x419a08, 1, 0x04, 0x00000021 }, + { 0x419a0c, 1, 0x04, 0x00020000 }, + { 0x419a10, 1, 0x04, 0x00000000 }, + { 0x419a14, 1, 0x04, 0x00000200 }, + { 0x419a1c, 1, 0x04, 0x0000c000 }, + { 0x419a20, 1, 0x04, 0x00020800 }, + { 0x419a30, 1, 0x04, 0x00000001 }, + { 0x419ac4, 1, 0x04, 0x0037f440 }, + { 0x419c00, 1, 0x04, 0x0000001a }, + { 0x419c04, 1, 0x04, 0x80000006 }, + { 0x419c08, 1, 0x04, 0x00000002 }, + { 0x419c20, 1, 0x04, 0x00000000 }, + { 0x419c24, 1, 0x04, 0x00084210 }, + { 0x419c28, 1, 0x04, 0x3efbefbe }, + { 0x419ce8, 1, 0x04, 0x00000000 }, + { 0x419cf4, 1, 0x04, 0x00000203 }, + { 0x419e04, 1, 0x04, 0x00000000 }, + { 0x419e08, 1, 0x04, 0x0000001d }, + { 0x419e0c, 1, 0x04, 0x00000000 }, + { 0x419e10, 1, 0x04, 0x00001c02 }, + { 0x419e44, 1, 0x04, 0x0013eff2 }, + { 0x419e48, 1, 0x04, 0x00000000 }, + { 0x419e4c, 1, 0x04, 0x0000007f }, + { 0x419e50, 2, 0x04, 0x00000000 }, + { 0x419e58, 1, 0x04, 0x00000001 }, + { 0x419e5c, 3, 0x04, 0x00000000 }, + { 0x419e68, 1, 0x04, 0x00000002 }, + { 0x419e6c, 12, 0x04, 0x00000000 }, + { 0x419eac, 1, 0x04, 0x00001fcf }, + { 0x419eb0, 1, 0x04, 0x0db00da0 }, + { 0x419eb8, 1, 0x04, 0x00000000 }, + { 0x419ec8, 1, 0x04, 0x0001304f }, + { 0x419f30, 4, 0x04, 0x00000000 }, + { 0x419f40, 1, 0x04, 0x00000018 }, + { 0x419f44, 3, 0x04, 0x00000000 }, + { 0x419f58, 1, 0x04, 0x00000000 }, + { 0x419f70, 1, 0x04, 0x00007300 }, + { 0x419f78, 1, 0x04, 0x000000eb }, + { 0x419f7c, 1, 0x04, 0x00000404 }, + + { 0x41be24, 1, 0x04, 0x00000006 }, + { 0x41bec0, 1, 0x04, 0x10000000 }, + { 0x41bec4, 1, 0x04, 0x00037f7f }, + { 0x41bee4, 1, 0x04, 0x00000000 }, + { 0x41bf00, 1, 0x04, 0x0a418820 }, + { 0x41bf04, 1, 0x04, 0x062080e6 }, + { 0x41bf08, 1, 0x04, 0x020398a4 }, + { 0x41bf0c, 1, 0x04, 0x0e629062 }, + { 0x41bf10, 1, 0x04, 0x0a418820 }, + { 0x41bf14, 1, 0x04, 0x000000e6 }, + { 0x41bfd0, 1, 0x04, 0x00900103 }, + { 0x41bfe0, 1, 0x04, 0x00400001 }, + { 0x41bfe4, 1, 0x04, 0x00000000 }, + + {} +}; + +static struct nvc0_graph_init * +nvf0_grctx_init_mmio[] = { + nvc0_grctx_init_base, + nvf0_grctx_init_unk40xx, + nvf0_grctx_init_unk44xx, + nve4_grctx_init_unk46xx, + nve4_grctx_init_unk47xx, + nve4_grctx_init_unk58xx, + nvf0_grctx_init_unk5bxx, + nvf0_grctx_init_unk60xx, + nvf0_grctx_init_unk64xx, + nve4_grctx_init_unk80xx, + nvf0_grctx_init_unk88xx, + nvd9_grctx_init_rop, + NULL +}; + +static struct nvc0_graph_mthd +nvf0_grctx_init_mthd[] = { + { 0xa197, nvc1_grctx_init_9097, }, + { 0x902d, nvc0_grctx_init_902d, }, + { 0x902d, nvc0_grctx_init_mthd_magic, }, + {} +}; + +struct nouveau_oclass * +nvf0_grctx_oclass = &(struct nvc0_grctx_oclass) { + .base.handle = NV_ENGCTX(GR, 0xf0), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = nvc0_graph_context_ctor, + .dtor = nvc0_graph_context_dtor, + .init = _nouveau_graph_context_init, + .fini = _nouveau_graph_context_fini, + .rd32 = _nouveau_graph_context_rd32, + .wr32 = _nouveau_graph_context_wr32, + }, + .main = nve4_grctx_generate_main, + .mods = nve4_grctx_generate_mods, + .mmio = nvf0_grctx_init_mmio, + .gpc = nvf0_grctx_init_gpc, + .tpc = nvf0_grctx_init_tpc, + .icmd = nvc0_grctx_init_icmd, + .mthd = nvf0_grctx_init_mthd, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c index d61c833be09f..200a5c54ccad 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c @@ -23,14 +23,12 @@ */ #include "nvc0.h" -#include "fuc/hubnvc0.fuc.h" -#include "fuc/gpcnvc0.fuc.h" /******************************************************************************* * Graphics object classes ******************************************************************************/ -static struct nouveau_oclass +struct nouveau_oclass nvc0_graph_sclass[] = { { 0x902d, &nouveau_object_ofuncs }, { 0x9039, &nouveau_object_ofuncs }, @@ -39,40 +37,6 @@ nvc0_graph_sclass[] = { {} }; -static struct nouveau_oclass -nvc1_graph_sclass[] = { - { 0x902d, &nouveau_object_ofuncs }, - { 0x9039, &nouveau_object_ofuncs }, - { 0x9097, &nouveau_object_ofuncs }, - { 0x90c0, &nouveau_object_ofuncs }, - { 0x9197, &nouveau_object_ofuncs }, - {} -}; - -static struct nouveau_oclass -nvc8_graph_sclass[] = { - { 0x902d, &nouveau_object_ofuncs }, - { 0x9039, &nouveau_object_ofuncs }, - { 0x9097, &nouveau_object_ofuncs }, - { 0x90c0, &nouveau_object_ofuncs }, - { 0x9197, &nouveau_object_ofuncs }, - { 0x9297, &nouveau_object_ofuncs }, - {} -}; - -u64 -nvc0_graph_units(struct nouveau_graph *graph) -{ - struct nvc0_graph_priv *priv = (void *)graph; - u64 cfg; - - cfg = (u32)priv->gpc_nr; - cfg |= (u32)priv->tpc_total << 8; - cfg |= (u64)priv->rop_nr << 32; - - return cfg; -} - /******************************************************************************* * PGRAPH context ******************************************************************************/ @@ -181,60 +145,265 @@ nvc0_graph_context_dtor(struct nouveau_object *object) nouveau_graph_context_destroy(&chan->base); } -static struct nouveau_oclass -nvc0_graph_cclass = { - .ofuncs = &(struct nouveau_ofuncs) { - .ctor = nvc0_graph_context_ctor, - .dtor = nvc0_graph_context_dtor, - .init = _nouveau_graph_context_init, - .fini = _nouveau_graph_context_fini, - .rd32 = _nouveau_graph_context_rd32, - .wr32 = _nouveau_graph_context_wr32, - }, -}; - /******************************************************************************* * PGRAPH engine/subdev functions ******************************************************************************/ -static void -nvc0_graph_ctxctl_debug_unit(struct nvc0_graph_priv *priv, u32 base) +struct nvc0_graph_init +nvc0_graph_init_regs[] = { + { 0x400080, 1, 0x04, 0x003083c2 }, + { 0x400088, 1, 0x04, 0x00006fe7 }, + { 0x40008c, 1, 0x04, 0x00000000 }, + { 0x400090, 1, 0x04, 0x00000030 }, + { 0x40013c, 1, 0x04, 0x013901f7 }, + { 0x400140, 1, 0x04, 0x00000100 }, + { 0x400144, 1, 0x04, 0x00000000 }, + { 0x400148, 1, 0x04, 0x00000110 }, + { 0x400138, 1, 0x04, 0x00000000 }, + { 0x400130, 2, 0x04, 0x00000000 }, + { 0x400124, 1, 0x04, 0x00000002 }, + {} +}; + +struct nvc0_graph_init +nvc0_graph_init_unk40xx[] = { + { 0x40415c, 1, 0x04, 0x00000000 }, + { 0x404170, 1, 0x04, 0x00000000 }, + {} +}; + +struct nvc0_graph_init +nvc0_graph_init_unk44xx[] = { + { 0x404488, 2, 0x04, 0x00000000 }, + {} +}; + +struct nvc0_graph_init +nvc0_graph_init_unk78xx[] = { + { 0x407808, 1, 0x04, 0x00000000 }, + {} +}; + +struct nvc0_graph_init +nvc0_graph_init_unk60xx[] = { + { 0x406024, 1, 0x04, 0x00000000 }, + {} +}; + +struct nvc0_graph_init +nvc0_graph_init_unk58xx[] = { + { 0x405844, 1, 0x04, 0x00ffffff }, + { 0x405850, 1, 0x04, 0x00000000 }, + { 0x405908, 1, 0x04, 0x00000000 }, + {} +}; + +struct nvc0_graph_init +nvc0_graph_init_unk80xx[] = { + { 0x40803c, 1, 0x04, 0x00000000 }, + {} +}; + +struct nvc0_graph_init +nvc0_graph_init_gpc[] = { + { 0x4184a0, 1, 0x04, 0x00000000 }, + { 0x418604, 1, 0x04, 0x00000000 }, + { 0x418680, 1, 0x04, 0x00000000 }, + { 0x418714, 1, 0x04, 0x80000000 }, + { 0x418384, 1, 0x04, 0x00000000 }, + { 0x418814, 3, 0x04, 0x00000000 }, + { 0x418b04, 1, 0x04, 0x00000000 }, + { 0x4188c8, 1, 0x04, 0x80000000 }, + { 0x4188cc, 1, 0x04, 0x00000000 }, + { 0x4188d0, 1, 0x04, 0x00010000 }, + { 0x4188d4, 1, 0x04, 0x00000001 }, + { 0x418910, 1, 0x04, 0x00010001 }, + { 0x418914, 1, 0x04, 0x00000301 }, + { 0x418918, 1, 0x04, 0x00800000 }, + { 0x418980, 1, 0x04, 0x77777770 }, + { 0x418984, 3, 0x04, 0x77777777 }, + { 0x418c04, 1, 0x04, 0x00000000 }, + { 0x418c88, 1, 0x04, 0x00000000 }, + { 0x418d00, 1, 0x04, 0x00000000 }, + { 0x418f08, 1, 0x04, 0x00000000 }, + { 0x418e00, 1, 0x04, 0x00000050 }, + { 0x418e08, 1, 0x04, 0x00000000 }, + { 0x41900c, 1, 0x04, 0x00000000 }, + { 0x419018, 1, 0x04, 0x00000000 }, + {} +}; + +static struct nvc0_graph_init +nvc0_graph_init_tpc[] = { + { 0x419d08, 2, 0x04, 0x00000000 }, + { 0x419d10, 1, 0x04, 0x00000014 }, + { 0x419ab0, 1, 0x04, 0x00000000 }, + { 0x419ab8, 1, 0x04, 0x000000e7 }, + { 0x419abc, 2, 0x04, 0x00000000 }, + { 0x41980c, 3, 0x04, 0x00000000 }, + { 0x419844, 1, 0x04, 0x00000000 }, + { 0x41984c, 1, 0x04, 0x00005bc5 }, + { 0x419850, 4, 0x04, 0x00000000 }, + { 0x419c98, 1, 0x04, 0x00000000 }, + { 0x419ca8, 1, 0x04, 0x80000000 }, + { 0x419cb4, 1, 0x04, 0x00000000 }, + { 0x419cb8, 1, 0x04, 0x00008bf4 }, + { 0x419cbc, 1, 0x04, 0x28137606 }, + { 0x419cc0, 2, 0x04, 0x00000000 }, + { 0x419bd4, 1, 0x04, 0x00800000 }, + { 0x419bdc, 1, 0x04, 0x00000000 }, + { 0x419d2c, 1, 0x04, 0x00000000 }, + { 0x419c0c, 1, 0x04, 0x00000000 }, + { 0x419e00, 1, 0x04, 0x00000000 }, + { 0x419ea0, 1, 0x04, 0x00000000 }, + { 0x419ea4, 1, 0x04, 0x00000100 }, + { 0x419ea8, 1, 0x04, 0x00001100 }, + { 0x419eac, 1, 0x04, 0x11100702 }, + { 0x419eb0, 1, 0x04, 0x00000003 }, + { 0x419eb4, 4, 0x04, 0x00000000 }, + { 0x419ec8, 1, 0x04, 0x06060618 }, + { 0x419ed0, 1, 0x04, 0x0eff0e38 }, + { 0x419ed4, 1, 0x04, 0x011104f1 }, + { 0x419edc, 1, 0x04, 0x00000000 }, + { 0x419f00, 1, 0x04, 0x00000000 }, + { 0x419f2c, 1, 0x04, 0x00000000 }, + {} +}; + +struct nvc0_graph_init +nvc0_graph_init_unk88xx[] = { + { 0x40880c, 1, 0x04, 0x00000000 }, + { 0x408910, 9, 0x04, 0x00000000 }, + { 0x408950, 1, 0x04, 0x00000000 }, + { 0x408954, 1, 0x04, 0x0000ffff }, + { 0x408984, 1, 0x04, 0x00000000 }, + { 0x408988, 1, 0x04, 0x08040201 }, + { 0x40898c, 1, 0x04, 0x80402010 }, + {} +}; + +void +nvc0_graph_mmio(struct nvc0_graph_priv *priv, struct nvc0_graph_init *init) { - nv_error(priv, "%06x - done 0x%08x\n", base, - nv_rd32(priv, base + 0x400)); - nv_error(priv, "%06x - stat 0x%08x 0x%08x 0x%08x 0x%08x\n", base, - nv_rd32(priv, base + 0x800), nv_rd32(priv, base + 0x804), - nv_rd32(priv, base + 0x808), nv_rd32(priv, base + 0x80c)); - nv_error(priv, "%06x - stat 0x%08x 0x%08x 0x%08x 0x%08x\n", base, - nv_rd32(priv, base + 0x810), nv_rd32(priv, base + 0x814), - nv_rd32(priv, base + 0x818), nv_rd32(priv, base + 0x81c)); + for (; init && init->count; init++) { + u32 addr = init->addr, i; + for (i = 0; i < init->count; i++) { + nv_wr32(priv, addr, init->data); + addr += init->pitch; + } + } } void -nvc0_graph_ctxctl_debug(struct nvc0_graph_priv *priv) +nvc0_graph_icmd(struct nvc0_graph_priv *priv, struct nvc0_graph_init *init) { - u32 gpcnr = nv_rd32(priv, 0x409604) & 0xffff; - u32 gpc; + u32 addr, data; + int i, j; + + nv_wr32(priv, 0x400208, 0x80000000); + for (i = 0; init->count; init++, i++) { + if (!i || data != init->data) { + nv_wr32(priv, 0x400204, init->data); + data = init->data; + } - nvc0_graph_ctxctl_debug_unit(priv, 0x409000); - for (gpc = 0; gpc < gpcnr; gpc++) - nvc0_graph_ctxctl_debug_unit(priv, 0x502000 + (gpc * 0x8000)); + addr = init->addr; + for (j = 0; j < init->count; j++) { + nv_wr32(priv, 0x400200, addr); + addr += init->pitch; + while (nv_rd32(priv, 0x400700) & 0x00000002) {} + } + } + nv_wr32(priv, 0x400208, 0x00000000); +} + +void +nvc0_graph_mthd(struct nvc0_graph_priv *priv, struct nvc0_graph_mthd *mthds) +{ + struct nvc0_graph_mthd *mthd; + struct nvc0_graph_init *init; + int i = 0, j; + u32 data; + + while ((mthd = &mthds[i++]) && (init = mthd->init)) { + u32 addr = 0x80000000 | mthd->oclass; + for (data = 0; init->count; init++) { + if (data != init->data) { + nv_wr32(priv, 0x40448c, init->data); + data = init->data; + } + + addr = (addr & 0x8000ffff) | (init->addr << 14); + for (j = 0; j < init->count; j++) { + nv_wr32(priv, 0x404488, addr); + addr += init->pitch << 14; + } + } + } +} + +u64 +nvc0_graph_units(struct nouveau_graph *graph) +{ + struct nvc0_graph_priv *priv = (void *)graph; + u64 cfg; + + cfg = (u32)priv->gpc_nr; + cfg |= (u32)priv->tpc_total << 8; + cfg |= (u64)priv->rop_nr << 32; + + return cfg; } +static const struct nouveau_enum nve0_sked_error[] = { + { 7, "CONSTANT_BUFFER_SIZE" }, + { 9, "LOCAL_MEMORY_SIZE_POS" }, + { 10, "LOCAL_MEMORY_SIZE_NEG" }, + { 11, "WARP_CSTACK_SIZE" }, + { 12, "TOTAL_TEMP_SIZE" }, + { 13, "REGISTER_COUNT" }, + { 18, "TOTAL_THREADS" }, + { 20, "PROGRAM_OFFSET" }, + { 21, "SHARED_MEMORY_SIZE" }, + { 25, "SHARED_CONFIG_TOO_SMALL" }, + { 26, "TOTAL_REGISTER_COUNT" }, + {} +}; + +static const struct nouveau_enum nvc0_gpc_rop_error[] = { + { 1, "RT_PITCH_OVERRUN" }, + { 4, "RT_WIDTH_OVERRUN" }, + { 5, "RT_HEIGHT_OVERRUN" }, + { 7, "ZETA_STORAGE_TYPE_MISMATCH" }, + { 8, "RT_STORAGE_TYPE_MISMATCH" }, + { 10, "RT_LINEAR_MISMATCH" }, + {} +}; + static void -nvc0_graph_ctxctl_isr(struct nvc0_graph_priv *priv) +nvc0_graph_trap_gpc_rop(struct nvc0_graph_priv *priv, int gpc) { - u32 ustat = nv_rd32(priv, 0x409c18); + u32 trap[4]; + int i; - if (ustat & 0x00000001) - nv_error(priv, "CTXCTRL ucode error\n"); - if (ustat & 0x00080000) - nv_error(priv, "CTXCTRL watchdog timeout\n"); - if (ustat & ~0x00080001) - nv_error(priv, "CTXCTRL 0x%08x\n", ustat); + trap[0] = nv_rd32(priv, GPC_UNIT(gpc, 0x0420)); + trap[1] = nv_rd32(priv, GPC_UNIT(gpc, 0x0434)); + trap[2] = nv_rd32(priv, GPC_UNIT(gpc, 0x0438)); + trap[3] = nv_rd32(priv, GPC_UNIT(gpc, 0x043c)); - nvc0_graph_ctxctl_debug(priv); - nv_wr32(priv, 0x409c20, ustat); + nv_error(priv, "GPC%d/PROP trap:", gpc); + for (i = 0; i <= 29; ++i) { + if (!(trap[0] & (1 << i))) + continue; + pr_cont(" "); + nouveau_enum_print(nvc0_gpc_rop_error, i); + } + pr_cont("\n"); + + nv_error(priv, "x = %u, y = %u, format = %x, storage type = %x\n", + trap[1] & 0xffff, trap[1] >> 16, (trap[2] >> 8) & 0x3f, + trap[3] & 0xff); + nv_wr32(priv, GPC_UNIT(gpc, 0x0420), 0xc0000000); } static const struct nouveau_enum nvc0_mp_warp_error[] = { @@ -283,13 +452,11 @@ nvc0_graph_trap_tpc(struct nvc0_graph_priv *priv, int gpc, int tpc) u32 trap = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x0224)); nv_error(priv, "GPC%d/TPC%d/TEX: 0x%08x\n", gpc, tpc, trap); nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x0224), 0xc0000000); - nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x0508), 0x00000001); stat &= ~0x00000001; } if (stat & 0x00000002) { nvc0_graph_trap_mp(priv, gpc, tpc); - nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x0508), 0x00000002); stat &= ~0x00000002; } @@ -297,7 +464,6 @@ nvc0_graph_trap_tpc(struct nvc0_graph_priv *priv, int gpc, int tpc) u32 trap = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x0084)); nv_error(priv, "GPC%d/TPC%d/POLY: 0x%08x\n", gpc, tpc, trap); nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x0084), 0xc0000000); - nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x0508), 0x00000004); stat &= ~0x00000004; } @@ -305,13 +471,11 @@ nvc0_graph_trap_tpc(struct nvc0_graph_priv *priv, int gpc, int tpc) u32 trap = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x048c)); nv_error(priv, "GPC%d/TPC%d/L1C: 0x%08x\n", gpc, tpc, trap); nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x048c), 0xc0000000); - nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x0508), 0x00000008); stat &= ~0x00000008; } if (stat) { nv_error(priv, "GPC%d/TPC%d/0x%08x: unknown\n", gpc, tpc, stat); - nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x0508), stat); } } @@ -322,10 +486,7 @@ nvc0_graph_trap_gpc(struct nvc0_graph_priv *priv, int gpc) int tpc; if (stat & 0x00000001) { - u32 trap = nv_rd32(priv, GPC_UNIT(gpc, 0x0420)); - nv_error(priv, "GPC%d/PROP: 0x%08x\n", gpc, trap); - nv_wr32(priv, GPC_UNIT(gpc, 0x0420), 0xc0000000); - nv_wr32(priv, GPC_UNIT(gpc, 0x2c90), 0x00000001); + nvc0_graph_trap_gpc_rop(priv, gpc); stat &= ~0x00000001; } @@ -333,7 +494,6 @@ nvc0_graph_trap_gpc(struct nvc0_graph_priv *priv, int gpc) u32 trap = nv_rd32(priv, GPC_UNIT(gpc, 0x0900)); nv_error(priv, "GPC%d/ZCULL: 0x%08x\n", gpc, trap); nv_wr32(priv, GPC_UNIT(gpc, 0x0900), 0xc0000000); - nv_wr32(priv, GPC_UNIT(gpc, 0x2c90), 0x00000002); stat &= ~0x00000002; } @@ -341,7 +501,6 @@ nvc0_graph_trap_gpc(struct nvc0_graph_priv *priv, int gpc) u32 trap = nv_rd32(priv, GPC_UNIT(gpc, 0x1028)); nv_error(priv, "GPC%d/CCACHE: 0x%08x\n", gpc, trap); nv_wr32(priv, GPC_UNIT(gpc, 0x1028), 0xc0000000); - nv_wr32(priv, GPC_UNIT(gpc, 0x2c90), 0x00000004); stat &= ~0x00000004; } @@ -349,7 +508,6 @@ nvc0_graph_trap_gpc(struct nvc0_graph_priv *priv, int gpc) u32 trap = nv_rd32(priv, GPC_UNIT(gpc, 0x0824)); nv_error(priv, "GPC%d/ESETUP: 0x%08x\n", gpc, trap); nv_wr32(priv, GPC_UNIT(gpc, 0x0824), 0xc0000000); - nv_wr32(priv, GPC_UNIT(gpc, 0x2c90), 0x00000008); stat &= ~0x00000009; } @@ -364,7 +522,6 @@ nvc0_graph_trap_gpc(struct nvc0_graph_priv *priv, int gpc) if (stat) { nv_error(priv, "GPC%d/0x%08x: unknown\n", gpc, stat); - nv_wr32(priv, GPC_UNIT(gpc, 0x2c90), stat); } } @@ -372,7 +529,7 @@ static void nvc0_graph_trap_intr(struct nvc0_graph_priv *priv) { u32 trap = nv_rd32(priv, 0x400108); - int rop, gpc; + int rop, gpc, i; if (trap & 0x00000001) { u32 stat = nv_rd32(priv, 0x404000); @@ -422,6 +579,24 @@ nvc0_graph_trap_intr(struct nvc0_graph_priv *priv) trap &= ~0x00000080; } + if (trap & 0x00000100) { + u32 stat = nv_rd32(priv, 0x407020); + + nv_error(priv, "SKED:"); + for (i = 0; i <= 29; ++i) { + if (!(stat & (1 << i))) + continue; + pr_cont(" "); + nouveau_enum_print(nve0_sked_error, i); + } + pr_cont("\n"); + + if (stat & 0x3fffffff) + nv_wr32(priv, 0x407020, 0x40000000); + nv_wr32(priv, 0x400108, 0x00000100); + trap &= ~0x00000100; + } + if (trap & 0x01000000) { u32 stat = nv_rd32(priv, 0x400118); for (gpc = 0; stat && gpc < priv->gpc_nr; gpc++) { @@ -455,6 +630,46 @@ nvc0_graph_trap_intr(struct nvc0_graph_priv *priv) } } +static void +nvc0_graph_ctxctl_debug_unit(struct nvc0_graph_priv *priv, u32 base) +{ + nv_error(priv, "%06x - done 0x%08x\n", base, + nv_rd32(priv, base + 0x400)); + nv_error(priv, "%06x - stat 0x%08x 0x%08x 0x%08x 0x%08x\n", base, + nv_rd32(priv, base + 0x800), nv_rd32(priv, base + 0x804), + nv_rd32(priv, base + 0x808), nv_rd32(priv, base + 0x80c)); + nv_error(priv, "%06x - stat 0x%08x 0x%08x 0x%08x 0x%08x\n", base, + nv_rd32(priv, base + 0x810), nv_rd32(priv, base + 0x814), + nv_rd32(priv, base + 0x818), nv_rd32(priv, base + 0x81c)); +} + +void +nvc0_graph_ctxctl_debug(struct nvc0_graph_priv *priv) +{ + u32 gpcnr = nv_rd32(priv, 0x409604) & 0xffff; + u32 gpc; + + nvc0_graph_ctxctl_debug_unit(priv, 0x409000); + for (gpc = 0; gpc < gpcnr; gpc++) + nvc0_graph_ctxctl_debug_unit(priv, 0x502000 + (gpc * 0x8000)); +} + +static void +nvc0_graph_ctxctl_isr(struct nvc0_graph_priv *priv) +{ + u32 ustat = nv_rd32(priv, 0x409c18); + + if (ustat & 0x00000001) + nv_error(priv, "CTXCTL ucode error\n"); + if (ustat & 0x00080000) + nv_error(priv, "CTXCTL watchdog timeout\n"); + if (ustat & ~0x00080001) + nv_error(priv, "CTXCTL 0x%08x\n", ustat); + + nvc0_graph_ctxctl_debug(priv); + nv_wr32(priv, 0x409c20, ustat); +} + static void nvc0_graph_intr(struct nouveau_subdev *subdev) { @@ -531,882 +746,61 @@ nvc0_graph_intr(struct nouveau_subdev *subdev) nouveau_engctx_put(engctx); } -int -nvc0_graph_ctor_fw(struct nvc0_graph_priv *priv, const char *fwname, - struct nvc0_graph_fuc *fuc) +void +nvc0_graph_init_fw(struct nvc0_graph_priv *priv, u32 fuc_base, + struct nvc0_graph_fuc *code, struct nvc0_graph_fuc *data) { - struct nouveau_device *device = nv_device(priv); - const struct firmware *fw; - char f[32]; - int ret; + int i; - snprintf(f, sizeof(f), "nouveau/nv%02x_%s", device->chipset, fwname); - ret = request_firmware(&fw, f, &device->pdev->dev); - if (ret) { - snprintf(f, sizeof(f), "nouveau/%s", fwname); - ret = request_firmware(&fw, f, &device->pdev->dev); - if (ret) { - nv_error(priv, "failed to load %s\n", fwname); - return ret; - } - } + nv_wr32(priv, fuc_base + 0x01c0, 0x01000000); + for (i = 0; i < data->size / 4; i++) + nv_wr32(priv, fuc_base + 0x01c4, data->data[i]); - fuc->size = fw->size; - fuc->data = kmemdup(fw->data, fuc->size, GFP_KERNEL); - release_firmware(fw); - return (fuc->data != NULL) ? 0 : -ENOMEM; + nv_wr32(priv, fuc_base + 0x0180, 0x01000000); + for (i = 0; i < code->size / 4; i++) { + if ((i & 0x3f) == 0) + nv_wr32(priv, fuc_base + 0x0188, i >> 6); + nv_wr32(priv, fuc_base + 0x0184, code->data[i]); + } } -static int -nvc0_graph_ctor(struct nouveau_object *parent, struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) +int +nvc0_graph_init_ctxctl(struct nvc0_graph_priv *priv) { - struct nouveau_device *device = nv_device(parent); - struct nvc0_graph_priv *priv; - bool enable = device->chipset != 0xd7; - int ret, i; + struct nvc0_graph_oclass *oclass = (void *)nv_object(priv)->oclass; + u32 r000260; + int i; - ret = nouveau_graph_create(parent, engine, oclass, enable, &priv); - *pobject = nv_object(priv); - if (ret) - return ret; + if (priv->firmware) { + /* load fuc microcode */ + r000260 = nv_mask(priv, 0x000260, 0x00000001, 0x00000000); + nvc0_graph_init_fw(priv, 0x409000, &priv->fuc409c, + &priv->fuc409d); + nvc0_graph_init_fw(priv, 0x41a000, &priv->fuc41ac, + &priv->fuc41ad); + nv_wr32(priv, 0x000260, r000260); - nv_subdev(priv)->unit = 0x18001000; - nv_subdev(priv)->intr = nvc0_graph_intr; - nv_engine(priv)->cclass = &nvc0_graph_cclass; + /* start both of them running */ + nv_wr32(priv, 0x409840, 0xffffffff); + nv_wr32(priv, 0x41a10c, 0x00000000); + nv_wr32(priv, 0x40910c, 0x00000000); + nv_wr32(priv, 0x41a100, 0x00000002); + nv_wr32(priv, 0x409100, 0x00000002); + if (!nv_wait(priv, 0x409800, 0x00000001, 0x00000001)) + nv_warn(priv, "0x409800 wait failed\n"); - priv->base.units = nvc0_graph_units; + nv_wr32(priv, 0x409840, 0xffffffff); + nv_wr32(priv, 0x409500, 0x7fffffff); + nv_wr32(priv, 0x409504, 0x00000021); - if (nouveau_boolopt(device->cfgopt, "NvGrUseFW", false)) { - nv_info(priv, "using external firmware\n"); - if (nvc0_graph_ctor_fw(priv, "fuc409c", &priv->fuc409c) || - nvc0_graph_ctor_fw(priv, "fuc409d", &priv->fuc409d) || - nvc0_graph_ctor_fw(priv, "fuc41ac", &priv->fuc41ac) || - nvc0_graph_ctor_fw(priv, "fuc41ad", &priv->fuc41ad)) - return -EINVAL; - priv->firmware = true; - } - - switch (nvc0_graph_class(priv)) { - case 0x9097: - nv_engine(priv)->sclass = nvc0_graph_sclass; - break; - case 0x9197: - nv_engine(priv)->sclass = nvc1_graph_sclass; - break; - case 0x9297: - nv_engine(priv)->sclass = nvc8_graph_sclass; - break; - } - - ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x1000, 256, 0, - &priv->unk4188b4); - if (ret) - return ret; - - ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x1000, 256, 0, - &priv->unk4188b8); - if (ret) - return ret; - - for (i = 0; i < 0x1000; i += 4) { - nv_wo32(priv->unk4188b4, i, 0x00000010); - nv_wo32(priv->unk4188b8, i, 0x00000010); - } - - priv->rop_nr = (nv_rd32(priv, 0x409604) & 0x001f0000) >> 16; - priv->gpc_nr = nv_rd32(priv, 0x409604) & 0x0000001f; - for (i = 0; i < priv->gpc_nr; i++) { - priv->tpc_nr[i] = nv_rd32(priv, GPC_UNIT(i, 0x2608)); - priv->tpc_total += priv->tpc_nr[i]; - } - - /*XXX: these need figuring out... though it might not even matter */ - switch (nv_device(priv)->chipset) { - case 0xc0: - if (priv->tpc_total == 11) { /* 465, 3/4/4/0, 4 */ - priv->magic_not_rop_nr = 0x07; - } else - if (priv->tpc_total == 14) { /* 470, 3/3/4/4, 5 */ - priv->magic_not_rop_nr = 0x05; - } else - if (priv->tpc_total == 15) { /* 480, 3/4/4/4, 6 */ - priv->magic_not_rop_nr = 0x06; - } - break; - case 0xc3: /* 450, 4/0/0/0, 2 */ - priv->magic_not_rop_nr = 0x03; - break; - case 0xc4: /* 460, 3/4/0/0, 4 */ - priv->magic_not_rop_nr = 0x01; - break; - case 0xc1: /* 2/0/0/0, 1 */ - priv->magic_not_rop_nr = 0x01; - break; - case 0xc8: /* 4/4/3/4, 5 */ - priv->magic_not_rop_nr = 0x06; - break; - case 0xce: /* 4/4/0/0, 4 */ - priv->magic_not_rop_nr = 0x03; - break; - case 0xcf: /* 4/0/0/0, 3 */ - priv->magic_not_rop_nr = 0x03; - break; - case 0xd9: /* 1/0/0/0, 1 */ - priv->magic_not_rop_nr = 0x01; - break; - } - - return 0; -} - -static void -nvc0_graph_dtor_fw(struct nvc0_graph_fuc *fuc) -{ - kfree(fuc->data); - fuc->data = NULL; -} - -void -nvc0_graph_dtor(struct nouveau_object *object) -{ - struct nvc0_graph_priv *priv = (void *)object; - - kfree(priv->data); - - nvc0_graph_dtor_fw(&priv->fuc409c); - nvc0_graph_dtor_fw(&priv->fuc409d); - nvc0_graph_dtor_fw(&priv->fuc41ac); - nvc0_graph_dtor_fw(&priv->fuc41ad); - - nouveau_gpuobj_ref(NULL, &priv->unk4188b8); - nouveau_gpuobj_ref(NULL, &priv->unk4188b4); - - nouveau_graph_destroy(&priv->base); -} - -static void -nvc0_graph_init_obj418880(struct nvc0_graph_priv *priv) -{ - int i; - - nv_wr32(priv, GPC_BCAST(0x0880), 0x00000000); - nv_wr32(priv, GPC_BCAST(0x08a4), 0x00000000); - for (i = 0; i < 4; i++) - nv_wr32(priv, GPC_BCAST(0x0888) + (i * 4), 0x00000000); - nv_wr32(priv, GPC_BCAST(0x08b4), priv->unk4188b4->addr >> 8); - nv_wr32(priv, GPC_BCAST(0x08b8), priv->unk4188b8->addr >> 8); -} - -static void -nvc0_graph_init_regs(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x400080, 0x003083c2); - nv_wr32(priv, 0x400088, 0x00006fe7); - nv_wr32(priv, 0x40008c, 0x00000000); - nv_wr32(priv, 0x400090, 0x00000030); - nv_wr32(priv, 0x40013c, 0x013901f7); - nv_wr32(priv, 0x400140, 0x00000100); - nv_wr32(priv, 0x400144, 0x00000000); - nv_wr32(priv, 0x400148, 0x00000110); - nv_wr32(priv, 0x400138, 0x00000000); - nv_wr32(priv, 0x400130, 0x00000000); - nv_wr32(priv, 0x400134, 0x00000000); - nv_wr32(priv, 0x400124, 0x00000002); -} - -static void -nvc0_graph_init_unk40xx(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x40415c, 0x00000000); - nv_wr32(priv, 0x404170, 0x00000000); -} - -static void -nvc0_graph_init_unk44xx(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x404488, 0x00000000); - nv_wr32(priv, 0x40448c, 0x00000000); -} - -static void -nvc0_graph_init_unk78xx(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x407808, 0x00000000); -} - -static void -nvc0_graph_init_unk60xx(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x406024, 0x00000000); -} - -static void -nvc0_graph_init_unk64xx(struct nvc0_graph_priv *priv) -{ - switch (nv_device(priv)->chipset) { - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x4064f0, 0x00000000); - nv_wr32(priv, 0x4064f4, 0x00000000); - nv_wr32(priv, 0x4064f8, 0x00000000); - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc1: - case 0xc8: - case 0xce: - case 0xcf: - break; - default: - BUG_ON(1); - break; - } -} - -static void -nvc0_graph_init_unk58xx(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x405844, 0x00ffffff); - nv_wr32(priv, 0x405850, 0x00000000); - switch (nv_device(priv)->chipset) { - case 0xc3: - case 0xc4: - case 0xc1: - case 0xce: - case 0xcf: - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x405900, 0x00002834); - break; - case 0xc0: - case 0xc8: - break; - default: - BUG_ON(1); - break; - } - nv_wr32(priv, 0x405908, 0x00000000); - switch (nv_device(priv)->chipset) { - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x405928, 0x00000000); - nv_wr32(priv, 0x40592c, 0x00000000); - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc1: - case 0xc8: - case 0xce: - case 0xcf: - break; - default: - BUG_ON(1); - break; - } -} - -static void -nvc0_graph_init_unk80xx(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x40803c, 0x00000000); -} - -static void -nvc0_graph_init_gpc(struct nvc0_graph_priv *priv) -{ - switch (nv_device(priv)->chipset) { - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x418408, 0x00000000); - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc1: - case 0xc8: - case 0xce: - case 0xcf: - break; - default: - BUG_ON(1); - break; - } - nv_wr32(priv, 0x4184a0, 0x00000000); - switch (nv_device(priv)->chipset) { - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x4184a4, 0x00000000); - nv_wr32(priv, 0x4184a8, 0x00000000); - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc1: - case 0xc8: - case 0xce: - case 0xcf: - break; - default: - BUG_ON(1); - break; - } - nv_wr32(priv, 0x418604, 0x00000000); - nv_wr32(priv, 0x418680, 0x00000000); - switch (nv_device(priv)->chipset) { - case 0xd9: - case 0xd7: - case 0xc1: - nv_wr32(priv, 0x418714, 0x00000000); - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc8: - case 0xce: - case 0xcf: - nv_wr32(priv, 0x418714, 0x80000000); - break; - default: - BUG_ON(1); - break; - } - nv_wr32(priv, 0x418384, 0x00000000); - nv_wr32(priv, 0x418814, 0x00000000); - nv_wr32(priv, 0x418818, 0x00000000); - nv_wr32(priv, 0x41881c, 0x00000000); - nv_wr32(priv, 0x418b04, 0x00000000); - switch (nv_device(priv)->chipset) { - case 0xd9: - case 0xd7: - case 0xc1: - case 0xc8: - nv_wr32(priv, 0x4188c8, 0x00000000); - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xce: - case 0xcf: - nv_wr32(priv, 0x4188c8, 0x80000000); - break; - default: - BUG_ON(1); - break; - } - nv_wr32(priv, 0x4188cc, 0x00000000); - nv_wr32(priv, 0x4188d0, 0x00010000); - nv_wr32(priv, 0x4188d4, 0x00000001); - nv_wr32(priv, 0x418910, 0x00010001); - nv_wr32(priv, 0x418914, 0x00000301); - nv_wr32(priv, 0x418918, 0x00800000); - nv_wr32(priv, 0x418980, 0x77777770); - nv_wr32(priv, 0x418984, 0x77777777); - nv_wr32(priv, 0x418988, 0x77777777); - nv_wr32(priv, 0x41898c, 0x77777777); - nv_wr32(priv, 0x418c04, 0x00000000); - switch (nv_device(priv)->chipset) { - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x418c64, 0x00000000); - nv_wr32(priv, 0x418c68, 0x00000000); - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc1: - case 0xc8: - case 0xce: - case 0xcf: - break; - default: - BUG_ON(1); - break; - } - nv_wr32(priv, 0x418c88, 0x00000000); - switch (nv_device(priv)->chipset) { - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x418cb4, 0x00000000); - nv_wr32(priv, 0x418cb8, 0x00000000); - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc1: - case 0xc8: - case 0xce: - case 0xcf: - break; - default: - BUG_ON(1); - break; - } - nv_wr32(priv, 0x418d00, 0x00000000); - switch (nv_device(priv)->chipset) { - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x418d28, 0x00000000); - nv_wr32(priv, 0x418d2c, 0x00000000); - nv_wr32(priv, 0x418f00, 0x00000000); - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc1: - case 0xc8: - case 0xce: - case 0xcf: - break; - default: - BUG_ON(1); - break; - } - nv_wr32(priv, 0x418f08, 0x00000000); - switch (nv_device(priv)->chipset) { - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x418f20, 0x00000000); - nv_wr32(priv, 0x418f24, 0x00000000); - /*fall-through*/ - case 0xc1: - nv_wr32(priv, 0x418e00, 0x00000003); - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc8: - case 0xce: - case 0xcf: - nv_wr32(priv, 0x418e00, 0x00000050); - break; - default: - BUG_ON(1); - break; - } - nv_wr32(priv, 0x418e08, 0x00000000); - switch (nv_device(priv)->chipset) { - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x418e1c, 0x00000000); - nv_wr32(priv, 0x418e20, 0x00000000); - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc1: - case 0xc8: - case 0xce: - case 0xcf: - break; - default: - BUG_ON(1); - break; - } - nv_wr32(priv, 0x41900c, 0x00000000); - nv_wr32(priv, 0x419018, 0x00000000); -} - -static void -nvc0_graph_init_tpc(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x419d08, 0x00000000); - nv_wr32(priv, 0x419d0c, 0x00000000); - nv_wr32(priv, 0x419d10, 0x00000014); - nv_wr32(priv, 0x419ab0, 0x00000000); - switch (nv_device(priv)->chipset) { - case 0xc3: - case 0xc4: - case 0xc1: - case 0xce: - case 0xcf: - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x419ac8, 0x00000000); - break; - case 0xc0: - case 0xc8: - break; - default: - BUG_ON(1); - break; - } - nv_wr32(priv, 0x419ab8, 0x000000e7); - nv_wr32(priv, 0x419abc, 0x00000000); - nv_wr32(priv, 0x419ac0, 0x00000000); - switch (nv_device(priv)->chipset) { - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x419ab4, 0x00000000); - nv_wr32(priv, 0x41980c, 0x00000010); - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc1: - case 0xc8: - case 0xce: - case 0xcf: - nv_wr32(priv, 0x41980c, 0x00000000); - break; - default: - BUG_ON(1); - break; - } - nv_wr32(priv, 0x419810, 0x00000000); - switch (nv_device(priv)->chipset) { - case 0xd9: - case 0xd7: - case 0xc1: - nv_wr32(priv, 0x419814, 0x00000004); - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc8: - case 0xce: - case 0xcf: - nv_wr32(priv, 0x419814, 0x00000000); - break; - default: - BUG_ON(1); - break; - } - nv_wr32(priv, 0x419844, 0x00000000); - switch (nv_device(priv)->chipset) { - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x41984c, 0x0000a918); - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc1: - case 0xc8: - case 0xce: - case 0xcf: - nv_wr32(priv, 0x41984c, 0x00005bc5); - break; - default: - BUG_ON(1); - break; - } - nv_wr32(priv, 0x419850, 0x00000000); - nv_wr32(priv, 0x419854, 0x00000000); - nv_wr32(priv, 0x419858, 0x00000000); - nv_wr32(priv, 0x41985c, 0x00000000); - switch (nv_device(priv)->chipset) { - case 0xc3: - case 0xc4: - case 0xc1: - case 0xce: - case 0xcf: - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x419880, 0x00000002); - break; - case 0xc0: - case 0xc8: - break; - default: - BUG_ON(1); - break; - } - nv_wr32(priv, 0x419c98, 0x00000000); - nv_wr32(priv, 0x419ca8, 0x80000000); - nv_wr32(priv, 0x419cb4, 0x00000000); - nv_wr32(priv, 0x419cb8, 0x00008bf4); - nv_wr32(priv, 0x419cbc, 0x28137606); - nv_wr32(priv, 0x419cc0, 0x00000000); - nv_wr32(priv, 0x419cc4, 0x00000000); - nv_wr32(priv, 0x419bd4, 0x00800000); - nv_wr32(priv, 0x419bdc, 0x00000000); - switch (nv_device(priv)->chipset) { - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x419bf8, 0x00000000); - nv_wr32(priv, 0x419bfc, 0x00000000); - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc1: - case 0xc8: - case 0xce: - case 0xcf: - break; - default: - BUG_ON(1); - break; - } - nv_wr32(priv, 0x419d2c, 0x00000000); - switch (nv_device(priv)->chipset) { - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x419d48, 0x00000000); - nv_wr32(priv, 0x419d4c, 0x00000000); - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc1: - case 0xc8: - case 0xce: - case 0xcf: - break; - default: - BUG_ON(1); - break; - } - nv_wr32(priv, 0x419c0c, 0x00000000); - nv_wr32(priv, 0x419e00, 0x00000000); - nv_wr32(priv, 0x419ea0, 0x00000000); - nv_wr32(priv, 0x419ea4, 0x00000100); - switch (nv_device(priv)->chipset) { - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x419ea8, 0x02001100); - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc1: - case 0xc8: - case 0xce: - case 0xcf: - nv_wr32(priv, 0x419ea8, 0x00001100); - break; - default: - BUG_ON(1); - break; - } - - switch (nv_device(priv)->chipset) { - case 0xc8: - nv_wr32(priv, 0x419eac, 0x11100f02); - break; - case 0xc0: - case 0xc3: - case 0xc4: - case 0xc1: - case 0xce: - case 0xcf: - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x419eac, 0x11100702); - break; - default: - BUG_ON(1); - break; - } - nv_wr32(priv, 0x419eb0, 0x00000003); - nv_wr32(priv, 0x419eb4, 0x00000000); - nv_wr32(priv, 0x419eb8, 0x00000000); - nv_wr32(priv, 0x419ebc, 0x00000000); - nv_wr32(priv, 0x419ec0, 0x00000000); - switch (nv_device(priv)->chipset) { - case 0xc3: - case 0xc4: - case 0xc1: - case 0xce: - case 0xcf: - case 0xd9: - case 0xd7: - nv_wr32(priv, 0x419ec8, 0x0e063818); - nv_wr32(priv, 0x419ecc, 0x0e060e06); - nv_wr32(priv, 0x419ed0, 0x00003818); - break; - case 0xc0: - case 0xc8: - nv_wr32(priv, 0x419ec8, 0x06060618); - nv_wr32(priv, 0x419ed0, 0x0eff0e38); - break; - default: - BUG_ON(1); - break; - } - nv_wr32(priv, 0x419ed4, 0x011104f1); - nv_wr32(priv, 0x419edc, 0x00000000); - nv_wr32(priv, 0x419f00, 0x00000000); - nv_wr32(priv, 0x419f2c, 0x00000000); -} - -static void -nvc0_graph_init_unk88xx(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x40880c, 0x00000000); - nv_wr32(priv, 0x408910, 0x00000000); - nv_wr32(priv, 0x408914, 0x00000000); - nv_wr32(priv, 0x408918, 0x00000000); - nv_wr32(priv, 0x40891c, 0x00000000); - nv_wr32(priv, 0x408920, 0x00000000); - nv_wr32(priv, 0x408924, 0x00000000); - nv_wr32(priv, 0x408928, 0x00000000); - nv_wr32(priv, 0x40892c, 0x00000000); - nv_wr32(priv, 0x408930, 0x00000000); - nv_wr32(priv, 0x408950, 0x00000000); - nv_wr32(priv, 0x408954, 0x0000ffff); - nv_wr32(priv, 0x408984, 0x00000000); - nv_wr32(priv, 0x408988, 0x08040201); - nv_wr32(priv, 0x40898c, 0x80402010); -} - -static void -nvc0_graph_init_gpc_0(struct nvc0_graph_priv *priv) -{ - const u32 magicgpc918 = DIV_ROUND_UP(0x00800000, priv->tpc_total); - u32 data[TPC_MAX / 8]; - u8 tpcnr[GPC_MAX]; - int i, gpc, tpc; - - nv_wr32(priv, TPC_UNIT(0, 0, 0x5c), 1); /* affects TFB offset queries */ - - /* - * TP ROP UNKVAL(magic_not_rop_nr) - * 450: 4/0/0/0 2 3 - * 460: 3/4/0/0 4 1 - * 465: 3/4/4/0 4 7 - * 470: 3/3/4/4 5 5 - * 480: 3/4/4/4 6 6 - */ - - memset(data, 0x00, sizeof(data)); - memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr)); - for (i = 0, gpc = -1; i < priv->tpc_total; i++) { - do { - gpc = (gpc + 1) % priv->gpc_nr; - } while (!tpcnr[gpc]); - tpc = priv->tpc_nr[gpc] - tpcnr[gpc]--; - - data[i / 8] |= tpc << ((i % 8) * 4); - } - - nv_wr32(priv, GPC_BCAST(0x0980), data[0]); - nv_wr32(priv, GPC_BCAST(0x0984), data[1]); - nv_wr32(priv, GPC_BCAST(0x0988), data[2]); - nv_wr32(priv, GPC_BCAST(0x098c), data[3]); - - for (gpc = 0; gpc < priv->gpc_nr; gpc++) { - nv_wr32(priv, GPC_UNIT(gpc, 0x0914), priv->magic_not_rop_nr << 8 | - priv->tpc_nr[gpc]); - nv_wr32(priv, GPC_UNIT(gpc, 0x0910), 0x00040000 | priv->tpc_total); - nv_wr32(priv, GPC_UNIT(gpc, 0x0918), magicgpc918); - } - - nv_wr32(priv, GPC_BCAST(0x1bd4), magicgpc918); - nv_wr32(priv, GPC_BCAST(0x08ac), nv_rd32(priv, 0x100800)); -} - -static void -nvc0_graph_init_units(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x409c24, 0x000f0000); - nv_wr32(priv, 0x404000, 0xc0000000); /* DISPATCH */ - nv_wr32(priv, 0x404600, 0xc0000000); /* M2MF */ - nv_wr32(priv, 0x408030, 0xc0000000); - nv_wr32(priv, 0x40601c, 0xc0000000); - nv_wr32(priv, 0x404490, 0xc0000000); /* MACRO */ - nv_wr32(priv, 0x406018, 0xc0000000); - nv_wr32(priv, 0x405840, 0xc0000000); - nv_wr32(priv, 0x405844, 0x00ffffff); - nv_mask(priv, 0x419cc0, 0x00000008, 0x00000008); - nv_mask(priv, 0x419eb4, 0x00001000, 0x00001000); -} - -static void -nvc0_graph_init_gpc_1(struct nvc0_graph_priv *priv) -{ - int gpc, tpc; - - for (gpc = 0; gpc < priv->gpc_nr; gpc++) { - nv_wr32(priv, GPC_UNIT(gpc, 0x0420), 0xc0000000); - nv_wr32(priv, GPC_UNIT(gpc, 0x0900), 0xc0000000); - nv_wr32(priv, GPC_UNIT(gpc, 0x1028), 0xc0000000); - nv_wr32(priv, GPC_UNIT(gpc, 0x0824), 0xc0000000); - for (tpc = 0; tpc < priv->tpc_nr[gpc]; tpc++) { - nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x508), 0xffffffff); - nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x50c), 0xffffffff); - nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x224), 0xc0000000); - nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x48c), 0xc0000000); - nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x084), 0xc0000000); - nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x644), 0x001ffffe); - nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x64c), 0x0000000f); - } - nv_wr32(priv, GPC_UNIT(gpc, 0x2c90), 0xffffffff); - nv_wr32(priv, GPC_UNIT(gpc, 0x2c94), 0xffffffff); - } -} - -static void -nvc0_graph_init_rop(struct nvc0_graph_priv *priv) -{ - int rop; - - for (rop = 0; rop < priv->rop_nr; rop++) { - nv_wr32(priv, ROP_UNIT(rop, 0x144), 0xc0000000); - nv_wr32(priv, ROP_UNIT(rop, 0x070), 0xc0000000); - nv_wr32(priv, ROP_UNIT(rop, 0x204), 0xffffffff); - nv_wr32(priv, ROP_UNIT(rop, 0x208), 0xffffffff); - } -} - -void -nvc0_graph_init_fw(struct nvc0_graph_priv *priv, u32 fuc_base, - struct nvc0_graph_fuc *code, struct nvc0_graph_fuc *data) -{ - int i; - - nv_wr32(priv, fuc_base + 0x01c0, 0x01000000); - for (i = 0; i < data->size / 4; i++) - nv_wr32(priv, fuc_base + 0x01c4, data->data[i]); - - nv_wr32(priv, fuc_base + 0x0180, 0x01000000); - for (i = 0; i < code->size / 4; i++) { - if ((i & 0x3f) == 0) - nv_wr32(priv, fuc_base + 0x0188, i >> 6); - nv_wr32(priv, fuc_base + 0x0184, code->data[i]); - } -} - -static int -nvc0_graph_init_ctxctl(struct nvc0_graph_priv *priv) -{ - u32 r000260; - int i; - - if (priv->firmware) { - /* load fuc microcode */ - r000260 = nv_mask(priv, 0x000260, 0x00000001, 0x00000000); - nvc0_graph_init_fw(priv, 0x409000, &priv->fuc409c, - &priv->fuc409d); - nvc0_graph_init_fw(priv, 0x41a000, &priv->fuc41ac, - &priv->fuc41ad); - nv_wr32(priv, 0x000260, r000260); - - /* start both of them running */ - nv_wr32(priv, 0x409840, 0xffffffff); - nv_wr32(priv, 0x41a10c, 0x00000000); - nv_wr32(priv, 0x40910c, 0x00000000); - nv_wr32(priv, 0x41a100, 0x00000002); - nv_wr32(priv, 0x409100, 0x00000002); - if (!nv_wait(priv, 0x409800, 0x00000001, 0x00000001)) - nv_warn(priv, "0x409800 wait failed\n"); - - nv_wr32(priv, 0x409840, 0xffffffff); - nv_wr32(priv, 0x409500, 0x7fffffff); - nv_wr32(priv, 0x409504, 0x00000021); - - nv_wr32(priv, 0x409840, 0xffffffff); - nv_wr32(priv, 0x409500, 0x00000000); - nv_wr32(priv, 0x409504, 0x00000010); - if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) { - nv_error(priv, "fuc09 req 0x10 timeout\n"); - return -EBUSY; - } - priv->size = nv_rd32(priv, 0x409800); + nv_wr32(priv, 0x409840, 0xffffffff); + nv_wr32(priv, 0x409500, 0x00000000); + nv_wr32(priv, 0x409504, 0x00000010); + if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) { + nv_error(priv, "fuc09 req 0x10 timeout\n"); + return -EBUSY; + } + priv->size = nv_rd32(priv, 0x409800); nv_wr32(priv, 0x409840, 0xffffffff); nv_wr32(priv, 0x409500, 0x00000000); @@ -1424,6 +818,38 @@ nvc0_graph_init_ctxctl(struct nvc0_graph_priv *priv) return -EBUSY; } + if (nv_device(priv)->chipset >= 0xe0) { + nv_wr32(priv, 0x409800, 0x00000000); + nv_wr32(priv, 0x409500, 0x00000001); + nv_wr32(priv, 0x409504, 0x00000030); + if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) { + nv_error(priv, "fuc09 req 0x30 timeout\n"); + return -EBUSY; + } + + nv_wr32(priv, 0x409810, 0xb00095c8); + nv_wr32(priv, 0x409800, 0x00000000); + nv_wr32(priv, 0x409500, 0x00000001); + nv_wr32(priv, 0x409504, 0x00000031); + if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) { + nv_error(priv, "fuc09 req 0x31 timeout\n"); + return -EBUSY; + } + + nv_wr32(priv, 0x409810, 0x00080420); + nv_wr32(priv, 0x409800, 0x00000000); + nv_wr32(priv, 0x409500, 0x00000001); + nv_wr32(priv, 0x409504, 0x00000032); + if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) { + nv_error(priv, "fuc09 req 0x32 timeout\n"); + return -EBUSY; + } + + nv_wr32(priv, 0x409614, 0x00000070); + nv_wr32(priv, 0x409614, 0x00000770); + nv_wr32(priv, 0x40802c, 0x00000001); + } + if (priv->data == NULL) { int ret = nvc0_grctx_generate(priv); if (ret) { @@ -1438,26 +864,26 @@ nvc0_graph_init_ctxctl(struct nvc0_graph_priv *priv) /* load HUB microcode */ r000260 = nv_mask(priv, 0x000260, 0x00000001, 0x00000000); nv_wr32(priv, 0x4091c0, 0x01000000); - for (i = 0; i < sizeof(nvc0_grhub_data) / 4; i++) - nv_wr32(priv, 0x4091c4, nvc0_grhub_data[i]); + for (i = 0; i < oclass->fecs.ucode->data.size / 4; i++) + nv_wr32(priv, 0x4091c4, oclass->fecs.ucode->data.data[i]); nv_wr32(priv, 0x409180, 0x01000000); - for (i = 0; i < sizeof(nvc0_grhub_code) / 4; i++) { + for (i = 0; i < oclass->fecs.ucode->code.size / 4; i++) { if ((i & 0x3f) == 0) nv_wr32(priv, 0x409188, i >> 6); - nv_wr32(priv, 0x409184, nvc0_grhub_code[i]); + nv_wr32(priv, 0x409184, oclass->fecs.ucode->code.data[i]); } /* load GPC microcode */ nv_wr32(priv, 0x41a1c0, 0x01000000); - for (i = 0; i < sizeof(nvc0_grgpc_data) / 4; i++) - nv_wr32(priv, 0x41a1c4, nvc0_grgpc_data[i]); + for (i = 0; i < oclass->gpccs.ucode->data.size / 4; i++) + nv_wr32(priv, 0x41a1c4, oclass->gpccs.ucode->data.data[i]); nv_wr32(priv, 0x41a180, 0x01000000); - for (i = 0; i < sizeof(nvc0_grgpc_code) / 4; i++) { + for (i = 0; i < oclass->gpccs.ucode->code.size / 4; i++) { if ((i & 0x3f) == 0) nv_wr32(priv, 0x41a188, i >> 6); - nv_wr32(priv, 0x41a184, nvc0_grgpc_code[i]); + nv_wr32(priv, 0x41a184, oclass->gpccs.ucode->code.data[i]); } nv_wr32(priv, 0x000260, r000260); @@ -1483,38 +909,103 @@ nvc0_graph_init_ctxctl(struct nvc0_graph_priv *priv) return 0; } -static int +int nvc0_graph_init(struct nouveau_object *object) { + struct nvc0_graph_oclass *oclass = (void *)object->oclass; struct nvc0_graph_priv *priv = (void *)object; - int ret; + const u32 magicgpc918 = DIV_ROUND_UP(0x00800000, priv->tpc_total); + u32 data[TPC_MAX / 8] = {}; + u8 tpcnr[GPC_MAX]; + int gpc, tpc, rop; + int ret, i; ret = nouveau_graph_init(&priv->base); if (ret) return ret; - nvc0_graph_init_obj418880(priv); - nvc0_graph_init_regs(priv); - nvc0_graph_init_unk40xx(priv); - nvc0_graph_init_unk44xx(priv); - nvc0_graph_init_unk78xx(priv); - nvc0_graph_init_unk60xx(priv); - nvc0_graph_init_unk64xx(priv); - nvc0_graph_init_unk58xx(priv); - nvc0_graph_init_unk80xx(priv); - nvc0_graph_init_gpc(priv); - nvc0_graph_init_tpc(priv); - nvc0_graph_init_unk88xx(priv); - nvc0_graph_init_gpc_0(priv); - /*nvc0_graph_init_unitplemented_c242(priv);*/ + nv_wr32(priv, GPC_BCAST(0x0880), 0x00000000); + nv_wr32(priv, GPC_BCAST(0x08a4), 0x00000000); + nv_wr32(priv, GPC_BCAST(0x0888), 0x00000000); + nv_wr32(priv, GPC_BCAST(0x088c), 0x00000000); + nv_wr32(priv, GPC_BCAST(0x0890), 0x00000000); + nv_wr32(priv, GPC_BCAST(0x0894), 0x00000000); + nv_wr32(priv, GPC_BCAST(0x08b4), priv->unk4188b4->addr >> 8); + nv_wr32(priv, GPC_BCAST(0x08b8), priv->unk4188b8->addr >> 8); + + for (i = 0; oclass->mmio[i]; i++) + nvc0_graph_mmio(priv, oclass->mmio[i]); + + /* affects TFB offset queries */ + nv_wr32(priv, TPC_UNIT(0, 0, 0x5c), 1); + + memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr)); + for (i = 0, gpc = -1; i < priv->tpc_total; i++) { + do { + gpc = (gpc + 1) % priv->gpc_nr; + } while (!tpcnr[gpc]); + tpc = priv->tpc_nr[gpc] - tpcnr[gpc]--; + + data[i / 8] |= tpc << ((i % 8) * 4); + } + + nv_wr32(priv, GPC_BCAST(0x0980), data[0]); + nv_wr32(priv, GPC_BCAST(0x0984), data[1]); + nv_wr32(priv, GPC_BCAST(0x0988), data[2]); + nv_wr32(priv, GPC_BCAST(0x098c), data[3]); + + for (gpc = 0; gpc < priv->gpc_nr; gpc++) { + nv_wr32(priv, GPC_UNIT(gpc, 0x0914), + priv->magic_not_rop_nr << 8 | priv->tpc_nr[gpc]); + nv_wr32(priv, GPC_UNIT(gpc, 0x0910), 0x00040000 | + priv->tpc_total); + nv_wr32(priv, GPC_UNIT(gpc, 0x0918), magicgpc918); + } + + nv_wr32(priv, GPC_BCAST(0x1bd4), magicgpc918); + nv_wr32(priv, GPC_BCAST(0x08ac), nv_rd32(priv, 0x100800)); nv_wr32(priv, 0x400500, 0x00010001); + nv_wr32(priv, 0x400100, 0xffffffff); nv_wr32(priv, 0x40013c, 0xffffffff); - nvc0_graph_init_units(priv); - nvc0_graph_init_gpc_1(priv); - nvc0_graph_init_rop(priv); + nv_wr32(priv, 0x409c24, 0x000f0000); + nv_wr32(priv, 0x404000, 0xc0000000); + nv_wr32(priv, 0x404600, 0xc0000000); + nv_wr32(priv, 0x408030, 0xc0000000); + nv_wr32(priv, 0x40601c, 0xc0000000); + nv_wr32(priv, 0x404490, 0xc0000000); + nv_wr32(priv, 0x406018, 0xc0000000); + nv_wr32(priv, 0x405840, 0xc0000000); + nv_wr32(priv, 0x405844, 0x00ffffff); + nv_mask(priv, 0x419cc0, 0x00000008, 0x00000008); + nv_mask(priv, 0x419eb4, 0x00001000, 0x00001000); + + for (gpc = 0; gpc < priv->gpc_nr; gpc++) { + nv_wr32(priv, GPC_UNIT(gpc, 0x0420), 0xc0000000); + nv_wr32(priv, GPC_UNIT(gpc, 0x0900), 0xc0000000); + nv_wr32(priv, GPC_UNIT(gpc, 0x1028), 0xc0000000); + nv_wr32(priv, GPC_UNIT(gpc, 0x0824), 0xc0000000); + for (tpc = 0; tpc < priv->tpc_nr[gpc]; tpc++) { + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x508), 0xffffffff); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x50c), 0xffffffff); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x224), 0xc0000000); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x48c), 0xc0000000); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x084), 0xc0000000); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x644), 0x001ffffe); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x64c), 0x0000000f); + } + nv_wr32(priv, GPC_UNIT(gpc, 0x2c90), 0xffffffff); + nv_wr32(priv, GPC_UNIT(gpc, 0x2c94), 0xffffffff); + } + + for (rop = 0; rop < priv->rop_nr; rop++) { + nv_wr32(priv, ROP_UNIT(rop, 0x144), 0xc0000000); + nv_wr32(priv, ROP_UNIT(rop, 0x070), 0xc0000000); + nv_wr32(priv, ROP_UNIT(rop, 0x204), 0xffffffff); + nv_wr32(priv, ROP_UNIT(rop, 0x208), 0xffffffff); + } nv_wr32(priv, 0x400108, 0xffffffff); nv_wr32(priv, 0x400138, 0xffffffff); @@ -1522,22 +1013,203 @@ nvc0_graph_init(struct nouveau_object *object) nv_wr32(priv, 0x400130, 0xffffffff); nv_wr32(priv, 0x40011c, 0xffffffff); nv_wr32(priv, 0x400134, 0xffffffff); + nv_wr32(priv, 0x400054, 0x34ce3464); + return nvc0_graph_init_ctxctl(priv); +} + +static void +nvc0_graph_dtor_fw(struct nvc0_graph_fuc *fuc) +{ + kfree(fuc->data); + fuc->data = NULL; +} + +int +nvc0_graph_ctor_fw(struct nvc0_graph_priv *priv, const char *fwname, + struct nvc0_graph_fuc *fuc) +{ + struct nouveau_device *device = nv_device(priv); + const struct firmware *fw; + char f[32]; + int ret; + + snprintf(f, sizeof(f), "nouveau/nv%02x_%s", device->chipset, fwname); + ret = request_firmware(&fw, f, &device->pdev->dev); + if (ret) { + snprintf(f, sizeof(f), "nouveau/%s", fwname); + ret = request_firmware(&fw, f, &device->pdev->dev); + if (ret) { + nv_error(priv, "failed to load %s\n", fwname); + return ret; + } + } + + fuc->size = fw->size; + fuc->data = kmemdup(fw->data, fuc->size, GFP_KERNEL); + release_firmware(fw); + return (fuc->data != NULL) ? 0 : -ENOMEM; +} + +void +nvc0_graph_dtor(struct nouveau_object *object) +{ + struct nvc0_graph_priv *priv = (void *)object; + + kfree(priv->data); + + nvc0_graph_dtor_fw(&priv->fuc409c); + nvc0_graph_dtor_fw(&priv->fuc409d); + nvc0_graph_dtor_fw(&priv->fuc41ac); + nvc0_graph_dtor_fw(&priv->fuc41ad); + + nouveau_gpuobj_ref(NULL, &priv->unk4188b8); + nouveau_gpuobj_ref(NULL, &priv->unk4188b4); + + nouveau_graph_destroy(&priv->base); +} + +int +nvc0_graph_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *bclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nvc0_graph_oclass *oclass = (void *)bclass; + struct nouveau_device *device = nv_device(parent); + struct nvc0_graph_priv *priv; + bool enable = device->chipset != 0xd7; + int ret, i; + + ret = nouveau_graph_create(parent, engine, bclass, enable, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x18001000; + nv_subdev(priv)->intr = nvc0_graph_intr; + + priv->base.units = nvc0_graph_units; + + if (nouveau_boolopt(device->cfgopt, "NvGrUseFW", false)) { + nv_info(priv, "using external firmware\n"); + if (nvc0_graph_ctor_fw(priv, "fuc409c", &priv->fuc409c) || + nvc0_graph_ctor_fw(priv, "fuc409d", &priv->fuc409d) || + nvc0_graph_ctor_fw(priv, "fuc41ac", &priv->fuc41ac) || + nvc0_graph_ctor_fw(priv, "fuc41ad", &priv->fuc41ad)) + return -EINVAL; + priv->firmware = true; + } + + ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x1000, 256, 0, + &priv->unk4188b4); + if (ret) + return ret; - ret = nvc0_graph_init_ctxctl(priv); + ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x1000, 256, 0, + &priv->unk4188b8); if (ret) return ret; + for (i = 0; i < 0x1000; i += 4) { + nv_wo32(priv->unk4188b4, i, 0x00000010); + nv_wo32(priv->unk4188b8, i, 0x00000010); + } + + priv->rop_nr = (nv_rd32(priv, 0x409604) & 0x001f0000) >> 16; + priv->gpc_nr = nv_rd32(priv, 0x409604) & 0x0000001f; + for (i = 0; i < priv->gpc_nr; i++) { + priv->tpc_nr[i] = nv_rd32(priv, GPC_UNIT(i, 0x2608)); + priv->tpc_total += priv->tpc_nr[i]; + } + + /*XXX: these need figuring out... though it might not even matter */ + switch (nv_device(priv)->chipset) { + case 0xc0: + if (priv->tpc_total == 11) { /* 465, 3/4/4/0, 4 */ + priv->magic_not_rop_nr = 0x07; + } else + if (priv->tpc_total == 14) { /* 470, 3/3/4/4, 5 */ + priv->magic_not_rop_nr = 0x05; + } else + if (priv->tpc_total == 15) { /* 480, 3/4/4/4, 6 */ + priv->magic_not_rop_nr = 0x06; + } + break; + case 0xc3: /* 450, 4/0/0/0, 2 */ + priv->magic_not_rop_nr = 0x03; + break; + case 0xc4: /* 460, 3/4/0/0, 4 */ + priv->magic_not_rop_nr = 0x01; + break; + case 0xc1: /* 2/0/0/0, 1 */ + priv->magic_not_rop_nr = 0x01; + break; + case 0xc8: /* 4/4/3/4, 5 */ + priv->magic_not_rop_nr = 0x06; + break; + case 0xce: /* 4/4/0/0, 4 */ + priv->magic_not_rop_nr = 0x03; + break; + case 0xcf: /* 4/0/0/0, 3 */ + priv->magic_not_rop_nr = 0x03; + break; + case 0xd9: /* 1/0/0/0, 1 */ + priv->magic_not_rop_nr = 0x01; + break; + } + + nv_engine(priv)->cclass = *oclass->cclass; + nv_engine(priv)->sclass = oclass->sclass; return 0; } -struct nouveau_oclass -nvc0_graph_oclass = { - .handle = NV_ENGINE(GR, 0xc0), - .ofuncs = &(struct nouveau_ofuncs) { +struct nvc0_graph_init * +nvc0_graph_init_mmio[] = { + nvc0_graph_init_regs, + nvc0_graph_init_unk40xx, + nvc0_graph_init_unk44xx, + nvc0_graph_init_unk78xx, + nvc0_graph_init_unk60xx, + nvc0_graph_init_unk58xx, + nvc0_graph_init_unk80xx, + nvc0_graph_init_gpc, + nvc0_graph_init_tpc, + nvc0_graph_init_unk88xx, + NULL +}; + +#include "fuc/hubnvc0.fuc.h" + +struct nvc0_graph_ucode +nvc0_graph_fecs_ucode = { + .code.data = nvc0_grhub_code, + .code.size = sizeof(nvc0_grhub_code), + .data.data = nvc0_grhub_data, + .data.size = sizeof(nvc0_grhub_data), +}; + +#include "fuc/gpcnvc0.fuc.h" + +struct nvc0_graph_ucode +nvc0_graph_gpccs_ucode = { + .code.data = nvc0_grgpc_code, + .code.size = sizeof(nvc0_grgpc_code), + .data.data = nvc0_grgpc_data, + .data.size = sizeof(nvc0_grgpc_data), +}; + +struct nouveau_oclass * +nvc0_graph_oclass = &(struct nvc0_graph_oclass) { + .base.handle = NV_ENGINE(GR, 0xc0), + .base.ofuncs = &(struct nouveau_ofuncs) { .ctor = nvc0_graph_ctor, .dtor = nvc0_graph_dtor, .init = nvc0_graph_init, .fini = _nouveau_graph_fini, }, -}; + .cclass = &nvc0_grctx_oclass, + .sclass = nvc0_graph_sclass, + .mmio = nvc0_graph_init_mmio, + .fecs.ucode = &nvc0_graph_fecs_ucode, + .gpccs.ucode = &nvc0_graph_gpccs_ucode, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h index af7212d96f3f..f7d0df7c84ad 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h @@ -102,76 +102,178 @@ struct nvc0_graph_chan { } data[4]; }; -static inline u32 -nvc0_graph_class(void *obj) -{ - struct nouveau_device *device = nv_device(obj); - - switch (device->chipset) { - case 0xc0: - case 0xc3: - case 0xc4: - case 0xce: /* guess, mmio trace shows only 0x9097 state */ - case 0xcf: /* guess, mmio trace shows only 0x9097 state */ - return 0x9097; - case 0xc1: - return 0x9197; - case 0xc8: - case 0xd9: - case 0xd7: - return 0x9297; - case 0xe4: - case 0xe7: - case 0xe6: - return 0xa097; - case 0xf0: - return 0xa197; - default: - return 0; - } -} - -void nv_icmd(struct nvc0_graph_priv *priv, u32 icmd, u32 data); - -static inline void -nv_mthd(struct nvc0_graph_priv *priv, u32 class, u32 mthd, u32 data) -{ - nv_wr32(priv, 0x40448c, data); - nv_wr32(priv, 0x404488, 0x80000000 | (mthd << 14) | class); -} +int nvc0_grctx_generate(struct nvc0_graph_priv *); + +int nvc0_graph_context_ctor(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, void *, u32, + struct nouveau_object **); +void nvc0_graph_context_dtor(struct nouveau_object *); + +void nvc0_graph_ctxctl_debug(struct nvc0_graph_priv *); + +u64 nvc0_graph_units(struct nouveau_graph *); +int nvc0_graph_ctor(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, void *data, u32 size, + struct nouveau_object **); +void nvc0_graph_dtor(struct nouveau_object *); +int nvc0_graph_init(struct nouveau_object *); +int nve4_graph_init(struct nouveau_object *); + +extern struct nouveau_oclass nvc0_graph_sclass[]; + +extern struct nouveau_oclass nvc8_graph_sclass[]; + +struct nvc0_graph_init { + u32 addr; + u8 count; + u8 pitch; + u32 data; +}; + +struct nvc0_graph_mthd { + u16 oclass; + struct nvc0_graph_init *init; +}; struct nvc0_grctx { struct nvc0_graph_priv *priv; struct nvc0_graph_data *data; struct nvc0_graph_mmio *mmio; - struct nouveau_gpuobj *chan; int buffer_nr; u64 buffer[4]; u64 addr; }; +struct nvc0_grctx_oclass { + struct nouveau_oclass base; + /* main context generation function */ + void (*main)(struct nvc0_graph_priv *, struct nvc0_grctx *); + /* context-specific modify-on-first-load list generation function */ + void (*mods)(struct nvc0_graph_priv *, struct nvc0_grctx *); + /* mmio context data */ + struct nvc0_graph_init **mmio; + struct nvc0_graph_init *gpc; + struct nvc0_graph_init *tpc; + /* indirect context data, generated with icmds/mthds */ + struct nvc0_graph_init *icmd; + struct nvc0_graph_mthd *mthd; +}; + +struct nvc0_graph_ucode { + struct nvc0_graph_fuc code; + struct nvc0_graph_fuc data; +}; + +extern struct nvc0_graph_ucode nvc0_graph_fecs_ucode; +extern struct nvc0_graph_ucode nvc0_graph_gpccs_ucode; + +struct nvc0_graph_oclass { + struct nouveau_oclass base; + struct nouveau_oclass **cclass; + struct nouveau_oclass *sclass; + struct nvc0_graph_init **mmio; + struct { + struct nvc0_graph_ucode *ucode; + } fecs; + struct { + struct nvc0_graph_ucode *ucode; + } gpccs; +}; + +void nvc0_graph_mmio(struct nvc0_graph_priv *, struct nvc0_graph_init *); +void nvc0_graph_icmd(struct nvc0_graph_priv *, struct nvc0_graph_init *); +void nvc0_graph_mthd(struct nvc0_graph_priv *, struct nvc0_graph_mthd *); +int nvc0_graph_init_ctxctl(struct nvc0_graph_priv *); + +extern struct nvc0_graph_init nvc0_graph_init_regs[]; +extern struct nvc0_graph_init nvc0_graph_init_unk40xx[]; +extern struct nvc0_graph_init nvc0_graph_init_unk44xx[]; +extern struct nvc0_graph_init nvc0_graph_init_unk78xx[]; +extern struct nvc0_graph_init nvc0_graph_init_unk60xx[]; +extern struct nvc0_graph_init nvc0_graph_init_unk58xx[]; +extern struct nvc0_graph_init nvc0_graph_init_unk80xx[]; +extern struct nvc0_graph_init nvc0_graph_init_gpc[]; +extern struct nvc0_graph_init nvc0_graph_init_unk88xx[]; + +extern struct nvc0_graph_init nvc3_graph_init_unk58xx[]; + +extern struct nvc0_graph_init nvd9_graph_init_unk64xx[]; + +extern struct nvc0_graph_init nve4_graph_init_regs[]; +extern struct nvc0_graph_init nve4_graph_init_unk[]; +extern struct nvc0_graph_init nve4_graph_init_unk88xx[]; + int nvc0_grctx_generate(struct nvc0_graph_priv *); -int nvc0_grctx_init(struct nvc0_graph_priv *, struct nvc0_grctx *); -void nvc0_grctx_data(struct nvc0_grctx *, u32, u32, u32); -void nvc0_grctx_mmio(struct nvc0_grctx *, u32, u32, u32, u32); -int nvc0_grctx_fini(struct nvc0_grctx *); +void nvc0_grctx_generate_main(struct nvc0_graph_priv *, struct nvc0_grctx *); +void nvc0_grctx_generate_mods(struct nvc0_graph_priv *, struct nvc0_grctx *); +void nvc0_grctx_generate_tpcid(struct nvc0_graph_priv *); +void nvc0_grctx_generate_r406028(struct nvc0_graph_priv *); +void nvc0_grctx_generate_r4060a8(struct nvc0_graph_priv *); +void nvc0_grctx_generate_r418bb8(struct nvc0_graph_priv *); +void nvc0_grctx_generate_r406800(struct nvc0_graph_priv *); -int nve0_grctx_generate(struct nvc0_graph_priv *); +extern struct nouveau_oclass *nvc0_grctx_oclass; +extern struct nvc0_graph_init *nvc0_grctx_init_mmio[]; +extern struct nvc0_graph_init nvc0_grctx_init_base[]; +extern struct nvc0_graph_init nvc0_grctx_init_unk40xx[]; +extern struct nvc0_graph_init nvc0_grctx_init_unk44xx[]; +extern struct nvc0_graph_init nvc0_grctx_init_unk46xx[]; +extern struct nvc0_graph_init nvc0_grctx_init_unk47xx[]; +extern struct nvc0_graph_init nvc0_grctx_init_unk60xx[]; +extern struct nvc0_graph_init nvc0_grctx_init_unk64xx[]; +extern struct nvc0_graph_init nvc0_grctx_init_unk78xx[]; +extern struct nvc0_graph_init nvc0_grctx_init_unk80xx[]; +extern struct nvc0_graph_init nvc0_grctx_init_gpc[]; +extern struct nvc0_graph_init nvc0_grctx_init_tpc[]; +extern struct nvc0_graph_init nvc0_grctx_init_icmd[]; +extern struct nvc0_graph_init nvd9_grctx_init_icmd[]; // -#define mmio_data(s,a,p) nvc0_grctx_data(&info, (s), (a), (p)) -#define mmio_list(r,d,s,b) nvc0_grctx_mmio(&info, (r), (d), (s), (b)) +extern struct nvc0_graph_mthd nvc0_grctx_init_mthd[]; +extern struct nvc0_graph_init nvc0_grctx_init_902d[]; +extern struct nvc0_graph_init nvc0_grctx_init_9039[]; +extern struct nvc0_graph_init nvc0_grctx_init_90c0[]; +extern struct nvc0_graph_init nvc0_grctx_init_mthd_magic[]; -void nvc0_graph_ctxctl_debug(struct nvc0_graph_priv *); -int nvc0_graph_ctor_fw(struct nvc0_graph_priv *, const char *, - struct nvc0_graph_fuc *); -void nvc0_graph_dtor(struct nouveau_object *); -void nvc0_graph_init_fw(struct nvc0_graph_priv *, u32 base, - struct nvc0_graph_fuc *, struct nvc0_graph_fuc *); -int nvc0_graph_context_ctor(struct nouveau_object *, struct nouveau_object *, - struct nouveau_oclass *, void *, u32, - struct nouveau_object **); -void nvc0_graph_context_dtor(struct nouveau_object *); +void nvc1_grctx_generate_mods(struct nvc0_graph_priv *, struct nvc0_grctx *); +extern struct nouveau_oclass *nvc1_grctx_oclass; +extern struct nvc0_graph_init nvc1_grctx_init_9097[]; + +extern struct nouveau_oclass *nvc3_grctx_oclass; + +extern struct nouveau_oclass *nvc8_grctx_oclass; +extern struct nvc0_graph_init nvc8_grctx_init_9197[]; +extern struct nvc0_graph_init nvc8_grctx_init_9297[]; + +extern struct nouveau_oclass *nvd9_grctx_oclass; +extern struct nvc0_graph_init nvd9_grctx_init_rop[]; + +void nve4_grctx_generate_main(struct nvc0_graph_priv *, struct nvc0_grctx *); +void nve4_grctx_generate_mods(struct nvc0_graph_priv *, struct nvc0_grctx *); +extern struct nouveau_oclass *nve4_grctx_oclass; +extern struct nvc0_graph_init nve4_grctx_init_unk46xx[]; +extern struct nvc0_graph_init nve4_grctx_init_unk47xx[]; +extern struct nvc0_graph_init nve4_grctx_init_unk58xx[]; +extern struct nvc0_graph_init nve4_grctx_init_unk80xx[]; +extern struct nvc0_graph_init nve4_grctx_init_unk90xx[]; + +extern struct nouveau_oclass *nvf0_grctx_oclass; + +#define mmio_data(s,a,p) do { \ + info->buffer[info->buffer_nr] = round_up(info->addr, (a)); \ + info->addr = info->buffer[info->buffer_nr++] + (s); \ + info->data->size = (s); \ + info->data->align = (a); \ + info->data->access = (p); \ + info->data++; \ +} while(0) -u64 nvc0_graph_units(struct nouveau_graph *); +#define mmio_list(r,d,s,b) do { \ + info->mmio->addr = (r); \ + info->mmio->data = (d); \ + info->mmio->shift = (s); \ + info->mmio->buffer = (b); \ + info->mmio++; \ + nv_wr32(priv, (r), (d) | ((s) ? (info->buffer[(b)] >> (s)) : 0)); \ +} while(0) #endif diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc1.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvc1.c new file mode 100644 index 000000000000..6d192d2719c7 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc1.c @@ -0,0 +1,143 @@ +/* + * Copyright 2013 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 "nvc0.h" + +/******************************************************************************* + * Graphics object classes + ******************************************************************************/ + +static struct nouveau_oclass +nvc1_graph_sclass[] = { + { 0x902d, &nouveau_object_ofuncs }, + { 0x9039, &nouveau_object_ofuncs }, + { 0x9097, &nouveau_object_ofuncs }, + { 0x90c0, &nouveau_object_ofuncs }, + { 0x9197, &nouveau_object_ofuncs }, + {} +}; + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +static struct nvc0_graph_init +nvc1_graph_init_gpc[] = { + { 0x4184a0, 1, 0x04, 0x00000000 }, + { 0x418604, 1, 0x04, 0x00000000 }, + { 0x418680, 1, 0x04, 0x00000000 }, + { 0x418714, 1, 0x04, 0x00000000 }, + { 0x418384, 1, 0x04, 0x00000000 }, + { 0x418814, 3, 0x04, 0x00000000 }, + { 0x418b04, 1, 0x04, 0x00000000 }, + { 0x4188c8, 2, 0x04, 0x00000000 }, + { 0x4188d0, 1, 0x04, 0x00010000 }, + { 0x4188d4, 1, 0x04, 0x00000001 }, + { 0x418910, 1, 0x04, 0x00010001 }, + { 0x418914, 1, 0x04, 0x00000301 }, + { 0x418918, 1, 0x04, 0x00800000 }, + { 0x418980, 1, 0x04, 0x77777770 }, + { 0x418984, 3, 0x04, 0x77777777 }, + { 0x418c04, 1, 0x04, 0x00000000 }, + { 0x418c88, 1, 0x04, 0x00000000 }, + { 0x418d00, 1, 0x04, 0x00000000 }, + { 0x418f08, 1, 0x04, 0x00000000 }, + { 0x418e00, 1, 0x04, 0x00000003 }, + { 0x418e08, 1, 0x04, 0x00000000 }, + { 0x41900c, 1, 0x04, 0x00000000 }, + { 0x419018, 1, 0x04, 0x00000000 }, + {} +}; + +static struct nvc0_graph_init +nvc1_graph_init_tpc[] = { + { 0x419d08, 2, 0x04, 0x00000000 }, + { 0x419d10, 1, 0x04, 0x00000014 }, + { 0x419ab0, 1, 0x04, 0x00000000 }, + { 0x419ac8, 1, 0x04, 0x00000000 }, + { 0x419ab8, 1, 0x04, 0x000000e7 }, + { 0x419abc, 2, 0x04, 0x00000000 }, + { 0x41980c, 2, 0x04, 0x00000000 }, + { 0x419814, 1, 0x04, 0x00000004 }, + { 0x419844, 1, 0x04, 0x00000000 }, + { 0x41984c, 1, 0x04, 0x00005bc5 }, + { 0x419850, 4, 0x04, 0x00000000 }, + { 0x419880, 1, 0x04, 0x00000002 }, + { 0x419c98, 1, 0x04, 0x00000000 }, + { 0x419ca8, 1, 0x04, 0x80000000 }, + { 0x419cb4, 1, 0x04, 0x00000000 }, + { 0x419cb8, 1, 0x04, 0x00008bf4 }, + { 0x419cbc, 1, 0x04, 0x28137606 }, + { 0x419cc0, 2, 0x04, 0x00000000 }, + { 0x419bd4, 1, 0x04, 0x00800000 }, + { 0x419bdc, 1, 0x04, 0x00000000 }, + { 0x419d2c, 1, 0x04, 0x00000000 }, + { 0x419c0c, 1, 0x04, 0x00000000 }, + { 0x419e00, 1, 0x04, 0x00000000 }, + { 0x419ea0, 1, 0x04, 0x00000000 }, + { 0x419ea4, 1, 0x04, 0x00000100 }, + { 0x419ea8, 1, 0x04, 0x00001100 }, + { 0x419eac, 1, 0x04, 0x11100702 }, + { 0x419eb0, 1, 0x04, 0x00000003 }, + { 0x419eb4, 4, 0x04, 0x00000000 }, + { 0x419ec8, 1, 0x04, 0x0e063818 }, + { 0x419ecc, 1, 0x04, 0x0e060e06 }, + { 0x419ed0, 1, 0x04, 0x00003818 }, + { 0x419ed4, 1, 0x04, 0x011104f1 }, + { 0x419edc, 1, 0x04, 0x00000000 }, + { 0x419f00, 1, 0x04, 0x00000000 }, + { 0x419f2c, 1, 0x04, 0x00000000 }, + {} +}; + +struct nvc0_graph_init * +nvc1_graph_init_mmio[] = { + nvc0_graph_init_regs, + nvc0_graph_init_unk40xx, + nvc0_graph_init_unk44xx, + nvc0_graph_init_unk78xx, + nvc0_graph_init_unk60xx, + nvc3_graph_init_unk58xx, + nvc0_graph_init_unk80xx, + nvc1_graph_init_gpc, + nvc1_graph_init_tpc, + nvc0_graph_init_unk88xx, + NULL +}; + +struct nouveau_oclass * +nvc1_graph_oclass = &(struct nvc0_graph_oclass) { + .base.handle = NV_ENGINE(GR, 0xc1), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = nvc0_graph_ctor, + .dtor = nvc0_graph_dtor, + .init = nvc0_graph_init, + .fini = _nouveau_graph_fini, + }, + .cclass = &nvc1_grctx_oclass, + .sclass = nvc1_graph_sclass, + .mmio = nvc1_graph_init_mmio, + .fecs.ucode = &nvc0_graph_fecs_ucode, + .gpccs.ucode = &nvc0_graph_gpccs_ucode, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc3.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvc3.c new file mode 100644 index 000000000000..48d3c761af5b --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc3.c @@ -0,0 +1,109 @@ +/* + * Copyright 2013 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 "nvc0.h" + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +struct nvc0_graph_init +nvc3_graph_init_unk58xx[] = { + { 0x405844, 1, 0x04, 0x00ffffff }, + { 0x405850, 1, 0x04, 0x00000000 }, + { 0x405900, 1, 0x04, 0x00002834 }, + { 0x405908, 1, 0x04, 0x00000000 }, + {} +}; + +static struct nvc0_graph_init +nvc3_graph_init_tpc[] = { + { 0x419d08, 2, 0x04, 0x00000000 }, + { 0x419d10, 1, 0x04, 0x00000014 }, + { 0x419ab0, 1, 0x04, 0x00000000 }, + { 0x419ac8, 1, 0x04, 0x00000000 }, + { 0x419ab8, 1, 0x04, 0x000000e7 }, + { 0x419abc, 2, 0x04, 0x00000000 }, + { 0x41980c, 3, 0x04, 0x00000000 }, + { 0x419844, 1, 0x04, 0x00000000 }, + { 0x41984c, 1, 0x04, 0x00005bc5 }, + { 0x419850, 4, 0x04, 0x00000000 }, + { 0x419880, 1, 0x04, 0x00000002 }, + { 0x419c98, 1, 0x04, 0x00000000 }, + { 0x419ca8, 1, 0x04, 0x80000000 }, + { 0x419cb4, 1, 0x04, 0x00000000 }, + { 0x419cb8, 1, 0x04, 0x00008bf4 }, + { 0x419cbc, 1, 0x04, 0x28137606 }, + { 0x419cc0, 2, 0x04, 0x00000000 }, + { 0x419bd4, 1, 0x04, 0x00800000 }, + { 0x419bdc, 1, 0x04, 0x00000000 }, + { 0x419d2c, 1, 0x04, 0x00000000 }, + { 0x419c0c, 1, 0x04, 0x00000000 }, + { 0x419e00, 1, 0x04, 0x00000000 }, + { 0x419ea0, 1, 0x04, 0x00000000 }, + { 0x419ea4, 1, 0x04, 0x00000100 }, + { 0x419ea8, 1, 0x04, 0x00001100 }, + { 0x419eac, 1, 0x04, 0x11100702 }, + { 0x419eb0, 1, 0x04, 0x00000003 }, + { 0x419eb4, 4, 0x04, 0x00000000 }, + { 0x419ec8, 1, 0x04, 0x0e063818 }, + { 0x419ecc, 1, 0x04, 0x0e060e06 }, + { 0x419ed0, 1, 0x04, 0x00003818 }, + { 0x419ed4, 1, 0x04, 0x011104f1 }, + { 0x419edc, 1, 0x04, 0x00000000 }, + { 0x419f00, 1, 0x04, 0x00000000 }, + { 0x419f2c, 1, 0x04, 0x00000000 }, + {} +}; + +static struct nvc0_graph_init * +nvc3_graph_init_mmio[] = { + nvc0_graph_init_regs, + nvc0_graph_init_unk40xx, + nvc0_graph_init_unk44xx, + nvc0_graph_init_unk78xx, + nvc0_graph_init_unk60xx, + nvc3_graph_init_unk58xx, + nvc0_graph_init_unk80xx, + nvc0_graph_init_gpc, + nvc3_graph_init_tpc, + nvc0_graph_init_unk88xx, + NULL +}; + +struct nouveau_oclass * +nvc3_graph_oclass = &(struct nvc0_graph_oclass) { + .base.handle = NV_ENGINE(GR, 0xc3), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = nvc0_graph_ctor, + .dtor = nvc0_graph_dtor, + .init = nvc0_graph_init, + .fini = _nouveau_graph_fini, + }, + .cclass = &nvc3_grctx_oclass, + .sclass = nvc0_graph_sclass, + .mmio = nvc3_graph_init_mmio, + .fecs.ucode = &nvc0_graph_fecs_ucode, + .gpccs.ucode = &nvc0_graph_gpccs_ucode, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc8.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvc8.c new file mode 100644 index 000000000000..e5b5593a7024 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc8.c @@ -0,0 +1,140 @@ +/* + * Copyright 2013 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 "nvc0.h" + +/******************************************************************************* + * Graphics object classes + ******************************************************************************/ + +struct nouveau_oclass +nvc8_graph_sclass[] = { + { 0x902d, &nouveau_object_ofuncs }, + { 0x9039, &nouveau_object_ofuncs }, + { 0x9097, &nouveau_object_ofuncs }, + { 0x90c0, &nouveau_object_ofuncs }, + { 0x9197, &nouveau_object_ofuncs }, + { 0x9297, &nouveau_object_ofuncs }, + {} +}; + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +static struct nvc0_graph_init +nvc8_graph_init_gpc[] = { + { 0x4184a0, 1, 0x04, 0x00000000 }, + { 0x418604, 1, 0x04, 0x00000000 }, + { 0x418680, 1, 0x04, 0x00000000 }, + { 0x418714, 1, 0x04, 0x80000000 }, + { 0x418384, 1, 0x04, 0x00000000 }, + { 0x418814, 3, 0x04, 0x00000000 }, + { 0x418b04, 1, 0x04, 0x00000000 }, + { 0x4188c8, 2, 0x04, 0x00000000 }, + { 0x4188d0, 1, 0x04, 0x00010000 }, + { 0x4188d4, 1, 0x04, 0x00000001 }, + { 0x418910, 1, 0x04, 0x00010001 }, + { 0x418914, 1, 0x04, 0x00000301 }, + { 0x418918, 1, 0x04, 0x00800000 }, + { 0x418980, 1, 0x04, 0x77777770 }, + { 0x418984, 3, 0x04, 0x77777777 }, + { 0x418c04, 1, 0x04, 0x00000000 }, + { 0x418c88, 1, 0x04, 0x00000000 }, + { 0x418d00, 1, 0x04, 0x00000000 }, + { 0x418f08, 1, 0x04, 0x00000000 }, + { 0x418e00, 1, 0x04, 0x00000050 }, + { 0x418e08, 1, 0x04, 0x00000000 }, + { 0x41900c, 1, 0x04, 0x00000000 }, + { 0x419018, 1, 0x04, 0x00000000 }, + {} +}; + +static struct nvc0_graph_init +nvc8_graph_init_tpc[] = { + { 0x419d08, 2, 0x04, 0x00000000 }, + { 0x419d10, 1, 0x04, 0x00000014 }, + { 0x419ab0, 1, 0x04, 0x00000000 }, + { 0x419ab8, 1, 0x04, 0x000000e7 }, + { 0x419abc, 2, 0x04, 0x00000000 }, + { 0x41980c, 3, 0x04, 0x00000000 }, + { 0x419844, 1, 0x04, 0x00000000 }, + { 0x41984c, 1, 0x04, 0x00005bc5 }, + { 0x419850, 4, 0x04, 0x00000000 }, + { 0x419c98, 1, 0x04, 0x00000000 }, + { 0x419ca8, 1, 0x04, 0x80000000 }, + { 0x419cb4, 1, 0x04, 0x00000000 }, + { 0x419cb8, 1, 0x04, 0x00008bf4 }, + { 0x419cbc, 1, 0x04, 0x28137606 }, + { 0x419cc0, 2, 0x04, 0x00000000 }, + { 0x419bd4, 1, 0x04, 0x00800000 }, + { 0x419bdc, 1, 0x04, 0x00000000 }, + { 0x419d2c, 1, 0x04, 0x00000000 }, + { 0x419c0c, 1, 0x04, 0x00000000 }, + { 0x419e00, 1, 0x04, 0x00000000 }, + { 0x419ea0, 1, 0x04, 0x00000000 }, + { 0x419ea4, 1, 0x04, 0x00000100 }, + { 0x419ea8, 1, 0x04, 0x00001100 }, + { 0x419eac, 1, 0x04, 0x11100f02 }, + { 0x419eb0, 1, 0x04, 0x00000003 }, + { 0x419eb4, 4, 0x04, 0x00000000 }, + { 0x419ec8, 1, 0x04, 0x06060618 }, + { 0x419ed0, 1, 0x04, 0x0eff0e38 }, + { 0x419ed4, 1, 0x04, 0x011104f1 }, + { 0x419edc, 1, 0x04, 0x00000000 }, + { 0x419f00, 1, 0x04, 0x00000000 }, + { 0x419f2c, 1, 0x04, 0x00000000 }, + {} +}; + +static struct nvc0_graph_init * +nvc8_graph_init_mmio[] = { + nvc0_graph_init_regs, + nvc0_graph_init_unk40xx, + nvc0_graph_init_unk44xx, + nvc0_graph_init_unk78xx, + nvc0_graph_init_unk60xx, + nvc0_graph_init_unk58xx, + nvc0_graph_init_unk80xx, + nvc8_graph_init_gpc, + nvc8_graph_init_tpc, + nvc0_graph_init_unk88xx, + NULL +}; + +struct nouveau_oclass * +nvc8_graph_oclass = &(struct nvc0_graph_oclass) { + .base.handle = NV_ENGINE(GR, 0xc8), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = nvc0_graph_ctor, + .dtor = nvc0_graph_dtor, + .init = nvc0_graph_init, + .fini = _nouveau_graph_fini, + }, + .cclass = &nvc8_grctx_oclass, + .sclass = nvc8_graph_sclass, + .mmio = nvc8_graph_init_mmio, + .fecs.ucode = &nvc0_graph_fecs_ucode, + .gpccs.ucode = &nvc0_graph_gpccs_ucode, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvd9.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvd9.c new file mode 100644 index 000000000000..0059788af0f1 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvd9.c @@ -0,0 +1,164 @@ +/* + * Copyright 2013 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 "nvc0.h" + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +struct nvc0_graph_init +nvd9_graph_init_unk64xx[] = { + { 0x4064f0, 3, 0x04, 0x00000000 }, + {} +}; + +static struct nvc0_graph_init +nvd9_graph_init_unk58xx[] = { + { 0x405844, 1, 0x04, 0x00ffffff }, + { 0x405850, 1, 0x04, 0x00000000 }, + { 0x405900, 1, 0x04, 0x00002834 }, + { 0x405908, 1, 0x04, 0x00000000 }, + { 0x405928, 1, 0x04, 0x00000000 }, + { 0x40592c, 1, 0x04, 0x00000000 }, + {} +}; + +static struct nvc0_graph_init +nvd9_graph_init_gpc[] = { + { 0x418408, 1, 0x04, 0x00000000 }, + { 0x4184a0, 1, 0x04, 0x00000000 }, + { 0x4184a4, 2, 0x04, 0x00000000 }, + { 0x418604, 1, 0x04, 0x00000000 }, + { 0x418680, 1, 0x04, 0x00000000 }, + { 0x418714, 1, 0x04, 0x00000000 }, + { 0x418384, 1, 0x04, 0x00000000 }, + { 0x418814, 3, 0x04, 0x00000000 }, + { 0x418b04, 1, 0x04, 0x00000000 }, + { 0x4188c8, 2, 0x04, 0x00000000 }, + { 0x4188d0, 1, 0x04, 0x00010000 }, + { 0x4188d4, 1, 0x04, 0x00000001 }, + { 0x418910, 1, 0x04, 0x00010001 }, + { 0x418914, 1, 0x04, 0x00000301 }, + { 0x418918, 1, 0x04, 0x00800000 }, + { 0x418980, 1, 0x04, 0x77777770 }, + { 0x418984, 3, 0x04, 0x77777777 }, + { 0x418c04, 1, 0x04, 0x00000000 }, + { 0x418c64, 1, 0x04, 0x00000000 }, + { 0x418c68, 1, 0x04, 0x00000000 }, + { 0x418c88, 1, 0x04, 0x00000000 }, + { 0x418cb4, 2, 0x04, 0x00000000 }, + { 0x418d00, 1, 0x04, 0x00000000 }, + { 0x418d28, 1, 0x04, 0x00000000 }, + { 0x418d2c, 1, 0x04, 0x00000000 }, + { 0x418f00, 1, 0x04, 0x00000000 }, + { 0x418f08, 1, 0x04, 0x00000000 }, + { 0x418f20, 2, 0x04, 0x00000000 }, + { 0x418e00, 1, 0x04, 0x00000003 }, + { 0x418e08, 1, 0x04, 0x00000000 }, + { 0x418e1c, 1, 0x04, 0x00000000 }, + { 0x418e20, 1, 0x04, 0x00000000 }, + { 0x41900c, 1, 0x04, 0x00000000 }, + { 0x419018, 1, 0x04, 0x00000000 }, + {} +}; + +static struct nvc0_graph_init +nvd9_graph_init_tpc[] = { + { 0x419d08, 2, 0x04, 0x00000000 }, + { 0x419d10, 1, 0x04, 0x00000014 }, + { 0x419ab0, 1, 0x04, 0x00000000 }, + { 0x419ac8, 1, 0x04, 0x00000000 }, + { 0x419ab8, 1, 0x04, 0x000000e7 }, + { 0x419abc, 2, 0x04, 0x00000000 }, + { 0x419ab4, 1, 0x04, 0x00000000 }, + { 0x41980c, 1, 0x04, 0x00000010 }, + { 0x419810, 1, 0x04, 0x00000000 }, + { 0x419814, 1, 0x04, 0x00000004 }, + { 0x419844, 1, 0x04, 0x00000000 }, + { 0x41984c, 1, 0x04, 0x0000a918 }, + { 0x419850, 4, 0x04, 0x00000000 }, + { 0x419880, 1, 0x04, 0x00000002 }, + { 0x419c98, 1, 0x04, 0x00000000 }, + { 0x419ca8, 1, 0x04, 0x80000000 }, + { 0x419cb4, 1, 0x04, 0x00000000 }, + { 0x419cb8, 1, 0x04, 0x00008bf4 }, + { 0x419cbc, 1, 0x04, 0x28137606 }, + { 0x419cc0, 2, 0x04, 0x00000000 }, + { 0x419bd4, 1, 0x04, 0x00800000 }, + { 0x419bdc, 1, 0x04, 0x00000000 }, + { 0x419bf8, 1, 0x04, 0x00000000 }, + { 0x419bfc, 1, 0x04, 0x00000000 }, + { 0x419d2c, 1, 0x04, 0x00000000 }, + { 0x419d48, 1, 0x04, 0x00000000 }, + { 0x419d4c, 1, 0x04, 0x00000000 }, + { 0x419c0c, 1, 0x04, 0x00000000 }, + { 0x419e00, 1, 0x04, 0x00000000 }, + { 0x419ea0, 1, 0x04, 0x00000000 }, + { 0x419ea4, 1, 0x04, 0x00000100 }, + { 0x419ea8, 1, 0x04, 0x02001100 }, + { 0x419eac, 1, 0x04, 0x11100702 }, + { 0x419eb0, 1, 0x04, 0x00000003 }, + { 0x419eb4, 4, 0x04, 0x00000000 }, + { 0x419ec8, 1, 0x04, 0x0e063818 }, + { 0x419ecc, 1, 0x04, 0x0e060e06 }, + { 0x419ed0, 1, 0x04, 0x00003818 }, + { 0x419ed4, 1, 0x04, 0x011104f1 }, + { 0x419edc, 1, 0x04, 0x00000000 }, + { 0x419f00, 1, 0x04, 0x00000000 }, + { 0x419f2c, 1, 0x04, 0x00000000 }, + {} +}; + +static struct nvc0_graph_init * +nvd9_graph_init_mmio[] = { + nvc0_graph_init_regs, + nvc0_graph_init_unk40xx, + nvc0_graph_init_unk44xx, + nvc0_graph_init_unk78xx, + nvc0_graph_init_unk60xx, + nvd9_graph_init_unk64xx, + nvd9_graph_init_unk58xx, + nvc0_graph_init_unk80xx, + nvd9_graph_init_gpc, + nvd9_graph_init_tpc, + nvc0_graph_init_unk88xx, + NULL +}; + +struct nouveau_oclass * +nvd9_graph_oclass = &(struct nvc0_graph_oclass) { + .base.handle = NV_ENGINE(GR, 0xd9), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = nvc0_graph_ctor, + .dtor = nvc0_graph_dtor, + .init = nvc0_graph_init, + .fini = _nouveau_graph_fini, + }, + .cclass = &nvd9_grctx_oclass, + .sclass = nvc8_graph_sclass, + .mmio = nvd9_graph_init_mmio, + .fecs.ucode = &nvc0_graph_fecs_ucode, + .gpccs.ucode = &nvc0_graph_gpccs_ucode, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c b/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c deleted file mode 100644 index f4685bb66eb8..000000000000 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c +++ /dev/null @@ -1,1105 +0,0 @@ -/* - * Copyright 2012 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 "nvc0.h" -#include "fuc/hubnve0.fuc.h" -#include "fuc/gpcnve0.fuc.h" - -/******************************************************************************* - * Graphics object classes - ******************************************************************************/ - -static struct nouveau_oclass -nve0_graph_sclass[] = { - { 0x902d, &nouveau_object_ofuncs }, - { 0xa040, &nouveau_object_ofuncs }, - { 0xa097, &nouveau_object_ofuncs }, - { 0xa0c0, &nouveau_object_ofuncs }, - {} -}; - -/******************************************************************************* - * PGRAPH context - ******************************************************************************/ - -static struct nouveau_oclass -nve0_graph_cclass = { - .handle = NV_ENGCTX(GR, 0xe0), - .ofuncs = &(struct nouveau_ofuncs) { - .ctor = nvc0_graph_context_ctor, - .dtor = nvc0_graph_context_dtor, - .init = _nouveau_graph_context_init, - .fini = _nouveau_graph_context_fini, - .rd32 = _nouveau_graph_context_rd32, - .wr32 = _nouveau_graph_context_wr32, - }, -}; - -/******************************************************************************* - * PGRAPH engine/subdev functions - ******************************************************************************/ - -static void -nve0_graph_ctxctl_isr(struct nvc0_graph_priv *priv) -{ - u32 ustat = nv_rd32(priv, 0x409c18); - - if (ustat & 0x00000001) - nv_error(priv, "CTXCTRL ucode error\n"); - if (ustat & 0x00080000) - nv_error(priv, "CTXCTRL watchdog timeout\n"); - if (ustat & ~0x00080001) - nv_error(priv, "CTXCTRL 0x%08x\n", ustat); - - nvc0_graph_ctxctl_debug(priv); - nv_wr32(priv, 0x409c20, ustat); -} - -static const struct nouveau_enum nve0_mp_warp_error[] = { - { 0x00, "NO_ERROR" }, - { 0x01, "STACK_MISMATCH" }, - { 0x05, "MISALIGNED_PC" }, - { 0x08, "MISALIGNED_GPR" }, - { 0x09, "INVALID_OPCODE" }, - { 0x0d, "GPR_OUT_OF_BOUNDS" }, - { 0x0e, "MEM_OUT_OF_BOUNDS" }, - { 0x0f, "UNALIGNED_MEM_ACCESS" }, - { 0x11, "INVALID_PARAM" }, - {} -}; - -static const struct nouveau_bitfield nve0_mp_global_error[] = { - { 0x00000004, "MULTIPLE_WARP_ERRORS" }, - { 0x00000008, "OUT_OF_STACK_SPACE" }, - {} -}; - -static const struct nouveau_enum nve0_gpc_rop_error[] = { - { 1, "RT_PITCH_OVERRUN" }, - { 4, "RT_WIDTH_OVERRUN" }, - { 5, "RT_HEIGHT_OVERRUN" }, - { 7, "ZETA_STORAGE_TYPE_MISMATCH" }, - { 8, "RT_STORAGE_TYPE_MISMATCH" }, - { 10, "RT_LINEAR_MISMATCH" }, - {} -}; - -static const struct nouveau_enum nve0_sked_error[] = { - { 7, "CONSTANT_BUFFER_SIZE" }, - { 9, "LOCAL_MEMORY_SIZE_POS" }, - { 10, "LOCAL_MEMORY_SIZE_NEG" }, - { 11, "WARP_CSTACK_SIZE" }, - { 12, "TOTAL_TEMP_SIZE" }, - { 13, "REGISTER_COUNT" }, - { 18, "TOTAL_THREADS" }, - { 20, "PROGRAM_OFFSET" }, - { 21, "SHARED_MEMORY_SIZE" }, - { 25, "SHARED_CONFIG_TOO_SMALL" }, - { 26, "TOTAL_REGISTER_COUNT" }, - {} -}; - -static void -nve0_graph_mp_trap(struct nvc0_graph_priv *priv, int gpc, int tpc) -{ - u32 werr = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x648)); - u32 gerr = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x650)); - - nv_error(priv, "GPC%i/TPC%i/MP trap:", gpc, tpc); - nouveau_bitfield_print(nve0_mp_global_error, gerr); - if (werr) { - pr_cont(" "); - nouveau_enum_print(nve0_mp_warp_error, werr & 0xffff); - } - pr_cont("\n"); - - nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x648), 0x00000000); - nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x650), gerr); -} - -static void -nve0_graph_tpc_trap(struct nvc0_graph_priv *priv, int gpc, int tpc) -{ - u32 stat = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x508)); - - if (stat & 0x1) { - u32 trap = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x224)); - nv_error(priv, "GPC%i/TPC%i/TEX trap: %08x\n", - gpc, tpc, trap); - - nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x224), 0xc0000000); - stat &= ~0x1; - } - - if (stat & 0x2) { - nve0_graph_mp_trap(priv, gpc, tpc); - stat &= ~0x2; - } - - if (stat & 0x4) { - u32 trap = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x084)); - nv_error(priv, "GPC%i/TPC%i/POLY trap: %08x\n", - gpc, tpc, trap); - - nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x084), 0xc0000000); - stat &= ~0x4; - } - - if (stat & 0x8) { - u32 trap = nv_rd32(priv, TPC_UNIT(gpc, tpc, 0x48c)); - nv_error(priv, "GPC%i/TPC%i/L1C trap: %08x\n", - gpc, tpc, trap); - - nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x48c), 0xc0000000); - stat &= ~0x8; - } - - if (stat) { - nv_error(priv, "GPC%i/TPC%i: unknown stat %08x\n", - gpc, tpc, stat); - } -} - -static void -nve0_graph_gpc_trap(struct nvc0_graph_priv *priv) -{ - const u32 mask = nv_rd32(priv, 0x400118); - int gpc; - - for (gpc = 0; gpc < 4; ++gpc) { - u32 stat; - int tpc; - - if (!(mask & (1 << gpc))) - continue; - stat = nv_rd32(priv, GPC_UNIT(gpc, 0x2c90)); - - if (stat & 0x0001) { - u32 trap[4]; - int i; - - trap[0] = nv_rd32(priv, GPC_UNIT(gpc, 0x0420)); - trap[1] = nv_rd32(priv, GPC_UNIT(gpc, 0x0434)); - trap[2] = nv_rd32(priv, GPC_UNIT(gpc, 0x0438)); - trap[3] = nv_rd32(priv, GPC_UNIT(gpc, 0x043c)); - - nv_error(priv, "GPC%i/PROP trap:", gpc); - for (i = 0; i <= 29; ++i) { - if (!(trap[0] & (1 << i))) - continue; - pr_cont(" "); - nouveau_enum_print(nve0_gpc_rop_error, i); - } - pr_cont("\n"); - - nv_error(priv, "x = %u, y = %u, " - "format = %x, storage type = %x\n", - trap[1] & 0xffff, - trap[1] >> 16, - (trap[2] >> 8) & 0x3f, - trap[3] & 0xff); - - nv_wr32(priv, GPC_UNIT(gpc, 0x0420), 0xc0000000); - stat &= ~0x0001; - } - - if (stat & 0x0002) { - u32 trap = nv_rd32(priv, GPC_UNIT(gpc, 0x0900)); - nv_error(priv, "GPC%i/ZCULL trap: %08x\n", gpc, - trap); - nv_wr32(priv, GPC_UNIT(gpc, 0x0900), 0xc0000000); - stat &= ~0x0002; - } - - if (stat & 0x0004) { - u32 trap = nv_rd32(priv, GPC_UNIT(gpc, 0x1028)); - nv_error(priv, "GPC%i/CCACHE trap: %08x\n", gpc, - trap); - nv_wr32(priv, GPC_UNIT(gpc, 0x1028), 0xc0000000); - stat &= ~0x0004; - } - - if (stat & 0x0008) { - u32 trap = nv_rd32(priv, GPC_UNIT(gpc, 0x0824)); - nv_error(priv, "GPC%i/ESETUP trap %08x\n", gpc, - trap); - nv_wr32(priv, GPC_UNIT(gpc, 0x0824), 0xc0000000); - stat &= ~0x0008; - } - - for (tpc = 0; tpc < 8; ++tpc) { - if (stat & (1 << (16 + tpc))) - nve0_graph_tpc_trap(priv, gpc, tpc); - } - stat &= ~0xff0000; - - if (stat) { - nv_error(priv, "GPC%i: unknown stat %08x\n", - gpc, stat); - } - } -} - - -static void -nve0_graph_trap_isr(struct nvc0_graph_priv *priv, int chid, u64 inst, - struct nouveau_object *engctx) -{ - u32 trap = nv_rd32(priv, 0x400108); - int i; - int rop; - - if (trap & 0x00000001) { - u32 stat = nv_rd32(priv, 0x404000); - nv_error(priv, "DISPATCH ch %d [0x%010llx %s] 0x%08x\n", - chid, inst, nouveau_client_name(engctx), stat); - nv_wr32(priv, 0x404000, 0xc0000000); - nv_wr32(priv, 0x400108, 0x00000001); - trap &= ~0x00000001; - } - - if (trap & 0x00000010) { - u32 stat = nv_rd32(priv, 0x405840); - nv_error(priv, "SHADER ch %d [0x%010llx %s] 0x%08x\n", - chid, inst, nouveau_client_name(engctx), stat); - nv_wr32(priv, 0x405840, 0xc0000000); - nv_wr32(priv, 0x400108, 0x00000010); - trap &= ~0x00000010; - } - - if (trap & 0x00000100) { - u32 stat = nv_rd32(priv, 0x407020); - nv_error(priv, "SKED ch %d [0x%010llx %s]:", - chid, inst, nouveau_client_name(engctx)); - - for (i = 0; i <= 29; ++i) { - if (!(stat & (1 << i))) - continue; - pr_cont(" "); - nouveau_enum_print(nve0_sked_error, i); - } - pr_cont("\n"); - - if (stat & 0x3fffffff) - nv_wr32(priv, 0x407020, 0x40000000); - nv_wr32(priv, 0x400108, 0x00000100); - trap &= ~0x00000100; - } - - if (trap & 0x01000000) { - nv_error(priv, "GPC ch %d [0x%010llx %s]:\n", - chid, inst, nouveau_client_name(engctx)); - nve0_graph_gpc_trap(priv); - trap &= ~0x01000000; - } - - if (trap & 0x02000000) { - for (rop = 0; rop < priv->rop_nr; rop++) { - u32 statz = nv_rd32(priv, ROP_UNIT(rop, 0x070)); - u32 statc = nv_rd32(priv, ROP_UNIT(rop, 0x144)); - nv_error(priv, - "ROP%d ch %d [0x%010llx %s] 0x%08x 0x%08x\n", - rop, chid, inst, nouveau_client_name(engctx), - statz, statc); - nv_wr32(priv, ROP_UNIT(rop, 0x070), 0xc0000000); - nv_wr32(priv, ROP_UNIT(rop, 0x144), 0xc0000000); - } - nv_wr32(priv, 0x400108, 0x02000000); - trap &= ~0x02000000; - } - - if (trap) { - nv_error(priv, "TRAP ch %d [0x%010llx %s] 0x%08x\n", - chid, inst, nouveau_client_name(engctx), trap); - nv_wr32(priv, 0x400108, trap); - } -} - -static void -nve0_graph_intr(struct nouveau_subdev *subdev) -{ - struct nouveau_fifo *pfifo = nouveau_fifo(subdev); - struct nouveau_engine *engine = nv_engine(subdev); - struct nouveau_object *engctx; - struct nouveau_handle *handle; - struct nvc0_graph_priv *priv = (void *)subdev; - u64 inst = nv_rd32(priv, 0x409b00) & 0x0fffffff; - u32 stat = nv_rd32(priv, 0x400100); - u32 addr = nv_rd32(priv, 0x400704); - u32 mthd = (addr & 0x00003ffc); - u32 subc = (addr & 0x00070000) >> 16; - u32 data = nv_rd32(priv, 0x400708); - u32 code = nv_rd32(priv, 0x400110); - u32 class = nv_rd32(priv, 0x404200 + (subc * 4)); - int chid; - - engctx = nouveau_engctx_get(engine, inst); - chid = pfifo->chid(pfifo, engctx); - - if (stat & 0x00000010) { - handle = nouveau_handle_get_class(engctx, class); - if (!handle || nv_call(handle->object, mthd, data)) { - nv_error(priv, - "ILLEGAL_MTHD ch %d [0x%010llx %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n", - chid, inst, nouveau_client_name(engctx), subc, - class, mthd, data); - } - nouveau_handle_put(handle); - nv_wr32(priv, 0x400100, 0x00000010); - stat &= ~0x00000010; - } - - if (stat & 0x00000020) { - nv_error(priv, - "ILLEGAL_CLASS ch %d [0x%010llx %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n", - chid, inst, nouveau_client_name(engctx), subc, class, - mthd, data); - nv_wr32(priv, 0x400100, 0x00000020); - stat &= ~0x00000020; - } - - if (stat & 0x00100000) { - nv_error(priv, "DATA_ERROR ["); - nouveau_enum_print(nv50_data_error_names, code); - pr_cont("] ch %d [0x%010llx %s] subc %d class 0x%04x mthd 0x%04x data 0x%08x\n", - chid, inst, nouveau_client_name(engctx), subc, class, - mthd, data); - nv_wr32(priv, 0x400100, 0x00100000); - stat &= ~0x00100000; - } - - if (stat & 0x00200000) { - nve0_graph_trap_isr(priv, chid, inst, engctx); - nv_wr32(priv, 0x400100, 0x00200000); - stat &= ~0x00200000; - } - - if (stat & 0x00080000) { - nve0_graph_ctxctl_isr(priv); - nv_wr32(priv, 0x400100, 0x00080000); - stat &= ~0x00080000; - } - - if (stat) { - nv_error(priv, "unknown stat 0x%08x\n", stat); - nv_wr32(priv, 0x400100, stat); - } - - nv_wr32(priv, 0x400500, 0x00010001); - nouveau_engctx_put(engctx); -} - -static int -nve0_graph_ctor(struct nouveau_object *parent, struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) -{ - struct nouveau_device *device = nv_device(parent); - struct nvc0_graph_priv *priv; - int ret, i; - - ret = nouveau_graph_create(parent, engine, oclass, true, &priv); - *pobject = nv_object(priv); - if (ret) - return ret; - - nv_subdev(priv)->unit = 0x18001000; - nv_subdev(priv)->intr = nve0_graph_intr; - nv_engine(priv)->cclass = &nve0_graph_cclass; - nv_engine(priv)->sclass = nve0_graph_sclass; - - priv->base.units = nvc0_graph_units; - - if (nouveau_boolopt(device->cfgopt, "NvGrUseFW", false)) { - nv_info(priv, "using external firmware\n"); - if (nvc0_graph_ctor_fw(priv, "fuc409c", &priv->fuc409c) || - nvc0_graph_ctor_fw(priv, "fuc409d", &priv->fuc409d) || - nvc0_graph_ctor_fw(priv, "fuc41ac", &priv->fuc41ac) || - nvc0_graph_ctor_fw(priv, "fuc41ad", &priv->fuc41ad)) - return -EINVAL; - priv->firmware = true; - } - - ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x1000, 256, 0, - &priv->unk4188b4); - if (ret) - return ret; - - ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x1000, 256, 0, - &priv->unk4188b8); - if (ret) - return ret; - - for (i = 0; i < 0x1000; i += 4) { - nv_wo32(priv->unk4188b4, i, 0x00000010); - nv_wo32(priv->unk4188b8, i, 0x00000010); - } - - priv->gpc_nr = nv_rd32(priv, 0x409604) & 0x0000001f; - priv->rop_nr = (nv_rd32(priv, 0x409604) & 0x001f0000) >> 16; - for (i = 0; i < priv->gpc_nr; i++) { - priv->tpc_nr[i] = nv_rd32(priv, GPC_UNIT(i, 0x2608)); - priv->tpc_total += priv->tpc_nr[i]; - } - - switch (nv_device(priv)->chipset) { - case 0xe4: - if (priv->tpc_total == 8) - priv->magic_not_rop_nr = 3; - else - if (priv->tpc_total == 7) - priv->magic_not_rop_nr = 1; - break; - case 0xe7: - case 0xe6: - priv->magic_not_rop_nr = 1; - break; - case 0xf0: - default: - break; - } - - return 0; -} - -static void -nve0_graph_init_obj418880(struct nvc0_graph_priv *priv) -{ - int i; - - nv_wr32(priv, GPC_BCAST(0x0880), 0x00000000); - nv_wr32(priv, GPC_BCAST(0x08a4), 0x00000000); - for (i = 0; i < 4; i++) - nv_wr32(priv, GPC_BCAST(0x0888) + (i * 4), 0x00000000); - nv_wr32(priv, GPC_BCAST(0x08b4), priv->unk4188b4->addr >> 8); - nv_wr32(priv, GPC_BCAST(0x08b8), priv->unk4188b8->addr >> 8); -} - -static void -nve0_graph_init_regs(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x400080, 0x003083c2); - nv_wr32(priv, 0x400088, 0x0001ffe7); - nv_wr32(priv, 0x40008c, 0x00000000); - nv_wr32(priv, 0x400090, 0x00000030); - nv_wr32(priv, 0x40013c, 0x003901f7); - nv_wr32(priv, 0x400140, 0x00000100); - nv_wr32(priv, 0x400144, 0x00000000); - nv_wr32(priv, 0x400148, 0x00000110); - nv_wr32(priv, 0x400138, 0x00000000); - nv_wr32(priv, 0x400130, 0x00000000); - nv_wr32(priv, 0x400134, 0x00000000); - nv_wr32(priv, 0x400124, 0x00000002); -} - -static void -nve0_graph_init_unk40xx(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x40415c, 0x00000000); - nv_wr32(priv, 0x404170, 0x00000000); - switch (nv_device(priv)->chipset) { - case 0xf0: - nv_wr32(priv, 0x4041b4, 0x00000000); - break; - default: - break; - } -} - -static void -nve0_graph_init_unk44xx(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x404488, 0x00000000); - nv_wr32(priv, 0x40448c, 0x00000000); -} - -static void -nve0_graph_init_unk78xx(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x407808, 0x00000000); -} - -static void -nve0_graph_init_unk60xx(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x406024, 0x00000000); -} - -static void -nve0_graph_init_unk64xx(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x4064f0, 0x00000000); - nv_wr32(priv, 0x4064f4, 0x00000000); - nv_wr32(priv, 0x4064f8, 0x00000000); -} - -static void -nve0_graph_init_unk58xx(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x405844, 0x00ffffff); - nv_wr32(priv, 0x405850, 0x00000000); - switch (nv_device(priv)->chipset) { - case 0xf0: - nv_wr32(priv, 0x405900, 0x0000ff00); - break; - default: - nv_wr32(priv, 0x405900, 0x0000ff34); - break; - } - nv_wr32(priv, 0x405908, 0x00000000); - nv_wr32(priv, 0x405928, 0x00000000); - nv_wr32(priv, 0x40592c, 0x00000000); -} - -static void -nve0_graph_init_unk80xx(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x40803c, 0x00000000); -} - -static void -nve0_graph_init_unk70xx(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x407010, 0x00000000); - switch (nv_device(priv)->chipset) { - case 0xf0: - nv_wr32(priv, 0x407040, 0x80440424); - nv_wr32(priv, 0x407048, 0x0000000a); - break; - default: - break; - } -} - -static void -nve0_graph_init_unk5bxx(struct nvc0_graph_priv *priv) -{ - switch (nv_device(priv)->chipset) { - case 0xf0: - nv_wr32(priv, 0x505b44, 0x00000000); - break; - default: - break; - } - nv_wr32(priv, 0x405b50, 0x00000000); -} - -static void -nve0_graph_init_gpc(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x418408, 0x00000000); - nv_wr32(priv, 0x4184a0, 0x00000000); - nv_wr32(priv, 0x4184a4, 0x00000000); - nv_wr32(priv, 0x4184a8, 0x00000000); - nv_wr32(priv, 0x418604, 0x00000000); - nv_wr32(priv, 0x418680, 0x00000000); - nv_wr32(priv, 0x418714, 0x00000000); - nv_wr32(priv, 0x418384, 0x00000000); - nv_wr32(priv, 0x418814, 0x00000000); - nv_wr32(priv, 0x418818, 0x00000000); - nv_wr32(priv, 0x41881c, 0x00000000); - nv_wr32(priv, 0x418b04, 0x00000000); - nv_wr32(priv, 0x4188c8, 0x00000000); - nv_wr32(priv, 0x4188cc, 0x00000000); - nv_wr32(priv, 0x4188d0, 0x00010000); - nv_wr32(priv, 0x4188d4, 0x00000001); - nv_wr32(priv, 0x418910, 0x00010001); - nv_wr32(priv, 0x418914, 0x00000301); - nv_wr32(priv, 0x418918, 0x00800000); - nv_wr32(priv, 0x418980, 0x77777770); - nv_wr32(priv, 0x418984, 0x77777777); - nv_wr32(priv, 0x418988, 0x77777777); - nv_wr32(priv, 0x41898c, 0x77777777); - nv_wr32(priv, 0x418c04, 0x00000000); - nv_wr32(priv, 0x418c64, 0x00000000); - nv_wr32(priv, 0x418c68, 0x00000000); - nv_wr32(priv, 0x418c88, 0x00000000); - nv_wr32(priv, 0x418cb4, 0x00000000); - nv_wr32(priv, 0x418cb8, 0x00000000); - nv_wr32(priv, 0x418d00, 0x00000000); - nv_wr32(priv, 0x418d28, 0x00000000); - nv_wr32(priv, 0x418d2c, 0x00000000); - switch (nv_device(priv)->chipset) { - case 0xf0: - nv_wr32(priv, 0x418f00, 0x00000400); - break; - default: - nv_wr32(priv, 0x418f00, 0x00000000); - break; - } - nv_wr32(priv, 0x418f08, 0x00000000); - nv_wr32(priv, 0x418f20, 0x00000000); - nv_wr32(priv, 0x418f24, 0x00000000); - switch (nv_device(priv)->chipset) { - case 0xf0: - nv_wr32(priv, 0x418e00, 0x00000000); - break; - default: - nv_wr32(priv, 0x418e00, 0x00000060); - break; - } - nv_wr32(priv, 0x418e08, 0x00000000); - nv_wr32(priv, 0x418e1c, 0x00000000); - nv_wr32(priv, 0x418e20, 0x00000000); - nv_wr32(priv, 0x41900c, 0x00000000); - nv_wr32(priv, 0x419018, 0x00000000); -} - -static void -nve0_graph_init_tpc(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x419d0c, 0x00000000); - nv_wr32(priv, 0x419d10, 0x00000014); - nv_wr32(priv, 0x419ab0, 0x00000000); - nv_wr32(priv, 0x419ac8, 0x00000000); - nv_wr32(priv, 0x419ab8, 0x000000e7); - switch (nv_device(priv)->chipset) { - case 0xf0: - nv_wr32(priv, 0x419aec, 0x00000000); - break; - default: - break; - } - nv_wr32(priv, 0x419abc, 0x00000000); - nv_wr32(priv, 0x419ac0, 0x00000000); - nv_wr32(priv, 0x419ab4, 0x00000000); - switch (nv_device(priv)->chipset) { - case 0xf0: - nv_wr32(priv, 0x419aa8, 0x00000000); - nv_wr32(priv, 0x419aac, 0x00000000); - break; - default: - break; - } - nv_wr32(priv, 0x41980c, 0x00000010); - nv_wr32(priv, 0x419844, 0x00000000); - nv_wr32(priv, 0x419850, 0x00000004); - nv_wr32(priv, 0x419854, 0x00000000); - nv_wr32(priv, 0x419858, 0x00000000); - nv_wr32(priv, 0x419c98, 0x00000000); - nv_wr32(priv, 0x419ca8, 0x00000000); - nv_wr32(priv, 0x419cb0, 0x01000000); - nv_wr32(priv, 0x419cb4, 0x00000000); - nv_wr32(priv, 0x419cb8, 0x00b08bea); - nv_wr32(priv, 0x419c84, 0x00010384); - switch (nv_device(priv)->chipset) { - case 0xf0: - nv_wr32(priv, 0x419cbc, 0x281b3646); - break; - default: - nv_wr32(priv, 0x419cbc, 0x28137646); - break; - } - nv_wr32(priv, 0x419cc0, 0x00000000); - nv_wr32(priv, 0x419cc4, 0x00000000); - switch (nv_device(priv)->chipset) { - case 0xf0: - nv_wr32(priv, 0x419c80, 0x00020230); - nv_wr32(priv, 0x419ccc, 0x00000000); - nv_wr32(priv, 0x419cd0, 0x00000000); - nv_wr32(priv, 0x419c0c, 0x00000000); - nv_wr32(priv, 0x419e00, 0x00000080); - break; - default: - nv_wr32(priv, 0x419c80, 0x00020232); - nv_wr32(priv, 0x419c0c, 0x00000000); - nv_wr32(priv, 0x419e00, 0x00000000); - break; - } - nv_wr32(priv, 0x419ea0, 0x00000000); - nv_wr32(priv, 0x419ee4, 0x00000000); - nv_wr32(priv, 0x419ea4, 0x00000100); - nv_wr32(priv, 0x419ea8, 0x00000000); - nv_wr32(priv, 0x419eb4, 0x00000000); - switch (nv_device(priv)->chipset) { - case 0xf0: - break; - default: - nv_wr32(priv, 0x419eb8, 0x00000000); - break; - } - nv_wr32(priv, 0x419ebc, 0x00000000); - nv_wr32(priv, 0x419ec0, 0x00000000); - nv_wr32(priv, 0x419edc, 0x00000000); - nv_wr32(priv, 0x419f00, 0x00000000); - switch (nv_device(priv)->chipset) { - case 0xf0: - nv_wr32(priv, 0x419ed0, 0x00003234); - nv_wr32(priv, 0x419f74, 0x00015555); - nv_wr32(priv, 0x419f80, 0x00000000); - nv_wr32(priv, 0x419f84, 0x00000000); - nv_wr32(priv, 0x419f88, 0x00000000); - nv_wr32(priv, 0x419f8c, 0x00000000); - break; - default: - nv_wr32(priv, 0x419f74, 0x00000555); - break; - } -} - -static void -nve0_graph_init_tpcunk(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x41be04, 0x00000000); - nv_wr32(priv, 0x41be08, 0x00000004); - nv_wr32(priv, 0x41be0c, 0x00000000); - nv_wr32(priv, 0x41be10, 0x003b8bc7); - nv_wr32(priv, 0x41be14, 0x00000000); - nv_wr32(priv, 0x41be18, 0x00000000); - nv_wr32(priv, 0x41bfd4, 0x00800000); - nv_wr32(priv, 0x41bfdc, 0x00000000); - nv_wr32(priv, 0x41bff8, 0x00000000); - nv_wr32(priv, 0x41bffc, 0x00000000); - nv_wr32(priv, 0x41becc, 0x00000000); - nv_wr32(priv, 0x41bee8, 0x00000000); - nv_wr32(priv, 0x41beec, 0x00000000); -} - -static void -nve0_graph_init_unk88xx(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x40880c, 0x00000000); - nv_wr32(priv, 0x408850, 0x00000004); - nv_wr32(priv, 0x408910, 0x00000000); - nv_wr32(priv, 0x408914, 0x00000000); - nv_wr32(priv, 0x408918, 0x00000000); - nv_wr32(priv, 0x40891c, 0x00000000); - nv_wr32(priv, 0x408920, 0x00000000); - nv_wr32(priv, 0x408924, 0x00000000); - nv_wr32(priv, 0x408928, 0x00000000); - nv_wr32(priv, 0x40892c, 0x00000000); - nv_wr32(priv, 0x408930, 0x00000000); - nv_wr32(priv, 0x408950, 0x00000000); - nv_wr32(priv, 0x408954, 0x0000ffff); - nv_wr32(priv, 0x408958, 0x00000034); - nv_wr32(priv, 0x408984, 0x00000000); - nv_wr32(priv, 0x408988, 0x08040201); - nv_wr32(priv, 0x40898c, 0x80402010); -} - -static void -nve0_graph_init_units(struct nvc0_graph_priv *priv) -{ - nv_wr32(priv, 0x409ffc, 0x00000000); - nv_wr32(priv, 0x409c14, 0x00003e3e); - switch (nv_device(priv)->chipset) { - case 0xe4: - case 0xe7: - case 0xe6: - nv_wr32(priv, 0x409c24, 0x000f0001); - break; - case 0xf0: - nv_wr32(priv, 0x409c24, 0x000f0000); - break; - } - - nv_wr32(priv, 0x404000, 0xc0000000); - nv_wr32(priv, 0x404600, 0xc0000000); - nv_wr32(priv, 0x408030, 0xc0000000); - nv_wr32(priv, 0x404490, 0xc0000000); - nv_wr32(priv, 0x406018, 0xc0000000); - nv_wr32(priv, 0x407020, 0x40000000); - nv_wr32(priv, 0x405840, 0xc0000000); - nv_wr32(priv, 0x405844, 0x00ffffff); - - nv_mask(priv, 0x419cc0, 0x00000008, 0x00000008); - nv_mask(priv, 0x419eb4, 0x00001000, 0x00001000); - -} - -static void -nve0_graph_init_gpc_0(struct nvc0_graph_priv *priv) -{ - const u32 magicgpc918 = DIV_ROUND_UP(0x00800000, priv->tpc_total); - u32 data[TPC_MAX / 8]; - u8 tpcnr[GPC_MAX]; - int i, gpc, tpc; - - nv_wr32(priv, GPC_UNIT(0, 0x3018), 0x00000001); - - memset(data, 0x00, sizeof(data)); - memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr)); - for (i = 0, gpc = -1; i < priv->tpc_total; i++) { - do { - gpc = (gpc + 1) % priv->gpc_nr; - } while (!tpcnr[gpc]); - tpc = priv->tpc_nr[gpc] - tpcnr[gpc]--; - - data[i / 8] |= tpc << ((i % 8) * 4); - } - - nv_wr32(priv, GPC_BCAST(0x0980), data[0]); - nv_wr32(priv, GPC_BCAST(0x0984), data[1]); - nv_wr32(priv, GPC_BCAST(0x0988), data[2]); - nv_wr32(priv, GPC_BCAST(0x098c), data[3]); - - for (gpc = 0; gpc < priv->gpc_nr; gpc++) { - nv_wr32(priv, GPC_UNIT(gpc, 0x0914), priv->magic_not_rop_nr << 8 | - priv->tpc_nr[gpc]); - nv_wr32(priv, GPC_UNIT(gpc, 0x0910), 0x00040000 | priv->tpc_total); - nv_wr32(priv, GPC_UNIT(gpc, 0x0918), magicgpc918); - } - - nv_wr32(priv, GPC_BCAST(0x3fd4), magicgpc918); - nv_wr32(priv, GPC_BCAST(0x08ac), nv_rd32(priv, 0x100800)); -} - -static void -nve0_graph_init_gpc_1(struct nvc0_graph_priv *priv) -{ - int gpc, tpc; - - for (gpc = 0; gpc < priv->gpc_nr; gpc++) { - nv_wr32(priv, GPC_UNIT(gpc, 0x3038), 0xc0000000); - nv_wr32(priv, GPC_UNIT(gpc, 0x0420), 0xc0000000); - nv_wr32(priv, GPC_UNIT(gpc, 0x0900), 0xc0000000); - nv_wr32(priv, GPC_UNIT(gpc, 0x1028), 0xc0000000); - nv_wr32(priv, GPC_UNIT(gpc, 0x0824), 0xc0000000); - for (tpc = 0; tpc < priv->tpc_nr[gpc]; tpc++) { - nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x508), 0xffffffff); - nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x50c), 0xffffffff); - nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x224), 0xc0000000); - nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x48c), 0xc0000000); - nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x084), 0xc0000000); - nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x644), 0x001ffffe); - nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x64c), 0x0000000f); - } - nv_wr32(priv, GPC_UNIT(gpc, 0x2c90), 0xffffffff); - nv_wr32(priv, GPC_UNIT(gpc, 0x2c94), 0xffffffff); - } -} - -static void -nve0_graph_init_rop(struct nvc0_graph_priv *priv) -{ - int rop; - - for (rop = 0; rop < priv->rop_nr; rop++) { - nv_wr32(priv, ROP_UNIT(rop, 0x144), 0xc0000000); - nv_wr32(priv, ROP_UNIT(rop, 0x070), 0xc0000000); - nv_wr32(priv, ROP_UNIT(rop, 0x204), 0xffffffff); - nv_wr32(priv, ROP_UNIT(rop, 0x208), 0xffffffff); - } -} - -static int -nve0_graph_init_ctxctl(struct nvc0_graph_priv *priv) -{ - u32 r000260; - int i; - - if (priv->firmware) { - /* load fuc microcode */ - r000260 = nv_mask(priv, 0x000260, 0x00000001, 0x00000000); - nvc0_graph_init_fw(priv, 0x409000, &priv->fuc409c, &priv->fuc409d); - nvc0_graph_init_fw(priv, 0x41a000, &priv->fuc41ac, &priv->fuc41ad); - nv_wr32(priv, 0x000260, r000260); - - /* start both of them running */ - nv_wr32(priv, 0x409840, 0xffffffff); - nv_wr32(priv, 0x41a10c, 0x00000000); - nv_wr32(priv, 0x40910c, 0x00000000); - nv_wr32(priv, 0x41a100, 0x00000002); - nv_wr32(priv, 0x409100, 0x00000002); - if (!nv_wait(priv, 0x409800, 0x00000001, 0x00000001)) - nv_error(priv, "0x409800 wait failed\n"); - - nv_wr32(priv, 0x409840, 0xffffffff); - nv_wr32(priv, 0x409500, 0x7fffffff); - nv_wr32(priv, 0x409504, 0x00000021); - - nv_wr32(priv, 0x409840, 0xffffffff); - nv_wr32(priv, 0x409500, 0x00000000); - nv_wr32(priv, 0x409504, 0x00000010); - if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) { - nv_error(priv, "fuc09 req 0x10 timeout\n"); - return -EBUSY; - } - priv->size = nv_rd32(priv, 0x409800); - - nv_wr32(priv, 0x409840, 0xffffffff); - nv_wr32(priv, 0x409500, 0x00000000); - nv_wr32(priv, 0x409504, 0x00000016); - if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) { - nv_error(priv, "fuc09 req 0x16 timeout\n"); - return -EBUSY; - } - - nv_wr32(priv, 0x409840, 0xffffffff); - nv_wr32(priv, 0x409500, 0x00000000); - nv_wr32(priv, 0x409504, 0x00000025); - if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) { - nv_error(priv, "fuc09 req 0x25 timeout\n"); - return -EBUSY; - } - - nv_wr32(priv, 0x409800, 0x00000000); - nv_wr32(priv, 0x409500, 0x00000001); - nv_wr32(priv, 0x409504, 0x00000030); - if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) { - nv_error(priv, "fuc09 req 0x30 timeout\n"); - return -EBUSY; - } - - nv_wr32(priv, 0x409810, 0xb00095c8); - nv_wr32(priv, 0x409800, 0x00000000); - nv_wr32(priv, 0x409500, 0x00000001); - nv_wr32(priv, 0x409504, 0x00000031); - if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) { - nv_error(priv, "fuc09 req 0x31 timeout\n"); - return -EBUSY; - } - - nv_wr32(priv, 0x409810, 0x00080420); - nv_wr32(priv, 0x409800, 0x00000000); - nv_wr32(priv, 0x409500, 0x00000001); - nv_wr32(priv, 0x409504, 0x00000032); - if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) { - nv_error(priv, "fuc09 req 0x32 timeout\n"); - return -EBUSY; - } - - nv_wr32(priv, 0x409614, 0x00000070); - nv_wr32(priv, 0x409614, 0x00000770); - nv_wr32(priv, 0x40802c, 0x00000001); - - if (priv->data == NULL) { - int ret = nve0_grctx_generate(priv); - if (ret) { - nv_error(priv, "failed to construct context\n"); - return ret; - } - } - - return 0; - } - - /* load HUB microcode */ - r000260 = nv_mask(priv, 0x000260, 0x00000001, 0x00000000); - nv_wr32(priv, 0x4091c0, 0x01000000); - for (i = 0; i < sizeof(nve0_grhub_data) / 4; i++) - nv_wr32(priv, 0x4091c4, nve0_grhub_data[i]); - - nv_wr32(priv, 0x409180, 0x01000000); - for (i = 0; i < sizeof(nve0_grhub_code) / 4; i++) { - if ((i & 0x3f) == 0) - nv_wr32(priv, 0x409188, i >> 6); - nv_wr32(priv, 0x409184, nve0_grhub_code[i]); - } - - /* load GPC microcode */ - nv_wr32(priv, 0x41a1c0, 0x01000000); - for (i = 0; i < sizeof(nve0_grgpc_data) / 4; i++) - nv_wr32(priv, 0x41a1c4, nve0_grgpc_data[i]); - - nv_wr32(priv, 0x41a180, 0x01000000); - for (i = 0; i < sizeof(nve0_grgpc_code) / 4; i++) { - if ((i & 0x3f) == 0) - nv_wr32(priv, 0x41a188, i >> 6); - nv_wr32(priv, 0x41a184, nve0_grgpc_code[i]); - } - nv_wr32(priv, 0x000260, r000260); - - /* start HUB ucode running, it'll init the GPCs */ - nv_wr32(priv, 0x409800, nv_device(priv)->chipset); - nv_wr32(priv, 0x40910c, 0x00000000); - nv_wr32(priv, 0x409100, 0x00000002); - if (!nv_wait(priv, 0x409800, 0x80000000, 0x80000000)) { - nv_error(priv, "HUB_INIT timed out\n"); - nvc0_graph_ctxctl_debug(priv); - return -EBUSY; - } - - priv->size = nv_rd32(priv, 0x409804); - if (priv->data == NULL) { - int ret = nve0_grctx_generate(priv); - if (ret) { - nv_error(priv, "failed to construct context\n"); - return ret; - } - } - - return 0; -} - -static int -nve0_graph_init(struct nouveau_object *object) -{ - struct nvc0_graph_priv *priv = (void *)object; - int ret; - - ret = nouveau_graph_init(&priv->base); - if (ret) - return ret; - - nve0_graph_init_obj418880(priv); - nve0_graph_init_regs(priv); - nve0_graph_init_unk40xx(priv); - nve0_graph_init_unk44xx(priv); - nve0_graph_init_unk78xx(priv); - nve0_graph_init_unk60xx(priv); - nve0_graph_init_unk64xx(priv); - nve0_graph_init_unk58xx(priv); - nve0_graph_init_unk80xx(priv); - nve0_graph_init_unk70xx(priv); - nve0_graph_init_unk5bxx(priv); - nve0_graph_init_gpc(priv); - nve0_graph_init_tpc(priv); - nve0_graph_init_tpcunk(priv); - nve0_graph_init_unk88xx(priv); - nve0_graph_init_gpc_0(priv); - - nv_wr32(priv, 0x400500, 0x00010001); - nv_wr32(priv, 0x400100, 0xffffffff); - nv_wr32(priv, 0x40013c, 0xffffffff); - - nve0_graph_init_units(priv); - nve0_graph_init_gpc_1(priv); - nve0_graph_init_rop(priv); - - nv_wr32(priv, 0x400108, 0xffffffff); - nv_wr32(priv, 0x400138, 0xffffffff); - nv_wr32(priv, 0x400118, 0xffffffff); - nv_wr32(priv, 0x400130, 0xffffffff); - nv_wr32(priv, 0x40011c, 0xffffffff); - nv_wr32(priv, 0x400134, 0xffffffff); - nv_wr32(priv, 0x400054, 0x34ce3464); - - ret = nve0_graph_init_ctxctl(priv); - if (ret) - return ret; - - return 0; -} - -struct nouveau_oclass -nve0_graph_oclass = { - .handle = NV_ENGINE(GR, 0xe0), - .ofuncs = &(struct nouveau_ofuncs) { - .ctor = nve0_graph_ctor, - .dtor = nvc0_graph_dtor, - .init = nve0_graph_init, - .fini = _nouveau_graph_fini, - }, -}; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nve4.c b/drivers/gpu/drm/nouveau/core/engine/graph/nve4.c new file mode 100644 index 000000000000..05ec09c88517 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nve4.c @@ -0,0 +1,354 @@ +/* + * Copyright 2013 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 "nvc0.h" + +/******************************************************************************* + * Graphics object classes + ******************************************************************************/ + +static struct nouveau_oclass +nve4_graph_sclass[] = { + { 0x902d, &nouveau_object_ofuncs }, + { 0xa040, &nouveau_object_ofuncs }, + { 0xa097, &nouveau_object_ofuncs }, + { 0xa0c0, &nouveau_object_ofuncs }, + {} +}; + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +struct nvc0_graph_init +nve4_graph_init_regs[] = { + { 0x400080, 1, 0x04, 0x003083c2 }, + { 0x400088, 1, 0x04, 0x0001ffe7 }, + { 0x40008c, 1, 0x04, 0x00000000 }, + { 0x400090, 1, 0x04, 0x00000030 }, + { 0x40013c, 1, 0x04, 0x003901f7 }, + { 0x400140, 1, 0x04, 0x00000100 }, + { 0x400144, 1, 0x04, 0x00000000 }, + { 0x400148, 1, 0x04, 0x00000110 }, + { 0x400138, 1, 0x04, 0x00000000 }, + { 0x400130, 2, 0x04, 0x00000000 }, + { 0x400124, 1, 0x04, 0x00000002 }, + {} +}; + +static struct nvc0_graph_init +nve4_graph_init_unk58xx[] = { + { 0x405844, 1, 0x04, 0x00ffffff }, + { 0x405850, 1, 0x04, 0x00000000 }, + { 0x405900, 1, 0x04, 0x0000ff34 }, + { 0x405908, 1, 0x04, 0x00000000 }, + { 0x405928, 1, 0x04, 0x00000000 }, + { 0x40592c, 1, 0x04, 0x00000000 }, + {} +}; + +static struct nvc0_graph_init +nve4_graph_init_unk70xx[] = { + { 0x407010, 1, 0x04, 0x00000000 }, + {} +}; + +struct nvc0_graph_init +nve4_graph_init_unk5bxx[] = { + { 0x405b50, 1, 0x04, 0x00000000 }, + {} +}; + +static struct nvc0_graph_init +nve4_graph_init_gpc[] = { + { 0x418408, 1, 0x04, 0x00000000 }, + { 0x4184a0, 1, 0x04, 0x00000000 }, + { 0x4184a4, 2, 0x04, 0x00000000 }, + { 0x418604, 1, 0x04, 0x00000000 }, + { 0x418680, 1, 0x04, 0x00000000 }, + { 0x418714, 1, 0x04, 0x00000000 }, + { 0x418384, 1, 0x04, 0x00000000 }, + { 0x418814, 3, 0x04, 0x00000000 }, + { 0x418b04, 1, 0x04, 0x00000000 }, + { 0x4188c8, 2, 0x04, 0x00000000 }, + { 0x4188d0, 1, 0x04, 0x00010000 }, + { 0x4188d4, 1, 0x04, 0x00000001 }, + { 0x418910, 1, 0x04, 0x00010001 }, + { 0x418914, 1, 0x04, 0x00000301 }, + { 0x418918, 1, 0x04, 0x00800000 }, + { 0x418980, 1, 0x04, 0x77777770 }, + { 0x418984, 3, 0x04, 0x77777777 }, + { 0x418c04, 1, 0x04, 0x00000000 }, + { 0x418c64, 1, 0x04, 0x00000000 }, + { 0x418c68, 1, 0x04, 0x00000000 }, + { 0x418c88, 1, 0x04, 0x00000000 }, + { 0x418cb4, 2, 0x04, 0x00000000 }, + { 0x418d00, 1, 0x04, 0x00000000 }, + { 0x418d28, 1, 0x04, 0x00000000 }, + { 0x418d2c, 1, 0x04, 0x00000000 }, + { 0x418f00, 1, 0x04, 0x00000000 }, + { 0x418f08, 1, 0x04, 0x00000000 }, + { 0x418f20, 2, 0x04, 0x00000000 }, + { 0x418e00, 1, 0x04, 0x00000060 }, + { 0x418e08, 1, 0x04, 0x00000000 }, + { 0x418e1c, 1, 0x04, 0x00000000 }, + { 0x418e20, 1, 0x04, 0x00000000 }, + { 0x41900c, 1, 0x04, 0x00000000 }, + { 0x419018, 1, 0x04, 0x00000000 }, + {} +}; + +static struct nvc0_graph_init +nve4_graph_init_tpc[] = { + { 0x419d0c, 1, 0x04, 0x00000000 }, + { 0x419d10, 1, 0x04, 0x00000014 }, + { 0x419ab0, 1, 0x04, 0x00000000 }, + { 0x419ac8, 1, 0x04, 0x00000000 }, + { 0x419ab8, 1, 0x04, 0x000000e7 }, + { 0x419abc, 2, 0x04, 0x00000000 }, + { 0x419ab4, 1, 0x04, 0x00000000 }, + { 0x41980c, 1, 0x04, 0x00000010 }, + { 0x419844, 1, 0x04, 0x00000000 }, + { 0x419850, 1, 0x04, 0x00000004 }, + { 0x419854, 2, 0x04, 0x00000000 }, + { 0x419c98, 1, 0x04, 0x00000000 }, + { 0x419ca8, 1, 0x04, 0x00000000 }, + { 0x419cb0, 1, 0x04, 0x01000000 }, + { 0x419cb4, 1, 0x04, 0x00000000 }, + { 0x419cb8, 1, 0x04, 0x00b08bea }, + { 0x419c84, 1, 0x04, 0x00010384 }, + { 0x419cbc, 1, 0x04, 0x28137646 }, + { 0x419cc0, 2, 0x04, 0x00000000 }, + { 0x419c80, 1, 0x04, 0x00020232 }, + { 0x419c0c, 1, 0x04, 0x00000000 }, + { 0x419e00, 1, 0x04, 0x00000000 }, + { 0x419ea0, 1, 0x04, 0x00000000 }, + { 0x419ee4, 1, 0x04, 0x00000000 }, + { 0x419ea4, 1, 0x04, 0x00000100 }, + { 0x419ea8, 1, 0x04, 0x00000000 }, + { 0x419eb4, 1, 0x04, 0x00000000 }, + { 0x419eb8, 3, 0x04, 0x00000000 }, + { 0x419edc, 1, 0x04, 0x00000000 }, + { 0x419f00, 1, 0x04, 0x00000000 }, + { 0x419f74, 1, 0x04, 0x00000555 }, + {} +}; + +struct nvc0_graph_init +nve4_graph_init_unk[] = { + { 0x41be04, 1, 0x04, 0x00000000 }, + { 0x41be08, 1, 0x04, 0x00000004 }, + { 0x41be0c, 1, 0x04, 0x00000000 }, + { 0x41be10, 1, 0x04, 0x003b8bc7 }, + { 0x41be14, 2, 0x04, 0x00000000 }, + { 0x41bfd4, 1, 0x04, 0x00800000 }, + { 0x41bfdc, 1, 0x04, 0x00000000 }, + { 0x41bff8, 1, 0x04, 0x00000000 }, + { 0x41bffc, 1, 0x04, 0x00000000 }, + { 0x41becc, 1, 0x04, 0x00000000 }, + { 0x41bee8, 1, 0x04, 0x00000000 }, + { 0x41beec, 1, 0x04, 0x00000000 }, + {} +}; + +struct nvc0_graph_init +nve4_graph_init_unk88xx[] = { + { 0x40880c, 1, 0x04, 0x00000000 }, + { 0x408850, 1, 0x04, 0x00000004 }, + { 0x408910, 9, 0x04, 0x00000000 }, + { 0x408950, 1, 0x04, 0x00000000 }, + { 0x408954, 1, 0x04, 0x0000ffff }, + { 0x408958, 1, 0x04, 0x00000034 }, + { 0x408984, 1, 0x04, 0x00000000 }, + { 0x408988, 1, 0x04, 0x08040201 }, + { 0x40898c, 1, 0x04, 0x80402010 }, + {} +}; + +int +nve4_graph_init(struct nouveau_object *object) +{ + struct nvc0_graph_oclass *oclass = (void *)object->oclass; + struct nvc0_graph_priv *priv = (void *)object; + const u32 magicgpc918 = DIV_ROUND_UP(0x00800000, priv->tpc_total); + u32 data[TPC_MAX / 8] = {}; + u8 tpcnr[GPC_MAX]; + int gpc, tpc, rop; + int ret, i; + + ret = nouveau_graph_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, GPC_BCAST(0x0880), 0x00000000); + nv_wr32(priv, GPC_BCAST(0x08a4), 0x00000000); + nv_wr32(priv, GPC_BCAST(0x0888), 0x00000000); + nv_wr32(priv, GPC_BCAST(0x088c), 0x00000000); + nv_wr32(priv, GPC_BCAST(0x0890), 0x00000000); + nv_wr32(priv, GPC_BCAST(0x0894), 0x00000000); + nv_wr32(priv, GPC_BCAST(0x08b4), priv->unk4188b4->addr >> 8); + nv_wr32(priv, GPC_BCAST(0x08b8), priv->unk4188b8->addr >> 8); + + for (i = 0; oclass->mmio[i]; i++) + nvc0_graph_mmio(priv, oclass->mmio[i]); + + nv_wr32(priv, GPC_UNIT(0, 0x3018), 0x00000001); + + memset(data, 0x00, sizeof(data)); + memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr)); + for (i = 0, gpc = -1; i < priv->tpc_total; i++) { + do { + gpc = (gpc + 1) % priv->gpc_nr; + } while (!tpcnr[gpc]); + tpc = priv->tpc_nr[gpc] - tpcnr[gpc]--; + + data[i / 8] |= tpc << ((i % 8) * 4); + } + + nv_wr32(priv, GPC_BCAST(0x0980), data[0]); + nv_wr32(priv, GPC_BCAST(0x0984), data[1]); + nv_wr32(priv, GPC_BCAST(0x0988), data[2]); + nv_wr32(priv, GPC_BCAST(0x098c), data[3]); + + for (gpc = 0; gpc < priv->gpc_nr; gpc++) { + nv_wr32(priv, GPC_UNIT(gpc, 0x0914), + priv->magic_not_rop_nr << 8 | priv->tpc_nr[gpc]); + nv_wr32(priv, GPC_UNIT(gpc, 0x0910), 0x00040000 | + priv->tpc_total); + nv_wr32(priv, GPC_UNIT(gpc, 0x0918), magicgpc918); + } + + nv_wr32(priv, GPC_BCAST(0x3fd4), magicgpc918); + nv_wr32(priv, GPC_BCAST(0x08ac), nv_rd32(priv, 0x100800)); + + nv_wr32(priv, 0x400500, 0x00010001); + + nv_wr32(priv, 0x400100, 0xffffffff); + nv_wr32(priv, 0x40013c, 0xffffffff); + + nv_wr32(priv, 0x409ffc, 0x00000000); + nv_wr32(priv, 0x409c14, 0x00003e3e); + nv_wr32(priv, 0x409c24, 0x000f0001); + nv_wr32(priv, 0x404000, 0xc0000000); + nv_wr32(priv, 0x404600, 0xc0000000); + nv_wr32(priv, 0x408030, 0xc0000000); + nv_wr32(priv, 0x404490, 0xc0000000); + nv_wr32(priv, 0x406018, 0xc0000000); + nv_wr32(priv, 0x407020, 0x40000000); + nv_wr32(priv, 0x405840, 0xc0000000); + nv_wr32(priv, 0x405844, 0x00ffffff); + nv_mask(priv, 0x419cc0, 0x00000008, 0x00000008); + nv_mask(priv, 0x419eb4, 0x00001000, 0x00001000); + + for (gpc = 0; gpc < priv->gpc_nr; gpc++) { + nv_wr32(priv, GPC_UNIT(gpc, 0x3038), 0xc0000000); + nv_wr32(priv, GPC_UNIT(gpc, 0x0420), 0xc0000000); + nv_wr32(priv, GPC_UNIT(gpc, 0x0900), 0xc0000000); + nv_wr32(priv, GPC_UNIT(gpc, 0x1028), 0xc0000000); + nv_wr32(priv, GPC_UNIT(gpc, 0x0824), 0xc0000000); + for (tpc = 0; tpc < priv->tpc_nr[gpc]; tpc++) { + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x508), 0xffffffff); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x50c), 0xffffffff); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x224), 0xc0000000); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x48c), 0xc0000000); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x084), 0xc0000000); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x644), 0x001ffffe); + nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x64c), 0x0000000f); + } + nv_wr32(priv, GPC_UNIT(gpc, 0x2c90), 0xffffffff); + nv_wr32(priv, GPC_UNIT(gpc, 0x2c94), 0xffffffff); + } + + for (rop = 0; rop < priv->rop_nr; rop++) { + nv_wr32(priv, ROP_UNIT(rop, 0x144), 0xc0000000); + nv_wr32(priv, ROP_UNIT(rop, 0x070), 0xc0000000); + nv_wr32(priv, ROP_UNIT(rop, 0x204), 0xffffffff); + nv_wr32(priv, ROP_UNIT(rop, 0x208), 0xffffffff); + } + + nv_wr32(priv, 0x400108, 0xffffffff); + nv_wr32(priv, 0x400138, 0xffffffff); + nv_wr32(priv, 0x400118, 0xffffffff); + nv_wr32(priv, 0x400130, 0xffffffff); + nv_wr32(priv, 0x40011c, 0xffffffff); + nv_wr32(priv, 0x400134, 0xffffffff); + + nv_wr32(priv, 0x400054, 0x34ce3464); + return nvc0_graph_init_ctxctl(priv); +} + +static struct nvc0_graph_init * +nve4_graph_init_mmio[] = { + nve4_graph_init_regs, + nvc0_graph_init_unk40xx, + nvc0_graph_init_unk44xx, + nvc0_graph_init_unk78xx, + nvc0_graph_init_unk60xx, + nvd9_graph_init_unk64xx, + nve4_graph_init_unk58xx, + nvc0_graph_init_unk80xx, + nve4_graph_init_unk70xx, + nve4_graph_init_unk5bxx, + nve4_graph_init_gpc, + nve4_graph_init_tpc, + nve4_graph_init_unk, + nve4_graph_init_unk88xx, + NULL +}; + +#include "fuc/hubnve0.fuc.h" + +static struct nvc0_graph_ucode +nve4_graph_fecs_ucode = { + .code.data = nve0_grhub_code, + .code.size = sizeof(nve0_grhub_code), + .data.data = nve0_grhub_data, + .data.size = sizeof(nve0_grhub_data), +}; + +#include "fuc/gpcnve0.fuc.h" + +static struct nvc0_graph_ucode +nve4_graph_gpccs_ucode = { + .code.data = nve0_grgpc_code, + .code.size = sizeof(nve0_grgpc_code), + .data.data = nve0_grgpc_data, + .data.size = sizeof(nve0_grgpc_data), +}; + +struct nouveau_oclass * +nve4_graph_oclass = &(struct nvc0_graph_oclass) { + .base.handle = NV_ENGINE(GR, 0xe4), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = nvc0_graph_ctor, + .dtor = nvc0_graph_dtor, + .init = nve4_graph_init, + .fini = _nouveau_graph_fini, + }, + .cclass = &nve4_grctx_oclass, + .sclass = nve4_graph_sclass, + .mmio = nve4_graph_init_mmio, + .fecs.ucode = &nve4_graph_fecs_ucode, + .gpccs.ucode = &nve4_graph_gpccs_ucode, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvf0.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvf0.c new file mode 100644 index 000000000000..1d6ebd05216e --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvf0.c @@ -0,0 +1,176 @@ +/* + * Copyright 2013 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 "nvc0.h" + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +static struct nvc0_graph_init +nvf0_graph_init_unk40xx[] = { + { 0x40415c, 1, 0x04, 0x00000000 }, + { 0x404170, 1, 0x04, 0x00000000 }, + { 0x4041b4, 1, 0x04, 0x00000000 }, + {} +}; + +static struct nvc0_graph_init +nvf0_graph_init_unk58xx[] = { + { 0x405844, 1, 0x04, 0x00ffffff }, + { 0x405850, 1, 0x04, 0x00000000 }, + { 0x405900, 1, 0x04, 0x0000ff00 }, + { 0x405908, 1, 0x04, 0x00000000 }, + { 0x405928, 1, 0x04, 0x00000000 }, + { 0x40592c, 1, 0x04, 0x00000000 }, + {} +}; + +static struct nvc0_graph_init +nvf0_graph_init_unk70xx[] = { + { 0x407010, 1, 0x04, 0x00000000 }, + { 0x407040, 1, 0x04, 0x80440424 }, + { 0x407048, 1, 0x04, 0x0000000a }, + {} +}; + +static struct nvc0_graph_init +nvf0_graph_init_unk5bxx[] = { + { 0x405b44, 1, 0x04, 0x00000000 }, + { 0x405b50, 1, 0x04, 0x00000000 }, + {} +}; + +static struct nvc0_graph_init +nvf0_graph_init_gpc[] = { + { 0x418408, 1, 0x04, 0x00000000 }, + { 0x4184a0, 1, 0x04, 0x00000000 }, + { 0x4184a4, 2, 0x04, 0x00000000 }, + { 0x418604, 1, 0x04, 0x00000000 }, + { 0x418680, 1, 0x04, 0x00000000 }, + { 0x418714, 1, 0x04, 0x00000000 }, + { 0x418384, 1, 0x04, 0x00000000 }, + { 0x418814, 3, 0x04, 0x00000000 }, + { 0x418b04, 1, 0x04, 0x00000000 }, + { 0x4188c8, 2, 0x04, 0x00000000 }, + { 0x4188d0, 1, 0x04, 0x00010000 }, + { 0x4188d4, 1, 0x04, 0x00000001 }, + { 0x418910, 1, 0x04, 0x00010001 }, + { 0x418914, 1, 0x04, 0x00000301 }, + { 0x418918, 1, 0x04, 0x00800000 }, + { 0x418980, 1, 0x04, 0x77777770 }, + { 0x418984, 3, 0x04, 0x77777777 }, + { 0x418c04, 1, 0x04, 0x00000000 }, + { 0x418c64, 1, 0x04, 0x00000000 }, + { 0x418c68, 1, 0x04, 0x00000000 }, + { 0x418c88, 1, 0x04, 0x00000000 }, + { 0x418cb4, 2, 0x04, 0x00000000 }, + { 0x418d00, 1, 0x04, 0x00000000 }, + { 0x418d28, 1, 0x04, 0x00000000 }, + { 0x418d2c, 1, 0x04, 0x00000000 }, + { 0x418f00, 1, 0x04, 0x00000400 }, + { 0x418f08, 1, 0x04, 0x00000000 }, + { 0x418f20, 1, 0x04, 0x00000000 }, + { 0x418f24, 1, 0x04, 0x00000000 }, + { 0x418e00, 1, 0x04, 0x00000000 }, + { 0x418e08, 1, 0x04, 0x00000000 }, + { 0x418e1c, 2, 0x04, 0x00000000 }, + { 0x41900c, 1, 0x04, 0x00000000 }, + { 0x419018, 1, 0x04, 0x00000000 }, + {} +}; + +static struct nvc0_graph_init +nvf0_graph_init_tpc[] = { + { 0x419d0c, 1, 0x04, 0x00000000 }, + { 0x419d10, 1, 0x04, 0x00000014 }, + { 0x419ab0, 1, 0x04, 0x00000000 }, + { 0x419ac8, 1, 0x04, 0x00000000 }, + { 0x419ab8, 1, 0x04, 0x000000e7 }, + { 0x419aec, 1, 0x04, 0x00000000 }, + { 0x419abc, 2, 0x04, 0x00000000 }, + { 0x419ab4, 1, 0x04, 0x00000000 }, + { 0x419aa8, 2, 0x04, 0x00000000 }, + { 0x41980c, 1, 0x04, 0x00000010 }, + { 0x419844, 1, 0x04, 0x00000000 }, + { 0x419850, 1, 0x04, 0x00000004 }, + { 0x419854, 2, 0x04, 0x00000000 }, + { 0x419c98, 1, 0x04, 0x00000000 }, + { 0x419ca8, 1, 0x04, 0x00000000 }, + { 0x419cb0, 1, 0x04, 0x01000000 }, + { 0x419cb4, 1, 0x04, 0x00000000 }, + { 0x419cb8, 1, 0x04, 0x00b08bea }, + { 0x419c84, 1, 0x04, 0x00010384 }, + { 0x419cbc, 1, 0x04, 0x281b3646 }, + { 0x419cc0, 2, 0x04, 0x00000000 }, + { 0x419c80, 1, 0x04, 0x00020230 }, + { 0x419ccc, 2, 0x04, 0x00000000 }, + { 0x419c0c, 1, 0x04, 0x00000000 }, + { 0x419e00, 1, 0x04, 0x00000080 }, + { 0x419ea0, 1, 0x04, 0x00000000 }, + { 0x419ee4, 1, 0x04, 0x00000000 }, + { 0x419ea4, 1, 0x04, 0x00000100 }, + { 0x419ea8, 1, 0x04, 0x00000000 }, + { 0x419eb4, 1, 0x04, 0x00000000 }, + { 0x419ebc, 2, 0x04, 0x00000000 }, + { 0x419edc, 1, 0x04, 0x00000000 }, + { 0x419f00, 1, 0x04, 0x00000000 }, + { 0x419ed0, 1, 0x04, 0x00003234 }, + { 0x419f74, 1, 0x04, 0x00015555 }, + { 0x419f80, 4, 0x04, 0x00000000 }, + {} +}; + +static struct nvc0_graph_init * +nvf0_graph_init_mmio[] = { + nve4_graph_init_regs, + nvf0_graph_init_unk40xx, + nvc0_graph_init_unk44xx, + nvc0_graph_init_unk78xx, + nvc0_graph_init_unk60xx, + nvd9_graph_init_unk64xx, + nvf0_graph_init_unk58xx, + nvc0_graph_init_unk80xx, + nvf0_graph_init_unk70xx, + nvf0_graph_init_unk5bxx, + nvf0_graph_init_gpc, + nvf0_graph_init_tpc, + nve4_graph_init_unk, + nve4_graph_init_unk88xx, + NULL +}; + +struct nouveau_oclass * +nvf0_graph_oclass = &(struct nvc0_graph_oclass) { + .base.handle = NV_ENGINE(GR, 0xf0), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = nvc0_graph_ctor, + .dtor = nvc0_graph_dtor, + .init = nve4_graph_init, + .fini = _nouveau_graph_fini, + }, + .cclass = &nvf0_grctx_oclass, + .sclass = NULL, + .mmio = nvf0_graph_init_mmio, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/include/engine/graph.h b/drivers/gpu/drm/nouveau/core/include/engine/graph.h index 5d392439f2ac..4f5c6248a219 100644 --- a/drivers/gpu/drm/nouveau/core/include/engine/graph.h +++ b/drivers/gpu/drm/nouveau/core/include/engine/graph.h @@ -61,8 +61,13 @@ extern struct nouveau_oclass nv34_graph_oclass; extern struct nouveau_oclass nv35_graph_oclass; extern struct nouveau_oclass nv40_graph_oclass; extern struct nouveau_oclass nv50_graph_oclass; -extern struct nouveau_oclass nvc0_graph_oclass; -extern struct nouveau_oclass nve0_graph_oclass; +extern struct nouveau_oclass *nvc0_graph_oclass; +extern struct nouveau_oclass *nvc1_graph_oclass; +extern struct nouveau_oclass *nvc3_graph_oclass; +extern struct nouveau_oclass *nvc8_graph_oclass; +extern struct nouveau_oclass *nvd9_graph_oclass; +extern struct nouveau_oclass *nve4_graph_oclass; +extern struct nouveau_oclass *nvf0_graph_oclass; extern const struct nouveau_bitfield nv04_graph_nsource[]; extern struct nouveau_ofuncs nv04_graph_ofuncs; -- cgit v1.2.3 From c03ff9e8fa5fc0186158b99a89f613325ff352cf Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 1 Jul 2013 10:54:27 +1000 Subject: drm/nvc0-/gr: pull out a group of separately context-switched gpc regs Signed-off-by: Ben Skeggs --- .../gpu/drm/nouveau/core/engine/graph/ctxnvc0.c | 47 ++++++++++++++-------- .../gpu/drm/nouveau/core/engine/graph/ctxnvc1.c | 9 ++++- .../gpu/drm/nouveau/core/engine/graph/ctxnvd9.c | 33 ++++----------- .../gpu/drm/nouveau/core/engine/graph/ctxnve4.c | 36 +++++------------ .../gpu/drm/nouveau/core/engine/graph/ctxnvf0.c | 33 ++++----------- drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h | 6 ++- 6 files changed, 68 insertions(+), 96 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c index a09ee65b0f99..087295db9707 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c @@ -749,7 +749,7 @@ nvc0_grctx_init_rop[] = { }; struct nvc0_graph_init -nvc0_grctx_init_gpc[] = { +nvc0_grctx_init_gpc_0[] = { { 0x418380, 1, 0x04, 0x00000016 }, { 0x418400, 1, 0x04, 0x38004e00 }, { 0x418404, 1, 0x04, 0x71e0ffff }, @@ -779,6 +779,26 @@ nvc0_grctx_init_gpc[] = { { 0x418924, 1, 0x04, 0x00000000 }, { 0x418928, 1, 0x04, 0x00ffff00 }, { 0x41892c, 1, 0x04, 0x0000ff00 }, + { 0x418b00, 1, 0x04, 0x00000000 }, + { 0x418b08, 1, 0x04, 0x0a418820 }, + { 0x418b0c, 1, 0x04, 0x062080e6 }, + { 0x418b10, 1, 0x04, 0x020398a4 }, + { 0x418b14, 1, 0x04, 0x0e629062 }, + { 0x418b18, 1, 0x04, 0x0a418820 }, + { 0x418b1c, 1, 0x04, 0x000000e6 }, + { 0x418bb8, 1, 0x04, 0x00000103 }, + { 0x418c08, 1, 0x04, 0x00000001 }, + { 0x418c10, 8, 0x04, 0x00000000 }, + { 0x418c80, 1, 0x04, 0x20200004 }, + { 0x418c8c, 1, 0x04, 0x00000001 }, + { 0x419000, 1, 0x04, 0x00000780 }, + { 0x419004, 2, 0x04, 0x00000000 }, + { 0x419014, 1, 0x04, 0x00000004 }, + {} +}; + +struct nvc0_graph_init +nvc0_grctx_init_gpc_1[] = { { 0x418a00, 3, 0x04, 0x00000000 }, { 0x418a0c, 1, 0x04, 0x00010000 }, { 0x418a10, 3, 0x04, 0x00000000 }, @@ -803,21 +823,6 @@ nvc0_grctx_init_gpc[] = { { 0x418ae0, 3, 0x04, 0x00000000 }, { 0x418aec, 1, 0x04, 0x00010000 }, { 0x418af0, 3, 0x04, 0x00000000 }, - { 0x418b00, 1, 0x04, 0x00000000 }, - { 0x418b08, 1, 0x04, 0x0a418820 }, - { 0x418b0c, 1, 0x04, 0x062080e6 }, - { 0x418b10, 1, 0x04, 0x020398a4 }, - { 0x418b14, 1, 0x04, 0x0e629062 }, - { 0x418b18, 1, 0x04, 0x0a418820 }, - { 0x418b1c, 1, 0x04, 0x000000e6 }, - { 0x418bb8, 1, 0x04, 0x00000103 }, - { 0x418c08, 1, 0x04, 0x00000001 }, - { 0x418c10, 8, 0x04, 0x00000000 }, - { 0x418c80, 1, 0x04, 0x20200004 }, - { 0x418c8c, 1, 0x04, 0x00000001 }, - { 0x419000, 1, 0x04, 0x00000780 }, - { 0x419004, 2, 0x04, 0x00000000 }, - { 0x419014, 1, 0x04, 0x00000004 }, {} }; @@ -1044,7 +1049,8 @@ nvc0_grctx_generate_main(struct nvc0_graph_priv *priv, struct nvc0_grctx *info) for (i = 0; oclass->mmio[i]; i++) nvc0_graph_mmio(priv, oclass->mmio[i]); - nvc0_graph_mmio(priv, oclass->gpc); + for (i = 0; oclass->gpc[i]; i++) + nvc0_graph_mmio(priv, oclass->gpc[i]); nvc0_graph_mmio(priv, oclass->tpc); nv_wr32(priv, 0x404154, 0x00000000); @@ -1188,6 +1194,13 @@ nvc0_grctx_init_mmio[] = { NULL }; +struct nvc0_graph_init * +nvc0_grctx_init_gpc[] = { + nvc0_grctx_init_gpc_0, + nvc0_grctx_init_gpc_1, + NULL +}; + struct nvc0_graph_init nvc0_grctx_init_mthd_magic[] = { { 0x3410, 1, 0x04, 0x00000000 }, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc1.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc1.c index 75047154b74d..09e17f9eb508 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc1.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc1.c @@ -601,7 +601,7 @@ nvc1_grctx_init_rop[] = { }; static struct nvc0_graph_init -nvc1_grctx_init_gpc[] = { +nvc1_grctx_init_gpc_0[] = { { 0x418380, 1, 0x04, 0x00000016 }, { 0x418400, 1, 0x04, 0x38004e00 }, { 0x418404, 1, 0x04, 0x71e0ffff }, @@ -772,6 +772,13 @@ nvc1_grctx_init_mmio[] = { NULL }; +struct nvc0_graph_init * +nvc1_grctx_init_gpc[] = { + nvc1_grctx_init_gpc_0, + nvc0_grctx_init_gpc_1, + NULL +}; + static struct nvc0_graph_mthd nvc1_grctx_init_mthd[] = { { 0x9097, nvc1_grctx_init_9097, }, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd9.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd9.c index 4be543e411cd..e4f1a8c6f68c 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd9.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd9.c @@ -358,7 +358,7 @@ nvd9_grctx_init_rop[] = { }; static struct nvc0_graph_init -nvd9_grctx_init_gpc[] = { +nvd9_grctx_init_gpc_0[] = { { 0x418380, 1, 0x04, 0x00000016 }, { 0x418400, 1, 0x04, 0x38004e00 }, { 0x418404, 1, 0x04, 0x71e0ffff }, @@ -385,30 +385,6 @@ nvd9_grctx_init_gpc[] = { { 0x418924, 1, 0x04, 0x00000000 }, { 0x418928, 1, 0x04, 0x00ffff00 }, { 0x41892c, 1, 0x04, 0x0000ff00 }, - { 0x418a00, 3, 0x04, 0x00000000 }, - { 0x418a0c, 1, 0x04, 0x00010000 }, - { 0x418a10, 3, 0x04, 0x00000000 }, - { 0x418a20, 3, 0x04, 0x00000000 }, - { 0x418a2c, 1, 0x04, 0x00010000 }, - { 0x418a30, 3, 0x04, 0x00000000 }, - { 0x418a40, 3, 0x04, 0x00000000 }, - { 0x418a4c, 1, 0x04, 0x00010000 }, - { 0x418a50, 3, 0x04, 0x00000000 }, - { 0x418a60, 3, 0x04, 0x00000000 }, - { 0x418a6c, 1, 0x04, 0x00010000 }, - { 0x418a70, 3, 0x04, 0x00000000 }, - { 0x418a80, 3, 0x04, 0x00000000 }, - { 0x418a8c, 1, 0x04, 0x00010000 }, - { 0x418a90, 3, 0x04, 0x00000000 }, - { 0x418aa0, 3, 0x04, 0x00000000 }, - { 0x418aac, 1, 0x04, 0x00010000 }, - { 0x418ab0, 3, 0x04, 0x00000000 }, - { 0x418ac0, 3, 0x04, 0x00000000 }, - { 0x418acc, 1, 0x04, 0x00010000 }, - { 0x418ad0, 3, 0x04, 0x00000000 }, - { 0x418ae0, 3, 0x04, 0x00000000 }, - { 0x418aec, 1, 0x04, 0x00010000 }, - { 0x418af0, 3, 0x04, 0x00000000 }, { 0x418b00, 1, 0x04, 0x00000006 }, { 0x418b08, 1, 0x04, 0x0a418820 }, { 0x418b0c, 1, 0x04, 0x062080e6 }, @@ -492,6 +468,13 @@ nvd9_grctx_init_mmio[] = { nvd9_grctx_init_rop, }; +struct nvc0_graph_init * +nvd9_grctx_init_gpc[] = { + nvd9_grctx_init_gpc_0, + nvc0_grctx_init_gpc_1, + NULL +}; + struct nvc0_graph_init nvd9_grctx_init_mthd_magic[] = { { 0x3410, 1, 0x04, 0x80002006 }, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c index 4f6041490986..1c68fb11b889 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c @@ -699,7 +699,7 @@ nve4_grctx_init_rop[] = { }; static struct nvc0_graph_init -nve4_grctx_init_gpc[] = { +nve4_grctx_init_gpc_0[] = { { 0x418380, 1, 0x04, 0x00000016 }, { 0x418400, 1, 0x04, 0x38004e00 }, { 0x418404, 1, 0x04, 0x71e0ffff }, @@ -726,30 +726,6 @@ nve4_grctx_init_gpc[] = { { 0x418924, 1, 0x04, 0x00000000 }, { 0x418928, 1, 0x04, 0x00ffff00 }, { 0x41892c, 1, 0x04, 0x0000ff00 }, - { 0x418a00, 3, 0x04, 0x00000000 }, - { 0x418a0c, 1, 0x04, 0x00010000 }, - { 0x418a10, 3, 0x04, 0x00000000 }, - { 0x418a20, 3, 0x04, 0x00000000 }, - { 0x418a2c, 1, 0x04, 0x00010000 }, - { 0x418a30, 3, 0x04, 0x00000000 }, - { 0x418a40, 3, 0x04, 0x00000000 }, - { 0x418a4c, 1, 0x04, 0x00010000 }, - { 0x418a50, 3, 0x04, 0x00000000 }, - { 0x418a60, 3, 0x04, 0x00000000 }, - { 0x418a6c, 1, 0x04, 0x00010000 }, - { 0x418a70, 3, 0x04, 0x00000000 }, - { 0x418a80, 3, 0x04, 0x00000000 }, - { 0x418a8c, 1, 0x04, 0x00010000 }, - { 0x418a90, 3, 0x04, 0x00000000 }, - { 0x418aa0, 3, 0x04, 0x00000000 }, - { 0x418aac, 1, 0x04, 0x00010000 }, - { 0x418ab0, 3, 0x04, 0x00000000 }, - { 0x418ac0, 3, 0x04, 0x00000000 }, - { 0x418acc, 1, 0x04, 0x00010000 }, - { 0x418ad0, 3, 0x04, 0x00000000 }, - { 0x418ae0, 3, 0x04, 0x00000000 }, - { 0x418aec, 1, 0x04, 0x00010000 }, - { 0x418af0, 3, 0x04, 0x00000000 }, { 0x418b00, 1, 0x04, 0x00000006 }, { 0x418b08, 1, 0x04, 0x0a418820 }, { 0x418b0c, 1, 0x04, 0x062080e6 }, @@ -937,7 +913,8 @@ nve4_grctx_generate_main(struct nvc0_graph_priv *priv, struct nvc0_grctx *info) for (i = 0; oclass->mmio[i]; i++) nvc0_graph_mmio(priv, oclass->mmio[i]); - nvc0_graph_mmio(priv, oclass->gpc); + for (i = 0; oclass->gpc[i]; i++) + nvc0_graph_mmio(priv, oclass->gpc[i]); nvc0_graph_mmio(priv, oclass->tpc); nv_wr32(priv, 0x404154, 0x00000000); @@ -1004,6 +981,13 @@ nve4_grctx_init_mmio[] = { NULL }; +struct nvc0_graph_init * +nve4_grctx_init_gpc[] = { + nve4_grctx_init_gpc_0, + nvc0_grctx_init_gpc_1, + NULL +}; + static struct nvc0_graph_mthd nve4_grctx_init_mthd[] = { { 0xa097, nve4_grctx_init_a097, }, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c index 8aae1f3415f5..a692389cd9cf 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c @@ -104,7 +104,7 @@ nvf0_grctx_init_unk88xx[] = { }; static struct nvc0_graph_init -nvf0_grctx_init_gpc[] = { +nvf0_grctx_init_gpc_0[] = { { 0x418380, 1, 0x04, 0x00000016 }, { 0x418400, 1, 0x04, 0x38004e00 }, { 0x418404, 1, 0x04, 0x71e0ffff }, @@ -133,30 +133,6 @@ nvf0_grctx_init_gpc[] = { { 0x418924, 1, 0x04, 0x00000000 }, { 0x418928, 1, 0x04, 0x00ffff00 }, { 0x41892c, 1, 0x04, 0x0000ff00 }, - { 0x418a00, 3, 0x04, 0x00000000 }, - { 0x418a0c, 1, 0x04, 0x00010000 }, - { 0x418a10, 3, 0x04, 0x00000000 }, - { 0x418a20, 3, 0x04, 0x00000000 }, - { 0x418a2c, 1, 0x04, 0x00010000 }, - { 0x418a30, 3, 0x04, 0x00000000 }, - { 0x418a40, 3, 0x04, 0x00000000 }, - { 0x418a4c, 1, 0x04, 0x00010000 }, - { 0x418a50, 3, 0x04, 0x00000000 }, - { 0x418a60, 3, 0x04, 0x00000000 }, - { 0x418a6c, 1, 0x04, 0x00010000 }, - { 0x418a70, 3, 0x04, 0x00000000 }, - { 0x418a80, 3, 0x04, 0x00000000 }, - { 0x418a8c, 1, 0x04, 0x00010000 }, - { 0x418a90, 3, 0x04, 0x00000000 }, - { 0x418aa0, 3, 0x04, 0x00000000 }, - { 0x418aac, 1, 0x04, 0x00010000 }, - { 0x418ab0, 3, 0x04, 0x00000000 }, - { 0x418ac0, 3, 0x04, 0x00000000 }, - { 0x418acc, 1, 0x04, 0x00010000 }, - { 0x418ad0, 3, 0x04, 0x00000000 }, - { 0x418ae0, 3, 0x04, 0x00000000 }, - { 0x418aec, 1, 0x04, 0x00010000 }, - { 0x418af0, 3, 0x04, 0x00000000 }, { 0x418b00, 1, 0x04, 0x00000006 }, { 0x418b08, 1, 0x04, 0x0a418820 }, { 0x418b0c, 1, 0x04, 0x062080e6 }, @@ -259,6 +235,13 @@ nvf0_grctx_init_mmio[] = { NULL }; +struct nvc0_graph_init * +nvf0_grctx_init_gpc[] = { + nvf0_grctx_init_gpc_0, + nvc0_grctx_init_gpc_1, + NULL +}; + static struct nvc0_graph_mthd nvf0_grctx_init_mthd[] = { { 0xa197, nvc1_grctx_init_9097, }, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h index f7d0df7c84ad..52d70ba5ffb5 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h @@ -152,7 +152,7 @@ struct nvc0_grctx_oclass { void (*mods)(struct nvc0_graph_priv *, struct nvc0_grctx *); /* mmio context data */ struct nvc0_graph_init **mmio; - struct nvc0_graph_init *gpc; + struct nvc0_graph_init **gpc; struct nvc0_graph_init *tpc; /* indirect context data, generated with icmds/mthds */ struct nvc0_graph_init *icmd; @@ -223,7 +223,9 @@ extern struct nvc0_graph_init nvc0_grctx_init_unk60xx[]; extern struct nvc0_graph_init nvc0_grctx_init_unk64xx[]; extern struct nvc0_graph_init nvc0_grctx_init_unk78xx[]; extern struct nvc0_graph_init nvc0_grctx_init_unk80xx[]; -extern struct nvc0_graph_init nvc0_grctx_init_gpc[]; +extern struct nvc0_graph_init nvc0_grctx_init_gpc_0[]; +extern struct nvc0_graph_init nvc0_grctx_init_gpc_1[]; +extern struct nvc0_graph_init *nvc0_grctx_init_gpc[]; extern struct nvc0_graph_init nvc0_grctx_init_tpc[]; extern struct nvc0_graph_init nvc0_grctx_init_icmd[]; extern struct nvc0_graph_init nvd9_grctx_init_icmd[]; // -- cgit v1.2.3 From 5ee86c4190f9e19a9e13906389069c73d7f75bfb Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 1 Jul 2013 14:32:42 +1000 Subject: drm/nve0-/gr: some new gpc registers can have multiple copies GK110 exposes more than one, and needs to be dealt with in the ctxsw ucode just like the TPC sets are. Broadcast is at +0xe00. Signed-off-by: Ben Skeggs --- .../gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc | 35 ++- .../nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h | 40 +-- .../drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc | 16 +- .../nouveau/core/engine/graph/fuc/gpcnve0.fuc.h | 316 +++++++++++---------- 4 files changed, 227 insertions(+), 180 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc index 4770e8c99432..3d7599d5cca6 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc @@ -38,6 +38,13 @@ tpc_mask: .b32 0 tpc_mmio_list_head: .b32 0 tpc_mmio_list_tail: .b32 0 +#ifdef NVGK +unk_count: .b32 1 +unk_mask: .b32 1 +unk_mmio_list_head: .b32 #nve4_unk_mmio_head +unk_mmio_list_tail: .b32 #nve4_unk_mmio_tail +#endif + cmd_queue: queue_init #endif @@ -160,6 +167,17 @@ init: add b32 $r2 $r14 add b32 $r3 $r14 +#ifdef NVGK + // calculate per-UNK mmio context size + ld b32 $r14 D[$r0 + #unk_mmio_list_head] + ld b32 $r15 D[$r0 + #unk_mmio_list_tail] + call #mmctx_size + ld b32 $r14 D[$r0 + #unk_count] + mulu $r14 $r15 + add b32 $r2 $r14 + add b32 $r3 $r14 +#endif + // round up base/size to 256 byte boundary (for strand SWBASE) add b32 $r4 0x1300 shr b32 $r3 2 @@ -335,7 +353,6 @@ ctx_xfer: // per-TPC mmio context xbit $r10 $flags $p1 // direction - or $r10 4 // last mov $r11 0x4000 sethi $r11 0x500000 // base = NV_PGRAPH_GPC0_TPC0 ld b32 $r12 D[$r0 + #gpc_id] @@ -347,6 +364,22 @@ ctx_xfer: mov $r14 0x800 // stride = 0x800 call #mmctx_xfer +#ifdef NVGK + // per-UNK mmio context + xbit $r10 $flags $p1 // direction + or $r10 4 // last + mov $r11 0x3000 + sethi $r11 0x500000 // base = NV_PGRAPH_GPC0_UNK0 + ld b32 $r12 D[$r0 + #gpc_id] + shl b32 $r12 15 + add b32 $r11 $r12 // base = NV_PGRAPH_GPCn_UNK0 + ld b32 $r12 D[$r0 + #unk_mmio_list_head] + ld b32 $r13 D[$r0 + #unk_mmio_list_tail] + ld b32 $r15 D[$r0 + #unk_mask] + mov $r14 0x200 // stride = 0x200 + call #mmctx_xfer +#endif + // wait for strands to finish call #strand_wait diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h index 66ec1acaadee..2744ed7d86a9 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h @@ -496,26 +496,26 @@ uint32_t nvc0_grgpc_code[] = { 0xf0020d98, 0x21f500e7, 0xacf0015c, - 0x04a5f001, - 0x4000b7f1, - 0x9850b3f0, - 0xc4b6000c, - 0x00bcbb0f, - 0x98050c98, - 0x0f98060d, - 0x00e7f104, - 0x5c21f508, - 0x0721f501, - 0x0601f402, -/* 0x054b: ctx_xfer_post */ - 0xf11412f4, - 0xf04afc17, - 0x27f00213, - 0x0012d00d, - 0x020721f5, -/* 0x055c: ctx_xfer_done */ - 0x048f21f5, - 0x000000f8, + 0x00b7f101, + 0x50b3f040, + 0xb6000c98, + 0xbcbb0fc4, + 0x050c9800, + 0x98060d98, + 0xe7f1040f, + 0x21f50800, + 0x21f5015c, + 0x01f40207, + 0x1412f406, +/* 0x0548: ctx_xfer_post */ + 0x4afc17f1, + 0xf00213f0, + 0x12d00d27, + 0x0721f500, +/* 0x0559: ctx_xfer_done */ + 0x8f21f502, + 0x0000f804, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc index 2fc585eeff95..056f0d374897 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc @@ -82,12 +82,6 @@ mmctx_data(0x000c80, 1) mmctx_data(0x000c8c, 1) mmctx_data(0x001000, 3) mmctx_data(0x001014, 1) -mmctx_data(0x003024, 1) -mmctx_data(0x0030c0, 2) -mmctx_data(0x0030e4, 1) -mmctx_data(0x003100, 6) -mmctx_data(0x0031d0, 1) -mmctx_data(0x0031e0, 2) nve4_gpc_mmio_tail: nvf0_gpc_mmio_head: @@ -166,6 +160,16 @@ mmctx_data(0x000758, 1) mmctx_data(0x000770, 1) mmctx_data(0x000778, 2) nvf0_tpc_mmio_tail: + +// UNK mmio lists +nve4_unk_mmio_head: +mmctx_data(0x000024, 1) +mmctx_data(0x0000c0, 2) +mmctx_data(0x0000e4, 1) +mmctx_data(0x000100, 6) +mmctx_data(0x0001d0, 1) +mmctx_data(0x0001e0, 2) +nve4_unk_mmio_tail: #undef INCLUDE_DATA .section #nve0_grgpc_code diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h index 504ae96cd3dd..08c0f4731681 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h @@ -13,7 +13,15 @@ uint32_t nve0_grgpc_data[] = { 0x00000000, /* 0x0018: tpc_mmio_list_tail */ 0x00000000, -/* 0x001c: cmd_queue */ +/* 0x001c: unk_count */ + 0x00000001, +/* 0x0020: unk_mask */ + 0x00000001, +/* 0x0024: unk_mmio_list_head */ + 0x00000220, +/* 0x0028: unk_mmio_list_tail */ + 0x00000238, +/* 0x002c: cmd_queue */ 0x00000000, 0x00000000, 0x00000000, @@ -32,21 +40,21 @@ uint32_t nve0_grgpc_data[] = { 0x00000000, 0x00000000, 0x00000000, -/* 0x0064: chipsets */ +/* 0x0074: chipsets */ 0x000000e4, - 0x011c0098, - 0x01d8018c, + 0x011400a8, + 0x01d00184, 0x000000e7, - 0x011c0098, - 0x01d8018c, + 0x011400a8, + 0x01d00184, 0x000000e6, - 0x011c0098, - 0x01d8018c, + 0x011400a8, + 0x01d00184, 0x000000f0, - 0x018c011c, - 0x022801d8, + 0x01840114, + 0x022001d0, 0x00000000, -/* 0x0098: nve4_gpc_mmio_head */ +/* 0x00a8: nve4_gpc_mmio_head */ 0x00000380, 0x04000400, 0x0800040c, @@ -74,14 +82,8 @@ uint32_t nve0_grgpc_data[] = { 0x00000c8c, 0x08001000, 0x00001014, - 0x00003024, - 0x040030c0, - 0x000030e4, - 0x14003100, - 0x000031d0, - 0x040031e0, -/* 0x011c: nve4_gpc_mmio_tail */ -/* 0x011c: nvf0_gpc_mmio_head */ +/* 0x0114: nve4_gpc_mmio_tail */ +/* 0x0114: nvf0_gpc_mmio_head */ 0x00000380, 0x04000400, 0x0800040c, @@ -110,8 +112,8 @@ uint32_t nve0_grgpc_data[] = { 0x00000d24, 0x08001000, 0x00001014, -/* 0x018c: nvf0_gpc_mmio_tail */ -/* 0x018c: nve4_tpc_mmio_head */ +/* 0x0184: nvf0_gpc_mmio_tail */ +/* 0x0184: nve4_tpc_mmio_head */ 0x00000048, 0x00000064, 0x00000088, @@ -131,8 +133,8 @@ uint32_t nve0_grgpc_data[] = { 0x00000758, 0x00000770, 0x04000778, -/* 0x01d8: nve4_tpc_mmio_tail */ -/* 0x01d8: nvf0_tpc_mmio_head */ +/* 0x01d0: nve4_tpc_mmio_tail */ +/* 0x01d0: nvf0_tpc_mmio_head */ 0x00000048, 0x00000064, 0x00000088, @@ -153,6 +155,14 @@ uint32_t nve0_grgpc_data[] = { 0x00000758, 0x00000770, 0x04000778, +/* 0x0220: nvf0_tpc_mmio_tail */ +/* 0x0220: nve4_unk_mmio_head */ + 0x00000024, + 0x040000c0, + 0x000000e4, + 0x14000100, + 0x000001d0, + 0x040001e0, }; uint32_t nve0_grgpc_code[] = { @@ -386,7 +396,7 @@ uint32_t nve0_grgpc_code[] = { 0xf10004fe, 0xf0120017, 0x12d00227, - 0x3e17f100, + 0x5417f100, 0x0010fe04, 0x040017f1, 0xf0c010d0, @@ -405,7 +415,7 @@ uint32_t nve0_grgpc_code[] = { 0x24b60800, 0x0022cf06, /* 0x035f: init_find_chipset */ - 0xb65817f0, + 0xb66817f0, 0x13980c10, 0x0432b800, 0xb00b0bf4, @@ -432,135 +442,135 @@ uint32_t nve0_grgpc_code[] = { 0x030e9801, 0xbb00effd, 0x3ebb002e, - 0x0040b700, - 0x0235b613, - 0xb60043d0, - 0x35b60825, - 0x0120b606, - 0xb60130b6, - 0x34b60824, - 0x022fb908, - 0x026321f5, - 0xf1003fbb, - 0xb6080017, - 0x13d00614, - 0x0010b740, - 0xf024bd08, - 0x12d01f29, -/* 0x0401: main */ - 0x0031f400, - 0xf00028f4, - 0x21f41cd7, - 0xf401f439, - 0xf404e4b0, - 0x81fe1e18, - 0x0627f001, - 0x12fd20bd, - 0x01e4b604, - 0xfe051efd, - 0x21f50018, - 0x0ef404c3, -/* 0x0431: main_not_ctx_xfer */ - 0x10ef94d3, - 0xf501f5f0, - 0xf402ec21, -/* 0x043e: ih */ - 0x80f9c60e, - 0xf90188fe, - 0xf990f980, - 0xf9b0f9a0, - 0xf9e0f9d0, - 0x800acff0, - 0xf404abc4, - 0xb7f11d0b, - 0xd7f01900, - 0x40becf1c, - 0xf400bfcf, - 0xb0b70421, - 0xe7f00400, - 0x00bed001, -/* 0x0474: ih_no_fifo */ - 0xfc400ad0, - 0xfce0fcf0, - 0xfcb0fcd0, - 0xfc90fca0, - 0x0088fe80, - 0x32f480fc, -/* 0x048f: hub_barrier_done */ - 0xf001f800, - 0x0e9801f7, - 0x04febb00, - 0x9418e7f1, - 0xf440e3f0, - 0x00f88d21, -/* 0x04a4: ctx_redswitch */ - 0x0614e7f1, - 0xf006e4b6, - 0xefd020f7, - 0x08f7f000, -/* 0x04b4: ctx_redswitch_delay */ - 0xf401f2b6, - 0xf7f1fd1b, - 0xefd00a20, -/* 0x04c3: ctx_xfer */ - 0xf100f800, - 0xb60a0417, - 0x1fd00614, - 0x0711f400, - 0x04a421f5, -/* 0x04d4: ctx_xfer_not_load */ - 0x4afc17f1, - 0xf00213f0, - 0x12d00c27, - 0x0721f500, - 0xfc27f102, - 0x0223f047, - 0xf00020d0, - 0x20b6012c, - 0x0012d003, + 0x090e9800, + 0xf50a0f98, + 0x98013d21, + 0xeffd070e, + 0x002ebb00, + 0xb7003ebb, + 0xb6130040, + 0x43d00235, + 0x0825b600, + 0xb60635b6, + 0x30b60120, + 0x0824b601, + 0xb90834b6, + 0x21f5022f, + 0x3fbb0263, + 0x0017f100, + 0x0614b608, + 0xb74013d0, + 0xbd080010, + 0x1f29f024, +/* 0x0417: main */ + 0xf40012d0, + 0x28f40031, + 0x2cd7f000, + 0xf43921f4, + 0xe4b0f401, + 0x1e18f404, + 0xf00181fe, + 0x20bd0627, + 0xb60412fd, + 0x1efd01e4, + 0x0018fe05, + 0x04d921f5, +/* 0x0447: main_not_ctx_xfer */ + 0x94d30ef4, + 0xf5f010ef, + 0xec21f501, + 0xc60ef402, +/* 0x0454: ih */ + 0x88fe80f9, + 0xf980f901, + 0xf9a0f990, + 0xf9d0f9b0, + 0xcff0f9e0, + 0xabc4800a, + 0x1d0bf404, + 0x1900b7f1, + 0xcf2cd7f0, + 0xbfcf40be, + 0x0421f400, + 0x0400b0b7, + 0xd001e7f0, +/* 0x048a: ih_no_fifo */ + 0x0ad000be, + 0xfcf0fc40, + 0xfcd0fce0, + 0xfca0fcb0, + 0xfe80fc90, + 0x80fc0088, + 0xf80032f4, +/* 0x04a5: hub_barrier_done */ + 0x01f7f001, + 0xbb000e98, + 0xe7f104fe, + 0xe3f09418, + 0x8d21f440, +/* 0x04ba: ctx_redswitch */ + 0xe7f100f8, + 0xe4b60614, + 0x20f7f006, + 0xf000efd0, +/* 0x04ca: ctx_redswitch_delay */ + 0xf2b608f7, + 0xfd1bf401, + 0x0a20f7f1, + 0xf800efd0, +/* 0x04d9: ctx_xfer */ + 0x0417f100, + 0x0614b60a, + 0xf4001fd0, + 0x21f50711, +/* 0x04ea: ctx_xfer_not_load */ + 0x17f104ba, + 0x13f04afc, + 0x0c27f002, + 0xf50012d0, + 0xf1020721, + 0xf047fc27, + 0x20d00223, + 0x012cf000, + 0xd00320b6, + 0xacf00012, + 0x02a5f001, + 0xf000b7f0, + 0x0c9850b3, + 0x0fc4b600, + 0x9800bcbb, + 0x0d98010c, + 0x00e7f002, + 0x015c21f5, + 0xf101acf0, + 0xf04000b7, + 0x0c9850b3, + 0x0fc4b600, + 0x9800bcbb, + 0x0d98050c, + 0x040f9806, + 0x0800e7f1, + 0x015c21f5, 0xf001acf0, - 0xb7f002a5, - 0x50b3f000, - 0xb6000c98, - 0xbcbb0fc4, - 0x010c9800, - 0xf0020d98, - 0x21f500e7, - 0xacf0015c, - 0x04a5f001, - 0x4000b7f1, - 0x9850b3f0, - 0xc4b6000c, - 0x00bcbb0f, - 0x98050c98, - 0x0f98060d, - 0x00e7f104, - 0x5c21f508, - 0x0721f501, - 0x0601f402, -/* 0x054b: ctx_xfer_post */ - 0xf11412f4, - 0xf04afc17, - 0x27f00213, - 0x0012d00d, - 0x020721f5, -/* 0x055c: ctx_xfer_done */ - 0x048f21f5, - 0x000000f8, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, + 0xb7f104a5, + 0xb3f03000, + 0x000c9850, + 0xbb0fc4b6, + 0x0c9800bc, + 0x0a0d9809, + 0xf1080f98, + 0xf50200e7, + 0xf5015c21, + 0xf4020721, + 0x12f40601, +/* 0x0585: ctx_xfer_post */ + 0xfc17f114, + 0x0213f04a, + 0xd00d27f0, + 0x21f50012, +/* 0x0596: ctx_xfer_done */ + 0x21f50207, + 0x00f804a5, 0x00000000, 0x00000000, 0x00000000, -- cgit v1.2.3 From 70f824ac8c369194e9499c59e687c6aa8b1a10c8 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 1 Jul 2013 14:48:33 +1000 Subject: drm/nvc0-/gr: tpc regs a subset of gpc, add separate list for gpc/unk regs Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c | 13 ++++++------- drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc1.c | 6 +++--- drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc3.c | 13 ++++++++++--- drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc8.c | 13 ++++++++++--- drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd9.c | 6 +++--- drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c | 17 ++++++++++------- drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c | 12 ++++++++---- drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h | 6 ++---- 8 files changed, 52 insertions(+), 34 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c index 087295db9707..34ed87f1ff16 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c @@ -1047,11 +1047,10 @@ nvc0_grctx_generate_main(struct nvc0_graph_priv *priv, struct nvc0_grctx *info) nv_mask(priv, 0x000260, 0x00000001, 0x00000000); - for (i = 0; oclass->mmio[i]; i++) - nvc0_graph_mmio(priv, oclass->mmio[i]); + for (i = 0; oclass->hub[i]; i++) + nvc0_graph_mmio(priv, oclass->hub[i]); for (i = 0; oclass->gpc[i]; i++) nvc0_graph_mmio(priv, oclass->gpc[i]); - nvc0_graph_mmio(priv, oclass->tpc); nv_wr32(priv, 0x404154, 0x00000000); @@ -1179,7 +1178,7 @@ done: } struct nvc0_graph_init * -nvc0_grctx_init_mmio[] = { +nvc0_grctx_init_hub[] = { nvc0_grctx_init_base, nvc0_grctx_init_unk40xx, nvc0_grctx_init_unk44xx, @@ -1194,10 +1193,11 @@ nvc0_grctx_init_mmio[] = { NULL }; -struct nvc0_graph_init * +static struct nvc0_graph_init * nvc0_grctx_init_gpc[] = { nvc0_grctx_init_gpc_0, nvc0_grctx_init_gpc_1, + nvc0_grctx_init_tpc, NULL }; @@ -1230,9 +1230,8 @@ nvc0_grctx_oclass = &(struct nvc0_grctx_oclass) { }, .main = nvc0_grctx_generate_main, .mods = nvc0_grctx_generate_mods, - .mmio = nvc0_grctx_init_mmio, + .hub = nvc0_grctx_init_hub, .gpc = nvc0_grctx_init_gpc, - .tpc = nvc0_grctx_init_tpc, .icmd = nvc0_grctx_init_icmd, .mthd = nvc0_grctx_init_mthd, }.base; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc1.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc1.c index 09e17f9eb508..b60dffab224f 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc1.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc1.c @@ -757,7 +757,7 @@ nvc1_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info) } static struct nvc0_graph_init * -nvc1_grctx_init_mmio[] = { +nvc1_grctx_init_hub[] = { nvc0_grctx_init_base, nvc0_grctx_init_unk40xx, nvc0_grctx_init_unk44xx, @@ -776,6 +776,7 @@ struct nvc0_graph_init * nvc1_grctx_init_gpc[] = { nvc1_grctx_init_gpc_0, nvc0_grctx_init_gpc_1, + nvc1_grctx_init_tpc, NULL }; @@ -803,9 +804,8 @@ nvc1_grctx_oclass = &(struct nvc0_grctx_oclass) { }, .main = nvc0_grctx_generate_main, .mods = nvc1_grctx_generate_mods, - .mmio = nvc1_grctx_init_mmio, + .hub = nvc1_grctx_init_hub, .gpc = nvc1_grctx_init_gpc, - .tpc = nvc1_grctx_init_tpc, .icmd = nvc1_grctx_init_icmd, .mthd = nvc1_grctx_init_mthd, }.base; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc3.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc3.c index 88eceefcd30d..56fa54719d2e 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc3.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc3.c @@ -70,6 +70,14 @@ nvc3_grctx_init_tpc[] = { {} }; +struct nvc0_graph_init * +nvc3_grctx_init_gpc[] = { + nvc0_grctx_init_gpc_0, + nvc0_grctx_init_gpc_1, + nvc3_grctx_init_tpc, + NULL +}; + struct nouveau_oclass * nvc3_grctx_oclass = &(struct nvc0_grctx_oclass) { .base.handle = NV_ENGCTX(GR, 0xc3), @@ -83,9 +91,8 @@ nvc3_grctx_oclass = &(struct nvc0_grctx_oclass) { }, .main = nvc0_grctx_generate_main, .mods = nvc0_grctx_generate_mods, - .mmio = nvc0_grctx_init_mmio, - .gpc = nvc0_grctx_init_gpc, - .tpc = nvc3_grctx_init_tpc, + .hub = nvc0_grctx_init_hub, + .gpc = nvc3_grctx_init_gpc, .icmd = nvc0_grctx_init_icmd, .mthd = nvc0_grctx_init_mthd, }.base; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc8.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc8.c index aa766813699b..2ba8ea81a63a 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc8.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc8.c @@ -341,6 +341,14 @@ nvc8_grctx_init_mthd[] = { {} }; +static struct nvc0_graph_init * +nvc8_grctx_init_gpc[] = { + nvc0_grctx_init_gpc_0, + nvc0_grctx_init_gpc_1, + nvc8_grctx_init_tpc, + NULL +}; + struct nouveau_oclass * nvc8_grctx_oclass = &(struct nvc0_grctx_oclass) { .base.handle = NV_ENGCTX(GR, 0xc8), @@ -354,9 +362,8 @@ nvc8_grctx_oclass = &(struct nvc0_grctx_oclass) { }, .main = nvc0_grctx_generate_main, .mods = nvc0_grctx_generate_mods, - .mmio = nvc0_grctx_init_mmio, - .gpc = nvc0_grctx_init_gpc, - .tpc = nvc8_grctx_init_tpc, + .hub = nvc0_grctx_init_hub, + .gpc = nvc8_grctx_init_gpc, .icmd = nvc8_grctx_init_icmd, .mthd = nvc8_grctx_init_mthd, }.base; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd9.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd9.c index e4f1a8c6f68c..e4eb91670ef2 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd9.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd9.c @@ -454,7 +454,7 @@ nvd9_grctx_init_tpc[] = { }; static struct nvc0_graph_init * -nvd9_grctx_init_mmio[] = { +nvd9_grctx_init_hub[] = { nvc0_grctx_init_base, nvd9_grctx_init_unk40xx, nvc0_grctx_init_unk44xx, @@ -472,6 +472,7 @@ struct nvc0_graph_init * nvd9_grctx_init_gpc[] = { nvd9_grctx_init_gpc_0, nvc0_grctx_init_gpc_1, + nvd9_grctx_init_tpc, NULL }; @@ -506,9 +507,8 @@ nvd9_grctx_oclass = &(struct nvc0_grctx_oclass) { }, .main = nvc0_grctx_generate_main, .mods = nvc1_grctx_generate_mods, - .mmio = nvd9_grctx_init_mmio, + .hub = nvd9_grctx_init_hub, .gpc = nvd9_grctx_init_gpc, - .tpc = nvd9_grctx_init_tpc, .icmd = nvd9_grctx_init_icmd, .mthd = nvd9_grctx_init_mthd, }.base; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c index 1c68fb11b889..51fb2687b570 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c @@ -783,7 +783,11 @@ nve4_grctx_init_tpc[] = { { 0x419f70, 1, 0x04, 0x00000000 }, { 0x419f78, 1, 0x04, 0x0000000b }, { 0x419f7c, 1, 0x04, 0x0000027a }, + {} +}; +static struct nvc0_graph_init +nve4_grctx_init_unk[] = { { 0x41be24, 1, 0x04, 0x00000006 }, { 0x41bec0, 1, 0x04, 0x12180000 }, { 0x41bec4, 1, 0x04, 0x00037f7f }, @@ -797,7 +801,6 @@ nve4_grctx_init_tpc[] = { { 0x41bfd0, 1, 0x04, 0x00900103 }, { 0x41bfe0, 1, 0x04, 0x00400001 }, { 0x41bfe4, 1, 0x04, 0x00000000 }, - {} }; @@ -911,11 +914,10 @@ nve4_grctx_generate_main(struct nvc0_graph_priv *priv, struct nvc0_grctx *info) nv_mask(priv, 0x000260, 0x00000001, 0x00000000); - for (i = 0; oclass->mmio[i]; i++) - nvc0_graph_mmio(priv, oclass->mmio[i]); + for (i = 0; oclass->hub[i]; i++) + nvc0_graph_mmio(priv, oclass->hub[i]); for (i = 0; oclass->gpc[i]; i++) nvc0_graph_mmio(priv, oclass->gpc[i]); - nvc0_graph_mmio(priv, oclass->tpc); nv_wr32(priv, 0x404154, 0x00000000); @@ -964,7 +966,7 @@ nve4_grctx_generate_main(struct nvc0_graph_priv *priv, struct nvc0_grctx *info) } static struct nvc0_graph_init * -nve4_grctx_init_mmio[] = { +nve4_grctx_init_hub[] = { nvc0_grctx_init_base, nve4_grctx_init_unk40xx, nvc0_grctx_init_unk44xx, @@ -985,6 +987,8 @@ struct nvc0_graph_init * nve4_grctx_init_gpc[] = { nve4_grctx_init_gpc_0, nvc0_grctx_init_gpc_1, + nve4_grctx_init_tpc, + nve4_grctx_init_unk, NULL }; @@ -1009,9 +1013,8 @@ nve4_grctx_oclass = &(struct nvc0_grctx_oclass) { }, .main = nve4_grctx_generate_main, .mods = nve4_grctx_generate_mods, - .mmio = nve4_grctx_init_mmio, + .hub = nve4_grctx_init_hub, .gpc = nve4_grctx_init_gpc, - .tpc = nve4_grctx_init_tpc, .icmd = nve4_grctx_init_icmd, .mthd = nve4_grctx_init_mthd, }.base; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c index a692389cd9cf..c41a6f07e958 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c @@ -200,7 +200,11 @@ nvf0_grctx_init_tpc[] = { { 0x419f70, 1, 0x04, 0x00007300 }, { 0x419f78, 1, 0x04, 0x000000eb }, { 0x419f7c, 1, 0x04, 0x00000404 }, + {} +}; +static struct nvc0_graph_init +nvf0_grctx_init_unk[] = { { 0x41be24, 1, 0x04, 0x00000006 }, { 0x41bec0, 1, 0x04, 0x10000000 }, { 0x41bec4, 1, 0x04, 0x00037f7f }, @@ -214,12 +218,11 @@ nvf0_grctx_init_tpc[] = { { 0x41bfd0, 1, 0x04, 0x00900103 }, { 0x41bfe0, 1, 0x04, 0x00400001 }, { 0x41bfe4, 1, 0x04, 0x00000000 }, - {} }; static struct nvc0_graph_init * -nvf0_grctx_init_mmio[] = { +nvf0_grctx_init_hub[] = { nvc0_grctx_init_base, nvf0_grctx_init_unk40xx, nvf0_grctx_init_unk44xx, @@ -239,6 +242,8 @@ struct nvc0_graph_init * nvf0_grctx_init_gpc[] = { nvf0_grctx_init_gpc_0, nvc0_grctx_init_gpc_1, + nvf0_grctx_init_tpc, + nvf0_grctx_init_unk, NULL }; @@ -263,9 +268,8 @@ nvf0_grctx_oclass = &(struct nvc0_grctx_oclass) { }, .main = nve4_grctx_generate_main, .mods = nve4_grctx_generate_mods, - .mmio = nvf0_grctx_init_mmio, + .hub = nvf0_grctx_init_hub, .gpc = nvf0_grctx_init_gpc, - .tpc = nvf0_grctx_init_tpc, .icmd = nvc0_grctx_init_icmd, .mthd = nvf0_grctx_init_mthd, }.base; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h index 52d70ba5ffb5..f8d653b11ad7 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h @@ -151,9 +151,8 @@ struct nvc0_grctx_oclass { /* context-specific modify-on-first-load list generation function */ void (*mods)(struct nvc0_graph_priv *, struct nvc0_grctx *); /* mmio context data */ - struct nvc0_graph_init **mmio; + struct nvc0_graph_init **hub; struct nvc0_graph_init **gpc; - struct nvc0_graph_init *tpc; /* indirect context data, generated with icmds/mthds */ struct nvc0_graph_init *icmd; struct nvc0_graph_mthd *mthd; @@ -213,7 +212,7 @@ void nvc0_grctx_generate_r418bb8(struct nvc0_graph_priv *); void nvc0_grctx_generate_r406800(struct nvc0_graph_priv *); extern struct nouveau_oclass *nvc0_grctx_oclass; -extern struct nvc0_graph_init *nvc0_grctx_init_mmio[]; +extern struct nvc0_graph_init *nvc0_grctx_init_hub[]; extern struct nvc0_graph_init nvc0_grctx_init_base[]; extern struct nvc0_graph_init nvc0_grctx_init_unk40xx[]; extern struct nvc0_graph_init nvc0_grctx_init_unk44xx[]; @@ -225,7 +224,6 @@ extern struct nvc0_graph_init nvc0_grctx_init_unk78xx[]; extern struct nvc0_graph_init nvc0_grctx_init_unk80xx[]; extern struct nvc0_graph_init nvc0_grctx_init_gpc_0[]; extern struct nvc0_graph_init nvc0_grctx_init_gpc_1[]; -extern struct nvc0_graph_init *nvc0_grctx_init_gpc[]; extern struct nvc0_graph_init nvc0_grctx_init_tpc[]; extern struct nvc0_graph_init nvc0_grctx_init_icmd[]; extern struct nvc0_graph_init nvd9_grctx_init_icmd[]; // -- cgit v1.2.3 From a32b2ffb82b5a386a13fde40dc131f853636dcf5 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 1 Jul 2013 15:11:24 +1000 Subject: drm/nvc0-/gr: generate cs register lists from grctx data Signed-off-by: Ben Skeggs --- .../gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc | 52 +- .../drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc | 114 --- .../nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h | 404 ++++------ .../drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc | 141 ---- .../nouveau/core/engine/graph/fuc/gpcnve0.fuc.h | 505 +++++------- .../gpu/drm/nouveau/core/engine/graph/fuc/hub.fuc | 37 +- .../drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc | 79 -- .../nouveau/core/engine/graph/fuc/hubnvc0.fuc.h | 869 +++++++++------------ .../drm/nouveau/core/engine/graph/fuc/hubnve0.fuc | 125 --- .../nouveau/core/engine/graph/fuc/hubnve0.fuc.h | 851 +++++++++----------- drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c | 48 +- 11 files changed, 1152 insertions(+), 2073 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc index 3d7599d5cca6..97f775b02451 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc @@ -29,23 +29,26 @@ */ #ifdef INCLUDE_DATA +gpc_mmio_list_head: .b32 #mmio_list_base +gpc_mmio_list_tail: +tpc_mmio_list_head: .b32 #mmio_list_base +tpc_mmio_list_tail: +unk_mmio_list_head: .b32 #mmio_list_base +unk_mmio_list_tail: .b32 #mmio_list_base + gpc_id: .b32 0 -gpc_mmio_list_head: .b32 0 -gpc_mmio_list_tail: .b32 0 tpc_count: .b32 0 tpc_mask: .b32 0 -tpc_mmio_list_head: .b32 0 -tpc_mmio_list_tail: .b32 0 #ifdef NVGK unk_count: .b32 1 unk_mask: .b32 1 -unk_mmio_list_head: .b32 #nve4_unk_mmio_head -unk_mmio_list_tail: .b32 #nve4_unk_mmio_tail #endif cmd_queue: queue_init + +mmio_list_base: #endif #ifdef INCLUDE_CODE @@ -68,7 +71,6 @@ error: // fall through to main loop after completion. // // Input: -// CC_SCRATCH[0]: chipset (PMC_BOOT_0 read returns 0x0bad0bad... sigh) // CC_SCRATCH[1]: context base // // Output: @@ -113,23 +115,7 @@ init: iord $r2 I[$r1 + 0x000] // MYINDEX st b32 D[$r0 + #gpc_id] $r2 - // find context data for this chipset - mov $r2 0x800 - shl b32 $r2 6 - iord $r2 I[$r2 + 0x000] // CC_SCRATCH[0] - mov $r1 #chipsets - 12 - init_find_chipset: - add b32 $r1 12 - ld b32 $r3 D[$r1 + 0x00] - cmpu b32 $r3 $r2 - bra e #init_context - cmpu b32 $r3 0 - bra ne #init_find_chipset - // unknown chipset - ret - // initialise context base, and size tracking - init_context: mov $r2 0x800 shl b32 $r2 6 iord $r2 I[$r2 + 0x100] // CC_SCRATCH[1], initial base @@ -143,24 +129,16 @@ init: iowr I[$r4 + 0x000] $r5 // MMCTX_SAVE_SWBASE iowr I[$r4 + 0x100] $r5 // MMCTX_LOAD_SWBASE - // calculate GPC mmio context size, store the chipset-specific - // mmio list pointers somewhere we can get at them later without - // re-parsing the chipset list - clear b32 $r14 - clear b32 $r15 - ld b16 $r14 D[$r1 + 4] - ld b16 $r15 D[$r1 + 6] - st b16 D[$r0 + #gpc_mmio_list_head] $r14 - st b16 D[$r0 + #gpc_mmio_list_tail] $r15 + // calculate GPC mmio context size + ld b32 $r14 D[$r0 + #gpc_mmio_list_head] + ld b32 $r15 D[$r0 + #gpc_mmio_list_tail] call #mmctx_size add b32 $r2 $r15 add b32 $r3 $r15 - // calculate per-TPC mmio context size, store the list pointers - ld b16 $r14 D[$r1 + 8] - ld b16 $r15 D[$r1 + 10] - st b16 D[$r0 + #tpc_mmio_list_head] $r14 - st b16 D[$r0 + #tpc_mmio_list_tail] $r15 + // calculate per-TPC mmio context size + ld b32 $r14 D[$r0 + #tpc_mmio_list_head] + ld b32 $r15 D[$r0 + #tpc_mmio_list_tail] call #mmctx_size ld b32 $r14 D[$r0 + #tpc_count] mulu $r14 $r15 diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc index c2d9e59bb58f..dcacfb5f6ff7 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc @@ -29,120 +29,6 @@ #define INCLUDE_DATA #include "com.fuc" #include "gpc.fuc" - -chipsets: -.b8 0xc0 0 0 0 -.b16 #nvc0_gpc_mmio_head -.b16 #nvc0_gpc_mmio_tail -.b16 #nvc0_tpc_mmio_head -.b16 #nvc0_tpc_mmio_tail -.b8 0xc1 0 0 0 -.b16 #nvc0_gpc_mmio_head -.b16 #nvc1_gpc_mmio_tail -.b16 #nvc0_tpc_mmio_head -.b16 #nvc1_tpc_mmio_tail -.b8 0xc3 0 0 0 -.b16 #nvc0_gpc_mmio_head -.b16 #nvc0_gpc_mmio_tail -.b16 #nvc0_tpc_mmio_head -.b16 #nvc3_tpc_mmio_tail -.b8 0xc4 0 0 0 -.b16 #nvc0_gpc_mmio_head -.b16 #nvc0_gpc_mmio_tail -.b16 #nvc0_tpc_mmio_head -.b16 #nvc3_tpc_mmio_tail -.b8 0xc8 0 0 0 -.b16 #nvc0_gpc_mmio_head -.b16 #nvc0_gpc_mmio_tail -.b16 #nvc0_tpc_mmio_head -.b16 #nvc0_tpc_mmio_tail -.b8 0xce 0 0 0 -.b16 #nvc0_gpc_mmio_head -.b16 #nvc0_gpc_mmio_tail -.b16 #nvc0_tpc_mmio_head -.b16 #nvc3_tpc_mmio_tail -.b8 0xcf 0 0 0 -.b16 #nvc0_gpc_mmio_head -.b16 #nvc0_gpc_mmio_tail -.b16 #nvc0_tpc_mmio_head -.b16 #nvc3_tpc_mmio_tail -.b8 0xd9 0 0 0 -.b16 #nvd9_gpc_mmio_head -.b16 #nvc1_gpc_mmio_tail -.b16 #nvc0_tpc_mmio_head -.b16 #nvd9_tpc_mmio_tail -.b8 0xd7 0 0 0 -.b16 #nvd9_gpc_mmio_head -.b16 #nvc1_gpc_mmio_tail -.b16 #nvc0_tpc_mmio_head -.b16 #nvd9_tpc_mmio_tail -.b8 0 0 0 0 - -// GPC mmio lists -nvc0_gpc_mmio_head: -mmctx_data(0x000408, 1) -nvd9_gpc_mmio_head: -mmctx_data(0x000380, 1) -mmctx_data(0x000400, 2); -mmctx_data(0x00040c, 3); -mmctx_data(0x000450, 9) -mmctx_data(0x000600, 1) -mmctx_data(0x000684, 1) -mmctx_data(0x000700, 5) -mmctx_data(0x000800, 1) -mmctx_data(0x000808, 3) -mmctx_data(0x000828, 1) -mmctx_data(0x000830, 1) -mmctx_data(0x0008d8, 1) -mmctx_data(0x0008e0, 1) -mmctx_data(0x0008e8, 6) -mmctx_data(0x00091c, 1) -mmctx_data(0x000924, 3) -mmctx_data(0x000b00, 1) -mmctx_data(0x000b08, 6) -mmctx_data(0x000bb8, 1) -mmctx_data(0x000c08, 1) -mmctx_data(0x000c10, 8) -mmctx_data(0x000c80, 1) -mmctx_data(0x000c8c, 1) -mmctx_data(0x001000, 3) -mmctx_data(0x001014, 1) -nvc0_gpc_mmio_tail: -mmctx_data(0x000c6c, 1); -nvc1_gpc_mmio_tail: - -// TPC mmio lists -nvc0_tpc_mmio_head: -mmctx_data(0x000018, 1) -mmctx_data(0x00003c, 1) -mmctx_data(0x000048, 1) -mmctx_data(0x000064, 1) -mmctx_data(0x000088, 1) -mmctx_data(0x000200, 6) -mmctx_data(0x000300, 6) -mmctx_data(0x0003d0, 1) -mmctx_data(0x0003e0, 2) -mmctx_data(0x000400, 3) -mmctx_data(0x000420, 1) -mmctx_data(0x0004b0, 1) -mmctx_data(0x0004e8, 1) -mmctx_data(0x0004f4, 1) -mmctx_data(0x000520, 2) -mmctx_data(0x000604, 4) -mmctx_data(0x000644, 20) -mmctx_data(0x000698, 1) -mmctx_data(0x000750, 2) -nvc0_tpc_mmio_tail: -mmctx_data(0x00021c, 2) -mmctx_data(0x0002c4, 1) -mmctx_data(0x000730, 8) -mmctx_data(0x000758, 1) -nvc3_tpc_mmio_tail: -mmctx_data(0x000544, 1) -nvc1_tpc_mmio_tail: -mmctx_data(0x000424, 2); -mmctx_data(0x0006e0, 1); -nvd9_tpc_mmio_tail: #undef INCLUDE_DATA .section #nvc0_grgpc_code diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h index 2744ed7d86a9..50675f30d24d 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h @@ -1,17 +1,19 @@ uint32_t nvc0_grgpc_data[] = { -/* 0x0000: gpc_id */ - 0x00000000, -/* 0x0004: gpc_mmio_list_head */ - 0x00000000, -/* 0x0008: gpc_mmio_list_tail */ - 0x00000000, -/* 0x000c: tpc_count */ - 0x00000000, -/* 0x0010: tpc_mask */ +/* 0x0000: gpc_mmio_list_head */ + 0x00000064, +/* 0x0004: gpc_mmio_list_tail */ +/* 0x0004: tpc_mmio_list_head */ + 0x00000064, +/* 0x0008: tpc_mmio_list_tail */ +/* 0x0008: unk_mmio_list_head */ + 0x00000064, +/* 0x000c: unk_mmio_list_tail */ + 0x00000064, +/* 0x0010: gpc_id */ 0x00000000, -/* 0x0014: tpc_mmio_list_head */ +/* 0x0014: tpc_count */ 0x00000000, -/* 0x0018: tpc_mmio_list_tail */ +/* 0x0018: tpc_mask */ 0x00000000, /* 0x001c: cmd_queue */ 0x00000000, @@ -32,96 +34,6 @@ uint32_t nvc0_grgpc_data[] = { 0x00000000, 0x00000000, 0x00000000, -/* 0x0064: chipsets */ - 0x000000c0, - 0x013c00d4, - 0x018c0140, - 0x000000c1, - 0x014000d4, - 0x01a00140, - 0x000000c3, - 0x013c00d4, - 0x019c0140, - 0x000000c4, - 0x013c00d4, - 0x019c0140, - 0x000000c8, - 0x013c00d4, - 0x018c0140, - 0x000000ce, - 0x013c00d4, - 0x019c0140, - 0x000000cf, - 0x013c00d4, - 0x019c0140, - 0x000000d9, - 0x014000d8, - 0x01a80140, - 0x000000d7, - 0x014000d8, - 0x01a80140, - 0x00000000, -/* 0x00d4: nvc0_gpc_mmio_head */ - 0x00000408, -/* 0x00d8: nvd9_gpc_mmio_head */ - 0x00000380, - 0x04000400, - 0x0800040c, - 0x20000450, - 0x00000600, - 0x00000684, - 0x10000700, - 0x00000800, - 0x08000808, - 0x00000828, - 0x00000830, - 0x000008d8, - 0x000008e0, - 0x140008e8, - 0x0000091c, - 0x08000924, - 0x00000b00, - 0x14000b08, - 0x00000bb8, - 0x00000c08, - 0x1c000c10, - 0x00000c80, - 0x00000c8c, - 0x08001000, - 0x00001014, -/* 0x013c: nvc0_gpc_mmio_tail */ - 0x00000c6c, -/* 0x0140: nvc1_gpc_mmio_tail */ -/* 0x0140: nvc0_tpc_mmio_head */ - 0x00000018, - 0x0000003c, - 0x00000048, - 0x00000064, - 0x00000088, - 0x14000200, - 0x14000300, - 0x000003d0, - 0x040003e0, - 0x08000400, - 0x00000420, - 0x000004b0, - 0x000004e8, - 0x000004f4, - 0x04000520, - 0x0c000604, - 0x4c000644, - 0x00000698, - 0x04000750, -/* 0x018c: nvc0_tpc_mmio_tail */ - 0x0400021c, - 0x000002c4, - 0x1c000730, - 0x00000758, -/* 0x019c: nvc3_tpc_mmio_tail */ - 0x00000544, -/* 0x01a0: nvc1_tpc_mmio_tail */ - 0x04000424, - 0x000006e0, }; uint32_t nvc0_grgpc_code[] = { @@ -355,7 +267,7 @@ uint32_t nvc0_grgpc_code[] = { 0xf10004fe, 0xf0120017, 0x12d00227, - 0x3e17f100, + 0x0d17f100, 0x0010fe04, 0x040017f1, 0xf0c010d0, @@ -367,154 +279,152 @@ uint32_t nvc0_grgpc_code[] = { 0x1f24f001, 0xb60432bb, 0x02800132, - 0x04038003, + 0x06038005, 0x040010b7, 0x800012cf, - 0x27f10002, + 0x27f10402, 0x24b60800, - 0x0022cf06, -/* 0x035f: init_find_chipset */ - 0xb65817f0, - 0x13980c10, - 0x0432b800, - 0xb00b0bf4, - 0x1bf40034, -/* 0x0373: init_context */ - 0xf100f8f1, - 0xb6080027, - 0x22cf0624, - 0xf134bd40, - 0xb6070047, - 0x25950644, - 0x0045d008, - 0xbd4045d0, - 0x58f4bde4, - 0x1f58021e, - 0x020e4003, - 0xf5040f40, - 0xbb013d21, - 0x3fbb002f, - 0x041e5800, - 0x40051f58, - 0x0f400a0e, - 0x3d21f50c, - 0x030e9801, - 0xbb00effd, - 0x3ebb002e, - 0x0040b700, - 0x0235b613, - 0xb60043d0, - 0x35b60825, - 0x0120b606, - 0xb60130b6, - 0x34b60824, - 0x022fb908, - 0x026321f5, - 0xf1003fbb, - 0xb6080017, - 0x13d00614, - 0x0010b740, - 0xf024bd08, - 0x12d01f29, -/* 0x0401: main */ - 0x0031f400, - 0xf00028f4, - 0x21f41cd7, - 0xf401f439, - 0xf404e4b0, - 0x81fe1e18, - 0x0627f001, - 0x12fd20bd, - 0x01e4b604, - 0xfe051efd, - 0x21f50018, - 0x0ef404c3, -/* 0x0431: main_not_ctx_xfer */ - 0x10ef94d3, - 0xf501f5f0, - 0xf402ec21, -/* 0x043e: ih */ - 0x80f9c60e, - 0xf90188fe, - 0xf990f980, - 0xf9b0f9a0, - 0xf9e0f9d0, - 0x800acff0, - 0xf404abc4, - 0xb7f11d0b, - 0xd7f01900, - 0x40becf1c, - 0xf400bfcf, - 0xb0b70421, - 0xe7f00400, - 0x00bed001, -/* 0x0474: ih_no_fifo */ - 0xfc400ad0, - 0xfce0fcf0, - 0xfcb0fcd0, - 0xfc90fca0, - 0x0088fe80, - 0x32f480fc, -/* 0x048f: hub_barrier_done */ - 0xf001f800, - 0x0e9801f7, - 0x04febb00, - 0x9418e7f1, - 0xf440e3f0, - 0x00f88d21, -/* 0x04a4: ctx_redswitch */ - 0x0614e7f1, - 0xf006e4b6, - 0xefd020f7, - 0x08f7f000, -/* 0x04b4: ctx_redswitch_delay */ - 0xf401f2b6, - 0xf7f1fd1b, - 0xefd00a20, -/* 0x04c3: ctx_xfer */ - 0xf100f800, - 0xb60a0417, - 0x1fd00614, - 0x0711f400, - 0x04a421f5, -/* 0x04d4: ctx_xfer_not_load */ - 0x4afc17f1, - 0xf00213f0, - 0x12d00c27, - 0x0721f500, - 0xfc27f102, - 0x0223f047, - 0xf00020d0, - 0x20b6012c, - 0x0012d003, - 0xf001acf0, - 0xb7f002a5, - 0x50b3f000, - 0xb6000c98, - 0xbcbb0fc4, - 0x010c9800, - 0xf0020d98, - 0x21f500e7, - 0xacf0015c, - 0x00b7f101, - 0x50b3f040, - 0xb6000c98, - 0xbcbb0fc4, - 0x050c9800, - 0x98060d98, - 0xe7f1040f, - 0x21f50800, - 0x21f5015c, - 0x01f40207, - 0x1412f406, -/* 0x0548: ctx_xfer_post */ - 0x4afc17f1, - 0xf00213f0, - 0x12d00d27, - 0x0721f500, -/* 0x0559: ctx_xfer_done */ - 0x8f21f502, - 0x0000f804, + 0x4022cf06, + 0x47f134bd, + 0x44b60700, + 0x08259506, + 0xd00045d0, + 0x0e984045, + 0x010f9800, + 0x013d21f5, + 0xbb002fbb, + 0x0e98003f, + 0x020f9801, + 0x013d21f5, + 0xfd050e98, + 0x2ebb00ef, + 0x003ebb00, + 0x130040b7, + 0xd00235b6, + 0x25b60043, + 0x0635b608, + 0xb60120b6, + 0x24b60130, + 0x0834b608, + 0xf5022fb9, + 0xbb026321, + 0x17f1003f, + 0x14b60800, + 0x4013d006, + 0x080010b7, + 0x29f024bd, + 0x0012d01f, +/* 0x03d0: main */ + 0xf40031f4, + 0xd7f00028, + 0x3921f41c, + 0xb0f401f4, + 0x18f404e4, + 0x0181fe1e, + 0xbd0627f0, + 0x0412fd20, + 0xfd01e4b6, + 0x18fe051e, + 0x9221f500, + 0xd30ef404, +/* 0x0400: main_not_ctx_xfer */ + 0xf010ef94, + 0x21f501f5, + 0x0ef402ec, +/* 0x040d: ih */ + 0xfe80f9c6, + 0x80f90188, + 0xa0f990f9, + 0xd0f9b0f9, + 0xf0f9e0f9, + 0xc4800acf, + 0x0bf404ab, + 0x00b7f11d, + 0x1cd7f019, + 0xcf40becf, + 0x21f400bf, + 0x00b0b704, + 0x01e7f004, +/* 0x0443: ih_no_fifo */ + 0xd000bed0, + 0xf0fc400a, + 0xd0fce0fc, + 0xa0fcb0fc, + 0x80fc90fc, + 0xfc0088fe, + 0x0032f480, +/* 0x045e: hub_barrier_done */ + 0xf7f001f8, + 0x040e9801, + 0xf104febb, + 0xf09418e7, + 0x21f440e3, +/* 0x0473: ctx_redswitch */ + 0xf100f88d, + 0xb60614e7, + 0xf7f006e4, + 0x00efd020, +/* 0x0483: ctx_redswitch_delay */ + 0xb608f7f0, + 0x1bf401f2, + 0x20f7f1fd, + 0x00efd00a, +/* 0x0492: ctx_xfer */ + 0x17f100f8, + 0x14b60a04, + 0x001fd006, + 0xf50711f4, +/* 0x04a3: ctx_xfer_not_load */ + 0xf1047321, + 0xf04afc17, + 0x27f00213, + 0x0012d00c, + 0x020721f5, + 0x47fc27f1, + 0xd00223f0, + 0x2cf00020, + 0x0320b601, + 0xf00012d0, + 0xa5f001ac, + 0x00b7f002, + 0x9850b3f0, + 0xc4b6040c, + 0x00bcbb0f, + 0x98000c98, + 0xe7f0010d, + 0x5c21f500, + 0x01acf001, + 0x4000b7f1, + 0x9850b3f0, + 0xc4b6040c, + 0x00bcbb0f, + 0x98010c98, + 0x0f98020d, + 0x00e7f106, + 0x5c21f508, + 0x0721f501, + 0x0601f402, +/* 0x0517: ctx_xfer_post */ + 0xf11412f4, + 0xf04afc17, + 0x27f00213, + 0x0012d00d, + 0x020721f5, +/* 0x0528: ctx_xfer_done */ + 0x045e21f5, + 0x000000f8, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc index 056f0d374897..b2169be2d82f 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc @@ -29,147 +29,6 @@ #define INCLUDE_DATA #include "com.fuc" #include "gpc.fuc" - -chipsets: -.b8 0xe4 0 0 0 -.b16 #nve4_gpc_mmio_head -.b16 #nve4_gpc_mmio_tail -.b16 #nve4_tpc_mmio_head -.b16 #nve4_tpc_mmio_tail -.b8 0xe7 0 0 0 -.b16 #nve4_gpc_mmio_head -.b16 #nve4_gpc_mmio_tail -.b16 #nve4_tpc_mmio_head -.b16 #nve4_tpc_mmio_tail -.b8 0xe6 0 0 0 -.b16 #nve4_gpc_mmio_head -.b16 #nve4_gpc_mmio_tail -.b16 #nve4_tpc_mmio_head -.b16 #nve4_tpc_mmio_tail -.b8 0xf0 0 0 0 -.b16 #nvf0_gpc_mmio_head -.b16 #nvf0_gpc_mmio_tail -.b16 #nvf0_tpc_mmio_head -.b16 #nvf0_tpc_mmio_tail -.b8 0 0 0 0 - -// GPC mmio lists -nve4_gpc_mmio_head: -mmctx_data(0x000380, 1) -mmctx_data(0x000400, 2) -mmctx_data(0x00040c, 3) -mmctx_data(0x000450, 9) -mmctx_data(0x000600, 1) -mmctx_data(0x000684, 1) -mmctx_data(0x000700, 5) -mmctx_data(0x000800, 1) -mmctx_data(0x000808, 3) -mmctx_data(0x000828, 1) -mmctx_data(0x000830, 1) -mmctx_data(0x0008d8, 1) -mmctx_data(0x0008e0, 1) -mmctx_data(0x0008e8, 6) -mmctx_data(0x00091c, 1) -mmctx_data(0x000924, 3) -mmctx_data(0x000b00, 1) -mmctx_data(0x000b08, 6) -mmctx_data(0x000bb8, 1) -mmctx_data(0x000c08, 1) -mmctx_data(0x000c10, 8) -mmctx_data(0x000c40, 1) -mmctx_data(0x000c6c, 1) -mmctx_data(0x000c80, 1) -mmctx_data(0x000c8c, 1) -mmctx_data(0x001000, 3) -mmctx_data(0x001014, 1) -nve4_gpc_mmio_tail: - -nvf0_gpc_mmio_head: -mmctx_data(0x000380, 1) -mmctx_data(0x000400, 2) -mmctx_data(0x00040c, 3) -mmctx_data(0x000450, 9) -mmctx_data(0x000600, 1) -mmctx_data(0x000684, 1) -mmctx_data(0x000700, 5) -mmctx_data(0x000800, 1) -mmctx_data(0x000808, 3) -mmctx_data(0x000828, 1) -mmctx_data(0x000830, 1) -mmctx_data(0x0008d8, 1) -mmctx_data(0x0008e0, 1) -mmctx_data(0x0008e8, 6) -mmctx_data(0x00091c, 1) -mmctx_data(0x000924, 3) -mmctx_data(0x000b00, 1) -mmctx_data(0x000b08, 6) -mmctx_data(0x000bb8, 1) -mmctx_data(0x000c08, 1) -mmctx_data(0x000c10, 8) -mmctx_data(0x000c40, 1) -mmctx_data(0x000c6c, 1) -mmctx_data(0x000c80, 1) -mmctx_data(0x000c8c, 1) -mmctx_data(0x000d24, 1) -mmctx_data(0x001000, 3) -mmctx_data(0x001014, 1) -nvf0_gpc_mmio_tail: - -// TPC mmio lists -nve4_tpc_mmio_head: -mmctx_data(0x000048, 1) -mmctx_data(0x000064, 1) -mmctx_data(0x000088, 1) -mmctx_data(0x000200, 6) -mmctx_data(0x00021c, 2) -mmctx_data(0x000230, 1) -mmctx_data(0x0002c4, 1) -mmctx_data(0x000400, 3) -mmctx_data(0x000420, 3) -mmctx_data(0x0004e8, 1) -mmctx_data(0x0004f4, 1) -mmctx_data(0x000604, 4) -mmctx_data(0x000644, 22) -mmctx_data(0x0006ac, 2) -mmctx_data(0x0006c8, 1) -mmctx_data(0x000730, 8) -mmctx_data(0x000758, 1) -mmctx_data(0x000770, 1) -mmctx_data(0x000778, 2) -nve4_tpc_mmio_tail: - -nvf0_tpc_mmio_head: -mmctx_data(0x000048, 1) -mmctx_data(0x000064, 1) -mmctx_data(0x000088, 1) -mmctx_data(0x000200, 6) -mmctx_data(0x00021c, 2) -mmctx_data(0x000230, 1) -mmctx_data(0x0002c4, 1) -mmctx_data(0x000400, 3) -mmctx_data(0x000420, 3) -mmctx_data(0x0004e8, 1) -mmctx_data(0x0004f4, 1) -mmctx_data(0x000604, 4) -mmctx_data(0x000644, 22) -mmctx_data(0x0006ac, 2) -mmctx_data(0x0006b8, 1) -mmctx_data(0x0006c8, 1) -mmctx_data(0x000730, 8) -mmctx_data(0x000758, 1) -mmctx_data(0x000770, 1) -mmctx_data(0x000778, 2) -nvf0_tpc_mmio_tail: - -// UNK mmio lists -nve4_unk_mmio_head: -mmctx_data(0x000024, 1) -mmctx_data(0x0000c0, 2) -mmctx_data(0x0000e4, 1) -mmctx_data(0x000100, 6) -mmctx_data(0x0001d0, 1) -mmctx_data(0x0001e0, 2) -nve4_unk_mmio_tail: #undef INCLUDE_DATA .section #nve0_grgpc_code diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h index 08c0f4731681..dc26c2822e9f 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h @@ -1,168 +1,43 @@ uint32_t nve0_grgpc_data[] = { -/* 0x0000: gpc_id */ +/* 0x0000: gpc_mmio_list_head */ + 0x0000006c, +/* 0x0004: gpc_mmio_list_tail */ +/* 0x0004: tpc_mmio_list_head */ + 0x0000006c, +/* 0x0008: tpc_mmio_list_tail */ +/* 0x0008: unk_mmio_list_head */ + 0x0000006c, +/* 0x000c: unk_mmio_list_tail */ + 0x0000006c, +/* 0x0010: gpc_id */ + 0x00000000, +/* 0x0014: tpc_count */ + 0x00000000, +/* 0x0018: tpc_mask */ + 0x00000000, +/* 0x001c: unk_count */ + 0x00000001, +/* 0x0020: unk_mask */ + 0x00000001, +/* 0x0024: cmd_queue */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, 0x00000000, -/* 0x0004: gpc_mmio_list_head */ 0x00000000, -/* 0x0008: gpc_mmio_list_tail */ 0x00000000, -/* 0x000c: tpc_count */ 0x00000000, -/* 0x0010: tpc_mask */ 0x00000000, -/* 0x0014: tpc_mmio_list_head */ 0x00000000, -/* 0x0018: tpc_mmio_list_tail */ 0x00000000, -/* 0x001c: unk_count */ - 0x00000001, -/* 0x0020: unk_mask */ - 0x00000001, -/* 0x0024: unk_mmio_list_head */ - 0x00000220, -/* 0x0028: unk_mmio_list_tail */ - 0x00000238, -/* 0x002c: cmd_queue */ - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, -/* 0x0074: chipsets */ - 0x000000e4, - 0x011400a8, - 0x01d00184, - 0x000000e7, - 0x011400a8, - 0x01d00184, - 0x000000e6, - 0x011400a8, - 0x01d00184, - 0x000000f0, - 0x01840114, - 0x022001d0, - 0x00000000, -/* 0x00a8: nve4_gpc_mmio_head */ - 0x00000380, - 0x04000400, - 0x0800040c, - 0x20000450, - 0x00000600, - 0x00000684, - 0x10000700, - 0x00000800, - 0x08000808, - 0x00000828, - 0x00000830, - 0x000008d8, - 0x000008e0, - 0x140008e8, - 0x0000091c, - 0x08000924, - 0x00000b00, - 0x14000b08, - 0x00000bb8, - 0x00000c08, - 0x1c000c10, - 0x00000c40, - 0x00000c6c, - 0x00000c80, - 0x00000c8c, - 0x08001000, - 0x00001014, -/* 0x0114: nve4_gpc_mmio_tail */ -/* 0x0114: nvf0_gpc_mmio_head */ - 0x00000380, - 0x04000400, - 0x0800040c, - 0x20000450, - 0x00000600, - 0x00000684, - 0x10000700, - 0x00000800, - 0x08000808, - 0x00000828, - 0x00000830, - 0x000008d8, - 0x000008e0, - 0x140008e8, - 0x0000091c, - 0x08000924, - 0x00000b00, - 0x14000b08, - 0x00000bb8, - 0x00000c08, - 0x1c000c10, - 0x00000c40, - 0x00000c6c, - 0x00000c80, - 0x00000c8c, - 0x00000d24, - 0x08001000, - 0x00001014, -/* 0x0184: nvf0_gpc_mmio_tail */ -/* 0x0184: nve4_tpc_mmio_head */ - 0x00000048, - 0x00000064, - 0x00000088, - 0x14000200, - 0x0400021c, - 0x00000230, - 0x000002c4, - 0x08000400, - 0x08000420, - 0x000004e8, - 0x000004f4, - 0x0c000604, - 0x54000644, - 0x040006ac, - 0x000006c8, - 0x1c000730, - 0x00000758, - 0x00000770, - 0x04000778, -/* 0x01d0: nve4_tpc_mmio_tail */ -/* 0x01d0: nvf0_tpc_mmio_head */ - 0x00000048, - 0x00000064, - 0x00000088, - 0x14000200, - 0x0400021c, - 0x00000230, - 0x000002c4, - 0x08000400, - 0x08000420, - 0x000004e8, - 0x000004f4, - 0x0c000604, - 0x54000644, - 0x040006ac, - 0x000006b8, - 0x000006c8, - 0x1c000730, - 0x00000758, - 0x00000770, - 0x04000778, -/* 0x0220: nvf0_tpc_mmio_tail */ -/* 0x0220: nve4_unk_mmio_head */ - 0x00000024, - 0x040000c0, - 0x000000e4, - 0x14000100, - 0x000001d0, - 0x040001e0, }; uint32_t nve0_grgpc_code[] = { @@ -396,7 +271,7 @@ uint32_t nve0_grgpc_code[] = { 0xf10004fe, 0xf0120017, 0x12d00227, - 0x5417f100, + 0x2317f100, 0x0010fe04, 0x040017f1, 0xf0c010d0, @@ -408,169 +283,167 @@ uint32_t nve0_grgpc_code[] = { 0x1f24f001, 0xb60432bb, 0x02800132, - 0x04038003, + 0x06038005, 0x040010b7, 0x800012cf, - 0x27f10002, + 0x27f10402, 0x24b60800, - 0x0022cf06, -/* 0x035f: init_find_chipset */ - 0xb66817f0, - 0x13980c10, - 0x0432b800, - 0xb00b0bf4, - 0x1bf40034, -/* 0x0373: init_context */ - 0xf100f8f1, - 0xb6080027, - 0x22cf0624, - 0xf134bd40, - 0xb6070047, - 0x25950644, - 0x0045d008, - 0xbd4045d0, - 0x58f4bde4, - 0x1f58021e, - 0x020e4003, - 0xf5040f40, - 0xbb013d21, - 0x3fbb002f, - 0x041e5800, - 0x40051f58, - 0x0f400a0e, - 0x3d21f50c, - 0x030e9801, - 0xbb00effd, - 0x3ebb002e, - 0x090e9800, - 0xf50a0f98, - 0x98013d21, - 0xeffd070e, - 0x002ebb00, - 0xb7003ebb, - 0xb6130040, - 0x43d00235, - 0x0825b600, - 0xb60635b6, - 0x30b60120, - 0x0824b601, - 0xb90834b6, - 0x21f5022f, - 0x3fbb0263, - 0x0017f100, - 0x0614b608, - 0xb74013d0, - 0xbd080010, - 0x1f29f024, -/* 0x0417: main */ - 0xf40012d0, - 0x28f40031, - 0x2cd7f000, - 0xf43921f4, - 0xe4b0f401, - 0x1e18f404, - 0xf00181fe, - 0x20bd0627, - 0xb60412fd, - 0x1efd01e4, - 0x0018fe05, - 0x04d921f5, -/* 0x0447: main_not_ctx_xfer */ - 0x94d30ef4, - 0xf5f010ef, - 0xec21f501, - 0xc60ef402, -/* 0x0454: ih */ - 0x88fe80f9, - 0xf980f901, - 0xf9a0f990, - 0xf9d0f9b0, - 0xcff0f9e0, - 0xabc4800a, - 0x1d0bf404, - 0x1900b7f1, - 0xcf2cd7f0, - 0xbfcf40be, - 0x0421f400, - 0x0400b0b7, - 0xd001e7f0, -/* 0x048a: ih_no_fifo */ - 0x0ad000be, - 0xfcf0fc40, - 0xfcd0fce0, - 0xfca0fcb0, - 0xfe80fc90, - 0x80fc0088, - 0xf80032f4, -/* 0x04a5: hub_barrier_done */ - 0x01f7f001, - 0xbb000e98, - 0xe7f104fe, - 0xe3f09418, - 0x8d21f440, -/* 0x04ba: ctx_redswitch */ - 0xe7f100f8, - 0xe4b60614, - 0x20f7f006, - 0xf000efd0, -/* 0x04ca: ctx_redswitch_delay */ - 0xf2b608f7, - 0xfd1bf401, - 0x0a20f7f1, - 0xf800efd0, -/* 0x04d9: ctx_xfer */ - 0x0417f100, - 0x0614b60a, - 0xf4001fd0, - 0x21f50711, -/* 0x04ea: ctx_xfer_not_load */ - 0x17f104ba, - 0x13f04afc, - 0x0c27f002, - 0xf50012d0, - 0xf1020721, - 0xf047fc27, - 0x20d00223, - 0x012cf000, - 0xd00320b6, - 0xacf00012, - 0x02a5f001, - 0xf000b7f0, - 0x0c9850b3, - 0x0fc4b600, - 0x9800bcbb, - 0x0d98010c, - 0x00e7f002, - 0x015c21f5, - 0xf101acf0, - 0xf04000b7, - 0x0c9850b3, - 0x0fc4b600, - 0x9800bcbb, - 0x0d98050c, - 0x040f9806, - 0x0800e7f1, - 0x015c21f5, - 0xf001acf0, - 0xb7f104a5, - 0xb3f03000, - 0x000c9850, - 0xbb0fc4b6, - 0x0c9800bc, - 0x0a0d9809, - 0xf1080f98, - 0xf50200e7, - 0xf5015c21, - 0xf4020721, - 0x12f40601, -/* 0x0585: ctx_xfer_post */ - 0xfc17f114, + 0x4022cf06, + 0x47f134bd, + 0x44b60700, + 0x08259506, + 0xd00045d0, + 0x0e984045, + 0x010f9800, + 0x013d21f5, + 0xbb002fbb, + 0x0e98003f, + 0x020f9801, + 0x013d21f5, + 0xfd050e98, + 0x2ebb00ef, + 0x003ebb00, + 0x98020e98, + 0x21f5030f, + 0x0e98013d, + 0x00effd07, + 0xbb002ebb, + 0x40b7003e, + 0x35b61300, + 0x0043d002, + 0xb60825b6, + 0x20b60635, + 0x0130b601, + 0xb60824b6, + 0x2fb90834, + 0x6321f502, + 0x003fbb02, + 0x080017f1, + 0xd00614b6, + 0x10b74013, + 0x24bd0800, + 0xd01f29f0, +/* 0x03e6: main */ + 0x31f40012, + 0x0028f400, + 0xf424d7f0, + 0x01f43921, + 0x04e4b0f4, + 0xfe1e18f4, + 0x27f00181, + 0xfd20bd06, + 0xe4b60412, + 0x051efd01, + 0xf50018fe, + 0xf404a821, +/* 0x0416: main_not_ctx_xfer */ + 0xef94d30e, + 0x01f5f010, + 0x02ec21f5, +/* 0x0423: ih */ + 0xf9c60ef4, + 0x0188fe80, + 0x90f980f9, + 0xb0f9a0f9, + 0xe0f9d0f9, + 0x0acff0f9, + 0x04abc480, + 0xf11d0bf4, + 0xf01900b7, + 0xbecf24d7, + 0x00bfcf40, + 0xb70421f4, + 0xf00400b0, + 0xbed001e7, +/* 0x0459: ih_no_fifo */ + 0x400ad000, + 0xe0fcf0fc, + 0xb0fcd0fc, + 0x90fca0fc, + 0x88fe80fc, + 0xf480fc00, + 0x01f80032, +/* 0x0474: hub_barrier_done */ + 0x9801f7f0, + 0xfebb040e, + 0x18e7f104, + 0x40e3f094, + 0xf88d21f4, +/* 0x0489: ctx_redswitch */ + 0x14e7f100, + 0x06e4b606, + 0xd020f7f0, + 0xf7f000ef, +/* 0x0499: ctx_redswitch_delay */ + 0x01f2b608, + 0xf1fd1bf4, + 0xd00a20f7, + 0x00f800ef, +/* 0x04a8: ctx_xfer */ + 0x0a0417f1, + 0xd00614b6, + 0x11f4001f, + 0x8921f507, +/* 0x04b9: ctx_xfer_not_load */ + 0xfc17f104, 0x0213f04a, - 0xd00d27f0, + 0xd00c27f0, 0x21f50012, -/* 0x0596: ctx_xfer_done */ - 0x21f50207, - 0x00f804a5, + 0x27f10207, + 0x23f047fc, + 0x0020d002, + 0xb6012cf0, + 0x12d00320, + 0x01acf000, + 0xf002a5f0, + 0xb3f000b7, + 0x040c9850, + 0xbb0fc4b6, + 0x0c9800bc, + 0x010d9800, + 0xf500e7f0, + 0xf0015c21, + 0xb7f101ac, + 0xb3f04000, + 0x040c9850, + 0xbb0fc4b6, + 0x0c9800bc, + 0x020d9801, + 0xf1060f98, + 0xf50800e7, + 0xf0015c21, + 0xa5f001ac, + 0x00b7f104, + 0x50b3f030, + 0xb6040c98, + 0xbcbb0fc4, + 0x020c9800, + 0x98030d98, + 0xe7f1080f, + 0x21f50200, + 0x21f5015c, + 0x01f40207, + 0x1412f406, +/* 0x0554: ctx_xfer_post */ + 0x4afc17f1, + 0xf00213f0, + 0x12d00d27, + 0x0721f500, +/* 0x0565: ctx_xfer_done */ + 0x7421f502, + 0x0000f804, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hub.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hub.fuc index 5c68bf6d69aa..2592a825e1a5 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hub.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hub.fuc @@ -24,11 +24,12 @@ */ #ifdef INCLUDE_DATA +hub_mmio_list_head: .b32 #hub_mmio_list_base +hub_mmio_list_tail: .b32 #hub_mmio_list_next + gpc_count: .b32 0 rop_count: .b32 0 cmd_queue: queue_init -hub_mmio_list_head: .b32 0 -hub_mmio_list_tail: .b32 0 ctx_current: .b32 0 @@ -40,6 +41,9 @@ chan_mmio_address: .b32 0 .align 256 xfer_data: .skip 256 +hub_mmio_list_base: +.b32 0x0417e91c // 0x17e91c, 2 +hub_mmio_list_next: #endif #ifdef INCLUDE_CODE @@ -62,9 +66,6 @@ error: // HUB fuc initialisation, executed by triggering ucode start, will // fall through to main loop after completion. // -// Input: -// CC_SCRATCH[0]: chipset (PMC_BOOT_0 read returns 0x0bad0bad... sigh) -// // Output: // CC_SCRATCH[0]: // 31:31: set to signal completion @@ -141,31 +142,12 @@ init: iowr I[$r2 + 0x000] $r1 iowr I[$r2 + 0x100] $r1 - // find context data for this chipset - mov $r2 0x800 - shl b32 $r2 6 - iord $r2 I[$r2 + 0x000] // CC_SCRATCH[0] - mov $r15 #chipsets - 8 - init_find_chipset: - add b32 $r15 8 - ld b32 $r3 D[$r15 + 0x00] - cmpu b32 $r3 $r2 - bra e #init_context - cmpu b32 $r3 0 - bra ne #init_find_chipset - // unknown chipset - ret - // context size calculation, reserve first 256 bytes for use by fuc - init_context: mov $r1 256 // calculate size of mmio context data - ld b16 $r14 D[$r15 + 4] - ld b16 $r15 D[$r15 + 6] - sethi $r14 0 - st b32 D[$r0 + #hub_mmio_list_head] $r14 - st b32 D[$r0 + #hub_mmio_list_tail] $r15 + ld b32 $r14 D[$r0 + #hub_mmio_list_head] + ld b32 $r15 D[$r0 + #hub_mmio_list_tail] call #mmctx_size // set mmctx base addresses now so we don't have to do it later, @@ -204,9 +186,6 @@ init: add b32 $r14 $r4 0x804 mov b32 $r15 $r1 call #nv_wr32 // CC_SCRATCH[1] = ctx offset - add b32 $r14 $r4 0x800 - mov b32 $r15 $r2 - call #nv_wr32 // CC_SCRATCH[0] = chipset add b32 $r14 $r4 0x10c clear b32 $r15 call #nv_wr32 diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc index f144f665b807..164d5b953c68 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc @@ -29,85 +29,6 @@ #define INCLUDE_DATA #include "com.fuc" #include "hub.fuc" - -chipsets: -.b8 0xc0 0 0 0 -.b16 #nvc0_hub_mmio_head -.b16 #nvc0_hub_mmio_tail -.b8 0xc1 0 0 0 -.b16 #nvc0_hub_mmio_head -.b16 #nvc1_hub_mmio_tail -.b8 0xc3 0 0 0 -.b16 #nvc0_hub_mmio_head -.b16 #nvc0_hub_mmio_tail -.b8 0xc4 0 0 0 -.b16 #nvc0_hub_mmio_head -.b16 #nvc0_hub_mmio_tail -.b8 0xc8 0 0 0 -.b16 #nvc0_hub_mmio_head -.b16 #nvc0_hub_mmio_tail -.b8 0xce 0 0 0 -.b16 #nvc0_hub_mmio_head -.b16 #nvc0_hub_mmio_tail -.b8 0xcf 0 0 0 -.b16 #nvc0_hub_mmio_head -.b16 #nvc0_hub_mmio_tail -.b8 0xd9 0 0 0 -.b16 #nvd9_hub_mmio_head -.b16 #nvd9_hub_mmio_tail -.b8 0xd7 0 0 0 -.b16 #nvd9_hub_mmio_head -.b16 #nvd9_hub_mmio_tail -.b8 0 0 0 0 - -nvc0_hub_mmio_head: -mmctx_data(0x40402c, 1) -mmctx_data(0x404174, 1) -nvd9_hub_mmio_head: -mmctx_data(0x17e91c, 2) -mmctx_data(0x400204, 2) -mmctx_data(0x404004, 10) -mmctx_data(0x404044, 1) -mmctx_data(0x404094, 14) -mmctx_data(0x4040d0, 7) -mmctx_data(0x4040f8, 1) -mmctx_data(0x404130, 3) -mmctx_data(0x404150, 3) -mmctx_data(0x404164, 2) -mmctx_data(0x404178, 2) -mmctx_data(0x404200, 8) -mmctx_data(0x404404, 14) -mmctx_data(0x404460, 4) -mmctx_data(0x404480, 1) -mmctx_data(0x404498, 1) -mmctx_data(0x404604, 4) -mmctx_data(0x404618, 32) -mmctx_data(0x404698, 21) -mmctx_data(0x4046f0, 2) -mmctx_data(0x404700, 22) -mmctx_data(0x405800, 1) -mmctx_data(0x405830, 3) -mmctx_data(0x405854, 1) -mmctx_data(0x405870, 4) -mmctx_data(0x405a00, 2) -mmctx_data(0x405a18, 1) -mmctx_data(0x406020, 1) -mmctx_data(0x406028, 4) -mmctx_data(0x4064a8, 2) -mmctx_data(0x4064b4, 2) -mmctx_data(0x407804, 1) -mmctx_data(0x40780c, 6) -mmctx_data(0x4078bc, 1) -mmctx_data(0x408000, 7) -mmctx_data(0x408064, 1) -mmctx_data(0x408800, 3) -mmctx_data(0x408900, 3) -mmctx_data(0x408980, 1) -nvc0_hub_mmio_tail: -mmctx_data(0x4064c0, 2) -nvc1_hub_mmio_tail: -mmctx_data(0x4064bc, 3) -nvd9_hub_mmio_tail: #undef INCLUDE_DATA .section #nvc0_grhub_code diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h index d1bf23001830..647452362527 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h @@ -1,9 +1,13 @@ uint32_t nvc0_grhub_data[] = { -/* 0x0000: gpc_count */ +/* 0x0000: hub_mmio_list_head */ + 0x00000300, +/* 0x0004: hub_mmio_list_tail */ + 0x00000304, +/* 0x0008: gpc_count */ 0x00000000, -/* 0x0004: rop_count */ +/* 0x000c: rop_count */ 0x00000000, -/* 0x0008: cmd_queue */ +/* 0x0010: cmd_queue */ 0x00000000, 0x00000000, 0x00000000, @@ -22,10 +26,6 @@ uint32_t nvc0_grhub_data[] = { 0x00000000, 0x00000000, 0x00000000, -/* 0x0050: hub_mmio_list_head */ - 0x00000000, -/* 0x0054: hub_mmio_list_tail */ - 0x00000000, /* 0x0058: ctx_current */ 0x00000000, 0x00000000, @@ -201,73 +201,8 @@ uint32_t nvc0_grhub_data[] = { 0x00000000, 0x00000000, 0x00000000, -/* 0x0300: chipsets */ - 0x000000c0, - 0x03f0034c, - 0x000000c1, - 0x03f4034c, - 0x000000c3, - 0x03f0034c, - 0x000000c4, - 0x03f0034c, - 0x000000c8, - 0x03f0034c, - 0x000000ce, - 0x03f0034c, - 0x000000cf, - 0x03f0034c, - 0x000000d9, - 0x03f80354, - 0x000000d7, - 0x03f80354, - 0x00000000, -/* 0x034c: nvc0_hub_mmio_head */ - 0x0040402c, - 0x00404174, -/* 0x0354: nvd9_hub_mmio_head */ +/* 0x0300: hub_mmio_list_base */ 0x0417e91c, - 0x04400204, - 0x24404004, - 0x00404044, - 0x34404094, - 0x184040d0, - 0x004040f8, - 0x08404130, - 0x08404150, - 0x04404164, - 0x04404178, - 0x1c404200, - 0x34404404, - 0x0c404460, - 0x00404480, - 0x00404498, - 0x0c404604, - 0x7c404618, - 0x50404698, - 0x044046f0, - 0x54404700, - 0x00405800, - 0x08405830, - 0x00405854, - 0x0c405870, - 0x04405a00, - 0x00405a18, - 0x00406020, - 0x0c406028, - 0x044064a8, - 0x044064b4, - 0x00407804, - 0x1440780c, - 0x004078bc, - 0x18408000, - 0x00408064, - 0x08408800, - 0x08408900, - 0x00408980, -/* 0x03f0: nvc0_hub_mmio_tail */ - 0x044064c0, -/* 0x03f4: nvc1_hub_mmio_tail */ - 0x084064bc, }; uint32_t nvc0_grhub_code[] = { @@ -503,7 +438,7 @@ uint32_t nvc0_grhub_code[] = { 0x0017f100, 0x0227f012, 0xf10012d0, - 0xfe05ba17, + 0xfe058517, 0x17f10010, 0x10d00400, 0x0437f1c0, @@ -527,462 +462,396 @@ uint32_t nvc0_grhub_code[] = { 0x9604e7f1, 0xf440e3f0, 0xf1c76821, - 0x01018090, + 0x03018090, 0x801ff4f0, - 0x17f0000f, + 0x17f0020f, 0x041fbb01, 0xf10112b6, 0xb6040c27, 0x21d00624, 0x4021d000, - 0x080027f1, - 0xcf0624b6, - 0xf7f10022, -/* 0x03aa: init_find_chipset */ - 0xf0b602f8, - 0x00f39808, - 0xf40432b8, - 0x34b00b0b, - 0xf11bf400, -/* 0x03be: init_context */ - 0x17f100f8, - 0xfe580100, - 0x03ff5802, - 0x8000e3f0, - 0x0f80140e, - 0x3d21f515, - 0x0037f101, - 0x0634b607, - 0xd0081495, - 0x34d00034, - 0x0030b740, - 0x001fbb13, - 0xd002f5b6, - 0x15b6003f, - 0x0110b608, - 0xb90814b6, - 0x21f5021f, - 0x1fbb0263, - 0x00039800, - 0x200047f1, -/* 0x040f: init_gpc */ - 0xa05043f0, - 0xb908044e, - 0x21f4021f, - 0x004ea08d, - 0x022fb908, - 0xa08d21f4, - 0xbd010c4e, - 0x8d21f4f4, - 0x01044ea0, + 0x010017f1, + 0x98000e98, + 0x21f5010f, + 0x37f1013d, + 0x34b60700, + 0x08149506, + 0xd00034d0, + 0x30b74034, + 0x1fbb1300, + 0x02f5b600, + 0xb6003fd0, + 0x10b60815, + 0x0814b601, + 0xf5021fb9, + 0xbb026321, + 0x0398001f, + 0x0047f102, + 0x5043f020, +/* 0x03e4: init_gpc */ + 0x08044ea0, + 0xf4021fb9, + 0x4ea08d21, + 0xf4bd010c, 0xa08d21f4, - 0xf001004e, - 0x21f402f7, - 0x004ea08d, -/* 0x0441: init_gpc_wait */ + 0xf401044e, + 0x4ea08d21, + 0xf7f00100, + 0x8d21f402, + 0x08004ea0, +/* 0x040c: init_gpc_wait */ + 0xc86821f4, + 0x0bf41fff, + 0x044ea0fa, 0x6821f408, - 0xf41fffc8, - 0x4ea0fa0b, - 0x21f40804, - 0x001fbb68, - 0x800040b7, - 0xf40132b6, - 0x27f1b41b, - 0x24b60800, - 0x4021d006, - 0x080020b7, - 0x19f014bd, - 0x0021d01f, -/* 0x0474: main */ - 0xf40031f4, - 0xd7f00028, - 0x3921f408, - 0xb1f401f4, - 0xf54001e4, - 0xf100d11b, + 0xb7001fbb, + 0xb6800040, + 0x1bf40132, + 0x0027f1be, + 0x0624b608, + 0xb74021d0, + 0xbd080020, + 0x1f19f014, +/* 0x043f: main */ + 0xf40021d0, + 0x28f40031, + 0x10d7f000, + 0xf43921f4, + 0xe4b1f401, + 0x1bf54001, + 0x87f100d1, + 0x84b6083c, + 0xf094bd06, + 0x89d00499, + 0x0017f100, + 0x0614b60b, + 0xcf4012cf, + 0x13c80011, + 0x7e0bf41f, + 0xf41f23c8, + 0x20f95a0b, + 0xf10212b9, 0xb6083c87, 0x94bd0684, - 0xd00499f0, - 0x17f10089, - 0x14b60b00, - 0x4012cf06, - 0xc80011cf, - 0x0bf41f13, - 0x1f23c87e, - 0xf95a0bf4, - 0x0212b920, - 0x083c87f1, + 0xd00799f0, + 0x32f40089, + 0x0231f401, + 0x07f521f5, + 0x085c87f1, 0xbd0684b6, 0x0799f094, - 0xf40089d0, - 0x31f40132, - 0x2a21f502, - 0x5c87f108, + 0xfc0089d0, + 0x3c87f120, 0x0684b608, 0x99f094bd, - 0x0089d007, - 0x87f120fc, - 0x84b6083c, - 0xf094bd06, - 0x89d00699, - 0x0131f400, - 0x082a21f5, + 0x0089d006, + 0xf50131f4, + 0xf107f521, + 0xb6085c87, + 0x94bd0684, + 0xd00699f0, + 0x0ef40089, +/* 0x04d5: chsw_prev_no_next */ + 0xb920f931, + 0x32f40212, + 0x0232f401, + 0x07f521f5, + 0x17f120fc, + 0x14b60b00, + 0x0012d006, +/* 0x04f3: chsw_no_prev */ + 0xc8130ef4, + 0x0bf41f23, + 0x0131f40d, + 0xf50232f4, +/* 0x0503: chsw_done */ + 0xf107f521, + 0xb60b0c17, + 0x27f00614, + 0x0012d001, 0x085c87f1, 0xbd0684b6, - 0x0699f094, - 0xf40089d0, -/* 0x050a: chsw_prev_no_next */ - 0x20f9310e, - 0xf40212b9, - 0x32f40132, - 0x2a21f502, - 0xf120fc08, - 0xb60b0017, - 0x12d00614, - 0x130ef400, -/* 0x0528: chsw_no_prev */ - 0xf41f23c8, - 0x31f40d0b, - 0x0232f401, - 0x082a21f5, -/* 0x0538: chsw_done */ - 0x0b0c17f1, - 0xf00614b6, - 0x12d00127, - 0x5c87f100, + 0x0499f094, + 0xf50089d0, +/* 0x0523: main_not_ctx_switch */ + 0xb0ff200e, + 0x1bf401e4, + 0x02f2b90d, + 0x078121f5, +/* 0x0533: main_not_ctx_chan */ + 0xb0420ef4, + 0x1bf402e4, + 0x3c87f12e, 0x0684b608, 0x99f094bd, - 0x0089d004, - 0xff200ef5, -/* 0x0558: main_not_ctx_switch */ - 0xf401e4b0, - 0xf2b90d1b, - 0xb621f502, - 0x420ef407, -/* 0x0568: main_not_ctx_chan */ - 0xf402e4b0, - 0x87f12e1b, - 0x84b6083c, + 0x0089d007, + 0xf40132f4, + 0x21f50232, + 0x87f107f5, + 0x84b6085c, 0xf094bd06, 0x89d00799, - 0x0132f400, - 0xf50232f4, - 0xf1082a21, - 0xb6085c87, - 0x94bd0684, - 0xd00799f0, - 0x0ef40089, -/* 0x0599: main_not_ctx_save */ - 0x10ef9411, - 0xf501f5f0, - 0xf502ec21, -/* 0x05a7: main_done */ - 0xf1fed10e, - 0xb6082017, - 0x24bd0614, - 0xd01f29f0, - 0x0ef50012, -/* 0x05ba: ih */ - 0x80f9febe, - 0xf90188fe, - 0xf990f980, - 0xf9b0f9a0, - 0xf9e0f9d0, - 0x800acff0, - 0xf404abc4, - 0xb7f11d0b, - 0xd7f01900, - 0x40becf08, - 0xf400bfcf, - 0xb0b70421, - 0xe7f00400, - 0x00bed001, -/* 0x05f0: ih_no_fifo */ - 0x0100abe4, - 0xf00d0bf4, - 0xe7f108d7, - 0x21f44001, -/* 0x0601: ih_no_ctxsw */ - 0x04b7f104, - 0xffb0bd01, - 0x0bf4b4ab, - 0x1ca7f10d, - 0x06a4b60c, -/* 0x0617: ih_no_other */ - 0xd000abd0, - 0xf0fc400a, - 0xd0fce0fc, - 0xa0fcb0fc, - 0x80fc90fc, - 0xfc0088fe, - 0x0032f480, -/* 0x0632: ctx_4160s */ - 0xe7f101f8, - 0xe3f04160, - 0x01f7f040, -/* 0x063f: ctx_4160s_wait */ - 0xf48d21f4, - 0xffc86821, - 0xfa0bf404, -/* 0x064a: ctx_4160c */ - 0xe7f100f8, - 0xe3f04160, - 0xf4f4bd40, - 0x00f88d21, -/* 0x0658: ctx_4170s */ - 0x4170e7f1, - 0xf040e3f0, - 0x21f410f5, -/* 0x0667: ctx_4170w */ + 0x110ef400, +/* 0x0564: main_not_ctx_save */ + 0xf010ef94, + 0x21f501f5, + 0x0ef502ec, +/* 0x0572: main_done */ + 0x17f1fed1, + 0x14b60820, + 0xf024bd06, + 0x12d01f29, + 0xbe0ef500, +/* 0x0585: ih */ + 0xfe80f9fe, + 0x80f90188, + 0xa0f990f9, + 0xd0f9b0f9, + 0xf0f9e0f9, + 0xc4800acf, + 0x0bf404ab, + 0x00b7f11d, + 0x10d7f019, + 0xcf40becf, + 0x21f400bf, + 0x00b0b704, + 0x01e7f004, +/* 0x05bb: ih_no_fifo */ + 0xe400bed0, + 0xf40100ab, + 0xd7f00d0b, + 0x01e7f110, + 0x0421f440, +/* 0x05cc: ih_no_ctxsw */ + 0x0104b7f1, + 0xabffb0bd, + 0x0d0bf4b4, + 0x0c1ca7f1, + 0xd006a4b6, +/* 0x05e2: ih_no_other */ + 0x0ad000ab, + 0xfcf0fc40, + 0xfcd0fce0, + 0xfca0fcb0, + 0xfe80fc90, + 0x80fc0088, + 0xf80032f4, +/* 0x05fd: ctx_4160s */ + 0x60e7f101, + 0x40e3f041, + 0xf401f7f0, +/* 0x060a: ctx_4160s_wait */ + 0x21f48d21, + 0x04ffc868, + 0xf8fa0bf4, +/* 0x0615: ctx_4160c */ + 0x60e7f100, + 0x40e3f041, + 0x21f4f4bd, +/* 0x0623: ctx_4170s */ 0xf100f88d, 0xf04170e7, - 0x21f440e3, - 0x10f4f068, - 0xf8f31bf4, -/* 0x0679: ctx_redswitch */ - 0x14e7f100, - 0x06e4b606, - 0x0270f7f1, - 0xf000efd0, -/* 0x068a: ctx_redswitch_delay */ - 0xf2b608f7, - 0xfd1bf401, - 0x0770f7f1, - 0xf800efd0, -/* 0x0699: ctx_86c */ - 0x6ce7f100, - 0x06e4b608, - 0xf100efd0, - 0xf08a14e7, - 0x21f440e3, - 0x6ce7f18d, - 0x41e3f0a8, - 0xf88d21f4, -/* 0x06b9: ctx_load */ - 0x3c87f100, - 0x0684b608, - 0x99f094bd, - 0x0089d005, - 0xf40ca7f0, - 0x17f1c921, - 0x14b60a24, - 0x0010d006, - 0x0b0037f1, - 0xd00634b6, - 0x17f14032, - 0x14b60a0c, - 0x0747f006, - 0xd00012d0, -/* 0x06f2: ctx_chan_wait_0 */ - 0x14cf4014, - 0x1f44f040, - 0xd0fa1bf4, - 0x0bfe0032, - 0x1f2af000, - 0xb60424b6, - 0x87f10220, - 0x84b6083c, - 0xf094bd06, - 0x89d00899, - 0x0417f100, - 0x0614b60a, - 0xf10012d0, - 0xb60a2017, - 0x27f00614, - 0x0023f102, - 0x0012d080, - 0xf11017f0, - 0xf0020027, - 0x12fa0223, - 0xf103f805, - 0xb6085c87, - 0x94bd0684, - 0xd00899f0, - 0x01980089, - 0x1814b681, - 0xb6800298, - 0x12fd0825, - 0x16018005, + 0xf5f040e3, + 0x8d21f410, +/* 0x0632: ctx_4170w */ + 0xe7f100f8, + 0xe3f04170, + 0x6821f440, + 0xf410f4f0, + 0x00f8f31b, +/* 0x0644: ctx_redswitch */ + 0x0614e7f1, + 0xf106e4b6, + 0xd00270f7, + 0xf7f000ef, +/* 0x0655: ctx_redswitch_delay */ + 0x01f2b608, + 0xf1fd1bf4, + 0xd00770f7, + 0x00f800ef, +/* 0x0664: ctx_86c */ + 0x086ce7f1, + 0xd006e4b6, + 0xe7f100ef, + 0xe3f08a14, + 0x8d21f440, + 0xa86ce7f1, + 0xf441e3f0, + 0x00f88d21, +/* 0x0684: ctx_load */ 0x083c87f1, 0xbd0684b6, - 0x0999f094, - 0xf10089d0, - 0xb60a0427, - 0x21d00624, - 0x0127f000, - 0x0a2017f1, + 0x0599f094, + 0xf00089d0, + 0x21f40ca7, + 0x2417f1c9, + 0x0614b60a, + 0xf10010d0, + 0xb60b0037, + 0x32d00634, + 0x0c17f140, + 0x0614b60a, + 0xd00747f0, + 0x14d00012, +/* 0x06bd: ctx_chan_wait_0 */ + 0x4014cf40, + 0xf41f44f0, + 0x32d0fa1b, + 0x000bfe00, + 0xb61f2af0, + 0x20b60424, + 0x3c87f102, + 0x0684b608, + 0x99f094bd, + 0x0089d008, + 0x0a0417f1, 0xd00614b6, 0x17f10012, - 0x13f00100, - 0x0501fa06, + 0x14b60a20, + 0x0227f006, + 0x800023f1, + 0xf00012d0, + 0x27f11017, + 0x23f00200, + 0x0512fa02, 0x87f103f8, 0x84b6085c, 0xf094bd06, - 0x89d00999, - 0x5c87f100, + 0x89d00899, + 0x81019800, + 0x981814b6, + 0x25b68002, + 0x0512fd08, + 0xf1160180, + 0xb6083c87, + 0x94bd0684, + 0xd00999f0, + 0x27f10089, + 0x24b60a04, + 0x0021d006, + 0xf10127f0, + 0xb60a2017, + 0x12d00614, + 0x0017f100, + 0x0613f001, + 0xf80501fa, + 0x5c87f103, 0x0684b608, 0x99f094bd, - 0x0089d005, -/* 0x07b6: ctx_chan */ - 0x21f500f8, - 0x21f50632, - 0xa7f006b9, - 0xc921f40c, - 0x0a1017f1, - 0xf00614b6, - 0x12d00527, -/* 0x07d1: ctx_chan_wait */ - 0x0012cf00, - 0xf40522fd, - 0x21f5fa1b, - 0x00f8064a, -/* 0x07e0: ctx_mmio_exec */ - 0xf1410398, - 0xb60a0427, - 0x23d00624, -/* 0x07ef: ctx_mmio_loop */ - 0xc434bd00, - 0x1bf4ff34, - 0x0057f10f, - 0x0653f002, - 0xf80535fa, -/* 0x0801: ctx_mmio_pull */ - 0x804e9803, - 0xf4814f98, - 0x30b68d21, - 0x0112b608, -/* 0x0813: ctx_mmio_done */ - 0x98df1bf4, - 0x23d01603, - 0x40008000, - 0x010017f1, - 0xfa0613f0, - 0x03f80601, -/* 0x082a: ctx_xfer */ - 0xf7f100f8, - 0xf4b60c00, - 0x04e7f006, -/* 0x0837: ctx_xfer_idle */ - 0xcf80fed0, - 0xe4f100fe, - 0x1bf42000, - 0x0611f4f9, -/* 0x0847: ctx_xfer_pre */ - 0xf01102f4, - 0x21f510f7, - 0x21f50699, - 0x11f40632, -/* 0x0855: ctx_xfer_pre_load */ - 0x02f7f01c, - 0x065821f5, - 0x066721f5, - 0x067921f5, - 0x21f5f4bd, - 0x21f50658, -/* 0x086e: ctx_xfer_exec */ - 0x019806b9, - 0x1427f116, - 0x0624b604, - 0xf10020d0, - 0xf0a500e7, - 0x1fb941e3, - 0x8d21f402, - 0xf004e0b6, - 0x2cf001fc, - 0x0124b602, - 0xf405f2fd, - 0x17f18d21, - 0x13f04afc, - 0x0c27f002, - 0xf50012d0, - 0xf1020721, - 0xf047fc27, - 0x20d00223, - 0x012cf000, - 0xd00320b6, - 0xacf00012, - 0x06a5f001, - 0x9800b7f0, - 0x0d98140c, - 0x00e7f015, - 0x015c21f5, - 0xf508a7f0, - 0xf5010321, - 0xf4020721, - 0xa7f02201, - 0xc921f40c, - 0x0a1017f1, - 0xf00614b6, - 0x12d00527, -/* 0x08f5: ctx_xfer_post_save_wait */ - 0x0012cf00, - 0xf40522fd, - 0x02f4fa1b, -/* 0x0901: ctx_xfer_post */ - 0x02f7f032, - 0x065821f5, - 0x21f5f4bd, - 0x21f50699, - 0x21f50226, - 0xf4bd0667, - 0x065821f5, - 0x981011f4, - 0x11fd4001, - 0x070bf405, - 0x07e021f5, -/* 0x092c: ctx_xfer_no_post_mmio */ - 0x064a21f5, -/* 0x0930: ctx_xfer_done */ - 0x000000f8, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, + 0x0089d009, + 0x085c87f1, + 0xbd0684b6, + 0x0599f094, + 0xf80089d0, +/* 0x0781: ctx_chan */ + 0xfd21f500, + 0x8421f505, + 0x0ca7f006, + 0xf1c921f4, + 0xb60a1017, + 0x27f00614, + 0x0012d005, +/* 0x079c: ctx_chan_wait */ + 0xfd0012cf, + 0x1bf40522, + 0x1521f5fa, +/* 0x07ab: ctx_mmio_exec */ + 0x9800f806, + 0x27f14103, + 0x24b60a04, + 0x0023d006, +/* 0x07ba: ctx_mmio_loop */ + 0x34c434bd, + 0x0f1bf4ff, + 0x020057f1, + 0xfa0653f0, + 0x03f80535, +/* 0x07cc: ctx_mmio_pull */ + 0x98804e98, + 0x21f4814f, + 0x0830b68d, + 0xf40112b6, +/* 0x07de: ctx_mmio_done */ + 0x0398df1b, + 0x0023d016, + 0xf1400080, + 0xf0010017, + 0x01fa0613, + 0xf803f806, +/* 0x07f5: ctx_xfer */ + 0x00f7f100, + 0x06f4b60c, + 0xd004e7f0, +/* 0x0802: ctx_xfer_idle */ + 0xfecf80fe, + 0x00e4f100, + 0xf91bf420, + 0xf40611f4, +/* 0x0812: ctx_xfer_pre */ + 0xf7f01102, + 0x6421f510, + 0xfd21f506, + 0x1c11f405, +/* 0x0820: ctx_xfer_pre_load */ + 0xf502f7f0, + 0xf5062321, + 0xf5063221, + 0xbd064421, + 0x2321f5f4, + 0x8421f506, +/* 0x0839: ctx_xfer_exec */ + 0x16019806, + 0x041427f1, + 0xd00624b6, + 0xe7f10020, + 0xe3f0a500, + 0x021fb941, + 0xb68d21f4, + 0xfcf004e0, + 0x022cf001, + 0xfd0124b6, + 0x21f405f2, + 0xfc17f18d, + 0x0213f04a, + 0xd00c27f0, + 0x21f50012, + 0x27f10207, + 0x23f047fc, + 0x0020d002, + 0xb6012cf0, + 0x12d00320, + 0x01acf000, + 0xf006a5f0, + 0x0c9800b7, + 0x010d9800, + 0xf500e7f0, + 0xf0015c21, + 0x21f508a7, + 0x21f50103, + 0x01f40207, + 0x0ca7f022, + 0xf1c921f4, + 0xb60a1017, + 0x27f00614, + 0x0012d005, +/* 0x08c0: ctx_xfer_post_save_wait */ + 0xfd0012cf, + 0x1bf40522, + 0x3202f4fa, +/* 0x08cc: ctx_xfer_post */ + 0xf502f7f0, + 0xbd062321, + 0x6421f5f4, + 0x2621f506, + 0x3221f502, + 0xf5f4bd06, + 0xf4062321, + 0x01981011, + 0x0511fd40, + 0xf5070bf4, +/* 0x08f7: ctx_xfer_no_post_mmio */ + 0xf507ab21, +/* 0x08fb: ctx_xfer_done */ + 0xf8061521, 0x00000000, }; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc index c7225db6486c..27c6a0fdfe0e 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc @@ -29,131 +29,6 @@ #define INCLUDE_DATA #include "com.fuc" #include "hub.fuc" - -chipsets: -.b8 0xe4 0 0 0 -.b16 #nve4_hub_mmio_head -.b16 #nve4_hub_mmio_tail -.b8 0xe7 0 0 0 -.b16 #nve4_hub_mmio_head -.b16 #nve4_hub_mmio_tail -.b8 0xe6 0 0 0 -.b16 #nve4_hub_mmio_head -.b16 #nve4_hub_mmio_tail -.b8 0xf0 0 0 0 -.b16 #nvf0_hub_mmio_head -.b16 #nvf0_hub_mmio_tail -.b8 0 0 0 0 - -nve4_hub_mmio_head: -mmctx_data(0x17e91c, 2) -mmctx_data(0x400204, 2) -mmctx_data(0x404010, 7) -mmctx_data(0x4040a8, 9) -mmctx_data(0x4040d0, 7) -mmctx_data(0x4040f8, 1) -mmctx_data(0x404130, 3) -mmctx_data(0x404150, 3) -mmctx_data(0x404164, 1) -mmctx_data(0x4041a0, 4) -mmctx_data(0x404200, 4) -mmctx_data(0x404404, 14) -mmctx_data(0x404460, 4) -mmctx_data(0x404480, 1) -mmctx_data(0x404498, 1) -mmctx_data(0x404604, 4) -mmctx_data(0x404618, 4) -mmctx_data(0x40462c, 2) -mmctx_data(0x404640, 1) -mmctx_data(0x404654, 1) -mmctx_data(0x404660, 1) -mmctx_data(0x404678, 19) -mmctx_data(0x4046c8, 3) -mmctx_data(0x404700, 3) -mmctx_data(0x404718, 10) -mmctx_data(0x404744, 2) -mmctx_data(0x404754, 1) -mmctx_data(0x405800, 1) -mmctx_data(0x405830, 3) -mmctx_data(0x405854, 1) -mmctx_data(0x405870, 4) -mmctx_data(0x405a00, 2) -mmctx_data(0x405a18, 1) -mmctx_data(0x405b00, 1) -mmctx_data(0x405b10, 1) -mmctx_data(0x406020, 1) -mmctx_data(0x406028, 4) -mmctx_data(0x4064a8, 2) -mmctx_data(0x4064b4, 2) -mmctx_data(0x4064c0, 12) -mmctx_data(0x4064fc, 1) -mmctx_data(0x407040, 1) -mmctx_data(0x407804, 1) -mmctx_data(0x40780c, 6) -mmctx_data(0x4078bc, 1) -mmctx_data(0x408000, 7) -mmctx_data(0x408064, 1) -mmctx_data(0x408800, 3) -mmctx_data(0x408840, 1) -mmctx_data(0x408900, 3) -mmctx_data(0x408980, 1) -nve4_hub_mmio_tail: - -nvf0_hub_mmio_head: -mmctx_data(0x17e91c, 2) -mmctx_data(0x400204, 2) -mmctx_data(0x404004, 17) -mmctx_data(0x4040a8, 9) -mmctx_data(0x4040d0, 7) -mmctx_data(0x4040f8, 1) -mmctx_data(0x404100, 10) -mmctx_data(0x404130, 3) -mmctx_data(0x404150, 3) -mmctx_data(0x404164, 1) -mmctx_data(0x40417c, 2) -mmctx_data(0x4041a0, 4) -mmctx_data(0x404200, 4) -mmctx_data(0x404404, 12) -mmctx_data(0x404438, 1) -mmctx_data(0x404460, 4) -mmctx_data(0x404480, 1) -mmctx_data(0x404498, 1) -mmctx_data(0x404604, 4) -mmctx_data(0x404618, 4) -mmctx_data(0x40462c, 2) -mmctx_data(0x404640, 1) -mmctx_data(0x404654, 1) -mmctx_data(0x404660, 1) -mmctx_data(0x404678, 19) -mmctx_data(0x4046c8, 3) -mmctx_data(0x404700, 3) -mmctx_data(0x404718, 10) -mmctx_data(0x404744, 2) -mmctx_data(0x404754, 1) -mmctx_data(0x405800, 1) -mmctx_data(0x405830, 3) -mmctx_data(0x405854, 1) -mmctx_data(0x405870, 4) -mmctx_data(0x405a00, 2) -mmctx_data(0x405a18, 1) -mmctx_data(0x405b00, 1) -mmctx_data(0x405b10, 1) -mmctx_data(0x405b20, 1) -mmctx_data(0x406020, 1) -mmctx_data(0x406028, 4) -mmctx_data(0x4064a8, 5) -mmctx_data(0x4064c0, 12) -mmctx_data(0x4064fc, 1) -mmctx_data(0x407804, 1) -mmctx_data(0x40780c, 6) -mmctx_data(0x4078bc, 1) -mmctx_data(0x408000, 7) -mmctx_data(0x408064, 1) -mmctx_data(0x408800, 3) -mmctx_data(0x408840, 1) -mmctx_data(0x408900, 3) -mmctx_data(0x408980, 1) -nvf0_hub_mmio_tail: #undef INCLUDE_DATA .section #nve0_grhub_code diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc.h index 623e8698ace1..973fcda48b78 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc.h @@ -1,9 +1,13 @@ uint32_t nve0_grhub_data[] = { -/* 0x0000: gpc_count */ +/* 0x0000: hub_mmio_list_head */ + 0x00000300, +/* 0x0004: hub_mmio_list_tail */ + 0x00000304, +/* 0x0008: gpc_count */ 0x00000000, -/* 0x0004: rop_count */ +/* 0x000c: rop_count */ 0x00000000, -/* 0x0008: cmd_queue */ +/* 0x0010: cmd_queue */ 0x00000000, 0x00000000, 0x00000000, @@ -22,10 +26,6 @@ uint32_t nve0_grhub_data[] = { 0x00000000, 0x00000000, 0x00000000, -/* 0x0050: hub_mmio_list_head */ - 0x00000000, -/* 0x0054: hub_mmio_list_tail */ - 0x00000000, /* 0x0058: ctx_current */ 0x00000000, 0x00000000, @@ -201,123 +201,8 @@ uint32_t nve0_grhub_data[] = { 0x00000000, 0x00000000, 0x00000000, -/* 0x0300: chipsets */ - 0x000000e4, - 0x03f00324, - 0x000000e7, - 0x03f00324, - 0x000000e6, - 0x03f00324, - 0x000000f0, - 0x04c403f0, - 0x00000000, -/* 0x0324: nve4_hub_mmio_head */ +/* 0x0300: hub_mmio_list_base */ 0x0417e91c, - 0x04400204, - 0x18404010, - 0x204040a8, - 0x184040d0, - 0x004040f8, - 0x08404130, - 0x08404150, - 0x00404164, - 0x0c4041a0, - 0x0c404200, - 0x34404404, - 0x0c404460, - 0x00404480, - 0x00404498, - 0x0c404604, - 0x0c404618, - 0x0440462c, - 0x00404640, - 0x00404654, - 0x00404660, - 0x48404678, - 0x084046c8, - 0x08404700, - 0x24404718, - 0x04404744, - 0x00404754, - 0x00405800, - 0x08405830, - 0x00405854, - 0x0c405870, - 0x04405a00, - 0x00405a18, - 0x00405b00, - 0x00405b10, - 0x00406020, - 0x0c406028, - 0x044064a8, - 0x044064b4, - 0x2c4064c0, - 0x004064fc, - 0x00407040, - 0x00407804, - 0x1440780c, - 0x004078bc, - 0x18408000, - 0x00408064, - 0x08408800, - 0x00408840, - 0x08408900, - 0x00408980, -/* 0x03f0: nve4_hub_mmio_tail */ -/* 0x03f0: nvf0_hub_mmio_head */ - 0x0417e91c, - 0x04400204, - 0x40404004, - 0x204040a8, - 0x184040d0, - 0x004040f8, - 0x24404100, - 0x08404130, - 0x08404150, - 0x00404164, - 0x0440417c, - 0x0c4041a0, - 0x0c404200, - 0x2c404404, - 0x00404438, - 0x0c404460, - 0x00404480, - 0x00404498, - 0x0c404604, - 0x0c404618, - 0x0440462c, - 0x00404640, - 0x00404654, - 0x00404660, - 0x48404678, - 0x084046c8, - 0x08404700, - 0x24404718, - 0x04404744, - 0x00404754, - 0x00405800, - 0x08405830, - 0x00405854, - 0x0c405870, - 0x04405a00, - 0x00405a18, - 0x00405b00, - 0x00405b10, - 0x00405b20, - 0x00406020, - 0x0c406028, - 0x104064a8, - 0x2c4064c0, - 0x004064fc, - 0x00407804, - 0x1440780c, - 0x004078bc, - 0x18408000, - 0x00408064, - 0x08408800, - 0x00408840, - 0x08408900, - 0x00408980, }; uint32_t nve0_grhub_code[] = { @@ -553,7 +438,7 @@ uint32_t nve0_grhub_code[] = { 0x0017f100, 0x0227f012, 0xf10012d0, - 0xfe05ba17, + 0xfe058517, 0x17f10010, 0x10d00400, 0x0437f1c0, @@ -577,395 +462,393 @@ uint32_t nve0_grhub_code[] = { 0x9604e7f1, 0xf440e3f0, 0xf1c76821, - 0x01018090, + 0x03018090, 0x801ff4f0, - 0x17f0000f, + 0x17f0020f, 0x041fbb01, 0xf10112b6, 0xb6040c27, 0x21d00624, 0x4021d000, - 0x080027f1, - 0xcf0624b6, - 0xf7f10022, -/* 0x03aa: init_find_chipset */ - 0xf0b602f8, - 0x00f39808, - 0xf40432b8, - 0x34b00b0b, - 0xf11bf400, -/* 0x03be: init_context */ - 0x17f100f8, - 0xfe580100, - 0x03ff5802, - 0x8000e3f0, - 0x0f80140e, - 0x3d21f515, - 0x0037f101, - 0x0634b607, - 0xd0081495, - 0x34d00034, - 0x0030b740, - 0x001fbb13, - 0xd002f5b6, - 0x15b6003f, - 0x0110b608, - 0xb90814b6, - 0x21f5021f, - 0x1fbb0263, - 0x00039800, - 0x200047f1, -/* 0x040f: init_gpc */ - 0xa05043f0, - 0xb908044e, - 0x21f4021f, - 0x004ea08d, - 0x022fb908, - 0xa08d21f4, - 0xbd010c4e, - 0x8d21f4f4, - 0x01044ea0, + 0x010017f1, + 0x98000e98, + 0x21f5010f, + 0x37f1013d, + 0x34b60700, + 0x08149506, + 0xd00034d0, + 0x30b74034, + 0x1fbb1300, + 0x02f5b600, + 0xb6003fd0, + 0x10b60815, + 0x0814b601, + 0xf5021fb9, + 0xbb026321, + 0x0398001f, + 0x0047f102, + 0x5043f020, +/* 0x03e4: init_gpc */ + 0x08044ea0, + 0xf4021fb9, + 0x4ea08d21, + 0xf4bd010c, 0xa08d21f4, - 0xf001004e, - 0x21f402f7, - 0x004ea08d, -/* 0x0441: init_gpc_wait */ + 0xf401044e, + 0x4ea08d21, + 0xf7f00100, + 0x8d21f402, + 0x08004ea0, +/* 0x040c: init_gpc_wait */ + 0xc86821f4, + 0x0bf41fff, + 0x044ea0fa, 0x6821f408, - 0xf41fffc8, - 0x4ea0fa0b, - 0x21f40804, - 0x001fbb68, - 0x800040b7, - 0xf40132b6, - 0x27f1b41b, - 0x24b60800, - 0x4021d006, - 0x080020b7, - 0x19f014bd, - 0x0021d01f, -/* 0x0474: main */ - 0xf40031f4, - 0xd7f00028, - 0x3921f408, - 0xb1f401f4, - 0xf54001e4, - 0xf100d11b, + 0xb7001fbb, + 0xb6800040, + 0x1bf40132, + 0x0027f1be, + 0x0624b608, + 0xb74021d0, + 0xbd080020, + 0x1f19f014, +/* 0x043f: main */ + 0xf40021d0, + 0x28f40031, + 0x10d7f000, + 0xf43921f4, + 0xe4b1f401, + 0x1bf54001, + 0x87f100d1, + 0x84b6083c, + 0xf094bd06, + 0x89d00499, + 0x0017f100, + 0x0614b60b, + 0xcf4012cf, + 0x13c80011, + 0x7e0bf41f, + 0xf41f23c8, + 0x20f95a0b, + 0xf10212b9, 0xb6083c87, 0x94bd0684, - 0xd00499f0, - 0x17f10089, - 0x14b60b00, - 0x4012cf06, - 0xc80011cf, - 0x0bf41f13, - 0x1f23c87e, - 0xf95a0bf4, - 0x0212b920, - 0x083c87f1, + 0xd00799f0, + 0x32f40089, + 0x0231f401, + 0x07c721f5, + 0x085c87f1, 0xbd0684b6, 0x0799f094, - 0xf40089d0, - 0x31f40132, - 0xfc21f502, - 0x5c87f107, + 0xfc0089d0, + 0x3c87f120, 0x0684b608, 0x99f094bd, - 0x0089d007, - 0x87f120fc, - 0x84b6083c, - 0xf094bd06, - 0x89d00699, - 0x0131f400, - 0x07fc21f5, + 0x0089d006, + 0xf50131f4, + 0xf107c721, + 0xb6085c87, + 0x94bd0684, + 0xd00699f0, + 0x0ef40089, +/* 0x04d5: chsw_prev_no_next */ + 0xb920f931, + 0x32f40212, + 0x0232f401, + 0x07c721f5, + 0x17f120fc, + 0x14b60b00, + 0x0012d006, +/* 0x04f3: chsw_no_prev */ + 0xc8130ef4, + 0x0bf41f23, + 0x0131f40d, + 0xf50232f4, +/* 0x0503: chsw_done */ + 0xf107c721, + 0xb60b0c17, + 0x27f00614, + 0x0012d001, 0x085c87f1, 0xbd0684b6, - 0x0699f094, - 0xf40089d0, -/* 0x050a: chsw_prev_no_next */ - 0x20f9310e, - 0xf40212b9, - 0x32f40132, - 0xfc21f502, - 0xf120fc07, - 0xb60b0017, - 0x12d00614, - 0x130ef400, -/* 0x0528: chsw_no_prev */ - 0xf41f23c8, - 0x31f40d0b, - 0x0232f401, - 0x07fc21f5, -/* 0x0538: chsw_done */ - 0x0b0c17f1, - 0xf00614b6, - 0x12d00127, - 0x5c87f100, + 0x0499f094, + 0xf50089d0, +/* 0x0523: main_not_ctx_switch */ + 0xb0ff200e, + 0x1bf401e4, + 0x02f2b90d, + 0x075b21f5, +/* 0x0533: main_not_ctx_chan */ + 0xb0420ef4, + 0x1bf402e4, + 0x3c87f12e, 0x0684b608, 0x99f094bd, - 0x0089d004, - 0xff200ef5, -/* 0x0558: main_not_ctx_switch */ - 0xf401e4b0, - 0xf2b90d1b, - 0x9021f502, - 0x420ef407, -/* 0x0568: main_not_ctx_chan */ - 0xf402e4b0, - 0x87f12e1b, - 0x84b6083c, + 0x0089d007, + 0xf40132f4, + 0x21f50232, + 0x87f107c7, + 0x84b6085c, 0xf094bd06, 0x89d00799, - 0x0132f400, - 0xf50232f4, - 0xf107fc21, - 0xb6085c87, - 0x94bd0684, - 0xd00799f0, - 0x0ef40089, -/* 0x0599: main_not_ctx_save */ - 0x10ef9411, - 0xf501f5f0, - 0xf502ec21, -/* 0x05a7: main_done */ - 0xf1fed10e, - 0xb6082017, - 0x24bd0614, - 0xd01f29f0, - 0x0ef50012, -/* 0x05ba: ih */ - 0x80f9febe, - 0xf90188fe, - 0xf990f980, - 0xf9b0f9a0, - 0xf9e0f9d0, - 0x800acff0, - 0xf404abc4, - 0xb7f11d0b, - 0xd7f01900, - 0x40becf08, - 0xf400bfcf, - 0xb0b70421, - 0xe7f00400, - 0x00bed001, -/* 0x05f0: ih_no_fifo */ - 0x0100abe4, - 0xf00d0bf4, - 0xe7f108d7, - 0x21f44001, -/* 0x0601: ih_no_ctxsw */ - 0x04b7f104, - 0xffb0bd01, - 0x0bf4b4ab, - 0x1ca7f10d, - 0x06a4b60c, -/* 0x0617: ih_no_other */ - 0xd000abd0, - 0xf0fc400a, - 0xd0fce0fc, - 0xa0fcb0fc, - 0x80fc90fc, - 0xfc0088fe, - 0x0032f480, -/* 0x0632: ctx_4170s */ - 0xe7f101f8, - 0xe3f04170, - 0x10f5f040, - 0xf88d21f4, -/* 0x0641: ctx_4170w */ - 0x70e7f100, + 0x110ef400, +/* 0x0564: main_not_ctx_save */ + 0xf010ef94, + 0x21f501f5, + 0x0ef502ec, +/* 0x0572: main_done */ + 0x17f1fed1, + 0x14b60820, + 0xf024bd06, + 0x12d01f29, + 0xbe0ef500, +/* 0x0585: ih */ + 0xfe80f9fe, + 0x80f90188, + 0xa0f990f9, + 0xd0f9b0f9, + 0xf0f9e0f9, + 0xc4800acf, + 0x0bf404ab, + 0x00b7f11d, + 0x10d7f019, + 0xcf40becf, + 0x21f400bf, + 0x00b0b704, + 0x01e7f004, +/* 0x05bb: ih_no_fifo */ + 0xe400bed0, + 0xf40100ab, + 0xd7f00d0b, + 0x01e7f110, + 0x0421f440, +/* 0x05cc: ih_no_ctxsw */ + 0x0104b7f1, + 0xabffb0bd, + 0x0d0bf4b4, + 0x0c1ca7f1, + 0xd006a4b6, +/* 0x05e2: ih_no_other */ + 0x0ad000ab, + 0xfcf0fc40, + 0xfcd0fce0, + 0xfca0fcb0, + 0xfe80fc90, + 0x80fc0088, + 0xf80032f4, +/* 0x05fd: ctx_4170s */ + 0x70e7f101, 0x40e3f041, - 0xf06821f4, - 0x1bf410f4, -/* 0x0653: ctx_redswitch */ - 0xf100f8f3, - 0xb60614e7, - 0xf7f106e4, - 0xefd00270, - 0x08f7f000, -/* 0x0664: ctx_redswitch_delay */ - 0xf401f2b6, - 0xf7f1fd1b, - 0xefd00770, -/* 0x0673: ctx_86c */ - 0xf100f800, - 0xb6086ce7, - 0xefd006e4, - 0x14e7f100, - 0x40e3f08a, - 0xf18d21f4, - 0xf0a86ce7, - 0x21f441e3, -/* 0x0693: ctx_load */ - 0xf100f88d, + 0xf410f5f0, + 0x00f88d21, +/* 0x060c: ctx_4170w */ + 0x4170e7f1, + 0xf440e3f0, + 0xf4f06821, + 0xf31bf410, +/* 0x061e: ctx_redswitch */ + 0xe7f100f8, + 0xe4b60614, + 0x70f7f106, + 0x00efd002, +/* 0x062f: ctx_redswitch_delay */ + 0xb608f7f0, + 0x1bf401f2, + 0x70f7f1fd, + 0x00efd007, +/* 0x063e: ctx_86c */ + 0xe7f100f8, + 0xe4b6086c, + 0x00efd006, + 0x8a14e7f1, + 0xf440e3f0, + 0xe7f18d21, + 0xe3f0a86c, + 0x8d21f441, +/* 0x065e: ctx_load */ + 0x87f100f8, + 0x84b6083c, + 0xf094bd06, + 0x89d00599, + 0x0ca7f000, + 0xf1c921f4, + 0xb60a2417, + 0x10d00614, + 0x0037f100, + 0x0634b60b, + 0xf14032d0, + 0xb60a0c17, + 0x47f00614, + 0x0012d007, +/* 0x0697: ctx_chan_wait_0 */ + 0xcf4014d0, + 0x44f04014, + 0xfa1bf41f, + 0xfe0032d0, + 0x2af0000b, + 0x0424b61f, + 0xf10220b6, 0xb6083c87, 0x94bd0684, - 0xd00599f0, - 0xa7f00089, - 0xc921f40c, - 0x0a2417f1, - 0xd00614b6, - 0x37f10010, - 0x34b60b00, - 0x4032d006, - 0x0a0c17f1, + 0xd00899f0, + 0x17f10089, + 0x14b60a04, + 0x0012d006, + 0x0a2017f1, 0xf00614b6, - 0x12d00747, - 0x4014d000, -/* 0x06cc: ctx_chan_wait_0 */ - 0xf04014cf, - 0x1bf41f44, - 0x0032d0fa, - 0xf0000bfe, - 0x24b61f2a, - 0x0220b604, - 0x083c87f1, + 0x23f10227, + 0x12d08000, + 0x1017f000, + 0x020027f1, + 0xfa0223f0, + 0x03f80512, + 0x085c87f1, 0xbd0684b6, 0x0899f094, - 0xf10089d0, - 0xb60a0417, - 0x12d00614, - 0x2017f100, - 0x0614b60a, - 0xf10227f0, - 0xd0800023, - 0x17f00012, - 0x0027f110, - 0x0223f002, - 0xf80512fa, - 0x5c87f103, + 0x980089d0, + 0x14b68101, + 0x80029818, + 0xfd0825b6, + 0x01800512, + 0x3c87f116, 0x0684b608, 0x99f094bd, - 0x0089d008, - 0xb6810198, - 0x02981814, - 0x0825b680, - 0x800512fd, - 0x87f11601, - 0x84b6083c, - 0xf094bd06, - 0x89d00999, - 0x0427f100, - 0x0624b60a, - 0xf00021d0, - 0x17f10127, - 0x14b60a20, - 0x0012d006, - 0x010017f1, - 0xfa0613f0, - 0x03f80501, - 0x085c87f1, - 0xbd0684b6, - 0x0999f094, - 0xf10089d0, + 0x0089d009, + 0x0a0427f1, + 0xd00624b6, + 0x27f00021, + 0x2017f101, + 0x0614b60a, + 0xf10012d0, + 0xf0010017, + 0x01fa0613, + 0xf103f805, 0xb6085c87, 0x94bd0684, - 0xd00599f0, - 0x00f80089, -/* 0x0790: ctx_chan */ - 0x069321f5, - 0xf40ca7f0, - 0x17f1c921, - 0x14b60a10, - 0x0527f006, -/* 0x07a7: ctx_chan_wait */ - 0xcf0012d0, - 0x22fd0012, - 0xfa1bf405, -/* 0x07b2: ctx_mmio_exec */ - 0x039800f8, - 0x0427f141, - 0x0624b60a, - 0xbd0023d0, -/* 0x07c1: ctx_mmio_loop */ - 0xff34c434, - 0xf10f1bf4, - 0xf0020057, - 0x35fa0653, -/* 0x07d3: ctx_mmio_pull */ - 0x9803f805, - 0x4f98804e, - 0x8d21f481, - 0xb60830b6, - 0x1bf40112, -/* 0x07e5: ctx_mmio_done */ - 0x160398df, - 0x800023d0, - 0x17f14000, - 0x13f00100, - 0x0601fa06, - 0x00f803f8, -/* 0x07fc: ctx_xfer */ - 0x0c00f7f1, - 0xf006f4b6, - 0xfed004e7, -/* 0x0809: ctx_xfer_idle */ - 0x00fecf80, - 0x2000e4f1, - 0xf4f91bf4, - 0x02f40611, -/* 0x0819: ctx_xfer_pre */ - 0x10f7f00d, - 0x067321f5, -/* 0x0823: ctx_xfer_pre_load */ - 0xf01c11f4, - 0x21f502f7, - 0x21f50632, - 0x21f50641, - 0xf4bd0653, - 0x063221f5, - 0x069321f5, -/* 0x083c: ctx_xfer_exec */ - 0xf1160198, - 0xb6041427, - 0x20d00624, - 0x00e7f100, - 0x41e3f0a5, - 0xf4021fb9, - 0xe0b68d21, - 0x01fcf004, - 0xb6022cf0, - 0xf2fd0124, - 0x8d21f405, - 0x4afc17f1, - 0xf00213f0, - 0x12d00c27, - 0x0721f500, - 0xfc27f102, - 0x0223f047, - 0xf00020d0, - 0x20b6012c, - 0x0012d003, - 0xf001acf0, - 0xb7f006a5, - 0x140c9800, - 0xf0150d98, - 0x21f500e7, - 0xa7f0015c, - 0x0321f508, - 0x0721f501, - 0x2201f402, - 0xf40ca7f0, - 0x17f1c921, - 0x14b60a10, - 0x0527f006, -/* 0x08c3: ctx_xfer_post_save_wait */ - 0xcf0012d0, - 0x22fd0012, - 0xfa1bf405, -/* 0x08cf: ctx_xfer_post */ - 0xf02e02f4, - 0x21f502f7, - 0xf4bd0632, - 0x067321f5, - 0x022621f5, - 0x064121f5, - 0x21f5f4bd, - 0x11f40632, - 0x40019810, - 0xf40511fd, - 0x21f5070b, -/* 0x08fa: ctx_xfer_no_post_mmio */ -/* 0x08fa: ctx_xfer_done */ - 0x00f807b2, + 0xd00999f0, + 0x87f10089, + 0x84b6085c, + 0xf094bd06, + 0x89d00599, +/* 0x075b: ctx_chan */ + 0xf500f800, + 0xf0065e21, + 0x21f40ca7, + 0x1017f1c9, + 0x0614b60a, + 0xd00527f0, +/* 0x0772: ctx_chan_wait */ + 0x12cf0012, + 0x0522fd00, + 0xf8fa1bf4, +/* 0x077d: ctx_mmio_exec */ + 0x41039800, + 0x0a0427f1, + 0xd00624b6, + 0x34bd0023, +/* 0x078c: ctx_mmio_loop */ + 0xf4ff34c4, + 0x57f10f1b, + 0x53f00200, + 0x0535fa06, +/* 0x079e: ctx_mmio_pull */ + 0x4e9803f8, + 0x814f9880, + 0xb68d21f4, + 0x12b60830, + 0xdf1bf401, +/* 0x07b0: ctx_mmio_done */ + 0xd0160398, + 0x00800023, + 0x0017f140, + 0x0613f001, + 0xf80601fa, +/* 0x07c7: ctx_xfer */ + 0xf100f803, + 0xb60c00f7, + 0xe7f006f4, + 0x80fed004, +/* 0x07d4: ctx_xfer_idle */ + 0xf100fecf, + 0xf42000e4, + 0x11f4f91b, + 0x0d02f406, +/* 0x07e4: ctx_xfer_pre */ + 0xf510f7f0, + 0xf4063e21, +/* 0x07ee: ctx_xfer_pre_load */ + 0xf7f01c11, + 0xfd21f502, + 0x0c21f505, + 0x1e21f506, + 0xf5f4bd06, + 0xf505fd21, +/* 0x0807: ctx_xfer_exec */ + 0x98065e21, + 0x27f11601, + 0x24b60414, + 0x0020d006, + 0xa500e7f1, + 0xb941e3f0, + 0x21f4021f, + 0x04e0b68d, + 0xf001fcf0, + 0x24b6022c, + 0x05f2fd01, + 0xf18d21f4, + 0xf04afc17, + 0x27f00213, + 0x0012d00c, + 0x020721f5, + 0x47fc27f1, + 0xd00223f0, + 0x2cf00020, + 0x0320b601, + 0xf00012d0, + 0xa5f001ac, + 0x00b7f006, + 0x98000c98, + 0xe7f0010d, + 0x5c21f500, + 0x08a7f001, + 0x010321f5, + 0x020721f5, + 0xf02201f4, + 0x21f40ca7, + 0x1017f1c9, + 0x0614b60a, + 0xd00527f0, +/* 0x088e: ctx_xfer_post_save_wait */ + 0x12cf0012, + 0x0522fd00, + 0xf4fa1bf4, +/* 0x089a: ctx_xfer_post */ + 0xf7f02e02, + 0xfd21f502, + 0xf5f4bd05, + 0xf5063e21, + 0xf5022621, + 0xbd060c21, + 0xfd21f5f4, + 0x1011f405, + 0xfd400198, + 0x0bf40511, + 0x7d21f507, +/* 0x08c5: ctx_xfer_no_post_mmio */ +/* 0x08c5: ctx_xfer_done */ + 0x0000f807, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, 0x00000000, }; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c index 200a5c54ccad..5f7a040cf0eb 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c @@ -764,10 +764,46 @@ nvc0_graph_init_fw(struct nvc0_graph_priv *priv, u32 fuc_base, } } +static void +nvc0_graph_init_csdata(struct nvc0_graph_priv *priv, + struct nvc0_graph_init *init, + u32 falcon, u32 starstar, u32 base) +{ + u32 addr = init->addr; + u32 next = addr; + u32 star, temp; + + nv_wr32(priv, falcon + 0x01c0, 0x02000000 + starstar); + star = nv_rd32(priv, falcon + 0x01c4); + temp = nv_rd32(priv, falcon + 0x01c4); + if (temp > star) + star = temp; + nv_wr32(priv, falcon + 0x01c0, 0x01000000 + star); + + do { + if (init->addr != next) { + while (addr < next) { + u32 nr = min((int)(next - addr) / 4, 32); + nv_wr32(priv, falcon + 0x01c4, + ((nr - 1) << 26) | (addr - base)); + addr += nr * 4; + star += 4; + } + addr = next = init->addr; + } + next += init->count * 4; + } while ((init++)->count); + + nv_wr32(priv, falcon + 0x01c0, 0x01000004 + starstar); + nv_wr32(priv, falcon + 0x01c4, star); +} + int nvc0_graph_init_ctxctl(struct nvc0_graph_priv *priv) { struct nvc0_graph_oclass *oclass = (void *)nv_object(priv)->oclass; + struct nvc0_grctx_oclass *cclass = (void *)nv_engine(priv)->cclass; + struct nvc0_graph_init *init; u32 r000260; int i; @@ -874,6 +910,10 @@ nvc0_graph_init_ctxctl(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x409184, oclass->fecs.ucode->code.data[i]); } + for (i = 0; (init = cclass->hub[i]); i++) { + nvc0_graph_init_csdata(priv, init, 0x409000, 0x000, 0x000000); + } + /* load GPC microcode */ nv_wr32(priv, 0x41a1c0, 0x01000000); for (i = 0; i < oclass->gpccs.ucode->data.size / 4; i++) @@ -887,8 +927,14 @@ nvc0_graph_init_ctxctl(struct nvc0_graph_priv *priv) } nv_wr32(priv, 0x000260, r000260); + if ((init = cclass->gpc[0])) + nvc0_graph_init_csdata(priv, init, 0x41a000, 0x000, 0x418000); + if ((init = cclass->gpc[2])) + nvc0_graph_init_csdata(priv, init, 0x41a000, 0x004, 0x419800); + if ((init = cclass->gpc[3])) + nvc0_graph_init_csdata(priv, init, 0x41a000, 0x008, 0x41be00); + /* start HUB ucode running, it'll init the GPCs */ - nv_wr32(priv, 0x409800, nv_device(priv)->chipset); nv_wr32(priv, 0x40910c, 0x00000000); nv_wr32(priv, 0x409100, 0x00000002); if (!nv_wait(priv, 0x409800, 0x80000000, 0x80000000)) { -- cgit v1.2.3 From 26410c679865bcfcbe18422ca1eb472cf12ea82d Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Thu, 4 Jul 2013 10:04:30 +1000 Subject: drm/nvd7/gr: initial support Signed-off-by: Maarten Lankhorst Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/Makefile | 2 + drivers/gpu/drm/nouveau/core/engine/device/nvc0.c | 2 +- .../gpu/drm/nouveau/core/engine/graph/ctxnvd7.c | 303 ++++++++ .../gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc | 6 +- .../drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc | 4 +- .../drm/nouveau/core/engine/graph/fuc/gpcnvd7.fuc | 42 + .../nouveau/core/engine/graph/fuc/gpcnvd7.fuc.h | 472 ++++++++++++ .../drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc | 4 +- .../gpu/drm/nouveau/core/engine/graph/fuc/hub.fuc | 10 +- .../drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc | 2 +- .../drm/nouveau/core/engine/graph/fuc/hubnvd7.fuc | 40 + .../nouveau/core/engine/graph/fuc/hubnvd7.fuc.h | 857 +++++++++++++++++++++ .../drm/nouveau/core/engine/graph/fuc/hubnve0.fuc | 2 +- .../drm/nouveau/core/engine/graph/fuc/macros.fuc | 4 + drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c | 20 +- drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h | 6 + drivers/gpu/drm/nouveau/core/engine/graph/nvc1.c | 1 + drivers/gpu/drm/nouveau/core/engine/graph/nvc3.c | 1 + drivers/gpu/drm/nouveau/core/engine/graph/nvc8.c | 1 + drivers/gpu/drm/nouveau/core/engine/graph/nvd7.c | 167 ++++ drivers/gpu/drm/nouveau/core/engine/graph/nvd9.c | 3 +- .../gpu/drm/nouveau/core/include/engine/graph.h | 1 + 22 files changed, 1930 insertions(+), 20 deletions(-) create mode 100644 drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd7.c create mode 100644 drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvd7.fuc create mode 100644 drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvd7.fuc.h create mode 100644 drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvd7.fuc create mode 100644 drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvd7.fuc.h create mode 100644 drivers/gpu/drm/nouveau/core/engine/graph/nvd7.c diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index 4c3d29d67436..d939a1da3203 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile @@ -203,6 +203,7 @@ nouveau-y += core/engine/graph/ctxnvc0.o nouveau-y += core/engine/graph/ctxnvc1.o nouveau-y += core/engine/graph/ctxnvc3.o nouveau-y += core/engine/graph/ctxnvc8.o +nouveau-y += core/engine/graph/ctxnvd7.o nouveau-y += core/engine/graph/ctxnvd9.o nouveau-y += core/engine/graph/ctxnve4.o nouveau-y += core/engine/graph/ctxnvf0.o @@ -220,6 +221,7 @@ nouveau-y += core/engine/graph/nvc0.o nouveau-y += core/engine/graph/nvc1.o nouveau-y += core/engine/graph/nvc3.o nouveau-y += core/engine/graph/nvc8.o +nouveau-y += core/engine/graph/nvd7.o nouveau-y += core/engine/graph/nvd9.o nouveau-y += core/engine/graph/nve4.o nouveau-y += core/engine/graph/nvf0.o diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c index 6637eecd831e..73d0db8e1d2c 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c @@ -304,7 +304,7 @@ nvc0_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvd0_dmaeng_oclass; device->oclass[NVDEV_ENGINE_FIFO ] = &nvc0_fifo_oclass; device->oclass[NVDEV_ENGINE_SW ] = &nvc0_software_oclass; - device->oclass[NVDEV_ENGINE_GR ] = nvd9_graph_oclass; + device->oclass[NVDEV_ENGINE_GR ] = nvd7_graph_oclass; device->oclass[NVDEV_ENGINE_VP ] = &nvc0_vp_oclass; device->oclass[NVDEV_ENGINE_BSP ] = &nvc0_bsp_oclass; device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd7.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd7.c new file mode 100644 index 000000000000..25d5676eec47 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd7.c @@ -0,0 +1,303 @@ +/* + * Copyright 2013 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 "nvc0.h" + +struct nvc0_graph_init +nvd7_grctx_init_unk40xx[] = { + { 0x404004, 10, 0x04, 0x00000000 }, + { 0x404044, 1, 0x04, 0x00000000 }, + { 0x404094, 1, 0x04, 0x00000000 }, + { 0x404098, 12, 0x04, 0x00000000 }, + { 0x4040c8, 1, 0x04, 0xf0000087 }, + { 0x4040d0, 6, 0x04, 0x00000000 }, + { 0x4040e8, 1, 0x04, 0x00001000 }, + { 0x4040f8, 1, 0x04, 0x00000000 }, + { 0x404130, 1, 0x04, 0x00000000 }, + { 0x404134, 1, 0x04, 0x00000000 }, + { 0x404138, 1, 0x04, 0x20000040 }, + { 0x404150, 1, 0x04, 0x0000002e }, + { 0x404154, 1, 0x04, 0x00000400 }, + { 0x404158, 1, 0x04, 0x00000200 }, + { 0x404164, 1, 0x04, 0x00000055 }, + { 0x404168, 1, 0x04, 0x00000000 }, + { 0x404178, 2, 0x04, 0x00000000 }, + { 0x404200, 8, 0x04, 0x00000000 }, + {} +}; + +static struct nvc0_graph_init +nvd7_grctx_init_unk58xx[] = { + { 0x405800, 1, 0x04, 0x0f8000bf }, + { 0x405830, 1, 0x04, 0x02180324 }, + { 0x405834, 1, 0x04, 0x08000000 }, + { 0x405838, 1, 0x04, 0x00000000 }, + { 0x405854, 1, 0x04, 0x00000000 }, + { 0x405870, 4, 0x04, 0x00000001 }, + { 0x405a00, 2, 0x04, 0x00000000 }, + { 0x405a18, 1, 0x04, 0x00000000 }, + {} +}; + +static struct nvc0_graph_init +nvd7_grctx_init_unk64xx[] = { + { 0x4064a8, 1, 0x04, 0x00000000 }, + { 0x4064ac, 1, 0x04, 0x00003fff }, + { 0x4064b4, 3, 0x04, 0x00000000 }, + { 0x4064c0, 1, 0x04, 0x801a0078 }, + { 0x4064c4, 1, 0x04, 0x00c9ffff }, + { 0x4064d0, 8, 0x04, 0x00000000 }, + {} +}; + +static struct nvc0_graph_init +nvd7_grctx_init_gpc_0[] = { + { 0x418380, 1, 0x04, 0x00000016 }, + { 0x418400, 1, 0x04, 0x38004e00 }, + { 0x418404, 1, 0x04, 0x71e0ffff }, + { 0x41840c, 1, 0x04, 0x00001008 }, + { 0x418410, 1, 0x04, 0x0fff0fff }, + { 0x418414, 1, 0x04, 0x02200fff }, + { 0x418450, 6, 0x04, 0x00000000 }, + { 0x418468, 1, 0x04, 0x00000001 }, + { 0x41846c, 2, 0x04, 0x00000000 }, + { 0x418600, 1, 0x04, 0x0000001f }, + { 0x418684, 1, 0x04, 0x0000000f }, + { 0x418700, 1, 0x04, 0x00000002 }, + { 0x418704, 1, 0x04, 0x00000080 }, + { 0x418708, 3, 0x04, 0x00000000 }, + { 0x418800, 1, 0x04, 0x7006860a }, + { 0x418808, 3, 0x04, 0x00000000 }, + { 0x418828, 1, 0x04, 0x00008442 }, + { 0x418830, 1, 0x04, 0x10000001 }, + { 0x4188d8, 1, 0x04, 0x00000008 }, + { 0x4188e0, 1, 0x04, 0x01000000 }, + { 0x4188e8, 5, 0x04, 0x00000000 }, + { 0x4188fc, 1, 0x04, 0x20100018 }, + { 0x41891c, 1, 0x04, 0x00ff00ff }, + { 0x418924, 1, 0x04, 0x00000000 }, + { 0x418928, 1, 0x04, 0x00ffff00 }, + { 0x41892c, 1, 0x04, 0x0000ff00 }, + { 0x418b00, 1, 0x04, 0x00000006 }, + { 0x418b08, 1, 0x04, 0x0a418820 }, + { 0x418b0c, 1, 0x04, 0x062080e6 }, + { 0x418b10, 1, 0x04, 0x020398a4 }, + { 0x418b14, 1, 0x04, 0x0e629062 }, + { 0x418b18, 1, 0x04, 0x0a418820 }, + { 0x418b1c, 1, 0x04, 0x000000e6 }, + { 0x418bb8, 1, 0x04, 0x00000103 }, + { 0x418c08, 1, 0x04, 0x00000001 }, + { 0x418c10, 8, 0x04, 0x00000000 }, + { 0x418c6c, 1, 0x04, 0x00000001 }, + { 0x418c80, 1, 0x04, 0x20200004 }, + { 0x418c8c, 1, 0x04, 0x00000001 }, + { 0x419000, 1, 0x04, 0x00000780 }, + { 0x419004, 2, 0x04, 0x00000000 }, + { 0x419014, 1, 0x04, 0x00000004 }, + {} +}; + +static struct nvc0_graph_init +nvd7_grctx_init_tpc[] = { + { 0x419848, 1, 0x04, 0x00000000 }, + { 0x419864, 1, 0x04, 0x00000129 }, + { 0x419888, 1, 0x04, 0x00000000 }, + { 0x419a00, 1, 0x04, 0x000001f0 }, + { 0x419a04, 1, 0x04, 0x00000001 }, + { 0x419a08, 1, 0x04, 0x00000023 }, + { 0x419a0c, 1, 0x04, 0x00020000 }, + { 0x419a10, 1, 0x04, 0x00000000 }, + { 0x419a14, 1, 0x04, 0x00000200 }, + { 0x419a1c, 1, 0x04, 0x00008000 }, + { 0x419a20, 1, 0x04, 0x00000800 }, + { 0x419ac4, 1, 0x04, 0x0017f440 }, + { 0x419c00, 1, 0x04, 0x0000000a }, + { 0x419c04, 1, 0x04, 0x00000006 }, + { 0x419c08, 1, 0x04, 0x00000002 }, + { 0x419c20, 1, 0x04, 0x00000000 }, + { 0x419c24, 1, 0x04, 0x00084210 }, + { 0x419c28, 1, 0x04, 0x3efbefbe }, + { 0x419cb0, 1, 0x04, 0x00020048 }, + { 0x419ce8, 1, 0x04, 0x00000000 }, + { 0x419cf4, 1, 0x04, 0x00000183 }, + { 0x419e04, 3, 0x04, 0x00000000 }, + { 0x419e10, 1, 0x04, 0x00000002 }, + { 0x419e44, 1, 0x04, 0x001beff2 }, + { 0x419e48, 1, 0x04, 0x00000000 }, + { 0x419e4c, 1, 0x04, 0x0000000f }, + { 0x419e50, 17, 0x04, 0x00000000 }, + { 0x419e98, 1, 0x04, 0x00000000 }, + { 0x419ee0, 1, 0x04, 0x00010110 }, + { 0x419f30, 11, 0x04, 0x00000000 }, + {} +}; + +static struct nvc0_graph_init +nvd7_grctx_init_unk[] = { + { 0x41be24, 1, 0x04, 0x00000002 }, + { 0x41bec0, 1, 0x04, 0x12180000 }, + { 0x41bec4, 1, 0x04, 0x00003fff }, + { 0x41bee4, 1, 0x04, 0x03240218 }, + { 0x41bf00, 1, 0x04, 0x0a418820 }, + { 0x41bf04, 1, 0x04, 0x062080e6 }, + { 0x41bf08, 1, 0x04, 0x020398a4 }, + { 0x41bf0c, 1, 0x04, 0x0e629062 }, + { 0x41bf10, 1, 0x04, 0x0a418820 }, + { 0x41bf14, 1, 0x04, 0x000000e6 }, + { 0x41bfd0, 1, 0x04, 0x00900103 }, + { 0x41bfe0, 1, 0x04, 0x00400001 }, + { 0x41bfe4, 1, 0x04, 0x00000000 }, + {} +}; + +static void +nvd7_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info) +{ + u32 magic[GPC_MAX][2]; + u32 offset; + int gpc; + + mmio_data(0x003000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS); + mmio_data(0x008000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS); + mmio_data(0x060000, 0x1000, NV_MEM_ACCESS_RW); + mmio_list(0x40800c, 0x00000000, 8, 1); + mmio_list(0x408010, 0x80000000, 0, 0); + mmio_list(0x419004, 0x00000000, 8, 1); + mmio_list(0x419008, 0x00000000, 0, 0); + mmio_list(0x408004, 0x00000000, 8, 0); + mmio_list(0x408008, 0x80000018, 0, 0); + mmio_list(0x418808, 0x00000000, 8, 0); + mmio_list(0x41880c, 0x80000018, 0, 0); + mmio_list(0x418810, 0x80000000, 12, 2); + mmio_list(0x419848, 0x10000000, 12, 2); + + mmio_list(0x405830, 0x02180324, 0, 0); + mmio_list(0x4064c4, 0x00c9ffff, 0, 0); + + for (gpc = 0, offset = 0; gpc < priv->gpc_nr; gpc++) { + u16 magic0 = 0x0218 * priv->tpc_nr[gpc]; + u16 magic1 = 0x0324 * priv->tpc_nr[gpc]; + magic[gpc][0] = 0x10000000 | (magic0 << 16) | offset; + magic[gpc][1] = 0x00000000 | (magic1 << 16); + offset += 0x0324 * priv->tpc_nr[gpc]; + } + + for (gpc = 0; gpc < priv->gpc_nr; gpc++) { + mmio_list(GPC_UNIT(gpc, 0x30c0), magic[gpc][0], 0, 0); + mmio_list(GPC_UNIT(gpc, 0x30e4), magic[gpc][1] | offset, 0, 0); + offset += 0x07ff * priv->tpc_nr[gpc]; + } + mmio_list(0x17e91c, 0x03060609, 0, 0); /* different from kepler */ +} + +void +nvd7_grctx_generate_main(struct nvc0_graph_priv *priv, struct nvc0_grctx *info) +{ + struct nvc0_grctx_oclass *oclass = (void *)nv_engine(priv)->cclass; + int i; + + nv_mask(priv, 0x000260, 0x00000001, 0x00000000); + + for (i = 0; oclass->hub[i]; i++) + nvc0_graph_mmio(priv, oclass->hub[i]); + for (i = 0; oclass->gpc[i]; i++) + nvc0_graph_mmio(priv, oclass->gpc[i]); + + nv_wr32(priv, 0x404154, 0x00000000); + + oclass->mods(priv, info); + + nv_wr32(priv, 0x418c6c, 0x1); + nv_wr32(priv, 0x41980c, 0x10); + nv_wr32(priv, 0x41be08, 0x4); + nv_wr32(priv, 0x4064c0, 0x801a0078); + nv_wr32(priv, 0x405800, 0xf8000bf); + nv_wr32(priv, 0x419c00, 0xa); + + nvc0_grctx_generate_tpcid(priv); + nvc0_grctx_generate_r406028(priv); + + nv_wr32(priv, 0x40602c, 0x00000000); + nv_wr32(priv, 0x405874, 0x00000000); + nv_wr32(priv, 0x406030, 0x00000000); + nv_wr32(priv, 0x405878, 0x00000000); + nv_wr32(priv, 0x406034, 0x00000000); + nv_wr32(priv, 0x40587c, 0x00000000); + + nvc0_grctx_generate_r4060a8(priv); + nve4_grctx_generate_r418bb8(priv); + nvc0_grctx_generate_r406800(priv); + + for (i = 0; i < 8; i++) + nv_wr32(priv, 0x4064d0 + (i * 0x04), 0x00000000); + + nvc0_graph_icmd(priv, oclass->icmd); + nv_wr32(priv, 0x404154, 0x00000400); + nvc0_graph_mthd(priv, oclass->mthd); + nv_mask(priv, 0x000260, 0x00000001, 0x00000001); +} + + +static struct nvc0_graph_init * +nvd7_grctx_init_hub[] = { + nvc0_grctx_init_base, + nvd7_grctx_init_unk40xx, + nvc0_grctx_init_unk44xx, + nvc0_grctx_init_unk46xx, + nvc0_grctx_init_unk47xx, + nvd7_grctx_init_unk58xx, + nvc0_grctx_init_unk60xx, + nvd7_grctx_init_unk64xx, + nvc0_grctx_init_unk78xx, + nvc0_grctx_init_unk80xx, + nvd9_grctx_init_rop, +}; + +struct nvc0_graph_init * +nvd7_grctx_init_gpc[] = { + nvd7_grctx_init_gpc_0, + nvc0_grctx_init_gpc_1, + nvd7_grctx_init_tpc, + nvd7_grctx_init_unk, + NULL +}; + +struct nouveau_oclass * +nvd7_grctx_oclass = &(struct nvc0_grctx_oclass) { + .base.handle = NV_ENGCTX(GR, 0xd7), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = nvc0_graph_context_ctor, + .dtor = nvc0_graph_context_dtor, + .init = _nouveau_graph_context_init, + .fini = _nouveau_graph_context_fini, + .rd32 = _nouveau_graph_context_rd32, + .wr32 = _nouveau_graph_context_wr32, + }, + .main = nvd7_grctx_generate_main, + .mods = nvd7_grctx_generate_mods, + .hub = nvd7_grctx_init_hub, + .gpc = nvd7_grctx_init_gpc, + .icmd = nvd9_grctx_init_icmd, + .mthd = nvd9_grctx_init_mthd, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc index 97f775b02451..32832720e420 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc @@ -41,7 +41,7 @@ gpc_id: .b32 0 tpc_count: .b32 0 tpc_mask: .b32 0 -#ifdef NVGK +#if NV_PGRAPH_GPCX_UNK__SIZE > 0 unk_count: .b32 1 unk_mask: .b32 1 #endif @@ -145,7 +145,7 @@ init: add b32 $r2 $r14 add b32 $r3 $r14 -#ifdef NVGK +#if NV_PGRAPH_GPCX_UNK__SIZE > 0 // calculate per-UNK mmio context size ld b32 $r14 D[$r0 + #unk_mmio_list_head] ld b32 $r15 D[$r0 + #unk_mmio_list_tail] @@ -342,7 +342,7 @@ ctx_xfer: mov $r14 0x800 // stride = 0x800 call #mmctx_xfer -#ifdef NVGK +#if NV_PGRAPH_GPCX_UNK__SIZE > 0 // per-UNK mmio context xbit $r10 $flags $p1 // direction or $r10 4 // last diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc index dcacfb5f6ff7..5ae06a2d64c9 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc @@ -22,7 +22,9 @@ * Authors: Ben Skeggs */ -#define NVGF +#define NV_PGRAPH_GPCX_UNK__SIZE 0x00000000 + +#define CHIPSET GF100 #include "macros.fuc" .section #nvc0_grgpc_data diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvd7.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvd7.fuc new file mode 100644 index 000000000000..c2f754edbd7d --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvd7.fuc @@ -0,0 +1,42 @@ +/* + * Copyright 2013 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 + */ + +#define NV_PGRAPH_GPCX_UNK__SIZE 0x00000001 + +#define CHIPSET GF117 +#include "macros.fuc" + +.section #nvd7_grgpc_data +#define INCLUDE_DATA +#include "com.fuc" +#include "gpc.fuc" +#undef INCLUDE_DATA + +.section #nvd7_grgpc_code +#define INCLUDE_CODE +bra #init +#include "com.fuc" +#include "gpc.fuc" +.align 256 +#undef INCLUDE_CODE diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvd7.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvd7.fuc.h new file mode 100644 index 000000000000..95d13a1dbb0a --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvd7.fuc.h @@ -0,0 +1,472 @@ +uint32_t nvd7_grgpc_data[] = { +/* 0x0000: gpc_mmio_list_head */ + 0x0000006c, +/* 0x0004: gpc_mmio_list_tail */ +/* 0x0004: tpc_mmio_list_head */ + 0x0000006c, +/* 0x0008: tpc_mmio_list_tail */ +/* 0x0008: unk_mmio_list_head */ + 0x0000006c, +/* 0x000c: unk_mmio_list_tail */ + 0x0000006c, +/* 0x0010: gpc_id */ + 0x00000000, +/* 0x0014: tpc_count */ + 0x00000000, +/* 0x0018: tpc_mask */ + 0x00000000, +/* 0x001c: unk_count */ + 0x00000001, +/* 0x0020: unk_mask */ + 0x00000001, +/* 0x0024: cmd_queue */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +uint32_t nvd7_grgpc_code[] = { + 0x03060ef5, +/* 0x0004: queue_put */ + 0x9800d898, + 0x86f001d9, + 0x0489b808, + 0xf00c1bf4, + 0x21f502f7, + 0x00f802ec, +/* 0x001c: queue_put_next */ + 0xb60798c4, + 0x8dbb0384, + 0x0880b600, + 0x80008e80, + 0x90b6018f, + 0x0f94f001, + 0xf801d980, +/* 0x0039: queue_get */ + 0x0131f400, + 0x9800d898, + 0x89b801d9, + 0x210bf404, + 0xb60789c4, + 0x9dbb0394, + 0x0890b600, + 0x98009e98, + 0x80b6019f, + 0x0f84f001, + 0xf400d880, +/* 0x0066: queue_get_done */ + 0x00f80132, +/* 0x0068: nv_rd32 */ + 0x0728b7f1, + 0xb906b4b6, + 0xc9f002ec, + 0x00bcd01f, +/* 0x0078: nv_rd32_wait */ + 0xc800bccf, + 0x1bf41fcc, + 0x06a7f0fa, + 0x010321f5, + 0xf840bfcf, +/* 0x008d: nv_wr32 */ + 0x28b7f100, + 0x06b4b607, + 0xb980bfd0, + 0xc9f002ec, + 0x1ec9f01f, +/* 0x00a3: nv_wr32_wait */ + 0xcf00bcd0, + 0xccc800bc, + 0xfa1bf41f, +/* 0x00ae: watchdog_reset */ + 0x87f100f8, + 0x84b60430, + 0x1ff9f006, + 0xf8008fd0, +/* 0x00bd: watchdog_clear */ + 0x3087f100, + 0x0684b604, + 0xf80080d0, +/* 0x00c9: wait_donez */ + 0x3c87f100, + 0x0684b608, + 0x99f094bd, + 0x0089d000, + 0x081887f1, + 0xd00684b6, +/* 0x00e2: wait_donez_ne */ + 0x87f1008a, + 0x84b60400, + 0x0088cf06, + 0xf4888aff, + 0x87f1f31b, + 0x84b6085c, + 0xf094bd06, + 0x89d00099, +/* 0x0103: wait_doneo */ + 0xf100f800, + 0xb6083c87, + 0x94bd0684, + 0xd00099f0, + 0x87f10089, + 0x84b60818, + 0x008ad006, +/* 0x011c: wait_doneo_e */ + 0x040087f1, + 0xcf0684b6, + 0x8aff0088, + 0xf30bf488, + 0x085c87f1, + 0xbd0684b6, + 0x0099f094, + 0xf80089d0, +/* 0x013d: mmctx_size */ +/* 0x013f: nv_mmctx_size_loop */ + 0x9894bd00, + 0x85b600e8, + 0x0180b61a, + 0xbb0284b6, + 0xe0b60098, + 0x04efb804, + 0xb9eb1bf4, + 0x00f8029f, +/* 0x015c: mmctx_xfer */ + 0x083c87f1, + 0xbd0684b6, + 0x0199f094, + 0xf10089d0, + 0xb6071087, + 0x94bd0684, + 0xf405bbfd, + 0x8bd0090b, + 0x0099f000, +/* 0x0180: mmctx_base_disabled */ + 0xf405eefd, + 0x8ed00c0b, + 0xc08fd080, +/* 0x018f: mmctx_multi_disabled */ + 0xb70199f0, + 0xc8010080, + 0xb4b600ab, + 0x0cb9f010, + 0xb601aec8, + 0xbefd11e4, + 0x008bd005, +/* 0x01a8: mmctx_exec_loop */ +/* 0x01a8: mmctx_wait_free */ + 0xf0008ecf, + 0x0bf41fe4, + 0x00ce98fa, + 0xd005e9fd, + 0xc0b6c08e, + 0x04cdb804, + 0xc8e81bf4, + 0x1bf402ab, +/* 0x01c9: mmctx_fini_wait */ + 0x008bcf18, + 0xb01fb4f0, + 0x1bf410b4, + 0x02a7f0f7, + 0xf4c921f4, +/* 0x01de: mmctx_stop */ + 0xabc81b0e, + 0x10b4b600, + 0xf00cb9f0, + 0x8bd012b9, +/* 0x01ed: mmctx_stop_wait */ + 0x008bcf00, + 0xf412bbc8, +/* 0x01f6: mmctx_done */ + 0x87f1fa1b, + 0x84b6085c, + 0xf094bd06, + 0x89d00199, +/* 0x0207: strand_wait */ + 0xf900f800, + 0x02a7f0a0, + 0xfcc921f4, +/* 0x0213: strand_pre */ + 0xf100f8a0, + 0xf04afc87, + 0x97f00283, + 0x0089d00c, + 0x020721f5, +/* 0x0226: strand_post */ + 0x87f100f8, + 0x83f04afc, + 0x0d97f002, + 0xf50089d0, + 0xf8020721, +/* 0x0239: strand_set */ + 0xfca7f100, + 0x02a3f04f, + 0x0500aba2, + 0xd00fc7f0, + 0xc7f000ac, + 0x00bcd00b, + 0x020721f5, + 0xf000aed0, + 0xbcd00ac7, + 0x0721f500, +/* 0x0263: strand_ctx_init */ + 0xf100f802, + 0xb6083c87, + 0x94bd0684, + 0xd00399f0, + 0x21f50089, + 0xe7f00213, + 0x3921f503, + 0xfca7f102, + 0x02a3f046, + 0x0400aba0, + 0xf040a0d0, + 0xbcd001c7, + 0x0721f500, + 0x010c9202, + 0xf000acd0, + 0xbcd002c7, + 0x0721f500, + 0x2621f502, + 0x8087f102, + 0x0684b608, + 0xb70089cf, + 0x95220080, +/* 0x02ba: ctx_init_strand_loop */ + 0x8ed008fe, + 0x408ed000, + 0xb6808acf, + 0xa0b606a5, + 0x00eabb01, + 0xb60480b6, + 0x1bf40192, + 0x08e4b6e8, + 0xf1f2efbc, + 0xb6085c87, + 0x94bd0684, + 0xd00399f0, + 0x00f80089, +/* 0x02ec: error */ + 0xe7f1e0f9, + 0xe3f09814, + 0x8d21f440, + 0x041ce0b7, + 0xf401f7f0, + 0xe0fc8d21, +/* 0x0306: init */ + 0x04bd00f8, + 0xf10004fe, + 0xf0120017, + 0x12d00227, + 0x2317f100, + 0x0010fe04, + 0x040017f1, + 0xf0c010d0, + 0x12d00427, + 0x1031f400, + 0x060817f1, + 0xcf0614b6, + 0x37f00012, + 0x1f24f001, + 0xb60432bb, + 0x02800132, + 0x06038005, + 0x040010b7, + 0x800012cf, + 0x27f10402, + 0x24b60800, + 0x4022cf06, + 0x47f134bd, + 0x44b60700, + 0x08259506, + 0xd00045d0, + 0x0e984045, + 0x010f9800, + 0x013d21f5, + 0xbb002fbb, + 0x0e98003f, + 0x020f9801, + 0x013d21f5, + 0xfd050e98, + 0x2ebb00ef, + 0x003ebb00, + 0x98020e98, + 0x21f5030f, + 0x0e98013d, + 0x00effd07, + 0xbb002ebb, + 0x40b7003e, + 0x35b61300, + 0x0043d002, + 0xb60825b6, + 0x20b60635, + 0x0130b601, + 0xb60824b6, + 0x2fb90834, + 0x6321f502, + 0x003fbb02, + 0x080017f1, + 0xd00614b6, + 0x10b74013, + 0x24bd0800, + 0xd01f29f0, +/* 0x03e6: main */ + 0x31f40012, + 0x0028f400, + 0xf424d7f0, + 0x01f43921, + 0x04e4b0f4, + 0xfe1e18f4, + 0x27f00181, + 0xfd20bd06, + 0xe4b60412, + 0x051efd01, + 0xf50018fe, + 0xf404a821, +/* 0x0416: main_not_ctx_xfer */ + 0xef94d30e, + 0x01f5f010, + 0x02ec21f5, +/* 0x0423: ih */ + 0xf9c60ef4, + 0x0188fe80, + 0x90f980f9, + 0xb0f9a0f9, + 0xe0f9d0f9, + 0x0acff0f9, + 0x04abc480, + 0xf11d0bf4, + 0xf01900b7, + 0xbecf24d7, + 0x00bfcf40, + 0xb70421f4, + 0xf00400b0, + 0xbed001e7, +/* 0x0459: ih_no_fifo */ + 0x400ad000, + 0xe0fcf0fc, + 0xb0fcd0fc, + 0x90fca0fc, + 0x88fe80fc, + 0xf480fc00, + 0x01f80032, +/* 0x0474: hub_barrier_done */ + 0x9801f7f0, + 0xfebb040e, + 0x18e7f104, + 0x40e3f094, + 0xf88d21f4, +/* 0x0489: ctx_redswitch */ + 0x14e7f100, + 0x06e4b606, + 0xd020f7f0, + 0xf7f000ef, +/* 0x0499: ctx_redswitch_delay */ + 0x01f2b608, + 0xf1fd1bf4, + 0xd00a20f7, + 0x00f800ef, +/* 0x04a8: ctx_xfer */ + 0x0a0417f1, + 0xd00614b6, + 0x11f4001f, + 0x8921f507, +/* 0x04b9: ctx_xfer_not_load */ + 0xfc17f104, + 0x0213f04a, + 0xd00c27f0, + 0x21f50012, + 0x27f10207, + 0x23f047fc, + 0x0020d002, + 0xb6012cf0, + 0x12d00320, + 0x01acf000, + 0xf002a5f0, + 0xb3f000b7, + 0x040c9850, + 0xbb0fc4b6, + 0x0c9800bc, + 0x010d9800, + 0xf500e7f0, + 0xf0015c21, + 0xb7f101ac, + 0xb3f04000, + 0x040c9850, + 0xbb0fc4b6, + 0x0c9800bc, + 0x020d9801, + 0xf1060f98, + 0xf50800e7, + 0xf0015c21, + 0xa5f001ac, + 0x00b7f104, + 0x50b3f030, + 0xb6040c98, + 0xbcbb0fc4, + 0x020c9800, + 0x98030d98, + 0xe7f1080f, + 0x21f50200, + 0x21f5015c, + 0x01f40207, + 0x1412f406, +/* 0x0554: ctx_xfer_post */ + 0x4afc17f1, + 0xf00213f0, + 0x12d00d27, + 0x0721f500, +/* 0x0565: ctx_xfer_done */ + 0x7421f502, + 0x0000f804, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc index b2169be2d82f..6b906cd2a31f 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc @@ -22,7 +22,9 @@ * Authors: Ben Skeggs */ -#define NVGK +#define NV_PGRAPH_GPCX_UNK__SIZE 0x00000001 + +#define CHIPSET GK100 #include "macros.fuc" .section #nve0_grgpc_data diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hub.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hub.fuc index 2592a825e1a5..6b81e7bae13f 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hub.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hub.fuc @@ -377,7 +377,7 @@ ih: bclr $flags $p0 iret -#ifdef NVGF +#if CHIPSET < GK100 // Not real sure, but, MEM_CMD 7 will hang forever if this isn't done ctx_4160s: mov $r14 0x4160 @@ -541,7 +541,7 @@ ctx_load: // In: $r2 channel address // ctx_chan: -#ifdef NVGF +#if CHIPSET < GK100 call #ctx_4160s #endif call #ctx_load @@ -555,7 +555,7 @@ ctx_chan: iord $r2 I[$r1 + 0x000] or $r2 $r2 bra ne #ctx_chan_wait -#ifdef NVGF +#if CHIPSET < GK100 call #ctx_4160c #endif ret @@ -634,7 +634,7 @@ ctx_xfer: ctx_xfer_pre: mov $r15 0x10 call #ctx_86c -#ifdef NVGF +#if CHIPSET < GK100 call #ctx_4160s #endif bra not $p1 #ctx_xfer_exec @@ -725,7 +725,7 @@ ctx_xfer: call #ctx_mmio_exec ctx_xfer_no_post_mmio: -#ifdef NVGF +#if CHIPSET < GK100 call #ctx_4160c #endif diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc index 164d5b953c68..3ff52badf932 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc @@ -22,7 +22,7 @@ * Authors: Ben Skeggs */ -#define NVGF +#define CHIPSET GF100 #include "macros.fuc" .section #nvc0_grhub_data diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvd7.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvd7.fuc new file mode 100644 index 000000000000..afbe03ac9077 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvd7.fuc @@ -0,0 +1,40 @@ +/* + * Copyright 2013 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 + */ + +#define CHIPSET GF117 +#include "macros.fuc" + +.section #nvd7_grhub_data +#define INCLUDE_DATA +#include "com.fuc" +#include "hub.fuc" +#undef INCLUDE_DATA + +.section #nvd7_grhub_code +#define INCLUDE_CODE +bra #init +#include "com.fuc" +#include "hub.fuc" +.align 256 +#undef INCLUDE_CODE diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvd7.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvd7.fuc.h new file mode 100644 index 000000000000..141d6a8ffe5d --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvd7.fuc.h @@ -0,0 +1,857 @@ +uint32_t nvd7_grhub_data[] = { +/* 0x0000: hub_mmio_list_head */ + 0x00000300, +/* 0x0004: hub_mmio_list_tail */ + 0x00000304, +/* 0x0008: gpc_count */ + 0x00000000, +/* 0x000c: rop_count */ + 0x00000000, +/* 0x0010: cmd_queue */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0058: ctx_current */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0100: chan_data */ +/* 0x0100: chan_mmio_count */ + 0x00000000, +/* 0x0104: chan_mmio_address */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0200: xfer_data */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0300: hub_mmio_list_base */ + 0x0417e91c, +}; + +uint32_t nvd7_grhub_code[] = { + 0x03090ef5, +/* 0x0004: queue_put */ + 0x9800d898, + 0x86f001d9, + 0x0489b808, + 0xf00c1bf4, + 0x21f502f7, + 0x00f802ec, +/* 0x001c: queue_put_next */ + 0xb60798c4, + 0x8dbb0384, + 0x0880b600, + 0x80008e80, + 0x90b6018f, + 0x0f94f001, + 0xf801d980, +/* 0x0039: queue_get */ + 0x0131f400, + 0x9800d898, + 0x89b801d9, + 0x210bf404, + 0xb60789c4, + 0x9dbb0394, + 0x0890b600, + 0x98009e98, + 0x80b6019f, + 0x0f84f001, + 0xf400d880, +/* 0x0066: queue_get_done */ + 0x00f80132, +/* 0x0068: nv_rd32 */ + 0x0728b7f1, + 0xb906b4b6, + 0xc9f002ec, + 0x00bcd01f, +/* 0x0078: nv_rd32_wait */ + 0xc800bccf, + 0x1bf41fcc, + 0x06a7f0fa, + 0x010321f5, + 0xf840bfcf, +/* 0x008d: nv_wr32 */ + 0x28b7f100, + 0x06b4b607, + 0xb980bfd0, + 0xc9f002ec, + 0x1ec9f01f, +/* 0x00a3: nv_wr32_wait */ + 0xcf00bcd0, + 0xccc800bc, + 0xfa1bf41f, +/* 0x00ae: watchdog_reset */ + 0x87f100f8, + 0x84b60430, + 0x1ff9f006, + 0xf8008fd0, +/* 0x00bd: watchdog_clear */ + 0x3087f100, + 0x0684b604, + 0xf80080d0, +/* 0x00c9: wait_donez */ + 0x3c87f100, + 0x0684b608, + 0x99f094bd, + 0x0089d000, + 0x081887f1, + 0xd00684b6, +/* 0x00e2: wait_donez_ne */ + 0x87f1008a, + 0x84b60400, + 0x0088cf06, + 0xf4888aff, + 0x87f1f31b, + 0x84b6085c, + 0xf094bd06, + 0x89d00099, +/* 0x0103: wait_doneo */ + 0xf100f800, + 0xb6083c87, + 0x94bd0684, + 0xd00099f0, + 0x87f10089, + 0x84b60818, + 0x008ad006, +/* 0x011c: wait_doneo_e */ + 0x040087f1, + 0xcf0684b6, + 0x8aff0088, + 0xf30bf488, + 0x085c87f1, + 0xbd0684b6, + 0x0099f094, + 0xf80089d0, +/* 0x013d: mmctx_size */ +/* 0x013f: nv_mmctx_size_loop */ + 0x9894bd00, + 0x85b600e8, + 0x0180b61a, + 0xbb0284b6, + 0xe0b60098, + 0x04efb804, + 0xb9eb1bf4, + 0x00f8029f, +/* 0x015c: mmctx_xfer */ + 0x083c87f1, + 0xbd0684b6, + 0x0199f094, + 0xf10089d0, + 0xb6071087, + 0x94bd0684, + 0xf405bbfd, + 0x8bd0090b, + 0x0099f000, +/* 0x0180: mmctx_base_disabled */ + 0xf405eefd, + 0x8ed00c0b, + 0xc08fd080, +/* 0x018f: mmctx_multi_disabled */ + 0xb70199f0, + 0xc8010080, + 0xb4b600ab, + 0x0cb9f010, + 0xb601aec8, + 0xbefd11e4, + 0x008bd005, +/* 0x01a8: mmctx_exec_loop */ +/* 0x01a8: mmctx_wait_free */ + 0xf0008ecf, + 0x0bf41fe4, + 0x00ce98fa, + 0xd005e9fd, + 0xc0b6c08e, + 0x04cdb804, + 0xc8e81bf4, + 0x1bf402ab, +/* 0x01c9: mmctx_fini_wait */ + 0x008bcf18, + 0xb01fb4f0, + 0x1bf410b4, + 0x02a7f0f7, + 0xf4c921f4, +/* 0x01de: mmctx_stop */ + 0xabc81b0e, + 0x10b4b600, + 0xf00cb9f0, + 0x8bd012b9, +/* 0x01ed: mmctx_stop_wait */ + 0x008bcf00, + 0xf412bbc8, +/* 0x01f6: mmctx_done */ + 0x87f1fa1b, + 0x84b6085c, + 0xf094bd06, + 0x89d00199, +/* 0x0207: strand_wait */ + 0xf900f800, + 0x02a7f0a0, + 0xfcc921f4, +/* 0x0213: strand_pre */ + 0xf100f8a0, + 0xf04afc87, + 0x97f00283, + 0x0089d00c, + 0x020721f5, +/* 0x0226: strand_post */ + 0x87f100f8, + 0x83f04afc, + 0x0d97f002, + 0xf50089d0, + 0xf8020721, +/* 0x0239: strand_set */ + 0xfca7f100, + 0x02a3f04f, + 0x0500aba2, + 0xd00fc7f0, + 0xc7f000ac, + 0x00bcd00b, + 0x020721f5, + 0xf000aed0, + 0xbcd00ac7, + 0x0721f500, +/* 0x0263: strand_ctx_init */ + 0xf100f802, + 0xb6083c87, + 0x94bd0684, + 0xd00399f0, + 0x21f50089, + 0xe7f00213, + 0x3921f503, + 0xfca7f102, + 0x02a3f046, + 0x0400aba0, + 0xf040a0d0, + 0xbcd001c7, + 0x0721f500, + 0x010c9202, + 0xf000acd0, + 0xbcd002c7, + 0x0721f500, + 0x2621f502, + 0x8087f102, + 0x0684b608, + 0xb70089cf, + 0x95220080, +/* 0x02ba: ctx_init_strand_loop */ + 0x8ed008fe, + 0x408ed000, + 0xb6808acf, + 0xa0b606a5, + 0x00eabb01, + 0xb60480b6, + 0x1bf40192, + 0x08e4b6e8, + 0xf1f2efbc, + 0xb6085c87, + 0x94bd0684, + 0xd00399f0, + 0x00f80089, +/* 0x02ec: error */ + 0xe7f1e0f9, + 0xe4b60814, + 0x00efd006, + 0x0c1ce7f1, + 0xf006e4b6, + 0xefd001f7, + 0xf8e0fc00, +/* 0x0309: init */ + 0xfe04bd00, + 0x07fe0004, + 0x0017f100, + 0x0227f012, + 0xf10012d0, + 0xfe058517, + 0x17f10010, + 0x10d00400, + 0x0437f1c0, + 0x0634b604, + 0x200327f1, + 0xf10032d0, + 0xd0200427, + 0x27f10132, + 0x32d0200b, + 0x0c27f102, + 0x0732d020, + 0x0c2427f1, + 0xb90624b6, + 0x23d00003, + 0x0427f100, + 0x0023f087, + 0xb70012d0, + 0xf0010012, + 0x12d00427, + 0x1031f400, + 0x9604e7f1, + 0xf440e3f0, + 0xf1c76821, + 0x03018090, + 0x801ff4f0, + 0x17f0020f, + 0x041fbb01, + 0xf10112b6, + 0xb6040c27, + 0x21d00624, + 0x4021d000, + 0x010017f1, + 0x98000e98, + 0x21f5010f, + 0x37f1013d, + 0x34b60700, + 0x08149506, + 0xd00034d0, + 0x30b74034, + 0x1fbb1300, + 0x02f5b600, + 0xb6003fd0, + 0x10b60815, + 0x0814b601, + 0xf5021fb9, + 0xbb026321, + 0x0398001f, + 0x0047f102, + 0x5043f020, +/* 0x03e4: init_gpc */ + 0x08044ea0, + 0xf4021fb9, + 0x4ea08d21, + 0xf4bd010c, + 0xa08d21f4, + 0xf401044e, + 0x4ea08d21, + 0xf7f00100, + 0x8d21f402, + 0x08004ea0, +/* 0x040c: init_gpc_wait */ + 0xc86821f4, + 0x0bf41fff, + 0x044ea0fa, + 0x6821f408, + 0xb7001fbb, + 0xb6800040, + 0x1bf40132, + 0x0027f1be, + 0x0624b608, + 0xb74021d0, + 0xbd080020, + 0x1f19f014, +/* 0x043f: main */ + 0xf40021d0, + 0x28f40031, + 0x10d7f000, + 0xf43921f4, + 0xe4b1f401, + 0x1bf54001, + 0x87f100d1, + 0x84b6083c, + 0xf094bd06, + 0x89d00499, + 0x0017f100, + 0x0614b60b, + 0xcf4012cf, + 0x13c80011, + 0x7e0bf41f, + 0xf41f23c8, + 0x20f95a0b, + 0xf10212b9, + 0xb6083c87, + 0x94bd0684, + 0xd00799f0, + 0x32f40089, + 0x0231f401, + 0x07f521f5, + 0x085c87f1, + 0xbd0684b6, + 0x0799f094, + 0xfc0089d0, + 0x3c87f120, + 0x0684b608, + 0x99f094bd, + 0x0089d006, + 0xf50131f4, + 0xf107f521, + 0xb6085c87, + 0x94bd0684, + 0xd00699f0, + 0x0ef40089, +/* 0x04d5: chsw_prev_no_next */ + 0xb920f931, + 0x32f40212, + 0x0232f401, + 0x07f521f5, + 0x17f120fc, + 0x14b60b00, + 0x0012d006, +/* 0x04f3: chsw_no_prev */ + 0xc8130ef4, + 0x0bf41f23, + 0x0131f40d, + 0xf50232f4, +/* 0x0503: chsw_done */ + 0xf107f521, + 0xb60b0c17, + 0x27f00614, + 0x0012d001, + 0x085c87f1, + 0xbd0684b6, + 0x0499f094, + 0xf50089d0, +/* 0x0523: main_not_ctx_switch */ + 0xb0ff200e, + 0x1bf401e4, + 0x02f2b90d, + 0x078121f5, +/* 0x0533: main_not_ctx_chan */ + 0xb0420ef4, + 0x1bf402e4, + 0x3c87f12e, + 0x0684b608, + 0x99f094bd, + 0x0089d007, + 0xf40132f4, + 0x21f50232, + 0x87f107f5, + 0x84b6085c, + 0xf094bd06, + 0x89d00799, + 0x110ef400, +/* 0x0564: main_not_ctx_save */ + 0xf010ef94, + 0x21f501f5, + 0x0ef502ec, +/* 0x0572: main_done */ + 0x17f1fed1, + 0x14b60820, + 0xf024bd06, + 0x12d01f29, + 0xbe0ef500, +/* 0x0585: ih */ + 0xfe80f9fe, + 0x80f90188, + 0xa0f990f9, + 0xd0f9b0f9, + 0xf0f9e0f9, + 0xc4800acf, + 0x0bf404ab, + 0x00b7f11d, + 0x10d7f019, + 0xcf40becf, + 0x21f400bf, + 0x00b0b704, + 0x01e7f004, +/* 0x05bb: ih_no_fifo */ + 0xe400bed0, + 0xf40100ab, + 0xd7f00d0b, + 0x01e7f110, + 0x0421f440, +/* 0x05cc: ih_no_ctxsw */ + 0x0104b7f1, + 0xabffb0bd, + 0x0d0bf4b4, + 0x0c1ca7f1, + 0xd006a4b6, +/* 0x05e2: ih_no_other */ + 0x0ad000ab, + 0xfcf0fc40, + 0xfcd0fce0, + 0xfca0fcb0, + 0xfe80fc90, + 0x80fc0088, + 0xf80032f4, +/* 0x05fd: ctx_4160s */ + 0x60e7f101, + 0x40e3f041, + 0xf401f7f0, +/* 0x060a: ctx_4160s_wait */ + 0x21f48d21, + 0x04ffc868, + 0xf8fa0bf4, +/* 0x0615: ctx_4160c */ + 0x60e7f100, + 0x40e3f041, + 0x21f4f4bd, +/* 0x0623: ctx_4170s */ + 0xf100f88d, + 0xf04170e7, + 0xf5f040e3, + 0x8d21f410, +/* 0x0632: ctx_4170w */ + 0xe7f100f8, + 0xe3f04170, + 0x6821f440, + 0xf410f4f0, + 0x00f8f31b, +/* 0x0644: ctx_redswitch */ + 0x0614e7f1, + 0xf106e4b6, + 0xd00270f7, + 0xf7f000ef, +/* 0x0655: ctx_redswitch_delay */ + 0x01f2b608, + 0xf1fd1bf4, + 0xd00770f7, + 0x00f800ef, +/* 0x0664: ctx_86c */ + 0x086ce7f1, + 0xd006e4b6, + 0xe7f100ef, + 0xe3f08a14, + 0x8d21f440, + 0xa86ce7f1, + 0xf441e3f0, + 0x00f88d21, +/* 0x0684: ctx_load */ + 0x083c87f1, + 0xbd0684b6, + 0x0599f094, + 0xf00089d0, + 0x21f40ca7, + 0x2417f1c9, + 0x0614b60a, + 0xf10010d0, + 0xb60b0037, + 0x32d00634, + 0x0c17f140, + 0x0614b60a, + 0xd00747f0, + 0x14d00012, +/* 0x06bd: ctx_chan_wait_0 */ + 0x4014cf40, + 0xf41f44f0, + 0x32d0fa1b, + 0x000bfe00, + 0xb61f2af0, + 0x20b60424, + 0x3c87f102, + 0x0684b608, + 0x99f094bd, + 0x0089d008, + 0x0a0417f1, + 0xd00614b6, + 0x17f10012, + 0x14b60a20, + 0x0227f006, + 0x800023f1, + 0xf00012d0, + 0x27f11017, + 0x23f00200, + 0x0512fa02, + 0x87f103f8, + 0x84b6085c, + 0xf094bd06, + 0x89d00899, + 0x81019800, + 0x981814b6, + 0x25b68002, + 0x0512fd08, + 0xf1160180, + 0xb6083c87, + 0x94bd0684, + 0xd00999f0, + 0x27f10089, + 0x24b60a04, + 0x0021d006, + 0xf10127f0, + 0xb60a2017, + 0x12d00614, + 0x0017f100, + 0x0613f001, + 0xf80501fa, + 0x5c87f103, + 0x0684b608, + 0x99f094bd, + 0x0089d009, + 0x085c87f1, + 0xbd0684b6, + 0x0599f094, + 0xf80089d0, +/* 0x0781: ctx_chan */ + 0xfd21f500, + 0x8421f505, + 0x0ca7f006, + 0xf1c921f4, + 0xb60a1017, + 0x27f00614, + 0x0012d005, +/* 0x079c: ctx_chan_wait */ + 0xfd0012cf, + 0x1bf40522, + 0x1521f5fa, +/* 0x07ab: ctx_mmio_exec */ + 0x9800f806, + 0x27f14103, + 0x24b60a04, + 0x0023d006, +/* 0x07ba: ctx_mmio_loop */ + 0x34c434bd, + 0x0f1bf4ff, + 0x020057f1, + 0xfa0653f0, + 0x03f80535, +/* 0x07cc: ctx_mmio_pull */ + 0x98804e98, + 0x21f4814f, + 0x0830b68d, + 0xf40112b6, +/* 0x07de: ctx_mmio_done */ + 0x0398df1b, + 0x0023d016, + 0xf1400080, + 0xf0010017, + 0x01fa0613, + 0xf803f806, +/* 0x07f5: ctx_xfer */ + 0x00f7f100, + 0x06f4b60c, + 0xd004e7f0, +/* 0x0802: ctx_xfer_idle */ + 0xfecf80fe, + 0x00e4f100, + 0xf91bf420, + 0xf40611f4, +/* 0x0812: ctx_xfer_pre */ + 0xf7f01102, + 0x6421f510, + 0xfd21f506, + 0x1c11f405, +/* 0x0820: ctx_xfer_pre_load */ + 0xf502f7f0, + 0xf5062321, + 0xf5063221, + 0xbd064421, + 0x2321f5f4, + 0x8421f506, +/* 0x0839: ctx_xfer_exec */ + 0x16019806, + 0x041427f1, + 0xd00624b6, + 0xe7f10020, + 0xe3f0a500, + 0x021fb941, + 0xb68d21f4, + 0xfcf004e0, + 0x022cf001, + 0xfd0124b6, + 0x21f405f2, + 0xfc17f18d, + 0x0213f04a, + 0xd00c27f0, + 0x21f50012, + 0x27f10207, + 0x23f047fc, + 0x0020d002, + 0xb6012cf0, + 0x12d00320, + 0x01acf000, + 0xf006a5f0, + 0x0c9800b7, + 0x010d9800, + 0xf500e7f0, + 0xf0015c21, + 0x21f508a7, + 0x21f50103, + 0x01f40207, + 0x0ca7f022, + 0xf1c921f4, + 0xb60a1017, + 0x27f00614, + 0x0012d005, +/* 0x08c0: ctx_xfer_post_save_wait */ + 0xfd0012cf, + 0x1bf40522, + 0x3202f4fa, +/* 0x08cc: ctx_xfer_post */ + 0xf502f7f0, + 0xbd062321, + 0x6421f5f4, + 0x2621f506, + 0x3221f502, + 0xf5f4bd06, + 0xf4062321, + 0x01981011, + 0x0511fd40, + 0xf5070bf4, +/* 0x08f7: ctx_xfer_no_post_mmio */ + 0xf507ab21, +/* 0x08fb: ctx_xfer_done */ + 0xf8061521, + 0x00000000, +}; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc index 27c6a0fdfe0e..d4840f1879fd 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc @@ -22,7 +22,7 @@ * Authors: Ben Skeggs */ -#define NVGK +#define CHIPSET GK100 #include "macros.fuc" .section #nve0_grhub_data diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/macros.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/macros.fuc index 43a0b9476efd..f73cf3efbc35 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/macros.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/macros.fuc @@ -24,6 +24,10 @@ #include "os.h" +#define GF100 0xc0 +#define GF117 0xd7 +#define GK100 0xe0 + #define mmctx_data(r,c) .b32 (((c - 1) << 26) | r) #define queue_init .skip 72 // (2 * 4) + ((8 * 4) * 2) diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c index 5f7a040cf0eb..fae6daec2a54 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c @@ -282,6 +282,12 @@ nvc0_graph_init_unk88xx[] = { {} }; +struct nvc0_graph_init +nvc0_graph_tpc_0[] = { + { 0x50405c, 1, 0x04, 0x00000001 }, + {} +}; + void nvc0_graph_mmio(struct nvc0_graph_priv *priv, struct nvc0_graph_init *init) { @@ -982,9 +988,6 @@ nvc0_graph_init(struct nouveau_object *object) for (i = 0; oclass->mmio[i]; i++) nvc0_graph_mmio(priv, oclass->mmio[i]); - /* affects TFB offset queries */ - nv_wr32(priv, TPC_UNIT(0, 0, 0x5c), 1); - memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr)); for (i = 0, gpc = -1; i < priv->tpc_total; i++) { do { @@ -1008,7 +1011,11 @@ nvc0_graph_init(struct nouveau_object *object) nv_wr32(priv, GPC_UNIT(gpc, 0x0918), magicgpc918); } - nv_wr32(priv, GPC_BCAST(0x1bd4), magicgpc918); + if (nv_device(priv)->chipset != 0xd7) + nv_wr32(priv, GPC_BCAST(0x1bd4), magicgpc918); + else + nv_wr32(priv, GPC_BCAST(0x3fd4), magicgpc918); + nv_wr32(priv, GPC_BCAST(0x08ac), nv_rd32(priv, 0x100800)); nv_wr32(priv, 0x400500, 0x00010001); @@ -1123,10 +1130,9 @@ nvc0_graph_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nvc0_graph_oclass *oclass = (void *)bclass; struct nouveau_device *device = nv_device(parent); struct nvc0_graph_priv *priv; - bool enable = device->chipset != 0xd7; int ret, i; - ret = nouveau_graph_create(parent, engine, bclass, enable, &priv); + ret = nouveau_graph_create(parent, engine, bclass, true, &priv); *pobject = nv_object(priv); if (ret) return ret; @@ -1199,6 +1205,7 @@ nvc0_graph_ctor(struct nouveau_object *parent, struct nouveau_object *engine, case 0xcf: /* 4/0/0/0, 3 */ priv->magic_not_rop_nr = 0x03; break; + case 0xd7: case 0xd9: /* 1/0/0/0, 1 */ priv->magic_not_rop_nr = 0x01; break; @@ -1221,6 +1228,7 @@ nvc0_graph_init_mmio[] = { nvc0_graph_init_gpc, nvc0_graph_init_tpc, nvc0_graph_init_unk88xx, + nvc0_graph_tpc_0, NULL }; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h index f8d653b11ad7..0180f05f40c3 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h @@ -193,9 +193,11 @@ extern struct nvc0_graph_init nvc0_graph_init_unk58xx[]; extern struct nvc0_graph_init nvc0_graph_init_unk80xx[]; extern struct nvc0_graph_init nvc0_graph_init_gpc[]; extern struct nvc0_graph_init nvc0_graph_init_unk88xx[]; +extern struct nvc0_graph_init nvc0_graph_tpc_0[]; extern struct nvc0_graph_init nvc3_graph_init_unk58xx[]; +extern struct nvc0_graph_init nvd9_graph_init_unk58xx[]; extern struct nvc0_graph_init nvd9_graph_init_unk64xx[]; extern struct nvc0_graph_init nve4_graph_init_regs[]; @@ -209,6 +211,7 @@ void nvc0_grctx_generate_tpcid(struct nvc0_graph_priv *); void nvc0_grctx_generate_r406028(struct nvc0_graph_priv *); void nvc0_grctx_generate_r4060a8(struct nvc0_graph_priv *); void nvc0_grctx_generate_r418bb8(struct nvc0_graph_priv *); +void nve4_grctx_generate_r418bb8(struct nvc0_graph_priv *); void nvc0_grctx_generate_r406800(struct nvc0_graph_priv *); extern struct nouveau_oclass *nvc0_grctx_oclass; @@ -244,8 +247,11 @@ extern struct nouveau_oclass *nvc8_grctx_oclass; extern struct nvc0_graph_init nvc8_grctx_init_9197[]; extern struct nvc0_graph_init nvc8_grctx_init_9297[]; +extern struct nouveau_oclass *nvd7_grctx_oclass; + extern struct nouveau_oclass *nvd9_grctx_oclass; extern struct nvc0_graph_init nvd9_grctx_init_rop[]; +extern struct nvc0_graph_mthd nvd9_grctx_init_mthd[]; void nve4_grctx_generate_main(struct nvc0_graph_priv *, struct nvc0_grctx *); void nve4_grctx_generate_mods(struct nvc0_graph_priv *, struct nvc0_grctx *); diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc1.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvc1.c index 6d192d2719c7..bc4a469b86cb 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nvc1.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc1.c @@ -123,6 +123,7 @@ nvc1_graph_init_mmio[] = { nvc1_graph_init_gpc, nvc1_graph_init_tpc, nvc0_graph_init_unk88xx, + nvc0_graph_tpc_0, NULL }; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc3.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvc3.c index 48d3c761af5b..d44b3b3ee800 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nvc3.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc3.c @@ -89,6 +89,7 @@ nvc3_graph_init_mmio[] = { nvc0_graph_init_gpc, nvc3_graph_init_tpc, nvc0_graph_init_unk88xx, + nvc0_graph_tpc_0, NULL }; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc8.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvc8.c index e5b5593a7024..02845e567314 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nvc8.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc8.c @@ -120,6 +120,7 @@ nvc8_graph_init_mmio[] = { nvc8_graph_init_gpc, nvc8_graph_init_tpc, nvc0_graph_init_unk88xx, + nvc0_graph_tpc_0, NULL }; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvd7.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvd7.c new file mode 100644 index 000000000000..5052d7ab4d72 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvd7.c @@ -0,0 +1,167 @@ +/* + * Copyright 2013 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 "nvc0.h" + +/******************************************************************************* + * PGRAPH engine/subdev functions + ******************************************************************************/ + +#include "fuc/hubnvd7.fuc.h" + +struct nvc0_graph_ucode +nvd7_graph_fecs_ucode = { + .code.data = nvd7_grhub_code, + .code.size = sizeof(nvd7_grhub_code), + .data.data = nvd7_grhub_data, + .data.size = sizeof(nvd7_grhub_data), +}; + +#include "fuc/gpcnvd7.fuc.h" + +struct nvc0_graph_ucode +nvd7_graph_gpccs_ucode = { + .code.data = nvd7_grgpc_code, + .code.size = sizeof(nvd7_grgpc_code), + .data.data = nvd7_grgpc_data, + .data.size = sizeof(nvd7_grgpc_data), +}; + +static struct nvc0_graph_init +nvd7_graph_init_gpc[] = { + { 0x418408, 1, 0x04, 0x00000000 }, + { 0x4184a0, 1, 0x04, 0x00000000 }, + { 0x4184a4, 2, 0x04, 0x00000000 }, + { 0x418604, 1, 0x04, 0x00000000 }, + { 0x418680, 1, 0x04, 0x00000000 }, + { 0x418714, 1, 0x04, 0x00000000 }, + { 0x418384, 1, 0x04, 0x00000000 }, + { 0x418814, 3, 0x04, 0x00000000 }, + { 0x418b04, 1, 0x04, 0x00000000 }, + { 0x4188c8, 2, 0x04, 0x00000000 }, + { 0x4188d0, 1, 0x04, 0x00010000 }, + { 0x4188d4, 1, 0x04, 0x00000001 }, + { 0x418910, 1, 0x04, 0x00010001 }, + { 0x418914, 1, 0x04, 0x00000301 }, + { 0x418918, 1, 0x04, 0x00800000 }, + { 0x418980, 1, 0x04, 0x77777770 }, + { 0x418984, 3, 0x04, 0x77777777 }, + { 0x418c04, 1, 0x04, 0x00000000 }, + { 0x418c64, 1, 0x04, 0x00000000 }, + { 0x418c68, 1, 0x04, 0x00000000 }, + { 0x418c88, 1, 0x04, 0x00000000 }, + { 0x418cb4, 2, 0x04, 0x00000000 }, + { 0x418d00, 1, 0x04, 0x00000000 }, + { 0x418d28, 1, 0x04, 0x00000000 }, + { 0x418f00, 1, 0x04, 0x00000000 }, + { 0x418f08, 1, 0x04, 0x00000000 }, + { 0x418f20, 2, 0x04, 0x00000000 }, + { 0x418e00, 1, 0x04, 0x00000003 }, + { 0x418e08, 1, 0x04, 0x00000000 }, + { 0x418e1c, 1, 0x04, 0x00000000 }, + { 0x418e20, 1, 0x04, 0x00000000 }, + { 0x41900c, 1, 0x04, 0x00000000 }, + { 0x419018, 1, 0x04, 0x00000000 }, + {} +}; + +static struct nvc0_graph_init +nvd7_graph_init_tpc[] = { + { 0x419d08, 2, 0x04, 0x00000000 }, + { 0x419d10, 1, 0x04, 0x00000014 }, + { 0x419ab0, 1, 0x04, 0x00000000 }, + { 0x419ac8, 1, 0x04, 0x00000000 }, + { 0x419ab8, 1, 0x04, 0x000000e7 }, + { 0x419abc, 2, 0x04, 0x00000000 }, + { 0x419ab4, 1, 0x04, 0x00000000 }, + { 0x41980c, 1, 0x04, 0x00000010 }, + { 0x419844, 1, 0x04, 0x00000000 }, + { 0x41984c, 1, 0x04, 0x00005bc8 }, + { 0x419850, 2, 0x04, 0x00000000 }, + { 0x419c98, 1, 0x04, 0x00000000 }, + { 0x419ca8, 1, 0x04, 0x80000000 }, + { 0x419cb4, 1, 0x04, 0x00000000 }, + { 0x419cb8, 1, 0x04, 0x00008bf4 }, + { 0x419cbc, 1, 0x04, 0x28137606 }, + { 0x419cc0, 2, 0x04, 0x00000000 }, + { 0x419c0c, 1, 0x04, 0x00000000 }, + { 0x419e00, 1, 0x04, 0x00000000 }, + { 0x419ea0, 1, 0x04, 0x00000000 }, + { 0x419ea4, 1, 0x04, 0x00000100 }, + { 0x419ea8, 1, 0x04, 0x02001100 }, + { 0x419eac, 1, 0x04, 0x11100702 }, + { 0x419eb0, 1, 0x04, 0x00000003 }, + { 0x419eb4, 4, 0x04, 0x00000000 }, + { 0x419ec8, 1, 0x04, 0x0e063818 }, + { 0x419ecc, 1, 0x04, 0x0e060e06 }, + { 0x419ed0, 1, 0x04, 0x00003818 }, + { 0x419ed4, 1, 0x04, 0x011104f1 }, + { 0x419edc, 1, 0x04, 0x00000000 }, + { 0x419f00, 1, 0x04, 0x00000000 }, + { 0x419f2c, 1, 0x04, 0x00000000 }, + {} +}; + +static struct nvc0_graph_init +nvd7_graph_init_tpc_0[] = { + { 0x40402c, 1, 0x04, 0x00000000 }, + { 0x4040f0, 1, 0x04, 0x00000000 }, + { 0x404174, 1, 0x04, 0x00000000 }, + { 0x503018, 1, 0x04, 0x00000001 }, + {} +}; + +static struct nvc0_graph_init * +nvd7_graph_init_mmio[] = { + nvc0_graph_init_regs, + nvc0_graph_init_unk40xx, + nvc0_graph_init_unk44xx, + nvc0_graph_init_unk78xx, + nvc0_graph_init_unk60xx, + nvd9_graph_init_unk64xx, + nvd9_graph_init_unk58xx, + nvc0_graph_init_unk80xx, + nvd7_graph_init_gpc, + nvd7_graph_init_tpc, + nve4_graph_init_unk, + nvc0_graph_init_unk88xx, + nvd7_graph_init_tpc_0, + NULL +}; + +struct nouveau_oclass * +nvd7_graph_oclass = &(struct nvc0_graph_oclass) { + .base.handle = NV_ENGINE(GR, 0xd7), + .base.ofuncs = &(struct nouveau_ofuncs) { + .ctor = nvc0_graph_ctor, + .dtor = nvc0_graph_dtor, + .init = nvc0_graph_init, + .fini = _nouveau_graph_fini, + }, + .cclass = &nvd7_grctx_oclass, + .sclass = nvc8_graph_sclass, + .mmio = nvd7_graph_init_mmio, + .fecs.ucode = &nvd7_graph_fecs_ucode, + .gpccs.ucode = &nvd7_graph_gpccs_ucode, +}.base; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvd9.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvd9.c index 0059788af0f1..652098e0df3f 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nvd9.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvd9.c @@ -34,7 +34,7 @@ nvd9_graph_init_unk64xx[] = { {} }; -static struct nvc0_graph_init +struct nvc0_graph_init nvd9_graph_init_unk58xx[] = { { 0x405844, 1, 0x04, 0x00ffffff }, { 0x405850, 1, 0x04, 0x00000000 }, @@ -144,6 +144,7 @@ nvd9_graph_init_mmio[] = { nvd9_graph_init_gpc, nvd9_graph_init_tpc, nvc0_graph_init_unk88xx, + nvc0_graph_tpc_0, NULL }; diff --git a/drivers/gpu/drm/nouveau/core/include/engine/graph.h b/drivers/gpu/drm/nouveau/core/include/engine/graph.h index 4f5c6248a219..8e1b52312ddc 100644 --- a/drivers/gpu/drm/nouveau/core/include/engine/graph.h +++ b/drivers/gpu/drm/nouveau/core/include/engine/graph.h @@ -65,6 +65,7 @@ extern struct nouveau_oclass *nvc0_graph_oclass; extern struct nouveau_oclass *nvc1_graph_oclass; extern struct nouveau_oclass *nvc3_graph_oclass; extern struct nouveau_oclass *nvc8_graph_oclass; +extern struct nouveau_oclass *nvd7_graph_oclass; extern struct nouveau_oclass *nvd9_graph_oclass; extern struct nouveau_oclass *nve4_graph_oclass; extern struct nouveau_oclass *nvf0_graph_oclass; -- cgit v1.2.3 From 56fbd2b65446d4fb4df7770c49a70d563b7569c9 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 2 May 2013 12:37:38 +1000 Subject: drm/nvf0/fifo: enable support Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/core/engine/device/nve0.c | 2 +- drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nve0.c b/drivers/gpu/drm/nouveau/core/engine/device/nve0.c index 78ae397c3eee..10a4ee208009 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/nve0.c @@ -163,8 +163,8 @@ nve0_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass; device->oclass[NVDEV_SUBDEV_BAR ] = &nvc0_bar_oclass; device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvd0_dmaeng_oclass; -#if 0 device->oclass[NVDEV_ENGINE_FIFO ] = &nve0_fifo_oclass; +#if 0 device->oclass[NVDEV_ENGINE_SW ] = &nvc0_software_oclass; device->oclass[NVDEV_ENGINE_GR ] = nvf0_graph_oclass; #endif diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c index 0338e66bc620..09644fa9602c 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c @@ -221,8 +221,10 @@ nve0_fifo_chan_ctor(struct nouveau_object *parent, } } - if (i == FIFO_ENGINE_NR) + if (i == FIFO_ENGINE_NR) { + nv_error(priv, "unsupported engines 0x%08x\n", args->engine); return -ENODEV; + } ret = nouveau_fifo_channel_create(parent, engine, oclass, 1, priv->user.bar.offset, 0x200, -- cgit v1.2.3 From 9ec2dbba9fedbd1788849fb00d659ebdf549a4f8 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 2 May 2013 12:38:41 +1000 Subject: drm/nvf0/ce: enable support Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/core/engine/device/nve0.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nve0.c b/drivers/gpu/drm/nouveau/core/engine/device/nve0.c index 10a4ee208009..90779120d9e9 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/nve0.c @@ -169,10 +169,10 @@ nve0_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_GR ] = nvf0_graph_oclass; #endif device->oclass[NVDEV_ENGINE_DISP ] = &nvf0_disp_oclass; -#if 0 device->oclass[NVDEV_ENGINE_COPY0 ] = &nve0_copy0_oclass; device->oclass[NVDEV_ENGINE_COPY1 ] = &nve0_copy1_oclass; device->oclass[NVDEV_ENGINE_COPY2 ] = &nve0_copy2_oclass; +#if 0 device->oclass[NVDEV_ENGINE_BSP ] = &nve0_bsp_oclass; device->oclass[NVDEV_ENGINE_VP ] = &nve0_vp_oclass; device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass; -- cgit v1.2.3 From b054aadfb0030b9717bb22f4283bfe5aec13440b Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 4 Jul 2013 11:01:34 +1000 Subject: drm/nvf0/gr: magic sequence that makes PGRAPH come out of hiding Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/core/engine/device/nve0.c | 2 - drivers/gpu/drm/nouveau/core/engine/graph/nvf0.c | 50 ++++++++++++++++++++++- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nve0.c b/drivers/gpu/drm/nouveau/core/engine/device/nve0.c index 90779120d9e9..7aca1877add4 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/nve0.c @@ -164,10 +164,8 @@ nve0_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_BAR ] = &nvc0_bar_oclass; device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvd0_dmaeng_oclass; device->oclass[NVDEV_ENGINE_FIFO ] = &nve0_fifo_oclass; -#if 0 device->oclass[NVDEV_ENGINE_SW ] = &nvc0_software_oclass; device->oclass[NVDEV_ENGINE_GR ] = nvf0_graph_oclass; -#endif device->oclass[NVDEV_ENGINE_DISP ] = &nvf0_disp_oclass; device->oclass[NVDEV_ENGINE_COPY0 ] = &nve0_copy0_oclass; device->oclass[NVDEV_ENGINE_COPY1 ] = &nve0_copy1_oclass; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvf0.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvf0.c index 1d6ebd05216e..d7dff11c1883 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nvf0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvf0.c @@ -24,6 +24,15 @@ #include "nvc0.h" +/******************************************************************************* + * Graphics object classes + ******************************************************************************/ + +static struct nouveau_oclass +nvf0_graph_sclass[] = { + {} +}; + /******************************************************************************* * PGRAPH engine/subdev functions ******************************************************************************/ @@ -142,6 +151,43 @@ nvf0_graph_init_tpc[] = { {} }; +static int +nvf0_graph_fini(struct nouveau_object *object, bool suspend) +{ + struct nvc0_graph_priv *priv = (void *)object; + static const struct { + u32 addr; + u32 data; + } magic[] = { + { 0x020520, 0xfffffffc }, + { 0x020524, 0xfffffffe }, + { 0x020524, 0xfffffffc }, + { 0x020524, 0xfffffff8 }, + { 0x020524, 0xffffffe0 }, + { 0x020530, 0xfffffffe }, + { 0x02052c, 0xfffffffa }, + { 0x02052c, 0xfffffff0 }, + { 0x02052c, 0xffffffc0 }, + { 0x02052c, 0xffffff00 }, + { 0x02052c, 0xfffffc00 }, + { 0x02052c, 0xfffcfc00 }, + { 0x02052c, 0xfff0fc00 }, + { 0x02052c, 0xff80fc00 }, + { 0x020528, 0xfffffffe }, + { 0x020528, 0xfffffffc }, + }; + int i; + + nv_mask(priv, 0x000200, 0x08001000, 0x00000000); + nv_mask(priv, 0x0206b4, 0x00000000, 0x00000000); + for (i = 0; i < ARRAY_SIZE(magic); i++) { + nv_wr32(priv, magic[i].addr, magic[i].data); + nv_wait(priv, magic[i].addr, 0x80000000, 0x00000000); + } + + return nouveau_graph_fini(&priv->base, suspend); +} + static struct nvc0_graph_init * nvf0_graph_init_mmio[] = { nve4_graph_init_regs, @@ -168,9 +214,9 @@ nvf0_graph_oclass = &(struct nvc0_graph_oclass) { .ctor = nvc0_graph_ctor, .dtor = nvc0_graph_dtor, .init = nve4_graph_init, - .fini = _nouveau_graph_fini, + .fini = nvf0_graph_fini, }, .cclass = &nvf0_grctx_oclass, - .sclass = NULL, + .sclass = nvf0_graph_sclass, .mmio = nvf0_graph_init_mmio, }.base; -- cgit v1.2.3 From 9d1c4c51ce9cd133a25b339be398e35663cc2ae5 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 4 Jul 2013 11:17:33 +1000 Subject: drm/nvf0/gr: enable support, if external cs ucode is available Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c | 3 ++- drivers/gpu/drm/nouveau/core/engine/graph/nvf0.c | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c index fae6daec2a54..3f4f35cc3848 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c @@ -1132,7 +1132,8 @@ nvc0_graph_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nvc0_graph_priv *priv; int ret, i; - ret = nouveau_graph_create(parent, engine, bclass, true, &priv); + ret = nouveau_graph_create(parent, engine, bclass, + (oclass->fecs.ucode != NULL), &priv); *pobject = nv_object(priv); if (ret) return ret; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvf0.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvf0.c index d7dff11c1883..01294b0a0632 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nvf0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvf0.c @@ -30,6 +30,10 @@ static struct nouveau_oclass nvf0_graph_sclass[] = { + { 0x902d, &nouveau_object_ofuncs }, + { 0xa140, &nouveau_object_ofuncs }, + { 0xa197, &nouveau_object_ofuncs }, + { 0xa1c0, &nouveau_object_ofuncs }, {} }; -- cgit v1.2.3 From 18ac4246510bee85d2efd6ed536b436e246f7624 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 4 Jul 2013 11:48:46 +1000 Subject: drm/nvc0/devinit: minor typo Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/core/subdev/devinit/nvc0.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/core/subdev/devinit/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/devinit/nvc0.c index af407a8637c7..19e265bf4574 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/devinit/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/devinit/nvc0.c @@ -80,7 +80,7 @@ nvc0_devinit_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass nvc0_devinit_oclass = { - .handle = NV_SUBDEV(DEVINIT, 0xa3), + .handle = NV_SUBDEV(DEVINIT, 0xc0), .ofuncs = &(struct nouveau_ofuncs) { .ctor = nvc0_devinit_ctor, .dtor = _nouveau_devinit_dtor, -- cgit v1.2.3 From 0085a60524aeb743c15bbdf7354f4e4f6623243e Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 4 Jul 2013 12:54:28 +1000 Subject: drm/nvf0/gr: fix ddx shaders locking up on me This can be generalised and used on GK104 (probably even GF117), but lets just make it work for now. Signed-off-by: Ben Skeggs --- .../gpu/drm/nouveau/core/engine/graph/ctxnve4.c | 2 +- .../gpu/drm/nouveau/core/engine/graph/ctxnvf0.c | 54 +++++++++++++++++++++- drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h | 1 - 3 files changed, 54 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c index 51fb2687b570..261a600c8ffb 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c @@ -804,7 +804,7 @@ nve4_grctx_init_unk[] = { {} }; -void +static void nve4_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info) { u32 magic[GPC_MAX][2]; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c index c41a6f07e958..c1bacc3d7374 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c @@ -221,6 +221,58 @@ nvf0_grctx_init_unk[] = { {} }; +static void +nvf0_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info) +{ + u32 magic[GPC_MAX][4]; + u32 offset; + int gpc; + + mmio_data(0x003000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS); + mmio_data(0x008000, 0x0100, NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS); + mmio_data(0x060000, 0x1000, NV_MEM_ACCESS_RW); + mmio_list(0x40800c, 0x00000000, 8, 1); + mmio_list(0x408010, 0x80000000, 0, 0); + mmio_list(0x419004, 0x00000000, 8, 1); + mmio_list(0x419008, 0x00000000, 0, 0); + mmio_list(0x4064cc, 0x80000000, 0, 0); + mmio_list(0x408004, 0x00000000, 8, 0); + mmio_list(0x408008, 0x80000030, 0, 0); + mmio_list(0x418808, 0x00000000, 8, 0); + mmio_list(0x41880c, 0x80000030, 0, 0); + mmio_list(0x4064c8, 0x01800600, 0, 0); + mmio_list(0x418810, 0x80000000, 12, 2); + mmio_list(0x419848, 0x10000000, 12, 2); + + mmio_list(0x405830, 0x02180648, 0, 0); + mmio_list(0x4064c4, 0x0192ffff, 0, 0); + + for (gpc = 0, offset = 0; gpc < priv->gpc_nr; gpc++) { + u16 magic0 = 0x0218 * (priv->tpc_nr[gpc] - 1); + u16 magic1 = 0x0648 * (priv->tpc_nr[gpc] - 1); + u16 magic2 = 0x0218; + u16 magic3 = 0x0648; + magic[gpc][0] = 0x10000000 | (magic0 << 16) | offset; + magic[gpc][1] = 0x00000000 | (magic1 << 16); + offset += 0x0324 * (priv->tpc_nr[gpc] - 1);; + magic[gpc][2] = 0x10000000 | (magic2 << 16) | offset; + magic[gpc][3] = 0x00000000 | (magic3 << 16); + offset += 0x0324; + } + + for (gpc = 0; gpc < priv->gpc_nr; gpc++) { + mmio_list(GPC_UNIT(gpc, 0x30c0), magic[gpc][0], 0, 0); + mmio_list(GPC_UNIT(gpc, 0x30e4), magic[gpc][1] | offset, 0, 0); + offset += 0x07ff * (priv->tpc_nr[gpc] - 1); + mmio_list(GPC_UNIT(gpc, 0x32c0), magic[gpc][2], 0, 0); + mmio_list(GPC_UNIT(gpc, 0x32e4), magic[gpc][3] | offset, 0, 0); + offset += 0x07ff; + } + + mmio_list(0x17e91c, 0x06060609, 0, 0); + mmio_list(0x17e920, 0x00090a05, 0, 0); +} + static struct nvc0_graph_init * nvf0_grctx_init_hub[] = { nvc0_grctx_init_base, @@ -267,7 +319,7 @@ nvf0_grctx_oclass = &(struct nvc0_grctx_oclass) { .wr32 = _nouveau_graph_context_wr32, }, .main = nve4_grctx_generate_main, - .mods = nve4_grctx_generate_mods, + .mods = nvf0_grctx_generate_mods, .hub = nvf0_grctx_init_hub, .gpc = nvf0_grctx_init_gpc, .icmd = nvc0_grctx_init_icmd, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h index 0180f05f40c3..dd06674336b4 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h @@ -254,7 +254,6 @@ extern struct nvc0_graph_init nvd9_grctx_init_rop[]; extern struct nvc0_graph_mthd nvd9_grctx_init_mthd[]; void nve4_grctx_generate_main(struct nvc0_graph_priv *, struct nvc0_grctx *); -void nve4_grctx_generate_mods(struct nvc0_graph_priv *, struct nvc0_grctx *); extern struct nouveau_oclass *nve4_grctx_oclass; extern struct nvc0_graph_init nve4_grctx_init_unk46xx[]; extern struct nvc0_graph_init nve4_grctx_init_unk47xx[]; -- cgit v1.2.3 From 960b4381c5fff0b0f16f4b812082811dde1ab7ab Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 4 Jul 2013 12:58:16 +1000 Subject: drm/nvc0-/gr: extend one of the magic calculations for >4 GPCs Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c index 34ed87f1ff16..118b54b1b83f 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c @@ -1014,13 +1014,14 @@ nvc0_grctx_generate_r418bb8(struct nvc0_graph_priv *priv) void nvc0_grctx_generate_r406800(struct nvc0_graph_priv *priv) { - u32 tpc_mask = 0, tpc_set = 0; - u8 tpcnr[GPC_MAX], a, b; - int gpc, tpc, i; + u64 tpc_mask = 0, tpc_set = 0; + u8 tpcnr[GPC_MAX]; + int gpc, tpc; + int i, a, b; memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr)); for (gpc = 0; gpc < priv->gpc_nr; gpc++) - tpc_mask |= ((1 << priv->tpc_nr[gpc]) - 1) << (gpc * 8); + tpc_mask |= ((1ULL << priv->tpc_nr[gpc]) - 1) << (gpc * 8); for (i = 0, gpc = -1, b = -1; i < 32; i++) { a = (i * (priv->tpc_total - 1)) / 32; @@ -1034,8 +1035,12 @@ nvc0_grctx_generate_r406800(struct nvc0_graph_priv *priv) tpc_set |= 1 << ((gpc * 8) + tpc); } - nv_wr32(priv, 0x406800 + (i * 0x20), tpc_set); - nv_wr32(priv, 0x406c00 + (i * 0x20), tpc_set ^ tpc_mask); + nv_wr32(priv, 0x406800 + (i * 0x20), lower_32_bits(tpc_set)); + nv_wr32(priv, 0x406c00 + (i * 0x20), lower_32_bits(tpc_set ^ tpc_mask)); + if (priv->gpc_nr > 4) { + nv_wr32(priv, 0x406804 + (i * 0x20), upper_32_bits(tpc_set)); + nv_wr32(priv, 0x406c04 + (i * 0x20), upper_32_bits(tpc_set ^ tpc_mask)); + } } } -- cgit v1.2.3 From 8f6fe26745d39299d43d79dd7ba9838517624c3f Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 4 Jul 2013 13:03:59 +1000 Subject: drm/nvf0/gr: build cs ucode for GK110 Signed-off-by: Ben Skeggs --- .../drm/nouveau/core/engine/graph/fuc/gpcnvf0.fuc | 42 + .../nouveau/core/engine/graph/fuc/gpcnvf0.fuc.h | 472 ++++++++++++ .../drm/nouveau/core/engine/graph/fuc/hubnvf0.fuc | 40 + .../nouveau/core/engine/graph/fuc/hubnvf0.fuc.h | 854 +++++++++++++++++++++ .../drm/nouveau/core/engine/graph/fuc/macros.fuc | 1 + drivers/gpu/drm/nouveau/core/engine/graph/nvf0.c | 22 + 6 files changed, 1431 insertions(+) create mode 100644 drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvf0.fuc create mode 100644 drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvf0.fuc.h create mode 100644 drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvf0.fuc create mode 100644 drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvf0.fuc.h diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvf0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvf0.fuc new file mode 100644 index 000000000000..90bbe525b626 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvf0.fuc @@ -0,0 +1,42 @@ +/* + * Copyright 2013 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 + */ + +#define NV_PGRAPH_GPCX_UNK__SIZE 0x00000002 + +#define CHIPSET GK110 +#include "macros.fuc" + +.section #nvf0_grgpc_data +#define INCLUDE_DATA +#include "com.fuc" +#include "gpc.fuc" +#undef INCLUDE_DATA + +.section #nvf0_grgpc_code +#define INCLUDE_CODE +bra #init +#include "com.fuc" +#include "gpc.fuc" +.align 256 +#undef INCLUDE_CODE diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvf0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvf0.fuc.h new file mode 100644 index 000000000000..ecf9a5fb8557 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvf0.fuc.h @@ -0,0 +1,472 @@ +uint32_t nvf0_grgpc_data[] = { +/* 0x0000: gpc_mmio_list_head */ + 0x0000006c, +/* 0x0004: gpc_mmio_list_tail */ +/* 0x0004: tpc_mmio_list_head */ + 0x0000006c, +/* 0x0008: tpc_mmio_list_tail */ +/* 0x0008: unk_mmio_list_head */ + 0x0000006c, +/* 0x000c: unk_mmio_list_tail */ + 0x0000006c, +/* 0x0010: gpc_id */ + 0x00000000, +/* 0x0014: tpc_count */ + 0x00000000, +/* 0x0018: tpc_mask */ + 0x00000000, +/* 0x001c: unk_count */ + 0x00000001, +/* 0x0020: unk_mask */ + 0x00000001, +/* 0x0024: cmd_queue */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +uint32_t nvf0_grgpc_code[] = { + 0x03060ef5, +/* 0x0004: queue_put */ + 0x9800d898, + 0x86f001d9, + 0x0489b808, + 0xf00c1bf4, + 0x21f502f7, + 0x00f802ec, +/* 0x001c: queue_put_next */ + 0xb60798c4, + 0x8dbb0384, + 0x0880b600, + 0x80008e80, + 0x90b6018f, + 0x0f94f001, + 0xf801d980, +/* 0x0039: queue_get */ + 0x0131f400, + 0x9800d898, + 0x89b801d9, + 0x210bf404, + 0xb60789c4, + 0x9dbb0394, + 0x0890b600, + 0x98009e98, + 0x80b6019f, + 0x0f84f001, + 0xf400d880, +/* 0x0066: queue_get_done */ + 0x00f80132, +/* 0x0068: nv_rd32 */ + 0x0728b7f1, + 0xb906b4b6, + 0xc9f002ec, + 0x00bcd01f, +/* 0x0078: nv_rd32_wait */ + 0xc800bccf, + 0x1bf41fcc, + 0x06a7f0fa, + 0x010321f5, + 0xf840bfcf, +/* 0x008d: nv_wr32 */ + 0x28b7f100, + 0x06b4b607, + 0xb980bfd0, + 0xc9f002ec, + 0x1ec9f01f, +/* 0x00a3: nv_wr32_wait */ + 0xcf00bcd0, + 0xccc800bc, + 0xfa1bf41f, +/* 0x00ae: watchdog_reset */ + 0x87f100f8, + 0x84b60430, + 0x1ff9f006, + 0xf8008fd0, +/* 0x00bd: watchdog_clear */ + 0x3087f100, + 0x0684b604, + 0xf80080d0, +/* 0x00c9: wait_donez */ + 0x3c87f100, + 0x0684b608, + 0x99f094bd, + 0x0089d000, + 0x081887f1, + 0xd00684b6, +/* 0x00e2: wait_donez_ne */ + 0x87f1008a, + 0x84b60400, + 0x0088cf06, + 0xf4888aff, + 0x87f1f31b, + 0x84b6085c, + 0xf094bd06, + 0x89d00099, +/* 0x0103: wait_doneo */ + 0xf100f800, + 0xb6083c87, + 0x94bd0684, + 0xd00099f0, + 0x87f10089, + 0x84b60818, + 0x008ad006, +/* 0x011c: wait_doneo_e */ + 0x040087f1, + 0xcf0684b6, + 0x8aff0088, + 0xf30bf488, + 0x085c87f1, + 0xbd0684b6, + 0x0099f094, + 0xf80089d0, +/* 0x013d: mmctx_size */ +/* 0x013f: nv_mmctx_size_loop */ + 0x9894bd00, + 0x85b600e8, + 0x0180b61a, + 0xbb0284b6, + 0xe0b60098, + 0x04efb804, + 0xb9eb1bf4, + 0x00f8029f, +/* 0x015c: mmctx_xfer */ + 0x083c87f1, + 0xbd0684b6, + 0x0199f094, + 0xf10089d0, + 0xb6071087, + 0x94bd0684, + 0xf405bbfd, + 0x8bd0090b, + 0x0099f000, +/* 0x0180: mmctx_base_disabled */ + 0xf405eefd, + 0x8ed00c0b, + 0xc08fd080, +/* 0x018f: mmctx_multi_disabled */ + 0xb70199f0, + 0xc8010080, + 0xb4b600ab, + 0x0cb9f010, + 0xb601aec8, + 0xbefd11e4, + 0x008bd005, +/* 0x01a8: mmctx_exec_loop */ +/* 0x01a8: mmctx_wait_free */ + 0xf0008ecf, + 0x0bf41fe4, + 0x00ce98fa, + 0xd005e9fd, + 0xc0b6c08e, + 0x04cdb804, + 0xc8e81bf4, + 0x1bf402ab, +/* 0x01c9: mmctx_fini_wait */ + 0x008bcf18, + 0xb01fb4f0, + 0x1bf410b4, + 0x02a7f0f7, + 0xf4c921f4, +/* 0x01de: mmctx_stop */ + 0xabc81b0e, + 0x10b4b600, + 0xf00cb9f0, + 0x8bd012b9, +/* 0x01ed: mmctx_stop_wait */ + 0x008bcf00, + 0xf412bbc8, +/* 0x01f6: mmctx_done */ + 0x87f1fa1b, + 0x84b6085c, + 0xf094bd06, + 0x89d00199, +/* 0x0207: strand_wait */ + 0xf900f800, + 0x02a7f0a0, + 0xfcc921f4, +/* 0x0213: strand_pre */ + 0xf100f8a0, + 0xf04afc87, + 0x97f00283, + 0x0089d00c, + 0x020721f5, +/* 0x0226: strand_post */ + 0x87f100f8, + 0x83f04afc, + 0x0d97f002, + 0xf50089d0, + 0xf8020721, +/* 0x0239: strand_set */ + 0xfca7f100, + 0x02a3f04f, + 0x0500aba2, + 0xd00fc7f0, + 0xc7f000ac, + 0x00bcd00b, + 0x020721f5, + 0xf000aed0, + 0xbcd00ac7, + 0x0721f500, +/* 0x0263: strand_ctx_init */ + 0xf100f802, + 0xb6083c87, + 0x94bd0684, + 0xd00399f0, + 0x21f50089, + 0xe7f00213, + 0x3921f503, + 0xfca7f102, + 0x02a3f046, + 0x0400aba0, + 0xf040a0d0, + 0xbcd001c7, + 0x0721f500, + 0x010c9202, + 0xf000acd0, + 0xbcd002c7, + 0x0721f500, + 0x2621f502, + 0x8087f102, + 0x0684b608, + 0xb70089cf, + 0x95220080, +/* 0x02ba: ctx_init_strand_loop */ + 0x8ed008fe, + 0x408ed000, + 0xb6808acf, + 0xa0b606a5, + 0x00eabb01, + 0xb60480b6, + 0x1bf40192, + 0x08e4b6e8, + 0xf1f2efbc, + 0xb6085c87, + 0x94bd0684, + 0xd00399f0, + 0x00f80089, +/* 0x02ec: error */ + 0xe7f1e0f9, + 0xe3f09814, + 0x8d21f440, + 0x041ce0b7, + 0xf401f7f0, + 0xe0fc8d21, +/* 0x0306: init */ + 0x04bd00f8, + 0xf10004fe, + 0xf0120017, + 0x12d00227, + 0x2317f100, + 0x0010fe04, + 0x040017f1, + 0xf0c010d0, + 0x12d00427, + 0x1031f400, + 0x060817f1, + 0xcf0614b6, + 0x37f00012, + 0x1f24f001, + 0xb60432bb, + 0x02800132, + 0x06038005, + 0x040010b7, + 0x800012cf, + 0x27f10402, + 0x24b60800, + 0x4022cf06, + 0x47f134bd, + 0x44b60700, + 0x08259506, + 0xd00045d0, + 0x0e984045, + 0x010f9800, + 0x013d21f5, + 0xbb002fbb, + 0x0e98003f, + 0x020f9801, + 0x013d21f5, + 0xfd050e98, + 0x2ebb00ef, + 0x003ebb00, + 0x98020e98, + 0x21f5030f, + 0x0e98013d, + 0x00effd07, + 0xbb002ebb, + 0x40b7003e, + 0x35b61300, + 0x0043d002, + 0xb60825b6, + 0x20b60635, + 0x0130b601, + 0xb60824b6, + 0x2fb90834, + 0x6321f502, + 0x003fbb02, + 0x080017f1, + 0xd00614b6, + 0x10b74013, + 0x24bd0800, + 0xd01f29f0, +/* 0x03e6: main */ + 0x31f40012, + 0x0028f400, + 0xf424d7f0, + 0x01f43921, + 0x04e4b0f4, + 0xfe1e18f4, + 0x27f00181, + 0xfd20bd06, + 0xe4b60412, + 0x051efd01, + 0xf50018fe, + 0xf404a821, +/* 0x0416: main_not_ctx_xfer */ + 0xef94d30e, + 0x01f5f010, + 0x02ec21f5, +/* 0x0423: ih */ + 0xf9c60ef4, + 0x0188fe80, + 0x90f980f9, + 0xb0f9a0f9, + 0xe0f9d0f9, + 0x0acff0f9, + 0x04abc480, + 0xf11d0bf4, + 0xf01900b7, + 0xbecf24d7, + 0x00bfcf40, + 0xb70421f4, + 0xf00400b0, + 0xbed001e7, +/* 0x0459: ih_no_fifo */ + 0x400ad000, + 0xe0fcf0fc, + 0xb0fcd0fc, + 0x90fca0fc, + 0x88fe80fc, + 0xf480fc00, + 0x01f80032, +/* 0x0474: hub_barrier_done */ + 0x9801f7f0, + 0xfebb040e, + 0x18e7f104, + 0x40e3f094, + 0xf88d21f4, +/* 0x0489: ctx_redswitch */ + 0x14e7f100, + 0x06e4b606, + 0xd020f7f0, + 0xf7f000ef, +/* 0x0499: ctx_redswitch_delay */ + 0x01f2b608, + 0xf1fd1bf4, + 0xd00a20f7, + 0x00f800ef, +/* 0x04a8: ctx_xfer */ + 0x0a0417f1, + 0xd00614b6, + 0x11f4001f, + 0x8921f507, +/* 0x04b9: ctx_xfer_not_load */ + 0xfc17f104, + 0x0213f04a, + 0xd00c27f0, + 0x21f50012, + 0x27f10207, + 0x23f047fc, + 0x0020d002, + 0xb6012cf0, + 0x12d00320, + 0x01acf000, + 0xf002a5f0, + 0xb3f000b7, + 0x040c9850, + 0xbb0fc4b6, + 0x0c9800bc, + 0x010d9800, + 0xf500e7f0, + 0xf0015c21, + 0xb7f101ac, + 0xb3f04000, + 0x040c9850, + 0xbb0fc4b6, + 0x0c9800bc, + 0x020d9801, + 0xf1060f98, + 0xf50800e7, + 0xf0015c21, + 0xa5f001ac, + 0x00b7f104, + 0x50b3f030, + 0xb6040c98, + 0xbcbb0fc4, + 0x020c9800, + 0x98030d98, + 0xe7f1080f, + 0x21f50200, + 0x21f5015c, + 0x01f40207, + 0x1412f406, +/* 0x0554: ctx_xfer_post */ + 0x4afc17f1, + 0xf00213f0, + 0x12d00d27, + 0x0721f500, +/* 0x0565: ctx_xfer_done */ + 0x7421f502, + 0x0000f804, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvf0.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvf0.fuc new file mode 100644 index 000000000000..ec42ed29b50d --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvf0.fuc @@ -0,0 +1,40 @@ +/* + * Copyright 2013 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 + */ + +#define CHIPSET GK110 +#include "macros.fuc" + +.section #nvf0_grhub_data +#define INCLUDE_DATA +#include "com.fuc" +#include "hub.fuc" +#undef INCLUDE_DATA + +.section #nvf0_grhub_code +#define INCLUDE_CODE +bra #init +#include "com.fuc" +#include "hub.fuc" +.align 256 +#undef INCLUDE_CODE diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvf0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvf0.fuc.h new file mode 100644 index 000000000000..1b64fe55903a --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvf0.fuc.h @@ -0,0 +1,854 @@ +uint32_t nvf0_grhub_data[] = { +/* 0x0000: hub_mmio_list_head */ + 0x00000300, +/* 0x0004: hub_mmio_list_tail */ + 0x00000304, +/* 0x0008: gpc_count */ + 0x00000000, +/* 0x000c: rop_count */ + 0x00000000, +/* 0x0010: cmd_queue */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0058: ctx_current */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0100: chan_data */ +/* 0x0100: chan_mmio_count */ + 0x00000000, +/* 0x0104: chan_mmio_address */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0200: xfer_data */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +/* 0x0300: hub_mmio_list_base */ + 0x0417e91c, +}; + +uint32_t nvf0_grhub_code[] = { + 0x03090ef5, +/* 0x0004: queue_put */ + 0x9800d898, + 0x86f001d9, + 0x0489b808, + 0xf00c1bf4, + 0x21f502f7, + 0x00f802ec, +/* 0x001c: queue_put_next */ + 0xb60798c4, + 0x8dbb0384, + 0x0880b600, + 0x80008e80, + 0x90b6018f, + 0x0f94f001, + 0xf801d980, +/* 0x0039: queue_get */ + 0x0131f400, + 0x9800d898, + 0x89b801d9, + 0x210bf404, + 0xb60789c4, + 0x9dbb0394, + 0x0890b600, + 0x98009e98, + 0x80b6019f, + 0x0f84f001, + 0xf400d880, +/* 0x0066: queue_get_done */ + 0x00f80132, +/* 0x0068: nv_rd32 */ + 0x0728b7f1, + 0xb906b4b6, + 0xc9f002ec, + 0x00bcd01f, +/* 0x0078: nv_rd32_wait */ + 0xc800bccf, + 0x1bf41fcc, + 0x06a7f0fa, + 0x010321f5, + 0xf840bfcf, +/* 0x008d: nv_wr32 */ + 0x28b7f100, + 0x06b4b607, + 0xb980bfd0, + 0xc9f002ec, + 0x1ec9f01f, +/* 0x00a3: nv_wr32_wait */ + 0xcf00bcd0, + 0xccc800bc, + 0xfa1bf41f, +/* 0x00ae: watchdog_reset */ + 0x87f100f8, + 0x84b60430, + 0x1ff9f006, + 0xf8008fd0, +/* 0x00bd: watchdog_clear */ + 0x3087f100, + 0x0684b604, + 0xf80080d0, +/* 0x00c9: wait_donez */ + 0x3c87f100, + 0x0684b608, + 0x99f094bd, + 0x0089d000, + 0x081887f1, + 0xd00684b6, +/* 0x00e2: wait_donez_ne */ + 0x87f1008a, + 0x84b60400, + 0x0088cf06, + 0xf4888aff, + 0x87f1f31b, + 0x84b6085c, + 0xf094bd06, + 0x89d00099, +/* 0x0103: wait_doneo */ + 0xf100f800, + 0xb6083c87, + 0x94bd0684, + 0xd00099f0, + 0x87f10089, + 0x84b60818, + 0x008ad006, +/* 0x011c: wait_doneo_e */ + 0x040087f1, + 0xcf0684b6, + 0x8aff0088, + 0xf30bf488, + 0x085c87f1, + 0xbd0684b6, + 0x0099f094, + 0xf80089d0, +/* 0x013d: mmctx_size */ +/* 0x013f: nv_mmctx_size_loop */ + 0x9894bd00, + 0x85b600e8, + 0x0180b61a, + 0xbb0284b6, + 0xe0b60098, + 0x04efb804, + 0xb9eb1bf4, + 0x00f8029f, +/* 0x015c: mmctx_xfer */ + 0x083c87f1, + 0xbd0684b6, + 0x0199f094, + 0xf10089d0, + 0xb6071087, + 0x94bd0684, + 0xf405bbfd, + 0x8bd0090b, + 0x0099f000, +/* 0x0180: mmctx_base_disabled */ + 0xf405eefd, + 0x8ed00c0b, + 0xc08fd080, +/* 0x018f: mmctx_multi_disabled */ + 0xb70199f0, + 0xc8010080, + 0xb4b600ab, + 0x0cb9f010, + 0xb601aec8, + 0xbefd11e4, + 0x008bd005, +/* 0x01a8: mmctx_exec_loop */ +/* 0x01a8: mmctx_wait_free */ + 0xf0008ecf, + 0x0bf41fe4, + 0x00ce98fa, + 0xd005e9fd, + 0xc0b6c08e, + 0x04cdb804, + 0xc8e81bf4, + 0x1bf402ab, +/* 0x01c9: mmctx_fini_wait */ + 0x008bcf18, + 0xb01fb4f0, + 0x1bf410b4, + 0x02a7f0f7, + 0xf4c921f4, +/* 0x01de: mmctx_stop */ + 0xabc81b0e, + 0x10b4b600, + 0xf00cb9f0, + 0x8bd012b9, +/* 0x01ed: mmctx_stop_wait */ + 0x008bcf00, + 0xf412bbc8, +/* 0x01f6: mmctx_done */ + 0x87f1fa1b, + 0x84b6085c, + 0xf094bd06, + 0x89d00199, +/* 0x0207: strand_wait */ + 0xf900f800, + 0x02a7f0a0, + 0xfcc921f4, +/* 0x0213: strand_pre */ + 0xf100f8a0, + 0xf04afc87, + 0x97f00283, + 0x0089d00c, + 0x020721f5, +/* 0x0226: strand_post */ + 0x87f100f8, + 0x83f04afc, + 0x0d97f002, + 0xf50089d0, + 0xf8020721, +/* 0x0239: strand_set */ + 0xfca7f100, + 0x02a3f04f, + 0x0500aba2, + 0xd00fc7f0, + 0xc7f000ac, + 0x00bcd00b, + 0x020721f5, + 0xf000aed0, + 0xbcd00ac7, + 0x0721f500, +/* 0x0263: strand_ctx_init */ + 0xf100f802, + 0xb6083c87, + 0x94bd0684, + 0xd00399f0, + 0x21f50089, + 0xe7f00213, + 0x3921f503, + 0xfca7f102, + 0x02a3f046, + 0x0400aba0, + 0xf040a0d0, + 0xbcd001c7, + 0x0721f500, + 0x010c9202, + 0xf000acd0, + 0xbcd002c7, + 0x0721f500, + 0x2621f502, + 0x8087f102, + 0x0684b608, + 0xb70089cf, + 0x95220080, +/* 0x02ba: ctx_init_strand_loop */ + 0x8ed008fe, + 0x408ed000, + 0xb6808acf, + 0xa0b606a5, + 0x00eabb01, + 0xb60480b6, + 0x1bf40192, + 0x08e4b6e8, + 0xf1f2efbc, + 0xb6085c87, + 0x94bd0684, + 0xd00399f0, + 0x00f80089, +/* 0x02ec: error */ + 0xe7f1e0f9, + 0xe4b60814, + 0x00efd006, + 0x0c1ce7f1, + 0xf006e4b6, + 0xefd001f7, + 0xf8e0fc00, +/* 0x0309: init */ + 0xfe04bd00, + 0x07fe0004, + 0x0017f100, + 0x0227f012, + 0xf10012d0, + 0xfe058517, + 0x17f10010, + 0x10d00400, + 0x0437f1c0, + 0x0634b604, + 0x200327f1, + 0xf10032d0, + 0xd0200427, + 0x27f10132, + 0x32d0200b, + 0x0c27f102, + 0x0732d020, + 0x0c2427f1, + 0xb90624b6, + 0x23d00003, + 0x0427f100, + 0x0023f087, + 0xb70012d0, + 0xf0010012, + 0x12d00427, + 0x1031f400, + 0x9604e7f1, + 0xf440e3f0, + 0xf1c76821, + 0x03018090, + 0x801ff4f0, + 0x17f0020f, + 0x041fbb01, + 0xf10112b6, + 0xb6040c27, + 0x21d00624, + 0x4021d000, + 0x010017f1, + 0x98000e98, + 0x21f5010f, + 0x37f1013d, + 0x34b60700, + 0x08149506, + 0xd00034d0, + 0x30b74034, + 0x1fbb1300, + 0x02f5b600, + 0xb6003fd0, + 0x10b60815, + 0x0814b601, + 0xf5021fb9, + 0xbb026321, + 0x0398001f, + 0x0047f102, + 0x5043f020, +/* 0x03e4: init_gpc */ + 0x08044ea0, + 0xf4021fb9, + 0x4ea08d21, + 0xf4bd010c, + 0xa08d21f4, + 0xf401044e, + 0x4ea08d21, + 0xf7f00100, + 0x8d21f402, + 0x08004ea0, +/* 0x040c: init_gpc_wait */ + 0xc86821f4, + 0x0bf41fff, + 0x044ea0fa, + 0x6821f408, + 0xb7001fbb, + 0xb6800040, + 0x1bf40132, + 0x0027f1be, + 0x0624b608, + 0xb74021d0, + 0xbd080020, + 0x1f19f014, +/* 0x043f: main */ + 0xf40021d0, + 0x28f40031, + 0x10d7f000, + 0xf43921f4, + 0xe4b1f401, + 0x1bf54001, + 0x87f100d1, + 0x84b6083c, + 0xf094bd06, + 0x89d00499, + 0x0017f100, + 0x0614b60b, + 0xcf4012cf, + 0x13c80011, + 0x7e0bf41f, + 0xf41f23c8, + 0x20f95a0b, + 0xf10212b9, + 0xb6083c87, + 0x94bd0684, + 0xd00799f0, + 0x32f40089, + 0x0231f401, + 0x07c721f5, + 0x085c87f1, + 0xbd0684b6, + 0x0799f094, + 0xfc0089d0, + 0x3c87f120, + 0x0684b608, + 0x99f094bd, + 0x0089d006, + 0xf50131f4, + 0xf107c721, + 0xb6085c87, + 0x94bd0684, + 0xd00699f0, + 0x0ef40089, +/* 0x04d5: chsw_prev_no_next */ + 0xb920f931, + 0x32f40212, + 0x0232f401, + 0x07c721f5, + 0x17f120fc, + 0x14b60b00, + 0x0012d006, +/* 0x04f3: chsw_no_prev */ + 0xc8130ef4, + 0x0bf41f23, + 0x0131f40d, + 0xf50232f4, +/* 0x0503: chsw_done */ + 0xf107c721, + 0xb60b0c17, + 0x27f00614, + 0x0012d001, + 0x085c87f1, + 0xbd0684b6, + 0x0499f094, + 0xf50089d0, +/* 0x0523: main_not_ctx_switch */ + 0xb0ff200e, + 0x1bf401e4, + 0x02f2b90d, + 0x075b21f5, +/* 0x0533: main_not_ctx_chan */ + 0xb0420ef4, + 0x1bf402e4, + 0x3c87f12e, + 0x0684b608, + 0x99f094bd, + 0x0089d007, + 0xf40132f4, + 0x21f50232, + 0x87f107c7, + 0x84b6085c, + 0xf094bd06, + 0x89d00799, + 0x110ef400, +/* 0x0564: main_not_ctx_save */ + 0xf010ef94, + 0x21f501f5, + 0x0ef502ec, +/* 0x0572: main_done */ + 0x17f1fed1, + 0x14b60820, + 0xf024bd06, + 0x12d01f29, + 0xbe0ef500, +/* 0x0585: ih */ + 0xfe80f9fe, + 0x80f90188, + 0xa0f990f9, + 0xd0f9b0f9, + 0xf0f9e0f9, + 0xc4800acf, + 0x0bf404ab, + 0x00b7f11d, + 0x10d7f019, + 0xcf40becf, + 0x21f400bf, + 0x00b0b704, + 0x01e7f004, +/* 0x05bb: ih_no_fifo */ + 0xe400bed0, + 0xf40100ab, + 0xd7f00d0b, + 0x01e7f110, + 0x0421f440, +/* 0x05cc: ih_no_ctxsw */ + 0x0104b7f1, + 0xabffb0bd, + 0x0d0bf4b4, + 0x0c1ca7f1, + 0xd006a4b6, +/* 0x05e2: ih_no_other */ + 0x0ad000ab, + 0xfcf0fc40, + 0xfcd0fce0, + 0xfca0fcb0, + 0xfe80fc90, + 0x80fc0088, + 0xf80032f4, +/* 0x05fd: ctx_4170s */ + 0x70e7f101, + 0x40e3f041, + 0xf410f5f0, + 0x00f88d21, +/* 0x060c: ctx_4170w */ + 0x4170e7f1, + 0xf440e3f0, + 0xf4f06821, + 0xf31bf410, +/* 0x061e: ctx_redswitch */ + 0xe7f100f8, + 0xe4b60614, + 0x70f7f106, + 0x00efd002, +/* 0x062f: ctx_redswitch_delay */ + 0xb608f7f0, + 0x1bf401f2, + 0x70f7f1fd, + 0x00efd007, +/* 0x063e: ctx_86c */ + 0xe7f100f8, + 0xe4b6086c, + 0x00efd006, + 0x8a14e7f1, + 0xf440e3f0, + 0xe7f18d21, + 0xe3f0a86c, + 0x8d21f441, +/* 0x065e: ctx_load */ + 0x87f100f8, + 0x84b6083c, + 0xf094bd06, + 0x89d00599, + 0x0ca7f000, + 0xf1c921f4, + 0xb60a2417, + 0x10d00614, + 0x0037f100, + 0x0634b60b, + 0xf14032d0, + 0xb60a0c17, + 0x47f00614, + 0x0012d007, +/* 0x0697: ctx_chan_wait_0 */ + 0xcf4014d0, + 0x44f04014, + 0xfa1bf41f, + 0xfe0032d0, + 0x2af0000b, + 0x0424b61f, + 0xf10220b6, + 0xb6083c87, + 0x94bd0684, + 0xd00899f0, + 0x17f10089, + 0x14b60a04, + 0x0012d006, + 0x0a2017f1, + 0xf00614b6, + 0x23f10227, + 0x12d08000, + 0x1017f000, + 0x020027f1, + 0xfa0223f0, + 0x03f80512, + 0x085c87f1, + 0xbd0684b6, + 0x0899f094, + 0x980089d0, + 0x14b68101, + 0x80029818, + 0xfd0825b6, + 0x01800512, + 0x3c87f116, + 0x0684b608, + 0x99f094bd, + 0x0089d009, + 0x0a0427f1, + 0xd00624b6, + 0x27f00021, + 0x2017f101, + 0x0614b60a, + 0xf10012d0, + 0xf0010017, + 0x01fa0613, + 0xf103f805, + 0xb6085c87, + 0x94bd0684, + 0xd00999f0, + 0x87f10089, + 0x84b6085c, + 0xf094bd06, + 0x89d00599, +/* 0x075b: ctx_chan */ + 0xf500f800, + 0xf0065e21, + 0x21f40ca7, + 0x1017f1c9, + 0x0614b60a, + 0xd00527f0, +/* 0x0772: ctx_chan_wait */ + 0x12cf0012, + 0x0522fd00, + 0xf8fa1bf4, +/* 0x077d: ctx_mmio_exec */ + 0x41039800, + 0x0a0427f1, + 0xd00624b6, + 0x34bd0023, +/* 0x078c: ctx_mmio_loop */ + 0xf4ff34c4, + 0x57f10f1b, + 0x53f00200, + 0x0535fa06, +/* 0x079e: ctx_mmio_pull */ + 0x4e9803f8, + 0x814f9880, + 0xb68d21f4, + 0x12b60830, + 0xdf1bf401, +/* 0x07b0: ctx_mmio_done */ + 0xd0160398, + 0x00800023, + 0x0017f140, + 0x0613f001, + 0xf80601fa, +/* 0x07c7: ctx_xfer */ + 0xf100f803, + 0xb60c00f7, + 0xe7f006f4, + 0x80fed004, +/* 0x07d4: ctx_xfer_idle */ + 0xf100fecf, + 0xf42000e4, + 0x11f4f91b, + 0x0d02f406, +/* 0x07e4: ctx_xfer_pre */ + 0xf510f7f0, + 0xf4063e21, +/* 0x07ee: ctx_xfer_pre_load */ + 0xf7f01c11, + 0xfd21f502, + 0x0c21f505, + 0x1e21f506, + 0xf5f4bd06, + 0xf505fd21, +/* 0x0807: ctx_xfer_exec */ + 0x98065e21, + 0x27f11601, + 0x24b60414, + 0x0020d006, + 0xa500e7f1, + 0xb941e3f0, + 0x21f4021f, + 0x04e0b68d, + 0xf001fcf0, + 0x24b6022c, + 0x05f2fd01, + 0xf18d21f4, + 0xf04afc17, + 0x27f00213, + 0x0012d00c, + 0x020721f5, + 0x47fc27f1, + 0xd00223f0, + 0x2cf00020, + 0x0320b601, + 0xf00012d0, + 0xa5f001ac, + 0x00b7f006, + 0x98000c98, + 0xe7f0010d, + 0x5c21f500, + 0x08a7f001, + 0x010321f5, + 0x020721f5, + 0xf02201f4, + 0x21f40ca7, + 0x1017f1c9, + 0x0614b60a, + 0xd00527f0, +/* 0x088e: ctx_xfer_post_save_wait */ + 0x12cf0012, + 0x0522fd00, + 0xf4fa1bf4, +/* 0x089a: ctx_xfer_post */ + 0xf7f02e02, + 0xfd21f502, + 0xf5f4bd05, + 0xf5063e21, + 0xf5022621, + 0xbd060c21, + 0xfd21f5f4, + 0x1011f405, + 0xfd400198, + 0x0bf40511, + 0x7d21f507, +/* 0x08c5: ctx_xfer_no_post_mmio */ +/* 0x08c5: ctx_xfer_done */ + 0x0000f807, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/macros.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/macros.fuc index f73cf3efbc35..c74642827021 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/macros.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/macros.fuc @@ -27,6 +27,7 @@ #define GF100 0xc0 #define GF117 0xd7 #define GK100 0xe0 +#define GK110 0xf0 #define mmctx_data(r,c) .b32 (((c - 1) << 26) | r) #define queue_init .skip 72 // (2 * 4) + ((8 * 4) * 2) diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvf0.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvf0.c index 01294b0a0632..2f0ac7832234 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nvf0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvf0.c @@ -211,6 +211,26 @@ nvf0_graph_init_mmio[] = { NULL }; +#include "fuc/hubnvf0.fuc.h" + +static struct nvc0_graph_ucode +nvf0_graph_fecs_ucode = { + .code.data = nvf0_grhub_code, + .code.size = sizeof(nvf0_grhub_code), + .data.data = nvf0_grhub_data, + .data.size = sizeof(nvf0_grhub_data), +}; + +#include "fuc/gpcnvf0.fuc.h" + +static struct nvc0_graph_ucode +nvf0_graph_gpccs_ucode = { + .code.data = nvf0_grgpc_code, + .code.size = sizeof(nvf0_grgpc_code), + .data.data = nvf0_grgpc_data, + .data.size = sizeof(nvf0_grgpc_data), +}; + struct nouveau_oclass * nvf0_graph_oclass = &(struct nvc0_graph_oclass) { .base.handle = NV_ENGINE(GR, 0xf0), @@ -223,4 +243,6 @@ nvf0_graph_oclass = &(struct nvc0_graph_oclass) { .cclass = &nvf0_grctx_oclass, .sclass = nvf0_graph_sclass, .mmio = nvf0_graph_init_mmio, + .fecs.ucode = 0 ? &nvf0_graph_fecs_ucode : NULL, + .gpccs.ucode = &nvf0_graph_gpccs_ucode, }.base; -- cgit v1.2.3 From f8adeb82a931c780900f8133aeeea6f0b5154573 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 4 Jul 2013 13:49:47 +1000 Subject: drm/nvc0-/gr: remove hardcoding of UNK count/mask in GPCCS ucode Signed-off-by: Ben Skeggs --- .../gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc | 29 +++++++++- .../nouveau/core/engine/graph/fuc/gpcnvd7.fuc.h | 65 +++++++++++----------- .../nouveau/core/engine/graph/fuc/gpcnve0.fuc.h | 65 +++++++++++----------- .../nouveau/core/engine/graph/fuc/gpcnvf0.fuc.h | 65 +++++++++++----------- 4 files changed, 129 insertions(+), 95 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc index 32832720e420..b9dba107e78f 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc @@ -42,8 +42,8 @@ tpc_count: .b32 0 tpc_mask: .b32 0 #if NV_PGRAPH_GPCX_UNK__SIZE > 0 -unk_count: .b32 1 -unk_mask: .b32 1 +unk_count: .b32 0 +unk_mask: .b32 0 #endif cmd_queue: queue_init @@ -115,6 +115,31 @@ init: iord $r2 I[$r1 + 0x000] // MYINDEX st b32 D[$r0 + #gpc_id] $r2 +#if NV_PGRAPH_GPCX_UNK__SIZE > 0 + // figure out which, and how many, UNKs are actually present + mov $r14 0x0c30 + sethi $r14 0x500000 + clear b32 $r2 + clear b32 $r3 + clear b32 $r4 + init_unk_loop: + call #nv_rd32 + cmp b32 $r15 0 + bra z #init_unk_next + mov $r15 1 + shl b32 $r15 $r2 + or $r4 $r15 + add b32 $r3 1 + init_unk_next: + add b32 $r2 1 + add b32 $r14 4 + cmp b32 $r2 NV_PGRAPH_GPCX_UNK__SIZE + bra ne #init_unk_loop + init_unk_done: + st b32 D[$r0 + #unk_count] $r3 + st b32 D[$r0 + #unk_mask] $r4 +#endif + // initialise context base, and size tracking mov $r2 0x800 shl b32 $r2 6 diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvd7.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvd7.fuc.h index 95d13a1dbb0a..66d034faedd3 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvd7.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvd7.fuc.h @@ -16,9 +16,9 @@ uint32_t nvd7_grgpc_data[] = { /* 0x0018: tpc_mask */ 0x00000000, /* 0x001c: unk_count */ - 0x00000001, + 0x00000000, /* 0x0020: unk_mask */ - 0x00000001, + 0x00000000, /* 0x0024: cmd_queue */ 0x00000000, 0x00000000, @@ -271,7 +271,7 @@ uint32_t nvd7_grgpc_code[] = { 0xf10004fe, 0xf0120017, 0x12d00227, - 0x2317f100, + 0x5717f100, 0x0010fe04, 0x040017f1, 0xf0c010d0, @@ -286,7 +286,23 @@ uint32_t nvd7_grgpc_code[] = { 0x06038005, 0x040010b7, 0x800012cf, - 0x27f10402, + 0xe7f10402, + 0xe3f00c30, + 0xbd24bd50, +/* 0x035f: init_unk_loop */ + 0xf444bd34, + 0xf6b06821, + 0x0f0bf400, + 0xbb01f7f0, + 0x4ffd04f2, + 0x0130b605, +/* 0x0374: init_unk_next */ + 0xb60120b6, + 0x26b004e0, + 0xe21bf401, +/* 0x0380: init_unk_done */ + 0x80070380, + 0x27f10804, 0x24b60800, 0x4022cf06, 0x47f134bd, @@ -323,7 +339,7 @@ uint32_t nvd7_grgpc_code[] = { 0x10b74013, 0x24bd0800, 0xd01f29f0, -/* 0x03e6: main */ +/* 0x041a: main */ 0x31f40012, 0x0028f400, 0xf424d7f0, @@ -335,12 +351,12 @@ uint32_t nvd7_grgpc_code[] = { 0xe4b60412, 0x051efd01, 0xf50018fe, - 0xf404a821, -/* 0x0416: main_not_ctx_xfer */ + 0xf404dc21, +/* 0x044a: main_not_ctx_xfer */ 0xef94d30e, 0x01f5f010, 0x02ec21f5, -/* 0x0423: ih */ +/* 0x0457: ih */ 0xf9c60ef4, 0x0188fe80, 0x90f980f9, @@ -355,7 +371,7 @@ uint32_t nvd7_grgpc_code[] = { 0xb70421f4, 0xf00400b0, 0xbed001e7, -/* 0x0459: ih_no_fifo */ +/* 0x048d: ih_no_fifo */ 0x400ad000, 0xe0fcf0fc, 0xb0fcd0fc, @@ -363,28 +379,28 @@ uint32_t nvd7_grgpc_code[] = { 0x88fe80fc, 0xf480fc00, 0x01f80032, -/* 0x0474: hub_barrier_done */ +/* 0x04a8: hub_barrier_done */ 0x9801f7f0, 0xfebb040e, 0x18e7f104, 0x40e3f094, 0xf88d21f4, -/* 0x0489: ctx_redswitch */ +/* 0x04bd: ctx_redswitch */ 0x14e7f100, 0x06e4b606, 0xd020f7f0, 0xf7f000ef, -/* 0x0499: ctx_redswitch_delay */ +/* 0x04cd: ctx_redswitch_delay */ 0x01f2b608, 0xf1fd1bf4, 0xd00a20f7, 0x00f800ef, -/* 0x04a8: ctx_xfer */ +/* 0x04dc: ctx_xfer */ 0x0a0417f1, 0xd00614b6, 0x11f4001f, - 0x8921f507, -/* 0x04b9: ctx_xfer_not_load */ + 0xbd21f507, +/* 0x04ed: ctx_xfer_not_load */ 0xfc17f104, 0x0213f04a, 0xd00c27f0, @@ -424,13 +440,13 @@ uint32_t nvd7_grgpc_code[] = { 0x21f5015c, 0x01f40207, 0x1412f406, -/* 0x0554: ctx_xfer_post */ +/* 0x0588: ctx_xfer_post */ 0x4afc17f1, 0xf00213f0, 0x12d00d27, 0x0721f500, -/* 0x0565: ctx_xfer_done */ - 0x7421f502, +/* 0x0599: ctx_xfer_done */ + 0xa821f502, 0x0000f804, 0x00000000, 0x00000000, @@ -456,17 +472,4 @@ uint32_t nvd7_grgpc_code[] = { 0x00000000, 0x00000000, 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, }; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h index dc26c2822e9f..dc9c7784aeca 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h @@ -16,9 +16,9 @@ uint32_t nve0_grgpc_data[] = { /* 0x0018: tpc_mask */ 0x00000000, /* 0x001c: unk_count */ - 0x00000001, + 0x00000000, /* 0x0020: unk_mask */ - 0x00000001, + 0x00000000, /* 0x0024: cmd_queue */ 0x00000000, 0x00000000, @@ -271,7 +271,7 @@ uint32_t nve0_grgpc_code[] = { 0xf10004fe, 0xf0120017, 0x12d00227, - 0x2317f100, + 0x5717f100, 0x0010fe04, 0x040017f1, 0xf0c010d0, @@ -286,7 +286,23 @@ uint32_t nve0_grgpc_code[] = { 0x06038005, 0x040010b7, 0x800012cf, - 0x27f10402, + 0xe7f10402, + 0xe3f00c30, + 0xbd24bd50, +/* 0x035f: init_unk_loop */ + 0xf444bd34, + 0xf6b06821, + 0x0f0bf400, + 0xbb01f7f0, + 0x4ffd04f2, + 0x0130b605, +/* 0x0374: init_unk_next */ + 0xb60120b6, + 0x26b004e0, + 0xe21bf401, +/* 0x0380: init_unk_done */ + 0x80070380, + 0x27f10804, 0x24b60800, 0x4022cf06, 0x47f134bd, @@ -323,7 +339,7 @@ uint32_t nve0_grgpc_code[] = { 0x10b74013, 0x24bd0800, 0xd01f29f0, -/* 0x03e6: main */ +/* 0x041a: main */ 0x31f40012, 0x0028f400, 0xf424d7f0, @@ -335,12 +351,12 @@ uint32_t nve0_grgpc_code[] = { 0xe4b60412, 0x051efd01, 0xf50018fe, - 0xf404a821, -/* 0x0416: main_not_ctx_xfer */ + 0xf404dc21, +/* 0x044a: main_not_ctx_xfer */ 0xef94d30e, 0x01f5f010, 0x02ec21f5, -/* 0x0423: ih */ +/* 0x0457: ih */ 0xf9c60ef4, 0x0188fe80, 0x90f980f9, @@ -355,7 +371,7 @@ uint32_t nve0_grgpc_code[] = { 0xb70421f4, 0xf00400b0, 0xbed001e7, -/* 0x0459: ih_no_fifo */ +/* 0x048d: ih_no_fifo */ 0x400ad000, 0xe0fcf0fc, 0xb0fcd0fc, @@ -363,28 +379,28 @@ uint32_t nve0_grgpc_code[] = { 0x88fe80fc, 0xf480fc00, 0x01f80032, -/* 0x0474: hub_barrier_done */ +/* 0x04a8: hub_barrier_done */ 0x9801f7f0, 0xfebb040e, 0x18e7f104, 0x40e3f094, 0xf88d21f4, -/* 0x0489: ctx_redswitch */ +/* 0x04bd: ctx_redswitch */ 0x14e7f100, 0x06e4b606, 0xd020f7f0, 0xf7f000ef, -/* 0x0499: ctx_redswitch_delay */ +/* 0x04cd: ctx_redswitch_delay */ 0x01f2b608, 0xf1fd1bf4, 0xd00a20f7, 0x00f800ef, -/* 0x04a8: ctx_xfer */ +/* 0x04dc: ctx_xfer */ 0x0a0417f1, 0xd00614b6, 0x11f4001f, - 0x8921f507, -/* 0x04b9: ctx_xfer_not_load */ + 0xbd21f507, +/* 0x04ed: ctx_xfer_not_load */ 0xfc17f104, 0x0213f04a, 0xd00c27f0, @@ -424,13 +440,13 @@ uint32_t nve0_grgpc_code[] = { 0x21f5015c, 0x01f40207, 0x1412f406, -/* 0x0554: ctx_xfer_post */ +/* 0x0588: ctx_xfer_post */ 0x4afc17f1, 0xf00213f0, 0x12d00d27, 0x0721f500, -/* 0x0565: ctx_xfer_done */ - 0x7421f502, +/* 0x0599: ctx_xfer_done */ + 0xa821f502, 0x0000f804, 0x00000000, 0x00000000, @@ -456,17 +472,4 @@ uint32_t nve0_grgpc_code[] = { 0x00000000, 0x00000000, 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, }; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvf0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvf0.fuc.h index ecf9a5fb8557..cbbed6aa080b 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvf0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvf0.fuc.h @@ -16,9 +16,9 @@ uint32_t nvf0_grgpc_data[] = { /* 0x0018: tpc_mask */ 0x00000000, /* 0x001c: unk_count */ - 0x00000001, + 0x00000000, /* 0x0020: unk_mask */ - 0x00000001, + 0x00000000, /* 0x0024: cmd_queue */ 0x00000000, 0x00000000, @@ -271,7 +271,7 @@ uint32_t nvf0_grgpc_code[] = { 0xf10004fe, 0xf0120017, 0x12d00227, - 0x2317f100, + 0x5717f100, 0x0010fe04, 0x040017f1, 0xf0c010d0, @@ -286,7 +286,23 @@ uint32_t nvf0_grgpc_code[] = { 0x06038005, 0x040010b7, 0x800012cf, - 0x27f10402, + 0xe7f10402, + 0xe3f00c30, + 0xbd24bd50, +/* 0x035f: init_unk_loop */ + 0xf444bd34, + 0xf6b06821, + 0x0f0bf400, + 0xbb01f7f0, + 0x4ffd04f2, + 0x0130b605, +/* 0x0374: init_unk_next */ + 0xb60120b6, + 0x26b004e0, + 0xe21bf402, +/* 0x0380: init_unk_done */ + 0x80070380, + 0x27f10804, 0x24b60800, 0x4022cf06, 0x47f134bd, @@ -323,7 +339,7 @@ uint32_t nvf0_grgpc_code[] = { 0x10b74013, 0x24bd0800, 0xd01f29f0, -/* 0x03e6: main */ +/* 0x041a: main */ 0x31f40012, 0x0028f400, 0xf424d7f0, @@ -335,12 +351,12 @@ uint32_t nvf0_grgpc_code[] = { 0xe4b60412, 0x051efd01, 0xf50018fe, - 0xf404a821, -/* 0x0416: main_not_ctx_xfer */ + 0xf404dc21, +/* 0x044a: main_not_ctx_xfer */ 0xef94d30e, 0x01f5f010, 0x02ec21f5, -/* 0x0423: ih */ +/* 0x0457: ih */ 0xf9c60ef4, 0x0188fe80, 0x90f980f9, @@ -355,7 +371,7 @@ uint32_t nvf0_grgpc_code[] = { 0xb70421f4, 0xf00400b0, 0xbed001e7, -/* 0x0459: ih_no_fifo */ +/* 0x048d: ih_no_fifo */ 0x400ad000, 0xe0fcf0fc, 0xb0fcd0fc, @@ -363,28 +379,28 @@ uint32_t nvf0_grgpc_code[] = { 0x88fe80fc, 0xf480fc00, 0x01f80032, -/* 0x0474: hub_barrier_done */ +/* 0x04a8: hub_barrier_done */ 0x9801f7f0, 0xfebb040e, 0x18e7f104, 0x40e3f094, 0xf88d21f4, -/* 0x0489: ctx_redswitch */ +/* 0x04bd: ctx_redswitch */ 0x14e7f100, 0x06e4b606, 0xd020f7f0, 0xf7f000ef, -/* 0x0499: ctx_redswitch_delay */ +/* 0x04cd: ctx_redswitch_delay */ 0x01f2b608, 0xf1fd1bf4, 0xd00a20f7, 0x00f800ef, -/* 0x04a8: ctx_xfer */ +/* 0x04dc: ctx_xfer */ 0x0a0417f1, 0xd00614b6, 0x11f4001f, - 0x8921f507, -/* 0x04b9: ctx_xfer_not_load */ + 0xbd21f507, +/* 0x04ed: ctx_xfer_not_load */ 0xfc17f104, 0x0213f04a, 0xd00c27f0, @@ -424,13 +440,13 @@ uint32_t nvf0_grgpc_code[] = { 0x21f5015c, 0x01f40207, 0x1412f406, -/* 0x0554: ctx_xfer_post */ +/* 0x0588: ctx_xfer_post */ 0x4afc17f1, 0xf00213f0, 0x12d00d27, 0x0721f500, -/* 0x0565: ctx_xfer_done */ - 0x7421f502, +/* 0x0599: ctx_xfer_done */ + 0xa821f502, 0x0000f804, 0x00000000, 0x00000000, @@ -456,17 +472,4 @@ uint32_t nvf0_grgpc_code[] = { 0x00000000, 0x00000000, 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, }; -- cgit v1.2.3 From 60a4acd7c9a5cde05ec17685072504f991fea321 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 4 Jul 2013 14:56:38 +1000 Subject: drm/nvf0-/gr: ctxsw scratch reg count got bumped to 16 Signed-off-by: Ben Skeggs --- .../gpu/drm/nouveau/core/engine/graph/fuc/com.fuc | 8 +- .../gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc | 12 +- .../nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h | 522 +++++----- .../nouveau/core/engine/graph/fuc/gpcnvd7.fuc.h | 580 +++++------ .../nouveau/core/engine/graph/fuc/gpcnve0.fuc.h | 580 +++++------ .../nouveau/core/engine/graph/fuc/gpcnvf0.fuc.h | 580 +++++------ .../gpu/drm/nouveau/core/engine/graph/fuc/hub.fuc | 22 +- .../nouveau/core/engine/graph/fuc/hubnvc0.fuc.h | 1012 +++++++++++--------- .../nouveau/core/engine/graph/fuc/hubnvd7.fuc.h | 1012 +++++++++++--------- .../nouveau/core/engine/graph/fuc/hubnve0.fuc.h | 982 ++++++++++--------- .../nouveau/core/engine/graph/fuc/hubnvf0.fuc.h | 982 ++++++++++--------- .../drm/nouveau/core/engine/graph/fuc/macros.fuc | 45 +- 12 files changed, 3303 insertions(+), 3034 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/com.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/com.fuc index da18885c559c..5d24b6de16cc 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/com.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/com.fuc @@ -149,13 +149,9 @@ watchdog_clear: // wait_donez: trace_set(T_WAIT); - mov $r8 0x818 - shl b32 $r8 6 - iowr I[$r8 + 0x000] $r10 + nv_iowr(NV_PGRAPH_FECS_CC_SCRATCH_VAL(6), 0, $r10) wait_donez_ne: - mov $r8 0x400 - shl b32 $r8 6 - iord $r8 I[$r8 + 0x000] + nv_iord($r8, NV_PGRAPH_FECS_SIGNAL, 0) xbit $r8 $r8 $r10 bra ne #wait_donez_ne trace_clr(T_WAIT) diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc index b9dba107e78f..b52f4a8b8699 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc @@ -141,9 +141,7 @@ init: #endif // initialise context base, and size tracking - mov $r2 0x800 - shl b32 $r2 6 - iord $r2 I[$r2 + 0x100] // CC_SCRATCH[1], initial base + nv_iord($r2, NV_PGRAPH_GPCX_GPCCS_CC_SCRATCH_VAL(1), 0) clear b32 $r3 // track GPC context size here // set mmctx base addresses now so we don't have to do it later, @@ -198,13 +196,10 @@ init: add b32 $r3 $r15 // save context size, and tell HUB we're done - mov $r1 0x800 - shl b32 $r1 6 - iowr I[$r1 + 0x100] $r3 // CC_SCRATCH[1] = context size - add b32 $r1 0x800 + nv_iowr(NV_PGRAPH_GPCX_GPCCS_CC_SCRATCH_VAL(1), 0, $r3) clear b32 $r2 bset $r2 31 - iowr I[$r1 + 0x000] $r2 // CC_SCRATCH[0] |= 0x80000000 + nv_iowr(NV_PGRAPH_GPCX_GPCCS_CC_SCRATCH_SET(0), 0, $r2) // Main program loop, very simple, sleeps until woken up by the interrupt // handler, pulls a command from the queue and executes its handler @@ -249,6 +244,7 @@ ih: push $r13 push $r14 push $r15 + clear b32 $r0 // incoming fifo command? iord $r10 I[$r0 + 0x200] // INTR diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h index 50675f30d24d..2afe75ce89e9 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h @@ -37,14 +37,14 @@ uint32_t nvc0_grgpc_data[] = { }; uint32_t nvc0_grgpc_code[] = { - 0x03060ef5, + 0x03180ef5, /* 0x0004: queue_put */ 0x9800d898, 0x86f001d9, 0x0489b808, 0xf00c1bf4, 0x21f502f7, - 0x00f802ec, + 0x00f802fe, /* 0x001c: queue_put_next */ 0xb60798c4, 0x8dbb0384, @@ -76,7 +76,7 @@ uint32_t nvc0_grgpc_code[] = { 0xc800bccf, 0x1bf41fcc, 0x06a7f0fa, - 0x010321f5, + 0x010921f5, 0xf840bfcf, /* 0x008d: nv_wr32 */ 0x28b7f100, @@ -98,63 +98,66 @@ uint32_t nvc0_grgpc_code[] = { 0x0684b604, 0xf80080d0, /* 0x00c9: wait_donez */ - 0x3c87f100, - 0x0684b608, - 0x99f094bd, - 0x0089d000, - 0x081887f1, - 0xd00684b6, -/* 0x00e2: wait_donez_ne */ - 0x87f1008a, - 0x84b60400, - 0x0088cf06, + 0xf094bd00, + 0x07f10099, + 0x03f00f00, + 0x0009d002, + 0x07f104bd, + 0x03f00600, + 0x000ad002, +/* 0x00e6: wait_donez_ne */ + 0x87f104bd, + 0x83f00000, + 0x0088cf01, 0xf4888aff, - 0x87f1f31b, - 0x84b6085c, - 0xf094bd06, - 0x89d00099, -/* 0x0103: wait_doneo */ - 0xf100f800, - 0xb6083c87, - 0x94bd0684, - 0xd00099f0, - 0x87f10089, + 0x94bdf31b, + 0xf10099f0, + 0xf0170007, + 0x09d00203, + 0xf804bd00, +/* 0x0109: wait_doneo */ + 0xf094bd00, + 0x07f10099, + 0x03f00f00, + 0x0009d002, + 0x87f104bd, 0x84b60818, 0x008ad006, -/* 0x011c: wait_doneo_e */ +/* 0x0124: wait_doneo_e */ 0x040087f1, 0xcf0684b6, 0x8aff0088, 0xf30bf488, - 0x085c87f1, - 0xbd0684b6, - 0x0099f094, - 0xf80089d0, -/* 0x013d: mmctx_size */ -/* 0x013f: nv_mmctx_size_loop */ - 0x9894bd00, - 0x85b600e8, - 0x0180b61a, - 0xbb0284b6, - 0xe0b60098, - 0x04efb804, - 0xb9eb1bf4, - 0x00f8029f, -/* 0x015c: mmctx_xfer */ - 0x083c87f1, - 0xbd0684b6, - 0x0199f094, - 0xf10089d0, + 0x99f094bd, + 0x0007f100, + 0x0203f017, + 0xbd0009d0, +/* 0x0147: mmctx_size */ + 0xbd00f804, +/* 0x0149: nv_mmctx_size_loop */ + 0x00e89894, + 0xb61a85b6, + 0x84b60180, + 0x0098bb02, + 0xb804e0b6, + 0x1bf404ef, + 0x029fb9eb, +/* 0x0166: mmctx_xfer */ + 0x94bd00f8, + 0xf10199f0, + 0xf00f0007, + 0x09d00203, + 0xf104bd00, 0xb6071087, 0x94bd0684, 0xf405bbfd, 0x8bd0090b, 0x0099f000, -/* 0x0180: mmctx_base_disabled */ +/* 0x018c: mmctx_base_disabled */ 0xf405eefd, 0x8ed00c0b, 0xc08fd080, -/* 0x018f: mmctx_multi_disabled */ +/* 0x019b: mmctx_multi_disabled */ 0xb70199f0, 0xc8010080, 0xb4b600ab, @@ -162,8 +165,8 @@ uint32_t nvc0_grgpc_code[] = { 0xb601aec8, 0xbefd11e4, 0x008bd005, -/* 0x01a8: mmctx_exec_loop */ -/* 0x01a8: mmctx_wait_free */ +/* 0x01b4: mmctx_exec_loop */ +/* 0x01b4: mmctx_wait_free */ 0xf0008ecf, 0x0bf41fe4, 0x00ce98fa, @@ -172,76 +175,77 @@ uint32_t nvc0_grgpc_code[] = { 0x04cdb804, 0xc8e81bf4, 0x1bf402ab, -/* 0x01c9: mmctx_fini_wait */ +/* 0x01d5: mmctx_fini_wait */ 0x008bcf18, 0xb01fb4f0, 0x1bf410b4, 0x02a7f0f7, 0xf4c921f4, -/* 0x01de: mmctx_stop */ +/* 0x01ea: mmctx_stop */ 0xabc81b0e, 0x10b4b600, 0xf00cb9f0, 0x8bd012b9, -/* 0x01ed: mmctx_stop_wait */ +/* 0x01f9: mmctx_stop_wait */ 0x008bcf00, 0xf412bbc8, -/* 0x01f6: mmctx_done */ - 0x87f1fa1b, - 0x84b6085c, - 0xf094bd06, - 0x89d00199, -/* 0x0207: strand_wait */ - 0xf900f800, - 0x02a7f0a0, - 0xfcc921f4, -/* 0x0213: strand_pre */ - 0xf100f8a0, - 0xf04afc87, - 0x97f00283, - 0x0089d00c, - 0x020721f5, -/* 0x0226: strand_post */ - 0x87f100f8, - 0x83f04afc, - 0x0d97f002, - 0xf50089d0, - 0xf8020721, -/* 0x0239: strand_set */ - 0xfca7f100, - 0x02a3f04f, - 0x0500aba2, - 0xd00fc7f0, - 0xc7f000ac, - 0x00bcd00b, - 0x020721f5, - 0xf000aed0, - 0xbcd00ac7, - 0x0721f500, -/* 0x0263: strand_ctx_init */ - 0xf100f802, - 0xb6083c87, - 0x94bd0684, - 0xd00399f0, +/* 0x0202: mmctx_done */ + 0x94bdfa1b, + 0xf10199f0, + 0xf0170007, + 0x09d00203, + 0xf804bd00, +/* 0x0215: strand_wait */ + 0xf0a0f900, + 0x21f402a7, + 0xf8a0fcc9, +/* 0x0221: strand_pre */ + 0xfc87f100, + 0x0283f04a, + 0xd00c97f0, 0x21f50089, - 0xe7f00213, - 0x3921f503, + 0x00f80215, +/* 0x0234: strand_post */ + 0x4afc87f1, + 0xf00283f0, + 0x89d00d97, + 0x1521f500, +/* 0x0247: strand_set */ + 0xf100f802, + 0xf04ffca7, + 0xaba202a3, + 0xc7f00500, + 0x00acd00f, + 0xd00bc7f0, + 0x21f500bc, + 0xaed00215, + 0x0ac7f000, + 0xf500bcd0, + 0xf8021521, +/* 0x0271: strand_ctx_init */ + 0xf094bd00, + 0x07f10399, + 0x03f00f00, + 0x0009d002, + 0x21f504bd, + 0xe7f00221, + 0x4721f503, 0xfca7f102, 0x02a3f046, 0x0400aba0, 0xf040a0d0, 0xbcd001c7, - 0x0721f500, + 0x1521f500, 0x010c9202, 0xf000acd0, 0xbcd002c7, - 0x0721f500, - 0x2621f502, + 0x1521f500, + 0x3421f502, 0x8087f102, 0x0684b608, 0xb70089cf, 0x95220080, -/* 0x02ba: ctx_init_strand_loop */ +/* 0x02ca: ctx_init_strand_loop */ 0x8ed008fe, 0x408ed000, 0xb6808acf, @@ -250,175 +254,171 @@ uint32_t nvc0_grgpc_code[] = { 0xb60480b6, 0x1bf40192, 0x08e4b6e8, - 0xf1f2efbc, - 0xb6085c87, - 0x94bd0684, - 0xd00399f0, - 0x00f80089, -/* 0x02ec: error */ - 0xe7f1e0f9, - 0xe3f09814, - 0x8d21f440, - 0x041ce0b7, - 0xf401f7f0, - 0xe0fc8d21, -/* 0x0306: init */ - 0x04bd00f8, - 0xf10004fe, - 0xf0120017, - 0x12d00227, - 0x0d17f100, - 0x0010fe04, - 0x040017f1, - 0xf0c010d0, - 0x12d00427, - 0x1031f400, - 0x060817f1, - 0xcf0614b6, - 0x37f00012, - 0x1f24f001, - 0xb60432bb, - 0x02800132, - 0x06038005, - 0x040010b7, - 0x800012cf, - 0x27f10402, - 0x24b60800, - 0x4022cf06, - 0x47f134bd, - 0x44b60700, - 0x08259506, - 0xd00045d0, - 0x0e984045, - 0x010f9800, - 0x013d21f5, - 0xbb002fbb, - 0x0e98003f, - 0x020f9801, - 0x013d21f5, - 0xfd050e98, - 0x2ebb00ef, - 0x003ebb00, - 0x130040b7, - 0xd00235b6, - 0x25b60043, - 0x0635b608, - 0xb60120b6, - 0x24b60130, - 0x0834b608, - 0xf5022fb9, - 0xbb026321, - 0x17f1003f, - 0x14b60800, - 0x4013d006, - 0x080010b7, + 0xbdf2efbc, + 0x0399f094, + 0x170007f1, + 0xd00203f0, + 0x04bd0009, +/* 0x02fe: error */ + 0xe0f900f8, + 0x9814e7f1, + 0xf440e3f0, + 0xe0b78d21, + 0xf7f0041c, + 0x8d21f401, + 0x00f8e0fc, +/* 0x0318: init */ + 0x04fe04bd, + 0x0017f100, + 0x0227f012, + 0xf10012d0, + 0xfe042617, + 0x17f10010, + 0x10d00400, + 0x0427f0c0, + 0xf40012d0, + 0x17f11031, + 0x14b60608, + 0x0012cf06, + 0xf00137f0, + 0x32bb1f24, + 0x0132b604, + 0x80050280, + 0x10b70603, + 0x12cf0400, + 0x04028000, + 0x010027f1, + 0xcf0223f0, + 0x34bd0022, + 0x070047f1, + 0x950644b6, + 0x45d00825, + 0x4045d000, + 0x98000e98, + 0x21f5010f, + 0x2fbb0147, + 0x003fbb00, + 0x98010e98, + 0x21f5020f, + 0x0e980147, + 0x00effd05, + 0xbb002ebb, + 0x40b7003e, + 0x35b61300, + 0x0043d002, + 0xb60825b6, + 0x20b60635, + 0x0130b601, + 0xb60824b6, + 0x2fb90834, + 0x7121f502, + 0x003fbb02, + 0x010007f1, + 0xd00203f0, + 0x04bd0003, 0x29f024bd, - 0x0012d01f, -/* 0x03d0: main */ - 0xf40031f4, - 0xd7f00028, - 0x3921f41c, - 0xb0f401f4, - 0x18f404e4, - 0x0181fe1e, - 0xbd0627f0, - 0x0412fd20, - 0xfd01e4b6, - 0x18fe051e, - 0x9221f500, - 0xd30ef404, -/* 0x0400: main_not_ctx_xfer */ - 0xf010ef94, - 0x21f501f5, - 0x0ef402ec, -/* 0x040d: ih */ - 0xfe80f9c6, - 0x80f90188, - 0xa0f990f9, - 0xd0f9b0f9, - 0xf0f9e0f9, - 0xc4800acf, - 0x0bf404ab, - 0x00b7f11d, - 0x1cd7f019, - 0xcf40becf, - 0x21f400bf, - 0x00b0b704, - 0x01e7f004, -/* 0x0443: ih_no_fifo */ - 0xd000bed0, - 0xf0fc400a, - 0xd0fce0fc, - 0xa0fcb0fc, - 0x80fc90fc, - 0xfc0088fe, - 0x0032f480, -/* 0x045e: hub_barrier_done */ - 0xf7f001f8, - 0x040e9801, - 0xf104febb, - 0xf09418e7, - 0x21f440e3, -/* 0x0473: ctx_redswitch */ - 0xf100f88d, - 0xb60614e7, - 0xf7f006e4, - 0x00efd020, -/* 0x0483: ctx_redswitch_delay */ - 0xb608f7f0, - 0x1bf401f2, - 0x20f7f1fd, - 0x00efd00a, -/* 0x0492: ctx_xfer */ - 0x17f100f8, - 0x14b60a04, - 0x001fd006, - 0xf50711f4, -/* 0x04a3: ctx_xfer_not_load */ - 0xf1047321, - 0xf04afc17, - 0x27f00213, - 0x0012d00c, - 0x020721f5, - 0x47fc27f1, - 0xd00223f0, - 0x2cf00020, - 0x0320b601, - 0xf00012d0, - 0xa5f001ac, - 0x00b7f002, - 0x9850b3f0, - 0xc4b6040c, - 0x00bcbb0f, - 0x98000c98, - 0xe7f0010d, - 0x5c21f500, - 0x01acf001, - 0x4000b7f1, - 0x9850b3f0, - 0xc4b6040c, - 0x00bcbb0f, - 0x98010c98, - 0x0f98020d, - 0x00e7f106, - 0x5c21f508, - 0x0721f501, - 0x0601f402, -/* 0x0517: ctx_xfer_post */ - 0xf11412f4, - 0xf04afc17, - 0x27f00213, - 0x0012d00d, - 0x020721f5, -/* 0x0528: ctx_xfer_done */ - 0x045e21f5, - 0x000000f8, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, + 0x0007f11f, + 0x0203f008, + 0xbd0002d0, +/* 0x03e9: main */ + 0x0031f404, + 0xf00028f4, + 0x21f41cd7, + 0xf401f439, + 0xf404e4b0, + 0x81fe1e18, + 0x0627f001, + 0x12fd20bd, + 0x01e4b604, + 0xfe051efd, + 0x21f50018, + 0x0ef404ad, +/* 0x0419: main_not_ctx_xfer */ + 0x10ef94d3, + 0xf501f5f0, + 0xf402fe21, +/* 0x0426: ih */ + 0x80f9c60e, + 0xf90188fe, + 0xf990f980, + 0xf9b0f9a0, + 0xf9e0f9d0, + 0xcf04bdf0, + 0xabc4800a, + 0x1d0bf404, + 0x1900b7f1, + 0xcf1cd7f0, + 0xbfcf40be, + 0x0421f400, + 0x0400b0b7, + 0xd001e7f0, +/* 0x045e: ih_no_fifo */ + 0x0ad000be, + 0xfcf0fc40, + 0xfcd0fce0, + 0xfca0fcb0, + 0xfe80fc90, + 0x80fc0088, + 0xf80032f4, +/* 0x0479: hub_barrier_done */ + 0x01f7f001, + 0xbb040e98, + 0xe7f104fe, + 0xe3f09418, + 0x8d21f440, +/* 0x048e: ctx_redswitch */ + 0xe7f100f8, + 0xe4b60614, + 0x20f7f006, + 0xf000efd0, +/* 0x049e: ctx_redswitch_delay */ + 0xf2b608f7, + 0xfd1bf401, + 0x0a20f7f1, + 0xf800efd0, +/* 0x04ad: ctx_xfer */ + 0x0417f100, + 0x0614b60a, + 0xf4001fd0, + 0x21f50711, +/* 0x04be: ctx_xfer_not_load */ + 0x17f1048e, + 0x13f04afc, + 0x0c27f002, + 0xf50012d0, + 0xf1021521, + 0xf047fc27, + 0x20d00223, + 0x012cf000, + 0xd00320b6, + 0xacf00012, + 0x02a5f001, + 0xf000b7f0, + 0x0c9850b3, + 0x0fc4b604, + 0x9800bcbb, + 0x0d98000c, + 0x00e7f001, + 0x016621f5, + 0xf101acf0, + 0xf04000b7, + 0x0c9850b3, + 0x0fc4b604, + 0x9800bcbb, + 0x0d98010c, + 0x060f9802, + 0x0800e7f1, + 0x016621f5, + 0x021521f5, + 0xf40601f4, +/* 0x0532: ctx_xfer_post */ + 0x17f11412, + 0x13f04afc, + 0x0d27f002, + 0xf50012d0, +/* 0x0543: ctx_xfer_done */ + 0xf5021521, + 0xf8047921, 0x00000000, 0x00000000, 0x00000000, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvd7.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvd7.fuc.h index 66d034faedd3..dd346c2a1624 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvd7.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvd7.fuc.h @@ -41,14 +41,14 @@ uint32_t nvd7_grgpc_data[] = { }; uint32_t nvd7_grgpc_code[] = { - 0x03060ef5, + 0x03180ef5, /* 0x0004: queue_put */ 0x9800d898, 0x86f001d9, 0x0489b808, 0xf00c1bf4, 0x21f502f7, - 0x00f802ec, + 0x00f802fe, /* 0x001c: queue_put_next */ 0xb60798c4, 0x8dbb0384, @@ -80,7 +80,7 @@ uint32_t nvd7_grgpc_code[] = { 0xc800bccf, 0x1bf41fcc, 0x06a7f0fa, - 0x010321f5, + 0x010921f5, 0xf840bfcf, /* 0x008d: nv_wr32 */ 0x28b7f100, @@ -102,63 +102,66 @@ uint32_t nvd7_grgpc_code[] = { 0x0684b604, 0xf80080d0, /* 0x00c9: wait_donez */ - 0x3c87f100, - 0x0684b608, - 0x99f094bd, - 0x0089d000, - 0x081887f1, - 0xd00684b6, -/* 0x00e2: wait_donez_ne */ - 0x87f1008a, - 0x84b60400, - 0x0088cf06, + 0xf094bd00, + 0x07f10099, + 0x03f00f00, + 0x0009d002, + 0x07f104bd, + 0x03f00600, + 0x000ad002, +/* 0x00e6: wait_donez_ne */ + 0x87f104bd, + 0x83f00000, + 0x0088cf01, 0xf4888aff, - 0x87f1f31b, - 0x84b6085c, - 0xf094bd06, - 0x89d00099, -/* 0x0103: wait_doneo */ - 0xf100f800, - 0xb6083c87, - 0x94bd0684, - 0xd00099f0, - 0x87f10089, + 0x94bdf31b, + 0xf10099f0, + 0xf0170007, + 0x09d00203, + 0xf804bd00, +/* 0x0109: wait_doneo */ + 0xf094bd00, + 0x07f10099, + 0x03f00f00, + 0x0009d002, + 0x87f104bd, 0x84b60818, 0x008ad006, -/* 0x011c: wait_doneo_e */ +/* 0x0124: wait_doneo_e */ 0x040087f1, 0xcf0684b6, 0x8aff0088, 0xf30bf488, - 0x085c87f1, - 0xbd0684b6, - 0x0099f094, - 0xf80089d0, -/* 0x013d: mmctx_size */ -/* 0x013f: nv_mmctx_size_loop */ - 0x9894bd00, - 0x85b600e8, - 0x0180b61a, - 0xbb0284b6, - 0xe0b60098, - 0x04efb804, - 0xb9eb1bf4, - 0x00f8029f, -/* 0x015c: mmctx_xfer */ - 0x083c87f1, - 0xbd0684b6, - 0x0199f094, - 0xf10089d0, + 0x99f094bd, + 0x0007f100, + 0x0203f017, + 0xbd0009d0, +/* 0x0147: mmctx_size */ + 0xbd00f804, +/* 0x0149: nv_mmctx_size_loop */ + 0x00e89894, + 0xb61a85b6, + 0x84b60180, + 0x0098bb02, + 0xb804e0b6, + 0x1bf404ef, + 0x029fb9eb, +/* 0x0166: mmctx_xfer */ + 0x94bd00f8, + 0xf10199f0, + 0xf00f0007, + 0x09d00203, + 0xf104bd00, 0xb6071087, 0x94bd0684, 0xf405bbfd, 0x8bd0090b, 0x0099f000, -/* 0x0180: mmctx_base_disabled */ +/* 0x018c: mmctx_base_disabled */ 0xf405eefd, 0x8ed00c0b, 0xc08fd080, -/* 0x018f: mmctx_multi_disabled */ +/* 0x019b: mmctx_multi_disabled */ 0xb70199f0, 0xc8010080, 0xb4b600ab, @@ -166,8 +169,8 @@ uint32_t nvd7_grgpc_code[] = { 0xb601aec8, 0xbefd11e4, 0x008bd005, -/* 0x01a8: mmctx_exec_loop */ -/* 0x01a8: mmctx_wait_free */ +/* 0x01b4: mmctx_exec_loop */ +/* 0x01b4: mmctx_wait_free */ 0xf0008ecf, 0x0bf41fe4, 0x00ce98fa, @@ -176,76 +179,77 @@ uint32_t nvd7_grgpc_code[] = { 0x04cdb804, 0xc8e81bf4, 0x1bf402ab, -/* 0x01c9: mmctx_fini_wait */ +/* 0x01d5: mmctx_fini_wait */ 0x008bcf18, 0xb01fb4f0, 0x1bf410b4, 0x02a7f0f7, 0xf4c921f4, -/* 0x01de: mmctx_stop */ +/* 0x01ea: mmctx_stop */ 0xabc81b0e, 0x10b4b600, 0xf00cb9f0, 0x8bd012b9, -/* 0x01ed: mmctx_stop_wait */ +/* 0x01f9: mmctx_stop_wait */ 0x008bcf00, 0xf412bbc8, -/* 0x01f6: mmctx_done */ - 0x87f1fa1b, - 0x84b6085c, - 0xf094bd06, - 0x89d00199, -/* 0x0207: strand_wait */ - 0xf900f800, - 0x02a7f0a0, - 0xfcc921f4, -/* 0x0213: strand_pre */ - 0xf100f8a0, - 0xf04afc87, - 0x97f00283, - 0x0089d00c, - 0x020721f5, -/* 0x0226: strand_post */ - 0x87f100f8, - 0x83f04afc, - 0x0d97f002, - 0xf50089d0, - 0xf8020721, -/* 0x0239: strand_set */ - 0xfca7f100, - 0x02a3f04f, - 0x0500aba2, - 0xd00fc7f0, - 0xc7f000ac, - 0x00bcd00b, - 0x020721f5, - 0xf000aed0, - 0xbcd00ac7, - 0x0721f500, -/* 0x0263: strand_ctx_init */ - 0xf100f802, - 0xb6083c87, - 0x94bd0684, - 0xd00399f0, +/* 0x0202: mmctx_done */ + 0x94bdfa1b, + 0xf10199f0, + 0xf0170007, + 0x09d00203, + 0xf804bd00, +/* 0x0215: strand_wait */ + 0xf0a0f900, + 0x21f402a7, + 0xf8a0fcc9, +/* 0x0221: strand_pre */ + 0xfc87f100, + 0x0283f04a, + 0xd00c97f0, 0x21f50089, - 0xe7f00213, - 0x3921f503, + 0x00f80215, +/* 0x0234: strand_post */ + 0x4afc87f1, + 0xf00283f0, + 0x89d00d97, + 0x1521f500, +/* 0x0247: strand_set */ + 0xf100f802, + 0xf04ffca7, + 0xaba202a3, + 0xc7f00500, + 0x00acd00f, + 0xd00bc7f0, + 0x21f500bc, + 0xaed00215, + 0x0ac7f000, + 0xf500bcd0, + 0xf8021521, +/* 0x0271: strand_ctx_init */ + 0xf094bd00, + 0x07f10399, + 0x03f00f00, + 0x0009d002, + 0x21f504bd, + 0xe7f00221, + 0x4721f503, 0xfca7f102, 0x02a3f046, 0x0400aba0, 0xf040a0d0, 0xbcd001c7, - 0x0721f500, + 0x1521f500, 0x010c9202, 0xf000acd0, 0xbcd002c7, - 0x0721f500, - 0x2621f502, + 0x1521f500, + 0x3421f502, 0x8087f102, 0x0684b608, 0xb70089cf, 0x95220080, -/* 0x02ba: ctx_init_strand_loop */ +/* 0x02ca: ctx_init_strand_loop */ 0x8ed008fe, 0x408ed000, 0xb6808acf, @@ -254,207 +258,203 @@ uint32_t nvd7_grgpc_code[] = { 0xb60480b6, 0x1bf40192, 0x08e4b6e8, - 0xf1f2efbc, - 0xb6085c87, - 0x94bd0684, - 0xd00399f0, - 0x00f80089, -/* 0x02ec: error */ - 0xe7f1e0f9, - 0xe3f09814, - 0x8d21f440, - 0x041ce0b7, - 0xf401f7f0, - 0xe0fc8d21, -/* 0x0306: init */ - 0x04bd00f8, - 0xf10004fe, - 0xf0120017, - 0x12d00227, - 0x5717f100, - 0x0010fe04, - 0x040017f1, - 0xf0c010d0, - 0x12d00427, - 0x1031f400, - 0x060817f1, - 0xcf0614b6, - 0x37f00012, - 0x1f24f001, - 0xb60432bb, - 0x02800132, - 0x06038005, - 0x040010b7, - 0x800012cf, - 0xe7f10402, - 0xe3f00c30, - 0xbd24bd50, -/* 0x035f: init_unk_loop */ - 0xf444bd34, - 0xf6b06821, - 0x0f0bf400, - 0xbb01f7f0, - 0x4ffd04f2, - 0x0130b605, -/* 0x0374: init_unk_next */ - 0xb60120b6, - 0x26b004e0, - 0xe21bf401, -/* 0x0380: init_unk_done */ - 0x80070380, - 0x27f10804, - 0x24b60800, - 0x4022cf06, - 0x47f134bd, - 0x44b60700, - 0x08259506, - 0xd00045d0, - 0x0e984045, - 0x010f9800, - 0x013d21f5, - 0xbb002fbb, - 0x0e98003f, - 0x020f9801, - 0x013d21f5, - 0xfd050e98, + 0xbdf2efbc, + 0x0399f094, + 0x170007f1, + 0xd00203f0, + 0x04bd0009, +/* 0x02fe: error */ + 0xe0f900f8, + 0x9814e7f1, + 0xf440e3f0, + 0xe0b78d21, + 0xf7f0041c, + 0x8d21f401, + 0x00f8e0fc, +/* 0x0318: init */ + 0x04fe04bd, + 0x0017f100, + 0x0227f012, + 0xf10012d0, + 0xfe047017, + 0x17f10010, + 0x10d00400, + 0x0427f0c0, + 0xf40012d0, + 0x17f11031, + 0x14b60608, + 0x0012cf06, + 0xf00137f0, + 0x32bb1f24, + 0x0132b604, + 0x80050280, + 0x10b70603, + 0x12cf0400, + 0x04028000, + 0x0c30e7f1, + 0xbd50e3f0, + 0xbd34bd24, +/* 0x0371: init_unk_loop */ + 0x6821f444, + 0xf400f6b0, + 0xf7f00f0b, + 0x04f2bb01, + 0xb6054ffd, +/* 0x0386: init_unk_next */ + 0x20b60130, + 0x04e0b601, + 0xf40126b0, +/* 0x0392: init_unk_done */ + 0x0380e21b, + 0x08048007, + 0x010027f1, + 0xcf0223f0, + 0x34bd0022, + 0x070047f1, + 0x950644b6, + 0x45d00825, + 0x4045d000, + 0x98000e98, + 0x21f5010f, + 0x2fbb0147, + 0x003fbb00, + 0x98010e98, + 0x21f5020f, + 0x0e980147, + 0x00effd05, + 0xbb002ebb, + 0x0e98003e, + 0x030f9802, + 0x014721f5, + 0xfd070e98, 0x2ebb00ef, 0x003ebb00, - 0x98020e98, - 0x21f5030f, - 0x0e98013d, - 0x00effd07, - 0xbb002ebb, - 0x40b7003e, - 0x35b61300, - 0x0043d002, - 0xb60825b6, - 0x20b60635, - 0x0130b601, - 0xb60824b6, - 0x2fb90834, - 0x6321f502, - 0x003fbb02, - 0x080017f1, - 0xd00614b6, - 0x10b74013, - 0x24bd0800, - 0xd01f29f0, -/* 0x041a: main */ - 0x31f40012, - 0x0028f400, - 0xf424d7f0, - 0x01f43921, - 0x04e4b0f4, - 0xfe1e18f4, - 0x27f00181, - 0xfd20bd06, - 0xe4b60412, - 0x051efd01, - 0xf50018fe, - 0xf404dc21, -/* 0x044a: main_not_ctx_xfer */ - 0xef94d30e, - 0x01f5f010, - 0x02ec21f5, -/* 0x0457: ih */ - 0xf9c60ef4, - 0x0188fe80, - 0x90f980f9, - 0xb0f9a0f9, - 0xe0f9d0f9, - 0x0acff0f9, - 0x04abc480, - 0xf11d0bf4, - 0xf01900b7, - 0xbecf24d7, - 0x00bfcf40, - 0xb70421f4, - 0xf00400b0, - 0xbed001e7, -/* 0x048d: ih_no_fifo */ - 0x400ad000, - 0xe0fcf0fc, - 0xb0fcd0fc, - 0x90fca0fc, - 0x88fe80fc, - 0xf480fc00, - 0x01f80032, -/* 0x04a8: hub_barrier_done */ - 0x9801f7f0, - 0xfebb040e, - 0x18e7f104, - 0x40e3f094, - 0xf88d21f4, -/* 0x04bd: ctx_redswitch */ - 0x14e7f100, - 0x06e4b606, - 0xd020f7f0, - 0xf7f000ef, -/* 0x04cd: ctx_redswitch_delay */ - 0x01f2b608, - 0xf1fd1bf4, - 0xd00a20f7, - 0x00f800ef, -/* 0x04dc: ctx_xfer */ - 0x0a0417f1, - 0xd00614b6, - 0x11f4001f, - 0xbd21f507, -/* 0x04ed: ctx_xfer_not_load */ - 0xfc17f104, - 0x0213f04a, - 0xd00c27f0, - 0x21f50012, - 0x27f10207, - 0x23f047fc, - 0x0020d002, - 0xb6012cf0, - 0x12d00320, - 0x01acf000, - 0xf002a5f0, - 0xb3f000b7, - 0x040c9850, - 0xbb0fc4b6, - 0x0c9800bc, - 0x010d9800, - 0xf500e7f0, - 0xf0015c21, - 0xb7f101ac, - 0xb3f04000, - 0x040c9850, - 0xbb0fc4b6, - 0x0c9800bc, - 0x020d9801, - 0xf1060f98, - 0xf50800e7, - 0xf0015c21, - 0xa5f001ac, - 0x00b7f104, - 0x50b3f030, - 0xb6040c98, - 0xbcbb0fc4, - 0x020c9800, - 0x98030d98, - 0xe7f1080f, - 0x21f50200, - 0x21f5015c, - 0x01f40207, - 0x1412f406, -/* 0x0588: ctx_xfer_post */ + 0x130040b7, + 0xd00235b6, + 0x25b60043, + 0x0635b608, + 0xb60120b6, + 0x24b60130, + 0x0834b608, + 0xf5022fb9, + 0xbb027121, + 0x07f1003f, + 0x03f00100, + 0x0003d002, + 0x24bd04bd, + 0xf11f29f0, + 0xf0080007, + 0x02d00203, +/* 0x0433: main */ + 0xf404bd00, + 0x28f40031, + 0x24d7f000, + 0xf43921f4, + 0xe4b0f401, + 0x1e18f404, + 0xf00181fe, + 0x20bd0627, + 0xb60412fd, + 0x1efd01e4, + 0x0018fe05, + 0x04f721f5, +/* 0x0463: main_not_ctx_xfer */ + 0x94d30ef4, + 0xf5f010ef, + 0xfe21f501, + 0xc60ef402, +/* 0x0470: ih */ + 0x88fe80f9, + 0xf980f901, + 0xf9a0f990, + 0xf9d0f9b0, + 0xbdf0f9e0, + 0x800acf04, + 0xf404abc4, + 0xb7f11d0b, + 0xd7f01900, + 0x40becf24, + 0xf400bfcf, + 0xb0b70421, + 0xe7f00400, + 0x00bed001, +/* 0x04a8: ih_no_fifo */ + 0xfc400ad0, + 0xfce0fcf0, + 0xfcb0fcd0, + 0xfc90fca0, + 0x0088fe80, + 0x32f480fc, +/* 0x04c3: hub_barrier_done */ + 0xf001f800, + 0x0e9801f7, + 0x04febb04, + 0x9418e7f1, + 0xf440e3f0, + 0x00f88d21, +/* 0x04d8: ctx_redswitch */ + 0x0614e7f1, + 0xf006e4b6, + 0xefd020f7, + 0x08f7f000, +/* 0x04e8: ctx_redswitch_delay */ + 0xf401f2b6, + 0xf7f1fd1b, + 0xefd00a20, +/* 0x04f7: ctx_xfer */ + 0xf100f800, + 0xb60a0417, + 0x1fd00614, + 0x0711f400, + 0x04d821f5, +/* 0x0508: ctx_xfer_not_load */ 0x4afc17f1, 0xf00213f0, - 0x12d00d27, - 0x0721f500, -/* 0x0599: ctx_xfer_done */ - 0xa821f502, - 0x0000f804, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, + 0x12d00c27, + 0x1521f500, + 0xfc27f102, + 0x0223f047, + 0xf00020d0, + 0x20b6012c, + 0x0012d003, + 0xf001acf0, + 0xb7f002a5, + 0x50b3f000, + 0xb6040c98, + 0xbcbb0fc4, + 0x000c9800, + 0xf0010d98, + 0x21f500e7, + 0xacf00166, + 0x00b7f101, + 0x50b3f040, + 0xb6040c98, + 0xbcbb0fc4, + 0x010c9800, + 0x98020d98, + 0xe7f1060f, + 0x21f50800, + 0xacf00166, + 0x04a5f001, + 0x3000b7f1, + 0x9850b3f0, + 0xc4b6040c, + 0x00bcbb0f, + 0x98020c98, + 0x0f98030d, + 0x00e7f108, + 0x6621f502, + 0x1521f501, + 0x0601f402, +/* 0x05a3: ctx_xfer_post */ + 0xf11412f4, + 0xf04afc17, + 0x27f00213, + 0x0012d00d, + 0x021521f5, +/* 0x05b4: ctx_xfer_done */ + 0x04c321f5, + 0x000000f8, 0x00000000, 0x00000000, 0x00000000, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h index dc9c7784aeca..7ff5ef6b0804 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnve0.fuc.h @@ -41,14 +41,14 @@ uint32_t nve0_grgpc_data[] = { }; uint32_t nve0_grgpc_code[] = { - 0x03060ef5, + 0x03180ef5, /* 0x0004: queue_put */ 0x9800d898, 0x86f001d9, 0x0489b808, 0xf00c1bf4, 0x21f502f7, - 0x00f802ec, + 0x00f802fe, /* 0x001c: queue_put_next */ 0xb60798c4, 0x8dbb0384, @@ -80,7 +80,7 @@ uint32_t nve0_grgpc_code[] = { 0xc800bccf, 0x1bf41fcc, 0x06a7f0fa, - 0x010321f5, + 0x010921f5, 0xf840bfcf, /* 0x008d: nv_wr32 */ 0x28b7f100, @@ -102,63 +102,66 @@ uint32_t nve0_grgpc_code[] = { 0x0684b604, 0xf80080d0, /* 0x00c9: wait_donez */ - 0x3c87f100, - 0x0684b608, - 0x99f094bd, - 0x0089d000, - 0x081887f1, - 0xd00684b6, -/* 0x00e2: wait_donez_ne */ - 0x87f1008a, - 0x84b60400, - 0x0088cf06, + 0xf094bd00, + 0x07f10099, + 0x03f00f00, + 0x0009d002, + 0x07f104bd, + 0x03f00600, + 0x000ad002, +/* 0x00e6: wait_donez_ne */ + 0x87f104bd, + 0x83f00000, + 0x0088cf01, 0xf4888aff, - 0x87f1f31b, - 0x84b6085c, - 0xf094bd06, - 0x89d00099, -/* 0x0103: wait_doneo */ - 0xf100f800, - 0xb6083c87, - 0x94bd0684, - 0xd00099f0, - 0x87f10089, + 0x94bdf31b, + 0xf10099f0, + 0xf0170007, + 0x09d00203, + 0xf804bd00, +/* 0x0109: wait_doneo */ + 0xf094bd00, + 0x07f10099, + 0x03f00f00, + 0x0009d002, + 0x87f104bd, 0x84b60818, 0x008ad006, -/* 0x011c: wait_doneo_e */ +/* 0x0124: wait_doneo_e */ 0x040087f1, 0xcf0684b6, 0x8aff0088, 0xf30bf488, - 0x085c87f1, - 0xbd0684b6, - 0x0099f094, - 0xf80089d0, -/* 0x013d: mmctx_size */ -/* 0x013f: nv_mmctx_size_loop */ - 0x9894bd00, - 0x85b600e8, - 0x0180b61a, - 0xbb0284b6, - 0xe0b60098, - 0x04efb804, - 0xb9eb1bf4, - 0x00f8029f, -/* 0x015c: mmctx_xfer */ - 0x083c87f1, - 0xbd0684b6, - 0x0199f094, - 0xf10089d0, + 0x99f094bd, + 0x0007f100, + 0x0203f017, + 0xbd0009d0, +/* 0x0147: mmctx_size */ + 0xbd00f804, +/* 0x0149: nv_mmctx_size_loop */ + 0x00e89894, + 0xb61a85b6, + 0x84b60180, + 0x0098bb02, + 0xb804e0b6, + 0x1bf404ef, + 0x029fb9eb, +/* 0x0166: mmctx_xfer */ + 0x94bd00f8, + 0xf10199f0, + 0xf00f0007, + 0x09d00203, + 0xf104bd00, 0xb6071087, 0x94bd0684, 0xf405bbfd, 0x8bd0090b, 0x0099f000, -/* 0x0180: mmctx_base_disabled */ +/* 0x018c: mmctx_base_disabled */ 0xf405eefd, 0x8ed00c0b, 0xc08fd080, -/* 0x018f: mmctx_multi_disabled */ +/* 0x019b: mmctx_multi_disabled */ 0xb70199f0, 0xc8010080, 0xb4b600ab, @@ -166,8 +169,8 @@ uint32_t nve0_grgpc_code[] = { 0xb601aec8, 0xbefd11e4, 0x008bd005, -/* 0x01a8: mmctx_exec_loop */ -/* 0x01a8: mmctx_wait_free */ +/* 0x01b4: mmctx_exec_loop */ +/* 0x01b4: mmctx_wait_free */ 0xf0008ecf, 0x0bf41fe4, 0x00ce98fa, @@ -176,76 +179,77 @@ uint32_t nve0_grgpc_code[] = { 0x04cdb804, 0xc8e81bf4, 0x1bf402ab, -/* 0x01c9: mmctx_fini_wait */ +/* 0x01d5: mmctx_fini_wait */ 0x008bcf18, 0xb01fb4f0, 0x1bf410b4, 0x02a7f0f7, 0xf4c921f4, -/* 0x01de: mmctx_stop */ +/* 0x01ea: mmctx_stop */ 0xabc81b0e, 0x10b4b600, 0xf00cb9f0, 0x8bd012b9, -/* 0x01ed: mmctx_stop_wait */ +/* 0x01f9: mmctx_stop_wait */ 0x008bcf00, 0xf412bbc8, -/* 0x01f6: mmctx_done */ - 0x87f1fa1b, - 0x84b6085c, - 0xf094bd06, - 0x89d00199, -/* 0x0207: strand_wait */ - 0xf900f800, - 0x02a7f0a0, - 0xfcc921f4, -/* 0x0213: strand_pre */ - 0xf100f8a0, - 0xf04afc87, - 0x97f00283, - 0x0089d00c, - 0x020721f5, -/* 0x0226: strand_post */ - 0x87f100f8, - 0x83f04afc, - 0x0d97f002, - 0xf50089d0, - 0xf8020721, -/* 0x0239: strand_set */ - 0xfca7f100, - 0x02a3f04f, - 0x0500aba2, - 0xd00fc7f0, - 0xc7f000ac, - 0x00bcd00b, - 0x020721f5, - 0xf000aed0, - 0xbcd00ac7, - 0x0721f500, -/* 0x0263: strand_ctx_init */ - 0xf100f802, - 0xb6083c87, - 0x94bd0684, - 0xd00399f0, +/* 0x0202: mmctx_done */ + 0x94bdfa1b, + 0xf10199f0, + 0xf0170007, + 0x09d00203, + 0xf804bd00, +/* 0x0215: strand_wait */ + 0xf0a0f900, + 0x21f402a7, + 0xf8a0fcc9, +/* 0x0221: strand_pre */ + 0xfc87f100, + 0x0283f04a, + 0xd00c97f0, 0x21f50089, - 0xe7f00213, - 0x3921f503, + 0x00f80215, +/* 0x0234: strand_post */ + 0x4afc87f1, + 0xf00283f0, + 0x89d00d97, + 0x1521f500, +/* 0x0247: strand_set */ + 0xf100f802, + 0xf04ffca7, + 0xaba202a3, + 0xc7f00500, + 0x00acd00f, + 0xd00bc7f0, + 0x21f500bc, + 0xaed00215, + 0x0ac7f000, + 0xf500bcd0, + 0xf8021521, +/* 0x0271: strand_ctx_init */ + 0xf094bd00, + 0x07f10399, + 0x03f00f00, + 0x0009d002, + 0x21f504bd, + 0xe7f00221, + 0x4721f503, 0xfca7f102, 0x02a3f046, 0x0400aba0, 0xf040a0d0, 0xbcd001c7, - 0x0721f500, + 0x1521f500, 0x010c9202, 0xf000acd0, 0xbcd002c7, - 0x0721f500, - 0x2621f502, + 0x1521f500, + 0x3421f502, 0x8087f102, 0x0684b608, 0xb70089cf, 0x95220080, -/* 0x02ba: ctx_init_strand_loop */ +/* 0x02ca: ctx_init_strand_loop */ 0x8ed008fe, 0x408ed000, 0xb6808acf, @@ -254,207 +258,203 @@ uint32_t nve0_grgpc_code[] = { 0xb60480b6, 0x1bf40192, 0x08e4b6e8, - 0xf1f2efbc, - 0xb6085c87, - 0x94bd0684, - 0xd00399f0, - 0x00f80089, -/* 0x02ec: error */ - 0xe7f1e0f9, - 0xe3f09814, - 0x8d21f440, - 0x041ce0b7, - 0xf401f7f0, - 0xe0fc8d21, -/* 0x0306: init */ - 0x04bd00f8, - 0xf10004fe, - 0xf0120017, - 0x12d00227, - 0x5717f100, - 0x0010fe04, - 0x040017f1, - 0xf0c010d0, - 0x12d00427, - 0x1031f400, - 0x060817f1, - 0xcf0614b6, - 0x37f00012, - 0x1f24f001, - 0xb60432bb, - 0x02800132, - 0x06038005, - 0x040010b7, - 0x800012cf, - 0xe7f10402, - 0xe3f00c30, - 0xbd24bd50, -/* 0x035f: init_unk_loop */ - 0xf444bd34, - 0xf6b06821, - 0x0f0bf400, - 0xbb01f7f0, - 0x4ffd04f2, - 0x0130b605, -/* 0x0374: init_unk_next */ - 0xb60120b6, - 0x26b004e0, - 0xe21bf401, -/* 0x0380: init_unk_done */ - 0x80070380, - 0x27f10804, - 0x24b60800, - 0x4022cf06, - 0x47f134bd, - 0x44b60700, - 0x08259506, - 0xd00045d0, - 0x0e984045, - 0x010f9800, - 0x013d21f5, - 0xbb002fbb, - 0x0e98003f, - 0x020f9801, - 0x013d21f5, - 0xfd050e98, + 0xbdf2efbc, + 0x0399f094, + 0x170007f1, + 0xd00203f0, + 0x04bd0009, +/* 0x02fe: error */ + 0xe0f900f8, + 0x9814e7f1, + 0xf440e3f0, + 0xe0b78d21, + 0xf7f0041c, + 0x8d21f401, + 0x00f8e0fc, +/* 0x0318: init */ + 0x04fe04bd, + 0x0017f100, + 0x0227f012, + 0xf10012d0, + 0xfe047017, + 0x17f10010, + 0x10d00400, + 0x0427f0c0, + 0xf40012d0, + 0x17f11031, + 0x14b60608, + 0x0012cf06, + 0xf00137f0, + 0x32bb1f24, + 0x0132b604, + 0x80050280, + 0x10b70603, + 0x12cf0400, + 0x04028000, + 0x0c30e7f1, + 0xbd50e3f0, + 0xbd34bd24, +/* 0x0371: init_unk_loop */ + 0x6821f444, + 0xf400f6b0, + 0xf7f00f0b, + 0x04f2bb01, + 0xb6054ffd, +/* 0x0386: init_unk_next */ + 0x20b60130, + 0x04e0b601, + 0xf40126b0, +/* 0x0392: init_unk_done */ + 0x0380e21b, + 0x08048007, + 0x010027f1, + 0xcf0223f0, + 0x34bd0022, + 0x070047f1, + 0x950644b6, + 0x45d00825, + 0x4045d000, + 0x98000e98, + 0x21f5010f, + 0x2fbb0147, + 0x003fbb00, + 0x98010e98, + 0x21f5020f, + 0x0e980147, + 0x00effd05, + 0xbb002ebb, + 0x0e98003e, + 0x030f9802, + 0x014721f5, + 0xfd070e98, 0x2ebb00ef, 0x003ebb00, - 0x98020e98, - 0x21f5030f, - 0x0e98013d, - 0x00effd07, - 0xbb002ebb, - 0x40b7003e, - 0x35b61300, - 0x0043d002, - 0xb60825b6, - 0x20b60635, - 0x0130b601, - 0xb60824b6, - 0x2fb90834, - 0x6321f502, - 0x003fbb02, - 0x080017f1, - 0xd00614b6, - 0x10b74013, - 0x24bd0800, - 0xd01f29f0, -/* 0x041a: main */ - 0x31f40012, - 0x0028f400, - 0xf424d7f0, - 0x01f43921, - 0x04e4b0f4, - 0xfe1e18f4, - 0x27f00181, - 0xfd20bd06, - 0xe4b60412, - 0x051efd01, - 0xf50018fe, - 0xf404dc21, -/* 0x044a: main_not_ctx_xfer */ - 0xef94d30e, - 0x01f5f010, - 0x02ec21f5, -/* 0x0457: ih */ - 0xf9c60ef4, - 0x0188fe80, - 0x90f980f9, - 0xb0f9a0f9, - 0xe0f9d0f9, - 0x0acff0f9, - 0x04abc480, - 0xf11d0bf4, - 0xf01900b7, - 0xbecf24d7, - 0x00bfcf40, - 0xb70421f4, - 0xf00400b0, - 0xbed001e7, -/* 0x048d: ih_no_fifo */ - 0x400ad000, - 0xe0fcf0fc, - 0xb0fcd0fc, - 0x90fca0fc, - 0x88fe80fc, - 0xf480fc00, - 0x01f80032, -/* 0x04a8: hub_barrier_done */ - 0x9801f7f0, - 0xfebb040e, - 0x18e7f104, - 0x40e3f094, - 0xf88d21f4, -/* 0x04bd: ctx_redswitch */ - 0x14e7f100, - 0x06e4b606, - 0xd020f7f0, - 0xf7f000ef, -/* 0x04cd: ctx_redswitch_delay */ - 0x01f2b608, - 0xf1fd1bf4, - 0xd00a20f7, - 0x00f800ef, -/* 0x04dc: ctx_xfer */ - 0x0a0417f1, - 0xd00614b6, - 0x11f4001f, - 0xbd21f507, -/* 0x04ed: ctx_xfer_not_load */ - 0xfc17f104, - 0x0213f04a, - 0xd00c27f0, - 0x21f50012, - 0x27f10207, - 0x23f047fc, - 0x0020d002, - 0xb6012cf0, - 0x12d00320, - 0x01acf000, - 0xf002a5f0, - 0xb3f000b7, - 0x040c9850, - 0xbb0fc4b6, - 0x0c9800bc, - 0x010d9800, - 0xf500e7f0, - 0xf0015c21, - 0xb7f101ac, - 0xb3f04000, - 0x040c9850, - 0xbb0fc4b6, - 0x0c9800bc, - 0x020d9801, - 0xf1060f98, - 0xf50800e7, - 0xf0015c21, - 0xa5f001ac, - 0x00b7f104, - 0x50b3f030, - 0xb6040c98, - 0xbcbb0fc4, - 0x020c9800, - 0x98030d98, - 0xe7f1080f, - 0x21f50200, - 0x21f5015c, - 0x01f40207, - 0x1412f406, -/* 0x0588: ctx_xfer_post */ + 0x130040b7, + 0xd00235b6, + 0x25b60043, + 0x0635b608, + 0xb60120b6, + 0x24b60130, + 0x0834b608, + 0xf5022fb9, + 0xbb027121, + 0x07f1003f, + 0x03f00100, + 0x0003d002, + 0x24bd04bd, + 0xf11f29f0, + 0xf0080007, + 0x02d00203, +/* 0x0433: main */ + 0xf404bd00, + 0x28f40031, + 0x24d7f000, + 0xf43921f4, + 0xe4b0f401, + 0x1e18f404, + 0xf00181fe, + 0x20bd0627, + 0xb60412fd, + 0x1efd01e4, + 0x0018fe05, + 0x04f721f5, +/* 0x0463: main_not_ctx_xfer */ + 0x94d30ef4, + 0xf5f010ef, + 0xfe21f501, + 0xc60ef402, +/* 0x0470: ih */ + 0x88fe80f9, + 0xf980f901, + 0xf9a0f990, + 0xf9d0f9b0, + 0xbdf0f9e0, + 0x800acf04, + 0xf404abc4, + 0xb7f11d0b, + 0xd7f01900, + 0x40becf24, + 0xf400bfcf, + 0xb0b70421, + 0xe7f00400, + 0x00bed001, +/* 0x04a8: ih_no_fifo */ + 0xfc400ad0, + 0xfce0fcf0, + 0xfcb0fcd0, + 0xfc90fca0, + 0x0088fe80, + 0x32f480fc, +/* 0x04c3: hub_barrier_done */ + 0xf001f800, + 0x0e9801f7, + 0x04febb04, + 0x9418e7f1, + 0xf440e3f0, + 0x00f88d21, +/* 0x04d8: ctx_redswitch */ + 0x0614e7f1, + 0xf006e4b6, + 0xefd020f7, + 0x08f7f000, +/* 0x04e8: ctx_redswitch_delay */ + 0xf401f2b6, + 0xf7f1fd1b, + 0xefd00a20, +/* 0x04f7: ctx_xfer */ + 0xf100f800, + 0xb60a0417, + 0x1fd00614, + 0x0711f400, + 0x04d821f5, +/* 0x0508: ctx_xfer_not_load */ 0x4afc17f1, 0xf00213f0, - 0x12d00d27, - 0x0721f500, -/* 0x0599: ctx_xfer_done */ - 0xa821f502, - 0x0000f804, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, + 0x12d00c27, + 0x1521f500, + 0xfc27f102, + 0x0223f047, + 0xf00020d0, + 0x20b6012c, + 0x0012d003, + 0xf001acf0, + 0xb7f002a5, + 0x50b3f000, + 0xb6040c98, + 0xbcbb0fc4, + 0x000c9800, + 0xf0010d98, + 0x21f500e7, + 0xacf00166, + 0x00b7f101, + 0x50b3f040, + 0xb6040c98, + 0xbcbb0fc4, + 0x010c9800, + 0x98020d98, + 0xe7f1060f, + 0x21f50800, + 0xacf00166, + 0x04a5f001, + 0x3000b7f1, + 0x9850b3f0, + 0xc4b6040c, + 0x00bcbb0f, + 0x98020c98, + 0x0f98030d, + 0x00e7f108, + 0x6621f502, + 0x1521f501, + 0x0601f402, +/* 0x05a3: ctx_xfer_post */ + 0xf11412f4, + 0xf04afc17, + 0x27f00213, + 0x0012d00d, + 0x021521f5, +/* 0x05b4: ctx_xfer_done */ + 0x04c321f5, + 0x000000f8, 0x00000000, 0x00000000, 0x00000000, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvf0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvf0.fuc.h index cbbed6aa080b..f870507be880 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvf0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvf0.fuc.h @@ -41,14 +41,14 @@ uint32_t nvf0_grgpc_data[] = { }; uint32_t nvf0_grgpc_code[] = { - 0x03060ef5, + 0x03180ef5, /* 0x0004: queue_put */ 0x9800d898, 0x86f001d9, 0x0489b808, 0xf00c1bf4, 0x21f502f7, - 0x00f802ec, + 0x00f802fe, /* 0x001c: queue_put_next */ 0xb60798c4, 0x8dbb0384, @@ -80,7 +80,7 @@ uint32_t nvf0_grgpc_code[] = { 0xc800bccf, 0x1bf41fcc, 0x06a7f0fa, - 0x010321f5, + 0x010921f5, 0xf840bfcf, /* 0x008d: nv_wr32 */ 0x28b7f100, @@ -102,63 +102,66 @@ uint32_t nvf0_grgpc_code[] = { 0x0684b604, 0xf80080d0, /* 0x00c9: wait_donez */ - 0x3c87f100, - 0x0684b608, - 0x99f094bd, - 0x0089d000, - 0x081887f1, - 0xd00684b6, -/* 0x00e2: wait_donez_ne */ - 0x87f1008a, - 0x84b60400, - 0x0088cf06, + 0xf094bd00, + 0x07f10099, + 0x03f03700, + 0x0009d002, + 0x07f104bd, + 0x03f00600, + 0x000ad002, +/* 0x00e6: wait_donez_ne */ + 0x87f104bd, + 0x83f00000, + 0x0088cf01, 0xf4888aff, - 0x87f1f31b, - 0x84b6085c, - 0xf094bd06, - 0x89d00099, -/* 0x0103: wait_doneo */ - 0xf100f800, - 0xb6083c87, - 0x94bd0684, - 0xd00099f0, - 0x87f10089, + 0x94bdf31b, + 0xf10099f0, + 0xf0170007, + 0x09d00203, + 0xf804bd00, +/* 0x0109: wait_doneo */ + 0xf094bd00, + 0x07f10099, + 0x03f03700, + 0x0009d002, + 0x87f104bd, 0x84b60818, 0x008ad006, -/* 0x011c: wait_doneo_e */ +/* 0x0124: wait_doneo_e */ 0x040087f1, 0xcf0684b6, 0x8aff0088, 0xf30bf488, - 0x085c87f1, - 0xbd0684b6, - 0x0099f094, - 0xf80089d0, -/* 0x013d: mmctx_size */ -/* 0x013f: nv_mmctx_size_loop */ - 0x9894bd00, - 0x85b600e8, - 0x0180b61a, - 0xbb0284b6, - 0xe0b60098, - 0x04efb804, - 0xb9eb1bf4, - 0x00f8029f, -/* 0x015c: mmctx_xfer */ - 0x083c87f1, - 0xbd0684b6, - 0x0199f094, - 0xf10089d0, + 0x99f094bd, + 0x0007f100, + 0x0203f017, + 0xbd0009d0, +/* 0x0147: mmctx_size */ + 0xbd00f804, +/* 0x0149: nv_mmctx_size_loop */ + 0x00e89894, + 0xb61a85b6, + 0x84b60180, + 0x0098bb02, + 0xb804e0b6, + 0x1bf404ef, + 0x029fb9eb, +/* 0x0166: mmctx_xfer */ + 0x94bd00f8, + 0xf10199f0, + 0xf0370007, + 0x09d00203, + 0xf104bd00, 0xb6071087, 0x94bd0684, 0xf405bbfd, 0x8bd0090b, 0x0099f000, -/* 0x0180: mmctx_base_disabled */ +/* 0x018c: mmctx_base_disabled */ 0xf405eefd, 0x8ed00c0b, 0xc08fd080, -/* 0x018f: mmctx_multi_disabled */ +/* 0x019b: mmctx_multi_disabled */ 0xb70199f0, 0xc8010080, 0xb4b600ab, @@ -166,8 +169,8 @@ uint32_t nvf0_grgpc_code[] = { 0xb601aec8, 0xbefd11e4, 0x008bd005, -/* 0x01a8: mmctx_exec_loop */ -/* 0x01a8: mmctx_wait_free */ +/* 0x01b4: mmctx_exec_loop */ +/* 0x01b4: mmctx_wait_free */ 0xf0008ecf, 0x0bf41fe4, 0x00ce98fa, @@ -176,76 +179,77 @@ uint32_t nvf0_grgpc_code[] = { 0x04cdb804, 0xc8e81bf4, 0x1bf402ab, -/* 0x01c9: mmctx_fini_wait */ +/* 0x01d5: mmctx_fini_wait */ 0x008bcf18, 0xb01fb4f0, 0x1bf410b4, 0x02a7f0f7, 0xf4c921f4, -/* 0x01de: mmctx_stop */ +/* 0x01ea: mmctx_stop */ 0xabc81b0e, 0x10b4b600, 0xf00cb9f0, 0x8bd012b9, -/* 0x01ed: mmctx_stop_wait */ +/* 0x01f9: mmctx_stop_wait */ 0x008bcf00, 0xf412bbc8, -/* 0x01f6: mmctx_done */ - 0x87f1fa1b, - 0x84b6085c, - 0xf094bd06, - 0x89d00199, -/* 0x0207: strand_wait */ - 0xf900f800, - 0x02a7f0a0, - 0xfcc921f4, -/* 0x0213: strand_pre */ - 0xf100f8a0, - 0xf04afc87, - 0x97f00283, - 0x0089d00c, - 0x020721f5, -/* 0x0226: strand_post */ - 0x87f100f8, - 0x83f04afc, - 0x0d97f002, - 0xf50089d0, - 0xf8020721, -/* 0x0239: strand_set */ - 0xfca7f100, - 0x02a3f04f, - 0x0500aba2, - 0xd00fc7f0, - 0xc7f000ac, - 0x00bcd00b, - 0x020721f5, - 0xf000aed0, - 0xbcd00ac7, - 0x0721f500, -/* 0x0263: strand_ctx_init */ - 0xf100f802, - 0xb6083c87, - 0x94bd0684, - 0xd00399f0, +/* 0x0202: mmctx_done */ + 0x94bdfa1b, + 0xf10199f0, + 0xf0170007, + 0x09d00203, + 0xf804bd00, +/* 0x0215: strand_wait */ + 0xf0a0f900, + 0x21f402a7, + 0xf8a0fcc9, +/* 0x0221: strand_pre */ + 0xfc87f100, + 0x0283f04a, + 0xd00c97f0, 0x21f50089, - 0xe7f00213, - 0x3921f503, + 0x00f80215, +/* 0x0234: strand_post */ + 0x4afc87f1, + 0xf00283f0, + 0x89d00d97, + 0x1521f500, +/* 0x0247: strand_set */ + 0xf100f802, + 0xf04ffca7, + 0xaba202a3, + 0xc7f00500, + 0x00acd00f, + 0xd00bc7f0, + 0x21f500bc, + 0xaed00215, + 0x0ac7f000, + 0xf500bcd0, + 0xf8021521, +/* 0x0271: strand_ctx_init */ + 0xf094bd00, + 0x07f10399, + 0x03f03700, + 0x0009d002, + 0x21f504bd, + 0xe7f00221, + 0x4721f503, 0xfca7f102, 0x02a3f046, 0x0400aba0, 0xf040a0d0, 0xbcd001c7, - 0x0721f500, + 0x1521f500, 0x010c9202, 0xf000acd0, 0xbcd002c7, - 0x0721f500, - 0x2621f502, + 0x1521f500, + 0x3421f502, 0x8087f102, 0x0684b608, 0xb70089cf, 0x95220080, -/* 0x02ba: ctx_init_strand_loop */ +/* 0x02ca: ctx_init_strand_loop */ 0x8ed008fe, 0x408ed000, 0xb6808acf, @@ -254,207 +258,203 @@ uint32_t nvf0_grgpc_code[] = { 0xb60480b6, 0x1bf40192, 0x08e4b6e8, - 0xf1f2efbc, - 0xb6085c87, - 0x94bd0684, - 0xd00399f0, - 0x00f80089, -/* 0x02ec: error */ - 0xe7f1e0f9, - 0xe3f09814, - 0x8d21f440, - 0x041ce0b7, - 0xf401f7f0, - 0xe0fc8d21, -/* 0x0306: init */ - 0x04bd00f8, - 0xf10004fe, - 0xf0120017, - 0x12d00227, - 0x5717f100, - 0x0010fe04, - 0x040017f1, - 0xf0c010d0, - 0x12d00427, - 0x1031f400, - 0x060817f1, - 0xcf0614b6, - 0x37f00012, - 0x1f24f001, - 0xb60432bb, - 0x02800132, - 0x06038005, - 0x040010b7, - 0x800012cf, - 0xe7f10402, - 0xe3f00c30, - 0xbd24bd50, -/* 0x035f: init_unk_loop */ - 0xf444bd34, - 0xf6b06821, - 0x0f0bf400, - 0xbb01f7f0, - 0x4ffd04f2, - 0x0130b605, -/* 0x0374: init_unk_next */ - 0xb60120b6, - 0x26b004e0, - 0xe21bf402, -/* 0x0380: init_unk_done */ - 0x80070380, - 0x27f10804, - 0x24b60800, - 0x4022cf06, - 0x47f134bd, - 0x44b60700, - 0x08259506, - 0xd00045d0, - 0x0e984045, - 0x010f9800, - 0x013d21f5, - 0xbb002fbb, - 0x0e98003f, - 0x020f9801, - 0x013d21f5, - 0xfd050e98, + 0xbdf2efbc, + 0x0399f094, + 0x170007f1, + 0xd00203f0, + 0x04bd0009, +/* 0x02fe: error */ + 0xe0f900f8, + 0x9814e7f1, + 0xf440e3f0, + 0xe0b78d21, + 0xf7f0041c, + 0x8d21f401, + 0x00f8e0fc, +/* 0x0318: init */ + 0x04fe04bd, + 0x0017f100, + 0x0227f012, + 0xf10012d0, + 0xfe047017, + 0x17f10010, + 0x10d00400, + 0x0427f0c0, + 0xf40012d0, + 0x17f11031, + 0x14b60608, + 0x0012cf06, + 0xf00137f0, + 0x32bb1f24, + 0x0132b604, + 0x80050280, + 0x10b70603, + 0x12cf0400, + 0x04028000, + 0x0c30e7f1, + 0xbd50e3f0, + 0xbd34bd24, +/* 0x0371: init_unk_loop */ + 0x6821f444, + 0xf400f6b0, + 0xf7f00f0b, + 0x04f2bb01, + 0xb6054ffd, +/* 0x0386: init_unk_next */ + 0x20b60130, + 0x04e0b601, + 0xf40226b0, +/* 0x0392: init_unk_done */ + 0x0380e21b, + 0x08048007, + 0x010027f1, + 0xcf0223f0, + 0x34bd0022, + 0x070047f1, + 0x950644b6, + 0x45d00825, + 0x4045d000, + 0x98000e98, + 0x21f5010f, + 0x2fbb0147, + 0x003fbb00, + 0x98010e98, + 0x21f5020f, + 0x0e980147, + 0x00effd05, + 0xbb002ebb, + 0x0e98003e, + 0x030f9802, + 0x014721f5, + 0xfd070e98, 0x2ebb00ef, 0x003ebb00, - 0x98020e98, - 0x21f5030f, - 0x0e98013d, - 0x00effd07, - 0xbb002ebb, - 0x40b7003e, - 0x35b61300, - 0x0043d002, - 0xb60825b6, - 0x20b60635, - 0x0130b601, - 0xb60824b6, - 0x2fb90834, - 0x6321f502, - 0x003fbb02, - 0x080017f1, - 0xd00614b6, - 0x10b74013, - 0x24bd0800, - 0xd01f29f0, -/* 0x041a: main */ - 0x31f40012, - 0x0028f400, - 0xf424d7f0, - 0x01f43921, - 0x04e4b0f4, - 0xfe1e18f4, - 0x27f00181, - 0xfd20bd06, - 0xe4b60412, - 0x051efd01, - 0xf50018fe, - 0xf404dc21, -/* 0x044a: main_not_ctx_xfer */ - 0xef94d30e, - 0x01f5f010, - 0x02ec21f5, -/* 0x0457: ih */ - 0xf9c60ef4, - 0x0188fe80, - 0x90f980f9, - 0xb0f9a0f9, - 0xe0f9d0f9, - 0x0acff0f9, - 0x04abc480, - 0xf11d0bf4, - 0xf01900b7, - 0xbecf24d7, - 0x00bfcf40, - 0xb70421f4, - 0xf00400b0, - 0xbed001e7, -/* 0x048d: ih_no_fifo */ - 0x400ad000, - 0xe0fcf0fc, - 0xb0fcd0fc, - 0x90fca0fc, - 0x88fe80fc, - 0xf480fc00, - 0x01f80032, -/* 0x04a8: hub_barrier_done */ - 0x9801f7f0, - 0xfebb040e, - 0x18e7f104, - 0x40e3f094, - 0xf88d21f4, -/* 0x04bd: ctx_redswitch */ - 0x14e7f100, - 0x06e4b606, - 0xd020f7f0, - 0xf7f000ef, -/* 0x04cd: ctx_redswitch_delay */ - 0x01f2b608, - 0xf1fd1bf4, - 0xd00a20f7, - 0x00f800ef, -/* 0x04dc: ctx_xfer */ - 0x0a0417f1, - 0xd00614b6, - 0x11f4001f, - 0xbd21f507, -/* 0x04ed: ctx_xfer_not_load */ - 0xfc17f104, - 0x0213f04a, - 0xd00c27f0, - 0x21f50012, - 0x27f10207, - 0x23f047fc, - 0x0020d002, - 0xb6012cf0, - 0x12d00320, - 0x01acf000, - 0xf002a5f0, - 0xb3f000b7, - 0x040c9850, - 0xbb0fc4b6, - 0x0c9800bc, - 0x010d9800, - 0xf500e7f0, - 0xf0015c21, - 0xb7f101ac, - 0xb3f04000, - 0x040c9850, - 0xbb0fc4b6, - 0x0c9800bc, - 0x020d9801, - 0xf1060f98, - 0xf50800e7, - 0xf0015c21, - 0xa5f001ac, - 0x00b7f104, - 0x50b3f030, - 0xb6040c98, - 0xbcbb0fc4, - 0x020c9800, - 0x98030d98, - 0xe7f1080f, - 0x21f50200, - 0x21f5015c, - 0x01f40207, - 0x1412f406, -/* 0x0588: ctx_xfer_post */ + 0x130040b7, + 0xd00235b6, + 0x25b60043, + 0x0635b608, + 0xb60120b6, + 0x24b60130, + 0x0834b608, + 0xf5022fb9, + 0xbb027121, + 0x07f1003f, + 0x03f00100, + 0x0003d002, + 0x24bd04bd, + 0xf11f29f0, + 0xf0300007, + 0x02d00203, +/* 0x0433: main */ + 0xf404bd00, + 0x28f40031, + 0x24d7f000, + 0xf43921f4, + 0xe4b0f401, + 0x1e18f404, + 0xf00181fe, + 0x20bd0627, + 0xb60412fd, + 0x1efd01e4, + 0x0018fe05, + 0x04f721f5, +/* 0x0463: main_not_ctx_xfer */ + 0x94d30ef4, + 0xf5f010ef, + 0xfe21f501, + 0xc60ef402, +/* 0x0470: ih */ + 0x88fe80f9, + 0xf980f901, + 0xf9a0f990, + 0xf9d0f9b0, + 0xbdf0f9e0, + 0x800acf04, + 0xf404abc4, + 0xb7f11d0b, + 0xd7f01900, + 0x40becf24, + 0xf400bfcf, + 0xb0b70421, + 0xe7f00400, + 0x00bed001, +/* 0x04a8: ih_no_fifo */ + 0xfc400ad0, + 0xfce0fcf0, + 0xfcb0fcd0, + 0xfc90fca0, + 0x0088fe80, + 0x32f480fc, +/* 0x04c3: hub_barrier_done */ + 0xf001f800, + 0x0e9801f7, + 0x04febb04, + 0x9418e7f1, + 0xf440e3f0, + 0x00f88d21, +/* 0x04d8: ctx_redswitch */ + 0x0614e7f1, + 0xf006e4b6, + 0xefd020f7, + 0x08f7f000, +/* 0x04e8: ctx_redswitch_delay */ + 0xf401f2b6, + 0xf7f1fd1b, + 0xefd00a20, +/* 0x04f7: ctx_xfer */ + 0xf100f800, + 0xb60a0417, + 0x1fd00614, + 0x0711f400, + 0x04d821f5, +/* 0x0508: ctx_xfer_not_load */ 0x4afc17f1, 0xf00213f0, - 0x12d00d27, - 0x0721f500, -/* 0x0599: ctx_xfer_done */ - 0xa821f502, - 0x0000f804, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, - 0x00000000, + 0x12d00c27, + 0x1521f500, + 0xfc27f102, + 0x0223f047, + 0xf00020d0, + 0x20b6012c, + 0x0012d003, + 0xf001acf0, + 0xb7f002a5, + 0x50b3f000, + 0xb6040c98, + 0xbcbb0fc4, + 0x000c9800, + 0xf0010d98, + 0x21f500e7, + 0xacf00166, + 0x00b7f101, + 0x50b3f040, + 0xb6040c98, + 0xbcbb0fc4, + 0x010c9800, + 0x98020d98, + 0xe7f1060f, + 0x21f50800, + 0xacf00166, + 0x04a5f001, + 0x3000b7f1, + 0x9850b3f0, + 0xc4b6040c, + 0x00bcbb0f, + 0x98020c98, + 0x0f98030d, + 0x00e7f108, + 0x6621f502, + 0x1521f501, + 0x0601f402, +/* 0x05a3: ctx_xfer_post */ + 0xf11412f4, + 0xf04afc17, + 0x27f00213, + 0x0012d00d, + 0x021521f5, +/* 0x05b4: ctx_xfer_done */ + 0x04c321f5, + 0x000000f8, 0x00000000, 0x00000000, 0x00000000, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hub.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hub.fuc index 6b81e7bae13f..b82d2ae89917 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hub.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hub.fuc @@ -52,15 +52,9 @@ hub_mmio_list_next: // In: $r15 error code (see nvc0.fuc) // error: - push $r14 - mov $r14 0x814 - shl b32 $r14 6 - iowr I[$r14 + 0x000] $r15 // CC_SCRATCH[5] = error code - mov $r14 0xc1c - shl b32 $r14 6 + nv_iowr(NV_PGRAPH_FECS_CC_SCRATCH_VAL(5), 0, $r15) mov $r15 1 - iowr I[$r14 + 0x000] $r15 // INTR_UP_SET - pop $r14 + nv_iowr(NV_PGRAPH_FECS_INTR_UP_SET, 0, $r15) ret // HUB fuc initialisation, executed by triggering ucode start, will @@ -211,13 +205,10 @@ init: bra ne #init_gpc // save context size, and tell host we're ready - mov $r2 0x800 - shl b32 $r2 6 - iowr I[$r2 + 0x100] $r1 // CC_SCRATCH[1] = context size - add b32 $r2 0x800 + nv_iowr(NV_PGRAPH_FECS_CC_SCRATCH_VAL(1), 0, $r1) clear b32 $r1 bset $r1 31 - iowr I[$r2 + 0x000] $r1 // CC_SCRATCH[0] |= 0x80000000 + nv_iowr(NV_PGRAPH_FECS_CC_SCRATCH_SET(0), 0, $r1) // Main program loop, very simple, sleeps until woken up by the interrupt // handler, pulls a command from the queue and executes its handler @@ -309,11 +300,9 @@ main: bra #main main_done: - mov $r1 0x820 - shl b32 $r1 6 clear b32 $r2 bset $r2 31 - iowr I[$r1 + 0x000] $r2 // CC_SCRATCH[0] |= 0x80000000 + nv_iowr(NV_PGRAPH_FECS_CC_SCRATCH_SET(0), 0, $r2) bra #main // interrupt handler @@ -327,6 +316,7 @@ ih: push $r13 push $r14 push $r15 + clear b32 $r0 // incoming fifo command? iord $r10 I[$r0 + 0x200] // INTR diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h index 647452362527..b59f694c0423 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvc0.fuc.h @@ -206,14 +206,14 @@ uint32_t nvc0_grhub_data[] = { }; uint32_t nvc0_grhub_code[] = { - 0x03090ef5, + 0x031b0ef5, /* 0x0004: queue_put */ 0x9800d898, 0x86f001d9, 0x0489b808, 0xf00c1bf4, 0x21f502f7, - 0x00f802ec, + 0x00f802fe, /* 0x001c: queue_put_next */ 0xb60798c4, 0x8dbb0384, @@ -245,7 +245,7 @@ uint32_t nvc0_grhub_code[] = { 0xc800bccf, 0x1bf41fcc, 0x06a7f0fa, - 0x010321f5, + 0x010921f5, 0xf840bfcf, /* 0x008d: nv_wr32 */ 0x28b7f100, @@ -267,63 +267,66 @@ uint32_t nvc0_grhub_code[] = { 0x0684b604, 0xf80080d0, /* 0x00c9: wait_donez */ - 0x3c87f100, - 0x0684b608, - 0x99f094bd, - 0x0089d000, - 0x081887f1, - 0xd00684b6, -/* 0x00e2: wait_donez_ne */ - 0x87f1008a, - 0x84b60400, - 0x0088cf06, + 0xf094bd00, + 0x07f10099, + 0x03f00f00, + 0x0009d002, + 0x07f104bd, + 0x03f00600, + 0x000ad002, +/* 0x00e6: wait_donez_ne */ + 0x87f104bd, + 0x83f00000, + 0x0088cf01, 0xf4888aff, - 0x87f1f31b, - 0x84b6085c, - 0xf094bd06, - 0x89d00099, -/* 0x0103: wait_doneo */ - 0xf100f800, - 0xb6083c87, - 0x94bd0684, - 0xd00099f0, - 0x87f10089, + 0x94bdf31b, + 0xf10099f0, + 0xf0170007, + 0x09d00203, + 0xf804bd00, +/* 0x0109: wait_doneo */ + 0xf094bd00, + 0x07f10099, + 0x03f00f00, + 0x0009d002, + 0x87f104bd, 0x84b60818, 0x008ad006, -/* 0x011c: wait_doneo_e */ +/* 0x0124: wait_doneo_e */ 0x040087f1, 0xcf0684b6, 0x8aff0088, 0xf30bf488, - 0x085c87f1, - 0xbd0684b6, - 0x0099f094, - 0xf80089d0, -/* 0x013d: mmctx_size */ -/* 0x013f: nv_mmctx_size_loop */ - 0x9894bd00, - 0x85b600e8, - 0x0180b61a, - 0xbb0284b6, - 0xe0b60098, - 0x04efb804, - 0xb9eb1bf4, - 0x00f8029f, -/* 0x015c: mmctx_xfer */ - 0x083c87f1, - 0xbd0684b6, - 0x0199f094, - 0xf10089d0, + 0x99f094bd, + 0x0007f100, + 0x0203f017, + 0xbd0009d0, +/* 0x0147: mmctx_size */ + 0xbd00f804, +/* 0x0149: nv_mmctx_size_loop */ + 0x00e89894, + 0xb61a85b6, + 0x84b60180, + 0x0098bb02, + 0xb804e0b6, + 0x1bf404ef, + 0x029fb9eb, +/* 0x0166: mmctx_xfer */ + 0x94bd00f8, + 0xf10199f0, + 0xf00f0007, + 0x09d00203, + 0xf104bd00, 0xb6071087, 0x94bd0684, 0xf405bbfd, 0x8bd0090b, 0x0099f000, -/* 0x0180: mmctx_base_disabled */ +/* 0x018c: mmctx_base_disabled */ 0xf405eefd, 0x8ed00c0b, 0xc08fd080, -/* 0x018f: mmctx_multi_disabled */ +/* 0x019b: mmctx_multi_disabled */ 0xb70199f0, 0xc8010080, 0xb4b600ab, @@ -331,8 +334,8 @@ uint32_t nvc0_grhub_code[] = { 0xb601aec8, 0xbefd11e4, 0x008bd005, -/* 0x01a8: mmctx_exec_loop */ -/* 0x01a8: mmctx_wait_free */ +/* 0x01b4: mmctx_exec_loop */ +/* 0x01b4: mmctx_wait_free */ 0xf0008ecf, 0x0bf41fe4, 0x00ce98fa, @@ -341,76 +344,77 @@ uint32_t nvc0_grhub_code[] = { 0x04cdb804, 0xc8e81bf4, 0x1bf402ab, -/* 0x01c9: mmctx_fini_wait */ +/* 0x01d5: mmctx_fini_wait */ 0x008bcf18, 0xb01fb4f0, 0x1bf410b4, 0x02a7f0f7, 0xf4c921f4, -/* 0x01de: mmctx_stop */ +/* 0x01ea: mmctx_stop */ 0xabc81b0e, 0x10b4b600, 0xf00cb9f0, 0x8bd012b9, -/* 0x01ed: mmctx_stop_wait */ +/* 0x01f9: mmctx_stop_wait */ 0x008bcf00, 0xf412bbc8, -/* 0x01f6: mmctx_done */ - 0x87f1fa1b, - 0x84b6085c, - 0xf094bd06, - 0x89d00199, -/* 0x0207: strand_wait */ - 0xf900f800, - 0x02a7f0a0, - 0xfcc921f4, -/* 0x0213: strand_pre */ - 0xf100f8a0, - 0xf04afc87, - 0x97f00283, - 0x0089d00c, - 0x020721f5, -/* 0x0226: strand_post */ - 0x87f100f8, - 0x83f04afc, - 0x0d97f002, - 0xf50089d0, - 0xf8020721, -/* 0x0239: strand_set */ - 0xfca7f100, - 0x02a3f04f, - 0x0500aba2, - 0xd00fc7f0, - 0xc7f000ac, - 0x00bcd00b, - 0x020721f5, - 0xf000aed0, - 0xbcd00ac7, - 0x0721f500, -/* 0x0263: strand_ctx_init */ - 0xf100f802, - 0xb6083c87, - 0x94bd0684, - 0xd00399f0, +/* 0x0202: mmctx_done */ + 0x94bdfa1b, + 0xf10199f0, + 0xf0170007, + 0x09d00203, + 0xf804bd00, +/* 0x0215: strand_wait */ + 0xf0a0f900, + 0x21f402a7, + 0xf8a0fcc9, +/* 0x0221: strand_pre */ + 0xfc87f100, + 0x0283f04a, + 0xd00c97f0, 0x21f50089, - 0xe7f00213, - 0x3921f503, + 0x00f80215, +/* 0x0234: strand_post */ + 0x4afc87f1, + 0xf00283f0, + 0x89d00d97, + 0x1521f500, +/* 0x0247: strand_set */ + 0xf100f802, + 0xf04ffca7, + 0xaba202a3, + 0xc7f00500, + 0x00acd00f, + 0xd00bc7f0, + 0x21f500bc, + 0xaed00215, + 0x0ac7f000, + 0xf500bcd0, + 0xf8021521, +/* 0x0271: strand_ctx_init */ + 0xf094bd00, + 0x07f10399, + 0x03f00f00, + 0x0009d002, + 0x21f504bd, + 0xe7f00221, + 0x4721f503, 0xfca7f102, 0x02a3f046, 0x0400aba0, 0xf040a0d0, 0xbcd001c7, - 0x0721f500, + 0x1521f500, 0x010c9202, 0xf000acd0, 0xbcd002c7, - 0x0721f500, - 0x2621f502, + 0x1521f500, + 0x3421f502, 0x8087f102, 0x0684b608, 0xb70089cf, 0x95220080, -/* 0x02ba: ctx_init_strand_loop */ +/* 0x02ca: ctx_init_strand_loop */ 0x8ed008fe, 0x408ed000, 0xb6808acf, @@ -419,270 +423,278 @@ uint32_t nvc0_grhub_code[] = { 0xb60480b6, 0x1bf40192, 0x08e4b6e8, - 0xf1f2efbc, - 0xb6085c87, - 0x94bd0684, - 0xd00399f0, - 0x00f80089, -/* 0x02ec: error */ - 0xe7f1e0f9, - 0xe4b60814, - 0x00efd006, - 0x0c1ce7f1, - 0xf006e4b6, - 0xefd001f7, - 0xf8e0fc00, -/* 0x0309: init */ - 0xfe04bd00, - 0x07fe0004, - 0x0017f100, - 0x0227f012, - 0xf10012d0, - 0xfe058517, - 0x17f10010, - 0x10d00400, - 0x0437f1c0, - 0x0634b604, - 0x200327f1, - 0xf10032d0, - 0xd0200427, - 0x27f10132, - 0x32d0200b, - 0x0c27f102, - 0x0732d020, - 0x0c2427f1, - 0xb90624b6, - 0x23d00003, + 0xbdf2efbc, + 0x0399f094, + 0x170007f1, + 0xd00203f0, + 0x04bd0009, +/* 0x02fe: error */ + 0x07f100f8, + 0x03f00500, + 0x000fd002, + 0xf7f004bd, + 0x0007f101, + 0x0303f007, + 0xbd000fd0, +/* 0x031b: init */ + 0xbd00f804, + 0x0004fe04, + 0xf10007fe, + 0xf0120017, + 0x12d00227, + 0xb117f100, + 0x0010fe05, + 0x040017f1, + 0xf1c010d0, + 0xb6040437, + 0x27f10634, + 0x32d02003, 0x0427f100, - 0x0023f087, - 0xb70012d0, - 0xf0010012, - 0x12d00427, - 0x1031f400, - 0x9604e7f1, - 0xf440e3f0, - 0xf1c76821, - 0x03018090, - 0x801ff4f0, - 0x17f0020f, - 0x041fbb01, - 0xf10112b6, - 0xb6040c27, - 0x21d00624, - 0x4021d000, - 0x010017f1, - 0x98000e98, - 0x21f5010f, - 0x37f1013d, - 0x34b60700, - 0x08149506, - 0xd00034d0, - 0x30b74034, - 0x1fbb1300, - 0x02f5b600, - 0xb6003fd0, - 0x10b60815, - 0x0814b601, - 0xf5021fb9, - 0xbb026321, - 0x0398001f, - 0x0047f102, - 0x5043f020, -/* 0x03e4: init_gpc */ - 0x08044ea0, - 0xf4021fb9, - 0x4ea08d21, - 0xf4bd010c, - 0xa08d21f4, - 0xf401044e, - 0x4ea08d21, - 0xf7f00100, + 0x0132d020, + 0x200b27f1, + 0xf10232d0, + 0xd0200c27, + 0x27f10732, + 0x24b60c24, + 0x0003b906, + 0xf10023d0, + 0xf0870427, + 0x12d00023, + 0x0012b700, + 0x0427f001, + 0xf40012d0, + 0xe7f11031, + 0xe3f09604, + 0x6821f440, + 0x8090f1c7, + 0xf4f00301, + 0x020f801f, + 0xbb0117f0, + 0x12b6041f, + 0x0c27f101, + 0x0624b604, + 0xd00021d0, + 0x17f14021, + 0x0e980100, + 0x010f9800, + 0x014721f5, + 0x070037f1, + 0x950634b6, + 0x34d00814, + 0x4034d000, + 0x130030b7, + 0xb6001fbb, + 0x3fd002f5, + 0x0815b600, + 0xb60110b6, + 0x1fb90814, + 0x7121f502, + 0x001fbb02, + 0xf1020398, + 0xf0200047, +/* 0x03f6: init_gpc */ + 0x4ea05043, + 0x1fb90804, 0x8d21f402, - 0x08004ea0, -/* 0x040c: init_gpc_wait */ - 0xc86821f4, - 0x0bf41fff, - 0x044ea0fa, - 0x6821f408, - 0xb7001fbb, - 0xb6800040, - 0x1bf40132, - 0x0027f1be, - 0x0624b608, - 0xb74021d0, - 0xbd080020, + 0x010c4ea0, + 0x21f4f4bd, + 0x044ea08d, + 0x8d21f401, + 0x01004ea0, + 0xf402f7f0, + 0x4ea08d21, +/* 0x041e: init_gpc_wait */ + 0x21f40800, + 0x1fffc868, + 0xa0fa0bf4, + 0xf408044e, + 0x1fbb6821, + 0x0040b700, + 0x0132b680, + 0xf1be1bf4, + 0xf0010007, + 0x01d00203, + 0xbd04bd00, 0x1f19f014, -/* 0x043f: main */ - 0xf40021d0, - 0x28f40031, - 0x10d7f000, - 0xf43921f4, - 0xe4b1f401, - 0x1bf54001, - 0x87f100d1, - 0x84b6083c, - 0xf094bd06, - 0x89d00499, - 0x0017f100, - 0x0614b60b, - 0xcf4012cf, - 0x13c80011, - 0x7e0bf41f, + 0x080007f1, + 0xd00203f0, + 0x04bd0001, +/* 0x0458: main */ + 0xf40031f4, + 0xd7f00028, + 0x3921f410, + 0xb1f401f4, + 0xf54001e4, + 0xbd00de1b, + 0x0499f094, + 0x0f0007f1, + 0xd00203f0, + 0x04bd0009, + 0x0b0017f1, + 0xcf0614b6, + 0x11cf4012, + 0x1f13c800, + 0x00870bf5, 0xf41f23c8, - 0x20f95a0b, - 0xf10212b9, - 0xb6083c87, - 0x94bd0684, - 0xd00799f0, - 0x32f40089, - 0x0231f401, - 0x07f521f5, - 0x085c87f1, - 0xbd0684b6, + 0x20f9620b, + 0xbd0212b9, 0x0799f094, - 0xfc0089d0, - 0x3c87f120, - 0x0684b608, - 0x99f094bd, - 0x0089d006, - 0xf50131f4, - 0xf107f521, - 0xb6085c87, - 0x94bd0684, - 0xd00699f0, - 0x0ef40089, -/* 0x04d5: chsw_prev_no_next */ + 0x0f0007f1, + 0xd00203f0, + 0x04bd0009, + 0xf40132f4, + 0x21f50231, + 0x94bd082f, + 0xf10799f0, + 0xf0170007, + 0x09d00203, + 0xfc04bd00, + 0xf094bd20, + 0x07f10699, + 0x03f00f00, + 0x0009d002, + 0x31f404bd, + 0x2f21f501, + 0xf094bd08, + 0x07f10699, + 0x03f01700, + 0x0009d002, + 0x0ef404bd, +/* 0x04f9: chsw_prev_no_next */ 0xb920f931, 0x32f40212, 0x0232f401, - 0x07f521f5, + 0x082f21f5, 0x17f120fc, 0x14b60b00, 0x0012d006, -/* 0x04f3: chsw_no_prev */ +/* 0x0517: chsw_no_prev */ 0xc8130ef4, 0x0bf41f23, 0x0131f40d, 0xf50232f4, -/* 0x0503: chsw_done */ - 0xf107f521, +/* 0x0527: chsw_done */ + 0xf1082f21, 0xb60b0c17, 0x27f00614, 0x0012d001, - 0x085c87f1, - 0xbd0684b6, - 0x0499f094, - 0xf50089d0, -/* 0x0523: main_not_ctx_switch */ - 0xb0ff200e, - 0x1bf401e4, - 0x02f2b90d, - 0x078121f5, -/* 0x0533: main_not_ctx_chan */ - 0xb0420ef4, - 0x1bf402e4, - 0x3c87f12e, - 0x0684b608, 0x99f094bd, - 0x0089d007, + 0x0007f104, + 0x0203f017, + 0xbd0009d0, + 0x130ef504, +/* 0x0549: main_not_ctx_switch */ + 0x01e4b0ff, + 0xb90d1bf4, + 0x21f502f2, + 0x0ef407bb, +/* 0x0559: main_not_ctx_chan */ + 0x02e4b046, + 0xbd321bf4, + 0x0799f094, + 0x0f0007f1, + 0xd00203f0, + 0x04bd0009, 0xf40132f4, 0x21f50232, - 0x87f107f5, - 0x84b6085c, - 0xf094bd06, - 0x89d00799, - 0x110ef400, -/* 0x0564: main_not_ctx_save */ - 0xf010ef94, - 0x21f501f5, - 0x0ef502ec, -/* 0x0572: main_done */ - 0x17f1fed1, - 0x14b60820, - 0xf024bd06, - 0x12d01f29, - 0xbe0ef500, -/* 0x0585: ih */ + 0x94bd082f, + 0xf10799f0, + 0xf0170007, + 0x09d00203, + 0xf404bd00, +/* 0x058e: main_not_ctx_save */ + 0xef94110e, + 0x01f5f010, + 0x02fe21f5, + 0xfec00ef5, +/* 0x059c: main_done */ + 0x29f024bd, + 0x0007f11f, + 0x0203f008, + 0xbd0002d0, + 0xab0ef504, +/* 0x05b1: ih */ 0xfe80f9fe, 0x80f90188, 0xa0f990f9, 0xd0f9b0f9, 0xf0f9e0f9, - 0xc4800acf, - 0x0bf404ab, - 0x00b7f11d, - 0x10d7f019, - 0xcf40becf, - 0x21f400bf, - 0x00b0b704, - 0x01e7f004, -/* 0x05bb: ih_no_fifo */ - 0xe400bed0, - 0xf40100ab, - 0xd7f00d0b, - 0x01e7f110, - 0x0421f440, -/* 0x05cc: ih_no_ctxsw */ - 0x0104b7f1, - 0xabffb0bd, - 0x0d0bf4b4, - 0x0c1ca7f1, - 0xd006a4b6, -/* 0x05e2: ih_no_other */ - 0x0ad000ab, - 0xfcf0fc40, - 0xfcd0fce0, - 0xfca0fcb0, - 0xfe80fc90, - 0x80fc0088, - 0xf80032f4, -/* 0x05fd: ctx_4160s */ - 0x60e7f101, - 0x40e3f041, - 0xf401f7f0, -/* 0x060a: ctx_4160s_wait */ - 0x21f48d21, - 0x04ffc868, - 0xf8fa0bf4, -/* 0x0615: ctx_4160c */ - 0x60e7f100, + 0x0acf04bd, + 0x04abc480, + 0xf11d0bf4, + 0xf01900b7, + 0xbecf10d7, + 0x00bfcf40, + 0xb70421f4, + 0xf00400b0, + 0xbed001e7, +/* 0x05e9: ih_no_fifo */ + 0x00abe400, + 0x0d0bf401, + 0xf110d7f0, + 0xf44001e7, +/* 0x05fa: ih_no_ctxsw */ + 0xb7f10421, + 0xb0bd0104, + 0xf4b4abff, + 0xa7f10d0b, + 0xa4b60c1c, + 0x00abd006, +/* 0x0610: ih_no_other */ + 0xfc400ad0, + 0xfce0fcf0, + 0xfcb0fcd0, + 0xfc90fca0, + 0x0088fe80, + 0x32f480fc, +/* 0x062b: ctx_4160s */ + 0xf101f800, + 0xf04160e7, + 0xf7f040e3, + 0x8d21f401, +/* 0x0638: ctx_4160s_wait */ + 0xc86821f4, + 0x0bf404ff, +/* 0x0643: ctx_4160c */ + 0xf100f8fa, + 0xf04160e7, + 0xf4bd40e3, + 0xf88d21f4, +/* 0x0651: ctx_4170s */ + 0x70e7f100, 0x40e3f041, - 0x21f4f4bd, -/* 0x0623: ctx_4170s */ - 0xf100f88d, - 0xf04170e7, - 0xf5f040e3, - 0x8d21f410, -/* 0x0632: ctx_4170w */ - 0xe7f100f8, - 0xe3f04170, - 0x6821f440, - 0xf410f4f0, - 0x00f8f31b, -/* 0x0644: ctx_redswitch */ - 0x0614e7f1, - 0xf106e4b6, - 0xd00270f7, - 0xf7f000ef, -/* 0x0655: ctx_redswitch_delay */ - 0x01f2b608, - 0xf1fd1bf4, - 0xd00770f7, - 0x00f800ef, -/* 0x0664: ctx_86c */ - 0x086ce7f1, - 0xd006e4b6, - 0xe7f100ef, - 0xe3f08a14, - 0x8d21f440, - 0xa86ce7f1, - 0xf441e3f0, + 0xf410f5f0, 0x00f88d21, -/* 0x0684: ctx_load */ - 0x083c87f1, - 0xbd0684b6, - 0x0599f094, - 0xf00089d0, +/* 0x0660: ctx_4170w */ + 0x4170e7f1, + 0xf440e3f0, + 0xf4f06821, + 0xf31bf410, +/* 0x0672: ctx_redswitch */ + 0xe7f100f8, + 0xe4b60614, + 0x70f7f106, + 0x00efd002, +/* 0x0683: ctx_redswitch_delay */ + 0xb608f7f0, + 0x1bf401f2, + 0x70f7f1fd, + 0x00efd007, +/* 0x0692: ctx_86c */ + 0xe7f100f8, + 0xe4b6086c, + 0x00efd006, + 0x8a14e7f1, + 0xf440e3f0, + 0xe7f18d21, + 0xe3f0a86c, + 0x8d21f441, +/* 0x06b2: ctx_load */ + 0x94bd00f8, + 0xf10599f0, + 0xf00f0007, + 0x09d00203, + 0xf004bd00, 0x21f40ca7, 0x2417f1c9, 0x0614b60a, @@ -693,165 +705,217 @@ uint32_t nvc0_grhub_code[] = { 0x0614b60a, 0xd00747f0, 0x14d00012, -/* 0x06bd: ctx_chan_wait_0 */ +/* 0x06ed: ctx_chan_wait_0 */ 0x4014cf40, 0xf41f44f0, 0x32d0fa1b, 0x000bfe00, 0xb61f2af0, 0x20b60424, - 0x3c87f102, - 0x0684b608, + 0xf094bd02, + 0x07f10899, + 0x03f00f00, + 0x0009d002, + 0x17f104bd, + 0x14b60a04, + 0x0012d006, + 0x0a2017f1, + 0xf00614b6, + 0x23f10227, + 0x12d08000, + 0x1017f000, + 0x020027f1, + 0xfa0223f0, + 0x03f80512, 0x99f094bd, - 0x0089d008, - 0x0a0417f1, - 0xd00614b6, - 0x17f10012, - 0x14b60a20, - 0x0227f006, - 0x800023f1, - 0xf00012d0, - 0x27f11017, - 0x23f00200, - 0x0512fa02, - 0x87f103f8, - 0x84b6085c, - 0xf094bd06, - 0x89d00899, - 0x81019800, + 0x0007f108, + 0x0203f017, + 0xbd0009d0, + 0x81019804, 0x981814b6, 0x25b68002, 0x0512fd08, - 0xf1160180, - 0xb6083c87, - 0x94bd0684, - 0xd00999f0, - 0x27f10089, - 0x24b60a04, - 0x0021d006, - 0xf10127f0, - 0xb60a2017, - 0x12d00614, - 0x0017f100, - 0x0613f001, - 0xf80501fa, - 0x5c87f103, - 0x0684b608, - 0x99f094bd, - 0x0089d009, - 0x085c87f1, - 0xbd0684b6, - 0x0599f094, - 0xf80089d0, -/* 0x0781: ctx_chan */ - 0xfd21f500, - 0x8421f505, - 0x0ca7f006, - 0xf1c921f4, - 0xb60a1017, - 0x27f00614, - 0x0012d005, -/* 0x079c: ctx_chan_wait */ - 0xfd0012cf, - 0x1bf40522, - 0x1521f5fa, -/* 0x07ab: ctx_mmio_exec */ - 0x9800f806, - 0x27f14103, - 0x24b60a04, - 0x0023d006, -/* 0x07ba: ctx_mmio_loop */ - 0x34c434bd, - 0x0f1bf4ff, - 0x020057f1, - 0xfa0653f0, - 0x03f80535, -/* 0x07cc: ctx_mmio_pull */ - 0x98804e98, - 0x21f4814f, - 0x0830b68d, - 0xf40112b6, -/* 0x07de: ctx_mmio_done */ - 0x0398df1b, - 0x0023d016, - 0xf1400080, + 0xbd160180, + 0x0999f094, + 0x0f0007f1, + 0xd00203f0, + 0x04bd0009, + 0x0a0427f1, + 0xd00624b6, + 0x27f00021, + 0x2017f101, + 0x0614b60a, + 0xf10012d0, 0xf0010017, 0x01fa0613, - 0xf803f806, -/* 0x07f5: ctx_xfer */ - 0x00f7f100, - 0x06f4b60c, - 0xd004e7f0, -/* 0x0802: ctx_xfer_idle */ - 0xfecf80fe, - 0x00e4f100, - 0xf91bf420, - 0xf40611f4, -/* 0x0812: ctx_xfer_pre */ - 0xf7f01102, - 0x6421f510, - 0xfd21f506, - 0x1c11f405, -/* 0x0820: ctx_xfer_pre_load */ - 0xf502f7f0, - 0xf5062321, - 0xf5063221, - 0xbd064421, - 0x2321f5f4, - 0x8421f506, -/* 0x0839: ctx_xfer_exec */ - 0x16019806, - 0x041427f1, + 0xbd03f805, + 0x0999f094, + 0x170007f1, + 0xd00203f0, + 0x04bd0009, + 0x99f094bd, + 0x0007f105, + 0x0203f017, + 0xbd0009d0, +/* 0x07bb: ctx_chan */ + 0xf500f804, + 0xf5062b21, + 0xf006b221, + 0x21f40ca7, + 0x1017f1c9, + 0x0614b60a, + 0xd00527f0, +/* 0x07d6: ctx_chan_wait */ + 0x12cf0012, + 0x0522fd00, + 0xf5fa1bf4, + 0xf8064321, +/* 0x07e5: ctx_mmio_exec */ + 0x41039800, + 0x0a0427f1, 0xd00624b6, - 0xe7f10020, - 0xe3f0a500, - 0x021fb941, + 0x34bd0023, +/* 0x07f4: ctx_mmio_loop */ + 0xf4ff34c4, + 0x57f10f1b, + 0x53f00200, + 0x0535fa06, +/* 0x0806: ctx_mmio_pull */ + 0x4e9803f8, + 0x814f9880, 0xb68d21f4, - 0xfcf004e0, - 0x022cf001, - 0xfd0124b6, - 0x21f405f2, - 0xfc17f18d, - 0x0213f04a, - 0xd00c27f0, - 0x21f50012, - 0x27f10207, - 0x23f047fc, - 0x0020d002, - 0xb6012cf0, - 0x12d00320, - 0x01acf000, - 0xf006a5f0, - 0x0c9800b7, - 0x010d9800, - 0xf500e7f0, - 0xf0015c21, - 0x21f508a7, - 0x21f50103, - 0x01f40207, - 0x0ca7f022, - 0xf1c921f4, - 0xb60a1017, - 0x27f00614, - 0x0012d005, -/* 0x08c0: ctx_xfer_post_save_wait */ - 0xfd0012cf, - 0x1bf40522, - 0x3202f4fa, -/* 0x08cc: ctx_xfer_post */ - 0xf502f7f0, - 0xbd062321, - 0x6421f5f4, - 0x2621f506, - 0x3221f502, + 0x12b60830, + 0xdf1bf401, +/* 0x0818: ctx_mmio_done */ + 0xd0160398, + 0x00800023, + 0x0017f140, + 0x0613f001, + 0xf80601fa, +/* 0x082f: ctx_xfer */ + 0xf100f803, + 0xb60c00f7, + 0xe7f006f4, + 0x80fed004, +/* 0x083c: ctx_xfer_idle */ + 0xf100fecf, + 0xf42000e4, + 0x11f4f91b, + 0x1102f406, +/* 0x084c: ctx_xfer_pre */ + 0xf510f7f0, + 0xf5069221, + 0xf4062b21, +/* 0x085a: ctx_xfer_pre_load */ + 0xf7f01c11, + 0x5121f502, + 0x6021f506, + 0x7221f506, 0xf5f4bd06, - 0xf4062321, - 0x01981011, - 0x0511fd40, - 0xf5070bf4, -/* 0x08f7: ctx_xfer_no_post_mmio */ - 0xf507ab21, -/* 0x08fb: ctx_xfer_done */ - 0xf8061521, + 0xf5065121, +/* 0x0873: ctx_xfer_exec */ + 0x9806b221, + 0x27f11601, + 0x24b60414, + 0x0020d006, + 0xa500e7f1, + 0xb941e3f0, + 0x21f4021f, + 0x04e0b68d, + 0xf001fcf0, + 0x24b6022c, + 0x05f2fd01, + 0xf18d21f4, + 0xf04afc17, + 0x27f00213, + 0x0012d00c, + 0x021521f5, + 0x47fc27f1, + 0xd00223f0, + 0x2cf00020, + 0x0320b601, + 0xf00012d0, + 0xa5f001ac, + 0x00b7f006, + 0x98000c98, + 0xe7f0010d, + 0x6621f500, + 0x08a7f001, + 0x010921f5, + 0x021521f5, + 0xf02201f4, + 0x21f40ca7, + 0x1017f1c9, + 0x0614b60a, + 0xd00527f0, +/* 0x08fa: ctx_xfer_post_save_wait */ + 0x12cf0012, + 0x0522fd00, + 0xf4fa1bf4, +/* 0x0906: ctx_xfer_post */ + 0xf7f03202, + 0x5121f502, + 0xf5f4bd06, + 0xf5069221, + 0xf5023421, + 0xbd066021, + 0x5121f5f4, + 0x1011f406, + 0xfd400198, + 0x0bf40511, + 0xe521f507, +/* 0x0931: ctx_xfer_no_post_mmio */ + 0x4321f507, +/* 0x0935: ctx_xfer_done */ + 0x0000f806, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, 0x00000000, }; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvd7.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvd7.fuc.h index 141d6a8ffe5d..a1b9f763996a 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvd7.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvd7.fuc.h @@ -206,14 +206,14 @@ uint32_t nvd7_grhub_data[] = { }; uint32_t nvd7_grhub_code[] = { - 0x03090ef5, + 0x031b0ef5, /* 0x0004: queue_put */ 0x9800d898, 0x86f001d9, 0x0489b808, 0xf00c1bf4, 0x21f502f7, - 0x00f802ec, + 0x00f802fe, /* 0x001c: queue_put_next */ 0xb60798c4, 0x8dbb0384, @@ -245,7 +245,7 @@ uint32_t nvd7_grhub_code[] = { 0xc800bccf, 0x1bf41fcc, 0x06a7f0fa, - 0x010321f5, + 0x010921f5, 0xf840bfcf, /* 0x008d: nv_wr32 */ 0x28b7f100, @@ -267,63 +267,66 @@ uint32_t nvd7_grhub_code[] = { 0x0684b604, 0xf80080d0, /* 0x00c9: wait_donez */ - 0x3c87f100, - 0x0684b608, - 0x99f094bd, - 0x0089d000, - 0x081887f1, - 0xd00684b6, -/* 0x00e2: wait_donez_ne */ - 0x87f1008a, - 0x84b60400, - 0x0088cf06, + 0xf094bd00, + 0x07f10099, + 0x03f00f00, + 0x0009d002, + 0x07f104bd, + 0x03f00600, + 0x000ad002, +/* 0x00e6: wait_donez_ne */ + 0x87f104bd, + 0x83f00000, + 0x0088cf01, 0xf4888aff, - 0x87f1f31b, - 0x84b6085c, - 0xf094bd06, - 0x89d00099, -/* 0x0103: wait_doneo */ - 0xf100f800, - 0xb6083c87, - 0x94bd0684, - 0xd00099f0, - 0x87f10089, + 0x94bdf31b, + 0xf10099f0, + 0xf0170007, + 0x09d00203, + 0xf804bd00, +/* 0x0109: wait_doneo */ + 0xf094bd00, + 0x07f10099, + 0x03f00f00, + 0x0009d002, + 0x87f104bd, 0x84b60818, 0x008ad006, -/* 0x011c: wait_doneo_e */ +/* 0x0124: wait_doneo_e */ 0x040087f1, 0xcf0684b6, 0x8aff0088, 0xf30bf488, - 0x085c87f1, - 0xbd0684b6, - 0x0099f094, - 0xf80089d0, -/* 0x013d: mmctx_size */ -/* 0x013f: nv_mmctx_size_loop */ - 0x9894bd00, - 0x85b600e8, - 0x0180b61a, - 0xbb0284b6, - 0xe0b60098, - 0x04efb804, - 0xb9eb1bf4, - 0x00f8029f, -/* 0x015c: mmctx_xfer */ - 0x083c87f1, - 0xbd0684b6, - 0x0199f094, - 0xf10089d0, + 0x99f094bd, + 0x0007f100, + 0x0203f017, + 0xbd0009d0, +/* 0x0147: mmctx_size */ + 0xbd00f804, +/* 0x0149: nv_mmctx_size_loop */ + 0x00e89894, + 0xb61a85b6, + 0x84b60180, + 0x0098bb02, + 0xb804e0b6, + 0x1bf404ef, + 0x029fb9eb, +/* 0x0166: mmctx_xfer */ + 0x94bd00f8, + 0xf10199f0, + 0xf00f0007, + 0x09d00203, + 0xf104bd00, 0xb6071087, 0x94bd0684, 0xf405bbfd, 0x8bd0090b, 0x0099f000, -/* 0x0180: mmctx_base_disabled */ +/* 0x018c: mmctx_base_disabled */ 0xf405eefd, 0x8ed00c0b, 0xc08fd080, -/* 0x018f: mmctx_multi_disabled */ +/* 0x019b: mmctx_multi_disabled */ 0xb70199f0, 0xc8010080, 0xb4b600ab, @@ -331,8 +334,8 @@ uint32_t nvd7_grhub_code[] = { 0xb601aec8, 0xbefd11e4, 0x008bd005, -/* 0x01a8: mmctx_exec_loop */ -/* 0x01a8: mmctx_wait_free */ +/* 0x01b4: mmctx_exec_loop */ +/* 0x01b4: mmctx_wait_free */ 0xf0008ecf, 0x0bf41fe4, 0x00ce98fa, @@ -341,76 +344,77 @@ uint32_t nvd7_grhub_code[] = { 0x04cdb804, 0xc8e81bf4, 0x1bf402ab, -/* 0x01c9: mmctx_fini_wait */ +/* 0x01d5: mmctx_fini_wait */ 0x008bcf18, 0xb01fb4f0, 0x1bf410b4, 0x02a7f0f7, 0xf4c921f4, -/* 0x01de: mmctx_stop */ +/* 0x01ea: mmctx_stop */ 0xabc81b0e, 0x10b4b600, 0xf00cb9f0, 0x8bd012b9, -/* 0x01ed: mmctx_stop_wait */ +/* 0x01f9: mmctx_stop_wait */ 0x008bcf00, 0xf412bbc8, -/* 0x01f6: mmctx_done */ - 0x87f1fa1b, - 0x84b6085c, - 0xf094bd06, - 0x89d00199, -/* 0x0207: strand_wait */ - 0xf900f800, - 0x02a7f0a0, - 0xfcc921f4, -/* 0x0213: strand_pre */ - 0xf100f8a0, - 0xf04afc87, - 0x97f00283, - 0x0089d00c, - 0x020721f5, -/* 0x0226: strand_post */ - 0x87f100f8, - 0x83f04afc, - 0x0d97f002, - 0xf50089d0, - 0xf8020721, -/* 0x0239: strand_set */ - 0xfca7f100, - 0x02a3f04f, - 0x0500aba2, - 0xd00fc7f0, - 0xc7f000ac, - 0x00bcd00b, - 0x020721f5, - 0xf000aed0, - 0xbcd00ac7, - 0x0721f500, -/* 0x0263: strand_ctx_init */ - 0xf100f802, - 0xb6083c87, - 0x94bd0684, - 0xd00399f0, +/* 0x0202: mmctx_done */ + 0x94bdfa1b, + 0xf10199f0, + 0xf0170007, + 0x09d00203, + 0xf804bd00, +/* 0x0215: strand_wait */ + 0xf0a0f900, + 0x21f402a7, + 0xf8a0fcc9, +/* 0x0221: strand_pre */ + 0xfc87f100, + 0x0283f04a, + 0xd00c97f0, 0x21f50089, - 0xe7f00213, - 0x3921f503, + 0x00f80215, +/* 0x0234: strand_post */ + 0x4afc87f1, + 0xf00283f0, + 0x89d00d97, + 0x1521f500, +/* 0x0247: strand_set */ + 0xf100f802, + 0xf04ffca7, + 0xaba202a3, + 0xc7f00500, + 0x00acd00f, + 0xd00bc7f0, + 0x21f500bc, + 0xaed00215, + 0x0ac7f000, + 0xf500bcd0, + 0xf8021521, +/* 0x0271: strand_ctx_init */ + 0xf094bd00, + 0x07f10399, + 0x03f00f00, + 0x0009d002, + 0x21f504bd, + 0xe7f00221, + 0x4721f503, 0xfca7f102, 0x02a3f046, 0x0400aba0, 0xf040a0d0, 0xbcd001c7, - 0x0721f500, + 0x1521f500, 0x010c9202, 0xf000acd0, 0xbcd002c7, - 0x0721f500, - 0x2621f502, + 0x1521f500, + 0x3421f502, 0x8087f102, 0x0684b608, 0xb70089cf, 0x95220080, -/* 0x02ba: ctx_init_strand_loop */ +/* 0x02ca: ctx_init_strand_loop */ 0x8ed008fe, 0x408ed000, 0xb6808acf, @@ -419,270 +423,278 @@ uint32_t nvd7_grhub_code[] = { 0xb60480b6, 0x1bf40192, 0x08e4b6e8, - 0xf1f2efbc, - 0xb6085c87, - 0x94bd0684, - 0xd00399f0, - 0x00f80089, -/* 0x02ec: error */ - 0xe7f1e0f9, - 0xe4b60814, - 0x00efd006, - 0x0c1ce7f1, - 0xf006e4b6, - 0xefd001f7, - 0xf8e0fc00, -/* 0x0309: init */ - 0xfe04bd00, - 0x07fe0004, - 0x0017f100, - 0x0227f012, - 0xf10012d0, - 0xfe058517, - 0x17f10010, - 0x10d00400, - 0x0437f1c0, - 0x0634b604, - 0x200327f1, - 0xf10032d0, - 0xd0200427, - 0x27f10132, - 0x32d0200b, - 0x0c27f102, - 0x0732d020, - 0x0c2427f1, - 0xb90624b6, - 0x23d00003, + 0xbdf2efbc, + 0x0399f094, + 0x170007f1, + 0xd00203f0, + 0x04bd0009, +/* 0x02fe: error */ + 0x07f100f8, + 0x03f00500, + 0x000fd002, + 0xf7f004bd, + 0x0007f101, + 0x0303f007, + 0xbd000fd0, +/* 0x031b: init */ + 0xbd00f804, + 0x0004fe04, + 0xf10007fe, + 0xf0120017, + 0x12d00227, + 0xb117f100, + 0x0010fe05, + 0x040017f1, + 0xf1c010d0, + 0xb6040437, + 0x27f10634, + 0x32d02003, 0x0427f100, - 0x0023f087, - 0xb70012d0, - 0xf0010012, - 0x12d00427, - 0x1031f400, - 0x9604e7f1, - 0xf440e3f0, - 0xf1c76821, - 0x03018090, - 0x801ff4f0, - 0x17f0020f, - 0x041fbb01, - 0xf10112b6, - 0xb6040c27, - 0x21d00624, - 0x4021d000, - 0x010017f1, - 0x98000e98, - 0x21f5010f, - 0x37f1013d, - 0x34b60700, - 0x08149506, - 0xd00034d0, - 0x30b74034, - 0x1fbb1300, - 0x02f5b600, - 0xb6003fd0, - 0x10b60815, - 0x0814b601, - 0xf5021fb9, - 0xbb026321, - 0x0398001f, - 0x0047f102, - 0x5043f020, -/* 0x03e4: init_gpc */ - 0x08044ea0, - 0xf4021fb9, - 0x4ea08d21, - 0xf4bd010c, - 0xa08d21f4, - 0xf401044e, - 0x4ea08d21, - 0xf7f00100, + 0x0132d020, + 0x200b27f1, + 0xf10232d0, + 0xd0200c27, + 0x27f10732, + 0x24b60c24, + 0x0003b906, + 0xf10023d0, + 0xf0870427, + 0x12d00023, + 0x0012b700, + 0x0427f001, + 0xf40012d0, + 0xe7f11031, + 0xe3f09604, + 0x6821f440, + 0x8090f1c7, + 0xf4f00301, + 0x020f801f, + 0xbb0117f0, + 0x12b6041f, + 0x0c27f101, + 0x0624b604, + 0xd00021d0, + 0x17f14021, + 0x0e980100, + 0x010f9800, + 0x014721f5, + 0x070037f1, + 0x950634b6, + 0x34d00814, + 0x4034d000, + 0x130030b7, + 0xb6001fbb, + 0x3fd002f5, + 0x0815b600, + 0xb60110b6, + 0x1fb90814, + 0x7121f502, + 0x001fbb02, + 0xf1020398, + 0xf0200047, +/* 0x03f6: init_gpc */ + 0x4ea05043, + 0x1fb90804, 0x8d21f402, - 0x08004ea0, -/* 0x040c: init_gpc_wait */ - 0xc86821f4, - 0x0bf41fff, - 0x044ea0fa, - 0x6821f408, - 0xb7001fbb, - 0xb6800040, - 0x1bf40132, - 0x0027f1be, - 0x0624b608, - 0xb74021d0, - 0xbd080020, + 0x010c4ea0, + 0x21f4f4bd, + 0x044ea08d, + 0x8d21f401, + 0x01004ea0, + 0xf402f7f0, + 0x4ea08d21, +/* 0x041e: init_gpc_wait */ + 0x21f40800, + 0x1fffc868, + 0xa0fa0bf4, + 0xf408044e, + 0x1fbb6821, + 0x0040b700, + 0x0132b680, + 0xf1be1bf4, + 0xf0010007, + 0x01d00203, + 0xbd04bd00, 0x1f19f014, -/* 0x043f: main */ - 0xf40021d0, - 0x28f40031, - 0x10d7f000, - 0xf43921f4, - 0xe4b1f401, - 0x1bf54001, - 0x87f100d1, - 0x84b6083c, - 0xf094bd06, - 0x89d00499, - 0x0017f100, - 0x0614b60b, - 0xcf4012cf, - 0x13c80011, - 0x7e0bf41f, + 0x080007f1, + 0xd00203f0, + 0x04bd0001, +/* 0x0458: main */ + 0xf40031f4, + 0xd7f00028, + 0x3921f410, + 0xb1f401f4, + 0xf54001e4, + 0xbd00de1b, + 0x0499f094, + 0x0f0007f1, + 0xd00203f0, + 0x04bd0009, + 0x0b0017f1, + 0xcf0614b6, + 0x11cf4012, + 0x1f13c800, + 0x00870bf5, 0xf41f23c8, - 0x20f95a0b, - 0xf10212b9, - 0xb6083c87, - 0x94bd0684, - 0xd00799f0, - 0x32f40089, - 0x0231f401, - 0x07f521f5, - 0x085c87f1, - 0xbd0684b6, + 0x20f9620b, + 0xbd0212b9, 0x0799f094, - 0xfc0089d0, - 0x3c87f120, - 0x0684b608, - 0x99f094bd, - 0x0089d006, - 0xf50131f4, - 0xf107f521, - 0xb6085c87, - 0x94bd0684, - 0xd00699f0, - 0x0ef40089, -/* 0x04d5: chsw_prev_no_next */ + 0x0f0007f1, + 0xd00203f0, + 0x04bd0009, + 0xf40132f4, + 0x21f50231, + 0x94bd082f, + 0xf10799f0, + 0xf0170007, + 0x09d00203, + 0xfc04bd00, + 0xf094bd20, + 0x07f10699, + 0x03f00f00, + 0x0009d002, + 0x31f404bd, + 0x2f21f501, + 0xf094bd08, + 0x07f10699, + 0x03f01700, + 0x0009d002, + 0x0ef404bd, +/* 0x04f9: chsw_prev_no_next */ 0xb920f931, 0x32f40212, 0x0232f401, - 0x07f521f5, + 0x082f21f5, 0x17f120fc, 0x14b60b00, 0x0012d006, -/* 0x04f3: chsw_no_prev */ +/* 0x0517: chsw_no_prev */ 0xc8130ef4, 0x0bf41f23, 0x0131f40d, 0xf50232f4, -/* 0x0503: chsw_done */ - 0xf107f521, +/* 0x0527: chsw_done */ + 0xf1082f21, 0xb60b0c17, 0x27f00614, 0x0012d001, - 0x085c87f1, - 0xbd0684b6, - 0x0499f094, - 0xf50089d0, -/* 0x0523: main_not_ctx_switch */ - 0xb0ff200e, - 0x1bf401e4, - 0x02f2b90d, - 0x078121f5, -/* 0x0533: main_not_ctx_chan */ - 0xb0420ef4, - 0x1bf402e4, - 0x3c87f12e, - 0x0684b608, 0x99f094bd, - 0x0089d007, + 0x0007f104, + 0x0203f017, + 0xbd0009d0, + 0x130ef504, +/* 0x0549: main_not_ctx_switch */ + 0x01e4b0ff, + 0xb90d1bf4, + 0x21f502f2, + 0x0ef407bb, +/* 0x0559: main_not_ctx_chan */ + 0x02e4b046, + 0xbd321bf4, + 0x0799f094, + 0x0f0007f1, + 0xd00203f0, + 0x04bd0009, 0xf40132f4, 0x21f50232, - 0x87f107f5, - 0x84b6085c, - 0xf094bd06, - 0x89d00799, - 0x110ef400, -/* 0x0564: main_not_ctx_save */ - 0xf010ef94, - 0x21f501f5, - 0x0ef502ec, -/* 0x0572: main_done */ - 0x17f1fed1, - 0x14b60820, - 0xf024bd06, - 0x12d01f29, - 0xbe0ef500, -/* 0x0585: ih */ + 0x94bd082f, + 0xf10799f0, + 0xf0170007, + 0x09d00203, + 0xf404bd00, +/* 0x058e: main_not_ctx_save */ + 0xef94110e, + 0x01f5f010, + 0x02fe21f5, + 0xfec00ef5, +/* 0x059c: main_done */ + 0x29f024bd, + 0x0007f11f, + 0x0203f008, + 0xbd0002d0, + 0xab0ef504, +/* 0x05b1: ih */ 0xfe80f9fe, 0x80f90188, 0xa0f990f9, 0xd0f9b0f9, 0xf0f9e0f9, - 0xc4800acf, - 0x0bf404ab, - 0x00b7f11d, - 0x10d7f019, - 0xcf40becf, - 0x21f400bf, - 0x00b0b704, - 0x01e7f004, -/* 0x05bb: ih_no_fifo */ - 0xe400bed0, - 0xf40100ab, - 0xd7f00d0b, - 0x01e7f110, - 0x0421f440, -/* 0x05cc: ih_no_ctxsw */ - 0x0104b7f1, - 0xabffb0bd, - 0x0d0bf4b4, - 0x0c1ca7f1, - 0xd006a4b6, -/* 0x05e2: ih_no_other */ - 0x0ad000ab, - 0xfcf0fc40, - 0xfcd0fce0, - 0xfca0fcb0, - 0xfe80fc90, - 0x80fc0088, - 0xf80032f4, -/* 0x05fd: ctx_4160s */ - 0x60e7f101, - 0x40e3f041, - 0xf401f7f0, -/* 0x060a: ctx_4160s_wait */ - 0x21f48d21, - 0x04ffc868, - 0xf8fa0bf4, -/* 0x0615: ctx_4160c */ - 0x60e7f100, + 0x0acf04bd, + 0x04abc480, + 0xf11d0bf4, + 0xf01900b7, + 0xbecf10d7, + 0x00bfcf40, + 0xb70421f4, + 0xf00400b0, + 0xbed001e7, +/* 0x05e9: ih_no_fifo */ + 0x00abe400, + 0x0d0bf401, + 0xf110d7f0, + 0xf44001e7, +/* 0x05fa: ih_no_ctxsw */ + 0xb7f10421, + 0xb0bd0104, + 0xf4b4abff, + 0xa7f10d0b, + 0xa4b60c1c, + 0x00abd006, +/* 0x0610: ih_no_other */ + 0xfc400ad0, + 0xfce0fcf0, + 0xfcb0fcd0, + 0xfc90fca0, + 0x0088fe80, + 0x32f480fc, +/* 0x062b: ctx_4160s */ + 0xf101f800, + 0xf04160e7, + 0xf7f040e3, + 0x8d21f401, +/* 0x0638: ctx_4160s_wait */ + 0xc86821f4, + 0x0bf404ff, +/* 0x0643: ctx_4160c */ + 0xf100f8fa, + 0xf04160e7, + 0xf4bd40e3, + 0xf88d21f4, +/* 0x0651: ctx_4170s */ + 0x70e7f100, 0x40e3f041, - 0x21f4f4bd, -/* 0x0623: ctx_4170s */ - 0xf100f88d, - 0xf04170e7, - 0xf5f040e3, - 0x8d21f410, -/* 0x0632: ctx_4170w */ - 0xe7f100f8, - 0xe3f04170, - 0x6821f440, - 0xf410f4f0, - 0x00f8f31b, -/* 0x0644: ctx_redswitch */ - 0x0614e7f1, - 0xf106e4b6, - 0xd00270f7, - 0xf7f000ef, -/* 0x0655: ctx_redswitch_delay */ - 0x01f2b608, - 0xf1fd1bf4, - 0xd00770f7, - 0x00f800ef, -/* 0x0664: ctx_86c */ - 0x086ce7f1, - 0xd006e4b6, - 0xe7f100ef, - 0xe3f08a14, - 0x8d21f440, - 0xa86ce7f1, - 0xf441e3f0, + 0xf410f5f0, 0x00f88d21, -/* 0x0684: ctx_load */ - 0x083c87f1, - 0xbd0684b6, - 0x0599f094, - 0xf00089d0, +/* 0x0660: ctx_4170w */ + 0x4170e7f1, + 0xf440e3f0, + 0xf4f06821, + 0xf31bf410, +/* 0x0672: ctx_redswitch */ + 0xe7f100f8, + 0xe4b60614, + 0x70f7f106, + 0x00efd002, +/* 0x0683: ctx_redswitch_delay */ + 0xb608f7f0, + 0x1bf401f2, + 0x70f7f1fd, + 0x00efd007, +/* 0x0692: ctx_86c */ + 0xe7f100f8, + 0xe4b6086c, + 0x00efd006, + 0x8a14e7f1, + 0xf440e3f0, + 0xe7f18d21, + 0xe3f0a86c, + 0x8d21f441, +/* 0x06b2: ctx_load */ + 0x94bd00f8, + 0xf10599f0, + 0xf00f0007, + 0x09d00203, + 0xf004bd00, 0x21f40ca7, 0x2417f1c9, 0x0614b60a, @@ -693,165 +705,217 @@ uint32_t nvd7_grhub_code[] = { 0x0614b60a, 0xd00747f0, 0x14d00012, -/* 0x06bd: ctx_chan_wait_0 */ +/* 0x06ed: ctx_chan_wait_0 */ 0x4014cf40, 0xf41f44f0, 0x32d0fa1b, 0x000bfe00, 0xb61f2af0, 0x20b60424, - 0x3c87f102, - 0x0684b608, + 0xf094bd02, + 0x07f10899, + 0x03f00f00, + 0x0009d002, + 0x17f104bd, + 0x14b60a04, + 0x0012d006, + 0x0a2017f1, + 0xf00614b6, + 0x23f10227, + 0x12d08000, + 0x1017f000, + 0x020027f1, + 0xfa0223f0, + 0x03f80512, 0x99f094bd, - 0x0089d008, - 0x0a0417f1, - 0xd00614b6, - 0x17f10012, - 0x14b60a20, - 0x0227f006, - 0x800023f1, - 0xf00012d0, - 0x27f11017, - 0x23f00200, - 0x0512fa02, - 0x87f103f8, - 0x84b6085c, - 0xf094bd06, - 0x89d00899, - 0x81019800, + 0x0007f108, + 0x0203f017, + 0xbd0009d0, + 0x81019804, 0x981814b6, 0x25b68002, 0x0512fd08, - 0xf1160180, - 0xb6083c87, - 0x94bd0684, - 0xd00999f0, - 0x27f10089, - 0x24b60a04, - 0x0021d006, - 0xf10127f0, - 0xb60a2017, - 0x12d00614, - 0x0017f100, - 0x0613f001, - 0xf80501fa, - 0x5c87f103, - 0x0684b608, - 0x99f094bd, - 0x0089d009, - 0x085c87f1, - 0xbd0684b6, - 0x0599f094, - 0xf80089d0, -/* 0x0781: ctx_chan */ - 0xfd21f500, - 0x8421f505, - 0x0ca7f006, - 0xf1c921f4, - 0xb60a1017, - 0x27f00614, - 0x0012d005, -/* 0x079c: ctx_chan_wait */ - 0xfd0012cf, - 0x1bf40522, - 0x1521f5fa, -/* 0x07ab: ctx_mmio_exec */ - 0x9800f806, - 0x27f14103, - 0x24b60a04, - 0x0023d006, -/* 0x07ba: ctx_mmio_loop */ - 0x34c434bd, - 0x0f1bf4ff, - 0x020057f1, - 0xfa0653f0, - 0x03f80535, -/* 0x07cc: ctx_mmio_pull */ - 0x98804e98, - 0x21f4814f, - 0x0830b68d, - 0xf40112b6, -/* 0x07de: ctx_mmio_done */ - 0x0398df1b, - 0x0023d016, - 0xf1400080, + 0xbd160180, + 0x0999f094, + 0x0f0007f1, + 0xd00203f0, + 0x04bd0009, + 0x0a0427f1, + 0xd00624b6, + 0x27f00021, + 0x2017f101, + 0x0614b60a, + 0xf10012d0, 0xf0010017, 0x01fa0613, - 0xf803f806, -/* 0x07f5: ctx_xfer */ - 0x00f7f100, - 0x06f4b60c, - 0xd004e7f0, -/* 0x0802: ctx_xfer_idle */ - 0xfecf80fe, - 0x00e4f100, - 0xf91bf420, - 0xf40611f4, -/* 0x0812: ctx_xfer_pre */ - 0xf7f01102, - 0x6421f510, - 0xfd21f506, - 0x1c11f405, -/* 0x0820: ctx_xfer_pre_load */ - 0xf502f7f0, - 0xf5062321, - 0xf5063221, - 0xbd064421, - 0x2321f5f4, - 0x8421f506, -/* 0x0839: ctx_xfer_exec */ - 0x16019806, - 0x041427f1, + 0xbd03f805, + 0x0999f094, + 0x170007f1, + 0xd00203f0, + 0x04bd0009, + 0x99f094bd, + 0x0007f105, + 0x0203f017, + 0xbd0009d0, +/* 0x07bb: ctx_chan */ + 0xf500f804, + 0xf5062b21, + 0xf006b221, + 0x21f40ca7, + 0x1017f1c9, + 0x0614b60a, + 0xd00527f0, +/* 0x07d6: ctx_chan_wait */ + 0x12cf0012, + 0x0522fd00, + 0xf5fa1bf4, + 0xf8064321, +/* 0x07e5: ctx_mmio_exec */ + 0x41039800, + 0x0a0427f1, 0xd00624b6, - 0xe7f10020, - 0xe3f0a500, - 0x021fb941, + 0x34bd0023, +/* 0x07f4: ctx_mmio_loop */ + 0xf4ff34c4, + 0x57f10f1b, + 0x53f00200, + 0x0535fa06, +/* 0x0806: ctx_mmio_pull */ + 0x4e9803f8, + 0x814f9880, 0xb68d21f4, - 0xfcf004e0, - 0x022cf001, - 0xfd0124b6, - 0x21f405f2, - 0xfc17f18d, - 0x0213f04a, - 0xd00c27f0, - 0x21f50012, - 0x27f10207, - 0x23f047fc, - 0x0020d002, - 0xb6012cf0, - 0x12d00320, - 0x01acf000, - 0xf006a5f0, - 0x0c9800b7, - 0x010d9800, - 0xf500e7f0, - 0xf0015c21, - 0x21f508a7, - 0x21f50103, - 0x01f40207, - 0x0ca7f022, - 0xf1c921f4, - 0xb60a1017, - 0x27f00614, - 0x0012d005, -/* 0x08c0: ctx_xfer_post_save_wait */ - 0xfd0012cf, - 0x1bf40522, - 0x3202f4fa, -/* 0x08cc: ctx_xfer_post */ - 0xf502f7f0, - 0xbd062321, - 0x6421f5f4, - 0x2621f506, - 0x3221f502, + 0x12b60830, + 0xdf1bf401, +/* 0x0818: ctx_mmio_done */ + 0xd0160398, + 0x00800023, + 0x0017f140, + 0x0613f001, + 0xf80601fa, +/* 0x082f: ctx_xfer */ + 0xf100f803, + 0xb60c00f7, + 0xe7f006f4, + 0x80fed004, +/* 0x083c: ctx_xfer_idle */ + 0xf100fecf, + 0xf42000e4, + 0x11f4f91b, + 0x1102f406, +/* 0x084c: ctx_xfer_pre */ + 0xf510f7f0, + 0xf5069221, + 0xf4062b21, +/* 0x085a: ctx_xfer_pre_load */ + 0xf7f01c11, + 0x5121f502, + 0x6021f506, + 0x7221f506, 0xf5f4bd06, - 0xf4062321, - 0x01981011, - 0x0511fd40, - 0xf5070bf4, -/* 0x08f7: ctx_xfer_no_post_mmio */ - 0xf507ab21, -/* 0x08fb: ctx_xfer_done */ - 0xf8061521, + 0xf5065121, +/* 0x0873: ctx_xfer_exec */ + 0x9806b221, + 0x27f11601, + 0x24b60414, + 0x0020d006, + 0xa500e7f1, + 0xb941e3f0, + 0x21f4021f, + 0x04e0b68d, + 0xf001fcf0, + 0x24b6022c, + 0x05f2fd01, + 0xf18d21f4, + 0xf04afc17, + 0x27f00213, + 0x0012d00c, + 0x021521f5, + 0x47fc27f1, + 0xd00223f0, + 0x2cf00020, + 0x0320b601, + 0xf00012d0, + 0xa5f001ac, + 0x00b7f006, + 0x98000c98, + 0xe7f0010d, + 0x6621f500, + 0x08a7f001, + 0x010921f5, + 0x021521f5, + 0xf02201f4, + 0x21f40ca7, + 0x1017f1c9, + 0x0614b60a, + 0xd00527f0, +/* 0x08fa: ctx_xfer_post_save_wait */ + 0x12cf0012, + 0x0522fd00, + 0xf4fa1bf4, +/* 0x0906: ctx_xfer_post */ + 0xf7f03202, + 0x5121f502, + 0xf5f4bd06, + 0xf5069221, + 0xf5023421, + 0xbd066021, + 0x5121f5f4, + 0x1011f406, + 0xfd400198, + 0x0bf40511, + 0xe521f507, +/* 0x0931: ctx_xfer_no_post_mmio */ + 0x4321f507, +/* 0x0935: ctx_xfer_done */ + 0x0000f806, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, 0x00000000, }; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc.h index 973fcda48b78..eb7bc0e9576e 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnve0.fuc.h @@ -206,14 +206,14 @@ uint32_t nve0_grhub_data[] = { }; uint32_t nve0_grhub_code[] = { - 0x03090ef5, + 0x031b0ef5, /* 0x0004: queue_put */ 0x9800d898, 0x86f001d9, 0x0489b808, 0xf00c1bf4, 0x21f502f7, - 0x00f802ec, + 0x00f802fe, /* 0x001c: queue_put_next */ 0xb60798c4, 0x8dbb0384, @@ -245,7 +245,7 @@ uint32_t nve0_grhub_code[] = { 0xc800bccf, 0x1bf41fcc, 0x06a7f0fa, - 0x010321f5, + 0x010921f5, 0xf840bfcf, /* 0x008d: nv_wr32 */ 0x28b7f100, @@ -267,63 +267,66 @@ uint32_t nve0_grhub_code[] = { 0x0684b604, 0xf80080d0, /* 0x00c9: wait_donez */ - 0x3c87f100, - 0x0684b608, - 0x99f094bd, - 0x0089d000, - 0x081887f1, - 0xd00684b6, -/* 0x00e2: wait_donez_ne */ - 0x87f1008a, - 0x84b60400, - 0x0088cf06, + 0xf094bd00, + 0x07f10099, + 0x03f00f00, + 0x0009d002, + 0x07f104bd, + 0x03f00600, + 0x000ad002, +/* 0x00e6: wait_donez_ne */ + 0x87f104bd, + 0x83f00000, + 0x0088cf01, 0xf4888aff, - 0x87f1f31b, - 0x84b6085c, - 0xf094bd06, - 0x89d00099, -/* 0x0103: wait_doneo */ - 0xf100f800, - 0xb6083c87, - 0x94bd0684, - 0xd00099f0, - 0x87f10089, + 0x94bdf31b, + 0xf10099f0, + 0xf0170007, + 0x09d00203, + 0xf804bd00, +/* 0x0109: wait_doneo */ + 0xf094bd00, + 0x07f10099, + 0x03f00f00, + 0x0009d002, + 0x87f104bd, 0x84b60818, 0x008ad006, -/* 0x011c: wait_doneo_e */ +/* 0x0124: wait_doneo_e */ 0x040087f1, 0xcf0684b6, 0x8aff0088, 0xf30bf488, - 0x085c87f1, - 0xbd0684b6, - 0x0099f094, - 0xf80089d0, -/* 0x013d: mmctx_size */ -/* 0x013f: nv_mmctx_size_loop */ - 0x9894bd00, - 0x85b600e8, - 0x0180b61a, - 0xbb0284b6, - 0xe0b60098, - 0x04efb804, - 0xb9eb1bf4, - 0x00f8029f, -/* 0x015c: mmctx_xfer */ - 0x083c87f1, - 0xbd0684b6, - 0x0199f094, - 0xf10089d0, + 0x99f094bd, + 0x0007f100, + 0x0203f017, + 0xbd0009d0, +/* 0x0147: mmctx_size */ + 0xbd00f804, +/* 0x0149: nv_mmctx_size_loop */ + 0x00e89894, + 0xb61a85b6, + 0x84b60180, + 0x0098bb02, + 0xb804e0b6, + 0x1bf404ef, + 0x029fb9eb, +/* 0x0166: mmctx_xfer */ + 0x94bd00f8, + 0xf10199f0, + 0xf00f0007, + 0x09d00203, + 0xf104bd00, 0xb6071087, 0x94bd0684, 0xf405bbfd, 0x8bd0090b, 0x0099f000, -/* 0x0180: mmctx_base_disabled */ +/* 0x018c: mmctx_base_disabled */ 0xf405eefd, 0x8ed00c0b, 0xc08fd080, -/* 0x018f: mmctx_multi_disabled */ +/* 0x019b: mmctx_multi_disabled */ 0xb70199f0, 0xc8010080, 0xb4b600ab, @@ -331,8 +334,8 @@ uint32_t nve0_grhub_code[] = { 0xb601aec8, 0xbefd11e4, 0x008bd005, -/* 0x01a8: mmctx_exec_loop */ -/* 0x01a8: mmctx_wait_free */ +/* 0x01b4: mmctx_exec_loop */ +/* 0x01b4: mmctx_wait_free */ 0xf0008ecf, 0x0bf41fe4, 0x00ce98fa, @@ -341,76 +344,77 @@ uint32_t nve0_grhub_code[] = { 0x04cdb804, 0xc8e81bf4, 0x1bf402ab, -/* 0x01c9: mmctx_fini_wait */ +/* 0x01d5: mmctx_fini_wait */ 0x008bcf18, 0xb01fb4f0, 0x1bf410b4, 0x02a7f0f7, 0xf4c921f4, -/* 0x01de: mmctx_stop */ +/* 0x01ea: mmctx_stop */ 0xabc81b0e, 0x10b4b600, 0xf00cb9f0, 0x8bd012b9, -/* 0x01ed: mmctx_stop_wait */ +/* 0x01f9: mmctx_stop_wait */ 0x008bcf00, 0xf412bbc8, -/* 0x01f6: mmctx_done */ - 0x87f1fa1b, - 0x84b6085c, - 0xf094bd06, - 0x89d00199, -/* 0x0207: strand_wait */ - 0xf900f800, - 0x02a7f0a0, - 0xfcc921f4, -/* 0x0213: strand_pre */ - 0xf100f8a0, - 0xf04afc87, - 0x97f00283, - 0x0089d00c, - 0x020721f5, -/* 0x0226: strand_post */ - 0x87f100f8, - 0x83f04afc, - 0x0d97f002, - 0xf50089d0, - 0xf8020721, -/* 0x0239: strand_set */ - 0xfca7f100, - 0x02a3f04f, - 0x0500aba2, - 0xd00fc7f0, - 0xc7f000ac, - 0x00bcd00b, - 0x020721f5, - 0xf000aed0, - 0xbcd00ac7, - 0x0721f500, -/* 0x0263: strand_ctx_init */ - 0xf100f802, - 0xb6083c87, - 0x94bd0684, - 0xd00399f0, +/* 0x0202: mmctx_done */ + 0x94bdfa1b, + 0xf10199f0, + 0xf0170007, + 0x09d00203, + 0xf804bd00, +/* 0x0215: strand_wait */ + 0xf0a0f900, + 0x21f402a7, + 0xf8a0fcc9, +/* 0x0221: strand_pre */ + 0xfc87f100, + 0x0283f04a, + 0xd00c97f0, 0x21f50089, - 0xe7f00213, - 0x3921f503, + 0x00f80215, +/* 0x0234: strand_post */ + 0x4afc87f1, + 0xf00283f0, + 0x89d00d97, + 0x1521f500, +/* 0x0247: strand_set */ + 0xf100f802, + 0xf04ffca7, + 0xaba202a3, + 0xc7f00500, + 0x00acd00f, + 0xd00bc7f0, + 0x21f500bc, + 0xaed00215, + 0x0ac7f000, + 0xf500bcd0, + 0xf8021521, +/* 0x0271: strand_ctx_init */ + 0xf094bd00, + 0x07f10399, + 0x03f00f00, + 0x0009d002, + 0x21f504bd, + 0xe7f00221, + 0x4721f503, 0xfca7f102, 0x02a3f046, 0x0400aba0, 0xf040a0d0, 0xbcd001c7, - 0x0721f500, + 0x1521f500, 0x010c9202, 0xf000acd0, 0xbcd002c7, - 0x0721f500, - 0x2621f502, + 0x1521f500, + 0x3421f502, 0x8087f102, 0x0684b608, 0xb70089cf, 0x95220080, -/* 0x02ba: ctx_init_strand_loop */ +/* 0x02ca: ctx_init_strand_loop */ 0x8ed008fe, 0x408ed000, 0xb6808acf, @@ -419,258 +423,266 @@ uint32_t nve0_grhub_code[] = { 0xb60480b6, 0x1bf40192, 0x08e4b6e8, - 0xf1f2efbc, - 0xb6085c87, - 0x94bd0684, - 0xd00399f0, - 0x00f80089, -/* 0x02ec: error */ - 0xe7f1e0f9, - 0xe4b60814, - 0x00efd006, - 0x0c1ce7f1, - 0xf006e4b6, - 0xefd001f7, - 0xf8e0fc00, -/* 0x0309: init */ - 0xfe04bd00, - 0x07fe0004, - 0x0017f100, - 0x0227f012, - 0xf10012d0, - 0xfe058517, - 0x17f10010, - 0x10d00400, - 0x0437f1c0, - 0x0634b604, - 0x200327f1, - 0xf10032d0, - 0xd0200427, - 0x27f10132, - 0x32d0200b, - 0x0c27f102, - 0x0732d020, - 0x0c2427f1, - 0xb90624b6, - 0x23d00003, + 0xbdf2efbc, + 0x0399f094, + 0x170007f1, + 0xd00203f0, + 0x04bd0009, +/* 0x02fe: error */ + 0x07f100f8, + 0x03f00500, + 0x000fd002, + 0xf7f004bd, + 0x0007f101, + 0x0303f007, + 0xbd000fd0, +/* 0x031b: init */ + 0xbd00f804, + 0x0004fe04, + 0xf10007fe, + 0xf0120017, + 0x12d00227, + 0xb117f100, + 0x0010fe05, + 0x040017f1, + 0xf1c010d0, + 0xb6040437, + 0x27f10634, + 0x32d02003, 0x0427f100, - 0x0023f087, - 0xb70012d0, - 0xf0010012, - 0x12d00427, - 0x1031f400, - 0x9604e7f1, - 0xf440e3f0, - 0xf1c76821, - 0x03018090, - 0x801ff4f0, - 0x17f0020f, - 0x041fbb01, - 0xf10112b6, - 0xb6040c27, - 0x21d00624, - 0x4021d000, - 0x010017f1, - 0x98000e98, - 0x21f5010f, - 0x37f1013d, - 0x34b60700, - 0x08149506, - 0xd00034d0, - 0x30b74034, - 0x1fbb1300, - 0x02f5b600, - 0xb6003fd0, - 0x10b60815, - 0x0814b601, - 0xf5021fb9, - 0xbb026321, - 0x0398001f, - 0x0047f102, - 0x5043f020, -/* 0x03e4: init_gpc */ - 0x08044ea0, - 0xf4021fb9, - 0x4ea08d21, - 0xf4bd010c, - 0xa08d21f4, - 0xf401044e, - 0x4ea08d21, - 0xf7f00100, + 0x0132d020, + 0x200b27f1, + 0xf10232d0, + 0xd0200c27, + 0x27f10732, + 0x24b60c24, + 0x0003b906, + 0xf10023d0, + 0xf0870427, + 0x12d00023, + 0x0012b700, + 0x0427f001, + 0xf40012d0, + 0xe7f11031, + 0xe3f09604, + 0x6821f440, + 0x8090f1c7, + 0xf4f00301, + 0x020f801f, + 0xbb0117f0, + 0x12b6041f, + 0x0c27f101, + 0x0624b604, + 0xd00021d0, + 0x17f14021, + 0x0e980100, + 0x010f9800, + 0x014721f5, + 0x070037f1, + 0x950634b6, + 0x34d00814, + 0x4034d000, + 0x130030b7, + 0xb6001fbb, + 0x3fd002f5, + 0x0815b600, + 0xb60110b6, + 0x1fb90814, + 0x7121f502, + 0x001fbb02, + 0xf1020398, + 0xf0200047, +/* 0x03f6: init_gpc */ + 0x4ea05043, + 0x1fb90804, 0x8d21f402, - 0x08004ea0, -/* 0x040c: init_gpc_wait */ - 0xc86821f4, - 0x0bf41fff, - 0x044ea0fa, - 0x6821f408, - 0xb7001fbb, - 0xb6800040, - 0x1bf40132, - 0x0027f1be, - 0x0624b608, - 0xb74021d0, - 0xbd080020, + 0x010c4ea0, + 0x21f4f4bd, + 0x044ea08d, + 0x8d21f401, + 0x01004ea0, + 0xf402f7f0, + 0x4ea08d21, +/* 0x041e: init_gpc_wait */ + 0x21f40800, + 0x1fffc868, + 0xa0fa0bf4, + 0xf408044e, + 0x1fbb6821, + 0x0040b700, + 0x0132b680, + 0xf1be1bf4, + 0xf0010007, + 0x01d00203, + 0xbd04bd00, 0x1f19f014, -/* 0x043f: main */ - 0xf40021d0, - 0x28f40031, - 0x10d7f000, - 0xf43921f4, - 0xe4b1f401, - 0x1bf54001, - 0x87f100d1, - 0x84b6083c, - 0xf094bd06, - 0x89d00499, - 0x0017f100, - 0x0614b60b, - 0xcf4012cf, - 0x13c80011, - 0x7e0bf41f, + 0x080007f1, + 0xd00203f0, + 0x04bd0001, +/* 0x0458: main */ + 0xf40031f4, + 0xd7f00028, + 0x3921f410, + 0xb1f401f4, + 0xf54001e4, + 0xbd00de1b, + 0x0499f094, + 0x0f0007f1, + 0xd00203f0, + 0x04bd0009, + 0x0b0017f1, + 0xcf0614b6, + 0x11cf4012, + 0x1f13c800, + 0x00870bf5, 0xf41f23c8, - 0x20f95a0b, - 0xf10212b9, - 0xb6083c87, - 0x94bd0684, - 0xd00799f0, - 0x32f40089, - 0x0231f401, - 0x07c721f5, - 0x085c87f1, - 0xbd0684b6, + 0x20f9620b, + 0xbd0212b9, 0x0799f094, - 0xfc0089d0, - 0x3c87f120, - 0x0684b608, - 0x99f094bd, - 0x0089d006, - 0xf50131f4, - 0xf107c721, - 0xb6085c87, - 0x94bd0684, - 0xd00699f0, - 0x0ef40089, -/* 0x04d5: chsw_prev_no_next */ + 0x0f0007f1, + 0xd00203f0, + 0x04bd0009, + 0xf40132f4, + 0x21f50231, + 0x94bd0801, + 0xf10799f0, + 0xf0170007, + 0x09d00203, + 0xfc04bd00, + 0xf094bd20, + 0x07f10699, + 0x03f00f00, + 0x0009d002, + 0x31f404bd, + 0x0121f501, + 0xf094bd08, + 0x07f10699, + 0x03f01700, + 0x0009d002, + 0x0ef404bd, +/* 0x04f9: chsw_prev_no_next */ 0xb920f931, 0x32f40212, 0x0232f401, - 0x07c721f5, + 0x080121f5, 0x17f120fc, 0x14b60b00, 0x0012d006, -/* 0x04f3: chsw_no_prev */ +/* 0x0517: chsw_no_prev */ 0xc8130ef4, 0x0bf41f23, 0x0131f40d, 0xf50232f4, -/* 0x0503: chsw_done */ - 0xf107c721, +/* 0x0527: chsw_done */ + 0xf1080121, 0xb60b0c17, 0x27f00614, 0x0012d001, - 0x085c87f1, - 0xbd0684b6, - 0x0499f094, - 0xf50089d0, -/* 0x0523: main_not_ctx_switch */ - 0xb0ff200e, - 0x1bf401e4, - 0x02f2b90d, - 0x075b21f5, -/* 0x0533: main_not_ctx_chan */ - 0xb0420ef4, - 0x1bf402e4, - 0x3c87f12e, - 0x0684b608, 0x99f094bd, - 0x0089d007, + 0x0007f104, + 0x0203f017, + 0xbd0009d0, + 0x130ef504, +/* 0x0549: main_not_ctx_switch */ + 0x01e4b0ff, + 0xb90d1bf4, + 0x21f502f2, + 0x0ef40795, +/* 0x0559: main_not_ctx_chan */ + 0x02e4b046, + 0xbd321bf4, + 0x0799f094, + 0x0f0007f1, + 0xd00203f0, + 0x04bd0009, 0xf40132f4, 0x21f50232, - 0x87f107c7, - 0x84b6085c, - 0xf094bd06, - 0x89d00799, - 0x110ef400, -/* 0x0564: main_not_ctx_save */ - 0xf010ef94, - 0x21f501f5, - 0x0ef502ec, -/* 0x0572: main_done */ - 0x17f1fed1, - 0x14b60820, - 0xf024bd06, - 0x12d01f29, - 0xbe0ef500, -/* 0x0585: ih */ + 0x94bd0801, + 0xf10799f0, + 0xf0170007, + 0x09d00203, + 0xf404bd00, +/* 0x058e: main_not_ctx_save */ + 0xef94110e, + 0x01f5f010, + 0x02fe21f5, + 0xfec00ef5, +/* 0x059c: main_done */ + 0x29f024bd, + 0x0007f11f, + 0x0203f008, + 0xbd0002d0, + 0xab0ef504, +/* 0x05b1: ih */ 0xfe80f9fe, 0x80f90188, 0xa0f990f9, 0xd0f9b0f9, 0xf0f9e0f9, - 0xc4800acf, - 0x0bf404ab, - 0x00b7f11d, - 0x10d7f019, - 0xcf40becf, - 0x21f400bf, - 0x00b0b704, - 0x01e7f004, -/* 0x05bb: ih_no_fifo */ - 0xe400bed0, - 0xf40100ab, - 0xd7f00d0b, - 0x01e7f110, - 0x0421f440, -/* 0x05cc: ih_no_ctxsw */ - 0x0104b7f1, - 0xabffb0bd, - 0x0d0bf4b4, - 0x0c1ca7f1, - 0xd006a4b6, -/* 0x05e2: ih_no_other */ - 0x0ad000ab, - 0xfcf0fc40, - 0xfcd0fce0, - 0xfca0fcb0, - 0xfe80fc90, - 0x80fc0088, - 0xf80032f4, -/* 0x05fd: ctx_4170s */ - 0x70e7f101, - 0x40e3f041, - 0xf410f5f0, - 0x00f88d21, -/* 0x060c: ctx_4170w */ - 0x4170e7f1, - 0xf440e3f0, - 0xf4f06821, - 0xf31bf410, -/* 0x061e: ctx_redswitch */ + 0x0acf04bd, + 0x04abc480, + 0xf11d0bf4, + 0xf01900b7, + 0xbecf10d7, + 0x00bfcf40, + 0xb70421f4, + 0xf00400b0, + 0xbed001e7, +/* 0x05e9: ih_no_fifo */ + 0x00abe400, + 0x0d0bf401, + 0xf110d7f0, + 0xf44001e7, +/* 0x05fa: ih_no_ctxsw */ + 0xb7f10421, + 0xb0bd0104, + 0xf4b4abff, + 0xa7f10d0b, + 0xa4b60c1c, + 0x00abd006, +/* 0x0610: ih_no_other */ + 0xfc400ad0, + 0xfce0fcf0, + 0xfcb0fcd0, + 0xfc90fca0, + 0x0088fe80, + 0x32f480fc, +/* 0x062b: ctx_4170s */ + 0xf101f800, + 0xf04170e7, + 0xf5f040e3, + 0x8d21f410, +/* 0x063a: ctx_4170w */ 0xe7f100f8, - 0xe4b60614, - 0x70f7f106, - 0x00efd002, -/* 0x062f: ctx_redswitch_delay */ - 0xb608f7f0, - 0x1bf401f2, - 0x70f7f1fd, - 0x00efd007, -/* 0x063e: ctx_86c */ - 0xe7f100f8, - 0xe4b6086c, - 0x00efd006, - 0x8a14e7f1, - 0xf440e3f0, - 0xe7f18d21, - 0xe3f0a86c, - 0x8d21f441, -/* 0x065e: ctx_load */ - 0x87f100f8, - 0x84b6083c, - 0xf094bd06, - 0x89d00599, - 0x0ca7f000, + 0xe3f04170, + 0x6821f440, + 0xf410f4f0, + 0x00f8f31b, +/* 0x064c: ctx_redswitch */ + 0x0614e7f1, + 0xf106e4b6, + 0xd00270f7, + 0xf7f000ef, +/* 0x065d: ctx_redswitch_delay */ + 0x01f2b608, + 0xf1fd1bf4, + 0xd00770f7, + 0x00f800ef, +/* 0x066c: ctx_86c */ + 0x086ce7f1, + 0xd006e4b6, + 0xe7f100ef, + 0xe3f08a14, + 0x8d21f440, + 0xa86ce7f1, + 0xf441e3f0, + 0x00f88d21, +/* 0x068c: ctx_load */ + 0x99f094bd, + 0x0007f105, + 0x0203f00f, + 0xbd0009d0, + 0x0ca7f004, 0xf1c921f4, 0xb60a2417, 0x10d00614, @@ -680,163 +692,215 @@ uint32_t nve0_grhub_code[] = { 0xb60a0c17, 0x47f00614, 0x0012d007, -/* 0x0697: ctx_chan_wait_0 */ +/* 0x06c7: ctx_chan_wait_0 */ 0xcf4014d0, 0x44f04014, 0xfa1bf41f, 0xfe0032d0, 0x2af0000b, 0x0424b61f, - 0xf10220b6, - 0xb6083c87, - 0x94bd0684, - 0xd00899f0, - 0x17f10089, - 0x14b60a04, - 0x0012d006, - 0x0a2017f1, - 0xf00614b6, - 0x23f10227, - 0x12d08000, - 0x1017f000, - 0x020027f1, - 0xfa0223f0, - 0x03f80512, - 0x085c87f1, - 0xbd0684b6, + 0xbd0220b6, 0x0899f094, - 0x980089d0, + 0x0f0007f1, + 0xd00203f0, + 0x04bd0009, + 0x0a0417f1, + 0xd00614b6, + 0x17f10012, + 0x14b60a20, + 0x0227f006, + 0x800023f1, + 0xf00012d0, + 0x27f11017, + 0x23f00200, + 0x0512fa02, + 0x94bd03f8, + 0xf10899f0, + 0xf0170007, + 0x09d00203, + 0x9804bd00, 0x14b68101, 0x80029818, 0xfd0825b6, 0x01800512, - 0x3c87f116, - 0x0684b608, - 0x99f094bd, - 0x0089d009, - 0x0a0427f1, - 0xd00624b6, - 0x27f00021, - 0x2017f101, - 0x0614b60a, - 0xf10012d0, + 0xf094bd16, + 0x07f10999, + 0x03f00f00, + 0x0009d002, + 0x27f104bd, + 0x24b60a04, + 0x0021d006, + 0xf10127f0, + 0xb60a2017, + 0x12d00614, + 0x0017f100, + 0x0613f001, + 0xf80501fa, + 0xf094bd03, + 0x07f10999, + 0x03f01700, + 0x0009d002, + 0x94bd04bd, + 0xf10599f0, + 0xf0170007, + 0x09d00203, + 0xf804bd00, +/* 0x0795: ctx_chan */ + 0x8c21f500, + 0x0ca7f006, + 0xf1c921f4, + 0xb60a1017, + 0x27f00614, + 0x0012d005, +/* 0x07ac: ctx_chan_wait */ + 0xfd0012cf, + 0x1bf40522, +/* 0x07b7: ctx_mmio_exec */ + 0x9800f8fa, + 0x27f14103, + 0x24b60a04, + 0x0023d006, +/* 0x07c6: ctx_mmio_loop */ + 0x34c434bd, + 0x0f1bf4ff, + 0x020057f1, + 0xfa0653f0, + 0x03f80535, +/* 0x07d8: ctx_mmio_pull */ + 0x98804e98, + 0x21f4814f, + 0x0830b68d, + 0xf40112b6, +/* 0x07ea: ctx_mmio_done */ + 0x0398df1b, + 0x0023d016, + 0xf1400080, 0xf0010017, 0x01fa0613, - 0xf103f805, - 0xb6085c87, - 0x94bd0684, - 0xd00999f0, - 0x87f10089, - 0x84b6085c, - 0xf094bd06, - 0x89d00599, -/* 0x075b: ctx_chan */ - 0xf500f800, - 0xf0065e21, - 0x21f40ca7, - 0x1017f1c9, - 0x0614b60a, - 0xd00527f0, -/* 0x0772: ctx_chan_wait */ - 0x12cf0012, - 0x0522fd00, - 0xf8fa1bf4, -/* 0x077d: ctx_mmio_exec */ - 0x41039800, - 0x0a0427f1, + 0xf803f806, +/* 0x0801: ctx_xfer */ + 0x00f7f100, + 0x06f4b60c, + 0xd004e7f0, +/* 0x080e: ctx_xfer_idle */ + 0xfecf80fe, + 0x00e4f100, + 0xf91bf420, + 0xf40611f4, +/* 0x081e: ctx_xfer_pre */ + 0xf7f00d02, + 0x6c21f510, + 0x1c11f406, +/* 0x0828: ctx_xfer_pre_load */ + 0xf502f7f0, + 0xf5062b21, + 0xf5063a21, + 0xbd064c21, + 0x2b21f5f4, + 0x8c21f506, +/* 0x0841: ctx_xfer_exec */ + 0x16019806, + 0x041427f1, 0xd00624b6, - 0x34bd0023, -/* 0x078c: ctx_mmio_loop */ - 0xf4ff34c4, - 0x57f10f1b, - 0x53f00200, - 0x0535fa06, -/* 0x079e: ctx_mmio_pull */ - 0x4e9803f8, - 0x814f9880, + 0xe7f10020, + 0xe3f0a500, + 0x021fb941, 0xb68d21f4, - 0x12b60830, - 0xdf1bf401, -/* 0x07b0: ctx_mmio_done */ - 0xd0160398, - 0x00800023, - 0x0017f140, - 0x0613f001, - 0xf80601fa, -/* 0x07c7: ctx_xfer */ - 0xf100f803, - 0xb60c00f7, - 0xe7f006f4, - 0x80fed004, -/* 0x07d4: ctx_xfer_idle */ - 0xf100fecf, - 0xf42000e4, - 0x11f4f91b, - 0x0d02f406, -/* 0x07e4: ctx_xfer_pre */ - 0xf510f7f0, - 0xf4063e21, -/* 0x07ee: ctx_xfer_pre_load */ - 0xf7f01c11, - 0xfd21f502, - 0x0c21f505, - 0x1e21f506, + 0xfcf004e0, + 0x022cf001, + 0xfd0124b6, + 0x21f405f2, + 0xfc17f18d, + 0x0213f04a, + 0xd00c27f0, + 0x21f50012, + 0x27f10215, + 0x23f047fc, + 0x0020d002, + 0xb6012cf0, + 0x12d00320, + 0x01acf000, + 0xf006a5f0, + 0x0c9800b7, + 0x010d9800, + 0xf500e7f0, + 0xf0016621, + 0x21f508a7, + 0x21f50109, + 0x01f40215, + 0x0ca7f022, + 0xf1c921f4, + 0xb60a1017, + 0x27f00614, + 0x0012d005, +/* 0x08c8: ctx_xfer_post_save_wait */ + 0xfd0012cf, + 0x1bf40522, + 0x2e02f4fa, +/* 0x08d4: ctx_xfer_post */ + 0xf502f7f0, + 0xbd062b21, + 0x6c21f5f4, + 0x3421f506, + 0x3a21f502, 0xf5f4bd06, - 0xf505fd21, -/* 0x0807: ctx_xfer_exec */ - 0x98065e21, - 0x27f11601, - 0x24b60414, - 0x0020d006, - 0xa500e7f1, - 0xb941e3f0, - 0x21f4021f, - 0x04e0b68d, - 0xf001fcf0, - 0x24b6022c, - 0x05f2fd01, - 0xf18d21f4, - 0xf04afc17, - 0x27f00213, - 0x0012d00c, - 0x020721f5, - 0x47fc27f1, - 0xd00223f0, - 0x2cf00020, - 0x0320b601, - 0xf00012d0, - 0xa5f001ac, - 0x00b7f006, - 0x98000c98, - 0xe7f0010d, - 0x5c21f500, - 0x08a7f001, - 0x010321f5, - 0x020721f5, - 0xf02201f4, - 0x21f40ca7, - 0x1017f1c9, - 0x0614b60a, - 0xd00527f0, -/* 0x088e: ctx_xfer_post_save_wait */ - 0x12cf0012, - 0x0522fd00, - 0xf4fa1bf4, -/* 0x089a: ctx_xfer_post */ - 0xf7f02e02, - 0xfd21f502, - 0xf5f4bd05, - 0xf5063e21, - 0xf5022621, - 0xbd060c21, - 0xfd21f5f4, - 0x1011f405, - 0xfd400198, - 0x0bf40511, - 0x7d21f507, -/* 0x08c5: ctx_xfer_no_post_mmio */ -/* 0x08c5: ctx_xfer_done */ - 0x0000f807, + 0xf4062b21, + 0x01981011, + 0x0511fd40, + 0xf5070bf4, +/* 0x08ff: ctx_xfer_no_post_mmio */ +/* 0x08ff: ctx_xfer_done */ + 0xf807b721, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvf0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvf0.fuc.h index 1b64fe55903a..438506d14749 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvf0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/hubnvf0.fuc.h @@ -206,14 +206,14 @@ uint32_t nvf0_grhub_data[] = { }; uint32_t nvf0_grhub_code[] = { - 0x03090ef5, + 0x031b0ef5, /* 0x0004: queue_put */ 0x9800d898, 0x86f001d9, 0x0489b808, 0xf00c1bf4, 0x21f502f7, - 0x00f802ec, + 0x00f802fe, /* 0x001c: queue_put_next */ 0xb60798c4, 0x8dbb0384, @@ -245,7 +245,7 @@ uint32_t nvf0_grhub_code[] = { 0xc800bccf, 0x1bf41fcc, 0x06a7f0fa, - 0x010321f5, + 0x010921f5, 0xf840bfcf, /* 0x008d: nv_wr32 */ 0x28b7f100, @@ -267,63 +267,66 @@ uint32_t nvf0_grhub_code[] = { 0x0684b604, 0xf80080d0, /* 0x00c9: wait_donez */ - 0x3c87f100, - 0x0684b608, - 0x99f094bd, - 0x0089d000, - 0x081887f1, - 0xd00684b6, -/* 0x00e2: wait_donez_ne */ - 0x87f1008a, - 0x84b60400, - 0x0088cf06, + 0xf094bd00, + 0x07f10099, + 0x03f03700, + 0x0009d002, + 0x07f104bd, + 0x03f00600, + 0x000ad002, +/* 0x00e6: wait_donez_ne */ + 0x87f104bd, + 0x83f00000, + 0x0088cf01, 0xf4888aff, - 0x87f1f31b, - 0x84b6085c, - 0xf094bd06, - 0x89d00099, -/* 0x0103: wait_doneo */ - 0xf100f800, - 0xb6083c87, - 0x94bd0684, - 0xd00099f0, - 0x87f10089, + 0x94bdf31b, + 0xf10099f0, + 0xf0170007, + 0x09d00203, + 0xf804bd00, +/* 0x0109: wait_doneo */ + 0xf094bd00, + 0x07f10099, + 0x03f03700, + 0x0009d002, + 0x87f104bd, 0x84b60818, 0x008ad006, -/* 0x011c: wait_doneo_e */ +/* 0x0124: wait_doneo_e */ 0x040087f1, 0xcf0684b6, 0x8aff0088, 0xf30bf488, - 0x085c87f1, - 0xbd0684b6, - 0x0099f094, - 0xf80089d0, -/* 0x013d: mmctx_size */ -/* 0x013f: nv_mmctx_size_loop */ - 0x9894bd00, - 0x85b600e8, - 0x0180b61a, - 0xbb0284b6, - 0xe0b60098, - 0x04efb804, - 0xb9eb1bf4, - 0x00f8029f, -/* 0x015c: mmctx_xfer */ - 0x083c87f1, - 0xbd0684b6, - 0x0199f094, - 0xf10089d0, + 0x99f094bd, + 0x0007f100, + 0x0203f017, + 0xbd0009d0, +/* 0x0147: mmctx_size */ + 0xbd00f804, +/* 0x0149: nv_mmctx_size_loop */ + 0x00e89894, + 0xb61a85b6, + 0x84b60180, + 0x0098bb02, + 0xb804e0b6, + 0x1bf404ef, + 0x029fb9eb, +/* 0x0166: mmctx_xfer */ + 0x94bd00f8, + 0xf10199f0, + 0xf0370007, + 0x09d00203, + 0xf104bd00, 0xb6071087, 0x94bd0684, 0xf405bbfd, 0x8bd0090b, 0x0099f000, -/* 0x0180: mmctx_base_disabled */ +/* 0x018c: mmctx_base_disabled */ 0xf405eefd, 0x8ed00c0b, 0xc08fd080, -/* 0x018f: mmctx_multi_disabled */ +/* 0x019b: mmctx_multi_disabled */ 0xb70199f0, 0xc8010080, 0xb4b600ab, @@ -331,8 +334,8 @@ uint32_t nvf0_grhub_code[] = { 0xb601aec8, 0xbefd11e4, 0x008bd005, -/* 0x01a8: mmctx_exec_loop */ -/* 0x01a8: mmctx_wait_free */ +/* 0x01b4: mmctx_exec_loop */ +/* 0x01b4: mmctx_wait_free */ 0xf0008ecf, 0x0bf41fe4, 0x00ce98fa, @@ -341,76 +344,77 @@ uint32_t nvf0_grhub_code[] = { 0x04cdb804, 0xc8e81bf4, 0x1bf402ab, -/* 0x01c9: mmctx_fini_wait */ +/* 0x01d5: mmctx_fini_wait */ 0x008bcf18, 0xb01fb4f0, 0x1bf410b4, 0x02a7f0f7, 0xf4c921f4, -/* 0x01de: mmctx_stop */ +/* 0x01ea: mmctx_stop */ 0xabc81b0e, 0x10b4b600, 0xf00cb9f0, 0x8bd012b9, -/* 0x01ed: mmctx_stop_wait */ +/* 0x01f9: mmctx_stop_wait */ 0x008bcf00, 0xf412bbc8, -/* 0x01f6: mmctx_done */ - 0x87f1fa1b, - 0x84b6085c, - 0xf094bd06, - 0x89d00199, -/* 0x0207: strand_wait */ - 0xf900f800, - 0x02a7f0a0, - 0xfcc921f4, -/* 0x0213: strand_pre */ - 0xf100f8a0, - 0xf04afc87, - 0x97f00283, - 0x0089d00c, - 0x020721f5, -/* 0x0226: strand_post */ - 0x87f100f8, - 0x83f04afc, - 0x0d97f002, - 0xf50089d0, - 0xf8020721, -/* 0x0239: strand_set */ - 0xfca7f100, - 0x02a3f04f, - 0x0500aba2, - 0xd00fc7f0, - 0xc7f000ac, - 0x00bcd00b, - 0x020721f5, - 0xf000aed0, - 0xbcd00ac7, - 0x0721f500, -/* 0x0263: strand_ctx_init */ - 0xf100f802, - 0xb6083c87, - 0x94bd0684, - 0xd00399f0, +/* 0x0202: mmctx_done */ + 0x94bdfa1b, + 0xf10199f0, + 0xf0170007, + 0x09d00203, + 0xf804bd00, +/* 0x0215: strand_wait */ + 0xf0a0f900, + 0x21f402a7, + 0xf8a0fcc9, +/* 0x0221: strand_pre */ + 0xfc87f100, + 0x0283f04a, + 0xd00c97f0, 0x21f50089, - 0xe7f00213, - 0x3921f503, + 0x00f80215, +/* 0x0234: strand_post */ + 0x4afc87f1, + 0xf00283f0, + 0x89d00d97, + 0x1521f500, +/* 0x0247: strand_set */ + 0xf100f802, + 0xf04ffca7, + 0xaba202a3, + 0xc7f00500, + 0x00acd00f, + 0xd00bc7f0, + 0x21f500bc, + 0xaed00215, + 0x0ac7f000, + 0xf500bcd0, + 0xf8021521, +/* 0x0271: strand_ctx_init */ + 0xf094bd00, + 0x07f10399, + 0x03f03700, + 0x0009d002, + 0x21f504bd, + 0xe7f00221, + 0x4721f503, 0xfca7f102, 0x02a3f046, 0x0400aba0, 0xf040a0d0, 0xbcd001c7, - 0x0721f500, + 0x1521f500, 0x010c9202, 0xf000acd0, 0xbcd002c7, - 0x0721f500, - 0x2621f502, + 0x1521f500, + 0x3421f502, 0x8087f102, 0x0684b608, 0xb70089cf, 0x95220080, -/* 0x02ba: ctx_init_strand_loop */ +/* 0x02ca: ctx_init_strand_loop */ 0x8ed008fe, 0x408ed000, 0xb6808acf, @@ -419,258 +423,266 @@ uint32_t nvf0_grhub_code[] = { 0xb60480b6, 0x1bf40192, 0x08e4b6e8, - 0xf1f2efbc, - 0xb6085c87, - 0x94bd0684, - 0xd00399f0, - 0x00f80089, -/* 0x02ec: error */ - 0xe7f1e0f9, - 0xe4b60814, - 0x00efd006, - 0x0c1ce7f1, - 0xf006e4b6, - 0xefd001f7, - 0xf8e0fc00, -/* 0x0309: init */ - 0xfe04bd00, - 0x07fe0004, - 0x0017f100, - 0x0227f012, - 0xf10012d0, - 0xfe058517, - 0x17f10010, - 0x10d00400, - 0x0437f1c0, - 0x0634b604, - 0x200327f1, - 0xf10032d0, - 0xd0200427, - 0x27f10132, - 0x32d0200b, - 0x0c27f102, - 0x0732d020, - 0x0c2427f1, - 0xb90624b6, - 0x23d00003, + 0xbdf2efbc, + 0x0399f094, + 0x170007f1, + 0xd00203f0, + 0x04bd0009, +/* 0x02fe: error */ + 0x07f100f8, + 0x03f00500, + 0x000fd002, + 0xf7f004bd, + 0x0007f101, + 0x0303f007, + 0xbd000fd0, +/* 0x031b: init */ + 0xbd00f804, + 0x0004fe04, + 0xf10007fe, + 0xf0120017, + 0x12d00227, + 0xb117f100, + 0x0010fe05, + 0x040017f1, + 0xf1c010d0, + 0xb6040437, + 0x27f10634, + 0x32d02003, 0x0427f100, - 0x0023f087, - 0xb70012d0, - 0xf0010012, - 0x12d00427, - 0x1031f400, - 0x9604e7f1, - 0xf440e3f0, - 0xf1c76821, - 0x03018090, - 0x801ff4f0, - 0x17f0020f, - 0x041fbb01, - 0xf10112b6, - 0xb6040c27, - 0x21d00624, - 0x4021d000, - 0x010017f1, - 0x98000e98, - 0x21f5010f, - 0x37f1013d, - 0x34b60700, - 0x08149506, - 0xd00034d0, - 0x30b74034, - 0x1fbb1300, - 0x02f5b600, - 0xb6003fd0, - 0x10b60815, - 0x0814b601, - 0xf5021fb9, - 0xbb026321, - 0x0398001f, - 0x0047f102, - 0x5043f020, -/* 0x03e4: init_gpc */ - 0x08044ea0, - 0xf4021fb9, - 0x4ea08d21, - 0xf4bd010c, - 0xa08d21f4, - 0xf401044e, - 0x4ea08d21, - 0xf7f00100, + 0x0132d020, + 0x200b27f1, + 0xf10232d0, + 0xd0200c27, + 0x27f10732, + 0x24b60c24, + 0x0003b906, + 0xf10023d0, + 0xf0870427, + 0x12d00023, + 0x0012b700, + 0x0427f001, + 0xf40012d0, + 0xe7f11031, + 0xe3f09604, + 0x6821f440, + 0x8090f1c7, + 0xf4f00301, + 0x020f801f, + 0xbb0117f0, + 0x12b6041f, + 0x0c27f101, + 0x0624b604, + 0xd00021d0, + 0x17f14021, + 0x0e980100, + 0x010f9800, + 0x014721f5, + 0x070037f1, + 0x950634b6, + 0x34d00814, + 0x4034d000, + 0x130030b7, + 0xb6001fbb, + 0x3fd002f5, + 0x0815b600, + 0xb60110b6, + 0x1fb90814, + 0x7121f502, + 0x001fbb02, + 0xf1020398, + 0xf0200047, +/* 0x03f6: init_gpc */ + 0x4ea05043, + 0x1fb90804, 0x8d21f402, - 0x08004ea0, -/* 0x040c: init_gpc_wait */ - 0xc86821f4, - 0x0bf41fff, - 0x044ea0fa, - 0x6821f408, - 0xb7001fbb, - 0xb6800040, - 0x1bf40132, - 0x0027f1be, - 0x0624b608, - 0xb74021d0, - 0xbd080020, + 0x010c4ea0, + 0x21f4f4bd, + 0x044ea08d, + 0x8d21f401, + 0x01004ea0, + 0xf402f7f0, + 0x4ea08d21, +/* 0x041e: init_gpc_wait */ + 0x21f40800, + 0x1fffc868, + 0xa0fa0bf4, + 0xf408044e, + 0x1fbb6821, + 0x0040b700, + 0x0132b680, + 0xf1be1bf4, + 0xf0010007, + 0x01d00203, + 0xbd04bd00, 0x1f19f014, -/* 0x043f: main */ - 0xf40021d0, - 0x28f40031, - 0x10d7f000, - 0xf43921f4, - 0xe4b1f401, - 0x1bf54001, - 0x87f100d1, - 0x84b6083c, - 0xf094bd06, - 0x89d00499, - 0x0017f100, - 0x0614b60b, - 0xcf4012cf, - 0x13c80011, - 0x7e0bf41f, + 0x300007f1, + 0xd00203f0, + 0x04bd0001, +/* 0x0458: main */ + 0xf40031f4, + 0xd7f00028, + 0x3921f410, + 0xb1f401f4, + 0xf54001e4, + 0xbd00de1b, + 0x0499f094, + 0x370007f1, + 0xd00203f0, + 0x04bd0009, + 0x0b0017f1, + 0xcf0614b6, + 0x11cf4012, + 0x1f13c800, + 0x00870bf5, 0xf41f23c8, - 0x20f95a0b, - 0xf10212b9, - 0xb6083c87, - 0x94bd0684, - 0xd00799f0, - 0x32f40089, - 0x0231f401, - 0x07c721f5, - 0x085c87f1, - 0xbd0684b6, + 0x20f9620b, + 0xbd0212b9, 0x0799f094, - 0xfc0089d0, - 0x3c87f120, - 0x0684b608, - 0x99f094bd, - 0x0089d006, - 0xf50131f4, - 0xf107c721, - 0xb6085c87, - 0x94bd0684, - 0xd00699f0, - 0x0ef40089, -/* 0x04d5: chsw_prev_no_next */ + 0x370007f1, + 0xd00203f0, + 0x04bd0009, + 0xf40132f4, + 0x21f50231, + 0x94bd0801, + 0xf10799f0, + 0xf0170007, + 0x09d00203, + 0xfc04bd00, + 0xf094bd20, + 0x07f10699, + 0x03f03700, + 0x0009d002, + 0x31f404bd, + 0x0121f501, + 0xf094bd08, + 0x07f10699, + 0x03f01700, + 0x0009d002, + 0x0ef404bd, +/* 0x04f9: chsw_prev_no_next */ 0xb920f931, 0x32f40212, 0x0232f401, - 0x07c721f5, + 0x080121f5, 0x17f120fc, 0x14b60b00, 0x0012d006, -/* 0x04f3: chsw_no_prev */ +/* 0x0517: chsw_no_prev */ 0xc8130ef4, 0x0bf41f23, 0x0131f40d, 0xf50232f4, -/* 0x0503: chsw_done */ - 0xf107c721, +/* 0x0527: chsw_done */ + 0xf1080121, 0xb60b0c17, 0x27f00614, 0x0012d001, - 0x085c87f1, - 0xbd0684b6, - 0x0499f094, - 0xf50089d0, -/* 0x0523: main_not_ctx_switch */ - 0xb0ff200e, - 0x1bf401e4, - 0x02f2b90d, - 0x075b21f5, -/* 0x0533: main_not_ctx_chan */ - 0xb0420ef4, - 0x1bf402e4, - 0x3c87f12e, - 0x0684b608, 0x99f094bd, - 0x0089d007, + 0x0007f104, + 0x0203f017, + 0xbd0009d0, + 0x130ef504, +/* 0x0549: main_not_ctx_switch */ + 0x01e4b0ff, + 0xb90d1bf4, + 0x21f502f2, + 0x0ef40795, +/* 0x0559: main_not_ctx_chan */ + 0x02e4b046, + 0xbd321bf4, + 0x0799f094, + 0x370007f1, + 0xd00203f0, + 0x04bd0009, 0xf40132f4, 0x21f50232, - 0x87f107c7, - 0x84b6085c, - 0xf094bd06, - 0x89d00799, - 0x110ef400, -/* 0x0564: main_not_ctx_save */ - 0xf010ef94, - 0x21f501f5, - 0x0ef502ec, -/* 0x0572: main_done */ - 0x17f1fed1, - 0x14b60820, - 0xf024bd06, - 0x12d01f29, - 0xbe0ef500, -/* 0x0585: ih */ + 0x94bd0801, + 0xf10799f0, + 0xf0170007, + 0x09d00203, + 0xf404bd00, +/* 0x058e: main_not_ctx_save */ + 0xef94110e, + 0x01f5f010, + 0x02fe21f5, + 0xfec00ef5, +/* 0x059c: main_done */ + 0x29f024bd, + 0x0007f11f, + 0x0203f030, + 0xbd0002d0, + 0xab0ef504, +/* 0x05b1: ih */ 0xfe80f9fe, 0x80f90188, 0xa0f990f9, 0xd0f9b0f9, 0xf0f9e0f9, - 0xc4800acf, - 0x0bf404ab, - 0x00b7f11d, - 0x10d7f019, - 0xcf40becf, - 0x21f400bf, - 0x00b0b704, - 0x01e7f004, -/* 0x05bb: ih_no_fifo */ - 0xe400bed0, - 0xf40100ab, - 0xd7f00d0b, - 0x01e7f110, - 0x0421f440, -/* 0x05cc: ih_no_ctxsw */ - 0x0104b7f1, - 0xabffb0bd, - 0x0d0bf4b4, - 0x0c1ca7f1, - 0xd006a4b6, -/* 0x05e2: ih_no_other */ - 0x0ad000ab, - 0xfcf0fc40, - 0xfcd0fce0, - 0xfca0fcb0, - 0xfe80fc90, - 0x80fc0088, - 0xf80032f4, -/* 0x05fd: ctx_4170s */ - 0x70e7f101, - 0x40e3f041, - 0xf410f5f0, - 0x00f88d21, -/* 0x060c: ctx_4170w */ - 0x4170e7f1, - 0xf440e3f0, - 0xf4f06821, - 0xf31bf410, -/* 0x061e: ctx_redswitch */ + 0x0acf04bd, + 0x04abc480, + 0xf11d0bf4, + 0xf01900b7, + 0xbecf10d7, + 0x00bfcf40, + 0xb70421f4, + 0xf00400b0, + 0xbed001e7, +/* 0x05e9: ih_no_fifo */ + 0x00abe400, + 0x0d0bf401, + 0xf110d7f0, + 0xf44001e7, +/* 0x05fa: ih_no_ctxsw */ + 0xb7f10421, + 0xb0bd0104, + 0xf4b4abff, + 0xa7f10d0b, + 0xa4b60c1c, + 0x00abd006, +/* 0x0610: ih_no_other */ + 0xfc400ad0, + 0xfce0fcf0, + 0xfcb0fcd0, + 0xfc90fca0, + 0x0088fe80, + 0x32f480fc, +/* 0x062b: ctx_4170s */ + 0xf101f800, + 0xf04170e7, + 0xf5f040e3, + 0x8d21f410, +/* 0x063a: ctx_4170w */ 0xe7f100f8, - 0xe4b60614, - 0x70f7f106, - 0x00efd002, -/* 0x062f: ctx_redswitch_delay */ - 0xb608f7f0, - 0x1bf401f2, - 0x70f7f1fd, - 0x00efd007, -/* 0x063e: ctx_86c */ - 0xe7f100f8, - 0xe4b6086c, - 0x00efd006, - 0x8a14e7f1, - 0xf440e3f0, - 0xe7f18d21, - 0xe3f0a86c, - 0x8d21f441, -/* 0x065e: ctx_load */ - 0x87f100f8, - 0x84b6083c, - 0xf094bd06, - 0x89d00599, - 0x0ca7f000, + 0xe3f04170, + 0x6821f440, + 0xf410f4f0, + 0x00f8f31b, +/* 0x064c: ctx_redswitch */ + 0x0614e7f1, + 0xf106e4b6, + 0xd00270f7, + 0xf7f000ef, +/* 0x065d: ctx_redswitch_delay */ + 0x01f2b608, + 0xf1fd1bf4, + 0xd00770f7, + 0x00f800ef, +/* 0x066c: ctx_86c */ + 0x086ce7f1, + 0xd006e4b6, + 0xe7f100ef, + 0xe3f08a14, + 0x8d21f440, + 0xa86ce7f1, + 0xf441e3f0, + 0x00f88d21, +/* 0x068c: ctx_load */ + 0x99f094bd, + 0x0007f105, + 0x0203f037, + 0xbd0009d0, + 0x0ca7f004, 0xf1c921f4, 0xb60a2417, 0x10d00614, @@ -680,163 +692,215 @@ uint32_t nvf0_grhub_code[] = { 0xb60a0c17, 0x47f00614, 0x0012d007, -/* 0x0697: ctx_chan_wait_0 */ +/* 0x06c7: ctx_chan_wait_0 */ 0xcf4014d0, 0x44f04014, 0xfa1bf41f, 0xfe0032d0, 0x2af0000b, 0x0424b61f, - 0xf10220b6, - 0xb6083c87, - 0x94bd0684, - 0xd00899f0, - 0x17f10089, - 0x14b60a04, - 0x0012d006, - 0x0a2017f1, - 0xf00614b6, - 0x23f10227, - 0x12d08000, - 0x1017f000, - 0x020027f1, - 0xfa0223f0, - 0x03f80512, - 0x085c87f1, - 0xbd0684b6, + 0xbd0220b6, 0x0899f094, - 0x980089d0, + 0x370007f1, + 0xd00203f0, + 0x04bd0009, + 0x0a0417f1, + 0xd00614b6, + 0x17f10012, + 0x14b60a20, + 0x0227f006, + 0x800023f1, + 0xf00012d0, + 0x27f11017, + 0x23f00200, + 0x0512fa02, + 0x94bd03f8, + 0xf10899f0, + 0xf0170007, + 0x09d00203, + 0x9804bd00, 0x14b68101, 0x80029818, 0xfd0825b6, 0x01800512, - 0x3c87f116, - 0x0684b608, - 0x99f094bd, - 0x0089d009, - 0x0a0427f1, - 0xd00624b6, - 0x27f00021, - 0x2017f101, - 0x0614b60a, - 0xf10012d0, + 0xf094bd16, + 0x07f10999, + 0x03f03700, + 0x0009d002, + 0x27f104bd, + 0x24b60a04, + 0x0021d006, + 0xf10127f0, + 0xb60a2017, + 0x12d00614, + 0x0017f100, + 0x0613f001, + 0xf80501fa, + 0xf094bd03, + 0x07f10999, + 0x03f01700, + 0x0009d002, + 0x94bd04bd, + 0xf10599f0, + 0xf0170007, + 0x09d00203, + 0xf804bd00, +/* 0x0795: ctx_chan */ + 0x8c21f500, + 0x0ca7f006, + 0xf1c921f4, + 0xb60a1017, + 0x27f00614, + 0x0012d005, +/* 0x07ac: ctx_chan_wait */ + 0xfd0012cf, + 0x1bf40522, +/* 0x07b7: ctx_mmio_exec */ + 0x9800f8fa, + 0x27f14103, + 0x24b60a04, + 0x0023d006, +/* 0x07c6: ctx_mmio_loop */ + 0x34c434bd, + 0x0f1bf4ff, + 0x020057f1, + 0xfa0653f0, + 0x03f80535, +/* 0x07d8: ctx_mmio_pull */ + 0x98804e98, + 0x21f4814f, + 0x0830b68d, + 0xf40112b6, +/* 0x07ea: ctx_mmio_done */ + 0x0398df1b, + 0x0023d016, + 0xf1400080, 0xf0010017, 0x01fa0613, - 0xf103f805, - 0xb6085c87, - 0x94bd0684, - 0xd00999f0, - 0x87f10089, - 0x84b6085c, - 0xf094bd06, - 0x89d00599, -/* 0x075b: ctx_chan */ - 0xf500f800, - 0xf0065e21, - 0x21f40ca7, - 0x1017f1c9, - 0x0614b60a, - 0xd00527f0, -/* 0x0772: ctx_chan_wait */ - 0x12cf0012, - 0x0522fd00, - 0xf8fa1bf4, -/* 0x077d: ctx_mmio_exec */ - 0x41039800, - 0x0a0427f1, + 0xf803f806, +/* 0x0801: ctx_xfer */ + 0x00f7f100, + 0x06f4b60c, + 0xd004e7f0, +/* 0x080e: ctx_xfer_idle */ + 0xfecf80fe, + 0x00e4f100, + 0xf91bf420, + 0xf40611f4, +/* 0x081e: ctx_xfer_pre */ + 0xf7f00d02, + 0x6c21f510, + 0x1c11f406, +/* 0x0828: ctx_xfer_pre_load */ + 0xf502f7f0, + 0xf5062b21, + 0xf5063a21, + 0xbd064c21, + 0x2b21f5f4, + 0x8c21f506, +/* 0x0841: ctx_xfer_exec */ + 0x16019806, + 0x041427f1, 0xd00624b6, - 0x34bd0023, -/* 0x078c: ctx_mmio_loop */ - 0xf4ff34c4, - 0x57f10f1b, - 0x53f00200, - 0x0535fa06, -/* 0x079e: ctx_mmio_pull */ - 0x4e9803f8, - 0x814f9880, + 0xe7f10020, + 0xe3f0a500, + 0x021fb941, 0xb68d21f4, - 0x12b60830, - 0xdf1bf401, -/* 0x07b0: ctx_mmio_done */ - 0xd0160398, - 0x00800023, - 0x0017f140, - 0x0613f001, - 0xf80601fa, -/* 0x07c7: ctx_xfer */ - 0xf100f803, - 0xb60c00f7, - 0xe7f006f4, - 0x80fed004, -/* 0x07d4: ctx_xfer_idle */ - 0xf100fecf, - 0xf42000e4, - 0x11f4f91b, - 0x0d02f406, -/* 0x07e4: ctx_xfer_pre */ - 0xf510f7f0, - 0xf4063e21, -/* 0x07ee: ctx_xfer_pre_load */ - 0xf7f01c11, - 0xfd21f502, - 0x0c21f505, - 0x1e21f506, + 0xfcf004e0, + 0x022cf001, + 0xfd0124b6, + 0x21f405f2, + 0xfc17f18d, + 0x0213f04a, + 0xd00c27f0, + 0x21f50012, + 0x27f10215, + 0x23f047fc, + 0x0020d002, + 0xb6012cf0, + 0x12d00320, + 0x01acf000, + 0xf006a5f0, + 0x0c9800b7, + 0x010d9800, + 0xf500e7f0, + 0xf0016621, + 0x21f508a7, + 0x21f50109, + 0x01f40215, + 0x0ca7f022, + 0xf1c921f4, + 0xb60a1017, + 0x27f00614, + 0x0012d005, +/* 0x08c8: ctx_xfer_post_save_wait */ + 0xfd0012cf, + 0x1bf40522, + 0x2e02f4fa, +/* 0x08d4: ctx_xfer_post */ + 0xf502f7f0, + 0xbd062b21, + 0x6c21f5f4, + 0x3421f506, + 0x3a21f502, 0xf5f4bd06, - 0xf505fd21, -/* 0x0807: ctx_xfer_exec */ - 0x98065e21, - 0x27f11601, - 0x24b60414, - 0x0020d006, - 0xa500e7f1, - 0xb941e3f0, - 0x21f4021f, - 0x04e0b68d, - 0xf001fcf0, - 0x24b6022c, - 0x05f2fd01, - 0xf18d21f4, - 0xf04afc17, - 0x27f00213, - 0x0012d00c, - 0x020721f5, - 0x47fc27f1, - 0xd00223f0, - 0x2cf00020, - 0x0320b601, - 0xf00012d0, - 0xa5f001ac, - 0x00b7f006, - 0x98000c98, - 0xe7f0010d, - 0x5c21f500, - 0x08a7f001, - 0x010321f5, - 0x020721f5, - 0xf02201f4, - 0x21f40ca7, - 0x1017f1c9, - 0x0614b60a, - 0xd00527f0, -/* 0x088e: ctx_xfer_post_save_wait */ - 0x12cf0012, - 0x0522fd00, - 0xf4fa1bf4, -/* 0x089a: ctx_xfer_post */ - 0xf7f02e02, - 0xfd21f502, - 0xf5f4bd05, - 0xf5063e21, - 0xf5022621, - 0xbd060c21, - 0xfd21f5f4, - 0x1011f405, - 0xfd400198, - 0x0bf40511, - 0x7d21f507, -/* 0x08c5: ctx_xfer_no_post_mmio */ -/* 0x08c5: ctx_xfer_done */ - 0x0000f807, + 0xf4062b21, + 0x01981011, + 0x0511fd40, + 0xf5070bf4, +/* 0x08ff: ctx_xfer_no_post_mmio */ +/* 0x08ff: ctx_xfer_done */ + 0xf807b721, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/macros.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/macros.fuc index c74642827021..33a5a82eccbd 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/macros.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/macros.fuc @@ -29,6 +29,28 @@ #define GK100 0xe0 #define GK110 0xf0 +#define NV_PGRAPH_FECS_SIGNAL 0x409400 +#if CHIPSET < GK110 +#define NV_PGRAPH_FECS_CC_SCRATCH_VAL(n) ((n) * 4 + 0x409800) +#define NV_PGRAPH_FECS_CC_SCRATCH_SET(n) ((n) * 4 + 0x409820) +#define NV_PGRAPH_FECS_CC_SCRATCH_CLR(n) ((n) * 4 + 0x409840) +#else +#define NV_PGRAPH_FECS_CC_SCRATCH_VAL(n) ((n) * 4 + 0x409800) +#define NV_PGRAPH_FECS_CC_SCRATCH_CLR(n) ((n) * 4 + 0x409840) +#define NV_PGRAPH_FECS_CC_SCRATCH_SET(n) ((n) * 4 + 0x4098c0) +#endif +#define NV_PGRAPH_FECS_INTR_UP_SET 0x409c1c + +#if CHIPSET < GK110 +#define NV_PGRAPH_GPCX_GPCCS_CC_SCRATCH_VAL(n) ((n) * 4 + 0x41a800) +#define NV_PGRAPH_GPCX_GPCCS_CC_SCRATCH_SET(n) ((n) * 4 + 0x41a820) +#define NV_PGRAPH_GPCX_GPCCS_CC_SCRATCH_CLR(n) ((n) * 4 + 0x41a840) +#else +#define NV_PGRAPH_GPCX_GPCCS_CC_SCRATCH_VAL(n) ((n) * 4 + 0x41a800) +#define NV_PGRAPH_GPCX_GPCCS_CC_SCRATCH_CLR(n) ((n) * 4 + 0x41a840) +#define NV_PGRAPH_GPCX_GPCCS_CC_SCRATCH_SET(n) ((n) * 4 + 0x41a8c0) +#endif + #define mmctx_data(r,c) .b32 (((c - 1) << 26) | r) #define queue_init .skip 72 // (2 * 4) + ((8 * 4) * 2) @@ -43,16 +65,25 @@ #define T_LCHAN 8 #define T_LCTXH 9 +#define nv_mkmm(rv,r) /* +*/ movw rv ((r) & 0x0000fffc) /* +*/ sethi rv ((r) & 0x00ff0000) +#define nv_mkio(rv,r,i) /* +*/ nv_mkmm(rv, (((r) & 0xffc) << 6) | ((i) << 2)) + +#define nv_iord(rv,r,i) /* +*/ nv_mkio(rv,r,i) /* +*/ iord rv I[rv] +#define nv_iowr(r,i,rv) /* +*/ nv_mkio($r0,r,i) /* +*/ iowr I[$r0] rv /* +*/ clear b32 $r0 + #define trace_set(bit) /* -*/ mov $r8 0x83c /* -*/ shl b32 $r8 6 /* */ clear b32 $r9 /* */ bset $r9 bit /* -*/ iowr I[$r8 + 0x000] $r9 - +*/ nv_iowr(NV_PGRAPH_FECS_CC_SCRATCH_SET(7), 0, $r9) #define trace_clr(bit) /* -*/ mov $r8 0x85c /* -*/ shl b32 $r8 6 /* */ clear b32 $r9 /* */ bset $r9 bit /* -*/ iowr I[$r8 + 0x000] $r9 +*/ nv_iowr(NV_PGRAPH_FECS_CC_SCRATCH_CLR(7), 0, $r9) -- cgit v1.2.3 From 0bfd6f734a99ce2c7217571c45c2456ae1da63c3 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 4 Jul 2013 17:37:23 +1000 Subject: drm/nvd7/devinit: use fermi class, not tesla Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/core/engine/device/nvc0.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c index 73d0db8e1d2c..418f51f50d7a 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/nvc0.c @@ -291,7 +291,7 @@ nvc0_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_CLOCK ] = &nvc0_clock_oclass; device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; - device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv50_devinit_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = &nvc0_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nvc0_mc_oclass; device->oclass[NVDEV_SUBDEV_BUS ] = &nvc0_bus_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; -- cgit v1.2.3 From d196e16ebf8bc9489ee3dc41dc5dfd84a70cec18 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 5 Jul 2013 10:26:20 +1000 Subject: drm/nvc0-/gr: factor out yet more unknown magic into versioned functions NVC1/NVD9 are the only chipsets that should have anything different happen on them after this. We previously weren't doing these register modifications, and NVIDIA do. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c | 7 +++++++ drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc1.c | 12 ++++++++++++ drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc3.c | 1 + drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc8.c | 1 + drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd7.c | 9 ++------- drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd9.c | 1 + drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c | 20 +++++++++++++------- drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c | 1 + drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h | 4 ++++ 9 files changed, 42 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c index 118b54b1b83f..b80723e7d49f 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c @@ -900,6 +900,11 @@ nvc0_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info) } } +void +nvc0_grctx_generate_unkn(struct nvc0_graph_priv *priv) +{ +} + void nvc0_grctx_generate_tpcid(struct nvc0_graph_priv *priv) { @@ -1060,6 +1065,7 @@ nvc0_grctx_generate_main(struct nvc0_graph_priv *priv, struct nvc0_grctx *info) nv_wr32(priv, 0x404154, 0x00000000); oclass->mods(priv, info); + oclass->unkn(priv); nvc0_grctx_generate_tpcid(priv); nvc0_grctx_generate_r406028(priv); @@ -1235,6 +1241,7 @@ nvc0_grctx_oclass = &(struct nvc0_grctx_oclass) { }, .main = nvc0_grctx_generate_main, .mods = nvc0_grctx_generate_mods, + .unkn = nvc0_grctx_generate_unkn, .hub = nvc0_grctx_init_hub, .gpc = nvc0_grctx_init_gpc, .icmd = nvc0_grctx_init_icmd, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc1.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc1.c index b60dffab224f..e5be3ee7f172 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc1.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc1.c @@ -756,6 +756,17 @@ nvc1_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info) } } +void +nvc1_grctx_generate_unkn(struct nvc0_graph_priv *priv) +{ + nv_mask(priv, 0x418c6c, 0x00000001, 0x00000001); + nv_mask(priv, 0x41980c, 0x00000010, 0x00000010); + nv_mask(priv, 0x419814, 0x00000004, 0x00000004); + nv_mask(priv, 0x4064c0, 0x80000000, 0x80000000); + nv_mask(priv, 0x405800, 0x08000000, 0x08000000); + nv_mask(priv, 0x419c00, 0x00000008, 0x00000008); +} + static struct nvc0_graph_init * nvc1_grctx_init_hub[] = { nvc0_grctx_init_base, @@ -804,6 +815,7 @@ nvc1_grctx_oclass = &(struct nvc0_grctx_oclass) { }, .main = nvc0_grctx_generate_main, .mods = nvc1_grctx_generate_mods, + .unkn = nvc1_grctx_generate_unkn, .hub = nvc1_grctx_init_hub, .gpc = nvc1_grctx_init_gpc, .icmd = nvc1_grctx_init_icmd, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc3.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc3.c index 56fa54719d2e..8f237b3bd8c6 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc3.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc3.c @@ -91,6 +91,7 @@ nvc3_grctx_oclass = &(struct nvc0_grctx_oclass) { }, .main = nvc0_grctx_generate_main, .mods = nvc0_grctx_generate_mods, + .unkn = nvc0_grctx_generate_unkn, .hub = nvc0_grctx_init_hub, .gpc = nvc3_grctx_init_gpc, .icmd = nvc0_grctx_init_icmd, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc8.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc8.c index 2ba8ea81a63a..d0d4ce3c4892 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc8.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc8.c @@ -362,6 +362,7 @@ nvc8_grctx_oclass = &(struct nvc0_grctx_oclass) { }, .main = nvc0_grctx_generate_main, .mods = nvc0_grctx_generate_mods, + .unkn = nvc0_grctx_generate_unkn, .hub = nvc0_grctx_init_hub, .gpc = nvc8_grctx_init_gpc, .icmd = nvc8_grctx_init_icmd, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd7.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd7.c index 25d5676eec47..36fa4da66abb 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd7.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd7.c @@ -227,13 +227,7 @@ nvd7_grctx_generate_main(struct nvc0_graph_priv *priv, struct nvc0_grctx *info) nv_wr32(priv, 0x404154, 0x00000000); oclass->mods(priv, info); - - nv_wr32(priv, 0x418c6c, 0x1); - nv_wr32(priv, 0x41980c, 0x10); - nv_wr32(priv, 0x41be08, 0x4); - nv_wr32(priv, 0x4064c0, 0x801a0078); - nv_wr32(priv, 0x405800, 0xf8000bf); - nv_wr32(priv, 0x419c00, 0xa); + oclass->unkn(priv); nvc0_grctx_generate_tpcid(priv); nvc0_grctx_generate_r406028(priv); @@ -296,6 +290,7 @@ nvd7_grctx_oclass = &(struct nvc0_grctx_oclass) { }, .main = nvd7_grctx_generate_main, .mods = nvd7_grctx_generate_mods, + .unkn = nve4_grctx_generate_unkn, .hub = nvd7_grctx_init_hub, .gpc = nvd7_grctx_init_gpc, .icmd = nvd9_grctx_init_icmd, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd9.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd9.c index e4eb91670ef2..818a4751df46 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd9.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd9.c @@ -507,6 +507,7 @@ nvd9_grctx_oclass = &(struct nvc0_grctx_oclass) { }, .main = nvc0_grctx_generate_main, .mods = nvc1_grctx_generate_mods, + .unkn = nvc1_grctx_generate_unkn, .hub = nvd9_grctx_init_hub, .gpc = nvd9_grctx_init_gpc, .icmd = nvd9_grctx_init_icmd, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c index 261a600c8ffb..0b72d7240b0b 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c @@ -848,6 +848,17 @@ nve4_grctx_generate_mods(struct nvc0_graph_priv *priv, struct nvc0_grctx *info) mmio_list(0x17e920, 0x00090a05, 0, 0); } +void +nve4_grctx_generate_unkn(struct nvc0_graph_priv *priv) +{ + nv_mask(priv, 0x418c6c, 0x00000001, 0x00000001); + nv_mask(priv, 0x41980c, 0x00000010, 0x00000010); + nv_mask(priv, 0x41be08, 0x00000004, 0x00000004); + nv_mask(priv, 0x4064c0, 0x80000000, 0x80000000); + nv_mask(priv, 0x405800, 0x08000000, 0x08000000); + nv_mask(priv, 0x419c00, 0x00000008, 0x00000008); +} + void nve4_grctx_generate_r418bb8(struct nvc0_graph_priv *priv) { @@ -922,13 +933,7 @@ nve4_grctx_generate_main(struct nvc0_graph_priv *priv, struct nvc0_grctx *info) nv_wr32(priv, 0x404154, 0x00000000); oclass->mods(priv, info); - - nv_wr32(priv, 0x418c6c, 0x1); - nv_wr32(priv, 0x41980c, 0x10); - nv_wr32(priv, 0x41be08, 0x4); - nv_wr32(priv, 0x4064c0, 0x801a00f0); - nv_wr32(priv, 0x405800, 0xf8000bf); - nv_wr32(priv, 0x419c00, 0xa); + oclass->unkn(priv); nvc0_grctx_generate_tpcid(priv); nvc0_grctx_generate_r406028(priv); @@ -1013,6 +1018,7 @@ nve4_grctx_oclass = &(struct nvc0_grctx_oclass) { }, .main = nve4_grctx_generate_main, .mods = nve4_grctx_generate_mods, + .unkn = nve4_grctx_generate_unkn, .hub = nve4_grctx_init_hub, .gpc = nve4_grctx_init_gpc, .icmd = nve4_grctx_init_icmd, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c index c1bacc3d7374..dcb2ebb8c29d 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvf0.c @@ -320,6 +320,7 @@ nvf0_grctx_oclass = &(struct nvc0_grctx_oclass) { }, .main = nve4_grctx_generate_main, .mods = nvf0_grctx_generate_mods, + .unkn = nve4_grctx_generate_unkn, .hub = nvf0_grctx_init_hub, .gpc = nvf0_grctx_init_gpc, .icmd = nvc0_grctx_init_icmd, diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h index dd06674336b4..ea17a80ad7fc 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.h @@ -150,6 +150,7 @@ struct nvc0_grctx_oclass { void (*main)(struct nvc0_graph_priv *, struct nvc0_grctx *); /* context-specific modify-on-first-load list generation function */ void (*mods)(struct nvc0_graph_priv *, struct nvc0_grctx *); + void (*unkn)(struct nvc0_graph_priv *); /* mmio context data */ struct nvc0_graph_init **hub; struct nvc0_graph_init **gpc; @@ -207,6 +208,7 @@ extern struct nvc0_graph_init nve4_graph_init_unk88xx[]; int nvc0_grctx_generate(struct nvc0_graph_priv *); void nvc0_grctx_generate_main(struct nvc0_graph_priv *, struct nvc0_grctx *); void nvc0_grctx_generate_mods(struct nvc0_graph_priv *, struct nvc0_grctx *); +void nvc0_grctx_generate_unkn(struct nvc0_graph_priv *); void nvc0_grctx_generate_tpcid(struct nvc0_graph_priv *); void nvc0_grctx_generate_r406028(struct nvc0_graph_priv *); void nvc0_grctx_generate_r4060a8(struct nvc0_graph_priv *); @@ -238,6 +240,7 @@ extern struct nvc0_graph_init nvc0_grctx_init_90c0[]; extern struct nvc0_graph_init nvc0_grctx_init_mthd_magic[]; void nvc1_grctx_generate_mods(struct nvc0_graph_priv *, struct nvc0_grctx *); +void nvc1_grctx_generate_unkn(struct nvc0_graph_priv *); extern struct nouveau_oclass *nvc1_grctx_oclass; extern struct nvc0_graph_init nvc1_grctx_init_9097[]; @@ -254,6 +257,7 @@ extern struct nvc0_graph_init nvd9_grctx_init_rop[]; extern struct nvc0_graph_mthd nvd9_grctx_init_mthd[]; void nve4_grctx_generate_main(struct nvc0_graph_priv *, struct nvc0_grctx *); +void nve4_grctx_generate_unkn(struct nvc0_graph_priv *); extern struct nouveau_oclass *nve4_grctx_oclass; extern struct nvc0_graph_init nve4_grctx_init_unk46xx[]; extern struct nvc0_graph_init nve4_grctx_init_unk47xx[]; -- cgit v1.2.3 From 5c5ae7157d78e59e062dd35fbe9dec4de852bec2 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 5 Jul 2013 10:47:26 +1000 Subject: drm/nvc0-/gr: remove some more of the hardcoded register writes Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c | 18 ++++++------------ drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd7.c | 8 -------- drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c | 8 -------- 3 files changed, 6 insertions(+), 28 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c index b80723e7d49f..64dca260912f 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvc0.c @@ -929,11 +929,13 @@ nvc0_grctx_generate_tpcid(struct nvc0_graph_priv *priv) void nvc0_grctx_generate_r406028(struct nvc0_graph_priv *priv) { - u32 tmp = 0, i; + u32 tmp[GPC_MAX / 8] = {}, i = 0; for (i = 0; i < priv->gpc_nr; i++) - tmp |= priv->tpc_nr[i] << (i * 4); - nv_wr32(priv, 0x406028, tmp); - nv_wr32(priv, 0x405870, tmp); + tmp[i / 8] |= priv->tpc_nr[i] << ((i % 8) * 4); + for (i = 0; i < 4; i++) { + nv_wr32(priv, 0x406028 + (i * 4), tmp[i]); + nv_wr32(priv, 0x405870 + (i * 4), tmp[i]); + } } void @@ -1069,14 +1071,6 @@ nvc0_grctx_generate_main(struct nvc0_graph_priv *priv, struct nvc0_grctx *info) nvc0_grctx_generate_tpcid(priv); nvc0_grctx_generate_r406028(priv); - - nv_wr32(priv, 0x40602c, 0x00000000); - nv_wr32(priv, 0x405874, 0x00000000); - nv_wr32(priv, 0x406030, 0x00000000); - nv_wr32(priv, 0x405878, 0x00000000); - nv_wr32(priv, 0x406034, 0x00000000); - nv_wr32(priv, 0x40587c, 0x00000000); - nvc0_grctx_generate_r4060a8(priv); nvc0_grctx_generate_r418bb8(priv); nvc0_grctx_generate_r406800(priv); diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd7.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd7.c index 36fa4da66abb..438e78410808 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd7.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnvd7.c @@ -231,14 +231,6 @@ nvd7_grctx_generate_main(struct nvc0_graph_priv *priv, struct nvc0_grctx *info) nvc0_grctx_generate_tpcid(priv); nvc0_grctx_generate_r406028(priv); - - nv_wr32(priv, 0x40602c, 0x00000000); - nv_wr32(priv, 0x405874, 0x00000000); - nv_wr32(priv, 0x406030, 0x00000000); - nv_wr32(priv, 0x405878, 0x00000000); - nv_wr32(priv, 0x406034, 0x00000000); - nv_wr32(priv, 0x40587c, 0x00000000); - nvc0_grctx_generate_r4060a8(priv); nve4_grctx_generate_r418bb8(priv); nvc0_grctx_generate_r406800(priv); diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c index 0b72d7240b0b..e2de73ee5eee 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/ctxnve4.c @@ -937,14 +937,6 @@ nve4_grctx_generate_main(struct nvc0_graph_priv *priv, struct nvc0_grctx *info) nvc0_grctx_generate_tpcid(priv); nvc0_grctx_generate_r406028(priv); - - nv_wr32(priv, 0x40602c, 0x00000000); - nv_wr32(priv, 0x405874, 0x00000000); - nv_wr32(priv, 0x406030, 0x00000000); - nv_wr32(priv, 0x405878, 0x00000000); - nv_wr32(priv, 0x406034, 0x00000000); - nv_wr32(priv, 0x40587c, 0x00000000); - nve4_grctx_generate_r418bb8(priv); nvc0_grctx_generate_r406800(priv); -- cgit v1.2.3 From d005f51eb93d71cd40ebd11dd377453fa8c8a42a Mon Sep 17 00:00:00 2001 From: Marcin Slusarz Date: Tue, 11 Jun 2013 10:50:30 +0200 Subject: drm/nouveau: use vmalloc for pgt allocation Page tables on nv50 take 48kB, which can be hard to allocate in one piece. Let's use vmalloc. Signed-off-by: Marcin Slusarz Cc: stable@vger.kernel.org [3.7+] Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/core/subdev/vm/base.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/subdev/vm/base.c b/drivers/gpu/drm/nouveau/core/subdev/vm/base.c index 34d3fbfbd631..67fcb6c852ac 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/vm/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/vm/base.c @@ -365,7 +365,7 @@ nouveau_vm_create(struct nouveau_vmmgr *vmm, u64 offset, u64 length, vm->fpde = offset >> (vmm->pgt_bits + 12); vm->lpde = (offset + length - 1) >> (vmm->pgt_bits + 12); - vm->pgt = kcalloc(vm->lpde - vm->fpde + 1, sizeof(*vm->pgt), GFP_KERNEL); + vm->pgt = vzalloc((vm->lpde - vm->fpde + 1) * sizeof(*vm->pgt)); if (!vm->pgt) { kfree(vm); return -ENOMEM; @@ -374,7 +374,7 @@ nouveau_vm_create(struct nouveau_vmmgr *vmm, u64 offset, u64 length, ret = nouveau_mm_init(&vm->mm, mm_offset >> 12, mm_length >> 12, block >> 12); if (ret) { - kfree(vm->pgt); + vfree(vm->pgt); kfree(vm); return ret; } @@ -450,7 +450,7 @@ nouveau_vm_del(struct nouveau_vm *vm) } nouveau_mm_fini(&vm->mm); - kfree(vm->pgt); + vfree(vm->pgt); kfree(vm); } -- cgit v1.2.3 From 378f2bcdf7c971453d11580936dc0ffe845f5880 Mon Sep 17 00:00:00 2001 From: Emil Velikov Date: Tue, 2 Jul 2013 14:44:12 +0100 Subject: drm/nv50-/disp: Use output specific mask in interrupt The commit commit 476e84e126171d809f9c0b5d97137f5055f95ca8 Author: Ben Skeggs Date: Mon Feb 11 09:24:23 2013 +1000 drm/nv50-/disp: initial supervisor support for off-chip encoders changed the write mask in one of the interrupt functions for on-chip encoders, causing a regression in certain VGA dual-head setups. This commit reintroduces the mask thus resolving the regression Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=66129 Reported-and-Tested-by: Yves-Alexis Cc: stable@vger.kernel.org [3.9+] CC: Ben Skeggs Signed-off-by: Emil Velikov Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/core/engine/disp/nv50.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c index 8b42f45c2b1d..7ffe2f309f12 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c @@ -1107,6 +1107,7 @@ nv50_disp_intr_unk20_2(struct nv50_disp_priv *priv, int head) u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff; u32 hval, hreg = 0x614200 + (head * 0x800); u32 oval, oreg; + u32 mask; u32 conf = exec_clkcmp(priv, head, 0xff, pclk, &outp); if (conf != ~0) { if (outp.location == 0 && outp.type == DCB_OUTPUT_DP) { @@ -1133,6 +1134,7 @@ nv50_disp_intr_unk20_2(struct nv50_disp_priv *priv, int head) oreg = 0x614280 + (ffs(outp.or) - 1) * 0x800; oval = 0x00000000; hval = 0x00000000; + mask = 0xffffffff; } else if (!outp.location) { if (outp.type == DCB_OUTPUT_DP) @@ -1140,14 +1142,16 @@ nv50_disp_intr_unk20_2(struct nv50_disp_priv *priv, int head) oreg = 0x614300 + (ffs(outp.or) - 1) * 0x800; oval = (conf & 0x0100) ? 0x00000101 : 0x00000000; hval = 0x00000000; + mask = 0x00000707; } else { oreg = 0x614380 + (ffs(outp.or) - 1) * 0x800; oval = 0x00000001; hval = 0x00000001; + mask = 0x00000707; } nv_mask(priv, hreg, 0x0000000f, hval); - nv_mask(priv, oreg, 0x00000707, oval); + nv_mask(priv, oreg, mask, oval); } } -- cgit v1.2.3 From bf03d1b293cc556df53545e318110505014d805e Mon Sep 17 00:00:00 2001 From: Ilia Mirkin Date: Wed, 3 Jul 2013 03:06:02 -0400 Subject: drm/nva3/disp: Fix HDMI audio regression This is the nva3 counterpart to commit beba44b17 (drm/nv84/disp: Fix HDMI audio regression). The regression happened as a result of refactoring in commit 8e9e3d2de (drm/nv84/disp: move hdmi control into core). Reported-and-tested-by: Max Baldwin Signed-off-by: Ilia Mirkin Signed-off-by: Ben Skeggs Cc: stable@vger.kernel.org --- drivers/gpu/drm/nouveau/core/engine/disp/hdminva3.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/hdminva3.c b/drivers/gpu/drm/nouveau/core/engine/disp/hdminva3.c index f065fc248adf..db8c6fd46278 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/hdminva3.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/hdminva3.c @@ -55,6 +55,10 @@ nva3_hdmi_ctrl(struct nv50_disp_priv *priv, int head, int or, u32 data) nv_wr32(priv, 0x61c510 + soff, 0x00000000); nv_mask(priv, 0x61c500 + soff, 0x00000001, 0x00000001); + nv_mask(priv, 0x61c5d0 + soff, 0x00070001, 0x00010001); /* SPARE, HW_CTS */ + nv_mask(priv, 0x61c568 + soff, 0x00010101, 0x00000000); /* ACR_CTRL, ?? */ + nv_mask(priv, 0x61c578 + soff, 0x80000000, 0x80000000); /* ACR_0441_ENABLE */ + /* ??? */ nv_mask(priv, 0x61733c, 0x00100000, 0x00100000); /* RESETF */ nv_mask(priv, 0x61733c, 0x10000000, 0x10000000); /* LOOKUP_EN */ -- cgit v1.2.3 From 3463ff67bc8d049098559adb850299c26b52350d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 4 Jul 2013 20:05:50 +0200 Subject: drm/rcar-du: Don't ignore rcar_du_crtc_create() return value Handle error cases correctly. Signed-off-by: Laurent Pinchart Signed-off-by: Dave Airlie --- drivers/gpu/drm/rcar-du/rcar_du_kms.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index 9c63f39658de..06cacf6532c0 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -191,8 +191,11 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) if (ret < 0) return ret; - for (i = 0; i < ARRAY_SIZE(rcdu->crtcs); ++i) - rcar_du_crtc_create(rcdu, i); + for (i = 0; i < ARRAY_SIZE(rcdu->crtcs); ++i) { + ret = rcar_du_crtc_create(rcdu, i); + if (ret < 0) + return ret; + } rcdu->used_crtcs = 0; rcdu->num_crtcs = i; -- cgit v1.2.3 From 59e32642d2e8fb170a1e777906dcb13359ea230f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 4 Jul 2013 20:05:51 +0200 Subject: drm/rcar-du: Fix buffer pitch alignment The DU requires a 16 pixels pitch alignement. Make sure dumb buffers are allocated with the correct pitch, and validate the pitch when creating frame buffers. Signed-off-by: Laurent Pinchart Signed-off-by: Dave Airlie --- drivers/gpu/drm/rcar-du/rcar_du_drv.c | 2 +- drivers/gpu/drm/rcar-du/rcar_du_kms.c | 19 ++++++++++++++++++- drivers/gpu/drm/rcar-du/rcar_du_kms.h | 3 +++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index 003b34ee38e3..ff82877de876 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c @@ -251,7 +251,7 @@ static struct drm_driver rcar_du_driver = { .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import = drm_gem_cma_dmabuf_import, .gem_prime_export = drm_gem_cma_dmabuf_export, - .dumb_create = drm_gem_cma_dumb_create, + .dumb_create = rcar_du_dumb_create, .dumb_map_offset = drm_gem_cma_dumb_map_offset, .dumb_destroy = drm_gem_cma_dumb_destroy, .fops = &rcar_du_fops, diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index 06cacf6532c0..d30c2e29bee2 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -138,11 +138,25 @@ void rcar_du_encoder_mode_commit(struct drm_encoder *encoder) * Frame buffer */ +int rcar_du_dumb_create(struct drm_file *file, struct drm_device *dev, + struct drm_mode_create_dumb *args) +{ + unsigned int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8); + unsigned int align; + + /* The pitch must be aligned to a 16 pixels boundary. */ + align = 16 * args->bpp / 8; + args->pitch = roundup(max(args->pitch, min_pitch), align); + + return drm_gem_cma_dumb_create(file, dev, args); +} + static struct drm_framebuffer * rcar_du_fb_create(struct drm_device *dev, struct drm_file *file_priv, struct drm_mode_fb_cmd2 *mode_cmd) { const struct rcar_du_format_info *format; + unsigned int align; format = rcar_du_format_info(mode_cmd->pixel_format); if (format == NULL) { @@ -151,7 +165,10 @@ rcar_du_fb_create(struct drm_device *dev, struct drm_file *file_priv, return ERR_PTR(-EINVAL); } - if (mode_cmd->pitches[0] & 15 || mode_cmd->pitches[0] >= 8192) { + align = 16 * format->bpp / 8; + + if (mode_cmd->pitches[0] & (align - 1) || + mode_cmd->pitches[0] >= 8192) { dev_dbg(dev->dev, "invalid pitch value %u\n", mode_cmd->pitches[0]); return ERR_PTR(-EINVAL); diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.h b/drivers/gpu/drm/rcar-du/rcar_du_kms.h index e4d8db069a06..dba472263486 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.h @@ -56,4 +56,7 @@ void rcar_du_encoder_mode_commit(struct drm_encoder *encoder); int rcar_du_modeset_init(struct rcar_du_device *rcdu); +int rcar_du_dumb_create(struct drm_file *file, struct drm_device *dev, + struct drm_mode_create_dumb *args); + #endif /* __RCAR_DU_KMS_H__ */ -- cgit v1.2.3 From f9d8a1294d37449f3f1b842ffb275e2ca41f5cf4 Mon Sep 17 00:00:00 2001 From: Joonyoung Shim Date: Thu, 4 Jul 2013 16:19:12 +0900 Subject: drm/prime: fix sgt NULL checking The drm_gem_map_detach() can be called with sgt is NULL. Signed-off-by: Joonyoung Shim Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_prime.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index 1e0de41f085c..ff5fecef367b 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -119,12 +119,13 @@ static void drm_gem_map_detach(struct dma_buf *dma_buf, return; sgt = prime_attach->sgt; + if (sgt) { + if (prime_attach->dir != DMA_NONE) + dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents, + prime_attach->dir); + sg_free_table(sgt); + } - if (prime_attach->dir != DMA_NONE) - dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents, - prime_attach->dir); - - sg_free_table(sgt); kfree(sgt); kfree(prime_attach); attach->priv = NULL; -- cgit v1.2.3 From 7c397cd97b8f46659698396b420bd48c3e6703e6 Mon Sep 17 00:00:00 2001 From: Joonyoung Shim Date: Fri, 28 Jun 2013 14:24:53 +0900 Subject: drm: add mmap function to prime helpers This adds to call low-level mmap() from prime helpers. Signed-off-by: Joonyoung Shim Acked-by: Laurent Pinchart Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_prime.c | 8 +++++++- include/drm/drmP.h | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index ff5fecef367b..85e450e3241c 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -245,7 +245,13 @@ static void drm_gem_dmabuf_kunmap(struct dma_buf *dma_buf, static int drm_gem_dmabuf_mmap(struct dma_buf *dma_buf, struct vm_area_struct *vma) { - return -EINVAL; + struct drm_gem_object *obj = dma_buf->priv; + struct drm_device *dev = obj->dev; + + if (!dev->driver->gem_prime_mmap) + return -ENOSYS; + + return dev->driver->gem_prime_mmap(obj, vma); } static const struct dma_buf_ops drm_gem_prime_dmabuf_ops = { diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 82670ac0d774..12083dc862a9 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -937,6 +937,8 @@ struct drm_driver { struct sg_table *sgt); void *(*gem_prime_vmap)(struct drm_gem_object *obj); void (*gem_prime_vunmap)(struct drm_gem_object *obj, void *vaddr); + int (*gem_prime_mmap)(struct drm_gem_object *obj, + struct vm_area_struct *vma); /* vga arb irq handler */ void (*vgaarb_irq)(struct drm_device *dev, bool state); -- cgit v1.2.3 From 78467dc5f70fb9bee4a32c0c3714c99b0b5465c7 Mon Sep 17 00:00:00 2001 From: Joonyoung Shim Date: Fri, 28 Jun 2013 14:24:54 +0900 Subject: drm/cma: add low-level hook functions to use prime helpers Instead of using the dma_buf functionality for GEM CMA, we can use prime helpers if we can provide low-level hook functions for GEM CMA. Signed-off-by: Joonyoung Shim Acked-by: Laurent Pinchart Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_gem_cma_helper.c | 79 ++++++++++++++++++++++++++++++++++++ include/drm/drm_gem_cma_helper.h | 9 ++++ 2 files changed, 88 insertions(+) diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c index ce063970d68c..83a45e5cf04e 100644 --- a/drivers/gpu/drm/drm_gem_cma_helper.c +++ b/drivers/gpu/drm/drm_gem_cma_helper.c @@ -602,3 +602,82 @@ error_gem_free: return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(drm_gem_cma_dmabuf_import); + +/* low-level interface prime helpers */ +struct sg_table *drm_gem_cma_prime_get_sg_table(struct drm_gem_object *obj) +{ + struct drm_gem_cma_object *cma_obj = to_drm_gem_cma_obj(obj); + struct sg_table *sgt; + int ret; + + sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); + if (!sgt) + return NULL; + + ret = dma_get_sgtable(obj->dev->dev, sgt, cma_obj->vaddr, + cma_obj->paddr, obj->size); + if (ret < 0) + goto out; + + return sgt; + +out: + kfree(sgt); + return NULL; +} +EXPORT_SYMBOL_GPL(drm_gem_cma_prime_get_sg_table); + +struct drm_gem_object * +drm_gem_cma_prime_import_sg_table(struct drm_device *dev, size_t size, + struct sg_table *sgt) +{ + struct drm_gem_cma_object *cma_obj; + + if (sgt->nents != 1) + return ERR_PTR(-EINVAL); + + /* Create a CMA GEM buffer. */ + cma_obj = __drm_gem_cma_create(dev, size); + if (IS_ERR(cma_obj)) + return ERR_PTR(PTR_ERR(cma_obj)); + + cma_obj->paddr = sg_dma_address(sgt->sgl); + cma_obj->sgt = sgt; + + DRM_DEBUG_PRIME("dma_addr = 0x%x, size = %zu\n", cma_obj->paddr, size); + + return &cma_obj->base; +} +EXPORT_SYMBOL_GPL(drm_gem_cma_prime_import_sg_table); + +int drm_gem_cma_prime_mmap(struct drm_gem_object *obj, + struct vm_area_struct *vma) +{ + struct drm_gem_cma_object *cma_obj; + struct drm_device *dev = obj->dev; + int ret; + + mutex_lock(&dev->struct_mutex); + ret = drm_gem_mmap_obj(obj, obj->size, vma); + mutex_unlock(&dev->struct_mutex); + if (ret < 0) + return ret; + + cma_obj = to_drm_gem_cma_obj(obj); + return drm_gem_cma_mmap_obj(cma_obj, vma); +} +EXPORT_SYMBOL_GPL(drm_gem_cma_prime_mmap); + +void *drm_gem_cma_prime_vmap(struct drm_gem_object *obj) +{ + struct drm_gem_cma_object *cma_obj = to_drm_gem_cma_obj(obj); + + return cma_obj->vaddr; +} +EXPORT_SYMBOL_GPL(drm_gem_cma_prime_vmap); + +void drm_gem_cma_prime_vunmap(struct drm_gem_object *obj, void *vaddr) +{ + /* Nothing to do */ +} +EXPORT_SYMBOL_GPL(drm_gem_cma_prime_vunmap); diff --git a/include/drm/drm_gem_cma_helper.h b/include/drm/drm_gem_cma_helper.h index 6e17251e9b28..9d39d2a51906 100644 --- a/include/drm/drm_gem_cma_helper.h +++ b/include/drm/drm_gem_cma_helper.h @@ -54,4 +54,13 @@ struct dma_buf *drm_gem_cma_dmabuf_export(struct drm_device *drm_dev, struct drm_gem_object *drm_gem_cma_dmabuf_import(struct drm_device *drm_dev, struct dma_buf *dma_buf); +struct sg_table *drm_gem_cma_prime_get_sg_table(struct drm_gem_object *obj); +struct drm_gem_object * +drm_gem_cma_prime_import_sg_table(struct drm_device *dev, size_t size, + struct sg_table *sgt); +int drm_gem_cma_prime_mmap(struct drm_gem_object *obj, + struct vm_area_struct *vma); +void *drm_gem_cma_prime_vmap(struct drm_gem_object *obj); +void drm_gem_cma_prime_vunmap(struct drm_gem_object *obj, void *vaddr); + #endif /* __DRM_GEM_CMA_HELPER_H__ */ -- cgit v1.2.3 From 6d35dea107834eb549c1fba28fea6ec39c81d0ba Mon Sep 17 00:00:00 2001 From: Joonyoung Shim Date: Fri, 28 Jun 2013 14:24:55 +0900 Subject: drm/cma: remove GEM CMA specific dma_buf functionality We can use prime helpers instead. Signed-off-by: Joonyoung Shim Acked-by: Laurent Pinchart Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_gem_cma_helper.c | 286 ----------------------------------- include/drm/drm_gem_cma_helper.h | 6 - 2 files changed, 292 deletions(-) diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c index 83a45e5cf04e..ece72a8ac245 100644 --- a/drivers/gpu/drm/drm_gem_cma_helper.c +++ b/drivers/gpu/drm/drm_gem_cma_helper.c @@ -317,292 +317,6 @@ void drm_gem_cma_describe(struct drm_gem_cma_object *cma_obj, struct seq_file *m EXPORT_SYMBOL_GPL(drm_gem_cma_describe); #endif -/* ----------------------------------------------------------------------------- - * DMA-BUF - */ - -struct drm_gem_cma_dmabuf_attachment { - struct sg_table sgt; - enum dma_data_direction dir; -}; - -static int drm_gem_cma_dmabuf_attach(struct dma_buf *dmabuf, struct device *dev, - struct dma_buf_attachment *attach) -{ - struct drm_gem_cma_dmabuf_attachment *cma_attach; - - cma_attach = kzalloc(sizeof(*cma_attach), GFP_KERNEL); - if (!cma_attach) - return -ENOMEM; - - cma_attach->dir = DMA_NONE; - attach->priv = cma_attach; - - return 0; -} - -static void drm_gem_cma_dmabuf_detach(struct dma_buf *dmabuf, - struct dma_buf_attachment *attach) -{ - struct drm_gem_cma_dmabuf_attachment *cma_attach = attach->priv; - struct sg_table *sgt; - - if (cma_attach == NULL) - return; - - sgt = &cma_attach->sgt; - - if (cma_attach->dir != DMA_NONE) - dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents, - cma_attach->dir); - - sg_free_table(sgt); - kfree(cma_attach); - attach->priv = NULL; -} - -static struct sg_table * -drm_gem_cma_dmabuf_map(struct dma_buf_attachment *attach, - enum dma_data_direction dir) -{ - struct drm_gem_cma_dmabuf_attachment *cma_attach = attach->priv; - struct drm_gem_cma_object *cma_obj = attach->dmabuf->priv; - struct drm_device *drm = cma_obj->base.dev; - struct scatterlist *rd, *wr; - struct sg_table *sgt; - unsigned int i; - int nents, ret; - - DRM_DEBUG_PRIME("\n"); - - if (WARN_ON(dir == DMA_NONE)) - return ERR_PTR(-EINVAL); - - /* Return the cached mapping when possible. */ - if (cma_attach->dir == dir) - return &cma_attach->sgt; - - /* Two mappings with different directions for the same attachment are - * not allowed. - */ - if (WARN_ON(cma_attach->dir != DMA_NONE)) - return ERR_PTR(-EBUSY); - - sgt = &cma_attach->sgt; - - ret = sg_alloc_table(sgt, cma_obj->sgt->orig_nents, GFP_KERNEL); - if (ret) { - DRM_ERROR("failed to alloc sgt.\n"); - return ERR_PTR(-ENOMEM); - } - - mutex_lock(&drm->struct_mutex); - - rd = cma_obj->sgt->sgl; - wr = sgt->sgl; - for (i = 0; i < sgt->orig_nents; ++i) { - sg_set_page(wr, sg_page(rd), rd->length, rd->offset); - rd = sg_next(rd); - wr = sg_next(wr); - } - - nents = dma_map_sg(attach->dev, sgt->sgl, sgt->orig_nents, dir); - if (!nents) { - DRM_ERROR("failed to map sgl with iommu.\n"); - sg_free_table(sgt); - sgt = ERR_PTR(-EIO); - goto done; - } - - cma_attach->dir = dir; - attach->priv = cma_attach; - - DRM_DEBUG_PRIME("buffer size = %zu\n", cma_obj->base.size); - -done: - mutex_unlock(&drm->struct_mutex); - return sgt; -} - -static void drm_gem_cma_dmabuf_unmap(struct dma_buf_attachment *attach, - struct sg_table *sgt, - enum dma_data_direction dir) -{ - /* Nothing to do. */ -} - -static void drm_gem_cma_dmabuf_release(struct dma_buf *dmabuf) -{ - struct drm_gem_cma_object *cma_obj = dmabuf->priv; - - DRM_DEBUG_PRIME("%s\n", __FILE__); - - /* - * drm_gem_cma_dmabuf_release() call means that file object's - * f_count is 0 and it calls drm_gem_object_handle_unreference() - * to drop the references that these values had been increased - * at drm_prime_handle_to_fd() - */ - if (cma_obj->base.export_dma_buf == dmabuf) { - cma_obj->base.export_dma_buf = NULL; - - /* - * drop this gem object refcount to release allocated buffer - * and resources. - */ - drm_gem_object_unreference_unlocked(&cma_obj->base); - } -} - -static void *drm_gem_cma_dmabuf_kmap_atomic(struct dma_buf *dmabuf, - unsigned long page_num) -{ - /* TODO */ - - return NULL; -} - -static void drm_gem_cma_dmabuf_kunmap_atomic(struct dma_buf *dmabuf, - unsigned long page_num, void *addr) -{ - /* TODO */ -} - -static void *drm_gem_cma_dmabuf_kmap(struct dma_buf *dmabuf, - unsigned long page_num) -{ - /* TODO */ - - return NULL; -} - -static void drm_gem_cma_dmabuf_kunmap(struct dma_buf *dmabuf, - unsigned long page_num, void *addr) -{ - /* TODO */ -} - -static int drm_gem_cma_dmabuf_mmap(struct dma_buf *dmabuf, - struct vm_area_struct *vma) -{ - struct drm_gem_cma_object *cma_obj = dmabuf->priv; - struct drm_gem_object *gem_obj = &cma_obj->base; - struct drm_device *dev = gem_obj->dev; - int ret; - - mutex_lock(&dev->struct_mutex); - ret = drm_gem_mmap_obj(gem_obj, gem_obj->size, vma); - mutex_unlock(&dev->struct_mutex); - if (ret < 0) - return ret; - - return drm_gem_cma_mmap_obj(cma_obj, vma); -} - -static void *drm_gem_cma_dmabuf_vmap(struct dma_buf *dmabuf) -{ - struct drm_gem_cma_object *cma_obj = dmabuf->priv; - - return cma_obj->vaddr; -} - -static struct dma_buf_ops drm_gem_cma_dmabuf_ops = { - .attach = drm_gem_cma_dmabuf_attach, - .detach = drm_gem_cma_dmabuf_detach, - .map_dma_buf = drm_gem_cma_dmabuf_map, - .unmap_dma_buf = drm_gem_cma_dmabuf_unmap, - .kmap = drm_gem_cma_dmabuf_kmap, - .kmap_atomic = drm_gem_cma_dmabuf_kmap_atomic, - .kunmap = drm_gem_cma_dmabuf_kunmap, - .kunmap_atomic = drm_gem_cma_dmabuf_kunmap_atomic, - .mmap = drm_gem_cma_dmabuf_mmap, - .vmap = drm_gem_cma_dmabuf_vmap, - .release = drm_gem_cma_dmabuf_release, -}; - -struct dma_buf *drm_gem_cma_dmabuf_export(struct drm_device *drm, - struct drm_gem_object *obj, int flags) -{ - struct drm_gem_cma_object *cma_obj = to_drm_gem_cma_obj(obj); - - return dma_buf_export(cma_obj, &drm_gem_cma_dmabuf_ops, - cma_obj->base.size, flags); -} -EXPORT_SYMBOL_GPL(drm_gem_cma_dmabuf_export); - -struct drm_gem_object *drm_gem_cma_dmabuf_import(struct drm_device *drm, - struct dma_buf *dma_buf) -{ - struct drm_gem_cma_object *cma_obj; - struct dma_buf_attachment *attach; - struct sg_table *sgt; - int ret; - - DRM_DEBUG_PRIME("%s\n", __FILE__); - - /* is this one of own objects? */ - if (dma_buf->ops == &drm_gem_cma_dmabuf_ops) { - struct drm_gem_object *obj; - - cma_obj = dma_buf->priv; - obj = &cma_obj->base; - - /* is it from our device? */ - if (obj->dev == drm) { - /* - * Importing dmabuf exported from out own gem increases - * refcount on gem itself instead of f_count of dmabuf. - */ - drm_gem_object_reference(obj); - dma_buf_put(dma_buf); - return obj; - } - } - - /* Create a CMA GEM buffer. */ - cma_obj = __drm_gem_cma_create(drm, dma_buf->size); - if (IS_ERR(cma_obj)) - return ERR_PTR(PTR_ERR(cma_obj)); - - /* Attach to the buffer and map it. Make sure the mapping is contiguous - * on the device memory bus, as that's all we support. - */ - attach = dma_buf_attach(dma_buf, drm->dev); - if (IS_ERR(attach)) { - ret = -EINVAL; - goto error_gem_free; - } - - sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); - if (IS_ERR_OR_NULL(sgt)) { - ret = sgt ? PTR_ERR(sgt) : -ENOMEM; - goto error_buf_detach; - } - - if (sgt->nents != 1) { - ret = -EINVAL; - goto error_buf_unmap; - } - - cma_obj->base.import_attach = attach; - cma_obj->paddr = sg_dma_address(sgt->sgl); - cma_obj->sgt = sgt; - - DRM_DEBUG_PRIME("dma_addr = 0x%x, size = %zu\n", cma_obj->paddr, - dma_buf->size); - - return &cma_obj->base; - -error_buf_unmap: - dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL); -error_buf_detach: - dma_buf_detach(dma_buf, attach); -error_gem_free: - drm_gem_cma_free_object(&cma_obj->base); - return ERR_PTR(ret); -} -EXPORT_SYMBOL_GPL(drm_gem_cma_dmabuf_import); - /* low-level interface prime helpers */ struct sg_table *drm_gem_cma_prime_get_sg_table(struct drm_gem_object *obj) { diff --git a/include/drm/drm_gem_cma_helper.h b/include/drm/drm_gem_cma_helper.h index 9d39d2a51906..c34f27f80bcc 100644 --- a/include/drm/drm_gem_cma_helper.h +++ b/include/drm/drm_gem_cma_helper.h @@ -48,12 +48,6 @@ extern const struct vm_operations_struct drm_gem_cma_vm_ops; void drm_gem_cma_describe(struct drm_gem_cma_object *obj, struct seq_file *m); #endif -struct dma_buf *drm_gem_cma_dmabuf_export(struct drm_device *drm_dev, - struct drm_gem_object *obj, - int flags); -struct drm_gem_object *drm_gem_cma_dmabuf_import(struct drm_device *drm_dev, - struct dma_buf *dma_buf); - struct sg_table *drm_gem_cma_prime_get_sg_table(struct drm_gem_object *obj); struct drm_gem_object * drm_gem_cma_prime_import_sg_table(struct drm_device *dev, size_t size, -- cgit v1.2.3 From 290ad0f9d954b445788bf26652b239c59cec2060 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Sun, 26 May 2013 11:53:20 +0200 Subject: dma: imx-dma: Add oftree support Adding devicetree support for imx-dma driver. Use driver name for function 'imx_dma_is_general_purpose' because the devicename for devicetree initialized devices is different. Signed-off-by: Markus Pargmann Reviewed-by: Arnd Bergmann Reviewed-by: Shawn Guo Acked-by: Sascha Hauer Signed-off-by: Vinod Koul --- .../devicetree/bindings/dma/fsl-imx-dma.txt | 48 ++++++++++++++ drivers/dma/imx-dma.c | 75 ++++++++++++++++++++++ include/linux/platform_data/dma-imx.h | 6 +- 3 files changed, 125 insertions(+), 4 deletions(-) create mode 100644 Documentation/devicetree/bindings/dma/fsl-imx-dma.txt diff --git a/Documentation/devicetree/bindings/dma/fsl-imx-dma.txt b/Documentation/devicetree/bindings/dma/fsl-imx-dma.txt new file mode 100644 index 000000000000..2717ecb47db9 --- /dev/null +++ b/Documentation/devicetree/bindings/dma/fsl-imx-dma.txt @@ -0,0 +1,48 @@ +* Freescale Direct Memory Access (DMA) Controller for i.MX + +This document will only describe differences to the generic DMA Controller and +DMA request bindings as described in dma/dma.txt . + +* DMA controller + +Required properties: +- compatible : Should be "fsl,-dma". chip can be imx1, imx21 or imx27 +- reg : Should contain DMA registers location and length +- interrupts : First item should be DMA interrupt, second one is optional and + should contain DMA Error interrupt +- #dma-cells : Has to be 1. imx-dma does not support anything else. + +Optional properties: +- #dma-channels : Number of DMA channels supported. Should be 16. +- #dma-requests : Number of DMA requests supported. + +Example: + + dma: dma@10001000 { + compatible = "fsl,imx27-dma"; + reg = <0x10001000 0x1000>; + interrupts = <32 33>; + #dma-cells = <1>; + #dma-channels = <16>; + }; + + +* DMA client + +Clients have to specify the DMA requests with phandles in a list. + +Required properties: +- dmas: List of one or more DMA request specifiers. One DMA request specifier + consists of a phandle to the DMA controller followed by the integer + specifiying the request line. +- dma-names: List of string identifiers for the DMA requests. For the correct + names, have a look at the specific client driver. + +Example: + + sdhci1: sdhci@10013000 { + ... + dmas = <&dma 7>; + dma-names = "rx-tx"; + ... + }; diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index f28583370d00..34c54cf08231 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include #include @@ -186,6 +188,11 @@ struct imxdma_engine { enum imx_dma_type devtype; }; +struct imxdma_filter_data { + struct imxdma_engine *imxdma; + int request; +}; + static struct platform_device_id imx_dma_devtype[] = { { .name = "imx1-dma", @@ -202,6 +209,22 @@ static struct platform_device_id imx_dma_devtype[] = { }; MODULE_DEVICE_TABLE(platform, imx_dma_devtype); +static const struct of_device_id imx_dma_of_dev_id[] = { + { + .compatible = "fsl,imx1-dma", + .data = &imx_dma_devtype[IMX1_DMA], + }, { + .compatible = "fsl,imx21-dma", + .data = &imx_dma_devtype[IMX21_DMA], + }, { + .compatible = "fsl,imx27-dma", + .data = &imx_dma_devtype[IMX27_DMA], + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, imx_dma_of_dev_id); + static inline int is_imx1_dma(struct imxdma_engine *imxdma) { return imxdma->devtype == IMX1_DMA; @@ -996,13 +1019,50 @@ static void imxdma_issue_pending(struct dma_chan *chan) spin_unlock_irqrestore(&imxdma->lock, flags); } +static bool imxdma_filter_fn(struct dma_chan *chan, void *param) +{ + struct imxdma_filter_data *fdata = param; + struct imxdma_channel *imxdma_chan = to_imxdma_chan(chan); + + if (chan->device->dev != fdata->imxdma->dev) + return false; + + imxdma_chan->dma_request = fdata->request; + chan->private = NULL; + + return true; +} + +static struct dma_chan *imxdma_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + int count = dma_spec->args_count; + struct imxdma_engine *imxdma = ofdma->of_dma_data; + struct imxdma_filter_data fdata = { + .imxdma = imxdma, + }; + + if (count != 1) + return NULL; + + fdata.request = dma_spec->args[0]; + + return dma_request_channel(imxdma->dma_device.cap_mask, + imxdma_filter_fn, &fdata); +} + static int __init imxdma_probe(struct platform_device *pdev) { struct imxdma_engine *imxdma; struct resource *res; + const struct of_device_id *of_id; int ret, i; int irq, irq_err; + of_id = of_match_device(imx_dma_of_dev_id, &pdev->dev); + if (of_id) + pdev->id_entry = of_id->data; + imxdma = devm_kzalloc(&pdev->dev, sizeof(*imxdma), GFP_KERNEL); if (!imxdma) return -ENOMEM; @@ -1136,8 +1196,19 @@ static int __init imxdma_probe(struct platform_device *pdev) goto err; } + if (pdev->dev.of_node) { + ret = of_dma_controller_register(pdev->dev.of_node, + imxdma_xlate, imxdma); + if (ret) { + dev_err(&pdev->dev, "unable to register of_dma_controller\n"); + goto err_of_dma_controller; + } + } + return 0; +err_of_dma_controller: + dma_async_device_unregister(&imxdma->dma_device); err: clk_disable_unprepare(imxdma->dma_ipg); clk_disable_unprepare(imxdma->dma_ahb); @@ -1150,6 +1221,9 @@ static int imxdma_remove(struct platform_device *pdev) dma_async_device_unregister(&imxdma->dma_device); + if (pdev->dev.of_node) + of_dma_controller_free(pdev->dev.of_node); + clk_disable_unprepare(imxdma->dma_ipg); clk_disable_unprepare(imxdma->dma_ahb); @@ -1159,6 +1233,7 @@ static int imxdma_remove(struct platform_device *pdev) static struct platform_driver imxdma_driver = { .driver = { .name = "imx-dma", + .of_match_table = imx_dma_of_dev_id, }, .id_table = imx_dma_devtype, .remove = imxdma_remove, diff --git a/include/linux/platform_data/dma-imx.h b/include/linux/platform_data/dma-imx.h index f6d30cc1cb77..beac6b8b6a7b 100644 --- a/include/linux/platform_data/dma-imx.h +++ b/include/linux/platform_data/dma-imx.h @@ -60,10 +60,8 @@ static inline int imx_dma_is_ipu(struct dma_chan *chan) static inline int imx_dma_is_general_purpose(struct dma_chan *chan) { - return strstr(dev_name(chan->device->dev), "sdma") || - !strcmp(dev_name(chan->device->dev), "imx1-dma") || - !strcmp(dev_name(chan->device->dev), "imx21-dma") || - !strcmp(dev_name(chan->device->dev), "imx27-dma"); + return !strcmp(chan->device->dev->driver->name, "imx-sdma") || + !strcmp(chan->device->dev->driver->name, "imx-dma"); } #endif -- cgit v1.2.3 From 5c6b3e7725384f02418d80e7dc32b1a690497004 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Sun, 26 May 2013 11:53:21 +0200 Subject: DMA: imx-dma: imxdma->dev used uninitialized imxdma->dev is used for dev_warn before it was set. Signed-off-by: Markus Pargmann Reviewed-by: Shawn Guo Acked-by: Sascha Hauer Signed-off-by: Vinod Koul --- drivers/dma/imx-dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index 34c54cf08231..ff2aab973b45 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -1067,6 +1067,7 @@ static int __init imxdma_probe(struct platform_device *pdev) if (!imxdma) return -ENOMEM; + imxdma->dev = &pdev->dev; imxdma->devtype = pdev->id_entry->driver_data; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -1171,7 +1172,6 @@ static int __init imxdma_probe(struct platform_device *pdev) &imxdma->dma_device.channels); } - imxdma->dev = &pdev->dev; imxdma->dma_device.dev = &pdev->dev; imxdma->dma_device.device_alloc_chan_resources = imxdma_alloc_chan_resources; -- cgit v1.2.3 From ea7e79063e604c89b16b819d2e88b20c421d9514 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Fri, 10 May 2013 15:19:13 +0200 Subject: dmaengine: at_hdmac/trivial: correct typo in comment Signed-off-by: Nicolas Ferre Acked-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Vinod Koul --- drivers/dma/at_hdmac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index e923cda930f9..cd494209352a 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -1120,7 +1120,7 @@ static int atc_alloc_chan_resources(struct dma_chan *chan) */ BUG_ON(!atslave->dma_dev || atslave->dma_dev != atdma->dma_common.dev); - /* if cfg configuration specified take it instad of default */ + /* if cfg configuration specified take it instead of default */ if (atslave->cfg) cfg = atslave->cfg; } -- cgit v1.2.3 From 72ae6e4b31e40397eaa81007b39a1074638a6798 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Fri, 10 May 2013 15:19:14 +0200 Subject: dmaengine: at_hdmac: extend hardware handshaking interface identification Peripheral handshaking identification numbers can be bigger than 15, so new fields have been created in the CFG register. Add macros to take this modification into account and use them in at_dma_xlate() function. Signed-off-by: Nicolas Ferre Acked-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Vinod Koul --- drivers/dma/at_hdmac.c | 2 ++ include/linux/platform_data/dma-atmel.h | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index cd494209352a..78c3fb4b4e40 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -1230,6 +1230,8 @@ static struct dma_chan *at_dma_xlate(struct of_phandle_args *dma_spec, per_id = dma_spec->args[1]; atslave->cfg = ATC_FIFOCFG_HALFFIFO | ATC_DST_H2SEL_HW | ATC_SRC_H2SEL_HW | ATC_DST_PER(per_id) + | ATC_DST_PER_MSB(per_id) + | ATC_SRC_PER_MSB(per_id) | ATC_SRC_PER(per_id); atslave->dma_dev = &dmac_pdev->dev; diff --git a/include/linux/platform_data/dma-atmel.h b/include/linux/platform_data/dma-atmel.h index cab0997be3de..e95f19c65873 100644 --- a/include/linux/platform_data/dma-atmel.h +++ b/include/linux/platform_data/dma-atmel.h @@ -35,16 +35,20 @@ struct at_dma_slave { /* Platform-configurable bits in CFG */ +#define ATC_PER_MSB(h) ((0x30U & (h)) >> 4) /* Extract most significant bits of a handshaking identifier */ + #define ATC_SRC_PER(h) (0xFU & (h)) /* Channel src rq associated with periph handshaking ifc h */ #define ATC_DST_PER(h) ((0xFU & (h)) << 4) /* Channel dst rq associated with periph handshaking ifc h */ #define ATC_SRC_REP (0x1 << 8) /* Source Replay Mod */ #define ATC_SRC_H2SEL (0x1 << 9) /* Source Handshaking Mod */ #define ATC_SRC_H2SEL_SW (0x0 << 9) #define ATC_SRC_H2SEL_HW (0x1 << 9) +#define ATC_SRC_PER_MSB(h) (ATC_PER_MSB(h) << 10) /* Channel src rq (most significant bits) */ #define ATC_DST_REP (0x1 << 12) /* Destination Replay Mod */ #define ATC_DST_H2SEL (0x1 << 13) /* Destination Handshaking Mod */ #define ATC_DST_H2SEL_SW (0x0 << 13) #define ATC_DST_H2SEL_HW (0x1 << 13) +#define ATC_DST_PER_MSB(h) (ATC_PER_MSB(h) << 14) /* Channel dst rq (most significant bits) */ #define ATC_SOD (0x1 << 16) /* Stop On Done */ #define ATC_LOCK_IF (0x1 << 20) /* Interface Lock */ #define ATC_LOCK_B (0x1 << 21) /* AHB Bus Lock */ -- cgit v1.2.3 From 6c22770f644bf23aecc11fedd7b305488a861bfc Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Fri, 10 May 2013 15:19:15 +0200 Subject: dmaengine: at_hdmac/trivial: rearrange CFG register bits assignment No modification in CFG register configuration, just rearrange bits directives to group logically and make it more readable. Signed-off-by: Nicolas Ferre Acked-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Vinod Koul --- drivers/dma/at_hdmac.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 78c3fb4b4e40..9e1ad73a78cf 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -1228,11 +1228,10 @@ static struct dma_chan *at_dma_xlate(struct of_phandle_args *dma_spec, * ignored depending on DMA transfer direction. */ per_id = dma_spec->args[1]; - atslave->cfg = ATC_FIFOCFG_HALFFIFO | ATC_DST_H2SEL_HW - | ATC_SRC_H2SEL_HW | ATC_DST_PER(per_id) - | ATC_DST_PER_MSB(per_id) - | ATC_SRC_PER_MSB(per_id) - | ATC_SRC_PER(per_id); + atslave->cfg = ATC_FIFOCFG_HALFFIFO + | ATC_DST_H2SEL_HW | ATC_SRC_H2SEL_HW + | ATC_DST_PER_MSB(per_id) | ATC_DST_PER(per_id) + | ATC_SRC_PER_MSB(per_id) | ATC_SRC_PER(per_id); atslave->dma_dev = &dmac_pdev->dev; chan = dma_request_channel(mask, at_dma_filter, atslave); -- cgit v1.2.3 From d088c33b646e9f3564eea7a057a2cb697c18bcd0 Mon Sep 17 00:00:00 2001 From: Elen Song Date: Fri, 10 May 2013 11:00:50 +0800 Subject: DMA: AT91: Get transfer width In one dma transfer, the data transfer width can be configured and it is limited by source or destination peripheral width, tx_width will save the transfer width, but for memcpy, either source or destination transfer width is taken as tx_width. Signed-off-by: Elen Song Signed-off-by: Vinod Koul --- drivers/dma/at_hdmac.c | 3 +++ drivers/dma/at_hdmac_regs.h | 2 ++ 2 files changed, 5 insertions(+) diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 9e1ad73a78cf..4c101a9dd3cb 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -615,6 +615,7 @@ atc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, /* First descriptor of the chain embedds additional information */ first->txd.cookie = -EBUSY; first->len = len; + first->tx_width = src_width; /* set end-of-link to the last link descriptor of list*/ set_desc_eol(desc); @@ -761,6 +762,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, /* First descriptor of the chain embedds additional information */ first->txd.cookie = -EBUSY; first->len = total_len; + first->tx_width = reg_width; /* first link descriptor of list is responsible of flags */ first->txd.flags = flags; /* client is in control of this ack */ @@ -919,6 +921,7 @@ atc_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, /* First descriptor of the chain embedds additional information */ first->txd.cookie = -EBUSY; first->len = buf_len; + first->tx_width = reg_width; return &first->txd; diff --git a/drivers/dma/at_hdmac_regs.h b/drivers/dma/at_hdmac_regs.h index c604d26fd4d3..3679933fb646 100644 --- a/drivers/dma/at_hdmac_regs.h +++ b/drivers/dma/at_hdmac_regs.h @@ -182,6 +182,7 @@ struct at_lli { * @txd: support for the async_tx api * @desc_node: node on the channed descriptors list * @len: total transaction bytecount + * @tx_width: transfer width */ struct at_desc { /* FIRST values the hardware uses */ @@ -192,6 +193,7 @@ struct at_desc { struct dma_async_tx_descriptor txd; struct list_head desc_node; size_t len; + u32 tx_width; }; static inline struct at_desc * -- cgit v1.2.3 From d48de6f1a81b3d10de0f5765aff1b3bd788617b0 Mon Sep 17 00:00:00 2001 From: Elen Song Date: Fri, 10 May 2013 11:01:46 +0800 Subject: DMA: AT91: Get residual bytes in dma buffer Add support for returning the residue for current transfer cookie by reading the transfered buffer size(BTSIZE) in CTRLA register. For a single buffer cookie, the descriptor length minus BTSIZE can get the residue. For a lli cookie, remain_desc will record remain descriptor length when last descriptor finish, the remain_desc minus BTSIZE can get the current residue. If the cookie has completed successfully, the residue will be zero. If the cookie is in progress, it will be the number of bytes yet to be transferred. If get residue error, the cookie will be turn into error status. Check dma fifo to see if data remain, let issue pending finish remain work if there is. Signed-off-by: Elen Song Signed-off-by: Vinod Koul --- drivers/dma/at_hdmac.c | 130 ++++++++++++++++++++++++++++++++++++++------ drivers/dma/at_hdmac_regs.h | 3 + 2 files changed, 115 insertions(+), 18 deletions(-) diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 4c101a9dd3cb..5ce89368a8db 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -54,6 +54,7 @@ MODULE_PARM_DESC(init_nr_desc_per_channel, /* prototypes */ static dma_cookie_t atc_tx_submit(struct dma_async_tx_descriptor *tx); +static void atc_issue_pending(struct dma_chan *chan); /*----------------------------------------------------------------------*/ @@ -230,6 +231,94 @@ static void atc_dostart(struct at_dma_chan *atchan, struct at_desc *first) vdbg_dump_regs(atchan); } +/* + * atc_get_current_descriptors - + * locate the descriptor which equal to physical address in DSCR + * @atchan: the channel we want to start + * @dscr_addr: physical descriptor address in DSCR + */ +static struct at_desc *atc_get_current_descriptors(struct at_dma_chan *atchan, + u32 dscr_addr) +{ + struct at_desc *desc, *_desc, *child, *desc_cur = NULL; + + list_for_each_entry_safe(desc, _desc, &atchan->active_list, desc_node) { + if (desc->lli.dscr == dscr_addr) { + desc_cur = desc; + break; + } + + list_for_each_entry(child, &desc->tx_list, desc_node) { + if (child->lli.dscr == dscr_addr) { + desc_cur = child; + break; + } + } + } + + return desc_cur; +} + +/* + * atc_get_bytes_left - + * Get the number of bytes residue in dma buffer, + * @chan: the channel we want to start + */ +static int atc_get_bytes_left(struct dma_chan *chan) +{ + struct at_dma_chan *atchan = to_at_dma_chan(chan); + struct at_dma *atdma = to_at_dma(chan->device); + int chan_id = atchan->chan_common.chan_id; + struct at_desc *desc_first = atc_first_active(atchan); + struct at_desc *desc_cur; + int ret = 0, count = 0; + + /* + * Initialize necessary values in the first time. + * remain_desc record remain desc length. + */ + if (atchan->remain_desc == 0) + /* First descriptor embedds the transaction length */ + atchan->remain_desc = desc_first->len; + + /* + * This happens when current descriptor transfer complete. + * The residual buffer size should reduce current descriptor length. + */ + if (unlikely(test_bit(ATC_IS_BTC, &atchan->status))) { + clear_bit(ATC_IS_BTC, &atchan->status); + desc_cur = atc_get_current_descriptors(atchan, + channel_readl(atchan, DSCR)); + if (!desc_cur) { + ret = -EINVAL; + goto out; + } + atchan->remain_desc -= (desc_cur->lli.ctrla & ATC_BTSIZE_MAX) + << (desc_first->tx_width); + if (atchan->remain_desc < 0) { + ret = -EINVAL; + goto out; + } else + ret = atchan->remain_desc; + } else { + /* + * Get residual bytes when current + * descriptor transfer in progress. + */ + count = (channel_readl(atchan, CTRLA) & ATC_BTSIZE_MAX) + << (desc_first->tx_width); + ret = atchan->remain_desc - count; + } + /* + * Check fifo empty. + */ + if (!(dma_readl(atdma, CHSR) & AT_DMA_EMPT(chan_id))) + atc_issue_pending(chan); + +out: + return ret; +} + /** * atc_chain_complete - finish work for one transaction chain * @atchan: channel we work on @@ -496,6 +585,8 @@ static irqreturn_t at_dma_interrupt(int irq, void *dev_id) /* Give information to tasklet */ set_bit(ATC_IS_ERROR, &atchan->status); } + if (pending & AT_DMA_BTC(i)) + set_bit(ATC_IS_BTC, &atchan->status); tasklet_schedule(&atchan->tasklet); ret = IRQ_HANDLED; } @@ -1035,34 +1126,35 @@ atc_tx_status(struct dma_chan *chan, struct dma_tx_state *txstate) { struct at_dma_chan *atchan = to_at_dma_chan(chan); - dma_cookie_t last_used; - dma_cookie_t last_complete; unsigned long flags; enum dma_status ret; - - spin_lock_irqsave(&atchan->lock, flags); + int bytes = 0; ret = dma_cookie_status(chan, cookie, txstate); - if (ret != DMA_SUCCESS) { - atc_cleanup_descriptors(atchan); + if (ret == DMA_SUCCESS) + return ret; + /* + * There's no point calculating the residue if there's + * no txstate to store the value. + */ + if (!txstate) + return DMA_ERROR; - ret = dma_cookie_status(chan, cookie, txstate); - } + spin_lock_irqsave(&atchan->lock, flags); - last_complete = chan->completed_cookie; - last_used = chan->cookie; + /* Get number of bytes left in the active transactions */ + bytes = atc_get_bytes_left(chan); spin_unlock_irqrestore(&atchan->lock, flags); - if (ret != DMA_SUCCESS) - dma_set_residue(txstate, atc_first_active(atchan)->len); - - if (atc_chan_is_paused(atchan)) - ret = DMA_PAUSED; + if (unlikely(bytes < 0)) { + dev_vdbg(chan2dev(chan), "get residual bytes error\n"); + return DMA_ERROR; + } else + dma_set_residue(txstate, bytes); - dev_vdbg(chan2dev(chan), "tx_status %d: cookie = %d (d%d, u%d)\n", - ret, cookie, last_complete ? last_complete : 0, - last_used ? last_used : 0); + dev_vdbg(chan2dev(chan), "tx_status %d: cookie = %d residue = %d\n", + ret, cookie, bytes); return ret; } @@ -1146,6 +1238,7 @@ static int atc_alloc_chan_resources(struct dma_chan *chan) spin_lock_irqsave(&atchan->lock, flags); atchan->descs_allocated = i; + atchan->remain_desc = 0; list_splice(&tmp_list, &atchan->free_list); dma_cookie_init(chan); spin_unlock_irqrestore(&atchan->lock, flags); @@ -1188,6 +1281,7 @@ static void atc_free_chan_resources(struct dma_chan *chan) list_splice_init(&atchan->free_list, &list); atchan->descs_allocated = 0; atchan->status = 0; + atchan->remain_desc = 0; dev_vdbg(chan2dev(chan), "free_chan_resources: done\n"); } diff --git a/drivers/dma/at_hdmac_regs.h b/drivers/dma/at_hdmac_regs.h index 3679933fb646..f31d647acdfa 100644 --- a/drivers/dma/at_hdmac_regs.h +++ b/drivers/dma/at_hdmac_regs.h @@ -213,6 +213,7 @@ txd_to_at_desc(struct dma_async_tx_descriptor *txd) enum atc_status { ATC_IS_ERROR = 0, ATC_IS_PAUSED = 1, + ATC_IS_BTC = 2, ATC_IS_CYCLIC = 24, }; @@ -230,6 +231,7 @@ enum atc_status { * @save_cfg: configuration register that is saved on suspend/resume cycle * @save_dscr: for cyclic operations, preserve next descriptor address in * the cyclic list on suspend/resume cycle + * @remain_desc: to save remain desc length * @dma_sconfig: configuration for slave transfers, passed via DMA_SLAVE_CONFIG * @lock: serializes enqueue/dequeue operations to descriptors lists * @active_list: list of descriptors dmaengine is being running on @@ -248,6 +250,7 @@ struct at_dma_chan { struct tasklet_struct tasklet; u32 save_cfg; u32 save_dscr; + u32 remain_desc; struct dma_slave_config dma_sconfig; spinlock_t lock; -- cgit v1.2.3 From dd3daca162f7411448dd80a8872a002c43cfd8e5 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Fri, 24 May 2013 10:10:13 +0900 Subject: dma: use platform_{get,set}_drvdata() Use the wrapper functions for getting and setting the driver data using platform_device instead of using dev_{get,set}_drvdata() with &pdev->dev, so we can directly pass a struct platform_device. Also, unnecessary dev_set_drvdata() is removed, because the driver core clears the driver data to NULL after device_release or on probe failure. Signed-off-by: Jingoo Han Signed-off-by: Vinod Koul --- drivers/dma/fsldma.c | 5 ++--- drivers/dma/ppc4xx/adma.c | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index 4fc2980556ad..49e8fbdb8983 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -1368,7 +1368,7 @@ static int fsldma_of_probe(struct platform_device *op) dma_set_mask(&(op->dev), DMA_BIT_MASK(36)); - dev_set_drvdata(&op->dev, fdev); + platform_set_drvdata(op, fdev); /* * We cannot use of_platform_bus_probe() because there is no @@ -1417,7 +1417,7 @@ static int fsldma_of_remove(struct platform_device *op) struct fsldma_device *fdev; unsigned int i; - fdev = dev_get_drvdata(&op->dev); + fdev = platform_get_drvdata(op); dma_async_device_unregister(&fdev->common); fsldma_free_irqs(fdev); @@ -1428,7 +1428,6 @@ static int fsldma_of_remove(struct platform_device *op) } iounmap(fdev->regs); - dev_set_drvdata(&op->dev, NULL); kfree(fdev); return 0; diff --git a/drivers/dma/ppc4xx/adma.c b/drivers/dma/ppc4xx/adma.c index 5d3d95569a1e..e68c51d13cb1 100644 --- a/drivers/dma/ppc4xx/adma.c +++ b/drivers/dma/ppc4xx/adma.c @@ -4481,7 +4481,7 @@ static int ppc440spe_adma_probe(struct platform_device *ofdev) adev->dev = &ofdev->dev; adev->common.dev = &ofdev->dev; INIT_LIST_HEAD(&adev->common.channels); - dev_set_drvdata(&ofdev->dev, adev); + platform_set_drvdata(ofdev, adev); /* create a channel */ chan = kzalloc(sizeof(*chan), GFP_KERNEL); @@ -4594,14 +4594,13 @@ out: */ static int ppc440spe_adma_remove(struct platform_device *ofdev) { - struct ppc440spe_adma_device *adev = dev_get_drvdata(&ofdev->dev); + struct ppc440spe_adma_device *adev = platform_get_drvdata(ofdev); struct device_node *np = ofdev->dev.of_node; struct resource res; struct dma_chan *chan, *_chan; struct ppc_dma_chan_ref *ref, *_ref; struct ppc440spe_adma_chan *ppc440spe_chan; - dev_set_drvdata(&ofdev->dev, NULL); if (adev->id < PPC440SPE_ADMA_ENGINES_NUM) ppc440spe_adma_devices[adev->id] = -1; -- cgit v1.2.3 From 3208b3701b98dcd14f0d5f0a36dd33d21b0b458f Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 24 May 2013 16:37:27 -0300 Subject: dma: mxs-dma: Staticize mxs_dma_xlate Fix the following sparse warning: drivers/dma/mxs-dma.c:696:17: warning: symbol 'mxs_dma_xlate' was not declared. Should it be static? Signed-off-by: Fabio Estevam Acked-by: Shawn Guo Signed-off-by: Vinod Koul --- drivers/dma/mxs-dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index b48a79c28845..719593002ab7 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c @@ -693,7 +693,7 @@ static bool mxs_dma_filter_fn(struct dma_chan *chan, void *fn_param) return true; } -struct dma_chan *mxs_dma_xlate(struct of_phandle_args *dma_spec, +static struct dma_chan *mxs_dma_xlate(struct of_phandle_args *dma_spec, struct of_dma *ofdma) { struct mxs_dma_engine *mxs_dma = ofdma->of_dma_data; -- cgit v1.2.3 From 36c6df5062568f0b923930b63e2e477cb3a391bd Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 6 May 2013 12:53:33 +0900 Subject: dma: at_hdmac: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Acked-by: Nicolas Ferre Signed-off-by: Vinod Koul --- drivers/dma/at_hdmac.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 5ce89368a8db..6db5228f4134 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -1570,7 +1570,6 @@ err_of_dma_controller_register: dma_async_device_unregister(&atdma->dma_common); dma_pool_destroy(atdma->dma_desc_pool); err_pool_create: - platform_set_drvdata(pdev, NULL); free_irq(platform_get_irq(pdev, 0), atdma); err_irq: clk_disable(atdma->clk); @@ -1595,7 +1594,6 @@ static int at_dma_remove(struct platform_device *pdev) dma_async_device_unregister(&atdma->dma_common); dma_pool_destroy(atdma->dma_desc_pool); - platform_set_drvdata(pdev, NULL); free_irq(platform_get_irq(pdev, 0), atdma); list_for_each_entry_safe(chan, _chan, &atdma->dma_common.channels, -- cgit v1.2.3 From c1a9d391adc7feb219edd354deacb587b26cad06 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 6 May 2013 12:54:48 +0900 Subject: dma: timb_dma: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure, since commit 0998d0631001288a5974afc0b2a5f568bcdecb4d (device-core: Ensure drvdata = NULL when no driver is bound). Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Jingoo Han Signed-off-by: Vinod Koul --- drivers/dma/timb_dma.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/dma/timb_dma.c b/drivers/dma/timb_dma.c index 26107ba6edb3..0ef43c136aa7 100644 --- a/drivers/dma/timb_dma.c +++ b/drivers/dma/timb_dma.c @@ -811,8 +811,6 @@ static int td_remove(struct platform_device *pdev) kfree(td); release_mem_region(iomem->start, resource_size(iomem)); - platform_set_drvdata(pdev, NULL); - dev_dbg(&pdev->dev, "Removed...\n"); return 0; } -- cgit v1.2.3 From 8004cbb481494c166596b0d469a6c777415e18f6 Mon Sep 17 00:00:00 2001 From: Denis Efremov Date: Thu, 9 May 2013 13:19:40 +0400 Subject: dw_dmac: remove inline marking of EXPORT_SYMBOL functions EXPORT_SYMBOL and inline directives are contradictory to each other. The patch fixes this inconsistency. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Denis Efremov Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 2e5deaa82b60..724083d02b34 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -556,14 +556,14 @@ static void dwc_handle_error(struct dw_dma *dw, struct dw_dma_chan *dwc) /* --------------------- Cyclic DMA API extensions -------------------- */ -inline dma_addr_t dw_dma_get_src_addr(struct dma_chan *chan) +dma_addr_t dw_dma_get_src_addr(struct dma_chan *chan) { struct dw_dma_chan *dwc = to_dw_dma_chan(chan); return channel_readl(dwc, SAR); } EXPORT_SYMBOL(dw_dma_get_src_addr); -inline dma_addr_t dw_dma_get_dst_addr(struct dma_chan *chan) +dma_addr_t dw_dma_get_dst_addr(struct dma_chan *chan) { struct dw_dma_chan *dwc = to_dw_dma_chan(chan); return channel_readl(dwc, DAR); -- cgit v1.2.3 From ac7ae754d592571478959833796b7bdf1a3c08da Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Sat, 11 May 2013 20:30:52 +0400 Subject: dma: tegra20-apbdma: err message correction Fixed err msg params order on irq request fail. Signed-off-by: Dmitry Osipenko Acked-by: Stephen Warren Acked-by: Laxman Dewangan Signed-off-by: Vinod Koul --- drivers/dma/tegra20-apb-dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index 33f59ecd256e..5953547a5f75 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -1334,7 +1334,7 @@ static int tegra_dma_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "request_irq failed with err %d channel %d\n", - i, ret); + ret, i); goto err_irq; } -- cgit v1.2.3 From 7bdc1e272a471062e8f310137c896e2355b46d13 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Sat, 11 May 2013 20:30:53 +0400 Subject: dma: tegra: avoid channel lock up after free Lock scenario: Channel 1 was allocated and prepared as slave_sg, used and freed. Now preparation of cyclic dma on channel 1 will fail with err "DMA configuration conflict" because tdc->isr_handler still setted to handle_once_dma_done. This happens because tegra_dma_abort_all() won't be called on channel freeing if pending list is empty and channel not busy. We need to clear isr_handler on channel freeing to avoid locking. Signed-off-by: Dmitry Osipenko Acked-by: Stephen Warren Acked-by: Laxman Dewangan Signed-off-by: Vinod Koul --- drivers/dma/tegra20-apb-dma.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index 5953547a5f75..f137914d7b16 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -1191,6 +1191,7 @@ static void tegra_dma_free_chan_resources(struct dma_chan *dc) list_splice_init(&tdc->free_dma_desc, &dma_desc_list); INIT_LIST_HEAD(&tdc->cb_desc); tdc->config_init = false; + tdc->isr_handler = NULL; spin_unlock_irqrestore(&tdc->lock, flags); while (!list_empty(&dma_desc_list)) { -- cgit v1.2.3 From add93b578edda2a952b9b481ce8da2a9dc412cee Mon Sep 17 00:00:00 2001 From: Rongjun Ying Date: Tue, 14 May 2013 23:03:20 +0800 Subject: dmaengine: sirf: set dma residue based on the current dma transfer position read SIRFSOC_DMA_CH_ADDR register to get current dma transfer position, then update dma residue so that things like ALSA drivers work as ALSA drivers need the right residue value. Signed-off-by: Rongjun Ying Signed-off-by: Barry Song Signed-off-by: Vinod Koul --- drivers/dma/sirf-dma.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/dma/sirf-dma.c b/drivers/dma/sirf-dma.c index 1765a0a2736d..716b23e4f327 100644 --- a/drivers/dma/sirf-dma.c +++ b/drivers/dma/sirf-dma.c @@ -466,12 +466,29 @@ static enum dma_status sirfsoc_dma_tx_status(struct dma_chan *chan, dma_cookie_t cookie, struct dma_tx_state *txstate) { + struct sirfsoc_dma *sdma = dma_chan_to_sirfsoc_dma(chan); struct sirfsoc_dma_chan *schan = dma_chan_to_sirfsoc_dma_chan(chan); unsigned long flags; enum dma_status ret; + struct sirfsoc_dma_desc *sdesc; + int cid = schan->chan.chan_id; + unsigned long dma_pos; + unsigned long dma_request_bytes; + unsigned long residue; spin_lock_irqsave(&schan->lock, flags); + + sdesc = list_first_entry(&schan->active, struct sirfsoc_dma_desc, + node); + dma_request_bytes = (sdesc->xlen + 1) * (sdesc->ylen + 1) * + (sdesc->width * SIRFSOC_DMA_WORD_LEN); + ret = dma_cookie_status(chan, cookie, txstate); + dma_pos = readl_relaxed(sdma->base + cid * 0x10 + SIRFSOC_DMA_CH_ADDR) + << 2; + residue = dma_request_bytes - (dma_pos - sdesc->addr); + dma_set_residue(txstate, residue); + spin_unlock_irqrestore(&schan->lock, flags); return ret; -- cgit v1.2.3 From 9479e17c9bb455c01b369d294e01de8fa9b0a8d3 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Thu, 30 May 2013 22:23:32 +0800 Subject: dma: imx-sdma: move to generic device tree bindings Update imx-sdma driver to adopt generic DMA device tree bindings. It calls of_dma_controller_register() with imx-sdma specific of_dma_xlate to get the generic DMA device tree helper support. The #dma-cells for imx-sdma must be 3, which includes request ID, peripheral type and priority. The existing way of requesting channel, clients directly call dma_request_channel(), still work there, and will be removed after all imx-sdma clients get converted to generic DMA device tree helper. Signed-off-by: Shawn Guo Signed-off-by: Vinod Koul Acked-by: Arnd Bergmann --- .../devicetree/bindings/dma/fsl-imx-sdma.txt | 56 ++++++++++++++++++++++ drivers/dma/imx-sdma.c | 40 ++++++++++++++++ 2 files changed, 96 insertions(+) diff --git a/Documentation/devicetree/bindings/dma/fsl-imx-sdma.txt b/Documentation/devicetree/bindings/dma/fsl-imx-sdma.txt index d1e3f443e205..68cee4f5539f 100644 --- a/Documentation/devicetree/bindings/dma/fsl-imx-sdma.txt +++ b/Documentation/devicetree/bindings/dma/fsl-imx-sdma.txt @@ -4,14 +4,70 @@ Required properties: - compatible : Should be "fsl,-sdma" - reg : Should contain SDMA registers location and length - interrupts : Should contain SDMA interrupt +- #dma-cells : Must be <3>. + The first cell specifies the DMA request/event ID. See details below + about the second and third cell. - fsl,sdma-ram-script-name : Should contain the full path of SDMA RAM scripts firmware +The second cell of dma phandle specifies the peripheral type of DMA transfer. +The full ID of peripheral types can be found below. + + ID transfer type + --------------------- + 0 MCU domain SSI + 1 Shared SSI + 2 MMC + 3 SDHC + 4 MCU domain UART + 5 Shared UART + 6 FIRI + 7 MCU domain CSPI + 8 Shared CSPI + 9 SIM + 10 ATA + 11 CCM + 12 External peripheral + 13 Memory Stick Host Controller + 14 Shared Memory Stick Host Controller + 15 DSP + 16 Memory + 17 FIFO type Memory + 18 SPDIF + 19 IPU Memory + 20 ASRC + 21 ESAI + +The third cell specifies the transfer priority as below. + + ID transfer priority + ------------------------- + 0 High + 1 Medium + 2 Low + Examples: sdma@83fb0000 { compatible = "fsl,imx51-sdma", "fsl,imx35-sdma"; reg = <0x83fb0000 0x4000>; interrupts = <6>; + #dma-cells = <3>; fsl,sdma-ram-script-name = "sdma-imx51.bin"; }; + +DMA clients connected to the i.MX SDMA controller must use the format +described in the dma.txt file. + +Examples: + +ssi2: ssi@70014000 { + compatible = "fsl,imx51-ssi", "fsl,imx21-ssi"; + reg = <0x70014000 0x4000>; + interrupts = <30>; + clocks = <&clks 49>; + dmas = <&sdma 24 1 0>, + <&sdma 25 1 0>; + dma-names = "rx", "tx"; + fsl,fifo-depth = <15>; +}; diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 092867bf795c..1e44b8cf95da 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -1296,6 +1297,35 @@ err_dma_alloc: return ret; } +static bool sdma_filter_fn(struct dma_chan *chan, void *fn_param) +{ + struct imx_dma_data *data = fn_param; + + if (!imx_dma_is_general_purpose(chan)) + return false; + + chan->private = data; + + return true; +} + +static struct dma_chan *sdma_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + struct sdma_engine *sdma = ofdma->of_dma_data; + dma_cap_mask_t mask = sdma->dma_device.cap_mask; + struct imx_dma_data data; + + if (dma_spec->args_count != 3) + return NULL; + + data.dma_request = dma_spec->args[0]; + data.peripheral_type = dma_spec->args[1]; + data.priority = dma_spec->args[2]; + + return dma_request_channel(mask, sdma_filter_fn, &data); +} + static int __init sdma_probe(struct platform_device *pdev) { const struct of_device_id *of_id = @@ -1443,10 +1473,20 @@ static int __init sdma_probe(struct platform_device *pdev) goto err_init; } + if (np) { + ret = of_dma_controller_register(np, sdma_xlate, sdma); + if (ret) { + dev_err(&pdev->dev, "failed to register controller\n"); + goto err_register; + } + } + dev_info(sdma->dev, "initialized\n"); return 0; +err_register: + dma_async_device_unregister(&sdma->dma_device); err_init: kfree(sdma->script_addrs); err_alloc: -- cgit v1.2.3 From 09677176610e7c3ed8ddb302fd24bbb59bdbf205 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Mon, 10 Jun 2013 19:34:37 +0100 Subject: dma: pl330: rip out broken, redundant ID probing The PL330 driver probes the peripheral and primecell IDs of the device to make sure that it is indeed an AMBA PL330. However, it does this by making byte accesses to a device mapping of the word-aligned ID registers, which is either UNPREDICTABLE or generates an alignment fault (depending on the presence of the virtualisation extensions). Rather than fix this code, we can actually rip most of it out and let the AMBA bus driver correctly do the probing for us. Cc: Jassi Brar Cc: Vinod Koul Signed-off-by: Will Deacon Acked-by: Jassi Brar Acked-by: Grant Likely Signed-off-by: Vinod Koul --- drivers/dma/pl330.c | 27 +++------------------------ 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index a17553f7c028..ac04335ef444 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -157,7 +157,6 @@ enum pl330_reqtype { #define PERIPH_REV_R0P0 0 #define PERIPH_REV_R1P0 1 #define PERIPH_REV_R1P1 2 -#define PCELL_ID 0xff0 #define CR0_PERIPH_REQ_SET (1 << 0) #define CR0_BOOT_EN_SET (1 << 1) @@ -193,8 +192,6 @@ enum pl330_reqtype { #define INTEG_CFG 0x0 #define PERIPH_ID_VAL ((PART << 0) | (DESIGNER << 12)) -#define PCELL_ID_VAL 0xb105f00d - #define PL330_STATE_STOPPED (1 << 0) #define PL330_STATE_EXECUTING (1 << 1) #define PL330_STATE_WFE (1 << 2) @@ -292,7 +289,6 @@ static unsigned cmd_line; /* Populated by the PL330 core driver for DMA API driver's info */ struct pl330_config { u32 periph_id; - u32 pcell_id; #define DMAC_MODE_NS (1 << 0) unsigned int mode; unsigned int data_bus_width:10; /* In number of bits */ @@ -650,19 +646,6 @@ static inline bool _manager_ns(struct pl330_thread *thrd) return (pl330->pinfo->pcfg.mode & DMAC_MODE_NS) ? true : false; } -static inline u32 get_id(struct pl330_info *pi, u32 off) -{ - void __iomem *regs = pi->base; - u32 id = 0; - - id |= (readb(regs + off + 0x0) << 0); - id |= (readb(regs + off + 0x4) << 8); - id |= (readb(regs + off + 0x8) << 16); - id |= (readb(regs + off + 0xc) << 24); - - return id; -} - static inline u32 get_revision(u32 periph_id) { return (periph_id >> PERIPH_REV_SHIFT) & PERIPH_REV_MASK; @@ -1986,9 +1969,6 @@ static void read_dmac_config(struct pl330_info *pi) pi->pcfg.num_events = val; pi->pcfg.irq_ns = readl(regs + CR3); - - pi->pcfg.periph_id = get_id(pi, PERIPH_ID); - pi->pcfg.pcell_id = get_id(pi, PCELL_ID); } static inline void _reset_thread(struct pl330_thread *thrd) @@ -2098,10 +2078,8 @@ static int pl330_add(struct pl330_info *pi) regs = pi->base; /* Check if we can handle this DMAC */ - if ((get_id(pi, PERIPH_ID) & 0xfffff) != PERIPH_ID_VAL - || get_id(pi, PCELL_ID) != PCELL_ID_VAL) { - dev_err(pi->dev, "PERIPH_ID 0x%x, PCELL_ID 0x%x !\n", - get_id(pi, PERIPH_ID), get_id(pi, PCELL_ID)); + if ((pi->pcfg.periph_id & 0xfffff) != PERIPH_ID_VAL) { + dev_err(pi->dev, "PERIPH_ID 0x%x !\n", pi->pcfg.periph_id); return -EINVAL; } @@ -2916,6 +2894,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) if (ret) return ret; + pi->pcfg.periph_id = adev->periphid; ret = pl330_add(pi); if (ret) goto probe_err1; -- cgit v1.2.3 From fed8c45727abd273fd74b3e78b35be4929121334 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Mon, 10 Jun 2013 19:34:38 +0100 Subject: dma: pl330: use dma_addr_t for describing bus addresses The microcode bus address (pl330_dmac.mcode_bus) is currently a u32, which fails to compile when building on a system with 64-bit bus addresses. This patch uses dma_addr_t to represent the address instead. Cc: Jassi Brar Cc: Vinod Koul Signed-off-by: Will Deacon Acked-by: Jassi Brar Acked-by: Grant Likely Signed-off-by: Vinod Koul --- drivers/dma/pl330.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index ac04335ef444..bd69cc47150e 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -501,7 +501,7 @@ struct pl330_dmac { /* Maximum possible events/irqs */ int events[32]; /* BUS address of MicroCode buffer */ - u32 mcode_bus; + dma_addr_t mcode_bus; /* CPU address of MicroCode buffer */ void *mcode_cpu; /* List of all Channel threads */ -- cgit v1.2.3 From 0b95961e03ecee31d6151db79cc0826e702d1e0a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 5 Jun 2013 15:26:43 +0300 Subject: dw_dmac: don't check resource with devm_ioremap_resource devm_ioremap_resource does sanity checks on the given resource. No need to duplicate this in the driver. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Acked-by: Arnd Bergmann Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 724083d02b34..2b65ba614e60 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1667,14 +1667,11 @@ static int dw_probe(struct platform_device *pdev) int err; int i; - io = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!io) - return -EINVAL; - irq = platform_get_irq(pdev, 0); if (irq < 0) return irq; + io = platform_get_resource(pdev, IORESOURCE_MEM, 0); regs = devm_ioremap_resource(&pdev->dev, io); if (IS_ERR(regs)) return PTR_ERR(regs); -- cgit v1.2.3 From 61a7649620d54a037c612f9a713abe5178cddc65 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 5 Jun 2013 15:26:44 +0300 Subject: dma: move dw_dmac driver to an own directory The dw_dmac driver is going to be split into multiple files. To make this more convenient move it to an own directory. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Acked-by: Arnd Bergmann Signed-off-by: Vinod Koul --- MAINTAINERS | 3 +- drivers/dma/Kconfig | 20 +- drivers/dma/Makefile | 2 +- drivers/dma/dw/Kconfig | 23 + drivers/dma/dw/Makefile | 1 + drivers/dma/dw/dw_dmac.c | 1969 +++++++++++++++++++++++++++++++++++++++++ drivers/dma/dw/dw_dmac_regs.h | 311 +++++++ drivers/dma/dw_dmac.c | 1969 ----------------------------------------- drivers/dma/dw_dmac_regs.h | 311 ------- 9 files changed, 2307 insertions(+), 2302 deletions(-) create mode 100644 drivers/dma/dw/Kconfig create mode 100644 drivers/dma/dw/Makefile create mode 100644 drivers/dma/dw/dw_dmac.c create mode 100644 drivers/dma/dw/dw_dmac_regs.h delete mode 100644 drivers/dma/dw_dmac.c delete mode 100644 drivers/dma/dw_dmac_regs.h diff --git a/MAINTAINERS b/MAINTAINERS index 250dc970c62d..e325809fd412 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6990,8 +6990,7 @@ SYNOPSYS DESIGNWARE DMAC DRIVER M: Viresh Kumar S: Maintained F: include/linux/dw_dmac.h -F: drivers/dma/dw_dmac_regs.h -F: drivers/dma/dw_dmac.c +F: drivers/dma/dw/ SYNOPSYS DESIGNWARE MMC/SD/SDIO DRIVER M: Seungwon Jeon diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index e9924898043a..146a1d864a71 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -79,25 +79,7 @@ config INTEL_IOP_ADMA help Enable support for the Intel(R) IOP Series RAID engines. -config DW_DMAC - tristate "Synopsys DesignWare AHB DMA support" - depends on GENERIC_HARDIRQS - select DMA_ENGINE - default y if CPU_AT32AP7000 - help - Support the Synopsys DesignWare AHB DMA controller. This - can be integrated in chips such as the Atmel AT32ap7000. - -config DW_DMAC_BIG_ENDIAN_IO - bool "Use big endian I/O register access" - default y if AVR32 - depends on DW_DMAC - help - Say yes here to use big endian I/O access when reading and writing - to the DMA controller registers. This is needed on some platforms, - like the Atmel AVR32 architecture. - - If unsure, use the default setting. +source "drivers/dma/dw/Kconfig" config AT_HDMAC tristate "Atmel AHB DMA support" diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index a2b0df591f95..ac44ca0d468a 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -15,7 +15,7 @@ obj-$(CONFIG_FSL_DMA) += fsldma.o obj-$(CONFIG_MPC512X_DMA) += mpc512x_dma.o obj-$(CONFIG_PPC_BESTCOMM) += bestcomm/ obj-$(CONFIG_MV_XOR) += mv_xor.o -obj-$(CONFIG_DW_DMAC) += dw_dmac.o +obj-$(CONFIG_DW_DMAC) += dw/ obj-$(CONFIG_AT_HDMAC) += at_hdmac.o obj-$(CONFIG_MX3_IPU) += ipu/ obj-$(CONFIG_TXX9_DMAC) += txx9dmac.o diff --git a/drivers/dma/dw/Kconfig b/drivers/dma/dw/Kconfig new file mode 100644 index 000000000000..38a215af5ccc --- /dev/null +++ b/drivers/dma/dw/Kconfig @@ -0,0 +1,23 @@ +# +# DMA engine configuration for dw +# + +config DW_DMAC + tristate "Synopsys DesignWare AHB DMA support" + depends on GENERIC_HARDIRQS + select DMA_ENGINE + default y if CPU_AT32AP7000 + help + Support the Synopsys DesignWare AHB DMA controller. This + can be integrated in chips such as the Atmel AT32ap7000. + +config DW_DMAC_BIG_ENDIAN_IO + bool "Use big endian I/O register access" + default y if AVR32 + depends on DW_DMAC + help + Say yes here to use big endian I/O access when reading and writing + to the DMA controller registers. This is needed on some platforms, + like the Atmel AVR32 architecture. + + If unsure, use the default setting. diff --git a/drivers/dma/dw/Makefile b/drivers/dma/dw/Makefile new file mode 100644 index 000000000000..dd8d9936beef --- /dev/null +++ b/drivers/dma/dw/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_DW_DMAC) += dw_dmac.o diff --git a/drivers/dma/dw/dw_dmac.c b/drivers/dma/dw/dw_dmac.c new file mode 100644 index 000000000000..15f3f4f79c10 --- /dev/null +++ b/drivers/dma/dw/dw_dmac.c @@ -0,0 +1,1969 @@ +/* + * Core driver for the Synopsys DesignWare DMA Controller + * + * Copyright (C) 2007-2008 Atmel Corporation + * Copyright (C) 2010-2011 ST Microelectronics + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../dmaengine.h" +#include "dw_dmac_regs.h" + +/* + * This supports the Synopsys "DesignWare AHB Central DMA Controller", + * (DW_ahb_dmac) which is used with various AMBA 2.0 systems (not all + * of which use ARM any more). See the "Databook" from Synopsys for + * information beyond what licensees probably provide. + * + * The driver has currently been tested only with the Atmel AT32AP7000, + * which does not support descriptor writeback. + */ + +static inline unsigned int dwc_get_dms(struct dw_dma_slave *slave) +{ + return slave ? slave->dst_master : 0; +} + +static inline unsigned int dwc_get_sms(struct dw_dma_slave *slave) +{ + return slave ? slave->src_master : 1; +} + +static inline void dwc_set_masters(struct dw_dma_chan *dwc) +{ + struct dw_dma *dw = to_dw_dma(dwc->chan.device); + struct dw_dma_slave *dws = dwc->chan.private; + unsigned char mmax = dw->nr_masters - 1; + + if (dwc->request_line == ~0) { + dwc->src_master = min_t(unsigned char, mmax, dwc_get_sms(dws)); + dwc->dst_master = min_t(unsigned char, mmax, dwc_get_dms(dws)); + } +} + +#define DWC_DEFAULT_CTLLO(_chan) ({ \ + struct dw_dma_chan *_dwc = to_dw_dma_chan(_chan); \ + struct dma_slave_config *_sconfig = &_dwc->dma_sconfig; \ + bool _is_slave = is_slave_direction(_dwc->direction); \ + u8 _smsize = _is_slave ? _sconfig->src_maxburst : \ + DW_DMA_MSIZE_16; \ + u8 _dmsize = _is_slave ? _sconfig->dst_maxburst : \ + DW_DMA_MSIZE_16; \ + \ + (DWC_CTLL_DST_MSIZE(_dmsize) \ + | DWC_CTLL_SRC_MSIZE(_smsize) \ + | DWC_CTLL_LLP_D_EN \ + | DWC_CTLL_LLP_S_EN \ + | DWC_CTLL_DMS(_dwc->dst_master) \ + | DWC_CTLL_SMS(_dwc->src_master)); \ + }) + +/* + * Number of descriptors to allocate for each channel. This should be + * made configurable somehow; preferably, the clients (at least the + * ones using slave transfers) should be able to give us a hint. + */ +#define NR_DESCS_PER_CHANNEL 64 + +/*----------------------------------------------------------------------*/ + +static struct device *chan2dev(struct dma_chan *chan) +{ + return &chan->dev->device; +} +static struct device *chan2parent(struct dma_chan *chan) +{ + return chan->dev->device.parent; +} + +static struct dw_desc *dwc_first_active(struct dw_dma_chan *dwc) +{ + return to_dw_desc(dwc->active_list.next); +} + +static struct dw_desc *dwc_desc_get(struct dw_dma_chan *dwc) +{ + struct dw_desc *desc, *_desc; + struct dw_desc *ret = NULL; + unsigned int i = 0; + unsigned long flags; + + spin_lock_irqsave(&dwc->lock, flags); + list_for_each_entry_safe(desc, _desc, &dwc->free_list, desc_node) { + i++; + if (async_tx_test_ack(&desc->txd)) { + list_del(&desc->desc_node); + ret = desc; + break; + } + dev_dbg(chan2dev(&dwc->chan), "desc %p not ACKed\n", desc); + } + spin_unlock_irqrestore(&dwc->lock, flags); + + dev_vdbg(chan2dev(&dwc->chan), "scanned %u descriptors on freelist\n", i); + + return ret; +} + +/* + * Move a descriptor, including any children, to the free list. + * `desc' must not be on any lists. + */ +static void dwc_desc_put(struct dw_dma_chan *dwc, struct dw_desc *desc) +{ + unsigned long flags; + + if (desc) { + struct dw_desc *child; + + spin_lock_irqsave(&dwc->lock, flags); + list_for_each_entry(child, &desc->tx_list, desc_node) + dev_vdbg(chan2dev(&dwc->chan), + "moving child desc %p to freelist\n", + child); + list_splice_init(&desc->tx_list, &dwc->free_list); + dev_vdbg(chan2dev(&dwc->chan), "moving desc %p to freelist\n", desc); + list_add(&desc->desc_node, &dwc->free_list); + spin_unlock_irqrestore(&dwc->lock, flags); + } +} + +static void dwc_initialize(struct dw_dma_chan *dwc) +{ + struct dw_dma *dw = to_dw_dma(dwc->chan.device); + struct dw_dma_slave *dws = dwc->chan.private; + u32 cfghi = DWC_CFGH_FIFO_MODE; + u32 cfglo = DWC_CFGL_CH_PRIOR(dwc->priority); + + if (dwc->initialized == true) + return; + + if (dws) { + /* + * We need controller-specific data to set up slave + * transfers. + */ + BUG_ON(!dws->dma_dev || dws->dma_dev != dw->dma.dev); + + cfghi = dws->cfg_hi; + cfglo |= dws->cfg_lo & ~DWC_CFGL_CH_PRIOR_MASK; + } else { + if (dwc->direction == DMA_MEM_TO_DEV) + cfghi = DWC_CFGH_DST_PER(dwc->request_line); + else if (dwc->direction == DMA_DEV_TO_MEM) + cfghi = DWC_CFGH_SRC_PER(dwc->request_line); + } + + channel_writel(dwc, CFG_LO, cfglo); + channel_writel(dwc, CFG_HI, cfghi); + + /* Enable interrupts */ + channel_set_bit(dw, MASK.XFER, dwc->mask); + channel_set_bit(dw, MASK.ERROR, dwc->mask); + + dwc->initialized = true; +} + +/*----------------------------------------------------------------------*/ + +static inline unsigned int dwc_fast_fls(unsigned long long v) +{ + /* + * We can be a lot more clever here, but this should take care + * of the most common optimization. + */ + if (!(v & 7)) + return 3; + else if (!(v & 3)) + return 2; + else if (!(v & 1)) + return 1; + return 0; +} + +static inline void dwc_dump_chan_regs(struct dw_dma_chan *dwc) +{ + dev_err(chan2dev(&dwc->chan), + " SAR: 0x%x DAR: 0x%x LLP: 0x%x CTL: 0x%x:%08x\n", + channel_readl(dwc, SAR), + channel_readl(dwc, DAR), + channel_readl(dwc, LLP), + channel_readl(dwc, CTL_HI), + channel_readl(dwc, CTL_LO)); +} + +static inline void dwc_chan_disable(struct dw_dma *dw, struct dw_dma_chan *dwc) +{ + channel_clear_bit(dw, CH_EN, dwc->mask); + while (dma_readl(dw, CH_EN) & dwc->mask) + cpu_relax(); +} + +/*----------------------------------------------------------------------*/ + +/* Perform single block transfer */ +static inline void dwc_do_single_block(struct dw_dma_chan *dwc, + struct dw_desc *desc) +{ + struct dw_dma *dw = to_dw_dma(dwc->chan.device); + u32 ctllo; + + /* Software emulation of LLP mode relies on interrupts to continue + * multi block transfer. */ + ctllo = desc->lli.ctllo | DWC_CTLL_INT_EN; + + channel_writel(dwc, SAR, desc->lli.sar); + channel_writel(dwc, DAR, desc->lli.dar); + channel_writel(dwc, CTL_LO, ctllo); + channel_writel(dwc, CTL_HI, desc->lli.ctlhi); + channel_set_bit(dw, CH_EN, dwc->mask); + + /* Move pointer to next descriptor */ + dwc->tx_node_active = dwc->tx_node_active->next; +} + +/* Called with dwc->lock held and bh disabled */ +static void dwc_dostart(struct dw_dma_chan *dwc, struct dw_desc *first) +{ + struct dw_dma *dw = to_dw_dma(dwc->chan.device); + unsigned long was_soft_llp; + + /* ASSERT: channel is idle */ + if (dma_readl(dw, CH_EN) & dwc->mask) { + dev_err(chan2dev(&dwc->chan), + "BUG: Attempted to start non-idle channel\n"); + dwc_dump_chan_regs(dwc); + + /* The tasklet will hopefully advance the queue... */ + return; + } + + if (dwc->nollp) { + was_soft_llp = test_and_set_bit(DW_DMA_IS_SOFT_LLP, + &dwc->flags); + if (was_soft_llp) { + dev_err(chan2dev(&dwc->chan), + "BUG: Attempted to start new LLP transfer " + "inside ongoing one\n"); + return; + } + + dwc_initialize(dwc); + + dwc->residue = first->total_len; + dwc->tx_node_active = &first->tx_list; + + /* Submit first block */ + dwc_do_single_block(dwc, first); + + return; + } + + dwc_initialize(dwc); + + channel_writel(dwc, LLP, first->txd.phys); + channel_writel(dwc, CTL_LO, + DWC_CTLL_LLP_D_EN | DWC_CTLL_LLP_S_EN); + channel_writel(dwc, CTL_HI, 0); + channel_set_bit(dw, CH_EN, dwc->mask); +} + +/*----------------------------------------------------------------------*/ + +static void +dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc, + bool callback_required) +{ + dma_async_tx_callback callback = NULL; + void *param = NULL; + struct dma_async_tx_descriptor *txd = &desc->txd; + struct dw_desc *child; + unsigned long flags; + + dev_vdbg(chan2dev(&dwc->chan), "descriptor %u complete\n", txd->cookie); + + spin_lock_irqsave(&dwc->lock, flags); + dma_cookie_complete(txd); + if (callback_required) { + callback = txd->callback; + param = txd->callback_param; + } + + /* async_tx_ack */ + list_for_each_entry(child, &desc->tx_list, desc_node) + async_tx_ack(&child->txd); + async_tx_ack(&desc->txd); + + list_splice_init(&desc->tx_list, &dwc->free_list); + list_move(&desc->desc_node, &dwc->free_list); + + if (!is_slave_direction(dwc->direction)) { + struct device *parent = chan2parent(&dwc->chan); + if (!(txd->flags & DMA_COMPL_SKIP_DEST_UNMAP)) { + if (txd->flags & DMA_COMPL_DEST_UNMAP_SINGLE) + dma_unmap_single(parent, desc->lli.dar, + desc->total_len, DMA_FROM_DEVICE); + else + dma_unmap_page(parent, desc->lli.dar, + desc->total_len, DMA_FROM_DEVICE); + } + if (!(txd->flags & DMA_COMPL_SKIP_SRC_UNMAP)) { + if (txd->flags & DMA_COMPL_SRC_UNMAP_SINGLE) + dma_unmap_single(parent, desc->lli.sar, + desc->total_len, DMA_TO_DEVICE); + else + dma_unmap_page(parent, desc->lli.sar, + desc->total_len, DMA_TO_DEVICE); + } + } + + spin_unlock_irqrestore(&dwc->lock, flags); + + if (callback) + callback(param); +} + +static void dwc_complete_all(struct dw_dma *dw, struct dw_dma_chan *dwc) +{ + struct dw_desc *desc, *_desc; + LIST_HEAD(list); + unsigned long flags; + + spin_lock_irqsave(&dwc->lock, flags); + if (dma_readl(dw, CH_EN) & dwc->mask) { + dev_err(chan2dev(&dwc->chan), + "BUG: XFER bit set, but channel not idle!\n"); + + /* Try to continue after resetting the channel... */ + dwc_chan_disable(dw, dwc); + } + + /* + * Submit queued descriptors ASAP, i.e. before we go through + * the completed ones. + */ + list_splice_init(&dwc->active_list, &list); + if (!list_empty(&dwc->queue)) { + list_move(dwc->queue.next, &dwc->active_list); + dwc_dostart(dwc, dwc_first_active(dwc)); + } + + spin_unlock_irqrestore(&dwc->lock, flags); + + list_for_each_entry_safe(desc, _desc, &list, desc_node) + dwc_descriptor_complete(dwc, desc, true); +} + +/* Returns how many bytes were already received from source */ +static inline u32 dwc_get_sent(struct dw_dma_chan *dwc) +{ + u32 ctlhi = channel_readl(dwc, CTL_HI); + u32 ctllo = channel_readl(dwc, CTL_LO); + + return (ctlhi & DWC_CTLH_BLOCK_TS_MASK) * (1 << (ctllo >> 4 & 7)); +} + +static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) +{ + dma_addr_t llp; + struct dw_desc *desc, *_desc; + struct dw_desc *child; + u32 status_xfer; + unsigned long flags; + + spin_lock_irqsave(&dwc->lock, flags); + llp = channel_readl(dwc, LLP); + status_xfer = dma_readl(dw, RAW.XFER); + + if (status_xfer & dwc->mask) { + /* Everything we've submitted is done */ + dma_writel(dw, CLEAR.XFER, dwc->mask); + + if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags)) { + struct list_head *head, *active = dwc->tx_node_active; + + /* + * We are inside first active descriptor. + * Otherwise something is really wrong. + */ + desc = dwc_first_active(dwc); + + head = &desc->tx_list; + if (active != head) { + /* Update desc to reflect last sent one */ + if (active != head->next) + desc = to_dw_desc(active->prev); + + dwc->residue -= desc->len; + + child = to_dw_desc(active); + + /* Submit next block */ + dwc_do_single_block(dwc, child); + + spin_unlock_irqrestore(&dwc->lock, flags); + return; + } + + /* We are done here */ + clear_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags); + } + + dwc->residue = 0; + + spin_unlock_irqrestore(&dwc->lock, flags); + + dwc_complete_all(dw, dwc); + return; + } + + if (list_empty(&dwc->active_list)) { + dwc->residue = 0; + spin_unlock_irqrestore(&dwc->lock, flags); + return; + } + + if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags)) { + dev_vdbg(chan2dev(&dwc->chan), "%s: soft LLP mode\n", __func__); + spin_unlock_irqrestore(&dwc->lock, flags); + return; + } + + dev_vdbg(chan2dev(&dwc->chan), "%s: llp=0x%llx\n", __func__, + (unsigned long long)llp); + + list_for_each_entry_safe(desc, _desc, &dwc->active_list, desc_node) { + /* Initial residue value */ + dwc->residue = desc->total_len; + + /* Check first descriptors addr */ + if (desc->txd.phys == llp) { + spin_unlock_irqrestore(&dwc->lock, flags); + return; + } + + /* Check first descriptors llp */ + if (desc->lli.llp == llp) { + /* This one is currently in progress */ + dwc->residue -= dwc_get_sent(dwc); + spin_unlock_irqrestore(&dwc->lock, flags); + return; + } + + dwc->residue -= desc->len; + list_for_each_entry(child, &desc->tx_list, desc_node) { + if (child->lli.llp == llp) { + /* Currently in progress */ + dwc->residue -= dwc_get_sent(dwc); + spin_unlock_irqrestore(&dwc->lock, flags); + return; + } + dwc->residue -= child->len; + } + + /* + * No descriptors so far seem to be in progress, i.e. + * this one must be done. + */ + spin_unlock_irqrestore(&dwc->lock, flags); + dwc_descriptor_complete(dwc, desc, true); + spin_lock_irqsave(&dwc->lock, flags); + } + + dev_err(chan2dev(&dwc->chan), + "BUG: All descriptors done, but channel not idle!\n"); + + /* Try to continue after resetting the channel... */ + dwc_chan_disable(dw, dwc); + + if (!list_empty(&dwc->queue)) { + list_move(dwc->queue.next, &dwc->active_list); + dwc_dostart(dwc, dwc_first_active(dwc)); + } + spin_unlock_irqrestore(&dwc->lock, flags); +} + +static inline void dwc_dump_lli(struct dw_dma_chan *dwc, struct dw_lli *lli) +{ + dev_crit(chan2dev(&dwc->chan), " desc: s0x%x d0x%x l0x%x c0x%x:%x\n", + lli->sar, lli->dar, lli->llp, lli->ctlhi, lli->ctllo); +} + +static void dwc_handle_error(struct dw_dma *dw, struct dw_dma_chan *dwc) +{ + struct dw_desc *bad_desc; + struct dw_desc *child; + unsigned long flags; + + dwc_scan_descriptors(dw, dwc); + + spin_lock_irqsave(&dwc->lock, flags); + + /* + * The descriptor currently at the head of the active list is + * borked. Since we don't have any way to report errors, we'll + * just have to scream loudly and try to carry on. + */ + bad_desc = dwc_first_active(dwc); + list_del_init(&bad_desc->desc_node); + list_move(dwc->queue.next, dwc->active_list.prev); + + /* Clear the error flag and try to restart the controller */ + dma_writel(dw, CLEAR.ERROR, dwc->mask); + if (!list_empty(&dwc->active_list)) + dwc_dostart(dwc, dwc_first_active(dwc)); + + /* + * WARN may seem harsh, but since this only happens + * when someone submits a bad physical address in a + * descriptor, we should consider ourselves lucky that the + * controller flagged an error instead of scribbling over + * random memory locations. + */ + dev_WARN(chan2dev(&dwc->chan), "Bad descriptor submitted for DMA!\n" + " cookie: %d\n", bad_desc->txd.cookie); + dwc_dump_lli(dwc, &bad_desc->lli); + list_for_each_entry(child, &bad_desc->tx_list, desc_node) + dwc_dump_lli(dwc, &child->lli); + + spin_unlock_irqrestore(&dwc->lock, flags); + + /* Pretend the descriptor completed successfully */ + dwc_descriptor_complete(dwc, bad_desc, true); +} + +/* --------------------- Cyclic DMA API extensions -------------------- */ + +dma_addr_t dw_dma_get_src_addr(struct dma_chan *chan) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + return channel_readl(dwc, SAR); +} +EXPORT_SYMBOL(dw_dma_get_src_addr); + +dma_addr_t dw_dma_get_dst_addr(struct dma_chan *chan) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + return channel_readl(dwc, DAR); +} +EXPORT_SYMBOL(dw_dma_get_dst_addr); + +/* Called with dwc->lock held and all DMAC interrupts disabled */ +static void dwc_handle_cyclic(struct dw_dma *dw, struct dw_dma_chan *dwc, + u32 status_err, u32 status_xfer) +{ + unsigned long flags; + + if (dwc->mask) { + void (*callback)(void *param); + void *callback_param; + + dev_vdbg(chan2dev(&dwc->chan), "new cyclic period llp 0x%08x\n", + channel_readl(dwc, LLP)); + + callback = dwc->cdesc->period_callback; + callback_param = dwc->cdesc->period_callback_param; + + if (callback) + callback(callback_param); + } + + /* + * Error and transfer complete are highly unlikely, and will most + * likely be due to a configuration error by the user. + */ + if (unlikely(status_err & dwc->mask) || + unlikely(status_xfer & dwc->mask)) { + int i; + + dev_err(chan2dev(&dwc->chan), "cyclic DMA unexpected %s " + "interrupt, stopping DMA transfer\n", + status_xfer ? "xfer" : "error"); + + spin_lock_irqsave(&dwc->lock, flags); + + dwc_dump_chan_regs(dwc); + + dwc_chan_disable(dw, dwc); + + /* Make sure DMA does not restart by loading a new list */ + channel_writel(dwc, LLP, 0); + channel_writel(dwc, CTL_LO, 0); + channel_writel(dwc, CTL_HI, 0); + + dma_writel(dw, CLEAR.ERROR, dwc->mask); + dma_writel(dw, CLEAR.XFER, dwc->mask); + + for (i = 0; i < dwc->cdesc->periods; i++) + dwc_dump_lli(dwc, &dwc->cdesc->desc[i]->lli); + + spin_unlock_irqrestore(&dwc->lock, flags); + } +} + +/* ------------------------------------------------------------------------- */ + +static void dw_dma_tasklet(unsigned long data) +{ + struct dw_dma *dw = (struct dw_dma *)data; + struct dw_dma_chan *dwc; + u32 status_xfer; + u32 status_err; + int i; + + status_xfer = dma_readl(dw, RAW.XFER); + status_err = dma_readl(dw, RAW.ERROR); + + dev_vdbg(dw->dma.dev, "%s: status_err=%x\n", __func__, status_err); + + for (i = 0; i < dw->dma.chancnt; i++) { + dwc = &dw->chan[i]; + if (test_bit(DW_DMA_IS_CYCLIC, &dwc->flags)) + dwc_handle_cyclic(dw, dwc, status_err, status_xfer); + else if (status_err & (1 << i)) + dwc_handle_error(dw, dwc); + else if (status_xfer & (1 << i)) + dwc_scan_descriptors(dw, dwc); + } + + /* + * Re-enable interrupts. + */ + channel_set_bit(dw, MASK.XFER, dw->all_chan_mask); + channel_set_bit(dw, MASK.ERROR, dw->all_chan_mask); +} + +static irqreturn_t dw_dma_interrupt(int irq, void *dev_id) +{ + struct dw_dma *dw = dev_id; + u32 status; + + dev_vdbg(dw->dma.dev, "%s: status=0x%x\n", __func__, + dma_readl(dw, STATUS_INT)); + + /* + * Just disable the interrupts. We'll turn them back on in the + * softirq handler. + */ + channel_clear_bit(dw, MASK.XFER, dw->all_chan_mask); + channel_clear_bit(dw, MASK.ERROR, dw->all_chan_mask); + + status = dma_readl(dw, STATUS_INT); + if (status) { + dev_err(dw->dma.dev, + "BUG: Unexpected interrupts pending: 0x%x\n", + status); + + /* Try to recover */ + channel_clear_bit(dw, MASK.XFER, (1 << 8) - 1); + channel_clear_bit(dw, MASK.SRC_TRAN, (1 << 8) - 1); + channel_clear_bit(dw, MASK.DST_TRAN, (1 << 8) - 1); + channel_clear_bit(dw, MASK.ERROR, (1 << 8) - 1); + } + + tasklet_schedule(&dw->tasklet); + + return IRQ_HANDLED; +} + +/*----------------------------------------------------------------------*/ + +static dma_cookie_t dwc_tx_submit(struct dma_async_tx_descriptor *tx) +{ + struct dw_desc *desc = txd_to_dw_desc(tx); + struct dw_dma_chan *dwc = to_dw_dma_chan(tx->chan); + dma_cookie_t cookie; + unsigned long flags; + + spin_lock_irqsave(&dwc->lock, flags); + cookie = dma_cookie_assign(tx); + + /* + * REVISIT: We should attempt to chain as many descriptors as + * possible, perhaps even appending to those already submitted + * for DMA. But this is hard to do in a race-free manner. + */ + if (list_empty(&dwc->active_list)) { + dev_vdbg(chan2dev(tx->chan), "%s: started %u\n", __func__, + desc->txd.cookie); + list_add_tail(&desc->desc_node, &dwc->active_list); + dwc_dostart(dwc, dwc_first_active(dwc)); + } else { + dev_vdbg(chan2dev(tx->chan), "%s: queued %u\n", __func__, + desc->txd.cookie); + + list_add_tail(&desc->desc_node, &dwc->queue); + } + + spin_unlock_irqrestore(&dwc->lock, flags); + + return cookie; +} + +static struct dma_async_tx_descriptor * +dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, + size_t len, unsigned long flags) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_dma *dw = to_dw_dma(chan->device); + struct dw_desc *desc; + struct dw_desc *first; + struct dw_desc *prev; + size_t xfer_count; + size_t offset; + unsigned int src_width; + unsigned int dst_width; + unsigned int data_width; + u32 ctllo; + + dev_vdbg(chan2dev(chan), + "%s: d0x%llx s0x%llx l0x%zx f0x%lx\n", __func__, + (unsigned long long)dest, (unsigned long long)src, + len, flags); + + if (unlikely(!len)) { + dev_dbg(chan2dev(chan), "%s: length is zero!\n", __func__); + return NULL; + } + + dwc->direction = DMA_MEM_TO_MEM; + + data_width = min_t(unsigned int, dw->data_width[dwc->src_master], + dw->data_width[dwc->dst_master]); + + src_width = dst_width = min_t(unsigned int, data_width, + dwc_fast_fls(src | dest | len)); + + ctllo = DWC_DEFAULT_CTLLO(chan) + | DWC_CTLL_DST_WIDTH(dst_width) + | DWC_CTLL_SRC_WIDTH(src_width) + | DWC_CTLL_DST_INC + | DWC_CTLL_SRC_INC + | DWC_CTLL_FC_M2M; + prev = first = NULL; + + for (offset = 0; offset < len; offset += xfer_count << src_width) { + xfer_count = min_t(size_t, (len - offset) >> src_width, + dwc->block_size); + + desc = dwc_desc_get(dwc); + if (!desc) + goto err_desc_get; + + desc->lli.sar = src + offset; + desc->lli.dar = dest + offset; + desc->lli.ctllo = ctllo; + desc->lli.ctlhi = xfer_count; + desc->len = xfer_count << src_width; + + if (!first) { + first = desc; + } else { + prev->lli.llp = desc->txd.phys; + list_add_tail(&desc->desc_node, + &first->tx_list); + } + prev = desc; + } + + if (flags & DMA_PREP_INTERRUPT) + /* Trigger interrupt after last block */ + prev->lli.ctllo |= DWC_CTLL_INT_EN; + + prev->lli.llp = 0; + first->txd.flags = flags; + first->total_len = len; + + return &first->txd; + +err_desc_get: + dwc_desc_put(dwc, first); + return NULL; +} + +static struct dma_async_tx_descriptor * +dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, + unsigned int sg_len, enum dma_transfer_direction direction, + unsigned long flags, void *context) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_dma *dw = to_dw_dma(chan->device); + struct dma_slave_config *sconfig = &dwc->dma_sconfig; + struct dw_desc *prev; + struct dw_desc *first; + u32 ctllo; + dma_addr_t reg; + unsigned int reg_width; + unsigned int mem_width; + unsigned int data_width; + unsigned int i; + struct scatterlist *sg; + size_t total_len = 0; + + dev_vdbg(chan2dev(chan), "%s\n", __func__); + + if (unlikely(!is_slave_direction(direction) || !sg_len)) + return NULL; + + dwc->direction = direction; + + prev = first = NULL; + + switch (direction) { + case DMA_MEM_TO_DEV: + reg_width = __fls(sconfig->dst_addr_width); + reg = sconfig->dst_addr; + ctllo = (DWC_DEFAULT_CTLLO(chan) + | DWC_CTLL_DST_WIDTH(reg_width) + | DWC_CTLL_DST_FIX + | DWC_CTLL_SRC_INC); + + ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_M2P) : + DWC_CTLL_FC(DW_DMA_FC_D_M2P); + + data_width = dw->data_width[dwc->src_master]; + + for_each_sg(sgl, sg, sg_len, i) { + struct dw_desc *desc; + u32 len, dlen, mem; + + mem = sg_dma_address(sg); + len = sg_dma_len(sg); + + mem_width = min_t(unsigned int, + data_width, dwc_fast_fls(mem | len)); + +slave_sg_todev_fill_desc: + desc = dwc_desc_get(dwc); + if (!desc) { + dev_err(chan2dev(chan), + "not enough descriptors available\n"); + goto err_desc_get; + } + + desc->lli.sar = mem; + desc->lli.dar = reg; + desc->lli.ctllo = ctllo | DWC_CTLL_SRC_WIDTH(mem_width); + if ((len >> mem_width) > dwc->block_size) { + dlen = dwc->block_size << mem_width; + mem += dlen; + len -= dlen; + } else { + dlen = len; + len = 0; + } + + desc->lli.ctlhi = dlen >> mem_width; + desc->len = dlen; + + if (!first) { + first = desc; + } else { + prev->lli.llp = desc->txd.phys; + list_add_tail(&desc->desc_node, + &first->tx_list); + } + prev = desc; + total_len += dlen; + + if (len) + goto slave_sg_todev_fill_desc; + } + break; + case DMA_DEV_TO_MEM: + reg_width = __fls(sconfig->src_addr_width); + reg = sconfig->src_addr; + ctllo = (DWC_DEFAULT_CTLLO(chan) + | DWC_CTLL_SRC_WIDTH(reg_width) + | DWC_CTLL_DST_INC + | DWC_CTLL_SRC_FIX); + + ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_P2M) : + DWC_CTLL_FC(DW_DMA_FC_D_P2M); + + data_width = dw->data_width[dwc->dst_master]; + + for_each_sg(sgl, sg, sg_len, i) { + struct dw_desc *desc; + u32 len, dlen, mem; + + mem = sg_dma_address(sg); + len = sg_dma_len(sg); + + mem_width = min_t(unsigned int, + data_width, dwc_fast_fls(mem | len)); + +slave_sg_fromdev_fill_desc: + desc = dwc_desc_get(dwc); + if (!desc) { + dev_err(chan2dev(chan), + "not enough descriptors available\n"); + goto err_desc_get; + } + + desc->lli.sar = reg; + desc->lli.dar = mem; + desc->lli.ctllo = ctllo | DWC_CTLL_DST_WIDTH(mem_width); + if ((len >> reg_width) > dwc->block_size) { + dlen = dwc->block_size << reg_width; + mem += dlen; + len -= dlen; + } else { + dlen = len; + len = 0; + } + desc->lli.ctlhi = dlen >> reg_width; + desc->len = dlen; + + if (!first) { + first = desc; + } else { + prev->lli.llp = desc->txd.phys; + list_add_tail(&desc->desc_node, + &first->tx_list); + } + prev = desc; + total_len += dlen; + + if (len) + goto slave_sg_fromdev_fill_desc; + } + break; + default: + return NULL; + } + + if (flags & DMA_PREP_INTERRUPT) + /* Trigger interrupt after last block */ + prev->lli.ctllo |= DWC_CTLL_INT_EN; + + prev->lli.llp = 0; + first->total_len = total_len; + + return &first->txd; + +err_desc_get: + dwc_desc_put(dwc, first); + return NULL; +} + +/* + * Fix sconfig's burst size according to dw_dmac. We need to convert them as: + * 1 -> 0, 4 -> 1, 8 -> 2, 16 -> 3. + * + * NOTE: burst size 2 is not supported by controller. + * + * This can be done by finding least significant bit set: n & (n - 1) + */ +static inline void convert_burst(u32 *maxburst) +{ + if (*maxburst > 1) + *maxburst = fls(*maxburst) - 2; + else + *maxburst = 0; +} + +static int +set_runtime_config(struct dma_chan *chan, struct dma_slave_config *sconfig) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + + /* Check if chan will be configured for slave transfers */ + if (!is_slave_direction(sconfig->direction)) + return -EINVAL; + + memcpy(&dwc->dma_sconfig, sconfig, sizeof(*sconfig)); + dwc->direction = sconfig->direction; + + /* Take the request line from slave_id member */ + if (dwc->request_line == ~0) + dwc->request_line = sconfig->slave_id; + + convert_burst(&dwc->dma_sconfig.src_maxburst); + convert_burst(&dwc->dma_sconfig.dst_maxburst); + + return 0; +} + +static inline void dwc_chan_pause(struct dw_dma_chan *dwc) +{ + u32 cfglo = channel_readl(dwc, CFG_LO); + unsigned int count = 20; /* timeout iterations */ + + channel_writel(dwc, CFG_LO, cfglo | DWC_CFGL_CH_SUSP); + while (!(channel_readl(dwc, CFG_LO) & DWC_CFGL_FIFO_EMPTY) && count--) + udelay(2); + + dwc->paused = true; +} + +static inline void dwc_chan_resume(struct dw_dma_chan *dwc) +{ + u32 cfglo = channel_readl(dwc, CFG_LO); + + channel_writel(dwc, CFG_LO, cfglo & ~DWC_CFGL_CH_SUSP); + + dwc->paused = false; +} + +static int dwc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, + unsigned long arg) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_dma *dw = to_dw_dma(chan->device); + struct dw_desc *desc, *_desc; + unsigned long flags; + LIST_HEAD(list); + + if (cmd == DMA_PAUSE) { + spin_lock_irqsave(&dwc->lock, flags); + + dwc_chan_pause(dwc); + + spin_unlock_irqrestore(&dwc->lock, flags); + } else if (cmd == DMA_RESUME) { + if (!dwc->paused) + return 0; + + spin_lock_irqsave(&dwc->lock, flags); + + dwc_chan_resume(dwc); + + spin_unlock_irqrestore(&dwc->lock, flags); + } else if (cmd == DMA_TERMINATE_ALL) { + spin_lock_irqsave(&dwc->lock, flags); + + clear_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags); + + dwc_chan_disable(dw, dwc); + + dwc_chan_resume(dwc); + + /* active_list entries will end up before queued entries */ + list_splice_init(&dwc->queue, &list); + list_splice_init(&dwc->active_list, &list); + + spin_unlock_irqrestore(&dwc->lock, flags); + + /* Flush all pending and queued descriptors */ + list_for_each_entry_safe(desc, _desc, &list, desc_node) + dwc_descriptor_complete(dwc, desc, false); + } else if (cmd == DMA_SLAVE_CONFIG) { + return set_runtime_config(chan, (struct dma_slave_config *)arg); + } else { + return -ENXIO; + } + + return 0; +} + +static inline u32 dwc_get_residue(struct dw_dma_chan *dwc) +{ + unsigned long flags; + u32 residue; + + spin_lock_irqsave(&dwc->lock, flags); + + residue = dwc->residue; + if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags) && residue) + residue -= dwc_get_sent(dwc); + + spin_unlock_irqrestore(&dwc->lock, flags); + return residue; +} + +static enum dma_status +dwc_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, + struct dma_tx_state *txstate) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + enum dma_status ret; + + ret = dma_cookie_status(chan, cookie, txstate); + if (ret != DMA_SUCCESS) { + dwc_scan_descriptors(to_dw_dma(chan->device), dwc); + + ret = dma_cookie_status(chan, cookie, txstate); + } + + if (ret != DMA_SUCCESS) + dma_set_residue(txstate, dwc_get_residue(dwc)); + + if (dwc->paused) + return DMA_PAUSED; + + return ret; +} + +static void dwc_issue_pending(struct dma_chan *chan) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + + if (!list_empty(&dwc->queue)) + dwc_scan_descriptors(to_dw_dma(chan->device), dwc); +} + +static int dwc_alloc_chan_resources(struct dma_chan *chan) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_dma *dw = to_dw_dma(chan->device); + struct dw_desc *desc; + int i; + unsigned long flags; + + dev_vdbg(chan2dev(chan), "%s\n", __func__); + + /* ASSERT: channel is idle */ + if (dma_readl(dw, CH_EN) & dwc->mask) { + dev_dbg(chan2dev(chan), "DMA channel not idle?\n"); + return -EIO; + } + + dma_cookie_init(chan); + + /* + * NOTE: some controllers may have additional features that we + * need to initialize here, like "scatter-gather" (which + * doesn't mean what you think it means), and status writeback. + */ + + dwc_set_masters(dwc); + + spin_lock_irqsave(&dwc->lock, flags); + i = dwc->descs_allocated; + while (dwc->descs_allocated < NR_DESCS_PER_CHANNEL) { + dma_addr_t phys; + + spin_unlock_irqrestore(&dwc->lock, flags); + + desc = dma_pool_alloc(dw->desc_pool, GFP_ATOMIC, &phys); + if (!desc) + goto err_desc_alloc; + + memset(desc, 0, sizeof(struct dw_desc)); + + INIT_LIST_HEAD(&desc->tx_list); + dma_async_tx_descriptor_init(&desc->txd, chan); + desc->txd.tx_submit = dwc_tx_submit; + desc->txd.flags = DMA_CTRL_ACK; + desc->txd.phys = phys; + + dwc_desc_put(dwc, desc); + + spin_lock_irqsave(&dwc->lock, flags); + i = ++dwc->descs_allocated; + } + + spin_unlock_irqrestore(&dwc->lock, flags); + + dev_dbg(chan2dev(chan), "%s: allocated %d descriptors\n", __func__, i); + + return i; + +err_desc_alloc: + dev_info(chan2dev(chan), "only allocated %d descriptors\n", i); + + return i; +} + +static void dwc_free_chan_resources(struct dma_chan *chan) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_dma *dw = to_dw_dma(chan->device); + struct dw_desc *desc, *_desc; + unsigned long flags; + LIST_HEAD(list); + + dev_dbg(chan2dev(chan), "%s: descs allocated=%u\n", __func__, + dwc->descs_allocated); + + /* ASSERT: channel is idle */ + BUG_ON(!list_empty(&dwc->active_list)); + BUG_ON(!list_empty(&dwc->queue)); + BUG_ON(dma_readl(to_dw_dma(chan->device), CH_EN) & dwc->mask); + + spin_lock_irqsave(&dwc->lock, flags); + list_splice_init(&dwc->free_list, &list); + dwc->descs_allocated = 0; + dwc->initialized = false; + dwc->request_line = ~0; + + /* Disable interrupts */ + channel_clear_bit(dw, MASK.XFER, dwc->mask); + channel_clear_bit(dw, MASK.ERROR, dwc->mask); + + spin_unlock_irqrestore(&dwc->lock, flags); + + list_for_each_entry_safe(desc, _desc, &list, desc_node) { + dev_vdbg(chan2dev(chan), " freeing descriptor %p\n", desc); + dma_pool_free(dw->desc_pool, desc, desc->txd.phys); + } + + dev_vdbg(chan2dev(chan), "%s: done\n", __func__); +} + +/*----------------------------------------------------------------------*/ + +struct dw_dma_of_filter_args { + struct dw_dma *dw; + unsigned int req; + unsigned int src; + unsigned int dst; +}; + +static bool dw_dma_of_filter(struct dma_chan *chan, void *param) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_dma_of_filter_args *fargs = param; + + /* Ensure the device matches our channel */ + if (chan->device != &fargs->dw->dma) + return false; + + dwc->request_line = fargs->req; + dwc->src_master = fargs->src; + dwc->dst_master = fargs->dst; + + return true; +} + +static struct dma_chan *dw_dma_of_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + struct dw_dma *dw = ofdma->of_dma_data; + struct dw_dma_of_filter_args fargs = { + .dw = dw, + }; + dma_cap_mask_t cap; + + if (dma_spec->args_count != 3) + return NULL; + + fargs.req = dma_spec->args[0]; + fargs.src = dma_spec->args[1]; + fargs.dst = dma_spec->args[2]; + + if (WARN_ON(fargs.req >= DW_DMA_MAX_NR_REQUESTS || + fargs.src >= dw->nr_masters || + fargs.dst >= dw->nr_masters)) + return NULL; + + dma_cap_zero(cap); + dma_cap_set(DMA_SLAVE, cap); + + /* TODO: there should be a simpler way to do this */ + return dma_request_channel(cap, dw_dma_of_filter, &fargs); +} + +#ifdef CONFIG_ACPI +static bool dw_dma_acpi_filter(struct dma_chan *chan, void *param) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct acpi_dma_spec *dma_spec = param; + + if (chan->device->dev != dma_spec->dev || + chan->chan_id != dma_spec->chan_id) + return false; + + dwc->request_line = dma_spec->slave_id; + dwc->src_master = dwc_get_sms(NULL); + dwc->dst_master = dwc_get_dms(NULL); + + return true; +} + +static void dw_dma_acpi_controller_register(struct dw_dma *dw) +{ + struct device *dev = dw->dma.dev; + struct acpi_dma_filter_info *info; + int ret; + + info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); + if (!info) + return; + + dma_cap_zero(info->dma_cap); + dma_cap_set(DMA_SLAVE, info->dma_cap); + info->filter_fn = dw_dma_acpi_filter; + + ret = devm_acpi_dma_controller_register(dev, acpi_dma_simple_xlate, + info); + if (ret) + dev_err(dev, "could not register acpi_dma_controller\n"); +} +#else /* !CONFIG_ACPI */ +static inline void dw_dma_acpi_controller_register(struct dw_dma *dw) {} +#endif /* !CONFIG_ACPI */ + +/* --------------------- Cyclic DMA API extensions -------------------- */ + +/** + * dw_dma_cyclic_start - start the cyclic DMA transfer + * @chan: the DMA channel to start + * + * Must be called with soft interrupts disabled. Returns zero on success or + * -errno on failure. + */ +int dw_dma_cyclic_start(struct dma_chan *chan) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_dma *dw = to_dw_dma(dwc->chan.device); + unsigned long flags; + + if (!test_bit(DW_DMA_IS_CYCLIC, &dwc->flags)) { + dev_err(chan2dev(&dwc->chan), "missing prep for cyclic DMA\n"); + return -ENODEV; + } + + spin_lock_irqsave(&dwc->lock, flags); + + /* Assert channel is idle */ + if (dma_readl(dw, CH_EN) & dwc->mask) { + dev_err(chan2dev(&dwc->chan), + "BUG: Attempted to start non-idle channel\n"); + dwc_dump_chan_regs(dwc); + spin_unlock_irqrestore(&dwc->lock, flags); + return -EBUSY; + } + + dma_writel(dw, CLEAR.ERROR, dwc->mask); + dma_writel(dw, CLEAR.XFER, dwc->mask); + + /* Setup DMAC channel registers */ + channel_writel(dwc, LLP, dwc->cdesc->desc[0]->txd.phys); + channel_writel(dwc, CTL_LO, DWC_CTLL_LLP_D_EN | DWC_CTLL_LLP_S_EN); + channel_writel(dwc, CTL_HI, 0); + + channel_set_bit(dw, CH_EN, dwc->mask); + + spin_unlock_irqrestore(&dwc->lock, flags); + + return 0; +} +EXPORT_SYMBOL(dw_dma_cyclic_start); + +/** + * dw_dma_cyclic_stop - stop the cyclic DMA transfer + * @chan: the DMA channel to stop + * + * Must be called with soft interrupts disabled. + */ +void dw_dma_cyclic_stop(struct dma_chan *chan) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_dma *dw = to_dw_dma(dwc->chan.device); + unsigned long flags; + + spin_lock_irqsave(&dwc->lock, flags); + + dwc_chan_disable(dw, dwc); + + spin_unlock_irqrestore(&dwc->lock, flags); +} +EXPORT_SYMBOL(dw_dma_cyclic_stop); + +/** + * dw_dma_cyclic_prep - prepare the cyclic DMA transfer + * @chan: the DMA channel to prepare + * @buf_addr: physical DMA address where the buffer starts + * @buf_len: total number of bytes for the entire buffer + * @period_len: number of bytes for each period + * @direction: transfer direction, to or from device + * + * Must be called before trying to start the transfer. Returns a valid struct + * dw_cyclic_desc if successful or an ERR_PTR(-errno) if not successful. + */ +struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, + dma_addr_t buf_addr, size_t buf_len, size_t period_len, + enum dma_transfer_direction direction) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dma_slave_config *sconfig = &dwc->dma_sconfig; + struct dw_cyclic_desc *cdesc; + struct dw_cyclic_desc *retval = NULL; + struct dw_desc *desc; + struct dw_desc *last = NULL; + unsigned long was_cyclic; + unsigned int reg_width; + unsigned int periods; + unsigned int i; + unsigned long flags; + + spin_lock_irqsave(&dwc->lock, flags); + if (dwc->nollp) { + spin_unlock_irqrestore(&dwc->lock, flags); + dev_dbg(chan2dev(&dwc->chan), + "channel doesn't support LLP transfers\n"); + return ERR_PTR(-EINVAL); + } + + if (!list_empty(&dwc->queue) || !list_empty(&dwc->active_list)) { + spin_unlock_irqrestore(&dwc->lock, flags); + dev_dbg(chan2dev(&dwc->chan), + "queue and/or active list are not empty\n"); + return ERR_PTR(-EBUSY); + } + + was_cyclic = test_and_set_bit(DW_DMA_IS_CYCLIC, &dwc->flags); + spin_unlock_irqrestore(&dwc->lock, flags); + if (was_cyclic) { + dev_dbg(chan2dev(&dwc->chan), + "channel already prepared for cyclic DMA\n"); + return ERR_PTR(-EBUSY); + } + + retval = ERR_PTR(-EINVAL); + + if (unlikely(!is_slave_direction(direction))) + goto out_err; + + dwc->direction = direction; + + if (direction == DMA_MEM_TO_DEV) + reg_width = __ffs(sconfig->dst_addr_width); + else + reg_width = __ffs(sconfig->src_addr_width); + + periods = buf_len / period_len; + + /* Check for too big/unaligned periods and unaligned DMA buffer. */ + if (period_len > (dwc->block_size << reg_width)) + goto out_err; + if (unlikely(period_len & ((1 << reg_width) - 1))) + goto out_err; + if (unlikely(buf_addr & ((1 << reg_width) - 1))) + goto out_err; + + retval = ERR_PTR(-ENOMEM); + + if (periods > NR_DESCS_PER_CHANNEL) + goto out_err; + + cdesc = kzalloc(sizeof(struct dw_cyclic_desc), GFP_KERNEL); + if (!cdesc) + goto out_err; + + cdesc->desc = kzalloc(sizeof(struct dw_desc *) * periods, GFP_KERNEL); + if (!cdesc->desc) + goto out_err_alloc; + + for (i = 0; i < periods; i++) { + desc = dwc_desc_get(dwc); + if (!desc) + goto out_err_desc_get; + + switch (direction) { + case DMA_MEM_TO_DEV: + desc->lli.dar = sconfig->dst_addr; + desc->lli.sar = buf_addr + (period_len * i); + desc->lli.ctllo = (DWC_DEFAULT_CTLLO(chan) + | DWC_CTLL_DST_WIDTH(reg_width) + | DWC_CTLL_SRC_WIDTH(reg_width) + | DWC_CTLL_DST_FIX + | DWC_CTLL_SRC_INC + | DWC_CTLL_INT_EN); + + desc->lli.ctllo |= sconfig->device_fc ? + DWC_CTLL_FC(DW_DMA_FC_P_M2P) : + DWC_CTLL_FC(DW_DMA_FC_D_M2P); + + break; + case DMA_DEV_TO_MEM: + desc->lli.dar = buf_addr + (period_len * i); + desc->lli.sar = sconfig->src_addr; + desc->lli.ctllo = (DWC_DEFAULT_CTLLO(chan) + | DWC_CTLL_SRC_WIDTH(reg_width) + | DWC_CTLL_DST_WIDTH(reg_width) + | DWC_CTLL_DST_INC + | DWC_CTLL_SRC_FIX + | DWC_CTLL_INT_EN); + + desc->lli.ctllo |= sconfig->device_fc ? + DWC_CTLL_FC(DW_DMA_FC_P_P2M) : + DWC_CTLL_FC(DW_DMA_FC_D_P2M); + + break; + default: + break; + } + + desc->lli.ctlhi = (period_len >> reg_width); + cdesc->desc[i] = desc; + + if (last) + last->lli.llp = desc->txd.phys; + + last = desc; + } + + /* Let's make a cyclic list */ + last->lli.llp = cdesc->desc[0]->txd.phys; + + dev_dbg(chan2dev(&dwc->chan), "cyclic prepared buf 0x%llx len %zu " + "period %zu periods %d\n", (unsigned long long)buf_addr, + buf_len, period_len, periods); + + cdesc->periods = periods; + dwc->cdesc = cdesc; + + return cdesc; + +out_err_desc_get: + while (i--) + dwc_desc_put(dwc, cdesc->desc[i]); +out_err_alloc: + kfree(cdesc); +out_err: + clear_bit(DW_DMA_IS_CYCLIC, &dwc->flags); + return (struct dw_cyclic_desc *)retval; +} +EXPORT_SYMBOL(dw_dma_cyclic_prep); + +/** + * dw_dma_cyclic_free - free a prepared cyclic DMA transfer + * @chan: the DMA channel to free + */ +void dw_dma_cyclic_free(struct dma_chan *chan) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_dma *dw = to_dw_dma(dwc->chan.device); + struct dw_cyclic_desc *cdesc = dwc->cdesc; + int i; + unsigned long flags; + + dev_dbg(chan2dev(&dwc->chan), "%s\n", __func__); + + if (!cdesc) + return; + + spin_lock_irqsave(&dwc->lock, flags); + + dwc_chan_disable(dw, dwc); + + dma_writel(dw, CLEAR.ERROR, dwc->mask); + dma_writel(dw, CLEAR.XFER, dwc->mask); + + spin_unlock_irqrestore(&dwc->lock, flags); + + for (i = 0; i < cdesc->periods; i++) + dwc_desc_put(dwc, cdesc->desc[i]); + + kfree(cdesc->desc); + kfree(cdesc); + + clear_bit(DW_DMA_IS_CYCLIC, &dwc->flags); +} +EXPORT_SYMBOL(dw_dma_cyclic_free); + +/*----------------------------------------------------------------------*/ + +static void dw_dma_off(struct dw_dma *dw) +{ + int i; + + dma_writel(dw, CFG, 0); + + channel_clear_bit(dw, MASK.XFER, dw->all_chan_mask); + channel_clear_bit(dw, MASK.SRC_TRAN, dw->all_chan_mask); + channel_clear_bit(dw, MASK.DST_TRAN, dw->all_chan_mask); + channel_clear_bit(dw, MASK.ERROR, dw->all_chan_mask); + + while (dma_readl(dw, CFG) & DW_CFG_DMA_EN) + cpu_relax(); + + for (i = 0; i < dw->dma.chancnt; i++) + dw->chan[i].initialized = false; +} + +#ifdef CONFIG_OF +static struct dw_dma_platform_data * +dw_dma_parse_dt(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct dw_dma_platform_data *pdata; + u32 tmp, arr[4]; + + if (!np) { + dev_err(&pdev->dev, "Missing DT data\n"); + return NULL; + } + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; + + if (of_property_read_u32(np, "dma-channels", &pdata->nr_channels)) + return NULL; + + if (of_property_read_bool(np, "is_private")) + pdata->is_private = true; + + if (!of_property_read_u32(np, "chan_allocation_order", &tmp)) + pdata->chan_allocation_order = (unsigned char)tmp; + + if (!of_property_read_u32(np, "chan_priority", &tmp)) + pdata->chan_priority = tmp; + + if (!of_property_read_u32(np, "block_size", &tmp)) + pdata->block_size = tmp; + + if (!of_property_read_u32(np, "dma-masters", &tmp)) { + if (tmp > 4) + return NULL; + + pdata->nr_masters = tmp; + } + + if (!of_property_read_u32_array(np, "data_width", arr, + pdata->nr_masters)) + for (tmp = 0; tmp < pdata->nr_masters; tmp++) + pdata->data_width[tmp] = arr[tmp]; + + return pdata; +} +#else +static inline struct dw_dma_platform_data * +dw_dma_parse_dt(struct platform_device *pdev) +{ + return NULL; +} +#endif + +static int dw_probe(struct platform_device *pdev) +{ + struct dw_dma_platform_data *pdata; + struct resource *io; + struct dw_dma *dw; + size_t size; + void __iomem *regs; + bool autocfg; + unsigned int dw_params; + unsigned int nr_channels; + unsigned int max_blk_size = 0; + int irq; + int err; + int i; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + io = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(&pdev->dev, io); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + /* Apply default dma_mask if needed */ + if (!pdev->dev.dma_mask) { + pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; + pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); + } + + dw_params = dma_read_byaddr(regs, DW_PARAMS); + autocfg = dw_params >> DW_PARAMS_EN & 0x1; + + dev_dbg(&pdev->dev, "DW_PARAMS: 0x%08x\n", dw_params); + + pdata = dev_get_platdata(&pdev->dev); + if (!pdata) + pdata = dw_dma_parse_dt(pdev); + + if (!pdata && autocfg) { + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + /* Fill platform data with the default values */ + pdata->is_private = true; + pdata->chan_allocation_order = CHAN_ALLOCATION_ASCENDING; + pdata->chan_priority = CHAN_PRIORITY_ASCENDING; + } else if (!pdata || pdata->nr_channels > DW_DMA_MAX_NR_CHANNELS) + return -EINVAL; + + if (autocfg) + nr_channels = (dw_params >> DW_PARAMS_NR_CHAN & 0x7) + 1; + else + nr_channels = pdata->nr_channels; + + size = sizeof(struct dw_dma) + nr_channels * sizeof(struct dw_dma_chan); + dw = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + if (!dw) + return -ENOMEM; + + dw->clk = devm_clk_get(&pdev->dev, "hclk"); + if (IS_ERR(dw->clk)) + return PTR_ERR(dw->clk); + clk_prepare_enable(dw->clk); + + dw->regs = regs; + + /* Get hardware configuration parameters */ + if (autocfg) { + max_blk_size = dma_readl(dw, MAX_BLK_SIZE); + + dw->nr_masters = (dw_params >> DW_PARAMS_NR_MASTER & 3) + 1; + for (i = 0; i < dw->nr_masters; i++) { + dw->data_width[i] = + (dw_params >> DW_PARAMS_DATA_WIDTH(i) & 3) + 2; + } + } else { + dw->nr_masters = pdata->nr_masters; + memcpy(dw->data_width, pdata->data_width, 4); + } + + /* Calculate all channel mask before DMA setup */ + dw->all_chan_mask = (1 << nr_channels) - 1; + + /* Force dma off, just in case */ + dw_dma_off(dw); + + /* Disable BLOCK interrupts as well */ + channel_clear_bit(dw, MASK.BLOCK, dw->all_chan_mask); + + err = devm_request_irq(&pdev->dev, irq, dw_dma_interrupt, 0, + "dw_dmac", dw); + if (err) + return err; + + platform_set_drvdata(pdev, dw); + + /* Create a pool of consistent memory blocks for hardware descriptors */ + dw->desc_pool = dmam_pool_create("dw_dmac_desc_pool", &pdev->dev, + sizeof(struct dw_desc), 4, 0); + if (!dw->desc_pool) { + dev_err(&pdev->dev, "No memory for descriptors dma pool\n"); + return -ENOMEM; + } + + tasklet_init(&dw->tasklet, dw_dma_tasklet, (unsigned long)dw); + + INIT_LIST_HEAD(&dw->dma.channels); + for (i = 0; i < nr_channels; i++) { + struct dw_dma_chan *dwc = &dw->chan[i]; + int r = nr_channels - i - 1; + + dwc->chan.device = &dw->dma; + dma_cookie_init(&dwc->chan); + if (pdata->chan_allocation_order == CHAN_ALLOCATION_ASCENDING) + list_add_tail(&dwc->chan.device_node, + &dw->dma.channels); + else + list_add(&dwc->chan.device_node, &dw->dma.channels); + + /* 7 is highest priority & 0 is lowest. */ + if (pdata->chan_priority == CHAN_PRIORITY_ASCENDING) + dwc->priority = r; + else + dwc->priority = i; + + dwc->ch_regs = &__dw_regs(dw)->CHAN[i]; + spin_lock_init(&dwc->lock); + dwc->mask = 1 << i; + + INIT_LIST_HEAD(&dwc->active_list); + INIT_LIST_HEAD(&dwc->queue); + INIT_LIST_HEAD(&dwc->free_list); + + channel_clear_bit(dw, CH_EN, dwc->mask); + + dwc->direction = DMA_TRANS_NONE; + dwc->request_line = ~0; + + /* Hardware configuration */ + if (autocfg) { + unsigned int dwc_params; + + dwc_params = dma_read_byaddr(regs + r * sizeof(u32), + DWC_PARAMS); + + dev_dbg(&pdev->dev, "DWC_PARAMS[%d]: 0x%08x\n", i, + dwc_params); + + /* Decode maximum block size for given channel. The + * stored 4 bit value represents blocks from 0x00 for 3 + * up to 0x0a for 4095. */ + dwc->block_size = + (4 << ((max_blk_size >> 4 * i) & 0xf)) - 1; + dwc->nollp = + (dwc_params >> DWC_PARAMS_MBLK_EN & 0x1) == 0; + } else { + dwc->block_size = pdata->block_size; + + /* Check if channel supports multi block transfer */ + channel_writel(dwc, LLP, 0xfffffffc); + dwc->nollp = + (channel_readl(dwc, LLP) & 0xfffffffc) == 0; + channel_writel(dwc, LLP, 0); + } + } + + /* Clear all interrupts on all channels. */ + dma_writel(dw, CLEAR.XFER, dw->all_chan_mask); + dma_writel(dw, CLEAR.BLOCK, dw->all_chan_mask); + dma_writel(dw, CLEAR.SRC_TRAN, dw->all_chan_mask); + dma_writel(dw, CLEAR.DST_TRAN, dw->all_chan_mask); + dma_writel(dw, CLEAR.ERROR, dw->all_chan_mask); + + dma_cap_set(DMA_MEMCPY, dw->dma.cap_mask); + dma_cap_set(DMA_SLAVE, dw->dma.cap_mask); + if (pdata->is_private) + dma_cap_set(DMA_PRIVATE, dw->dma.cap_mask); + dw->dma.dev = &pdev->dev; + dw->dma.device_alloc_chan_resources = dwc_alloc_chan_resources; + dw->dma.device_free_chan_resources = dwc_free_chan_resources; + + dw->dma.device_prep_dma_memcpy = dwc_prep_dma_memcpy; + + dw->dma.device_prep_slave_sg = dwc_prep_slave_sg; + dw->dma.device_control = dwc_control; + + dw->dma.device_tx_status = dwc_tx_status; + dw->dma.device_issue_pending = dwc_issue_pending; + + dma_writel(dw, CFG, DW_CFG_DMA_EN); + + dev_info(&pdev->dev, "DesignWare DMA Controller, %d channels\n", + nr_channels); + + dma_async_device_register(&dw->dma); + + if (pdev->dev.of_node) { + err = of_dma_controller_register(pdev->dev.of_node, + dw_dma_of_xlate, dw); + if (err) + dev_err(&pdev->dev, + "could not register of_dma_controller\n"); + } + + if (ACPI_HANDLE(&pdev->dev)) + dw_dma_acpi_controller_register(dw); + + return 0; +} + +static int dw_remove(struct platform_device *pdev) +{ + struct dw_dma *dw = platform_get_drvdata(pdev); + struct dw_dma_chan *dwc, *_dwc; + + if (pdev->dev.of_node) + of_dma_controller_free(pdev->dev.of_node); + dw_dma_off(dw); + dma_async_device_unregister(&dw->dma); + + tasklet_kill(&dw->tasklet); + + list_for_each_entry_safe(dwc, _dwc, &dw->dma.channels, + chan.device_node) { + list_del(&dwc->chan.device_node); + channel_clear_bit(dw, CH_EN, dwc->mask); + } + + return 0; +} + +static void dw_shutdown(struct platform_device *pdev) +{ + struct dw_dma *dw = platform_get_drvdata(pdev); + + dw_dma_off(dw); + clk_disable_unprepare(dw->clk); +} + +static int dw_suspend_noirq(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct dw_dma *dw = platform_get_drvdata(pdev); + + dw_dma_off(dw); + clk_disable_unprepare(dw->clk); + + return 0; +} + +static int dw_resume_noirq(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct dw_dma *dw = platform_get_drvdata(pdev); + + clk_prepare_enable(dw->clk); + dma_writel(dw, CFG, DW_CFG_DMA_EN); + + return 0; +} + +static const struct dev_pm_ops dw_dev_pm_ops = { + .suspend_noirq = dw_suspend_noirq, + .resume_noirq = dw_resume_noirq, + .freeze_noirq = dw_suspend_noirq, + .thaw_noirq = dw_resume_noirq, + .restore_noirq = dw_resume_noirq, + .poweroff_noirq = dw_suspend_noirq, +}; + +#ifdef CONFIG_OF +static const struct of_device_id dw_dma_of_id_table[] = { + { .compatible = "snps,dma-spear1340" }, + {} +}; +MODULE_DEVICE_TABLE(of, dw_dma_of_id_table); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id dw_dma_acpi_id_table[] = { + { "INTL9C60", 0 }, + { } +}; +#endif + +static struct platform_driver dw_driver = { + .probe = dw_probe, + .remove = dw_remove, + .shutdown = dw_shutdown, + .driver = { + .name = "dw_dmac", + .pm = &dw_dev_pm_ops, + .of_match_table = of_match_ptr(dw_dma_of_id_table), + .acpi_match_table = ACPI_PTR(dw_dma_acpi_id_table), + }, +}; + +static int __init dw_init(void) +{ + return platform_driver_register(&dw_driver); +} +subsys_initcall(dw_init); + +static void __exit dw_exit(void) +{ + platform_driver_unregister(&dw_driver); +} +module_exit(dw_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Synopsys DesignWare DMA Controller driver"); +MODULE_AUTHOR("Haavard Skinnemoen (Atmel)"); +MODULE_AUTHOR("Viresh Kumar "); diff --git a/drivers/dma/dw/dw_dmac_regs.h b/drivers/dma/dw/dw_dmac_regs.h new file mode 100644 index 000000000000..9d417200bd57 --- /dev/null +++ b/drivers/dma/dw/dw_dmac_regs.h @@ -0,0 +1,311 @@ +/* + * Driver for the Synopsys DesignWare AHB DMA Controller + * + * Copyright (C) 2005-2007 Atmel Corporation + * Copyright (C) 2010-2011 ST Microelectronics + * + * 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 +#include + +#define DW_DMA_MAX_NR_CHANNELS 8 +#define DW_DMA_MAX_NR_REQUESTS 16 + +/* flow controller */ +enum dw_dma_fc { + DW_DMA_FC_D_M2M, + DW_DMA_FC_D_M2P, + DW_DMA_FC_D_P2M, + DW_DMA_FC_D_P2P, + DW_DMA_FC_P_P2M, + DW_DMA_FC_SP_P2P, + DW_DMA_FC_P_M2P, + DW_DMA_FC_DP_P2P, +}; + +/* + * Redefine this macro to handle differences between 32- and 64-bit + * addressing, big vs. little endian, etc. + */ +#define DW_REG(name) u32 name; u32 __pad_##name + +/* Hardware register definitions. */ +struct dw_dma_chan_regs { + DW_REG(SAR); /* Source Address Register */ + DW_REG(DAR); /* Destination Address Register */ + DW_REG(LLP); /* Linked List Pointer */ + u32 CTL_LO; /* Control Register Low */ + u32 CTL_HI; /* Control Register High */ + DW_REG(SSTAT); + DW_REG(DSTAT); + DW_REG(SSTATAR); + DW_REG(DSTATAR); + u32 CFG_LO; /* Configuration Register Low */ + u32 CFG_HI; /* Configuration Register High */ + DW_REG(SGR); + DW_REG(DSR); +}; + +struct dw_dma_irq_regs { + DW_REG(XFER); + DW_REG(BLOCK); + DW_REG(SRC_TRAN); + DW_REG(DST_TRAN); + DW_REG(ERROR); +}; + +struct dw_dma_regs { + /* per-channel registers */ + struct dw_dma_chan_regs CHAN[DW_DMA_MAX_NR_CHANNELS]; + + /* irq handling */ + struct dw_dma_irq_regs RAW; /* r */ + struct dw_dma_irq_regs STATUS; /* r (raw & mask) */ + struct dw_dma_irq_regs MASK; /* rw (set = irq enabled) */ + struct dw_dma_irq_regs CLEAR; /* w (ack, affects "raw") */ + + DW_REG(STATUS_INT); /* r */ + + /* software handshaking */ + DW_REG(REQ_SRC); + DW_REG(REQ_DST); + DW_REG(SGL_REQ_SRC); + DW_REG(SGL_REQ_DST); + DW_REG(LAST_SRC); + DW_REG(LAST_DST); + + /* miscellaneous */ + DW_REG(CFG); + DW_REG(CH_EN); + DW_REG(ID); + DW_REG(TEST); + + /* reserved */ + DW_REG(__reserved0); + DW_REG(__reserved1); + + /* optional encoded params, 0x3c8..0x3f7 */ + u32 __reserved; + + /* per-channel configuration registers */ + u32 DWC_PARAMS[DW_DMA_MAX_NR_CHANNELS]; + u32 MULTI_BLK_TYPE; + u32 MAX_BLK_SIZE; + + /* top-level parameters */ + u32 DW_PARAMS; +}; + +#ifdef CONFIG_DW_DMAC_BIG_ENDIAN_IO +#define dma_readl_native ioread32be +#define dma_writel_native iowrite32be +#else +#define dma_readl_native readl +#define dma_writel_native writel +#endif + +/* To access the registers in early stage of probe */ +#define dma_read_byaddr(addr, name) \ + dma_readl_native((addr) + offsetof(struct dw_dma_regs, name)) + +/* Bitfields in DW_PARAMS */ +#define DW_PARAMS_NR_CHAN 8 /* number of channels */ +#define DW_PARAMS_NR_MASTER 11 /* number of AHB masters */ +#define DW_PARAMS_DATA_WIDTH(n) (15 + 2 * (n)) +#define DW_PARAMS_DATA_WIDTH1 15 /* master 1 data width */ +#define DW_PARAMS_DATA_WIDTH2 17 /* master 2 data width */ +#define DW_PARAMS_DATA_WIDTH3 19 /* master 3 data width */ +#define DW_PARAMS_DATA_WIDTH4 21 /* master 4 data width */ +#define DW_PARAMS_EN 28 /* encoded parameters */ + +/* Bitfields in DWC_PARAMS */ +#define DWC_PARAMS_MBLK_EN 11 /* multi block transfer */ + +/* Bitfields in CTL_LO */ +#define DWC_CTLL_INT_EN (1 << 0) /* irqs enabled? */ +#define DWC_CTLL_DST_WIDTH(n) ((n)<<1) /* bytes per element */ +#define DWC_CTLL_SRC_WIDTH(n) ((n)<<4) +#define DWC_CTLL_DST_INC (0<<7) /* DAR update/not */ +#define DWC_CTLL_DST_DEC (1<<7) +#define DWC_CTLL_DST_FIX (2<<7) +#define DWC_CTLL_SRC_INC (0<<7) /* SAR update/not */ +#define DWC_CTLL_SRC_DEC (1<<9) +#define DWC_CTLL_SRC_FIX (2<<9) +#define DWC_CTLL_DST_MSIZE(n) ((n)<<11) /* burst, #elements */ +#define DWC_CTLL_SRC_MSIZE(n) ((n)<<14) +#define DWC_CTLL_S_GATH_EN (1 << 17) /* src gather, !FIX */ +#define DWC_CTLL_D_SCAT_EN (1 << 18) /* dst scatter, !FIX */ +#define DWC_CTLL_FC(n) ((n) << 20) +#define DWC_CTLL_FC_M2M (0 << 20) /* mem-to-mem */ +#define DWC_CTLL_FC_M2P (1 << 20) /* mem-to-periph */ +#define DWC_CTLL_FC_P2M (2 << 20) /* periph-to-mem */ +#define DWC_CTLL_FC_P2P (3 << 20) /* periph-to-periph */ +/* plus 4 transfer types for peripheral-as-flow-controller */ +#define DWC_CTLL_DMS(n) ((n)<<23) /* dst master select */ +#define DWC_CTLL_SMS(n) ((n)<<25) /* src master select */ +#define DWC_CTLL_LLP_D_EN (1 << 27) /* dest block chain */ +#define DWC_CTLL_LLP_S_EN (1 << 28) /* src block chain */ + +/* Bitfields in CTL_HI */ +#define DWC_CTLH_DONE 0x00001000 +#define DWC_CTLH_BLOCK_TS_MASK 0x00000fff + +/* Bitfields in CFG_LO. Platform-configurable bits are in */ +#define DWC_CFGL_CH_PRIOR_MASK (0x7 << 5) /* priority mask */ +#define DWC_CFGL_CH_PRIOR(x) ((x) << 5) /* priority */ +#define DWC_CFGL_CH_SUSP (1 << 8) /* pause xfer */ +#define DWC_CFGL_FIFO_EMPTY (1 << 9) /* pause xfer */ +#define DWC_CFGL_HS_DST (1 << 10) /* handshake w/dst */ +#define DWC_CFGL_HS_SRC (1 << 11) /* handshake w/src */ +#define DWC_CFGL_MAX_BURST(x) ((x) << 20) +#define DWC_CFGL_RELOAD_SAR (1 << 30) +#define DWC_CFGL_RELOAD_DAR (1 << 31) + +/* Bitfields in CFG_HI. Platform-configurable bits are in */ +#define DWC_CFGH_DS_UPD_EN (1 << 5) +#define DWC_CFGH_SS_UPD_EN (1 << 6) + +/* Bitfields in SGR */ +#define DWC_SGR_SGI(x) ((x) << 0) +#define DWC_SGR_SGC(x) ((x) << 20) + +/* Bitfields in DSR */ +#define DWC_DSR_DSI(x) ((x) << 0) +#define DWC_DSR_DSC(x) ((x) << 20) + +/* Bitfields in CFG */ +#define DW_CFG_DMA_EN (1 << 0) + +enum dw_dmac_flags { + DW_DMA_IS_CYCLIC = 0, + DW_DMA_IS_SOFT_LLP = 1, +}; + +struct dw_dma_chan { + struct dma_chan chan; + void __iomem *ch_regs; + u8 mask; + u8 priority; + enum dma_transfer_direction direction; + bool paused; + bool initialized; + + /* software emulation of the LLP transfers */ + struct list_head *tx_node_active; + + spinlock_t lock; + + /* these other elements are all protected by lock */ + unsigned long flags; + struct list_head active_list; + struct list_head queue; + struct list_head free_list; + u32 residue; + struct dw_cyclic_desc *cdesc; + + unsigned int descs_allocated; + + /* hardware configuration */ + unsigned int block_size; + bool nollp; + + /* custom slave configuration */ + unsigned int request_line; + unsigned char src_master; + unsigned char dst_master; + + /* configuration passed via DMA_SLAVE_CONFIG */ + struct dma_slave_config dma_sconfig; +}; + +static inline struct dw_dma_chan_regs __iomem * +__dwc_regs(struct dw_dma_chan *dwc) +{ + return dwc->ch_regs; +} + +#define channel_readl(dwc, name) \ + dma_readl_native(&(__dwc_regs(dwc)->name)) +#define channel_writel(dwc, name, val) \ + dma_writel_native((val), &(__dwc_regs(dwc)->name)) + +static inline struct dw_dma_chan *to_dw_dma_chan(struct dma_chan *chan) +{ + return container_of(chan, struct dw_dma_chan, chan); +} + +struct dw_dma { + struct dma_device dma; + void __iomem *regs; + struct dma_pool *desc_pool; + struct tasklet_struct tasklet; + struct clk *clk; + + u8 all_chan_mask; + + /* hardware configuration */ + unsigned char nr_masters; + unsigned char data_width[4]; + + struct dw_dma_chan chan[0]; +}; + +static inline struct dw_dma_regs __iomem *__dw_regs(struct dw_dma *dw) +{ + return dw->regs; +} + +#define dma_readl(dw, name) \ + dma_readl_native(&(__dw_regs(dw)->name)) +#define dma_writel(dw, name, val) \ + dma_writel_native((val), &(__dw_regs(dw)->name)) + +#define channel_set_bit(dw, reg, mask) \ + dma_writel(dw, reg, ((mask) << 8) | (mask)) +#define channel_clear_bit(dw, reg, mask) \ + dma_writel(dw, reg, ((mask) << 8) | 0) + +static inline struct dw_dma *to_dw_dma(struct dma_device *ddev) +{ + return container_of(ddev, struct dw_dma, dma); +} + +/* LLI == Linked List Item; a.k.a. DMA block descriptor */ +struct dw_lli { + /* values that are not changed by hardware */ + u32 sar; + u32 dar; + u32 llp; /* chain to next lli */ + u32 ctllo; + /* values that may get written back: */ + u32 ctlhi; + /* sstat and dstat can snapshot peripheral register state. + * silicon config may discard either or both... + */ + u32 sstat; + u32 dstat; +}; + +struct dw_desc { + /* FIRST values the hardware uses */ + struct dw_lli lli; + + /* THEN values for driver housekeeping */ + struct list_head desc_node; + struct list_head tx_list; + struct dma_async_tx_descriptor txd; + size_t len; + size_t total_len; +}; + +#define to_dw_desc(h) list_entry(h, struct dw_desc, desc_node) + +static inline struct dw_desc * +txd_to_dw_desc(struct dma_async_tx_descriptor *txd) +{ + return container_of(txd, struct dw_desc, txd); +} diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c deleted file mode 100644 index 2b65ba614e60..000000000000 --- a/drivers/dma/dw_dmac.c +++ /dev/null @@ -1,1969 +0,0 @@ -/* - * Core driver for the Synopsys DesignWare DMA Controller - * - * Copyright (C) 2007-2008 Atmel Corporation - * Copyright (C) 2010-2011 ST Microelectronics - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dw_dmac_regs.h" -#include "dmaengine.h" - -/* - * This supports the Synopsys "DesignWare AHB Central DMA Controller", - * (DW_ahb_dmac) which is used with various AMBA 2.0 systems (not all - * of which use ARM any more). See the "Databook" from Synopsys for - * information beyond what licensees probably provide. - * - * The driver has currently been tested only with the Atmel AT32AP7000, - * which does not support descriptor writeback. - */ - -static inline unsigned int dwc_get_dms(struct dw_dma_slave *slave) -{ - return slave ? slave->dst_master : 0; -} - -static inline unsigned int dwc_get_sms(struct dw_dma_slave *slave) -{ - return slave ? slave->src_master : 1; -} - -static inline void dwc_set_masters(struct dw_dma_chan *dwc) -{ - struct dw_dma *dw = to_dw_dma(dwc->chan.device); - struct dw_dma_slave *dws = dwc->chan.private; - unsigned char mmax = dw->nr_masters - 1; - - if (dwc->request_line == ~0) { - dwc->src_master = min_t(unsigned char, mmax, dwc_get_sms(dws)); - dwc->dst_master = min_t(unsigned char, mmax, dwc_get_dms(dws)); - } -} - -#define DWC_DEFAULT_CTLLO(_chan) ({ \ - struct dw_dma_chan *_dwc = to_dw_dma_chan(_chan); \ - struct dma_slave_config *_sconfig = &_dwc->dma_sconfig; \ - bool _is_slave = is_slave_direction(_dwc->direction); \ - u8 _smsize = _is_slave ? _sconfig->src_maxburst : \ - DW_DMA_MSIZE_16; \ - u8 _dmsize = _is_slave ? _sconfig->dst_maxburst : \ - DW_DMA_MSIZE_16; \ - \ - (DWC_CTLL_DST_MSIZE(_dmsize) \ - | DWC_CTLL_SRC_MSIZE(_smsize) \ - | DWC_CTLL_LLP_D_EN \ - | DWC_CTLL_LLP_S_EN \ - | DWC_CTLL_DMS(_dwc->dst_master) \ - | DWC_CTLL_SMS(_dwc->src_master)); \ - }) - -/* - * Number of descriptors to allocate for each channel. This should be - * made configurable somehow; preferably, the clients (at least the - * ones using slave transfers) should be able to give us a hint. - */ -#define NR_DESCS_PER_CHANNEL 64 - -/*----------------------------------------------------------------------*/ - -static struct device *chan2dev(struct dma_chan *chan) -{ - return &chan->dev->device; -} -static struct device *chan2parent(struct dma_chan *chan) -{ - return chan->dev->device.parent; -} - -static struct dw_desc *dwc_first_active(struct dw_dma_chan *dwc) -{ - return to_dw_desc(dwc->active_list.next); -} - -static struct dw_desc *dwc_desc_get(struct dw_dma_chan *dwc) -{ - struct dw_desc *desc, *_desc; - struct dw_desc *ret = NULL; - unsigned int i = 0; - unsigned long flags; - - spin_lock_irqsave(&dwc->lock, flags); - list_for_each_entry_safe(desc, _desc, &dwc->free_list, desc_node) { - i++; - if (async_tx_test_ack(&desc->txd)) { - list_del(&desc->desc_node); - ret = desc; - break; - } - dev_dbg(chan2dev(&dwc->chan), "desc %p not ACKed\n", desc); - } - spin_unlock_irqrestore(&dwc->lock, flags); - - dev_vdbg(chan2dev(&dwc->chan), "scanned %u descriptors on freelist\n", i); - - return ret; -} - -/* - * Move a descriptor, including any children, to the free list. - * `desc' must not be on any lists. - */ -static void dwc_desc_put(struct dw_dma_chan *dwc, struct dw_desc *desc) -{ - unsigned long flags; - - if (desc) { - struct dw_desc *child; - - spin_lock_irqsave(&dwc->lock, flags); - list_for_each_entry(child, &desc->tx_list, desc_node) - dev_vdbg(chan2dev(&dwc->chan), - "moving child desc %p to freelist\n", - child); - list_splice_init(&desc->tx_list, &dwc->free_list); - dev_vdbg(chan2dev(&dwc->chan), "moving desc %p to freelist\n", desc); - list_add(&desc->desc_node, &dwc->free_list); - spin_unlock_irqrestore(&dwc->lock, flags); - } -} - -static void dwc_initialize(struct dw_dma_chan *dwc) -{ - struct dw_dma *dw = to_dw_dma(dwc->chan.device); - struct dw_dma_slave *dws = dwc->chan.private; - u32 cfghi = DWC_CFGH_FIFO_MODE; - u32 cfglo = DWC_CFGL_CH_PRIOR(dwc->priority); - - if (dwc->initialized == true) - return; - - if (dws) { - /* - * We need controller-specific data to set up slave - * transfers. - */ - BUG_ON(!dws->dma_dev || dws->dma_dev != dw->dma.dev); - - cfghi = dws->cfg_hi; - cfglo |= dws->cfg_lo & ~DWC_CFGL_CH_PRIOR_MASK; - } else { - if (dwc->direction == DMA_MEM_TO_DEV) - cfghi = DWC_CFGH_DST_PER(dwc->request_line); - else if (dwc->direction == DMA_DEV_TO_MEM) - cfghi = DWC_CFGH_SRC_PER(dwc->request_line); - } - - channel_writel(dwc, CFG_LO, cfglo); - channel_writel(dwc, CFG_HI, cfghi); - - /* Enable interrupts */ - channel_set_bit(dw, MASK.XFER, dwc->mask); - channel_set_bit(dw, MASK.ERROR, dwc->mask); - - dwc->initialized = true; -} - -/*----------------------------------------------------------------------*/ - -static inline unsigned int dwc_fast_fls(unsigned long long v) -{ - /* - * We can be a lot more clever here, but this should take care - * of the most common optimization. - */ - if (!(v & 7)) - return 3; - else if (!(v & 3)) - return 2; - else if (!(v & 1)) - return 1; - return 0; -} - -static inline void dwc_dump_chan_regs(struct dw_dma_chan *dwc) -{ - dev_err(chan2dev(&dwc->chan), - " SAR: 0x%x DAR: 0x%x LLP: 0x%x CTL: 0x%x:%08x\n", - channel_readl(dwc, SAR), - channel_readl(dwc, DAR), - channel_readl(dwc, LLP), - channel_readl(dwc, CTL_HI), - channel_readl(dwc, CTL_LO)); -} - -static inline void dwc_chan_disable(struct dw_dma *dw, struct dw_dma_chan *dwc) -{ - channel_clear_bit(dw, CH_EN, dwc->mask); - while (dma_readl(dw, CH_EN) & dwc->mask) - cpu_relax(); -} - -/*----------------------------------------------------------------------*/ - -/* Perform single block transfer */ -static inline void dwc_do_single_block(struct dw_dma_chan *dwc, - struct dw_desc *desc) -{ - struct dw_dma *dw = to_dw_dma(dwc->chan.device); - u32 ctllo; - - /* Software emulation of LLP mode relies on interrupts to continue - * multi block transfer. */ - ctllo = desc->lli.ctllo | DWC_CTLL_INT_EN; - - channel_writel(dwc, SAR, desc->lli.sar); - channel_writel(dwc, DAR, desc->lli.dar); - channel_writel(dwc, CTL_LO, ctllo); - channel_writel(dwc, CTL_HI, desc->lli.ctlhi); - channel_set_bit(dw, CH_EN, dwc->mask); - - /* Move pointer to next descriptor */ - dwc->tx_node_active = dwc->tx_node_active->next; -} - -/* Called with dwc->lock held and bh disabled */ -static void dwc_dostart(struct dw_dma_chan *dwc, struct dw_desc *first) -{ - struct dw_dma *dw = to_dw_dma(dwc->chan.device); - unsigned long was_soft_llp; - - /* ASSERT: channel is idle */ - if (dma_readl(dw, CH_EN) & dwc->mask) { - dev_err(chan2dev(&dwc->chan), - "BUG: Attempted to start non-idle channel\n"); - dwc_dump_chan_regs(dwc); - - /* The tasklet will hopefully advance the queue... */ - return; - } - - if (dwc->nollp) { - was_soft_llp = test_and_set_bit(DW_DMA_IS_SOFT_LLP, - &dwc->flags); - if (was_soft_llp) { - dev_err(chan2dev(&dwc->chan), - "BUG: Attempted to start new LLP transfer " - "inside ongoing one\n"); - return; - } - - dwc_initialize(dwc); - - dwc->residue = first->total_len; - dwc->tx_node_active = &first->tx_list; - - /* Submit first block */ - dwc_do_single_block(dwc, first); - - return; - } - - dwc_initialize(dwc); - - channel_writel(dwc, LLP, first->txd.phys); - channel_writel(dwc, CTL_LO, - DWC_CTLL_LLP_D_EN | DWC_CTLL_LLP_S_EN); - channel_writel(dwc, CTL_HI, 0); - channel_set_bit(dw, CH_EN, dwc->mask); -} - -/*----------------------------------------------------------------------*/ - -static void -dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc, - bool callback_required) -{ - dma_async_tx_callback callback = NULL; - void *param = NULL; - struct dma_async_tx_descriptor *txd = &desc->txd; - struct dw_desc *child; - unsigned long flags; - - dev_vdbg(chan2dev(&dwc->chan), "descriptor %u complete\n", txd->cookie); - - spin_lock_irqsave(&dwc->lock, flags); - dma_cookie_complete(txd); - if (callback_required) { - callback = txd->callback; - param = txd->callback_param; - } - - /* async_tx_ack */ - list_for_each_entry(child, &desc->tx_list, desc_node) - async_tx_ack(&child->txd); - async_tx_ack(&desc->txd); - - list_splice_init(&desc->tx_list, &dwc->free_list); - list_move(&desc->desc_node, &dwc->free_list); - - if (!is_slave_direction(dwc->direction)) { - struct device *parent = chan2parent(&dwc->chan); - if (!(txd->flags & DMA_COMPL_SKIP_DEST_UNMAP)) { - if (txd->flags & DMA_COMPL_DEST_UNMAP_SINGLE) - dma_unmap_single(parent, desc->lli.dar, - desc->total_len, DMA_FROM_DEVICE); - else - dma_unmap_page(parent, desc->lli.dar, - desc->total_len, DMA_FROM_DEVICE); - } - if (!(txd->flags & DMA_COMPL_SKIP_SRC_UNMAP)) { - if (txd->flags & DMA_COMPL_SRC_UNMAP_SINGLE) - dma_unmap_single(parent, desc->lli.sar, - desc->total_len, DMA_TO_DEVICE); - else - dma_unmap_page(parent, desc->lli.sar, - desc->total_len, DMA_TO_DEVICE); - } - } - - spin_unlock_irqrestore(&dwc->lock, flags); - - if (callback) - callback(param); -} - -static void dwc_complete_all(struct dw_dma *dw, struct dw_dma_chan *dwc) -{ - struct dw_desc *desc, *_desc; - LIST_HEAD(list); - unsigned long flags; - - spin_lock_irqsave(&dwc->lock, flags); - if (dma_readl(dw, CH_EN) & dwc->mask) { - dev_err(chan2dev(&dwc->chan), - "BUG: XFER bit set, but channel not idle!\n"); - - /* Try to continue after resetting the channel... */ - dwc_chan_disable(dw, dwc); - } - - /* - * Submit queued descriptors ASAP, i.e. before we go through - * the completed ones. - */ - list_splice_init(&dwc->active_list, &list); - if (!list_empty(&dwc->queue)) { - list_move(dwc->queue.next, &dwc->active_list); - dwc_dostart(dwc, dwc_first_active(dwc)); - } - - spin_unlock_irqrestore(&dwc->lock, flags); - - list_for_each_entry_safe(desc, _desc, &list, desc_node) - dwc_descriptor_complete(dwc, desc, true); -} - -/* Returns how many bytes were already received from source */ -static inline u32 dwc_get_sent(struct dw_dma_chan *dwc) -{ - u32 ctlhi = channel_readl(dwc, CTL_HI); - u32 ctllo = channel_readl(dwc, CTL_LO); - - return (ctlhi & DWC_CTLH_BLOCK_TS_MASK) * (1 << (ctllo >> 4 & 7)); -} - -static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) -{ - dma_addr_t llp; - struct dw_desc *desc, *_desc; - struct dw_desc *child; - u32 status_xfer; - unsigned long flags; - - spin_lock_irqsave(&dwc->lock, flags); - llp = channel_readl(dwc, LLP); - status_xfer = dma_readl(dw, RAW.XFER); - - if (status_xfer & dwc->mask) { - /* Everything we've submitted is done */ - dma_writel(dw, CLEAR.XFER, dwc->mask); - - if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags)) { - struct list_head *head, *active = dwc->tx_node_active; - - /* - * We are inside first active descriptor. - * Otherwise something is really wrong. - */ - desc = dwc_first_active(dwc); - - head = &desc->tx_list; - if (active != head) { - /* Update desc to reflect last sent one */ - if (active != head->next) - desc = to_dw_desc(active->prev); - - dwc->residue -= desc->len; - - child = to_dw_desc(active); - - /* Submit next block */ - dwc_do_single_block(dwc, child); - - spin_unlock_irqrestore(&dwc->lock, flags); - return; - } - - /* We are done here */ - clear_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags); - } - - dwc->residue = 0; - - spin_unlock_irqrestore(&dwc->lock, flags); - - dwc_complete_all(dw, dwc); - return; - } - - if (list_empty(&dwc->active_list)) { - dwc->residue = 0; - spin_unlock_irqrestore(&dwc->lock, flags); - return; - } - - if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags)) { - dev_vdbg(chan2dev(&dwc->chan), "%s: soft LLP mode\n", __func__); - spin_unlock_irqrestore(&dwc->lock, flags); - return; - } - - dev_vdbg(chan2dev(&dwc->chan), "%s: llp=0x%llx\n", __func__, - (unsigned long long)llp); - - list_for_each_entry_safe(desc, _desc, &dwc->active_list, desc_node) { - /* Initial residue value */ - dwc->residue = desc->total_len; - - /* Check first descriptors addr */ - if (desc->txd.phys == llp) { - spin_unlock_irqrestore(&dwc->lock, flags); - return; - } - - /* Check first descriptors llp */ - if (desc->lli.llp == llp) { - /* This one is currently in progress */ - dwc->residue -= dwc_get_sent(dwc); - spin_unlock_irqrestore(&dwc->lock, flags); - return; - } - - dwc->residue -= desc->len; - list_for_each_entry(child, &desc->tx_list, desc_node) { - if (child->lli.llp == llp) { - /* Currently in progress */ - dwc->residue -= dwc_get_sent(dwc); - spin_unlock_irqrestore(&dwc->lock, flags); - return; - } - dwc->residue -= child->len; - } - - /* - * No descriptors so far seem to be in progress, i.e. - * this one must be done. - */ - spin_unlock_irqrestore(&dwc->lock, flags); - dwc_descriptor_complete(dwc, desc, true); - spin_lock_irqsave(&dwc->lock, flags); - } - - dev_err(chan2dev(&dwc->chan), - "BUG: All descriptors done, but channel not idle!\n"); - - /* Try to continue after resetting the channel... */ - dwc_chan_disable(dw, dwc); - - if (!list_empty(&dwc->queue)) { - list_move(dwc->queue.next, &dwc->active_list); - dwc_dostart(dwc, dwc_first_active(dwc)); - } - spin_unlock_irqrestore(&dwc->lock, flags); -} - -static inline void dwc_dump_lli(struct dw_dma_chan *dwc, struct dw_lli *lli) -{ - dev_crit(chan2dev(&dwc->chan), " desc: s0x%x d0x%x l0x%x c0x%x:%x\n", - lli->sar, lli->dar, lli->llp, lli->ctlhi, lli->ctllo); -} - -static void dwc_handle_error(struct dw_dma *dw, struct dw_dma_chan *dwc) -{ - struct dw_desc *bad_desc; - struct dw_desc *child; - unsigned long flags; - - dwc_scan_descriptors(dw, dwc); - - spin_lock_irqsave(&dwc->lock, flags); - - /* - * The descriptor currently at the head of the active list is - * borked. Since we don't have any way to report errors, we'll - * just have to scream loudly and try to carry on. - */ - bad_desc = dwc_first_active(dwc); - list_del_init(&bad_desc->desc_node); - list_move(dwc->queue.next, dwc->active_list.prev); - - /* Clear the error flag and try to restart the controller */ - dma_writel(dw, CLEAR.ERROR, dwc->mask); - if (!list_empty(&dwc->active_list)) - dwc_dostart(dwc, dwc_first_active(dwc)); - - /* - * WARN may seem harsh, but since this only happens - * when someone submits a bad physical address in a - * descriptor, we should consider ourselves lucky that the - * controller flagged an error instead of scribbling over - * random memory locations. - */ - dev_WARN(chan2dev(&dwc->chan), "Bad descriptor submitted for DMA!\n" - " cookie: %d\n", bad_desc->txd.cookie); - dwc_dump_lli(dwc, &bad_desc->lli); - list_for_each_entry(child, &bad_desc->tx_list, desc_node) - dwc_dump_lli(dwc, &child->lli); - - spin_unlock_irqrestore(&dwc->lock, flags); - - /* Pretend the descriptor completed successfully */ - dwc_descriptor_complete(dwc, bad_desc, true); -} - -/* --------------------- Cyclic DMA API extensions -------------------- */ - -dma_addr_t dw_dma_get_src_addr(struct dma_chan *chan) -{ - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - return channel_readl(dwc, SAR); -} -EXPORT_SYMBOL(dw_dma_get_src_addr); - -dma_addr_t dw_dma_get_dst_addr(struct dma_chan *chan) -{ - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - return channel_readl(dwc, DAR); -} -EXPORT_SYMBOL(dw_dma_get_dst_addr); - -/* Called with dwc->lock held and all DMAC interrupts disabled */ -static void dwc_handle_cyclic(struct dw_dma *dw, struct dw_dma_chan *dwc, - u32 status_err, u32 status_xfer) -{ - unsigned long flags; - - if (dwc->mask) { - void (*callback)(void *param); - void *callback_param; - - dev_vdbg(chan2dev(&dwc->chan), "new cyclic period llp 0x%08x\n", - channel_readl(dwc, LLP)); - - callback = dwc->cdesc->period_callback; - callback_param = dwc->cdesc->period_callback_param; - - if (callback) - callback(callback_param); - } - - /* - * Error and transfer complete are highly unlikely, and will most - * likely be due to a configuration error by the user. - */ - if (unlikely(status_err & dwc->mask) || - unlikely(status_xfer & dwc->mask)) { - int i; - - dev_err(chan2dev(&dwc->chan), "cyclic DMA unexpected %s " - "interrupt, stopping DMA transfer\n", - status_xfer ? "xfer" : "error"); - - spin_lock_irqsave(&dwc->lock, flags); - - dwc_dump_chan_regs(dwc); - - dwc_chan_disable(dw, dwc); - - /* Make sure DMA does not restart by loading a new list */ - channel_writel(dwc, LLP, 0); - channel_writel(dwc, CTL_LO, 0); - channel_writel(dwc, CTL_HI, 0); - - dma_writel(dw, CLEAR.ERROR, dwc->mask); - dma_writel(dw, CLEAR.XFER, dwc->mask); - - for (i = 0; i < dwc->cdesc->periods; i++) - dwc_dump_lli(dwc, &dwc->cdesc->desc[i]->lli); - - spin_unlock_irqrestore(&dwc->lock, flags); - } -} - -/* ------------------------------------------------------------------------- */ - -static void dw_dma_tasklet(unsigned long data) -{ - struct dw_dma *dw = (struct dw_dma *)data; - struct dw_dma_chan *dwc; - u32 status_xfer; - u32 status_err; - int i; - - status_xfer = dma_readl(dw, RAW.XFER); - status_err = dma_readl(dw, RAW.ERROR); - - dev_vdbg(dw->dma.dev, "%s: status_err=%x\n", __func__, status_err); - - for (i = 0; i < dw->dma.chancnt; i++) { - dwc = &dw->chan[i]; - if (test_bit(DW_DMA_IS_CYCLIC, &dwc->flags)) - dwc_handle_cyclic(dw, dwc, status_err, status_xfer); - else if (status_err & (1 << i)) - dwc_handle_error(dw, dwc); - else if (status_xfer & (1 << i)) - dwc_scan_descriptors(dw, dwc); - } - - /* - * Re-enable interrupts. - */ - channel_set_bit(dw, MASK.XFER, dw->all_chan_mask); - channel_set_bit(dw, MASK.ERROR, dw->all_chan_mask); -} - -static irqreturn_t dw_dma_interrupt(int irq, void *dev_id) -{ - struct dw_dma *dw = dev_id; - u32 status; - - dev_vdbg(dw->dma.dev, "%s: status=0x%x\n", __func__, - dma_readl(dw, STATUS_INT)); - - /* - * Just disable the interrupts. We'll turn them back on in the - * softirq handler. - */ - channel_clear_bit(dw, MASK.XFER, dw->all_chan_mask); - channel_clear_bit(dw, MASK.ERROR, dw->all_chan_mask); - - status = dma_readl(dw, STATUS_INT); - if (status) { - dev_err(dw->dma.dev, - "BUG: Unexpected interrupts pending: 0x%x\n", - status); - - /* Try to recover */ - channel_clear_bit(dw, MASK.XFER, (1 << 8) - 1); - channel_clear_bit(dw, MASK.SRC_TRAN, (1 << 8) - 1); - channel_clear_bit(dw, MASK.DST_TRAN, (1 << 8) - 1); - channel_clear_bit(dw, MASK.ERROR, (1 << 8) - 1); - } - - tasklet_schedule(&dw->tasklet); - - return IRQ_HANDLED; -} - -/*----------------------------------------------------------------------*/ - -static dma_cookie_t dwc_tx_submit(struct dma_async_tx_descriptor *tx) -{ - struct dw_desc *desc = txd_to_dw_desc(tx); - struct dw_dma_chan *dwc = to_dw_dma_chan(tx->chan); - dma_cookie_t cookie; - unsigned long flags; - - spin_lock_irqsave(&dwc->lock, flags); - cookie = dma_cookie_assign(tx); - - /* - * REVISIT: We should attempt to chain as many descriptors as - * possible, perhaps even appending to those already submitted - * for DMA. But this is hard to do in a race-free manner. - */ - if (list_empty(&dwc->active_list)) { - dev_vdbg(chan2dev(tx->chan), "%s: started %u\n", __func__, - desc->txd.cookie); - list_add_tail(&desc->desc_node, &dwc->active_list); - dwc_dostart(dwc, dwc_first_active(dwc)); - } else { - dev_vdbg(chan2dev(tx->chan), "%s: queued %u\n", __func__, - desc->txd.cookie); - - list_add_tail(&desc->desc_node, &dwc->queue); - } - - spin_unlock_irqrestore(&dwc->lock, flags); - - return cookie; -} - -static struct dma_async_tx_descriptor * -dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, - size_t len, unsigned long flags) -{ - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dw_dma *dw = to_dw_dma(chan->device); - struct dw_desc *desc; - struct dw_desc *first; - struct dw_desc *prev; - size_t xfer_count; - size_t offset; - unsigned int src_width; - unsigned int dst_width; - unsigned int data_width; - u32 ctllo; - - dev_vdbg(chan2dev(chan), - "%s: d0x%llx s0x%llx l0x%zx f0x%lx\n", __func__, - (unsigned long long)dest, (unsigned long long)src, - len, flags); - - if (unlikely(!len)) { - dev_dbg(chan2dev(chan), "%s: length is zero!\n", __func__); - return NULL; - } - - dwc->direction = DMA_MEM_TO_MEM; - - data_width = min_t(unsigned int, dw->data_width[dwc->src_master], - dw->data_width[dwc->dst_master]); - - src_width = dst_width = min_t(unsigned int, data_width, - dwc_fast_fls(src | dest | len)); - - ctllo = DWC_DEFAULT_CTLLO(chan) - | DWC_CTLL_DST_WIDTH(dst_width) - | DWC_CTLL_SRC_WIDTH(src_width) - | DWC_CTLL_DST_INC - | DWC_CTLL_SRC_INC - | DWC_CTLL_FC_M2M; - prev = first = NULL; - - for (offset = 0; offset < len; offset += xfer_count << src_width) { - xfer_count = min_t(size_t, (len - offset) >> src_width, - dwc->block_size); - - desc = dwc_desc_get(dwc); - if (!desc) - goto err_desc_get; - - desc->lli.sar = src + offset; - desc->lli.dar = dest + offset; - desc->lli.ctllo = ctllo; - desc->lli.ctlhi = xfer_count; - desc->len = xfer_count << src_width; - - if (!first) { - first = desc; - } else { - prev->lli.llp = desc->txd.phys; - list_add_tail(&desc->desc_node, - &first->tx_list); - } - prev = desc; - } - - if (flags & DMA_PREP_INTERRUPT) - /* Trigger interrupt after last block */ - prev->lli.ctllo |= DWC_CTLL_INT_EN; - - prev->lli.llp = 0; - first->txd.flags = flags; - first->total_len = len; - - return &first->txd; - -err_desc_get: - dwc_desc_put(dwc, first); - return NULL; -} - -static struct dma_async_tx_descriptor * -dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, - unsigned int sg_len, enum dma_transfer_direction direction, - unsigned long flags, void *context) -{ - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dw_dma *dw = to_dw_dma(chan->device); - struct dma_slave_config *sconfig = &dwc->dma_sconfig; - struct dw_desc *prev; - struct dw_desc *first; - u32 ctllo; - dma_addr_t reg; - unsigned int reg_width; - unsigned int mem_width; - unsigned int data_width; - unsigned int i; - struct scatterlist *sg; - size_t total_len = 0; - - dev_vdbg(chan2dev(chan), "%s\n", __func__); - - if (unlikely(!is_slave_direction(direction) || !sg_len)) - return NULL; - - dwc->direction = direction; - - prev = first = NULL; - - switch (direction) { - case DMA_MEM_TO_DEV: - reg_width = __fls(sconfig->dst_addr_width); - reg = sconfig->dst_addr; - ctllo = (DWC_DEFAULT_CTLLO(chan) - | DWC_CTLL_DST_WIDTH(reg_width) - | DWC_CTLL_DST_FIX - | DWC_CTLL_SRC_INC); - - ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_M2P) : - DWC_CTLL_FC(DW_DMA_FC_D_M2P); - - data_width = dw->data_width[dwc->src_master]; - - for_each_sg(sgl, sg, sg_len, i) { - struct dw_desc *desc; - u32 len, dlen, mem; - - mem = sg_dma_address(sg); - len = sg_dma_len(sg); - - mem_width = min_t(unsigned int, - data_width, dwc_fast_fls(mem | len)); - -slave_sg_todev_fill_desc: - desc = dwc_desc_get(dwc); - if (!desc) { - dev_err(chan2dev(chan), - "not enough descriptors available\n"); - goto err_desc_get; - } - - desc->lli.sar = mem; - desc->lli.dar = reg; - desc->lli.ctllo = ctllo | DWC_CTLL_SRC_WIDTH(mem_width); - if ((len >> mem_width) > dwc->block_size) { - dlen = dwc->block_size << mem_width; - mem += dlen; - len -= dlen; - } else { - dlen = len; - len = 0; - } - - desc->lli.ctlhi = dlen >> mem_width; - desc->len = dlen; - - if (!first) { - first = desc; - } else { - prev->lli.llp = desc->txd.phys; - list_add_tail(&desc->desc_node, - &first->tx_list); - } - prev = desc; - total_len += dlen; - - if (len) - goto slave_sg_todev_fill_desc; - } - break; - case DMA_DEV_TO_MEM: - reg_width = __fls(sconfig->src_addr_width); - reg = sconfig->src_addr; - ctllo = (DWC_DEFAULT_CTLLO(chan) - | DWC_CTLL_SRC_WIDTH(reg_width) - | DWC_CTLL_DST_INC - | DWC_CTLL_SRC_FIX); - - ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_P2M) : - DWC_CTLL_FC(DW_DMA_FC_D_P2M); - - data_width = dw->data_width[dwc->dst_master]; - - for_each_sg(sgl, sg, sg_len, i) { - struct dw_desc *desc; - u32 len, dlen, mem; - - mem = sg_dma_address(sg); - len = sg_dma_len(sg); - - mem_width = min_t(unsigned int, - data_width, dwc_fast_fls(mem | len)); - -slave_sg_fromdev_fill_desc: - desc = dwc_desc_get(dwc); - if (!desc) { - dev_err(chan2dev(chan), - "not enough descriptors available\n"); - goto err_desc_get; - } - - desc->lli.sar = reg; - desc->lli.dar = mem; - desc->lli.ctllo = ctllo | DWC_CTLL_DST_WIDTH(mem_width); - if ((len >> reg_width) > dwc->block_size) { - dlen = dwc->block_size << reg_width; - mem += dlen; - len -= dlen; - } else { - dlen = len; - len = 0; - } - desc->lli.ctlhi = dlen >> reg_width; - desc->len = dlen; - - if (!first) { - first = desc; - } else { - prev->lli.llp = desc->txd.phys; - list_add_tail(&desc->desc_node, - &first->tx_list); - } - prev = desc; - total_len += dlen; - - if (len) - goto slave_sg_fromdev_fill_desc; - } - break; - default: - return NULL; - } - - if (flags & DMA_PREP_INTERRUPT) - /* Trigger interrupt after last block */ - prev->lli.ctllo |= DWC_CTLL_INT_EN; - - prev->lli.llp = 0; - first->total_len = total_len; - - return &first->txd; - -err_desc_get: - dwc_desc_put(dwc, first); - return NULL; -} - -/* - * Fix sconfig's burst size according to dw_dmac. We need to convert them as: - * 1 -> 0, 4 -> 1, 8 -> 2, 16 -> 3. - * - * NOTE: burst size 2 is not supported by controller. - * - * This can be done by finding least significant bit set: n & (n - 1) - */ -static inline void convert_burst(u32 *maxburst) -{ - if (*maxburst > 1) - *maxburst = fls(*maxburst) - 2; - else - *maxburst = 0; -} - -static int -set_runtime_config(struct dma_chan *chan, struct dma_slave_config *sconfig) -{ - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - - /* Check if chan will be configured for slave transfers */ - if (!is_slave_direction(sconfig->direction)) - return -EINVAL; - - memcpy(&dwc->dma_sconfig, sconfig, sizeof(*sconfig)); - dwc->direction = sconfig->direction; - - /* Take the request line from slave_id member */ - if (dwc->request_line == ~0) - dwc->request_line = sconfig->slave_id; - - convert_burst(&dwc->dma_sconfig.src_maxburst); - convert_burst(&dwc->dma_sconfig.dst_maxburst); - - return 0; -} - -static inline void dwc_chan_pause(struct dw_dma_chan *dwc) -{ - u32 cfglo = channel_readl(dwc, CFG_LO); - unsigned int count = 20; /* timeout iterations */ - - channel_writel(dwc, CFG_LO, cfglo | DWC_CFGL_CH_SUSP); - while (!(channel_readl(dwc, CFG_LO) & DWC_CFGL_FIFO_EMPTY) && count--) - udelay(2); - - dwc->paused = true; -} - -static inline void dwc_chan_resume(struct dw_dma_chan *dwc) -{ - u32 cfglo = channel_readl(dwc, CFG_LO); - - channel_writel(dwc, CFG_LO, cfglo & ~DWC_CFGL_CH_SUSP); - - dwc->paused = false; -} - -static int dwc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - unsigned long arg) -{ - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dw_dma *dw = to_dw_dma(chan->device); - struct dw_desc *desc, *_desc; - unsigned long flags; - LIST_HEAD(list); - - if (cmd == DMA_PAUSE) { - spin_lock_irqsave(&dwc->lock, flags); - - dwc_chan_pause(dwc); - - spin_unlock_irqrestore(&dwc->lock, flags); - } else if (cmd == DMA_RESUME) { - if (!dwc->paused) - return 0; - - spin_lock_irqsave(&dwc->lock, flags); - - dwc_chan_resume(dwc); - - spin_unlock_irqrestore(&dwc->lock, flags); - } else if (cmd == DMA_TERMINATE_ALL) { - spin_lock_irqsave(&dwc->lock, flags); - - clear_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags); - - dwc_chan_disable(dw, dwc); - - dwc_chan_resume(dwc); - - /* active_list entries will end up before queued entries */ - list_splice_init(&dwc->queue, &list); - list_splice_init(&dwc->active_list, &list); - - spin_unlock_irqrestore(&dwc->lock, flags); - - /* Flush all pending and queued descriptors */ - list_for_each_entry_safe(desc, _desc, &list, desc_node) - dwc_descriptor_complete(dwc, desc, false); - } else if (cmd == DMA_SLAVE_CONFIG) { - return set_runtime_config(chan, (struct dma_slave_config *)arg); - } else { - return -ENXIO; - } - - return 0; -} - -static inline u32 dwc_get_residue(struct dw_dma_chan *dwc) -{ - unsigned long flags; - u32 residue; - - spin_lock_irqsave(&dwc->lock, flags); - - residue = dwc->residue; - if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags) && residue) - residue -= dwc_get_sent(dwc); - - spin_unlock_irqrestore(&dwc->lock, flags); - return residue; -} - -static enum dma_status -dwc_tx_status(struct dma_chan *chan, - dma_cookie_t cookie, - struct dma_tx_state *txstate) -{ - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - enum dma_status ret; - - ret = dma_cookie_status(chan, cookie, txstate); - if (ret != DMA_SUCCESS) { - dwc_scan_descriptors(to_dw_dma(chan->device), dwc); - - ret = dma_cookie_status(chan, cookie, txstate); - } - - if (ret != DMA_SUCCESS) - dma_set_residue(txstate, dwc_get_residue(dwc)); - - if (dwc->paused) - return DMA_PAUSED; - - return ret; -} - -static void dwc_issue_pending(struct dma_chan *chan) -{ - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - - if (!list_empty(&dwc->queue)) - dwc_scan_descriptors(to_dw_dma(chan->device), dwc); -} - -static int dwc_alloc_chan_resources(struct dma_chan *chan) -{ - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dw_dma *dw = to_dw_dma(chan->device); - struct dw_desc *desc; - int i; - unsigned long flags; - - dev_vdbg(chan2dev(chan), "%s\n", __func__); - - /* ASSERT: channel is idle */ - if (dma_readl(dw, CH_EN) & dwc->mask) { - dev_dbg(chan2dev(chan), "DMA channel not idle?\n"); - return -EIO; - } - - dma_cookie_init(chan); - - /* - * NOTE: some controllers may have additional features that we - * need to initialize here, like "scatter-gather" (which - * doesn't mean what you think it means), and status writeback. - */ - - dwc_set_masters(dwc); - - spin_lock_irqsave(&dwc->lock, flags); - i = dwc->descs_allocated; - while (dwc->descs_allocated < NR_DESCS_PER_CHANNEL) { - dma_addr_t phys; - - spin_unlock_irqrestore(&dwc->lock, flags); - - desc = dma_pool_alloc(dw->desc_pool, GFP_ATOMIC, &phys); - if (!desc) - goto err_desc_alloc; - - memset(desc, 0, sizeof(struct dw_desc)); - - INIT_LIST_HEAD(&desc->tx_list); - dma_async_tx_descriptor_init(&desc->txd, chan); - desc->txd.tx_submit = dwc_tx_submit; - desc->txd.flags = DMA_CTRL_ACK; - desc->txd.phys = phys; - - dwc_desc_put(dwc, desc); - - spin_lock_irqsave(&dwc->lock, flags); - i = ++dwc->descs_allocated; - } - - spin_unlock_irqrestore(&dwc->lock, flags); - - dev_dbg(chan2dev(chan), "%s: allocated %d descriptors\n", __func__, i); - - return i; - -err_desc_alloc: - dev_info(chan2dev(chan), "only allocated %d descriptors\n", i); - - return i; -} - -static void dwc_free_chan_resources(struct dma_chan *chan) -{ - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dw_dma *dw = to_dw_dma(chan->device); - struct dw_desc *desc, *_desc; - unsigned long flags; - LIST_HEAD(list); - - dev_dbg(chan2dev(chan), "%s: descs allocated=%u\n", __func__, - dwc->descs_allocated); - - /* ASSERT: channel is idle */ - BUG_ON(!list_empty(&dwc->active_list)); - BUG_ON(!list_empty(&dwc->queue)); - BUG_ON(dma_readl(to_dw_dma(chan->device), CH_EN) & dwc->mask); - - spin_lock_irqsave(&dwc->lock, flags); - list_splice_init(&dwc->free_list, &list); - dwc->descs_allocated = 0; - dwc->initialized = false; - dwc->request_line = ~0; - - /* Disable interrupts */ - channel_clear_bit(dw, MASK.XFER, dwc->mask); - channel_clear_bit(dw, MASK.ERROR, dwc->mask); - - spin_unlock_irqrestore(&dwc->lock, flags); - - list_for_each_entry_safe(desc, _desc, &list, desc_node) { - dev_vdbg(chan2dev(chan), " freeing descriptor %p\n", desc); - dma_pool_free(dw->desc_pool, desc, desc->txd.phys); - } - - dev_vdbg(chan2dev(chan), "%s: done\n", __func__); -} - -/*----------------------------------------------------------------------*/ - -struct dw_dma_of_filter_args { - struct dw_dma *dw; - unsigned int req; - unsigned int src; - unsigned int dst; -}; - -static bool dw_dma_of_filter(struct dma_chan *chan, void *param) -{ - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dw_dma_of_filter_args *fargs = param; - - /* Ensure the device matches our channel */ - if (chan->device != &fargs->dw->dma) - return false; - - dwc->request_line = fargs->req; - dwc->src_master = fargs->src; - dwc->dst_master = fargs->dst; - - return true; -} - -static struct dma_chan *dw_dma_of_xlate(struct of_phandle_args *dma_spec, - struct of_dma *ofdma) -{ - struct dw_dma *dw = ofdma->of_dma_data; - struct dw_dma_of_filter_args fargs = { - .dw = dw, - }; - dma_cap_mask_t cap; - - if (dma_spec->args_count != 3) - return NULL; - - fargs.req = dma_spec->args[0]; - fargs.src = dma_spec->args[1]; - fargs.dst = dma_spec->args[2]; - - if (WARN_ON(fargs.req >= DW_DMA_MAX_NR_REQUESTS || - fargs.src >= dw->nr_masters || - fargs.dst >= dw->nr_masters)) - return NULL; - - dma_cap_zero(cap); - dma_cap_set(DMA_SLAVE, cap); - - /* TODO: there should be a simpler way to do this */ - return dma_request_channel(cap, dw_dma_of_filter, &fargs); -} - -#ifdef CONFIG_ACPI -static bool dw_dma_acpi_filter(struct dma_chan *chan, void *param) -{ - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct acpi_dma_spec *dma_spec = param; - - if (chan->device->dev != dma_spec->dev || - chan->chan_id != dma_spec->chan_id) - return false; - - dwc->request_line = dma_spec->slave_id; - dwc->src_master = dwc_get_sms(NULL); - dwc->dst_master = dwc_get_dms(NULL); - - return true; -} - -static void dw_dma_acpi_controller_register(struct dw_dma *dw) -{ - struct device *dev = dw->dma.dev; - struct acpi_dma_filter_info *info; - int ret; - - info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); - if (!info) - return; - - dma_cap_zero(info->dma_cap); - dma_cap_set(DMA_SLAVE, info->dma_cap); - info->filter_fn = dw_dma_acpi_filter; - - ret = devm_acpi_dma_controller_register(dev, acpi_dma_simple_xlate, - info); - if (ret) - dev_err(dev, "could not register acpi_dma_controller\n"); -} -#else /* !CONFIG_ACPI */ -static inline void dw_dma_acpi_controller_register(struct dw_dma *dw) {} -#endif /* !CONFIG_ACPI */ - -/* --------------------- Cyclic DMA API extensions -------------------- */ - -/** - * dw_dma_cyclic_start - start the cyclic DMA transfer - * @chan: the DMA channel to start - * - * Must be called with soft interrupts disabled. Returns zero on success or - * -errno on failure. - */ -int dw_dma_cyclic_start(struct dma_chan *chan) -{ - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dw_dma *dw = to_dw_dma(dwc->chan.device); - unsigned long flags; - - if (!test_bit(DW_DMA_IS_CYCLIC, &dwc->flags)) { - dev_err(chan2dev(&dwc->chan), "missing prep for cyclic DMA\n"); - return -ENODEV; - } - - spin_lock_irqsave(&dwc->lock, flags); - - /* Assert channel is idle */ - if (dma_readl(dw, CH_EN) & dwc->mask) { - dev_err(chan2dev(&dwc->chan), - "BUG: Attempted to start non-idle channel\n"); - dwc_dump_chan_regs(dwc); - spin_unlock_irqrestore(&dwc->lock, flags); - return -EBUSY; - } - - dma_writel(dw, CLEAR.ERROR, dwc->mask); - dma_writel(dw, CLEAR.XFER, dwc->mask); - - /* Setup DMAC channel registers */ - channel_writel(dwc, LLP, dwc->cdesc->desc[0]->txd.phys); - channel_writel(dwc, CTL_LO, DWC_CTLL_LLP_D_EN | DWC_CTLL_LLP_S_EN); - channel_writel(dwc, CTL_HI, 0); - - channel_set_bit(dw, CH_EN, dwc->mask); - - spin_unlock_irqrestore(&dwc->lock, flags); - - return 0; -} -EXPORT_SYMBOL(dw_dma_cyclic_start); - -/** - * dw_dma_cyclic_stop - stop the cyclic DMA transfer - * @chan: the DMA channel to stop - * - * Must be called with soft interrupts disabled. - */ -void dw_dma_cyclic_stop(struct dma_chan *chan) -{ - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dw_dma *dw = to_dw_dma(dwc->chan.device); - unsigned long flags; - - spin_lock_irqsave(&dwc->lock, flags); - - dwc_chan_disable(dw, dwc); - - spin_unlock_irqrestore(&dwc->lock, flags); -} -EXPORT_SYMBOL(dw_dma_cyclic_stop); - -/** - * dw_dma_cyclic_prep - prepare the cyclic DMA transfer - * @chan: the DMA channel to prepare - * @buf_addr: physical DMA address where the buffer starts - * @buf_len: total number of bytes for the entire buffer - * @period_len: number of bytes for each period - * @direction: transfer direction, to or from device - * - * Must be called before trying to start the transfer. Returns a valid struct - * dw_cyclic_desc if successful or an ERR_PTR(-errno) if not successful. - */ -struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, - dma_addr_t buf_addr, size_t buf_len, size_t period_len, - enum dma_transfer_direction direction) -{ - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dma_slave_config *sconfig = &dwc->dma_sconfig; - struct dw_cyclic_desc *cdesc; - struct dw_cyclic_desc *retval = NULL; - struct dw_desc *desc; - struct dw_desc *last = NULL; - unsigned long was_cyclic; - unsigned int reg_width; - unsigned int periods; - unsigned int i; - unsigned long flags; - - spin_lock_irqsave(&dwc->lock, flags); - if (dwc->nollp) { - spin_unlock_irqrestore(&dwc->lock, flags); - dev_dbg(chan2dev(&dwc->chan), - "channel doesn't support LLP transfers\n"); - return ERR_PTR(-EINVAL); - } - - if (!list_empty(&dwc->queue) || !list_empty(&dwc->active_list)) { - spin_unlock_irqrestore(&dwc->lock, flags); - dev_dbg(chan2dev(&dwc->chan), - "queue and/or active list are not empty\n"); - return ERR_PTR(-EBUSY); - } - - was_cyclic = test_and_set_bit(DW_DMA_IS_CYCLIC, &dwc->flags); - spin_unlock_irqrestore(&dwc->lock, flags); - if (was_cyclic) { - dev_dbg(chan2dev(&dwc->chan), - "channel already prepared for cyclic DMA\n"); - return ERR_PTR(-EBUSY); - } - - retval = ERR_PTR(-EINVAL); - - if (unlikely(!is_slave_direction(direction))) - goto out_err; - - dwc->direction = direction; - - if (direction == DMA_MEM_TO_DEV) - reg_width = __ffs(sconfig->dst_addr_width); - else - reg_width = __ffs(sconfig->src_addr_width); - - periods = buf_len / period_len; - - /* Check for too big/unaligned periods and unaligned DMA buffer. */ - if (period_len > (dwc->block_size << reg_width)) - goto out_err; - if (unlikely(period_len & ((1 << reg_width) - 1))) - goto out_err; - if (unlikely(buf_addr & ((1 << reg_width) - 1))) - goto out_err; - - retval = ERR_PTR(-ENOMEM); - - if (periods > NR_DESCS_PER_CHANNEL) - goto out_err; - - cdesc = kzalloc(sizeof(struct dw_cyclic_desc), GFP_KERNEL); - if (!cdesc) - goto out_err; - - cdesc->desc = kzalloc(sizeof(struct dw_desc *) * periods, GFP_KERNEL); - if (!cdesc->desc) - goto out_err_alloc; - - for (i = 0; i < periods; i++) { - desc = dwc_desc_get(dwc); - if (!desc) - goto out_err_desc_get; - - switch (direction) { - case DMA_MEM_TO_DEV: - desc->lli.dar = sconfig->dst_addr; - desc->lli.sar = buf_addr + (period_len * i); - desc->lli.ctllo = (DWC_DEFAULT_CTLLO(chan) - | DWC_CTLL_DST_WIDTH(reg_width) - | DWC_CTLL_SRC_WIDTH(reg_width) - | DWC_CTLL_DST_FIX - | DWC_CTLL_SRC_INC - | DWC_CTLL_INT_EN); - - desc->lli.ctllo |= sconfig->device_fc ? - DWC_CTLL_FC(DW_DMA_FC_P_M2P) : - DWC_CTLL_FC(DW_DMA_FC_D_M2P); - - break; - case DMA_DEV_TO_MEM: - desc->lli.dar = buf_addr + (period_len * i); - desc->lli.sar = sconfig->src_addr; - desc->lli.ctllo = (DWC_DEFAULT_CTLLO(chan) - | DWC_CTLL_SRC_WIDTH(reg_width) - | DWC_CTLL_DST_WIDTH(reg_width) - | DWC_CTLL_DST_INC - | DWC_CTLL_SRC_FIX - | DWC_CTLL_INT_EN); - - desc->lli.ctllo |= sconfig->device_fc ? - DWC_CTLL_FC(DW_DMA_FC_P_P2M) : - DWC_CTLL_FC(DW_DMA_FC_D_P2M); - - break; - default: - break; - } - - desc->lli.ctlhi = (period_len >> reg_width); - cdesc->desc[i] = desc; - - if (last) - last->lli.llp = desc->txd.phys; - - last = desc; - } - - /* Let's make a cyclic list */ - last->lli.llp = cdesc->desc[0]->txd.phys; - - dev_dbg(chan2dev(&dwc->chan), "cyclic prepared buf 0x%llx len %zu " - "period %zu periods %d\n", (unsigned long long)buf_addr, - buf_len, period_len, periods); - - cdesc->periods = periods; - dwc->cdesc = cdesc; - - return cdesc; - -out_err_desc_get: - while (i--) - dwc_desc_put(dwc, cdesc->desc[i]); -out_err_alloc: - kfree(cdesc); -out_err: - clear_bit(DW_DMA_IS_CYCLIC, &dwc->flags); - return (struct dw_cyclic_desc *)retval; -} -EXPORT_SYMBOL(dw_dma_cyclic_prep); - -/** - * dw_dma_cyclic_free - free a prepared cyclic DMA transfer - * @chan: the DMA channel to free - */ -void dw_dma_cyclic_free(struct dma_chan *chan) -{ - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dw_dma *dw = to_dw_dma(dwc->chan.device); - struct dw_cyclic_desc *cdesc = dwc->cdesc; - int i; - unsigned long flags; - - dev_dbg(chan2dev(&dwc->chan), "%s\n", __func__); - - if (!cdesc) - return; - - spin_lock_irqsave(&dwc->lock, flags); - - dwc_chan_disable(dw, dwc); - - dma_writel(dw, CLEAR.ERROR, dwc->mask); - dma_writel(dw, CLEAR.XFER, dwc->mask); - - spin_unlock_irqrestore(&dwc->lock, flags); - - for (i = 0; i < cdesc->periods; i++) - dwc_desc_put(dwc, cdesc->desc[i]); - - kfree(cdesc->desc); - kfree(cdesc); - - clear_bit(DW_DMA_IS_CYCLIC, &dwc->flags); -} -EXPORT_SYMBOL(dw_dma_cyclic_free); - -/*----------------------------------------------------------------------*/ - -static void dw_dma_off(struct dw_dma *dw) -{ - int i; - - dma_writel(dw, CFG, 0); - - channel_clear_bit(dw, MASK.XFER, dw->all_chan_mask); - channel_clear_bit(dw, MASK.SRC_TRAN, dw->all_chan_mask); - channel_clear_bit(dw, MASK.DST_TRAN, dw->all_chan_mask); - channel_clear_bit(dw, MASK.ERROR, dw->all_chan_mask); - - while (dma_readl(dw, CFG) & DW_CFG_DMA_EN) - cpu_relax(); - - for (i = 0; i < dw->dma.chancnt; i++) - dw->chan[i].initialized = false; -} - -#ifdef CONFIG_OF -static struct dw_dma_platform_data * -dw_dma_parse_dt(struct platform_device *pdev) -{ - struct device_node *np = pdev->dev.of_node; - struct dw_dma_platform_data *pdata; - u32 tmp, arr[4]; - - if (!np) { - dev_err(&pdev->dev, "Missing DT data\n"); - return NULL; - } - - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return NULL; - - if (of_property_read_u32(np, "dma-channels", &pdata->nr_channels)) - return NULL; - - if (of_property_read_bool(np, "is_private")) - pdata->is_private = true; - - if (!of_property_read_u32(np, "chan_allocation_order", &tmp)) - pdata->chan_allocation_order = (unsigned char)tmp; - - if (!of_property_read_u32(np, "chan_priority", &tmp)) - pdata->chan_priority = tmp; - - if (!of_property_read_u32(np, "block_size", &tmp)) - pdata->block_size = tmp; - - if (!of_property_read_u32(np, "dma-masters", &tmp)) { - if (tmp > 4) - return NULL; - - pdata->nr_masters = tmp; - } - - if (!of_property_read_u32_array(np, "data_width", arr, - pdata->nr_masters)) - for (tmp = 0; tmp < pdata->nr_masters; tmp++) - pdata->data_width[tmp] = arr[tmp]; - - return pdata; -} -#else -static inline struct dw_dma_platform_data * -dw_dma_parse_dt(struct platform_device *pdev) -{ - return NULL; -} -#endif - -static int dw_probe(struct platform_device *pdev) -{ - struct dw_dma_platform_data *pdata; - struct resource *io; - struct dw_dma *dw; - size_t size; - void __iomem *regs; - bool autocfg; - unsigned int dw_params; - unsigned int nr_channels; - unsigned int max_blk_size = 0; - int irq; - int err; - int i; - - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - - io = platform_get_resource(pdev, IORESOURCE_MEM, 0); - regs = devm_ioremap_resource(&pdev->dev, io); - if (IS_ERR(regs)) - return PTR_ERR(regs); - - /* Apply default dma_mask if needed */ - if (!pdev->dev.dma_mask) { - pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; - pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); - } - - dw_params = dma_read_byaddr(regs, DW_PARAMS); - autocfg = dw_params >> DW_PARAMS_EN & 0x1; - - dev_dbg(&pdev->dev, "DW_PARAMS: 0x%08x\n", dw_params); - - pdata = dev_get_platdata(&pdev->dev); - if (!pdata) - pdata = dw_dma_parse_dt(pdev); - - if (!pdata && autocfg) { - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return -ENOMEM; - - /* Fill platform data with the default values */ - pdata->is_private = true; - pdata->chan_allocation_order = CHAN_ALLOCATION_ASCENDING; - pdata->chan_priority = CHAN_PRIORITY_ASCENDING; - } else if (!pdata || pdata->nr_channels > DW_DMA_MAX_NR_CHANNELS) - return -EINVAL; - - if (autocfg) - nr_channels = (dw_params >> DW_PARAMS_NR_CHAN & 0x7) + 1; - else - nr_channels = pdata->nr_channels; - - size = sizeof(struct dw_dma) + nr_channels * sizeof(struct dw_dma_chan); - dw = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); - if (!dw) - return -ENOMEM; - - dw->clk = devm_clk_get(&pdev->dev, "hclk"); - if (IS_ERR(dw->clk)) - return PTR_ERR(dw->clk); - clk_prepare_enable(dw->clk); - - dw->regs = regs; - - /* Get hardware configuration parameters */ - if (autocfg) { - max_blk_size = dma_readl(dw, MAX_BLK_SIZE); - - dw->nr_masters = (dw_params >> DW_PARAMS_NR_MASTER & 3) + 1; - for (i = 0; i < dw->nr_masters; i++) { - dw->data_width[i] = - (dw_params >> DW_PARAMS_DATA_WIDTH(i) & 3) + 2; - } - } else { - dw->nr_masters = pdata->nr_masters; - memcpy(dw->data_width, pdata->data_width, 4); - } - - /* Calculate all channel mask before DMA setup */ - dw->all_chan_mask = (1 << nr_channels) - 1; - - /* Force dma off, just in case */ - dw_dma_off(dw); - - /* Disable BLOCK interrupts as well */ - channel_clear_bit(dw, MASK.BLOCK, dw->all_chan_mask); - - err = devm_request_irq(&pdev->dev, irq, dw_dma_interrupt, 0, - "dw_dmac", dw); - if (err) - return err; - - platform_set_drvdata(pdev, dw); - - /* Create a pool of consistent memory blocks for hardware descriptors */ - dw->desc_pool = dmam_pool_create("dw_dmac_desc_pool", &pdev->dev, - sizeof(struct dw_desc), 4, 0); - if (!dw->desc_pool) { - dev_err(&pdev->dev, "No memory for descriptors dma pool\n"); - return -ENOMEM; - } - - tasklet_init(&dw->tasklet, dw_dma_tasklet, (unsigned long)dw); - - INIT_LIST_HEAD(&dw->dma.channels); - for (i = 0; i < nr_channels; i++) { - struct dw_dma_chan *dwc = &dw->chan[i]; - int r = nr_channels - i - 1; - - dwc->chan.device = &dw->dma; - dma_cookie_init(&dwc->chan); - if (pdata->chan_allocation_order == CHAN_ALLOCATION_ASCENDING) - list_add_tail(&dwc->chan.device_node, - &dw->dma.channels); - else - list_add(&dwc->chan.device_node, &dw->dma.channels); - - /* 7 is highest priority & 0 is lowest. */ - if (pdata->chan_priority == CHAN_PRIORITY_ASCENDING) - dwc->priority = r; - else - dwc->priority = i; - - dwc->ch_regs = &__dw_regs(dw)->CHAN[i]; - spin_lock_init(&dwc->lock); - dwc->mask = 1 << i; - - INIT_LIST_HEAD(&dwc->active_list); - INIT_LIST_HEAD(&dwc->queue); - INIT_LIST_HEAD(&dwc->free_list); - - channel_clear_bit(dw, CH_EN, dwc->mask); - - dwc->direction = DMA_TRANS_NONE; - dwc->request_line = ~0; - - /* Hardware configuration */ - if (autocfg) { - unsigned int dwc_params; - - dwc_params = dma_read_byaddr(regs + r * sizeof(u32), - DWC_PARAMS); - - dev_dbg(&pdev->dev, "DWC_PARAMS[%d]: 0x%08x\n", i, - dwc_params); - - /* Decode maximum block size for given channel. The - * stored 4 bit value represents blocks from 0x00 for 3 - * up to 0x0a for 4095. */ - dwc->block_size = - (4 << ((max_blk_size >> 4 * i) & 0xf)) - 1; - dwc->nollp = - (dwc_params >> DWC_PARAMS_MBLK_EN & 0x1) == 0; - } else { - dwc->block_size = pdata->block_size; - - /* Check if channel supports multi block transfer */ - channel_writel(dwc, LLP, 0xfffffffc); - dwc->nollp = - (channel_readl(dwc, LLP) & 0xfffffffc) == 0; - channel_writel(dwc, LLP, 0); - } - } - - /* Clear all interrupts on all channels. */ - dma_writel(dw, CLEAR.XFER, dw->all_chan_mask); - dma_writel(dw, CLEAR.BLOCK, dw->all_chan_mask); - dma_writel(dw, CLEAR.SRC_TRAN, dw->all_chan_mask); - dma_writel(dw, CLEAR.DST_TRAN, dw->all_chan_mask); - dma_writel(dw, CLEAR.ERROR, dw->all_chan_mask); - - dma_cap_set(DMA_MEMCPY, dw->dma.cap_mask); - dma_cap_set(DMA_SLAVE, dw->dma.cap_mask); - if (pdata->is_private) - dma_cap_set(DMA_PRIVATE, dw->dma.cap_mask); - dw->dma.dev = &pdev->dev; - dw->dma.device_alloc_chan_resources = dwc_alloc_chan_resources; - dw->dma.device_free_chan_resources = dwc_free_chan_resources; - - dw->dma.device_prep_dma_memcpy = dwc_prep_dma_memcpy; - - dw->dma.device_prep_slave_sg = dwc_prep_slave_sg; - dw->dma.device_control = dwc_control; - - dw->dma.device_tx_status = dwc_tx_status; - dw->dma.device_issue_pending = dwc_issue_pending; - - dma_writel(dw, CFG, DW_CFG_DMA_EN); - - dev_info(&pdev->dev, "DesignWare DMA Controller, %d channels\n", - nr_channels); - - dma_async_device_register(&dw->dma); - - if (pdev->dev.of_node) { - err = of_dma_controller_register(pdev->dev.of_node, - dw_dma_of_xlate, dw); - if (err) - dev_err(&pdev->dev, - "could not register of_dma_controller\n"); - } - - if (ACPI_HANDLE(&pdev->dev)) - dw_dma_acpi_controller_register(dw); - - return 0; -} - -static int dw_remove(struct platform_device *pdev) -{ - struct dw_dma *dw = platform_get_drvdata(pdev); - struct dw_dma_chan *dwc, *_dwc; - - if (pdev->dev.of_node) - of_dma_controller_free(pdev->dev.of_node); - dw_dma_off(dw); - dma_async_device_unregister(&dw->dma); - - tasklet_kill(&dw->tasklet); - - list_for_each_entry_safe(dwc, _dwc, &dw->dma.channels, - chan.device_node) { - list_del(&dwc->chan.device_node); - channel_clear_bit(dw, CH_EN, dwc->mask); - } - - return 0; -} - -static void dw_shutdown(struct platform_device *pdev) -{ - struct dw_dma *dw = platform_get_drvdata(pdev); - - dw_dma_off(dw); - clk_disable_unprepare(dw->clk); -} - -static int dw_suspend_noirq(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct dw_dma *dw = platform_get_drvdata(pdev); - - dw_dma_off(dw); - clk_disable_unprepare(dw->clk); - - return 0; -} - -static int dw_resume_noirq(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct dw_dma *dw = platform_get_drvdata(pdev); - - clk_prepare_enable(dw->clk); - dma_writel(dw, CFG, DW_CFG_DMA_EN); - - return 0; -} - -static const struct dev_pm_ops dw_dev_pm_ops = { - .suspend_noirq = dw_suspend_noirq, - .resume_noirq = dw_resume_noirq, - .freeze_noirq = dw_suspend_noirq, - .thaw_noirq = dw_resume_noirq, - .restore_noirq = dw_resume_noirq, - .poweroff_noirq = dw_suspend_noirq, -}; - -#ifdef CONFIG_OF -static const struct of_device_id dw_dma_of_id_table[] = { - { .compatible = "snps,dma-spear1340" }, - {} -}; -MODULE_DEVICE_TABLE(of, dw_dma_of_id_table); -#endif - -#ifdef CONFIG_ACPI -static const struct acpi_device_id dw_dma_acpi_id_table[] = { - { "INTL9C60", 0 }, - { } -}; -#endif - -static struct platform_driver dw_driver = { - .probe = dw_probe, - .remove = dw_remove, - .shutdown = dw_shutdown, - .driver = { - .name = "dw_dmac", - .pm = &dw_dev_pm_ops, - .of_match_table = of_match_ptr(dw_dma_of_id_table), - .acpi_match_table = ACPI_PTR(dw_dma_acpi_id_table), - }, -}; - -static int __init dw_init(void) -{ - return platform_driver_register(&dw_driver); -} -subsys_initcall(dw_init); - -static void __exit dw_exit(void) -{ - platform_driver_unregister(&dw_driver); -} -module_exit(dw_exit); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("Synopsys DesignWare DMA Controller driver"); -MODULE_AUTHOR("Haavard Skinnemoen (Atmel)"); -MODULE_AUTHOR("Viresh Kumar "); diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h deleted file mode 100644 index 9d417200bd57..000000000000 --- a/drivers/dma/dw_dmac_regs.h +++ /dev/null @@ -1,311 +0,0 @@ -/* - * Driver for the Synopsys DesignWare AHB DMA Controller - * - * Copyright (C) 2005-2007 Atmel Corporation - * Copyright (C) 2010-2011 ST Microelectronics - * - * 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 -#include - -#define DW_DMA_MAX_NR_CHANNELS 8 -#define DW_DMA_MAX_NR_REQUESTS 16 - -/* flow controller */ -enum dw_dma_fc { - DW_DMA_FC_D_M2M, - DW_DMA_FC_D_M2P, - DW_DMA_FC_D_P2M, - DW_DMA_FC_D_P2P, - DW_DMA_FC_P_P2M, - DW_DMA_FC_SP_P2P, - DW_DMA_FC_P_M2P, - DW_DMA_FC_DP_P2P, -}; - -/* - * Redefine this macro to handle differences between 32- and 64-bit - * addressing, big vs. little endian, etc. - */ -#define DW_REG(name) u32 name; u32 __pad_##name - -/* Hardware register definitions. */ -struct dw_dma_chan_regs { - DW_REG(SAR); /* Source Address Register */ - DW_REG(DAR); /* Destination Address Register */ - DW_REG(LLP); /* Linked List Pointer */ - u32 CTL_LO; /* Control Register Low */ - u32 CTL_HI; /* Control Register High */ - DW_REG(SSTAT); - DW_REG(DSTAT); - DW_REG(SSTATAR); - DW_REG(DSTATAR); - u32 CFG_LO; /* Configuration Register Low */ - u32 CFG_HI; /* Configuration Register High */ - DW_REG(SGR); - DW_REG(DSR); -}; - -struct dw_dma_irq_regs { - DW_REG(XFER); - DW_REG(BLOCK); - DW_REG(SRC_TRAN); - DW_REG(DST_TRAN); - DW_REG(ERROR); -}; - -struct dw_dma_regs { - /* per-channel registers */ - struct dw_dma_chan_regs CHAN[DW_DMA_MAX_NR_CHANNELS]; - - /* irq handling */ - struct dw_dma_irq_regs RAW; /* r */ - struct dw_dma_irq_regs STATUS; /* r (raw & mask) */ - struct dw_dma_irq_regs MASK; /* rw (set = irq enabled) */ - struct dw_dma_irq_regs CLEAR; /* w (ack, affects "raw") */ - - DW_REG(STATUS_INT); /* r */ - - /* software handshaking */ - DW_REG(REQ_SRC); - DW_REG(REQ_DST); - DW_REG(SGL_REQ_SRC); - DW_REG(SGL_REQ_DST); - DW_REG(LAST_SRC); - DW_REG(LAST_DST); - - /* miscellaneous */ - DW_REG(CFG); - DW_REG(CH_EN); - DW_REG(ID); - DW_REG(TEST); - - /* reserved */ - DW_REG(__reserved0); - DW_REG(__reserved1); - - /* optional encoded params, 0x3c8..0x3f7 */ - u32 __reserved; - - /* per-channel configuration registers */ - u32 DWC_PARAMS[DW_DMA_MAX_NR_CHANNELS]; - u32 MULTI_BLK_TYPE; - u32 MAX_BLK_SIZE; - - /* top-level parameters */ - u32 DW_PARAMS; -}; - -#ifdef CONFIG_DW_DMAC_BIG_ENDIAN_IO -#define dma_readl_native ioread32be -#define dma_writel_native iowrite32be -#else -#define dma_readl_native readl -#define dma_writel_native writel -#endif - -/* To access the registers in early stage of probe */ -#define dma_read_byaddr(addr, name) \ - dma_readl_native((addr) + offsetof(struct dw_dma_regs, name)) - -/* Bitfields in DW_PARAMS */ -#define DW_PARAMS_NR_CHAN 8 /* number of channels */ -#define DW_PARAMS_NR_MASTER 11 /* number of AHB masters */ -#define DW_PARAMS_DATA_WIDTH(n) (15 + 2 * (n)) -#define DW_PARAMS_DATA_WIDTH1 15 /* master 1 data width */ -#define DW_PARAMS_DATA_WIDTH2 17 /* master 2 data width */ -#define DW_PARAMS_DATA_WIDTH3 19 /* master 3 data width */ -#define DW_PARAMS_DATA_WIDTH4 21 /* master 4 data width */ -#define DW_PARAMS_EN 28 /* encoded parameters */ - -/* Bitfields in DWC_PARAMS */ -#define DWC_PARAMS_MBLK_EN 11 /* multi block transfer */ - -/* Bitfields in CTL_LO */ -#define DWC_CTLL_INT_EN (1 << 0) /* irqs enabled? */ -#define DWC_CTLL_DST_WIDTH(n) ((n)<<1) /* bytes per element */ -#define DWC_CTLL_SRC_WIDTH(n) ((n)<<4) -#define DWC_CTLL_DST_INC (0<<7) /* DAR update/not */ -#define DWC_CTLL_DST_DEC (1<<7) -#define DWC_CTLL_DST_FIX (2<<7) -#define DWC_CTLL_SRC_INC (0<<7) /* SAR update/not */ -#define DWC_CTLL_SRC_DEC (1<<9) -#define DWC_CTLL_SRC_FIX (2<<9) -#define DWC_CTLL_DST_MSIZE(n) ((n)<<11) /* burst, #elements */ -#define DWC_CTLL_SRC_MSIZE(n) ((n)<<14) -#define DWC_CTLL_S_GATH_EN (1 << 17) /* src gather, !FIX */ -#define DWC_CTLL_D_SCAT_EN (1 << 18) /* dst scatter, !FIX */ -#define DWC_CTLL_FC(n) ((n) << 20) -#define DWC_CTLL_FC_M2M (0 << 20) /* mem-to-mem */ -#define DWC_CTLL_FC_M2P (1 << 20) /* mem-to-periph */ -#define DWC_CTLL_FC_P2M (2 << 20) /* periph-to-mem */ -#define DWC_CTLL_FC_P2P (3 << 20) /* periph-to-periph */ -/* plus 4 transfer types for peripheral-as-flow-controller */ -#define DWC_CTLL_DMS(n) ((n)<<23) /* dst master select */ -#define DWC_CTLL_SMS(n) ((n)<<25) /* src master select */ -#define DWC_CTLL_LLP_D_EN (1 << 27) /* dest block chain */ -#define DWC_CTLL_LLP_S_EN (1 << 28) /* src block chain */ - -/* Bitfields in CTL_HI */ -#define DWC_CTLH_DONE 0x00001000 -#define DWC_CTLH_BLOCK_TS_MASK 0x00000fff - -/* Bitfields in CFG_LO. Platform-configurable bits are in */ -#define DWC_CFGL_CH_PRIOR_MASK (0x7 << 5) /* priority mask */ -#define DWC_CFGL_CH_PRIOR(x) ((x) << 5) /* priority */ -#define DWC_CFGL_CH_SUSP (1 << 8) /* pause xfer */ -#define DWC_CFGL_FIFO_EMPTY (1 << 9) /* pause xfer */ -#define DWC_CFGL_HS_DST (1 << 10) /* handshake w/dst */ -#define DWC_CFGL_HS_SRC (1 << 11) /* handshake w/src */ -#define DWC_CFGL_MAX_BURST(x) ((x) << 20) -#define DWC_CFGL_RELOAD_SAR (1 << 30) -#define DWC_CFGL_RELOAD_DAR (1 << 31) - -/* Bitfields in CFG_HI. Platform-configurable bits are in */ -#define DWC_CFGH_DS_UPD_EN (1 << 5) -#define DWC_CFGH_SS_UPD_EN (1 << 6) - -/* Bitfields in SGR */ -#define DWC_SGR_SGI(x) ((x) << 0) -#define DWC_SGR_SGC(x) ((x) << 20) - -/* Bitfields in DSR */ -#define DWC_DSR_DSI(x) ((x) << 0) -#define DWC_DSR_DSC(x) ((x) << 20) - -/* Bitfields in CFG */ -#define DW_CFG_DMA_EN (1 << 0) - -enum dw_dmac_flags { - DW_DMA_IS_CYCLIC = 0, - DW_DMA_IS_SOFT_LLP = 1, -}; - -struct dw_dma_chan { - struct dma_chan chan; - void __iomem *ch_regs; - u8 mask; - u8 priority; - enum dma_transfer_direction direction; - bool paused; - bool initialized; - - /* software emulation of the LLP transfers */ - struct list_head *tx_node_active; - - spinlock_t lock; - - /* these other elements are all protected by lock */ - unsigned long flags; - struct list_head active_list; - struct list_head queue; - struct list_head free_list; - u32 residue; - struct dw_cyclic_desc *cdesc; - - unsigned int descs_allocated; - - /* hardware configuration */ - unsigned int block_size; - bool nollp; - - /* custom slave configuration */ - unsigned int request_line; - unsigned char src_master; - unsigned char dst_master; - - /* configuration passed via DMA_SLAVE_CONFIG */ - struct dma_slave_config dma_sconfig; -}; - -static inline struct dw_dma_chan_regs __iomem * -__dwc_regs(struct dw_dma_chan *dwc) -{ - return dwc->ch_regs; -} - -#define channel_readl(dwc, name) \ - dma_readl_native(&(__dwc_regs(dwc)->name)) -#define channel_writel(dwc, name, val) \ - dma_writel_native((val), &(__dwc_regs(dwc)->name)) - -static inline struct dw_dma_chan *to_dw_dma_chan(struct dma_chan *chan) -{ - return container_of(chan, struct dw_dma_chan, chan); -} - -struct dw_dma { - struct dma_device dma; - void __iomem *regs; - struct dma_pool *desc_pool; - struct tasklet_struct tasklet; - struct clk *clk; - - u8 all_chan_mask; - - /* hardware configuration */ - unsigned char nr_masters; - unsigned char data_width[4]; - - struct dw_dma_chan chan[0]; -}; - -static inline struct dw_dma_regs __iomem *__dw_regs(struct dw_dma *dw) -{ - return dw->regs; -} - -#define dma_readl(dw, name) \ - dma_readl_native(&(__dw_regs(dw)->name)) -#define dma_writel(dw, name, val) \ - dma_writel_native((val), &(__dw_regs(dw)->name)) - -#define channel_set_bit(dw, reg, mask) \ - dma_writel(dw, reg, ((mask) << 8) | (mask)) -#define channel_clear_bit(dw, reg, mask) \ - dma_writel(dw, reg, ((mask) << 8) | 0) - -static inline struct dw_dma *to_dw_dma(struct dma_device *ddev) -{ - return container_of(ddev, struct dw_dma, dma); -} - -/* LLI == Linked List Item; a.k.a. DMA block descriptor */ -struct dw_lli { - /* values that are not changed by hardware */ - u32 sar; - u32 dar; - u32 llp; /* chain to next lli */ - u32 ctllo; - /* values that may get written back: */ - u32 ctlhi; - /* sstat and dstat can snapshot peripheral register state. - * silicon config may discard either or both... - */ - u32 sstat; - u32 dstat; -}; - -struct dw_desc { - /* FIRST values the hardware uses */ - struct dw_lli lli; - - /* THEN values for driver housekeeping */ - struct list_head desc_node; - struct list_head tx_list; - struct dma_async_tx_descriptor txd; - size_t len; - size_t total_len; -}; - -#define to_dw_desc(h) list_entry(h, struct dw_desc, desc_node) - -static inline struct dw_desc * -txd_to_dw_desc(struct dma_async_tx_descriptor *txd) -{ - return container_of(txd, struct dw_desc, txd); -} -- cgit v1.2.3 From 9cade1a46c77dfc96d57a3ea6354e95b2a7fcf61 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 5 Jun 2013 15:26:45 +0300 Subject: dma: dw: split driver to library part and platform code To simplify the driver development let's split driver to library and platform code parts. It helps us to add PCI driver in future. Signed-off-by: Andy Shevchenko Acked-by: Arnd Bergmann Acked-by: Viresh Kumar [Fixed compile error and few checkpatch issues] Signed-off-by: Vinod Koul --- drivers/dma/Makefile | 2 +- drivers/dma/dw/Kconfig | 8 +- drivers/dma/dw/Makefile | 6 +- drivers/dma/dw/core.c | 1730 ++++++++++++++++++++++++++++++++++++ drivers/dma/dw/dw_dmac.c | 1969 ----------------------------------------- drivers/dma/dw/dw_dmac_regs.h | 311 ------- drivers/dma/dw/internal.h | 70 ++ drivers/dma/dw/platform.c | 317 +++++++ drivers/dma/dw/regs.h | 312 +++++++ 9 files changed, 2441 insertions(+), 2284 deletions(-) create mode 100644 drivers/dma/dw/core.c delete mode 100644 drivers/dma/dw/dw_dmac.c delete mode 100644 drivers/dma/dw/dw_dmac_regs.h create mode 100644 drivers/dma/dw/internal.h create mode 100644 drivers/dma/dw/platform.c create mode 100644 drivers/dma/dw/regs.h diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index ac44ca0d468a..6e2a521fbbe3 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -15,7 +15,7 @@ obj-$(CONFIG_FSL_DMA) += fsldma.o obj-$(CONFIG_MPC512X_DMA) += mpc512x_dma.o obj-$(CONFIG_PPC_BESTCOMM) += bestcomm/ obj-$(CONFIG_MV_XOR) += mv_xor.o -obj-$(CONFIG_DW_DMAC) += dw/ +obj-$(CONFIG_DW_DMAC_CORE) += dw/ obj-$(CONFIG_AT_HDMAC) += at_hdmac.o obj-$(CONFIG_MX3_IPU) += ipu/ obj-$(CONFIG_TXX9_DMAC) += txx9dmac.o diff --git a/drivers/dma/dw/Kconfig b/drivers/dma/dw/Kconfig index 38a215af5ccc..efd9e02a58eb 100644 --- a/drivers/dma/dw/Kconfig +++ b/drivers/dma/dw/Kconfig @@ -2,10 +2,14 @@ # DMA engine configuration for dw # -config DW_DMAC +config DW_DMAC_CORE tristate "Synopsys DesignWare AHB DMA support" depends on GENERIC_HARDIRQS select DMA_ENGINE + +config DW_DMAC + tristate "Synopsys DesignWare AHB DMA platform driver" + select DW_DMAC_CORE default y if CPU_AT32AP7000 help Support the Synopsys DesignWare AHB DMA controller. This @@ -14,7 +18,7 @@ config DW_DMAC config DW_DMAC_BIG_ENDIAN_IO bool "Use big endian I/O register access" default y if AVR32 - depends on DW_DMAC + depends on DW_DMAC_CORE help Say yes here to use big endian I/O access when reading and writing to the DMA controller registers. This is needed on some platforms, diff --git a/drivers/dma/dw/Makefile b/drivers/dma/dw/Makefile index dd8d9936beef..47f36746c559 100644 --- a/drivers/dma/dw/Makefile +++ b/drivers/dma/dw/Makefile @@ -1 +1,5 @@ -obj-$(CONFIG_DW_DMAC) += dw_dmac.o +obj-$(CONFIG_DW_DMAC_CORE) += dw_dmac_core.o +dw_dmac_core-objs := core.o + +obj-$(CONFIG_DW_DMAC) += dw_dmac.o +dw_dmac-objs := platform.o diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c new file mode 100644 index 000000000000..eea479c12173 --- /dev/null +++ b/drivers/dma/dw/core.c @@ -0,0 +1,1730 @@ +/* + * Core driver for the Synopsys DesignWare DMA Controller + * + * Copyright (C) 2007-2008 Atmel Corporation + * Copyright (C) 2010-2011 ST Microelectronics + * Copyright (C) 2013 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../dmaengine.h" +#include "internal.h" + +/* + * This supports the Synopsys "DesignWare AHB Central DMA Controller", + * (DW_ahb_dmac) which is used with various AMBA 2.0 systems (not all + * of which use ARM any more). See the "Databook" from Synopsys for + * information beyond what licensees probably provide. + * + * The driver has currently been tested only with the Atmel AT32AP7000, + * which does not support descriptor writeback. + */ + +static inline void dwc_set_masters(struct dw_dma_chan *dwc) +{ + struct dw_dma *dw = to_dw_dma(dwc->chan.device); + struct dw_dma_slave *dws = dwc->chan.private; + unsigned char mmax = dw->nr_masters - 1; + + if (dwc->request_line == ~0) { + dwc->src_master = min_t(unsigned char, mmax, dwc_get_sms(dws)); + dwc->dst_master = min_t(unsigned char, mmax, dwc_get_dms(dws)); + } +} + +#define DWC_DEFAULT_CTLLO(_chan) ({ \ + struct dw_dma_chan *_dwc = to_dw_dma_chan(_chan); \ + struct dma_slave_config *_sconfig = &_dwc->dma_sconfig; \ + bool _is_slave = is_slave_direction(_dwc->direction); \ + u8 _smsize = _is_slave ? _sconfig->src_maxburst : \ + DW_DMA_MSIZE_16; \ + u8 _dmsize = _is_slave ? _sconfig->dst_maxburst : \ + DW_DMA_MSIZE_16; \ + \ + (DWC_CTLL_DST_MSIZE(_dmsize) \ + | DWC_CTLL_SRC_MSIZE(_smsize) \ + | DWC_CTLL_LLP_D_EN \ + | DWC_CTLL_LLP_S_EN \ + | DWC_CTLL_DMS(_dwc->dst_master) \ + | DWC_CTLL_SMS(_dwc->src_master)); \ + }) + +/* + * Number of descriptors to allocate for each channel. This should be + * made configurable somehow; preferably, the clients (at least the + * ones using slave transfers) should be able to give us a hint. + */ +#define NR_DESCS_PER_CHANNEL 64 + +/*----------------------------------------------------------------------*/ + +static struct device *chan2dev(struct dma_chan *chan) +{ + return &chan->dev->device; +} +static struct device *chan2parent(struct dma_chan *chan) +{ + return chan->dev->device.parent; +} + +static struct dw_desc *dwc_first_active(struct dw_dma_chan *dwc) +{ + return to_dw_desc(dwc->active_list.next); +} + +static struct dw_desc *dwc_desc_get(struct dw_dma_chan *dwc) +{ + struct dw_desc *desc, *_desc; + struct dw_desc *ret = NULL; + unsigned int i = 0; + unsigned long flags; + + spin_lock_irqsave(&dwc->lock, flags); + list_for_each_entry_safe(desc, _desc, &dwc->free_list, desc_node) { + i++; + if (async_tx_test_ack(&desc->txd)) { + list_del(&desc->desc_node); + ret = desc; + break; + } + dev_dbg(chan2dev(&dwc->chan), "desc %p not ACKed\n", desc); + } + spin_unlock_irqrestore(&dwc->lock, flags); + + dev_vdbg(chan2dev(&dwc->chan), "scanned %u descriptors on freelist\n", i); + + return ret; +} + +/* + * Move a descriptor, including any children, to the free list. + * `desc' must not be on any lists. + */ +static void dwc_desc_put(struct dw_dma_chan *dwc, struct dw_desc *desc) +{ + unsigned long flags; + + if (desc) { + struct dw_desc *child; + + spin_lock_irqsave(&dwc->lock, flags); + list_for_each_entry(child, &desc->tx_list, desc_node) + dev_vdbg(chan2dev(&dwc->chan), + "moving child desc %p to freelist\n", + child); + list_splice_init(&desc->tx_list, &dwc->free_list); + dev_vdbg(chan2dev(&dwc->chan), "moving desc %p to freelist\n", desc); + list_add(&desc->desc_node, &dwc->free_list); + spin_unlock_irqrestore(&dwc->lock, flags); + } +} + +static void dwc_initialize(struct dw_dma_chan *dwc) +{ + struct dw_dma *dw = to_dw_dma(dwc->chan.device); + struct dw_dma_slave *dws = dwc->chan.private; + u32 cfghi = DWC_CFGH_FIFO_MODE; + u32 cfglo = DWC_CFGL_CH_PRIOR(dwc->priority); + + if (dwc->initialized == true) + return; + + if (dws) { + /* + * We need controller-specific data to set up slave + * transfers. + */ + BUG_ON(!dws->dma_dev || dws->dma_dev != dw->dma.dev); + + cfghi = dws->cfg_hi; + cfglo |= dws->cfg_lo & ~DWC_CFGL_CH_PRIOR_MASK; + } else { + if (dwc->direction == DMA_MEM_TO_DEV) + cfghi = DWC_CFGH_DST_PER(dwc->request_line); + else if (dwc->direction == DMA_DEV_TO_MEM) + cfghi = DWC_CFGH_SRC_PER(dwc->request_line); + } + + channel_writel(dwc, CFG_LO, cfglo); + channel_writel(dwc, CFG_HI, cfghi); + + /* Enable interrupts */ + channel_set_bit(dw, MASK.XFER, dwc->mask); + channel_set_bit(dw, MASK.ERROR, dwc->mask); + + dwc->initialized = true; +} + +/*----------------------------------------------------------------------*/ + +static inline unsigned int dwc_fast_fls(unsigned long long v) +{ + /* + * We can be a lot more clever here, but this should take care + * of the most common optimization. + */ + if (!(v & 7)) + return 3; + else if (!(v & 3)) + return 2; + else if (!(v & 1)) + return 1; + return 0; +} + +static inline void dwc_dump_chan_regs(struct dw_dma_chan *dwc) +{ + dev_err(chan2dev(&dwc->chan), + " SAR: 0x%x DAR: 0x%x LLP: 0x%x CTL: 0x%x:%08x\n", + channel_readl(dwc, SAR), + channel_readl(dwc, DAR), + channel_readl(dwc, LLP), + channel_readl(dwc, CTL_HI), + channel_readl(dwc, CTL_LO)); +} + +static inline void dwc_chan_disable(struct dw_dma *dw, struct dw_dma_chan *dwc) +{ + channel_clear_bit(dw, CH_EN, dwc->mask); + while (dma_readl(dw, CH_EN) & dwc->mask) + cpu_relax(); +} + +/*----------------------------------------------------------------------*/ + +/* Perform single block transfer */ +static inline void dwc_do_single_block(struct dw_dma_chan *dwc, + struct dw_desc *desc) +{ + struct dw_dma *dw = to_dw_dma(dwc->chan.device); + u32 ctllo; + + /* Software emulation of LLP mode relies on interrupts to continue + * multi block transfer. */ + ctllo = desc->lli.ctllo | DWC_CTLL_INT_EN; + + channel_writel(dwc, SAR, desc->lli.sar); + channel_writel(dwc, DAR, desc->lli.dar); + channel_writel(dwc, CTL_LO, ctllo); + channel_writel(dwc, CTL_HI, desc->lli.ctlhi); + channel_set_bit(dw, CH_EN, dwc->mask); + + /* Move pointer to next descriptor */ + dwc->tx_node_active = dwc->tx_node_active->next; +} + +/* Called with dwc->lock held and bh disabled */ +static void dwc_dostart(struct dw_dma_chan *dwc, struct dw_desc *first) +{ + struct dw_dma *dw = to_dw_dma(dwc->chan.device); + unsigned long was_soft_llp; + + /* ASSERT: channel is idle */ + if (dma_readl(dw, CH_EN) & dwc->mask) { + dev_err(chan2dev(&dwc->chan), + "BUG: Attempted to start non-idle channel\n"); + dwc_dump_chan_regs(dwc); + + /* The tasklet will hopefully advance the queue... */ + return; + } + + if (dwc->nollp) { + was_soft_llp = test_and_set_bit(DW_DMA_IS_SOFT_LLP, + &dwc->flags); + if (was_soft_llp) { + dev_err(chan2dev(&dwc->chan), + "BUG: Attempted to start new LLP transfer " + "inside ongoing one\n"); + return; + } + + dwc_initialize(dwc); + + dwc->residue = first->total_len; + dwc->tx_node_active = &first->tx_list; + + /* Submit first block */ + dwc_do_single_block(dwc, first); + + return; + } + + dwc_initialize(dwc); + + channel_writel(dwc, LLP, first->txd.phys); + channel_writel(dwc, CTL_LO, + DWC_CTLL_LLP_D_EN | DWC_CTLL_LLP_S_EN); + channel_writel(dwc, CTL_HI, 0); + channel_set_bit(dw, CH_EN, dwc->mask); +} + +/*----------------------------------------------------------------------*/ + +static void +dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc, + bool callback_required) +{ + dma_async_tx_callback callback = NULL; + void *param = NULL; + struct dma_async_tx_descriptor *txd = &desc->txd; + struct dw_desc *child; + unsigned long flags; + + dev_vdbg(chan2dev(&dwc->chan), "descriptor %u complete\n", txd->cookie); + + spin_lock_irqsave(&dwc->lock, flags); + dma_cookie_complete(txd); + if (callback_required) { + callback = txd->callback; + param = txd->callback_param; + } + + /* async_tx_ack */ + list_for_each_entry(child, &desc->tx_list, desc_node) + async_tx_ack(&child->txd); + async_tx_ack(&desc->txd); + + list_splice_init(&desc->tx_list, &dwc->free_list); + list_move(&desc->desc_node, &dwc->free_list); + + if (!is_slave_direction(dwc->direction)) { + struct device *parent = chan2parent(&dwc->chan); + if (!(txd->flags & DMA_COMPL_SKIP_DEST_UNMAP)) { + if (txd->flags & DMA_COMPL_DEST_UNMAP_SINGLE) + dma_unmap_single(parent, desc->lli.dar, + desc->total_len, DMA_FROM_DEVICE); + else + dma_unmap_page(parent, desc->lli.dar, + desc->total_len, DMA_FROM_DEVICE); + } + if (!(txd->flags & DMA_COMPL_SKIP_SRC_UNMAP)) { + if (txd->flags & DMA_COMPL_SRC_UNMAP_SINGLE) + dma_unmap_single(parent, desc->lli.sar, + desc->total_len, DMA_TO_DEVICE); + else + dma_unmap_page(parent, desc->lli.sar, + desc->total_len, DMA_TO_DEVICE); + } + } + + spin_unlock_irqrestore(&dwc->lock, flags); + + if (callback) + callback(param); +} + +static void dwc_complete_all(struct dw_dma *dw, struct dw_dma_chan *dwc) +{ + struct dw_desc *desc, *_desc; + LIST_HEAD(list); + unsigned long flags; + + spin_lock_irqsave(&dwc->lock, flags); + if (dma_readl(dw, CH_EN) & dwc->mask) { + dev_err(chan2dev(&dwc->chan), + "BUG: XFER bit set, but channel not idle!\n"); + + /* Try to continue after resetting the channel... */ + dwc_chan_disable(dw, dwc); + } + + /* + * Submit queued descriptors ASAP, i.e. before we go through + * the completed ones. + */ + list_splice_init(&dwc->active_list, &list); + if (!list_empty(&dwc->queue)) { + list_move(dwc->queue.next, &dwc->active_list); + dwc_dostart(dwc, dwc_first_active(dwc)); + } + + spin_unlock_irqrestore(&dwc->lock, flags); + + list_for_each_entry_safe(desc, _desc, &list, desc_node) + dwc_descriptor_complete(dwc, desc, true); +} + +/* Returns how many bytes were already received from source */ +static inline u32 dwc_get_sent(struct dw_dma_chan *dwc) +{ + u32 ctlhi = channel_readl(dwc, CTL_HI); + u32 ctllo = channel_readl(dwc, CTL_LO); + + return (ctlhi & DWC_CTLH_BLOCK_TS_MASK) * (1 << (ctllo >> 4 & 7)); +} + +static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) +{ + dma_addr_t llp; + struct dw_desc *desc, *_desc; + struct dw_desc *child; + u32 status_xfer; + unsigned long flags; + + spin_lock_irqsave(&dwc->lock, flags); + llp = channel_readl(dwc, LLP); + status_xfer = dma_readl(dw, RAW.XFER); + + if (status_xfer & dwc->mask) { + /* Everything we've submitted is done */ + dma_writel(dw, CLEAR.XFER, dwc->mask); + + if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags)) { + struct list_head *head, *active = dwc->tx_node_active; + + /* + * We are inside first active descriptor. + * Otherwise something is really wrong. + */ + desc = dwc_first_active(dwc); + + head = &desc->tx_list; + if (active != head) { + /* Update desc to reflect last sent one */ + if (active != head->next) + desc = to_dw_desc(active->prev); + + dwc->residue -= desc->len; + + child = to_dw_desc(active); + + /* Submit next block */ + dwc_do_single_block(dwc, child); + + spin_unlock_irqrestore(&dwc->lock, flags); + return; + } + + /* We are done here */ + clear_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags); + } + + dwc->residue = 0; + + spin_unlock_irqrestore(&dwc->lock, flags); + + dwc_complete_all(dw, dwc); + return; + } + + if (list_empty(&dwc->active_list)) { + dwc->residue = 0; + spin_unlock_irqrestore(&dwc->lock, flags); + return; + } + + if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags)) { + dev_vdbg(chan2dev(&dwc->chan), "%s: soft LLP mode\n", __func__); + spin_unlock_irqrestore(&dwc->lock, flags); + return; + } + + dev_vdbg(chan2dev(&dwc->chan), "%s: llp=0x%llx\n", __func__, + (unsigned long long)llp); + + list_for_each_entry_safe(desc, _desc, &dwc->active_list, desc_node) { + /* Initial residue value */ + dwc->residue = desc->total_len; + + /* Check first descriptors addr */ + if (desc->txd.phys == llp) { + spin_unlock_irqrestore(&dwc->lock, flags); + return; + } + + /* Check first descriptors llp */ + if (desc->lli.llp == llp) { + /* This one is currently in progress */ + dwc->residue -= dwc_get_sent(dwc); + spin_unlock_irqrestore(&dwc->lock, flags); + return; + } + + dwc->residue -= desc->len; + list_for_each_entry(child, &desc->tx_list, desc_node) { + if (child->lli.llp == llp) { + /* Currently in progress */ + dwc->residue -= dwc_get_sent(dwc); + spin_unlock_irqrestore(&dwc->lock, flags); + return; + } + dwc->residue -= child->len; + } + + /* + * No descriptors so far seem to be in progress, i.e. + * this one must be done. + */ + spin_unlock_irqrestore(&dwc->lock, flags); + dwc_descriptor_complete(dwc, desc, true); + spin_lock_irqsave(&dwc->lock, flags); + } + + dev_err(chan2dev(&dwc->chan), + "BUG: All descriptors done, but channel not idle!\n"); + + /* Try to continue after resetting the channel... */ + dwc_chan_disable(dw, dwc); + + if (!list_empty(&dwc->queue)) { + list_move(dwc->queue.next, &dwc->active_list); + dwc_dostart(dwc, dwc_first_active(dwc)); + } + spin_unlock_irqrestore(&dwc->lock, flags); +} + +static inline void dwc_dump_lli(struct dw_dma_chan *dwc, struct dw_lli *lli) +{ + dev_crit(chan2dev(&dwc->chan), " desc: s0x%x d0x%x l0x%x c0x%x:%x\n", + lli->sar, lli->dar, lli->llp, lli->ctlhi, lli->ctllo); +} + +static void dwc_handle_error(struct dw_dma *dw, struct dw_dma_chan *dwc) +{ + struct dw_desc *bad_desc; + struct dw_desc *child; + unsigned long flags; + + dwc_scan_descriptors(dw, dwc); + + spin_lock_irqsave(&dwc->lock, flags); + + /* + * The descriptor currently at the head of the active list is + * borked. Since we don't have any way to report errors, we'll + * just have to scream loudly and try to carry on. + */ + bad_desc = dwc_first_active(dwc); + list_del_init(&bad_desc->desc_node); + list_move(dwc->queue.next, dwc->active_list.prev); + + /* Clear the error flag and try to restart the controller */ + dma_writel(dw, CLEAR.ERROR, dwc->mask); + if (!list_empty(&dwc->active_list)) + dwc_dostart(dwc, dwc_first_active(dwc)); + + /* + * WARN may seem harsh, but since this only happens + * when someone submits a bad physical address in a + * descriptor, we should consider ourselves lucky that the + * controller flagged an error instead of scribbling over + * random memory locations. + */ + dev_WARN(chan2dev(&dwc->chan), "Bad descriptor submitted for DMA!\n" + " cookie: %d\n", bad_desc->txd.cookie); + dwc_dump_lli(dwc, &bad_desc->lli); + list_for_each_entry(child, &bad_desc->tx_list, desc_node) + dwc_dump_lli(dwc, &child->lli); + + spin_unlock_irqrestore(&dwc->lock, flags); + + /* Pretend the descriptor completed successfully */ + dwc_descriptor_complete(dwc, bad_desc, true); +} + +/* --------------------- Cyclic DMA API extensions -------------------- */ + +dma_addr_t dw_dma_get_src_addr(struct dma_chan *chan) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + return channel_readl(dwc, SAR); +} +EXPORT_SYMBOL(dw_dma_get_src_addr); + +dma_addr_t dw_dma_get_dst_addr(struct dma_chan *chan) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + return channel_readl(dwc, DAR); +} +EXPORT_SYMBOL(dw_dma_get_dst_addr); + +/* Called with dwc->lock held and all DMAC interrupts disabled */ +static void dwc_handle_cyclic(struct dw_dma *dw, struct dw_dma_chan *dwc, + u32 status_err, u32 status_xfer) +{ + unsigned long flags; + + if (dwc->mask) { + void (*callback)(void *param); + void *callback_param; + + dev_vdbg(chan2dev(&dwc->chan), "new cyclic period llp 0x%08x\n", + channel_readl(dwc, LLP)); + + callback = dwc->cdesc->period_callback; + callback_param = dwc->cdesc->period_callback_param; + + if (callback) + callback(callback_param); + } + + /* + * Error and transfer complete are highly unlikely, and will most + * likely be due to a configuration error by the user. + */ + if (unlikely(status_err & dwc->mask) || + unlikely(status_xfer & dwc->mask)) { + int i; + + dev_err(chan2dev(&dwc->chan), "cyclic DMA unexpected %s " + "interrupt, stopping DMA transfer\n", + status_xfer ? "xfer" : "error"); + + spin_lock_irqsave(&dwc->lock, flags); + + dwc_dump_chan_regs(dwc); + + dwc_chan_disable(dw, dwc); + + /* Make sure DMA does not restart by loading a new list */ + channel_writel(dwc, LLP, 0); + channel_writel(dwc, CTL_LO, 0); + channel_writel(dwc, CTL_HI, 0); + + dma_writel(dw, CLEAR.ERROR, dwc->mask); + dma_writel(dw, CLEAR.XFER, dwc->mask); + + for (i = 0; i < dwc->cdesc->periods; i++) + dwc_dump_lli(dwc, &dwc->cdesc->desc[i]->lli); + + spin_unlock_irqrestore(&dwc->lock, flags); + } +} + +/* ------------------------------------------------------------------------- */ + +static void dw_dma_tasklet(unsigned long data) +{ + struct dw_dma *dw = (struct dw_dma *)data; + struct dw_dma_chan *dwc; + u32 status_xfer; + u32 status_err; + int i; + + status_xfer = dma_readl(dw, RAW.XFER); + status_err = dma_readl(dw, RAW.ERROR); + + dev_vdbg(dw->dma.dev, "%s: status_err=%x\n", __func__, status_err); + + for (i = 0; i < dw->dma.chancnt; i++) { + dwc = &dw->chan[i]; + if (test_bit(DW_DMA_IS_CYCLIC, &dwc->flags)) + dwc_handle_cyclic(dw, dwc, status_err, status_xfer); + else if (status_err & (1 << i)) + dwc_handle_error(dw, dwc); + else if (status_xfer & (1 << i)) + dwc_scan_descriptors(dw, dwc); + } + + /* + * Re-enable interrupts. + */ + channel_set_bit(dw, MASK.XFER, dw->all_chan_mask); + channel_set_bit(dw, MASK.ERROR, dw->all_chan_mask); +} + +static irqreturn_t dw_dma_interrupt(int irq, void *dev_id) +{ + struct dw_dma *dw = dev_id; + u32 status; + + dev_vdbg(dw->dma.dev, "%s: status=0x%x\n", __func__, + dma_readl(dw, STATUS_INT)); + + /* + * Just disable the interrupts. We'll turn them back on in the + * softirq handler. + */ + channel_clear_bit(dw, MASK.XFER, dw->all_chan_mask); + channel_clear_bit(dw, MASK.ERROR, dw->all_chan_mask); + + status = dma_readl(dw, STATUS_INT); + if (status) { + dev_err(dw->dma.dev, + "BUG: Unexpected interrupts pending: 0x%x\n", + status); + + /* Try to recover */ + channel_clear_bit(dw, MASK.XFER, (1 << 8) - 1); + channel_clear_bit(dw, MASK.SRC_TRAN, (1 << 8) - 1); + channel_clear_bit(dw, MASK.DST_TRAN, (1 << 8) - 1); + channel_clear_bit(dw, MASK.ERROR, (1 << 8) - 1); + } + + tasklet_schedule(&dw->tasklet); + + return IRQ_HANDLED; +} + +/*----------------------------------------------------------------------*/ + +static dma_cookie_t dwc_tx_submit(struct dma_async_tx_descriptor *tx) +{ + struct dw_desc *desc = txd_to_dw_desc(tx); + struct dw_dma_chan *dwc = to_dw_dma_chan(tx->chan); + dma_cookie_t cookie; + unsigned long flags; + + spin_lock_irqsave(&dwc->lock, flags); + cookie = dma_cookie_assign(tx); + + /* + * REVISIT: We should attempt to chain as many descriptors as + * possible, perhaps even appending to those already submitted + * for DMA. But this is hard to do in a race-free manner. + */ + if (list_empty(&dwc->active_list)) { + dev_vdbg(chan2dev(tx->chan), "%s: started %u\n", __func__, + desc->txd.cookie); + list_add_tail(&desc->desc_node, &dwc->active_list); + dwc_dostart(dwc, dwc_first_active(dwc)); + } else { + dev_vdbg(chan2dev(tx->chan), "%s: queued %u\n", __func__, + desc->txd.cookie); + + list_add_tail(&desc->desc_node, &dwc->queue); + } + + spin_unlock_irqrestore(&dwc->lock, flags); + + return cookie; +} + +static struct dma_async_tx_descriptor * +dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, + size_t len, unsigned long flags) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_dma *dw = to_dw_dma(chan->device); + struct dw_desc *desc; + struct dw_desc *first; + struct dw_desc *prev; + size_t xfer_count; + size_t offset; + unsigned int src_width; + unsigned int dst_width; + unsigned int data_width; + u32 ctllo; + + dev_vdbg(chan2dev(chan), + "%s: d0x%llx s0x%llx l0x%zx f0x%lx\n", __func__, + (unsigned long long)dest, (unsigned long long)src, + len, flags); + + if (unlikely(!len)) { + dev_dbg(chan2dev(chan), "%s: length is zero!\n", __func__); + return NULL; + } + + dwc->direction = DMA_MEM_TO_MEM; + + data_width = min_t(unsigned int, dw->data_width[dwc->src_master], + dw->data_width[dwc->dst_master]); + + src_width = dst_width = min_t(unsigned int, data_width, + dwc_fast_fls(src | dest | len)); + + ctllo = DWC_DEFAULT_CTLLO(chan) + | DWC_CTLL_DST_WIDTH(dst_width) + | DWC_CTLL_SRC_WIDTH(src_width) + | DWC_CTLL_DST_INC + | DWC_CTLL_SRC_INC + | DWC_CTLL_FC_M2M; + prev = first = NULL; + + for (offset = 0; offset < len; offset += xfer_count << src_width) { + xfer_count = min_t(size_t, (len - offset) >> src_width, + dwc->block_size); + + desc = dwc_desc_get(dwc); + if (!desc) + goto err_desc_get; + + desc->lli.sar = src + offset; + desc->lli.dar = dest + offset; + desc->lli.ctllo = ctllo; + desc->lli.ctlhi = xfer_count; + desc->len = xfer_count << src_width; + + if (!first) { + first = desc; + } else { + prev->lli.llp = desc->txd.phys; + list_add_tail(&desc->desc_node, + &first->tx_list); + } + prev = desc; + } + + if (flags & DMA_PREP_INTERRUPT) + /* Trigger interrupt after last block */ + prev->lli.ctllo |= DWC_CTLL_INT_EN; + + prev->lli.llp = 0; + first->txd.flags = flags; + first->total_len = len; + + return &first->txd; + +err_desc_get: + dwc_desc_put(dwc, first); + return NULL; +} + +static struct dma_async_tx_descriptor * +dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, + unsigned int sg_len, enum dma_transfer_direction direction, + unsigned long flags, void *context) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_dma *dw = to_dw_dma(chan->device); + struct dma_slave_config *sconfig = &dwc->dma_sconfig; + struct dw_desc *prev; + struct dw_desc *first; + u32 ctllo; + dma_addr_t reg; + unsigned int reg_width; + unsigned int mem_width; + unsigned int data_width; + unsigned int i; + struct scatterlist *sg; + size_t total_len = 0; + + dev_vdbg(chan2dev(chan), "%s\n", __func__); + + if (unlikely(!is_slave_direction(direction) || !sg_len)) + return NULL; + + dwc->direction = direction; + + prev = first = NULL; + + switch (direction) { + case DMA_MEM_TO_DEV: + reg_width = __fls(sconfig->dst_addr_width); + reg = sconfig->dst_addr; + ctllo = (DWC_DEFAULT_CTLLO(chan) + | DWC_CTLL_DST_WIDTH(reg_width) + | DWC_CTLL_DST_FIX + | DWC_CTLL_SRC_INC); + + ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_M2P) : + DWC_CTLL_FC(DW_DMA_FC_D_M2P); + + data_width = dw->data_width[dwc->src_master]; + + for_each_sg(sgl, sg, sg_len, i) { + struct dw_desc *desc; + u32 len, dlen, mem; + + mem = sg_dma_address(sg); + len = sg_dma_len(sg); + + mem_width = min_t(unsigned int, + data_width, dwc_fast_fls(mem | len)); + +slave_sg_todev_fill_desc: + desc = dwc_desc_get(dwc); + if (!desc) { + dev_err(chan2dev(chan), + "not enough descriptors available\n"); + goto err_desc_get; + } + + desc->lli.sar = mem; + desc->lli.dar = reg; + desc->lli.ctllo = ctllo | DWC_CTLL_SRC_WIDTH(mem_width); + if ((len >> mem_width) > dwc->block_size) { + dlen = dwc->block_size << mem_width; + mem += dlen; + len -= dlen; + } else { + dlen = len; + len = 0; + } + + desc->lli.ctlhi = dlen >> mem_width; + desc->len = dlen; + + if (!first) { + first = desc; + } else { + prev->lli.llp = desc->txd.phys; + list_add_tail(&desc->desc_node, + &first->tx_list); + } + prev = desc; + total_len += dlen; + + if (len) + goto slave_sg_todev_fill_desc; + } + break; + case DMA_DEV_TO_MEM: + reg_width = __fls(sconfig->src_addr_width); + reg = sconfig->src_addr; + ctllo = (DWC_DEFAULT_CTLLO(chan) + | DWC_CTLL_SRC_WIDTH(reg_width) + | DWC_CTLL_DST_INC + | DWC_CTLL_SRC_FIX); + + ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_P2M) : + DWC_CTLL_FC(DW_DMA_FC_D_P2M); + + data_width = dw->data_width[dwc->dst_master]; + + for_each_sg(sgl, sg, sg_len, i) { + struct dw_desc *desc; + u32 len, dlen, mem; + + mem = sg_dma_address(sg); + len = sg_dma_len(sg); + + mem_width = min_t(unsigned int, + data_width, dwc_fast_fls(mem | len)); + +slave_sg_fromdev_fill_desc: + desc = dwc_desc_get(dwc); + if (!desc) { + dev_err(chan2dev(chan), + "not enough descriptors available\n"); + goto err_desc_get; + } + + desc->lli.sar = reg; + desc->lli.dar = mem; + desc->lli.ctllo = ctllo | DWC_CTLL_DST_WIDTH(mem_width); + if ((len >> reg_width) > dwc->block_size) { + dlen = dwc->block_size << reg_width; + mem += dlen; + len -= dlen; + } else { + dlen = len; + len = 0; + } + desc->lli.ctlhi = dlen >> reg_width; + desc->len = dlen; + + if (!first) { + first = desc; + } else { + prev->lli.llp = desc->txd.phys; + list_add_tail(&desc->desc_node, + &first->tx_list); + } + prev = desc; + total_len += dlen; + + if (len) + goto slave_sg_fromdev_fill_desc; + } + break; + default: + return NULL; + } + + if (flags & DMA_PREP_INTERRUPT) + /* Trigger interrupt after last block */ + prev->lli.ctllo |= DWC_CTLL_INT_EN; + + prev->lli.llp = 0; + first->total_len = total_len; + + return &first->txd; + +err_desc_get: + dwc_desc_put(dwc, first); + return NULL; +} + +/* + * Fix sconfig's burst size according to dw_dmac. We need to convert them as: + * 1 -> 0, 4 -> 1, 8 -> 2, 16 -> 3. + * + * NOTE: burst size 2 is not supported by controller. + * + * This can be done by finding least significant bit set: n & (n - 1) + */ +static inline void convert_burst(u32 *maxburst) +{ + if (*maxburst > 1) + *maxburst = fls(*maxburst) - 2; + else + *maxburst = 0; +} + +static int +set_runtime_config(struct dma_chan *chan, struct dma_slave_config *sconfig) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + + /* Check if chan will be configured for slave transfers */ + if (!is_slave_direction(sconfig->direction)) + return -EINVAL; + + memcpy(&dwc->dma_sconfig, sconfig, sizeof(*sconfig)); + dwc->direction = sconfig->direction; + + /* Take the request line from slave_id member */ + if (dwc->request_line == ~0) + dwc->request_line = sconfig->slave_id; + + convert_burst(&dwc->dma_sconfig.src_maxburst); + convert_burst(&dwc->dma_sconfig.dst_maxburst); + + return 0; +} + +static inline void dwc_chan_pause(struct dw_dma_chan *dwc) +{ + u32 cfglo = channel_readl(dwc, CFG_LO); + unsigned int count = 20; /* timeout iterations */ + + channel_writel(dwc, CFG_LO, cfglo | DWC_CFGL_CH_SUSP); + while (!(channel_readl(dwc, CFG_LO) & DWC_CFGL_FIFO_EMPTY) && count--) + udelay(2); + + dwc->paused = true; +} + +static inline void dwc_chan_resume(struct dw_dma_chan *dwc) +{ + u32 cfglo = channel_readl(dwc, CFG_LO); + + channel_writel(dwc, CFG_LO, cfglo & ~DWC_CFGL_CH_SUSP); + + dwc->paused = false; +} + +static int dwc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, + unsigned long arg) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_dma *dw = to_dw_dma(chan->device); + struct dw_desc *desc, *_desc; + unsigned long flags; + LIST_HEAD(list); + + if (cmd == DMA_PAUSE) { + spin_lock_irqsave(&dwc->lock, flags); + + dwc_chan_pause(dwc); + + spin_unlock_irqrestore(&dwc->lock, flags); + } else if (cmd == DMA_RESUME) { + if (!dwc->paused) + return 0; + + spin_lock_irqsave(&dwc->lock, flags); + + dwc_chan_resume(dwc); + + spin_unlock_irqrestore(&dwc->lock, flags); + } else if (cmd == DMA_TERMINATE_ALL) { + spin_lock_irqsave(&dwc->lock, flags); + + clear_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags); + + dwc_chan_disable(dw, dwc); + + dwc_chan_resume(dwc); + + /* active_list entries will end up before queued entries */ + list_splice_init(&dwc->queue, &list); + list_splice_init(&dwc->active_list, &list); + + spin_unlock_irqrestore(&dwc->lock, flags); + + /* Flush all pending and queued descriptors */ + list_for_each_entry_safe(desc, _desc, &list, desc_node) + dwc_descriptor_complete(dwc, desc, false); + } else if (cmd == DMA_SLAVE_CONFIG) { + return set_runtime_config(chan, (struct dma_slave_config *)arg); + } else { + return -ENXIO; + } + + return 0; +} + +static inline u32 dwc_get_residue(struct dw_dma_chan *dwc) +{ + unsigned long flags; + u32 residue; + + spin_lock_irqsave(&dwc->lock, flags); + + residue = dwc->residue; + if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags) && residue) + residue -= dwc_get_sent(dwc); + + spin_unlock_irqrestore(&dwc->lock, flags); + return residue; +} + +static enum dma_status +dwc_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, + struct dma_tx_state *txstate) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + enum dma_status ret; + + ret = dma_cookie_status(chan, cookie, txstate); + if (ret != DMA_SUCCESS) { + dwc_scan_descriptors(to_dw_dma(chan->device), dwc); + + ret = dma_cookie_status(chan, cookie, txstate); + } + + if (ret != DMA_SUCCESS) + dma_set_residue(txstate, dwc_get_residue(dwc)); + + if (dwc->paused) + return DMA_PAUSED; + + return ret; +} + +static void dwc_issue_pending(struct dma_chan *chan) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + + if (!list_empty(&dwc->queue)) + dwc_scan_descriptors(to_dw_dma(chan->device), dwc); +} + +static int dwc_alloc_chan_resources(struct dma_chan *chan) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_dma *dw = to_dw_dma(chan->device); + struct dw_desc *desc; + int i; + unsigned long flags; + + dev_vdbg(chan2dev(chan), "%s\n", __func__); + + /* ASSERT: channel is idle */ + if (dma_readl(dw, CH_EN) & dwc->mask) { + dev_dbg(chan2dev(chan), "DMA channel not idle?\n"); + return -EIO; + } + + dma_cookie_init(chan); + + /* + * NOTE: some controllers may have additional features that we + * need to initialize here, like "scatter-gather" (which + * doesn't mean what you think it means), and status writeback. + */ + + dwc_set_masters(dwc); + + spin_lock_irqsave(&dwc->lock, flags); + i = dwc->descs_allocated; + while (dwc->descs_allocated < NR_DESCS_PER_CHANNEL) { + dma_addr_t phys; + + spin_unlock_irqrestore(&dwc->lock, flags); + + desc = dma_pool_alloc(dw->desc_pool, GFP_ATOMIC, &phys); + if (!desc) + goto err_desc_alloc; + + memset(desc, 0, sizeof(struct dw_desc)); + + INIT_LIST_HEAD(&desc->tx_list); + dma_async_tx_descriptor_init(&desc->txd, chan); + desc->txd.tx_submit = dwc_tx_submit; + desc->txd.flags = DMA_CTRL_ACK; + desc->txd.phys = phys; + + dwc_desc_put(dwc, desc); + + spin_lock_irqsave(&dwc->lock, flags); + i = ++dwc->descs_allocated; + } + + spin_unlock_irqrestore(&dwc->lock, flags); + + dev_dbg(chan2dev(chan), "%s: allocated %d descriptors\n", __func__, i); + + return i; + +err_desc_alloc: + dev_info(chan2dev(chan), "only allocated %d descriptors\n", i); + + return i; +} + +static void dwc_free_chan_resources(struct dma_chan *chan) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_dma *dw = to_dw_dma(chan->device); + struct dw_desc *desc, *_desc; + unsigned long flags; + LIST_HEAD(list); + + dev_dbg(chan2dev(chan), "%s: descs allocated=%u\n", __func__, + dwc->descs_allocated); + + /* ASSERT: channel is idle */ + BUG_ON(!list_empty(&dwc->active_list)); + BUG_ON(!list_empty(&dwc->queue)); + BUG_ON(dma_readl(to_dw_dma(chan->device), CH_EN) & dwc->mask); + + spin_lock_irqsave(&dwc->lock, flags); + list_splice_init(&dwc->free_list, &list); + dwc->descs_allocated = 0; + dwc->initialized = false; + dwc->request_line = ~0; + + /* Disable interrupts */ + channel_clear_bit(dw, MASK.XFER, dwc->mask); + channel_clear_bit(dw, MASK.ERROR, dwc->mask); + + spin_unlock_irqrestore(&dwc->lock, flags); + + list_for_each_entry_safe(desc, _desc, &list, desc_node) { + dev_vdbg(chan2dev(chan), " freeing descriptor %p\n", desc); + dma_pool_free(dw->desc_pool, desc, desc->txd.phys); + } + + dev_vdbg(chan2dev(chan), "%s: done\n", __func__); +} + +/* --------------------- Cyclic DMA API extensions -------------------- */ + +/** + * dw_dma_cyclic_start - start the cyclic DMA transfer + * @chan: the DMA channel to start + * + * Must be called with soft interrupts disabled. Returns zero on success or + * -errno on failure. + */ +int dw_dma_cyclic_start(struct dma_chan *chan) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_dma *dw = to_dw_dma(dwc->chan.device); + unsigned long flags; + + if (!test_bit(DW_DMA_IS_CYCLIC, &dwc->flags)) { + dev_err(chan2dev(&dwc->chan), "missing prep for cyclic DMA\n"); + return -ENODEV; + } + + spin_lock_irqsave(&dwc->lock, flags); + + /* Assert channel is idle */ + if (dma_readl(dw, CH_EN) & dwc->mask) { + dev_err(chan2dev(&dwc->chan), + "BUG: Attempted to start non-idle channel\n"); + dwc_dump_chan_regs(dwc); + spin_unlock_irqrestore(&dwc->lock, flags); + return -EBUSY; + } + + dma_writel(dw, CLEAR.ERROR, dwc->mask); + dma_writel(dw, CLEAR.XFER, dwc->mask); + + /* Setup DMAC channel registers */ + channel_writel(dwc, LLP, dwc->cdesc->desc[0]->txd.phys); + channel_writel(dwc, CTL_LO, DWC_CTLL_LLP_D_EN | DWC_CTLL_LLP_S_EN); + channel_writel(dwc, CTL_HI, 0); + + channel_set_bit(dw, CH_EN, dwc->mask); + + spin_unlock_irqrestore(&dwc->lock, flags); + + return 0; +} +EXPORT_SYMBOL(dw_dma_cyclic_start); + +/** + * dw_dma_cyclic_stop - stop the cyclic DMA transfer + * @chan: the DMA channel to stop + * + * Must be called with soft interrupts disabled. + */ +void dw_dma_cyclic_stop(struct dma_chan *chan) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_dma *dw = to_dw_dma(dwc->chan.device); + unsigned long flags; + + spin_lock_irqsave(&dwc->lock, flags); + + dwc_chan_disable(dw, dwc); + + spin_unlock_irqrestore(&dwc->lock, flags); +} +EXPORT_SYMBOL(dw_dma_cyclic_stop); + +/** + * dw_dma_cyclic_prep - prepare the cyclic DMA transfer + * @chan: the DMA channel to prepare + * @buf_addr: physical DMA address where the buffer starts + * @buf_len: total number of bytes for the entire buffer + * @period_len: number of bytes for each period + * @direction: transfer direction, to or from device + * + * Must be called before trying to start the transfer. Returns a valid struct + * dw_cyclic_desc if successful or an ERR_PTR(-errno) if not successful. + */ +struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, + dma_addr_t buf_addr, size_t buf_len, size_t period_len, + enum dma_transfer_direction direction) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dma_slave_config *sconfig = &dwc->dma_sconfig; + struct dw_cyclic_desc *cdesc; + struct dw_cyclic_desc *retval = NULL; + struct dw_desc *desc; + struct dw_desc *last = NULL; + unsigned long was_cyclic; + unsigned int reg_width; + unsigned int periods; + unsigned int i; + unsigned long flags; + + spin_lock_irqsave(&dwc->lock, flags); + if (dwc->nollp) { + spin_unlock_irqrestore(&dwc->lock, flags); + dev_dbg(chan2dev(&dwc->chan), + "channel doesn't support LLP transfers\n"); + return ERR_PTR(-EINVAL); + } + + if (!list_empty(&dwc->queue) || !list_empty(&dwc->active_list)) { + spin_unlock_irqrestore(&dwc->lock, flags); + dev_dbg(chan2dev(&dwc->chan), + "queue and/or active list are not empty\n"); + return ERR_PTR(-EBUSY); + } + + was_cyclic = test_and_set_bit(DW_DMA_IS_CYCLIC, &dwc->flags); + spin_unlock_irqrestore(&dwc->lock, flags); + if (was_cyclic) { + dev_dbg(chan2dev(&dwc->chan), + "channel already prepared for cyclic DMA\n"); + return ERR_PTR(-EBUSY); + } + + retval = ERR_PTR(-EINVAL); + + if (unlikely(!is_slave_direction(direction))) + goto out_err; + + dwc->direction = direction; + + if (direction == DMA_MEM_TO_DEV) + reg_width = __ffs(sconfig->dst_addr_width); + else + reg_width = __ffs(sconfig->src_addr_width); + + periods = buf_len / period_len; + + /* Check for too big/unaligned periods and unaligned DMA buffer. */ + if (period_len > (dwc->block_size << reg_width)) + goto out_err; + if (unlikely(period_len & ((1 << reg_width) - 1))) + goto out_err; + if (unlikely(buf_addr & ((1 << reg_width) - 1))) + goto out_err; + + retval = ERR_PTR(-ENOMEM); + + if (periods > NR_DESCS_PER_CHANNEL) + goto out_err; + + cdesc = kzalloc(sizeof(struct dw_cyclic_desc), GFP_KERNEL); + if (!cdesc) + goto out_err; + + cdesc->desc = kzalloc(sizeof(struct dw_desc *) * periods, GFP_KERNEL); + if (!cdesc->desc) + goto out_err_alloc; + + for (i = 0; i < periods; i++) { + desc = dwc_desc_get(dwc); + if (!desc) + goto out_err_desc_get; + + switch (direction) { + case DMA_MEM_TO_DEV: + desc->lli.dar = sconfig->dst_addr; + desc->lli.sar = buf_addr + (period_len * i); + desc->lli.ctllo = (DWC_DEFAULT_CTLLO(chan) + | DWC_CTLL_DST_WIDTH(reg_width) + | DWC_CTLL_SRC_WIDTH(reg_width) + | DWC_CTLL_DST_FIX + | DWC_CTLL_SRC_INC + | DWC_CTLL_INT_EN); + + desc->lli.ctllo |= sconfig->device_fc ? + DWC_CTLL_FC(DW_DMA_FC_P_M2P) : + DWC_CTLL_FC(DW_DMA_FC_D_M2P); + + break; + case DMA_DEV_TO_MEM: + desc->lli.dar = buf_addr + (period_len * i); + desc->lli.sar = sconfig->src_addr; + desc->lli.ctllo = (DWC_DEFAULT_CTLLO(chan) + | DWC_CTLL_SRC_WIDTH(reg_width) + | DWC_CTLL_DST_WIDTH(reg_width) + | DWC_CTLL_DST_INC + | DWC_CTLL_SRC_FIX + | DWC_CTLL_INT_EN); + + desc->lli.ctllo |= sconfig->device_fc ? + DWC_CTLL_FC(DW_DMA_FC_P_P2M) : + DWC_CTLL_FC(DW_DMA_FC_D_P2M); + + break; + default: + break; + } + + desc->lli.ctlhi = (period_len >> reg_width); + cdesc->desc[i] = desc; + + if (last) + last->lli.llp = desc->txd.phys; + + last = desc; + } + + /* Let's make a cyclic list */ + last->lli.llp = cdesc->desc[0]->txd.phys; + + dev_dbg(chan2dev(&dwc->chan), "cyclic prepared buf 0x%llx len %zu " + "period %zu periods %d\n", (unsigned long long)buf_addr, + buf_len, period_len, periods); + + cdesc->periods = periods; + dwc->cdesc = cdesc; + + return cdesc; + +out_err_desc_get: + while (i--) + dwc_desc_put(dwc, cdesc->desc[i]); +out_err_alloc: + kfree(cdesc); +out_err: + clear_bit(DW_DMA_IS_CYCLIC, &dwc->flags); + return (struct dw_cyclic_desc *)retval; +} +EXPORT_SYMBOL(dw_dma_cyclic_prep); + +/** + * dw_dma_cyclic_free - free a prepared cyclic DMA transfer + * @chan: the DMA channel to free + */ +void dw_dma_cyclic_free(struct dma_chan *chan) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_dma *dw = to_dw_dma(dwc->chan.device); + struct dw_cyclic_desc *cdesc = dwc->cdesc; + int i; + unsigned long flags; + + dev_dbg(chan2dev(&dwc->chan), "%s\n", __func__); + + if (!cdesc) + return; + + spin_lock_irqsave(&dwc->lock, flags); + + dwc_chan_disable(dw, dwc); + + dma_writel(dw, CLEAR.ERROR, dwc->mask); + dma_writel(dw, CLEAR.XFER, dwc->mask); + + spin_unlock_irqrestore(&dwc->lock, flags); + + for (i = 0; i < cdesc->periods; i++) + dwc_desc_put(dwc, cdesc->desc[i]); + + kfree(cdesc->desc); + kfree(cdesc); + + clear_bit(DW_DMA_IS_CYCLIC, &dwc->flags); +} +EXPORT_SYMBOL(dw_dma_cyclic_free); + +/*----------------------------------------------------------------------*/ + +static void dw_dma_off(struct dw_dma *dw) +{ + int i; + + dma_writel(dw, CFG, 0); + + channel_clear_bit(dw, MASK.XFER, dw->all_chan_mask); + channel_clear_bit(dw, MASK.SRC_TRAN, dw->all_chan_mask); + channel_clear_bit(dw, MASK.DST_TRAN, dw->all_chan_mask); + channel_clear_bit(dw, MASK.ERROR, dw->all_chan_mask); + + while (dma_readl(dw, CFG) & DW_CFG_DMA_EN) + cpu_relax(); + + for (i = 0; i < dw->dma.chancnt; i++) + dw->chan[i].initialized = false; +} + +int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata) +{ + struct dw_dma *dw; + size_t size; + bool autocfg; + unsigned int dw_params; + unsigned int nr_channels; + unsigned int max_blk_size = 0; + int err; + int i; + + dw_params = dma_read_byaddr(chip->regs, DW_PARAMS); + autocfg = dw_params >> DW_PARAMS_EN & 0x1; + + dev_dbg(chip->dev, "DW_PARAMS: 0x%08x\n", dw_params); + + if (!pdata && autocfg) { + pdata = devm_kzalloc(chip->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + /* Fill platform data with the default values */ + pdata->is_private = true; + pdata->chan_allocation_order = CHAN_ALLOCATION_ASCENDING; + pdata->chan_priority = CHAN_PRIORITY_ASCENDING; + } else if (!pdata || pdata->nr_channels > DW_DMA_MAX_NR_CHANNELS) + return -EINVAL; + + if (autocfg) + nr_channels = (dw_params >> DW_PARAMS_NR_CHAN & 0x7) + 1; + else + nr_channels = pdata->nr_channels; + + size = sizeof(struct dw_dma) + nr_channels * sizeof(struct dw_dma_chan); + dw = devm_kzalloc(chip->dev, size, GFP_KERNEL); + if (!dw) + return -ENOMEM; + + dw->clk = devm_clk_get(chip->dev, "hclk"); + if (IS_ERR(dw->clk)) + return PTR_ERR(dw->clk); + clk_prepare_enable(dw->clk); + + dw->regs = chip->regs; + chip->dw = dw; + + /* Get hardware configuration parameters */ + if (autocfg) { + max_blk_size = dma_readl(dw, MAX_BLK_SIZE); + + dw->nr_masters = (dw_params >> DW_PARAMS_NR_MASTER & 3) + 1; + for (i = 0; i < dw->nr_masters; i++) { + dw->data_width[i] = + (dw_params >> DW_PARAMS_DATA_WIDTH(i) & 3) + 2; + } + } else { + dw->nr_masters = pdata->nr_masters; + memcpy(dw->data_width, pdata->data_width, 4); + } + + /* Calculate all channel mask before DMA setup */ + dw->all_chan_mask = (1 << nr_channels) - 1; + + /* Force dma off, just in case */ + dw_dma_off(dw); + + /* Disable BLOCK interrupts as well */ + channel_clear_bit(dw, MASK.BLOCK, dw->all_chan_mask); + + err = devm_request_irq(chip->dev, chip->irq, dw_dma_interrupt, 0, + "dw_dmac", dw); + if (err) + return err; + + /* Create a pool of consistent memory blocks for hardware descriptors */ + dw->desc_pool = dmam_pool_create("dw_dmac_desc_pool", chip->dev, + sizeof(struct dw_desc), 4, 0); + if (!dw->desc_pool) { + dev_err(chip->dev, "No memory for descriptors dma pool\n"); + return -ENOMEM; + } + + tasklet_init(&dw->tasklet, dw_dma_tasklet, (unsigned long)dw); + + INIT_LIST_HEAD(&dw->dma.channels); + for (i = 0; i < nr_channels; i++) { + struct dw_dma_chan *dwc = &dw->chan[i]; + int r = nr_channels - i - 1; + + dwc->chan.device = &dw->dma; + dma_cookie_init(&dwc->chan); + if (pdata->chan_allocation_order == CHAN_ALLOCATION_ASCENDING) + list_add_tail(&dwc->chan.device_node, + &dw->dma.channels); + else + list_add(&dwc->chan.device_node, &dw->dma.channels); + + /* 7 is highest priority & 0 is lowest. */ + if (pdata->chan_priority == CHAN_PRIORITY_ASCENDING) + dwc->priority = r; + else + dwc->priority = i; + + dwc->ch_regs = &__dw_regs(dw)->CHAN[i]; + spin_lock_init(&dwc->lock); + dwc->mask = 1 << i; + + INIT_LIST_HEAD(&dwc->active_list); + INIT_LIST_HEAD(&dwc->queue); + INIT_LIST_HEAD(&dwc->free_list); + + channel_clear_bit(dw, CH_EN, dwc->mask); + + dwc->direction = DMA_TRANS_NONE; + dwc->request_line = ~0; + + /* Hardware configuration */ + if (autocfg) { + unsigned int dwc_params; + void __iomem *addr = chip->regs + r * sizeof(u32); + + dwc_params = dma_read_byaddr(addr, DWC_PARAMS); + + dev_dbg(chip->dev, "DWC_PARAMS[%d]: 0x%08x\n", i, + dwc_params); + + /* Decode maximum block size for given channel. The + * stored 4 bit value represents blocks from 0x00 for 3 + * up to 0x0a for 4095. */ + dwc->block_size = + (4 << ((max_blk_size >> 4 * i) & 0xf)) - 1; + dwc->nollp = + (dwc_params >> DWC_PARAMS_MBLK_EN & 0x1) == 0; + } else { + dwc->block_size = pdata->block_size; + + /* Check if channel supports multi block transfer */ + channel_writel(dwc, LLP, 0xfffffffc); + dwc->nollp = + (channel_readl(dwc, LLP) & 0xfffffffc) == 0; + channel_writel(dwc, LLP, 0); + } + } + + /* Clear all interrupts on all channels. */ + dma_writel(dw, CLEAR.XFER, dw->all_chan_mask); + dma_writel(dw, CLEAR.BLOCK, dw->all_chan_mask); + dma_writel(dw, CLEAR.SRC_TRAN, dw->all_chan_mask); + dma_writel(dw, CLEAR.DST_TRAN, dw->all_chan_mask); + dma_writel(dw, CLEAR.ERROR, dw->all_chan_mask); + + dma_cap_set(DMA_MEMCPY, dw->dma.cap_mask); + dma_cap_set(DMA_SLAVE, dw->dma.cap_mask); + if (pdata->is_private) + dma_cap_set(DMA_PRIVATE, dw->dma.cap_mask); + dw->dma.dev = chip->dev; + dw->dma.device_alloc_chan_resources = dwc_alloc_chan_resources; + dw->dma.device_free_chan_resources = dwc_free_chan_resources; + + dw->dma.device_prep_dma_memcpy = dwc_prep_dma_memcpy; + + dw->dma.device_prep_slave_sg = dwc_prep_slave_sg; + dw->dma.device_control = dwc_control; + + dw->dma.device_tx_status = dwc_tx_status; + dw->dma.device_issue_pending = dwc_issue_pending; + + dma_writel(dw, CFG, DW_CFG_DMA_EN); + + dev_info(chip->dev, "DesignWare DMA Controller, %d channels\n", + nr_channels); + + dma_async_device_register(&dw->dma); + + return 0; +} +EXPORT_SYMBOL_GPL(dw_dma_probe); + +int dw_dma_remove(struct dw_dma_chip *chip) +{ + struct dw_dma *dw = chip->dw; + struct dw_dma_chan *dwc, *_dwc; + + dw_dma_off(dw); + dma_async_device_unregister(&dw->dma); + + tasklet_kill(&dw->tasklet); + + list_for_each_entry_safe(dwc, _dwc, &dw->dma.channels, + chan.device_node) { + list_del(&dwc->chan.device_node); + channel_clear_bit(dw, CH_EN, dwc->mask); + } + + return 0; +} +EXPORT_SYMBOL_GPL(dw_dma_remove); + +void dw_dma_shutdown(struct dw_dma_chip *chip) +{ + struct dw_dma *dw = chip->dw; + + dw_dma_off(dw); + clk_disable_unprepare(dw->clk); +} +EXPORT_SYMBOL_GPL(dw_dma_shutdown); + +#ifdef CONFIG_PM_SLEEP + +int dw_dma_suspend(struct dw_dma_chip *chip) +{ + struct dw_dma *dw = chip->dw; + + dw_dma_off(dw); + clk_disable_unprepare(dw->clk); + + return 0; +} +EXPORT_SYMBOL_GPL(dw_dma_suspend); + +int dw_dma_resume(struct dw_dma_chip *chip) +{ + struct dw_dma *dw = chip->dw; + + clk_prepare_enable(dw->clk); + dma_writel(dw, CFG, DW_CFG_DMA_EN); + + return 0; +} +EXPORT_SYMBOL_GPL(dw_dma_resume); + +#endif /* CONFIG_PM_SLEEP */ + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Synopsys DesignWare DMA Controller core driver"); +MODULE_AUTHOR("Haavard Skinnemoen (Atmel)"); +MODULE_AUTHOR("Viresh Kumar "); diff --git a/drivers/dma/dw/dw_dmac.c b/drivers/dma/dw/dw_dmac.c deleted file mode 100644 index 15f3f4f79c10..000000000000 --- a/drivers/dma/dw/dw_dmac.c +++ /dev/null @@ -1,1969 +0,0 @@ -/* - * Core driver for the Synopsys DesignWare DMA Controller - * - * Copyright (C) 2007-2008 Atmel Corporation - * Copyright (C) 2010-2011 ST Microelectronics - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../dmaengine.h" -#include "dw_dmac_regs.h" - -/* - * This supports the Synopsys "DesignWare AHB Central DMA Controller", - * (DW_ahb_dmac) which is used with various AMBA 2.0 systems (not all - * of which use ARM any more). See the "Databook" from Synopsys for - * information beyond what licensees probably provide. - * - * The driver has currently been tested only with the Atmel AT32AP7000, - * which does not support descriptor writeback. - */ - -static inline unsigned int dwc_get_dms(struct dw_dma_slave *slave) -{ - return slave ? slave->dst_master : 0; -} - -static inline unsigned int dwc_get_sms(struct dw_dma_slave *slave) -{ - return slave ? slave->src_master : 1; -} - -static inline void dwc_set_masters(struct dw_dma_chan *dwc) -{ - struct dw_dma *dw = to_dw_dma(dwc->chan.device); - struct dw_dma_slave *dws = dwc->chan.private; - unsigned char mmax = dw->nr_masters - 1; - - if (dwc->request_line == ~0) { - dwc->src_master = min_t(unsigned char, mmax, dwc_get_sms(dws)); - dwc->dst_master = min_t(unsigned char, mmax, dwc_get_dms(dws)); - } -} - -#define DWC_DEFAULT_CTLLO(_chan) ({ \ - struct dw_dma_chan *_dwc = to_dw_dma_chan(_chan); \ - struct dma_slave_config *_sconfig = &_dwc->dma_sconfig; \ - bool _is_slave = is_slave_direction(_dwc->direction); \ - u8 _smsize = _is_slave ? _sconfig->src_maxburst : \ - DW_DMA_MSIZE_16; \ - u8 _dmsize = _is_slave ? _sconfig->dst_maxburst : \ - DW_DMA_MSIZE_16; \ - \ - (DWC_CTLL_DST_MSIZE(_dmsize) \ - | DWC_CTLL_SRC_MSIZE(_smsize) \ - | DWC_CTLL_LLP_D_EN \ - | DWC_CTLL_LLP_S_EN \ - | DWC_CTLL_DMS(_dwc->dst_master) \ - | DWC_CTLL_SMS(_dwc->src_master)); \ - }) - -/* - * Number of descriptors to allocate for each channel. This should be - * made configurable somehow; preferably, the clients (at least the - * ones using slave transfers) should be able to give us a hint. - */ -#define NR_DESCS_PER_CHANNEL 64 - -/*----------------------------------------------------------------------*/ - -static struct device *chan2dev(struct dma_chan *chan) -{ - return &chan->dev->device; -} -static struct device *chan2parent(struct dma_chan *chan) -{ - return chan->dev->device.parent; -} - -static struct dw_desc *dwc_first_active(struct dw_dma_chan *dwc) -{ - return to_dw_desc(dwc->active_list.next); -} - -static struct dw_desc *dwc_desc_get(struct dw_dma_chan *dwc) -{ - struct dw_desc *desc, *_desc; - struct dw_desc *ret = NULL; - unsigned int i = 0; - unsigned long flags; - - spin_lock_irqsave(&dwc->lock, flags); - list_for_each_entry_safe(desc, _desc, &dwc->free_list, desc_node) { - i++; - if (async_tx_test_ack(&desc->txd)) { - list_del(&desc->desc_node); - ret = desc; - break; - } - dev_dbg(chan2dev(&dwc->chan), "desc %p not ACKed\n", desc); - } - spin_unlock_irqrestore(&dwc->lock, flags); - - dev_vdbg(chan2dev(&dwc->chan), "scanned %u descriptors on freelist\n", i); - - return ret; -} - -/* - * Move a descriptor, including any children, to the free list. - * `desc' must not be on any lists. - */ -static void dwc_desc_put(struct dw_dma_chan *dwc, struct dw_desc *desc) -{ - unsigned long flags; - - if (desc) { - struct dw_desc *child; - - spin_lock_irqsave(&dwc->lock, flags); - list_for_each_entry(child, &desc->tx_list, desc_node) - dev_vdbg(chan2dev(&dwc->chan), - "moving child desc %p to freelist\n", - child); - list_splice_init(&desc->tx_list, &dwc->free_list); - dev_vdbg(chan2dev(&dwc->chan), "moving desc %p to freelist\n", desc); - list_add(&desc->desc_node, &dwc->free_list); - spin_unlock_irqrestore(&dwc->lock, flags); - } -} - -static void dwc_initialize(struct dw_dma_chan *dwc) -{ - struct dw_dma *dw = to_dw_dma(dwc->chan.device); - struct dw_dma_slave *dws = dwc->chan.private; - u32 cfghi = DWC_CFGH_FIFO_MODE; - u32 cfglo = DWC_CFGL_CH_PRIOR(dwc->priority); - - if (dwc->initialized == true) - return; - - if (dws) { - /* - * We need controller-specific data to set up slave - * transfers. - */ - BUG_ON(!dws->dma_dev || dws->dma_dev != dw->dma.dev); - - cfghi = dws->cfg_hi; - cfglo |= dws->cfg_lo & ~DWC_CFGL_CH_PRIOR_MASK; - } else { - if (dwc->direction == DMA_MEM_TO_DEV) - cfghi = DWC_CFGH_DST_PER(dwc->request_line); - else if (dwc->direction == DMA_DEV_TO_MEM) - cfghi = DWC_CFGH_SRC_PER(dwc->request_line); - } - - channel_writel(dwc, CFG_LO, cfglo); - channel_writel(dwc, CFG_HI, cfghi); - - /* Enable interrupts */ - channel_set_bit(dw, MASK.XFER, dwc->mask); - channel_set_bit(dw, MASK.ERROR, dwc->mask); - - dwc->initialized = true; -} - -/*----------------------------------------------------------------------*/ - -static inline unsigned int dwc_fast_fls(unsigned long long v) -{ - /* - * We can be a lot more clever here, but this should take care - * of the most common optimization. - */ - if (!(v & 7)) - return 3; - else if (!(v & 3)) - return 2; - else if (!(v & 1)) - return 1; - return 0; -} - -static inline void dwc_dump_chan_regs(struct dw_dma_chan *dwc) -{ - dev_err(chan2dev(&dwc->chan), - " SAR: 0x%x DAR: 0x%x LLP: 0x%x CTL: 0x%x:%08x\n", - channel_readl(dwc, SAR), - channel_readl(dwc, DAR), - channel_readl(dwc, LLP), - channel_readl(dwc, CTL_HI), - channel_readl(dwc, CTL_LO)); -} - -static inline void dwc_chan_disable(struct dw_dma *dw, struct dw_dma_chan *dwc) -{ - channel_clear_bit(dw, CH_EN, dwc->mask); - while (dma_readl(dw, CH_EN) & dwc->mask) - cpu_relax(); -} - -/*----------------------------------------------------------------------*/ - -/* Perform single block transfer */ -static inline void dwc_do_single_block(struct dw_dma_chan *dwc, - struct dw_desc *desc) -{ - struct dw_dma *dw = to_dw_dma(dwc->chan.device); - u32 ctllo; - - /* Software emulation of LLP mode relies on interrupts to continue - * multi block transfer. */ - ctllo = desc->lli.ctllo | DWC_CTLL_INT_EN; - - channel_writel(dwc, SAR, desc->lli.sar); - channel_writel(dwc, DAR, desc->lli.dar); - channel_writel(dwc, CTL_LO, ctllo); - channel_writel(dwc, CTL_HI, desc->lli.ctlhi); - channel_set_bit(dw, CH_EN, dwc->mask); - - /* Move pointer to next descriptor */ - dwc->tx_node_active = dwc->tx_node_active->next; -} - -/* Called with dwc->lock held and bh disabled */ -static void dwc_dostart(struct dw_dma_chan *dwc, struct dw_desc *first) -{ - struct dw_dma *dw = to_dw_dma(dwc->chan.device); - unsigned long was_soft_llp; - - /* ASSERT: channel is idle */ - if (dma_readl(dw, CH_EN) & dwc->mask) { - dev_err(chan2dev(&dwc->chan), - "BUG: Attempted to start non-idle channel\n"); - dwc_dump_chan_regs(dwc); - - /* The tasklet will hopefully advance the queue... */ - return; - } - - if (dwc->nollp) { - was_soft_llp = test_and_set_bit(DW_DMA_IS_SOFT_LLP, - &dwc->flags); - if (was_soft_llp) { - dev_err(chan2dev(&dwc->chan), - "BUG: Attempted to start new LLP transfer " - "inside ongoing one\n"); - return; - } - - dwc_initialize(dwc); - - dwc->residue = first->total_len; - dwc->tx_node_active = &first->tx_list; - - /* Submit first block */ - dwc_do_single_block(dwc, first); - - return; - } - - dwc_initialize(dwc); - - channel_writel(dwc, LLP, first->txd.phys); - channel_writel(dwc, CTL_LO, - DWC_CTLL_LLP_D_EN | DWC_CTLL_LLP_S_EN); - channel_writel(dwc, CTL_HI, 0); - channel_set_bit(dw, CH_EN, dwc->mask); -} - -/*----------------------------------------------------------------------*/ - -static void -dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc, - bool callback_required) -{ - dma_async_tx_callback callback = NULL; - void *param = NULL; - struct dma_async_tx_descriptor *txd = &desc->txd; - struct dw_desc *child; - unsigned long flags; - - dev_vdbg(chan2dev(&dwc->chan), "descriptor %u complete\n", txd->cookie); - - spin_lock_irqsave(&dwc->lock, flags); - dma_cookie_complete(txd); - if (callback_required) { - callback = txd->callback; - param = txd->callback_param; - } - - /* async_tx_ack */ - list_for_each_entry(child, &desc->tx_list, desc_node) - async_tx_ack(&child->txd); - async_tx_ack(&desc->txd); - - list_splice_init(&desc->tx_list, &dwc->free_list); - list_move(&desc->desc_node, &dwc->free_list); - - if (!is_slave_direction(dwc->direction)) { - struct device *parent = chan2parent(&dwc->chan); - if (!(txd->flags & DMA_COMPL_SKIP_DEST_UNMAP)) { - if (txd->flags & DMA_COMPL_DEST_UNMAP_SINGLE) - dma_unmap_single(parent, desc->lli.dar, - desc->total_len, DMA_FROM_DEVICE); - else - dma_unmap_page(parent, desc->lli.dar, - desc->total_len, DMA_FROM_DEVICE); - } - if (!(txd->flags & DMA_COMPL_SKIP_SRC_UNMAP)) { - if (txd->flags & DMA_COMPL_SRC_UNMAP_SINGLE) - dma_unmap_single(parent, desc->lli.sar, - desc->total_len, DMA_TO_DEVICE); - else - dma_unmap_page(parent, desc->lli.sar, - desc->total_len, DMA_TO_DEVICE); - } - } - - spin_unlock_irqrestore(&dwc->lock, flags); - - if (callback) - callback(param); -} - -static void dwc_complete_all(struct dw_dma *dw, struct dw_dma_chan *dwc) -{ - struct dw_desc *desc, *_desc; - LIST_HEAD(list); - unsigned long flags; - - spin_lock_irqsave(&dwc->lock, flags); - if (dma_readl(dw, CH_EN) & dwc->mask) { - dev_err(chan2dev(&dwc->chan), - "BUG: XFER bit set, but channel not idle!\n"); - - /* Try to continue after resetting the channel... */ - dwc_chan_disable(dw, dwc); - } - - /* - * Submit queued descriptors ASAP, i.e. before we go through - * the completed ones. - */ - list_splice_init(&dwc->active_list, &list); - if (!list_empty(&dwc->queue)) { - list_move(dwc->queue.next, &dwc->active_list); - dwc_dostart(dwc, dwc_first_active(dwc)); - } - - spin_unlock_irqrestore(&dwc->lock, flags); - - list_for_each_entry_safe(desc, _desc, &list, desc_node) - dwc_descriptor_complete(dwc, desc, true); -} - -/* Returns how many bytes were already received from source */ -static inline u32 dwc_get_sent(struct dw_dma_chan *dwc) -{ - u32 ctlhi = channel_readl(dwc, CTL_HI); - u32 ctllo = channel_readl(dwc, CTL_LO); - - return (ctlhi & DWC_CTLH_BLOCK_TS_MASK) * (1 << (ctllo >> 4 & 7)); -} - -static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) -{ - dma_addr_t llp; - struct dw_desc *desc, *_desc; - struct dw_desc *child; - u32 status_xfer; - unsigned long flags; - - spin_lock_irqsave(&dwc->lock, flags); - llp = channel_readl(dwc, LLP); - status_xfer = dma_readl(dw, RAW.XFER); - - if (status_xfer & dwc->mask) { - /* Everything we've submitted is done */ - dma_writel(dw, CLEAR.XFER, dwc->mask); - - if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags)) { - struct list_head *head, *active = dwc->tx_node_active; - - /* - * We are inside first active descriptor. - * Otherwise something is really wrong. - */ - desc = dwc_first_active(dwc); - - head = &desc->tx_list; - if (active != head) { - /* Update desc to reflect last sent one */ - if (active != head->next) - desc = to_dw_desc(active->prev); - - dwc->residue -= desc->len; - - child = to_dw_desc(active); - - /* Submit next block */ - dwc_do_single_block(dwc, child); - - spin_unlock_irqrestore(&dwc->lock, flags); - return; - } - - /* We are done here */ - clear_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags); - } - - dwc->residue = 0; - - spin_unlock_irqrestore(&dwc->lock, flags); - - dwc_complete_all(dw, dwc); - return; - } - - if (list_empty(&dwc->active_list)) { - dwc->residue = 0; - spin_unlock_irqrestore(&dwc->lock, flags); - return; - } - - if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags)) { - dev_vdbg(chan2dev(&dwc->chan), "%s: soft LLP mode\n", __func__); - spin_unlock_irqrestore(&dwc->lock, flags); - return; - } - - dev_vdbg(chan2dev(&dwc->chan), "%s: llp=0x%llx\n", __func__, - (unsigned long long)llp); - - list_for_each_entry_safe(desc, _desc, &dwc->active_list, desc_node) { - /* Initial residue value */ - dwc->residue = desc->total_len; - - /* Check first descriptors addr */ - if (desc->txd.phys == llp) { - spin_unlock_irqrestore(&dwc->lock, flags); - return; - } - - /* Check first descriptors llp */ - if (desc->lli.llp == llp) { - /* This one is currently in progress */ - dwc->residue -= dwc_get_sent(dwc); - spin_unlock_irqrestore(&dwc->lock, flags); - return; - } - - dwc->residue -= desc->len; - list_for_each_entry(child, &desc->tx_list, desc_node) { - if (child->lli.llp == llp) { - /* Currently in progress */ - dwc->residue -= dwc_get_sent(dwc); - spin_unlock_irqrestore(&dwc->lock, flags); - return; - } - dwc->residue -= child->len; - } - - /* - * No descriptors so far seem to be in progress, i.e. - * this one must be done. - */ - spin_unlock_irqrestore(&dwc->lock, flags); - dwc_descriptor_complete(dwc, desc, true); - spin_lock_irqsave(&dwc->lock, flags); - } - - dev_err(chan2dev(&dwc->chan), - "BUG: All descriptors done, but channel not idle!\n"); - - /* Try to continue after resetting the channel... */ - dwc_chan_disable(dw, dwc); - - if (!list_empty(&dwc->queue)) { - list_move(dwc->queue.next, &dwc->active_list); - dwc_dostart(dwc, dwc_first_active(dwc)); - } - spin_unlock_irqrestore(&dwc->lock, flags); -} - -static inline void dwc_dump_lli(struct dw_dma_chan *dwc, struct dw_lli *lli) -{ - dev_crit(chan2dev(&dwc->chan), " desc: s0x%x d0x%x l0x%x c0x%x:%x\n", - lli->sar, lli->dar, lli->llp, lli->ctlhi, lli->ctllo); -} - -static void dwc_handle_error(struct dw_dma *dw, struct dw_dma_chan *dwc) -{ - struct dw_desc *bad_desc; - struct dw_desc *child; - unsigned long flags; - - dwc_scan_descriptors(dw, dwc); - - spin_lock_irqsave(&dwc->lock, flags); - - /* - * The descriptor currently at the head of the active list is - * borked. Since we don't have any way to report errors, we'll - * just have to scream loudly and try to carry on. - */ - bad_desc = dwc_first_active(dwc); - list_del_init(&bad_desc->desc_node); - list_move(dwc->queue.next, dwc->active_list.prev); - - /* Clear the error flag and try to restart the controller */ - dma_writel(dw, CLEAR.ERROR, dwc->mask); - if (!list_empty(&dwc->active_list)) - dwc_dostart(dwc, dwc_first_active(dwc)); - - /* - * WARN may seem harsh, but since this only happens - * when someone submits a bad physical address in a - * descriptor, we should consider ourselves lucky that the - * controller flagged an error instead of scribbling over - * random memory locations. - */ - dev_WARN(chan2dev(&dwc->chan), "Bad descriptor submitted for DMA!\n" - " cookie: %d\n", bad_desc->txd.cookie); - dwc_dump_lli(dwc, &bad_desc->lli); - list_for_each_entry(child, &bad_desc->tx_list, desc_node) - dwc_dump_lli(dwc, &child->lli); - - spin_unlock_irqrestore(&dwc->lock, flags); - - /* Pretend the descriptor completed successfully */ - dwc_descriptor_complete(dwc, bad_desc, true); -} - -/* --------------------- Cyclic DMA API extensions -------------------- */ - -dma_addr_t dw_dma_get_src_addr(struct dma_chan *chan) -{ - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - return channel_readl(dwc, SAR); -} -EXPORT_SYMBOL(dw_dma_get_src_addr); - -dma_addr_t dw_dma_get_dst_addr(struct dma_chan *chan) -{ - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - return channel_readl(dwc, DAR); -} -EXPORT_SYMBOL(dw_dma_get_dst_addr); - -/* Called with dwc->lock held and all DMAC interrupts disabled */ -static void dwc_handle_cyclic(struct dw_dma *dw, struct dw_dma_chan *dwc, - u32 status_err, u32 status_xfer) -{ - unsigned long flags; - - if (dwc->mask) { - void (*callback)(void *param); - void *callback_param; - - dev_vdbg(chan2dev(&dwc->chan), "new cyclic period llp 0x%08x\n", - channel_readl(dwc, LLP)); - - callback = dwc->cdesc->period_callback; - callback_param = dwc->cdesc->period_callback_param; - - if (callback) - callback(callback_param); - } - - /* - * Error and transfer complete are highly unlikely, and will most - * likely be due to a configuration error by the user. - */ - if (unlikely(status_err & dwc->mask) || - unlikely(status_xfer & dwc->mask)) { - int i; - - dev_err(chan2dev(&dwc->chan), "cyclic DMA unexpected %s " - "interrupt, stopping DMA transfer\n", - status_xfer ? "xfer" : "error"); - - spin_lock_irqsave(&dwc->lock, flags); - - dwc_dump_chan_regs(dwc); - - dwc_chan_disable(dw, dwc); - - /* Make sure DMA does not restart by loading a new list */ - channel_writel(dwc, LLP, 0); - channel_writel(dwc, CTL_LO, 0); - channel_writel(dwc, CTL_HI, 0); - - dma_writel(dw, CLEAR.ERROR, dwc->mask); - dma_writel(dw, CLEAR.XFER, dwc->mask); - - for (i = 0; i < dwc->cdesc->periods; i++) - dwc_dump_lli(dwc, &dwc->cdesc->desc[i]->lli); - - spin_unlock_irqrestore(&dwc->lock, flags); - } -} - -/* ------------------------------------------------------------------------- */ - -static void dw_dma_tasklet(unsigned long data) -{ - struct dw_dma *dw = (struct dw_dma *)data; - struct dw_dma_chan *dwc; - u32 status_xfer; - u32 status_err; - int i; - - status_xfer = dma_readl(dw, RAW.XFER); - status_err = dma_readl(dw, RAW.ERROR); - - dev_vdbg(dw->dma.dev, "%s: status_err=%x\n", __func__, status_err); - - for (i = 0; i < dw->dma.chancnt; i++) { - dwc = &dw->chan[i]; - if (test_bit(DW_DMA_IS_CYCLIC, &dwc->flags)) - dwc_handle_cyclic(dw, dwc, status_err, status_xfer); - else if (status_err & (1 << i)) - dwc_handle_error(dw, dwc); - else if (status_xfer & (1 << i)) - dwc_scan_descriptors(dw, dwc); - } - - /* - * Re-enable interrupts. - */ - channel_set_bit(dw, MASK.XFER, dw->all_chan_mask); - channel_set_bit(dw, MASK.ERROR, dw->all_chan_mask); -} - -static irqreturn_t dw_dma_interrupt(int irq, void *dev_id) -{ - struct dw_dma *dw = dev_id; - u32 status; - - dev_vdbg(dw->dma.dev, "%s: status=0x%x\n", __func__, - dma_readl(dw, STATUS_INT)); - - /* - * Just disable the interrupts. We'll turn them back on in the - * softirq handler. - */ - channel_clear_bit(dw, MASK.XFER, dw->all_chan_mask); - channel_clear_bit(dw, MASK.ERROR, dw->all_chan_mask); - - status = dma_readl(dw, STATUS_INT); - if (status) { - dev_err(dw->dma.dev, - "BUG: Unexpected interrupts pending: 0x%x\n", - status); - - /* Try to recover */ - channel_clear_bit(dw, MASK.XFER, (1 << 8) - 1); - channel_clear_bit(dw, MASK.SRC_TRAN, (1 << 8) - 1); - channel_clear_bit(dw, MASK.DST_TRAN, (1 << 8) - 1); - channel_clear_bit(dw, MASK.ERROR, (1 << 8) - 1); - } - - tasklet_schedule(&dw->tasklet); - - return IRQ_HANDLED; -} - -/*----------------------------------------------------------------------*/ - -static dma_cookie_t dwc_tx_submit(struct dma_async_tx_descriptor *tx) -{ - struct dw_desc *desc = txd_to_dw_desc(tx); - struct dw_dma_chan *dwc = to_dw_dma_chan(tx->chan); - dma_cookie_t cookie; - unsigned long flags; - - spin_lock_irqsave(&dwc->lock, flags); - cookie = dma_cookie_assign(tx); - - /* - * REVISIT: We should attempt to chain as many descriptors as - * possible, perhaps even appending to those already submitted - * for DMA. But this is hard to do in a race-free manner. - */ - if (list_empty(&dwc->active_list)) { - dev_vdbg(chan2dev(tx->chan), "%s: started %u\n", __func__, - desc->txd.cookie); - list_add_tail(&desc->desc_node, &dwc->active_list); - dwc_dostart(dwc, dwc_first_active(dwc)); - } else { - dev_vdbg(chan2dev(tx->chan), "%s: queued %u\n", __func__, - desc->txd.cookie); - - list_add_tail(&desc->desc_node, &dwc->queue); - } - - spin_unlock_irqrestore(&dwc->lock, flags); - - return cookie; -} - -static struct dma_async_tx_descriptor * -dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, - size_t len, unsigned long flags) -{ - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dw_dma *dw = to_dw_dma(chan->device); - struct dw_desc *desc; - struct dw_desc *first; - struct dw_desc *prev; - size_t xfer_count; - size_t offset; - unsigned int src_width; - unsigned int dst_width; - unsigned int data_width; - u32 ctllo; - - dev_vdbg(chan2dev(chan), - "%s: d0x%llx s0x%llx l0x%zx f0x%lx\n", __func__, - (unsigned long long)dest, (unsigned long long)src, - len, flags); - - if (unlikely(!len)) { - dev_dbg(chan2dev(chan), "%s: length is zero!\n", __func__); - return NULL; - } - - dwc->direction = DMA_MEM_TO_MEM; - - data_width = min_t(unsigned int, dw->data_width[dwc->src_master], - dw->data_width[dwc->dst_master]); - - src_width = dst_width = min_t(unsigned int, data_width, - dwc_fast_fls(src | dest | len)); - - ctllo = DWC_DEFAULT_CTLLO(chan) - | DWC_CTLL_DST_WIDTH(dst_width) - | DWC_CTLL_SRC_WIDTH(src_width) - | DWC_CTLL_DST_INC - | DWC_CTLL_SRC_INC - | DWC_CTLL_FC_M2M; - prev = first = NULL; - - for (offset = 0; offset < len; offset += xfer_count << src_width) { - xfer_count = min_t(size_t, (len - offset) >> src_width, - dwc->block_size); - - desc = dwc_desc_get(dwc); - if (!desc) - goto err_desc_get; - - desc->lli.sar = src + offset; - desc->lli.dar = dest + offset; - desc->lli.ctllo = ctllo; - desc->lli.ctlhi = xfer_count; - desc->len = xfer_count << src_width; - - if (!first) { - first = desc; - } else { - prev->lli.llp = desc->txd.phys; - list_add_tail(&desc->desc_node, - &first->tx_list); - } - prev = desc; - } - - if (flags & DMA_PREP_INTERRUPT) - /* Trigger interrupt after last block */ - prev->lli.ctllo |= DWC_CTLL_INT_EN; - - prev->lli.llp = 0; - first->txd.flags = flags; - first->total_len = len; - - return &first->txd; - -err_desc_get: - dwc_desc_put(dwc, first); - return NULL; -} - -static struct dma_async_tx_descriptor * -dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, - unsigned int sg_len, enum dma_transfer_direction direction, - unsigned long flags, void *context) -{ - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dw_dma *dw = to_dw_dma(chan->device); - struct dma_slave_config *sconfig = &dwc->dma_sconfig; - struct dw_desc *prev; - struct dw_desc *first; - u32 ctllo; - dma_addr_t reg; - unsigned int reg_width; - unsigned int mem_width; - unsigned int data_width; - unsigned int i; - struct scatterlist *sg; - size_t total_len = 0; - - dev_vdbg(chan2dev(chan), "%s\n", __func__); - - if (unlikely(!is_slave_direction(direction) || !sg_len)) - return NULL; - - dwc->direction = direction; - - prev = first = NULL; - - switch (direction) { - case DMA_MEM_TO_DEV: - reg_width = __fls(sconfig->dst_addr_width); - reg = sconfig->dst_addr; - ctllo = (DWC_DEFAULT_CTLLO(chan) - | DWC_CTLL_DST_WIDTH(reg_width) - | DWC_CTLL_DST_FIX - | DWC_CTLL_SRC_INC); - - ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_M2P) : - DWC_CTLL_FC(DW_DMA_FC_D_M2P); - - data_width = dw->data_width[dwc->src_master]; - - for_each_sg(sgl, sg, sg_len, i) { - struct dw_desc *desc; - u32 len, dlen, mem; - - mem = sg_dma_address(sg); - len = sg_dma_len(sg); - - mem_width = min_t(unsigned int, - data_width, dwc_fast_fls(mem | len)); - -slave_sg_todev_fill_desc: - desc = dwc_desc_get(dwc); - if (!desc) { - dev_err(chan2dev(chan), - "not enough descriptors available\n"); - goto err_desc_get; - } - - desc->lli.sar = mem; - desc->lli.dar = reg; - desc->lli.ctllo = ctllo | DWC_CTLL_SRC_WIDTH(mem_width); - if ((len >> mem_width) > dwc->block_size) { - dlen = dwc->block_size << mem_width; - mem += dlen; - len -= dlen; - } else { - dlen = len; - len = 0; - } - - desc->lli.ctlhi = dlen >> mem_width; - desc->len = dlen; - - if (!first) { - first = desc; - } else { - prev->lli.llp = desc->txd.phys; - list_add_tail(&desc->desc_node, - &first->tx_list); - } - prev = desc; - total_len += dlen; - - if (len) - goto slave_sg_todev_fill_desc; - } - break; - case DMA_DEV_TO_MEM: - reg_width = __fls(sconfig->src_addr_width); - reg = sconfig->src_addr; - ctllo = (DWC_DEFAULT_CTLLO(chan) - | DWC_CTLL_SRC_WIDTH(reg_width) - | DWC_CTLL_DST_INC - | DWC_CTLL_SRC_FIX); - - ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_P2M) : - DWC_CTLL_FC(DW_DMA_FC_D_P2M); - - data_width = dw->data_width[dwc->dst_master]; - - for_each_sg(sgl, sg, sg_len, i) { - struct dw_desc *desc; - u32 len, dlen, mem; - - mem = sg_dma_address(sg); - len = sg_dma_len(sg); - - mem_width = min_t(unsigned int, - data_width, dwc_fast_fls(mem | len)); - -slave_sg_fromdev_fill_desc: - desc = dwc_desc_get(dwc); - if (!desc) { - dev_err(chan2dev(chan), - "not enough descriptors available\n"); - goto err_desc_get; - } - - desc->lli.sar = reg; - desc->lli.dar = mem; - desc->lli.ctllo = ctllo | DWC_CTLL_DST_WIDTH(mem_width); - if ((len >> reg_width) > dwc->block_size) { - dlen = dwc->block_size << reg_width; - mem += dlen; - len -= dlen; - } else { - dlen = len; - len = 0; - } - desc->lli.ctlhi = dlen >> reg_width; - desc->len = dlen; - - if (!first) { - first = desc; - } else { - prev->lli.llp = desc->txd.phys; - list_add_tail(&desc->desc_node, - &first->tx_list); - } - prev = desc; - total_len += dlen; - - if (len) - goto slave_sg_fromdev_fill_desc; - } - break; - default: - return NULL; - } - - if (flags & DMA_PREP_INTERRUPT) - /* Trigger interrupt after last block */ - prev->lli.ctllo |= DWC_CTLL_INT_EN; - - prev->lli.llp = 0; - first->total_len = total_len; - - return &first->txd; - -err_desc_get: - dwc_desc_put(dwc, first); - return NULL; -} - -/* - * Fix sconfig's burst size according to dw_dmac. We need to convert them as: - * 1 -> 0, 4 -> 1, 8 -> 2, 16 -> 3. - * - * NOTE: burst size 2 is not supported by controller. - * - * This can be done by finding least significant bit set: n & (n - 1) - */ -static inline void convert_burst(u32 *maxburst) -{ - if (*maxburst > 1) - *maxburst = fls(*maxburst) - 2; - else - *maxburst = 0; -} - -static int -set_runtime_config(struct dma_chan *chan, struct dma_slave_config *sconfig) -{ - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - - /* Check if chan will be configured for slave transfers */ - if (!is_slave_direction(sconfig->direction)) - return -EINVAL; - - memcpy(&dwc->dma_sconfig, sconfig, sizeof(*sconfig)); - dwc->direction = sconfig->direction; - - /* Take the request line from slave_id member */ - if (dwc->request_line == ~0) - dwc->request_line = sconfig->slave_id; - - convert_burst(&dwc->dma_sconfig.src_maxburst); - convert_burst(&dwc->dma_sconfig.dst_maxburst); - - return 0; -} - -static inline void dwc_chan_pause(struct dw_dma_chan *dwc) -{ - u32 cfglo = channel_readl(dwc, CFG_LO); - unsigned int count = 20; /* timeout iterations */ - - channel_writel(dwc, CFG_LO, cfglo | DWC_CFGL_CH_SUSP); - while (!(channel_readl(dwc, CFG_LO) & DWC_CFGL_FIFO_EMPTY) && count--) - udelay(2); - - dwc->paused = true; -} - -static inline void dwc_chan_resume(struct dw_dma_chan *dwc) -{ - u32 cfglo = channel_readl(dwc, CFG_LO); - - channel_writel(dwc, CFG_LO, cfglo & ~DWC_CFGL_CH_SUSP); - - dwc->paused = false; -} - -static int dwc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - unsigned long arg) -{ - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dw_dma *dw = to_dw_dma(chan->device); - struct dw_desc *desc, *_desc; - unsigned long flags; - LIST_HEAD(list); - - if (cmd == DMA_PAUSE) { - spin_lock_irqsave(&dwc->lock, flags); - - dwc_chan_pause(dwc); - - spin_unlock_irqrestore(&dwc->lock, flags); - } else if (cmd == DMA_RESUME) { - if (!dwc->paused) - return 0; - - spin_lock_irqsave(&dwc->lock, flags); - - dwc_chan_resume(dwc); - - spin_unlock_irqrestore(&dwc->lock, flags); - } else if (cmd == DMA_TERMINATE_ALL) { - spin_lock_irqsave(&dwc->lock, flags); - - clear_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags); - - dwc_chan_disable(dw, dwc); - - dwc_chan_resume(dwc); - - /* active_list entries will end up before queued entries */ - list_splice_init(&dwc->queue, &list); - list_splice_init(&dwc->active_list, &list); - - spin_unlock_irqrestore(&dwc->lock, flags); - - /* Flush all pending and queued descriptors */ - list_for_each_entry_safe(desc, _desc, &list, desc_node) - dwc_descriptor_complete(dwc, desc, false); - } else if (cmd == DMA_SLAVE_CONFIG) { - return set_runtime_config(chan, (struct dma_slave_config *)arg); - } else { - return -ENXIO; - } - - return 0; -} - -static inline u32 dwc_get_residue(struct dw_dma_chan *dwc) -{ - unsigned long flags; - u32 residue; - - spin_lock_irqsave(&dwc->lock, flags); - - residue = dwc->residue; - if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags) && residue) - residue -= dwc_get_sent(dwc); - - spin_unlock_irqrestore(&dwc->lock, flags); - return residue; -} - -static enum dma_status -dwc_tx_status(struct dma_chan *chan, - dma_cookie_t cookie, - struct dma_tx_state *txstate) -{ - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - enum dma_status ret; - - ret = dma_cookie_status(chan, cookie, txstate); - if (ret != DMA_SUCCESS) { - dwc_scan_descriptors(to_dw_dma(chan->device), dwc); - - ret = dma_cookie_status(chan, cookie, txstate); - } - - if (ret != DMA_SUCCESS) - dma_set_residue(txstate, dwc_get_residue(dwc)); - - if (dwc->paused) - return DMA_PAUSED; - - return ret; -} - -static void dwc_issue_pending(struct dma_chan *chan) -{ - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - - if (!list_empty(&dwc->queue)) - dwc_scan_descriptors(to_dw_dma(chan->device), dwc); -} - -static int dwc_alloc_chan_resources(struct dma_chan *chan) -{ - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dw_dma *dw = to_dw_dma(chan->device); - struct dw_desc *desc; - int i; - unsigned long flags; - - dev_vdbg(chan2dev(chan), "%s\n", __func__); - - /* ASSERT: channel is idle */ - if (dma_readl(dw, CH_EN) & dwc->mask) { - dev_dbg(chan2dev(chan), "DMA channel not idle?\n"); - return -EIO; - } - - dma_cookie_init(chan); - - /* - * NOTE: some controllers may have additional features that we - * need to initialize here, like "scatter-gather" (which - * doesn't mean what you think it means), and status writeback. - */ - - dwc_set_masters(dwc); - - spin_lock_irqsave(&dwc->lock, flags); - i = dwc->descs_allocated; - while (dwc->descs_allocated < NR_DESCS_PER_CHANNEL) { - dma_addr_t phys; - - spin_unlock_irqrestore(&dwc->lock, flags); - - desc = dma_pool_alloc(dw->desc_pool, GFP_ATOMIC, &phys); - if (!desc) - goto err_desc_alloc; - - memset(desc, 0, sizeof(struct dw_desc)); - - INIT_LIST_HEAD(&desc->tx_list); - dma_async_tx_descriptor_init(&desc->txd, chan); - desc->txd.tx_submit = dwc_tx_submit; - desc->txd.flags = DMA_CTRL_ACK; - desc->txd.phys = phys; - - dwc_desc_put(dwc, desc); - - spin_lock_irqsave(&dwc->lock, flags); - i = ++dwc->descs_allocated; - } - - spin_unlock_irqrestore(&dwc->lock, flags); - - dev_dbg(chan2dev(chan), "%s: allocated %d descriptors\n", __func__, i); - - return i; - -err_desc_alloc: - dev_info(chan2dev(chan), "only allocated %d descriptors\n", i); - - return i; -} - -static void dwc_free_chan_resources(struct dma_chan *chan) -{ - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dw_dma *dw = to_dw_dma(chan->device); - struct dw_desc *desc, *_desc; - unsigned long flags; - LIST_HEAD(list); - - dev_dbg(chan2dev(chan), "%s: descs allocated=%u\n", __func__, - dwc->descs_allocated); - - /* ASSERT: channel is idle */ - BUG_ON(!list_empty(&dwc->active_list)); - BUG_ON(!list_empty(&dwc->queue)); - BUG_ON(dma_readl(to_dw_dma(chan->device), CH_EN) & dwc->mask); - - spin_lock_irqsave(&dwc->lock, flags); - list_splice_init(&dwc->free_list, &list); - dwc->descs_allocated = 0; - dwc->initialized = false; - dwc->request_line = ~0; - - /* Disable interrupts */ - channel_clear_bit(dw, MASK.XFER, dwc->mask); - channel_clear_bit(dw, MASK.ERROR, dwc->mask); - - spin_unlock_irqrestore(&dwc->lock, flags); - - list_for_each_entry_safe(desc, _desc, &list, desc_node) { - dev_vdbg(chan2dev(chan), " freeing descriptor %p\n", desc); - dma_pool_free(dw->desc_pool, desc, desc->txd.phys); - } - - dev_vdbg(chan2dev(chan), "%s: done\n", __func__); -} - -/*----------------------------------------------------------------------*/ - -struct dw_dma_of_filter_args { - struct dw_dma *dw; - unsigned int req; - unsigned int src; - unsigned int dst; -}; - -static bool dw_dma_of_filter(struct dma_chan *chan, void *param) -{ - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dw_dma_of_filter_args *fargs = param; - - /* Ensure the device matches our channel */ - if (chan->device != &fargs->dw->dma) - return false; - - dwc->request_line = fargs->req; - dwc->src_master = fargs->src; - dwc->dst_master = fargs->dst; - - return true; -} - -static struct dma_chan *dw_dma_of_xlate(struct of_phandle_args *dma_spec, - struct of_dma *ofdma) -{ - struct dw_dma *dw = ofdma->of_dma_data; - struct dw_dma_of_filter_args fargs = { - .dw = dw, - }; - dma_cap_mask_t cap; - - if (dma_spec->args_count != 3) - return NULL; - - fargs.req = dma_spec->args[0]; - fargs.src = dma_spec->args[1]; - fargs.dst = dma_spec->args[2]; - - if (WARN_ON(fargs.req >= DW_DMA_MAX_NR_REQUESTS || - fargs.src >= dw->nr_masters || - fargs.dst >= dw->nr_masters)) - return NULL; - - dma_cap_zero(cap); - dma_cap_set(DMA_SLAVE, cap); - - /* TODO: there should be a simpler way to do this */ - return dma_request_channel(cap, dw_dma_of_filter, &fargs); -} - -#ifdef CONFIG_ACPI -static bool dw_dma_acpi_filter(struct dma_chan *chan, void *param) -{ - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct acpi_dma_spec *dma_spec = param; - - if (chan->device->dev != dma_spec->dev || - chan->chan_id != dma_spec->chan_id) - return false; - - dwc->request_line = dma_spec->slave_id; - dwc->src_master = dwc_get_sms(NULL); - dwc->dst_master = dwc_get_dms(NULL); - - return true; -} - -static void dw_dma_acpi_controller_register(struct dw_dma *dw) -{ - struct device *dev = dw->dma.dev; - struct acpi_dma_filter_info *info; - int ret; - - info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); - if (!info) - return; - - dma_cap_zero(info->dma_cap); - dma_cap_set(DMA_SLAVE, info->dma_cap); - info->filter_fn = dw_dma_acpi_filter; - - ret = devm_acpi_dma_controller_register(dev, acpi_dma_simple_xlate, - info); - if (ret) - dev_err(dev, "could not register acpi_dma_controller\n"); -} -#else /* !CONFIG_ACPI */ -static inline void dw_dma_acpi_controller_register(struct dw_dma *dw) {} -#endif /* !CONFIG_ACPI */ - -/* --------------------- Cyclic DMA API extensions -------------------- */ - -/** - * dw_dma_cyclic_start - start the cyclic DMA transfer - * @chan: the DMA channel to start - * - * Must be called with soft interrupts disabled. Returns zero on success or - * -errno on failure. - */ -int dw_dma_cyclic_start(struct dma_chan *chan) -{ - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dw_dma *dw = to_dw_dma(dwc->chan.device); - unsigned long flags; - - if (!test_bit(DW_DMA_IS_CYCLIC, &dwc->flags)) { - dev_err(chan2dev(&dwc->chan), "missing prep for cyclic DMA\n"); - return -ENODEV; - } - - spin_lock_irqsave(&dwc->lock, flags); - - /* Assert channel is idle */ - if (dma_readl(dw, CH_EN) & dwc->mask) { - dev_err(chan2dev(&dwc->chan), - "BUG: Attempted to start non-idle channel\n"); - dwc_dump_chan_regs(dwc); - spin_unlock_irqrestore(&dwc->lock, flags); - return -EBUSY; - } - - dma_writel(dw, CLEAR.ERROR, dwc->mask); - dma_writel(dw, CLEAR.XFER, dwc->mask); - - /* Setup DMAC channel registers */ - channel_writel(dwc, LLP, dwc->cdesc->desc[0]->txd.phys); - channel_writel(dwc, CTL_LO, DWC_CTLL_LLP_D_EN | DWC_CTLL_LLP_S_EN); - channel_writel(dwc, CTL_HI, 0); - - channel_set_bit(dw, CH_EN, dwc->mask); - - spin_unlock_irqrestore(&dwc->lock, flags); - - return 0; -} -EXPORT_SYMBOL(dw_dma_cyclic_start); - -/** - * dw_dma_cyclic_stop - stop the cyclic DMA transfer - * @chan: the DMA channel to stop - * - * Must be called with soft interrupts disabled. - */ -void dw_dma_cyclic_stop(struct dma_chan *chan) -{ - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dw_dma *dw = to_dw_dma(dwc->chan.device); - unsigned long flags; - - spin_lock_irqsave(&dwc->lock, flags); - - dwc_chan_disable(dw, dwc); - - spin_unlock_irqrestore(&dwc->lock, flags); -} -EXPORT_SYMBOL(dw_dma_cyclic_stop); - -/** - * dw_dma_cyclic_prep - prepare the cyclic DMA transfer - * @chan: the DMA channel to prepare - * @buf_addr: physical DMA address where the buffer starts - * @buf_len: total number of bytes for the entire buffer - * @period_len: number of bytes for each period - * @direction: transfer direction, to or from device - * - * Must be called before trying to start the transfer. Returns a valid struct - * dw_cyclic_desc if successful or an ERR_PTR(-errno) if not successful. - */ -struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, - dma_addr_t buf_addr, size_t buf_len, size_t period_len, - enum dma_transfer_direction direction) -{ - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dma_slave_config *sconfig = &dwc->dma_sconfig; - struct dw_cyclic_desc *cdesc; - struct dw_cyclic_desc *retval = NULL; - struct dw_desc *desc; - struct dw_desc *last = NULL; - unsigned long was_cyclic; - unsigned int reg_width; - unsigned int periods; - unsigned int i; - unsigned long flags; - - spin_lock_irqsave(&dwc->lock, flags); - if (dwc->nollp) { - spin_unlock_irqrestore(&dwc->lock, flags); - dev_dbg(chan2dev(&dwc->chan), - "channel doesn't support LLP transfers\n"); - return ERR_PTR(-EINVAL); - } - - if (!list_empty(&dwc->queue) || !list_empty(&dwc->active_list)) { - spin_unlock_irqrestore(&dwc->lock, flags); - dev_dbg(chan2dev(&dwc->chan), - "queue and/or active list are not empty\n"); - return ERR_PTR(-EBUSY); - } - - was_cyclic = test_and_set_bit(DW_DMA_IS_CYCLIC, &dwc->flags); - spin_unlock_irqrestore(&dwc->lock, flags); - if (was_cyclic) { - dev_dbg(chan2dev(&dwc->chan), - "channel already prepared for cyclic DMA\n"); - return ERR_PTR(-EBUSY); - } - - retval = ERR_PTR(-EINVAL); - - if (unlikely(!is_slave_direction(direction))) - goto out_err; - - dwc->direction = direction; - - if (direction == DMA_MEM_TO_DEV) - reg_width = __ffs(sconfig->dst_addr_width); - else - reg_width = __ffs(sconfig->src_addr_width); - - periods = buf_len / period_len; - - /* Check for too big/unaligned periods and unaligned DMA buffer. */ - if (period_len > (dwc->block_size << reg_width)) - goto out_err; - if (unlikely(period_len & ((1 << reg_width) - 1))) - goto out_err; - if (unlikely(buf_addr & ((1 << reg_width) - 1))) - goto out_err; - - retval = ERR_PTR(-ENOMEM); - - if (periods > NR_DESCS_PER_CHANNEL) - goto out_err; - - cdesc = kzalloc(sizeof(struct dw_cyclic_desc), GFP_KERNEL); - if (!cdesc) - goto out_err; - - cdesc->desc = kzalloc(sizeof(struct dw_desc *) * periods, GFP_KERNEL); - if (!cdesc->desc) - goto out_err_alloc; - - for (i = 0; i < periods; i++) { - desc = dwc_desc_get(dwc); - if (!desc) - goto out_err_desc_get; - - switch (direction) { - case DMA_MEM_TO_DEV: - desc->lli.dar = sconfig->dst_addr; - desc->lli.sar = buf_addr + (period_len * i); - desc->lli.ctllo = (DWC_DEFAULT_CTLLO(chan) - | DWC_CTLL_DST_WIDTH(reg_width) - | DWC_CTLL_SRC_WIDTH(reg_width) - | DWC_CTLL_DST_FIX - | DWC_CTLL_SRC_INC - | DWC_CTLL_INT_EN); - - desc->lli.ctllo |= sconfig->device_fc ? - DWC_CTLL_FC(DW_DMA_FC_P_M2P) : - DWC_CTLL_FC(DW_DMA_FC_D_M2P); - - break; - case DMA_DEV_TO_MEM: - desc->lli.dar = buf_addr + (period_len * i); - desc->lli.sar = sconfig->src_addr; - desc->lli.ctllo = (DWC_DEFAULT_CTLLO(chan) - | DWC_CTLL_SRC_WIDTH(reg_width) - | DWC_CTLL_DST_WIDTH(reg_width) - | DWC_CTLL_DST_INC - | DWC_CTLL_SRC_FIX - | DWC_CTLL_INT_EN); - - desc->lli.ctllo |= sconfig->device_fc ? - DWC_CTLL_FC(DW_DMA_FC_P_P2M) : - DWC_CTLL_FC(DW_DMA_FC_D_P2M); - - break; - default: - break; - } - - desc->lli.ctlhi = (period_len >> reg_width); - cdesc->desc[i] = desc; - - if (last) - last->lli.llp = desc->txd.phys; - - last = desc; - } - - /* Let's make a cyclic list */ - last->lli.llp = cdesc->desc[0]->txd.phys; - - dev_dbg(chan2dev(&dwc->chan), "cyclic prepared buf 0x%llx len %zu " - "period %zu periods %d\n", (unsigned long long)buf_addr, - buf_len, period_len, periods); - - cdesc->periods = periods; - dwc->cdesc = cdesc; - - return cdesc; - -out_err_desc_get: - while (i--) - dwc_desc_put(dwc, cdesc->desc[i]); -out_err_alloc: - kfree(cdesc); -out_err: - clear_bit(DW_DMA_IS_CYCLIC, &dwc->flags); - return (struct dw_cyclic_desc *)retval; -} -EXPORT_SYMBOL(dw_dma_cyclic_prep); - -/** - * dw_dma_cyclic_free - free a prepared cyclic DMA transfer - * @chan: the DMA channel to free - */ -void dw_dma_cyclic_free(struct dma_chan *chan) -{ - struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dw_dma *dw = to_dw_dma(dwc->chan.device); - struct dw_cyclic_desc *cdesc = dwc->cdesc; - int i; - unsigned long flags; - - dev_dbg(chan2dev(&dwc->chan), "%s\n", __func__); - - if (!cdesc) - return; - - spin_lock_irqsave(&dwc->lock, flags); - - dwc_chan_disable(dw, dwc); - - dma_writel(dw, CLEAR.ERROR, dwc->mask); - dma_writel(dw, CLEAR.XFER, dwc->mask); - - spin_unlock_irqrestore(&dwc->lock, flags); - - for (i = 0; i < cdesc->periods; i++) - dwc_desc_put(dwc, cdesc->desc[i]); - - kfree(cdesc->desc); - kfree(cdesc); - - clear_bit(DW_DMA_IS_CYCLIC, &dwc->flags); -} -EXPORT_SYMBOL(dw_dma_cyclic_free); - -/*----------------------------------------------------------------------*/ - -static void dw_dma_off(struct dw_dma *dw) -{ - int i; - - dma_writel(dw, CFG, 0); - - channel_clear_bit(dw, MASK.XFER, dw->all_chan_mask); - channel_clear_bit(dw, MASK.SRC_TRAN, dw->all_chan_mask); - channel_clear_bit(dw, MASK.DST_TRAN, dw->all_chan_mask); - channel_clear_bit(dw, MASK.ERROR, dw->all_chan_mask); - - while (dma_readl(dw, CFG) & DW_CFG_DMA_EN) - cpu_relax(); - - for (i = 0; i < dw->dma.chancnt; i++) - dw->chan[i].initialized = false; -} - -#ifdef CONFIG_OF -static struct dw_dma_platform_data * -dw_dma_parse_dt(struct platform_device *pdev) -{ - struct device_node *np = pdev->dev.of_node; - struct dw_dma_platform_data *pdata; - u32 tmp, arr[4]; - - if (!np) { - dev_err(&pdev->dev, "Missing DT data\n"); - return NULL; - } - - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return NULL; - - if (of_property_read_u32(np, "dma-channels", &pdata->nr_channels)) - return NULL; - - if (of_property_read_bool(np, "is_private")) - pdata->is_private = true; - - if (!of_property_read_u32(np, "chan_allocation_order", &tmp)) - pdata->chan_allocation_order = (unsigned char)tmp; - - if (!of_property_read_u32(np, "chan_priority", &tmp)) - pdata->chan_priority = tmp; - - if (!of_property_read_u32(np, "block_size", &tmp)) - pdata->block_size = tmp; - - if (!of_property_read_u32(np, "dma-masters", &tmp)) { - if (tmp > 4) - return NULL; - - pdata->nr_masters = tmp; - } - - if (!of_property_read_u32_array(np, "data_width", arr, - pdata->nr_masters)) - for (tmp = 0; tmp < pdata->nr_masters; tmp++) - pdata->data_width[tmp] = arr[tmp]; - - return pdata; -} -#else -static inline struct dw_dma_platform_data * -dw_dma_parse_dt(struct platform_device *pdev) -{ - return NULL; -} -#endif - -static int dw_probe(struct platform_device *pdev) -{ - struct dw_dma_platform_data *pdata; - struct resource *io; - struct dw_dma *dw; - size_t size; - void __iomem *regs; - bool autocfg; - unsigned int dw_params; - unsigned int nr_channels; - unsigned int max_blk_size = 0; - int irq; - int err; - int i; - - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - - io = platform_get_resource(pdev, IORESOURCE_MEM, 0); - regs = devm_ioremap_resource(&pdev->dev, io); - if (IS_ERR(regs)) - return PTR_ERR(regs); - - /* Apply default dma_mask if needed */ - if (!pdev->dev.dma_mask) { - pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; - pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); - } - - dw_params = dma_read_byaddr(regs, DW_PARAMS); - autocfg = dw_params >> DW_PARAMS_EN & 0x1; - - dev_dbg(&pdev->dev, "DW_PARAMS: 0x%08x\n", dw_params); - - pdata = dev_get_platdata(&pdev->dev); - if (!pdata) - pdata = dw_dma_parse_dt(pdev); - - if (!pdata && autocfg) { - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return -ENOMEM; - - /* Fill platform data with the default values */ - pdata->is_private = true; - pdata->chan_allocation_order = CHAN_ALLOCATION_ASCENDING; - pdata->chan_priority = CHAN_PRIORITY_ASCENDING; - } else if (!pdata || pdata->nr_channels > DW_DMA_MAX_NR_CHANNELS) - return -EINVAL; - - if (autocfg) - nr_channels = (dw_params >> DW_PARAMS_NR_CHAN & 0x7) + 1; - else - nr_channels = pdata->nr_channels; - - size = sizeof(struct dw_dma) + nr_channels * sizeof(struct dw_dma_chan); - dw = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); - if (!dw) - return -ENOMEM; - - dw->clk = devm_clk_get(&pdev->dev, "hclk"); - if (IS_ERR(dw->clk)) - return PTR_ERR(dw->clk); - clk_prepare_enable(dw->clk); - - dw->regs = regs; - - /* Get hardware configuration parameters */ - if (autocfg) { - max_blk_size = dma_readl(dw, MAX_BLK_SIZE); - - dw->nr_masters = (dw_params >> DW_PARAMS_NR_MASTER & 3) + 1; - for (i = 0; i < dw->nr_masters; i++) { - dw->data_width[i] = - (dw_params >> DW_PARAMS_DATA_WIDTH(i) & 3) + 2; - } - } else { - dw->nr_masters = pdata->nr_masters; - memcpy(dw->data_width, pdata->data_width, 4); - } - - /* Calculate all channel mask before DMA setup */ - dw->all_chan_mask = (1 << nr_channels) - 1; - - /* Force dma off, just in case */ - dw_dma_off(dw); - - /* Disable BLOCK interrupts as well */ - channel_clear_bit(dw, MASK.BLOCK, dw->all_chan_mask); - - err = devm_request_irq(&pdev->dev, irq, dw_dma_interrupt, 0, - "dw_dmac", dw); - if (err) - return err; - - platform_set_drvdata(pdev, dw); - - /* Create a pool of consistent memory blocks for hardware descriptors */ - dw->desc_pool = dmam_pool_create("dw_dmac_desc_pool", &pdev->dev, - sizeof(struct dw_desc), 4, 0); - if (!dw->desc_pool) { - dev_err(&pdev->dev, "No memory for descriptors dma pool\n"); - return -ENOMEM; - } - - tasklet_init(&dw->tasklet, dw_dma_tasklet, (unsigned long)dw); - - INIT_LIST_HEAD(&dw->dma.channels); - for (i = 0; i < nr_channels; i++) { - struct dw_dma_chan *dwc = &dw->chan[i]; - int r = nr_channels - i - 1; - - dwc->chan.device = &dw->dma; - dma_cookie_init(&dwc->chan); - if (pdata->chan_allocation_order == CHAN_ALLOCATION_ASCENDING) - list_add_tail(&dwc->chan.device_node, - &dw->dma.channels); - else - list_add(&dwc->chan.device_node, &dw->dma.channels); - - /* 7 is highest priority & 0 is lowest. */ - if (pdata->chan_priority == CHAN_PRIORITY_ASCENDING) - dwc->priority = r; - else - dwc->priority = i; - - dwc->ch_regs = &__dw_regs(dw)->CHAN[i]; - spin_lock_init(&dwc->lock); - dwc->mask = 1 << i; - - INIT_LIST_HEAD(&dwc->active_list); - INIT_LIST_HEAD(&dwc->queue); - INIT_LIST_HEAD(&dwc->free_list); - - channel_clear_bit(dw, CH_EN, dwc->mask); - - dwc->direction = DMA_TRANS_NONE; - dwc->request_line = ~0; - - /* Hardware configuration */ - if (autocfg) { - unsigned int dwc_params; - - dwc_params = dma_read_byaddr(regs + r * sizeof(u32), - DWC_PARAMS); - - dev_dbg(&pdev->dev, "DWC_PARAMS[%d]: 0x%08x\n", i, - dwc_params); - - /* Decode maximum block size for given channel. The - * stored 4 bit value represents blocks from 0x00 for 3 - * up to 0x0a for 4095. */ - dwc->block_size = - (4 << ((max_blk_size >> 4 * i) & 0xf)) - 1; - dwc->nollp = - (dwc_params >> DWC_PARAMS_MBLK_EN & 0x1) == 0; - } else { - dwc->block_size = pdata->block_size; - - /* Check if channel supports multi block transfer */ - channel_writel(dwc, LLP, 0xfffffffc); - dwc->nollp = - (channel_readl(dwc, LLP) & 0xfffffffc) == 0; - channel_writel(dwc, LLP, 0); - } - } - - /* Clear all interrupts on all channels. */ - dma_writel(dw, CLEAR.XFER, dw->all_chan_mask); - dma_writel(dw, CLEAR.BLOCK, dw->all_chan_mask); - dma_writel(dw, CLEAR.SRC_TRAN, dw->all_chan_mask); - dma_writel(dw, CLEAR.DST_TRAN, dw->all_chan_mask); - dma_writel(dw, CLEAR.ERROR, dw->all_chan_mask); - - dma_cap_set(DMA_MEMCPY, dw->dma.cap_mask); - dma_cap_set(DMA_SLAVE, dw->dma.cap_mask); - if (pdata->is_private) - dma_cap_set(DMA_PRIVATE, dw->dma.cap_mask); - dw->dma.dev = &pdev->dev; - dw->dma.device_alloc_chan_resources = dwc_alloc_chan_resources; - dw->dma.device_free_chan_resources = dwc_free_chan_resources; - - dw->dma.device_prep_dma_memcpy = dwc_prep_dma_memcpy; - - dw->dma.device_prep_slave_sg = dwc_prep_slave_sg; - dw->dma.device_control = dwc_control; - - dw->dma.device_tx_status = dwc_tx_status; - dw->dma.device_issue_pending = dwc_issue_pending; - - dma_writel(dw, CFG, DW_CFG_DMA_EN); - - dev_info(&pdev->dev, "DesignWare DMA Controller, %d channels\n", - nr_channels); - - dma_async_device_register(&dw->dma); - - if (pdev->dev.of_node) { - err = of_dma_controller_register(pdev->dev.of_node, - dw_dma_of_xlate, dw); - if (err) - dev_err(&pdev->dev, - "could not register of_dma_controller\n"); - } - - if (ACPI_HANDLE(&pdev->dev)) - dw_dma_acpi_controller_register(dw); - - return 0; -} - -static int dw_remove(struct platform_device *pdev) -{ - struct dw_dma *dw = platform_get_drvdata(pdev); - struct dw_dma_chan *dwc, *_dwc; - - if (pdev->dev.of_node) - of_dma_controller_free(pdev->dev.of_node); - dw_dma_off(dw); - dma_async_device_unregister(&dw->dma); - - tasklet_kill(&dw->tasklet); - - list_for_each_entry_safe(dwc, _dwc, &dw->dma.channels, - chan.device_node) { - list_del(&dwc->chan.device_node); - channel_clear_bit(dw, CH_EN, dwc->mask); - } - - return 0; -} - -static void dw_shutdown(struct platform_device *pdev) -{ - struct dw_dma *dw = platform_get_drvdata(pdev); - - dw_dma_off(dw); - clk_disable_unprepare(dw->clk); -} - -static int dw_suspend_noirq(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct dw_dma *dw = platform_get_drvdata(pdev); - - dw_dma_off(dw); - clk_disable_unprepare(dw->clk); - - return 0; -} - -static int dw_resume_noirq(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct dw_dma *dw = platform_get_drvdata(pdev); - - clk_prepare_enable(dw->clk); - dma_writel(dw, CFG, DW_CFG_DMA_EN); - - return 0; -} - -static const struct dev_pm_ops dw_dev_pm_ops = { - .suspend_noirq = dw_suspend_noirq, - .resume_noirq = dw_resume_noirq, - .freeze_noirq = dw_suspend_noirq, - .thaw_noirq = dw_resume_noirq, - .restore_noirq = dw_resume_noirq, - .poweroff_noirq = dw_suspend_noirq, -}; - -#ifdef CONFIG_OF -static const struct of_device_id dw_dma_of_id_table[] = { - { .compatible = "snps,dma-spear1340" }, - {} -}; -MODULE_DEVICE_TABLE(of, dw_dma_of_id_table); -#endif - -#ifdef CONFIG_ACPI -static const struct acpi_device_id dw_dma_acpi_id_table[] = { - { "INTL9C60", 0 }, - { } -}; -#endif - -static struct platform_driver dw_driver = { - .probe = dw_probe, - .remove = dw_remove, - .shutdown = dw_shutdown, - .driver = { - .name = "dw_dmac", - .pm = &dw_dev_pm_ops, - .of_match_table = of_match_ptr(dw_dma_of_id_table), - .acpi_match_table = ACPI_PTR(dw_dma_acpi_id_table), - }, -}; - -static int __init dw_init(void) -{ - return platform_driver_register(&dw_driver); -} -subsys_initcall(dw_init); - -static void __exit dw_exit(void) -{ - platform_driver_unregister(&dw_driver); -} -module_exit(dw_exit); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("Synopsys DesignWare DMA Controller driver"); -MODULE_AUTHOR("Haavard Skinnemoen (Atmel)"); -MODULE_AUTHOR("Viresh Kumar "); diff --git a/drivers/dma/dw/dw_dmac_regs.h b/drivers/dma/dw/dw_dmac_regs.h deleted file mode 100644 index 9d417200bd57..000000000000 --- a/drivers/dma/dw/dw_dmac_regs.h +++ /dev/null @@ -1,311 +0,0 @@ -/* - * Driver for the Synopsys DesignWare AHB DMA Controller - * - * Copyright (C) 2005-2007 Atmel Corporation - * Copyright (C) 2010-2011 ST Microelectronics - * - * 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 -#include - -#define DW_DMA_MAX_NR_CHANNELS 8 -#define DW_DMA_MAX_NR_REQUESTS 16 - -/* flow controller */ -enum dw_dma_fc { - DW_DMA_FC_D_M2M, - DW_DMA_FC_D_M2P, - DW_DMA_FC_D_P2M, - DW_DMA_FC_D_P2P, - DW_DMA_FC_P_P2M, - DW_DMA_FC_SP_P2P, - DW_DMA_FC_P_M2P, - DW_DMA_FC_DP_P2P, -}; - -/* - * Redefine this macro to handle differences between 32- and 64-bit - * addressing, big vs. little endian, etc. - */ -#define DW_REG(name) u32 name; u32 __pad_##name - -/* Hardware register definitions. */ -struct dw_dma_chan_regs { - DW_REG(SAR); /* Source Address Register */ - DW_REG(DAR); /* Destination Address Register */ - DW_REG(LLP); /* Linked List Pointer */ - u32 CTL_LO; /* Control Register Low */ - u32 CTL_HI; /* Control Register High */ - DW_REG(SSTAT); - DW_REG(DSTAT); - DW_REG(SSTATAR); - DW_REG(DSTATAR); - u32 CFG_LO; /* Configuration Register Low */ - u32 CFG_HI; /* Configuration Register High */ - DW_REG(SGR); - DW_REG(DSR); -}; - -struct dw_dma_irq_regs { - DW_REG(XFER); - DW_REG(BLOCK); - DW_REG(SRC_TRAN); - DW_REG(DST_TRAN); - DW_REG(ERROR); -}; - -struct dw_dma_regs { - /* per-channel registers */ - struct dw_dma_chan_regs CHAN[DW_DMA_MAX_NR_CHANNELS]; - - /* irq handling */ - struct dw_dma_irq_regs RAW; /* r */ - struct dw_dma_irq_regs STATUS; /* r (raw & mask) */ - struct dw_dma_irq_regs MASK; /* rw (set = irq enabled) */ - struct dw_dma_irq_regs CLEAR; /* w (ack, affects "raw") */ - - DW_REG(STATUS_INT); /* r */ - - /* software handshaking */ - DW_REG(REQ_SRC); - DW_REG(REQ_DST); - DW_REG(SGL_REQ_SRC); - DW_REG(SGL_REQ_DST); - DW_REG(LAST_SRC); - DW_REG(LAST_DST); - - /* miscellaneous */ - DW_REG(CFG); - DW_REG(CH_EN); - DW_REG(ID); - DW_REG(TEST); - - /* reserved */ - DW_REG(__reserved0); - DW_REG(__reserved1); - - /* optional encoded params, 0x3c8..0x3f7 */ - u32 __reserved; - - /* per-channel configuration registers */ - u32 DWC_PARAMS[DW_DMA_MAX_NR_CHANNELS]; - u32 MULTI_BLK_TYPE; - u32 MAX_BLK_SIZE; - - /* top-level parameters */ - u32 DW_PARAMS; -}; - -#ifdef CONFIG_DW_DMAC_BIG_ENDIAN_IO -#define dma_readl_native ioread32be -#define dma_writel_native iowrite32be -#else -#define dma_readl_native readl -#define dma_writel_native writel -#endif - -/* To access the registers in early stage of probe */ -#define dma_read_byaddr(addr, name) \ - dma_readl_native((addr) + offsetof(struct dw_dma_regs, name)) - -/* Bitfields in DW_PARAMS */ -#define DW_PARAMS_NR_CHAN 8 /* number of channels */ -#define DW_PARAMS_NR_MASTER 11 /* number of AHB masters */ -#define DW_PARAMS_DATA_WIDTH(n) (15 + 2 * (n)) -#define DW_PARAMS_DATA_WIDTH1 15 /* master 1 data width */ -#define DW_PARAMS_DATA_WIDTH2 17 /* master 2 data width */ -#define DW_PARAMS_DATA_WIDTH3 19 /* master 3 data width */ -#define DW_PARAMS_DATA_WIDTH4 21 /* master 4 data width */ -#define DW_PARAMS_EN 28 /* encoded parameters */ - -/* Bitfields in DWC_PARAMS */ -#define DWC_PARAMS_MBLK_EN 11 /* multi block transfer */ - -/* Bitfields in CTL_LO */ -#define DWC_CTLL_INT_EN (1 << 0) /* irqs enabled? */ -#define DWC_CTLL_DST_WIDTH(n) ((n)<<1) /* bytes per element */ -#define DWC_CTLL_SRC_WIDTH(n) ((n)<<4) -#define DWC_CTLL_DST_INC (0<<7) /* DAR update/not */ -#define DWC_CTLL_DST_DEC (1<<7) -#define DWC_CTLL_DST_FIX (2<<7) -#define DWC_CTLL_SRC_INC (0<<7) /* SAR update/not */ -#define DWC_CTLL_SRC_DEC (1<<9) -#define DWC_CTLL_SRC_FIX (2<<9) -#define DWC_CTLL_DST_MSIZE(n) ((n)<<11) /* burst, #elements */ -#define DWC_CTLL_SRC_MSIZE(n) ((n)<<14) -#define DWC_CTLL_S_GATH_EN (1 << 17) /* src gather, !FIX */ -#define DWC_CTLL_D_SCAT_EN (1 << 18) /* dst scatter, !FIX */ -#define DWC_CTLL_FC(n) ((n) << 20) -#define DWC_CTLL_FC_M2M (0 << 20) /* mem-to-mem */ -#define DWC_CTLL_FC_M2P (1 << 20) /* mem-to-periph */ -#define DWC_CTLL_FC_P2M (2 << 20) /* periph-to-mem */ -#define DWC_CTLL_FC_P2P (3 << 20) /* periph-to-periph */ -/* plus 4 transfer types for peripheral-as-flow-controller */ -#define DWC_CTLL_DMS(n) ((n)<<23) /* dst master select */ -#define DWC_CTLL_SMS(n) ((n)<<25) /* src master select */ -#define DWC_CTLL_LLP_D_EN (1 << 27) /* dest block chain */ -#define DWC_CTLL_LLP_S_EN (1 << 28) /* src block chain */ - -/* Bitfields in CTL_HI */ -#define DWC_CTLH_DONE 0x00001000 -#define DWC_CTLH_BLOCK_TS_MASK 0x00000fff - -/* Bitfields in CFG_LO. Platform-configurable bits are in */ -#define DWC_CFGL_CH_PRIOR_MASK (0x7 << 5) /* priority mask */ -#define DWC_CFGL_CH_PRIOR(x) ((x) << 5) /* priority */ -#define DWC_CFGL_CH_SUSP (1 << 8) /* pause xfer */ -#define DWC_CFGL_FIFO_EMPTY (1 << 9) /* pause xfer */ -#define DWC_CFGL_HS_DST (1 << 10) /* handshake w/dst */ -#define DWC_CFGL_HS_SRC (1 << 11) /* handshake w/src */ -#define DWC_CFGL_MAX_BURST(x) ((x) << 20) -#define DWC_CFGL_RELOAD_SAR (1 << 30) -#define DWC_CFGL_RELOAD_DAR (1 << 31) - -/* Bitfields in CFG_HI. Platform-configurable bits are in */ -#define DWC_CFGH_DS_UPD_EN (1 << 5) -#define DWC_CFGH_SS_UPD_EN (1 << 6) - -/* Bitfields in SGR */ -#define DWC_SGR_SGI(x) ((x) << 0) -#define DWC_SGR_SGC(x) ((x) << 20) - -/* Bitfields in DSR */ -#define DWC_DSR_DSI(x) ((x) << 0) -#define DWC_DSR_DSC(x) ((x) << 20) - -/* Bitfields in CFG */ -#define DW_CFG_DMA_EN (1 << 0) - -enum dw_dmac_flags { - DW_DMA_IS_CYCLIC = 0, - DW_DMA_IS_SOFT_LLP = 1, -}; - -struct dw_dma_chan { - struct dma_chan chan; - void __iomem *ch_regs; - u8 mask; - u8 priority; - enum dma_transfer_direction direction; - bool paused; - bool initialized; - - /* software emulation of the LLP transfers */ - struct list_head *tx_node_active; - - spinlock_t lock; - - /* these other elements are all protected by lock */ - unsigned long flags; - struct list_head active_list; - struct list_head queue; - struct list_head free_list; - u32 residue; - struct dw_cyclic_desc *cdesc; - - unsigned int descs_allocated; - - /* hardware configuration */ - unsigned int block_size; - bool nollp; - - /* custom slave configuration */ - unsigned int request_line; - unsigned char src_master; - unsigned char dst_master; - - /* configuration passed via DMA_SLAVE_CONFIG */ - struct dma_slave_config dma_sconfig; -}; - -static inline struct dw_dma_chan_regs __iomem * -__dwc_regs(struct dw_dma_chan *dwc) -{ - return dwc->ch_regs; -} - -#define channel_readl(dwc, name) \ - dma_readl_native(&(__dwc_regs(dwc)->name)) -#define channel_writel(dwc, name, val) \ - dma_writel_native((val), &(__dwc_regs(dwc)->name)) - -static inline struct dw_dma_chan *to_dw_dma_chan(struct dma_chan *chan) -{ - return container_of(chan, struct dw_dma_chan, chan); -} - -struct dw_dma { - struct dma_device dma; - void __iomem *regs; - struct dma_pool *desc_pool; - struct tasklet_struct tasklet; - struct clk *clk; - - u8 all_chan_mask; - - /* hardware configuration */ - unsigned char nr_masters; - unsigned char data_width[4]; - - struct dw_dma_chan chan[0]; -}; - -static inline struct dw_dma_regs __iomem *__dw_regs(struct dw_dma *dw) -{ - return dw->regs; -} - -#define dma_readl(dw, name) \ - dma_readl_native(&(__dw_regs(dw)->name)) -#define dma_writel(dw, name, val) \ - dma_writel_native((val), &(__dw_regs(dw)->name)) - -#define channel_set_bit(dw, reg, mask) \ - dma_writel(dw, reg, ((mask) << 8) | (mask)) -#define channel_clear_bit(dw, reg, mask) \ - dma_writel(dw, reg, ((mask) << 8) | 0) - -static inline struct dw_dma *to_dw_dma(struct dma_device *ddev) -{ - return container_of(ddev, struct dw_dma, dma); -} - -/* LLI == Linked List Item; a.k.a. DMA block descriptor */ -struct dw_lli { - /* values that are not changed by hardware */ - u32 sar; - u32 dar; - u32 llp; /* chain to next lli */ - u32 ctllo; - /* values that may get written back: */ - u32 ctlhi; - /* sstat and dstat can snapshot peripheral register state. - * silicon config may discard either or both... - */ - u32 sstat; - u32 dstat; -}; - -struct dw_desc { - /* FIRST values the hardware uses */ - struct dw_lli lli; - - /* THEN values for driver housekeeping */ - struct list_head desc_node; - struct list_head tx_list; - struct dma_async_tx_descriptor txd; - size_t len; - size_t total_len; -}; - -#define to_dw_desc(h) list_entry(h, struct dw_desc, desc_node) - -static inline struct dw_desc * -txd_to_dw_desc(struct dma_async_tx_descriptor *txd) -{ - return container_of(txd, struct dw_desc, txd); -} diff --git a/drivers/dma/dw/internal.h b/drivers/dma/dw/internal.h new file mode 100644 index 000000000000..32667f9e0dda --- /dev/null +++ b/drivers/dma/dw/internal.h @@ -0,0 +1,70 @@ +/* + * Driver for the Synopsys DesignWare DMA Controller + * + * Copyright (C) 2013 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _DW_DMAC_INTERNAL_H +#define _DW_DMAC_INTERNAL_H + +#include +#include + +#include "regs.h" + +/** + * struct dw_dma_chip - representation of DesignWare DMA controller hardware + * @dev: struct device of the DMA controller + * @irq: irq line + * @regs: memory mapped I/O space + * @dw: struct dw_dma that is filed by dw_dma_probe() + */ +struct dw_dma_chip { + struct device *dev; + int irq; + void __iomem *regs; + struct dw_dma *dw; +}; + +/* Export to the platform drivers */ +int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata); +int dw_dma_remove(struct dw_dma_chip *chip); + +void dw_dma_shutdown(struct dw_dma_chip *chip); + +#ifdef CONFIG_PM_SLEEP + +int dw_dma_suspend(struct dw_dma_chip *chip); +int dw_dma_resume(struct dw_dma_chip *chip); + +#endif /* CONFIG_PM_SLEEP */ + +/** + * dwc_get_dms - get destination master + * @slave: pointer to the custom slave configuration + * + * Returns destination master in the custom slave configuration if defined, or + * default value otherwise. + */ +static inline unsigned int dwc_get_dms(struct dw_dma_slave *slave) +{ + return slave ? slave->dst_master : 0; +} + +/** + * dwc_get_sms - get source master + * @slave: pointer to the custom slave configuration + * + * Returns source master in the custom slave configuration if defined, or + * default value otherwise. + */ +static inline unsigned int dwc_get_sms(struct dw_dma_slave *slave) +{ + return slave ? slave->src_master : 1; +} + +#endif /* _DW_DMAC_INTERNAL_H */ diff --git a/drivers/dma/dw/platform.c b/drivers/dma/dw/platform.c new file mode 100644 index 000000000000..6c9449cffae8 --- /dev/null +++ b/drivers/dma/dw/platform.c @@ -0,0 +1,317 @@ +/* + * Platform driver for the Synopsys DesignWare DMA Controller + * + * Copyright (C) 2007-2008 Atmel Corporation + * Copyright (C) 2010-2011 ST Microelectronics + * Copyright (C) 2013 Intel Corporation + * + * Some parts of this driver are derived from the original dw_dmac. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "internal.h" + +struct dw_dma_of_filter_args { + struct dw_dma *dw; + unsigned int req; + unsigned int src; + unsigned int dst; +}; + +static bool dw_dma_of_filter(struct dma_chan *chan, void *param) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_dma_of_filter_args *fargs = param; + + /* Ensure the device matches our channel */ + if (chan->device != &fargs->dw->dma) + return false; + + dwc->request_line = fargs->req; + dwc->src_master = fargs->src; + dwc->dst_master = fargs->dst; + + return true; +} + +static struct dma_chan *dw_dma_of_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + struct dw_dma *dw = ofdma->of_dma_data; + struct dw_dma_of_filter_args fargs = { + .dw = dw, + }; + dma_cap_mask_t cap; + + if (dma_spec->args_count != 3) + return NULL; + + fargs.req = dma_spec->args[0]; + fargs.src = dma_spec->args[1]; + fargs.dst = dma_spec->args[2]; + + if (WARN_ON(fargs.req >= DW_DMA_MAX_NR_REQUESTS || + fargs.src >= dw->nr_masters || + fargs.dst >= dw->nr_masters)) + return NULL; + + dma_cap_zero(cap); + dma_cap_set(DMA_SLAVE, cap); + + /* TODO: there should be a simpler way to do this */ + return dma_request_channel(cap, dw_dma_of_filter, &fargs); +} + +#ifdef CONFIG_ACPI +static bool dw_dma_acpi_filter(struct dma_chan *chan, void *param) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct acpi_dma_spec *dma_spec = param; + + if (chan->device->dev != dma_spec->dev || + chan->chan_id != dma_spec->chan_id) + return false; + + dwc->request_line = dma_spec->slave_id; + dwc->src_master = dwc_get_sms(NULL); + dwc->dst_master = dwc_get_dms(NULL); + + return true; +} + +static void dw_dma_acpi_controller_register(struct dw_dma *dw) +{ + struct device *dev = dw->dma.dev; + struct acpi_dma_filter_info *info; + int ret; + + info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); + if (!info) + return; + + dma_cap_zero(info->dma_cap); + dma_cap_set(DMA_SLAVE, info->dma_cap); + info->filter_fn = dw_dma_acpi_filter; + + ret = devm_acpi_dma_controller_register(dev, acpi_dma_simple_xlate, + info); + if (ret) + dev_err(dev, "could not register acpi_dma_controller\n"); +} +#else /* !CONFIG_ACPI */ +static inline void dw_dma_acpi_controller_register(struct dw_dma *dw) {} +#endif /* !CONFIG_ACPI */ + +#ifdef CONFIG_OF +static struct dw_dma_platform_data * +dw_dma_parse_dt(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct dw_dma_platform_data *pdata; + u32 tmp, arr[4]; + + if (!np) { + dev_err(&pdev->dev, "Missing DT data\n"); + return NULL; + } + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; + + if (of_property_read_u32(np, "dma-channels", &pdata->nr_channels)) + return NULL; + + if (of_property_read_bool(np, "is_private")) + pdata->is_private = true; + + if (!of_property_read_u32(np, "chan_allocation_order", &tmp)) + pdata->chan_allocation_order = (unsigned char)tmp; + + if (!of_property_read_u32(np, "chan_priority", &tmp)) + pdata->chan_priority = tmp; + + if (!of_property_read_u32(np, "block_size", &tmp)) + pdata->block_size = tmp; + + if (!of_property_read_u32(np, "dma-masters", &tmp)) { + if (tmp > 4) + return NULL; + + pdata->nr_masters = tmp; + } + + if (!of_property_read_u32_array(np, "data_width", arr, + pdata->nr_masters)) + for (tmp = 0; tmp < pdata->nr_masters; tmp++) + pdata->data_width[tmp] = arr[tmp]; + + return pdata; +} +#else +static inline struct dw_dma_platform_data * +dw_dma_parse_dt(struct platform_device *pdev) +{ + return NULL; +} +#endif + +static int dw_probe(struct platform_device *pdev) +{ + struct dw_dma_chip *chip; + struct device *dev = &pdev->dev; + struct resource *mem; + struct dw_dma_platform_data *pdata; + int err; + + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->irq = platform_get_irq(pdev, 0); + if (chip->irq < 0) + return chip->irq; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + chip->regs = devm_ioremap_resource(dev, mem); + if (IS_ERR(chip->regs)) + return PTR_ERR(chip->regs); + + /* Apply default dma_mask if needed */ + if (!dev->dma_mask) { + dev->dma_mask = &dev->coherent_dma_mask; + dev->coherent_dma_mask = DMA_BIT_MASK(32); + } + + pdata = dev_get_platdata(dev); + if (!pdata) + pdata = dw_dma_parse_dt(pdev); + + chip->dev = dev; + + err = dw_dma_probe(chip, pdata); + if (err) + return err; + + platform_set_drvdata(pdev, chip); + + if (pdev->dev.of_node) { + err = of_dma_controller_register(pdev->dev.of_node, + dw_dma_of_xlate, chip->dw); + if (err) + dev_err(&pdev->dev, + "could not register of_dma_controller\n"); + } + + if (ACPI_HANDLE(&pdev->dev)) + dw_dma_acpi_controller_register(chip->dw); + + return 0; +} + +static int dw_remove(struct platform_device *pdev) +{ + struct dw_dma_chip *chip = platform_get_drvdata(pdev); + + if (pdev->dev.of_node) + of_dma_controller_free(pdev->dev.of_node); + + return dw_dma_remove(chip); +} + +static void dw_shutdown(struct platform_device *pdev) +{ + struct dw_dma_chip *chip = platform_get_drvdata(pdev); + + dw_dma_shutdown(chip); +} + +#ifdef CONFIG_OF +static const struct of_device_id dw_dma_of_id_table[] = { + { .compatible = "snps,dma-spear1340" }, + {} +}; +MODULE_DEVICE_TABLE(of, dw_dma_of_id_table); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id dw_dma_acpi_id_table[] = { + { "INTL9C60", 0 }, + { } +}; +#endif + +#ifdef CONFIG_PM_SLEEP + +static int dw_suspend_noirq(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct dw_dma_chip *chip = platform_get_drvdata(pdev); + + return dw_dma_suspend(chip); +} + +static int dw_resume_noirq(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct dw_dma_chip *chip = platform_get_drvdata(pdev); + + return dw_dma_resume(chip); +} + +#else /* !CONFIG_PM_SLEEP */ + +#define dw_suspend_noirq NULL +#define dw_resume_noirq NULL + +#endif /* !CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops dw_dev_pm_ops = { + .suspend_noirq = dw_suspend_noirq, + .resume_noirq = dw_resume_noirq, + .freeze_noirq = dw_suspend_noirq, + .thaw_noirq = dw_resume_noirq, + .restore_noirq = dw_resume_noirq, + .poweroff_noirq = dw_suspend_noirq, +}; + +static struct platform_driver dw_driver = { + .probe = dw_probe, + .remove = dw_remove, + .shutdown = dw_shutdown, + .driver = { + .name = "dw_dmac", + .pm = &dw_dev_pm_ops, + .of_match_table = of_match_ptr(dw_dma_of_id_table), + .acpi_match_table = ACPI_PTR(dw_dma_acpi_id_table), + }, +}; + +static int __init dw_init(void) +{ + return platform_driver_register(&dw_driver); +} +subsys_initcall(dw_init); + +static void __exit dw_exit(void) +{ + platform_driver_unregister(&dw_driver); +} +module_exit(dw_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Synopsys DesignWare DMA Controller platform driver"); diff --git a/drivers/dma/dw/regs.h b/drivers/dma/dw/regs.h new file mode 100644 index 000000000000..07c5a6ecb52b --- /dev/null +++ b/drivers/dma/dw/regs.h @@ -0,0 +1,312 @@ +/* + * Driver for the Synopsys DesignWare AHB DMA Controller + * + * Copyright (C) 2005-2007 Atmel Corporation + * Copyright (C) 2010-2011 ST Microelectronics + * + * 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 +#include +#include + +#define DW_DMA_MAX_NR_CHANNELS 8 +#define DW_DMA_MAX_NR_REQUESTS 16 + +/* flow controller */ +enum dw_dma_fc { + DW_DMA_FC_D_M2M, + DW_DMA_FC_D_M2P, + DW_DMA_FC_D_P2M, + DW_DMA_FC_D_P2P, + DW_DMA_FC_P_P2M, + DW_DMA_FC_SP_P2P, + DW_DMA_FC_P_M2P, + DW_DMA_FC_DP_P2P, +}; + +/* + * Redefine this macro to handle differences between 32- and 64-bit + * addressing, big vs. little endian, etc. + */ +#define DW_REG(name) u32 name; u32 __pad_##name + +/* Hardware register definitions. */ +struct dw_dma_chan_regs { + DW_REG(SAR); /* Source Address Register */ + DW_REG(DAR); /* Destination Address Register */ + DW_REG(LLP); /* Linked List Pointer */ + u32 CTL_LO; /* Control Register Low */ + u32 CTL_HI; /* Control Register High */ + DW_REG(SSTAT); + DW_REG(DSTAT); + DW_REG(SSTATAR); + DW_REG(DSTATAR); + u32 CFG_LO; /* Configuration Register Low */ + u32 CFG_HI; /* Configuration Register High */ + DW_REG(SGR); + DW_REG(DSR); +}; + +struct dw_dma_irq_regs { + DW_REG(XFER); + DW_REG(BLOCK); + DW_REG(SRC_TRAN); + DW_REG(DST_TRAN); + DW_REG(ERROR); +}; + +struct dw_dma_regs { + /* per-channel registers */ + struct dw_dma_chan_regs CHAN[DW_DMA_MAX_NR_CHANNELS]; + + /* irq handling */ + struct dw_dma_irq_regs RAW; /* r */ + struct dw_dma_irq_regs STATUS; /* r (raw & mask) */ + struct dw_dma_irq_regs MASK; /* rw (set = irq enabled) */ + struct dw_dma_irq_regs CLEAR; /* w (ack, affects "raw") */ + + DW_REG(STATUS_INT); /* r */ + + /* software handshaking */ + DW_REG(REQ_SRC); + DW_REG(REQ_DST); + DW_REG(SGL_REQ_SRC); + DW_REG(SGL_REQ_DST); + DW_REG(LAST_SRC); + DW_REG(LAST_DST); + + /* miscellaneous */ + DW_REG(CFG); + DW_REG(CH_EN); + DW_REG(ID); + DW_REG(TEST); + + /* reserved */ + DW_REG(__reserved0); + DW_REG(__reserved1); + + /* optional encoded params, 0x3c8..0x3f7 */ + u32 __reserved; + + /* per-channel configuration registers */ + u32 DWC_PARAMS[DW_DMA_MAX_NR_CHANNELS]; + u32 MULTI_BLK_TYPE; + u32 MAX_BLK_SIZE; + + /* top-level parameters */ + u32 DW_PARAMS; +}; + +#ifdef CONFIG_DW_DMAC_BIG_ENDIAN_IO +#define dma_readl_native ioread32be +#define dma_writel_native iowrite32be +#else +#define dma_readl_native readl +#define dma_writel_native writel +#endif + +/* To access the registers in early stage of probe */ +#define dma_read_byaddr(addr, name) \ + dma_readl_native((addr) + offsetof(struct dw_dma_regs, name)) + +/* Bitfields in DW_PARAMS */ +#define DW_PARAMS_NR_CHAN 8 /* number of channels */ +#define DW_PARAMS_NR_MASTER 11 /* number of AHB masters */ +#define DW_PARAMS_DATA_WIDTH(n) (15 + 2 * (n)) +#define DW_PARAMS_DATA_WIDTH1 15 /* master 1 data width */ +#define DW_PARAMS_DATA_WIDTH2 17 /* master 2 data width */ +#define DW_PARAMS_DATA_WIDTH3 19 /* master 3 data width */ +#define DW_PARAMS_DATA_WIDTH4 21 /* master 4 data width */ +#define DW_PARAMS_EN 28 /* encoded parameters */ + +/* Bitfields in DWC_PARAMS */ +#define DWC_PARAMS_MBLK_EN 11 /* multi block transfer */ + +/* Bitfields in CTL_LO */ +#define DWC_CTLL_INT_EN (1 << 0) /* irqs enabled? */ +#define DWC_CTLL_DST_WIDTH(n) ((n)<<1) /* bytes per element */ +#define DWC_CTLL_SRC_WIDTH(n) ((n)<<4) +#define DWC_CTLL_DST_INC (0<<7) /* DAR update/not */ +#define DWC_CTLL_DST_DEC (1<<7) +#define DWC_CTLL_DST_FIX (2<<7) +#define DWC_CTLL_SRC_INC (0<<7) /* SAR update/not */ +#define DWC_CTLL_SRC_DEC (1<<9) +#define DWC_CTLL_SRC_FIX (2<<9) +#define DWC_CTLL_DST_MSIZE(n) ((n)<<11) /* burst, #elements */ +#define DWC_CTLL_SRC_MSIZE(n) ((n)<<14) +#define DWC_CTLL_S_GATH_EN (1 << 17) /* src gather, !FIX */ +#define DWC_CTLL_D_SCAT_EN (1 << 18) /* dst scatter, !FIX */ +#define DWC_CTLL_FC(n) ((n) << 20) +#define DWC_CTLL_FC_M2M (0 << 20) /* mem-to-mem */ +#define DWC_CTLL_FC_M2P (1 << 20) /* mem-to-periph */ +#define DWC_CTLL_FC_P2M (2 << 20) /* periph-to-mem */ +#define DWC_CTLL_FC_P2P (3 << 20) /* periph-to-periph */ +/* plus 4 transfer types for peripheral-as-flow-controller */ +#define DWC_CTLL_DMS(n) ((n)<<23) /* dst master select */ +#define DWC_CTLL_SMS(n) ((n)<<25) /* src master select */ +#define DWC_CTLL_LLP_D_EN (1 << 27) /* dest block chain */ +#define DWC_CTLL_LLP_S_EN (1 << 28) /* src block chain */ + +/* Bitfields in CTL_HI */ +#define DWC_CTLH_DONE 0x00001000 +#define DWC_CTLH_BLOCK_TS_MASK 0x00000fff + +/* Bitfields in CFG_LO. Platform-configurable bits are in */ +#define DWC_CFGL_CH_PRIOR_MASK (0x7 << 5) /* priority mask */ +#define DWC_CFGL_CH_PRIOR(x) ((x) << 5) /* priority */ +#define DWC_CFGL_CH_SUSP (1 << 8) /* pause xfer */ +#define DWC_CFGL_FIFO_EMPTY (1 << 9) /* pause xfer */ +#define DWC_CFGL_HS_DST (1 << 10) /* handshake w/dst */ +#define DWC_CFGL_HS_SRC (1 << 11) /* handshake w/src */ +#define DWC_CFGL_MAX_BURST(x) ((x) << 20) +#define DWC_CFGL_RELOAD_SAR (1 << 30) +#define DWC_CFGL_RELOAD_DAR (1 << 31) + +/* Bitfields in CFG_HI. Platform-configurable bits are in */ +#define DWC_CFGH_DS_UPD_EN (1 << 5) +#define DWC_CFGH_SS_UPD_EN (1 << 6) + +/* Bitfields in SGR */ +#define DWC_SGR_SGI(x) ((x) << 0) +#define DWC_SGR_SGC(x) ((x) << 20) + +/* Bitfields in DSR */ +#define DWC_DSR_DSI(x) ((x) << 0) +#define DWC_DSR_DSC(x) ((x) << 20) + +/* Bitfields in CFG */ +#define DW_CFG_DMA_EN (1 << 0) + +enum dw_dmac_flags { + DW_DMA_IS_CYCLIC = 0, + DW_DMA_IS_SOFT_LLP = 1, +}; + +struct dw_dma_chan { + struct dma_chan chan; + void __iomem *ch_regs; + u8 mask; + u8 priority; + enum dma_transfer_direction direction; + bool paused; + bool initialized; + + /* software emulation of the LLP transfers */ + struct list_head *tx_node_active; + + spinlock_t lock; + + /* these other elements are all protected by lock */ + unsigned long flags; + struct list_head active_list; + struct list_head queue; + struct list_head free_list; + u32 residue; + struct dw_cyclic_desc *cdesc; + + unsigned int descs_allocated; + + /* hardware configuration */ + unsigned int block_size; + bool nollp; + + /* custom slave configuration */ + unsigned int request_line; + unsigned char src_master; + unsigned char dst_master; + + /* configuration passed via DMA_SLAVE_CONFIG */ + struct dma_slave_config dma_sconfig; +}; + +static inline struct dw_dma_chan_regs __iomem * +__dwc_regs(struct dw_dma_chan *dwc) +{ + return dwc->ch_regs; +} + +#define channel_readl(dwc, name) \ + dma_readl_native(&(__dwc_regs(dwc)->name)) +#define channel_writel(dwc, name, val) \ + dma_writel_native((val), &(__dwc_regs(dwc)->name)) + +static inline struct dw_dma_chan *to_dw_dma_chan(struct dma_chan *chan) +{ + return container_of(chan, struct dw_dma_chan, chan); +} + +struct dw_dma { + struct dma_device dma; + void __iomem *regs; + struct dma_pool *desc_pool; + struct tasklet_struct tasklet; + struct clk *clk; + + u8 all_chan_mask; + + /* hardware configuration */ + unsigned char nr_masters; + unsigned char data_width[4]; + + struct dw_dma_chan chan[0]; +}; + +static inline struct dw_dma_regs __iomem *__dw_regs(struct dw_dma *dw) +{ + return dw->regs; +} + +#define dma_readl(dw, name) \ + dma_readl_native(&(__dw_regs(dw)->name)) +#define dma_writel(dw, name, val) \ + dma_writel_native((val), &(__dw_regs(dw)->name)) + +#define channel_set_bit(dw, reg, mask) \ + dma_writel(dw, reg, ((mask) << 8) | (mask)) +#define channel_clear_bit(dw, reg, mask) \ + dma_writel(dw, reg, ((mask) << 8) | 0) + +static inline struct dw_dma *to_dw_dma(struct dma_device *ddev) +{ + return container_of(ddev, struct dw_dma, dma); +} + +/* LLI == Linked List Item; a.k.a. DMA block descriptor */ +struct dw_lli { + /* values that are not changed by hardware */ + u32 sar; + u32 dar; + u32 llp; /* chain to next lli */ + u32 ctllo; + /* values that may get written back: */ + u32 ctlhi; + /* sstat and dstat can snapshot peripheral register state. + * silicon config may discard either or both... + */ + u32 sstat; + u32 dstat; +}; + +struct dw_desc { + /* FIRST values the hardware uses */ + struct dw_lli lli; + + /* THEN values for driver housekeeping */ + struct list_head desc_node; + struct list_head tx_list; + struct dma_async_tx_descriptor txd; + size_t len; + size_t total_len; +}; + +#define to_dw_desc(h) list_entry(h, struct dw_desc, desc_node) + +static inline struct dw_desc * +txd_to_dw_desc(struct dma_async_tx_descriptor *txd) +{ + return container_of(txd, struct dw_desc, txd); +} -- cgit v1.2.3 From fed42c198b45ece0b37eb25d37cbc4a9959c6522 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 5 Jun 2013 15:26:46 +0300 Subject: dma: dw: add PCI part of the driver This is the PCI part of the DesignWare DMAC driver. The controller is usually used in the Intel hardware such as Intel Medfield. Signed-off-by: Andy Shevchenko Acked-by: Arnd Bergmann Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw/Kconfig | 9 +++++ drivers/dma/dw/Makefile | 3 ++ drivers/dma/dw/pci.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 113 insertions(+) create mode 100644 drivers/dma/dw/pci.c diff --git a/drivers/dma/dw/Kconfig b/drivers/dma/dw/Kconfig index efd9e02a58eb..db2b41fab626 100644 --- a/drivers/dma/dw/Kconfig +++ b/drivers/dma/dw/Kconfig @@ -15,6 +15,15 @@ config DW_DMAC Support the Synopsys DesignWare AHB DMA controller. This can be integrated in chips such as the Atmel AT32ap7000. +config DW_DMAC_PCI + tristate "Synopsys DesignWare AHB DMA PCI driver" + depends on PCI + select DW_DMAC_CORE + help + Support the Synopsys DesignWare AHB DMA controller on the + platfroms that enumerate it as a PCI device. For example, + Intel Medfield has integrated this GPDMA controller. + config DW_DMAC_BIG_ENDIAN_IO bool "Use big endian I/O register access" default y if AVR32 diff --git a/drivers/dma/dw/Makefile b/drivers/dma/dw/Makefile index 47f36746c559..3eebd1ce2c6b 100644 --- a/drivers/dma/dw/Makefile +++ b/drivers/dma/dw/Makefile @@ -3,3 +3,6 @@ dw_dmac_core-objs := core.o obj-$(CONFIG_DW_DMAC) += dw_dmac.o dw_dmac-objs := platform.o + +obj-$(CONFIG_DW_DMAC_PCI) += dw_dmac_pci.o +dw_dmac_pci-objs := pci.o diff --git a/drivers/dma/dw/pci.c b/drivers/dma/dw/pci.c new file mode 100644 index 000000000000..e89fc24b8293 --- /dev/null +++ b/drivers/dma/dw/pci.c @@ -0,0 +1,101 @@ +/* + * PCI driver for the Synopsys DesignWare DMA Controller + * + * Copyright (C) 2013 Intel Corporation + * Author: Andy Shevchenko + * + * 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 +#include +#include + +#include "internal.h" + +static struct dw_dma_platform_data dw_pci_pdata = { + .is_private = 1, + .chan_allocation_order = CHAN_ALLOCATION_ASCENDING, + .chan_priority = CHAN_PRIORITY_ASCENDING, +}; + +static int dw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pid) +{ + struct dw_dma_chip *chip; + struct dw_dma_platform_data *pdata = (void *)pid->driver_data; + int ret; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + ret = pcim_iomap_regions(pdev, 1 << 0, pci_name(pdev)); + if (ret) { + dev_err(&pdev->dev, "I/O memory remapping failed\n"); + return ret; + } + + pci_set_master(pdev); + pci_try_set_mwi(pdev); + + ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->dev = &pdev->dev; + chip->regs = pcim_iomap_table(pdev)[0]; + chip->irq = pdev->irq; + + ret = dw_dma_probe(chip, pdata); + if (ret) + return ret; + + pci_set_drvdata(pdev, chip); + + return 0; +} + +static void dw_pci_remove(struct pci_dev *pdev) +{ + struct dw_dma_chip *chip = pci_get_drvdata(pdev); + int ret; + + ret = dw_dma_remove(chip); + if (ret) + dev_warn(&pdev->dev, "can't remove device properly: %d\n", ret); +} + +static DEFINE_PCI_DEVICE_TABLE(dw_pci_id_table) = { + /* Medfield */ + { PCI_VDEVICE(INTEL, 0x0827), (kernel_ulong_t)&dw_pci_pdata }, + { PCI_VDEVICE(INTEL, 0x0830), (kernel_ulong_t)&dw_pci_pdata }, + + /* BayTrail */ + { PCI_VDEVICE(INTEL, 0x0f06), (kernel_ulong_t)&dw_pci_pdata }, + { PCI_VDEVICE(INTEL, 0x0f40), (kernel_ulong_t)&dw_pci_pdata }, + { } +}; +MODULE_DEVICE_TABLE(pci, dw_pci_id_table); + +static struct pci_driver dw_pci_driver = { + .name = "dw_dmac_pci", + .id_table = dw_pci_id_table, + .probe = dw_pci_probe, + .remove = dw_pci_remove, +}; + +module_pci_driver(dw_pci_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Synopsys DesignWare DMA Controller PCI driver"); +MODULE_AUTHOR("Andy Shevchenko "); -- cgit v1.2.3 From e368b510c01aaf7b2957306836ffdeacc24712a3 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 12 Jun 2013 13:39:57 +0530 Subject: dmaengine: dw: select DW_DMAC_BIG_ENDIAN_IO automagically Signed-off-by: Vinod Koul --- drivers/dma/dw/Kconfig | 11 ++--------- drivers/dma/dw/regs.h | 6 ++++++ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/dma/dw/Kconfig b/drivers/dma/dw/Kconfig index db2b41fab626..dde13248b681 100644 --- a/drivers/dma/dw/Kconfig +++ b/drivers/dma/dw/Kconfig @@ -10,6 +10,7 @@ config DW_DMAC_CORE config DW_DMAC tristate "Synopsys DesignWare AHB DMA platform driver" select DW_DMAC_CORE + select DW_DMAC_BIG_ENDIAN_IO if AVR32 default y if CPU_AT32AP7000 help Support the Synopsys DesignWare AHB DMA controller. This @@ -25,12 +26,4 @@ config DW_DMAC_PCI Intel Medfield has integrated this GPDMA controller. config DW_DMAC_BIG_ENDIAN_IO - bool "Use big endian I/O register access" - default y if AVR32 - depends on DW_DMAC_CORE - help - Say yes here to use big endian I/O access when reading and writing - to the DMA controller registers. This is needed on some platforms, - like the Atmel AVR32 architecture. - - If unsure, use the default setting. + bool diff --git a/drivers/dma/dw/regs.h b/drivers/dma/dw/regs.h index 07c5a6ecb52b..deb4274f80f4 100644 --- a/drivers/dma/dw/regs.h +++ b/drivers/dma/dw/regs.h @@ -101,6 +101,12 @@ struct dw_dma_regs { u32 DW_PARAMS; }; +/* + * Big endian I/O access when reading and writing to the DMA controller + * registers. This is needed on some platforms, like the Atmel AVR32 + * architecture. + */ + #ifdef CONFIG_DW_DMAC_BIG_ENDIAN_IO #define dma_readl_native ioread32be #define dma_writel_native iowrite32be -- cgit v1.2.3 From d7cabeed830b7eb3958cbc084a85649126cd670f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 19 Jun 2013 20:38:28 +0100 Subject: dmaengine: PL08x: Avoid collisions with get_signal() macro As pointed out by Arnd Bergmann there is a get_signal macro definied in linux/signal.h which can conflict with the platform data callback function of the same name leading to confusing errors from the compiler (especially if signal.h manages to get pulled into the driver itself due to header dependencies). Avoid such errors by renaming get_signal and put_signal in the platform data to get_xfer_signal and put_xfer_signal. Signed-off-by: Mark Brown Acked-by: Arnd Bergmann Signed-off-by: Vinod Koul --- arch/arm/mach-lpc32xx/phy3250.c | 4 ++-- arch/arm/mach-spear/spear3xx.c | 4 ++-- arch/arm/mach-spear/spear6xx.c | 4 ++-- drivers/dma/amba-pl08x.c | 8 ++++---- include/linux/amba/pl08x.h | 8 ++++---- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/arch/arm/mach-lpc32xx/phy3250.c b/arch/arm/mach-lpc32xx/phy3250.c index c1cd5a943ab1..e54f87ec2e4a 100644 --- a/arch/arm/mach-lpc32xx/phy3250.c +++ b/arch/arm/mach-lpc32xx/phy3250.c @@ -182,8 +182,8 @@ static void pl08x_put_signal(const struct pl08x_channel_data *cd, int ch) static struct pl08x_platform_data pl08x_pd = { .slave_channels = &pl08x_slave_channels[0], .num_slave_channels = ARRAY_SIZE(pl08x_slave_channels), - .get_signal = pl08x_get_signal, - .put_signal = pl08x_put_signal, + .get_xfer_signal = pl08x_get_signal, + .put_xfer_signal = pl08x_put_signal, .lli_buses = PL08X_AHB1, .mem_buses = PL08X_AHB1, }; diff --git a/arch/arm/mach-spear/spear3xx.c b/arch/arm/mach-spear/spear3xx.c index 0227c97797cd..bf3b1fd8cb23 100644 --- a/arch/arm/mach-spear/spear3xx.c +++ b/arch/arm/mach-spear/spear3xx.c @@ -56,8 +56,8 @@ struct pl08x_platform_data pl080_plat_data = { }, .lli_buses = PL08X_AHB1, .mem_buses = PL08X_AHB1, - .get_signal = pl080_get_signal, - .put_signal = pl080_put_signal, + .get_xfer_signal = pl080_get_signal, + .put_xfer_signal = pl080_put_signal, }; /* diff --git a/arch/arm/mach-spear/spear6xx.c b/arch/arm/mach-spear/spear6xx.c index ec8eefbbdfad..cf477c78bb18 100644 --- a/arch/arm/mach-spear/spear6xx.c +++ b/arch/arm/mach-spear/spear6xx.c @@ -335,8 +335,8 @@ static struct pl08x_platform_data spear6xx_pl080_plat_data = { }, .lli_buses = PL08X_AHB1, .mem_buses = PL08X_AHB1, - .get_signal = pl080_get_signal, - .put_signal = pl080_put_signal, + .get_xfer_signal = pl080_get_signal, + .put_xfer_signal = pl080_put_signal, .slave_channels = spear600_dma_info, .num_slave_channels = ARRAY_SIZE(spear600_dma_info), }; diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 8bad254a498d..06fe45c74de5 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -299,8 +299,8 @@ static int pl08x_request_mux(struct pl08x_dma_chan *plchan) const struct pl08x_platform_data *pd = plchan->host->pd; int ret; - if (plchan->mux_use++ == 0 && pd->get_signal) { - ret = pd->get_signal(plchan->cd); + if (plchan->mux_use++ == 0 && pd->get_xfer_signal) { + ret = pd->get_xfer_signal(plchan->cd); if (ret < 0) { plchan->mux_use = 0; return ret; @@ -318,8 +318,8 @@ static void pl08x_release_mux(struct pl08x_dma_chan *plchan) if (plchan->signal >= 0) { WARN_ON(plchan->mux_use == 0); - if (--plchan->mux_use == 0 && pd->put_signal) { - pd->put_signal(plchan->cd, plchan->signal); + if (--plchan->mux_use == 0 && pd->put_xfer_signal) { + pd->put_xfer_signal(plchan->cd, plchan->signal); plchan->signal = -1; } } diff --git a/include/linux/amba/pl08x.h b/include/linux/amba/pl08x.h index 2a5f64a11b77..10fe2a211c2e 100644 --- a/include/linux/amba/pl08x.h +++ b/include/linux/amba/pl08x.h @@ -76,11 +76,11 @@ struct pl08x_channel_data { * platform, all inclusive, including multiplexed channels. The available * physical channels will be multiplexed around these signals as they are * requested, just enumerate all possible channels. - * @get_signal: request a physical signal to be used for a DMA transfer + * @get_xfer_signal: request a physical signal to be used for a DMA transfer * immediately: if there is some multiplexing or similar blocking the use * of the channel the transfer can be denied by returning less than zero, * else it returns the allocated signal number - * @put_signal: indicate to the platform that this physical signal is not + * @put_xfer_signal: indicate to the platform that this physical signal is not * running any DMA transfer and multiplexing can be recycled * @lli_buses: buses which LLIs can be fetched from: PL08X_AHB1 | PL08X_AHB2 * @mem_buses: buses which memory can be accessed from: PL08X_AHB1 | PL08X_AHB2 @@ -89,8 +89,8 @@ struct pl08x_platform_data { const struct pl08x_channel_data *slave_channels; unsigned int num_slave_channels; struct pl08x_channel_data memcpy_channel; - int (*get_signal)(const struct pl08x_channel_data *); - void (*put_signal)(const struct pl08x_channel_data *, int); + int (*get_xfer_signal)(const struct pl08x_channel_data *); + void (*put_xfer_signal)(const struct pl08x_channel_data *, int); u8 lli_buses; u8 mem_buses; }; -- cgit v1.2.3 From 8e3c518fba4f2ddd192171cbd7b23ec26900bf6b Mon Sep 17 00:00:00 2001 From: Qiao Zhou Date: Sat, 15 Jun 2013 12:51:48 +0800 Subject: dma: mmp_tdma: disable irq when disabling dma channel mask dma irq when disabling dma channel, so that interrupt status will not be set and interrupt won't come again. Signed-off-by: Qiao Zhou Acked-by: Zhangfei Gao Signed-off-by: Vinod Koul --- drivers/dma/mmp_tdma.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/dma/mmp_tdma.c b/drivers/dma/mmp_tdma.c index 43d5a6c33297..9b9366537d73 100644 --- a/drivers/dma/mmp_tdma.c +++ b/drivers/dma/mmp_tdma.c @@ -154,6 +154,10 @@ static void mmp_tdma_disable_chan(struct mmp_tdma_chan *tdmac) { writel(readl(tdmac->reg_base + TDCR) & ~TDCR_CHANEN, tdmac->reg_base + TDCR); + + /* disable irq */ + writel(0, tdmac->reg_base + TDIMR); + tdmac->status = DMA_SUCCESS; } -- cgit v1.2.3 From c8c81f32eef1d77134c81810b17f282bd619fb0b Mon Sep 17 00:00:00 2001 From: Maarten ter Huurne Date: Thu, 30 May 2013 18:25:01 +0200 Subject: MIPS: jz4740: Acquire and enable DMA controller clock Previously, it was assumed that the DMA controller clock is not gated when the kernel starts running. While that is the power-on state, it is safer to not rely on that. Signed-off-by: Maarten ter Huurne Signed-off-by: Lars-Peter Clausen Acked-by: Ralf Baechle Signed-off-by: Vinod Koul --- arch/mips/jz4740/dma.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/arch/mips/jz4740/dma.c b/arch/mips/jz4740/dma.c index 317ec6fffb12..0e34b97efa8c 100644 --- a/arch/mips/jz4740/dma.c +++ b/arch/mips/jz4740/dma.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -268,6 +269,7 @@ static irqreturn_t jz4740_dma_irq(int irq, void *dev_id) static int jz4740_dma_init(void) { + struct clk *clk; unsigned int ret; jz4740_dma_base = ioremap(JZ4740_DMAC_BASE_ADDR, 0x400); @@ -277,11 +279,29 @@ static int jz4740_dma_init(void) spin_lock_init(&jz4740_dma_lock); - ret = request_irq(JZ4740_IRQ_DMAC, jz4740_dma_irq, 0, "DMA", NULL); + clk = clk_get(NULL, "dma"); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + printk(KERN_ERR "JZ4740 DMA: Failed to request clock: %d\n", + ret); + goto err_iounmap; + } - if (ret) + ret = request_irq(JZ4740_IRQ_DMAC, jz4740_dma_irq, 0, "DMA", NULL); + if (ret) { printk(KERN_ERR "JZ4740 DMA: Failed to request irq: %d\n", ret); + goto err_clkput; + } + + clk_prepare_enable(clk); + + return 0; + +err_clkput: + clk_put(clk); +err_iounmap: + iounmap(jz4740_dma_base); return ret; } arch_initcall(jz4740_dma_init); -- cgit v1.2.3 From 7c169a42d961daccf5638ea41d1c76700ad6ca42 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 30 May 2013 18:25:02 +0200 Subject: dma: Add a jz4740 dmaengine driver This patch adds dmaengine support for the JZ4740 DMA controller. For now the driver will be a wrapper around the custom JZ4740 DMA API. Once all users of the custom JZ4740 DMA API have been converted to the dmaengine API the custom API will be removed and direct hardware access will be added to the dmaengine driver. Signed-off-by: Lars-Peter Clausen Signed-off-by: Vinod Koul --- drivers/dma/Kconfig | 6 + drivers/dma/Makefile | 1 + drivers/dma/dma-jz4740.c | 433 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 440 insertions(+) create mode 100644 drivers/dma/dma-jz4740.c diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 146a1d864a71..f2b3f0315ea2 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -294,6 +294,12 @@ config MMP_PDMA help Support the MMP PDMA engine for PXA and MMP platfrom. +config DMA_JZ4740 + tristate "JZ4740 DMA support" + depends on MACH_JZ4740 + select DMA_ENGINE + select DMA_VIRTUAL_CHANNELS + config DMA_ENGINE bool diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 6e2a521fbbe3..5e0f2ef85614 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -38,3 +38,4 @@ obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o obj-$(CONFIG_MMP_TDMA) += mmp_tdma.o obj-$(CONFIG_DMA_OMAP) += omap-dma.o obj-$(CONFIG_MMP_PDMA) += mmp_pdma.o +obj-$(CONFIG_DMA_JZ4740) += dma-jz4740.o diff --git a/drivers/dma/dma-jz4740.c b/drivers/dma/dma-jz4740.c new file mode 100644 index 000000000000..3d424341d8fe --- /dev/null +++ b/drivers/dma/dma-jz4740.c @@ -0,0 +1,433 @@ +/* + * Copyright (C) 2013, Lars-Peter Clausen + * JZ4740 DMAC support + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "virt-dma.h" + +#define JZ_DMA_NR_CHANS 6 + +struct jz4740_dma_sg { + dma_addr_t addr; + unsigned int len; +}; + +struct jz4740_dma_desc { + struct virt_dma_desc vdesc; + + enum dma_transfer_direction direction; + bool cyclic; + + unsigned int num_sgs; + struct jz4740_dma_sg sg[]; +}; + +struct jz4740_dmaengine_chan { + struct virt_dma_chan vchan; + struct jz4740_dma_chan *jz_chan; + + dma_addr_t fifo_addr; + + struct jz4740_dma_desc *desc; + unsigned int next_sg; +}; + +struct jz4740_dma_dev { + struct dma_device ddev; + + struct jz4740_dmaengine_chan chan[JZ_DMA_NR_CHANS]; +}; + +static struct jz4740_dmaengine_chan *to_jz4740_dma_chan(struct dma_chan *c) +{ + return container_of(c, struct jz4740_dmaengine_chan, vchan.chan); +} + +static struct jz4740_dma_desc *to_jz4740_dma_desc(struct virt_dma_desc *vdesc) +{ + return container_of(vdesc, struct jz4740_dma_desc, vdesc); +} + +static struct jz4740_dma_desc *jz4740_dma_alloc_desc(unsigned int num_sgs) +{ + return kzalloc(sizeof(struct jz4740_dma_desc) + + sizeof(struct jz4740_dma_sg) * num_sgs, GFP_ATOMIC); +} + +static enum jz4740_dma_width jz4740_dma_width(enum dma_slave_buswidth width) +{ + switch (width) { + case DMA_SLAVE_BUSWIDTH_1_BYTE: + return JZ4740_DMA_WIDTH_8BIT; + case DMA_SLAVE_BUSWIDTH_2_BYTES: + return JZ4740_DMA_WIDTH_16BIT; + case DMA_SLAVE_BUSWIDTH_4_BYTES: + return JZ4740_DMA_WIDTH_32BIT; + default: + return JZ4740_DMA_WIDTH_32BIT; + } +} + +static enum jz4740_dma_transfer_size jz4740_dma_maxburst(u32 maxburst) +{ + if (maxburst <= 1) + return JZ4740_DMA_TRANSFER_SIZE_1BYTE; + else if (maxburst <= 3) + return JZ4740_DMA_TRANSFER_SIZE_2BYTE; + else if (maxburst <= 15) + return JZ4740_DMA_TRANSFER_SIZE_4BYTE; + else if (maxburst <= 31) + return JZ4740_DMA_TRANSFER_SIZE_16BYTE; + + return JZ4740_DMA_TRANSFER_SIZE_32BYTE; +} + +static int jz4740_dma_slave_config(struct dma_chan *c, + const struct dma_slave_config *config) +{ + struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); + struct jz4740_dma_config jzcfg; + + switch (config->direction) { + case DMA_MEM_TO_DEV: + jzcfg.flags = JZ4740_DMA_SRC_AUTOINC; + jzcfg.transfer_size = jz4740_dma_maxburst(config->dst_maxburst); + chan->fifo_addr = config->dst_addr; + break; + case DMA_DEV_TO_MEM: + jzcfg.flags = JZ4740_DMA_DST_AUTOINC; + jzcfg.transfer_size = jz4740_dma_maxburst(config->src_maxburst); + chan->fifo_addr = config->src_addr; + break; + default: + return -EINVAL; + } + + + jzcfg.src_width = jz4740_dma_width(config->src_addr_width); + jzcfg.dst_width = jz4740_dma_width(config->dst_addr_width); + jzcfg.mode = JZ4740_DMA_MODE_SINGLE; + jzcfg.request_type = config->slave_id; + + jz4740_dma_configure(chan->jz_chan, &jzcfg); + + return 0; +} + +static int jz4740_dma_terminate_all(struct dma_chan *c) +{ + struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); + unsigned long flags; + LIST_HEAD(head); + + spin_lock_irqsave(&chan->vchan.lock, flags); + jz4740_dma_disable(chan->jz_chan); + chan->desc = NULL; + vchan_get_all_descriptors(&chan->vchan, &head); + spin_unlock_irqrestore(&chan->vchan.lock, flags); + + vchan_dma_desc_free_list(&chan->vchan, &head); + + return 0; +} + +static int jz4740_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, + unsigned long arg) +{ + struct dma_slave_config *config = (struct dma_slave_config *)arg; + + switch (cmd) { + case DMA_SLAVE_CONFIG: + return jz4740_dma_slave_config(chan, config); + case DMA_TERMINATE_ALL: + return jz4740_dma_terminate_all(chan); + default: + return -ENOSYS; + } +} + +static int jz4740_dma_start_transfer(struct jz4740_dmaengine_chan *chan) +{ + dma_addr_t src_addr, dst_addr; + struct virt_dma_desc *vdesc; + struct jz4740_dma_sg *sg; + + jz4740_dma_disable(chan->jz_chan); + + if (!chan->desc) { + vdesc = vchan_next_desc(&chan->vchan); + if (!vdesc) + return 0; + chan->desc = to_jz4740_dma_desc(vdesc); + chan->next_sg = 0; + } + + if (chan->next_sg == chan->desc->num_sgs) + chan->next_sg = 0; + + sg = &chan->desc->sg[chan->next_sg]; + + if (chan->desc->direction == DMA_MEM_TO_DEV) { + src_addr = sg->addr; + dst_addr = chan->fifo_addr; + } else { + src_addr = chan->fifo_addr; + dst_addr = sg->addr; + } + jz4740_dma_set_src_addr(chan->jz_chan, src_addr); + jz4740_dma_set_dst_addr(chan->jz_chan, dst_addr); + jz4740_dma_set_transfer_count(chan->jz_chan, sg->len); + + chan->next_sg++; + + jz4740_dma_enable(chan->jz_chan); + + return 0; +} + +static void jz4740_dma_complete_cb(struct jz4740_dma_chan *jz_chan, int error, + void *devid) +{ + struct jz4740_dmaengine_chan *chan = devid; + + spin_lock(&chan->vchan.lock); + if (chan->desc) { + if (chan->desc && chan->desc->cyclic) { + vchan_cyclic_callback(&chan->desc->vdesc); + } else { + if (chan->next_sg == chan->desc->num_sgs) { + chan->desc = NULL; + vchan_cookie_complete(&chan->desc->vdesc); + } + } + } + jz4740_dma_start_transfer(chan); + spin_unlock(&chan->vchan.lock); +} + +static void jz4740_dma_issue_pending(struct dma_chan *c) +{ + struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); + unsigned long flags; + + spin_lock_irqsave(&chan->vchan.lock, flags); + if (vchan_issue_pending(&chan->vchan) && !chan->desc) + jz4740_dma_start_transfer(chan); + spin_unlock_irqrestore(&chan->vchan.lock, flags); +} + +static struct dma_async_tx_descriptor *jz4740_dma_prep_slave_sg( + struct dma_chan *c, struct scatterlist *sgl, + unsigned int sg_len, enum dma_transfer_direction direction, + unsigned long flags, void *context) +{ + struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); + struct jz4740_dma_desc *desc; + struct scatterlist *sg; + unsigned int i; + + desc = jz4740_dma_alloc_desc(sg_len); + if (!desc) + return NULL; + + for_each_sg(sgl, sg, sg_len, i) { + desc->sg[i].addr = sg_dma_address(sg); + desc->sg[i].len = sg_dma_len(sg); + } + + desc->num_sgs = sg_len; + desc->direction = direction; + desc->cyclic = false; + + return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags); +} + +static struct dma_async_tx_descriptor *jz4740_dma_prep_dma_cyclic( + struct dma_chan *c, dma_addr_t buf_addr, size_t buf_len, + size_t period_len, enum dma_transfer_direction direction, + unsigned long flags, void *context) +{ + struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); + struct jz4740_dma_desc *desc; + unsigned int num_periods, i; + + if (buf_len % period_len) + return NULL; + + num_periods = buf_len / period_len; + + desc = jz4740_dma_alloc_desc(num_periods); + if (!desc) + return NULL; + + for (i = 0; i < num_periods; i++) { + desc->sg[i].addr = buf_addr; + desc->sg[i].len = period_len; + buf_addr += period_len; + } + + desc->num_sgs = num_periods; + desc->direction = direction; + desc->cyclic = true; + + return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags); +} + +static size_t jz4740_dma_desc_residue(struct jz4740_dmaengine_chan *chan, + struct jz4740_dma_desc *desc, unsigned int next_sg) +{ + size_t residue = 0; + unsigned int i; + + residue = 0; + + for (i = next_sg; i < desc->num_sgs; i++) + residue += desc->sg[i].len; + + if (next_sg != 0) + residue += jz4740_dma_get_residue(chan->jz_chan); + + return residue; +} + +static enum dma_status jz4740_dma_tx_status(struct dma_chan *c, + dma_cookie_t cookie, struct dma_tx_state *state) +{ + struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); + struct virt_dma_desc *vdesc; + enum dma_status status; + unsigned long flags; + + status = dma_cookie_status(c, cookie, state); + if (status == DMA_SUCCESS || !state) + return status; + + spin_lock_irqsave(&chan->vchan.lock, flags); + vdesc = vchan_find_desc(&chan->vchan, cookie); + if (cookie == chan->desc->vdesc.tx.cookie) { + state->residue = jz4740_dma_desc_residue(chan, chan->desc, + chan->next_sg); + } else if (vdesc) { + state->residue = jz4740_dma_desc_residue(chan, + to_jz4740_dma_desc(vdesc), 0); + } else { + state->residue = 0; + } + spin_unlock_irqrestore(&chan->vchan.lock, flags); + + return status; +} + +static int jz4740_dma_alloc_chan_resources(struct dma_chan *c) +{ + struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); + + chan->jz_chan = jz4740_dma_request(chan, NULL); + if (!chan->jz_chan) + return -EBUSY; + + jz4740_dma_set_complete_cb(chan->jz_chan, jz4740_dma_complete_cb); + + return 0; +} + +static void jz4740_dma_free_chan_resources(struct dma_chan *c) +{ + struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); + + vchan_free_chan_resources(&chan->vchan); + jz4740_dma_free(chan->jz_chan); + chan->jz_chan = NULL; +} + +static void jz4740_dma_desc_free(struct virt_dma_desc *vdesc) +{ + kfree(container_of(vdesc, struct jz4740_dma_desc, vdesc)); +} + +static int jz4740_dma_probe(struct platform_device *pdev) +{ + struct jz4740_dmaengine_chan *chan; + struct jz4740_dma_dev *dmadev; + struct dma_device *dd; + unsigned int i; + int ret; + + dmadev = devm_kzalloc(&pdev->dev, sizeof(*dmadev), GFP_KERNEL); + if (!dmadev) + return -EINVAL; + + dd = &dmadev->ddev; + + dma_cap_set(DMA_SLAVE, dd->cap_mask); + dma_cap_set(DMA_CYCLIC, dd->cap_mask); + dd->device_alloc_chan_resources = jz4740_dma_alloc_chan_resources; + dd->device_free_chan_resources = jz4740_dma_free_chan_resources; + dd->device_tx_status = jz4740_dma_tx_status; + dd->device_issue_pending = jz4740_dma_issue_pending; + dd->device_prep_slave_sg = jz4740_dma_prep_slave_sg; + dd->device_prep_dma_cyclic = jz4740_dma_prep_dma_cyclic; + dd->device_control = jz4740_dma_control; + dd->dev = &pdev->dev; + dd->chancnt = JZ_DMA_NR_CHANS; + INIT_LIST_HEAD(&dd->channels); + + for (i = 0; i < dd->chancnt; i++) { + chan = &dmadev->chan[i]; + chan->vchan.desc_free = jz4740_dma_desc_free; + vchan_init(&chan->vchan, dd); + } + + ret = dma_async_device_register(dd); + if (ret) + return ret; + + platform_set_drvdata(pdev, dmadev); + + return 0; +} + +static int jz4740_dma_remove(struct platform_device *pdev) +{ + struct jz4740_dma_dev *dmadev = platform_get_drvdata(pdev); + + dma_async_device_unregister(&dmadev->ddev); + + return 0; +} + +static struct platform_driver jz4740_dma_driver = { + .probe = jz4740_dma_probe, + .remove = jz4740_dma_remove, + .driver = { + .name = "jz4740-dma", + .owner = THIS_MODULE, + }, +}; +module_platform_driver(jz4740_dma_driver); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("JZ4740 DMA driver"); +MODULE_LICENSE("GPLv2"); -- cgit v1.2.3 From cdcb90ad48816f51fdb4e93b2a33fe6038ce0949 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 30 May 2013 18:25:03 +0200 Subject: MIPS: jz4740: Register jz4740 DMA device Register a device for the newly added jz4740 dmaengine driver. Signed-off-by: Lars-Peter Clausen Acked-by: Ralf Baechle [manually edited to align struct assignment] Signed-off-by: Vinod Koul --- arch/mips/include/asm/mach-jz4740/platform.h | 1 + arch/mips/jz4740/board-qi_lb60.c | 1 + arch/mips/jz4740/platform.c | 21 +++++++++++++++++++++ 3 files changed, 23 insertions(+) diff --git a/arch/mips/include/asm/mach-jz4740/platform.h b/arch/mips/include/asm/mach-jz4740/platform.h index 72cfebdb5a47..05988c2d6565 100644 --- a/arch/mips/include/asm/mach-jz4740/platform.h +++ b/arch/mips/include/asm/mach-jz4740/platform.h @@ -32,6 +32,7 @@ extern struct platform_device jz4740_codec_device; extern struct platform_device jz4740_adc_device; extern struct platform_device jz4740_wdt_device; extern struct platform_device jz4740_pwm_device; +extern struct platform_device jz4740_dma_device; void jz4740_serial_device_register(void); diff --git a/arch/mips/jz4740/board-qi_lb60.c b/arch/mips/jz4740/board-qi_lb60.c index be2b3deeef1d..8a5ec0eedeb0 100644 --- a/arch/mips/jz4740/board-qi_lb60.c +++ b/arch/mips/jz4740/board-qi_lb60.c @@ -438,6 +438,7 @@ static struct platform_device *jz_platform_devices[] __initdata = { &jz4740_rtc_device, &jz4740_adc_device, &jz4740_pwm_device, + &jz4740_dma_device, &qi_lb60_gpio_keys, &qi_lb60_pwm_beeper, &qi_lb60_charger_device, diff --git a/arch/mips/jz4740/platform.c b/arch/mips/jz4740/platform.c index e9348fd26a35..df65677f3d0b 100644 --- a/arch/mips/jz4740/platform.c +++ b/arch/mips/jz4740/platform.c @@ -329,3 +329,24 @@ struct platform_device jz4740_pwm_device = { .name = "jz4740-pwm", .id = -1, }; + +/* DMA */ +static struct resource jz4740_dma_resources[] = { + { + .start = JZ4740_DMAC_BASE_ADDR, + .end = JZ4740_DMAC_BASE_ADDR + 0x400 - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = JZ4740_IRQ_DMAC, + .end = JZ4740_IRQ_DMAC, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device jz4740_dma_device = { + .name = "jz4740-dma", + .id = -1, + .num_resources = ARRAY_SIZE(jz4740_dma_resources), + .resource = jz4740_dma_resources, +}; -- cgit v1.2.3 From 25ce6c35fea0588c7a1b68e55d700c22d9e97ce0 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 30 May 2013 18:25:05 +0200 Subject: MIPS: jz4740: Remove custom DMA API Now that all users of the custom jz4740 DMA API have been converted to use the dmaengine API instead we can remove the custom API and move all the code talking to the hardware to the dmaengine driver. Signed-off-by: Lars-Peter Clausen Acked-by: Ralf Baechle Signed-off-by: Vinod Koul --- arch/mips/include/asm/mach-jz4740/dma.h | 56 ------ arch/mips/jz4740/Makefile | 2 +- arch/mips/jz4740/dma.c | 307 -------------------------------- drivers/dma/dma-jz4740.c | 258 +++++++++++++++++++++++---- 4 files changed, 222 insertions(+), 401 deletions(-) delete mode 100644 arch/mips/jz4740/dma.c diff --git a/arch/mips/include/asm/mach-jz4740/dma.h b/arch/mips/include/asm/mach-jz4740/dma.h index 98b4e7c0dbae..509cd5828044 100644 --- a/arch/mips/include/asm/mach-jz4740/dma.h +++ b/arch/mips/include/asm/mach-jz4740/dma.h @@ -16,8 +16,6 @@ #ifndef __ASM_MACH_JZ4740_DMA_H__ #define __ASM_MACH_JZ4740_DMA_H__ -struct jz4740_dma_chan; - enum jz4740_dma_request_type { JZ4740_DMA_TYPE_AUTO_REQUEST = 8, JZ4740_DMA_TYPE_UART_TRANSMIT = 20, @@ -33,58 +31,4 @@ enum jz4740_dma_request_type { JZ4740_DMA_TYPE_SLCD = 30, }; -enum jz4740_dma_width { - JZ4740_DMA_WIDTH_32BIT = 0, - JZ4740_DMA_WIDTH_8BIT = 1, - JZ4740_DMA_WIDTH_16BIT = 2, -}; - -enum jz4740_dma_transfer_size { - JZ4740_DMA_TRANSFER_SIZE_4BYTE = 0, - JZ4740_DMA_TRANSFER_SIZE_1BYTE = 1, - JZ4740_DMA_TRANSFER_SIZE_2BYTE = 2, - JZ4740_DMA_TRANSFER_SIZE_16BYTE = 3, - JZ4740_DMA_TRANSFER_SIZE_32BYTE = 4, -}; - -enum jz4740_dma_flags { - JZ4740_DMA_SRC_AUTOINC = 0x2, - JZ4740_DMA_DST_AUTOINC = 0x1, -}; - -enum jz4740_dma_mode { - JZ4740_DMA_MODE_SINGLE = 0, - JZ4740_DMA_MODE_BLOCK = 1, -}; - -struct jz4740_dma_config { - enum jz4740_dma_width src_width; - enum jz4740_dma_width dst_width; - enum jz4740_dma_transfer_size transfer_size; - enum jz4740_dma_request_type request_type; - enum jz4740_dma_flags flags; - enum jz4740_dma_mode mode; -}; - -typedef void (*jz4740_dma_complete_callback_t)(struct jz4740_dma_chan *, int, void *); - -struct jz4740_dma_chan *jz4740_dma_request(void *dev, const char *name); -void jz4740_dma_free(struct jz4740_dma_chan *dma); - -void jz4740_dma_configure(struct jz4740_dma_chan *dma, - const struct jz4740_dma_config *config); - - -void jz4740_dma_enable(struct jz4740_dma_chan *dma); -void jz4740_dma_disable(struct jz4740_dma_chan *dma); - -void jz4740_dma_set_src_addr(struct jz4740_dma_chan *dma, dma_addr_t src); -void jz4740_dma_set_dst_addr(struct jz4740_dma_chan *dma, dma_addr_t dst); -void jz4740_dma_set_transfer_count(struct jz4740_dma_chan *dma, uint32_t count); - -uint32_t jz4740_dma_get_residue(const struct jz4740_dma_chan *dma); - -void jz4740_dma_set_complete_cb(struct jz4740_dma_chan *dma, - jz4740_dma_complete_callback_t cb); - #endif /* __ASM_JZ4740_DMA_H__ */ diff --git a/arch/mips/jz4740/Makefile b/arch/mips/jz4740/Makefile index 63bad0e491d0..28e5535dfa9e 100644 --- a/arch/mips/jz4740/Makefile +++ b/arch/mips/jz4740/Makefile @@ -4,7 +4,7 @@ # Object file lists. -obj-y += prom.o irq.o time.o reset.o setup.o dma.o \ +obj-y += prom.o irq.o time.o reset.o setup.o \ gpio.o clock.o platform.o timer.o serial.o obj-$(CONFIG_DEBUG_FS) += clock-debugfs.o diff --git a/arch/mips/jz4740/dma.c b/arch/mips/jz4740/dma.c deleted file mode 100644 index 0e34b97efa8c..000000000000 --- a/arch/mips/jz4740/dma.c +++ /dev/null @@ -1,307 +0,0 @@ -/* - * Copyright (C) 2010, Lars-Peter Clausen - * JZ4740 SoC DMA support - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ - -#include -#include -#include -#include -#include - -#include -#include -#include - -#define JZ_REG_DMA_SRC_ADDR(x) (0x00 + (x) * 0x20) -#define JZ_REG_DMA_DST_ADDR(x) (0x04 + (x) * 0x20) -#define JZ_REG_DMA_TRANSFER_COUNT(x) (0x08 + (x) * 0x20) -#define JZ_REG_DMA_REQ_TYPE(x) (0x0C + (x) * 0x20) -#define JZ_REG_DMA_STATUS_CTRL(x) (0x10 + (x) * 0x20) -#define JZ_REG_DMA_CMD(x) (0x14 + (x) * 0x20) -#define JZ_REG_DMA_DESC_ADDR(x) (0x18 + (x) * 0x20) - -#define JZ_REG_DMA_CTRL 0x300 -#define JZ_REG_DMA_IRQ 0x304 -#define JZ_REG_DMA_DOORBELL 0x308 -#define JZ_REG_DMA_DOORBELL_SET 0x30C - -#define JZ_DMA_STATUS_CTRL_NO_DESC BIT(31) -#define JZ_DMA_STATUS_CTRL_DESC_INV BIT(6) -#define JZ_DMA_STATUS_CTRL_ADDR_ERR BIT(4) -#define JZ_DMA_STATUS_CTRL_TRANSFER_DONE BIT(3) -#define JZ_DMA_STATUS_CTRL_HALT BIT(2) -#define JZ_DMA_STATUS_CTRL_COUNT_TERMINATE BIT(1) -#define JZ_DMA_STATUS_CTRL_ENABLE BIT(0) - -#define JZ_DMA_CMD_SRC_INC BIT(23) -#define JZ_DMA_CMD_DST_INC BIT(22) -#define JZ_DMA_CMD_RDIL_MASK (0xf << 16) -#define JZ_DMA_CMD_SRC_WIDTH_MASK (0x3 << 14) -#define JZ_DMA_CMD_DST_WIDTH_MASK (0x3 << 12) -#define JZ_DMA_CMD_INTERVAL_LENGTH_MASK (0x7 << 8) -#define JZ_DMA_CMD_BLOCK_MODE BIT(7) -#define JZ_DMA_CMD_DESC_VALID BIT(4) -#define JZ_DMA_CMD_DESC_VALID_MODE BIT(3) -#define JZ_DMA_CMD_VALID_IRQ_ENABLE BIT(2) -#define JZ_DMA_CMD_TRANSFER_IRQ_ENABLE BIT(1) -#define JZ_DMA_CMD_LINK_ENABLE BIT(0) - -#define JZ_DMA_CMD_FLAGS_OFFSET 22 -#define JZ_DMA_CMD_RDIL_OFFSET 16 -#define JZ_DMA_CMD_SRC_WIDTH_OFFSET 14 -#define JZ_DMA_CMD_DST_WIDTH_OFFSET 12 -#define JZ_DMA_CMD_TRANSFER_SIZE_OFFSET 8 -#define JZ_DMA_CMD_MODE_OFFSET 7 - -#define JZ_DMA_CTRL_PRIORITY_MASK (0x3 << 8) -#define JZ_DMA_CTRL_HALT BIT(3) -#define JZ_DMA_CTRL_ADDRESS_ERROR BIT(2) -#define JZ_DMA_CTRL_ENABLE BIT(0) - - -static void __iomem *jz4740_dma_base; -static spinlock_t jz4740_dma_lock; - -static inline uint32_t jz4740_dma_read(size_t reg) -{ - return readl(jz4740_dma_base + reg); -} - -static inline void jz4740_dma_write(size_t reg, uint32_t val) -{ - writel(val, jz4740_dma_base + reg); -} - -static inline void jz4740_dma_write_mask(size_t reg, uint32_t val, uint32_t mask) -{ - uint32_t val2; - val2 = jz4740_dma_read(reg); - val2 &= ~mask; - val2 |= val; - jz4740_dma_write(reg, val2); -} - -struct jz4740_dma_chan { - unsigned int id; - void *dev; - const char *name; - - enum jz4740_dma_flags flags; - uint32_t transfer_shift; - - jz4740_dma_complete_callback_t complete_cb; - - unsigned used:1; -}; - -#define JZ4740_DMA_CHANNEL(_id) { .id = _id } - -struct jz4740_dma_chan jz4740_dma_channels[] = { - JZ4740_DMA_CHANNEL(0), - JZ4740_DMA_CHANNEL(1), - JZ4740_DMA_CHANNEL(2), - JZ4740_DMA_CHANNEL(3), - JZ4740_DMA_CHANNEL(4), - JZ4740_DMA_CHANNEL(5), -}; - -struct jz4740_dma_chan *jz4740_dma_request(void *dev, const char *name) -{ - unsigned int i; - struct jz4740_dma_chan *dma = NULL; - - spin_lock(&jz4740_dma_lock); - - for (i = 0; i < ARRAY_SIZE(jz4740_dma_channels); ++i) { - if (!jz4740_dma_channels[i].used) { - dma = &jz4740_dma_channels[i]; - dma->used = 1; - break; - } - } - - spin_unlock(&jz4740_dma_lock); - - if (!dma) - return NULL; - - dma->dev = dev; - dma->name = name; - - return dma; -} -EXPORT_SYMBOL_GPL(jz4740_dma_request); - -void jz4740_dma_configure(struct jz4740_dma_chan *dma, - const struct jz4740_dma_config *config) -{ - uint32_t cmd; - - switch (config->transfer_size) { - case JZ4740_DMA_TRANSFER_SIZE_2BYTE: - dma->transfer_shift = 1; - break; - case JZ4740_DMA_TRANSFER_SIZE_4BYTE: - dma->transfer_shift = 2; - break; - case JZ4740_DMA_TRANSFER_SIZE_16BYTE: - dma->transfer_shift = 4; - break; - case JZ4740_DMA_TRANSFER_SIZE_32BYTE: - dma->transfer_shift = 5; - break; - default: - dma->transfer_shift = 0; - break; - } - - cmd = config->flags << JZ_DMA_CMD_FLAGS_OFFSET; - cmd |= config->src_width << JZ_DMA_CMD_SRC_WIDTH_OFFSET; - cmd |= config->dst_width << JZ_DMA_CMD_DST_WIDTH_OFFSET; - cmd |= config->transfer_size << JZ_DMA_CMD_TRANSFER_SIZE_OFFSET; - cmd |= config->mode << JZ_DMA_CMD_MODE_OFFSET; - cmd |= JZ_DMA_CMD_TRANSFER_IRQ_ENABLE; - - jz4740_dma_write(JZ_REG_DMA_CMD(dma->id), cmd); - jz4740_dma_write(JZ_REG_DMA_STATUS_CTRL(dma->id), 0); - jz4740_dma_write(JZ_REG_DMA_REQ_TYPE(dma->id), config->request_type); -} -EXPORT_SYMBOL_GPL(jz4740_dma_configure); - -void jz4740_dma_set_src_addr(struct jz4740_dma_chan *dma, dma_addr_t src) -{ - jz4740_dma_write(JZ_REG_DMA_SRC_ADDR(dma->id), src); -} -EXPORT_SYMBOL_GPL(jz4740_dma_set_src_addr); - -void jz4740_dma_set_dst_addr(struct jz4740_dma_chan *dma, dma_addr_t dst) -{ - jz4740_dma_write(JZ_REG_DMA_DST_ADDR(dma->id), dst); -} -EXPORT_SYMBOL_GPL(jz4740_dma_set_dst_addr); - -void jz4740_dma_set_transfer_count(struct jz4740_dma_chan *dma, uint32_t count) -{ - count >>= dma->transfer_shift; - jz4740_dma_write(JZ_REG_DMA_TRANSFER_COUNT(dma->id), count); -} -EXPORT_SYMBOL_GPL(jz4740_dma_set_transfer_count); - -void jz4740_dma_set_complete_cb(struct jz4740_dma_chan *dma, - jz4740_dma_complete_callback_t cb) -{ - dma->complete_cb = cb; -} -EXPORT_SYMBOL_GPL(jz4740_dma_set_complete_cb); - -void jz4740_dma_free(struct jz4740_dma_chan *dma) -{ - dma->dev = NULL; - dma->complete_cb = NULL; - dma->used = 0; -} -EXPORT_SYMBOL_GPL(jz4740_dma_free); - -void jz4740_dma_enable(struct jz4740_dma_chan *dma) -{ - jz4740_dma_write_mask(JZ_REG_DMA_STATUS_CTRL(dma->id), - JZ_DMA_STATUS_CTRL_NO_DESC | JZ_DMA_STATUS_CTRL_ENABLE, - JZ_DMA_STATUS_CTRL_HALT | JZ_DMA_STATUS_CTRL_NO_DESC | - JZ_DMA_STATUS_CTRL_ENABLE); - - jz4740_dma_write_mask(JZ_REG_DMA_CTRL, - JZ_DMA_CTRL_ENABLE, - JZ_DMA_CTRL_HALT | JZ_DMA_CTRL_ENABLE); -} -EXPORT_SYMBOL_GPL(jz4740_dma_enable); - -void jz4740_dma_disable(struct jz4740_dma_chan *dma) -{ - jz4740_dma_write_mask(JZ_REG_DMA_STATUS_CTRL(dma->id), 0, - JZ_DMA_STATUS_CTRL_ENABLE); -} -EXPORT_SYMBOL_GPL(jz4740_dma_disable); - -uint32_t jz4740_dma_get_residue(const struct jz4740_dma_chan *dma) -{ - uint32_t residue; - residue = jz4740_dma_read(JZ_REG_DMA_TRANSFER_COUNT(dma->id)); - return residue << dma->transfer_shift; -} -EXPORT_SYMBOL_GPL(jz4740_dma_get_residue); - -static void jz4740_dma_chan_irq(struct jz4740_dma_chan *dma) -{ - (void) jz4740_dma_read(JZ_REG_DMA_STATUS_CTRL(dma->id)); - - jz4740_dma_write_mask(JZ_REG_DMA_STATUS_CTRL(dma->id), 0, - JZ_DMA_STATUS_CTRL_ENABLE | JZ_DMA_STATUS_CTRL_TRANSFER_DONE); - - if (dma->complete_cb) - dma->complete_cb(dma, 0, dma->dev); -} - -static irqreturn_t jz4740_dma_irq(int irq, void *dev_id) -{ - uint32_t irq_status; - unsigned int i; - - irq_status = readl(jz4740_dma_base + JZ_REG_DMA_IRQ); - - for (i = 0; i < 6; ++i) { - if (irq_status & (1 << i)) - jz4740_dma_chan_irq(&jz4740_dma_channels[i]); - } - - return IRQ_HANDLED; -} - -static int jz4740_dma_init(void) -{ - struct clk *clk; - unsigned int ret; - - jz4740_dma_base = ioremap(JZ4740_DMAC_BASE_ADDR, 0x400); - - if (!jz4740_dma_base) - return -EBUSY; - - spin_lock_init(&jz4740_dma_lock); - - clk = clk_get(NULL, "dma"); - if (IS_ERR(clk)) { - ret = PTR_ERR(clk); - printk(KERN_ERR "JZ4740 DMA: Failed to request clock: %d\n", - ret); - goto err_iounmap; - } - - ret = request_irq(JZ4740_IRQ_DMAC, jz4740_dma_irq, 0, "DMA", NULL); - if (ret) { - printk(KERN_ERR "JZ4740 DMA: Failed to request irq: %d\n", ret); - goto err_clkput; - } - - clk_prepare_enable(clk); - - return 0; - -err_clkput: - clk_put(clk); - -err_iounmap: - iounmap(jz4740_dma_base); - return ret; -} -arch_initcall(jz4740_dma_init); diff --git a/drivers/dma/dma-jz4740.c b/drivers/dma/dma-jz4740.c index 3d424341d8fe..b0c0c8268d42 100644 --- a/drivers/dma/dma-jz4740.c +++ b/drivers/dma/dma-jz4740.c @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include @@ -29,6 +31,76 @@ #define JZ_DMA_NR_CHANS 6 +#define JZ_REG_DMA_SRC_ADDR(x) (0x00 + (x) * 0x20) +#define JZ_REG_DMA_DST_ADDR(x) (0x04 + (x) * 0x20) +#define JZ_REG_DMA_TRANSFER_COUNT(x) (0x08 + (x) * 0x20) +#define JZ_REG_DMA_REQ_TYPE(x) (0x0C + (x) * 0x20) +#define JZ_REG_DMA_STATUS_CTRL(x) (0x10 + (x) * 0x20) +#define JZ_REG_DMA_CMD(x) (0x14 + (x) * 0x20) +#define JZ_REG_DMA_DESC_ADDR(x) (0x18 + (x) * 0x20) + +#define JZ_REG_DMA_CTRL 0x300 +#define JZ_REG_DMA_IRQ 0x304 +#define JZ_REG_DMA_DOORBELL 0x308 +#define JZ_REG_DMA_DOORBELL_SET 0x30C + +#define JZ_DMA_STATUS_CTRL_NO_DESC BIT(31) +#define JZ_DMA_STATUS_CTRL_DESC_INV BIT(6) +#define JZ_DMA_STATUS_CTRL_ADDR_ERR BIT(4) +#define JZ_DMA_STATUS_CTRL_TRANSFER_DONE BIT(3) +#define JZ_DMA_STATUS_CTRL_HALT BIT(2) +#define JZ_DMA_STATUS_CTRL_COUNT_TERMINATE BIT(1) +#define JZ_DMA_STATUS_CTRL_ENABLE BIT(0) + +#define JZ_DMA_CMD_SRC_INC BIT(23) +#define JZ_DMA_CMD_DST_INC BIT(22) +#define JZ_DMA_CMD_RDIL_MASK (0xf << 16) +#define JZ_DMA_CMD_SRC_WIDTH_MASK (0x3 << 14) +#define JZ_DMA_CMD_DST_WIDTH_MASK (0x3 << 12) +#define JZ_DMA_CMD_INTERVAL_LENGTH_MASK (0x7 << 8) +#define JZ_DMA_CMD_BLOCK_MODE BIT(7) +#define JZ_DMA_CMD_DESC_VALID BIT(4) +#define JZ_DMA_CMD_DESC_VALID_MODE BIT(3) +#define JZ_DMA_CMD_VALID_IRQ_ENABLE BIT(2) +#define JZ_DMA_CMD_TRANSFER_IRQ_ENABLE BIT(1) +#define JZ_DMA_CMD_LINK_ENABLE BIT(0) + +#define JZ_DMA_CMD_FLAGS_OFFSET 22 +#define JZ_DMA_CMD_RDIL_OFFSET 16 +#define JZ_DMA_CMD_SRC_WIDTH_OFFSET 14 +#define JZ_DMA_CMD_DST_WIDTH_OFFSET 12 +#define JZ_DMA_CMD_TRANSFER_SIZE_OFFSET 8 +#define JZ_DMA_CMD_MODE_OFFSET 7 + +#define JZ_DMA_CTRL_PRIORITY_MASK (0x3 << 8) +#define JZ_DMA_CTRL_HALT BIT(3) +#define JZ_DMA_CTRL_ADDRESS_ERROR BIT(2) +#define JZ_DMA_CTRL_ENABLE BIT(0) + +enum jz4740_dma_width { + JZ4740_DMA_WIDTH_32BIT = 0, + JZ4740_DMA_WIDTH_8BIT = 1, + JZ4740_DMA_WIDTH_16BIT = 2, +}; + +enum jz4740_dma_transfer_size { + JZ4740_DMA_TRANSFER_SIZE_4BYTE = 0, + JZ4740_DMA_TRANSFER_SIZE_1BYTE = 1, + JZ4740_DMA_TRANSFER_SIZE_2BYTE = 2, + JZ4740_DMA_TRANSFER_SIZE_16BYTE = 3, + JZ4740_DMA_TRANSFER_SIZE_32BYTE = 4, +}; + +enum jz4740_dma_flags { + JZ4740_DMA_SRC_AUTOINC = 0x2, + JZ4740_DMA_DST_AUTOINC = 0x1, +}; + +enum jz4740_dma_mode { + JZ4740_DMA_MODE_SINGLE = 0, + JZ4740_DMA_MODE_BLOCK = 1, +}; + struct jz4740_dma_sg { dma_addr_t addr; unsigned int len; @@ -46,9 +118,10 @@ struct jz4740_dma_desc { struct jz4740_dmaengine_chan { struct virt_dma_chan vchan; - struct jz4740_dma_chan *jz_chan; + unsigned int id; dma_addr_t fifo_addr; + unsigned int transfer_shift; struct jz4740_dma_desc *desc; unsigned int next_sg; @@ -56,10 +129,19 @@ struct jz4740_dmaengine_chan { struct jz4740_dma_dev { struct dma_device ddev; + void __iomem *base; + struct clk *clk; struct jz4740_dmaengine_chan chan[JZ_DMA_NR_CHANS]; }; +static struct jz4740_dma_dev *jz4740_dma_chan_get_dev( + struct jz4740_dmaengine_chan *chan) +{ + return container_of(chan->vchan.chan.device, struct jz4740_dma_dev, + ddev); +} + static struct jz4740_dmaengine_chan *to_jz4740_dma_chan(struct dma_chan *c) { return container_of(c, struct jz4740_dmaengine_chan, vchan.chan); @@ -70,6 +152,29 @@ static struct jz4740_dma_desc *to_jz4740_dma_desc(struct virt_dma_desc *vdesc) return container_of(vdesc, struct jz4740_dma_desc, vdesc); } +static inline uint32_t jz4740_dma_read(struct jz4740_dma_dev *dmadev, + unsigned int reg) +{ + return readl(dmadev->base + reg); +} + +static inline void jz4740_dma_write(struct jz4740_dma_dev *dmadev, + unsigned reg, uint32_t val) +{ + writel(val, dmadev->base + reg); +} + +static inline void jz4740_dma_write_mask(struct jz4740_dma_dev *dmadev, + unsigned int reg, uint32_t val, uint32_t mask) +{ + uint32_t tmp; + + tmp = jz4740_dma_read(dmadev, reg); + tmp &= ~mask; + tmp |= val; + jz4740_dma_write(dmadev, reg, tmp); +} + static struct jz4740_dma_desc *jz4740_dma_alloc_desc(unsigned int num_sgs) { return kzalloc(sizeof(struct jz4740_dma_desc) + @@ -108,30 +213,60 @@ static int jz4740_dma_slave_config(struct dma_chan *c, const struct dma_slave_config *config) { struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); - struct jz4740_dma_config jzcfg; + struct jz4740_dma_dev *dmadev = jz4740_dma_chan_get_dev(chan); + enum jz4740_dma_width src_width; + enum jz4740_dma_width dst_width; + enum jz4740_dma_transfer_size transfer_size; + enum jz4740_dma_flags flags; + uint32_t cmd; switch (config->direction) { case DMA_MEM_TO_DEV: - jzcfg.flags = JZ4740_DMA_SRC_AUTOINC; - jzcfg.transfer_size = jz4740_dma_maxburst(config->dst_maxburst); + flags = JZ4740_DMA_SRC_AUTOINC; + transfer_size = jz4740_dma_maxburst(config->dst_maxburst); chan->fifo_addr = config->dst_addr; break; case DMA_DEV_TO_MEM: - jzcfg.flags = JZ4740_DMA_DST_AUTOINC; - jzcfg.transfer_size = jz4740_dma_maxburst(config->src_maxburst); + flags = JZ4740_DMA_DST_AUTOINC; + transfer_size = jz4740_dma_maxburst(config->src_maxburst); chan->fifo_addr = config->src_addr; break; default: return -EINVAL; } + src_width = jz4740_dma_width(config->src_addr_width); + dst_width = jz4740_dma_width(config->dst_addr_width); + + switch (transfer_size) { + case JZ4740_DMA_TRANSFER_SIZE_2BYTE: + chan->transfer_shift = 1; + break; + case JZ4740_DMA_TRANSFER_SIZE_4BYTE: + chan->transfer_shift = 2; + break; + case JZ4740_DMA_TRANSFER_SIZE_16BYTE: + chan->transfer_shift = 4; + break; + case JZ4740_DMA_TRANSFER_SIZE_32BYTE: + chan->transfer_shift = 5; + break; + default: + chan->transfer_shift = 0; + break; + } - jzcfg.src_width = jz4740_dma_width(config->src_addr_width); - jzcfg.dst_width = jz4740_dma_width(config->dst_addr_width); - jzcfg.mode = JZ4740_DMA_MODE_SINGLE; - jzcfg.request_type = config->slave_id; + cmd = flags << JZ_DMA_CMD_FLAGS_OFFSET; + cmd |= src_width << JZ_DMA_CMD_SRC_WIDTH_OFFSET; + cmd |= dst_width << JZ_DMA_CMD_DST_WIDTH_OFFSET; + cmd |= transfer_size << JZ_DMA_CMD_TRANSFER_SIZE_OFFSET; + cmd |= JZ4740_DMA_MODE_SINGLE << JZ_DMA_CMD_MODE_OFFSET; + cmd |= JZ_DMA_CMD_TRANSFER_IRQ_ENABLE; - jz4740_dma_configure(chan->jz_chan, &jzcfg); + jz4740_dma_write(dmadev, JZ_REG_DMA_CMD(chan->id), cmd); + jz4740_dma_write(dmadev, JZ_REG_DMA_STATUS_CTRL(chan->id), 0); + jz4740_dma_write(dmadev, JZ_REG_DMA_REQ_TYPE(chan->id), + config->slave_id); return 0; } @@ -139,11 +274,13 @@ static int jz4740_dma_slave_config(struct dma_chan *c, static int jz4740_dma_terminate_all(struct dma_chan *c) { struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); + struct jz4740_dma_dev *dmadev = jz4740_dma_chan_get_dev(chan); unsigned long flags; LIST_HEAD(head); spin_lock_irqsave(&chan->vchan.lock, flags); - jz4740_dma_disable(chan->jz_chan); + jz4740_dma_write_mask(dmadev, JZ_REG_DMA_STATUS_CTRL(chan->id), 0, + JZ_DMA_STATUS_CTRL_ENABLE); chan->desc = NULL; vchan_get_all_descriptors(&chan->vchan, &head); spin_unlock_irqrestore(&chan->vchan.lock, flags); @@ -170,11 +307,13 @@ static int jz4740_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, static int jz4740_dma_start_transfer(struct jz4740_dmaengine_chan *chan) { + struct jz4740_dma_dev *dmadev = jz4740_dma_chan_get_dev(chan); dma_addr_t src_addr, dst_addr; struct virt_dma_desc *vdesc; struct jz4740_dma_sg *sg; - jz4740_dma_disable(chan->jz_chan); + jz4740_dma_write_mask(dmadev, JZ_REG_DMA_STATUS_CTRL(chan->id), 0, + JZ_DMA_STATUS_CTRL_ENABLE); if (!chan->desc) { vdesc = vchan_next_desc(&chan->vchan); @@ -196,22 +335,27 @@ static int jz4740_dma_start_transfer(struct jz4740_dmaengine_chan *chan) src_addr = chan->fifo_addr; dst_addr = sg->addr; } - jz4740_dma_set_src_addr(chan->jz_chan, src_addr); - jz4740_dma_set_dst_addr(chan->jz_chan, dst_addr); - jz4740_dma_set_transfer_count(chan->jz_chan, sg->len); + jz4740_dma_write(dmadev, JZ_REG_DMA_SRC_ADDR(chan->id), src_addr); + jz4740_dma_write(dmadev, JZ_REG_DMA_DST_ADDR(chan->id), dst_addr); + jz4740_dma_write(dmadev, JZ_REG_DMA_TRANSFER_COUNT(chan->id), + sg->len >> chan->transfer_shift); chan->next_sg++; - jz4740_dma_enable(chan->jz_chan); + jz4740_dma_write_mask(dmadev, JZ_REG_DMA_STATUS_CTRL(chan->id), + JZ_DMA_STATUS_CTRL_NO_DESC | JZ_DMA_STATUS_CTRL_ENABLE, + JZ_DMA_STATUS_CTRL_HALT | JZ_DMA_STATUS_CTRL_NO_DESC | + JZ_DMA_STATUS_CTRL_ENABLE); + + jz4740_dma_write_mask(dmadev, JZ_REG_DMA_CTRL, + JZ_DMA_CTRL_ENABLE, + JZ_DMA_CTRL_HALT | JZ_DMA_CTRL_ENABLE); return 0; } -static void jz4740_dma_complete_cb(struct jz4740_dma_chan *jz_chan, int error, - void *devid) +static void jz4740_dma_chan_irq(struct jz4740_dmaengine_chan *chan) { - struct jz4740_dmaengine_chan *chan = devid; - spin_lock(&chan->vchan.lock); if (chan->desc) { if (chan->desc && chan->desc->cyclic) { @@ -227,6 +371,28 @@ static void jz4740_dma_complete_cb(struct jz4740_dma_chan *jz_chan, int error, spin_unlock(&chan->vchan.lock); } +static irqreturn_t jz4740_dma_irq(int irq, void *devid) +{ + struct jz4740_dma_dev *dmadev = devid; + uint32_t irq_status; + unsigned int i; + + irq_status = readl(dmadev->base + JZ_REG_DMA_IRQ); + + for (i = 0; i < 6; ++i) { + if (irq_status & (1 << i)) { + jz4740_dma_write_mask(dmadev, + JZ_REG_DMA_STATUS_CTRL(i), 0, + JZ_DMA_STATUS_CTRL_ENABLE | + JZ_DMA_STATUS_CTRL_TRANSFER_DONE); + + jz4740_dma_chan_irq(&dmadev->chan[i]); + } + } + + return IRQ_HANDLED; +} + static void jz4740_dma_issue_pending(struct dma_chan *c) { struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); @@ -298,7 +464,8 @@ static struct dma_async_tx_descriptor *jz4740_dma_prep_dma_cyclic( static size_t jz4740_dma_desc_residue(struct jz4740_dmaengine_chan *chan, struct jz4740_dma_desc *desc, unsigned int next_sg) { - size_t residue = 0; + struct jz4740_dma_dev *dmadev = jz4740_dma_chan_get_dev(chan); + unsigned int residue, count; unsigned int i; residue = 0; @@ -306,8 +473,11 @@ static size_t jz4740_dma_desc_residue(struct jz4740_dmaengine_chan *chan, for (i = next_sg; i < desc->num_sgs; i++) residue += desc->sg[i].len; - if (next_sg != 0) - residue += jz4740_dma_get_residue(chan->jz_chan); + if (next_sg != 0) { + count = jz4740_dma_read(dmadev, + JZ_REG_DMA_TRANSFER_COUNT(chan->id)); + residue += count << chan->transfer_shift; + } return residue; } @@ -342,24 +512,12 @@ static enum dma_status jz4740_dma_tx_status(struct dma_chan *c, static int jz4740_dma_alloc_chan_resources(struct dma_chan *c) { - struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); - - chan->jz_chan = jz4740_dma_request(chan, NULL); - if (!chan->jz_chan) - return -EBUSY; - - jz4740_dma_set_complete_cb(chan->jz_chan, jz4740_dma_complete_cb); - return 0; } static void jz4740_dma_free_chan_resources(struct dma_chan *c) { - struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); - - vchan_free_chan_resources(&chan->vchan); - jz4740_dma_free(chan->jz_chan); - chan->jz_chan = NULL; + vchan_free_chan_resources(to_virt_chan(c)); } static void jz4740_dma_desc_free(struct virt_dma_desc *vdesc) @@ -373,7 +531,9 @@ static int jz4740_dma_probe(struct platform_device *pdev) struct jz4740_dma_dev *dmadev; struct dma_device *dd; unsigned int i; + struct resource *res; int ret; + int irq; dmadev = devm_kzalloc(&pdev->dev, sizeof(*dmadev), GFP_KERNEL); if (!dmadev) @@ -381,6 +541,17 @@ static int jz4740_dma_probe(struct platform_device *pdev) dd = &dmadev->ddev; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + dmadev->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(dmadev->base)) + return PTR_ERR(dmadev->base); + + dmadev->clk = clk_get(&pdev->dev, "dma"); + if (IS_ERR(dmadev->clk)) + return PTR_ERR(dmadev->clk); + + clk_prepare_enable(dmadev->clk); + dma_cap_set(DMA_SLAVE, dd->cap_mask); dma_cap_set(DMA_CYCLIC, dd->cap_mask); dd->device_alloc_chan_resources = jz4740_dma_alloc_chan_resources; @@ -396,6 +567,7 @@ static int jz4740_dma_probe(struct platform_device *pdev) for (i = 0; i < dd->chancnt; i++) { chan = &dmadev->chan[i]; + chan->id = i; chan->vchan.desc_free = jz4740_dma_desc_free; vchan_init(&chan->vchan, dd); } @@ -404,16 +576,28 @@ static int jz4740_dma_probe(struct platform_device *pdev) if (ret) return ret; + irq = platform_get_irq(pdev, 0); + ret = request_irq(irq, jz4740_dma_irq, 0, dev_name(&pdev->dev), dmadev); + if (ret) + goto err_unregister; + platform_set_drvdata(pdev, dmadev); return 0; + +err_unregister: + dma_async_device_unregister(dd); + return ret; } static int jz4740_dma_remove(struct platform_device *pdev) { struct jz4740_dma_dev *dmadev = platform_get_drvdata(pdev); + int irq = platform_get_irq(pdev, 0); + free_irq(irq, dmadev); dma_async_device_unregister(&dmadev->ddev); + clk_disable_unprepare(dmadev->clk); return 0; } -- cgit v1.2.3 From 757f4e51b74f7fb19251020e742a78111fdea194 Mon Sep 17 00:00:00 2001 From: Maarten ter Huurne Date: Thu, 30 May 2013 18:25:00 +0200 Subject: MIPS: jz4740: Correct clock gate bit for DMA controller Signed-off-by: Maarten ter Huurne Signed-off-by: Lars-Peter Clausen Acked-by: Ralf Baechle Signed-off-by: Vinod Koul --- arch/mips/jz4740/clock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/jz4740/clock.c b/arch/mips/jz4740/clock.c index 484d38a0864f..1b5f55426cad 100644 --- a/arch/mips/jz4740/clock.c +++ b/arch/mips/jz4740/clock.c @@ -687,7 +687,7 @@ static struct clk jz4740_clock_simple_clks[] = { [3] = { .name = "dma", .parent = &jz_clk_high_speed_peripheral.clk, - .gate_bit = JZ_CLOCK_GATE_UART0, + .gate_bit = JZ_CLOCK_GATE_DMAC, .ops = &jz_clk_simple_ops, }, [4] = { -- cgit v1.2.3 From 764037c6f55c5c844d0ab6049e6e7c0dc3cb7665 Mon Sep 17 00:00:00 2001 From: Ludovic Desroches Date: Thu, 13 Jun 2013 10:39:38 +0200 Subject: ARM: at91: dt: add header to define at_hdmac configuration DMA-cell content is a concatenation of several values. In order to keep this stuff human readable, macros are introduced. The values for the FIFO configuration are not the same as the ones used in the configuration register in order to keep backward compatibility. Most devices use the half FIFO configuration but USART ones have to use the ASAP configuration. This parameter was not initially planed to be into the at91 dma dt binding. The third cell will be used to store this parameter, it will become a concatenation of the FIFO configuration and of the peripheral ID. In order to keep backward compatibility i.e. FIFO configuration is equal to 0, we have to perform a translation since the value to put in the register to set half FIFO is 1. Acked-by: Arnd Bergmann Acked-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Ludovic Desroches Signed-off-by: Nicolas Ferre --- include/dt-bindings/dma/at91.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 include/dt-bindings/dma/at91.h diff --git a/include/dt-bindings/dma/at91.h b/include/dt-bindings/dma/at91.h new file mode 100644 index 000000000000..e835037a77b4 --- /dev/null +++ b/include/dt-bindings/dma/at91.h @@ -0,0 +1,27 @@ +/* + * This header provides macros for at91 dma bindings. + * + * Copyright (C) 2013 Ludovic Desroches + * + * GPLv2 only + */ + +#ifndef __DT_BINDINGS_AT91_DMA_H__ +#define __DT_BINDINGS_AT91_DMA_H__ + +/* + * Source and/or destination peripheral ID + */ +#define AT91_DMA_CFG_PER_ID_MASK (0xff) +#define AT91_DMA_CFG_PER_ID(id) (id & AT91_DMA_CFG_PER_ID_MASK) + +/* + * FIFO configuration: it defines when a request is serviced. + */ +#define AT91_DMA_CFG_FIFOCFG_OFFSET (8) +#define AT91_DMA_CFG_FIFOCFG_MASK (0xf << AT91_DMA_CFG_FIFOCFG_OFFSET) +#define AT91_DMA_CFG_FIFOCFG_HALF (0x0 << AT91_DMA_CFG_FIFOCFG_OFFSET) /* half FIFO (default behavior) */ +#define AT91_DMA_CFG_FIFOCFG_ALAP (0x1 << AT91_DMA_CFG_FIFOCFG_OFFSET) /* largest defined AHB burst */ +#define AT91_DMA_CFG_FIFOCFG_ASAP (0x2 << AT91_DMA_CFG_FIFOCFG_OFFSET) /* single AHB access */ + +#endif /* __DT_BINDINGS_AT91_DMA_H__ */ -- cgit v1.2.3 From 62971b29825adb06908bad81e7679d1f7904b24d Mon Sep 17 00:00:00 2001 From: Ludovic Desroches Date: Thu, 13 Jun 2013 10:39:39 +0200 Subject: dmaengine: at_hdmac: add FIFO configuration parameter to DMA DT binding For most devices the FIFO configuration is the same i.e. when half FIFO size is available/filled, a source/destination request is serviced. But USART devices have to do it when there is enough space/data available to perform a single AHB access so the ASAP configuration. Acked-by: Nicolas Ferre Acked-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Ludovic Desroches Signed-off-by: Nicolas Ferre --- .../devicetree/bindings/dma/atmel-dma.txt | 7 ++++-- drivers/dma/at_hdmac.c | 25 ++++++++++++++++++---- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/dma/atmel-dma.txt b/Documentation/devicetree/bindings/dma/atmel-dma.txt index c80e8a3402f0..c280a0e6f42d 100644 --- a/Documentation/devicetree/bindings/dma/atmel-dma.txt +++ b/Documentation/devicetree/bindings/dma/atmel-dma.txt @@ -24,8 +24,11 @@ The three cells in order are: 1. A phandle pointing to the DMA controller. 2. The memory interface (16 most significant bits), the peripheral interface (16 less significant bits). -3. The peripheral identifier for the hardware handshaking interface. The -identifier can be different for tx and rx. +3. Parameters for the at91 DMA configuration register which are device +dependant: + - bit 7-0: peripheral identifier for the hardware handshaking interface. The + identifier can be different for tx and rx. + - bit 11-8: FIFO configuration. 0 for half FIFO, 1 for ALAP, 1 for ASAP. Example: diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 6db5228f4134..b7050a46bd87 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -14,6 +14,7 @@ * found on AT91SAM9263. */ +#include #include #include #include @@ -1320,15 +1321,31 @@ static struct dma_chan *at_dma_xlate(struct of_phandle_args *dma_spec, atslave = devm_kzalloc(&dmac_pdev->dev, sizeof(*atslave), GFP_KERNEL); if (!atslave) return NULL; + + atslave->cfg = ATC_DST_H2SEL_HW | ATC_SRC_H2SEL_HW; /* * We can fill both SRC_PER and DST_PER, one of these fields will be * ignored depending on DMA transfer direction. */ - per_id = dma_spec->args[1]; - atslave->cfg = ATC_FIFOCFG_HALFFIFO - | ATC_DST_H2SEL_HW | ATC_SRC_H2SEL_HW - | ATC_DST_PER_MSB(per_id) | ATC_DST_PER(per_id) + per_id = dma_spec->args[1] & AT91_DMA_CFG_PER_ID_MASK; + atslave->cfg |= ATC_DST_PER_MSB(per_id) | ATC_DST_PER(per_id) | ATC_SRC_PER_MSB(per_id) | ATC_SRC_PER(per_id); + /* + * We have to translate the value we get from the device tree since + * the half FIFO configuration value had to be 0 to keep backward + * compatibility. + */ + switch (dma_spec->args[1] & AT91_DMA_CFG_FIFOCFG_MASK) { + case AT91_DMA_CFG_FIFOCFG_ALAP: + atslave->cfg |= ATC_FIFOCFG_LARGESTBURST; + break; + case AT91_DMA_CFG_FIFOCFG_ASAP: + atslave->cfg |= ATC_FIFOCFG_ENOUGHSPACE; + break; + case AT91_DMA_CFG_FIFOCFG_HALF: + default: + atslave->cfg |= ATC_FIFOCFG_HALFFIFO; + } atslave->dma_dev = &dmac_pdev->dev; chan = dma_request_channel(mask, at_dma_filter, atslave); -- cgit v1.2.3 From 538eea6c7c2387b11fa5ba919743cd36e3a4d42f Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Fri, 7 Jun 2013 17:10:53 +0200 Subject: dmaengine: at_hdmac: remove unsuded atc_cleanup_descriptors() Since patch 7c407d3e54dcc0c79119553c8d5ef176c1d5bc3a (DMA: AT91: Get residual bytes in dma buffer), the function atc_cleanup_descriptors() is not used anymore. We remove it to prevent warnings. Reported-by: Arnd Bergmann Signed-off-by: Nicolas Ferre Acked-by: Arnd Bergmann --- drivers/dma/at_hdmac.c | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index b7050a46bd87..1ee3ed187f4d 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -416,37 +416,6 @@ static void atc_complete_all(struct at_dma_chan *atchan) atc_chain_complete(atchan, desc); } -/** - * atc_cleanup_descriptors - cleanup up finished descriptors in active_list - * @atchan: channel to be cleaned up - * - * Called with atchan->lock held and bh disabled - */ -static void atc_cleanup_descriptors(struct at_dma_chan *atchan) -{ - struct at_desc *desc, *_desc; - struct at_desc *child; - - dev_vdbg(chan2dev(&atchan->chan_common), "cleanup descriptors\n"); - - list_for_each_entry_safe(desc, _desc, &atchan->active_list, desc_node) { - if (!(desc->lli.ctrla & ATC_DONE)) - /* This one is currently in progress */ - return; - - list_for_each_entry(child, &desc->tx_list, desc_node) - if (!(child->lli.ctrla & ATC_DONE)) - /* Currently in progress */ - return; - - /* - * No descriptors so far seem to be in progress, i.e. - * this chain must be done. - */ - atc_chain_complete(atchan, desc); - } -} - /** * atc_advance_work - at the end of a transaction, move forward * @atchan: channel where the transaction ended -- cgit v1.2.3 From c3dbc60c9b2510fee6cea9b77b89a7708bf98bd3 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Fri, 7 Jun 2013 17:26:14 +0200 Subject: dmaengine/trivial: at_hdmac: add curly brackets to if/else expressions Correct coding style following the patch: 7c407d3e54dcc0c79119553c8d5ef176c1d5bc3a (DMA: AT91: Get residual bytes in dma buffer). Signed-off-by: Nicolas Ferre --- drivers/dma/at_hdmac.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 1ee3ed187f4d..3e070d2fb6a3 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -299,8 +299,9 @@ static int atc_get_bytes_left(struct dma_chan *chan) if (atchan->remain_desc < 0) { ret = -EINVAL; goto out; - } else + } else { ret = atchan->remain_desc; + } } else { /* * Get residual bytes when current @@ -1120,8 +1121,9 @@ atc_tx_status(struct dma_chan *chan, if (unlikely(bytes < 0)) { dev_vdbg(chan2dev(chan), "get residual bytes error\n"); return DMA_ERROR; - } else + } else { dma_set_residue(txstate, bytes); + } dev_vdbg(chan2dev(chan), "tx_status %d: cookie = %d residue = %d\n", ret, cookie, bytes); -- cgit v1.2.3 From f784d9c90469d75a9f7a38c1568f47e95ae504ca Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Wed, 19 Jun 2013 13:14:54 +0200 Subject: dmaengine: at_hdmac: prepare clk before calling enable Replace clk_enable/disable with clk_prepare_enable/disable_unprepare to avoid common clk framework warnings. Signed-off-by: Boris BREZILLON [nicolas.ferre@atmel.com: remove return code checking in at_dma_resume_noirq()] Signed-off-by: Nicolas Ferre --- drivers/dma/at_hdmac.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 3e070d2fb6a3..c787f38a186a 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -1460,7 +1460,9 @@ static int __init at_dma_probe(struct platform_device *pdev) err = PTR_ERR(atdma->clk); goto err_clk; } - clk_enable(atdma->clk); + err = clk_prepare_enable(atdma->clk); + if (err) + goto err_clk_prepare; /* force dma off, just in case */ at_dma_off(atdma); @@ -1560,7 +1562,8 @@ err_of_dma_controller_register: err_pool_create: free_irq(platform_get_irq(pdev, 0), atdma); err_irq: - clk_disable(atdma->clk); + clk_disable_unprepare(atdma->clk); +err_clk_prepare: clk_put(atdma->clk); err_clk: iounmap(atdma->regs); @@ -1596,7 +1599,7 @@ static int at_dma_remove(struct platform_device *pdev) list_del(&chan->device_node); } - clk_disable(atdma->clk); + clk_disable_unprepare(atdma->clk); clk_put(atdma->clk); iounmap(atdma->regs); @@ -1615,7 +1618,7 @@ static void at_dma_shutdown(struct platform_device *pdev) struct at_dma *atdma = platform_get_drvdata(pdev); at_dma_off(platform_get_drvdata(pdev)); - clk_disable(atdma->clk); + clk_disable_unprepare(atdma->clk); } static int at_dma_prepare(struct device *dev) @@ -1672,7 +1675,7 @@ static int at_dma_suspend_noirq(struct device *dev) /* disable DMA controller */ at_dma_off(atdma); - clk_disable(atdma->clk); + clk_disable_unprepare(atdma->clk); return 0; } @@ -1702,7 +1705,7 @@ static int at_dma_resume_noirq(struct device *dev) struct dma_chan *chan, *_chan; /* bring back DMA controller */ - clk_enable(atdma->clk); + clk_prepare_enable(atdma->clk); dma_writel(atdma, EN, AT_DMA_ENABLE); /* clear any pending interrupt */ -- cgit v1.2.3 From fa74326c44767626a5ae794b29d54103e2259e64 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 6 Jun 2013 17:37:13 +0200 Subject: DMA: shdma: (cosmetic) don't re-calculate a pointer Use an existing pointer instead of retrieving it again. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Vinod Koul --- drivers/dma/sh/shdma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/sh/shdma.c b/drivers/dma/sh/shdma.c index b70709b030d8..a5a1887c34b5 100644 --- a/drivers/dma/sh/shdma.c +++ b/drivers/dma/sh/shdma.c @@ -729,7 +729,7 @@ static int sh_dmae_probe(struct platform_device *pdev) goto eshdma; /* platform data */ - shdev->pdata = pdev->dev.platform_data; + shdev->pdata = pdata; if (pdata->chcr_offset) shdev->chcr_offset = pdata->chcr_offset; -- cgit v1.2.3 From d0951a23383d09276f7976ed34d8f1cede629b48 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 6 Jun 2013 17:37:14 +0200 Subject: DMA: shdma: shdma_chan_filter() has to be in shdma-base.h shdma_chan_filter() is a function, provided by the shdma-base.c module, move its declaration to the appropriate header. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Vinod Koul --- include/linux/sh_dma.h | 2 -- include/linux/shdma-base.h | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/include/linux/sh_dma.h b/include/linux/sh_dma.h index b64d6bec6f90..4e83f3e034f3 100644 --- a/include/linux/sh_dma.h +++ b/include/linux/sh_dma.h @@ -99,6 +99,4 @@ struct sh_dmae_pdata { #define CHCR_TE 0x00000002 #define CHCR_IE 0x00000004 -bool shdma_chan_filter(struct dma_chan *chan, void *arg); - #endif diff --git a/include/linux/shdma-base.h b/include/linux/shdma-base.h index a3728bf66f0e..9a938971bc4a 100644 --- a/include/linux/shdma-base.h +++ b/include/linux/shdma-base.h @@ -122,5 +122,6 @@ void shdma_chan_remove(struct shdma_chan *schan); int shdma_init(struct device *dev, struct shdma_dev *sdev, int chan_num); void shdma_cleanup(struct shdma_dev *sdev); +bool shdma_chan_filter(struct dma_chan *chan, void *arg); #endif -- cgit v1.2.3 From 67eacc1583909d0588c8d5d80c16298c899a6382 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 18 Jun 2013 18:16:57 +0200 Subject: DMA: shdma: add DT support This patch adds Device Tree support to the shdma driver. No special DT properties are used, only standard DMA DT bindings are implemented. Since shdma controllers reside on SoCs, their configuration is SoC-specific and shall be passed to the driver from the SoC platform data, using the auxdata procedure. Signed-off-by: Guennadi Liakhovetski Acked-by: Arnd Bergmann Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/dma/shdma.txt | 75 ++++++++++++++++++++++ drivers/dma/sh/Makefile | 2 +- drivers/dma/sh/shdma-base.c | 26 ++++++-- drivers/dma/sh/shdma-of.c | 82 +++++++++++++++++++++++++ drivers/dma/sh/shdma.c | 31 ++++++++-- include/linux/shdma-base.h | 2 + 6 files changed, 205 insertions(+), 13 deletions(-) create mode 100644 Documentation/devicetree/bindings/dma/shdma.txt create mode 100644 drivers/dma/sh/shdma-of.c diff --git a/Documentation/devicetree/bindings/dma/shdma.txt b/Documentation/devicetree/bindings/dma/shdma.txt new file mode 100644 index 000000000000..c15994aa1939 --- /dev/null +++ b/Documentation/devicetree/bindings/dma/shdma.txt @@ -0,0 +1,75 @@ +* SHDMA Device Tree bindings + +Sh-/r-mobile and r-car systems often have multiple identical DMA controller +instances, capable of serving any of a common set of DMA slave devices, using +the same configuration. To describe this topology we require all compatible +SHDMA DT nodes to be placed under a DMA multiplexer node. All such compatible +DMAC instances have the same number of channels and use the same DMA +descriptors. Therefore respective DMA DT bindings can also all be placed in the +multiplexer node. Even if there is only one such DMAC instance on a system, it +still has to be placed under such a multiplexer node. + +* DMA multiplexer + +Required properties: +- compatible: should be "renesas,shdma-mux" +- #dma-cells: should be <1>, see "dmas" property below + +Optional properties (currently unused): +- dma-channels: number of DMA channels +- dma-requests: number of DMA request signals + +* DMA controller + +Required properties: +- compatible: should be "renesas,shdma" + +Example: + dmac: dma-mux0 { + compatible = "renesas,shdma-mux"; + #dma-cells = <1>; + dma-channels = <6>; + dma-requests = <256>; + reg = <0 0>; /* Needed for AUXDATA */ + #address-cells = <1>; + #size-cells = <1>; + ranges; + + dma0: shdma@fe008020 { + compatible = "renesas,shdma"; + reg = <0xfe008020 0x270>, + <0xfe009000 0xc>; + interrupt-parent = <&gic>; + interrupts = <0 34 4 + 0 28 4 + 0 29 4 + 0 30 4 + 0 31 4 + 0 32 4 + 0 33 4>; + interrupt-names = "error", + "ch0", "ch1", "ch2", "ch3", + "ch4", "ch5"; + }; + + dma1: shdma@fe018020 { + ... + }; + + dma2: shdma@fe028020 { + ... + }; + }; + +* DMA client + +Required properties: +- dmas: a list of <[DMA multiplexer phandle] [MID/RID value]> pairs, + where MID/RID values are fixed handles, specified in the SoC + manual +- dma-names: a list of DMA channel names, one per "dmas" entry + +Example: + dmas = <&dmac 0xd1 + &dmac 0xd2>; + dma-names = "tx", "rx"; diff --git a/drivers/dma/sh/Makefile b/drivers/dma/sh/Makefile index c07ca4612e46..c962138dde96 100644 --- a/drivers/dma/sh/Makefile +++ b/drivers/dma/sh/Makefile @@ -1,3 +1,3 @@ -obj-$(CONFIG_SH_DMAE_BASE) += shdma-base.o +obj-$(CONFIG_SH_DMAE_BASE) += shdma-base.o shdma-of.o obj-$(CONFIG_SH_DMAE) += shdma.o obj-$(CONFIG_SUDMAC) += sudmac.o diff --git a/drivers/dma/sh/shdma-base.c b/drivers/dma/sh/shdma-base.c index 4acb85a10250..28ca36121631 100644 --- a/drivers/dma/sh/shdma-base.c +++ b/drivers/dma/sh/shdma-base.c @@ -175,7 +175,18 @@ static int shdma_setup_slave(struct shdma_chan *schan, int slave_id) { struct shdma_dev *sdev = to_shdma_dev(schan->dma_chan.device); const struct shdma_ops *ops = sdev->ops; - int ret; + int ret, match; + + if (schan->dev->of_node) { + match = schan->hw_req; + ret = ops->set_slave(schan, match, true); + if (ret < 0) + return ret; + + slave_id = schan->slave_id; + } else { + match = slave_id; + } if (slave_id < 0 || slave_id >= slave_num) return -EINVAL; @@ -183,7 +194,7 @@ static int shdma_setup_slave(struct shdma_chan *schan, int slave_id) if (test_and_set_bit(slave_id, shdma_slave_used)) return -EBUSY; - ret = ops->set_slave(schan, slave_id, false); + ret = ops->set_slave(schan, match, false); if (ret < 0) { clear_bit(slave_id, shdma_slave_used); return ret; @@ -206,23 +217,26 @@ static int shdma_setup_slave(struct shdma_chan *schan, int slave_id) * services would have to provide their own filters, which first would check * the device driver, similar to how other DMAC drivers, e.g., sa11x0-dma.c, do * this, and only then, in case of a match, call this common filter. + * NOTE 2: This filter function is also used in the DT case by shdma_of_xlate(). + * In that case the MID-RID value is used for slave channel filtering and is + * passed to this function in the "arg" parameter. */ bool shdma_chan_filter(struct dma_chan *chan, void *arg) { struct shdma_chan *schan = to_shdma_chan(chan); struct shdma_dev *sdev = to_shdma_dev(schan->dma_chan.device); const struct shdma_ops *ops = sdev->ops; - int slave_id = (int)arg; + int match = (int)arg; int ret; - if (slave_id < 0) + if (match < 0) /* No slave requested - arbitrary channel */ return true; - if (slave_id >= slave_num) + if (!schan->dev->of_node && match >= slave_num) return false; - ret = ops->set_slave(schan, slave_id, true); + ret = ops->set_slave(schan, match, true); if (ret < 0) return false; diff --git a/drivers/dma/sh/shdma-of.c b/drivers/dma/sh/shdma-of.c new file mode 100644 index 000000000000..11bcb05cd79c --- /dev/null +++ b/drivers/dma/sh/shdma-of.c @@ -0,0 +1,82 @@ +/* + * SHDMA Device Tree glue + * + * Copyright (C) 2013 Renesas Electronics Inc. + * Author: Guennadi Liakhovetski + * + * This is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define to_shdma_chan(c) container_of(c, struct shdma_chan, dma_chan) + +static struct dma_chan *shdma_of_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + u32 id = dma_spec->args[0]; + dma_cap_mask_t mask; + struct dma_chan *chan; + + if (dma_spec->args_count != 1) + return NULL; + + dma_cap_zero(mask); + /* Only slave DMA channels can be allocated via DT */ + dma_cap_set(DMA_SLAVE, mask); + + chan = dma_request_channel(mask, shdma_chan_filter, (void *)id); + if (chan) + to_shdma_chan(chan)->hw_req = id; + + return chan; +} + +static int shdma_of_probe(struct platform_device *pdev) +{ + const struct of_dev_auxdata *lookup = pdev->dev.platform_data; + int ret; + + if (!lookup) + return -EINVAL; + + ret = of_dma_controller_register(pdev->dev.of_node, + shdma_of_xlate, pdev); + if (ret < 0) + return ret; + + ret = of_platform_populate(pdev->dev.of_node, NULL, lookup, &pdev->dev); + if (ret < 0) + of_dma_controller_free(pdev->dev.of_node); + + return ret; +} + +static const struct of_device_id shdma_of_match[] = { + { .compatible = "renesas,shdma-mux", }, + { } +}; +MODULE_DEVICE_TABLE(of, sh_dmae_of_match); + +static struct platform_driver shdma_of = { + .driver = { + .owner = THIS_MODULE, + .name = "shdma-of", + .of_match_table = shdma_of_match, + }, + .probe = shdma_of_probe, +}; + +module_platform_driver(shdma_of); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("SH-DMA driver DT glue"); +MODULE_AUTHOR("Guennadi Liakhovetski "); diff --git a/drivers/dma/sh/shdma.c b/drivers/dma/sh/shdma.c index a5a1887c34b5..b67f45f5c271 100644 --- a/drivers/dma/sh/shdma.c +++ b/drivers/dma/sh/shdma.c @@ -301,20 +301,32 @@ static void sh_dmae_setup_xfer(struct shdma_chan *schan, } } +/* + * Find a slave channel configuration from the contoller list by either a slave + * ID in the non-DT case, or by a MID/RID value in the DT case + */ static const struct sh_dmae_slave_config *dmae_find_slave( - struct sh_dmae_chan *sh_chan, int slave_id) + struct sh_dmae_chan *sh_chan, int match) { struct sh_dmae_device *shdev = to_sh_dev(sh_chan); struct sh_dmae_pdata *pdata = shdev->pdata; const struct sh_dmae_slave_config *cfg; int i; - if (slave_id >= SH_DMA_SLAVE_NUMBER) - return NULL; + if (!sh_chan->shdma_chan.dev->of_node) { + if (match >= SH_DMA_SLAVE_NUMBER) + return NULL; - for (i = 0, cfg = pdata->slave; i < pdata->slave_num; i++, cfg++) - if (cfg->slave_id == slave_id) - return cfg; + for (i = 0, cfg = pdata->slave; i < pdata->slave_num; i++, cfg++) + if (cfg->slave_id == match) + return cfg; + } else { + for (i = 0, cfg = pdata->slave; i < pdata->slave_num; i++, cfg++) + if (cfg->mid_rid == match) { + sh_chan->shdma_chan.slave_id = cfg->slave_id; + return cfg; + } + } return NULL; } @@ -920,11 +932,18 @@ static int sh_dmae_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id sh_dmae_of_match[] = { + { .compatible = "renesas,shdma", }, + { } +}; +MODULE_DEVICE_TABLE(of, sh_dmae_of_match); + static struct platform_driver sh_dmae_driver = { .driver = { .owner = THIS_MODULE, .pm = &sh_dmae_pm, .name = SH_DMAE_DRV_NAME, + .of_match_table = sh_dmae_of_match, }, .remove = sh_dmae_remove, .shutdown = sh_dmae_shutdown, diff --git a/include/linux/shdma-base.h b/include/linux/shdma-base.h index 9a938971bc4a..382cf710ca9a 100644 --- a/include/linux/shdma-base.h +++ b/include/linux/shdma-base.h @@ -68,6 +68,8 @@ struct shdma_chan { int id; /* Raw id of this channel */ int irq; /* Channel IRQ */ int slave_id; /* Client ID for slave DMA */ + int hw_req; /* DMA request line for slave DMA - same + * as MID/RID, used with DT */ enum shdma_pm_state pm_state; }; -- cgit v1.2.3 From 100ac5331519866afa9b000b10642b00cb6d03dd Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 3 Jul 2013 09:55:42 +0200 Subject: perf/x86/amd: Do not print an error when the device is not present As Linus said its not an error to not have an AMD IOMMU; esp. when you're not even running on an AMD platform. Reported-by: Linus Torvalds Signed-off-by: Peter Zijlstra Acked-by: Suravee Suthikulpanit Cc: Arnaldo Carvalho de Melo Link: http://lkml.kernel.org/r/20130703075542.GF23916@twins.programming.kicks-ass.net Signed-off-by: Ingo Molnar --- arch/x86/kernel/cpu/perf_event_amd_iommu.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/arch/x86/kernel/cpu/perf_event_amd_iommu.c b/arch/x86/kernel/cpu/perf_event_amd_iommu.c index 0db655ef3918..639d1289b1ba 100644 --- a/arch/x86/kernel/cpu/perf_event_amd_iommu.c +++ b/arch/x86/kernel/cpu/perf_event_amd_iommu.c @@ -491,10 +491,8 @@ static struct perf_amd_iommu __perf_iommu = { static __init int amd_iommu_pc_init(void) { /* Make sure the IOMMU PC resource is available */ - if (!amd_iommu_pc_supported()) { - pr_err("perf: amd_iommu PMU not installed. No support!\n"); + if (!amd_iommu_pc_supported()) return -ENODEV; - } _init_perf_amd_iommu(&__perf_iommu, "amd_iommu"); -- cgit v1.2.3 From e5302920da9ef23f9d19d4e9ac85704cc25bee7a Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Fri, 5 Jul 2013 00:30:11 +0200 Subject: perf: Fix interrupt handler timing harness This patch fixes a serious bug in: 14c63f17b1fd perf: Drop sample rate when sampling is too slow There was an misunderstanding on the API of the do_div() macro. It returns the remainder of the division and this was not what the function expected leading to disabling the interrupt latency watchdog. This patch also remove a duplicate assignment in perf_sample_event_took(). Signed-off-by: Stephane Eranian Cc: peterz@infradead.org Cc: dave.hansen@linux.intel.com Cc: ak@linux.intel.com Cc: jolsa@redhat.com Link: http://lkml.kernel.org/r/20130704223010.GA30625@quad Signed-off-by: Ingo Molnar --- kernel/events/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/events/core.c b/kernel/events/core.c index 1db3af933704..1833bc5a84a7 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -182,7 +182,7 @@ void update_perf_cpu_limits(void) u64 tmp = perf_sample_period_ns; tmp *= sysctl_perf_cpu_time_max_percent; - tmp = do_div(tmp, 100); + do_div(tmp, 100); atomic_set(&perf_sample_allowed_ns, tmp); } @@ -232,7 +232,7 @@ DEFINE_PER_CPU(u64, running_sample_length); void perf_sample_event_took(u64 sample_len_ns) { u64 avg_local_sample_len; - u64 local_samples_len = __get_cpu_var(running_sample_length); + u64 local_samples_len; if (atomic_read(&perf_sample_allowed_ns) == 0) return; -- cgit v1.2.3 From 734d4e159b283a4ae4d007b7e7a91d84398ccb92 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Thu, 4 Jul 2013 23:48:46 +0100 Subject: sfc: Fix memory leak when discarding scattered packets Commit 2768935a4660 ('sfc: reuse pages to avoid DMA mapping/unmapping costs') did not fully take account of DMA scattering which was introduced immediately before. If a received packet is invalid and must be discarded, we only drop a reference to the first buffer's page, but we need to drop a reference for each buffer the packet used. I think this bug was missed partly because efx_recycle_rx_buffers() was not renamed and so no longer does what its name says. It does not change the state of buffers, but only prepares the underlying pages for recycling. Rename it accordingly. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- drivers/net/ethernet/sfc/rx.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/sfc/rx.c b/drivers/net/ethernet/sfc/rx.c index 65646cd7af8e..6af9cfda50fb 100644 --- a/drivers/net/ethernet/sfc/rx.c +++ b/drivers/net/ethernet/sfc/rx.c @@ -282,9 +282,9 @@ static void efx_fini_rx_buffer(struct efx_rx_queue *rx_queue, } /* Recycle the pages that are used by buffers that have just been received. */ -static void efx_recycle_rx_buffers(struct efx_channel *channel, - struct efx_rx_buffer *rx_buf, - unsigned int n_frags) +static void efx_recycle_rx_pages(struct efx_channel *channel, + struct efx_rx_buffer *rx_buf, + unsigned int n_frags) { struct efx_rx_queue *rx_queue = efx_channel_get_rx_queue(channel); @@ -294,6 +294,20 @@ static void efx_recycle_rx_buffers(struct efx_channel *channel, } while (--n_frags); } +static void efx_discard_rx_packet(struct efx_channel *channel, + struct efx_rx_buffer *rx_buf, + unsigned int n_frags) +{ + struct efx_rx_queue *rx_queue = efx_channel_get_rx_queue(channel); + + efx_recycle_rx_pages(channel, rx_buf, n_frags); + + do { + efx_free_rx_buffer(rx_buf); + rx_buf = efx_rx_buf_next(rx_queue, rx_buf); + } while (--n_frags); +} + /** * efx_fast_push_rx_descriptors - push new RX descriptors quickly * @rx_queue: RX descriptor queue @@ -533,8 +547,7 @@ void efx_rx_packet(struct efx_rx_queue *rx_queue, unsigned int index, */ if (unlikely(rx_buf->flags & EFX_RX_PKT_DISCARD)) { efx_rx_flush_packet(channel); - put_page(rx_buf->page); - efx_recycle_rx_buffers(channel, rx_buf, n_frags); + efx_discard_rx_packet(channel, rx_buf, n_frags); return; } @@ -570,9 +583,9 @@ void efx_rx_packet(struct efx_rx_queue *rx_queue, unsigned int index, efx_sync_rx_buffer(efx, rx_buf, rx_buf->len); } - /* All fragments have been DMA-synced, so recycle buffers and pages. */ + /* All fragments have been DMA-synced, so recycle pages. */ rx_buf = efx_rx_buffer(rx_queue, index); - efx_recycle_rx_buffers(channel, rx_buf, n_frags); + efx_recycle_rx_pages(channel, rx_buf, n_frags); /* Pipeline receives so that we give time for packet headers to be * prefetched into cache. -- cgit v1.2.3 From 332962f2c88868ed3cdab466870baaa34dd58612 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 4 Jul 2013 22:46:45 +0200 Subject: clocksource: Reselect clocksource when watchdog validated high-res capability Up to commit 5d33b883a (clocksource: Always verify highres capability) we had no sanity check when selecting a clocksource, which prevented that a non highres capable clocksource is used when the system already switched to highres/nohz mode. The new sanity check works as Alex and Tim found out. It prevents the TSC from being used. This happens because on x86 the boot process looks like this: tsc_start_freqency_validation(TSC); clocksource_register(HPET); clocksource_done_booting(); clocksource_select() Selects HPET which is valid for high-res switch_to_highres(); clocksource_register(TSC); TSC is not selected, because it is not yet flagged as VALID_HIGH_RES clocksource_watchdog() Validates TSC for highres, but that does not make TSC the current clocksource. Before the sanity check was added, we installed TSC unvalidated which worked most of the time. If the TSC was really detected as unstable, then the unstable logic removed it and installed HPET again. The sanity check is correct and needed. So the watchdog needs to kick a reselection of the clocksource, when it qualifies TSC as a valid high res clocksource. To solve this, we mark the clocksource which got the flag CLOCK_SOURCE_VALID_FOR_HRES set by the watchdog with an new flag CLOCK_SOURCE_RESELECT and trigger the watchdog thread. The watchdog thread evaluates the flag and invokes clocksource_select() when set. To avoid that the clocksource_done_booting() code, which is about to install the first real clocksource anyway, needs to go through clocksource_select and tick_oneshot_notify() pointlessly, split out the clocksource_watchdog_kthread() list walk code and invoke the select/notify only when called from clocksource_watchdog_kthread(). So clocksource_done_booting() can utilize the same splitout code without the select/notify invocation and the clocksource_mutex unlock/relock dance. Reported-and-tested-by: Alex Shi Cc: Hans Peter Anvin Cc: Tim Chen Cc: Andi Kleen Tested-by: Peter Zijlstra Cc: Ingo Molnar Cc: Davidlohr Bueso Cc: John Stultz Link: http://lkml.kernel.org/r/alpine.DEB.2.02.1307042239150.11637@ionos.tec.linutronix.de Signed-off-by: Thomas Gleixner --- include/linux/clocksource.h | 1 + kernel/time/clocksource.c | 57 +++++++++++++++++++++++++++++++++------------ 2 files changed, 43 insertions(+), 15 deletions(-) diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index 2f39a4911668..dbbf8aa7731b 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h @@ -210,6 +210,7 @@ struct clocksource { #define CLOCK_SOURCE_VALID_FOR_HRES 0x20 #define CLOCK_SOURCE_UNSTABLE 0x40 #define CLOCK_SOURCE_SUSPEND_NONSTOP 0x80 +#define CLOCK_SOURCE_RESELECT 0x100 /* simplify initialization of mask field */ #define CLOCKSOURCE_MASK(bits) (cycle_t)((bits) < 64 ? ((1ULL<<(bits))-1) : -1) diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index e713ef7d19a7..50a8736757f3 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c @@ -181,6 +181,7 @@ static int finished_booting; #ifdef CONFIG_CLOCKSOURCE_WATCHDOG static void clocksource_watchdog_work(struct work_struct *work); +static void clocksource_select(void); static LIST_HEAD(watchdog_list); static struct clocksource *watchdog; @@ -301,13 +302,30 @@ static void clocksource_watchdog(unsigned long data) if (!(cs->flags & CLOCK_SOURCE_VALID_FOR_HRES) && (cs->flags & CLOCK_SOURCE_IS_CONTINUOUS) && (watchdog->flags & CLOCK_SOURCE_IS_CONTINUOUS)) { + /* Mark it valid for high-res. */ cs->flags |= CLOCK_SOURCE_VALID_FOR_HRES; + + /* + * clocksource_done_booting() will sort it if + * finished_booting is not set yet. + */ + if (!finished_booting) + continue; + /* - * We just marked the clocksource as highres-capable, - * notify the rest of the system as well so that we - * transition into high-res mode: + * If this is not the current clocksource let + * the watchdog thread reselect it. Due to the + * change to high res this clocksource might + * be preferred now. If it is the current + * clocksource let the tick code know about + * that change. */ - tick_clock_notify(); + if (cs != curr_clocksource) { + cs->flags |= CLOCK_SOURCE_RESELECT; + schedule_work(&watchdog_work); + } else { + tick_clock_notify(); + } } } @@ -404,19 +422,25 @@ static void clocksource_dequeue_watchdog(struct clocksource *cs) spin_unlock_irqrestore(&watchdog_lock, flags); } -static int clocksource_watchdog_kthread(void *data) +static int __clocksource_watchdog_kthread(void) { struct clocksource *cs, *tmp; unsigned long flags; LIST_HEAD(unstable); + int select = 0; - mutex_lock(&clocksource_mutex); spin_lock_irqsave(&watchdog_lock, flags); - list_for_each_entry_safe(cs, tmp, &watchdog_list, wd_list) + list_for_each_entry_safe(cs, tmp, &watchdog_list, wd_list) { if (cs->flags & CLOCK_SOURCE_UNSTABLE) { list_del_init(&cs->wd_list); list_add(&cs->wd_list, &unstable); + select = 1; + } + if (cs->flags & CLOCK_SOURCE_RESELECT) { + cs->flags &= ~CLOCK_SOURCE_RESELECT; + select = 1; } + } /* Check if the watchdog timer needs to be stopped. */ clocksource_stop_watchdog(); spin_unlock_irqrestore(&watchdog_lock, flags); @@ -426,6 +450,14 @@ static int clocksource_watchdog_kthread(void *data) list_del_init(&cs->wd_list); __clocksource_change_rating(cs, 0); } + return select; +} + +static int clocksource_watchdog_kthread(void *data) +{ + mutex_lock(&clocksource_mutex); + if (__clocksource_watchdog_kthread()) + clocksource_select(); mutex_unlock(&clocksource_mutex); return 0; } @@ -445,7 +477,7 @@ static void clocksource_enqueue_watchdog(struct clocksource *cs) static inline void clocksource_dequeue_watchdog(struct clocksource *cs) { } static inline void clocksource_resume_watchdog(void) { } -static inline int clocksource_watchdog_kthread(void *data) { return 0; } +static inline int __clocksource_watchdog_kthread(void) { return 0; } static bool clocksource_is_watchdog(struct clocksource *cs) { return false; } #endif /* CONFIG_CLOCKSOURCE_WATCHDOG */ @@ -647,16 +679,11 @@ static int __init clocksource_done_booting(void) { mutex_lock(&clocksource_mutex); curr_clocksource = clocksource_default_clock(); - mutex_unlock(&clocksource_mutex); - finished_booting = 1; - /* * Run the watchdog first to eliminate unstable clock sources */ - clocksource_watchdog_kthread(NULL); - - mutex_lock(&clocksource_mutex); + __clocksource_watchdog_kthread(); clocksource_select(); mutex_unlock(&clocksource_mutex); return 0; @@ -789,7 +816,6 @@ static void __clocksource_change_rating(struct clocksource *cs, int rating) list_del(&cs->list); cs->rating = rating; clocksource_enqueue(cs); - clocksource_select(); } /** @@ -801,6 +827,7 @@ void clocksource_change_rating(struct clocksource *cs, int rating) { mutex_lock(&clocksource_mutex); __clocksource_change_rating(cs, rating); + clocksource_select(); mutex_unlock(&clocksource_mutex); } EXPORT_SYMBOL(clocksource_change_rating); -- cgit v1.2.3 From 10a0b6176b9f8b026ce07acd8f755297653c443c Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 5 Jul 2013 12:15:56 +0300 Subject: ACPI / power: add missing newline to debug messages There are few places in power.c where debug messages have no newline at the end. Reading such debug messages from dmesg is not fun, so fix this by adding the missing newlines. Signed-off-by: Mika Westerberg Cc: All Signed-off-by: Rafael J. Wysocki --- drivers/acpi/power.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 288bb270f8ed..5c28c894c0fc 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -279,7 +279,7 @@ static int acpi_power_on_unlocked(struct acpi_power_resource *resource) if (resource->ref_count++) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Power resource [%s] already on", + "Power resource [%s] already on\n", resource->name)); } else { result = __acpi_power_on(resource); @@ -325,7 +325,7 @@ static int acpi_power_off_unlocked(struct acpi_power_resource *resource) if (!resource->ref_count) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Power resource [%s] already off", + "Power resource [%s] already off\n", resource->name)); return 0; } -- cgit v1.2.3 From 84d08fa888e7c2d53b5bbc764db2ef02968b499c Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 5 Jul 2013 18:59:33 +0400 Subject: helper for reading ->d_count Signed-off-by: Al Viro --- .../staging/lustre/lustre/include/linux/lustre_patchless_compat.h | 2 +- fs/autofs4/expire.c | 8 ++++---- fs/autofs4/root.c | 2 +- fs/ceph/inode.c | 4 ++-- fs/ceph/mds_client.c | 2 +- fs/coda/dir.c | 2 +- fs/ecryptfs/inode.c | 2 +- fs/locks.c | 2 +- fs/nfs/dir.c | 6 +++--- fs/nfs/unlink.c | 2 +- fs/nilfs2/super.c | 2 +- include/linux/dcache.h | 5 +++++ 12 files changed, 22 insertions(+), 17 deletions(-) diff --git a/drivers/staging/lustre/lustre/include/linux/lustre_patchless_compat.h b/drivers/staging/lustre/lustre/include/linux/lustre_patchless_compat.h index f0508084e8c5..6367f4cc7361 100644 --- a/drivers/staging/lustre/lustre/include/linux/lustre_patchless_compat.h +++ b/drivers/staging/lustre/lustre/include/linux/lustre_patchless_compat.h @@ -60,7 +60,7 @@ truncate_complete_page(struct address_space *mapping, struct page *page) ll_delete_from_page_cache(page); } -# define d_refcount(d) ((d)->d_count) +# define d_refcount(d) d_count(d) #ifdef ATTR_OPEN # define ATTR_FROM_OPEN ATTR_OPEN diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c index 13ddec92341c..3d9d3f5d5dda 100644 --- a/fs/autofs4/expire.c +++ b/fs/autofs4/expire.c @@ -109,7 +109,7 @@ cont: spin_lock_nested(&q->d_lock, DENTRY_D_LOCK_NESTED); /* Already gone or negative dentry (under construction) - try next */ - if (q->d_count == 0 || !simple_positive(q)) { + if (!d_count(q) || !simple_positive(q)) { spin_unlock(&q->d_lock); next = q->d_u.d_child.next; goto cont; @@ -267,7 +267,7 @@ static int autofs4_tree_busy(struct vfsmount *mnt, else ino_count++; - if (p->d_count > ino_count) { + if (d_count(p) > ino_count) { top_ino->last_used = jiffies; dput(p); return 1; @@ -409,7 +409,7 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb, if (!exp_leaves) { /* Path walk currently on this dentry? */ ino_count = atomic_read(&ino->count) + 1; - if (dentry->d_count > ino_count) + if (d_count(dentry) > ino_count) goto next; if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { @@ -423,7 +423,7 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb, } else { /* Path walk currently on this dentry? */ ino_count = atomic_read(&ino->count) + 1; - if (dentry->d_count > ino_count) + if (d_count(dentry) > ino_count) goto next; expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index ca8e55548d98..92ef341ba0cf 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c @@ -179,7 +179,7 @@ static struct dentry *autofs4_lookup_active(struct dentry *dentry) spin_lock(&active->d_lock); /* Already gone? */ - if (active->d_count == 0) + if (!d_count(active)) goto next; qstr = &active->d_name; diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index be0f7e20d62e..bd2289a4f40d 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -903,8 +903,8 @@ static struct dentry *splice_dentry(struct dentry *dn, struct inode *in, } else if (realdn) { dout("dn %p (%d) spliced with %p (%d) " "inode %p ino %llx.%llx\n", - dn, dn->d_count, - realdn, realdn->d_count, + dn, d_count(dn), + realdn, d_count(realdn), realdn->d_inode, ceph_vinop(realdn->d_inode)); dput(dn); dn = realdn; diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 74fd2898b2ab..99890b02a10b 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -1553,7 +1553,7 @@ retry: *base = ceph_ino(temp->d_inode); *plen = len; dout("build_path on %p %d built %llx '%.*s'\n", - dentry, dentry->d_count, *base, len, path); + dentry, d_count(dentry), *base, len, path); return path; } diff --git a/fs/coda/dir.c b/fs/coda/dir.c index 14a14808320c..190effc6a6fa 100644 --- a/fs/coda/dir.c +++ b/fs/coda/dir.c @@ -526,7 +526,7 @@ static int coda_dentry_revalidate(struct dentry *de, unsigned int flags) if (cii->c_flags & C_FLUSH) coda_flag_inode_children(inode, C_FLUSH); - if (de->d_count > 1) + if (d_count(de) > 1) /* pretend it's valid, but don't change the flags */ goto out; diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index a2f2bb2c256d..67e9b6339691 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -358,7 +358,7 @@ static int ecryptfs_lookup_interpose(struct dentry *dentry, lower_mnt = mntget(ecryptfs_dentry_to_lower_mnt(dentry->d_parent)); fsstack_copy_attr_atime(dir_inode, lower_dentry->d_parent->d_inode); - BUG_ON(!lower_dentry->d_count); + BUG_ON(!d_count(lower_dentry)); ecryptfs_set_dentry_private(dentry, dentry_info); ecryptfs_set_dentry_lower(dentry, lower_dentry); diff --git a/fs/locks.c b/fs/locks.c index 04e2c1fdb157..c98e1a1431ea 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -1454,7 +1454,7 @@ static int generic_add_lease(struct file *filp, long arg, struct file_lock **flp if ((arg == F_RDLCK) && (atomic_read(&inode->i_writecount) > 0)) goto out; if ((arg == F_WRLCK) - && ((dentry->d_count > 1) + && ((d_count(dentry) > 1) || (atomic_read(&inode->i_count) > 1))) goto out; diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index d7ed697133f0..c933bdfbcb1f 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -1721,7 +1721,7 @@ int nfs_unlink(struct inode *dir, struct dentry *dentry) dir->i_ino, dentry->d_name.name); spin_lock(&dentry->d_lock); - if (dentry->d_count > 1) { + if (d_count(dentry) > 1) { spin_unlock(&dentry->d_lock); /* Start asynchronous writeout of the inode */ write_inode_now(dentry->d_inode, 0); @@ -1866,7 +1866,7 @@ int nfs_rename(struct inode *old_dir, struct dentry *old_dentry, dfprintk(VFS, "NFS: rename(%s/%s -> %s/%s, ct=%d)\n", old_dentry->d_parent->d_name.name, old_dentry->d_name.name, new_dentry->d_parent->d_name.name, new_dentry->d_name.name, - new_dentry->d_count); + d_count(new_dentry)); /* * For non-directories, check whether the target is busy and if so, @@ -1884,7 +1884,7 @@ int nfs_rename(struct inode *old_dir, struct dentry *old_dentry, rehash = new_dentry; } - if (new_dentry->d_count > 2) { + if (d_count(new_dentry) > 2) { int err; /* copy the target dentry's name */ diff --git a/fs/nfs/unlink.c b/fs/nfs/unlink.c index 1f1f38f0c5d5..60395ad3a2e4 100644 --- a/fs/nfs/unlink.c +++ b/fs/nfs/unlink.c @@ -479,7 +479,7 @@ nfs_sillyrename(struct inode *dir, struct dentry *dentry) dfprintk(VFS, "NFS: silly-rename(%s/%s, ct=%d)\n", dentry->d_parent->d_name.name, dentry->d_name.name, - dentry->d_count); + d_count(dentry)); nfs_inc_stats(dir, NFSIOS_SILLYRENAME); /* diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index 1427de5ebf4d..af3ba0478cdf 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -996,7 +996,7 @@ static int nilfs_attach_snapshot(struct super_block *s, __u64 cno, static int nilfs_tree_was_touched(struct dentry *root_dentry) { - return root_dentry->d_count > 1; + return d_count(root_dentry) > 1; } /** diff --git a/include/linux/dcache.h b/include/linux/dcache.h index f42dbe145479..3092df3614ae 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -324,6 +324,11 @@ static inline int __d_rcu_to_refcount(struct dentry *dentry, unsigned seq) return ret; } +static inline unsigned d_count(struct dentry *dentry) +{ + return dentry->d_count; +} + /* validate "insecure" dentry pointer */ extern int d_validate(struct dentry *, struct dentry *); -- cgit v1.2.3 From 193deee199c55ce06bca2b3e5e2d3c10208a942a Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 5 Jul 2013 19:06:16 +0400 Subject: lustre: kill the pointless wrapper Signed-off-by: Al Viro --- .../staging/lustre/lustre/include/linux/lustre_patchless_compat.h | 2 -- drivers/staging/lustre/lustre/include/linux/lvfs.h | 2 +- drivers/staging/lustre/lustre/llite/dcache.c | 8 ++++---- drivers/staging/lustre/lustre/llite/llite_internal.h | 4 ++-- drivers/staging/lustre/lustre/llite/llite_lib.c | 2 +- drivers/staging/lustre/lustre/llite/namei.c | 4 ++-- drivers/staging/lustre/lustre/lvfs/lvfs_linux.c | 4 ++-- 7 files changed, 12 insertions(+), 14 deletions(-) diff --git a/drivers/staging/lustre/lustre/include/linux/lustre_patchless_compat.h b/drivers/staging/lustre/lustre/include/linux/lustre_patchless_compat.h index 6367f4cc7361..a8e9c0c8ffd2 100644 --- a/drivers/staging/lustre/lustre/include/linux/lustre_patchless_compat.h +++ b/drivers/staging/lustre/lustre/include/linux/lustre_patchless_compat.h @@ -60,8 +60,6 @@ truncate_complete_page(struct address_space *mapping, struct page *page) ll_delete_from_page_cache(page); } -# define d_refcount(d) d_count(d) - #ifdef ATTR_OPEN # define ATTR_FROM_OPEN ATTR_OPEN #else diff --git a/drivers/staging/lustre/lustre/include/linux/lvfs.h b/drivers/staging/lustre/lustre/include/linux/lvfs.h index b4db6cb581bd..eb59ac7d5946 100644 --- a/drivers/staging/lustre/lustre/include/linux/lvfs.h +++ b/drivers/staging/lustre/lustre/include/linux/lvfs.h @@ -99,7 +99,7 @@ static inline void l_dput(struct dentry *de) if (!de || IS_ERR(de)) return; //shrink_dcache_parent(de); - LASSERT(d_refcount(de) > 0); + LASSERT(d_count(de) > 0); dput(de); } diff --git a/drivers/staging/lustre/lustre/llite/dcache.c b/drivers/staging/lustre/lustre/llite/dcache.c index 7d6abfff9740..ff0d085077c8 100644 --- a/drivers/staging/lustre/lustre/llite/dcache.c +++ b/drivers/staging/lustre/lustre/llite/dcache.c @@ -98,7 +98,7 @@ int ll_dcompare(const struct dentry *parent, const struct inode *pinode, CDEBUG(D_DENTRY, "found name %.*s(%p) flags %#x refc %d\n", name->len, name->name, dentry, dentry->d_flags, - d_refcount(dentry)); + d_count(dentry)); /* mountpoint is always valid */ if (d_mountpoint((struct dentry *)dentry)) @@ -165,7 +165,7 @@ static int ll_ddelete(const struct dentry *de) list_empty(&de->d_subdirs) ? "" : "subdirs"); /* kernel >= 2.6.38 last refcount is decreased after this function. */ - LASSERT(d_refcount(de) == 1); + LASSERT(d_count(de) == 1); /* Disable this piece of code temproarily because this is called * inside dcache_lock so it's not appropriate to do lots of work @@ -190,7 +190,7 @@ static int ll_set_dd(struct dentry *de) CDEBUG(D_DENTRY, "ldd on dentry %.*s (%p) parent %p inode %p refc %d\n", de->d_name.len, de->d_name.name, de, de->d_parent, de->d_inode, - d_refcount(de)); + d_count(de)); if (de->d_fsdata == NULL) { struct ll_dentry_data *lld; @@ -540,7 +540,7 @@ out: CDEBUG(D_DENTRY, "revalidated dentry %.*s (%p) parent %p " "inode %p refc %d\n", de->d_name.len, de->d_name.name, de, de->d_parent, de->d_inode, - d_refcount(de)); + d_count(de)); ll_set_lock_data(exp, de->d_inode, it, &bits); diff --git a/drivers/staging/lustre/lustre/llite/llite_internal.h b/drivers/staging/lustre/lustre/llite/llite_internal.h index 992cd203ca1a..5227c5c4ebe2 100644 --- a/drivers/staging/lustre/lustre/llite/llite_internal.h +++ b/drivers/staging/lustre/lustre/llite/llite_internal.h @@ -1529,12 +1529,12 @@ static inline void d_lustre_invalidate(struct dentry *dentry, int nested) { CDEBUG(D_DENTRY, "invalidate dentry %.*s (%p) parent %p inode %p " "refc %d\n", dentry->d_name.len, dentry->d_name.name, dentry, - dentry->d_parent, dentry->d_inode, d_refcount(dentry)); + dentry->d_parent, dentry->d_inode, d_count(dentry)); spin_lock_nested(&dentry->d_lock, nested ? DENTRY_D_LOCK_NESTED : DENTRY_D_LOCK_NORMAL); __d_lustre_invalidate(dentry); - if (d_refcount(dentry) == 0) + if (d_count(dentry) == 0) __d_drop(dentry); spin_unlock(&dentry->d_lock); } diff --git a/drivers/staging/lustre/lustre/llite/llite_lib.c b/drivers/staging/lustre/lustre/llite/llite_lib.c index 2311b20ee99a..afae8010623d 100644 --- a/drivers/staging/lustre/lustre/llite/llite_lib.c +++ b/drivers/staging/lustre/lustre/llite/llite_lib.c @@ -659,7 +659,7 @@ void lustre_dump_dentry(struct dentry *dentry, int recur) " flags=0x%x, fsdata=%p, %d subdirs\n", dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_parent->d_name.len, dentry->d_parent->d_name.name, - dentry->d_parent, dentry->d_inode, d_refcount(dentry), + dentry->d_parent, dentry->d_inode, d_count(dentry), dentry->d_flags, dentry->d_fsdata, subdirs); if (dentry->d_inode != NULL) ll_dump_inode(dentry->d_inode); diff --git a/drivers/staging/lustre/lustre/llite/namei.c b/drivers/staging/lustre/lustre/llite/namei.c index 58d59aa12619..ff8f63de5612 100644 --- a/drivers/staging/lustre/lustre/llite/namei.c +++ b/drivers/staging/lustre/lustre/llite/namei.c @@ -409,7 +409,7 @@ struct dentry *ll_splice_alias(struct inode *inode, struct dentry *de) iput(inode); CDEBUG(D_DENTRY, "Reuse dentry %p inode %p refc %d flags %#x\n", - new, new->d_inode, d_refcount(new), new->d_flags); + new, new->d_inode, d_count(new), new->d_flags); return new; } } @@ -417,7 +417,7 @@ struct dentry *ll_splice_alias(struct inode *inode, struct dentry *de) __d_lustre_invalidate(de); d_add(de, inode); CDEBUG(D_DENTRY, "Add dentry %p inode %p refc %d flags %#x\n", - de, de->d_inode, d_refcount(de), de->d_flags); + de, de->d_inode, d_count(de), de->d_flags); return de; } diff --git a/drivers/staging/lustre/lustre/lvfs/lvfs_linux.c b/drivers/staging/lustre/lustre/lvfs/lvfs_linux.c index 1e6f32c3549b..e70d8fe99888 100644 --- a/drivers/staging/lustre/lustre/lvfs/lvfs_linux.c +++ b/drivers/staging/lustre/lustre/lvfs/lvfs_linux.c @@ -121,8 +121,8 @@ void push_ctxt(struct lvfs_run_ctxt *save, struct lvfs_run_ctxt *new_ctx, OBD_SET_CTXT_MAGIC(save); save->fs = get_fs(); - LASSERT(d_refcount(cfs_fs_pwd(current->fs))); - LASSERT(d_refcount(new_ctx->pwd)); + LASSERT(d_count(cfs_fs_pwd(current->fs))); + LASSERT(d_count(new_ctx->pwd)); save->pwd = dget(cfs_fs_pwd(current->fs)); save->pwdmnt = mntget(cfs_fs_mnt(current->fs)); save->luc.luc_umask = current_umask(); -- cgit v1.2.3 From 5ec2481b7b47a4005bb446d176e5d0257400c77d Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 5 Jul 2013 12:09:18 +0200 Subject: hrtimers: Move SMP function call to thread context smp_call_function_* must not be called from softirq context. But clock_was_set() which calls on_each_cpu() is called from softirq context to implement a delayed clock_was_set() for the timer interrupt handler. Though that almost never gets invoked. A recent change in the resume code uses the softirq based delayed clock_was_set to support Xens resume mechanism. linux-next contains a new warning which warns if smp_call_function_* is called from softirq context which gets triggered by that Xen change. Fix this by moving the delayed clock_was_set() call to a work context. Reported-and-tested-by: Artem Savkov Reported-by: Sasha Levin Cc: David Vrabel Cc: Ingo Molnar Cc: H. Peter Anvin , Cc: Konrad Wilk Cc: John Stultz Cc: xen-devel@lists.xen.org Cc: stable@vger.kernel.org Signed-off-by: Thomas Gleixner --- kernel/hrtimer.c | 37 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index e86827e94c9a..b9b9420a1297 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -721,17 +721,20 @@ static int hrtimer_switch_to_hres(void) return 1; } +static void clock_was_set_work(struct work_struct *work) +{ + clock_was_set(); +} + +static DECLARE_WORK(hrtimer_work, clock_was_set_work); + /* - * Called from timekeeping code to reprogramm the hrtimer interrupt - * device. If called from the timer interrupt context we defer it to - * softirq context. + * Called from timekeeping and resume code to reprogramm the hrtimer + * interrupt device on all cpus. */ void clock_was_set_delayed(void) { - struct hrtimer_cpu_base *cpu_base = &__get_cpu_var(hrtimer_bases); - - cpu_base->clock_was_set = 1; - __raise_softirq_irqoff(HRTIMER_SOFTIRQ); + schedule_work(&hrtimer_work); } #else @@ -775,12 +778,7 @@ void clock_was_set(void) * During resume we might have to reprogram the high resolution timer * interrupt on all online CPUs. However, all other CPUs will be * stopped with IRQs interrupts disabled so the clock_was_set() call - * must be deferred to the softirq. - * - * The one-shot timer has already been programmed to fire immediately - * (see tick_resume_oneshot()) and this interrupt will trigger the - * softirq to run early enough to correctly reprogram the timers on - * all CPUs. + * must be deferred. */ void hrtimers_resume(void) { @@ -789,8 +787,10 @@ void hrtimers_resume(void) WARN_ONCE(!irqs_disabled(), KERN_INFO "hrtimers_resume() called with IRQs enabled!"); - cpu_base->clock_was_set = 1; - __raise_softirq_irqoff(HRTIMER_SOFTIRQ); + /* Retrigger on the local CPU */ + retrigger_next_event(NULL); + /* And schedule a retrigger for all others */ + clock_was_set_delayed(); } static inline void timer_stats_hrtimer_set_start_info(struct hrtimer *timer) @@ -1441,13 +1441,6 @@ void hrtimer_peek_ahead_timers(void) static void run_hrtimer_softirq(struct softirq_action *h) { - struct hrtimer_cpu_base *cpu_base = &__get_cpu_var(hrtimer_bases); - - if (cpu_base->clock_was_set) { - cpu_base->clock_was_set = 0; - clock_was_set(); - } - hrtimer_peek_ahead_timers(); } -- cgit v1.2.3 From c9faff6cbb3d2b37b3aa356ce455848f91685b24 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 13 Jun 2013 11:50:26 +0300 Subject: mmc: sdhci-pci: add support for eMMC hardware reset for BYT eMMC. Add support for eMMC hardware reset for BYT eMMC. Signed-off-by: Adrian Hunter Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-pci.c | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 611331a39fe5..e082fac6bc96 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -77,6 +77,8 @@ struct sdhci_pci_slot { int rst_n_gpio; int cd_gpio; int cd_irq; + + void (*hw_reset)(struct sdhci_host *host); }; struct sdhci_pci_chip { @@ -307,10 +309,27 @@ static const struct sdhci_pci_fixes sdhci_intel_pch_sdio = { .probe_slot = pch_hc_probe_slot, }; +static void sdhci_pci_int_hw_reset(struct sdhci_host *host) +{ + u8 reg; + + reg = sdhci_readb(host, SDHCI_POWER_CONTROL); + reg |= 0x10; + sdhci_writeb(host, reg, SDHCI_POWER_CONTROL); + /* For eMMC, minimum is 1us but give it 9us for good measure */ + udelay(9); + reg &= ~0x10; + sdhci_writeb(host, reg, SDHCI_POWER_CONTROL); + /* For eMMC, minimum is 200us but give it 300us for good measure */ + usleep_range(300, 1000); +} + static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot) { - slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE; + slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE | + MMC_CAP_HW_RESET; slot->host->mmc->caps2 |= MMC_CAP2_HC_ERASE_SZ; + slot->hw_reset = sdhci_pci_int_hw_reset; return 0; } @@ -1016,7 +1035,7 @@ static int sdhci_pci_bus_width(struct sdhci_host *host, int width) return 0; } -static void sdhci_pci_hw_reset(struct sdhci_host *host) +static void sdhci_pci_gpio_hw_reset(struct sdhci_host *host) { struct sdhci_pci_slot *slot = sdhci_priv(host); int rst_n_gpio = slot->rst_n_gpio; @@ -1031,6 +1050,14 @@ static void sdhci_pci_hw_reset(struct sdhci_host *host) usleep_range(300, 1000); } +static void sdhci_pci_hw_reset(struct sdhci_host *host) +{ + struct sdhci_pci_slot *slot = sdhci_priv(host); + + if (slot->hw_reset) + slot->hw_reset(host); +} + static const struct sdhci_ops sdhci_pci_ops = { .enable_dma = sdhci_pci_enable_dma, .platform_bus_width = sdhci_pci_bus_width, @@ -1328,6 +1355,7 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot( if (!gpio_request(slot->rst_n_gpio, "eMMC_reset")) { gpio_direction_output(slot->rst_n_gpio, 1); slot->host->mmc->caps |= MMC_CAP_HW_RESET; + slot->hw_reset = sdhci_pci_gpio_hw_reset; } else { dev_warn(&pdev->dev, "failed to request rst_n_gpio\n"); slot->rst_n_gpio = -EINVAL; -- cgit v1.2.3 From b04fa064e72c301e075c2d52c146282f8f464083 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 13 Jun 2013 11:50:27 +0300 Subject: mmc: sdhci-acpi: add support for eMMC hardware reset for HID 80860F14 Add support for eMMC hardware reset for HID 80860F14. Signed-off-by: Adrian Hunter Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-acpi.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index 08a85ec33224..cdd4ce0d7c90 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -85,12 +86,37 @@ static int sdhci_acpi_enable_dma(struct sdhci_host *host) return 0; } +static void sdhci_acpi_int_hw_reset(struct sdhci_host *host) +{ + u8 reg; + + reg = sdhci_readb(host, SDHCI_POWER_CONTROL); + reg |= 0x10; + sdhci_writeb(host, reg, SDHCI_POWER_CONTROL); + /* For eMMC, minimum is 1us but give it 9us for good measure */ + udelay(9); + reg &= ~0x10; + sdhci_writeb(host, reg, SDHCI_POWER_CONTROL); + /* For eMMC, minimum is 200us but give it 300us for good measure */ + usleep_range(300, 1000); +} + static const struct sdhci_ops sdhci_acpi_ops_dflt = { .enable_dma = sdhci_acpi_enable_dma, }; +static const struct sdhci_ops sdhci_acpi_ops_int = { + .enable_dma = sdhci_acpi_enable_dma, + .hw_reset = sdhci_acpi_int_hw_reset, +}; + +static const struct sdhci_acpi_chip sdhci_acpi_chip_int = { + .ops = &sdhci_acpi_ops_int, +}; + static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = { - .caps = MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE, + .chip = &sdhci_acpi_chip_int, + .caps = MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE | MMC_CAP_HW_RESET, .caps2 = MMC_CAP2_HC_ERASE_SZ, .flags = SDHCI_ACPI_RUNTIME_PM, }; -- cgit v1.2.3 From 04520817d90f1dce863ab7f06531da7579798696 Mon Sep 17 00:00:00 2001 From: Heiko Stübner Date: Thu, 13 Jun 2013 10:56:23 +0200 Subject: mmc: dw_mmc-pltfm: remove static from dw_mci_pltfm_remove dw_mci_pltfm_remove gets exported and used by dw_mmc-exynos, so should not be static. Signed-off-by: Heiko Stuebner Acked-by: Jaehoon Chung Acked-by: Seungwon Jeon Signed-off-by: Chris Ball --- drivers/mmc/host/dw_mmc-pltfm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c index 2721bd5839cb..19edb0cd0c5e 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.c +++ b/drivers/mmc/host/dw_mmc-pltfm.c @@ -65,7 +65,7 @@ static int dw_mci_pltfm_probe(struct platform_device *pdev) return dw_mci_pltfm_register(pdev, NULL); } -static int dw_mci_pltfm_remove(struct platform_device *pdev) +int dw_mci_pltfm_remove(struct platform_device *pdev) { struct dw_mci *host = platform_get_drvdata(pdev); -- cgit v1.2.3 From b177a530bbe4f7dd01617f542311f87208d21ec6 Mon Sep 17 00:00:00 2001 From: Heiko Stübner Date: Thu, 13 Jun 2013 10:57:01 +0200 Subject: mmc: dw_mmc-pltfm: move probe and remove below dt match table In a subsquent patch probe will need to do some handling of data from the dt match table. So to prevent the need for forward declarations, move probe and remove below the match table. Signed-off-by: Heiko Stuebner Acked-by: Seungwon Jeon Signed-off-by: Chris Ball --- drivers/mmc/host/dw_mmc-pltfm.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c index 19edb0cd0c5e..54e6f061daa4 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.c +++ b/drivers/mmc/host/dw_mmc-pltfm.c @@ -60,20 +60,6 @@ int dw_mci_pltfm_register(struct platform_device *pdev, } EXPORT_SYMBOL_GPL(dw_mci_pltfm_register); -static int dw_mci_pltfm_probe(struct platform_device *pdev) -{ - return dw_mci_pltfm_register(pdev, NULL); -} - -int dw_mci_pltfm_remove(struct platform_device *pdev) -{ - struct dw_mci *host = platform_get_drvdata(pdev); - - dw_mci_remove(host); - return 0; -} -EXPORT_SYMBOL_GPL(dw_mci_pltfm_remove); - #ifdef CONFIG_PM_SLEEP /* * TODO: we should probably disable the clock to the card in the suspend path. @@ -105,6 +91,20 @@ static const struct of_device_id dw_mci_pltfm_match[] = { }; MODULE_DEVICE_TABLE(of, dw_mci_pltfm_match); +static int dw_mci_pltfm_probe(struct platform_device *pdev) +{ + return dw_mci_pltfm_register(pdev, NULL); +} + +int dw_mci_pltfm_remove(struct platform_device *pdev) +{ + struct dw_mci *host = platform_get_drvdata(pdev); + + dw_mci_remove(host); + return 0; +} +EXPORT_SYMBOL_GPL(dw_mci_pltfm_remove); + static struct platform_driver dw_mci_pltfm_driver = { .probe = dw_mci_pltfm_probe, .remove = dw_mci_pltfm_remove, -- cgit v1.2.3 From c73e41c898bb59aaf50098c2c672c7132a88fdbc Mon Sep 17 00:00:00 2001 From: Heiko Stübner Date: Thu, 27 Jun 2013 11:55:35 -0400 Subject: mmc: dw_mmc-pltfm: add Rockchip variant Cortex-A9 SoCs from Rockchip use a slightly modified variant of dw_mmc controllers that seems to require the SDMMC_CMD_USE_HOLD_REG bit to always be set. There also seem to be no other modifications (additional register etc) present, so to keep the footprint low, add this small variant to the pltfm driver. Signed-off-by: Heiko Stuebner Acked-by: Seungwon Jeon Signed-off-by: Chris Ball --- .../devicetree/bindings/mmc/rockchip-dw-mshc.txt | 23 ++++++++++++++++++++++ drivers/mmc/host/dw_mmc-pltfm.c | 21 +++++++++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt diff --git a/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt new file mode 100644 index 000000000000..8a3d91d47b6a --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt @@ -0,0 +1,23 @@ +* Rockchip specific extensions to the Synopsis Designware Mobile + Storage Host Controller + +The Synopsis designware mobile storage host controller is used to interface +a SoC with storage medium such as eMMC or SD/MMC cards. This file documents +differences between the core Synopsis dw mshc controller properties described +by synopsis-dw-mshc.txt and the properties used by the Rockchip specific +extensions to the Synopsis Designware Mobile Storage Host Controller. + +Required Properties: + +* compatible: should be + - "rockchip,rk2928-dw-mshc": for Rockchip RK2928 and following + +Example: + + rkdwmmc0@12200000 { + compatible = "rockchip,rk2928-dw-mshc"; + reg = <0x12200000 0x1000>; + interrupts = <0 75 0>; + #address-cells = <1>; + #size-cells = <0>; + }; diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c index 54e6f061daa4..ee525565aa77 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.c +++ b/drivers/mmc/host/dw_mmc-pltfm.c @@ -24,6 +24,15 @@ #include "dw_mmc.h" +static void dw_mci_rockchip_prepare_command(struct dw_mci *host, u32 *cmdr) +{ + *cmdr |= SDMMC_CMD_USE_HOLD_REG; +} + +static const struct dw_mci_drv_data rockchip_drv_data = { + .prepare_command = dw_mci_rockchip_prepare_command, +}; + int dw_mci_pltfm_register(struct platform_device *pdev, const struct dw_mci_drv_data *drv_data) { @@ -87,13 +96,23 @@ EXPORT_SYMBOL_GPL(dw_mci_pltfm_pmops); static const struct of_device_id dw_mci_pltfm_match[] = { { .compatible = "snps,dw-mshc", }, + { .compatible = "rockchip,rk2928-dw-mshc", + .data = &rockchip_drv_data }, {}, }; MODULE_DEVICE_TABLE(of, dw_mci_pltfm_match); static int dw_mci_pltfm_probe(struct platform_device *pdev) { - return dw_mci_pltfm_register(pdev, NULL); + const struct dw_mci_drv_data *drv_data = NULL; + const struct of_device_id *match; + + if (pdev->dev.of_node) { + match = of_match_node(dw_mci_pltfm_match, pdev->dev.of_node); + drv_data = match->data; + } + + return dw_mci_pltfm_register(pdev, drv_data); } int dw_mci_pltfm_remove(struct platform_device *pdev) -- cgit v1.2.3 From 599115686d8f62999a871f7d7ee87de3b939b258 Mon Sep 17 00:00:00 2001 From: Giuseppe CAVALLARO Date: Thu, 13 Jun 2013 16:41:28 +0200 Subject: mmc: sdhci: fix ctrl_2 on super-speed selection This patch fixes the HC ctrl_2 programming where, in case of SDR104 and HS200, we have to write 100b in the the UHS Mode bits. We wrote 101b that is reserved from Arasan Specs. Reported-by: Youssef Triki Signed-off-by: Giuseppe Cavallaro Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 9bd6ab203843..3ad3973bdb33 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1543,16 +1543,15 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); /* Select Bus Speed Mode for host */ ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; - if (ios->timing == MMC_TIMING_MMC_HS200) - ctrl_2 |= SDHCI_CTRL_HS_SDR200; + if ((ios->timing == MMC_TIMING_MMC_HS200) || + (ios->timing == MMC_TIMING_UHS_SDR104)) + ctrl_2 |= SDHCI_CTRL_UHS_SDR104; else if (ios->timing == MMC_TIMING_UHS_SDR12) ctrl_2 |= SDHCI_CTRL_UHS_SDR12; else if (ios->timing == MMC_TIMING_UHS_SDR25) ctrl_2 |= SDHCI_CTRL_UHS_SDR25; else if (ios->timing == MMC_TIMING_UHS_SDR50) ctrl_2 |= SDHCI_CTRL_UHS_SDR50; - else if (ios->timing == MMC_TIMING_UHS_SDR104) - ctrl_2 |= SDHCI_CTRL_UHS_SDR104; else if (ios->timing == MMC_TIMING_UHS_DDR50) ctrl_2 |= SDHCI_CTRL_UHS_DDR50; sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); -- cgit v1.2.3 From d7a985e08fe84307430090b8604f5652080cc930 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 13 Jun 2013 18:25:55 +0300 Subject: mmc: omap: remove unnecessary #if 0's In commit 3451c067 (mmc: omap: add DMA engine support), some #if 0's were used to comment out parts of the code. This has been in the code for over a year and are not needed anymore (and the commented-out code doesn't even compile). Remove them. Signed-off-by: Luciano Coelho Signed-off-by: Chris Ball --- drivers/mmc/host/omap.c | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index 4b3e0eb77fe0..b94f38ec2a83 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -1413,33 +1413,17 @@ static int mmc_omap_probe(struct platform_device *pdev) else sig = host->id == 0 ? OMAP_DMA_MMC_TX : OMAP_DMA_MMC2_TX; host->dma_tx = dma_request_channel(mask, omap_dma_filter_fn, &sig); -#if 0 - if (!host->dma_tx) { - dev_err(host->dev, "unable to obtain TX DMA engine channel %u\n", - sig); - goto err_dma; - } -#else if (!host->dma_tx) dev_warn(host->dev, "unable to obtain TX DMA engine channel %u\n", sig); -#endif if (mmc_omap2()) sig = host->id == 0 ? OMAP24XX_DMA_MMC1_RX : OMAP24XX_DMA_MMC2_RX; else sig = host->id == 0 ? OMAP_DMA_MMC_RX : OMAP_DMA_MMC2_RX; host->dma_rx = dma_request_channel(mask, omap_dma_filter_fn, &sig); -#if 0 - if (!host->dma_rx) { - dev_err(host->dev, "unable to obtain RX DMA engine channel %u\n", - sig); - goto err_dma; - } -#else if (!host->dma_rx) dev_warn(host->dev, "unable to obtain RX DMA engine channel %u\n", sig); -#endif ret = request_irq(host->irq, mmc_omap_irq, 0, DRIVER_NAME, host); if (ret) -- cgit v1.2.3 From 7c4f10ac5a940b3994be7f1801a606f8605e7a69 Mon Sep 17 00:00:00 2001 From: Romain Izard Date: Fri, 14 Jun 2013 14:25:44 +0200 Subject: mmc: core: production year for eMMC 4.41 and later The field containing the production date in the CID register only uses 4 bits to encode the year, starting from 1997 in the original standard. In 2013, the production year field contains 0, and the kernel reports a 1997 production date. The eMMC 4.51 specification adds a new interpretation rule. For all devices implementing the 4.41 specification or later, the production year field will be interpreted as a value between 2010 and 2025, with 0 corresponding to 2013. Signed-off-by: Romain Izard Signed-off-by: Chris Ball --- drivers/mmc/core/mmc.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index e5ed79eb8e3d..6d02012a1d0b 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -482,6 +482,10 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) } if (card->ext_csd.rev >= 5) { + /* Adjust production date as per JEDEC JESD84-B451 */ + if (card->cid.year < 2010) + card->cid.year += 16; + /* check whether the eMMC card supports BKOPS */ if (ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) { card->ext_csd.bkops = 1; -- cgit v1.2.3 From 66b50a00992dca97b442e016a9b2dba892e2df61 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Thu, 27 Jun 2013 12:00:05 -0400 Subject: mmc: esdhc: Add support for 8-bit bus width and non-removable card This patch adds support of connecting an MMC media using an 8-bit bus width connection to Freescale's P2020 H/W SDHC controller. During the probe function, the generic function mmc_of_parse is called to detect whether the controller is configured with 8-bit bus width. Also, the generic function detects if the non-removable property is set in the device tree. The function esdhc_pltfm_bus_width was added because the bus width configuration is platform specific. Signed-off-by: Oded Gabbay Reviewed-by: Anton Vorontsov Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-esdhc.h | 7 +++++++ drivers/mmc/host/sdhci-of-esdhc.c | 44 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h index 6f16406c37cd..a2a06420e463 100644 --- a/drivers/mmc/host/sdhci-esdhc.h +++ b/drivers/mmc/host/sdhci-esdhc.h @@ -36,6 +36,13 @@ /* pltfm-specific */ #define ESDHC_HOST_CONTROL_LE 0x20 +/* + * P2020 interpretation of the SDHCI_HOST_CONTROL register + */ +#define ESDHC_CTRL_4BITBUS (0x1 << 1) +#define ESDHC_CTRL_8BITBUS (0x2 << 1) +#define ESDHC_CTRL_BUSWIDTH_MASK (0x3 << 1) + /* OF-specific */ #define ESDHC_DMA_SYSCTL 0x40c #define ESDHC_DMA_SNOOP 0x00000040 diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 2b7369729f91..b2a635e73aee 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -13,6 +13,7 @@ * your option) any later version. */ +#include #include #include #include @@ -230,6 +231,30 @@ static void esdhc_of_platform_init(struct sdhci_host *host) host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ; } +static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width) +{ + u32 ctrl; + + switch (width) { + case MMC_BUS_WIDTH_8: + ctrl = ESDHC_CTRL_8BITBUS; + break; + + case MMC_BUS_WIDTH_4: + ctrl = ESDHC_CTRL_4BITBUS; + break; + + default: + ctrl = 0; + break; + } + + clrsetbits_be32(host->ioaddr + SDHCI_HOST_CONTROL, + ESDHC_CTRL_BUSWIDTH_MASK, ctrl); + + return 0; +} + static const struct sdhci_ops sdhci_esdhc_ops = { .read_l = esdhc_readl, .read_w = esdhc_readw, @@ -247,6 +272,7 @@ static const struct sdhci_ops sdhci_esdhc_ops = { .platform_resume = esdhc_of_resume, #endif .adma_workaround = esdhci_of_adma_workaround, + .platform_bus_width = esdhc_pltfm_bus_width, }; static const struct sdhci_pltfm_data sdhci_esdhc_pdata = { @@ -262,7 +288,23 @@ static const struct sdhci_pltfm_data sdhci_esdhc_pdata = { static int sdhci_esdhc_probe(struct platform_device *pdev) { - return sdhci_pltfm_register(pdev, &sdhci_esdhc_pdata, 0); + struct sdhci_host *host; + int ret; + + host = sdhci_pltfm_init(pdev, &sdhci_esdhc_pdata, 0); + if (IS_ERR(host)) + return PTR_ERR(host); + + sdhci_get_of_property(pdev); + + /* call to generic mmc_of_parse to support additional capabilities */ + mmc_of_parse(host->mmc); + + ret = sdhci_add_host(host); + if (ret) + sdhci_pltfm_free(pdev); + + return ret; } static int sdhci_esdhc_remove(struct platform_device *pdev) -- cgit v1.2.3 From dcaff04d36fd7f22973bf4fc108912ce19bcef4f Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Fri, 5 Jul 2013 12:48:35 -0400 Subject: mmc: esdhc: Fix bug when writing to SDHCI_HOST_CONTROL register The P2020 has a non-standard implementation of the SDHCI_HOST_CONTROL register. This patch adds a QUIRK in the SDHCI header to signal that a host controller has a non-standard SDHCI_HOST_CONTROL register. The patch adds a check to the function esdhc_writeb in file sdhci-of-esdhc.c, where it checks if the write is done to the SDHCI_HOST_CONTROL register and th host has the above mentioned QUIRK, then the function simply returns instead of writing to the register. The patch also detects if the processor is P2020 (by looking in dev tree) and if so, adds the QUIRK to the host->quirk2 Signed-off-by: Oded Gabbay Reviewed-by: Anton Vorontsov Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-of-esdhc.c | 17 +++++++++++++++++ include/linux/mmc/sdhci.h | 2 ++ 2 files changed, 19 insertions(+) diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index b2a635e73aee..15039e2d1c12 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -121,6 +121,13 @@ static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg) if (reg == SDHCI_HOST_CONTROL) { u32 dma_bits; + /* + * If host control register is not standard, exit + * this function + */ + if (host->quirks2 & SDHCI_QUIRK2_BROKEN_HOST_CONTROL) + return; + /* DMA select is 22,23 bits in Protocol Control Register */ dma_bits = (val & SDHCI_CTRL_DMA_MASK) << 5; clrsetbits_be32(host->ioaddr + reg , SDHCI_CTRL_DMA_MASK << 5, @@ -289,6 +296,7 @@ static const struct sdhci_pltfm_data sdhci_esdhc_pdata = { static int sdhci_esdhc_probe(struct platform_device *pdev) { struct sdhci_host *host; + struct device_node *np; int ret; host = sdhci_pltfm_init(pdev, &sdhci_esdhc_pdata, 0); @@ -297,6 +305,15 @@ static int sdhci_esdhc_probe(struct platform_device *pdev) sdhci_get_of_property(pdev); + np = pdev->dev.of_node; + if (of_device_is_compatible(np, "fsl,p2020-esdhc")) { + /* + * Freescale messed up with P2020 as it has a non-standard + * host control register + */ + host->quirks2 |= SDHCI_QUIRK2_BROKEN_HOST_CONTROL; + } + /* call to generic mmc_of_parse to support additional capabilities */ mmc_of_parse(host->mmc); diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h index a74518009099..e3c6a74d980a 100644 --- a/include/linux/mmc/sdhci.h +++ b/include/linux/mmc/sdhci.h @@ -96,6 +96,8 @@ struct sdhci_host { #define SDHCI_QUIRK2_NO_1_8_V (1<<2) #define SDHCI_QUIRK2_PRESET_VALUE_BROKEN (1<<3) #define SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON (1<<4) +/* Controller has a non-standard host control register */ +#define SDHCI_QUIRK2_BROKEN_HOST_CONTROL (1<<5) int irq; /* Device IRQ */ void __iomem *ioaddr; /* Mapped address */ -- cgit v1.2.3 From 30d025c0f7234409e8ee1bf22d1729055e640ec6 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 20 Jun 2013 12:57:59 +0300 Subject: mmc: sdhci-pci: add another device id Add another PCI device id for an eMMC host controller. Signed-off-by: Adrian Hunter Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-pci.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index e082fac6bc96..d7d6bc8968d2 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -36,6 +36,7 @@ #define PCI_DEVICE_ID_INTEL_BYT_EMMC 0x0f14 #define PCI_DEVICE_ID_INTEL_BYT_SDIO 0x0f15 #define PCI_DEVICE_ID_INTEL_BYT_SD 0x0f16 +#define PCI_DEVICE_ID_INTEL_BYT_EMMC2 0x0f50 /* * PCI registers @@ -930,6 +931,14 @@ static const struct pci_device_id pci_ids[] = { .driver_data = (kernel_ulong_t)&sdhci_intel_byt_sd, }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_BYT_EMMC2, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_byt_emmc, + }, + { .vendor = PCI_VENDOR_ID_O2, .device = PCI_DEVICE_ID_O2_8120, -- cgit v1.2.3 From 203bb5af65d4edaf600d75130438c0c512b05986 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 2 Jul 2013 12:53:01 +0200 Subject: mmc: core: Fixup Oops for SDIO shutdown Commit "mmc: core: Handle card shutdown from mmc_bus" introduced an Oops in the shutdown sequence for SDIO. The drv pointer, does not exist for SDIO since the probing of the SDIO card from the mmc_bus perspective is expected to fail by returning -ENODEV. This patch adds the proper check for the pointer before calling it. Signed-off-by: Ulf Hansson Reported-by: Stephen Warren Reported-by: Tuomas Tynkkynen Tested-by: Tuomas Tynkkynen Acked-by: Jaehoon Chung Signed-off-by: Chris Ball --- drivers/mmc/core/bus.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index 4c0decfffb53..d4b99bbe0781 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -129,7 +129,8 @@ static void mmc_bus_shutdown(struct device *dev) struct mmc_host *host = card->host; int ret; - drv->shutdown(card); + if (dev->driver && drv->shutdown) + drv->shutdown(card); if (host->bus_ops->shutdown) { ret = host->bus_ops->shutdown(host); -- cgit v1.2.3 From 722e1280c932dd42c474390482429ce12aff9031 Mon Sep 17 00:00:00 2001 From: Christian Daudt Date: Thu, 20 Jun 2013 14:26:36 -0700 Subject: mmc: sdhci: add card_event callback to sdhci Add a card_event callback to sdhci so that clients can provide their own card_event to be called when card_detect is triggered. Signed-off-by: Christian Daudt Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 4 ++++ drivers/mmc/host/sdhci.h | 1 + 2 files changed, 5 insertions(+) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 3ad3973bdb33..a78bd4f3aecc 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2062,6 +2062,10 @@ static void sdhci_card_event(struct mmc_host *mmc) struct sdhci_host *host = mmc_priv(mmc); unsigned long flags; + /* First check if client has provided their own card event */ + if (host->ops->card_event) + host->ops->card_event(host); + spin_lock_irqsave(&host->lock, flags); /* Check host->mrq first in case we are runtime suspended */ diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 379e09d9f3c1..b037f188fe44 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -294,6 +294,7 @@ struct sdhci_ops { void (*platform_resume)(struct sdhci_host *host); void (*adma_workaround)(struct sdhci_host *host, u32 intmask); void (*platform_init)(struct sdhci_host *host); + void (*card_event)(struct sdhci_host *host); }; #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS -- cgit v1.2.3 From 01ebea1b411aafc8eab440bf1d2037f01bbed99b Mon Sep 17 00:00:00 2001 From: Christian Daudt Date: Thu, 20 Jun 2013 14:26:37 -0700 Subject: mmc: bcm281xx SDHCI driver Add SDHCI driver for the Broadcom 281xx SoCs. Still missing: - power managemement Signed-off-by: Christian Daudt Acked-by: Arnd Bergmann Signed-off-by: Chris Ball --- arch/arm/configs/bcm_defconfig | 7 + drivers/mmc/host/Kconfig | 11 ++ drivers/mmc/host/Makefile | 1 + drivers/mmc/host/sdhci-bcm-kona.c | 348 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 367 insertions(+) create mode 100644 drivers/mmc/host/sdhci-bcm-kona.c diff --git a/arch/arm/configs/bcm_defconfig b/arch/arm/configs/bcm_defconfig index e3bf2d65618e..65edf6d47215 100644 --- a/arch/arm/configs/bcm_defconfig +++ b/arch/arm/configs/bcm_defconfig @@ -78,6 +78,13 @@ CONFIG_BACKLIGHT_LCD_SUPPORT=y CONFIG_LCD_CLASS_DEVICE=y CONFIG_BACKLIGHT_CLASS_DEVICE=y # CONFIG_USB_SUPPORT is not set +CONFIG_MMC=y +CONFIG_MMC_UNSAFE_RESUME=y +CONFIG_MMC_BLOCK_MINORS=32 +CONFIG_MMC_TEST=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_BCM_KONA=y CONFIG_NEW_LEDS=y CONFIG_LEDS_CLASS=y CONFIG_LEDS_TRIGGERS=y diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 1be228998c81..8a4c066787d7 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -249,6 +249,17 @@ config MMC_SDHCI_S3C_DMA YMMV. +config MMC_SDHCI_BCM_KONA + tristate "SDHCI support on Broadcom KONA platform" + depends on ARCH_BCM + select MMC_SDHCI_PLTFM + help + This selects the Broadcom Kona Secure Digital Host Controller + Interface(SDHCI) support. + This is used in Broadcom mobile SoCs. + + If you have a controller with this interface, say Y or M here. + config MMC_SDHCI_BCM2835 tristate "SDHCI platform support for the BCM2835 SD/MMC Controller" depends on ARCH_BCM2835 diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 67718c1b79ce..d422e2167e19 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -61,6 +61,7 @@ obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o obj-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci-tegra.o obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o +obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o obj-$(CONFIG_MMC_SDHCI_BCM2835) += sdhci-bcm2835.o ifeq ($(CONFIG_CB710_DEBUG),y) diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c new file mode 100644 index 000000000000..87175f9817c2 --- /dev/null +++ b/drivers/mmc/host/sdhci-bcm-kona.c @@ -0,0 +1,348 @@ +/* + * Copyright (C) 2013 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sdhci-pltfm.h" +#include "sdhci.h" + +#define SDHCI_SOFT_RESET 0x01000000 +#define KONA_SDHOST_CORECTRL 0x8000 +#define KONA_SDHOST_CD_PINCTRL 0x00000008 +#define KONA_SDHOST_STOP_HCLK 0x00000004 +#define KONA_SDHOST_RESET 0x00000002 +#define KONA_SDHOST_EN 0x00000001 + +#define KONA_SDHOST_CORESTAT 0x8004 +#define KONA_SDHOST_WP 0x00000002 +#define KONA_SDHOST_CD_SW 0x00000001 + +#define KONA_SDHOST_COREIMR 0x8008 +#define KONA_SDHOST_IP 0x00000001 + +#define KONA_SDHOST_COREISR 0x800C +#define KONA_SDHOST_COREIMSR 0x8010 +#define KONA_SDHOST_COREDBG1 0x8014 +#define KONA_SDHOST_COREGPO_MASK 0x8018 + +#define SD_DETECT_GPIO_DEBOUNCE_128MS 128 + +#define KONA_MMC_AUTOSUSPEND_DELAY (50) + +struct sdhci_bcm_kona_dev { + struct mutex write_lock; /* protect back to back writes */ +}; + + +static int sdhci_bcm_kona_sd_reset(struct sdhci_host *host) +{ + unsigned int val; + unsigned long timeout; + + /* This timeout should be sufficent for core to reset */ + timeout = jiffies + msecs_to_jiffies(100); + + /* reset the host using the top level reset */ + val = sdhci_readl(host, KONA_SDHOST_CORECTRL); + val |= KONA_SDHOST_RESET; + sdhci_writel(host, val, KONA_SDHOST_CORECTRL); + + while (!(sdhci_readl(host, KONA_SDHOST_CORECTRL) & KONA_SDHOST_RESET)) { + if (time_is_before_jiffies(timeout)) { + pr_err("Error: sd host is stuck in reset!!!\n"); + return -EFAULT; + } + } + + /* bring the host out of reset */ + val = sdhci_readl(host, KONA_SDHOST_CORECTRL); + val &= ~KONA_SDHOST_RESET; + + /* + * Back-to-Back register write needs a delay of 1ms at bootup (min 10uS) + * Back-to-Back writes to same register needs delay when SD bus clock + * is very low w.r.t AHB clock, mainly during boot-time and during card + * insert-removal. + */ + usleep_range(1000, 5000); + sdhci_writel(host, val, KONA_SDHOST_CORECTRL); + + return 0; +} + +static void sdhci_bcm_kona_sd_init(struct sdhci_host *host) +{ + unsigned int val; + + /* enable the interrupt from the IP core */ + val = sdhci_readl(host, KONA_SDHOST_COREIMR); + val |= KONA_SDHOST_IP; + sdhci_writel(host, val, KONA_SDHOST_COREIMR); + + /* Enable the AHB clock gating module to the host */ + val = sdhci_readl(host, KONA_SDHOST_CORECTRL); + val |= KONA_SDHOST_EN; + + /* + * Back-to-Back register write needs a delay of 1ms at bootup (min 10uS) + * Back-to-Back writes to same register needs delay when SD bus clock + * is very low w.r.t AHB clock, mainly during boot-time and during card + * insert-removal. + */ + usleep_range(1000, 5000); + sdhci_writel(host, val, KONA_SDHOST_CORECTRL); +} + +/* + * Software emulation of the SD card insertion/removal. Set insert=1 for insert + * and insert=0 for removal. The card detection is done by GPIO. For Broadcom + * IP to function properly the bit 0 of CORESTAT register needs to be set/reset + * to generate the CD IRQ handled in sdhci.c which schedules card_tasklet. + */ +static int sdhci_bcm_kona_sd_card_emulate(struct sdhci_host *host, int insert) +{ + struct sdhci_pltfm_host *pltfm_priv = sdhci_priv(host); + struct sdhci_bcm_kona_dev *kona_dev = sdhci_pltfm_priv(pltfm_priv); + u32 val; + + /* + * Back-to-Back register write needs a delay of min 10uS. + * Back-to-Back writes to same register needs delay when SD bus clock + * is very low w.r.t AHB clock, mainly during boot-time and during card + * insert-removal. + * We keep 20uS + */ + mutex_lock(&kona_dev->write_lock); + udelay(20); + val = sdhci_readl(host, KONA_SDHOST_CORESTAT); + + if (insert) { + int ret; + + ret = mmc_gpio_get_ro(host->mmc); + if (ret >= 0) + val = (val & ~KONA_SDHOST_WP) | + ((ret) ? KONA_SDHOST_WP : 0); + + val |= KONA_SDHOST_CD_SW; + sdhci_writel(host, val, KONA_SDHOST_CORESTAT); + } else { + val &= ~KONA_SDHOST_CD_SW; + sdhci_writel(host, val, KONA_SDHOST_CORESTAT); + } + mutex_unlock(&kona_dev->write_lock); + + return 0; +} + +/* + * SD card interrupt event callback + */ +void sdhci_bcm_kona_card_event(struct sdhci_host *host) +{ + if (mmc_gpio_get_cd(host->mmc) > 0) { + dev_dbg(mmc_dev(host->mmc), + "card inserted\n"); + sdhci_bcm_kona_sd_card_emulate(host, 1); + } else { + dev_dbg(mmc_dev(host->mmc), + "card removed\n"); + sdhci_bcm_kona_sd_card_emulate(host, 0); + } +} + +/* + * Get the base clock. Use central clock source for now. Not sure if different + * clock speed to each dev is allowed + */ +static unsigned int sdhci_bcm_kona_get_max_clk(struct sdhci_host *host) +{ + struct sdhci_bcm_kona_dev *kona_dev; + struct sdhci_pltfm_host *pltfm_priv = sdhci_priv(host); + kona_dev = sdhci_pltfm_priv(pltfm_priv); + + return host->mmc->f_max; +} + +static unsigned int sdhci_bcm_kona_get_timeout_clock(struct sdhci_host *host) +{ + return sdhci_bcm_kona_get_max_clk(host); +} + +static void sdhci_bcm_kona_init_74_clocks(struct sdhci_host *host, + u8 power_mode) +{ + /* + * JEDEC and SD spec specify supplying 74 continuous clocks to + * device after power up. With minimum bus (100KHz) that + * that translates to 740us + */ + if (power_mode != MMC_POWER_OFF) + udelay(740); +} + +static struct sdhci_ops sdhci_bcm_kona_ops = { + .get_max_clock = sdhci_bcm_kona_get_max_clk, + .get_timeout_clock = sdhci_bcm_kona_get_timeout_clock, + .platform_send_init_74_clocks = sdhci_bcm_kona_init_74_clocks, + .card_event = sdhci_bcm_kona_card_event, +}; + +static struct sdhci_pltfm_data sdhci_pltfm_data_kona = { + .ops = &sdhci_bcm_kona_ops, + .quirks = SDHCI_QUIRK_NO_CARD_NO_RESET | + SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | SDHCI_QUIRK_32BIT_DMA_ADDR | + SDHCI_QUIRK_32BIT_DMA_SIZE | SDHCI_QUIRK_32BIT_ADMA_SIZE | + SDHCI_QUIRK_FORCE_BLK_SZ_2048 | + SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, +}; + +static const struct of_device_id sdhci_bcm_kona_of_match[] __initdata = { + { .compatible = "bcm,kona-sdhci"}, + {} +}; +MODULE_DEVICE_TABLE(of, sdhci_bcm_kona_of_match); + +static int __init sdhci_bcm_kona_probe(struct platform_device *pdev) +{ + struct sdhci_bcm_kona_dev *kona_dev = NULL; + struct sdhci_pltfm_host *pltfm_priv; + struct device *dev = &pdev->dev; + struct sdhci_host *host; + int ret; + + ret = 0; + + host = sdhci_pltfm_init(pdev, &sdhci_pltfm_data_kona, + sizeof(*kona_dev)); + if (IS_ERR(host)) + return PTR_ERR(host); + + dev_dbg(dev, "%s: inited. IOADDR=%p\n", __func__, host->ioaddr); + + pltfm_priv = sdhci_priv(host); + + kona_dev = sdhci_pltfm_priv(pltfm_priv); + mutex_init(&kona_dev->write_lock); + + mmc_of_parse(host->mmc); + + if (!host->mmc->f_max) { + dev_err(&pdev->dev, "Missing max-freq for SDHCI cfg\n"); + ret = -ENXIO; + goto err_pltfm_free; + } + + dev_dbg(dev, "non-removable=%c\n", + (host->mmc->caps & MMC_CAP_NONREMOVABLE) ? 'Y' : 'N'); + dev_dbg(dev, "cd_gpio %c, wp_gpio %c\n", + (mmc_gpio_get_cd(host->mmc) != -ENOSYS) ? 'Y' : 'N', + (mmc_gpio_get_ro(host->mmc) != -ENOSYS) ? 'Y' : 'N'); + + if (host->mmc->caps | MMC_CAP_NONREMOVABLE) + host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; + + dev_dbg(dev, "is_8bit=%c\n", + (host->mmc->caps | MMC_CAP_8_BIT_DATA) ? 'Y' : 'N'); + + ret = sdhci_bcm_kona_sd_reset(host); + if (ret) + goto err_pltfm_free; + + sdhci_bcm_kona_sd_init(host); + + ret = sdhci_add_host(host); + if (ret) { + dev_err(dev, "Failed sdhci_add_host\n"); + goto err_reset; + } + + /* if device is eMMC, emulate card insert right here */ + if (host->mmc->caps | MMC_CAP_NONREMOVABLE) { + ret = sdhci_bcm_kona_sd_card_emulate(host, 1); + if (ret) { + dev_err(dev, + "unable to emulate card insertion\n"); + goto err_remove_host; + } + } + /* + * Since the card detection GPIO interrupt is configured to be + * edge sensitive, check the initial GPIO value here, emulate + * only if the card is present + */ + if (mmc_gpio_get_cd(host->mmc) > 0) + sdhci_bcm_kona_sd_card_emulate(host, 1); + + dev_dbg(dev, "initialized properly\n"); + return 0; + +err_remove_host: + sdhci_remove_host(host, 0); + +err_reset: + sdhci_bcm_kona_sd_reset(host); + +err_pltfm_free: + sdhci_pltfm_free(pdev); + + dev_err(dev, "Probing of sdhci-pltfm failed: %d\n", ret); + return ret; +} + +static int __exit sdhci_bcm_kona_remove(struct platform_device *pdev) +{ + struct sdhci_host *host = platform_get_drvdata(pdev); + int dead; + u32 scratch; + + dead = 0; + scratch = readl(host->ioaddr + SDHCI_INT_STATUS); + if (scratch == (u32)-1) + dead = 1; + sdhci_remove_host(host, dead); + + sdhci_free_host(host); + + return 0; +} + +static struct platform_driver sdhci_bcm_kona_driver = { + .driver = { + .name = "sdhci-kona", + .owner = THIS_MODULE, + .pm = SDHCI_PLTFM_PMOPS, + .of_match_table = of_match_ptr(sdhci_bcm_kona_of_match), + }, + .probe = sdhci_bcm_kona_probe, + .remove = __exit_p(sdhci_bcm_kona_remove), +}; +module_platform_driver(sdhci_bcm_kona_driver); + +MODULE_DESCRIPTION("SDHCI driver for Broadcom Kona platform"); +MODULE_AUTHOR("Broadcom"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From c6cf7777a32da874fabec4fd1c2a579f0ba4e4dd Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 5 Jul 2013 13:14:30 -0400 Subject: drm/radeon: set default clocks for SI when DPM is disabled Fix patching of vddc values for SI and enable manually forcing clocks to default levels as per NI. This improves the out of the box performance with SI asics. Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/evergreen.c | 8 ++++---- drivers/gpu/drm/radeon/radeon_atombios.c | 4 ++++ drivers/gpu/drm/radeon/radeon_pm.c | 8 ++++---- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index 2e1de4fd2975..e49059dc9b8f 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -1504,8 +1504,8 @@ void evergreen_pm_misc(struct radeon_device *rdev) struct radeon_voltage *voltage = &ps->clock_info[req_cm_idx].voltage; if (voltage->type == VOLTAGE_SW) { - /* 0xff01 is a flag rather then an actual voltage */ - if (voltage->voltage == 0xff01) + /* 0xff0x are flags rather then an actual voltage */ + if ((voltage->voltage & 0xff00) == 0xff00) return; if (voltage->voltage && (voltage->voltage != rdev->pm.current_vddc)) { radeon_atom_set_voltage(rdev, voltage->voltage, SET_VOLTAGE_TYPE_ASIC_VDDC); @@ -1525,8 +1525,8 @@ void evergreen_pm_misc(struct radeon_device *rdev) voltage = &rdev->pm.power_state[req_ps_idx]. clock_info[rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_cm_idx].voltage; - /* 0xff01 is a flag rather then an actual voltage */ - if (voltage->vddci == 0xff01) + /* 0xff0x are flags rather then an actual voltage */ + if ((voltage->vddci & 0xff00) == 0xff00) return; if (voltage->vddci && (voltage->vddci != rdev->pm.current_vddci)) { radeon_atom_set_voltage(rdev, voltage->vddci, SET_VOLTAGE_TYPE_ASIC_VDDCI); diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index b1777d10d0b5..fbdaff55556b 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -2441,6 +2441,10 @@ static bool radeon_atombios_parse_pplib_clock_info(struct radeon_device *rdev, case ATOM_VIRTUAL_VOLTAGE_ID1: case ATOM_VIRTUAL_VOLTAGE_ID2: case ATOM_VIRTUAL_VOLTAGE_ID3: + case ATOM_VIRTUAL_VOLTAGE_ID4: + case ATOM_VIRTUAL_VOLTAGE_ID5: + case ATOM_VIRTUAL_VOLTAGE_ID6: + case ATOM_VIRTUAL_VOLTAGE_ID7: if (radeon_atom_get_max_vddc(rdev, VOLTAGE_TYPE_VDDC, rdev->pm.power_state[state_index].clock_info[mode_index].voltage.voltage, &vddc) == 0) diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index ebbdb477745a..c3e5e119702d 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -852,7 +852,7 @@ static void radeon_pm_resume_old(struct radeon_device *rdev) { /* set up the default clocks if the MC ucode is loaded */ if ((rdev->family >= CHIP_BARTS) && - (rdev->family <= CHIP_CAYMAN) && + (rdev->family <= CHIP_HAINAN) && rdev->mc_fw) { if (rdev->pm.default_vddc) radeon_atom_set_voltage(rdev, rdev->pm.default_vddc, @@ -896,7 +896,7 @@ static void radeon_pm_resume_dpm(struct radeon_device *rdev) if (ret) { DRM_ERROR("radeon: dpm resume failed\n"); if ((rdev->family >= CHIP_BARTS) && - (rdev->family <= CHIP_CAYMAN) && + (rdev->family <= CHIP_HAINAN) && rdev->mc_fw) { if (rdev->pm.default_vddc) radeon_atom_set_voltage(rdev, rdev->pm.default_vddc, @@ -947,7 +947,7 @@ static int radeon_pm_init_old(struct radeon_device *rdev) radeon_pm_init_profile(rdev); /* set up the default clocks if the MC ucode is loaded */ if ((rdev->family >= CHIP_BARTS) && - (rdev->family <= CHIP_CAYMAN) && + (rdev->family <= CHIP_HAINAN) && rdev->mc_fw) { if (rdev->pm.default_vddc) radeon_atom_set_voltage(rdev, rdev->pm.default_vddc, @@ -1032,7 +1032,7 @@ static int radeon_pm_init_dpm(struct radeon_device *rdev) if (ret) { rdev->pm.dpm_enabled = false; if ((rdev->family >= CHIP_BARTS) && - (rdev->family <= CHIP_CAYMAN) && + (rdev->family <= CHIP_HAINAN) && rdev->mc_fw) { if (rdev->pm.default_vddc) radeon_atom_set_voltage(rdev, rdev->pm.default_vddc, -- cgit v1.2.3 From edcaa5b12525f0de79e027ea1ae8a96ee7d785b3 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 5 Jul 2013 11:48:31 -0400 Subject: drm/radeon: add support for 3d perf states on older asics Certain older rv770 asics have both a performance and a 3D performance state rather than just multiple performance levels in the state power state. The current code would select the performance state rather than the 3D performance state when the "performance" profile was selected. This change switches to the "balanced" profile by default which ends up being the internal performance profile. When the user selects the "performance" profile, it selects the internal 3D performance state so the user can select the higher performance modes. For most asics this changes nothing. For certain rv770 asics with static performance and 3D performance states, this allows you to select between then using by selecting the "balanced" and "performance" dpm profiles. Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/radeon.h | 1 + drivers/gpu/drm/radeon/radeon_pm.c | 17 ++++++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index f51807f04f65..08cececb376d 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -1100,6 +1100,7 @@ enum radeon_pm_state_type { POWER_STATE_TYPE_INTERNAL_THERMAL, POWER_STATE_TYPE_INTERNAL_ACPI, POWER_STATE_TYPE_INTERNAL_ULV, + POWER_STATE_TYPE_INTERNAL_3DPERF, }; enum radeon_pm_profile_type { diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index c3e5e119702d..aaafb931922f 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -586,11 +586,16 @@ static struct radeon_ps *radeon_dpm_pick_power_state(struct radeon_device *rdev, struct radeon_ps *ps; u32 ui_class; -restart_search: + /* certain older asics have a separare 3D performance state, + * so try that first if the user selected performance + */ + if (dpm_state == POWER_STATE_TYPE_PERFORMANCE) + dpm_state = POWER_STATE_TYPE_INTERNAL_3DPERF; /* balanced states don't exist at the moment */ if (dpm_state == POWER_STATE_TYPE_BALANCED) dpm_state = POWER_STATE_TYPE_PERFORMANCE; +restart_search: /* Pick the best power state based on current conditions */ for (i = 0; i < rdev->pm.dpm.num_ps; i++) { ps = &rdev->pm.dpm.ps[i]; @@ -657,6 +662,10 @@ restart_search: if (ps->class2 & ATOM_PPLIB_CLASSIFICATION2_ULV) return ps; break; + case POWER_STATE_TYPE_INTERNAL_3DPERF: + if (ps->class & ATOM_PPLIB_CLASSIFICATION_3DPERFORMANCE) + return ps; + break; default: break; } @@ -675,6 +684,8 @@ restart_search: dpm_state = POWER_STATE_TYPE_BATTERY; goto restart_search; case POWER_STATE_TYPE_BATTERY: + case POWER_STATE_TYPE_BALANCED: + case POWER_STATE_TYPE_INTERNAL_3DPERF: dpm_state = POWER_STATE_TYPE_PERFORMANCE; goto restart_search; default: @@ -1003,8 +1014,8 @@ static int radeon_pm_init_dpm(struct radeon_device *rdev) int ret; /* default to performance state */ - rdev->pm.dpm.state = POWER_STATE_TYPE_PERFORMANCE; - rdev->pm.dpm.user_state = POWER_STATE_TYPE_PERFORMANCE; + rdev->pm.dpm.state = POWER_STATE_TYPE_BALANCED; + rdev->pm.dpm.user_state = POWER_STATE_TYPE_BALANCED; rdev->pm.default_sclk = rdev->clock.default_sclk; rdev->pm.default_mclk = rdev->clock.default_mclk; rdev->pm.current_sclk = rdev->clock.default_sclk; -- cgit v1.2.3 From 67d5ced503db5b44cb82f378c9cb3f0e77a94e7f Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Fri, 5 Jul 2013 10:05:49 -0400 Subject: drm/radeon: fix surface setup on r1xx r1xx asics have a slightly different surface register setup compared to newer asics. There is no specific enable bit for macro tiling, rather, to disable macro tiling, you need to set the surface pitch to 0. With this fixed, the special rn50 handling can go. Noticed-by: Mark Kettenis Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/r100.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c index d0314ecbd7c1..c9affefd79f6 100644 --- a/drivers/gpu/drm/radeon/r100.c +++ b/drivers/gpu/drm/radeon/r100.c @@ -3077,6 +3077,10 @@ int r100_set_surface_reg(struct radeon_device *rdev, int reg, flags |= RADEON_SURF_TILE_COLOR_BOTH; if (tiling_flags & RADEON_TILING_MACRO) flags |= RADEON_SURF_TILE_COLOR_MACRO; + /* setting pitch to 0 disables tiling */ + if ((tiling_flags & (RADEON_TILING_MACRO|RADEON_TILING_MICRO)) + == 0) + pitch = 0; } else if (rdev->family <= CHIP_RV280) { if (tiling_flags & (RADEON_TILING_MACRO)) flags |= R200_SURF_TILE_COLOR_MACRO; @@ -3094,13 +3098,6 @@ int r100_set_surface_reg(struct radeon_device *rdev, int reg, if (tiling_flags & RADEON_TILING_SWAP_32BIT) flags |= RADEON_SURF_AP0_SWP_32BPP | RADEON_SURF_AP1_SWP_32BPP; - /* when we aren't tiling the pitch seems to needs to be furtherdivided down. - tested on power5 + rn50 server */ - if (tiling_flags & (RADEON_TILING_SWAP_16BIT | RADEON_TILING_SWAP_32BIT)) { - if (!(tiling_flags & (RADEON_TILING_MACRO | RADEON_TILING_MICRO))) - if (ASIC_IS_RN50(rdev)) - pitch /= 16; - } - /* r100/r200 divide by 16 */ if (rdev->family < CHIP_R300) flags |= pitch / 16; -- cgit v1.2.3 From 70d01a5ee29fcb23a6b5948227b1aee212922ade Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Tue, 2 Jul 2013 18:38:02 -0400 Subject: drm/radeon/dpm: add infrastructure to force performance levels This allows you to force specific power levels within a power state. Due to hardware restrictions between generations, the interface is limited to the following 3 selections: auto: all levels enabled low: forced to the lowest power level high: forced to the highest power level Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/radeon.h | 10 ++++++++ drivers/gpu/drm/radeon/radeon_pm.c | 52 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 08cececb376d..b4681313646c 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -1335,6 +1335,12 @@ enum radeon_pcie_gen { RADEON_PCIE_GEN_INVALID = 0xffff }; +enum radeon_dpm_forced_level { + RADEON_DPM_FORCED_LEVEL_AUTO = 0, + RADEON_DPM_FORCED_LEVEL_LOW = 1, + RADEON_DPM_FORCED_LEVEL_HIGH = 2, +}; + struct radeon_dpm { struct radeon_ps *ps; /* number of valid power states */ @@ -1374,6 +1380,8 @@ struct radeon_dpm { bool uvd_active; /* thermal handling */ struct radeon_dpm_thermal thermal; + /* forced levels */ + enum radeon_dpm_forced_level forced_level; }; void radeon_dpm_enable_power_state(struct radeon_device *rdev, @@ -1669,6 +1677,7 @@ struct radeon_asic { u32 (*get_mclk)(struct radeon_device *rdev, bool low); void (*print_power_state)(struct radeon_device *rdev, struct radeon_ps *ps); void (*debugfs_print_current_performance_level)(struct radeon_device *rdev, struct seq_file *m); + int (*force_performance_level)(struct radeon_device *rdev, enum radeon_dpm_forced_level level); } dpm; /* pageflipping */ struct { @@ -2436,6 +2445,7 @@ void radeon_ring_write(struct radeon_ring *ring, uint32_t v); #define radeon_dpm_get_mclk(rdev, l) rdev->asic->dpm.get_mclk((rdev), (l)) #define radeon_dpm_print_power_state(rdev, ps) rdev->asic->dpm.print_power_state((rdev), (ps)) #define radeon_dpm_debugfs_print_current_performance_level(rdev, m) rdev->asic->dpm.debugfs_print_current_performance_level((rdev), (m)) +#define radeon_dpm_force_performance_level(rdev, l) rdev->asic->dpm.force_performance_level((rdev), (l)) /* Common functions */ /* AGP */ diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index aaafb931922f..2557e8bab5cc 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -468,9 +468,57 @@ fail: return count; } +static ssize_t radeon_get_dpm_forced_performance_level(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev)); + struct radeon_device *rdev = ddev->dev_private; + enum radeon_dpm_forced_level level = rdev->pm.dpm.forced_level; + + return snprintf(buf, PAGE_SIZE, "%s\n", + (level == RADEON_DPM_FORCED_LEVEL_AUTO) ? "auto" : + (level == RADEON_DPM_FORCED_LEVEL_LOW) ? "low" : "high"); +} + +static ssize_t radeon_set_dpm_forced_performance_level(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev)); + struct radeon_device *rdev = ddev->dev_private; + enum radeon_dpm_forced_level level; + int ret = 0; + + mutex_lock(&rdev->pm.mutex); + if (strncmp("low", buf, strlen("low")) == 0) { + level = RADEON_DPM_FORCED_LEVEL_LOW; + } else if (strncmp("high", buf, strlen("high")) == 0) { + level = RADEON_DPM_FORCED_LEVEL_HIGH; + } else if (strncmp("auto", buf, strlen("auto")) == 0) { + level = RADEON_DPM_FORCED_LEVEL_AUTO; + } else { + mutex_unlock(&rdev->pm.mutex); + count = -EINVAL; + goto fail; + } + if (rdev->asic->dpm.force_performance_level) { + ret = radeon_dpm_force_performance_level(rdev, level); + if (ret) + count = -EINVAL; + } + mutex_unlock(&rdev->pm.mutex); +fail: + return count; +} + static DEVICE_ATTR(power_profile, S_IRUGO | S_IWUSR, radeon_get_pm_profile, radeon_set_pm_profile); static DEVICE_ATTR(power_method, S_IRUGO | S_IWUSR, radeon_get_pm_method, radeon_set_pm_method); static DEVICE_ATTR(power_dpm_state, S_IRUGO | S_IWUSR, radeon_get_dpm_state, radeon_set_dpm_state); +static DEVICE_ATTR(power_dpm_force_performance_level, S_IRUGO | S_IWUSR, + radeon_get_dpm_forced_performance_level, + radeon_set_dpm_forced_performance_level); static ssize_t radeon_hwmon_show_temp(struct device *dev, struct device_attribute *attr, @@ -1064,6 +1112,9 @@ static int radeon_pm_init_dpm(struct radeon_device *rdev) if (rdev->pm.num_power_states > 1) { ret = device_create_file(rdev->dev, &dev_attr_power_dpm_state); + if (ret) + DRM_ERROR("failed to create device file for dpm state\n"); + ret = device_create_file(rdev->dev, &dev_attr_power_dpm_force_performance_level); if (ret) DRM_ERROR("failed to create device file for dpm state\n"); /* XXX: these are noops for dpm but are here for backwards compat */ @@ -1170,6 +1221,7 @@ static void radeon_pm_fini_dpm(struct radeon_device *rdev) mutex_unlock(&rdev->pm.mutex); device_remove_file(rdev->dev, &dev_attr_power_dpm_state); + device_remove_file(rdev->dev, &dev_attr_power_dpm_force_performance_level); /* XXX backwards compat */ device_remove_file(rdev->dev, &dev_attr_power_profile); device_remove_file(rdev->dev, &dev_attr_power_method); -- cgit v1.2.3 From 8b5e6b7f0ec81f237d87cf9632309db9481c6fb5 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Tue, 2 Jul 2013 18:40:35 -0400 Subject: drm/radeon/dpm: implement force performance levels for 7xx/eg/btc Allows you to limit the selected power levels via sysfs. Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/btc_dpm.c | 4 ++-- drivers/gpu/drm/radeon/cypress_dpm.c | 4 ++-- drivers/gpu/drm/radeon/ppsmc.h | 2 ++ drivers/gpu/drm/radeon/radeon_asic.c | 3 +++ drivers/gpu/drm/radeon/radeon_asic.h | 2 ++ drivers/gpu/drm/radeon/rv770_dpm.c | 31 ++++++++++++++++++++++++------- drivers/gpu/drm/radeon/rv770_dpm.h | 3 ++- 7 files changed, 37 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/radeon/btc_dpm.c b/drivers/gpu/drm/radeon/btc_dpm.c index f072660c7665..91567448c55d 100644 --- a/drivers/gpu/drm/radeon/btc_dpm.c +++ b/drivers/gpu/drm/radeon/btc_dpm.c @@ -2326,9 +2326,9 @@ int btc_dpm_set_power_state(struct radeon_device *rdev) return ret; } - ret = rv770_unrestrict_performance_levels_after_switch(rdev); + ret = rv770_dpm_force_performance_level(rdev, RADEON_DPM_FORCED_LEVEL_AUTO); if (ret) { - DRM_ERROR("rv770_unrestrict_performance_levels_after_switch failed\n"); + DRM_ERROR("rv770_dpm_force_performance_level failed\n"); return ret; } diff --git a/drivers/gpu/drm/radeon/cypress_dpm.c b/drivers/gpu/drm/radeon/cypress_dpm.c index 5ada922e5cec..9ef840807dd1 100644 --- a/drivers/gpu/drm/radeon/cypress_dpm.c +++ b/drivers/gpu/drm/radeon/cypress_dpm.c @@ -2014,9 +2014,9 @@ int cypress_dpm_set_power_state(struct radeon_device *rdev) if (eg_pi->pcie_performance_request) cypress_notify_link_speed_change_after_state_change(rdev, new_ps, old_ps); - ret = rv770_unrestrict_performance_levels_after_switch(rdev); + ret = rv770_dpm_force_performance_level(rdev, RADEON_DPM_FORCED_LEVEL_AUTO); if (ret) { - DRM_ERROR("rv770_unrestrict_performance_levels_after_switch failed\n"); + DRM_ERROR("rv770_dpm_force_performance_level failed\n"); return ret; } diff --git a/drivers/gpu/drm/radeon/ppsmc.h b/drivers/gpu/drm/radeon/ppsmc.h index 8fb1113a8fd7..fc71ca6b04ca 100644 --- a/drivers/gpu/drm/radeon/ppsmc.h +++ b/drivers/gpu/drm/radeon/ppsmc.h @@ -71,6 +71,8 @@ typedef uint8_t PPSMC_Result; #define PPSMC_MSG_SwitchToSwState ((uint8_t)0x20) #define PPSMC_MSG_SwitchToInitialState ((uint8_t)0x40) #define PPSMC_MSG_NoForcedLevel ((uint8_t)0x41) +#define PPSMC_MSG_ForceHigh ((uint8_t)0x42) +#define PPSMC_MSG_ForceMediumOrHigh ((uint8_t)0x43) #define PPSMC_MSG_SwitchToMinimumPower ((uint8_t)0x51) #define PPSMC_MSG_ResumeFromMinimumPower ((uint8_t)0x52) #define PPSMC_MSG_EnableCac ((uint8_t)0x53) diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index a5b244dc50ca..221a0b31e649 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -1393,6 +1393,7 @@ static struct radeon_asic rv770_asic = { .get_mclk = &rv770_dpm_get_mclk, .print_power_state = &rv770_dpm_print_power_state, .debugfs_print_current_performance_level = &rv770_dpm_debugfs_print_current_performance_level, + .force_performance_level = &rv770_dpm_force_performance_level, }, .pflip = { .pre_page_flip = &rs600_pre_page_flip, @@ -1516,6 +1517,7 @@ static struct radeon_asic evergreen_asic = { .get_mclk = &rv770_dpm_get_mclk, .print_power_state = &rv770_dpm_print_power_state, .debugfs_print_current_performance_level = &rv770_dpm_debugfs_print_current_performance_level, + .force_performance_level = &rv770_dpm_force_performance_level, }, .pflip = { .pre_page_flip = &evergreen_pre_page_flip, @@ -1762,6 +1764,7 @@ static struct radeon_asic btc_asic = { .get_mclk = &btc_dpm_get_mclk, .print_power_state = &rv770_dpm_print_power_state, .debugfs_print_current_performance_level = &rv770_dpm_debugfs_print_current_performance_level, + .force_performance_level = &rv770_dpm_force_performance_level, }, .pflip = { .pre_page_flip = &evergreen_pre_page_flip, diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 6822c7aeacaa..a053dc1e7866 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -478,6 +478,8 @@ void rv770_dpm_print_power_state(struct radeon_device *rdev, struct radeon_ps *ps); void rv770_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev, struct seq_file *m); +int rv770_dpm_force_performance_level(struct radeon_device *rdev, + enum radeon_dpm_forced_level level); /* * evergreen diff --git a/drivers/gpu/drm/radeon/rv770_dpm.c b/drivers/gpu/drm/radeon/rv770_dpm.c index 9af464d48eaa..c3025de4f4e2 100644 --- a/drivers/gpu/drm/radeon/rv770_dpm.c +++ b/drivers/gpu/drm/radeon/rv770_dpm.c @@ -1471,14 +1471,30 @@ int rv770_restrict_performance_levels_before_switch(struct radeon_device *rdev) return 0; } -int rv770_unrestrict_performance_levels_after_switch(struct radeon_device *rdev) -{ - if (rv770_send_msg_to_smc(rdev, (PPSMC_Msg)(PPSMC_MSG_NoForcedLevel)) != PPSMC_Result_OK) - return -EINVAL; +int rv770_dpm_force_performance_level(struct radeon_device *rdev, + enum radeon_dpm_forced_level level) +{ + PPSMC_Msg msg; + + if (level == RADEON_DPM_FORCED_LEVEL_HIGH) { + if (rv770_send_msg_to_smc(rdev, PPSMC_MSG_ZeroLevelsDisabled) != PPSMC_Result_OK) + return -EINVAL; + msg = PPSMC_MSG_ForceHigh; + } else if (level == RADEON_DPM_FORCED_LEVEL_LOW) { + if (rv770_send_msg_to_smc(rdev, PPSMC_MSG_NoForcedLevel) != PPSMC_Result_OK) + return -EINVAL; + msg = (PPSMC_Msg)(PPSMC_MSG_TwoLevelsDisabled); + } else { + if (rv770_send_msg_to_smc(rdev, PPSMC_MSG_NoForcedLevel) != PPSMC_Result_OK) + return -EINVAL; + msg = (PPSMC_Msg)(PPSMC_MSG_ZeroLevelsDisabled); + } - if (rv770_send_msg_to_smc(rdev, (PPSMC_Msg)(PPSMC_MSG_ZeroLevelsDisabled)) != PPSMC_Result_OK) + if (rv770_send_msg_to_smc(rdev, msg) != PPSMC_Result_OK) return -EINVAL; + rdev->pm.dpm.forced_level = level; + return 0; } @@ -2047,9 +2063,10 @@ int rv770_dpm_set_power_state(struct radeon_device *rdev) if (pi->dcodt) rv770_program_dcodt_after_state_switch(rdev, new_ps, old_ps); rv770_set_uvd_clock_after_set_eng_clock(rdev, new_ps, old_ps); - ret = rv770_unrestrict_performance_levels_after_switch(rdev); + + ret = rv770_dpm_force_performance_level(rdev, RADEON_DPM_FORCED_LEVEL_AUTO); if (ret) { - DRM_ERROR("rv770_unrestrict_performance_levels_after_switch failed\n"); + DRM_ERROR("rv770_dpm_force_performance_level failed\n"); return ret; } diff --git a/drivers/gpu/drm/radeon/rv770_dpm.h b/drivers/gpu/drm/radeon/rv770_dpm.h index f1e1fcf7f622..96b1b2a62a8a 100644 --- a/drivers/gpu/drm/radeon/rv770_dpm.h +++ b/drivers/gpu/drm/radeon/rv770_dpm.h @@ -262,7 +262,8 @@ void rv770_stop_dpm(struct radeon_device *rdev); void r7xx_stop_smc(struct radeon_device *rdev); void rv770_reset_smio_status(struct radeon_device *rdev); int rv770_restrict_performance_levels_before_switch(struct radeon_device *rdev); -int rv770_unrestrict_performance_levels_after_switch(struct radeon_device *rdev); +int rv770_dpm_force_performance_level(struct radeon_device *rdev, + enum radeon_dpm_forced_level level); int rv770_halt_smc(struct radeon_device *rdev); int rv770_resume_smc(struct radeon_device *rdev); int rv770_set_sw_state(struct radeon_device *rdev); -- cgit v1.2.3 From 170a47f010182152bed6f44f3878dd0423df2b78 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Tue, 2 Jul 2013 18:43:53 -0400 Subject: drm/radeon/dpm: implement force performance level for cayman Allows you to force a performance level via sysfs. Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/ni_dpm.c | 38 +++++++++++++++++++++++++++++------- drivers/gpu/drm/radeon/radeon_asic.c | 1 + drivers/gpu/drm/radeon/radeon_asic.h | 2 ++ 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/radeon/ni_dpm.c b/drivers/gpu/drm/radeon/ni_dpm.c index a4cb99c2da85..bd96eaa3f015 100644 --- a/drivers/gpu/drm/radeon/ni_dpm.c +++ b/drivers/gpu/drm/radeon/ni_dpm.c @@ -1037,13 +1037,37 @@ static int ni_restrict_performance_levels_before_switch(struct radeon_device *rd 0 : -EINVAL; } -static int ni_unrestrict_performance_levels_after_switch(struct radeon_device *rdev) +int ni_dpm_force_performance_level(struct radeon_device *rdev, + enum radeon_dpm_forced_level level) { - if (ni_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetForcedLevels, 0) != PPSMC_Result_OK) - return -EINVAL; + struct radeon_ps *rps = rdev->pm.dpm.current_ps; + struct ni_ps *ps = ni_get_ps(rps); + u32 levels; - return (ni_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetEnabledLevels, 0) == PPSMC_Result_OK) ? - 0 : -EINVAL; + if (level == RADEON_DPM_FORCED_LEVEL_HIGH) { + if (ni_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetEnabledLevels, 0) != PPSMC_Result_OK) + return -EINVAL; + + if (ni_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetForcedLevels, 1) != PPSMC_Result_OK) + return -EINVAL; + } else if (level == RADEON_DPM_FORCED_LEVEL_LOW) { + if (ni_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetForcedLevels, 0) != PPSMC_Result_OK) + return -EINVAL; + + levels = ps->performance_level_count - 1; + if (ni_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetEnabledLevels, levels) != PPSMC_Result_OK) + return -EINVAL; + } else if (level == RADEON_DPM_FORCED_LEVEL_AUTO) { + if (ni_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetForcedLevels, 0) != PPSMC_Result_OK) + return -EINVAL; + + if (ni_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetEnabledLevels, 0) != PPSMC_Result_OK) + return -EINVAL; + } + + rdev->pm.dpm.forced_level = level; + + return 0; } static void ni_stop_smc(struct radeon_device *rdev) @@ -3831,9 +3855,9 @@ int ni_dpm_set_power_state(struct radeon_device *rdev) return ret; } - ret = ni_unrestrict_performance_levels_after_switch(rdev); + ret = ni_dpm_force_performance_level(rdev, RADEON_DPM_FORCED_LEVEL_AUTO); if (ret) { - DRM_ERROR("ni_unrestrict_performance_levels_after_switch failed\n"); + DRM_ERROR("ni_dpm_force_performance_level failed\n"); return ret; } diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index 221a0b31e649..9c1b7b1c0268 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -1940,6 +1940,7 @@ static struct radeon_asic cayman_asic = { .get_mclk = &ni_dpm_get_mclk, .print_power_state = &ni_dpm_print_power_state, .debugfs_print_current_performance_level = &ni_dpm_debugfs_print_current_performance_level, + .force_performance_level = &ni_dpm_force_performance_level, }, .pflip = { .pre_page_flip = &evergreen_pre_page_flip, diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index a053dc1e7866..2bacc777d549 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -617,6 +617,8 @@ void ni_dpm_print_power_state(struct radeon_device *rdev, struct radeon_ps *ps); void ni_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev, struct seq_file *m); +int ni_dpm_force_performance_level(struct radeon_device *rdev, + enum radeon_dpm_forced_level level); int trinity_dpm_init(struct radeon_device *rdev); int trinity_dpm_enable(struct radeon_device *rdev); void trinity_dpm_disable(struct radeon_device *rdev); -- cgit v1.2.3 From a160a6a3367611351624fca06838000b02aae2c7 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Tue, 2 Jul 2013 18:46:28 -0400 Subject: drm/radeon/dpm: implement force performance level for SI Allows you to force the selected performance level via sysfs. Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/radeon_asic.c | 1 + drivers/gpu/drm/radeon/radeon_asic.h | 2 ++ drivers/gpu/drm/radeon/si_dpm.c | 42 ++++++++++++++++++++++++++++-------- 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index 9c1b7b1c0268..6ec4831fdb58 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -2287,6 +2287,7 @@ static struct radeon_asic si_asic = { .get_mclk = &ni_dpm_get_mclk, .print_power_state = &ni_dpm_print_power_state, .debugfs_print_current_performance_level = &si_dpm_debugfs_print_current_performance_level, + .force_performance_level = &si_dpm_force_performance_level, }, .pflip = { .pre_page_flip = &evergreen_pre_page_flip, diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 2bacc777d549..7efa51a13b3c 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -683,6 +683,8 @@ void si_dpm_fini(struct radeon_device *rdev); void si_dpm_display_configuration_changed(struct radeon_device *rdev); void si_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev, struct seq_file *m); +int si_dpm_force_performance_level(struct radeon_device *rdev, + enum radeon_dpm_forced_level level); /* DCE8 - CIK */ void dce8_bandwidth_update(struct radeon_device *rdev); diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c index a7e97cd05e96..700a167be369 100644 --- a/drivers/gpu/drm/radeon/si_dpm.c +++ b/drivers/gpu/drm/radeon/si_dpm.c @@ -3231,16 +3231,38 @@ static int si_restrict_performance_levels_before_switch(struct radeon_device *rd 0 : -EINVAL; } -#if 0 -static int si_unrestrict_performance_levels_after_switch(struct radeon_device *rdev) +int si_dpm_force_performance_level(struct radeon_device *rdev, + enum radeon_dpm_forced_level level) { - if (si_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetForcedLevels, 0) != PPSMC_Result_OK) - return -EINVAL; + struct radeon_ps *rps = rdev->pm.dpm.current_ps; + struct ni_ps *ps = ni_get_ps(rps); + u32 levels; - return (si_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetEnabledLevels, 0) == PPSMC_Result_OK) ? - 0 : -EINVAL; + if (level == RADEON_DPM_FORCED_LEVEL_HIGH) { + if (si_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetEnabledLevels, 0) != PPSMC_Result_OK) + return -EINVAL; + + if (si_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetForcedLevels, 1) != PPSMC_Result_OK) + return -EINVAL; + } else if (level == RADEON_DPM_FORCED_LEVEL_LOW) { + if (si_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetForcedLevels, 0) != PPSMC_Result_OK) + return -EINVAL; + + levels = ps->performance_level_count - 1; + if (si_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetEnabledLevels, levels) != PPSMC_Result_OK) + return -EINVAL; + } else if (level == RADEON_DPM_FORCED_LEVEL_AUTO) { + if (si_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetForcedLevels, 0) != PPSMC_Result_OK) + return -EINVAL; + + if (si_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetEnabledLevels, 0) != PPSMC_Result_OK) + return -EINVAL; + } + + rdev->pm.dpm.forced_level = level; + + return 0; } -#endif static int si_set_boot_state(struct radeon_device *rdev) { @@ -5992,11 +6014,13 @@ int si_dpm_set_power_state(struct radeon_device *rdev) #if 0 /* XXX */ - ret = si_unrestrict_performance_levels_after_switch(rdev); + ret = si_dpm_force_performance_level(rdev, RADEON_DPM_FORCED_LEVEL_AUTO); if (ret) { - DRM_ERROR("si_unrestrict_performance_levels_after_switch failed\n"); + DRM_ERROR("si_dpm_force_performance_level failed\n"); return ret; } +#else + rdev->pm.dpm.forced_level = RADEON_DPM_FORCED_LEVEL_AUTO; #endif return 0; -- cgit v1.2.3 From 5d5e559193afd516daae9816e892bf2d97be0988 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Tue, 2 Jul 2013 18:50:09 -0400 Subject: drm/radeon/dpm: implement force performance level for ON/LN Allows you to force the selected performance level via sysfs. Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/radeon_asic.c | 1 + drivers/gpu/drm/radeon/radeon_asic.h | 2 ++ drivers/gpu/drm/radeon/sumo_dpm.c | 44 ++++++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index 6ec4831fdb58..5d592aed1ee9 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -1641,6 +1641,7 @@ static struct radeon_asic sumo_asic = { .get_mclk = &sumo_dpm_get_mclk, .print_power_state = &sumo_dpm_print_power_state, .debugfs_print_current_performance_level = &sumo_dpm_debugfs_print_current_performance_level, + .force_performance_level = &sumo_dpm_force_performance_level, }, .pflip = { .pre_page_flip = &evergreen_pre_page_flip, diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 7efa51a13b3c..1a89b3af04f3 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -569,6 +569,8 @@ void sumo_dpm_print_power_state(struct radeon_device *rdev, struct radeon_ps *ps); void sumo_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev, struct seq_file *m); +int sumo_dpm_force_performance_level(struct radeon_device *rdev, + enum radeon_dpm_forced_level level); /* * cayman diff --git a/drivers/gpu/drm/radeon/sumo_dpm.c b/drivers/gpu/drm/radeon/sumo_dpm.c index dc599060a9a4..11b6b9924f1b 100644 --- a/drivers/gpu/drm/radeon/sumo_dpm.c +++ b/drivers/gpu/drm/radeon/sumo_dpm.c @@ -1319,6 +1319,8 @@ int sumo_dpm_set_power_state(struct radeon_device *rdev) if (pi->enable_dpm) sumo_set_uvd_clock_after_set_eng_clock(rdev, new_ps, old_ps); + rdev->pm.dpm.forced_level = RADEON_DPM_FORCED_LEVEL_AUTO; + return 0; } @@ -1830,3 +1832,45 @@ u32 sumo_dpm_get_mclk(struct radeon_device *rdev, bool low) return pi->sys_info.bootup_uma_clk; } + +int sumo_dpm_force_performance_level(struct radeon_device *rdev, + enum radeon_dpm_forced_level level) +{ + struct sumo_power_info *pi = sumo_get_pi(rdev); + struct radeon_ps *rps = &pi->current_rps; + struct sumo_ps *ps = sumo_get_ps(rps); + int i; + + if (ps->num_levels <= 1) + return 0; + + if (level == RADEON_DPM_FORCED_LEVEL_HIGH) { + sumo_power_level_enable(rdev, ps->num_levels - 1, true); + sumo_set_forced_level(rdev, ps->num_levels - 1); + sumo_set_forced_mode_enabled(rdev); + for (i = 0; i < ps->num_levels - 1; i++) { + sumo_power_level_enable(rdev, i, false); + } + sumo_set_forced_mode(rdev, false); + sumo_set_forced_mode_enabled(rdev); + sumo_set_forced_mode(rdev, false); + } else if (level == RADEON_DPM_FORCED_LEVEL_LOW) { + sumo_power_level_enable(rdev, 0, true); + sumo_set_forced_level(rdev, 0); + sumo_set_forced_mode_enabled(rdev); + for (i = 1; i < ps->num_levels; i++) { + sumo_power_level_enable(rdev, i, false); + } + sumo_set_forced_mode(rdev, false); + sumo_set_forced_mode_enabled(rdev); + sumo_set_forced_mode(rdev, false); + } else { + for (i = 0; i < ps->num_levels; i++) { + sumo_power_level_enable(rdev, i, true); + } + } + + rdev->pm.dpm.forced_level = level; + + return 0; +} -- cgit v1.2.3 From 9b5de59629d2e58eab41e2f0e5cc60b3c395f1c3 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Tue, 2 Jul 2013 18:52:10 -0400 Subject: drm/radeon/dpm: implement force performance level for TN Allows you to force the selected performance level via sysfs. Signed-off-by: Alex Deucher --- drivers/gpu/drm/radeon/ppsmc.h | 1 + drivers/gpu/drm/radeon/radeon_asic.c | 1 + drivers/gpu/drm/radeon/radeon_asic.h | 2 ++ drivers/gpu/drm/radeon/trinity_dpm.c | 32 ++++++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/trinity_dpm.h | 1 + drivers/gpu/drm/radeon/trinity_smc.c | 7 +++++++ 6 files changed, 44 insertions(+) diff --git a/drivers/gpu/drm/radeon/ppsmc.h b/drivers/gpu/drm/radeon/ppsmc.h index fc71ca6b04ca..b5564a3645d2 100644 --- a/drivers/gpu/drm/radeon/ppsmc.h +++ b/drivers/gpu/drm/radeon/ppsmc.h @@ -103,6 +103,7 @@ typedef uint8_t PPSMC_Result; #define PPSMC_MSG_DPM_Config ((uint32_t) 0x102) #define PPSMC_MSG_DPM_ForceState ((uint32_t) 0x104) #define PPSMC_MSG_PG_SIMD_Config ((uint32_t) 0x108) +#define PPSMC_MSG_DPM_N_LevelsDisabled ((uint32_t) 0x112) #define PPSMC_MSG_DCE_RemoveVoltageAdjustment ((uint32_t) 0x11d) #define PPSMC_MSG_DCE_AllowVoltageAdjustment ((uint32_t) 0x11e) #define PPSMC_MSG_UVD_DPM_Config ((uint32_t) 0x124) diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index 5d592aed1ee9..a6906d4ca734 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -2115,6 +2115,7 @@ static struct radeon_asic trinity_asic = { .get_mclk = &trinity_dpm_get_mclk, .print_power_state = &trinity_dpm_print_power_state, .debugfs_print_current_performance_level = &trinity_dpm_debugfs_print_current_performance_level, + .force_performance_level = &trinity_dpm_force_performance_level, }, .pflip = { .pre_page_flip = &evergreen_pre_page_flip, diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 1a89b3af04f3..4456f85a932e 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -636,6 +636,8 @@ void trinity_dpm_print_power_state(struct radeon_device *rdev, struct radeon_ps *ps); void trinity_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev, struct seq_file *m); +int trinity_dpm_force_performance_level(struct radeon_device *rdev, + enum radeon_dpm_forced_level level); /* DCE6 - SI */ void dce6_bandwidth_update(struct radeon_device *rdev); diff --git a/drivers/gpu/drm/radeon/trinity_dpm.c b/drivers/gpu/drm/radeon/trinity_dpm.c index 8a32bcc6bbb5..a1eb5f59939f 100644 --- a/drivers/gpu/drm/radeon/trinity_dpm.c +++ b/drivers/gpu/drm/radeon/trinity_dpm.c @@ -1158,6 +1158,37 @@ static void trinity_setup_nbp_sim(struct radeon_device *rdev, } } +int trinity_dpm_force_performance_level(struct radeon_device *rdev, + enum radeon_dpm_forced_level level) +{ + struct trinity_power_info *pi = trinity_get_pi(rdev); + struct radeon_ps *rps = &pi->current_rps; + struct trinity_ps *ps = trinity_get_ps(rps); + int i, ret; + + if (ps->num_levels <= 1) + return 0; + + if (level == RADEON_DPM_FORCED_LEVEL_HIGH) { + /* not supported by the hw */ + return -EINVAL; + } else if (level == RADEON_DPM_FORCED_LEVEL_LOW) { + ret = trinity_dpm_n_levels_disabled(rdev, ps->num_levels - 1); + if (ret) + return ret; + } else { + for (i = 0; i < ps->num_levels; i++) { + ret = trinity_dpm_n_levels_disabled(rdev, 0); + if (ret) + return ret; + } + } + + rdev->pm.dpm.forced_level = level; + + return 0; +} + int trinity_dpm_pre_set_power_state(struct radeon_device *rdev) { struct trinity_power_info *pi = trinity_get_pi(rdev); @@ -1190,6 +1221,7 @@ int trinity_dpm_set_power_state(struct radeon_device *rdev) trinity_force_level_0(rdev); trinity_unforce_levels(rdev); trinity_set_uvd_clock_after_set_eng_clock(rdev, new_ps, old_ps); + rdev->pm.dpm.forced_level = RADEON_DPM_FORCED_LEVEL_AUTO; } trinity_release_mutex(rdev); diff --git a/drivers/gpu/drm/radeon/trinity_dpm.h b/drivers/gpu/drm/radeon/trinity_dpm.h index c621b843aab5..e82df071f8b3 100644 --- a/drivers/gpu/drm/radeon/trinity_dpm.h +++ b/drivers/gpu/drm/radeon/trinity_dpm.h @@ -121,6 +121,7 @@ struct trinity_power_info { int trinity_dpm_config(struct radeon_device *rdev, bool enable); int trinity_uvd_dpm_config(struct radeon_device *rdev); int trinity_dpm_force_state(struct radeon_device *rdev, u32 n); +int trinity_dpm_n_levels_disabled(struct radeon_device *rdev, u32 n); int trinity_dpm_no_forced_level(struct radeon_device *rdev); int trinity_dce_enable_voltage_adjustment(struct radeon_device *rdev, bool enable); diff --git a/drivers/gpu/drm/radeon/trinity_smc.c b/drivers/gpu/drm/radeon/trinity_smc.c index 85f86a29513c..a42d89f1830c 100644 --- a/drivers/gpu/drm/radeon/trinity_smc.c +++ b/drivers/gpu/drm/radeon/trinity_smc.c @@ -73,6 +73,13 @@ int trinity_dpm_force_state(struct radeon_device *rdev, u32 n) return trinity_notify_message_to_smu(rdev, PPSMC_MSG_DPM_ForceState); } +int trinity_dpm_n_levels_disabled(struct radeon_device *rdev, u32 n) +{ + WREG32_SMC(SMU_SCRATCH0, n); + + return trinity_notify_message_to_smu(rdev, PPSMC_MSG_DPM_N_LevelsDisabled); +} + int trinity_uvd_dpm_config(struct radeon_device *rdev) { return trinity_notify_message_to_smu(rdev, PPSMC_MSG_UVD_DPM_Config); -- cgit v1.2.3 From 73b0cd674ccc64c921e25bd7154f26d342116539 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sat, 6 Jul 2013 10:34:00 +0200 Subject: hrtimer: Remove unused variable Sigh, should have noticed myself. Reported-by: fengguang.wu@intel.com Signed-off-by: Thomas Gleixner --- kernel/hrtimer.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index b9b9420a1297..3a951d8d5770 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -782,8 +782,6 @@ void clock_was_set(void) */ void hrtimers_resume(void) { - struct hrtimer_cpu_base *cpu_base = &__get_cpu_var(hrtimer_bases); - WARN_ONCE(!irqs_disabled(), KERN_INFO "hrtimers_resume() called with IRQs enabled!"); -- cgit v1.2.3 From 3de91f92377f1477c88a58bcad5b5615ffc4aad5 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 6 Jul 2013 23:04:23 +0400 Subject: mode_t whack-a-mole... Signed-off-by: Al Viro --- drivers/staging/lustre/lustre/include/lprocfs_status.h | 6 +++--- drivers/staging/lustre/lustre/obdclass/lprocfs_status.c | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/staging/lustre/lustre/include/lprocfs_status.h b/drivers/staging/lustre/lustre/include/lprocfs_status.h index e770d0260576..55f182205d78 100644 --- a/drivers/staging/lustre/lustre/include/lprocfs_status.h +++ b/drivers/staging/lustre/lustre/include/lprocfs_status.h @@ -53,7 +53,7 @@ struct lprocfs_vars { /** * /proc file mode. */ - mode_t proc_mode; + umode_t proc_mode; }; struct lprocfs_static_vars { @@ -600,11 +600,11 @@ extern int lprocfs_obd_setup(struct obd_device *obd, struct lprocfs_vars *list); extern int lprocfs_obd_cleanup(struct obd_device *obd); extern int lprocfs_seq_create(proc_dir_entry_t *parent, const char *name, - mode_t mode, + umode_t mode, const struct file_operations *seq_fops, void *data); extern int lprocfs_obd_seq_create(struct obd_device *dev, const char *name, - mode_t mode, + umode_t mode, const struct file_operations *seq_fops, void *data); diff --git a/drivers/staging/lustre/lustre/obdclass/lprocfs_status.c b/drivers/staging/lustre/lustre/obdclass/lprocfs_status.c index 3b157f89c300..f7af3d6a4efc 100644 --- a/drivers/staging/lustre/lustre/obdclass/lprocfs_status.c +++ b/drivers/staging/lustre/lustre/obdclass/lprocfs_status.c @@ -73,7 +73,7 @@ proc_dir_entry_t *lprocfs_add_simple(struct proc_dir_entry *root, struct file_operations *fops) { proc_dir_entry_t *proc; - mode_t mode = 0; + umode_t mode = 0; if (root == NULL || name == NULL || fops == NULL) return ERR_PTR(-EINVAL); @@ -140,7 +140,7 @@ int lprocfs_add_vars(struct proc_dir_entry *root, struct lprocfs_vars *list, while (list->name != NULL) { struct proc_dir_entry *proc; - mode_t mode = 0; + umode_t mode = 0; if (list->proc_mode != 0000) { mode = list->proc_mode; @@ -1899,7 +1899,7 @@ EXPORT_SYMBOL(lprocfs_find_named_value); int lprocfs_seq_create(proc_dir_entry_t *parent, const char *name, - mode_t mode, + umode_t mode, const struct file_operations *seq_fops, void *data) { @@ -1919,7 +1919,7 @@ EXPORT_SYMBOL(lprocfs_seq_create); int lprocfs_obd_seq_create(struct obd_device *dev, const char *name, - mode_t mode, + umode_t mode, const struct file_operations *seq_fops, void *data) { -- cgit v1.2.3 From c7e8e8a8f7a70b343ca1e0f90a31e35ab2d16de1 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Fri, 5 Jul 2013 19:36:17 +0800 Subject: bridge: fix some kernel warning in multicast timer Several people reported the warning: "kernel BUG at kernel/timer.c:729!" and the stack trace is: #7 [ffff880214d25c10] mod_timer+501 at ffffffff8106d905 #8 [ffff880214d25c50] br_multicast_del_pg.isra.20+261 at ffffffffa0731d25 [bridge] #9 [ffff880214d25c80] br_multicast_disable_port+88 at ffffffffa0732948 [bridge] #10 [ffff880214d25cb0] br_stp_disable_port+154 at ffffffffa072bcca [bridge] #11 [ffff880214d25ce8] br_device_event+520 at ffffffffa072a4e8 [bridge] #12 [ffff880214d25d18] notifier_call_chain+76 at ffffffff8164aafc #13 [ffff880214d25d50] raw_notifier_call_chain+22 at ffffffff810858f6 #14 [ffff880214d25d60] call_netdevice_notifiers+45 at ffffffff81536aad #15 [ffff880214d25d80] dev_close_many+183 at ffffffff81536d17 #16 [ffff880214d25dc0] rollback_registered_many+168 at ffffffff81537f68 #17 [ffff880214d25de8] rollback_registered+49 at ffffffff81538101 #18 [ffff880214d25e10] unregister_netdevice_queue+72 at ffffffff815390d8 #19 [ffff880214d25e30] __tun_detach+272 at ffffffffa074c2f0 [tun] #20 [ffff880214d25e88] tun_chr_close+45 at ffffffffa074c4bd [tun] #21 [ffff880214d25ea8] __fput+225 at ffffffff8119b1f1 #22 [ffff880214d25ef0] ____fput+14 at ffffffff8119b3fe #23 [ffff880214d25f00] task_work_run+159 at ffffffff8107cf7f #24 [ffff880214d25f30] do_notify_resume+97 at ffffffff810139e1 #25 [ffff880214d25f50] int_signal+18 at ffffffff8164f292 this is due to I forgot to check if mp->timer is armed in br_multicast_del_pg(). This bug is introduced by commit 9f00b2e7cf241fa389733d41b6 (bridge: only expire the mdb entry when query is received). Same for __br_mdb_del(). Tested-by: poma Reported-by: LiYonghua <809674045@qq.com> Reported-by: Robert Hancock Cc: Herbert Xu Cc: Stephen Hemminger Cc: "David S. Miller" Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- net/bridge/br_mdb.c | 2 +- net/bridge/br_multicast.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c index 19942e38fd2d..0daae3ec2355 100644 --- a/net/bridge/br_mdb.c +++ b/net/bridge/br_mdb.c @@ -447,7 +447,7 @@ static int __br_mdb_del(struct net_bridge *br, struct br_mdb_entry *entry) call_rcu_bh(&p->rcu, br_multicast_free_pg); err = 0; - if (!mp->ports && !mp->mglist && + if (!mp->ports && !mp->mglist && mp->timer_armed && netif_running(br->dev)) mod_timer(&mp->timer, jiffies); break; diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 81befac015e1..69af490cce44 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -270,7 +270,7 @@ static void br_multicast_del_pg(struct net_bridge *br, del_timer(&p->timer); call_rcu_bh(&p->rcu, br_multicast_free_pg); - if (!mp->ports && !mp->mglist && + if (!mp->ports && !mp->mglist && mp->timer_armed && netif_running(br->dev)) mod_timer(&mp->timer, jiffies); -- cgit v1.2.3 From 3df8f68aaf7ebe3d136a22262b41b350b0a1858b Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Wed, 26 Jun 2013 02:31:42 -0700 Subject: iser-target: Fix isert_put_reject payload buffer post This patch adds the missing isert_put_reject() logic to post a outgoing payload buffer to hold the 48 bytes of original PDU header request payload for the rejected cmd. It also fixes ISTATE_SEND_REJECT handling in isert_response_completion() -> isert_do_control_comp() code, and drops incorrect iscsi_cmd_t->reject_comp usage. Cc: Or Gerlitz Cc: Mike Christie Cc: stable@vger.kernel.org # 3.10+ Signed-off-by: Nicholas Bellinger --- drivers/infiniband/ulp/isert/ib_isert.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index c48c9481025c..1b38eff7f340 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c @@ -1243,6 +1243,9 @@ isert_put_cmd(struct isert_cmd *isert_cmd) * associated cmd->se_cmd needs to be released. */ if (cmd->se_cmd.se_tfo != NULL) { + pr_debug("Calling transport_generic_free_cmd from" + " isert_put_cmd for 0x%02x\n", + cmd->iscsi_opcode); transport_generic_free_cmd(&cmd->se_cmd, 0); break; } @@ -1339,8 +1342,8 @@ isert_do_control_comp(struct work_struct *work) atomic_dec(&isert_conn->post_send_buf_count); cmd->i_state = ISTATE_SENT_STATUS; - complete(&cmd->reject_comp); isert_completion_put(&isert_cmd->tx_desc, isert_cmd, ib_dev); + break; case ISTATE_SEND_LOGOUTRSP: pr_debug("Calling iscsit_logout_post_handler >>>>>>>>>>>>>>\n"); /* @@ -1366,7 +1369,8 @@ isert_response_completion(struct iser_tx_desc *tx_desc, struct iscsi_cmd *cmd = &isert_cmd->iscsi_cmd; if (cmd->i_state == ISTATE_SEND_TASKMGTRSP || - cmd->i_state == ISTATE_SEND_LOGOUTRSP) { + cmd->i_state == ISTATE_SEND_LOGOUTRSP || + cmd->i_state == ISTATE_SEND_REJECT) { isert_unmap_tx_desc(tx_desc, ib_dev); INIT_WORK(&isert_cmd->comp_work, isert_do_control_comp); @@ -1658,11 +1662,25 @@ isert_put_reject(struct iscsi_cmd *cmd, struct iscsi_conn *conn) struct isert_cmd, iscsi_cmd); struct isert_conn *isert_conn = (struct isert_conn *)conn->context; struct ib_send_wr *send_wr = &isert_cmd->tx_desc.send_wr; + struct ib_device *ib_dev = isert_conn->conn_cm_id->device; + struct ib_sge *tx_dsg = &isert_cmd->tx_desc.tx_sg[1]; + struct iscsi_reject *hdr = + (struct iscsi_reject *)&isert_cmd->tx_desc.iscsi_header; isert_create_send_desc(isert_conn, isert_cmd, &isert_cmd->tx_desc); - iscsit_build_reject(cmd, conn, (struct iscsi_reject *) - &isert_cmd->tx_desc.iscsi_header); + iscsit_build_reject(cmd, conn, hdr); isert_init_tx_hdrs(isert_conn, &isert_cmd->tx_desc); + + hton24(hdr->dlength, ISCSI_HDR_LEN); + isert_cmd->sense_buf_dma = ib_dma_map_single(ib_dev, + (void *)cmd->buf_ptr, ISCSI_HDR_LEN, + DMA_TO_DEVICE); + isert_cmd->sense_buf_len = ISCSI_HDR_LEN; + tx_dsg->addr = isert_cmd->sense_buf_dma; + tx_dsg->length = ISCSI_HDR_LEN; + tx_dsg->lkey = isert_conn->conn_mr->lkey; + isert_cmd->tx_desc.num_sge = 2; + isert_init_send_wr(isert_cmd, send_wr); pr_debug("Posting Reject IB_WR_SEND >>>>>>>>>>>>>>>>>>>>>>\n"); -- cgit v1.2.3 From ba159914086f06532079fc15141f46ffe7e04a41 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Wed, 3 Jul 2013 03:48:24 -0700 Subject: iscsi-target: Fix iscsit_add_reject* usage for iser This patch changes iscsit_add_reject() + iscsit_add_reject_from_cmd() usage to not sleep on iscsi_cmd->reject_comp to address a free-after-use usage bug in v3.10 with iser-target code. It saves ->reject_reason for use within iscsit_build_reject() so the correct value for both transport cases. It also drops the legacy fail_conn parameter usage throughput iscsi-target code and adds two iscsit_add_reject_cmd() and iscsit_reject_cmd helper functions, along with various small cleanups. (v2: Re-enable target_put_sess_cmd() to be called from iscsit_add_reject_from_cmd() for rejects invoked after target_get_sess_cmd() has been called) Cc: Or Gerlitz Cc: Mike Christie Cc: stable@vger.kernel.org # 3.10+ Signed-off-by: Nicholas Bellinger --- drivers/infiniband/ulp/isert/ib_isert.c | 5 - drivers/target/iscsi/iscsi_target.c | 255 +++++++++++++------------------ drivers/target/iscsi/iscsi_target.h | 2 +- drivers/target/iscsi/iscsi_target_core.h | 8 +- drivers/target/iscsi/iscsi_target_erl0.c | 7 +- drivers/target/iscsi/iscsi_target_erl1.c | 20 ++- drivers/target/iscsi/iscsi_target_util.c | 1 - include/target/iscsi/iscsi_transport.h | 2 - 8 files changed, 120 insertions(+), 180 deletions(-) diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index 1b38eff7f340..134ff58c6b17 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c @@ -939,11 +939,6 @@ sequence_cmd: if (!rc && dump_payload == false && unsol_data) iscsit_set_unsoliticed_dataout(cmd); - if (rc == CMDSN_ERROR_CANNOT_RECOVER) - return iscsit_add_reject_from_cmd( - ISCSI_REASON_PROTOCOL_ERROR, - 1, 0, (unsigned char *)hdr, cmd); - return 0; } diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 19a31f9bb7d5..2a25bd3b065c 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -628,25 +628,18 @@ static void __exit iscsi_target_cleanup_module(void) } static int iscsit_add_reject( + struct iscsi_conn *conn, u8 reason, - int fail_conn, - unsigned char *buf, - struct iscsi_conn *conn) + unsigned char *buf) { struct iscsi_cmd *cmd; - struct iscsi_reject *hdr; - int ret; cmd = iscsit_allocate_cmd(conn, GFP_KERNEL); if (!cmd) return -1; cmd->iscsi_opcode = ISCSI_OP_REJECT; - if (fail_conn) - cmd->cmd_flags |= ICF_REJECT_FAIL_CONN; - - hdr = (struct iscsi_reject *) cmd->pdu; - hdr->reason = reason; + cmd->reject_reason = reason; cmd->buf_ptr = kmemdup(buf, ISCSI_HDR_LEN, GFP_KERNEL); if (!cmd->buf_ptr) { @@ -662,23 +655,16 @@ static int iscsit_add_reject( cmd->i_state = ISTATE_SEND_REJECT; iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state); - ret = wait_for_completion_interruptible(&cmd->reject_comp); - if (ret != 0) - return -1; - - return (!fail_conn) ? 0 : -1; + return -1; } -int iscsit_add_reject_from_cmd( +static int iscsit_add_reject_from_cmd( + struct iscsi_cmd *cmd, u8 reason, - int fail_conn, - int add_to_conn, - unsigned char *buf, - struct iscsi_cmd *cmd) + bool add_to_conn, + unsigned char *buf) { struct iscsi_conn *conn; - struct iscsi_reject *hdr; - int ret; if (!cmd->conn) { pr_err("cmd->conn is NULL for ITT: 0x%08x\n", @@ -688,11 +674,7 @@ int iscsit_add_reject_from_cmd( conn = cmd->conn; cmd->iscsi_opcode = ISCSI_OP_REJECT; - if (fail_conn) - cmd->cmd_flags |= ICF_REJECT_FAIL_CONN; - - hdr = (struct iscsi_reject *) cmd->pdu; - hdr->reason = reason; + cmd->reject_reason = reason; cmd->buf_ptr = kmemdup(buf, ISCSI_HDR_LEN, GFP_KERNEL); if (!cmd->buf_ptr) { @@ -709,8 +691,6 @@ int iscsit_add_reject_from_cmd( cmd->i_state = ISTATE_SEND_REJECT; iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state); - - ret = wait_for_completion_interruptible(&cmd->reject_comp); /* * Perform the kref_put now if se_cmd has already been setup by * scsit_setup_scsi_cmd() @@ -719,12 +699,19 @@ int iscsit_add_reject_from_cmd( pr_debug("iscsi reject: calling target_put_sess_cmd >>>>>>\n"); target_put_sess_cmd(conn->sess->se_sess, &cmd->se_cmd); } - if (ret != 0) - return -1; + return -1; +} - return (!fail_conn) ? 0 : -1; +static int iscsit_add_reject_cmd(struct iscsi_cmd *cmd, u8 reason, + unsigned char *buf) +{ + return iscsit_add_reject_from_cmd(cmd, reason, true, buf); +} + +int iscsit_reject_cmd(struct iscsi_cmd *cmd, u8 reason, unsigned char *buf) +{ + return iscsit_add_reject_from_cmd(cmd, reason, false, buf); } -EXPORT_SYMBOL(iscsit_add_reject_from_cmd); /* * Map some portion of the allocated scatterlist to an iovec, suitable for @@ -844,8 +831,8 @@ int iscsit_setup_scsi_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, !(hdr->flags & ISCSI_FLAG_CMD_FINAL)) { pr_err("ISCSI_FLAG_CMD_WRITE & ISCSI_FLAG_CMD_FINAL" " not set. Bad iSCSI Initiator.\n"); - return iscsit_add_reject_from_cmd(ISCSI_REASON_BOOKMARK_INVALID, - 1, 1, buf, cmd); + return iscsit_add_reject_cmd(cmd, + ISCSI_REASON_BOOKMARK_INVALID, buf); } if (((hdr->flags & ISCSI_FLAG_CMD_READ) || @@ -865,8 +852,8 @@ int iscsit_setup_scsi_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, pr_err("ISCSI_FLAG_CMD_READ or ISCSI_FLAG_CMD_WRITE" " set when Expected Data Transfer Length is 0 for" " CDB: 0x%02x. Bad iSCSI Initiator.\n", hdr->cdb[0]); - return iscsit_add_reject_from_cmd(ISCSI_REASON_BOOKMARK_INVALID, - 1, 1, buf, cmd); + return iscsit_add_reject_cmd(cmd, + ISCSI_REASON_BOOKMARK_INVALID, buf); } done: @@ -875,62 +862,62 @@ done: pr_err("ISCSI_FLAG_CMD_READ and/or ISCSI_FLAG_CMD_WRITE" " MUST be set if Expected Data Transfer Length is not 0." " Bad iSCSI Initiator\n"); - return iscsit_add_reject_from_cmd(ISCSI_REASON_BOOKMARK_INVALID, - 1, 1, buf, cmd); + return iscsit_add_reject_cmd(cmd, + ISCSI_REASON_BOOKMARK_INVALID, buf); } if ((hdr->flags & ISCSI_FLAG_CMD_READ) && (hdr->flags & ISCSI_FLAG_CMD_WRITE)) { pr_err("Bidirectional operations not supported!\n"); - return iscsit_add_reject_from_cmd(ISCSI_REASON_BOOKMARK_INVALID, - 1, 1, buf, cmd); + return iscsit_add_reject_cmd(cmd, + ISCSI_REASON_BOOKMARK_INVALID, buf); } if (hdr->opcode & ISCSI_OP_IMMEDIATE) { pr_err("Illegally set Immediate Bit in iSCSI Initiator" " Scsi Command PDU.\n"); - return iscsit_add_reject_from_cmd(ISCSI_REASON_BOOKMARK_INVALID, - 1, 1, buf, cmd); + return iscsit_add_reject_cmd(cmd, + ISCSI_REASON_BOOKMARK_INVALID, buf); } if (payload_length && !conn->sess->sess_ops->ImmediateData) { pr_err("ImmediateData=No but DataSegmentLength=%u," " protocol error.\n", payload_length); - return iscsit_add_reject_from_cmd(ISCSI_REASON_PROTOCOL_ERROR, - 1, 1, buf, cmd); + return iscsit_add_reject_cmd(cmd, + ISCSI_REASON_PROTOCOL_ERROR, buf); } - if ((be32_to_cpu(hdr->data_length )== payload_length) && + if ((be32_to_cpu(hdr->data_length) == payload_length) && (!(hdr->flags & ISCSI_FLAG_CMD_FINAL))) { pr_err("Expected Data Transfer Length and Length of" " Immediate Data are the same, but ISCSI_FLAG_CMD_FINAL" " bit is not set protocol error\n"); - return iscsit_add_reject_from_cmd(ISCSI_REASON_PROTOCOL_ERROR, - 1, 1, buf, cmd); + return iscsit_add_reject_cmd(cmd, + ISCSI_REASON_PROTOCOL_ERROR, buf); } if (payload_length > be32_to_cpu(hdr->data_length)) { pr_err("DataSegmentLength: %u is greater than" " EDTL: %u, protocol error.\n", payload_length, hdr->data_length); - return iscsit_add_reject_from_cmd(ISCSI_REASON_PROTOCOL_ERROR, - 1, 1, buf, cmd); + return iscsit_add_reject_cmd(cmd, + ISCSI_REASON_PROTOCOL_ERROR, buf); } if (payload_length > conn->conn_ops->MaxXmitDataSegmentLength) { pr_err("DataSegmentLength: %u is greater than" " MaxXmitDataSegmentLength: %u, protocol error.\n", payload_length, conn->conn_ops->MaxXmitDataSegmentLength); - return iscsit_add_reject_from_cmd(ISCSI_REASON_PROTOCOL_ERROR, - 1, 1, buf, cmd); + return iscsit_add_reject_cmd(cmd, + ISCSI_REASON_PROTOCOL_ERROR, buf); } if (payload_length > conn->sess->sess_ops->FirstBurstLength) { pr_err("DataSegmentLength: %u is greater than" " FirstBurstLength: %u, protocol error.\n", payload_length, conn->sess->sess_ops->FirstBurstLength); - return iscsit_add_reject_from_cmd(ISCSI_REASON_BOOKMARK_INVALID, - 1, 1, buf, cmd); + return iscsit_add_reject_cmd(cmd, + ISCSI_REASON_BOOKMARK_INVALID, buf); } data_direction = (hdr->flags & ISCSI_FLAG_CMD_WRITE) ? DMA_TO_DEVICE : @@ -985,9 +972,8 @@ done: dr = iscsit_allocate_datain_req(); if (!dr) - return iscsit_add_reject_from_cmd( - ISCSI_REASON_BOOKMARK_NO_RESOURCES, - 1, 1, buf, cmd); + return iscsit_add_reject_cmd(cmd, + ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf); iscsit_attach_datain_req(cmd, dr); } @@ -1015,18 +1001,16 @@ done: cmd->sense_reason = target_setup_cmd_from_cdb(&cmd->se_cmd, hdr->cdb); if (cmd->sense_reason) { if (cmd->sense_reason == TCM_OUT_OF_RESOURCES) { - return iscsit_add_reject_from_cmd( - ISCSI_REASON_BOOKMARK_NO_RESOURCES, - 1, 1, buf, cmd); + return iscsit_add_reject_cmd(cmd, + ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf); } goto attach_cmd; } if (iscsit_build_pdu_and_seq_lists(cmd, payload_length) < 0) { - return iscsit_add_reject_from_cmd( - ISCSI_REASON_BOOKMARK_NO_RESOURCES, - 1, 1, buf, cmd); + return iscsit_add_reject_cmd(cmd, + ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf); } attach_cmd: @@ -1075,10 +1059,6 @@ int iscsit_process_scsi_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, target_put_sess_cmd(conn->sess->se_sess, &cmd->se_cmd); return 0; - } else if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER) { - return iscsit_add_reject_from_cmd( - ISCSI_REASON_PROTOCOL_ERROR, - 1, 0, (unsigned char *)hdr, cmd); } } @@ -1149,11 +1129,6 @@ after_immediate_data: } else if (cmd->unsolicited_data) iscsit_set_unsoliticed_dataout(cmd); - if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER) - return iscsit_add_reject_from_cmd( - ISCSI_REASON_PROTOCOL_ERROR, - 1, 0, (unsigned char *)hdr, cmd); - } else if (immed_ret == IMMEDIATE_DATA_ERL1_CRC_FAILURE) { /* * Immediate Data failed DataCRC and ERL>=1, @@ -1190,9 +1165,8 @@ iscsit_handle_scsi_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, * traditional iSCSI block I/O. */ if (iscsit_allocate_iovecs(cmd) < 0) { - return iscsit_add_reject_from_cmd( - ISCSI_REASON_BOOKMARK_NO_RESOURCES, - 1, 0, buf, cmd); + return iscsit_add_reject_cmd(cmd, + ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf); } immed_data = cmd->immediate_data; @@ -1282,8 +1256,8 @@ iscsit_check_dataout_hdr(struct iscsi_conn *conn, unsigned char *buf, if (!payload_length) { pr_err("DataOUT payload is ZERO, protocol error.\n"); - return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1, - buf, conn); + return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR, + buf); } /* iSCSI write */ @@ -1300,8 +1274,8 @@ iscsit_check_dataout_hdr(struct iscsi_conn *conn, unsigned char *buf, pr_err("DataSegmentLength: %u is greater than" " MaxXmitDataSegmentLength: %u\n", payload_length, conn->conn_ops->MaxXmitDataSegmentLength); - return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1, - buf, conn); + return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR, + buf); } cmd = iscsit_find_cmd_from_itt_or_dump(conn, hdr->itt, @@ -1324,8 +1298,7 @@ iscsit_check_dataout_hdr(struct iscsi_conn *conn, unsigned char *buf, if (cmd->data_direction != DMA_TO_DEVICE) { pr_err("Command ITT: 0x%08x received DataOUT for a" " NON-WRITE command.\n", cmd->init_task_tag); - return iscsit_add_reject_from_cmd(ISCSI_REASON_PROTOCOL_ERROR, - 1, 0, buf, cmd); + return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR, buf); } se_cmd = &cmd->se_cmd; iscsit_mod_dataout_timer(cmd); @@ -1334,8 +1307,7 @@ iscsit_check_dataout_hdr(struct iscsi_conn *conn, unsigned char *buf, pr_err("DataOut Offset: %u, Length %u greater than" " iSCSI Command EDTL %u, protocol error.\n", hdr->offset, payload_length, cmd->se_cmd.data_length); - return iscsit_add_reject_from_cmd(ISCSI_REASON_BOOKMARK_INVALID, - 1, 0, buf, cmd); + return iscsit_reject_cmd(cmd, ISCSI_REASON_BOOKMARK_INVALID, buf); } if (cmd->unsolicited_data) { @@ -1543,8 +1515,8 @@ int iscsit_setup_nop_out(struct iscsi_conn *conn, struct iscsi_cmd *cmd, if (hdr->itt == RESERVED_ITT && !(hdr->opcode & ISCSI_OP_IMMEDIATE)) { pr_err("NOPOUT ITT is reserved, but Immediate Bit is" " not set, protocol error.\n"); - return iscsit_add_reject_from_cmd(ISCSI_REASON_PROTOCOL_ERROR, - 1, 0, (unsigned char *)hdr, cmd); + return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR, + (unsigned char *)hdr); } if (payload_length > conn->conn_ops->MaxXmitDataSegmentLength) { @@ -1552,8 +1524,8 @@ int iscsit_setup_nop_out(struct iscsi_conn *conn, struct iscsi_cmd *cmd, " greater than MaxXmitDataSegmentLength: %u, protocol" " error.\n", payload_length, conn->conn_ops->MaxXmitDataSegmentLength); - return iscsit_add_reject_from_cmd(ISCSI_REASON_PROTOCOL_ERROR, - 1, 0, (unsigned char *)hdr, cmd); + return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR, + (unsigned char *)hdr); } pr_debug("Got NOPOUT Ping %s ITT: 0x%08x, TTT: 0x%08x," @@ -1612,9 +1584,7 @@ int iscsit_process_nop_out(struct iscsi_conn *conn, struct iscsi_cmd *cmd, return 0; if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER) - return iscsit_add_reject_from_cmd( - ISCSI_REASON_PROTOCOL_ERROR, - 1, 0, (unsigned char *)hdr, cmd); + return -1; return 0; } @@ -1780,8 +1750,8 @@ iscsit_handle_task_mgt_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, pr_err("Task Management Request TASK_REASSIGN not" " issued as immediate command, bad iSCSI Initiator" "implementation\n"); - return iscsit_add_reject_from_cmd(ISCSI_REASON_PROTOCOL_ERROR, - 1, 1, buf, cmd); + return iscsit_add_reject_cmd(cmd, + ISCSI_REASON_PROTOCOL_ERROR, buf); } if ((function != ISCSI_TM_FUNC_ABORT_TASK) && be32_to_cpu(hdr->refcmdsn) != ISCSI_RESERVED_TAG) @@ -1793,9 +1763,9 @@ iscsit_handle_task_mgt_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, if (!cmd->tmr_req) { pr_err("Unable to allocate memory for" " Task Management command!\n"); - return iscsit_add_reject_from_cmd( - ISCSI_REASON_BOOKMARK_NO_RESOURCES, - 1, 1, buf, cmd); + return iscsit_add_reject_cmd(cmd, + ISCSI_REASON_BOOKMARK_NO_RESOURCES, + buf); } /* @@ -1837,17 +1807,15 @@ iscsit_handle_task_mgt_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, default: pr_err("Unknown iSCSI TMR Function:" " 0x%02x\n", function); - return iscsit_add_reject_from_cmd( - ISCSI_REASON_BOOKMARK_NO_RESOURCES, - 1, 1, buf, cmd); + return iscsit_add_reject_cmd(cmd, + ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf); } ret = core_tmr_alloc_req(&cmd->se_cmd, cmd->tmr_req, tcm_function, GFP_KERNEL); if (ret < 0) - return iscsit_add_reject_from_cmd( - ISCSI_REASON_BOOKMARK_NO_RESOURCES, - 1, 1, buf, cmd); + return iscsit_add_reject_cmd(cmd, + ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf); cmd->tmr_req->se_tmr_req = cmd->se_cmd.se_tmr_req; } @@ -1906,9 +1874,8 @@ iscsit_handle_task_mgt_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, break; if (iscsit_check_task_reassign_expdatasn(tmr_req, conn) < 0) - return iscsit_add_reject_from_cmd( - ISCSI_REASON_BOOKMARK_INVALID, 1, 1, - buf, cmd); + return iscsit_add_reject_cmd(cmd, + ISCSI_REASON_BOOKMARK_INVALID, buf); break; default: pr_err("Unknown TMR function: 0x%02x, protocol" @@ -1932,9 +1899,7 @@ attach: else if (cmdsn_ret == CMDSN_LOWER_THAN_EXP) return 0; else if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER) - return iscsit_add_reject_from_cmd( - ISCSI_REASON_PROTOCOL_ERROR, - 1, 0, buf, cmd); + return -1; } iscsit_ack_from_expstatsn(conn, be32_to_cpu(hdr->exp_statsn)); @@ -1970,8 +1935,8 @@ iscsit_setup_text_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, pr_err("Unable to accept text parameter length: %u" "greater than MaxXmitDataSegmentLength %u.\n", payload_length, conn->conn_ops->MaxXmitDataSegmentLength); - return iscsit_add_reject_from_cmd(ISCSI_REASON_PROTOCOL_ERROR, - 1, 0, (unsigned char *)hdr, cmd); + return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR, + (unsigned char *)hdr); } pr_debug("Got Text Request: ITT: 0x%08x, CmdSN: 0x%08x," @@ -2033,17 +1998,16 @@ iscsit_process_text_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, if (!(hdr->opcode & ISCSI_OP_IMMEDIATE)) { cmdsn_ret = iscsit_sequence_cmd(conn, cmd, hdr->cmdsn); if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER) - return iscsit_add_reject_from_cmd( - ISCSI_REASON_PROTOCOL_ERROR, - 1, 0, (unsigned char *)hdr, cmd); + return -1; + return 0; } return iscsit_execute_cmd(cmd, 0); reject: - return iscsit_add_reject_from_cmd(ISCSI_REASON_PROTOCOL_ERROR, - 0, 0, (unsigned char *)hdr, cmd); + return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR, + (unsigned char *)hdr); } EXPORT_SYMBOL(iscsit_process_text_cmd); @@ -2139,8 +2103,7 @@ iscsit_handle_text_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, reject: kfree(cmd->text_in_ptr); cmd->text_in_ptr = NULL; - return iscsit_add_reject_from_cmd(ISCSI_REASON_PROTOCOL_ERROR, - 0, 0, buf, cmd); + return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR, buf); } EXPORT_SYMBOL(iscsit_handle_text_cmd); @@ -2322,13 +2285,10 @@ iscsit_handle_logout_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, return ret; } else { cmdsn_ret = iscsit_sequence_cmd(conn, cmd, hdr->cmdsn); - if (cmdsn_ret == CMDSN_LOWER_THAN_EXP) { + if (cmdsn_ret == CMDSN_LOWER_THAN_EXP) logout_remove = 0; - } else if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER) { - return iscsit_add_reject_from_cmd( - ISCSI_REASON_PROTOCOL_ERROR, - 1, 0, buf, cmd); - } + else if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER) + return -1; } return logout_remove; @@ -2352,8 +2312,8 @@ static int iscsit_handle_snack( if (!conn->sess->sess_ops->ErrorRecoveryLevel) { pr_err("Initiator sent SNACK request while in" " ErrorRecoveryLevel=0.\n"); - return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1, - buf, conn); + return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR, + buf); } /* * SNACK_DATA and SNACK_R2T are both 0, so check which function to @@ -2377,13 +2337,13 @@ static int iscsit_handle_snack( case ISCSI_FLAG_SNACK_TYPE_RDATA: /* FIXME: Support R-Data SNACK */ pr_err("R-Data SNACK Not Supported.\n"); - return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1, - buf, conn); + return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR, + buf); default: pr_err("Unknown SNACK type 0x%02x, protocol" " error.\n", hdr->flags & 0x0f); - return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1, - buf, conn); + return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR, + buf); } return 0; @@ -2455,14 +2415,14 @@ static int iscsit_handle_immediate_data( pr_err("Unable to recover from" " Immediate Data digest failure while" " in ERL=0.\n"); - iscsit_add_reject_from_cmd( + iscsit_reject_cmd(cmd, ISCSI_REASON_DATA_DIGEST_ERROR, - 1, 0, (unsigned char *)hdr, cmd); + (unsigned char *)hdr); return IMMEDIATE_DATA_CANNOT_RECOVER; } else { - iscsit_add_reject_from_cmd( + iscsit_reject_cmd(cmd, ISCSI_REASON_DATA_DIGEST_ERROR, - 0, 0, (unsigned char *)hdr, cmd); + (unsigned char *)hdr); return IMMEDIATE_DATA_ERL1_CRC_FAILURE; } } else { @@ -3595,6 +3555,7 @@ iscsit_build_reject(struct iscsi_cmd *cmd, struct iscsi_conn *conn, struct iscsi_reject *hdr) { hdr->opcode = ISCSI_OP_REJECT; + hdr->reason = cmd->reject_reason; hdr->flags |= ISCSI_FLAG_CMD_FINAL; hton24(hdr->dlength, ISCSI_HDR_LEN); hdr->ffffffff = cpu_to_be32(0xffffffff); @@ -3868,18 +3829,11 @@ check_rsp_state: case ISTATE_SEND_STATUS_RECOVERY: case ISTATE_SEND_TEXTRSP: case ISTATE_SEND_TASKMGTRSP: + case ISTATE_SEND_REJECT: spin_lock_bh(&cmd->istate_lock); cmd->i_state = ISTATE_SENT_STATUS; spin_unlock_bh(&cmd->istate_lock); break; - case ISTATE_SEND_REJECT: - if (cmd->cmd_flags & ICF_REJECT_FAIL_CONN) { - cmd->cmd_flags &= ~ICF_REJECT_FAIL_CONN; - complete(&cmd->reject_comp); - goto err; - } - complete(&cmd->reject_comp); - break; default: pr_err("Unknown Opcode: 0x%02x ITT:" " 0x%08x, i_state: %d on CID: %hu\n", @@ -3984,8 +3938,7 @@ static int iscsi_target_rx_opcode(struct iscsi_conn *conn, unsigned char *buf) case ISCSI_OP_SCSI_CMD: cmd = iscsit_allocate_cmd(conn, GFP_KERNEL); if (!cmd) - return iscsit_add_reject(ISCSI_REASON_BOOKMARK_NO_RESOURCES, - 1, buf, conn); + goto reject; ret = iscsit_handle_scsi_cmd(conn, cmd, buf); break; @@ -3997,32 +3950,28 @@ static int iscsi_target_rx_opcode(struct iscsi_conn *conn, unsigned char *buf) if (hdr->ttt == cpu_to_be32(0xFFFFFFFF)) { cmd = iscsit_allocate_cmd(conn, GFP_KERNEL); if (!cmd) - return iscsit_add_reject(ISCSI_REASON_BOOKMARK_NO_RESOURCES, - 1, buf, conn); + goto reject; } ret = iscsit_handle_nop_out(conn, cmd, buf); break; case ISCSI_OP_SCSI_TMFUNC: cmd = iscsit_allocate_cmd(conn, GFP_KERNEL); if (!cmd) - return iscsit_add_reject(ISCSI_REASON_BOOKMARK_NO_RESOURCES, - 1, buf, conn); + goto reject; ret = iscsit_handle_task_mgt_cmd(conn, cmd, buf); break; case ISCSI_OP_TEXT: cmd = iscsit_allocate_cmd(conn, GFP_KERNEL); if (!cmd) - return iscsit_add_reject(ISCSI_REASON_BOOKMARK_NO_RESOURCES, - 1, buf, conn); + goto reject; ret = iscsit_handle_text_cmd(conn, cmd, buf); break; case ISCSI_OP_LOGOUT: cmd = iscsit_allocate_cmd(conn, GFP_KERNEL); if (!cmd) - return iscsit_add_reject(ISCSI_REASON_BOOKMARK_NO_RESOURCES, - 1, buf, conn); + goto reject; ret = iscsit_handle_logout_cmd(conn, cmd, buf); if (ret > 0) @@ -4054,6 +4003,8 @@ static int iscsi_target_rx_opcode(struct iscsi_conn *conn, unsigned char *buf) } return ret; +reject: + return iscsit_add_reject(conn, ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf); } int iscsi_target_rx_thread(void *arg) @@ -4148,8 +4099,8 @@ restart: (!(opcode & ISCSI_OP_LOGOUT)))) { pr_err("Received illegal iSCSI Opcode: 0x%02x" " while in Discovery Session, rejecting.\n", opcode); - iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1, - buffer, conn); + iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR, + buffer); goto transport_err; } diff --git a/drivers/target/iscsi/iscsi_target.h b/drivers/target/iscsi/iscsi_target.h index a0050b2f294e..2c437cb8ca00 100644 --- a/drivers/target/iscsi/iscsi_target.h +++ b/drivers/target/iscsi/iscsi_target.h @@ -15,7 +15,7 @@ extern struct iscsi_np *iscsit_add_np(struct __kernel_sockaddr_storage *, extern int iscsit_reset_np_thread(struct iscsi_np *, struct iscsi_tpg_np *, struct iscsi_portal_group *); extern int iscsit_del_np(struct iscsi_np *); -extern int iscsit_add_reject_from_cmd(u8, int, int, unsigned char *, struct iscsi_cmd *); +extern int iscsit_reject_cmd(struct iscsi_cmd *cmd, u8, unsigned char *); extern void iscsit_set_unsoliticed_dataout(struct iscsi_cmd *); extern int iscsit_logout_closesession(struct iscsi_cmd *, struct iscsi_conn *); extern int iscsit_logout_closeconnection(struct iscsi_cmd *, struct iscsi_conn *); diff --git a/drivers/target/iscsi/iscsi_target_core.h b/drivers/target/iscsi/iscsi_target_core.h index caa0ad9a778b..4f77a78edef9 100644 --- a/drivers/target/iscsi/iscsi_target_core.h +++ b/drivers/target/iscsi/iscsi_target_core.h @@ -132,9 +132,8 @@ enum cmd_flags_table { ICF_CONTIG_MEMORY = 0x00000020, ICF_ATTACHED_TO_RQUEUE = 0x00000040, ICF_OOO_CMDSN = 0x00000080, - ICF_REJECT_FAIL_CONN = 0x00000100, - IFC_SENDTARGETS_ALL = 0x00000200, - IFC_SENDTARGETS_SINGLE = 0x00000400, + IFC_SENDTARGETS_ALL = 0x00000100, + IFC_SENDTARGETS_SINGLE = 0x00000200, }; /* struct iscsi_cmd->i_state */ @@ -368,6 +367,8 @@ struct iscsi_cmd { u8 maxcmdsn_inc; /* Immediate Unsolicited Dataout */ u8 unsolicited_data; + /* Reject reason code */ + u8 reject_reason; /* CID contained in logout PDU when opcode == ISCSI_INIT_LOGOUT_CMND */ u16 logout_cid; /* Command flags */ @@ -450,7 +451,6 @@ struct iscsi_cmd { struct list_head datain_list; /* R2T List */ struct list_head cmd_r2t_list; - struct completion reject_comp; /* Timer for DataOUT */ struct timer_list dataout_timer; /* Iovecs for SCSI data payload RX/TX w/ kernel level sockets */ diff --git a/drivers/target/iscsi/iscsi_target_erl0.c b/drivers/target/iscsi/iscsi_target_erl0.c index 8e6298cc8839..8f074e0b6099 100644 --- a/drivers/target/iscsi/iscsi_target_erl0.c +++ b/drivers/target/iscsi/iscsi_target_erl0.c @@ -746,13 +746,12 @@ int iscsit_check_post_dataout( if (!conn->sess->sess_ops->ErrorRecoveryLevel) { pr_err("Unable to recover from DataOUT CRC" " failure while ERL=0, closing session.\n"); - iscsit_add_reject_from_cmd(ISCSI_REASON_DATA_DIGEST_ERROR, - 1, 0, buf, cmd); + iscsit_reject_cmd(cmd, ISCSI_REASON_DATA_DIGEST_ERROR, + buf); return DATAOUT_CANNOT_RECOVER; } - iscsit_add_reject_from_cmd(ISCSI_REASON_DATA_DIGEST_ERROR, - 0, 0, buf, cmd); + iscsit_reject_cmd(cmd, ISCSI_REASON_DATA_DIGEST_ERROR, buf); return iscsit_dataout_post_crc_failed(cmd, buf); } } diff --git a/drivers/target/iscsi/iscsi_target_erl1.c b/drivers/target/iscsi/iscsi_target_erl1.c index 40d9dbca987b..d00f1326f0c8 100644 --- a/drivers/target/iscsi/iscsi_target_erl1.c +++ b/drivers/target/iscsi/iscsi_target_erl1.c @@ -162,9 +162,8 @@ static int iscsit_handle_r2t_snack( " protocol error.\n", cmd->init_task_tag, begrun, (begrun + runlength), cmd->acked_data_sn); - return iscsit_add_reject_from_cmd( - ISCSI_REASON_PROTOCOL_ERROR, - 1, 0, buf, cmd); + return iscsit_reject_cmd(cmd, + ISCSI_REASON_PROTOCOL_ERROR, buf); } if (runlength) { @@ -173,8 +172,8 @@ static int iscsit_handle_r2t_snack( " with BegRun: 0x%08x, RunLength: 0x%08x, exceeds" " current R2TSN: 0x%08x, protocol error.\n", cmd->init_task_tag, begrun, runlength, cmd->r2t_sn); - return iscsit_add_reject_from_cmd( - ISCSI_REASON_BOOKMARK_INVALID, 1, 0, buf, cmd); + return iscsit_reject_cmd(cmd, + ISCSI_REASON_BOOKMARK_INVALID, buf); } last_r2tsn = (begrun + runlength); } else @@ -433,8 +432,7 @@ static int iscsit_handle_recovery_datain( " protocol error.\n", cmd->init_task_tag, begrun, (begrun + runlength), cmd->acked_data_sn); - return iscsit_add_reject_from_cmd(ISCSI_REASON_PROTOCOL_ERROR, - 1, 0, buf, cmd); + return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR, buf); } /* @@ -445,14 +443,14 @@ static int iscsit_handle_recovery_datain( pr_err("Initiator requesting BegRun: 0x%08x, RunLength" ": 0x%08x greater than maximum DataSN: 0x%08x.\n", begrun, runlength, (cmd->data_sn - 1)); - return iscsit_add_reject_from_cmd(ISCSI_REASON_BOOKMARK_INVALID, - 1, 0, buf, cmd); + return iscsit_reject_cmd(cmd, ISCSI_REASON_BOOKMARK_INVALID, + buf); } dr = iscsit_allocate_datain_req(); if (!dr) - return iscsit_add_reject_from_cmd(ISCSI_REASON_BOOKMARK_NO_RESOURCES, - 1, 0, buf, cmd); + return iscsit_reject_cmd(cmd, ISCSI_REASON_BOOKMARK_NO_RESOURCES, + buf); dr->data_sn = dr->begrun = begrun; dr->runlength = runlength; diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c index fe712d6cc478..96ce6f2ec428 100644 --- a/drivers/target/iscsi/iscsi_target_util.c +++ b/drivers/target/iscsi/iscsi_target_util.c @@ -178,7 +178,6 @@ struct iscsi_cmd *iscsit_allocate_cmd(struct iscsi_conn *conn, gfp_t gfp_mask) INIT_LIST_HEAD(&cmd->i_conn_node); INIT_LIST_HEAD(&cmd->datain_list); INIT_LIST_HEAD(&cmd->cmd_r2t_list); - init_completion(&cmd->reject_comp); spin_lock_init(&cmd->datain_lock); spin_lock_init(&cmd->dataout_timeout_lock); spin_lock_init(&cmd->istate_lock); diff --git a/include/target/iscsi/iscsi_transport.h b/include/target/iscsi/iscsi_transport.h index ce991ba9e887..19a43ad977c2 100644 --- a/include/target/iscsi/iscsi_transport.h +++ b/include/target/iscsi/iscsi_transport.h @@ -34,8 +34,6 @@ extern void iscsit_put_transport(struct iscsit_transport *); /* * From iscsi_target.c */ -extern int iscsit_add_reject_from_cmd(u8, int, int, unsigned char *, - struct iscsi_cmd *); extern int iscsit_setup_scsi_cmd(struct iscsi_conn *, struct iscsi_cmd *, unsigned char *); extern void iscsit_set_unsoliticed_dataout(struct iscsi_cmd *); -- cgit v1.2.3 From 561bf15892375597ee59d473a704a3e634c4f311 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Wed, 3 Jul 2013 03:58:58 -0700 Subject: iscsi-target: Fix iscsit_sequence_cmd reject handling for iser This patch moves ISCSI_OP_REJECT failures into iscsit_sequence_cmd() in order to avoid external iscsit_reject_cmd() reject usage for all PDU types. It also updates PDU specific handlers for traditional iscsi-target code to not reset the session after posting a ISCSI_OP_REJECT during setup. (v2: Fix CMDSN_LOWER_THAN_EXP for ISCSI_OP_SCSI to call target_put_sess_cmd() after iscsit_sequence_cmd() failure) Cc: Or Gerlitz Cc: Mike Christie Cc: stable@vger.kernel.org # 3.10+ Signed-off-by: Nicholas Bellinger --- drivers/infiniband/ulp/isert/ib_isert.c | 2 +- drivers/target/iscsi/iscsi_target.c | 55 ++++++++++++++++++++------------ drivers/target/iscsi/iscsi_target_erl1.c | 6 ++-- drivers/target/iscsi/iscsi_target_util.c | 26 +++++++++++---- drivers/target/iscsi/iscsi_target_util.h | 3 +- include/target/iscsi/iscsi_transport.h | 3 +- 6 files changed, 62 insertions(+), 33 deletions(-) diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index 134ff58c6b17..7e219a46e7ca 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c @@ -934,7 +934,7 @@ isert_handle_scsi_cmd(struct isert_conn *isert_conn, } sequence_cmd: - rc = iscsit_sequence_cmd(conn, cmd, hdr->cmdsn); + rc = iscsit_sequence_cmd(conn, cmd, buf, hdr->cmdsn); if (!rc && dump_payload == false && unsol_data) iscsit_set_unsoliticed_dataout(cmd); diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 2a25bd3b065c..4319dad7d919 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -1052,11 +1052,11 @@ int iscsit_process_scsi_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, * be acknowledged. (See below) */ if (!cmd->immediate_data) { - cmdsn_ret = iscsit_sequence_cmd(conn, cmd, hdr->cmdsn); - if (cmdsn_ret == CMDSN_LOWER_THAN_EXP) { - if (!cmd->sense_reason) - return 0; - + cmdsn_ret = iscsit_sequence_cmd(conn, cmd, + (unsigned char *)hdr, hdr->cmdsn); + if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER) + return -1; + else if (cmdsn_ret == CMDSN_LOWER_THAN_EXP) { target_put_sess_cmd(conn->sess->se_sess, &cmd->se_cmd); return 0; } @@ -1083,6 +1083,9 @@ int iscsit_process_scsi_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, * iscsit_check_received_cmdsn() in iscsit_get_immediate_data() below. */ if (cmd->sense_reason) { + if (cmd->reject_reason) + return 0; + target_put_sess_cmd(conn->sess->se_sess, &cmd->se_cmd); return 1; } @@ -1091,10 +1094,8 @@ int iscsit_process_scsi_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, * the backend memory allocation. */ cmd->sense_reason = transport_generic_new_cmd(&cmd->se_cmd); - if (cmd->sense_reason) { - target_put_sess_cmd(conn->sess->se_sess, &cmd->se_cmd); + if (cmd->sense_reason) return 1; - } return 0; } @@ -1104,6 +1105,7 @@ static int iscsit_get_immediate_data(struct iscsi_cmd *cmd, struct iscsi_scsi_req *hdr, bool dump_payload) { + struct iscsi_conn *conn = cmd->conn; int cmdsn_ret = 0, immed_ret = IMMEDIATE_DATA_NORMAL_OPERATION; /* * Special case for Unsupported SAM WRITE Opcodes and ImmediateData=Yes. @@ -1120,12 +1122,22 @@ after_immediate_data: * DataCRC, check against ExpCmdSN/MaxCmdSN if * Immediate Bit is not set. */ - cmdsn_ret = iscsit_sequence_cmd(cmd->conn, cmd, hdr->cmdsn); + cmdsn_ret = iscsit_sequence_cmd(cmd->conn, cmd, + (unsigned char *)hdr, hdr->cmdsn); + if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER) { + return -1; + } else if (cmdsn_ret == CMDSN_LOWER_THAN_EXP) { + target_put_sess_cmd(conn->sess->se_sess, &cmd->se_cmd); + return 0; + } if (cmd->sense_reason) { - if (iscsit_dump_data_payload(cmd->conn, - cmd->first_burst_len, 1) < 0) - return -1; + int rc; + + rc = iscsit_dump_data_payload(cmd->conn, + cmd->first_burst_len, 1); + target_put_sess_cmd(conn->sess->se_sess, &cmd->se_cmd); + return rc; } else if (cmd->unsolicited_data) iscsit_set_unsoliticed_dataout(cmd); @@ -1159,7 +1171,7 @@ iscsit_handle_scsi_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, rc = iscsit_setup_scsi_cmd(conn, cmd, buf); if (rc < 0) - return rc; + return 0; /* * Allocation iovecs needed for struct socket operations for * traditional iSCSI block I/O. @@ -1494,7 +1506,7 @@ static int iscsit_handle_data_out(struct iscsi_conn *conn, unsigned char *buf) rc = iscsit_check_dataout_hdr(conn, buf, &cmd); if (rc < 0) - return rc; + return 0; else if (!cmd) return 0; @@ -1579,10 +1591,10 @@ int iscsit_process_nop_out(struct iscsi_conn *conn, struct iscsi_cmd *cmd, return 0; } - cmdsn_ret = iscsit_sequence_cmd(conn, cmd, hdr->cmdsn); + cmdsn_ret = iscsit_sequence_cmd(conn, cmd, + (unsigned char *)hdr, hdr->cmdsn); if (cmdsn_ret == CMDSN_LOWER_THAN_EXP) return 0; - if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER) return -1; @@ -1623,7 +1635,7 @@ static int iscsit_handle_nop_out(struct iscsi_conn *conn, struct iscsi_cmd *cmd, ret = iscsit_setup_nop_out(conn, cmd, hdr); if (ret < 0) - return ret; + return 0; /* * Handle NOP-OUT payload for traditional iSCSI sockets */ @@ -1893,7 +1905,7 @@ attach: spin_unlock_bh(&conn->cmd_lock); if (!(hdr->opcode & ISCSI_OP_IMMEDIATE)) { - int cmdsn_ret = iscsit_sequence_cmd(conn, cmd, hdr->cmdsn); + int cmdsn_ret = iscsit_sequence_cmd(conn, cmd, buf, hdr->cmdsn); if (cmdsn_ret == CMDSN_HIGHER_THAN_EXP) out_of_order_cmdsn = 1; else if (cmdsn_ret == CMDSN_LOWER_THAN_EXP) @@ -1996,7 +2008,8 @@ iscsit_process_text_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, iscsit_ack_from_expstatsn(conn, be32_to_cpu(hdr->exp_statsn)); if (!(hdr->opcode & ISCSI_OP_IMMEDIATE)) { - cmdsn_ret = iscsit_sequence_cmd(conn, cmd, hdr->cmdsn); + cmdsn_ret = iscsit_sequence_cmd(conn, cmd, + (unsigned char *)hdr, hdr->cmdsn); if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER) return -1; @@ -2022,7 +2035,7 @@ iscsit_handle_text_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, rc = iscsit_setup_text_cmd(conn, cmd, hdr); if (rc < 0) - return rc; + return 0; rx_size = payload_length; if (payload_length) { @@ -2284,7 +2297,7 @@ iscsit_handle_logout_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, if (ret < 0) return ret; } else { - cmdsn_ret = iscsit_sequence_cmd(conn, cmd, hdr->cmdsn); + cmdsn_ret = iscsit_sequence_cmd(conn, cmd, buf, hdr->cmdsn); if (cmdsn_ret == CMDSN_LOWER_THAN_EXP) logout_remove = 0; else if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER) diff --git a/drivers/target/iscsi/iscsi_target_erl1.c b/drivers/target/iscsi/iscsi_target_erl1.c index d00f1326f0c8..586c268679a4 100644 --- a/drivers/target/iscsi/iscsi_target_erl1.c +++ b/drivers/target/iscsi/iscsi_target_erl1.c @@ -1088,7 +1088,7 @@ int iscsit_handle_ooo_cmdsn( ooo_cmdsn = iscsit_allocate_ooo_cmdsn(); if (!ooo_cmdsn) - return CMDSN_ERROR_CANNOT_RECOVER; + return -ENOMEM; ooo_cmdsn->cmd = cmd; ooo_cmdsn->batch_count = (batch) ? @@ -1099,10 +1099,10 @@ int iscsit_handle_ooo_cmdsn( if (iscsit_attach_ooo_cmdsn(sess, ooo_cmdsn) < 0) { kmem_cache_free(lio_ooo_cache, ooo_cmdsn); - return CMDSN_ERROR_CANNOT_RECOVER; + return -ENOMEM; } - return CMDSN_HIGHER_THAN_EXP; + return 0; } static int iscsit_set_dataout_timeout_values( diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c index 96ce6f2ec428..1df06d5e4e01 100644 --- a/drivers/target/iscsi/iscsi_target_util.c +++ b/drivers/target/iscsi/iscsi_target_util.c @@ -283,13 +283,12 @@ static inline int iscsit_check_received_cmdsn(struct iscsi_session *sess, u32 cm * Commands may be received out of order if MC/S is in use. * Ensure they are executed in CmdSN order. */ -int iscsit_sequence_cmd( - struct iscsi_conn *conn, - struct iscsi_cmd *cmd, - __be32 cmdsn) +int iscsit_sequence_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, + unsigned char *buf, __be32 cmdsn) { - int ret; - int cmdsn_ret; + int ret, cmdsn_ret; + bool reject = false; + u8 reason = ISCSI_REASON_BOOKMARK_NO_RESOURCES; mutex_lock(&conn->sess->cmdsn_mutex); @@ -299,9 +298,19 @@ int iscsit_sequence_cmd( ret = iscsit_execute_cmd(cmd, 0); if ((ret >= 0) && !list_empty(&conn->sess->sess_ooo_cmdsn_list)) iscsit_execute_ooo_cmdsns(conn->sess); + else if (ret < 0) { + reject = true; + ret = CMDSN_ERROR_CANNOT_RECOVER; + } break; case CMDSN_HIGHER_THAN_EXP: ret = iscsit_handle_ooo_cmdsn(conn->sess, cmd, be32_to_cpu(cmdsn)); + if (ret < 0) { + reject = true; + ret = CMDSN_ERROR_CANNOT_RECOVER; + break; + } + ret = CMDSN_HIGHER_THAN_EXP; break; case CMDSN_LOWER_THAN_EXP: cmd->i_state = ISTATE_REMOVE; @@ -309,11 +318,16 @@ int iscsit_sequence_cmd( ret = cmdsn_ret; break; default: + reason = ISCSI_REASON_PROTOCOL_ERROR; + reject = true; ret = cmdsn_ret; break; } mutex_unlock(&conn->sess->cmdsn_mutex); + if (reject) + iscsit_reject_cmd(cmd, reason, buf); + return ret; } EXPORT_SYMBOL(iscsit_sequence_cmd); diff --git a/drivers/target/iscsi/iscsi_target_util.h b/drivers/target/iscsi/iscsi_target_util.h index a4422659d049..e4fc34a02f57 100644 --- a/drivers/target/iscsi/iscsi_target_util.h +++ b/drivers/target/iscsi/iscsi_target_util.h @@ -13,7 +13,8 @@ extern struct iscsi_cmd *iscsit_allocate_cmd(struct iscsi_conn *, gfp_t); extern struct iscsi_seq *iscsit_get_seq_holder_for_datain(struct iscsi_cmd *, u32); extern struct iscsi_seq *iscsit_get_seq_holder_for_r2t(struct iscsi_cmd *); extern struct iscsi_r2t *iscsit_get_holder_for_r2tsn(struct iscsi_cmd *, u32); -int iscsit_sequence_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, __be32 cmdsn); +extern int iscsit_sequence_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, + unsigned char * ,__be32 cmdsn); extern int iscsit_check_unsolicited_dataout(struct iscsi_cmd *, unsigned char *); extern struct iscsi_cmd *iscsit_find_cmd_from_itt(struct iscsi_conn *, itt_t); extern struct iscsi_cmd *iscsit_find_cmd_from_itt_or_dump(struct iscsi_conn *, diff --git a/include/target/iscsi/iscsi_transport.h b/include/target/iscsi/iscsi_transport.h index 19a43ad977c2..ce4070d8f156 100644 --- a/include/target/iscsi/iscsi_transport.h +++ b/include/target/iscsi/iscsi_transport.h @@ -86,4 +86,5 @@ extern int iscsit_tmr_post_handler(struct iscsi_cmd *, struct iscsi_conn *); * From iscsi_target_util.c */ extern struct iscsi_cmd *iscsit_allocate_cmd(struct iscsi_conn *, gfp_t); -extern int iscsit_sequence_cmd(struct iscsi_conn *, struct iscsi_cmd *, __be32); +extern int iscsit_sequence_cmd(struct iscsi_conn *, struct iscsi_cmd *, + unsigned char *, __be32); -- cgit v1.2.3 From 186a9647019587b3784694894c4d136fd00cfd7b Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Wed, 3 Jul 2013 03:11:48 -0700 Subject: iscsi-target: Fix ISCSI_OP_SCSI_TMFUNC handling for iser This patch adds target_get_sess_cmd reference counting for iscsit_handle_task_mgt_cmd(), and adds a target_put_sess_cmd() for the failure case. It also fixes a bug where ISCSI_OP_SCSI_TMFUNC type commands where leaking iscsi_cmd->i_conn_node and eventually triggering an OOPs during struct isert_conn shutdown. Cc: stable@vger.kernel.org # 3.10+ Signed-off-by: Nicholas Bellinger --- drivers/infiniband/ulp/isert/ib_isert.c | 16 ++++++++-------- drivers/target/iscsi/iscsi_target.c | 12 ++++++++++-- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index 7e219a46e7ca..9efbf13007e4 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c @@ -1200,14 +1200,12 @@ isert_put_cmd(struct isert_cmd *isert_cmd) { struct iscsi_cmd *cmd = &isert_cmd->iscsi_cmd; struct isert_conn *isert_conn = isert_cmd->conn; - struct iscsi_conn *conn; + struct iscsi_conn *conn = isert_conn->conn; pr_debug("Entering isert_put_cmd: %p\n", isert_cmd); switch (cmd->iscsi_opcode) { case ISCSI_OP_SCSI_CMD: - conn = isert_conn->conn; - spin_lock_bh(&conn->cmd_lock); if (!list_empty(&cmd->i_conn_node)) list_del(&cmd->i_conn_node); @@ -1217,16 +1215,18 @@ isert_put_cmd(struct isert_cmd *isert_cmd) iscsit_stop_dataout_timer(cmd); isert_unmap_cmd(isert_cmd, isert_conn); - /* - * Fall-through - */ + transport_generic_free_cmd(&cmd->se_cmd, 0); + break; case ISCSI_OP_SCSI_TMFUNC: + spin_lock_bh(&conn->cmd_lock); + if (!list_empty(&cmd->i_conn_node)) + list_del(&cmd->i_conn_node); + spin_unlock_bh(&conn->cmd_lock); + transport_generic_free_cmd(&cmd->se_cmd, 0); break; case ISCSI_OP_REJECT: case ISCSI_OP_NOOP_OUT: - conn = isert_conn->conn; - spin_lock_bh(&conn->cmd_lock); if (!list_empty(&cmd->i_conn_node)) list_del(&cmd->i_conn_node); diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 4319dad7d919..c30ec1d5756e 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -1737,8 +1737,8 @@ iscsit_handle_task_mgt_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, struct se_tmr_req *se_tmr; struct iscsi_tmr_req *tmr_req; struct iscsi_tm *hdr; - int out_of_order_cmdsn = 0; - int ret; + int out_of_order_cmdsn = 0, ret; + bool sess_ref = false; u8 function; hdr = (struct iscsi_tm *) buf; @@ -1794,6 +1794,9 @@ iscsit_handle_task_mgt_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, conn->sess->se_sess, 0, DMA_NONE, MSG_SIMPLE_TAG, cmd->sense_buffer + 2); + target_get_sess_cmd(conn->sess->se_sess, &cmd->se_cmd, true); + sess_ref = true; + switch (function) { case ISCSI_TM_FUNC_ABORT_TASK: tcm_function = TMR_ABORT_TASK; @@ -1931,6 +1934,11 @@ attach: * For connection recovery, this is also the default action for * TMR TASK_REASSIGN. */ + if (sess_ref) { + pr_debug("Handle TMR, using sess_ref=true check\n"); + target_put_sess_cmd(conn->sess->se_sess, &cmd->se_cmd); + } + iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state); return 0; } -- cgit v1.2.3 From c38e39c378f46f00ce922dd40a91043a9925c28d Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 25 Jun 2013 17:29:46 +0300 Subject: vhost-net: fix use-after-free in vhost_net_flush vhost_net_ubuf_put_and_wait has a confusing name: it will actually also free it's argument. Thus since commit 1280c27f8e29acf4af2da914e80ec27c3dbd5c01 "vhost-net: flush outstanding DMAs on memory change" vhost_net_flush tries to use the argument after passing it to vhost_net_ubuf_put_and_wait, this results in use after free. To fix, don't free the argument in vhost_net_ubuf_put_and_wait, add an new API for callers that want to free ubufs. Acked-by: Asias He Acked-by: Jason Wang Signed-off-by: Michael S. Tsirkin --- drivers/vhost/net.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index f80d3dd41d8c..8ca5ac71b845 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -150,6 +150,11 @@ static void vhost_net_ubuf_put_and_wait(struct vhost_net_ubuf_ref *ubufs) { kref_put(&ubufs->kref, vhost_net_zerocopy_done_signal); wait_event(ubufs->wait, !atomic_read(&ubufs->kref.refcount)); +} + +static void vhost_net_ubuf_put_wait_and_free(struct vhost_net_ubuf_ref *ubufs) +{ + vhost_net_ubuf_put_and_wait(ubufs); kfree(ubufs); } @@ -948,7 +953,7 @@ static long vhost_net_set_backend(struct vhost_net *n, unsigned index, int fd) mutex_unlock(&vq->mutex); if (oldubufs) { - vhost_net_ubuf_put_and_wait(oldubufs); + vhost_net_ubuf_put_wait_and_free(oldubufs); mutex_lock(&vq->mutex); vhost_zerocopy_signal_used(n, vq); mutex_unlock(&vq->mutex); @@ -966,7 +971,7 @@ err_used: rcu_assign_pointer(vq->private_data, oldsock); vhost_net_enable_vq(n, vq); if (ubufs) - vhost_net_ubuf_put_and_wait(ubufs); + vhost_net_ubuf_put_wait_and_free(ubufs); err_ubufs: fput(sock->file); err_vq: -- cgit v1.2.3 From 6d5e6aa860a33fdfcd07de658c8108027c06c329 Mon Sep 17 00:00:00 2001 From: Asias He Date: Mon, 6 May 2013 16:38:23 +0800 Subject: vhost: Simplify dev->vqs[i] access Signed-off-by: Asias He Signed-off-by: Michael S. Tsirkin --- drivers/vhost/vhost.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c index 60aa5ad09a2f..4d135b1a861e 100644 --- a/drivers/vhost/vhost.c +++ b/drivers/vhost/vhost.c @@ -251,17 +251,16 @@ static void vhost_vq_free_iovecs(struct vhost_virtqueue *vq) /* Helper to allocate iovec buffers for all vqs. */ static long vhost_dev_alloc_iovecs(struct vhost_dev *dev) { + struct vhost_virtqueue *vq; int i; for (i = 0; i < dev->nvqs; ++i) { - dev->vqs[i]->indirect = kmalloc(sizeof *dev->vqs[i]->indirect * - UIO_MAXIOV, GFP_KERNEL); - dev->vqs[i]->log = kmalloc(sizeof *dev->vqs[i]->log * UIO_MAXIOV, - GFP_KERNEL); - dev->vqs[i]->heads = kmalloc(sizeof *dev->vqs[i]->heads * - UIO_MAXIOV, GFP_KERNEL); - if (!dev->vqs[i]->indirect || !dev->vqs[i]->log || - !dev->vqs[i]->heads) + vq = dev->vqs[i]; + vq->indirect = kmalloc(sizeof *vq->indirect * UIO_MAXIOV, + GFP_KERNEL); + vq->log = kmalloc(sizeof *vq->log * UIO_MAXIOV, GFP_KERNEL); + vq->heads = kmalloc(sizeof *vq->heads * UIO_MAXIOV, GFP_KERNEL); + if (!vq->indirect || !vq->log || !vq->heads) goto err_nomem; } return 0; @@ -283,6 +282,7 @@ static void vhost_dev_free_iovecs(struct vhost_dev *dev) long vhost_dev_init(struct vhost_dev *dev, struct vhost_virtqueue **vqs, int nvqs) { + struct vhost_virtqueue *vq; int i; dev->vqs = vqs; @@ -297,15 +297,16 @@ long vhost_dev_init(struct vhost_dev *dev, dev->worker = NULL; for (i = 0; i < dev->nvqs; ++i) { - dev->vqs[i]->log = NULL; - dev->vqs[i]->indirect = NULL; - dev->vqs[i]->heads = NULL; - dev->vqs[i]->dev = dev; - mutex_init(&dev->vqs[i]->mutex); - vhost_vq_reset(dev, dev->vqs[i]); - if (dev->vqs[i]->handle_kick) - vhost_poll_init(&dev->vqs[i]->poll, - dev->vqs[i]->handle_kick, POLLIN, dev); + vq = dev->vqs[i]; + vq->log = NULL; + vq->indirect = NULL; + vq->heads = NULL; + vq->dev = dev; + mutex_init(&vq->mutex); + vhost_vq_reset(dev, vq); + if (vq->handle_kick) + vhost_poll_init(&vq->poll, vq->handle_kick, + POLLIN, dev); } return 0; -- cgit v1.2.3 From deeacef086a0b312da846aac7b7cd046523bf6c6 Mon Sep 17 00:00:00 2001 From: Asias He Date: Mon, 6 May 2013 16:38:25 +0800 Subject: vhost-scsi: Remove unnecessary forward struct vhost_scsi declaration It was needed when struct tcm_vhost_tpg is in tcm_vhost.h Signed-off-by: Asias He Signed-off-by: Michael S. Tsirkin --- drivers/vhost/scsi.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c index 701420297225..d5a91cf84221 100644 --- a/drivers/vhost/scsi.c +++ b/drivers/vhost/scsi.c @@ -116,7 +116,6 @@ struct tcm_vhost_nacl { struct se_node_acl se_node_acl; }; -struct vhost_scsi; struct tcm_vhost_tpg { /* Vhost port target portal group tag for TCM */ u16 tport_tpgt; -- cgit v1.2.3 From c7289312fe9b4077b5c15632b232e76144a220c6 Mon Sep 17 00:00:00 2001 From: Asias He Date: Mon, 6 May 2013 16:38:26 +0800 Subject: vhost-scsi: Rename struct vhost_scsi *s to *vs vs is used everywhere, make the naming more consistent. Signed-off-by: Asias He Signed-off-by: Michael S. Tsirkin --- drivers/vhost/scsi.c | 56 ++++++++++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c index d5a91cf84221..e5edb651b6d2 100644 --- a/drivers/vhost/scsi.c +++ b/drivers/vhost/scsi.c @@ -1337,63 +1337,63 @@ static int vhost_scsi_set_features(struct vhost_scsi *vs, u64 features) static int vhost_scsi_open(struct inode *inode, struct file *f) { - struct vhost_scsi *s; + struct vhost_scsi *vs; struct vhost_virtqueue **vqs; int r, i; - s = kzalloc(sizeof(*s), GFP_KERNEL); - if (!s) + vs = kzalloc(sizeof(*vs), GFP_KERNEL); + if (!vs) return -ENOMEM; vqs = kmalloc(VHOST_SCSI_MAX_VQ * sizeof(*vqs), GFP_KERNEL); if (!vqs) { - kfree(s); + kfree(vs); return -ENOMEM; } - vhost_work_init(&s->vs_completion_work, vhost_scsi_complete_cmd_work); - vhost_work_init(&s->vs_event_work, tcm_vhost_evt_work); + vhost_work_init(&vs->vs_completion_work, vhost_scsi_complete_cmd_work); + vhost_work_init(&vs->vs_event_work, tcm_vhost_evt_work); - s->vs_events_nr = 0; - s->vs_events_missed = false; + vs->vs_events_nr = 0; + vs->vs_events_missed = false; - vqs[VHOST_SCSI_VQ_CTL] = &s->vqs[VHOST_SCSI_VQ_CTL].vq; - vqs[VHOST_SCSI_VQ_EVT] = &s->vqs[VHOST_SCSI_VQ_EVT].vq; - s->vqs[VHOST_SCSI_VQ_CTL].vq.handle_kick = vhost_scsi_ctl_handle_kick; - s->vqs[VHOST_SCSI_VQ_EVT].vq.handle_kick = vhost_scsi_evt_handle_kick; + vqs[VHOST_SCSI_VQ_CTL] = &vs->vqs[VHOST_SCSI_VQ_CTL].vq; + vqs[VHOST_SCSI_VQ_EVT] = &vs->vqs[VHOST_SCSI_VQ_EVT].vq; + vs->vqs[VHOST_SCSI_VQ_CTL].vq.handle_kick = vhost_scsi_ctl_handle_kick; + vs->vqs[VHOST_SCSI_VQ_EVT].vq.handle_kick = vhost_scsi_evt_handle_kick; for (i = VHOST_SCSI_VQ_IO; i < VHOST_SCSI_MAX_VQ; i++) { - vqs[i] = &s->vqs[i].vq; - s->vqs[i].vq.handle_kick = vhost_scsi_handle_kick; + vqs[i] = &vs->vqs[i].vq; + vs->vqs[i].vq.handle_kick = vhost_scsi_handle_kick; } - r = vhost_dev_init(&s->dev, vqs, VHOST_SCSI_MAX_VQ); + r = vhost_dev_init(&vs->dev, vqs, VHOST_SCSI_MAX_VQ); - tcm_vhost_init_inflight(s, NULL); + tcm_vhost_init_inflight(vs, NULL); if (r < 0) { kfree(vqs); - kfree(s); + kfree(vs); return r; } - f->private_data = s; + f->private_data = vs; return 0; } static int vhost_scsi_release(struct inode *inode, struct file *f) { - struct vhost_scsi *s = f->private_data; + struct vhost_scsi *vs = f->private_data; struct vhost_scsi_target t; - mutex_lock(&s->dev.mutex); - memcpy(t.vhost_wwpn, s->vs_vhost_wwpn, sizeof(t.vhost_wwpn)); - mutex_unlock(&s->dev.mutex); - vhost_scsi_clear_endpoint(s, &t); - vhost_dev_stop(&s->dev); - vhost_dev_cleanup(&s->dev, false); + mutex_lock(&vs->dev.mutex); + memcpy(t.vhost_wwpn, vs->vs_vhost_wwpn, sizeof(t.vhost_wwpn)); + mutex_unlock(&vs->dev.mutex); + vhost_scsi_clear_endpoint(vs, &t); + vhost_dev_stop(&vs->dev); + vhost_dev_cleanup(&vs->dev, false); /* Jobs can re-queue themselves in evt kick handler. Do extra flush. */ - vhost_scsi_flush(s); - kfree(s->dev.vqs); - kfree(s); + vhost_scsi_flush(vs); + kfree(vs->dev.vqs); + kfree(vs); return 0; } -- cgit v1.2.3 From 683bd967dd32eec4e4c45e4d38e9f807a2fd9479 Mon Sep 17 00:00:00 2001 From: Asias He Date: Mon, 6 May 2013 16:38:27 +0800 Subject: vhost-scsi: Make func indention more consistent Signed-off-by: Asias He Signed-off-by: Michael S. Tsirkin --- drivers/vhost/scsi.c | 154 +++++++++++++++++++++++++++++---------------------- 1 file changed, 88 insertions(+), 66 deletions(-) diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c index e5edb651b6d2..acac9cc93edf 100644 --- a/drivers/vhost/scsi.c +++ b/drivers/vhost/scsi.c @@ -328,11 +328,12 @@ static u32 tcm_vhost_get_default_depth(struct se_portal_group *se_tpg) return 1; } -static u32 tcm_vhost_get_pr_transport_id(struct se_portal_group *se_tpg, - struct se_node_acl *se_nacl, - struct t10_pr_registration *pr_reg, - int *format_code, - unsigned char *buf) +static u32 +tcm_vhost_get_pr_transport_id(struct se_portal_group *se_tpg, + struct se_node_acl *se_nacl, + struct t10_pr_registration *pr_reg, + int *format_code, + unsigned char *buf) { struct tcm_vhost_tpg *tpg = container_of(se_tpg, struct tcm_vhost_tpg, se_tpg); @@ -358,10 +359,11 @@ static u32 tcm_vhost_get_pr_transport_id(struct se_portal_group *se_tpg, format_code, buf); } -static u32 tcm_vhost_get_pr_transport_id_len(struct se_portal_group *se_tpg, - struct se_node_acl *se_nacl, - struct t10_pr_registration *pr_reg, - int *format_code) +static u32 +tcm_vhost_get_pr_transport_id_len(struct se_portal_group *se_tpg, + struct se_node_acl *se_nacl, + struct t10_pr_registration *pr_reg, + int *format_code) { struct tcm_vhost_tpg *tpg = container_of(se_tpg, struct tcm_vhost_tpg, se_tpg); @@ -387,10 +389,11 @@ static u32 tcm_vhost_get_pr_transport_id_len(struct se_portal_group *se_tpg, format_code); } -static char *tcm_vhost_parse_pr_out_transport_id(struct se_portal_group *se_tpg, - const char *buf, - u32 *out_tid_len, - char **port_nexus_ptr) +static char * +tcm_vhost_parse_pr_out_transport_id(struct se_portal_group *se_tpg, + const char *buf, + u32 *out_tid_len, + char **port_nexus_ptr) { struct tcm_vhost_tpg *tpg = container_of(se_tpg, struct tcm_vhost_tpg, se_tpg); @@ -416,8 +419,8 @@ static char *tcm_vhost_parse_pr_out_transport_id(struct se_portal_group *se_tpg, port_nexus_ptr); } -static struct se_node_acl *tcm_vhost_alloc_fabric_acl( - struct se_portal_group *se_tpg) +static struct se_node_acl * +tcm_vhost_alloc_fabric_acl(struct se_portal_group *se_tpg) { struct tcm_vhost_nacl *nacl; @@ -430,8 +433,9 @@ static struct se_node_acl *tcm_vhost_alloc_fabric_acl( return &nacl->se_node_acl; } -static void tcm_vhost_release_fabric_acl(struct se_portal_group *se_tpg, - struct se_node_acl *se_nacl) +static void +tcm_vhost_release_fabric_acl(struct se_portal_group *se_tpg, + struct se_node_acl *se_nacl) { struct tcm_vhost_nacl *nacl = container_of(se_nacl, struct tcm_vhost_nacl, se_node_acl); @@ -526,8 +530,9 @@ static void tcm_vhost_free_evt(struct vhost_scsi *vs, struct tcm_vhost_evt *evt) kfree(evt); } -static struct tcm_vhost_evt *tcm_vhost_allocate_evt(struct vhost_scsi *vs, - u32 event, u32 reason) +static struct tcm_vhost_evt * +tcm_vhost_allocate_evt(struct vhost_scsi *vs, + u32 event, u32 reason) { struct vhost_virtqueue *vq = &vs->vqs[VHOST_SCSI_VQ_EVT].vq; struct tcm_vhost_evt *evt; @@ -571,8 +576,8 @@ static void vhost_scsi_free_cmd(struct tcm_vhost_cmd *tv_cmd) kfree(tv_cmd); } -static void tcm_vhost_do_evt_work(struct vhost_scsi *vs, - struct tcm_vhost_evt *evt) +static void +tcm_vhost_do_evt_work(struct vhost_scsi *vs, struct tcm_vhost_evt *evt) { struct vhost_virtqueue *vq = &vs->vqs[VHOST_SCSI_VQ_EVT].vq; struct virtio_scsi_event *event = &evt->event; @@ -693,12 +698,12 @@ static void vhost_scsi_complete_cmd_work(struct vhost_work *work) vhost_signal(&vs->dev, &vs->vqs[vq].vq); } -static struct tcm_vhost_cmd *vhost_scsi_allocate_cmd( - struct vhost_virtqueue *vq, - struct tcm_vhost_tpg *tv_tpg, - struct virtio_scsi_cmd_req *v_req, - u32 exp_data_len, - int data_direction) +static struct tcm_vhost_cmd * +vhost_scsi_allocate_cmd(struct vhost_virtqueue *vq, + struct tcm_vhost_tpg *tv_tpg, + struct virtio_scsi_cmd_req *v_req, + u32 exp_data_len, + int data_direction) { struct tcm_vhost_cmd *tv_cmd; struct tcm_vhost_nexus *tv_nexus; @@ -729,8 +734,11 @@ static struct tcm_vhost_cmd *vhost_scsi_allocate_cmd( * * Returns the number of scatterlist entries used or -errno on error. */ -static int vhost_scsi_map_to_sgl(struct scatterlist *sgl, - unsigned int sgl_count, struct iovec *iov, int write) +static int +vhost_scsi_map_to_sgl(struct scatterlist *sgl, + unsigned int sgl_count, + struct iovec *iov, + int write) { unsigned int npages = 0, pages_nr, offset, nbytes; struct scatterlist *sg = sgl; @@ -774,8 +782,11 @@ out: return ret; } -static int vhost_scsi_map_iov_to_sgl(struct tcm_vhost_cmd *tv_cmd, - struct iovec *iov, unsigned int niov, int write) +static int +vhost_scsi_map_iov_to_sgl(struct tcm_vhost_cmd *tv_cmd, + struct iovec *iov, + unsigned int niov, + int write) { int ret; unsigned int i; @@ -855,8 +866,10 @@ static void tcm_vhost_submission_work(struct work_struct *work) } } -static void vhost_scsi_send_bad_target(struct vhost_scsi *vs, - struct vhost_virtqueue *vq, int head, unsigned out) +static void +vhost_scsi_send_bad_target(struct vhost_scsi *vs, + struct vhost_virtqueue *vq, + int head, unsigned out) { struct virtio_scsi_cmd_resp __user *resp; struct virtio_scsi_cmd_resp rsp; @@ -872,8 +885,8 @@ static void vhost_scsi_send_bad_target(struct vhost_scsi *vs, pr_err("Faulted on virtio_scsi_cmd_resp\n"); } -static void vhost_scsi_handle_vq(struct vhost_scsi *vs, - struct vhost_virtqueue *vq) +static void +vhost_scsi_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq) { struct tcm_vhost_tpg **vs_tpg; struct virtio_scsi_cmd_req v_req; @@ -1054,8 +1067,12 @@ static void vhost_scsi_ctl_handle_kick(struct vhost_work *work) pr_debug("%s: The handling func for control queue.\n", __func__); } -static void tcm_vhost_send_evt(struct vhost_scsi *vs, struct tcm_vhost_tpg *tpg, - struct se_lun *lun, u32 event, u32 reason) +static void +tcm_vhost_send_evt(struct vhost_scsi *vs, + struct tcm_vhost_tpg *tpg, + struct se_lun *lun, + u32 event, + u32 reason) { struct tcm_vhost_evt *evt; @@ -1145,9 +1162,9 @@ static void vhost_scsi_flush(struct vhost_scsi *vs) * The lock nesting rule is: * tcm_vhost_mutex -> vs->dev.mutex -> tpg->tv_tpg_mutex -> vq->mutex */ -static int vhost_scsi_set_endpoint( - struct vhost_scsi *vs, - struct vhost_scsi_target *t) +static int +vhost_scsi_set_endpoint(struct vhost_scsi *vs, + struct vhost_scsi_target *t) { struct tcm_vhost_tport *tv_tport; struct tcm_vhost_tpg *tv_tpg; @@ -1235,9 +1252,9 @@ out: return ret; } -static int vhost_scsi_clear_endpoint( - struct vhost_scsi *vs, - struct vhost_scsi_target *t) +static int +vhost_scsi_clear_endpoint(struct vhost_scsi *vs, + struct vhost_scsi_target *t) { struct tcm_vhost_tport *tv_tport; struct tcm_vhost_tpg *tv_tpg; @@ -1397,8 +1414,10 @@ static int vhost_scsi_release(struct inode *inode, struct file *f) return 0; } -static long vhost_scsi_ioctl(struct file *f, unsigned int ioctl, - unsigned long arg) +static long +vhost_scsi_ioctl(struct file *f, + unsigned int ioctl, + unsigned long arg) { struct vhost_scsi *vs = f->private_data; struct vhost_scsi_target backend; @@ -1514,8 +1533,9 @@ static char *tcm_vhost_dump_proto_id(struct tcm_vhost_tport *tport) return "Unknown"; } -static void tcm_vhost_do_plug(struct tcm_vhost_tpg *tpg, - struct se_lun *lun, bool plug) +static void +tcm_vhost_do_plug(struct tcm_vhost_tpg *tpg, + struct se_lun *lun, bool plug) { struct vhost_scsi *vs = tpg->vhost_scsi; @@ -1555,7 +1575,7 @@ static void tcm_vhost_hotunplug(struct tcm_vhost_tpg *tpg, struct se_lun *lun) } static int tcm_vhost_port_link(struct se_portal_group *se_tpg, - struct se_lun *lun) + struct se_lun *lun) { struct tcm_vhost_tpg *tv_tpg = container_of(se_tpg, struct tcm_vhost_tpg, se_tpg); @@ -1574,7 +1594,7 @@ static int tcm_vhost_port_link(struct se_portal_group *se_tpg, } static void tcm_vhost_port_unlink(struct se_portal_group *se_tpg, - struct se_lun *lun) + struct se_lun *lun) { struct tcm_vhost_tpg *tv_tpg = container_of(se_tpg, struct tcm_vhost_tpg, se_tpg); @@ -1590,10 +1610,10 @@ static void tcm_vhost_port_unlink(struct se_portal_group *se_tpg, mutex_unlock(&tcm_vhost_mutex); } -static struct se_node_acl *tcm_vhost_make_nodeacl( - struct se_portal_group *se_tpg, - struct config_group *group, - const char *name) +static struct se_node_acl * +tcm_vhost_make_nodeacl(struct se_portal_group *se_tpg, + struct config_group *group, + const char *name) { struct se_node_acl *se_nacl, *se_nacl_new; struct tcm_vhost_nacl *nacl; @@ -1635,7 +1655,7 @@ static void tcm_vhost_drop_nodeacl(struct se_node_acl *se_acl) } static int tcm_vhost_make_nexus(struct tcm_vhost_tpg *tv_tpg, - const char *name) + const char *name) { struct se_portal_group *se_tpg; struct tcm_vhost_nexus *tv_nexus; @@ -1739,7 +1759,7 @@ static int tcm_vhost_drop_nexus(struct tcm_vhost_tpg *tpg) } static ssize_t tcm_vhost_tpg_show_nexus(struct se_portal_group *se_tpg, - char *page) + char *page) { struct tcm_vhost_tpg *tv_tpg = container_of(se_tpg, struct tcm_vhost_tpg, se_tpg); @@ -1760,8 +1780,8 @@ static ssize_t tcm_vhost_tpg_show_nexus(struct se_portal_group *se_tpg, } static ssize_t tcm_vhost_tpg_store_nexus(struct se_portal_group *se_tpg, - const char *page, - size_t count) + const char *page, + size_t count) { struct tcm_vhost_tpg *tv_tpg = container_of(se_tpg, struct tcm_vhost_tpg, se_tpg); @@ -1844,9 +1864,10 @@ static struct configfs_attribute *tcm_vhost_tpg_attrs[] = { NULL, }; -static struct se_portal_group *tcm_vhost_make_tpg(struct se_wwn *wwn, - struct config_group *group, - const char *name) +static struct se_portal_group * +tcm_vhost_make_tpg(struct se_wwn *wwn, + struct config_group *group, + const char *name) { struct tcm_vhost_tport *tport = container_of(wwn, struct tcm_vhost_tport, tport_wwn); @@ -1902,9 +1923,10 @@ static void tcm_vhost_drop_tpg(struct se_portal_group *se_tpg) kfree(tpg); } -static struct se_wwn *tcm_vhost_make_tport(struct target_fabric_configfs *tf, - struct config_group *group, - const char *name) +static struct se_wwn * +tcm_vhost_make_tport(struct target_fabric_configfs *tf, + struct config_group *group, + const char *name) { struct tcm_vhost_tport *tport; char *ptr; @@ -1974,9 +1996,9 @@ static void tcm_vhost_drop_tport(struct se_wwn *wwn) kfree(tport); } -static ssize_t tcm_vhost_wwn_show_attr_version( - struct target_fabric_configfs *tf, - char *page) +static ssize_t +tcm_vhost_wwn_show_attr_version(struct target_fabric_configfs *tf, + char *page) { return sprintf(page, "TCM_VHOST fabric module %s on %s/%s" "on "UTS_RELEASE"\n", TCM_VHOST_VERSION, utsname()->sysname, -- cgit v1.2.3 From 9871831283e79575deb63fa341e9c7f3c1223d10 Mon Sep 17 00:00:00 2001 From: Asias He Date: Mon, 6 May 2013 16:38:28 +0800 Subject: vhost-scsi: Rename struct tcm_vhost_tpg *tv_tpg to *tpg Signed-off-by: Asias He Signed-off-by: Michael S. Tsirkin --- drivers/vhost/scsi.c | 122 +++++++++++++++++++++++++-------------------------- 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c index acac9cc93edf..7fb18595c744 100644 --- a/drivers/vhost/scsi.c +++ b/drivers/vhost/scsi.c @@ -700,7 +700,7 @@ static void vhost_scsi_complete_cmd_work(struct vhost_work *work) static struct tcm_vhost_cmd * vhost_scsi_allocate_cmd(struct vhost_virtqueue *vq, - struct tcm_vhost_tpg *tv_tpg, + struct tcm_vhost_tpg *tpg, struct virtio_scsi_cmd_req *v_req, u32 exp_data_len, int data_direction) @@ -708,7 +708,7 @@ vhost_scsi_allocate_cmd(struct vhost_virtqueue *vq, struct tcm_vhost_cmd *tv_cmd; struct tcm_vhost_nexus *tv_nexus; - tv_nexus = tv_tpg->tpg_nexus; + tv_nexus = tpg->tpg_nexus; if (!tv_nexus) { pr_err("Unable to locate active struct tcm_vhost_nexus\n"); return ERR_PTR(-EIO); @@ -890,7 +890,7 @@ vhost_scsi_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq) { struct tcm_vhost_tpg **vs_tpg; struct virtio_scsi_cmd_req v_req; - struct tcm_vhost_tpg *tv_tpg; + struct tcm_vhost_tpg *tpg; struct tcm_vhost_cmd *tv_cmd; u32 exp_data_len, data_first, data_num, data_direction; unsigned out, in, i; @@ -976,10 +976,10 @@ vhost_scsi_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq) /* Extract the tpgt */ target = v_req.lun[1]; - tv_tpg = ACCESS_ONCE(vs_tpg[target]); + tpg = ACCESS_ONCE(vs_tpg[target]); /* Target does not exist, fail the request */ - if (unlikely(!tv_tpg)) { + if (unlikely(!tpg)) { vhost_scsi_send_bad_target(vs, vq, head, out); continue; } @@ -988,7 +988,7 @@ vhost_scsi_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq) for (i = 0; i < data_num; i++) exp_data_len += vq->iov[data_first + i].iov_len; - tv_cmd = vhost_scsi_allocate_cmd(vq, tv_tpg, &v_req, + tv_cmd = vhost_scsi_allocate_cmd(vq, tpg, &v_req, exp_data_len, data_direction); if (IS_ERR(tv_cmd)) { vq_err(vq, "vhost_scsi_allocate_cmd failed %ld\n", @@ -1167,7 +1167,7 @@ vhost_scsi_set_endpoint(struct vhost_scsi *vs, struct vhost_scsi_target *t) { struct tcm_vhost_tport *tv_tport; - struct tcm_vhost_tpg *tv_tpg; + struct tcm_vhost_tpg *tpg; struct tcm_vhost_tpg **vs_tpg; struct vhost_virtqueue *vq; int index, ret, i, len; @@ -1194,32 +1194,32 @@ vhost_scsi_set_endpoint(struct vhost_scsi *vs, if (vs->vs_tpg) memcpy(vs_tpg, vs->vs_tpg, len); - list_for_each_entry(tv_tpg, &tcm_vhost_list, tv_tpg_list) { - mutex_lock(&tv_tpg->tv_tpg_mutex); - if (!tv_tpg->tpg_nexus) { - mutex_unlock(&tv_tpg->tv_tpg_mutex); + list_for_each_entry(tpg, &tcm_vhost_list, tv_tpg_list) { + mutex_lock(&tpg->tv_tpg_mutex); + if (!tpg->tpg_nexus) { + mutex_unlock(&tpg->tv_tpg_mutex); continue; } - if (tv_tpg->tv_tpg_vhost_count != 0) { - mutex_unlock(&tv_tpg->tv_tpg_mutex); + if (tpg->tv_tpg_vhost_count != 0) { + mutex_unlock(&tpg->tv_tpg_mutex); continue; } - tv_tport = tv_tpg->tport; + tv_tport = tpg->tport; if (!strcmp(tv_tport->tport_name, t->vhost_wwpn)) { - if (vs->vs_tpg && vs->vs_tpg[tv_tpg->tport_tpgt]) { + if (vs->vs_tpg && vs->vs_tpg[tpg->tport_tpgt]) { kfree(vs_tpg); - mutex_unlock(&tv_tpg->tv_tpg_mutex); + mutex_unlock(&tpg->tv_tpg_mutex); ret = -EEXIST; goto out; } - tv_tpg->tv_tpg_vhost_count++; - tv_tpg->vhost_scsi = vs; - vs_tpg[tv_tpg->tport_tpgt] = tv_tpg; + tpg->tv_tpg_vhost_count++; + tpg->vhost_scsi = vs; + vs_tpg[tpg->tport_tpgt] = tpg; smp_mb__after_atomic_inc(); match = true; } - mutex_unlock(&tv_tpg->tv_tpg_mutex); + mutex_unlock(&tpg->tv_tpg_mutex); } if (match) { @@ -1257,7 +1257,7 @@ vhost_scsi_clear_endpoint(struct vhost_scsi *vs, struct vhost_scsi_target *t) { struct tcm_vhost_tport *tv_tport; - struct tcm_vhost_tpg *tv_tpg; + struct tcm_vhost_tpg *tpg; struct vhost_virtqueue *vq; bool match = false; int index, ret, i; @@ -1280,30 +1280,30 @@ vhost_scsi_clear_endpoint(struct vhost_scsi *vs, for (i = 0; i < VHOST_SCSI_MAX_TARGET; i++) { target = i; - tv_tpg = vs->vs_tpg[target]; - if (!tv_tpg) + tpg = vs->vs_tpg[target]; + if (!tpg) continue; - mutex_lock(&tv_tpg->tv_tpg_mutex); - tv_tport = tv_tpg->tport; + mutex_lock(&tpg->tv_tpg_mutex); + tv_tport = tpg->tport; if (!tv_tport) { ret = -ENODEV; goto err_tpg; } if (strcmp(tv_tport->tport_name, t->vhost_wwpn)) { - pr_warn("tv_tport->tport_name: %s, tv_tpg->tport_tpgt: %hu" + pr_warn("tv_tport->tport_name: %s, tpg->tport_tpgt: %hu" " does not match t->vhost_wwpn: %s, t->vhost_tpgt: %hu\n", - tv_tport->tport_name, tv_tpg->tport_tpgt, + tv_tport->tport_name, tpg->tport_tpgt, t->vhost_wwpn, t->vhost_tpgt); ret = -EINVAL; goto err_tpg; } - tv_tpg->tv_tpg_vhost_count--; - tv_tpg->vhost_scsi = NULL; + tpg->tv_tpg_vhost_count--; + tpg->vhost_scsi = NULL; vs->vs_tpg[target] = NULL; match = true; - mutex_unlock(&tv_tpg->tv_tpg_mutex); + mutex_unlock(&tpg->tv_tpg_mutex); } if (match) { for (i = 0; i < VHOST_SCSI_MAX_VQ; i++) { @@ -1327,7 +1327,7 @@ vhost_scsi_clear_endpoint(struct vhost_scsi *vs, return 0; err_tpg: - mutex_unlock(&tv_tpg->tv_tpg_mutex); + mutex_unlock(&tpg->tv_tpg_mutex); err_dev: mutex_unlock(&vs->dev.mutex); mutex_unlock(&tcm_vhost_mutex); @@ -1577,16 +1577,16 @@ static void tcm_vhost_hotunplug(struct tcm_vhost_tpg *tpg, struct se_lun *lun) static int tcm_vhost_port_link(struct se_portal_group *se_tpg, struct se_lun *lun) { - struct tcm_vhost_tpg *tv_tpg = container_of(se_tpg, + struct tcm_vhost_tpg *tpg = container_of(se_tpg, struct tcm_vhost_tpg, se_tpg); mutex_lock(&tcm_vhost_mutex); - mutex_lock(&tv_tpg->tv_tpg_mutex); - tv_tpg->tv_tpg_port_count++; - mutex_unlock(&tv_tpg->tv_tpg_mutex); + mutex_lock(&tpg->tv_tpg_mutex); + tpg->tv_tpg_port_count++; + mutex_unlock(&tpg->tv_tpg_mutex); - tcm_vhost_hotplug(tv_tpg, lun); + tcm_vhost_hotplug(tpg, lun); mutex_unlock(&tcm_vhost_mutex); @@ -1596,16 +1596,16 @@ static int tcm_vhost_port_link(struct se_portal_group *se_tpg, static void tcm_vhost_port_unlink(struct se_portal_group *se_tpg, struct se_lun *lun) { - struct tcm_vhost_tpg *tv_tpg = container_of(se_tpg, + struct tcm_vhost_tpg *tpg = container_of(se_tpg, struct tcm_vhost_tpg, se_tpg); mutex_lock(&tcm_vhost_mutex); - mutex_lock(&tv_tpg->tv_tpg_mutex); - tv_tpg->tv_tpg_port_count--; - mutex_unlock(&tv_tpg->tv_tpg_mutex); + mutex_lock(&tpg->tv_tpg_mutex); + tpg->tv_tpg_port_count--; + mutex_unlock(&tpg->tv_tpg_mutex); - tcm_vhost_hotunplug(tv_tpg, lun); + tcm_vhost_hotunplug(tpg, lun); mutex_unlock(&tcm_vhost_mutex); } @@ -1654,23 +1654,23 @@ static void tcm_vhost_drop_nodeacl(struct se_node_acl *se_acl) kfree(nacl); } -static int tcm_vhost_make_nexus(struct tcm_vhost_tpg *tv_tpg, +static int tcm_vhost_make_nexus(struct tcm_vhost_tpg *tpg, const char *name) { struct se_portal_group *se_tpg; struct tcm_vhost_nexus *tv_nexus; - mutex_lock(&tv_tpg->tv_tpg_mutex); - if (tv_tpg->tpg_nexus) { - mutex_unlock(&tv_tpg->tv_tpg_mutex); - pr_debug("tv_tpg->tpg_nexus already exists\n"); + mutex_lock(&tpg->tv_tpg_mutex); + if (tpg->tpg_nexus) { + mutex_unlock(&tpg->tv_tpg_mutex); + pr_debug("tpg->tpg_nexus already exists\n"); return -EEXIST; } - se_tpg = &tv_tpg->se_tpg; + se_tpg = &tpg->se_tpg; tv_nexus = kzalloc(sizeof(struct tcm_vhost_nexus), GFP_KERNEL); if (!tv_nexus) { - mutex_unlock(&tv_tpg->tv_tpg_mutex); + mutex_unlock(&tpg->tv_tpg_mutex); pr_err("Unable to allocate struct tcm_vhost_nexus\n"); return -ENOMEM; } @@ -1679,7 +1679,7 @@ static int tcm_vhost_make_nexus(struct tcm_vhost_tpg *tv_tpg, */ tv_nexus->tvn_se_sess = transport_init_session(); if (IS_ERR(tv_nexus->tvn_se_sess)) { - mutex_unlock(&tv_tpg->tv_tpg_mutex); + mutex_unlock(&tpg->tv_tpg_mutex); kfree(tv_nexus); return -ENOMEM; } @@ -1691,7 +1691,7 @@ static int tcm_vhost_make_nexus(struct tcm_vhost_tpg *tv_tpg, tv_nexus->tvn_se_sess->se_node_acl = core_tpg_check_initiator_node_acl( se_tpg, (unsigned char *)name); if (!tv_nexus->tvn_se_sess->se_node_acl) { - mutex_unlock(&tv_tpg->tv_tpg_mutex); + mutex_unlock(&tpg->tv_tpg_mutex); pr_debug("core_tpg_check_initiator_node_acl() failed" " for %s\n", name); transport_free_session(tv_nexus->tvn_se_sess); @@ -1704,9 +1704,9 @@ static int tcm_vhost_make_nexus(struct tcm_vhost_tpg *tv_tpg, */ __transport_register_session(se_tpg, tv_nexus->tvn_se_sess->se_node_acl, tv_nexus->tvn_se_sess, tv_nexus); - tv_tpg->tpg_nexus = tv_nexus; + tpg->tpg_nexus = tv_nexus; - mutex_unlock(&tv_tpg->tv_tpg_mutex); + mutex_unlock(&tpg->tv_tpg_mutex); return 0; } @@ -1761,20 +1761,20 @@ static int tcm_vhost_drop_nexus(struct tcm_vhost_tpg *tpg) static ssize_t tcm_vhost_tpg_show_nexus(struct se_portal_group *se_tpg, char *page) { - struct tcm_vhost_tpg *tv_tpg = container_of(se_tpg, + struct tcm_vhost_tpg *tpg = container_of(se_tpg, struct tcm_vhost_tpg, se_tpg); struct tcm_vhost_nexus *tv_nexus; ssize_t ret; - mutex_lock(&tv_tpg->tv_tpg_mutex); - tv_nexus = tv_tpg->tpg_nexus; + mutex_lock(&tpg->tv_tpg_mutex); + tv_nexus = tpg->tpg_nexus; if (!tv_nexus) { - mutex_unlock(&tv_tpg->tv_tpg_mutex); + mutex_unlock(&tpg->tv_tpg_mutex); return -ENODEV; } ret = snprintf(page, PAGE_SIZE, "%s\n", tv_nexus->tvn_se_sess->se_node_acl->initiatorname); - mutex_unlock(&tv_tpg->tv_tpg_mutex); + mutex_unlock(&tpg->tv_tpg_mutex); return ret; } @@ -1783,16 +1783,16 @@ static ssize_t tcm_vhost_tpg_store_nexus(struct se_portal_group *se_tpg, const char *page, size_t count) { - struct tcm_vhost_tpg *tv_tpg = container_of(se_tpg, + struct tcm_vhost_tpg *tpg = container_of(se_tpg, struct tcm_vhost_tpg, se_tpg); - struct tcm_vhost_tport *tport_wwn = tv_tpg->tport; + struct tcm_vhost_tport *tport_wwn = tpg->tport; unsigned char i_port[TCM_VHOST_NAMELEN], *ptr, *port_ptr; int ret; /* * Shutdown the active I_T nexus if 'NULL' is passed.. */ if (!strncmp(page, "NULL", 4)) { - ret = tcm_vhost_drop_nexus(tv_tpg); + ret = tcm_vhost_drop_nexus(tpg); return (!ret) ? count : ret; } /* @@ -1850,7 +1850,7 @@ check_newline: if (i_port[strlen(i_port)-1] == '\n') i_port[strlen(i_port)-1] = '\0'; - ret = tcm_vhost_make_nexus(tv_tpg, port_ptr); + ret = tcm_vhost_make_nexus(tpg, port_ptr); if (ret < 0) return ret; -- cgit v1.2.3 From 3c63f66a0dcdd6cb8bcacf210181f2b3baed19be Mon Sep 17 00:00:00 2001 From: Asias He Date: Mon, 6 May 2013 16:38:29 +0800 Subject: vhost-scsi: Rename struct tcm_vhost_cmd *tv_cmd to *cmd This way, we use cmd for struct tcm_vhost_cmd and evt for struct tcm_vhost_cmd. Signed-off-by: Asias He Signed-off-by: Michael S. Tsirkin --- drivers/vhost/scsi.c | 142 +++++++++++++++++++++++++-------------------------- 1 file changed, 71 insertions(+), 71 deletions(-) diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c index 7fb18595c744..03765e17c154 100644 --- a/drivers/vhost/scsi.c +++ b/drivers/vhost/scsi.c @@ -494,28 +494,28 @@ static int tcm_vhost_get_cmd_state(struct se_cmd *se_cmd) return 0; } -static void vhost_scsi_complete_cmd(struct tcm_vhost_cmd *tv_cmd) +static void vhost_scsi_complete_cmd(struct tcm_vhost_cmd *cmd) { - struct vhost_scsi *vs = tv_cmd->tvc_vhost; + struct vhost_scsi *vs = cmd->tvc_vhost; - llist_add(&tv_cmd->tvc_completion_list, &vs->vs_completion_list); + llist_add(&cmd->tvc_completion_list, &vs->vs_completion_list); vhost_work_queue(&vs->dev, &vs->vs_completion_work); } static int tcm_vhost_queue_data_in(struct se_cmd *se_cmd) { - struct tcm_vhost_cmd *tv_cmd = container_of(se_cmd, + struct tcm_vhost_cmd *cmd = container_of(se_cmd, struct tcm_vhost_cmd, tvc_se_cmd); - vhost_scsi_complete_cmd(tv_cmd); + vhost_scsi_complete_cmd(cmd); return 0; } static int tcm_vhost_queue_status(struct se_cmd *se_cmd) { - struct tcm_vhost_cmd *tv_cmd = container_of(se_cmd, + struct tcm_vhost_cmd *cmd = container_of(se_cmd, struct tcm_vhost_cmd, tvc_se_cmd); - vhost_scsi_complete_cmd(tv_cmd); + vhost_scsi_complete_cmd(cmd); return 0; } @@ -556,24 +556,24 @@ tcm_vhost_allocate_evt(struct vhost_scsi *vs, return evt; } -static void vhost_scsi_free_cmd(struct tcm_vhost_cmd *tv_cmd) +static void vhost_scsi_free_cmd(struct tcm_vhost_cmd *cmd) { - struct se_cmd *se_cmd = &tv_cmd->tvc_se_cmd; + struct se_cmd *se_cmd = &cmd->tvc_se_cmd; /* TODO locking against target/backend threads? */ transport_generic_free_cmd(se_cmd, 1); - if (tv_cmd->tvc_sgl_count) { + if (cmd->tvc_sgl_count) { u32 i; - for (i = 0; i < tv_cmd->tvc_sgl_count; i++) - put_page(sg_page(&tv_cmd->tvc_sgl[i])); + for (i = 0; i < cmd->tvc_sgl_count; i++) + put_page(sg_page(&cmd->tvc_sgl[i])); - kfree(tv_cmd->tvc_sgl); + kfree(cmd->tvc_sgl); } - tcm_vhost_put_inflight(tv_cmd->inflight); + tcm_vhost_put_inflight(cmd->inflight); - kfree(tv_cmd); + kfree(cmd); } static void @@ -656,7 +656,7 @@ static void vhost_scsi_complete_cmd_work(struct vhost_work *work) vs_completion_work); DECLARE_BITMAP(signal, VHOST_SCSI_MAX_VQ); struct virtio_scsi_cmd_resp v_rsp; - struct tcm_vhost_cmd *tv_cmd; + struct tcm_vhost_cmd *cmd; struct llist_node *llnode; struct se_cmd *se_cmd; int ret, vq; @@ -664,32 +664,32 @@ static void vhost_scsi_complete_cmd_work(struct vhost_work *work) bitmap_zero(signal, VHOST_SCSI_MAX_VQ); llnode = llist_del_all(&vs->vs_completion_list); while (llnode) { - tv_cmd = llist_entry(llnode, struct tcm_vhost_cmd, + cmd = llist_entry(llnode, struct tcm_vhost_cmd, tvc_completion_list); llnode = llist_next(llnode); - se_cmd = &tv_cmd->tvc_se_cmd; + se_cmd = &cmd->tvc_se_cmd; pr_debug("%s tv_cmd %p resid %u status %#02x\n", __func__, - tv_cmd, se_cmd->residual_count, se_cmd->scsi_status); + cmd, se_cmd->residual_count, se_cmd->scsi_status); memset(&v_rsp, 0, sizeof(v_rsp)); v_rsp.resid = se_cmd->residual_count; /* TODO is status_qualifier field needed? */ v_rsp.status = se_cmd->scsi_status; v_rsp.sense_len = se_cmd->scsi_sense_length; - memcpy(v_rsp.sense, tv_cmd->tvc_sense_buf, + memcpy(v_rsp.sense, cmd->tvc_sense_buf, v_rsp.sense_len); - ret = copy_to_user(tv_cmd->tvc_resp, &v_rsp, sizeof(v_rsp)); + ret = copy_to_user(cmd->tvc_resp, &v_rsp, sizeof(v_rsp)); if (likely(ret == 0)) { struct vhost_scsi_virtqueue *q; - vhost_add_used(tv_cmd->tvc_vq, tv_cmd->tvc_vq_desc, 0); - q = container_of(tv_cmd->tvc_vq, struct vhost_scsi_virtqueue, vq); + vhost_add_used(cmd->tvc_vq, cmd->tvc_vq_desc, 0); + q = container_of(cmd->tvc_vq, struct vhost_scsi_virtqueue, vq); vq = q - vs->vqs; __set_bit(vq, signal); } else pr_err("Faulted on virtio_scsi_cmd_resp\n"); - vhost_scsi_free_cmd(tv_cmd); + vhost_scsi_free_cmd(cmd); } vq = -1; @@ -705,7 +705,7 @@ vhost_scsi_allocate_cmd(struct vhost_virtqueue *vq, u32 exp_data_len, int data_direction) { - struct tcm_vhost_cmd *tv_cmd; + struct tcm_vhost_cmd *cmd; struct tcm_vhost_nexus *tv_nexus; tv_nexus = tpg->tpg_nexus; @@ -714,19 +714,19 @@ vhost_scsi_allocate_cmd(struct vhost_virtqueue *vq, return ERR_PTR(-EIO); } - tv_cmd = kzalloc(sizeof(struct tcm_vhost_cmd), GFP_ATOMIC); - if (!tv_cmd) { + cmd = kzalloc(sizeof(struct tcm_vhost_cmd), GFP_ATOMIC); + if (!cmd) { pr_err("Unable to allocate struct tcm_vhost_cmd\n"); return ERR_PTR(-ENOMEM); } - tv_cmd->tvc_tag = v_req->tag; - tv_cmd->tvc_task_attr = v_req->task_attr; - tv_cmd->tvc_exp_data_len = exp_data_len; - tv_cmd->tvc_data_direction = data_direction; - tv_cmd->tvc_nexus = tv_nexus; - tv_cmd->inflight = tcm_vhost_get_inflight(vq); + cmd->tvc_tag = v_req->tag; + cmd->tvc_task_attr = v_req->task_attr; + cmd->tvc_exp_data_len = exp_data_len; + cmd->tvc_data_direction = data_direction; + cmd->tvc_nexus = tv_nexus; + cmd->inflight = tcm_vhost_get_inflight(vq); - return tv_cmd; + return cmd; } /* @@ -783,7 +783,7 @@ out: } static int -vhost_scsi_map_iov_to_sgl(struct tcm_vhost_cmd *tv_cmd, +vhost_scsi_map_iov_to_sgl(struct tcm_vhost_cmd *cmd, struct iovec *iov, unsigned int niov, int write) @@ -802,25 +802,25 @@ vhost_scsi_map_iov_to_sgl(struct tcm_vhost_cmd *tv_cmd, /* TODO overflow checking */ - sg = kmalloc(sizeof(tv_cmd->tvc_sgl[0]) * sgl_count, GFP_ATOMIC); + sg = kmalloc(sizeof(cmd->tvc_sgl[0]) * sgl_count, GFP_ATOMIC); if (!sg) return -ENOMEM; pr_debug("%s sg %p sgl_count %u is_err %d\n", __func__, sg, sgl_count, !sg); sg_init_table(sg, sgl_count); - tv_cmd->tvc_sgl = sg; - tv_cmd->tvc_sgl_count = sgl_count; + cmd->tvc_sgl = sg; + cmd->tvc_sgl_count = sgl_count; pr_debug("Mapping %u iovecs for %u pages\n", niov, sgl_count); for (i = 0; i < niov; i++) { ret = vhost_scsi_map_to_sgl(sg, sgl_count, &iov[i], write); if (ret < 0) { - for (i = 0; i < tv_cmd->tvc_sgl_count; i++) - put_page(sg_page(&tv_cmd->tvc_sgl[i])); - kfree(tv_cmd->tvc_sgl); - tv_cmd->tvc_sgl = NULL; - tv_cmd->tvc_sgl_count = 0; + for (i = 0; i < cmd->tvc_sgl_count; i++) + put_page(sg_page(&cmd->tvc_sgl[i])); + kfree(cmd->tvc_sgl); + cmd->tvc_sgl = NULL; + cmd->tvc_sgl_count = 0; return ret; } @@ -832,15 +832,15 @@ vhost_scsi_map_iov_to_sgl(struct tcm_vhost_cmd *tv_cmd, static void tcm_vhost_submission_work(struct work_struct *work) { - struct tcm_vhost_cmd *tv_cmd = + struct tcm_vhost_cmd *cmd = container_of(work, struct tcm_vhost_cmd, work); struct tcm_vhost_nexus *tv_nexus; - struct se_cmd *se_cmd = &tv_cmd->tvc_se_cmd; + struct se_cmd *se_cmd = &cmd->tvc_se_cmd; struct scatterlist *sg_ptr, *sg_bidi_ptr = NULL; int rc, sg_no_bidi = 0; - if (tv_cmd->tvc_sgl_count) { - sg_ptr = tv_cmd->tvc_sgl; + if (cmd->tvc_sgl_count) { + sg_ptr = cmd->tvc_sgl; /* FIXME: Fix BIDI operation in tcm_vhost_submission_work() */ #if 0 if (se_cmd->se_cmd_flags & SCF_BIDI) { @@ -851,13 +851,13 @@ static void tcm_vhost_submission_work(struct work_struct *work) } else { sg_ptr = NULL; } - tv_nexus = tv_cmd->tvc_nexus; + tv_nexus = cmd->tvc_nexus; rc = target_submit_cmd_map_sgls(se_cmd, tv_nexus->tvn_se_sess, - tv_cmd->tvc_cdb, &tv_cmd->tvc_sense_buf[0], - tv_cmd->tvc_lun, tv_cmd->tvc_exp_data_len, - tv_cmd->tvc_task_attr, tv_cmd->tvc_data_direction, - 0, sg_ptr, tv_cmd->tvc_sgl_count, + cmd->tvc_cdb, &cmd->tvc_sense_buf[0], + cmd->tvc_lun, cmd->tvc_exp_data_len, + cmd->tvc_task_attr, cmd->tvc_data_direction, + 0, sg_ptr, cmd->tvc_sgl_count, sg_bidi_ptr, sg_no_bidi); if (rc < 0) { transport_send_check_condition_and_sense(se_cmd, @@ -891,7 +891,7 @@ vhost_scsi_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq) struct tcm_vhost_tpg **vs_tpg; struct virtio_scsi_cmd_req v_req; struct tcm_vhost_tpg *tpg; - struct tcm_vhost_cmd *tv_cmd; + struct tcm_vhost_cmd *cmd; u32 exp_data_len, data_first, data_num, data_direction; unsigned out, in, i; int head, ret; @@ -988,46 +988,46 @@ vhost_scsi_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq) for (i = 0; i < data_num; i++) exp_data_len += vq->iov[data_first + i].iov_len; - tv_cmd = vhost_scsi_allocate_cmd(vq, tpg, &v_req, + cmd = vhost_scsi_allocate_cmd(vq, tpg, &v_req, exp_data_len, data_direction); - if (IS_ERR(tv_cmd)) { + if (IS_ERR(cmd)) { vq_err(vq, "vhost_scsi_allocate_cmd failed %ld\n", - PTR_ERR(tv_cmd)); + PTR_ERR(cmd)); goto err_cmd; } pr_debug("Allocated tv_cmd: %p exp_data_len: %d, data_direction" - ": %d\n", tv_cmd, exp_data_len, data_direction); + ": %d\n", cmd, exp_data_len, data_direction); - tv_cmd->tvc_vhost = vs; - tv_cmd->tvc_vq = vq; - tv_cmd->tvc_resp = vq->iov[out].iov_base; + cmd->tvc_vhost = vs; + cmd->tvc_vq = vq; + cmd->tvc_resp = vq->iov[out].iov_base; /* - * Copy in the recieved CDB descriptor into tv_cmd->tvc_cdb + * Copy in the recieved CDB descriptor into cmd->tvc_cdb * that will be used by tcm_vhost_new_cmd_map() and down into * target_setup_cmd_from_cdb() */ - memcpy(tv_cmd->tvc_cdb, v_req.cdb, TCM_VHOST_MAX_CDB_SIZE); + memcpy(cmd->tvc_cdb, v_req.cdb, TCM_VHOST_MAX_CDB_SIZE); /* * Check that the recieved CDB size does not exceeded our * hardcoded max for tcm_vhost */ /* TODO what if cdb was too small for varlen cdb header? */ - if (unlikely(scsi_command_size(tv_cmd->tvc_cdb) > + if (unlikely(scsi_command_size(cmd->tvc_cdb) > TCM_VHOST_MAX_CDB_SIZE)) { vq_err(vq, "Received SCSI CDB with command_size: %d that" " exceeds SCSI_MAX_VARLEN_CDB_SIZE: %d\n", - scsi_command_size(tv_cmd->tvc_cdb), + scsi_command_size(cmd->tvc_cdb), TCM_VHOST_MAX_CDB_SIZE); goto err_free; } - tv_cmd->tvc_lun = ((v_req.lun[2] << 8) | v_req.lun[3]) & 0x3FFF; + cmd->tvc_lun = ((v_req.lun[2] << 8) | v_req.lun[3]) & 0x3FFF; pr_debug("vhost_scsi got command opcode: %#02x, lun: %d\n", - tv_cmd->tvc_cdb[0], tv_cmd->tvc_lun); + cmd->tvc_cdb[0], cmd->tvc_lun); if (data_direction != DMA_NONE) { - ret = vhost_scsi_map_iov_to_sgl(tv_cmd, + ret = vhost_scsi_map_iov_to_sgl(cmd, &vq->iov[data_first], data_num, data_direction == DMA_TO_DEVICE); if (unlikely(ret)) { @@ -1041,22 +1041,22 @@ vhost_scsi_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq) * complete the virtio-scsi request in TCM callback context via * tcm_vhost_queue_data_in() and tcm_vhost_queue_status() */ - tv_cmd->tvc_vq_desc = head; + cmd->tvc_vq_desc = head; /* * Dispatch tv_cmd descriptor for cmwq execution in process * context provided by tcm_vhost_workqueue. This also ensures * tv_cmd is executed on the same kworker CPU as this vhost * thread to gain positive L2 cache locality effects.. */ - INIT_WORK(&tv_cmd->work, tcm_vhost_submission_work); - queue_work(tcm_vhost_workqueue, &tv_cmd->work); + INIT_WORK(&cmd->work, tcm_vhost_submission_work); + queue_work(tcm_vhost_workqueue, &cmd->work); } mutex_unlock(&vq->mutex); return; err_free: - vhost_scsi_free_cmd(tv_cmd); + vhost_scsi_free_cmd(cmd); err_cmd: vhost_scsi_send_bad_target(vs, vq, head, out); mutex_unlock(&vq->mutex); -- cgit v1.2.3 From 6ac1afbf6132df0fcb0898f3509371305af7de16 Mon Sep 17 00:00:00 2001 From: Asias He Date: Mon, 6 May 2013 16:38:21 +0800 Subject: vhost: Make vhost a separate module Currently, vhost-net and vhost-scsi are sharing the vhost core code. However, vhost-scsi shares the code by including the vhost.c file directly. Making vhost a separate module makes it is easier to share code with other vhost devices. Signed-off-by: Asias He Signed-off-by: Michael S. Tsirkin --- drivers/vhost/Kconfig | 8 ++++++++ drivers/vhost/Makefile | 3 ++- drivers/vhost/scsi.c | 1 - drivers/vhost/test.c | 2 +- drivers/vhost/vhost.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++- drivers/vhost/vhost.h | 2 ++ 6 files changed, 63 insertions(+), 4 deletions(-) diff --git a/drivers/vhost/Kconfig b/drivers/vhost/Kconfig index 8b9226da3f54..017a1e8a8f6f 100644 --- a/drivers/vhost/Kconfig +++ b/drivers/vhost/Kconfig @@ -1,6 +1,7 @@ config VHOST_NET tristate "Host kernel accelerator for virtio net" depends on NET && EVENTFD && (TUN || !TUN) && (MACVTAP || !MACVTAP) + select VHOST select VHOST_RING ---help--- This kernel module can be loaded in host kernel to accelerate @@ -13,6 +14,7 @@ config VHOST_NET config VHOST_SCSI tristate "VHOST_SCSI TCM fabric driver" depends on TARGET_CORE && EVENTFD && m + select VHOST select VHOST_RING default n ---help--- @@ -24,3 +26,9 @@ config VHOST_RING ---help--- This option is selected by any driver which needs to access the host side of a virtio ring. + +config VHOST + tristate + ---help--- + This option is selected by any driver which needs to access + the core of vhost. diff --git a/drivers/vhost/Makefile b/drivers/vhost/Makefile index 654e9afb11f5..e0441c34db1c 100644 --- a/drivers/vhost/Makefile +++ b/drivers/vhost/Makefile @@ -1,7 +1,8 @@ obj-$(CONFIG_VHOST_NET) += vhost_net.o -vhost_net-y := vhost.o net.o +vhost_net-y := net.o obj-$(CONFIG_VHOST_SCSI) += vhost_scsi.o vhost_scsi-y := scsi.o obj-$(CONFIG_VHOST_RING) += vringh.o +obj-$(CONFIG_VHOST) += vhost.o diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c index 03765e17c154..5531ebcc7276 100644 --- a/drivers/vhost/scsi.c +++ b/drivers/vhost/scsi.c @@ -49,7 +49,6 @@ #include #include -#include "vhost.c" #include "vhost.h" #define TCM_VHOST_VERSION "v0.1" diff --git a/drivers/vhost/test.c b/drivers/vhost/test.c index 1ee45bc85f67..9b71a577f7bd 100644 --- a/drivers/vhost/test.c +++ b/drivers/vhost/test.c @@ -18,7 +18,7 @@ #include #include "test.h" -#include "vhost.c" +#include "vhost.h" /* Max number of bytes transferred before requeueing the job. * Using this limit prevents one virtqueue from starving others. */ diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c index 4d135b1a861e..e58cf0001cee 100644 --- a/drivers/vhost/vhost.c +++ b/drivers/vhost/vhost.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "vhost.h" @@ -66,6 +67,7 @@ void vhost_work_init(struct vhost_work *work, vhost_work_fn_t fn) work->flushing = 0; work->queue_seq = work->done_seq = 0; } +EXPORT_SYMBOL_GPL(vhost_work_init); /* Init poll structure */ void vhost_poll_init(struct vhost_poll *poll, vhost_work_fn_t fn, @@ -79,6 +81,7 @@ void vhost_poll_init(struct vhost_poll *poll, vhost_work_fn_t fn, vhost_work_init(&poll->work, fn); } +EXPORT_SYMBOL_GPL(vhost_poll_init); /* Start polling a file. We add ourselves to file's wait queue. The caller must * keep a reference to a file until after vhost_poll_stop is called. */ @@ -101,6 +104,7 @@ int vhost_poll_start(struct vhost_poll *poll, struct file *file) return ret; } +EXPORT_SYMBOL_GPL(vhost_poll_start); /* Stop polling a file. After this function returns, it becomes safe to drop the * file reference. You must also flush afterwards. */ @@ -111,6 +115,7 @@ void vhost_poll_stop(struct vhost_poll *poll) poll->wqh = NULL; } } +EXPORT_SYMBOL_GPL(vhost_poll_stop); static bool vhost_work_seq_done(struct vhost_dev *dev, struct vhost_work *work, unsigned seq) @@ -123,7 +128,7 @@ static bool vhost_work_seq_done(struct vhost_dev *dev, struct vhost_work *work, return left <= 0; } -static void vhost_work_flush(struct vhost_dev *dev, struct vhost_work *work) +void vhost_work_flush(struct vhost_dev *dev, struct vhost_work *work) { unsigned seq; int flushing; @@ -138,6 +143,7 @@ static void vhost_work_flush(struct vhost_dev *dev, struct vhost_work *work) spin_unlock_irq(&dev->work_lock); BUG_ON(flushing < 0); } +EXPORT_SYMBOL_GPL(vhost_work_flush); /* Flush any work that has been scheduled. When calling this, don't hold any * locks that are also used by the callback. */ @@ -145,6 +151,7 @@ void vhost_poll_flush(struct vhost_poll *poll) { vhost_work_flush(poll->dev, &poll->work); } +EXPORT_SYMBOL_GPL(vhost_poll_flush); void vhost_work_queue(struct vhost_dev *dev, struct vhost_work *work) { @@ -158,11 +165,13 @@ void vhost_work_queue(struct vhost_dev *dev, struct vhost_work *work) } spin_unlock_irqrestore(&dev->work_lock, flags); } +EXPORT_SYMBOL_GPL(vhost_work_queue); void vhost_poll_queue(struct vhost_poll *poll) { vhost_work_queue(poll->dev, &poll->work); } +EXPORT_SYMBOL_GPL(vhost_poll_queue); static void vhost_vq_reset(struct vhost_dev *dev, struct vhost_virtqueue *vq) @@ -311,6 +320,7 @@ long vhost_dev_init(struct vhost_dev *dev, return 0; } +EXPORT_SYMBOL_GPL(vhost_dev_init); /* Caller should have device mutex */ long vhost_dev_check_owner(struct vhost_dev *dev) @@ -318,6 +328,7 @@ long vhost_dev_check_owner(struct vhost_dev *dev) /* Are you the owner? If not, I don't think you mean to do that */ return dev->mm == current->mm ? 0 : -EPERM; } +EXPORT_SYMBOL_GPL(vhost_dev_check_owner); struct vhost_attach_cgroups_struct { struct vhost_work work; @@ -349,6 +360,7 @@ bool vhost_dev_has_owner(struct vhost_dev *dev) { return dev->mm; } +EXPORT_SYMBOL_GPL(vhost_dev_has_owner); /* Caller should have device mutex */ long vhost_dev_set_owner(struct vhost_dev *dev) @@ -392,11 +404,13 @@ err_worker: err_mm: return err; } +EXPORT_SYMBOL_GPL(vhost_dev_set_owner); struct vhost_memory *vhost_dev_reset_owner_prepare(void) { return kmalloc(offsetof(struct vhost_memory, regions), GFP_KERNEL); } +EXPORT_SYMBOL_GPL(vhost_dev_reset_owner_prepare); /* Caller should have device mutex */ void vhost_dev_reset_owner(struct vhost_dev *dev, struct vhost_memory *memory) @@ -407,6 +421,7 @@ void vhost_dev_reset_owner(struct vhost_dev *dev, struct vhost_memory *memory) memory->nregions = 0; RCU_INIT_POINTER(dev->memory, memory); } +EXPORT_SYMBOL_GPL(vhost_dev_reset_owner); void vhost_dev_stop(struct vhost_dev *dev) { @@ -419,6 +434,7 @@ void vhost_dev_stop(struct vhost_dev *dev) } } } +EXPORT_SYMBOL_GPL(vhost_dev_stop); /* Caller should have device mutex if and only if locked is set */ void vhost_dev_cleanup(struct vhost_dev *dev, bool locked) @@ -459,6 +475,7 @@ void vhost_dev_cleanup(struct vhost_dev *dev, bool locked) mmput(dev->mm); dev->mm = NULL; } +EXPORT_SYMBOL_GPL(vhost_dev_cleanup); static int log_access_ok(void __user *log_base, u64 addr, unsigned long sz) { @@ -544,6 +561,7 @@ int vhost_log_access_ok(struct vhost_dev *dev) lockdep_is_held(&dev->mutex)); return memory_access_ok(dev, mp, 1); } +EXPORT_SYMBOL_GPL(vhost_log_access_ok); /* Verify access for write logging. */ /* Caller should have vq mutex and device mutex */ @@ -569,6 +587,7 @@ int vhost_vq_access_ok(struct vhost_virtqueue *vq) return vq_access_ok(vq->dev, vq->num, vq->desc, vq->avail, vq->used) && vq_log_access_ok(vq->dev, vq, vq->log_base); } +EXPORT_SYMBOL_GPL(vhost_vq_access_ok); static long vhost_set_memory(struct vhost_dev *d, struct vhost_memory __user *m) { @@ -798,6 +817,7 @@ long vhost_vring_ioctl(struct vhost_dev *d, int ioctl, void __user *argp) vhost_poll_flush(&vq->poll); return r; } +EXPORT_SYMBOL_GPL(vhost_vring_ioctl); /* Caller must have device mutex */ long vhost_dev_ioctl(struct vhost_dev *d, unsigned int ioctl, void __user *argp) @@ -878,6 +898,7 @@ long vhost_dev_ioctl(struct vhost_dev *d, unsigned int ioctl, void __user *argp) done: return r; } +EXPORT_SYMBOL_GPL(vhost_dev_ioctl); static const struct vhost_memory_region *find_region(struct vhost_memory *mem, __u64 addr, __u32 len) @@ -969,6 +990,7 @@ int vhost_log_write(struct vhost_virtqueue *vq, struct vhost_log *log, BUG(); return 0; } +EXPORT_SYMBOL_GPL(vhost_log_write); static int vhost_update_used_flags(struct vhost_virtqueue *vq) { @@ -1020,6 +1042,7 @@ int vhost_init_used(struct vhost_virtqueue *vq) vq->signalled_used_valid = false; return get_user(vq->last_used_idx, &vq->used->idx); } +EXPORT_SYMBOL_GPL(vhost_init_used); static int translate_desc(struct vhost_dev *dev, u64 addr, u32 len, struct iovec iov[], int iov_size) @@ -1296,12 +1319,14 @@ int vhost_get_vq_desc(struct vhost_dev *dev, struct vhost_virtqueue *vq, BUG_ON(!(vq->used_flags & VRING_USED_F_NO_NOTIFY)); return head; } +EXPORT_SYMBOL_GPL(vhost_get_vq_desc); /* Reverse the effect of vhost_get_vq_desc. Useful for error handling. */ void vhost_discard_vq_desc(struct vhost_virtqueue *vq, int n) { vq->last_avail_idx -= n; } +EXPORT_SYMBOL_GPL(vhost_discard_vq_desc); /* After we've used one of their buffers, we tell them about it. We'll then * want to notify the guest, using eventfd. */ @@ -1350,6 +1375,7 @@ int vhost_add_used(struct vhost_virtqueue *vq, unsigned int head, int len) vq->signalled_used_valid = false; return 0; } +EXPORT_SYMBOL_GPL(vhost_add_used); static int __vhost_add_used_n(struct vhost_virtqueue *vq, struct vring_used_elem *heads, @@ -1419,6 +1445,7 @@ int vhost_add_used_n(struct vhost_virtqueue *vq, struct vring_used_elem *heads, } return r; } +EXPORT_SYMBOL_GPL(vhost_add_used_n); static bool vhost_notify(struct vhost_dev *dev, struct vhost_virtqueue *vq) { @@ -1463,6 +1490,7 @@ void vhost_signal(struct vhost_dev *dev, struct vhost_virtqueue *vq) if (vq->call_ctx && vhost_notify(dev, vq)) eventfd_signal(vq->call_ctx, 1); } +EXPORT_SYMBOL_GPL(vhost_signal); /* And here's the combo meal deal. Supersize me! */ void vhost_add_used_and_signal(struct vhost_dev *dev, @@ -1472,6 +1500,7 @@ void vhost_add_used_and_signal(struct vhost_dev *dev, vhost_add_used(vq, head, len); vhost_signal(dev, vq); } +EXPORT_SYMBOL_GPL(vhost_add_used_and_signal); /* multi-buffer version of vhost_add_used_and_signal */ void vhost_add_used_and_signal_n(struct vhost_dev *dev, @@ -1481,6 +1510,7 @@ void vhost_add_used_and_signal_n(struct vhost_dev *dev, vhost_add_used_n(vq, heads, count); vhost_signal(dev, vq); } +EXPORT_SYMBOL_GPL(vhost_add_used_and_signal_n); /* OK, now we need to know about added descriptors. */ bool vhost_enable_notify(struct vhost_dev *dev, struct vhost_virtqueue *vq) @@ -1518,6 +1548,7 @@ bool vhost_enable_notify(struct vhost_dev *dev, struct vhost_virtqueue *vq) return avail_idx != vq->avail_idx; } +EXPORT_SYMBOL_GPL(vhost_enable_notify); /* We don't need to be notified again. */ void vhost_disable_notify(struct vhost_dev *dev, struct vhost_virtqueue *vq) @@ -1534,3 +1565,21 @@ void vhost_disable_notify(struct vhost_dev *dev, struct vhost_virtqueue *vq) &vq->used->flags, r); } } +EXPORT_SYMBOL_GPL(vhost_disable_notify); + +static int __init vhost_init(void) +{ + return 0; +} + +static void __exit vhost_exit(void) +{ +} + +module_init(vhost_init); +module_exit(vhost_exit); + +MODULE_VERSION("0.0.1"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Michael S. Tsirkin"); +MODULE_DESCRIPTION("Host kernel accelerator for virtio"); diff --git a/drivers/vhost/vhost.h b/drivers/vhost/vhost.h index 64adcf99ff33..42298cd23c73 100644 --- a/drivers/vhost/vhost.h +++ b/drivers/vhost/vhost.h @@ -46,6 +46,8 @@ int vhost_poll_start(struct vhost_poll *poll, struct file *file); void vhost_poll_stop(struct vhost_poll *poll); void vhost_poll_flush(struct vhost_poll *poll); void vhost_poll_queue(struct vhost_poll *poll); +void vhost_work_flush(struct vhost_dev *dev, struct vhost_work *work); +long vhost_vring_ioctl(struct vhost_dev *d, int ioctl, void __user *argp); struct vhost_log { u64 addr; -- cgit v1.2.3 From 0a1febf7baafe2de156e0fadd15afb7ebec5d74f Mon Sep 17 00:00:00 2001 From: Asias He Date: Wed, 5 Jun 2013 21:17:38 +0800 Subject: vhost: Make local function static $ make C=1 M=drivers/vhost drivers/vhost/net.c:168:5: warning: symbol 'vhost_net_set_ubuf_info' was not declared. Should it be static? drivers/vhost/net.c:194:6: warning: symbol 'vhost_net_vq_reset' was not declared. Should it be static? drivers/vhost/scsi.c:219:6: warning: symbol 'tcm_vhost_done_inflight' was not declared. Should it be static? Signed-off-by: Asias He Signed-off-by: Michael S. Tsirkin --- drivers/vhost/net.c | 4 ++-- drivers/vhost/scsi.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index 8ca5ac71b845..027be91db139 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -168,7 +168,7 @@ static void vhost_net_clear_ubuf_info(struct vhost_net *n) } } -int vhost_net_set_ubuf_info(struct vhost_net *n) +static int vhost_net_set_ubuf_info(struct vhost_net *n) { bool zcopy; int i; @@ -189,7 +189,7 @@ err: return -ENOMEM; } -void vhost_net_vq_reset(struct vhost_net *n) +static void vhost_net_vq_reset(struct vhost_net *n) { int i; diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c index 5531ebcc7276..4264840ef7dc 100644 --- a/drivers/vhost/scsi.c +++ b/drivers/vhost/scsi.c @@ -216,7 +216,7 @@ static int iov_num_pages(struct iovec *iov) ((unsigned long)iov->iov_base & PAGE_MASK)) >> PAGE_SHIFT; } -void tcm_vhost_done_inflight(struct kref *kref) +static void tcm_vhost_done_inflight(struct kref *kref) { struct vhost_scsi_inflight *inflight; -- cgit v1.2.3 From 09a34c8404c1d4c5782de319c02e1d742c57875c Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Sun, 7 Jul 2013 17:12:36 +0300 Subject: vhost/test: update test after vhost cleanups We changed the type of dev.vqs field, update test module accordingly. Signed-off-by: Michael S. Tsirkin --- drivers/vhost/test.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/drivers/vhost/test.c b/drivers/vhost/test.c index 9b71a577f7bd..a73ea217f24d 100644 --- a/drivers/vhost/test.c +++ b/drivers/vhost/test.c @@ -38,17 +38,19 @@ struct vhost_test { * read-size critical section for our kind of RCU. */ static void handle_vq(struct vhost_test *n) { - struct vhost_virtqueue *vq = &n->dev.vqs[VHOST_TEST_VQ]; + struct vhost_virtqueue *vq = &n->vqs[VHOST_TEST_VQ]; unsigned out, in; int head; size_t len, total_len = 0; void *private; - private = rcu_dereference_check(vq->private_data, 1); - if (!private) + mutex_lock(&vq->mutex); + private = vq->private_data; + if (!private) { + mutex_unlock(&vq->mutex); return; + } - mutex_lock(&vq->mutex); vhost_disable_notify(&n->dev, vq); for (;;) { @@ -102,15 +104,23 @@ static int vhost_test_open(struct inode *inode, struct file *f) { struct vhost_test *n = kmalloc(sizeof *n, GFP_KERNEL); struct vhost_dev *dev; + struct vhost_virtqueue **vqs; int r; if (!n) return -ENOMEM; + vqs = kmalloc(VHOST_TEST_VQ_MAX * sizeof(*vqs), GFP_KERNEL); + if (!vqs) { + kfree(n); + return -ENOMEM; + } dev = &n->dev; + vqs[VHOST_TEST_VQ] = &n->vqs[VHOST_TEST_VQ]; n->vqs[VHOST_TEST_VQ].handle_kick = handle_vq_kick; - r = vhost_dev_init(dev, n->vqs, VHOST_TEST_VQ_MAX); + r = vhost_dev_init(dev, vqs, VHOST_TEST_VQ_MAX); if (r < 0) { + kfree(vqs); kfree(n); return r; } @@ -126,9 +136,8 @@ static void *vhost_test_stop_vq(struct vhost_test *n, void *private; mutex_lock(&vq->mutex); - private = rcu_dereference_protected(vq->private_data, - lockdep_is_held(&vq->mutex)); - rcu_assign_pointer(vq->private_data, NULL); + private = vq->private_data; + vq->private_data = NULL; mutex_unlock(&vq->mutex); return private; } @@ -140,7 +149,7 @@ static void vhost_test_stop(struct vhost_test *n, void **privatep) static void vhost_test_flush_vq(struct vhost_test *n, int index) { - vhost_poll_flush(&n->dev.vqs[index].poll); + vhost_poll_flush(&n->vqs[index].poll); } static void vhost_test_flush(struct vhost_test *n) @@ -268,14 +277,14 @@ static long vhost_test_ioctl(struct file *f, unsigned int ioctl, return -EFAULT; return vhost_test_run(n, test); case VHOST_GET_FEATURES: - features = VHOST_NET_FEATURES; + features = VHOST_FEATURES; if (copy_to_user(featurep, &features, sizeof features)) return -EFAULT; return 0; case VHOST_SET_FEATURES: if (copy_from_user(&features, featurep, sizeof features)) return -EFAULT; - if (features & ~VHOST_NET_FEATURES) + if (features & ~VHOST_FEATURES) return -EOPNOTSUPP; return vhost_test_set_features(n, features); case VHOST_RESET_OWNER: -- cgit v1.2.3 From 1bb3f6a252c92cbc07884091e185a51b4ccb4f1d Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Mon, 8 Jul 2013 10:40:35 +1000 Subject: drm/nouveau: fix minor thinko causing bo moves to not be async on kepler Reported-by: Maarten Lankhorst Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_bo.c | 3 ++- drivers/gpu/drm/nouveau/nouveau_drm.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index a1cf8255db50..4b1afb131380 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -968,7 +968,7 @@ nouveau_bo_move_m2mf(struct ttm_buffer_object *bo, int evict, bool intr, bool no_wait_gpu, struct ttm_mem_reg *new_mem) { struct nouveau_drm *drm = nouveau_bdev(bo->bdev); - struct nouveau_channel *chan = chan = drm->channel; + struct nouveau_channel *chan = chan = drm->ttm.chan; struct nouveau_bo *nvbo = nouveau_bo(bo); struct ttm_mem_reg *old_mem = &bo->mem; int ret; @@ -1052,6 +1052,7 @@ nouveau_bo_move_init(struct nouveau_drm *drm) } drm->ttm.move = mthd->exec; + drm->ttm.chan = chan; name = mthd->name; break; } diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.h b/drivers/gpu/drm/nouveau/nouveau_drm.h index f2b30f89dee0..41ff7e0d403a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.h +++ b/drivers/gpu/drm/nouveau/nouveau_drm.h @@ -96,6 +96,7 @@ struct nouveau_drm { int (*move)(struct nouveau_channel *, struct ttm_buffer_object *, struct ttm_mem_reg *, struct ttm_mem_reg *); + struct nouveau_channel *chan; int mtrr; } ttm; -- cgit v1.2.3 From d2989b534ef6834ebf2425aecc040b894b567c91 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Sun, 7 Jul 2013 10:37:48 +0200 Subject: drm/nvc0/gr: fix gpc firmware regression "drm/nve0-/gr: some new gpc registers can have multiple copies" 5ee86c4190f9e caused a regression for nvc0, because the bit indicating last transfer has occured was no longer set, resulting in random system lockups. Reported-by: Ronald Uitermark Tested-by: Ronald Uitermark Signed-off-by: Maarten Lankhorst Signed-off-by: Ben Skeggs --- .../gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc | 3 ++ .../nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h | 40 +++++++++++----------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc index b52f4a8b8699..5547c1b3f4f2 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpc.fuc @@ -352,6 +352,9 @@ ctx_xfer: // per-TPC mmio context xbit $r10 $flags $p1 // direction +#if !NV_PGRAPH_GPCX_UNK__SIZE + or $r10 4 // last +#endif mov $r11 0x4000 sethi $r11 0x500000 // base = NV_PGRAPH_GPC0_TPC0 ld b32 $r12 D[$r0 + #gpc_id] diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h index 2afe75ce89e9..f2b0dea80116 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/fuc/gpcnvc0.fuc.h @@ -400,26 +400,26 @@ uint32_t nvc0_grgpc_code[] = { 0x0d98000c, 0x00e7f001, 0x016621f5, - 0xf101acf0, - 0xf04000b7, - 0x0c9850b3, - 0x0fc4b604, - 0x9800bcbb, - 0x0d98010c, - 0x060f9802, - 0x0800e7f1, - 0x016621f5, - 0x021521f5, - 0xf40601f4, -/* 0x0532: ctx_xfer_post */ - 0x17f11412, - 0x13f04afc, - 0x0d27f002, - 0xf50012d0, -/* 0x0543: ctx_xfer_done */ - 0xf5021521, - 0xf8047921, - 0x00000000, + 0xf001acf0, + 0xb7f104a5, + 0xb3f04000, + 0x040c9850, + 0xbb0fc4b6, + 0x0c9800bc, + 0x020d9801, + 0xf1060f98, + 0xf50800e7, + 0xf5016621, + 0xf4021521, + 0x12f40601, +/* 0x0535: ctx_xfer_post */ + 0xfc17f114, + 0x0213f04a, + 0xd00d27f0, + 0x21f50012, +/* 0x0546: ctx_xfer_done */ + 0x21f50215, + 0x00f80479, 0x00000000, 0x00000000, 0x00000000, -- cgit v1.2.3 From b2cb96494d83b894a43ba8b9023eead8ff50684b Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Wed, 3 Jul 2013 03:05:37 -0700 Subject: iser-target: Fix session reset bug with RDMA_CM_EVENT_DISCONNECTED This patch addresses a bug where RDMA_CM_EVENT_DISCONNECTED may occur before the connection shutdown has been completed by rx/tx threads, that causes isert_free_conn() to wait indefinately on ->conn_wait. This patch allows isert_disconnect_work code to invoke rdma_disconnect when isert_disconnect_work() process context is started by client session reset before isert_free_conn() code has been reached. It also adds isert_conn->conn_mutex protection for ->state within isert_disconnect_work(), isert_cq_comp_err() and isert_free_conn() code, along with isert_check_state() for wait_event usage. (v2: Add explicit iscsit_cause_connection_reinstatement call during isert_disconnect_work() to force conn reset) Cc: stable@vger.kernel.org # 3.10+ Cc: Or Gerlitz Signed-off-by: Nicholas Bellinger --- drivers/infiniband/ulp/isert/ib_isert.c | 70 +++++++++++++++++++++++++++----- drivers/infiniband/ulp/isert/ib_isert.h | 1 + drivers/target/iscsi/iscsi_target_erl0.c | 1 + include/target/iscsi/iscsi_transport.h | 4 ++ 4 files changed, 66 insertions(+), 10 deletions(-) diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index 9efbf13007e4..d6cc5999d793 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c @@ -388,6 +388,7 @@ isert_connect_request(struct rdma_cm_id *cma_id, struct rdma_cm_event *event) init_waitqueue_head(&isert_conn->conn_wait_comp_err); kref_init(&isert_conn->conn_kref); kref_get(&isert_conn->conn_kref); + mutex_init(&isert_conn->conn_mutex); cma_id->context = isert_conn; isert_conn->conn_cm_id = cma_id; @@ -540,15 +541,32 @@ isert_disconnect_work(struct work_struct *work) struct isert_conn, conn_logout_work); pr_debug("isert_disconnect_work(): >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); - + mutex_lock(&isert_conn->conn_mutex); isert_conn->state = ISER_CONN_DOWN; if (isert_conn->post_recv_buf_count == 0 && atomic_read(&isert_conn->post_send_buf_count) == 0) { pr_debug("Calling wake_up(&isert_conn->conn_wait);\n"); - wake_up(&isert_conn->conn_wait); + mutex_unlock(&isert_conn->conn_mutex); + goto wake_up; + } + if (!isert_conn->conn_cm_id) { + mutex_unlock(&isert_conn->conn_mutex); + isert_put_conn(isert_conn); + return; + } + if (!isert_conn->logout_posted) { + pr_debug("Calling rdma_disconnect for !logout_posted from" + " isert_disconnect_work\n"); + rdma_disconnect(isert_conn->conn_cm_id); + mutex_unlock(&isert_conn->conn_mutex); + iscsit_cause_connection_reinstatement(isert_conn->conn, 0); + goto wake_up; } + mutex_unlock(&isert_conn->conn_mutex); +wake_up: + wake_up(&isert_conn->conn_wait); isert_put_conn(isert_conn); } @@ -1439,7 +1457,11 @@ isert_cq_comp_err(struct iser_tx_desc *tx_desc, struct isert_conn *isert_conn) pr_debug("isert_cq_comp_err >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); pr_debug("Calling wake_up from isert_cq_comp_err\n"); - isert_conn->state = ISER_CONN_TERMINATING; + mutex_lock(&isert_conn->conn_mutex); + if (isert_conn->state != ISER_CONN_DOWN) + isert_conn->state = ISER_CONN_TERMINATING; + mutex_unlock(&isert_conn->conn_mutex); + wake_up(&isert_conn->conn_wait_comp_err); } } @@ -2209,6 +2231,17 @@ isert_free_np(struct iscsi_np *np) kfree(isert_np); } +static int isert_check_state(struct isert_conn *isert_conn, int state) +{ + int ret; + + mutex_lock(&isert_conn->conn_mutex); + ret = (isert_conn->state == state); + mutex_unlock(&isert_conn->conn_mutex); + + return ret; +} + static void isert_free_conn(struct iscsi_conn *conn) { struct isert_conn *isert_conn = conn->context; @@ -2218,26 +2251,43 @@ static void isert_free_conn(struct iscsi_conn *conn) * Decrement post_send_buf_count for special case when called * from isert_do_control_comp() -> iscsit_logout_post_handler() */ + mutex_lock(&isert_conn->conn_mutex); if (isert_conn->logout_posted) atomic_dec(&isert_conn->post_send_buf_count); - if (isert_conn->conn_cm_id) + if (isert_conn->conn_cm_id && isert_conn->state != ISER_CONN_DOWN) { + pr_debug("Calling rdma_disconnect from isert_free_conn\n"); rdma_disconnect(isert_conn->conn_cm_id); + } /* * Only wait for conn_wait_comp_err if the isert_conn made it * into full feature phase.. */ - if (isert_conn->state > ISER_CONN_INIT) { + if (isert_conn->state == ISER_CONN_UP) { pr_debug("isert_free_conn: Before wait_event comp_err %d\n", isert_conn->state); + mutex_unlock(&isert_conn->conn_mutex); + wait_event(isert_conn->conn_wait_comp_err, - isert_conn->state == ISER_CONN_TERMINATING); - pr_debug("isert_free_conn: After wait_event #1 >>>>>>>>>>>>\n"); + (isert_check_state(isert_conn, ISER_CONN_TERMINATING))); + + wait_event(isert_conn->conn_wait, + (isert_check_state(isert_conn, ISER_CONN_DOWN))); + + isert_put_conn(isert_conn); + return; + } + if (isert_conn->state == ISER_CONN_INIT) { + mutex_unlock(&isert_conn->conn_mutex); + isert_put_conn(isert_conn); + return; } + pr_debug("isert_free_conn: wait_event conn_wait %d\n", + isert_conn->state); + mutex_unlock(&isert_conn->conn_mutex); - pr_debug("isert_free_conn: wait_event conn_wait %d\n", isert_conn->state); - wait_event(isert_conn->conn_wait, isert_conn->state == ISER_CONN_DOWN); - pr_debug("isert_free_conn: After wait_event #2 >>>>>>>>>>>>>>>>>>>>\n"); + wait_event(isert_conn->conn_wait, + (isert_check_state(isert_conn, ISER_CONN_DOWN))); isert_put_conn(isert_conn); } diff --git a/drivers/infiniband/ulp/isert/ib_isert.h b/drivers/infiniband/ulp/isert/ib_isert.h index b104f4c2cd38..5795c82a2306 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.h +++ b/drivers/infiniband/ulp/isert/ib_isert.h @@ -102,6 +102,7 @@ struct isert_conn { struct ib_qp *conn_qp; struct isert_device *conn_device; struct work_struct conn_logout_work; + struct mutex conn_mutex; wait_queue_head_t conn_wait; wait_queue_head_t conn_wait_comp_err; struct kref conn_kref; diff --git a/drivers/target/iscsi/iscsi_target_erl0.c b/drivers/target/iscsi/iscsi_target_erl0.c index 8f074e0b6099..3722f8dffa68 100644 --- a/drivers/target/iscsi/iscsi_target_erl0.c +++ b/drivers/target/iscsi/iscsi_target_erl0.c @@ -908,6 +908,7 @@ void iscsit_cause_connection_reinstatement(struct iscsi_conn *conn, int sleep) wait_for_completion(&conn->conn_wait_comp); complete(&conn->conn_post_wait_comp); } +EXPORT_SYMBOL(iscsit_cause_connection_reinstatement); void iscsit_fall_back_to_erl0(struct iscsi_session *sess) { diff --git a/include/target/iscsi/iscsi_transport.h b/include/target/iscsi/iscsi_transport.h index ce4070d8f156..e5d09d242ba3 100644 --- a/include/target/iscsi/iscsi_transport.h +++ b/include/target/iscsi/iscsi_transport.h @@ -72,6 +72,10 @@ extern int iscsit_logout_post_handler(struct iscsi_cmd *, struct iscsi_conn *); * From iscsi_target_device.c */ extern void iscsit_increment_maxcmdsn(struct iscsi_cmd *, struct iscsi_session *); +/* + * From iscsi_target_erl0.c + */ +extern void iscsit_cause_connection_reinstatement(struct iscsi_conn *, int); /* * From iscsi_target_erl1.c */ -- cgit v1.2.3 From e5c0d6ad557b32f431a70a4efba820430f6ff88b Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Wed, 26 Jun 2013 17:36:17 -0700 Subject: target: Add tracepoints for SCSI commands being processed This patch adds tracepoints to the target code for commands being received and being completed, which is quite useful for debugging interactions with initiators. For example, one can do something like the following to watch commands that are completing unsuccessfully: # echo 'scsi_status!=0' > /sys/kernel/debug/tracing/events/target/target_cmd_complete/filter # echo 1 > /sys/kernel/debug/tracing/events/target/target_cmd_complete/enable # cat /sys/kernel/debug/tracing/trace iscsi_trx-0-1902 [003] ...1 990185.810385: target_cmd_complete: iqn.1993-08.org.debian:01:e51ede6aacfd <- LUN 001 status CHECK CONDITION (sense len 18 / 70 00 05 00 00 00 00 0a 00 00 00 00 20 00 00 00 00 00) 0x95 data_length 512 CDB 95 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 (TA:SIMPLE C:00) (v2: Drop undefined COMPARE_AND_WRITE) Signed-off-by: Roland Dreier Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_transport.c | 16 ++- include/trace/events/target.h | 214 +++++++++++++++++++++++++++++++++ 2 files changed, 229 insertions(+), 1 deletion(-) create mode 100644 include/trace/events/target.h diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index ae40addd4ce0..7172d005d063 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -52,6 +52,9 @@ #include "target_core_pr.h" #include "target_core_ua.h" +#define CREATE_TRACE_POINTS +#include + static struct workqueue_struct *target_completion_wq; static struct kmem_cache *se_sess_cache; struct kmem_cache *se_ua_cache; @@ -1123,6 +1126,8 @@ target_setup_cmd_from_cdb(struct se_cmd *cmd, unsigned char *cdb) */ memcpy(cmd->t_task_cdb, cdb, scsi_command_size(cdb)); + trace_target_sequencer_start(cmd); + /* * Check for an existing UNIT ATTENTION condition */ @@ -1546,7 +1551,8 @@ void transport_generic_request_failure(struct se_cmd *cmd, cmd->orig_fe_lun, 0x2C, ASCQ_2CH_PREVIOUS_RESERVATION_CONFLICT_STATUS); - ret = cmd->se_tfo->queue_status(cmd); + trace_target_cmd_complete(cmd); + ret = cmd->se_tfo-> queue_status(cmd); if (ret == -EAGAIN || ret == -ENOMEM) goto queue_full; goto check_stop; @@ -1766,6 +1772,7 @@ static void transport_complete_qf(struct se_cmd *cmd) transport_complete_task_attr(cmd); if (cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) { + trace_target_cmd_complete(cmd); ret = cmd->se_tfo->queue_status(cmd); if (ret) goto out; @@ -1773,6 +1780,7 @@ static void transport_complete_qf(struct se_cmd *cmd) switch (cmd->data_direction) { case DMA_FROM_DEVICE: + trace_target_cmd_complete(cmd); ret = cmd->se_tfo->queue_data_in(cmd); break; case DMA_TO_DEVICE: @@ -1783,6 +1791,7 @@ static void transport_complete_qf(struct se_cmd *cmd) } /* Fall through for DMA_TO_DEVICE */ case DMA_NONE: + trace_target_cmd_complete(cmd); ret = cmd->se_tfo->queue_status(cmd); break; default: @@ -1861,6 +1870,7 @@ static void target_complete_ok_work(struct work_struct *work) } spin_unlock(&cmd->se_lun->lun_sep_lock); + trace_target_cmd_complete(cmd); ret = cmd->se_tfo->queue_data_in(cmd); if (ret == -EAGAIN || ret == -ENOMEM) goto queue_full; @@ -1889,6 +1899,7 @@ static void target_complete_ok_work(struct work_struct *work) } /* Fall through for DMA_TO_DEVICE */ case DMA_NONE: + trace_target_cmd_complete(cmd); ret = cmd->se_tfo->queue_status(cmd); if (ret == -EAGAIN || ret == -ENOMEM) goto queue_full; @@ -2745,6 +2756,7 @@ transport_send_check_condition_and_sense(struct se_cmd *cmd, cmd->scsi_sense_length = TRANSPORT_SENSE_BUFFER; after_reason: + trace_target_cmd_complete(cmd); return cmd->se_tfo->queue_status(cmd); } EXPORT_SYMBOL(transport_send_check_condition_and_sense); @@ -2761,6 +2773,7 @@ int transport_check_aborted_status(struct se_cmd *cmd, int send_status) cmd->t_task_cdb[0], cmd->se_tfo->get_task_tag(cmd)); cmd->se_cmd_flags |= SCF_SENT_DELAYED_TAS; + trace_target_cmd_complete(cmd); cmd->se_tfo->queue_status(cmd); return 1; @@ -2798,6 +2811,7 @@ void transport_send_task_abort(struct se_cmd *cmd) " ITT: 0x%08x\n", cmd->t_task_cdb[0], cmd->se_tfo->get_task_tag(cmd)); + trace_target_cmd_complete(cmd); cmd->se_tfo->queue_status(cmd); } diff --git a/include/trace/events/target.h b/include/trace/events/target.h new file mode 100644 index 000000000000..aef8fc354025 --- /dev/null +++ b/include/trace/events/target.h @@ -0,0 +1,214 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM target + +#if !defined(_TRACE_TARGET_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_TARGET_H + +#include +#include +#include +#include +#include + +/* cribbed verbatim from */ +#define scsi_opcode_name(opcode) { opcode, #opcode } +#define show_opcode_name(val) \ + __print_symbolic(val, \ + scsi_opcode_name(TEST_UNIT_READY), \ + scsi_opcode_name(REZERO_UNIT), \ + scsi_opcode_name(REQUEST_SENSE), \ + scsi_opcode_name(FORMAT_UNIT), \ + scsi_opcode_name(READ_BLOCK_LIMITS), \ + scsi_opcode_name(REASSIGN_BLOCKS), \ + scsi_opcode_name(INITIALIZE_ELEMENT_STATUS), \ + scsi_opcode_name(READ_6), \ + scsi_opcode_name(WRITE_6), \ + scsi_opcode_name(SEEK_6), \ + scsi_opcode_name(READ_REVERSE), \ + scsi_opcode_name(WRITE_FILEMARKS), \ + scsi_opcode_name(SPACE), \ + scsi_opcode_name(INQUIRY), \ + scsi_opcode_name(RECOVER_BUFFERED_DATA), \ + scsi_opcode_name(MODE_SELECT), \ + scsi_opcode_name(RESERVE), \ + scsi_opcode_name(RELEASE), \ + scsi_opcode_name(COPY), \ + scsi_opcode_name(ERASE), \ + scsi_opcode_name(MODE_SENSE), \ + scsi_opcode_name(START_STOP), \ + scsi_opcode_name(RECEIVE_DIAGNOSTIC), \ + scsi_opcode_name(SEND_DIAGNOSTIC), \ + scsi_opcode_name(ALLOW_MEDIUM_REMOVAL), \ + scsi_opcode_name(SET_WINDOW), \ + scsi_opcode_name(READ_CAPACITY), \ + scsi_opcode_name(READ_10), \ + scsi_opcode_name(WRITE_10), \ + scsi_opcode_name(SEEK_10), \ + scsi_opcode_name(POSITION_TO_ELEMENT), \ + scsi_opcode_name(WRITE_VERIFY), \ + scsi_opcode_name(VERIFY), \ + scsi_opcode_name(SEARCH_HIGH), \ + scsi_opcode_name(SEARCH_EQUAL), \ + scsi_opcode_name(SEARCH_LOW), \ + scsi_opcode_name(SET_LIMITS), \ + scsi_opcode_name(PRE_FETCH), \ + scsi_opcode_name(READ_POSITION), \ + scsi_opcode_name(SYNCHRONIZE_CACHE), \ + scsi_opcode_name(LOCK_UNLOCK_CACHE), \ + scsi_opcode_name(READ_DEFECT_DATA), \ + scsi_opcode_name(MEDIUM_SCAN), \ + scsi_opcode_name(COMPARE), \ + scsi_opcode_name(COPY_VERIFY), \ + scsi_opcode_name(WRITE_BUFFER), \ + scsi_opcode_name(READ_BUFFER), \ + scsi_opcode_name(UPDATE_BLOCK), \ + scsi_opcode_name(READ_LONG), \ + scsi_opcode_name(WRITE_LONG), \ + scsi_opcode_name(CHANGE_DEFINITION), \ + scsi_opcode_name(WRITE_SAME), \ + scsi_opcode_name(UNMAP), \ + scsi_opcode_name(READ_TOC), \ + scsi_opcode_name(LOG_SELECT), \ + scsi_opcode_name(LOG_SENSE), \ + scsi_opcode_name(XDWRITEREAD_10), \ + scsi_opcode_name(MODE_SELECT_10), \ + scsi_opcode_name(RESERVE_10), \ + scsi_opcode_name(RELEASE_10), \ + scsi_opcode_name(MODE_SENSE_10), \ + scsi_opcode_name(PERSISTENT_RESERVE_IN), \ + scsi_opcode_name(PERSISTENT_RESERVE_OUT), \ + scsi_opcode_name(VARIABLE_LENGTH_CMD), \ + scsi_opcode_name(REPORT_LUNS), \ + scsi_opcode_name(MAINTENANCE_IN), \ + scsi_opcode_name(MAINTENANCE_OUT), \ + scsi_opcode_name(MOVE_MEDIUM), \ + scsi_opcode_name(EXCHANGE_MEDIUM), \ + scsi_opcode_name(READ_12), \ + scsi_opcode_name(WRITE_12), \ + scsi_opcode_name(WRITE_VERIFY_12), \ + scsi_opcode_name(SEARCH_HIGH_12), \ + scsi_opcode_name(SEARCH_EQUAL_12), \ + scsi_opcode_name(SEARCH_LOW_12), \ + scsi_opcode_name(READ_ELEMENT_STATUS), \ + scsi_opcode_name(SEND_VOLUME_TAG), \ + scsi_opcode_name(WRITE_LONG_2), \ + scsi_opcode_name(READ_16), \ + scsi_opcode_name(WRITE_16), \ + scsi_opcode_name(VERIFY_16), \ + scsi_opcode_name(WRITE_SAME_16), \ + scsi_opcode_name(SERVICE_ACTION_IN), \ + scsi_opcode_name(SAI_READ_CAPACITY_16), \ + scsi_opcode_name(SAI_GET_LBA_STATUS), \ + scsi_opcode_name(MI_REPORT_TARGET_PGS), \ + scsi_opcode_name(MO_SET_TARGET_PGS), \ + scsi_opcode_name(READ_32), \ + scsi_opcode_name(WRITE_32), \ + scsi_opcode_name(WRITE_SAME_32), \ + scsi_opcode_name(ATA_16), \ + scsi_opcode_name(ATA_12)) + +#define show_task_attribute_name(val) \ + __print_symbolic(val, \ + { MSG_SIMPLE_TAG, "SIMPLE" }, \ + { MSG_HEAD_TAG, "HEAD" }, \ + { MSG_ORDERED_TAG, "ORDERED" }, \ + { MSG_ACA_TAG, "ACA" } ) + +#define show_scsi_status_name(val) \ + __print_symbolic(val, \ + { SAM_STAT_GOOD, "GOOD" }, \ + { SAM_STAT_CHECK_CONDITION, "CHECK CONDITION" }, \ + { SAM_STAT_CONDITION_MET, "CONDITION MET" }, \ + { SAM_STAT_BUSY, "BUSY" }, \ + { SAM_STAT_INTERMEDIATE, "INTERMEDIATE" }, \ + { SAM_STAT_INTERMEDIATE_CONDITION_MET, "INTERMEDIATE CONDITION MET" }, \ + { SAM_STAT_RESERVATION_CONFLICT, "RESERVATION CONFLICT" }, \ + { SAM_STAT_COMMAND_TERMINATED, "COMMAND TERMINATED" }, \ + { SAM_STAT_TASK_SET_FULL, "TASK SET FULL" }, \ + { SAM_STAT_ACA_ACTIVE, "ACA ACTIVE" }, \ + { SAM_STAT_TASK_ABORTED, "TASK ABORTED" } ) + +TRACE_EVENT(target_sequencer_start, + + TP_PROTO(struct se_cmd *cmd), + + TP_ARGS(cmd), + + TP_STRUCT__entry( + __field( unsigned int, unpacked_lun ) + __field( unsigned int, opcode ) + __field( unsigned int, data_length ) + __field( unsigned int, task_attribute ) + __array( unsigned char, cdb, TCM_MAX_COMMAND_SIZE ) + __string( initiator, cmd->se_sess->se_node_acl->initiatorname ) + ), + + TP_fast_assign( + __entry->unpacked_lun = cmd->se_lun->unpacked_lun; + __entry->opcode = cmd->t_task_cdb[0]; + __entry->data_length = cmd->data_length; + __entry->task_attribute = cmd->sam_task_attr; + memcpy(__entry->cdb, cmd->t_task_cdb, TCM_MAX_COMMAND_SIZE); + __assign_str(initiator, cmd->se_sess->se_node_acl->initiatorname); + ), + + TP_printk("%s -> LUN %03u %s data_length %6u CDB %s (TA:%s C:%02x)", + __get_str(initiator), __entry->unpacked_lun, + show_opcode_name(__entry->opcode), + __entry->data_length, __print_hex(__entry->cdb, 16), + show_task_attribute_name(__entry->task_attribute), + scsi_command_size(__entry->cdb) <= 16 ? + __entry->cdb[scsi_command_size(__entry->cdb) - 1] : + __entry->cdb[1] + ) +); + +TRACE_EVENT(target_cmd_complete, + + TP_PROTO(struct se_cmd *cmd), + + TP_ARGS(cmd), + + TP_STRUCT__entry( + __field( unsigned int, unpacked_lun ) + __field( unsigned int, opcode ) + __field( unsigned int, data_length ) + __field( unsigned int, task_attribute ) + __field( unsigned char, scsi_status ) + __field( unsigned char, sense_length ) + __array( unsigned char, cdb, TCM_MAX_COMMAND_SIZE ) + __array( unsigned char, sense_data, 18 ) + __string(initiator, cmd->se_sess->se_node_acl->initiatorname) + ), + + TP_fast_assign( + __entry->unpacked_lun = cmd->se_lun->unpacked_lun; + __entry->opcode = cmd->t_task_cdb[0]; + __entry->data_length = cmd->data_length; + __entry->task_attribute = cmd->sam_task_attr; + __entry->scsi_status = cmd->scsi_status; + __entry->sense_length = cmd->scsi_status == SAM_STAT_CHECK_CONDITION ? + min(18, ((u8 *) cmd->sense_buffer)[SPC_ADD_SENSE_LEN_OFFSET] + 8) : 0; + memcpy(__entry->cdb, cmd->t_task_cdb, TCM_MAX_COMMAND_SIZE); + memcpy(__entry->sense_data, cmd->sense_buffer, __entry->sense_length); + __assign_str(initiator, cmd->se_sess->se_node_acl->initiatorname); + ), + + TP_printk("%s <- LUN %03u status %s (sense len %d%s%s) %s data_length %6u CDB %s (TA:%s C:%02x)", + __get_str(initiator), __entry->unpacked_lun, + show_scsi_status_name(__entry->scsi_status), + __entry->sense_length, __entry->sense_length ? " / " : "", + __print_hex(__entry->sense_data, __entry->sense_length), + show_opcode_name(__entry->opcode), + __entry->data_length, __print_hex(__entry->cdb, 16), + show_task_attribute_name(__entry->task_attribute), + scsi_command_size(__entry->cdb) <= 16 ? + __entry->cdb[scsi_command_size(__entry->cdb) - 1] : + __entry->cdb[1] + ) +); + +#endif /* _TRACE_TARGET_H */ + +/* This part must be outside protection */ +#include -- cgit v1.2.3 From 09ceadc70383a4105c0372f9e83da2c0cb0a8017 Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Wed, 26 Jun 2013 17:36:18 -0700 Subject: target: Return correct sense data for IO past the end of a device We should use TCM_ADDRESS_OUT_OF_RANGE (-> sense data LOGICAL BLOCK ADDRESS OUT OF RANGE) for IOs past the end of a device instead of INVALID FIELD IN CDB. Signed-off-by: Roland Dreier Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_sbc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c index bbc5b0ee2bdc..ee0cb9d96929 100644 --- a/drivers/target/target_core_sbc.c +++ b/drivers/target/target_core_sbc.c @@ -581,7 +581,7 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops) pr_err("cmd exceeds last lba %llu " "(lba %llu, sectors %u)\n", end_lba, cmd->t_task_lba, sectors); - return TCM_INVALID_CDB_FIELD; + return TCM_ADDRESS_OUT_OF_RANGE; } size = sbc_get_size(cmd, sectors); -- cgit v1.2.3 From 8dc8632aa7bf1de7a56daea56a7011cbfff76678 Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Wed, 26 Jun 2013 17:36:19 -0700 Subject: target: Add (obsolete) checking for PMI/LBA fields in READ CAPACITY(10) The SBC-2 specification of READ CAPACITY(10) has PMI and LOGICAL BLOCK ADDRESS fields in the CDB; in SBC-3 these fields are simply listed as obsolete. However, SBC-2 also has the language If the PMI bit is set to zero and the LOGICAL BLOCK ADDRESS field is not set to zero, the device server shall terminate the command with CHECK CONDITION status with the sense key set to ILLEGAL REQUEST and the additional sense code set to INVALID FIELD IN CDB. and in fact at least the Windows SCSI compliance test checks this behavior. Since no one following SBC-3 is going to set these fields, we might as well include the check from SBC-2 and pass this test. Signed-off-by: Roland Dreier Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_sbc.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c index ee0cb9d96929..8a462773d0c8 100644 --- a/drivers/target/target_core_sbc.c +++ b/drivers/target/target_core_sbc.c @@ -38,11 +38,27 @@ static sense_reason_t sbc_emulate_readcapacity(struct se_cmd *cmd) { struct se_device *dev = cmd->se_dev; + unsigned char *cdb = cmd->t_task_cdb; unsigned long long blocks_long = dev->transport->get_blocks(dev); unsigned char *rbuf; unsigned char buf[8]; u32 blocks; + /* + * SBC-2 says: + * If the PMI bit is set to zero and the LOGICAL BLOCK + * ADDRESS field is not set to zero, the device server shall + * terminate the command with CHECK CONDITION status with + * the sense key set to ILLEGAL REQUEST and the additional + * sense code set to INVALID FIELD IN CDB. + * + * In SBC-3, these fields are obsolete, but some SCSI + * compliance tests actually check this, so we might as well + * follow SBC-2. + */ + if (!(cdb[8] & 1) && !!(cdb[2] | cdb[3] | cdb[4] | cdb[5])) + return TCM_INVALID_CDB_FIELD; + if (blocks_long >= 0x00000000ffffffff) blocks = 0xffffffff; else -- cgit v1.2.3 From c5a2adbfcbc1d364371cd81c4180c7a83b73c5a0 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Mon, 1 Jul 2013 15:11:21 -0700 Subject: iser-target: Add vendor_err debug output Add output for ib_wc.vendor_err in isert_cq_[t,r]x_work(), which is useful for debugging future issues. Reported-by: Or Gerlitz Signed-off-by: Nicholas Bellinger --- drivers/infiniband/ulp/isert/ib_isert.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index d6cc5999d793..66712bd7dc11 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c @@ -1487,6 +1487,7 @@ isert_cq_tx_work(struct work_struct *work) } else { pr_debug("TX wc.status != IB_WC_SUCCESS >>>>>>>>>>>>>>\n"); pr_debug("TX wc.status: 0x%08x\n", wc.status); + pr_debug("TX wc.vendor_err: 0x%08x\n", wc.vendor_err); atomic_dec(&isert_conn->post_send_buf_count); isert_cq_comp_err(tx_desc, isert_conn); } @@ -1526,9 +1527,11 @@ isert_cq_rx_work(struct work_struct *work) isert_rx_completion(rx_desc, isert_conn, xfer_len); } else { pr_debug("RX wc.status != IB_WC_SUCCESS >>>>>>>>>>>>>>\n"); - if (wc.status != IB_WC_WR_FLUSH_ERR) + if (wc.status != IB_WC_WR_FLUSH_ERR) { pr_debug("RX wc.status: 0x%08x\n", wc.status); - + pr_debug("RX wc.vendor_err: 0x%08x\n", + wc.vendor_err); + } isert_conn->post_recv_buf_count--; isert_cq_comp_err(NULL, isert_conn); } -- cgit v1.2.3 From dbbc5d11074fc017488e6bdaaf9238d53e35ab07 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Wed, 3 Jul 2013 19:39:37 -0700 Subject: iser-target: Rename sense_buf_[dma,len] to pdu_[dma,len] Now that these two variables are used for REJECT payloads as well as SCSI response sense payloads, rename them to something that makes more sense. Signed-off-by: Nicholas Bellinger --- drivers/infiniband/ulp/isert/ib_isert.c | 30 +++++++++++++++--------------- drivers/infiniband/ulp/isert/ib_isert.h | 4 ++-- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index 66712bd7dc11..fa4251803577 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c @@ -1286,11 +1286,11 @@ static void isert_completion_put(struct iser_tx_desc *tx_desc, struct isert_cmd *isert_cmd, struct ib_device *ib_dev) { - if (isert_cmd->sense_buf_dma != 0) { - pr_debug("Calling ib_dma_unmap_single for isert_cmd->sense_buf_dma\n"); - ib_dma_unmap_single(ib_dev, isert_cmd->sense_buf_dma, - isert_cmd->sense_buf_len, DMA_TO_DEVICE); - isert_cmd->sense_buf_dma = 0; + if (isert_cmd->pdu_buf_dma != 0) { + pr_debug("Calling ib_dma_unmap_single for isert_cmd->pdu_buf_dma\n"); + ib_dma_unmap_single(ib_dev, isert_cmd->pdu_buf_dma, + isert_cmd->pdu_buf_len, DMA_TO_DEVICE); + isert_cmd->pdu_buf_dma = 0; } isert_unmap_tx_desc(tx_desc, ib_dev); @@ -1588,7 +1588,7 @@ isert_put_response(struct iscsi_conn *conn, struct iscsi_cmd *cmd) (cmd->se_cmd.se_cmd_flags & SCF_EMULATED_TASK_SENSE))) { struct ib_device *ib_dev = isert_conn->conn_cm_id->device; struct ib_sge *tx_dsg = &isert_cmd->tx_desc.tx_sg[1]; - u32 padding, sense_len; + u32 padding, pdu_len; put_unaligned_be16(cmd->se_cmd.scsi_sense_length, cmd->sense_buffer); @@ -1596,15 +1596,15 @@ isert_put_response(struct iscsi_conn *conn, struct iscsi_cmd *cmd) padding = -(cmd->se_cmd.scsi_sense_length) & 3; hton24(hdr->dlength, (u32)cmd->se_cmd.scsi_sense_length); - sense_len = cmd->se_cmd.scsi_sense_length + padding; + pdu_len = cmd->se_cmd.scsi_sense_length + padding; - isert_cmd->sense_buf_dma = ib_dma_map_single(ib_dev, - (void *)cmd->sense_buffer, sense_len, + isert_cmd->pdu_buf_dma = ib_dma_map_single(ib_dev, + (void *)cmd->sense_buffer, pdu_len, DMA_TO_DEVICE); - isert_cmd->sense_buf_len = sense_len; - tx_dsg->addr = isert_cmd->sense_buf_dma; - tx_dsg->length = sense_len; + isert_cmd->pdu_buf_len = pdu_len; + tx_dsg->addr = isert_cmd->pdu_buf_dma; + tx_dsg->length = pdu_len; tx_dsg->lkey = isert_conn->conn_mr->lkey; isert_cmd->tx_desc.num_sge = 2; } @@ -1692,11 +1692,11 @@ isert_put_reject(struct iscsi_cmd *cmd, struct iscsi_conn *conn) isert_init_tx_hdrs(isert_conn, &isert_cmd->tx_desc); hton24(hdr->dlength, ISCSI_HDR_LEN); - isert_cmd->sense_buf_dma = ib_dma_map_single(ib_dev, + isert_cmd->pdu_buf_dma = ib_dma_map_single(ib_dev, (void *)cmd->buf_ptr, ISCSI_HDR_LEN, DMA_TO_DEVICE); - isert_cmd->sense_buf_len = ISCSI_HDR_LEN; - tx_dsg->addr = isert_cmd->sense_buf_dma; + isert_cmd->pdu_buf_len = ISCSI_HDR_LEN; + tx_dsg->addr = isert_cmd->pdu_buf_dma; tx_dsg->length = ISCSI_HDR_LEN; tx_dsg->lkey = isert_conn->conn_mr->lkey; isert_cmd->tx_desc.num_sge = 2; diff --git a/drivers/infiniband/ulp/isert/ib_isert.h b/drivers/infiniband/ulp/isert/ib_isert.h index 5795c82a2306..191117b5b508 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.h +++ b/drivers/infiniband/ulp/isert/ib_isert.h @@ -61,8 +61,8 @@ struct isert_cmd { uint32_t write_stag; uint64_t read_va; uint64_t write_va; - u64 sense_buf_dma; - u32 sense_buf_len; + u64 pdu_buf_dma; + u32 pdu_buf_len; u32 read_va_off; u32 write_va_off; u32 rdma_wr_num; -- cgit v1.2.3 From adb54c29310e8785e4f2eb0dc76e0e2fde943b11 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Fri, 14 Jun 2013 16:47:15 -0700 Subject: iser-target: Add support for ISCSI_OP_TEXT opcode + payload handling This patch adds isert_handle_text_cmd() to handle incoming ISCSI_OP_TEXT PDU processing, along with isert_put_text_rsp() for posting ISCSI_OP_TEXT_RSP ib_send_wr response. It copies ISCSI_OP_TEXT payload using unsolicited payload at &iser_rx_desc->data[0] into iscsi_cmd->text_in_ptr for usage with outgoing isert_put_text_rsp() -> iscsit_build_text_rsp() v2 changes: - Let iscsit_build_text_rsp() determine any extra padding Reported-by: Or Gerlitz Cc: Or Gerlitz Cc: Mike Christie Signed-off-by: Nicholas Bellinger --- drivers/infiniband/ulp/isert/ib_isert.c | 89 ++++++++++++++++++++++++++++++++- 1 file changed, 88 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index fa4251803577..f02bfcc282e5 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c @@ -1032,6 +1032,33 @@ isert_handle_nop_out(struct isert_conn *isert_conn, struct isert_cmd *isert_cmd, return iscsit_process_nop_out(conn, cmd, hdr); } +static int +isert_handle_text_cmd(struct isert_conn *isert_conn, struct isert_cmd *isert_cmd, + struct iser_rx_desc *rx_desc, struct iscsi_text *hdr) +{ + struct iscsi_cmd *cmd = &isert_cmd->iscsi_cmd; + struct iscsi_conn *conn = isert_conn->conn; + u32 payload_length = ntoh24(hdr->dlength); + int rc; + unsigned char *text_in; + + rc = iscsit_setup_text_cmd(conn, cmd, hdr); + if (rc < 0) + return rc; + + text_in = kzalloc(payload_length, GFP_KERNEL); + if (!text_in) { + pr_err("Unable to allocate text_in of payload_length: %u\n", + payload_length); + return -ENOMEM; + } + cmd->text_in_ptr = text_in; + + memcpy(cmd->text_in_ptr, &rx_desc->data[0], payload_length); + + return iscsit_process_text_cmd(conn, cmd, hdr); +} + static int isert_rx_opcode(struct isert_conn *isert_conn, struct iser_rx_desc *rx_desc, uint32_t read_stag, uint64_t read_va, @@ -1091,6 +1118,15 @@ isert_rx_opcode(struct isert_conn *isert_conn, struct iser_rx_desc *rx_desc, SECONDS_FOR_LOGOUT_COMP * HZ); break; + case ISCSI_OP_TEXT: + cmd = iscsit_allocate_cmd(conn, GFP_KERNEL); + if (!cmd) + break; + + isert_cmd = container_of(cmd, struct isert_cmd, iscsi_cmd); + ret = isert_handle_text_cmd(isert_conn, isert_cmd, + rx_desc, (struct iscsi_text *)hdr); + break; default: pr_err("Got unknown iSCSI OpCode: 0x%02x\n", opcode); dump_stack(); @@ -1245,6 +1281,7 @@ isert_put_cmd(struct isert_cmd *isert_cmd) break; case ISCSI_OP_REJECT: case ISCSI_OP_NOOP_OUT: + case ISCSI_OP_TEXT: spin_lock_bh(&conn->cmd_lock); if (!list_empty(&cmd->i_conn_node)) list_del(&cmd->i_conn_node); @@ -1366,6 +1403,11 @@ isert_do_control_comp(struct work_struct *work) isert_conn->logout_posted = true; iscsit_logout_post_handler(cmd, cmd->conn); break; + case ISTATE_SEND_TEXTRSP: + atomic_dec(&isert_conn->post_send_buf_count); + cmd->i_state = ISTATE_SENT_STATUS; + isert_completion_put(&isert_cmd->tx_desc, isert_cmd, ib_dev); + break; default: pr_err("Unknown do_control_comp i_state %d\n", cmd->i_state); dump_stack(); @@ -1383,7 +1425,8 @@ isert_response_completion(struct iser_tx_desc *tx_desc, if (cmd->i_state == ISTATE_SEND_TASKMGTRSP || cmd->i_state == ISTATE_SEND_LOGOUTRSP || - cmd->i_state == ISTATE_SEND_REJECT) { + cmd->i_state == ISTATE_SEND_REJECT || + cmd->i_state == ISTATE_SEND_TEXTRSP) { isert_unmap_tx_desc(tx_desc, ib_dev); INIT_WORK(&isert_cmd->comp_work, isert_do_control_comp); @@ -1708,6 +1751,47 @@ isert_put_reject(struct iscsi_cmd *cmd, struct iscsi_conn *conn) return isert_post_response(isert_conn, isert_cmd); } +static int +isert_put_text_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn) +{ + struct isert_cmd *isert_cmd = container_of(cmd, + struct isert_cmd, iscsi_cmd); + struct isert_conn *isert_conn = (struct isert_conn *)conn->context; + struct ib_send_wr *send_wr = &isert_cmd->tx_desc.send_wr; + struct iscsi_text_rsp *hdr = + (struct iscsi_text_rsp *)&isert_cmd->tx_desc.iscsi_header; + u32 txt_rsp_len; + int rc; + + isert_create_send_desc(isert_conn, isert_cmd, &isert_cmd->tx_desc); + rc = iscsit_build_text_rsp(cmd, conn, hdr); + if (rc < 0) + return rc; + + txt_rsp_len = rc; + isert_init_tx_hdrs(isert_conn, &isert_cmd->tx_desc); + + if (txt_rsp_len) { + struct ib_device *ib_dev = isert_conn->conn_cm_id->device; + struct ib_sge *tx_dsg = &isert_cmd->tx_desc.tx_sg[1]; + void *txt_rsp_buf = cmd->buf_ptr; + + isert_cmd->pdu_buf_dma = ib_dma_map_single(ib_dev, + txt_rsp_buf, txt_rsp_len, DMA_TO_DEVICE); + + isert_cmd->pdu_buf_len = txt_rsp_len; + tx_dsg->addr = isert_cmd->pdu_buf_dma; + tx_dsg->length = txt_rsp_len; + tx_dsg->lkey = isert_conn->conn_mr->lkey; + isert_cmd->tx_desc.num_sge = 2; + } + isert_init_send_wr(isert_cmd, send_wr); + + pr_debug("Posting Text Response IB_WR_SEND >>>>>>>>>>>>>>>>>>>>>>\n"); + + return isert_post_response(isert_conn, isert_cmd); +} + static int isert_build_rdma_wr(struct isert_conn *isert_conn, struct isert_cmd *isert_cmd, struct ib_sge *ib_sge, struct ib_send_wr *send_wr, @@ -2006,6 +2090,9 @@ isert_response_queue(struct iscsi_conn *conn, struct iscsi_cmd *cmd, int state) case ISTATE_SEND_REJECT: ret = isert_put_reject(cmd, conn); break; + case ISTATE_SEND_TEXTRSP: + ret = isert_put_text_rsp(cmd, conn); + break; case ISTATE_SEND_STATUS: /* * Special case for sending non GOOD SCSI status from TX thread -- cgit v1.2.3 From 0fbfc46fb0b2f543a8b539e94c6c293ebc0b05a6 Mon Sep 17 00:00:00 2001 From: Jörn Engel Date: Wed, 3 Jul 2013 11:35:11 -0400 Subject: iscsi-target: Fix tfc_tpg_nacl_auth_cit configfs length overflow This patch fixes a potential buffer overflow while processing iscsi_node_auth input for configfs attributes within NodeACL tfc_tpg_nacl_auth_cit context. Signed-off-by: Joern Engel Cc: stable@vger.kernel.org Signed-off-by: Nicholas Bellinger --- drivers/target/iscsi/iscsi_target_configfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/target/iscsi/iscsi_target_configfs.c b/drivers/target/iscsi/iscsi_target_configfs.c index e251849a6140..88bc805e998f 100644 --- a/drivers/target/iscsi/iscsi_target_configfs.c +++ b/drivers/target/iscsi/iscsi_target_configfs.c @@ -473,7 +473,7 @@ static ssize_t __iscsi_##prefix##_store_##name( \ if (!capable(CAP_SYS_ADMIN)) \ return -EPERM; \ \ - snprintf(auth->name, PAGE_SIZE, "%s", page); \ + snprintf(auth->name, sizeof(auth->name), "%s", page); \ if (!strncmp("NULL", auth->name, 4)) \ auth->naf_flags &= ~flags; \ else \ -- cgit v1.2.3 From 37b32c6faf0e0e40c65c1014df7527ececdd3f9a Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Sat, 6 Jul 2013 16:48:55 -0700 Subject: iscsi-target: Fix tfc_tpg_auth_cit configfs length overflow This patch fixes another potential buffer overflow while processing iscsi_node_auth input for configfs attributes in v3.11 for-next TPG tfc_tpg_auth_cit context. Reported-by: Joern Engel Signed-off-by: Nicholas Bellinger --- drivers/target/iscsi/iscsi_target_configfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/target/iscsi/iscsi_target_configfs.c b/drivers/target/iscsi/iscsi_target_configfs.c index 88bc805e998f..b41d4786fa08 100644 --- a/drivers/target/iscsi/iscsi_target_configfs.c +++ b/drivers/target/iscsi/iscsi_target_configfs.c @@ -1081,7 +1081,7 @@ static ssize_t __iscsi_##prefix##_store_##name( \ if (!capable(CAP_SYS_ADMIN)) \ return -EPERM; \ \ - snprintf(auth->name, PAGE_SIZE, "%s", page); \ + snprintf(auth->name, sizeof(auth->name), "%s", page); \ if (!(strncmp("NULL", auth->name, 4))) \ auth->naf_flags &= ~flags; \ else \ -- cgit v1.2.3 From ad7babd23726b442b0b5fd92d8bd0611af5e5d6a Mon Sep 17 00:00:00 2001 From: Jörn Engel Date: Wed, 3 Jul 2013 11:35:11 -0400 Subject: iscsi-target: kstrtou* configfs attribute parameter cleanups This patch includes the conversion of iscsi-target configfs attributes for NetworkPortal, NodeACL, TPG, IQN and Discovery groups to use kstrtou*() instead of simple_strtou*(). It also cleans up new-line usage during iscsi_tpg_param_store_##name to use isspace(). Signed-off-by: Joern Engel Signed-off-by: Nicholas Bellinger --- drivers/target/iscsi/iscsi_target_configfs.c | 53 +++++++++++++++++----------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/drivers/target/iscsi/iscsi_target_configfs.c b/drivers/target/iscsi/iscsi_target_configfs.c index b41d4786fa08..684d73fcbedf 100644 --- a/drivers/target/iscsi/iscsi_target_configfs.c +++ b/drivers/target/iscsi/iscsi_target_configfs.c @@ -20,6 +20,7 @@ ****************************************************************************/ #include +#include #include #include #include @@ -78,11 +79,12 @@ static ssize_t lio_target_np_store_sctp( struct iscsi_tpg_np *tpg_np = container_of(se_tpg_np, struct iscsi_tpg_np, se_tpg_np); struct iscsi_tpg_np *tpg_np_sctp = NULL; - char *endptr; u32 op; int ret; - op = simple_strtoul(page, &endptr, 0); + ret = kstrtou32(page, 0, &op); + if (ret) + return ret; if ((op != 1) && (op != 0)) { pr_err("Illegal value for tpg_enable: %u\n", op); return -EINVAL; @@ -381,11 +383,12 @@ static ssize_t iscsi_nacl_attrib_store_##name( \ { \ struct iscsi_node_acl *nacl = container_of(se_nacl, struct iscsi_node_acl, \ se_node_acl); \ - char *endptr; \ u32 val; \ int ret; \ \ - val = simple_strtoul(page, &endptr, 0); \ + ret = kstrtou32(page, 0, &val); \ + if (ret) \ + return ret; \ ret = iscsit_na_##name(nacl, val); \ if (ret < 0) \ return ret; \ @@ -788,11 +791,12 @@ static ssize_t lio_target_nacl_store_cmdsn_depth( struct iscsi_portal_group *tpg = container_of(se_tpg, struct iscsi_portal_group, tpg_se_tpg); struct config_item *acl_ci, *tpg_ci, *wwn_ci; - char *endptr; u32 cmdsn_depth = 0; int ret; - cmdsn_depth = simple_strtoul(page, &endptr, 0); + ret = kstrtou32(page, 0, &cmdsn_depth); + if (ret) + return ret; if (cmdsn_depth > TA_DEFAULT_CMDSN_DEPTH_MAX) { pr_err("Passed cmdsn_depth: %u exceeds" " TA_DEFAULT_CMDSN_DEPTH_MAX: %u\n", cmdsn_depth, @@ -976,14 +980,15 @@ static ssize_t iscsi_tpg_attrib_store_##name( \ { \ struct iscsi_portal_group *tpg = container_of(se_tpg, \ struct iscsi_portal_group, tpg_se_tpg); \ - char *endptr; \ u32 val; \ int ret; \ \ if (iscsit_get_tpg(tpg) < 0) \ return -EINVAL; \ \ - val = simple_strtoul(page, &endptr, 0); \ + ret = kstrtou32(page, 0, &val); \ + if (ret) \ + goto out; \ ret = iscsit_ta_##name(tpg, val); \ if (ret < 0) \ goto out; \ @@ -1211,13 +1216,14 @@ static ssize_t iscsi_tpg_param_store_##name( \ struct iscsi_portal_group *tpg = container_of(se_tpg, \ struct iscsi_portal_group, tpg_se_tpg); \ char *buf; \ - int ret; \ + int ret, len; \ \ buf = kzalloc(PAGE_SIZE, GFP_KERNEL); \ if (!buf) \ return -ENOMEM; \ - snprintf(buf, PAGE_SIZE, "%s=%s", __stringify(name), page); \ - buf[strlen(buf)-1] = '\0'; /* Kill newline */ \ + len = snprintf(buf, PAGE_SIZE, "%s=%s", __stringify(name), page); \ + if (isspace(buf[len-1])) \ + buf[len-1] = '\0'; /* Kill newline */ \ \ if (iscsit_get_tpg(tpg) < 0) { \ kfree(buf); \ @@ -1354,11 +1360,12 @@ static ssize_t lio_target_tpg_store_enable( { struct iscsi_portal_group *tpg = container_of(se_tpg, struct iscsi_portal_group, tpg_se_tpg); - char *endptr; u32 op; - int ret = 0; + int ret; - op = simple_strtoul(page, &endptr, 0); + ret = kstrtou32(page, 0, &op); + if (ret) + return ret; if ((op != 1) && (op != 0)) { pr_err("Illegal value for tpg_enable: %u\n", op); return -EINVAL; @@ -1406,15 +1413,15 @@ static struct se_portal_group *lio_target_tiqn_addtpg( { struct iscsi_portal_group *tpg; struct iscsi_tiqn *tiqn; - char *tpgt_str, *end_ptr; - int ret = 0; - unsigned short int tpgt; + char *tpgt_str; + int ret; + u16 tpgt; tiqn = container_of(wwn, struct iscsi_tiqn, tiqn_wwn); /* * Only tpgt_# directory groups can be created below * target/iscsi/iqn.superturodiskarry/ - */ + */ tpgt_str = strstr(name, "tpgt_"); if (!tpgt_str) { pr_err("Unable to locate \"tpgt_#\" directory" @@ -1422,7 +1429,9 @@ static struct se_portal_group *lio_target_tiqn_addtpg( return NULL; } tpgt_str += 5; /* Skip ahead of "tpgt_" */ - tpgt = (unsigned short int) simple_strtoul(tpgt_str, &end_ptr, 0); + ret = kstrtou16(tpgt_str, 0, &tpgt); + if (ret) + return NULL; tpg = iscsit_alloc_portal_group(tiqn, tpgt); if (!tpg) @@ -1630,10 +1639,12 @@ static ssize_t iscsi_disc_store_enforce_discovery_auth( { struct iscsi_param *param; struct iscsi_portal_group *discovery_tpg = iscsit_global->discovery_tpg; - char *endptr; u32 op; + int err; - op = simple_strtoul(page, &endptr, 0); + err = kstrtou32(page, 0, &op); + if (err) + return -EINVAL; if ((op != 1) && (op != 0)) { pr_err("Illegal value for enforce_discovery_auth:" " %u\n", op); -- cgit v1.2.3 From 11fee8a751670cf6d60b1912e2e9cb1c7e392842 Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Wed, 3 Jul 2013 11:22:16 -0400 Subject: target: remove unused codes from enum tcm_tmrsp_table Three have been checked for but were never set. Remove the dead code. Also renumbers the remaining ones to a) get rid of the holes after the removal and b) avoid a collision between TMR_FUNCTION_COMPLETE==0 and the uninitialized case. If we failed to set a code, we should rather fall into the default case then return success. Signed-off-by: Joern Engel Signed-off-by: Nicholas Bellinger --- drivers/target/iscsi/iscsi_target.c | 2 -- drivers/target/tcm_fc/tfc_cmd.c | 3 --- include/target/target_core_base.h | 13 +++++-------- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index c30ec1d5756e..f73da43cdf9e 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -3286,8 +3286,6 @@ static u8 iscsit_convert_tcm_tmr_rsp(struct se_tmr_req *se_tmr) return ISCSI_TMF_RSP_NO_LUN; case TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED: return ISCSI_TMF_RSP_NOT_SUPPORTED; - case TMR_FUNCTION_AUTHORIZATION_FAILED: - return ISCSI_TMF_RSP_AUTH_FAILED; case TMR_FUNCTION_REJECTED: default: return ISCSI_TMF_RSP_REJECTED; diff --git a/drivers/target/tcm_fc/tfc_cmd.c b/drivers/target/tcm_fc/tfc_cmd.c index b406f178ff39..7b6bb72fa475 100644 --- a/drivers/target/tcm_fc/tfc_cmd.c +++ b/drivers/target/tcm_fc/tfc_cmd.c @@ -413,10 +413,7 @@ int ft_queue_tm_resp(struct se_cmd *se_cmd) code = FCP_TMF_REJECTED; break; case TMR_TASK_DOES_NOT_EXIST: - case TMR_TASK_STILL_ALLEGIANT: - case TMR_TASK_FAILOVER_NOT_SUPPORTED: case TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED: - case TMR_FUNCTION_AUTHORIZATION_FAILED: default: code = FCP_TMF_FAILED; break; diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index d92ec6765f5d..e34fc904f2e1 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -218,14 +218,11 @@ enum tcm_tmreq_table { /* fabric independent task management response values */ enum tcm_tmrsp_table { - TMR_FUNCTION_COMPLETE = 0, - TMR_TASK_DOES_NOT_EXIST = 1, - TMR_LUN_DOES_NOT_EXIST = 2, - TMR_TASK_STILL_ALLEGIANT = 3, - TMR_TASK_FAILOVER_NOT_SUPPORTED = 4, - TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED = 5, - TMR_FUNCTION_AUTHORIZATION_FAILED = 6, - TMR_FUNCTION_REJECTED = 255, + TMR_FUNCTION_COMPLETE = 1, + TMR_TASK_DOES_NOT_EXIST = 2, + TMR_LUN_DOES_NOT_EXIST = 3, + TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED = 4, + TMR_FUNCTION_REJECTED = 5, }; /* -- cgit v1.2.3 From b79fafac70fc9bbe640b8193ed772eb850efdfe6 Mon Sep 17 00:00:00 2001 From: Joern Engel Date: Wed, 3 Jul 2013 11:22:17 -0400 Subject: target: make queue_tm_rsp() return void The return value wasn't checked by any of the callers. Assuming this is correct behaviour, we can simplify some code by not bothering to generate it. nab: Add srpt_queue_data_in() + srpt_queue_tm_rsp() nops around srpt_queue_response() void return Signed-off-by: Joern Engel Signed-off-by: Nicholas Bellinger --- drivers/infiniband/ulp/srpt/ib_srpt.c | 27 +++++++++++++++++---------- drivers/scsi/qla2xxx/tcm_qla2xxx.c | 4 +--- drivers/target/iscsi/iscsi_target_configfs.c | 3 +-- drivers/target/loopback/tcm_loop.c | 3 +-- drivers/target/sbp/sbp_target.c | 3 +-- drivers/target/tcm_fc/tcm_fc.h | 2 +- drivers/target/tcm_fc/tfc_cmd.c | 5 ++--- drivers/usb/gadget/tcm_usb_gadget.c | 3 +-- drivers/vhost/scsi.c | 4 ++-- include/target/target_core_fabric.h | 2 +- 10 files changed, 28 insertions(+), 28 deletions(-) diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c index 3f3f0416fbdd..653ac6bfc57a 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.c +++ b/drivers/infiniband/ulp/srpt/ib_srpt.c @@ -3011,7 +3011,7 @@ static u8 tcm_to_srp_tsk_mgmt_status(const int tcm_mgmt_status) * Callback function called by the TCM core. Must not block since it can be * invoked on the context of the IB completion handler. */ -static int srpt_queue_response(struct se_cmd *cmd) +static void srpt_queue_response(struct se_cmd *cmd) { struct srpt_rdma_ch *ch; struct srpt_send_ioctx *ioctx; @@ -3022,8 +3022,6 @@ static int srpt_queue_response(struct se_cmd *cmd) int resp_len; u8 srp_tm_status; - ret = 0; - ioctx = container_of(cmd, struct srpt_send_ioctx, cmd); ch = ioctx->ch; BUG_ON(!ch); @@ -3049,7 +3047,7 @@ static int srpt_queue_response(struct se_cmd *cmd) || WARN_ON_ONCE(state == SRPT_STATE_CMD_RSP_SENT))) { atomic_inc(&ch->req_lim_delta); srpt_abort_cmd(ioctx); - goto out; + return; } dir = ioctx->cmd.data_direction; @@ -3061,7 +3059,7 @@ static int srpt_queue_response(struct se_cmd *cmd) if (ret) { printk(KERN_ERR "xfer_data failed for tag %llu\n", ioctx->tag); - goto out; + return; } } @@ -3082,9 +3080,17 @@ static int srpt_queue_response(struct se_cmd *cmd) srpt_set_cmd_state(ioctx, SRPT_STATE_DONE); target_put_sess_cmd(ioctx->ch->sess, &ioctx->cmd); } +} -out: - return ret; +static int srpt_queue_data_in(struct se_cmd *cmd) +{ + srpt_queue_response(cmd); + return 0; +} + +static void srpt_queue_tm_rsp(struct se_cmd *cmd) +{ + srpt_queue_response(cmd); } static int srpt_queue_status(struct se_cmd *cmd) @@ -3097,7 +3103,8 @@ static int srpt_queue_status(struct se_cmd *cmd) (SCF_TRANSPORT_TASK_SENSE | SCF_EMULATED_TASK_SENSE)) WARN_ON(cmd->scsi_status != SAM_STAT_CHECK_CONDITION); ioctx->queue_status_only = true; - return srpt_queue_response(cmd); + srpt_queue_response(cmd); + return 0; } static void srpt_refresh_port_work(struct work_struct *work) @@ -3930,9 +3937,9 @@ static struct target_core_fabric_ops srpt_template = { .set_default_node_attributes = srpt_set_default_node_attrs, .get_task_tag = srpt_get_task_tag, .get_cmd_state = srpt_get_tcm_cmd_state, - .queue_data_in = srpt_queue_response, + .queue_data_in = srpt_queue_data_in, .queue_status = srpt_queue_status, - .queue_tm_rsp = srpt_queue_response, + .queue_tm_rsp = srpt_queue_tm_rsp, /* * Setup function pointers for generic logic in * target_core_fabric_configfs.c diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.c b/drivers/scsi/qla2xxx/tcm_qla2xxx.c index bb7eb909f0b1..75caa39b807d 100644 --- a/drivers/scsi/qla2xxx/tcm_qla2xxx.c +++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.c @@ -699,7 +699,7 @@ static int tcm_qla2xxx_queue_status(struct se_cmd *se_cmd) return qlt_xmit_response(cmd, xmit_type, se_cmd->scsi_status); } -static int tcm_qla2xxx_queue_tm_rsp(struct se_cmd *se_cmd) +static void tcm_qla2xxx_queue_tm_rsp(struct se_cmd *se_cmd) { struct se_tmr_req *se_tmr = se_cmd->se_tmr_req; struct qla_tgt_mgmt_cmd *mcmd = container_of(se_cmd, @@ -731,8 +731,6 @@ static int tcm_qla2xxx_queue_tm_rsp(struct se_cmd *se_cmd) * CTIO response packet. */ qlt_xmit_tm_rsp(mcmd); - - return 0; } /* Local pointer to allocated TCM configfs fabric module */ diff --git a/drivers/target/iscsi/iscsi_target_configfs.c b/drivers/target/iscsi/iscsi_target_configfs.c index 684d73fcbedf..7d4e19f39fe6 100644 --- a/drivers/target/iscsi/iscsi_target_configfs.c +++ b/drivers/target/iscsi/iscsi_target_configfs.c @@ -1790,13 +1790,12 @@ static int lio_queue_status(struct se_cmd *se_cmd) return 0; } -static int lio_queue_tm_rsp(struct se_cmd *se_cmd) +static void lio_queue_tm_rsp(struct se_cmd *se_cmd) { struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd); cmd->i_state = ISTATE_SEND_TASKMGTRSP; iscsit_add_cmd_to_response_queue(cmd, cmd->conn, cmd->i_state); - return 0; } static char *lio_tpg_get_endpoint_wwn(struct se_portal_group *se_tpg) diff --git a/drivers/target/loopback/tcm_loop.c b/drivers/target/loopback/tcm_loop.c index 7c908141cc8a..568ad25f25d3 100644 --- a/drivers/target/loopback/tcm_loop.c +++ b/drivers/target/loopback/tcm_loop.c @@ -786,7 +786,7 @@ static int tcm_loop_queue_status(struct se_cmd *se_cmd) return 0; } -static int tcm_loop_queue_tm_rsp(struct se_cmd *se_cmd) +static void tcm_loop_queue_tm_rsp(struct se_cmd *se_cmd) { struct se_tmr_req *se_tmr = se_cmd->se_tmr_req; struct tcm_loop_tmr *tl_tmr = se_tmr->fabric_tmr_ptr; @@ -796,7 +796,6 @@ static int tcm_loop_queue_tm_rsp(struct se_cmd *se_cmd) */ atomic_set(&tl_tmr->tmr_complete, 1); wake_up(&tl_tmr->tl_tmr_wait); - return 0; } static char *tcm_loop_dump_proto_id(struct tcm_loop_hba *tl_hba) diff --git a/drivers/target/sbp/sbp_target.c b/drivers/target/sbp/sbp_target.c index d3536f57444f..e51b09a04d52 100644 --- a/drivers/target/sbp/sbp_target.c +++ b/drivers/target/sbp/sbp_target.c @@ -1842,9 +1842,8 @@ static int sbp_queue_status(struct se_cmd *se_cmd) return sbp_send_sense(req); } -static int sbp_queue_tm_rsp(struct se_cmd *se_cmd) +static void sbp_queue_tm_rsp(struct se_cmd *se_cmd) { - return 0; } static int sbp_check_stop_free(struct se_cmd *se_cmd) diff --git a/drivers/target/tcm_fc/tcm_fc.h b/drivers/target/tcm_fc/tcm_fc.h index eea69358ced3..0dd54a44abcf 100644 --- a/drivers/target/tcm_fc/tcm_fc.h +++ b/drivers/target/tcm_fc/tcm_fc.h @@ -161,7 +161,7 @@ int ft_write_pending(struct se_cmd *); int ft_write_pending_status(struct se_cmd *); u32 ft_get_task_tag(struct se_cmd *); int ft_get_cmd_state(struct se_cmd *); -int ft_queue_tm_resp(struct se_cmd *); +void ft_queue_tm_resp(struct se_cmd *); /* * other internal functions. diff --git a/drivers/target/tcm_fc/tfc_cmd.c b/drivers/target/tcm_fc/tfc_cmd.c index 7b6bb72fa475..0e5a1caed176 100644 --- a/drivers/target/tcm_fc/tfc_cmd.c +++ b/drivers/target/tcm_fc/tfc_cmd.c @@ -394,14 +394,14 @@ static void ft_send_tm(struct ft_cmd *cmd) /* * Send status from completed task management request. */ -int ft_queue_tm_resp(struct se_cmd *se_cmd) +void ft_queue_tm_resp(struct se_cmd *se_cmd) { struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd); struct se_tmr_req *tmr = se_cmd->se_tmr_req; enum fcp_resp_rsp_codes code; if (cmd->aborted) - return 0; + return; switch (tmr->response) { case TMR_FUNCTION_COMPLETE: code = FCP_TMF_CMPL; @@ -421,7 +421,6 @@ int ft_queue_tm_resp(struct se_cmd *se_cmd) pr_debug("tmr fn %d resp %d fcp code %d\n", tmr->function, tmr->response, code); ft_send_resp_code(cmd, code); - return 0; } static void ft_send_work(struct work_struct *work); diff --git a/drivers/usb/gadget/tcm_usb_gadget.c b/drivers/usb/gadget/tcm_usb_gadget.c index 7cacd6ae818e..0ff33396eef3 100644 --- a/drivers/usb/gadget/tcm_usb_gadget.c +++ b/drivers/usb/gadget/tcm_usb_gadget.c @@ -1467,9 +1467,8 @@ static int usbg_get_cmd_state(struct se_cmd *se_cmd) return 0; } -static int usbg_queue_tm_rsp(struct se_cmd *se_cmd) +static void usbg_queue_tm_rsp(struct se_cmd *se_cmd) { - return 0; } static const char *usbg_check_wwn(const char *name) diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c index 1e5e82042f84..b35193807f0b 100644 --- a/drivers/vhost/scsi.c +++ b/drivers/vhost/scsi.c @@ -528,9 +528,9 @@ static int tcm_vhost_queue_status(struct se_cmd *se_cmd) return 0; } -static int tcm_vhost_queue_tm_rsp(struct se_cmd *se_cmd) +static void tcm_vhost_queue_tm_rsp(struct se_cmd *se_cmd) { - return 0; + return; } static void tcm_vhost_free_evt(struct vhost_scsi *vs, struct tcm_vhost_evt *evt) diff --git a/include/target/target_core_fabric.h b/include/target/target_core_fabric.h index 1dcce9cc99b9..7a16178424f9 100644 --- a/include/target/target_core_fabric.h +++ b/include/target/target_core_fabric.h @@ -61,7 +61,7 @@ struct target_core_fabric_ops { int (*get_cmd_state)(struct se_cmd *); int (*queue_data_in)(struct se_cmd *); int (*queue_status)(struct se_cmd *); - int (*queue_tm_rsp)(struct se_cmd *); + void (*queue_tm_rsp)(struct se_cmd *); /* * fabric module calls for target_core_fabric_configfs.c */ -- cgit v1.2.3 From ca40d24eb8fc3c194b4439493ecf6b2d703812b8 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Sun, 7 Jul 2013 17:45:08 -0700 Subject: iser-target: Ignore non TEXT + LOGOUT opcodes for discovery This patch adds a check in isert_rx_opcode() to ignore non TEXT + LOGOUT opcodes when SessionType=Discovery has been negotiated. Cc: Or Gerlitz Signed-off-by: Nicholas Bellinger --- drivers/infiniband/ulp/isert/ib_isert.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index f02bfcc282e5..ecd16541896b 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c @@ -1066,11 +1066,19 @@ isert_rx_opcode(struct isert_conn *isert_conn, struct iser_rx_desc *rx_desc, { struct iscsi_hdr *hdr = &rx_desc->iscsi_header; struct iscsi_conn *conn = isert_conn->conn; + struct iscsi_session *sess = conn->sess; struct iscsi_cmd *cmd; struct isert_cmd *isert_cmd; int ret = -EINVAL; u8 opcode = (hdr->opcode & ISCSI_OPCODE_MASK); + if (sess->sess_ops->SessionType && + (!(opcode & ISCSI_OP_TEXT) || !(opcode & ISCSI_OP_LOGOUT))) { + pr_err("Got illegal opcode: 0x%02x in SessionType=Discovery," + " ignoring\n", opcode); + return 0; + } + switch (opcode) { case ISCSI_OP_SCSI_CMD: cmd = iscsit_allocate_cmd(conn, GFP_KERNEL); -- cgit v1.2.3 From 2f28c8b31dc501027d9aa6acf496c5941736312b Mon Sep 17 00:00:00 2001 From: Jim Garlick Date: Wed, 29 May 2013 12:15:07 -0700 Subject: net/9p: add privport option to 9p tcp transport If the privport option is specified, the tcp transport binds local address to a reserved port before connecting to the 9p server. In some cases when 9P AUTH cannot be implemented, this is better than nothing. Signed-off-by: Jim Garlick Signed-off-by: Eric Van Hensbergen --- include/net/9p/transport.h | 3 +++ net/9p/trans_fd.c | 40 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/include/net/9p/transport.h b/include/net/9p/transport.h index adcbb20f6511..9a36d9297114 100644 --- a/include/net/9p/transport.h +++ b/include/net/9p/transport.h @@ -26,6 +26,9 @@ #ifndef NET_9P_TRANSPORT_H #define NET_9P_TRANSPORT_H +#define P9_DEF_MIN_RESVPORT (665U) +#define P9_DEF_MAX_RESVPORT (1023U) + /** * struct p9_trans_module - transport module interface * @list: used to maintain a list of currently available transports diff --git a/net/9p/trans_fd.c b/net/9p/trans_fd.c index 02efb25c2957..3ffda1b3799b 100644 --- a/net/9p/trans_fd.c +++ b/net/9p/trans_fd.c @@ -63,6 +63,7 @@ struct p9_fd_opts { int rfd; int wfd; u16 port; + int privport; }; /** @@ -87,12 +88,15 @@ struct p9_trans_fd { enum { /* Options that take integer arguments */ Opt_port, Opt_rfdno, Opt_wfdno, Opt_err, + /* Options that take no arguments */ + Opt_privport, }; static const match_table_t tokens = { {Opt_port, "port=%u"}, {Opt_rfdno, "rfdno=%u"}, {Opt_wfdno, "wfdno=%u"}, + {Opt_privport, "privport"}, {Opt_err, NULL}, }; @@ -161,6 +165,9 @@ static DEFINE_SPINLOCK(p9_poll_lock); static LIST_HEAD(p9_poll_pending_list); static DECLARE_WORK(p9_poll_work, p9_poll_workfn); +static unsigned int p9_ipport_resv_min = P9_DEF_MIN_RESVPORT; +static unsigned int p9_ipport_resv_max = P9_DEF_MAX_RESVPORT; + static void p9_mux_poll_stop(struct p9_conn *m) { unsigned long flags; @@ -741,7 +748,7 @@ static int parse_opts(char *params, struct p9_fd_opts *opts) if (!*p) continue; token = match_token(p, tokens, args); - if (token != Opt_err) { + if ((token != Opt_err) && (token != Opt_privport)) { r = match_int(&args[0], &option); if (r < 0) { p9_debug(P9_DEBUG_ERROR, @@ -759,6 +766,9 @@ static int parse_opts(char *params, struct p9_fd_opts *opts) case Opt_wfdno: opts->wfd = option; break; + case Opt_privport: + opts->privport = 1; + break; default: continue; } @@ -898,6 +908,24 @@ static inline int valid_ipaddr4(const char *buf) return 0; } +static int p9_bind_privport(struct socket *sock) +{ + struct sockaddr_in cl; + int port, err = -EINVAL; + + memset(&cl, 0, sizeof(cl)); + cl.sin_family = AF_INET; + cl.sin_addr.s_addr = INADDR_ANY; + for (port = p9_ipport_resv_max; port >= p9_ipport_resv_min; port--) { + cl.sin_port = htons((ushort)port); + err = kernel_bind(sock, (struct sockaddr *)&cl, sizeof(cl)); + if (err != -EADDRINUSE) + break; + } + return err; +} + + static int p9_fd_create_tcp(struct p9_client *client, const char *addr, char *args) { @@ -926,6 +954,16 @@ p9_fd_create_tcp(struct p9_client *client, const char *addr, char *args) return err; } + if (opts.privport) { + err = p9_bind_privport(csocket); + if (err < 0) { + pr_err("%s (%d): problem binding to privport\n", + __func__, task_pid_nr(current)); + sock_release(csocket); + return err; + } + } + err = csocket->ops->connect(csocket, (struct sockaddr *)&sin_server, sizeof(struct sockaddr_in), 0); -- cgit v1.2.3 From d9a738597faf7cd2edeec82ce8fd81969fed8390 Mon Sep 17 00:00:00 2001 From: Jim Garlick Date: Wed, 29 May 2013 12:09:39 -0700 Subject: fs/9p: xattr: add trusted and security namespaces Allow requests for security.* and trusted.* xattr name spaces to pass through to server. The new files are 99% cut and paste from fs/9p/xattr_user.c with the namespaces changed. It has the intended effect in superficial testing. I do not know much detail about how these namespaces are used, but passing them through to the server, which can decide whether to handle them or not, seems reasonable. I want to support a use case where an ext4 file system is mounted via 9P, then re-exported via samba to windows clients in a cluster. Windows wants to store xattrs such as security.NTACL. This works when ext4 directly backs samba, but not when 9P is inserted. This use case is documented here: http://code.google.com/p/diod/issues/detail?id=95 Signed-off-by: Jim Garlick Signed-off-by: Eric Van Hensbergen --- fs/9p/Kconfig | 13 ++++++++ fs/9p/Makefile | 4 ++- fs/9p/xattr.c | 4 +++ fs/9p/xattr.h | 2 ++ fs/9p/xattr_security.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++ fs/9p/xattr_trusted.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 182 insertions(+), 1 deletion(-) create mode 100644 fs/9p/xattr_security.c create mode 100644 fs/9p/xattr_trusted.c diff --git a/fs/9p/Kconfig b/fs/9p/Kconfig index 55abfd62654a..6489e1fc1afd 100644 --- a/fs/9p/Kconfig +++ b/fs/9p/Kconfig @@ -31,3 +31,16 @@ config 9P_FS_POSIX_ACL If you don't know what Access Control Lists are, say N endif + + +config 9P_FS_SECURITY + bool "9P Security Labels" + depends on 9P_FS + help + Security labels support alternative access control models + implemented by security modules like SELinux. This option + enables an extended attribute handler for file security + labels in the 9P filesystem. + + If you are not using a security module that requires using + extended attributes for file security labels, say N. diff --git a/fs/9p/Makefile b/fs/9p/Makefile index ab8c12780634..ff7be98f84f2 100644 --- a/fs/9p/Makefile +++ b/fs/9p/Makefile @@ -11,7 +11,9 @@ obj-$(CONFIG_9P_FS) := 9p.o v9fs.o \ fid.o \ xattr.o \ - xattr_user.o + xattr_user.o \ + xattr_trusted.o 9p-$(CONFIG_9P_FSCACHE) += cache.o 9p-$(CONFIG_9P_FS_POSIX_ACL) += acl.o +9p-$(CONFIG_9P_FS_SECURITY) += xattr_security.o diff --git a/fs/9p/xattr.c b/fs/9p/xattr.c index c45e016b190f..3c28cdfb8c47 100644 --- a/fs/9p/xattr.c +++ b/fs/9p/xattr.c @@ -167,9 +167,13 @@ ssize_t v9fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size) const struct xattr_handler *v9fs_xattr_handlers[] = { &v9fs_xattr_user_handler, + &v9fs_xattr_trusted_handler, #ifdef CONFIG_9P_FS_POSIX_ACL &v9fs_xattr_acl_access_handler, &v9fs_xattr_acl_default_handler, +#endif +#ifdef CONFIG_9P_FS_SECURITY + &v9fs_xattr_security_handler, #endif NULL }; diff --git a/fs/9p/xattr.h b/fs/9p/xattr.h index eec348a3df71..d3e2ea3840be 100644 --- a/fs/9p/xattr.h +++ b/fs/9p/xattr.h @@ -20,6 +20,8 @@ extern const struct xattr_handler *v9fs_xattr_handlers[]; extern struct xattr_handler v9fs_xattr_user_handler; +extern struct xattr_handler v9fs_xattr_trusted_handler; +extern struct xattr_handler v9fs_xattr_security_handler; extern const struct xattr_handler v9fs_xattr_acl_access_handler; extern const struct xattr_handler v9fs_xattr_acl_default_handler; diff --git a/fs/9p/xattr_security.c b/fs/9p/xattr_security.c new file mode 100644 index 000000000000..cb247a142a6e --- /dev/null +++ b/fs/9p/xattr_security.c @@ -0,0 +1,80 @@ +/* + * Copyright IBM Corporation, 2010 + * Author Aneesh Kumar K.V + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2.1 of the GNU Lesser General Public License + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + */ + + +#include +#include +#include +#include +#include "xattr.h" + +static int v9fs_xattr_security_get(struct dentry *dentry, const char *name, + void *buffer, size_t size, int type) +{ + int retval; + char *full_name; + size_t name_len; + size_t prefix_len = XATTR_SECURITY_PREFIX_LEN; + + if (name == NULL) + return -EINVAL; + + if (strcmp(name, "") == 0) + return -EINVAL; + + name_len = strlen(name); + full_name = kmalloc(prefix_len + name_len + 1 , GFP_KERNEL); + if (!full_name) + return -ENOMEM; + memcpy(full_name, XATTR_SECURITY_PREFIX, prefix_len); + memcpy(full_name+prefix_len, name, name_len); + full_name[prefix_len + name_len] = '\0'; + + retval = v9fs_xattr_get(dentry, full_name, buffer, size); + kfree(full_name); + return retval; +} + +static int v9fs_xattr_security_set(struct dentry *dentry, const char *name, + const void *value, size_t size, int flags, int type) +{ + int retval; + char *full_name; + size_t name_len; + size_t prefix_len = XATTR_SECURITY_PREFIX_LEN; + + if (name == NULL) + return -EINVAL; + + if (strcmp(name, "") == 0) + return -EINVAL; + + name_len = strlen(name); + full_name = kmalloc(prefix_len + name_len + 1 , GFP_KERNEL); + if (!full_name) + return -ENOMEM; + memcpy(full_name, XATTR_SECURITY_PREFIX, prefix_len); + memcpy(full_name + prefix_len, name, name_len); + full_name[prefix_len + name_len] = '\0'; + + retval = v9fs_xattr_set(dentry, full_name, value, size, flags); + kfree(full_name); + return retval; +} + +struct xattr_handler v9fs_xattr_security_handler = { + .prefix = XATTR_SECURITY_PREFIX, + .get = v9fs_xattr_security_get, + .set = v9fs_xattr_security_set, +}; diff --git a/fs/9p/xattr_trusted.c b/fs/9p/xattr_trusted.c new file mode 100644 index 000000000000..e30d33b8a3fb --- /dev/null +++ b/fs/9p/xattr_trusted.c @@ -0,0 +1,80 @@ +/* + * Copyright IBM Corporation, 2010 + * Author Aneesh Kumar K.V + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2.1 of the GNU Lesser General Public License + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + */ + + +#include +#include +#include +#include +#include "xattr.h" + +static int v9fs_xattr_trusted_get(struct dentry *dentry, const char *name, + void *buffer, size_t size, int type) +{ + int retval; + char *full_name; + size_t name_len; + size_t prefix_len = XATTR_TRUSTED_PREFIX_LEN; + + if (name == NULL) + return -EINVAL; + + if (strcmp(name, "") == 0) + return -EINVAL; + + name_len = strlen(name); + full_name = kmalloc(prefix_len + name_len + 1 , GFP_KERNEL); + if (!full_name) + return -ENOMEM; + memcpy(full_name, XATTR_TRUSTED_PREFIX, prefix_len); + memcpy(full_name+prefix_len, name, name_len); + full_name[prefix_len + name_len] = '\0'; + + retval = v9fs_xattr_get(dentry, full_name, buffer, size); + kfree(full_name); + return retval; +} + +static int v9fs_xattr_trusted_set(struct dentry *dentry, const char *name, + const void *value, size_t size, int flags, int type) +{ + int retval; + char *full_name; + size_t name_len; + size_t prefix_len = XATTR_TRUSTED_PREFIX_LEN; + + if (name == NULL) + return -EINVAL; + + if (strcmp(name, "") == 0) + return -EINVAL; + + name_len = strlen(name); + full_name = kmalloc(prefix_len + name_len + 1 , GFP_KERNEL); + if (!full_name) + return -ENOMEM; + memcpy(full_name, XATTR_TRUSTED_PREFIX, prefix_len); + memcpy(full_name + prefix_len, name, name_len); + full_name[prefix_len + name_len] = '\0'; + + retval = v9fs_xattr_set(dentry, full_name, value, size, flags); + kfree(full_name); + return retval; +} + +struct xattr_handler v9fs_xattr_trusted_handler = { + .prefix = XATTR_TRUSTED_PREFIX, + .get = v9fs_xattr_trusted_get, + .set = v9fs_xattr_trusted_set, +}; -- cgit v1.2.3 From ea071aa1365eaf8a79b33bd8699cb0811dcddf34 Mon Sep 17 00:00:00 2001 From: Simon Derr Date: Fri, 21 Jun 2013 15:32:34 +0200 Subject: 9P: Fix fcall allocation for rdma The current code assumes that when a request in the request array does have a tc, it also has a rc. This is normally true, but not always : when using RDMA, req->rc will temporarily be set to NULL after the request has been sent. That is usually OK though, as when the reply arrives, req->rc will be reassigned to a sane value before the request is recycled. But there is a catch : if the request is flushed, the reply will never arrive, and req->rc will be NULL, but not req->tc. This patch fixes p9_tag_alloc to take this into account. Signed-off-by: Simon Derr Signed-off-by: Eric Van Hensbergen --- net/9p/client.c | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/net/9p/client.c b/net/9p/client.c index 01f1779eba80..5828769d1f3d 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -258,27 +258,25 @@ p9_tag_alloc(struct p9_client *c, u16 tag, unsigned int max_size) req = &c->reqs[row][col]; if (!req->tc) { req->wq = kmalloc(sizeof(wait_queue_head_t), GFP_NOFS); - if (!req->wq) { - pr_err("Couldn't grow tag array\n"); - return ERR_PTR(-ENOMEM); - } + if (!req->wq) + goto grow_failed; + init_waitqueue_head(req->wq); req->tc = kmalloc(sizeof(struct p9_fcall) + alloc_msize, GFP_NOFS); + if (!req->tc) + goto grow_failed; + + req->tc->capacity = alloc_msize; + req->tc->sdata = (char *) req->tc + sizeof(struct p9_fcall); + } + if (!req->rc) { req->rc = kmalloc(sizeof(struct p9_fcall) + alloc_msize, GFP_NOFS); - if ((!req->tc) || (!req->rc)) { - pr_err("Couldn't grow tag array\n"); - kfree(req->tc); - kfree(req->rc); - kfree(req->wq); - req->tc = req->rc = NULL; - req->wq = NULL; - return ERR_PTR(-ENOMEM); - } - req->tc->capacity = alloc_msize; + if (!req->rc) + goto grow_failed; + req->rc->capacity = alloc_msize; - req->tc->sdata = (char *) req->tc + sizeof(struct p9_fcall); req->rc->sdata = (char *) req->rc + sizeof(struct p9_fcall); } @@ -288,7 +286,16 @@ p9_tag_alloc(struct p9_client *c, u16 tag, unsigned int max_size) req->tc->tag = tag-1; req->status = REQ_STATUS_ALLOC; - return &c->reqs[row][col]; + return req; + +grow_failed: + pr_err("Couldn't grow tag array\n"); + kfree(req->tc); + kfree(req->rc); + kfree(req->wq); + req->tc = req->rc = NULL; + req->wq = NULL; + return ERR_PTR(-ENOMEM); } /** -- cgit v1.2.3 From 17b6fd9d6dfa0faed3a25a6045f7456821ea140a Mon Sep 17 00:00:00 2001 From: Simon Derr Date: Fri, 21 Jun 2013 15:32:35 +0200 Subject: 9P/RDMA: rdma_request() needs not allocate req->rc p9_tag_alloc() takes care of that. Signed-off-by: Simon Derr Signed-off-by: Eric Van Hensbergen --- net/9p/trans_rdma.c | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/net/9p/trans_rdma.c b/net/9p/trans_rdma.c index 2c69ddd691a1..b1dfdf2078ff 100644 --- a/net/9p/trans_rdma.c +++ b/net/9p/trans_rdma.c @@ -427,26 +427,7 @@ static int rdma_request(struct p9_client *client, struct p9_req_t *req) err = -ENOMEM; goto err_close; } - - /* - * If the request has a buffer, steal it, otherwise - * allocate a new one. Typically, requests should already - * have receive buffers allocated and just swap them around - */ - if (!req->rc) { - req->rc = kmalloc(sizeof(struct p9_fcall)+client->msize, - GFP_NOFS); - if (req->rc) { - req->rc->sdata = (char *) req->rc + - sizeof(struct p9_fcall); - req->rc->capacity = client->msize; - } - } rpl_context->rc = req->rc; - if (!rpl_context->rc) { - err = -ENOMEM; - goto err_free2; - } /* * Post a receive buffer for this request. We need to ensure -- cgit v1.2.3 From 5387320d4814aa1e40b50529d960a8f2b3340535 Mon Sep 17 00:00:00 2001 From: Simon Derr Date: Fri, 21 Jun 2013 15:32:36 +0200 Subject: 9pnet: refactor struct p9_fcall alloc code Signed-off-by: Simon Derr Signed-off-by: Eric Van Hensbergen --- net/9p/client.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/net/9p/client.c b/net/9p/client.c index 5828769d1f3d..db5bf2480a33 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -204,6 +204,17 @@ free_and_return: return ret; } +struct p9_fcall *p9_fcall_alloc(int alloc_msize) +{ + struct p9_fcall *fc; + fc = kmalloc(sizeof(struct p9_fcall) + alloc_msize, GFP_NOFS); + if (!fc) + return NULL; + fc->capacity = alloc_msize; + fc->sdata = (char *) fc + sizeof(struct p9_fcall); + return fc; +} + /** * p9_tag_alloc - lookup/allocate a request by tag * @c: client session to lookup tag within @@ -256,29 +267,19 @@ p9_tag_alloc(struct p9_client *c, u16 tag, unsigned int max_size) col = tag % P9_ROW_MAXTAG; req = &c->reqs[row][col]; - if (!req->tc) { + if (!req->wq) { req->wq = kmalloc(sizeof(wait_queue_head_t), GFP_NOFS); if (!req->wq) goto grow_failed; - init_waitqueue_head(req->wq); - req->tc = kmalloc(sizeof(struct p9_fcall) + alloc_msize, - GFP_NOFS); - if (!req->tc) - goto grow_failed; - - req->tc->capacity = alloc_msize; - req->tc->sdata = (char *) req->tc + sizeof(struct p9_fcall); } - if (!req->rc) { - req->rc = kmalloc(sizeof(struct p9_fcall) + alloc_msize, - GFP_NOFS); - if (!req->rc) - goto grow_failed; - req->rc->capacity = alloc_msize; - req->rc->sdata = (char *) req->rc + sizeof(struct p9_fcall); - } + if (!req->tc) + req->tc = p9_fcall_alloc(alloc_msize); + if (!req->rc) + req->rc = p9_fcall_alloc(alloc_msize); + if (!req->tc || !req->rc) + goto grow_failed; p9pdu_reset(req->tc); p9pdu_reset(req->rc); -- cgit v1.2.3 From 3fcc62f4e8620fd5f85f957a5e708e69a20adb51 Mon Sep 17 00:00:00 2001 From: Simon Derr Date: Fri, 21 Jun 2013 15:32:37 +0200 Subject: 9P/RDMA: increase P9_RDMA_MAXSIZE to 1MB The current value is too low to get good performance. Signed-off-by: Simon Derr Signed-off-by: Eric Van Hensbergen --- net/9p/trans_rdma.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/net/9p/trans_rdma.c b/net/9p/trans_rdma.c index b1dfdf2078ff..b8b66d38f5b0 100644 --- a/net/9p/trans_rdma.c +++ b/net/9p/trans_rdma.c @@ -57,9 +57,7 @@ #define P9_RDMA_IRD 0 #define P9_RDMA_ORD 0 #define P9_RDMA_TIMEOUT 30000 /* 30 seconds */ -#define P9_RDMA_MAXSIZE (4*4096) /* Min SGE is 4, so we can - * safely advertise a maxsize - * of 64k */ +#define P9_RDMA_MAXSIZE (1024*1024) /* 1MB */ /** * struct p9_trans_rdma - RDMA transport instance -- cgit v1.2.3 From 47229ff85e5a0b0613df2288d212938aeb9687da Mon Sep 17 00:00:00 2001 From: Simon Derr Date: Fri, 21 Jun 2013 15:32:38 +0200 Subject: 9P/RDMA: Protect against duplicate replies A well-behaved server would not send twice the reply to a request. But if it ever happens... This additional check prevents the kernel from leaking memory and possibly more nasty consequences in that unlikely event. Signed-off-by: Simon Derr Signed-off-by: Eric Van Hensbergen --- net/9p/trans_rdma.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/net/9p/trans_rdma.c b/net/9p/trans_rdma.c index b8b66d38f5b0..274a9c1d3c3d 100644 --- a/net/9p/trans_rdma.c +++ b/net/9p/trans_rdma.c @@ -294,6 +294,13 @@ handle_recv(struct p9_client *client, struct p9_trans_rdma *rdma, if (!req) goto err_out; + /* Check that we have not yet received a reply for this request. + */ + if (unlikely(req->rc)) { + pr_err("Duplicate reply for request %d", tag); + goto err_out; + } + req->rc = c->rc; req->status = REQ_STATUS_RCVD; p9_client_cb(client, req); -- cgit v1.2.3 From fd453d0ed6c1dacef8eff466df473d62d63db1e9 Mon Sep 17 00:00:00 2001 From: Simon Derr Date: Fri, 21 Jun 2013 15:32:39 +0200 Subject: 9P/RDMA: Use a semaphore to protect the RQ The current code keeps track of the number of buffers posted in the RQ, and will prevent it from overflowing. But it does so by simply dropping post requests (And leaking memory in the process). When this happens there will actually be too few buffers posted, and soon the 9P server will complain about 'RNR retry counter exceeded' errors. Instead, use a semaphore, and block until the RQ is ready for another buffer to be posted. Signed-off-by: Simon Derr Signed-off-by: Eric Van Hensbergen --- net/9p/trans_rdma.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/net/9p/trans_rdma.c b/net/9p/trans_rdma.c index 274a9c1d3c3d..ad8dc331574b 100644 --- a/net/9p/trans_rdma.c +++ b/net/9p/trans_rdma.c @@ -73,7 +73,7 @@ * @sq_depth: The depth of the Send Queue * @sq_sem: Semaphore for the SQ * @rq_depth: The depth of the Receive Queue. - * @rq_count: Count of requests in the Receive Queue. + * @rq_sem: Semaphore for the RQ * @addr: The remote peer's address * @req_lock: Protects the active request list * @cm_done: Completion event for connection management tracking @@ -98,7 +98,7 @@ struct p9_trans_rdma { int sq_depth; struct semaphore sq_sem; int rq_depth; - atomic_t rq_count; + struct semaphore rq_sem; struct sockaddr_in addr; spinlock_t req_lock; @@ -341,8 +341,8 @@ static void cq_comp_handler(struct ib_cq *cq, void *cq_context) switch (c->wc_op) { case IB_WC_RECV: - atomic_dec(&rdma->rq_count); handle_recv(client, rdma, c, wc.status, wc.byte_len); + up(&rdma->rq_sem); break; case IB_WC_SEND: @@ -441,12 +441,14 @@ static int rdma_request(struct p9_client *client, struct p9_req_t *req) * outstanding request, so we must keep a count to avoid * overflowing the RQ. */ - if (atomic_inc_return(&rdma->rq_count) <= rdma->rq_depth) { - err = post_recv(client, rpl_context); - if (err) - goto err_free1; - } else - atomic_dec(&rdma->rq_count); + if (down_interruptible(&rdma->rq_sem)) + goto error; /* FIXME : -EINTR instead */ + + err = post_recv(client, rpl_context); + if (err) { + p9_debug(P9_DEBUG_FCALL, "POST RECV failed\n"); + goto err_free1; + } /* remove posted receive buffer from request structure */ req->rc = NULL; @@ -537,7 +539,7 @@ static struct p9_trans_rdma *alloc_rdma(struct p9_rdma_opts *opts) spin_lock_init(&rdma->req_lock); init_completion(&rdma->cm_done); sema_init(&rdma->sq_sem, rdma->sq_depth); - atomic_set(&rdma->rq_count, 0); + sema_init(&rdma->rq_sem, rdma->rq_depth); return rdma; } -- cgit v1.2.3 From b530e252e291c27fdcb1b73c72ad17f75c8bdba6 Mon Sep 17 00:00:00 2001 From: Simon Derr Date: Fri, 21 Jun 2013 15:32:40 +0200 Subject: 9P/RDMA: Do not free req->rc in error handling in rdma_request() rdma_request() should never be in charge of freeing rc. When an error occurs: * Either the rc buffer has been recv_post()'ed. then kfree()'ing it certainly is a bad idea. * Or is has not, and in that case req->rc still points to it, hence it needs not be freed. Signed-off-by: Simon Derr Signed-off-by: Eric Van Hensbergen --- net/9p/trans_rdma.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/net/9p/trans_rdma.c b/net/9p/trans_rdma.c index ad8dc331574b..1bd4c7150114 100644 --- a/net/9p/trans_rdma.c +++ b/net/9p/trans_rdma.c @@ -447,7 +447,7 @@ static int rdma_request(struct p9_client *client, struct p9_req_t *req) err = post_recv(client, rpl_context); if (err) { p9_debug(P9_DEBUG_FCALL, "POST RECV failed\n"); - goto err_free1; + goto err_free; } /* remove posted receive buffer from request structure */ @@ -457,7 +457,7 @@ static int rdma_request(struct p9_client *client, struct p9_req_t *req) c = kmalloc(sizeof *c, GFP_NOFS); if (!c) { err = -ENOMEM; - goto err_free1; + goto err_free; } c->req = req; @@ -486,13 +486,10 @@ static int rdma_request(struct p9_client *client, struct p9_req_t *req) error: kfree(c); - kfree(rpl_context->rc); kfree(rpl_context); p9_debug(P9_DEBUG_ERROR, "EIO\n"); return -EIO; - err_free1: - kfree(rpl_context->rc); - err_free2: + err_free: kfree(rpl_context); err_close: spin_lock_irqsave(&rdma->req_lock, flags); -- cgit v1.2.3 From 2f52d07cb75d96fcbb5b9ab72938590fa9ffb19d Mon Sep 17 00:00:00 2001 From: Simon Derr Date: Fri, 21 Jun 2013 15:32:41 +0200 Subject: 9P/RDMA: Improve error handling in rdma_request Most importantly: - do not free the recv context (rpl_context) after a successful post_recv() - but do free the send context (c) after a failed send. Signed-off-by: Simon Derr Signed-off-by: Eric Van Hensbergen --- net/9p/trans_rdma.c | 44 ++++++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/net/9p/trans_rdma.c b/net/9p/trans_rdma.c index 1bd4c7150114..926e72d00e57 100644 --- a/net/9p/trans_rdma.c +++ b/net/9p/trans_rdma.c @@ -430,7 +430,7 @@ static int rdma_request(struct p9_client *client, struct p9_req_t *req) rpl_context = kmalloc(sizeof *rpl_context, GFP_NOFS); if (!rpl_context) { err = -ENOMEM; - goto err_close; + goto recv_error; } rpl_context->rc = req->rc; @@ -441,13 +441,15 @@ static int rdma_request(struct p9_client *client, struct p9_req_t *req) * outstanding request, so we must keep a count to avoid * overflowing the RQ. */ - if (down_interruptible(&rdma->rq_sem)) - goto error; /* FIXME : -EINTR instead */ + if (down_interruptible(&rdma->rq_sem)) { + err = -EINTR; + goto recv_error; + } err = post_recv(client, rpl_context); if (err) { p9_debug(P9_DEBUG_FCALL, "POST RECV failed\n"); - goto err_free; + goto recv_error; } /* remove posted receive buffer from request structure */ @@ -457,15 +459,17 @@ static int rdma_request(struct p9_client *client, struct p9_req_t *req) c = kmalloc(sizeof *c, GFP_NOFS); if (!c) { err = -ENOMEM; - goto err_free; + goto send_error; } c->req = req; c->busa = ib_dma_map_single(rdma->cm_id->device, c->req->tc->sdata, c->req->tc->size, DMA_TO_DEVICE); - if (ib_dma_mapping_error(rdma->cm_id->device, c->busa)) - goto error; + if (ib_dma_mapping_error(rdma->cm_id->device, c->busa)) { + err = -EIO; + goto send_error; + } sge.addr = c->busa; sge.length = c->req->tc->size; @@ -479,19 +483,27 @@ static int rdma_request(struct p9_client *client, struct p9_req_t *req) wr.sg_list = &sge; wr.num_sge = 1; - if (down_interruptible(&rdma->sq_sem)) - goto error; + if (down_interruptible(&rdma->sq_sem)) { + err = -EINTR; + goto send_error; + } - return ib_post_send(rdma->qp, &wr, &bad_wr); + err = ib_post_send(rdma->qp, &wr, &bad_wr); + if (err) + goto send_error; - error: + /* Success */ + return 0; + + /* Handle errors that happened during or while preparing the send: */ + send_error: kfree(c); + p9_debug(P9_DEBUG_ERROR, "Error %d in rdma_request()\n", err); + return err; + + /* Handle errors that happened during or while preparing post_recv(): */ + recv_error: kfree(rpl_context); - p9_debug(P9_DEBUG_ERROR, "EIO\n"); - return -EIO; - err_free: - kfree(rpl_context); - err_close: spin_lock_irqsave(&rdma->req_lock, flags); if (rdma->state < P9_RDMA_CLOSING) { rdma->state = P9_RDMA_CLOSING; -- cgit v1.2.3 From 1cff33069a4a1ac9ed080756113ecd17ad408282 Mon Sep 17 00:00:00 2001 From: Simon Derr Date: Fri, 21 Jun 2013 15:32:42 +0200 Subject: 9P/RDMA: count posted buffers without a pending request In rdma_request(): If an error occurs between posting the recv and the send, there will be a reply context posted without a pending request. Since there is no way to "un-post" it, we remember it and skip post_recv() for the next request. Signed-off-by: Simon Derr Signed-off-by: Eric Van Hensbergen --- net/9p/client.c | 6 ++++-- net/9p/trans_rdma.c | 31 ++++++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/net/9p/client.c b/net/9p/client.c index db5bf2480a33..d18a0b22f62c 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -656,8 +656,10 @@ static int p9_client_flush(struct p9_client *c, struct p9_req_t *oldreq) return PTR_ERR(req); - /* if we haven't received a response for oldreq, - remove it from the list. */ + /* + * if we haven't received a response for oldreq, + * remove it from the list. + */ spin_lock(&c->lock); if (oldreq->status == REQ_STATUS_FLSH) list_del(&oldreq->req_list); diff --git a/net/9p/trans_rdma.c b/net/9p/trans_rdma.c index 926e72d00e57..8f68df5d2973 100644 --- a/net/9p/trans_rdma.c +++ b/net/9p/trans_rdma.c @@ -74,6 +74,8 @@ * @sq_sem: Semaphore for the SQ * @rq_depth: The depth of the Receive Queue. * @rq_sem: Semaphore for the RQ + * @excess_rc : Amount of posted Receive Contexts without a pending request. + * See rdma_request() * @addr: The remote peer's address * @req_lock: Protects the active request list * @cm_done: Completion event for connection management tracking @@ -99,6 +101,7 @@ struct p9_trans_rdma { struct semaphore sq_sem; int rq_depth; struct semaphore rq_sem; + atomic_t excess_rc; struct sockaddr_in addr; spinlock_t req_lock; @@ -426,6 +429,26 @@ static int rdma_request(struct p9_client *client, struct p9_req_t *req) struct p9_rdma_context *c = NULL; struct p9_rdma_context *rpl_context = NULL; + /* When an error occurs between posting the recv and the send, + * there will be a receive context posted without a pending request. + * Since there is no way to "un-post" it, we remember it and skip + * post_recv() for the next request. + * So here, + * see if we are this `next request' and need to absorb an excess rc. + * If yes, then drop and free our own, and do not recv_post(). + **/ + if (unlikely(atomic_read(&rdma->excess_rc) > 0)) { + if ((atomic_sub_return(1, &rdma->excess_rc) >= 0)) { + /* Got one ! */ + kfree(req->rc); + req->rc = NULL; + goto dont_need_post_recv; + } else { + /* We raced and lost. */ + atomic_inc(&rdma->excess_rc); + } + } + /* Allocate an fcall for the reply */ rpl_context = kmalloc(sizeof *rpl_context, GFP_NOFS); if (!rpl_context) { @@ -451,10 +474,10 @@ static int rdma_request(struct p9_client *client, struct p9_req_t *req) p9_debug(P9_DEBUG_FCALL, "POST RECV failed\n"); goto recv_error; } - /* remove posted receive buffer from request structure */ req->rc = NULL; +dont_need_post_recv: /* Post the request */ c = kmalloc(sizeof *c, GFP_NOFS); if (!c) { @@ -499,6 +522,11 @@ static int rdma_request(struct p9_client *client, struct p9_req_t *req) send_error: kfree(c); p9_debug(P9_DEBUG_ERROR, "Error %d in rdma_request()\n", err); + + /* Ach. + * We did recv_post(), but not send. We have one recv_post in excess. + */ + atomic_inc(&rdma->excess_rc); return err; /* Handle errors that happened during or while preparing post_recv(): */ @@ -549,6 +577,7 @@ static struct p9_trans_rdma *alloc_rdma(struct p9_rdma_opts *opts) init_completion(&rdma->cm_done); sema_init(&rdma->sq_sem, rdma->sq_depth); sema_init(&rdma->rq_sem, rdma->rq_depth); + atomic_set(&rdma->excess_rc, 0); return rdma; } -- cgit v1.2.3 From 80b45261a0b263536b043c5ccfc4ba4fc27c2acc Mon Sep 17 00:00:00 2001 From: Simon Derr Date: Fri, 21 Jun 2013 15:32:43 +0200 Subject: 9P: Add cancelled() to the transport functions. RDMA needs to post a buffer for each incoming reply. Hence it needs to keep count of these and needs to be aware of whether a flushed request has received a reply or not. This patch adds the cancelled() callback to the transport modules. It is called when RFLUSH has been received and that the corresponding request will never receive a reply. Signed-off-by: Simon Derr Signed-off-by: Eric Van Hensbergen --- include/net/9p/transport.h | 3 +++ net/9p/client.c | 12 +++++++++--- net/9p/trans_rdma.c | 11 +++++++++++ 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/include/net/9p/transport.h b/include/net/9p/transport.h index 9a36d9297114..d9fa68f26c41 100644 --- a/include/net/9p/transport.h +++ b/include/net/9p/transport.h @@ -40,6 +40,8 @@ * @close: member function to discard a connection on this transport * @request: member function to issue a request to the transport * @cancel: member function to cancel a request (if it hasn't been sent) + * @cancelled: member function to notify that a cancelled request will not + * not receive a reply * * This is the basic API for a transport module which is registered by the * transport module with the 9P core network module and used by the client @@ -58,6 +60,7 @@ struct p9_trans_module { void (*close) (struct p9_client *); int (*request) (struct p9_client *, struct p9_req_t *req); int (*cancel) (struct p9_client *, struct p9_req_t *req); + int (*cancelled)(struct p9_client *, struct p9_req_t *req); int (*zc_request)(struct p9_client *, struct p9_req_t *, char *, char *, int , int, int, int); }; diff --git a/net/9p/client.c b/net/9p/client.c index d18a0b22f62c..8b93cae2d11d 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -658,12 +658,18 @@ static int p9_client_flush(struct p9_client *c, struct p9_req_t *oldreq) /* * if we haven't received a response for oldreq, - * remove it from the list. + * remove it from the list, and notify the transport + * layer that the reply will never arrive. */ spin_lock(&c->lock); - if (oldreq->status == REQ_STATUS_FLSH) + if (oldreq->status == REQ_STATUS_FLSH) { list_del(&oldreq->req_list); - spin_unlock(&c->lock); + spin_unlock(&c->lock); + if (c->trans_mod->cancelled) + c->trans_mod->cancelled(c, req); + } else { + spin_unlock(&c->lock); + } p9_free_req(c, req); return 0; diff --git a/net/9p/trans_rdma.c b/net/9p/trans_rdma.c index 8f68df5d2973..928f2bb9bf8d 100644 --- a/net/9p/trans_rdma.c +++ b/net/9p/trans_rdma.c @@ -588,6 +588,17 @@ static int rdma_cancel(struct p9_client *client, struct p9_req_t *req) return 1; } +/* A request has been fully flushed without a reply. + * That means we have posted one buffer in excess. + */ +static int rdma_cancelled(struct p9_client *client, struct p9_req_t *req) +{ + struct p9_trans_rdma *rdma = client->trans; + + atomic_inc(&rdma->excess_rc); + return 0; +} + /** * trans_create_rdma - Transport method for creating atransport instance * @client: client instance -- cgit v1.2.3 From f2692ea8d5b535277bc06b315eabd32ef4e7a11c Mon Sep 17 00:00:00 2001 From: Gu Zheng Date: Fri, 28 Jun 2013 12:44:13 -0500 Subject: fs/9p: Remove the unused variable "err" in v9fs_vfs_getattr() Delete the unused variable "err" in v9fs_vfs_getattr() Signed-off-by: Gu Zheng Signed-off-by: Eric Van Hensbergen --- fs/9p/vfs_inode.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index d86edc8d3fd0..25b018efb8ab 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -1054,13 +1054,11 @@ static int v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) { - int err; struct v9fs_session_info *v9ses; struct p9_fid *fid; struct p9_wstat *st; p9_debug(P9_DEBUG_VFS, "dentry: %p\n", dentry); - err = -EPERM; v9ses = v9fs_dentry2v9ses(dentry); if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) { generic_fillattr(dentry->d_inode, stat); -- cgit v1.2.3 From cd63a5ffd24214246b1092365c7d6c2cd5aca29b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 5 Jul 2013 12:13:59 +0200 Subject: ALSA: hda - Keep halting ALC5505 DSP ALC5505 DSP is enabled even though we don't use the features yet at all. This results in the unnecessarily high power consumption, more than 100mV higher. Until we implement the DSP support, better to bypass DSP for saving more power. Reported-by: Mengdong Lin [Patch modified by Mengdong to cal alc5505_dsp_init() with extra acl5505_dsp_halt().] Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 14ac9b0e740c..8bd226149868 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -37,6 +37,9 @@ #include "hda_jack.h" #include "hda_generic.h" +/* keep halting ALC5505 DSP, for power saving */ +#define HALT_REALTEK_ALC5505 + /* unsol event tags */ #define ALC_DCVOL_EVENT 0x08 @@ -2659,15 +2662,27 @@ static void alc5505_dsp_init(struct hda_codec *codec) alc5505_coef_set(codec, 0x880c, 0x00000004); /* DRAM Function control */ alc5505_coef_set(codec, 0x880c, 0x00000003); alc5505_coef_set(codec, 0x880c, 0x00000010); + +#ifdef HALT_REALTEK_ALC5505 + alc5505_dsp_halt(codec); +#endif } +#ifdef HALT_REALTEK_ALC5505 +#define alc5505_dsp_suspend(codec) /* NOP */ +#define alc5505_dsp_resume(codec) /* NOP */ +#else +#define alc5505_dsp_suspend(codec) alc5505_dsp_halt(codec) +#define alc5505_dsp_resume(codec) alc5505_dsp_back_from_halt(codec) +#endif + #ifdef CONFIG_PM static int alc269_suspend(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; if (spec->has_alc5505_dsp) - alc5505_dsp_halt(codec); + alc5505_dsp_suspend(codec); return alc_suspend(codec); } @@ -2696,7 +2711,7 @@ static int alc269_resume(struct hda_codec *codec) alc_inv_dmic_sync(codec, true); hda_call_check_power_status(codec, 0x01); if (spec->has_alc5505_dsp) - alc5505_dsp_back_from_halt(codec); + alc5505_dsp_resume(codec); return 0; } #endif /* CONFIG_PM */ -- cgit v1.2.3 From f47a3ca224deff9e768eb49487afa391e28b06b0 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Mon, 3 Jun 2013 11:07:26 +0300 Subject: xtensa: tell git to ignore copied zlib source files Signed-off-by: Baruch Siach Signed-off-by: Chris Zankel --- arch/xtensa/boot/lib/.gitignore | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 arch/xtensa/boot/lib/.gitignore diff --git a/arch/xtensa/boot/lib/.gitignore b/arch/xtensa/boot/lib/.gitignore new file mode 100644 index 000000000000..1629a6167755 --- /dev/null +++ b/arch/xtensa/boot/lib/.gitignore @@ -0,0 +1,3 @@ +inffast.c +inflate.c +inftrees.c -- cgit v1.2.3 From ed9dfed62c6c057c1c51295d4373ae55c9284a10 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Mon, 17 Jun 2013 11:29:42 +0300 Subject: xtensa: timex.h: remove unused symbols Signed-off-by: Baruch Siach Signed-off-by: Chris Zankel --- arch/xtensa/include/asm/timex.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/arch/xtensa/include/asm/timex.h b/arch/xtensa/include/asm/timex.h index 3d35e5d0367e..c47989a0e3ad 100644 --- a/arch/xtensa/include/asm/timex.h +++ b/arch/xtensa/include/asm/timex.h @@ -35,19 +35,12 @@ # error "Bad timer number for Linux configurations!" #endif -#define LINUX_TIMER_MASK (1L << LINUX_TIMER_INT) - -#define CLOCK_TICK_RATE 1193180 /* (everyone is using this value) */ -#define CLOCK_TICK_FACTOR 20 /* Factor of both 10^6 and CLOCK_TICK_RATE */ - #ifdef CONFIG_XTENSA_CALIBRATE_CCOUNT extern unsigned long ccount_per_jiffy; extern unsigned long nsec_per_ccount; #define CCOUNT_PER_JIFFY ccount_per_jiffy -#define NSEC_PER_CCOUNT nsec_per_ccount #else #define CCOUNT_PER_JIFFY (CONFIG_XTENSA_CPU_CLOCK*(1000000UL/HZ)) -#define NSEC_PER_CCOUNT (1000UL / CONFIG_XTENSA_CPU_CLOCK) #endif -- cgit v1.2.3 From e504c4b6076d9ec1caccaac65803fe3fc29afec8 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Mon, 17 Jun 2013 11:29:43 +0300 Subject: xtensa: cleanup ccount frequency tracking Remove unused nsec_per_ccount, and rename ccount_per_jiffy to ccount_preq. Signed-off-by: Baruch Siach Signed-off-by: Chris Zankel --- arch/xtensa/include/asm/timex.h | 5 ++--- arch/xtensa/kernel/platform.c | 2 +- arch/xtensa/kernel/time.c | 7 +++---- arch/xtensa/platforms/xtfpga/setup.c | 3 +-- arch/xtensa/variants/s6000/delay.c | 3 +-- 5 files changed, 8 insertions(+), 12 deletions(-) diff --git a/arch/xtensa/include/asm/timex.h b/arch/xtensa/include/asm/timex.h index c47989a0e3ad..69f901713fb6 100644 --- a/arch/xtensa/include/asm/timex.h +++ b/arch/xtensa/include/asm/timex.h @@ -36,9 +36,8 @@ #endif #ifdef CONFIG_XTENSA_CALIBRATE_CCOUNT -extern unsigned long ccount_per_jiffy; -extern unsigned long nsec_per_ccount; -#define CCOUNT_PER_JIFFY ccount_per_jiffy +extern unsigned long ccount_freq; +#define CCOUNT_PER_JIFFY (ccount_freq / HZ) #else #define CCOUNT_PER_JIFFY (CONFIG_XTENSA_CPU_CLOCK*(1000000UL/HZ)) #endif diff --git a/arch/xtensa/kernel/platform.c b/arch/xtensa/kernel/platform.c index da827cbb2f59..1cf008284dd2 100644 --- a/arch/xtensa/kernel/platform.c +++ b/arch/xtensa/kernel/platform.c @@ -41,6 +41,6 @@ _F(void, pcibios_init, (void), { }); _F(void, calibrate_ccount, (void), { pr_err("ERROR: Cannot calibrate cpu frequency! Assuming 10MHz.\n"); - ccount_per_jiffy = 10 * (1000000UL/HZ); + ccount_freq = 10 * 1000000UL; }); #endif diff --git a/arch/xtensa/kernel/time.c b/arch/xtensa/kernel/time.c index ffb474104311..a32bc2ea7982 100644 --- a/arch/xtensa/kernel/time.c +++ b/arch/xtensa/kernel/time.c @@ -28,8 +28,7 @@ #include #ifdef CONFIG_XTENSA_CALIBRATE_CCOUNT -unsigned long ccount_per_jiffy; /* per 1/HZ */ -unsigned long nsec_per_ccount; /* nsec per ccount increment */ +unsigned long ccount_freq; /* ccount Hz */ #endif static cycle_t ccount_read(struct clocksource *cs) @@ -57,8 +56,8 @@ void __init time_init(void) #ifdef CONFIG_XTENSA_CALIBRATE_CCOUNT printk("Calibrating CPU frequency "); platform_calibrate_ccount(); - printk("%d.%02d MHz\n", (int)ccount_per_jiffy/(1000000/HZ), - (int)(ccount_per_jiffy/(10000/HZ))%100); + printk("%d.%02d MHz\n", (int)ccount_freq/1000000, + (int)(ccount_freq/10000)%100); #endif clocksource_register_hz(&ccount_clocksource, CCOUNT_PER_JIFFY * HZ); diff --git a/arch/xtensa/platforms/xtfpga/setup.c b/arch/xtensa/platforms/xtfpga/setup.c index c7ce62ff73f7..74bb74fa3f87 100644 --- a/arch/xtensa/platforms/xtfpga/setup.c +++ b/arch/xtensa/platforms/xtfpga/setup.c @@ -179,8 +179,7 @@ void __init platform_calibrate_ccount(void) if (!clk_freq) clk_freq = *(long *)XTFPGA_CLKFRQ_VADDR; - ccount_per_jiffy = clk_freq / HZ; - nsec_per_ccount = 1000000000UL / clk_freq; + ccount_freq = clk_freq; } #endif diff --git a/arch/xtensa/variants/s6000/delay.c b/arch/xtensa/variants/s6000/delay.c index 54b2b573f166..baefd3a3c2aa 100644 --- a/arch/xtensa/variants/s6000/delay.c +++ b/arch/xtensa/variants/s6000/delay.c @@ -22,6 +22,5 @@ void platform_calibrate_ccount(void) a = b; } while (--i >= 0); b -= a; - nsec_per_ccount = (LOOPS * 10000) / b; - ccount_per_jiffy = b * (100000UL / (LOOPS * HZ)); + ccount_freq = b * (100000UL / LOOPS); } -- cgit v1.2.3 From 8102f47ab5fcffc50f3ff94ca9b9073c12c18cc7 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Mon, 17 Jun 2013 11:29:44 +0300 Subject: xtensa: consolidate ccount access routines Use get_ccount everywhere; remove xtensa_get_ccount. Signed-off-by: Baruch Siach Signed-off-by: Chris Zankel --- arch/xtensa/include/asm/delay.h | 13 +++---------- arch/xtensa/variants/s6000/delay.c | 3 +-- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/arch/xtensa/include/asm/delay.h b/arch/xtensa/include/asm/delay.h index 61fc5faeb46c..3899610c1dff 100644 --- a/arch/xtensa/include/asm/delay.h +++ b/arch/xtensa/include/asm/delay.h @@ -12,7 +12,7 @@ #ifndef _XTENSA_DELAY_H #define _XTENSA_DELAY_H -#include +#include #include extern unsigned long loops_per_jiffy; @@ -24,24 +24,17 @@ static inline void __delay(unsigned long loops) : "=r" (loops) : "0" (loops)); } -static __inline__ u32 xtensa_get_ccount(void) -{ - u32 ccount; - asm volatile ("rsr %0, ccount\n" : "=r" (ccount)); - return ccount; -} - /* For SMP/NUMA systems, change boot_cpu_data to something like * local_cpu_data->... where local_cpu_data points to the current * cpu. */ static __inline__ void udelay (unsigned long usecs) { - unsigned long start = xtensa_get_ccount(); + unsigned long start = get_ccount(); unsigned long cycles = usecs * (loops_per_jiffy / (1000000UL / HZ)); /* Note: all variables are unsigned (can wrap around)! */ - while (((unsigned long)xtensa_get_ccount()) - start < cycles) + while (((unsigned long)get_ccount()) - start < cycles) ; } diff --git a/arch/xtensa/variants/s6000/delay.c b/arch/xtensa/variants/s6000/delay.c index baefd3a3c2aa..39154563ee17 100644 --- a/arch/xtensa/variants/s6000/delay.c +++ b/arch/xtensa/variants/s6000/delay.c @@ -1,4 +1,3 @@ -#include #include #include #include @@ -17,7 +16,7 @@ void platform_calibrate_ccount(void) "1: l32i %0, %2, 0 ;" " beq %0, %1, 1b ;" : "=&a"(u) : "a"(t), "a"(tstamp)); - b = xtensa_get_ccount(); + b = get_ccount(); if (i == LOOPS) a = b; } while (--i >= 0); -- cgit v1.2.3 From 925f5532e83bfe236b4f69ba4265c19663cfa9c7 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Tue, 18 Jun 2013 08:48:53 +0300 Subject: xtensa: ccount based clockevent implementation Reused some code from a preliminary implementation by Max Fillippov. Cc: Max Filippov Signed-off-by: Baruch Siach Signed-off-by: Chris Zankel --- arch/xtensa/Kconfig | 1 + arch/xtensa/kernel/time.c | 110 ++++++++++++++++++++++++++++++++-------------- 2 files changed, 79 insertions(+), 32 deletions(-) diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig index 0a1b95f81a32..dadcf824a5cb 100644 --- a/arch/xtensa/Kconfig +++ b/arch/xtensa/Kconfig @@ -6,6 +6,7 @@ config XTENSA select ARCH_WANT_FRAME_POINTERS select HAVE_IDE select GENERIC_ATOMIC64 + select GENERIC_CLOCKEVENTS select HAVE_GENERIC_HARDIRQS select VIRT_TO_BUS select GENERIC_IRQ_SHOW diff --git a/arch/xtensa/kernel/time.c b/arch/xtensa/kernel/time.c index a32bc2ea7982..ece4f9588625 100644 --- a/arch/xtensa/kernel/time.c +++ b/arch/xtensa/kernel/time.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -43,16 +44,80 @@ static struct clocksource ccount_clocksource = { .mask = CLOCKSOURCE_MASK(32), }; +static int ccount_timer_set_next_event(unsigned long delta, + struct clock_event_device *dev); +static void ccount_timer_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt); +static struct ccount_timer_t { + struct clock_event_device evt; + int irq_enabled; +} ccount_timer = { + .evt = { + .name = "ccount_clockevent", + .features = CLOCK_EVT_FEAT_ONESHOT, + .rating = 300, + .set_next_event = ccount_timer_set_next_event, + .set_mode = ccount_timer_set_mode, + }, +}; + +static int ccount_timer_set_next_event(unsigned long delta, + struct clock_event_device *dev) +{ + unsigned long flags, next; + int ret = 0; + + local_irq_save(flags); + next = get_ccount() + delta; + set_linux_timer(next); + if (next - get_ccount() > delta) + ret = -ETIME; + local_irq_restore(flags); + + return ret; +} + +static void ccount_timer_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + struct ccount_timer_t *timer = + container_of(evt, struct ccount_timer_t, evt); + + /* + * There is no way to disable the timer interrupt at the device level, + * only at the intenable register itself. Since enable_irq/disable_irq + * calls are nested, we need to make sure that these calls are + * balanced. + */ + switch (mode) { + case CLOCK_EVT_MODE_SHUTDOWN: + case CLOCK_EVT_MODE_UNUSED: + if (timer->irq_enabled) { + disable_irq(evt->irq); + timer->irq_enabled = 0; + } + break; + case CLOCK_EVT_MODE_RESUME: + case CLOCK_EVT_MODE_ONESHOT: + if (!timer->irq_enabled) { + enable_irq(evt->irq); + timer->irq_enabled = 1; + } + default: + break; + } +} + static irqreturn_t timer_interrupt(int irq, void *dev_id); static struct irqaction timer_irqaction = { .handler = timer_interrupt, - .flags = IRQF_DISABLED, + .flags = IRQF_TIMER, .name = "timer", + .dev_id = &ccount_timer, }; void __init time_init(void) { - unsigned int irq; #ifdef CONFIG_XTENSA_CALIBRATE_CCOUNT printk("Calibrating CPU frequency "); platform_calibrate_ccount(); @@ -61,11 +126,14 @@ void __init time_init(void) #endif clocksource_register_hz(&ccount_clocksource, CCOUNT_PER_JIFFY * HZ); - /* Initialize the linux timer interrupt. */ - - irq = irq_create_mapping(NULL, LINUX_TIMER_INT); - setup_irq(irq, &timer_irqaction); - set_linux_timer(get_ccount() + CCOUNT_PER_JIFFY); + ccount_timer.evt.cpumask = cpumask_of(0); + ccount_timer.evt.irq = irq_create_mapping(NULL, LINUX_TIMER_INT); + if (WARN(!ccount_timer.evt.irq, "error: can't map timer irq")) + return; + clockevents_config_and_register(&ccount_timer.evt, ccount_freq, 0xf, + 0xffffffff); + setup_irq(ccount_timer.evt.irq, &timer_irqaction); + ccount_timer.irq_enabled = 1; } /* @@ -74,36 +142,14 @@ void __init time_init(void) irqreturn_t timer_interrupt (int irq, void *dev_id) { + struct ccount_timer_t *timer = dev_id; + struct clock_event_device *evt = &timer->evt; - unsigned long next; - - next = get_linux_timer(); - -again: - while ((signed long)(get_ccount() - next) > 0) { - - profile_tick(CPU_PROFILING); -#ifndef CONFIG_SMP - update_process_times(user_mode(get_irq_regs())); -#endif - - xtime_update(1); /* Linux handler in kernel/time/timekeeping */ - - /* Note that writing CCOMPARE clears the interrupt. */ - - next += CCOUNT_PER_JIFFY; - set_linux_timer(next); - } + evt->event_handler(evt); /* Allow platform to do something useful (Wdog). */ - platform_heartbeat(); - /* Make sure we didn't miss any tick... */ - - if ((signed long)(get_ccount() - next) > 0) - goto again; - return IRQ_HANDLED; } -- cgit v1.2.3 From e3f432919feb4f26fe837472669e397ba8e8fccb Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Mon, 17 Jun 2013 11:29:46 +0300 Subject: xtensa: ccount based sched_clock Signed-off-by: Baruch Siach Signed-off-by: Chris Zankel --- arch/xtensa/Kconfig | 1 + arch/xtensa/kernel/time.c | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig index dadcf824a5cb..8852c0d35834 100644 --- a/arch/xtensa/Kconfig +++ b/arch/xtensa/Kconfig @@ -11,6 +11,7 @@ config XTENSA select VIRT_TO_BUS select GENERIC_IRQ_SHOW select GENERIC_CPU_DEVICES + select GENERIC_SCHED_CLOCK select MODULES_USE_ELF_RELA select GENERIC_PCI_IOMAP select ARCH_WANT_IPC_PARSE_VERSION diff --git a/arch/xtensa/kernel/time.c b/arch/xtensa/kernel/time.c index ece4f9588625..bdbb17312526 100644 --- a/arch/xtensa/kernel/time.c +++ b/arch/xtensa/kernel/time.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -37,6 +38,11 @@ static cycle_t ccount_read(struct clocksource *cs) return (cycle_t)get_ccount(); } +static u32 notrace ccount_sched_clock_read(void) +{ + return get_ccount(); +} + static struct clocksource ccount_clocksource = { .name = "ccount", .rating = 200, @@ -134,6 +140,8 @@ void __init time_init(void) 0xffffffff); setup_irq(ccount_timer.evt.irq, &timer_irqaction); ccount_timer.irq_enabled = 1; + + setup_sched_clock(ccount_sched_clock_read, 32, ccount_freq); } /* -- cgit v1.2.3 From 77b4dcdbfab4c288a3d211f307b5daa0bf45d0f8 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Fri, 7 Jun 2013 08:44:34 +0300 Subject: xtensa: tell git to ignore generated .dtb files Signed-off-by: Baruch Siach Signed-off-by: Max Filippov Signed-off-by: Chris Zankel --- arch/xtensa/boot/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/xtensa/boot/.gitignore b/arch/xtensa/boot/.gitignore index 38177c7ebcab..be7655998b26 100644 --- a/arch/xtensa/boot/.gitignore +++ b/arch/xtensa/boot/.gitignore @@ -1,2 +1,3 @@ uImage zImage.redboot +*.dtb -- cgit v1.2.3 From 661b40b0362c16b9969bf018636fb60454f62ee5 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Thu, 6 Jun 2013 14:23:37 +0300 Subject: xtensa: bootparams: fix typo Signed-off-by: Baruch Siach Signed-off-by: Max Filippov Signed-off-by: Chris Zankel --- arch/xtensa/include/asm/bootparam.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/xtensa/include/asm/bootparam.h b/arch/xtensa/include/asm/bootparam.h index 0c25799facab..23392c5630ce 100644 --- a/arch/xtensa/include/asm/bootparam.h +++ b/arch/xtensa/include/asm/bootparam.h @@ -20,7 +20,7 @@ #define BP_TAG_COMMAND_LINE 0x1001 /* command line (0-terminated string)*/ #define BP_TAG_INITRD 0x1002 /* ramdisk addr and size (bp_meminfo) */ #define BP_TAG_MEMORY 0x1003 /* memory addr and size (bp_meminfo) */ -#define BP_TAG_SERIAL_BAUSRATE 0x1004 /* baud rate of current console. */ +#define BP_TAG_SERIAL_BAUDRATE 0x1004 /* baud rate of current console. */ #define BP_TAG_SERIAL_PORT 0x1005 /* serial device of current console */ #define BP_TAG_FDT 0x1006 /* flat device tree addr */ -- cgit v1.2.3 From c5a771d0678f9613e9f89cf1a5bdcfa5b08b225b Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Sun, 9 Jun 2013 04:52:11 +0400 Subject: xtensa: adjust boot parameters address when INITIALIZE_XTENSA_MMU_INSIDE_VMLINUX is selected The virtual address of boot parameters chain is passed to the kernel via a2 register. Adjust it in case it is remapped during MMUv3 -> MMUv2 mapping change, i.e. when it is in the first 128M. Also fix interpretation of initrd and FDT addresses passed in the boot parameters: these are physical addresses. Cc: stable@vger.kernel.org Reported-by: Baruch Siach Signed-off-by: Max Filippov Signed-off-by: Chris Zankel --- arch/xtensa/kernel/head.S | 9 +++++++++ arch/xtensa/kernel/setup.c | 6 +++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/arch/xtensa/kernel/head.S b/arch/xtensa/kernel/head.S index ef12c0e6fa25..7d740ebbe198 100644 --- a/arch/xtensa/kernel/head.S +++ b/arch/xtensa/kernel/head.S @@ -68,6 +68,15 @@ _SetupMMU: #ifdef CONFIG_INITIALIZE_XTENSA_MMU_INSIDE_VMLINUX initialize_mmu +#if defined(CONFIG_MMU) && XCHAL_HAVE_PTP_MMU && XCHAL_HAVE_SPANNING_WAY + rsr a2, excsave1 + movi a3, 0x08000000 + bgeu a2, a3, 1f + movi a3, 0xd0000000 + add a2, a2, a3 + wsr a2, excsave1 +1: +#endif #endif .end no-absolute-literals diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c index 6dd25ecde3f5..14c6c3a6f04b 100644 --- a/arch/xtensa/kernel/setup.c +++ b/arch/xtensa/kernel/setup.c @@ -152,8 +152,8 @@ static int __init parse_tag_initrd(const bp_tag_t* tag) { meminfo_t* mi; mi = (meminfo_t*)(tag->data); - initrd_start = (void*)(mi->start); - initrd_end = (void*)(mi->end); + initrd_start = __va(mi->start); + initrd_end = __va(mi->end); return 0; } @@ -164,7 +164,7 @@ __tagtable(BP_TAG_INITRD, parse_tag_initrd); static int __init parse_tag_fdt(const bp_tag_t *tag) { - dtb_start = (void *)(tag->data[0]); + dtb_start = __va(tag->data[0]); return 0; } -- cgit v1.2.3 From a99e07ee5e887750f5136bc6799abe47a56fd2c9 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Wed, 15 May 2013 19:34:05 +0400 Subject: xtensa: check TLB sanity on return to userspace - check that user TLB mappings correspond to the current page table; - check that TLB mapping VPN is in the kernel/user address range in accordance with its ASID. Signed-off-by: Max Filippov Signed-off-by: Chris Zankel --- arch/xtensa/Kconfig.debug | 10 ++++ arch/xtensa/kernel/entry.S | 9 +++- arch/xtensa/mm/tlb.c | 113 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 131 insertions(+), 1 deletion(-) diff --git a/arch/xtensa/Kconfig.debug b/arch/xtensa/Kconfig.debug index a34010e0e51c..af7da74d535f 100644 --- a/arch/xtensa/Kconfig.debug +++ b/arch/xtensa/Kconfig.debug @@ -2,6 +2,16 @@ menu "Kernel hacking" source "lib/Kconfig.debug" +config DEBUG_TLB_SANITY + bool "Debug TLB sanity" + depends on DEBUG_KERNEL + help + Enable this to turn on TLB sanity check on each entry to userspace. + This check can spot missing TLB invalidation/wrong PTE permissions/ + premature page freeing. + + If unsure, say N. + config LD_NO_RELAX bool "Disable linker relaxation" default n diff --git a/arch/xtensa/kernel/entry.S b/arch/xtensa/kernel/entry.S index fa94512ff84d..9298742f0fd0 100644 --- a/arch/xtensa/kernel/entry.S +++ b/arch/xtensa/kernel/entry.S @@ -458,7 +458,7 @@ common_exception_return: _bbsi.l a4, TIF_NEED_RESCHED, 3f _bbsi.l a4, TIF_NOTIFY_RESUME, 2f - _bbci.l a4, TIF_SIGPENDING, 4f + _bbci.l a4, TIF_SIGPENDING, 5f 2: l32i a4, a1, PT_DEPC bgeui a4, VALID_DOUBLE_EXCEPTION_ADDRESS, 4f @@ -476,6 +476,13 @@ common_exception_return: callx4 a4 j 1b +5: +#ifdef CONFIG_DEBUG_TLB_SANITY + l32i a4, a1, PT_DEPC + bgeui a4, VALID_DOUBLE_EXCEPTION_ADDRESS, 4f + movi a4, check_tlb_sanity + callx4 a4 +#endif 4: /* Restore optional registers. */ load_xtregs_opt a1 a2 a4 a5 a6 a7 PT_XTREGS_OPT diff --git a/arch/xtensa/mm/tlb.c b/arch/xtensa/mm/tlb.c index 743346150eea..ca9d2366bf12 100644 --- a/arch/xtensa/mm/tlb.c +++ b/arch/xtensa/mm/tlb.c @@ -141,3 +141,116 @@ void flush_tlb_page (struct vm_area_struct *vma, unsigned long page) local_irq_restore(flags); } + +#ifdef CONFIG_DEBUG_TLB_SANITY + +static unsigned get_pte_for_vaddr(unsigned vaddr) +{ + struct task_struct *task = get_current(); + struct mm_struct *mm = task->mm; + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; + + if (!mm) + mm = task->active_mm; + pgd = pgd_offset(mm, vaddr); + if (pgd_none_or_clear_bad(pgd)) + return 0; + pmd = pmd_offset(pgd, vaddr); + if (pmd_none_or_clear_bad(pmd)) + return 0; + pte = pte_offset_map(pmd, vaddr); + if (!pte) + return 0; + return pte_val(*pte); +} + +enum { + TLB_SUSPICIOUS = 1, + TLB_INSANE = 2, +}; + +static void tlb_insane(void) +{ + BUG_ON(1); +} + +static void tlb_suspicious(void) +{ + WARN_ON(1); +} + +/* + * Check that TLB entries with kernel ASID (1) have kernel VMA (>= TASK_SIZE), + * and TLB entries with user ASID (>=4) have VMA < TASK_SIZE. + * + * Check that valid TLB entries either have the same PA as the PTE, or PTE is + * marked as non-present. Non-present PTE and the page with non-zero refcount + * and zero mapcount is normal for batched TLB flush operation. Zero refcount + * means that the page was freed prematurely. Non-zero mapcount is unusual, + * but does not necessary means an error, thus marked as suspicious. + */ +static int check_tlb_entry(unsigned w, unsigned e, bool dtlb) +{ + unsigned tlbidx = w | (e << PAGE_SHIFT); + unsigned r0 = dtlb ? + read_dtlb_virtual(tlbidx) : read_itlb_virtual(tlbidx); + unsigned vpn = (r0 & PAGE_MASK) | (e << PAGE_SHIFT); + unsigned pte = get_pte_for_vaddr(vpn); + unsigned mm_asid = (get_rasid_register() >> 8) & ASID_MASK; + unsigned tlb_asid = r0 & ASID_MASK; + bool kernel = tlb_asid == 1; + int rc = 0; + + if (tlb_asid > 0 && ((vpn < TASK_SIZE) == kernel)) { + pr_err("%cTLB: way: %u, entry: %u, VPN %08x in %s PTE\n", + dtlb ? 'D' : 'I', w, e, vpn, + kernel ? "kernel" : "user"); + rc |= TLB_INSANE; + } + + if (tlb_asid == mm_asid) { + unsigned r1 = dtlb ? read_dtlb_translation(tlbidx) : + read_itlb_translation(tlbidx); + if ((pte ^ r1) & PAGE_MASK) { + pr_err("%cTLB: way: %u, entry: %u, mapping: %08x->%08x, PTE: %08x\n", + dtlb ? 'D' : 'I', w, e, r0, r1, pte); + if (pte == 0 || !pte_present(__pte(pte))) { + struct page *p = pfn_to_page(r1 >> PAGE_SHIFT); + pr_err("page refcount: %d, mapcount: %d\n", + page_count(p), + page_mapcount(p)); + if (!page_count(p)) + rc |= TLB_INSANE; + else if (page_mapped(p)) + rc |= TLB_SUSPICIOUS; + } else { + rc |= TLB_INSANE; + } + } + } + return rc; +} + +void check_tlb_sanity(void) +{ + unsigned long flags; + unsigned w, e; + int bug = 0; + + local_irq_save(flags); + for (w = 0; w < DTLB_ARF_WAYS; ++w) + for (e = 0; e < (1 << XCHAL_DTLB_ARF_ENTRIES_LOG2); ++e) + bug |= check_tlb_entry(w, e, true); + for (w = 0; w < ITLB_ARF_WAYS; ++w) + for (e = 0; e < (1 << XCHAL_ITLB_ARF_ENTRIES_LOG2); ++e) + bug |= check_tlb_entry(w, e, false); + if (bug & TLB_INSANE) + tlb_insane(); + if (bug & TLB_SUSPICIOUS) + tlb_suspicious(); + local_irq_restore(flags); +} + +#endif /* CONFIG_DEBUG_TLB_SANITY */ -- cgit v1.2.3 From 220f5354089b27e8eeaa8658806dabc59ce55729 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 22 Feb 2013 00:09:22 +0530 Subject: xtensa: Flat DeviceTree copy not future-safe flat DT copy code calls bootmem allocator with @align = 0. This is probably OK with legacy allocator which xtensa uses right now, but this will panic right away with memblock allocator Signed-off-by: Vineet Gupta Cc: Chris Zankel Cc: Max Filippov Cc: Marc Gauthier Cc: linux-xtensa@linux-xtensa.org Cc: linux-kernel@vger.kernel.org Acked-by: Max Filippov Signed-off-by: Max Filippov Signed-off-by: Chris Zankel --- arch/xtensa/kernel/setup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c index 14c6c3a6f04b..42a8bba0b0ea 100644 --- a/arch/xtensa/kernel/setup.c +++ b/arch/xtensa/kernel/setup.c @@ -256,7 +256,7 @@ void __init early_init_devtree(void *params) static void __init copy_devtree(void) { void *alloc = early_init_dt_alloc_memory_arch( - be32_to_cpu(initial_boot_params->totalsize), 0); + be32_to_cpu(initial_boot_params->totalsize), 8); if (alloc) { memcpy(alloc, initial_boot_params, be32_to_cpu(initial_boot_params->totalsize)); -- cgit v1.2.3 From 478ba61afccd3a4d7ca662cadff1d35d183fe67d Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Fri, 24 May 2013 07:02:25 +0400 Subject: xtensa: add static function tracer support Signed-off-by: Max Filippov Signed-off-by: Chris Zankel --- arch/xtensa/Kconfig | 1 + arch/xtensa/boot/lib/Makefile | 7 ++++++ arch/xtensa/include/asm/ftrace.h | 17 +++++++++++-- arch/xtensa/kernel/Makefile | 1 + arch/xtensa/kernel/mcount.S | 50 +++++++++++++++++++++++++++++++++++++++ arch/xtensa/kernel/xtensa_ksyms.c | 4 ++++ 6 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 arch/xtensa/kernel/mcount.S diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig index 8852c0d35834..7ea6451a3a33 100644 --- a/arch/xtensa/Kconfig +++ b/arch/xtensa/Kconfig @@ -19,6 +19,7 @@ config XTENSA select CLONE_BACKWARDS select IRQ_DOMAIN select HAVE_OPROFILE + select HAVE_FUNCTION_TRACER help Xtensa processors are 32-bit RISC machines designed by Tensilica primarily for embedded systems. These processors are both diff --git a/arch/xtensa/boot/lib/Makefile b/arch/xtensa/boot/lib/Makefile index ad8952e8a07f..6868f2ca6af8 100644 --- a/arch/xtensa/boot/lib/Makefile +++ b/arch/xtensa/boot/lib/Makefile @@ -7,6 +7,13 @@ zlib := inffast.c inflate.c inftrees.c lib-y += $(zlib:.c=.o) zmem.o ccflags-y := -Ilib/zlib_inflate +ifdef CONFIG_FUNCTION_TRACER +CFLAGS_REMOVE_inflate.o = -pg +CFLAGS_REMOVE_zmem.o = -pg +CFLAGS_REMOVE_inftrees.o = -pg +CFLAGS_REMOVE_inffast.o = -pg +endif + quiet_cmd_copy_zlib = COPY $@ cmd_copy_zlib = cat $< > $@ diff --git a/arch/xtensa/include/asm/ftrace.h b/arch/xtensa/include/asm/ftrace.h index 36dc7a684397..73cc3f482304 100644 --- a/arch/xtensa/include/asm/ftrace.h +++ b/arch/xtensa/include/asm/ftrace.h @@ -13,6 +13,7 @@ #include #define HAVE_ARCH_CALLER_ADDR +#ifndef __ASSEMBLY__ #define CALLER_ADDR0 ({ unsigned long a0, a1; \ __asm__ __volatile__ ( \ "mov %0, a0\n" \ @@ -24,10 +25,22 @@ extern unsigned long return_address(unsigned level); #define CALLER_ADDR1 return_address(1) #define CALLER_ADDR2 return_address(2) #define CALLER_ADDR3 return_address(3) -#else +#else /* CONFIG_FRAME_POINTER */ #define CALLER_ADDR1 (0) #define CALLER_ADDR2 (0) #define CALLER_ADDR3 (0) -#endif +#endif /* CONFIG_FRAME_POINTER */ +#endif /* __ASSEMBLY__ */ + +#ifdef CONFIG_FUNCTION_TRACER + +#define MCOUNT_ADDR ((unsigned long)(_mcount)) +#define MCOUNT_INSN_SIZE 3 + +#ifndef __ASSEMBLY__ +extern void _mcount(void); +#define mcount _mcount +#endif /* __ASSEMBLY__ */ +#endif /* CONFIG_FUNCTION_TRACER */ #endif /* _XTENSA_FTRACE_H */ diff --git a/arch/xtensa/kernel/Makefile b/arch/xtensa/kernel/Makefile index 1e7fc87a94bb..f90265ec1ccc 100644 --- a/arch/xtensa/kernel/Makefile +++ b/arch/xtensa/kernel/Makefile @@ -11,6 +11,7 @@ obj-y := align.o coprocessor.o entry.o irq.o pci-dma.o platform.o process.o \ obj-$(CONFIG_KGDB) += xtensa-stub.o obj-$(CONFIG_PCI) += pci.o obj-$(CONFIG_MODULES) += xtensa_ksyms.o module.o +obj-$(CONFIG_FUNCTION_TRACER) += mcount.o AFLAGS_head.o += -mtext-section-literals diff --git a/arch/xtensa/kernel/mcount.S b/arch/xtensa/kernel/mcount.S new file mode 100644 index 000000000000..0eeda2e4a25e --- /dev/null +++ b/arch/xtensa/kernel/mcount.S @@ -0,0 +1,50 @@ +/* + * arch/xtensa/kernel/mcount.S + * + * Xtensa specific mcount support + * + * 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. + * + * Copyright (C) 2013 Tensilica Inc. + */ + +#include +#include + +/* + * Entry condition: + * + * a2: a0 of the caller + */ + +ENTRY(_mcount) + + entry a1, 16 + + movi a4, ftrace_trace_function + l32i a4, a4, 0 + movi a3, ftrace_stub + bne a3, a4, 1f + retw + +1: xor a7, a2, a1 + movi a3, 0x3fffffff + and a7, a7, a3 + xor a7, a7, a1 + + xor a6, a0, a1 + and a6, a6, a3 + xor a6, a6, a1 + addi a6, a6, -MCOUNT_INSN_SIZE + callx4 a4 + + retw + +ENDPROC(_mcount) + +ENTRY(ftrace_stub) + entry a1, 16 + retw +ENDPROC(ftrace_stub) diff --git a/arch/xtensa/kernel/xtensa_ksyms.c b/arch/xtensa/kernel/xtensa_ksyms.c index 42c53c87c204..d8507f812f46 100644 --- a/arch/xtensa/kernel/xtensa_ksyms.c +++ b/arch/xtensa/kernel/xtensa_ksyms.c @@ -124,3 +124,7 @@ extern long common_exception_return; extern long _spill_registers; EXPORT_SYMBOL(common_exception_return); EXPORT_SYMBOL(_spill_registers); + +#ifdef CONFIG_FUNCTION_TRACER +EXPORT_SYMBOL(_mcount); +#endif -- cgit v1.2.3 From 0eb5afb3bae69a18bb4a8dbcbd361c4403fb54cd Mon Sep 17 00:00:00 2001 From: Zhao Hongjiang Date: Mon, 8 Jul 2013 15:22:50 +0800 Subject: xtensa: remove the second argument of __bio_kmap_atomic() kmap_atomic allows only one argument now, just remove the unused 'kmtype'. Signed-off-by: Zhao Hongjiang Acked-by: Geert Uytterhoeven Signed-off-by: Chris Zankel --- arch/xtensa/platforms/iss/simdisk.c | 4 ++-- include/linux/bio.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/xtensa/platforms/iss/simdisk.c b/arch/xtensa/platforms/iss/simdisk.c index c0edb35424ce..8c6e819cd8ed 100644 --- a/arch/xtensa/platforms/iss/simdisk.c +++ b/arch/xtensa/platforms/iss/simdisk.c @@ -108,13 +108,13 @@ static int simdisk_xfer_bio(struct simdisk *dev, struct bio *bio) sector_t sector = bio->bi_sector; bio_for_each_segment(bvec, bio, i) { - char *buffer = __bio_kmap_atomic(bio, i, KM_USER0); + char *buffer = __bio_kmap_atomic(bio, i); unsigned len = bvec->bv_len >> SECTOR_SHIFT; simdisk_transfer(dev, sector, len, buffer, bio_data_dir(bio) == WRITE); sector += len; - __bio_kunmap_atomic(bio, KM_USER0); + __bio_kunmap_atomic(bio); } return 0; } diff --git a/include/linux/bio.h b/include/linux/bio.h index ef24466d8f82..ec48bac5b039 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -97,11 +97,11 @@ static inline void *bio_data(struct bio *bio) * permanent PIO fall back, user is probably better off disabling highmem * I/O completely on that queue (see ide-dma for example) */ -#define __bio_kmap_atomic(bio, idx, kmtype) \ +#define __bio_kmap_atomic(bio, idx) \ (kmap_atomic(bio_iovec_idx((bio), (idx))->bv_page) + \ bio_iovec_idx((bio), (idx))->bv_offset) -#define __bio_kunmap_atomic(addr, kmtype) kunmap_atomic(addr) +#define __bio_kunmap_atomic(addr) kunmap_atomic(addr) /* * merge helpers etc -- cgit v1.2.3 From 1b6c79361ba5ce30b40f0f7d6fc2421dc5fcbe0c Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Sun, 26 May 2013 12:35:38 +0200 Subject: video: imxfb: Add DT support Add devicetree support for imx framebuffer driver. It uses the generic display bindings and helper functions. Signed-off-by: Markus Pargmann Cc: Fabio Estevam Cc: Mark Rutland Acked-by: Jean-Christophe PLAGNIOL-VILLARD Acked-by: Sascha Hauer --- .../devicetree/bindings/video/fsl,imx-fb.txt | 51 ++++++ drivers/video/Kconfig | 2 + drivers/video/imxfb.c | 194 +++++++++++++++++---- 3 files changed, 212 insertions(+), 35 deletions(-) create mode 100644 Documentation/devicetree/bindings/video/fsl,imx-fb.txt diff --git a/Documentation/devicetree/bindings/video/fsl,imx-fb.txt b/Documentation/devicetree/bindings/video/fsl,imx-fb.txt new file mode 100644 index 000000000000..46da08db186a --- /dev/null +++ b/Documentation/devicetree/bindings/video/fsl,imx-fb.txt @@ -0,0 +1,51 @@ +Freescale imx21 Framebuffer + +This framebuffer driver supports devices imx1, imx21, imx25, and imx27. + +Required properties: +- compatible : "fsl,-fb", chip should be imx1 or imx21 +- reg : Should contain 1 register ranges(address and length) +- interrupts : One interrupt of the fb dev + +Required nodes: +- display: Phandle to a display node as described in + Documentation/devicetree/bindings/video/display-timing.txt + Additional, the display node has to define properties: + - bits-per-pixel: Bits per pixel + - fsl,pcr: LCDC PCR value + +Optional properties: +- fsl,dmacr: DMA Control Register value. This is optional. By default, the + register is not modified as recommended by the datasheet. +- fsl,lscr1: LCDC Sharp Configuration Register value. + +Example: + + imxfb: fb@10021000 { + compatible = "fsl,imx21-fb"; + interrupts = <61>; + reg = <0x10021000 0x1000>; + display = <&display0>; + }; + + ... + + display0: display0 { + model = "Primeview-PD050VL1"; + native-mode = <&timing_disp0>; + bits-per-pixel = <16>; + fsl,pcr = <0xf0c88080>; /* non-standard but required */ + display-timings { + timing_disp0: 640x480 { + hactive = <640>; + vactive = <480>; + hback-porch = <112>; + hfront-porch = <36>; + hsync-len = <32>; + vback-porch = <33>; + vfront-porch = <33>; + vsync-len = <2>; + clock-frequency = <25000000>; + }; + }; + }; diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 2c301f8441e9..4cf1e1dd5621 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -367,6 +367,8 @@ config FB_IMX select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT + select FB_MODE_HELPERS + select VIDEOMODE_HELPERS config FB_CYBER2000 tristate "CyberPro 2000/2010/5000 support" diff --git a/drivers/video/imxfb.c b/drivers/video/imxfb.c index 12af22ba4d92..38733ac2b698 100644 --- a/drivers/video/imxfb.c +++ b/drivers/video/imxfb.c @@ -31,6 +31,12 @@ #include #include #include +#include +#include + +#include